From 80bb98fc64e50b3628afb3393b79eaa25fa80757 Mon Sep 17 00:00:00 2001 From: Fabian Bohle <49951809+fabothch@users.noreply.github.com> Date: Mon, 1 Mar 2021 15:59:01 +0100 Subject: [PATCH] improvements * added progress option to track CENSO progress using printout to stderr * updated NMR kt2 reference shielding constants * fixed wb97x-V in TM ($disp3) * updated README * added information functional availability in -tutorial * added load_balancing automatical O and P balance * corrected NMR SD of sigma * created censo_nmr_ref.json for user editable shift references --- README.rst | 10 ++++- censo_qm/cfg.py | 80 ++++++++++++++++++++++++++++++++++++- censo_qm/cheapscreening.py | 14 +++++-- censo_qm/inputhandling.py | 14 +++++++ censo_qm/nmrproperties.py | 58 +++++++++++++++++++-------- censo_qm/opticalrotation.py | 3 +- censo_qm/optimization.py | 23 +++++++---- censo_qm/parallel.py | 46 +++++++++++++++++++-- censo_qm/prescreening.py | 18 +++++++-- censo_qm/refinement.py | 30 ++++++++++---- censo_qm/setupcenso.py | 28 +++++++++++-- censo_qm/tm_job.py | 4 +- censo_qm/tutorial.py | 26 ++++++++++++ censo_qm/utilities.py | 9 +++-- setup.cfg | 2 +- 15 files changed, 308 insertions(+), 57 deletions(-) diff --git a/README.rst b/README.rst index fa823a2..1396084 100644 --- a/README.rst +++ b/README.rst @@ -58,19 +58,24 @@ settings and provide paths to the external programs e.g. `xtb`, `crest`, `orca` vi /home/$USER/.ensorc -Interactive Documentation can be accessed: +**Interactive Documentation can be accessed:** .. code:: $ censo -tutorial - Explainations on the commandline arguments can be printed by: .. code:: $ censo --help +Online Documentation: +--------------------- + +Can be found following: https://fbohle.gitbook.io/censo/ + + The molecule numbering from the input structure ensemble is kept throughout the entire program. There are several program parts which can be used to filter a structure ensemble: @@ -96,6 +101,7 @@ ensemble: Usage: +------ .. code:: diff --git a/censo_qm/cfg.py b/censo_qm/cfg.py index 108366c..b8a3983 100644 --- a/censo_qm/cfg.py +++ b/censo_qm/cfg.py @@ -5,7 +5,8 @@ """ import os -__version__ = "1.0.4" +__version__ = "1.0.5" + DESCR = f""" ______________________________________________________________ | | @@ -325,6 +326,16 @@ class NmrRef(): """nmrreference data in the format: [reference-molecule][func-geometry][funcS][solvent] + h_tm_shieldings + c_tm_shieldings + f_tm_shieldings + si_tm_shieldings + p_tm_shieldings + h_orca_shieldings + c_orca_shieldings + f_orca_shieldings + si_orca_shieldings + p_orca_shieldings """ h_tm_shieldings = { "TMS": { @@ -388,6 +399,18 @@ class NmrRef(): "methanol": 31.644428750000003, "thf": 31.633562058333336, "toluene": 31.6153103 + }, + "kt2": { + "gas": 31.667160058333337, + "acetone": 31.629431858333334, + "chcl3": 31.634026916666674, + "acetonitrile": 31.627110300000002, + "ch2cl2": 31.645328925, + "dmso": 31.629352433333338, + "h2o": 31.635154825, + "methanol": 31.642592158333333, + "thf": 31.631994574999997, + "toluene": 31.612610516666663 } }, "pbeh-3c": { @@ -794,6 +817,18 @@ class NmrRef(): "methanol": 188.355007975, "thf": 188.4689729, "toluene": 188.594034275 + }, + "kt2": { + "gas": 189.78494644999998, + "acetone": 190.329502875, + "chcl3": 190.204013175, + "acetonitrile": 190.397052075, + "ch2cl2": 190.06665505, + "dmso": 190.4107424, + "h2o": 190.40970589999998, + "methanol": 190.188391875, + "thf": 190.2872299, + "toluene": 190.41299607500002 } }, "pbeh-3c": { @@ -2228,6 +2263,18 @@ class NmrRef(): "methanol": 331.3449225, "thf": 331.3305993, "toluene": 331.3160006 + }, + "kt2": { + "gas": 340.923509, + "acetone": 340.3208483, + "chcl3": 340.3430966, + "acetonitrile": 340.3155874, + "ch2cl2": 340.3599861, + "dmso": 340.3553012, + "h2o": 340.4180652, + "methanol": 340.3808603, + "thf": 340.348664, + "toluene": 340.3406705 } }, "pbeh-3c": { @@ -2572,4 +2619,33 @@ class NmrRef(): }, } } - \ No newline at end of file + def NMRRef_to_dict(self): + """Convert NMRRef data to a dict object""" + dict_ret = dict( + h_tm_shieldings=self.h_tm_shieldings, + c_tm_shieldings=self.c_tm_shieldings, + f_tm_shieldings=self.f_tm_shieldings, + si_tm_shieldings=self.si_tm_shieldings, + p_tm_shieldings=self.p_tm_shieldings, + h_orca_shieldings=self.h_orca_shieldings, + c_orca_shieldings=self.c_orca_shieldings, + f_orca_shieldings=self.f_orca_shieldings, + si_orca_shieldings=self.si_orca_shieldings, + p_orca_shieldings=self.p_orca_shieldings, + ) + return dict_ret + + def dict_to_NMRRef(self, dictionary): + """Convert dict object to NMRRef data """ + NmrRef_object = NmrRef() + NmrRef_object.h_tm_shieldings = dictionary.get('h_tm_shieldings', NmrRef_object.h_tm_shieldings) + NmrRef_object.c_tm_shieldings = dictionary.get('c_tm_shieldings', NmrRef_object.c_tm_shieldings) + NmrRef_object.f_tm_shieldings = dictionary.get('f_tm_shieldings', NmrRef_object.f_tm_shieldings) + NmrRef_object.si_tm_shieldings = dictionary.get('si_tm_shieldings', NmrRef_object.si_tm_shieldings) + NmrRef_object.p_tm_shieldings = dictionary.get('p_tm_shieldings', NmrRef_object.p_tm_shieldings) + NmrRef_object.h_orca_shieldings = dictionary.get('h_orca_shieldings', NmrRef_object.h_orca_shieldings) + NmrRef_object.c_orca_shieldings = dictionary.get('c_orca_shieldings', NmrRef_object.c_orca_shieldings) + NmrRef_object.f_orca_shieldings = dictionary.get('f_orca_shieldings', NmrRef_object.f_orca_shieldings) + NmrRef_object.si_orca_shieldings = dictionary.get('si_orca_shieldings', NmrRef_object.si_orca_shieldings) + NmrRef_object.p_orca_shieldings = dictionary.get('p_orca_shieldings', NmrRef_object.p_orca_shieldings) + return NmrRef_object diff --git a/censo_qm/cheapscreening.py b/censo_qm/cheapscreening.py index 2827949..c4a9d39 100644 --- a/censo_qm/cheapscreening.py +++ b/censo_qm/cheapscreening.py @@ -148,7 +148,6 @@ def part0(config, conformers, ensembledata): "unpaired": config.unpaired, "solvent": config.solvent, "sm": config.sm_rrho, - "omp": config.omp, "gfn_version": config.part0_gfnv, "energy": 0.0, "energy2": 0.0, @@ -190,7 +189,6 @@ def part0(config, conformers, ensembledata): "unpaired": config.unpaired, "solvent": "gas", "sm": "gas-phase", - "omp": config.omp, "energy": 0.0, "energy2": 0.0, "success": False, @@ -231,10 +229,18 @@ def part0(config, conformers, ensembledata): calculate, store_confs, save_errors = ensemble2coord( config, folder, calculate, store_confs, save_errors ) - # parallel calculation: calculate = run_in_parallel( - config, q, resultq, job, config.maxthreads, calculate, instruction, folder + config, + q, + resultq, + job, + config.maxthreads, + config.omp, + calculate, + instruction, + config.balance, + folder ) for conf in list(calculate): diff --git a/censo_qm/inputhandling.py b/censo_qm/inputhandling.py index c2f5aec..28915b1 100755 --- a/censo_qm/inputhandling.py +++ b/censo_qm/inputhandling.py @@ -845,6 +845,16 @@ def cml(startup_description, options, argv=None): " threads with each (omp) 4 cores --> 20 cores need to be available on " "the machine.", ) + group7.add_argument( + "-balance", + "--balance", + dest="balance", + choices=["on", "off"], + action="store", + metavar="", + help="Automatically balance the number of threads and cores when a low number" + "of conformers is left. (never exceed O*P cores).", + ) group11 = parser.add_argument_group("Concerning overall mRRHO calculations") group11.add_argument( "-imagthr", @@ -1066,6 +1076,7 @@ class internal_settings: "scale": "scale", "cosmorsparam": "cosmorsparam", "progress": "progress", + "balance": "balance", } knownbasissets3 = [ "SVP", @@ -1504,6 +1515,7 @@ def __init__(self): ("basis", {"default": "automatic", "type": str}), ("maxthreads", {"default": 1, "type": int}), ("omp", {"default": 1, "type": int}), + ("balance", {"default": False, "type": bool}), ("cosmorsparam", {"default": "automatic", "type": str}), ] @@ -1782,6 +1794,7 @@ def __init__(self, path=os.getcwd(), *args, **kwargs): self.basis = "automatic" self.maxthreads = 1 self.omp = 1 + self.balance=False self.cosmorsparam = "automatic" # part0 self.part0 = False @@ -2725,6 +2738,7 @@ def print_parameters(self): info.append(["crestcheck", "checking the DFT-ensemble using CREST"]) info.append(["maxthreads", "maxthreads"]) info.append(["omp", "omp"]) + info.append(["balance", "automatically balance maxthreads and omp"]) if self.part0: # PART0: diff --git a/censo_qm/nmrproperties.py b/censo_qm/nmrproperties.py index 3a45323..8946871 100644 --- a/censo_qm/nmrproperties.py +++ b/censo_qm/nmrproperties.py @@ -5,6 +5,7 @@ import os import shutil import sys +import json from random import normalvariate from multiprocessing import JoinableQueue as Queue from .cfg import ( @@ -12,6 +13,7 @@ DIGILEN, AU2KCAL, WARNLEN, + CODING, NmrRef ) from .parallel import run_in_parallel @@ -90,7 +92,7 @@ def average_shieldings(config, calculate, element_ref_shield, energy, solv, rrho sigma_std_dev[i] = [] for conf in calculate: - # get shielding constants + # get averaged shielding constants if not element: element = get_atom( os.path.normpath(os.path.join(config.cwd, "CONF" + str(conf.id), "NMR")) @@ -100,7 +102,7 @@ def average_shieldings(config, calculate, element_ref_shield, energy, solv, rrho [conf.shieldings.get(eq_atom, 0.0) for eq_atom in chemeq[atom]] ) / len(chemeq[atom]) averaged[atom] = conf.bm_weight * sigma + averaged.get(atom, 0.0) - + # get SD on shieldings based on SD Gsolv if solv is not None: get_std_dev = ( lambda conf: conf.lowlevel_gsolv_compare_info["std_dev"] @@ -114,21 +116,18 @@ def average_shieldings(config, calculate, element_ref_shield, energy, solv, rrho 0.0, get_std_dev(conf) ) calculate = calc_boltzmannweights(calculate, "free_energy", config.temperature) + tmp_sigma = {} + for i in range(1, config.nat + 1): + tmp_sigma[i] = 0 for conf in calculate: - # get shielding constants - if not element: - element = get_atom( - os.path.normpath( - os.path.join(config.cwd, "CONF" + str(conf.id), "NMR") - ) - ) for atom in conf.shieldings.keys(): sigma = sum( [conf.shieldings.get(eq_atom, 0.0) for eq_atom in chemeq[atom]] ) / len(chemeq[atom]) - sigma_std_dev[atom].append( - conf.bm_weight * sigma + averaged.get(atom, 0.0) - ) + tmp_sigma[atom] += conf.bm_weight * sigma + for atom in conf.shieldings.keys(): + sigma_std_dev[atom].append(tmp_sigma[atom]) + print("\nAveraged shielding constants:") print("# in coord element σ(sigma) SD(σ based on SD Gsolv) shift σ_ref") @@ -151,7 +150,6 @@ def average_shieldings(config, calculate, element_ref_shield, energy, solv, rrho f"{std_dev:^ 24.6f} {make_shift(atom):>5} {element_ref_shield.get(element[atom], 0.0)}" ) except Exception as e: - #print(e) print(f"{atom:< {10}} {element[atom]:^{7}} {sigma:> {maxsigma}.2f}") print("".ljust(int(80), "-")) @@ -247,7 +245,22 @@ def write_anmrrc(config): ): for element in ref_decision.keys(): ref_decision[element]["active"] = True - nmrref = NmrRef() + + # read NMR_references + nmr_ref_user_path = os.path.expanduser( + os.path.join("~/.censo_assets/", "censo_nmr_ref.json") + ) + if os.path.isfile(nmr_ref_user_path): + try: + with open(nmr_ref_user_path, "r", encoding=CODING, newline=None) as inp: + tmp_ref = json.load(inp) + except (ValueError, TypeError, FileNotFoundError): + print( + f"{'ERROR:':{WARNLEN}}Your censo_nmr_ref.json file in {nmr_ref_user_path} is corrupted!\n" + ) + tmp_ref = {} + nmrref = NmrRef().dict_to_NMRRef(tmp_ref) + # end reading NMR_references for element, value in ref_decision.items(): if value.get("active", False): try: @@ -746,7 +759,6 @@ def part4(config, conformers, store_confs, ensembledata): "solvent": config.solvent, "sm": config.sm4_j, "success": False, - "omp": config.omp, # nmractive nuclei "h_active": config.h_active, "c_active": config.c_active, @@ -796,8 +808,10 @@ def part4(config, conformers, store_confs, ensembledata): resultq, job, config.maxthreads, + config.omp, calculate, instruction_j, + config.balance, folder, ) for conf in list(calculate): @@ -880,7 +894,6 @@ def part4(config, conformers, store_confs, ensembledata): "solvent": config.solvent, "sm": config.sm4_s, "success": False, - "omp": config.omp, # nmractive nuclei "h_active": config.h_active, "c_active": config.c_active, @@ -950,8 +963,10 @@ def part4(config, conformers, store_confs, ensembledata): resultq, job, config.maxthreads, + config.omp, calculate, instruction_s, + config.balance, folder, ) for conf in list(calculate): @@ -1053,7 +1068,16 @@ def part4(config, conformers, store_confs, ensembledata): # write generic: instructgeneric = {"jobtype": "genericout", "nat": int(config.nat)} calculate = run_in_parallel( - config, q, resultq, job, config.maxthreads, calculate, instructgeneric, folder + config, + q, + resultq, + job, + config.maxthreads, + config.omp, + calculate, + instructgeneric, + False, + folder ) # printout the averaged shielding constants diff --git a/censo_qm/opticalrotation.py b/censo_qm/opticalrotation.py index 42648a5..5ff1b9c 100644 --- a/censo_qm/opticalrotation.py +++ b/censo_qm/opticalrotation.py @@ -431,7 +431,6 @@ def part5(config, conformers, store_confs, ensembledata): "solvent": "gas", "sm": "cosmo", "success": False, - "omp": config.omp, "freq_or": config.freq_or, } if config.prog == "orca": @@ -476,8 +475,10 @@ def part5(config, conformers, store_confs, ensembledata): resultq, job, config.maxthreads, + config.omp, calculate, instruction_or, + config.balance, folder, ) calculate.sort(key=lambda x: int(x.id)) diff --git a/censo_qm/optimization.py b/censo_qm/optimization.py index 9806121..d4fd6b7 100755 --- a/censo_qm/optimization.py +++ b/censo_qm/optimization.py @@ -224,7 +224,6 @@ def part2(config, conformers, store_confs, ensembledata): "solvent": config.solvent, "sm": config.sm2, "optlevel": config.optlevel2, - "omp": config.omp, "copymos": "", "energy": 0.0, "energy2": 0.0, @@ -242,7 +241,6 @@ def part2(config, conformers, store_confs, ensembledata): "converged": False, "hlow": config.hlow, "sm": config.sm2, - "omp": config.omp, "optcycles": config.optcycles, "optlevel": config.optlevel2, "multiTemp": False, @@ -259,7 +257,6 @@ def part2(config, conformers, store_confs, ensembledata): "charge": config.charge, "unpaired": config.unpaired, "solvent": config.solvent, - "omp": config.omp, "progpath": config.external_paths["xtbpath"], "bhess": config.bhess, "sm_rrho": config.sm_rrho, @@ -336,8 +333,10 @@ def part2(config, conformers, store_confs, ensembledata): resultq, job, config.maxthreads, + config.omp, calculate, instruction_prep, + config.balance, config.func, ) # check if too many calculations failed @@ -425,8 +424,10 @@ def part2(config, conformers, store_confs, ensembledata): resultq, job, config.maxthreads, + config.omp, calculate, instruction_opt, + config.balance, config.func, ) @@ -514,8 +515,10 @@ def part2(config, conformers, store_confs, ensembledata): resultq, job, config.maxthreads, + config.omp, calculate, instruction_rrho_crude, + config.balance, folder_rrho_crude, ) check = {True: "was successful", False: "FAILED"} @@ -808,8 +811,10 @@ def part2(config, conformers, store_confs, ensembledata): resultq, job, config.maxthreads, + config.omp, calculate, instruction_opt, + config.balance, config.func, ) # check if optimization crashed @@ -905,7 +910,6 @@ def part2(config, conformers, store_confs, ensembledata): "unpaired": config.unpaired, "solvent": config.solvent, "sm": config.smgsolv2, - "omp": config.omp, "temperature": config.temperature, "energy": 0.0, "energy2": 0.0, @@ -1108,8 +1112,10 @@ def part2(config, conformers, store_confs, ensembledata): resultq, job, config.maxthreads, + config.omp, calculate, instruction_gsolv, + config.balance, folder, ) @@ -1255,7 +1261,6 @@ def part2(config, conformers, store_confs, ensembledata): "charge": config.charge, "unpaired": config.unpaired, "solvent": config.solvent, - "omp": config.omp, "progpath": config.external_paths["xtbpath"], "bhess": config.bhess, "sm_rrho": config.sm_rrho, @@ -1321,8 +1326,10 @@ def part2(config, conformers, store_confs, ensembledata): resultq, job, config.maxthreads, + config.omp, calculate, instruction_rrho, + config.balance, folderrho, ) check = {True: "was successful", False: "FAILED"} @@ -1499,7 +1506,6 @@ def part2(config, conformers, store_confs, ensembledata): "unpaired": config.unpaired, "solvent": config.solvent, "sm": "alpb_gsolv", - "omp": config.omp, "temperature": config.temperature, "energy": 0.0, "energy2": 0.0, @@ -1588,8 +1594,10 @@ def part2(config, conformers, store_confs, ensembledata): resultq, job, config.maxthreads, + config.omp, calculate, instruction_gsolv_compare, + config.balance, folder_compare, ) for conf in calculate: @@ -1863,7 +1871,8 @@ def part2(config, conformers, store_confs, ensembledata): ensembledata.nconfs_per_part["part2"] = len(calculate) if calculate: print( - f"\nConformers that are below the Boltzmann threshold G_thr(2) of {config.part2_threshold}%:" + f"\nConformers that are below the Boltzmann threshold G_thr(2) " + f"of {config.part2_threshold}%:" ) print_block(["CONF" + str(i.id) for i in calculate]) else: diff --git a/censo_qm/parallel.py b/censo_qm/parallel.py index 6a74bbe..26c30b9 100644 --- a/censo_qm/parallel.py +++ b/censo_qm/parallel.py @@ -9,7 +9,35 @@ from .tm_job import TmJob from .orca_job import OrcaJob from .utilities import print +from .cfg import ENVIRON +def balance_load(P,O,nconf, do_change): + """Balance calculation load between threads (P) and number of cores per + thread (O) + """ + changed = False + max_cores = P*O + if do_change: + if nconf < P: + try: + P_old = P + P = nconf + O_old = O + O=1 + while True: + if P*O <= max_cores: + if P*(O+1) <=max_cores: + O+=1 + else: + break + else: + break + changed = True + except: + pass + if changed: + print(f"Adjusting the number of threads (P) = {P} and number of cores per thread (O) = {O}") + return P, O, changed def execute_data(q, resultq): """ @@ -40,19 +68,25 @@ def execute_data(q, resultq): def run_in_parallel( - config, q, resultq, job, maxthreads, loopover, instructdict, foldername="" + config, q, resultq, job, maxthreads, omp, loopover, instructdict, balance=False,foldername="" ): """Run jobs in parallel q = queue to put assemble tasks resultq = queue to retrieve results job = information which kind of job is to be performed tm_job , orca_job loopover is list of qm_class objects - instrucdict example : {'jobtype': 'prep', 'chrg': args.chrg} + instructdict example : {'jobtype': 'prep', 'chrg': args.chrg} foldername is for existing objects to change the workdir results = list of qm_class objects with results from calculations """ + omp_initial = omp if instructdict.get("jobtype", None) is None: raise KeyError("jobtype is missing in instructdict!") + if len(loopover) != 0: + nconf = len(loopover) + else: + balance = False + maxthreads, omp, changed = balance_load(maxthreads, omp, nconf, balance) if all(isinstance(x, QmJob) for x in loopover): for item in loopover: if isinstance(item, TmJob) and job == OrcaJob: @@ -66,13 +100,16 @@ def run_in_parallel( ) # update instructions item.job.update(instructdict) + item.job["omp"] = omp # put item on queue q.put(item) time.sleep(0.02) time.sleep(0.02) + if changed: + ENVIRON['PARNODES'] = str(omp) njobs = q.qsize() if instructdict.get("onlyread", False): - print(f"\nReading data from {njobs} conformers calculated in " "previous run.") + print(f"\nReading data from {njobs} conformers calculated in previous run.") else: response = { "prep": f"\nPreparing {q.qsize()} calculations.", @@ -128,4 +165,7 @@ def run_in_parallel( results.sort(key=lambda x: int(x.id)) if njobs != len(results): print(f"ERROR some conformers were lost!") + + if changed: + ENVIRON['PARNODES'] = str(omp_initial) return results diff --git a/censo_qm/prescreening.py b/censo_qm/prescreening.py index 8b7869c..947ddcb 100755 --- a/censo_qm/prescreening.py +++ b/censo_qm/prescreening.py @@ -189,7 +189,6 @@ def part1(config, conformers, store_confs, ensembledata): "unpaired": config.unpaired, "solvent": config.solvent, "sm": config.smgsolv1, - "omp": config.omp, "temperature": config.temperature, "gfn_version": config.part1_gfnv, "energy": 0.0, @@ -305,7 +304,16 @@ def part1(config, conformers, store_confs, ensembledata): # parallel calculation: calculate = run_in_parallel( - config, q, resultq, job, config.maxthreads, calculate, instruction, folder + config, + q, + resultq, + job, + config.maxthreads, + config.omp, + calculate, + instruction, + config.balance, + folder ) for conf in list(calculate): @@ -542,7 +550,6 @@ def part1(config, conformers, store_confs, ensembledata): "charge": config.charge, "unpaired": config.unpaired, "solvent": config.solvent, - "omp": config.omp, "progpath": config.external_paths["xtbpath"], "bhess": config.bhess, "consider_sym": config.consider_sym, @@ -584,8 +591,10 @@ def part1(config, conformers, store_confs, ensembledata): resultq, job, config.maxthreads, + config.omp, calculate, instruction_prerrho, + config.balance, folderrho, ) check = {True: "was successful", False: "FAILED"} @@ -1014,7 +1023,6 @@ def part1(config, conformers, store_confs, ensembledata): "progpath": config.external_paths["xtbpath"], "sm": config.sm_rrho, "rmsdbias": config.rmsdbias, - "omp": config.omp, "temperature": config.temperature, "gfn_version": config.part1_gfnv, "energy": 0.0, @@ -1036,8 +1044,10 @@ def part1(config, conformers, store_confs, ensembledata): resultq, job, config.maxthreads, + config.omp, calculate, instruction_gfn, + config.balance, folder_gfn, ) for conf in list(calculate): diff --git a/censo_qm/refinement.py b/censo_qm/refinement.py index 838072b..b8513fa 100755 --- a/censo_qm/refinement.py +++ b/censo_qm/refinement.py @@ -1,6 +1,7 @@ """ REFINEMENT == Part3 -designed to yield high level free energies on dft optimized conformers. +designed to yield high level free energies on any conformers (DFT or non-DFT +optimized). """ from multiprocessing import JoinableQueue as Queue import shutil @@ -192,14 +193,17 @@ def part3(config, conformers, store_confs, ensembledata): # check if calculated on unoptimized geometries: if any([conf.optimization_info["info"] == "not_calculated" for conf in calculate+prev_calculated]): if config.part2: - print_errors(f"{'ERROR:':{WARNLEN}}Calculating (free) energies on DFT unoptimized geometries!\n" + print_errors(f"{'ERROR:':{WARNLEN}}Calculating (free) energies on " + f"DFT unoptimized geometries!\n" f"{'':{WARNLEN}}Even though part2 is calculated!\n" - f"{'':{WARNLEN}}Calculation on mixture of optimized and unoptimized geometries is not advised!", + f"{'':{WARNLEN}}Calculation on mixture of optimized " + f"and unoptimized geometries is not advised!", save_errors) print("Going to exit!") sys.exit(1) else: - print_errors(f"{'WARNING:':{WARNLEN}} Calculating (free) energies on DFT unoptimized geometries!", save_errors) + print_errors(f"{'WARNING:':{WARNLEN}} Calculating (free) energies " + f"on DFT unoptimized geometries!", save_errors) geometries_from_input = True @@ -217,7 +221,6 @@ def part3(config, conformers, store_confs, ensembledata): "unpaired": config.unpaired, "solvent": config.solvent, "sm": config.smgsolv3, - "omp": config.omp, "temperature": config.temperature, "gfn_version": config.part3_gfnv, "copymos": "", @@ -352,7 +355,8 @@ def part3(config, conformers, store_confs, ensembledata): try: shutil.copy(tmp1, tmp2) except FileNotFoundError: - print_errors(f"{'ERROR:':{WARNLEN}}can't copy optimized geometry of CONF{conf.id}!", save_errors) + print_errors(f"{'ERROR:':{WARNLEN}}can't copy optimized " + f"geometry of CONF{conf.id}!", save_errors) store_confs.append(calculate.pop(calculate.index(conf))) if config.solvent == "gas": @@ -362,7 +366,16 @@ def part3(config, conformers, store_confs, ensembledata): print_block(["CONF" + str(i.id) for i in calculate]) # parallel calculation: calculate = run_in_parallel( - config, q, resultq, job, config.maxthreads, calculate, instruction, folder + config, + q, + resultq, + job, + config.maxthreads, + config.omp, + calculate, + instruction, + config.balance, + folder ) # check if too many calculations failed @@ -475,7 +488,6 @@ def part3(config, conformers, store_confs, ensembledata): "charge": config.charge, "unpaired": config.unpaired, "solvent": config.solvent, - "omp": config.omp, "bhess": config.bhess, "sm_rrho": config.sm_rrho, "rmsdbias": config.rmsdbias, @@ -635,8 +647,10 @@ def part3(config, conformers, store_confs, ensembledata): resultq, job, config.maxthreads, + config.omp, calculate, instruction_rrho, + config.balance, folder_rrho, ) check = {True: "was successful", False: "FAILED"} diff --git a/censo_qm/setupcenso.py b/censo_qm/setupcenso.py index a816426..4b190a8 100755 --- a/censo_qm/setupcenso.py +++ b/censo_qm/setupcenso.py @@ -6,7 +6,15 @@ import sys import json from collections import OrderedDict -from .cfg import CODING, PLENGTH, DESCR, WARNLEN, censo_solvent_db, __version__ +from .cfg import ( + CODING, + PLENGTH, + DESCR, + WARNLEN, + censo_solvent_db, + __version__, + NmrRef, +) from .inputhandling import config_setup, internal_settings from .datastructure import MoleculeData from .qm_job import QmJob @@ -113,9 +121,23 @@ def enso_startup(cwd, args): with open(solvent_user_path, "w") as out: json.dump(censo_solvent_db, out, indent=4, sort_keys=True) config.save_infos.append( - "Creating file: {}\n".format(os.path.basename(solvent_user_path)) + "Creating file: {}".format(os.path.basename(solvent_user_path)) ) - + ### END solvent database adjustable by user + ### NMR reference shielding constant database adjustable by user + if not os.path.isdir(censo_assets_path): + mkdir_p(censo_assets_path) + nmr_ref_user_path = os.path.expanduser( + os.path.join("~/.censo_assets/", "censo_nmr_ref.json") + ) + if not os.path.isfile(nmr_ref_user_path): + with open(nmr_ref_user_path, "w") as out: + tmp = NmrRef() + json.dump(tmp, out, default=NmrRef.NMRRef_to_dict, indent=4, sort_keys=True) + config.save_infos.append( + "Creating file: {}".format(os.path.basename(nmr_ref_user_path)) + ) + ### END NMR reference shielding constant database adjustable by user if args.restart and os.path.isfile(os.path.join(config.cwd, "enso.json")): tmp = config.read_json(os.path.join(config.cwd, "enso.json"), silent=True) previous_settings = tmp.get("settings") diff --git a/censo_qm/tm_job.py b/censo_qm/tm_job.py index 7686838..b202637 100644 --- a/censo_qm/tm_job.py +++ b/censo_qm/tm_job.py @@ -97,6 +97,8 @@ def _prep_cefine(self): if self.job["func"] in ("kt2", "kt1"): # used only for shielding or coupling calcs! call.extend(["-novdw"]) + elif self.job["func"] in ("wb97x-v",): + call.extend(["-novdw"]) # r2scan-3c hack if self.job["func"] == "r2scan-3c": @@ -107,8 +109,6 @@ def _prep_cefine(self): if "-novdw" in call: requestnovdw = True # print("FOUND NOVDW") - elif self.job["func"] in ("wb97x-v",): - requestnovdw = True else: requestnovdw = False diff --git a/censo_qm/tutorial.py b/censo_qm/tutorial.py index 0095c52..1fa259a 100644 --- a/censo_qm/tutorial.py +++ b/censo_qm/tutorial.py @@ -648,6 +648,31 @@ def interactiv_doc(): """ + functionals = f""" + Functionals that can be employed by TM for func: + {', '.join(internal_settings.func_tm)} + + Functionals that can be employed by TM for func3 + {', '.join(internal_settings.func3_tm)} + + Functionals that can be employed by TM for funcJ + {', '.join(internal_settings.func_j_tm)} + + Functionals that can be employed by TM for funcS + {', '.join(internal_settings.func_s_tm)} + + Functionals that can be employed by ORCA for func: + {', '.join(internal_settings.func_orca)} + + Functionals that can be employed by ORCA for func3 + {', '.join(internal_settings.func3_orca)} + + Functionals that can be employed by ORCA for funcJ + {', '.join(internal_settings.func_j_orca)} + + Functionals that can be employed by ORCA for funcS + {', '.join(internal_settings.func_s_orca)} + """ # CONFORMER numbering kept from crest input @@ -671,6 +696,7 @@ def interactiv_doc(): "solvation": solvation, "examples": example_applications, "files": files, + "functionals": functionals, "jobscript": jobscript, } tutorial_data['everything'] = '\n\n'.join(tutorial_data.values()) diff --git a/censo_qm/utilities.py b/censo_qm/utilities.py index 1be3a45..f42295f 100755 --- a/censo_qm/utilities.py +++ b/censo_qm/utilities.py @@ -830,9 +830,12 @@ def calc_std_dev(data): Calculate standard deviation """ n = len(data) - mean = sum(data) / n - variance = sum([(x - mean) ** 2 for x in data]) / (n - 1) - std_dev = math.sqrt(variance) + if len(data) != 0: + mean = sum(data) / n + variance = sum([(x - mean) ** 2 for x in data]) / (n - 1) + std_dev = math.sqrt(variance) + else: + std_dev = 0.0 return std_dev diff --git a/setup.cfg b/setup.cfg index 3dcf8cf..4c2d4b4 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = censo-QM -version = 1.0.2 +version = 1.0.5 description = CENSO - Comandline ENergetic SOrting for conformer rotamer ensembles long_description = file: README.rst long_description_content_type = text/x-rst