Skip to content

Commit

Permalink
[One_dim] add normalize to flamebase to solutionArray importer
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidAkinpelu committed Jun 16, 2021
1 parent e34d374 commit 4224ccb
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 87 deletions.
93 changes: 35 additions & 58 deletions interfaces/cython/cantera/composite.py
Original file line number Diff line number Diff line change
Expand Up @@ -650,10 +650,11 @@ def append(self, state=None, normalize=None, **kwargs):
mystates.append(T=300, P=101325, X={'O2':1.0, 'N2':3.76})
By default, setters with `moleFractions` are normalized to sum to 1.0 in all
cases. while element appended using ``state`` or the setter contains
`massFractions` specified as an array, are non-normalized. If this is
not desired, the ``normalize`` argument can be set to ``True``.
By default, the mass or mole fractions will be non-normalized
if appending a `state` or numpy array specifying `X` or `Y`.
When appending a state, the mass or mole fractions are always
non-normalized. For states setters, if the non-normalized
form is desired, the ``normalize`` argument can be set to ``True``.
For example::
mystates.append(T=300, P=101325, Y=gas.Y - 0.1, normalize=True)
Expand Down Expand Up @@ -681,48 +682,30 @@ def append(self, state=None, normalize=None, **kwargs):
extra_temp[name] = kwargs.pop(name)

if state is not None:
mode = "".join(self._phase._native_state)
if normalize:
if len(mode) == 3:
state_data = (state[0], state[1], state[2:])
elif len(mode) == 2:
state_data = (state[0], state[1:])
else:
state_data = state
setattr(self._phase, mode, state_data)
else:
self._phase.state = state
self._phase.state = state

# Non normalize form must be set using the mass fractions as the
# mole fractions are not in the native_state setter.
elif len(kwargs) == 1:
attr, value = kwargs.popitem()
if frozenset(attr) not in self._phase._full_states:
raise KeyError(
"'{}' does not specify a full thermodynamic state".format(attr)
)
if attr[-1] == "Y":
if normalize:
if attr[-1] == "X":
if not normalize and isinstance(value[-1], np.ndarray):
self._phase.set_unnormalized_mole_fractions(value[-1])
attr = attr[:-1]
value = value[:-1]
setattr(self._phase, attr, value)
elif normalize is None:
if isinstance(value[-1], (list, tuple, np.ndarray)):
self._phase.set_unnormalized_mass_fractions(value[-1])
attr = attr[:-1]
value = value[:-1]
setattr(self._phase, attr, value)
else:
setattr(self._phase, attr, list(kwargs.values()))
else:
if isinstance(value[-1], (list, tuple, np.ndarray)):
self._phase.set_unnormalized_mass_fractions(value[-1])
attr = attr[:-1]
value = value[:-1]
setattr(self._phase, attr, value)
else:
raise ValueError(
"Mass fraction must be specified as an array if "
"`normalize` is True"
)
setattr(self._phase, attr, value)
elif attr[-1] == "Y":
if not normalize and isinstance(value[-1], np.ndarray):
self._phase.set_unnormalized_mass_fractions(value[-1])
attr = attr[:-1]
value = value[:-1]
setattr(self._phase, attr, value)
else:
setattr(self._phase, attr, value)
else:
setattr(self._phase, attr, value)

Expand All @@ -734,26 +717,20 @@ def append(self, state=None, normalize=None, **kwargs):
"{} is not a valid combination of properties for setting "
"the thermodynamic state".format(tuple(kwargs))
) from None
if attr[-1] == "Y":
if normalize:
if attr[-1] == "X":
if not normalize and isinstance(kwargs["X"], np.ndarray):
self._phase.set_unnormalized_mole_fractions(kwargs.pop("X"))
attr = attr[:-1]
setattr(self._phase, attr, list(kwargs.values()))
elif normalize is None:
if isinstance(kwargs["Y"], (list, tuple, np.ndarray)):
self._phase.set_unnormalized_mass_fractions(kwargs.pop("Y"))
attr = attr[:-1]
setattr(self._phase, attr, list(kwargs.values()))
else:
setattr(self._phase, attr, list(kwargs.values()))
else:
if isinstance(kwargs["Y"], (list, tuple, np.ndarray)):
self._phase.set_unnormalized_mass_fractions(kwargs.pop("Y"))
attr = attr[:-1]
setattr(self._phase, attr, list(kwargs.values()))
else:
raise ValueError(
"Mass fraction must be specified as an array if "
"`normalize` is True"
)
setattr(self._phase, attr, list(kwargs.values()))
elif attr[-1] == "Y":
if not normalize and isinstance(kwargs["Y"], np.ndarray):
self._phase.set_unnormalized_mass_fractions(kwargs.pop("Y"))
attr = attr[:-1]
setattr(self._phase, attr, list(kwargs.values()))
else:
setattr(self._phase, attr, list(kwargs.values()))
else:
setattr(self._phase, attr, list(kwargs.values()))

Expand Down Expand Up @@ -832,9 +809,9 @@ def restore_data(self, data, normalize=False):
does not contain those entries, an error is raised.
If the data contains mass or mole fractions they will be set
without normalizing their sum to 1.0 by default. If this is not desired,
the ``normalize`` argument can be set to ``True`` to force the data
to sum to 1.0.
without normalizing their sum to 1.0 by default. If this is
not desired, the ``normalize`` argument can be set to ``True``
to force the data to sum to 1.0.
"""

# check arguments
Expand Down
51 changes: 41 additions & 10 deletions interfaces/cython/cantera/onedim.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ def set_gas_state(self, point):
self.gas.set_unnormalized_mass_fractions(Y)
self.gas.TP = self.value(self.flame, 'T', point), self.P

def write_csv(self, filename, species='X', quiet=True):
def write_csv(self, filename, species='X', quiet=True, normalize=False):
"""
Write the velocity, temperature, density, and species profiles
to a CSV file.
Expand All @@ -387,20 +387,28 @@ def write_csv(self, filename, species='X', quiet=True):
:param species:
Attribute to use obtaining species profiles, e.g. ``X`` for
mole fractions or ``Y`` for mass fractions.
:param normalize:
Boolean flag to indicate whether the mole/mass fractions should
be normalized (default is ``False``)
"""

# save data
cols = ('extra', 'T', 'D', species)
self.to_solution_array().write_csv(filename, cols=cols)
self.to_solution_array(normalize=normalize).write_csv(filename, cols=cols)

if not quiet:
print("Solution saved to '{0}'.".format(filename))

def to_solution_array(self, domain=None):
def to_solution_array(self, domain=None, normalize=False):
"""
Return the solution vector as a `SolutionArray` object.
Derived classes define default values for *other*.
If the data contains mass or mole fractions they will be set
without normalizing their sum to 1.0 by default. If this is
not desired, the ``normalize`` argument can be set to
``True`` to force the data to sum to 1.0.
"""
if domain is None:
domain = self.flame
Expand All @@ -413,7 +421,21 @@ def to_solution_array(self, domain=None):
if n_points:
arr = SolutionArray(self.phase(domain), n_points,
extra=other_cols, meta=meta)
arr.TPY = states
if normalize:
arr.TPY = states
else:
if len(states) == 3:
if n_points==1:
arr._phase.set_unnormalized_mass_fractions(states[2])
arr._phase.TP = states[0], states[1]
arr._states[0] = arr._phase.state
else:
for i in range(n_points):
arr._phase.set_unnormalized_mass_fractions(states[2][i])
arr._phase.TP = states[0][i], states[1]
arr._states[i] = arr._phase.state
else:
arr.TPY = states
return arr
else:
return SolutionArray(self.phase(domain), meta=meta)
Expand All @@ -436,20 +458,23 @@ def from_solution_array(self, arr, domain=None):
meta = arr.meta
super().restore_data(domain, states, other_cols, meta)

def to_pandas(self, species='X'):
def to_pandas(self, species='X', normalize=False):
"""
Return the solution vector as a `pandas.DataFrame`.
:param species:
Attribute to use obtaining species profiles, e.g. ``X`` for
mole fractions or ``Y`` for mass fractions.
:param normalize:
Boolean flag to indicate whether the mole/mass fractions should
be normalized (default is ``False``)
This method uses `to_solution_array` and requires a working pandas
installation. Use pip or conda to install `pandas` to enable this
method.
"""
cols = ('extra', 'T', 'D', species)
return self.to_solution_array().to_pandas(cols=cols)
return self.to_solution_array(normalize=normalize).to_pandas(cols=cols)

def from_pandas(self, df, restore_boundaries=True, settings=None):
"""
Expand All @@ -476,7 +501,7 @@ def from_pandas(self, df, restore_boundaries=True, settings=None):

def write_hdf(self, filename, *args, group=None, species='X', mode='a',
description=None, compression=None, compression_opts=None,
quiet=True, **kwargs):
quiet=True, normalize=False, **kwargs):
"""
Write the solution vector to a HDF container file.
Expand Down Expand Up @@ -537,6 +562,9 @@ def write_hdf(self, filename, *args, group=None, species='X', mode='a',
corresponds to the compression level {None, 0-9}.
:param quiet:
Suppress message confirming successful file output.
:param normalize:
Boolean flag to indicate whether the mole/mass fractions should
be normalized (default is ``False``)
Additional arguments (i.e. *args* and *kwargs*) are passed on to
`SolutionArray.collect_data`. The method exports data using
Expand All @@ -551,7 +579,7 @@ def write_hdf(self, filename, *args, group=None, species='X', mode='a',
if description is not None:
meta['description'] = description
for i in range(3):
arr = self.to_solution_array(domain=self.domains[i])
arr = self.to_solution_array(domain=self.domains[i], normalize=normalize)
group = arr.write_hdf(filename, *args, group=group, cols=cols,
subgroup=self.domains[i].name,
attrs=meta, mode=mode, append=(i > 0),
Expand All @@ -565,7 +593,7 @@ def write_hdf(self, filename, *args, group=None, species='X', mode='a',
msg = "Solution saved to '{0}' as group '{1}'."
print(msg.format(filename, group))

def read_hdf(self, filename, group=None, restore_boundaries=True):
def read_hdf(self, filename, group=None, restore_boundaries=True, normalize=True):
"""
Restore the solution vector from a HDF container file.
Expand All @@ -576,6 +604,9 @@ def read_hdf(self, filename, group=None, restore_boundaries=True):
:param restore_boundaries:
Boolean flag to indicate whether boundaries should be restored
(default is ``True``)
:param normalize:
Boolean flag to indicate whether the mole/mass fractions should
be normalized (default is ``True``)
The method imports data using `SolutionArray.read_hdf` via
`from_solution_array` and requires a working installation of h5py
Expand All @@ -589,7 +620,7 @@ def read_hdf(self, filename, group=None, restore_boundaries=True):
for d in domains:
arr = SolutionArray(self.phase(d), extra=self.other_components(d))
meta = arr.read_hdf(filename, group=group,
subgroup=self.domains[d].name)
subgroup=self.domains[d].name, normalize=normalize)
self.from_solution_array(arr, domain=d)

self.settings = meta
Expand Down
22 changes: 6 additions & 16 deletions interfaces/cython/cantera/test/test_composite.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,35 +135,25 @@ def test_collect_data(self):
self.assertIn('X', collected)
self.assertEqual(collected['X'].shape, (0, self.gas.n_species))

def test_append_no_norm_data(self):
def test_append_state(self):
gas = ct.Solution("h2o2.yaml")
gas.TP = 300, ct.one_atm
gas.set_unnormalized_mass_fractions(np.full(gas.n_species, 0.3))
gas.TPX = 300, ct.one_atm, 'H2:0.5, O2:0.4'
states = ct.SolutionArray(gas)
states.append(T=gas.T, P=gas.P, Y=gas.Y, normalize=False)
states.append(gas.state)
self.assertEqual(states[0].T, gas.T)
self.assertEqual(states[0].P, gas.P)
self.assertArrayNear(states[0].Y, gas.Y)
self.assertArrayNear(states[0].X, gas.X)

def test_append_no_norm_state(self):
def test_append_no_norm_data(self):
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)
states.append(gas.state, normalize=False)
states.append(T=gas.T, P=gas.P, Y=gas.Y, normalize=False)
self.assertEqual(states[0].T, gas.T)
self.assertEqual(states[0].P, gas.P)
self.assertArrayNear(states[0].Y, gas.Y)

def test_append_norm_state(self):
gas = ct.Solution("h2o2.yaml")
gas.TPX = 300, ct.one_atm, 'H2:0.5, O2:0.4'
states = ct.SolutionArray(gas)
states.append(gas.state)
self.assertEqual(states[0].T, gas.T)
self.assertEqual(states[0].P, gas.P)
self.assertArrayNear(states[0].X, gas.X)

def test_import_no_norm_data(self):
outfile = pjoin(self.test_work_dir, "solutionarray.h5")
if os.path.exists(outfile):
Expand Down
6 changes: 3 additions & 3 deletions interfaces/cython/cantera/test/test_onedim.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ def test_collect_restore(self):
def test_solution_array_output(self):
self.run_mix(phi=1.0, T=300, width=2.0, p=1.0, refine=False)

flow = self.sim.to_solution_array()
flow = self.sim.to_solution_array(normalize=True)
self.assertArrayNear(self.sim.grid, flow.grid)
self.assertArrayNear(self.sim.T, flow.T)
for k in flow._extra.keys():
Expand All @@ -291,7 +291,7 @@ def test_solution_array_output(self):

def test_restart(self):
self.run_mix(phi=1.0, T=300, width=2.0, p=1.0, refine=False)
arr = self.sim.to_solution_array()
arr = self.sim.to_solution_array(normalize=False)

reactants = {'H2': 0.9, 'O2': 0.5, 'AR': 2}
self.create_sim(1.1 * ct.one_atm, 500, reactants, 2.0)
Expand Down Expand Up @@ -657,7 +657,7 @@ def test_write_hdf(self):
self.sim.write_hdf(filename, description=desc)

f = ct.FreeFlame(self.gas)
meta = f.read_hdf(filename)
meta = f.read_hdf(filename, normalize=False)
self.assertArrayNear(f.grid, self.sim.grid)
self.assertArrayNear(f.T, self.sim.T)
self.assertEqual(meta['description'], desc)
Expand Down

0 comments on commit 4224ccb

Please sign in to comment.