diff --git a/HISTORY.rst b/HISTORY.rst index 1c24bf8..473b8c6 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -1,6 +1,11 @@ ======= History ======= +2024.10.5 -- Enhancements and bug fixes for thermochemistry + * Improved GUI for thermochemistry so that it automatically recognizes whether it is + after e.g. an optimization and configures appropriately. + * Fixed and issue with transferring the multipole moments to the JSON file + 2024.7.30 -- Enhanced results and fixed problem with psi4.ini * Added to the results by using cclib to parse the output. * If ~/SEAMM/psi4.ini did not exist the version that was automatically created was diff --git a/psi4_step/energy.py b/psi4_step/energy.py index a2de91b..c598402 100644 --- a/psi4_step/energy.py +++ b/psi4_step/energy.py @@ -168,21 +168,7 @@ def get_input(self, calculation_type="energy", restart=None): lines.append("#" * 80) # Figure out what we are doing! - if P["level"] == "recommended": - method_string = P["method"] - else: - method_string = P["advanced_method"] - - # Allow the full name, or the short name, or just pray. - if method_string in psi4_step.methods: - method = psi4_step.methods[method_string]["method"] - else: - method = method_string.lower() - for key in psi4_step.methods: - if psi4_step.methods[key]["method"] == method: - break - else: - method = method_string + method, functional, method_string = self.get_method() # lines.append('set scf_type df') # lines.append('set guess sad') @@ -251,32 +237,12 @@ def get_input(self, calculation_type="energy", restart=None): lines.append("") if method == "dft": - if P["level"] == "recommended": - functional_string = P["functional"] - else: - functional_string = P["advanced_functional"] - - # Allow the full name, or the short name, or just pray. - if functional_string in psi4_step.dft_functionals: - functional = psi4_step.dft_functionals[functional_string]["name"] - else: - functional = functional_string.lower() - for key in psi4_step.dft_functionals: - if psi4_step.dft_functionals[key]["name"] == functional: - break - else: - functional = functional_string - - if ( - P["dispersion"] != "none" - and len(psi4_step.dft_functionals[functional_string]["dispersion"]) > 1 - ): - functional = functional + "-" + P["dispersion"] if restart is None: lines.append( f"Eelec, wfn = {calculation_type}('{functional}', return_wfn=True)" ) - lines.append(f"G = gradient('{functional}', ref_wfn=wfn)") + if calculation_type == "energy": + lines.append(f"G = gradient('{functional}', ref_wfn=wfn)") else: if calculation_type == "gradient": lines.append( @@ -294,7 +260,8 @@ def get_input(self, calculation_type="energy", restart=None): lines.append( f"Eelec, wfn = {calculation_type}('{method}', return_wfn=True)" ) - lines.append(f"G = gradient('{method}', ref_wfn=wfn)") + if calculation_type == "energy": + lines.append(f"G = gradient('{method}', ref_wfn=wfn)") else: if calculation_type == "gradient": lines.append( @@ -347,9 +314,9 @@ def get_input(self, calculation_type="energy", restart=None): variables["_method"] = "{method}" variables["_method_string"] = "{method_string}" - +tmp = fix_multipoles(variables) with open("{filename}", "w") as fd: - json.dump(fix_multipoles(variables), fd, sort_keys=True, indent=3) + json.dump(tmp, fd, sort_keys=True, indent=3) """ ) @@ -358,6 +325,54 @@ def get_input(self, calculation_type="energy", restart=None): return "\n".join(lines) + def get_method(self): + """Get the method and functional to use""" + P = self.parameters.current_values_to_dict( + context=seamm.flowchart_variables._data + ) + + if P["level"] == "recommended": + method_string = P["method"] + else: + method_string = P["advanced_method"] + + # Allow the full name, or the short name, or just pray. + if method_string in psi4_step.methods: + method = psi4_step.methods[method_string]["method"] + else: + method = method_string.lower() + for key in psi4_step.methods: + if psi4_step.methods[key]["method"] == method: + break + else: + method = method_string + + if method == "dft": + if P["level"] == "recommended": + functional_string = P["functional"] + else: + functional_string = P["advanced_functional"] + + # Allow the full name, or the short name, or just pray. + if functional_string in psi4_step.dft_functionals: + functional = psi4_step.dft_functionals[functional_string]["name"] + else: + functional = functional_string.lower() + for key in psi4_step.dft_functionals: + if psi4_step.dft_functionals[key]["name"] == functional: + break + else: + functional = functional_string + + if ( + P["dispersion"] != "none" + and len(psi4_step.dft_functionals[functional_string]["dispersion"]) > 1 + ): + functional = functional + "-" + P["dispersion"] + else: + functional = method + return method, functional, method_string + def analyze(self, indent="", data={}, out=[]): """Parse the output and generating the text output and store the data in variables for other stages to access diff --git a/psi4_step/psi4.py b/psi4_step/psi4.py index 4b83dd2..9ee352e 100644 --- a/psi4_step/psi4.py +++ b/psi4_step/psi4.py @@ -38,57 +38,7 @@ def fix_multipoles(data): it = iter(data.items()) for key, value in it: if 'PROP ' == key[0:5]: - result[key[6:]] = value[0] - elif 'SCF DIPOLE' == key: - result[key] = value[0] - elif 'CURRENT DIPOLE' == key: - result[key] = value[0] - elif '32-POLE' in key: - tmp = [] - while True: - value = 0.0 if abs(value) < 1.0e-10 else value - tmp.append(value) - if 'ZZZZZ' in key: - break - key, value = next(it) - result['32-POLE'] = tmp - elif 'HEXADECAPOLE' in key: - tmp = [] - while True: - value = 0.0 if abs(value) < 1.0e-10 else value - tmp.append(value) - if 'ZZZZ' in key: - break - key, value = next(it) - result['HEXADECAPOLE'] = tmp - elif 'OCTUPOLE' in key: - tmp = [] - while True: - value = 0.0 if abs(value) < 1.0e-10 else value - tmp.append(value) - if 'ZZZ' in key: - break - key, value = next(it) - result['OCTUPOLE'] = tmp - elif 'QUADRUPOLE' in key: - tmp = [] - while True: - value = 0.0 if abs(value) < 1.0e-10 else value - tmp.append(value) - if 'ZZ' in key: - break - key, value = next(it) - result['QUADRUPOLE'] = tmp - elif 'DIPOLE' in key: - tmp = [] - while True: - value = 0.0 if abs(value) < 1.0e-10 else value - tmp.append(value) - result[key] = value - if 'Z' in key: - break - key, value = next(it) - result[key[0:-2]] = tmp + result[key[5:]] = value[0] elif 'ESP AT CENTER' in key: esp.append(value) else: @@ -96,6 +46,7 @@ def fix_multipoles(data): if len(esp) > 0: result['ELECTROSTATIC POTENTIAL'] = esp + return result """ @@ -420,8 +371,8 @@ def run(self): text = node.get_input() input_data.append(text) - input_data.append("clean()") - input_data.append("clean_variables()") + # input_data.append("clean()") + # input_data.append("clean_variables()") # input_data.append('clean_options()') node = node.next() diff --git a/psi4_step/thermochemistry.py b/psi4_step/thermochemistry.py index 8782bb1..23c6d90 100644 --- a/psi4_step/thermochemistry.py +++ b/psi4_step/thermochemistry.py @@ -118,7 +118,7 @@ def git_revision(self): """The git version of this module.""" return psi4_step.__git_revision__ - def description_text(self, P=None): + def description_text(self, P=None, configuration=None): """Create the text description of what this step will do. The dictionary of control values is passed in as P so that the code can test values, etc. @@ -136,7 +136,9 @@ def description_text(self, P=None): if not P: P = self.parameters.values_to_dict() - text = super().description_text(P=P, calculation_type="Thermochemistry") + text = super().description_text( + P=P, calculation_type="Thermochemistry", configuration=configuration + ) added = ( "\nThe thermodynamic functions will be calculated at temperature {T} and " @@ -146,7 +148,7 @@ def description_text(self, P=None): return text + "\n" + __(added, **P, indent=4 * " ").__str__() def get_input(self, calculation_type="frequency"): - """Get the input for an optimization calculation for Psi4""" + """Get the input for a frequency calculation for Psi4""" _, configuration = self.get_system_configuration() # Create the directory @@ -165,29 +167,14 @@ def get_input(self, calculation_type="frequency"): PP[key] = "{:~P}".format(PP[key]) self.description = [] - self.description.append(__(self.description_text(PP), **PP, indent=self.indent)) + self.description.append( + __( + self.description_text(PP, configuration=configuration), + **PP, + indent=self.indent, + ) + ) # Figure out what we are doing! The method is HF, B3LYP, CCSD, etc. - if P["level"] == "recommended": - method_string = P["method"] - else: - method_string = P["advanced_method"] - - if method_string in psi4_step.methods: - method = psi4_step.methods[method_string]["method"] - else: - method = method_string - - if method == "dft": - if P["level"] == "recommended": - functional_string = P["functional"] - else: - functional_string = P["advanced_functional"] - method = psi4_step.dft_functionals[functional_string]["name"] - if ( - P["dispersion"] != "none" - and len(psi4_step.dft_functionals[functional_string]["dispersion"]) > 1 - ): - method = method + "-" + P["dispersion"] lines = [] lines.append("") @@ -196,18 +183,21 @@ def get_input(self, calculation_type="frequency"): lines.append("#" * 80) lines.append("") # lines.append("initial.find_point_group(tolerance=1.0e-5)") - lines.append("initial.symmetrize(1.0e-5)") - lines.append("point_group = initial.point_group().symbol()") - lines.append("") + # lines.append("initial.symmetrize(1.0e-5)") + # lines.append("point_group = initial.point_group().symbol()") + # lines.append("") if not P["use existing parameters"]: # Add in the input from the energy part of things lines.append(super().get_input(calculation_type=calculation_type)) else: - lines.append(f"Eelec, wfn = frequency('{method}', return_wfn=True)") + previous = self.previous() + method, functional, _ = previous.get_method() - # Orbital plots - lines.append(self.plot_input()) + if method == "dft": + lines.append(f"Eelec, wfn = frequency('{functional}', return_wfn=True)") + else: + lines.append(f"Eelec, wfn = frequency('{method}', return_wfn=True)") lines.append( f""" diff --git a/psi4_step/tk_thermochemistry.py b/psi4_step/tk_thermochemistry.py index d4532dc..ebf9780 100644 --- a/psi4_step/tk_thermochemistry.py +++ b/psi4_step/tk_thermochemistry.py @@ -82,6 +82,7 @@ def __init__( None """ self.dialog = None + self.first_calculation = False super().__init__( tk_flowchart=tk_flowchart, @@ -133,9 +134,9 @@ def create_dialog(self): # and binding to change as needed for key in ("use existing parameters",): - self[key].combobox.bind("<>", self.reset_thermochemistry) - self[key].combobox.bind("", self.reset_thermochemistry) - self[key].combobox.bind("", self.reset_thermochemistry) + self[key].combobox.bind("<>", self.reset_dialog) + self[key].combobox.bind("", self.reset_dialog) + self[key].combobox.bind("", self.reset_dialog) # Top level needs to call reset_dialog if self.node.calculation == "thermochemistry": @@ -143,6 +144,18 @@ def create_dialog(self): return frame + def edit(self): + """Present a dialog for editing this step's parameters. + Look at the flowchart to see if a previous step was a calculation. + """ + previous = self.node.previous() + self.first_calculation = isinstance(previous, psi4_step.Initialization) + + super().edit() + + if self.first_calculation: + self["use existing parameters"].set("no") + def reset_dialog(self, widget=None, row=0): """Layout the widgets in the dialog. @@ -172,18 +185,16 @@ def reset_dialog(self, widget=None, row=0): for slave in frame.grid_slaves(): slave.grid_forget() - # Shortcut for parameters - P = self.node.parameters - self["thermochemistry"].grid(row=row, column=0) row += 1 self.reset_thermochemistry() - if not P["use existing parameters"]: + if self.first_calculation or self["use existing parameters"].get() != "yes": row = super().reset_dialog(row=row) - else: - self.reset_calculation() + + self.fit_dialog() + return row def reset_thermochemistry(self, widget=None): @@ -194,7 +205,11 @@ def reset_thermochemistry(self, widget=None): widgets = [] row = 0 - for key in ("use existing parameters", "T", "P"): + if not self.first_calculation: + self["use existing parameters"].grid(row=row, column=0, sticky=tk.EW) + widgets.append(self["use existing parameters"]) + row += 1 + for key in ("T", "P"): self[key].grid(row=row, column=0, sticky=tk.EW) widgets.append(self[key]) row += 1