Skip to content

Commit 9c1f76b

Browse files
author
Sean M. Colby
authoredSep 22, 2023
Merge pull request #17 from pnnl/mdopt
Fix XTB single point optimization functionality
2 parents 1db2464 + a11c908 commit 9c1f76b

File tree

2 files changed

+110
-70
lines changed

2 files changed

+110
-70
lines changed
 

‎isicle/md.py

+97-55
Original file line numberDiff line numberDiff line change
@@ -134,23 +134,29 @@ def save_geometry(self, fmt='xyz'):
134134
isicle.io.save(geomfile, self.geom)
135135
self.geom.path = geomfile
136136

137-
def _configure_xtb(self, forcefield='gfn2', optlevel='normal', charge=None, solvation=None):
137+
def _configure_xtb(self, forcefield='gfn2', optlevel='normal', charge=0, solvation=None,
138+
ignore_topology=False, cycles=None, dryrun=False):
138139
"""
139140
Set command line for xtb simulations.
140141
141142
Parameters
142143
----------
143144
forcefield : str
144-
GFN forcefield for the optimization
145-
Default: gff
146-
Supported forcefields: gfn2, gfn1, gff
145+
GFN forcefield for the optimization.
146+
Supported : gfn2, gfn1, gff
147147
optlevel : str
148-
Optimization convergence level
149-
Default : normal
148+
Optimization convergence level.
150149
Supported : crude, sloppy, loose, lax, normal, tight, vtight extreme
151150
charge : int
152151
Charge of molecular system.
153-
Default : 0 (Neutral charge)
152+
solvation : str
153+
Specify solvent for simulation.
154+
ignore_topolgy : bool
155+
Turn off only the initial topology check prior to the conformational search.
156+
cycles : int
157+
Maximum number of optimization cycles.
158+
dryrun : bool
159+
Signal whether to perform a dry run.
154160
155161
"""
156162

@@ -166,55 +172,69 @@ def _configure_xtb(self, forcefield='gfn2', optlevel='normal', charge=None, solv
166172
# Add forcefield
167173
s += '--' + forcefield + ' '
168174

169-
# Add optional charge
175+
# Add charge
170176
if charge is not None:
171-
s += '--chrg ' + charge + ' '
177+
s += '--chrg ' + str(charge) + ' '
178+
179+
# Add dryrun option
180+
if dryrun:
181+
s += '--dryrun '
172182

173183
# Add optional implicit solvation
174184
if solvation is not None:
175185
s += '--alpb ' + solvation + ' '
176186

187+
if ignore_topology:
188+
s += '--noreftopo '
189+
190+
if cycles:
191+
s += '--cycles ' + str(cycles) + ' '
192+
177193
# Add output
178194
s += '&>' + ' '
179195

180196
s += '{}.{}'.format(self.basename, "out")
181197
return s
182198

183-
def _configure_crest(self, ewin=6, optlevel='Normal', forcefield='gfn2',
199+
def _configure_crest(self, forcefield='gfn2', optlevel='Normal', ewin=6,
184200
protonate=False, deprotonate=False, tautomerize=False,
185-
ion=None, charge=None, dryrun=False, processes=1,
186-
solvation=None, ignore_topology=False):
201+
ion=None, charge=None, solvation=None, ignore_topology=False,
202+
cycles=None, dryrun=False, processes=1):
187203
"""
188204
Set command line for crest simulations.
189205
190206
Parameters
191207
----------
192-
ewin : int
193-
Energy window (kcal/mol) for conformer, (de)protomer, or tautomer search.
194-
Default : 6
208+
forcefield : str
209+
GFN forcefield for the optimization
210+
Supported : gfn2, gfn1, gff
195211
optlevel : str
196212
Optimization convergence level
197-
Default : normal
198213
Supported : crude, sloppy, loose, lax, normal, tight, vtight extreme
199-
forcefield : str
200-
GFN forcefield for the optimization
201-
Default: gff
202-
Supported forcefields: gfn2, gfn1, gff
214+
ewin : int
215+
Energy window (kcal/mol) for conformer, (de)protomer, or tautomer search.
203216
protonate : bool
204217
Signal to initiate protomer search. Suggested ewin = 30.
205-
Default : False
206218
deprotonate : bool
207219
Signal to initiate deprotonated conformers. Suggesting ewin = 30.
208-
Default : False
209-
tautomer : bool
220+
tautomerize : bool
210221
Signal to initiate tautomer search.
211-
Default : False
212222
ion : str
213223
Keyword to couple with protonate to ionize molecule with an ion other than a proton.
214224
See :obj:`~isicle.adduct.parse_ion` for list of ion options.
215225
charge : int
216226
Charge of molecular system.
217-
Default : 0 (Neutral charge)
227+
solvation : str
228+
Specify solvent for simulation.
229+
ignore_topolgy : bool
230+
Turn off only the initial topology check prior to the conformational search.
231+
cycles : int
232+
Maximum number of optimization cycles.
233+
dryrun : bool
234+
Signal whether to perform a dry run.
235+
processes : int
236+
Number of parallel processes.
237+
218238
"""
219239

220240
# Start base command
@@ -237,8 +257,9 @@ def _configure_crest(self, ewin=6, optlevel='Normal', forcefield='gfn2',
237257
if ion is not None:
238258
s += '-swel ' + ion + ' '
239259

260+
# Add charge
240261
if charge is not None:
241-
s += '-chrg ' + str(charge) + ' '
262+
s += '--chrg ' + str(charge) + ' '
242263

243264
# Add dryrun option
244265
if dryrun:
@@ -263,6 +284,9 @@ def _configure_crest(self, ewin=6, optlevel='Normal', forcefield='gfn2',
263284
if ignore_topology:
264285
s += '--noreftopo '
265286

287+
if cycles:
288+
s += '--cycles ' + str(cycles) + ' '
289+
266290
# Add output
267291
s += '&>' + ' '
268292

@@ -272,34 +296,39 @@ def _configure_crest(self, ewin=6, optlevel='Normal', forcefield='gfn2',
272296

273297
return s
274298

275-
def configure(self, task='optimize', forcefield='gfn2', charge=None,
276-
ewin=6, ion=None, optlevel='Normal', dryrun=False, processes=1,
277-
solvation=None, ignore_topology=False):
299+
def configure(self, task='optimize', forcefield='gfn2', optlevel='Normal',
300+
ewin=6, charge=None, ion=None, solvation=None,
301+
ignore_topology=False, cycles=None, dryrun=False, processes=1):
278302
"""
279303
Generate command line
280304
281305
Parameters
282306
----------
283-
tasks : str
307+
task : str
284308
Set task to "optimize", "conformer", "protonate", "deprotonate", or "tautomerize".
285-
Default : "optimize"
286309
forcefield : str
287310
GFN forcefield for the optimization
288-
Default: gff
289-
Supported forcefields: gfn2, gfn1, gff
290-
ewin : int
291-
Energy window (kcal/mol) for conformer(set to 6), (de)protomer(set to 30), or tautomer(set to 30) search.
292-
Default : 6
293-
ion : str
294-
Ion for protomer calculation.
311+
Supported : gfn2, gfn1, gff
295312
optlevel : str or list of str
296313
Set optimization level. Supply globally or per task.
314+
ewin : int
315+
Energy window (kcal/mol) for conformer(set to 6), (de)protomer(set to 30), or tautomer(set to 30) search
316+
charge : int
317+
Charge of molecular system.
297318
ion : str
298319
Keyword to couple with protonate to ionize molecule with an ion other than a proton.
299320
See :obj:`~isicle.adduct.parse_ion` for list of ion options.
300-
charge : int
301-
Charge of molecular system.
302-
Default : 0 (Neutral charge)
321+
solvation : str
322+
Specify solvent for simulation.
323+
ignore_topolgy : bool
324+
Turn off only the initial topology check prior to the conformational search.
325+
cycles : int
326+
Maximum number of optimization cycles.
327+
dryrun : bool
328+
Signal whether to perform a dry run.
329+
processes : int
330+
Number of parallel processes.
331+
303332
"""
304333

305334
if type(task) == list:
@@ -311,7 +340,12 @@ def configure(self, task='optimize', forcefield='gfn2', charge=None,
311340

312341
if task == 'optimize':
313342
config = self._configure_xtb(optlevel=optlevel,
314-
forcefield=forcefield)
343+
forcefield=forcefield,
344+
charge=charge,
345+
dryrun=dryrun,
346+
solvation=solvation,
347+
ignore_topology=ignore_topology,
348+
cycles=cycles)
315349

316350
else:
317351
if task == 'conformer':
@@ -338,7 +372,8 @@ def configure(self, task='optimize', forcefield='gfn2', charge=None,
338372
dryrun=dryrun,
339373
processes=processes,
340374
solvation=solvation,
341-
ignore_topology=ignore_topology)
375+
ignore_topology=ignore_topology,
376+
cycles=cycles)
342377
else:
343378
raise Error(
344379
'Task not assigned properly, please choose optimize, conformer, protonate, deprotonate, or tautomerize')
@@ -369,7 +404,6 @@ def finish(self):
369404
self.temp_dir, self.basename + '.out'))
370405

371406
result = parser.parse()
372-
373407
self.__dict__.update(result)
374408

375409
for i in self.geom:
@@ -385,9 +419,9 @@ def finish(self):
385419
else:
386420
self.geom = self.geom[0]
387421

388-
def run(self, geom, task='optimize', forcefield='gfn2', charge=None,
389-
ewin=6, ion=None, optlevel='Normal', dryrun=False, processes=1,
390-
solvation=None, ignore_topology=False):
422+
def run(self, geom, task='optimize', forcefield='gfn2', optlevel='Normal',
423+
ewin=6, charge=None, ion=None, solvation=None, ignore_topology=False,
424+
cycles=None, dryrun=False, processes=1):
391425
"""
392426
Optimize geometry via density functional theory using supplied functional
393427
and basis set.
@@ -396,21 +430,29 @@ def run(self, geom, task='optimize', forcefield='gfn2', charge=None,
396430
----------
397431
geom : :obj:`~isicle.geometry.Geometry`
398432
Molecule representation.
399-
tasks : str
433+
task : str
400434
Set task to "optimize", "conformer", "protonate", "deprotonate", or "tautomerize".
401435
forcefield : str
402436
GFN forcefield for the optimization, including "gfn2", "gfn1", "gff".
403-
ewin : int
404-
Energy window (kcal/mol) for conformer(set to 6), (de)protomer(set to 30), or tautomer(set to 30) search.
405-
ion : str
406-
Ion for protomer calculation.
407437
optlevel : str or list of str
408438
Set optimization level. Supply globally or per task.
439+
ewin : int
440+
Energy window (kcal/mol) for conformer(set to 6), (de)protomer(set to 30), or tautomer(set to 30) search.
441+
charge : int
442+
Charge of molecular system.
409443
ion : str
410444
Keyword to couple with protonate to ionize molecule with an ion other than a proton.
411445
See :obj:`~isicle.adduct.parse_ion` for list of ion options.
412-
charge : int
413-
Charge of molecular system. Defaults to 0 (neutral).
446+
solvation : str
447+
Specify solvent for simulation.
448+
ignore_topolgy : bool
449+
Turn off only the initial topology check prior to the conformational search.
450+
cycles : int
451+
Maximum number of optimization cycles.
452+
dryrun : bool
453+
Signal whether to perform a dry run.
454+
processes : int
455+
Number of parallel processes.
414456
415457
Returns
416458
-------
@@ -428,7 +470,7 @@ def run(self, geom, task='optimize', forcefield='gfn2', charge=None,
428470
# Configure
429471
self.configure(task=task, forcefield=forcefield, charge=charge,
430472
ewin=ewin, ion=ion, optlevel=optlevel, dryrun=dryrun, processes=processes,
431-
solvation=solvation, ignore_topology=ignore_topology)
473+
solvation=solvation, ignore_topology=ignore_topology, cycles=cycles)
432474

433475
# Run QM simulation
434476
self.submit()

‎isicle/parse.py

+13-15
Original file line numberDiff line numberDiff line change
@@ -739,21 +739,21 @@ def parse(self):
739739
# Check that the file is valid first
740740
if len(self.contents) == 0:
741741
raise RuntimeError('No contents to parse: {}'.format(self.path))
742-
if "terminated normally" not in self.contents[-1]:
743-
if "ratio" not in self.contents[-2]:
744-
raise RuntimeError('XTB job failed: {}'.format(self.path))
742+
743+
last_lines = ''.join(self.contents[-10:])
744+
if ("terminat" not in last_lines) \
745+
& ("normal" not in last_lines) \
746+
& ("ratio" not in last_lines):
747+
raise RuntimeError('XTB job failed: {}'.format(self.path))
745748

746749
self.parse_crest = False
747750
self.parse_opt = False
748751
self.parse_isomer = False
749752

750753
# Initialize result object to store info
751754
result = {}
755+
result['protocol'] = self._parse_protocol()
752756

753-
try:
754-
result['protocol'] = self._parse_protocol()
755-
except:
756-
pass
757757
try:
758758
result['timing'] = self._parse_timing()
759759
except:
@@ -767,14 +767,12 @@ def parse(self):
767767
# Parse geometry from assoc. XYZ file
768768
try:
769769
if self.path.endswith('xyz'):
770-
771-
if 'geometry' in to_parse:
772-
try:
773-
self.xyz_path = self.path
774-
result['geom'] = self._parse_xyz()
775-
776-
except:
777-
pass
770+
try:
771+
self.xyz_path = self.path
772+
result['geom'] = self._parse_xyz()
773+
774+
except:
775+
pass
778776

779777
if self.path.endswith('out') or self.path.endswith('log'):
780778

0 commit comments

Comments
 (0)
Failed to load comments.