From 4350219b55e4ebbc65f402f2153ddf41bddef097 Mon Sep 17 00:00:00 2001 From: Juanvi Alegre-Requena Date: Sun, 5 Mar 2023 18:30:40 +0100 Subject: [PATCH 1/3] 1. Changing PATH in files and input options --- aqme/cmin.py | 24 ++-- aqme/csearch/base.py | 17 +-- aqme/csearch/utils.py | 39 ++++-- aqme/qcorr.py | 9 +- aqme/qdescp.py | 9 +- aqme/qprep.py | 7 +- aqme/utils.py | 82 +++++++++--- docs/Misc/versions.rst | 69 +++++----- setup.py | 10 +- tests/csearch_input/pentane_com.xyz | 19 --- tests/csearch_input/pentane_gjf.xyz | 19 --- tests/test_cmin.py | 24 +++- tests/test_csearch.py | 191 ++++++++++------------------ 13 files changed, 242 insertions(+), 277 deletions(-) delete mode 100644 tests/csearch_input/pentane_com.xyz delete mode 100644 tests/csearch_input/pentane_gjf.xyz diff --git a/aqme/cmin.py b/aqme/cmin.py index 663470bd..4f635c86 100644 --- a/aqme/cmin.py +++ b/aqme/cmin.py @@ -101,9 +101,10 @@ import time from aqme.utils import ( load_variables, - substituted_mol, mol_from_sdf_or_mol_or_mol2, - add_prefix_suffix + add_prefix_suffix, + check_files, + check_xtb ) from aqme.filter import ewin_filter, pre_E_filter, RMSD_and_E_filter from aqme.cmin_utils import creation_of_dup_csv_cmin @@ -147,10 +148,7 @@ def __init__(self, **kwargs): os.chdir(self.args.w_dir_main) # retrieves the different files to run in CMIN - if len(self.args.files) == 0: - self.args.log.write('\nx No files were found! Make sure you use quotation marks if you are using * (i.e. --files "*.sdf")') - self.args.log.finalize() - sys.exit() + _ = check_files(self,'cmin') # create the dataframe to store the data self.final_dup_data = creation_of_dup_csv_cmin(self.args.program.lower()) @@ -277,6 +275,8 @@ def compute_cmin(self, file): charge,mult,final_mult,dup_data = self.charge_mult_cmin(dup_data, dup_data_idx) elif self.args.program.lower() == "xtb": + # checks if xTB is installed + _ = self.get_cmin_model() # sets charge and mult file_format = os.path.splitext(file)[1] charge_input, mult_input, final_mult = None, None, None @@ -431,7 +431,7 @@ def compute_cmin(self, file): # xTB AND ANI MAIN OPTIMIZATION PROCESS def ani_optimize(self, mol, charge, mult): - # Attempts ANI/xTB imports and exits if the programs are not installed + # Attempts ANI imports and exits if the programs are not installed try: import torch import warnings @@ -527,15 +527,7 @@ def get_cmin_model(self): model = getattr(torchani.models,self.args.ani_method)() elif self.args.program.lower() == "xtb": - try: - subprocess.run( - ["xtb", "-h"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL - ) - except FileNotFoundError: - self.args.log.write("x xTB is not installed (CREST cannot be used)! You can install the program with 'conda install -c conda-forge xtb'") - self.args.log.finalize() - sys.exit() - + _ = check_xtb(self) model = None return model diff --git a/aqme/csearch/base.py b/aqme/csearch/base.py index 6c80ab3e..16abf2bd 100644 --- a/aqme/csearch/base.py +++ b/aqme/csearch/base.py @@ -206,7 +206,9 @@ from aqme.utils import ( substituted_mol, load_variables, - set_metal_atomic_number + set_metal_atomic_number, + check_xtb, + get_files ) from aqme.csearch.crest import xtb_opt_main @@ -239,14 +241,7 @@ def __init__(self, **kwargs): self.args.auto_metal_atoms = False if self.args.program.lower() == "crest": - try: - subprocess.run( - ["xtb", "-h"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL - ) - except FileNotFoundError: - self.args.log.write("x xTB is not installed (CREST cannot be used)! You can install the program with 'conda install -c conda-forge xtb'") - self.args.log.finalize() - sys.exit() + _ = check_xtb(self) if self.args.smi is None and self.args.input == "": self.args.log.write("\nx Program requires either a SMILES or an input file to proceed! Please look up acceptable file formats. Specify: smi='CCC' (or input='filename.csv')") @@ -271,7 +266,7 @@ def __init__(self, **kwargs): if self.args.smi is not None: csearch_files = [self.args.name] else: - csearch_files = glob.glob(self.args.input) + csearch_files = get_files(self.args.input) if len(csearch_files) == 0: self.args.log.write(f"\nx Input file ({self.args.input}) not found!") self.args.log.finalize() @@ -292,7 +287,7 @@ def __init__(self, **kwargs): # store all the information into a CSV file csearch_file_no_path = ( - csearch_file.replace("/", "\\").split("\\")[-1].split(".")[0] + os.path.basename(csearch_file).split(".")[0] ) self.csearch_csv_file = self.args.w_dir_main.joinpath( f"CSEARCH-Data-{csearch_file_no_path}.csv" diff --git a/aqme/csearch/utils.py b/aqme/csearch/utils.py index 1d0e9685..917fd2d3 100644 --- a/aqme/csearch/utils.py +++ b/aqme/csearch/utils.py @@ -261,7 +261,7 @@ def prepare_cdx_files(args, csearch_file): job_inputs = [] for i, (smiles, _) in enumerate(molecules): - name = f"{csearch_file.split('.')[0]}_{str(i)}" + name = f"{os.path.basename(csearch_file).split('.')[0]}_{str(i)}" name = add_prefix_suffix(name, args) obj = ( @@ -294,19 +294,22 @@ def generate_mol_from_cdx(csearch_file): def prepare_com_files(args, csearch_file): job_inputs = [] - if csearch_file.split(".")[1] in ["gjf", "com"]: + filename = os.path.basename(csearch_file) + if filename.split('.')[1] in ["gjf", "com"]: xyz_file, _, _ = com_2_xyz(csearch_file) _, charge, mult = get_info_input(csearch_file) else: xyz_file = csearch_file charge, mult = read_xyz_charge_mult(xyz_file) - xyz_2_sdf(xyz_file) - name = os.path.splitext(csearch_file)[0] - name = add_prefix_suffix(name, args) + _ = xyz_2_sdf(xyz_file) + + sdffile = f'{os.path.dirname(csearch_file)}/{filename.split(".")[0]}.sdf' - sdffile = f"{os.path.splitext(csearch_file)[0]}.sdf" suppl, _, _, _ = mol_from_sdf_or_mol_or_mol2(sdffile, "csearch") + name = filename.split('.')[0] + name = add_prefix_suffix(name, args) + obj = ( suppl[0], name, @@ -318,28 +321,37 @@ def prepare_com_files(args, csearch_file): args.constraints_dihedral, ) job_inputs.append(obj) + if os.path.basename(csearch_file).split('.')[1] in ["gjf", "com"]: + os.remove(xyz_file) + os.remove(sdffile) return job_inputs def prepare_pdb_files(args, csearch_file): + filename = os.path.basename(csearch_file) + sdffile = f'{os.path.dirname(csearch_file)}/{filename.split(".")[0]}.sdf' command_pdb = [ "obabel", "-ipdb", csearch_file, "-osdf", - f'-O{csearch_file.split(".")[0]}.sdf', + f'-O{sdffile}', ] subprocess.run(command_pdb, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) job_inputs = prepare_sdf_files(args, csearch_file) - os.remove(f'{csearch_file.split(".")[0]}.sdf') + os.remove(sdffile) return job_inputs def prepare_sdf_files(args, csearch_file): - suppl, charges, mults, IDs = mol_from_sdf_or_mol_or_mol2(csearch_file, "csearch") - job_inputs = [] + filename = os.path.basename(csearch_file) + sdffile = f'{os.path.dirname(csearch_file)}/{filename}' + suppl, charges, mults, IDs = mol_from_sdf_or_mol_or_mol2(sdffile, "csearch") + if sdffile.split('.')[0] in ['mol','mol2']: + os.remove(f'{sdffile.split(".")[0]}.sdf') + job_inputs = [] for mol, charge, mult, name in zip(suppl, charges, mults, IDs): name = add_prefix_suffix(name, args) obj = ( @@ -391,15 +403,16 @@ def com_2_xyz(input_file): COM to XYZ to SDF for obabel """ - filename = input_file.split(".")[0] + filename = os.path.basename(input_file).split('.')[0] + path_xyz = f'{os.path.dirname(input_file)}/{filename}.xyz' # Create the 'xyz' file and/or get the total charge xyz, charge, mult = get_info_input(input_file) xyz_txt = "\n".join(xyz) - with open(f"{filename}.xyz", "w") as F: + with open(path_xyz, "w") as F: F.write(f"{len(xyz)}\n{filename}\n{xyz_txt}\n") - return f"{filename}.xyz", charge, mult + return path_xyz, charge, mult def minimize_rdkit_energy(mol, conf, log, FF, maxsteps): diff --git a/aqme/qcorr.py b/aqme/qcorr.py index fc91b700..6a7589af 100644 --- a/aqme/qcorr.py +++ b/aqme/qcorr.py @@ -82,6 +82,7 @@ load_variables, read_file, cclib_atoms_coords, + check_files ) from aqme.qcorr_utils import ( detect_linear, @@ -108,13 +109,11 @@ def __init__(self, **kwargs): # load default and user-specified variables self.args = load_variables(kwargs, "qcorr") - if len(self.args.files) == 0: - self.args.log.write('\nx No files were found! Make sure you use quotation marks if you are using * (i.e. --files "*.log")') - self.args.log.finalize() - sys.exit() + # retrieves the different files to run in QCORR + _ = check_files(self,'qcorr') # QCORR analysis - if self.args.files[0].split('.')[1].lower() not in ['log','out','json']: + if os.path.basename(self.args.files[0]).split('.')[1].lower() not in ['log','out','json']: self.args.log.write(f"\nx The format used ({self.args.files[0].split('.')[1].lower()}) is not compatible with QCORR! Formats accepted: log, out, json") self.args.log.finalize() sys.exit() diff --git a/aqme/qdescp.py b/aqme/qdescp.py index cffbddee..ff28bc29 100644 --- a/aqme/qdescp.py +++ b/aqme/qdescp.py @@ -85,7 +85,8 @@ load_variables, read_xyz_charge_mult, mol_from_sdf_or_mol_or_mol2, - run_command + run_command, + check_files ) from aqme.qdescp_utils import ( get_boltz_avg_properties_xtb, @@ -116,10 +117,8 @@ def __init__(self, **kwargs): else: destination = Path(self.args.destination) - if len(self.args.files) == 0: - self.args.log.write('\nx No files were found! Make sure you use quotation marks if you are using * (i.e. --files "*.sdf")') - self.args.log.finalize() - sys.exit() + # retrieves the different files to run in QDESCP + _ = check_files(self,'qdescp') qdescp_program = True if self.args.program is None: diff --git a/aqme/qprep.py b/aqme/qprep.py index 6929eff3..a411b867 100644 --- a/aqme/qprep.py +++ b/aqme/qprep.py @@ -61,6 +61,7 @@ read_xyz_charge_mult, mol_from_sdf_or_mol_or_mol2, add_prefix_suffix, + check_files ) from aqme.csearch.crest import xyzall_2_xyz from pathlib import Path @@ -77,10 +78,8 @@ def __init__(self, create_dat=True, **kwargs): # load default and user-specified variables self.args = load_variables(kwargs, "qprep", create_dat=create_dat) - if len(self.args.files) == 0: - self.args.log.write('\nx No files were found! Make sure you use quotation marks if you are using * (i.e. --files "*.sdf")') - self.args.log.finalize() - sys.exit() + # retrieves the different files to run in QPREP + _ = check_files(self,'qprep') file_format = os.path.splitext(self.args.files[0])[1].split('.')[1] if file_format.lower() not in ['sdf', 'xyz', 'pdb', 'log', 'out', 'json']: diff --git a/aqme/utils.py b/aqme/utils.py index 0d36c155..134c5d01 100644 --- a/aqme/utils.py +++ b/aqme/utils.py @@ -24,7 +24,7 @@ J_TO_AU = 4.184 * 627.509541 * 1000.0 # UNIT CONVERSION T = 298.15 -aqme_version = "1.4.4" +aqme_version = "1.4.6" time_run = time.strftime("%Y/%m/%d %H:%M:%S", time.localtime()) aqme_ref = f"AQME v {aqme_version}, Alegre-Requena, J. V.; Sowndarya, S.; Perez-Soto, R.; Alturaifi, T. M.; Paton, R. S., 2022. https://github.com/jvalegre/aqme" @@ -363,8 +363,9 @@ def command_line_args(): sys.exit() else: # this "if" allows to use * to select multiple files in multiple OS - if arg_name.lower() == 'files' and value.find('*') > -1: - kwargs[arg_name] = glob.glob(value) + if arg_name.lower() == 'files': + value = get_files(value) + kwargs[arg_name] = value else: # this converts the string parameters to lists if arg_name.lower() in ["files", "gen_atoms", "constraints_atoms", "constraints_dist", "constraints_angle", "constraints_dihedral", "atom_types", "cartesians", "nmr_atoms", "nmr_slope", "nmr_intercept"]: @@ -373,7 +374,7 @@ def command_line_args(): value = ast.literal_eval(value) except (SyntaxError, ValueError): # this line fixes issues when using "[X]" or ["X"] instead of "['X']" when using lists - if arg_name.lower() in ["files", "gen_atoms"]: + if arg_name.lower() in ["gen_atoms"]: value = value.replace('[',']').replace(',',']').split(']') while('' in value): value.remove('') @@ -401,9 +402,11 @@ def load_variables(kwargs, aqme_module, create_dat=True): self.initial_dir = Path(os.getcwd()) + # get PATH for the files option + self.files = get_files(self.files) + if not isinstance(self.files, list): self.w_dir_main = os.path.dirname(self.files) - check_files = os.path.basename(self.files) elif len(self.files) != 0: self.w_dir_main = os.path.dirname(self.files[0]) else: @@ -434,16 +437,6 @@ def load_variables(kwargs, aqme_module, create_dat=True): if error_setup: self.w_dir_main = Path(os.getcwd()) - - if not isinstance(self.files, list): - if not isinstance(self.files, Mol): - self.files = glob.glob(f"{self.w_dir_main}/{check_files}") - else: - self.files = [self.files] - # this function is useful when PATH objects are given - for i,file in enumerate(self.files): - if not isinstance(file, Mol): - self.files[i] = str(file) # start a log file to track the QCORR module if create_dat: @@ -710,6 +703,7 @@ def mol_from_sdf_or_mol_or_mol2(input_file, module): return suppl, charges, mults, IDs + def add_prefix_suffix(name, args): if args.prefix != "": name = f"{args.prefix}_{name}" @@ -717,3 +711,61 @@ def add_prefix_suffix(name, args): name += f'_{args.suffix}' return name + + +def check_files(self,module): + if module.lower() in ['cmin','qdescp','qprep']: + format_file = "*.sdf" + elif module.lower() in ['qcorr']: + format_file = "*.log" + no_file_found = False + if len(self.args.files) == 0: + self.args.log.write(f'\nx No files were found! In the "files" option, make sure that 1) the PATH to the files is correct, 2) the PATH doesn\'t start with "/", and 3) you use quotation marks if you are using * (i.e. --files "{format_file}")') + no_file_found = True + else: + for file in self.args.files: + if not os.path.exists(file): + no_file_found = True + self.args.log.write(f'\nx File {file} was not found! In the "files" option, make sure that 1) the PATH to the files is correct and 2) the PATH doesn\'t start with "/".') + break + if no_file_found: + self.args.log.finalize() + sys.exit() + + +def check_xtb(self): + try: + subprocess.run( + ["xtb", "-h"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL + ) + except FileNotFoundError: + self.args.log.write("x xTB is not installed (CMIN-xTB cannot be used)! You can install the program with 'conda install -c conda-forge xtb'") + self.args.log.finalize() + sys.exit() + + +def get_files(value): + if not isinstance(value, list): + value = value.replace('[',']').replace(',',']').split(']') + while('' in value): + value.remove('') + new_value = [] + for val in value: + if not isinstance(val, Path): + if ( + Path(f"{val}").exists() + and os.getcwd() not in f"{val}" + ): + new_value.append(Path(f"{os.getcwd()}/{val}")) + elif '*' in val: + if os.getcwd() not in f"{val}": + list_of_val = glob.glob(f"{os.getcwd()}/{val}") + else: + list_of_val = glob.glob(val) + for ele in list_of_val: + new_value.append(Path(ele)) + else: + new_value.append(Path(val)) + else: + new_value.append(val) + return new_value \ No newline at end of file diff --git a/docs/Misc/versions.rst b/docs/Misc/versions.rst index b721ffc1..d8ea195a 100644 --- a/docs/Misc/versions.rst +++ b/docs/Misc/versions.rst @@ -4,66 +4,73 @@ Versions ======== +Version 1.4.6 [`url `__] + - The files and input options are compatible with partial PATHs, full PATHs, and direct names + from command lines and Jupyter Notebooks + - The SUMM option was fixed in CSEARCH + - The files and input options now tolerate PATHs that contain directories with "." characters + Version 1.4.5 [`url `__] - Suffix/prefix options work in CSEARCH, CMIN and QPREP - Automatic recognition of metals with the auto_metal_atom option - - In QPREP, if qm_input starts with "p ", the Gaussian inputs starts with "#p" + - In QPREP, if qm_input starts with "p ", the Gaussian inputs starts with "#p" + - CSEARCH-CREST updates the CREST outfile as the program calculates (not at the end only) Version 1.4.4 [`url `__] - When using a CSV as input, the user can specify charge and mult for each species by - using the charge/mult columns - - QCORR now detects duplicates including the successful calculations from previous runs - - Fixed an error in full_check from QCORR when using genecp - - Admits lists in command lines specified as ["X"], "[X]" and '["X"]' + using the charge/mult columns + - QCORR now detects duplicates including the successful calculations from previous runs + - Fixed an error in full_check from QCORR when using genecp + - Admits lists in command lines specified as ["X"], "[X]" and '["X"]' Version 1.4.3 [`url `__] - - Return metal into RDKit mol object when using the metal_atoms option with CSEARCH-CREST + - Return metal into RDKit mol object when using the metal_atoms option with CSEARCH-CREST - Doubles bonds do not add extra charges in metal complexes when using the automated charge - calculation from SMILES + calculation from SMILES - Deprotonated SiR3 groups add -1 charge to metal complexes when using the automated charge - calculation from SMILES + calculation from SMILES Version 1.4.2 [`url `__] - - Fixed an error that raised when using CSEARCH-CREST with organic molecules - - Adding more information printed when running CSEARCH - - Updated README with citations from external programs + - Fixed an error that raised when using CSEARCH-CREST with organic molecules + - Adding more information printed when running CSEARCH + - Updated README with citations from external programs - Fixed a bug during filtering of xTB conformers in CMIN (using kcal/mol instead of Hartree - in the filters now) - - Writing CSEARCH-CREST conformers in kcal/mol instead of Hartrees + in the filters now) + - Writing CSEARCH-CREST conformers in kcal/mol instead of Hartrees - Templates are not active when using metals with different number of ligands - (i.e. if complex_type='linear' and Cu2+/CuL2 are used simultaneously) - - Fixed squarepyramidal templates + (i.e. if complex_type='linear' and Cu2+/CuL2 are used simultaneously) + - Fixed squarepyramidal templates Version 1.4.1 [`url `__] - Changed the way xTB works in CMIN. Before, it worked through xtb-python, but in this version xtb is called through the xTB external command. This change speeds up the - calculations and avoids problems for people that do not have xtb-python installed. - - Fixed some bugs in the PATHs when using AQME through command lines - - Updated information printed in QDESCP - - Adding more error prints when no program or files are specified + calculations and avoids problems for people that do not have xtb-python installed. + - Fixed some bugs in the PATHs when using AQME through command lines + - Updated information printed in QDESCP + - Adding more error prints when no program or files are specified Version 1.4.0 [`url `__] - - Fixed a bug in the automated charge and multiplicity detector for metal complexes - - Adapted CREST workflows to work with metal templates - - Refactored utils and rearrange files to meet code analyzer standards + - Fixed a bug in the automated charge and multiplicity detector for metal complexes + - Adapted CREST workflows to work with metal templates + - Refactored utils and rearrange files to meet code analyzer standards - The mol object that CREST uses as input now comes from the RDKit conformer generator (otherwise, metal templates aren't applied and - stereochemistry information might be lost) + stereochemistry information might be lost) Version 1.3.1 [`url `__] - - Workflows were updated - - Small fixes in CREST when using constraints - - Readme was updated - - GoodVibes added in installation requirements + - Workflows were updated + - Small fixes in CREST when using constraints + - Readme was updated + - GoodVibes added in installation requirements Version 1.3.0 [`url `__] - - Publication version + - Publication version Version 1.2.0 [`url `__] - - This version improves how AQME reads PATHs from arguments to make the program more robust + - This version improves how AQME reads PATHs from arguments to make the program more robust Version 1.1.0 [`url `__] - - Fixes pip install issue coming from older versions + - Fixes pip install issue coming from older versions Version 1.0.0 [`url `__] - - First official version of AQME ready to generate publication-quality results + - First official version of AQME ready to generate publication-quality results diff --git a/setup.py b/setup.py index 779f03b6..edb12387 100644 --- a/setup.py +++ b/setup.py @@ -1,14 +1,10 @@ from setuptools import setup, find_packages -import io - -# read the contents of your README file -from os import path - +version = "1.4.6" setup( name="aqme", packages=find_packages(exclude=["tests"]), package_data={"aqme": ["templates/*"]}, - version="1.4.4", + version=version, license="MIT", description="Automated Quantum Mechanical Environments", long_description="Documentation in Read The Docs: https://aqme.readthedocs.io", @@ -25,7 +21,7 @@ "automated", ], url="https://github.com/jvalegre/aqme", - download_url="https://github.com/jvalegre/aqme/archive/refs/tags/1.4.4.tar.gz", + download_url=f"https://github.com/jvalegre/aqme/archive/refs/tags/{version}.tar.gz", classifiers=[ "Development Status :: 5 - Production/Stable", # Chose either "3 - Alpha", "4 - Beta" or "5 - Production/Stable" as the current state of your package "Intended Audience :: Developers", # Define that your audience are developers diff --git a/tests/csearch_input/pentane_com.xyz b/tests/csearch_input/pentane_com.xyz deleted file mode 100644 index ddb4fce1..00000000 --- a/tests/csearch_input/pentane_com.xyz +++ /dev/null @@ -1,19 +0,0 @@ -17 -pentane_com -C -1.31982355 -0.25423777 -0.02060588 -H -0.96316913 -1.26304778 -0.02060588 -H -0.96315071 0.25016042 -0.89425738 -H -2.38982355 -0.25422459 -0.02060588 -C -0.80648133 0.47171850 1.23679909 -H -1.16313432 1.48052901 1.23679821 -H -1.16315561 -0.03267957 2.11045008 -C 0.73351867 0.47169768 1.23680056 -H 1.09019266 0.97592756 0.36305238 -H 1.09017188 -0.53711273 1.23699498 -C 1.24686016 1.19789551 2.49406634 -H 0.89014973 0.69369050 3.36781400 -H 0.89024275 2.20671858 2.49385027 -C 2.78686016 1.19782038 2.49409918 -H 3.14356973 1.70202473 1.62035078 -H 3.14353249 1.70238678 3.36765375 -H 3.14347779 0.18899740 2.49431581 diff --git a/tests/csearch_input/pentane_gjf.xyz b/tests/csearch_input/pentane_gjf.xyz deleted file mode 100644 index 425fa8d0..00000000 --- a/tests/csearch_input/pentane_gjf.xyz +++ /dev/null @@ -1,19 +0,0 @@ -17 -pentane_gjf -C -1.31982355 -0.25423777 -0.02060588 -H -0.96316913 -1.26304778 -0.02060588 -H -0.96315071 0.25016042 -0.89425738 -H -2.38982355 -0.25422459 -0.02060588 -C -0.80648133 0.47171850 1.23679909 -H -1.16313432 1.48052901 1.23679821 -H -1.16315561 -0.03267957 2.11045008 -C 0.73351867 0.47169768 1.23680056 -H 1.09019266 0.97592756 0.36305238 -H 1.09017188 -0.53711273 1.23699498 -C 1.24686016 1.19789551 2.49406634 -H 0.89014973 0.69369050 3.36781400 -H 0.89024275 2.20671858 2.49385027 -C 2.78686016 1.19782038 2.49409918 -H 3.14356973 1.70202473 1.62035078 -H 3.14353249 1.70238678 3.36765375 -H 3.14347779 0.18899740 2.49431581 diff --git a/tests/test_cmin.py b/tests/test_cmin.py index 4a169fdb..84ca1122 100644 --- a/tests/test_cmin.py +++ b/tests/test_cmin.py @@ -23,20 +23,32 @@ # tests of basic ANI and xTB optimizations @pytest.mark.parametrize( - "program, sdf, output_nummols", + "path, program, sdf, output_nummols", [ # tests for conformer generation with RDKit - ("ani", "pentane_rdkit_methods.sdf", 4), - ("xtb", "pentane_rdkit_methods.sdf", 4), + ("complete", "ani", "pentane_rdkit_methods.sdf", 4), + ("complete", "xtb", "pentane_rdkit_methods.sdf", 4), + ("partial", "ani", "tests/cmin_methods/pentane_rdkit_methods.sdf", 4), # test for partial path in the files option + ("name", "ani", "pentane_rdkit_methods.sdf", 4), # test for direct name in the files option ], ) def test_cmin_methods( - program, sdf, output_nummols + path, program, sdf, output_nummols ): # runs the program with the different tests - os.chdir(cmin_methods_dir) - cmin(program=program,files=f'{cmin_methods_dir}/{sdf}') + os.chdir(w_dir_main) + if path == 'complete': + cmin(program=program,files=f'{cmin_methods_dir}/{sdf}') + os.chdir(cmin_methods_dir) + elif path == 'partial': + cmin(program=program,files=f'{sdf}') + os.chdir(cmin_methods_dir) + sdf = 'pentane_rdkit_methods.sdf' + elif path == 'name': + os.chdir(cmin_methods_dir) # first go to the folder with SDF + cmin(program=program,files=f'{sdf}') + file = f'{cmin_methods_dir}/CMIN/{sdf.split(".")[0]}_{program}.sdf' file2 = f'{cmin_methods_dir}/CMIN/{sdf.split(".")[0]}_{program}_all_confs.sdf' diff --git a/tests/test_csearch.py b/tests/test_csearch.py index c24661d9..ae106613 100644 --- a/tests/test_csearch.py +++ b/tests/test_csearch.py @@ -64,6 +64,8 @@ def test_csearch_varfile(varfile, nameinvarfile, output_nummols): # tests for conformer generation with RDKit ("rdkit", "pentane.smi", [2, 4]), ("rdkit", "pentane.csv", [2, 4]), + ("rdkit", "partial_path", [2, 4]), # checks partial PATHs + ("rdkit", "file_name", [2, 4]), # checks file_name ("rdkit", "molecules.cdx", [4, 2]), ("rdkit", "pentane_gjf.gjf", 4), ("rdkit", "pentane_com.com", 4), @@ -75,98 +77,81 @@ def test_csearch_varfile(varfile, nameinvarfile, output_nummols): ], ) def test_csearch_input_parameters(program, input, output_nummols): - os.chdir(csearch_input_dir) + # runs the program with the different tests - csearch(w_dir_main=csearch_input_dir, program=program, input=input) - + os.chdir(w_dir_main) + if input == "partial_path": + input = "tests/csearch_input/pentane.csv" + csearch(destination=f'{csearch_input_dir}/CSEARCH', program=program, input=input) + input = "pentane.csv" + os.chdir(csearch_input_dir) + elif input == "file_name": + input = "pentane.csv" + os.chdir(csearch_input_dir) + csearch(destination=f'{csearch_input_dir}/CSEARCH', program=program, input=input) + else: + csearch(destination=f'{csearch_input_dir}/CSEARCH', program=program, input=f'{csearch_input_dir}/{input}') + os.chdir(csearch_input_dir) + # tests here if input in ["pentane.smi", "pentane.csv"]: - file1 = str( - "CSEARCH/" - + "butane_" - + input.split(".")[1] - + "_" - + program - + ".sdf" - ) - file2 = str( - "CSEARCH/" - + "pentane_" - + input.split(".")[1] - + "_" - + program - + ".sdf" - ) - mol1 = rdkit.Chem.SDMolSupplier(file1, removeHs=False) - mol2 = rdkit.Chem.SDMolSupplier(file2, removeHs=False) - assert len(mol1) == output_nummols[0] - assert len(mol2) == output_nummols[1] + file1 = f'{csearch_input_dir}/CSEARCH/butane_{input.split(".")[1]}_{program}.sdf' + file2 = f'{csearch_input_dir}/CSEARCH/pentane_{input.split(".")[1]}_{program}.sdf' + + with rdkit.Chem.SDMolSupplier(file1, removeHs=False) as mol1: + assert len(mol1) == output_nummols[0] + with rdkit.Chem.SDMolSupplier(file2, removeHs=False) as mol2: + assert len(mol2) == output_nummols[1] + os.remove(file1) + os.remove(file2) elif input in ["molecules.cdx"]: - file1 = str("CSEARCH/" + "molecules_0_" + program + ".sdf") - file2 = str("CSEARCH/" + "molecules_1_" + program + ".sdf") + file1 = f'{csearch_input_dir}/CSEARCH/molecules_0_{program}.sdf' + file2 = f'{csearch_input_dir}/CSEARCH/molecules_1_{program}.sdf' mol1 = rdkit.Chem.SDMolSupplier(file1, removeHs=False) mol2 = rdkit.Chem.SDMolSupplier(file2, removeHs=False) assert len(mol1) == output_nummols[0] assert len(mol2) == output_nummols[1] - elif input in ["pentane_sdf.sdf"]: - file = str( - "CSEARCH/" + input.split(".")[0] + "_" + program + ".sdf" - ) - mols = rdkit.Chem.SDMolSupplier(file, removeHs=False) - assert len(mols) == output_nummols else: - file = str( - "CSEARCH/" + input.split(".")[0] + "_" + program + ".sdf" - ) + file = f'{csearch_input_dir}/CSEARCH/{input.split(".")[0]}_{program}.sdf' mols = rdkit.Chem.SDMolSupplier(file, removeHs=False) assert len(mols) == output_nummols os.chdir(w_dir_main) -# # tests for parameters of csearch random initialzation -# @pytest.mark.parametrize( -# "program, smi, name, max_matches_rmsd , max_mol_wt , ff, degree, output, max_torsions, prefix, output_nummols ", -# [ -# # tests for conformer generation with RDKit -# ("summ", "CCCCC", "pentane", 500, 200, "MMFF", 30, ".sdf", 20, "mol", 4), -# ], -# ) -# def test_csearch_others_parameters( -# program, -# smi, -# name, -# max_matches_rmsd, -# max_mol_wt, -# ff, -# degree, -# output, -# max_torsions, -# prefix, -# output_nummols, -# ): -# os.chdir(csearch_others_dir) -# # runs the program with the different tests -# csearch( -# w_dir_main=csearch_others_dir, -# program=program, -# smi=smi, -# name=name, -# max_matches_rmsd=max_matches_rmsd, -# max_mol_wt=max_mol_wt, -# ff=ff, -# degree=degree, -# output=output, -# max_torsions=max_torsions, -# prefix=prefix, -# ) - -# # tests here -# file = str( -# "CSEARCH/" + prefix + "_" + name + "_" + program + ".sdf" -# ) -# mols = rdkit.Chem.SDMolSupplier(file, removeHs=False) -# assert len(mols) == output_nummols -# os.chdir(w_dir_main) +# tests for parameters of SUMM +@pytest.mark.parametrize( + "program, smi, name, charge, mult, ang_summ, output_nummols", + [ + ("summ", "CCCCC", "pentane_summ", 3, 4, 4, 60, 4), + ], +) +def test_csearch_summ_parameters( + program, + smi, + name, + charge, + mult, + ang_summ, + output_nummols, +): + os.chdir(csearch_rdkit_summ_dir) + # runs the program with the different tests + csearch( + program=program, + smi=smi, + name=name, + charge=charge, + mult=mult, + degree=ang_summ, + ) + + # tests here + file = str("CSEARCH/" + name + "_" + program + ".sdf") + mols = rdkit.Chem.SDMolSupplier(file, removeHs=False) + assert len(mols) == output_nummols + assert charge == int(mols[0].GetProp("Real charge")) + assert mult == int(mols[0].GetProp("Mult")) + os.chdir(w_dir_main) # tests for parameters of CREST @@ -363,11 +348,11 @@ def test_csearch_crest_parameters( # check if xtb_keywords are correct in CREST assert line.find('-P 14') > -1 + # tests for parameters of csearch fullmonte @pytest.mark.parametrize( "program, smi, name, charge, mult, ewin_fullmonte, ewin_sample_fullmonte, nsteps_fullmonte, nrot_fullmonte, ang_fullmonte, output_nummols", [ - # tests for conformer generation with RDKit ("fullmonte", "CCCCC", "pentane_fullmonte", 3, 4, 12, 3, 200, 4, 10, 4), ], ) @@ -398,7 +383,6 @@ def test_csearch_fullmonte_parameters( nsteps_fullmonte=nsteps_fullmonte, nrot_fullmonte=nrot_fullmonte, ang_fullmonte=ang_fullmonte, - output_nummols=output_nummols, ) # tests here @@ -430,21 +414,6 @@ def test_csearch_fullmonte_parameters( 0.2, 4, ), - # ( - # "summ", - # "CCCCC", - # "pentane_summ", - # 0, - # 1, - # "auto", - # 100, - # True, - # 40, - # 0.0001, - # 0.2, - # 0.1, - # 5, - # ), ( "rdkit", "CC[CH]CC", @@ -460,21 +429,6 @@ def test_csearch_fullmonte_parameters( 0.3, 41, ), - # ( - # "summ", - # "CC[CH]CC", - # "radical_summ", - # 0, - # 2, - # "auto", - # 500, - # True, - # 2, - # 0.0001, - # 0.2, - # 0.2, - # 3, - # ), ( "rdkit", "C[NH2+]CC", @@ -490,24 +444,9 @@ def test_csearch_fullmonte_parameters( 0.6, 5, ), - # ( - # "summ", - # "C[NH2+]CC", - # "charged_summ", - # 1, - # 0, - # "auto", - # 1000, - # False, - # 10, - # 0.0001, - # 0.2, - # 0.2, - # 2, - # ), ], ) -def test_csearch_rdkit_summ_parameters( +def test_csearch_rdkit_parameters( program, smi, name, From 016784ee948613629e52341e9c89bf715f529bf8 Mon Sep 17 00:00:00 2001 From: Juanvi Alegre-Requena Date: Sun, 5 Mar 2023 18:38:50 +0100 Subject: [PATCH 2/3] hotfix csearch summ test --- tests/test_csearch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_csearch.py b/tests/test_csearch.py index ae106613..7198b562 100644 --- a/tests/test_csearch.py +++ b/tests/test_csearch.py @@ -122,7 +122,7 @@ def test_csearch_input_parameters(program, input, output_nummols): @pytest.mark.parametrize( "program, smi, name, charge, mult, ang_summ, output_nummols", [ - ("summ", "CCCCC", "pentane_summ", 3, 4, 4, 60, 4), + ("summ", "CCCCC", "pentane_summ", 3, 4, 120, 4), ], ) def test_csearch_summ_parameters( From ecf234b2ab03536d004d6316dd1d67ee27f92069 Mon Sep 17 00:00:00 2001 From: Juanvi Alegre-Requena Date: Mon, 6 Mar 2023 16:05:29 +0100 Subject: [PATCH 3/3] hotfix as_posix() for files and input (LINUX comp) --- aqme/utils.py | 8 +-- tests/test_cmin.py | 146 +++++------------------------------------- tests/test_csearch.py | 1 + 3 files changed, 21 insertions(+), 134 deletions(-) diff --git a/aqme/utils.py b/aqme/utils.py index 134c5d01..85e45149 100644 --- a/aqme/utils.py +++ b/aqme/utils.py @@ -756,16 +756,16 @@ def get_files(value): Path(f"{val}").exists() and os.getcwd() not in f"{val}" ): - new_value.append(Path(f"{os.getcwd()}/{val}")) + new_value.append(f"{os.getcwd()}/{val}") elif '*' in val: if os.getcwd() not in f"{val}": list_of_val = glob.glob(f"{os.getcwd()}/{val}") else: list_of_val = glob.glob(val) for ele in list_of_val: - new_value.append(Path(ele)) + new_value.append(ele) else: - new_value.append(Path(val)) + new_value.append(val) else: - new_value.append(val) + new_value.append(val.as_posix()) return new_value \ No newline at end of file diff --git a/tests/test_cmin.py b/tests/test_cmin.py index 84ca1122..931330cb 100644 --- a/tests/test_cmin.py +++ b/tests/test_cmin.py @@ -17,9 +17,6 @@ cmin_methods_dir = w_dir_main + "/tests/cmin_methods" if not os.path.exists(cmin_methods_dir): os.mkdir(cmin_methods_dir) -cmin_xtb_dir = w_dir_main + "/tests/cmin_xtb" -if not os.path.exists(cmin_xtb_dir): - os.mkdir(cmin_xtb_dir) # tests of basic ANI and xTB optimizations @pytest.mark.parametrize( @@ -61,7 +58,6 @@ def test_cmin_methods( # in this case, RDKit, ANI and xTB should lead to the same 4 conformers assert len(mols_all) == output_nummols assert len(mols) == output_nummols - os.chdir(w_dir_main) # check that the optimizations work (different geometry than initial) outfile = open(file, "r") @@ -70,130 +66,20 @@ def test_cmin_methods( coords = ['2.5236','0.0073','0.1777'] for coord in coords: assert coord not in outlines[4] + os.chdir(w_dir_main) -# # tests for PATHs -# @pytest.mark.parametrize( -# "program, sdf, output_nummols", -# [ -# # tests for conformer generation with RDKit -# ("destination", 4), -# ("working_dir", 4), -# ], -# ) -# def test_cmin_methods( -# program, sdf, output_nummols -# ): - -# # runs the program with the different tests -# base_folder = f'CMIN_{program}' -# cmin( -# w_dir_main=cmin_methods_dir, -# program=program, -# destination=f'{cmin_methods_dir}/{base_folder}', -# files=f'{cmin_methods_dir}/{sdf}' -# ) - -# file = f'{cmin_methods_dir}/{base_folder}/{sdf.split(".")[0]}_{program}.sdf' -# file2 = f'{cmin_methods_dir}/{base_folder}/{sdf.split(".")[0]}_{program}_all_confs.sdf' - -# assert os.path.exists(file) -# assert os.path.exists(file2) - -# mols = rdkit.Chem.SDMolSupplier(file2, removeHs=False, sanitize=False) -# assert len(mols) == output_nummols - - -# # tests for parameters of cmin paramters -# @pytest.mark.parametrize( -# "program, sdf, metal_complex,complex_type, charge, mult, ewin_cmin, initial_energy_threshold, energy_threshold,rms_threshold, output_nummols", -# [ -# # tests for conformer generation with RDKit -# ( -# "xtb", -# "pentane_rdkit.sdf", -# False, -# None, -# 0, -# 1, -# 5, -# 0.003, -# 0.4, -# 0.5, -# 4, -# ), -# ( -# "xtb", -# "Pd_complex_0_rdkit.sdf", -# True, -# "squareplanar", -# 0, -# 1, -# 8, -# 0.004, -# 0.3, -# 0.2, -# 2, -# ), -# ], -# ) -# def test_cmin_xtb_parameters( -# program, -# sdf, -# metal_complex, -# complex_type, -# charge, -# mult, -# ewin_cmin, -# initial_energy_threshold, -# energy_threshold, -# rms_threshold, -# output_nummols, -# ): -# os.chdir(cmin_xtb_dir) -# # runs the program with the different tests -# cmin( -# w_dir_main=cmin_xtb_dir, -# program=program, -# files=sdf, -# charge=charge, -# mult=mult, -# ewin_cmin=ewin_cmin, -# initial_energy_threshold=initial_energy_threshold, -# energy_threshold=energy_threshold, -# rms_threshold=rms_threshold, -# xtb_accuracy=xtb_accuracy, -# xtb_electronic_temperature=xtb_electronic_temperature, -# xtb_max_iterations=xtb_max_iterations, -# ) - -# # tests here -# file = str("CMIN/" + sdf.split(".")[0] + "_" + program + ".sdf") -# file2 = str( -# "CMIN/" + sdf.split(".")[0] + "_" + program + "_all_confs.sdf" -# ) -# assert os.path.exists(file) -# assert os.path.exists(file2) - -# mols = rdkit.Chem.SDMolSupplier(file, removeHs=False, sanitize=False) -# assert len(mols) == output_nummols - -# assert int(mols[0].GetProp("Real charge")) == charge -# assert int(mols[0].GetProp("Mult")) == mult - -# os.chdir(w_dir_main) - - -# tests for removing foler and creation of CMIN DAT and CSV files -# @pytest.mark.parametrize( -# "folder_list, file_list", -# [ -# ["/tests/cmin_methods/CMIN_xtb","/tests/cmin_methods/CMIN_ani"], -# ["/tests/cmin_methods/CMIN_xtb*","/tests/cmin_methods/CMIN_ani*"] - -# ], -# ) -# def test_remove(folder_list, file_list): -# for i,folder in enumerate(folder_list): -# shutil.rmtree(w_dir_main + "/" + folder) -# for f in glob.glob(file_list[i]): -# os.remove(f) \ No newline at end of file +# tests for removing foler +@pytest.mark.parametrize( + "folder_list, file_list", + [ + ( + ["tests/cmin_methods/CMIN"],["tests/cmin_methods/CMIN*"] + ), + ], +) +def test_remove(folder_list, file_list): + for i,folder in enumerate(folder_list): + shutil.rmtree(w_dir_main + "/" + folder) + for f in glob.glob(file_list[i]): + os.remove(f) + os.chdir(w_dir_main) diff --git a/tests/test_csearch.py b/tests/test_csearch.py index 7198b562..5e4379b8 100644 --- a/tests/test_csearch.py +++ b/tests/test_csearch.py @@ -912,3 +912,4 @@ def test_remove(folder_list, file_list): shutil.rmtree(w_dir_main + "/" + folder) for f in glob.glob(file_list[i]): os.remove(f) + os.chdir(w_dir_main)