From 02175a3b17bc92b155976215c5132ac45e77abdd Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Tue, 8 Oct 2019 15:00:07 -0500 Subject: [PATCH] [Thermo] address discussion and review comments * rename C++ object to 'Solution' (from 'SolutionBase') * remove 'phaseID' from 'Solution' ('id' remains assigned to 'Phase') * remove 'type' from C++ object (no polymorphism anticipated) * assign 'name' to 'Solution' (link back from 'Phase' until deprecated) * clarify 'phase' as 'phase_id' in Python interface * address various feedback in review comments --- include/cantera/base/Solution.h | 72 ++++++++++++ include/cantera/base/SolutionBase.h | 104 ------------------ include/cantera/kinetics/Kinetics.h | 10 +- include/cantera/thermo/Phase.h | 10 +- include/cantera/transport/TransportBase.h | 10 +- interfaces/cython/cantera/_cantera.pxd | 18 ++- interfaces/cython/cantera/base.pyx | 98 +++++------------ interfaces/cython/cantera/ck2yaml.py | 10 +- interfaces/cython/cantera/composite.py | 26 ++--- interfaces/cython/cantera/kinetics.pyx | 11 +- .../cython/cantera/test/test_convert.py | 32 +++--- .../cython/cantera/test/test_reactor.py | 4 +- interfaces/cython/cantera/test/test_thermo.py | 50 ++++----- interfaces/cython/cantera/thermo.pyx | 33 +++++- src/base/Solution.cpp | 46 ++++++++ src/base/SolutionBase.cpp | 99 ----------------- src/thermo/Phase.cpp | 17 ++- 17 files changed, 272 insertions(+), 378 deletions(-) create mode 100644 include/cantera/base/Solution.h delete mode 100644 include/cantera/base/SolutionBase.h create mode 100644 src/base/Solution.cpp delete mode 100644 src/base/SolutionBase.cpp diff --git a/include/cantera/base/Solution.h b/include/cantera/base/Solution.h new file mode 100644 index 0000000000..37141cc4dc --- /dev/null +++ b/include/cantera/base/Solution.h @@ -0,0 +1,72 @@ +//! @file Solution.h + +// This file is part of Cantera. See License.txt in the top-level directory or +// at https://cantera.org/license.txt for license and copyright information. + +#ifndef CT_SOLUTION_H +#define CT_SOLUTION_H + +#include "cantera/base/ctexceptions.h" + +namespace Cantera +{ + +class ThermoPhase; +class Kinetics; +class Transport; + +//! A container class holding managers for all pieces defining a phase +class Solution : public std::enable_shared_from_this +{ +private: + Solution(); + +public: + ~Solution() {} + Solution(const Solution&) = delete; + Solution& operator=(const Solution&) = delete; + + static shared_ptr create() { + return shared_ptr( new Solution ); + } + + //! Return the name of this Solution object + std::string name() const; + + //! Set the name of this Solution object + void setName(const std::string& name); + + //! Set the ThermoPhase object + void setThermoPhase(shared_ptr thermo); + + //! Set the Kinetics object + void setKinetics(shared_ptr kinetics); + + //! Set the Transport object + void setTransport(shared_ptr transport); + + //! Accessor for the ThermoPhase object + ThermoPhase& thermo() { + return *m_thermo; + } + + //! Accessor for the Kinetics object + Kinetics& kinetics() { + return *m_kinetics; + } + + //! Accessor for the Transport object + Transport& transport() { + return *m_transport; + } + +protected: + shared_ptr m_thermo; //! ThermoPhase manager + shared_ptr m_kinetics; //! Kinetics manager + shared_ptr m_transport; //! Transport manager + + std::string m_name; //! name of Solution object +}; + +} +#endif diff --git a/include/cantera/base/SolutionBase.h b/include/cantera/base/SolutionBase.h deleted file mode 100644 index cec66ffb13..0000000000 --- a/include/cantera/base/SolutionBase.h +++ /dev/null @@ -1,104 +0,0 @@ -//! @file SolutionBase.h - -// This file is part of Cantera. See License.txt in the top-level directory or -// at https://cantera.org/license.txt for license and copyright information. - -#ifndef CT_SOLUTIONBASE_H -#define CT_SOLUTIONBASE_H - -#include "cantera/base/ctexceptions.h" - -namespace Cantera -{ - -class ThermoPhase; -class Kinetics; -class Transport; - -//! A container class holding managers for all pieces defining a phase -class SolutionBase : public std::enable_shared_from_this -{ -public: - SolutionBase(); - SolutionBase(const std::string& infile, const std::string& phasename); - ~SolutionBase() {} - SolutionBase(const SolutionBase&) = delete; - SolutionBase& operator=(const SolutionBase&) = delete; - - static shared_ptr create() { - return shared_ptr( new SolutionBase ); - } - - //! String indicating the type of the SolutionBase object. Corresponds - //! to the type of phase originally instantiated - std::string type() const; - - //! Set the type of this SolutionBase object - void setType(const std::string& type); - - /*! Name and phase - * Class SolutionBase references two strings that identify a SolutionBase. - * The phase is the value of the phase name in YAML (or ID attribute - * of the XML node) that is used to initialize a phase when it is read. - * The name field is also initialized to the value of the phase name - * read from the XML/YAML node. - * - * However, the name field may be changed to another value during the course - * of a calculation. For example, if a SolutionBase is located in two places, - * but has the same constitutive input, the phase of the two SolutionBases - * will be the same, but the names of the two SolutionBases need to differ. - * - * It is an error to have two phases in a single problem with the same name - * attribute. Thus, it is expected that there is a 1-1 correspondence between - * names and unique SolutionBase objects within a Cantera problem. - */ - - //! Return the phase string of this SolutionBase. - std::string phase() const; - - //! Set the phase string of this SolutionBase. - void setPhase(const std::string& id); - - //! Return the name of this SolutionBase object - std::string name() const; - - //! Set the name of this SolutionBase object - void setName(const std::string& name); - - //! Generate self-documenting YAML string - virtual std::string toYAML() const; - - //! Set the ThermoPhase object - void setThermoPhase(shared_ptr thermo); - - //! Set the Kinetics object - void setKinetics(shared_ptr kinetics); - - //! Set the Transport object - void setTransport(shared_ptr transport); - - //! Accessor for the ThermoPhase object - ThermoPhase& thermo() { - return *m_thermo; - } - - //! Accessor for the Kinetics object - Kinetics& kinetics() { - return *m_kinetics; - } - - //! Accessor for the Transport object - Transport& transport() { - return *m_transport; - } - -protected: - shared_ptr m_thermo; //! ThermoPhase manager - shared_ptr m_kinetics; //! Kinetics manager - shared_ptr m_transport; //! Transport manager - - std::string m_type; //! type of SolutionBase object -}; - -} -#endif diff --git a/include/cantera/kinetics/Kinetics.h b/include/cantera/kinetics/Kinetics.h index 4cacaa8e49..57b2853954 100644 --- a/include/cantera/kinetics/Kinetics.h +++ b/include/cantera/kinetics/Kinetics.h @@ -19,7 +19,7 @@ namespace Cantera { -class SolutionBase; +class Solution; /** * @defgroup chemkinetics Chemical Kinetics @@ -816,8 +816,8 @@ class Kinetics void selectPhase(const doublereal* data, const thermo_t* phase, doublereal* phase_data); - //! Set root SolutionBase holding all phase information - virtual void setRoot(std::shared_ptr root) { + //! Set root Solution holding all phase information + virtual void setRoot(std::shared_ptr root) { m_root = root; } @@ -943,8 +943,8 @@ class Kinetics //! @see skipUndeclaredThirdBodies() bool m_skipUndeclaredThirdBodies; - //! reference to SolutionBase - std::weak_ptr m_root; + //! reference to Solution + std::weak_ptr m_root; }; } diff --git a/include/cantera/thermo/Phase.h b/include/cantera/thermo/Phase.h index 289b3faba5..0dc75d7f4c 100644 --- a/include/cantera/thermo/Phase.h +++ b/include/cantera/thermo/Phase.h @@ -29,7 +29,7 @@ namespace Cantera * support thermodynamic calculations (see \ref thermoprops). */ -class SolutionBase; +class Solution; //! Class Phase is the base class for phases of matter, managing the species and //! elements in a phase, as well as the independent variables of temperature, @@ -760,8 +760,8 @@ class Phase m_caseSensitiveSpecies = cflag; } - //! Set root SolutionBase holding all phase information - virtual void setRoot(std::shared_ptr root) { + //! Set root Solution holding all phase information + virtual void setRoot(std::shared_ptr root) { m_root = root; } @@ -878,8 +878,8 @@ class Phase //! Entropy at 298.15 K and 1 bar of stable state pure elements (J kmol-1) vector_fp m_entropy298; - //! reference to SolutionBase - std::weak_ptr m_root; + //! reference to Solution + std::weak_ptr m_root; }; } diff --git a/include/cantera/transport/TransportBase.h b/include/cantera/transport/TransportBase.h index 171aff9f39..664d47d2a4 100644 --- a/include/cantera/transport/TransportBase.h +++ b/include/cantera/transport/TransportBase.h @@ -74,7 +74,7 @@ const VelocityBasis VB_SPECIES_2 = 2; const VelocityBasis VB_SPECIES_3 = 3; //@} -class SolutionBase; +class Solution; //! Base class for transport property managers. /*! @@ -656,8 +656,8 @@ class Transport */ virtual void setThermo(thermo_t& thermo); - //! Set root SolutionBase holding all phase information - virtual void setRoot(std::shared_ptr root) { + //! Set root Solution holding all phase information + virtual void setRoot(std::shared_ptr root) { m_root = root; } @@ -688,8 +688,8 @@ class Transport //! Defaults to the mass averaged basis = -2 int m_velocityBasis; - //! reference to SolutionBase - std::weak_ptr m_root; + //! reference to Solution + std::weak_ptr m_root; }; } diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 7c4a4c253b..827b4acf24 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -121,20 +121,16 @@ cdef extern from "cantera/thermo/Species.h" namespace "Cantera": cdef vector[shared_ptr[CxxSpecies]] CxxGetSpecies "getSpecies" (CxxAnyValue&) except +translate_exception -cdef extern from "cantera/base/SolutionBase.h" namespace "Cantera": - cdef cppclass CxxSolutionBase "Cantera::SolutionBase": - CxxSolutionBase() - string type() - string setType(string) - string phase() - void setPhase(string) +cdef extern from "cantera/base/Solution.h" namespace "Cantera": + cdef cppclass CxxSolution "Cantera::Solution": + CxxSolution() string name() void setName(string) void setThermoPhase(shared_ptr[CxxThermoPhase]) void setKinetics(shared_ptr[CxxKinetics]) void setTransport(shared_ptr[CxxTransport]) - cdef shared_ptr[CxxSolutionBase] CxxNewSolutionBase "Cantera::SolutionBase::create" () + cdef shared_ptr[CxxSolution] CxxNewSolution "Cantera::Solution::create" () cdef extern from "cantera/thermo/ThermoPhase.h" namespace "Cantera": @@ -144,6 +140,8 @@ 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 @@ -941,8 +939,8 @@ cdef class GasTransportData: cdef _assign(self, shared_ptr[CxxTransportData] other) cdef class _SolutionBase: - cdef shared_ptr[CxxSolutionBase] _base - cdef CxxSolutionBase* base + cdef shared_ptr[CxxSolution] _base + cdef CxxSolution* base cdef shared_ptr[CxxThermoPhase] _thermo cdef CxxThermoPhase* thermo cdef shared_ptr[CxxKinetics] _kinetics diff --git a/interfaces/cython/cantera/base.pyx b/interfaces/cython/cantera/base.pyx index 290898bcca..76db56e515 100644 --- a/interfaces/cython/cantera/base.pyx +++ b/interfaces/cython/cantera/base.pyx @@ -6,24 +6,24 @@ from collections import defaultdict as _defaultdict _phase_counts = _defaultdict(int) cdef class _SolutionBase: - def __cinit__(self, infile='', phase='', adjacent=(), origin=None, + def __cinit__(self, infile='', phase_id='', adjacent=(), origin=None, source=None, yaml=None, thermo=None, species=(), kinetics=None, reactions=(), **kwargs): if 'phaseid' in kwargs: - if phase is not '': + if phase_id is not '': raise AttributeError('duplicate specification of phase name') - warnings.warn("Keyword `phase` replaces `phaseid`", + warnings.warn("Keyword 'phase_id' replaces 'phaseid'", FutureWarning) - phase = kwargs['phaseid'] + phase_id = kwargs['phaseid'] if 'phases' in kwargs: if len(adjacent)>0: raise AttributeError( 'duplicate specification of adjacent phases') - warnings.warn("Keyword `adjacent` replaces `phases`", + warnings.warn("Keyword 'adjacent' replaces 'phases'", FutureWarning) adjacent = kwargs['phases'] @@ -46,7 +46,7 @@ cdef class _SolutionBase: return # Assign base and set managers to NULL - self._base = CxxNewSolutionBase() + self._base = CxxNewSolution() self.base = self._base.get() self.thermo = NULL self.kinetics = NULL @@ -54,9 +54,9 @@ cdef class _SolutionBase: # Parse inputs if infile.endswith('.yml') or infile.endswith('.yaml') or yaml: - self._init_yaml(infile, phase, adjacent, yaml) + self._init_yaml(infile, phase_id, adjacent, yaml) elif infile or source: - self._init_cti_xml(infile, phase, adjacent, source) + self._init_cti_xml(infile, phase_id, adjacent, source) elif thermo and species: self._init_parts(thermo, species, kinetics, adjacent, reactions) else: @@ -72,14 +72,8 @@ cdef class _SolutionBase: if isinstance(self, Transport): assert self.transport is not NULL - base_type = kwargs.get('base_type', None) - if base_type: - self.base.setType(stringify(base_type)) - else: - raise ValueError('Missing required keyword `base_type`.') - - phase_name = pystr(self.base.phase()) - name = kwargs.get('name', None) + phase_name = pystr(self.thermo.id()) + name = kwargs.get('name') if name is not None: self.name = name elif phase_name in _phase_counts: @@ -90,49 +84,11 @@ cdef class _SolutionBase: _phase_counts[phase_name] = 0 self.name = phase_name - property type: - """ - The type of the SolutionBase object. The type is set during object - instantiation and cannot be modified. - """ - def __get__(self): - return pystr(self.base.type()) - - property phase: - """ - The ID of the SolutionBase object. The phase corresponds to the - CTI/XML/YAML input file entry. - """ - def __get__(self): - return pystr(self.base.phase()) - def __set__(self, phase_name): - # may consider removing/deprecating, but resetting of the phase name - # is required to associate surface kinetics (with phase name being 'gas') - self.base.setPhase(stringify(phase_name)) - - property ID: - """ - The ID of the SolutionBase object. The default is taken from the - CTI/XML/YAML input file. - - .. deprecated:: 2.5 - - To be deprecated with version 2.5, and removed thereafter. - Renamed to `phase`. - """ - def __get__(self): - warnings.warn("To be removed after Cantera 2.5. " - "Use `phase` attribute instead", DeprecationWarning) - return pystr(self.base.phase()) - def __set__(self, id_): - warnings.warn("To be removed after Cantera 2.5. " - "Use `phase` attribute instead", DeprecationWarning) - self.base.setPhase(stringify(id_)) - property name: """ - The name assigned to this SolutionBase object. The default is - taken from the CTI/XML/YAML input file. + 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. """ def __get__(self): return pystr(self.base.name()) @@ -155,7 +111,7 @@ cdef class _SolutionBase: return thermo, kinetics, transport - def _init_yaml(self, infile, phase, adjacent, source): + def _init_yaml(self, infile, phase_id, adjacent, source): """ Instantiate a set of new Cantera C++ objects from a YAML phase definition @@ -167,7 +123,7 @@ cdef class _SolutionBase: root = AnyMapFromYamlString(stringify(source)) phaseNode = root[stringify("phases")].getMapWhere(stringify("name"), - stringify(phase)) + stringify(phase_id)) # Thermo if isinstance(self, ThermoPhase): @@ -178,19 +134,19 @@ cdef class _SolutionBase: # Kinetics cdef vector[CxxThermoPhase*] v - cdef _SolutionBase adj + cdef _SolutionBase phase if isinstance(self, Kinetics): v.push_back(self.thermo) - for adj in adjacent: + for phase in adjacent: # adjacent bulk phases for a surface phase - v.push_back(adj.thermo) + v.push_back(phase.thermo) self._kinetics = newKinetics(v, phaseNode, root) self.kinetics = self._kinetics.get() else: self.kinetics = NULL - def _init_cti_xml(self, infile, phase, adjacent, source): + def _init_cti_xml(self, infile, phase_id, adjacent, source): """ Instantiate a set of new Cantera C++ objects from a CTI or XML phase definition @@ -202,8 +158,8 @@ cdef class _SolutionBase: # Get XML data cdef XML_Node* phaseNode - if phase: - phaseNode = rootNode.findID(stringify(phase)) + if phase_id: + phaseNode = rootNode.findID(stringify(phase_id)) else: phaseNode = rootNode.findByName(stringify('phase')) if phaseNode is NULL: @@ -218,13 +174,13 @@ cdef class _SolutionBase: # Kinetics cdef vector[CxxThermoPhase*] v - cdef _SolutionBase adj + cdef _SolutionBase phase if isinstance(self, Kinetics): v.push_back(self.thermo) - for adj in adjacent: + for phase in adjacent: # adjacent bulk phases for a surface phase - v.push_back(adj.thermo) + v.push_back(phase.thermo) self.kinetics = newKineticsMgr(deref(phaseNode), v) self._kinetics.reset(self.kinetics) else: @@ -246,15 +202,15 @@ cdef class _SolutionBase: if not kinetics: kinetics = "none" - cdef ThermoPhase adj + cdef ThermoPhase phase cdef Reaction reaction if isinstance(self, Kinetics): self.kinetics = CxxNewKinetics(stringify(kinetics)) self._kinetics.reset(self.kinetics) self.kinetics.addPhase(deref(self.thermo)) - for adj in adjacent: + for phase in adjacent: # adjacent bulk phases for a surface phase - self.kinetics.addPhase(deref(adj.thermo)) + self.kinetics.addPhase(deref(phase.thermo)) self.kinetics.init() self.kinetics.skipUndeclaredThirdBodies(True) for reaction in reactions: diff --git a/interfaces/cython/cantera/ck2yaml.py b/interfaces/cython/cantera/ck2yaml.py index c935822fbc..1ffe5a3c72 100644 --- a/interfaces/cython/cantera/ck2yaml.py +++ b/interfaces/cython/cantera/ck2yaml.py @@ -12,7 +12,7 @@ [--thermo=] [--transport=] [--surface=] - [--phase=] + [--phase-id=] [--output=] [--permissive] [-d | --debug] @@ -32,7 +32,7 @@ 'surface'. The '--permissive' option allows certain recoverable parsing errors (e.g. -duplicate transport data) to be ignored. The '--phase=' option +duplicate transport data) to be ignored. The '--phase-id=' option is used to override default phase names (i.e. 'gas'). """ @@ -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=', + longOptions = ['input=', 'thermo=', 'transport=', 'surface=', 'phase-id=', 'output=', 'permissive', 'help', 'debug', 'quiet', 'no-validate', 'id='] @@ -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=...'") + "option '--id=...' is superseded by '--phase-id=...'") else: - phase_name = options.get('--phase', 'gas') + phase_name = options.get('--phase-id', 'gas') if not input_file and not thermo_file: print('At least one of the arguments "--input=..." or "--thermo=..."' diff --git a/interfaces/cython/cantera/composite.py b/interfaces/cython/cantera/composite.py index 9cc5898e3c..fb9ae6b074 100644 --- a/interfaces/cython/cantera/composite.py +++ b/interfaces/cython/cantera/composite.py @@ -25,12 +25,13 @@ class Solution(ThermoPhase, Kinetics, Transport): gas = ct.Solution('gri30.yaml') - If an input file defines multiple phases, the *phases* entry (in YAML), - *name* (in CTI), or *id* (in XML) can be used to specify the desired - phase via the ``phase`` keyword argument of the constructor:: + 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 + the constructor:: - gas = ct.Solution('diamond.yaml', phase='gas') - diamond = ct.Solution('diamond.yaml', phase='diamond') + gas = ct.Solution('diamond.yaml', phase_id='gas') + diamond = ct.Solution('diamond.yaml', phase_id='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 @@ -79,9 +80,6 @@ class Solution(ThermoPhase, Kinetics, Transport): """ __slots__ = () - def __init__(self, *args, **kwargs): - super().__init__(*args, base_type='Solution', **kwargs) - class Interface(InterfacePhase, InterfaceKinetics): """ @@ -95,16 +93,13 @@ 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='gas') - diamond = ct.Solution('diamond.yaml', phase='diamond') - diamond_surf = ct.Interface('diamond.yaml', phase='diamond_100', + 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', adjacent=[gas, diamond]) """ __slots__ = ('_phase_indices',) - def __init__(self, *args, **kwargs): - super().__init__(*args, base_type='Interface', **kwargs) - class DustyGas(ThermoPhase, Kinetics, DustyGasTransport): """ @@ -115,9 +110,6 @@ class DustyGas(ThermoPhase, Kinetics, DustyGasTransport): """ __slots__ = () - def __init__(self, *args, **kwargs): - super().__init__(*args, base_type='DustyGas', **kwargs) - class Quantity: """ diff --git a/interfaces/cython/cantera/kinetics.pyx b/interfaces/cython/cantera/kinetics.pyx index f3c71bb8c2..b16c9690af 100644 --- a/interfaces/cython/cantera/kinetics.pyx +++ b/interfaces/cython/cantera/kinetics.pyx @@ -26,11 +26,6 @@ cdef class Kinetics(_SolutionBase): a reaction mechanism. """ - def __init__(self, *args, **kwargs): - base_type = kwargs.pop('base_type', 'Kinetics') - kwargs['base_type'] = base_type - super().__init__(*args, **kwargs) - property kinetics_model: """ Return type of kinetics. @@ -376,13 +371,13 @@ 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='', phaseid='', phases=(), *args, **kwargs): - super().__init__(infile, phaseid, phases, *args, **kwargs) + def __init__(self, infile='', phase_id='', adjacent=(), *args, **kwargs): + super().__init__(infile, phase_id, adjacent, *args, **kwargs) if pystr(self.kinetics.kineticsType()) not in ("Surf", "Edge"): raise TypeError("Underlying Kinetics class is not of the correct type.") self._phase_indices = {} - for phase in [self] + list(phases): + for phase in [self] + list(adjacent): i = self.kinetics.phaseIndex(stringify(phase.name)) self._phase_indices[phase] = i self._phase_indices[phase.name] = i diff --git a/interfaces/cython/cantera/test/test_convert.py b/interfaces/cython/cantera/test/test_convert.py index 726f547c2b..d101ba38e6 100644 --- a/interfaces/cython/cantera/test/test_convert.py +++ b/interfaces/cython/cantera/test/test_convert.py @@ -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='Pt_surf', ctiphases=[ctiGas], yamlphases=[yamlGas]) + phase_id='Pt_surf', ctiphases=[ctiGas], yamlphases=[yamlGas]) self.checkKinetics(ctiGas, yamlGas, [500, 1200], [1e4, 3e5]) self.checkThermo(ctiSurf, yamlSurf, [400, 800, 1600]) @@ -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='metal') - ctiOxide, yamlOxide = self.checkConversion('sofc', phase='oxide_bulk') + ctiMetal, yamlMetal = self.checkConversion('sofc', phase_id='metal') + ctiOxide, yamlOxide = self.checkConversion('sofc', phase_id='oxide_bulk') ctiMSurf, yamlMSurf = self.checkConversion('sofc', ct.Interface, - phase='metal_surface', ctiphases=[ctiGas, ctiMetal], + phase_id='metal_surface', ctiphases=[ctiGas, ctiMetal], yamlphases=[yamlGas, yamlMetal]) ctiOSurf, yamlOSurf = self.checkConversion('sofc', ct.Interface, - phase='oxide_surface', ctiphases=[ctiGas, ctiOxide], + phase_id='oxide_surface', ctiphases=[ctiGas, ctiOxide], yamlphases=[yamlGas, yamlOxide]) cti_tpb, yaml_tpb = self.checkConversion('sofc', ct.Interface, - phase='tpb', ctiphases=[ctiMetal, ctiMSurf, ctiOSurf], + phase_id='tpb', ctiphases=[ctiMetal, ctiMSurf, ctiOSurf], yamlphases=[yamlMetal, yamlMSurf, yamlOSurf]) self.checkThermo(ctiMSurf, yamlMSurf, [900, 1000, 1100]) @@ -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=name) + ctiPhase, yamlPhase = self.checkConversion('liquidvapor', phase_id=name) self.checkThermo(ctiPhase, yamlPhase, [1.3 * ctiPhase.min_temp, 0.7 * ctiPhase.max_temp]) @@ -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='gas') - ctiSolid, yamlSolid = self.checkConversion('diamond', phase='diamond') + ctiGas, yamlGas = self.checkConversion('diamond', phase_id='gas') + ctiSolid, yamlSolid = self.checkConversion('diamond', phase_id='diamond') ctiSurf, yamlSurf = self.checkConversion('diamond', - ct.Interface, phase='diamond_100', ctiphases=[ctiGas, ctiSolid], + ct.Interface, phase_id='diamond_100', ctiphases=[ctiGas, ctiSolid], yamlphases=[yamlGas, yamlSolid]) self.checkThermo(ctiSolid, yamlSolid, [300, 500]) self.checkThermo(ctiSurf, yamlSurf, [330, 490]) @@ -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='anode') - ctiCathode, yamlCathode = self.checkConversion(name, phase='cathode') - ctiMetal, yamlMetal = self.checkConversion(name, phase='electron') - ctiElyt, yamlElyt = self.checkConversion(name, phase='electrolyte') + 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') ctiAnodeInt, yamlAnodeInt = self.checkConversion(name, - phase='edge_anode_electrolyte', + phase_id='edge_anode_electrolyte', ctiphases=[ctiAnode, ctiMetal, ctiElyt], yamlphases=[yamlAnode, yamlMetal, yamlElyt]) ctiCathodeInt, yamlCathodeInt = self.checkConversion(name, - phase='edge_cathode_electrolyte', + phase_id='edge_cathode_electrolyte', ctiphases=[ctiCathode, ctiMetal, ctiElyt], yamlphases=[yamlCathode, yamlMetal, yamlElyt]) diff --git a/interfaces/cython/cantera/test/test_reactor.py b/interfaces/cython/cantera/test/test_reactor.py index 7d084bc7d0..c8425d9904 100644 --- a/interfaces/cython/cantera/test/test_reactor.py +++ b/interfaces/cython/cantera/test/test_reactor.py @@ -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 = 'gas' + self.gas1.phase_id = 'gas' self.gas2 = ct.Solution('gri30.xml') - self.gas2.phase = 'gas' + self.gas2.phase_id = 'gas' resGas = ct.Solution('gri30.xml') solid = ct.Solution('diamond.xml', 'diamond') diff --git a/interfaces/cython/cantera/test/test_thermo.py b/interfaces/cython/cantera/test/test_thermo.py index 5c3e783e19..c621c491bf 100644 --- a/interfaces/cython/cantera/test/test_thermo.py +++ b/interfaces/cython/cantera/test/test_thermo.py @@ -15,20 +15,18 @@ def setUp(self): self.phase = ct.Solution('h2o2.xml') def test_base_attributes(self): - self.assertTrue(isinstance(self.phase.type, str)) - self.assertTrue(self.phase.type=='Solution') - self.assertTrue(isinstance(self.phase.name, str)) - self.assertTrue(isinstance(self.phase.thermo_model, str)) - self.assertTrue(isinstance(self.phase.kinetics_model, str)) - self.assertTrue(isinstance(self.phase.transport_model, str)) - self.assertTrue(isinstance(self.phase.composite, tuple)) - self.assertTrue(len(self.phase.composite)==3) - self.assertTrue( - self.phase.composite == (self.phase.thermo_model, - self.phase.kinetics_model, - self.phase.transport_model)) + self.assertIsInstance(self.phase.name, str) + self.assertIsInstance(self.phase.thermo_model, str) + self.assertIsInstance(self.phase.kinetics_model, str) + self.assertIsInstance(self.phase.transport_model, str) + self.assertIsInstance(self.phase.composite, tuple) + self.assertEqual(len(self.phase.composite), 3) + self.assertEqual(self.phase.composite, + (self.phase.thermo_model, + self.phase.kinetics_model, + self.phase.transport_model)) self.phase.name = 'spam' - self.assertTrue(self.phase.name=='spam') + self.assertEqual(self.phase.name, 'spam') with self.assertRaises(AttributeError): self.phase.type = 'eggs' @@ -324,30 +322,30 @@ def test_name(self): self.assertIn('something', self.phase.report()) def test_phase(self): - self.assertEqual(self.phase.phase, 'ohmech') + self.assertEqual(self.phase.phase_id, 'ohmech') warnings.simplefilter("always") with warnings.catch_warnings(record=True) as w: self.assertEqual(self.phase.ID, 'ohmech') - self.assertTrue(len(w) == 1) + self.assertEqual(len(w), 1) self.assertTrue(issubclass(w[-1].category, DeprecationWarning)) - self.assertTrue("To be removed after Cantera 2.5. " - in str(w[-1].message)) + self.assertIn("To be removed after Cantera 2.5. ", + str(w[-1].message)) with warnings.catch_warnings(record=True) as w: self.phase.ID = 'something' - self.assertEqual(self.phase.phase, 'something') - self.assertTrue(len(w) == 1) + self.assertEqual(self.phase.phase_id, 'something') + self.assertEqual(len(w), 1) self.assertTrue(issubclass(w[-1].category, DeprecationWarning)) - self.assertTrue("To be removed after Cantera 2.5. " - in str(w[-1].message)) + self.assertIn("To be removed after Cantera 2.5. ", + str(w[-1].message)) with warnings.catch_warnings(record=True) as w: gas = ct.Solution('h2o2.cti', phaseid='ohmech') - self.assertTrue(len(w) == 1) + self.assertEqual(len(w), 1) self.assertTrue(issubclass(w[-1].category, FutureWarning)) - self.assertTrue("Keyword `phase` replaces `phaseid`" - in str(w[-1].message)) + self.assertIn("Keyword 'phase_id' replaces 'phaseid'", + str(w[-1].message)) def test_badLength(self): X = np.zeros(5) @@ -882,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, phase) + self.assertEqual(gas.phase_id, phase) self.assertNear(gas.T, T) self.assertNear(gas.P, P) self.assertEqual(gas.n_species, nSpec) @@ -1171,7 +1169,7 @@ def test_invalid(self): def test_wrap(self): st = self.gas.species('H2O').thermo - self.assertTrue(isinstance(st, ct.NasaPoly2)) + self.assertIsInstance(st, ct.NasaPoly2) for T in [300, 500, 900, 1200, 2000]: self.gas.TP = T, 101325 diff --git a/interfaces/cython/cantera/thermo.pyx b/interfaces/cython/cantera/thermo.pyx index 7777a80811..e71b12ed6d 100644 --- a/interfaces/cython/cantera/thermo.pyx +++ b/interfaces/cython/cantera/thermo.pyx @@ -263,8 +263,6 @@ cdef class ThermoPhase(_SolutionBase): # The signature of this function causes warnings for Sphinx documentation def __init__(self, *args, **kwargs): - base_type = kwargs.pop('base_type', 'ThermoPhase') - kwargs['base_type'] = base_type super().__init__(*args, **kwargs) if 'source' not in kwargs: self.thermo_basis = mass_basis @@ -291,6 +289,37 @@ 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. + + .. deprecated:: 2.5 + + To be deprecated with version 2.5, and removed thereafter. + Renamed to `phase_ID`. + """ + def __get__(self): + warnings.warn("To be removed after Cantera 2.5. " + "Use 'phase' attribute instead", DeprecationWarning) + return pystr(self.thermo.id()) + def __set__(self, id_): + warnings.warn("To be removed after Cantera 2.5. " + "Use 'phase' attribute instead", DeprecationWarning) + self.thermo.setID(stringify(id_)) + property basis: """ Determines whether intensive thermodynamic properties are treated on a diff --git a/src/base/Solution.cpp b/src/base/Solution.cpp new file mode 100644 index 0000000000..51099a1868 --- /dev/null +++ b/src/base/Solution.cpp @@ -0,0 +1,46 @@ +//! @file Solution.cpp + +// This file is part of Cantera. See License.txt in the top-level directory or +// at https://cantera.org/license.txt for license and copyright information. + +#include "cantera/base/Solution.h" +#include "cantera/thermo/ThermoPhase.h" +#include "cantera/kinetics/Kinetics.h" +#include "cantera/transport/TransportBase.h" + +namespace Cantera +{ + +Solution::Solution() : + m_name("") +{} + +std::string Solution::name() const { + return m_name; +} + +void Solution::setName(const std::string& name){ + m_name = name; +} + +void Solution::setThermoPhase(shared_ptr thermo) { + m_thermo = thermo; + if (m_thermo) { + m_thermo->setRoot(shared_from_this()); + } +} + +void Solution::setKinetics(shared_ptr kinetics) { + m_kinetics = kinetics; + if (m_kinetics) { + m_kinetics->setRoot(shared_from_this()); + } +} + +void Solution::setTransport(shared_ptr transport) { + m_transport = transport; + if (m_transport) { + m_transport->setRoot(shared_from_this()); + } +} +} diff --git a/src/base/SolutionBase.cpp b/src/base/SolutionBase.cpp deleted file mode 100644 index dd9e8db6cc..0000000000 --- a/src/base/SolutionBase.cpp +++ /dev/null @@ -1,99 +0,0 @@ -//! @file SolutionBase.cpp - -// This file is part of Cantera. See License.txt in the top-level directory or -// at https://cantera.org/license.txt for license and copyright information. - -#include "cantera/base/SolutionBase.h" -#include "cantera/thermo/ThermoPhase.h" -#include "cantera/kinetics/Kinetics.h" -#include "cantera/transport/TransportBase.h" - -namespace Cantera -{ - -SolutionBase::SolutionBase() : - m_thermo(nullptr), - m_kinetics(nullptr), - m_transport(nullptr), - m_type("") -{} - -SolutionBase::SolutionBase(const std::string& infile, - const std::string& phasename) : - SolutionBase() -{ - // this *may* be a spot to load all pieces of a phase - throw NotImplementedError("SolutionBase constructor from file"); -} - -std::string SolutionBase::type() const { - return m_type; -} - -void SolutionBase::setType(const std::string& type){ - m_type = type; -} - -std::string SolutionBase::phase() const { - // currently managed by ThermoPhase - if (m_thermo) { - return m_thermo->id(); - } else { - throw CanteraError("SolutionBase::id()", "Missing ThermoPhase."); - } -} - -void SolutionBase::setPhase(const std::string& id) { - // currently managed by ThermoPhase - // note: may consider removing (but needed for association of surface - // kinetics which require the phase name "gas") - if (m_thermo) { - return m_thermo->setID(id); - } else { - throw CanteraError("SolutionBase::setID()", "Missing ThermoPhase."); - } -} - -std::string SolutionBase::name() const { - // currently managed by ThermoPhase - if (m_thermo) { - return m_thermo->name(); - } else { - throw CanteraError("SolutionBase::name()", "Missing ThermoPhase."); - } -} - -void SolutionBase::setName(const std::string& name){ - // currently managed by ThermoPhase - if (m_thermo) { - return m_thermo->setName(name); - } else { - throw CanteraError("SolutionBase::setName()", "Missing ThermoPhase."); - } -} - -std::string SolutionBase::toYAML() const { - throw NotImplementedError("SolutionBase::toYAML"); -} - -void SolutionBase::setThermoPhase(shared_ptr thermo) { - m_thermo = thermo; - if (m_thermo) { - m_thermo->setRoot(shared_from_this()); - } -} - -void SolutionBase::setKinetics(shared_ptr kinetics) { - m_kinetics = kinetics; - if (m_kinetics) { - m_kinetics->setRoot(shared_from_this()); - } -} - -void SolutionBase::setTransport(shared_ptr transport) { - m_transport = transport; - if (m_transport) { - m_transport->setRoot(shared_from_this()); - } -} -} diff --git a/src/thermo/Phase.cpp b/src/thermo/Phase.cpp index 85798d5ec8..202f33b43c 100644 --- a/src/thermo/Phase.cpp +++ b/src/thermo/Phase.cpp @@ -9,6 +9,7 @@ #include "cantera/thermo/Phase.h" #include "cantera/base/utilities.h" #include "cantera/base/stringUtils.h" +#include "cantera/base/Solution.h" #include "cantera/base/ctml.h" #include "cantera/thermo/ThermoFactory.h" @@ -78,12 +79,22 @@ void Phase::setID(const std::string& id_) std::string Phase::name() const { - return m_name; + auto root = m_root.lock(); + if (root) { + return root->name(); + } else { + return m_name; + } } -void Phase::setName(const std::string& nm) +void Phase::setName(const std::string& name) { - m_name = nm; + auto root = m_root.lock(); + if (root) { + root->setName(name); + } else { + m_name = name; + } } size_t Phase::nElements() const