16
16
from pysisyphus .io import fchk as io_fchk
17
17
18
18
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
+
19
112
class Gaussian16 (OverlapCalculator ):
20
113
conf_key = "gaussian16"
21
114
_set_plans = (
22
115
{"key" : "chk" , "condition" : lambda self : self .keep_chk },
23
116
"fchk" ,
24
117
("fchk" , "mwfn_wf" ),
25
118
"dump_635r" ,
119
+ ("log" , "out" ),
26
120
)
27
121
28
122
def __init__ (
@@ -34,6 +128,7 @@ def __init__(
34
128
wavefunction_dump = True ,
35
129
stable = "" ,
36
130
fchk = None ,
131
+ iop9_40 = 3 ,
37
132
** kwargs ,
38
133
):
39
134
super ().__init__ (** kwargs )
@@ -56,6 +151,7 @@ def __init__(
56
151
self .wavefunction_dump = True
57
152
self .stable = stable
58
153
self .fchk = fchk
154
+ self .iop9_40 = iop9_40
59
155
60
156
keywords = {
61
157
kw : option
@@ -135,7 +231,7 @@ def make_exc_str(self):
135
231
nstates = f"nstates={ self .nstates } "
136
232
pair2str = lambda k , v : f"{ k } " + (f"={ v } " if v else "" )
137
233
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 } ) "
139
235
return exc_str
140
236
141
237
def reuse_data (self , path ):
@@ -370,7 +466,9 @@ def get_energy(self, atoms, coords, **prepare_kwargs):
370
466
return results
371
467
372
468
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
374
472
375
473
def get_forces (self , atoms , coords , ** prepare_kwargs ):
376
474
did_stable = False
@@ -438,10 +536,13 @@ def run_calculation(self, atoms, coords, **prepare_kwargs):
438
536
)
439
537
return results
440
538
539
+ def get_stored_wavefunction (self , ** kwargs ):
540
+ return self .load_wavefunction_from_file (self .fchk , ** kwargs )
541
+
441
542
def get_wavefunction (self , atoms , coords , ** prepare_kwargs ):
442
543
with GroundStateContext (self ):
443
544
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 ( )
445
546
return results
446
547
447
548
def get_relaxed_density (self , atoms , coords , root , ** prepare_kwargs ):
0 commit comments