PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
Loading...
Searching...
No Matches
Functions
BodyForces.h File Reference

Momentum source terms added to the contravariant RHS. More...

#include "variables.h"
#include "logging.h"
#include "Metric.h"
Include dependency graph for BodyForces.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

PetscErrorCode ComputeDrivenChannelFlowSource (UserCtx *user, Vec Rct)
 Applies a momentum source term to drive flow in a periodic channel or pipe.
 

Detailed Description

Momentum source terms added to the contravariant RHS.

Contract for a body force

Every body force here is invoked from ComputeBodyForces() (src/rhs.c), which is the single extension point. A new force should:

  1. take (UserCtx *user, Vec Rct),
  2. detect for itself whether it is active and return early if not,
  3. accumulate into Rct with +=, never assign.

RULE: DO NOT ADVANCE PER-TIMESTEP STATE HERE

ComputeBodyForces() is called from ComputeRHS(), which runs once per residual evaluation - that is, once per Jameson RK stage under the Picard solver and once per Newton residual evaluation (including every finite difference probe) under Newton-Krylov. It is emphatically NOT called once per physical timestep.

Any force that carries state across calls - a filter, a ramp, a moving average, an integral controller term - must therefore gate its update on simCtx->step and reuse the resolved value for the rest of that step:

if (simCtx->myForceStep != simCtx->step) {
... advance the state ...
simCtx->myForceStep = simCtx->step;
}

Advancing it unconditionally makes the applied force depend on how many residual evaluations preceded it. That is history dependence, and it breaks two things at once: MomentumNewtonKrylov_FormResidual() requires F(X) to be a deterministic function of the trial vector alone, and the Picard shadow-Jacobian estimate assumes body forces are a constant forcing with zero velocity Jacobian.

This is not hypothetical. The driven-flow smoothing EMA in ComputeDrivenChannelFlowSource() had exactly this defect: the applied force walked 0.5, 0.75, 0.875 ... of the way toward its target across evaluations within a single timestep. tests/smoke/run_driven_periodic_regression.sh asserts the force is piecewise constant per step; extend it when adding a stateful force.

Definition in file BodyForces.h.

Function Documentation

◆ ComputeDrivenChannelFlowSource()

PetscErrorCode ComputeDrivenChannelFlowSource ( UserCtx user,
Vec  Rct 
)

Applies a momentum source term to drive flow in a periodic channel or pipe.

This function is the "engine" of the driven flow control system. It operates by:

  1. Introspecting the boundary condition handlers to see if a DRIVEN_ flow handler is active on any face. This determines if a driven flow is enabled and in which direction ('X', 'Y', or 'Z').
  2. If a driven flow is active, it reads the bulkVelocityCorrection value that was computed by the handler's PreStep method and stored in the SimCtx.
  3. It translates this velocity correction into a momentum source term.
  4. It adds this source term to the appropriate component of the contravariant RHS vector (Rct) for all fluid cells in the domain.

If no driven flow handler is found, this function does nothing.

Parameters
userThe UserCtx containing the simulation state for a single block.
RctThe PETSc Vec for the contravariant RHS, which will be modified in-place.
Returns
PetscErrorCode 0 on success.

Applies a momentum source term to drive flow in a periodic channel or pipe.

Local to this translation unit.

Definition at line 14 of file BodyForces.c.

15{
16 PetscErrorCode ierr;
17 SimCtx *simCtx = user->simCtx;
18 PetscFunctionBeginUser;
19
20 // --- Step 1: Discover if and where a driven flow is active ---
21 char drivenDirection = ' '; // Use space as a null/not-found indicator
22 for (int i = 0; i < 6; i++) {
23 BCHandlerType handler_type = user->boundary_faces[i].handler_type;
24 if (handler_type == BC_HANDLER_PERIODIC_DRIVEN_CONSTANT_FLUX ||
26 {
27 switch (user->boundary_faces[i].face_id) {
28 case BC_FACE_NEG_X: case BC_FACE_POS_X: drivenDirection = 'X'; break;
29 case BC_FACE_NEG_Y: case BC_FACE_POS_Y: drivenDirection = 'Y'; break;
30 case BC_FACE_NEG_Z: case BC_FACE_POS_Z: drivenDirection = 'Z'; break;
31 }
32 break; // Found it, no need to check other faces
33 }
34 }
35
36 // --- Step 2: Early exit if no driven flow is configured ---
37 if (drivenDirection == ' ') {
38 PetscFunctionReturn(0);
39 }
40
41 // --- Step 3: Get the control signal and exit if no correction is needed ---
42 PetscReal bulkVelocityCorrection = simCtx->bulkVelocityCorrection;
43 if (PetscAbsReal(bulkVelocityCorrection) < 1.0e-12) {
44 PetscFunctionReturn(0);
45 }
46
47 LOG_ALLOW(LOCAL, LOG_DEBUG, "Rank %d, Block %d: Applying driven flow momentum source in '%c' direction.\n",
48 simCtx->rank, user->_this, drivenDirection);
49 LOG_ALLOW(LOCAL, LOG_DEBUG, " - Received Bulk Velocity Correction: %le\n", bulkVelocityCorrection);
50
51 // --- Step 4: Setup for calculation ---
52 DMDALocalInfo info = user->info;
53 PetscInt i, j, k;
54 PetscInt lxs = (info.xs == 0) ? 1 : info.xs;
55 PetscInt lys = (info.ys == 0) ? 1 : info.ys;
56 PetscInt lzs = (info.zs == 0) ? 1 : info.zs;
57 PetscInt lxe = (info.xs + info.xm == info.mx) ? info.mx - 1 : info.xs + info.xm;
58 PetscInt lye = (info.ys + info.ym == info.my) ? info.my - 1 : info.ys + info.ym;
59 PetscInt lze = (info.zs + info.zm == info.mz) ? info.mz - 1 : info.zs + info.zm;
60
61 Cmpnts ***rct, ***csi, ***eta, ***zet;
62 PetscReal ***nvert;
63 ierr = DMDAVecGetArray(user->fda, Rct, &rct); CHKERRQ(ierr);
64 ierr = DMDAVecGetArrayRead(user->fda, user->lCsi, (const Cmpnts***)&csi); CHKERRQ(ierr);
65 ierr = DMDAVecGetArrayRead(user->fda, user->lEta, (const Cmpnts***)&eta); CHKERRQ(ierr);
66 ierr = DMDAVecGetArrayRead(user->fda, user->lZet, (const Cmpnts***)&zet); CHKERRQ(ierr);
67 ierr = DMDAVecGetArrayRead(user->da, user->lNvert, (const PetscReal***)&nvert); CHKERRQ(ierr);
68
69 // Calculate the driving force magnitude for the current timestep, smoothed
70 // with the value from the previous step for stability.
71 //
72 // ONCE PER PHYSICAL STEP, NOT ONCE PER CALL. This function runs from
73 // ComputeRHS, which executes once per Jameson RK stage under the Picard
74 // solver and once per residual evaluation under Newton-Krylov. The smoothing
75 // below carries state in simCtx across calls, so advancing it every call
76 // would walk the applied force toward its target within a single timestep
77 // (0.5, then 0.75, then 0.875 ... of the way there). The force would then
78 // depend on how many residual evaluations preceded it - history dependence
79 // that MomentumNewtonKrylov_FormResidual() explicitly forbids, and that
80 // breaks the constant-forcing assumption behind the Picard shadow-Jacobian
81 // estimate. Resolve it once for the step and reuse it thereafter.
82 const PetscReal forceScalingFactor = simCtx->forceScalingFactor;
83 PetscReal drivingForceMagnitude;
84
85 if (simCtx->drivingForceStep != simCtx->step) {
86 const PetscReal targetForce = (bulkVelocityCorrection / simCtx->dt / 1.0 * COEF_TIME_ACCURACY); // replaced simCtx->st with 1.0.
87 drivingForceMagnitude = (simCtx->drivingForceMagnitude * 0.5) + (targetForce * 0.5);
88 simCtx->drivingForceMagnitude = drivingForceMagnitude;
89 simCtx->drivingForceStep = simCtx->step;
90 } else {
91 drivingForceMagnitude = simCtx->drivingForceMagnitude;
92 }
93
94 LOG_ALLOW(GLOBAL, LOG_DEBUG, " - Previous driving force: %le\n", simCtx->drivingForceMagnitude);
95 LOG_ALLOW(GLOBAL, LOG_DEBUG, " - New smoothed driving force: %le\n", drivingForceMagnitude);
96 LOG_ALLOW(GLOBAL, LOG_DEBUG, " - Force scaling factor: %f\n", simCtx->forceScalingFactor);
97
98 PetscBool hasLoggedApplication = PETSC_FALSE; // Flag to log details only once per rank.
99 // --- Step 5: Apply the momentum source to the correct RHS component ---
100 for (k = lzs; k < lze; k++) {
101 for (j = lys; j < lye; j++) {
102 for (i = lxs; i < lxe; i++) {
103 if (nvert[k][j][i] < 0.1) { // Apply only to fluid cells
104 PetscReal faceArea = 0.0;
105 PetscReal momentumSource = 0.0;
106
107 switch (drivenDirection) {
108 case 'X':
109 faceArea = sqrt(csi[k][j][i].x * csi[k][j][i].x + csi[k][j][i].y * csi[k][j][i].y + csi[k][j][i].z * csi[k][j][i].z);
110 momentumSource = drivingForceMagnitude * forceScalingFactor * faceArea;
111 rct[k][j][i].x += momentumSource;
112
113 // Log details for the very first point where force is applied on this rank.
114 if (!hasLoggedApplication) {
115 LOG_ALLOW(LOCAL, LOG_DEBUG,"Body Force %le added at (%d,%d,%d)\n",momentumSource, k, j, i);
116 hasLoggedApplication = PETSC_TRUE;
117 }
118 break;
119 case 'Y':
120 faceArea = sqrt(eta[k][j][i].x * eta[k][j][i].x + eta[k][j][i].y * eta[k][j][i].y + eta[k][j][i].z * eta[k][j][i].z);
121 momentumSource = drivingForceMagnitude * forceScalingFactor * faceArea;
122 rct[k][j][i].y += momentumSource;
123
124 // Log details for the very first point where force is applied on this rank.
125 if (!hasLoggedApplication) {
126 LOG_ALLOW(LOCAL, LOG_DEBUG,"Body Force %le added at (%d,%d,%d)\n",momentumSource, k, j, i);
127 hasLoggedApplication = PETSC_TRUE;
128 }
129 break;
130 case 'Z':
131 faceArea = sqrt(zet[k][j][i].x * zet[k][j][i].x + zet[k][j][i].y * zet[k][j][i].y + zet[k][j][i].z * zet[k][j][i].z);
132 momentumSource = drivingForceMagnitude * forceScalingFactor * faceArea;
133 rct[k][j][i].z += momentumSource;
134
135 // Log details for the very first point where force is applied on this rank.
136 if (!hasLoggedApplication) {
137 LOG_ALLOW(LOCAL, LOG_DEBUG,"Body Force %le added at (%d,%d,%d)\n",momentumSource, k, j, i);
138 hasLoggedApplication = PETSC_TRUE;
139 }
140 break;
141 }
142 }
143 }
144 }
145 }
146
147 // --- Step 6: Restore arrays ---
148 ierr = DMDAVecRestoreArray(user->fda, Rct, &rct); CHKERRQ(ierr);
149 ierr = DMDAVecRestoreArrayRead(user->fda, user->lCsi, (const Cmpnts***)&csi); CHKERRQ(ierr);
150 ierr = DMDAVecRestoreArrayRead(user->fda, user->lEta, (const Cmpnts***)&eta); CHKERRQ(ierr);
151 ierr = DMDAVecRestoreArrayRead(user->fda, user->lZet, (const Cmpnts***)&zet); CHKERRQ(ierr);
152 ierr = DMDAVecRestoreArrayRead(user->da, user->lNvert, (const PetscReal***)&nvert); CHKERRQ(ierr);
153
154 PetscFunctionReturn(0);
155}
#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_DEBUG
Detailed debugging information.
Definition logging.h:32
PetscMPIInt rank
Definition variables.h:698
BoundaryFaceConfig boundary_faces[6]
Definition variables.h:931
Vec lNvert
Definition variables.h:939
PetscReal forceScalingFactor
Definition variables.h:801
SimCtx * simCtx
Back-pointer to the master simulation context.
Definition variables.h:909
Vec lZet
Definition variables.h:974
BCHandlerType
Defines the specific computational "strategy" for a boundary handler.
Definition variables.h:303
@ BC_HANDLER_PERIODIC_DRIVEN_INITIAL_FLUX
Definition variables.h:319
@ BC_HANDLER_PERIODIC_DRIVEN_CONSTANT_FLUX
Definition variables.h:318
BCHandlerType handler_type
Definition variables.h:369
PetscInt _this
Definition variables.h:924
PetscReal dt
Definition variables.h:710
PetscReal bulkVelocityCorrection
Definition variables.h:813
PetscScalar x
Definition variables.h:103
Vec lCsi
Definition variables.h:974
PetscScalar z
Definition variables.h:103
PetscInt drivingForceStep
Definition variables.h:806
PetscInt step
Definition variables.h:703
DMDALocalInfo info
Definition variables.h:918
PetscScalar y
Definition variables.h:103
Vec lEta
Definition variables.h:974
#define COEF_TIME_ACCURACY
Coefficient controlling the temporal accuracy scheme (e.g., 1.5 for 2nd Order Backward Difference).
Definition variables.h:57
PetscReal drivingForceMagnitude
Definition variables.h:801
@ 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
A 3D point or vector with PetscScalar components.
Definition variables.h:102
The master context for the entire simulation.
Definition variables.h:695
Here is the caller graph for this function: