[oneD] Ensure Python API passes Solutions to boundaries

This commit is contained in:
Ingmar Schoegl
2022-09-05 18:29:14 -05:00
committed by Ray Speth
parent 3dfb13e8be
commit 760ea59edd
5 changed files with 83 additions and 60 deletions

View File

@@ -106,6 +106,10 @@ class Inlet1D : public Boundary1D
public:
Inlet1D();
Inlet1D(shared_ptr<Solution> solution) : Inlet1D() {
m_solution = solution;
}
//! set spreading rate
virtual void setSpreadRate(double V0) {
m_V0 = V0;
@@ -154,6 +158,10 @@ public:
m_type = cEmptyType;
}
Empty1D(shared_ptr<Solution> solution) : Empty1D() {
m_solution = solution;
}
virtual void showSolution(const double* x) {}
virtual void init();
@@ -176,6 +184,10 @@ public:
m_type = cSymmType;
}
Symm1D(shared_ptr<Solution> solution) : Symm1D() {
m_solution = solution;
}
virtual void init();
virtual void eval(size_t jg, double* xg, double* rg,
@@ -196,6 +208,10 @@ public:
m_type = cOutletType;
}
Outlet1D(shared_ptr<Solution> solution) : Outlet1D() {
m_solution = solution;
}
virtual void init();
virtual void eval(size_t jg, double* xg, double* rg,
@@ -214,6 +230,10 @@ class OutletRes1D : public Boundary1D
public:
OutletRes1D();
OutletRes1D(shared_ptr<Solution> solution) : OutletRes1D() {
m_solution = solution;
}
virtual void showSolution(const double* x) {}
virtual size_t nSpecies() {
@@ -251,6 +271,10 @@ public:
m_type = cSurfType;
}
Surf1D(shared_ptr<Solution> solution) : Surf1D() {
m_solution = solution;
}
virtual void init();
virtual void eval(size_t jg, double* xg, double* rg,

View File

@@ -48,24 +48,24 @@ cdef extern from "cantera/oneD/Boundary1D.h":
double massFraction(size_t)
cdef cppclass CxxInlet1D "Cantera::Inlet1D":
CxxInlet1D()
CxxInlet1D(shared_ptr[CxxSolution])
double spreadRate()
void setSpreadRate(double)
cdef cppclass CxxOutlet1D "Cantera::Outlet1D":
CxxOutlet1D()
CxxOutlet1D(shared_ptr[CxxSolution])
cdef cppclass CxxOutletRes1D "Cantera::OutletRes1D":
CxxOutletRes1D()
CxxOutletRes1D(shared_ptr[CxxSolution])
cdef cppclass CxxSymm1D "Cantera::Symm1D":
CxxSymm1D()
CxxSymm1D(shared_ptr[CxxSolution])
cdef cppclass CxxSurf1D "Cantera::Surf1D":
CxxSurf1D()
CxxSurf1D(shared_ptr[CxxSolution])
cdef cppclass CxxReactingSurf1D "Cantera::ReactingSurf1D":
CxxReactingSurf1D()
CxxReactingSurf1D() # deprecated in Python API (Cantera 3.0)
CxxReactingSurf1D(shared_ptr[CxxSolution]) except +translate_exception
void setKineticsMgr(CxxInterfaceKinetics*) except +translate_exception
void enableCoverageEquations(cbool) except +translate_exception

View File

@@ -10,15 +10,16 @@ from ._utils cimport stringify, pystr
from ._utils import CanteraError
from cython.operator import dereference as deref
# Need a pure-python class to store weakrefs to
class _WeakrefProxy:
pass
cdef class Domain1D:
def __cinit__(self, *args, **kwargs):
def __cinit__(self, _SolutionBase phase not None, *args, **kwargs):
self.domain = NULL
def __init__(self, _SolutionBase phase, *args, name=None, **kwargs):
def __init__(self, phase, *args, name=None, **kwargs):
self._weakref_proxy = _WeakrefProxy()
if self.domain is NULL:
raise TypeError("Can't instantiate abstract class Domain1D.")
@@ -26,8 +27,6 @@ cdef class Domain1D:
if name is not None:
self.name = name
if not isinstance(phase, _SolutionBase):
raise TypeError(f"Received phase with invalid type '{type(phase)}'.")
self.gas = phase
self.gas._references[self._weakref_proxy] = True
self.set_default_tolerances()
@@ -286,14 +285,11 @@ cdef class Boundary1D(Domain1D):
def __cinit__(self, *args, **kwargs):
self.boundary = NULL
def __init__(self, *args, phase=None, **kwargs):
def __init__(self, phase, name=None):
if self.boundary is NULL:
raise TypeError("Can't instantiate abstract class Boundary1D.")
self.domain = <CxxDomain1D*>(self.boundary)
if phase is not None:
Domain1D.__init__(self, phase, *args, **kwargs)
else:
Domain1D.__init__(self, *args, **kwargs)
Domain1D.__init__(self, phase, name=name)
property T:
""" The temperature [K] at this boundary. """
@@ -347,8 +343,8 @@ cdef class Inlet1D(Boundary1D):
domain - it must be either the leftmost or rightmost domain in a
stack.
"""
def __cinit__(self, *args, **kwargs):
self.inlet = new CxxInlet1D()
def __cinit__(self, _SolutionBase phase, *args, **kwargs):
self.inlet = new CxxInlet1D(phase._base)
self.boundary = <CxxBoundary1D*>(self.inlet)
def __dealloc__(self):
@@ -369,8 +365,8 @@ cdef class Outlet1D(Boundary1D):
A one-dimensional outlet. An outlet imposes a zero-gradient boundary
condition on the flow.
"""
def __cinit__(self, *args, **kwargs):
self.outlet = new CxxOutlet1D()
def __cinit__(self, _SolutionBase phase, *args, **kwargs):
self.outlet = new CxxOutlet1D(phase._base)
self.boundary = <CxxBoundary1D*>(self.outlet)
def __dealloc__(self):
@@ -381,8 +377,8 @@ cdef class OutletReservoir1D(Boundary1D):
"""
A one-dimensional outlet into a reservoir.
"""
def __cinit__(self, *args, **kwargs):
self.outlet = new CxxOutletRes1D()
def __cinit__(self, _SolutionBase phase, *args, **kwargs):
self.outlet = new CxxOutletRes1D(phase._base)
self.boundary = <CxxBoundary1D*>(self.outlet)
def __dealloc__(self):
@@ -391,8 +387,8 @@ cdef class OutletReservoir1D(Boundary1D):
cdef class SymmetryPlane1D(Boundary1D):
"""A symmetry plane."""
def __cinit__(self, *args, **kwargs):
self.symm = new CxxSymm1D()
def __cinit__(self, _SolutionBase phase, *args, **kwargs):
self.symm = new CxxSymm1D(phase._base)
self.boundary = <CxxBoundary1D*>(self.symm)
def __dealloc__(self):
@@ -401,8 +397,8 @@ cdef class SymmetryPlane1D(Boundary1D):
cdef class Surface1D(Boundary1D):
"""A solid surface."""
def __cinit__(self, *args, **kwargs):
self.surf = new CxxSurf1D()
def __cinit__(self, _SolutionBase phase, *args, **kwargs):
self.surf = new CxxSurf1D(phase._base)
self.boundary = <CxxBoundary1D*>(self.surf)
def __dealloc__(self):
@@ -420,39 +416,31 @@ cdef class ReactingSurface1D(Boundary1D):
Starting in Cantera 3.0, parameter `phase` should reference surface instead of
gas phase.
"""
def __cinit__(self, *args, phase=None, **kwargs):
cdef _SolutionBase sol
if isinstance(phase, _SolutionBase) and phase.phase_of_matter != "gas":
sol = phase
self.surf = new CxxReactingSurf1D(sol._base)
def __cinit__(self, _SolutionBase phase, *args, **kwargs):
if phase.phase_of_matter != "gas":
self.surf = new CxxReactingSurf1D(phase._base)
else:
# legacy pathway - deprecation is handled in __init__
self.surf = new CxxReactingSurf1D()
self.boundary = <CxxBoundary1D*>(self.surf)
def __init__(self, *args, phase=None, **kwargs):
def __init__(self, _SolutionBase phase, name=None):
self._weakref_proxy = _WeakrefProxy()
if phase is None and isinstance(args[0], _SolutionBase):
phase = args[0]
args = args[1:]
cdef _SolutionBase sol
if isinstance(phase, _SolutionBase):
if phase.phase_of_matter == "gas":
warnings.warn("Starting in Cantera 3.0, parameter 'phase' should "
"reference surface instead of gas phase.", DeprecationWarning)
super().__init__(*args, phase=phase, **kwargs)
else:
sol = phase
gas = None
for val in sol._adjacent.values():
if val.phase_of_matter == "gas":
gas = val
break
if gas is None:
raise CanteraError("ReactingSurface1D needs an adjacent gas phase")
super().__init__(*args, phase=gas, **kwargs)
if phase.phase_of_matter == "gas":
warnings.warn("Starting in Cantera 3.0, parameter 'phase' should "
"reference surface instead of gas phase.", DeprecationWarning)
super().__init__(phase, name=name)
else:
super().__init__(*args, phase=phase, **kwargs)
sol = phase
gas = None
for val in sol._adjacent.values():
if val.phase_of_matter == "gas":
gas = val
break
if gas is None:
raise CanteraError("ReactingSurface1D needs an adjacent gas phase")
super().__init__(gas, name=name)
self.surface = phase
self.surface._references[self._weakref_proxy] = True

View File

@@ -612,24 +612,22 @@ ReactingSurf1D::ReactingSurf1D()
ReactingSurf1D::ReactingSurf1D(shared_ptr<Solution> solution)
{
if (!std::dynamic_pointer_cast<SurfPhase>(solution->thermo())) {
auto phase = std::dynamic_pointer_cast<SurfPhase>(solution->thermo());
if (!phase) {
throw CanteraError("ReactingSurf1D::ReactingSurf1D",
"Detected incompatible ThermoPhase type '{}'", solution->thermo()->type());
}
if (!std::dynamic_pointer_cast<InterfaceKinetics>(solution->kinetics())) {
auto kin = std::dynamic_pointer_cast<InterfaceKinetics>(solution->kinetics());
if (!kin) {
throw CanteraError("ReactingSurf1D::ReactingSurf1D",
"Detected incompatible kinetics type '{}'",
solution->kinetics()->kineticsType());
}
m_solution = solution;
m_kin = (InterfaceKinetics*)solution->kinetics().get();
m_kin = kin.get();
m_sphase = phase.get();
m_surfindex = m_kin->surfacePhaseIndex();
m_sphase = (SurfPhase*)&m_kin->thermo(m_surfindex);
if (m_sphase->name() != m_solution->thermo()->name()) {
throw CanteraError("ReactingSurf1D::ReactingSurf1D",
"Detected inconsistent ThermoPhase objects: mismatch of '{}' and '{}'.",
m_sphase->name(), m_solution->thermo()->name());
}
m_nsp = m_sphase->nSpecies();
m_enabled = true;
}

View File

@@ -76,6 +76,19 @@ class TestOnedim(utilities.CanteraTest):
with self.assertRaises(NotImplementedError):
copy.copy(flame)
def test_exceptions(self):
with pytest.raises(TypeError, match="Argument 'phase' has incorrect type"):
ct.Inlet1D(None)
gas = ct.Solution("h2o2.yaml")
with pytest.warns(DeprecationWarning, match="should reference surface"):
ct.ReactingSurface1D(gas)
with pytest.raises(TypeError, match="unexpected keyword"):
ct.ReactingSurface1D(gas, foo="bar")
interface = ct.Solution("diamond.yaml", "diamond_100")
surf = ct.ReactingSurface1D(interface)
with pytest.warns(DeprecationWarning, match="Method to be removed"):
surf.set_kinetics(interface)
def test_invalid_property(self):
gas1 = ct.Solution("h2o2.yaml")
inlet = ct.Inlet1D(name='something', phase=gas1)