From 0aa5723def8d9aff24f644785f385cd12c63b30f Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Fri, 16 Aug 2019 07:26:24 -0500 Subject: [PATCH 01/17] [Base] create SolutionBase object in C++ * Add Base.h/Base.cpp with definition of SolutionBase * Link C++ object into Cython interface * Add unique_name and type attributes to Cython _SolutionBase --- include/cantera/base/Base.h | 72 +++++++++++++++++++++++++ interfaces/cython/cantera/_cantera.pxd | 14 +++++ interfaces/cython/cantera/base.pyx | 38 +++++++++++++ interfaces/cython/cantera/composite.py | 9 ++++ interfaces/cython/cantera/kinetics.pyx | 5 ++ interfaces/cython/cantera/thermo.pyx | 2 + interfaces/cython/cantera/transport.pyx | 4 ++ src/base/Base.cpp | 38 +++++++++++++ 8 files changed, 182 insertions(+) create mode 100644 include/cantera/base/Base.h create mode 100644 src/base/Base.cpp diff --git a/include/cantera/base/Base.h b/include/cantera/base/Base.h new file mode 100644 index 00000000000..4c3acea5a14 --- /dev/null +++ b/include/cantera/base/Base.h @@ -0,0 +1,72 @@ +//! @file Base.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_BASE_H +#define CT_BASE_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: + SolutionBase(); + SolutionBase(const std::string& infile, const std::string& phasename); + ~SolutionBase() {} + SolutionBase(const SolutionBase&) = delete; + SolutionBase& operator=(const SolutionBase&) = delete; + + //! String indicating the type of the SolutionBase object. Corresponds + //! to the type of phase originally instantiated + std::string type() const { + return m_type; + } + + //! Set the type of this SolutionBase object + void setType(const std::string& type){ + m_type = type; + } + + //! Return the name of this SolutionBase object + std::string name() const { + return m_name; + } + + //! Set the name of this SolutionBase object + void setName(const std::string& name){ + m_name = name; + } + + //! Generate self-documenting YAML string + virtual std::string toYAML() const { + throw NotImplementedError("SolutionBase::toYAML"); + } + + //! 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); + +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 + std::string m_name; //! name of SolutionBase object +}; + +} +#endif diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index a37104633f3..ff5ac7219f8 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -120,6 +120,19 @@ cdef extern from "cantera/thermo/Species.h" namespace "Cantera": cdef shared_ptr[CxxSpecies] CxxNewSpecies "newSpecies" (CxxAnyMap&) except +translate_exception cdef vector[shared_ptr[CxxSpecies]] CxxGetSpecies "getSpecies" (CxxAnyValue&) except +translate_exception + +cdef extern from "cantera/base/Base.h" namespace "Cantera": + cdef cppclass CxxSolutionBase "Cantera::SolutionBase": + CxxSolutionBase() + string type() + string setType(string) + string name() + void setName(string) + void setThermoPhase(shared_ptr[CxxThermoPhase]) + void setKinetics(shared_ptr[CxxKinetics]) + void setTransport(shared_ptr[CxxTransport]) + + cdef extern from "cantera/thermo/ThermoPhase.h" namespace "Cantera": cdef cppclass CxxThermoPhase "Cantera::ThermoPhase": CxxThermoPhase() @@ -928,6 +941,7 @@ cdef class GasTransportData: cdef _assign(self, shared_ptr[CxxTransportData] other) cdef class _SolutionBase: + cdef CxxSolutionBase* 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 f2284c1af9b..802b8c13f71 100644 --- a/interfaces/cython/cantera/base.pyx +++ b/interfaces/cython/cantera/base.pyx @@ -1,6 +1,10 @@ # 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. +from collections import defaultdict as _defaultdict + +_phase_counts = _defaultdict(int) + cdef class _SolutionBase: def __cinit__(self, infile='', phaseid='', phases=(), origin=None, source=None, yaml=None, thermo=None, species=(), @@ -10,6 +14,7 @@ cdef class _SolutionBase: if origin is not None: other = <_SolutionBase?>origin + self.base = other.base self.thermo = other.thermo self.kinetics = other.kinetics self.transport = other.transport @@ -21,6 +26,9 @@ cdef class _SolutionBase: self._selected_species = other._selected_species.copy() return + # Assign type and unique_name during __init__ + self.base = new CxxSolutionBase() + if infile.endswith('.yml') or infile.endswith('.yaml') or yaml: self._init_yaml(infile, phaseid, phases, yaml) elif infile or source: @@ -31,6 +39,8 @@ cdef class _SolutionBase: raise ValueError("Arguments are insufficient to define a phase") # Initialization of transport is deferred to Transport.__init__ + self.base.setThermoPhase(self._thermo) + self.base.setKinetics(self._kinetics) self.transport = NULL self._selected_species = np.ndarray(0, dtype=np.integer) @@ -39,6 +49,34 @@ 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`.') + + phasename = pystr(self.thermo.name()) + name = kwargs.get('name', None) + if name is not None: + self.unique_name = name + else: + _phase_counts[phasename] += 1 + n = _phase_counts[phasename] + self.unique_name = '{0}_{1}'.format(phasename, n) + + property type: + """The type of the SolutionBase object.""" + def __get__(self): + return pystr(self.base.type()) + + property unique_name: + """The unique name of the SolutionBase object.""" + def __get__(self): + return pystr(self.base.name()) + + def __set__(self, name): + self.base.setName(stringify(name)) + def _init_yaml(self, infile, phaseid, phases, source): """ Instantiate a set of new Cantera C++ objects from a YAML diff --git a/interfaces/cython/cantera/composite.py b/interfaces/cython/cantera/composite.py index 196dc48e798..22f5dee6d30 100644 --- a/interfaces/cython/cantera/composite.py +++ b/interfaces/cython/cantera/composite.py @@ -63,6 +63,9 @@ class Solution(ThermoPhase, Kinetics, Transport): """ __slots__ = () + def __init__(self, *args, **kwargs): + super().__init__(*args, base_type='Solution', **kwargs) + class Interface(InterfacePhase, InterfaceKinetics): """ @@ -82,6 +85,9 @@ class Interface(InterfacePhase, InterfaceKinetics): """ __slots__ = ('_phase_indices',) + def __init__(self, *args, **kwargs): + super().__init__(*args, base_type='Interface', **kwargs) + class DustyGas(ThermoPhase, Kinetics, DustyGasTransport): """ @@ -93,6 +99,9 @@ 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 44ae9281f01..89e7544c958 100644 --- a/interfaces/cython/cantera/kinetics.pyx +++ b/interfaces/cython/cantera/kinetics.pyx @@ -26,6 +26,11 @@ 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 n_total_species: """ Total number of species in all phases participating in the kinetics diff --git a/interfaces/cython/cantera/thermo.pyx b/interfaces/cython/cantera/thermo.pyx index 4980b69ecca..9ed8a479ea1 100644 --- a/interfaces/cython/cantera/thermo.pyx +++ b/interfaces/cython/cantera/thermo.pyx @@ -263,6 +263,8 @@ 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 diff --git a/interfaces/cython/cantera/transport.pyx b/interfaces/cython/cantera/transport.pyx index b6235b7dee6..82fa37aca04 100644 --- a/interfaces/cython/cantera/transport.pyx +++ b/interfaces/cython/cantera/transport.pyx @@ -144,7 +144,11 @@ cdef class Transport(_SolutionBase): model = 'None' self.transport = newTransportMgr(stringify(model), self.thermo) self._transport.reset(self.transport) + + base_type = kwargs.pop('base_type', 'Transport') + kwargs['base_type'] = base_type super().__init__(*args, **kwargs) + self.base.setTransport(self._transport) property transport_model: """ diff --git a/src/base/Base.cpp b/src/base/Base.cpp new file mode 100644 index 00000000000..367b5659d41 --- /dev/null +++ b/src/base/Base.cpp @@ -0,0 +1,38 @@ +//! @file Base.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/Base.h" + +namespace Cantera +{ + +SolutionBase::SolutionBase() : + m_thermo(nullptr), + m_kinetics(nullptr), + m_transport(nullptr), + m_type(""), + m_name("") +{} + +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"); +} + +void SolutionBase::setThermoPhase(shared_ptr thermo) { + m_thermo = thermo; +} + +void SolutionBase::setKinetics(shared_ptr kinetics) { + m_kinetics = kinetics; +} + +void SolutionBase::setTransport(shared_ptr transport) { + m_transport = transport; +} +} From bc04a967d29d22328f763036f786a827a1872cd6 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Fri, 16 Aug 2019 09:33:15 -0500 Subject: [PATCH 02/17] [Base] add unit test probing methods of C++ SolutionBase --- interfaces/cython/cantera/test/test_thermo.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/interfaces/cython/cantera/test/test_thermo.py b/interfaces/cython/cantera/test/test_thermo.py index 8ce4d846e6d..49ed49df609 100644 --- a/interfaces/cython/cantera/test/test_thermo.py +++ b/interfaces/cython/cantera/test/test_thermo.py @@ -12,6 +12,15 @@ class TestThermoPhase(utilities.CanteraTest): def setUp(self): self.phase = ct.Solution('h2o2.xml') + def test_cpp_attributes(self): + self.assertTrue(isinstance(self.phase.type, str)) + self.assertTrue(self.phase.type=='Solution') + self.assertTrue(isinstance(self.phase.unique_name, str)) + self.phase.unique_name = 'spam' + self.assertTrue(self.phase.unique_name=='spam') + with self.assertRaises(AttributeError): + self.phase.type = 'eggs' + def test_phases(self): self.assertEqual(self.phase.n_phases, 1) From 8036169772987d4022db3ad7e62e99708987094a Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Fri, 16 Aug 2019 10:42:30 -0500 Subject: [PATCH 03/17] [Base] link managers back to SolutionBase --- include/cantera/base/Base.h | 7 ++++++- include/cantera/kinetics/Kinetics.h | 10 ++++++++++ include/cantera/thermo/ThermoPhase.h | 10 ++++++++++ include/cantera/transport/TransportBase.h | 10 ++++++++++ interfaces/cython/cantera/_cantera.pxd | 3 +++ interfaces/cython/cantera/base.pyx | 11 ++++++++--- src/base/Base.cpp | 13 +++++++++++++ 7 files changed, 60 insertions(+), 4 deletions(-) diff --git a/include/cantera/base/Base.h b/include/cantera/base/Base.h index 4c3acea5a14..9c084642285 100644 --- a/include/cantera/base/Base.h +++ b/include/cantera/base/Base.h @@ -16,7 +16,8 @@ class Kinetics; class Transport; //! A container class holding managers for all pieces defining a phase -class SolutionBase { +class SolutionBase : public std::enable_shared_from_this +{ public: SolutionBase(); SolutionBase(const std::string& infile, const std::string& phasename); @@ -24,6 +25,10 @@ class 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 { diff --git a/include/cantera/kinetics/Kinetics.h b/include/cantera/kinetics/Kinetics.h index 17b232d4b82..4cacaa8e49e 100644 --- a/include/cantera/kinetics/Kinetics.h +++ b/include/cantera/kinetics/Kinetics.h @@ -19,6 +19,8 @@ namespace Cantera { +class SolutionBase; + /** * @defgroup chemkinetics Chemical Kinetics */ @@ -814,6 +816,11 @@ 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) { + m_root = root; + } + protected: //! Cache for saved calculations within each Kinetics object. ValueCache m_cache; @@ -935,6 +942,9 @@ class Kinetics //! @see skipUndeclaredThirdBodies() bool m_skipUndeclaredThirdBodies; + + //! reference to SolutionBase + std::weak_ptr m_root; }; } diff --git a/include/cantera/thermo/ThermoPhase.h b/include/cantera/thermo/ThermoPhase.h index fdac8cf29c9..2b21ce6e1d4 100644 --- a/include/cantera/thermo/ThermoPhase.h +++ b/include/cantera/thermo/ThermoPhase.h @@ -40,6 +40,8 @@ const int cSS_CONVENTION_VPSS = 1; const int cSS_CONVENTION_SLAVE = 2; //@} +class SolutionBase; + //! Base class for a phase with thermodynamic properties. /*! * Class ThermoPhase is the base class for the family of classes that represent @@ -1616,6 +1618,11 @@ class ThermoPhase : public Phase //@} + //! Set root SolutionBase holding all phase information + virtual void setRoot(std::shared_ptr root) { + m_root = root; + } + protected: //! Fills `names` and `data` with the column names and species thermo //! properties to be included in the output of the reportCSV method. @@ -1660,6 +1667,9 @@ class ThermoPhase : public Phase //! last value of the temperature processed by reference state mutable doublereal m_tlast; + + //! reference to SolutionBase + std::weak_ptr m_root; }; //! typedef for the ThermoPhase class diff --git a/include/cantera/transport/TransportBase.h b/include/cantera/transport/TransportBase.h index 9447fd67703..171aff9f396 100644 --- a/include/cantera/transport/TransportBase.h +++ b/include/cantera/transport/TransportBase.h @@ -74,6 +74,8 @@ const VelocityBasis VB_SPECIES_2 = 2; const VelocityBasis VB_SPECIES_3 = 3; //@} +class SolutionBase; + //! Base class for transport property managers. /*! * All classes that compute transport properties for a single phase derive from @@ -654,6 +656,11 @@ class Transport */ virtual void setThermo(thermo_t& thermo); + //! Set root SolutionBase holding all phase information + virtual void setRoot(std::shared_ptr root) { + m_root = root; + } + protected: //! Enable the transport object for use. /*! @@ -680,6 +687,9 @@ class Transport //! Velocity basis from which diffusion velocities are computed. //! Defaults to the mass averaged basis = -2 int m_velocityBasis; + + //! reference to SolutionBase + std::weak_ptr m_root; }; } diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index ff5ac7219f8..ff576b20efd 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -132,6 +132,8 @@ cdef extern from "cantera/base/Base.h" namespace "Cantera": void setKinetics(shared_ptr[CxxKinetics]) void setTransport(shared_ptr[CxxTransport]) + cdef shared_ptr[CxxSolutionBase] CxxNewSolutionBase "Cantera::SolutionBase::create" () + cdef extern from "cantera/thermo/ThermoPhase.h" namespace "Cantera": cdef cppclass CxxThermoPhase "Cantera::ThermoPhase": @@ -942,6 +944,7 @@ cdef class GasTransportData: cdef class _SolutionBase: cdef CxxSolutionBase* base + cdef shared_ptr[CxxSolutionBase] _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 802b8c13f71..4bb8dcb57ff 100644 --- a/interfaces/cython/cantera/base.pyx +++ b/interfaces/cython/cantera/base.pyx @@ -18,6 +18,7 @@ cdef class _SolutionBase: self.thermo = other.thermo self.kinetics = other.kinetics self.transport = other.transport + self._base = other._base self._thermo = other._thermo self._kinetics = other._kinetics self._transport = other._transport @@ -26,9 +27,14 @@ cdef class _SolutionBase: self._selected_species = other._selected_species.copy() return - # Assign type and unique_name during __init__ - self.base = new CxxSolutionBase() + # Assign base and set managers to NULL + self._base = CxxNewSolutionBase() + self.base = self._base.get() + self.thermo = NULL + self.kinetics = NULL + self.transport = NULL + # Parse inputs if infile.endswith('.yml') or infile.endswith('.yaml') or yaml: self._init_yaml(infile, phaseid, phases, yaml) elif infile or source: @@ -41,7 +47,6 @@ cdef class _SolutionBase: # Initialization of transport is deferred to Transport.__init__ self.base.setThermoPhase(self._thermo) self.base.setKinetics(self._kinetics) - self.transport = NULL self._selected_species = np.ndarray(0, dtype=np.integer) diff --git a/src/base/Base.cpp b/src/base/Base.cpp index 367b5659d41..82fe246e08d 100644 --- a/src/base/Base.cpp +++ b/src/base/Base.cpp @@ -4,6 +4,9 @@ // at https://cantera.org/license.txt for license and copyright information. #include "cantera/base/Base.h" +#include "cantera/thermo/ThermoPhase.h" +#include "cantera/kinetics/Kinetics.h" +#include "cantera/transport/TransportBase.h" namespace Cantera { @@ -24,15 +27,25 @@ SolutionBase::SolutionBase(const std::string& infile, throw NotImplementedError("SolutionBase constructor from file"); } + 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()); + } } } From bc6ff919148cdb194718ce0e5742dc85ff5db03a Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Fri, 16 Aug 2019 16:39:12 -0500 Subject: [PATCH 04/17] [Base] remove redundant unique_name attribute * Resolve conflation of gas.ID and gas.name in unit tests * Also fixes #691 --- include/cantera/thermo/Phase.h | 10 ++++++++++ include/cantera/thermo/ThermoPhase.h | 12 +---------- interfaces/cython/cantera/_cantera.pxd | 2 +- interfaces/cython/cantera/base.pyx | 20 ++++++++++++------- .../cython/cantera/test/test_mixture.py | 5 +++-- interfaces/cython/cantera/test/test_thermo.py | 12 ++++------- interfaces/cython/cantera/thermo.pyx | 11 ---------- src/thermo/Phase.cpp | 15 ++++++++++++-- src/thermo/ThermoFactory.cpp | 1 + 9 files changed, 46 insertions(+), 42 deletions(-) diff --git a/include/cantera/thermo/Phase.h b/include/cantera/thermo/Phase.h index a82e69d7842..289b3faba54 100644 --- a/include/cantera/thermo/Phase.h +++ b/include/cantera/thermo/Phase.h @@ -29,6 +29,8 @@ namespace Cantera * support thermodynamic calculations (see \ref thermoprops). */ +class SolutionBase; + //! 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, //! mass density, species mass/mole fraction, and other generalized forces and @@ -758,6 +760,11 @@ class Phase m_caseSensitiveSpecies = cflag; } + //! Set root SolutionBase holding all phase information + virtual void setRoot(std::shared_ptr root) { + m_root = root; + } + protected: //! Cached for saved calculations within each ThermoPhase. /*! @@ -870,6 +877,9 @@ 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; }; } diff --git a/include/cantera/thermo/ThermoPhase.h b/include/cantera/thermo/ThermoPhase.h index 2b21ce6e1d4..a1b6da7a69d 100644 --- a/include/cantera/thermo/ThermoPhase.h +++ b/include/cantera/thermo/ThermoPhase.h @@ -6,7 +6,7 @@ */ // 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. +// at http://www.cantera.org/license.txt for license and copyright information. #ifndef CT_THERMOPHASE_H #define CT_THERMOPHASE_H @@ -40,8 +40,6 @@ const int cSS_CONVENTION_VPSS = 1; const int cSS_CONVENTION_SLAVE = 2; //@} -class SolutionBase; - //! Base class for a phase with thermodynamic properties. /*! * Class ThermoPhase is the base class for the family of classes that represent @@ -1618,11 +1616,6 @@ class ThermoPhase : public Phase //@} - //! Set root SolutionBase holding all phase information - virtual void setRoot(std::shared_ptr root) { - m_root = root; - } - protected: //! Fills `names` and `data` with the column names and species thermo //! properties to be included in the output of the reportCSV method. @@ -1667,9 +1660,6 @@ class ThermoPhase : public Phase //! last value of the temperature processed by reference state mutable doublereal m_tlast; - - //! reference to SolutionBase - std::weak_ptr m_root; }; //! typedef for the ThermoPhase class diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index ff576b20efd..fa93cc36e46 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -943,8 +943,8 @@ cdef class GasTransportData: cdef _assign(self, shared_ptr[CxxTransportData] other) cdef class _SolutionBase: - cdef CxxSolutionBase* base cdef shared_ptr[CxxSolutionBase] _base + cdef CxxSolutionBase* 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 4bb8dcb57ff..7a271593895 100644 --- a/interfaces/cython/cantera/base.pyx +++ b/interfaces/cython/cantera/base.pyx @@ -60,22 +60,28 @@ cdef class _SolutionBase: else: raise ValueError('Missing required keyword `base_type`.') - phasename = pystr(self.thermo.name()) + phase_name = pystr(self.thermo.id()) name = kwargs.get('name', None) if name is not None: - self.unique_name = name + 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[phasename] += 1 - n = _phase_counts[phasename] - self.unique_name = '{0}_{1}'.format(phasename, n) + _phase_counts[phase_name] = 0 + self.name = phase_name property type: """The type of the SolutionBase object.""" def __get__(self): return pystr(self.base.type()) - property unique_name: - """The unique name of the SolutionBase object.""" + property name: + """ + The name assigned to this SolutionBase object. The default is + taken from the CTI/XML/YAML input file. + """ def __get__(self): return pystr(self.base.name()) diff --git a/interfaces/cython/cantera/test/test_mixture.py b/interfaces/cython/cantera/test/test_mixture.py index 42f882bb404..b96107ee1f3 100644 --- a/interfaces/cython/cantera/test/test_mixture.py +++ b/interfaces/cython/cantera/test/test_mixture.py @@ -112,10 +112,11 @@ def test_charge(self): def test_phase_moles(self): M = self.mix.phase_moles() + name = self.phase2.name self.assertEqual(M[0], self.mix.phase_moles(0)) - self.assertEqual(M[1], self.mix.phase_moles('air')) + self.assertEqual(M[1], self.mix.phase_moles(name)) - self.mix.set_phase_moles('air', 4) + self.mix.set_phase_moles(name, 4) self.assertEqual(self.mix.phase_moles(1), 4) def test_species_moles(self): diff --git a/interfaces/cython/cantera/test/test_thermo.py b/interfaces/cython/cantera/test/test_thermo.py index 49ed49df609..e86752aa018 100644 --- a/interfaces/cython/cantera/test/test_thermo.py +++ b/interfaces/cython/cantera/test/test_thermo.py @@ -15,9 +15,9 @@ def setUp(self): def test_cpp_attributes(self): self.assertTrue(isinstance(self.phase.type, str)) self.assertTrue(self.phase.type=='Solution') - self.assertTrue(isinstance(self.phase.unique_name, str)) - self.phase.unique_name = 'spam' - self.assertTrue(self.phase.unique_name=='spam') + self.assertTrue(isinstance(self.phase.name, str)) + self.phase.name = 'spam' + self.assertTrue(self.phase.name=='spam') with self.assertRaises(AttributeError): self.phase.type = 'eggs' @@ -308,18 +308,14 @@ def test_default_report(self): self.assertNotIn(name, report) def test_name(self): - self.assertEqual(self.phase.name, 'ohmech') - self.phase.name = 'something' self.assertEqual(self.phase.name, 'something') self.assertIn('something', self.phase.report()) def test_ID(self): self.assertEqual(self.phase.ID, 'ohmech') - self.phase.ID = 'something' self.assertEqual(self.phase.ID, 'something') - self.assertEqual(self.phase.name, 'ohmech') def test_badLength(self): X = np.zeros(5) @@ -854,7 +850,7 @@ class ImportTest(utilities.CanteraTest): Test the various ways of creating a Solution object """ def check(self, gas, name, T, P, nSpec, nElem): - self.assertEqual(gas.name, name) + self.assertEqual(gas.ID, name) self.assertNear(gas.T, T) self.assertNear(gas.P, P) self.assertEqual(gas.n_species, nSpec) diff --git a/interfaces/cython/cantera/thermo.pyx b/interfaces/cython/cantera/thermo.pyx index 9ed8a479ea1..432cf1fc1be 100644 --- a/interfaces/cython/cantera/thermo.pyx +++ b/interfaces/cython/cantera/thermo.pyx @@ -284,16 +284,6 @@ cdef class ThermoPhase(_SolutionBase): def __call__(self, *args, **kwargs): print(self.report(*args, **kwargs)) - property name: - """ - The name assigned to this phase. The default is taken from the CTI/XML - input file. - """ - def __get__(self): - return pystr(self.thermo.name()) - def __set__(self, name): - self.thermo.setName(stringify(name)) - property ID: """ The ID of the phase. The default is taken from the CTI/XML input file. @@ -303,7 +293,6 @@ cdef class ThermoPhase(_SolutionBase): def __set__(self, id_): self.thermo.setID(stringify(id_)) - property basis: """ Determines whether intensive thermodynamic properties are treated on a diff --git a/src/thermo/Phase.cpp b/src/thermo/Phase.cpp index 85798d5ec82..fc21d66ead5 100644 --- a/src/thermo/Phase.cpp +++ b/src/thermo/Phase.cpp @@ -10,6 +10,7 @@ #include "cantera/base/utilities.h" #include "cantera/base/stringUtils.h" #include "cantera/base/ctml.h" +#include "cantera/base/Base.h" #include "cantera/thermo/ThermoFactory.h" using namespace std; @@ -78,12 +79,22 @@ void Phase::setID(const std::string& id_) std::string Phase::name() const { - return m_name; + if (m_root.expired()) { + return m_name; + } else { + auto root = m_root.lock(); + return root->name(); + } } void Phase::setName(const std::string& nm) { - m_name = nm; + if (m_root.expired()) { + m_name = nm; + } else { + auto root = m_root.lock(); + root->setName(nm); + } } size_t Phase::nElements() const diff --git a/src/thermo/ThermoFactory.cpp b/src/thermo/ThermoFactory.cpp index 5d4c96f3396..e5a5da4941a 100644 --- a/src/thermo/ThermoFactory.cpp +++ b/src/thermo/ThermoFactory.cpp @@ -448,6 +448,7 @@ void addSpecies(ThermoPhase& thermo, const AnyValue& names, const AnyValue& spec void setupPhase(ThermoPhase& thermo, AnyMap& phaseNode, const AnyMap& rootNode) { thermo.setName(phaseNode["name"].asString()); + thermo.setID(phaseNode["name"].asString()); if (rootNode.hasKey("__file__")) { phaseNode["__file__"] = rootNode["__file__"]; } From 11681321b730bbe4d3c99cf09e235f20aa9c86e9 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Fri, 16 Aug 2019 19:43:40 -0500 Subject: [PATCH 05/17] [Base] associate phase ID with SolutionBase object --- include/cantera/base/Base.h | 44 ++++++++++++++--------- interfaces/cython/cantera/_cantera.pxd | 6 ++-- interfaces/cython/cantera/base.pyx | 17 +++++++-- interfaces/cython/cantera/thermo.pyx | 9 ----- src/base/Base.cpp | 50 ++++++++++++++++++++++++-- src/thermo/Phase.cpp | 17 ++------- 6 files changed, 96 insertions(+), 47 deletions(-) diff --git a/include/cantera/base/Base.h b/include/cantera/base/Base.h index 9c084642285..a73c3511322 100644 --- a/include/cantera/base/Base.h +++ b/include/cantera/base/Base.h @@ -31,29 +31,42 @@ class SolutionBase : public std::enable_shared_from_this //! String indicating the type of the SolutionBase object. Corresponds //! to the type of phase originally instantiated - std::string type() const { - return m_type; - } + std::string type() const; //! Set the type of this SolutionBase object - void setType(const std::string& type){ - m_type = type; - } + void setType(const std::string& type); + + /*! Name and ID + * Class SolutionBase references two strings that identify a SolutionBase. + * The ID is the value of the ID attribute of the XML/YAML node that is used + * to initialize a phase when it is read. The name field is also initialized + * to the value of the ID attribute of 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 IDs of the two SolutionBases + * will be the same, but the names of the two SOlutionBases may be different. + * + * It is an error to have two phases in a single problem with the same name + * and ID (or the name from one phase being the same as the id of another + * SolutionBase). Thus, it is expected that there is a 1-1 correspondence + * between names and unique SolutionBase objects within a Cantera problem. + */ + + //! Return the string id for the SolutionBase. + std::string id() const; + + //! Set the string id for the SolutionBase. + void setID(const std::string& id); //! Return the name of this SolutionBase object - std::string name() const { - return m_name; - } + std::string name() const; //! Set the name of this SolutionBase object - void setName(const std::string& name){ - m_name = name; - } + void setName(const std::string& name); //! Generate self-documenting YAML string - virtual std::string toYAML() const { - throw NotImplementedError("SolutionBase::toYAML"); - } + virtual std::string toYAML() const; //! Set the ThermoPhase object void setThermoPhase(shared_ptr thermo); @@ -70,7 +83,6 @@ class SolutionBase : public std::enable_shared_from_this shared_ptr m_transport; //! Transport manager std::string m_type; //! type of SolutionBase object - std::string m_name; //! name of SolutionBase object }; } diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index fa93cc36e46..db4d7957e09 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -126,6 +126,8 @@ cdef extern from "cantera/base/Base.h" namespace "Cantera": CxxSolutionBase() string type() string setType(string) + string id() + void setID(string) string name() void setName(string) void setThermoPhase(shared_ptr[CxxThermoPhase]) @@ -142,10 +144,6 @@ cdef extern from "cantera/thermo/ThermoPhase.h" namespace "Cantera": # miscellaneous string type() string report(cbool, double) except +translate_exception - string name() - void setName(string) - string id() - void setID(string) double minTemp() except +translate_exception double maxTemp() except +translate_exception double refPressure() except +translate_exception diff --git a/interfaces/cython/cantera/base.pyx b/interfaces/cython/cantera/base.pyx index 7a271593895..de5ef9cd961 100644 --- a/interfaces/cython/cantera/base.pyx +++ b/interfaces/cython/cantera/base.pyx @@ -60,7 +60,7 @@ cdef class _SolutionBase: else: raise ValueError('Missing required keyword `base_type`.') - phase_name = pystr(self.thermo.id()) + phase_name = pystr(self.base.id()) name = kwargs.get('name', None) if name is not None: self.name = name @@ -73,10 +73,23 @@ cdef class _SolutionBase: self.name = phase_name property type: - """The type of the SolutionBase object.""" + """ + 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 ID: + """ + The ID of the SolutionBase object. The default is taken from the + CTI/XML/YAML input file. + """ + def __get__(self): + return pystr(self.base.id()) + def __set__(self, id_): + self.base.setID(stringify(id_)) + property name: """ The name assigned to this SolutionBase object. The default is diff --git a/interfaces/cython/cantera/thermo.pyx b/interfaces/cython/cantera/thermo.pyx index 432cf1fc1be..09466dc4e37 100644 --- a/interfaces/cython/cantera/thermo.pyx +++ b/interfaces/cython/cantera/thermo.pyx @@ -284,15 +284,6 @@ cdef class ThermoPhase(_SolutionBase): def __call__(self, *args, **kwargs): print(self.report(*args, **kwargs)) - property ID: - """ - The ID of the phase. The default is taken from the CTI/XML input file. - """ - def __get__(self): - return pystr(self.thermo.id()) - def __set__(self, id_): - self.thermo.setID(stringify(id_)) - property basis: """ Determines whether intensive thermodynamic properties are treated on a diff --git a/src/base/Base.cpp b/src/base/Base.cpp index 82fe246e08d..29043517e7d 100644 --- a/src/base/Base.cpp +++ b/src/base/Base.cpp @@ -15,8 +15,7 @@ SolutionBase::SolutionBase() : m_thermo(nullptr), m_kinetics(nullptr), m_transport(nullptr), - m_type(""), - m_name("") + m_type("") {} SolutionBase::SolutionBase(const std::string& infile, @@ -27,6 +26,53 @@ SolutionBase::SolutionBase(const std::string& infile, 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::id() const { + // currently managed by ThermoPhase + if (m_thermo) { + return m_thermo->id(); + } else { + throw CanteraError("SolutionBase::id()", "Missing ThermoPhase."); + } +} + +void SolutionBase::setID(const std::string& id) { + // currently managed by ThermoPhase + 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; diff --git a/src/thermo/Phase.cpp b/src/thermo/Phase.cpp index fc21d66ead5..c82b3e756b9 100644 --- a/src/thermo/Phase.cpp +++ b/src/thermo/Phase.cpp @@ -4,13 +4,12 @@ */ // 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. +// at http://www.cantera.org/license.txt for license and copyright information. #include "cantera/thermo/Phase.h" #include "cantera/base/utilities.h" #include "cantera/base/stringUtils.h" #include "cantera/base/ctml.h" -#include "cantera/base/Base.h" #include "cantera/thermo/ThermoFactory.h" using namespace std; @@ -79,22 +78,12 @@ void Phase::setID(const std::string& id_) std::string Phase::name() const { - if (m_root.expired()) { - return m_name; - } else { - auto root = m_root.lock(); - return root->name(); - } + return m_name; } void Phase::setName(const std::string& nm) { - if (m_root.expired()) { - m_name = nm; - } else { - auto root = m_root.lock(); - root->setName(nm); - } + m_name = nm; } size_t Phase::nElements() const From 04dced2a0a01afe2c4924c72c7fd0cff96657fb5 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 17 Aug 2019 08:16:57 -0500 Subject: [PATCH 06/17] [Base] break out thermo/kinetics manager types to Python --- interfaces/cython/cantera/base.pyx | 15 +++++++++++++++ interfaces/cython/cantera/kinetics.pyx | 7 +++++++ interfaces/cython/cantera/test/test_thermo.py | 11 ++++++++++- interfaces/cython/cantera/thermo.pyx | 7 +++++++ 4 files changed, 39 insertions(+), 1 deletion(-) diff --git a/interfaces/cython/cantera/base.pyx b/interfaces/cython/cantera/base.pyx index de5ef9cd961..7ef88c8d6bb 100644 --- a/interfaces/cython/cantera/base.pyx +++ b/interfaces/cython/cantera/base.pyx @@ -101,6 +101,21 @@ cdef class _SolutionBase: def __set__(self, name): self.base.setName(stringify(name)) + property composite: + """ + Returns tuple of thermo/kinetics/transport models associated with + this SolutionBase object. + """ + def __get__(self): + thermo = None if self.thermo == NULL \ + else pystr(self.thermo.type()) + kinetics = None if self.kinetics == NULL \ + else pystr(self.kinetics.kineticsType()) + transport = None if self.transport == NULL \ + else pystr(self.transport.transportType()) + + return thermo, kinetics, transport + def _init_yaml(self, infile, phaseid, phases, source): """ Instantiate a set of new Cantera C++ objects from a YAML diff --git a/interfaces/cython/cantera/kinetics.pyx b/interfaces/cython/cantera/kinetics.pyx index 89e7544c958..f3c71bb8c2e 100644 --- a/interfaces/cython/cantera/kinetics.pyx +++ b/interfaces/cython/cantera/kinetics.pyx @@ -31,6 +31,13 @@ cdef class Kinetics(_SolutionBase): kwargs['base_type'] = base_type super().__init__(*args, **kwargs) + property kinetics_model: + """ + Return type of kinetics. + """ + def __get__(self): + return pystr(self.kinetics.kineticsType()) + property n_total_species: """ Total number of species in all phases participating in the kinetics diff --git a/interfaces/cython/cantera/test/test_thermo.py b/interfaces/cython/cantera/test/test_thermo.py index e86752aa018..c9a1ef54084 100644 --- a/interfaces/cython/cantera/test/test_thermo.py +++ b/interfaces/cython/cantera/test/test_thermo.py @@ -12,10 +12,19 @@ class TestThermoPhase(utilities.CanteraTest): def setUp(self): self.phase = ct.Solution('h2o2.xml') - def test_cpp_attributes(self): + 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.phase.name = 'spam' self.assertTrue(self.phase.name=='spam') with self.assertRaises(AttributeError): diff --git a/interfaces/cython/cantera/thermo.pyx b/interfaces/cython/cantera/thermo.pyx index 09466dc4e37..7777a808113 100644 --- a/interfaces/cython/cantera/thermo.pyx +++ b/interfaces/cython/cantera/thermo.pyx @@ -270,6 +270,13 @@ cdef class ThermoPhase(_SolutionBase): self.thermo_basis = mass_basis self._references = weakref.WeakKeyDictionary() + property thermo_model: + """ + Return thermodynamic model describing phase. + """ + def __get__(self): + return pystr(self.thermo.type()) + def report(self, show_thermo=True, float threshold=1e-14): """ Generate a report describing the thermodynamic state of this phase. To From b643a63a9d60c02ecf9638154a6995dd31469638 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 17 Aug 2019 10:01:54 -0500 Subject: [PATCH 07/17] [Base] replace Solution property `ID` by `phase` in Python * Clarifies the meaning of ID * Creates a PEP8 compliant attribute that does not conflict with a built-in function name that is also consistent with the YAML entry. * Change associated member function names in C++ SolutionBase * Deprecate `ID` in Python (to be removed after Cantera 2.5) --- include/cantera/base/Base.h | 26 ++++++++--------- interfaces/cython/cantera/_cantera.pxd | 4 +-- interfaces/cython/cantera/base.pyx | 27 ++++++++++++++++-- .../cython/cantera/test/test_reactor.py | 4 +-- interfaces/cython/cantera/test/test_thermo.py | 28 +++++++++++++++---- src/base/Base.cpp | 6 ++-- 6 files changed, 67 insertions(+), 28 deletions(-) diff --git a/include/cantera/base/Base.h b/include/cantera/base/Base.h index a73c3511322..a22e1580ed3 100644 --- a/include/cantera/base/Base.h +++ b/include/cantera/base/Base.h @@ -36,28 +36,28 @@ class SolutionBase : public std::enable_shared_from_this //! Set the type of this SolutionBase object void setType(const std::string& type); - /*! Name and ID + /*! Name and phase * Class SolutionBase references two strings that identify a SolutionBase. - * The ID is the value of the ID attribute of the XML/YAML node that is used - * to initialize a phase when it is read. The name field is also initialized - * to the value of the ID attribute of the XML/YAML node. + * 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 IDs of the two SolutionBases - * will be the same, but the names of the two SOlutionBases may be different. + * 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 - * and ID (or the name from one phase being the same as the id of another - * SolutionBase). Thus, it is expected that there is a 1-1 correspondence - * between names and unique SolutionBase objects within a Cantera problem. + * attribute. Thus, it is expected that there is a 1-1 correspondence between + * names and unique SolutionBase objects within a Cantera problem. */ - //! Return the string id for the SolutionBase. - std::string id() const; + //! Return the phase string of this SolutionBase. + std::string phase() const; - //! Set the string id for the SolutionBase. - void setID(const std::string& id); + //! Set the phase string of this SolutionBase. + void setPhase(const std::string& id); //! Return the name of this SolutionBase object std::string name() const; diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index db4d7957e09..94f232b8373 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -126,8 +126,8 @@ cdef extern from "cantera/base/Base.h" namespace "Cantera": CxxSolutionBase() string type() string setType(string) - string id() - void setID(string) + string phase() + void setPhase(string) string name() void setName(string) void setThermoPhase(shared_ptr[CxxThermoPhase]) diff --git a/interfaces/cython/cantera/base.pyx b/interfaces/cython/cantera/base.pyx index 7ef88c8d6bb..a5be061bfb5 100644 --- a/interfaces/cython/cantera/base.pyx +++ b/interfaces/cython/cantera/base.pyx @@ -60,7 +60,7 @@ cdef class _SolutionBase: else: raise ValueError('Missing required keyword `base_type`.') - phase_name = pystr(self.base.id()) + phase_name = pystr(self.base.phase()) name = kwargs.get('name', None) if name is not None: self.name = name @@ -80,15 +80,36 @@ cdef class _SolutionBase: 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): - return pystr(self.base.id()) + warnings.warn("To be removed after Cantera 2.5. " + "Use `phase` attribute instead", DeprecationWarning) + return pystr(self.base.phase()) def __set__(self, id_): - self.base.setID(stringify(id_)) + warnings.warn("To be removed after Cantera 2.5. " + "Use `phase` attribute instead", DeprecationWarning) + self.base.setPhase(stringify(id_)) property name: """ diff --git a/interfaces/cython/cantera/test/test_reactor.py b/interfaces/cython/cantera/test/test_reactor.py index 9e788cb5bb6..7d084bc7d05 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.ID = 'gas' + self.gas1.phase = 'gas' self.gas2 = ct.Solution('gri30.xml') - self.gas2.ID = 'gas' + self.gas2.phase = '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 c9a1ef54084..cc57b712ee5 100644 --- a/interfaces/cython/cantera/test/test_thermo.py +++ b/interfaces/cython/cantera/test/test_thermo.py @@ -7,6 +7,8 @@ import cantera as ct from . import utilities +import warnings + class TestThermoPhase(utilities.CanteraTest): def setUp(self): @@ -321,10 +323,24 @@ def test_name(self): self.assertEqual(self.phase.name, 'something') self.assertIn('something', self.phase.report()) - def test_ID(self): - self.assertEqual(self.phase.ID, 'ohmech') - self.phase.ID = 'something' - self.assertEqual(self.phase.ID, 'something') + def test_phase(self): + self.assertEqual(self.phase.phase, 'ohmech') + warnings.simplefilter("always") + + with warnings.catch_warnings(record=True) as w: + self.assertEqual(self.phase.ID, 'ohmech') + self.assertTrue(len(w) == 1) + self.assertTrue(issubclass(w[-1].category, DeprecationWarning)) + self.assertTrue("To be removed after Cantera 2.5. " + in 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.assertTrue(issubclass(w[-1].category, DeprecationWarning)) + self.assertTrue("To be removed after Cantera 2.5. " + in str(w[-1].message)) def test_badLength(self): X = np.zeros(5) @@ -858,8 +874,8 @@ class ImportTest(utilities.CanteraTest): """ Test the various ways of creating a Solution object """ - def check(self, gas, name, T, P, nSpec, nElem): - self.assertEqual(gas.ID, name) + def check(self, gas, phase, T, P, nSpec, nElem): + self.assertEqual(gas.phase, phase) self.assertNear(gas.T, T) self.assertNear(gas.P, P) self.assertEqual(gas.n_species, nSpec) diff --git a/src/base/Base.cpp b/src/base/Base.cpp index 29043517e7d..258ee953e25 100644 --- a/src/base/Base.cpp +++ b/src/base/Base.cpp @@ -34,7 +34,7 @@ void SolutionBase::setType(const std::string& type){ m_type = type; } -std::string SolutionBase::id() const { +std::string SolutionBase::phase() const { // currently managed by ThermoPhase if (m_thermo) { return m_thermo->id(); @@ -43,8 +43,10 @@ std::string SolutionBase::id() const { } } -void SolutionBase::setID(const std::string& id) { +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 { From 93d9bd6350fb29d2255c554ba35a27cac2f5acf8 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 17 Aug 2019 11:54:57 -0500 Subject: [PATCH 08/17] [Base] clarify keyword arguments of _SolutionBase initializers * Replace `phaseid` by `phase` * Replace `phases` by `adjacent` * Add deprecation warnings and update unit tests --- interfaces/cython/cantera/base.pyx | 59 +++++++++++++------ .../cython/cantera/test/test_convert.py | 36 +++++------ .../cython/cantera/test/test_kinetics.py | 4 +- interfaces/cython/cantera/test/test_thermo.py | 7 +++ 4 files changed, 67 insertions(+), 39 deletions(-) diff --git a/interfaces/cython/cantera/base.pyx b/interfaces/cython/cantera/base.pyx index a5be061bfb5..fd08b7d7d2e 100644 --- a/interfaces/cython/cantera/base.pyx +++ b/interfaces/cython/cantera/base.pyx @@ -6,9 +6,29 @@ from collections import defaultdict as _defaultdict _phase_counts = _defaultdict(int) cdef class _SolutionBase: - def __cinit__(self, infile='', phaseid='', phases=(), origin=None, + def __cinit__(self, infile='', phase='', adjacent=(), origin=None, source=None, yaml=None, thermo=None, species=(), kinetics=None, reactions=(), **kwargs): + + if 'phaseid' in kwargs: + if phase is not '': + raise AttributeError('duplicate specification of phase name') + + warnings.warn("To be removed after Cantera 2.5. " + "Keyword `phaseid` is replaced by `phase`", + DeprecationWarning) + phase = kwargs['phaseid'] + + if 'phases' in kwargs: + if len(adjacent)>0: + raise AttributeError( + 'duplicate specification of adjacent phases') + + warnings.warn("To be removed after Cantera 2.5. " + "Keyword `phases` is replaced by `adjacent`", + DeprecationWarning) + adjacent = kwargs['phases'] + # Shallow copy of an existing Solution (for slicing support) cdef _SolutionBase other if origin is not None: @@ -36,11 +56,11 @@ cdef class _SolutionBase: # Parse inputs if infile.endswith('.yml') or infile.endswith('.yaml') or yaml: - self._init_yaml(infile, phaseid, phases, yaml) + self._init_yaml(infile, phase, adjacent, yaml) elif infile or source: - self._init_cti_xml(infile, phaseid, phases, source) + self._init_cti_xml(infile, phase, adjacent, source) elif thermo and species: - self._init_parts(thermo, species, kinetics, phases, reactions) + self._init_parts(thermo, species, kinetics, adjacent, reactions) else: raise ValueError("Arguments are insufficient to define a phase") @@ -137,7 +157,7 @@ cdef class _SolutionBase: return thermo, kinetics, transport - def _init_yaml(self, infile, phaseid, phases, source): + def _init_yaml(self, infile, phase, adjacent, source): """ Instantiate a set of new Cantera C++ objects from a YAML phase definition @@ -149,7 +169,7 @@ cdef class _SolutionBase: root = AnyMapFromYamlString(stringify(source)) phaseNode = root[stringify("phases")].getMapWhere(stringify("name"), - stringify(phaseid)) + stringify(phase)) # Thermo if isinstance(self, ThermoPhase): @@ -160,19 +180,19 @@ cdef class _SolutionBase: # Kinetics cdef vector[CxxThermoPhase*] v - cdef _SolutionBase phase + cdef _SolutionBase adj if isinstance(self, Kinetics): v.push_back(self.thermo) - for phase in phases: + for adj in adjacent: # adjacent bulk phases for a surface phase - v.push_back(phase.thermo) + v.push_back(adj.thermo) self._kinetics = newKinetics(v, phaseNode, root) self.kinetics = self._kinetics.get() else: self.kinetics = NULL - def _init_cti_xml(self, infile, phaseid, phases, source): + def _init_cti_xml(self, infile, phase, adjacent, source): """ Instantiate a set of new Cantera C++ objects from a CTI or XML phase definition @@ -184,8 +204,8 @@ cdef class _SolutionBase: # Get XML data cdef XML_Node* phaseNode - if phaseid: - phaseNode = rootNode.findID(stringify(phaseid)) + if phase: + phaseNode = rootNode.findID(stringify(phase)) else: phaseNode = rootNode.findByName(stringify('phase')) if phaseNode is NULL: @@ -200,19 +220,19 @@ cdef class _SolutionBase: # Kinetics cdef vector[CxxThermoPhase*] v - cdef _SolutionBase phase + cdef _SolutionBase adj if isinstance(self, Kinetics): v.push_back(self.thermo) - for phase in phases: + for adj in adjacent: # adjacent bulk phases for a surface phase - v.push_back(phase.thermo) + v.push_back(adj.thermo) self.kinetics = newKineticsMgr(deref(phaseNode), v) self._kinetics.reset(self.kinetics) else: self.kinetics = NULL - def _init_parts(self, thermo, species, kinetics, phases, reactions): + def _init_parts(self, thermo, species, kinetics, adjacent, reactions): """ Instantiate a set of new Cantera C++ objects based on a string defining the model type and a list of Species objects. @@ -228,14 +248,15 @@ cdef class _SolutionBase: if not kinetics: kinetics = "none" - cdef ThermoPhase phase + cdef ThermoPhase adj cdef Reaction reaction if isinstance(self, Kinetics): self.kinetics = CxxNewKinetics(stringify(kinetics)) self._kinetics.reset(self.kinetics) self.kinetics.addPhase(deref(self.thermo)) - for phase in phases: - self.kinetics.addPhase(deref(phase.thermo)) + for adj in adjacent: + # adjacent bulk phases for a surface phase + self.kinetics.addPhase(deref(adj.thermo)) self.kinetics.init() self.kinetics.skipUndeclaredThirdBodies(True) for reaction in reactions: diff --git a/interfaces/cython/cantera/test/test_convert.py b/interfaces/cython/cantera/test/test_convert.py index 1d464023232..726f547c2b2 100644 --- a/interfaces/cython/cantera/test/test_convert.py +++ b/interfaces/cython/cantera/test/test_convert.py @@ -571,8 +571,8 @@ def setUpClass(cls): def checkConversion(self, basename, cls=ct.Solution, ctiphases=(), yamlphases=(), **kwargs): - ctiPhase = cls(basename + '.cti', phases=ctiphases, **kwargs) - yamlPhase = cls(basename + '.yaml', phases=yamlphases, **kwargs) + ctiPhase = cls(basename + '.cti', adjacent=ctiphases, **kwargs) + yamlPhase = cls(basename + '.yaml', adjacent=yamlphases, **kwargs) self.assertEqual(ctiPhase.element_names, yamlPhase.element_names) self.assertEqual(ctiPhase.species_names, yamlPhase.species_names) @@ -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, - phaseid='Pt_surf', ctiphases=[ctiGas], yamlphases=[yamlGas]) + phase='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', phaseid='metal') - ctiOxide, yamlOxide = self.checkConversion('sofc', phaseid='oxide_bulk') + ctiMetal, yamlMetal = self.checkConversion('sofc', phase='metal') + ctiOxide, yamlOxide = self.checkConversion('sofc', phase='oxide_bulk') ctiMSurf, yamlMSurf = self.checkConversion('sofc', ct.Interface, - phaseid='metal_surface', ctiphases=[ctiGas, ctiMetal], + phase='metal_surface', ctiphases=[ctiGas, ctiMetal], yamlphases=[yamlGas, yamlMetal]) ctiOSurf, yamlOSurf = self.checkConversion('sofc', ct.Interface, - phaseid='oxide_surface', ctiphases=[ctiGas, ctiOxide], + phase='oxide_surface', ctiphases=[ctiGas, ctiOxide], yamlphases=[yamlGas, yamlOxide]) cti_tpb, yaml_tpb = self.checkConversion('sofc', ct.Interface, - phaseid='tpb', ctiphases=[ctiMetal, ctiMSurf, ctiOSurf], + phase='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', phaseid=name) + ctiPhase, yamlPhase = self.checkConversion('liquidvapor', phase=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', phaseid='gas') - ctiSolid, yamlSolid = self.checkConversion('diamond', phaseid='diamond') + ctiGas, yamlGas = self.checkConversion('diamond', phase='gas') + ctiSolid, yamlSolid = self.checkConversion('diamond', phase='diamond') ctiSurf, yamlSurf = self.checkConversion('diamond', - ct.Interface, phaseid='diamond_100', ctiphases=[ctiGas, ctiSolid], + ct.Interface, phase='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, phaseid='anode') - ctiCathode, yamlCathode = self.checkConversion(name, phaseid='cathode') - ctiMetal, yamlMetal = self.checkConversion(name, phaseid='electron') - ctiElyt, yamlElyt = self.checkConversion(name, phaseid='electrolyte') + 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') ctiAnodeInt, yamlAnodeInt = self.checkConversion(name, - phaseid='edge_anode_electrolyte', + phase='edge_anode_electrolyte', ctiphases=[ctiAnode, ctiMetal, ctiElyt], yamlphases=[yamlAnode, yamlMetal, yamlElyt]) ctiCathodeInt, yamlCathodeInt = self.checkConversion(name, - phaseid='edge_cathode_electrolyte', + phase='edge_cathode_electrolyte', ctiphases=[ctiCathode, ctiMetal, ctiElyt], yamlphases=[yamlCathode, yamlMetal, yamlElyt]) diff --git a/interfaces/cython/cantera/test/test_kinetics.py b/interfaces/cython/cantera/test/test_kinetics.py index 46385664584..58d0637eda3 100644 --- a/interfaces/cython/cantera/test/test_kinetics.py +++ b/interfaces/cython/cantera/test/test_kinetics.py @@ -185,7 +185,7 @@ def test_surface(self): surf2 = ct.Interface(thermo='Surface', kinetics='interface', species=surf_species, reactions=reactions, - phases=[gas]) + adjacent=[gas]) surf1.site_density = surf2.site_density = 5e-9 gas.TP = surf2.TP = surf1.TP = 900, 2*ct.one_atm surf2.concentrations = surf1.concentrations @@ -1032,7 +1032,7 @@ def test_interface(self): self.assertNear(r1.coverage_deps['H(S)'][2], -6e6) surf2 = ct.Interface(thermo='Surface', species=surf_species, - kinetics='interface', reactions=[r1], phases=[gas]) + kinetics='interface', reactions=[r1], adjacent=[gas]) surf2.site_density = surf1.site_density surf1.coverages = surf2.coverages = 'PT(S):0.7, H(S):0.3' diff --git a/interfaces/cython/cantera/test/test_thermo.py b/interfaces/cython/cantera/test/test_thermo.py index cc57b712ee5..a9479cfa5ed 100644 --- a/interfaces/cython/cantera/test/test_thermo.py +++ b/interfaces/cython/cantera/test/test_thermo.py @@ -342,6 +342,13 @@ def test_phase(self): self.assertTrue("To be removed after Cantera 2.5. " in 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.assertTrue(issubclass(w[-1].category, DeprecationWarning)) + self.assertTrue("To be removed after Cantera 2.5. " + in str(w[-1].message)) + def test_badLength(self): X = np.zeros(5) with self.assertRaisesRegex(ValueError, 'incorrect length'): From ee484eef53fae1348a14d7705429fbc4afccaa3c Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sat, 17 Aug 2019 12:52:28 -0500 Subject: [PATCH 09/17] [Base] update docstrings for Solution and Interface objects * Reflects changes to `phase` and `adjacent` keyword --- interfaces/cython/cantera/composite.py | 30 +++++++++++++++++--------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/interfaces/cython/cantera/composite.py b/interfaces/cython/cantera/composite.py index 22f5dee6d30..917418c55f7 100644 --- a/interfaces/cython/cantera/composite.py +++ b/interfaces/cython/cantera/composite.py @@ -25,11 +25,20 @@ class Solution(ThermoPhase, Kinetics, Transport): gas = ct.Solution('gri30.cti') - If an input file defines multiple phases, the phase *name* (in CTI) or *id* - (in XML) can be used to specify the desired phase:: + If an input file defines multiple phases, the phase *name* (in CTI), *id* + (in XML) or *phases* entry (in YAML) can be used to specify the desired + phase via the ``phase`` keyword argument of the constructor:: - gas = ct.Solution('diamond.cti', 'gas') - diamond = ct.Solution('diamond.cti', 'diamond') + gas = ct.Solution('diamond.cti', phase='gas') + diamond = ct.Solution('diamond.cti', phase='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.:: + + gas = ct.Solution('gri30.cti', 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 @@ -57,7 +66,8 @@ class Solution(ThermoPhase, Kinetics, Transport): ideal_gas(name='gas', elements='O H Ar', species='gri30: all', reactions='gri30: all', - options=['skip_undeclared_elements', 'skip_undeclared_species', 'skip_undeclared_third_bodies'], + options=['skip_undeclared_elements', 'skip_undeclared_species', + 'skip_undeclared_third_bodies'], initial_state=state(temperature=300, pressure=101325))''' gas = ct.Solution(source=cti_def) """ @@ -77,11 +87,12 @@ class Interface(InterfacePhase, InterfaceKinetics): To construct an `Interface` object, adjacent bulk phases which participate in reactions need to be created and then passed in as a list in the - ``phases`` argument to the constructor:: + ``adjacent`` argument to the constructor:: - gas = ct.Solution('diamond.cti', 'gas') - diamond = ct.Solution('diamond.cti', 'diamond') - diamond_surf = ct.Interface('diamond.cti', 'diamond_100', [gas, diamond]) + gas = ct.Solution('diamond.cti', phase='gas') + diamond = ct.Solution('diamond.cti', phase='diamond') + diamond_surf = ct.Interface('diamond.cti', phase='diamond_100', + adjacent=[gas, diamond]) """ __slots__ = ('_phase_indices',) @@ -95,7 +106,6 @@ class DustyGas(ThermoPhase, Kinetics, DustyGasTransport): The only transport properties computed are the multicomponent diffusion coefficients. The model does not compute viscosity or thermal conductivity. - """ __slots__ = () From ec6f5e021a728db0d50102a7147fc027a5c19e04 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 18 Aug 2019 05:43:19 -0500 Subject: [PATCH 10/17] [Base] use FutureWarning for deprecated keywords --- interfaces/cython/cantera/base.pyx | 10 ++++---- interfaces/cython/cantera/composite.py | 24 +++++++++---------- interfaces/cython/cantera/test/test_thermo.py | 4 ++-- 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/interfaces/cython/cantera/base.pyx b/interfaces/cython/cantera/base.pyx index fd08b7d7d2e..290898bcca5 100644 --- a/interfaces/cython/cantera/base.pyx +++ b/interfaces/cython/cantera/base.pyx @@ -14,9 +14,8 @@ cdef class _SolutionBase: if phase is not '': raise AttributeError('duplicate specification of phase name') - warnings.warn("To be removed after Cantera 2.5. " - "Keyword `phaseid` is replaced by `phase`", - DeprecationWarning) + warnings.warn("Keyword `phase` replaces `phaseid`", + FutureWarning) phase = kwargs['phaseid'] if 'phases' in kwargs: @@ -24,9 +23,8 @@ cdef class _SolutionBase: raise AttributeError( 'duplicate specification of adjacent phases') - warnings.warn("To be removed after Cantera 2.5. " - "Keyword `phases` is replaced by `adjacent`", - DeprecationWarning) + warnings.warn("Keyword `adjacent` replaces `phases`", + FutureWarning) adjacent = kwargs['phases'] # Shallow copy of an existing Solution (for slicing support) diff --git a/interfaces/cython/cantera/composite.py b/interfaces/cython/cantera/composite.py index 917418c55f7..77fb69f555a 100644 --- a/interfaces/cython/cantera/composite.py +++ b/interfaces/cython/cantera/composite.py @@ -23,14 +23,14 @@ class Solution(ThermoPhase, Kinetics, Transport): The most common way to instantiate `Solution` objects is by using a phase definition, species and reactions defined in an input file:: - gas = ct.Solution('gri30.cti') + gas = ct.Solution('gri30.yaml') - If an input file defines multiple phases, the phase *name* (in CTI), *id* - (in XML) or *phases* entry (in YAML) can be used to specify the desired + 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:: - gas = ct.Solution('diamond.cti', phase='gas') - diamond = ct.Solution('diamond.cti', phase='diamond') + gas = ct.Solution('diamond.yaml', phase='gas') + diamond = ct.Solution('diamond.yaml', phase='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 @@ -38,14 +38,14 @@ class Solution(ThermoPhase, Kinetics, Transport): by a suffix. A custom name can be set via the ``name`` keyword argument of the constructor, i.e.:: - gas = ct.Solution('gri30.cti', name='my_custom_name') + gas = ct.Solution('gri30.yaml', 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 directly in Python:: - spec = ct.Species.listFromFile('gri30.cti') - rxns = ct.Reaction.listFromFile('gri30.cti') + spec = ct.Species.listFromFile('gri30.yaml') + rxns = ct.Reaction.listFromFile('gri30.yaml') gas = ct.Solution(thermo='IdealGas', kinetics='GasKinetics', species=spec, reactions=rxns) @@ -89,9 +89,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.cti', phase='gas') - diamond = ct.Solution('diamond.cti', phase='diamond') - diamond_surf = ct.Interface('diamond.cti', phase='diamond_100', + gas = ct.Solution('diamond.yaml', phase='gas') + diamond = ct.Solution('diamond.yaml', phase='diamond') + diamond_surf = ct.Interface('diamond.yaml', phase='diamond_100', adjacent=[gas, diamond]) """ __slots__ = ('_phase_indices',) @@ -316,7 +316,7 @@ class SolutionArray: with shapes described in the same way as Numpy arrays. All of the states can be set in a single call:: - >>> gas = ct.Solution('gri30.cti') + >>> gas = ct.Solution('gri30.yaml') >>> states = ct.SolutionArray(gas, (6, 10)) >>> T = np.linspace(300, 1000, 10) # row vector >>> P = ct.one_atm * np.linspace(0.1, 5.0, 6)[:,np.newaxis] # column vector diff --git a/interfaces/cython/cantera/test/test_thermo.py b/interfaces/cython/cantera/test/test_thermo.py index a9479cfa5ed..5c3e783e194 100644 --- a/interfaces/cython/cantera/test/test_thermo.py +++ b/interfaces/cython/cantera/test/test_thermo.py @@ -345,8 +345,8 @@ def test_phase(self): with warnings.catch_warnings(record=True) as w: gas = ct.Solution('h2o2.cti', phaseid='ohmech') self.assertTrue(len(w) == 1) - self.assertTrue(issubclass(w[-1].category, DeprecationWarning)) - self.assertTrue("To be removed after Cantera 2.5. " + self.assertTrue(issubclass(w[-1].category, FutureWarning)) + self.assertTrue("Keyword `phase` replaces `phaseid`" in str(w[-1].message)) def test_badLength(self): From cf29e6b27be0c227bf209951d911db796af38889 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 29 Sep 2019 08:43:54 -0500 Subject: [PATCH 11/17] [Base] rename Base.h/.cpp to SolutionBase.h/.cpp --- .../cantera/base/{Base.h => SolutionBase.h} | 21 ++++++++++++++++--- include/cantera/thermo/ThermoPhase.h | 2 +- interfaces/cython/cantera/_cantera.pxd | 2 +- src/base/{Base.cpp => SolutionBase.cpp} | 4 ++-- src/thermo/Phase.cpp | 2 +- 5 files changed, 23 insertions(+), 8 deletions(-) rename include/cantera/base/{Base.h => SolutionBase.h} (87%) rename src/base/{Base.cpp => SolutionBase.cpp} (97%) diff --git a/include/cantera/base/Base.h b/include/cantera/base/SolutionBase.h similarity index 87% rename from include/cantera/base/Base.h rename to include/cantera/base/SolutionBase.h index a22e1580ed3..ea5ee8bb217 100644 --- a/include/cantera/base/Base.h +++ b/include/cantera/base/SolutionBase.h @@ -1,10 +1,10 @@ -//! @file Base.h +//! @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_BASE_H -#define CT_BASE_H +#ifndef CT_SOLUTIONBASE_H +#define CT_SOLUTIONBASE_H #include "cantera/base/ctexceptions.h" @@ -77,6 +77,21 @@ class SolutionBase : public std::enable_shared_from_this //! Set the Transport object void setTransport(shared_ptr transport); + //! Accessor for the ThermoPhase object + shared_ptr thermo() { + return m_thermo; + } + + //! Accessor for the Kinetics object + shared_ptr kinetics() { + return m_kinetics; + } + + //! Accessor for the Transport object + shared_ptr transport() { + return m_transport; + } + protected: shared_ptr m_thermo; //! ThermoPhase manager shared_ptr m_kinetics; //! Kinetics manager diff --git a/include/cantera/thermo/ThermoPhase.h b/include/cantera/thermo/ThermoPhase.h index a1b6da7a69d..fdac8cf29c9 100644 --- a/include/cantera/thermo/ThermoPhase.h +++ b/include/cantera/thermo/ThermoPhase.h @@ -6,7 +6,7 @@ */ // This file is part of Cantera. See License.txt in the top-level directory or -// at http://www.cantera.org/license.txt for license and copyright information. +// at https://cantera.org/license.txt for license and copyright information. #ifndef CT_THERMOPHASE_H #define CT_THERMOPHASE_H diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 94f232b8373..7c4a4c253b8 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -121,7 +121,7 @@ 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/Base.h" namespace "Cantera": +cdef extern from "cantera/base/SolutionBase.h" namespace "Cantera": cdef cppclass CxxSolutionBase "Cantera::SolutionBase": CxxSolutionBase() string type() diff --git a/src/base/Base.cpp b/src/base/SolutionBase.cpp similarity index 97% rename from src/base/Base.cpp rename to src/base/SolutionBase.cpp index 258ee953e25..dd9e8db6cca 100644 --- a/src/base/Base.cpp +++ b/src/base/SolutionBase.cpp @@ -1,9 +1,9 @@ -//! @file Base.cpp +//! @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/Base.h" +#include "cantera/base/SolutionBase.h" #include "cantera/thermo/ThermoPhase.h" #include "cantera/kinetics/Kinetics.h" #include "cantera/transport/TransportBase.h" diff --git a/src/thermo/Phase.cpp b/src/thermo/Phase.cpp index c82b3e756b9..85798d5ec82 100644 --- a/src/thermo/Phase.cpp +++ b/src/thermo/Phase.cpp @@ -4,7 +4,7 @@ */ // This file is part of Cantera. See License.txt in the top-level directory or -// at http://www.cantera.org/license.txt for license and copyright information. +// at https://cantera.org/license.txt for license and copyright information. #include "cantera/thermo/Phase.h" #include "cantera/base/utilities.h" From 5932a2403de5e14e8150105019fb0ff74eb11db6 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Tue, 1 Oct 2019 13:49:03 -0500 Subject: [PATCH 12/17] [Input] rename --id to --phase in ck2yaml options The option --phase is consistent with the resulting yaml entry in 'phases'. The --id option is still supported, with a warning being issued. --- include/cantera/base/SolutionBase.h | 12 ++++++------ interfaces/cython/cantera/ck2yaml.py | 19 +++++++++++++------ interfaces/cython/cantera/composite.py | 14 ++++++++++---- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/include/cantera/base/SolutionBase.h b/include/cantera/base/SolutionBase.h index ea5ee8bb217..cec66ffb135 100644 --- a/include/cantera/base/SolutionBase.h +++ b/include/cantera/base/SolutionBase.h @@ -78,18 +78,18 @@ class SolutionBase : public std::enable_shared_from_this void setTransport(shared_ptr transport); //! Accessor for the ThermoPhase object - shared_ptr thermo() { - return m_thermo; + ThermoPhase& thermo() { + return *m_thermo; } //! Accessor for the Kinetics object - shared_ptr kinetics() { - return m_kinetics; + Kinetics& kinetics() { + return *m_kinetics; } //! Accessor for the Transport object - shared_ptr transport() { - return m_transport; + Transport& transport() { + return *m_transport; } protected: diff --git a/interfaces/cython/cantera/ck2yaml.py b/interfaces/cython/cantera/ck2yaml.py index 43bef62a1f3..c935822fbce 100644 --- a/interfaces/cython/cantera/ck2yaml.py +++ b/interfaces/cython/cantera/ck2yaml.py @@ -12,7 +12,7 @@ [--thermo=] [--transport=] [--surface=] - [--id=] + [--phase=] [--output=] [--permissive] [-d | --debug] @@ -32,7 +32,8 @@ 'surface'. The '--permissive' option allows certain recoverable parsing errors (e.g. -duplicate transport data) to be ignored. +duplicate transport data) to be ignored. The '--phase=' option +is used to override default phase names (i.e. 'gas'). """ from collections import defaultdict, OrderedDict @@ -1797,7 +1798,7 @@ def parse_transport_data(self, lines, filename, line_offset): if speciesName in self.species_dict: if len(data) != 7: raise InputError('Unable to parse line {} of {}:\n"""\n{}"""\n' - '6 transport parameters expected, but found {}.', + '6 transport parameters expected, but found {}.', line_offset + i, filename, original_line, len(data)-1) if self.species_dict[speciesName].transport is None: @@ -2024,9 +2025,9 @@ def convert_mech(input_file, thermo_file=None, transport_file=None, surface_file def main(argv): - longOptions = ['input=', 'thermo=', 'transport=', 'surface=', 'id=', + longOptions = ['input=', 'thermo=', 'transport=', 'surface=', 'phase=', 'output=', 'permissive', 'help', 'debug', 'quiet', - 'no-validate'] + 'no-validate', 'id='] try: optlist, args = getopt.getopt(argv, 'dh', longOptions) @@ -2054,7 +2055,13 @@ def main(argv): quiet = '--quiet' in options transport_file = options.get('--transport') surface_file = options.get('--surface') - phase_name = options.get('--id', 'gas') + + if '--id' in options: + phase_name = options.get('--id', 'gas') + logging.warning("\nFutureWarning: " + "option '--id=...' is superseded by '--phase=...'") + else: + phase_name = options.get('--phase', '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 77fb69f555a..9cc5898e3c7 100644 --- a/interfaces/cython/cantera/composite.py +++ b/interfaces/cython/cantera/composite.py @@ -32,9 +32,9 @@ class Solution(ThermoPhase, Kinetics, Transport): gas = ct.Solution('diamond.yaml', phase='gas') diamond = ct.Solution('diamond.yaml', phase='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 + 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.:: @@ -54,6 +54,12 @@ class Solution(ThermoPhase, Kinetics, Transport): ``species`` and ``reactions`` keyword arguments are lists of `Species` and `Reaction` objects, respectively. + Types of underlying models that form the composite `Solution` object are + queried using the ``thermo_model``, ``kinetics_model`` and + ``transport_model`` attributes; further, the ``composite`` attribute is a + shorthand returning a tuple containing the types of the three contitutive + models. + For non-trivial uses cases of this functionality, see the examples `extract_submechanism.py `_ and `mechanism_reduction.py `_. @@ -66,7 +72,7 @@ class Solution(ThermoPhase, Kinetics, Transport): ideal_gas(name='gas', elements='O H Ar', species='gri30: all', reactions='gri30: all', - options=['skip_undeclared_elements', 'skip_undeclared_species', + options=['skip_undeclared_elements', 'skip_undeclared_species', 'skip_undeclared_third_bodies'], initial_state=state(temperature=300, pressure=101325))''' gas = ct.Solution(source=cti_def) From 02175a3b17bc92b155976215c5132ac45e77abdd Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Tue, 8 Oct 2019 15:00:07 -0500 Subject: [PATCH 13/17] [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 00000000000..37141cc4dc9 --- /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 cec66ffb135..00000000000 --- 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 4cacaa8e49e..57b2853954f 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 289b3faba54..0dc75d7f4c8 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 171aff9f396..664d47d2a4d 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 7c4a4c253b8..827b4acf240 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 290898bcca5..76db56e5153 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 c935822fbce..1ffe5a3c729 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 9cc5898e3c7..fb9ae6b074a 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 f3c71bb8c2e..b16c9690af0 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 726f547c2b2..d101ba38e64 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 7d084bc7d05..c8425d99047 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 5c3e783e194..c621c491bf4 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 7777a808113..e71b12ed6dd 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 00000000000..51099a18681 --- /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 dd9e8db6cca..00000000000 --- 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 85798d5ec82..202f33b43c2 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 From ce707142a17f8cd65e879578cc56af3b1c8cbf97 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Wed, 9 Oct 2019 19:01:43 -0500 Subject: [PATCH 14/17] [Thermo] replace 'phase_id' by 'name' * 'name' corresponds to the YAML entry * rename Solution keyword 'phaseid' to 'name' (instead of 'phase_id') * rename ck2yaml argument '--id' to '--name' (instead of '--phase-id') * ensure that C++ Phase::m_id is always the same as Phase::m_name --- include/cantera/base/Solution.h | 2 -- interfaces/cython/cantera/_cantera.pxd | 2 -- interfaces/cython/cantera/base.pyx | 36 ++++++++----------- interfaces/cython/cantera/ck2yaml.py | 10 +++--- interfaces/cython/cantera/composite.py | 23 ++++++------ interfaces/cython/cantera/kinetics.pyx | 4 +-- .../cython/cantera/test/test_convert.py | 32 ++++++++--------- .../cython/cantera/test/test_reactor.py | 4 +-- interfaces/cython/cantera/test/test_thermo.py | 8 ++--- interfaces/cython/cantera/thermo.pyx | 24 ++++--------- src/base/Solution.cpp | 28 ++++++++++----- src/thermo/Phase.cpp | 17 +++------ 12 files changed, 83 insertions(+), 107 deletions(-) diff --git a/include/cantera/base/Solution.h b/include/cantera/base/Solution.h index 37141cc4dc9..50c8979e338 100644 --- a/include/cantera/base/Solution.h +++ b/include/cantera/base/Solution.h @@ -64,8 +64,6 @@ class Solution : public std::enable_shared_from_this 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 }; } diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 827b4acf240..02a1fb1213e 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -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 diff --git a/interfaces/cython/cantera/base.pyx b/interfaces/cython/cantera/base.pyx index 76db56e5153..3609ba25a77 100644 --- a/interfaces/cython/cantera/base.pyx +++ b/interfaces/cython/cantera/base.pyx @@ -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: @@ -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: @@ -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()) @@ -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 @@ -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): @@ -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 @@ -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: diff --git a/interfaces/cython/cantera/ck2yaml.py b/interfaces/cython/cantera/ck2yaml.py index 1ffe5a3c729..39df856bfdc 100644 --- a/interfaces/cython/cantera/ck2yaml.py +++ b/interfaces/cython/cantera/ck2yaml.py @@ -12,7 +12,7 @@ [--thermo=] [--transport=] [--surface=] - [--phase-id=] + [--name=] [--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-id=' option +duplicate transport data) to be ignored. The '--name=' 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-id=', + longOptions = ['input=', 'thermo=', 'transport=', 'surface=', 'name=', '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-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=..."' diff --git a/interfaces/cython/cantera/composite.py b/interfaces/cython/cantera/composite.py index fb9ae6b074a..ee5421be9c7 100644 --- a/interfaces/cython/cantera/composite.py +++ b/interfaces/cython/cantera/composite.py @@ -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:: - 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 @@ -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 @@ -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',) diff --git a/interfaces/cython/cantera/kinetics.pyx b/interfaces/cython/cantera/kinetics.pyx index b16c9690af0..05ddf9fdffe 100644 --- a/interfaces/cython/cantera/kinetics.pyx +++ b/interfaces/cython/cantera/kinetics.pyx @@ -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.") diff --git a/interfaces/cython/cantera/test/test_convert.py b/interfaces/cython/cantera/test/test_convert.py index d101ba38e64..0d5c7707e3c 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_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]) @@ -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]) @@ -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]) @@ -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]) @@ -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]) diff --git a/interfaces/cython/cantera/test/test_reactor.py b/interfaces/cython/cantera/test/test_reactor.py index c8425d99047..71cc77634eb 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_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') diff --git a/interfaces/cython/cantera/test/test_thermo.py b/interfaces/cython/cantera/test/test_thermo.py index c621c491bf4..ff20e3cdc83 100644 --- a/interfaces/cython/cantera/test/test_thermo.py +++ b/interfaces/cython/cantera/test/test_thermo.py @@ -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: @@ -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. ", @@ -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): @@ -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) diff --git a/interfaces/cython/cantera/thermo.pyx b/interfaces/cython/cantera/thermo.pyx index e71b12ed6dd..3d6260dcff0 100644 --- a/interfaces/cython/cantera/thermo.pyx +++ b/interfaces/cython/cantera/thermo.pyx @@ -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: """ diff --git a/src/base/Solution.cpp b/src/base/Solution.cpp index 51099a18681..85e0f1877c9 100644 --- a/src/base/Solution.cpp +++ b/src/base/Solution.cpp @@ -1,4 +1,7 @@ -//! @file Solution.cpp +/** + * @file Solution.cpp + * Definition file for class Solution. + */ // 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. @@ -11,16 +14,24 @@ namespace Cantera { -Solution::Solution() : - m_name("") -{} +Solution::Solution() {} std::string Solution::name() const { - return m_name; + if (m_thermo) { + return m_thermo->name(); + } else { + throw CanteraError("Solution::name()", + "Requires associated 'ThermoPhase'"); + } } -void Solution::setName(const std::string& name){ - m_name = name; +void Solution::setName(const std::string& name) { + if (m_thermo) { + m_thermo->setName(name); + } else { + throw CanteraError("Solution::setName()", + "Requires associated 'ThermoPhase'"); + } } void Solution::setThermoPhase(shared_ptr thermo) { @@ -43,4 +54,5 @@ void Solution::setTransport(shared_ptr transport) { m_transport->setRoot(shared_from_this()); } } -} + +} // namespace Cantera diff --git a/src/thermo/Phase.cpp b/src/thermo/Phase.cpp index 202f33b43c2..f1ef31a9a44 100644 --- a/src/thermo/Phase.cpp +++ b/src/thermo/Phase.cpp @@ -9,7 +9,6 @@ #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" @@ -75,26 +74,18 @@ std::string Phase::id() const void Phase::setID(const std::string& id_) { m_id = id_; + m_name = id_; } std::string Phase::name() const { - auto root = m_root.lock(); - if (root) { - return root->name(); - } else { - return m_name; - } + return m_name; } void Phase::setName(const std::string& name) { - auto root = m_root.lock(); - if (root) { - root->setName(name); - } else { - m_name = name; - } + m_name = name; + m_id = name; } size_t Phase::nElements() const From 891ce3e3302a78cec75bd47ca9e4a4fb5f4c1d2e Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Thu, 10 Oct 2019 06:37:19 -0500 Subject: [PATCH 15/17] [Thermo] deprecate Phase::id and Phase::setID * Merge usage of 'id' and 'name' in the context of Phase objects * Raise deprecation warnings for Phase::id and Phase::setID --- include/cantera/thermo/Phase.h | 27 ++++++++++--------- interfaces/cython/cantera/composite.py | 5 ++-- .../cython/cantera/test/test_mixture.py | 5 ++-- src/kinetics/Kinetics.cpp | 6 ++--- src/kinetics/importKinetics.cpp | 8 +++--- src/thermo/FixedChemPotSSTP.cpp | 1 - src/thermo/LatticeSolidPhase.cpp | 2 +- src/thermo/Phase.cpp | 6 +++++ src/thermo/PureFluidPhase.cpp | 2 +- src/thermo/ThermoFactory.cpp | 10 +++---- 10 files changed, 38 insertions(+), 34 deletions(-) diff --git a/include/cantera/thermo/Phase.h b/include/cantera/thermo/Phase.h index 0dc75d7f4c8..5365a3b41ee 100644 --- a/include/cantera/thermo/Phase.h +++ b/include/cantera/thermo/Phase.h @@ -124,30 +124,31 @@ class Phase */ void setXMLdata(XML_Node& xmlPhase); - /*! @name Name and ID - * Class Phase contains two strings that identify a phase. The ID is the - * value of the ID attribute of the XML phase node that is used to - * initialize a phase when it is read. The name field is also initialized - * to the value of the ID attribute of the XML phase node. + /*! @name Name + * Class Phase uses the string name to identify a phase. The name is the + * value of the corresponding key in the phase map (in YAML), name (in + * CTI), or id (in XML) that is used to initialize a phase when it is read. * * However, the name field may be changed to another value during the - * course of a calculation. For example, if a phase is located in two - * places, but has the same constitutive input, the IDs of the two phases - * will be the same, but the names of the two phases may be different. - * - * It is an error to have two phases in a single problem with the same name - * and ID (or the name from one phase being the same as the id of - * another phase). Thus, it is expected that there is a 1-1 correspondence - * between names and unique phases within a Cantera problem. + * course of a calculation. For example, if duplicates of a phase object + * are instantiated and used in multiple places (e.g. a ReactorNet), they + * will have the same constitutive input, i.e. the names of the phases will + * be the same. Note that this is not a problem for Cantera internally; + * however, a user may want to rename phase objects in order to clarify. */ //!@{ //! Return the string id for the phase. + /*! + * @deprecated To be removed after Cantera 2.5. + */ std::string id() const; //! Set the string id for the phase. /*! * @param id String id of the phase + * + * @deprecated To be removed after Cantera 2.5. */ void setID(const std::string& id); diff --git a/interfaces/cython/cantera/composite.py b/interfaces/cython/cantera/composite.py index ee5421be9c7..dcae86607fe 100644 --- a/interfaces/cython/cantera/composite.py +++ b/interfaces/cython/cantera/composite.py @@ -33,8 +33,9 @@ class Solution(ThermoPhase, Kinetics, Transport): gas = ct.Solution('diamond.yaml', name='gas') diamond = ct.Solution('diamond.yaml', name='diamond') - The name of the `Solution` object defaults to the *phase* specified in the - input file. Once instatiated, a custom name can assigned via:: + The name of the `Solution` object defaults to the *phase* identifier + specified in the input file. Upon initialization of a 'Solution' object, + a custom name can assigned via:: gas.name = 'my_custom_name' diff --git a/interfaces/cython/cantera/test/test_mixture.py b/interfaces/cython/cantera/test/test_mixture.py index b96107ee1f3..42f882bb404 100644 --- a/interfaces/cython/cantera/test/test_mixture.py +++ b/interfaces/cython/cantera/test/test_mixture.py @@ -112,11 +112,10 @@ def test_charge(self): def test_phase_moles(self): M = self.mix.phase_moles() - name = self.phase2.name self.assertEqual(M[0], self.mix.phase_moles(0)) - self.assertEqual(M[1], self.mix.phase_moles(name)) + self.assertEqual(M[1], self.mix.phase_moles('air')) - self.mix.set_phase_moles(name, 4) + self.mix.set_phase_moles('air', 4) self.assertEqual(self.mix.phase_moles(1), 4) def test_species_moles(self): diff --git a/src/kinetics/Kinetics.cpp b/src/kinetics/Kinetics.cpp index 300110b57e8..570f592f980 100644 --- a/src/kinetics/Kinetics.cpp +++ b/src/kinetics/Kinetics.cpp @@ -306,7 +306,7 @@ size_t Kinetics::kineticsSpeciesIndex(const std::string& nm, } for (size_t n = 0; n < m_thermo.size(); n++) { - string id = thermo(n).id(); + string id = thermo(n).name(); if (ph == id) { size_t k = thermo(n).speciesIndex(nm); if (k == npos) { @@ -451,7 +451,7 @@ void Kinetics::addPhase(thermo_t& thermo) m_rxnphase = nPhases(); } m_thermo.push_back(&thermo); - m_phaseindex[m_thermo.back()->id()] = nPhases(); + m_phaseindex[m_thermo.back()->name()] = nPhases(); resizeSpecies(); } @@ -608,7 +608,7 @@ shared_ptr Kinetics::reaction(size_t i) checkReactionIndex(i); return m_reactions[i]; } - + shared_ptr Kinetics::reaction(size_t i) const { checkReactionIndex(i); diff --git a/src/kinetics/importKinetics.cpp b/src/kinetics/importKinetics.cpp index 7d05355ff75..296eb6f35dc 100644 --- a/src/kinetics/importKinetics.cpp +++ b/src/kinetics/importKinetics.cpp @@ -168,7 +168,7 @@ bool importKinetics(const XML_Node& phase, std::vector th, // loop over the supplied 'ThermoPhase' objects representing // phases, to find an object with the same id. for (size_t m = 0; m < th.size(); m++) { - if (th[m]->id() == phase_id) { + if (th[m]->name() == phase_id) { phase_ok = true; // if no phase with this id has been added to //the kinetics manager yet, then add this one @@ -176,7 +176,7 @@ bool importKinetics(const XML_Node& phase, std::vector th, k->addPhase(*th[m]); } } - msg += " "+th[m]->id(); + msg += " "+th[m]->name(); } if (!phase_ok) { throw CanteraError("importKinetics", @@ -254,7 +254,7 @@ bool checkElectrochemReaction(const XML_Node& p, Kinetics& kin, const XML_Node& size_t k = ph.speciesIndex(sp.first); double stoich = sp.second; for (size_t m = 0; m < phase_ids.size(); m++) { - if (phase_ids[m] == ph.id()) { + if (phase_ids[m] == ph.name()) { e_counter[m] += stoich * ph.charge(k); break; } @@ -267,7 +267,7 @@ bool checkElectrochemReaction(const XML_Node& p, Kinetics& kin, const XML_Node& size_t k = ph.speciesIndex(sp.first); double stoich = sp.second; for (size_t m = 0; m < phase_ids.size(); m++) { - if (phase_ids[m] == ph.id()) { + if (phase_ids[m] == ph.name()) { e_counter[m] -= stoich * ph.charge(k); break; } diff --git a/src/thermo/FixedChemPotSSTP.cpp b/src/thermo/FixedChemPotSSTP.cpp index a94a0e10563..62105fdf902 100644 --- a/src/thermo/FixedChemPotSSTP.cpp +++ b/src/thermo/FixedChemPotSSTP.cpp @@ -40,7 +40,6 @@ FixedChemPotSSTP::FixedChemPotSSTP(const std::string& Ename, doublereal val) : chemPot_(0.0) { std::string pname = Ename + "Fixed"; - setID(pname); setName(pname); setNDim(3); addElement(Ename); diff --git a/src/thermo/LatticeSolidPhase.cpp b/src/thermo/LatticeSolidPhase.cpp index 02293b85ba7..a3ce57767fc 100644 --- a/src/thermo/LatticeSolidPhase.cpp +++ b/src/thermo/LatticeSolidPhase.cpp @@ -347,7 +347,7 @@ void LatticeSolidPhase::setLatticeStoichiometry(const compositionMap& comp) } // Add in the lattice stoichiometry constraint for (size_t i = 1; i < m_lattice.size(); i++) { - string econ = fmt::format("LC_{}_{}", i, id()); + string econ = fmt::format("LC_{}_{}", i, name()); size_t m = addElement(econ, 0.0, 0, 0.0, CT_ELEM_TYPE_LATTICERATIO); size_t mm = nElements(); for (size_t k = 0; k < m_lattice[0]->nSpecies(); k++) { diff --git a/src/thermo/Phase.cpp b/src/thermo/Phase.cpp index f1ef31a9a44..e662fbdedfe 100644 --- a/src/thermo/Phase.cpp +++ b/src/thermo/Phase.cpp @@ -68,11 +68,17 @@ void Phase::setXMLdata(XML_Node& xmlPhase) std::string Phase::id() const { + warn_deprecated("Phase::id", + "To be removed after Cantera 2.5. " + "Usage merged with 'Phase::name'"); return m_id; } void Phase::setID(const std::string& id_) { + warn_deprecated("Phase::setID", + "To be removed after Cantera 2.5. " + "Usage merged with 'Phase::setName'"); m_id = id_; m_name = id_; } diff --git a/src/thermo/PureFluidPhase.cpp b/src/thermo/PureFluidPhase.cpp index 2382ac51199..5b77323500a 100644 --- a/src/thermo/PureFluidPhase.cpp +++ b/src/thermo/PureFluidPhase.cpp @@ -64,7 +64,7 @@ void PureFluidPhase::initThermo() m_sub->setStdState(h0_RT*GasConstant*298.15/m_mw, s_R*GasConstant/m_mw, T0, p); debuglog("PureFluidPhase::initThermo: initialized phase " - +id()+"\n", m_verbose); + +name()+"\n", m_verbose); } void PureFluidPhase::setParametersFromXML(const XML_Node& eosdata) diff --git a/src/thermo/ThermoFactory.cpp b/src/thermo/ThermoFactory.cpp index e5a5da4941a..b152c55570b 100644 --- a/src/thermo/ThermoFactory.cpp +++ b/src/thermo/ThermoFactory.cpp @@ -250,7 +250,6 @@ void importPhase(XML_Node& phase, ThermoPhase* th) th->setXMLdata(phase); // set the id attribute of the phase to the 'id' attribute in the XML tree. - th->setID(phase.id()); th->setName(phase.id()); // Number of spatial dimensions. Defaults to 3 (bulk phase) @@ -258,7 +257,7 @@ void importPhase(XML_Node& phase, ThermoPhase* th) int idim = intValue(phase["dim"]); if (idim < 1 || idim > 3) { throw CanteraError("importPhase", - "phase, " + th->id() + + "phase, " + th->name() + ", has unphysical number of dimensions: " + phase["dim"]); } th->setNDim(idim); @@ -274,7 +273,7 @@ void importPhase(XML_Node& phase, ThermoPhase* th) th->setParametersFromXML(eos); } else { throw CanteraError("importPhase", - " phase, " + th->id() + + " phase, " + th->name() + ", XML_Node does not have a \"thermo\" XML_Node"); } @@ -284,7 +283,7 @@ void importPhase(XML_Node& phase, ThermoPhase* th) vpss_ptr = dynamic_cast (th); if (vpss_ptr == 0) { throw CanteraError("importPhase", - "phase, " + th->id() + ", was VPSS, but dynamic cast failed"); + "phase, " + th->name() + ", was VPSS, but dynamic cast failed"); } } @@ -300,7 +299,7 @@ void importPhase(XML_Node& phase, ThermoPhase* th) vector sparrays = phase.getChildren("speciesArray"); if (ssConvention != cSS_CONVENTION_SLAVE && sparrays.empty()) { throw CanteraError("importPhase", - "phase, " + th->id() + ", has zero \"speciesArray\" XML nodes.\n" + "phase, " + th->name() + ", has zero \"speciesArray\" XML nodes.\n" + " There must be at least one speciesArray nodes " "with one or more species"); } @@ -448,7 +447,6 @@ void addSpecies(ThermoPhase& thermo, const AnyValue& names, const AnyValue& spec void setupPhase(ThermoPhase& thermo, AnyMap& phaseNode, const AnyMap& rootNode) { thermo.setName(phaseNode["name"].asString()); - thermo.setID(phaseNode["name"].asString()); if (rootNode.hasKey("__file__")) { phaseNode["__file__"] = rootNode["__file__"]; } From 9696e55aa54ae2559accebd23b67d6e026066ee9 Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Sun, 6 Oct 2019 20:18:20 -0500 Subject: [PATCH 16/17] [Thermo] deprecate name/id support in Phase::speciesIndex --- include/cantera/thermo/Phase.h | 14 +++++--------- src/thermo/Phase.cpp | 5 +++++ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/include/cantera/thermo/Phase.h b/include/cantera/thermo/Phase.h index 5365a3b41ee..86288ebfe2e 100644 --- a/include/cantera/thermo/Phase.h +++ b/include/cantera/thermo/Phase.h @@ -68,15 +68,11 @@ class Solution; * operate on a state vector, which is in general of length (2 + nSpecies()). * The first two entries of the state vector are temperature and density. * - * A species name may be referred to via three methods: - * - * - "speciesName" - * - "PhaseId:speciesName" - * - "phaseName:speciesName" - * . - * - * The first two methods of naming may not yield a unique species within - * complicated assemblies of %Cantera Phases. + * A species name is referred to via "speciesName", which is unique within a + * given phase. Note that within multiphase mixtures ("MultiPhase"), both a + * phase name/index as well as species name are required to access information + * about a species in a particular phase. For surfaces, the species names are + * unique among the phases. * * @todo * - Make the concept of saving state vectors more general, so that it can diff --git a/src/thermo/Phase.cpp b/src/thermo/Phase.cpp index e662fbdedfe..3e827e980a4 100644 --- a/src/thermo/Phase.cpp +++ b/src/thermo/Phase.cpp @@ -214,6 +214,11 @@ size_t Phase::speciesIndex(const std::string& nameStr) const std::string pn; std::string sn = parseSpeciesName(nameStr, pn); if (pn == "" || pn == m_name || pn == m_id) { + warn_deprecated("Phase::speciesIndex()", + "Retrieval of species indices via " + "'PhaseId:speciesName' or 'phaseName:" + "speciesName to be removed " + "after Cantera 2.5."); it = m_speciesIndices.find(nameStr); if (it != m_speciesIndices.end()) { return it->second; From 6190b4fca1fa463101d37c0c6384e9dbe6d9761e Mon Sep 17 00:00:00 2001 From: Ingmar Schoegl Date: Tue, 22 Oct 2019 13:44:50 -0500 Subject: [PATCH 17/17] [Thermo] address code review comments --- include/cantera/base/Solution.h | 6 +++--- include/cantera/thermo/Phase.h | 4 ++-- interfaces/cython/cantera/base.pyx | 5 +---- interfaces/cython/cantera/transport.pyx | 2 -- src/thermo/Phase.cpp | 12 ++++-------- 5 files changed, 10 insertions(+), 19 deletions(-) diff --git a/include/cantera/base/Solution.h b/include/cantera/base/Solution.h index 50c8979e338..bf886a95dc1 100644 --- a/include/cantera/base/Solution.h +++ b/include/cantera/base/Solution.h @@ -61,9 +61,9 @@ class Solution : public std::enable_shared_from_this } protected: - shared_ptr m_thermo; //! ThermoPhase manager - shared_ptr m_kinetics; //! Kinetics manager - shared_ptr m_transport; //! Transport manager + shared_ptr m_thermo; //!< ThermoPhase manager + shared_ptr m_kinetics; //!< Kinetics manager + shared_ptr m_transport; //!< Transport manager }; } diff --git a/include/cantera/thermo/Phase.h b/include/cantera/thermo/Phase.h index 86288ebfe2e..48635dc4c00 100644 --- a/include/cantera/thermo/Phase.h +++ b/include/cantera/thermo/Phase.h @@ -68,8 +68,8 @@ class Solution; * operate on a state vector, which is in general of length (2 + nSpecies()). * The first two entries of the state vector are temperature and density. * - * A species name is referred to via "speciesName", which is unique within a - * given phase. Note that within multiphase mixtures ("MultiPhase"), both a + * A species name is referred to via speciesName(), which is unique within a + * given phase. Note that within multiphase mixtures (MultiPhase()), both a * phase name/index as well as species name are required to access information * about a species in a particular phase. For surfaces, the species names are * unique among the phases. diff --git a/interfaces/cython/cantera/base.pyx b/interfaces/cython/cantera/base.pyx index 3609ba25a77..6444a4c56d2 100644 --- a/interfaces/cython/cantera/base.pyx +++ b/interfaces/cython/cantera/base.pyx @@ -70,16 +70,13 @@ cdef class _SolutionBase: if isinstance(self, Transport): assert self.transport is not NULL - phase_name = pystr(self.base.name()) name = kwargs.get('name') if name is not None: self.name = name - else: - self.name = phase_name property name: """ - The name assigned to this object. The default value corresponds + The name assigned to this object. The default value corresponds to the CTI/XML/YAML input file phase entry. """ def __get__(self): diff --git a/interfaces/cython/cantera/transport.pyx b/interfaces/cython/cantera/transport.pyx index 82fa37aca04..b9b0bb24e81 100644 --- a/interfaces/cython/cantera/transport.pyx +++ b/interfaces/cython/cantera/transport.pyx @@ -145,8 +145,6 @@ cdef class Transport(_SolutionBase): self.transport = newTransportMgr(stringify(model), self.thermo) self._transport.reset(self.transport) - base_type = kwargs.pop('base_type', 'Transport') - kwargs['base_type'] = base_type super().__init__(*args, **kwargs) self.base.setTransport(self._transport) diff --git a/src/thermo/Phase.cpp b/src/thermo/Phase.cpp index 3e827e980a4..2c18552e387 100644 --- a/src/thermo/Phase.cpp +++ b/src/thermo/Phase.cpp @@ -69,16 +69,14 @@ void Phase::setXMLdata(XML_Node& xmlPhase) std::string Phase::id() const { warn_deprecated("Phase::id", - "To be removed after Cantera 2.5. " - "Usage merged with 'Phase::name'"); + "To be removed after Cantera 2.5. Usage merged with 'Phase::name'"); return m_id; } void Phase::setID(const std::string& id_) { warn_deprecated("Phase::setID", - "To be removed after Cantera 2.5. " - "Usage merged with 'Phase::setName'"); + "To be removed after Cantera 2.5. Usage merged with 'Phase::setName'"); m_id = id_; m_name = id_; } @@ -215,10 +213,8 @@ size_t Phase::speciesIndex(const std::string& nameStr) const std::string sn = parseSpeciesName(nameStr, pn); if (pn == "" || pn == m_name || pn == m_id) { warn_deprecated("Phase::speciesIndex()", - "Retrieval of species indices via " - "'PhaseId:speciesName' or 'phaseName:" - "speciesName to be removed " - "after Cantera 2.5."); + "Retrieval of species indices via 'PhaseId:speciesName' or " + "'phaseName:speciesName' to be removed after Cantera 2.5."); it = m_speciesIndices.find(nameStr); if (it != m_speciesIndices.end()) { return it->second;