31 const Cmpnts v = {1.0, 2.0, 3.0};
32 const SymTensor a = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0};
33 const SymTensor b = {2.0, 0.0, 0.0, 2.0, 0.0, 2.0};
36 PetscFunctionBeginUser;
39 PetscCall(
PicurvAssertRealNear(1.0, outer.
xx, 1.0e-14,
"self outer product should square the x component"));
42 PetscCall(
PicurvAssertRealNear(9.0, outer.
zz, 1.0e-14,
"self outer product should square the z component"));
49 "the deviator should be trace free"));
51 "the deviator should leave off-diagonal components alone"));
55 "contraction against a diagonal tensor should scale its trace"));
58 "the squared norm must count off-diagonal components twice"));
64 PetscFunctionReturn(0);
71 const Cmpnts dudx = {0.0, 2.0, 0.0};
72 const Cmpnts dvdx = {0.0, 0.0, 0.0};
73 const Cmpnts dwdx = {0.0, 0.0, 0.0};
77 PetscFunctionBeginUser;
81 "pure shear should give S_xy = half the velocity gradient"));
84 "an incompressible gradient should give a trace-free strain"));
87 "the strain magnitude must count both off-diagonal entries"));
89 PetscFunctionReturn(0);
97 const Cmpnts csi = {1.0, 0.0, 0.0};
98 const Cmpnts eta = {0.0, 4.0, 0.0};
99 const Cmpnts zet = {0.0, 0.0, 4.0};
100 const PetscReal aj = 0.25;
101 PetscReal cube_root = 0.0, geometric = 0.0, max_edge = 0.0;
103 PetscFunctionBeginUser;
111 "the cube-root model should depend only on cell volume"));
113 "the geometric mean of the extents should recover the same volume"));
115 "the max-edge model should report the long direction"));
117 "a stretched cell should make the max-edge width the larger of the two"));
119 PetscFunctionReturn(0);
125 const Cmpnts velocity = {1.5, -0.5, 2.0};
129 PetscFunctionBeginUser;
134 "a uniform field carries no stress between the two filter widths"));
135 PetscFunctionReturn(0);
149 const PetscReal delta = 0.5;
150 const PetscReal alpha = 4.0;
151 Cmpnts dudx = {0.0, 2.0, 0.0}, dvdx = {0.0, 0.0, 0.0}, dwdx = {0.0, 0.0, 0.0};
152 SymTensor strain, product, model, expected;
155 PetscFunctionBeginUser;
162 strain, 0.0, strain));
165 "constant strain should give the closed-form model tensor"));
167 "the model tensor must be trace free"));
168 PetscFunctionReturn(0);
183 const PetscReal delta = 1.0;
184 const PetscReal alpha = 4.0;
185 double magnitude_stencil[3][3][3], weights[3][3][3];
186 SymTensor strain_stencil[3][3][3], product_stencil[3][3][3];
187 SymTensor strain_filtered, product_filtered, model, collapsed;
188 PetscReal magnitude_filtered, separation;
190 PetscFunctionBeginUser;
192 for (PetscInt k = 0; k < 3; ++k)
193 for (PetscInt j = 0; j < 3; ++j)
194 for (PetscInt i = 0; i < 3; ++i) {
197 const PetscReal shear = 1.0 + 0.5 * (PetscReal)(i + j + k);
198 Cmpnts dudx = {0.0, 2.0 * shear, 0.0};
199 Cmpnts dvdx = {0.0, 0.0, 0.0};
200 Cmpnts dwdx = {0.0, 0.0, 0.0};
205 strain_stencil[k][j][i] = strain;
206 magnitude_stencil[k][j][i] = magnitude;
207 product_stencil[k][j][i] =
SymTensorCombine(magnitude, strain, 0.0, strain);
208 weights[k][j][i] = 1.0;
216 separation = PetscAbsReal(product_filtered.
xy - magnitude_filtered * strain_filtered.
xy);
218 "a varying strain field must separate the filtered product from "
219 "the product of the filtered factors"));
221 model =
GermanoModelTensor(delta, alpha, magnitude_filtered, strain_filtered, product_filtered);
223 strain_filtered, 0.0, strain_filtered));
226 "the model tensor must be built from the filtered product, not from "
227 "the product of separately filtered quantities"));
228 PetscFunctionReturn(0);
235 PetscBool limited = PETSC_FALSE;
237 PetscFunctionBeginUser;
243 "clamping should discard a negative coefficient"));
244 PetscCall(
PicurvAssertBool(limited,
"clamping a negative coefficient counts as limiting"));
246 "clamping should cap the coefficient at max_cs squared"));
248 "clamping should pass an admissible coefficient through"));
249 PetscCall(
PicurvAssertBool((PetscBool)(!limited),
"an admissible coefficient is not limited"));
253 "clip_negative should discard a negative coefficient"));
255 "clip_negative should impose no ceiling"));
259 "no limiting should preserve the sign that carries backscatter"));
260 PetscCall(
PicurvAssertBool((PetscBool)(!limited),
"an unlimited coefficient is not modified"));
261 PetscFunctionReturn(0);
267 const PetscReal molecular = 0.01;
269 PetscFunctionBeginUser;
273 1.0e-13,
"eddy viscosity should be the coefficient times Delta^2 |S|"));
279 1.0e-14,
"the floor should stop nu_t at minus the molecular value"));
282 1.0e-14,
"a ratio of one half should keep half the molecular viscosity"));
285 1.0e-14,
"a mild backscatter should survive the floor untouched"));
286 PetscFunctionReturn(0);
292 PetscFunctionBeginUser;
295 "the Yoshizawa relation should scale with Delta^2 |S|^2"));
296 PetscFunctionReturn(0);
311 PetscFunctionBeginUser;
312 PetscCall(VecSet(local, value));
314 PetscFunctionReturn(0);
323 PetscReal ***array = NULL;
324 PetscInt xs, xe, ys, ye, zs, ze;
326 PetscFunctionBeginUser;
327 PetscCall(DMDAGetLocalInfo(user->
da, &info));
328 xs = (info.xs == 0) ? 1 : info.xs;
329 ys = (info.ys == 0) ? 1 : info.ys;
330 zs = (info.zs == 0) ? 1 : info.zs;
331 xe = (info.xs + info.xm == info.mx) ? info.mx - 1 : info.xs + info.xm;
332 ye = (info.ys + info.ym == info.my) ? info.my - 1 : info.ys + info.ym;
333 ze = (info.zs + info.zm == info.mz) ? info.mz - 1 : info.zs + info.zm;
335 PetscCall(DMDAVecGetArray(user->
da, local, &array));
336 for (PetscInt k = zs; k < ze; ++k)
337 for (PetscInt j = ys; j < ye; ++j)
338 for (PetscInt i = xs; i < xe; ++i) array[k][j][i] = value;
339 PetscCall(DMDAVecRestoreArray(user->
da, local, &array));
340 PetscFunctionReturn(0);
347 PetscReal ***array = NULL;
349 PetscFunctionBeginUser;
350 PetscCall(DMDAVecGetArrayRead(user->
da, local, &array));
351 *value = array[k][j][i];
352 PetscCall(DMDAVecRestoreArrayRead(user->
da, local, &array));
353 PetscFunctionReturn(0);
369 const PetscBool selected[3] = {xi, eta, zeta};
371 for (PetscInt axis = 0; axis < 3; ++axis) {
372 if (!selected[axis])
continue;
383 Vec numerator, denominator, ratio;
385 PetscReal ***num = NULL;
386 PetscReal value = 0.0;
387 const PetscBool none[3] = {PETSC_FALSE, PETSC_FALSE, PETSC_FALSE};
389 PetscFunctionBeginUser;
391 PetscCall(VecSet(user->
lNvert, 0.0));
392 PetscCall(VecSet(user->
lAj, 1.0));
394 PetscCall(VecDuplicate(user->
lNvert, &numerator));
395 PetscCall(VecDuplicate(user->
lNvert, &denominator));
396 PetscCall(VecDuplicate(user->
lNvert, &ratio));
403 PetscCall(DMDAVecGetArray(user->
da, numerator, &num));
404 for (PetscInt k = 1; k < 6; ++k)
405 for (PetscInt j = 1; j < 6; ++j)
406 for (PetscInt i = 1; i < 6; ++i) num[k][j][i] = (PetscReal)(i + j + k);
407 PetscCall(DMDAVecRestoreArray(user->
da, numerator, &num));
412 PETSC_COMM_WORLD, ratio, NULL));
416 "with no averaging direction the quotient should stay pointwise"));
418 PetscCall(VecDestroy(&numerator));
419 PetscCall(VecDestroy(&denominator));
420 PetscCall(VecDestroy(&ratio));
422 PetscFunctionReturn(0);
438 Vec numerator, denominator, ratio;
440 PetscReal ***num = NULL, ***den = NULL;
441 PetscReal value = 0.0;
442 const PetscBool all[3] = {PETSC_TRUE, PETSC_TRUE, PETSC_TRUE};
444 PetscFunctionBeginUser;
446 PetscCall(VecSet(user->
lNvert, 0.0));
447 PetscCall(VecSet(user->
lAj, 1.0));
449 PetscCall(VecDuplicate(user->
lNvert, &numerator));
450 PetscCall(VecDuplicate(user->
lNvert, &denominator));
451 PetscCall(VecDuplicate(user->
lNvert, &ratio));
456 PetscCall(DMDAVecGetArray(user->
da, numerator, &num));
457 PetscCall(DMDAVecGetArray(user->
da, denominator, &den));
458 for (PetscInt k = 1; k < 6; ++k)
459 for (PetscInt j = 1; j < 6; ++j)
460 for (PetscInt i = 1; i < 6; ++i) {
461 const PetscBool low = (PetscBool)(i < 4);
463 num[k][j][i] = low ? 1.0 : 6.0;
464 den[k][j][i] = low ? 1.0 : 2.0;
466 PetscCall(DMDAVecRestoreArray(user->
da, numerator, &num));
467 PetscCall(DMDAVecRestoreArray(user->
da, denominator, &den));
472 PETSC_COMM_WORLD, ratio, NULL));
477 "the global average should divide summed numerators by summed "
478 "denominators, not average the pointwise quotients"));
481 "a global average should be the same number in every cell"));
483 PetscCall(VecDestroy(&numerator));
484 PetscCall(VecDestroy(&denominator));
485 PetscCall(VecDestroy(&ratio));
487 PetscFunctionReturn(0);
497 Vec numerator, denominator, ratio;
499 PetscReal ***num = NULL;
500 PetscReal at_low = 0.0, at_high = 0.0;
502 const PetscBool ik[3] = {PETSC_TRUE, PETSC_FALSE, PETSC_TRUE};
504 PetscFunctionBeginUser;
506 PetscCall(VecSet(user->
lNvert, 0.0));
507 PetscCall(VecSet(user->
lAj, 1.0));
509 PetscCall(VecDuplicate(user->
lNvert, &numerator));
510 PetscCall(VecDuplicate(user->
lNvert, &denominator));
511 PetscCall(VecDuplicate(user->
lNvert, &ratio));
518 PetscCall(DMDAVecGetArray(user->
da, numerator, &num));
519 for (PetscInt k = 1; k < 6; ++k)
520 for (PetscInt j = 1; j < 6; ++j)
521 for (PetscInt i = 1; i < 6; ++i) num[k][j][i] = (PetscReal)j;
522 PetscCall(DMDAVecRestoreArray(user->
da, numerator, &num));
527 PETSC_COMM_WORLD, ratio, NULL));
532 "averaging over xi and zeta should preserve the eta profile"));
534 "each retained eta plane should keep its own average"));
536 PetscCall(VecDestroy(&numerator));
537 PetscCall(VecDestroy(&denominator));
538 PetscCall(VecDestroy(&ratio));
540 PetscFunctionReturn(0);
554 Vec numerator, denominator, ratio;
556 PetscReal ***num = NULL;
558 PetscReal clean = 0.0, poisoned = 0.0;
559 const PetscBool all[3] = {PETSC_TRUE, PETSC_TRUE, PETSC_TRUE};
561 PetscFunctionBeginUser;
563 PETSC_TRUE, PETSC_TRUE, PETSC_TRUE));
565 PetscCall(DMDAGetLocalInfo(user->
da, &info));
566 PetscCall(VecSet(user->
lNvert, 0.0));
567 PetscCall(VecSet(user->
lAj, 1.0));
569 PetscCall(VecDuplicate(user->
lNvert, &numerator));
570 PetscCall(VecDuplicate(user->
lNvert, &denominator));
571 PetscCall(VecDuplicate(user->
lNvert, &ratio));
581 PETSC_COMM_WORLD, ratio, NULL));
585 PetscCall(DMDAVecGetArray(user->
da, numerator, &num));
586 for (PetscInt k = 0; k < info.mz; ++k)
587 for (PetscInt j = 0; j < info.my; ++j)
588 for (PetscInt i = 0; i < info.mx; ++i) {
589 const PetscBool duplicate = (PetscBool)(i == 0 || j == 0 || k == 0 ||
590 i == info.mx - 1 || j == info.my - 1 || k == info.mz - 1);
592 if (duplicate) num[k][j][i] = 1.0e6;
594 PetscCall(DMDAVecRestoreArray(user->
da, numerator, &num));
599 PETSC_COMM_WORLD, ratio, NULL));
603 "the interior average should be the interior value"));
605 "the duplicate planes must not contribute to a spatial average"));
607 PetscCall(VecDestroy(&numerator));
608 PetscCall(VecDestroy(&denominator));
609 PetscCall(VecDestroy(&ratio));
611 PetscFunctionReturn(0);
619 PetscBool direction[3];
621 PetscFunctionBeginUser;
626 PETSC_TRUE, PETSC_FALSE, PETSC_TRUE));
630 PetscCall(
PicurvAssertBool(direction[0],
"a periodic xi axis should be averaged over"));
632 "a wall-normal axis should keep its own coefficient profile"));
633 PetscCall(
PicurvAssertBool(direction[2],
"a periodic zeta axis should be averaged over"));
640 PetscCall(
PicurvAssertBool(direction[0],
"an explicit direction should be honoured"));
642 "an explicit selection should replace the periodic default"));
645 PetscFunctionReturn(0);
653 PetscBool direction[3];
655 PetscFunctionBeginUser;
661 PetscCall(
PicurvAssertBool((PetscBool)(!direction[0] && !direction[1] && !direction[2]),
662 "local averaging should select no direction at all"));
666 PetscCall(
PicurvAssertBool((PetscBool)(direction[0] && direction[1] && direction[2]),
667 "global averaging should select every direction"));
670 PetscFunctionReturn(0);
685 PetscReal ***
nu_t = NULL;
686 const PetscReal constant_cs = 0.2;
688 const PetscReal expected = constant_cs * constant_cs * PetscSqrtReal(2.0);
690 PetscFunctionBeginUser;
695 PetscCall(DMCreateGlobalVector(user->
da, &user->
Nu_t));
696 PetscCall(DMCreateLocalVector(user->
da, &user->
lNu_t));
697 PetscCall(VecSet(user->
Aj, 1.0));
698 PetscCall(VecSet(user->
Nu_t, 0.0));
700 PetscCall(DMDAVecGetArray(user->
fda, user->
Ucat, &ucat));
701 for (PetscInt k = user->
info.zs; k < user->
info.zs + user->
info.zm; ++k)
702 for (PetscInt j = user->
info.ys; j < user->
info.ys + user->
info.ym; ++j)
703 for (PetscInt i = user->
info.xs; i < user->
info.xs + user->
info.xm; ++i) {
704 ucat[k][j][i].
x = (PetscReal)i;
705 ucat[k][j][i].
y = 0.0;
706 ucat[k][j][i].
z = 0.0;
708 PetscCall(DMDAVecRestoreArray(user->
fda, user->
Ucat, &ucat));
709 PetscCall(DMGlobalToLocalBegin(user->
fda, user->
Ucat, INSERT_VALUES, user->
lUcat));
710 PetscCall(DMGlobalToLocalEnd(user->
fda, user->
Ucat, INSERT_VALUES, user->
lUcat));
711 PetscCall(DMGlobalToLocalBegin(user->
da, user->
Aj, INSERT_VALUES, user->
lAj));
712 PetscCall(DMGlobalToLocalEnd(user->
da, user->
Aj, INSERT_VALUES, user->
lAj));
715 "the constant model should allocate no coefficient field"));
718 PetscCall(DMDAVecGetArrayRead(user->
da, user->
Nu_t, &
nu_t));
720 "the constant model should read its coefficient from configuration"));
721 PetscCall(DMDAVecRestoreArrayRead(user->
da, user->
Nu_t, &
nu_t));
724 PetscFunctionReturn(0);
732 PetscErrorCode call_ierr;
734 PetscFunctionBeginUser;
740 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
742 PetscCall(PetscPopErrorHandler());
745 "the dynamic procedure should reject a model that has no "
746 "coefficient field to fill"));
749 PetscFunctionReturn(0);
765 PetscReal ***coefficient = NULL;
767 PetscFunctionBeginUser;
773 PetscCall(DMCreateGlobalVector(user->
da, &user->
CS));
774 PetscCall(DMCreateLocalVector(user->
da, &user->
lCs));
775 PetscCall(VecSet(user->
Aj, 1.0));
776 PetscCall(VecSet(user->
lAj, 1.0));
777 PetscCall(VecSet(user->
lNvert, 0.0));
779 PetscCall(DMDAVecGetArray(user->
fda, user->
Ucat, &ucat));
780 for (PetscInt k = user->
info.zs; k < user->
info.zs + user->
info.zm; ++k)
781 for (PetscInt j = user->
info.ys; j < user->
info.ys + user->
info.ym; ++j)
782 for (PetscInt i = user->
info.xs; i < user->
info.xs + user->
info.xm; ++i) {
783 ucat[k][j][i].
x = 1.25;
784 ucat[k][j][i].
y = -0.5;
785 ucat[k][j][i].
z = 3.0;
787 PetscCall(DMDAVecRestoreArray(user->
fda, user->
Ucat, &ucat));
788 PetscCall(DMGlobalToLocalBegin(user->
fda, user->
Ucat, INSERT_VALUES, user->
lUcat));
789 PetscCall(DMGlobalToLocalEnd(user->
fda, user->
Ucat, INSERT_VALUES, user->
lUcat));
793 PetscCall(DMDAVecGetArrayRead(user->
da, user->
CS, &coefficient));
794 for (PetscInt k = 1; k < 6; ++k)
795 for (PetscInt j = 1; j < 6; ++j)
796 for (PetscInt i = 1; i < 6; ++i) {
798 "a uniform field should give the dynamic procedure "
801 PetscCall(DMDAVecRestoreArrayRead(user->
da, user->
CS, &coefficient));
804 PetscFunctionReturn(0);
821 PetscReal ***coefficient = NULL;
822 PetscReal reference = 0.0;
824 const PetscReal wavenumber = 2.0 * PETSC_PI / 5.0;
826 PetscFunctionBeginUser;
828 PETSC_TRUE, PETSC_TRUE, PETSC_TRUE));
835 PetscCall(DMCreateGlobalVector(user->
da, &user->
CS));
836 PetscCall(DMCreateLocalVector(user->
da, &user->
lCs));
837 PetscCall(VecSet(user->
Aj, 1.0));
838 PetscCall(VecSet(user->
lAj, 1.0));
839 PetscCall(VecSet(user->
lNvert, 0.0));
843 PetscCall(DMDAVecGetArray(user->
fda, user->
Ucat, &ucat));
844 for (PetscInt k = user->
info.zs; k < user->
info.zs + user->
info.zm; ++k)
845 for (PetscInt j = user->
info.ys; j < user->
info.ys + user->
info.ym; ++j)
846 for (PetscInt i = user->
info.xs; i < user->
info.xs + user->
info.xm; ++i) {
847 ucat[k][j][i].
x = PetscSinReal(wavenumber * (PetscReal)j);
848 ucat[k][j][i].
y = PetscSinReal(wavenumber * (PetscReal)k);
849 ucat[k][j][i].
z = PetscSinReal(wavenumber * (PetscReal)i);
851 PetscCall(DMDAVecRestoreArray(user->
fda, user->
Ucat, &ucat));
857 PetscCall(DMDAVecGetArrayRead(user->
da, user->
CS, &coefficient));
858 reference = coefficient[1][1][1];
859 PetscCall(
PicurvAssertBool((PetscBool)(PetscIsNormalReal(reference) || reference == 0.0),
860 "the dynamic coefficient must be finite"));
862 reference <= simCtx->les_config.max_cs *
864 "clamping should keep the coefficient inside its ceiling"));
865 for (PetscInt k = 1; k < 6; ++k)
866 for (PetscInt j = 1; j < 6; ++j)
867 for (PetscInt i = 1; i < 6; ++i) {
869 "global averaging should give the block one coefficient"));
871 PetscCall(DMDAVecRestoreArrayRead(user->
da, user->
CS, &coefficient));
874 PetscFunctionReturn(0);
905 ierr = PetscInitialize(&argc, &argv, NULL,
"PICurv LES closure tests");
910 ierr =
PicurvRunTests(
"unit-les", cases,
sizeof(cases) /
sizeof(cases[0]));
916 ierr = PetscFinalize();
PetscErrorCode SynchronizePeriodicCellFields(UserCtx *user, PetscInt num_fields, const FieldId field_ids[])
Synchronizes periodic endpoint cells for a list of cell-centered fields.
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.
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.
FieldId
Compile-time identity for a catalogued Eulerian field.
PetscReal SymTensorNormSq(SymTensor t)
Returns the squared Frobenius norm t_ij t_ij.
PetscErrorCode ComputeEddyViscosityLES(UserCtx *user)
Computes the turbulent eddy viscosity for one block.
SymTensor SymTensorCombine(PetscReal a, SymTensor x, PetscReal b, SymTensor y)
Forms the linear combination a*x + b*y.
SymTensor SymTensorSelfOuter(Cmpnts v)
Forms a symmetric tensor from a vector's outer product with itself.
SymTensor SymTensorDeviator(SymTensor t)
Removes the isotropic part, returning t_ij - (1/3) delta_ij t_kk.
PetscReal SubgridKineticEnergy(PetscReal yoshizawa_ci, PetscReal delta, PetscReal strain_magnitude)
Returns the modelled subgrid kinetic energy at a cell.
PetscReal SymTensorTrace(SymTensor t)
Returns the trace t_kk.
PetscReal EddyViscosityFromCoefficient(PetscReal coefficient, PetscReal delta, PetscReal strain_magnitude, PetscReal molecular_viscosity, PetscReal min_viscosity_ratio)
Builds the eddy viscosity from a model coefficient.
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.
PetscReal ClipModelCoefficient(PetscReal coefficient, const LESConfig *config, PetscBool *limited)
Applies the configured admissible range to one model coefficient.
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.
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 ResolveLESAveragingDirections(UserCtx *user, PetscBool direction[3])
Resolves which logical directions the dynamic coefficient is averaged over.
SymTensor LeonardStress(Cmpnts velocity_filtered, SymTensor velocity_product_filtered)
Forms the Leonard stress L_ij = (u_i u_j)^ - u^_i u^_j.
PetscReal SymTensorContract(SymTensor a, SymTensor b)
Contracts two symmetric tensors as a_ij b_ij.
PetscErrorCode ComputeSmagorinskyConstant(UserCtx *user)
Computes the dynamic Smagorinsky coefficient field for one block.
PetscErrorCode UpdateLocalGhosts(UserCtx *user, FieldId field_id)
Updates the local vector (including ghost points) from its corresponding global vector.
PetscErrorCode LESConfigSetDefaults(LESConfig *config)
Installs the shipped defaults for every LES closure parameter.
Spatial target resolution for the field-statistics pipeline.
@ PICURV_STATISTICS_MASK_FLUID
PetscErrorCode SpatialTargetPlanCreate(UserCtx *user, FieldId field_id, PicurvStatisticsMask mask, SpatialTargetPlan *plan)
Resolves the iteration domain for one field on one block.
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.
static PetscErrorCode SetInteriorScalar(UserCtx *user, Vec local, PetscReal value)
Writes a scalar into every cell the closure treats as owned and interior.
static PetscErrorCode TestAverageRatioDividesSummedFields(void)
Tests that averaging divides summed numerators by summed denominators.
static PetscErrorCode TestAverageRatioRetainsUnaveragedDirection(void)
Tests that averaging over two directions leaves a profile along the third.
static PetscErrorCode TestHomogeneousAveragingDerivesPeriodicAxes(void)
Tests that homogeneous averaging falls back to the block's periodic axes.
static PetscErrorCode TestGermanoModelTensorOnConstantStrain(void)
Tests the model tensor where the two filter terms provably coincide.
static void DeclarePeriodicFaces(UserCtx *user, PetscBool xi, PetscBool eta, PetscBool zeta)
Declares periodic boundary pairs on the axes a test wants homogeneous.
int main(int argc, char **argv)
Entry point for the LES closure suite.
static PetscErrorCode TestEddyViscosityFloorBoundsTotalViscosity(void)
Tests eddy-viscosity assembly and the total-viscosity floor.
static PetscErrorCode TestDynamicProcedureVanishesOnUniformFlow(void)
Tests that the dynamic procedure returns a zero coefficient on uniform flow.
static PetscErrorCode TestLeonardStressVanishesOnUniformFlow(void)
Tests that the Leonard stress vanishes on a uniform velocity field.
static PetscErrorCode FillLocalScalar(UserCtx *user, Vec local, PetscReal value)
Fills a ghosted local scalar field over the whole local array.
static PetscErrorCode TestSymTensorAlgebra(void)
Tests the symmetric-tensor primitives against hand-computed values.
static PetscErrorCode TestConstantModelNeedsNoCoefficientField(void)
Tests that the constant model builds its viscosity without a coefficient field.
static PetscErrorCode TestAverageRatioIgnoresPeriodicDuplicatePlanes(void)
Tests that a spatial average ignores the periodic duplicate planes.
static PetscErrorCode TestStrainRateFromGradients(void)
Tests strain-rate assembly and its magnitude for a known velocity gradient.
static PetscErrorCode ReadLocalScalar(UserCtx *user, Vec local, PetscInt i, PetscInt j, PetscInt k, PetscReal *value)
Reads one cell of a ghosted local scalar field.
static PetscErrorCode TestAveragingModesSelectTheirOwnDirections(void)
Tests that local and global averaging ignore the configured direction list.
static PetscErrorCode TestSubgridKineticEnergy(void)
Tests the Yoshizawa subgrid kinetic energy against its closed form.
static PetscErrorCode TestFilterWidthModelsSeparateOnStretchedCell(void)
Tests that the three filter-width models separate on an anisotropic cell.
static PetscErrorCode TestDynamicProcedureRejectsConstantModel(void)
Tests that the dynamic procedure refuses to run for the constant model.
static PetscErrorCode TestGermanoModelTensorUsesFilteredProduct(void)
Tests that the model tensor uses the filtered product, not the filtered factors.
static PetscErrorCode TestAverageRatioLocalIsPointwise(void)
Tests that an empty direction set reproduces the pointwise local model.
static PetscErrorCode TestDynamicProcedureGlobalAverageIsUniform(void)
Tests the dynamic procedure end to end on a periodic sheared field.
static PetscErrorCode TestClipModelCoefficientModes(void)
Tests each limiting mode, including the sign that carries backscatter.
PetscErrorCode PicurvCreateMinimalContexts(SimCtx **simCtx_out, UserCtx **user_out, PetscInt mx, PetscInt my, PetscInt mz)
Builds minimal SimCtx and UserCtx fixtures for C unit tests.
PetscErrorCode PicurvAssertRealNear(PetscReal expected, PetscReal actual, PetscReal tol, const char *context)
Asserts that two real values agree within tolerance.
PetscErrorCode PicurvDestroyMinimalContexts(SimCtx **simCtx_ptr, UserCtx **user_ptr)
Destroys minimal SimCtx/UserCtx fixtures and all owned PETSc objects.
PetscErrorCode PicurvCreateMinimalContextsWithPeriodicity(SimCtx **simCtx_out, UserCtx **user_out, PetscInt mx, PetscInt my, PetscInt mz, PetscBool x_periodic, PetscBool y_periodic, PetscBool z_periodic)
Builds minimal SimCtx and UserCtx fixtures for C unit tests with configurable periodicity.
PetscErrorCode PicurvRunTests(const char *suite_name, const PicurvTestCase *cases, size_t case_count)
Runs a named C test suite and prints pass/fail progress markers.
PetscErrorCode PicurvAssertBool(PetscBool value, const char *context)
Asserts that one boolean condition is true.
Shared declarations for the PICurv C test fixture and assertion layer.
Named test case descriptor consumed by PicurvRunTests.
BoundaryFaceConfig boundary_faces[6]
LESConfig les_config
Parameters of the LES closure selected by les.
LESTestFilterKernel
Selects the discrete test-filter kernel used by the dynamic procedure.
@ LES_TEST_FILTER_VOLUME_WEIGHTED_BOX
PetscReal max_cs
Ceiling on Cs under LES_CLIP_CLAMP.
LESClipMode clip_mode
Admissible range for the coefficient.
PetscBool averaging_direction[3]
Averaged-over logical directions (xi, eta, zeta).
@ LES_AVERAGING_HOMOGENEOUS
PetscReal constant_cs
Fixed Cs for CONSTANT_SMAGORINSKY; unused by the dynamic model.
LESAveragingMode averaging_mode
Averaging set for the Germano contractions.
PetscInt les
Active LES closure; an LESModelType value.
@ LES_FILTER_WIDTH_GEOMETRIC_MEAN
@ LES_FILTER_WIDTH_CUBE_ROOT_VOLUME
@ LES_FILTER_WIDTH_MAX_EDGE
BCFace
Identifies the six logical faces of a structured computational block.
A 3D point or vector with PetscScalar components.
Every user-selectable parameter of the LES closure.
The master context for the entire simulation.
A symmetric second-order tensor stored by its six independent components.
User-defined context containing data specific to a single computational grid level.
double nu_t(double yplus)
Computes turbulent eddy viscosity ratio (ν_t / ν)