PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
 
Loading...
Searching...
No Matches
les.h
Go to the documentation of this file.
1#ifndef LES_H
2#define LES_H
3
4#include "variables.h"
5#include "logging.h"
6#include "Metric.h"
7#include "setup.h"
8#include "Filter.h"
9#include "Boundaries.h"
10
11/*================================================================================*
12 * SYMMETRIC TENSOR KERNELS (no LES-specific knowledge) *
13 *================================================================================*/
14
15/**
16 * @brief Forms a symmetric tensor from a vector's outer product with itself.
17 *
18 * Used to build the resolved velocity products the Leonard stress subtracts.
19 *
20 * @param[in] v Vector to square.
21 * @return The tensor `v_i v_j`.
22 */
24
25/**
26 * @brief Forms the linear combination `a*x + b*y`.
27 *
28 * One primitive covers addition, subtraction, and scaling, so tensor algebra at the
29 * call sites reads as algebra instead of six repeated component expressions.
30 *
31 * @param[in] a Scalar multiplying @p x.
32 * @param[in] x First tensor.
33 * @param[in] b Scalar multiplying @p y.
34 * @param[in] y Second tensor.
35 * @return The combined tensor.
36 */
37SymTensor SymTensorCombine(PetscReal a, SymTensor x, PetscReal b, SymTensor y);
38
39/**
40 * @brief Returns the trace `t_kk`.
41 * @param[in] t Tensor to trace.
42 * @return Sum of the three diagonal components.
43 */
44PetscReal SymTensorTrace(SymTensor t);
45
46/**
47 * @brief Removes the isotropic part, returning `t_ij - (1/3) delta_ij t_kk`.
48 *
49 * The Germano model tensor must be deviatoric so that the trace of the Leonard
50 * stress, which the incompressible pressure absorbs, cannot enter the contraction
51 * through discrete divergence error.
52 *
53 * @param[in] t Tensor to project.
54 * @return The trace-free part of @p t.
55 */
57
58/**
59 * @brief Contracts two symmetric tensors as `a_ij b_ij`.
60 *
61 * Only six components are stored, so the three off-diagonal products are counted
62 * twice. Centralising that factor is the point: written out at each call site it is
63 * a silent error whenever one term is missed.
64 *
65 * @param[in] a First tensor.
66 * @param[in] b Second tensor.
67 * @return The full nine-term contraction.
68 */
70
71/**
72 * @brief Applies a symmetric tensor to a vector, returning `t_ij v_j`.
73 *
74 * The three stored off-diagonal components each stand for two entries of the full
75 * tensor, so a caller writing this out by hand has six chances to reference the wrong
76 * one. Centralising it is the same reason `SymTensorContract()` exists.
77 *
78 * @param[in] t Tensor to apply.
79 * @param[in] v Vector to apply it to.
80 * @return The resulting vector.
81 */
83
84/**
85 * @brief Returns the squared Frobenius norm `t_ij t_ij`.
86 * @param[in] t Tensor to measure.
87 * @return The self-contraction of @p t.
88 */
89PetscReal SymTensorNormSq(SymTensor t);
90
91/*================================================================================*
92 * STRAIN RATE AND FILTER WIDTH *
93 *================================================================================*/
94
95/**
96 * @brief Builds the strain-rate tensor and its magnitude from a velocity gradient.
97 *
98 * `S_ij = 0.5 (du_i/dx_j + du_j/dx_i)` and `|S| = sqrt(2 S_ij S_ij)`. This is the
99 * single definition of both quantities; the dynamic procedure and the eddy-viscosity
100 * assembly consume it rather than each expanding the algebra themselves.
101 *
102 * @param[in] dudx Gradient of the x velocity component.
103 * @param[in] dvdx Gradient of the y velocity component.
104 * @param[in] dwdx Gradient of the z velocity component.
105 * @param[out] strain Resolved strain-rate tensor; may be NULL if only the magnitude is wanted.
106 * @param[out] magnitude Strain-rate magnitude `|S|`; may be NULL.
107 * @return PetscErrorCode 0 on success.
108 */
109PetscErrorCode StrainRateFromGradients(Cmpnts dudx, Cmpnts dvdx, Cmpnts dwdx,
110 SymTensor *strain, PetscReal *magnitude);
111
112/**
113 * @brief Computes one cell's grid filter width under the selected width model.
114 *
115 * ::LES_FILTER_WIDTH_CUBE_ROOT_VOLUME uses the cell volume alone and so ignores
116 * anisotropy; the other models take the cell's extents along its own grid directions
117 * from @ref ComputeCellDirectionalExtents, so they follow the cell's shape and are
118 * unchanged by its orientation.
119 *
120 * @param[in] model Width model to apply.
121 * @param[in] aj Inverse cell volume (the Jacobian determinant) at the cell.
122 * @param[in] csi Xi-direction metric vector at the cell.
123 * @param[in] eta Eta-direction metric vector at the cell.
124 * @param[in] zet Zeta-direction metric vector at the cell.
125 * @param[out] delta Resolved filter width.
126 * @return PetscErrorCode 0 on success, or `PETSC_ERR_ARG_OUTOFRANGE` for a
127 * non-positive Jacobian.
128 */
129PetscErrorCode ComputeCellFilterWidth(LESFilterWidthModel model, PetscReal aj,
130 Cmpnts csi, Cmpnts eta, Cmpnts zet, PetscReal *delta);
131
132/*================================================================================*
133 * GERMANO IDENTITY KERNELS *
134 *================================================================================*/
135
136/**
137 * @brief Forms the Leonard stress `L_ij = (u_i u_j)^ - u^_i u^_j`.
138 *
139 * `L_ij` is the stress carried by the scales between the grid filter and the test
140 * filter. It is computable entirely from the resolved field, which is what makes the
141 * dynamic procedure possible.
142 *
143 * @param[in] velocity_filtered Test-filtered resolved velocity `u^_i`.
144 * @param[in] velocity_product_filtered Test filter of the products `(u_i u_j)^`.
145 * @return The Leonard stress tensor.
146 */
147SymTensor LeonardStress(Cmpnts velocity_filtered, SymTensor velocity_product_filtered);
148
149/**
150 * @brief Forms the deviatoric Germano model tensor `M_ij`.
151 *
152 * `M_ij = -2 Delta^2 ( alpha |S^| S^_ij - (|S| S_ij)^ )`, made trace-free.
153 *
154 * The two terms are structurally different and must stay so. The first is the model
155 * evaluated on the test-filtered strain, the product of separately filtered
156 * quantities. The second is the grid-level model formed pointwise and then
157 * test-filtered, that is, the filter of a product. Their difference is the entire
158 * information content of the dynamic procedure: collapsing them into the same
159 * expression yields a well-scaled, flow-responsive, and wrong coefficient that is
160 * not the Germano-Lilly coefficient and cannot be cited as one.
161 *
162 * @param[in] delta Grid filter width at the cell.
163 * @param[in] alpha Squared test-to-grid filter width ratio.
164 * @param[in] strain_magnitude_filtered Test-filtered strain magnitude `|S|^`.
165 * @param[in] strain_filtered Test-filtered strain tensor `S^_ij`.
166 * @param[in] strain_product_filtered Test filter of the product `(|S| S_ij)^`.
167 * @return The deviatoric model tensor.
168 */
169SymTensor GermanoModelTensor(PetscReal delta, PetscReal alpha,
170 PetscReal strain_magnitude_filtered,
171 SymTensor strain_filtered,
172 SymTensor strain_product_filtered);
173
174/*================================================================================*
175 * COEFFICIENT AVERAGING AND LIMITING *
176 *================================================================================*/
177
178/**
179 * @brief Resolves which logical directions the dynamic coefficient is averaged over.
180 *
181 * The resulting selector is passed to `PicurvSpatialRatioAverage()`, which performs
182 * the reduction; this routine only decides which directions it spans.
183 *
184 * ::LES_AVERAGING_LOCAL selects none, ::LES_AVERAGING_GLOBAL selects all three, and
185 * ::LES_AVERAGING_HOMOGENEOUS uses the directions the configuration names, defaulting
186 * to the block's periodic axes when it names none. Periodicity is read from the
187 * boundary face configuration, which is the same source the periodic field
188 * synchronization uses.
189 *
190 * @param[in] user Block context supplying configuration and boundary faces.
191 * @param[out] direction Per-direction averaging flags in `(xi, eta, zeta)` order.
192 * @return PetscErrorCode 0 on success, or `PETSC_ERR_ARG_WRONGSTATE` when
193 * homogeneous averaging is requested but no direction can be resolved.
194 */
195PetscErrorCode ResolveLESAveragingDirections(UserCtx *user, PetscBool direction[3]);
196
197/**
198 * @brief Applies the configured admissible range to one model coefficient.
199 *
200 * The coefficient carries the sign of `<L:M>`; a negative value is backscatter,
201 * energy returning from the unresolved scales, not an error. ::LES_CLIP_NONE keeps
202 * it, leaving stability to the total-viscosity floor applied downstream.
203 *
204 * @param[in] coefficient Raw coefficient from the Germano contraction.
205 * @param[in] config LES configuration supplying the mode and ceiling.
206 * @param[out] limited Set to `PETSC_TRUE` when the value was modified; may be NULL.
207 * @return The coefficient after limiting.
208 */
209PetscReal ClipModelCoefficient(PetscReal coefficient, const LESConfig *config, PetscBool *limited);
210
211/**
212 * @brief Builds the eddy viscosity from a model coefficient.
213 *
214 * `nu_t = C Delta^2 |S|`, floored so that `nu + nu_t >= min_viscosity_ratio * nu`.
215 * The floor constrains the quantity that must stay positive for the momentum
216 * operator to remain well posed, which is why backscatter is limited here rather
217 * than by forcing the coefficient positive.
218 *
219 * @param[in] coefficient Model coefficient, signed.
220 * @param[in] delta Grid filter width at the cell.
221 * @param[in] strain_magnitude Resolved strain magnitude `|S|`.
222 * @param[in] molecular_viscosity Molecular kinematic viscosity.
223 * @param[in] min_viscosity_ratio Lower bound on total viscosity as a multiple of molecular.
224 * @return The admissible eddy viscosity.
225 */
226PetscReal EddyViscosityFromCoefficient(PetscReal coefficient, PetscReal delta,
227 PetscReal strain_magnitude,
228 PetscReal molecular_viscosity,
229 PetscReal min_viscosity_ratio);
230
231/**
232 * @brief WALE eddy viscosity (Nicoud & Ducros 1999) from the resolved velocity gradient.
233 *
234 * `nu_t = (C_w Delta)^2 (S^d_ij S^d_ij)^(3/2) / [(S_ij S_ij)^(5/2) + (S^d_ij S^d_ij)^(5/4)]`,
235 * where `S^d` is the traceless symmetric part of the squared gradient. It vanishes in pure
236 * shear, so a laminar shear layer gets none, and it falls as the cube of wall distance
237 * approaching a wall without a damping function. It needs no coefficient field, no test
238 * filter, and no averaging, so it is valid on geometry with no homogeneous direction.
239 *
240 * @param[in] dudx Gradient of the x velocity component.
241 * @param[in] dvdx Gradient of the y velocity component.
242 * @param[in] dwdx Gradient of the z velocity component.
243 * @param[in] delta Grid filter width at the cell.
244 * @param[in] coefficient Model constant `C_w`.
245 * @return The eddy viscosity, zero where the gradient carries no model invariant.
246 */
247PetscReal WALEEddyViscosity(Cmpnts dudx, Cmpnts dvdx, Cmpnts dwdx,
248 PetscReal delta, PetscReal coefficient);
249
250/**
251 * @brief Vreman eddy viscosity (Vreman 2004), weighting each grid direction by its own spacing.
252 *
253 * `nu_t = c sqrt(B_beta / (alpha_ij alpha_ij))`, with `alpha_ij = du_j/dx_i` and
254 * `beta_ij = sum_m d_i^(m) d_j^(m)`, where `d^(m)` is the change in velocity across the
255 * cell along grid direction `m` - `edges[m] . grad(u)`. On an aligned Cartesian grid that
256 * is Vreman's `Delta_m^2 alpha_mi alpha_mj`; on a curvilinear grid it is the same quantity
257 * measured along the cell's own edges, so the model is resolved per direction rather than
258 * through one scalar width, and unchanged by the grid's orientation. It vanishes in pure
259 * shear and needs no coefficient field, test filter, or averaging.
260 *
261 * @param[in] dudx Gradient of the x velocity component.
262 * @param[in] dvdx Gradient of the y velocity component.
263 * @param[in] dwdx Gradient of the z velocity component.
264 * @param[in] edges Cell edge vectors along xi, eta, zeta, from @ref ComputeCellEdgeVectors.
265 * @param[in] coefficient Model constant `c`.
266 * @return The eddy viscosity, zero where `B_beta` vanishes.
267 */
268PetscReal VremanEddyViscosity(Cmpnts dudx, Cmpnts dvdx, Cmpnts dwdx,
269 const Cmpnts edges[3], PetscReal coefficient);
270
271/**
272 * @brief Returns the modelled subgrid kinetic energy at a cell.
273 *
274 * Uses the Yoshizawa relation `k_sgs = 2 C_I Delta^2 |S|^2`. Reported as a
275 * diagnostic: compared against the resolved turbulent kinetic energy from the
276 * field-statistics pipeline it measures how much of the energy the grid resolves.
277 *
278 * @param[in] yoshizawa_ci Yoshizawa constant `C_I`.
279 * @param[in] delta Grid filter width at the cell.
280 * @param[in] strain_magnitude Resolved strain magnitude `|S|`.
281 * @return The modelled subgrid kinetic energy.
282 */
283PetscReal SubgridKineticEnergy(PetscReal yoshizawa_ci, PetscReal delta, PetscReal strain_magnitude);
284
285/*================================================================================*
286 * RUNTIME ENTRY POINTS *
287 *================================================================================*/
288
289/**
290 * @brief Computes the dynamic Smagorinsky coefficient field for one block.
291 *
292 * Runs the Germano-Lilly procedure: test-filters the resolved field, forms the
293 * Leonard stress and the model tensor, contracts them, averages the two
294 * contractions over the configured directions, limits the quotient, and stores it in
295 * `UserCtx::CS`.
296 *
297 * The stored quantity is the coefficient `C` that multiplies `Delta^2 |S|` in the
298 * eddy viscosity, that is, `Cs^2` rather than `Cs`. It is signed under
299 * ::LES_CLIP_NONE. Reported values are converted to `Cs` at the diagnostic boundary,
300 * where the familiar target near 0.16 applies.
301 *
302 * Only ::DYNAMIC_SMAGORINSKY reaches this routine; the constant model prescribes its
303 * coefficient from configuration and allocates no field. Callers honour
304 * `LESConfig::dynamic_frequency`, and the caller must have refreshed `UserCtx::lUcat`
305 * and its periodic images beforehand.
306 *
307 * @param[in,out] user The user context for a single computational block.
308 * @return PetscErrorCode 0 on success.
309 */
310PetscErrorCode ComputeSmagorinskyConstant(UserCtx *user);
311
312/**
313 * @brief Computes the turbulent eddy viscosity for one block.
314 *
315 * Evaluates `nu_t = C Delta^2 |S|` at every fluid cell and stores it in
316 * `UserCtx::Nu_t`. The coefficient comes from `UserCtx::lCs` for the dynamic model
317 * and directly from `LESConfig::constant_cs` for the constant model, which therefore
318 * needs no coefficient field at all.
319 *
320 * @param[in,out] user The user context for a single computational block.
321 * @return PetscErrorCode 0 on success.
322 */
323PetscErrorCode ComputeEddyViscosityLES(UserCtx *user);
324
325/**
326 * @brief Appends one row of LES coefficient statistics to the run's log directory.
327 *
328 * Writes `les_coefficient.csv`: the effective coefficient reported as `Cs`, its
329 * spatial spread, eddy-viscosity levels, the modelled subgrid energy, and the
330 * fractions of the domain that were backscattering or limited before this step's
331 * clipping. Those last two describe the pre-clipping state, which no stored field
332 * preserves.
333 *
334 * Values are instantaneous volume statistics, not time averages; window averages of
335 * the same model's fields come from the field-statistics pipeline instead.
336 *
337 * @param[in] user The user context for a single computational block.
338 * @return PetscErrorCode 0 on success.
339 */
340PetscErrorCode LogLESDiagnostics(UserCtx *user);
341
342#endif // LES_H
Cmpnts SymTensorTimesVector(SymTensor t, Cmpnts v)
Applies a symmetric tensor to a vector, returning t_ij v_j.
Definition les.c:232
PetscReal SymTensorNormSq(SymTensor t)
Returns the squared Frobenius norm t_ij t_ij.
Definition les.c:250
PetscErrorCode ComputeEddyViscosityLES(UserCtx *user)
Computes the turbulent eddy viscosity for one block.
Definition les.c:952
SymTensor SymTensorCombine(PetscReal a, SymTensor x, PetscReal b, SymTensor y)
Forms the linear combination a*x + b*y.
Definition les.c:165
SymTensor SymTensorSelfOuter(Cmpnts v)
Forms a symmetric tensor from a vector's outer product with itself.
Definition les.c:144
SymTensor SymTensorDeviator(SymTensor t)
Removes the isotropic part, returning t_ij - (1/3) delta_ij t_kk.
Definition les.c:199
PetscReal SubgridKineticEnergy(PetscReal yoshizawa_ci, PetscReal delta, PetscReal strain_magnitude)
Returns the modelled subgrid kinetic energy at a cell.
Definition les.c:595
PetscReal SymTensorTrace(SymTensor t)
Returns the trace t_kk.
Definition les.c:186
PetscReal EddyViscosityFromCoefficient(PetscReal coefficient, PetscReal delta, PetscReal strain_magnitude, PetscReal molecular_viscosity, PetscReal min_viscosity_ratio)
Builds the eddy viscosity from a model coefficient.
Definition les.c:497
SymTensor GermanoModelTensor(PetscReal delta, PetscReal alpha, PetscReal strain_magnitude_filtered, SymTensor strain_filtered, SymTensor strain_product_filtered)
Forms the deviatoric Germano model tensor M_ij.
Definition les.c:374
PetscReal ClipModelCoefficient(PetscReal coefficient, const LESConfig *config, PetscBool *limited)
Applies the configured admissible range to one model coefficient.
Definition les.c:466
PetscErrorCode ComputeCellFilterWidth(LESFilterWidthModel model, PetscReal aj, Cmpnts csi, Cmpnts eta, Cmpnts zet, PetscReal *delta)
Computes one cell's grid filter width under the selected width model.
Definition les.c:296
PetscErrorCode StrainRateFromGradients(Cmpnts dudx, Cmpnts dvdx, Cmpnts dwdx, SymTensor *strain, PetscReal *magnitude)
Builds the strain-rate tensor and its magnitude from a velocity gradient.
Definition les.c:267
PetscErrorCode ResolveLESAveragingDirections(UserCtx *user, PetscBool direction[3])
Resolves which logical directions the dynamic coefficient is averaged over.
Definition les.c:410
SymTensor LeonardStress(Cmpnts velocity_filtered, SymTensor velocity_product_filtered)
Forms the Leonard stress L_ij = (u_i u_j)^ - u^_i u^_j.
Definition les.c:359
PetscReal SymTensorContract(SymTensor a, SymTensor b)
Contracts two symmetric tensors as a_ij b_ij.
Definition les.c:217
PetscErrorCode LogLESDiagnostics(UserCtx *user)
Appends one row of LES coefficient statistics to the run's log directory.
Definition les.c:1045
PetscErrorCode ComputeSmagorinskyConstant(UserCtx *user)
Computes the dynamic Smagorinsky coefficient field for one block.
Definition les.c:715
PetscReal WALEEddyViscosity(Cmpnts dudx, Cmpnts dvdx, Cmpnts dwdx, PetscReal delta, PetscReal coefficient)
WALE eddy viscosity (Nicoud & Ducros 1999) from the resolved velocity gradient.
Definition les.c:520
PetscReal VremanEddyViscosity(Cmpnts dudx, Cmpnts dvdx, Cmpnts dwdx, const Cmpnts edges[3], PetscReal coefficient)
Vreman eddy viscosity (Vreman 2004), weighting each grid direction by its own spacing.
Definition les.c:557
Logging utilities and macros for PETSc-based applications.
Main header file for a complex fluid dynamics solver.
LESFilterWidthModel
Selects how a cell's grid filter width is derived from its metrics.
Definition variables.h:579
A 3D point or vector with PetscScalar components.
Definition variables.h:121
Every user-selectable parameter of the LES closure.
Definition variables.h:629
A symmetric second-order tensor stored by its six independent components.
Definition variables.h:138
User-defined context containing data specific to a single computational grid level.
Definition variables.h:1074