From 44c96987b6e144ab9e18bb6f179e58a1b0cb7827 Mon Sep 17 00:00:00 2001 From: Geoff Hutchison Date: Sat, 30 Nov 2024 13:04:50 -0500 Subject: [PATCH] Unbundle all charge and forcefield scripts Fixes #1620 Signed-off-by: Geoff Hutchison --- avogadro/qtplugins/forcefield/CMakeLists.txt | 12 +- .../qtplugins/forcefield/scripts/ani2x.py | 89 ---------- avogadro/qtplugins/forcefield/scripts/gaff.py | 88 ---------- avogadro/qtplugins/forcefield/scripts/gfn1.py | 104 ------------ avogadro/qtplugins/forcefield/scripts/gfn2.py | 104 ------------ .../qtplugins/forcefield/scripts/gfnff.py | 154 ----------------- .../qtplugins/forcefield/scripts/mmff94.py | 88 ---------- avogadro/qtplugins/forcefield/scripts/uff.py | 87 ---------- .../qtplugins/scriptcharges/CMakeLists.txt | 9 +- .../chargeScripts/antechamber.py | 155 ------------------ .../scriptcharges/chargeScripts/xtb.py | 104 ------------ 11 files changed, 2 insertions(+), 992 deletions(-) delete mode 100644 avogadro/qtplugins/forcefield/scripts/ani2x.py delete mode 100644 avogadro/qtplugins/forcefield/scripts/gaff.py delete mode 100644 avogadro/qtplugins/forcefield/scripts/gfn1.py delete mode 100644 avogadro/qtplugins/forcefield/scripts/gfn2.py delete mode 100644 avogadro/qtplugins/forcefield/scripts/gfnff.py delete mode 100644 avogadro/qtplugins/forcefield/scripts/mmff94.py delete mode 100644 avogadro/qtplugins/forcefield/scripts/uff.py delete mode 100644 avogadro/qtplugins/scriptcharges/chargeScripts/antechamber.py delete mode 100644 avogadro/qtplugins/scriptcharges/chargeScripts/xtb.py diff --git a/avogadro/qtplugins/forcefield/CMakeLists.txt b/avogadro/qtplugins/forcefield/CMakeLists.txt index 484689aede..4ca74d4dfc 100644 --- a/avogadro/qtplugins/forcefield/CMakeLists.txt +++ b/avogadro/qtplugins/forcefield/CMakeLists.txt @@ -44,14 +44,6 @@ if (BUILD_GPL_PLUGINS AND OpenBabel3_LIBRARY) target_link_libraries(Forcefield PRIVATE OpenBabel3) endif() -# Bundled forcefield scripts -set(forcefields - scripts/ani2x.py - scripts/gfn1.py - scripts/gfn2.py - scripts/gfnff.py -) - if (NOT BUILD_GPL_PLUGINS) # install the OB / Pybel forcefield scripts list(APPEND forcefields @@ -61,6 +53,4 @@ if (NOT BUILD_GPL_PLUGINS) ) endif() -# Don't install the scripts - we'll use these as plugins -# install(PROGRAMS ${forcefields} -# DESTINATION "${INSTALL_LIBRARY_DIR}/avogadro2/scripts/energy/") +# Don't install any scripts - we'll use these as plugins diff --git a/avogadro/qtplugins/forcefield/scripts/ani2x.py b/avogadro/qtplugins/forcefield/scripts/ani2x.py deleted file mode 100644 index 06266cf08b..0000000000 --- a/avogadro/qtplugins/forcefield/scripts/ani2x.py +++ /dev/null @@ -1,89 +0,0 @@ -# This source file is part of the Avogadro project. -# This source code is released under the 3-Clause BSD License, (see "LICENSE"). - -import argparse -import json -import sys - -try: - import torch - import torchani - import numpy as np - - device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') - model = torchani.models.ANI2x(periodic_table_index=True).to(device) - - imported = True -except ImportError: - imported = False - - -def getMetaData(): - # before we return metadata, make sure xtb is in the path - if not imported: - return {} # Avogadro will ignore us now - - metaData = { - "name": "ANI2x", - "identifier": "ANI2x", - "description": "Calculate ANI-2x energies and gradients", - "inputFormat": "cjson", - "elements": "1,6-9,16-17", - "unitCell": False, - "gradients": True, - "ion": False, - "radical": False, - } - return metaData - - -def run(filename): - # we get the molecule from the supplied filename - # in cjson format (it's a temporary file created by Avogadro) - with open(filename, "r") as f: - mol_cjson = json.load(f) - - # first setup the calculator - atoms = np.array(mol_cjson["atoms"]["elements"]["number"]) - species = torch.tensor([atoms], device=device) - coord_list = mol_cjson["atoms"]["coords"]["3d"] - np_coords = np.array(coord_list, dtype=float).reshape(-1, 3) - coordinates = torch.tensor([np_coords], requires_grad=True, device=device) - - # we loop forever - Avogadro will kill the process when done - num_atoms = len(atoms) - while True: - # read new coordinates from stdin - for i in range(num_atoms): - np_coords[i] = np.fromstring(input(), sep=" ") - coordinates = torch.tensor([np_coords], requires_grad=True, device=device) - - # first print the energy of these coordinates - energy = model((species, coordinates)).energies - print("AvogadroEnergy:", energy) # in Hartree - - # now print the gradient on each atom - print("AvogadroGradient:") - derivative = torch.autograd.grad(energy.sum(), coordinates)[0] - for i in range(num_atoms): - print(derivative[0][i][0].item(), derivative[0][i][1].item(), derivative[0][i][2].item()) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser("ANI-2x calculator") - parser.add_argument("--display-name", action="store_true") - parser.add_argument("--metadata", action="store_true") - parser.add_argument("-f", "--file", nargs=1) - parser.add_argument("--lang", nargs="?", default="en") - args = vars(parser.parse_args()) - - if args["metadata"]: - print(json.dumps(getMetaData())) - elif args["display_name"]: - name = getMetaData().get("name") - if name: - print(name) - else: - sys.exit("ANI-2x is unavailable") - elif args["file"]: - run(args["file"][0]) diff --git a/avogadro/qtplugins/forcefield/scripts/gaff.py b/avogadro/qtplugins/forcefield/scripts/gaff.py deleted file mode 100644 index 491c66d1f6..0000000000 --- a/avogadro/qtplugins/forcefield/scripts/gaff.py +++ /dev/null @@ -1,88 +0,0 @@ -# This source file is part of the Avogadro project. -# This source code is released under the 3-Clause BSD License, (see "LICENSE"). - -import argparse -import json -import sys - -try: - from openbabel import pybel - import numpy as np - - imported = True -except ImportError: - imported = False - - -def getMetaData(): - # before we return metadata, make sure xtb is in the path - if not imported: - return {} # Avogadro will ignore us now - - metaData = { - "name": "GAFF", - "identifier": "GAFF", - "description": "Calculate GAFF energies and gradients", - "inputFormat": "cml", - "elements": "1,6-9,14-17,35,53", - "unitCell": False, - "gradients": True, - "ion": False, - "radical": False, - } - return metaData - - -def run(filename): - # we get the molecule from the supplied filename - # in cjson format (it's a temporary file created by Avogadro) - mol = next(pybel.readfile("cml", filename)) - - ff = pybel._forcefields["gaff"] - success = ff.Setup(mol.OBMol) - if not success: - # should never happen, but just in case - sys.exit("GAFF setup failed") - - # we loop forever - Avogadro will kill the process when done - num_atoms = len(mol.atoms) - while True: - # read new coordinates from stdin - for i in range(num_atoms): - coordinates = np.fromstring(input(), sep=" ") - atom = mol.atoms[i] - atom.OBAtom.SetVector(coordinates[0], coordinates[1], coordinates[2]) - - # update the molecule geometry for the next energy - ff.SetCoordinates(mol.OBMol) - - # first print the energy of these coordinates - energy = ff.Energy(True) # in kJ/mol - print("AvogadroEnergy:", energy) # in kJ/mol - - # now print the gradient on each atom - print("AvogadroGradient:") - for atom in mol.atoms: - grad = ff.GetGradient(atom.OBAtom) - print(-1.0*grad.GetX(), -1.0*grad.GetY(), -1.0*grad.GetZ()) - - - -if __name__ == "__main__": - parser = argparse.ArgumentParser("GAFF calculator") - parser.add_argument("--display-name", action="store_true") - parser.add_argument("--metadata", action="store_true") - parser.add_argument("-f", "--file", nargs=1) - parser.add_argument("--lang", nargs="?", default="en") - args = vars(parser.parse_args()) - - if args["metadata"]: - print(json.dumps(getMetaData())) - elif args["display_name"]: - name = getMetaData().get("name") - if name: - print(name) - else: - sys.exit("pybel is unavailable") - elif args["file"]: - run(args["file"][0]) diff --git a/avogadro/qtplugins/forcefield/scripts/gfn1.py b/avogadro/qtplugins/forcefield/scripts/gfn1.py deleted file mode 100644 index fdb5a5fb1f..0000000000 --- a/avogadro/qtplugins/forcefield/scripts/gfn1.py +++ /dev/null @@ -1,104 +0,0 @@ -# This source file is part of the Avogadro project. -# This source code is released under the 3-Clause BSD License, (see "LICENSE"). - -import argparse -import json -import sys - -try: - import numpy as np - from xtb.libxtb import VERBOSITY_MUTED - from xtb.interface import Calculator, Param - imported = True -except ImportError: - imported = False - -def getMetaData(): - # before we return metadata, make sure xtb is in the path - if not imported: - return {} # Avogadro will ignore us now - - metaData = { - "name": "GFN1", - "identifier": "GFN1", - "description": "Calculate GFN1-xtb energies and gradients", - "inputFormat": "cjson", - "elements": "1-86", - "unitCell": False, - "gradients": True, - "ion": True, - "radical": True, - } - return metaData - - -def run(filename): - # we get the molecule from the supplied filename - # in cjson format (it's a temporary file created by Avogadro) - with open(filename, "r") as f: - mol_cjson = json.load(f) - - # first setup the calculator - atoms = np.array(mol_cjson["atoms"]["elements"]["number"]) - coord_list = mol_cjson["atoms"]["coords"]["3d"] - coordinates = np.array(coord_list, dtype=float).reshape(-1, 3) - # .. we need to convert from Angstrom to Bohr - coordinates /= 0.52917721067 - - # check for total charge - # and spin multiplicity - charge = None # neutral - spin = None # singlet - if "properties" in mol_cjson: - if "totalCharge" in mol_cjson["properties"]: - charge = mol_cjson["properties"]["totalCharge"] - if "totalSpinMultiplicity" in mol_cjson["properties"]: - spin = mol_cjson["properties"]["totalSpinMultiplicity"] - - calc = Calculator(Param.GFN1xTB, atoms, coordinates, - charge=charge, uhf=spin) - calc.set_verbosity(VERBOSITY_MUTED) - res = calc.singlepoint() - - # we loop forever - Avogadro will kill the process when done - while(True): - # read new coordinates from stdin - for i in range(len(atoms)): - coordinates[i] = np.fromstring(input()) - # .. convert from Angstrom to Bohr - coordinates /= 0.52917721067 - - # update the calculator and run a new calculation - calc.update(coordinates) - calc.singlepoint(res) - - # first print the energy of these coordinates - print("AvogadroEnergy:", res.get_energy()) # in Hartree - - # now print the gradient - # .. we don't want the "[]" in the output - print("AvogadroGradient:") - grad = res.get_gradient() * 4961.475 # convert units - output = np.array2string(grad) - output = output.replace("[", "").replace("]", "") - print(output) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser("GFN1 calculator") - parser.add_argument("--display-name", action="store_true") - parser.add_argument("--metadata", action="store_true") - parser.add_argument("-f", "--file", nargs=1) - parser.add_argument("--lang", nargs="?", default="en") - args = vars(parser.parse_args()) - - if args["metadata"]: - print(json.dumps(getMetaData())) - elif args["display_name"]: - name = getMetaData().get("name") - if name: - print(name) - else: - sys.exit("xtb-python is unavailable") - elif args["file"]: - run(args["file"][0]) diff --git a/avogadro/qtplugins/forcefield/scripts/gfn2.py b/avogadro/qtplugins/forcefield/scripts/gfn2.py deleted file mode 100644 index 7b67c67c7e..0000000000 --- a/avogadro/qtplugins/forcefield/scripts/gfn2.py +++ /dev/null @@ -1,104 +0,0 @@ -# This source file is part of the Avogadro project. -# This source code is released under the 3-Clause BSD License, (see "LICENSE"). - -import argparse -import json -import sys - -try: - import numpy as np - from xtb.libxtb import VERBOSITY_MUTED - from xtb.interface import Calculator, Param - imported = True -except ImportError: - imported = False - -def getMetaData(): - # before we return metadata, make sure xtb is in the path - if not imported: - return {} # Avogadro will ignore us now - - metaData = { - "name": "GFN2", - "identifier": "GFN2", - "description": "Calculate GFN2-xtb energies and gradients", - "inputFormat": "cjson", - "elements": "1-86", - "unitCell": False, - "gradients": True, - "ion": True, - "radical": True, - } - return metaData - - -def run(filename): - # we get the molecule from the supplied filename - # in cjson format (it's a temporary file created by Avogadro) - with open(filename, "r") as f: - mol_cjson = json.load(f) - - # first setup the calculator - atoms = np.array(mol_cjson["atoms"]["elements"]["number"]) - coord_list = mol_cjson["atoms"]["coords"]["3d"] - coordinates = np.array(coord_list, dtype=float).reshape(-1, 3) - # .. we need to convert from Angstrom to Bohr - coordinates /= 0.52917721067 - - # check for total charge - # and spin multiplicity - charge = None # neutral - spin = None # singlet - if "properties" in mol_cjson: - if "totalCharge" in mol_cjson["properties"]: - charge = mol_cjson["properties"]["totalCharge"] - if "totalSpinMultiplicity" in mol_cjson["properties"]: - spin = mol_cjson["properties"]["totalSpinMultiplicity"] - - calc = Calculator(Param.GFN2xTB, atoms, coordinates, - charge=charge, uhf=spin) - calc.set_verbosity(VERBOSITY_MUTED) - res = calc.singlepoint() - - # we loop forever - Avogadro will kill the process when done - while(True): - # read new coordinates from stdin - for i in range(len(atoms)): - coordinates[i] = np.fromstring(input()) - # .. convert from Angstrom to Bohr - coordinates /= 0.52917721067 - - # update the calculator and run a new calculation - calc.update(coordinates) - calc.singlepoint(res) - - # first print the energy of these coordinates - print("AvogadroEnergy:", res.get_energy()) # in Hartree - - # now print the gradient - # .. we don't want the "[]" in the output - print("AvogadroGradient:") - grad = res.get_gradient() * 4961.475 # convert units - output = np.array2string(grad) - output = output.replace("[", "").replace("]", "") - print(output) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser("GFN2 calculator") - parser.add_argument("--display-name", action="store_true") - parser.add_argument("--metadata", action="store_true") - parser.add_argument("-f", "--file", nargs=1) - parser.add_argument("--lang", nargs="?", default="en") - args = vars(parser.parse_args()) - - if args["metadata"]: - print(json.dumps(getMetaData())) - elif args["display_name"]: - name = getMetaData().get("name") - if name: - print(name) - else: - sys.exit("xtb-python is unavailable") - elif args["file"]: - run(args["file"][0]) diff --git a/avogadro/qtplugins/forcefield/scripts/gfnff.py b/avogadro/qtplugins/forcefield/scripts/gfnff.py deleted file mode 100644 index ddb0d44596..0000000000 --- a/avogadro/qtplugins/forcefield/scripts/gfnff.py +++ /dev/null @@ -1,154 +0,0 @@ -# This source file is part of the Avogadro project. -# This source code is released under the 3-Clause BSD License, (see "LICENSE"). - -import argparse -import json -import sys -import os - -try: - import numpy as np - from xtb.libxtb import VERBOSITY_MUTED - from xtb.interface import Calculator, Param, Environment - - imported = True -except ImportError: - imported = False - -# we need to redirect stdout -# this is from https://eli.thegreenplace.net/2015/redirecting-all-kinds-of-stdout-in-python/ -from contextlib import contextmanager -import ctypes -import io -import tempfile - -libc = ctypes.CDLL(None) - -@contextmanager -def stdout_redirector(stream): - # The original fd stdout points to. Usually 1 on POSIX systems. - original_stdout_fd = sys.stdout.fileno() - - def _redirect_stdout(to_fd): - """Redirect stdout to the given file descriptor.""" - # Flush the C-level buffer stdout - #libc.fflush(c_stdout) - # Flush and close sys.stdout - also closes the file descriptor (fd) - sys.stdout.close() - # Make original_stdout_fd point to the same file as to_fd - os.dup2(to_fd, original_stdout_fd) - # Create a new sys.stdout that points to the redirected fd - sys.stdout = io.TextIOWrapper(os.fdopen(original_stdout_fd, 'wb')) - - # Save a copy of the original stdout fd in saved_stdout_fd - saved_stdout_fd = os.dup(original_stdout_fd) - try: - # Create a temporary file and redirect stdout to it - tfile = tempfile.TemporaryFile(mode='w+b') - _redirect_stdout(tfile.fileno()) - # Yield to caller, then redirect stdout back to the saved fd - yield - _redirect_stdout(saved_stdout_fd) - # Copy contents of temporary file to the given stream - tfile.flush() - tfile.seek(0, io.SEEK_SET) - stream.write(tfile.read()) - finally: - tfile.close() - os.close(saved_stdout_fd) - - -def getMetaData(): - # before we return metadata, make sure xtb is in the path - if not imported: - return {} # Avogadro will ignore us now - - metaData = { - "name": "GFN-FF", - "identifier": "GFN-FF", - "description": "Calculate GFNFF-xtb energies and gradients", - "inputFormat": "cjson", - "elements": "1-86", - "unitCell": False, - "gradients": True, - "ion": True, - "radical": False, - } - return metaData - - -def run(filename): - # we get the molecule from the supplied filename - # in cjson format (it's a temporary file created by Avogadro) - with open(filename, "r") as f: - mol_cjson = json.load(f) - - # first setup the calculator - atoms = np.array(mol_cjson["atoms"]["elements"]["number"]) - coord_list = mol_cjson["atoms"]["coords"]["3d"] - coordinates = np.array(coord_list, dtype=float).reshape(-1, 3) - # .. we need to convert from Angstrom to Bohr - coordinates /= 0.52917721067 - - # check for total charge - # and spin multiplicity - charge = None # neutral - spin = None # singlet - if "properties" in mol_cjson: - if "totalCharge" in mol_cjson["properties"]: - charge = mol_cjson["properties"]["totalCharge"] - if "totalSpinMultiplicity" in mol_cjson["properties"]: - spin = mol_cjson["properties"]["totalSpinMultiplicity"] - - # xtb doesn't properly mute - # so we redirect stdout to a StringIO - # and then just ignore it - f = io.BytesIO() - with stdout_redirector(f): - calc = Calculator(Param.GFNFF, atoms, coordinates, - charge=charge, uhf=spin) - calc.set_verbosity(VERBOSITY_MUTED) - res = calc.singlepoint() - - # we loop forever - Avogadro will kill our process when done - while True: - # read new coordinates from stdin - for i in range(len(atoms)): - coordinates[i] = np.fromstring(input(), sep=' ') - # .. convert from Angstrom to Bohr - coordinates /= 0.52917721067 - - # update the calculator and run a new calculation - calc.update(coordinates) - calc.singlepoint(res) - - print("AvogadroEnergy:", res.get_energy()) # in Hartree - # times 2625.5 kJ/mol - - # now print the gradient - # .. we don't want the "[]" in the output - print("AvogadroGradient:") - grad = res.get_gradient() * 4961.475 # convert units - output = np.array2string(grad) - output = output.replace("[", "").replace("]", "") - print(output) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser("GFN-FF calculator") - parser.add_argument("--display-name", action="store_true") - parser.add_argument("--metadata", action="store_true") - parser.add_argument("-f", "--file", nargs=1) - parser.add_argument("--lang", nargs="?", default="en") - args = vars(parser.parse_args()) - - if args["metadata"]: - print(json.dumps(getMetaData())) - elif args["display_name"]: - name = getMetaData().get("name") - if name: - print(name) - else: - sys.exit("xtb-python is unavailable") - elif args["file"]: - run(args["file"][0]) diff --git a/avogadro/qtplugins/forcefield/scripts/mmff94.py b/avogadro/qtplugins/forcefield/scripts/mmff94.py deleted file mode 100644 index 3206123c40..0000000000 --- a/avogadro/qtplugins/forcefield/scripts/mmff94.py +++ /dev/null @@ -1,88 +0,0 @@ -# This source file is part of the Avogadro project. -# This source code is released under the 3-Clause BSD License, (see "LICENSE"). - -import argparse -import json -import sys - -try: - from openbabel import pybel - import numpy as np - - imported = True -except ImportError: - imported = False - - -def getMetaData(): - # before we return metadata, make sure xtb is in the path - if not imported: - return {} # Avogadro will ignore us now - - metaData = { - "name": "MMFF94", - "identifier": "MMFF94", - "description": "Calculate MMFF94 energies and gradients", - "inputFormat": "cml", - "elements": "1,6-9,14-17,35,53", - "unitCell": False, - "gradients": True, - "ion": False, - "radical": False, - } - return metaData - - -def run(filename): - # we get the molecule from the supplied filename - # in cjson format (it's a temporary file created by Avogadro) - mol = next(pybel.readfile("cml", filename)) - - ff = pybel._forcefields["mmff94"] - success = ff.Setup(mol.OBMol) - if not success: - # should never happen, but just in case - sys.exit("MMFF94 force field setup failed") - - # we loop forever - Avogadro will kill the process when done - num_atoms = len(mol.atoms) - while True: - # read new coordinates from stdin - for i in range(num_atoms): - coordinates = np.fromstring(input(), sep=" ") - atom = mol.atoms[i] - atom.OBAtom.SetVector(coordinates[0], coordinates[1], coordinates[2]) - - # update the molecule geometry for the next energy - ff.SetCoordinates(mol.OBMol) - - # first print the energy of these coordinates - energy = ff.Energy(True) # in kJ/mol - print("AvogadroEnergy:", energy) # in kJ/mol - - # now print the gradient on each atom - print("AvogadroGradient:") - for atom in mol.atoms: - grad = ff.GetGradient(atom.OBAtom) - print(-1.0*grad.GetX(), -1.0*grad.GetY(), -1.0*grad.GetZ()) - - - -if __name__ == "__main__": - parser = argparse.ArgumentParser("MMFF94 calculator") - parser.add_argument("--display-name", action="store_true") - parser.add_argument("--metadata", action="store_true") - parser.add_argument("-f", "--file", nargs=1) - parser.add_argument("--lang", nargs="?", default="en") - args = vars(parser.parse_args()) - - if args["metadata"]: - print(json.dumps(getMetaData())) - elif args["display_name"]: - name = getMetaData().get("name") - if name: - print(name) - else: - sys.exit("pybel is unavailable") - elif args["file"]: - run(args["file"][0]) diff --git a/avogadro/qtplugins/forcefield/scripts/uff.py b/avogadro/qtplugins/forcefield/scripts/uff.py deleted file mode 100644 index 6c5c926be5..0000000000 --- a/avogadro/qtplugins/forcefield/scripts/uff.py +++ /dev/null @@ -1,87 +0,0 @@ -# This source file is part of the Avogadro project. -# This source code is released under the 3-Clause BSD License, (see "LICENSE"). - -import argparse -import json -import sys - -try: - from openbabel import pybel - import numpy as np - - imported = True -except ImportError: - imported = False - - -def getMetaData(): - # before we return metadata, make sure xtb is in the path - if not imported: - return {} # Avogadro will ignore us now - - metaData = { - "name": "UFF", - "identifier": "UFF", - "description": "Calculate UFF energies and gradients", - "inputFormat": "cml", - "elements": "1-86", - "unitCell": False, - "gradients": True, - "ion": False, - "radical": False, - } - return metaData - - -def run(filename): - # we get the molecule from the supplied filename - # in cjson format (it's a temporary file created by Avogadro) - mol = next(pybel.readfile("cml", filename)) - - ff = pybel._forcefields["uff"] - success = ff.Setup(mol.OBMol) - if not success: - # should never happen, but just in case - sys.exit("UFF force field setup failed") - - # we loop forever - Avogadro will kill the process when done - num_atoms = len(mol.atoms) - while True: - # read new coordinates from stdin - for i in range(num_atoms): - coordinates = np.fromstring(input(), sep=" ") - atom = mol.atoms[i] - atom.OBAtom.SetVector(coordinates[0], coordinates[1], coordinates[2]) - - # update the molecule geometry for the next energy - ff.SetCoordinates(mol.OBMol) - - # first print the energy of these coordinates - energy = ff.Energy(True) # in kJ/mol - print("AvogadroEnergy:", energy) # in kJ/mol - - # now print the gradient on each atom - print("AvogadroGradient:") - for atom in mol.atoms: - grad = ff.GetGradient(atom.OBAtom) - print(-1.0*grad.GetX(), -1.0*grad.GetY(), -1.0*grad.GetZ()) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser("UFF calculator") - parser.add_argument("--display-name", action="store_true") - parser.add_argument("--metadata", action="store_true") - parser.add_argument("-f", "--file", nargs=1) - parser.add_argument("--lang", nargs="?", default="en") - args = vars(parser.parse_args()) - - if args["metadata"]: - print(json.dumps(getMetaData())) - elif args["display_name"]: - name = getMetaData().get("name") - if name: - print(name) - else: - sys.exit("pybel is unavailable") - elif args["file"]: - run(args["file"][0]) diff --git a/avogadro/qtplugins/scriptcharges/CMakeLists.txt b/avogadro/qtplugins/scriptcharges/CMakeLists.txt index 4941a7b9bd..fe477d6316 100644 --- a/avogadro/qtplugins/scriptcharges/CMakeLists.txt +++ b/avogadro/qtplugins/scriptcharges/CMakeLists.txt @@ -14,11 +14,4 @@ avogadro_plugin(ScriptCharges target_link_libraries(ScriptCharges PRIVATE Avogadro::Calc ) -# Bundled format scripts: -set(charge_scripts - chargeScripts/xtb.py - chargeScripts/antechamber.py -) - -install(PROGRAMS ${charge_scripts} - DESTINATION "${INSTALL_LIBRARY_DIR}/avogadro2/scripts/charges/") +# charge scripts are now download only diff --git a/avogadro/qtplugins/scriptcharges/chargeScripts/antechamber.py b/avogadro/qtplugins/scriptcharges/chargeScripts/antechamber.py deleted file mode 100644 index f902abc8a3..0000000000 --- a/avogadro/qtplugins/scriptcharges/chargeScripts/antechamber.py +++ /dev/null @@ -1,155 +0,0 @@ -# This source file is part of the Avogadro project. -# This source code is released under the 3-Clause BSD License, (see "LICENSE"). - -# This assigns charges using AM1-BCC from antechamber -# .. which is released under the GPL license - -import argparse -import json -import sys -import os -from shutil import which -import tempfile -import subprocess - - -def getMetaData(): - # before we return metadata, make sure antechamber is in the path - if which("antechamber") is None: - return {} # Avogadro will ignore us now - - metaData = {} - metaData["inputFormat"] = "sdf" # could be other formats, but this is fine - metaData["identifier"] = "AM1BCC" - metaData["name"] = "AM1-BCC" - metaData["description"] = "Calculate atomic partial charges using AM1-BCC" - metaData["charges"] = True - metaData["potential"] = False - metaData[ - "elements" - ] = "1,6,7,8,9,14,15,16,17,35,53" # H, C, N, O, F, Si, S, P, Cl, Br, I - return metaData - - -def charges(): - # Avogadro will send us the sdf file as stdin - # we need to write it to a temporary file - - # get the whole sdf file - sdf = sys.stdin.read() - - fd, name = tempfile.mkstemp(".sdf") - os.write(fd, sdf.encode()) - os.close(fd) - - # run xtb - binary = which("antechamber") - if binary is None: # we check again - return "" - - # for now, ignore the output itself - tempdir = tempfile.mkdtemp() - lig1 = tempdir + "/" + "lig1.mol2" - lig2 = tempdir + "/" + "lig2.mol2" - output = subprocess.run( - [ - binary, - "-i", - name, - "-fi", - "sdf", - "-o", - lig1, - "-fo", - "mol2", - "-c", - "bcc", - "-pf", - "y", - "-ek", - "maxcyc=0, qm_theory='AM1', scfconv=1.d-10, ndiis_attempts=700," - ], - stdout=subprocess.PIPE, - cwd=tempdir, - check=True, - ) - output = subprocess.run( - [ - binary, - "-i", - lig1, - "-fi", - "mol2", - "-o", - lig2, - "-fo", - "mol2", - "-c", - "wc", - "-cf", - "charges", - "-pf", - "y", - ], - stdout=subprocess.PIPE, - cwd=tempdir, - check=True, - ) - # instead we read the "charges.txt" file - result = "" - with open(tempdir + "/" + "charges", "r", encoding="utf-8") as f: - # we get lines with multiple charges per line - for line in f: - charges = line.split() - for charge in charges: - result += charge + "\n" - - # try to cleanup the temporary files - os.remove(name) - for filename in os.listdir(tempdir): - try: - os.remove(tempdir + "/" + filename) - except: - continue - # and try to cleanup the directory - try: - os.rmdir(tempdir) - except: - pass - - # write the charges to stdout - return result - - -def potential(): - # The default will calculate ESP from the partial charges - - # if your plugin has a potential, you can return it here - # .. you'll get JSON with the file and the set of points - # e.g. { "xyz" : "xyz file contents", "points" : [ x,y,z, x,y,z, ... ] } - # or { "sdf" : "sdf file contents", "points" : [ x,y,z, x,y,z, ... ] } - # .. and you print the list of potentials to stdout - return "" - - -if __name__ == "__main__": - parser = argparse.ArgumentParser("AM1-BCC partial charges") - parser.add_argument("--display-name", action="store_true") - parser.add_argument("--metadata", action="store_true") - parser.add_argument("--charges", action="store_true") - parser.add_argument("--potential", action="store_true") - parser.add_argument("--lang", nargs="?", default="en") - args = vars(parser.parse_args()) - - if args["metadata"]: - print(json.dumps(getMetaData())) - elif args["display_name"]: - name = getMetaData().get("name") - if name: - print(name) - else: - sys.exit("antechamber is unavailable") - elif args["charges"]: - print(charges()) - elif args["potential"]: - print(potential()) diff --git a/avogadro/qtplugins/scriptcharges/chargeScripts/xtb.py b/avogadro/qtplugins/scriptcharges/chargeScripts/xtb.py deleted file mode 100644 index 5f6f181939..0000000000 --- a/avogadro/qtplugins/scriptcharges/chargeScripts/xtb.py +++ /dev/null @@ -1,104 +0,0 @@ -# This source file is part of the Avogadro project. -# This source code is released under the 3-Clause BSD License, (see "LICENSE"). - -import argparse -import json -import sys -import os -from shutil import which -import tempfile -import subprocess - - -def getMetaData(): - # before we return metadata, make sure xtb is in the path - if which("xtb") is None: - return {} # Avogadro will ignore us now - - metaData = {} - metaData["inputFormat"] = "mol" # could be other formats, but this is fine - metaData["identifier"] = "GFN2" - metaData["name"] = "GFN2" - metaData["description"] = "Calculate atomic partial charges using GFN2 and xtb" - metaData["charges"] = True - metaData["potential"] = False - metaData["elements"] = "1-86" # up to Radon - return metaData - - -def charges(): - # Avogadro will send us the mol file as stdin - # we need to write it to a temporary file - - # get the whole file - mol = sys.stdin.read() - - fd, name = tempfile.mkstemp(".mol") - os.write(fd, mol.encode()) - os.close(fd) - - # run xtb - xtb = which("xtb") - if xtb is None: # we check again - return "" - - # for now, ignore the output itself - tempdir = tempfile.mkdtemp() - output = subprocess.run( - [xtb, name], stdout=subprocess.PIPE, cwd=tempdir, check=True - ) - # instead we read the "charges" file - result = "" - with open(tempdir + "/" + "charges", "r", encoding="utf-8") as f: - result = f.read() - - # try to cleanup the temporary files - os.remove(name) - for filename in os.listdir(tempdir): - try: - os.remove(tempdir + "/" + filename) - except: - continue - # and try to cleanup the directory - try: - os.rmdir(tempdir) - except: - pass - - # write the charges to stdout - return result - - -def potential(): - # at the moment, xtb doesn't have a good way to do this - # and the method shouldn't be called anyway - - # if your plugin has a potential, you can return it here - # .. you'll get JSON with the file and the set of points - # e.g. { "xyz" : "xyz file contents", "points" : [ x,y,z, x,y,z, ... ] } - # or { "sdf" : "sdf file contents", "points" : [ x,y,z, x,y,z, ... ] } - # .. and you print the list of potentials to stdout - return "" - - -if __name__ == "__main__": - parser = argparse.ArgumentParser("GFN2 partial charges") - parser.add_argument("--display-name", action="store_true") - parser.add_argument("--metadata", action="store_true") - parser.add_argument("--charges", action="store_true") - parser.add_argument("--potential", action="store_true") - parser.add_argument("--lang", nargs="?", default="en") - args = vars(parser.parse_args()) - - if args["metadata"]: - print(json.dumps(getMetaData())) - elif args["display_name"]: - name = getMetaData().get("name") - if name: - print(name) - else: - sys.exit("xtb is unavailable") - elif args["charges"]: - print(charges()) - elif args["potential"]: - print(potential())