Files
cantera/test/python/test_convert.py
T

1760 lines
78 KiB
Python

from __future__ import annotations
import itertools
from pathlib import Path
import pytest
from pytest import approx
import cantera as ct
from cantera import ck2yaml, cti2yaml, ctml2yaml, yaml2ck, lxcat2yaml
from .utilities import load_yaml
class Testck2yaml:
@pytest.fixture(autouse=True)
def inject_fixtures(self, capsys, test_data_path):
self._capsys = capsys
self.test_data_path = test_data_path
def convert(self, inputFile, thermo=None, transport=None,
surface=None, output=None, quiet=True, extra=None, **kwargs):
if output is None:
output = Path(inputFile or thermo).stem # strip '.inp'
if inputFile is not None:
inputFile = self.test_data_path / inputFile
if thermo is not None:
thermo = self.test_data_path / thermo
if transport is not None:
transport = self.test_data_path / transport
if surface is not None:
surface = self.test_data_path / surface
if extra is not None:
extra = self.test_data_path / extra
output = self.test_work_path / (output + "-from-ck.yaml")
output.unlink(missing_ok=True)
ck2yaml.convert(inputFile, thermo_file=thermo,
transport_file=transport, surface_file=surface, out_name=output,
extra_file=extra, quiet=quiet, **kwargs)
return output
def checkConversion(self, refFile, testFile):
ref = ct.Solution(refFile)
gas = ct.Solution(testFile)
assert ref.element_names == gas.element_names
assert ref.species_names == gas.species_names
coeffs_ref = ref.reactant_stoich_coeffs
coeffs_gas = gas.reactant_stoich_coeffs
assert coeffs_gas.shape == coeffs_ref.shape
assert (coeffs_gas == coeffs_ref).all()
compositionA = [[ref.n_atoms(i,j) for j in range(ref.n_elements)]
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
return ref, gas
def checkThermo(self, ref, gas, temperatures):
for T in temperatures:
ref.TP = T, ct.one_atm
gas.TP = T, ct.one_atm
ref_cp = ref.standard_cp_R
gas_cp = gas.standard_cp_R
ref_h = ref.standard_enthalpies_RT
gas_h = gas.standard_enthalpies_RT
ref_s = ref.standard_entropies_R
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), '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):
ref.TP = T, P
gas.TP = T, P
ref_kf = ref.forward_rate_constants
ref_kr = ref.reverse_rate_constants
gas_kf = gas.forward_rate_constants
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), 'kf' + message
assert ref_kr[i] == approx(gas_kr[i], rel=tol), 'kr' + message
@pytest.mark.slow_test
def test_gri30(self):
output = self.convert('gri30.inp', thermo='gri30_thermo.dat',
transport='gri30_tran.dat', output='gri30_test')
ref, gas = self.checkConversion("gri30.yaml", output)
self.checkKinetics(ref, gas, [300, 1500], [5e3, 1e5, 2e6])
def test_soot(self):
output = self.convert("soot.inp", thermo="soot-therm.dat", output="soot_test")
ref, gas = self.checkConversion("soot.yaml", output)
self.checkThermo(ref, gas, [300, 1100])
self.checkKinetics(ref, gas, [300, 1100], [5e3, 1e5, 2e6])
def test_pdep(self):
output = self.convert('pdep-test.inp')
ref, gas = self.checkConversion('pdep-test.yaml', output)
self.checkKinetics(ref, gas, [300, 800, 1450, 2800], [5e3, 1e5, 2e6])
def test_species_only(self):
self.convert(None, thermo='dummy-thermo.dat', output='dummy-thermo')
yaml = ("{phases: [{name: gas, species: "
"[{dummy-thermo-from-ck.yaml/species: [R1A, R1B, P1]}], "
"thermo: ideal-gas}]}")
gas = ct.Solution(yaml=yaml)
assert gas.n_species == 3
assert gas.n_reactions == 0
def test_missing_thermo(self):
with pytest.raises(ck2yaml.InputError):
self.convert('h2o2_missingThermo.inp')
captured = self._capsys.readouterr()
assert "No thermo data found for species 'H2'" in captured.out
def test_duplicate_thermo(self):
with pytest.raises(ck2yaml.InputError):
self.convert('duplicate-thermo.inp')
captured = self._capsys.readouterr()
assert "Found additional thermo entry for species 'bar'" in captured.out
assert "2 additional errors about redundant thermo data" in captured.out
output = self.convert('duplicate-thermo.inp', permissive=True, quiet=False)
captured = self._capsys.readouterr()
assert "2 additional warnings about redundant thermo data" in captured.out
gas = ct.Solution(output)
assert gas.n_species == 4
assert gas.n_reactions == 2
def test_bad_elemental_composition(self):
with pytest.raises(ck2yaml.InputError,
match="Error parsing elemental composition"):
self.convert(None, thermo='bad-nasa7-composition.dat')
captured = self._capsys.readouterr()
assert "Error parsing elemental composition" in captured.out
def test_bad_nasa7_temperature_ranges(self):
with pytest.raises(ck2yaml.InputError):
self.convert(None, thermo='bad-nasa7-Trange.dat')
captured = self._capsys.readouterr()
assert "Only one temperature range defined" in captured.out
def test_bad_nasa7_formatting(self):
with pytest.raises(ck2yaml.InputError):
self.convert(None, thermo='bad-nasa7-formatting.dat')
captured = self._capsys.readouterr()
assert "Lines could not be parsed as a NASA7 entry" in captured.out
assert "line 7" in captured.out # bad entry for O2
assert "line 15" in captured.out # bad entry for H2
assert "line 24" in captured.out # bad entry for OH
def test_bad_nasa7_formatting_warn(self):
self.convert(None, thermo='bad-nasa7-formatting.dat', permissive=True)
yaml = ("{phases: [{name: gas, species: "
"[{bad-nasa7-formatting-from-ck.yaml/species: all}], "
"thermo: ideal-gas}]}")
gas = ct.Solution(yaml=yaml)
assert set(gas.species_names) == {"O", "H", "H2O"}
def test_bad_nasa7_float(self):
with pytest.raises(ck2yaml.InputError):
self.convert(None, thermo='bad-nasa7-float.dat')
captured = self._capsys.readouterr()
assert "could not convert string to float: '-0.35101023e+ 0'" in captured.out
def test_bad_nasa9_temperature_ranges(self):
with pytest.raises(ck2yaml.InputError,
match="non-adjacent temperature ranges"):
self.convert(None, thermo='bad-nasa9-Trange.dat')
self._capsys.readouterr()
def test_bad_nasa9_coeffs(self):
with pytest.raises(ck2yaml.InputError) as info:
self.convert(None, thermo='bad-nasa9-coeffs.dat')
err_text = str(info.value)
captured = self._capsys.readouterr()
for out in err_text, captured.out:
assert "Error while reading thermo entry" in out
assert "\nALCL3" in out
assert "could not convert string to float" in out
def test_duplicate_species(self):
with pytest.raises(ck2yaml.InputError):
self.convert('duplicate-species.inp')
captured = self._capsys.readouterr()
assert "multiple declarations for species 'bar'" in captured.out
assert "Suppressed 2 additional errors about redundant species" in captured.out
output = self.convert('duplicate-species.inp', permissive=True, quiet=False)
captured = self._capsys.readouterr()
assert "Ignoring redundant declaration for species 'bar'" in captured.out
assert "Suppressed 2 additional warnings" in captured.out
gas = ct.Solution(output)
assert gas.species_names == ['foo','bar','baz']
def test_pathologicalSpeciesNames(self):
output = self.convert('species-names.inp')
gas = ct.Solution(output)
assert gas.n_species == 10
assert gas.species_name(0) == '(Parens)'
assert gas.species_name(1) == '@#$%^-2'
assert gas.species_index('co:lons:') == 2
assert gas.species_name(3) == '[xy2]*{.}'
assert gas.species_name(4) == 'plus+'
assert gas.species_name(5) == 'eq=uals'
assert gas.species_name(6) == 'plus'
assert gas.species_name(7) == 'trans_butene'
assert gas.species_name(8) == 'co'
assert gas.species_name(9) == "amp&ersand"
assert gas.n_reactions == 13
nu = gas.product_stoich_coeffs - gas.reactant_stoich_coeffs
assert list(nu[:,0]) == [-1, -1, 0, 2, 0, 0, 0, 0, 0, 0]
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]
def test_unterminatedSections(self):
output = self.convert('unterminated-sections.inp', quiet=False)
captured = self._capsys.readouterr()
for section in ["ELEMENTS", "SPECIES", "THERMO", "REACTIONS"]:
assert f"{section} section implicitly ended" in captured.out
gas = ct.Solution(output)
assert gas.n_species == 3
assert gas.n_reactions == 2
def test_unterminatedSections2(self):
output = self.convert('unterminated-sections2.inp', quiet=False)
captured = self._capsys.readouterr()
for section in ["ELEMENTS", "SPECIES", "THERMO", "REACTIONS"]:
assert f"{section} section implicitly ended" in captured.out
gas = ct.Solution(output)
assert gas.n_species == 3
assert gas.n_reactions == 2
def test_unrecognized_section(self):
with pytest.raises(ck2yaml.InputError):
self.convert('unrecognized-section.inp', thermo='dummy-thermo.dat',
permissive=True)
captured = self._capsys.readouterr()
assert "unrecognized keyword 'SPAM'" in captured.out
def test_nasa9(self):
output = self.convert("nasa9-test.inp", thermo="nasa9-test-therm.dat")
ref, gas = self.checkConversion("nasa9-test.yaml", output)
self.checkThermo(ref, gas, [300, 500, 1200, 5000])
def test_nasa9_subset(self):
output = self.convert("nasa9-test-subset.inp", thermo="nasa9-test-therm.dat")
ref, gas = self.checkConversion("nasa9-test-subset.yaml", output)
self.checkThermo(ref, gas, [300, 500, 1200, 5000])
def test_nasa9_embedded(self):
with pytest.raises(ck2yaml.InputError):
self.convert("nasa9-embedded.inp")
captured = self._capsys.readouterr()
assert "SPECIES section implicitly ended" in captured.out
assert "THERMO NASA9 section implicitly ended" in captured.out
assert "Found additional thermo entry for species 'AR'" in captured.out
assert "Suppressed 2 additional errors" in captured.out
output = self.convert("nasa9-embedded.inp", quiet=False, permissive=True)
captured = self._capsys.readouterr()
assert "Suppressed 2 additional warnings" in captured.out
ref, gas = self.checkConversion("nasa9-embedded.yaml", output)
self.checkThermo(ref, gas, [300, 500, 1200, 5000])
self.checkKinetics(ref, gas, [300, 1200, 9000], [5e3, 1e5, 2e6])
def test_sri_falloff(self):
output = self.convert("sri-falloff.inp", thermo="dummy-thermo.dat")
ref, gas = self.checkConversion("sri-falloff.yaml", output)
self.checkKinetics(ref, gas, [300, 800, 1450, 2800], [5e3, 1e5, 2e6])
def test_chemically_activated(self):
output = self.convert("chemically-activated-reaction.inp")
ref, gas = self.checkConversion("chemically-activated-reaction.yaml",
output)
self.checkKinetics(ref, gas, [300, 800, 1450, 2800], [5e3, 1e5, 2e6, 1e7])
def test_falloff_no_rate(self):
with pytest.raises(ck2yaml.InputError, match="implies pressure dependence"):
self.convert("falloff-error.inp", thermo="dummy-thermo.dat")
captured = self._capsys.readouterr()
def test_explicit_third_bodies(self):
output = self.convert("explicit-third-bodies.inp", thermo="dummy-thermo.dat")
ref, gas = self.checkConversion("explicit-third-bodies.yaml", output)
self.checkKinetics(ref, gas, [300, 800, 1450, 2800], [5e3, 1e5, 2e6])
def test_explicit_reverse_rate(self):
output = self.convert("explicit-reverse-rate.inp", thermo="dummy-thermo.dat")
ref, gas = self.checkConversion("explicit-reverse-rate.yaml", output)
self.checkKinetics(ref, gas, [300, 800, 1450, 2800], [5e3, 1e5, 2e6])
# Reactions with explicit reverse rate constants are transformed into
# 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[1] == 0.0
assert Rr[2] == 0.0
assert Rr[3] == 0.0
assert Rr[4] == 0.0
Rstoich = gas.reactant_stoich_coeffs
Pstoich = gas.product_stoich_coeffs
assert list(Rstoich[:, 0]) == list(Pstoich[:, 1])
assert list(Rstoich[:, 1]) == list(Pstoich[:, 0])
assert list(Rstoich[:, 2]) == list(Pstoich[:, 3])
assert list(Rstoich[:, 3]) == list(Pstoich[:, 2])
assert gas.n_reactions == 5
def test_explicit_forward_order(self):
output = self.convert("explicit-forward-order.inp", thermo="dummy-thermo.dat")
ref, gas = self.checkConversion("explicit-forward-order.yaml", output)
self.checkKinetics(ref, gas, [300, 800, 1450, 2800], [5e3, 1e5, 2e6])
def test_negative_order(self):
output = self.convert('negative-order.inp', thermo='dummy-thermo.dat',
quiet=False)
captured = self._capsys.readouterr()
assert "Negative reaction order" in captured.out
ref, gas = self.checkConversion("negative-order.yaml", output)
self.checkKinetics(ref, gas, [300, 800, 1450, 2800], [5e3, 1e5, 2e6])
def test_nonreactant_order(self):
output = self.convert("nonreactant-order.inp", thermo="dummy-thermo.dat",
quiet=False)
captured = self._capsys.readouterr()
assert "Non-reactant order" in captured.out
ref, gas = self.checkConversion("nonreactant-order.yaml", output)
self.checkKinetics(ref, gas, [300, 800, 1450, 2800], [5e3, 1e5, 2e6])
def test_negative_A_factor(self):
output = self.convert('negative-rate.inp', thermo='dummy-thermo.dat')
gas = ct.Solution(output) # Validate the mechanism
assert gas.reaction(4).rate.pre_exponential_factor < 0
assert gas.reaction(1).rate.pre_exponential_factor < 0
assert gas.reaction(2).rate.pre_exponential_factor < 0
assert gas.forward_rate_constants[5] < 0
def test_bad_troe_value(self):
with pytest.raises(ck2yaml.InputError):
self.convert('bad-troe.inp', thermo='dummy-thermo.dat')
captured = self._capsys.readouterr()
assert "Error while reading reaction in bad-troe.inp" in captured.out
assert "on line 10" in captured.out
assert "R1A+R1B(+M) <=> H+P1(+M)" in captured.out
assert "could not convert string to float" in captured.out
def test_invalid_reaction_equation(self):
with pytest.raises(ck2yaml.InputError):
self.convert('invalid-equation.inp', thermo='dummy-thermo.dat')
captured = self._capsys.readouterr()
assert "Unparsable line: 'R1A <-> R1B" in captured.out
def test_mismatched_third_body(self):
with pytest.raises(ck2yaml.InputError, match="Third bodies do not match"):
self.convert("mismatched-third-body.inp", thermo="gri30_thermo.dat")
captured = self._capsys.readouterr()
assert "Third bodies do not match: 'M' and 'AR'" in captured.out
@pytest.mark.slow_test
def test_reaction_units(self):
out_def = self.convert('units-default.inp', thermo='dummy-thermo.dat')
out_cus = self.convert('units-custom.inp', thermo='dummy-thermo.dat')
default, custom = self.checkConversion(out_def, out_cus)
self.checkKinetics(default, custom,
[300, 800, 1450, 2800], [5e0, 5e3, 1e5, 2e6, 1e8], 1e-7)
def test_float_stoich_coeffs(self):
output = self.convert('float-stoich.inp', thermo='dummy-thermo.dat')
gas = ct.Solution(output)
R = gas.reactant_stoich_coeffs
P = gas.product_stoich_coeffs
assert R[:,0] == approx([0, 1.5, 0.5, 0])
assert P[:,0] == approx([1, 0, 0, 1])
assert R[:,1] == approx([1, 0, 0, 1])
assert P[:,1] == approx([0, 0.33, 1.67, 0])
def test_unparsable_reaction(self):
with pytest.raises(ck2yaml.InputError, match="Unparsable line"):
self.convert("unparsable-reaction.inp", thermo="dummy-thermo.dat")
captured = self._capsys.readouterr()
def test_reaction_no_reactants(self):
with pytest.raises(ck2yaml.InputError):
self.convert("reaction-no-reactants.inp", thermo="dummy-thermo.dat")
captured = self._capsys.readouterr()
assert "No reactant species found in reaction equation" in captured.out
def test_undefined_species(self):
with pytest.raises(ck2yaml.InputError, match="Unexpected token"):
self.convert("undefined-species.inp", thermo="dummy-thermo.dat")
captured = self._capsys.readouterr()
assert "May be due to undeclared species 'XYZ'" in captured.out
def test_bad_chebyshev_params(self):
with pytest.raises(ck2yaml.InputError):
self.convert("bad-chebyshev-params.inp", thermo="dummy-thermo.dat")
captured = self._capsys.readouterr()
assert "Missing TCHEB entry for Chebyshev reaction" in captured.out
assert "Missing PCHEB entry for Chebyshev reaction" in captured.out
assert "Expected 3*5 = 15 but got 16." in captured.out
def test_bad_parameters_multiple_types(self):
with pytest.raises(ck2yaml.InputError,
match="contains parameters for more than one reaction type"):
self.convert("bad-reaction-params.inp", thermo="dummy-thermo.dat")
captured = self._capsys.readouterr()
def test_photon(self):
with pytest.raises(ck2yaml.InputError,
match="reversible reaction containing a product photon"):
self.convert("photo-reaction.inp", thermo="dummy-thermo.dat")
output = self.convert('photo-reaction.inp', thermo='dummy-thermo.dat',
permissive=True)
captured = self._capsys.readouterr()
ref, gas = self.checkConversion("photo-reaction.yaml", output)
self.checkKinetics(ref, gas, [300, 800, 1450, 2800], [5e3, 1e5, 2e6])
def test_photon_reactant_error(self):
with pytest.raises(ck2yaml.InputError,
match="Reactant photon not supported"):
self.convert("bad-photo-reaction.inp", thermo="dummy-thermo.dat")
captured = self._capsys.readouterr()
def test_transport_normal(self):
output = self.convert('h2o2.inp', transport='gri30_tran.dat',
output='h2o2_transport_normal')
gas = ct.Solution(output)
gas.TPX = 300, 101325, 'H2:1.0, O2:1.0'
assert gas.thermal_conductivity == approx(0.07663, rel=1e-4)
def test_transport_embedded(self):
output = self.convert('with-transport.inp')
gas = ct.Solution(output)
gas.X = [0.2, 0.3, 0.5]
D = gas.mix_diff_coeffs
for d in D:
assert d > 0.0
def test_transport_missing_species(self):
with pytest.raises(ck2yaml.InputError):
self.convert('h2o2.inp', transport='h2o2-missing-species-tran.dat',
output='h2o2_transport_missing_species')
captured = self._capsys.readouterr()
assert "No transport data for species 'H2O2'" in captured.out
def test_transport_extra_column_entries(self):
with pytest.raises(ck2yaml.InputError):
self.convert('h2o2.inp',
transport='h2o2-extra-column-entries-tran.dat',
output='h2o2_extra-column-entries-tran')
captured = self._capsys.readouterr()
assert "572.400" in captured.out
assert "6 transport parameters were expected, but found 7" in captured.out
def test_transport_duplicate_species(self):
with pytest.raises(ck2yaml.InputError):
self.convert('h2o2.inp',
transport='h2o2-duplicate-species-tran.dat',
output='h2o2_transport_duplicate_species')
captured = self._capsys.readouterr()
assert "Duplicate transport data for species 'H2O'" in captured.out
assert "2 additional errors about duplicate transport data" in captured.out
self.convert('h2o2.inp',
transport='h2o2-duplicate-species-tran.dat',
output='h2o2_transport_duplicate_species', permissive=True, quiet=False)
captured = self._capsys.readouterr()
assert "2 additional warnings about duplicate transport data" in captured.out
def test_transport_bad_geometry(self):
with pytest.raises(ck2yaml.InputError):
self.convert('h2o2.inp',
transport='h2o2-bad-geometry-tran.dat',
output='h2o2_transport_bad_geometry')
captured = self._capsys.readouterr()
assert ("Invalid geometry flag value '3' for species 'HO2'. "
"Flag value must be 0, 1, or 2.") in captured.out
with pytest.raises(ck2yaml.InputError):
self.convert('h2o2.inp',
transport='h2o2-character-geometry-tran.dat',
output='h2o2_transport_character_geometry')
captured = self._capsys.readouterr()
assert ("Invalid geometry flag 'a' for species 'HO2'. Flag must be an integer."
in captured.out)
def test_transport_float_geometry(self):
# Runs but issues a log message
self.convert('h2o2.inp',
transport='h2o2-float-geometry-tran.dat',
output='h2o2_transport_float_geometry', quiet=False)
captured = self._capsys.readouterr()
assert "Incorrect geometry flag" in captured.out
assert "automatically converted" in captured.out
# Runs with no logging message
output = self.convert('h2o2.inp',
transport='h2o2-float-geometry-tran.dat',
output='h2o2_transport_float_geometry', permissive=True, quiet=False)
captured = self._capsys.readouterr()
assert "Incorrect geometry flag" not in captured.out
gas = ct.Solution(output)
assert gas.species("H").transport.geometry == 'atom'
assert gas.species("H2").transport.geometry == 'linear'
assert gas.species("H2O").transport.geometry == 'nonlinear'
with pytest.raises(ck2yaml.InputError, match='Invalid float geometry flag'):
self.convert('h2o2.inp',
transport='h2o2-float-arithmetic-error-geometry-tran.dat',
output='h2o2_transport_float_geometry', permissive=True)
captured = self._capsys.readouterr()
assert "0.999999" in captured.out
def test_empty_reaction_section(self):
output = self.convert('h2o2_emptyReactions.inp')
gas = ct.Solution(output)
assert gas.n_species == 9
assert gas.n_reactions == 0
def test_reaction_comments1(self):
output = self.convert('pdep-test.inp')
text = output.read_text()
assert 'Generic mechanism header' in text
assert 'Single PLOG reaction' in text
assert 'Multiple PLOG expressions at the same pressure' in text
def test_reaction_comments2(self):
output = self.convert('explicit-third-bodies.inp', thermo='dummy-thermo.dat')
text = output.read_text()
assert 'An end of line comment' in text
assert 'A comment after the last reaction' in text
def test_custom_element(self):
output = self.convert('custom-elements.inp')
gas = ct.Solution(output)
assert gas.n_elements == 4
assert gas.atomic_weight(2) == approx(13.003)
assert gas.n_atoms('ethane', 'C') == 2
assert gas.n_atoms('CC', 'C') == 1
assert gas.n_atoms('CC', 'Ci') == 1
def test_surface_mech(self):
output = self.convert('surface1-gas.inp', surface='surface1.inp',
output='surface1')
surf = ct.Interface(output, 'PT_SURFACE')
gas = surf.adjacent["gas"]
assert gas.n_reactions == 11
assert surf.n_reactions == 15
assert surf.species('O2_Pt').size == 3
# Different units for rate constants in each input file
# 62.1 kJ/gmol = 6.21e7 J/kmol
assert gas.reaction(0).rate.activation_energy == approx(6.21e7)
# 67400 J/mol = 6.74e7 J/kmol
assert surf.reaction(1).rate.activation_energy == approx(6.74e7)
# Sticking coefficients
assert surf.reaction(4).duplicate
assert not isinstance(surf.reaction(1).rate, ct.StickingArrheniusRate)
assert isinstance(surf.reaction(2).rate, ct.StickingArrheniusRate)
assert surf.reaction(2).rate.motz_wise_correction
assert isinstance(surf.reaction(4).rate, ct.StickingArrheniusRate)
assert not surf.reaction(4).rate.motz_wise_correction
assert surf.reaction(6).rate.motz_wise_correction
# Coverage dependencies
covdeps = surf.reaction(1).rate.coverage_dependencies
assert len(covdeps) == 2
assert "H_Pt" in covdeps
assert covdeps["OH_Pt"]["m"] == 1.0
assert covdeps["H_Pt"]["E"] == approx(-6e6) # 6000 J/gmol = 6e6 J/kmol
def test_surface_mech2(self):
output = self.convert('surface1-gas-noreac.inp', surface='surface1.inp',
output='surface1-nogasreac')
gas = ct.Solution(output, 'gas')
surf = ct.Interface(output, 'PT_SURFACE', [gas])
assert gas.n_reactions == 0
assert surf.n_reactions == 15
covdeps = surf.reaction(1).rate.coverage_dependencies
assert "H_Pt" in covdeps
assert covdeps["OH_Pt"]["m"] == 1.0
assert covdeps["H_Pt"]["E"] == approx(-6e6)
def test_surf_bad_files(self):
with pytest.raises(ck2yaml.InputError):
self.convert('surface1.inp')
captured = self._capsys.readouterr()
assert "must be specified using the '--surface' option" in captured.out
with pytest.raises(SystemExit):
ck2yaml.main([
f"--surface={self.test_data_path}/surface1.inp",
f"--thermo={self.test_data_path}/dummy-thermo.dat"])
captured = self._capsys.readouterr()
assert "Cannot specify a surface mechanism without a gas phase" in captured.out
def test_surface_mech3(self):
# This tests the case where the thermo data for both the gas and surface are
# combined in a file separate from the gas and surface definitions. Also covers
# the case where the surface does not have a name specified.
output = self.convert('surface2-gas.inp', thermo='surface2-thermo.dat',
surface='surface2.inp', output='surface2')
captured = self._capsys.readouterr()
assert "SITE section implicitly ended" in captured.out
surf = ct.Interface(output, 'surface1')
assert surf.n_species == 6
assert surf.n_reactions == 15
assert surf.reaction(4).duplicate is True
def test_missing_site_density(self):
with pytest.raises(ck2yaml.InputError, match="no site density"):
self.convert("surface1-gas.inp", surface="missing-site-density.inp")
captured = self._capsys.readouterr()
def test_bad_reaction_option(self):
with pytest.raises(ck2yaml.InputError):
self.convert("surface1-gas.inp", surface="surface1-bad-option.inp")
captured = self._capsys.readouterr()
assert "Unrecognized token 'XYZ' on REACTIONS line" in captured.out
def test_third_body_plus_falloff_reactions(self):
output = self.convert("third_body_plus_falloff_reaction.inp")
gas = ct.Solution(output)
assert gas.n_reactions == 2
def test_blank_line_in_header(self):
output = self.convert("blank_line_in_header.inp")
gas = ct.Solution(output)
assert gas.n_reactions == 1
def test_missing_input_files(self):
with pytest.raises(IOError, match="Missing input file"):
self.convert('nonexistent-file-813.inp')
with pytest.raises(SystemExit):
ck2yaml.main([
f"--input=nonexistent-file-813.inp"])
captured = self._capsys.readouterr()
assert "Missing input file: 'nonexistent-file-813.inp'" in captured.out
def test_extra(self):
output = self.convert("h2o2.inp", output="h2o2_extra", extra="extra.yaml")
yml = load_yaml(output)
desc = yml['description'].split('\n')[-1]
assert desc == 'This is an alternative description.'
for key in ['foo', 'bar']:
assert key in yml.keys()
def test_extra_reserved(self):
with pytest.raises(ck2yaml.InputError,
match="must not redefine reserved field.+'species'"):
self.convert("h2o2.inp", output="h2o2_extra1", extra="extra-reserved.yaml")
captured = self._capsys.readouterr()
def test_extra_description_str(self):
with pytest.raises(ck2yaml.InputError):
self.convert("h2o2.inp", output="h2o2_extra1",
extra="extra-bad-description.yaml")
captured = self._capsys.readouterr()
assert "alternate description" in captured.out
assert "needs to be a string" in captured.out
def test_sri_zero(self):
# This test tests it can convert the SRI parameters when D or E equal to 0
output = self.convert('sri_convert_test.txt')
mech = load_yaml(output)
D = mech['reactions'][0]['SRI']['D']
E = mech['reactions'][0]['SRI']['E']
assert D == 0
assert E == 0
def test_duplicate_reactions(self):
# Running a test this way instead of using the convertMech function
# tests the behavior of the ck2yaml.main function and the mechanism
# validation step.
with pytest.raises(SystemExit):
ck2yaml.main([
f"--input={self.test_data_path}/undeclared-duplicate-reactions.inp",
f"--thermo={self.test_data_path}/dummy-thermo.dat",
f"--output={self.test_work_path}/undeclared-duplicate-reactions.yaml"])
captured = self._capsys.readouterr()
for token in ('FAILED', 'Line 12', 'Line 14', 'R1A', 'R1B'):
assert token in captured.out
def test_multiple_duplicate_reactions(self):
with pytest.raises(SystemExit):
ck2yaml.main([
f"--input={self.test_data_path}/undeclared-duplicate-reactions2.inp",
f"--thermo={self.test_data_path}/dummy-thermo.dat",
f"--output={self.test_work_path}/undeclared-duplicate-reactions2.yaml"])
captured = self._capsys.readouterr()
for token in ('FAILED', 'Line 12', 'Line 14', 'Line 11', 'Line 15',
'R1A + R1B', 'R3 + H'):
assert token in captured.out
def test_single_Tint(self):
output = self.convert(None, thermo="thermo_single_Tint.dat",
output="thermo_single_Tint",
single_intermediate_temperature=True)
mech = load_yaml(output)
# Al(cr)
thermo = mech["species"][0]["thermo"]
assert thermo["temperature-ranges"] == [200.0, 933.61]
assert len(thermo["data"]) == 1
assert thermo["data"][0][0] == 1.01040191
# AlBr3(L)
thermo = mech["species"][1]["thermo"]
assert thermo["temperature-ranges"] == [370.6, 5000.0]
assert len(thermo["data"]) == 1
assert thermo["data"][0][0] == 15.02975
# AlF3(b)
thermo = mech["species"][2]["thermo"]
assert thermo["temperature-ranges"] == [728.0, 1000.0, 2523.0]
assert len(thermo["data"]) == 2
assert thermo["data"][1][0] == 10.41947
# AlF3(L)
thermo = mech["species"][3]["thermo"]
assert thermo["temperature-ranges"] == [2523.0, 5000.0]
assert len(thermo["data"]) == 1
assert thermo["data"][0][0] == 15.096679
def test_error_for_big_element_number(self):
with pytest.raises(ck2yaml.InputError):
self.convert('big_element_num_err.inp')
captured = self._capsys.readouterr()
assert "no more than 3 digits." in captured.out
class Testyaml2ck:
"""Test yaml2ck by converting to CK then back to YAML to read with Cantera."""
ext: str = "-from-yaml2ck.yaml"
@pytest.fixture(autouse=True)
def inject_fixtures(self, test_data_path):
self.test_data_path = test_data_path
def _convert_to_ck(
self,
input_file: Path,
phase_name: str = "",
output: tuple[str, str, str] | tuple = (),
) -> tuple[Path | None, Path | None, Path | None]:
mechanism_path: Path | str
if not output:
stem = Path(input_file).stem # strip '.inp'
mechanism_path = self.test_work_path / (stem + "-from-yaml.ck")
thermo_path = transport_path = None
else:
if len(output) != 3:
raise ValueError(
"convert_to_ck output must be a tuple of length three "
"containing the mechanism, thermo, and transport file names."
)
mechanism_path, thermo_path, transport_path = output
mech, thermo, transport = yaml2ck.convert(
input_file,
phase_name=phase_name,
mechanism_path=mechanism_path,
thermo_path=thermo_path,
transport_path=transport_path,
overwrite=True,
sort_elements=None,
sort_species=None
)
return mech, thermo, transport
def convert(
self,
input_file: Path,
phase_name: str = "",
mech: str | Path | None = None,
thermo: str | Path | None = None,
transport: str | Path | None = None,
permissive: bool = False,
) -> str:
if mech is not None:
mech, thermo, transport = self._convert_to_ck(
input_file,
phase_name,
(mech, thermo, transport),
)
else:
mech, thermo, transport = self._convert_to_ck(input_file, phase_name)
output = self.test_work_path / (Path(input_file).stem + self.ext)
ck2yaml.convert(
mech,
thermo_file=thermo,
transport_file=transport,
out_name=output,
quiet=True,
permissive=permissive,
)
return mech
def check_conversion(self, basename, cls=ct.Solution, **kwargs):
# The round-trip YAML->CK->YAML will always have the single phase name 'gas'
# even if the input YAML phase has a different name
if "name" in kwargs:
phase_name = kwargs.pop("name")
else:
phase_name = ""
ckname = self.test_work_path / (basename.stem + self.ext)
ck_phase = cls(ckname, **kwargs)
yaml_phase = cls(basename, phase_name, **kwargs)
assert set(ck_phase.element_names) == set(yaml_phase.element_names)
assert set(ck_phase.species_names) == set(yaml_phase.species_names)
yamlSpecies = [yaml_phase.species(s) for s in ck_phase.species_names]
for C, Y in zip(ck_phase.species(), yamlSpecies):
assert C.composition == Y.composition
assert ck_phase.n_reactions == yaml_phase.n_reactions
for C, Y in zip(ck_phase.reactions(), yaml_phase.reactions()):
assert C.__class__ == Y.__class__
assert C.reactants == Y.reactants
assert C.products == Y.products
assert C.duplicate == Y.duplicate
for i, sp in zip(range(ck_phase.n_reactions), ck_phase.kinetics_species_names):
assert ck_phase.reactant_stoich_coeff(sp, i) == yaml_phase.reactant_stoich_coeff(sp, i)
return ck_phase, yaml_phase
def check_thermo(self, ck_phase, yaml_phase, temperatures, tol=1e-7):
yaml_idx = {ck_phase.species_index(s): yaml_phase.species_index(s) for s in ck_phase.species_names}
for T in temperatures:
ck_phase.TP = T, ct.one_atm
yaml_phase.TP = T, ct.one_atm
cp_ck = ck_phase.partial_molar_cp
cp_yaml = yaml_phase.partial_molar_cp
h_ck = ck_phase.partial_molar_enthalpies
h_yaml = yaml_phase.partial_molar_enthalpies
s_ck = ck_phase.partial_molar_entropies
s_yaml = yaml_phase.partial_molar_entropies
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), '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):
ck_phase.TP = T, P
yaml_phase.TP = T, P
kf_ck = ck_phase.forward_rate_constants
kr_ck = ck_phase.reverse_rate_constants
kf_yaml = yaml_phase.forward_rate_constants
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), '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}
ck_phase.transport_model = model
yaml_phase.transport_model = model
for T in temperatures:
ck_phase.TP = T, ct.one_atm
yaml_phase.TP = T, ct.one_atm
assert ck_phase.viscosity == approx(yaml_phase.viscosity)
assert ck_phase.thermal_conductivity == approx(yaml_phase.thermal_conductivity)
Dkm_ck = ck_phase.mix_diff_coeffs
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]]), message
@pytest.mark.slow_test
def test_gri30(self, cantera_data_path):
input_file = cantera_data_path / "gri30.yaml"
self.convert(input_file)
X = {'O2': 0.3, 'H2': 0.1, 'CH4': 0.2, 'CO2': 0.4}
ck_phase, yaml_phase = self.check_conversion(input_file)
ck_phase.X = X
yaml_phase.X = X
self.check_thermo(ck_phase, yaml_phase, [300, 500, 1300, 2000])
self.check_kinetics(ck_phase, yaml_phase, [900, 1800], [2e5, 20e5])
self.check_transport(ck_phase, yaml_phase, [298, 1001, 2400])
def test_nonreactant_orders(self):
input_file = self.test_data_path / "reaction-orders.yaml"
self.convert(input_file, permissive=True)
ck_phase, yaml_phase = self.check_conversion(input_file)
self.check_thermo(ck_phase, yaml_phase, [300, 500])
self.check_kinetics(ck_phase, yaml_phase, [300, 1001, 2500], [1e5, 10e5])
def test_phase_id(self, cantera_data_path):
input_file = cantera_data_path / "nDodecane_Reitz.yaml"
self.convert(input_file, "nDodecane_IG")
ck_phase, yaml_phase = self.check_conversion(input_file, name="nDodecane_IG")
ck_phase.X = "h2:1"
yaml_phase.X = "h2:1"
self.check_kinetics(
ck_phase, yaml_phase, [300, 800, 1450, 2800], [5e3, 1e5, 2e6], tol=4e-6
)
def test_third_body_reactions(self):
input_file = self.test_data_path / "explicit-third-bodies.yaml"
mech = self.convert(input_file)
with open(mech) as fid:
lines = fid.readlines()
for i, line in enumerate(lines):
if line.startswith("R1A + R1B"):
next = lines[i + 1]
assert next.startswith("LOW") or next.strip() == "DUPLICATE"
ck_phase, yaml_phase = self.check_conversion(input_file)
self.check_kinetics(
ck_phase, yaml_phase, [300, 800, 1450, 2800], [5e3, 1e5, 2e6]
)
def test_undeclared_third_body(self):
input_file = self.test_data_path / "undeclared-third-body.yaml"
mech = self.convert(input_file)
with open(mech) as fid:
lines = fid.readlines()
for i, line in enumerate(lines):
if line.startswith("H + O2 + M <=> HO2 + M"):
next = lines[i + 1]
assert "N2" not in next
assert all(sp in next for sp in ["AR", "C2H6", "CO", "CO2", "H2O", "O2"])
ck_phase, yaml_phase = self.check_conversion(input_file)
self.check_kinetics(ck_phase, yaml_phase, [900, 1800], [2e5, 20e5])
def test_pdep(self):
input_file = self.test_data_path / "pdep-test.yaml"
self.convert(input_file)
ck_phase, yaml_phase = self.check_conversion(input_file)
# Chebyshev coefficients in XML are truncated to 6 digits, limiting accuracy
self.check_kinetics(ck_phase, yaml_phase, [300, 1000, 2200],
[100, ct.one_atm, 2e5, 2e6, 9.9e6], tol=2e-4)
def test_sri_falloff(self):
input_file = self.test_data_path / "sri-falloff.yaml"
self.convert(input_file)
ck_phase, yaml_phase = self.check_conversion(input_file)
self.check_kinetics(ck_phase, yaml_phase, [300, 800, 1450, 2800], [5e3, 1e5, 2e6])
def test_chemically_activated(self):
input_file = self.test_data_path / "chemically-activated-reaction.yaml"
self.convert(input_file)
ck_phase, yaml_phase = self.check_conversion(input_file)
# pre-exponential factor in XML is truncated to 7 sig figs, limiting accuracy
self.check_kinetics(
ck_phase, yaml_phase, [300, 800, 1450, 2800], [5e3, 1e5, 2e6, 1e7], tol=1e-7
)
def test_yaml_2_ck_reactions(self):
input_file = self.test_data_path / "yaml-ck-reactions.yaml"
self.convert(input_file)
ck_phase, yaml_phase = self.check_conversion(input_file)
X = {'O2': 0.3, 'H': 0.1, 'H2': 0.2, 'AR': 0.4}
ck_phase.X = X
yaml_phase.X = X
self.check_thermo(ck_phase, yaml_phase, [300, 500, 1300, 2000])
self.check_kinetics(ck_phase, yaml_phase, [900, 1800], [2e5, 20e5], tol=2e-7)
self.check_transport(ck_phase, yaml_phase, [298, 1001, 2400])
def test_phase_no_reactions(self):
input_file = self.test_data_path / "yaml-ck-reactions.yaml"
yaml_phase = ct.Solution(input_file, name="no-reactions")
assert yaml_phase.n_reactions == 0
ck_file = self.test_work_path / 'no-reactions.ck'
ck_file.unlink(missing_ok=True)
yaml_phase.write_chemkin(ck_file, quiet=True)
assert ck_file.exists()
def test_write_chemkin(self):
# test alternative converter
yaml_phase = ct.Solution('h2o2.yaml')
ck_file = self.test_work_path / 'test.ck'
ck_file.unlink(missing_ok=True)
yaml_phase.write_chemkin(ck_file, quiet=True)
yaml_phase.write_chemkin(
ck_file, sort_species='alphabetical', overwrite=True, quiet=True)
assert ck_file.exists()
yaml_file = self.test_work_path / 'test.yaml'
yaml_file.unlink(missing_ok=True)
ck2yaml.convert(ck_file, out_name=yaml_file, quiet=True)
assert yaml_file.exists()
ck_phase = ct.Solution(yaml_file)
X = {'O2': 0.3, 'H': 0.1, 'H2': 0.2, 'AR': 0.4}
ck_phase.X = X
yaml_phase.X = X
self.check_thermo(ck_phase, yaml_phase, [300, 500, 1300, 2000])
self.check_kinetics(ck_phase, yaml_phase, [900, 1800], [2e5, 20e5], tol=2e-7)
self.check_transport(ck_phase, yaml_phase, [298, 1001, 2400])
def test_write_notes(self):
input_file = self.test_data_path / 'species-names.yaml'
yaml_phase = ct.Solution(input_file)
assert yaml_phase.species("eq=uals").input_data["thermo"]["note"] == 120521
assert yaml_phase.species("plus").input_data["thermo"]["note"] == 12.05
ck_file = self.test_work_path / 'species-names.ck'
ck_file.unlink(missing_ok=True)
yaml_phase.write_chemkin(ck_file, quiet=True)
yaml_file = self.test_work_path / 'species-names.yaml'
yaml_file.unlink(missing_ok=True)
ck2yaml.convert(ck_file, out_name=yaml_file, quiet=True)
assert yaml_file.exists()
ck_phase = ct.Solution(yaml_file)
assert ck_phase.species("eq=uals").input_data["thermo"]["note"] == "120521"
assert ck_phase.species("plus").input_data["thermo"]["note"] == "12.05"
def test_multi_line_notes(self):
input_file = self.test_data_path / 'multi-line-notes.yaml'
yaml_phase = ct.Solution(input_file)
assert yaml_phase.species("H2").input_data["thermo"]["note"] == "Line 1\nLine 2"
ck_file = self.test_work_path / 'multi-line-notes.ck'
ck_file.unlink(missing_ok=True)
yaml_phase.write_chemkin(ck_file, quiet=True)
yaml_file = self.test_work_path / 'multi-line-notes.yaml'
yaml_file.unlink(missing_ok=True)
ck2yaml.convert(ck_file, out_name=yaml_file, quiet=True)
assert yaml_file.exists()
ck_phase = ct.Solution(yaml_file)
assert ck_phase.species("H2").input_data["thermo"]["note"] == "Line 1\nLine 2"
class Testcti2yaml:
@pytest.fixture(autouse=True)
def inject_fixtures(self, test_data_path):
self.test_data_path = test_data_path
def convert(self, basename, src_dir=None, encoding=None):
if src_dir is None:
src_dir = self.test_data_path
cti2yaml.convert(
filename=Path(src_dir) / f"{basename}.cti",
output_name=self.test_work_path / f"{basename}-from-cti.yaml",
encoding=encoding,
)
def checkConversion(self, basename, cls=ct.Solution, ctiphases=(),
yamlphases=(), **kwargs):
ctiPhase = cls(f"{basename}-from-cti.yaml", adjacent=ctiphases, **kwargs)
yamlPhase = cls(f"{basename}.yaml", adjacent=yamlphases, **kwargs)
assert ctiPhase.element_names == yamlPhase.element_names
assert ctiPhase.species_names == yamlPhase.species_names
assert ctiPhase.n_reactions == yamlPhase.n_reactions
for C, Y in zip(ctiPhase.species(), yamlPhase.species()):
assert C.composition == Y.composition
for C, Y in zip(ctiPhase.reactions(), yamlPhase.reactions()):
assert C.__class__ == Y.__class__
assert C.reactants == Y.reactants
assert C.products == Y.products
assert C.duplicate == Y.duplicate
for i, sp in zip(range(ctiPhase.n_reactions), ctiPhase.kinetics_species_names):
assert ctiPhase.reactant_stoich_coeff(sp, i) == yamlPhase.reactant_stoich_coeff(sp, i)
return ctiPhase, yamlPhase
def checkThermo(self, ctiPhase, yamlPhase, temperatures, tol=1e-7, check_cp=True):
for T in temperatures:
ctiPhase.TP = T, ct.one_atm
yamlPhase.TP = T, ct.one_atm
if check_cp:
cp_cti = ctiPhase.partial_molar_cp
cp_yaml = yamlPhase.partial_molar_cp
else:
with pytest.raises(NotImplementedError):
yamlPhase.partial_molar_cp
h_cti = ctiPhase.partial_molar_enthalpies
h_yaml = yamlPhase.partial_molar_enthalpies
s_cti = ctiPhase.partial_molar_entropies
s_yaml = yamlPhase.partial_molar_entropies
assert ctiPhase.density == approx(yamlPhase.density)
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), '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):
ctiPhase.TP = T, P
yamlPhase.TP = T, P
kf_cti = ctiPhase.forward_rate_constants
kr_cti = ctiPhase.reverse_rate_constants
kf_yaml = yamlPhase.forward_rate_constants
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), 'kf ' + message
assert kr_cti[i] == approx(kr_yaml[i], rel=tol), 'kr ' + message
def checkTransport(self, ctiPhase, yamlPhase, temperatures,
model='mixture-averaged'):
ctiPhase.transport_model = model
yamlPhase.transport_model = model
for T in temperatures:
ctiPhase.TP = T, ct.one_atm
yamlPhase.TP = T, ct.one_atm
assert ctiPhase.viscosity == approx(yamlPhase.viscosity)
assert ctiPhase.thermal_conductivity == approx(yamlPhase.thermal_conductivity)
Dkm_cti = ctiPhase.mix_diff_coeffs
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]), message
@pytest.mark.slow_test
def test_gri30(self):
self.convert("gri30")
ctiPhase, yamlPhase = self.checkConversion('gri30')
X = {'O2': 0.3, 'H2': 0.1, 'CH4': 0.2, 'CO2': 0.4}
ctiPhase.X = X
yamlPhase.X = X
self.checkThermo(ctiPhase, yamlPhase, [300, 500, 1300, 2000])
self.checkKinetics(ctiPhase, yamlPhase, [900, 1800], [2e5, 20e5])
self.checkTransport(ctiPhase, yamlPhase, [298, 1001, 2400])
def test_pdep(self):
self.convert("pdep-test")
ctiPhase, yamlPhase = self.checkConversion('pdep-test')
# Agreement limited by low precision used by ck2cti for Chebyshev coeffs
self.checkKinetics(ctiPhase, yamlPhase, [300, 1000, 2200],
[100, ct.one_atm, 2e5, 2e6, 9.9e6], tol=2e-4)
def test_ptcombust(self):
self.convert("ptcombust")
ctiSurf, yamlSurf = self.checkConversion("ptcombust", ct.Interface,
name="Pt_surf")
yamlGas = yamlSurf.adjacent["gas"]
ctiGas = ctiSurf.adjacent["gas"]
self.checkKinetics(ctiGas, yamlGas, [500, 1200], [1e4, 3e5])
self.checkThermo(ctiSurf, yamlSurf, [400, 800, 1600])
self.checkKinetics(ctiSurf, yamlSurf, [500, 1200], [1e4, 3e5])
@pytest.mark.slow_test
def test_ptcombust_motzwise(self):
self.convert("ptcombust-motzwise")
ctiSurf, yamlSurf = self.checkConversion("ptcombust-motzwise", ct.Interface,
name="Pt_surf")
yamlGas = yamlSurf.adjacent["gas"]
ctiGas = ctiSurf.adjacent["gas"]
self.checkKinetics(ctiGas, yamlGas, [500, 1200], [1e4, 3e5])
self.checkThermo(ctiSurf, yamlSurf, [400, 800, 1600])
self.checkKinetics(ctiSurf, yamlSurf, [900], [101325])
@pytest.mark.usefixtures("allow_deprecated")
def test_sofc(self):
self.convert("sofc")
cti_tpb, yaml_tpb = self.checkConversion("sofc", ct.Interface, name="tpb")
ctiMetal, ctiMSurf, ctiOSurf = cti_tpb.adjacent.values()
yamlMetal, yamlMSurf, yamlOSurf = yaml_tpb.adjacent.values()
assert "oxide_bulk" in ctiOSurf.adjacent
assert "gas" in ctiOSurf.adjacent
self.checkThermo(ctiMSurf, yamlMSurf, [900, 1000, 1100])
self.checkThermo(ctiOSurf, yamlOSurf, [900, 1000, 1100])
ctiMetal.electric_potential = yamlMetal.electric_potential = 2
self.checkKinetics(cti_tpb, yaml_tpb, [900, 1000, 1100], [1e5])
ctiMetal.electric_potential = yamlMetal.electric_potential = 4
self.checkKinetics(cti_tpb, yaml_tpb, [900, 1000, 1100], [1e5])
@pytest.mark.slow_test
def test_liquidvapor(self):
self.convert("liquidvapor")
for name in ["water", "nitrogen", "methane", "hydrogen", "oxygen", "heptane"]:
ctiPhase, yamlPhase = self.checkConversion("liquidvapor", name=name)
self.checkThermo(ctiPhase, yamlPhase,
[1.3 * ctiPhase.min_temp, 0.7 * ctiPhase.max_temp])
def test_Redlich_Kwong_CO2(self):
self.convert("co2_RK_example")
ctiGas, yamlGas = self.checkConversion('co2_RK_example')
for P in [1e5, 2e6, 1.3e7]:
yamlGas.TP = ctiGas.TP = 300, P
self.checkThermo(ctiGas, yamlGas, [300, 400, 500], check_cp=False)
def test_diamond(self):
self.convert("diamond")
ctiSurf, yamlSurf = self.checkConversion("diamond", ct.Interface,
name="diamond_100")
ctiSolid = ctiSurf.adjacent["diamond"]
yamlSolid = yamlSurf.adjacent["diamond"]
self.checkThermo(ctiSolid, yamlSolid, [300, 500])
self.checkThermo(ctiSurf, yamlSurf, [330, 490])
self.checkKinetics(ctiSurf, yamlSurf, [400, 800], [2e5])
def test_lithium_ion_battery(self):
name = 'lithium_ion_battery'
self.convert(name, encoding="utf-8")
ctiAnode, yamlAnode = self.checkConversion(name, name='anode')
ctiCathode, yamlCathode = self.checkConversion(name, name='cathode')
ctiMetal, yamlMetal = self.checkConversion(name, name='electron')
ctiElyt, yamlElyt = self.checkConversion(name, name='electrolyte')
ctiAnodeInt, yamlAnodeInt = self.checkConversion(name,
name='edge_anode_electrolyte',
ctiphases=[ctiAnode, ctiMetal, ctiElyt],
yamlphases=[yamlAnode, yamlMetal, yamlElyt])
ctiCathodeInt, yamlCathodeInt = self.checkConversion(name,
name='edge_cathode_electrolyte',
ctiphases=[ctiCathode, ctiMetal, ctiElyt],
yamlphases=[yamlCathode, yamlMetal, yamlElyt])
self.checkThermo(ctiAnode, yamlAnode, [300, 330])
self.checkThermo(ctiCathode, yamlCathode, [300, 330])
ctiAnode.X = yamlAnode.X = [0.7, 0.3]
self.checkThermo(ctiAnode, yamlAnode, [300, 330])
ctiCathode.X = yamlCathode.X = [0.2, 0.8]
self.checkThermo(ctiCathode, yamlCathode, [300, 330])
for phase in [ctiAnode, yamlAnode, ctiCathode, yamlCathode, ctiMetal,
yamlMetal, ctiElyt, yamlElyt, ctiAnodeInt, yamlAnodeInt,
ctiCathodeInt, yamlCathodeInt]:
phase.TP = 300, 1e5
ctiMetal.electric_potential = yamlMetal.electric_potential = 0
ctiElyt.electric_potential = yamlElyt.electric_potential = 1.9
self.checkKinetics(ctiAnodeInt, yamlAnodeInt, [300], [1e5])
ctiMetal.electric_potential = yamlMetal.electric_potential = 2.2
ctiElyt.electric_potential = yamlElyt.electric_potential = 0
self.checkKinetics(ctiCathodeInt, yamlCathodeInt, [300], [1e5])
def test_ch4_ion(self):
self.convert("ch4_ion")
ctiGas, yamlGas = self.checkConversion("ch4_ion")
self.checkThermo(ctiGas, yamlGas, [300, 500, 1300, 2000])
self.checkKinetics(ctiGas, yamlGas, [900, 1800], [2e5, 20e5])
self.checkTransport(ctiGas, yamlGas, [298, 1001, 2400])
def test_description(self):
self.convert("haca2")
ctiGas, yamlGas = self.checkConversion("haca2")
assert ctiGas.input_header["description"].startswith("HACA Mechanism")
assert yamlGas.input_header["description"].startswith("HACA Mechanism")
def test_nonreactant_orders(self):
self.convert("reaction-orders")
ctiGas, yamlGas = self.checkConversion("reaction-orders")
assert ctiGas.input_header["description"].startswith("Input file to test")
self.checkThermo(ctiGas, yamlGas, [300, 500])
self.checkKinetics(ctiGas, yamlGas, [300, 1001, 2500], [1e5, 10e5])
class Testctml2yaml:
@pytest.fixture(autouse=True)
def inject_fixtures(self, test_data_path):
self.test_data_path = test_data_path
def convert(self, basename, src_dir=None):
if src_dir is None:
src_dir = self.test_data_path
ctml2yaml.convert(
Path(src_dir) / f"{basename}.xml",
self.test_work_path / f"{basename}-from-xml.yaml",
)
def checkConversion(self, basename, cls=ct.Solution, ctmlphases=(),
yamlphases=(), **kwargs):
ctmlPhase = cls(f"{basename}-from-xml.yaml", adjacent=ctmlphases, **kwargs)
yamlPhase = cls(f"{basename}.yaml", adjacent=yamlphases, **kwargs)
assert ctmlPhase.element_names == yamlPhase.element_names
assert ctmlPhase.species_names == yamlPhase.species_names
assert ctmlPhase.n_reactions == yamlPhase.n_reactions
for C, Y in zip(ctmlPhase.species(), yamlPhase.species()):
assert C.composition == Y.composition
for C, Y in zip(ctmlPhase.reactions(), yamlPhase.reactions()):
assert C.__class__ == Y.__class__
assert C.reactants == Y.reactants
assert C.products == Y.products
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))
return ctmlPhase, yamlPhase
def checkThermo(self, ctmlPhase, yamlPhase, temperatures, pressure=ct.one_atm,
tol=1e-7, check_cp=True):
for T in temperatures:
ctmlPhase.TP = T, pressure
yamlPhase.TP = T, pressure
if check_cp:
cp_ctml = ctmlPhase.partial_molar_cp
cp_yaml = yamlPhase.partial_molar_cp
else:
with pytest.raises(NotImplementedError):
yamlPhase.partial_molar_cp
h_ctml = ctmlPhase.partial_molar_enthalpies
h_yaml = yamlPhase.partial_molar_enthalpies
s_ctml = ctmlPhase.partial_molar_entropies
s_yaml = yamlPhase.partial_molar_entropies
assert ctmlPhase.density == approx(yamlPhase.density)
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), '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):
ctmlPhase.TP = T, P
yamlPhase.TP = T, P
kf_ctml = ctmlPhase.forward_rate_constants
kr_ctml = ctmlPhase.reverse_rate_constants
kf_yaml = yamlPhase.forward_rate_constants
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), 'kf ' + message
assert kr_ctml[i] == approx(kr_yaml[i], rel=tol), 'kr ' + message
def checkTransport(self, ctmlPhase, yamlPhase, temperatures,
model='mixture-averaged'):
ctmlPhase.transport_model = model
yamlPhase.transport_model = model
for T in temperatures:
ctmlPhase.TP = T, ct.one_atm
yamlPhase.TP = T, ct.one_atm
assert ctmlPhase.viscosity == approx(yamlPhase.viscosity)
assert ctmlPhase.thermal_conductivity == approx(yamlPhase.thermal_conductivity)
Dkm_ctml = ctmlPhase.mix_diff_coeffs
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]), message
@pytest.mark.slow_test
def test_gri30(self):
self.convert("gri30")
ctmlPhase, yamlPhase = self.checkConversion('gri30')
X = {'O2': 0.3, 'H2': 0.1, 'CH4': 0.2, 'CO2': 0.4}
ctmlPhase.X = X
yamlPhase.X = X
self.checkThermo(ctmlPhase, yamlPhase, [300, 500, 1300, 2000])
self.checkKinetics(ctmlPhase, yamlPhase, [900, 1800], [2e5, 20e5])
self.checkTransport(ctmlPhase, yamlPhase, [298, 1001, 2400])
def test_pdep(self):
self.convert("pdep-test")
ctmlPhase, yamlPhase = self.checkConversion('pdep-test')
# Chebyshev coefficients in XML are truncated to 6 digits, limiting accuracy
self.checkKinetics(ctmlPhase, yamlPhase, [300, 1000, 2200],
[100, ct.one_atm, 2e5, 2e6, 9.9e6], tol=2e-4)
def test_ptcombust(self):
self.convert("ptcombust")
ctmlSurf, yamlSurf = self.checkConversion("ptcombust", ct.Interface,
name="Pt_surf")
ctmlGas = ctmlSurf.adjacent["gas"]
yamlGas = yamlSurf.adjacent["gas"]
self.checkKinetics(ctmlGas, yamlGas, [500, 1200], [1e4, 3e5])
self.checkThermo(ctmlSurf, yamlSurf, [400, 800, 1600])
self.checkKinetics(ctmlSurf, yamlSurf, [500, 1200], [1e4, 3e5])
def test_ptcombust_motzwise(self):
self.convert("ptcombust-motzwise")
ctmlGas, yamlGas = self.checkConversion('ptcombust-motzwise')
ctmlSurf, yamlSurf = self.checkConversion('ptcombust-motzwise', ct.Interface,
name='Pt_surf', ctmlphases=[ctmlGas], yamlphases=[yamlGas])
self.checkKinetics(ctmlGas, yamlGas, [500, 1200], [1e4, 3e5])
self.checkThermo(ctmlSurf, yamlSurf, [400, 800, 1600])
self.checkKinetics(ctmlSurf, yamlSurf, [500, 1200], [1e4, 3e5])
@pytest.mark.usefixtures("allow_deprecated")
def test_sofc(self):
self.convert("sofc")
ctml_tpb, yaml_tpb = self.checkConversion("sofc", ct.Interface, name="tpb")
ctmlMetal, ctmlMSurf, ctmlOSurf = ctml_tpb.adjacent.values()
yamlMetal, yamlMSurf, yamlOSurf = yaml_tpb.adjacent.values()
assert "oxide_bulk" in ctmlOSurf.adjacent
assert "gas" in ctmlOSurf.adjacent
self.checkThermo(ctmlMSurf, yamlMSurf, [900, 1000, 1100])
self.checkThermo(ctmlOSurf, yamlOSurf, [900, 1000, 1100])
ctmlMetal.electric_potential = yamlMetal.electric_potential = 2
self.checkKinetics(ctml_tpb, yaml_tpb, [900, 1000, 1100], [1e5])
ctmlMetal.electric_potential = yamlMetal.electric_potential = 4
self.checkKinetics(ctml_tpb, yaml_tpb, [900, 1000, 1100], [1e5])
def test_liquidvapor(self):
self.convert("liquidvapor")
for name in ["water", "nitrogen", "methane", "hydrogen", "oxygen", "heptane"]:
ctmlPhase, yamlPhase = self.checkConversion("liquidvapor", name=name)
self.checkThermo(ctmlPhase, yamlPhase,
[1.3 * ctmlPhase.min_temp, 0.7 * ctmlPhase.max_temp])
def test_Redlich_Kwong_CO2(self):
self.convert("co2_RK_example")
ctmlGas, yamlGas = self.checkConversion('co2_RK_example')
for P in [1e5, 2e6, 1.3e7]:
yamlGas.TP = ctmlGas.TP = 300, P
self.checkThermo(ctmlGas, yamlGas, [300, 400, 500], check_cp=False)
def test_diamond(self):
self.convert("diamond")
ctmlGas, yamlGas = self.checkConversion('diamond', name='gas')
ctmlSolid, yamlSolid = self.checkConversion('diamond', name='diamond')
ctmlSurf, yamlSurf = self.checkConversion('diamond',
ct.Interface, name='diamond_100', ctmlphases=[ctmlGas, ctmlSolid],
yamlphases=[yamlGas, yamlSolid])
self.checkThermo(ctmlSolid, yamlSolid, [300, 500])
self.checkThermo(ctmlSurf, yamlSurf, [330, 490])
self.checkKinetics(ctmlSurf, yamlSurf, [400, 800], [2e5])
def test_lithium_ion_battery(self):
name = 'lithium_ion_battery'
self.convert(name)
ctmlAnode, yamlAnode = self.checkConversion(name, name='anode')
ctmlCathode, yamlCathode = self.checkConversion(name, name='cathode')
ctmlMetal, yamlMetal = self.checkConversion(name, name='electron')
ctmlElyt, yamlElyt = self.checkConversion(name, name='electrolyte')
ctmlAnodeInt, yamlAnodeInt = self.checkConversion(name,
name='edge_anode_electrolyte',
ctmlphases=[ctmlAnode, ctmlMetal, ctmlElyt],
yamlphases=[yamlAnode, yamlMetal, yamlElyt])
ctmlCathodeInt, yamlCathodeInt = self.checkConversion(name,
name='edge_cathode_electrolyte',
ctmlphases=[ctmlCathode, ctmlMetal, ctmlElyt],
yamlphases=[yamlCathode, yamlMetal, yamlElyt])
self.checkThermo(ctmlAnode, yamlAnode, [300, 330])
self.checkThermo(ctmlCathode, yamlCathode, [300, 330])
ctmlAnode.X = yamlAnode.X = [0.7, 0.3]
self.checkThermo(ctmlAnode, yamlAnode, [300, 330])
ctmlCathode.X = yamlCathode.X = [0.2, 0.8]
self.checkThermo(ctmlCathode, yamlCathode, [300, 330])
for phase in [ctmlAnode, yamlAnode, ctmlCathode, yamlCathode, ctmlMetal,
yamlMetal, ctmlElyt, yamlElyt, ctmlAnodeInt, yamlAnodeInt,
ctmlCathodeInt, yamlCathodeInt]:
phase.TP = 300, 1e5
ctmlMetal.electric_potential = yamlMetal.electric_potential = 0
ctmlElyt.electric_potential = yamlElyt.electric_potential = 1.9
self.checkKinetics(ctmlAnodeInt, yamlAnodeInt, [300], [1e5])
ctmlMetal.electric_potential = yamlMetal.electric_potential = 2.2
ctmlElyt.electric_potential = yamlElyt.electric_potential = 0
self.checkKinetics(ctmlCathodeInt, yamlCathodeInt, [300], [1e5])
def test_noxNeg(self):
self.convert("noxNeg")
ctmlGas, yamlGas = self.checkConversion('noxNeg')
self.checkThermo(ctmlGas, yamlGas, [300, 1000])
self.checkKinetics(ctmlGas, yamlGas, [300, 1000], [1e5])
def test_ch4_ion(self):
self.convert("ch4_ion")
ctmlGas, yamlGas = self.checkConversion("ch4_ion")
self.checkThermo(ctmlGas, yamlGas, [300, 500, 1300, 2000])
self.checkKinetics(ctmlGas, yamlGas, [900, 1800], [2e5, 20e5])
self.checkTransport(ctmlGas, yamlGas, [298, 1001, 2400])
def test_nasa9(self):
self.convert("nasa9-test")
ctmlGas, yamlGas = self.checkConversion("nasa9-test")
self.checkThermo(ctmlGas, yamlGas, [300, 500, 1300, 2000])
def test_chemically_activated(self):
self.convert("chemically-activated-reaction")
ctmlGas, yamlGas = self.checkConversion("chemically-activated-reaction")
self.checkThermo(ctmlGas, yamlGas, [300, 500, 1300, 2000])
self.checkKinetics(ctmlGas, yamlGas, [900, 1800], [2e5, 20e5])
def test_explicit_forward_order(self):
self.convert("explicit-forward-order")
ctmlGas, yamlGas = self.checkConversion("explicit-forward-order")
self.checkThermo(ctmlGas, yamlGas, [300, 500, 1300, 2000])
# Accuracy limited by precision of ck2cti
self.checkKinetics(ctmlGas, yamlGas, [900, 1800], [2e5, 20e5], tol=2e-7)
def test_explicit_reverse_rate(self):
self.convert("explicit-reverse-rate")
ctmlGas, yamlGas = self.checkConversion("explicit-reverse-rate")
self.checkThermo(ctmlGas, yamlGas, [300, 500, 1300, 2000])
self.checkKinetics(ctmlGas, yamlGas, [900, 1800], [2e5, 20e5])
def test_explicit_third_bodies(self):
self.convert("explicit-third-bodies")
ctmlGas, yamlGas = self.checkConversion("explicit-third-bodies")
self.checkThermo(ctmlGas, yamlGas, [300, 500, 1300, 2000])
self.checkKinetics(ctmlGas, yamlGas, [900, 1800], [2e5, 20e5])
def test_fractional_stoich_coeffs(self):
self.convert("frac")
ctmlGas, yamlGas = self.checkConversion("frac")
self.checkThermo(ctmlGas, yamlGas, [300, 500, 1300, 2000])
self.checkKinetics(ctmlGas, yamlGas, [900, 1800], [2e5, 20e5])
def test_water_IAPWS95_thermo(self):
self.convert("liquid-water")
ctmlWater, yamlWater = self.checkConversion("liquid-water")
self.checkThermo(ctmlWater, yamlWater, [300, 500, 1300, 2000], pressure=22064000.0)
assert ctmlWater.transport_model == yamlWater.transport_model
ctmlWater.TP = yamlWater.TP = 300, 22064000.0
dens = ctmlWater.density
for T in [298, 1001, 2400]:
ctmlWater.TD = T, dens
yamlWater.TD = T, dens
assert ctmlWater.viscosity == approx(yamlWater.viscosity)
assert ctmlWater.thermal_conductivity == approx(yamlWater.thermal_conductivity)
def test_hmw_nacl_phase(self):
basename = "HMW_NaCl_sp1977_alt"
self.convert(basename)
ctmlPhase, yamlPhase = self.checkConversion(basename)
self.checkThermo(ctmlPhase, yamlPhase, [300, 500])
def test_NaCl_solid_phase(self):
self.convert("NaCl_Solid")
ctmlPhase, yamlPhase = self.checkConversion("NaCl_Solid")
self.checkThermo(ctmlPhase, yamlPhase, [300, 500, 1300, 2000])
def test_DH_NaCl_phase(self):
self.convert("debye-huckel-all")
for name in [
"debye-huckel-dilute",
"debye-huckel-B-dot-ak",
"debye-huckel-B-dot-a",
"debye-huckel-pitzer-beta_ij",
"debye-huckel-beta_ij",
]:
ctmlPhase, yamlPhase = self.checkConversion("debye-huckel-all", name=name)
self.checkThermo(ctmlPhase, yamlPhase, [300, 500])
def test_Redlich_Kister(self):
self.convert("RedlichKisterVPSSTP_valid")
ctmlPhase, yamlPhase = self.checkConversion("RedlichKisterVPSSTP_valid")
self.checkThermo(ctmlPhase, yamlPhase, [300, 500])
def test_species_names(self):
self.convert("species-names")
ctmlGas, yamlGas = self.checkConversion('species-names')
self.checkThermo(ctmlGas, yamlGas, [300, 500, 1300, 2000])
def test_sri_falloff_reaction(self):
self.convert("sri-falloff")
ctmlGas, yamlGas = self.checkConversion("sri-falloff")
self.checkThermo(ctmlGas, yamlGas, [300, 500, 1300, 2000])
self.checkKinetics(ctmlGas, yamlGas, [900, 1800], [2e5, 20e5])
def test_vpss_and_hkft(self):
self.convert("pdss_hkft")
ctmlPhase, yamlPhase = self.checkConversion("pdss_hkft")
self.checkThermo(ctmlPhase, yamlPhase, [300, 500])
@pytest.mark.usefixtures("allow_deprecated")
def test_lattice_solid(self):
self.convert("Li7Si3_ls")
ctmlPhase, yamlPhase = self.checkConversion("Li7Si3_ls",
name="Li7Si3_and_Interstitials(S)")
self.checkThermo(ctmlPhase, yamlPhase, [300, 500])
def test_margules(self):
self.convert("LiKCl_liquid")
ctmlPhase, yamlPhase = self.checkConversion("LiKCl_liquid")
self.checkThermo(ctmlPhase, yamlPhase, [300, 500])
def test_idealsolidsoln(self):
with pytest.warns(UserWarning, match="SolidKinetics type is not implemented"):
self.convert("IdealSolidSolnPhaseExample")
# SolidKinetics is not implemented, so can't create a Kinetics class instance.
basename = "IdealSolidSolnPhaseExample"
ctmlPhase = ct.ThermoPhase(basename + "-from-xml.yaml")
yamlPhase = ct.ThermoPhase(basename + ".yaml")
assert ctmlPhase.element_names == yamlPhase.element_names
assert ctmlPhase.species_names == yamlPhase.species_names
self.checkThermo(ctmlPhase, yamlPhase, [300, 500])
def test_idealmolalsoln(self):
self.convert("IdealMolalSolnPhaseExample")
ctmlPhase, yamlPhase = self.checkConversion("IdealMolalSolnPhaseExample")
self.checkThermo(ctmlPhase, yamlPhase, [300, 500])
def test_transport_models(self):
self.convert("transport_models_test")
for name in ["UnityLewis", "CK_Mix", "CK_Multi", "HighP"]:
ctmlPhase, yamlPhase = self.checkConversion("transport_models_test", name=name)
self.checkTransport(ctmlPhase, yamlPhase, [298, 1001, 2500])
def test_nonreactant_orders(self):
self.convert("reaction-orders")
ctmlPhase, yamlPhase = self.checkConversion("reaction-orders")
self.checkThermo(ctmlPhase, yamlPhase, [300, 500])
self.checkKinetics(ctmlPhase, yamlPhase, [300, 1001, 2500], [1e5, 10e5])
def test_species_ss_temperature_polynomials(self):
self.convert("Li_Liquid")
ctmlPhase, yamlPhase = self.checkConversion("Li_Liquid")
self.checkThermo(ctmlPhase, yamlPhase, [300, 500])
def test_duplicate_section_ids(self):
with pytest.warns(UserWarning, match="Duplicate 'speciesData' id"):
self.convert("duplicate-speciesData-ids")
with pytest.warns(UserWarning, match="Duplicate 'reactionData' id"):
self.convert("duplicate-reactionData-ids")
class Testlxcat2yaml:
@pytest.fixture(autouse=True)
def inject_fixtures(self, test_data_path):
self.test_data_path = test_data_path
def convert(self, inputFile=None, database=None, mechFile=None, phase=None,
insert=True, output=None):
if inputFile is not None:
inputFile = self.test_data_path / inputFile
if mechFile is not None:
mechFile = self.test_data_path / mechFile
if output is None:
output = Path(inputFile).stem # strip '.xml'
# output to work dir
output = self.test_work_path / output
lxcat2yaml.convert(inputFile, database, mechFile, phase, insert, output)
return output
def test_mechanism_with_lxcat(self):
# get Solution from the mechanism file
phase = "isotropic-electron-energy-plasma"
mechFile = "lxcat-test-convert.yaml"
gas1 = ct.Solution(self.test_data_path / mechFile,
phase=phase, transport_model=None)
# get a stand-alone collisions
standAloneFile = "stand-alone-lxcat.yaml"
self.convert(inputFile='lxcat-test-convert.xml', database="test",
mechFile=mechFile, insert=False,
output=standAloneFile)
# add collisions to the reaction list
rxn_list = ct.Reaction.list_from_file(self.test_work_path / standAloneFile,
gas1, section="collisions")
for R in rxn_list:
gas1.add_reaction(R)
# get Solution from the output file
output = "output-lxcat.yaml"
self.convert(inputFile="lxcat-test-convert.xml", database="test",
mechFile=mechFile, insert=True,
output=output)
gas2 = ct.Solution(self.test_work_path / output,
phase=phase, transport_model=None)
# check number of reactions
assert gas1.n_reactions == gas2.n_reactions == 4
for i in range(1, gas1.n_reactions):
assert (gas1.reaction(i).rate.energy_levels
== approx(gas2.reaction(i).rate.energy_levels))
assert (gas1.reaction(i).rate.cross_sections
== approx(gas2.reaction(i).rate.cross_sections))
def test_stand_alone_lxcat(self):
outfile = "stand-alone-lxcat-without-mech.yaml"
self.convert(inputFile='lxcat-test-convert.xml',
database="test", insert=False,
output=outfile)
# get Solution from the mechanism file
phase = "isotropic-electron-energy-plasma"
mechFile = "lxcat-test-convert.yaml"
gas = ct.Solution(self.test_data_path / mechFile,
phase=phase, transport_model=None)
# add collisions to the reaction list
rxn_list = ct.Reaction.list_from_file(self.test_work_path / outfile,
gas, section="collisions")
# verify the data
assert len(rxn_list) == 2
assert rxn_list[0].equation == "O2 + e => O2(Total-Ionization)+ + 2 e"
assert rxn_list[0].reaction_type == "electron-collision-plasma"
assert rxn_list[0].rate.energy_levels == approx([15., 20.])
assert rxn_list[0].rate.cross_sections == approx([0.0, 5.5e-22])
assert rxn_list[1].equation == "O2 + e => O2-"
assert rxn_list[1].reaction_type == "electron-collision-plasma"
assert rxn_list[1].rate.energy_levels == approx([0.0, 1.0])
assert rxn_list[1].rate.cross_sections == approx([0.0, 1.0e-22])