1
1
import json
2
2
import os
3
+ import shutil
4
+ import struct
5
+ from monty .io import zopen
3
6
4
7
from fireworks import FiretaskBase , FWAction , explicit_serialize
5
8
from fireworks .utilities .fw_serializers import DATETIME_HANDLER
@@ -50,6 +53,8 @@ class QChemToDb(FiretaskBase):
50
53
"calc_loc" ,
51
54
"input_file" ,
52
55
"output_file" ,
56
+ "parse_grad_file" ,
57
+ "parse_hess_file" ,
53
58
"additional_fields" ,
54
59
"db_file" ,
55
60
"fw_spec_field" ,
@@ -84,13 +89,123 @@ def run_task(self, fw_spec):
84
89
multirun = multirun ,
85
90
)
86
91
92
+ logger .info ("Drone finished!" )
93
+
94
+ # parse the GRAD file, if desired and if it is present
95
+ if self .get ("parse_grad_file" , False ):
96
+ grad_file = None
97
+ if os .path .exists (os .path .join (calc_dir , "GRAD.gz" )):
98
+ grad_file = os .path .join (calc_dir , "GRAD.gz" )
99
+ elif os .path .exists (os .path .join (calc_dir , "GRAD" )):
100
+ grad_file = os .path .join (calc_dir , "GRAD" )
101
+ elif os .path .exists (os .path .join (calc_dir , "scratch/GRAD.gz" )):
102
+ grad_file = os .path .join (calc_dir , "scratch/GRAD.gz" )
103
+ elif os .path .exists (os .path .join (calc_dir , "scratch/GRAD" )):
104
+ grad_file = os .path .join (calc_dir , "scratch/GRAD" )
105
+ elif os .path .exists (os .path .join (calc_dir , "131.0.gz" )):
106
+ grad_file = os .path .join (calc_dir , "131.0.gz" )
107
+ elif os .path .exists (os .path .join (calc_dir , "131.0" )):
108
+ grad_file = os .path .join (calc_dir , "131.0" )
109
+ elif os .path .exists (os .path .join (calc_dir , "scratch/131.0.gz" )):
110
+ grad_file = os .path .join (calc_dir , "scratch/131.0.gz" )
111
+ elif os .path .exists (os .path .join (calc_dir , "scratch/131.0" )):
112
+ grad_file = os .path .join (calc_dir , "scratch/131.0" )
113
+
114
+ if grad_file is None :
115
+ task_doc ["warnings" ]["grad_file_missing" ] = True
116
+ else :
117
+ logger .info ("Parsing gradient file" )
118
+ grad = []
119
+ if grad_file [- 5 :] == "131.0" or grad_file [- 8 :] == "131.0.gz" :
120
+ tmp_grad_data = []
121
+ logger .info ("Gradient file is binary" )
122
+ with zopen (grad_file , mode = "rb" ) as file :
123
+ binary = file .read ()
124
+ for ii in range (int (len (binary ) / 8 )):
125
+ tmp_grad_data .append (
126
+ struct .unpack ("d" , binary [ii * 8 : (ii + 1 ) * 8 ])[0 ]
127
+ )
128
+ grad = []
129
+ for ii in range (int (len (tmp_grad_data ) / 3 )):
130
+ grad .append (
131
+ [
132
+ float (tmp_grad_data [ii * 3 ]),
133
+ float (tmp_grad_data [ii * 3 + 1 ]),
134
+ float (tmp_grad_data [ii * 3 + 2 ]),
135
+ ]
136
+ )
137
+ else :
138
+ logger .info ("Gradient file is text" )
139
+ with zopen (grad_file , mode = "rt" , encoding = "ISO-8859-1" ) as f :
140
+ lines = f .readlines ()
141
+ for line in lines :
142
+ split_line = line .split ()
143
+ if len (split_line ) == 3 :
144
+ grad .append (
145
+ [
146
+ float (split_line [0 ]),
147
+ float (split_line [1 ]),
148
+ float (split_line [2 ]),
149
+ ]
150
+ )
151
+ task_doc ["output" ]["precise_gradients" ] = grad
152
+ logger .info ("Done parsing gradient. Now removing scratch directory" )
153
+ if os .path .exists (os .path .join (calc_dir , "scratch" )):
154
+ shutil .rmtree (os .path .join (calc_dir , "scratch" ))
155
+
156
+ # parse the HESS file(s), if desired and if one or multiple are present
157
+ if self .get ("parse_hess_file" , False ):
158
+ hess_files = []
159
+ for filename in os .listdir (calc_dir ):
160
+ if "HESS" in filename :
161
+ hess_files .append (filename )
162
+ elif filename == "scratch" :
163
+ for subfilename in os .listdir (os .path .join (calc_dir , "scratch" )):
164
+ if "HESS" in subfilename :
165
+ hess_files .append ("scratch/" + subfilename )
166
+ elif subfilename == "132.0" or subfilename == "132.0.gz" :
167
+ hess_files .append ("scratch/" + subfilename )
168
+
169
+ if len (hess_files ) == 0 :
170
+ task_doc ["warnings" ]["hess_file_missing" ] = True
171
+ else :
172
+ logger .info ("Parsing Hessian file" )
173
+ hess_data = {}
174
+ for hess_name in hess_files :
175
+ if hess_name [- 5 :] == "132.0" or hess_name [- 8 :] == "132.0.gz" :
176
+ logger .info ("Hessian file is binary" )
177
+ tmp_hess_data = []
178
+ with zopen (
179
+ os .path .join (calc_dir , hess_name ), mode = "rb"
180
+ ) as file :
181
+ binary = file .read ()
182
+ for ii in range (int (len (binary ) / 8 )):
183
+ tmp_hess_data .append (
184
+ struct .unpack ("d" , binary [ii * 8 : (ii + 1 ) * 8 ])[0 ]
185
+ )
186
+ hess_data [hess_name ] = tmp_hess_data
187
+ else :
188
+ logger .info ("Hessian file is text" )
189
+ with zopen (
190
+ os .path .join (calc_dir , hess_name ),
191
+ mode = "rt" ,
192
+ encoding = "ISO-8859-1" ,
193
+ ) as f :
194
+ hess_data [hess_name ] = f .readlines ()
195
+ task_doc ["output" ]["hess_data" ] = hess_data
196
+ logger .info ("Done parsing Hessian. Now removing scratch directory" )
197
+ if os .path .exists (os .path .join (calc_dir , "scratch" )):
198
+ shutil .rmtree (os .path .join (calc_dir , "scratch" ))
199
+
87
200
# Check for additional keys to set based on the fw_spec
201
+ logger .info ("Updating fw_spec_field" )
88
202
if self .get ("fw_spec_field" ):
89
203
task_doc .update (
90
204
{self .get ("fw_spec_field" ): fw_spec .get (self .get ("fw_spec_field" ))}
91
205
)
92
206
93
207
# Update fw_spec with final/optimized structure
208
+ logger .info ("Updating charges in spec" )
94
209
update_spec = {}
95
210
if task_doc .get ("output" ).get ("optimized_molecule" ):
96
211
update_spec ["prev_calc_molecule" ] = task_doc ["output" ]["optimized_molecule" ]
@@ -101,14 +216,17 @@ def run_task(self, fw_spec):
101
216
update_spec ["prev_calc_esp" ] = task_doc ["output" ]["ESP" ]
102
217
103
218
# get the database connection
219
+ logger .info ("Get database connection" )
104
220
db_file = env_chk (self .get ("db_file" ), fw_spec )
105
221
106
222
# db insertion or taskdoc dump
107
223
if not db_file :
108
224
with open (os .path .join (calc_dir , "task.json" ), "w" ) as f :
109
225
f .write (json .dumps (task_doc , default = DATETIME_HANDLER ))
110
226
else :
227
+ logger .info ("Connecting to QChemCalcDb" )
111
228
mmdb = QChemCalcDb .from_db_file (db_file , admin = True )
229
+ logger .info ("Starting task_doc insert" )
112
230
t_id = mmdb .insert (task_doc )
113
231
logger .info (f"Finished parsing with task_id: { t_id } " )
114
232
@@ -195,12 +313,26 @@ def run_task(self, fw_spec):
195
313
task_doc_clean ["orig" ]["molecule" ]["spin_multiplicity" ] = 1
196
314
task_doc_clean ["output" ]["initial_molecule" ]["charge" ] = 1
197
315
task_doc_clean ["output" ]["initial_molecule" ]["spin_multiplicity" ] = 1
198
- task_doc_clean ["output" ]["initial_molecule" ]["sites" ] = [{'name' : 'H' , 'species' : [{'element' : 'H' , 'occu' : 1 }], 'xyz' : [0.0 , 0.0 , 0.0 ], 'properties' : {}}]
316
+ task_doc_clean ["output" ]["initial_molecule" ]["sites" ] = [
317
+ {
318
+ "name" : "H" ,
319
+ "species" : [{"element" : "H" , "occu" : 1 }],
320
+ "xyz" : [0.0 , 0.0 , 0.0 ],
321
+ "properties" : {},
322
+ }
323
+ ]
199
324
task_doc_clean ["output" ]["mulliken" ] = [+ 1.000000 ]
200
325
task_doc_clean ["output" ]["resp" ] = [+ 1.000000 ]
201
326
task_doc_clean ["output" ]["optimized_molecule" ]["charge" ] = 1
202
327
task_doc_clean ["output" ]["optimized_molecule" ]["spin_multiplicity" ] = 1
203
- task_doc_clean ["output" ]["optimized_molecule" ]["sites" ] = [{'name' : 'H' , 'species' : [{'element' : 'H' , 'occu' : 1 }], 'xyz' : [0.0 , 0.0 , 0.0 ], 'properties' : {}}]
328
+ task_doc_clean ["output" ]["optimized_molecule" ]["sites" ] = [
329
+ {
330
+ "name" : "H" ,
331
+ "species" : [{"element" : "H" , "occu" : 1 }],
332
+ "xyz" : [0.0 , 0.0 , 0.0 ],
333
+ "properties" : {},
334
+ }
335
+ ]
204
336
task_doc_clean ["output" ]["final_energy" ] = (
205
337
task_doc_2 ["output" ]["final_energy" ] - task_doc_1 ["output" ]["final_energy" ]
206
338
)
0 commit comments