def test_mechanism_simple_api_methods(self): try: xml = abspath(join('tests', 'test_mechanisms', 'h2-burke.xml')) sol = Solution(xml) m = ChemicalMechanismSpec.from_solution(sol) self.assertEqual(m.n_species, sol.n_species, 'ChemicalMechanismSpec.n_species vs ct.Solution.n_species') self.assertEqual(m.n_reactions, sol.n_reactions, 'ChemicalMechanismSpec.n_reactions vs ct.Solution.n_reactions') for i in range(m.n_species): self.assertEqual(m.species_names[i], sol.species_names[i], 'ChemicalMechanismSpec.species_name[i] vs ct.Solution.species_name[i]') for n in m.species_names: self.assertEqual(m.species_index(n), sol.species_index(n), 'ChemicalMechanismSpec.species_index(name) vs ct.Solution.species_index(name)') for i, n in enumerate(m.species_names): self.assertEqual(m.species_index(n), i, 'species names and indices are consistent, index vs i') self.assertEqual(n, m.species_names[i], 'species names and indices are consistent, name vs n') self.assertEqual(m.molecular_weight(i), m.molecular_weight(n), 'ChemicalMechanismSpec molecular_weight(name) vs molecular_weight(idx)') except: self.assertTrue(False) try: xml = abspath(join('tests', 'test_mechanisms', 'h2-burke.xml')) m = ChemicalMechanismSpec.from_solution(Solution(xml)) m.molecular_weight(list()) self.assertTrue(False) except: self.assertTrue(True)
def validate_on_mechanism(mech, temperature, pressure, tau, do_rhs, do_jac): xml = join(test_mech_directory, mech + '.xml') T = temperature Tin = T + 1000. p = pressure r = ChemicalMechanismSpec(xml, 'gas').griffon gas = Solution(xml) ns = gas.n_species y = np.ones(ns) # equal masses in the reactor gas.TPY = T, p, y y = np.copy(gas.Y) xin = np.ones(ns) # equal moles in the feed gas.TPX = Tin, p, xin yin = np.copy(gas.Y) state = hstack((T, y[:-1])) rhsCN = rhs_cantera(p, T, y, Tin, yin, tau, gas) rhsGR = np.empty(ns) r.reactor_rhs_isobaric(state, p, Tin, yin, tau, 0, 0, 0, 0, 0, 0, True, rhsGR) if do_rhs: return max(abs(rhsGR - rhsCN) / (abs(rhsCN) + 1.)) < 1.e-4 if do_jac: jacGR = np.empty(ns * ns) r.reactor_jac_isobaric(state, p, Tin, yin, tau, 0, 0, 0, 0, 0, 0, True, 0, 0, rhsGR, jacGR) jacGR = jacGR.reshape((ns, ns), order='F') dT = 1.e-6 dY = 1.e-6 jacFD = np.empty((ns, ns)) rhsGR1, rhsGR2 = np.empty(ns), np.empty(ns) state_m = hstack((T - dT, y[:-1])) state_p = hstack((T + dT, y[:-1])) r.reactor_rhs_isobaric(state_m, p, Tin, yin, tau, 0, 0, 0, 0, 0, 0, True, rhsGR1) r.reactor_rhs_isobaric(state_p, p, Tin, yin, tau, 0, 0, 0, 0, 0, 0, True, rhsGR2) jacFD[:, 0] = (- rhsGR1 + rhsGR2) / (2. * dT) for i in range(ns - 1): y_m1, y_p1 = np.copy(y), np.copy(y) y_m1[i] += - dY y_m1[-1] -= - dY y_p1[i] += dY y_p1[-1] -= dY state_m = hstack((T, y_m1[:-1])) state_p = hstack((T, y_p1[:-1])) r.reactor_rhs_isobaric(state_m, p, Tin, yin, tau, 0, 0, 0, 0, 0, 0, True, rhsGR1) r.reactor_rhs_isobaric(state_p, p, Tin, yin, tau, 0, 0, 0, 0, 0, 0, True, rhsGR2) jacFD[:, 1 + i] = (- rhsGR1 + rhsGR2) / (2. * dY) pass_jac = max(abs(jacGR - jacFD) / (abs(jacGR) + 1.)) < 1.e-2 return pass_jac
def new_result_dict(sln: ct.Solution, species_list=None): """ Returns an empty result dictionary used for storing state variables during kinetic simulations :param sln: ct.Solution object to be used in the simulation :param species_list: list of species for which to record mole fractions; default None to record all species in mechansim :return dict: result dictionary with empty lists in fields for time, T, P, and all species, along with a list of all included species and another of the species indices within the sln.X array """ # if the species list is None, make entries for all the species in the Solution object if species_list is None: species_list = sln.species_names else: # remove any species in the list not in the Solution to prevent errors species_list_2, species_list = list(species_list), [ ] # store provided species_list as species_list_2, make a # new species list for storing valid species for sp in species_list_2: if sp in sln.species_names and sp not in species_list: species_list += [sp] elif sp not in sln.species_names: print(f"{sp} not in mechanism; removed from tracked species.") # start initializing the dictionary with basic properties - time, temperature (T), pressure (P) result_dict = {'time': [], 'T': [], 'P': []} # now add entries for the species for sp in species_list: result_dict[sp] = [] # finally, add entries with a list of the species and their indices result_dict['species'] = species_list result_dict['indices'] = [sln.species_index(sp) for sp in species_list] return result_dict
def pymars(model_file, conditions, error, method, target_species, retained_species=None, run_sensitivity_analysis=False, epsilon_star=0.1): """Driver function for pyMARS to reduce a model. Parameters ---------- model_file : str Cantera-format model to be reduced (e.g., 'mech.cti'). conditions : str File with list of autoignition initial conditions. error : float Maximum error % for the reduced model. method : {'DRG', 'DRGEP', 'PFA'} Skeletal reduction method to use. target_species: list of str List of target species for reduction. retained_species : list of str, optional List of non-target species to always retain. run_sensitivity_analysis : bool, optional Flag to run sensitivity analysis after completing another method. epsilon_star : float, optional Epsilon^* value used to determine species for sensitivity analysis. Returns ------- Converted mechanism file Trimmed Solution Object Trimmed Mechanism file Examples -------- >>> pymars('gri30.cti', conditions_file, 10.0, 'DRGEP', ['CH4', 'O2'], retained_species=['N2']) """ solution_object = Solution(model_file) if method == 'DRG': results = run_drg(solution_object, conditions, error, target_species, retained_species) elif method == 'PFA': results = run_pfa(solution_object, conditions, error, target_species, retained_species) elif method == 'DRGEP': results = run_drgep(solution_object, conditions, error, target_species, retained_species) reduced_model, reduced_error = results if run_sensitivity_analysis: results = run_sa(solution_object, reduced_model, epsilon_star, conditions, error, retained_species) reduced_model, reduced_error = results sa_file = soln2cti.write(reduced_model)
def test_mechanism_serialization(self): xml = abspath(join('tests', 'test_mechanisms', 'h2-burke.xml')) sol = Solution(xml) m1 = ChemicalMechanismSpec.from_solution(sol) m1_pickle = pickle.dumps(m1) m2 = pickle.loads(m1_pickle) self.assertTrue(m1.mech_data['species'] == m2.mech_data['species']) self.assertTrue(m1.mech_data['reactions'] == m2.mech_data['reactions'])
def test_create_valid_mechanism_spec_from_solution(self): try: xml = abspath(join('tests', 'test_mechanisms', 'h2-burke.xml')) sol = Solution(xml) m = ChemicalMechanismSpec.from_solution(sol) g = m.griffon x = m.mech_xml_path n = m.group_name s = m.gas self.assertTrue(True) except: self.assertTrue(False)
def test_reflected_shock_frozen(): mechanism = 'gri30.cti' # define states initial_gas = Solution(mechanism) initial_gas.TPX = 300, 101325, {'H2': 1} working_gas = Solution(mechanism) working_gas.TPX = 300, 101325 * 2, {'H2': 1} velocity_guess = 7 # errors from hand calculations good_errors = [ -73.5, # enthalpy 101316.97487230592 # pressure ] test_errors = calculate_error.reflected_shock_frozen( working_gas=working_gas, post_shock_gas=initial_gas, shock_speed=velocity_guess) for test, good in zip(test_errors, good_errors): assert abs(test - good) / good < 1e-7
def test_equilibrium(): mechanism = 'gri30.cti' # define states initial_gas = Solution(mechanism) initial_gas.TPX = 300, 101325, {'H2': 1} working_gas = Solution(mechanism) working_gas.TPX = 300, 101325 * 2, {'H2': 1} velocity_guess = 7 # errors from hand calculations good_errors = [ -18.375000000, # enthalpy 101322.993718076, # pressure ] test_errors = calculate_error.equilibrium( working_gas=working_gas, initial_state_gas=initial_gas, initial_velocity_guess=velocity_guess) for test, good in zip(test_errors, good_errors): assert abs(test - good) / good < 1e-7
def test_no_convergence(): # ensure the proper warning is generated when solution doesn't converge mechanism = 'gri30.cti' initial_gas = Solution(mechanism) initial_gas.TPX = 300, 101325, {'H2': 1} working_gas = Solution(mechanism) working_gas.TPX = 300, 101325 * 2, {'H2': 1} with pytest.warns(Warning, match='No convergence within 1 iterations'): sd.Detonation.cj_state(working_gas, initial_gas, 1e-50, 1e-50, 1.5, 1)
def equilibrium_constants_expr(sol: ct.Solution, react: ct.Reaction, gibbs_rt): indices_reac = [sol.species_index(sp) for sp in react.reactants] indices_prod = [sol.species_index(sp) for sp in react.products] # Stoichiometric coefficients nu_reac = [react.reactants[sp] for sp in react.reactants] nu_prod = [react.products[sp] for sp in react.products] sum_r = sum(nu_reac_i * gibbs_rt[indices_reac_i] for indices_reac_i, nu_reac_i in zip(indices_reac, nu_reac)) sum_p = sum(nu_prod_i * gibbs_rt[indices_prod_i] for indices_prod_i, nu_prod_i in zip(indices_prod, nu_prod)) # Check if reaction is termolecular sum_nu_net = sum(nu_prod) - sum(nu_reac) if sum_nu_net < 0: # Three species on reactants side return sum_p + p.Variable("C0") - sum_r elif sum_nu_net > 0: # Three species on products side return sum_p - (sum_r + p.Variable("C0")) else: return sum_p - sum_r
def test_good_input(): # compare against SDToolbox results mechanism = 'gri30.cti' initial_gas = Solution(mechanism) initial_gas.TPX = 300, 101325, {'H2': 1} working_gas = Solution(mechanism) working_gas.TPX = 300, 101325 * 2, {'H2': 1} cj_calcs = sd.Detonation.cj_state( working_gas, initial_gas, 1e-5, 1e-5, 1.5 ) good_temp = 355.77590742266216 good_press = 180244.9690980063 good_species = {'H': 2.8407416566652653e-30, 'H2': 1.0} test_temp = cj_calcs[0].T check_temp = abs(test_temp - good_temp) / good_temp < 1e-7 test_press = cj_calcs[0].P check_press = abs(test_press - good_press) / good_press < 1e-7 good_velocity = 1700.3611387277992 test_velocity = cj_calcs[1] check_velocity = abs(test_velocity - good_velocity) / good_velocity \ < 1e-7 checks = [check_temp, check_press, check_velocity] # make sure the species in each solution are the same test_species = cj_calcs[0].mole_fraction_dict() for species in good_species: checks.append( good_species[species] - test_species[species] < 1e-7 ) assert all(checks)
def validate_on_mechanism(mech, temperature, pressure, full_Jacobian, isochoric): xml = join(test_mech_directory, mech + '.xml') r = ChemicalMechanismSpec(xml, 'gas').griffon gas = Solution(xml) ns = gas.n_species T = temperature p = pressure gas.TPX = T, p, ones(ns) y = gas.Y rho = gas.density_mass if full_Jacobian and isochoric: state = hstack((rho, T, y[:-1])) rhsGRTemporary = np.empty(ns + 1) jac_dense = np.empty((ns + 1) * (ns + 1)) jac_sparse = np.empty((ns + 1) * (ns + 1)) r.reactor_jac_isochoric(state, 0, 0, np.ndarray(1), 0, 0, 0, 0, 0, 0, 0, False, 0, rhsGRTemporary, jac_dense) r.reactor_jac_isochoric(state, 0, 0, np.ndarray(1), 0, 0, 0, 0, 0, 0, 0, False, 2, rhsGRTemporary, jac_sparse) jac_dense = jac_dense.reshape((ns + 1, ns + 1), order='F') jac_sparse = jac_sparse.reshape((ns + 1, ns + 1), order='F') elif full_Jacobian and not isochoric: state = hstack((T, y[:-1])) k = np.empty(ns) jac_dense = np.empty(ns * ns) jac_sparse = np.empty(ns * ns) r.reactor_jac_isobaric(state, p, 0, np.ndarray(1), 0, 0, 0, 0, 0, 0, 0, False, 0, 0, k, jac_dense) r.reactor_jac_isobaric(state, p, 0, np.ndarray(1), 0, 0, 0, 0, 0, 0, 0, False, 2, 0, k, jac_sparse) jac_dense = jac_dense.reshape((ns, ns), order='F') jac_sparse = jac_sparse.reshape((ns, ns), order='F') else: jac_dense = np.zeros((ns + 1) * (ns + 1)) jac_sparse = np.zeros((ns + 1) * (ns + 1)) r.prod_rates_primitive_sensitivities(rho, T, y, 0, jac_dense) r.prod_rates_primitive_sensitivities(rho, T, y, 2, jac_sparse) jac_dense = jac_dense.reshape((ns + 1, ns + 1), order='F')[:-1, :] jac_sparse = jac_sparse.reshape((ns + 1, ns + 1), order='F')[:-1, :] jac_fd = np.zeros_like(jac_dense) rhsGR1 = np.zeros(ns) rhsGR2 = np.zeros(ns) dT = 1.e-4 dr = 1.e-4 dY = 1.e-4 r.production_rates(T, rho + dr, y, rhsGR1) r.production_rates(T, rho - dr, y, rhsGR2) jac_fd[:, 0] = (rhsGR1 - rhsGR2) / dr * 0.5 r.production_rates(T + dT, rho, y, rhsGR1) r.production_rates(T - dT, rho, y, rhsGR2) jac_fd[:, 1] = (rhsGR1 - rhsGR2) / dT * 0.5 for spec_idx in range(ns - 1): Yp = np.copy(y) Yp[spec_idx] += dY Yp[-1] -= dY Ym = np.copy(y) Ym[spec_idx] -= dY Ym[-1] += dY r.production_rates(T, rho, Yp, rhsGR1) r.production_rates(T, rho, Ym, rhsGR2) jac_fd[:, 2 + spec_idx] = (rhsGR1 - rhsGR2) / dY * 0.5 pass_sparse_vs_dense_jac = np.linalg.norm( jac_dense.ravel() - jac_sparse.ravel(), ord=np.Inf) < 1.e-10 # if not pass_sparse_vs_dense_jac: # print(mech) # diff = abs(jac_dense - jac_sparse) / (abs(jac_dense) + 1.) # nr, nc = jac_dense.shape # print('dense') # for ir in range(nr): # for ic in range(nc): # print(f'{jac_dense[ir, ic]:8.0e}', end=', ') # print('') # print('sparse') # for ir in range(nr): # for ic in range(nc): # print(f'{jac_sparse[ir, ic]:8.0e}', end=', ') # print('') # print('finite difference') # for ir in range(nr): # for ic in range(nc): # print(f'{jac_fd[ir, ic]:8.0e}', end=', ') # print('') # print('diff') # for ir in range(nr): # for ic in range(nc): # print(f'{diff[ir, ic]:8.0e}' if diff[ir, ic] > 1.e-12 else f'{"":8}', end=', ') # print('') return pass_sparse_vs_dense_jac
def validate_on_mechanism(mech, temperature, pressure, test_rhs=True, test_jac=True): xml = join(test_mech_directory, mech + '.xml') T = temperature p = pressure r = ChemicalMechanismSpec(xml, 'gas').griffon gas = Solution(xml) ns = gas.n_species gas.TPX = T, p, ones(ns) y = gas.Y state = hstack((T, y[:-1])) rhsGR = np.empty(ns) r.reactor_rhs_isobaric(state, p, 0., np.ndarray(1), 0, 0, 0, 0, 0, 0, 0, False, rhsGR) if test_jac: Tin, yin, tau = 0, np.ndarray(1), 0 rhsTmp = np.empty(ns) jacGR = np.empty(ns * ns) r.reactor_jac_isobaric(state, p, Tin, yin, tau, 0, 0, 0, 0, 0, 0, False, 0, 0, rhsTmp, jacGR) jacGR = jacGR.reshape((ns, ns), order='F') dT = 1.e-6 dY = 1.e-6 jacFD = np.empty((ns, ns)) rhsGR1, rhsGR2 = np.empty(ns), np.empty(ns) state_m = hstack((T - dT, y[:-1])) state_p = hstack((T + dT, y[:-1])) r.reactor_rhs_isobaric(state_m, p, Tin, yin, tau, 0, 0, 0, 0, 0, 0, False, rhsGR1) r.reactor_rhs_isobaric(state_p, p, Tin, yin, tau, 0, 0, 0, 0, 0, 0, False, rhsGR2) jacFD[:, 0] = (- rhsGR1 + rhsGR2) / (2. * dT) for i in range(ns - 1): y_m1, y_p1 = np.copy(y), np.copy(y) y_m1[i] += - dY y_m1[-1] -= - dY y_p1[i] += dY y_p1[-1] -= dY state_m = hstack((T, y_m1[:-1])) state_p = hstack((T, y_p1[:-1])) r.reactor_rhs_isobaric(state_m, p, Tin, yin, tau, 0, 0, 0, 0, 0, 0, False, rhsGR1) r.reactor_rhs_isobaric(state_p, p, Tin, yin, tau, 0, 0, 0, 0, 0, 0, False, rhsGR2) jacFD[:, 1 + i] = (- rhsGR1 + rhsGR2) / (2. * dY) pass_jac = max(abs(jacGR - jacFD) / (abs(jacGR) + 1.)) < 1.e-2 w = gas.net_production_rates * gas.molecular_weights h = gas.standard_enthalpies_RT * gas.T * gas_constant / gas.molecular_weights rhsCN = zeros(ns) rhsCN[1:] = w[:-1] / gas.density rhsCN[0] = - sum(w * h) / gas.density / gas.cp_mass pass_rhs = max(abs(rhsGR - rhsCN) / (abs(rhsCN) + 1.)) < 100. * sqrt(np.finfo(float).eps) if test_rhs and test_jac: return pass_rhs and pass_jac if test_rhs: return pass_rhs if test_jac: return pass_jac
def get_kinetic_parameters_from_yaml( loaded_yaml: dict, gas: ct.Solution) -> Tuple[KineticsCoeffs, KineticsData]: """ Extract Arrhenius, Troe parameters, reaction type indices from loaded YAML file returns: tuple containing namedtuples of KineticCoeffs and KineticsData Adapted from https://github.com/DENG-MIT/reactorch/blob/master/reactorch/Solution.py """ reactions = defaultdict(list) efficiencies_coeffs = onp.ones((gas.n_species, gas.n_reactions)) reactant_stoich_coeffs = reactant_orders = gas.reactant_stoich_coeffs() arrhenius_coeffs = onp.zeros((gas.n_reactions, 3), dtype=onp.float64) arrhenius0_coeffs = onp.zeros((gas.n_reactions, 3), dtype=onp.float64) troe_coeffs = onp.zeros((gas.n_reactions, 4), dtype=onp.float64) is_reversible = onp.zeros(gas.n_reactions) three_body_indices, falloff_indices, troe_falloff_indices = list(), list( ), list() for i in range(gas.n_reactions): reactions[i] = {"equation": gas.reaction_equation(i)} reactions[i]["reactants"] = gas.reactants(i) reactions[i]["products"] = gas.products(i) reactions[i]["reaction_type"] = gas.reaction_type(i) if gas.is_reversible(i): is_reversible[i] = 1.0 if gas.reaction_type(i) in [1, 2]: reactions[i]["A"] = loaded_yaml["reactions"][i]["rate-constant"][ "A"] reactions[i]["b"] = loaded_yaml["reactions"][i]["rate-constant"][ "b"] if type(loaded_yaml["reactions"][i]["rate-constant"]["Ea"]) is str: Ea = onp.float64([ loaded_yaml["reactions"][i]["rate-constant"]["Ea"].split( " ")[0] ]) else: Ea = loaded_yaml["reactions"][i]["rate-constant"]["Ea"] reactions[i]["Ea"] = Ea if gas.reaction_type(i) in [2]: three_body_indices.append(i) if "efficiencies" in loaded_yaml["reactions"][i]: reactions[i]["efficiencies"] = loaded_yaml["reactions"][i][ "efficiencies"] for key, value in reactions[i]["efficiencies"].items(): efficiencies_coeffs[gas.species_index(key), i] = value if gas.reaction_type(i) in [4]: if "efficiencies" in loaded_yaml["reactions"][i]: reactions[i]["efficiencies"] = loaded_yaml["reactions"][i][ "efficiencies"] for key, value in reactions[i]["efficiencies"].items(): efficiencies_coeffs[gas.species_index(key), i] = value high_p = loaded_yaml["reactions"][i]["high-P-rate-constant"] low_p = loaded_yaml["reactions"][i]["low-P-rate-constant"] reactions[i]["A"] = high_p["A"] reactions[i]["b"] = high_p["b"] if type(high_p["Ea"]) is str: high_Ea = onp.float64([high_p["Ea"].split(" ")[0]]) else: high_Ea = high_p["Ea"] reactions[i]["Ea"] = high_Ea reactions[i]["A_0"] = low_p["A"] reactions[i]["b_0"] = low_p["b"] if type(low_p["Ea"]) is str: low_Ea = onp.float64([low_p["Ea"].split(" ")[0]]) else: low_Ea = low_p["Ea"] reactions[i]["Ea_0"] = low_Ea if "Troe" in loaded_yaml["reactions"][i]: troe_falloff_indices.append(i) Troe = loaded_yaml["reactions"][i]["Troe"] if "T2" in loaded_yaml["reactions"][i]["Troe"]: reactions[i]["Troe"] = { "A": Troe["A"], "T1": Troe["T1"], "T2": Troe["T2"], "T3": Troe["T3"], } troe_coeffs[i, 0] = Troe["A"] troe_coeffs[i, 1] = Troe["T1"] troe_coeffs[i, 2] = Troe["T2"] troe_coeffs[i, 3] = Troe["T3"] else: reactions[i]["Troe"] = { "A": Troe["A"], "T1": Troe["T1"], "T3": Troe["T3"], } troe_coeffs[i, 0] = Troe["A"] troe_coeffs[i, 1] = Troe["T1"] troe_coeffs[i, 2] = 0.0 troe_coeffs[i, 3] = Troe["T3"] else: falloff_indices.append(i) if "orders" in loaded_yaml["reactions"][i]: for key, value in loaded_yaml["reactions"][i]["orders"].items(): reactant_orders[gas.species_index(key), i] = value if "units" in loaded_yaml: if (loaded_yaml["units"]["length"] == "cm" and loaded_yaml["units"]["quantity"] == "mol"): reactions[i]["A"] *= (1e-3)**( reactant_stoich_coeffs[:, i].sum() - 1) if gas.reaction_type(i) in [2]: reactions[i]["A"] *= 1e-3 if gas.reaction_type(i) in [4]: reactions[i]["A_0"] *= 1e-3 reactions[i]["A_0"] *= (1e-3)**( reactant_stoich_coeffs[:, i].sum() - 1) arrhenius0_coeffs[i, 0] = reactions[i]["A_0"] arrhenius0_coeffs[i, 1] = reactions[i]["b_0"] arrhenius0_coeffs[i, 2] = reactions[i]["Ea_0"] arrhenius_coeffs[i, 0] = reactions[i]["A"] arrhenius_coeffs[i, 1] = reactions[i]["b"] arrhenius_coeffs[i, 2] = reactions[i]["Ea"] kinetics_coeffs = KineticsCoeffs( arrhenius_coeffs=np.array(arrhenius_coeffs, dtype=np.float64), arrhenius0_coeffs=np.array(arrhenius0_coeffs, dtype=np.float64), troe_coeffs=np.array(troe_coeffs, dtype=np.float64), efficiency_coeffs=np.array(efficiencies_coeffs, dtype=np.float64), ) kinetics_data = KineticsData( three_body_indices=np.array(three_body_indices, dtype=np.int64), falloff_indices=np.array(falloff_indices, dtype=np.int64), troe_falloff_indices=np.array(troe_falloff_indices, dtype=np.int64), is_reversible=np.array(is_reversible, dtype=np.int64), ) return kinetics_coeffs, kinetics_data
def _extract_cantera_mechanism_data(cls, ctsol: ct.Solution): ct_element_mw_map = cls._get_cantera_element_mw_map(ctsol) elem_list = ctsol.element_names ref_temperature = 298.15 ref_pressure = ctsol.reference_pressure spec_name_list = list() spec_dict = dict() reac_temporary_list = list() # todo: add error checking for i in range(ctsol.n_species): sp = ctsol.species(i) spec_name_list.append(sp.name) if isinstance(sp.thermo, ct.ConstantCp): spec_dict[sp.name] = dict({ 'atoms': sp.composition, 'heat-capacity': dict({ 'type': 'constant', 'Tmin': sp.thermo.min_temp, 'Tmax': sp.thermo.max_temp, 'T0': sp.thermo.coeffs[0], 'h0': sp.thermo.coeffs[1], 's0': sp.thermo.coeffs[2], 'cp': sp.thermo.coeffs[3] }) }) elif isinstance(sp.thermo, ct.NasaPoly2): spec_dict[sp.name] = dict({ 'atoms': sp.composition, 'heat-capacity': dict({ 'type': 'NASA7', 'Tmin': sp.thermo.min_temp, 'Tmid': sp.thermo.coeffs[0], 'Tmax': sp.thermo.max_temp, 'low-coeffs': sp.thermo.coeffs[8:], 'high-coeffs': sp.thermo.coeffs[1:8] }) }) for i in range(ctsol.n_reactions): rx = ctsol.reaction(i) if isinstance(rx, ct.FalloffReaction): f = rx.falloff if isinstance(f, ct.TroeFalloff): reac_temporary_list.append( (3, dict({ 'type': 'Troe', 'reversible': rx.reversible, 'reactants': rx.reactants, 'products': rx.products, 'default-eff': rx.default_efficiency, 'efficiencies': rx.efficiencies, 'fwd-A': rx.high_rate.pre_exponential_factor, 'fwd-b': rx.high_rate.temperature_exponent, 'fwd-Ea': rx.high_rate.activation_energy, 'flf-A': rx.low_rate.pre_exponential_factor, 'flf-b': rx.low_rate.temperature_exponent, 'flf-Ea': rx.low_rate.activation_energy, 'Troe-params': rx.falloff.parameters }))) if rx.orders: reac_temporary_list[-1][1]['orders'] = rx.orders else: reac_temporary_list.append( (2, dict({ 'type': 'Lindemann', 'reversible': rx.reversible, 'reactants': rx.reactants, 'products': rx.products, 'default-eff': rx.default_efficiency, 'efficiencies': rx.efficiencies, 'fwd-A': rx.high_rate.pre_exponential_factor, 'fwd-b': rx.high_rate.temperature_exponent, 'fwd-Ea': rx.high_rate.activation_energy, 'flf-A': rx.low_rate.pre_exponential_factor, 'flf-b': rx.low_rate.temperature_exponent, 'flf-Ea': rx.low_rate.activation_energy }))) if rx.orders: reac_temporary_list[-1][1]['orders'] = rx.orders elif isinstance(rx, ct.ThreeBodyReaction): reac_temporary_list.append((1, dict({ 'type': 'three-body', 'reversible': rx.reversible, 'reactants': rx.reactants, 'products': rx.products, 'default-eff': rx.default_efficiency, 'efficiencies': rx.efficiencies, 'A': rx.rate.pre_exponential_factor, 'b': rx.rate.temperature_exponent, 'Ea': rx.rate.activation_energy }))) if rx.orders: reac_temporary_list[-1][1]['orders'] = rx.orders elif isinstance(rx, ct.ElementaryReaction): reac_temporary_list.append((0, dict({ 'type': 'simple', 'reversible': rx.reversible, 'reactants': rx.reactants, 'products': rx.products, 'A': rx.rate.pre_exponential_factor, 'b': rx.rate.temperature_exponent, 'Ea': rx.rate.activation_energy }))) if rx.orders: reac_temporary_list[-1][1]['orders'] = rx.orders reac_list = [ y[1] for y in sorted(reac_temporary_list, key=lambda x: x[0]) ] return ct_element_mw_map, elem_list, ref_temperature, ref_pressure, spec_name_list, spec_dict, reac_list
def validate_on_mechanism(mech, temperature, pressure, tau, do_rhs, do_jac): xml = join(test_mech_directory, mech + '.xml') T = temperature Tin = T + 1000. p = pressure r = ChemicalMechanismSpec(xml, 'gas').griffon gas = Solution(xml) ns = gas.n_species y = np.ones(ns) # equal masses in the reactor gas.TPY = T, p, y y = np.copy(gas.Y) rho = gas.density_mass xin = np.ones(ns) # equal moles in the feed gas.TPX = Tin, p, xin yin = np.copy(gas.Y) rhoin = gas.density_mass state = hstack((rho, T, y[:-1])) rhsGRChemOnly = np.zeros(ns + 1) r.reactor_rhs_isochoric(state, rhoin, Tin, yin, tau, 0, 0, 0, 0, 0, 0, False, rhsGRChemOnly) rhsCN = rhs_cantera(p, T, y, rhoin, Tin, yin, tau, gas, rhsGRChemOnly) rhsGR = np.empty(ns + 1) r.reactor_rhs_isochoric(state, rhoin, Tin, yin, tau, 0, 0, 0, 0, 0, 0, True, rhsGR) if do_rhs: return max(abs(rhsGR - rhsCN) / (abs(rhsCN) + 1.)) < 100. * sqrt(np.finfo(float).eps) if do_jac: jacGR = np.empty((ns + 1) * (ns + 1)) r.reactor_jac_isochoric(state, rhoin, Tin, yin, tau, 0, 0, 0, 0, 0, 0, True, 0, rhsGR, jacGR) jacGR = jacGR.reshape((ns + 1, ns + 1), order='F') drho = 1.e-6 dT = 1.e-6 dY = 1.e-6 jacFD = np.empty((ns + 1, ns + 1)) rhsGR1, rhsGR2 = np.empty(ns + 1), np.empty(ns + 1) state_m = hstack((rho - drho, T, y[:-1])) state_p = hstack((rho + drho, T, y[:-1])) r.reactor_rhs_isochoric(state_m, rhoin, Tin, yin, tau, 0, 0, 0, 0, 0, 0, True, rhsGR1) r.reactor_rhs_isochoric(state_p, rhoin, Tin, yin, tau, 0, 0, 0, 0, 0, 0, True, rhsGR2) jacFD[:, 0] = (-rhsGR1 + rhsGR2) / (2. * drho) state_m = hstack((rho, T - dT, y[:-1])) state_p = hstack((rho, T + dT, y[:-1])) r.reactor_rhs_isochoric(state_m, rhoin, Tin, yin, tau, 0, 0, 0, 0, 0, 0, True, rhsGR1) r.reactor_rhs_isochoric(state_p, rhoin, Tin, yin, tau, 0, 0, 0, 0, 0, 0, True, rhsGR2) jacFD[:, 1] = (-rhsGR1 + rhsGR2) / (2. * dT) for i in range(ns - 1): y_m1, y_p1 = np.copy(y), np.copy(y) y_m1[i] += -dY y_m1[-1] -= -dY y_p1[i] += dY y_p1[-1] -= dY state_m = hstack((rho, T, y_m1[:-1])) state_p = hstack((rho, T, y_p1[:-1])) r.reactor_rhs_isochoric(state_m, rhoin, Tin, yin, tau, 0, 0, 0, 0, 0, 0, True, rhsGR1) r.reactor_rhs_isochoric(state_p, rhoin, Tin, yin, tau, 0, 0, 0, 0, 0, 0, True, rhsGR2) jacFD[:, 2 + i] = (-rhsGR1 + rhsGR2) / (2. * dY) return max(abs(jacGR - jacFD) / (abs(jacGR) + 1.)) < 1.e-4
def validate_on_mechanism(mech, temperature, pressure, test_rhs=True, test_jac=True): xml = join(test_mech_directory, mech + '.xml') r = ChemicalMechanismSpec(xml, 'gas').griffon gas = Solution(xml) ns = gas.n_species T = temperature p = pressure gas.TPX = T, p, ones(ns) y = gas.Y rho = gas.density_mass state = hstack((rho, T, y[:-1])) rhsGR = np.empty(ns + 1) rhsGRTemporary = np.empty(ns + 1) jacGR = np.empty((ns + 1) * (ns + 1)) r.reactor_rhs_isochoric(state, 0, 0, np.ndarray(1), 0, 0, 0, 0, 0, 0, 0, False, rhsGR) r.reactor_jac_isochoric(state, 0, 0, np.ndarray(1), 0, 0, 0, 0, 0, 0, 0, False, 0, rhsGRTemporary, jacGR) jacGR = jacGR.reshape((ns + 1, ns + 1), order='F') def cantera_rhs(rho_arg, T_arg, Y_arg): gas.TDY = T_arg, rho_arg, Y_arg w = gas.net_production_rates * gas.molecular_weights e = gas.standard_int_energies_RT * gas.T * gas_constant / gas.molecular_weights cv = gas.cv_mass rhs = zeros(ns + 1) rhs[0] = 0. rhs[1] = -sum(w * e) / (rho_arg * cv) rhs[2:] = w[:-1] / rho return rhs rhsCN = cantera_rhs(rho, T, y) if test_rhs: pass_rhs = max(abs(rhsGR - rhsCN) / (abs(rhsCN) + 1.)) < 100. * sqrt(np.finfo(float).eps) if test_jac: jacFD = zeros((ns + 1, ns + 1)) wm1 = zeros(ns + 1) wp1 = zeros(ns + 1) drho = 1.e-4 dT = 1.e-2 dY = 1.e-6 state_m = hstack((rho - drho, T, y[:-1])) state_p = hstack((rho + drho, T, y[:-1])) r.reactor_rhs_isochoric(state_m, 0, 0, np.ndarray(1), 0, 0, 0, 0, 0, 0, 0, False, wm1) r.reactor_rhs_isochoric(state_p, 0, 0, np.ndarray(1), 0, 0, 0, 0, 0, 0, 0, False, wp1) jacFD[:, 0] = (-wm1 + wp1) / (2. * drho) state_m = hstack((rho, T - dT, y[:-1])) state_p = hstack((rho, T + dT, y[:-1])) r.reactor_rhs_isochoric(state_m, 0, 0, np.ndarray(1), 0, 0, 0, 0, 0, 0, 0, False, wm1) r.reactor_rhs_isochoric(state_p, 0, 0, np.ndarray(1), 0, 0, 0, 0, 0, 0, 0, False, wp1) jacFD[:, 1] = (-wm1 + wp1) / (2. * dT) for i in range(ns - 1): y_m1, y_p1 = copy(y), copy(y) y_m1[i] += -dY y_m1[-1] -= -dY y_p1[i] += dY y_p1[-1] -= dY state_m = hstack((rho, T, y_m1[:-1])) state_p = hstack((rho, T, y_p1[:-1])) r.reactor_rhs_isochoric(state_m, 0, 0, np.ndarray(1), 0, 0, 0, 0, 0, 0, 0, False, wm1) r.reactor_rhs_isochoric(state_p, 0, 0, np.ndarray(1), 0, 0, 0, 0, 0, 0, 0, False, wp1) jacFD[:, 2 + i] = (-wm1 + wp1) / (2. * dY) gas.TDY = T, rho, y cv = gas.cv_mass cvi = gas.standard_cp_R * gas_constant / gas.molecular_weights w = gas.net_production_rates * gas.molecular_weights e = gas.standard_int_energies_RT * gas.T * gas_constant / gas.molecular_weights gas.TDY = T + dT, rho, y wp = gas.net_production_rates * gas.molecular_weights cvp = gas.cv_mass gas.TDY = T - dT, rho, y wm = gas.net_production_rates * gas.molecular_weights cvm = gas.cv_mass wsensT = (wp - wm) / (2. * dT) cvsensT = (cvp - cvm) / (2. * dT) jacFD11 = np.copy(jacFD[1, 1]) jacSemiFD11 = -1. / cv * (1. / rho * (sum(wsensT * e) + sum(cvi * w)) + cvsensT * rhsGR[1]) pass_jac = max(abs(jacGR - jacFD) / (abs(jacGR) + 1.)) < 1.e-4 if test_rhs: return pass_rhs if test_jac: if not pass_jac: print(jacGR[1, 1]) print(jacSemiFD11) print(jacFD11) print(abs(jacGR - jacFD) / (abs(jacGR) + 1.)) return pass_jac