From ba439b344639209f87a2b8458817be5960e0139b Mon Sep 17 00:00:00 2001 From: Alon Grinberg Dana Date: Sat, 18 May 2024 22:54:11 +0300 Subject: [PATCH 1/9] Minor: Logger import in RMG adapter --- t3/simulate/rmg_constantTP.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/t3/simulate/rmg_constantTP.py b/t3/simulate/rmg_constantTP.py index e18a002e..6261afe8 100755 --- a/t3/simulate/rmg_constantTP.py +++ b/t3/simulate/rmg_constantTP.py @@ -7,9 +7,8 @@ import itertools import os import pandas as pd -import re import shutil -from typing import List, Optional +from typing import List, Optional, TYPE_CHECKING from rmgpy.kinetics.diffusionLimited import diffusion_limiter from rmgpy.rmg.listener import SimulationProfilePlotter, SimulationProfileWriter @@ -21,11 +20,13 @@ from t3.common import get_chem_to_rmg_rxn_index_map, get_species_by_label, get_values_within_range, \ get_observable_label_from_header, get_parameter_from_header, time_lapse -from t3.logger import Logger from t3.simulate.adapter import SimulateAdapter from t3.simulate.factory import register_simulate_adapter from t3.utils.writer import write_rmg_input_file +if TYPE_CHECKING: + from t3.logger import Logger + class RMGConstantTP(SimulateAdapter): """ @@ -63,7 +64,7 @@ def __init__(self, t3: dict, rmg: dict, paths: dict, - logger: Logger, + logger: 'Logger', atol: float = 1e-16, rtol: float = 1e-8, observable_list: Optional[list] = None, From a8ddaee7a86064a4888b6bafba4a9d0b53338d59 Mon Sep 17 00:00:00 2001 From: Alon Grinberg Dana Date: Sat, 25 May 2024 18:47:30 +0300 Subject: [PATCH 2/9] Added shared_library_name to schema with validators for library_name and shared_library_name --- t3/schema.py | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/t3/schema.py b/t3/schema.py index 630f80af..cf896837 100644 --- a/t3/schema.py +++ b/t3/schema.py @@ -10,6 +10,7 @@ from pydantic import BaseModel, conint, confloat, constr, root_validator, validator +from t3.common import VALID_CHARS from t3.simulate.factory import _registered_simulate_adapters @@ -43,7 +44,8 @@ class T3Options(BaseModel): max_rmg_processes: Optional[conint(ge=1)] = None max_rmg_iterations: Optional[conint(ge=1)] = None library_name: constr(max_length=255) = 'T3lib' - save_libraries_directly_in_rmgdb: bool = False + shared_library_name: Optional[constr(max_length=255)] = None + external_library_path: Optional[constr(max_length=255)] = None num_sa_per_temperature_range: conint(ge=1) = 3 num_sa_per_pressure_range: conint(ge=1) = 3 num_sa_per_volume_range: conint(ge=1) = 3 @@ -63,6 +65,35 @@ def check_collision_violators_rates(cls, value, values): values['collision_violators_thermo'] = True return value + @validator('library_name') + def check_library_name(cls, value): + """T3Options.library_name validator""" + for char in value: + if char not in VALID_CHARS: + raise ValueError(f'The library name "{value}" contains an invalid character: {char}.\n' + f'Only the following characters are allowed:\n{VALID_CHARS}') + return value + + @validator('shared_library_name') + def check_shared_library_name(cls, value): + """T3Options.shared_library_name validator""" + if value is not None: + for char in value: + if char not in VALID_CHARS + '/': + raise ValueError(f'The shared library name "{value}" contains an invalid character: {char}.\n' + f'Only the following characters are allowed:\n{VALID_CHARS}') + return value + + @validator('external_library_path') + def check_external_library_path(cls, value): + """T3Options.external_library_path validator""" + if value is not None: + for char in value: + if char not in VALID_CHARS + '/': + raise ValueError(f'The external library path "{value}" contains an invalid character: {char}.\n' + f'Only the following characters are allowed:\n{VALID_CHARS}') + return value + class T3Sensitivity(BaseModel): """ From eb46ee769e63e58626323215622affaf6a7eac38 Mon Sep 17 00:00:00 2001 From: Alon Grinberg Dana Date: Sat, 18 May 2024 09:45:10 +0300 Subject: [PATCH 3/9] Tests: Schema shared_library_name --- tests/test_schema.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/test_schema.py b/tests/test_schema.py index 6d3b5540..70f1ba62 100644 --- a/tests/test_schema.py +++ b/tests/test_schema.py @@ -56,6 +56,7 @@ def test_t3_options_schema(): assert t3_options.max_RMG_walltime == '00:02:00:00' assert t3_options.max_T3_walltime == '01:00:00:00' assert t3_options.library_name == 'T3_library' + assert t3_options.shared_library_name is None with pytest.raises(ValidationError): # check that flux_adapter is constrained to at most 255 characters @@ -85,6 +86,14 @@ def test_t3_options_schema(): # check that library_name is constrained to at most 255 characters T3Options(library_name=quote) + with pytest.raises(ValidationError): + # check that library_name is constrained to valid characters + T3Options(library_name='lib name with spaces') + + with pytest.raises(ValidationError): + # check that shared_library_name is constrained to valid characters + T3Options(shared_library_name='lib@name') + def test_t3_sensitivity_schema(): """Test creating an instance of T3Sensitivity""" From b041601723a19befda3982ce420f1a7005ddf7b1 Mon Sep 17 00:00:00 2001 From: Alon Grinberg Dana Date: Sat, 18 May 2024 23:55:57 +0300 Subject: [PATCH 4/9] Added shared_library_name to commented input file --- examples/commented/input.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/commented/input.yml b/examples/commented/input.yml index 8d06a436..d5a44c8e 100644 --- a/examples/commented/input.yml +++ b/examples/commented/input.yml @@ -21,8 +21,9 @@ t3: max_RMG_exceptions_allowed: 10 # optional, maximum number of times RMG is allowed to crash, default: 10 max_RMG_walltime: '00:02:00:00' # optional, default: ``None`` max_T3_walltime: '01:00:00:00' # optional, default: ``None`` - library_name: T3 # optional, default: 'T3' - save_libraries_directly_in_rmgdb: False # optional, whether to save the RMG kinetics and thermo libraries directly in the RMG-database repository, default: ``False`` + library_name: T3 # optional, this is the name of the RMG libraries T3 creates as saves as output. default: 'T3' + shared_library_name: T3_shared # optional, this is the name of RMG libraries (kinetics and thermo) created inside the respective RMG database paths that several T3 concurrent projects may share. default: ``None`` + external_library_path: None # optional, path to an external RMG library to use for saving shared libraries, default: ``None`` (i.e., use the RMG database path) num_sa_per_temperature_range: 3 # optional, if a range of temperatures is given this argument specifies how many equally distributed points to generate for local SA runs, default: 3 num_sa_per_pressure_range: 3 # optional, if a range of pressures is given this argument specifies how many equally distributed points to generate for local SA runs, default: 3 num_sa_per_volume_range: 3 # optional, if a range of volumes is given this argument specifies how many equally distributed points to generate for local SA runs, default: 3 From 57ec69d93347784446fb08e1583f143b83b17917 Mon Sep 17 00:00:00 2001 From: Alon Grinberg Dana Date: Sat, 18 May 2024 10:57:19 +0300 Subject: [PATCH 5/9] Implemented shared RMG libraries in main --- t3/main.py | 55 +++++++++++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/t3/main.py b/t3/main.py index cc45ebc5..fd082296 100755 --- a/t3/main.py +++ b/t3/main.py @@ -382,12 +382,12 @@ def set_paths(self, 'ARC thermo lib': os.path.join(iteration_path, 'ARC', 'output', 'RMG libraries', 'thermo', f"{self.qm['project'] if 'project' in self.qm else self.project}.py"), 'ARC kinetics lib': os.path.join(iteration_path, 'ARC', 'output', 'RMG libraries', 'kinetics'), - 'RMG T3 thermo lib': os.path.join(RMG_THERMO_LIB_BASE_PATH, f"{self.t3['options']['library_name']}.py") \ - if self.t3['options']['save_libraries_directly_in_rmgdb'] \ - else os.path.join(project_directory, 'Libraries', f"{self.t3['options']['library_name']}.py"), - 'RMG T3 kinetics lib': os.path.join(RMG_KINETICS_LIB_BASE_PATH, f"{self.t3['options']['library_name']}") \ - if self.t3['options']['save_libraries_directly_in_rmgdb'] \ - else os.path.join(project_directory, 'Libraries', f"{self.t3['options']['library_name']}"), + 'RMG T3 thermo lib': os.path.join(project_directory, 'Libraries', f"{self.t3['options']['library_name']}.py"), + 'RMG T3 kinetics lib': os.path.join(project_directory, 'Libraries', f"{self.t3['options']['library_name']}"), + 'shared RMG T3 thermo lib': os.path.join(RMG_THERMO_LIB_BASE_PATH, f"{self.t3['options']['shared_library_name']}.py") + if self.t3['options']['shared_library_name'] is not None else None, + 'shared RMG T3 kinetics lib': os.path.join(RMG_KINETICS_LIB_BASE_PATH, f"{self.t3['options']['shared_library_name']}") + if self.t3['options']['shared_library_name'] is not None else None, } def restart(self) -> Tuple[int, bool]: @@ -594,29 +594,16 @@ def run_rmg(self, restart_rmg: bool = False): Run RMG. Raises: - Various RMG Exceptions: if RMG crushed too many times. + Various RMG Exceptions if RMG crushed too many times. """ self.logger.info(f'Running RMG (tolerance = {self.get_current_rmg_tol()}, iteration {self.iteration})...') # Use the RMG T3 libraries only if they exist and not already in use. - # 1. thermo - t3_thermo_lib = self.t3['options']['library_name'] if self.t3['options']['save_libraries_directly_in_rmgdb'] \ - else self.paths['RMG T3 thermo lib'] - if t3_thermo_lib not in self.rmg['database']['thermo_libraries'] \ - and os.path.isfile(self.paths['RMG T3 thermo lib']): - self.rmg['database']['thermo_libraries'] = [t3_thermo_lib] + self.rmg['database']['thermo_libraries'] - elif t3_thermo_lib in self.rmg['database']['thermo_libraries'] \ - and not os.path.isfile(self.paths['RMG T3 thermo lib']): - self.rmg['database']['thermo_libraries'].pop(self.rmg['database']['thermo_libraries'].index(t3_thermo_lib)) - # 2. kinetics - t3_kinetics_lib = self.t3['options']['library_name'] if self.t3['options']['save_libraries_directly_in_rmgdb'] \ - else self.paths['RMG T3 kinetics lib'] - if t3_kinetics_lib not in self.rmg['database']['kinetics_libraries'] \ - and os.path.isdir(self.paths['RMG T3 kinetics lib']): - self.rmg['database']['kinetics_libraries'] = [t3_kinetics_lib] + self.rmg['database']['kinetics_libraries'] - elif t3_kinetics_lib in self.rmg['database']['kinetics_libraries'] \ - and not os.path.isdir(self.paths['RMG T3 kinetics lib']): - self.rmg['database']['kinetics_libraries'].pop(self.rmg['database']['kinetics_libraries'].index(t3_kinetics_lib)) + for token in ['thermo', 'kinetics']: + t3_lib, shared_rmg_lib = self.paths[f'RMG T3 {token} lib'], self.paths[f'shared RMG T3 {token} lib'] + if shared_rmg_lib is not None: + self.add_library_to_rmg_run(library_name=shared_rmg_lib, library_type=token) + self.add_library_to_rmg_run(library_name=t3_lib, library_type=token) write_rmg_input_file( rmg=self.rmg, @@ -1501,6 +1488,24 @@ def load_species_and_reactions(self): ) self.reactions[key] = mod_rxn_dict + def add_library_to_rmg_run(self, + library_name: str, + library_type: str, + ) -> None: + """ + Add a library to the RMG run. + + Args: + library_name (str): The library name. + library_type (str): The library type, either 'thermo' or 'kinetics'. + """ + library_type = 'thermo_libraries' if library_type == 'thermo' else 'kinetics_libraries' + exists_function = os.path.isfile if library_type == 'thermo_libraries' else os.path.isdir + if library_name not in self.rmg['database'][library_type] and exists_function(library_name): + self.rmg['database'][library_type] = [library_name] + self.rmg['database'][library_type] + elif library_name in self.rmg['database'][library_type] and not os.path.isfile(library_name): + self.rmg['database'][library_type].pop(self.rmg['database'][library_type].index(library_name)) + def check_overtime(self) -> bool: """ Check that the timer hasn't run out. From 53fea02eb6b8dd1651472f2168f9b76d26352a2c Mon Sep 17 00:00:00 2001 From: Alon Grinberg Dana Date: Fri, 31 May 2024 05:35:15 +0300 Subject: [PATCH 6/9] Relocated add_to_rmg_libraries from main into a new libraries util Extracted functions and added a race condition handler --- t3/main.py | 128 ++++------------------------ t3/utils/libraries.py | 189 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 203 insertions(+), 114 deletions(-) create mode 100644 t3/utils/libraries.py diff --git a/t3/main.py b/t3/main.py index fd082296..d78032b7 100755 --- a/t3/main.py +++ b/t3/main.py @@ -24,19 +24,15 @@ from arkane import Arkane from rmgpy import settings as rmg_settings from rmgpy.chemkin import load_chemkin_file -from rmgpy.data.kinetics import KineticsLibrary from rmgpy.data.kinetics.library import LibraryReaction -from rmgpy.data.thermo import ThermoLibrary from rmgpy.exceptions import (ChemicallySignificantEigenvaluesError, ChemkinError, ModifiedStrongCollisionError, NetworkError, ) -from rmgpy.kinetics import Arrhenius, KineticsData from rmgpy.reaction import Reaction from rmgpy.rmg.pdep import PDepReaction from rmgpy.species import Species -from rmgpy.thermo import NASAPolynomial, NASA, ThermoData, Wilhoit from arc.common import (get_number_with_ordinal_indicator, get_ordinal_indicator, @@ -54,6 +50,7 @@ from t3.runners.rmg_runner import rmg_runner from t3.schema import InputBase from t3.simulate.factory import simulate_factory +from t3.utils.libraries import add_to_rmg_libraries from t3.utils.writer import write_pdep_network_file, write_rmg_input_file RMG_THERMO_LIB_BASE_PATH = os.path.join(rmg_settings['database.directory'], 'thermo', 'libraries') @@ -382,11 +379,13 @@ def set_paths(self, 'ARC thermo lib': os.path.join(iteration_path, 'ARC', 'output', 'RMG libraries', 'thermo', f"{self.qm['project'] if 'project' in self.qm else self.project}.py"), 'ARC kinetics lib': os.path.join(iteration_path, 'ARC', 'output', 'RMG libraries', 'kinetics'), - 'RMG T3 thermo lib': os.path.join(project_directory, 'Libraries', f"{self.t3['options']['library_name']}.py"), - 'RMG T3 kinetics lib': os.path.join(project_directory, 'Libraries', f"{self.t3['options']['library_name']}"), - 'shared RMG T3 thermo lib': os.path.join(RMG_THERMO_LIB_BASE_PATH, f"{self.t3['options']['shared_library_name']}.py") + 'T3 thermo lib': os.path.join(project_directory, 'Libraries', f"{self.t3['options']['library_name']}.py"), + 'T3 kinetics lib': os.path.join(project_directory, 'Libraries', f"{self.t3['options']['library_name']}"), + 'shared T3 thermo lib': os.path.join(self.t3['options']['external_library_path'] or RMG_THERMO_LIB_BASE_PATH, + f"{self.t3['options']['shared_library_name']}.py") if self.t3['options']['shared_library_name'] is not None else None, - 'shared RMG T3 kinetics lib': os.path.join(RMG_KINETICS_LIB_BASE_PATH, f"{self.t3['options']['shared_library_name']}") + 'shared T3 kinetics lib': os.path.join(self.t3['options']['external_library_path'] or RMG_KINETICS_LIB_BASE_PATH, + f"{self.t3['options']['shared_library_name']}") if self.t3['options']['shared_library_name'] is not None else None, } @@ -572,7 +571,10 @@ def process_arc_run(self): ) if len(converged_spc_keys) or len(converged_rxn_keys): # we calculated something, add to thermo/kinetic library - self.add_to_rmg_libraries() + add_to_rmg_libraries(library_name=self.t3['options']['library_name'], + shared_library_name=self.t3['options']['shared_library_name'], + paths=self.paths, + logger=self.logger) # clear the calculated objects from self.qm: self.qm['species'], self.qm['reactions'] = list(), list() self.dump_species_and_reactions() @@ -598,9 +600,9 @@ def run_rmg(self, restart_rmg: bool = False): """ self.logger.info(f'Running RMG (tolerance = {self.get_current_rmg_tol()}, iteration {self.iteration})...') - # Use the RMG T3 libraries only if they exist and not already in use. + # Use the T3 libraries only if they exist and not already in use. for token in ['thermo', 'kinetics']: - t3_lib, shared_rmg_lib = self.paths[f'RMG T3 {token} lib'], self.paths[f'shared RMG T3 {token} lib'] + t3_lib, shared_rmg_lib = self.paths[f'T3 {token} lib'], self.paths[f'shared T3 {token} lib'] if shared_rmg_lib is not None: self.add_library_to_rmg_run(library_name=shared_rmg_lib, library_type=token) self.add_library_to_rmg_run(library_name=t3_lib, library_type=token) @@ -1347,108 +1349,6 @@ def add_reaction(self, self.reactions[rxn_key]['reasons'].append(reason) return None - def add_to_rmg_libraries(self): - """ - Creates RMG libraries in the RMG database repository if they don't already exist, - and appends with the respective entries from the libraries generated by ARC. - - Todo: - Tests kinetics libraries. - """ - # 1. Thermo: - arc_thermo_lib_path = self.paths['ARC thermo lib'] - rmg_t3_thermo_lib_path = self.paths['RMG T3 thermo lib'] - local_context = { - 'ThermoData': ThermoData, - 'Wilhoit': Wilhoit, - 'NASAPolynomial': NASAPolynomial, - 'NASA': NASA, - } - if os.path.isfile(arc_thermo_lib_path) and os.path.isfile(rmg_t3_thermo_lib_path): - # This thermo library already exists in the RMG database: Load it, append new entries, and save. - rmg_thermo_lib, arc_thermo_lib = ThermoLibrary(), ThermoLibrary() - rmg_thermo_lib.load(path=rmg_t3_thermo_lib_path, local_context=local_context, global_context=dict()) - arc_thermo_lib.load(path=arc_thermo_lib_path, local_context=local_context, global_context=dict()) - arc_description = arc_thermo_lib.long_desc - description_to_append = '\n' - append = False - for line in arc_description.splitlines(): - if 'Overall time since project initiation' in line: - append = False - if append: - description_to_append += line + '\n' - if 'Considered the following' in line: - append = True - rmg_thermo_lib.long_desc += description_to_append - for entry in arc_thermo_lib.entries.values(): - entry_species = Species(molecule=[entry.item]) - entry_species.generate_resonance_structures(keep_isomorphic=False, filter_structures=True) - for existing_entry in rmg_thermo_lib.entries.values(): - if entry_species.is_isomorphic(existing_entry.item): - if entry.label != existing_entry.label: - self.logger.warning(f"Not adding species {entry.label} to the " - f"{self.t3['options']['library_name']} thermo library, " - f"the species seems to already exist under the label " - f"{existing_entry.label}.") - break - rmg_thermo_lib.entries[entry.label] = entry - rmg_thermo_lib.save(path=rmg_t3_thermo_lib_path) - else: - # This thermo library doesn't exist in the RMG database: Just copy the library generated by ARC. - if os.path.isfile(arc_thermo_lib_path): - if not os.path.isdir(os.path.dirname(rmg_t3_thermo_lib_path)): - os.makedirs(os.path.dirname(rmg_t3_thermo_lib_path)) - shutil.copy(arc_thermo_lib_path, rmg_t3_thermo_lib_path) - - # 2. Kinetics: - arc_kinetics_lib_path = self.paths['ARC kinetics lib'] - rmg_t3_kinetics_lib_path = self.paths['RMG T3 kinetics lib'] - local_context = { - 'KineticsData': KineticsData, - 'Arrhenius': Arrhenius, - } - if os.path.isdir(arc_kinetics_lib_path) and os.path.isdir(rmg_t3_kinetics_lib_path): - # This kinetics library already exists (in the RMG database or locally): Load it, append entries, and save. - rmg_kinetics_lib, arc_kinetics_lib = KineticsLibrary(), KineticsLibrary() - rmg_kinetics_lib.load(path=rmg_t3_kinetics_lib_path, local_context=local_context, global_context=dict()) - arc_kinetics_lib.load(path=arc_kinetics_lib_path, local_context=local_context, global_context=dict()) - arc_description = arc_kinetics_lib.long_desc - description_to_append = '\n' - append = False - for line in arc_description.splitlines(): - if 'Overall time since project initiation' in line: - append = False - if append: - description_to_append += line + '\n' - if 'Considered the following' in line: - append = True - rmg_kinetics_lib.long_desc += description_to_append - for entry in arc_kinetics_lib.entries.values(): - entry_reaction = Reaction(reactants=entry.item.reactants[:], - products=entry.item.products[:], - specific_collider=entry.item.specific_collider, - kinetics=entry.data, - duplicate=entry.item.duplicate, - reversible=entry.item.reversible, - allow_pdep_route=entry.item.allow_pdep_route, - elementary_high_p=entry.item.elementary_high_p, - ) - for existing_entry in rmg_kinetics_lib.entries.values(): - if entry_reaction.is_isomorphic(existing_entry.item): - self.logger.warning(f"Not adding reaction {entry.label} to the " - f"{self.t3['options']['library_name']} kinetics library, " - f"the reaction seems to already exist under the label " - f"{existing_entry.label}.") - break - rmg_kinetics_lib.entries[entry.label] = entry - rmg_kinetics_lib.save(path=rmg_t3_kinetics_lib_path) - else: - # This kinetics library doesn't exist in the RMG database: Just copy the library generated by ARC. - if os.path.isfile(arc_kinetics_lib_path): - if not os.path.isdir(os.path.dirname(rmg_t3_kinetics_lib_path)): - os.makedirs(rmg_t3_kinetics_lib_path) - shutil.copy(arc_kinetics_lib_path, rmg_t3_kinetics_lib_path) - def dump_species_and_reactions(self): """ Dump self.species and self.reactions in case T3 needs to be restarted. @@ -1493,7 +1393,7 @@ def add_library_to_rmg_run(self, library_type: str, ) -> None: """ - Add a library to the RMG run. + Add a T3-generated library to the RMG run. Args: library_name (str): The library name. diff --git a/t3/utils/libraries.py b/t3/utils/libraries.py new file mode 100644 index 00000000..59cfd51b --- /dev/null +++ b/t3/utils/libraries.py @@ -0,0 +1,189 @@ +""" +t3 utils libraries module +for working with RMG thermo and kinetics libraries +""" + +import datetime +import os +import shutil +import time + +from typing import Dict, TYPE_CHECKING + +from rmgpy.data.kinetics import KineticsLibrary +from rmgpy.data.thermo import ThermoLibrary +from rmgpy.kinetics import Arrhenius, KineticsData +from rmgpy.reaction import Reaction +from rmgpy.thermo import NASAPolynomial, NASA, ThermoData, Wilhoit +from rmgpy.species import Species + +if TYPE_CHECKING: + from t3.logger import Logger + + +def add_to_rmg_libraries(library_name: str, + shared_library_name: str, + paths: Dict[str, str], + logger: 'Logger', + ): + """ + Creates RMG libraries in the RMG database repository if they don't already exist, + and appends with the respective entries from the libraries generated by ARC. + + Args: + library_name (str): The name of the RMG library. + shared_library_name (str): The name of an RMG database library shared between T3 projects. + paths (Dict[str, str]): T3's dictionary of paths. + logger (Logger): Instance of T3's Logger class. + """ + for token in ['thermo', 'kinetics']: + arc_lib_path, t3_lib_path, shared_lib_path = \ + paths[f'ARC {token} lib'], paths[f'T3 {token} lib'], paths[f'shared T3 {token} lib'] + if token == 'thermo': + local_context = { + 'ThermoData': ThermoData, + 'Wilhoit': Wilhoit, + 'NASAPolynomial': NASAPolynomial, + 'NASA': NASA, + } + else: + local_context = { + 'KineticsData': KineticsData, + 'Arrhenius': Arrhenius, + } + for to_lib_path, lib_name, race in zip([shared_lib_path, t3_lib_path], + [library_name, shared_library_name], + [True, False]): + if os.path.isfile(arc_lib_path) and to_lib_path is not None and os.path.isfile(to_lib_path): + append_to_rmg_library(library_name=lib_name, + from_lib_path=arc_lib_path, + to_lib_path=to_lib_path, + local_context=local_context, + lib_type=token, + logger=logger, + race=race, + ) + else: + # The destination library (T3's or the shared) doesn't exist. Just copy the library generated by ARC. + if os.path.isfile(arc_lib_path) and to_lib_path is not None: + if not os.path.isdir(os.path.dirname(to_lib_path)): + os.makedirs(os.path.dirname(to_lib_path)) + shutil.copy(arc_lib_path, to_lib_path) + + +def append_to_rmg_library(library_name: str, + from_lib_path: str, + to_lib_path: str, + local_context: dict, + lib_type: str, + logger: 'Logger', + race: bool = False, + ): + """ + Append the entries from the ARC-generated library to an RMG library. + + Args: + library_name (str): The name of the RMG library. + from_lib_path (str): The path to the ARC-generated library. + to_lib_path (str): The path to the RMG library to append to. + local_context (dict): The local context to use when loading the libraries. + lib_type (str): The type of the library (either 'thermo' or 'kinetics'). + logger (Logger): Instance of T3's Logger class. + race (bool, optional): Whether to take measures to avoid a race condition when appending to the library. + """ + race_path = os.path.join(os.path.dirname(to_lib_path), f'{library_name}.race') + if race: + race_free = check_race_condition(race_path) + if not race_free: + logger.error(f'Could not write to library {to_lib_path} due to a race condition.\n' + f'Check whether it is safe to delete the {race_path} file to continue.') + return + from_lib, to_lib = (ThermoLibrary(), ThermoLibrary()) if lib_type == 'thermo' \ + else (KineticsLibrary(), KineticsLibrary()) + to_lib.load(path=to_lib_path, local_context=local_context, global_context=dict()) + from_lib.load(path=from_lib_path, local_context=local_context, global_context=dict()) + from_description = from_lib.long_desc + description_to_append = '\n' + append = False + for line in from_description.splitlines(): + if 'Overall time since project initiation' in line: + append = False + if append: + description_to_append += line + '\n' + if 'Considered the following' in line: + append = True + to_lib.long_desc += description_to_append + for entry in from_lib.entries.values(): + skip_entry = False + if lib_type == 'thermo': + entry_species = Species(molecule=[entry.item]) + entry_species.generate_resonance_structures(keep_isomorphic=False, filter_structures=True) + for existing_entry in to_lib.entries.values(): + if entry_species.is_isomorphic(existing_entry.item): + if entry.label != existing_entry.label: + logger.warning(f"Not adding species {entry.label} to the {library_name} thermo library, " + f"the species seems to already exist under the label {existing_entry.label}.") + skip_entry = True + break + elif lib_type == 'kinetics': + entry_reaction = Reaction(reactants=entry.item.reactants[:], + products=entry.item.products[:], + specific_collider=entry.item.specific_collider, + kinetics=entry.data, + duplicate=entry.item.duplicate, + reversible=entry.item.reversible, + allow_pdep_route=entry.item.allow_pdep_route, + elementary_high_p=entry.item.elementary_high_p, + ) + for existing_entry in to_lib.entries.values(): + if entry_reaction.is_isomorphic(existing_entry.item): + logger.warning(f"Not adding reaction {entry.label} to the {library_name} kinetics library, " + f"the reaction seems to already exist under the label {existing_entry.label}.") + skip_entry = True + break + if not skip_entry: + to_lib.entries[entry.label] = entry + to_lib.save(path=to_lib_path) + lift_race_condition(race_path) + + +def check_race_condition(race_path: str, + ) -> bool: + """ + Check for a race condition and avoid one by creating a race holder file. + + Args: + race_path (str): The path to the race file to check. + + Returns: + bool: Whether there is no race condition and T3 may continue (True) or an unavoidable race exists (False). + """ + counter = 0 + while os.path.isfile(race_path): + with open(race_path, 'r') as f: + content = f.read() + if content: + creation_date = content.split(' ')[-1] + creation_datetime = datetime.datetime.strptime(creation_date, "%H%M%S_%b%d_%Y") + time_delta = datetime.datetime.now() - creation_datetime + if time_delta.total_seconds() > 1000: + lift_race_condition(race_path) + return True + counter += 1 + time.sleep(10) + if counter > 1000: + return False + with open(race_path, 'w') as f: + f.write(f'Race created at {datetime.datetime.now().strftime("%H%M%S_%b%d_%Y")}') + return True + + +def lift_race_condition(race_path: str) -> None: + """ + Lift the race condition by deleting the race holder file. + + Args: + race_path (str): The path to the race file to check. + """ + if os.path.isfile(race_path): + os.remove(race_path) From 2bb4ba27bb27035efe69fd320430b7ca189ba231 Mon Sep 17 00:00:00 2001 From: Alon Grinberg Dana Date: Sat, 18 May 2024 23:40:08 +0300 Subject: [PATCH 7/9] Tests: libraries util --- tests/test_libraries.py | 158 ++++++++++++++++++++++++++++++++++++++++ tests/test_main.py | 156 ++++----------------------------------- 2 files changed, 174 insertions(+), 140 deletions(-) create mode 100644 tests/test_libraries.py diff --git a/tests/test_libraries.py b/tests/test_libraries.py new file mode 100644 index 00000000..821e98fa --- /dev/null +++ b/tests/test_libraries.py @@ -0,0 +1,158 @@ +#!/usr/bin/env python3 +# encoding: utf-8 + +""" +t3 tests test_libraries module +""" + +import os +import shutil + +from rmgpy.data.thermo import ThermoLibrary +from rmgpy.species import Species +from rmgpy.thermo import ThermoData +from rmgpy.statmech import Conformer, IdealGasTranslation, NonlinearRotor, HarmonicOscillator + +from t3.common import DATA_BASE_PATH +from tests.common import run_minimal +from t3.utils.libraries import add_to_rmg_libraries + + +def test_add_to_rmg_library(): + """Test adding thermo calculations to an existing thermo library""" + libraries_path = os.path.join(DATA_BASE_PATH, 'libraries') + if not os.path.isdir(libraries_path): + os.makedirs(libraries_path) + + spc_1 = Species( + index=1, + label='C2H4', + thermo=ThermoData( + Tdata=([300.0, 400.0, 500.0, 600.0, 800.0, 1000.0, 1500.0], 'K'), + Cpdata=([3.0, 4.0, 5.0, 6.0, 8.0, 10.0, 15.0], 'cal/(mol*K)'), + H298=(-20.0, 'kcal/mol'), + S298=(50.0, 'cal/(mol*K)'), + Tmin=(300.0, 'K'), + Tmax=(2000.0, 'K'), + ), + conformer=Conformer( + E0=(0.0, 'kJ/mol'), + modes=[ + IdealGasTranslation(mass=(28.03, 'amu')), + NonlinearRotor(inertia=([5.6952e-47, 2.7758e-46, 3.3454e-46], 'kg*m^2'), symmetry=1), + HarmonicOscillator(frequencies=([834.50, 973.31, 975.37, 1067.1, 1238.5, 1379.5, 1472.3, 1691.3, + 3121.6, 3136.7, 3192.5, 3221.0], 'cm^-1')), + ], + spin_multiplicity=1, + optical_isomers=1, + ), + smiles='C=C', + ) + + spc_2 = Species( + index=2, + label='CH4', + thermo=ThermoData( + Tdata=([300.0, 400.0, 500.0, 600.0, 800.0, 1000.0, 1500.0], 'K'), + Cpdata=([3.0, 4.0, 5.0, 6.0, 8.0, 10.0, 15.0], 'cal/(mol*K)'), + H298=(-50.0, 'kcal/mol'), + S298=(100.0, 'cal/(mol*K)'), + Tmin=(300.0, 'K'), + Tmax=(2000.0, 'K'), + ), + conformer=Conformer( + E0=(0.0, 'kJ/mol'), + modes=[ + IdealGasTranslation(mass=(28.03, 'amu')), + NonlinearRotor(inertia=([5.6952e-47, 2.7758e-46, 3.3454e-46], 'kg*m^2'), symmetry=1), + ], + spin_multiplicity=1, + optical_isomers=1, + ), + smiles='C', + ) + + spc_3 = Species( + index=2, + label='C3H7', + thermo=ThermoData( + Tdata=([300.0, 400.0, 500.0, 600.0, 800.0, 1000.0, 1500.0], 'K'), + Cpdata=([3.0, 4.0, 5.0, 6.0, 8.0, 10.0, 15.0], 'cal/(mol*K)'), + H298=(-92.0, 'kcal/mol'), # this is different + S298=(12.0, 'cal/(mol*K)'), # this is different + Tmin=(300.0, 'K'), + Tmax=(2000.0, 'K'), + ), + conformer=Conformer( + E0=(0.0, 'kJ/mol'), + modes=[ + IdealGasTranslation(mass=(28.03, 'amu')), + NonlinearRotor(inertia=([5.6952e-47, 2.7758e-46, 3.3454e-46], 'kg*m^2'), symmetry=1), + ], + spin_multiplicity=1, + optical_isomers=1, + ), + smiles='[CH2]CC', + ) + + # 1. Test adding one species to an existing library. + for lib_name, spc_list in [('RMG_library', [spc_1, spc_2]), ('ARC_library', [spc_3])]: + thermo_library = ThermoLibrary(name=lib_name, long_desc=lib_name) + for i, spc in enumerate(spc_list): + thermo_library.load_entry(index=i, + label=spc.label, + molecule=spc.to_adjacency_list(), + thermo=spc.thermo, + shortDesc=spc.label, + longDesc=spc.label) + thermo_library.save(os.path.join(libraries_path, f'{lib_name}.py')) + + t3 = run_minimal() + t3.set_paths() + t3.paths['ARC thermo lib'] = os.path.join(libraries_path, 'ARC_library.py') + t3.paths['T3 thermo lib'] = os.path.join(libraries_path, 'RMG_library.py') + add_to_rmg_libraries(library_name=t3.t3['options']['library_name'], + shared_library_name=t3.t3['options']['shared_library_name'], + paths=t3.paths, + logger=t3.logger) + with open(t3.paths['T3 thermo lib'], 'r') as f: + lines = f.readlines() + for line in [" H298 = (-92,'kcal/mol'),\n", + " S298 = (12,'cal/(mol*K)'),\n", + ]: + assert line in lines + + # 2. Test adding one species to an existing library when the new library has a species that also exists. + for lib_name, spc_list in [('RMG_library', [spc_1, spc_2]), ('ARC_library', [spc_1, spc_3])]: + thermo_library = ThermoLibrary(name=lib_name, long_desc=lib_name) + for i, spc in enumerate(spc_list): + thermo_library.load_entry(index=i, + label=spc.label, + molecule=spc.to_adjacency_list(), + thermo=spc.thermo, + shortDesc=spc.label, + longDesc=spc.label) + thermo_library.save(os.path.join(libraries_path, f'{lib_name}.py')) + + t3 = run_minimal() + t3.set_paths() + t3.paths['ARC thermo lib'] = os.path.join(libraries_path, 'ARC_library.py') + t3.paths['T3 thermo lib'] = os.path.join(libraries_path, 'RMG_library.py') + add_to_rmg_libraries(library_name=t3.t3['options']['library_name'], + shared_library_name=t3.t3['options']['shared_library_name'], + paths=t3.paths, + logger=t3.logger) + with open(t3.paths['T3 thermo lib'], 'r') as f: + lines = f.readlines() + count = 0 + for line in lines: + if 'entry(' in line: + count += 1 + assert count == 3 + + +def teardown_module(): + """teardown any state that was previously set up.""" + path = os.path.join(DATA_BASE_PATH, 'libraries') + if os.path.isdir(path): + shutil.rmtree(path, ignore_errors=True) diff --git a/tests/test_main.py b/tests/test_main.py index 3eeab6d4..4d3b3211 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -2,7 +2,7 @@ # encoding: utf-8 """ -t3 tests test_tandem module +t3 tests test_main module """ import datetime @@ -10,12 +10,10 @@ import shutil import re -from rmgpy.data.thermo import ThermoLibrary from rmgpy.reaction import Reaction from rmgpy.rmg.pdep import PDepNetwork, PDepReaction from rmgpy.species import Species from rmgpy.thermo import NASA, ThermoData -from rmgpy.statmech import Conformer, IdealGasTranslation, NonlinearRotor, HarmonicOscillator from arc.common import read_yaml_file from arc.species import ARCSpecies @@ -51,8 +49,9 @@ 'num_sa_per_pressure_range': 3, 'num_sa_per_temperature_range': 3, 'num_sa_per_volume_range': 3, - 'save_libraries_directly_in_rmgdb': False, - 'profiles_adapter': 'RMG'}, + 'profiles_adapter': 'RMG', + 'shared_library_name': None, + }, 'sensitivity': {'ME_methods': ['CSE', 'MSC'], 'SA_threshold': 0.01, 'adapter': 'RMGConstantTP', @@ -289,11 +288,16 @@ def test_set_paths(): 'iteration': 'T3/Projects/test_minimal_delete_after_usage/iteration_1', 'species dict': 'T3/Projects/test_minimal_delete_after_usage/iteration_1/RMG/chemkin/' 'species_dictionary.txt', - 'RMG T3 thermo lib': 'test_minimal_delete_after_usage/Libraries/T3lib.py', - 'RMG T3 kinetics lib': 'test_minimal_delete_after_usage/Libraries/T3', + 'T3 thermo lib': 'test_minimal_delete_after_usage/Libraries/T3lib.py', + 'T3 kinetics lib': 'test_minimal_delete_after_usage/Libraries/T3', + 'shared T3 thermo lib': None, + 'shared T3 kinetics lib': None, } for key, path in t3.paths.items(): - assert paths[key] in path + if path is None: + assert paths[key] is None + else: + assert paths[key] in path def test_restart(): @@ -467,15 +471,15 @@ def test_process_arc_run(): t3.process_arc_run() assert t3.species[0]['converged'] is True assert t3.species[1]['converged'] is False - assert os.path.isfile(t3.paths['RMG T3 thermo lib']) - with open(t3.paths['RMG T3 thermo lib'], 'r') as f: + assert os.path.isfile(t3.paths['T3 thermo lib']) + with open(t3.paths['T3 thermo lib'], 'r') as f: lines = f.readlines() for line in ['name = "T3"\n', "Species imipramine_ol_2_ket_4 (run time: 1 day, 8:24:38)\n", ' label = "imipramine_ol_2_ket_4",\n', " E0 = (-171.078,'kJ/mol'),\n"]: assert line in lines - os.remove(t3.paths['RMG T3 thermo lib']) + os.remove(t3.paths['T3 thermo lib']) def test_get_current_rmg_tol(): @@ -922,133 +926,6 @@ def test_add_reaction(): assert rmg_rxn_1.products[1].label == 's12_NO' -def test_add_to_rmg_library(): - """Test adding thermo calculations to an existing thermo library""" - libraries_path = os.path.join(DATA_BASE_PATH, 'libraries') - if not os.path.isdir(libraries_path): - os.makedirs(libraries_path) - - spc_1 = Species( - index=1, - label='C2H4', - thermo=ThermoData( - Tdata=([300.0, 400.0, 500.0, 600.0, 800.0, 1000.0, 1500.0], 'K'), - Cpdata=([3.0, 4.0, 5.0, 6.0, 8.0, 10.0, 15.0], 'cal/(mol*K)'), - H298=(-20.0, 'kcal/mol'), - S298=(50.0, 'cal/(mol*K)'), - Tmin=(300.0, 'K'), - Tmax=(2000.0, 'K'), - ), - conformer=Conformer( - E0=(0.0, 'kJ/mol'), - modes=[ - IdealGasTranslation(mass=(28.03, 'amu')), - NonlinearRotor(inertia=([5.6952e-47, 2.7758e-46, 3.3454e-46], 'kg*m^2'), symmetry=1), - HarmonicOscillator(frequencies=([834.50, 973.31, 975.37, 1067.1, 1238.5, 1379.5, 1472.3, 1691.3, - 3121.6, 3136.7, 3192.5, 3221.0], 'cm^-1')), - ], - spin_multiplicity=1, - optical_isomers=1, - ), - smiles='C=C', - ) - - spc_2 = Species( - index=2, - label='CH4', - thermo=ThermoData( - Tdata=([300.0, 400.0, 500.0, 600.0, 800.0, 1000.0, 1500.0], 'K'), - Cpdata=([3.0, 4.0, 5.0, 6.0, 8.0, 10.0, 15.0], 'cal/(mol*K)'), - H298=(-50.0, 'kcal/mol'), - S298=(100.0, 'cal/(mol*K)'), - Tmin=(300.0, 'K'), - Tmax=(2000.0, 'K'), - ), - conformer=Conformer( - E0=(0.0, 'kJ/mol'), - modes=[ - IdealGasTranslation(mass=(28.03, 'amu')), - NonlinearRotor(inertia=([5.6952e-47, 2.7758e-46, 3.3454e-46], 'kg*m^2'), symmetry=1), - ], - spin_multiplicity=1, - optical_isomers=1, - ), - smiles='C', - ) - - spc_3 = Species( - index=2, - label='C3H7', - thermo=ThermoData( - Tdata=([300.0, 400.0, 500.0, 600.0, 800.0, 1000.0, 1500.0], 'K'), - Cpdata=([3.0, 4.0, 5.0, 6.0, 8.0, 10.0, 15.0], 'cal/(mol*K)'), - H298=(-92.0, 'kcal/mol'), # this is different - S298=(12.0, 'cal/(mol*K)'), # this is different - Tmin=(300.0, 'K'), - Tmax=(2000.0, 'K'), - ), - conformer=Conformer( - E0=(0.0, 'kJ/mol'), - modes=[ - IdealGasTranslation(mass=(28.03, 'amu')), - NonlinearRotor(inertia=([5.6952e-47, 2.7758e-46, 3.3454e-46], 'kg*m^2'), symmetry=1), - ], - spin_multiplicity=1, - optical_isomers=1, - ), - smiles='[CH2]CC', - ) - - # 1. Test adding one species to an existing library. - for lib_name, spc_list in [('RMG_library', [spc_1, spc_2]), ('ARC_library', [spc_3])]: - thermo_library = ThermoLibrary(name=lib_name, long_desc=lib_name) - for i, spc in enumerate(spc_list): - thermo_library.load_entry(index=i, - label=spc.label, - molecule=spc.to_adjacency_list(), - thermo=spc.thermo, - shortDesc=spc.label, - longDesc=spc.label) - thermo_library.save(os.path.join(libraries_path, f'{lib_name}.py')) - - t3 = run_minimal() - t3.set_paths() - t3.paths['ARC thermo lib'] = os.path.join(libraries_path, 'ARC_library.py') - t3.paths['RMG T3 thermo lib'] = os.path.join(libraries_path, 'RMG_library.py') - t3.add_to_rmg_libraries() - with open(t3.paths['RMG T3 thermo lib'], 'r') as f: - lines = f.readlines() - for line in [" H298 = (-92,'kcal/mol'),\n", - " S298 = (12,'cal/(mol*K)'),\n", - ]: - assert line in lines - - # 2. Test adding one species to an existing library when the new library has a species that also exists. - for lib_name, spc_list in [('RMG_library', [spc_1, spc_2]), ('ARC_library', [spc_1, spc_3])]: - thermo_library = ThermoLibrary(name=lib_name, long_desc=lib_name) - for i, spc in enumerate(spc_list): - thermo_library.load_entry(index=i, - label=spc.label, - molecule=spc.to_adjacency_list(), - thermo=spc.thermo, - shortDesc=spc.label, - longDesc=spc.label) - thermo_library.save(os.path.join(libraries_path, f'{lib_name}.py')) - - t3 = run_minimal() - t3.set_paths() - t3.paths['ARC thermo lib'] = os.path.join(libraries_path, 'ARC_library.py') - t3.paths['RMG T3 thermo lib'] = os.path.join(libraries_path, 'RMG_library.py') - t3.add_to_rmg_libraries() - with open(t3.paths['RMG T3 thermo lib'], 'r') as f: - lines = f.readlines() - count = 0 - for line in lines: - if 'entry(' in line: - count += 1 - assert count == 3 - - def test_dump_species(): """Test dump species for restart purposes""" # create an empty `iteration_5` directory @@ -1167,7 +1044,7 @@ def test_check_overtime(): def teardown_module(): - """teardown any state that was previously setup with a setup_module method.""" + """teardown any state that was previously set up.""" # delete log files for i in range(10): @@ -1197,7 +1074,6 @@ def teardown_module(): os.path.join(DATA_BASE_PATH, 'determine_species', 'log_archive'), os.path.join(DATA_BASE_PATH, 'pdep_network', 'log_archive'), os.path.join(DATA_BASE_PATH, 'process_arc', 'log_archive'), - os.path.join(DATA_BASE_PATH, 'libraries'), os.path.join(restart_base_path, 'r6', 'iteration_6', 'ARC', 'output'), os.path.join(restart_base_path, 'r6', 'iteration_6', 'ARC', 'log_and_restart_archive'), ]: From 9b2c07fdd4d94842df968d6493c616bfd34d8b27 Mon Sep 17 00:00:00 2001 From: Alon Grinberg Dana Date: Sun, 2 Jun 2024 05:38:41 +0300 Subject: [PATCH 8/9] Fix the ARCSpecies.include_in_thermo_lib arg assignment Assign always, not only if xyz is given --- t3/main.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/t3/main.py b/t3/main.py index d78032b7..28630e08 100755 --- a/t3/main.py +++ b/t3/main.py @@ -1055,21 +1055,22 @@ def trsh_rmg_tol(self, factor: float = 0.5): self.logger.info(f'Regenerating the RMG model with a tolerance move to core ' f'of {factor * core_tolerance[self.iteration]}.') - def species_requires_refinement(self, species: Optional[Species]) -> bool: + def species_requires_refinement(self, species: Optional[Union[Species, ARCSpecies]]) -> bool: """ Determine whether a species thermochemical properties should be calculated based on the data uncertainty. First check that this species was not previously considered. Args: - species (Species): The species for which the query is performed. + species (Union[Species, ARCSpecies]): The species for which the query is performed. Returns: bool: Whether the species thermochemical properties should be calculated. ``True`` if they should be. """ if species is None: return False - thermo_comment = species.thermo.comment.split('Solvation')[0] + thermo = species.thermo if isinstance(species, Species) else species.rmg_species.thermo + thermo_comment = thermo.comment.split('Solvation')[0] if (self.get_species_key(species=species) is None or self.species[self.get_species_key(species=species)]['converged'] is None) \ and ('group additivity' in thermo_comment or '+ radical(' in thermo_comment): @@ -1238,7 +1239,7 @@ def add_species(self, key = self.get_species_key(species=species) if key is None: key = len(list(self.species.keys())) - qm_species = get_species_with_qm_label(species=species, key=key) + qm_species = get_species_with_qm_label(species=species, key=key, arc_species=True) self.species[key] = {'RMG label': species.label, 'Chemkin label': species.to_chemkin(), 'QM label': qm_species.label, @@ -1264,15 +1265,15 @@ def add_species(self, xyzs.append(xyz_dict) if len(xyzs): if self.qm['adapter'] == 'ARC': - # Make qm_species and ARCSpecies instance to consider the xyz information + # Make qm_species an ARCSpecies instance to consider the xyz information qm_species = ARCSpecies(label=qm_species.label, - rmg_species=qm_species, + rmg_species=species, xyz=xyzs, - include_in_thermo_lib=self.species_requires_refinement(qm_species), ) else: raise NotImplementedError(f"Passing XYZ information to {self.qm['adapter']} " f"is not yet implemented.") + qm_species.include_in_thermo_lib = self.species_requires_refinement(qm_species) self.qm['species'].append(qm_species) return key @@ -1512,6 +1513,7 @@ def get_species_label_by_structure(adj: str, def get_species_with_qm_label(species: Species, key: int, + arc_species: bool = False, ) -> Species: """ Get a copy of the species with an updated QM label. @@ -1520,9 +1522,10 @@ def get_species_with_qm_label(species: Species, Args: species (Species): The species to consider. key (int): The respective species key, if exists. + arc_species (bool, optional): Whether to return an ARCSpecies object instance. Returns: - Species: A copy of the original species with a formatted QM species label. + Union[Species, ARCSpecies]: A copy of the original species with a formatted QM species label. Todo: Add tests. @@ -1530,4 +1533,8 @@ def get_species_with_qm_label(species: Species, qm_species = species.copy(deep=False) legalize_species_label(species=qm_species) qm_species.label = f's{key}_{qm_species.label}' + if isinstance(qm_species, Species) and arc_species: + qm_species = ARCSpecies(label=qm_species.label, + rmg_species=qm_species, + ) return qm_species From c43622e063f4e6ca5a0b8c814d1b339070d39597 Mon Sep 17 00:00:00 2001 From: Alon Grinberg Dana Date: Tue, 9 Jul 2024 20:49:10 +0300 Subject: [PATCH 9/9] Tests: Adaptations to main test --- tests/test_main.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/tests/test_main.py b/tests/test_main.py index 4d3b3211..d630f427 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -10,6 +10,7 @@ import shutil import re +from rmgpy.kinetics import Arrhenius from rmgpy.reaction import Reaction from rmgpy.rmg.pdep import PDepNetwork, PDepReaction from rmgpy.species import Species @@ -34,6 +35,7 @@ 'all_core_species': False, 'collision_violators_thermo': False, 'collision_violators_rates': False, + 'external_library_path': None, 'fit_missing_GAV': False, 'flux_adapter': 'RMG', 'library_name': 'T3lib', @@ -906,14 +908,21 @@ def test_add_reaction(): # check that reactant and product labels of an RMG reaction are set correctly when adding a reaction rmg_rxn_1 = Reaction(label='[N-]=[N+](N=O)[O] + HON <=> [O-][N+](=N)N=O + NO', - reactants=[Species(label='[N-]=[N+](N=O)[O]', smiles='[N-]=[N+](N=O)[O]'), - Species(label='HON', smiles='[N-]=[OH+]')], - products=[Species(label='[O-][N+](=N)N=O', smiles='[O-][N+](=N)N=O'), - Species(label='NO', smiles='[N]=O')]) + reactants=[Species(label='[N-]=[N+](N=O)[O]', smiles='[N-]=[N+](N=O)[O]', + thermo=ThermoData(comment='comment 1')), + Species(label='HON', smiles='[N-]=[OH+]', + thermo=ThermoData(comment='comment 2'))], + products=[Species(label='[O-][N+](=N)N=O', smiles='[O-][N+](=N)N=O', + thermo=ThermoData(comment='comment 3')), + Species(label='NO', smiles='[N]=O', + thermo=ThermoData(comment='comment 4'))], + kinetics=Arrhenius(A=(1, 'cm^3/(mol*s)'), n=0, Ea=(0, 'kJ/mol'), comment='kinetic comment 0')) t3.add_reaction(reaction=rmg_rxn_1, reasons='reason 4') assert t3.get_reaction_key(reaction=rmg_rxn_1) == 3 assert t3.reactions[3]['RMG label'] == 's9_N3O2 + s10_HON <=> s11_HN3O2 + s12_NO' - assert t3.reactions[3]['Chemkin label'] == '' + assert t3.reactions[3]['Chemkin label'] == '! kinetic comment 0\n' + \ + 'N3O2+HON<=>HN3O2+NO 1.000000e+00 0.000 ' + \ + '0.000 \n' assert t3.reactions[3]['QM label'] == 's9_N3O2 + s10_HON <=> s11_HN3O2 + s12_NO' assert t3.reactions[3]['SMILES label'] == '[N-]=[N+](N=O)[O] + [N-]=[OH+] <=> [O-][N+](=N)N=O + [N]=O' assert isinstance(t3.reactions[3]['object'], Reaction)