Skip to content

Commit

Permalink
[Input/Python] Enable generation of YAML files from Python Solutions
Browse files Browse the repository at this point in the history
  • Loading branch information
speth committed Mar 10, 2021
1 parent f79a9f9 commit 86435ab
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 29 deletions.
15 changes: 11 additions & 4 deletions include/cantera/base/YamlWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,12 @@ class YamlWriter
void addPhase(shared_ptr<ThermoPhase> thermo, shared_ptr<Kinetics> kin={},
shared_ptr<Transport> tran={});

//! Return a YAML string that contains the definitions for the added phases,
//! species, and reactions
std::string toYamlString() const;

//! Write the definitions for the added phases, species and reactions to
//! the specified file.
void toYamlFile(const std::string& filename) const;

//! For output floating point values, set the maximum number of digits to
Expand All @@ -47,10 +52,12 @@ class YamlWriter
m_skip_user_defined = skip;
}

//! Set the units to be used in the output file
void setUnits(const UnitSystem& units) {
m_output_units = units;
}
//! Set the units to be used in the output file. Dimensions not specified
//! will use Cantera's defaults.
//! @param units A map where keys are dimensions (mass, length, time,
//! quantity, pressure, energy, activation-energy) and the values are
//! corresponding units supported by the UnitSystem class.
void setUnits(const std::map<std::string, std::string>& units={});

protected:
std::vector<shared_ptr<Solution>> m_phases;
Expand Down
13 changes: 13 additions & 0 deletions interfaces/cython/cantera/_cantera.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,15 @@ cdef extern from "cantera/transport/TransportData.h" namespace "Cantera":
double dispersion_coefficient
double quadrupole_polarizability

cdef extern from "cantera/base/YamlWriter.h" namespace "Cantera":
cdef cppclass CxxYamlWriter "Cantera::YamlWriter":
CxxYamlWriter()
void addPhase(shared_ptr[CxxSolution]) except +translate_exception
string toYamlString() except +translate_exception
void toYamlFile(string&) except +translate_exception
void setPrecision(int)
void skipUserDefined(cbool)
void setUnits(stdmap[string, string]&) except +translate_exception

cdef extern from "cantera/equil/MultiPhase.h" namespace "Cantera":
cdef cppclass CxxMultiPhase "Cantera::MultiPhase":
Expand Down Expand Up @@ -1056,6 +1065,10 @@ cdef class Transport(_SolutionBase):
cdef class DustyGasTransport(Transport):
pass

cdef class YamlWriter:
cdef shared_ptr[CxxYamlWriter] _writer
cdef CxxYamlWriter* writer

cdef class Mixture:
cdef CxxMultiPhase* mix
cdef list _phases
Expand Down
1 change: 1 addition & 0 deletions interfaces/cython/cantera/_cantera.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ include "reaction.pyx"
include "kinetics.pyx"
include "transport.pyx"

include "yamlwriter.pyx"
include "mixture.pyx"
include "reactor.pyx"
include "onedim.pyx"
Expand Down
34 changes: 34 additions & 0 deletions interfaces/cython/cantera/base.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,40 @@ cdef class _SolutionBase:
self.transport.getParameters(params)
return mergeAnyMap(params, self.thermo.input())

def write_yaml(self, filename, phases=None, units=None, precision=None,
skip_user_defined=None):
"""
Write the definition for this phase, any additional phases specified,
and their species and reactions to the specified file.
:param filename:
The name of the output file
:param phases:
Additional ThermoPhase / Solution objects to be included in the
output file
:param units:
A dictionary of the units to be used for each dimension. See
`YamlWriter.output_units`.
:param precision:
For output floating point values, the maximum number of digits to
the right of the decimal point. The default is 15 digits.
:param skip_user_defined:
If `True`, user-defined fields which are not used by Cantera will
be stripped from the output.
"""
Y = YamlWriter()
Y.add_solution(self)
if phases is not None:
for phase in phases:
Y.add_solution(phase)
if units is not None:
Y.output_units = units
if precision is not None:
Y.precision = precision
if skip_user_defined is not None:
Y.skip_user_defined = skip_user_defined
Y.to_file(filename)

def __getitem__(self, selection):
copy = self.__class__(origin=self)
if isinstance(selection, slice):
Expand Down
18 changes: 17 additions & 1 deletion interfaces/cython/cantera/kinetics.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ cdef class InterfaceKinetics(Kinetics):

def _phase_slice(self, phase):
p = self.phase_index(phase)
k1 = self.kinetics_species_index(0, p)
k1 = self.kinetics_speci(0, p)

if p == self.n_phases - 1:
k2 = self.n_total_species
Expand Down Expand Up @@ -460,3 +460,19 @@ cdef class InterfaceKinetics(Kinetics):
species in all phases.
"""
return self.net_production_rates[self._phase_slice(phase)]

def write_yaml(self, filename, phases=None, units=None, precision=None,
skip_user_defined=None):
"""
See `_SolutionBase.write_yaml`.
"""
if phases is not None:
phases = list(phases)
else:
phases = []

for phase in self._phase_indices:
if isinstance(phase, _SolutionBase) and phase is not self:
phases.append(phase)

super().write_yaml(filename, phases, units, precision, skip_user_defined)
41 changes: 41 additions & 0 deletions interfaces/cython/cantera/test/test_composite.py
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,47 @@ def test_input_data_debye_huckel(self):
self.assertNotIn('kinetics', data)
self.assertNotIn('transport', data)

def test_yaml_simple(self):
gas = ct.Solution('h2o2.yaml')
gas.write_yaml('h2o2-generated.yaml')
with open('h2o2-generated.yaml', 'r') as infile:
generated = yaml.safe_load(infile)
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)
for i, reaction in enumerate(generated['reactions']):
self.assertEqual(reaction['equation'], gas.reaction_equation(i))

def test_yaml_outunits(self):
gas = ct.Solution('h2o2.yaml')
units = {'length': 'cm', 'quantity': 'mol', 'energy': 'cal'}
gas.write_yaml('h2o2-generated.yaml', units=units)
with open('h2o2-generated.yaml') as infile:
generated = yaml.safe_load(infile)
with open(pjoin(self.cantera_data, "h2o2.yaml")) as infile:
original = yaml.safe_load(infile)
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'])

def test_yaml_surface(self):
gas = ct.Solution('ptcombust.yaml', 'gas')
surf = ct.Interface('ptcombust.yaml', 'Pt_surf', [gas])
surf.write_yaml('ptcombust-generated.yaml')

with open('ptcombust-generated.yaml') as infile:
generated = yaml.safe_load(infile)
for key in ('phases', 'species', 'gas-reactions', 'Pt_surf-reactions'):
self.assertIn(key, generated)
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)


class TestSpeciesSerialization(utilities.CanteraTest):
def test_species_simple(self):
Expand Down
6 changes: 6 additions & 0 deletions src/base/YamlWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,4 +208,10 @@ void YamlWriter::toYamlFile(const std::string& filename) const
out << toYamlString();
}

void YamlWriter::setUnits(const std::map<std::string, std::string>& units)
{
m_output_units = UnitSystem();
m_output_units.setDefaults(units);
}

}
41 changes: 17 additions & 24 deletions test/general/test_serialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,14 +114,11 @@ TEST(YamlWriter, reaction_units_from_Yaml)
YamlWriter writer;
writer.addPhase(original);
writer.setPrecision(14);
UnitSystem outUnits;
std::map<std::string, std::string> defaults = {
writer.setUnits({
{"activation-energy", "K"},
{"quantity", "mol"},
{"length", "cm"}
};
outUnits.setDefaults(defaults);
writer.setUnits(outUnits);
});
writer.toYamlFile("generated-h2o2-outunits.yaml");
auto duplicate = newSolution("generated-h2o2-outunits.yaml");

Expand All @@ -143,19 +140,16 @@ TEST(YamlWriter, reaction_units_from_Xml)
YamlWriter writer;
writer.addPhase(original);
writer.setPrecision(14);
UnitSystem outUnits;
std::map<std::string, std::string> defaults = {
writer.setUnits({
{"activation-energy", "K"},
{"quantity", "mol"},
{"length", "cm"}
};
outUnits.setDefaults(defaults);
writer.setUnits(outUnits);
});
// Should fail because pre-exponential factors from XML can't be converted
EXPECT_THROW(writer.toYamlFile("generated-h2o2-fail.yaml"), CanteraError);

// Outputting with the default MKS+kmol system still works
writer.setUnits(UnitSystem());
writer.setUnits();
writer.toYamlFile("generated-h2o2-from-xml.yaml");
auto duplicate = newSolution("generated-h2o2-from-xml.yaml");

Expand All @@ -177,15 +171,12 @@ TEST(YamlWriter, chebyshev_units_from_Yaml)
YamlWriter writer;
writer.addPhase(original);
writer.setPrecision(14);
UnitSystem outUnits;
std::map<std::string, std::string> defaults = {
writer.setUnits({
{"activation-energy", "K"},
{"quantity", "mol"},
{"length", "cm"},
{"pressure", "atm"}
};
outUnits.setDefaults(defaults);
writer.setUnits(outUnits);
});
writer.toYamlFile("generated-pdep-test.yaml");
auto duplicate = newSolution("generated-pdep-test.yaml");

Expand Down Expand Up @@ -246,9 +237,11 @@ TEST(YamlWriter, Interface)
YamlWriter writer;
writer.addPhase(gas1);
writer.addPhase(surf1, kin1);
UnitSystem U{"mm", "molec"};
U.setDefaultActivationEnergy("K");
writer.setUnits(U);
writer.setUnits({
{"length", "mm"},
{"quantity", "molec"},
{"activation-energy", "K"}
});
writer.toYamlFile("generated-ptcombust.yaml");

shared_ptr<ThermoPhase> gas2(newPhase("generated-ptcombust.yaml", "gas"));
Expand Down Expand Up @@ -301,11 +294,11 @@ TEST(YamlWriter, sofc)
writer.addPhase(metal1);
writer.addPhase(gas1);
writer.addPhase(ox_bulk1);

UnitSystem U;
U.setDefaults({"cm", "atm"});
U.setDefaultActivationEnergy("eV");
writer.setUnits(U);
writer.setUnits({
{"length", "cm"},
{"pressure", "atm"},
{"activation-energy", "eV"}
});
writer.toYamlFile("generated-sofc.yaml");

shared_ptr<ThermoPhase> gas2(newPhase("generated-sofc.yaml", "gas"));
Expand Down

0 comments on commit 86435ab

Please sign in to comment.