Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions doc/sphinx/python/onedim.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ CounterflowDiffusionFlame
^^^^^^^^^^^^^^^^^^^^^^^^^
.. autoclass:: CounterflowDiffusionFlame

.. note::
``CounterflowDiffusionFlame.set_initial_guess`` accepts ``mode="linear"``
to initialize profiles by linearly interpolating inlet temperature and
composition.

CounterflowPremixedFlame
^^^^^^^^^^^^^^^^^^^^^^^^
.. autoclass:: CounterflowPremixedFlame
Expand Down
49 changes: 40 additions & 9 deletions interfaces/cython/cantera/onedim.py
Original file line number Diff line number Diff line change
Expand Up @@ -925,16 +925,24 @@ def __init__(self, gas, grid=None, width=None):

super().__init__((self.fuel_inlet, self.flame, self.oxidizer_inlet), gas, grid)

def set_initial_guess(self, data=None, group=None):
def set_initial_guess(self, data=None, group=None, mode="stoich"):
"""
Set the initial guess for the solution. By default, the initial guess
is generated by assuming infinitely-fast chemistry. Alternatively, a
previously calculated result can be supplied as an initial guess via
'data' and 'key' inputs (see `FlameBase.set_initial_guess`).

:param mode:
Initial guess mode. ``'stoich'`` (default) uses the stoichiometric,
infinitely-fast chemistry guess. ``'linear'`` linearly interpolates
inlet temperature and composition profiles.
If ``data`` is provided, this option is ignored.
"""
super().set_initial_guess(data=data, group=group)
if data:
return
if mode not in ("stoich", "linear"):
raise ValueError("mode must be 'stoich' or 'linear'")

# Compute stoichiometric mixture composition
Yin_f = self.fuel_inlet.Y
Expand All @@ -955,7 +963,37 @@ def set_initial_guess(self, data=None, group=None):
raise CanteraError("Mass flow for fuel and/or oxidizer "
"must be positive")

zst = 1 / (1 + self.gas.stoich_air_fuel_ratio(Yin_f, Yin_o, 'mass'))
zz = self.flame.grid
dz = zz[-1] - zz[0]
a = (u0o + u0f)/dz
L = - 0.5 * (rho0o + rho0f) * a**2

x0 = np.sqrt(mdotf*u0f) * dz / (np.sqrt(mdotf*u0f) + np.sqrt(mdoto*u0o))
nz = len(zz)

if mode == "linear":
zrel = (zz - zz[0])/dz
T = (1.0 - zrel) * T0f + zrel * T0o
Y = np.outer((1.0 - zrel), Yin_f) + np.outer(zrel, Yin_o)
T[0] = T0f
T[-1] = T0o
Y[0] = Yin_f
Y[-1] = Yin_o

self.flame.set_profile('velocity', [0.0, 1.0], [u0f, -u0o])
self.flame.set_profile('spreadRate', [0.0, x0/dz, 1.0], [0.0, a, 0.0])
self.flame.set_profile("Lambda", [0.0, 1.0], [L, L])
self.flame.set_profile('T', zrel, T)
for k, spec in enumerate(self.gas.species_names):
self.flame.set_profile(spec, zrel, Y[:,k])
return

afr = self.gas.stoich_air_fuel_ratio(Yin_f, Yin_o, 'mass')
zst = 1 / (1 + afr)
if not np.isfinite(zst) or zst <= 0.0 or zst >= 1.0:
raise CanteraError(
"Stoichiometric initial guess requires a reactive fuel/oxidizer "
"pair; use mode='linear'.")
Yst = zst * Yin_f + (1.0 - zst) * Yin_o

# get adiabatic flame temperature and composition
Expand All @@ -966,16 +1004,9 @@ def set_initial_guess(self, data=None, group=None):
Yeq = self.gas.Y

# estimate strain rate
zz = self.flame.grid
dz = zz[-1] - zz[0]
a = (u0o + u0f)/dz
kOx = (self.gas.species_index('O2') if 'O2' in self.gas.species_names else
self.gas.species_index('o2'))
f = np.sqrt(a / (2.0 * self.gas.mix_diff_coeffs[kOx]))
L = - 0.5 * (rho0o + rho0f) * a**2

x0 = np.sqrt(mdotf*u0f) * dz / (np.sqrt(mdotf*u0f) + np.sqrt(mdoto*u0o))
nz = len(zz)

Y = np.zeros((nz, self.gas.n_species))
T = np.zeros(nz)
Expand Down
1 change: 1 addition & 0 deletions interfaces/cython/cantera/onedim.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@ class BurnerFlame(FlameBase):
self,
data: SolutionArray[Solution] | DataFrame | str | Path | None = None,
group: str | None = None,
mode: Literal["stoich", "linear"] = "stoich",
) -> None: ...
def solve(
self,
Expand Down
35 changes: 35 additions & 0 deletions test/python/test_onedim.py
Original file line number Diff line number Diff line change
Expand Up @@ -1295,6 +1295,41 @@ def test_bad_boundary_conditions(self):
sim.fuel_inlet.X = "H2: 1.0"
sim.set_initial_guess()

def test_linear_initial_guess_inert(self):
gas = ct.Solution("h2o2.yaml")
gas.TP = 300, ct.one_atm
sim = ct.CounterflowDiffusionFlame(gas, width=0.02)
sim.fuel_inlet.mdot = 0.2
sim.oxidizer_inlet.mdot = 0.2
sim.fuel_inlet.X = "AR:1.0"
sim.oxidizer_inlet.X = "N2:1.0"
sim.fuel_inlet.T = 300
sim.oxidizer_inlet.T = 600

with pytest.raises(ValueError, match="mode must be 'stoich' or 'linear'"):
sim.set_initial_guess(mode="bad-mode")

with pytest.raises(ct.CanteraError, match="Stoichiometric initial guess"):
sim.set_initial_guess()

sim.set_initial_guess(mode="linear")

zrel = (sim.grid - sim.grid[0]) / (sim.grid[-1] - sim.grid[0])
k_ar = gas.species_index("AR")
k_n2 = gas.species_index("N2")

assert sim.T[0] == approx(sim.fuel_inlet.T)
assert sim.T[-1] == approx(sim.oxidizer_inlet.T)
assert sim.Y[k_ar, 0] == approx(1.0)
assert sim.Y[k_n2, 0] == approx(0.0)
assert sim.Y[k_ar, -1] == approx(0.0)
assert sim.Y[k_n2, -1] == approx(1.0)
assert np.allclose(sim.Y[k_ar], 1.0 - zrel)
assert np.allclose(sim.Y[k_n2], zrel)

sim.energy_enabled = False
sim.solve(loglevel=0, refine_grid=False)

def run_restore_diffusionflame(self, fname):
gas = ct.Solution("h2o2.yaml")
sim = ct.CounterflowDiffusionFlame(gas)
Expand Down
Loading