2021-04-11 11:17:11 -04:00
|
|
|
import sys
|
2019-11-01 23:49:15 -05:00
|
|
|
|
|
|
|
|
import numpy as np
|
2020-04-07 21:33:04 -05:00
|
|
|
from collections import OrderedDict
|
2021-11-27 20:46:38 -06:00
|
|
|
import pickle
|
2019-11-01 23:49:15 -05:00
|
|
|
|
|
|
|
|
import cantera as ct
|
2022-12-20 16:10:51 -05:00
|
|
|
|
|
|
|
|
try:
|
2023-01-22 13:01:08 -05:00
|
|
|
ct.composite._import_pandas()
|
2022-12-20 16:10:51 -05:00
|
|
|
except ImportError:
|
2023-01-22 13:01:08 -05:00
|
|
|
pass
|
2022-12-20 16:10:51 -05:00
|
|
|
|
2023-01-22 13:01:08 -05:00
|
|
|
try:
|
|
|
|
|
ct.composite._import_h5py()
|
|
|
|
|
except ImportError:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
from cantera.composite import _pandas, _h5py
|
2019-11-01 23:49:15 -05:00
|
|
|
from . import utilities
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestModels(utilities.CanteraTest):
|
|
|
|
|
|
2019-11-10 22:52:03 -06:00
|
|
|
@classmethod
|
|
|
|
|
def setUpClass(cls):
|
|
|
|
|
utilities.CanteraTest.setUpClass()
|
2021-06-01 18:32:05 -05:00
|
|
|
cls.yml_file = cls.test_data_path / "thermo-models.yaml"
|
2021-05-31 16:16:23 -05:00
|
|
|
cls.yml = utilities.load_yaml(cls.yml_file)
|
2019-11-10 22:52:03 -06:00
|
|
|
|
2019-11-01 23:49:15 -05:00
|
|
|
def test_load_thermo_models(self):
|
2019-11-10 22:52:03 -06:00
|
|
|
for ph in self.yml['phases']:
|
2019-11-01 23:49:15 -05:00
|
|
|
ph_name = ph['name']
|
|
|
|
|
try:
|
2019-11-10 22:52:03 -06:00
|
|
|
sol = ct.Solution(self.yml_file, ph_name)
|
2019-11-01 23:49:15 -05:00
|
|
|
|
|
|
|
|
T0, p0 = sol.TP
|
2019-11-13 12:13:11 -06:00
|
|
|
TD = sol.TD
|
2019-11-01 23:49:15 -05:00
|
|
|
z = sol.state # calls Phase::saveState
|
|
|
|
|
sol.TP = 300, 2*ct.one_atm
|
|
|
|
|
sol.state = z # calls Phase::restoreState
|
|
|
|
|
self.assertEqual(sol.T, T0)
|
|
|
|
|
self.assertEqual(sol.P, p0)
|
|
|
|
|
|
2023-03-10 18:55:24 -05:00
|
|
|
if sol.thermo_model in ('pure-fluid',):
|
2019-12-06 19:02:34 -06:00
|
|
|
self.assertTrue(sol.has_phase_transition)
|
|
|
|
|
else:
|
|
|
|
|
self.assertFalse(sol.has_phase_transition)
|
|
|
|
|
|
2019-11-13 12:13:11 -06:00
|
|
|
if not sol.is_compressible:
|
|
|
|
|
with self.assertRaisesRegex(ct.CanteraError,
|
|
|
|
|
'Density is not an independent'):
|
|
|
|
|
sol.TD = TD
|
|
|
|
|
|
|
|
|
|
self.assertEqual(len(z), sol.state_size)
|
|
|
|
|
if sol.is_pure:
|
2019-11-01 23:49:15 -05:00
|
|
|
# stoich phase (fixed composition)
|
|
|
|
|
self.assertEqual(sol.n_species, 1)
|
|
|
|
|
self.assertEqual(len(z), 2)
|
|
|
|
|
else:
|
|
|
|
|
self.assertEqual(len(z), 2 + sol.n_species)
|
|
|
|
|
|
|
|
|
|
except Exception as inst:
|
|
|
|
|
|
|
|
|
|
# raise meaningful error message without breaking test suite
|
|
|
|
|
# ignore deprecation warnings originating in C++ layer
|
|
|
|
|
# (converted to errors in test suite)
|
|
|
|
|
if 'Deprecated' not in str(inst):
|
|
|
|
|
|
2019-11-10 22:52:03 -06:00
|
|
|
msg = ("Error in processing of phase '{}' with type '{}'\n"
|
|
|
|
|
"TPX = {}")
|
|
|
|
|
msg = msg.format(ph['name'], ph['thermo'], sol.TPX)
|
2019-11-03 09:33:20 -06:00
|
|
|
raise TypeError(msg) from inst
|
2019-11-01 23:49:15 -05:00
|
|
|
|
|
|
|
|
def test_restore_thermo_models(self):
|
|
|
|
|
|
|
|
|
|
def check(a, b):
|
|
|
|
|
self.assertArrayNear(a.T, b.T)
|
|
|
|
|
self.assertArrayNear(a.P, b.P)
|
|
|
|
|
self.assertArrayNear(a.X, b.X)
|
|
|
|
|
|
2019-11-10 22:52:03 -06:00
|
|
|
for ph in self.yml['phases']:
|
2019-11-01 23:49:15 -05:00
|
|
|
|
2019-11-10 22:52:03 -06:00
|
|
|
skipped = ['pure-fluid']
|
2019-11-01 23:49:15 -05:00
|
|
|
if ph['thermo'] in skipped:
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
ph_name = ph['name']
|
|
|
|
|
|
|
|
|
|
try:
|
2019-11-10 22:52:03 -06:00
|
|
|
sol = ct.Solution(self.yml_file, ph_name)
|
2019-11-01 23:49:15 -05:00
|
|
|
a = ct.SolutionArray(sol, 10)
|
2020-08-28 11:52:04 -05:00
|
|
|
if ph['thermo'] == 'liquid-water-IAPWS95':
|
|
|
|
|
# ensure that phase remains liquid
|
|
|
|
|
a.TP = sol.T, sol.critical_pressure
|
2019-11-01 23:49:15 -05:00
|
|
|
|
|
|
|
|
# assign some state
|
|
|
|
|
T = 373.15 + 100*np.random.rand(10)
|
2019-11-03 09:33:20 -06:00
|
|
|
P = a.P * (1 + np.random.rand(10))
|
2019-11-13 12:13:11 -06:00
|
|
|
if sol.is_pure:
|
2019-11-10 22:52:03 -06:00
|
|
|
a.TP = T, P
|
|
|
|
|
else:
|
|
|
|
|
X = a.X
|
|
|
|
|
xmin = np.min(X[X>0])
|
|
|
|
|
ix = np.where(xmin)
|
|
|
|
|
X[ix] = .5 * X[ix]
|
2019-11-01 23:49:15 -05:00
|
|
|
X = np.diag(X.sum(axis=1)).dot(X)
|
2019-11-13 12:13:11 -06:00
|
|
|
self.assertFalse(sol.is_pure)
|
2019-11-03 09:33:20 -06:00
|
|
|
self.assertIn('TPX', sol._full_states.values())
|
|
|
|
|
a.TPX = T, P, X
|
2019-11-01 23:49:15 -05:00
|
|
|
|
|
|
|
|
# default columns
|
2020-04-07 21:33:04 -05:00
|
|
|
data = a.collect_data()
|
2019-11-01 23:49:15 -05:00
|
|
|
b = ct.SolutionArray(sol)
|
2020-04-07 21:33:04 -05:00
|
|
|
b.restore_data(data)
|
2019-11-01 23:49:15 -05:00
|
|
|
check(a, b)
|
|
|
|
|
|
|
|
|
|
except Exception as inst:
|
|
|
|
|
|
|
|
|
|
# raise meaningful error message without breaking test suite
|
|
|
|
|
# ignore deprecation warnings originating in C++ layer
|
|
|
|
|
# (converted to errors in test suite)
|
|
|
|
|
if 'Deprecated' not in str(inst):
|
|
|
|
|
|
2019-11-10 22:52:03 -06:00
|
|
|
msg = ("Error in processing of phase '{}' with type '{}'\n"
|
|
|
|
|
"TPX = {}")
|
|
|
|
|
msg = msg.format(ph['name'], ph['thermo'], sol.TPX)
|
2019-11-03 09:33:20 -06:00
|
|
|
raise TypeError(msg) from inst
|
2019-11-01 23:49:15 -05:00
|
|
|
|
|
|
|
|
|
2021-11-27 20:46:38 -06:00
|
|
|
class TestPickle(utilities.CanteraTest):
|
|
|
|
|
|
|
|
|
|
def test_pickle_gas(self):
|
|
|
|
|
gas = ct.Solution("h2o2.yaml", transport_model=None)
|
|
|
|
|
gas.TPX = 500, 500000, "H2:.75,O2:.25"
|
2022-03-29 14:30:54 -04:00
|
|
|
with open(self.test_work_path / "gas.pkl", "wb") as pkl:
|
2021-11-27 20:46:38 -06:00
|
|
|
pickle.dump(gas, pkl)
|
|
|
|
|
|
2022-03-29 14:30:54 -04:00
|
|
|
with open(self.test_work_path / "gas.pkl", "rb") as pkl:
|
2021-11-27 20:46:38 -06:00
|
|
|
gas2 = pickle.load(pkl)
|
|
|
|
|
self.assertNear(gas.T, gas2.T)
|
|
|
|
|
self.assertNear(gas.P, gas2.P)
|
|
|
|
|
self.assertArrayNear(gas.X, gas2.X)
|
|
|
|
|
|
|
|
|
|
self.assertEqual(gas2.transport_model, "None")
|
|
|
|
|
|
|
|
|
|
def test_pickle_gas_with_transport(self):
|
|
|
|
|
gas = ct.Solution("h2o2.yaml")
|
|
|
|
|
gas.TPX = 500, 500000, "H2:.75,O2:.25"
|
2023-03-10 19:34:00 -05:00
|
|
|
gas.transport_model = "multicomponent"
|
2022-03-29 14:30:54 -04:00
|
|
|
with open(self.test_work_path / "gas.pkl", "wb") as pkl:
|
2021-11-27 20:46:38 -06:00
|
|
|
pickle.dump(gas, pkl)
|
|
|
|
|
|
2022-03-29 14:30:54 -04:00
|
|
|
with open(self.test_work_path / "gas.pkl", "rb") as pkl:
|
2021-11-27 20:46:38 -06:00
|
|
|
gas2 = pickle.load(pkl)
|
|
|
|
|
self.assertNear(gas.T, gas2.T)
|
|
|
|
|
self.assertNear(gas.P, gas2.P)
|
|
|
|
|
self.assertArrayNear(gas.X, gas2.X)
|
|
|
|
|
|
2023-03-10 19:34:00 -05:00
|
|
|
self.assertEqual(gas2.transport_model, "multicomponent")
|
2021-11-27 20:46:38 -06:00
|
|
|
|
|
|
|
|
def test_pickle_interface(self):
|
|
|
|
|
gas = ct.Solution("diamond.yaml", "gas")
|
|
|
|
|
solid = ct.Solution("diamond.yaml", "diamond")
|
|
|
|
|
interface = ct.Interface("diamond.yaml", "diamond_100", (gas, solid))
|
|
|
|
|
|
|
|
|
|
with self.assertRaises(NotImplementedError):
|
2022-03-29 14:30:54 -04:00
|
|
|
with open(self.test_work_path / "interface.pkl", "wb") as pkl:
|
2021-11-27 20:46:38 -06:00
|
|
|
pickle.dump(interface, pkl)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestEmptyThermoPhase(utilities.CanteraTest):
|
|
|
|
|
""" Test empty Solution object """
|
|
|
|
|
@classmethod
|
|
|
|
|
def setUpClass(cls):
|
|
|
|
|
utilities.CanteraTest.setUpClass()
|
|
|
|
|
cls.gas = ct.ThermoPhase()
|
|
|
|
|
|
|
|
|
|
def test_empty_report(self):
|
|
|
|
|
with self.assertRaisesRegex(ct.CanteraError, "NotImplementedError"):
|
|
|
|
|
self.gas()
|
|
|
|
|
|
|
|
|
|
def test_empty_TP(self):
|
|
|
|
|
with self.assertRaisesRegex(ct.CanteraError, "NotImplementedError"):
|
|
|
|
|
self.gas.TP = 300, ct.one_atm
|
|
|
|
|
|
|
|
|
|
def test_empty_equilibrate(self):
|
|
|
|
|
with self.assertRaisesRegex(ct.CanteraError, "NotImplementedError"):
|
|
|
|
|
self.gas.equilibrate("TP")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestEmptySolution(TestEmptyThermoPhase):
|
|
|
|
|
""" Test empty Solution object """
|
|
|
|
|
@classmethod
|
|
|
|
|
def setUpClass(cls):
|
|
|
|
|
utilities.CanteraTest.setUpClass()
|
|
|
|
|
cls.gas = ct.Solution()
|
|
|
|
|
|
|
|
|
|
def test_empty_composite(self):
|
|
|
|
|
self.assertEqual(self.gas.thermo_model, "None")
|
|
|
|
|
self.assertEqual(self.gas.composite, ("None", "None", "None"))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestEmptyEdgeCases(utilities.CanteraTest):
|
|
|
|
|
""" Test for edge cases where constructors are not allowed """
|
|
|
|
|
def test_empty_phase(self):
|
|
|
|
|
with self.assertRaisesRegex(ValueError, "Arguments are insufficient to define a phase"):
|
|
|
|
|
ct.ThermoPhase(thermo="ideal-gas")
|
|
|
|
|
|
|
|
|
|
def test_empty_kinetics(self):
|
|
|
|
|
with self.assertRaisesRegex(ValueError, "Cannot instantiate"):
|
|
|
|
|
ct.Kinetics()
|
|
|
|
|
|
|
|
|
|
def test_empty_transport(self):
|
|
|
|
|
with self.assertRaisesRegex(ValueError, "Cannot instantiate"):
|
|
|
|
|
ct.Transport()
|
|
|
|
|
|
|
|
|
|
|
2020-04-06 08:44:51 -05:00
|
|
|
class TestSolutionArrayIO(utilities.CanteraTest):
|
|
|
|
|
""" Test SolutionArray file IO """
|
|
|
|
|
@classmethod
|
|
|
|
|
def setUpClass(cls):
|
|
|
|
|
utilities.CanteraTest.setUpClass()
|
2021-04-26 20:21:59 -04:00
|
|
|
cls.gas = ct.Solution('h2o2.yaml', transport_model=None)
|
2020-04-06 08:44:51 -05:00
|
|
|
|
2020-05-30 11:18:22 -05:00
|
|
|
def test_collect_data(self):
|
|
|
|
|
states = ct.SolutionArray(self.gas)
|
|
|
|
|
collected = states.collect_data(tabular=True)
|
|
|
|
|
self.assertIsInstance(collected, dict)
|
|
|
|
|
self.assertIn('Y_H2', collected)
|
|
|
|
|
self.assertEqual(len(collected['Y_H2']), 0)
|
|
|
|
|
|
|
|
|
|
states = ct.SolutionArray(self.gas)
|
|
|
|
|
collected = states.collect_data(tabular=False, species='X')
|
|
|
|
|
self.assertIn('X', collected)
|
|
|
|
|
self.assertEqual(collected['X'].shape, (0, self.gas.n_species))
|
|
|
|
|
|
2022-02-21 08:39:12 -06:00
|
|
|
def test_getitem(self):
|
|
|
|
|
states = ct.SolutionArray(self.gas, 10, extra={"index": range(10)})
|
|
|
|
|
for ix, state in enumerate(states):
|
|
|
|
|
assert state.index == ix
|
|
|
|
|
|
|
|
|
|
assert list(states[:2].index) == [0, 1]
|
|
|
|
|
assert list(states[100:102].index) == [] # outside of range
|
|
|
|
|
|
2021-06-02 17:39:55 -05:00
|
|
|
def test_append_state(self):
|
2021-05-18 17:30:39 -05:00
|
|
|
gas = ct.Solution("h2o2.yaml")
|
2021-06-02 17:39:55 -05:00
|
|
|
gas.TPX = 300, ct.one_atm, 'H2:0.5, O2:0.4'
|
2021-05-11 08:30:45 -05:00
|
|
|
states = ct.SolutionArray(gas)
|
2021-06-02 17:39:55 -05:00
|
|
|
states.append(gas.state)
|
2021-05-11 08:30:45 -05:00
|
|
|
self.assertEqual(states[0].T, gas.T)
|
|
|
|
|
self.assertEqual(states[0].P, gas.P)
|
2021-06-02 17:39:55 -05:00
|
|
|
self.assertArrayNear(states[0].X, gas.X)
|
2022-05-17 17:00:02 -04:00
|
|
|
self.assertEqual(len(states), 1)
|
|
|
|
|
self.assertEqual(states.shape, (1,))
|
|
|
|
|
self.assertEqual(states.ndim, 1)
|
|
|
|
|
self.assertEqual(states.size, 1)
|
2021-05-26 14:14:14 -05:00
|
|
|
|
2021-06-02 17:39:55 -05:00
|
|
|
def test_append_no_norm_data(self):
|
2021-05-28 17:09:36 -05:00
|
|
|
gas = ct.Solution("h2o2.yaml")
|
|
|
|
|
gas.TP = 300, ct.one_atm
|
|
|
|
|
gas.set_unnormalized_mass_fractions(np.full(gas.n_species, 0.3))
|
|
|
|
|
states = ct.SolutionArray(gas)
|
2021-06-02 17:39:55 -05:00
|
|
|
states.append(T=gas.T, P=gas.P, Y=gas.Y, normalize=False)
|
2021-05-28 17:09:36 -05:00
|
|
|
self.assertEqual(states[0].T, gas.T)
|
|
|
|
|
self.assertEqual(states[0].P, gas.P)
|
|
|
|
|
self.assertArrayNear(states[0].Y, gas.Y)
|
|
|
|
|
|
2023-01-22 13:01:08 -05:00
|
|
|
@utilities.unittest.skipIf(_h5py is None, "h5py is not installed")
|
2021-05-12 14:44:05 -05:00
|
|
|
def test_import_no_norm_data(self):
|
2021-06-15 10:44:04 -05:00
|
|
|
outfile = self.test_work_path / "solutionarray.h5"
|
|
|
|
|
# In Python >= 3.8, this can be replaced by the missing_ok argument
|
|
|
|
|
if outfile.is_file():
|
|
|
|
|
outfile.unlink()
|
2021-05-11 08:30:45 -05:00
|
|
|
|
2021-05-18 17:30:39 -05:00
|
|
|
gas = ct.Solution("h2o2.yaml")
|
2021-05-28 14:27:14 -05:00
|
|
|
gas.set_unnormalized_mole_fractions(np.full(gas.n_species, 0.3))
|
2021-05-11 08:30:45 -05:00
|
|
|
states = ct.SolutionArray(gas, 5)
|
2021-05-28 14:27:14 -05:00
|
|
|
states.write_hdf(outfile)
|
2021-05-11 08:30:45 -05:00
|
|
|
|
2021-05-18 17:30:39 -05:00
|
|
|
gas_new = ct.Solution("h2o2.yaml")
|
2021-05-11 08:30:45 -05:00
|
|
|
b = ct.SolutionArray(gas_new)
|
2021-05-28 14:27:14 -05:00
|
|
|
b.read_hdf(outfile, normalize=False)
|
2021-05-11 08:30:45 -05:00
|
|
|
self.assertArrayNear(states.T, b.T)
|
|
|
|
|
self.assertArrayNear(states.P, b.P)
|
|
|
|
|
self.assertArrayNear(states.X, b.X)
|
|
|
|
|
|
2020-04-06 08:44:51 -05:00
|
|
|
def test_write_csv(self):
|
|
|
|
|
states = ct.SolutionArray(self.gas, 7)
|
|
|
|
|
states.TPX = np.linspace(300, 1000, 7), 2e5, 'H2:0.5, O2:0.4'
|
|
|
|
|
states.equilibrate('HP')
|
|
|
|
|
|
2021-06-01 18:32:05 -05:00
|
|
|
outfile = self.test_work_path / "solutionarray.csv"
|
2020-04-06 08:44:51 -05:00
|
|
|
states.write_csv(outfile)
|
|
|
|
|
|
|
|
|
|
data = np.genfromtxt(outfile, names=True, delimiter=',')
|
|
|
|
|
self.assertEqual(len(data), 7)
|
|
|
|
|
self.assertEqual(len(data.dtype), self.gas.n_species + 2)
|
|
|
|
|
self.assertIn('Y_H2', data.dtype.fields)
|
|
|
|
|
|
|
|
|
|
b = ct.SolutionArray(self.gas)
|
|
|
|
|
b.read_csv(outfile)
|
2020-08-06 21:28:13 -05:00
|
|
|
self.assertArrayNear(states.T, b.T)
|
|
|
|
|
self.assertArrayNear(states.P, b.P)
|
|
|
|
|
self.assertArrayNear(states.X, b.X)
|
2020-04-06 08:44:51 -05:00
|
|
|
|
2021-07-16 09:23:32 -05:00
|
|
|
def test_write_csv_single_row(self):
|
|
|
|
|
gas = ct.Solution("gri30.yaml")
|
|
|
|
|
states = ct.SolutionArray(gas)
|
|
|
|
|
states.append(T=300., P=ct.one_atm, X="CH4:0.5, O2:0.4")
|
|
|
|
|
states.equilibrate("HP")
|
|
|
|
|
|
|
|
|
|
outfile = self.test_work_path / "solutionarray.csv"
|
|
|
|
|
states.write_csv(outfile)
|
|
|
|
|
|
|
|
|
|
b = ct.SolutionArray(gas)
|
|
|
|
|
b.read_csv(outfile)
|
|
|
|
|
self.assertArrayNear(states.T, b.T)
|
|
|
|
|
self.assertArrayNear(states.P, b.P)
|
|
|
|
|
self.assertArrayNear(states.X, b.X)
|
|
|
|
|
|
2020-07-10 14:46:32 -05:00
|
|
|
def test_write_csv_str_column(self):
|
|
|
|
|
states = ct.SolutionArray(self.gas, 3, extra={'spam': 'eggs'})
|
|
|
|
|
|
2021-06-01 18:32:05 -05:00
|
|
|
outfile = self.test_work_path / "solutionarray.csv"
|
2020-07-10 14:46:32 -05:00
|
|
|
states.write_csv(outfile)
|
|
|
|
|
|
|
|
|
|
b = ct.SolutionArray(self.gas, extra={'spam'})
|
|
|
|
|
b.read_csv(outfile)
|
2020-08-06 18:06:14 -05:00
|
|
|
self.assertEqual(list(states.spam), list(b.spam))
|
2020-07-10 14:46:32 -05:00
|
|
|
|
2020-09-05 13:46:31 -05:00
|
|
|
def test_write_csv_multidim_column(self):
|
|
|
|
|
states = ct.SolutionArray(self.gas, 3, extra={'spam': np.zeros((3, 5,))})
|
|
|
|
|
|
2021-06-01 18:32:05 -05:00
|
|
|
outfile = self.test_work_path / "solutionarray.csv"
|
2020-09-05 13:46:31 -05:00
|
|
|
with self.assertRaisesRegex(NotImplementedError, 'not supported'):
|
|
|
|
|
states.write_csv(outfile)
|
|
|
|
|
|
2023-01-22 13:01:08 -05:00
|
|
|
@utilities.unittest.skipIf(_pandas is None, "pandas is not installed")
|
2020-04-06 08:44:51 -05:00
|
|
|
def test_to_pandas(self):
|
2020-09-05 13:46:31 -05:00
|
|
|
states = ct.SolutionArray(self.gas, 7, extra={"props": range(7)})
|
2020-04-06 08:44:51 -05:00
|
|
|
states.TPX = np.linspace(300, 1000, 7), 2e5, 'H2:0.5, O2:0.4'
|
2020-09-05 13:46:31 -05:00
|
|
|
df = states.to_pandas()
|
|
|
|
|
self.assertEqual(df.shape[0], 7)
|
|
|
|
|
|
|
|
|
|
states.props = np.zeros((7,2,))
|
|
|
|
|
with self.assertRaisesRegex(NotImplementedError, 'not supported'):
|
|
|
|
|
states.to_pandas()
|
2020-04-06 08:44:51 -05:00
|
|
|
|
2023-01-22 13:01:08 -05:00
|
|
|
@utilities.unittest.skipIf(_h5py is None, "h5py is not installed")
|
2020-04-06 08:44:51 -05:00
|
|
|
def test_write_hdf(self):
|
2021-06-01 18:32:05 -05:00
|
|
|
outfile = self.test_work_path / "solutionarray.h5"
|
2021-06-01 22:09:40 -05:00
|
|
|
# In Python >= 3.8, this can be replaced by the missing_ok argument
|
2021-06-01 18:32:05 -05:00
|
|
|
if outfile.is_file():
|
|
|
|
|
outfile.unlink()
|
2020-04-06 08:44:51 -05:00
|
|
|
|
|
|
|
|
extra = {'foo': range(7), 'bar': range(7)}
|
|
|
|
|
meta = {'spam': 'eggs', 'hello': 'world'}
|
|
|
|
|
states = ct.SolutionArray(self.gas, 7, extra=extra, meta=meta)
|
|
|
|
|
states.TPX = np.linspace(300, 1000, 7), 2e5, 'H2:0.5, O2:0.4'
|
|
|
|
|
states.equilibrate('HP')
|
|
|
|
|
|
|
|
|
|
states.write_hdf(outfile, attrs={'foobar': 'spam and eggs'})
|
|
|
|
|
|
|
|
|
|
b = ct.SolutionArray(self.gas)
|
|
|
|
|
attr = b.read_hdf(outfile)
|
2020-08-06 21:28:13 -05:00
|
|
|
self.assertArrayNear(states.T, b.T)
|
|
|
|
|
self.assertArrayNear(states.P, b.P)
|
|
|
|
|
self.assertArrayNear(states.X, b.X)
|
|
|
|
|
self.assertArrayNear(states.foo, b.foo)
|
|
|
|
|
self.assertArrayNear(states.bar, b.bar)
|
2020-04-06 08:44:51 -05:00
|
|
|
self.assertEqual(b.meta['spam'], 'eggs')
|
|
|
|
|
self.assertEqual(b.meta['hello'], 'world')
|
|
|
|
|
self.assertEqual(attr['foobar'], 'spam and eggs')
|
|
|
|
|
|
2021-04-26 20:21:59 -04:00
|
|
|
gas = ct.Solution('gri30.yaml', transport_model=None)
|
2020-04-06 08:44:51 -05:00
|
|
|
ct.SolutionArray(gas, 10).write_hdf(outfile)
|
|
|
|
|
|
2023-01-22 13:01:08 -05:00
|
|
|
with _h5py.File(outfile, 'a') as hdf:
|
2020-04-06 08:44:51 -05:00
|
|
|
hdf.create_group('spam')
|
|
|
|
|
|
|
|
|
|
c = ct.SolutionArray(self.gas)
|
2022-12-05 12:06:01 -06:00
|
|
|
with self.assertRaisesRegex(ValueError, 'requires a non-empty data dictionary'):
|
2020-04-06 08:44:51 -05:00
|
|
|
c.read_hdf(outfile, group='spam')
|
|
|
|
|
with self.assertRaisesRegex(IOError, 'does not contain group'):
|
|
|
|
|
c.read_hdf(outfile, group='eggs')
|
|
|
|
|
with self.assertRaisesRegex(IOError, 'phases do not match'):
|
|
|
|
|
c.read_hdf(outfile, group='group1')
|
2020-04-10 19:05:21 -05:00
|
|
|
with self.assertRaisesRegex(IOError, 'does not contain data'):
|
2020-05-30 15:34:41 -05:00
|
|
|
c.read_hdf(outfile, subgroup='foo')
|
2020-04-06 08:44:51 -05:00
|
|
|
|
2020-05-30 15:34:41 -05:00
|
|
|
states.write_hdf(outfile, group='foo/bar/baz')
|
|
|
|
|
c.read_hdf(outfile, group='foo/bar/baz')
|
2020-08-06 21:28:13 -05:00
|
|
|
self.assertArrayNear(states.T, c.T)
|
2020-04-06 08:44:51 -05:00
|
|
|
|
2023-01-22 13:01:08 -05:00
|
|
|
@utilities.unittest.skipIf(_h5py is None, "h5py is not installed")
|
2020-07-10 16:06:52 -05:00
|
|
|
def test_write_hdf_str_column(self):
|
2021-06-01 18:32:05 -05:00
|
|
|
outfile = self.test_work_path / "solutionarray.h5"
|
2021-06-01 22:09:40 -05:00
|
|
|
# In Python >= 3.8, this can be replaced by the missing_ok argument
|
2021-06-01 18:32:05 -05:00
|
|
|
if outfile.is_file():
|
|
|
|
|
outfile.unlink()
|
2020-09-05 13:46:31 -05:00
|
|
|
|
2020-07-10 16:06:52 -05:00
|
|
|
states = ct.SolutionArray(self.gas, 3, extra={'spam': 'eggs'})
|
2020-09-05 13:46:31 -05:00
|
|
|
states.write_hdf(outfile, mode='w')
|
|
|
|
|
|
|
|
|
|
b = ct.SolutionArray(self.gas, extra={'spam'})
|
|
|
|
|
b.read_hdf(outfile)
|
|
|
|
|
self.assertEqual(list(states.spam), list(b.spam))
|
2020-07-10 16:06:52 -05:00
|
|
|
|
2023-01-22 13:01:08 -05:00
|
|
|
@utilities.unittest.skipIf(_h5py is None, "h5py is not installed")
|
2020-09-05 13:46:31 -05:00
|
|
|
def test_write_hdf_multidim_column(self):
|
2021-06-01 18:32:05 -05:00
|
|
|
outfile = self.test_work_path / "solutionarray.h5"
|
2021-06-01 22:09:40 -05:00
|
|
|
# In Python >= 3.8, this can be replaced by the missing_ok argument
|
2021-06-01 18:32:05 -05:00
|
|
|
if outfile.is_file():
|
|
|
|
|
outfile.unlink()
|
2020-09-05 13:46:31 -05:00
|
|
|
|
|
|
|
|
states = ct.SolutionArray(self.gas, 3, extra={'spam': [[1, 2], [3, 4], [5, 6]]})
|
2020-07-10 16:06:52 -05:00
|
|
|
states.write_hdf(outfile, mode='w')
|
|
|
|
|
|
|
|
|
|
b = ct.SolutionArray(self.gas, extra={'spam'})
|
|
|
|
|
b.read_hdf(outfile)
|
2020-09-05 13:46:31 -05:00
|
|
|
self.assertArrayNear(states.spam, b.spam)
|
2020-07-10 16:06:52 -05:00
|
|
|
|
|
|
|
|
|
2019-11-01 23:49:15 -05:00
|
|
|
class TestRestoreIdealGas(utilities.CanteraTest):
|
|
|
|
|
""" Test restoring of the IdealGas class """
|
|
|
|
|
@classmethod
|
|
|
|
|
def setUpClass(cls):
|
|
|
|
|
utilities.CanteraTest.setUpClass()
|
2021-04-26 20:21:59 -04:00
|
|
|
cls.gas = ct.Solution('h2o2.yaml', transport_model=None)
|
2019-11-01 23:49:15 -05:00
|
|
|
|
|
|
|
|
def test_restore_gas(self):
|
|
|
|
|
|
|
|
|
|
def check(a, b, atol=None):
|
|
|
|
|
if atol is None:
|
|
|
|
|
self.assertArrayNear(a.T, b.T)
|
|
|
|
|
self.assertArrayNear(a.P, b.P)
|
|
|
|
|
self.assertArrayNear(a.X, b.X)
|
|
|
|
|
else:
|
|
|
|
|
self.assertArrayNear(a.T, b.T, atol=atol)
|
|
|
|
|
self.assertArrayNear(a.P, b.P, atol=atol)
|
|
|
|
|
self.assertArrayNear(a.X, b.X, atol=atol)
|
|
|
|
|
|
|
|
|
|
# test ThermoPhase
|
|
|
|
|
a = ct.SolutionArray(self.gas)
|
|
|
|
|
for i in range(10):
|
|
|
|
|
T = 300 + 1800*np.random.random()
|
|
|
|
|
P = ct.one_atm*(1 + 10*np.random.random())
|
|
|
|
|
X = np.random.random(self.gas.n_species)
|
|
|
|
|
X[-1] = 0.
|
|
|
|
|
X /= X.sum()
|
|
|
|
|
a.append(T=T, P=P, X=X)
|
|
|
|
|
|
2020-04-07 21:33:04 -05:00
|
|
|
data = a.collect_data()
|
2019-11-01 23:49:15 -05:00
|
|
|
|
|
|
|
|
# basic restore
|
|
|
|
|
b = ct.SolutionArray(self.gas)
|
2021-05-28 14:27:14 -05:00
|
|
|
b.restore_data(data, normalize=True)
|
2019-11-01 23:49:15 -05:00
|
|
|
check(a, b)
|
|
|
|
|
|
|
|
|
|
# skip concentrations
|
|
|
|
|
b = ct.SolutionArray(self.gas)
|
2020-06-01 09:33:24 -05:00
|
|
|
b.restore_data({'T': data['T'], 'density': data['density']})
|
2020-08-06 21:28:13 -05:00
|
|
|
self.assertArrayNear(a.T, b.T)
|
|
|
|
|
self.assertArrayNear(a.density, b.density)
|
2019-11-01 23:49:15 -05:00
|
|
|
self.assertFalse(np.allclose(a.X, b.X))
|
|
|
|
|
|
|
|
|
|
# wrong data shape
|
|
|
|
|
b = ct.SolutionArray(self.gas)
|
2020-04-08 01:12:21 -05:00
|
|
|
with self.assertRaises(ValueError):
|
2020-04-07 21:33:04 -05:00
|
|
|
b.restore_data(OrderedDict([(k, v[np.newaxis, :])
|
|
|
|
|
for k, v in data.items()]))
|
2019-11-01 23:49:15 -05:00
|
|
|
|
|
|
|
|
# inconsistent shape of receiving SolutionArray
|
|
|
|
|
b = ct.SolutionArray(self.gas, 9)
|
|
|
|
|
with self.assertRaises(ValueError):
|
2020-04-07 21:33:04 -05:00
|
|
|
b.restore_data(data)
|
2019-11-01 23:49:15 -05:00
|
|
|
|
|
|
|
|
# incomplete state
|
|
|
|
|
b = ct.SolutionArray(self.gas)
|
|
|
|
|
with self.assertRaises(ValueError):
|
2020-04-07 21:33:04 -05:00
|
|
|
b.restore_data(OrderedDict([tup for i, tup in enumerate(data.items())
|
|
|
|
|
if i]))
|
2019-11-01 23:49:15 -05:00
|
|
|
|
|
|
|
|
# add extra column
|
2020-04-07 21:33:04 -05:00
|
|
|
t = np.arange(10, dtype=float)
|
2019-11-01 23:49:15 -05:00
|
|
|
|
|
|
|
|
# auto-detection of extra
|
|
|
|
|
b = ct.SolutionArray(self.gas)
|
2020-04-07 21:33:04 -05:00
|
|
|
data_mod = OrderedDict(data)
|
|
|
|
|
data_mod['time'] = t
|
|
|
|
|
b.restore_data(data_mod)
|
2019-11-01 23:49:15 -05:00
|
|
|
check(a, b)
|
|
|
|
|
|
|
|
|
|
# explicit extra
|
|
|
|
|
b = ct.SolutionArray(self.gas, extra=('time',))
|
2020-04-07 21:33:04 -05:00
|
|
|
b.restore_data(data_mod)
|
2019-11-01 23:49:15 -05:00
|
|
|
check(a, b)
|
2020-04-07 21:33:04 -05:00
|
|
|
self.assertArrayNear(b.time, t)
|
2019-11-01 23:49:15 -05:00
|
|
|
|
|
|
|
|
# wrong extra
|
|
|
|
|
b = ct.SolutionArray(self.gas, extra=('xyz',))
|
|
|
|
|
with self.assertRaises(KeyError):
|
2020-04-07 21:33:04 -05:00
|
|
|
b.restore_data(data_mod)
|
2019-11-01 23:49:15 -05:00
|
|
|
|
|
|
|
|
# missing extra
|
|
|
|
|
b = ct.SolutionArray(self.gas, extra=('time'))
|
|
|
|
|
with self.assertRaises(KeyError):
|
2020-04-07 21:33:04 -05:00
|
|
|
b.restore_data(data)
|
2019-11-01 23:49:15 -05:00
|
|
|
|
|
|
|
|
# inconsistent species
|
2020-04-08 01:12:21 -05:00
|
|
|
data_mod = a.collect_data(tabular=True)
|
2020-04-07 21:33:04 -05:00
|
|
|
val = data_mod.pop('Y_AR')
|
|
|
|
|
data_mod['Y_invalid'] = val
|
2019-11-01 23:49:15 -05:00
|
|
|
b = ct.SolutionArray(self.gas)
|
|
|
|
|
with self.assertRaises(ValueError):
|
2020-04-07 21:33:04 -05:00
|
|
|
b.restore_data(data_mod)
|
2019-11-01 23:49:15 -05:00
|
|
|
|
|
|
|
|
# incomplete species info (using threshold)
|
2020-04-07 21:33:04 -05:00
|
|
|
data = a.collect_data(threshold=1e-6)
|
2019-11-01 23:49:15 -05:00
|
|
|
|
|
|
|
|
# basic restore
|
|
|
|
|
b = ct.SolutionArray(self.gas)
|
2020-04-07 21:33:04 -05:00
|
|
|
b.restore_data(data)
|
2019-11-01 23:49:15 -05:00
|
|
|
check(a, b, atol=1e-6)
|
|
|
|
|
|
|
|
|
|
# skip calculated properties
|
|
|
|
|
cols = ('T', 'P', 'X', 'gibbs_mass', 'forward_rates_of_progress')
|
2020-04-07 21:33:04 -05:00
|
|
|
data = a.collect_data(cols=cols, threshold=1e-6)
|
2019-11-01 23:49:15 -05:00
|
|
|
|
|
|
|
|
b = ct.SolutionArray(self.gas)
|
2020-04-07 21:33:04 -05:00
|
|
|
b.restore_data(data)
|
2019-11-01 23:49:15 -05:00
|
|
|
check(a, b)
|
2020-08-06 21:04:53 -05:00
|
|
|
self.assertEqual(len(b._extra), 0)
|
2019-11-01 23:49:15 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestRestorePureFluid(utilities.CanteraTest):
|
|
|
|
|
""" Test restoring of the PureFluid class """
|
|
|
|
|
@classmethod
|
|
|
|
|
def setUpClass(cls):
|
|
|
|
|
utilities.CanteraTest.setUpClass()
|
|
|
|
|
cls.water = ct.Water()
|
|
|
|
|
|
|
|
|
|
def test_restore_water(self):
|
|
|
|
|
|
|
|
|
|
def check(a, b):
|
|
|
|
|
self.assertArrayNear(a.T, b.T)
|
|
|
|
|
self.assertArrayNear(a.P, b.P)
|
2019-10-03 09:40:00 -05:00
|
|
|
self.assertArrayNear(a.Q, b.Q)
|
2019-11-01 23:49:15 -05:00
|
|
|
|
2019-12-06 19:02:34 -06:00
|
|
|
self.assertTrue(self.water.has_phase_transition)
|
2020-04-06 08:44:51 -05:00
|
|
|
|
2019-11-01 23:49:15 -05:00
|
|
|
# benchmark
|
|
|
|
|
a = ct.SolutionArray(self.water, 10)
|
2019-10-03 09:40:00 -05:00
|
|
|
a.TQ = 373.15, np.linspace(0., 1., 10)
|
2019-11-01 23:49:15 -05:00
|
|
|
|
|
|
|
|
# complete data
|
2019-10-03 09:40:00 -05:00
|
|
|
cols = ('T', 'P', 'Q')
|
2020-04-07 21:33:04 -05:00
|
|
|
data = a.collect_data(cols=cols)
|
2019-11-01 23:49:15 -05:00
|
|
|
b = ct.SolutionArray(self.water)
|
2020-04-07 21:33:04 -05:00
|
|
|
b.restore_data(data)
|
2019-11-01 23:49:15 -05:00
|
|
|
check(a, b)
|
|
|
|
|
|
|
|
|
|
# partial data
|
2019-10-03 09:40:00 -05:00
|
|
|
cols = ('T', 'Q')
|
2020-04-07 21:33:04 -05:00
|
|
|
data = a.collect_data(cols=cols)
|
2019-11-01 23:49:15 -05:00
|
|
|
b = ct.SolutionArray(self.water)
|
2020-04-07 21:33:04 -05:00
|
|
|
b.restore_data(data)
|
2019-11-01 23:49:15 -05:00
|
|
|
check(a, b)
|
|
|
|
|
|
|
|
|
|
# default columns
|
2020-04-07 21:33:04 -05:00
|
|
|
data = a.collect_data()
|
|
|
|
|
self.assertEqual(list(data.keys()), ['T', 'density'])
|
2019-11-01 23:49:15 -05:00
|
|
|
b = ct.SolutionArray(self.water)
|
2020-04-07 21:33:04 -05:00
|
|
|
b.restore_data(data)
|
2019-11-01 23:49:15 -05:00
|
|
|
check(a, b)
|
|
|
|
|
|
|
|
|
|
# default state plus Y
|
2019-11-13 12:13:11 -06:00
|
|
|
cols = ('T', 'D', 'Y')
|
2020-04-07 21:33:04 -05:00
|
|
|
data = a.collect_data(cols=cols)
|
2019-11-01 23:49:15 -05:00
|
|
|
b = ct.SolutionArray(self.water)
|
2020-04-07 21:33:04 -05:00
|
|
|
b.restore_data(data)
|
2019-11-01 23:49:15 -05:00
|
|
|
check(a, b)
|
2020-01-21 17:51:54 -05:00
|
|
|
|
2023-01-22 13:01:08 -05:00
|
|
|
@utilities.unittest.skipIf(_h5py is None, "h5py is not installed")
|
2021-05-28 14:27:14 -05:00
|
|
|
def test_import_no_norm_water(self):
|
2021-06-15 10:44:04 -05:00
|
|
|
outfile = self.test_work_path / "solutionarray.h5"
|
|
|
|
|
# In Python >= 3.8, this can be replaced by the missing_ok argument
|
|
|
|
|
if outfile.is_file():
|
|
|
|
|
outfile.unlink()
|
2021-05-28 14:27:14 -05:00
|
|
|
|
|
|
|
|
w = ct.Water()
|
|
|
|
|
w.TQ = 300, 0.5
|
|
|
|
|
states = ct.SolutionArray(w, 5)
|
|
|
|
|
states.write_hdf(outfile)
|
|
|
|
|
|
|
|
|
|
w_new = ct.Water()
|
|
|
|
|
c = ct.SolutionArray(w_new)
|
2021-06-15 10:44:04 -05:00
|
|
|
c.read_hdf(outfile, normalize=False)
|
2021-05-28 14:27:14 -05:00
|
|
|
self.assertArrayNear(states.T, c.T)
|
|
|
|
|
self.assertArrayNear(states.P, c.P)
|
|
|
|
|
self.assertArrayNear(states.Q, c.Q)
|
|
|
|
|
|
|
|
|
|
def test_append_no_norm_water(self):
|
|
|
|
|
w = ct.Water()
|
|
|
|
|
states = ct.SolutionArray(w)
|
|
|
|
|
w.TQ = 300, 0.5
|
|
|
|
|
states.append(w.state)
|
|
|
|
|
self.assertEqual(states[0].T, w.T)
|
|
|
|
|
self.assertEqual(states[0].P, w.P)
|
|
|
|
|
self.assertEqual(states[0].Q, w.Q)
|
|
|
|
|
|
2020-01-21 17:51:54 -05:00
|
|
|
|
|
|
|
|
class TestSolutionSerialization(utilities.CanteraTest):
|
|
|
|
|
def test_input_data_simple(self):
|
|
|
|
|
gas = ct.Solution('h2o2.yaml')
|
|
|
|
|
data = gas.input_data
|
|
|
|
|
self.assertEqual(data['name'], 'ohmech')
|
|
|
|
|
self.assertEqual(data['thermo'], 'ideal-gas')
|
|
|
|
|
self.assertEqual(data['kinetics'], 'gas')
|
|
|
|
|
self.assertEqual(data['transport'], 'mixture-averaged')
|
|
|
|
|
|
2021-05-08 22:18:28 -04:00
|
|
|
def test_input_data_user_modifications(self):
|
|
|
|
|
gas = ct.Solution("h2o2.yaml")
|
|
|
|
|
data1 = gas.input_data
|
|
|
|
|
gas.update_user_data({"foo": True}) # should get overwritten
|
|
|
|
|
extra = {"foo": [1.2, 3.4], "bar": [[1, 2], [3, 4]]}
|
|
|
|
|
gas.update_user_data(extra)
|
|
|
|
|
data2 = gas.input_data
|
|
|
|
|
self.assertEqual(extra["foo"], data2["foo"])
|
|
|
|
|
self.assertEqual(extra["bar"], data2["bar"])
|
|
|
|
|
gas.clear_user_data()
|
|
|
|
|
data3 = gas.input_data
|
|
|
|
|
self.assertEqual(data1, data3)
|
|
|
|
|
|
2020-01-21 17:51:54 -05:00
|
|
|
def test_input_data_state(self):
|
2021-04-26 20:21:59 -04:00
|
|
|
gas = ct.Solution('h2o2.yaml', transport_model=None)
|
2020-01-21 17:51:54 -05:00
|
|
|
data = gas.input_data
|
|
|
|
|
self.assertEqual(gas.T, data['state']['T'])
|
|
|
|
|
self.assertEqual(gas.density, data['state']['density'])
|
|
|
|
|
|
|
|
|
|
gas.TP = 500, 3.14e5
|
|
|
|
|
data = gas.input_data
|
|
|
|
|
self.assertEqual(gas.T, data['state']['T'])
|
|
|
|
|
self.assertEqual(gas.density, data['state']['density'])
|
|
|
|
|
|
|
|
|
|
def test_input_data_custom(self):
|
|
|
|
|
gas = ct.Solution('ideal-gas.yaml')
|
|
|
|
|
data = gas.input_data
|
|
|
|
|
self.assertEqual(data['custom-field']['first'], True)
|
|
|
|
|
self.assertEqual(data['custom-field']['last'], [100, 200, 300])
|
|
|
|
|
|
2021-04-11 11:17:11 -04:00
|
|
|
if sys.version_info >= (3,7):
|
|
|
|
|
# Check that items are ordered as expected
|
|
|
|
|
self.assertEqual(
|
|
|
|
|
list(data),
|
2021-10-26 11:18:50 -05:00
|
|
|
["name", "thermo", "elements", "species", "state",
|
|
|
|
|
"custom-field", "literal-string"]
|
2021-04-11 11:17:11 -04:00
|
|
|
)
|
2021-10-26 11:18:50 -05:00
|
|
|
self.assertEqual(list(data["custom-field"]), ["first", "second", "last"])
|
|
|
|
|
self.assertEqual(data["literal-string"], "spam\nand\neggs\n")
|
2021-04-11 11:17:11 -04:00
|
|
|
|
2020-01-21 17:51:54 -05:00
|
|
|
def test_input_data_debye_huckel(self):
|
|
|
|
|
soln = ct.Solution('thermo-models.yaml', 'debye-huckel-B-dot-ak')
|
|
|
|
|
data = soln.input_data
|
|
|
|
|
self.assertEqual(data['thermo'], 'Debye-Huckel')
|
|
|
|
|
act_data = data['activity-data']
|
|
|
|
|
self.assertEqual(act_data['model'], 'B-dot-with-variable-a')
|
|
|
|
|
self.assertEqual(act_data['default-ionic-radius'], 4e-10)
|
|
|
|
|
self.assertNotIn('kinetics', data)
|
|
|
|
|
self.assertNotIn('transport', data)
|
2020-01-21 18:34:18 -05:00
|
|
|
|
2021-03-10 17:19:51 -05:00
|
|
|
def test_yaml_simple(self):
|
|
|
|
|
gas = ct.Solution('h2o2.yaml')
|
2021-04-12 18:57:42 -04:00
|
|
|
gas.TPX = 500, ct.one_atm, 'H2: 1.0, O2: 1.0'
|
|
|
|
|
gas.equilibrate('HP')
|
|
|
|
|
gas.TP = 1500, ct.one_atm
|
2022-03-29 14:30:54 -04:00
|
|
|
gas.write_yaml(self.test_work_path / "h2o2-generated.yaml")
|
|
|
|
|
generated = utilities.load_yaml(self.test_work_path / "h2o2-generated.yaml")
|
2021-03-10 17:19:51 -05:00
|
|
|
for key in ('generator', 'date', 'phases', 'species', 'reactions'):
|
|
|
|
|
self.assertIn(key, generated)
|
|
|
|
|
self.assertEqual(generated['phases'][0]['transport'], 'mixture-averaged')
|
|
|
|
|
for i, species in enumerate(generated['species']):
|
|
|
|
|
self.assertEqual(species['composition'], gas.species(i).composition)
|
2022-03-05 10:28:42 -06:00
|
|
|
for blessed, generated in zip(gas.reactions(), generated["reactions"]):
|
|
|
|
|
assert blessed.equation == generated["equation"]
|
2021-03-10 17:19:51 -05:00
|
|
|
|
2022-03-29 14:30:54 -04:00
|
|
|
gas2 = ct.Solution(self.test_work_path / "h2o2-generated.yaml")
|
2021-04-12 18:57:42 -04:00
|
|
|
self.assertArrayNear(gas.concentrations, gas2.concentrations)
|
|
|
|
|
self.assertArrayNear(gas.partial_molar_enthalpies,
|
|
|
|
|
gas2.partial_molar_enthalpies)
|
|
|
|
|
self.assertArrayNear(gas.forward_rate_constants,
|
|
|
|
|
gas2.forward_rate_constants)
|
|
|
|
|
self.assertArrayNear(gas.mix_diff_coeffs, gas2.mix_diff_coeffs)
|
|
|
|
|
|
2021-07-26 15:10:03 -05:00
|
|
|
def test_yaml_outunits1(self):
|
2021-03-10 17:19:51 -05:00
|
|
|
gas = ct.Solution('h2o2.yaml')
|
2021-04-12 18:57:42 -04:00
|
|
|
gas.TPX = 500, ct.one_atm, 'H2: 1.0, O2: 1.0'
|
|
|
|
|
gas.equilibrate('HP')
|
|
|
|
|
gas.TP = 1500, ct.one_atm
|
2021-03-10 17:19:51 -05:00
|
|
|
units = {'length': 'cm', 'quantity': 'mol', 'energy': 'cal'}
|
2022-03-29 14:30:54 -04:00
|
|
|
gas.write_yaml(self.test_work_path / "h2o2-generated.yaml", units=units)
|
|
|
|
|
generated = utilities.load_yaml(self.test_work_path / "h2o2-generated.yaml")
|
2021-06-01 18:32:05 -05:00
|
|
|
original = utilities.load_yaml(self.cantera_data_path / "h2o2.yaml")
|
2021-03-10 17:19:51 -05:00
|
|
|
self.assertEqual(generated['units'], units)
|
|
|
|
|
|
|
|
|
|
for r1, r2 in zip(original['reactions'], generated['reactions']):
|
|
|
|
|
if 'rate-constant' in r1:
|
|
|
|
|
self.assertNear(r1['rate-constant']['A'], r2['rate-constant']['A'])
|
|
|
|
|
self.assertNear(r1['rate-constant']['Ea'], r2['rate-constant']['Ea'])
|
|
|
|
|
|
2022-03-29 14:30:54 -04:00
|
|
|
gas2 = ct.Solution(self.test_work_path / "h2o2-generated.yaml")
|
2021-04-12 18:57:42 -04:00
|
|
|
self.assertArrayNear(gas.concentrations, gas2.concentrations)
|
|
|
|
|
self.assertArrayNear(gas.partial_molar_enthalpies,
|
|
|
|
|
gas2.partial_molar_enthalpies)
|
|
|
|
|
self.assertArrayNear(gas.forward_rate_constants,
|
|
|
|
|
gas2.forward_rate_constants)
|
|
|
|
|
self.assertArrayNear(gas.mix_diff_coeffs, gas2.mix_diff_coeffs)
|
|
|
|
|
|
2021-07-26 15:10:03 -05:00
|
|
|
def test_yaml_outunits2(self):
|
|
|
|
|
gas = ct.Solution('h2o2.yaml')
|
|
|
|
|
gas.TPX = 500, ct.one_atm, 'H2: 1.0, O2: 1.0'
|
|
|
|
|
gas.equilibrate('HP')
|
|
|
|
|
gas.TP = 1500, ct.one_atm
|
|
|
|
|
units = {'length': 'cm', 'quantity': 'mol', 'energy': 'cal'}
|
|
|
|
|
system = ct.UnitSystem(units)
|
2022-03-29 14:30:54 -04:00
|
|
|
gas.write_yaml(self.test_work_path / "h2o2-generated.yaml", units=system)
|
|
|
|
|
generated = utilities.load_yaml(self.test_work_path / "h2o2-generated.yaml")
|
2021-07-26 15:10:03 -05:00
|
|
|
original = utilities.load_yaml(self.cantera_data_path / "h2o2.yaml")
|
|
|
|
|
|
|
|
|
|
for r1, r2 in zip(original['reactions'], generated['reactions']):
|
|
|
|
|
if 'rate-constant' in r1:
|
|
|
|
|
self.assertNear(r1['rate-constant']['A'], r2['rate-constant']['A'])
|
|
|
|
|
self.assertNear(r1['rate-constant']['Ea'], r2['rate-constant']['Ea'])
|
|
|
|
|
|
2022-03-29 14:30:54 -04:00
|
|
|
gas2 = ct.Solution(self.test_work_path / "h2o2-generated.yaml")
|
2021-07-26 15:10:03 -05:00
|
|
|
self.assertArrayNear(gas.concentrations, gas2.concentrations)
|
|
|
|
|
self.assertArrayNear(gas.partial_molar_enthalpies,
|
|
|
|
|
gas2.partial_molar_enthalpies)
|
|
|
|
|
self.assertArrayNear(gas.forward_rate_constants,
|
|
|
|
|
gas2.forward_rate_constants)
|
|
|
|
|
self.assertArrayNear(gas.mix_diff_coeffs, gas2.mix_diff_coeffs)
|
|
|
|
|
|
2022-01-13 09:24:58 -05:00
|
|
|
def check_ptcombust(self, gas, surf):
|
2022-03-29 14:30:54 -04:00
|
|
|
generated = utilities.load_yaml(self.test_work_path / "ptcombust-generated.yaml")
|
2022-01-13 09:24:58 -05:00
|
|
|
for key in ("phases", "species", "gas-reactions", "Pt_surf-reactions"):
|
2021-03-10 17:19:51 -05:00
|
|
|
self.assertIn(key, generated)
|
2022-01-13 09:24:58 -05:00
|
|
|
self.assertEqual(len(generated["gas-reactions"]), gas.n_reactions)
|
|
|
|
|
self.assertEqual(len(generated["Pt_surf-reactions"]), surf.n_reactions)
|
|
|
|
|
self.assertEqual(len(generated["species"]), surf.n_total_species)
|
2021-03-10 17:19:51 -05:00
|
|
|
|
2022-03-29 14:30:54 -04:00
|
|
|
surf2 = ct.Solution(self.test_work_path / "ptcombust-generated.yaml", "Pt_surf")
|
2021-04-12 18:57:42 -04:00
|
|
|
self.assertArrayNear(surf.concentrations, surf2.concentrations)
|
|
|
|
|
self.assertArrayNear(surf.partial_molar_enthalpies,
|
|
|
|
|
surf2.partial_molar_enthalpies)
|
|
|
|
|
self.assertArrayNear(surf.forward_rate_constants,
|
|
|
|
|
surf2.forward_rate_constants)
|
2020-01-21 18:34:18 -05:00
|
|
|
|
2022-01-13 09:24:58 -05:00
|
|
|
def test_yaml_surface_explicit(self):
|
|
|
|
|
gas = ct.Solution("ptcombust.yaml", "gas")
|
|
|
|
|
surf = ct.Interface("ptcombust.yaml", "Pt_surf", [gas])
|
|
|
|
|
gas.TPY = 900, ct.one_atm, np.ones(gas.n_species)
|
|
|
|
|
surf.coverages = np.ones(surf.n_species)
|
2022-03-29 14:30:54 -04:00
|
|
|
surf.write_yaml(self.test_work_path / "ptcombust-generated.yaml")
|
2022-01-13 09:24:58 -05:00
|
|
|
self.check_ptcombust(gas, surf)
|
|
|
|
|
|
|
|
|
|
def test_yaml_surface_adjacent(self):
|
|
|
|
|
surf = ct.Interface("ptcombust.yaml", "Pt_surf")
|
|
|
|
|
gas = surf.adjacent["gas"]
|
|
|
|
|
gas.TPY = 900, ct.one_atm, np.ones(gas.n_species)
|
|
|
|
|
surf.coverages = np.ones(surf.n_species)
|
2022-03-29 14:30:54 -04:00
|
|
|
surf.write_yaml(self.test_work_path / "ptcombust-generated.yaml")
|
2022-01-13 09:24:58 -05:00
|
|
|
self.check_ptcombust(gas, surf)
|
|
|
|
|
|
2021-04-13 18:47:29 -04:00
|
|
|
def test_yaml_eos(self):
|
|
|
|
|
ice = ct.Solution('water.yaml', 'ice')
|
|
|
|
|
ice.TP = 270, 2 * ct.one_atm
|
2022-03-29 14:30:54 -04:00
|
|
|
ice.write_yaml(self.test_work_path / "ice-generated.yaml", units={'length': 'mm', 'mass': 'g'})
|
2021-04-13 18:47:29 -04:00
|
|
|
|
2022-03-29 14:30:54 -04:00
|
|
|
ice2 = ct.Solution(self.test_work_path / "ice-generated.yaml")
|
2021-04-13 18:47:29 -04:00
|
|
|
self.assertNear(ice.density, ice2.density)
|
|
|
|
|
self.assertNear(ice.entropy_mole, ice2.entropy_mole)
|
|
|
|
|
|
|
|
|
|
def test_yaml_inconsistent_species(self):
|
2021-04-26 20:21:59 -04:00
|
|
|
gas = ct.Solution('h2o2.yaml', transport_model=None)
|
|
|
|
|
gas2 = ct.Solution('h2o2.yaml', transport_model=None)
|
2021-04-13 18:47:29 -04:00
|
|
|
gas2.name = 'modified'
|
|
|
|
|
# modify the NASA coefficients for one species
|
|
|
|
|
h2 = gas2.species('H2')
|
|
|
|
|
nasa_coeffs = h2.thermo.coeffs
|
|
|
|
|
nasa_coeffs[1] += 0.1
|
|
|
|
|
nasa_coeffs[8] += 0.1
|
|
|
|
|
h2.thermo = ct.NasaPoly2(h2.thermo.min_temp, h2.thermo.max_temp,
|
|
|
|
|
h2.thermo.reference_pressure, nasa_coeffs)
|
|
|
|
|
gas2.modify_species(gas2.species_index('H2'), h2)
|
|
|
|
|
with self.assertRaisesRegex(ct.CanteraError, "different definitions"):
|
2022-03-29 14:30:54 -04:00
|
|
|
gas.write_yaml(self.test_work_path / "h2o2-error.yaml", phases=gas2)
|
2021-04-13 18:47:29 -04:00
|
|
|
|
2021-05-08 22:18:28 -04:00
|
|
|
def test_yaml_user_data(self):
|
|
|
|
|
gas = ct.Solution("h2o2.yaml")
|
|
|
|
|
extra = {"spam": {"A": 1, "B": 2}, "eggs": [1, 2.3, 4.5]}
|
|
|
|
|
gas.update_user_data(extra)
|
|
|
|
|
S = gas.species(2)
|
|
|
|
|
S.update_user_data({"foo": "bar"})
|
|
|
|
|
S.transport.update_user_data({"baz": 1234.5})
|
|
|
|
|
S.thermo.update_user_data({"something": (False, True)})
|
|
|
|
|
gas.reaction(5).update_user_data({"baked-beans": True})
|
|
|
|
|
|
2022-03-29 14:30:54 -04:00
|
|
|
gas.write_yaml(self.test_work_path / "h2o2-generated-user-data.yaml")
|
|
|
|
|
gas2 = ct.Solution(self.test_work_path / "h2o2-generated-user-data.yaml")
|
2021-05-08 22:18:28 -04:00
|
|
|
data2 = gas2.species(2).input_data
|
|
|
|
|
|
|
|
|
|
self.assertEqual(gas2.input_data["spam"], extra["spam"])
|
|
|
|
|
self.assertEqual(gas2.input_data["eggs"], extra["eggs"])
|
|
|
|
|
self.assertEqual(data2["foo"], "bar")
|
|
|
|
|
self.assertEqual(data2["transport"]["baz"], 1234.5)
|
|
|
|
|
self.assertEqual(data2["thermo"]["something"], [False, True])
|
|
|
|
|
self.assertTrue(gas2.reaction(5).input_data["baked-beans"])
|
|
|
|
|
|
2021-04-13 18:47:29 -04:00
|
|
|
|
2020-01-21 18:34:18 -05:00
|
|
|
class TestSpeciesSerialization(utilities.CanteraTest):
|
|
|
|
|
def test_species_simple(self):
|
2021-04-26 20:21:59 -04:00
|
|
|
gas = ct.Solution('h2o2.yaml', transport_model=None)
|
2020-01-21 18:34:18 -05:00
|
|
|
data = gas.species('H2O').input_data
|
|
|
|
|
self.assertEqual(data['name'], 'H2O')
|
|
|
|
|
self.assertEqual(data['composition'], {'H': 2, 'O': 1})
|
|
|
|
|
|
|
|
|
|
def test_species_thermo(self):
|
2021-04-26 20:21:59 -04:00
|
|
|
gas = ct.Solution('h2o2.yaml', transport_model=None)
|
2020-01-21 18:34:18 -05:00
|
|
|
data = gas.species('H2O').input_data['thermo']
|
|
|
|
|
self.assertEqual(data['model'], 'NASA7')
|
|
|
|
|
self.assertEqual(data['temperature-ranges'], [200, 1000, 3500])
|
|
|
|
|
self.assertEqual(data['note'], 'L8/89')
|
|
|
|
|
|
|
|
|
|
def test_species_transport(self):
|
|
|
|
|
gas = ct.Solution('h2o2.yaml')
|
|
|
|
|
data = gas.species('H2O').input_data['transport']
|
|
|
|
|
self.assertEqual(data['model'], 'gas')
|
|
|
|
|
self.assertEqual(data['geometry'], 'nonlinear')
|
|
|
|
|
self.assertNear(data['dipole'], 1.844)
|
2022-01-13 09:24:58 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestInterfaceAdjacent(utilities.CanteraTest):
|
|
|
|
|
def test_surface(self):
|
|
|
|
|
surf = ct.Interface("ptcombust.yaml", "Pt_surf")
|
|
|
|
|
self.assertEqual(list(surf.adjacent), ["gas"])
|
|
|
|
|
self.assertEqual(surf.phase_index(surf), 0)
|
|
|
|
|
self.assertEqual(surf.phase_index("gas"), 1)
|
|
|
|
|
self.assertEqual(surf.phase_index(surf.adjacent["gas"]), 1)
|
|
|
|
|
|
|
|
|
|
def test_named_adjacent(self):
|
|
|
|
|
# override the adjacent-phases to change the order
|
|
|
|
|
surf = ct.Interface("surface-phases.yaml", "anode-surface",
|
|
|
|
|
adjacent=["electrolyte", "graphite"])
|
|
|
|
|
self.assertEqual(list(surf.adjacent), ["electrolyte", "graphite"])
|
|
|
|
|
|
|
|
|
|
def test_edge(self):
|
|
|
|
|
tpb = ct.Interface("sofc.yaml", "tpb")
|
|
|
|
|
self.assertEqual(set(tpb.adjacent), {"metal_surface", "oxide_surface", "metal"})
|
|
|
|
|
self.assertIsInstance(tpb.adjacent["metal_surface"], ct.Interface)
|
|
|
|
|
self.assertNotIsInstance(tpb.adjacent["metal"], ct.Interface)
|
|
|
|
|
gas1 = tpb.adjacent["metal_surface"].adjacent["gas"]
|
|
|
|
|
gas2 = tpb.adjacent["oxide_surface"].adjacent["gas"]
|
|
|
|
|
gas1.X = [0.1, 0.4, 0.3, 0.2]
|
|
|
|
|
self.assertArrayNear(gas1.X, gas2.X)
|
|
|
|
|
|
|
|
|
|
def test_invalid(self):
|
|
|
|
|
with self.assertRaisesRegex(ct.CanteraError, "does not contain"):
|
|
|
|
|
surf = ct.Interface("ptcombust.yaml", "Pt_surf", ["foo"])
|
|
|
|
|
|
|
|
|
|
with self.assertRaises(TypeError):
|
|
|
|
|
surf = ct.Interface("ptcombust.yaml", "Pt_surf", [2])
|
|
|
|
|
|
|
|
|
|
def test_remote_file(self):
|
|
|
|
|
yaml = """
|
|
|
|
|
phases:
|
|
|
|
|
- name: Pt_surf
|
|
|
|
|
thermo: ideal-surface
|
|
|
|
|
adjacent-phases: [{ptcombust.yaml/phases: [gas]}]
|
|
|
|
|
species: [{ptcombust.yaml/species: all}]
|
|
|
|
|
kinetics: surface
|
|
|
|
|
reactions: [{ptcombust.yaml/reactions: all}]
|
|
|
|
|
site-density: 2.7063e-09
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
surf = ct.Interface(yaml=yaml)
|
|
|
|
|
self.assertEqual(surf.adjacent["gas"].n_species, 32)
|
|
|
|
|
self.assertEqual(surf.n_reactions, 24)
|