def build_reaction_name_dcts(mech1_str, mech2_str, t_ref, temps, pressures, ignore_reverse=True, remove_bad_fits=False): """ Parses the strings of two mechanism files and calculates rate constants [k(T,P)]s at an input set of temperatures and pressures. :param mech1_str: string of mechanism 1 input file :type mech1_str: str :param mech2_str: string of mechanism 2 input file :type mech2_str: str :param t_ref: Reference temperature (K) :type t_ref: float :param temps: List of Temperatures (K) :type temps: numpy.ndarray :return mech1_ktp_dct: rate constants for mechanism 1 :rtype: dict[pressure: rates] :return mech2_ktp_dct: rate constants for mechanism 2 :rtype: dict[pressure: rates] """ mech1_reaction_block = remove_whitespace( mech_parser.reaction_block(mech1_str)) mech1_units = reaction_units(mech1_str) mech1_ktp_dct = rates.mechanism(mech1_reaction_block, mech1_units, t_ref, temps, pressures, ignore_reverse=ignore_reverse, remove_bad_fits=remove_bad_fits) if mech2_str: mech2_reaction_block = remove_whitespace( mech_parser.reaction_block(mech2_str)) mech2_units = reaction_units(mech2_str) mech2_ktp_dct = rates.mechanism(mech2_reaction_block, mech2_units, t_ref, temps, pressures, ignore_reverse=ignore_reverse, remove_bad_fits=remove_bad_fits) else: mech2_ktp_dct = {} return mech1_ktp_dct, mech2_ktp_dct
def load_rxn_ktp_dcts_chemkin(mech_filenames, direc, temps, pressures): """ Read one or more Chemkin-formatted mechanisms files and calculate rates at the indicated pressures and temperatures. Return a list of rxn_ktp_dcts. :param mech_filenames: filenames containing Chemkin-formatted kinetics information :type mech_filenames: list [filename1, filename2, ...] :param direc: directory with file(s) (all files must be in the same directory) :type direc: str :param temps: temperatures at which to do calculations (Kelvin) :type temps: list [float] :param pressures: pressures at which to do calculations (atm) :type pressures: list [float] :return rxn_ktp_dcts: list of rxn_ktp_dcts :rtype: list of dcts [rxn_ktp_dct1, rxn_ktp_dct2, ...] """ rxn_ktp_dcts = [] for mech_filename in mech_filenames: mech_str = parser.read_file(direc, mech_filename) ea_units, a_units = parser_mech.reaction_units(mech_str) rxn_block_str = parser_mech.reaction_block(mech_str) rxn_param_dct = parser_rxn.param_dct(rxn_block_str, ea_units, a_units) rxn_ktp_dct = calc_rates.eval_rxn_param_dct(rxn_param_dct, pressures, temps) rxn_ktp_dcts.append(rxn_ktp_dct) return rxn_ktp_dcts
def build_reaction_name_dcts(mech1_str, mech2_str, t_ref, temps, pressures): """ builds the reaction dictionaries indexed by names """ mech1_reaction_block = chemkin_io.parser.util.clean_up_whitespace( chemkin_io.parser.mechanism.reaction_block(mech1_str)) mech1_units = reaction_units(mech1_str) mech1_ktp_dct = rates.mechanism(mech1_reaction_block, mech1_units, t_ref, temps, pressures) mech2_reaction_block = chemkin_io.parser.util.clean_up_whitespace( chemkin_io.parser.mechanism.reaction_block(mech2_str)) mech2_units = reaction_units(mech2_str) mech2_ktp_dct = rates.mechanism(mech2_reaction_block, mech2_units, t_ref, temps, pressures) return mech1_ktp_dct, mech2_ktp_dct
def parse_rxn_param_dct(mech_str): """ Parses a raw Chemkin mechanism string and yields a rxn_param_dct :param mech_str: raw string from reading a Chemkin file :type mech_str: str :return rxn_param_dct: rxn_param_dct object :rtype: dct {rxn1: param_tuple1, rxn2: ...} """ ea_units, a_units = parser_mech.reaction_units(mech_str) rxn_block_str = parser_mech.reaction_block(mech_str) rxn_param_dct = parser_rxn.get_rxn_param_dct(rxn_block_str, ea_units, a_units) return rxn_param_dct
def align_mechs(mech_filenames, thermo_filenames, spc_csv_filenames, temps, pressures, rev_rates=True, remove_loners=True, write_file=False, print_output=False): """ Takes any number of mechanisms and renames the species to all be the same, reverses any reactions to be in the same direction, and outputs a dictionary of all the reaction names and the corresponding k(T,P) dictionaries. :param mech_filenames: names of the Chemkin-formatted mechanism text files :type mech_filenames: list :param thermo_filenames: names of the Chemkin-formatted thermo text files; if the thermo info is contained in the mechanism.txt, just put the same filename here :type thermo_filenames: list :param spc_csv_filenames: names of the spc_csv filenames, which should have the following info: names, SMILES, inchi, multiplicity, charge, sensitivity (sensitivity not required) :type spc_csv_filenames: list :param temps: array of temperatures in Kelvin :type temps: Numpy 1-D array :param pressures: array of pressures in atm :type pressures: Numpy 1-D arrayi :param rev_rates: whether or not to reverse reactions; thermo filenames not required if False :type rev_rates: Bool :return aligned_rxn_ktp_dcts: dictionary of k(T,P) values for each mechanism, all under the same reaction name index :rtype: dict {((rcts), (prds)): [{ktp_dct1, ktp_dct2, ...], ...} :return aligned_rxn_em_dcts: dictionary of Boolean parameters indicating whether or not the rxn has a '+ M' term (i.e., third body) associated with it :rtype: dict {((rcts), (prds)): Boolean, ...} """ if rev_rates: assert len(mech_filenames) == len(spc_csv_filenames) == len( thermo_filenames ), (f"""The lengths of the mechanism, spc_csv, and thermo filename inputs should all be the same, but are instead {len(mech_filenames)}, {len(spc_csv_filenames)}, and {len(thermo_filenames)}, respectively.""") else: assert len(mech_filenames) == len(spc_csv_filenames), ( f"""The lengths of the mechanism and spc_csv inputs should all be the same, but are instead {len(mech_filenames)} and {len(spc_csv_filenames)}, respectively.""") num_mechs = len(mech_filenames) # Load rxn_ktp_dcts, spc_ident_dcts, and spc_thermo_dcts rxn_ktp_dcts = [] rxn_param_dcts = [] spc_ident_dcts = [] spc_thermo_dcts = [] for idx in range(num_mechs): # Get the rxn_ktp_dct and the rxn_param_dct mech_str = parser.ptt.read_inp_str(JOB_PATH, mech_filenames[idx], remove_comments=False) ea_units, a_units = parser_mech.reaction_units(mech_str) rxn_block_str = parser_mech.reaction_block(mech_str) rxn_param_dct = parser_rxn.param_dct(rxn_block_str, ea_units, a_units) rxn_ktp_dct = calc_rates.eval_rxn_param_dct(rxn_param_dct, pressures, temps) rxn_ktp_dcts.append(rxn_ktp_dct) rxn_param_dcts.append(rxn_param_dct) # Get the spc_dct spc_csv_str = parser.ptt.read_inp_str(JOB_PATH, spc_csv_filenames[idx], remove_comments=False) spc_dct = parser_spc.build_spc_dct(spc_csv_str, 'csv') spc_ident_dcts.append(spc_dct) # Get the thermo_dct if rev_rates: thermo_str = parser.ptt.read_inp_str(JOB_PATH, thermo_filenames[idx], remove_comments=False) thermo_block_str = parser_mech.thermo_block(thermo_str) spc_nasa7_dct = parser_thermo.create_spc_nasa7_dct( thermo_block_str) spc_thermo_dct = calc_thermo.create_spc_thermo_dct( spc_nasa7_dct, temps) spc_thermo_dcts.append(spc_thermo_dct) # Get the renamed dictionaries renamed_rxn_ktp_dcts, rename_instructions_lst = rename_dcts( rxn_ktp_dcts, 'rxn', spc_ident_dcts) renamed_rxn_param_dcts, _ = rename_dcts(rxn_param_dcts, 'rxn', spc_ident_dcts) if rev_rates: renamed_spc_thermo_dcts, _ = rename_dcts(spc_thermo_dcts, 'spc', spc_ident_dcts) # Get the em_param_dcts renamed_rxn_em_dcts = [] for dct in renamed_rxn_param_dcts: renamed_rxn_em_dct = get_rxn_em_dct(dct) renamed_rxn_em_dcts.append(renamed_rxn_em_dct) # Loop over each reaction and reverse if necessary # This creates the reversed dct, which has uniform species names and uniform reactant/product ordering reversed_rxn_ktp_dcts = copy.copy(renamed_rxn_ktp_dcts) reversed_rxn_em_dcts = copy.copy(renamed_rxn_em_dcts) for mech_idx in range(num_mechs - 1): for idx2 in range(mech_idx + 1, num_mechs): # If indicated, reverse the rxn_ktp_dcts and rxm_em_dcts if rev_rates: reversed_rxn_ktp_dct = reverse_rxn_ktp_dct( renamed_rxn_ktp_dcts[mech_idx], renamed_rxn_ktp_dcts[idx2], renamed_rxn_param_dcts[mech_idx], renamed_rxn_param_dcts[idx2], renamed_spc_thermo_dcts[mech_idx], temps, rev_rates) reversed_rxn_em_dct = reverse_rxn_em_dct( renamed_rxn_em_dcts[idx2], renamed_rxn_param_dcts[mech_idx], renamed_rxn_param_dcts[idx2], rev_rates) # Otherwise, just rename so the reactants and products are in the same order else: reversed_rxn_ktp_dct = reverse_rxn_ktp_dct( renamed_rxn_ktp_dcts[mech_idx], renamed_rxn_ktp_dcts[idx2], renamed_rxn_param_dcts[mech_idx], renamed_rxn_param_dcts[idx2], [], temps, rev_rates) reversed_rxn_em_dct = reverse_rxn_em_dct( renamed_rxn_em_dcts[idx2], renamed_rxn_param_dcts[mech_idx], renamed_rxn_param_dcts[idx2], rev_rates) reversed_rxn_ktp_dcts[idx2] = reversed_rxn_ktp_dct reversed_rxn_em_dcts[idx2] = reversed_rxn_em_dct # Sort the reversed dictionaries into a single output # Loop over each mechanism aligned_rxn_ktp_dct = {} aligned_rxn_em_dct = {} for mech_idx, dct in enumerate(reversed_rxn_ktp_dcts): for rxn, ktp_dct in dct.items(): # If the reaction does not yet exist, add it if rxn not in aligned_rxn_ktp_dct.keys(): ktp_dct_list = [ None ] * mech_idx # add empty entries to account for previous mechs that are missing ktp_dct_list.append(ktp_dct) aligned_rxn_ktp_dct[rxn] = ktp_dct_list try: aligned_rxn_em_dct[rxn] = reversed_rxn_em_dcts[mech_idx][ rxn] # add the em_param except KeyError: print( f'There was a key error in mech idx {mech_idx} for the rxn {rxn}' ) # If the reaction already exists, append the new ktp_dct else: ktp_dct_list = aligned_rxn_ktp_dct[ rxn] # get the current list of ktp_dcts # If any of the previous entries were blank, add None entries to fill if len(ktp_dct_list) < mech_idx: ktp_dct_list.extend([None] * (mech_idx - len(ktp_dct_list))) ktp_dct_list.append(ktp_dct) # Clean up the aligned dct for rxn, ktp_dct_list in aligned_rxn_ktp_dct.items(): # Add None entries so that all are the same length if len(ktp_dct_list) < num_mechs: ktp_dct_list.extend([None] * (num_mechs - len(ktp_dct_list))) aligned_rxn_ktp_dct[rxn] = ktp_dct_list if print_output: print('renamed_rxn_param_dcts', renamed_rxn_param_dcts) print('renamed_rxn_em_dcts', renamed_rxn_em_dcts) print('reversed_rxn_ktp_dcts', reversed_rxn_ktp_dcts) print('reversed_rxn_em_dcts', reversed_rxn_em_dcts) print('aligned_rxn_ktp_dct', aligned_rxn_ktp_dct) print('aligned_rxn_em_dct', aligned_rxn_em_dct) if write_file: with open("align_mechs_output.txt", "w") as f: for mech_idx, rename_instructions in enumerate( rename_instructions_lst): f.write( f"Rename instructions for converting mech {mech_idx+2} to mech {mech_idx+1}:\n" ) f.write( f"First column: mech {mech_idx+2} name, Second column: mech {mech_idx+1} name\n" ) for spc_name2, spc_name1 in rename_instructions.items(): f.write( ('{0:<1s}, {1:<5s}\n').format(spc_name2, spc_name1)) f.write("\n\n") # Write the aligned_rxn_ktp_dct f.write(f"Combined_rxn_ktp_dct\n\n") f.write( ('{0:<64s}{1:<15s}{2:<15s}').format('Rxn name', 'In mech 1?', 'In mech 2?')) for rxn, ktp_dct_lst in aligned_rxn_ktp_dct.items(): rxn_name = format_rxn_name(rxn, aligned_rxn_em_dct[rxn]) present = [] for entry in ktp_dct_lst: if entry: present.append('Yes') else: present.append('No') f.write(('\n{0:<64s}{1:<15s}{2:<15s}').format( rxn_name, present[0], present[1])) f.close() if remove_loners: aligned_rxn_ktp_dct, aligned_rxn_em_dct = remove_lone_reactions( aligned_rxn_ktp_dct, aligned_rxn_em_dct) return aligned_rxn_ktp_dct, aligned_rxn_em_dct
import numpy as np import sys import ioformat.pathtools as fileio from chemkin_io.parser import mechanism as parser_mech from chemkin_io.parser import reaction as parser_rxn from mechanalyzer.calculator import rates as calc_rates from mechanalyzer.builder import checker # INPUTS mech_filename = 'chomech_v11a_ascii_cleaned.inp' temps = np.linspace(300, 3000, 28) pressures = np.array([1, 10, 100]) k_thresholds = [1e11, 1e15, 1e22] rxn_num_threshold = 2 output_filename = 'mech_check.txt' # Load dcts JOB_PATH = sys.argv[1] mech_str = fileio.read_file(JOB_PATH, mech_filename) ea_units, a_units = parser_mech.reaction_units(mech_str) rxn_block_str = parser_mech.reaction_block(mech_str) RXN_PARAM_DCT = parser_rxn.param_dct(rxn_block_str, ea_units, a_units) RXN_KTP_DCT = calc_rates.eval_rxn_param_dct(RXN_PARAM_DCT, pressures, temps) output_str = checker.run_all_checks(RXN_PARAM_DCT, RXN_KTP_DCT, k_thresholds, rxn_num_threshold) fileio.write_file(output_str, JOB_PATH, output_filename)