Skip to content

Commit 154d085

Browse files
Flexible setup of flamelet boundary conditions (#2814)
* flexible BC for flamelet equations * add jacobian of the split source terms, rename FLAMELET->FLAME * add python wrapper to flamelet enthalpy --------- Co-authored-by: Pedro Gomes <38071223+pcarruscag@users.noreply.github.com>
1 parent f4ceaab commit 154d085

13 files changed

Lines changed: 398 additions & 20 deletions

Common/include/CConfig.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10265,4 +10265,12 @@ class CConfig {
1026510265
*/
1026610266
const FluidFlamelet_ParsedOptions& GetFlameletParsedOptions() const { return flamelet_ParsedOptions; }
1026710267

10268+
/*!
10269+
* \brief Get the enthalpy BC mode for the flamelet solver.
10270+
* FLOW_MARKERS: derive enthalpy BCs from MARKER_ISOTHERMAL/MARKER_HEATFLUX/MARKER_INLET (temperature-based).
10271+
* SPECIES_MARKERS: take enthalpy BCs directly from MARKER_WALL_SPECIES/MARKER_INLET_SPECIES.
10272+
* \return FLAMELET_ENTHALPY_BC enum value.
10273+
*/
10274+
FLAMELET_ENTHALPY_BC GetFlamelet_Enthalpy_BC() const { return flamelet_ParsedOptions.enthalpy_bc; }
10275+
1026810276
};

Common/include/option_structure.hpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1441,12 +1441,30 @@ static const MapType<std::string, FLAMELET_INIT_TYPE> Flamelet_Init_Map = {
14411441
MakePair("SPARK", FLAMELET_INIT_TYPE::SPARK)
14421442
};
14431443

1444+
/*!
1445+
* \brief Selects the source of wall/inlet enthalpy boundary conditions for the flamelet solver.
1446+
* SPECIES_MARKERS (default): inlet H is taken directly from MARKER_INLET_SPECIES; wall enthalpy BC
1447+
* is obtained from MARKER_WALL_SPECIES.
1448+
* FLOW_MARKERS: inlet H is derived from the MARKER_INLET temperature via a Newton iteration on the
1449+
* LUT (reverse lookup using Z,T) from MARKER_ISOTHERMAL or MARKER_HEATFLUX.
1450+
*/
1451+
enum class FLAMELET_ENTHALPY_BC {
1452+
FLOW_MARKERS, /*!< \brief Derive inlet H from MARKER_INLET T (LUT Newton); walls from MARKER_ISOTHERMAL/MARKER_HEATFLUX. */
1453+
SPECIES_MARKERS, /*!< \brief Take inlet H directly from MARKER_INLET_SPECIES; walls from MARKER_WALL_SPECIES (default). */
1454+
};
1455+
1456+
static const MapType<std::string, FLAMELET_ENTHALPY_BC> Flamelet_Enthalpy_BC_Map = {
1457+
MakePair("FLOW_MARKERS", FLAMELET_ENTHALPY_BC::FLOW_MARKERS)
1458+
MakePair("SPECIES_MARKERS", FLAMELET_ENTHALPY_BC::SPECIES_MARKERS)
1459+
};
1460+
14441461
/*!
14451462
* \brief Structure containing parsed options for flamelet fluid model.
14461463
*/
14471464
struct FluidFlamelet_ParsedOptions {
14481465
///TODO: Add python wrapper initialization option
14491466
FLAMELET_INIT_TYPE ignition_method = FLAMELET_INIT_TYPE::NONE; /*!< \brief Method for solution ignition for flamelet problems. */
1467+
FLAMELET_ENTHALPY_BC enthalpy_bc = FLAMELET_ENTHALPY_BC::SPECIES_MARKERS; /*!< \brief Source of enthalpy BCs: species markers (default, backward-compatible) or flow markers. */
14501468
unsigned short n_scalars = 0; /*!< \brief Number of transported scalars for flamelet LUT approach. */
14511469
unsigned short n_lookups = 0; /*!< \brief Number of lookup variables, for visualization only. */
14521470
unsigned short n_table_sources = 0; /*!< \brief Number of transported scalar source terms for LUT. */

Common/src/CConfig.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1410,6 +1410,10 @@ void CConfig::SetConfig_Options() {
14101410

14111411
/*!\brief FLAME_INIT_METHOD \n DESCRIPTION: Ignition method for flamelet solver \n DEFAULT: no ignition; cold flow only. */
14121412
addEnumOption("FLAME_INIT_METHOD", flamelet_ParsedOptions.ignition_method, Flamelet_Init_Map, FLAMELET_INIT_TYPE::NONE);
1413+
/*!\brief FLAME_ENTHALPY_BC \n DESCRIPTION: enthalpy BC for thermal walls. FLOW_MARKERS (default): enthalpy derived
1414+
from MARKER_ISOTHERMAL temperature or MARKER_HEATFLUX via GetEnthFromTemp. SPECIES_MARKERS: enthalpy and all other
1415+
scalars taken directly from MARKER_WALL_SPECIES or the Python wrapper (SetMarkerCustomScalar). \n DEFAULT: FLOW_MARKERS \ingroup Config */
1416+
addEnumOption("FLAME_ENTHALPY_BC", flamelet_ParsedOptions.enthalpy_bc, Flamelet_Enthalpy_BC_Map, FLAMELET_ENTHALPY_BC::FLOW_MARKERS);
14131417
/*!\brief FLAME_INIT \n DESCRIPTION: flame front initialization using the flamelet model \ingroup Config*/
14141418
addDoubleArrayOption("FLAME_INIT", flamelet_ParsedOptions.flame_init.size(), false, flamelet_ParsedOptions.flame_init.begin());
14151419

SU2_CFD/flamelet_python_bc.md

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
# Flamelet Solver — Python Wrapper Wall Boundary Conditions
2+
3+
This document explains how to use the Python wrapper (`pysu2`) to set
4+
custom per-vertex boundary conditions for the flamelet (FGM) scalar transport
5+
solver, and how the setup differs between the two `FLAMELET_ENTHALPY_BC` modes.
6+
7+
---
8+
9+
## Background
10+
11+
The flamelet solver transports `nVar` scalar variables. Their order is:
12+
13+
| Index | Variable |
14+
|-------|----------|
15+
| 0 | `PROGVAR` (progress variable) |
16+
| 1 | `ENTH` (total enthalpy) |
17+
| 2 … n\_CV−1 | additional control variables (e.g. `MIXFRAC`) |
18+
| n\_CV … nVar−1 | auxiliary / user-defined species |
19+
20+
Two modes control how the enthalpy boundary condition is derived at thermal
21+
walls (`MARKER_ISOTHERMAL`, `MARKER_HEATFLUX`):
22+
23+
- **`FLOW_MARKERS`** — enthalpy is derived from the flow thermal field (wall
24+
temperature via `MARKER_ISOTHERMAL`, or wall heat flux via `MARKER_HEATFLUX`).
25+
- **`SPECIES_MARKERS`** (default) — enthalpy and all other scalars are taken
26+
directly from `MARKER_WALL_SPECIES`, or from the Python wrapper when
27+
`MARKER_PYTHON_CUSTOM` is active.
28+
29+
---
30+
31+
## Mode 1: `FLAMELET_ENTHALPY_BC = FLOW_MARKERS`
32+
33+
### How it works
34+
35+
The C++ function `BC_HeatFlux_Wall` reads the wall heat flux and applies it as
36+
a Neumann condition on `I_ENTH`. When the marker is also listed in
37+
`MARKER_PYTHON_CUSTOM`, it instead reads a **per-vertex heat flux** set by the
38+
Python wrapper.
39+
40+
```
41+
driver.SetMarkerCustomNormalHeatFlux(iMarker, iVertex, q_wall)
42+
→ geometry->CustomBoundaryHeatFlux[iMarker][iVertex]
43+
→ BC_HeatFlux_Wall reads geometry->GetCustomBoundaryHeatFlux(...)
44+
```
45+
46+
> `BC_Isothermal_Wall` in `FLOW_MARKERS` mode always converts the wall
47+
> temperature (from `MARKER_ISOTHERMAL`) to enthalpy via `GetEnthFromTemp`.
48+
> Per-vertex customisation of the enthalpy Dirichlet value is not supported in
49+
> this mode — use `SPECIES_MARKERS` instead.
50+
51+
### Limitations in this mode
52+
53+
- Only `I_ENTH` is reachable via the Python wrapper.
54+
- Non-enthalpy scalars (`PROGVAR`, auxiliary species) receive implicit zero
55+
Neumann at the wall and cannot be set via Python.
56+
57+
### config.cfg
58+
59+
```cfg
60+
FLAMELET_ENTHALPY_BC = FLOW_MARKERS
61+
62+
% Flow solver wall BC — sets KindBC and provides the fallback heat flux value
63+
% used when MARKER_PYTHON_CUSTOM is not active.
64+
MARKER_HEATFLUX = ( burner_wall, 0.0 )
65+
66+
% Enable the per-vertex Python override path.
67+
MARKER_PYTHON_CUSTOM = ( burner_wall )
68+
```
69+
70+
### run.py
71+
72+
```python
73+
import pysu2
74+
75+
driver = pysu2.CSinglezoneDriver("config.cfg", 1, False)
76+
77+
marker_name = "burner_wall"
78+
iMarker = list(driver.GetMarkerTags()).index(marker_name)
79+
n_vertex = driver.GetNumberMarkerNodes(iMarker)
80+
81+
def compute_heat_flux(coord):
82+
"""Heat flux [W/m²], positive = into the domain."""
83+
import math
84+
r = math.sqrt(coord[0]**2 + coord[1]**2)
85+
return -5000.0 * math.exp(-r**2 / 0.01) # Gaussian profile, heat out
86+
87+
for iteration in range(driver.GetNumberIter()):
88+
89+
for iVertex in range(n_vertex):
90+
node_id = driver.GetMarkerNode(iMarker, iVertex)
91+
coord = driver.GetInitialMeshCoord(node_id) # [x, y(, z)]
92+
q_wall = compute_heat_flux(coord)
93+
driver.SetMarkerCustomNormalHeatFlux(iMarker, iVertex, q_wall)
94+
95+
driver.Preprocess(iteration)
96+
driver.Run()
97+
driver.Postprocess()
98+
driver.Monitor(iteration)
99+
driver.Output(iteration)
100+
101+
driver.Finalize()
102+
```
103+
104+
---
105+
106+
## Mode 2: `FLAMELET_ENTHALPY_BC = SPECIES_MARKERS` (default)
107+
108+
### How it works
109+
110+
Both `BC_HeatFlux_Wall` and `BC_Isothermal_Wall` delegate immediately to
111+
`CSpeciesSolver::BC_Wall_Generic`, which processes **all `nVar` scalars**
112+
independently. When `MARKER_PYTHON_CUSTOM` is active, the value for each
113+
scalar is taken from the array set by `driver.SetMarkerCustomScalar()`.
114+
115+
```
116+
driver.SetMarkerCustomScalar(iMarker, iVertex, [val_0, val_1, ..., val_nVar-1])
117+
→ CustomBoundaryScalar[iMarker](iVertex, iVar)
118+
→ BC_Wall_Generic reads CustomBoundaryScalar for every iVar
119+
```
120+
121+
The **type** of BC for each scalar (Dirichlet `VALUE` or Neumann `FLUX`) is
122+
read from `MARKER_WALL_SPECIES` in the config and cannot be changed from
123+
Python. The Python wrapper only overrides the **magnitude** per vertex.
124+
125+
### config.cfg
126+
127+
The example below uses `nVar = 3` (PROGVAR, ENTH, one auxiliary species).
128+
Adjust the number of `BC_TYPE, value` pairs to match the actual number of
129+
scalar variables in your setup.
130+
131+
```cfg
132+
FLAMELET_ENTHALPY_BC = SPECIES_MARKERS
133+
134+
% Flow solver wall BC — still required to set KindBC.
135+
% The choice between MARKER_ISOTHERMAL and MARKER_HEATFLUX only affects
136+
% the flow solver; the species solver uses BC_Wall_Generic in both cases.
137+
MARKER_ISOTHERMAL = ( burner_wall, 300.0 )
138+
139+
% Per-variable BC types for the species solver.
140+
% Format: (marker_name, BC_TYPE, fallback_value, BC_TYPE, fallback_value, ...)
141+
% One BC_TYPE+value pair per scalar variable, in index order.
142+
% BC_TYPE: FLUX → Neumann (value is normal flux density [unit/m²])
143+
% VALUE → Dirichlet (value is the scalar value at the wall)
144+
%
145+
% When MARKER_PYTHON_CUSTOM is active, the fallback_value is ignored and
146+
% the Python-supplied value is used instead. The BC_TYPE is always from config.
147+
%
148+
% idx 0 PROGVAR : zero Neumann (no progress variable source at the wall)
149+
% idx 1 ENTH : Dirichlet (enthalpy value set per-vertex by Python)
150+
% idx 2 aux_1 : zero Neumann (no auxiliary species flux at the wall)
151+
MARKER_WALL_SPECIES = ( burner_wall, FLUX, 0.0, VALUE, 0.0, FLUX, 0.0 )
152+
153+
% Enable the per-vertex Python override path.
154+
MARKER_PYTHON_CUSTOM = ( burner_wall )
155+
```
156+
157+
### run.py
158+
159+
```python
160+
import pysu2
161+
162+
driver = pysu2.CSinglezoneDriver("config.cfg", 1, False)
163+
164+
marker_name = "burner_wall"
165+
iMarker = list(driver.GetMarkerTags()).index(marker_name)
166+
n_vertex = driver.GetNumberMarkerNodes(iMarker)
167+
168+
def compute_wall_enthalpy(coord):
169+
"""Return wall enthalpy [J/kg] at this vertex."""
170+
# Example: uniform value corresponding to T=300 K from your LUT.
171+
return 3.5e5
172+
173+
for iteration in range(driver.GetNumberIter()):
174+
175+
for iVertex in range(n_vertex):
176+
node_id = driver.GetMarkerNode(iMarker, iVertex)
177+
coord = driver.GetInitialMeshCoord(node_id)
178+
179+
enth_wall = compute_wall_enthalpy(coord)
180+
181+
# Pass one value per scalar variable (length = nVar).
182+
# PROGVAR (idx 0): 0.0 → applied as FLUX → zero Neumann (BC_TYPE from config)
183+
# ENTH (idx 1): enth_wall → applied as VALUE → Dirichlet
184+
# aux_1 (idx 2): 0.0 → applied as FLUX → zero Neumann
185+
driver.SetMarkerCustomScalar(iMarker, iVertex, [0.0, enth_wall, 0.0])
186+
187+
driver.Preprocess(iteration)
188+
driver.Run()
189+
driver.Postprocess()
190+
driver.Monitor(iteration)
191+
driver.Output(iteration)
192+
193+
driver.Finalize()
194+
```
195+
196+
---
197+
198+
## Comparison
199+
200+
| | `FLOW_MARKERS` | `SPECIES_MARKERS` |
201+
|---|---|---|
202+
| **Python API** | `SetMarkerCustomNormalHeatFlux` | `SetMarkerCustomScalar` |
203+
| **Storage** | `geometry->CustomBoundaryHeatFlux` | `CustomBoundaryScalar[iMarker](iVertex, iVar)` |
204+
| **What you set** | Per-vertex heat flux (W/m²) for `I_ENTH` only | Per-vertex values for **all** `nVar` scalars |
205+
| **BC type control** | Hard-coded Neumann in C++ | Per-variable via `MARKER_WALL_SPECIES` in config |
206+
| **`MARKER_ISOTHERMAL` walls** | Enthalpy computed from T via `GetEnthFromTemp`; not overridable by Python | Enthalpy set as Dirichlet `VALUE` from Python |
207+
| **Non-enthalpy scalars** | Not reachable via Python | All controlled via the same `SetMarkerCustomScalar` call |
208+
| **Required config keys** | `MARKER_HEATFLUX`, `MARKER_PYTHON_CUSTOM` | `MARKER_ISOTHERMAL` or `MARKER_HEATFLUX`, `MARKER_WALL_SPECIES`, `MARKER_PYTHON_CUSTOM` |
209+
210+
---
211+
212+
## Notes
213+
214+
- `MARKER_PYTHON_CUSTOM` is a purely additive flag. A marker can appear in
215+
both `MARKER_HEATFLUX` (or `MARKER_ISOTHERMAL`) and `MARKER_PYTHON_CUSTOM`
216+
simultaneously.
217+
- The fallback values in `MARKER_WALL_SPECIES` are used when `py_custom` is
218+
false (e.g., during a plain SU2\_CFD run without the Python driver). They
219+
allow the same config to be used both ways.
220+
- In `SPECIES_MARKERS` mode, `SetMarkerCustomScalar` must supply a vector of
221+
exactly `nVar` values. Indices that correspond to `FLUX`-type variables
222+
should receive `0.0` unless you intentionally want a non-zero flux there.
223+
- The `CHT` (conjugate heat transfer) path (`BC_ConjugateHeat_Interface`)
224+
always uses `BC_Isothermal_Wall_Generic` and is unaffected by
225+
`FLAMELET_ENTHALPY_BC`. Python custom BCs are not applicable to CHT markers.

SU2_CFD/include/solvers/CSpeciesFlameletSolver.hpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,21 @@ class CSpeciesFlameletSolver final : public CSpeciesSolver {
162162
void BC_Isothermal_Wall(CGeometry* geometry, CSolver** solver_container, CNumerics* conv_numerics,
163163
CNumerics* visc_numerics, CConfig* config, unsigned short val_marker) override;
164164

165+
/*!
166+
* \brief Impose the heat-flux wall boundary condition on the flamelet enthalpy scalar.
167+
* When FLAMELET_ENTHALPY_BC= FLOW_MARKERS (default), the heat flux value from MARKER_HEATFLUX
168+
* is applied as a Neumann boundary condition on the enthalpy scalar H.
169+
* When FLAMELET_ENTHALPY_BC= SPECIES_MARKERS, use flux/value from MARKER_WALL_SPECIES.
170+
* \param[in] geometry - Geometrical definition of the problem.
171+
* \param[in] solver_container - Container vector with all the solutions.
172+
* \param[in] conv_numerics - Description of the numerical method.
173+
* \param[in] visc_numerics - Description of the numerical method.
174+
* \param[in] config - Definition of the particular problem.
175+
* \param[in] val_marker - Surface marker where the boundary condition is applied.
176+
*/
177+
void BC_HeatFlux_Wall(CGeometry* geometry, CSolver** solver_container, CNumerics* conv_numerics,
178+
CNumerics* visc_numerics, CConfig* config, unsigned short val_marker) override;
179+
165180
/*!
166181
* \brief Impose the inlet boundary condition.
167182
* \param[in] geometry - Geometrical definition of the problem.

SU2_CFD/include/variables/CSpeciesFlameletVariable.hpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class CSpeciesFlameletVariable final : public CSpeciesVariable {
3838
MatrixType source_scalar; /*!< \brief Vector of the source terms from the lookup table for each scalar equation */
3939
MatrixType lookup_scalar; /*!< \brief Vector of the source terms from the lookup table for each scalar equation */
4040
su2vector<unsigned short> table_misses; /*!< \brief Vector of lookup table misses. */
41+
MatrixType source_cons_jac; /*!< \brief Consumption-rate Jacobian dS_aux_i/dY_aux_i = source_cons_i, one column per user scalar. */
4142

4243
public:
4344
/*!
@@ -87,4 +88,18 @@ class CSpeciesFlameletVariable final : public CSpeciesVariable {
8788
inline void SetTableMisses(unsigned long iPoint, unsigned short misses) override { table_misses[iPoint] = misses; }
8889

8990
inline unsigned short GetTableMisses(unsigned long iPoint) const override { return table_misses[iPoint]; }
91+
92+
/*!
93+
* \brief Store the consumption-rate Jacobian dS_aux_i/dY_aux_i = source_cons_i for user scalar i_aux.
94+
*/
95+
inline void SetAuxSourceCons(unsigned long iPoint, unsigned long i_aux, su2double val) {
96+
source_cons_jac(iPoint, i_aux) = val;
97+
}
98+
99+
/*!
100+
* \brief Get the consumption-rate Jacobian entry for user scalar i_aux at iPoint.
101+
*/
102+
inline su2double GetAuxSourceCons(unsigned long iPoint, unsigned long i_aux) const {
103+
return source_cons_jac(iPoint, i_aux);
104+
}
90105
};

SU2_CFD/src/solvers/CIncEulerSolver.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1183,7 +1183,7 @@ void CIncEulerSolver::Centered_Residual(CGeometry *geometry, CSolver **solver_co
11831183

11841184
if (LD2_Scheme) {
11851185
numerics->SetPrimVarGradient(nodes->GetGradient_Primitive(iPoint), nodes->GetGradient_Primitive(jPoint));
1186-
if (!geometry->nodes->GetPeriodicBoundary(iPoint) || (geometry->nodes->GetPeriodicBoundary(iPoint)
1186+
if (!geometry->nodes->GetPeriodicBoundary(iPoint) || (geometry->nodes->GetPeriodicBoundary(iPoint)
11871187
&& !geometry->nodes->GetPeriodicBoundary(jPoint))) {
11881188
numerics->SetCoord(geometry->nodes->GetCoord(iPoint), geometry->nodes->GetCoord(jPoint));
11891189
} else {
@@ -2535,11 +2535,19 @@ void CIncEulerSolver::BC_Inlet(CGeometry *geometry, CSolver **solver_container,
25352535
if (species_model) scalar_inlet = config->GetInlet_SpeciesVal(config->GetMarker_All_TagBound(val_marker));
25362536
CFluidModel* auxFluidModel = solver_container[FLOW_SOL]->GetFluidModel();
25372537
auxFluidModel->SetTDState_T(V_inlet[prim_idx.Temperature()], scalar_inlet);
2538-
V_inlet[prim_idx.Enthalpy()] = auxFluidModel->GetEnthalpy();
2538+
2539+
/*--- For the flamelet model with FLOW_MARKERS enthalpy BC, we obtain the inlet enthalpy
2540+
from the flamelet species solver With SPECIES_MARKERS, the enthalpy in MARKER_INLET_SPECIES
2541+
is used directly. ---*/
2542+
if (config->GetKind_Species_Model() == SPECIES_MODEL::FLAMELET &&
2543+
config->GetFlamelet_Enthalpy_BC() == FLAMELET_ENTHALPY_BC::FLOW_MARKERS)
2544+
V_inlet[prim_idx.Enthalpy()] = nodes->GetEnthalpy(iPoint);
2545+
else
2546+
V_inlet[prim_idx.Enthalpy()] = auxFluidModel->GetEnthalpy();
25392547

25402548
/*--- Access density at the node. This is either constant by
2541-
construction, or will be set fixed implicitly by the temperature
2542-
and equation of state. ---*/
2549+
construction, or will be set fixed implicitly by the temperature
2550+
and equation of state. ---*/
25432551

25442552
V_inlet[prim_idx.Density()] = nodes->GetDensity(iPoint);
25452553

0 commit comments

Comments
 (0)