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

Public interface for grid, solver, and metric setup routines. More...

#include "variables.h"
#include "logging.h"
#include "io.h"
#include "setup.h"
#include "AnalyticalSolutions.h"
Include dependency graph for grid.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

PetscErrorCode DefineAllGridDimensions (SimCtx *simCtx)
 Orchestrates the parsing and setting of grid dimensions for all blocks.
 
PetscErrorCode InitializeAllGridDMs (SimCtx *simCtx)
 Orchestrates the creation of DMDA objects for every block and multigrid level.
 
PetscErrorCode AssignAllGridCoordinates (SimCtx *simCtx)
 Orchestrates the assignment of physical coordinates to all DMDA objects.
 
PetscErrorCode ValidatePeriodicGeometry (UserCtx *user)
 Validates that configured geometric periodic seams match by translation.
 
PetscErrorCode ComputeLocalBoundingBox (UserCtx *user, BoundingBox *localBBox)
 Computes the local bounding box of the grid on the current process.
 
PetscErrorCode GatherAllBoundingBoxes (UserCtx *user, BoundingBox **allBBoxes)
 Gathers local bounding boxes from all MPI processes to rank 0.
 
PetscErrorCode BroadcastAllBoundingBoxes (UserCtx *user, BoundingBox **bboxlist)
 Broadcasts the bounding box information collected on rank 0 to all other ranks.
 
PetscErrorCode CalculateInletProperties (UserCtx *user)
 Calculates the center and area of the primary INLET face.
 
PetscErrorCode CalculateOutletProperties (UserCtx *user)
 Calculates the center and area of the primary OUTLET face.
 
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.
 

Detailed Description

Public interface for grid, solver, and metric setup routines.

Definition in file grid.h.

Function Documentation

◆ DefineAllGridDimensions()

PetscErrorCode DefineAllGridDimensions ( SimCtx simCtx)

Orchestrates the parsing and setting of grid dimensions for all blocks.

This function serves as the high-level entry point for defining the geometric properties of each grid block in the simulation. It iterates through every block defined by simCtx->block_number.

For each block, it performs two key actions:

  1. It explicitly sets the block's index (_this) in the corresponding UserCtx struct for the finest multigrid level. This makes the context "self-aware".
  2. It calls a helper function (ParseAndSetGridInputs) to handle the detailed work of parsing options or files to populate the rest of the geometric properties for that specific block (e.g., IM, Min_X, rx).
Parameters
simCtxThe master SimCtx, which contains the number of blocks and the UserCtx hierarchy to be configured.
Returns
PetscErrorCode 0 on success, or a PETSc error code on failure.

Orchestrates the parsing and setting of grid dimensions for all blocks.

Local to this translation unit.

Definition at line 57 of file grid.c.

58{
59 PetscErrorCode ierr;
60 PetscInt nblk = simCtx->block_number;
61 UserCtx *finest_users;
62
63 PetscFunctionBeginUser;
64
66
67 if (simCtx->usermg.mglevels == 0) {
68 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONGSTATE, "MG levels not set. Cannot get finest_users.");
69 }
70 // Get the UserCtx array for the finest grid level
71 finest_users = simCtx->usermg.mgctx[simCtx->usermg.mglevels - 1].user;
72
73 LOG_ALLOW(GLOBAL, LOG_INFO, "Defining grid dimensions for %d blocks...\n", nblk);
74 if (strcmp(simCtx->eulerianSource, "analytical") == 0 &&
77 "Analytical type '%s' requires custom geometry; preloading finest-grid IM/JM/KM once.\n",
79 ierr = PopulateFinestUserGridResolutionFromOptions(finest_users, nblk); CHKERRQ(ierr);
80 }
81
82 // Loop over each block to configure its grid dimensions and geometry.
83 for (PetscInt bi = 0; bi < nblk; bi++) {
84 LOG_ALLOW_SYNC(GLOBAL, LOG_DEBUG, "Rank %d: --- Configuring Geometry for Block %d ---\n", simCtx->rank, bi);
85
86 // Before calling any helpers, set the block index in the context.
87 // This makes the UserCtx self-aware of which block it represents.
88 LOG_ALLOW(GLOBAL,LOG_DEBUG,"finest_users->_this = %d, bi = %d\n",finest_users[bi]._this,bi);
89 //finest_user[bi]._this = bi;
90
91 // Call the helper function for this specific block. It can now derive
92 // all necessary information from the UserCtx pointer it receives.
93 ierr = ParseAndSetGridInputs(&finest_users[bi]); CHKERRQ(ierr);
94 }
95
97
98 PetscFunctionReturn(0);
99}
PetscBool AnalyticalTypeRequiresCustomGeometry(const char *analytical_type)
Reports whether an analytical type requires custom geometry/decomposition logic.
static PetscErrorCode ParseAndSetGridInputs(UserCtx *user)
Internal helper implementation: ParseAndSetGridInputs().
Definition grid.c:14
PetscErrorCode PopulateFinestUserGridResolutionFromOptions(UserCtx *finest_users, PetscInt nblk)
Parses grid resolution arrays (-im, -jm, -km) once and applies them to all finest-grid blocks.
Definition io.c:170
#define LOG_ALLOW_SYNC(scope, level, fmt,...)
Synchronized logging macro that checks both the log level and whether the calling function is in the ...
Definition logging.h:252
#define GLOBAL
Scope for global logging across all processes.
Definition logging.h:45
#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:199
#define PROFILE_FUNCTION_END
Marks the end of a profiled code block.
Definition logging.h:827
@ LOG_INFO
Informational messages about program execution.
Definition logging.h:30
@ LOG_DEBUG
Detailed debugging information.
Definition logging.h:31
#define PROFILE_FUNCTION_BEGIN
Marks the beginning of a profiled code block (typically a function).
Definition logging.h:818
UserCtx * user
Definition variables.h:569
PetscMPIInt rank
Definition variables.h:687
PetscInt block_number
Definition variables.h:768
UserMG usermg
Definition variables.h:821
char eulerianSource[PETSC_MAX_PATH_LEN]
Definition variables.h:704
PetscInt mglevels
Definition variables.h:576
char AnalyticalSolutionType[PETSC_MAX_PATH_LEN]
Definition variables.h:717
MGCtx * mgctx
Definition variables.h:579
User-defined context containing data specific to a single computational grid level.
Definition variables.h:876
Here is the call graph for this function:
Here is the caller graph for this function:

◆ InitializeAllGridDMs()

PetscErrorCode InitializeAllGridDMs ( SimCtx simCtx)

Orchestrates the creation of DMDA objects for every block and multigrid level.

This function systematically builds the entire DMDA hierarchy. It first calculates the dimensions (IM, JM, KM) for all coarse grids based on the finest grid's dimensions and the semi-coarsening flags. It then iterates from the coarsest to the finest level, calling a powerful helper function (InitializeSingleGridDM) to create the DMs for each block, ensuring that finer grids are properly aligned with their coarser parents for multigrid efficiency.

Parameters
simCtxThe master SimCtx, containing the configured UserCtx hierarchy.
Returns
PetscErrorCode 0 on success, or a PETSc error code on failure.

Orchestrates the creation of DMDA objects for every block and multigrid level.

Local to this translation unit.

Definition at line 235 of file grid.c.

236{
237 PetscErrorCode ierr;
238 UserMG *usermg = &simCtx->usermg;
239 MGCtx *mgctx = usermg->mgctx;
240 PetscInt nblk = simCtx->block_number;
241
242 PetscFunctionBeginUser;
243
245
246 LOG_ALLOW(GLOBAL,LOG_INFO, "Pre-scanning BCs to identify domain periodicity.\n");
247 ierr = DeterminePeriodicity(simCtx); CHKERRQ(ierr);
248
249 LOG_ALLOW(GLOBAL, LOG_INFO, "Creating DMDA objects for all levels and blocks...\n");
250
251 // --- Part 1: Calculate Coarse Grid Dimensions & VALIDATE ---
252 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Calculating and validating coarse grid dimensions...\n");
253 for (PetscInt level = usermg->mglevels - 2; level >= 0; level--) {
254 for (PetscInt bi = 0; bi < nblk; bi++) {
255 UserCtx *user_coarse = &mgctx[level].user[bi];
256 UserCtx *user_fine = &mgctx[level + 1].user[bi];
257
258 user_coarse->IM = user_fine->isc ? user_fine->IM : (user_fine->IM + 1) / 2;
259 user_coarse->JM = user_fine->jsc ? user_fine->JM : (user_fine->JM + 1) / 2;
260 user_coarse->KM = user_fine->ksc ? user_fine->KM : (user_fine->KM + 1) / 2;
261
262 LOG_ALLOW_SYNC(LOCAL, LOG_TRACE, "Rank %d: Block %d, Level %d dims calculated: %d x %d x %d\n",
263 simCtx->rank, bi, level, user_coarse->IM, user_coarse->JM, user_coarse->KM);
264
265 // Validation check from legacy MGDACreate to ensure coarsening is possible
266 PetscInt check_i = user_coarse->IM * (2 - user_coarse->isc) - (user_fine->IM + 1 - user_coarse->isc);
267 PetscInt check_j = user_coarse->JM * (2 - user_coarse->jsc) - (user_fine->JM + 1 - user_coarse->jsc);
268 PetscInt check_k = user_coarse->KM * (2 - user_coarse->ksc) - (user_fine->KM + 1 - user_coarse->ksc);
269
270 if (check_i + check_j + check_k != 0) {
271 // SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG,
272 // "Grid at level %d, block %d cannot be coarsened from %dx%dx%d to %dx%dx%d with the given semi-coarsening flags. Check grid dimensions.",
273 // level, bi, user_fine->IM, user_fine->JM, user_fine->KM, user_coarse->IM, user_coarse->JM, user_coarse->KM);
274 LOG(GLOBAL,LOG_WARNING,"WARNING: Grid at level %d, block %d can't be consistently coarsened further.\n", level, bi);
275 }
276 }
277 }
278
279 // --- Part 2: Create DMs from Coarse to Fine for each Block ---
280 for (PetscInt bi = 0; bi < nblk; bi++) {
281 LOG_ALLOW_SYNC(GLOBAL, LOG_DEBUG, "--- Creating DMs for Block %d ---\n", bi);
282
283 // Create the coarsest level DM first (passing NULL for the coarse_user)
284 ierr = InitializeSingleGridDM(&mgctx[0].user[bi], NULL); CHKERRQ(ierr);
285
286 // Create finer level DMs, passing the next-coarser context for alignment
287 for (PetscInt level = 1; level < usermg->mglevels; level++) {
288 ierr = InitializeSingleGridDM(&mgctx[level].user[bi], &mgctx[level-1].user[bi]); CHKERRQ(ierr);
289 }
290 }
291
292 // --- Optional: View the finest DM for debugging verification ---
293 if (get_log_level() >= LOG_DEBUG) {
294 LOG_ALLOW_SYNC(GLOBAL, LOG_INFO, "--- Viewing Finest DMDA (Level %d, Block 0) ---\n", usermg->mglevels - 1);
295 ierr = DMView(mgctx[usermg->mglevels - 1].user[0].da, PETSC_VIEWER_STDOUT_WORLD); CHKERRQ(ierr);
296 }
297
298 LOG_ALLOW(GLOBAL, LOG_INFO, "DMDA object creation complete.\n");
299
301
302 PetscFunctionReturn(0);
303}
static PetscErrorCode InitializeSingleGridDM(UserCtx *user, UserCtx *coarse_user)
Internal helper implementation: InitializeSingleGridDM().
Definition grid.c:107
PetscErrorCode DeterminePeriodicity(SimCtx *simCtx)
Scans all block-specific boundary condition files to determine a globally consistent periodicity for ...
Definition io.c:637
#define LOCAL
Logging scope definitions for controlling message output.
Definition logging.h:44
#define LOG(scope, level, fmt,...)
Logging macro for PETSc-based applications with scope control.
Definition logging.h:83
LogLevel get_log_level()
Retrieves the current logging level from the environment variable LOG_LEVEL.
Definition logging.c:84
@ LOG_TRACE
Very fine-grained tracing information for in-depth debugging.
Definition logging.h:32
@ LOG_WARNING
Non-critical issues that warrant attention.
Definition logging.h:29
PetscInt isc
Definition variables.h:889
PetscInt ksc
Definition variables.h:889
PetscInt KM
Definition variables.h:885
PetscInt jsc
Definition variables.h:889
PetscInt JM
Definition variables.h:885
PetscInt IM
Definition variables.h:885
Context for Multigrid operations.
Definition variables.h:568
User-level context for managing the entire multigrid hierarchy.
Definition variables.h:575
Here is the call graph for this function:
Here is the caller graph for this function:

◆ AssignAllGridCoordinates()

PetscErrorCode AssignAllGridCoordinates ( SimCtx simCtx)

Orchestrates the assignment of physical coordinates to all DMDA objects.

This function manages the entire process of populating the coordinate vectors for every DMDA across all multigrid levels and blocks. It follows a two-part strategy that is essential for multigrid methods:

  1. Populate Finest Level: It first loops through each block and calls a helper (SetFinestLevelCoordinates) to set the physical coordinates for the highest-resolution grid (the finest multigrid level).
  2. Restrict to Coarser Levels: It then iterates downwards from the finest level, calling a helper (RestrictCoordinates) to copy the coordinate values from the fine grid nodes to their corresponding parent nodes on the coarser grids. This ensures all levels represent the exact same geometry.
Parameters
simCtxThe master SimCtx, containing the configured UserCtx hierarchy.
Returns
PetscErrorCode 0 on success, or a PETSc error code on failure.

Orchestrates the assignment of physical coordinates to all DMDA objects.

Local to this translation unit.

Definition at line 317 of file grid.c.

318{
319 PetscErrorCode ierr;
320 UserMG *usermg = &simCtx->usermg;
321 PetscInt nblk = simCtx->block_number;
322
323 PetscFunctionBeginUser;
324
326
327 LOG_ALLOW(GLOBAL, LOG_INFO, "Assigning physical coordinates to all grid DMs...\n");
328
329 // --- Part 1: Populate the Finest Grid Level ---
330 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Setting coordinates for the finest grid level (%d)...\n", usermg->mglevels - 1);
331 for (PetscInt bi = 0; bi < nblk; bi++) {
332 UserCtx *fine_user = &usermg->mgctx[usermg->mglevels - 1].user[bi];
333 ierr = SetFinestLevelCoordinates(fine_user); CHKERRQ(ierr);
334 LOG_ALLOW(GLOBAL,LOG_TRACE,"The Finest level coordinates for block %d have been set.\n",bi);
336 ierr = LOG_FIELD_MIN_MAX(fine_user,"Coordinates");
337 }
338 }
339 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Finest level coordinates have been set for all blocks.\n");
340
341 // --- Part 2: Restrict Coordinates to Coarser Levels ---
342 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Restricting coordinates to coarser grid levels...\n");
343 for (PetscInt level = usermg->mglevels - 2; level >= 0; level--) {
344 for (PetscInt bi = 0; bi < nblk; bi++) {
345 UserCtx *coarse_user = &usermg->mgctx[level].user[bi];
346 UserCtx *fine_user = &usermg->mgctx[level + 1].user[bi];
347 ierr = RestrictCoordinates(coarse_user, fine_user); CHKERRQ(ierr);
348
349 LOG_ALLOW(GLOBAL,LOG_TRACE,"Coordinates restricted to block %d level %d.\n",bi,level);
351 ierr = LOG_FIELD_MIN_MAX(coarse_user,"Coordinates");
352 }
353 }
354 }
355
356 LOG_ALLOW(GLOBAL, LOG_INFO, "Physical coordinates assigned to all grid levels and blocks.\n");
357
359
360 PetscFunctionReturn(0);
361}
static PetscErrorCode RestrictCoordinates(UserCtx *coarse_user, UserCtx *fine_user)
Internal helper implementation: RestrictCoordinates().
Definition grid.c:737
static PetscErrorCode SetFinestLevelCoordinates(UserCtx *user)
Internal helper implementation: SetFinestLevelCoordinates().
Definition grid.c:516
#define __FUNCT__
Definition grid.c:9
PetscBool is_function_allowed(const char *functionName)
Checks if a given function is in the allow-list.
Definition logging.c:183
PetscErrorCode LOG_FIELD_MIN_MAX(UserCtx *user, const char *fieldName)
Computes and logs the local and global min/max values of a 3-component vector field.
Definition logging.c:2349
@ LOG_VERBOSE
Extremely detailed logs, typically for development use only.
Definition logging.h:33
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ValidatePeriodicGeometry()

PetscErrorCode ValidatePeriodicGeometry ( UserCtx user)

Validates that configured geometric periodic seams match by translation.

Each active periodic direction is checked independently using the physical nodal coordinates. On success, the constant seam translation is stored in the UserCtx.

Parameters
userGrid/block context with assigned coordinates and boundary configuration.
Returns
PetscErrorCode 0 on success, or a user-input error for unsupported geometry.

Validates that configured geometric periodic seams match by translation.

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

Definition at line 380 of file grid.c.

381{
382 const BCFace neg_faces[3] = {BC_FACE_NEG_X, BC_FACE_NEG_Y, BC_FACE_NEG_Z};
383 const BCFace pos_faces[3] = {BC_FACE_POS_X, BC_FACE_POS_Y, BC_FACE_POS_Z};
384 const char axis_names[3] = {'X', 'Y', 'Z'};
385 const Cmpnts ***coor = NULL;
386 Vec lcoor = NULL;
387 DMDALocalInfo info;
388
389 PetscFunctionBeginUser;
390 PetscCall(DMDAGetLocalInfo(user->da, &info));
391 PetscCall(DMGetCoordinatesLocal(user->da, &lcoor));
392 PetscCheck(lcoor != NULL, PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONGSTATE,
393 "Cannot validate periodic geometry before local coordinates are assigned.");
394 for (PetscInt axis = 0; axis < 3; axis++) {
395 const PetscBool neg_periodic =
396 user->boundary_faces[neg_faces[axis]].mathematical_type == PERIODIC;
397 const PetscBool pos_periodic =
398 user->boundary_faces[pos_faces[axis]].mathematical_type == PERIODIC;
399 PetscReal local_min[3] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MAX_REAL};
400 PetscReal local_max[3] = {-PETSC_MAX_REAL, -PETSC_MAX_REAL, -PETSC_MAX_REAL};
401 PetscReal global_min[3], global_max[3];
402 PetscInt local_count = 0, global_count = 0;
403
404 user->periodic_translation_valid[axis] = PETSC_FALSE;
405 user->periodic_translation[axis] = (Cmpnts){0.0, 0.0, 0.0};
406 if (!neg_periodic && !pos_periodic) continue;
407
408 PetscCheck(neg_periodic && pos_periodic, PETSC_COMM_WORLD, PETSC_ERR_USER_INPUT,
409 "Periodic geometry in the %c direction requires paired negative and positive faces.",
410 axis_names[axis]);
411 const PetscInt axis_size = axis == 0 ? info.mx : (axis == 1 ? info.my : info.mz);
412 PetscCheck(axis_size >= 5, PETSC_COMM_WORLD, PETSC_ERR_USER_INPUT,
413 "%c-periodic geometry on block %d level %d requires at least four physical "
414 "nodes in that direction; found %d.",
415 axis_names[axis], user->_this, user->thislevel, axis_size - 1);
416
417 PetscCall(DMDAVecGetArrayRead(user->fda, lcoor, &coor));
418 if (axis == 0 && info.xs == 0) {
419 for (PetscInt k = PetscMax(info.zs, 0); k < PetscMin(info.zs + info.zm, info.mz - 1); k++) {
420 for (PetscInt j = PetscMax(info.ys, 0); j < PetscMin(info.ys + info.ym, info.my - 1); j++) {
421 const Cmpnts delta = {
422 coor[k][j][-2].x - coor[k][j][0].x,
423 coor[k][j][-2].y - coor[k][j][0].y,
424 coor[k][j][-2].z - coor[k][j][0].z
425 };
426 for (PetscInt c = 0; c < 3; c++) {
427 local_min[c] = PetscMin(local_min[c], CoordinateComponent(delta, c));
428 local_max[c] = PetscMax(local_max[c], CoordinateComponent(delta, c));
429 }
430 local_count++;
431 }
432 }
433 } else if (axis == 1 && info.ys == 0) {
434 for (PetscInt k = PetscMax(info.zs, 0); k < PetscMin(info.zs + info.zm, info.mz - 1); k++) {
435 for (PetscInt i = PetscMax(info.xs, 0); i < PetscMin(info.xs + info.xm, info.mx - 1); i++) {
436 const Cmpnts delta = {
437 coor[k][-2][i].x - coor[k][0][i].x,
438 coor[k][-2][i].y - coor[k][0][i].y,
439 coor[k][-2][i].z - coor[k][0][i].z
440 };
441 for (PetscInt c = 0; c < 3; c++) {
442 local_min[c] = PetscMin(local_min[c], CoordinateComponent(delta, c));
443 local_max[c] = PetscMax(local_max[c], CoordinateComponent(delta, c));
444 }
445 local_count++;
446 }
447 }
448 } else if (axis == 2 && info.zs == 0) {
449 for (PetscInt j = PetscMax(info.ys, 0); j < PetscMin(info.ys + info.ym, info.my - 1); j++) {
450 for (PetscInt i = PetscMax(info.xs, 0); i < PetscMin(info.xs + info.xm, info.mx - 1); i++) {
451 const Cmpnts delta = {
452 coor[-2][j][i].x - coor[0][j][i].x,
453 coor[-2][j][i].y - coor[0][j][i].y,
454 coor[-2][j][i].z - coor[0][j][i].z
455 };
456 for (PetscInt c = 0; c < 3; c++) {
457 local_min[c] = PetscMin(local_min[c], CoordinateComponent(delta, c));
458 local_max[c] = PetscMax(local_max[c], CoordinateComponent(delta, c));
459 }
460 local_count++;
461 }
462 }
463 }
464 PetscCall(DMDAVecRestoreArrayRead(user->fda, lcoor, &coor));
465
466 PetscCallMPI(MPI_Allreduce(local_min, global_min, 3, MPIU_REAL, MPI_MIN, PETSC_COMM_WORLD));
467 PetscCallMPI(MPI_Allreduce(local_max, global_max, 3, MPIU_REAL, MPI_MAX, PETSC_COMM_WORLD));
468 PetscCallMPI(MPI_Allreduce(&local_count, &global_count, 1, MPIU_INT, MPI_SUM, PETSC_COMM_WORLD));
469 PetscCheck(global_count > 0, PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONGSTATE,
470 "No physical seam nodes were available to validate %c-periodic geometry.",
471 axis_names[axis]);
472
473 PetscReal translation[3];
474 PetscReal scale = 1.0;
475 PetscReal max_mismatch = 0.0;
476 for (PetscInt c = 0; c < 3; c++) {
477 translation[c] = 0.5 * (global_min[c] + global_max[c]);
478 scale = PetscMax(scale, PetscAbsReal(translation[c]));
479 max_mismatch = PetscMax(max_mismatch, global_max[c] - global_min[c]);
480 }
481 const PetscReal tolerance = 1.0e-9 * scale + 100.0 * PETSC_MACHINE_EPSILON;
482 const PetscReal magnitude = PetscSqrtReal(
483 PetscSqr(translation[0]) + PetscSqr(translation[1]) + PetscSqr(translation[2]));
484
485 PetscCheck(max_mismatch <= tolerance, PETSC_COMM_WORLD, PETSC_ERR_USER_INPUT,
486 "Unsupported %c-periodic geometry on block %d level %d: opposite physical "
487 "surfaces are not related by one constant translation. Maximum component "
488 "mismatch is %.12e (tolerance %.12e).",
489 axis_names[axis], user->_this, user->thislevel,
490 (double)max_mismatch, (double)tolerance);
491 PetscCheck(magnitude > tolerance, PETSC_COMM_WORLD, PETSC_ERR_USER_INPUT,
492 "Unsupported %c-periodic geometry on block %d level %d: seam translation "
493 "magnitude %.12e is zero or too small.",
494 axis_names[axis], user->_this, user->thislevel, (double)magnitude);
495
496 user->periodic_translation[axis] =
497 (Cmpnts){translation[0], translation[1], translation[2]};
498 user->periodic_translation_valid[axis] = PETSC_TRUE;
500 "Validated %c-periodic geometry for block %d level %d with translation "
501 "(%.12e, %.12e, %.12e).\n",
502 axis_names[axis], user->_this, user->thislevel,
503 (double)translation[0], (double)translation[1], (double)translation[2]);
504 }
505
506 PetscFunctionReturn(0);
507}
static PetscReal CoordinateComponent(Cmpnts value, PetscInt component)
Returns one Cartesian component from a coordinate/vector value.
Definition grid.c:366
@ PERIODIC
Definition variables.h:290
BoundaryFaceConfig boundary_faces[6]
Definition variables.h:896
PetscInt _this
Definition variables.h:889
PetscScalar x
Definition variables.h:101
PetscInt thislevel
Definition variables.h:944
PetscScalar z
Definition variables.h:101
PetscScalar y
Definition variables.h:101
Cmpnts periodic_translation[3]
Definition variables.h:892
PetscBool periodic_translation_valid[3]
Definition variables.h:893
BCType mathematical_type
Definition variables.h:366
BCFace
Identifies the six logical faces of a structured computational block.
Definition variables.h:259
@ BC_FACE_NEG_X
Definition variables.h:260
@ BC_FACE_POS_Z
Definition variables.h:262
@ BC_FACE_POS_Y
Definition variables.h:261
@ BC_FACE_NEG_Z
Definition variables.h:262
@ BC_FACE_POS_X
Definition variables.h:260
@ BC_FACE_NEG_Y
Definition variables.h:261
A 3D point or vector with PetscScalar components.
Definition variables.h:100
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ComputeLocalBoundingBox()

PetscErrorCode ComputeLocalBoundingBox ( UserCtx user,
BoundingBox localBBox 
)

Computes the local bounding box of the grid on the current process.

This function calculates the minimum and maximum coordinates of the local grid points owned by the current MPI process and stores the computed bounding box in the provided structure.

Parameters
[in]userPointer to the user-defined context containing grid information.
[out]localBBoxPointer to the BoundingBox structure to store the computed bounding box.
Returns
PetscErrorCode Returns 0 on success, non-zero on failure.

Computes the local bounding box of the grid on the current process.

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

See also
ComputeLocalBoundingBox()

Definition at line 812 of file grid.c.

813{
814 PetscErrorCode ierr;
815 PetscInt i, j, k;
816 PetscMPIInt rank;
817 PetscInt xs, ys, zs, xe, ye, ze;
818 DMDALocalInfo info;
819 Vec coordinates;
820 Cmpnts ***coordArray;
821 Cmpnts minCoords, maxCoords;
822
823 PetscFunctionBeginUser;
824
826
827 // Start of function execution
828 LOG_ALLOW(GLOBAL, LOG_INFO, "Entering the function.\n");
829
830 // Validate input Pointers
831 if (!user) {
832 LOG_ALLOW(LOCAL, LOG_ERROR, "Input 'user' Pointer is NULL.\n");
833 return PETSC_ERR_ARG_NULL;
834 }
835 if (!localBBox) {
836 LOG_ALLOW(LOCAL, LOG_ERROR, "Output 'localBBox' Pointer is NULL.\n");
837 return PETSC_ERR_ARG_NULL;
838 }
839
840 // Get MPI rank
841 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
842
843 // Get the local coordinates vector from the DMDA
844 ierr = DMGetCoordinatesLocal(user->da, &coordinates);
845 if (ierr) {
846 LOG_ALLOW(LOCAL, LOG_ERROR, "Error getting local coordinates vector.\n");
847 return ierr;
848 }
849
850 if (!coordinates) {
851 LOG_ALLOW(LOCAL, LOG_ERROR, "Coordinates vector is NULL.\n");
852 return PETSC_ERR_ARG_NULL;
853 }
854
855 // Access the coordinate array for reading
856 ierr = DMDAVecGetArrayRead(user->fda, coordinates, &coordArray);
857 if (ierr) {
858 LOG_ALLOW(LOCAL, LOG_ERROR, "Error accessing coordinate array.\n");
859 return ierr;
860 }
861
862 // Get the local grid information (indices and sizes)
863 ierr = DMDAGetLocalInfo(user->da, &info);
864 if (ierr) {
865 LOG_ALLOW(LOCAL, LOG_ERROR, "Error getting DMDA local info.\n");
866 return ierr;
867 }
868
869
870 xs = info.gxs; xe = xs + info.gxm;
871 ys = info.gys; ye = ys + info.gym;
872 zs = info.gzs; ze = zs + info.gzm;
873
874 /*
875 xs = info.xs; xe = xs + info.xm;
876 ys = info.ys; ye = ys + info.ym;
877 zs = info.zs; ze = zs + info.zm;
878 */
879
880 // Initialize min and max coordinates with extreme values
881 minCoords.x = minCoords.y = minCoords.z = PETSC_MAX_REAL;
882 maxCoords.x = maxCoords.y = maxCoords.z = PETSC_MIN_REAL;
883
884 LOG_ALLOW(LOCAL, LOG_TRACE, "[Rank %d] Grid indices (Including Ghosts): xs=%d, xe=%d, ys=%d, ye=%d, zs=%d, ze=%d.\n",rank, xs, xe, ys, ye, zs, ze);
885
886 // Iterate over the local grid to find min and max coordinates
887 for (k = zs; k < ze; k++) {
888 for (j = ys; j < ye; j++) {
889 for (i = xs; i < xe; i++) {
890 // Only consider nodes within the physical domain.
891 if(i < user->IM && j < user->JM && k < user->KM){
892 Cmpnts coord = coordArray[k][j][i];
893
894 // Update min and max coordinates
895 if (coord.x < minCoords.x) minCoords.x = coord.x;
896 if (coord.y < minCoords.y) minCoords.y = coord.y;
897 if (coord.z < minCoords.z) minCoords.z = coord.z;
898
899 if (coord.x > maxCoords.x) maxCoords.x = coord.x;
900 if (coord.y > maxCoords.y) maxCoords.y = coord.y;
901 if (coord.z > maxCoords.z) maxCoords.z = coord.z;
902 }
903 }
904 }
905 }
906
907
908 // Add tolerance to bboxes.
909 minCoords.x = minCoords.x - BBOX_TOLERANCE;
910 minCoords.y = minCoords.y - BBOX_TOLERANCE;
911 minCoords.z = minCoords.z - BBOX_TOLERANCE;
912
913 maxCoords.x = maxCoords.x + BBOX_TOLERANCE;
914 maxCoords.y = maxCoords.y + BBOX_TOLERANCE;
915 maxCoords.z = maxCoords.z + BBOX_TOLERANCE;
916
917 LOG_ALLOW(LOCAL,LOG_DEBUG," Tolerance added to the limits: %.8e .\n",(PetscReal)BBOX_TOLERANCE);
918
919 // Log the computed min and max coordinates
920 LOG_ALLOW(LOCAL, LOG_INFO,"[Rank %d] Bounding Box Ranges = X[%.6f, %.6f], Y[%.6f,%.6f], Z[%.6f, %.6f].\n",rank,minCoords.x, maxCoords.x,minCoords.y, maxCoords.y, minCoords.z, maxCoords.z);
921
922
923
924 // Restore the coordinate array
925 ierr = DMDAVecRestoreArrayRead(user->fda, coordinates, &coordArray);
926 if (ierr) {
927 LOG_ALLOW(LOCAL, LOG_ERROR, "Error restoring coordinate array.\n");
928 return ierr;
929 }
930
931 // Set the local bounding box
932 localBBox->min_coords = minCoords;
933 localBBox->max_coords = maxCoords;
934
935 // Update the bounding box inside the UserCtx for consistency
936 user->bbox = *localBBox;
937
938 LOG_ALLOW(GLOBAL, LOG_INFO, "Exiting the function successfully.\n");
939
941
942 PetscFunctionReturn(0);
943}
#define BBOX_TOLERANCE
Definition grid.c:6
@ LOG_ERROR
Critical errors that may halt the program.
Definition logging.h:28
Cmpnts max_coords
Maximum x, y, z coordinates of the bounding box.
Definition variables.h:171
Cmpnts min_coords
Minimum x, y, z coordinates of the bounding box.
Definition variables.h:170
BoundingBox bbox
Definition variables.h:887
Here is the caller graph for this function:

◆ GatherAllBoundingBoxes()

PetscErrorCode GatherAllBoundingBoxes ( UserCtx user,
BoundingBox **  allBBoxes 
)

Gathers local bounding boxes from all MPI processes to rank 0.

This function computes the local bounding box on each process, then collects all local bounding boxes on the root process (rank 0) using MPI. The result is stored in an array of BoundingBox structures on rank 0.

Parameters
[in]userPointer to the user-defined context containing grid information.
[out]allBBoxesPointer to a pointer where the array of gathered bounding boxes will be stored on rank 0. The caller on rank 0 must free this array.
Returns
PetscErrorCode Returns 0 on success, non-zero on failure.

Gathers local bounding boxes from all MPI processes to rank 0.

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

See also
GatherAllBoundingBoxes()

Definition at line 954 of file grid.c.

955{
956 PetscErrorCode ierr;
957 PetscMPIInt rank, size;
958 BoundingBox *bboxArray = NULL;
959 BoundingBox localBBox;
960
961 PetscFunctionBeginUser;
962
964
965 /* Validate */
966 if (!user || !allBBoxes) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
967 "GatherAllBoundingBoxes: NULL pointer");
968
969 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRMPI(ierr);
970 ierr = MPI_Comm_size(PETSC_COMM_WORLD, &size); CHKERRMPI(ierr);
971
972 /* Compute local bbox */
973 ierr = ComputeLocalBoundingBox(user, &localBBox); CHKERRQ(ierr);
974
975 /* Ensure everyone is synchronized before the gather */
976 MPI_Barrier(PETSC_COMM_WORLD);
978 "Rank %d: about to MPI_Gather(localBBox)\n", rank);
979
980 /* Allocate on root */
981 if (rank == 0) {
982 bboxArray = (BoundingBox*)malloc(size * sizeof(BoundingBox));
983 if (!bboxArray) SETERRABORT(PETSC_COMM_WORLD, PETSC_ERR_MEM,
984 "GatherAllBoundingBoxes: malloc failed");
985 }
986
987 /* Collective: every rank must call */
988 ierr = MPI_Gather(&localBBox, sizeof(BoundingBox), MPI_BYTE,
989 bboxArray, sizeof(BoundingBox), MPI_BYTE,
990 0, PETSC_COMM_WORLD);
991 CHKERRMPI(ierr);
992
993 MPI_Barrier(PETSC_COMM_WORLD);
995 "Rank %d: completed MPI_Gather(localBBox)\n", rank);
996
997 /* Return result */
998 if (rank == 0) {
999 *allBBoxes = bboxArray;
1000 } else {
1001 *allBBoxes = NULL;
1002 }
1003
1005
1006 PetscFunctionReturn(0);
1007}
PetscErrorCode ComputeLocalBoundingBox(UserCtx *user, BoundingBox *localBBox)
Implementation of ComputeLocalBoundingBox().
Definition grid.c:812
Defines a 3D axis-aligned bounding box.
Definition variables.h:169
Here is the call graph for this function:
Here is the caller graph for this function:

◆ BroadcastAllBoundingBoxes()

PetscErrorCode BroadcastAllBoundingBoxes ( UserCtx user,
BoundingBox **  bboxlist 
)

Broadcasts the bounding box information collected on rank 0 to all other ranks.

This function assumes that GatherAllBoundingBoxes() was previously called, so bboxlist is allocated and populated on rank 0. All other ranks will allocate memory for bboxlist, and this function will use MPI_Bcast to distribute the bounding box data to them.

Parameters
[in]userPointer to the UserCtx structure. (Currently unused in this function, but kept for consistency.)
[in,out]bboxlistPointer to the array of BoundingBoxes. On rank 0, this should point to a valid array of size 'size' (where size is the number of MPI ranks). On non-root ranks, this function will allocate memory for bboxlist.
Returns
PetscErrorCode Returns 0 on success, non-zero on MPI or PETSc-related errors.

Broadcasts the bounding box information collected on rank 0 to all other ranks.

Local to this translation unit.

Definition at line 1016 of file grid.c.

1017{
1018 PetscErrorCode ierr;
1019 (void)user;
1020 PetscMPIInt rank, size;
1021
1022 PetscFunctionBeginUser;
1023
1025
1026 if (!bboxlist) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
1027 "BroadcastAllBoundingBoxes: NULL pointer");
1028
1029 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRMPI(ierr);
1030 ierr = MPI_Comm_size(PETSC_COMM_WORLD, &size); CHKERRMPI(ierr);
1031
1032 /* Non-root ranks must allocate before the Bcast */
1033 if (rank != 0) {
1034 *bboxlist = (BoundingBox*)malloc(size * sizeof(BoundingBox));
1035 if (!*bboxlist) SETERRABORT(PETSC_COMM_WORLD, PETSC_ERR_MEM,
1036 "BroadcastAllBoundingBoxes: malloc failed");
1037 }
1038
1039 MPI_Barrier(PETSC_COMM_WORLD);
1041 "Rank %d: about to MPI_Bcast(%d boxes)\n", rank, size);
1042
1043 /* Collective: every rank must call */
1044 ierr = MPI_Bcast(*bboxlist, size * sizeof(BoundingBox), MPI_BYTE,
1045 0, PETSC_COMM_WORLD);
1046 CHKERRMPI(ierr);
1047
1048 MPI_Barrier(PETSC_COMM_WORLD);
1050 "Rank %d: completed MPI_Bcast(%d boxes)\n", rank, size);
1051
1052
1054
1055 PetscFunctionReturn(0);
1056}
Here is the caller graph for this function:

◆ CalculateInletProperties()

PetscErrorCode CalculateInletProperties ( UserCtx user)

Calculates the center and area of the primary INLET face.

This function identifies the primary INLET face from the boundary face configurations, computes its geometric center and total area using a generic utility function, and stores these results in the simulation context.

Parameters
userPointer to the UserCtx containing boundary face information.
Returns
PetscErrorCode

Calculates the center and area of the primary INLET face.

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

See also
CalculateInletProperties()

Definition at line 1066 of file grid.c.

1067{
1068 PetscErrorCode ierr;
1069 BCFace inlet_face_id = -1;
1070 PetscBool inlet_found = PETSC_FALSE;
1071
1072 PetscFunctionBeginUser;
1074
1075 // 1. Identify the primary inlet face from the configuration
1076 for (int i = 0; i < 6; i++) {
1077 if (user->boundary_faces[i].mathematical_type == INLET) {
1078 inlet_face_id = user->boundary_faces[i].face_id;
1079 inlet_found = PETSC_TRUE;
1080 break; // Use the first inlet found
1081 }
1082 }
1083
1084 if (!inlet_found) {
1085 LOG_ALLOW(GLOBAL, LOG_INFO, "No INLET face found. Skipping inlet center calculation.\n");
1087 PetscFunctionReturn(0);
1088 }
1089
1090 Cmpnts inlet_center;
1091 PetscReal inlet_area;
1092
1093 // 2. Call the generic utility to compute the center and area of any face.
1094 ierr = CalculateFaceCenterAndArea(user,inlet_face_id,&inlet_center,&inlet_area); CHKERRQ(ierr);
1095
1096 // 3. Store results in the SimCtx
1097 user->simCtx->CMx_c = inlet_center.x;
1098 user->simCtx->CMy_c = inlet_center.y;
1099 user->simCtx->CMz_c = inlet_center.z;
1100 user->simCtx->AreaInSum = inlet_area;
1101
1103 "Rank[%d] Inlet Center: (%.6f, %.6f, %.6f), Area: %.6f\n",
1104 user->simCtx->rank, inlet_center.x, inlet_center.y, inlet_center.z, inlet_area);
1105
1107 PetscFunctionReturn(0);
1108
1109}
PetscErrorCode CalculateFaceCenterAndArea(UserCtx *user, BCFace face_id, Cmpnts *face_center, PetscReal *face_area)
Implementation of CalculateFaceCenterAndArea().
Definition grid.c:1162
@ INLET
Definition variables.h:288
SimCtx * simCtx
Back-pointer to the master simulation context.
Definition variables.h:879
PetscReal CMy_c
Definition variables.h:761
PetscReal CMz_c
Definition variables.h:761
PetscReal AreaInSum
Definition variables.h:783
PetscReal CMx_c
Definition variables.h:761
Here is the call graph for this function:
Here is the caller graph for this function:

◆ CalculateOutletProperties()

PetscErrorCode CalculateOutletProperties ( UserCtx user)

Calculates the center and area of the primary OUTLET face.

This function identifies the primary OUTLET face from the boundary face configurations, computes its geometric center and total area using a generic utility function, and stores these results in the simulation context.

Parameters
userPointer to the UserCtx containing boundary face information.
Returns
PetscErrorCode

Calculates the center and area of the primary OUTLET face.

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

See also
CalculateOutletProperties()

Definition at line 1119 of file grid.c.

1120{
1121 PetscErrorCode ierr;
1122 BCFace outlet_face_id = -1;
1123 PetscBool outlet_found = PETSC_FALSE;
1124 PetscFunctionBeginUser;
1126 // 1. Identify the primary outlet face from the configuration
1127 for (int i = 0; i < 6; i++) {
1128 if (user->boundary_faces[i].mathematical_type == OUTLET) {
1129 outlet_face_id = user->boundary_faces[i].face_id;
1130 outlet_found = PETSC_TRUE;
1131 break; // Use the first outlet found
1132 }
1133 }
1134 if (!outlet_found) {
1135 LOG_ALLOW(GLOBAL, LOG_INFO, "No OUTLET face found. Skipping outlet center calculation.\n");
1137 PetscFunctionReturn(0);
1138 }
1139 PetscReal outlet_area;
1140 Cmpnts outlet_center;
1141 // 2. Call the generic utility to compute the center and area of any face
1142 ierr = CalculateFaceCenterAndArea(user,outlet_face_id,&outlet_center,&outlet_area); CHKERRQ(ierr);
1143 // 3. Store results in the SimCtx
1144 user->simCtx->AreaOutSum = outlet_area;
1145
1147 "Outlet Center: (%.6f, %.6f, %.6f), Area: %.6f\n",
1148 outlet_center.x, outlet_center.y, outlet_center.z, outlet_area);
1149
1151 PetscFunctionReturn(0);
1152}
@ OUTLET
Definition variables.h:287
PetscReal AreaOutSum
Definition variables.h:783
Here is the call graph for this function:
Here is the caller graph for this function:

◆ CalculateFaceCenterAndArea()

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.

This function computes two key properties of a boundary face in the computational domain:

  1. Geometric Center: The average (x,y,z) position of all physical nodes on the face
  2. Total Area: The sum of face area vector magnitudes from all non-solid cells adjacent to the face

Indexing Architecture

The solver uses different indexing conventions for different field types:

Node-Centered Fields (Coordinates):

  • Direct indexing: Node n stored at coor[n]
  • For mx=26: Physical nodes [0-24], Dummy at [25]
  • For mz=98: Physical nodes [0-96], Dummy at [97]

Face-Centered Fields (Metrics: csi, eta, zet):

  • Direct indexing: Face n stored at csi/eta/zet[n]
  • For mx=26: Physical faces [0-24], Dummy at [25]
  • For mz=98: Physical faces [0-96], Dummy at [97]
  • Face at index k bounds cells k-1 and k

Cell-Centered Fields (nvert):

  • Shifted indexing: Physical cell c stored at nvert[c+1]
  • For mx=26 (25 cells): Cell 0→nvert[1], Cell 23→nvert[24]
  • For mz=98 (96 cells): Cell 0→nvert[1], Cell 95→nvert[96]
  • nvert[0] and nvert[mx-1] are ghost values

Face-to-Index Mapping

Example for a domain with mx=26, my=26, mz=98:

Face ID Node Index Face Metric Adjacent Cell (shifted) Physical Extent
BC_FACE_NEG_X i=0 csi[k][j][0] nvert[k][j][1] (Cell 0) j∈[0,24], k∈[0,96]
BC_FACE_POS_X i=24 csi[k][j][24] nvert[k][j][24] (Cell 23) j∈[0,24], k∈[0,96]
BC_FACE_NEG_Y j=0 eta[k][0][i] nvert[k][1][i] (Cell 0) i∈[0,24], k∈[0,96]
BC_FACE_POS_Y j=24 eta[k][24][i] nvert[k][24][i] (Cell 23) i∈[0,24], k∈[0,96]
BC_FACE_NEG_Z k=0 zet[0][j][i] nvert[1][j][i] (Cell 0) i∈[0,24], j∈[0,24]
BC_FACE_POS_Z k=96 zet[96][j][i] nvert[96][j][i] (Cell 95) i∈[0,24], j∈[0,24]

Algorithm

The function performs two separate computations with different loop bounds:

1. Center Calculation (uses ALL physical nodes):

  • Loop over all physical nodes on the face (excluding dummy indices)
  • Accumulate coordinate sums: Σx, Σy, Σz
  • Count number of nodes
  • Average: center = (Σx/n, Σy/n, Σz/n)

2. Area Calculation (uses INTERIOR cells only):

  • Loop over interior cell range to avoid accessing ghost values in nvert
  • For each face adjacent to a fluid cell (nvert < 0.1):
    • Compute area magnitude: |csi/eta/zet| = √(x² + y² + z²)
    • Accumulate to total area

Loop Bound Details

Why different bounds for center vs. area?

For BC_FACE_NEG_X at i=0 with my=26, mz=98:

Center calculation (coordinates):

  • j ∈ [ys, j_max): Includes j=[0,24] (25 nodes), excludes dummy at j=25
  • k ∈ [zs, k_max): Includes k=[0,96] (97 nodes), excludes dummy at k=97
  • Total: 25 × 97 = 2,425 nodes

Area calculation (nvert checks):

  • j ∈ [lys, lye): j=[1,24] (24 values), excludes boundaries
  • k ∈ [lzs, lze): k=[1,96] (96 values), excludes boundaries
  • Why restricted?
    • At j=0: nvert[k][0][1] is ghost (no cell at j=-1)
    • At j=25: nvert[k][25][1] is ghost (no cell at j=24, index 25 is dummy)
    • At k=0: nvert[0][j][1] is ghost (no cell at k=-1)
    • At k=97: nvert[97][j][1] is ghost (no cell at k=96, index 97 is dummy)
  • Total: 24 × 96 = 2,304 interior cells adjacent to face

Area Calculation Formulas

Face area contributions are computed from metric tensor magnitudes:

  • i-faces (±Xi): Area = |csi| = √(csi_x² + csi_y² + csi_z²)
  • j-faces (±Eta): Area = |eta| = √(eta_x² + eta_y² + eta_z²)
  • k-faces (±Zeta): Area = |zet| = √(zet_x² + zet_y² + zet_z²)
Parameters
[in]userPointer to UserCtx containing grid info, DMs, and field vectors
[in]face_idEnum identifying which boundary face to analyze (BC_FACE_NEG_X, etc.)
[out]face_centerPointer to Cmpnts structure to store computed geometric center (x,y,z)
[out]face_areaPointer to PetscReal to store computed total face area
Returns
PetscErrorCode Returns 0 on success, non-zero PETSc error code on failure
Note
This function uses MPI_Allreduce, so it must be called collectively by all ranks
Only ranks that own the specified boundary face contribute to the calculation
Center calculation includes ALL physical nodes on the face
Area calculation ONLY includes faces adjacent to fluid cells (nvert < 0.1)
Dummy/unused indices (e.g., k=97, j=25 for standard test case) are excluded
Warning
Assumes grid and field arrays have been properly initialized
Incorrect face_id values will result in zero contribution from all ranks
See also
CanRankServiceFace() for determining rank ownership of boundary faces
BCFace enum for valid face_id values
LOG_FIELD_ANATOMY() for debugging field indexing
Example Usage:
Cmpnts inlet_center;
PetscReal inlet_area;
ierr = CalculateFaceCenterAndArea(user, BC_FACE_NEG_Z, &inlet_center, &inlet_area);
PetscPrintf(PETSC_COMM_WORLD, "Inlet center: (%.4f, %.4f, %.4f), Area: %.6f\n",
inlet_center.x, inlet_center.y, inlet_center.z, inlet_area);
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:1162

Calculates the geometric center and total area of a specified boundary face.

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

See also
CalculateFaceCenterAndArea()

< Local sum of (x,y,z) coordinates

< Local sum of face area magnitudes

< Local count of nodes

< Global sum of coordinates

< Global sum of areas

< Global count of nodes

< i-range: [xs, xe)

< j-range: [ys, ye)

< k-range: [zs, ze)

< Physical domain size in i (exclude dummy)

< Physical domain size in j (exclude dummy)

< Physical domain size in k (exclude dummy)

< Start at 1 if on -Xi boundary

< End at mx-1 if on +Xi boundary

< Start at 1 if on -Eta boundary

< End at my-1 if on +Eta boundary

< Start at 1 if on -Zeta boundary

< End at mz-1 if on +Zeta boundary

< Exclude dummy at i=mx-1 (e.g., i=25)

< Exclude dummy at j=my-1 (e.g., j=25)

< Exclude dummy at k=mz-1 (e.g., k=97)

< Local ghosted coordinate vector

< Nodal coordinates [k][j][i]

< Face metric tensors [k][j][i]

< Cell blanking field [k][j][i] (shifted +1)

Definition at line 1162 of file grid.c.

1164{
1165 PetscErrorCode ierr;
1166 DMDALocalInfo info;
1167
1168 // ========================================================================
1169 // Local accumulators for this rank's contribution
1170 // ========================================================================
1171 PetscReal local_sum[3] = {0.0, 0.0, 0.0}; ///< Local sum of (x,y,z) coordinates
1172 PetscReal localAreaSum = 0.0; ///< Local sum of face area magnitudes
1173 PetscCount local_n_points = 0; ///< Local count of nodes
1174
1175 // ========================================================================
1176 // Global accumulators after MPI reduction
1177 // ========================================================================
1178 PetscReal global_sum[3] = {0.0, 0.0, 0.0}; ///< Global sum of coordinates
1179 PetscReal globalAreaSum = 0.0; ///< Global sum of areas
1180 PetscCount global_n_points = 0; ///< Global count of nodes
1181
1182 // ========================================================================
1183 // Grid information and array pointers
1184 // ========================================================================
1185 info = user->info;
1186
1187 // Rank's owned range in global indices
1188 PetscInt xs = info.xs, xe = info.xs + info.xm; ///< i-range: [xs, xe)
1189 PetscInt ys = info.ys, ye = info.ys + info.ym; ///< j-range: [ys, ye)
1190 PetscInt zs = info.zs, ze = info.zs + info.zm; ///< k-range: [zs, ze)
1191
1192 // Global domain dimensions (total allocated, includes dummy at end)
1193 PetscInt mx = info.mx, my = info.my, mz = info.mz;
1194 PetscInt IM = user->IM; ///< Physical domain size in i (exclude dummy)
1195 PetscInt JM = user->JM; ///< Physical domain size in j (exclude dummy)
1196 PetscInt KM = user->KM; ///< Physical domain size in k (exclude dummy)
1197
1198 // ========================================================================
1199 // Interior loop bounds (adjusted to avoid ghost/boundary cells)
1200 // These are used for nvert checks where we need valid cell indices
1201 // ========================================================================
1202 PetscInt lxs = xs; if(xs == 0) lxs = xs + 1; ///< Start at 1 if on -Xi boundary
1203 PetscInt lxe = xe; if(xe == mx) lxe = xe - 1; ///< End at mx-1 if on +Xi boundary
1204 PetscInt lys = ys; if(ys == 0) lys = ys + 1; ///< Start at 1 if on -Eta boundary
1205 PetscInt lye = ye; if(ye == my) lye = ye - 1; ///< End at my-1 if on +Eta boundary
1206 PetscInt lzs = zs; if(zs == 0) lzs = zs + 1; ///< Start at 1 if on -Zeta boundary
1207 PetscInt lze = ze; if(ze == mz) lze = ze - 1; ///< End at mz-1 if on +Zeta boundary
1208
1209 // ========================================================================
1210 // Physical node bounds (exclude dummy indices at mx-1, my-1, mz-1)
1211 // These are used for coordinate loops where we want ALL physical nodes
1212 // ========================================================================
1213 PetscInt i_max = (xe == mx) ? mx - 1 : xe; ///< Exclude dummy at i=mx-1 (e.g., i=25)
1214 PetscInt j_max = (ye == my) ? my - 1 : ye; ///< Exclude dummy at j=my-1 (e.g., j=25)
1215 PetscInt k_max = (ze == mz) ? mz - 1 : ze; ///< Exclude dummy at k=mz-1 (e.g., k=97)
1216
1217 // ========================================================================
1218 // Array pointers for field access
1219 // ========================================================================
1220 Vec lCoor; ///< Local ghosted coordinate vector
1221 Cmpnts ***coor; ///< Nodal coordinates [k][j][i]
1222 Cmpnts ***csi, ***eta, ***zet; ///< Face metric tensors [k][j][i]
1223 PetscReal ***nvert; ///< Cell blanking field [k][j][i] (shifted +1)
1224
1225 PetscFunctionBeginUser;
1227
1228 // ========================================================================
1229 // Step 1: Check if this rank owns the specified boundary face
1230 // ========================================================================
1231 PetscBool owns_face = PETSC_FALSE;
1232 ierr = CanRankServiceFace(&info,IM,JM,KM,face_id,&owns_face); CHKERRQ(ierr);
1233 if(owns_face){
1234 // ========================================================================
1235 // Step 2: Get read-only array access for all required fields
1236 // ========================================================================
1237 ierr = DMGetCoordinatesLocal(user->da, &lCoor); CHKERRQ(ierr);
1238 ierr = DMDAVecGetArrayRead(user->fda, lCoor, &coor); CHKERRQ(ierr);
1239 ierr = DMDAVecGetArrayRead(user->da, user->lNvert, &nvert); CHKERRQ(ierr);
1240 ierr = DMDAVecGetArrayRead(user->fda, user->lCsi, &csi); CHKERRQ(ierr);
1241 ierr = DMDAVecGetArrayRead(user->fda, user->lEta, &eta); CHKERRQ(ierr);
1242 ierr = DMDAVecGetArrayRead(user->fda, user->lZet, &zet); CHKERRQ(ierr);
1243
1244 // ========================================================================
1245 // Step 3: Loop over the specified face and accumulate center and area
1246 // ========================================================================
1247 switch (face_id) {
1248
1249 // ====================================================================
1250 // BC_FACE_NEG_X: Face at i=0 (bottom boundary in i-direction)
1251 // ====================================================================
1252 case BC_FACE_NEG_X:
1253 if (xs == 0) {
1254 PetscInt i = 0; // Face is at node index i=0
1255
1256 // ---- Part 1: Center calculation (ALL physical nodes) ----
1257 // Loop over ALL physical nodes on this face
1258 // For my=26, mz=98: j∈[0,24], k∈[0,96] → 25×97 = 2,425 nodes
1259 for (PetscInt k = zs; k < k_max; k++) {
1260 for (PetscInt j = ys; j < j_max; j++) {
1261 // Accumulate coordinates at node [k][j][0]
1262 local_sum[0] += coor[k][j][i].x;
1263 local_sum[1] += coor[k][j][i].y;
1264 local_sum[2] += coor[k][j][i].z;
1265 local_n_points++;
1266 }
1267 }
1268
1269 // ---- Part 2: Area calculation (INTERIOR cells only) ----
1270 // Loop over interior range where nvert checks are valid
1271 // For my=26, mz=98: j∈[1,24], k∈[1,96] → 24×96 = 2,304 cells
1272 for (PetscInt k = lzs; k < lze; k++) {
1273 for (PetscInt j = lys; j < lye; j++) {
1274 // Check if adjacent cell is fluid
1275 // nvert[k][j][i+1] = nvert[k][j][1] checks Cell 0
1276 // (Physical Cell 0 in j-k plane, stored at shifted index [1])
1277 if (nvert[k][j][i+1] < 0.1) {
1278 // Cell is fluid - add face area contribution
1279 // Face area = magnitude of csi metric at [k][j][0]
1280 localAreaSum += sqrt(csi[k][j][i].x * csi[k][j][i].x +
1281 csi[k][j][i].y * csi[k][j][i].y +
1282 csi[k][j][i].z * csi[k][j][i].z);
1283 }
1284 }
1285 }
1286 }
1287 break;
1288
1289 // ====================================================================
1290 // BC_FACE_POS_X: Face at i=IM-1 (top boundary in i-direction)
1291 // ====================================================================
1292 case BC_FACE_POS_X:
1293 if (xe == mx) {
1294 PetscInt i = mx - 2; // Last physical node (e.g., i=24 for mx=26)
1295
1296 // ---- Part 1: Center calculation (ALL physical nodes) ----
1297 for (PetscInt k = zs; k < k_max; k++) {
1298 for (PetscInt j = ys; j < j_max; j++) {
1299 local_sum[0] += coor[k][j][i].x;
1300 local_sum[1] += coor[k][j][i].y;
1301 local_sum[2] += coor[k][j][i].z;
1302 local_n_points++;
1303 }
1304 }
1305
1306 // ---- Part 2: Area calculation (INTERIOR cells only) ----
1307 for (PetscInt k = lzs; k < lze; k++) {
1308 for (PetscInt j = lys; j < lye; j++) {
1309 // Check if adjacent cell is fluid
1310 // nvert[k][j][i] = nvert[k][j][24] checks last cell (Cell 23)
1311 // (Physical Cell 23, stored at shifted index [24])
1312 if (nvert[k][j][i] < 0.1) {
1313 // Face area = magnitude of csi metric at [k][j][24]
1314 localAreaSum += sqrt(csi[k][j][i].x * csi[k][j][i].x +
1315 csi[k][j][i].y * csi[k][j][i].y +
1316 csi[k][j][i].z * csi[k][j][i].z);
1317 }
1318 }
1319 }
1320 }
1321 break;
1322
1323 // ====================================================================
1324 // BC_FACE_NEG_Y: Face at j=0 (bottom boundary in j-direction)
1325 // ====================================================================
1326 case BC_FACE_NEG_Y:
1327 if (ys == 0) {
1328 PetscInt j = 0; // Face is at node index j=0
1329
1330 // ---- Part 1: Center calculation (ALL physical nodes) ----
1331 // For mx=26, mz=98: i∈[0,24], k∈[0,96] → 25×97 = 2,425 nodes
1332 for (PetscInt k = zs; k < k_max; k++) {
1333 for (PetscInt i = xs; i < i_max; i++) {
1334 local_sum[0] += coor[k][j][i].x;
1335 local_sum[1] += coor[k][j][i].y;
1336 local_sum[2] += coor[k][j][i].z;
1337 local_n_points++;
1338 }
1339 }
1340
1341 // ---- Part 2: Area calculation (INTERIOR cells only) ----
1342 // For mx=26, mz=98: i∈[1,24], k∈[1,96] → 24×96 = 2,304 cells
1343 for (PetscInt k = lzs; k < lze; k++) {
1344 for (PetscInt i = lxs; i < lxe; i++) {
1345 // nvert[k][j+1][i] = nvert[k][1][i] checks Cell 0
1346 if (nvert[k][j+1][i] < 0.1) {
1347 // Face area = magnitude of eta metric at [k][0][i]
1348 localAreaSum += sqrt(eta[k][j][i].x * eta[k][j][i].x +
1349 eta[k][j][i].y * eta[k][j][i].y +
1350 eta[k][j][i].z * eta[k][j][i].z);
1351 }
1352 }
1353 }
1354 }
1355 break;
1356
1357 // ====================================================================
1358 // BC_FACE_POS_Y: Face at j=JM-1 (top boundary in j-direction)
1359 // ====================================================================
1360 case BC_FACE_POS_Y:
1361 if (ye == my) {
1362 PetscInt j = my - 2; // Last physical node (e.g., j=24 for my=26)
1363
1364 // ---- Part 1: Center calculation (ALL physical nodes) ----
1365 for (PetscInt k = zs; k < k_max; k++) {
1366 for (PetscInt i = xs; i < i_max; i++) {
1367 local_sum[0] += coor[k][j][i].x;
1368 local_sum[1] += coor[k][j][i].y;
1369 local_sum[2] += coor[k][j][i].z;
1370 local_n_points++;
1371 }
1372 }
1373
1374 // ---- Part 2: Area calculation (INTERIOR cells only) ----
1375 for (PetscInt k = lzs; k < lze; k++) {
1376 for (PetscInt i = lxs; i < lxe; i++) {
1377 // nvert[k][j][i] = nvert[k][24][i] checks last cell (Cell 23)
1378 if (nvert[k][j][i] < 0.1) {
1379 // Face area = magnitude of eta metric at [k][24][i]
1380 localAreaSum += sqrt(eta[k][j][i].x * eta[k][j][i].x +
1381 eta[k][j][i].y * eta[k][j][i].y +
1382 eta[k][j][i].z * eta[k][j][i].z);
1383 }
1384 }
1385 }
1386 }
1387 break;
1388
1389 // ====================================================================
1390 // BC_FACE_NEG_Z: Face at k=0 (inlet, bottom boundary in k-direction)
1391 // ====================================================================
1392 case BC_FACE_NEG_Z:
1393 if (zs == 0) {
1394 PetscInt k = 0; // Face is at node index k=0
1395
1396 // ---- Part 1: Center calculation (ALL physical nodes) ----
1397 // For mx=26, my=26: i∈[0,24], j∈[0,24] → 25×25 = 625 nodes
1398 for (PetscInt j = ys; j < j_max; j++) {
1399 for (PetscInt i = xs; i < i_max; i++) {
1400 local_sum[0] += coor[k][j][i].x;
1401 local_sum[1] += coor[k][j][i].y;
1402 local_sum[2] += coor[k][j][i].z;
1403 local_n_points++;
1404 }
1405 }
1406
1407 // ---- Part 2: Area calculation (INTERIOR cells only) ----
1408 // For mx=26, my=26: i∈[1,24], j∈[1,24] → 24×24 = 576 cells
1409 for (PetscInt j = lys; j < lye; j++) {
1410 for (PetscInt i = lxs; i < lxe; i++) {
1411 // nvert[k+1][j][i] = nvert[1][j][i] checks Cell 0
1412 // (Physical Cell 0 in i-j plane, stored at shifted index [1])
1413 if (nvert[k+1][j][i] < 0.1) {
1414 // Face area = magnitude of zet metric at [0][j][i]
1415 localAreaSum += sqrt(zet[k][j][i].x * zet[k][j][i].x +
1416 zet[k][j][i].y * zet[k][j][i].y +
1417 zet[k][j][i].z * zet[k][j][i].z);
1418 }
1419 }
1420 }
1421 }
1422 break;
1423
1424 // ====================================================================
1425 // BC_FACE_POS_Z: Face at k=KM-1 (outlet, top boundary in k-direction)
1426 // ====================================================================
1427 case BC_FACE_POS_Z:
1428 if (ze == mz) {
1429 PetscInt k = mz - 2; // Last physical node (e.g., k=96 for mz=98)
1430
1431 // ---- Part 1: Center calculation (ALL physical nodes) ----
1432 // For mx=26, my=26: i∈[0,24], j∈[0,24] → 25×25 = 625 nodes
1433 for (PetscInt j = ys; j < j_max; j++) {
1434 for (PetscInt i = xs; i < i_max; i++) {
1435 local_sum[0] += coor[k][j][i].x;
1436 local_sum[1] += coor[k][j][i].y;
1437 local_sum[2] += coor[k][j][i].z;
1438 local_n_points++;
1439 }
1440 }
1441
1442 // ---- Part 2: Area calculation (INTERIOR cells only) ----
1443 // For mx=26, my=26: i∈[1,24], j∈[1,24] → 24×24 = 576 cells
1444 for (PetscInt j = lys; j < lye; j++) {
1445 for (PetscInt i = lxs; i < lxe; i++) {
1446 // nvert[k][j][i] = nvert[96][j][i] checks last cell (Cell 95)
1447 // (Physical Cell 95, stored at shifted index [96])
1448 if (nvert[k][j][i] < 0.1) {
1449 // Face area = magnitude of zet metric at [96][j][i]
1450 localAreaSum += sqrt(zet[k][j][i].x * zet[k][j][i].x +
1451 zet[k][j][i].y * zet[k][j][i].y +
1452 zet[k][j][i].z * zet[k][j][i].z);
1453 }
1454 }
1455 }
1456 }
1457 break;
1458
1459 default:
1460 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_OUTOFRANGE,
1461 "Unknown face_id %d in CalculateFaceCenterAndArea", face_id);
1462 }
1463
1464 // ========================================================================
1465 // Step 4: Restore array access (release pointers)
1466 // ========================================================================
1467 ierr = DMDAVecRestoreArrayRead(user->fda, lCoor, &coor); CHKERRQ(ierr);
1468 ierr = DMDAVecRestoreArrayRead(user->da, user->lNvert, &nvert); CHKERRQ(ierr);
1469 ierr = DMDAVecRestoreArrayRead(user->fda, user->lCsi, &csi); CHKERRQ(ierr);
1470 ierr = DMDAVecRestoreArrayRead(user->fda, user->lEta, &eta); CHKERRQ(ierr);
1471 ierr = DMDAVecRestoreArrayRead(user->fda, user->lZet, &zet); CHKERRQ(ierr);
1472 }
1473 // ========================================================================
1474 // Step 5: Perform MPI reductions to get global sums
1475 // ========================================================================
1476 // Sum coordinate contributions from all ranks
1477 ierr = MPI_Allreduce(local_sum, global_sum, 3, MPI_DOUBLE, MPI_SUM,
1478 PETSC_COMM_WORLD); CHKERRQ(ierr);
1479
1480 // Sum node counts from all ranks
1481 ierr = MPI_Allreduce(&local_n_points, &global_n_points, 1, MPI_COUNT, MPI_SUM,
1482 PETSC_COMM_WORLD); CHKERRQ(ierr);
1483
1484 // Sum area contributions from all ranks
1485 ierr = MPI_Allreduce(&localAreaSum, &globalAreaSum, 1, MPI_DOUBLE, MPI_SUM,
1486 PETSC_COMM_WORLD); CHKERRQ(ierr);
1487
1488 // ========================================================================
1489 // Step 6: Calculate geometric center by averaging coordinates
1490 // ========================================================================
1491 if (global_n_points > 0) {
1492 face_center->x = global_sum[0] / global_n_points;
1493 face_center->y = global_sum[1] / global_n_points;
1494 face_center->z = global_sum[2] / global_n_points;
1496 "Calculated center for Face %s: (x=%.4f, y=%.4f, z=%.4f) from %lld nodes\n",
1497 BCFaceToString(face_id),
1498 face_center->x, face_center->y, face_center->z,
1499 (long long)global_n_points);
1500 } else {
1501 // No nodes found - this should not happen for a valid face
1503 "WARNING: Face %s identified but no grid points found. Center not calculated.\n",
1504 BCFaceToString(face_id));
1505 face_center->x = face_center->y = face_center->z = 0.0;
1506 }
1507
1508 // ========================================================================
1509 // Step 7: Return computed total area
1510 // ========================================================================
1511 *face_area = globalAreaSum;
1513 "Calculated area for Face %s: Area=%.6f\n",
1514 BCFaceToString(face_id), *face_area);
1515
1516 PetscFunctionReturn(0);
1517}
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:126
const char * BCFaceToString(BCFace face)
Helper function to convert BCFace enum to a string representation.
Definition logging.c:669
Vec lNvert
Definition variables.h:904
Vec lZet
Definition variables.h:927
Vec lCsi
Definition variables.h:927
DMDALocalInfo info
Definition variables.h:883
Vec lEta
Definition variables.h:927
Here is the call graph for this function:
Here is the caller graph for this function: