PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
 
Loading...
Searching...
No Matches
Functions
les.h File Reference
#include "variables.h"
#include "logging.h"
#include "Metric.h"
#include "setup.h"
#include "Filter.h"
#include "Boundaries.h"
Include dependency graph for les.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

SymTensor SymTensorSelfOuter (Cmpnts v)
 Forms a symmetric tensor from a vector's outer product with itself.
 
SymTensor SymTensorCombine (PetscReal a, SymTensor x, PetscReal b, SymTensor y)
 Forms the linear combination a*x + b*y.
 
PetscReal SymTensorTrace (SymTensor t)
 Returns the trace t_kk.
 
SymTensor SymTensorDeviator (SymTensor t)
 Removes the isotropic part, returning t_ij - (1/3) delta_ij t_kk.
 
PetscReal SymTensorContract (SymTensor a, SymTensor b)
 Contracts two symmetric tensors as a_ij b_ij.
 
Cmpnts SymTensorTimesVector (SymTensor t, Cmpnts v)
 Applies a symmetric tensor to a vector, returning t_ij v_j.
 
PetscReal SymTensorNormSq (SymTensor t)
 Returns the squared Frobenius norm t_ij t_ij.
 
PetscErrorCode StrainRateFromGradients (Cmpnts dudx, Cmpnts dvdx, Cmpnts dwdx, SymTensor *strain, PetscReal *magnitude)
 Builds the strain-rate tensor and its magnitude from a velocity gradient.
 
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.
 
SymTensor LeonardStress (Cmpnts velocity_filtered, SymTensor velocity_product_filtered)
 Forms the Leonard stress L_ij = (u_i u_j)^ - u^_i u^_j.
 
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.
 
PetscErrorCode ResolveLESAveragingDirections (UserCtx *user, PetscBool direction[3])
 Resolves which logical directions the dynamic coefficient is averaged over.
 
PetscReal ClipModelCoefficient (PetscReal coefficient, const LESConfig *config, PetscBool *limited)
 Applies the configured admissible range to one model coefficient.
 
PetscReal EddyViscosityFromCoefficient (PetscReal coefficient, PetscReal delta, PetscReal strain_magnitude, PetscReal molecular_viscosity, PetscReal min_viscosity_ratio)
 Builds the eddy viscosity from a model coefficient.
 
PetscReal WALEEddyViscosity (Cmpnts dudx, Cmpnts dvdx, Cmpnts dwdx, PetscReal delta, PetscReal coefficient)
 WALE eddy viscosity (Nicoud & Ducros 1999) from the resolved velocity gradient.
 
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.
 
PetscReal SubgridKineticEnergy (PetscReal yoshizawa_ci, PetscReal delta, PetscReal strain_magnitude)
 Returns the modelled subgrid kinetic energy at a cell.
 
PetscErrorCode ComputeSmagorinskyConstant (UserCtx *user)
 Computes the dynamic Smagorinsky coefficient field for one block.
 
PetscErrorCode ComputeEddyViscosityLES (UserCtx *user)
 Computes the turbulent eddy viscosity for one block.
 
PetscErrorCode LogLESDiagnostics (UserCtx *user)
 Appends one row of LES coefficient statistics to the run's log directory.
 

Function Documentation

◆ SymTensorSelfOuter()

SymTensor SymTensorSelfOuter ( Cmpnts  v)

Forms a symmetric tensor from a vector's outer product with itself.

Used to build the resolved velocity products the Leonard stress subtracts.

Parameters
[in]vVector to square.
Returns
The tensor v_i v_j.

Forms a symmetric tensor from a vector's outer product with itself.

Full API contract is documented with the header declaration in include/les.h.

See also
SymTensorSelfOuter()

Definition at line 144 of file les.c.

145{
146 SymTensor t;
147
148 t.xx = v.x * v.x;
149 t.xy = v.x * v.y;
150 t.xz = v.x * v.z;
151 t.yy = v.y * v.y;
152 t.yz = v.y * v.z;
153 t.zz = v.z * v.z;
154 return t;
155}
PetscReal yy
Definition variables.h:139
PetscScalar x
Definition variables.h:122
PetscScalar z
Definition variables.h:122
PetscReal xx
Definition variables.h:139
PetscScalar y
Definition variables.h:122
PetscReal yz
Definition variables.h:139
PetscReal zz
Definition variables.h:139
PetscReal xz
Definition variables.h:139
PetscReal xy
Definition variables.h:139
A symmetric second-order tensor stored by its six independent components.
Definition variables.h:138
Here is the caller graph for this function:

◆ SymTensorCombine()

SymTensor SymTensorCombine ( PetscReal  a,
SymTensor  x,
PetscReal  b,
SymTensor  y 
)

Forms the linear combination a*x + b*y.

One primitive covers addition, subtraction, and scaling, so tensor algebra at the call sites reads as algebra instead of six repeated component expressions.

Parameters
[in]aScalar multiplying x.
[in]xFirst tensor.
[in]bScalar multiplying y.
[in]ySecond tensor.
Returns
The combined tensor.

Forms the linear combination a*x + b*y.

Full API contract is documented with the header declaration in include/les.h.

See also
SymTensorCombine()

Definition at line 165 of file les.c.

166{
167 SymTensor t;
168
169 t.xx = a * x.xx + b * y.xx;
170 t.xy = a * x.xy + b * y.xy;
171 t.xz = a * x.xz + b * y.xz;
172 t.yy = a * x.yy + b * y.yy;
173 t.yz = a * x.yz + b * y.yz;
174 t.zz = a * x.zz + b * y.zz;
175 return t;
176}
Here is the caller graph for this function:

◆ SymTensorTrace()

PetscReal SymTensorTrace ( SymTensor  t)

Returns the trace t_kk.

Parameters
[in]tTensor to trace.
Returns
Sum of the three diagonal components.

Returns the trace t_kk.

Full API contract is documented with the header declaration in include/les.h.

See also
SymTensorTrace()

Definition at line 186 of file les.c.

187{
188 return t.xx + t.yy + t.zz;
189}
Here is the caller graph for this function:

◆ SymTensorDeviator()

SymTensor SymTensorDeviator ( SymTensor  t)

Removes the isotropic part, returning t_ij - (1/3) delta_ij t_kk.

The Germano model tensor must be deviatoric so that the trace of the Leonard stress, which the incompressible pressure absorbs, cannot enter the contraction through discrete divergence error.

Parameters
[in]tTensor to project.
Returns
The trace-free part of t.

Removes the isotropic part, returning t_ij - (1/3) delta_ij t_kk.

Full API contract is documented with the header declaration in include/les.h.

See also
SymTensorDeviator()

Definition at line 199 of file les.c.

200{
201 const PetscReal isotropic = SymTensorTrace(t) / 3.0;
202
203 t.xx -= isotropic;
204 t.yy -= isotropic;
205 t.zz -= isotropic;
206 return t;
207}
PetscReal SymTensorTrace(SymTensor t)
Implementation of SymTensorTrace().
Definition les.c:186
Here is the call graph for this function:
Here is the caller graph for this function:

◆ SymTensorContract()

PetscReal SymTensorContract ( SymTensor  a,
SymTensor  b 
)

Contracts two symmetric tensors as a_ij b_ij.

Only six components are stored, so the three off-diagonal products are counted twice. Centralising that factor is the point: written out at each call site it is a silent error whenever one term is missed.

Parameters
[in]aFirst tensor.
[in]bSecond tensor.
Returns
The full nine-term contraction.

Contracts two symmetric tensors as a_ij b_ij.

Full API contract is documented with the header declaration in include/les.h.

See also
SymTensorContract()

Definition at line 217 of file les.c.

218{
219 // The three off-diagonal components each stand for two entries of the full tensor.
220 return a.xx * b.xx + a.yy * b.yy + a.zz * b.zz +
221 2.0 * (a.xy * b.xy + a.xz * b.xz + a.yz * b.yz);
222}
Here is the caller graph for this function:

◆ SymTensorTimesVector()

Cmpnts SymTensorTimesVector ( SymTensor  t,
Cmpnts  v 
)

Applies a symmetric tensor to a vector, returning t_ij v_j.

The three stored off-diagonal components each stand for two entries of the full tensor, so a caller writing this out by hand has six chances to reference the wrong one. Centralising it is the same reason SymTensorContract() exists.

Parameters
[in]tTensor to apply.
[in]vVector to apply it to.
Returns
The resulting vector.

Applies a symmetric tensor to a vector, returning t_ij v_j.

Full API contract is documented with the header declaration in include/les.h.

See also
SymTensorTimesVector()

Definition at line 232 of file les.c.

233{
234 Cmpnts result;
235
236 result.x = t.xx * v.x + t.xy * v.y + t.xz * v.z;
237 result.y = t.xy * v.x + t.yy * v.y + t.yz * v.z;
238 result.z = t.xz * v.x + t.yz * v.y + t.zz * v.z;
239 return result;
240}
A 3D point or vector with PetscScalar components.
Definition variables.h:121
Here is the caller graph for this function:

◆ SymTensorNormSq()

PetscReal SymTensorNormSq ( SymTensor  t)

Returns the squared Frobenius norm t_ij t_ij.

Parameters
[in]tTensor to measure.
Returns
The self-contraction of t.

Returns the squared Frobenius norm t_ij t_ij.

Full API contract is documented with the header declaration in include/les.h.

See also
SymTensorNormSq()

Definition at line 250 of file les.c.

251{
252 return SymTensorContract(t, t);
253}
PetscReal SymTensorContract(SymTensor a, SymTensor b)
Implementation of SymTensorContract().
Definition les.c:217
Here is the call graph for this function:
Here is the caller graph for this function:

◆ StrainRateFromGradients()

PetscErrorCode StrainRateFromGradients ( Cmpnts  dudx,
Cmpnts  dvdx,
Cmpnts  dwdx,
SymTensor *  strain,
PetscReal *  magnitude 
)

Builds the strain-rate tensor and its magnitude from a velocity gradient.

S_ij = 0.5 (du_i/dx_j + du_j/dx_i) and |S| = sqrt(2 S_ij S_ij). This is the single definition of both quantities; the dynamic procedure and the eddy-viscosity assembly consume it rather than each expanding the algebra themselves.

Parameters
[in]dudxGradient of the x velocity component.
[in]dvdxGradient of the y velocity component.
[in]dwdxGradient of the z velocity component.
[out]strainResolved strain-rate tensor; may be NULL if only the magnitude is wanted.
[out]magnitudeStrain-rate magnitude |S|; may be NULL.
Returns
PetscErrorCode 0 on success.

Builds the strain-rate tensor and its magnitude from a velocity gradient.

Full API contract is documented with the header declaration in include/les.h.

See also
StrainRateFromGradients()

Definition at line 267 of file les.c.

269{
270 SymTensor s;
271
272 PetscFunctionBeginUser;
273
274 s.xx = dudx.x;
275 s.xy = 0.5 * (dudx.y + dvdx.x);
276 s.xz = 0.5 * (dudx.z + dwdx.x);
277 s.yy = dvdx.y;
278 s.yz = 0.5 * (dvdx.z + dwdx.y);
279 s.zz = dwdx.z;
280
281 if (strain) *strain = s;
282 // |S| = sqrt(2 S_ij S_ij), with the off-diagonal doubling handled by the contraction.
283 if (magnitude) *magnitude = PetscSqrtReal(2.0 * SymTensorNormSq(s));
284
285 PetscFunctionReturn(0);
286}
PetscReal SymTensorNormSq(SymTensor t)
Implementation of SymTensorNormSq().
Definition les.c:250
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ComputeCellFilterWidth()

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.

LES_FILTER_WIDTH_CUBE_ROOT_VOLUME uses the cell volume alone and so ignores anisotropy; the other models take the cell's extents along its own grid directions from ComputeCellDirectionalExtents, so they follow the cell's shape and are unchanged by its orientation.

Parameters
[in]modelWidth model to apply.
[in]ajInverse cell volume (the Jacobian determinant) at the cell.
[in]csiXi-direction metric vector at the cell.
[in]etaEta-direction metric vector at the cell.
[in]zetZeta-direction metric vector at the cell.
[out]deltaResolved filter width.
Returns
PetscErrorCode 0 on success, or PETSC_ERR_ARG_OUTOFRANGE for a non-positive Jacobian.

Computes one cell's grid filter width under the selected width model.

Full API contract is documented with the header declaration in include/les.h.

See also
ComputeCellFilterWidth()

Definition at line 296 of file les.c.

298{
299 double dx = 0.0, dy = 0.0, dz = 0.0;
300
301 PetscFunctionBeginUser;
302 PetscCheck(delta != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
303 "Filter width destination cannot be NULL.");
304 PetscCheck(aj > 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE,
305 "Cell Jacobian must be positive to define a filter width; received %g.", (double)aj);
306
308 // Cell volume alone: exact for a cube, and increasingly optimistic as the
309 // cell is stretched, because it averages the three extents geometrically.
310 *delta = PetscPowReal(1.0 / aj, 1.0 / 3.0);
311 PetscFunctionReturn(0);
312 }
313
314 // The cell's own extents, not the Cartesian components of its diagonal: a filter
315 // width that changed when the same cell was rotated would hand a bend nine times the
316 // eddy viscosity of an identical cell in a straight section.
317 PetscCall(ComputeCellDirectionalExtents(aj, csi, eta, zet, &dx, &dy, &dz));
318
319 switch (model) {
321 // The largest unresolved scale the cell can carry; conservative on stretched grids.
322 *delta = PetscMax(dx, PetscMax(dy, dz));
323 break;
325 // Scotti, Meneveau & Lilly (1993): the cube root of the extents, multiplied by
326 // f(a1, a2), where a1 and a2 are the two shorter extents over the longest. f is 1
327 // for a cube and grows with the cell's distortion. It assumes the cut-off lies in
328 // the inertial range in every direction, which a very long cell violates.
329 const double longest = PetscMax(dx, PetscMax(dy, dz));
330 const double shortest = PetscMin(dx, PetscMin(dy, dz));
331 const double middle = dx + dy + dz - longest - shortest;
332 const double ln_a1 = log(shortest / longest), ln_a2 = log(middle / longest);
333 const double shape = cosh(sqrt((4.0 / 27.0) *
334 (ln_a1 * ln_a1 - ln_a1 * ln_a2 + ln_a2 * ln_a2)));
335 *delta = PetscPowReal(PetscMax(dx * dy * dz, LES_EPSILON), 1.0 / 3.0) * shape;
336 break;
337 }
339 default:
340 *delta = PetscPowReal(PetscMax(dx * dy * dz, LES_EPSILON), 1.0 / 3.0);
341 break;
342 }
343
344 PetscFunctionReturn(0);
345}
PetscErrorCode ComputeCellDirectionalExtents(PetscReal ajc, Cmpnts csi, Cmpnts eta, Cmpnts zet, double *l_xi, double *l_eta, double *l_zeta)
Computes a cell's extent along each of its own grid directions.
Definition Metric.c:328
static const double LES_EPSILON
Definition les.c:31
@ LES_FILTER_WIDTH_SCOTTI
Definition variables.h:583
@ LES_FILTER_WIDTH_GEOMETRIC_MEAN
Definition variables.h:581
@ LES_FILTER_WIDTH_CUBE_ROOT_VOLUME
Definition variables.h:580
@ LES_FILTER_WIDTH_MAX_EDGE
Definition variables.h:582
Here is the call graph for this function:
Here is the caller graph for this function:

◆ LeonardStress()

SymTensor LeonardStress ( Cmpnts  velocity_filtered,
SymTensor  velocity_product_filtered 
)

Forms the Leonard stress L_ij = (u_i u_j)^ - u^_i u^_j.

L_ij is the stress carried by the scales between the grid filter and the test filter. It is computable entirely from the resolved field, which is what makes the dynamic procedure possible.

Parameters
[in]velocity_filteredTest-filtered resolved velocity u^_i.
[in]velocity_product_filteredTest filter of the products (u_i u_j)^.
Returns
The Leonard stress tensor.

Forms the Leonard stress L_ij = (u_i u_j)^ - u^_i u^_j.

Full API contract is documented with the header declaration in include/les.h.

See also
LeonardStress()

Definition at line 359 of file les.c.

360{
361 // L_ij = (u_i u_j)^ - u^_i u^_j : the stress carried between the two filter widths.
362 return SymTensorCombine(1.0, velocity_product_filtered,
363 -1.0, SymTensorSelfOuter(velocity_filtered));
364}
SymTensor SymTensorCombine(PetscReal a, SymTensor x, PetscReal b, SymTensor y)
Implementation of SymTensorCombine().
Definition les.c:165
SymTensor SymTensorSelfOuter(Cmpnts v)
Implementation of SymTensorSelfOuter().
Definition les.c:144
Here is the call graph for this function:
Here is the caller graph for this function:

◆ GermanoModelTensor()

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.

M_ij = -2 Delta^2 ( alpha |S^| S^_ij - (|S| S_ij)^ ), made trace-free.

The two terms are structurally different and must stay so. The first is the model evaluated on the test-filtered strain, the product of separately filtered quantities. The second is the grid-level model formed pointwise and then test-filtered, that is, the filter of a product. Their difference is the entire information content of the dynamic procedure: collapsing them into the same expression yields a well-scaled, flow-responsive, and wrong coefficient that is not the Germano-Lilly coefficient and cannot be cited as one.

Parameters
[in]deltaGrid filter width at the cell.
[in]alphaSquared test-to-grid filter width ratio.
[in]strain_magnitude_filteredTest-filtered strain magnitude |S|^.
[in]strain_filteredTest-filtered strain tensor S^_ij.
[in]strain_product_filteredTest filter of the product (|S| S_ij)^.
Returns
The deviatoric model tensor.

Forms the deviatoric Germano model tensor M_ij.

Full API contract is documented with the header declaration in include/les.h.

See also
GermanoModelTensor()

Definition at line 374 of file les.c.

378{
379 // Term one: the model evaluated on the test-filtered strain. Both factors were
380 // filtered separately, then multiplied.
381 const SymTensor test_scale = SymTensorCombine(alpha * strain_magnitude_filtered,
382 strain_filtered, 0.0, strain_filtered);
383
384 // Term two: the grid-scale model formed pointwise and then test-filtered. This is
385 // the filter of a product, and it is not equal to the product of the filtered
386 // factors above. That inequality carries the whole information content of the
387 // dynamic procedure.
388 const SymTensor grid_scale = strain_product_filtered;
389
390 const SymTensor model = SymTensorCombine(-2.0 * delta * delta, test_scale,
391 2.0 * delta * delta, grid_scale);
392
393 // Trace-free, so the Leonard stress's trace cannot enter the contraction through
394 // whatever discrete divergence the velocity field carries.
395 return SymTensorDeviator(model);
396}
SymTensor SymTensorDeviator(SymTensor t)
Implementation of SymTensorDeviator().
Definition les.c:199
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ResolveLESAveragingDirections()

PetscErrorCode ResolveLESAveragingDirections ( UserCtx *  user,
PetscBool  direction[3] 
)

Resolves which logical directions the dynamic coefficient is averaged over.

The resulting selector is passed to PicurvSpatialRatioAverage(), which performs the reduction; this routine only decides which directions it spans.

LES_AVERAGING_LOCAL selects none, LES_AVERAGING_GLOBAL selects all three, and LES_AVERAGING_HOMOGENEOUS uses the directions the configuration names, defaulting to the block's periodic axes when it names none. Periodicity is read from the boundary face configuration, which is the same source the periodic field synchronization uses.

Parameters
[in]userBlock context supplying configuration and boundary faces.
[out]directionPer-direction averaging flags in (xi, eta, zeta) order.
Returns
PetscErrorCode 0 on success, or PETSC_ERR_ARG_WRONGSTATE when homogeneous averaging is requested but no direction can be resolved.

Resolves which logical directions the dynamic coefficient is averaged over.

Full API contract is documented with the header declaration in include/les.h.

See also
ResolveLESAveragingDirections()

Definition at line 410 of file les.c.

411{
412 const LESConfig *config = &user->simCtx->les_config;
413 PetscInt selected = 0;
414 PetscBool periodic[3];
415
416 PetscFunctionBeginUser;
417 PetscCheck(direction != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
418 "Averaging direction destination cannot be NULL.");
419 LESPeriodicAxes(user->simCtx, periodic);
420
421 for (PetscInt axis = 0; axis < 3; axis++) direction[axis] = PETSC_FALSE;
422
423 switch (config->averaging_mode) {
425 // No averaging: the coefficient is whatever the single cell's contractions give.
426 PetscFunctionReturn(0);
427
429 for (PetscInt axis = 0; axis < 3; axis++) direction[axis] = PETSC_TRUE;
430 PetscFunctionReturn(0);
431
433 default:
434 for (PetscInt axis = 0; axis < 3; axis++) {
435 if (config->averaging_direction[axis]) {
436 direction[axis] = PETSC_TRUE;
437 selected++;
438 }
439 }
440 // Naming no direction asks for the periodic axes, which is where the flow is
441 // homogeneous in every case the solver can currently express.
442 if (selected == 0) {
443 for (PetscInt axis = 0; axis < 3; axis++) {
444 if (periodic[axis]) {
445 direction[axis] = PETSC_TRUE;
446 selected++;
447 }
448 }
449 }
450 PetscCheck(selected > 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE,
451 "Homogeneous LES averaging was requested, but this block declares no "
452 "periodic axis and the configuration names no direction. Name the "
453 "homogeneous directions explicitly or select local averaging.");
454 PetscFunctionReturn(0);
455 }
456}
static void LESPeriodicAxes(const SimCtx *simCtx, PetscBool periodic[3])
Reports the block's per-axis periodicity in (xi, eta, zeta) order.
Definition les.c:129
LESConfig les_config
Parameters of the LES closure selected by les.
Definition variables.h:987
SimCtx * simCtx
Back-pointer to the master simulation context.
Definition variables.h:1077
PetscBool averaging_direction[3]
Averaged-over logical directions (xi, eta, zeta).
Definition variables.h:638
@ LES_AVERAGING_LOCAL
Definition variables.h:605
@ LES_AVERAGING_GLOBAL
Definition variables.h:607
@ LES_AVERAGING_HOMOGENEOUS
Definition variables.h:606
LESAveragingMode averaging_mode
Averaging set for the Germano contractions.
Definition variables.h:637
Every user-selectable parameter of the LES closure.
Definition variables.h:629
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ClipModelCoefficient()

PetscReal ClipModelCoefficient ( PetscReal  coefficient,
const LESConfig *  config,
PetscBool *  limited 
)

Applies the configured admissible range to one model coefficient.

The coefficient carries the sign of <L:M>; a negative value is backscatter, energy returning from the unresolved scales, not an error. LES_CLIP_NONE keeps it, leaving stability to the total-viscosity floor applied downstream.

Parameters
[in]coefficientRaw coefficient from the Germano contraction.
[in]configLES configuration supplying the mode and ceiling.
[out]limitedSet to PETSC_TRUE when the value was modified; may be NULL.
Returns
The coefficient after limiting.

Applies the configured admissible range to one model coefficient.

Full API contract is documented with the header declaration in include/les.h.

See also
ClipModelCoefficient()

Definition at line 466 of file les.c.

467{
468 PetscReal result = coefficient;
469
470 switch (config->clip_mode) {
471 case LES_CLIP_NONE:
472 // Signed coefficient retained: backscatter survives, and stability is left to
473 // the total-viscosity floor rather than to the coefficient's sign.
474 break;
476 if (result < 0.0) result = 0.0;
477 break;
478 case LES_CLIP_CLAMP:
479 default:
480 if (result < 0.0) result = 0.0;
481 if (result > config->max_cs * config->max_cs) result = config->max_cs * config->max_cs;
482 break;
483 }
484
485 if (limited) *limited = (PetscBool)(result != coefficient);
486 return result;
487}
@ LES_CLIP_CLIP_NEGATIVE
Definition variables.h:619
@ LES_CLIP_CLAMP
Definition variables.h:618
@ LES_CLIP_NONE
Definition variables.h:620
PetscReal max_cs
Ceiling on Cs under LES_CLIP_CLAMP.
Definition variables.h:640
LESClipMode clip_mode
Admissible range for the coefficient.
Definition variables.h:639
Here is the caller graph for this function:

◆ EddyViscosityFromCoefficient()

PetscReal EddyViscosityFromCoefficient ( PetscReal  coefficient,
PetscReal  delta,
PetscReal  strain_magnitude,
PetscReal  molecular_viscosity,
PetscReal  min_viscosity_ratio 
)

Builds the eddy viscosity from a model coefficient.

nu_t = C Delta^2 |S|, floored so that nu + nu_t >= min_viscosity_ratio * nu. The floor constrains the quantity that must stay positive for the momentum operator to remain well posed, which is why backscatter is limited here rather than by forcing the coefficient positive.

Parameters
[in]coefficientModel coefficient, signed.
[in]deltaGrid filter width at the cell.
[in]strain_magnitudeResolved strain magnitude |S|.
[in]molecular_viscosityMolecular kinematic viscosity.
[in]min_viscosity_ratioLower bound on total viscosity as a multiple of molecular.
Returns
The admissible eddy viscosity.

Builds the eddy viscosity from a model coefficient.

Full API contract is documented with the header declaration in include/les.h.

See also
EddyViscosityFromCoefficient()

Definition at line 497 of file les.c.

501{
502 PetscReal nu_t = coefficient * delta * delta * strain_magnitude;
503
504 // A negative eddy viscosity is physical up to the point where it would drive the
505 // total viscosity non-positive and make the momentum operator ill posed.
506 const PetscReal floor = (min_viscosity_ratio - 1.0) * molecular_viscosity;
507
508 if (nu_t < floor) nu_t = floor;
509 return nu_t;
510}
double nu_t(double yplus)
Computes turbulent eddy viscosity ratio (ν_t / ν)
Here is the call graph for this function:
Here is the caller graph for this function:

◆ WALEEddyViscosity()

PetscReal WALEEddyViscosity ( Cmpnts  dudx,
Cmpnts  dvdx,
Cmpnts  dwdx,
PetscReal  delta,
PetscReal  coefficient 
)

WALE eddy viscosity (Nicoud & Ducros 1999) from the resolved velocity gradient.

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)], where S^d is the traceless symmetric part of the squared gradient. It vanishes in pure shear, so a laminar shear layer gets none, and it falls as the cube of wall distance approaching a wall without a damping function. It needs no coefficient field, no test filter, and no averaging, so it is valid on geometry with no homogeneous direction.

Parameters
[in]dudxGradient of the x velocity component.
[in]dvdxGradient of the y velocity component.
[in]dwdxGradient of the z velocity component.
[in]deltaGrid filter width at the cell.
[in]coefficientModel constant C_w.
Returns
The eddy viscosity, zero where the gradient carries no model invariant.

WALE eddy viscosity (Nicoud & Ducros 1999) from the resolved velocity gradient.

Full API contract is documented with the header declaration in include/les.h.

See also
WALEEddyViscosity()

Definition at line 520 of file les.c.

522{
523 const PetscReal g[3][3] = {{dudx.x, dudx.y, dudx.z},
524 {dvdx.x, dvdx.y, dvdx.z},
525 {dwdx.x, dwdx.y, dwdx.z}};
526 PetscReal g2[3][3], sd[3][3], trace = 0.0, ss = 0.0, sdsd = 0.0, denominator;
527
528 for (PetscInt a = 0; a < 3; ++a)
529 for (PetscInt b = 0; b < 3; ++b) {
530 g2[a][b] = 0.0;
531 for (PetscInt c = 0; c < 3; ++c) g2[a][b] += g[a][c] * g[c][b];
532 }
533 for (PetscInt a = 0; a < 3; ++a) trace += g2[a][a];
534 for (PetscInt a = 0; a < 3; ++a)
535 for (PetscInt b = 0; b < 3; ++b) {
536 const PetscReal s = 0.5 * (g[a][b] + g[b][a]);
537 // Traceless symmetric part of the squared gradient: it vanishes in pure shear,
538 // which is what lets the model switch itself off in a laminar shear layer.
539 sd[a][b] = 0.5 * (g2[a][b] + g2[b][a]) - (a == b ? trace / 3.0 : 0.0);
540 ss += s * s;
541 sdsd += sd[a][b] * sd[a][b];
542 }
543
544 denominator = PetscPowReal(ss, 2.5) + PetscPowReal(sdsd, 1.25);
545 if (denominator <= LES_EPSILON * LES_EPSILON) return 0.0;
546 return coefficient * coefficient * delta * delta * PetscPowReal(sdsd, 1.5) / denominator;
547}
Here is the caller graph for this function:

◆ VremanEddyViscosity()

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.

nu_t = c sqrt(B_beta / (alpha_ij alpha_ij)), with alpha_ij = du_j/dx_i and beta_ij = sum_m d_i^(m) d_j^(m), where d^(m) is the change in velocity across the cell along grid direction m - edges[m] . grad(u). On an aligned Cartesian grid that is Vreman's Delta_m^2 alpha_mi alpha_mj; on a curvilinear grid it is the same quantity measured along the cell's own edges, so the model is resolved per direction rather than through one scalar width, and unchanged by the grid's orientation. It vanishes in pure shear and needs no coefficient field, test filter, or averaging.

Parameters
[in]dudxGradient of the x velocity component.
[in]dvdxGradient of the y velocity component.
[in]dwdxGradient of the z velocity component.
[in]edgesCell edge vectors along xi, eta, zeta, from ComputeCellEdgeVectors.
[in]coefficientModel constant c.
Returns
The eddy viscosity, zero where B_beta vanishes.

Vreman eddy viscosity (Vreman 2004), weighting each grid direction by its own spacing.

Full API contract is documented with the header declaration in include/les.h.

See also
VremanEddyViscosity()

Definition at line 557 of file les.c.

559{
560 const Cmpnts gradient[3] = {dudx, dvdx, dwdx};
561 PetscReal beta[3][3] = {{0.0}}, alpha_sq = 0.0, b_beta;
562
563 // Vreman's beta_ij = sum_m Delta_m^2 alpha_mi alpha_mj on a Cartesian grid, where
564 // Delta_m alpha_mi is the change in u_i across the cell in direction m. On a
565 // curvilinear grid that change is edge_m . grad(u_i), so the same sum over the
566 // cell's own directions reproduces the Cartesian form on an aligned grid and does
567 // not depend on the grid's orientation elsewhere.
568 for (PetscInt m = 0; m < 3; ++m) {
569 PetscReal change[3];
570 for (PetscInt a = 0; a < 3; ++a)
571 change[a] = edges[m].x * gradient[a].x + edges[m].y * gradient[a].y + edges[m].z * gradient[a].z;
572 for (PetscInt a = 0; a < 3; ++a)
573 for (PetscInt b = 0; b < 3; ++b) beta[a][b] += change[a] * change[b];
574 }
575 for (PetscInt a = 0; a < 3; ++a)
576 alpha_sq += gradient[a].x * gradient[a].x + gradient[a].y * gradient[a].y + gradient[a].z * gradient[a].z;
577
578 b_beta = beta[0][0] * beta[1][1] - beta[0][1] * beta[0][1]
579 + beta[0][0] * beta[2][2] - beta[0][2] * beta[0][2]
580 + beta[1][1] * beta[2][2] - beta[1][2] * beta[1][2];
581
582 // B_beta is non-negative in exact arithmetic; rounding can leave it a hair below.
583 if (alpha_sq <= LES_EPSILON * LES_EPSILON || b_beta <= 0.0) return 0.0;
584 return coefficient * PetscSqrtReal(b_beta / alpha_sq);
585}
Here is the caller graph for this function:

◆ SubgridKineticEnergy()

PetscReal SubgridKineticEnergy ( PetscReal  yoshizawa_ci,
PetscReal  delta,
PetscReal  strain_magnitude 
)

Returns the modelled subgrid kinetic energy at a cell.

Uses the Yoshizawa relation k_sgs = 2 C_I Delta^2 |S|^2. Reported as a diagnostic: compared against the resolved turbulent kinetic energy from the field-statistics pipeline it measures how much of the energy the grid resolves.

Parameters
[in]yoshizawa_ciYoshizawa constant C_I.
[in]deltaGrid filter width at the cell.
[in]strain_magnitudeResolved strain magnitude |S|.
Returns
The modelled subgrid kinetic energy.

Returns the modelled subgrid kinetic energy at a cell.

Full API contract is documented with the header declaration in include/les.h.

See also
SubgridKineticEnergy()

Definition at line 595 of file les.c.

596{
597 return 2.0 * yoshizawa_ci * delta * delta * strain_magnitude * strain_magnitude;
598}
Here is the caller graph for this function:

◆ ComputeSmagorinskyConstant()

PetscErrorCode ComputeSmagorinskyConstant ( UserCtx *  user)

Computes the dynamic Smagorinsky coefficient field for one block.

Runs the Germano-Lilly procedure: test-filters the resolved field, forms the Leonard stress and the model tensor, contracts them, averages the two contractions over the configured directions, limits the quotient, and stores it in UserCtx::CS.

The stored quantity is the coefficient C that multiplies Delta^2 |S| in the eddy viscosity, that is, Cs^2 rather than Cs. It is signed under LES_CLIP_NONE. Reported values are converted to Cs at the diagnostic boundary, where the familiar target near 0.16 applies.

Only DYNAMIC_SMAGORINSKY reaches this routine; the constant model prescribes its coefficient from configuration and allocates no field. Callers honour LESConfig::dynamic_frequency, and the caller must have refreshed UserCtx::lUcat and its periodic images beforehand.

Parameters
[in,out]userThe user context for a single computational block.
Returns
PetscErrorCode 0 on success.

Computes the dynamic Smagorinsky coefficient field for one block.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/les.h.

See also
ComputeSmagorinskyConstant()

Definition at line 715 of file les.c.

716{
717 SimCtx *simCtx = user->simCtx;
718 const LESConfig *config = &simCtx->les_config;
719 DM da = user->da, fda = user->fda;
721 PetscBool average_direction[3];
722 PetscReal alpha;
723
724 Vec lStrainDiag, lStrainOff, lStrainAbs, lLM, lMM, lCsq;
725 Vec lWallMask = NULL;
726
727 Cmpnts ***ucat = NULL, ***strain_diag = NULL, ***strain_off = NULL;
728 Cmpnts ***csi = NULL, ***eta = NULL, ***zet = NULL;
729 PetscReal ***strain_abs = NULL, ***nvert = NULL, ***aj = NULL;
730 PetscReal ***lm = NULL, ***mm = NULL, ***csq = NULL, ***cs_out = NULL;
731
732 LESDiagnosticsState diagnostics = {0.0, 0.0, 0.0, 0.0, 0.0, PETSC_TRUE};
733
734 PetscFunctionBeginUser;
736
737 PetscCheck(simCtx->les == DYNAMIC_SMAGORINSKY, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE,
738 "The dynamic procedure runs only for the dynamic Smagorinsky model. The "
739 "constant model prescribes its coefficient from configuration and holds "
740 "no coefficient field.");
741
742 // The dynamic procedure needs a developed field to sample. Starting from rest it
743 // has nothing to measure, so the first steps run with no subgrid dissipation.
744 if (simCtx->step < 2 && simCtx->StartStep == 0) {
745 PetscCall(VecSet(user->CS, 0.0));
746 PetscCall(FinalizeSmagorinskyConstantField(user));
747 user->les_diagnostics = (LESDiagnosticsState){0.0, 0.0, 0.0, 0.0, 0.0, PETSC_FALSE};
749 "Holding the dynamic coefficient at zero for the opening steps (step=%d).\n",
750 simCtx->step);
752 PetscFunctionReturn(0);
753 }
754
755 PetscCall(LESResolveDomain(user, &plan));
756 PetscCall(ResolveLESAveragingDirections(user, average_direction));
757
758 // alpha is the squared ratio of the two filter widths and sets the relative weight
759 // of the test-scale and grid-scale terms in the model tensor. The widths are the
760 // cube roots of the three directional widths, and the configured ratio applies to
761 // each direction the kernel filters. The box filters all three, so alpha is the
762 // ratio squared. Simpson filters only i and k and leaves j at the grid width, so
763 // its widths differ by ratio^(2/3) and alpha is ratio^(4/3); squaring the ratio
764 // there halved the dynamic coefficient on HOM02 (0.091 against the box's 0.194).
766 alpha = PetscPowReal(config->test_filter_width_ratio, 4.0 / 3.0);
767 } else {
768 alpha = config->test_filter_width_ratio * config->test_filter_width_ratio;
769 }
770
771 PetscCall(VecDuplicate(user->lUcat, &lStrainDiag));
772 PetscCall(VecDuplicate(user->lUcat, &lStrainOff));
773 PetscCall(VecDuplicate(user->lNvert, &lStrainAbs));
774 PetscCall(VecDuplicate(user->lNvert, &lLM));
775 PetscCall(VecDuplicate(user->lNvert, &lMM));
776 PetscCall(VecDuplicate(user->lNvert, &lCsq));
777 PetscCall(VecSet(lStrainDiag, 0.0));
778 PetscCall(VecSet(lStrainOff, 0.0));
779 PetscCall(VecSet(lStrainAbs, 0.0));
780 PetscCall(VecSet(lLM, 0.0));
781 PetscCall(VecSet(lMM, 0.0));
782 PetscCall(VecSet(lCsq, 0.0));
783 /* A wall model overwrites the first interior cell, so those cells carry an imposed
784 profile rather than a measurement and are kept out of the contraction sums. */
785 if (simCtx->wallfunction) {
786 PetscCall(VecDuplicate(user->lNvert, &lWallMask));
787 PetscCall(BuildWallModelExclusionMask(user, lWallMask));
788 }
789
790 PetscCall(DMDAVecGetArray(fda, user->lUcat, &ucat));
791
792 // --- 1. Strain rate everywhere the filter stencil will look -----------------
793 PetscCall(ComputeStrainRateField(user, ucat, lStrainDiag, lStrainOff, lStrainAbs));
794
795 PetscCall(DMDAVecGetArrayRead(fda, lStrainDiag, &strain_diag));
796 PetscCall(DMDAVecGetArrayRead(fda, lStrainOff, &strain_off));
797 PetscCall(DMDAVecGetArrayRead(da, lStrainAbs, &strain_abs));
798 PetscCall(DMDAVecGetArrayRead(fda, user->lCsi, &csi));
799 PetscCall(DMDAVecGetArrayRead(fda, user->lEta, &eta));
800 PetscCall(DMDAVecGetArrayRead(fda, user->lZet, &zet));
801 PetscCall(DMDAVecGetArrayRead(da, user->lNvert, &nvert));
802 PetscCall(DMDAVecGetArrayRead(da, user->lAj, &aj));
803 PetscCall(DMDAVecGetArray(da, lLM, &lm));
804 PetscCall(DMDAVecGetArray(da, lMM, &mm));
805
806 // --- 2. Germano contractions at every fluid cell ----------------------------
807 for (PetscInt k = plan.start[2]; k < plan.end[2]; k++)
808 for (PetscInt j = plan.start[1]; j < plan.end[1]; j++)
809 for (PetscInt i = plan.start[0]; i < plan.end[0]; i++) {
810 double velocity_x[3][3][3], velocity_y[3][3][3], velocity_z[3][3][3];
811 double strain_abs_stencil[3][3][3], weights[3][3][3];
812 SymTensor strain_stencil[3][3][3], product_stencil[3][3][3], velocity_product[3][3][3];
813 Cmpnts velocity_filtered;
814 SymTensor strain_filtered, product_filtered, velocity_product_filtered;
815 SymTensor leonard, model;
816 PetscReal strain_abs_filtered, delta, volume;
817
818 if (!SpatialTargetPlanMaskAllows(&plan, nvert[k][j][i])) {
819 lm[k][j][i] = 0.0;
820 mm[k][j][i] = 0.0;
821 continue;
822 }
823
824 // --- 2a. Gather the 3x3x3 stencil -------------------------------------
825 for (PetscInt r = -1; r <= 1; r++)
826 for (PetscInt q = -1; q <= 1; q++)
827 for (PetscInt p = -1; p <= 1; p++) {
828 const PetscInt R = r + 1, Q = q + 1, P = p + 1;
829 const PetscInt KK = k + r, JJ = j + q, II = i + p;
830 const SymTensor strain = LESStrainAt(strain_diag[KK][JJ][II], strain_off[KK][JJ][II]);
831 const PetscReal strain_abs_here = strain_abs[KK][JJ][II];
832
833 velocity_x[R][Q][P] = ucat[KK][JJ][II].x;
834 velocity_y[R][Q][P] = ucat[KK][JJ][II].y;
835 velocity_z[R][Q][P] = ucat[KK][JJ][II].z;
836
837 strain_stencil[R][Q][P] = strain;
838 strain_abs_stencil[R][Q][P] = strain_abs_here;
839
840 // The grid-scale model formed pointwise, before any test filtering. Test
841 // filtering this product is the term the model tensor cannot do without,
842 // and it is not the same as multiplying the two filtered factors.
843 product_stencil[R][Q][P] = SymTensorCombine(strain_abs_here, strain, 0.0, strain);
844
845 velocity_product[R][Q][P] = SymTensorSelfOuter(ucat[KK][JJ][II]);
846 weights[R][Q][P] = aj[KK][JJ][II];
847 }
848
849 // --- 2b. Test-filter every quantity the identity needs ----------------
850 velocity_filtered.x = ApplyLESTestFilter(config->test_filter_kernel, velocity_x, weights);
851 velocity_filtered.y = ApplyLESTestFilter(config->test_filter_kernel, velocity_y, weights);
852 velocity_filtered.z = ApplyLESTestFilter(config->test_filter_kernel, velocity_z, weights);
853 strain_abs_filtered = ApplyLESTestFilter(config->test_filter_kernel, strain_abs_stencil, weights);
854 PetscCall(ApplyLESTestFilterSymTensor(config->test_filter_kernel, strain_stencil, weights, &strain_filtered));
855 PetscCall(ApplyLESTestFilterSymTensor(config->test_filter_kernel, product_stencil, weights, &product_filtered));
856 PetscCall(ApplyLESTestFilterSymTensor(config->test_filter_kernel, velocity_product, weights, &velocity_product_filtered));
857
858 // --- 2c. Leonard stress and model tensor -------------------------------
859 leonard = LeonardStress(velocity_filtered, velocity_product_filtered);
860
861 PetscCall(ComputeCellFilterWidth(config->filter_width_model, aj[k][j][i],
862 csi[k][j][i], eta[k][j][i], zet[k][j][i], &delta));
863
864 model = GermanoModelTensor(delta, alpha, strain_abs_filtered, strain_filtered, product_filtered);
865
866 // --- 2d. Lilly's least-squares contraction -----------------------------
867 // Stored pre-multiplied by cell volume. The average that follows is over
868 // volume, not over cell count, so that a refined region does not dominate it
869 // merely by holding more cells; carrying the weight here keeps the shared
870 // averaging kernel free of any weighting policy of its own.
871 volume = 1.0 / aj[k][j][i];
872 lm[k][j][i] = volume * SymTensorContract(leonard, model);
873 mm[k][j][i] = volume * SymTensorNormSq(model);
874 }
875
876 PetscCall(DMDAVecRestoreArrayRead(fda, lStrainDiag, &strain_diag));
877 PetscCall(DMDAVecRestoreArrayRead(fda, lStrainOff, &strain_off));
878 PetscCall(DMDAVecRestoreArray(da, lLM, &lm));
879 PetscCall(DMDAVecRestoreArray(da, lMM, &mm));
880 PetscCall(DMDAVecRestoreArray(fda, user->lUcat, &ucat));
881
882 // --- 3. Average the two contractions, then divide ---------------------------
883 // Both sums are formed before the division. Averaging the pointwise quotients
884 // instead would not be the least-squares coefficient Lilly's closure defines.
885 PetscCall(PicurvSpatialRatioAverage(user, &plan, lLM, lMM, lWallMask, average_direction,
886 PetscObjectComm((PetscObject)da), lCsq, NULL));
887
888 // --- 4. Limit the coefficient and record what limiting cost -----------------
889 PetscCall(DMDAVecGetArrayRead(da, lLM, &lm));
890 PetscCall(DMDAVecGetArrayRead(da, lMM, &mm));
891 PetscCall(DMDAVecGetArrayRead(da, lCsq, &csq));
892 PetscCall(DMDAVecGetArray(da, user->CS, &cs_out));
893
894 for (PetscInt k = plan.start[2]; k < plan.end[2]; k++)
895 for (PetscInt j = plan.start[1]; j < plan.end[1]; j++)
896 for (PetscInt i = plan.start[0]; i < plan.end[0]; i++) {
897 PetscBool limited = PETSC_FALSE;
898 PetscReal weight, raw;
899
900 if (!SpatialTargetPlanMaskAllows(&plan, nvert[k][j][i])) {
901 cs_out[k][j][i] = 0.0;
902 continue;
903 }
904
905 weight = 1.0 / aj[k][j][i];
906 raw = csq[k][j][i];
907
908 cs_out[k][j][i] = ClipModelCoefficient(raw, config, &limited);
909
910 // The two contraction fields already carry the cell volume from the loop
911 // above, so they are summed as they stand rather than weighted twice.
912 diagnostics.contraction_lm += lm[k][j][i];
913 diagnostics.contraction_mm += mm[k][j][i];
914 diagnostics.fluid_volume += weight;
915 if (raw < 0.0) diagnostics.backscatter_volume += weight;
916 if (limited) diagnostics.limited_volume += weight;
917 }
918
919 PetscCall(DMDAVecRestoreArrayRead(da, lLM, &lm));
920 PetscCall(DMDAVecRestoreArrayRead(da, lMM, &mm));
921 PetscCall(DMDAVecRestoreArrayRead(da, lCsq, &csq));
922 PetscCall(DMDAVecRestoreArray(da, user->CS, &cs_out));
923 PetscCall(DMDAVecRestoreArrayRead(fda, user->lCsi, &csi));
924 PetscCall(DMDAVecRestoreArrayRead(fda, user->lEta, &eta));
925 PetscCall(DMDAVecRestoreArrayRead(fda, user->lZet, &zet));
926 PetscCall(DMDAVecRestoreArrayRead(da, user->lNvert, &nvert));
927 PetscCall(DMDAVecRestoreArrayRead(da, user->lAj, &aj));
928
929 PetscCall(VecDestroy(&lStrainDiag));
930 PetscCall(VecDestroy(&lStrainOff));
931 PetscCall(VecDestroy(&lStrainAbs));
932 PetscCall(VecDestroy(&lLM));
933 PetscCall(VecDestroy(&lMM));
934 PetscCall(VecDestroy(&lCsq));
935 if (lWallMask) PetscCall(VecDestroy(&lWallMask));
936
937 user->les_diagnostics = diagnostics;
938 PetscCall(FinalizeSmagorinskyConstantField(user));
939
941 PetscFunctionReturn(0);
942}
double ApplyLESTestFilter(LESTestFilterKernel kernel, double values[3][3][3], double weights[3][3][3])
Applies a numerical "test filter" to a 3x3x3 stencil of data points.
Definition Filter.c:123
PetscErrorCode ApplyLESTestFilterSymTensor(LESTestFilterKernel kernel, SymTensor values[3][3][3], double weights[3][3][3], SymTensor *filtered)
Applies the test filter to all six components of a symmetric tensor.
Definition Filter.c:147
SymTensor GermanoModelTensor(PetscReal delta, PetscReal alpha, PetscReal strain_magnitude_filtered, SymTensor strain_filtered, SymTensor strain_product_filtered)
Implementation of GermanoModelTensor().
Definition les.c:374
PetscReal ClipModelCoefficient(PetscReal coefficient, const LESConfig *config, PetscBool *limited)
Implementation of ClipModelCoefficient().
Definition les.c:466
PetscErrorCode ComputeCellFilterWidth(LESFilterWidthModel model, PetscReal aj, Cmpnts csi, Cmpnts eta, Cmpnts zet, PetscReal *delta)
Implementation of ComputeCellFilterWidth().
Definition les.c:296
PetscErrorCode ResolveLESAveragingDirections(UserCtx *user, PetscBool direction[3])
Implementation of ResolveLESAveragingDirections().
Definition les.c:410
SymTensor LeonardStress(Cmpnts velocity_filtered, SymTensor velocity_product_filtered)
Implementation of LeonardStress().
Definition les.c:359
static SymTensor LESStrainAt(Cmpnts diagonal, Cmpnts offdiagonal)
Rebuilds a symmetric strain tensor from its two scratch storage vectors.
Definition les.c:690
static PetscErrorCode FinalizeSmagorinskyConstantField(UserCtx *user)
Synchronizes the completed Smagorinsky coefficient field.
Definition les.c:40
static PetscErrorCode ComputeStrainRateField(UserCtx *user, Cmpnts ***ucat, Vec strain_diagonal, Vec strain_offdiagonal, Vec strain_magnitude)
Fills the strain-rate scratch fields across the computed range and its halo.
Definition les.c:634
static PetscErrorCode LESResolveDomain(UserCtx *user, SpatialTargetPlan *plan)
Resolves the iteration domain the closure computes over.
Definition les.c:63
static PetscErrorCode BuildWallModelExclusionMask(UserCtx *user, Vec inclusion)
Marks the near-wall cells a wall model overwrites, so the procedure skips them.
Definition les.c:87
#define GLOBAL
Scope for global logging across all processes.
Definition logging.h:46
#define LOG_ALLOW(scope, level, fmt,...)
Logging macro that checks both the log level and whether the calling function is in the allowed-funct...
Definition logging.h:200
#define PROFILE_FUNCTION_END
Marks the end of a profiled code block.
Definition logging.h:894
@ LOG_DEBUG
Detailed debugging information.
Definition logging.h:32
#define PROFILE_FUNCTION_BEGIN
Marks the beginning of a profiled code block (typically a function).
Definition logging.h:885
PetscBool SpatialTargetPlanMaskAllows(const SpatialTargetPlan *plan, PetscReal nvert_value)
Reports whether a point passes the plan's mask.
PetscInt end[3]
Exclusive end per dimension (i, j, k).
PetscInt start[3]
Inclusive start per dimension (i, j, k).
PetscErrorCode PicurvSpatialRatioAverage(UserCtx *user, const SpatialTargetPlan *plan, Vec numerator, Vec denominator, Vec inclusion, const PetscBool average_direction[3], MPI_Comm comm, Vec ratio, PetscReal *scalar)
Averages two fields over a target domain and divides the results.
Resolved iteration domain for one field on one block.
@ DYNAMIC_SMAGORINSKY
Definition variables.h:551
LESFilterWidthModel filter_width_model
How the grid filter width Delta is derived per cell.
Definition variables.h:634
PetscReal contraction_mm
Volume-weighted sum of M_ij M_ij over fluid cells.
Definition variables.h:656
LESTestFilterKernel test_filter_kernel
Discrete test-filter stencil.
Definition variables.h:635
Vec lNvert
Definition variables.h:1113
@ LES_TEST_FILTER_SIMPSON_IK
Definition variables.h:593
LESDiagnosticsState les_diagnostics
Pre-clipping statistics from the last dynamic update.
Definition variables.h:1157
PetscInt StartStep
Definition variables.h:869
PetscReal limited_volume
Fluid volume whose coefficient the clip modified.
Definition variables.h:658
PetscReal fluid_volume
Total fluid volume sampled.
Definition variables.h:659
PetscReal contraction_lm
Volume-weighted sum of L_ij M_ij over fluid cells.
Definition variables.h:655
PetscInt wallfunction
Enable wall functions on WALL faces.
Definition variables.h:985
PetscReal test_filter_width_ratio
Test-to-grid ratio per filtered direction; alpha is its square for the box, ratio^(4/3) for Simpson.
Definition variables.h:636
PetscInt step
Definition variables.h:867
Vec lUcat
Definition variables.h:1113
PetscInt les
Active LES closure; an LESModelType value.
Definition variables.h:984
PetscReal backscatter_volume
Fluid volume whose raw coefficient was negative.
Definition variables.h:657
Pre-clipping volume statistics captured by one dynamic-coefficient update.
Definition variables.h:654
The master context for the entire simulation.
Definition variables.h:859
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ComputeEddyViscosityLES()

PetscErrorCode ComputeEddyViscosityLES ( UserCtx *  user)

Computes the turbulent eddy viscosity for one block.

Evaluates nu_t = C Delta^2 |S| at every fluid cell and stores it in UserCtx::Nu_t. The coefficient comes from UserCtx::lCs for the dynamic model and directly from LESConfig::constant_cs for the constant model, which therefore needs no coefficient field at all.

Parameters
[in,out]userThe user context for a single computational block.
Returns
PetscErrorCode 0 on success.

Computes the turbulent eddy viscosity for one block.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/les.h.

See also
ComputeEddyViscosityLES()

Definition at line 952 of file les.c.

953{
954 SimCtx *simCtx = user->simCtx;
955 const LESConfig *config = &simCtx->les_config;
956 DM da = user->da, fda = user->fda;
958 const PetscReal molecular_viscosity = 1.0 / simCtx->ren;
959
960 // The constant model's coefficient is a number from the configuration, so it needs
961 // no field, no ghost exchange, and no periodic synchronization.
962 const PetscBool coefficient_is_field = (PetscBool)(simCtx->les == DYNAMIC_SMAGORINSKY);
963 const PetscReal prescribed_coefficient = config->constant_cs * config->constant_cs;
964
965 Cmpnts ***ucat = NULL, ***csi = NULL, ***eta = NULL, ***zet = NULL;
966 PetscReal ***coefficient = NULL, ***nu_t = NULL, ***nvert = NULL, ***aj = NULL;
967
968 PetscFunctionBeginUser;
970
971 PetscCall(LESResolveDomain(user, &plan));
972
973 PetscCall(DMDAVecGetArrayRead(fda, user->lUcat, &ucat));
974 PetscCall(DMDAVecGetArrayRead(fda, user->lCsi, &csi));
975 PetscCall(DMDAVecGetArrayRead(fda, user->lEta, &eta));
976 PetscCall(DMDAVecGetArrayRead(fda, user->lZet, &zet));
977 PetscCall(DMDAVecGetArray(da, user->Nu_t, &nu_t));
978 PetscCall(DMDAVecGetArrayRead(da, user->lNvert, &nvert));
979 PetscCall(DMDAVecGetArrayRead(da, user->lAj, &aj));
980 if (coefficient_is_field) PetscCall(DMDAVecGetArrayRead(da, user->lCs, &coefficient));
981
982 for (PetscInt k = plan.start[2]; k < plan.end[2]; k++)
983 for (PetscInt j = plan.start[1]; j < plan.end[1]; j++)
984 for (PetscInt i = plan.start[0]; i < plan.end[0]; i++) {
985 Cmpnts dudx, dvdx, dwdx;
986 PetscReal strain_magnitude, delta, model_coefficient;
987
988 if (!SpatialTargetPlanMaskAllows(&plan, nvert[k][j][i])) {
989 nu_t[k][j][i] = 0.0;
990 continue;
991 }
992
993 PetscCall(ComputeVectorFieldDerivatives(user, i, j, k, (Cmpnts ***)ucat, &dudx, &dvdx, &dwdx));
994
995 if (simCtx->les == VREMAN) {
996 // Resolved per direction from the cell's own edges; no scalar width enters.
997 Cmpnts edges[3];
998 PetscCall(ComputeCellEdgeVectors(aj[k][j][i], csi[k][j][i], eta[k][j][i], zet[k][j][i], edges));
999 nu_t[k][j][i] = VremanEddyViscosity(dudx, dvdx, dwdx, edges, config->vreman_coefficient);
1000 continue;
1001 }
1002
1003 PetscCall(ComputeCellFilterWidth(config->filter_width_model, aj[k][j][i],
1004 csi[k][j][i], eta[k][j][i], zet[k][j][i], &delta));
1005 if (simCtx->les == WALE) {
1006 nu_t[k][j][i] = WALEEddyViscosity(dudx, dvdx, dwdx, delta, config->wale_coefficient);
1007 continue;
1008 }
1009
1010 PetscCall(StrainRateFromGradients(dudx, dvdx, dwdx, NULL, &strain_magnitude));
1011 model_coefficient = coefficient_is_field ? coefficient[k][j][i] : prescribed_coefficient;
1012
1013 nu_t[k][j][i] = EddyViscosityFromCoefficient(model_coefficient, delta, strain_magnitude,
1014 molecular_viscosity, config->min_viscosity_ratio);
1015 }
1016
1017 if (coefficient_is_field) PetscCall(DMDAVecRestoreArrayRead(da, user->lCs, &coefficient));
1018 PetscCall(DMDAVecRestoreArrayRead(fda, user->lUcat, &ucat));
1019 PetscCall(DMDAVecRestoreArrayRead(fda, user->lCsi, &csi));
1020 PetscCall(DMDAVecRestoreArrayRead(fda, user->lEta, &eta));
1021 PetscCall(DMDAVecRestoreArrayRead(fda, user->lZet, &zet));
1022 PetscCall(DMDAVecRestoreArray(da, user->Nu_t, &nu_t));
1023 PetscCall(DMDAVecRestoreArrayRead(da, user->lNvert, &nvert));
1024 PetscCall(DMDAVecRestoreArrayRead(da, user->lAj, &aj));
1025
1026 {
1027 const FieldId periodic_fields[] = {FIELD_ID_NU_T};
1028
1029 PetscCall(SynchronizePeriodicCellFields(user, 1, periodic_fields));
1030 PetscCall(UpdateLocalGhosts(user, FIELD_ID_NU_T));
1031 }
1032
1034 PetscFunctionReturn(0);
1035}
PetscErrorCode SynchronizePeriodicCellFields(UserCtx *user, PetscInt num_fields, const FieldId field_ids[])
Synchronizes periodic endpoint cells for a list of cell-centered fields.
PetscErrorCode ComputeCellEdgeVectors(PetscReal ajc, Cmpnts csi, Cmpnts eta, Cmpnts zet, Cmpnts edges[3])
Computes a cell's edge vectors along its three grid directions.
Definition Metric.c:360
FieldId
Compile-time identity for a catalogued Eulerian field.
@ FIELD_ID_NU_T
PetscReal EddyViscosityFromCoefficient(PetscReal coefficient, PetscReal delta, PetscReal strain_magnitude, PetscReal molecular_viscosity, PetscReal min_viscosity_ratio)
Implementation of EddyViscosityFromCoefficient().
Definition les.c:497
PetscErrorCode StrainRateFromGradients(Cmpnts dudx, Cmpnts dvdx, Cmpnts dwdx, SymTensor *strain, PetscReal *magnitude)
Implementation of StrainRateFromGradients().
Definition les.c:267
PetscReal WALEEddyViscosity(Cmpnts dudx, Cmpnts dvdx, Cmpnts dwdx, PetscReal delta, PetscReal coefficient)
Implementation of WALEEddyViscosity().
Definition les.c:520
PetscReal VremanEddyViscosity(Cmpnts dudx, Cmpnts dvdx, Cmpnts dwdx, const Cmpnts edges[3], PetscReal coefficient)
Implementation of VremanEddyViscosity().
Definition les.c:557
PetscErrorCode ComputeVectorFieldDerivatives(UserCtx *user, PetscInt i, PetscInt j, PetscInt k, Cmpnts ***field_data, Cmpnts *dudx, Cmpnts *dvdx, Cmpnts *dwdx)
Computes the derivatives of a cell-centered vector field at a specific grid point.
Definition setup.c:4074
PetscErrorCode UpdateLocalGhosts(UserCtx *user, FieldId field_id)
Updates the local vector (including ghost points) from its corresponding global vector.
Definition setup.c:2489
@ VREMAN
Definition variables.h:552
@ WALE
Definition variables.h:553
PetscReal ren
Definition variables.h:906
PetscReal wale_coefficient
Model constant C_w for WALE (Nicoud & Ducros 1999).
Definition variables.h:633
PetscReal min_viscosity_ratio
Enforce nu + nu_t >= ratio * nu.
Definition variables.h:641
PetscReal constant_cs
Fixed Cs for CONSTANT_SMAGORINSKY; unused by the dynamic model.
Definition variables.h:631
PetscReal vreman_coefficient
Model constant c for VREMAN (Vreman 2004: 2.5 Cs^2).
Definition variables.h:632
Here is the call graph for this function:
Here is the caller graph for this function:

◆ LogLESDiagnostics()

PetscErrorCode LogLESDiagnostics ( UserCtx *  user)

Appends one row of LES coefficient statistics to the run's log directory.

Writes les_coefficient.csv: the effective coefficient reported as Cs, its spatial spread, eddy-viscosity levels, the modelled subgrid energy, and the fractions of the domain that were backscattering or limited before this step's clipping. Those last two describe the pre-clipping state, which no stored field preserves.

Values are instantaneous volume statistics, not time averages; window averages of the same model's fields come from the field-statistics pipeline instead.

Parameters
[in]userThe user context for a single computational block.
Returns
PetscErrorCode 0 on success.

Appends one row of LES coefficient statistics to the run's log directory.

Full API contract (arguments, ownership, side effects) is documented with the header declaration in include/les.h.

See also
LogLESDiagnostics()

Definition at line 1045 of file les.c.

1046{
1047 SimCtx *simCtx = user->simCtx;
1048 const LESConfig *config = &simCtx->les_config;
1049 DM da = user->da, fda = user->fda;
1050 SpatialTargetPlan plan;
1051 MPI_Comm comm;
1052
1053 const PetscBool dynamic = (PetscBool)(simCtx->les == DYNAMIC_SMAGORINSKY);
1054 // Vreman and WALE carry no Smagorinsky coefficient, so the coefficient columns are
1055 // written as nan rather than as a number that would read like a measured Cs.
1056 const PetscBool has_coefficient =
1057 (PetscBool)(simCtx->les == DYNAMIC_SMAGORINSKY || simCtx->les == CONSTANT_SMAGORINSKY);
1058
1059 // Index 0..4: fluid volume, coefficient first and second moments, nu_t volume sum,
1060 // subgrid-energy volume sum. Index 5..6: the pre-clip fractions. Reduced together
1061 // so the diagnostic costs one collective rather than seven.
1062 PetscReal local_sum[7] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
1063 PetscReal global_sum[7];
1064 PetscReal local_max[2] = {0.0, 0.0}, global_max[2];
1065 PetscReal local_min_coefficient = PETSC_MAX_REAL, global_min_coefficient;
1066 PetscReal contraction[2], global_contraction[2];
1067
1068 Cmpnts ***ucat = NULL, ***csi = NULL, ***eta = NULL, ***zet = NULL;
1069 PetscReal ***coefficient = NULL, ***nu_t = NULL, ***nvert = NULL, ***aj = NULL;
1070
1071 PetscFunctionBeginUser;
1072
1073 if (!config->diagnostics_enabled) PetscFunctionReturn(0);
1074 if (config->diagnostics_cadence <= 0) PetscFunctionReturn(0);
1075 if (simCtx->step % config->diagnostics_cadence != 0) PetscFunctionReturn(0);
1076
1077 PetscCall(LESResolveDomain(user, &plan));
1078 PetscCall(PetscObjectGetComm((PetscObject)da, &comm));
1079
1080 PetscCall(DMDAVecGetArrayRead(fda, user->lUcat, &ucat));
1081 PetscCall(DMDAVecGetArrayRead(fda, user->lCsi, &csi));
1082 PetscCall(DMDAVecGetArrayRead(fda, user->lEta, &eta));
1083 PetscCall(DMDAVecGetArrayRead(fda, user->lZet, &zet));
1084 PetscCall(DMDAVecGetArrayRead(da, user->lNu_t, &nu_t));
1085 PetscCall(DMDAVecGetArrayRead(da, user->lNvert, &nvert));
1086 PetscCall(DMDAVecGetArrayRead(da, user->lAj, &aj));
1087 if (dynamic) PetscCall(DMDAVecGetArrayRead(da, user->lCs, &coefficient));
1088
1089 for (PetscInt k = plan.start[2]; k < plan.end[2]; k++)
1090 for (PetscInt j = plan.start[1]; j < plan.end[1]; j++)
1091 for (PetscInt i = plan.start[0]; i < plan.end[0]; i++) {
1092 Cmpnts dudx, dvdx, dwdx;
1093 PetscReal strain_magnitude, delta, weight, value;
1094
1095 if (!SpatialTargetPlanMaskAllows(&plan, nvert[k][j][i])) continue;
1096
1097 weight = 1.0 / aj[k][j][i];
1098 value = dynamic ? coefficient[k][j][i]
1099 : config->constant_cs * config->constant_cs;
1100
1101 PetscCall(ComputeVectorFieldDerivatives(user, i, j, k, (Cmpnts ***)ucat, &dudx, &dvdx, &dwdx));
1102 PetscCall(StrainRateFromGradients(dudx, dvdx, dwdx, NULL, &strain_magnitude));
1103 PetscCall(ComputeCellFilterWidth(config->filter_width_model, aj[k][j][i],
1104 csi[k][j][i], eta[k][j][i], zet[k][j][i], &delta));
1105
1106 local_sum[0] += weight;
1107 local_sum[3] += weight * nu_t[k][j][i];
1108 local_sum[4] += weight * SubgridKineticEnergy(config->yoshizawa_ci, delta, strain_magnitude);
1109 local_max[1] = PetscMax(local_max[1], nu_t[k][j][i]);
1110 if (has_coefficient) {
1111 local_sum[1] += weight * value;
1112 local_sum[2] += weight * value * value;
1113 local_max[0] = PetscMax(local_max[0], value);
1114 local_min_coefficient = PetscMin(local_min_coefficient, value);
1115 }
1116 }
1117
1118 if (dynamic) PetscCall(DMDAVecRestoreArrayRead(da, user->lCs, &coefficient));
1119 PetscCall(DMDAVecRestoreArrayRead(fda, user->lUcat, &ucat));
1120 PetscCall(DMDAVecRestoreArrayRead(fda, user->lCsi, &csi));
1121 PetscCall(DMDAVecRestoreArrayRead(fda, user->lEta, &eta));
1122 PetscCall(DMDAVecRestoreArrayRead(fda, user->lZet, &zet));
1123 PetscCall(DMDAVecRestoreArrayRead(da, user->lNu_t, &nu_t));
1124 PetscCall(DMDAVecRestoreArrayRead(da, user->lNvert, &nvert));
1125 PetscCall(DMDAVecRestoreArrayRead(da, user->lAj, &aj));
1126
1127 local_sum[5] = user->les_diagnostics.valid ? user->les_diagnostics.backscatter_volume : 0.0;
1128 local_sum[6] = user->les_diagnostics.valid ? user->les_diagnostics.limited_volume : 0.0;
1129 contraction[0] = user->les_diagnostics.valid ? user->les_diagnostics.contraction_lm : 0.0;
1130 contraction[1] = user->les_diagnostics.valid ? user->les_diagnostics.contraction_mm : 0.0;
1131
1132 PetscCallMPI(MPI_Allreduce(local_sum, global_sum, 7, MPIU_REAL, MPI_SUM, comm));
1133 PetscCallMPI(MPI_Allreduce(local_max, global_max, 2, MPIU_REAL, MPI_MAX, comm));
1134 PetscCallMPI(MPI_Allreduce(&local_min_coefficient, &global_min_coefficient, 1, MPIU_REAL, MPI_MIN, comm));
1135 PetscCallMPI(MPI_Allreduce(contraction, global_contraction, 2, MPIU_REAL, MPI_SUM, comm));
1136
1137 if (simCtx->rank == 0) {
1138 const PetscReal volume = (global_sum[0] > 0.0) ? global_sum[0] : 1.0;
1139 const PetscReal mean = global_sum[1] / volume;
1140 const PetscReal mean_square = global_sum[2] / volume;
1141 const PetscReal variance = PetscMax(mean_square - mean * mean, 0.0);
1142 const PetscReal molecular = 1.0 / simCtx->ren;
1143 const PetscReal effective = (global_contraction[1] > LES_EPSILON)
1144 ? (global_contraction[0] / global_contraction[1]) : 0.0;
1145 FILE *file = NULL;
1146
1147 // Reported as Cs rather than as the stored coefficient, because that is the
1148 // number with a recognised value: near 0.16 for decaying isotropic turbulence.
1149 // A negative average means the box is backscattering on balance, and is written
1150 // as a negative Cs rather than hidden behind a square root.
1151 const PetscReal cs_effective = (effective >= 0.0) ? PetscSqrtReal(effective)
1152 : -PetscSqrtReal(-effective);
1153 const PetscReal cs_mean = (mean >= 0.0) ? PetscSqrtReal(mean) : -PetscSqrtReal(-mean);
1154
1155 PetscCall(PicurvOpenDiagnosticsCsv(simCtx, "les_coefficient.csv",
1156 "step,time,cs_effective,cs_mean,coefficient_mean,"
1157 "coefficient_rms,coefficient_min,coefficient_max,"
1158 "nu_t_mean,nu_t_max,nu_t_over_nu_mean,k_sgs_mean,"
1159 "backscatter_fraction,limited_fraction,physical_time", &file));
1160 PetscReal physical_time = 0.0;
1161 PetscCall(PicurvPhysicalTime(simCtx, simCtx->ti, &physical_time));
1162 fprintf(file,
1163 "%d,%.6e,%.6e,%.6e,%.6e,%.6e,%.6e,%.6e,%.6e,%.6e,%.6e,%.6e,%.6e,%.6e,%.6e\n",
1164 (int)simCtx->step, (double)simCtx->ti,
1165 has_coefficient ? (double)cs_effective : (double)NAN,
1166 has_coefficient ? (double)cs_mean : (double)NAN,
1167 has_coefficient ? (double)mean : (double)NAN,
1168 has_coefficient ? (double)PetscSqrtReal(variance) : (double)NAN,
1169 has_coefficient ? (double)global_min_coefficient : (double)NAN,
1170 has_coefficient ? (double)global_max[0] : (double)NAN,
1171 (double)(global_sum[3] / volume), (double)global_max[1],
1172 (double)(global_sum[3] / volume / molecular),
1173 (double)(global_sum[4] / volume),
1174 (double)(global_sum[5] / volume), (double)(global_sum[6] / volume),
1175 (double)physical_time);
1176 PetscCheck(fclose(file) == 0, PETSC_COMM_SELF, PETSC_ERR_FILE_WRITE,
1177 "Unable to close the LES diagnostics file.");
1178
1180 " LES coefficient: Cs_effective=%.4f, nu_t/nu (mean)=%.3e, backscatter=%.1f%%\n",
1181 (double)cs_effective, (double)(global_sum[3] / volume / molecular),
1182 (double)(100.0 * global_sum[5] / volume));
1183 }
1184
1185 PetscFunctionReturn(0);
1186}
PetscErrorCode PicurvPhysicalTime(const SimCtx *simCtx, PetscReal solver_time, PetscReal *physical)
Convert a solver time to physical seconds, t * L_ref / U_ref.
Definition io.c:3177
PetscReal SubgridKineticEnergy(PetscReal yoshizawa_ci, PetscReal delta, PetscReal strain_magnitude)
Implementation of SubgridKineticEnergy().
Definition les.c:595
@ LOG_INFO
Informational messages about program execution.
Definition logging.h:31
PetscErrorCode PicurvOpenDiagnosticsCsv(const SimCtx *simCtx, const char *filename, const char *header, FILE **file)
Opens a per-run diagnostics CSV in the run's analysis directory for appending.
Definition logging.c:3503
@ CONSTANT_SMAGORINSKY
Definition variables.h:550
PetscReal yoshizawa_ci
Yoshizawa constant for the reported SGS kinetic energy.
Definition variables.h:642
PetscMPIInt rank
Definition variables.h:862
PetscBool valid
Set once a dynamic update has populated this state.
Definition variables.h:660
Vec lNu_t
Definition variables.h:1156
PetscInt diagnostics_cadence
Steps between diagnostic rows.
Definition variables.h:644
PetscBool diagnostics_enabled
Append per-step coefficient statistics to the run log directory.
Definition variables.h:643
PetscReal ti
Definition variables.h:868
Here is the call graph for this function:
Here is the caller graph for this function: