def _test_property(self, key): for file in single_job_out_names: self.assertEqual(QCOutput(os.path.join(test_dir, file)).data.get(key), single_job_dict[file].get(key)) for file in multi_job_out_names: outputs = QCOutput.multiple_outputs_from_file(QCOutput, os.path.join(test_dir, file), keep_sub_files=False) for i, sub_output in enumerate(outputs): self.assertEqual(sub_output.data.get(key), multi_job_dict[file][i].get(key))
def process_qchem_multirun(dir_name, input_files, output_files): """ Process a QChem run which is known to include multiple calculations in a single input/output pair. """ if len(input_files) != 1: raise ValueError( "ERROR: The drone can only process a directory containing a single input/output pair when each include multiple calculations." ) else: for key in input_files: to_return = [] qchem_input_file = os.path.join(dir_name, input_files.get(key)) qchem_output_file = os.path.join(dir_name, output_files.get(key)) multi_out = QCOutput.multiple_outputs_from_file( QCOutput, qchem_output_file, keep_sub_files=False) multi_in = QCInput.from_multi_jobs_file(qchem_input_file) for ii, out in enumerate(multi_out): d = out.data d["input"] = {} d["input"]["molecule"] = multi_in[ii].molecule d["input"]["rem"] = multi_in[ii].rem d["input"]["opt"] = multi_in[ii].opt d["input"]["pcm"] = multi_in[ii].pcm d["input"]["solvent"] = multi_in[ii].solvent d["task"] = {"type": key, "name": "calc" + str(ii)} to_return.append(d) return to_return
def generate_single_job_dict(): """ Used to generate test dictionary for single jobs """ single_job_dict = {} for file in single_job_out_names: single_job_dict[file] = QCOutput(os.path.join(test_dir, file)).data dumpfn(single_job_dict, "single_job.json")
def test_RunQChemFake(self): os.chdir(os.path.join(module_dir, "..", "fake_run")) firetask = RunQChemFake(ref_dir=os.path.join(module_dir, "..", "..", "test_files", "real_run")) firetask.run_task(fw_spec={}) ref_out = QCOutput( os.path.join(module_dir, "..", "..", "test_files", "real_run", "mol.qout")).data this_out = QCOutput("mol.qout").data for key in ref_out: try: self.assertEqual(ref_out[key], this_out[key]) except ValueError: np.testing.assert_array_equal(ref_out[key], this_out[key]) for filename in os.listdir( os.path.join(module_dir, "..", "..", "test_files", "real_run")): self.assertEqual(os.path.isfile(filename), True) os.chdir(module_dir)
def generate_multi_job_dict(): """ Used to generate test dictionary for multiple jobs """ multi_job_dict = {} for file in multi_job_out_names: outputs = QCOutput.multiple_outputs_from_file(QCOutput, os.path.join(test_dir, file), keep_sub_files=False) data = [] for sub_output in outputs: data.append(sub_output.data) multi_job_dict[file] = data dumpfn(multi_job_dict, "multi_job.json")
def process_qchemrun(dir_name, taskname, input_file, output_file): """ Process a QChem calculation, aka an input/output pair. """ qchem_input_file = os.path.join(dir_name, input_file) qchem_output_file = os.path.join(dir_name, output_file) d = QCOutput(qchem_output_file).data temp_input = QCInput.from_file(qchem_input_file) d["input"] = {} d["input"]["molecule"] = temp_input.molecule d["input"]["rem"] = temp_input.rem d["input"]["opt"] = temp_input.opt d["input"]["pcm"] = temp_input.pcm d["input"]["solvent"] = temp_input.solvent d["task"] = {"type": taskname, "name": taskname} return d
class QChemSCFErrorHandler(ErrorHandler): """ QChem ErrorHandler class that addresses SCF non-convergence. """ is_monitor = False def __init__(self, input_file="mol.qin", output_file="mol.qout", rca_gdm_thresh=1.0E-3, scf_max_cycles=200): """ Initializes the error handler from a set of input and output files. Args: input_file (str): Name of the QChem input file. output_file (str): Name of the QChem output file. rca_gdm_thresh (float): The threshold for the prior scf algorithm. If last deltaE is larger than the threshold try RCA_DIIS first, else, try DIIS_GDM first. scf_max_cycles (int): The max iterations to set to fix SCF failure. """ self.input_file = input_file self.output_file = output_file self.scf_max_cycles = scf_max_cycles self.geom_max_cycles = geom_max_cycles self.qcinp = QCInput.from_file(self.input_file) self.outdata = None self.errors = None self.qchem_job = qchem_job def check(self): # Checks output file for errors. self.outdata = QCOutput(self.output_file).data self.errors = self.outdata.get("errors") return len(self.errors) > 0 def correct(self): print("This hasn't been implemented yet!") return {"errors": self.errors, "actions": None}
def setUpClass(cls): out_file = os.path.join(module_dir, "..", "..", "test_files", "FF_working", "test.qout.opt_1") qc_out = QCOutput(filename=out_file) cls.act_mol = qc_out.data["molecule_from_optimized_geometry"]
# coding: utf-8 from __future__ import division, print_function, unicode_literals, absolute_import import os from atomate.qchem.workflows.base.double_FF_opt import get_wf_double_FF_opt from fireworks.core.launchpad import LaunchPad from pymatgen.io.qchem_io.outputs import QCOutput out_file = os.path.join("/global/cscratch1/sd/sblau", "FF_working", "test.qout.opt_0") qc_out = QCOutput(filename=out_file) act_mol = qc_out.data["initial_molecule"] wf = get_wf_double_FF_opt(molecule=act_mol, pcm_dielectric=10.0, max_cores=32, qchem_input_params={ "basis_set": "6-311++g**", "overwrite_inputs": { "rem": { "sym_ignore": "true" } } }) lp = LaunchPad.auto_load() lp.add_wf(wf)
def test_torsion_potential(self): # location of test files test_tor_files = os.path.join(module_dir, "..", "..", "test_files", "torsion_wf") # define starting molecule and torsion potential workflow object initial_qcin = QCInput.from_file( os.path.join(test_tor_files, "initial_opt", "mol.qin")) initial_mol = initial_qcin.molecule atom_indexes = [6, 8, 9, 10] angles = [0.0, 90.0, 180.0] rem = [] # add the first rem section rem.append({ "jobtype": "opt", "method": "wb97m-v", "basis": "def2-tzvppd", "gen_scfman": "true", "geom_opt_max_cycles": 75, "max_scf_cycles": 300, "scf_algorithm": "diis", "scf_guess": "sad", "sym_ignore": "true", "symmetry": "false", "thresh": 14 }) # the second rem section rem.append({ "jobtype": "opt", "method": "wb97m-v", "basis": "def2-tzvppd", "geom_opt_max_cycles": 75, "max_scf_cycles": 300, "scf_algorithm": "diis", "scf_guess": "sad", "sym_ignore": "true", "symmetry": "false", "thresh": 14 }) real_wf = get_wf_torsion_potential(molecule=initial_mol, atom_indexes=atom_indexes, angles=angles, rem=rem, db_file=">>db_file<<") # use powerup to replace run with fake run # def ref_dirs ref_dirs = { "initial_opt": os.path.join(test_tor_files, "initial_opt"), "opt_0": os.path.join(test_tor_files, "opt_0"), "opt_90": os.path.join(test_tor_files, "opt_90"), "opt_180": os.path.join(test_tor_files, "opt_180") } fake_wf = use_fake_qchem(real_wf, ref_dirs) self.lp.add_wf(fake_wf) rapidfire( self.lp, fworker=FWorker(env={"db_file": os.path.join(db_dir, "db.json")})) wf_test = self.lp.get_wf_by_fw_id(1) self.assertTrue( all([s == "COMPLETED" for s in wf_test.fw_states.values()])) # Checking of the inputs happens in fake_run_qchem so there is no point to retest the inputs # Check the output info that gets inserted in the DB init_opt = self.get_task_collection().find_one( {"task_label": "initial_opt"}) init_opt_final_mol = Molecule.from_dict( init_opt["output"]["optimized_molecule"]) init_opt_final_e = init_opt["output"]["final_energy"] # parse output file act_init_opt_out = QCOutput( os.path.join(test_tor_files, "initial_opt", "mol.qout")) act_init_opt_mol = act_init_opt_out.data[ "molecule_from_optimized_geometry"] act_init_opt_final_e = act_init_opt_out.data["final_energy"] np.testing.assert_equal(act_init_opt_mol.species, init_opt_final_mol.species) np.testing.assert_allclose(act_init_opt_mol.cart_coords, init_opt_final_mol.cart_coords, atol=0.0001) np.testing.assert_equal(act_init_opt_final_e, init_opt_final_e) # Optimization of 0 torsion opt_0 = self.get_task_collection().find_one({"task_label": "opt_0"}) opt_0_final_mol = Molecule.from_dict( opt_0["output"]["optimized_molecule"]) opt_0_final_e = opt_0["output"]["final_energy"] # parse output file act_opt_0_out = QCOutput( os.path.join(test_tor_files, "opt_0", "mol.qout")) act_opt_0_mol = act_opt_0_out.data["molecule_from_optimized_geometry"] act_opt_0_final_e = act_opt_0_out.data["final_energy"] np.testing.assert_equal(act_opt_0_mol.species, opt_0_final_mol.species) np.testing.assert_allclose(act_opt_0_mol.cart_coords, opt_0_final_mol.cart_coords, atol=0.0001) np.testing.assert_equal(act_opt_0_final_e, opt_0_final_e) # Optimization of 90 torsion opt_90 = self.get_task_collection().find_one({"task_label": "opt_90"}) opt_90_final_mol = Molecule.from_dict( opt_90["output"]["optimized_molecule"]) opt_90_final_e = opt_90["output"]["final_energy"] # parse output file act_opt_90_out = QCOutput( os.path.join(test_tor_files, "opt_90", "mol.qout")) act_opt_90_mol = act_opt_90_out.data[ "molecule_from_optimized_geometry"] act_opt_90_final_e = act_opt_90_out.data["final_energy"] np.testing.assert_equal(act_opt_90_mol.species, opt_90_final_mol.species) np.testing.assert_allclose(act_opt_90_mol.cart_coords, opt_90_final_mol.cart_coords, atol=0.0001) np.testing.assert_equal(act_opt_90_final_e, opt_90_final_e) # Optimization of 180 torsion opt_180 = self.get_task_collection().find_one( {"task_label": "opt_180"}) opt_180_final_mol = Molecule.from_dict( opt_180["output"]["optimized_molecule"]) opt_180_final_e = opt_180["output"]["final_energy"] # parse output file act_opt_180_out = QCOutput( os.path.join(test_tor_files, "opt_180", "mol.qout")) act_opt_180_mol = act_opt_180_out.data[ "molecule_from_optimized_geometry"] act_opt_180_final_e = act_opt_180_out.data["final_energy"] np.testing.assert_equal(act_opt_180_mol.species, opt_180_final_mol.species) np.testing.assert_allclose(act_opt_180_mol.cart_coords, opt_180_final_mol.cart_coords, atol=0.0001) np.testing.assert_equal(act_opt_180_final_e, opt_180_final_e)
def check(self): # Checks output file for errors. self.outdata = QCOutput(self.output_file).data self.errors = self.outdata.get("errors") return len(self.errors) > 0
class QChemErrorHandler(ErrorHandler): """ Master QChemErrorHandler class that handles a number of common errors that occur during QChem runs. """ is_monitor = False def __init__(self, input_file="mol.qin", output_file="mol.qout", scf_max_cycles=200, geom_max_cycles=200): """ Initializes the error handler from a set of input and output files. Args: input_file (str): Name of the QChem input file. output_file (str): Name of the QChem output file. scf_max_cycles (int): The max iterations to set to fix SCF failure. geom_max_cycles (int): The max iterations to set to fix geometry optimization failure. """ self.input_file = input_file self.output_file = output_file self.scf_max_cycles = scf_max_cycles self.geom_max_cycles = geom_max_cycles self.qcinp = QCInput.from_file(self.input_file) self.outdata = None self.errors = [] def check(self): # Checks output file for errors. self.outdata = QCOutput(self.output_file).data self.errors = self.outdata.get("errors") return len(self.errors) > 0 def correct(self): backup({self.input_file, self.output_file}) actions = [] if "SCF_failed_to_converge" in self.errors: # Check number of SCF cycles. If not set or less than scf_max_cycles, # increase to that value and rerun. If already set, check if # scf_algorithm is unset or set to DIIS, in which case set to RCA-DIIS. # Otherwise, tell user to call SCF error handler and do nothing. if self.qcinp.rem.get("max_scf_cycles") != str( self.scf_max_cycles): self.qcinp.rem["max_scf_cycles"] = self.scf_max_cycles actions.append({"max_scf_cycles": self.scf_max_cycles}) elif self.qcinp.rem.get("scf_algorithm", "diis").lower() == "diis": self.qcinp.rem["scf_algorithm"] = "rca_diis" actions.append({"scf_algorithm": "rca_diis"}) if self.qcinp.rem.get("gen_scfman"): self.qcinp.rem["gen_scfman"] = False actions.append({"gen_scfman": False}) else: print( "More advanced changes may impact the SCF result. Use the SCF error handler" ) elif "out_of_opt_cycles" in self.errors: # Check number of opt cycles. If less than geom_max_cycles, increase # to that value, set last geom as new starting geom and rerun. if self.qcinp.rem.get( "geom_opt_max_cycles") != self.geom_max_cycles: self.qcinp.rem["geom_opt_max_cycles"] = self.geom_max_cycles actions.append({"geom_max_cycles:": self.scf_max_cycles}) if len(self.outdata.get("energy_trajectory")) > 1: if self.qcinp.molecule.spin_multiplicity != self.outdata.get( "molecule_from_last_geometry").spin_multiplicity: raise AssertionError('Multiplicities should match!') if self.qcinp.molecule.charge != self.outdata.get( "molecule_from_last_geometry").charge: raise AssertionError('Charges should match!') self.qcinp.molecule = self.outdata.get( "molecule_from_last_geometry") actions.append({"molecule": "molecule_from_last_geometry"}) else: print( "How do I get the geometry optimization converged when already at the maximum number of cycles?" ) elif "unable_to_determine_lamda" in self.errors: # Set last geom as new starting geom and rerun. If no opt cycles, # use diff SCF strat? Diff initial guess? Change basis? if len(self.outdata.get("energy_trajectory")) > 1: if self.qcinp.molecule.spin_multiplicity != self.outdata.get( "molecule_from_last_geometry").spin_multiplicity: raise AssertionError('Multiplicities should match!') if self.qcinp.molecule.charge != self.outdata.get( "molecule_from_last_geometry").charge: raise AssertionError('Charges should match!') self.qcinp.molecule = self.outdata.get( "molecule_from_last_geometry") actions.append({"molecule": "molecule_from_last_geometry"}) elif self.qcinp.rem.get("scf_algorithm", "diis").lower() == "diis": self.qcinp.rem["scf_algorithm"] = "rca_diis" actions.append({"scf_algorithm": "rca_diis"}) if self.qcinp.rem.get("gen_scfman"): self.qcinp.rem["gen_scfman"] = False actions.append({"gen_scfman": False}) else: print( "Use a different initial guess? Perhaps a different basis?" ) elif "linear_dependent_basis" in self.errors: # DIIS -> RCA_DIIS. If already RCA_DIIS, change basis? if self.qcinp.rem.get("scf_algorithm", "diis").lower() == "diis": self.qcinp.rem["scf_algorithm"] = "rca_diis" actions.append({"scf_algorithm": "rca_diis"}) if self.qcinp.rem.get("gen_scfman"): self.qcinp.rem["gen_scfman"] = False actions.append({"gen_scfman": False}) else: print("Perhaps use a better basis?") elif "failed_to_transform_coords" in self.errors: # Check for symmetry flag in rem. If not False, set to False and rerun. # If already False, increase threshold? if not self.qcinp.rem.get("sym_ignore") or self.qcinp.rem.get( "symmetry"): self.qcinp.rem["sym_ignore"] = True self.qcinp.rem["symmetry"] = False actions.append({"sym_ignore": True}) actions.append({"symmetry": False}) else: print("Perhaps increase the threshold?") elif "input_file_error" in self.errors: print( "Something is wrong with the input file. Examine error message by hand." ) return {"errors": self.errors, "actions": None} elif "failed_to_read_input" in self.errors: # Almost certainly just a temporary problem that will not be encountered again. Rerun job as-is. actions.append({"rerun job as-is"}) elif "IO_error" in self.errors: # Almost certainly just a temporary problem that will not be encountered again. Rerun job as-is. actions.append({"rerun job as-is"}) elif "unknown_error" in self.errors: print("Examine error message by hand.") return {"errors": self.errors, "actions": None} else: # You should never get here. If correct is being called then errors should have at least one entry, # in which case it should have been caught by the if/elifs above. print( "If you get this message, something has gone terribly wrong!") return {"errors": self.errors, "actions": None} os.rename(self.input_file, self.input_file + ".last") self.qcinp.write_file(self.input_file) return {"errors": self.errors, "actions": actions}
def setUp(self, lpad=False): out_file = os.path.join(module_dir, "..", "..", "test_files", "FF_working", "test.qout.opt_0") qc_out = QCOutput(filename=out_file) self.act_mol = qc_out.data["initial_molecule"] super(TestCore, self).setUp(lpad=False)