Skip to content

Commit b65c17d

Browse files
authored
Merge pull request #35 from molssi-seamm/dev
Enhancements and bug fixes for thermochemistry
2 parents 248a997 + 3ba7643 commit b65c17d

File tree

5 files changed

+110
-134
lines changed

5 files changed

+110
-134
lines changed

HISTORY.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
=======
22
History
33
=======
4+
2024.10.5 -- Enhancements and bug fixes for thermochemistry
5+
* Improved GUI for thermochemistry so that it automatically recognizes whether it is
6+
after e.g. an optimization and configures appropriately.
7+
* Fixed and issue with transferring the multipole moments to the JSON file
8+
49
2024.7.30 -- Enhanced results and fixed problem with psi4.ini
510
* Added to the results by using cclib to parse the output.
611
* If ~/SEAMM/psi4.ini did not exist the version that was automatically created was

psi4_step/energy.py

Lines changed: 55 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -168,21 +168,7 @@ def get_input(self, calculation_type="energy", restart=None):
168168
lines.append("#" * 80)
169169

170170
# Figure out what we are doing!
171-
if P["level"] == "recommended":
172-
method_string = P["method"]
173-
else:
174-
method_string = P["advanced_method"]
175-
176-
# Allow the full name, or the short name, or just pray.
177-
if method_string in psi4_step.methods:
178-
method = psi4_step.methods[method_string]["method"]
179-
else:
180-
method = method_string.lower()
181-
for key in psi4_step.methods:
182-
if psi4_step.methods[key]["method"] == method:
183-
break
184-
else:
185-
method = method_string
171+
method, functional, method_string = self.get_method()
186172

187173
# lines.append('set scf_type df')
188174
# lines.append('set guess sad')
@@ -251,32 +237,12 @@ def get_input(self, calculation_type="energy", restart=None):
251237

252238
lines.append("")
253239
if method == "dft":
254-
if P["level"] == "recommended":
255-
functional_string = P["functional"]
256-
else:
257-
functional_string = P["advanced_functional"]
258-
259-
# Allow the full name, or the short name, or just pray.
260-
if functional_string in psi4_step.dft_functionals:
261-
functional = psi4_step.dft_functionals[functional_string]["name"]
262-
else:
263-
functional = functional_string.lower()
264-
for key in psi4_step.dft_functionals:
265-
if psi4_step.dft_functionals[key]["name"] == functional:
266-
break
267-
else:
268-
functional = functional_string
269-
270-
if (
271-
P["dispersion"] != "none"
272-
and len(psi4_step.dft_functionals[functional_string]["dispersion"]) > 1
273-
):
274-
functional = functional + "-" + P["dispersion"]
275240
if restart is None:
276241
lines.append(
277242
f"Eelec, wfn = {calculation_type}('{functional}', return_wfn=True)"
278243
)
279-
lines.append(f"G = gradient('{functional}', ref_wfn=wfn)")
244+
if calculation_type == "energy":
245+
lines.append(f"G = gradient('{functional}', ref_wfn=wfn)")
280246
else:
281247
if calculation_type == "gradient":
282248
lines.append(
@@ -294,7 +260,8 @@ def get_input(self, calculation_type="energy", restart=None):
294260
lines.append(
295261
f"Eelec, wfn = {calculation_type}('{method}', return_wfn=True)"
296262
)
297-
lines.append(f"G = gradient('{method}', ref_wfn=wfn)")
263+
if calculation_type == "energy":
264+
lines.append(f"G = gradient('{method}', ref_wfn=wfn)")
298265
else:
299266
if calculation_type == "gradient":
300267
lines.append(
@@ -347,9 +314,9 @@ def get_input(self, calculation_type="energy", restart=None):
347314
variables["_method"] = "{method}"
348315
variables["_method_string"] = "{method_string}"
349316
350-
317+
tmp = fix_multipoles(variables)
351318
with open("{filename}", "w") as fd:
352-
json.dump(fix_multipoles(variables), fd, sort_keys=True, indent=3)
319+
json.dump(tmp, fd, sort_keys=True, indent=3)
353320
"""
354321
)
355322

@@ -358,6 +325,54 @@ def get_input(self, calculation_type="energy", restart=None):
358325

359326
return "\n".join(lines)
360327

328+
def get_method(self):
329+
"""Get the method and functional to use"""
330+
P = self.parameters.current_values_to_dict(
331+
context=seamm.flowchart_variables._data
332+
)
333+
334+
if P["level"] == "recommended":
335+
method_string = P["method"]
336+
else:
337+
method_string = P["advanced_method"]
338+
339+
# Allow the full name, or the short name, or just pray.
340+
if method_string in psi4_step.methods:
341+
method = psi4_step.methods[method_string]["method"]
342+
else:
343+
method = method_string.lower()
344+
for key in psi4_step.methods:
345+
if psi4_step.methods[key]["method"] == method:
346+
break
347+
else:
348+
method = method_string
349+
350+
if method == "dft":
351+
if P["level"] == "recommended":
352+
functional_string = P["functional"]
353+
else:
354+
functional_string = P["advanced_functional"]
355+
356+
# Allow the full name, or the short name, or just pray.
357+
if functional_string in psi4_step.dft_functionals:
358+
functional = psi4_step.dft_functionals[functional_string]["name"]
359+
else:
360+
functional = functional_string.lower()
361+
for key in psi4_step.dft_functionals:
362+
if psi4_step.dft_functionals[key]["name"] == functional:
363+
break
364+
else:
365+
functional = functional_string
366+
367+
if (
368+
P["dispersion"] != "none"
369+
and len(psi4_step.dft_functionals[functional_string]["dispersion"]) > 1
370+
):
371+
functional = functional + "-" + P["dispersion"]
372+
else:
373+
functional = method
374+
return method, functional, method_string
375+
361376
def analyze(self, indent="", data={}, out=[]):
362377
"""Parse the output and generating the text output and store the
363378
data in variables for other stages to access

psi4_step/psi4.py

Lines changed: 4 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -38,64 +38,15 @@ def fix_multipoles(data):
3838
it = iter(data.items())
3939
for key, value in it:
4040
if 'PROP ' == key[0:5]:
41-
result[key[6:]] = value[0]
42-
elif 'SCF DIPOLE' == key:
43-
result[key] = value[0]
44-
elif 'CURRENT DIPOLE' == key:
45-
result[key] = value[0]
46-
elif '32-POLE' in key:
47-
tmp = []
48-
while True:
49-
value = 0.0 if abs(value) < 1.0e-10 else value
50-
tmp.append(value)
51-
if 'ZZZZZ' in key:
52-
break
53-
key, value = next(it)
54-
result['32-POLE'] = tmp
55-
elif 'HEXADECAPOLE' in key:
56-
tmp = []
57-
while True:
58-
value = 0.0 if abs(value) < 1.0e-10 else value
59-
tmp.append(value)
60-
if 'ZZZZ' in key:
61-
break
62-
key, value = next(it)
63-
result['HEXADECAPOLE'] = tmp
64-
elif 'OCTUPOLE' in key:
65-
tmp = []
66-
while True:
67-
value = 0.0 if abs(value) < 1.0e-10 else value
68-
tmp.append(value)
69-
if 'ZZZ' in key:
70-
break
71-
key, value = next(it)
72-
result['OCTUPOLE'] = tmp
73-
elif 'QUADRUPOLE' in key:
74-
tmp = []
75-
while True:
76-
value = 0.0 if abs(value) < 1.0e-10 else value
77-
tmp.append(value)
78-
if 'ZZ' in key:
79-
break
80-
key, value = next(it)
81-
result['QUADRUPOLE'] = tmp
82-
elif 'DIPOLE' in key:
83-
tmp = []
84-
while True:
85-
value = 0.0 if abs(value) < 1.0e-10 else value
86-
tmp.append(value)
87-
result[key] = value
88-
if 'Z' in key:
89-
break
90-
key, value = next(it)
91-
result[key[0:-2]] = tmp
41+
result[key[5:]] = value[0]
9242
elif 'ESP AT CENTER' in key:
9343
esp.append(value)
9444
else:
9545
result[key] = value
9646
9747
if len(esp) > 0:
9848
result['ELECTROSTATIC POTENTIAL'] = esp
49+
9950
return result
10051
"""
10152

@@ -420,8 +371,8 @@ def run(self):
420371
text = node.get_input()
421372
input_data.append(text)
422373

423-
input_data.append("clean()")
424-
input_data.append("clean_variables()")
374+
# input_data.append("clean()")
375+
# input_data.append("clean_variables()")
425376
# input_data.append('clean_options()')
426377

427378
node = node.next()

psi4_step/thermochemistry.py

Lines changed: 21 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ def git_revision(self):
118118
"""The git version of this module."""
119119
return psi4_step.__git_revision__
120120

121-
def description_text(self, P=None):
121+
def description_text(self, P=None, configuration=None):
122122
"""Create the text description of what this step will do.
123123
The dictionary of control values is passed in as P so that
124124
the code can test values, etc.
@@ -136,7 +136,9 @@ def description_text(self, P=None):
136136
if not P:
137137
P = self.parameters.values_to_dict()
138138

139-
text = super().description_text(P=P, calculation_type="Thermochemistry")
139+
text = super().description_text(
140+
P=P, calculation_type="Thermochemistry", configuration=configuration
141+
)
140142

141143
added = (
142144
"\nThe thermodynamic functions will be calculated at temperature {T} and "
@@ -146,7 +148,7 @@ def description_text(self, P=None):
146148
return text + "\n" + __(added, **P, indent=4 * " ").__str__()
147149

148150
def get_input(self, calculation_type="frequency"):
149-
"""Get the input for an optimization calculation for Psi4"""
151+
"""Get the input for a frequency calculation for Psi4"""
150152
_, configuration = self.get_system_configuration()
151153

152154
# Create the directory
@@ -165,29 +167,14 @@ def get_input(self, calculation_type="frequency"):
165167
PP[key] = "{:~P}".format(PP[key])
166168

167169
self.description = []
168-
self.description.append(__(self.description_text(PP), **PP, indent=self.indent))
170+
self.description.append(
171+
__(
172+
self.description_text(PP, configuration=configuration),
173+
**PP,
174+
indent=self.indent,
175+
)
176+
)
169177
# Figure out what we are doing! The method is HF, B3LYP, CCSD, etc.
170-
if P["level"] == "recommended":
171-
method_string = P["method"]
172-
else:
173-
method_string = P["advanced_method"]
174-
175-
if method_string in psi4_step.methods:
176-
method = psi4_step.methods[method_string]["method"]
177-
else:
178-
method = method_string
179-
180-
if method == "dft":
181-
if P["level"] == "recommended":
182-
functional_string = P["functional"]
183-
else:
184-
functional_string = P["advanced_functional"]
185-
method = psi4_step.dft_functionals[functional_string]["name"]
186-
if (
187-
P["dispersion"] != "none"
188-
and len(psi4_step.dft_functionals[functional_string]["dispersion"]) > 1
189-
):
190-
method = method + "-" + P["dispersion"]
191178

192179
lines = []
193180
lines.append("")
@@ -196,18 +183,21 @@ def get_input(self, calculation_type="frequency"):
196183
lines.append("#" * 80)
197184
lines.append("")
198185
# lines.append("initial.find_point_group(tolerance=1.0e-5)")
199-
lines.append("initial.symmetrize(1.0e-5)")
200-
lines.append("point_group = initial.point_group().symbol()")
201-
lines.append("")
186+
# lines.append("initial.symmetrize(1.0e-5)")
187+
# lines.append("point_group = initial.point_group().symbol()")
188+
# lines.append("")
202189

203190
if not P["use existing parameters"]:
204191
# Add in the input from the energy part of things
205192
lines.append(super().get_input(calculation_type=calculation_type))
206193
else:
207-
lines.append(f"Eelec, wfn = frequency('{method}', return_wfn=True)")
194+
previous = self.previous()
195+
method, functional, _ = previous.get_method()
208196

209-
# Orbital plots
210-
lines.append(self.plot_input())
197+
if method == "dft":
198+
lines.append(f"Eelec, wfn = frequency('{functional}', return_wfn=True)")
199+
else:
200+
lines.append(f"Eelec, wfn = frequency('{method}', return_wfn=True)")
211201

212202
lines.append(
213203
f"""

psi4_step/tk_thermochemistry.py

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ def __init__(
8282
None
8383
"""
8484
self.dialog = None
85+
self.first_calculation = False
8586

8687
super().__init__(
8788
tk_flowchart=tk_flowchart,
@@ -133,16 +134,28 @@ def create_dialog(self):
133134

134135
# and binding to change as needed
135136
for key in ("use existing parameters",):
136-
self[key].combobox.bind("<<ComboboxSelected>>", self.reset_thermochemistry)
137-
self[key].combobox.bind("<Return>", self.reset_thermochemistry)
138-
self[key].combobox.bind("<FocusOut>", self.reset_thermochemistry)
137+
self[key].combobox.bind("<<ComboboxSelected>>", self.reset_dialog)
138+
self[key].combobox.bind("<Return>", self.reset_dialog)
139+
self[key].combobox.bind("<FocusOut>", self.reset_dialog)
139140

140141
# Top level needs to call reset_dialog
141142
if self.node.calculation == "thermochemistry":
142143
self.reset_dialog()
143144

144145
return frame
145146

147+
def edit(self):
148+
"""Present a dialog for editing this step's parameters.
149+
Look at the flowchart to see if a previous step was a calculation.
150+
"""
151+
previous = self.node.previous()
152+
self.first_calculation = isinstance(previous, psi4_step.Initialization)
153+
154+
super().edit()
155+
156+
if self.first_calculation:
157+
self["use existing parameters"].set("no")
158+
146159
def reset_dialog(self, widget=None, row=0):
147160
"""Layout the widgets in the dialog.
148161
@@ -172,18 +185,16 @@ def reset_dialog(self, widget=None, row=0):
172185
for slave in frame.grid_slaves():
173186
slave.grid_forget()
174187

175-
# Shortcut for parameters
176-
P = self.node.parameters
177-
178188
self["thermochemistry"].grid(row=row, column=0)
179189
row += 1
180190

181191
self.reset_thermochemistry()
182192

183-
if not P["use existing parameters"]:
193+
if self.first_calculation or self["use existing parameters"].get() != "yes":
184194
row = super().reset_dialog(row=row)
185-
else:
186-
self.reset_calculation()
195+
196+
self.fit_dialog()
197+
187198
return row
188199

189200
def reset_thermochemistry(self, widget=None):
@@ -194,7 +205,11 @@ def reset_thermochemistry(self, widget=None):
194205
widgets = []
195206
row = 0
196207

197-
for key in ("use existing parameters", "T", "P"):
208+
if not self.first_calculation:
209+
self["use existing parameters"].grid(row=row, column=0, sticky=tk.EW)
210+
widgets.append(self["use existing parameters"])
211+
row += 1
212+
for key in ("T", "P"):
198213
self[key].grid(row=row, column=0, sticky=tk.EW)
199214
widgets.append(self[key])
200215
row += 1

0 commit comments

Comments
 (0)