Skip to content

Commit

Permalink
Report energy in conformers_before_optimization.txt (#743)
Browse files Browse the repository at this point in the history
The previous `conformers_before_optimization.txt` doesn't include energy
information for the conformer before the optimization, so the updated
one will include this.
  • Loading branch information
alongd authored Sep 9, 2024
2 parents 5a82b3c + e9de000 commit fb16f4a
Show file tree
Hide file tree
Showing 7 changed files with 33 additions and 22 deletions.
4 changes: 1 addition & 3 deletions arc/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -756,9 +756,7 @@ def extremum_list(lst: list,
Returns: Optional[Union[int, None]]
The entry with the minimal/maximal value.
"""
if len(lst) == 0:
return None
elif all([entry is None for entry in lst]):
if lst is None or len(lst) == 0 or all([entry is None for entry in lst]):
return None
elif len(lst) == 1:
return lst[0]
Expand Down
18 changes: 10 additions & 8 deletions arc/plotter.py
Original file line number Diff line number Diff line change
Expand Up @@ -900,6 +900,7 @@ def save_conformers_file(project_directory: str,
ts_methods: Optional[List[str]] = None,
im_freqs: Optional[List[List[float]]] = None,
log_content: bool = False,
before_optimization: bool = True,
):
"""
Save the conformers before or after optimization.
Expand All @@ -918,21 +919,22 @@ def save_conformers_file(project_directory: str,
ts_methods (list, optional): Entries are method names used to generate the TS guess.
im_freqs (list, optional): Entries lists of imaginary frequencies.
log_content (bool): Whether to log the content of the conformers file. ``True`` to log, default is ``False``.
before_optimization (bool): Whether the conformers are before DFT optimization. ``True`` for before, default is ``True``.
"""
spc_dir = 'rxns' if is_ts else 'Species'
geo_dir = os.path.join(project_directory, 'output', spc_dir, label, 'geometry', 'conformers')
if not os.path.exists(geo_dir):
os.makedirs(geo_dir)
if energies is not None and any(e is not None for e in energies):
optimized = True
min_e = extremum_list(energies, return_min=True)
conf_path = os.path.join(geo_dir, 'conformers_after_optimization.txt')
else:
optimized = False
if before_optimization:
conf_path = os.path.join(geo_dir, 'conformers_before_optimization.txt')
else:
conf_path = os.path.join(geo_dir, 'conformers_after_optimization.txt')
min_e = extremum_list(energies, return_min=True)
with open(conf_path, 'w') as f:
content = ''
if optimized:
if before_optimization:
content += f'Conformers for {label}, computed using a force field:\n\n'
else:
level_of_theory = level_of_theory.simple() if isinstance(level_of_theory, Level) else level_of_theory
content += f'Conformers for {label}, optimized at the {level_of_theory} level:\n\n'
for i, xyz in enumerate(xyzs):
Expand All @@ -950,7 +952,7 @@ def save_conformers_file(project_directory: str,
content += f'TS guess method: {ts_methods[i]}\n'
if im_freqs is not None and im_freqs[i] is not None:
content += f'Imaginary frequencies: {im_freqs[i]}\n'
if optimized:
if min_e is not None:
if energies[i] == min_e:
content += 'Relative Energy: 0 kJ/mol (lowest)'
elif energies[i] is not None:
Expand Down
3 changes: 2 additions & 1 deletion arc/plotter_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ def test_save_conformers_file(self):
plotter.save_conformers_file(project_directory=project_directory, label=spc1.label,
xyzs=spc1.conformers, level_of_theory='APFD/def2tzvp',
multiplicity=spc1.multiplicity, charge=spc1.charge, is_ts=False,
energies=spc1.conformer_energies)
energies=spc1.conformer_energies,
before_optimization=True,)
conf_file_path = os.path.join(project_directory, 'output', 'Species', label, 'geometry', 'conformers',
'conformers_before_optimization.txt')
self.assertTrue(os.path.isfile(conf_file_path))
Expand Down
10 changes: 8 additions & 2 deletions arc/scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -1145,6 +1145,7 @@ def run_ts_conformer_jobs(self, label: str):
f'{tsg.method_direction if tsg.method_direction is not None else ""} '
f'{tsg.method_index if tsg.method_index is not None else ""} '
for tsg in self.species_dict[label].ts_guesses],
before_optimization=True,
)
successful_tsgs = [tsg for tsg in self.species_dict[label].ts_guesses if tsg.success]
if len(successful_tsgs) > 1:
Expand Down Expand Up @@ -1852,6 +1853,8 @@ def process_conformers(self, label):
multiplicity=self.species_dict[label].multiplicity,
charge=self.species_dict[label].charge,
is_ts=False,
energies=self.species_dict[label].conformer_energies,
before_optimization=True,
) # before optimization
self.species_dict[label].conformers_before_opt = tuple(self.species_dict[label].conformers)
if self.species_dict[label].initial_xyz is None and self.species_dict[label].final_xyz is None \
Expand Down Expand Up @@ -2028,8 +2031,9 @@ def determine_most_stable_conformer(self, label):
multiplicity=self.species_dict[label].multiplicity,
charge=self.species_dict[label].charge,
is_ts=False,
energies=self.species_dict[label].conformer_energies, # after optimization
)
energies=self.species_dict[label].conformer_energies,
before_optimization=False,
) # after optimization
# Run isomorphism checks if a 2D representation is available
if self.species_dict[label].mol is not None:
for i, xyz in enumerate(xyzs):
Expand Down Expand Up @@ -2244,6 +2248,7 @@ def determine_most_likely_ts_conformer(self, label: str):
for tsg in self.species_dict[label].ts_guesses],
im_freqs=[tsg.imaginary_freqs for tsg in self.species_dict[label].ts_guesses]
if any(tsg.imaginary_freqs is not None for tsg in self.species_dict[label].ts_guesses) else None,
before_optimization=False,
)

def parse_composite_geo(self,
Expand Down Expand Up @@ -2579,6 +2584,7 @@ def check_negative_freq(self,
for tsg in self.species_dict[label].ts_guesses],
im_freqs=[tsg.imaginary_freqs for tsg in self.species_dict[label].ts_guesses]
if any(tsg.imaginary_freqs is not None for tsg in self.species_dict[label].ts_guesses) else None,
before_optimization=False,
)
if not self.testing:
# Update restart dictionary and save the yaml restart file:
Expand Down
12 changes: 7 additions & 5 deletions arc/scheduler_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,16 +184,18 @@ def test_conformers(self):
xyzs=self.sched1.species_dict['C2H6'].conformers,
level_of_theory=Level(method='CBS-QB3'),
multiplicity=1,
charge=0)
charge=0,
before_optimization=True,)
c2h6_conf_path = os.path.join(self.sched1.project_directory, 'output', 'Species', 'C2H6', 'geometry',
'conformers', 'conformers_before_optimization.txt')
self.assertTrue(os.path.isfile(c2h6_conf_path))
with open(c2h6_conf_path, 'r') as f:
lines = f.readlines()
self.assertEqual(lines[0], 'conformer 0:\n')
self.assertEqual(lines[1][0], 'C')
self.assertEqual(lines[9], '\n')
self.assertEqual(lines[10], 'SMILES: CC\n')
self.assertEqual(lines[0], 'Conformers for C2H6, computed using a force field:\n')
self.assertEqual(lines[2], 'conformer 0:\n')
self.assertEqual(lines[3][0], 'C')
self.assertEqual(lines[11], '\n')
self.assertEqual(lines[12], 'SMILES: CC\n')

def test_check_negative_freq(self):
"""Test the check_negative_freq() method"""
Expand Down
2 changes: 1 addition & 1 deletion arc/species/species.py
Original file line number Diff line number Diff line change
Expand Up @@ -1119,7 +1119,7 @@ def generate_conformers(self,
)
if len(lowest_confs):
self.conformers.extend([conf['xyz'] for conf in lowest_confs])
self.conformer_energies.extend([None] * len(lowest_confs))
self.conformer_energies.extend([conf['FF energy'] for conf in lowest_confs])
else:
xyz = self.get_xyz(generate=False)
if xyz is None or not xyz:
Expand Down
6 changes: 4 additions & 2 deletions arc/species/species_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1538,13 +1538,15 @@ def test_append_conformers(self):

# test w/o energies
save_conformers_file(project_directory=project_directory, label='vinoxy', xyzs=xyzs,
level_of_theory=Level(repr='level1'), multiplicity=2, charge=0)
level_of_theory=Level(repr='level1'), multiplicity=2, charge=0,
before_optimization=True,)
self.assertTrue(os.path.isfile(os.path.join(project_directory, 'output', 'Species', 'vinoxy', 'geometry',
'conformers', 'conformers_before_optimization.txt')))

# test with energies
save_conformers_file(project_directory=project_directory, label='vinoxy', xyzs=xyzs,
level_of_theory=Level(repr='level1'), multiplicity=2, charge=0, energies=energies)
level_of_theory=Level(repr='level1'), multiplicity=2, charge=0,
energies=energies, before_optimization=False,)
self.assertTrue(os.path.isfile(os.path.join(project_directory, 'output', 'Species', 'vinoxy', 'geometry',
'conformers', 'conformers_after_optimization.txt')))

Expand Down

0 comments on commit fb16f4a

Please sign in to comment.