Skip to content

Commit

Permalink
[1D/Python] Create BurnerIonFlame and add test
Browse files Browse the repository at this point in the history
Create a base class (IonFlameBase) for both IonFreeFlame and BurnerIonFlame, and
use the set_axisymmetric_flow() and set_free_flow() methods to select the flow
type.

Also combines FreeFlow and AxisymmetricStagnationFlow classes into class
IdealGasFlow.
  • Loading branch information
bangshiuh authored and speth committed Aug 10, 2018
1 parent a5762ea commit 2527869
Show file tree
Hide file tree
Showing 7 changed files with 196 additions and 106 deletions.
2 changes: 2 additions & 0 deletions include/cantera/oneD/StFlow.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,12 @@ class StFlow : public Domain1D

void setFreeFlow() {
m_type = cFreeFlow;
m_dovisc = false;
}

void setAxisymmetricFlow() {
m_type = cAxisymmetricStagnationFlow;
m_dovisc = true;
}

virtual std::string flowType() {
Expand Down
8 changes: 5 additions & 3 deletions interfaces/cython/cantera/_cantera.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -687,7 +687,6 @@ cdef extern from "cantera/oneD/StFlow.h":
cbool doEnergy(size_t)
void enableSoret(cbool) except +translate_exception
cbool withSoret()
void setViscosityFlag(bool)
void setFreeFlow()
void setAxisymmetricFlow()

Expand Down Expand Up @@ -1041,13 +1040,16 @@ cdef class ReactingSurface1D(Boundary1D):
cdef class _FlowBase(Domain1D):
cdef CxxStFlow* flow

cdef class FreeFlow(_FlowBase):
cdef class IdealGasFlow(_FlowBase):
pass

cdef class FreeFlow(IdealGasFlow):
pass

cdef class IonFlow(_FlowBase):
pass

cdef class AxisymmetricStagnationFlow(_FlowBase):
cdef class AxisymmetricStagnationFlow(IdealGasFlow):
pass

cdef class Sim1D:
Expand Down
29 changes: 29 additions & 0 deletions interfaces/cython/cantera/examples/onedim/ion_burner_flame.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""
A burner-stabilized lean premixed hydrogen-oxygen flame at low pressure.
"""

import cantera as ct
import numpy as np

p = ct.one_atm
tburner = 600.0
reactants = 'CH4:1.0, O2:2.0, N2:7.52' # premixed gas composition
width = 0.5 # m
loglevel = 1 # amount of diagnostic output (0 to 5)

gas = ct.Solution('gri30_ion.cti')
gas.TPX = tburner, p, reactants
mdot = 0.15 * gas.density

f = ct.IonBurnerFlame(gas, width=width)
f.burner.mdot = mdot
f.set_refine_criteria(ratio=3.0, slope=0.05, curve=0.1)
f.show_solution()

f.transport_model = 'Ion'
f.solve(loglevel, auto=True)
f.solve(loglevel=loglevel, stage=2, enable_energy=True)
f.save('CH4_burner_flame.xml', 'mix', 'solution with mixture-averaged transport')

f.write_csv('CH4_burner_flame.csv', quiet=False)

Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
gas.TPX = Tin, p, reactants

# Set up flame object
f = ct.IonFlame(gas, width=width)
f = ct.IonFreeFlame(gas, width=width)
f.set_refine_criteria(ratio=3, slope=0.05, curve=0.1)
f.show_solution()

Expand Down
117 changes: 71 additions & 46 deletions interfaces/cython/cantera/onedim.py
Original file line number Diff line number Diff line change
Expand Up @@ -394,9 +394,9 @@ class FreeFlame(FlameBase):

def __init__(self, gas, grid=None, width=None):
"""
A domain of type FreeFlow named 'flame' will be created to represent
the flame. The three domains comprising the stack are stored as
``self.inlet``, ``self.flame``, and ``self.outlet``.
A domain of type IdealGasFlow named 'flame' will be created to represent
the flame and set to free flow. The three domains comprising the stack
are stored as ``self.inlet``, ``self.flame``, and ``self.outlet``.
:param grid:
A list of points to be used as the initial grid. Not recommended
Expand All @@ -410,7 +410,8 @@ def __init__(self, gas, grid=None, width=None):
self.outlet = Outlet1D(name='products', phase=gas)
if not hasattr(self, 'flame'):
# Create flame domain if not already instantiated by a child class
self.flame = FreeFlow(gas, name='flame')
self.flame = IdealGasFlow(gas, name='flame')
self.flame.set_free_flow()

if width is not None:
grid = np.array([0.0, 0.2, 0.3, 0.4, 0.5, 0.6, 0.8, 1.0]) * width
Expand Down Expand Up @@ -561,29 +562,12 @@ def perturb(sim, i, dp):
return self.solve_adjoint(perturb, self.gas.n_reactions, dgdx) / Su0


class IonFlame(FreeFlame):
__slots__ = ('inlet', 'outlet', 'flame')

def __init__(self, gas, grid=None, width=None):
if not hasattr(self, 'flame'):
# Create flame domain if not already instantiated by a child class
self.flame = IonFlow(gas, name='flame')

super(IonFlame, self).__init__(gas, grid, width)

def solve(self, loglevel=1, refine_grid=True, auto=False, stage=1, enable_energy=True):
self.flame.set_solvingStage(stage)
if stage == 1:
super(IonFlame, self).solve(loglevel, refine_grid, auto)
if stage == 2:
self.poisson_enabled = True
super(IonFlame, self).solve(loglevel, refine_grid, auto)
class IonFlameBase(FlameBase):

def write_csv(self, filename, species='X', quiet=True):
"""
Write the velocity, temperature, density, electric potential,
, electric field stregth, and species profiles to a CSV file.
:param filename:
Output file name
:param species:
Expand Down Expand Up @@ -641,6 +625,26 @@ def E(self):
Efield.append((phi[np-2] - phi[np-1]) / (z[np-1] - z[np-2]))
return Efield

def solve(self, loglevel=1, refine_grid=True, auto=False, stage=1, enable_energy=True):
self.flame.set_solvingStage(stage)
if stage == 1:
super(IonFlameBase, self).solve(loglevel, refine_grid, auto)
if stage == 2:
self.poisson_enabled = True
super(IonFlameBase, self).solve(loglevel, refine_grid, auto)


class IonFreeFlame(IonFlameBase, FreeFlame):
__slots__ = ('inlet', 'outlet', 'flame')

def __init__(self, gas, grid=None, width=None):
if not hasattr(self, 'flame'):
# Create flame domain if not already instantiated by a child class
self.flame = IonFlow(gas, name='flame')
self.flame.set_free_flow()

super(IonFreeFlame, self).__init__(gas, grid, width)


class BurnerFlame(FlameBase):
"""A burner-stabilized flat flame."""
Expand All @@ -659,14 +663,17 @@ def __init__(self, gas, grid=None, width=None):
Defines a grid on the interval [0, width] with internal points
determined automatically by the solver.
A domain of class `AxisymmetricStagnationFlow` named ``flame`` will
be created to represent the flame. The three domains comprising the
stack are stored as ``self.burner``, ``self.flame``, and
``self.outlet``.
A domain of class `IdealGasFlow` named ``flame`` will be created to
represent the flame and set to axisymmetric stagnation flow. The three
domains comprising the stack are stored as ``self.burner``,
``self.flame``, and ``self.outlet``.
"""
self.burner = Inlet1D(name='burner', phase=gas)
self.outlet = Outlet1D(name='outlet', phase=gas)
self.flame = AxisymmetricStagnationFlow(gas, name='flame')
if not hasattr(self, 'flame'):
# Create flame domain if not already instantiated by a child class
self.flame = IdealGasFlow(gas, name='flame')
self.flame.set_axisymmetric_flow()

if width is not None:
grid = np.array([0.0, 0.1, 0.2, 0.3, 0.5, 0.7, 1.0]) * width
Expand Down Expand Up @@ -765,6 +772,19 @@ def check_blowoff(t):
self.set_steady_callback(original_callback)


class IonBurnerFlame(IonFlameBase, BurnerFlame):
"""A burner-stabilized flat flame with ionized gas."""
__slots__ = ('burner', 'flame', 'outlet')

def __init__(self, gas, grid=None, width=None):
if not hasattr(self, 'flame'):
# Create flame domain if not already instantiated by a child class
self.flame = IonFlow(gas, name='flame')
self.flame.set_axisymmetric_flow()

super(IonBurnerFlame, self).__init__(gas, grid, width)


class CounterflowDiffusionFlame(FlameBase):
""" A counterflow diffusion flame """
__slots__ = ('fuel_inlet', 'flame', 'oxidizer_inlet')
Expand All @@ -782,18 +802,19 @@ def __init__(self, gas, grid=None, width=None):
Defines a grid on the interval [0, width] with internal points
determined automatically by the solver.
A domain of class `AxisymmetricStagnationFlow` named ``flame`` will
be created to represent the flame. The three domains comprising the
stack are stored as ``self.fuel_inlet``, ``self.flame``, and
``self.oxidizer_inlet``.
A domain of class `IdealGasFlow` named ``flame`` will be created to
represent the flame and set to axisymmetric stagnation flow. The three
domains comprising the stack are stored as ``self.fuel_inlet``,
``self.flame``, and ``self.oxidizer_inlet``.
"""
self.fuel_inlet = Inlet1D(name='fuel_inlet', phase=gas)
self.fuel_inlet.T = gas.T

self.oxidizer_inlet = Inlet1D(name='oxidizer_inlet', phase=gas)
self.oxidizer_inlet.T = gas.T

self.flame = AxisymmetricStagnationFlow(gas, name='flame')
self.flame = IdealGasFlow(gas, name='flame')
self.flame.set_axisymmetric_flow()

if width is not None:
grid = np.array([0.0, 0.2, 0.4, 0.6, 0.8, 1.0]) * width
Expand Down Expand Up @@ -1062,12 +1083,14 @@ def __init__(self, gas, grid=None, width=None, surface=None):
:param surface:
A Kinetics object used to compute any surface reactions.
A domain of class `AxisymmetricStagnationFlow` named ``flame`` will be
created to represent the flow. The three domains comprising the stack
are stored as ``self.inlet``, ``self.flame``, and ``self.surface``.
A domain of class `IdealGasFlow` named ``flame`` will be created to
represent the flame and set to axisymmetric stagnation flow. The three
domains comprising the stack are stored as ``self.inlet``,
``self.flame``, and ``self.surface``.
"""
self.inlet = Inlet1D(name='inlet', phase=gas)
self.flame = AxisymmetricStagnationFlow(gas, name='flame')
self.flame = IdealGasFlow(gas, name='flame')
self.flame.set_axisymmetric_flow()

if width is not None:
grid = np.array([0.0, 0.2, 0.4, 0.6, 0.8, 1.0]) * width
Expand Down Expand Up @@ -1138,18 +1161,19 @@ def __init__(self, gas, grid=None, width=None):
Defines a grid on the interval [0, width] with internal points
determined automatically by the solver.
A domain of class `AxisymmetricStagnationFlow` named ``flame`` will
be created to represent the flame. The three domains comprising the
stack are stored as ``self.reactants``, ``self.flame``, and
``self.products``.
A domain of class `IdealGasFlow` named ``flame`` will be created to
represent the flame and set to axisymmetric stagnation flow. The three
domains comprising the stack are stored as ``self.reactants``,
``self.flame``, and ``self.products``.
"""
self.reactants = Inlet1D(name='reactants', phase=gas)
self.reactants.T = gas.T

self.products = Inlet1D(name='products', phase=gas)
self.products.T = gas.T

self.flame = AxisymmetricStagnationFlow(gas, name='flame')
self.flame = IdealGasFlow(gas, name='flame')
self.flame.set_axisymmetric_flow()

if width is not None:
# Create grid points aligned with initial guess profile
Expand Down Expand Up @@ -1231,15 +1255,16 @@ def __init__(self, gas, grid=None, width=None):
Defines a grid on the interval [0, width] with internal points
determined automatically by the solver.
A domain of class `AxisymmetricStagnationFlow` named ``flame`` will
be created to represent the flame. The three domains comprising the
stack are stored as ``self.reactants``, ``self.flame``, and
``self.products``.
A domain of class `IdealGasFlow` named ``flame`` will be created to
represent the flame and set to axisymmetric stagnation flow. The three
domains comprising the stack are stored as ``self.reactants``,
``self.flame``, and ``self.products``.
"""
self.reactants = Inlet1D(name='reactants', phase=gas)
self.reactants.T = gas.T

self.flame = AxisymmetricStagnationFlow(gas, name='flame')
self.flame = IdealGasFlow(gas, name='flame')
self.flame.set_axisymmetric_flow()

#The right boundary is a symmetry plane
self.products = SymmetryPlane1D(name='products', phase=gas)
Expand Down
Loading

0 comments on commit 2527869

Please sign in to comment.