Skip to content

Commit

Permalink
[oneD] Ensure Python API passes Solutions to boundaries
Browse files Browse the repository at this point in the history
  • Loading branch information
ischoegl authored and speth committed Sep 29, 2022
1 parent 3dfb13e commit 760ea59
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 60 deletions.
24 changes: 24 additions & 0 deletions include/cantera/oneD/Boundary1D.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -154,6 +158,10 @@ class Empty1D : public Boundary1D
m_type = cEmptyType;
}

Empty1D(shared_ptr<Solution> solution) : Empty1D() {
m_solution = solution;
}

virtual void showSolution(const double* x) {}

virtual void init();
Expand All @@ -176,6 +184,10 @@ class Symm1D : public Boundary1D
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,
Expand All @@ -196,6 +208,10 @@ class Outlet1D : public Boundary1D
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,
Expand All @@ -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() {
Expand Down Expand Up @@ -251,6 +271,10 @@ class Surf1D : public Boundary1D
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,
Expand Down
12 changes: 6 additions & 6 deletions interfaces/cython/cantera/_onedim.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
78 changes: 33 additions & 45 deletions interfaces/cython/cantera/_onedim.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,23 @@ 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.")

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()
Expand Down Expand Up @@ -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. """
Expand Down Expand Up @@ -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):
Expand All @@ -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):
Expand All @@ -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):
Expand All @@ -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):
Expand All @@ -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):
Expand All @@ -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

Expand Down
16 changes: 7 additions & 9 deletions src/oneD/Boundary1D.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
13 changes: 13 additions & 0 deletions test/python/test_onedim.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,19 @@ def test_uncopyable(self):
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)
Expand Down

0 comments on commit 760ea59

Please sign in to comment.