def setUp(self): pymagen_sodium = Molecule(species=['Na'], coords=[[999.0, 999.0, 999.0]], charge=1) # noinspection PyProtectedMember sodium_obmol = BabelMolAdaptor(pymagen_sodium)._obmol acetoxyq_anion_qcout = QcOutput( os.path.join(test_dir, "acetoxyq_anion.out")) pymatgen_acetoxyq = acetoxyq_anion_qcout.data[0]["molecules"][-1] acetoxyq_obmol = BabelMolAdaptor(pymatgen_acetoxyq)._obmol tfsi_qcout = QcOutput(os.path.join(test_dir, "tfsi.qcout")) pymatgen_tfsi = tfsi_qcout.data[0]["molecules"][-1] # noinspection PyProtectedMember tfsi_obmol = BabelMolAdaptor(pymatgen_tfsi)._obmol fragments = [sodium_obmol, tfsi_obmol] nums_fragments = [1, 1] self.acetoxyq_natfsi_placer = IonPlacer(acetoxyq_obmol, fragments, nums_fragments, None) rad_util = AtomicRadiusUtils(covalent_radius_scale=3.0, metal_radius_scale=1.5) mol_radius = rad_util.get_radius(acetoxyq_obmol) cation_radius = rad_util.get_radius(sodium_obmol) anion_radius = rad_util.get_radius(tfsi_obmol) mol_coords = IonPlacer.normalize_molecule(acetoxyq_obmol) fragments_atom_radius = [cation_radius, anion_radius] nums_fragments = [1, 1] self.detector = ContactDetector(mol_coords, mol_radius, fragments_atom_radius, nums_fragments)
def test_structural_change_in_geom_opt(self): qcout_path = os.path.join(test_dir, "mol_1_3_bond.qcout") qcout = QcOutput(qcout_path) mol1 = qcout.data[0]["molecules"][0] mol2 = qcout.data[0]["molecules"][-1] priority_bonds = [[0, 1], [0, 2], [1, 3], [1, 4], [1, 7], [2, 5], [2, 6], [2, 8], [4, 6], [4, 10], [6, 9]] msc = MoleculeStructureComparator(priority_bonds=priority_bonds) self.assertTrue(msc.are_equal(mol1, mol2))
def check(self): # Checks output file for errors. self.outdata = QcOutput(self.output_file).data self.qcinp = QcInput.from_file(self.input_file) self.error_step_id = None self.errors = None self.fix_step = None for i, od in enumerate(self.outdata): if od["has_error"]: self.error_step_id = i self.fix_step = self.qcinp.jobs[i] self.errors = sorted(list(set(od["errors"]))) return True return False
def test_get_basis(self): filename = os.path.join(test_dir, "quinoxaline_anion.qcout") qcout = QcOutput(filename) charges = qcout.data[0]["charges"]["nbo"] mol = qcout.data[0]["molecules"][-1] basis = self.generator.get_basis(mol, charges) ans = [('C', '6-31G*'), ('C', '6-31G*'), ('C', '6-31G*'), ('C', '6-31G*'), ('C', '6-31G*'), ('C', '6-31G*'), ('H', '6-31G*'), ('H', '6-31G*'), ('H', '6-31G*'), ('H', '6-31G*'), ('C', '6-31G*'), ('C', '6-31G*'), ('H', '6-31G*'), ('N', '6-31+G*'), ('N', '6-31+G*'), ('H', '6-31G*')] self.maxDiff = None self.assertEqual(basis, [(b[0], b[1].lower()) for b in ans])
def main(): def gcd(a, b): if b == 0: return a else: return gcd(b, a % b) def lcm(a, b): return a * b / gcd(a, b) import argparse parser = argparse.ArgumentParser( description="Place salt around a molecule") parser.add_argument("-m", "--molecule", dest="molecule", type=str, required=True, help="the file name of molecule") parser.add_argument("-l", "--ligand", dest="fragments", type=str, nargs='+', required=True, help="the list of fragment file names to to be placed around the molecule") parser.add_argument("-n", "--nums_fragments", dest="nums_fragments", type=int, nargs='+', required=True, help="the number of each fragment, the order must be the same with FRAGMENTS") parser.add_argument("-c", "--charge", dest="charge", type=int, required=True, help="total charge of the system") parser.add_argument("-t", "--taboo_tolerance", dest="taboo_tolerance", type=float, default=1.0, help="The radius to taboo a solution (in Angstrom)") parser.add_argument("-r", "--ratio_taboo_particles", dest="ratio_taboo_particles", type=float, default=0.5, help="ratio of particle within the tolerance to consider taboo current solution") parser.add_argument("-o", "--outputfile", dest="outputfile", type=str, required=True, help="the file name of the aligned conformer") parser.add_argument("-i", "--iterations", dest="iterations", type=int, default=600, help="maximum number of evaluations") parser.add_argument("-s", "--size", dest="size", type=int, default=15, help="population size") parser.add_argument("-k", "--num_neighbours", dest="num_neighbours", type=int, default=2, help="number of neighbours") parser.add_argument("--force_ordered_fragment", dest="force_ordered_fragment", action="store_true", help="set this option to keep the fragment of the same in the order of input along the X-axis") parser.add_argument("--topology", dest="topology", choices=["ring", "star"], type=str, default="ring", help="the topology of the PSO information network") parser.add_argument("--initial_guess", dest="initial_guess", choices=["breadth", "center", "volume"], default="breadth", help="where should particles should be initially put") parser.add_argument("--bound_setter", dest="bound_setter", choices=["chain", "volume"], default="chain", help="method to set the bound conditions of PSO") parser.add_argument("--always_write_best", dest="always_write_best", action="store_true", help="enable this option to output the best structure at every iteration") parser.add_argument("--random_seed", dest="random_seed", default=None, type=int, help="random seed for PSO, an integer is expected") parser.add_argument("--max_generations_each_conformer", dest="max_generations_each_conformer", default=100, type=int, help="maximum generations for each conformer") parser.add_argument("-e", "--evaluator", dest="evaluator", type=str, default="hardsphere", choices=["hardsphere", "sqm"], help="Energy Evaluator") options = parser.parse_args() if options.evaluator == 'hardsphere': qcout_molecule = QcOutput(options.molecule) qcout_cation = QcOutput(options.cation) qcout_anion = QcOutput(options.anion) total_charge_cation = qcout_cation.data[0]["molecules"][-1].charge total_charge_anion = qcout_anion.data[0]["molecules"][-1].charge total_charge_mol = qcout_molecule.data[0]["molecules"][-1].charge num_lcm = lcm(total_charge_cation, -total_charge_anion) num_cation = num_lcm / total_charge_cation num_anion = num_lcm / -total_charge_anion pymatgen_mol_molecule = qcout_molecule.data[0]["molecules"][-1] pymatgen_mol_cation = qcout_cation.data[0]["molecules"][-1] pymatgen_mol_anion = qcout_anion.data[0]["molecules"][-1] # noinspection PyProtectedMember molecule = BabelMolAdaptor(pymatgen_mol_molecule)._obmol # noinspection PyProtectedMember obmol_cation = BabelMolAdaptor(pymatgen_mol_cation)._obmol # noinspection PyProtectedMember obmol_anion = BabelMolAdaptor(pymatgen_mol_anion)._obmol energy_evaluator = HardSphereElectrostaticEnergyEvaluator.from_qchem_output( qcout_molecule, qcout_cation, qcout_anion) fragments = [obmol_cation, obmol_anion] else: # noinspection PyProtectedMember molecule = BabelMolAdaptor.from_file(options.molecule, os.path.splitext( options.molecule)[1][ 1:])._obmol fragments = [] for frag_file in options.fragments: file_format = os.path.splitext(frag_file)[1][1:] # noinspection PyProtectedMember fragments.append( BabelMolAdaptor.from_file(frag_file, file_format)._obmol) energy_evaluator = SemiEmpricalQuatumMechanicalEnergyEvaluator( molecule, fragments, options.nums_fragments, total_charge=options.charge, taboo_tolerance_ang=options.taboo_tolerance, force_order_fragment=options.force_ordered_fragment, bound_setter=options.bound_setter) if len(fragments) != len(options.nums_fragments): raise ValueError( "you must specify the duplicated count for every fragment") placer = IonPlacer(molecule=molecule, fragments=fragments, nums_fragments=options.nums_fragments, energy_evaluator=energy_evaluator, taboo_tolerance_ang=options.taboo_tolerance, taboo_tolerance_particle_ratio=options.ratio_taboo_particles, topology=options.topology, initial_guess=options.initial_guess, bound_setter=options.bound_setter, always_write_best=options.always_write_best, random_seed=options.random_seed, max_generations_each_conformer=options.max_generations_each_conformer) energy_evaluator.arranger = placer placer.place(max_evaluations=options.iterations, pop_size=options.size, neighborhood_size=options.num_neighbours) print('It took {:.1f} seconds to place the salt'.format(placer .playing_time))
def _run_qchem_on_alcf(self, log_file_object=None): parent_qcinp = QcInput.from_file(self.input_file) njobs = len(parent_qcinp.jobs) return_codes = [] alcf_cmds = [] qc_jobids = [] for i, j in enumerate(parent_qcinp.jobs): qsub_cmd = copy.deepcopy(self.current_command) sub_input_filename = "alcf_{}_{}".format(i + 1, self.input_file) sub_output_filename = "alcf_{}_{}".format(i + 1, self.output_file) sub_log_filename = "alcf_{}_{}".format(i + 1, self.qclog_file) qsub_cmd[-2] = sub_input_filename sub_qcinp = QcInput([copy.deepcopy(j)]) if "scf_guess" in sub_qcinp.jobs[0].params["rem"] and \ sub_qcinp.jobs[0].params["rem"]["scf_guess"] == "read": sub_qcinp.jobs[0].params["rem"].pop("scf_guess") if i > 0: if isinstance(j.mol, str) and j.mol == "read": prev_qcout_filename = "alcf_{}_{}".format( i + 1 - 1, self.output_file) prev_qcout = QcOutput(prev_qcout_filename) prev_final_mol = prev_qcout.data[0]["molecules"][-1] j.mol = prev_final_mol sub_qcinp.write_file(sub_input_filename) logging.info("The command to run QChem is {}".format( ' '.join(qsub_cmd))) alcf_cmds.append(qsub_cmd) p = subprocess.Popen(qsub_cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = p.communicate() qc_jobid = int(out.strip()) qc_jobids.append(qc_jobid) cqwait_cmd = shlex.split("cqwait {}".format(qc_jobid)) subprocess.call(cqwait_cmd) output_file_name = "{}.output".format(qc_jobid) cobaltlog_file_name = "{}.cobaltlog".format(qc_jobid) with open(cobaltlog_file_name) as f: cobaltlog_last_line = f.readlines()[-1] exit_code_pattern = re.compile( "an exit code of (?P<code>\d+);") m = exit_code_pattern.search(cobaltlog_last_line) if m: rc = float(m.group("code")) else: rc = -99999 return_codes.append(rc) for name_change_trial in range(10): if not os.path.exists(output_file_name): message = "{} is not found in {}, {}th wait " \ "for 5 mins\n".format( output_file_name, os.getcwd(), name_change_trial) logging.info(message) if log_file_object: log_file_object.writelines([message]) time.sleep(60 * 5) pass else: message = "Found qchem output file {} in {}, change file " \ "name\n".format(output_file_name, os.getcwd(), name_change_trial) logging.info(message) if log_file_object: log_file_object.writelines([message]) break log_file_object.flush() os.fsync(log_file_object.fileno()) shutil.move(output_file_name, sub_output_filename) shutil.move(cobaltlog_file_name, sub_log_filename) overall_return_code = min(return_codes) with open(self.output_file, "w") as out_file_object: for i, job_cmd, rc, qc_jobid in zip(range(njobs), alcf_cmds, return_codes, qc_jobids): sub_output_filename = "alcf_{}_{}".format( i + 1, self.output_file) sub_log_filename = "alcf_{}_{}".format(i + 1, self.qclog_file) with open(sub_output_filename) as sub_out_file_object: header_lines = [ "Running Job {} of {} {}\n".format( i + 1, njobs, self.input_file), " ".join(job_cmd) + "\n" ] if i > 0: header_lines = ['', ''] + header_lines sub_out = sub_out_file_object.readlines() out_file_object.writelines(header_lines) out_file_object.writelines(sub_out) if rc < 0 and rc != -99999: out_file_object.writelines([ "Application {} exit codes: {}\n".format( qc_jobid, rc), '\n', '\n' ]) if log_file_object: with open(sub_log_filename) as sub_log_file_object: sub_log = sub_log_file_object.readlines() log_file_object.writelines(sub_log) return overall_return_code
def main(): import argparse parser = argparse.ArgumentParser( description="Run A QChem Job for a QChem Input File") parser.add_argument("-i", "--input", dest="input", type=str, required=True, help="the QChem output file with imaginary frequency") parser.add_argument("-o", "--output", dest="output", type=str, required=True, help="the QChem input file with perturbed geometry") parser.add_argument("-s", "--scale", dest="scale", type=float, default=0.3, help="the scale factor to perturb molecule") parser.add_argument("-r", "--reverse", dest="reverse", action="store_true", help="use reversed direction to perturb molecule") parser.add_argument("-v", "--verbose", dest="verbose", action="store_true", help="print parameters") options = parser.parse_args() qcout = QcOutput(options.input) charge = None spin_multiplicity = None for d in qcout.data: j = d["input"] if j.charge is not None: charge = j.charge if j.spin_multiplicity is not None: spin_multiplicity = j.spin_multiplicity if qcout.data[-1]['frequencies'][0]["frequency"] < -0.00: old_mol = qcout.data[-1]["molecules"][-1] vib_mode = qcout.data[-1]['frequencies'][0]["vib_mode"] if options.verbose: if options.reverse: direction_text = "User forward direction" else: direction_text = "User reversed direction" print("{} with scale factor {}".format(direction_text, options.scale)) new_mol = perturb_molecule(old_mol, vib_mode, reversed_direction=options.reverse, perturb_scale=options.scale) qctask_freq = qcout.data[-1]["input"] qctask_freq.mol = "read" qctask_freq.charge = charge qctask_freq.spin_multiplicity = spin_multiplicity qctask_opt = copy.deepcopy(qctask_freq) qctask_opt.params["rem"]["jobtype"] = "opt" qctask_opt.params["rem"].pop("scf_guess", None) qctask_opt.mol = new_mol qcinp = QcInput([qctask_opt, qctask_freq]) qcinp.write_file(options.output) else: raise ValueError( "Must have an imaginary frequency to perturb the molecule")
def main(): import argparse parser = argparse.ArgumentParser( description="Run A QChem Job for a QChem Input File") parser.add_argument("-f", "--filename", dest="filename", type=str, required=True, help="the QChem input filename") parser.add_argument("-e", "--eliminate", dest="eli_img", action="store_true", help="whether to eliminate imaginary frequency") parser.add_argument("-b", "--mixed_basis", dest="mixed_basis", type=json.loads, required=False, help="The mixed basis as a dict") parser.add_argument("-a", "--mixed_aux_basis", dest="mixed_aux_basis", type=json.loads, required=False, help="The mixed auxiliary basis as a dict") parser.add_argument("-s", "--solvent", dest="solvent", type=str, required=False, help="the implicit solvent") parser.add_argument("-n", "--run_name", dest="run_name", type=str, default=None, help="the implicit solvent") options = parser.parse_args() call_qchem_task(options.filename, options.solvent, options.mixed_basis, options.mixed_aux_basis, run_name=options.run_name) if options.eli_img: base_filename = os.path.splitext(options.filename)[0] output_filename = base_filename + ".qcout" qcout = QcOutput(output_filename) charge = None spin_multiplicity = None for d in qcout.data: j = d["input"] if j.charge is not None: charge = j.charge if j.spin_multiplicity is not None: spin_multiplicity = j.spin_multiplicity if qcout.data[-1]['frequencies'][0]["frequency"] < -0.00: os.system("tar czvf img_freq_1.tar.gz *") old_mol = qcout.data[-1]["molecules"][-1] vib_mode = qcout.data[-1]['frequencies'][0]["vib_mode"] new_mol = perturb_molecule(old_mol, vib_mode) qctask_freq = qcout.data[-1]["input"] qctask_freq.mol = "read" qctask_freq.charge = charge qctask_freq.spin_multiplicity = spin_multiplicity qctask_opt = copy.deepcopy(qctask_freq) qctask_opt.params["rem"]["jobtype"] = "opt" qctask_opt.params["rem"].pop("scf_guess", None) qctask_opt.mol = new_mol qcinp = QcInput([qctask_opt, qctask_freq]) eli_file_1 = base_filename + "_eli_img_1.qcinp" qcinp.write_file(eli_file_1) call_qchem_task(eli_file_1, options.solvent, options.mixed_basis, options.mixed_aux_basis, run_name=options.run_name) output_filename = base_filename + "_eli_img_1.qcout" qcout = QcOutput(output_filename) if qcout.data[-1]['frequencies'][0]["frequency"] < -0.00: os.system("tar czvf img_freq_2.tar.gz *") old_mol = qcout.data[-1]["molecules"][-1] vib_mode = qcout.data[-1]['frequencies'][0]["vib_mode"] new_mol = perturb_molecule(old_mol, vib_mode) qctask_freq = qcout.data[-1]["input"] qctask_freq.mol = "read" qctask_freq.charge = charge qctask_freq.spin_multiplicity = spin_multiplicity qctask_opt = copy.deepcopy(qctask_freq) qctask_opt.params["rem"]["jobtype"] = "opt" qctask_opt.params["rem"].pop("scf_guess", None) qctask_opt.mol = new_mol qcinp = QcInput([qctask_opt, qctask_freq]) for j in qcinp.jobs: j.set_dft_grid(128, 302) j.set_integral_threshold(12) if j.params["rem"]["jobtype"] == "opt": j.scale_geom_opt_threshold(0.1, 0.1, 0.1) j.set_geom_max_iterations(100) eli_file_2 = base_filename + "_eli_img_2.qcinp" qcinp.write_file(eli_file_2) call_qchem_task(eli_file_2, options.solvent, options.mixed_basis, options.mixed_aux_basis, run_name=options.run_name) output_filename = base_filename + "_eli_img_2.qcout" qcout = QcOutput(output_filename) if qcout.data[-1]['frequencies'][0]["frequency"] < -0.00: os.system("tar czvf img_freq_3.tar.gz *") old_mol = qcout.data[-1]["molecules"][-1] vib_mode = qcout.data[-1]['frequencies'][0]["vib_mode"] new_mol = perturb_molecule(old_mol, vib_mode) qctask_freq = qcout.data[-1]["input"] qctask_freq.mol = "read" qctask_freq.charge = charge qctask_freq.spin_multiplicity = spin_multiplicity qctask_opt = copy.deepcopy(qctask_freq) qctask_opt.params["rem"]["jobtype"] = "opt" qctask_opt.params["rem"].pop("scf_guess", None) qctask_opt.mol = new_mol qcinp = QcInput([qctask_opt, qctask_freq]) for j in qcinp.jobs: j.set_dft_grid(90, 590) j.set_integral_threshold(12) if j.params["rem"]["jobtype"] == "opt": j.scale_geom_opt_threshold(0.1, 0.1, 0.1) j.set_geom_max_iterations(100) eli_file_3 = base_filename + "_eli_img_3.qcinp" qcinp.write_file(eli_file_3) call_qchem_task(eli_file_3, options.solvent, options.mixed_basis, options.mixed_aux_basis, run_name=options.run_name)
def get_task_doc(cls, path, fw_spec=None): """ Get the entire task doc for a path, including any post-processing. """ logger.info("Getting task doc for file:{}".format(path)) qcout = QcOutput(zpath(path)) data = qcout.data initial_mol = data[0]["molecules"][0] mol = data[0]["molecules"][-1] if data[0]["jobtype"] == "freq": mol = Molecule.from_dict(initial_mol.as_dict()) bb = BabelMolAdaptor(mol) pbmol = bb.pybel_mol xyz = XYZ(mol) smiles = pbmol.write(str("smi")).split()[0] can = pbmol.write(str("can")).split()[0] inchi_final = pbmol.write(str("inchi")).strip() svg = cls.modify_svg(cls.xyz2svg(xyz)) comp = mol.composition charge = mol.charge spin_mult = mol.spin_multiplicity data_dict = {} pga = PointGroupAnalyzer(mol) sch_symbol = pga.sch_symbol stationary_type = None has_structure_changing_job = False for d in data: if d["jobtype"] == "opt": data_dict["geom_opt"] = d has_structure_changing_job = True elif d["jobtype"] == "freq": data_dict["freq"] = d has_structure_changing_job = True if not d["has_error"]: if d['frequencies'][0]["frequency"] < -0.00: # it is stupied that -0.00 is less than 0.00 stationary_type = "non-minimum" else: stationary_type = "minimum" else: stationary_type = "unknown" elif d["jobtype"] == "sp": suffix = "" if d["solvent_method"] == "NA" \ else "_" + d["solvent_method"] data_dict["scf" + suffix] = d elif d["jobtype"] == "aimd": data_dict["amid"] = d has_structure_changing_job = True data = data_dict d = { "path": os.path.abspath(path), "folder": os.path.basename(os.path.dirname(os.path.abspath(path))), "calculations": data, "molecule_initial": initial_mol.as_dict(), "molecule_final": mol.as_dict(), "pointgroup": sch_symbol, "pretty_formula": comp.reduced_formula, "reduced_cell_formula_abc": comp.alphabetical_formula, "formula": comp.formula, "charge": charge, "spin_multiplicity": spin_mult, "composition": comp.as_dict(), "elements": list(comp.as_dict().keys()), "nelements": len(comp), "smiles": smiles, "can": can, "inchi_final": inchi_final, "svg": svg, "xyz": str(xyz), "names": get_nih_names(smiles) } if stationary_type: d['stationary_type'] = stationary_type if fw_spec: inchi_initial = fw_spec['inchi'] if inchi_initial != d['inchi_final']: d['inchi_changed'] = True else: d['inchi_changed'] = False if has_structure_changing_job: d['structure_changed'] = cls._check_structure_change( initial_mol, mol, path) else: d['structure_changed'] = False if d['structure_changed']: d['state'] = 'rejected' d['reject_reason'] = 'structural change' if "state" not in d: for v in data_dict.values(): if v['has_error']: d['state'] = "error" errors = d.get("errors", []) errors += v["errors"] d["errors"] = errors if "state" not in d: d["state"] = "successful" return jsanitize(d)