Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow Geometry objects as arguments for Calculation command, move conformers/CREST options, and add solvate command #50

Merged
merged 10 commits into from
Dec 4, 2024
Merged
49 changes: 33 additions & 16 deletions avo_xtb/about.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import argparse
import json
import logging
import re
import subprocess
import sys

Expand All @@ -13,6 +14,30 @@
logger = logging.getLogger(__name__)


# Get xtb and crest versions
# Regex to match version numbers (e.g., 6.7.1, 2.12, 6.4.0)
pattern = r"\bversion\s+(\d+\.\d+\.\d+|\d+\.\d+)\b"

if easyxtb.XTB_BIN:
xtb_version_info = subprocess.run(
[str(easyxtb.XTB_BIN), "--version"],
encoding="utf-8",
capture_output=True,
).stdout
XTB_VERSION = re.findall(pattern, xtb_version_info, re.IGNORECASE)[0]
else:
XTB_VERSION = None
if easyxtb.CREST_BIN:
crest_version_info = subprocess.run(
[str(easyxtb.CREST_BIN), "--version"],
encoding="utf-8",
capture_output=True,
).stdout
CREST_VERSION = re.findall(pattern, crest_version_info, re.IGNORECASE)[0]
else:
CREST_VERSION = None


if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--debug", action="store_true")
Expand All @@ -37,30 +62,22 @@
avo_input = json.loads(sys.stdin.read())
output = avo_input.copy()

if easyxtb.XTB_BIN:
xtb_version = subprocess.run(
[str(easyxtb.XTB_BIN), "--version"],
encoding="utf-8",
capture_output=True,
).stdout.splitlines()[-2].strip()
if XTB_VERSION:
xtb_version_msg = XTB_VERSION
else:
xtb_version = "No xtb binary found"
if easyxtb.CREST_BIN:
crest_version = subprocess.run(
[str(easyxtb.CREST_BIN), "--version"],
encoding="utf-8",
capture_output=True,
).stdout.splitlines()[-4].strip()
xtb_version_msg = "No xtb binary found"
if CREST_VERSION:
crest_version_msg = CREST_VERSION
else:
crest_version = "No CREST binary found"
crest_version_msg = "No CREST binary found"

# Do nothing to data other than add message with version and path info
output["message"] = (
"avo_xtb plugin\n"
+ f"easyxtb version: {easyxtb.configuration.easyxtb_VERSION}\n"
+ f"xtb version: {xtb_version}\n"
+ f"xtb version: {xtb_version_msg}\n"
+ f"xtb path: {easyxtb.XTB_BIN}\n"
+ f"CREST version: {crest_version}\n"
+ f"CREST version: {crest_version_msg}\n"
+ f"CREST path: {easyxtb.CREST_BIN}"
)

Expand Down
40 changes: 26 additions & 14 deletions avo_xtb/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,25 +33,31 @@
"default": str(easyxtb.XTB_BIN),
"order": 1.0,
},
"crest_bin": {
"type": "string",
"label": "Location of the CREST binary",
"default": str(easyxtb.CREST_BIN),
"order": 2.0,
},
"user_dir": {
"type": "string",
"label": "Run calculations (in subfolder) in",
"default": str(easyxtb.CALC_DIR),
"order": 2.0,
"order": 3.0,
},
"n_proc": {
"type": "integer",
"label": "Parallel processes to run",
"minimum": 1,
"default": 1,
"order": 3.0
"order": 4.0
},
"energy_units": {
"type": "stringList",
"label": "Preferred energy units",
"values": ["kJ/mol", "kcal/mol"],
"default": 0,
"order": 4.0,
"order": 5.0,
},
"solvent": {
"type": "stringList",
Expand Down Expand Up @@ -84,14 +90,14 @@
"water",
],
"default": 0,
"order": 5.0,
"order": 6.0,
},
"method": {
"type": "stringList",
"label": "Method (xtb only)",
"label": "Method",
"values": methods,
"default": methods[-1],
"order": 6.0,
"order": 7.0,
},
"opt_lvl": {
"type": "stringList",
Expand All @@ -107,7 +113,7 @@
"extreme",
],
"default": 4,
"order": 7.0,
"order": 8.0,
},
"warning": {
"type": "text",
Expand Down Expand Up @@ -146,24 +152,30 @@
easyxtb.config["calc_dir"] = str(easyxtb.CALC_DIR)

# Save change to xtb_bin if there has been one
if avo_input["xtb_bin"] != str(easyxtb.XTB_BIN):
if avo_input["xtb_bin"] in ["none", ""]:
pass
elif avo_input["xtb_bin"] != str(easyxtb.XTB_BIN):
easyxtb.XTB_BIN = Path(avo_input["xtb_bin"])
easyxtb.config["xtb_bin"] = str(easyxtb.XTB_BIN)

# Save change to crest_bin if there has been one
if avo_input["crest_bin"] in ["none", ""]:
pass
elif Path(avo_input["crest_bin"]) != easyxtb.CREST_BIN:
easyxtb.CREST_BIN = Path(avo_input["crest_bin"])
easyxtb.config["crest_bin"] = str(easyxtb.CREST_BIN)

# Update number of threads
easyxtb.config["n_proc"] = avo_input["n_proc"]

# Update energy units
easyxtb.config["energy_units"] = avo_input["energy_units"]

# Convert "none" string to Python None
# Update solvent
if avo_input["solvent"] == "none":
solvent_selected = None
easyxtb.config["solvent"] = None
else:
solvent_selected = avo_input["solvent"]

# Update solvent
easyxtb.config["solvent"] = solvent_selected
easyxtb.config["solvent"] = avo_input["solvent"]

# Update method
easyxtb.config["method"] = methods.index(avo_input["method"])
Expand Down
100 changes: 8 additions & 92 deletions avo_xtb/conformers.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,74 +31,7 @@

if args.print_options:
options = {
"inputMoleculeFormat": "xyz",
"userOptions": {
"crest_bin": {
"type": "string",
"label": "Location of the CREST binary",
"default": str(easyxtb.CREST_BIN),
"order": 1.0,
},
"save_dir": {
"type": "string",
"label": "Save results in",
"default": str(easyxtb.CALC_DIR),
"order": 2.0,
},
# "Number of threads": {
# "type": "integer",
# #"label": "Number of cores",
# "minimum": 1,
# "default": 1,
# "order": 3.0
# },
# "Memory per core": {
# "type": "integer",
# #"label" "Memory per core",
# "minimum": 1,
# "default": 1,
# "suffix": " GB",
# "order": 4.0
# },
"help": {
"type": "text",
"label": "For help see",
"default": "https://crest-lab.github.io/crest-docs/",
"order": 9.0,
},
"solvent": {
"type": "stringList",
"label": "Solvation",
"values": [
"none",
"acetone",
"acetonitrile",
"aniline",
"benzaldehyde",
"benzene",
"ch2cl2",
"chcl3",
"cs2",
"dioxane",
"dmf",
"dmso",
"ether",
"ethylacetate",
"furane",
"hexandecane",
"hexane",
"methanol",
"nitromethane",
"octanol",
"woctanol",
"phenol",
"toluene",
"thf",
"water",
],
"default": 0,
"order": 6.0,
},
"ewin": {
"type": "integer",
"label": "Keep all conformers within",
Expand All @@ -114,15 +47,18 @@
"default": False,
"order": 8.0,
},
"help": {
"type": "text",
"label": "For help see",
"default": "https://crest-lab.github.io/crest-docs/",
"order": 9.0,
},
},
}
# Display energy in kcal if user has insisted on it
if easyxtb.config["energy_units"] == "kcal/mol":
options["userOptions"]["ewin"]["default"] = 6
options["userOptions"]["ewin"]["suffix"] = " kcal/mol"
# Make solvation default if found in user config
if easyxtb.config["solvent"] is not None:
options["userOptions"]["solvent"]["default"] = easyxtb.config["solvent"]
print(json.dumps(options))
if args.display_name:
print("Conformers…")
Expand All @@ -135,30 +71,17 @@
# Extract the coords
geom = easyxtb.Geometry.from_cjson(avo_input["cjson"])

# If provided crest path different to that stored, use it and save it
if Path(avo_input["crest_bin"]) != easyxtb.CREST_BIN:
crest_bin = Path(avo_input["crest_bin"])
easyxtb.config["crest_bin"] = str(crest_bin)
with open(easyxtb.config_file, "w", encoding="utf-8") as config_path:
json.dump(easyxtb.config, config_path)

# crest takes energies in kcal so convert if provided in kJ (default)
if easyxtb.config["energy_units"] == "kJ/mol":
ewin_kcal = avo_input["ewin"] / 4.184
else:
ewin_kcal = avo_input["ewin"]

# Convert "none" string to Python None
if avo_input["solvent"] == "none":
solvation = None
else:
solvation = avo_input["solvent"]

# Run calculation; returns set of conformers as well as Calculation object
conformers, calc = easyxtb.calculate.conformers(
geom,
solvation=solvation,
method=2,
solvation=easyxtb.config["solvent"],
method=easyxtb.config["method"],
ewin=ewin_kcal,
hess=avo_input["hess"],
return_calc=True,
Expand Down Expand Up @@ -190,13 +113,6 @@
with open(easyxtb.TEMP_DIR / "result.cjson", "w", encoding="utf-8") as save_file:
json.dump(output["cjson"], save_file, indent=2)

# If user specified a save location, copy calculation directory to there
if not (
avo_input["save_dir"] in ["", None]
or Path(avo_input["save_dir"]) == easyxtb.TEMP_DIR
):
copytree(easyxtb.TEMP_DIR, Path(avo_input["save_dir"]), dirs_exist_ok=True)

# Pass back to Avogadro
print(json.dumps(output))
logger.debug(f"The following dictionary was passed back to Avogadro: {output}")
2 changes: 1 addition & 1 deletion avo_xtb/deprotonate.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
tautomers, calc = easyxtb.calculate.deprotonate(
geom,
solvation=easyxtb.config["solvent"],
method=2,
method=easyxtb.config["method"],
return_calc=True,
)

Expand Down
3 changes: 2 additions & 1 deletion avo_xtb/protonate.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import json
import logging
import sys
from copy import deepcopy

from support import easyxtb

Expand All @@ -19,7 +20,7 @@ def cleanup_after_taut(cjson: dict) -> dict:
Essentially gives an empty cjson, with only the total charge and spin retained.
"""

output = easyxtb.convert.empty_cjson.copy()
output = deepcopy(easyxtb.convert.empty_cjson)
output["properties"]["totalCharge"] = cjson["properties"]["totalCharge"]
output["properties"]["totalSpinMultiplicity"] = cjson["properties"]["totalSpinMultiplicity"]

Expand Down
3 changes: 2 additions & 1 deletion avo_xtb/run_xtb.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import json
import logging
import sys
from copy import deepcopy
from pathlib import Path
from shutil import copytree

Expand Down Expand Up @@ -120,7 +121,7 @@
# Start by passing back an empty cjson, then add changes
output = {
"moleculeFormat": "cjson",
"cjson": easyxtb.convert.empty_cjson.copy(),
"cjson": deepcopy(easyxtb.convert.empty_cjson),
}

# TODO Catch errors in xtb execution
Expand Down
Loading