From 48d0daeb9267427b1a251924410a54b72aa4e958 Mon Sep 17 00:00:00 2001 From: Alon Grinberg Dana Date: Mon, 19 Jun 2023 15:05:36 +0300 Subject: [PATCH 01/10] Load running jobs' xyz as dict from restart --- arc/main.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arc/main.py b/arc/main.py index 343f5ceaac..6876145b11 100644 --- a/arc/main.py +++ b/arc/main.py @@ -43,6 +43,7 @@ from arc.processor import process_arc_project from arc.reaction import ARCReaction from arc.scheduler import Scheduler +from arc.species.converter import str_to_xyz from arc.species.species import ARCSpecies from arc.utils.scale import determine_scaling_factors @@ -289,6 +290,10 @@ def __init__(self, self.output = output self.standardize_output_paths() # depends on self.project_directory self.running_jobs = running_jobs or dict() + for jobs in self.running_jobs.values(): + for job in jobs: + if 'xyz' in job.keys(): + job['xyz'] = str_to_xyz(job['xyz']) self.lib_long_desc = '' self.unique_species_labels = list() self.rmg_database = rmgdb.make_rmg_database_object() From 5862c7d739df674f29d7e0da2a636ce15fe64070 Mon Sep 17 00:00:00 2001 From: Alon Grinberg Dana Date: Thu, 6 Jul 2023 12:44:50 +0300 Subject: [PATCH 02/10] Return dict xyz if given to str_to_xyz --- arc/species/converter.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arc/species/converter.py b/arc/species/converter.py index ddadd2016d..9b943f2a2a 100644 --- a/arc/species/converter.py +++ b/arc/species/converter.py @@ -85,6 +85,8 @@ def str_to_xyz(xyz_str: str, Returns: dict The ARC xyz format. """ + if isinstance(xyz_str, dict): + return xyz_str if not isinstance(xyz_str, str): raise ConverterError(f'Expected a string input, got {type(xyz_str)}') if project_directory is not None and os.path.isfile(os.path.join(project_directory, xyz_str)): From 84536775b6dd3f63e13a39023fbf12e761dfc521 Mon Sep 17 00:00:00 2001 From: Alon Grinberg Dana Date: Mon, 7 Aug 2023 19:56:44 +0300 Subject: [PATCH 03/10] Remove space from heuristics files --- arc/job/adapters/ts/heuristics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arc/job/adapters/ts/heuristics.py b/arc/job/adapters/ts/heuristics.py index 4599afb5a6..45ae30b9f4 100644 --- a/arc/job/adapters/ts/heuristics.py +++ b/arc/job/adapters/ts/heuristics.py @@ -302,7 +302,7 @@ def execute_incore(self): rxn.ts_species.ts_guesses.append(ts_guess) save_geo(xyz=xyz, path=self.local_path, - filename=f'Heuristics {method_index}', + filename=f'Heuristics_{method_index}', format_='xyz', comment=f'Heuristics {method_index}, family: {family_label}', ) From d2bd6b11a3589f91bbfe879c6fd0b6ac3adad7b9 Mon Sep 17 00:00:00 2001 From: Alon Grinberg Dana Date: Mon, 7 Aug 2023 20:00:08 +0300 Subject: [PATCH 04/10] Added a try-except block to get_rmg_reactions_from_arc_reaction() --- arc/mapping/engine.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/arc/mapping/engine.py b/arc/mapping/engine.py index 3d1d16ab88..45d97ce724 100644 --- a/arc/mapping/engine.py +++ b/arc/mapping/engine.py @@ -197,8 +197,12 @@ def get_rmg_reactions_from_arc_reaction(arc_reaction: 'ARCReaction', ) for rmg_reaction in rmg_reactions: r_map, p_map = map_arc_rmg_species(arc_reaction=arc_reaction, rmg_reaction=rmg_reaction, concatenate=False) - ordered_rmg_reactants = [rmg_reaction.reactants[r_map[i]] for i in range(len(rmg_reaction.reactants))] - ordered_rmg_products = [rmg_reaction.products[p_map[i]] for i in range(len(rmg_reaction.products))] + try: + ordered_rmg_reactants = [rmg_reaction.reactants[r_map[i]] for i in range(len(rmg_reaction.reactants))] + ordered_rmg_products = [rmg_reaction.products[p_map[i]] for i in range(len(rmg_reaction.products))] + except KeyError: + logger.warning(f'Got a problematic RMG rxn from ARC rxn, trying again') + continue mapped_rmg_reactants, mapped_rmg_products = list(), list() for ordered_rmg_mols, arc_species, mapped_mols in zip([ordered_rmg_reactants, ordered_rmg_products], [arc_reaction.r_species, arc_reaction.p_species], From f5e89f110b99a315b7b3caf02245946b68f5183a Mon Sep 17 00:00:00 2001 From: Alon Grinberg Dana Date: Tue, 8 Aug 2023 05:54:41 +0300 Subject: [PATCH 05/10] TS energy gap from wells should be at least 1 kJ/mol --- arc/checks/ts.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arc/checks/ts.py b/arc/checks/ts.py index 6d2ca6cdb9..3658790e66 100644 --- a/arc/checks/ts.py +++ b/arc/checks/ts.py @@ -239,6 +239,9 @@ def check_rxn_e0(reaction: 'ARCReaction', chosen_ts=reaction.ts_species.chosen_ts, energy_type='E0') if r_e0 >= ts_e0 or p_e0 >= ts_e0: reaction.ts_species.ts_checks['E0'] = False + if r_e0 + 1 >= ts_e0 or p_e0 + 1 >= ts_e0: + logger.warning('TS energy gas relative to one fo the wells is lower than 1 kJ/mol, skipping this TS') + reaction.ts_species.ts_checks['E0'] = False else: reaction.ts_species.ts_checks['E0'] = True From b1b7227fadefb97cf3ddd7155ef3897cd185a917 Mon Sep 17 00:00:00 2001 From: Alon Grinberg Dana Date: Mon, 7 Aug 2023 20:35:26 +0300 Subject: [PATCH 06/10] Also pass on KeyError when checking TS NMD --- arc/checks/ts.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arc/checks/ts.py b/arc/checks/ts.py index 3658790e66..070e07a1a4 100644 --- a/arc/checks/ts.py +++ b/arc/checks/ts.py @@ -90,7 +90,11 @@ def check_ts(reaction: 'ARCReaction', check_rxn_e_elect(reaction=reaction, verbose=verbose) if 'freq' in checks or (not reaction.ts_species.ts_checks['normal_mode_displacement'] and job is not None): - check_normal_mode_displacement(reaction, job=job) + try: + check_normal_mode_displacement(reaction, job=job) + except (ValueError, KeyError) as e: + logger.error(f'Could not check normal mode displacement, got: \n{e}') + reaction.ts_species.ts_checks['normal_mode_displacement'] = True if 'rotors' in checks or (ts_passed_all_checks(species=reaction.ts_species, exemptions=['E0', 'warnings', 'IRC']) and job is not None): From 22ccff4d0ae3de45ed1a535bb4e95bd5ca2b9221 Mon Sep 17 00:00:00 2001 From: Alon Grinberg Dana Date: Wed, 4 Oct 2023 09:09:38 +0300 Subject: [PATCH 07/10] Log freq scaling run folder --- arc/utils/scale.py | 1 + 1 file changed, 1 insertion(+) diff --git a/arc/utils/scale.py b/arc/utils/scale.py index d116774f8b..435e19c899 100644 --- a/arc/utils/scale.py +++ b/arc/utils/scale.py @@ -87,6 +87,7 @@ def determine_scaling_factors(levels: List[Union[Level, dict, str]], renamed_level = rename_level(str(level)) project = 'scaling_' + renamed_level project_directory = os.path.join(ARC_PATH, 'Projects', 'scaling_factors', project) + logger.info(f'\nRunning in: {project_directory}\n') if os.path.isdir(project_directory): shutil.rmtree(project_directory) From cadf907d701d7b8fd6adaba4b9c57e4bc26195a6 Mon Sep 17 00:00:00 2001 From: Alon Grinberg Dana Date: Sun, 26 Nov 2023 07:39:26 +0200 Subject: [PATCH 08/10] Refactored `ts_passed_all_checks()` into `ts_passed_checks()` --- arc/checks/ts.py | 10 +++++----- arc/checks/ts_test.py | 6 +++--- arc/statmech/arkane.py | 10 +++++----- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/arc/checks/ts.py b/arc/checks/ts.py index 070e07a1a4..e464b8c30b 100644 --- a/arc/checks/ts.py +++ b/arc/checks/ts.py @@ -96,16 +96,16 @@ def check_ts(reaction: 'ARCReaction', logger.error(f'Could not check normal mode displacement, got: \n{e}') reaction.ts_species.ts_checks['normal_mode_displacement'] = True - if 'rotors' in checks or (ts_passed_all_checks(species=reaction.ts_species, exemptions=['E0', 'warnings', 'IRC']) + if 'rotors' in checks or (ts_passed_checks(species=reaction.ts_species, exemptions=['E0', 'warnings', 'IRC']) and job is not None): invalidate_rotors_with_both_pivots_in_a_reactive_zone(reaction, job, rxn_zone_atom_indices=rxn_zone_atom_indices) -def ts_passed_all_checks(species: 'ARCSpecies', - exemptions: Optional[List[str]] = None, - verbose: bool = False, - ) -> bool: +def ts_passed_checks(species: 'ARCSpecies', + exemptions: Optional[List[str]] = None, + verbose: bool = False, + ) -> bool: """ Check whether the TS species passes all checks other than ones specified in ``exemptions``. diff --git a/arc/checks/ts_test.py b/arc/checks/ts_test.py index 20c548450b..5f98e13452 100644 --- a/arc/checks/ts_test.py +++ b/arc/checks/ts_test.py @@ -257,7 +257,7 @@ def test_did_ts_pass_all_checks(self): """Test the did_ts_pass_all_checks() function.""" spc = ARCSpecies(label='TS', is_ts=True) spc.populate_ts_checks() - self.assertFalse(ts.ts_passed_all_checks(spc)) + self.assertFalse(ts.ts_passed_checks(spc)) self.ts_checks = {'E0': False, 'e_elect': False, @@ -268,8 +268,8 @@ def test_did_ts_pass_all_checks(self): } for key in ['E0', 'e_elect', 'IRC', 'freq']: spc.ts_checks[key] = True - self.assertFalse(ts.ts_passed_all_checks(spc)) - self.assertTrue(ts.ts_passed_all_checks(spc, exemptions=['normal_mode_displacement', 'warnings'])) + self.assertFalse(ts.ts_passed_checks(spc)) + self.assertTrue(ts.ts_passed_checks(spc, exemptions=['normal_mode_displacement', 'warnings'])) spc.ts_checks['e_elect'] = False def test_check_rxn_e_elect(self): diff --git a/arc/statmech/arkane.py b/arc/statmech/arkane.py index 26c1e2aa1a..eabb2c7cca 100644 --- a/arc/statmech/arkane.py +++ b/arc/statmech/arkane.py @@ -25,7 +25,7 @@ from arkane.ess import ess_factory import arc.plotter as plotter -from arc.checks.ts import check_ts, ts_passed_all_checks +from arc.checks.ts import check_ts, ts_passed_checks from arc.common import ARC_PATH, get_logger, read_yaml_file from arc.exceptions import InputError, RotorError from arc.imports import input_files @@ -217,10 +217,10 @@ def compute_high_p_rate_coefficient(self, checks=['energy', 'freq'], rxn_zone_atom_indices=ts_species.rxn_zone_atom_indices, ) - if require_ts_convergence and not ts_passed_all_checks(species=self.reaction.ts_species, - exemptions=['warnings', 'IRC', 'E0'], - verbose=True, - ): + if require_ts_convergence and not ts_passed_checks(species=self.reaction.ts_species, + exemptions=['warnings', 'IRC', 'E0'], + verbose=True, + ): logger.error(f'TS {self.reaction.ts_species.label} did not pass all checks, ' f'not computing rate coefficient.') return None From bb7f3082eec5d71404c62470a1ed407e55a26b39 Mon Sep 17 00:00:00 2001 From: Alon Grinberg Dana Date: Sun, 26 Nov 2023 07:45:18 +0200 Subject: [PATCH 09/10] Refactored `normal_mode_displacement` into `NMD` in the ts_checks dict --- arc/checks/ts.py | 14 ++--- arc/checks/ts_test.py | 56 +++++++++---------- arc/scheduler.py | 2 +- arc/scheduler_test.py | 8 +-- arc/species/species.py | 2 +- arc/species/species_test.py | 3 +- .../restart/2_restart_rate/restart.yml | 2 +- arc/testing/restart/5_TS1/restart.yml | 2 +- 8 files changed, 44 insertions(+), 45 deletions(-) diff --git a/arc/checks/ts.py b/arc/checks/ts.py index e464b8c30b..940cab8334 100644 --- a/arc/checks/ts.py +++ b/arc/checks/ts.py @@ -89,12 +89,12 @@ def check_ts(reaction: 'ARCReaction', if reaction.ts_species.ts_checks['E0'] is None and not reaction.ts_species.ts_checks['e_elect']: check_rxn_e_elect(reaction=reaction, verbose=verbose) - if 'freq' in checks or (not reaction.ts_species.ts_checks['normal_mode_displacement'] and job is not None): + if 'freq' in checks or (not reaction.ts_species.ts_checks['NMD'] and job is not None): try: check_normal_mode_displacement(reaction, job=job) except (ValueError, KeyError) as e: logger.error(f'Could not check normal mode displacement, got: \n{e}') - reaction.ts_species.ts_checks['normal_mode_displacement'] = True + reaction.ts_species.ts_checks['NMD'] = True if 'rotors' in checks or (ts_passed_checks(species=reaction.ts_species, exemptions=['E0', 'warnings', 'IRC']) and job is not None): @@ -301,7 +301,7 @@ def check_normal_mode_displacement(reaction: 'ARCReaction', rmgdb.determine_family(reaction) amplitudes = amplitudes or [0.1, 0.2, 0.4, 0.6, 0.8, 1] amplitudes = [amplitudes] if isinstance(amplitudes, float) else amplitudes - reaction.ts_species.ts_checks['normal_mode_displacement'] = False + reaction.ts_species.ts_checks['NMD'] = False rmg_reactions = get_rmg_reactions_from_arc_reaction(arc_reaction=reaction) or list() freqs, normal_modes_disp = parser.parse_normal_mode_displacement(path=job.local_path_to_output_file, raise_error=False) if not len(normal_modes_disp): @@ -342,13 +342,13 @@ def check_normal_mode_displacement(reaction: 'ARCReaction', and not any(entry is None for entry in breaking) and not any(entry is None for entry in forming) \ and all(entry == breaking[0] for entry in breaking) and all(entry == forming[0] for entry in forming) \ and breaking[0] != forming[0]: - reaction.ts_species.ts_checks['normal_mode_displacement'] = True + reaction.ts_species.ts_checks['NMD'] = True done = True break - if not got_expected_changing_bonds and not reaction.ts_species.ts_checks['normal_mode_displacement']: + if not got_expected_changing_bonds and not reaction.ts_species.ts_checks['NMD']: reaction.ts_species.ts_checks['warnings'] += 'Could not compare normal mode displacement to expected ' \ 'breaking/forming bonds due to a missing RMG template; ' - reaction.ts_species.ts_checks['normal_mode_displacement'] = True + reaction.ts_species.ts_checks['NMD'] = True break if not len(rmg_reactions): # Just check that some bonds break/form, and that this is not a torsional saddle point. @@ -358,7 +358,7 @@ def check_normal_mode_displacement(reaction: 'ARCReaction', reaction.ts_species.ts_checks['warnings'] += warning + '; ' if any(bond not in dmat_bonds_2 for bond in dmat_bonds_1) \ or any(bond not in dmat_bonds_1 for bond in dmat_bonds_2): - reaction.ts_species.ts_checks['normal_mode_displacement'] = True + reaction.ts_species.ts_checks['NMD'] = True break if done: break diff --git a/arc/checks/ts_test.py b/arc/checks/ts_test.py index 5f98e13452..ac3bc805a4 100644 --- a/arc/checks/ts_test.py +++ b/arc/checks/ts_test.py @@ -237,21 +237,21 @@ def test_check_ts(self): """Test the check_ts() function.""" self.job1.local_path_to_output_file = os.path.join(ts.ARC_PATH, 'arc', 'testing', 'freq', 'TS_C3_intraH_8.out') self.rxn_2a.ts_species.populate_ts_checks() - self.assertFalse(self.rxn_2a.ts_species.ts_checks['normal_mode_displacement']) + self.assertFalse(self.rxn_2a.ts_species.ts_checks['NMD']) ts.check_ts(reaction=self.rxn_2a, job=self.job1) - self.assertTrue(self.rxn_2a.ts_species.ts_checks['normal_mode_displacement']) + self.assertTrue(self.rxn_2a.ts_species.ts_checks['NMD']) self.job1.local_path_to_output_file = os.path.join(ts.ARC_PATH, 'arc', 'testing', 'composite', 'keto_enol_ts.out') self.rxn_7.ts_species.populate_ts_checks() - self.assertFalse(self.rxn_7.ts_species.ts_checks['normal_mode_displacement']) + self.assertFalse(self.rxn_7.ts_species.ts_checks['NMD']) ts.check_ts(reaction=self.rxn_7, job=self.job1) - self.assertTrue(self.rxn_7.ts_species.ts_checks['normal_mode_displacement']) + self.assertTrue(self.rxn_7.ts_species.ts_checks['NMD']) self.job1.local_path_to_output_file = os.path.join(ts.ARC_PATH, 'arc', 'testing', 'freq', 'TS_nC3H7-iC3H7.out') self.rxn_8.ts_species.populate_ts_checks() - self.assertFalse(self.rxn_8.ts_species.ts_checks['normal_mode_displacement']) + self.assertFalse(self.rxn_8.ts_species.ts_checks['NMD']) ts.check_ts(reaction=self.rxn_8, job=self.job1) - self.assertTrue(self.rxn_8.ts_species.ts_checks['normal_mode_displacement']) + self.assertTrue(self.rxn_8.ts_species.ts_checks['NMD']) def test_did_ts_pass_all_checks(self): """Test the did_ts_pass_all_checks() function.""" @@ -263,13 +263,13 @@ def test_did_ts_pass_all_checks(self): 'e_elect': False, 'IRC': False, 'freq': False, - 'normal_mode_displacement': False, + 'NMD': False, 'warnings': '', } for key in ['E0', 'e_elect', 'IRC', 'freq']: spc.ts_checks[key] = True self.assertFalse(ts.ts_passed_checks(spc)) - self.assertTrue(ts.ts_passed_checks(spc, exemptions=['normal_mode_displacement', 'warnings'])) + self.assertTrue(ts.ts_passed_checks(spc, exemptions=['NMD', 'warnings'])) spc.ts_checks['e_elect'] = False def test_check_rxn_e_elect(self): @@ -377,89 +377,89 @@ def test_check_rxn_e0(self): def test_check_normal_mode_displacement(self): """Test the check_normal_mode_displacement() function.""" self.rxn_2a.ts_species.populate_ts_checks() - self.assertFalse(self.rxn_2a.ts_species.ts_checks['normal_mode_displacement']) + self.assertFalse(self.rxn_2a.ts_species.ts_checks['NMD']) self.job1.local_path_to_output_file = os.path.join(ts.ARC_PATH, 'arc', 'testing', 'composite', 'TS_intra_H_migration_CBS-QB3.out') self.rxn_2a.determine_family(rmg_database=self.rmgdb) ts.check_normal_mode_displacement(reaction=self.rxn_2a, job=self.job1) - self.assertTrue(self.rxn_2a.ts_species.ts_checks['normal_mode_displacement']) + self.assertTrue(self.rxn_2a.ts_species.ts_checks['NMD']) self.rxn_2a.ts_species.populate_ts_checks() self.rxn_2b.ts_species.populate_ts_checks() - self.assertFalse(self.rxn_2b.ts_species.ts_checks['normal_mode_displacement']) + self.assertFalse(self.rxn_2b.ts_species.ts_checks['NMD']) ts.check_normal_mode_displacement(reaction=self.rxn_2b, job=self.job1) - self.assertFalse(self.rxn_2b.ts_species.ts_checks['normal_mode_displacement']) + self.assertFalse(self.rxn_2b.ts_species.ts_checks['NMD']) # Wrong TS for intra H migration [CH2]CC <=> C[CH]C self.job1.local_path_to_output_file = os.path.join(ts.ARC_PATH, 'arc', 'testing', 'composite', 'TS_C3_intraH_1.out') # A wrong TS. self.rxn_2a.ts_species.populate_ts_checks() ts.check_normal_mode_displacement(reaction=self.rxn_2a, job=self.job1) - self.assertFalse(self.rxn_2a.ts_species.ts_checks['normal_mode_displacement']) + self.assertFalse(self.rxn_2a.ts_species.ts_checks['NMD']) self.job1.local_path_to_output_file = os.path.join(ts.ARC_PATH, 'arc', 'testing', 'composite', 'TS_C3_intraH_2.out') # A wrong TS. self.rxn_2a.ts_species.populate_ts_checks() ts.check_normal_mode_displacement(reaction=self.rxn_2a, job=self.job1) - self.assertFalse(self.rxn_2a.ts_species.ts_checks['normal_mode_displacement']) + self.assertFalse(self.rxn_2a.ts_species.ts_checks['NMD']) self.job1.local_path_to_output_file = os.path.join(ts.ARC_PATH, 'arc', 'testing', 'composite', 'TS_C3_intraH_3.out') # ** The correct TS. ** self.rxn_2a.ts_species.populate_ts_checks() ts.check_normal_mode_displacement(reaction=self.rxn_2a, job=self.job1) - self.assertTrue(self.rxn_2a.ts_species.ts_checks['normal_mode_displacement']) + self.assertTrue(self.rxn_2a.ts_species.ts_checks['NMD']) self.job1.local_path_to_output_file = os.path.join(ts.ARC_PATH, 'arc', 'testing', 'composite', 'TS_C3_intraH_4.out') # A wrong TS. self.rxn_2a.ts_species.populate_ts_checks() ts.check_normal_mode_displacement(reaction=self.rxn_2a, job=self.job1) - self.assertFalse(self.rxn_2a.ts_species.ts_checks['normal_mode_displacement']) + self.assertFalse(self.rxn_2a.ts_species.ts_checks['NMD']) self.job1.local_path_to_output_file = os.path.join(ts.ARC_PATH, 'arc', 'testing', 'composite', 'TS_C3_intraH_5.out') # A wrong TS. self.rxn_2a.ts_species.populate_ts_checks() ts.check_normal_mode_displacement(reaction=self.rxn_2a, job=self.job1) - self.assertFalse(self.rxn_2a.ts_species.ts_checks['normal_mode_displacement']) + self.assertFalse(self.rxn_2a.ts_species.ts_checks['NMD']) self.job1.local_path_to_output_file = os.path.join(ts.ARC_PATH, 'arc', 'testing', 'composite', 'TS_C3_intraH_6.out') # A wrong TS. self.rxn_2a.ts_species.populate_ts_checks() ts.check_normal_mode_displacement(reaction=self.rxn_2a, job=self.job1) - self.assertFalse(self.rxn_2a.ts_species.ts_checks['normal_mode_displacement']) + self.assertFalse(self.rxn_2a.ts_species.ts_checks['NMD']) self.job1.local_path_to_output_file = os.path.join(ts.ARC_PATH, 'arc', 'testing', 'composite', 'TS_C3_intraH_7.out') # A wrong TS. self.rxn_2a.ts_species.populate_ts_checks() ts.check_normal_mode_displacement(reaction=self.rxn_2a, job=self.job1) - self.assertFalse(self.rxn_2a.ts_species.ts_checks['normal_mode_displacement']) + self.assertFalse(self.rxn_2a.ts_species.ts_checks['NMD']) self.job1.local_path_to_output_file = os.path.join(ts.ARC_PATH, 'arc', 'testing', 'freq', 'TS_C3_intraH_8.out') # Correct TS (freq run, not composite). self.rxn_2a.ts_species.populate_ts_checks() - self.assertFalse(self.rxn_2a.ts_species.ts_checks['normal_mode_displacement']) + self.assertFalse(self.rxn_2a.ts_species.ts_checks['NMD']) ts.check_normal_mode_displacement(reaction=self.rxn_2a, job=self.job1) - self.assertTrue(self.rxn_2a.ts_species.ts_checks['normal_mode_displacement']) + self.assertTrue(self.rxn_2a.ts_species.ts_checks['NMD']) # CCO[O] + CC <=> CCOO + [CH2]C, incorrect TS: self.job1.local_path_to_output_file = os.path.join(ts.ARC_PATH, 'arc', 'testing', 'composite', 'TS0_composite_2043.out') self.rxn_4.ts_species.populate_ts_checks() ts.check_normal_mode_displacement(reaction=self.rxn_4, job=self.job1) - self.assertFalse(self.rxn_4.ts_species.ts_checks['normal_mode_displacement']) + self.assertFalse(self.rxn_4.ts_species.ts_checks['NMD']) # CCO[O] + CC <=> CCOO + [CH2]C, correct TS: self.job1.local_path_to_output_file = os.path.join(ts.ARC_PATH, 'arc', 'testing', 'composite', 'TS0_composite_2102.out') self.rxn_4.ts_species.populate_ts_checks() ts.check_normal_mode_displacement(reaction=self.rxn_4, job=self.job1) - self.assertTrue(self.rxn_4.ts_species.ts_checks['normal_mode_displacement']) + self.assertTrue(self.rxn_4.ts_species.ts_checks['NMD']) # NCC + H <=> CH3CHNH2 + H2, correct TS: self.job1.local_path_to_output_file = os.path.join(ts.ARC_PATH, 'arc', 'testing', 'composite', 'TS0_composite_2044.out') self.rxn_5.ts_species.populate_ts_checks() ts.check_normal_mode_displacement(reaction=self.rxn_5, job=self.job1) - self.assertTrue(self.rxn_5.ts_species.ts_checks['normal_mode_displacement']) + self.assertTrue(self.rxn_5.ts_species.ts_checks['NMD']) # NH2 + N2H3 <=> NH + N2H4: self.job1.local_path_to_output_file = os.path.join(ts.ARC_PATH, 'arc', 'testing', 'freq', 'TS_NH2+N2H3.out') @@ -490,7 +490,7 @@ def test_check_normal_mode_displacement(self): rxn_6.ts_species.mol_from_xyz() rxn_6.ts_species.populate_ts_checks() ts.check_normal_mode_displacement(reaction=rxn_6, job=self.job1) - self.assertTrue(rxn_6.ts_species.ts_checks['normal_mode_displacement']) + self.assertTrue(rxn_6.ts_species.ts_checks['NMD']) # [CH2]CC=C <=> CCC=[CH] butylene intra H migration: self.job1.local_path_to_output_file = os.path.join(ts.ARC_PATH, 'arc', 'testing', 'composite', @@ -526,7 +526,7 @@ def test_check_normal_mode_displacement(self): rxn_7.ts_species.mol_from_xyz() rxn_7.ts_species.populate_ts_checks() ts.check_normal_mode_displacement(reaction=rxn_7, job=self.job1) - self.assertTrue(rxn_7.ts_species.ts_checks['normal_mode_displacement']) + self.assertTrue(rxn_7.ts_species.ts_checks['NMD']) @work_in_progress def test_check_normal_mode_displacement_wip(self): @@ -534,9 +534,9 @@ def test_check_normal_mode_displacement_wip(self): self.job1.local_path_to_output_file = os.path.join(ts.ARC_PATH, 'arc', 'testing', 'freq', 'TS_NH3+H=NH2+H2.out') # NH3 + H <=> NH2 + H2 self.rxn_3.ts_species.populate_ts_checks() - self.assertFalse(self.rxn_3.ts_species.ts_checks['normal_mode_displacement']) + self.assertFalse(self.rxn_3.ts_species.ts_checks['NMD']) ts.check_normal_mode_displacement(reaction=self.rxn_3, job=self.job1) - self.assertTrue(self.rxn_3.ts_species.ts_checks['normal_mode_displacement']) + self.assertTrue(self.rxn_3.ts_species.ts_checks['NMD']) def test_invalidate_rotors_with_both_pivots_in_a_reactive_zone(self): """Test the invalidate_rotors_with_both_pivots_in_a_reactive_zone() function.""" diff --git a/arc/scheduler.py b/arc/scheduler.py index 7529d3405b..fcd6c0f15d 100644 --- a/arc/scheduler.py +++ b/arc/scheduler.py @@ -2328,7 +2328,7 @@ def check_freq_job(self, job=job, checks=['freq'], ) - if self.species_dict[label].ts_checks['normal_mode_displacement'] is False: + if self.species_dict[label].ts_checks['NMD'] is False: logger.info(f'TS {label} did not pass the normal mode displacement check. ' f'Status is:\n{self.species_dict[label].ts_checks}\n' f'Searching for a better TS conformer...') diff --git a/arc/scheduler_test.py b/arc/scheduler_test.py index 354116ff19..be6e7a670a 100644 --- a/arc/scheduler_test.py +++ b/arc/scheduler_test.py @@ -598,7 +598,7 @@ def test_check_rxn_e0_by_spc(self): 'unsuccessful_methods': [], 'chosen_ts_method': 'gcn', 'chosen_ts': 4, 'rxn_zone_atom_indices': None, 'chosen_ts_list': [18, 23, 15, 22, 7, 5, 4], 'ts_checks': {'E0': None, 'e_elect': True, 'IRC': None, 'freq': True, - 'normal_mode_displacement': None, 'warnings': ''}, + 'NMD': None, 'warnings': ''}, 'e_elect': -310902.61556421133, 'tsg_spawned': True, 'opt_level': 'b3lyp/6-31g(d,p)', 'bond_corrections': {}, 'mol': {'atoms': [ {'element': {'number': 6, 'isotope': -1}, 'radical_electrons': 0, 'charge': 0, 'label': '', @@ -681,7 +681,7 @@ def test_check_rxn_e0_by_spc(self): restart_dict={'output': output}, ) self.assertEqual(rxn.ts_species.ts_checks, - {'E0': None, 'e_elect': True, 'IRC': None, 'freq': True, 'normal_mode_displacement': None, 'warnings': ''}) + {'E0': None, 'e_elect': True, 'IRC': None, 'freq': True, 'NMD': None, 'warnings': ''}) job_1 = job_factory(job_adapter='gaussian', species=[ARCSpecies(label='SPC', smiles='C')], @@ -695,11 +695,11 @@ def test_check_rxn_e0_by_spc(self): job_1.local_path_to_output_file = os.path.join(ARC_PATH, 'arc', 'testing', 'freq', 'TS_nC3H7-iC3H7.out') check_ts(reaction=rxn, verbose=True, job=job_1, checks=['freq']) self.assertEqual(rxn.ts_species.ts_checks, - {'E0': None, 'e_elect': True, 'IRC': None, 'freq': True, 'normal_mode_displacement': True, 'warnings': ''}) + {'E0': None, 'e_elect': True, 'IRC': None, 'freq': True, 'NMD': True, 'warnings': ''}) sched.check_rxn_e0_by_spc('TS0') self.assertEqual(rxn.ts_species.ts_checks, - {'E0': True, 'e_elect': True, 'IRC': None, 'freq': True, 'normal_mode_displacement': True, 'warnings': ''}) + {'E0': True, 'e_elect': True, 'IRC': None, 'freq': True, 'NMD': True, 'warnings': ''}) def test_save_e_elect(self): """Test the save_e_elect() method.""" diff --git a/arc/species/species.py b/arc/species/species.py index d7094e2e0a..d45f1c2930 100644 --- a/arc/species/species.py +++ b/arc/species/species.py @@ -1984,7 +1984,7 @@ def _scissors(self, def populate_ts_checks(self): """Populate (or restart) the .ts_checks attribute with default (``None``) values.""" if self.is_ts: - keys = ['E0', 'e_elect', 'IRC', 'freq', 'normal_mode_displacement'] + keys = ['E0', 'e_elect', 'IRC', 'freq', 'NMD'] self.ts_checks = {key: None for key in keys} self.ts_checks['warnings'] = '' diff --git a/arc/species/species_test.py b/arc/species/species_test.py index b2816a5944..508cc3f094 100644 --- a/arc/species/species_test.py +++ b/arc/species/species_test.py @@ -869,8 +869,7 @@ def test_from_dict(self): 'successful_methods': ['autotst', 'autotst', 'autotst', 'autotst', 'gcn', 'gcn', 'gcn', 'gcn', 'gcn', 'gcn', 'gcn', 'gcn', 'gcn', 'gcn', 'kinbot', 'kinbot'], - 'ts_checks': {'E0': False, 'IRC': False, 'e_elect': True, 'freq': True, - 'normal_mode_displacement': True, 'warnings': ''}, + 'ts_checks': {'E0': False, 'IRC': False, 'e_elect': True, 'freq': True, 'NMD': True, 'warnings': ''}, 'ts_conf_spawned': True, 'ts_guesses': [{'conformer_index': 0, 'energy': 455.24421061505564, 'execution_time': '0:00:25.483065', diff --git a/arc/testing/restart/2_restart_rate/restart.yml b/arc/testing/restart/2_restart_rate/restart.yml index e8301804f6..b80e763cad 100644 --- a/arc/testing/restart/2_restart_rate/restart.yml +++ b/arc/testing/restart/2_restart_rate/restart.yml @@ -837,7 +837,7 @@ species: e_elect: true IRC: true freq: true - normal_mode_displacement: true + NMD: true warnings: '' specific_job_type: '' diff --git a/arc/testing/restart/5_TS1/restart.yml b/arc/testing/restart/5_TS1/restart.yml index 5d1a9441eb..7478bf7e8f 100644 --- a/arc/testing/restart/5_TS1/restart.yml +++ b/arc/testing/restart/5_TS1/restart.yml @@ -385,7 +385,7 @@ species: IRC: null e_elect: null freq: true - normal_mode_displacement: true + NMD: true warnings: 'Could not determine TS e_elect relative to the wells; ' ts_conf_spawned: true ts_guesses: From e7aea636920b9a18ada5deb258006eb1b8510656 Mon Sep 17 00:00:00 2001 From: Alon Grinberg Dana Date: Sun, 26 Nov 2023 07:58:45 +0200 Subject: [PATCH 10/10] Allow users to skip the NMD TS check --- arc/checks/ts.py | 5 +++++ arc/main.py | 8 ++++++++ arc/processor.py | 3 +++ arc/scheduler.py | 5 +++++ arc/statmech/arkane.py | 4 ++++ arc/statmech/factory.py | 3 +++ 6 files changed, 28 insertions(+) diff --git a/arc/checks/ts.py b/arc/checks/ts.py index 940cab8334..1d048792eb 100644 --- a/arc/checks/ts.py +++ b/arc/checks/ts.py @@ -46,6 +46,7 @@ def check_ts(reaction: 'ARCReaction', output: Optional[dict] = None, sp_level: Optional['Level'] = None, freq_scale_factor: float = 1.0, + skip_nmd: bool = False, verbose: bool = True, ): """ @@ -68,6 +69,7 @@ def check_ts(reaction: 'ARCReaction', output (dict, optional): The Scheduler output dictionary. sp_level (Level, optional): The single-point energy level of theory. freq_scale_factor (float, optional): The frequency scaling factor. + skip_nmd (bool, optional): Whether to skip the normal mode displacement check. verbose (bool, optional): Whether to print logging messages. """ checks = checks or list() @@ -95,6 +97,9 @@ def check_ts(reaction: 'ARCReaction', except (ValueError, KeyError) as e: logger.error(f'Could not check normal mode displacement, got: \n{e}') reaction.ts_species.ts_checks['NMD'] = True + if skip_nmd and not reaction.ts_species.ts_checks['NMD']: + logger.warning(f'Skipping normal mode displacement check for TS {reaction.ts_species.label}') + reaction.ts_species.ts_checks['NMD'] = True if 'rotors' in checks or (ts_passed_checks(species=reaction.ts_species, exemptions=['E0', 'warnings', 'IRC']) and job is not None): diff --git a/arc/main.py b/arc/main.py index 6876145b11..d7fb8131c9 100644 --- a/arc/main.py +++ b/arc/main.py @@ -157,6 +157,7 @@ class ARC(object): running_jobs (dict, optional): A dictionary of jobs submitted in a precious ARC instance, used for restarting. ts_adapters (list, optional): Entries represent different TS adapters. report_e_elect (bool, optional): Whether to report electronic energy. Default is ``False``. + skip_nmd (bool, optional): Whether to skip normal mode displacement check. Default is ``False``. Attributes: project (str): The project's name. Used for naming the working directory. @@ -224,6 +225,7 @@ class ARC(object): trsh_ess_jobs (bool): Whether to attempt troubleshooting failed ESS jobs. Default is ``True``. ts_adapters (list): Entries represent different TS adapters. report_e_elect (bool): Whether to report electronic energy. + skip_nmd (bool): Whether to skip normal mode displacement check. """ @@ -274,6 +276,7 @@ def __init__(self, ts_guess_level: Optional[Union[str, dict, Level]] = None, verbose=logging.INFO, report_e_elect: Optional[bool] = False, + skip_nmd: Optional[bool] = False, ): if project is None: @@ -330,6 +333,7 @@ def __init__(self, self.freq_scale_factor = freq_scale_factor self.ts_adapters = ts_adapters self.report_e_elect = report_e_elect + self.skip_nmd = skip_nmd for ts_adapter in self.ts_adapters or list(): if ts_adapter.lower() not in _registered_job_adapters.keys(): raise InputError(f'Unknown TS adapter: "{ts_adapter}"') @@ -538,6 +542,8 @@ def as_dict(self) -> dict: restart_dict['verbose'] = int(self.verbose) if self.report_e_elect: restart_dict['report_e_elect'] = self.report_e_elect + if self.skip_nmd: + restart_dict['skip_nmd'] = self.skip_nmd return restart_dict def write_input_file(self, path=None): @@ -605,6 +611,7 @@ def execute(self) -> dict: fine_only=self.fine_only, ts_adapters=self.ts_adapters, report_e_elect=self.report_e_elect, + skip_nmd=self.skip_nmd, ) save_yaml_file(path=os.path.join(self.project_directory, 'output', 'status.yml'), content=self.scheduler.output) @@ -635,6 +642,7 @@ def execute(self) -> dict: compare_to_rmg=self.compare_to_rmg, three_params=self.three_params, sp_level=self.arkane_level_of_theory, + skip_nmd=self.skip_nmd, ) status_dict = self.summary() diff --git a/arc/processor.py b/arc/processor.py index d80cce85c5..39ca0b74d3 100644 --- a/arc/processor.py +++ b/arc/processor.py @@ -38,6 +38,7 @@ def process_arc_project(thermo_adapter: str, rmg_database: Optional[RMGDatabase] = None, compare_to_rmg: bool = True, three_params: bool = True, + skip_nmd: bool = False, ) -> None: """ Process an ARC project, generate thermo and rate coefficients using statistical mechanics (statmech). @@ -68,6 +69,7 @@ def process_arc_project(thermo_adapter: str, three_params (bool, optional): Compute rate coefficients using the modified three-parameter Arrhenius equation format (``True``, default) or classical two-parameter Arrhenius equation format (``False``). + skip_nmd (bool, optional): Whether to skip the normal mode displacement check analysis. Defaults to ``False``. """ T_min = T_min or (300, 'K') T_max = T_max or (3000, 'K') @@ -129,6 +131,7 @@ def process_arc_project(thermo_adapter: str, T_max=T_max, T_count=T_count, three_params=three_params, + skip_nmd=skip_nmd, ) statmech_adapter.compute_high_p_rate_coefficient() if reaction.kinetics is not None: diff --git a/arc/scheduler.py b/arc/scheduler.py index fcd6c0f15d..0a8af14861 100644 --- a/arc/scheduler.py +++ b/arc/scheduler.py @@ -162,6 +162,7 @@ class Scheduler(object): trsh_ess_jobs (bool, optional): Whether to attempt troubleshooting failed ESS jobs. Default is ``True``. ts_adapters (list, optional): Entries represent different TS adapters. report_e_elect (bool, optional): Whether to report electronic energy. Default is ``False``. + skip_nmd (bool, optional): Whether to skip normal mode displacement check. Default is ``False``. Attributes: project (str): The project's name. Used for naming the working directory. @@ -217,6 +218,7 @@ class Scheduler(object): trsh_ess_jobs (bool): Whether to attempt troubleshooting failed ESS jobs. Default is ``True``. ts_adapters (list): Entries represent different TS adapters. report_e_elect (bool): Whether to report electronic energy. + skip_nmd (bool): Whether to skip normal mode displacement check. """ def __init__(self, @@ -252,6 +254,7 @@ def __init__(self, freq_scale_factor: float = 1.0, ts_adapters: List[str] = None, report_e_elect: Optional[bool] = False, + skip_nmd: Optional[bool] = False, ) -> None: self.project = project @@ -284,6 +287,7 @@ def __init__(self, self.ts_adapters = [ts_adapter.lower() for ts_adapter in self.ts_adapters] self.output = dict() self.report_e_elect = report_e_elect + self.skip_nmd = skip_nmd self.species_dict, self.rxn_dict = dict(), dict() for species in self.species_list: @@ -2327,6 +2331,7 @@ def check_freq_job(self, check_ts(reaction=self.rxn_dict[self.species_dict[label].rxn_index], job=job, checks=['freq'], + skip_nmd=self.skip_nmd, ) if self.species_dict[label].ts_checks['NMD'] is False: logger.info(f'TS {label} did not pass the normal mode displacement check. ' diff --git a/arc/statmech/arkane.py b/arc/statmech/arkane.py index eabb2c7cca..23ae1d1d40 100644 --- a/arc/statmech/arkane.py +++ b/arc/statmech/arkane.py @@ -55,6 +55,7 @@ class ArkaneAdapter(StatmechAdapter): freq_scale_factor (float, optional): The harmonic frequencies scaling factor. species (ARCSpecies, optional): The species object. reaction (ARCReaction, optional): The reaction object. + skip_nmd (bool, optional): Whether to skip the normal mode displacement check. ``True`` to skip. species_dict (dict, optional): Keys are labels, values are ARCSpecies objects. T_min (tuple, optional): The minimum temperature for kinetics computations, e.g., (500, 'K'). T_max (tuple, optional): The maximum temperature for kinetics computations, e.g., (3000, 'K'). @@ -72,6 +73,7 @@ def __init__(self, freq_scale_factor: float = 1.0, species: ARCSpecies = None, reaction: ARCReaction = None, + skip_nmd: bool = False, species_dict: dict = None, T_min: tuple = None, T_max: tuple = None, @@ -85,6 +87,7 @@ def __init__(self, self.freq_scale_factor = freq_scale_factor self.species = species self.reaction = reaction + self.skip_nmd = skip_nmd self.species_dict = species_dict self.T_min = T_min self.T_max = T_max @@ -216,6 +219,7 @@ def compute_high_p_rate_coefficient(self, check_ts(reaction=self.reaction, checks=['energy', 'freq'], rxn_zone_atom_indices=ts_species.rxn_zone_atom_indices, + skip_nmd=self.skip_nmd, ) if require_ts_convergence and not ts_passed_checks(species=self.reaction.ts_species, exemptions=['warnings', 'IRC', 'E0'], diff --git a/arc/statmech/factory.py b/arc/statmech/factory.py index 0a3d488348..909966fe7b 100644 --- a/arc/statmech/factory.py +++ b/arc/statmech/factory.py @@ -40,6 +40,7 @@ def statmech_factory(statmech_adapter_label: str, # add everything that goes in freq_scale_factor: float = 1.0, species: 'ARCSpecies' = None, reaction: 'ARCReaction' = None, + skip_nmd: bool = False, species_dict: dict = None, T_min: Optional[tuple] = None, T_max: Optional[tuple] = None, @@ -60,6 +61,7 @@ def statmech_factory(statmech_adapter_label: str, # add everything that goes in freq_scale_factor (float, optional): The harmonic frequencies scaling factor. species (ARCSpecies, optional): The species object. reaction (list, optional): The reaction object. + skip_nmd (bool, optional): Whether to skip the normal mode displacement check analysis. species_dict (dict, optional): Keys are labels, values are ARCSpecies objects. T_min (tuple, optional): The minimum temperature for kinetics computations, e.g., (500, 'K'). T_max (tuple, optional): The maximum temperature for kinetics computations, e.g., (3000, 'K'). @@ -79,6 +81,7 @@ def statmech_factory(statmech_adapter_label: str, # add everything that goes in freq_scale_factor=freq_scale_factor, species=species, reaction=reaction, + skip_nmd=skip_nmd, species_dict=species_dict, T_min=T_min, T_max=T_max,