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

Go to the source code of this file.

Data Structures

struct  InletConstantData
 Private data structure for the Constant Velocity Inlet handler. More...
 
struct  InletParabolicData
 Private data structure for the Parabolic Velocity Inlet handler. More...
 
struct  InletProfileFileData
 
struct  DrivenFluxData
 Private data structure shared by both periodic driven-flux handlers. More...
 

Macros

#define __FUNCT__   "Validate_DrivenFlowConfiguration"
 
#define __FUNCT__   "Create_WallNoSlip"
 
#define __FUNCT__   "Apply_WallNoSlip"
 
#define __FUNCT__   "Create_InletConstantVelocity"
 
#define __FUNCT__   "Initialize_InletConstantVelocity"
 
#define __FUNCT__   "PreStep_InletConstantVelocity"
 
#define __FUNCT__   "Apply_InletConstantVelocity"
 
#define __FUNCT__   "PostStep_InletConstantVelocity"
 
#define __FUNCT__   "Destroy_InletConstantVelocity"
 
#define __FUNCT__   "Create_InletParabolicProfile"
 
#define __FUNCT__   "Initialize_InletParabolicProfile"
 
#define __FUNCT__   "PreStep_InletParabolicProfile"
 
#define __FUNCT__   "Apply_InletParabolicProfile"
 
#define __FUNCT__   "PostStep_InletParabolicProfile"
 
#define __FUNCT__   "Destroy_InletParabolicProfile"
 
#define __FUNCT__   "Create_InletProfileFromFile"
 
#define __FUNCT__   "Initialize_InletProfileFromFile"
 
#define __FUNCT__   "PreStep_InletProfileFromFile"
 
#define __FUNCT__   "Apply_InletProfileFromFile"
 
#define __FUNCT__   "PostStep_InletProfileFromFile"
 
#define __FUNCT__   "Destroy_InletProfileFromFile"
 
#define __FUNCT__   "Create_OutletConservation"
 
#define __FUNCT__   "PreStep_OutletConservation"
 
#define __FUNCT__   "Apply_OutletConservation"
 
#define __FUNCT__   "PostStep_OutletConservation"
 
#define __FUNCT__   "MeasureDrivenFluxes"
 
#define __FUNCT__   "Create_PeriodicDrivenConstant"
 
#define __FUNCT__   "Initialize_PeriodicDrivenConstant"
 
#define __FUNCT__   "PreStep_PeriodicDrivenConstant"
 
#define __FUNCT__   "Apply_PeriodicDrivenConstant"
 
#define __FUNCT__   "Destroy_PeriodicDrivenConstant"
 
#define __FUNCT__   "Create_PeriodicDrivenInitial"
 
#define __FUNCT__   "Initialize_PeriodicDrivenInitial"
 
#define __FUNCT__   "PreStep_PeriodicDrivenInitial"
 

Functions

PetscErrorCode Validate_DrivenFlowConfiguration (UserCtx *user)
 Internal helper implementation: Validate_DrivenFlowConfiguration().
 
static PetscErrorCode Apply_WallNoSlip (BoundaryCondition *self, BCContext *ctx)
 Apply no-slip velocity values to wall-adjacent cells for this boundary condition.
 
PetscErrorCode Create_WallNoSlip (BoundaryCondition *bc)
 Implementation of Create_WallNoSlip().
 
static PetscErrorCode Initialize_InletConstantVelocity (BoundaryCondition *self, BCContext *ctx)
 Initialize persistent state for a constant-velocity inlet boundary.
 
static PetscErrorCode PreStep_InletConstantVelocity (BoundaryCondition *self, BCContext *ctx, PetscReal *local_inflow_contribution, PetscReal *local_outflow_contribution)
 Update constant-inlet data required before the next solver step.
 
static PetscErrorCode Apply_InletConstantVelocity (BoundaryCondition *self, BCContext *ctx)
 Impose the configured constant velocity on inlet boundary cells.
 
static PetscErrorCode PostStep_InletConstantVelocity (BoundaryCondition *self, BCContext *ctx, PetscReal *local_inflow_contribution, PetscReal *local_outflow_contribution)
 Perform post-step bookkeeping for a constant-velocity inlet boundary.
 
static PetscErrorCode Destroy_InletConstantVelocity (BoundaryCondition *self)
 Release resources owned by a constant-velocity inlet boundary.
 
PetscErrorCode Create_InletConstantVelocity (BoundaryCondition *bc)
 Implementation of Create_InletConstantVelocity().
 
static PetscErrorCode Initialize_InletParabolicProfile (BoundaryCondition *self, BCContext *ctx)
 Initialize the geometric data used to evaluate a parabolic inlet profile.
 
static PetscErrorCode PreStep_InletParabolicProfile (BoundaryCondition *self, BCContext *ctx, PetscReal *local_inflow_contribution, PetscReal *local_outflow_contribution)
 Refresh parabolic-inlet values required before the solver step.
 
static PetscErrorCode Apply_InletParabolicProfile (BoundaryCondition *self, BCContext *ctx)
 Impose the evaluated parabolic velocity profile on inlet cells.
 
static PetscErrorCode PostStep_InletParabolicProfile (BoundaryCondition *self, BCContext *ctx, PetscReal *local_inflow_contribution, PetscReal *local_outflow_contribution)
 Perform post-step bookkeeping for a parabolic inlet boundary.
 
static PetscErrorCode Destroy_InletParabolicProfile (BoundaryCondition *self)
 Release resources owned by a parabolic inlet boundary.
 
PetscErrorCode Create_InletParabolicProfile (BoundaryCondition *bc)
 Implementation of Create_InletParabolicProfile().
 
static PetscErrorCode Initialize_InletProfileFromFile (BoundaryCondition *self, BCContext *ctx)
 Initializes a file-prescribed inlet profile handler for one boundary face.
 
static PetscErrorCode PreStep_InletProfileFromFile (BoundaryCondition *self, BCContext *ctx, PetscReal *local_inflow_contribution, PetscReal *local_outflow_contribution)
 Pre-step hook for the static file-prescribed inlet profile handler.
 
static PetscErrorCode Apply_InletProfileFromFile (BoundaryCondition *self, BCContext *ctx)
 Applies the loaded PICSLICE scalar profile to Ucont and Ubcs on an inlet face.
 
static PetscErrorCode PostStep_InletProfileFromFile (BoundaryCondition *self, BCContext *ctx, PetscReal *local_inflow_contribution, PetscReal *local_outflow_contribution)
 Accumulates the applied inlet flux for a file-prescribed profile.
 
static PetscErrorCode Destroy_InletProfileFromFile (BoundaryCondition *self)
 Releases private storage owned by a file-prescribed inlet profile handler.
 
static PetscErrorCode GetBCParamStringLocal (BC_Param *params, const char *key, const char **value_out, PetscBool *found)
 Looks up a string-valued boundary-condition parameter in a BC_Param list.
 
static PetscErrorCode GetProfileFileExpectedDims (UserCtx *user, BCFace face_id, PetscInt *n1, PetscInt *n2)
 Computes the expected PICSLICE dimensions for an inlet face.
 
static PetscErrorCode ReadPicSliceProfile (const char *source_file, PetscInt expected_n1, PetscInt expected_n2, InletProfileFileData *data)
 Reads and validates a static scalar inlet profile from a canonical PICSLICE file.
 
static PetscReal ProfileSpeedAt (const InletProfileFileData *data, PetscInt a, PetscInt b)
 Returns one scalar speed from the flattened PICSLICE profile.
 
PetscErrorCode Create_InletProfileFromFile (BoundaryCondition *bc)
 Implementation of Create_InletProfileFromFile().
 
static PetscErrorCode PreStep_OutletConservation (BoundaryCondition *self, BCContext *ctx, PetscReal *local_inflow_contribution, PetscReal *local_outflow_contribution)
 Prepare the outlet-conservation correction before advancing the solver.
 
static PetscErrorCode Apply_OutletConservation (BoundaryCondition *self, BCContext *ctx)
 (Handler Action) Applies mass conservation correction to the outlet face.
 
static PetscErrorCode PostStep_OutletConservation (BoundaryCondition *self, BCContext *ctx, PetscReal *local_inflow_contribution, PetscReal *local_outflow_contribution)
 Update outlet-conservation state after a completed solver step.
 
PetscErrorCode Create_OutletConservation (BoundaryCondition *bc)
 Implementation of Create_OutletConservation().
 
PetscErrorCode Create_PeriodicGeometric (BoundaryCondition *bc)
 Implementation of Create_PeriodicGeometric().
 
static PetscErrorCode MeasureDrivenFluxes (UserCtx *user, char direction, PetscReal *boundaryFlux, PetscReal *planarAverageFlux)
 Measure the two volumetric fluxes the driven-flow controller senses.
 
static PetscErrorCode Initialize_PeriodicDrivenConstant (BoundaryCondition *self, BCContext *ctx)
 Initialize constant forcing data for a periodically driven boundary.
 
static PetscErrorCode PreStep_PeriodicDrivenConstant (BoundaryCondition *self, BCContext *ctx, PetscReal *local_inflow_contribution, PetscReal *local_outflow_contribution)
 Prepare constant periodic-driving data before the solver step.
 
static PetscErrorCode Apply_PeriodicDrivenConstant (BoundaryCondition *self, BCContext *ctx)
 Apply the configured constant driving term to the periodic boundary.
 
static PetscErrorCode Destroy_PeriodicDrivenConstant (BoundaryCondition *self)
 Release resources owned by the constant periodic-driving boundary.
 
PetscErrorCode Create_PeriodicDrivenConstant (BoundaryCondition *bc)
 Internal helper implementation: Create_PeriodicDrivenConstant().
 
static PetscErrorCode Initialize_PeriodicDrivenInitial (BoundaryCondition *self, BCContext *ctx)
 Initialize forcing data for a periodic boundary driven to its initial flux.
 
static PetscErrorCode PreStep_PeriodicDrivenInitial (BoundaryCondition *self, BCContext *ctx, PetscReal *local_inflow_contribution, PetscReal *local_outflow_contribution)
 Latch the initial-state flux once, then drive to it like a constant target.
 
PetscErrorCode Create_PeriodicDrivenInitial (BoundaryCondition *bc)
 Internal helper implementation: Create_PeriodicDrivenInitial().
 

Data Structure Documentation

◆ InletConstantData

struct InletConstantData

Private data structure for the Constant Velocity Inlet handler.

Definition at line 310 of file BC_Handlers.c.

Data Fields
PetscReal normal_velocity

◆ InletParabolicData

struct InletParabolicData

Private data structure for the Parabolic Velocity Inlet handler.

Stores the peak velocity and pre-computed cross-stream geometry needed to evaluate the parabolic profile at each boundary node.

Definition at line 699 of file BC_Handlers.c.

Data Fields
PetscReal v_max Peak centerline velocity (from user params).
PetscReal cs1_center Center index in cross-stream direction 1.
PetscReal cs2_center Center index in cross-stream direction 2.
PetscReal cs1_half Half-width (in index space) in cross-stream direction 1.
PetscReal cs2_half Half-width (in index space) in cross-stream direction 2.

◆ InletProfileFileData

struct InletProfileFileData

Definition at line 1106 of file BC_Handlers.c.

Data Fields
PetscInt n1
PetscInt n2
PetscReal * profile
PetscReal min_speed
PetscReal max_speed
char * source_file

◆ DrivenFluxData

struct DrivenFluxData

Private data structure shared by both periodic driven-flux handlers.

constant_flux fills targetVolumetricFlux from the bcs file at initialization; initial_flux latches it from the starting field at the first PreStep. Everything downstream of the target is identical, so both handlers reuse this struct and the PreStep/Apply/Destroy implementations below.

Definition at line 2308 of file BC_Handlers.c.

Data Fields
char direction
PetscReal targetVolumetricFlux
PetscBool isMasterController
PetscBool enforceSeamFlux
PetscInt lastBulkCorrectionStep

Macro Definition Documentation

◆ __FUNCT__ [1/34]

#define __FUNCT__   "Validate_DrivenFlowConfiguration"

Definition at line 10 of file BC_Handlers.c.

◆ __FUNCT__ [2/34]

#define __FUNCT__   "Create_WallNoSlip"

Definition at line 10 of file BC_Handlers.c.

◆ __FUNCT__ [3/34]

#define __FUNCT__   "Apply_WallNoSlip"

Definition at line 10 of file BC_Handlers.c.

◆ __FUNCT__ [4/34]

#define __FUNCT__   "Create_InletConstantVelocity"

Definition at line 10 of file BC_Handlers.c.

◆ __FUNCT__ [5/34]

#define __FUNCT__   "Initialize_InletConstantVelocity"

Definition at line 10 of file BC_Handlers.c.

◆ __FUNCT__ [6/34]

#define __FUNCT__   "PreStep_InletConstantVelocity"

Definition at line 10 of file BC_Handlers.c.

◆ __FUNCT__ [7/34]

#define __FUNCT__   "Apply_InletConstantVelocity"

Definition at line 10 of file BC_Handlers.c.

◆ __FUNCT__ [8/34]

#define __FUNCT__   "PostStep_InletConstantVelocity"

Definition at line 10 of file BC_Handlers.c.

◆ __FUNCT__ [9/34]

#define __FUNCT__   "Destroy_InletConstantVelocity"

Definition at line 10 of file BC_Handlers.c.

◆ __FUNCT__ [10/34]

#define __FUNCT__   "Create_InletParabolicProfile"

Definition at line 10 of file BC_Handlers.c.

◆ __FUNCT__ [11/34]

#define __FUNCT__   "Initialize_InletParabolicProfile"

Definition at line 10 of file BC_Handlers.c.

◆ __FUNCT__ [12/34]

#define __FUNCT__   "PreStep_InletParabolicProfile"

Definition at line 10 of file BC_Handlers.c.

◆ __FUNCT__ [13/34]

#define __FUNCT__   "Apply_InletParabolicProfile"

Definition at line 10 of file BC_Handlers.c.

◆ __FUNCT__ [14/34]

#define __FUNCT__   "PostStep_InletParabolicProfile"

Definition at line 10 of file BC_Handlers.c.

◆ __FUNCT__ [15/34]

#define __FUNCT__   "Destroy_InletParabolicProfile"

Definition at line 10 of file BC_Handlers.c.

◆ __FUNCT__ [16/34]

#define __FUNCT__   "Create_InletProfileFromFile"

Definition at line 10 of file BC_Handlers.c.

◆ __FUNCT__ [17/34]

#define __FUNCT__   "Initialize_InletProfileFromFile"

Definition at line 10 of file BC_Handlers.c.

◆ __FUNCT__ [18/34]

#define __FUNCT__   "PreStep_InletProfileFromFile"

Definition at line 10 of file BC_Handlers.c.

◆ __FUNCT__ [19/34]

#define __FUNCT__   "Apply_InletProfileFromFile"

Definition at line 10 of file BC_Handlers.c.

◆ __FUNCT__ [20/34]

#define __FUNCT__   "PostStep_InletProfileFromFile"

Definition at line 10 of file BC_Handlers.c.

◆ __FUNCT__ [21/34]

#define __FUNCT__   "Destroy_InletProfileFromFile"

Definition at line 10 of file BC_Handlers.c.

◆ __FUNCT__ [22/34]

#define __FUNCT__   "Create_OutletConservation"

Definition at line 10 of file BC_Handlers.c.

◆ __FUNCT__ [23/34]

#define __FUNCT__   "PreStep_OutletConservation"

Definition at line 10 of file BC_Handlers.c.

◆ __FUNCT__ [24/34]

#define __FUNCT__   "Apply_OutletConservation"

Definition at line 10 of file BC_Handlers.c.

◆ __FUNCT__ [25/34]

#define __FUNCT__   "PostStep_OutletConservation"

Definition at line 10 of file BC_Handlers.c.

◆ __FUNCT__ [26/34]

#define __FUNCT__   "MeasureDrivenFluxes"

Definition at line 10 of file BC_Handlers.c.

◆ __FUNCT__ [27/34]

#define __FUNCT__   "Create_PeriodicDrivenConstant"

Definition at line 10 of file BC_Handlers.c.

◆ __FUNCT__ [28/34]

#define __FUNCT__   "Initialize_PeriodicDrivenConstant"

Definition at line 10 of file BC_Handlers.c.

◆ __FUNCT__ [29/34]

#define __FUNCT__   "PreStep_PeriodicDrivenConstant"

Definition at line 10 of file BC_Handlers.c.

◆ __FUNCT__ [30/34]

#define __FUNCT__   "Apply_PeriodicDrivenConstant"

Definition at line 10 of file BC_Handlers.c.

◆ __FUNCT__ [31/34]

#define __FUNCT__   "Destroy_PeriodicDrivenConstant"

Definition at line 10 of file BC_Handlers.c.

◆ __FUNCT__ [32/34]

#define __FUNCT__   "Create_PeriodicDrivenInitial"

Definition at line 10 of file BC_Handlers.c.

◆ __FUNCT__ [33/34]

#define __FUNCT__   "Initialize_PeriodicDrivenInitial"

Definition at line 10 of file BC_Handlers.c.

◆ __FUNCT__ [34/34]

#define __FUNCT__   "PreStep_PeriodicDrivenInitial"

Definition at line 10 of file BC_Handlers.c.

Function Documentation

◆ Validate_DrivenFlowConfiguration()

PetscErrorCode Validate_DrivenFlowConfiguration ( UserCtx user)

Internal helper implementation: Validate_DrivenFlowConfiguration().

(Private) Validates all consistency rules for a driven flow (channel/pipe) setup.

Local to this translation unit.

Definition at line 15 of file BC_Handlers.c.

16{
17 PetscFunctionBeginUser;
18
19 // --- CHECK 1: Detect if a driven flow is active. ---
20 PetscBool is_driven_flow_active = PETSC_FALSE;
21 char driven_direction = ' ';
22 const char* first_driven_face_name = "";
23
24 for (int i = 0; i < 6; i++) {
25 BCHandlerType handler_type = user->boundary_faces[i].handler_type;
26 if (handler_type == BC_HANDLER_PERIODIC_DRIVEN_CONSTANT_FLUX ||
28 {
29 is_driven_flow_active = PETSC_TRUE;
30 first_driven_face_name = BCFaceToString((BCFace)i);
31
32 if (i <= 1) driven_direction = 'X';
33 else if (i <= 3) driven_direction = 'Y';
34 else driven_direction = 'Z';
35
36 break; // Exit loop once we've confirmed it's active and found the direction.
37 }
38 }
39
40 // If no driven flow handler is found, validation for this rule set is complete.
41 if (!is_driven_flow_active) {
42 PetscFunctionReturn(0);
43 }
44
45 LOG_ALLOW(GLOBAL, LOG_DEBUG, " - Driven Flow Handler detected on face %s. Applying driven flow validation rules...\n", first_driven_face_name);
46
47 // --- CHECK 2: Ensure no conflicting BCs (Inlet/Outlet/Far-field) are present. ---
48 LOG_ALLOW(GLOBAL, LOG_DEBUG, " - Checking for incompatible Inlet/Outlet/Far-field BCs...\n");
49 for (int i = 0; i < 6; i++) {
50 BCType math_type = user->boundary_faces[i].mathematical_type;
51 if (math_type == INLET || math_type == OUTLET || math_type == FARFIELD) {
52 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_USER_INPUT,
53 "Configuration Error: A DRIVEN flow handler is active, which is incompatible with the %s boundary condition found on face %s.",
54 BCTypeToString(math_type), BCFaceToString((BCFace)i));
55 }
56 }
57 LOG_ALLOW(GLOBAL, LOG_DEBUG, " ... No conflicting BC types found. OK.\n");
58
59 // --- CHECK 3: Ensure both ends of the driven direction have identical, valid setups. ---
60 LOG_ALLOW(GLOBAL, LOG_DEBUG, " - Validating symmetry and mathematical types for the '%c' direction...\n", driven_direction);
61
62 PetscInt neg_face_idx = 0, pos_face_idx = 0;
63 if (driven_direction == 'X') {
64 neg_face_idx = BC_FACE_NEG_X; pos_face_idx = BC_FACE_POS_X;
65 } else if (driven_direction == 'Y') {
66 neg_face_idx = BC_FACE_NEG_Y; pos_face_idx = BC_FACE_POS_Y;
67 } else { // 'Z'
68 neg_face_idx = BC_FACE_NEG_Z; pos_face_idx = BC_FACE_POS_Z;
69 }
70
71 BoundaryFaceConfig *neg_face_cfg = &user->boundary_faces[neg_face_idx];
72 BoundaryFaceConfig *pos_face_cfg = &user->boundary_faces[pos_face_idx];
73
74 // Rule 3a: Both faces must be PERIODIC.
75 if (neg_face_cfg->mathematical_type != PERIODIC || pos_face_cfg->mathematical_type != PERIODIC) {
76 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_USER_INPUT,
77 "Configuration Error: For a driven flow in the '%c' direction, both the %s and %s faces must be of mathematical_type PERIODIC.",
78 driven_direction, BCFaceToString((BCFace)neg_face_idx), BCFaceToString((BCFace)pos_face_idx));
79 }
80
81 // Rule 3b: Both faces must use the exact same handler type.
82 if (neg_face_cfg->handler_type != pos_face_cfg->handler_type) {
83 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_USER_INPUT,
84 "Configuration Error: The DRIVEN handlers on the %s and %s faces of the '%c' direction do not match. Both must be the same type (e.g., both CONSTANT_FLUX).",
85 BCFaceToString((BCFace)neg_face_idx), BCFaceToString((BCFace)pos_face_idx), driven_direction);
86 }
87
88 LOG_ALLOW(GLOBAL, LOG_DEBUG, " ... Symmetry and mathematical types are valid. OK.\n");
89
90 PetscFunctionReturn(0);
91}
#define GLOBAL
Scope for global logging across all processes.
Definition logging.h:46
const char * BCFaceToString(BCFace face)
Returns the canonical log token for a boundary-face enum value.
Definition logging.c:671
#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
const char * BCTypeToString(BCType type)
Returns the canonical log token for a boundary mathematical type.
Definition logging.c:773
@ LOG_DEBUG
Detailed debugging information.
Definition logging.h:32
BCType
Defines the general mathematical/physical Category of a boundary.
Definition variables.h:283
@ INLET
Definition variables.h:290
@ FARFIELD
Definition variables.h:291
@ OUTLET
Definition variables.h:289
@ PERIODIC
Definition variables.h:292
BoundaryFaceConfig boundary_faces[6]
Definition variables.h:931
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
BCType mathematical_type
Definition variables.h:368
BCFace
Identifies the six logical faces of a structured computational block.
Definition variables.h:261
@ 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
Holds the complete configuration for one of the six boundary faces.
Definition variables.h:366
Here is the call graph for this function:
Here is the caller graph for this function:

◆ Apply_WallNoSlip()

static PetscErrorCode Apply_WallNoSlip ( BoundaryCondition self,
BCContext ctx 
)
static

Apply no-slip velocity values to wall-adjacent cells for this boundary condition.

Definition at line 143 of file BC_Handlers.c.

144{
145 PetscErrorCode ierr;
146 UserCtx* user = ctx->user;
147 BCFace face_id = ctx->face_id;
148 PetscBool can_service;
149
150 (void)self; // Unused for simple handlers
151
152 PetscFunctionBeginUser;
153 DMDALocalInfo *info = &user->info;
154 Cmpnts ***ubcs, ***ucont;
155 PetscInt IM_nodes_global, JM_nodes_global,KM_nodes_global;
156
157 IM_nodes_global = user->IM;
158 JM_nodes_global = user->JM;
159 KM_nodes_global = user->KM;
160
161 ierr = CanRankServiceFace(info,IM_nodes_global,JM_nodes_global,KM_nodes_global,face_id,&can_service); CHKERRQ(ierr);
162 // Check if this rank owns part of this boundary face
163 if (!can_service) PetscFunctionReturn(0);
164
165 LOG_ALLOW(LOCAL, LOG_DEBUG, "Apply_WallNoSlip: Applying to Face %d (%s).\n",
166 face_id, BCFaceToString(face_id));
167
168 // Get arrays
169
170 ierr = DMDAVecGetArray(user->fda, user->Bcs.Ubcs, &ubcs); CHKERRQ(ierr);
171 ierr = DMDAVecGetArray(user->fda, user->Ucont, &ucont); CHKERRQ(ierr);
172
173 PetscInt xs = info->xs, xe = info->xs + info->xm;
174 PetscInt ys = info->ys, ye = info->ys + info->ym;
175 PetscInt zs = info->zs, ze = info->zs + info->zm;
176 PetscInt mx = info->mx, my = info->my, mz = info->mz;
177
178 // ✅ Use shrunken loop bounds (avoids edges/corners like inlet handler)
179 PetscInt lxs = xs, lxe = xe, lys = ys, lye = ye, lzs = zs, lze = ze;
180 if (xs == 0) lxs = xs + 1;
181 if (xe == mx) lxe = xe - 1;
182 if (ys == 0) lys = ys + 1;
183 if (ye == my) lye = ye - 1;
184 if (zs == 0) lzs = zs + 1;
185 if (ze == mz) lze = ze - 1;
186
187 switch (face_id) {
188 case BC_FACE_NEG_X: {
189 if (xs == 0){
190 PetscInt i = xs;
191 for (PetscInt k = lzs; k < lze; k++) {
192 for (PetscInt j = lys; j < lye; j++) {
193 // ✅ Set contravariant flux to zero (no penetration)
194 ucont[k][j][i].x = 0.0;
195
196 // ✅ Set boundary velocity to zero (no slip)
197 ubcs[k][j][i].x = 0.0;
198 ubcs[k][j][i].y = 0.0;
199 ubcs[k][j][i].z = 0.0;
200 }
201 }
202 }
203 break;
204 }
205
206 case BC_FACE_POS_X: {
207 if (xe == mx){
208 PetscInt i = xe - 1;
209 for (PetscInt k = lzs; k < lze; k++) {
210 for (PetscInt j = lys; j < lye; j++) {
211 ucont[k][j][i-1].x = 0.0;
212
213 ubcs[k][j][i].x = 0.0;
214 ubcs[k][j][i].y = 0.0;
215 ubcs[k][j][i].z = 0.0;
216 }
217 }
218 }
219 break;
220 }
221 case BC_FACE_NEG_Y: {
222 if (ys == 0){
223 PetscInt j = ys;
224 for (PetscInt k = lzs; k < lze; k++) {
225 for (PetscInt i = lxs; i < lxe; i++) {
226 ucont[k][j][i].y = 0.0;
227
228 ubcs[k][j][i].x = 0.0;
229 ubcs[k][j][i].y = 0.0;
230 ubcs[k][j][i].z = 0.0;
231 }
232 }
233 }
234 } break;
235
236 case BC_FACE_POS_Y: {
237 if (ye == my){
238 PetscInt j = ye - 1;
239 for (PetscInt k = lzs; k < lze; k++) {
240 for (PetscInt i = lxs; i < lxe; i++) {
241 ucont[k][j-1][i].y = 0.0;
242
243 ubcs[k][j][i].x = 0.0;
244 ubcs[k][j][i].y = 0.0;
245 ubcs[k][j][i].z = 0.0;
246 }
247 }
248 }
249 } break;
250
251 case BC_FACE_NEG_Z: {
252 if (zs == 0){
253 PetscInt k = zs;
254 for (PetscInt j = lys; j < lye; j++) {
255 for (PetscInt i = lxs; i < lxe; i++) {
256 ucont[k][j][i].z = 0.0;
257
258 ubcs[k][j][i].x = 0.0;
259 ubcs[k][j][i].y = 0.0;
260 ubcs[k][j][i].z = 0.0;
261 }
262 }
263 }
264 } break;
265
266 case BC_FACE_POS_Z: {
267 if (ze == mz) {
268 PetscInt k = ze - 1;
269 for (PetscInt j = lys; j < lye; j++) {
270 for (PetscInt i = lxs; i < lxe; i++) {
271 ucont[k-1][j][i].z = 0.0;
272
273 ubcs[k][j][i].x = 0.0;
274 ubcs[k][j][i].y = 0.0;
275 ubcs[k][j][i].z = 0.0;
276 }
277 }
278 }
279 } break;
280 }
281
282 // Restore arrays
283 ierr = DMDAVecRestoreArray(user->fda, user->Bcs.Ubcs, &ubcs); CHKERRQ(ierr);
284 ierr = DMDAVecRestoreArray(user->fda, user->Ucont, &ucont); CHKERRQ(ierr);
285
286 PetscFunctionReturn(0);
287}
PetscErrorCode CanRankServiceFace(const DMDALocalInfo *info, PetscInt IM_nodes_global, PetscInt JM_nodes_global, PetscInt KM_nodes_global, BCFace face_id, PetscBool *can_service_out)
Determines if the current MPI rank owns any part of a specified global face.
Definition Boundaries.c:127
#define LOCAL
Logging scope definitions for controlling message output.
Definition logging.h:45
PetscInt KM
Definition variables.h:920
BCFace face_id
Definition variables.h:345
Vec Ucont
Definition variables.h:939
Vec Ubcs
Physical Cartesian velocity at boundary faces. Full 3D array but only boundary-face entries are meani...
Definition variables.h:123
PetscScalar x
Definition variables.h:103
BCS Bcs
Definition variables.h:934
UserCtx * user
Definition variables.h:344
PetscScalar z
Definition variables.h:103
PetscInt JM
Definition variables.h:920
DMDALocalInfo info
Definition variables.h:918
PetscScalar y
Definition variables.h:103
PetscInt IM
Definition variables.h:920
A 3D point or vector with PetscScalar components.
Definition variables.h:102
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:

◆ Create_WallNoSlip()

PetscErrorCode Create_WallNoSlip ( BoundaryCondition bc)

Implementation of Create_WallNoSlip().

Configures a BoundaryCondition object to behave as a no-slip, stationary wall.

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

See also
Create_WallNoSlip()

Definition at line 114 of file BC_Handlers.c.

115{
116 PetscFunctionBeginUser;
117
118 if (!bc) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
119 "Input BoundaryCondition object is NULL in Create_WallNoSlip");
120
121 // ✅ Set priority
123
124 // Assign function pointers
125 bc->Initialize = NULL;
126 bc->PreStep = NULL;
128 bc->PostStep = NULL;
129 bc->UpdateUbcs = NULL;
130 bc->Destroy = NULL;
131
132 // No private data needed for this simple handler
133 bc->data = NULL;
134
135 PetscFunctionReturn(0);
136}
static PetscErrorCode Apply_WallNoSlip(BoundaryCondition *self, BCContext *ctx)
Apply no-slip velocity values to wall-adjacent cells for this boundary condition.
PetscErrorCode(* PostStep)(BoundaryCondition *self, BCContext *ctx, PetscReal *local_inflow, PetscReal *local_outflow)
Definition variables.h:360
PetscErrorCode(* PreStep)(BoundaryCondition *self, BCContext *ctx, PetscReal *local_inflow, PetscReal *local_outflow)
Definition variables.h:358
PetscErrorCode(* Destroy)(BoundaryCondition *self)
Definition variables.h:362
PetscErrorCode(* Initialize)(BoundaryCondition *self, BCContext *ctx)
Definition variables.h:357
PetscErrorCode(* UpdateUbcs)(BoundaryCondition *self, BCContext *ctx)
Definition variables.h:361
PetscErrorCode(* Apply)(BoundaryCondition *self, BCContext *ctx)
Definition variables.h:359
BCPriorityType priority
Definition variables.h:355
@ BC_PRIORITY_WALL
Definition variables.h:327
Here is the call graph for this function:
Here is the caller graph for this function:

◆ Initialize_InletConstantVelocity()

static PetscErrorCode Initialize_InletConstantVelocity ( BoundaryCondition self,
BCContext ctx 
)
static

Initialize persistent state for a constant-velocity inlet boundary.

Definition at line 350 of file BC_Handlers.c.

351{
352 PetscErrorCode ierr;
353 UserCtx* user = ctx->user;
354 BCFace face_id = ctx->face_id;
356 PetscBool found;
357
358 PetscFunctionBeginUser;
359 LOG_ALLOW(LOCAL, LOG_DEBUG, "Initialize_InletConstantVelocity: Initializing handler for Face %d. \n", face_id);
360 data->normal_velocity = 0.0;
361
362 switch (face_id) {
363 case BC_FACE_NEG_X:
364 case BC_FACE_POS_X:
365 // For X-faces, read "vx" as normal velocity
366 ierr = GetBCParamReal(user->boundary_faces[face_id].params, "vx",
367 &data->normal_velocity, &found); CHKERRQ(ierr);
368 break;
369
370 case BC_FACE_NEG_Y:
371 case BC_FACE_POS_Y:
372 // For Y-faces, read "vy" as normal velocity
373 ierr = GetBCParamReal(user->boundary_faces[face_id].params, "vy",
374 &data->normal_velocity, &found); CHKERRQ(ierr);
375 break;
376
377 case BC_FACE_NEG_Z:
378 case BC_FACE_POS_Z:
379 // For Z-faces, read "vz" as normal velocity
380 ierr = GetBCParamReal(user->boundary_faces[face_id].params, "vz",
381 &data->normal_velocity, &found); CHKERRQ(ierr);
382 break;
383 }
384
385 LOG_ALLOW(LOCAL, LOG_INFO, " Inlet Face %d: normal velocity = %.4f\n",
386 face_id, data->normal_velocity);
387
388 // Set initial boundary state
389 ierr = Apply_InletConstantVelocity(self, ctx); CHKERRQ(ierr);
390
391 PetscFunctionReturn(0);
392}
static PetscErrorCode Apply_InletConstantVelocity(BoundaryCondition *self, BCContext *ctx)
Impose the configured constant velocity on inlet boundary cells.
Private data structure for the Constant Velocity Inlet handler.
PetscErrorCode GetBCParamReal(BC_Param *params, const char *key, PetscReal *value_out, PetscBool *found)
Searches a BC_Param linked list for a key and returns its value as a double.
Definition io.c:752
@ LOG_INFO
Informational messages about program execution.
Definition logging.h:31
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PreStep_InletConstantVelocity()

static PetscErrorCode PreStep_InletConstantVelocity ( BoundaryCondition self,
BCContext ctx,
PetscReal *  in,
PetscReal *  out 
)
static

Update constant-inlet data required before the next solver step.

Definition at line 399 of file BC_Handlers.c.

402{
403 // No preparation needed for constant velocity inlet.
404 // The velocity is already stored in self->data from Initialize.
405 // Apply will set ucont, and PostStep will measure the actual flux.
406
407 (void)self;
408 (void)ctx;
409 (void)local_inflow_contribution;
410 (void)local_outflow_contribution;
411
412 PetscFunctionBeginUser;
413 PetscFunctionReturn(0);
414}
Here is the caller graph for this function:

◆ Apply_InletConstantVelocity()

static PetscErrorCode Apply_InletConstantVelocity ( BoundaryCondition self,
BCContext ctx 
)
static

Impose the configured constant velocity on inlet boundary cells.

Definition at line 421 of file BC_Handlers.c.

422{
423 PetscErrorCode ierr;
424 UserCtx* user = ctx->user;
425 BCFace face_id = ctx->face_id;
427 PetscBool can_service;
428
429 PetscFunctionBeginUser;
430
431 DMDALocalInfo *info = &user->info;
432 Cmpnts ***ubcs, ***ucont, ***csi, ***eta, ***zet;
433 PetscReal ***nvert;
434 PetscInt IM_nodes_global, JM_nodes_global,KM_nodes_global;
435
436 IM_nodes_global = user->IM;
437 JM_nodes_global = user->JM;
438 KM_nodes_global = user->KM;
439
440 ierr = CanRankServiceFace(info,IM_nodes_global,JM_nodes_global,KM_nodes_global,face_id,&can_service); CHKERRQ(ierr);
441
442 if (!can_service) PetscFunctionReturn(0);
443
444 // Get arrays
445
446 ierr = DMDAVecGetArray(user->fda, user->Bcs.Ubcs, &ubcs); CHKERRQ(ierr);
447 ierr = DMDAVecGetArray(user->fda, user->Ucont, &ucont); CHKERRQ(ierr);
448 ierr = DMDAVecGetArrayRead(user->fda, user->lCsi, (const Cmpnts***)&csi); CHKERRQ(ierr);
449 ierr = DMDAVecGetArrayRead(user->fda, user->lEta, (const Cmpnts***)&eta); CHKERRQ(ierr);
450 ierr = DMDAVecGetArrayRead(user->fda, user->lZet, (const Cmpnts***)&zet); CHKERRQ(ierr);
451 ierr = DMDAVecGetArrayRead(user->da, user->lNvert, (const PetscReal***)&nvert); CHKERRQ(ierr);
452
453 // Get SCALAR velocity (not vector!)
454 PetscReal uin_this_point = data->normal_velocity;
455
456 PetscInt xs = info->xs, xe = info->xs + info->xm;
457 PetscInt ys = info->ys, ye = info->ys + info->ym;
458 PetscInt zs = info->zs, ze = info->zs + info->zm;
459 PetscInt mx = info->mx, my = info->my, mz = info->mz;
460
461 PetscInt lxs = xs, lxe = xe, lys = ys, lye = ye, lzs = zs, lze = ze;
462 if (xs == 0) lxs = xs + 1;
463 if (xe == mx) lxe = xe - 1;
464 if (ys == 0) lys = ys + 1;
465 if (ye == my) lye = ye - 1;
466 if (zs == 0) lzs = zs + 1;
467 if (ze == mz) lze = ze - 1;
468
469 switch (face_id) {
470 case BC_FACE_NEG_X:
471 case BC_FACE_POS_X: {
472 PetscReal sign = (face_id == BC_FACE_NEG_X) ? 1.0 : -1.0;
473 PetscInt i = (face_id == BC_FACE_NEG_X) ? xs : mx - 2;
474
475 for (PetscInt k = lzs; k < lze; k++) {
476 for (PetscInt j = lys; j < lye; j++) {
477 if ((sign > 0 && nvert[k][j][i+1] > 0.1) ||
478 (sign < 0 && nvert[k][j][i] > 0.1)) continue;
479
480 PetscReal CellArea = sqrt(csi[k][j][i].x * csi[k][j][i].x +
481 csi[k][j][i].y * csi[k][j][i].y +
482 csi[k][j][i].z * csi[k][j][i].z);
483
484 ucont[k][j][i].x = sign * uin_this_point * CellArea;
485
486 ubcs[k][j][i + (sign < 0)].x = sign * uin_this_point * csi[k][j][i].x / CellArea;
487 ubcs[k][j][i + (sign < 0)].y = sign * uin_this_point * csi[k][j][i].y / CellArea;
488 ubcs[k][j][i + (sign < 0)].z = sign * uin_this_point * csi[k][j][i].z / CellArea;
489 }
490 }
491 } break;
492
493 case BC_FACE_NEG_Y:
494 case BC_FACE_POS_Y: {
495 PetscReal sign = (face_id == BC_FACE_NEG_Y) ? 1.0 : -1.0;
496 PetscInt j = (face_id == BC_FACE_NEG_Y) ? ys : my - 2;
497
498 for (PetscInt k = lzs; k < lze; k++) {
499 for (PetscInt i = lxs; i < lxe; i++) {
500 if ((sign > 0 && nvert[k][j+1][i] > 0.1) ||
501 (sign < 0 && nvert[k][j][i] > 0.1)) continue;
502
503 PetscReal CellArea = sqrt(eta[k][j][i].x * eta[k][j][i].x +
504 eta[k][j][i].y * eta[k][j][i].y +
505 eta[k][j][i].z * eta[k][j][i].z);
506
507 ucont[k][j][i].y = sign * uin_this_point * CellArea;
508
509 ubcs[k][j + (sign < 0)][i].x = sign * uin_this_point * eta[k][j][i].x / CellArea;
510 ubcs[k][j + (sign < 0)][i].y = sign * uin_this_point * eta[k][j][i].y / CellArea;
511 ubcs[k][j + (sign < 0)][i].z = sign * uin_this_point * eta[k][j][i].z / CellArea;
512 }
513 }
514 } break;
515
516 case BC_FACE_NEG_Z:
517 case BC_FACE_POS_Z: {
518 PetscReal sign = (face_id == BC_FACE_NEG_Z) ? 1.0 : -1.0;
519 PetscInt k = (face_id == BC_FACE_NEG_Z) ? zs : mz - 2;
520
521 for (PetscInt j = lys; j < lye; j++) {
522 for (PetscInt i = lxs; i < lxe; i++) {
523 if ((sign > 0 && nvert[k+1][j][i] > 0.1) ||
524 (sign < 0 && nvert[k][j][i] > 0.1)) continue;
525
526 PetscReal CellArea = sqrt(zet[k][j][i].x * zet[k][j][i].x +
527 zet[k][j][i].y * zet[k][j][i].y +
528 zet[k][j][i].z * zet[k][j][i].z);
529
530 ucont[k][j][i].z = sign * uin_this_point * CellArea;
531
532 ubcs[k + (sign < 0)][j][i].x = sign * uin_this_point * zet[k][j][i].x / CellArea;
533 ubcs[k + (sign < 0)][j][i].y = sign * uin_this_point * zet[k][j][i].y / CellArea;
534 ubcs[k + (sign < 0)][j][i].z = sign * uin_this_point * zet[k][j][i].z / CellArea;
535 }
536 }
537 } break;
538 }
539
540 // Restore arrays
541 ierr = DMDAVecRestoreArray(user->fda, user->Bcs.Ubcs, &ubcs); CHKERRQ(ierr);
542 ierr = DMDAVecRestoreArray(user->fda, user->Ucont, &ucont); CHKERRQ(ierr);
543 ierr = DMDAVecRestoreArrayRead(user->fda, user->lCsi, (const Cmpnts***)&csi); CHKERRQ(ierr);
544 ierr = DMDAVecRestoreArrayRead(user->fda, user->lEta, (const Cmpnts***)&eta); CHKERRQ(ierr);
545 ierr = DMDAVecRestoreArrayRead(user->fda, user->lZet, (const Cmpnts***)&zet); CHKERRQ(ierr);
546 ierr = DMDAVecRestoreArrayRead(user->da, user->lNvert, (const PetscReal***)&nvert); CHKERRQ(ierr);
547
548 PetscFunctionReturn(0);
549}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PostStep_InletConstantVelocity()

static PetscErrorCode PostStep_InletConstantVelocity ( BoundaryCondition self,
BCContext ctx,
PetscReal *  in,
PetscReal *  out 
)
static

Perform post-step bookkeeping for a constant-velocity inlet boundary.

Definition at line 556 of file BC_Handlers.c.

559{
560 PetscErrorCode ierr;
561 UserCtx* user = ctx->user;
562 BCFace face_id = ctx->face_id;
563 PetscBool can_service;
564
565 (void)self;
566 (void)local_outflow_contribution;
567
568 PetscFunctionBeginUser;
569
570 DMDALocalInfo *info = &user->info;
571 Cmpnts ***ucont;
572
573 PetscInt IM_nodes_global, JM_nodes_global,KM_nodes_global;
574
575 IM_nodes_global = user->IM;
576 JM_nodes_global = user->JM;
577 KM_nodes_global = user->KM;
578
579 ierr = CanRankServiceFace(info,IM_nodes_global,JM_nodes_global,KM_nodes_global,face_id,&can_service); CHKERRQ(ierr);
580
581
582 if (!can_service) PetscFunctionReturn(0);
583
584 ierr = DMDAVecGetArrayRead(user->fda, user->Ucont, (const Cmpnts***)&ucont); CHKERRQ(ierr);
585
586 PetscReal local_flux = 0.0;
587
588 PetscInt xs = info->xs, xe = info->xs + info->xm;
589 PetscInt ys = info->ys, ye = info->ys + info->ym;
590 PetscInt zs = info->zs, ze = info->zs + info->zm;
591 PetscInt mx = info->mx, my = info->my, mz = info->mz;
592
593 PetscInt lxs = xs, lxe = xe, lys = ys, lye = ye, lzs = zs, lze = ze;
594 if (xs == 0) lxs = xs + 1;
595 if (xe == mx) lxe = xe - 1;
596 if (ys == 0) lys = ys + 1;
597 if (ye == my) lye = ye - 1;
598 if (zs == 0) lzs = zs + 1;
599 if (ze == mz) lze = ze - 1;
600
601 // Sum ucont components
602 switch (face_id) {
603 case BC_FACE_NEG_X:
604 case BC_FACE_POS_X: {
605 PetscInt i = (face_id == BC_FACE_NEG_X) ? xs : mx - 2;
606 for (PetscInt k = lzs; k < lze; k++) {
607 for (PetscInt j = lys; j < lye; j++) {
608 local_flux += ucont[k][j][i].x;
609 }
610 }
611 } break;
612
613 case BC_FACE_NEG_Y:
614 case BC_FACE_POS_Y: {
615 PetscInt j = (face_id == BC_FACE_NEG_Y) ? ys : my - 2;
616 for (PetscInt k = lzs; k < lze; k++) {
617 for (PetscInt i = lxs; i < lxe; i++) {
618 local_flux += ucont[k][j][i].y;
619 }
620 }
621 } break;
622
623 case BC_FACE_NEG_Z:
624 case BC_FACE_POS_Z: {
625 PetscInt k = (face_id == BC_FACE_NEG_Z) ? zs : mz - 2;
626 for (PetscInt j = lys; j < lye; j++) {
627 for (PetscInt i = lxs; i < lxe; i++) {
628 local_flux += ucont[k][j][i].z;
629 }
630 }
631 } break;
632 }
633
634 ierr = DMDAVecRestoreArrayRead(user->fda, user->Ucont, (const Cmpnts***)&ucont); CHKERRQ(ierr);
635
636 *local_inflow_contribution += local_flux;
637
638 LOG_ALLOW(LOCAL, LOG_DEBUG, "PostStep_InletConstantVelocity: Face %d, flux = %.6e\n",
639 face_id, local_flux);
640
641 PetscFunctionReturn(0);
642}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ Destroy_InletConstantVelocity()

static PetscErrorCode Destroy_InletConstantVelocity ( BoundaryCondition self)
static

Release resources owned by a constant-velocity inlet boundary.

Definition at line 650 of file BC_Handlers.c.

651{
652 PetscFunctionBeginUser;
653 if (self && self->data) {
654 PetscFree(self->data);
655 self->data = NULL;
656 }
657 PetscFunctionReturn(0);
658}
Here is the caller graph for this function:

◆ Create_InletConstantVelocity()

PetscErrorCode Create_InletConstantVelocity ( BoundaryCondition bc)

Implementation of Create_InletConstantVelocity().

Configures a BoundaryCondition object to behave as a constant velocity inlet.

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

See also
Create_InletConstantVelocity()

Definition at line 322 of file BC_Handlers.c.

323{
324 PetscErrorCode ierr;
325 PetscFunctionBeginUser;
326
327 if (!bc) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "BoundaryCondition is NULL");
328
329 InletConstantData *data = NULL;
330 ierr = PetscMalloc1(1, &data); CHKERRQ(ierr);
331 bc->data = (void*)data;
332
338 bc->UpdateUbcs = NULL;
340
341 PetscFunctionReturn(0);
342}
static PetscErrorCode PreStep_InletConstantVelocity(BoundaryCondition *self, BCContext *ctx, PetscReal *in, PetscReal *out)
Update constant-inlet data required before the next solver step.
static PetscErrorCode Destroy_InletConstantVelocity(BoundaryCondition *self)
Release resources owned by a constant-velocity inlet boundary.
static PetscErrorCode Initialize_InletConstantVelocity(BoundaryCondition *self, BCContext *ctx)
Initialize persistent state for a constant-velocity inlet boundary.
static PetscErrorCode PostStep_InletConstantVelocity(BoundaryCondition *self, BCContext *ctx, PetscReal *in, PetscReal *out)
Perform post-step bookkeeping for a constant-velocity inlet boundary.
@ BC_PRIORITY_INLET
Definition variables.h:325
Here is the call graph for this function:
Here is the caller graph for this function:

◆ Initialize_InletParabolicProfile()

static PetscErrorCode Initialize_InletParabolicProfile ( BoundaryCondition self,
BCContext ctx 
)
static

Initialize the geometric data used to evaluate a parabolic inlet profile.

Definition at line 743 of file BC_Handlers.c.

744{
745 PetscErrorCode ierr;
746 UserCtx* user = ctx->user;
747 BCFace face_id = ctx->face_id;
749 PetscBool found;
750
751 PetscFunctionBeginUser;
752 LOG_ALLOW(LOCAL, LOG_DEBUG, "Initialize_InletParabolicProfile: Initializing handler for Face %d.\n", face_id);
753
754 // --- Parse v_max from boundary condition parameters ---
755 data->v_max = 0.0;
756 ierr = GetBCParamReal(user->boundary_faces[face_id].params, "v_max",
757 &data->v_max, &found); CHKERRQ(ierr);
758 if (!found) {
759 LOG_ALLOW(GLOBAL, LOG_WARNING, "Initialize_InletParabolicProfile: 'v_max' not found in params for face %d. Defaulting to 0.0.\n", face_id);
760 }
761
762 // --- Determine cross-stream dimensions based on face orientation ---
763 PetscReal cs1_dim, cs2_dim;
764 switch (face_id) {
765 case BC_FACE_NEG_X:
766 case BC_FACE_POS_X:
767 cs1_dim = (PetscReal)user->JM; // j-direction
768 cs2_dim = (PetscReal)user->KM; // k-direction
769 break;
770 case BC_FACE_NEG_Y:
771 case BC_FACE_POS_Y:
772 cs1_dim = (PetscReal)user->IM; // i-direction
773 cs2_dim = (PetscReal)user->KM; // k-direction
774 break;
775 case BC_FACE_NEG_Z:
776 case BC_FACE_POS_Z:
777 default:
778 cs1_dim = (PetscReal)user->IM; // i-direction
779 cs2_dim = (PetscReal)user->JM; // j-direction
780 break;
781 }
782
783 // Interior width = dim - 2 (nodes 1 through dim-2 are interior)
784 PetscReal cs1_width = cs1_dim - 2.0;
785 PetscReal cs2_width = cs2_dim - 2.0;
786
787 data->cs1_center = 1.0 + cs1_width / 2.0;
788 data->cs2_center = 1.0 + cs2_width / 2.0;
789 data->cs1_half = cs1_width / 2.0;
790 data->cs2_half = cs2_width / 2.0;
791
792 LOG_ALLOW(LOCAL, LOG_INFO, " Inlet Face %d (Parabolic): v_max = %.4f\n", face_id, data->v_max);
793 LOG_ALLOW(LOCAL, LOG_DEBUG, " Cross-stream 1: center=%.1f, half=%.1f\n", data->cs1_center, data->cs1_half);
794 LOG_ALLOW(LOCAL, LOG_DEBUG, " Cross-stream 2: center=%.1f, half=%.1f\n", data->cs2_center, data->cs2_half);
795
796 // Set initial boundary state
797 ierr = Apply_InletParabolicProfile(self, ctx); CHKERRQ(ierr);
798
799 PetscFunctionReturn(0);
800}
static PetscErrorCode Apply_InletParabolicProfile(BoundaryCondition *self, BCContext *ctx)
Impose the evaluated parabolic velocity profile on inlet cells.
Private data structure for the Parabolic Velocity Inlet handler.
@ LOG_WARNING
Non-critical issues that warrant attention.
Definition logging.h:30
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PreStep_InletParabolicProfile()

static PetscErrorCode PreStep_InletParabolicProfile ( BoundaryCondition self,
BCContext ctx,
PetscReal *  in,
PetscReal *  out 
)
static

Refresh parabolic-inlet values required before the solver step.

Definition at line 808 of file BC_Handlers.c.

811{
812 (void)self;
813 (void)ctx;
814 (void)local_inflow_contribution;
815 (void)local_outflow_contribution;
816
817 PetscFunctionBeginUser;
818 PetscFunctionReturn(0);
819}
Here is the caller graph for this function:

◆ Apply_InletParabolicProfile()

static PetscErrorCode Apply_InletParabolicProfile ( BoundaryCondition self,
BCContext ctx 
)
static

Impose the evaluated parabolic velocity profile on inlet cells.

Definition at line 827 of file BC_Handlers.c.

828{
829 PetscErrorCode ierr;
830 UserCtx* user = ctx->user;
831 BCFace face_id = ctx->face_id;
833 PetscBool can_service;
834
835 PetscFunctionBeginUser;
836
837 DMDALocalInfo *info = &user->info;
838 Cmpnts ***ubcs, ***ucont, ***csi, ***eta, ***zet;
839 PetscReal ***nvert;
840 PetscInt IM_nodes_global, JM_nodes_global, KM_nodes_global;
841
842 IM_nodes_global = user->IM;
843 JM_nodes_global = user->JM;
844 KM_nodes_global = user->KM;
845
846 ierr = CanRankServiceFace(info, IM_nodes_global, JM_nodes_global, KM_nodes_global,
847 face_id, &can_service); CHKERRQ(ierr);
848
849 if (!can_service) PetscFunctionReturn(0);
850
851 // Get arrays
852 ierr = DMDAVecGetArray(user->fda, user->Bcs.Ubcs, &ubcs); CHKERRQ(ierr);
853 ierr = DMDAVecGetArray(user->fda, user->Ucont, &ucont); CHKERRQ(ierr);
854 ierr = DMDAVecGetArrayRead(user->fda, user->lCsi, (const Cmpnts***)&csi); CHKERRQ(ierr);
855 ierr = DMDAVecGetArrayRead(user->fda, user->lEta, (const Cmpnts***)&eta); CHKERRQ(ierr);
856 ierr = DMDAVecGetArrayRead(user->fda, user->lZet, (const Cmpnts***)&zet); CHKERRQ(ierr);
857 ierr = DMDAVecGetArrayRead(user->da, user->lNvert, (const PetscReal***)&nvert); CHKERRQ(ierr);
858
859 PetscInt xs = info->xs, xe = info->xs + info->xm;
860 PetscInt ys = info->ys, ye = info->ys + info->ym;
861 PetscInt zs = info->zs, ze = info->zs + info->zm;
862 PetscInt mx = info->mx, my = info->my, mz = info->mz;
863
864 PetscInt lxs = xs, lxe = xe, lys = ys, lye = ye, lzs = zs, lze = ze;
865 if (xs == 0) lxs = xs + 1;
866 if (xe == mx) lxe = xe - 1;
867 if (ys == 0) lys = ys + 1;
868 if (ye == my) lye = ye - 1;
869 if (zs == 0) lzs = zs + 1;
870 if (ze == mz) lze = ze - 1;
871
872 switch (face_id) {
873 case BC_FACE_NEG_X:
874 case BC_FACE_POS_X: {
875 // X-faces: normal = i, cross-stream = (j, k)
876 PetscReal sign = (face_id == BC_FACE_NEG_X) ? 1.0 : -1.0;
877 PetscInt i = (face_id == BC_FACE_NEG_X) ? xs : mx - 2;
878
879 for (PetscInt k = lzs; k < lze; k++) {
880 for (PetscInt j = lys; j < lye; j++) {
881 if ((sign > 0 && nvert[k][j][i+1] > 0.1) ||
882 (sign < 0 && nvert[k][j][i] > 0.1)) continue;
883
884 // Evaluate parabolic profile: cs1 = j, cs2 = k
885 PetscReal cs1_norm = ((PetscReal)j - data->cs1_center) / data->cs1_half;
886 PetscReal cs2_norm = ((PetscReal)k - data->cs2_center) / data->cs2_half;
887 PetscReal profile = PetscMax(0.0, 1.0 - cs1_norm * cs1_norm)
888 * PetscMax(0.0, 1.0 - cs2_norm * cs2_norm);
889 PetscReal uin_local = data->v_max * profile;
890
891 PetscReal CellArea = sqrt(csi[k][j][i].x * csi[k][j][i].x +
892 csi[k][j][i].y * csi[k][j][i].y +
893 csi[k][j][i].z * csi[k][j][i].z);
894
895 ucont[k][j][i].x = sign * uin_local * CellArea;
896
897 ubcs[k][j][i + (sign < 0)].x = sign * uin_local * csi[k][j][i].x / CellArea;
898 ubcs[k][j][i + (sign < 0)].y = sign * uin_local * csi[k][j][i].y / CellArea;
899 ubcs[k][j][i + (sign < 0)].z = sign * uin_local * csi[k][j][i].z / CellArea;
900 }
901 }
902 } break;
903
904 case BC_FACE_NEG_Y:
905 case BC_FACE_POS_Y: {
906 // Y-faces: normal = j, cross-stream = (i, k)
907 PetscReal sign = (face_id == BC_FACE_NEG_Y) ? 1.0 : -1.0;
908 PetscInt j = (face_id == BC_FACE_NEG_Y) ? ys : my - 2;
909
910 for (PetscInt k = lzs; k < lze; k++) {
911 for (PetscInt i = lxs; i < lxe; i++) {
912 if ((sign > 0 && nvert[k][j+1][i] > 0.1) ||
913 (sign < 0 && nvert[k][j][i] > 0.1)) continue;
914
915 // Evaluate parabolic profile: cs1 = i, cs2 = k
916 PetscReal cs1_norm = ((PetscReal)i - data->cs1_center) / data->cs1_half;
917 PetscReal cs2_norm = ((PetscReal)k - data->cs2_center) / data->cs2_half;
918 PetscReal profile = PetscMax(0.0, 1.0 - cs1_norm * cs1_norm)
919 * PetscMax(0.0, 1.0 - cs2_norm * cs2_norm);
920 PetscReal uin_local = data->v_max * profile;
921
922 PetscReal CellArea = sqrt(eta[k][j][i].x * eta[k][j][i].x +
923 eta[k][j][i].y * eta[k][j][i].y +
924 eta[k][j][i].z * eta[k][j][i].z);
925
926 ucont[k][j][i].y = sign * uin_local * CellArea;
927
928 ubcs[k][j + (sign < 0)][i].x = sign * uin_local * eta[k][j][i].x / CellArea;
929 ubcs[k][j + (sign < 0)][i].y = sign * uin_local * eta[k][j][i].y / CellArea;
930 ubcs[k][j + (sign < 0)][i].z = sign * uin_local * eta[k][j][i].z / CellArea;
931 }
932 }
933 } break;
934
935 case BC_FACE_NEG_Z:
936 case BC_FACE_POS_Z: {
937 // Z-faces: normal = k, cross-stream = (i, j)
938 PetscReal sign = (face_id == BC_FACE_NEG_Z) ? 1.0 : -1.0;
939 PetscInt k = (face_id == BC_FACE_NEG_Z) ? zs : mz - 2;
940
941 for (PetscInt j = lys; j < lye; j++) {
942 for (PetscInt i = lxs; i < lxe; i++) {
943 if ((sign > 0 && nvert[k+1][j][i] > 0.1) ||
944 (sign < 0 && nvert[k][j][i] > 0.1)) continue;
945
946 // Evaluate parabolic profile: cs1 = i, cs2 = j
947 PetscReal cs1_norm = ((PetscReal)i - data->cs1_center) / data->cs1_half;
948 PetscReal cs2_norm = ((PetscReal)j - data->cs2_center) / data->cs2_half;
949 PetscReal profile = PetscMax(0.0, 1.0 - cs1_norm * cs1_norm)
950 * PetscMax(0.0, 1.0 - cs2_norm * cs2_norm);
951 PetscReal uin_local = data->v_max * profile;
952
953 PetscReal CellArea = sqrt(zet[k][j][i].x * zet[k][j][i].x +
954 zet[k][j][i].y * zet[k][j][i].y +
955 zet[k][j][i].z * zet[k][j][i].z);
956
957 ucont[k][j][i].z = sign * uin_local * CellArea;
958
959 ubcs[k + (sign < 0)][j][i].x = sign * uin_local * zet[k][j][i].x / CellArea;
960 ubcs[k + (sign < 0)][j][i].y = sign * uin_local * zet[k][j][i].y / CellArea;
961 ubcs[k + (sign < 0)][j][i].z = sign * uin_local * zet[k][j][i].z / CellArea;
962 }
963 }
964 } break;
965 }
966
967 // Restore arrays
968 ierr = DMDAVecRestoreArray(user->fda, user->Bcs.Ubcs, &ubcs); CHKERRQ(ierr);
969 ierr = DMDAVecRestoreArray(user->fda, user->Ucont, &ucont); CHKERRQ(ierr);
970 ierr = DMDAVecRestoreArrayRead(user->fda, user->lCsi, (const Cmpnts***)&csi); CHKERRQ(ierr);
971 ierr = DMDAVecRestoreArrayRead(user->fda, user->lEta, (const Cmpnts***)&eta); CHKERRQ(ierr);
972 ierr = DMDAVecRestoreArrayRead(user->fda, user->lZet, (const Cmpnts***)&zet); CHKERRQ(ierr);
973 ierr = DMDAVecRestoreArrayRead(user->da, user->lNvert, (const PetscReal***)&nvert); CHKERRQ(ierr);
974
975 PetscFunctionReturn(0);
976}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PostStep_InletParabolicProfile()

static PetscErrorCode PostStep_InletParabolicProfile ( BoundaryCondition self,
BCContext ctx,
PetscReal *  in,
PetscReal *  out 
)
static

Perform post-step bookkeeping for a parabolic inlet boundary.

Definition at line 984 of file BC_Handlers.c.

987{
988 PetscErrorCode ierr;
989 UserCtx* user = ctx->user;
990 BCFace face_id = ctx->face_id;
991 PetscBool can_service;
992
993 (void)self;
994 (void)local_outflow_contribution;
995
996 PetscFunctionBeginUser;
997
998 DMDALocalInfo *info = &user->info;
999 Cmpnts ***ucont;
1000
1001 PetscInt IM_nodes_global, JM_nodes_global, KM_nodes_global;
1002
1003 IM_nodes_global = user->IM;
1004 JM_nodes_global = user->JM;
1005 KM_nodes_global = user->KM;
1006
1007 ierr = CanRankServiceFace(info, IM_nodes_global, JM_nodes_global, KM_nodes_global,
1008 face_id, &can_service); CHKERRQ(ierr);
1009
1010 if (!can_service) PetscFunctionReturn(0);
1011
1012 ierr = DMDAVecGetArrayRead(user->fda, user->Ucont, (const Cmpnts***)&ucont); CHKERRQ(ierr);
1013
1014 PetscReal local_flux = 0.0;
1015
1016 PetscInt xs = info->xs, xe = info->xs + info->xm;
1017 PetscInt ys = info->ys, ye = info->ys + info->ym;
1018 PetscInt zs = info->zs, ze = info->zs + info->zm;
1019 PetscInt mx = info->mx, my = info->my, mz = info->mz;
1020
1021 PetscInt lxs = xs, lxe = xe, lys = ys, lye = ye, lzs = zs, lze = ze;
1022 if (xs == 0) lxs = xs + 1;
1023 if (xe == mx) lxe = xe - 1;
1024 if (ys == 0) lys = ys + 1;
1025 if (ye == my) lye = ye - 1;
1026 if (zs == 0) lzs = zs + 1;
1027 if (ze == mz) lze = ze - 1;
1028
1029 switch (face_id) {
1030 case BC_FACE_NEG_X:
1031 case BC_FACE_POS_X: {
1032 PetscInt i = (face_id == BC_FACE_NEG_X) ? xs : mx - 2;
1033 for (PetscInt k = lzs; k < lze; k++) {
1034 for (PetscInt j = lys; j < lye; j++) {
1035 local_flux += ucont[k][j][i].x;
1036 }
1037 }
1038 } break;
1039
1040 case BC_FACE_NEG_Y:
1041 case BC_FACE_POS_Y: {
1042 PetscInt j = (face_id == BC_FACE_NEG_Y) ? ys : my - 2;
1043 for (PetscInt k = lzs; k < lze; k++) {
1044 for (PetscInt i = lxs; i < lxe; i++) {
1045 local_flux += ucont[k][j][i].y;
1046 }
1047 }
1048 } break;
1049
1050 case BC_FACE_NEG_Z:
1051 case BC_FACE_POS_Z: {
1052 PetscInt k = (face_id == BC_FACE_NEG_Z) ? zs : mz - 2;
1053 for (PetscInt j = lys; j < lye; j++) {
1054 for (PetscInt i = lxs; i < lxe; i++) {
1055 local_flux += ucont[k][j][i].z;
1056 }
1057 }
1058 } break;
1059 }
1060
1061 ierr = DMDAVecRestoreArrayRead(user->fda, user->Ucont, (const Cmpnts***)&ucont); CHKERRQ(ierr);
1062
1063 *local_inflow_contribution += local_flux;
1064
1065 LOG_ALLOW(LOCAL, LOG_DEBUG, "PostStep_InletParabolicProfile: Face %d, flux = %.6e\n",
1066 face_id, local_flux);
1067
1068 PetscFunctionReturn(0);
1069}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ Destroy_InletParabolicProfile()

static PetscErrorCode Destroy_InletParabolicProfile ( BoundaryCondition self)
static

Release resources owned by a parabolic inlet boundary.

Definition at line 1077 of file BC_Handlers.c.

1078{
1079 PetscFunctionBeginUser;
1080 if (self && self->data) {
1081 PetscFree(self->data);
1082 self->data = NULL;
1083 }
1084 PetscFunctionReturn(0);
1085}
Here is the caller graph for this function:

◆ Create_InletParabolicProfile()

PetscErrorCode Create_InletParabolicProfile ( BoundaryCondition bc)

Implementation of Create_InletParabolicProfile().

Configures a BoundaryCondition object for a parabolic inlet profile.

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

See also
Create_InletParabolicProfile()

Definition at line 715 of file BC_Handlers.c.

716{
717 PetscErrorCode ierr;
718 PetscFunctionBeginUser;
719
720 if (!bc) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "BoundaryCondition is NULL");
721
722 InletParabolicData *data = NULL;
723 ierr = PetscMalloc1(1, &data); CHKERRQ(ierr);
724 bc->data = (void*)data;
725
731 bc->UpdateUbcs = NULL;
733
734 PetscFunctionReturn(0);
735}
static PetscErrorCode Initialize_InletParabolicProfile(BoundaryCondition *self, BCContext *ctx)
Initialize the geometric data used to evaluate a parabolic inlet profile.
static PetscErrorCode PostStep_InletParabolicProfile(BoundaryCondition *self, BCContext *ctx, PetscReal *in, PetscReal *out)
Perform post-step bookkeeping for a parabolic inlet boundary.
static PetscErrorCode PreStep_InletParabolicProfile(BoundaryCondition *self, BCContext *ctx, PetscReal *in, PetscReal *out)
Refresh parabolic-inlet values required before the solver step.
static PetscErrorCode Destroy_InletParabolicProfile(BoundaryCondition *self)
Release resources owned by a parabolic inlet boundary.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ Initialize_InletProfileFromFile()

static PetscErrorCode Initialize_InletProfileFromFile ( BoundaryCondition self,
BCContext ctx 
)
static

Initializes a file-prescribed inlet profile handler for one boundary face.

Reads the source_file BC parameter, validates the target face dimensions, loads the PICSLICE scalar speed profile, records summary statistics for logging, and applies the profile once to initialize boundary state.

Parameters
selfBoundaryCondition object configured by Create_InletProfileFromFile().
ctxRuntime boundary context containing the UserCtx and face id.
Returns
PetscErrorCode 0 on success, or a PETSc error for missing parameters or malformed files.

Definition at line 1333 of file BC_Handlers.c.

1334{
1335 PetscErrorCode ierr;
1336 UserCtx *user = ctx->user;
1337 BCFace face_id = ctx->face_id;
1339 PetscBool found = PETSC_FALSE;
1340 const char *source_file = NULL;
1341 PetscInt expected_n1 = 0, expected_n2 = 0;
1342
1343 PetscFunctionBeginUser;
1344 ierr = GetBCParamStringLocal(user->boundary_faces[face_id].params, "source_file",
1345 &source_file, &found); CHKERRQ(ierr);
1346 if (!found || !source_file || source_file[0] == '\0') {
1347 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG,
1348 "InletProfileFromFile requires source_file parameter for face %d.", face_id);
1349 }
1350
1351 ierr = PetscStrallocpy(source_file, &data->source_file); CHKERRQ(ierr);
1352 ierr = GetProfileFileExpectedDims(user, face_id, &expected_n1, &expected_n2); CHKERRQ(ierr);
1353 ierr = ReadPicSliceProfile(source_file, expected_n1, expected_n2, data); CHKERRQ(ierr);
1354
1356 " Inlet Face %d (Prescribed Flow): source=%s dims=(%d,%d) speed[min,max]=[%.6e, %.6e]\n",
1357 face_id, data->source_file, data->n1, data->n2,
1358 (double)data->min_speed, (double)data->max_speed);
1359
1360 ierr = Apply_InletProfileFromFile(self, ctx); CHKERRQ(ierr);
1361 PetscFunctionReturn(0);
1362}
static PetscErrorCode GetProfileFileExpectedDims(UserCtx *user, BCFace face_id, PetscInt *n1, PetscInt *n2)
Computes the expected PICSLICE dimensions for an inlet face.
static PetscErrorCode ReadPicSliceProfile(const char *source_file, PetscInt expected_n1, PetscInt expected_n2, InletProfileFileData *data)
Reads and validates a static scalar inlet profile from a canonical PICSLICE file.
static PetscErrorCode Apply_InletProfileFromFile(BoundaryCondition *self, BCContext *ctx)
Applies the loaded PICSLICE scalar profile to Ucont and Ubcs on an inlet face.
static PetscErrorCode GetBCParamStringLocal(BC_Param *params, const char *key, const char **value_out, PetscBool *found)
Looks up a string-valued boundary-condition parameter in a BC_Param list.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PreStep_InletProfileFromFile()

static PetscErrorCode PreStep_InletProfileFromFile ( BoundaryCondition self,
BCContext ctx,
PetscReal *  local_inflow_contribution,
PetscReal *  local_outflow_contribution 
)
static

Pre-step hook for the static file-prescribed inlet profile handler.

Static profiles require no per-step preparation. The hook is implemented so future time-varying profile support can reuse the same handler lifecycle.

Parameters
selfBoundaryCondition object for this inlet handler.
ctxRuntime boundary context.
local_inflow_contributionInflow accumulator, intentionally unchanged.
local_outflow_contributionOutflow accumulator, intentionally unchanged.
Returns
PetscErrorCode 0 on success.

Definition at line 1378 of file BC_Handlers.c.

1381{
1382 (void)self;
1383 (void)ctx;
1384 (void)local_inflow_contribution;
1385 (void)local_outflow_contribution;
1386 PetscFunctionBeginUser;
1387 PetscFunctionReturn(0);
1388}
Here is the caller graph for this function:

◆ Apply_InletProfileFromFile()

static PetscErrorCode Apply_InletProfileFromFile ( BoundaryCondition self,
BCContext ctx 
)
static

Applies the loaded PICSLICE scalar profile to Ucont and Ubcs on an inlet face.

Each stored scalar is treated as a positive normal speed magnitude. The routine uses the same negative/positive face sign convention, immersed-boundary skip checks, metric vectors, and CellArea conversion used by the constant and parabolic inlet handlers.

Parameters
selfBoundaryCondition object with InletProfileFileData storage.
ctxRuntime boundary context containing arrays and face id.
Returns
PetscErrorCode 0 on success, or a PETSc error from DMDA array access.

Definition at line 1404 of file BC_Handlers.c.

1405{
1406 PetscErrorCode ierr;
1407 UserCtx *user = ctx->user;
1408 BCFace face_id = ctx->face_id;
1410 PetscBool can_service;
1411
1412 PetscFunctionBeginUser;
1413 DMDALocalInfo *info = &user->info;
1414 Cmpnts ***ubcs, ***ucont, ***csi, ***eta, ***zet;
1415 PetscReal ***nvert;
1416
1417 ierr = CanRankServiceFace(info, user->IM, user->JM, user->KM, face_id, &can_service); CHKERRQ(ierr);
1418 if (!can_service) PetscFunctionReturn(0);
1419
1420 ierr = DMDAVecGetArray(user->fda, user->Bcs.Ubcs, &ubcs); CHKERRQ(ierr);
1421 ierr = DMDAVecGetArray(user->fda, user->Ucont, &ucont); CHKERRQ(ierr);
1422 ierr = DMDAVecGetArrayRead(user->fda, user->lCsi, (const Cmpnts***)&csi); CHKERRQ(ierr);
1423 ierr = DMDAVecGetArrayRead(user->fda, user->lEta, (const Cmpnts***)&eta); CHKERRQ(ierr);
1424 ierr = DMDAVecGetArrayRead(user->fda, user->lZet, (const Cmpnts***)&zet); CHKERRQ(ierr);
1425 ierr = DMDAVecGetArrayRead(user->da, user->lNvert, (const PetscReal***)&nvert); CHKERRQ(ierr);
1426
1427 PetscInt xs = info->xs, xe = info->xs + info->xm;
1428 PetscInt ys = info->ys, ye = info->ys + info->ym;
1429 PetscInt zs = info->zs, ze = info->zs + info->zm;
1430 PetscInt mx = info->mx, my = info->my, mz = info->mz;
1431
1432 PetscInt lxs = xs, lxe = xe, lys = ys, lye = ye, lzs = zs, lze = ze;
1433 if (xs == 0) lxs = xs + 1;
1434 if (xe == mx) lxe = xe - 1;
1435 if (ys == 0) lys = ys + 1;
1436 if (ye == my) lye = ye - 1;
1437 if (zs == 0) lzs = zs + 1;
1438 if (ze == mz) lze = ze - 1;
1439
1440 switch (face_id) {
1441 case BC_FACE_NEG_X:
1442 case BC_FACE_POS_X: {
1443 PetscReal sign = (face_id == BC_FACE_NEG_X) ? 1.0 : -1.0;
1444 PetscInt i = (face_id == BC_FACE_NEG_X) ? xs : mx - 2;
1445 for (PetscInt k = lzs; k < lze; k++) {
1446 for (PetscInt j = lys; j < lye; j++) {
1447 if ((sign > 0 && nvert[k][j][i+1] > 0.1) ||
1448 (sign < 0 && nvert[k][j][i] > 0.1)) continue;
1449 PetscReal uin_local = ProfileSpeedAt(data, k - 1, j - 1);
1450 PetscReal CellArea = sqrt(csi[k][j][i].x * csi[k][j][i].x +
1451 csi[k][j][i].y * csi[k][j][i].y +
1452 csi[k][j][i].z * csi[k][j][i].z);
1453 ucont[k][j][i].x = sign * uin_local * CellArea;
1454 ubcs[k][j][i + (sign < 0)].x = sign * uin_local * csi[k][j][i].x / CellArea;
1455 ubcs[k][j][i + (sign < 0)].y = sign * uin_local * csi[k][j][i].y / CellArea;
1456 ubcs[k][j][i + (sign < 0)].z = sign * uin_local * csi[k][j][i].z / CellArea;
1457 }
1458 }
1459 } break;
1460 case BC_FACE_NEG_Y:
1461 case BC_FACE_POS_Y: {
1462 PetscReal sign = (face_id == BC_FACE_NEG_Y) ? 1.0 : -1.0;
1463 PetscInt j = (face_id == BC_FACE_NEG_Y) ? ys : my - 2;
1464 for (PetscInt k = lzs; k < lze; k++) {
1465 for (PetscInt i = lxs; i < lxe; i++) {
1466 if ((sign > 0 && nvert[k][j+1][i] > 0.1) ||
1467 (sign < 0 && nvert[k][j][i] > 0.1)) continue;
1468 PetscReal uin_local = ProfileSpeedAt(data, k - 1, i - 1);
1469 PetscReal CellArea = sqrt(eta[k][j][i].x * eta[k][j][i].x +
1470 eta[k][j][i].y * eta[k][j][i].y +
1471 eta[k][j][i].z * eta[k][j][i].z);
1472 ucont[k][j][i].y = sign * uin_local * CellArea;
1473 ubcs[k][j + (sign < 0)][i].x = sign * uin_local * eta[k][j][i].x / CellArea;
1474 ubcs[k][j + (sign < 0)][i].y = sign * uin_local * eta[k][j][i].y / CellArea;
1475 ubcs[k][j + (sign < 0)][i].z = sign * uin_local * eta[k][j][i].z / CellArea;
1476 }
1477 }
1478 } break;
1479 case BC_FACE_NEG_Z:
1480 case BC_FACE_POS_Z: {
1481 PetscReal sign = (face_id == BC_FACE_NEG_Z) ? 1.0 : -1.0;
1482 PetscInt k = (face_id == BC_FACE_NEG_Z) ? zs : mz - 2;
1483 for (PetscInt j = lys; j < lye; j++) {
1484 for (PetscInt i = lxs; i < lxe; i++) {
1485 if ((sign > 0 && nvert[k+1][j][i] > 0.1) ||
1486 (sign < 0 && nvert[k][j][i] > 0.1)) continue;
1487 PetscReal uin_local = ProfileSpeedAt(data, j - 1, i - 1);
1488 PetscReal CellArea = sqrt(zet[k][j][i].x * zet[k][j][i].x +
1489 zet[k][j][i].y * zet[k][j][i].y +
1490 zet[k][j][i].z * zet[k][j][i].z);
1491 ucont[k][j][i].z = sign * uin_local * CellArea;
1492 ubcs[k + (sign < 0)][j][i].x = sign * uin_local * zet[k][j][i].x / CellArea;
1493 ubcs[k + (sign < 0)][j][i].y = sign * uin_local * zet[k][j][i].y / CellArea;
1494 ubcs[k + (sign < 0)][j][i].z = sign * uin_local * zet[k][j][i].z / CellArea;
1495 }
1496 }
1497 } break;
1498 }
1499
1500 ierr = DMDAVecRestoreArray(user->fda, user->Bcs.Ubcs, &ubcs); CHKERRQ(ierr);
1501 ierr = DMDAVecRestoreArray(user->fda, user->Ucont, &ucont); CHKERRQ(ierr);
1502 ierr = DMDAVecRestoreArrayRead(user->fda, user->lCsi, (const Cmpnts***)&csi); CHKERRQ(ierr);
1503 ierr = DMDAVecRestoreArrayRead(user->fda, user->lEta, (const Cmpnts***)&eta); CHKERRQ(ierr);
1504 ierr = DMDAVecRestoreArrayRead(user->fda, user->lZet, (const Cmpnts***)&zet); CHKERRQ(ierr);
1505 ierr = DMDAVecRestoreArrayRead(user->da, user->lNvert, (const PetscReal***)&nvert); CHKERRQ(ierr);
1506
1507 PetscFunctionReturn(0);
1508}
static PetscReal ProfileSpeedAt(const InletProfileFileData *data, PetscInt a, PetscInt b)
Returns one scalar speed from the flattened PICSLICE profile.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PostStep_InletProfileFromFile()

static PetscErrorCode PostStep_InletProfileFromFile ( BoundaryCondition self,
BCContext ctx,
PetscReal *  local_inflow_contribution,
PetscReal *  local_outflow_contribution 
)
static

Accumulates the applied inlet flux for a file-prescribed profile.

Sums the face-normal Ucont component over the same interior face slots populated by Apply_InletProfileFromFile().

Parameters
selfBoundaryCondition object for this inlet handler.
ctxRuntime boundary context containing the UserCtx and face id.
local_inflow_contributionAccumulator incremented by the measured inlet flux.
local_outflow_contributionOutflow accumulator, intentionally unchanged.
Returns
PetscErrorCode 0 on success, or a PETSc error from DMDA array access.

Definition at line 1524 of file BC_Handlers.c.

1527{
1528 PetscErrorCode ierr;
1529 UserCtx *user = ctx->user;
1530 BCFace face_id = ctx->face_id;
1531 PetscBool can_service;
1532
1533 (void)self;
1534 (void)local_outflow_contribution;
1535
1536 PetscFunctionBeginUser;
1537 DMDALocalInfo *info = &user->info;
1538 Cmpnts ***ucont;
1539
1540 ierr = CanRankServiceFace(info, user->IM, user->JM, user->KM, face_id, &can_service); CHKERRQ(ierr);
1541 if (!can_service) PetscFunctionReturn(0);
1542
1543 ierr = DMDAVecGetArrayRead(user->fda, user->Ucont, (const Cmpnts***)&ucont); CHKERRQ(ierr);
1544 PetscReal local_flux = 0.0;
1545
1546 PetscInt xs = info->xs, xe = info->xs + info->xm;
1547 PetscInt ys = info->ys, ye = info->ys + info->ym;
1548 PetscInt zs = info->zs, ze = info->zs + info->zm;
1549 PetscInt mx = info->mx, my = info->my, mz = info->mz;
1550
1551 PetscInt lxs = xs, lxe = xe, lys = ys, lye = ye, lzs = zs, lze = ze;
1552 if (xs == 0) lxs = xs + 1;
1553 if (xe == mx) lxe = xe - 1;
1554 if (ys == 0) lys = ys + 1;
1555 if (ye == my) lye = ye - 1;
1556 if (zs == 0) lzs = zs + 1;
1557 if (ze == mz) lze = ze - 1;
1558
1559 switch (face_id) {
1560 case BC_FACE_NEG_X:
1561 case BC_FACE_POS_X: {
1562 PetscInt i = (face_id == BC_FACE_NEG_X) ? xs : mx - 2;
1563 for (PetscInt k = lzs; k < lze; k++)
1564 for (PetscInt j = lys; j < lye; j++)
1565 local_flux += ucont[k][j][i].x;
1566 } break;
1567 case BC_FACE_NEG_Y:
1568 case BC_FACE_POS_Y: {
1569 PetscInt j = (face_id == BC_FACE_NEG_Y) ? ys : my - 2;
1570 for (PetscInt k = lzs; k < lze; k++)
1571 for (PetscInt i = lxs; i < lxe; i++)
1572 local_flux += ucont[k][j][i].y;
1573 } break;
1574 case BC_FACE_NEG_Z:
1575 case BC_FACE_POS_Z: {
1576 PetscInt k = (face_id == BC_FACE_NEG_Z) ? zs : mz - 2;
1577 for (PetscInt j = lys; j < lye; j++)
1578 for (PetscInt i = lxs; i < lxe; i++)
1579 local_flux += ucont[k][j][i].z;
1580 } break;
1581 }
1582
1583 ierr = DMDAVecRestoreArrayRead(user->fda, user->Ucont, (const Cmpnts***)&ucont); CHKERRQ(ierr);
1584 *local_inflow_contribution += local_flux;
1585
1586 LOG_ALLOW(LOCAL, LOG_DEBUG, "PostStep_InletProfileFromFile: Face %d, flux = %.6e\n",
1587 face_id, local_flux);
1588
1589 PetscFunctionReturn(0);
1590}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ Destroy_InletProfileFromFile()

static PetscErrorCode Destroy_InletProfileFromFile ( BoundaryCondition self)
static

Releases private storage owned by a file-prescribed inlet profile handler.

Parameters
selfBoundaryCondition object whose data field stores InletProfileFileData.
Returns
PetscErrorCode 0 on success.

Definition at line 1600 of file BC_Handlers.c.

1601{
1602 PetscFunctionBeginUser;
1603 if (self && self->data) {
1605 PetscFree(data->profile);
1606 PetscFree(data->source_file);
1607 PetscFree(self->data);
1608 self->data = NULL;
1609 }
1610 PetscFunctionReturn(0);
1611}
Here is the caller graph for this function:

◆ GetBCParamStringLocal()

static PetscErrorCode GetBCParamStringLocal ( BC_Param params,
const char *  key,
const char **  value_out,
PetscBool *  found 
)
static

Looks up a string-valued boundary-condition parameter in a BC_Param list.

Parameters
paramsHead of the boundary-condition parameter linked list.
keyCase-insensitive key to search for.
[out]value_outBorrowed pointer to the matching value string, or NULL if absent.
[out]foundPETSC_TRUE when the key is present, PETSC_FALSE otherwise.
Returns
PetscErrorCode 0 on success.

Definition at line 1124 of file BC_Handlers.c.

1126{
1127 PetscFunctionBeginUser;
1128 *found = PETSC_FALSE;
1129 *value_out = NULL;
1130 for (BC_Param *param = params; param; param = param->next) {
1131 if (strcasecmp(param->key, key) == 0) {
1132 *value_out = param->value;
1133 *found = PETSC_TRUE;
1134 PetscFunctionReturn(0);
1135 }
1136 }
1137 PetscFunctionReturn(0);
1138}
struct BC_Param_s * next
Definition variables.h:339
A node in a linked list for storing key-value parameters from the bcs.dat file.
Definition variables.h:336
Here is the caller graph for this function:

◆ GetProfileFileExpectedDims()

static PetscErrorCode GetProfileFileExpectedDims ( UserCtx user,
BCFace  face_id,
PetscInt *  n1,
PetscInt *  n2 
)
static

Computes the expected PICSLICE dimensions for an inlet face.

Parameters
userUser context containing global grid node counts.
face_idBoundary face whose tangential profile dimensions are requested.
[out]n1First PICSLICE dimension in handler storage order.
[out]n2Second PICSLICE dimension in handler storage order.
Returns
PetscErrorCode 0 on success, or a PETSc error for unsupported faces or invalid dimensions.

Definition at line 1149 of file BC_Handlers.c.

1151{
1152 PetscFunctionBeginUser;
1153 switch (face_id) {
1154 case BC_FACE_NEG_X:
1155 case BC_FACE_POS_X:
1156 *n1 = user->KM - 1;
1157 *n2 = user->JM - 1;
1158 break;
1159 case BC_FACE_NEG_Y:
1160 case BC_FACE_POS_Y:
1161 *n1 = user->KM - 1;
1162 *n2 = user->IM - 1;
1163 break;
1164 case BC_FACE_NEG_Z:
1165 case BC_FACE_POS_Z:
1166 *n1 = user->JM - 1;
1167 *n2 = user->IM - 1;
1168 break;
1169 default:
1170 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE,
1171 "Unsupported face id %d for inlet profile dimensions.", face_id);
1172 }
1173 if (*n1 <= 0 || *n2 <= 0) {
1174 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG,
1175 "Invalid inlet profile dimensions (%d, %d) for grid (%d, %d, %d).",
1176 *n1, *n2, user->IM, user->JM, user->KM);
1177 }
1178 PetscFunctionReturn(0);
1179}
Here is the caller graph for this function:

◆ ReadPicSliceProfile()

static PetscErrorCode ReadPicSliceProfile ( const char *  source_file,
PetscInt  expected_n1,
PetscInt  expected_n2,
InletProfileFileData data 
)
static

Reads and validates a static scalar inlet profile from a canonical PICSLICE file.

The file must contain magic token PICSLICE, frame count 1, the expected two-dimensional face shape, and exactly one finite nonnegative scalar speed value per interior face slot. Values are stored row-major in data->profile.

Parameters
source_filePath to the PICSLICE profile file.
expected_n1Expected first slice dimension.
expected_n2Expected second slice dimension.
[in,out]dataHandler-private storage that receives dimensions, profile values, and min/max speeds.
Returns
PetscErrorCode 0 on success, or a PETSc file/validation error on malformed input.

Definition at line 1194 of file BC_Handlers.c.

1196{
1197 PetscErrorCode ierr;
1198 FILE *fd = NULL;
1199 char magic[32] = {0};
1200 PetscInt frame_count = 0, n1 = 0, n2 = 0;
1201
1202 PetscFunctionBeginUser;
1203 fd = fopen(source_file, "r");
1204 if (!fd) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
1205 "Cannot open PICSLICE inlet profile file: %s", source_file);
1206
1207 if (fscanf(fd, "%31s", magic) != 1 || strcmp(magic, "PICSLICE") != 0) {
1208 fclose(fd);
1209 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_READ,
1210 "PICSLICE inlet profile file %s must begin with PICSLICE header.", source_file);
1211 }
1212 if (fscanf(fd, "%d", &frame_count) != 1) {
1213 fclose(fd);
1214 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_READ,
1215 "PICSLICE inlet profile file %s missing frame count.", source_file);
1216 }
1217 if (frame_count != 1) {
1218 fclose(fd);
1219 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_UNEXPECTED,
1220 "PICSLICE inlet profile file %s has %d frames; static handler requires 1.",
1221 source_file, frame_count);
1222 }
1223 if (fscanf(fd, "%d %d", &n1, &n2) != 2) {
1224 fclose(fd);
1225 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_READ,
1226 "PICSLICE inlet profile file %s missing slice dimensions.", source_file);
1227 }
1228 if (n1 != expected_n1 || n2 != expected_n2) {
1229 fclose(fd);
1230 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_UNEXPECTED,
1231 "PICSLICE inlet profile dimensions mismatch for %s: expected (%d, %d), found (%d, %d).",
1232 source_file, expected_n1, expected_n2, n1, n2);
1233 }
1234
1235 data->n1 = n1;
1236 data->n2 = n2;
1237 ierr = PetscMalloc1(n1 * n2, &data->profile); CHKERRQ(ierr);
1238 data->min_speed = PETSC_MAX_REAL;
1239 data->max_speed = -PETSC_MAX_REAL;
1240
1241 for (PetscInt idx = 0; idx < n1 * n2; idx++) {
1242 PetscReal value = 0.0;
1243 if (fscanf(fd, "%le", &value) != 1) {
1244 fclose(fd);
1245 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_READ,
1246 "PICSLICE inlet profile file %s ended early: expected %d values.",
1247 source_file, n1 * n2);
1248 }
1249 if (PetscIsInfOrNanReal(value) || value < 0.0) {
1250 fclose(fd);
1251 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_UNEXPECTED,
1252 "PICSLICE inlet profile file %s contains invalid speed %.6e at flat index %d.",
1253 source_file, (double)value, idx);
1254 }
1255 data->profile[idx] = value;
1256 data->min_speed = PetscMin(data->min_speed, value);
1257 data->max_speed = PetscMax(data->max_speed, value);
1258 }
1259
1260 char extra[64];
1261 if (fscanf(fd, "%63s", extra) == 1) {
1262 fclose(fd);
1263 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_UNEXPECTED,
1264 "PICSLICE inlet profile file %s has extra token after %d values: %s",
1265 source_file, n1 * n2, extra);
1266 }
1267 fclose(fd);
1268 PetscFunctionReturn(0);
1269}
Here is the caller graph for this function:

◆ ProfileSpeedAt()

static PetscReal ProfileSpeedAt ( const InletProfileFileData data,
PetscInt  a,
PetscInt  b 
)
inlinestatic

Returns one scalar speed from the flattened PICSLICE profile.

Parameters
dataHandler-private profile storage.
aFirst profile index in face-specific storage order.
bSecond profile index in face-specific storage order.
Returns
Scalar normal speed at (a, b).

Definition at line 1279 of file BC_Handlers.c.

1280{
1281 return data->profile[a * data->n2 + b];
1282}
Here is the caller graph for this function:

◆ Create_InletProfileFromFile()

PetscErrorCode Create_InletProfileFromFile ( BoundaryCondition bc)

Implementation of Create_InletProfileFromFile().

Configures a BoundaryCondition object for a file-prescribed inlet profile.

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

See also
Create_InletProfileFromFile()

Definition at line 1292 of file BC_Handlers.c.

1293{
1294 PetscErrorCode ierr;
1295 PetscFunctionBeginUser;
1296
1297 if (!bc) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "BoundaryCondition is NULL");
1298
1299 InletProfileFileData *data = NULL;
1300 ierr = PetscMalloc1(1, &data); CHKERRQ(ierr);
1301 data->n1 = 0;
1302 data->n2 = 0;
1303 data->profile = NULL;
1304 data->min_speed = 0.0;
1305 data->max_speed = 0.0;
1306 data->source_file = NULL;
1307 bc->data = (void*)data;
1308
1314 bc->UpdateUbcs = NULL;
1316
1317 PetscFunctionReturn(0);
1318}
static PetscErrorCode Initialize_InletProfileFromFile(BoundaryCondition *self, BCContext *ctx)
Initializes a file-prescribed inlet profile handler for one boundary face.
static PetscErrorCode Destroy_InletProfileFromFile(BoundaryCondition *self)
Releases private storage owned by a file-prescribed inlet profile handler.
static PetscErrorCode PreStep_InletProfileFromFile(BoundaryCondition *self, BCContext *ctx, PetscReal *in, PetscReal *out)
Pre-step hook for the static file-prescribed inlet profile handler.
static PetscErrorCode PostStep_InletProfileFromFile(BoundaryCondition *self, BCContext *ctx, PetscReal *in, PetscReal *out)
Accumulates the applied inlet flux for a file-prescribed profile.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PreStep_OutletConservation()

static PetscErrorCode PreStep_OutletConservation ( BoundaryCondition self,
BCContext ctx,
PetscReal *  local_inflow_contribution,
PetscReal *  local_outflow_contribution 
)
static

Prepare the outlet-conservation correction before advancing the solver.

Definition at line 1672 of file BC_Handlers.c.

1674{
1675 PetscErrorCode ierr;
1676 UserCtx* user = ctx->user;
1677 BCFace face_id = ctx->face_id;
1678 DMDALocalInfo* info = &user->info;
1679 PetscBool can_service;
1680
1681 // Suppress unused parameter warnings for clarity.
1682 (void)self;
1683 (void)local_inflow_contribution;
1684
1685 PetscFunctionBeginUser;
1686
1687 // Step 1: Use the robust utility function to determine if this MPI rank owns a computable
1688 // portion of the specified boundary face. If not, there is no work to do, so we exit immediately.
1689 const PetscInt IM_nodes_global = user->IM;
1690 const PetscInt JM_nodes_global = user->JM;
1691 const PetscInt KM_nodes_global = user->KM;
1692 ierr = CanRankServiceFace(info, IM_nodes_global, JM_nodes_global, KM_nodes_global, face_id, &can_service); CHKERRQ(ierr);
1693
1694 if (!can_service) {
1695 PetscFunctionReturn(0);
1696 }
1697
1698 // Step 2: Get read-only access to the necessary PETSc arrays.
1699 // We use the local versions (`lUcat`, `lNvert`) which include ghost cell data,
1700 // ensuring we have the correct interior values adjacent to the boundary.
1701 Cmpnts ***ucat, ***csi, ***eta, ***zet;
1702 PetscReal ***nvert;
1703 ierr = DMDAVecGetArrayRead(user->fda, user->lUcat, (const Cmpnts***)&ucat); CHKERRQ(ierr);
1704 ierr = DMDAVecGetArrayRead(user->da, user->lNvert, (const PetscReal***)&nvert); CHKERRQ(ierr);
1705 ierr = DMDAVecGetArrayRead(user->fda, user->lCsi, (const Cmpnts***)&csi); CHKERRQ(ierr);
1706 ierr = DMDAVecGetArrayRead(user->fda, user->lEta, (const Cmpnts***)&eta); CHKERRQ(ierr);
1707 ierr = DMDAVecGetArrayRead(user->fda, user->lZet, (const Cmpnts***)&zet); CHKERRQ(ierr);
1708
1709 PetscReal local_flux_out = 0.0;
1710 const PetscInt xs=info->xs, xe=info->xs+info->xm;
1711 const PetscInt ys=info->ys, ye=info->ys+info->ym;
1712 const PetscInt zs=info->zs, ze=info->zs+info->zm;
1713 const PetscInt mx=info->mx, my=info->my, mz=info->mz;
1714
1715 // Step 3: Replicate the legacy shrunk loop bounds to exclude corners and edges.
1716 PetscInt lxs = xs; if (xs == 0) lxs = xs + 1;
1717 PetscInt lxe = xe; if (xe == mx) lxe = xe - 1;
1718 PetscInt lys = ys; if (ys == 0) lys = ys + 1;
1719 PetscInt lye = ye; if (ye == my) lye = ye - 1;
1720 PetscInt lzs = zs; if (zs == 0) lzs = zs + 1;
1721 PetscInt lze = ze; if (ze == mz) lze = ze - 1;
1722
1723 // Step 4: Loop over the specified face using the corrected bounds and indexing to calculate flux.
1724 switch (face_id) {
1725 case BC_FACE_NEG_X: {
1726 const PetscInt i_cell = xs + 1; // Index for first interior cell-centered data
1727 const PetscInt i_face = xs; // Index for the -X face of that cell
1728 for (int k=lzs; k<lze; k++) for (int j=lys; j<lye; j++) {
1729 if (nvert[k][j][i_cell] < 0.1) {
1730 local_flux_out += (ucat[k][j][i_cell].x * csi[k][j][i_face].x + ucat[k][j][i_cell].y * csi[k][j][i_face].y + ucat[k][j][i_cell].z * csi[k][j][i_face].z);
1731 }
1732 }
1733 break;
1734 }
1735 case BC_FACE_POS_X: {
1736 const PetscInt i_cell = xe - 2; // Index for last interior cell-centered data
1737 const PetscInt i_face = xe - 2; // Index for the +X face of that cell
1738 for (int k=lzs; k<lze; k++) for (int j=lys; j<lye; j++) {
1739 if (nvert[k][j][i_cell] < 0.1) {
1740 local_flux_out += (ucat[k][j][i_cell].x * csi[k][j][i_face].x + ucat[k][j][i_cell].y * csi[k][j][i_face].y + ucat[k][j][i_cell].z * csi[k][j][i_face].z);
1741 }
1742 }
1743 break;
1744 }
1745 case BC_FACE_NEG_Y: {
1746 const PetscInt j_cell = ys + 1;
1747 const PetscInt j_face = ys;
1748 for (int k=lzs; k<lze; k++) for (int i=lxs; i<lxe; i++) {
1749 if (nvert[k][j_cell][i] < 0.1) {
1750 local_flux_out += (ucat[k][j_cell][i].x * eta[k][j_face][i].x + ucat[k][j_cell][i].y * eta[k][j_face][i].y + ucat[k][j_cell][i].z * eta[k][j_face][i].z);
1751 }
1752 }
1753 break;
1754 }
1755 case BC_FACE_POS_Y: {
1756 const PetscInt j_cell = ye - 2;
1757 const PetscInt j_face = ye - 2;
1758 for (int k=lzs; k<lze; k++) for (int i=lxs; i<lxe; i++) {
1759 if (nvert[k][j_cell][i] < 0.1) {
1760 local_flux_out += (ucat[k][j_cell][i].x * eta[k][j_face][i].x + ucat[k][j_cell][i].y * eta[k][j_face][i].y + ucat[k][j_cell][i].z * eta[k][j_face][i].z);
1761 }
1762 }
1763 break;
1764 }
1765 case BC_FACE_NEG_Z: {
1766 const PetscInt k_cell = zs + 1;
1767 const PetscInt k_face = zs;
1768 for (int j=lys; j<lye; j++) for (int i=lxs; i<lxe; i++) {
1769 if (nvert[k_cell][j][i] < 0.1) {
1770 local_flux_out += (ucat[k_cell][j][i].x * zet[k_face][j][i].x + ucat[k_cell][j][i].y * zet[k_face][j][i].y + ucat[k_cell][j][i].z * zet[k_face][j][i].z);
1771 }
1772 }
1773 break;
1774 }
1775 case BC_FACE_POS_Z: {
1776 const PetscInt k_cell = ze - 2;
1777 const PetscInt k_face = ze - 2;
1778 for (int j=lys; j<lye; j++) for (int i=lxs; i<lxe; i++) {
1779 if (nvert[k_cell][j][i] < 0.1) {
1780 local_flux_out += (ucat[k_cell][j][i].x * zet[k_face][j][i].x + ucat[k_cell][j][i].y * zet[k_face][j][i].y + ucat[k_cell][j][i].z * zet[k_face][j][i].z);
1781 }
1782 }
1783 break;
1784 }
1785 }
1786
1787 // Step 5: Restore the PETSc arrays.
1788 ierr = DMDAVecRestoreArrayRead(user->fda, user->lUcat, (const Cmpnts***)&ucat); CHKERRQ(ierr);
1789 ierr = DMDAVecRestoreArrayRead(user->da, user->lNvert, (const PetscReal***)&nvert); CHKERRQ(ierr);
1790 ierr = DMDAVecRestoreArrayRead(user->fda, user->lCsi, (const Cmpnts***)&csi); CHKERRQ(ierr);
1791 ierr = DMDAVecRestoreArrayRead(user->fda, user->lEta, (const Cmpnts***)&eta); CHKERRQ(ierr);
1792 ierr = DMDAVecRestoreArrayRead(user->fda, user->lZet, (const Cmpnts***)&zet); CHKERRQ(ierr);
1793
1794 // Step 6: Add this face's calculated flux to the accumulator for this rank.
1795 *local_outflow_contribution += local_flux_out;
1796
1797 PetscFunctionReturn(0);
1798}
Vec lNvert
Definition variables.h:939
Vec lZet
Definition variables.h:974
Vec lCsi
Definition variables.h:974
Vec lUcat
Definition variables.h:939
Vec lEta
Definition variables.h:974
Here is the call graph for this function:
Here is the caller graph for this function:

◆ Apply_OutletConservation()

static PetscErrorCode Apply_OutletConservation ( BoundaryCondition self,
BCContext ctx 
)
static

(Handler Action) Applies mass conservation correction to the outlet face.

This function calculates a global correction factor based on the total inflow and outflow fluxes and applies it to the contravariant velocity (ucont) on the outlet face to ensure mass conservation.

Definition at line 1808 of file BC_Handlers.c.

1809{
1810 PetscErrorCode ierr;
1811 (void)self;
1812 UserCtx* user = ctx->user;
1813 BCFace face_id = ctx->face_id;
1814 DMDALocalInfo* info = &user->info;
1815 PetscBool can_service;
1816
1817 PetscFunctionBeginUser;
1819
1820 const PetscInt IM_nodes_global = user->IM;
1821 const PetscInt JM_nodes_global = user->JM;
1822 const PetscInt KM_nodes_global = user->KM;
1823 ierr = CanRankServiceFace(info, IM_nodes_global, JM_nodes_global, KM_nodes_global, face_id, &can_service); CHKERRQ(ierr);
1824
1825 if (!can_service) {
1827 PetscFunctionReturn(0);
1828 }
1829
1830 // --- STEP 1: Calculate the correction factor using pre-calculated area ---
1831 PetscReal total_inflow = *ctx->global_inflow_sum + *ctx->global_farfield_inflow_sum;
1832 PetscReal flux_imbalance = total_inflow - *ctx->global_outflow_sum;
1833
1834 // Directly use the pre-calculated area from the simulation context.
1835 PetscReal velocity_correction = (PetscAbsReal(user->simCtx->AreaOutSum) > 1e-12)
1836 ? flux_imbalance / user->simCtx->AreaOutSum
1837 : 0.0;
1838
1839 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Outlet Correction on Face %d: Imbalance=%.4e, Pre-calc Area=%.4e, V_corr=%.4e\n",
1840 face_id, flux_imbalance, user->simCtx->AreaOutSum, velocity_correction);
1841
1842 // --- STEP 2: Apply the correction to ucont on the outlet face ---
1843
1844 // Get read/write access to necessary arrays
1845
1846 Cmpnts ***ubcs, ***ucont, ***csi, ***eta, ***zet, ***ucat;
1847 PetscReal ***nvert;
1848 ierr = DMDAVecGetArray(user->fda, user->Bcs.Ubcs, &ubcs); CHKERRQ(ierr);
1849 ierr = DMDAVecGetArray(user->fda, user->Ucont, &ucont); CHKERRQ(ierr);
1850 ierr = DMDAVecGetArrayRead(user->fda,user->lUcat, (const Cmpnts***)&ucat); CHKERRQ(ierr);
1851 ierr = DMDAVecGetArrayRead(user->fda, user->lCsi, (const Cmpnts***)&csi); CHKERRQ(ierr);
1852 ierr = DMDAVecGetArrayRead(user->fda, user->lEta, (const Cmpnts***)&eta); CHKERRQ(ierr);
1853 ierr = DMDAVecGetArrayRead(user->fda, user->lZet, (const Cmpnts***)&zet); CHKERRQ(ierr);
1854 ierr = DMDAVecGetArrayRead(user->da, user->lNvert, (const PetscReal***)&nvert); CHKERRQ(ierr);
1855
1856 // Get local grid bounds to exclude corners/edges
1857 PetscInt xs = info->xs, xe = info->xs + info->xm;
1858 PetscInt ys = info->ys, ye = info->ys + info->ym;
1859 PetscInt zs = info->zs, ze = info->zs + info->zm;
1860 PetscInt mx = info->mx, my = info->my, mz = info->mz;
1861 PetscInt lxs = xs, lxe = xe, lys = ys, lye = ye, lzs = zs, lze = ze;
1862
1863 if (xs == 0) lxs = xs + 1;
1864 if (xe == mx) lxe = xe - 1;
1865 if (ys == 0) lys = ys + 1;
1866 if (ye == my) lye = ye - 1;
1867 if (zs == 0) lzs = zs + 1;
1868 if (ze == mz) lze = ze - 1;
1869
1870 // Loop over faces and apply correction
1871 switch(face_id){
1872 case BC_FACE_NEG_X:{
1873 const PetscInt i_cell = xs + 1;
1874 const PetscInt i_face = xs;
1875 const PetscInt i_dummy = xs;
1876 for (PetscInt k = lzs; k < lze; k++) {
1877 for (PetscInt j = lys; j < lye; j++) {
1878 if (nvert[k][j][i_cell] < 0.1) {
1879 // Set ubcs
1880 ubcs[k][j][i_dummy] = ucat[k][j][i_cell];
1881
1882 // Calculate Local uncorrected original flux
1883 PetscReal Uncorrected_local_flux = (ubcs[k][j][i_dummy].x * csi[k][j][i_face].x) + (ubcs[k][j][i_dummy].y * csi[k][j][i_face].y) + (ubcs[k][j][i_dummy].z * csi[k][j][i_face].z);
1884
1885 PetscReal Cell_Area = sqrt((csi[k][j][i_face].x*csi[k][j][i_face].x) + (csi[k][j][i_face].y*csi[k][j][i_face].y) + (csi[k][j][i_face].z*csi[k][j][i_face].z));
1886
1887 PetscReal Correction_flux = velocity_correction*Cell_Area;
1888
1889 ucont[k][j][i_face].x = Uncorrected_local_flux + Correction_flux;
1890 }
1891 }
1892 }
1893 break;
1894 }
1895 case BC_FACE_POS_X:{
1896 const PetscInt i_cell = xe - 2;
1897 const PetscInt i_face = xe - 2;
1898 const PetscInt i_dummy = xe - 1;
1899 for(PetscInt k = lzs; k < lze; k++) for (PetscInt j = lys; j < lye; j++){
1900 if(nvert[k][j][i_cell]<0.1){
1901 // Set ubcs
1902 ubcs[k][j][i_dummy] = ucat[k][j][i_cell];
1903
1904 // Calculate Local uncorrected original flux
1905 PetscReal Uncorrected_local_flux = (ubcs[k][j][i_dummy].x * csi[k][j][i_face].x) + (ubcs[k][j][i_dummy].y * csi[k][j][i_face].y) + (ubcs[k][j][i_dummy].z * csi[k][j][i_face].z);
1906
1907 PetscReal Cell_Area = sqrt((csi[k][j][i_face].x*csi[k][j][i_face].x) + (csi[k][j][i_face].y*csi[k][j][i_face].y) + (csi[k][j][i_face].z*csi[k][j][i_face].z));
1908
1909 PetscReal Correction_flux = velocity_correction*Cell_Area;
1910
1911 ucont[k][j][i_face].x = Uncorrected_local_flux + Correction_flux;
1912 }
1913 }
1914 break;
1915 }
1916 case BC_FACE_NEG_Y:{
1917 const PetscInt j_cell = ys + 1;
1918 const PetscInt j_face = ys;
1919 const PetscInt j_dummy = ys;
1920 for(PetscInt k = lzs; k < lze; k++) for (PetscInt i = lxs; i < lxe; i++){
1921 if(nvert[k][j_cell][i]<0.1){
1922 // Set ubcs
1923 ubcs[k][j_dummy][i] = ucat[k][j_cell][i];
1924
1925 // Calculate Local uncorrected original flux
1926 PetscReal Uncorrected_local_flux = (ubcs[k][j_dummy][i].x*eta[k][j_face][i].x) + (ubcs[k][j_dummy][i].y*eta[k][j_face][i].y) + (ubcs[k][j_dummy][i].z*eta[k][j_face][i].z);
1927
1928 PetscReal Cell_Area = sqrt((eta[k][j_face][i].x*eta[k][j_face][i].x)+(eta[k][j_face][i].y*eta[k][j_face][i].y)+(eta[k][j_face][i].z*eta[k][j_face][i].z));
1929
1930 PetscReal Correction_flux = velocity_correction*Cell_Area;
1931
1932 ucont[k][j_face][i].y = Uncorrected_local_flux + Correction_flux;
1933 }
1934 }
1935 break;
1936 }
1937 case BC_FACE_POS_Y:{
1938 const PetscInt j_cell = ye - 2;
1939 const PetscInt j_face = ye - 2;
1940 const PetscInt j_dummy = ye - 1;
1941 for(PetscInt k = lzs; k < lze; k++) for (PetscInt i = lxs; i < lxe; i++){
1942 if(nvert[k][j_cell][i]<0.1){
1943 // Set ubcs
1944 ubcs[k][j_dummy][i] = ucat[k][j_cell][i];
1945
1946 // Calculate Local uncorrected original flux
1947 PetscReal Uncorrected_local_flux = (ubcs[k][j_dummy][i].x*eta[k][j_face][i].x) + (ubcs[k][j_dummy][i].y*eta[k][j_face][i].y) + (ubcs[k][j_dummy][i].z*eta[k][j_face][i].z);
1948
1949 PetscReal Cell_Area = sqrt((eta[k][j_face][i].x*eta[k][j_face][i].x)+(eta[k][j_face][i].y*eta[k][j_face][i].y)+(eta[k][j_face][i].z*eta[k][j_face][i].z));
1950
1951 PetscReal Correction_flux = velocity_correction*Cell_Area;
1952
1953 ucont[k][j_face][i].y = Uncorrected_local_flux + Correction_flux;
1954 }
1955 }
1956 break;
1957 }
1958 case BC_FACE_NEG_Z:{
1959 const PetscInt k_cell = zs + 1;
1960 const PetscInt k_face = zs;
1961 const PetscInt k_dummy = zs;
1962 for(PetscInt j = lys; j < lye; j++) for (PetscInt i = lxs; i < lxe; i++){
1963 if(nvert[k_cell][j][i]<0.1){
1964 // Set ubcs
1965 ubcs[k_dummy][j][i] = ucat[k_cell][j][i];
1966
1967 // Calculate Local uncorrected original flux
1968 PetscReal Uncorrected_local_flux = ((ubcs[k_dummy][j][i].x*zet[k_face][j][i].x) + (ubcs[k_dummy][j][i].y*zet[k_face][j][i].y) + (ubcs[k_dummy][j][i].z*zet[k_face][j][i].z));
1969
1970 PetscReal Cell_Area = sqrt((zet[k_face][j][i].x*zet[k_face][j][i].x)+(zet[k_face][j][i].y*zet[k_face][j][i].y)+(zet[k_face][j][i].z*zet[k_face][j][i].z));
1971
1972 PetscReal Correction_flux = velocity_correction*Cell_Area;
1973
1974 ucont[k_face][j][i].z = Uncorrected_local_flux + Correction_flux;
1975 }
1976 }
1977 break;
1978 }
1979 case BC_FACE_POS_Z:{
1980 const PetscInt k_cell = ze - 2;
1981 const PetscInt k_face = ze - 2;
1982 const PetscInt k_dummy = ze - 1;
1983 for(PetscInt j = lys; j < lye; j++) for (PetscInt i = lxs; i < lxe; i++){
1984 if(nvert[k_cell][j][i]<0.1){
1985 // Set ubcs
1986 ubcs[k_dummy][j][i] = ucat[k_cell][j][i];
1987
1988 // Calculate Local uncorrected original flux
1989 PetscReal Uncorrected_local_flux = ((ubcs[k_dummy][j][i].x*zet[k_face][j][i].x) + (ubcs[k_dummy][j][i].y*zet[k_face][j][i].y) + (ubcs[k_dummy][j][i].z*zet[k_face][j][i].z));
1990
1991 PetscReal Cell_Area = sqrt((zet[k_face][j][i].x*zet[k_face][j][i].x)+(zet[k_face][j][i].y*zet[k_face][j][i].y)+(zet[k_face][j][i].z*zet[k_face][j][i].z));
1992
1993 PetscReal Correction_flux = velocity_correction*Cell_Area;
1994
1995 ucont[k_face][j][i].z = Uncorrected_local_flux + Correction_flux;
1996 }
1997 }
1998 break;
1999 }
2000 }
2001
2002 // Restore all arrays
2003 ierr = DMDAVecRestoreArray(user->fda, user->Bcs.Ubcs, &ubcs); CHKERRQ(ierr);
2004 ierr = DMDAVecRestoreArray(user->fda, user->Ucont, &ucont); CHKERRQ(ierr);
2005 ierr = DMDAVecRestoreArrayRead(user->fda,user->lUcat, (const Cmpnts***)&ucat); CHKERRQ(ierr);
2006 ierr = DMDAVecRestoreArrayRead(user->fda, user->lCsi, (const Cmpnts***)&csi); CHKERRQ(ierr);
2007 ierr = DMDAVecRestoreArrayRead(user->fda, user->lEta, (const Cmpnts***)&eta); CHKERRQ(ierr);
2008 ierr = DMDAVecRestoreArrayRead(user->fda, user->lZet, (const Cmpnts***)&zet); CHKERRQ(ierr);
2009 ierr = DMDAVecRestoreArrayRead(user->da, user->lNvert, (const PetscReal***)&nvert); CHKERRQ(ierr);
2010
2012 PetscFunctionReturn(0);
2013}
#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
const PetscReal * global_outflow_sum
Definition variables.h:349
SimCtx * simCtx
Back-pointer to the master simulation context.
Definition variables.h:909
const PetscReal * global_inflow_sum
Definition variables.h:346
const PetscReal * global_farfield_inflow_sum
Definition variables.h:347
PetscReal AreaOutSum
Definition variables.h:815
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PostStep_OutletConservation()

static PetscErrorCode PostStep_OutletConservation ( BoundaryCondition self,
BCContext ctx,
PetscReal *  in,
PetscReal *  out 
)
static

Update outlet-conservation state after a completed solver step.

Definition at line 2020 of file BC_Handlers.c.

2023{
2024 PetscErrorCode ierr;
2025 UserCtx* user = ctx->user;
2026 BCFace face_id = ctx->face_id;
2027 DMDALocalInfo* info = &user->info;
2028 PetscBool can_service;
2029
2030 (void)self;
2031 (void)local_inflow_contribution;
2032
2033 PetscFunctionBeginUser;
2034 const PetscInt IM_nodes_global = user->IM;
2035 const PetscInt JM_nodes_global = user->JM;
2036 const PetscInt KM_nodes_global = user->KM;
2037 ierr = CanRankServiceFace(info, IM_nodes_global, JM_nodes_global, KM_nodes_global, face_id, &can_service); CHKERRQ(ierr);
2038
2039 if (!can_service) PetscFunctionReturn(0);
2040
2041 // Get arrays (need both ucont and nvert)
2042 Cmpnts ***ucont;
2043 PetscReal ***nvert; // ✅ ADD nvert
2044
2045 ierr = DMDAVecGetArrayRead(user->fda, user->Ucont, (const Cmpnts***)&ucont); CHKERRQ(ierr);
2046 ierr = DMDAVecGetArrayRead(user->da, user->lNvert, (const PetscReal***)&nvert); CHKERRQ(ierr); // ✅ ADD
2047
2048 PetscReal local_flux = 0.0;
2049
2050 PetscInt xs = info->xs, xe = info->xs + info->xm;
2051 PetscInt ys = info->ys, ye = info->ys + info->ym;
2052 PetscInt zs = info->zs, ze = info->zs + info->zm;
2053 PetscInt mx = info->mx, my = info->my, mz = info->mz;
2054
2055 PetscInt lxs = xs, lxe = xe, lys = ys, lye = ye, lzs = zs, lze = ze;
2056 if (xs == 0) lxs = xs + 1;
2057 if (xe == mx) lxe = xe - 1;
2058 if (ys == 0) lys = ys + 1;
2059 if (ye == my) lye = ye - 1;
2060 if (zs == 0) lzs = zs + 1;
2061 if (ze == mz) lze = ze - 1;
2062
2063 // Sum ucont components, skipping solid cells (same indices as PreStep)
2064 switch (face_id) {
2065 case BC_FACE_NEG_X: {
2066 const PetscInt i_cell = xs + 1; // ✅ Match PreStep
2067 const PetscInt i_face = xs;
2068 for (PetscInt k = lzs; k < lze; k++) {
2069 for (PetscInt j = lys; j < lye; j++) {
2070 if (nvert[k][j][i_cell] < 0.1) { // ✅ Skip solid cells
2071 local_flux += ucont[k][j][i_face].x;
2072 }
2073 }
2074 }
2075 } break;
2076
2077 case BC_FACE_POS_X: {
2078 const PetscInt i_cell = xe - 2; // ✅ Match PreStep
2079 const PetscInt i_face = xe - 2;
2080 for (PetscInt k = lzs; k < lze; k++) {
2081 for (PetscInt j = lys; j < lye; j++) {
2082 if (nvert[k][j][i_cell] < 0.1) { // ✅ Skip solid cells
2083 local_flux += ucont[k][j][i_face].x;
2084 }
2085 }
2086 }
2087 } break;
2088
2089 case BC_FACE_NEG_Y: {
2090 const PetscInt j_cell = ys + 1; // ✅ Match PreStep
2091 const PetscInt j_face = ys;
2092 for (PetscInt k = lzs; k < lze; k++) {
2093 for (PetscInt i = lxs; i < lxe; i++) {
2094 if (nvert[k][j_cell][i] < 0.1) { // ✅ Skip solid cells
2095 local_flux += ucont[k][j_face][i].y;
2096 }
2097 }
2098 }
2099 } break;
2100
2101 case BC_FACE_POS_Y: {
2102 const PetscInt j_cell = ye - 2; // ✅ Match PreStep
2103 const PetscInt j_face = ye - 2;
2104 for (PetscInt k = lzs; k < lze; k++) {
2105 for (PetscInt i = lxs; i < lxe; i++) {
2106 if (nvert[k][j_cell][i] < 0.1) { // ✅ Skip solid cells
2107 local_flux += ucont[k][j_face][i].y;
2108 }
2109 }
2110 }
2111 } break;
2112
2113 case BC_FACE_NEG_Z: {
2114 const PetscInt k_cell = zs + 1; // ✅ Match PreStep
2115 const PetscInt k_face = zs;
2116 for (PetscInt j = lys; j < lye; j++) {
2117 for (PetscInt i = lxs; i < lxe; i++) {
2118 if (nvert[k_cell][j][i] < 0.1) { // ✅ Skip solid cells
2119 local_flux += ucont[k_face][j][i].z;
2120 }
2121 }
2122 }
2123 } break;
2124
2125 case BC_FACE_POS_Z: {
2126 const PetscInt k_cell = ze - 2; // ✅ Match PreStep
2127 const PetscInt k_face = ze - 2;
2128 for (PetscInt j = lys; j < lye; j++) {
2129 for (PetscInt i = lxs; i < lxe; i++) {
2130 if (nvert[k_cell][j][i] < 0.1) { // ✅ Skip solid cells
2131 local_flux += ucont[k_face][j][i].z;
2132 }
2133 }
2134 }
2135 } break;
2136 }
2137
2138 // Restore arrays
2139 ierr = DMDAVecRestoreArrayRead(user->fda, user->Ucont, (const Cmpnts***)&ucont); CHKERRQ(ierr);
2140 ierr = DMDAVecRestoreArrayRead(user->da, user->lNvert, (const PetscReal***)&nvert); CHKERRQ(ierr); // ✅ ADD
2141
2142 // Add to accumulator
2143 *local_outflow_contribution += local_flux;
2144
2145 LOG_ALLOW(LOCAL, LOG_DEBUG, "PostStep_OutletConservation: Face %d, corrected flux = %.6e\n",
2146 face_id, local_flux);
2147
2148 PetscFunctionReturn(0);
2149}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ Create_OutletConservation()

PetscErrorCode Create_OutletConservation ( BoundaryCondition bc)

Implementation of Create_OutletConservation().

Configures a BoundaryCondition object for conservative outlet treatment.

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

See also
Create_OutletConservation()

Definition at line 1644 of file BC_Handlers.c.

1645{
1646 PetscFunctionBeginUser;
1647
1648 if (!bc) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Input BoundaryCondition is NULL");
1649
1650 // This handler has the highest priority to ensure it runs after
1651 // all inflow fluxes have been calculated.
1653
1654 // Assign function pointers
1655 bc->Initialize = NULL; // No initialization needed
1659 bc->UpdateUbcs = NULL;
1660 bc->Destroy = NULL; // No private data to destroy
1661
1662 bc->data = NULL;
1663
1664 PetscFunctionReturn(0);
1665}
static PetscErrorCode Apply_OutletConservation(BoundaryCondition *self, BCContext *ctx)
(Handler Action) Applies mass conservation correction to the outlet face.
static PetscErrorCode PostStep_OutletConservation(BoundaryCondition *self, BCContext *ctx, PetscReal *in, PetscReal *out)
Update outlet-conservation state after a completed solver step.
static PetscErrorCode PreStep_OutletConservation(BoundaryCondition *self, BCContext *ctx, PetscReal *local_inflow_contribution, PetscReal *local_outflow_contribution)
Prepare the outlet-conservation correction before advancing the solver.
@ BC_PRIORITY_OUTLET
Definition variables.h:328
Here is the call graph for this function:
Here is the caller graph for this function:

◆ Create_PeriodicGeometric()

PetscErrorCode Create_PeriodicGeometric ( BoundaryCondition bc)

Implementation of Create_PeriodicGeometric().

Configures a BoundaryCondition object for geometric periodic coupling.

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

See also
Create_PeriodicGeometric()

Definition at line 2159 of file BC_Handlers.c.

2159 {
2160 PetscFunctionBeginUser;
2161
2162 if (!bc) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Input BoundaryCondition is NULL");
2164
2165 // Assign function pointers
2166 bc->Initialize = NULL; // No initialization needed
2167 bc->PreStep = NULL;
2168 bc->Apply = NULL;
2169 bc->PostStep = NULL;
2170 bc->UpdateUbcs = NULL;
2171 bc->Destroy = NULL; // No private data to destroy
2172
2173 bc->data = NULL;
2174
2175 PetscFunctionReturn(0);
2176}
Here is the caller graph for this function:

◆ MeasureDrivenFluxes()

static PetscErrorCode MeasureDrivenFluxes ( UserCtx user,
char  direction,
PetscReal *  boundaryFlux,
PetscReal *  planarAverageFlux 
)
static

Measure the two volumetric fluxes the driven-flow controller senses.

Both periodic driven handlers steer on the same pair of measurements, so they are taken here in a single sweep of lUcont:

  • *boundaryFlux is the flux through the single periodic boundary plane. It is fast and responsive but noisy, and drives the boundary trim.
  • *planarAverageFlux is the flux averaged over every cross-sectional plane in the driven direction. It is stable and inertial, and drives the momentum source. It is also the quantity initial_flux latches at t=0.
Parameters
[in]userBlock context supplying lUcont, lNvert and info.
[in]directionDriven direction, 'X', 'Y' or 'Z'.
[out]boundaryFluxGlobally reduced flux through the boundary plane.
[out]planarAverageFluxGlobally reduced plane-averaged flux.
Returns
PetscErrorCode 0 on success.

Definition at line 2199 of file BC_Handlers.c.

2202{
2203 PetscErrorCode ierr;
2204 DMDALocalInfo info = user->info;
2205 PetscInt i, j, k;
2206
2207 PetscFunctionBeginUser;
2208
2209 // --- Get read-only access to necessary field data ---
2210 Cmpnts ***ucont;
2211 PetscReal ***nvert;
2212 ierr = DMDAVecGetArrayRead(user->fda, user->lUcont, (const Cmpnts***)&ucont); CHKERRQ(ierr);
2213 ierr = DMDAVecGetArrayRead(user->da, user->lNvert, (const PetscReal***)&nvert); CHKERRQ(ierr);
2214
2215 // --- Define local loop bounds ---
2216 PetscInt lxs = (info.xs == 0) ? 1 : info.xs;
2217 PetscInt lys = (info.ys == 0) ? 1 : info.ys;
2218 PetscInt lzs = (info.zs == 0) ? 1 : info.zs;
2219 PetscInt lxe = (info.xs + info.xm == info.mx) ? info.mx - 1 : info.xs + info.xm;
2220 PetscInt lye = (info.ys + info.ym == info.my) ? info.my - 1 : info.ys + info.ym;
2221 PetscInt lze = (info.zs + info.zm == info.mz) ? info.mz - 1 : info.zs + info.zm;
2222
2223 // --- Initialize local accumulators ---
2224 PetscReal localCurrentBoundaryFlux = 0.0;
2225 PetscReal localAveragePlanarVolumetricFluxTerm = 0.0;
2226
2227 // --- Measure local contributions to the two flux types, generalized by direction ---
2228 switch (direction) {
2229 case 'X':
2230 if (info.xs == 0) { // Only the rank on the negative face contributes to boundary flux
2231 i = 0;
2232 for (k = lzs; k < lze; k++) for (j = lys; j < lye; j++) {
2233 if (nvert[k][j][i + 1] < 0.1) localCurrentBoundaryFlux += ucont[k][j][i].x;
2234 }
2235 }
2236 for (i = info.xs; i < lxe; i++) {
2237 for (k = lzs; k < lze; k++) for (j = lys; j < lye; j++) {
2238 if (nvert[k][j][i + 1] < 0.1) localAveragePlanarVolumetricFluxTerm += ucont[k][j][i].x / (PetscReal)(info.mx - 1);
2239 }
2240 }
2241 break;
2242 case 'Y':
2243 if (info.ys == 0) {
2244 j = 0;
2245 for (k = lzs; k < lze; k++) for (i = lxs; i < lxe; i++) {
2246 if (nvert[k][j + 1][i] < 0.1) localCurrentBoundaryFlux += ucont[k][j][i].y;
2247 }
2248 }
2249 for (j = info.ys; j < lye; j++) {
2250 for (k = lzs; k < lze; k++) for (i = lxs; i < lxe; i++) {
2251 if (nvert[k][j + 1][i] < 0.1) localAveragePlanarVolumetricFluxTerm += ucont[k][j][i].y / (PetscReal)(info.my - 1);
2252 }
2253 }
2254 break;
2255 case 'Z':
2256 if (info.zs == 0) {
2257 k = 0;
2258 for (j = lys; j < lye; j++) for (i = lxs; i < lxe; i++) {
2259 if (nvert[k + 1][j][i] < 0.1) localCurrentBoundaryFlux += ucont[k][j][i].z;
2260 }
2261 }
2262 for (k = info.zs; k < lze; k++) {
2263 for (j = lys; j < lye; j++) for (i = lxs; i < lxe; i++) {
2264 if (nvert[k + 1][j][i] < 0.1) localAveragePlanarVolumetricFluxTerm += ucont[k][j][i].z / (PetscReal)(info.mz - 1);
2265 }
2266 }
2267 break;
2268 default:
2269 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG,
2270 "MeasureDrivenFluxes received an unknown driven direction '%c'.", direction);
2271 }
2272
2273 // --- Release array access as soon as possible ---
2274 ierr = DMDAVecRestoreArrayRead(user->fda, user->lUcont, (const Cmpnts***)&ucont); CHKERRQ(ierr);
2275 ierr = DMDAVecRestoreArrayRead(user->da, user->lNvert, (const PetscReal***)&nvert); CHKERRQ(ierr);
2276
2277 // --- Perform global reductions to get the final flux values ---
2278 ierr = MPI_Allreduce(&localCurrentBoundaryFlux, boundaryFlux, 1, MPI_DOUBLE, MPI_SUM, PETSC_COMM_WORLD); CHKERRQ(ierr);
2279 ierr = MPI_Allreduce(&localAveragePlanarVolumetricFluxTerm, planarAverageFlux, 1, MPI_DOUBLE, MPI_SUM, PETSC_COMM_WORLD); CHKERRQ(ierr);
2280
2281 PetscFunctionReturn(0);
2282}
Vec lUcont
Definition variables.h:939
Here is the caller graph for this function:

◆ Initialize_PeriodicDrivenConstant()

static PetscErrorCode Initialize_PeriodicDrivenConstant ( BoundaryCondition self,
BCContext ctx 
)
static

Initialize constant forcing data for a periodically driven boundary.

Definition at line 2368 of file BC_Handlers.c.

2369{
2370 PetscErrorCode ierr;
2371 DrivenFluxData *data = (DrivenFluxData*)self->data;
2372 BCFace face_id = ctx->face_id;
2373 UserCtx* user = ctx->user;
2374
2375 PetscFunctionBeginUser;
2376
2377 LOG_ALLOW(LOCAL, LOG_DEBUG, "Initializing PERIODIC_DRIVEN_CONSTANT_FLUX handler on Face %s...\n", BCFaceToString(face_id));
2378
2379 // --- 1. Validation: Ensure the mathematical type is PERIODIC ---
2380 if (user->boundary_faces[face_id].mathematical_type != PERIODIC) {
2381 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_USER_INPUT,
2382 "Configuration Error: Handler PERIODIC_DRIVEN_CONSTANT_FLUX on Face %s must be applied to a face with mathematical_type PERIODIC.",
2383 BCFaceToString(face_id));
2384 }
2385
2386 // --- 2. Role Assignment: Determine direction and master status ---
2387 data->isMasterController = PETSC_FALSE;
2388 switch (face_id) {
2389 case BC_FACE_NEG_X: data->direction = 'X'; data->isMasterController = PETSC_TRUE; break;
2390 case BC_FACE_POS_X: data->direction = 'X'; break;
2391 case BC_FACE_NEG_Y: data->direction = 'Y'; data->isMasterController = PETSC_TRUE; break;
2392 case BC_FACE_POS_Y: data->direction = 'Y'; break;
2393 case BC_FACE_NEG_Z: data->direction = 'Z'; data->isMasterController = PETSC_TRUE; break;
2394 case BC_FACE_POS_Z: data->direction = 'Z'; break;
2395 }
2396
2397 // --- 3. Parameter Parsing (Master Controller only) ---
2398 if (data->isMasterController) {
2399 PetscBool found;
2400
2401 // Attempt to read the 'target_flux' parameter from the bcs.run file.
2402 ierr = GetBCParamReal(user->boundary_faces[face_id].params, "target_flux",
2403 &data->targetVolumetricFlux, &found); CHKERRQ(ierr);
2404
2405 // If the required parameter is not found, halt with an informative error.
2406 if (!found) {
2407 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_USER_INPUT,
2408 "Configuration Error: Handler PERIODIC_DRIVEN_CONSTANT_FLUX on Face %s requires a 'target_flux' parameter in the bcs file (e.g., target_flux=10.0).",
2409 BCFaceToString(face_id));
2410 }
2411
2412 LOG_ALLOW(GLOBAL, LOG_INFO, "Driven Flow (Dir %c): Constant target volumetric flux set to %le.\n",
2413 data->direction, data->targetVolumetricFlux);
2414
2415 // Store the target flux in the UserCtx. This makes it globally accessible
2416 // to other parts of the solver, such as the `CorrectChannelFluxProfile` enforcer function.
2417 user->simCtx->targetVolumetricFlux = data->targetVolumetricFlux;
2418 // The target is fixed for the run from here on; see the flag's comment in
2419 // variables.h for how initial_flux uses the same latch.
2420 user->simCtx->drivenFluxTargetLatched = PETSC_TRUE;
2421 }
2422
2423 PetscBool trimfound;
2424 // Optional seam-flux enforcement; accepts the deprecated `apply_trim` spelling.
2425 ierr = GetDrivenSeamFluxFlag(user->boundary_faces[face_id].params,
2426 &data->enforceSeamFlux, &trimfound); CHKERRQ(ierr);
2427
2428 if(!trimfound) LOG_ALLOW(GLOBAL,LOG_DEBUG,"Seam-flux enforcement not specified, defaults to %s.\n",data->enforceSeamFlux? "True":"False");
2429
2430 PetscFunctionReturn(0);
2431}
Private data structure shared by both periodic driven-flux handlers.
PetscErrorCode GetDrivenSeamFluxFlag(BC_Param *params, PetscBool *value_out, PetscBool *found)
Read the driven-flow seam-flux flag, accepting its deprecated apply_trim spelling.
Definition io.c:814
PetscReal targetVolumetricFlux
Definition variables.h:807
PetscBool drivenFluxTargetLatched
Definition variables.h:812
BC_Param * params
Definition variables.h:370
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PreStep_PeriodicDrivenConstant()

static PetscErrorCode PreStep_PeriodicDrivenConstant ( BoundaryCondition self,
BCContext ctx,
PetscReal *  in,
PetscReal *  out 
)
static

Prepare constant periodic-driving data before the solver step.

Definition at line 2438 of file BC_Handlers.c.

2441{
2442 PetscErrorCode ierr;
2443 DrivenFluxData *data = (DrivenFluxData*)self->data;
2444 UserCtx* user = ctx->user;
2445 SimCtx* simCtx = user->simCtx;
2446
2447 PetscFunctionBeginUser;
2448
2449 // --- Master Check: Only the handler on the negative face performs calculations ---
2450 if (!data->isMasterController) {
2451 PetscFunctionReturn(0);
2452 }
2453
2454 // The controller senses two fluxes; see MeasureDrivenFluxes() for what each
2455 // one is for and why the controller needs both.
2456 char direction = data->direction;
2457 PetscReal globalCurrentBoundaryFlux, globalAveragePlanarVolumetricFlux;
2458 ierr = MeasureDrivenFluxes(user, direction, &globalCurrentBoundaryFlux,
2459 &globalAveragePlanarVolumetricFlux); CHKERRQ(ierr);
2460
2461 // --- Get cross-sectional area using the dedicated geometry function ---
2462 Cmpnts ignored_center;
2463 PetscReal globalBoundaryArea;
2464 BCFace neg_face_id = (direction == 'X') ? BC_FACE_NEG_X : (direction == 'Y') ? BC_FACE_NEG_Y : BC_FACE_NEG_Z;
2465 ierr = CalculateFaceCenterAndArea(user, neg_face_id, &ignored_center, &globalBoundaryArea); CHKERRQ(ierr);
2466
2467 // --- Calculate the two correction terms ---
2468 //
2469 // These are refreshed on DIFFERENT cadences, and the difference matters.
2470 //
2471 // ApplyBoundaryConditions() runs BoundarySystem_ExecuteStep() -- and hence
2472 // this PreStep -- three times per call, and it is itself called once per
2473 // Jameson RK stage under the Picard solver and once per residual evaluation
2474 // under Newton-Krylov. So "per PreStep" is emphatically not "per timestep".
2475 //
2476 // - bulkVelocityCorrection scales the momentum source in ComputeRHS. Both
2477 // momentum solvers are built on it being FROZEN across a timestep: the
2478 // Picard shadow-Jacobian estimate treats the body force as a constant
2479 // forcing with zero velocity Jacobian, and the Newton solve needs a
2480 // source that does not drift between residual evaluations. It is
2481 // therefore computed once per physical step, from the field at the start
2482 // of that step, and held.
2483 //
2484 // - boundaryVelocityCorrection is the tactical trim applied to the
2485 // boundary fluxes in Apply(). It is deliberately re-measured on every
2486 // pass: Apply() accumulates it into Ucont, and re-measuring is what makes
2487 // that accumulation self-limiting as the seam converges. Freezing it
2488 // would make repeated Apply() calls add the same trim over and over.
2489 if (globalBoundaryArea > 1.0e-12) {
2490 if (data->lastBulkCorrectionStep != simCtx->step) {
2491 simCtx->bulkVelocityCorrection = (data->targetVolumetricFlux - globalAveragePlanarVolumetricFlux) / globalBoundaryArea;
2492 data->lastBulkCorrectionStep = simCtx->step;
2493 }
2494 simCtx->boundaryVelocityCorrection = (data->targetVolumetricFlux - globalCurrentBoundaryFlux) / globalBoundaryArea;
2495 } else {
2496 simCtx->bulkVelocityCorrection = 0.0;
2497 simCtx->boundaryVelocityCorrection = 0.0;
2498 data->lastBulkCorrectionStep = simCtx->step;
2499 }
2500
2501 LOG_ALLOW(GLOBAL, LOG_INFO, "Driven Flow Controller Update (Dir %c):\n", data->direction);
2502 LOG_ALLOW(GLOBAL, LOG_INFO, " - Target Volumetric Flux: %.6e\n", data->targetVolumetricFlux);
2503 LOG_ALLOW(GLOBAL, LOG_INFO, " - Avg Planar Volumetric Flux (Stable): %.6e\n", globalAveragePlanarVolumetricFlux);
2504 LOG_ALLOW(GLOBAL, LOG_INFO, " - Boundary Flux (Fast): %.6e\n", globalCurrentBoundaryFlux);
2505 LOG_ALLOW(GLOBAL, LOG_INFO, " - Bulk Velocity Correction: %.6e (For Momentum Source)\n", simCtx->bulkVelocityCorrection);
2506 LOG_ALLOW(GLOBAL, LOG_INFO, " - Boundary Velocity Correction: %.6e (For Boundary Trim)\n", simCtx->boundaryVelocityCorrection);
2507
2508 // Suppress unused parameter warnings for this handler
2509 (void)local_inflow_contribution;
2510 (void)local_outflow_contribution;
2511
2512 PetscFunctionReturn(0);
2513}
static PetscErrorCode MeasureDrivenFluxes(UserCtx *user, char direction, PetscReal *boundaryFlux, PetscReal *planarAverageFlux)
Measure the two volumetric fluxes the driven-flow controller senses.
PetscErrorCode CalculateFaceCenterAndArea(UserCtx *user, BCFace face_id, Cmpnts *face_center, PetscReal *face_area)
Calculates the geometric center and total area of a specified boundary face.
Definition grid.c:1207
PetscReal boundaryVelocityCorrection
Definition variables.h:814
PetscReal bulkVelocityCorrection
Definition variables.h:813
PetscInt step
Definition variables.h:703
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:

◆ Apply_PeriodicDrivenConstant()

static PetscErrorCode Apply_PeriodicDrivenConstant ( BoundaryCondition self,
BCContext ctx 
)
static

Apply the configured constant driving term to the periodic boundary.

Definition at line 2520 of file BC_Handlers.c.

2521{
2522 PetscErrorCode ierr;
2523 DrivenFluxData *data = (DrivenFluxData*)self->data;
2524 UserCtx* user = ctx->user;
2525 BCFace face_id = ctx->face_id;
2526 PetscBool can_service;
2527
2528 PetscFunctionBeginUser;
2529
2530 // Check if this rank owns part of this boundary face
2531 ierr = CanRankServiceFace(&user->info, user->IM, user->JM, user->KM, face_id, &can_service); CHKERRQ(ierr);
2532 if (!can_service) {
2533 PetscFunctionReturn(0);
2534 }
2535
2536 // If the correction is negligible, no work is needed.
2537 if (PetscAbsReal(user->simCtx->boundaryVelocityCorrection) < 1e-12) {
2538 PetscFunctionReturn(0);
2539 }
2540
2541 LOG_ALLOW(LOCAL, LOG_TRACE, "Apply_PeriodicDrivenConstant: Applying boundary trim on Face %s...\n", BCFaceToString(face_id));
2542
2543 // --- Get read/write access to necessary arrays ---
2544 DMDALocalInfo info = user->info;
2545 Cmpnts ***ucont, ***uch, ***csi, ***eta, ***zet;
2546 PetscReal ***nvert;
2547
2548 ierr = DMDAVecGetArray(user->fda, user->Ucont, &ucont); CHKERRQ(ierr);
2549 ierr = DMDAVecGetArray(user->fda, user->Bcs.Uch, &uch); CHKERRQ(ierr);
2550 ierr = DMDAVecGetArrayRead(user->fda, user->lCsi, (const Cmpnts***)&csi); CHKERRQ(ierr);
2551 ierr = DMDAVecGetArrayRead(user->fda, user->lEta, (const Cmpnts***)&eta); CHKERRQ(ierr);
2552 ierr = DMDAVecGetArrayRead(user->fda, user->lZet, (const Cmpnts***)&zet); CHKERRQ(ierr);
2553 ierr = DMDAVecGetArrayRead(user->da, user->lNvert, (const PetscReal***)&nvert); CHKERRQ(ierr);
2554
2555 PetscInt lxs = (info.xs == 0) ? 1 : info.xs;
2556 PetscInt lys = (info.ys == 0) ? 1 : info.ys;
2557 PetscInt lzs = (info.zs == 0) ? 1 : info.zs;
2558 PetscInt lxe = (info.xs + info.xm == info.mx) ? info.mx - 1 : info.xs + info.xm;
2559 PetscInt lye = (info.ys + info.ym == info.my) ? info.my - 1 : info.ys + info.ym;
2560 PetscInt lze = (info.zs + info.zm == info.mz) ? info.mz - 1 : info.zs + info.zm;
2561
2562 // --- Apply correction to the appropriate face and velocity component ---
2563 switch (face_id) {
2564 case BC_FACE_NEG_X: case BC_FACE_POS_X: {
2565 PetscInt i_face = (face_id == BC_FACE_NEG_X) ? info.xs : info.mx - 2;
2566 PetscInt i_nvert = (face_id == BC_FACE_NEG_X) ? info.xs + 1 : info.mx - 2;
2567
2568 for (PetscInt k = lzs; k < lze; k++) for (PetscInt j = lys; j < lye; j++) {
2569 if (nvert[k][j][i_nvert] < 0.1) {
2570 PetscReal faceArea = sqrt(csi[k][j][i_face].x*csi[k][j][i_nvert].x + csi[k][j][i_nvert].y*csi[k][j][i_nvert].y + csi[k][j][i_face].z*csi[k][j][i_face].z);
2571 PetscReal fluxTrim = user->simCtx->boundaryVelocityCorrection * faceArea;
2572 if(data->enforceSeamFlux) ucont[k][j][i_face].x += fluxTrim;
2573 uch[k][j][i_face].x = fluxTrim; // Store correction for diagnostics
2574 }
2575 }
2576 } break;
2577
2578 case BC_FACE_NEG_Y: case BC_FACE_POS_Y: {
2579 PetscInt j_face = (face_id == BC_FACE_NEG_Y) ? info.ys : info.my - 2;
2580 PetscInt j_nvert = (face_id == BC_FACE_NEG_Y) ? info.ys + 1 : info.my - 2;
2581
2582 for (PetscInt k = lzs; k < lze; k++) for (PetscInt i = lxs; i < lxe; i++) {
2583 if (nvert[k][j_nvert][i] < 0.1) {
2584 PetscReal faceArea = sqrt(eta[k][j_face][i].x*eta[k][j_face][i].x + eta[k][j_face][i].y*eta[k][j_face][i].y + eta[k][j_face][i].z*eta[k][j_face][i].z);
2585 PetscReal fluxTrim = user->simCtx->boundaryVelocityCorrection * faceArea;
2586 if(data->enforceSeamFlux) ucont[k][j_face][i].y += fluxTrim;
2587 uch[k][j_face][i].y = fluxTrim;
2588 }
2589 }
2590 } break;
2591
2592 case BC_FACE_NEG_Z: case BC_FACE_POS_Z: {
2593 PetscInt k_face = (face_id == BC_FACE_NEG_Z) ? info.zs : info.mz - 2;
2594 PetscInt k_nvert = (face_id == BC_FACE_NEG_Z) ? info.zs + 1 : info.mz - 2;
2595
2596 for (PetscInt j = lys; j < lye; j++) for (PetscInt i = lxs; i < lxe; i++) {
2597 if (nvert[k_nvert][j][i] < 0.1) {
2598 PetscReal faceArea = sqrt(zet[k_nvert][j][i].x*zet[k_nvert][j][i].x + zet[k_nvert][j][i].y*zet[k_nvert][j][i].y + zet[k_nvert][j][i].z*zet[k_nvert][j][i].z);
2599 PetscReal fluxTrim = user->simCtx->boundaryVelocityCorrection * faceArea;
2600 if(data->enforceSeamFlux) ucont[k_face][j][i].z += fluxTrim;
2601 uch[k_face][j][i].z = fluxTrim;
2602 }
2603 }
2604 } break;
2605 }
2606
2607 // --- Restore arrays ---
2608 ierr = DMDAVecRestoreArray(user->fda, user->Ucont, &ucont); CHKERRQ(ierr);
2609 ierr = DMDAVecRestoreArray(user->fda, user->Bcs.Uch, &uch); CHKERRQ(ierr);
2610 ierr = DMDAVecRestoreArrayRead(user->fda, user->lCsi, (const Cmpnts***)&csi); CHKERRQ(ierr);
2611 ierr = DMDAVecRestoreArrayRead(user->fda, user->lEta, (const Cmpnts***)&eta); CHKERRQ(ierr);
2612 ierr = DMDAVecRestoreArrayRead(user->fda, user->lZet, (const Cmpnts***)&zet); CHKERRQ(ierr);
2613 ierr = DMDAVecRestoreArrayRead(user->da, user->lNvert, (const PetscReal***)&nvert); CHKERRQ(ierr);
2614
2615 PetscFunctionReturn(0);
2616}
@ LOG_TRACE
Very fine-grained tracing information for in-depth debugging.
Definition logging.h:33
Vec Uch
Characteristic velocity for boundary conditions.
Definition variables.h:124
Here is the call graph for this function:
Here is the caller graph for this function:

◆ Destroy_PeriodicDrivenConstant()

static PetscErrorCode Destroy_PeriodicDrivenConstant ( BoundaryCondition self)
static

Release resources owned by the constant periodic-driving boundary.

Definition at line 2623 of file BC_Handlers.c.

2624{
2625 PetscFunctionBeginUser;
2626
2627 // Check that the handler object and its private data pointer are valid before trying to free.
2628 if (self && self->data) {
2629 // The private data was allocated with PetscNew(), so it must be freed with PetscFree().
2630 PetscFree(self->data);
2631
2632 // It is good practice to nullify the pointer after freeing to prevent
2633 // any accidental use of the dangling pointer (use-after-free).
2634 self->data = NULL;
2635
2636 LOG_ALLOW(LOCAL, LOG_TRACE, "Destroy_PeriodicDrivenConstant: Private data freed successfully.\n");
2637 }
2638
2639 PetscFunctionReturn(0);
2640}
Here is the caller graph for this function:

◆ Create_PeriodicDrivenConstant()

PetscErrorCode Create_PeriodicDrivenConstant ( BoundaryCondition bc)

Internal helper implementation: Create_PeriodicDrivenConstant().

Configures a BoundaryCondition object for periodic driven-flow forcing.

Local to this translation unit.

Definition at line 2325 of file BC_Handlers.c.

2326{
2327 PetscErrorCode ierr;
2328 PetscFunctionBeginUser;
2329
2330 if (!bc) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Input BoundaryCondition object is NULL in Create_PeriodicDrivenConstantFlux");
2331
2332 // --- Allocate the private data structure ---
2333 DrivenFluxData *data = NULL;
2334 ierr = PetscNew(&data); CHKERRQ(ierr);
2335 // Initialize fields to safe default values
2336 data->direction = ' ';
2337 data->targetVolumetricFlux = 0.0;
2338 data->isMasterController = PETSC_FALSE;
2339 data->enforceSeamFlux = PETSC_FALSE;
2340 data->lastBulkCorrectionStep = -1;
2341
2342 // Attach the private data to the generic handler object
2343 bc->data = (void*)data;
2344
2345 // --- Configure the handler's properties and methods ---
2346
2347 // Set priority: Using BC_PRIORITY_INLET ensures this handler's PreStep runs
2348 // before other handlers (like outlets) that might depend on its calculations.
2349 // It is the caller's responsibility that there are no Inlets called along with driven periodic to avoid clash.
2351
2352 // Assign the function pointers to the implementations in this file.
2356 bc->PostStep = NULL; // This handler has no action after the main solver step.
2357 bc->UpdateUbcs = NULL; // The boundary value is not flow-dependent (it's periodic).
2359
2360 PetscFunctionReturn(0);
2361}
PetscBool enforceSeamFlux
PetscBool isMasterController
PetscInt lastBulkCorrectionStep
static PetscErrorCode Apply_PeriodicDrivenConstant(BoundaryCondition *self, BCContext *ctx)
Apply the configured constant driving term to the periodic boundary.
static PetscErrorCode Initialize_PeriodicDrivenConstant(BoundaryCondition *self, BCContext *ctx)
Initialize constant forcing data for a periodically driven boundary.
static PetscErrorCode Destroy_PeriodicDrivenConstant(BoundaryCondition *self)
Release resources owned by the constant periodic-driving boundary.
static PetscErrorCode PreStep_PeriodicDrivenConstant(BoundaryCondition *self, BCContext *ctx, PetscReal *in, PetscReal *out)
Prepare constant periodic-driving data before the solver step.
PetscReal targetVolumetricFlux
Here is the call graph for this function:
Here is the caller graph for this function:

◆ Initialize_PeriodicDrivenInitial()

static PetscErrorCode Initialize_PeriodicDrivenInitial ( BoundaryCondition self,
BCContext ctx 
)
static

Initialize forcing data for a periodic boundary driven to its initial flux.

Note
The target itself is NOT measured here. Boundary handlers are initialized before InitializeEulerianState() runs, so at this point Ucont still holds zeros. The measurement is deferred to the first PreStep, by which time either the initial condition has been applied or the restart target has been restored from the checkpoint manifest.

Definition at line 2716 of file BC_Handlers.c.

2717{
2718 PetscErrorCode ierr;
2719 DrivenFluxData *data = (DrivenFluxData*)self->data;
2720 BCFace face_id = ctx->face_id;
2721 UserCtx* user = ctx->user;
2722
2723 PetscFunctionBeginUser;
2724
2725 LOG_ALLOW(LOCAL, LOG_DEBUG, "Initializing PERIODIC_DRIVEN_INITIAL_FLUX handler on Face %s...\n", BCFaceToString(face_id));
2726
2727 // --- 1. Validation: Ensure the mathematical type is PERIODIC ---
2728 if (user->boundary_faces[face_id].mathematical_type != PERIODIC) {
2729 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_USER_INPUT,
2730 "Configuration Error: Handler PERIODIC_DRIVEN_INITIAL_FLUX on Face %s must be applied to a face with mathematical_type PERIODIC.",
2731 BCFaceToString(face_id));
2732 }
2733
2734 // --- 2. Role Assignment: Determine direction and master status ---
2735 data->isMasterController = PETSC_FALSE;
2736 switch (face_id) {
2737 case BC_FACE_NEG_X: data->direction = 'X'; data->isMasterController = PETSC_TRUE; break;
2738 case BC_FACE_POS_X: data->direction = 'X'; break;
2739 case BC_FACE_NEG_Y: data->direction = 'Y'; data->isMasterController = PETSC_TRUE; break;
2740 case BC_FACE_POS_Y: data->direction = 'Y'; break;
2741 case BC_FACE_NEG_Z: data->direction = 'Z'; data->isMasterController = PETSC_TRUE; break;
2742 case BC_FACE_POS_Z: data->direction = 'Z'; break;
2743 }
2744
2745 // --- 3. Parameter Parsing (Master Controller only) ---
2746 if (data->isMasterController) {
2747 PetscReal unused_flux;
2748 PetscBool found;
2749
2750 // This handler derives its own target, so an explicit one is a config error
2751 // rather than something to silently ignore. Users who want to prescribe the
2752 // flux should select the `constant_flux` handler instead.
2753 ierr = GetBCParamReal(user->boundary_faces[face_id].params, "target_flux",
2754 &unused_flux, &found); CHKERRQ(ierr);
2755 if (found) {
2756 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_USER_INPUT,
2757 "Configuration Error: Handler PERIODIC_DRIVEN_INITIAL_FLUX on Face %s takes no 'target_flux' parameter; "
2758 "it measures the flux of the initial condition and holds that. Use handler 'constant_flux' to prescribe a target.",
2759 BCFaceToString(face_id));
2760 }
2761
2762 LOG_ALLOW(GLOBAL, LOG_INFO, "Driven Flow (Dir %c): target volumetric flux will be latched from the initial state.\n",
2763 data->direction);
2764 }
2765
2766 PetscBool trimfound;
2767 // Optional seam-flux enforcement; accepts the deprecated `apply_trim` spelling.
2768 ierr = GetDrivenSeamFluxFlag(user->boundary_faces[face_id].params,
2769 &data->enforceSeamFlux, &trimfound); CHKERRQ(ierr);
2770
2771 if(!trimfound) LOG_ALLOW(GLOBAL,LOG_DEBUG,"Seam-flux enforcement not specified, defaults to %s.\n",data->enforceSeamFlux? "True":"False");
2772
2773 PetscFunctionReturn(0);
2774}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PreStep_PeriodicDrivenInitial()

static PetscErrorCode PreStep_PeriodicDrivenInitial ( BoundaryCondition self,
BCContext ctx,
PetscReal *  local_inflow_contribution,
PetscReal *  local_outflow_contribution 
)
static

Latch the initial-state flux once, then drive to it like a constant target.

The latch is one-shot and guarded by simCtx->drivenFluxTargetLatched:

  • Fresh start: the flag is false and Ucont now holds the initial condition, so the plane-averaged flux is measured and stored.
  • Restart: ReadSimulationFields() restored both the target and the flag from the checkpoint manifest, so the original target survives instead of being re-measured from a drifted field.

Definition at line 2788 of file BC_Handlers.c.

2791{
2792 PetscErrorCode ierr;
2793 DrivenFluxData *data = (DrivenFluxData*)self->data;
2794 SimCtx* simCtx = ctx->user->simCtx;
2795
2796 PetscFunctionBeginUser;
2797
2798 if (data->isMasterController) {
2799 if (!simCtx->drivenFluxTargetLatched) {
2800 PetscReal boundaryFlux, planarAverageFlux;
2801
2802 /* Boundary handlers are also exercised once during setup, from
2803 * FinalizeBlockState(), and at that point the initial condition has
2804 * been written to Ucont but not yet scattered into the ghosted
2805 * lUcont that MeasureDrivenFluxes() reads. Latching there would
2806 * record a target of zero. Setup runs with step == StartStep, so
2807 * wait for the first PreStep of the first real timestep: by then
2808 * lUcont holds the initial condition (or, on a restart, the state
2809 * the restored target already describes). Do no work at all until
2810 * then, so no bogus correction is derived from a zero target. */
2811 if (simCtx->step <= simCtx->StartStep) {
2812 simCtx->bulkVelocityCorrection = 0.0;
2813 simCtx->boundaryVelocityCorrection = 0.0;
2814 PetscFunctionReturn(0);
2815 }
2816
2817 ierr = MeasureDrivenFluxes(ctx->user, data->direction,
2818 &boundaryFlux, &planarAverageFlux); CHKERRQ(ierr);
2819
2820 simCtx->targetVolumetricFlux = planarAverageFlux;
2821 simCtx->drivenFluxTargetLatched = PETSC_TRUE;
2822
2824 "Driven Flow (Dir %c): latched initial volumetric flux %.6e as the target.\n",
2825 data->direction, planarAverageFlux);
2826 }
2827 // Keep the handler's copy in step with the authoritative value in SimCtx,
2828 // which is also where a restart deposits the restored target.
2829 data->targetVolumetricFlux = simCtx->targetVolumetricFlux;
2830 }
2831
2832 ierr = PreStep_PeriodicDrivenConstant(self, ctx,
2833 local_inflow_contribution,
2834 local_outflow_contribution); CHKERRQ(ierr);
2835
2836 PetscFunctionReturn(0);
2837}
PetscInt StartStep
Definition variables.h:705
Here is the call graph for this function:
Here is the caller graph for this function:

◆ Create_PeriodicDrivenInitial()

PetscErrorCode Create_PeriodicDrivenInitial ( BoundaryCondition bc)

Internal helper implementation: Create_PeriodicDrivenInitial().

Configures a BoundaryCondition object for initial-flux periodic driving.

Local to this translation unit.

Definition at line 2669 of file BC_Handlers.c.

2670{
2671 PetscErrorCode ierr;
2672 PetscFunctionBeginUser;
2673
2674 if (!bc) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Input BoundaryCondition object is NULL in Create_PeriodicDrivenInitial");
2675
2676 // --- Allocate the private data structure ---
2677 DrivenFluxData *data = NULL;
2678 ierr = PetscNew(&data); CHKERRQ(ierr);
2679 // Initialize fields to safe default values
2680 data->direction = ' ';
2681 data->targetVolumetricFlux = 0.0;
2682 data->isMasterController = PETSC_FALSE;
2683 data->enforceSeamFlux = PETSC_FALSE;
2684 data->lastBulkCorrectionStep = -1;
2685
2686 // Attach the private data to the generic handler object
2687 bc->data = (void*)data;
2688
2689 // --- Configure the handler's properties and methods ---
2690
2691 // Same priority reasoning as the constant-flux handler: PreStep must run
2692 // before any handler that depends on the controller's corrections.
2694
2697 bc->Apply = Apply_PeriodicDrivenConstant; // Boundary trim is target-agnostic.
2698 bc->PostStep = NULL; // This handler has no action after the main solver step.
2699 bc->UpdateUbcs = NULL; // The boundary value is not flow-dependent (it's periodic).
2700 bc->Destroy = Destroy_PeriodicDrivenConstant; // Same private data layout.
2701
2702 PetscFunctionReturn(0);
2703}
static PetscErrorCode Initialize_PeriodicDrivenInitial(BoundaryCondition *self, BCContext *ctx)
Initialize forcing data for a periodic boundary driven to its initial flux.
static PetscErrorCode PreStep_PeriodicDrivenInitial(BoundaryCondition *self, BCContext *ctx, PetscReal *in, PetscReal *out)
Latch the initial-state flux once, then drive to it like a constant target.
Here is the call graph for this function:
Here is the caller graph for this function: