Replies: 7 comments
-
|
Beta Was this translation helpful? Give feedback.
-
Thank you very much for the quick assistance. Regarding 1.: To explain why this is the case, I would like to provide an executable program: flamelet_solver_FiPyWithCantera.zip. I have still calculated the source terms and assigned them to a CellVariable to keep it executable for the time being. To run the code, you only need a Cantera installation, since the abstract 'phase' object is now a Cantera object, which is used to get the thermochemical quantities of interest. However, the principle is exactly the same. To be able to (re-)compute the thermochemical quantities
and eventually determine the source terms
At this point, Cantera expects a scalar value for the temperature and pressure, and a 1D mass fraction array (containing the mass fractions of each species involved in mixture). It is therefore not possible to pass the entire temperature field and a 2D mass fraction array over all species and mixture fractions (i.e. over the entire mesh). It is only possible to evaluate one grid point, i.e. one mixture fraction at a time. Still, I hope there is a workaround that allows updating the source terms in each solution step according to the current values of the solution variables. Do you have any idea how I could handle this problem? I would really like to continue working with FiPy. Final remark: Regarding 2.: |
Beta Was this translation helpful? Give feedback.
-
Those sources definitely complicate things. That's an unfortunate design on Cantera's part, but they're hardly the only C programmers who write bad (by which I mean non-vectorizable) Python interfaces. Here's how I would start to approach things, to at least get the sources to update every sweep (right now, you only get the initial value of the sources, which might be why you're not observing combustion): diff --git a/flamelet_solver_FiPyWithCantera.py b/flamelet_solver_FiPyWithCantera.py
index 26673cf..669a6fb 100644
--- a/flamelet_solver_FiPyWithCantera.py
+++ b/flamelet_solver_FiPyWithCantera.py
@@ -80,15 +80,21 @@ def solve_and_plot(
# declare empty list for the equations
eq_vec = []
+ source_vec = []
+
# 1. define species transport equations
# loop over species
for species_index, species_name in enumerate(phase.species_names):
# append equation
- eq_vec.append(0 == DiffusionTerm(coeff=compute_D(Z_st, SDR_st, mesh), var=phi_vec[species_index]) + compute_S_species(phase, p, phi_vec, mesh, species_index))
+ S_species = CellVariable(mesh=mesh, name=f"S_{species_name}")
+ source_vec.append(S_species)
+ eq_vec.append(0 == DiffusionTerm(coeff=compute_D(Z_st, SDR_st, mesh), var=phi_vec[species_index]) + S_species)
# 2. define temperature equation
# append equation
- eq_vec.append(0 == DiffusionTerm(coeff=compute_D(Z_st, SDR_st, mesh), var=phi_vec[-1]) + compute_S_energy(phase, p, phi_vec, mesh))
+ S_energy = CellVariable(mesh=mesh, name="S_energy")
+ source_vec.append(S_energy)
+ eq_vec.append(0 == DiffusionTerm(coeff=compute_D(Z_st, SDR_st, mesh), var=phi_vec[-1]) + S_energy)
# 3. couple equations
# loop over all equations
@@ -132,6 +138,11 @@ def solve_and_plot(
print("STOP: Maximum number of iterations exceeded.")
break
+ for species_index, (species_name, S_species) in enumerate(zip(phase.species_names, source_vec[:-1])):
+ S_species.value = compute_S_species(phase, p, phi_vec, mesh, species_index)
+
+ S_energy.value = compute_S_energy(phase, p, phi_vec, mesh)
+
# sweep until convergence criterion met
res = coupled_eq.sweep(dt=time_step, solver=solver)
@@ -226,8 +237,7 @@ def compute_S_species(
# set mixture fraction vector
Z_vec = mesh.cellCenters[0]
- # create CellVariable
- S_species = CellVariable(mesh=mesh)
+ S_species = []
# declare array
Y_vec = numerix.zeros(len(phase.species_names))
@@ -253,7 +263,7 @@ def compute_S_species(
omega_vec = phase.net_production_rates * phase.molecular_weights
# store value of species source term at current grid point
- S_species.put(Z_index, omega_vec[species_index_of_interest] / rho)
+ S_species.append(omega_vec[species_index_of_interest] / rho)
return S_species
@@ -275,7 +285,7 @@ def compute_S_energy(
Z_vec = mesh.cellCenters[0]
# create CellVariable
- S_energy = CellVariable(mesh=mesh)
+ S_energy = []
# declare array
Y_vec = numerix.zeros(len(phase.species_names))
@@ -307,7 +317,7 @@ def compute_S_energy(
h_vec = phase.partial_molar_enthalpies / phase.molecular_weights
# store value of energy source term at current grid point
- S_energy.put(Z_index, -numerix.dot(h_vec, omega_vec) / (rho * cp))
+ S_energy.append(-numerix.dot(h_vec, omega_vec) / (rho * cp))
return S_energy Unfortunately, this blows up after three sweeps with
To stabilize things, you may need to employ relaxation (add a Once this is working, I can show you how to make custom Note: Since your equations don't involve |
Beta Was this translation helpful? Give feedback.
-
Once again, thank you very much for the detailed guidance! I have implemented the workaround to calculate the source terms accordingly. However, I did not achieve convergent solver behavior even including the transient terms... Instead, I realized two previously neglected modifications and was able to achieve convergence w/o transient terms, at least for large stoichiometric scalar dissipation rates: flamelet_solver_FiPyWithCantera.zip Details:1. Modification: where It now depends on the density, which in turn depends on the solution variables. As a consequence the diffusion coefficient must now also be updated continuously during the solution process 2. Modification: However, the high stoichiometric scalar dissipation rate (~ high flame strechting) probably causes the flame to extinguish. Finally, I obtain again only the inert mixing solution w/o combustion for the given input parameters. As soon as I reduce its value further or choose unequal injection temperatures, I run into numerical instabilities until the solution diverges or negative temperature values result. Questions:
I am very looking forward to your answers. Thank you in advance! |
Beta Was this translation helpful? Give feedback.
-
You already update everything every sweep. The only way to make things update more often is to decouple and update after the sweep of each successive equation. There are some cases where that can help, e.g., semiconductor drift-diffusion equations, SIMPLE method for fluid flow, ... Usually, though (and definitely in those cases) there's a bunch of other algorithmic stuff or fancy damping going on for each partial solve. Don't get into that mess if you don't have to. Generally speaking, making things more complicated seldom helps with convergence or stability. For troubleshooting, I recommend some combination of:
Once you start getting converged solutions, you can start gradually reintroducing complexity. |
Beta Was this translation helpful? Give feedback.
-
In case you are unaware, what you call the "convective term of the temperature equation" is a consequence of writing: Following the spatial heat equation, here Since, evidently, The expressions Having done all that, however, since and write it as 0 == DiffusionTerm(coeff=D, var=phi_vec[-1]) + source_vec[-1] like you did originally. If, despite all this, you're committed to including such terms, then, yes, If you really, really want to go down this route, I can show you how to remove some of the redundant looping in note: Many treatments of the heat equation (like on Wikipedia) put If this is the case, your terms arising from |
Beta Was this translation helpful? Give feedback.
-
There's evidently a problem with |
Beta Was this translation helpful? Give feedback.
-
Hello,
I have some issues with applying FiPy to my problem and would like to ask for your assistance.
Physical problem description:
I would like to use FiPy to solve the steady-state counterflow diffusion flame problem at constant prescribed background pressure, i.e. the classical "flamelet" equations in mixture fraction coordinates (1D mesh) for combustion modeling. The coupled set of equations is composed of the species transport equations as well as the energy equation:
In the end, I am interested in the chemical mixture composition in terms of mass fractions as a function of mixture fraction$Y_{i}(Z)$ as well as in the temperature profile $T(Z)$ . The mixture fraction represents the local fuel content in the mixture and is restricted to the interval [0, 1]. The scalar dissipation rate, which acts as the diffusion coefficient of the problem under consideration ($D = \chi/2$ ), depends on the mesh (i.e. the mixture fraction) and results from the analytical profile
where the index "st" represents the value at stoichiometric mixing conditions. The value of$\chi_{\mathrm{st}}$ is prescribed and kept constant. The value of $Z_{\mathrm{st}}$ is also constant for a fixed propellant combination and thus known a-priori.
The remaining variables are functions of pressure$p$ and the solution variables $Y_{i}, T$ , i.e. $\dot{\omega_{i}}, \rho, c_{p}, h_{i} = f(p, Y_{i}, T)$ . I can determine their values with the help of another class of my python program, as soon as I know (the pressure,) the mass fraction vector and the temperature.
I would like to apply Dirichlet boundary conditions for the propellant compositions and injection temperatures:
@ oxidizer inlet (corresponds to mixture fraction$Z = 0$ ~ no fuel in mixture)
$Y_{i}(Z=0) = 1$
$Y_{i}(Z=0) = 0$
if i == ox_index:
else:
@ fuel inlet (corresponds to mixture fraction$Z = 1$ ~ pure fuel in mixture)
$Y_{i}(Z=1) = 1$
$Y_{i}(Z=1) = 0$
if i == fu_index:
else:
Furthermore, resonable initial guesses for the profiles of the solution variables are available.
I would like to employ the Generalized Minimum Residual (GMRES) solver method from PETSc.
Code:
I attach the code snippets of my attempt to solve the Flamelet equations with FiPy as .txt file:
solver.txt
Issues:
With this code, I observe a strange solver behavior, which may be due to the fact that both the respective source terms in the species transport equations
$S_{\mathrm{species}} = \frac{\dot{\omega_{i}}}{\rho}$
$S_{\mathrm{energy}} = -\frac{1}{\rho c_{p}} \sum_{i}{h_{i} \dot{\omega}_{i}}$
as well as the source term in the energy equation
are evaluated only once at the beginning and are not updated again according to the current values of the solution variables in each iteration. How can I ensure that the source terms always correspond to the current values of the solution variables and are therefore consistent?
The documentation (https://www.ctcms.nist.gov/fipy/documentation/SOLVERS.html) states that no preconditioners are implemented for PETSc. However, in the generated log file it is stated that the incomplete LU-decomposition ("ilu") is applied. Is the documentation not up to date or is it just a "dummy" entry in the log-file? Since the system of equations is very stiff due to the chemical reaction terms, a suitable preconditioning is probably indispensable. Since Trilinos and PySparse are only available for python 2 and the documentation states that there are no preconditioners available for either PETSc or scipy.sparse solvers as well, I wonder what I could do. Is PyAMG in combination with a scipy solver the only alternative?
I would like to thank you for your help in advance.
Best regards
Beta Was this translation helpful? Give feedback.
All reactions