def test_log_bde_report(self): """Test the log_bde_report() function""" path = os.path.join(arc_path, 'arc', 'testing', 'bde_report_test.txt') bde_report = { 'aniline': { (1, 2): 431.43, (5, 8): 465.36, (6, 9): 458.70, (3, 10): 463.16, (4, 11): 463.16, (7, 12): 458.70, (1, 13): 372.31, (1, 14): 372.31, (5, 6): 'N/A' } } xyz = """N 2.28116100 -0.20275000 -0.29653100 C 0.90749600 -0.08067400 -0.11852200 C 0.09862900 -1.21367300 -0.02143500 C 0.30223500 1.17638000 -0.08930600 C -1.87236600 0.16329100 0.13332800 C -1.27400900 -1.08769400 0.10342700 C -1.07133200 1.29144700 0.03586700 H -2.94554700 0.25749800 0.23136900 H -1.88237600 -1.98069300 0.17844600 H 0.55264300 -2.19782900 -0.04842100 H 0.91592000 2.06653500 -0.16951700 H -1.51965000 2.27721000 0.05753400 H 2.68270800 -1.06667200 0.02551200 H 2.82448700 0.59762700 -0.02174900""" aniline = ARCSpecies(label='aniline', xyz=xyz, smiles='c1ccc(cc1)N', bdes=['all_h', (1, 2), (5, 6)]) spc_dict = {'aniline': aniline} plotter.log_bde_report(path, bde_report, spc_dict) with open(path, 'r') as f: content = f.read() expected_content = """ BDE report for aniline: Pivots Atoms BDE (kJ/mol) -------- ----- ------------ (1, 13) N - H 372.31 (1, 14) N - H 372.31 (1, 2) N - C 431.43 (6, 9) C - H 458.70 (7, 12) C - H 458.70 (3, 10) C - H 463.16 (4, 11) C - H 463.16 (5, 8) C - H 465.36 (5, 6) C - C N/A """ self.assertEqual(content, expected_content)
def process_arc_project( thermo_adapter: str, kinetics_adapter: str, project: str, project_directory: str, species_dict: dict, reactions: list, output_dict: dict, bac_type: Optional[str] = None, sp_level: Optional[Level] = None, freq_scale_factor: float = 1.0, compute_thermo: bool = True, compute_rates: bool = True, compute_transport: bool = False, T_min: tuple = None, T_max: tuple = None, T_count: int = 50, lib_long_desc: str = '', rmg_database: Optional[RMGDatabase] = None, compare_to_rmg: bool = True, three_params: bool = True, ) -> None: """ Process an ARC project, generate thermo and rate coefficients using statistical mechanics (statmech). Args: thermo_adapter (str): The software to use for calculating thermodynamic data. kinetics_adapter (str): The software to use for calculating rate coefficients. project (str): The ARC project name. project_directory (str): The path to the ARC project directory. species_dict (dict): Keys are labels, values are ARCSpecies objects. reactions (list): Entries are ARCReaction objects. output_dict (dict): Keys are labels, values are output file paths. See Scheduler for a description of this dictionary. bac_type (str, optional): The bond additivity correction type. 'p' for Petersson- or 'm' for Melius-type BAC. ``None`` to not use BAC. sp_level (Level, optional): The level of theory used for energy corrections. freq_scale_factor (float, optional): The harmonic frequencies scaling factor. compute_thermo (bool, optional): Whether to compute thermodynamic properties for the provided species. compute_rates (bool, optional): Whether to compute high pressure limit rate coefficients. compute_transport (bool, optional): Whether to compute transport properties. 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'). T_count (int, optional): The number of temperature points between ``T_min`` and ``T_max``. lib_long_desc (str, optional): A multiline description of levels of theory for the resulting RMG libraries. rmg_database (RMGDatabase, optional): The RMG database object. compare_to_rmg (bool, optional): If ``True``, ARC's calculations will be compared against estimations from RMG's database. 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``). """ T_min = T_min or (300, 'K') T_max = T_max or (3000, 'K') if isinstance(T_min, (int, float)): T_min = (T_min, 'K') if isinstance(T_max, (int, float)): T_max = (T_max, 'K') T_count = T_count or 50 species_for_thermo_lib, unconverged_species = list(), list() rxns_for_kinetics_lib, unconverged_rxns = list(), list() species_for_transport_lib = list() bde_report = dict() output_directory = os.path.join(project_directory, 'output') libraries_path = os.path.join(output_directory, 'RMG libraries') if not os.path.isdir(output_directory): os.makedirs(output_directory) # guarantees that the adapters are supported: thermo_adapter_label = StatmechEnum(thermo_adapter) kinetics_adapter_label = StatmechEnum(kinetics_adapter) # 1. Rates if compute_rates: for reaction in reactions: species_converged = True considered_labels = list( ) # species labels considered in this reaction if output_dict[reaction.ts_label]['convergence']: for species in reaction.r_species + reaction.p_species: if species.label in considered_labels: # consider cases where the same species appears in a reaction both as a reactant # and as a product (e.g., H2O that catalyzes a reaction). continue considered_labels.append(species.label) if output_dict[species.label]['convergence']: statmech_adapter = statmech_factory( statmech_adapter_label=kinetics_adapter_label, output_directory=output_directory, output_dict=output_dict, bac_type=None, sp_level=sp_level, freq_scale_factor=freq_scale_factor, species=species, ) statmech_adapter.compute_thermo(kinetics_flag=True) else: logger.error( f'Species {species.label} did not converge, cannot compute a rate coefficient ' f'for {reaction.label}') unconverged_species.append(species) species_converged = False if species_converged: statmech_adapter = statmech_factory( statmech_adapter_label=kinetics_adapter_label, output_directory=output_directory, output_dict=output_dict, bac_type=None, sp_level=sp_level, freq_scale_factor=freq_scale_factor, reaction=reaction, species_dict=species_dict, T_min=T_min, T_max=T_max, T_count=T_count, three_params=three_params, ) statmech_adapter.compute_high_p_rate_coefficient() if reaction.kinetics is not None: rxns_for_kinetics_lib.append(reaction) else: unconverged_rxns.append(reaction) else: unconverged_rxns.append(reaction) if rxns_for_kinetics_lib: plotter.save_kinetics_lib(rxn_list=rxns_for_kinetics_lib, path=libraries_path, name=project, lib_long_desc=lib_long_desc) # 2. Thermo if compute_thermo: for species in species_dict.values(): if (species.compute_thermo or species.e0_only ) and output_dict[species.label]['convergence']: statmech_adapter = statmech_factory( statmech_adapter_label=thermo_adapter_label, output_directory=output_directory, output_dict=output_dict, bac_type=bac_type, sp_level=sp_level, freq_scale_factor=freq_scale_factor, species=species, ) statmech_adapter.compute_thermo(kinetics_flag=False, e0_only=species.e0_only) if species.thermo is not None: species_for_thermo_lib.append(species) elif not species.e0_only and species not in unconverged_species: unconverged_species.append(species) elif species.compute_thermo and not output_dict[species.label]['convergence'] \ and species not in unconverged_species: unconverged_species.append(species) if species_for_thermo_lib: plotter.save_thermo_lib(species_list=species_for_thermo_lib, path=libraries_path, name=project, lib_long_desc=lib_long_desc) # 3. Transport if compute_transport: for species in species_dict.values(): if output_dict[species.label]['job_types'][ 'onedmin'] and output_dict[species.label]['convergence']: pass # todo if species_for_transport_lib: plotter.save_transport_lib(species_list=species_for_thermo_lib, path=libraries_path, name=project, lib_long_desc=lib_long_desc) # 4. BDE for species in species_dict.values(): # looping again to make sure all relevant Species.e0 attributes were set if species.bdes is not None: bde_report[species.label] = process_bdes(label=species.label, species_dict=species_dict) if bde_report: bde_path = os.path.join(project_directory, 'output', 'BDE_report.txt') plotter.log_bde_report(path=bde_path, bde_report=bde_report, spc_dict=species_dict) # Comparisons if compare_to_rmg: try: load_rmg_database(rmg_database=rmg_database, species_dict=species_dict, output_dict=output_dict) except Exception as e: logger.error(f'Could not load the RMG database! Got:\n{e}') else: compare_thermo(species_for_thermo_lib=species_for_thermo_lib, rmg_database=rmg_database, output_directory=output_directory) compare_rates(rxns_for_kinetics_lib, rmg_database, output_directory=output_directory, T_min=T_min, T_max=T_max, T_count=T_count) compare_transport(species_for_transport_lib, rmg_database, output_directory=output_directory) write_unconverged_log(unconverged_species=unconverged_species, unconverged_rxns=unconverged_rxns, log_file_path=os.path.join( output_directory, 'unconverged_species.log')) clean_output_directory(project_directory)