From 3a300b4611569d5db34cc93fb8aa0a8ef80b5cfa Mon Sep 17 00:00:00 2001 From: Christopher Neal Date: Thu, 17 Oct 2024 03:13:33 -0400 Subject: [PATCH] [test] addressing review comments --- test/python/test_composite.py | 9 +- test/python/test_convert.py | 71 ++++---- test/python/test_jacobian.py | 306 +++++++++++++--------------------- test/python/test_kinetics.py | 181 +++++++++----------- test/python/test_mixture.py | 5 +- test/python/test_onedim.py | 82 ++++----- test/python/test_purefluid.py | 21 +-- test/python/test_reaction.py | 149 ++++++++--------- test/python/test_reactor.py | 73 ++++---- test/python/test_thermo.py | 1 + test/python/test_transport.py | 20 +-- test/python/utilities.py | 3 +- 12 files changed, 382 insertions(+), 539 deletions(-) diff --git a/test/python/test_composite.py b/test/python/test_composite.py index 53111bb838..d1562ded28 100644 --- a/test/python/test_composite.py +++ b/test/python/test_composite.py @@ -791,7 +791,7 @@ def test_legacy_hdf_multidim(self, test_data_path): @pytest.mark.skipif("native" not in ct.hdf_support(), reason="Cantera compiled without HDF support") @pytest.mark.filterwarnings("ignore:.*legacy HDF.*:UserWarning") - def test_legacy_hdf(self, test_data_path, legacy=False): + def test_legacy_hdf(self, test_data_path): # recreate states used to create legacy HDF file (valid portion) extra = {'foo': range(7), 'bar': range(7)} meta = {'spam': 'eggs', 'hello': 'world'} @@ -814,7 +814,7 @@ def test_legacy_hdf(self, test_data_path, legacy=False): @pytest.mark.skipif("native" not in ct.hdf_support(), reason="Cantera compiled without HDF support") @pytest.mark.filterwarnings("ignore:.*legacy HDF.*:UserWarning") - def test_read_legacy_hdf_no_norm(self, test_data_path, legacy=False): + def test_read_legacy_hdf_no_norm(self, test_data_path): # recreate states used to create legacy HDF file self.gas.set_unnormalized_mole_fractions(np.full(self.gas.n_species, 0.3)) states = ct.SolutionArray(self.gas, 5) @@ -822,10 +822,7 @@ def test_read_legacy_hdf_no_norm(self, test_data_path, legacy=False): infile = test_data_path / "solutionarray_no_norm_legacy.h5" b = ct.SolutionArray(self.gas) - if legacy: - b.read_hdf(infile, normalize=False) - else: - b.restore(infile, "group0") + b.restore(infile, "group0") assert states.T == approx(b.T, rel=1e-7) assert states.P == approx(b.P, rel=1e-7) assert states.X == approx(b.X, rel=1e-7) diff --git a/test/python/test_convert.py b/test/python/test_convert.py index 7385d9cd5f..2998987f8c 100644 --- a/test/python/test_convert.py +++ b/test/python/test_convert.py @@ -54,7 +54,7 @@ def checkConversion(self, refFile, testFile): for i in range(ref.n_species)] compositionB = [[gas.n_atoms(i,j) for j in range(gas.n_elements)] for i in range(gas.n_species)] - assert compositionA, compositionB + assert compositionA == compositionB return ref, gas @@ -70,9 +70,9 @@ def checkThermo(self, ref, gas, temperatures): gas_s = gas.standard_entropies_R for i in range(gas.n_species): message = ' for species {0} at T = {1}'.format(i, T) - assert ref_cp[i] == approx(gas_cp[i], rel=1e-7) - assert ref_h[i] == approx(gas_h[i], rel=1e-7) - assert ref_s[i] == approx(gas_s[i], rel=1e-7) + assert ref_cp[i] == approx(gas_cp[i], rel=1e-7), 'cp' + message + assert ref_h[i] == approx(gas_h[i], rel=1e-7), 'h' + message + assert ref_s[i] == approx(gas_s[i], rel=1e-7), 's' + message def checkKinetics(self, ref, gas, temperatures, pressures, tol=1e-8): for T,P in itertools.product(temperatures, pressures): @@ -84,8 +84,8 @@ def checkKinetics(self, ref, gas, temperatures, pressures, tol=1e-8): gas_kr = gas.reverse_rate_constants for i in range(gas.n_reactions): message = ' for reaction {0} at T = {1}, P = {2}'.format(i, T, P) - assert ref_kf[i] == approx(gas_kf[i], rel=tol) - assert ref_kr[i] == approx(gas_kr[i], rel=tol) + assert ref_kf[i] == approx(gas_kf[i], rel=tol), 'kf' + message + assert ref_kr[i] == approx(gas_kr[i], rel=tol), 'kr' + message @pytest.mark.slow_test def test_gri30(self): @@ -228,15 +228,15 @@ def test_pathologicalSpeciesNames(self): assert list(nu[:,1]) == [-2, 3, 0, -1, 0, 0, 0, 0, 0, 0] assert list(nu[:,2]) == [-1, 0, 0, 0, 1, 0, 0, 0, 0, 0] assert list(nu[:,3]) == [3, 0, 0, 0, -2, -1, 0, 0, 0, 0] - assert list(nu[:,4])== [2, 0, 0, 0, -1, 0, -1, 0, 0, 0] - assert list(nu[:,5])== [1, 0, 0, 0, 1, -1, -1, 0, 0, 0] - assert list(nu[:,6])== [2, 0, -1, 0, 0, -1, 0, 0, 0, 0] - assert list(nu[:,7])== [0, 0, 0, 0, -1, 1, 0, 0, 0, 0] - assert list(nu[:,8])== [0, 0, 0, 0, -1, 1, 0, 0, 0, 0] - assert list(nu[:,9])== [0, 0, 0, 0, -1, 1, 0, 0, 0, 0] - assert list(nu[:,10])== [0, 0, -1, 0, 2, 0, 0, -1, 0, 0] - assert list(nu[:,11])== [0, 0, -1, 0, 2, 0, 0, 0, -1, 0] - assert list(nu[:,12])== [0, 0, 0, 0, 1, 0, 0, 0, 0, -1] + assert list(nu[:,4]) == [2, 0, 0, 0, -1, 0, -1, 0, 0, 0] + assert list(nu[:,5]) == [1, 0, 0, 0, 1, -1, -1, 0, 0, 0] + assert list(nu[:,6]) == [2, 0, -1, 0, 0, -1, 0, 0, 0, 0] + assert list(nu[:,7]) == [0, 0, 0, 0, -1, 1, 0, 0, 0, 0] + assert list(nu[:,8]) == [0, 0, 0, 0, -1, 1, 0, 0, 0, 0] + assert list(nu[:,9]) == [0, 0, 0, 0, -1, 1, 0, 0, 0, 0] + assert list(nu[:,10]) == [0, 0, -1, 0, 2, 0, 0, -1, 0, 0] + assert list(nu[:,11]) == [0, 0, -1, 0, 2, 0, 0, 0, -1, 0] + assert list(nu[:,12]) == [0, 0, 0, 0, 1, 0, 0, 0, 0, -1] def test_unterminatedSections(self): output = self.convert('unterminated-sections.inp', quiet=False) @@ -320,7 +320,7 @@ def test_explicit_reverse_rate(self): # two irreversible reactions with reactants and products swapped, unless # the explicit reverse rate is zero so only the forward reaction is used. Rr = gas.reverse_rate_constants - assert Rr[0] == 0.0 + assert Rr[0] == 0.0 assert Rr[1] == 0.0 assert Rr[2] == 0.0 assert Rr[3] == 0.0 @@ -891,9 +891,9 @@ def check_thermo(self, ck_phase, yaml_phase, temperatures, tol=1e-7): assert ck_phase.density == approx(yaml_phase.density) for i in range(ck_phase.n_species): message = ' for species {0} at T = {1}'.format(i, T) - assert cp_ck[i] == approx(cp_yaml[yaml_idx[i]], rel=tol) - assert h_ck[i] == approx(h_yaml[yaml_idx[i]], rel=tol) - assert s_ck[i] == approx(s_yaml[yaml_idx[i]], rel=tol) + assert cp_ck[i] == approx(cp_yaml[yaml_idx[i]], rel=tol), 'cp' + message + assert h_ck[i] == approx(h_yaml[yaml_idx[i]], rel=tol), 'h' + message + assert s_ck[i] == approx(s_yaml[yaml_idx[i]], rel=tol), 's' + message def check_kinetics(self, ck_phase, yaml_phase, temperatures, pressures, tol=1e-7): for T, P in itertools.product(temperatures, pressures): @@ -905,8 +905,8 @@ def check_kinetics(self, ck_phase, yaml_phase, temperatures, pressures, tol=1e-7 kr_yaml = yaml_phase.reverse_rate_constants for i in range(yaml_phase.n_reactions): message = f"for reaction {i+1}: {yaml_phase.reaction(i)} at T = {T}, P = {P}" - assert kf_ck[i] == approx(kf_yaml[i], rel=tol) - assert kr_ck[i] == approx(kr_yaml[i], rel=tol) + assert kf_ck[i] == approx(kf_yaml[i], rel=tol), 'kf ' + message + assert kr_ck[i] == approx(kr_yaml[i], rel=tol), 'kr ' + message def check_transport(self, ck_phase, yaml_phase, temperatures, model="mixture-averaged"): yaml_idx = {ck_phase.species_index(s): yaml_phase.species_index(s) for s in ck_phase.species_names} @@ -921,7 +921,7 @@ def check_transport(self, ck_phase, yaml_phase, temperatures, model="mixture-ave Dkm_yaml = yaml_phase.mix_diff_coeffs for i in range(ck_phase.n_species): message = 'dkm for species {0} at T = {1}'.format(i, T) - assert Dkm_ck[i] == approx(Dkm_yaml[yaml_idx[i]]) + assert Dkm_ck[i] == approx(Dkm_yaml[yaml_idx[i]]), message @pytest.mark.slow_test def test_gri30(self, cantera_data_path): @@ -1139,9 +1139,9 @@ def checkThermo(self, ctiPhase, yamlPhase, temperatures, tol=1e-7, check_cp=True for i in range(ctiPhase.n_species): message = ' for species {0} at T = {1}'.format(i, T) if check_cp: - cp_cti[i] == approx(cp_yaml[i], rel=tol) - assert h_cti[i] == approx(h_yaml[i], rel=tol) - assert s_cti[i] == approx(s_yaml[i], rel=tol) + cp_cti[i] == approx(cp_yaml[i], rel=tol), 'cp' + message + assert h_cti[i] == approx(h_yaml[i], rel=tol), 'h' + message + assert s_cti[i] == approx(s_yaml[i], rel=tol), 's' + message def checkKinetics(self, ctiPhase, yamlPhase, temperatures, pressures, tol=1e-7): for T,P in itertools.product(temperatures, pressures): @@ -1153,8 +1153,8 @@ def checkKinetics(self, ctiPhase, yamlPhase, temperatures, pressures, tol=1e-7): kr_yaml = yamlPhase.reverse_rate_constants for i in range(yamlPhase.n_reactions): message = ' for reaction {0} at T = {1}, P = {2}'.format(i, T, P) - assert kf_cti[i] == approx(kf_yaml[i], rel=tol) - assert kr_cti[i] == approx(kr_yaml[i], rel=tol) + assert kf_cti[i] == approx(kf_yaml[i], rel=tol), 'kf ' + message + assert kr_cti[i] == approx(kr_yaml[i], rel=tol), 'kr ' + message def checkTransport(self, ctiPhase, yamlPhase, temperatures, model='mixture-averaged'): @@ -1169,7 +1169,7 @@ def checkTransport(self, ctiPhase, yamlPhase, temperatures, Dkm_yaml = yamlPhase.mix_diff_coeffs for i in range(ctiPhase.n_species): message = 'dkm for species {0} at T = {1}'.format(i, T) - assert Dkm_cti[i] == approx(Dkm_yaml[i]) + assert Dkm_cti[i] == approx(Dkm_yaml[i]), message @pytest.mark.slow_test def test_gri30(self): @@ -1343,7 +1343,8 @@ def checkConversion(self, basename, cls=ct.Solution, ctmlphases=(), assert C.duplicate == Y.duplicate for i, sp in zip(range(ctmlPhase.n_reactions), ctmlPhase.kinetics_species_names): - assert ctmlPhase.reactant_stoich_coeff(sp, i) == yamlPhase.reactant_stoich_coeff(sp, i) + assert (ctmlPhase.reactant_stoich_coeff(sp, i) + == yamlPhase.reactant_stoich_coeff(sp, i)) return ctmlPhase, yamlPhase @@ -1366,9 +1367,9 @@ def checkThermo(self, ctmlPhase, yamlPhase, temperatures, pressure=ct.one_atm, for i in range(ctmlPhase.n_species): message = ' for species {0} at T = {1}'.format(ctmlPhase.species_names[i], T) if check_cp: - assert cp_ctml[i] == approx(cp_yaml[i], rel=tol) - assert h_ctml[i] == approx(h_yaml[i], rel=tol) - assert s_ctml[i] == approx(s_yaml[i], rel=tol) + assert cp_ctml[i] == approx(cp_yaml[i], rel=tol), 'cp' + message + assert h_ctml[i] == approx(h_yaml[i], rel=tol), 'h' + message + assert s_ctml[i] == approx(s_yaml[i], rel=tol), 's' + message def checkKinetics(self, ctmlPhase, yamlPhase, temperatures, pressures, tol=1e-7): for T,P in itertools.product(temperatures, pressures): @@ -1380,8 +1381,8 @@ def checkKinetics(self, ctmlPhase, yamlPhase, temperatures, pressures, tol=1e-7) kr_yaml = yamlPhase.reverse_rate_constants for i in range(yamlPhase.n_reactions): message = ' for reaction {0} at T = {1}, P = {2}'.format(i, T, P) - assert kf_ctml[i] == approx(kf_yaml[i], rel=tol) - assert kr_ctml[i] == approx(kr_yaml[i], rel=tol) + assert kf_ctml[i] == approx(kf_yaml[i], rel=tol), 'kf ' + message + assert kr_ctml[i] == approx(kr_yaml[i], rel=tol), 'kr ' + message def checkTransport(self, ctmlPhase, yamlPhase, temperatures, model='mixture-averaged'): @@ -1396,7 +1397,7 @@ def checkTransport(self, ctmlPhase, yamlPhase, temperatures, Dkm_yaml = yamlPhase.mix_diff_coeffs for i in range(ctmlPhase.n_species): message = 'dkm for species {0} at T = {1}'.format(i, T) - assert Dkm_ctml[i] == approx(Dkm_yaml[i]) + assert Dkm_ctml[i] == approx(Dkm_yaml[i]), message @pytest.mark.slow_test def test_gri30(self): diff --git a/test/python/test_jacobian.py b/test/python/test_jacobian.py index ef60df6aef..7c80f7ce83 100644 --- a/test/python/test_jacobian.py +++ b/test/python/test_jacobian.py @@ -19,6 +19,12 @@ class RateExpressionTests: """ rtol = 1e-5 # relative tolerance for comparisons + rxn_idx = None + equation = None + rate_type = None + orders = None + ix3b = [] + @pytest.fixture(scope='class') def rxn(self, gas, rxn_idx): """Fixture to retrieve the reaction object.""" @@ -45,31 +51,43 @@ def pix(self, gas, rxn): return [gas.species_index(k) for k in rxn.products.keys()] @pytest.fixture(scope='class') - def ix3b(self): + def rxn_idx(self, test_data): + return test_data["rxn_idx"] + + @pytest.fixture(scope='class') + def equation(self, test_data): + return test_data["equation"] + + @pytest.fixture(scope='class') + def rate_type(self, test_data): + return test_data["rate_type"] + + @pytest.fixture(scope='class') + def ix3b(self, test_data): """Fixture to retrieve species indices involved in three-body interactions.""" - return [] + return test_data.get("ix3b", []) @pytest.fixture(scope='class') - def orders(self): + def orders(self, test_data): """Fixture to retrieve reaction orders.""" - return None + return test_data.get("orders", None) @pytest.fixture(scope='class') - def setup_rate_expression_tests(self, gas, rxn, r_stoich, p_stoich): + def setup_rate_expression_tests(self, gas, r_stoich, p_stoich, rxn): """ Sets the TPX state for the gas object and provides stoichiometric coefficients - and species indices. Runs once per class. + and species indices as well as setting test-specific attributes. Runs once + per class. """ tpx = gas.TPX return { "tpx": tpx, "r_stoich": r_stoich, - "p_stoich": p_stoich, - "rxn": rxn + "p_stoich": p_stoich } @pytest.fixture(scope='function', autouse=True) - def setup_rate_expression_data(self, gas, setup_rate_expression_tests, rxn_idx): + def setup_rate_expression_data(self, gas, setup_rate_expression_tests, rxn_idx, rxn): """ Resets the gas state before each test and configures reaction multipliers. Also verifies stoichiometric coefficients. @@ -78,7 +96,6 @@ def setup_rate_expression_data(self, gas, setup_rate_expression_tests, rxn_idx): tpx = data["tpx"] r_stoich = data["r_stoich"] p_stoich = data["p_stoich"] - rxn = data["rxn"] # Reset gas phase gas.TPX = tpx @@ -90,13 +107,13 @@ def setup_rate_expression_data(self, gas, setup_rate_expression_tests, rxn_idx): for k, v in rxn.reactants.items(): ix = gas.species_index(k) actual = r_stoich[ix, rxn_idx] - assert actual == v, f"Reactant stoich mismatch for species '{k}': expected {v}, got {actual}" + assert actual == v, f"Reactant stoich mismatch for species '{k}'" # Check stoichiometric coefficients for products for k, v in rxn.products.items(): ix = gas.species_index(k) actual = p_stoich[ix, rxn_idx] - assert actual == v, f"Product stoich mismatch for species '{k}': expected {v}, got {actual}" + assert actual == v, f"Product stoich mismatch for species '{k}'" def test_input(self, equation, rate_type, rxn): """Ensure that correct equation is referenced""" @@ -589,39 +606,23 @@ class TestElementaryRev(HydrogenOxygen): """Standard elementary reaction with two reactants""" @pytest.fixture(scope='class') - def rxn_idx(self): - return 2 - - @pytest.fixture(scope='class') - def equation(self): - return "H2 + O <=> H + OH" - - @pytest.fixture(scope='class') - def rate_type(self): - return "Arrhenius" - - @pytest.fixture(scope='class') - def orders(self): - return None + def test_data(self): + return { + "rxn_idx": 2, + "equation": "H2 + O <=> H + OH", + "rate_type": "Arrhenius" + } class TestElementarySelf(HydrogenOxygen): """Elementary reaction with reactant reacting with itself""" @pytest.fixture(scope='class') - def rxn_idx(self): - return 27 - - @pytest.fixture(scope='class') - def equation(self): - return "2 HO2 <=> H2O2 + O2" - - @pytest.fixture(scope='class') - def rate_type(self): - return "Arrhenius" - - @pytest.fixture(scope='class') - def orders(self): - return None + def test_data(self): + return { + "rxn_idx": 27, + "equation": "2 HO2 <=> H2O2 + O2", + "rate_type": "Arrhenius" + } class TestFalloff(HydrogenOxygen): @@ -629,40 +630,24 @@ class TestFalloff(HydrogenOxygen): rtol = 2e-4 @pytest.fixture(scope='class') - def rxn_idx(self): - return 21 - - @pytest.fixture(scope='class') - def equation(self): - return "2 OH (+M) <=> H2O2 (+M)" - - @pytest.fixture(scope='class') - def rate_type(self): - return "falloff" - - @pytest.fixture(scope='class') - def orders(self): - return None - + def test_data(self): + return { + "rxn_idx": 21, + "equation": "2 OH (+M) <=> H2O2 (+M)", + "rate_type": "falloff" + } class TestThreeBody(HydrogenOxygen): """ Three-body reaction with default efficiency""" @pytest.fixture(scope='class') - def ix3b(self, gas): - return list(range(gas.n_species)) - - @pytest.fixture(scope='class') - def rxn_idx(self): - return 1 - - @pytest.fixture(scope='class') - def equation(self): - return "H + O + M <=> OH + M" - - @pytest.fixture(scope='class') - def rate_type(self): - return "Arrhenius" + def test_data(self, gas): + return { + "rxn_idx": 1, + "equation": "H + O + M <=> OH + M", + "rate_type": "Arrhenius", + "ix3b": list(range(gas.n_species)) + } def test_thirdbodies_forward(self, gas, rxn_idx): drop = gas.forward_rates_of_progress_ddX @@ -680,7 +665,6 @@ def test_thirdbodies_reverse(self, gas, rxn_idx): rop = gas.reverse_rates_of_progress assert rop[rxn_idx] == approx((dropm[rxn_idx] * gas.X).sum()) -@pytest.mark.usefixtures("setup_rate_expression_data") class EdgeCases(RateExpressionTests): @pytest.fixture(scope='class') @@ -696,98 +680,61 @@ class TestElementaryIrr(EdgeCases): """Irreversible elementary reaction with two reactants""" @pytest.fixture(scope='class') - def rxn_idx(self): - return 0 - - @pytest.fixture(scope='class') - def equation(self): - return "HO2 + O => O2 + OH" - - @pytest.fixture(scope='class') - def rate_type(self): - return "Arrhenius" - - @pytest.fixture(scope='class') - def orders(self): - return None + def test_data(self): + return { + "rxn_idx": 0, + "equation": "HO2 + O => O2 + OH", + "rate_type": "Arrhenius" + } class TestElementaryOne(EdgeCases): """Three-body reaction with single reactant species""" @pytest.fixture(scope='class') - def rxn_idx(self): - return 1 - - @pytest.fixture(scope='class') - def equation(self): - return "H2 <=> 2 H" - - @pytest.fixture(scope='class') - def rate_type(self): - return "Arrhenius" - - @pytest.fixture(scope='class') - def orders(self): - return None + def test_data(self): + return { + "rxn_idx": 1, + "equation": "H2 <=> 2 H", + "rate_type": "Arrhenius" + } class TestElementaryThree(EdgeCases): """Elementary reaction with three reactants""" @pytest.fixture(scope='class') - def rxn_idx(self): - return 2 - - @pytest.fixture(scope='class') - def equation(self): - return "2 H + O <=> H2O" - - @pytest.fixture(scope='class') - def rate_type(self): - return "Arrhenius" + def test_data(self): + return { + "rxn_idx": 2, + "equation": "2 H + O <=> H2O", + "rate_type": "Arrhenius" + } - @pytest.fixture(scope='class') - def orders(self): - return None class TestElementaryFrac(EdgeCases): """Elementary reaction with specified reaction order""" @pytest.fixture(scope='class') - def rxn_idx(self): - return 3 - - @pytest.fixture(scope='class') - def equation(self): - return "0.7 H2 + 0.2 O2 + 0.6 OH => H2O" - - @pytest.fixture(scope='class') - def rate_type(self): - return "Arrhenius" - - @pytest.fixture(scope='class') - def orders(self): - return {"H2": 0.8, "O2": 1.0, "OH": 2.0} + def test_data(self): + return { + "rxn_idx": 3, + "equation": "0.7 H2 + 0.2 O2 + 0.6 OH => H2O", + "rate_type": "Arrhenius", + "orders": {"H2": 0.8, "O2": 1.0, "OH": 2.0} + } class TestThreeBodyNoDefault(EdgeCases): """Three body reaction without default efficiency""" @pytest.fixture(scope='class') - def ix3b(self, gas): + def test_data(self, gas): efficiencies = {"H2": 2.0, "H2O": 6.0, "AR": 0.7} - return [gas.species_index(k) for k in efficiencies.keys()] - - @pytest.fixture(scope='class') - def rxn_idx(self): - return 4 - - @pytest.fixture(scope='class') - def equation(self): - return "H + O + M <=> OH + M" - - @pytest.fixture(scope='class') - def rate_type(self): - return "Arrhenius" + return { + "rxn_idx": 4, + "equation": "H + O + M <=> OH + M", + "rate_type": "Arrhenius", + "ix3b": [gas.species_index(k) for k in efficiencies.keys()] + } class FromScratchCases(RateExpressionTests): @@ -826,84 +773,59 @@ class TestPlog(FromScratchCases): """ Plog reaction""" @pytest.fixture(scope='class') - def rxn_idx(self): - return 3 - - @pytest.fixture(scope='class') - def equation(self): - return "H2 + O2 <=> 2 OH" - - @pytest.fixture(scope='class') - def rate_type(self): - return "pressure-dependent-Arrhenius" - - @pytest.fixture(scope='class') - def orders(self): - return None + def test_data(self): + return { + "rxn_idx": 3, + "equation": "H2 + O2 <=> 2 OH", + "rate_type": "pressure-dependent-Arrhenius" + } -@pytest.mark.usefixtures("setup_rate_expression_data") class TestChebyshev(FromScratchCases): """Chebyshev reaction""" @pytest.fixture(scope='class') - def rxn_idx(self): - return 4 - - @pytest.fixture(scope='class') - def equation(self): - return "HO2 <=> O + OH" - - @pytest.fixture(scope='class') - def rate_type(self): - return "Chebyshev" - - @pytest.fixture(scope='class') - def orders(self): - return None + def test_data(self): + return { + "rxn_idx": 4, + "equation": "HO2 <=> O + OH", + "rate_type": "Chebyshev" + } class TestBlowersMasel(FromScratchCases): """Blowers-Masel""" @pytest.fixture(scope='class') - def rxn_idx(self): - return 6 - - @pytest.fixture(scope='class') - def equation(self): - return "H2 + O <=> H + OH" - - @pytest.fixture(scope='class') - def rate_type(self): - return "Blowers-Masel" - - @pytest.fixture(scope='class') - def orders(self): - return None + def test_data(self): + return { + "rxn_idx": 6, + "equation": "H2 + O <=> H + OH", + "rate_type": "Blowers-Masel" + } @pytest.mark.xfail(reason="Change of reaction enthalpy is not considered") @pytest.mark.filterwarnings("ignore:.*does not consider.*(electron|enthalpy).*:UserWarning") def test_forward_rop_ddT(self, gas, rxn_idx, rxn): - """Override to handle expected fall-off rate limitations.""" + """Override to handle issues with Blowers-Masel derivatives.""" super().test_forward_rop_ddT(gas, rxn_idx, rxn) @pytest.mark.xfail(reason="Change of reaction enthalpy is not considered") @pytest.mark.filterwarnings("ignore:.*does not consider.*(electron|enthalpy).*:UserWarning") def test_reverse_rop_ddT(self, gas, rxn_idx, rxn): - """Override to handle expected fall-off rate limitations.""" + """Override to handle issues with Blowers-Masel derivatives.""" super().test_reverse_rop_ddT(gas, rxn_idx, rxn) @pytest.mark.xfail(reason="Change of reaction enthalpy is not considered") @pytest.mark.filterwarnings("ignore:.*does not consider.*(electron|enthalpy).*:UserWarning") def test_net_rop_ddT(self, gas, rxn_idx, rxn): - """Override to handle expected fall-off rate limitations.""" + """Override to handle issues with Blowers-Masel derivatives.""" super().test_net_rop_ddT(gas, rxn_idx, rxn) @pytest.mark.xfail(reason="Change of reaction enthalpy is not considered") @pytest.mark.filterwarnings("ignore:.*does not consider.*(electron|enthalpy).*:UserWarning") def test_net_rate_ddT(self, gas, rix, pix): - """Override to handle expected fall-off rate limitations.""" + """Override to handle issues with Blowers-Masel derivatives.""" super().test_net_rate_ddT(gas, rix, pix) @@ -1148,7 +1070,6 @@ def test_net_rop_ddT(self, gas): print(drop_num[i]) raise err -@pytest.mark.usefixtures("full_tests_data") class TestFullHydrogenOxygen(FullTests): @pytest.fixture(scope='class') @@ -1158,7 +1079,6 @@ def gas(self): gas.TPX = 300, 5 * ct.one_atm, "H2:1, O2:3" return gas -@pytest.mark.usefixtures("full_tests_data") class TestFullGriMech(FullTests): @pytest.fixture(scope='class') @@ -1168,7 +1088,6 @@ def gas(self): gas.TPX = 300, 5 * ct.one_atm, "CH4:1, C3H8:.1, O2:1, N2:3.76" return gas -@pytest.mark.usefixtures("full_tests_data") class TestFullEdgeCases(FullTests): @pytest.fixture(scope='class') @@ -1253,13 +1172,13 @@ def setup_surface_rate_expression_data(self, gas, surf, initial_conditions, rxn_ for k, v in rxn.reactants.items(): ix = sidxs[k] actual = r_stoich[ix, rxn_idx] - assert actual == v, f"Reactant stoich mismatch for species '{k}': expected {v}, got {actual}" + assert actual == v, f"Reactant stoich mismatch for species '{k}'" # Check stoichiometric coefficients for products for k, v in rxn.products.items(): ix = sidxs[k] actual = p_stoich[ix, rxn_idx] - assert actual == v, f"Product stoich mismatch for species '{k}': expected {v}, got {actual}" + assert actual == v, f"Product stoich mismatch for species '{k}'" def get_concentrations(self, surf, gas): """Concatenate concentrations from surface and gas phases.""" @@ -1269,8 +1188,8 @@ def get_concentrations(self, surf, gas): def test_input(self, rxn_data, equation, rate_type): """Ensure that the correct equation and rate type are referenced.""" rxn = rxn_data["rxn"] - assert equation == rxn.equation, f"Expected equation '{equation}', got '{rxn.equation}'" - assert rate_type == rxn.rate.type, f"Expected rate type '{rate_type}', got '{rxn.rate.type}'" + assert equation == rxn.equation + assert rate_type == rxn.rate.type def test_forward_rop_ddCi(self, surf, gas, rxn_data, orders, rxn_idx): """Test forward rates of progress derivatives.""" @@ -1579,7 +1498,6 @@ def test_net_rop_ddCi(self, gas, surf): assert drop == approx(ropf - ropr, rel=self.rtol) -@pytest.mark.usefixtures("setup_surface_full_data") class TestFullPlatinumHydrogen(SurfaceFullTests): phase_defs = """ diff --git a/test/python/test_kinetics.py b/test/python/test_kinetics.py index b380b03e95..4222d897b8 100644 --- a/test/python/test_kinetics.py +++ b/test/python/test_kinetics.py @@ -22,15 +22,10 @@ class TestKinetics: - @pytest.fixture(scope='class') - def initial_conditions(self): - return { 'X': [0.1, 1e-4, 1e-5, 0.2, 2e-4, 0.3, 1e-6, 5e-5, 0.4, 0], - 'T': 800, 'P': 2 * ct.one_atm} - @pytest.fixture - def phase(self, initial_conditions): + def phase(self): phase = ct.Solution('h2o2.yaml', transport_model=None) - phase.TPX = initial_conditions['T'], initial_conditions['P'], initial_conditions['X'] + phase.TPX = 800, 2 * ct.one_atm, [0.1, 1e-4, 1e-5, 0.2, 2e-4, 0.3, 1e-6, 5e-5, 0.4, 0] return phase @pytest.mark.usefixtures("allow_deprecated") @@ -115,34 +110,34 @@ def test_stoich_coeffs(self, phase): nu_r = phase.reactant_stoich_coeffs nu_p = phase.product_stoich_coeffs - def check_reactant(s, i, value, phase): + def check_reactant(s, i, value): k = phase.kinetics_species_index(s) assert phase.reactant_stoich_coeff(s, i) == value assert phase.reactant_stoich_coeff(k, i) == value assert nu_r[k, i] == value - def check_product(s, i, value, phase): + def check_product(s, i, value): k = phase.kinetics_species_index(s) assert phase.product_stoich_coeff(k, i) == value assert phase.product_stoich_coeff(s, i) == value assert nu_p[k, i] == value # H + H2O2 <=> HO2 + H2 - check_reactant('H', 18, 1, phase) - check_reactant('H2O2', 18, 1, phase) - check_reactant('HO2', 18, 0, phase) - check_reactant('H2', 18, 0, phase) + check_reactant('H', 18, 1) + check_reactant('H2O2', 18, 1) + check_reactant('HO2', 18, 0) + check_reactant('H2', 18, 0) - check_product('H', 18, 0, phase) - check_product('H2O2', 18, 0, phase) - check_product('HO2', 18, 1, phase) - check_product('H2', 18, 1, phase) + check_product('H', 18, 0) + check_product('H2O2', 18, 0) + check_product('HO2', 18, 1) + check_product('H2', 18, 1) # 2 O + M <=> O2 + M - check_reactant('O', 0, 2, phase) - check_reactant('O2', 0, 0, phase) - check_product('O', 0, 0, phase) - check_product('O2', 0, 1, phase) + check_reactant('O', 0, 2) + check_reactant('O2', 0, 0) + check_product('O', 0, 0) + check_product('O2', 0, 1) @pytest.mark.skipif(isinstance(_scipy_sparse, ImportError), reason="scipy is not installed") def test_stoich_coeffs_sparse(self, phase): @@ -160,8 +155,8 @@ def test_stoich_coeffs_sparse(self, phase): def test_rates_of_progress(self, phase): assert len(phase.net_rates_of_progress) == phase.n_reactions - assert (phase.forward_rates_of_progress - - phase.reverse_rates_of_progress) == approx(phase.net_rates_of_progress) + assert (phase.forward_rates_of_progress - phase.reverse_rates_of_progress + == approx(phase.net_rates_of_progress)) def test_heat_release(self, phase): hrr = - phase.partial_molar_enthalpies.dot(phase.net_production_rates) @@ -171,29 +166,27 @@ def test_heat_release(self, phase): def test_rate_constants(self, phase): assert len(phase.forward_rate_constants) == phase.n_reactions ix = phase.reverse_rate_constants != 0. - assert (phase.forward_rate_constants[ix] / - phase.reverse_rate_constants[ix]) == approx( - phase.equilibrium_constants[ix]) + assert (phase.forward_rate_constants[ix] / phase.reverse_rate_constants[ix] + == approx(phase.equilibrium_constants[ix])) def test_species_rates(self, phase): nu_p = phase.product_stoich_coeffs nu_r = phase.reactant_stoich_coeffs - creation = (np.dot(nu_p, phase.forward_rates_of_progress) + - np.dot(nu_r, phase.reverse_rates_of_progress)) - destruction = (np.dot(nu_r, phase.forward_rates_of_progress) + - np.dot(nu_p, phase.reverse_rates_of_progress)) + creation = (np.dot(nu_p, phase.forward_rates_of_progress) + + np.dot(nu_r, phase.reverse_rates_of_progress)) + destruction = (np.dot(nu_r, phase.forward_rates_of_progress) + + np.dot(nu_p, phase.reverse_rates_of_progress)) assert phase.creation_rates == approx(creation) assert phase.destruction_rates == approx(destruction) assert phase.net_production_rates == approx(creation - destruction) def test_reaction_deltas(self, phase): - assert (phase.delta_enthalpy - - phase.delta_entropy * phase.T) == approx(phase.delta_gibbs) + assert (phase.delta_enthalpy - phase.delta_entropy * phase.T + == approx(phase.delta_gibbs)) - assert (phase.delta_standard_enthalpy - - phase.delta_standard_entropy * phase.T) == approx( - phase.delta_standard_gibbs) + assert (phase.delta_standard_enthalpy - phase.delta_standard_entropy * phase.T + == approx(phase.delta_standard_gibbs)) class TestKineticsFromReactions: @@ -475,10 +468,10 @@ def test_modify_thermo(self): for i, R in enumerate(gas.reactions()): if ('OH' in R.reactants or 'OH' in R.products) and R.reversible: # Rate should be different if reaction involves OH - assert not approx(w2[i] / w1[i], rel=1e-5) == 1 + assert not 1 == approx(w2[i] / w1[i], rel=1e-5) else: # Rate should be the same if reaction does not involve OH - assert approx(w2[i] / w1[i], rel=1e-5) == 1 + assert 1 == approx(w2[i] / w1[i], rel=1e-5) def test_pdep_err(self): err_msg = ("InputFileError thrown by PlogRate::validate:", @@ -737,16 +730,15 @@ class TestReactionPath: def gas(self): gas = ct.Solution('gri30.yaml', transport_model=None) gas.TPX = 1300.0, ct.one_atm, 'CH4:0.4, O2:1, N2:3.76' - return gas - @pytest.fixture(scope='class', autouse=True) - def run_reactor(self, gas): + # Advance the reactor r = ct.IdealGasReactor(gas) net = ct.ReactorNet([r]) T = r.T while T < 1900: net.step() T = r.T + return gas def check_dot(self, gas, diagram, element): diagram.label_threshold = 0 @@ -1015,8 +1007,8 @@ def cathode_curr(E): Ec = newton_solve(cathode_curr, xstart=Ec0+0.1, C=curr) cathode_bulk.electric_potential = phi_oxide_c + Ec data.append([Ea - Ea0, 0.1*curr, Ec - Ec0, delta_V, - cathode_bulk.electric_potential - - anode_bulk.electric_potential]) + cathode_bulk.electric_potential + - anode_bulk.electric_potential]) compare(data, test_data_path / "sofc-test.csv", rtol=1e-7) @@ -1135,7 +1127,7 @@ def test_interface_current(self): method = anode_int.interface_current(p) manual = sum(net_prod_rates * charges) * ct.faraday - assert method == manual + assert method == manual class TestDuplicateReactions: @@ -1184,17 +1176,11 @@ def test_nonreacting_species(self): assert gas.n_reactions == 3 -# Fixture for the following test classes -@pytest.fixture(scope='class') -def initial_conditions(): - return {'T': 900, 'P': 2*ct.one_atm, - 'X': 'H2:0.1, H2O:0.2, O2:0.7, O:1e-4, OH:1e-5, H:2e-5'} - @pytest.fixture(scope='class') -def gas(initial_conditions): +def gas(): gas = ct.Solution('h2o2.yaml', transport_model=None) - gas.X = initial_conditions['X'] - gas.TP = initial_conditions['T'], initial_conditions['P'] + gas.X = 'H2:0.1, H2O:0.2, O2:0.7, O:1e-4, OH:1e-5, H:2e-5' + gas.TP = 900, 2*ct.one_atm return gas @pytest.fixture(scope='class') @@ -1272,6 +1258,7 @@ def test_custom_from_scratch(self, gas): species=species, reactions=custom_reactions) gas1.TPX = gas.TPX + # remove references to Python objects del custom_reactions del rate1 @@ -1306,10 +1293,10 @@ def test_elementary(self, gas, species): species=species, reactions=[r]) gas2.TPX = gas.TPX - assert gas2.forward_rate_constants[0] == approx( - gas.forward_rate_constants[2]) - assert gas2.net_rates_of_progress[0] == approx( - gas.net_rates_of_progress[2]) + assert (gas2.forward_rate_constants[0] + == approx(gas.forward_rate_constants[2])) + assert (gas2.net_rates_of_progress[0] + == approx(gas.net_rates_of_progress[2])) def test_arrhenius_rate(self, gas): R = gas.reaction(2) @@ -1382,10 +1369,10 @@ def test_falloff(self, gas, species): species=species, reactions=[r]) gas2.TPX = gas.TPX - assert gas2.forward_rate_constants[0] == approx( - gas.forward_rate_constants[21]) - assert gas2.net_rates_of_progress[0] == approx( - gas.net_rates_of_progress[21]) + assert (gas2.forward_rate_constants[0] + == approx(gas.forward_rate_constants[21])) + assert (gas2.net_rates_of_progress[0] + == approx(gas.net_rates_of_progress[21])) def test_modify_falloff(self): gas = ct.Solution('gri30.yaml', transport_model=None) @@ -1414,10 +1401,8 @@ def test_threebody(self, gas, species): species=species, reactions=[r]) gas2.TPX = gas.TPX - assert gas2.forward_rate_constants[0] == approx( - gas.forward_rate_constants[1]) - assert gas2.net_rates_of_progress[0] == approx( - gas.net_rates_of_progress[1]) + assert gas2.forward_rate_constants[0] == approx(gas.forward_rate_constants[1]) + assert gas2.net_rates_of_progress[0] == approx(gas.net_rates_of_progress[1]) def test_modify_third_body(self, gas): gas = ct.Solution('h2o2.yaml', transport_model=None) @@ -1456,17 +1441,17 @@ def test_plog(self): for P in [0.001, 0.01, 0.2, 1.0, 1.1, 9.0, 10.0, 99.0, 103.0]: gas1.TP = gas2.TP = 900, P * ct.one_atm - assert gas2.forward_rate_constants[0] == approx( - gas1.forward_rate_constants[0]) - assert gas2.net_rates_of_progress[0] == approx( - gas1.net_rates_of_progress[0]) + assert (gas2.forward_rate_constants[0] + == approx(gas1.forward_rate_constants[0])) + assert (gas2.net_rates_of_progress[0] + == approx(gas1.net_rates_of_progress[0])) def test_plog_rate(self): gas1 = ct.Solution('pdep-test.yaml') gas1.TP = 800, 2*ct.one_atm for i in range(4): - assert gas1.reaction(i).rate(gas1.T, gas1.P) == approx( - gas1.forward_rate_constants[i]) + assert (gas1.reaction(i).rate(gas1.T, gas1.P) + == approx(gas1.forward_rate_constants[i])) def test_plog_invalid_third_body(self): with pytest.raises(ct.CanteraError, match="Found superfluous"): @@ -1491,7 +1476,7 @@ def test_modify_plog(self): # ... but should change the rate at higher pressures gas.TP = 1010, 12.0 * ct.one_atm kf = gas.forward_rates_of_progress - kf[0] != approx(kf[1]) + assert kf[0] != approx(kf[1]) class TestChebyshevReaction: def test_chebyshev(self): @@ -1514,10 +1499,10 @@ def test_chebyshev(self): for T,P in itertools.product([300, 500, 1500], [1e4, 4e5, 3e6]): gas1.TP = gas2.TP = T, P - assert gas2.forward_rate_constants[0] == approx( - gas1.forward_rate_constants[4]) - assert gas2.net_rates_of_progress[0] == approx( - gas1.net_rates_of_progress[4]) + assert (gas2.forward_rate_constants[0] + == approx(gas1.forward_rate_constants[4])) + assert (gas2.net_rates_of_progress[0] + == approx(gas1.net_rates_of_progress[4])) def test_chebyshev_single_P(self): species = ct.Species.list_from_file("pdep-test.yaml") @@ -1564,8 +1549,8 @@ def test_chebyshev_rate(self): gas1 = ct.Solution('pdep-test.yaml') gas1.TP = 800, 2*ct.one_atm for i in range(4,6): - assert gas1.reaction(i).rate(gas1.T, gas1.P) == approx( - gas1.forward_rate_constants[i]) + assert (gas1.reaction(i).rate(gas1.T, gas1.P) + == approx(gas1.forward_rate_constants[i])) def test_chebyshev_bad_shape_yaml(self): species = ct.Species.list_from_file("pdep-test.yaml") @@ -1609,7 +1594,7 @@ class TestBlowersMaselReaction: def test_BlowersMasel(self, gas): r = ct.Reaction({"O":1, "H2":1}, {"H":1, "OH":1}, - ct.BlowersMaselRate(3.87e1, 2.7, 6260*1000*4.184, 1e9*1000*4.184)) + ct.BlowersMaselRate(3.87e1, 2.7, 6260*1000*4.184, 1e9*1000*4.184)) gas1 = ct.Solution("blowers-masel.yaml", "gas") assert isinstance(gas1.reaction(0).rate, ct.BlowersMaselRate) @@ -1622,10 +1607,10 @@ def test_BlowersMasel(self, gas): gas1.X = 'H2:0.1, H2O:0.2, O2:0.7, O:1e-4, OH:1e-5, H:2e-5' gas2.X = 'H2:0.1, H2O:0.2, O2:0.7, O:1e-4, OH:1e-5, H:2e-5' - assert gas2.forward_rate_constants[0] == approx( - gas1.forward_rate_constants[0], rel=1e-7) - assert gas2.net_rates_of_progress[0] == approx( - gas1.net_rates_of_progress[0], rel=1e-7) + assert (gas2.forward_rate_constants[0] + == approx(gas1.forward_rate_constants[0], rel=1e-7)) + assert (gas2.net_rates_of_progress[0] + == approx(gas1.net_rates_of_progress[0], rel=1e-7)) def test_negative_A_blowersmasel(self): species = ct.Solution("blowers-masel.yaml").species() @@ -1648,8 +1633,8 @@ def test_Blowers_Masel_change_enthalpy(self): b = r.rate.temperature_exponent vp = 2 * w * (w+E0) / (w - E0) deltaH = gas.delta_enthalpy[0] - E = ((w + deltaH / 2) * (vp - 2 * w + deltaH) ** 2 / - (vp ** 2 - 4 * w ** 2 + deltaH ** 2)) + E = ((w + deltaH / 2) * (vp - 2 * w + deltaH) ** 2 + / (vp ** 2 - 4 * w ** 2 + deltaH ** 2)) deltaH_high = 10 * gas.reaction(0).rate.activation_energy deltaH_low = -20 * gas.reaction(0).rate.activation_energy @@ -1669,8 +1654,8 @@ def test_Blowers_Masel_change_enthalpy(self): gas.reaction(0).rate.delta_enthalpy = deltaH_high assert gas.reaction(0).rate.delta_enthalpy == approx(deltaH_high) assert deltaH_high == approx(gas.reaction(0).rate.activation_energy) - assert A*gas.T**b*np.exp(-deltaH_high/ct.gas_constant/gas.T) == approx( - gas.forward_rate_constants[0]) + assert (A*gas.T**b*np.exp(-deltaH_high/ct.gas_constant/gas.T) + == approx(gas.forward_rate_constants[0])) perturbed_coeffs = species.thermo.coeffs.copy() perturbed_coeffs[6] += deltaH_low / ct.gas_constant @@ -1681,8 +1666,8 @@ def test_Blowers_Masel_change_enthalpy(self): gas.reaction(0).rate.delta_enthalpy = deltaH_low assert gas.reaction(0).rate.delta_enthalpy == approx(deltaH_low) assert gas.reaction(0).rate.activation_energy == 0 - assert A*gas.T**b*np.exp(0/ct.gas_constant/gas.T) == approx( - gas.forward_rate_constants[0]) + assert (A*gas.T**b*np.exp(0/ct.gas_constant/gas.T) + == approx(gas.forward_rate_constants[0])) def test_modify_BlowersMasel(self, gas): gas = ct.Solution("blowers-masel.yaml") @@ -1730,10 +1715,10 @@ def test_interface(self): for T in [300, 500, 1500]: gas.TP = surf1.TP = surf2.TP = T, 5*ct.one_atm - assert surf1.forward_rate_constants[1] == approx( - surf2.forward_rate_constants[0]) - assert surf1.net_rates_of_progress[1] == approx( - surf2.net_rates_of_progress[0]) + assert (surf1.forward_rate_constants[1] + == approx(surf2.forward_rate_constants[0])) + assert (surf1.net_rates_of_progress[1] + == approx(surf2.net_rates_of_progress[0])) def test_BlowersMaselinterface(self): gas = ct.Solution("gri30.yaml", transport_model=None) @@ -1757,10 +1742,10 @@ def test_BlowersMaselinterface(self): for T in [300, 500, 1500]: gas.TP = surf1.TP = surf2.TP = T, 5*ct.one_atm - assert surf1.forward_rate_constants[0] == approx( - surf2.forward_rate_constants[0]) - assert surf1.net_rates_of_progress[0] == approx( - surf2.net_rates_of_progress[0]) + assert (surf1.forward_rate_constants[0] + == approx(surf2.forward_rate_constants[0])) + assert (surf1.net_rates_of_progress[0] + == approx(surf2.net_rates_of_progress[0])) def test_modify_interface(self): surf = ct.Interface("ptcombust.yaml", "Pt_surf") @@ -1803,8 +1788,8 @@ def test_modify_BMinterface(self): surf.coverages = "O(S):0.2, PT(S):0.6, H(S):0.2" k3 = surf.forward_rate_constants[0] - assert k1 / k2 == approx(np.exp(-O2_delta_theta_k * Ek / - ct.gas_constant / surf.T)) + assert (np.exp(-O2_delta_theta_k * Ek / ct.gas_constant / surf.T) + == approx(k1 / k2)) assert k2 == approx(k3) diff --git a/test/python/test_mixture.py b/test/python/test_mixture.py index f9d8627cb7..8de4ddb913 100644 --- a/test/python/test_mixture.py +++ b/test/python/test_mixture.py @@ -17,7 +17,7 @@ def phase2(self): @pytest.fixture(scope='function') def mix(request, phase1, phase2): # Create instance-level data for each test - return ct.Mixture([(phase1, 1.0), (phase2, 2.0)]) + return ct.Mixture([(phase1, 1.0), (phase2, 2.0)]) def test_sizes(self, mix, phase1, phase2): assert mix.n_phases == 2 @@ -28,9 +28,6 @@ def test_sizes(self, mix, phase1, phase2): assert len(E) == mix.n_elements def test_element_index(self, mix): - m_H = mix.element_index('H') - assert m_H == mix.element_index(m_H) - with pytest.raises(ValueError, match='No such element'): mix.element_index('W') diff --git a/test/python/test_onedim.py b/test/python/test_onedim.py index 97b79e388b..1d85eb8ac1 100644 --- a/test/python/test_onedim.py +++ b/test/python/test_onedim.py @@ -999,7 +999,7 @@ def test_mixture_averaged(self, test_data_path, saveReference=False): np.savetxt(referenceFile, data, '%11.6e', ', ') else: bad = compareProfiles(test_data_path / referenceFile, data, - rtol=1e-2, atol=1e-8, xtol=1e-2) + rtol=1e-2, atol=1e-8, xtol=1e-2) assert not bad, bad def test_auto(self, test_data_path, saveReference=False): @@ -1038,7 +1038,7 @@ def time_step_func(dt): np.savetxt(referenceFile, data, '%11.6e', ', ') else: bad = compareProfiles(test_data_path / referenceFile, data, - rtol=1e-2, atol=1e-8, xtol=1e-2) + rtol=1e-2, atol=1e-8, xtol=1e-2) assert not bad, bad def run_extinction(self, mdot_fuel, mdot_ox, T_ox, width, P): @@ -1121,7 +1121,7 @@ def test_mixture_averaged_rad(self, test_data_path, saveReference=False): np.savetxt(referenceFile, data, '%11.6e', ', ') else: bad = compareProfiles(test_data_path / referenceFile, data, - rtol=1e-2, atol=1e-8, xtol=1e-2) + rtol=1e-2, atol=1e-8, xtol=1e-2) assert not bad, bad filename = self.test_work_path / "DiffusionFlameTest-h2-mix-rad.csv" @@ -1408,7 +1408,7 @@ def test_mixture_averaged(self, test_data_path, saveReference=False): np.savetxt(referenceFile, data, '%11.6e', ', ') else: bad = compareProfiles(test_data_path / referenceFile, data, - rtol=1e-2, atol=1e-8, xtol=1e-2) + rtol=1e-2, atol=1e-8, xtol=1e-2) assert not bad, bad filename = self.test_work_path / "CounterflowPremixedFlame-h2-mix.csv" @@ -1419,42 +1419,41 @@ def test_mixture_averaged(self, test_data_path, saveReference=False): csv_data = np.genfromtxt(filename, dtype=float, delimiter=',', names=True) assert 'qdot' not in csv_data.dtype.names - def run_case(self, phi, T, width, P): - gas = ct.Solution("h2o2.yaml") - gas.TPX = T, P * ct.one_atm, {'H2':phi, 'O2':0.5, 'AR':2} - sim = ct.CounterflowPremixedFlame(gas=gas, width=width) - sim.reactants.mdot = 10 * gas.density - sim.products.mdot = 5 * gas.density - sim.set_refine_criteria(ratio=6, slope=0.7, curve=0.8, prune=0.2) - sim.solve(loglevel=0, auto=True) - assert all(sim.T >= T - 1e-3) - assert all(sim.spread_rate >= -1e-9) - assert np.allclose(sim.L, sim.L[0]) - return sim - - @pytest.mark.slow_test - def test_solve_case1(self): - self.run_case(phi=0.4, T=400, width=0.05, P=10.0) - - @pytest.mark.slow_test - def test_solve_case2(self): - self.run_case(phi=0.5, T=500, width=0.03, P=2.0) - - @pytest.mark.slow_test - def test_solve_case3(self): - self.run_case(phi=0.7, T=300, width=0.05, P=2.0) - - @pytest.mark.slow_test - def test_solve_case4(self): - self.run_case(phi=1.5, T=400, width=0.03, P=0.02) + @pytest.fixture(scope='function') + def run_case(self): + """Helper function to create and run a simulation case.""" + def _run_case(phi, T, width, P): + gas = ct.Solution("h2o2.yaml") + gas.TPX = T, P * ct.one_atm, {'H2': phi, 'O2': 0.5, 'AR': 2} + sim = ct.CounterflowPremixedFlame(gas=gas, width=width) + sim.reactants.mdot = 10 * gas.density + sim.products.mdot = 5 * gas.density + sim.set_refine_criteria(ratio=6, slope=0.7, curve=0.8, prune=0.2) + sim.solve(loglevel=0, auto=True) + assert all(sim.T >= T - 1e-3) + assert all(sim.spread_rate >= -1e-9) + assert np.allclose(sim.L, sim.L[0]) + return sim + return _run_case + @pytest.mark.parametrize( + "phi, T, width, P", + [ + (0.4, 400, 0.05, 10.0), + (0.5, 500, 0.03, 2.0), + (0.7, 300, 0.05, 2.0), + (1.5, 400, 0.03, 0.02), + (2.0, 300, 0.2, 0.2), + ] + ) @pytest.mark.slow_test - def test_solve_case5(self): - self.run_case(phi=2.0, T=300, width=0.2, P=0.2) + def test_solve_case(self, run_case, phi, T, width, P): + """Parameterized test cases for different simulation setups.""" + run_case(phi, T, width, P) @pytest.mark.slow_test - def test_restart(self): - sim = self.run_case(phi=2.0, T=300, width=0.2, P=0.2) + def test_restart(self, run_case): + sim = run_case(phi=2.0, T=300, width=0.2, P=0.2) arr = sim.to_array() sim.reactants.mdot *= 1.1 @@ -1527,7 +1526,7 @@ def test_mixture_averaged(self, test_data_path, saveReference=False): np.savetxt(referenceFile, data, '%11.6e', ', ') else: bad = compareProfiles(test_data_path / referenceFile, data, - rtol=1e-2, atol=1e-8, xtol=1e-2) + rtol=1e-2, atol=1e-8, xtol=1e-2) assert not bad, bad filename = self.test_work_path / "CounterflowPremixedFlame-h2-mix-RK.csv" @@ -1601,15 +1600,6 @@ def _run_case(phi, T, width, P): assert np.allclose(sim.L, 0) return _run_case - def solve(self, phi, T, width, P): - gas = ct.Solution("h2o2.yaml") - gas.TPX = T, ct.one_atm*P, {'H2':phi, 'O2':0.5, 'AR':1.5} - sim = ct.BurnerFlame(gas=gas, width=width) - sim.burner.mdot = gas.density * 0.15 - sim.solve(loglevel=0, auto=True) - assert sim.T[1] > T - assert np.allclose(sim.L, 0) - @pytest.mark.parametrize( "phi, T, width, P", [ diff --git a/test/python/test_purefluid.py b/test/python/test_purefluid.py index 5c0bd58114..dc25effcab 100644 --- a/test/python/test_purefluid.py +++ b/test/python/test_purefluid.py @@ -399,6 +399,7 @@ def setup_fluid(request): request.cls.s0 = request.cls.fluid.s +@pytest.mark.usefixtures("setup_fluid") class PureFluidTestCases: """ Test the results of pure fluid phase calculations against tabulated @@ -429,7 +430,7 @@ def test_consistency_temperature(self): # At constant volume, dU = T dS msg = 'At state: T=%s, rho=%s' % (state.T, state.rho) - assert (u2-u1)/(s2-s1) == approx(state.T, rel=self.tol.dUdS) + assert (u2-u1)/(s2-s1) == approx(state.T, rel=self.tol.dUdS), msg def test_consistency_volume(self): for state in self.states: @@ -446,7 +447,7 @@ def test_consistency_volume(self): # At constant temperature, dA = - p dV msg = 'At state: T=%s, rho=%s' % (state.T, state.rho) - assert -(a2-a1)/dV == approx(p, rel=tol) + assert -(a2-a1)/dV == approx(p, rel=tol), msg def test_saturation(self): for state in self.states: @@ -471,7 +472,7 @@ def test_saturation(self): # Clausius-Clapeyron Relation msg = 'At state: T=%s, rho=%s' % (state.T, state.rho) assert (p2-p1)/dT == approx((hg-hf)/(state.T * (vg-vf)), - rel=self.tol.dPdT) + rel=self.tol.dPdT), msg # True for a change in state at constant pressure and temperature assert hg-hf == approx(state.T * (sg-sf), rel=self.tol.hTs) @@ -483,24 +484,23 @@ def test_pressure(self): tol = 70*self.tol.p if state.phase == 'liquid' else self.tol.p tol *= state.tolMod msg = 'At state: T=%s, rho=%s' % (state.T, state.rho) - assert self.fluid.P == approx(state.p, rel=tol) + assert self.fluid.P == approx(state.p, rel=tol), msg def test_internal_energy(self): for state in self.states: self.fluid.TD = state.T, state.rho msg = 'At state: T=%s, rho=%s' % (state.T, state.rho) assert self.fluid.u - self.u0 == approx(state.u - self.refState.u, - rel=self.tol.u * state.tolMod) + rel=self.tol.u * state.tolMod), msg def test_entropy(self): for state in self.states: self.fluid.TD = state.T, state.rho msg = 'At state: T=%s, rho=%s' % (state.T, state.rho) assert self.fluid.s - self.s0 == approx(state.s - self.refState.s, - rel=self.tol.s * state.tolMod) + rel=self.tol.s * state.tolMod), msg -@pytest.mark.usefixtures("setup_fluid") class TestHFC134a(PureFluidTestCases): """ Reference values for HFC134a taken from NIST Chemistry WebBook, which @@ -531,7 +531,6 @@ class TestHFC134a(PureFluidTestCases): # different methods for satisfying the phase equilibrium condition g_l = g_v. # Cantera uses the actual equation of state, while the tabulated values given # by Reynolds are based on the given P_sat(T_sat) relations. -@pytest.mark.usefixtures("setup_fluid") class TestCarbonDioxide(PureFluidTestCases): states = [ StateData('liquid', 230.0, 2.0, rho=1132.4, h=28.25, s=0.1208), @@ -546,7 +545,6 @@ class TestCarbonDioxide(PureFluidTestCases): tol = Tolerances(2e-3, 2e-3, 2e-3) -@pytest.mark.usefixtures("setup_fluid") class TestHeptane(PureFluidTestCases): states = [ StateData('liquid', 300.0, 0.006637, v=0.001476, h=0.0, s=0.0, relax=True), # sat @@ -562,7 +560,6 @@ class TestHeptane(PureFluidTestCases): # para-hydrogen -@pytest.mark.usefixtures("setup_fluid") class TestHydrogen(PureFluidTestCases): states = [ StateData('liquid', 18.0, 0.04807, v=0.013660, h=30.1, s=1.856, relax=True), # sat @@ -578,7 +575,6 @@ class TestHydrogen(PureFluidTestCases): name = "hydrogen" tol = Tolerances(2e-3, 2e-3, 2e-3, 2e-4) -@pytest.mark.usefixtures("setup_fluid") class TestMethane(PureFluidTestCases): states = [ StateData('liquid', 100.0, 0.50, rho=439.39, h=31.65, s=0.3206), @@ -593,7 +589,6 @@ class TestMethane(PureFluidTestCases): name = "methane" tol = Tolerances(2e-3, 2e-3, 2e-3) -@pytest.mark.usefixtures("setup_fluid") class TestNitrogen(PureFluidTestCases): states = [ StateData('liquid', 80.0, 0.1370, v=0.001256, h=33.50, s=0.4668, relax=True), # sat @@ -607,7 +602,6 @@ class TestNitrogen(PureFluidTestCases): name = "nitrogen" tol = Tolerances(2e-3, 2e-3, 2e-3) -@pytest.mark.usefixtures("setup_fluid") class TestOxygen(PureFluidTestCases): states = [ StateData('liquid', 80.0, 0.03009, v=0.000840, h=42.56, s=0.6405, relax=True), # sat @@ -622,7 +616,6 @@ class TestOxygen(PureFluidTestCases): name = "oxygen" tol = Tolerances(2e-3, 2e-3, 2e-3) -@pytest.mark.usefixtures("setup_fluid") class TestWater(PureFluidTestCases): states = [ StateData('liquid', 295.0, 0.002620, v=0.0010025, h=90.7, s=0.3193, relax=True), diff --git a/test/python/test_reaction.py b/test/python/test_reaction.py index 36d204f489..5b9871d9d7 100644 --- a/test/python/test_reaction.py +++ b/test/python/test_reaction.py @@ -11,7 +11,7 @@ class TestImplicitThirdBody: - """ tests for three-body reactions with specified collision partner """ + """Tests for three-body reactions with specified collision partner """ @pytest.fixture(scope='class') def gas(self): @@ -158,17 +158,8 @@ def test_user_override(self, gas): assert rxn.input_data["type"] == "elementary" -@pytest.fixture(scope='class') -def solution(request): - request.cls.soln = ct.Solution("kineticsfromscratch.yaml") - -@pytest.fixture(scope='function') -def setup_reaction_rate_tests(request, solution): - request.cls.soln.X = "H2:0.1, H2O:0.2, O2:0.7, O:1e-4, OH:1e-5, H:2e-5, H2O2:1e-7" - request.cls.soln.TP = 900, 2 * ct.one_atm - class ReactionRateTests: - # test suite for reaction rate expressions + """Test suite for reaction rate expressions""" _cls = None # reaction rate object to be tested _type = None # name of reaction rate @@ -177,6 +168,16 @@ class ReactionRateTests: _input = None # input parameters (dict corresponding to YAML) _yaml = None # yaml string specifying parameters + @pytest.fixture(scope='class') + def solution(self): + return ct.Solution("kineticsfromscratch.yaml") + + @pytest.fixture(scope='function', autouse=True) + def setup_test_data(self, solution): + self.soln = solution + self.soln.X = "H2:0.1, H2O:0.2, O2:0.7, O:1e-4, OH:1e-5, H:2e-5, H2O2:1e-7" + self.soln.TP = 900, 2 * ct.one_atm + def finalize(self, rate): # perform additional setup after construction (whenever applicable) return rate @@ -286,9 +287,8 @@ def test_derivative_ddP(self): k1 = self.eval(rate) assert (k1 - k0) / deltaP == approx(drate[self._index], rel=1e-6) -@pytest.mark.usefixtures("setup_reaction_rate_tests") class TestArrheniusRate(ReactionRateTests): - # test Arrhenius rate expressions + """Test Arrhenius rate expressions""" _cls = ct.ArrheniusRate _type = "Arrhenius" @@ -340,9 +340,8 @@ def test_derivative_ddT_exact(self): dkdT_numeric = (rate(T + dT) - rate(T)) / dT assert dkdT == approx(dkdT_numeric, rel=1.e-6) -@pytest.mark.usefixtures("setup_reaction_rate_tests") class TestBlowersMaselRate(ReactionRateTests): - # test Blowers-Masel rate expressions + """Test Blowers-Masel rate expressions""" _cls = ct.BlowersMaselRate _type = "Blowers-Masel" @@ -379,9 +378,8 @@ def test_negative_A(self): def test_derivative_ddT(self): super().test_derivative_ddT() -@pytest.mark.usefixtures("setup_reaction_rate_tests") class TestTwoTempPlasmaRate(ReactionRateTests): - # test TwoTempPlasma rate expressions + """Test TwoTempPlasma rate expressions""" _cls = ct.TwoTempPlasmaRate _type = "two-temperature-plasma" @@ -431,7 +429,7 @@ def test_derivative_ddT(self): class TestTwoTempPlasmaRateShort(TestTwoTempPlasmaRate): - # test TwoTempPlasma rate expressions + """Test TwoTempPlasma rate expressions""" _index = 12 _input = {"rate-constant": {"A": 17283, "b": -3.1}} @@ -450,18 +448,19 @@ def test_from_parts(self): self.check_rate(rate) -@pytest.fixture(scope='class') -def setup_falloff_rate_tests(request, solution): - param = request.cls._input["low-P-rate-constant"] - request.cls._parts["low"] = ct.Arrhenius(param["A"], param["b"], param["Ea"]) - param = request.cls._input["high-P-rate-constant"] - request.cls._parts["high"] = ct.Arrhenius(param["A"], param["b"], param["Ea"]) - class FalloffRateTests(ReactionRateTests): - # test Falloff rate expressions + """Test Falloff rate expressions""" + _type = "falloff" _n_data = [0] # list of valid falloff coefficient array lengths + @pytest.fixture(scope='class', autouse=True) + def setup_falloff_data(self): + param = self._input["low-P-rate-constant"] + self._parts["low"] = ct.Arrhenius(param["A"], param["b"], param["Ea"]) + param = self._input["high-P-rate-constant"] + self._parts["high"] = ct.Arrhenius(param["A"], param["b"], param["Ea"]) + def eval(self, rate): concm = self.soln.third_body_concentrations[self._index] return rate(self.soln.T, concm) @@ -507,9 +506,8 @@ def test_derivative_ddP(self): k1 = self.eval(rate) assert (k1 - k0) / deltaP == approx(drate[self._index], rel=1e-6) -@pytest.mark.usefixtures("setup_falloff_rate_tests") class TestLindemannRate(FalloffRateTests): - # test Lindemann rate expressions + """Test Lindemann rate expressions""" _cls = ct.LindemannRate _index = 7 @@ -532,9 +530,8 @@ def test_falloff_function(self): # Falloff-function for Lindemann is unity by definition assert np.isclose(rate.falloff_function(300, 0.), 1.) -@pytest.mark.usefixtures("setup_falloff_rate_tests") class TestTroeRate(FalloffRateTests): - # test Troe rate expressions + """Test Troe rate expressions""" _cls = ct.TroeRate _index = 2 @@ -564,9 +561,8 @@ def test_unexpected_parameter(self): with pytest.warns(UserWarning, match="Unexpected parameter value T2=0"): ct.ReactionRate.from_yaml(yaml) -@pytest.mark.usefixtures("setup_falloff_rate_tests") class TestSriRate(FalloffRateTests): - # test SRI rate expressions + """Test SRI rate expressions""" _cls = ct.SriRate _index = 8 @@ -585,9 +581,8 @@ class TestSriRate(FalloffRateTests): """ _n_data = [3, 5] -@pytest.mark.usefixtures("setup_falloff_rate_tests") class TestTsangRate(FalloffRateTests): - # test Tsang rate expressions + """Test Tsang rate expressions""" _cls = ct.TsangRate _index = 9 @@ -606,16 +601,9 @@ class TestTsangRate(FalloffRateTests): """ _n_data = [1, 2] -@pytest.fixture(scope='class') -def setup_plog_rate_tests(request, solution): - request.cls._parts = { - "rates": [(rc["P"], ct.Arrhenius(rc["A"], rc["b"], rc["Ea"])) - for rc in request.cls._input["rate-constants"]], - } -@pytest.mark.usefixtures("setup_plog_rate_tests") class TestPlogRate(ReactionRateTests): - # test Plog rate expressions + """Test Plog rate expressions""" _cls = ct.PlogRate _type = "pressure-dependent-Arrhenius" @@ -634,6 +622,13 @@ class TestPlogRate(ReactionRateTests): - {P: 100.0 atm, A: 5.9632e+56, b: -11.529, Ea: 5.25996e+04 cal/mol} """ + @pytest.fixture(scope='function', autouse=True) + def setup_plog_data(self, setup_test_data): + self._parts = { + "rates": [(rc["P"], ct.Arrhenius(rc["A"], rc["b"], rc["Ea"])) + for rc in self._input["rate-constants"]], + } + def eval(self, rate): # check evaluation as a function of temperature and pressure return rate(self.soln.T, self.soln.P) @@ -689,17 +684,8 @@ def test_standalone(self): ct.ReactionRate.from_yaml(yaml) -@pytest.fixture(scope='class') -def setup_test_chebyshev_rate(request, solution): - request.cls._parts = { - "pressure_range": request.cls._input["pressure-range"], - "temperature_range": request.cls._input["temperature-range"], - "data": request.cls._input["data"], - } - -@pytest.mark.usefixtures("setup_test_chebyshev_rate") class TestChebyshevRate(ReactionRateTests): - # test Chebyshev rate expressions + """Test Chebyshev rate expressions""" _cls = ct.ChebyshevRate _type = "Chebyshev" @@ -719,6 +705,14 @@ class TestChebyshevRate(ReactionRateTests): - [0.3177, 0.26889, 0.094806, -7.6385e-03] """ + @pytest.fixture(scope='function', autouse=True) + def setup_chebyshev_data(self, setup_test_data): + self._parts = { + "pressure_range": self._input["pressure-range"], + "temperature_range": self._input["temperature-range"], + "data": self._input["data"], + } + def eval(self, rate): # check evaluation as a function of temperature and pressure return rate(self.soln.T, self.soln.P) @@ -736,19 +730,20 @@ def test_from_parts(self): assert rate.n_temperature == rate.data.shape[0] -@pytest.fixture(scope='class') -def setup_surface_reaction_rate_tests(request): - request.cls.soln = ct.Interface("kineticsfromscratch.yaml", "Pt_surf", transport_model=None) - request.cls.gas = request.cls.soln.adjacent["ohmech"] +class SurfaceReactionRateTests(ReactionRateTests): + """Test suite for surface reaction rate expressions""" -@pytest.fixture(scope='function') -def surface_rate_reaction_data(request, setup_surface_reaction_rate_tests): - request.cls.soln.TP = 900, ct.one_atm - request.cls.gas.X = "H2:0.05, H2O:0.01, O:1e-4, OH: 1e5, H:2e-5, O2:0.21, AR:0.79" - request.cls.gas.TP = 900, ct.one_atm + @pytest.fixture(scope='class', autouse=True) + def solution(self): + return ct.Interface("kineticsfromscratch.yaml", "Pt_surf", transport_model=None) -class SurfaceReactionRateTests(ReactionRateTests): - # test suite for surface reaction rate expressions + @pytest.fixture(scope='function', autouse=True) + def setup_test_data(self, solution): + self.soln = solution + self.gas = self.soln.adjacent['ohmech'] + self.soln.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, species=True): # evaluate rate expression @@ -820,7 +815,6 @@ def test_sticking_coeffs(self): assert rate.sticking_order == self._sticking_order assert rate.sticking_weight == approx(weight) -@pytest.mark.usefixtures("surface_rate_reaction_data") class TestSurfaceArrheniusRate(SurfaceReactionRateTests): # test interface-Arrhenius rate expressions without coverage dependency @@ -834,7 +828,6 @@ class TestSurfaceArrheniusRate(SurfaceReactionRateTests): type: interface-Arrhenius """ -@pytest.mark.usefixtures("surface_rate_reaction_data") class TestInterfaceArrheniusRate(SurfaceReactionRateTests): # test interface-Arrhenius rate expressions with coverage dependency @@ -853,7 +846,6 @@ class TestInterfaceArrheniusRate(SurfaceReactionRateTests): type: interface-Arrhenius """ -@pytest.mark.usefixtures("surface_rate_reaction_data") class TestStickingRate(StickingReactionRateTests): # test surface-sticking rate expressions without coverage dependency @@ -869,7 +861,6 @@ class TestStickingRate(StickingReactionRateTests): type: sticking-Arrhenius """ -@pytest.mark.usefixtures("surface_rate_reaction_data") class TestCoverageStickingRate(StickingReactionRateTests): # test sticking rate expressions with coverage dependency @@ -890,7 +881,6 @@ class TestCoverageStickingRate(StickingReactionRateTests): type: sticking-Arrhenius """ -@pytest.mark.usefixtures("surface_rate_reaction_data") class TestMotzWiseStickingRate(StickingReactionRateTests): # test interface reaction with coverages @@ -910,7 +900,6 @@ class TestMotzWiseStickingRate(StickingReactionRateTests): type: sticking-Arrhenius """ -@pytest.mark.usefixtures("surface_rate_reaction_data") class TestSurfaceBMRate(SurfaceReactionRateTests): # test coverage-Blowers-Masel rate expressions with coverage dependency @@ -926,7 +915,6 @@ class TestSurfaceBMRate(SurfaceReactionRateTests): type: interface-Blowers-Masel """ -@pytest.mark.usefixtures("surface_rate_reaction_data") class TestSurfaceBMRate(SurfaceReactionRateTests): # test coverage-Blowers-Masel rate expressions with coverage dependency @@ -945,7 +933,6 @@ class TestSurfaceBMRate(SurfaceReactionRateTests): type: interface-Blowers-Masel """ -@pytest.mark.usefixtures("surface_rate_reaction_data") class TestBMStickate(StickingReactionRateTests): # test coverage-Blowers-Masel stick expressions with coverage dependency @@ -1182,7 +1169,7 @@ def check_equal(self, one, two): @pytest.fixture(scope='class') def setup_elementary_tests(request, setup_reaction_tests): """ - Uses the base classes fixture as a dependency to make sure the + Uses the base class's fixture as a dependency to make sure the execution order is correct. """ request.cls._rate_obj = ct.ArrheniusRate(**request.cls._rate) @@ -1478,8 +1465,10 @@ def check_rates(self, rates, other): for index, item in enumerate(rates): P, rate = item assert P == approx(other[index][0]) - assert rate.pre_exponential_factor == approx(other[index][1].pre_exponential_factor) - assert rate.temperature_exponent == approx(other[index][1].temperature_exponent) + assert (rate.pre_exponential_factor + == approx(other[index][1].pre_exponential_factor)) + assert (rate.temperature_exponent + == approx(other[index][1].temperature_exponent)) assert rate.activation_energy == approx(other[index][1].activation_energy) @pytest.mark.usefixtures("reaction_data") @@ -1518,22 +1507,14 @@ class TestChebyshev(ReactionTests): def eval_rate(self, rate): return rate(self.soln.T, self.soln.P) - -@pytest.fixture(scope='function') -def setup_custom_tests(request, reaction_data): - """ - statidmethod is used on the lambda function to prevent it from being passed - self as the first argument. - """ - request.cls._rate = staticmethod(lambda T: 38.7 * T**2.7 * np.exp(-3150.15428/T)) - -@pytest.mark.usefixtures("setup_custom_tests") +@pytest.mark.usefixtures("reaction_data") class TestCustom(ReactionTests): # test Custom reaction # probe O + H2 <=> H + OH _rate_cls = ct.CustomRate _equation = "H2 + O <=> H + OH" + _rate = staticmethod(lambda T: 38.7 * T**2.7 * np.exp(-3150.15428/T)) _rate_obj = ct.CustomRate(lambda T: 38.7 * T**2.7 * np.exp(-3150.15428/T)) _index = 0 _rate_type = "custom-rate-function" diff --git a/test/python/test_reactor.py b/test/python/test_reactor.py index bf8a67c1c1..193c0f13a6 100644 --- a/test/python/test_reactor.py +++ b/test/python/test_reactor.py @@ -105,8 +105,10 @@ def test_component_names(self): N = self.net.n_vars // 2 for i in range(N): assert self.r1.component_index(self.r1.component_name(i)) == i - assert self.net.component_name(i) == '{}: {}'.format(self.r1.name, self.r1.component_name(i)) - assert self.net.component_name(N+i) == '{}: {}'.format(self.r2.name, self.r2.component_name(i)) + assert (self.net.component_name(i) + == '{}: {}'.format(self.r1.name, self.r1.component_name(i))) + assert (self.net.component_name(N+i) + == '{}: {}'.format(self.r2.name, self.r2.component_name(i))) def test_independent_variable(self): self.make_reactors(independent=False, n_reactors=1) @@ -216,8 +218,6 @@ def test_timestepping(self): assert t - tPrev <= 1.0001 * dt_max assert t == approx(self.net.time) - #assert self.net.time == approx(tEnd) - def test_maxsteps(self): self.make_reactors() @@ -400,11 +400,11 @@ def test_heat_transfer2(self): self.r2.volume = 0.25 w = self.add_wall(U=100, A=0.5) - assert w.heat_transfer_coeff * w.area * (self.r1.T - self.r2.T) == approx( - w.heat_rate) + assert (w.heat_transfer_coeff * w.area * (self.r1.T - self.r2.T) + == approx(w.heat_rate)) self.net.advance(1.0) - assert w.heat_transfer_coeff * w.area * (self.r1.T - self.r2.T) == approx( - w.heat_rate) + assert (w.heat_transfer_coeff * w.area * (self.r1.T - self.r2.T) + == approx(w.heat_rate)) T1b = self.r1.T T2b = self.r2.T @@ -2443,7 +2443,8 @@ def calc_dtdh(self, species): return dtdp # @todo: replace np.trapz with np.trapezoid when dropping support for NumPy 1.x - @pytest.mark.skip(reason="Integration of sensitivity ODEs is unreliable, see: https://github.com/Cantera/enhancements/issues/55") + @pytest.mark.skip(reason="Integration of sensitivity ODEs is unreliable, " + "see: https://github.com/Cantera/enhancements/issues/55") @pytest.mark.filterwarnings("ignore:`trapz` is deprecated") def test_ignition_delay_sensitivity(self): species = ('H2', 'H', 'O2', 'H2O2', 'H2O', 'OH', 'HO2') @@ -2455,12 +2456,9 @@ def test_ignition_delay_sensitivity(self): assert dtigdh_cvodes[i] == approx(dtigdh, rel=5e-2, abs=1e-14) -@pytest.fixture(scope='class') -def setup_combustor_tests(request, test_data_path): - request.cls.referenceFile = test_data_path / "CombustorTest-integrateWithAdvance.csv" @pytest.fixture(scope='function') -def setup_combustor_tests_data(request, setup_combustor_tests): +def setup_combustor_tests_data(request): gas = ct.Solution('h2o2.yaml', transport_model=None) # create a reservoir for the fuel inlet, and set to pure methane. @@ -2518,8 +2516,9 @@ class CombustorTests: with some simplifications so that they run faster and produce more consistent output. """ + referenceFile = "CombustorTest-integrateWithAdvance.csv" - def test_integrateWithStep(self): + def test_integrateWithStep(self, test_data_path): tnow = 0.0 tfinal = 0.25 self.data = [] @@ -2529,11 +2528,11 @@ def test_integrateWithStep(self): list(self.combustor.thermo.X)) assert tnow >= tfinal - bad = compareProfiles(self.referenceFile, self.data, - rtol=1e-3, atol=1e-9) + bad = compareProfiles(test_data_path / self.referenceFile, self.data, + rtol=1e-3, atol=1e-9) assert not bad, bad - def test_integrateWithAdvance(self, saveReference=False): + def test_integrateWithAdvance(self, test_data_path, saveReference=False): self.data = [] for t in np.linspace(0, 0.25, 101)[1:]: self.sim.advance(t) @@ -2541,13 +2540,14 @@ def test_integrateWithAdvance(self, saveReference=False): list(self.combustor.thermo.X)) if saveReference: - np.savetxt(self.referenceFile, np.array(self.data), '%11.6e', ', ') + np.savetxt(test_data_path / self.referenceFile, np.array(self.data), + '%11.6e', ', ') else: - bad = compareProfiles(self.referenceFile, self.data, - rtol=1e-6, atol=1e-12) + bad = compareProfiles(test_data_path / self.referenceFile, self.data, + rtol=1e-6, atol=1e-12) assert not bad, bad - def test_invasive_mdot_function(self): + def test_invasive_mdot_function(self, test_data_path): def igniter_mdot(t, t0=0.1, fwhm=0.05, amplitude=0.1): # Querying properties of the igniter changes the state of the # underlying ThermoPhase object, but shouldn't affect the @@ -2562,17 +2562,13 @@ def igniter_mdot(t, t0=0.1, fwhm=0.05, amplitude=0.1): self.data.append([t, self.combustor.T] + list(self.combustor.thermo.X)) - bad = compareProfiles(self.referenceFile, self.data, - rtol=1e-6, atol=1e-12) + bad = compareProfiles(test_data_path / self.referenceFile, self.data, + rtol=1e-6, atol=1e-12) assert not bad, bad -@pytest.fixture(scope='class') -def setup_wall_tests(request, test_data_path): - request.cls.referenceFile = test_data_path / "WallTest-integrateWithAdvance.csv" - @pytest.fixture(scope='function') -def setup_wall_tests_data(request, setup_wall_tests): +def setup_wall_tests_data(request): # reservoir to represent the environment gas0 = ct.Solution("air.yaml") gas0.TP = 300, ct.one_atm @@ -2607,8 +2603,9 @@ class WallTests: with some simplifications so that they run faster and produce more consistent output. """ + referenceFile = "WallTest-integrateWithAdvance.csv" - def test_integrateWithStep(self): + def test_integrateWithStep(self, test_data_path): tnow = 0.0 tfinal = 0.01 self.data = [] @@ -2620,11 +2617,11 @@ def test_integrateWithStep(self): self.r1.volume, self.r2.volume]) assert tnow >= tfinal - bad = compareProfiles(self.referenceFile, self.data, - rtol=1e-3, atol=1e-8) + bad = compareProfiles(test_data_path / self.referenceFile, self.data, + rtol=1e-3, atol=1e-8) assert not bad, bad - def test_integrateWithAdvance(self, saveReference=False): + def test_integrateWithAdvance(self, test_data_path, saveReference=False): self.data = [] for t in np.linspace(0, 0.01, 200)[1:]: self.sim.advance(t) @@ -2634,10 +2631,11 @@ def test_integrateWithAdvance(self, saveReference=False): self.r1.volume, self.r2.volume]) if saveReference: - np.savetxt(self.referenceFile, np.array(self.data), '%11.6e', ', ') + np.savetxt(test_data_path / self.referenceFile, np.array(self.data), + '%11.6e', ', ') else: - bad = compareProfiles(self.referenceFile, self.data, - rtol=2e-5, atol=1e-9) + bad = compareProfiles(test_data_path / self.referenceFile, self.data, + rtol=2e-5, atol=1e-9) assert not bad, bad @@ -2731,11 +2729,10 @@ def test_ConstPressureReactor(self): @pytest.fixture(scope='function') -def setup_advance_converages_data(request, cantera_data_path): +def setup_advance_converages_data(request): mechanism_file = 'ptcombust.yaml' interface_phase = 'Pt_surf' - request.cls.surf = ct.Interface(f'{cantera_data_path}/{mechanism_file}', interface_phase) - #request.cls.surf = ct.Interface(mechanism_file, interface_phase) + request.cls.surf = ct.Interface(mechanism_file, interface_phase) request.cls.gas = request.cls.surf.adjacent["gas"] @pytest.mark.usefixtures('setup_advance_converages_data') diff --git a/test/python/test_thermo.py b/test/python/test_thermo.py index 41371355d4..be9bc5b969 100644 --- a/test/python/test_thermo.py +++ b/test/python/test_thermo.py @@ -1114,6 +1114,7 @@ def test_nondimensional(self): @pytest.fixture(scope='function') def setup_interface_tests(request): request.cls.interface = ct.Interface("diamond.yaml", "diamond_100") + @pytest.mark.usefixtures("setup_interface_tests") class TestInterfacePhase: diff --git a/test/python/test_transport.py b/test/python/test_transport.py index 4cb43c32a9..fde443ca0a 100644 --- a/test/python/test_transport.py +++ b/test/python/test_transport.py @@ -209,18 +209,10 @@ def test_transport_polynomial_fits_diffusion(self, phase): class TestIonTransport: - @pytest.fixture(scope='class') - def initial_conditions(self): - return { - 'T': 2237, - 'P': ct.one_atm, - 'X': 'O2:0.7010, H2O:0.1885, CO2:9.558e-2' - } - @pytest.fixture(scope='function') - def gas(self, initial_conditions): + def gas(self): gas = ct.Solution('ch4_ion.yaml') - gas.TPX = initial_conditions['T'], initial_conditions['P'], initial_conditions['X'] + gas.TPX = 2237, ct.one_atm, 'O2:0.7010, H2O:0.1885, CO2:9.558e-2' return gas @pytest.fixture(scope='function') @@ -397,10 +389,6 @@ class TestWaterTransport: the critical point. """ - @pytest.fixture(scope='class') - def water(self): - return ct.Water() - @pytest.fixture(scope='class') def water(self): return ct.Water() @@ -518,10 +506,6 @@ def gas(self): gas = ct.Solution("h2o2.yaml") return gas - @pytest.fixture(scope='function', autouse=True) - def initial_condition(self, gas): - gas.X = 'H2O:0.6, H2:0.4' - def test_read(self, gas): tr = gas.species('H2').transport assert tr.geometry == 'linear' diff --git a/test/python/utilities.py b/test/python/utilities.py index 1e9752f081..3554971f03 100644 --- a/test/python/utilities.py +++ b/test/python/utilities.py @@ -37,8 +37,7 @@ def compare(data, reference_file, rtol=1e-8, atol=1e-12): assert ref[i] == approx(data[i], rel=rtol, abs=atol) else: # Generate the output file for the first time - warnings.warn('Generating test data file:' + - Path(reference_file).resolve()) + warnings.warn('Generating test data file:' + Path(reference_file).resolve()) np.savetxt(reference_file, data, fmt='%.10e') def compareProfiles(reference, sample, rtol=1e-5, atol=1e-12, xtol=1e-5):