cantera/interfaces/cython/cantera/test/test_thermo.py

999 lines
35 KiB
Python

from .utilities import unittest
import numpy as np
import cantera as ct
from . import utilities
class TestThermoPhase(utilities.CanteraTest):
def setUp(self):
self.phase = ct.Solution('h2o2.xml')
def test_phases(self):
self.assertEqual(self.phase.n_phases, 1)
def test_species(self):
self.assertEqual(self.phase.n_species, 9)
for i,name in enumerate(self.phase.species_names):
self.assertEqual(name, self.phase.species_name(i))
self.assertEqual(i, self.phase.species_index(name))
self.assertEqual(i, self.phase.species_index(i))
def test_elements(self):
self.assertEqual(self.phase.n_elements, 3)
for i,symbol in enumerate(self.phase.element_names):
self.assertEqual(symbol, self.phase.element_name(i))
self.assertEqual(i, self.phase.element_index(symbol))
self.assertEqual(i, self.phase.element_index(i))
def test_n_atoms(self):
data = [(1, 'O', 'O'), (2, 'O', 'O2'), (1, b'H', b'OH'),
(2, 'H', 'H2O'), (2, u'O', u'H2O2'), (1, 'Ar', 'AR'),
(0, 'O', 'H'), (0, 'H', 'AR'), (0, 'Ar', 'HO2')]
for (n, elem, species) in data:
self.assertEqual(self.phase.n_atoms(species, elem), n)
mElem = self.phase.element_index(elem)
kSpec = self.phase.species_index(species)
self.assertEqual(self.phase.n_atoms(kSpec, mElem), n)
with self.assertRaises(ValueError):
self.phase.n_atoms('C', 'H2')
with self.assertRaises(ValueError):
self.phase.n_atoms('H', 'CH4')
def test_elemental_mass_fraction(self):
self.phase.Y = 'H2O:0.5, O2:0.5'
Zo = self.phase.elemental_mass_fraction('O')
Zh = self.phase.elemental_mass_fraction('H')
Zar = self.phase.elemental_mass_fraction('Ar')
mO = self.phase.element_index('O')
self.assertEqual(Zo, self.phase.elemental_mass_fraction(mO))
self.assertNear(Zo, 0.5 + 0.5 * (15.9994 / 18.01528))
self.assertNear(Zh, 0.5 * (2.01588 / 18.01528))
self.assertEqual(Zar, 0.0)
with self.assertRaises(ValueError):
self.phase.elemental_mass_fraction('C')
with self.assertRaises(ValueError):
self.phase.elemental_mass_fraction(5)
def test_elemental_mole_fraction(self):
self.phase.X = 'H2O:0.5, O2:0.5'
Zo = self.phase.elemental_mole_fraction('O')
Zh = self.phase.elemental_mole_fraction('H')
Zar = self.phase.elemental_mole_fraction('Ar')
mO = self.phase.element_index('O')
self.assertEqual(Zo, self.phase.elemental_mole_fraction(mO))
self.assertNear(Zo, (0.5 + 1) / (0.5*3 + 0.5*2))
self.assertNear(Zh, (2*0.5) / (0.5*3 + 0.5*2))
self.assertEqual(Zar, 0.0)
with self.assertRaises(ValueError):
self.phase.elemental_mole_fraction('C')
with self.assertRaises(ValueError):
self.phase.elemental_mole_fraction(5)
def test_elemental_mass_mole_fraction(self):
# expected relationship between elmental mass and mole fractions
comps = ['H2O:0.5, O2:0.5', 'H2:0.1, O2:0.4, H2O2:0.3, AR:0.2',
'O2:0.1, H2:0.9']
for comp in comps:
self.phase.X = comp
denom = sum(self.phase.elemental_mole_fraction(i)
* self.phase.atomic_weight(i)
for i in range(self.phase.n_elements))
for i in range(self.phase.n_elements):
self.assertNear(self.phase.elemental_mass_fraction(i),
self.phase.elemental_mole_fraction(i)
* self.phase.atomic_weight(i) / denom)
def test_weights(self):
atomic_weights = self.phase.atomic_weights
molecular_weights = self.phase.molecular_weights
self.assertEqual(self.phase.n_elements, len(atomic_weights))
self.assertEqual(self.phase.n_species, len(molecular_weights))
for i,mw in enumerate(molecular_weights):
test_weight = 0.0
for j,aw in enumerate(atomic_weights):
test_weight += aw * self.phase.n_atoms(i,j)
self.assertAlmostEqual(test_weight, mw)
def test_setComposition(self):
X = np.zeros(self.phase.n_species)
X[2] = 1.0
self.phase.X = X
Y = self.phase.Y
self.assertEqual(list(X), list(Y))
def test_setCompositionString(self):
self.phase.X = 'H2:1.0, O2:1.0'
X = self.phase.X
self.assertNear(X[0], 0.5)
self.assertNear(X[3], 0.5)
with self.assertRaises(Exception):
self.phase.X = 'H2:1.0, CO2:1.5'
def test_setCompositionStringBad(self):
X0 = self.phase.X
with self.assertRaises(Exception):
self.phase.X = 'H2:1.0, O2:asdf'
self.assertArrayNear(X0, self.phase.X)
with self.assertRaises(Exception):
self.phase.X = 'H2:1e-x4'
self.assertArrayNear(X0, self.phase.X)
with self.assertRaises(Exception):
self.phase.X = 'H2:1e-1.4'
self.assertArrayNear(X0, self.phase.X)
def test_setCompositionDict(self):
self.phase.X = {b'H2':1.0, b'O2':3.0}
X = self.phase.X
self.assertNear(X[0], 0.25)
self.assertNear(X[3], 0.75)
self.phase.Y = {u'H2':1.0, u'O2':3.0}
Y = self.phase.Y
self.assertNear(Y[0], 0.25)
self.assertNear(Y[3], 0.75)
def test_getCompositionDict(self):
self.phase.X = 'OH:1e-9, O2:0.4, AR:0.6'
self.assertEqual(len(self.phase.mole_fraction_dict(1e-7)), 2)
self.assertEqual(len(self.phase.mole_fraction_dict()), 3)
self.phase.Y = 'O2:0.4, AR:0.6'
Y1 = self.phase.mass_fraction_dict()
self.assertNear(Y1['O2'], 0.4)
self.assertNear(Y1['AR'], 0.6)
def test_setCompositionNoNorm(self):
X = np.zeros(self.phase.n_species)
X[2] = 1.0
X[0] = 0.01
self.phase.set_unnormalized_mole_fractions(X)
self.assertArrayNear(self.phase.X, X)
self.assertNear(sum(X), 1.01)
Y = np.zeros(self.phase.n_species)
Y[2] = 1.0
Y[0] = 0.01
self.phase.set_unnormalized_mass_fractions(Y)
self.assertArrayNear(self.phase.Y, Y)
self.assertNear(sum(Y), 1.01)
def test_setCompositionNoNormBad(self):
X = np.zeros(self.phase.n_species - 1)
with self.assertRaises(ValueError):
self.phase.set_unnormalized_mole_fractions(X)
with self.assertRaises(ValueError):
self.phase.set_unnormalized_mass_fractions([1,2,3])
def test_setCompositionDict_bad1(self):
with self.assertRaises(Exception):
self.phase.X = {'H2':1.0, 'HCl':3.0}
def test_setCompositionDict_bad2(self):
with self.assertRaises(Exception):
self.phase.Y = {'H2':1.0, 'O2':'xx'}
def test_setCompositionSlice(self):
self.phase['H2', 'O2'].X = 0.1, 0.9
X = self.phase.X
self.assertNear(X[0], 0.1)
self.assertNear(X[3], 0.9)
def test_setCompositionSlice_bad(self):
with self.assertRaises(ValueError):
self.phase['H2','O2'].Y = [0.1, 0.2, 0.3]
def test_full_report(self):
report = self.phase.report(threshold=0.0)
self.assertIn(self.phase.name, report)
self.assertIn('temperature', report)
self.assertNotIn('minor', report)
for name in self.phase.species_names:
self.assertIn(name, report)
def test_default_report(self):
self.phase.X = 'H2:0.1, O2:0.9, HO2:1e-10, H2O2:1e-20'
report = self.phase.report()
self.assertIn('minor', report)
for name in (' H2 ', ' O2 ', ' HO2 '):
self.assertIn(name, report)
for name in (' H2O2 ', ' OH ', ' AR '):
self.assertNotIn(name, report)
def test_name(self):
self.assertEqual(self.phase.name, 'ohmech')
self.phase.name = 'something'
self.assertEqual(self.phase.name, 'something')
self.assertIn('something', self.phase.report())
def test_ID(self):
self.assertEqual(self.phase.ID, 'ohmech')
self.phase.ID = 'something'
self.assertEqual(self.phase.ID, 'something')
self.assertEqual(self.phase.name, 'ohmech')
def test_badLength(self):
X = np.zeros(5)
with self.assertRaises(ValueError):
self.phase.X = X
with self.assertRaises(ValueError):
self.phase.Y = X
def test_mass_basis(self):
self.assertEqual(self.phase.basis, 'mass')
self.assertEqual(self.phase.density_mass, self.phase.density)
self.assertEqual(self.phase.enthalpy_mass, self.phase.h)
self.assertEqual(self.phase.entropy_mass, self.phase.s)
self.assertEqual(self.phase.int_energy_mass, self.phase.u)
self.assertEqual(self.phase.volume_mass, self.phase.v)
self.assertEqual(self.phase.cv_mass, self.phase.cv)
self.assertEqual(self.phase.cp_mass, self.phase.cp)
def test_molar_basis(self):
self.phase.basis = 'molar'
self.assertEqual(self.phase.basis, 'molar')
self.assertEqual(self.phase.density_mole, self.phase.density)
self.assertEqual(self.phase.enthalpy_mole, self.phase.h)
self.assertEqual(self.phase.entropy_mole, self.phase.s)
self.assertEqual(self.phase.int_energy_mole, self.phase.u)
self.assertEqual(self.phase.volume_mole, self.phase.v)
self.assertEqual(self.phase.cv_mole, self.phase.cv)
self.assertEqual(self.phase.cp_mole, self.phase.cp)
def check_setters(self, T1, rho1, Y1):
T0, rho0, Y0 = self.phase.TDY
self.phase.TDY = T1, rho1, Y1
X1 = self.phase.X
P1 = self.phase.P
h1 = self.phase.h
s1 = self.phase.s
u1 = self.phase.u
v1 = self.phase.v
def check_state(T, rho, Y):
self.assertNear(self.phase.T, T)
self.assertNear(self.phase.density, rho)
self.assertArrayNear(self.phase.Y, Y)
self.phase.TDY = T0, rho0, Y0
self.phase.TPY = T1, P1, Y1
check_state(T1, rho1, Y1)
self.phase.TDY = T0, rho0, Y0
self.phase.UVY = u1, v1, Y1
check_state(T1, rho1, Y1)
self.phase.TDY = T0, rho0, Y0
self.phase.HPY = h1, P1, Y1
check_state(T1, rho1, Y1)
self.phase.TDY = T0, rho0, Y0
self.phase.SPY = s1, P1, Y1
check_state(T1, rho1, Y1)
self.phase.TDY = T0, rho0, Y0
self.phase.TPX = T1, P1, X1
check_state(T1, rho1, Y1)
self.phase.TDY = T0, rho0, Y0
self.phase.UVX = u1, v1, X1
check_state(T1, rho1, Y1)
self.phase.TDY = T0, rho0, Y0
self.phase.HPX = h1, P1, X1
check_state(T1, rho1, Y1)
self.phase.TDY = T0, rho0, Y0
self.phase.SPX = s1, P1, X1
check_state(T1, rho1, Y1)
self.phase.TDY = T0, rho0, Y0
self.phase.SVX = s1, v1, X1
check_state(T1, rho1, Y1)
self.phase.TDY = T0, rho0, Y0
self.phase.SVY = s1, v1, Y1
check_state(T1, rho1, Y1)
self.phase.TDY = T0, rho0, Y0
self.phase.DPX = rho1, P1, X1
check_state(T1, rho1, Y1)
self.phase.TDY = T0, rho0, Y0
self.phase.DPY = rho1, P1, Y1
check_state(T1, rho1, Y1)
def test_setState_mass(self):
self.check_setters(T1 = 500.0, rho1 = 1.5,
Y1 = [0.1, 0.0, 0.0, 0.1, 0.4, 0.2, 0.0, 0.0, 0.2])
def test_setState_mole(self):
self.phase.basis = 'molar'
self.check_setters(T1 = 750.0, rho1 = 0.02,
Y1 = [0.2, 0.1, 0.0, 0.3, 0.1, 0.0, 0.0, 0.2, 0.1])
def test_setters_hold_constant(self):
props = ('T','P','s','h','u','v','X','Y')
pairs = [('TP', 'T', 'P'), ('SP', 's', 'P'),
('UV', 'u', 'v')]
self.phase.TDX = 1000, 1.5, 'H2O:0.1, O2:0.95, AR:3.0'
values = {}
for p in props:
values[p] = getattr(self.phase, p)
for pair, first, second in pairs:
self.phase.TDX = 500, 2.5, 'H2:0.1, O2:1.0, AR:3.0'
first_val = getattr(self.phase, first)
second_val = getattr(self.phase, second)
setattr(self.phase, pair, (values[first], None))
self.assertNear(getattr(self.phase, first), values[first])
self.assertNear(getattr(self.phase, second), second_val)
self.phase.TDX = 500, 2.5, 'H2:0.1, O2:1.0, AR:3.0'
setattr(self.phase, pair, (None, values[second]))
self.assertNear(getattr(self.phase, first), first_val)
self.assertNear(getattr(self.phase, second), values[second])
self.phase.TDX = 500, 2.5, 'H2:0.1, O2:1.0, AR:3.0'
setattr(self.phase, pair + 'X', (None, None, values['X']))
self.assertNear(getattr(self.phase, first), first_val)
self.assertNear(getattr(self.phase, second), second_val)
self.phase.TDX = 500, 2.5, 'H2:0.1, O2:1.0, AR:3.0'
setattr(self.phase, pair + 'Y', (None, None, values['Y']))
self.assertNear(getattr(self.phase, first), first_val)
self.assertNear(getattr(self.phase, second), second_val)
def test_setter_errors(self):
with self.assertRaises(Exception):
self.phase.TD = 400
with self.assertRaises(AssertionError):
self.phase.TP = 300, 101325, 'CH4:1.0'
with self.assertRaises(AssertionError):
self.phase.HPY = 1.2e6, 101325
with self.assertRaises(AssertionError):
self.phase.UVX = -4e5, 4.4, 'H2:1.0', -1
def test_invalid_property(self):
x = self.phase
with self.assertRaises(AttributeError):
x.foobar = 300
with self.assertRaises(AttributeError):
x.foobar
def check_getters(self):
T,D,X = self.phase.TDX
self.assertNear(T, self.phase.T)
self.assertNear(D, self.phase.density)
self.assertArrayNear(X, self.phase.X)
T,D,Y = self.phase.TDY
self.assertNear(T, self.phase.T)
self.assertNear(D, self.phase.density)
self.assertArrayNear(Y, self.phase.Y)
T,D = self.phase.TD
self.assertNear(T, self.phase.T)
self.assertNear(D, self.phase.density)
T,P,X = self.phase.TPX
self.assertNear(T, self.phase.T)
self.assertNear(P, self.phase.P)
self.assertArrayNear(X, self.phase.X)
T,P,Y = self.phase.TPY
self.assertNear(T, self.phase.T)
self.assertNear(P, self.phase.P)
self.assertArrayNear(Y, self.phase.Y)
T,P = self.phase.TP
self.assertNear(T, self.phase.T)
self.assertNear(P, self.phase.P)
H,P,X = self.phase.HPX
self.assertNear(H, self.phase.h)
self.assertNear(P, self.phase.P)
self.assertArrayNear(X, self.phase.X)
H,P,Y = self.phase.HPY
self.assertNear(H, self.phase.h)
self.assertNear(P, self.phase.P)
self.assertArrayNear(Y, self.phase.Y)
H,P = self.phase.HP
self.assertNear(H, self.phase.h)
self.assertNear(P, self.phase.P)
U,V,X = self.phase.UVX
self.assertNear(U, self.phase.u)
self.assertNear(V, self.phase.v)
self.assertArrayNear(X, self.phase.X)
U,V,Y = self.phase.UVY
self.assertNear(U, self.phase.u)
self.assertNear(V, self.phase.v)
self.assertArrayNear(Y, self.phase.Y)
U,V = self.phase.UV
self.assertNear(U, self.phase.u)
self.assertNear(V, self.phase.v)
S,P,X = self.phase.SPX
self.assertNear(S, self.phase.s)
self.assertNear(P, self.phase.P)
self.assertArrayNear(X, self.phase.X)
S,P,Y = self.phase.SPY
self.assertNear(S, self.phase.s)
self.assertNear(P, self.phase.P)
self.assertArrayNear(Y, self.phase.Y)
S,P = self.phase.SP
self.assertNear(S, self.phase.s)
self.assertNear(P, self.phase.P)
S,V,X = self.phase.SVX
self.assertNear(S, self.phase.s)
self.assertNear(V, self.phase.v)
self.assertArrayNear(X, self.phase.X)
S,V,Y = self.phase.SVY
self.assertNear(S, self.phase.s)
self.assertNear(V, self.phase.v)
self.assertArrayNear(Y, self.phase.Y)
S,V = self.phase.SV
self.assertNear(S, self.phase.s)
self.assertNear(V, self.phase.v)
D,P,X = self.phase.DPX
self.assertNear(D, self.phase.density)
self.assertNear(P, self.phase.P)
self.assertArrayNear(X, self.phase.X)
D,P,Y = self.phase.DPY
self.assertNear(D, self.phase.density)
self.assertNear(P, self.phase.P)
self.assertArrayNear(Y, self.phase.Y)
D,P = self.phase.DP
self.assertNear(D, self.phase.density)
self.assertNear(P, self.phase.P)
def test_getState_mass(self):
self.phase.TDY = 350.0, 0.7, 'H2:0.1, H2O2:0.1, AR:0.8'
self.check_getters()
def test_getState_mole(self):
self.phase.basis = 'molar'
self.phase.TDX = 350.0, 0.01, 'H2:0.1, O2:0.3, AR:0.6'
self.check_getters()
def test_getState(self):
self.assertNear(self.phase.P, ct.one_atm)
self.assertNear(self.phase.T, 300)
def test_partial_molar(self):
self.phase.TDY = 350.0, 0.6, 'H2:0.1, H2O2:0.1, AR:0.8'
self.assertNear(sum(self.phase.partial_molar_enthalpies * self.phase.X),
self.phase.enthalpy_mole)
self.assertNear(sum(self.phase.partial_molar_entropies * self.phase.X),
self.phase.entropy_mole)
self.assertNear(sum(self.phase.partial_molar_int_energies * self.phase.X),
self.phase.int_energy_mole)
self.assertNear(sum(self.phase.chemical_potentials * self.phase.X),
self.phase.gibbs_mole)
self.assertNear(sum(self.phase.partial_molar_cp * self.phase.X),
self.phase.cp_mole)
def test_nondimensional(self):
self.phase.TDY = 850.0, 0.2, 'H2:0.1, H2O:0.6, AR:0.3'
H = (sum(self.phase.standard_enthalpies_RT * self.phase.X) *
ct.gas_constant * self.phase.T)
self.assertNear(H, self.phase.enthalpy_mole)
U = (sum(self.phase.standard_int_energies_RT * self.phase.X) *
ct.gas_constant * self.phase.T)
self.assertNear(U, self.phase.int_energy_mole)
cp = sum(self.phase.standard_cp_R * self.phase.X) * ct.gas_constant
self.assertNear(cp, self.phase.cp_mole)
def test_isothermal_compressibility(self):
self.assertNear(self.phase.isothermal_compressibility, 1.0/self.phase.P)
def test_thermal_expansion_coeff(self):
self.assertNear(self.phase.thermal_expansion_coeff, 1.0/self.phase.T)
def test_ref_info(self):
self.assertNear(self.phase.reference_pressure, ct.one_atm)
self.assertNear(self.phase.min_temp, 300.0)
self.assertNear(self.phase.max_temp, 3500.0)
def test_unpicklable(self):
import pickle
with self.assertRaises(NotImplementedError):
pickle.dumps(self.phase)
def test_uncopyable(self):
import copy
with self.assertRaises(NotImplementedError):
copy.copy(self.phase)
class TestThermo(utilities.CanteraTest):
def setUp(self):
self.gas = ct.ThermoPhase('h2o2.xml')
self.gas.TPX = 450, 2e5, 'H2:1.0, O2:0.4, AR:3, H2O:0.1'
def test_setSV_lowT(self):
"""
Set state in terms of (s,v) when the end temperature is below the
phase's nominal temperature limit.
"""
self.gas.TPX = 450, 1e5, 'H2:1.0, O2:0.4, AR:3'
s1, v1 = self.gas.SV
self.gas.SV = s1, 3 * v1
self.assertNear(self.gas.s, s1)
self.assertNear(self.gas.v, 3 * v1)
self.assertTrue(self.gas.T < self.gas.min_temp)
def test_setSV_highT(self):
"""
Set state in terms of (s,v) when the end temperature is above the
phase's nominal temperature limit.
"""
self.gas.TPX = 2900, 1e5, 'H2:1.0, O2:0.4, AR:3'
s1, v1 = self.gas.SV
self.gas.SV = s1, 0.3 * v1
self.assertNear(self.gas.s, s1)
self.assertNear(self.gas.v, 0.3 * v1)
self.assertTrue(self.gas.T > self.gas.max_temp)
def test_setHP_lowT(self):
"""
Set state in terms of (s,v) when the end temperature is below the
phase's nominal temperature limit.
"""
self.gas.TPX = 450, 1e5, 'H2:1.0, O2:0.4, AR:3'
deltaH = 1.25e5
h1, p1 = self.gas.HP
self.gas.HP = h1 - deltaH, None
self.assertNear(self.gas.h, h1 - deltaH)
self.assertNear(self.gas.P, p1)
self.assertTrue(self.gas.T < self.gas.min_temp)
def test_setHP_highT(self):
"""
Set state in terms of (s,v) when the end temperature is above the
phase's nominal temperature limit.
"""
self.gas.TPX = 2800, 1e5, 'H2:1.0, O2:0.4, AR:3'
deltaH = 8.25e5
h1, p1 = self.gas.HP
self.gas.HP = h1 + deltaH, None
self.assertNear(self.gas.h, h1 + deltaH)
self.assertNear(self.gas.P, p1)
self.assertTrue(self.gas.T > self.gas.max_temp)
def test_volume(self):
""" This phase should follow the ideal gas law """
g = self.gas
self.assertAlmostEqual(g.P, g.density_mole * ct.gas_constant * g.T)
self.assertAlmostEqual(
g.P / g.density,
ct.gas_constant / g.mean_molecular_weight * g.T)
self.assertAlmostEqual(g.density, 1.0 / g.volume_mass)
def test_energy(self):
g = self.gas
mmw = g.mean_molecular_weight
self.assertAlmostEqual(g.enthalpy_mass, g.enthalpy_mole / mmw)
self.assertAlmostEqual(g.int_energy_mass, g.int_energy_mole / mmw)
self.assertAlmostEqual(g.gibbs_mass, g.gibbs_mole / mmw)
self.assertAlmostEqual(g.entropy_mass, g.entropy_mole / mmw)
self.assertAlmostEqual(g.cv_mass, g.cv_mole / mmw)
self.assertAlmostEqual(g.cp_mass, g.cp_mole / mmw)
self.assertAlmostEqual(g.cv_mole + ct.gas_constant, g.cp_mole)
def test_nondimensional(self):
g = self.gas
R = ct.gas_constant
self.assertAlmostEqual(np.dot(g.standard_cp_R, g.X),
g.cp_mole / R)
self.assertAlmostEqual(np.dot(g.standard_enthalpies_RT, g.X),
g.enthalpy_mole / (R*g.T))
Smix_R = - np.dot(g.X, np.log(g.X+1e-20))
self.assertAlmostEqual(np.dot(g.standard_entropies_R, g.X) + Smix_R,
g.entropy_mole / R)
self.assertAlmostEqual(np.dot(g.standard_gibbs_RT, g.X) - Smix_R,
g.gibbs_mole / (R*g.T))
class TestInterfacePhase(utilities.CanteraTest):
def setUp(self):
self.gas = ct.Solution('diamond.xml', 'gas')
self.solid = ct.Solution('diamond.xml', 'diamond')
self.interface = ct.Interface('diamond.xml', 'diamond_100',
(self.gas, self.solid))
def test_properties(self):
self.interface.site_density = 100
self.assertNear(self.interface.site_density, 100)
def test_coverages_array(self):
C = np.zeros(self.interface.n_species)
C[1] = 0.25
C[3] = 0.125
C[4] = 0.125
self.interface.coverages = C
C = self.interface.coverages
# should now be normalized
self.assertNear(C[1], 0.5)
self.assertNear(C[3], 0.25)
self.assertNear(C[4], 0.25)
self.assertNear(sum(C), 1.0)
def test_coverages_string(self):
self.interface.coverages = 'c6HM:0.2, c6H*:0.8'
C = self.interface.coverages
self.assertNear(C[self.interface.species_index('c6HM')], 0.2)
self.assertNear(C[self.interface.species_index('c6H*')], 0.8)
def test_coverages_dict(self):
self.interface.coverages = {'c6**':1.0, 'c6*M':3.0}
C = self.interface.coverages
self.assertNear(C[self.interface.species_index('c6**')], 0.25)
self.assertNear(C[self.interface.species_index('c6*M')], 0.75)
class ImportTest(utilities.CanteraTest):
"""
Test the various ways of creating a Solution object
"""
def check(self, gas, name, T, P, nSpec, nElem):
self.assertEqual(gas.name, name)
self.assertAlmostEqual(gas.T, T)
self.assertAlmostEqual(gas.P, P)
self.assertEqual(gas.n_species, nSpec)
self.assertEqual(gas.n_elements, nElem)
def test_import_phase_cti(self):
gas1 = ct.Solution('../data/air-no-reactions.cti', 'air')
self.check(gas1, 'air', 300, 101325, 8, 3)
gas2 = ct.Solution('../data/air-no-reactions.cti', 'notair')
self.check(gas2, 'notair', 900, 5*101325, 7, 2)
def test_import_phase_cti2(self):
# This should import the first phase, i.e. 'air'
gas = ct.Solution('../data/air-no-reactions.cti')
self.check(gas, 'air', 300, 101325, 8, 3)
def test_import_phase_xml(self):
gas1 = ct.Solution('../data/air-no-reactions.xml', 'air')
self.check(gas1, 'air', 300, 101325, 8, 3)
gas2 = ct.Solution('../data/air-no-reactions.xml', 'notair')
self.check(gas2, 'notair', 900, 5*101325, 7, 2)
def test_import_phase_cti_text(self):
cti_def = """
ideal_gas(name='spam', elements='O H',
species='gri30: all',
options='skip_undeclared_elements',
initial_state=state(temperature=350, pressure=2e6))
"""
gas = ct.Solution(source=cti_def)
self.check(gas, 'spam', 350, 2e6, 8, 2)
def test_import_phase_xml_text(self):
xml_def = """
<?xml version="1.0"?>
<ctml>
<validate reactions="yes" species="yes"/>
<phase dim="3" id="spam">
<elementArray datasrc="elements.xml">O</elementArray>
<speciesArray datasrc="gri30.xml#species_data">all
<skip element="undeclared"/>
</speciesArray>
<state>
<temperature units="K">350.0</temperature>
<pressure units="Pa">2000000.0</pressure>
</state>
<thermo model="IdealGas"/>
<kinetics model="GasKinetics"/>
<transport model="None"/>
</phase>
</ctml>"""
gas = ct.Solution(source=xml_def)
self.check(gas, 'spam', 350, 2e6, 2, 1)
def test_import_from_species(self):
gas1 = ct.Solution('h2o2.xml')
gas1.TPX = 350, 101325, 'H2:0.3, O2:0.7'
gas1.equilibrate('HP')
species = ct.Species.listFromFile('h2o2.xml')
gas2 = ct.ThermoPhase(thermo='IdealGas', species=species)
gas2.TPX = 350, 101325, 'H2:0.3, O2:0.7'
gas2.equilibrate('HP')
self.assertEqual(gas1.n_elements, gas2.n_elements)
self.assertEqual(gas1.species_names, gas2.species_names)
self.assertNear(gas1.T, gas2.T)
self.assertArrayNear(gas1.X, gas2.X)
def test_checkReactionBalance(self):
with self.assertRaises(Exception):
ct.Solution('../data/h2o2_unbalancedReaction.xml')
class TestSpecies(utilities.CanteraTest):
def setUp(self):
self.gas = ct.Solution('h2o2.xml')
def test_standalone(self):
s = ct.Species('CH4', {'C':1, 'H':4})
self.assertEqual(s.name, 'CH4')
c = s.composition
self.assertEqual(len(c), 2)
self.assertEqual(c['C'], 1)
self.assertEqual(c['H'], 4)
def test_defaults(self):
s = ct.Species('H2')
self.assertEqual(s.size, 1.0)
self.assertEqual(s.charge, 0.0)
self.assertIsNone(s.thermo)
self.assertIsNone(s.transport)
def test_index_accessor(self):
for k in range(self.gas.n_species):
s = self.gas.species(k)
self.assertEqual(s.name, self.gas.species_name(k))
for m,n in s.composition.items():
self.assertEqual(n, self.gas.n_atoms(k,m))
def test_species_noargs(self):
for k,s in enumerate(self.gas.species()):
self.assertEqual(s.name, self.gas.species_name(k))
def test_name_accessor(self):
for name in self.gas.species_names:
s = self.gas.species(name)
self.assertEqual(s.name, name)
def test_fromCti(self):
h2_cti = """
species(
name="H2",
atoms="H:2",
thermo=(
NASA([200.00, 1000.00],
[2.344331120E+00, 7.980520750E-03, -1.947815100E-05,
2.015720940E-08, -7.376117610E-12, -9.179351730E+02,
6.830102380E-01]),
NASA([1000.00, 3500.00],
[3.337279200E+00, -4.940247310E-05, 4.994567780E-07,
-1.795663940E-10, 2.002553760E-14, -9.501589220E+02,
-3.205023310E+00])
),
transport=gas_transport(geom="linear",
diam=2.92,
well_depth=38.00,
polar=0.79,
rot_relax=280.00),
note = "TPIS78"
)"""
s1 = self.gas.species('H2')
s2 = ct.Species.fromCti(h2_cti)
self.assertEqual(s2.name, 'H2')
self.assertEqual(s1.composition, s2.composition)
self.assertEqual(s1.thermo.cp(350), s2.thermo.cp(350))
def test_fromXml(self):
import xml.etree.ElementTree as ET
root = ET.parse('../../build/data/h2o2.xml').getroot()
h2_node = root.find('.//species[@name="H2"]')
h2_string = ET.tostring(h2_node)
s1 = self.gas.species('H2')
s2 = ct.Species.fromXml(h2_string)
self.assertEqual(s2.name, 'H2')
self.assertEqual(s1.composition, s2.composition)
self.assertEqual(s1.thermo.cp(350), s2.thermo.cp(350))
def test_listFromFile_cti(self):
S = ct.Species.listFromFile('h2o2.cti')
self.assertEqual({sp.name for sp in S},
set(self.gas.species_names))
def test_listFromFile_xml(self):
S = ct.Species.listFromFile('h2o2.xml')
self.assertEqual({sp.name for sp in S},
set(self.gas.species_names))
def test_listFromCti(self):
S = ct.Species.listFromCti(open('../../build/data/h2o2.cti').read())
self.assertEqual({sp.name for sp in S},
set(self.gas.species_names))
def test_listFromXml(self):
S = ct.Species.listFromXml(open('../../build/data/h2o2.xml').read())
self.assertEqual({sp.name for sp in S},
set(self.gas.species_names))
class TestSpeciesThermo(utilities.CanteraTest):
h2o_coeffs = [
1000.0, 3.03399249E+00, 2.17691804E-03, -1.64072518E-07,
-9.70419870E-11, 1.68200992E-14, -3.00042971E+04, 4.96677010E+00,
4.19864056E+00, -2.03643410E-03, 6.52040211E-06, -5.48797062E-09,
1.77197817E-12, -3.02937267E+04, -8.49032208E-01
]
def setUp(self):
self.gas = ct.Solution('h2o2.xml')
self.gas.X = 'H2O:1.0'
def test_create(self):
st = ct.NasaPoly2(300, 3500, 101325, self.h2o_coeffs)
for T in [300, 500, 900, 1200, 2000]:
self.gas.TP = T, 101325
self.assertAlmostEqual(st.cp(T), self.gas.cp_mole)
self.assertAlmostEqual(st.h(T), self.gas.enthalpy_mole)
self.assertAlmostEqual(st.s(T), self.gas.entropy_mole)
def test_invalid(self):
with self.assertRaises(ValueError):
# not enough coefficients
st = ct.NasaPoly2(300, 3500, 101325,
[1000.0, 3.03399249E+00, 2.17691804E-03])
def test_wrap(self):
st = self.gas.species('H2O').thermo
self.assertTrue(isinstance(st, ct.NasaPoly2))
for T in [300, 500, 900, 1200, 2000]:
self.gas.TP = T, 101325
self.assertAlmostEqual(st.cp(T), self.gas.cp_mole)
self.assertAlmostEqual(st.h(T), self.gas.enthalpy_mole)
self.assertAlmostEqual(st.s(T), self.gas.entropy_mole)
def test_coeffs(self):
st = ct.NasaPoly2(300, 3500, 101325, self.h2o_coeffs)
self.assertEqual(st.min_temp, 300)
self.assertEqual(st.max_temp, 3500)
self.assertEqual(st.reference_pressure, 101325)
self.assertArrayNear(self.h2o_coeffs, st.coeffs)
class TestQuantity(utilities.CanteraTest):
@classmethod
def setUpClass(cls):
cls.gas = ct.Solution('gri30.xml')
def setUp(self):
self.gas.TPX = 300, 101325, 'O2:1.0, N2:3.76'
def test_mass_moles(self):
q1 = ct.Quantity(self.gas, mass=5)
self.assertNear(q1.mass, 5)
self.assertNear(q1.moles, 5 / q1.mean_molecular_weight)
q1.mass = 7
self.assertNear(q1.moles, 7 / q1.mean_molecular_weight)
q1.moles = 9
self.assertNear(q1.moles, 9)
self.assertNear(q1.mass, 9 * q1.mean_molecular_weight)
def test_extensive(self):
q1 = ct.Quantity(self.gas, mass=5)
self.assertNear(q1.mass, 5)
self.assertNear(q1.volume * q1.density, q1.mass)
self.assertNear(q1.V * q1.density, q1.mass)
self.assertNear(q1.int_energy, q1.moles * q1.int_energy_mole)
self.assertNear(q1.enthalpy, q1.moles * q1.enthalpy_mole)
self.assertNear(q1.entropy, q1.moles * q1.entropy_mole)
self.assertNear(q1.gibbs, q1.moles * q1.gibbs_mole)
self.assertNear(q1.int_energy, q1.U)
self.assertNear(q1.enthalpy, q1.H)
self.assertNear(q1.entropy, q1.S)
self.assertNear(q1.gibbs, q1.G)
def test_multiply(self):
q1 = ct.Quantity(self.gas, mass=5)
q2 = q1 * 2.5
self.assertNear(q1.mass * 2.5, q2.mass)
self.assertNear(q1.moles * 2.5, q2.moles)
self.assertNear(q1.entropy * 2.5, q2.entropy)
self.assertArrayNear(q1.X, q2.X)
def test_iadd(self):
q0 = ct.Quantity(self.gas, mass=5)
q1 = ct.Quantity(self.gas, mass=5)
q2 = ct.Quantity(self.gas, mass=5)
q2.TPX = 500, 101325, 'CH4:1.0'
q1 += q2
self.assertNear(q0.mass + q2.mass, q1.mass)
# addition is at constant UV
self.assertNear(q0.U + q2.U, q1.U)
self.assertNear(q0.V + q2.V, q1.V)
self.assertArrayNear(q0.X*q0.moles + q2.X*q2.moles, q1.X*q1.moles)
def test_add(self):
q1 = ct.Quantity(self.gas, mass=5)
q2 = ct.Quantity(self.gas, mass=5)
q2.TPX = 500, 101325, 'CH4:1.0'
q3 = q1 + q2
self.assertNear(q1.mass + q2.mass, q3.mass)
# addition is at constant UV
self.assertNear(q1.U + q2.U, q3.U)
self.assertNear(q1.V + q2.V, q3.V)
self.assertArrayNear(q1.X*q1.moles + q2.X*q2.moles, q3.X*q3.moles)
def test_equilibrate(self):
self.gas.TPX = 300, 101325, 'CH4:1.0, O2:0.2, N2:1.0'
q1 = ct.Quantity(self.gas)
self.gas.equilibrate('HP')
T2 = self.gas.T
self.assertNear(q1.T, 300)
q1.equilibrate('HP')
self.assertNear(q1.T, T2)
def test_incompatible(self):
gas2 = ct.Solution('h2o2.xml')
q1 = ct.Quantity(self.gas)
q2 = ct.Quantity(gas2)
with self.assertRaises(Exception):
q1+q2