Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create SolutionBase object in C++ #696

Merged
merged 17 commits into from
Oct 23, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions include/cantera/base/Solution.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,6 @@ class Solution : public std::enable_shared_from_this<Solution>
shared_ptr<ThermoPhase> m_thermo; //! ThermoPhase manager
shared_ptr<Kinetics> m_kinetics; //! Kinetics manager
shared_ptr<Transport> m_transport; //! Transport manager
ischoegl marked this conversation as resolved.
Show resolved Hide resolved

std::string m_name; //! name of Solution object
};

}
Expand Down
2 changes: 0 additions & 2 deletions interfaces/cython/cantera/_cantera.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,6 @@ cdef extern from "cantera/thermo/ThermoPhase.h" namespace "Cantera":
# miscellaneous
string type()
string report(cbool, double) except +translate_exception
string id()
void setID(string)
double minTemp() except +translate_exception
double maxTemp() except +translate_exception
double refPressure() except +translate_exception
Expand Down
36 changes: 14 additions & 22 deletions interfaces/cython/cantera/base.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,18 @@

from collections import defaultdict as _defaultdict

_phase_counts = _defaultdict(int)

cdef class _SolutionBase:
def __cinit__(self, infile='', phase_id='', adjacent=(), origin=None,
def __cinit__(self, infile='', name='', adjacent=(), origin=None,
source=None, yaml=None, thermo=None, species=(),
kinetics=None, reactions=(), **kwargs):

if 'phaseid' in kwargs:
if phase_id is not '':
if name is not '':
raise AttributeError('duplicate specification of phase name')

warnings.warn("Keyword 'phase_id' replaces 'phaseid'",
warnings.warn("Keyword 'name' replaces 'phaseid'",
FutureWarning)
phase_id = kwargs['phaseid']
name = kwargs['phaseid']

if 'phases' in kwargs:
if len(adjacent)>0:
Expand Down Expand Up @@ -54,9 +52,9 @@ cdef class _SolutionBase:

# Parse inputs
if infile.endswith('.yml') or infile.endswith('.yaml') or yaml:
self._init_yaml(infile, phase_id, adjacent, yaml)
self._init_yaml(infile, name, adjacent, yaml)
elif infile or source:
self._init_cti_xml(infile, phase_id, adjacent, source)
self._init_cti_xml(infile, name, adjacent, source)
elif thermo and species:
self._init_parts(thermo, species, kinetics, adjacent, reactions)
else:
Expand All @@ -72,23 +70,17 @@ cdef class _SolutionBase:
if isinstance(self, Transport):
assert self.transport is not NULL

phase_name = pystr(self.thermo.id())
phase_name = pystr(self.base.name())
name = kwargs.get('name')
if name is not None:
self.name = name
elif phase_name in _phase_counts:
_phase_counts[phase_name] += 1
n = _phase_counts[phase_name]
self.name = '{0}_{1}'.format(phase_name, n)
else:
_phase_counts[phase_name] = 0
self.name = phase_name

property name:
"""
The name assigned to this SolutionBase object. The default value
is based on the phase identifier in the CTI/XML/YAML input file;
a numbered suffix is added if needed to create a unique name.
The name assigned to this object. The default value corresponds
to the CTI/XML/YAML input file phase entry.
"""
def __get__(self):
return pystr(self.base.name())
Expand All @@ -111,7 +103,7 @@ cdef class _SolutionBase:

return thermo, kinetics, transport

def _init_yaml(self, infile, phase_id, adjacent, source):
def _init_yaml(self, infile, name, adjacent, source):
"""
Instantiate a set of new Cantera C++ objects from a YAML
phase definition
Expand All @@ -123,7 +115,7 @@ cdef class _SolutionBase:
root = AnyMapFromYamlString(stringify(source))

phaseNode = root[stringify("phases")].getMapWhere(stringify("name"),
stringify(phase_id))
stringify(name))

# Thermo
if isinstance(self, ThermoPhase):
Expand All @@ -146,7 +138,7 @@ cdef class _SolutionBase:
else:
self.kinetics = NULL

def _init_cti_xml(self, infile, phase_id, adjacent, source):
def _init_cti_xml(self, infile, name, adjacent, source):
"""
Instantiate a set of new Cantera C++ objects from a CTI or XML
phase definition
Expand All @@ -158,8 +150,8 @@ cdef class _SolutionBase:

# Get XML data
cdef XML_Node* phaseNode
if phase_id:
phaseNode = rootNode.findID(stringify(phase_id))
if name:
phaseNode = rootNode.findID(stringify(name))
else:
phaseNode = rootNode.findByName(stringify('phase'))
if phaseNode is NULL:
Expand Down
10 changes: 5 additions & 5 deletions interfaces/cython/cantera/ck2yaml.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
[--thermo=<filename>]
[--transport=<filename>]
[--surface=<filename>]
[--phase-id=<phase-id>]
[--name=<name>]
[--output=<filename>]
[--permissive]
[-d | --debug]
Expand All @@ -32,7 +32,7 @@
'surface'.

The '--permissive' option allows certain recoverable parsing errors (e.g.
duplicate transport data) to be ignored. The '--phase-id=<phase-id>' option
duplicate transport data) to be ignored. The '--name=<name>' option
is used to override default phase names (i.e. 'gas').
"""

Expand Down Expand Up @@ -2025,7 +2025,7 @@ def convert_mech(input_file, thermo_file=None, transport_file=None, surface_file

def main(argv):

longOptions = ['input=', 'thermo=', 'transport=', 'surface=', 'phase-id=',
longOptions = ['input=', 'thermo=', 'transport=', 'surface=', 'name=',
'output=', 'permissive', 'help', 'debug', 'quiet',
'no-validate', 'id=']

Expand Down Expand Up @@ -2059,9 +2059,9 @@ def main(argv):
if '--id' in options:
phase_name = options.get('--id', 'gas')
logging.warning("\nFutureWarning: "
"option '--id=...' is superseded by '--phase-id=...'")
"option '--id=...' is superseded by '--name=...'")
else:
phase_name = options.get('--phase-id', 'gas')
phase_name = options.get('--name', 'gas')

if not input_file and not thermo_file:
print('At least one of the arguments "--input=..." or "--thermo=..."'
Expand Down
23 changes: 10 additions & 13 deletions interfaces/cython/cantera/composite.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,16 @@ class Solution(ThermoPhase, Kinetics, Transport):

If an input file defines multiple phases, the corresponding key in the
*phases* map (in YAML), *name* (in CTI), or *id* (in XML) can be used
to specify the desired phase via the ``phase_id`` keyword argument of
to specify the desired phase via the ``name`` keyword argument of
the constructor::

gas = ct.Solution('diamond.yaml', phase_id='gas')
diamond = ct.Solution('diamond.yaml', phase_id='diamond')
gas = ct.Solution('diamond.yaml', name='gas')
diamond = ct.Solution('diamond.yaml', name='diamond')

The name of the `Solution` object needs to be unique and defaults to the
*phase* specified in the input file. If another object using the same
constituting information already exists, the name is automatically appended
by a suffix. A custom name can be set via the ``name`` keyword argument of
the constructor, i.e.::
The name of the `Solution` object defaults to the *phase* specified in the
input file. Once instatiated, a custom name can assigned via::
ischoegl marked this conversation as resolved.
Show resolved Hide resolved

gas = ct.Solution('gri30.yaml', name='my_custom_name')
gas.name = 'my_custom_name'

`Solution` objects can also be constructed using `Species` and `Reaction`
objects which can themselves either be imported from input files or defined
Expand All @@ -48,7 +45,7 @@ class Solution(ThermoPhase, Kinetics, Transport):
spec = ct.Species.listFromFile('gri30.yaml')
rxns = ct.Reaction.listFromFile('gri30.yaml')
gas = ct.Solution(thermo='IdealGas', kinetics='GasKinetics',
species=spec, reactions=rxns)
species=spec, reactions=rxns, name='my_custom_name')

where the ``thermo`` and ``kinetics`` keyword arguments are strings
specifying the thermodynamic and kinetics model, respectively, and
Expand Down Expand Up @@ -93,9 +90,9 @@ class Interface(InterfacePhase, InterfaceKinetics):
in reactions need to be created and then passed in as a list in the
``adjacent`` argument to the constructor::

gas = ct.Solution('diamond.yaml', phase_id='gas')
diamond = ct.Solution('diamond.yaml', phase_id='diamond')
diamond_surf = ct.Interface('diamond.yaml', phase_id='diamond_100',
gas = ct.Solution('diamond.yaml', name='gas')
diamond = ct.Solution('diamond.yaml', name='diamond')
diamond_surf = ct.Interface('diamond.yaml', name='diamond_100',
adjacent=[gas, diamond])
"""
__slots__ = ('_phase_indices',)
Expand Down
4 changes: 2 additions & 2 deletions interfaces/cython/cantera/kinetics.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -371,8 +371,8 @@ cdef class InterfaceKinetics(Kinetics):
A kinetics manager for heterogeneous reaction mechanisms. The
reactions are assumed to occur at an interface between bulk phases.
"""
def __init__(self, infile='', phase_id='', adjacent=(), *args, **kwargs):
super().__init__(infile, phase_id, adjacent, *args, **kwargs)
def __init__(self, infile='', name='', adjacent=(), *args, **kwargs):
super().__init__(infile, name, adjacent, *args, **kwargs)
if pystr(self.kinetics.kineticsType()) not in ("Surf", "Edge"):
raise TypeError("Underlying Kinetics class is not of the correct type.")

Expand Down
32 changes: 16 additions & 16 deletions interfaces/cython/cantera/test/test_convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -660,7 +660,7 @@ def test_ptcombust(self):
Path(self.test_work_dir).joinpath('ptcombust.yaml'))
ctiGas, yamlGas = self.checkConversion('ptcombust')
ctiSurf, yamlSurf = self.checkConversion('ptcombust', ct.Interface,
phase_id='Pt_surf', ctiphases=[ctiGas], yamlphases=[yamlGas])
name='Pt_surf', ctiphases=[ctiGas], yamlphases=[yamlGas])

self.checkKinetics(ctiGas, yamlGas, [500, 1200], [1e4, 3e5])
self.checkThermo(ctiSurf, yamlSurf, [400, 800, 1600])
Expand All @@ -670,16 +670,16 @@ def test_sofc(self):
cti2yaml.convert(Path(self.cantera_data).joinpath('sofc.cti'),
Path(self.test_work_dir).joinpath('sofc.yaml'))
ctiGas, yamlGas = self.checkConversion('sofc')
ctiMetal, yamlMetal = self.checkConversion('sofc', phase_id='metal')
ctiOxide, yamlOxide = self.checkConversion('sofc', phase_id='oxide_bulk')
ctiMetal, yamlMetal = self.checkConversion('sofc', name='metal')
ctiOxide, yamlOxide = self.checkConversion('sofc', name='oxide_bulk')
ctiMSurf, yamlMSurf = self.checkConversion('sofc', ct.Interface,
phase_id='metal_surface', ctiphases=[ctiGas, ctiMetal],
name='metal_surface', ctiphases=[ctiGas, ctiMetal],
yamlphases=[yamlGas, yamlMetal])
ctiOSurf, yamlOSurf = self.checkConversion('sofc', ct.Interface,
phase_id='oxide_surface', ctiphases=[ctiGas, ctiOxide],
name='oxide_surface', ctiphases=[ctiGas, ctiOxide],
yamlphases=[yamlGas, yamlOxide])
cti_tpb, yaml_tpb = self.checkConversion('sofc', ct.Interface,
phase_id='tpb', ctiphases=[ctiMetal, ctiMSurf, ctiOSurf],
name='tpb', ctiphases=[ctiMetal, ctiMSurf, ctiOSurf],
yamlphases=[yamlMetal, yamlMSurf, yamlOSurf])

self.checkThermo(ctiMSurf, yamlMSurf, [900, 1000, 1100])
Expand All @@ -694,7 +694,7 @@ def test_liquidvapor(self):
Path(self.test_work_dir).joinpath('liquidvapor.yaml'))
for name in ['water', 'nitrogen', 'methane', 'hydrogen', 'oxygen',
'hfc134a', 'carbondioxide', 'heptane']:
ctiPhase, yamlPhase = self.checkConversion('liquidvapor', phase_id=name)
ctiPhase, yamlPhase = self.checkConversion('liquidvapor', name=name)
self.checkThermo(ctiPhase, yamlPhase,
[1.3 * ctiPhase.min_temp, 0.7 * ctiPhase.max_temp])

Expand All @@ -717,10 +717,10 @@ def test_Redlich_Kwong_ndodecane(self):
def test_diamond(self):
cti2yaml.convert(Path(self.cantera_data).joinpath('diamond.cti'),
Path(self.test_work_dir).joinpath('diamond.yaml'))
ctiGas, yamlGas = self.checkConversion('diamond', phase_id='gas')
ctiSolid, yamlSolid = self.checkConversion('diamond', phase_id='diamond')
ctiGas, yamlGas = self.checkConversion('diamond', name='gas')
ctiSolid, yamlSolid = self.checkConversion('diamond', name='diamond')
ctiSurf, yamlSurf = self.checkConversion('diamond',
ct.Interface, phase_id='diamond_100', ctiphases=[ctiGas, ctiSolid],
ct.Interface, name='diamond_100', ctiphases=[ctiGas, ctiSolid],
yamlphases=[yamlGas, yamlSolid])
self.checkThermo(ctiSolid, yamlSolid, [300, 500])
self.checkThermo(ctiSurf, yamlSurf, [330, 490])
Expand All @@ -730,16 +730,16 @@ def test_lithium_ion_battery(self):
cti2yaml.convert(Path(self.cantera_data).joinpath('lithium_ion_battery.cti'),
Path(self.test_work_dir).joinpath('lithium_ion_battery.yaml'))
name = 'lithium_ion_battery'
ctiAnode, yamlAnode = self.checkConversion(name, phase_id='anode')
ctiCathode, yamlCathode = self.checkConversion(name, phase_id='cathode')
ctiMetal, yamlMetal = self.checkConversion(name, phase_id='electron')
ctiElyt, yamlElyt = self.checkConversion(name, phase_id='electrolyte')
ctiAnode, yamlAnode = self.checkConversion(name, name='anode')
ctiCathode, yamlCathode = self.checkConversion(name, name='cathode')
ctiMetal, yamlMetal = self.checkConversion(name, name='electron')
ctiElyt, yamlElyt = self.checkConversion(name, name='electrolyte')
ctiAnodeInt, yamlAnodeInt = self.checkConversion(name,
phase_id='edge_anode_electrolyte',
name='edge_anode_electrolyte',
ctiphases=[ctiAnode, ctiMetal, ctiElyt],
yamlphases=[yamlAnode, yamlMetal, yamlElyt])
ctiCathodeInt, yamlCathodeInt = self.checkConversion(name,
phase_id='edge_cathode_electrolyte',
name='edge_cathode_electrolyte',
ctiphases=[ctiCathode, ctiMetal, ctiElyt],
yamlphases=[yamlCathode, yamlMetal, yamlElyt])

Expand Down
4 changes: 2 additions & 2 deletions interfaces/cython/cantera/test/test_reactor.py
Original file line number Diff line number Diff line change
Expand Up @@ -929,9 +929,9 @@ def create_reactors(self, add_Q=False, add_mdot=False, add_surf=False):
self.gas.TPX = 900, 25*ct.one_atm, 'CO:0.5, H2O:0.2'

self.gas1 = ct.Solution('gri30.xml')
self.gas1.phase_id = 'gas'
self.gas1.name = 'gas'
self.gas2 = ct.Solution('gri30.xml')
self.gas2.phase_id = 'gas'
self.gas2.name = 'gas'
resGas = ct.Solution('gri30.xml')
solid = ct.Solution('diamond.xml', 'diamond')

Expand Down
8 changes: 4 additions & 4 deletions interfaces/cython/cantera/test/test_thermo.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ def test_name(self):
self.assertIn('something', self.phase.report())

def test_phase(self):
self.assertEqual(self.phase.phase_id, 'ohmech')
self.assertEqual(self.phase.name, 'ohmech')
warnings.simplefilter("always")

with warnings.catch_warnings(record=True) as w:
Expand All @@ -334,7 +334,7 @@ def test_phase(self):

with warnings.catch_warnings(record=True) as w:
self.phase.ID = 'something'
self.assertEqual(self.phase.phase_id, 'something')
self.assertEqual(self.phase.name, 'something')
self.assertEqual(len(w), 1)
self.assertTrue(issubclass(w[-1].category, DeprecationWarning))
self.assertIn("To be removed after Cantera 2.5. ",
Expand All @@ -344,7 +344,7 @@ def test_phase(self):
gas = ct.Solution('h2o2.cti', phaseid='ohmech')
self.assertEqual(len(w), 1)
self.assertTrue(issubclass(w[-1].category, FutureWarning))
self.assertIn("Keyword 'phase_id' replaces 'phaseid'",
self.assertIn("Keyword 'name' replaces 'phaseid'",
str(w[-1].message))

def test_badLength(self):
Expand Down Expand Up @@ -880,7 +880,7 @@ class ImportTest(utilities.CanteraTest):
Test the various ways of creating a Solution object
"""
def check(self, gas, phase, T, P, nSpec, nElem):
self.assertEqual(gas.phase_id, phase)
self.assertEqual(gas.name, phase)
self.assertNear(gas.T, T)
self.assertNear(gas.P, P)
self.assertEqual(gas.n_species, nSpec)
Expand Down
24 changes: 6 additions & 18 deletions interfaces/cython/cantera/thermo.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -289,36 +289,24 @@ cdef class ThermoPhase(_SolutionBase):
def __call__(self, *args, **kwargs):
print(self.report(*args, **kwargs))

property phase_id:
"""
The identifier of the object. The default value corresponds to the
CTI/XML/YAML input file phase entry, and should remain unchanged.
"""
def __get__(self):
return pystr(self.thermo.id())
def __set__(self, phase_id):
# may consider removing/deprecating, but resetting of the phase name
# is required to associate surface kinetics (with phase name being 'gas')
self.thermo.setID(stringify(phase_id))

property ID:
"""
The identifier of the object. The default value corresponds to the
CTI/XML/YAML input file phase entry, and should remain unchanged.
CTI/XML/YAML input file phase entry.

.. deprecated:: 2.5

To be deprecated with version 2.5, and removed thereafter.
Renamed to `phase_ID`.
Usage merged with `name`.
"""
def __get__(self):
warnings.warn("To be removed after Cantera 2.5. "
"Use 'phase' attribute instead", DeprecationWarning)
return pystr(self.thermo.id())
"Use 'name' attribute instead", DeprecationWarning)
return pystr(self.base.name())
def __set__(self, id_):
warnings.warn("To be removed after Cantera 2.5. "
"Use 'phase' attribute instead", DeprecationWarning)
self.thermo.setID(stringify(id_))
"Use 'name' attribute instead", DeprecationWarning)
self.base.setName(stringify(id_))

property basis:
"""
Expand Down
Loading