Skip to content

Commit d950481

Browse files
author
Johannes Steinmetzer
committed
add: CI-coeff parsing from Gaussian log files
there are still some sign issues with in the 1tdm test ...
1 parent 639fc65 commit d950481

File tree

2 files changed

+105
-3
lines changed

2 files changed

+105
-3
lines changed

pysisyphus/calculators/Gaussian16.py

+104-3
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,107 @@
1616
from pysisyphus.io import fchk as io_fchk
1717

1818

19+
NMO_RE = re.compile("NBasis=\s+(?P<nbfs>\d+)\s+NAE=\s+(?P<nalpha>\d+)\s+NBE=\s+(?P<nbeta>\d+)")
20+
RESTRICTED_RE = re.compile("RHF ground state")
21+
TRANS_PATTERN = (
22+
r"(?:\d+(?:A|B){0,1})\s+"
23+
r"(?:\->|\<\-)\s+"
24+
r"(?:\d+(?:A|B){0,1})\s+"
25+
r"(?:[\d\-\.]+)\s+"
26+
)
27+
# Excited State 2: 2.009-?Sym 7.2325 eV 171.43 nm f=0.0000 <S**2>=0.759
28+
EXC_STATE_RE = re.compile(
29+
r"Excited State\s+(?P<root>\d+):\s+"
30+
r"(?P<label>(?:Singlet|[\d.]+)-\?Sym)\s+"
31+
r"(?P<exc_ev>[\d\-\.]+) eV\s+"
32+
r"(?P<exc_nm>[\d\.\-]+) nm\s+"
33+
r"f=(?P<fosc>[\d\.]+)\s+"
34+
r"<S\*\*2>=(?P<S2>[\d\.]+)\s+"
35+
rf"((?:(?!Excited State){TRANS_PATTERN}\s+)+)"
36+
, re.DOTALL
37+
)
38+
39+
40+
def get_nmos(text):
41+
mobj = NMO_RE.search(text)
42+
nbfs = int(mobj.group("nbfs"))
43+
nalpha = int(mobj.group("nalpha"))
44+
nbeta = int(mobj.group("nbeta"))
45+
nocc_a = nalpha
46+
nvirt_a = nbfs - nocc_a
47+
nocc_b = nbeta
48+
nvirt_b = nbfs - nocc_b
49+
restricted = bool(RESTRICTED_RE.search(text))
50+
return nocc_a, nvirt_a, nocc_b, nvirt_b, restricted
51+
52+
53+
def to_ind_and_spin(lbl):
54+
try:
55+
mo_ind = int(lbl)
56+
spin = "A"
57+
except ValueError:
58+
mo_ind, spin = int(lbl[:-1]), lbl[-1]
59+
mo_ind -= 1
60+
return mo_ind, spin
61+
62+
63+
@file_or_str(".log")
64+
def parse_ci_coeffs(text, restricted_same_ab=False):
65+
nocc_a, nvirt_a, nocc_b, nvirt_b, restricted = get_nmos(text)
66+
shape_a = (nocc_a, nvirt_a)
67+
shape_b = (nocc_b, nvirt_b)
68+
69+
exc_states = EXC_STATE_RE.findall(text)
70+
nstates = len(exc_states)
71+
72+
Xa = np.zeros((nstates, *shape_a))
73+
Ya = np.zeros_like(Xa)
74+
Xb = np.zeros((nstates, *shape_b))
75+
Yb = np.zeros_like(Xb)
76+
77+
for state, exc_state in enumerate(exc_states):
78+
root, label, exc_ev, exc_nm, fosc, s2, trans = exc_state
79+
root = int(root)
80+
exc_ev = float(exc_ev)
81+
trans = trans.strip().split()
82+
assert len(trans) % 4 == 0, len(trans)
83+
ntrans = len(trans) // 4
84+
for j in range(ntrans):
85+
trans_slice = slice(j*4, (j+1)*4)
86+
from_, kind, to_, coeff = trans[trans_slice]
87+
coeff = float(coeff)
88+
from_ind, from_spin = to_ind_and_spin(from_)
89+
to_ind, to_spin = to_ind_and_spin(to_)
90+
# alpha-alpha
91+
if from_spin == "A" and to_spin == "A":
92+
# excitation
93+
if kind == "->":
94+
Xa[state, from_ind, to_ind-nocc_a] = coeff
95+
# deexcitation
96+
else:
97+
Ya[state, from_ind, to_ind-nocc_a] = coeff
98+
# beta-beta excitation
99+
elif from_spin == "B" and to_spin == "B":
100+
# excitation
101+
if kind == "->":
102+
Xb[state, from_ind, to_ind-nocc_b] = coeff
103+
# deexcitation
104+
else:
105+
Yb[state, from_ind, to_ind-nocc_b] = coeff
106+
if restricted and restricted_same_ab:
107+
Xb = Xa.copy()
108+
Yb = Ya.copy()
109+
return Xa, Ya, Xb, Yb
110+
111+
19112
class Gaussian16(OverlapCalculator):
20113
conf_key = "gaussian16"
21114
_set_plans = (
22115
{"key": "chk", "condition": lambda self: self.keep_chk},
23116
"fchk",
24117
("fchk", "mwfn_wf"),
25118
"dump_635r",
119+
("log", "out"),
26120
)
27121

28122
def __init__(
@@ -34,6 +128,7 @@ def __init__(
34128
wavefunction_dump=True,
35129
stable="",
36130
fchk=None,
131+
iop9_40=3,
37132
**kwargs,
38133
):
39134
super().__init__(**kwargs)
@@ -56,6 +151,7 @@ def __init__(
56151
self.wavefunction_dump = True
57152
self.stable = stable
58153
self.fchk = fchk
154+
self.iop9_40 = iop9_40
59155

60156
keywords = {
61157
kw: option
@@ -135,7 +231,7 @@ def make_exc_str(self):
135231
nstates = f"nstates={self.nstates}"
136232
pair2str = lambda k, v: f"{k}" + (f"={v}" if v else "")
137233
arg_str = ",".join([pair2str(k, v) for k, v in self.exc_args.items()])
138-
exc_str = f"{self.exc_key}=({root_str},{nstates},{arg_str})"
234+
exc_str = f"{self.exc_key}=({root_str},{nstates},{arg_str}) iop(9/40={self.iop9_40})"
139235
return exc_str
140236

141237
def reuse_data(self, path):
@@ -370,7 +466,9 @@ def get_energy(self, atoms, coords, **prepare_kwargs):
370466
return results
371467

372468
def get_all_energies(self, atoms, coords, **prepare_kwargs):
373-
return self.get_energy(atoms, coords, **prepare_kwargs)
469+
results = self.get_energy(atoms, coords, **prepare_kwargs)
470+
results["td_1tdms"] = parse_ci_coeffs(self.out, restricted_same_ab=True)
471+
return results
374472

375473
def get_forces(self, atoms, coords, **prepare_kwargs):
376474
did_stable = False
@@ -438,10 +536,13 @@ def run_calculation(self, atoms, coords, **prepare_kwargs):
438536
)
439537
return results
440538

539+
def get_stored_wavefunction(self, **kwargs):
540+
return self.load_wavefunction_from_file(self.fchk, **kwargs)
541+
441542
def get_wavefunction(self, atoms, coords, **prepare_kwargs):
442543
with GroundStateContext(self):
443544
results = self.get_energy(atoms, coords, **prepare_kwargs)
444-
results["wavefunction"] = self.load_wavefunction_from_file(self.fchk)
545+
results["wavefunction"] = self.get_stored_wavefunction()
445546
return results
446547

447548
def get_relaxed_density(self, atoms, coords, root, **prepare_kwargs):

tests/test_es_capabilities/test_es_capabilities.py

+1
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ def test_relaxed_density(root, ref_energy, ref_dpm, calc_cls, calc_kwargs, this_
279279
Gaussian16,
280280
{
281281
"route": "hf def2svp td(nstates=3)",
282+
"iop9_40": 4,
282283
},
283284
marks=using("gaussian16"),
284285
),

0 commit comments

Comments
 (0)