From 1b190f7c20515c213410551cc212827dbd1a2fc7 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 22 Jan 2022 18:17:37 -0600 Subject: [PATCH] [UnitTests] Add unit tests for InterfaceKinetics Switch from self.gas to self.kin to accommodate non-gaseous phases --- .../cython/cantera/test/test_kinetics.py | 4 +- .../cython/cantera/test/test_reaction.py | 425 ++++++++++++++---- 2 files changed, 336 insertions(+), 93 deletions(-) diff --git a/interfaces/cython/cantera/test/test_kinetics.py b/interfaces/cython/cantera/test/test_kinetics.py index ac5d80e3719..a6b9d74035f 100644 --- a/interfaces/cython/cantera/test/test_kinetics.py +++ b/interfaces/cython/cantera/test/test_kinetics.py @@ -476,10 +476,10 @@ def test_sticking_coeff_err(self): "at T = 5000.0", "at T = 10000.0", "Sticking coefficient is greater than 1 for reaction", - "CanteraError thrown by InterfaceReaction::validate:", + "CanteraError thrown by InterfaceReaction2::validate:", ) if not ct.debug_mode_enabled(): - err_msg += ("CanteraError thrown by BlowersMaselInterfaceReaction::validate:",) + err_msg += ("CanteraError thrown by BlowersMaselInterfaceReaction2::validate:",) ct.make_warnings_fatal() for err in err_msg: diff --git a/interfaces/cython/cantera/test/test_reaction.py b/interfaces/cython/cantera/test/test_reaction.py index 8275be79080..02c7dcf3554 100644 --- a/interfaces/cython/cantera/test/test_reaction.py +++ b/interfaces/cython/cantera/test/test_reaction.py @@ -71,7 +71,7 @@ def test_duplicate(self): gas1.write_yaml(fname) with self.assertRaisesRegex(Exception, "Undeclared duplicate reactions"): - gas2 = ct.Solution(fname) + ct.Solution(fname) Path(fname).unlink() @@ -131,7 +131,7 @@ class ReactionRateTests: def setUpClass(cls): utilities.CanteraTest.setUpClass() ct.use_legacy_rate_constants(False) - cls.gas = ct.Solution("kineticsfromscratch.yaml") + cls.sol = ct.Solution("kineticsfromscratch.yaml") # suppress user warning (e.g. temperature derivative of Blowers-Masel) cls.warnings_suppressed = ct.warnings_suppressed() @@ -143,9 +143,9 @@ def tearDownClass(cls): ct.make_warnings_fatal() def setUp(self): - self.gas.X = "H2:0.1, H2O:0.2, O2:0.7, O:1e-4, OH:1e-5, H:2e-5, H2O2:1e-7" - self.gas.TP = 900, 2 * ct.one_atm - self.gas.Te = 2300 + self.sol.X = "H2:0.1, H2O:0.2, O2:0.7, O:1e-4, OH:1e-5, H:2e-5, H2O2:1e-7" + self.sol.TP = 900, 2 * ct.one_atm + self.sol.Te = 2300 def from_parts(self): # create reaction rate object from parts @@ -173,7 +173,7 @@ def from_dict(self, input=None): def eval(self, rate): # evaluate rate expression - return rate(self.gas.T) + return rate(self.sol.T) def check_rate(self, rate): # check rates @@ -181,7 +181,7 @@ def check_rate(self, rate): self.assertIn(self._cls.__name__, f"{rate}") value = self.eval(rate) self.assertIsFinite(value) - self.assertNear(value, self.gas.forward_rate_constants[self._index]) + self.assertNear(value, self.sol.forward_rate_constants[self._index]) def test_from_parts(self): # check constructors (from argument list) @@ -224,28 +224,28 @@ def test_with_units(self): def test_temperature_derivative(self): # check temperature derivative against numerical derivative - deltaT = self.gas.derivative_settings["rtol-delta"] - deltaT *= self.gas.T + deltaT = self.sol.derivative_settings["rtol-delta"] + deltaT *= self.sol.T rate = self.from_yaml() k0 = self.eval(rate) # derivative at constant pressure - dcdt = - self.gas.density_mole / self.gas.T - drate = self.gas.forward_rate_constants_ddT - drate += self.gas.forward_rate_constants_ddC * dcdt - self.gas.TP = self.gas.T + deltaT, self.gas.P + dcdt = - self.sol.density_mole / self.sol.T + drate = self.sol.forward_rate_constants_ddT + drate += self.sol.forward_rate_constants_ddC * dcdt + self.sol.TP = self.sol.T + deltaT, self.sol.P k1 = self.eval(rate) self.assertNear((k1 - k0) / deltaT, drate[self._index], 1e-6) def test_pressure_derivative(self): # check pressure derivative against numerical derivative - deltaP = self.gas.derivative_settings["rtol-delta"] - deltaP *= self.gas.P + deltaP = self.sol.derivative_settings["rtol-delta"] + deltaP *= self.sol.P rate = self.from_yaml() k0 = self.eval(rate) - drate = self.gas.forward_rate_constants_ddP - self.gas.TP = self.gas.T, self.gas.P + deltaP + drate = self.sol.forward_rate_constants_ddP + self.sol.TP = self.sol.T, self.sol.P + deltaP k1 = self.eval(rate) self.assertNear((k1 - k0) / deltaP, drate[self._index], 1e-6) @@ -288,7 +288,7 @@ def test_temperature_derivative_exact(self): # check exact derivative against analytical and numerical derivatives rate = self.from_parts() T = 1000. - self.gas.TP = T, self.gas.P + self.sol.TP = T, self.sol.P R = ct.gas_constant Ea = rate.activation_energy @@ -299,7 +299,7 @@ def test_temperature_derivative_exact(self): scaled_ddT = (Ea / R / T + b) / T - dkdT = self.gas.forward_rate_constants_ddT[self._index] + dkdT = self.sol.forward_rate_constants_ddT[self._index] self.assertNear(dkdT, k0 * scaled_ddT) # exact dT = 1.e-6 @@ -325,8 +325,8 @@ def setUpClass(cls): cls._parts = cls._input["rate-constant"] def eval(self, rate): - delta_enthalpy = self.gas.delta_enthalpy[self._index] - return rate(self.gas.T, delta_enthalpy) + delta_enthalpy = self.sol.delta_enthalpy[self._index] + return rate(self.sol.T, delta_enthalpy) def test_from_parts(self): rate = self.from_parts() @@ -367,7 +367,7 @@ def setUpClass(cls): def eval(self, rate): # check evaluation as a function of temperature and electron temperature - return rate(self.gas.T, self.gas.Te) + return rate(self.sol.T, self.sol.Te) def test_from_parts(self): rate = self.from_parts() @@ -398,8 +398,8 @@ def setUpClass(cls): cls._parts["high"] = ct.Arrhenius(param["A"], param["b"], param["Ea"]) def eval(self, rate): - concm = self.gas.third_body_concentrations[self._index] - return rate(self.gas.T, concm) + concm = self.sol.third_body_concentrations[self._index] + return rate(self.sol.T, concm) def test_data(self): rate = self.from_parts() @@ -407,37 +407,37 @@ def test_data(self): rate.falloff_coeffs = np.random.rand(n) def test_temperature_derivative(self): - pert = self.gas.derivative_settings["rtol-delta"] - deltaT = self.gas.T * pert - TP = self.gas.TP + pert = self.sol.derivative_settings["rtol-delta"] + deltaT = self.sol.T * pert + TP = self.sol.TP rate = self.from_yaml() k0 = self.eval(rate) # derivative at constant volume - drate = self.gas.forward_rate_constants_ddT - self.gas.TP = self.gas.T * (1 + pert), self.gas.P * (1 + pert) + drate = self.sol.forward_rate_constants_ddT + self.sol.TP = self.sol.T * (1 + pert), self.sol.P * (1 + pert) k1 = self.eval(rate) self.assertNear((k1 - k0) / deltaT, drate[self._index], 1e-6) # derivative at constant pressure - self.gas.TP = TP - dcdt = - self.gas.density_mole / self.gas.T - drate += self.gas.forward_rate_constants_ddC * dcdt - self.gas.TP = self.gas.T * (1 + pert), self.gas.P + self.sol.TP = TP + dcdt = - self.sol.density_mole / self.sol.T + drate += self.sol.forward_rate_constants_ddC * dcdt + self.sol.TP = self.sol.T * (1 + pert), self.sol.P k1 = self.eval(rate) self.assertNear((k1 - k0) / deltaT, drate[self._index], 1e-6) def test_pressure_derivative(self): - pert = self.gas.derivative_settings["rtol-delta"] - deltaP = self.gas.P * pert + pert = self.sol.derivative_settings["rtol-delta"] + deltaP = self.sol.P * pert rate = self.from_yaml() k0 = self.eval(rate) # derivative at constant temperature - drate = self.gas.forward_rate_constants_ddP - dcdp = self.gas.density_mole / self.gas.P - drate += self.gas.forward_rate_constants_ddC * dcdp - self.gas.TP = self.gas.T, self.gas.P + deltaP + drate = self.sol.forward_rate_constants_ddP + dcdp = self.sol.density_mole / self.sol.P + drate += self.sol.forward_rate_constants_ddC * dcdp + self.sol.TP = self.sol.T, self.sol.P + deltaP k1 = self.eval(rate) self.assertNear((k1 - k0) / deltaP, drate[self._index], 1e-6) @@ -559,7 +559,7 @@ def setUpClass(cls): def eval(self, rate): # check evaluation as a function of temperature and pressure - return rate(self.gas.T, self.gas.P) + return rate(self.sol.T, self.sol.P) def test_get_rates(self): # test getter for property rates @@ -644,7 +644,7 @@ def setUpClass(cls): def eval(self, rate): # check evaluation as a function of temperature and pressure - return rate(self.gas.T, self.gas.P) + return rate(self.sol.T, self.sol.P) def test_from_parts(self): rate = self.from_parts() @@ -657,6 +657,101 @@ def test_from_parts(self): self.assertTrue(np.all(self._parts["data"] == rate.data)) +class SurfaceReactionRateTests(ReactionRateTests): + # test suite for surface reaction rate expressions + + @classmethod + def setUpClass(cls): + utilities.CanteraTest.setUpClass() + ct.use_legacy_rate_constants(False) + cls.sol = ct.Interface("kineticsfromscratch.yaml", "Pt_surf", transport_model=None) + cls.gas = cls.sol.adjacent["ohmech"] + + # suppress user warning (e.g. temperature derivative of Blowers-Masel) + cls.warnings_suppressed = ct.warnings_suppressed() + ct.suppress_warnings() + + def setUp(self): + self.sol.TP = 900, ct.one_atm + self.gas.X = "H2:0.05, H2O:0.01, O:1e-4, OH: 1e5, H:2e-5, O2:0.21, AR:0.79" + self.gas.TP = 900, ct.one_atm + + def eval(self, rate): + # evaluate rate expression + return rate(self.sol.T, self.sol.coverages) + + @utilities.unittest.skip("temperature derivative is not implemented") + def test_temperature_derivative(self): + pass + + @utilities.unittest.skip("pressure derivative is not implemented") + def test_pressure_derivative(self): + pass + + +class TestSurfaceArrheniusRate(SurfaceReactionRateTests, utilities.CanteraTest): + # test surface-Arrhenius rate expressions without coverage dependency + + _cls = ct.SurfaceArrheniusRate + _type = "surface-Arrhenius" + _index = 0 + _input = {"rate-constant": {"A": 3.7e+20, "b": 0, "Ea": 1.15e7}} + _yaml = """ + rate-constant: {A: 3.7e+20, b: 0, Ea: 11500 J/mol} + type: surface-Arrhenius + """ + + @classmethod + def setUpClass(cls): + SurfaceReactionRateTests.setUpClass() + cls._parts = cls._input["rate-constant"] + + +class TestSurfaceArrheniusRate2(SurfaceReactionRateTests, utilities.CanteraTest): + # test surface-Arrhenius rate expressions without coverage dependency + + _cls = ct.SurfaceArrheniusRate + _type = "surface-Arrhenius" + _index = 1 + _input = {"rate-constant": {"A": 3.7e+20, "b": 0, "Ea": 213200000.}} + _coverage_deps = {"O(S)": {"a": 0.0, "m": 0.0, "E": -60000000.}} + _yaml = """ + rate-constant: {A: 3.7e+20, b: 0, Ea: 213200 J/mol} + coverage-dependencies: + O(S): {a: 0.0, m: 0.0, E: -6.0e+04 J/mol} + type: surface-Arrhenius + """ + + @classmethod + def setUpClass(cls): + SurfaceReactionRateTests.setUpClass() + cls._parts = cls._input["rate-constant"] + + def from_parts(self): + # create reaction rate object from parts + rr = self._cls(**self._parts) + rr.coverage_dependencies = self._coverage_deps + return rr + + +# class TestSurfaceStickingRate(SurfaceReactionRateTests, utilities.CanteraTest): +# # test surface-sticking rate expressions without coverage dependency + +# _cls = ct.SurfaceStickingRate +# _type = "surface-stick" +# _index = 2 +# _input = {"sticking-coefficient": {"A": 1., "b": 0, "Ea": 0}} +# _yaml = """ +# sticking-coefficient: {A: 1.0, b: 0, Ea: 0} +# type: surface-stick +# """ + +# @classmethod +# def setUpClass(cls): +# SurfaceReactionRateTests.setUpClass() +# cls._parts = cls._input["sticking-coefficient"] + + class ReactionTests: # test suite for reaction expressions @@ -676,53 +771,54 @@ class ReactionTests: @classmethod def setUpClass(cls): utilities.CanteraTest.setUpClass() - cls.gas = ct.Solution("kineticsfromscratch.yaml", transport_model=None) - cls.species = cls.gas.species() + cls.sol = ct.Solution("kineticsfromscratch.yaml", transport_model=None) + cls.species = cls.sol.species() def setUp(self): - self.gas.X = "H2:0.1, H2O:0.2, O2:0.7, O:1e-4, OH:1e-5, H:2e-5" - self.gas.TP = 900, 2*ct.one_atm + self.sol.X = "H2:0.1, H2O:0.2, O2:0.7, O:1e-4, OH:1e-5, H:2e-5" + self.sol.TP = 900, 2*ct.one_atm + self.adj = [] def eval_rate(self, rate): # evaluate rate expression - return rate(self.gas.T) + return rate(self.sol.T) def from_yaml(self, deprecated=False): # create reaction object from yaml if deprecated: with self.assertWarnsRegex(DeprecationWarning, "is renamed to 'from_yaml'"): - return ct.Reaction.fromYaml(self._yaml, kinetics=self.gas) - return ct.Reaction.from_yaml(self._yaml, kinetics=self.gas) + return ct.Reaction.fromYaml(self._yaml, kinetics=self.sol) + return ct.Reaction.from_yaml(self._yaml, kinetics=self.sol) def from_dict(self): # create reaction rate object from input data input_data = self.from_yaml().input_data - return ct.Reaction.from_dict(input_data, kinetics=self.gas) + return ct.Reaction.from_dict(input_data, kinetics=self.sol) def from_rate(self, rate): # create reaction object from keywords / rate - return self._cls(equation=self._equation, rate=rate, kinetics=self.gas, + return self._cls(equation=self._equation, rate=rate, kinetics=self.sol, legacy=self._legacy, **self._kwargs) def from_parts(self): # create reaction rate object from parts - orig = self.gas.reaction(self._index) + orig = self.sol.reaction(self._index) rxn = self._cls(orig.reactants, orig.products, legacy=self._legacy) rxn.rate = self._rate_obj return rxn def check_rate(self, rate_obj): if self._legacy: - rate = rate_obj(self.gas.T) + rate = rate_obj(self.sol.T) else: rate = self.eval_rate(rate_obj) - self.assertNear(rate, self.gas.forward_rate_constants[self._index]) + self.assertNear(rate, self.sol.forward_rate_constants[self._index]) def check_rxn(self, rxn, check_legacy=True): # helper function that checks reaction configuration ix = self._index - self.assertEqual(rxn.reactants, self.gas.reaction(ix).reactants) - self.assertEqual(rxn.products, self.gas.reaction(ix).products) + self.assertEqual(rxn.reactants, self.sol.reaction(ix).reactants) + self.assertEqual(rxn.products, self.sol.reaction(ix).products) if check_legacy: self.assertEqual(rxn.reaction_type, self._type) self.assertEqual(rxn.uses_legacy, self._type.endswith("-legacy")) @@ -731,20 +827,20 @@ def check_rxn(self, rxn, check_legacy=True): if not self._legacy: # legacy rate evaluation is not consistent self.check_rate(rxn.rate) - gas2 = ct.Solution(thermo="IdealGas", kinetics="GasKinetics", + kin2 = ct.Solution(thermo="IdealGas", kinetics="GasKinetics", species=self.species, reactions=[rxn]) - gas2.TPX = self.gas.TPX - self.check_solution(gas2, check_legacy) + kin2.TPX = self.sol.TPX + self.check_solution(kin2, check_legacy) - def check_solution(self, gas2, check_legacy=True): + def check_solution(self, kin2, check_legacy=True): # helper function that checks evaluation of reaction rates ix = self._index if check_legacy: - self.assertEqual(gas2.reaction_type_str(0), self._type) - self.assertNear(gas2.forward_rate_constants[0], - self.gas.forward_rate_constants[ix]) - self.assertNear(gas2.net_rates_of_progress[0], - self.gas.net_rates_of_progress[ix]) + self.assertEqual(kin2.reaction_type_str(0), self._type) + self.assertNear(kin2.forward_rate_constants[0], + self.sol.forward_rate_constants[ix]) + self.assertNear(kin2.net_rates_of_progress[0], + self.sol.net_rates_of_progress[ix]) def test_rate(self): # check consistency of reaction rate and forward rate constant @@ -782,12 +878,12 @@ def test_add_rxn(self): # check adding new reaction to solution if self._rate_obj is None: return - gas2 = ct.Solution(thermo="IdealGas", kinetics="GasKinetics", - species=self.species, reactions=[]) - gas2.TPX = self.gas.TPX + kin2 = ct.Solution(thermo=self.sol.thermo_model, kinetics=self.sol.kinetics_model, + species=self.species, reactions=[], adjacent=self.adj) + kin2.TPX = self.sol.TPX rxn = self.from_rate(self._rate_obj) - gas2.add_reaction(rxn) - self.check_solution(gas2) + kin2.add_reaction(rxn) + self.check_solution(kin2) def test_raises_invalid_rate(self): # check exception for instantiation from keywords / invalid rate @@ -806,22 +902,22 @@ def test_no_rate(self): return rxn = self.from_rate(None) if self._legacy: - self.assertNear(rxn.rate(self.gas.T), 0.) + self.assertNear(rxn.rate(self.sol.T), 0.) else: self.assertIsNaN(self.eval_rate(rxn.rate)) - gas2 = ct.Solution(thermo="IdealGas", kinetics="GasKinetics", - species=self.species, reactions=[rxn]) - gas2.TPX = self.gas.TPX + kin2 = ct.Solution(thermo=self.sol.thermo_model, kinetics=self.sol.kinetics_model, + species=self.species, reactions=[rxn], adjacent=self.adj) + kin2.TPX = self.sol.TPX if self._legacy: - self.assertNear(gas2.forward_rate_constants[0], 0.) - self.assertNear(gas2.net_rates_of_progress[0], 0.) + self.assertNear(kin2.forward_rate_constants[0], 0.) + self.assertNear(kin2.net_rates_of_progress[0], 0.) elif not ct.debug_mode_enabled(): - self.assertIsNaN(gas2.forward_rate_constants[0]) - self.assertIsNaN(gas2.net_rates_of_progress[0]) + self.assertIsNaN(kin2.forward_rate_constants[0]) + self.assertIsNaN(kin2.net_rates_of_progress[0]) else: with self.assertRaisesRegex(ct.CanteraError, "not finite"): - gas2.net_rates_of_progress + kin2.net_rates_of_progress def test_replace_rate(self): # check replacing reaction rate expression @@ -974,7 +1070,7 @@ def test_rate(self): def test_efficiencies(self): # check efficiencies - rxn = self._cls(equation=self._equation, rate=self._rate_obj, kinetics=self.gas, + rxn = self._cls(equation=self._equation, rate=self._rate_obj, kinetics=self.sol, legacy=self._legacy, **self._kwargs) self.assertEqual(rxn.efficiencies, self._kwargs["efficiencies"]) @@ -1037,7 +1133,7 @@ def setUpClass(cls): cls._rate_obj = ct.TwoTempPlasmaRate(**cls._rate) def eval_rate(self, rate): - return rate(self.gas.T, self.gas.Te) + return rate(self.sol.T, self.sol.Te) class TestBlowersMasel(ReactionTests, utilities.CanteraTest): @@ -1060,8 +1156,8 @@ def setUpClass(cls): cls._rate_obj = ct.BlowersMaselRate(**cls._rate) def eval_rate(self, rate): - delta_enthalpy = self.gas.delta_enthalpy[self._index] - return rate(self.gas.T, delta_enthalpy) + delta_enthalpy = self.sol.delta_enthalpy[self._index] + return rate(self.sol.T, delta_enthalpy) class TestTroe2(ReactionTests, utilities.CanteraTest): @@ -1130,8 +1226,8 @@ def setUpClass(cls): cls._rate_obj = ct.TroeRate(low=low, high=high, falloff_coeffs=data) def eval_rate(self, rate): - concm = self.gas.third_body_concentrations[self._index] - return rate(self.gas.T, concm) + concm = self.sol.third_body_concentrations[self._index] + return rate(self.sol.T, concm) def from_parts(self): rxn = ReactionTests.from_parts(self) @@ -1172,8 +1268,8 @@ def setUpClass(cls): cls._rate_obj = ct.LindemannRate(low=low, high=high, falloff_coeffs=[]) def eval_rate(self, rate): - concm = self.gas.third_body_concentrations[self._index] - return rate(self.gas.T, concm) + concm = self.sol.third_body_concentrations[self._index] + return rate(self.sol.T, concm) def from_parts(self): rxn = ReactionTests.from_parts(self) @@ -1237,8 +1333,8 @@ def setUpClass(cls): cls._rate_obj.chemically_activated = True def eval_rate(self, rate): - concm = self.gas.third_body_concentrations[self._index] - return rate(self.gas.T, concm) + concm = self.sol.third_body_concentrations[self._index] + return rate(self.sol.T, concm) class TestPlog2(ReactionTests, utilities.CanteraTest): @@ -1321,7 +1417,7 @@ class TestPlog(TestPlog2): """ def eval_rate(self, rate): - return rate(self.gas.T, self.gas.P) + return rate(self.sol.T, self.sol.P) class TestChebyshev2(ReactionTests, utilities.CanteraTest): @@ -1377,7 +1473,7 @@ class TestChebyshev(TestChebyshev2): """ def eval_rate(self, rate): - return rate(self.gas.T, self.gas.P) + return rate(self.sol.T, self.sol.P) class TestCustom(ReactionTests, utilities.CanteraTest): @@ -1418,9 +1514,156 @@ def test_rate_func(self): # check result of rate expression f = ct.Func1(self._rate) rate = ct.CustomRate(f) - self.assertNear(rate(self.gas.T), self.gas.forward_rate_constants[self._index]) + self.assertNear(rate(self.sol.T), self.sol.forward_rate_constants[self._index]) def test_custom_lambda(self): # check instantiation from keywords / rate provided as lambda function rxn = self.from_rate(lambda T: 38.7 * T**2.7 * exp(-3150.15428/T)) self.check_rxn(rxn) + + +class SurfaceReactionTests(ReactionTests): + # test suite for surface reaction expressions + + @classmethod + def setUpClass(cls): + utilities.CanteraTest.setUpClass() + cls.sol = ct.Interface("kineticsfromscratch.yaml", "Pt_surf", transport_model=None) + cls.gas = cls.sol.adjacent["ohmech"] + cls.adj = [cls.gas] + cls.species = cls.sol.species() + cls.concentrations = cls.sol.concentrations + + def setUp(self): + self.sol.TP = 900, ct.one_atm + self.gas.X = "H2:0.05, H2O:0.01, O:1e-4, OH: 1e5, H:2e-5, O2:0.21, AR:0.79" + self.gas.TP = 900, ct.one_atm + + def check_rxn(self, rxn, check_legacy=True): + # helper function that checks reaction configuration + ix = self._index + self.assertEqual(rxn.reactants, self.sol.reaction(ix).reactants) + self.assertEqual(rxn.products, self.sol.reaction(ix).products) + if check_legacy: + self.assertEqual(rxn.reaction_type, self._type) + #self.assertEqual(rxn.uses_legacy, self._type.endswith("-legacy")) + self.assertEqual(rxn.uses_legacy, self._legacy) + + if not self._legacy: + # legacy rate evaluation is not consistent + self.check_rate(rxn.rate) + kin2 = ct.Interface(thermo="Surface", kinetics="interface", + species=self.species, reactions=[rxn], adjacent=self.adj) + + kin2.site_density = self.sol.site_density + kin2.coverages = self.sol.coverages + kin2.TP = self.sol.TP + self.check_solution(kin2, check_legacy) + + +class TestInterfaceReaction(SurfaceReactionTests, utilities.CanteraTest): + # test legacy version of interface reaction + + _cls = ct.InterfaceReaction + _equation = "H(S) + O(S) <=> OH(S) + PT(S)" + _rate = {"A": 3.7e+20, "b": 0, "Ea": 1.15e7} + _index = 0 + _type = "interface" + _legacy = True + _yaml = """ + equation: H(S) + O(S) <=> OH(S) + PT(S) + rate-constant: {A: 3.7e+20, b: 0, Ea: 11500 J/mol} + """ + + +class TestSurfaceArrheniusReaction(SurfaceReactionTests, utilities.CanteraTest): + # test interface reaction without coverages + + _cls = ct.SurfaceArrheniusReaction + _equation = "H(S) + O(S) <=> OH(S) + PT(S)" + _rate = {"A": 3.7e+20, "b": 0, "Ea": 1.15e7} + _coverage_deps = None + _index = 0 + _type = "surface-Arrhenius" + _legacy = False + _yaml = """ + equation: H(S) + O(S) <=> OH(S) + PT(S) + rate-constant: {A: 3.7e+20, b: 0, Ea: 11500 J/mol} + type: surface-Arrhenius + """ + + @classmethod + def setUpClass(cls): + SurfaceReactionTests.setUpClass() + cls._rate_obj = ct.SurfaceArrheniusRate(**cls._rate) + if cls._coverage_deps: + cls._rate_obj.coverage_dependencies = cls._coverage_deps + + def eval_rate(self, rate): + return rate(self.sol.T, self.sol.coverages) + + +class TestCoverageArrheniusReaction(TestSurfaceArrheniusReaction): + # test interface reaction with coverages + + _equation = "2 O(S) => O2 + 2 PT(S)" + _rate = {"A": 3.7e+20, "b": 0, "Ea": 213200000.} + _index = 1 + _coverage_deps = {"O(S)": {"a": 0.0, "m": 0.0, "E": -60000000.}} + _yaml = """ + equation: 2 O(S) => O2 + 2 PT(S) + rate-constant: {A: 3.7e+21, b: 0, Ea: 213200} + coverage-dependencies: + O(S): {a: 0.0, m: 0.0, E: -6.0e+04} + units: {length: cm, quantity: mol, activation-energy: J/mol} + type: surface-Arrhenius + """ + + +# class TestStickingReaction(TestSurfaceArrheniusReaction): +# # test interface reaction with coverages + +# _cls = ct.StickingReaction +# _equation = "H + PT(S) => H(S)" +# _rate = {"A": 3.7e+20, "b": 0, "Ea": 213200000.} +# _index = 2 +# _type = "surface-stick" +# _yaml = """ +# equation: H + PT(S) => H(S) +# sticking-coefficient: {A: 1.0, b: 0, Ea: 0} +# units: {length: cm, quantity: mol, activation-energy: J/mol} +# type: surface-stick +# """ + +# @classmethod +# def setUpClass(cls): +# SurfaceReactionTests.setUpClass() +# cls._rate_obj = ct.SurfaceStickingRate(**cls._rate) +# if cls._coverage_deps: +# cls._rate_obj.coverage_dependencies = cls._coverage_deps + + +# - equation: H2 + 2 PT(S) => 2 H(S) +# sticking-coefficient: {A: 0.046, b: 0, Ea: 0} +# coverage-dependencies: +# PT(S): {a: 0.0, m: -1.0, E: 0.0} +# units: {length: cm, quantity: mol, activation-energy: J/mol} +# note: Reaction 1 from methane_pox_on_pt.yaml +# - equation: OH + PT(S) => OH(S) +# sticking-coefficient: {A: 1.0, b: 0, Ea: 0} +# Motz-Wise: true +# units: {length: cm, quantity: mol, activation-energy: J/mol} +# note: Reaction 10 from ptcombust-motzwise.yaml +# - equation: 2 H(S) => H2 + 2 PT(S) +# type: Blowers-Masel +# rate-constant: {A: 3.7e+21, b: 0, Ea0: 67400, w: 1000000} +# coverage-dependencies: +# H(S): {a: 0.0, m: 0.0, E: -6000.0} +# units: {length: cm, quantity: mol, activation-energy: J/mol} +# note: Reaction 1 from BM-ptcombust-Motz-Wise.yaml +# - equation: OH + PT(S) => OH(S) +# type: Blowers-Masel +# sticking-coefficient: {A: 1.0, b: 0, Ea0: 0, w: 100000} +# Motz-Wise: true +# units: {length: cm, quantity: mol, activation-energy: J/mol} +# note: Reaction 5 from BM-ptcombust-Motz-Wise.yaml