|
| 1 | +# This module defines firetasks for writing QChem input files |
| 2 | + |
| 3 | +import os |
| 4 | + |
| 5 | +from fireworks import FiretaskBase, explicit_serialize |
| 6 | +from pymatgen.analysis.graphs import MoleculeGraph |
| 7 | +from pymatgen.analysis.local_env import OpenBabelNN |
| 8 | +from pymatgen.io.qchem.inputs import QCInput |
| 9 | + |
| 10 | +from atomate.utils.utils import load_class |
| 11 | + |
| 12 | +__author__ = "Brandon Wood" |
| 13 | +__copyright__ = "Copyright 2018, The Materials Project" |
| 14 | +__version__ = "0.1" |
| 15 | +__maintainer__ = "Brandon Wood" |
| 16 | + |
| 17 | +__status__ = "Alpha" |
| 18 | +__date__ = "5/20/18" |
| 19 | +__credits__ = "Sam Blau, Shyam Dwaraknath" |
| 20 | + |
| 21 | + |
| 22 | +@explicit_serialize |
| 23 | +class WriteInputFromIOSet(FiretaskBase): |
| 24 | + """ |
| 25 | + Writes QChem Input files from input sets. A dictionary is passed to WriteInputFromIOSet where |
| 26 | + parameters are given as keys in the dictionary. |
| 27 | +
|
| 28 | + required_params: |
| 29 | + qc_input_set (QChemDictSet or str): Either a QChemDictSet object or a string |
| 30 | + name for the QChem input set (e.g., "OptSet"). *** Note that if the molecule is to be inherited through |
| 31 | + fw_spec qc_input_set must be a string name for the QChem input set. *** |
| 32 | +
|
| 33 | + optional_params: |
| 34 | + molecule (Molecule): Molecule that will be subjected to an electronic structure calculation |
| 35 | + qchem_input_params (dict): When using a string name for QChem input set, use this as a dict |
| 36 | + to specify kwargs for instantiating the input set parameters. This setting is |
| 37 | + ignored if you provide the full object representation of a QChemDictSet. Basic uses |
| 38 | + would be to modify the default inputs of the set, such as dft_rung, basis_set, |
| 39 | + pcm_dielectric, scf_algorithm, or max_scf_cycles. See pymatgen/io/qchem/sets.py for |
| 40 | + default values of all input parameters. For instance, if a user wanted to use a |
| 41 | + more advanced DFT functional, include a pcm with a dielectric of 30, and use a |
| 42 | + larger basis, the user would set qchem_input_params = {"dft_rung": 5, |
| 43 | + "pcm_dielectric": 30, "basis_set": "6-311++g**"}. However, more advanced |
| 44 | + customization of the input is also possible through the overwrite_inputs key which |
| 45 | + allows the user to directly modify the rem, pcm, smd, and solvent dictionaries that |
| 46 | + QChemDictSet passes to inputs.py to print an actual input file. For instance, if a |
| 47 | + user wanted to set the sym_ignore flag in the rem section of the input file to |
| 48 | + true, then they would set qchem_input_params = {"overwrite_inputs": "rem": |
| 49 | + {"sym_ignore": "true"}}. Of course, overwrite_inputs could be used in conjunction |
| 50 | + with more typical modifications, as seen in the test_double_FF_opt workflow test. |
| 51 | + input_file (str): Name of the QChem input file. Defaults to mol.qin |
| 52 | + write_to_dir (str): Path of the directory where the QChem input file will be written, |
| 53 | + the default is to write to the current working directory |
| 54 | + """ |
| 55 | + |
| 56 | + required_params = ["qchem_input_set"] |
| 57 | + optional_params = ["molecule", "qchem_input_params", "input_file", "write_to_dir"] |
| 58 | + |
| 59 | + def run_task(self, fw_spec): |
| 60 | + input_file = os.path.join( |
| 61 | + self.get("write_to_dir", ""), self.get("input_file", "mol.qin") |
| 62 | + ) |
| 63 | + |
| 64 | + # if a full QChemDictSet object was provided |
| 65 | + if hasattr(self["qchem_input_set"], "write_file"): |
| 66 | + qcin = self["qchem_input_set"] |
| 67 | + # if a molecule is being passed through fw_spec |
| 68 | + elif fw_spec.get("prev_calc_molecule"): |
| 69 | + prev_calc_mol = fw_spec.get("prev_calc_molecule") |
| 70 | + # if a molecule is also passed as an optional parameter |
| 71 | + if self.get("molecule"): |
| 72 | + mol = self.get("molecule") |
| 73 | + # check if mol and prev_calc_mol are isomorphic |
| 74 | + mol_graph = MoleculeGraph.with_local_env_strategy(mol, OpenBabelNN()) |
| 75 | + prev_mol_graph = MoleculeGraph.with_local_env_strategy( |
| 76 | + prev_calc_mol, OpenBabelNN() |
| 77 | + ) |
| 78 | + # If they are isomorphic, aka a previous FW has not changed bonding, |
| 79 | + # then we will use prev_calc_mol. If bonding has changed, we will use mol. |
| 80 | + if mol_graph.isomorphic_to(prev_mol_graph): |
| 81 | + mol = prev_calc_mol |
| 82 | + elif self["qchem_input_set"] != "OptSet": |
| 83 | + print( |
| 84 | + "WARNING: Molecule from spec is not isomorphic to passed molecule!" |
| 85 | + ) |
| 86 | + mol = prev_calc_mol |
| 87 | + else: |
| 88 | + print( |
| 89 | + "Not using prev_calc_mol as it is not isomorphic to passed molecule!" |
| 90 | + ) |
| 91 | + else: |
| 92 | + mol = prev_calc_mol |
| 93 | + |
| 94 | + qcin_cls = load_class("pymatgen.io.qchem.sets", self["qchem_input_set"]) |
| 95 | + qcin = qcin_cls(mol, **self.get("qchem_input_params", {})) |
| 96 | + # if a molecule is only included as an optional parameter |
| 97 | + elif self.get("molecule"): |
| 98 | + qcin_cls = load_class("pymatgen.io.qchem.sets", self["qchem_input_set"]) |
| 99 | + qcin = qcin_cls(self.get("molecule"), **self.get("qchem_input_params", {})) |
| 100 | + # if no molecule is present raise an error |
| 101 | + else: |
| 102 | + raise KeyError( |
| 103 | + "No molecule present, add as an optional param or check fw_spec" |
| 104 | + ) |
| 105 | + qcin.write(input_file) |
| 106 | + |
| 107 | + |
| 108 | +@explicit_serialize |
| 109 | +class WriteCustomInput(FiretaskBase): |
| 110 | + """ |
| 111 | + Writes QChem Input files from custom input sets. This firetask gives the maximum flexibility when trying |
| 112 | + to define custom input parameters. |
| 113 | +
|
| 114 | + required_params: |
| 115 | + qchem_input_custom (dict): Define custom input parameters to generate a qchem input file. |
| 116 | + This should be a dictionary of dictionaries (i.e. {{"rem": {"method": "b3lyp", basis": "6-31*G++", ...} |
| 117 | + Each QChem section should be a key with its own dictionary as the value. For more details on how |
| 118 | + the input should be structured look at pymatgen.io.qchem.inputs |
| 119 | + *** *** |
| 120 | +
|
| 121 | + optional_params: |
| 122 | + input_file (str): Name of the QChem input file. Defaults to mol.qin |
| 123 | + write_to_dir (str): Path of the directory where the QChem input file will be written, |
| 124 | + the default is to write to the current working directory |
| 125 | + """ |
| 126 | + |
| 127 | + required_params = ["rem"] |
| 128 | + # optional_params will need to be modified if more QChem sections are added QCInput |
| 129 | + optional_params = [ |
| 130 | + "molecule", |
| 131 | + "opt", |
| 132 | + "pcm", |
| 133 | + "solvent", |
| 134 | + "van_der_waals", |
| 135 | + "input_file", |
| 136 | + "write_to_dir", |
| 137 | + ] |
| 138 | + |
| 139 | + def run_task(self, fw_spec): |
| 140 | + input_file = os.path.join( |
| 141 | + self.get("write_to_dir", ""), self.get("input_file", "mol.qin") |
| 142 | + ) |
| 143 | + # if a molecule is being passed through fw_spec |
| 144 | + if fw_spec.get("prev_calc_molecule"): |
| 145 | + prev_calc_mol = fw_spec.get("prev_calc_molecule") |
| 146 | + # if a molecule is also passed as an optional parameter |
| 147 | + if self.get("molecule"): |
| 148 | + mol = self.get("molecule") |
| 149 | + # check if mol and prev_calc_mol are isomorphic |
| 150 | + mol_graph = MoleculeGraph.with_local_env_strategy(mol, OpenBabelNN()) |
| 151 | + prev_mol_graph = MoleculeGraph.with_local_env_strategy( |
| 152 | + prev_calc_mol, OpenBabelNN() |
| 153 | + ) |
| 154 | + if mol_graph.isomorphic_to(prev_mol_graph): |
| 155 | + mol = prev_calc_mol |
| 156 | + else: |
| 157 | + print( |
| 158 | + "WARNING: Molecule from spec is not isomorphic to passed molecule!" |
| 159 | + ) |
| 160 | + else: |
| 161 | + mol = prev_calc_mol |
| 162 | + elif self.get("molecule"): |
| 163 | + mol = self.get("molecule") |
| 164 | + else: |
| 165 | + raise KeyError( |
| 166 | + "No molecule present, add as an optional param or check fw_spec" |
| 167 | + ) |
| 168 | + # in the current structure there needs to be a statement for every optional QChem section |
| 169 | + # the code below defaults the section to None if the variable is not passed |
| 170 | + opt = self.get("opt", None) |
| 171 | + pcm = self.get("pcm", None) |
| 172 | + solvent = self.get("solvent", None) |
| 173 | + vdw_mode = self.get("vdw_mode", "atomic") |
| 174 | + van_der_waals = self.get("van_der_waals", None) |
| 175 | + |
| 176 | + qcin = QCInput( |
| 177 | + molecule=mol, |
| 178 | + rem=self["rem"], |
| 179 | + opt=opt, |
| 180 | + pcm=pcm, |
| 181 | + solvent=solvent, |
| 182 | + van_der_waals=van_der_waals, |
| 183 | + vdw_mode=vdw_mode, |
| 184 | + ) |
| 185 | + qcin.write_file(input_file) |
| 186 | + |
| 187 | + |
| 188 | +@explicit_serialize |
| 189 | +class WriteInput(FiretaskBase): |
| 190 | + """ |
| 191 | + Writes QChem input file from QCInput object. |
| 192 | +
|
| 193 | + required_params: |
| 194 | + qc_input (QCInput): QCInput object |
| 195 | +
|
| 196 | + optional_params: |
| 197 | + input_file (str): Name of the QChem input file. Defaults to mol.qin |
| 198 | + write_to_dir (str): Path of the directory where the QChem input file will be written, |
| 199 | + the default is to write to the current working directory |
| 200 | +
|
| 201 | + """ |
| 202 | + |
| 203 | + required_params = ["qc_input"] |
| 204 | + optional_params = ["input_file", "write_to_dir"] |
| 205 | + |
| 206 | + def run_task(self, fw_spec): |
| 207 | + # if a QCInput object is provided |
| 208 | + input_file = os.path.join( |
| 209 | + self.get("write_to_dir", ""), self.get("input_file", "mol.qin") |
| 210 | + ) |
| 211 | + |
| 212 | + qcin = self["qc_input"] |
| 213 | + qcin.write_file(input_file) |
0 commit comments