PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
Loading...
Searching...
No Matches
Macros | Functions
momentumsolvers.c File Reference
#include "momentumsolvers.h"
Include dependency graph for momentumsolvers.c:

Go to the source code of this file.

Macros

#define MOM_CFL_CAP_SAFETY   0.60
 
#define MOM_CFL_CAP_RELAX   1.005
 
#define __FUNCT__   "MomentumUsesBDF2"
 
#define __FUNCT__   "MomentumBDFCoefficient"
 
#define __FUNCT__   "ComputeTotalResidual"
 
#define __FUNCT__   "Momentum_Solver_Explicit_RungeKutta4"
 
#define __FUNCT__   "ComputeGlobalSpectralRadiusEstimate"
 
#define MOM_SKIP_SOLID(v)   ((v) > 0.1)
 
#define MOM_VISC_ONESIDED(v)   ((v) > 0.5 && (v) < 7.0)
 
#define MOM_QUICK_BLOCKS(v)   ((v) >= 0.1 && (v) <= 7.0)
 
#define __FUNCT__   "ComputeMomentumStabilityEstimate"
 
#define MOM_ROW(cmp)
 
#define __FUNCT__   "MomentumSolver_DualTime_Picard_JamesonRK"
 

Functions

PetscBool MomentumUsesBDF2 (SimCtx *simCtx)
 Single source of truth for the BDF1/BDF2 selection.
 
PetscReal MomentumBDFCoefficient (SimCtx *simCtx)
 Returns a0 in {1.0, 1.5} for the current physical step.
 
PetscErrorCode ComputeTotalResidual (UserCtx *user)
 Shared implementation of ComputeTotalResidual().
 
PetscErrorCode MomentumSolver_Explicit_RungeKutta4 (UserCtx *user, IBMNodes *ibm, FSInfo *fsi)
 Internal helper implementation: MomentumSolver_Explicit_RungeKutta4().
 
static PetscErrorCode ComputeGlobalSpectralRadiusEstimate (UserCtx *user, PetscInt block_number, PetscReal dt, PetscReal *lambda_max_out)
 Compute a conservative global pseudo-time spectral radius estimate.
 
static PetscReal MomFaceGabs (Cmpnts N, Cmpnts A, Cmpnts B)
 
static PetscBool MomCellUsesOneSidedViscousStencil (PetscReal ***nvert, PetscInt k, PetscInt j, PetscInt i)
 
static PetscBool MomCellHasSolidNeighbor (PetscReal ***nvert, PetscInt k, PetscInt j, PetscInt i)
 
static PetscBool MomQuickDirModified (PetscReal ***nvert, PetscInt a, PetscInt b, PetscInt c, PetscInt idx, PetscInt m, PetscBool np0, PetscBool np1, PetscInt g0, PetscInt g1, char dir)
 
PetscInt MomCellActiveRows (PetscReal ***nvert, PetscInt k, PetscInt j, PetscInt i, PetscInt mx, PetscInt my, PetscInt mz, PetscBool np_x1, PetscBool np_y1, PetscBool np_z1, PetscInt twoD)
 Active staggered-momentum row mask for a cell (exposed for unit testing).
 
PetscErrorCode ComputeMomentumStabilityEstimate (UserCtx *user, PetscInt block_number, PetscReal dt, MomStabCandidate candidate, MomStabilityReport *rep)
 Practical conservative momentum pseudo-time stability estimate (shadow).
 
PetscErrorCode MomentumSolver_DualTime_Picard_JamesonRK (UserCtx *user, IBMNodes *ibm, FSInfo *fsi)
 Internal helper implementation: MomentumSolver_DualTime_Picard_JamesonRK().
 

Macro Definition Documentation

◆ MOM_CFL_CAP_SAFETY

#define MOM_CFL_CAP_SAFETY   0.60

Definition at line 6 of file momentumsolvers.c.

◆ MOM_CFL_CAP_RELAX

#define MOM_CFL_CAP_RELAX   1.005

Definition at line 7 of file momentumsolvers.c.

◆ __FUNCT__ [1/7]

#define __FUNCT__   "MomentumUsesBDF2"

Definition at line 15 of file momentumsolvers.c.

◆ __FUNCT__ [2/7]

#define __FUNCT__   "MomentumBDFCoefficient"

Definition at line 15 of file momentumsolvers.c.

◆ __FUNCT__ [3/7]

#define __FUNCT__   "ComputeTotalResidual"

Definition at line 15 of file momentumsolvers.c.

◆ __FUNCT__ [4/7]

#define __FUNCT__   "Momentum_Solver_Explicit_RungeKutta4"

Definition at line 15 of file momentumsolvers.c.

◆ __FUNCT__ [5/7]

#define __FUNCT__   "ComputeGlobalSpectralRadiusEstimate"

Definition at line 15 of file momentumsolvers.c.

◆ MOM_SKIP_SOLID

#define MOM_SKIP_SOLID (   v)    ((v) > 0.1)

Definition at line 310 of file momentumsolvers.c.

◆ MOM_VISC_ONESIDED

#define MOM_VISC_ONESIDED (   v)    ((v) > 0.5 && (v) < 7.0)

Definition at line 311 of file momentumsolvers.c.

◆ MOM_QUICK_BLOCKS

#define MOM_QUICK_BLOCKS (   v)    ((v) >= 0.1 && (v) <= 7.0)

Definition at line 312 of file momentumsolvers.c.

◆ __FUNCT__ [6/7]

#define __FUNCT__   "ComputeMomentumStabilityEstimate"

Definition at line 15 of file momentumsolvers.c.

◆ MOM_ROW

#define MOM_ROW (   cmp)
Value:
( \
PetscAbsReal(Ajc*(C.x*duc.cmp + E.x*due.cmp + Z.x*duz.cmp)) + \
PetscAbsReal(Ajc*(C.y*duc.cmp + E.y*due.cmp + Z.y*duz.cmp)) + \
PetscAbsReal(Ajc*(C.z*duc.cmp + E.z*due.cmp + Z.z*duz.cmp)) )

◆ __FUNCT__ [7/7]

#define __FUNCT__   "MomentumSolver_DualTime_Picard_JamesonRK"

Definition at line 15 of file momentumsolvers.c.

Function Documentation

◆ MomentumUsesBDF2()

PetscBool MomentumUsesBDF2 ( SimCtx simCtx)

Single source of truth for the BDF1/BDF2 selection.

Returns whether the current physical step uses the BDF2 discretization.

See header.

The run loop increments step before invoking the momentum solve. Therefore, BDF1 applies at a fresh start until two physical states exist. A committed checkpoint restores Ucont_rm1, so a restarted solve can retain BDF2 on its first advanced step.

Definition at line 24 of file momentumsolvers.c.

25{
26 const PetscInt ti = simCtx->step;
27 const PetscInt tistart = simCtx->StartStep;
28 return (PetscBool)(COEF_TIME_ACCURACY > 1.1 && ti != 1 &&
29 (simCtx->restartHistoryAvailable || ti > tistart + 1));
30}
PetscInt StartStep
Definition variables.h:705
PetscInt step
Definition variables.h:703
PetscBool restartHistoryAvailable
Definition variables.h:723
#define COEF_TIME_ACCURACY
Coefficient controlling the temporal accuracy scheme (e.g., 1.5 for 2nd Order Backward Difference).
Definition variables.h:57
Here is the caller graph for this function:

◆ MomentumBDFCoefficient()

PetscReal MomentumBDFCoefficient ( SimCtx simCtx)

Returns a0 in {1.0, 1.5} for the current physical step.

Returns the BDF physical-time coefficient a0 for the current step.

See header.

Definition at line 37 of file momentumsolvers.c.

38{
39 return MomentumUsesBDF2(simCtx) ? COEF_TIME_ACCURACY : 1.0;
40}
PetscBool MomentumUsesBDF2(SimCtx *simCtx)
Single source of truth for the BDF1/BDF2 selection.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ComputeTotalResidual()

PetscErrorCode ComputeTotalResidual ( UserCtx user)

Shared implementation of ComputeTotalResidual().

Computes the shared spatial-plus-BDF momentum residual in user->Rhs.

Definition at line 48 of file momentumsolvers.c.

49{
50 PetscErrorCode ierr;
51 SimCtx *simCtx = user->simCtx;
52
53 // Extract Time Parameters from Context.
54 // BDF step selection now lives in MomentumUsesBDF2() (shared with the stability estimate).
55 const PetscReal dt = simCtx->dt;
56
57 PetscFunctionBeginUser;
59 // 1. Calculate Spatial Terms (stored in user->Rhs)
60 // Rhs = -Div(Flux) + Viscous + Source
61 ierr = ComputeRHS(user, user->Rhs); CHKERRQ(ierr);
62
63 // 2. Add Physical Time Derivative Terms (BDF Discretization)
64 // The equation solved is: dU/dtau = RHS_Spatial + RHS_Temporal
65
66 /* BDF order + leading coefficient from the shared helpers (single source of truth,
67 shared with the momentum stability estimate). a0 = 1.5 (==COEF_TIME_ACCURACY) for
68 BDF2, 1.0 for BDF1; using a0 for the current-state term keeps the residual
69 numerically identical to the historical inlined coefficients. */
70 const PetscBool use_bdf2 = MomentumUsesBDF2(simCtx);
71 const PetscReal a0 = MomentumBDFCoefficient(simCtx);
72 if (use_bdf2) {
73 // --- BDF2 (Second Order Backward Difference) ---
74 // (a0*U^{n} - 2.0*U^{n-1} + 0.5*U^{n-2}) / dt = RHS_Spatial(U^{n})
75 ierr = VecAXPY(user->Rhs, -a0/dt, user->Ucont); CHKERRQ(ierr);
76 ierr = VecAXPY(user->Rhs, +2.0/dt, user->Ucont_o); CHKERRQ(ierr);
77 ierr = VecAXPY(user->Rhs, -0.5/dt, user->Ucont_rm1); CHKERRQ(ierr);
78 } else {
79 // --- BDF1 (First Order / Euler Implicit) ---
80 // (a0*U^{n} - U^{n-1}) / dt = RHS_Spatial(U^{n}), with a0 = 1.0
81 ierr = VecAXPY(user->Rhs, -a0/dt, user->Ucont); CHKERRQ(ierr);
82 ierr = VecAXPY(user->Rhs, +1.0/dt, user->Ucont_o); CHKERRQ(ierr);
83 }
84
85 // 3. Enforce Boundary Conditions on the Residual
86 ierr = EnforceRHSBoundaryConditions(user); CHKERRQ(ierr);
87
89 PetscFunctionReturn(0);
90}
PetscErrorCode EnforceRHSBoundaryConditions(UserCtx *user)
Zeroes every momentum RHS row that does not carry an independent unknown.
Definition Boundaries.c:660
#define PROFILE_FUNCTION_END
Marks the end of a profiled code block.
Definition logging.h:859
#define PROFILE_FUNCTION_BEGIN
Marks the beginning of a profiled code block (typically a function).
Definition logging.h:850
PetscReal MomentumBDFCoefficient(SimCtx *simCtx)
Returns a0 in {1.0, 1.5} for the current physical step.
PetscErrorCode ComputeRHS(UserCtx *user, Vec Rhs)
Computes the Right-Hand Side (RHS) of the momentum equations.
Definition rhs.c:1105
Vec Rhs
Definition variables.h:947
SimCtx * simCtx
Back-pointer to the master simulation context.
Definition variables.h:909
PetscReal dt
Definition variables.h:710
Vec Ucont
Definition variables.h:939
Vec Ucont_o
Definition variables.h:946
Vec Ucont_rm1
Definition variables.h:947
The master context for the entire simulation.
Definition variables.h:695
Here is the call graph for this function:
Here is the caller graph for this function:

◆ MomentumSolver_Explicit_RungeKutta4()

PetscErrorCode MomentumSolver_Explicit_RungeKutta4 ( UserCtx user,
IBMNodes ibm,
FSInfo fsi 
)

Internal helper implementation: MomentumSolver_Explicit_RungeKutta4().

Advances the momentum equations using an explicit 4th-order Runge-Kutta scheme.

Local to this translation unit.

Definition at line 99 of file momentumsolvers.c.

100{
101 PetscErrorCode ierr;
102 (void)fsi;
103 // --- Context Acquisition ---
104 // Get the master simulation context from the first block's UserCtx.
105 // This is the bridge to access all former global variables.
106 SimCtx *simCtx = user[0].simCtx;
107 PetscReal dt = simCtx->dt;
108 //PetscReal st = simCtx->st;
109 PetscInt istage;
110 PetscReal alfa[] = {0.25, 1.0/3.0, 0.5, 1.0};
111
112 PetscFunctionBeginUser;
113
115
116 LOG_ALLOW(GLOBAL, LOG_INFO, "Executing explicit momentum solver (Runge-Kutta) for %d block(s).\n",simCtx->block_number);
117
118 // --- 1. Pre-Loop Initialization (Legacy Logic) ---
119 // This block prepares boundary conditions and allocates the RHS vector for all blocks
120 // before the main RK loop begins. This logic is preserved from the original code.
121 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Preparing all blocks for Runge-Kutta solve...\n");
122 for (PetscInt bi = 0; bi < simCtx->block_number; bi++) {
123
124 ierr = ApplyBoundaryConditions(&user[bi]); CHKERRQ(ierr);
125
126 /*
127 // Immersed boundary interpolation (if enabled)
128 if (simCtx->immersed) {
129 LOG_ALLOW(LOCAL, LOG_DEBUG, " Performing pre-RK IBM interpolation for block %d.\n", bi);
130 for (PetscInt ibi = 0; ibi < simCtx->NumberOfBodies; ibi++) {
131 // The 'ibm' and 'fsi' pointers are passed directly from FlowSolver
132 ierr = ibm_interpolation_advanced(&user[bi], &ibm[ibi], ibi, 1); CHKERRQ(ierr);
133 }
134 }
135 */
136
137 // Allocate the persistent RHS vector for this block's context
138 ierr = VecDuplicate(user[bi].Ucont, &user[bi].Rhs); CHKERRQ(ierr);
139 }
140 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Pre-loop initialization complete.\n");
141
142 // --- 2. Main Runge-Kutta Loop ---
143 // The legacy code had an outer `pseudot` loop that only ran once. We preserve it.
144 for (PetscInt pseudot = 0; pseudot < 1; pseudot++) {
145 // Loop over each block to perform the RK stages
146 for (PetscInt bi = 0; bi < simCtx->block_number; bi++) {
147 for (istage = 0; istage < 4; istage++) {
148 LOG_ALLOW(LOCAL, LOG_DEBUG, " Block %d, RK Stage %d (alpha=%.4f)...\n", bi, istage, alfa[istage]);
149
150 // a. Calculate the Right-Hand Side (RHS) of the momentum equation.
151 ierr = ComputeRHS(&user[bi], user[bi].Rhs); CHKERRQ(ierr);
152
153 // b. Advance Ucont to the next intermediate stage using the RK coefficient.
154 // Ucont_new = Ucont_old + alpha * dt * RHS
155 ierr = VecWAXPY(user[bi].Ucont, alfa[istage] * dt, user[bi].Rhs, user[bi].Ucont_o); CHKERRQ(ierr);
156
157 // c. Synchronize periodic endpoints and local ghosts for the new intermediate Ucont.
158 const FieldId staggered_fields[] = {FIELD_ID_UCONT};
159 ierr = SynchronizePeriodicStaggeredFields(&user[bi], 1, staggered_fields); CHKERRQ(ierr);
160
161 // d. Re-apply boundary conditions for the new intermediate velocity.
162 // This is crucial for the stability and accuracy of the multi-stage scheme.
163 ierr = ApplyBoundaryConditions(&user[bi]); CHKERRQ(ierr);
164
165 } // End of RK stages for one block
166
167 /*
168 // Final IBM Interpolation for the block (if enabled)
169 if (simCtx->immersed) {
170 LOG_ALLOW(LOCAL, LOG_DEBUG, " Performing post-RK IBM interpolation for block %d.\n", bi);
171 for (PetscInt ibi = 0; ibi < simCtx->NumberOfBodies; ibi++) {
172 ierr = ibm_interpolation_advanced(&user[bi], &ibm[ibi], ibi, 1); CHKERRQ(ierr);
173 }
174 }
175 */
176
177 } // End loop over blocks
178
179 // --- 3. Inter-Block Communication (Legacy Logic) ---
180 // This is called after all blocks have completed their RK stages.
181 if (simCtx->block_number > 1) {
182 // LOG_ALLOW(GLOBAL, LOG_DEBUG, "Updating multi-block interfaces after RK stages.\n");
183 // ierr = Block_Interface_U(user); CHKERRQ(ierr);
184 }
185
186 } // End of pseudo-time loop
187
188 // --- 4. Cleanup ---
189 // Destroy the RHS vectors that were created at the start of this function.
190 for (PetscInt bi = 0; bi < simCtx->block_number; bi++) {
191 ierr = VecDestroy(&user[bi].Rhs); CHKERRQ(ierr);
192 }
193
194 LOG_ALLOW(GLOBAL, LOG_INFO, "Runge-Kutta solve completed for all blocks.\n");
195
197
198 PetscFunctionReturn(0);
199}
PetscErrorCode SynchronizePeriodicStaggeredFields(UserCtx *user, PetscInt num_fields, const FieldId field_ids[])
Synchronizes persistent component-staggered vector fields.
PetscErrorCode ApplyBoundaryConditions(UserCtx *user)
Main boundary-condition orchestrator executed during solver timestepping.
FieldId
Compile-time identity for a catalogued Eulerian field.
@ FIELD_ID_UCONT
#define LOCAL
Logging scope definitions for controlling message output.
Definition logging.h:45
#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
@ LOG_INFO
Informational messages about program execution.
Definition logging.h:31
@ LOG_DEBUG
Detailed debugging information.
Definition logging.h:32
PetscInt block_number
Definition variables.h:790
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ComputeGlobalSpectralRadiusEstimate()

static PetscErrorCode ComputeGlobalSpectralRadiusEstimate ( UserCtx user,
PetscInt  block_number,
PetscReal  dt,
PetscReal *  lambda_max_out 
)
static

Compute a conservative global pseudo-time spectral radius estimate.

The spectral radius lambda_max (units: 1/s) governs the pseudo-time stability limit for the explicit Jameson 4-stage RK smoother. The pseudo-time step is then:

dtau = pseudo_cfl / lambda_max

making pseudo_cfl a true dimensionless Courant number independent of the physical timestep dt. Note: 2.83 is only the 4-stage RK imaginary-axis scalar limit; it is NOT a generally-stable bound for the actual nonlinear/non-normal operator (see A4 validation).

Per owned cell (k,j,i) the convective spectral radius contribution is:

lambda_cell = (|U_xi_flux| + |U_eta_flux| + |U_zeta_flux|) x (1/V)

where:

  • U_xi_flux = ucont[k][j][i].x : volumetric flux through the xi-face [m3/s]
  • U_eta_flux = ucont[k][j][i].y : volumetric flux through the eta-face [m3/s]
  • U_zeta_flux = ucont[k][j][i].z : volumetric flux through the zeta-face [m3/s]
  • 1/V = lAj[k][j][i] : inverse cell volume (cell Jacobian) [1/m3]

Product units: [m3/s * 1/m3] = [1/s] = spectral radius.

Only the 'positive' face at index (k,j,i) is used per direction (no neighbor access), so no ghost-cell scatter is needed. lUcont is valid after the pre-loop SynchronizePeriodicStaggeredFields; lAj is set at grid initialisation and never changes. No DMGlobalToLocal calls are added.

A BDF2 lower bound COEF_TIME_ACCURACY/dt is applied after the MPI reduction so that zero-flow startup (all ucont == 0) gives dtau = pseudo_cfl * dt / 1.5 rather than infinity.

Parameters
[in]userArray of UserCtx (one per block).
[in]block_numberNumber of blocks.
[in]dtPhysical time step (for the BDF2 lower bound).
[out]lambda_max_outGlobal spectral radius [1/s].

Definition at line 241 of file momentumsolvers.c.

243{
244 PetscErrorCode ierr;
245 PetscReal local_max = 0.0;
246
247 PetscFunctionBeginUser;
248
249 for (PetscInt bi = 0; bi < block_number; bi++) {
250 DMDALocalInfo info = user[bi].info; /* pre-stored local ownership info */
251 Cmpnts ***ucont;
252 PetscReal ***aj;
253
254 /* Read-only access: lUcont (valid after SynchronizePeriodicStaggeredFields),
255 lAj (grid metric, unchanged after initialisation). */
256 ierr = DMDAVecGetArrayRead(user[bi].fda, user[bi].lUcont, &ucont); CHKERRQ(ierr);
257 ierr = DMDAVecGetArrayRead(user[bi].da, user[bi].lAj, &aj); CHKERRQ(ierr);
258
259 /* Loop over owned cells only — no ghost-cell indices accessed. */
260 for (PetscInt k = info.zs; k < info.zs + info.zm; k++) {
261 for (PetscInt j = info.ys; j < info.ys + info.ym; j++) {
262 for (PetscInt i = info.xs; i < info.xs + info.xm; i++) {
263 /* Sum absolute face-flux magnitudes (one face per coordinate direction).
264 * Using only the 'positive' face (index i/j/k, not i-1/j-1/k-1) avoids
265 * neighbor access. For smooth velocity fields the error is < 2x and the
266 * adaptive CFL controller handles any residual conservatism. */
267 PetscReal flux_sum = PetscAbsReal(ucont[k][j][i].x)
268 + PetscAbsReal(ucont[k][j][i].y)
269 + PetscAbsReal(ucont[k][j][i].z);
270
271 /* aj = 1/J = 1/cell_volume; lambda = flux/volume [1/s] */
272 PetscReal lambda = flux_sum * aj[k][j][i];
273 local_max = PetscMax(local_max, lambda);
274 }
275 }
276 }
277
278 ierr = DMDAVecRestoreArrayRead(user[bi].fda, user[bi].lUcont, &ucont); CHKERRQ(ierr);
279 ierr = DMDAVecRestoreArrayRead(user[bi].da, user[bi].lAj, &aj); CHKERRQ(ierr);
280 }
281
282 /* Reduce across all MPI ranks to get the global maximum. */
283 ierr = MPI_Allreduce(&local_max, lambda_max_out, 1, MPIU_REAL, MPI_MAX, PETSC_COMM_WORLD);
284 CHKERRQ(ierr);
285
286 /* BDF2 lower bound: if the velocity field is zero (startup, stagnant IC) lambda_max
287 would be 0, giving dtau = inf. Clamp to 1.5/dt so dtau = pseudo_cfl * dt/1.5 in
288 that degenerate case (conservative but finite). */
289 *lambda_max_out = PetscMax(*lambda_max_out, COEF_TIME_ACCURACY / dt);
290
291 PetscFunctionReturn(0);
292}
DMDALocalInfo info
Definition variables.h:918
A 3D point or vector with PetscScalar components.
Definition variables.h:102
Here is the caller graph for this function:

◆ MomFaceGabs()

static PetscReal MomFaceGabs ( Cmpnts  N,
Cmpnts  A,
Cmpnts  B 
)
inlinestatic

Definition at line 298 of file momentumsolvers.c.

299{
300 const PetscReal NN = N.x*N.x + N.y*N.y + N.z*N.z;
301 const PetscReal AN = A.x*N.x + A.y*N.y + A.z*N.z;
302 const PetscReal BN = B.x*N.x + B.y*N.y + B.z*N.z;
303 return PetscAbsReal(NN) + PetscAbsReal(AN) + PetscAbsReal(BN);
304}
#define BN
Definition poisson.c:310
PetscScalar x
Definition variables.h:103
PetscScalar z
Definition variables.h:103
PetscScalar y
Definition variables.h:103
Here is the caller graph for this function:

◆ MomCellUsesOneSidedViscousStencil()

static PetscBool MomCellUsesOneSidedViscousStencil ( PetscReal ***  nvert,
PetscInt  k,
PetscInt  j,
PetscInt  i 
)
static

Definition at line 318 of file momentumsolvers.c.

319{
320 for (PetscInt dk = -1; dk <= 1; dk++)
321 for (PetscInt dj = -1; dj <= 1; dj++)
322 for (PetscInt di = -1; di <= 1; di++) {
323 if (!dk && !dj && !di) continue;
324 if (MOM_VISC_ONESIDED(nvert[k+dk][j+dj][i+di])) return PETSC_TRUE;
325 }
326 return PETSC_FALSE;
327}
#define MOM_VISC_ONESIDED(v)
Here is the caller graph for this function:

◆ MomCellHasSolidNeighbor()

static PetscBool MomCellHasSolidNeighbor ( PetscReal ***  nvert,
PetscInt  k,
PetscInt  j,
PetscInt  i 
)
static

Definition at line 331 of file momentumsolvers.c.

332{
333 for (PetscInt dk = -1; dk <= 1; dk++)
334 for (PetscInt dj = -1; dj <= 1; dj++)
335 for (PetscInt di = -1; di <= 1; di++) {
336 if (!dk && !dj && !di) continue;
337 if (MOM_SKIP_SOLID(nvert[k+dk][j+dj][i+di])) return PETSC_TRUE;
338 }
339 return PETSC_FALSE;
340}
#define MOM_SKIP_SOLID(v)
Here is the caller graph for this function:

◆ MomQuickDirModified()

static PetscBool MomQuickDirModified ( PetscReal ***  nvert,
PetscInt  a,
PetscInt  b,
PetscInt  c,
PetscInt  idx,
PetscInt  m,
PetscBool  np0,
PetscBool  np1,
PetscInt  g0,
PetscInt  g1,
char  dir 
)
static

Definition at line 347 of file momentumsolvers.c.

350{
351 /* (a,b,c) are the fixed indices; idx is the moving index in direction `dir`.
352 The two contributing faces (idx, idx-1) have QUICK stencils spanning idx-2..idx+2. */
353 if (np0 && idx <= 1) return PETSC_TRUE; /* faces idx,idx-1 reach the negative edge */
354 if (np1 && idx >= m-2) return PETSC_TRUE; /* ... or the positive edge */
355 for (PetscInt d = -2; d <= 2; d++) {
356 if (d == 0) continue;
357 const PetscInt p = idx + d;
358 /* Required stencil information unavailable in the local ghost range: do NOT assume
359 fluid -> conservatively classify the direction as modified (use 2.5). */
360 if (p < g0 || p >= g1) return PETSC_TRUE;
361 PetscReal v;
362 if (dir == 'i') v = nvert[a][b][p];
363 else if (dir == 'j') v = nvert[a][p][c];
364 else v = nvert[p][b][c];
365 if (MOM_QUICK_BLOCKS(v)) return PETSC_TRUE;
366 }
367 return PETSC_FALSE;
368}
#define MOM_QUICK_BLOCKS(v)
Here is the caller graph for this function:

◆ MomCellActiveRows()

PetscInt MomCellActiveRows ( PetscReal ***  nvert,
PetscInt  k,
PetscInt  j,
PetscInt  i,
PetscInt  mx,
PetscInt  my,
PetscInt  mz,
PetscBool  np_x1,
PetscBool  np_y1,
PetscBool  np_z1,
PetscInt  twoD 
)

Active staggered-momentum row mask for a cell (exposed for unit testing).

Returns a 3-bit mask (xi=1, eta=2, zeta=4). A solid cell yields 0 (all inactive); a positive solid neighbour or a positive non-periodic physical face clears the corresponding normal row; TwoD (1/2/3) clears the homogeneous direction's row.

Parameters
nvertLocal nvert array (ghosted).
kCell k index.
jCell j index.
iCell i index.
mxGlobal x dimension.
myGlobal y dimension.
mzGlobal z dimension.
np_x1True if the positive-x face is non-periodic.
np_y1True if the positive-y face is non-periodic.
np_z1True if the positive-z face is non-periodic.
twoDTwoD homogeneous-direction selector (0 none, 1 xi, 2 eta, 3 zeta).
Returns
3-bit active-row mask (0 when the location carries no active unknown).

Definition at line 374 of file momentumsolvers.c.

377{
378 if (MOM_SKIP_SOLID(nvert[k][j][i])) return 0; /* solid cell: all rows inactive */
379 PetscInt bits = 0x7;
380 if (MOM_SKIP_SOLID(nvert[k][j][i+1])) bits &= ~0x1; /* positive xi neighbour solid */
381 if (MOM_SKIP_SOLID(nvert[k][j+1][i])) bits &= ~0x2; /* positive eta neighbour solid */
382 if (MOM_SKIP_SOLID(nvert[k+1][j][i])) bits &= ~0x4; /* positive zeta neighbour solid */
383 if (np_x1 && i == mx-2) bits &= ~0x1; /* positive non-periodic xi face */
384 if (np_y1 && j == my-2) bits &= ~0x2;
385 if (np_z1 && k == mz-2) bits &= ~0x4;
386 if (twoD == 1) bits &= ~0x1; /* TwoD homogeneous direction: xi */
387 else if (twoD == 2) bits &= ~0x2; /* eta */
388 else if (twoD == 3) bits &= ~0x4; /* zeta */
389 return bits;
390}
Here is the caller graph for this function:

◆ ComputeMomentumStabilityEstimate()

PetscErrorCode ComputeMomentumStabilityEstimate ( UserCtx user,
PetscInt  block_number,
PetscReal  dt,
MomStabCandidate  candidate,
MomStabilityReport rep 
)

Practical conservative momentum pseudo-time stability estimate (shadow).

Compute the momentum pseudo-time stability estimate (shadow/diagnostic).

See header.

Operator-scaled estimate lambda = max_cell (a0/dt + lambda_c + lambda_nu) over active, non-solid interior cells, blocks and MPI ranks (lambda_c already carries the per-direction QUICK scheme factors – it is NOT multiplied by f_c again here). Read-only; no halo exchange, 5 global scalar collectives. NOT a proven spectral radius. Drives nothing in shadow mode.

Definition at line 402 of file momentumsolvers.c.

405{
406 PetscErrorCode ierr;
407 PetscMPIInt rank;
408 PetscFunctionBeginUser;
409
410 /* ---- input validation (no silent fallback) ---- */
411 PetscCheck(user != NULL, PETSC_COMM_WORLD, PETSC_ERR_ARG_NULL, "user is NULL");
412 PetscCheck(rep != NULL, PETSC_COMM_WORLD, PETSC_ERR_ARG_NULL, "rep is NULL");
413 PetscCheck(block_number > 0, PETSC_COMM_WORLD, PETSC_ERR_ARG_OUTOFRANGE, "block_number must be > 0");
414 PetscCheck(candidate >= MOM_STAB_CAND_B && candidate <= MOM_STAB_CAND_D,
415 PETSC_COMM_WORLD, PETSC_ERR_ARG_OUTOFRANGE, "unsupported stability candidate enum");
416 PetscCheck(PetscIsNormalReal(dt) && dt > 0.0, PETSC_COMM_WORLD, PETSC_ERR_ARG_OUTOFRANGE,
417 "dt must be finite and positive");
418
419 SimCtx *simCtx = user[0].simCtx;
420 PetscCheck(simCtx != NULL, PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONGSTATE, "user[0].simCtx is NULL");
421 const PetscReal a0 = MomentumBDFCoefficient(simCtx);
422 const PetscBool centered = (PetscBool)(simCtx->les || simCtx->central);
423 const PetscBool has_nut = (PetscBool)(simCtx->les || simCtx->rans);
424 const PetscBool inviscid = (PetscBool)simCtx->invicid;
425 PetscCheck(inviscid || (PetscIsNormalReal(simCtx->ren) && simCtx->ren > 0.0),
426 PETSC_COMM_WORLD, PETSC_ERR_ARG_OUTOFRANGE, "Reynolds number must be finite and positive when viscous");
427 const PetscReal lambda_t = a0 / dt;
428 const PetscReal nu_mol = inviscid ? 0.0 : 1.0 / simCtx->ren;
429 const PetscInt twoD = simCtx->TwoD;
430
431 /* Shadow-mode completeness flag. The estimate does NOT cover the Clark nonlinear stress
432 Jacobian, nor RANS eddy-viscosity sign behaviour (not verified sign-definite).
433 Body forces in the supported configs read a per-timestep-frozen scalar
434 (simCtx->bulkVelocityCorrection) inside ComputeRHS, so within a pseudo-solve they are a
435 constant forcing with ZERO velocity Jacobian (consistent with the frozen-pressure
436 treatment) -- they do not make the estimate incomplete. */
437 rep->estimate_incomplete = (PetscBool)(simCtx->clark || simCtx->rans);
438
439 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
440
441 /* Per-candidate local maxima; selected candidate also tracks the controlling cell. */
442 PetscReal locmaxB = 0.0, locmaxC = 0.0, locmaxD = 0.0, sel_max = 0.0;
443 PetscReal sel_lc = 0.0, sel_lv = 0.0;
444 PetscInt sel_ci = -1, sel_cj = -1, sel_ck = -1, sel_blk = -1, sel_class = 0, sel_os = 0;
445 PetscInt local_active = 0;
446
447 for (PetscInt bi = 0; bi < block_number; bi++) {
448 UserCtx *u = &user[bi];
449 DMDALocalInfo info = u->info;
450 const PetscInt mx = info.mx, my = info.my, mz = info.mz;
451 const PetscInt xs = info.xs, xe = xs + info.xm;
452 const PetscInt ys = info.ys, ye = ys + info.ym;
453 const PetscInt zs = info.zs, ze = zs + info.zm;
454 /* Interior range == where ComputeRHS evaluates the residual; boundary faces
455 (0 and m-1) are RHS-masked, so excluding them implements active-row masking
456 for the normal boundary rows. The last physical face (m-2) stays IN with the
457 full cell estimate (its tangential rows remain active). */
458 const PetscInt lxs = (xs==0)?xs+1:xs, lxe = (xe==mx)?xe-1:xe;
459 const PetscInt lys = (ys==0)?ys+1:ys, lye = (ye==my)?ye-1:ye;
460 const PetscInt lzs = (zs==0)?zs+1:zs, lze = (ze==mz)?ze-1:ze;
461
462 /* Non-periodic boundary flags (QUICK stencil is degraded near these). */
463 const PetscBool npx0 = (PetscBool)(u->boundary_faces[BC_FACE_NEG_X].mathematical_type != PERIODIC);
464 const PetscBool npx1 = (PetscBool)(u->boundary_faces[BC_FACE_POS_X].mathematical_type != PERIODIC);
465 const PetscBool npy0 = (PetscBool)(u->boundary_faces[BC_FACE_NEG_Y].mathematical_type != PERIODIC);
466 const PetscBool npy1 = (PetscBool)(u->boundary_faces[BC_FACE_POS_Y].mathematical_type != PERIODIC);
467 const PetscBool npz0 = (PetscBool)(u->boundary_faces[BC_FACE_NEG_Z].mathematical_type != PERIODIC);
468 const PetscBool npz1 = (PetscBool)(u->boundary_faces[BC_FACE_POS_Z].mathematical_type != PERIODIC);
469 const PetscBool wxn = (PetscBool)(u->boundary_faces[BC_FACE_NEG_X].mathematical_type == WALL);
470 const PetscBool wxp = (PetscBool)(u->boundary_faces[BC_FACE_POS_X].mathematical_type == WALL);
471 const PetscBool wyn = (PetscBool)(u->boundary_faces[BC_FACE_NEG_Y].mathematical_type == WALL);
472 const PetscBool wyp = (PetscBool)(u->boundary_faces[BC_FACE_POS_Y].mathematical_type == WALL);
473 const PetscBool wzn = (PetscBool)(u->boundary_faces[BC_FACE_NEG_Z].mathematical_type == WALL);
474 const PetscBool wzp = (PetscBool)(u->boundary_faces[BC_FACE_POS_Z].mathematical_type == WALL);
475
476 Cmpnts ***ucont, ***ucat = NULL;
477 Cmpnts ***icsi, ***ieta, ***izet, ***jcsi, ***jeta, ***jzet, ***kcsi, ***keta, ***kzet;
478 Cmpnts ***csi = NULL, ***eta = NULL, ***zet = NULL;
479 PetscReal ***aj, ***iaj, ***jaj, ***kaj, ***nvert, ***nut = NULL;
480
481 ierr = DMDAVecGetArrayRead(u->fda, u->lUcont, &ucont); CHKERRQ(ierr);
482 ierr = DMDAVecGetArrayRead(u->da, u->lAj, &aj); CHKERRQ(ierr);
483 ierr = DMDAVecGetArrayRead(u->da, u->lIAj, &iaj); CHKERRQ(ierr);
484 ierr = DMDAVecGetArrayRead(u->da, u->lJAj, &jaj); CHKERRQ(ierr);
485 ierr = DMDAVecGetArrayRead(u->da, u->lKAj, &kaj); CHKERRQ(ierr);
486 ierr = DMDAVecGetArrayRead(u->fda, u->lICsi, &icsi); CHKERRQ(ierr);
487 ierr = DMDAVecGetArrayRead(u->fda, u->lIEta, &ieta); CHKERRQ(ierr);
488 ierr = DMDAVecGetArrayRead(u->fda, u->lIZet, &izet); CHKERRQ(ierr);
489 ierr = DMDAVecGetArrayRead(u->fda, u->lJCsi, &jcsi); CHKERRQ(ierr);
490 ierr = DMDAVecGetArrayRead(u->fda, u->lJEta, &jeta); CHKERRQ(ierr);
491 ierr = DMDAVecGetArrayRead(u->fda, u->lJZet, &jzet); CHKERRQ(ierr);
492 ierr = DMDAVecGetArrayRead(u->fda, u->lKCsi, &kcsi); CHKERRQ(ierr);
493 ierr = DMDAVecGetArrayRead(u->fda, u->lKEta, &keta); CHKERRQ(ierr);
494 ierr = DMDAVecGetArrayRead(u->fda, u->lKZet, &kzet); CHKERRQ(ierr);
495 ierr = DMDAVecGetArrayRead(u->da, u->lNvert, &nvert); CHKERRQ(ierr);
496 if (has_nut) { ierr = DMDAVecGetArrayRead(u->da, u->lNu_t, &nut); CHKERRQ(ierr); }
497 /* Cartesian velocity + cell metrics: always read so candidate D's gradient term
498 (and hence rep->lambda_D) is meaningful regardless of the selected candidate.
499 Precondition: lUcat fresh (Contra2Cart has run in the preceding residual eval). */
500 ierr = DMDAVecGetArrayRead(u->fda, u->lUcat, &ucat); CHKERRQ(ierr);
501 ierr = DMDAVecGetArrayRead(u->fda, u->lCsi, &csi); CHKERRQ(ierr);
502 ierr = DMDAVecGetArrayRead(u->fda, u->lEta, &eta); CHKERRQ(ierr);
503 ierr = DMDAVecGetArrayRead(u->fda, u->lZet, &zet); CHKERRQ(ierr);
504
505 /* Error flag for the cell loop: on a bad metric/estimate we record the location and
506 jump to block_cleanup so EVERY acquired array is restored before the error returns. */
507 PetscErrorCode cell_err = 0;
508 PetscInt be_i = -1, be_j = -1, be_k = -1;
509 const char *be_what = NULL;
510
511 for (PetscInt k = lzs; k < lze; k++) {
512 for (PetscInt j = lys; j < lye; j++) {
513 for (PetscInt i = lxs; i < lxe; i++) {
514 /* ---- active-row mask: skip only locations with no active unknown ---- */
515 const PetscInt rows = MomCellActiveRows(nvert, k, j, i, mx, my, mz,
516 npx1, npy1, npz1, twoD);
517 if (rows == 0) continue;
518 local_active++;
519 const PetscReal Ajc = aj[k][j][i];
520 if (!(PetscIsNormalReal(Ajc) && Ajc > 0.0)) {
521 cell_err = PETSC_ERR_FP; be_i = i; be_j = j; be_k = k;
522 be_what = "non-finite/non-positive cell inverse-Jacobian";
523 goto block_cleanup;
524 }
525
526 /* ---- directional QUICK modification (per direction; both faces; 2nd neighbour) ---- */
527 const PetscBool mod_x = MomQuickDirModified(nvert, k, j, i, i, mx, npx0, npx1,
528 info.gxs, info.gxs+info.gxm, 'i');
529 const PetscBool mod_y = MomQuickDirModified(nvert, k, j, i, j, my, npy0, npy1,
530 info.gys, info.gys+info.gym, 'j');
531 const PetscBool mod_z = MomQuickDirModified(nvert, k, j, i, k, mz, npz0, npz1,
532 info.gzs, info.gzs+info.gzm, 'k');
533
534 /* ---- classify cell: interior / physical-boundary / IB-adjacent ---- */
535 const PetscBool bnd = (PetscBool)((npx0 && i<=1) || (npx1 && i>=mx-2) ||
536 (npy0 && j<=1) || (npy1 && j>=my-2) ||
537 (npz0 && k<=1) || (npz1 && k>=mz-2));
538 const PetscBool ib = MomCellHasSolidNeighbor(nvert, k, j, i);
539 const PetscInt cls = ib ? 2 : (bnd ? 1 : 0);
540
541 /* ---- convective: six contravariant face fluxes, DIRECTIONAL QUICK factors ---- */
542 const PetscReal Uxp = ucont[k][j][i].x, Uxm = ucont[k][j][i-1].x;
543 const PetscReal Uyp = ucont[k][j][i].y, Uym = ucont[k][j-1][i].y;
544 const PetscReal Uzp = ucont[k][j][i].z, Uzm = ucont[k-1][j][i].z;
545 const PetscReal divU = PetscAbsReal((Uxp-Uxm)+(Uyp-Uym)+(Uzp-Uzm));
546 const PetscReal fx = centered ? 1.0 : (mod_x ? 2.5 : (4.0/3.0));
547 const PetscReal fy = centered ? 1.0 : (mod_y ? 2.5 : (4.0/3.0));
548 const PetscReal fz = centered ? 1.0 : (mod_z ? 2.5 : (4.0/3.0));
549
550 /* per-direction transport scale; C's divergence term stays unscaled by f_d. */
551 const PetscReal lcB = 0.5 * Ajc * (
552 fx * (PetscAbsReal(Uxp)+PetscAbsReal(Uxm))
553 + fy * (PetscAbsReal(Uyp)+PetscAbsReal(Uym))
554 + fz * (PetscAbsReal(Uzp)+PetscAbsReal(Uzm)) );
555 const PetscReal lcC = lcB + 0.5 * Ajc * divU;
556 /* lambda_grad_u = max_i sum_j |du_i/dx_j| from fresh Cartesian velocity
557 (the nonlinear zero-order Jacobian term delta_u . grad u).
558 Physical gradient: d/dx_dir = Ajc*(Csi.dir d/dxi + Eta.dir d/deta + Zet.dir d/dzeta). */
559 const Cmpnts duc = { 0.5*(ucat[k][j][i+1].x-ucat[k][j][i-1].x),
560 0.5*(ucat[k][j][i+1].y-ucat[k][j][i-1].y),
561 0.5*(ucat[k][j][i+1].z-ucat[k][j][i-1].z) };
562 const Cmpnts due = { 0.5*(ucat[k][j+1][i].x-ucat[k][j-1][i].x),
563 0.5*(ucat[k][j+1][i].y-ucat[k][j-1][i].y),
564 0.5*(ucat[k][j+1][i].z-ucat[k][j-1][i].z) };
565 const Cmpnts duz = { 0.5*(ucat[k+1][j][i].x-ucat[k-1][j][i].x),
566 0.5*(ucat[k+1][j][i].y-ucat[k-1][j][i].y),
567 0.5*(ucat[k+1][j][i].z-ucat[k-1][j][i].z) };
568 const Cmpnts C = csi[k][j][i], E = eta[k][j][i], Z = zet[k][j][i];
569 #define MOM_ROW(cmp) ( \
570 PetscAbsReal(Ajc*(C.x*duc.cmp + E.x*due.cmp + Z.x*duz.cmp)) + \
571 PetscAbsReal(Ajc*(C.y*duc.cmp + E.y*due.cmp + Z.y*duz.cmp)) + \
572 PetscAbsReal(Ajc*(C.z*duc.cmp + E.z*due.cmp + Z.z*duz.cmp)) )
573 const PetscReal lgrad = PetscMax(MOM_ROW(x), PetscMax(MOM_ROW(y), MOM_ROW(z)));
574 #undef MOM_ROW
575 const PetscReal lcD = lcC + lgrad;
576
577 /* ---- viscous: six faces, face Jacobians, full metric rows ---- */
578 PetscReal lv = 0.0;
579 if (!inviscid) {
580 /* xi+ (i), xi- (i-1) */
581 for (PetscInt s = 0; s < 2; s++) {
582 const PetscInt fi = (s==0) ? i : i-1;
583 if (!(PetscIsNormalReal(iaj[k][j][fi]) && iaj[k][j][fi] > 0.0)) {
584 cell_err = PETSC_ERR_FP; be_i=i; be_j=j; be_k=k;
585 be_what = "non-finite/non-positive xi-face inverse-Jacobian"; goto block_cleanup; }
586 PetscReal nuf = nu_mol;
587 if (has_nut) {
588 PetscReal nt = 0.5*(nut[k][j][fi] + nut[k][j][fi+1]);
589 if ((wxn && fi==0) || (wxp && fi==mx-2)) nt = 0.0;
590 nuf += nt;
591 }
592 lv += PetscAbsReal(nuf) * Ajc * iaj[k][j][fi]
593 * MomFaceGabs(icsi[k][j][fi], ieta[k][j][fi], izet[k][j][fi]);
594 }
595 /* eta+ (j), eta- (j-1) */
596 for (PetscInt s = 0; s < 2; s++) {
597 const PetscInt fj = (s==0) ? j : j-1;
598 if (!(PetscIsNormalReal(jaj[k][fj][i]) && jaj[k][fj][i] > 0.0)) {
599 cell_err = PETSC_ERR_FP; be_i=i; be_j=j; be_k=k;
600 be_what = "non-finite/non-positive eta-face inverse-Jacobian"; goto block_cleanup; }
601 PetscReal nuf = nu_mol;
602 if (has_nut) {
603 PetscReal nt = 0.5*(nut[k][fj][i] + nut[k][fj+1][i]);
604 if ((wyn && fj==0) || (wyp && fj==my-2)) nt = 0.0;
605 nuf += nt;
606 }
607 /* eta-face normal is the eta metric (jeta) -> pass as N. */
608 lv += PetscAbsReal(nuf) * Ajc * jaj[k][fj][i]
609 * MomFaceGabs(jeta[k][fj][i], jcsi[k][fj][i], jzet[k][fj][i]);
610 }
611 /* zeta+ (k), zeta- (k-1) */
612 for (PetscInt s = 0; s < 2; s++) {
613 const PetscInt fk = (s==0) ? k : k-1;
614 if (!(PetscIsNormalReal(kaj[fk][j][i]) && kaj[fk][j][i] > 0.0)) {
615 cell_err = PETSC_ERR_FP; be_i=i; be_j=j; be_k=k;
616 be_what = "non-finite/non-positive zeta-face inverse-Jacobian"; goto block_cleanup; }
617 PetscReal nuf = nu_mol;
618 if (has_nut) {
619 PetscReal nt = 0.5*(nut[fk][j][i] + nut[fk+1][j][i]);
620 if ((wzn && fk==0) || (wzp && fk==mz-2)) nt = 0.0;
621 nuf += nt;
622 }
623 /* zeta-face normal is the zeta metric (kzet) -> pass as N. */
624 lv += PetscAbsReal(nuf) * Ajc * kaj[fk][j][i]
625 * MomFaceGabs(kzet[fk][j][i], kcsi[fk][j][i], keta[fk][j][i]);
626 }
627 lv *= 4.0; /* 2 (two-face row-sum) x 2 (longitudinal full-stress) */
628 }
629
630 /* one-sided viscous cross-derivative branch near a solid: conservative x2,
631 applied ONCE per cell, after a complete 3x3x3 solid-band check. */
632 PetscInt one_sided = 0;
633 if (!inviscid && MomCellUsesOneSidedViscousStencil(nvert, k, j, i)) {
634 one_sided = 1;
635 lv *= 2.0;
636 }
637
638 /* ---- candidate totals and running maxima ---- */
639 const PetscReal lB = lambda_t + lcB + lv;
640 const PetscReal lC = lambda_t + lcC + lv;
641 const PetscReal lD = lambda_t + lcD + lv;
642 if (PetscIsInfOrNanReal(lD)) {
643 cell_err = PETSC_ERR_FP; be_i=i; be_j=j; be_k=k;
644 be_what = "non-finite stability estimate"; goto block_cleanup;
645 }
646 locmaxB = PetscMax(locmaxB, lB);
647 locmaxC = PetscMax(locmaxC, lC);
648 locmaxD = PetscMax(locmaxD, lD);
649
650 const PetscReal lsel = (candidate==MOM_STAB_CAND_B)?lB:(candidate==MOM_STAB_CAND_C)?lC:lD;
651 const PetscReal lcsel = (candidate==MOM_STAB_CAND_B)?lcB:(candidate==MOM_STAB_CAND_C)?lcC:lcD;
652 if (lsel > sel_max) {
653 sel_max = lsel; sel_lc = lcsel; sel_lv = lv;
654 sel_ci = i; sel_cj = j; sel_ck = k; sel_blk = bi;
655 sel_class = cls; sel_os = one_sided;
656 }
657 }
658 }
659 }
660
661 /* Single cleanup point: reached on normal completion AND on any cell-loop error.
662 Every successful GetArrayRead above has exactly one matching restore here. */
663 block_cleanup:
664 ierr = DMDAVecRestoreArrayRead(u->fda, u->lUcont, &ucont); CHKERRQ(ierr);
665 ierr = DMDAVecRestoreArrayRead(u->da, u->lAj, &aj); CHKERRQ(ierr);
666 ierr = DMDAVecRestoreArrayRead(u->da, u->lIAj, &iaj); CHKERRQ(ierr);
667 ierr = DMDAVecRestoreArrayRead(u->da, u->lJAj, &jaj); CHKERRQ(ierr);
668 ierr = DMDAVecRestoreArrayRead(u->da, u->lKAj, &kaj); CHKERRQ(ierr);
669 ierr = DMDAVecRestoreArrayRead(u->fda, u->lICsi, &icsi); CHKERRQ(ierr);
670 ierr = DMDAVecRestoreArrayRead(u->fda, u->lIEta, &ieta); CHKERRQ(ierr);
671 ierr = DMDAVecRestoreArrayRead(u->fda, u->lIZet, &izet); CHKERRQ(ierr);
672 ierr = DMDAVecRestoreArrayRead(u->fda, u->lJCsi, &jcsi); CHKERRQ(ierr);
673 ierr = DMDAVecRestoreArrayRead(u->fda, u->lJEta, &jeta); CHKERRQ(ierr);
674 ierr = DMDAVecRestoreArrayRead(u->fda, u->lJZet, &jzet); CHKERRQ(ierr);
675 ierr = DMDAVecRestoreArrayRead(u->fda, u->lKCsi, &kcsi); CHKERRQ(ierr);
676 ierr = DMDAVecRestoreArrayRead(u->fda, u->lKEta, &keta); CHKERRQ(ierr);
677 ierr = DMDAVecRestoreArrayRead(u->fda, u->lKZet, &kzet); CHKERRQ(ierr);
678 ierr = DMDAVecRestoreArrayRead(u->da, u->lNvert, &nvert); CHKERRQ(ierr);
679 if (has_nut) { ierr = DMDAVecRestoreArrayRead(u->da, u->lNu_t, &nut); CHKERRQ(ierr); }
680 ierr = DMDAVecRestoreArrayRead(u->fda, u->lUcat, &ucat); CHKERRQ(ierr);
681 ierr = DMDAVecRestoreArrayRead(u->fda, u->lCsi, &csi); CHKERRQ(ierr);
682 ierr = DMDAVecRestoreArrayRead(u->fda, u->lEta, &eta); CHKERRQ(ierr);
683 ierr = DMDAVecRestoreArrayRead(u->fda, u->lZet, &zet); CHKERRQ(ierr);
684
685 /* Now that this block's arrays are restored, surface any cell-loop error. */
686 PetscCheck(cell_err == 0, PETSC_COMM_SELF, cell_err, "%s at (%" PetscInt_FMT
687 ",%" PetscInt_FMT ",%" PetscInt_FMT ")", be_what ? be_what : "error", be_i, be_j, be_k);
688 }
689
690 /* ---- global reductions (no ghost/halo exchange; scalar collectives only) ----
691 5 collectives total: one 3-element MAX (B/C/D), one SUM (active-cell count),
692 one MIN (portable owner selection), and two broadcasts (the controlling-cell
693 real breakdown and integer indices). */
694 PetscReal loc3[3] = { locmaxB, locmaxC, locmaxD }, glo3[3];
695 ierr = MPI_Allreduce(loc3, glo3, 3, MPIU_REAL, MPI_MAX, PETSC_COMM_WORLD); CHKERRQ(ierr);
696
697 PetscInt global_active = 0;
698 ierr = MPI_Allreduce(&local_active, &global_active, 1, MPIU_INT, MPI_SUM, PETSC_COMM_WORLD); CHKERRQ(ierr);
699
700 /* The selected candidate's global max equals glo3[candidate] (no extra reduction).
701 Portable owner pick: lowest rank whose local sel_max equals the global max. */
702 const PetscReal sel_global = glo3[(int)candidate];
703 PetscMPIInt nproc, claim, owner;
704 ierr = MPI_Comm_size(PETSC_COMM_WORLD, &nproc); CHKERRQ(ierr);
705 claim = (sel_max == sel_global) ? rank : nproc;
706 ierr = MPI_Allreduce(&claim, &owner, 1, MPI_INT, MPI_MIN, PETSC_COMM_WORLD); CHKERRQ(ierr);
707 if (owner == nproc) owner = 0; /* no active cell anywhere: deterministic fallback owner */
708
709 PetscReal dbuf[2] = { sel_lc, sel_lv };
710 PetscInt ibuf[6] = { sel_ci, sel_cj, sel_ck, sel_blk, sel_class, sel_os };
711 ierr = MPI_Bcast(dbuf, 2, MPIU_REAL, owner, PETSC_COMM_WORLD); CHKERRQ(ierr);
712 ierr = MPI_Bcast(ibuf, 6, MPIU_INT, owner, PETSC_COMM_WORLD); CHKERRQ(ierr);
713
714 rep->lambda_t = lambda_t;
715 rep->lambda_c = dbuf[0];
716 rep->lambda_v = dbuf[1];
717 rep->lambda = sel_global;
718 rep->lambda_B = glo3[0]; rep->lambda_C = glo3[1]; rep->lambda_D = glo3[2];
719 rep->ci = ibuf[0]; rep->cj = ibuf[1]; rep->ck = ibuf[2];
720 rep->cblock = ibuf[3]; rep->cclass = ibuf[4]; rep->one_sided = ibuf[5];
721 rep->active_cells = global_active;
722
723 /* Only a genuinely empty active set (all cells masked) may fall back to the temporal
724 term alone. A non-finite or non-positive estimate with active cells is a hard error. */
725 if (global_active == 0) {
726 rep->lambda = lambda_t;
727 } else {
728 PetscCheck(PetscIsNormalReal(rep->lambda) && rep->lambda > 0.0, PETSC_COMM_WORLD, PETSC_ERR_FP,
729 "momentum stability estimate is non-finite or non-positive with %" PetscInt_FMT
730 " active cells", global_active);
731 }
732
733 /* Dominant limiter at the controlling cell. */
734 if (rep->lambda_t >= rep->lambda_c && rep->lambda_t >= rep->lambda_v) rep->limiter = MOM_STAB_LIMITER_TIME;
735 else if (rep->lambda_c >= rep->lambda_v) rep->limiter = MOM_STAB_LIMITER_CONVECTION;
737
738 PetscFunctionReturn(0);
739}
#define MOM_ROW(cmp)
static PetscBool MomQuickDirModified(PetscReal ***nvert, PetscInt a, PetscInt b, PetscInt c, PetscInt idx, PetscInt m, PetscBool np0, PetscBool np1, PetscInt g0, PetscInt g1, char dir)
static PetscBool MomCellHasSolidNeighbor(PetscReal ***nvert, PetscInt k, PetscInt j, PetscInt i)
static PetscReal MomFaceGabs(Cmpnts N, Cmpnts A, Cmpnts B)
PetscInt MomCellActiveRows(PetscReal ***nvert, PetscInt k, PetscInt j, PetscInt i, PetscInt mx, PetscInt my, PetscInt mz, PetscBool np_x1, PetscBool np_y1, PetscBool np_z1, PetscInt twoD)
Active staggered-momentum row mask for a cell (exposed for unit testing).
static PetscBool MomCellUsesOneSidedViscousStencil(PetscReal ***nvert, PetscInt k, PetscInt j, PetscInt i)
PetscBool estimate_incomplete
@ MOM_STAB_LIMITER_CONVECTION
@ MOM_STAB_LIMITER_VISCOSITY
@ MOM_STAB_LIMITER_TIME
MomStabLimiter limiter
@ MOM_STAB_CAND_B
@ MOM_STAB_CAND_C
@ MOM_STAB_CAND_D
PetscInt clark
Definition variables.h:822
@ PERIODIC
Definition variables.h:292
@ WALL
Definition variables.h:286
PetscInt TwoD
Definition variables.h:727
BoundaryFaceConfig boundary_faces[6]
Definition variables.h:931
Vec lIEta
Definition variables.h:977
Vec lIZet
Definition variables.h:977
Vec lNvert
Definition variables.h:939
PetscInt rans
Definition variables.h:821
Vec lZet
Definition variables.h:974
PetscReal ren
Definition variables.h:744
Vec lIAj
Definition variables.h:977
Vec lKEta
Definition variables.h:979
Vec lJCsi
Definition variables.h:978
PetscInt invicid
Definition variables.h:727
Vec lKZet
Definition variables.h:979
Vec lNu_t
Definition variables.h:982
Vec lJEta
Definition variables.h:978
Vec lCsi
Definition variables.h:974
Vec lKCsi
Definition variables.h:979
PetscInt central
Definition variables.h:742
Vec lJZet
Definition variables.h:978
Vec lUcont
Definition variables.h:939
Vec lAj
Definition variables.h:974
Vec lICsi
Definition variables.h:977
Vec lUcat
Definition variables.h:939
Vec lEta
Definition variables.h:974
PetscInt les
Definition variables.h:821
BCType mathematical_type
Definition variables.h:368
Vec lJAj
Definition variables.h:978
Vec lKAj
Definition variables.h:979
@ BC_FACE_NEG_X
Definition variables.h:262
@ BC_FACE_POS_Z
Definition variables.h:264
@ BC_FACE_POS_Y
Definition variables.h:263
@ BC_FACE_NEG_Z
Definition variables.h:264
@ BC_FACE_POS_X
Definition variables.h:262
@ BC_FACE_NEG_Y
Definition variables.h:263
User-defined context containing data specific to a single computational grid level.
Definition variables.h:906
Here is the call graph for this function:
Here is the caller graph for this function:

◆ MomentumSolver_DualTime_Picard_JamesonRK()

PetscErrorCode MomentumSolver_DualTime_Picard_JamesonRK ( UserCtx user,
IBMNodes ibm,
FSInfo fsi 
)

Internal helper implementation: MomentumSolver_DualTime_Picard_JamesonRK().

Solves the momentum equations using dual-time Picard iteration with Jameson RK smoothing.

Local to this translation unit.

Definition at line 748 of file momentumsolvers.c.

749{
750 (void)ibm;
751 (void)fsi;
752 // --- CONTEXT ACQUISITION BLOCK ---
753 SimCtx *simCtx = user[0].simCtx;
754 const PetscInt block_number = simCtx->block_number;
755
756 // Legacy Names (Physics/Time) - Kept for brevity in formulas
757 const PetscInt ti = simCtx->step;
758 const PetscReal dt = simCtx->dt; // Physical Time Step
759 /* cfl: initial pseudo-CFL Courant number (dimensionless, flow-independent since Phase 3).
760 * Actual dtau = cfl / lambda_max, computed after the spectral radius estimate below. */
761 const PetscReal cfl = simCtx->pseudo_cfl;
762 const PetscReal alfa[] = {0.25, 1.0/3.0, 0.5, 1.0}; // Jameson RK smoothing coefficients
763 Vec *pRhs; // Per-block backup of Rhs at last accepted pseudo-state.
764
765 // State flags
766 PetscBool force_restart = PETSC_FALSE;
767
768 // Renamed Solver Parameters
769 const PetscInt max_pseudo_steps = simCtx->mom_max_pseudo_steps; // Max Dual-Time Iterations
770 const PetscReal tol_abs_delta = simCtx->mom_atol; // Stop if |dU| < tol
771 const PetscReal tol_rtol_delta = simCtx->mom_rtol; // Stop if |dU|/|dU0| < tol
772 // --- END CONTEXT ACQUISITION BLOCK ---
773
774 PetscErrorCode ierr;
775 PetscMPIInt rank;
776 PetscInt istage, pseudo_iter;
777 PetscInt accepted_iter = 0, rejected_iter = 0, recovery_streak = 0;
778 PetscReal ts, te, cput;
779
780 // --- Global Convergence Metrics ---
781 PetscReal global_norm_delta = 10.0;
782 PetscReal global_rel_delta = 1.0;
783 PetscReal global_norm_resid = 1.0;
784 PetscReal global_rel_resid = 1.0;
785 PetscReal smoothed_trial_ratio = 1.0; /* EMA-smoothed ratio; initialized neutral */
786
787 // --- Spectral-radius-based pseudo-time step ---
788 /* lambda_max: global maximum spectral radius [1/s], computed once per physical timestep.
789 * pseudo_dtau[bi]: physical pseudo-time step dtau = pseudo_cfl / lambda_max [time].
790 * Unlike the old scheme (dtau = pseudo_cfl * dt), this is independent of dt and makes
791 * pseudo_cfl a true Courant number. (2.83 is only the scalar imaginary-axis RK limit,
792 * not a generally-stable value for the actual operator -- to be characterized in A4.)
793 * dtau_min / dtau_max: per-timestep bounds derived from simCtx->min/max_pseudo_cfl. */
794 PetscReal lambda_max = 0.0;
795 /* cfl_cap: dimensionless memory of where trials have failed. Without it the controller
796 * re-discovers the stability limit every physical step -- measured on the laminar
797 * channel it climbed 1.36 -> 2.00, rejected, recovered, and climbed straight back, with
798 * 28.8% of all trials spent above the limit. Carried in CFL (not dtau) so it stays
799 * meaningful as lambda_max evolves; see the rejection and growth paths below. */
800 PetscReal cfl_cap = simCtx->max_pseudo_cfl;
801 /* resid_ref: reference scale for the residual [same units as R], recomputed each
802 * physical step. See the assembly below for why the absolute residual tolerance is
803 * measured against this rather than being a raw dimensional bound. */
804 PetscReal resid_ref = 0.0;
805 PetscReal dtau_min, dtau_max;
806
807 // --- Local Metric Arrays (Per Block) ---
808 PetscReal *delta_sol_norm_init, *delta_sol_norm_prev, *delta_sol_norm_curr, *delta_sol_rel_curr;
809 PetscReal *resid_norm_init, *resid_norm_prev, *resid_norm_curr, *resid_rel_curr;
810 PetscReal *pseudo_dtau; /* per-block pseudo-time step dtau [physical time, NOT dtau/dt] */
811 PetscReal *trial_ratio_log; /* per-block step-to-step residual ratio (for diagnostics) */
812 PetscReal last_accepted_resid; /* last accepted |R|, for final summary */
813
814 PetscFunctionBeginUser;
816 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
817 LOG_ALLOW(GLOBAL, LOG_INFO, "Executing Dual-Time Momentum Solver for %d block(s).\n", block_number);
818
819 // --- Allocate metric arrays ---
820 ierr = PetscMalloc2(block_number, &delta_sol_norm_init, block_number, &delta_sol_norm_prev); CHKERRQ(ierr);
821 ierr = PetscMalloc2(block_number, &delta_sol_norm_curr, block_number, &delta_sol_rel_curr); CHKERRQ(ierr);
822 ierr = PetscMalloc2(block_number, &resid_norm_init, block_number, &resid_norm_prev); CHKERRQ(ierr);
823 ierr = PetscMalloc2(block_number, &resid_norm_curr, block_number, &pseudo_dtau); CHKERRQ(ierr);
824 ierr = PetscMalloc1(block_number, &resid_rel_curr); CHKERRQ(ierr);
825 ierr = PetscMalloc1(block_number, &trial_ratio_log); CHKERRQ(ierr);
826 ierr = PetscMalloc1(block_number, &pRhs); CHKERRQ(ierr);
827 last_accepted_resid = 0.0;
828
829 ierr = PetscTime(&ts); CHKERRQ(ierr);
830
831 // --- 1. Pre-Loop Initialization ---
832
833 if (block_number > 1) {
834 // ierr = Block_Interface_U(user); CHKERRQ(ierr);
835 }
836
837 for (PetscInt bi = 0; bi < block_number; bi++) {
838 ierr = ApplyBoundaryConditions(&user[bi]); CHKERRQ(ierr);
839
840 // Immersed boundary interpolation (if enabled)
841
842 // Allocate workspace
843 ierr = VecDuplicate(user[bi].Ucont, &user[bi].Rhs); CHKERRQ(ierr);
844 ierr = VecDuplicate(user[bi].Rhs, &pRhs[bi]); CHKERRQ(ierr);
845 ierr = VecDuplicate(user[bi].Ucont, &user[bi].dUcont); CHKERRQ(ierr);
846 ierr = VecDuplicate(user[bi].Ucont, &user[bi].pUcont); CHKERRQ(ierr);
847
848 // Initialize Backup (pUcont) with current state
849 ierr = VecCopy(user[bi].Ucont, user[bi].pUcont); CHKERRQ(ierr);
850
851 // --- Calculate Initial Total Residual (Spatial + Temporal) ---
852 ierr = ComputeTotalResidual(&user[bi]); CHKERRQ(ierr);
853
854 // Backup Rhs with current state
855 ierr = VecCopy(user[bi].Rhs, pRhs[bi]); CHKERRQ(ierr);
856
857 // Compute Initial Norms
858 ierr = VecNorm(user[bi].Rhs, NORM_INFINITY, &resid_norm_init[bi]); CHKERRQ(ierr);
859
860 /* Reference scale for the absolute residual test (see its use below). VecNorm is
861 * collective, so this is already global; take the max across blocks. */
862 {
863 PetscReal ucont_inf;
864 ierr = VecNorm(user[bi].Ucont, NORM_INFINITY, &ucont_inf); CHKERRQ(ierr);
865 resid_ref = PetscMax(resid_ref, MomentumBDFCoefficient(simCtx) * ucont_inf / dt);
866 }
867
868 // Initialize history for backtracking logic
869 resid_norm_prev[bi] = resid_norm_init[bi]; /* meaningful ratio on first iteration */
870 delta_sol_norm_prev[bi] = 1000.0;
871 /* pseudo_dtau[bi] is set after ComputeGlobalSpectralRadiusEstimate below. */
872
873 LOG_ALLOW(GLOBAL,LOG_INFO," Block %d | Max RHS = %.6f | initial pseudo-CFL = %.4f .\n", bi, resid_norm_init[bi], cfl);
874 }
875
876 /* --- Spectral Radius Estimate (computed once per physical timestep) ---
877 * lambda_max [1/s] is the global maximum of (face_flux_sum * inverse_cell_volume)
878 * over all owned cells across all MPI ranks. See ComputeGlobalSpectralRadiusEstimate().
879 *
880 * dtau = pseudo_cfl / lambda_max (pseudo_cfl is now a true Courant number)
881 * dtau_min/max are derived from the user-specified min/max_pseudo_cfl bounds.
882 *
883 * The BDF2 lower bound inside the helper ensures lambda_max >= 1.5/dt, so dtau
884 * remains finite even when the velocity field is zero (startup / stagnant ICs). */
885 ierr = ComputeGlobalSpectralRadiusEstimate(user, block_number, dt, &lambda_max); CHKERRQ(ierr);
886 dtau_min = simCtx->min_pseudo_cfl / lambda_max;
887 dtau_max = simCtx->max_pseudo_cfl / lambda_max;
888
889 /* Initialise per-block pseudo-time step from the warm-start CFL (carried from last timestep). */
890 for (PetscInt bi = 0; bi < block_number; bi++) {
891 pseudo_dtau[bi] = cfl / lambda_max; /* dtau [physical time], NOT a fraction of dt */
892 }
893
894 /* --- SHADOW MODE: new operator-scaled stability estimate (diagnostic only) ---
895 * Computes the Workstream-A estimate alongside the legacy one and logs a compact
896 * comparison. The legacy estimate above continues to drive production dtau; the
897 * shadow estimate changes nothing. Enable with -mom_stability_shadow. */
898 {
899 PetscBool shadow = PETSC_FALSE;
900 ierr = PetscOptionsGetBool(NULL, NULL, "-mom_stability_shadow", &shadow, NULL); CHKERRQ(ierr);
901 if (shadow) {
903 ierr = ComputeMomentumStabilityEstimate(user, block_number, dt, MOM_STAB_CAND_C, &rep); CHKERRQ(ierr);
904 const char *lim = (rep.limiter==MOM_STAB_LIMITER_TIME) ? "time"
905 : (rep.limiter==MOM_STAB_LIMITER_CONVECTION) ? "convection" : "viscosity";
906 /* Detailed shadow comparison at DEBUG only (gated by level + function allowlist);
907 never printed unconditionally. Enable in tests via the logging controls. */
909 "Momentum scale [shadow]: legacy_dtau=%.4e new_dtau=%.4e ratio=%.4f limiter=%s | "
910 "lambda_legacy=%.4e lambda_new=%.4e (B=%.4e C=%.4e D=%.4e) | "
911 "lt=%.4e lc=%.4e lv=%.4e | cell=(%d,%d,%d) blk=%d class=%d onesided=%d\n",
912 cfl / lambda_max, cfl / rep.lambda, rep.lambda / lambda_max, lim,
913 lambda_max, rep.lambda, rep.lambda_B, rep.lambda_C, rep.lambda_D,
914 rep.lambda_t, rep.lambda_c, rep.lambda_v,
915 (int)rep.ci, (int)rep.cj, (int)rep.ck, (int)rep.cblock, (int)rep.cclass, (int)rep.one_sided);
916 }
917 }
918
920 "Dual-time solver: lambda_max=%.4e [1/s] resid_ref=%.4e dtau_init=%.4e dtau range [%.4e, %.4e] "
921 "CFL range [%.4f, %.4f] rejection_threshold=%.3f (EMA alpha=%.2f) "
922 "growth=%.3f reduction=%.3f max_accepted=%d.\n",
923 lambda_max, resid_ref, cfl / lambda_max, dtau_min, dtau_max,
924 simCtx->min_pseudo_cfl, simCtx->max_pseudo_cfl,
926 simCtx->mom_ratio_ema_alpha,
928 max_pseudo_steps);
929
930 // --- 2. Main Pseudo-Time Iteration Loop ---
931 pseudo_iter = 0;
932 PetscBool residual_convergence_enabled =
933 (PetscBool)(simCtx->mom_resid_atol > 0.0 || simCtx->mom_resid_rtol > 0.0);
934 PetscBool converged = PETSC_FALSE;
935 PetscBool last_trial_nonfinite = PETSC_FALSE;
936 /* Rejected iterations no longer consume the accepted-iteration budget.
937 A hard cap of 3x max_pseudo_steps guards against infinite rejection loops. */
938 const PetscInt max_total_attempts = max_pseudo_steps * 3;
939 while (!converged && accepted_iter < max_pseudo_steps && pseudo_iter < max_total_attempts)
940 {
941 pseudo_iter++;
942 force_restart = PETSC_FALSE;
943
944 // Every attempt starts from the last globally accepted state.
945 for (PetscInt bi = 0; bi < block_number; bi++) {
946 ierr = VecCopy(user[bi].pUcont, user[bi].Ucont); CHKERRQ(ierr);
947 ierr = VecCopy(pRhs[bi], user[bi].Rhs); CHKERRQ(ierr);
948 const FieldId staggered_fields[] = {FIELD_ID_UCONT};
949 ierr = SynchronizePeriodicStaggeredFields(&user[bi], 1, staggered_fields); CHKERRQ(ierr);
950 ierr = ApplyBoundaryConditions(&user[bi]); CHKERRQ(ierr);
951 }
952
953 for (PetscInt bi = 0; bi < block_number; bi++) {
954
955 // === 4-Stage Jameson RK Smoothing Loop ===
956 for (istage = 0; istage < 4; istage++) {
957
958 LOG_ALLOW(GLOBAL,LOG_TRACE," Pseudo-Iter: %d | RK-Stage: %d\n", pseudo_iter, istage);
959
960 /* RK Update: U_new = U_old + (dtau * alpha) * Residual
961 * pseudo_dtau[bi] IS dtau [physical time] — no further multiplication by dt.
962 * (In the old scheme dtau = pseudo_cfl * dt; here dtau = pseudo_cfl / lambda_max.) */
963 ierr = VecWAXPY(user[bi].Ucont,
964 pseudo_dtau[bi] * alfa[istage],
965 user[bi].Rhs,
966 user[bi].pUcont); CHKERRQ(ierr);
967
968 // Sync Ghosts & Re-apply BCs for intermediate stage
969 const FieldId staggered_fields[] = {FIELD_ID_UCONT};
970 ierr = SynchronizePeriodicStaggeredFields(&user[bi], 1, staggered_fields); CHKERRQ(ierr);
971
972 ierr = ApplyBoundaryConditions(&user[bi]); CHKERRQ(ierr);
973
974 // --- Re-calculate Total Residual for next stage ---
975 ierr = ComputeTotalResidual(&user[bi]); CHKERRQ(ierr);
976
977 } // End RK Stages
978
979 // Immersed boundary interpolation (if enabled)
980
981 // === Convergence Metrics Calculation ===
982
983 // Calculate dU = U_current - U_backup
984 ierr = VecWAXPY(user[bi].dUcont, -1.0, user[bi].pUcont, user[bi].Ucont); CHKERRQ(ierr);
985
986 // Compute Infinity Norms
987 ierr = VecNorm(user[bi].dUcont, NORM_INFINITY, &delta_sol_norm_curr[bi]); CHKERRQ(ierr);
988 ierr = VecNorm(user[bi].Rhs, NORM_INFINITY, &resid_norm_curr[bi]); CHKERRQ(ierr);
989
990 // Normalize relative metrics
991 if (pseudo_iter == 1) {
992 delta_sol_norm_init[bi] = delta_sol_norm_curr[bi];
993 delta_sol_rel_curr[bi] = 1.0;
994 resid_rel_curr[bi] = 1.0;
995 // resid_norm_init[bi] set correctly before the loop — do not overwrite
996 } else {
997 if (delta_sol_norm_init[bi] > 1.0e-10)
998 delta_sol_rel_curr[bi] = delta_sol_norm_curr[bi] / delta_sol_norm_init[bi];
999 else
1000 delta_sol_rel_curr[bi] = 0.0;
1001
1002 if(resid_norm_init[bi] > 1.0e-10)
1003 resid_rel_curr[bi] = resid_norm_curr[bi] / resid_norm_init[bi];
1004 else
1005 resid_rel_curr[bi] = 0.0;
1006 }
1007
1008 /* Pre-compute per-block step-to-step trial ratio for later file logging. */
1009 {
1010 const PetscReal resid_floor_log = 1.0e-30;
1011 if (resid_norm_prev[bi] > resid_floor_log)
1012 trial_ratio_log[bi] = resid_norm_curr[bi] / resid_norm_prev[bi];
1013 else if (resid_norm_curr[bi] <= resid_floor_log)
1014 trial_ratio_log[bi] = 0.0;
1015 else
1016 trial_ratio_log[bi] = PETSC_MAX_REAL;
1017 }
1018 } // End loop over blocks
1019
1020 // --- Update Global Convergence Criteria ---
1021 global_norm_delta = -1.0e20;
1022 global_rel_delta = -1.0e20;
1023 global_norm_resid = -1.0e20;
1024 global_rel_resid = -1.0e20;
1025
1026 for (PetscInt bi = 0; bi < block_number; bi++) {
1027 global_norm_delta = PetscMax(delta_sol_norm_curr[bi], global_norm_delta);
1028 global_rel_delta = PetscMax(delta_sol_rel_curr[bi], global_rel_delta);
1029 global_norm_resid = PetscMax(resid_norm_curr[bi], global_norm_resid);
1030 global_rel_resid = PetscMax(resid_rel_curr[bi], global_rel_resid);
1031 }
1032 ierr = PetscTime(&te); CHKERRQ(ierr);
1033 cput = te - ts;
1034 LOG_ALLOW(GLOBAL, LOG_INFO, " Pseudo-Iter(k) %d: |dUk|=%e, |dUk|/|dU0| = %e, |Rk|/|R0| = %e, CPU=%.2fs\n",
1035 pseudo_iter, global_norm_delta, global_rel_delta, global_rel_resid, cput);
1036
1037 // === Adaptive Pseudo-CFL Trial Acceptance and Rollback ===
1038 const PetscReal resid_floor = 1.0e-30;
1039 PetscReal global_trial_ratio = 0.0;
1040 PetscBool global_nonfinite = PETSC_FALSE;
1041 for (PetscInt bi = 0; bi < block_number; bi++) {
1042 PetscReal ratio;
1043 if (resid_norm_prev[bi] > resid_floor) {
1044 ratio = resid_norm_curr[bi] / resid_norm_prev[bi];
1045 } else if (resid_norm_curr[bi] <= resid_floor) {
1046 ratio = 0.0;
1047 } else {
1048 ratio = PETSC_MAX_REAL;
1049 }
1050 global_trial_ratio = PetscMax(global_trial_ratio, ratio);
1051 global_nonfinite = (PetscBool)(global_nonfinite ||
1052 PetscIsInfOrNanReal(delta_sol_norm_curr[bi]) ||
1053 PetscIsInfOrNanReal(resid_norm_curr[bi]) ||
1054 PetscIsInfOrNanReal(ratio));
1055 }
1056
1057 PetscReal reduced_trial_ratio;
1058 PetscMPIInt local_nonfinite = global_nonfinite ? 1 : 0;
1059 PetscMPIInt reduced_nonfinite = 0;
1060 ierr = MPI_Allreduce(&global_trial_ratio, &reduced_trial_ratio, 1, MPIU_REAL, MPI_MAX, PETSC_COMM_WORLD); CHKERRQ(ierr);
1061 ierr = MPI_Allreduce(&local_nonfinite, &reduced_nonfinite, 1, MPI_INT, MPI_LOR, PETSC_COMM_WORLD); CHKERRQ(ierr);
1062 global_trial_ratio = reduced_trial_ratio;
1063 global_nonfinite = reduced_nonfinite ? PETSC_TRUE : PETSC_FALSE;
1064 last_trial_nonfinite = global_nonfinite;
1065
1066 /* EMA-smooth the trial ratio to tolerate occasional non-monotonic residual bumps.
1067 Non-finite trials bypass smoothing and always trigger rollback.
1068
1069 The smoothed value is only a CANDIDATE until the trial is accepted. A rejected
1070 trial is rolled back -- its state never happened -- so letting its ratio persist
1071 into the next decision is a state leak, and a costly one: measured on the laminar
1072 channel, a diverging trial at cfl 2.0 (raw ratio 3.97) pushed the EMA to 1.61, and
1073 the *next* trial at cfl 1.5 was rejected too despite a raw ratio of 0.994, i.e.
1074 despite the residual actually decreasing. That false rejection cost four discarded
1075 ComputeRHS calls and drove dtau down to cfl 1.125, below the measured optimum.
1076 The dtau reduction on the rejection path already carries the information; the EMA
1077 does not need to carry it as well. */
1078 PetscReal trial_smoothed = smoothed_trial_ratio;
1079 if (!global_nonfinite) {
1080 trial_smoothed = simCtx->mom_ratio_ema_alpha * global_trial_ratio
1081 + (1.0 - simCtx->mom_ratio_ema_alpha) * smoothed_trial_ratio;
1082 }
1084 " [k=%d] raw_ratio=%.4e smoothed_ratio=%.4e (threshold=%.3f) | "
1085 "|R_prev|=%.6e | |R_curr|=%.6e | CFL=%.6f\n",
1086 pseudo_iter, global_trial_ratio, trial_smoothed,
1088 resid_norm_prev[0], resid_norm_curr[0], pseudo_dtau[0]);
1089
1090 if (simCtx->no_pseudo_cfl_backtrack) {
1091 /* Diagnostic mode: commit every finite trial; only non-finite triggers rollback. */
1092 force_restart = global_nonfinite;
1093 } else {
1094 force_restart = (PetscBool)(global_nonfinite ||
1096 }
1097
1098 if (force_restart) {
1099 /* old_dtau: dtau used for this (rejected) trial [physical time].
1100 * next_dtau: reduced dtau for the retry, clamped to dtau_min.
1101 * Both are physical-time values; the effective CFL = dtau * lambda_max. */
1102 PetscReal old_dtau = pseudo_dtau[0];
1103 PetscReal next_dtau = PetscMax(dtau_min, old_dtau * simCtx->pseudo_cfl_reduction_factor);
1104 /* This CFL failed: never grow back to it. MOM_CFL_CAP_SAFETY keeps the ceiling
1105 * off the edge, where the smoother damps worst even when it is still stable. */
1106 cfl_cap = PetscMax(simCtx->min_pseudo_cfl,
1107 PetscMin(cfl_cap, old_dtau * lambda_max * MOM_CFL_CAP_SAFETY));
1108 rejected_iter++;
1109 recovery_streak = 0;
1110 for (PetscInt bi = 0; bi < block_number; bi++) {
1111 ierr = VecCopy(user[bi].pUcont, user[bi].Ucont); CHKERRQ(ierr);
1112 ierr = VecCopy(pRhs[bi], user[bi].Rhs); CHKERRQ(ierr);
1113 const FieldId staggered_fields[] = {FIELD_ID_UCONT};
1114 ierr = SynchronizePeriodicStaggeredFields(&user[bi], 1, staggered_fields); CHKERRQ(ierr);
1115 ierr = ApplyBoundaryConditions(&user[bi]); CHKERRQ(ierr);
1116 pseudo_dtau[bi] = next_dtau;
1117 }
1119 " Trial %d REJECTED (raw_ratio=%.4e, smoothed=%.4e, nonfinite=%d); "
1120 "dtau %.4e -> %.4e (cfl_eff %.4f -> %.4f)%s\n",
1121 pseudo_iter, global_trial_ratio, trial_smoothed, (int)global_nonfinite,
1122 old_dtau, next_dtau, old_dtau * lambda_max, next_dtau * lambda_max,
1123 (old_dtau == next_dtau) ? " [AT FLOOR — no dtau reduction]" : "");
1124 /* Log rejected trial to file before rolling back. */
1125 if (!rank) {
1126 for (PetscInt bi = 0; bi < block_number; bi++) {
1127 FILE *f;
1128 char filen[PETSC_MAX_PATH_LEN + 128];
1129 ierr = PetscSNPrintf(filen, sizeof(filen),
1130 "%s/Momentum_Solver_DualTime_Picard_Jameson_RK_History_Block_%1d.log",
1131 simCtx->log_dir, bi); CHKERRQ(ierr);
1132 if (simCtx->step == simCtx->StartStep + 1 && pseudo_iter == 1 && !simCtx->continueMode)
1133 f = fopen(filen, "w");
1134 else
1135 f = fopen(filen, "a");
1136 if (simCtx->continueMode && simCtx->step == simCtx->StartStep + 1 && pseudo_iter == 1)
1137 PetscFPrintf(PETSC_COMM_WORLD, f, "# Continuation from step %" PetscInt_FMT "\n", simCtx->StartStep);
1138 /* Log format: dtau [physical time] + cfl_eff [dimensionless Courant number = dtau*lambda_max] */
1139 PetscFPrintf(PETSC_COMM_WORLD, f,
1140 "Step: %d | PseudoIter(k): %d | dtau: %.6e | cfl_eff: %.4f | |dUk|: %le | "
1141 "|dUk|/|dU0|: %le | |Rk|: %le | |Rk|/|R0|: %le | "
1142 "trial_ratio: %le | smoothed_ratio: %le | status: rejected | "
1143 "dtau_after: %.6e | cfl_eff_after: %.4f\n",
1144 (int)ti, (int)pseudo_iter, old_dtau, old_dtau * lambda_max,
1145 delta_sol_norm_curr[bi], delta_sol_rel_curr[bi],
1146 resid_norm_curr[bi], resid_rel_curr[bi],
1147 trial_ratio_log[bi], trial_smoothed,
1148 next_dtau, next_dtau * lambda_max);
1149 fclose(f);
1150 }
1151 }
1152 if (old_dtau <= dtau_min) {
1153 if (global_nonfinite) {
1154 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_CONV_FAILED,
1155 "Momentum solver produced a non-finite trial at minimum pseudo-CFL.");
1156 }
1157 /* Finite rejection at the dtau floor: reducing further is impossible.
1158 Break instead of retrying bit-identically until max_pseudo_steps. */
1160 " Trial %d REJECTED (ratio=%.4e) at minimum dtau (%.4e, cfl_eff=%.4f) "
1161 "with no further reduction possible; breaking retry loop.\n",
1162 pseudo_iter, global_trial_ratio, old_dtau, old_dtau * lambda_max);
1163 break;
1164 }
1165 continue;
1166 }
1167
1168 accepted_iter++;
1169 smoothed_trial_ratio = trial_smoothed; /* commit only for a trial that survives */
1170 last_trial_nonfinite = PETSC_FALSE;
1171 last_accepted_resid = resid_norm_curr[0]; /* track for final summary */
1172 for (PetscInt bi = 0; bi < block_number; bi++) {
1173 ierr = VecCopy(user[bi].Ucont, user[bi].pUcont); CHKERRQ(ierr);
1174 ierr = VecCopy(user[bi].Rhs, pRhs[bi]); CHKERRQ(ierr);
1175 resid_norm_prev[bi] = resid_norm_curr[bi];
1176 delta_sol_norm_prev[bi] = delta_sol_norm_curr[bi];
1177 }
1178
1179 /* old_dtau: dtau used for this (accepted) trial.
1180 * next_dtau: dtau for the next trial, grown/reduced based on convergence rate.
1181 * Bounds (dtau_min, dtau_max) are derived from simCtx->min/max_pseudo_cfl / lambda_max. */
1182 PetscReal old_dtau = pseudo_dtau[0]; /* save before any update */
1183 PetscReal next_dtau = old_dtau;
1184 if (global_trial_ratio < 0.90) {
1185 /* Residual decreasing fast: grow dtau to accelerate convergence. */
1186 next_dtau *= simCtx->pseudo_cfl_growth_factor;
1187 recovery_streak = 0;
1188 } else if (global_trial_ratio <= 1.0) {
1189 /* Residual barely decreasing: grow only after 3 clean consecutive trials. */
1190 recovery_streak++;
1191 if (recovery_streak >= 3) {
1192 next_dtau *= simCtx->pseudo_cfl_growth_factor;
1193 recovery_streak = 0;
1194 }
1195 } else {
1196 /* Accepted but residual slightly grew (within EMA noise allowance): reduce dtau. */
1197 next_dtau *= simCtx->pseudo_cfl_reduction_factor;
1198 recovery_streak = 0;
1199 }
1200 /* Relax the cap slowly on healthy trials: a single rejection caused by a transient
1201 * must not hold the step down for the rest of the run, but recovery has to be slow
1202 * enough that the controller does not simply walk back into the wall. */
1203 cfl_cap = PetscMin(simCtx->max_pseudo_cfl, cfl_cap * MOM_CFL_CAP_RELAX);
1204 next_dtau = PetscMin(next_dtau, dtau_max);
1205 next_dtau = PetscMin(next_dtau, cfl_cap / lambda_max);
1206 next_dtau = PetscMax(next_dtau, dtau_min);
1207 for (PetscInt bi = 0; bi < block_number; bi++) pseudo_dtau[bi] = next_dtau;
1208
1210 " Trial %d ACCEPTED (raw_ratio=%.4e, smoothed=%.4e); |dU|=%.6e | "
1211 "dtau %.4e -> %.4e (cfl_eff %.4f -> %.4f)\n",
1212 pseudo_iter, global_trial_ratio, trial_smoothed, global_norm_delta,
1213 old_dtau, next_dtau, old_dtau * lambda_max, next_dtau * lambda_max);
1214
1215 /* --- Post-decision file logging (one row per accepted trial) --- */
1216 if (!rank) {
1217 for (PetscInt bi = 0; bi < block_number; bi++) {
1218 FILE *f;
1219 char filen[PETSC_MAX_PATH_LEN + 128];
1220 ierr = PetscSNPrintf(filen, sizeof(filen),
1221 "%s/Momentum_Solver_DualTime_Picard_Jameson_RK_History_Block_%1d.log",
1222 simCtx->log_dir, bi); CHKERRQ(ierr);
1223 if (simCtx->step == simCtx->StartStep + 1 && pseudo_iter == 1 && !simCtx->continueMode)
1224 f = fopen(filen, "w");
1225 else
1226 f = fopen(filen, "a");
1227 if (simCtx->continueMode && simCtx->step == simCtx->StartStep + 1 && pseudo_iter == 1)
1228 PetscFPrintf(PETSC_COMM_WORLD, f, "# Continuation from step %" PetscInt_FMT "\n", simCtx->StartStep);
1229 /* Log format: dtau [physical time] + cfl_eff [dimensionless Courant number = dtau*lambda_max] */
1230 PetscFPrintf(PETSC_COMM_WORLD, f,
1231 "Step: %d | PseudoIter(k): %d | dtau: %.6e | cfl_eff: %.4f | |dUk|: %le | "
1232 "|dUk|/|dU0|: %le | |Rk|: %le | |Rk|/|R0|: %le | "
1233 "trial_ratio: %le | smoothed_ratio: %le | status: accepted | "
1234 "dtau_after: %.6e | cfl_eff_after: %.4f\n",
1235 (int)ti, (int)pseudo_iter, old_dtau, old_dtau * lambda_max,
1236 delta_sol_norm_curr[bi], delta_sol_rel_curr[bi],
1237 resid_norm_curr[bi], resid_rel_curr[bi],
1238 trial_ratio_log[bi], trial_smoothed,
1239 next_dtau, next_dtau * lambda_max);
1240 fclose(f);
1241 }
1242 }
1243
1244 /* --- Convergence decision ---
1245 *
1246 * The residual is what states that the momentum equations are satisfied at the
1247 * current state; the update norm measures progress, not correctness. The two
1248 * residual tests therefore carry different weight and are structured differently.
1249 *
1250 * converged = residual_abs_pass || (residual_rel_pass && update_pass)
1251 *
1252 * ABSOLUTE branch, sufficient on its own. |R| <= mom_resid_atol * resid_ref says
1253 * the equations are satisfied to the configured level outright, so it needs no
1254 * corroboration -- this is what an absolute tolerance means, and how PETSc's SNES
1255 * and KSP already behave. Requiring the update guard alongside it made the floor
1256 * unable to fire: measured on the laminar channel at step 793, |R| dropped below
1257 * the floor at pseudo-iteration 8 and the step still ran to 20, because
1258 * |dU|/|dU0| had not yet reached its own tolerance.
1259 *
1260 * RELATIVE branch, weaker evidence, paired with the update guard. A 1000x
1261 * reduction of |R0| says the iteration made progress from wherever it started,
1262 * not that the result is small; |R0| itself collapses as a run approaches steady
1263 * state. The guard stays mandatory here.
1264 *
1265 * The update norm is never sufficient by itself in either branch. |dU| ~ dtau*|R|,
1266 * so it goes small whenever dtau goes small -- observed as |dU| falling only ~10%
1267 * over 100 pseudo-iterations of a completely frozen iteration. An ABSOLUTE bound
1268 * on it (mom_atol) is the disguised, step-size-dependent residual bound
1269 * |R| <= mom_atol/dtau, which tightens as the controller succeeds in growing dtau;
1270 * it duplicates the residual test while pulling against it and takes no part here.
1271 *
1272 * With no residual criterion configured at all -- both residual tolerances set
1273 * non-positive, an explicit opt-out since the defaults now enable them -- the
1274 * update norms are the only information available and the legacy test is retained
1275 * unchanged.
1276 */
1277 if (residual_convergence_enabled) {
1278 const PetscBool residual_abs_pass = (PetscBool)(
1279 simCtx->mom_resid_atol > 0.0 && resid_ref > 0.0 &&
1280 global_norm_resid <= simCtx->mom_resid_atol * resid_ref);
1281 const PetscBool residual_rel_pass = (PetscBool)(
1282 simCtx->mom_resid_rtol > 0.0 && global_rel_resid <= simCtx->mom_resid_rtol);
1283 const PetscBool update_pass = (PetscBool)(
1284 tol_rtol_delta <= 0.0 || global_rel_delta <= tol_rtol_delta);
1285 converged = (PetscBool)(residual_abs_pass || (residual_rel_pass && update_pass));
1286 } else {
1287 converged = (PetscBool)(global_norm_delta <= tol_abs_delta &&
1288 global_rel_delta <= tol_rtol_delta);
1289 }
1290
1291 if (block_number > 1) {
1292 // ierr = Block_Interface_U(user); CHKERRQ(ierr);
1293 }
1294 } // End while loop
1295
1296 if (last_trial_nonfinite) {
1297 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_CONV_FAILED,
1298 "Momentum solver exhausted its attempt limit while recovering from a non-finite trial.");
1299 }
1300
1301 /* Convert the final dtau back to a CFL number for the warm-start of the next physical timestep.
1302 * simCtx->pseudo_cfl stores a dimensionless Courant number (dtau * lambda_max); the next
1303 * timestep's lambda_max may differ as the flow evolves, so storing the CFL number (not dtau)
1304 * ensures the warm-start adapts correctly to the new spectral radius. */
1305 PetscReal next_dtau_start = pseudo_dtau[0];
1306 PetscReal next_cfl_warmstart = next_dtau_start * lambda_max; /* CFL = dtau * lambda_max */
1307
1308 if (!rank) {
1310 " Step %d finished: dtau=%.4e cfl_eff=%.4f lambda_max=%.4e [1/s]. "
1311 "Next step warm-starts at cfl=%.4f.\n",
1312 simCtx->step, next_dtau_start, next_cfl_warmstart, lambda_max, next_cfl_warmstart);
1313 }
1314
1315 /* Store the CFL number (not dtau) so it remains meaningful across physical timesteps. */
1316 simCtx->pseudo_cfl = next_cfl_warmstart;
1317 simCtx->mom_last_lambda_max = lambda_max;
1318
1319 /* Expose the last accepted finite state. On converged or floor-break exits pUcont==Ucont
1320 already (VecCopy would be an identity), but ghosts may have gone stale; always re-sync
1321 and re-apply BCs. Only restore the solution vector itself when nothing was ever accepted. */
1322 if (accepted_iter == 0) {
1323 for (PetscInt bi = 0; bi < block_number; bi++) {
1324 ierr = VecCopy(user[bi].pUcont, user[bi].Ucont); CHKERRQ(ierr);
1325 ierr = VecCopy(pRhs[bi], user[bi].Rhs); CHKERRQ(ierr);
1326 }
1327 }
1328 for (PetscInt bi = 0; bi < block_number; bi++) {
1329 const FieldId staggered_fields[] = {FIELD_ID_UCONT};
1330 ierr = SynchronizePeriodicStaggeredFields(&user[bi], 1, staggered_fields); CHKERRQ(ierr);
1331 ierr = ApplyBoundaryConditions(&user[bi]); CHKERRQ(ierr);
1332 }
1333
1334 simCtx->mom_last_converged = converged;
1335 if (!converged) {
1336 PetscPrintf(PETSC_COMM_WORLD,
1337 "[WARNING] Momentum solver step %d: reached %d total attempts (%d accepted, %d rejected) "
1338 "without convergence; continuing from last accepted finite state.\n",
1339 (int)ti, pseudo_iter, accepted_iter, rejected_iter);
1340 }
1341 if (accepted_iter == 0) {
1342 PetscPrintf(PETSC_COMM_WORLD,
1343 "[WARNING] Momentum solver step %d: no pseudo-time trials were accepted; "
1344 "retaining physical-step entry state.\n", (int)ti);
1345 }
1347 "Momentum solver finished: %d attempts (%d accepted, %d rejected) of %d max accepted / %d hard cap, "
1348 "converged=%s, last accepted |R|=%.6e, last accepted |dU|=%.6e, "
1349 "next_dtau=%.4e (cfl=%.4f).\n",
1350 pseudo_iter, accepted_iter, rejected_iter, max_pseudo_steps, max_total_attempts,
1351 converged ? "yes" : "no",
1352 (accepted_iter > 0) ? last_accepted_resid : resid_norm_init[0],
1353 (accepted_iter > 0) ? delta_sol_norm_prev[0] : 0.0,
1354 next_dtau_start, next_cfl_warmstart);
1355
1356 // --- Final Cleanup ---
1357 for (PetscInt bi = 0; bi < block_number; bi++) {
1358 ierr = VecDestroy(&user[bi].Rhs); CHKERRQ(ierr);
1359 ierr = VecDestroy(&user[bi].dUcont); CHKERRQ(ierr);
1360 ierr = VecDestroy(&user[bi].pUcont); CHKERRQ(ierr);
1361 ierr = VecDestroy(&pRhs[bi]); CHKERRQ(ierr);
1362 }
1363 ierr = PetscFree(pRhs); CHKERRQ(ierr);
1364
1365 ierr = PetscFree2(delta_sol_norm_init, delta_sol_norm_prev);CHKERRQ(ierr);
1366 ierr = PetscFree2(delta_sol_norm_curr, delta_sol_rel_curr);CHKERRQ(ierr);
1367 ierr = PetscFree2(resid_norm_init, resid_norm_prev);CHKERRQ(ierr);
1368 ierr = PetscFree2(resid_norm_curr, pseudo_dtau); CHKERRQ(ierr);
1369 ierr = PetscFree(resid_rel_curr); CHKERRQ(ierr);
1370 ierr = PetscFree(trial_ratio_log); CHKERRQ(ierr);
1371
1373 PetscFunctionReturn(0);
1374}
@ LOG_TRACE
Very fine-grained tracing information for in-depth debugging.
Definition logging.h:33
@ LOG_WARNING
Non-critical issues that warrant attention.
Definition logging.h:30
#define MOM_CFL_CAP_SAFETY
static PetscErrorCode ComputeGlobalSpectralRadiusEstimate(UserCtx *user, PetscInt block_number, PetscReal dt, PetscReal *lambda_max_out)
Compute a conservative global pseudo-time spectral radius estimate.
PetscErrorCode ComputeTotalResidual(UserCtx *user)
Shared implementation of ComputeTotalResidual().
PetscErrorCode ComputeMomentumStabilityEstimate(UserCtx *user, PetscInt block_number, PetscReal dt, MomStabCandidate candidate, MomStabilityReport *rep)
Practical conservative momentum pseudo-time stability estimate (shadow).
#define MOM_CFL_CAP_RELAX
Diagnostic report produced by ComputeMomentumStabilityEstimate().
PetscBool continueMode
Definition variables.h:712
PetscReal mom_rtol
Definition variables.h:738
PetscReal mom_last_lambda_max
Definition variables.h:751
PetscReal pseudo_cfl_reduction_factor
Definition variables.h:745
PetscReal min_pseudo_cfl
Definition variables.h:746
PetscBool mom_last_converged
Definition variables.h:750
PetscReal mom_atol
Definition variables.h:738
PetscBool no_pseudo_cfl_backtrack
Definition variables.h:748
PetscReal max_pseudo_cfl
Definition variables.h:746
char log_dir[PETSC_MAX_PATH_LEN]
Definition variables.h:718
PetscReal mom_dt_jameson_residual_norm_noise_allowance_factor
Definition variables.h:747
PetscReal pseudo_cfl_growth_factor
Definition variables.h:745
PetscReal mom_resid_rtol
Definition variables.h:738
PetscInt mom_max_pseudo_steps
Definition variables.h:737
PetscReal mom_ratio_ema_alpha
Definition variables.h:749
PetscReal mom_resid_atol
Definition variables.h:738
PetscReal pseudo_cfl
Definition variables.h:744
Here is the call graph for this function:
Here is the caller graph for this function: