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.
 
PetscErrorCode CreateCompatibleBlockDM (DM source, PetscInt dof, DM *result)
 Creates a DMDA sharing a block's decomposition at a different degree of freedom.
 

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)
Parse grid-generation options and store the resulting geometry settings in the context.
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:532
#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:253
#define GLOBAL
Scope for global logging across all processes.
Definition logging.h:46
#define LOG_ALLOW(scope, level, fmt,...)
Logging macro that checks both the log level and whether the calling function is in the allowed-funct...
Definition logging.h:200
#define PROFILE_FUNCTION_END
Marks the end of a profiled code block.
Definition logging.h:859
@ LOG_INFO
Informational messages about program execution.
Definition logging.h:31
@ LOG_DEBUG
Detailed debugging information.
Definition logging.h:32
#define PROFILE_FUNCTION_BEGIN
Marks the beginning of a profiled code block (typically a function).
Definition logging.h:850
UserCtx * user
Definition variables.h:571
PetscMPIInt rank
Definition variables.h:698
PetscInt block_number
Definition variables.h:790
UserMG usermg
Definition variables.h:852
char eulerianSource[PETSC_MAX_PATH_LEN]
Definition variables.h:715
PetscInt mglevels
Definition variables.h:578
char AnalyticalSolutionType[PETSC_MAX_PATH_LEN]
Definition variables.h:729
MGCtx * mgctx
Definition variables.h:581
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:

◆ 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 276 of file grid.c.

277{
278 PetscErrorCode ierr;
279 UserMG *usermg = &simCtx->usermg;
280 MGCtx *mgctx = usermg->mgctx;
281 PetscInt nblk = simCtx->block_number;
282
283 PetscFunctionBeginUser;
284
286
287 LOG_ALLOW(GLOBAL,LOG_INFO, "Pre-scanning BCs to identify domain periodicity.\n");
288 ierr = DeterminePeriodicity(simCtx); CHKERRQ(ierr);
289
290 LOG_ALLOW(GLOBAL, LOG_INFO, "Creating DMDA objects for all levels and blocks...\n");
291
292 // --- Part 1: Calculate Coarse Grid Dimensions & VALIDATE ---
293 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Calculating and validating coarse grid dimensions...\n");
294 for (PetscInt level = usermg->mglevels - 2; level >= 0; level--) {
295 for (PetscInt bi = 0; bi < nblk; bi++) {
296 UserCtx *user_coarse = &mgctx[level].user[bi];
297 UserCtx *user_fine = &mgctx[level + 1].user[bi];
298
299 user_coarse->IM = user_fine->isc ? user_fine->IM : (user_fine->IM + 1) / 2;
300 user_coarse->JM = user_fine->jsc ? user_fine->JM : (user_fine->JM + 1) / 2;
301 user_coarse->KM = user_fine->ksc ? user_fine->KM : (user_fine->KM + 1) / 2;
302
303 LOG_ALLOW_SYNC(LOCAL, LOG_TRACE, "Rank %d: Block %d, Level %d dims calculated: %d x %d x %d\n",
304 simCtx->rank, bi, level, user_coarse->IM, user_coarse->JM, user_coarse->KM);
305
306 // Validation check from legacy MGDACreate to ensure coarsening is possible
307 PetscInt check_i = user_coarse->IM * (2 - user_coarse->isc) - (user_fine->IM + 1 - user_coarse->isc);
308 PetscInt check_j = user_coarse->JM * (2 - user_coarse->jsc) - (user_fine->JM + 1 - user_coarse->jsc);
309 PetscInt check_k = user_coarse->KM * (2 - user_coarse->ksc) - (user_fine->KM + 1 - user_coarse->ksc);
310
311 if (check_i + check_j + check_k != 0) {
312 // SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONG,
313 // "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.",
314 // level, bi, user_fine->IM, user_fine->JM, user_fine->KM, user_coarse->IM, user_coarse->JM, user_coarse->KM);
315 LOG(GLOBAL,LOG_WARNING,"WARNING: Grid at level %d, block %d can't be consistently coarsened further.\n", level, bi);
316 }
317 }
318 }
319
320 // --- Part 2: Create DMs from Coarse to Fine for each Block ---
321 for (PetscInt bi = 0; bi < nblk; bi++) {
322 LOG_ALLOW_SYNC(GLOBAL, LOG_DEBUG, "--- Creating DMs for Block %d ---\n", bi);
323
324 // Create the coarsest level DM first (passing NULL for the coarse_user)
325 ierr = InitializeSingleGridDM(&mgctx[0].user[bi], NULL); CHKERRQ(ierr);
326
327 // Create finer level DMs, passing the next-coarser context for alignment
328 for (PetscInt level = 1; level < usermg->mglevels; level++) {
329 ierr = InitializeSingleGridDM(&mgctx[level].user[bi], &mgctx[level-1].user[bi]); CHKERRQ(ierr);
330 }
331 }
332
333 // --- Optional: View the finest DM for debugging verification ---
334 if (get_log_level() >= LOG_DEBUG) {
335 LOG_ALLOW_SYNC(GLOBAL, LOG_INFO, "--- Viewing Finest DMDA (Level %d, Block 0) ---\n", usermg->mglevels - 1);
336 ierr = DMView(mgctx[usermg->mglevels - 1].user[0].da, PETSC_VIEWER_STDOUT_WORLD); CHKERRQ(ierr);
337 }
338
339 LOG_ALLOW(GLOBAL, LOG_INFO, "DMDA object creation complete.\n");
340
342
343 PetscFunctionReturn(0);
344}
static PetscErrorCode InitializeSingleGridDM(UserCtx *user, UserCtx *coarse_user)
Create and configure one PETSc DMDA for a multigrid level.
Definition grid.c:142
PetscErrorCode DeterminePeriodicity(SimCtx *simCtx)
Scans all block-specific boundary condition files to determine a globally consistent periodicity for ...
Definition io.c:1024
#define LOCAL
Logging scope definitions for controlling message output.
Definition logging.h:45
#define LOG(scope, level, fmt,...)
Logging macro for PETSc-based applications with scope control.
Definition logging.h:84
LogLevel get_log_level()
Retrieves the current logging level from the environment variable LOG_LEVEL.
Definition logging.c:87
@ LOG_TRACE
Very fine-grained tracing information for in-depth debugging.
Definition logging.h:33
@ LOG_WARNING
Non-critical issues that warrant attention.
Definition logging.h:30
PetscInt isc
Definition variables.h:924
PetscInt ksc
Definition variables.h:924
PetscInt KM
Definition variables.h:920
PetscInt jsc
Definition variables.h:924
PetscInt JM
Definition variables.h:920
PetscInt IM
Definition variables.h:920
Context for Multigrid operations.
Definition variables.h:570
User-level context for managing the entire multigrid hierarchy.
Definition variables.h:577
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 358 of file grid.c.

359{
360 PetscErrorCode ierr;
361 UserMG *usermg = &simCtx->usermg;
362 PetscInt nblk = simCtx->block_number;
363
364 PetscFunctionBeginUser;
365
367
368 LOG_ALLOW(GLOBAL, LOG_INFO, "Assigning physical coordinates to all grid DMs...\n");
369
370 // --- Part 1: Populate the Finest Grid Level ---
371 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Setting coordinates for the finest grid level (%d)...\n", usermg->mglevels - 1);
372 for (PetscInt bi = 0; bi < nblk; bi++) {
373 UserCtx *fine_user = &usermg->mgctx[usermg->mglevels - 1].user[bi];
374 ierr = SetFinestLevelCoordinates(fine_user); CHKERRQ(ierr);
375 LOG_ALLOW(GLOBAL,LOG_TRACE,"The Finest level coordinates for block %d have been set.\n",bi);
377 ierr = LOG_FIELD_MIN_MAX(fine_user, FIELD_ID_COORDINATES);
378 }
379 }
380 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Finest level coordinates have been set for all blocks.\n");
381
382 // --- Part 2: Restrict Coordinates to Coarser Levels ---
383 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Restricting coordinates to coarser grid levels...\n");
384 for (PetscInt level = usermg->mglevels - 2; level >= 0; level--) {
385 for (PetscInt bi = 0; bi < nblk; bi++) {
386 UserCtx *coarse_user = &usermg->mgctx[level].user[bi];
387 UserCtx *fine_user = &usermg->mgctx[level + 1].user[bi];
388 ierr = RestrictCoordinates(coarse_user, fine_user); CHKERRQ(ierr);
389
390 LOG_ALLOW(GLOBAL,LOG_TRACE,"Coordinates restricted to block %d level %d.\n",bi,level);
392 ierr = LOG_FIELD_MIN_MAX(coarse_user, FIELD_ID_COORDINATES);
393 }
394 }
395 }
396
397 LOG_ALLOW(GLOBAL, LOG_INFO, "Physical coordinates assigned to all grid levels and blocks.\n");
398
400
401 PetscFunctionReturn(0);
402}
@ FIELD_ID_COORDINATES
static PetscErrorCode RestrictCoordinates(UserCtx *coarse_user, UserCtx *fine_user)
Restrict finest-grid coordinates onto each coarser multigrid level.
Definition grid.c:775
static PetscErrorCode SetFinestLevelCoordinates(UserCtx *user)
Attach the generated physical coordinates to the finest-level DMDA.
Definition grid.c:558
#define __FUNCT__
Definition grid.c:10
PetscErrorCode LOG_FIELD_MIN_MAX(UserCtx *user, FieldId field_id)
Computes and logs the local and global min/max values of a 3-component vector field.
Definition logging.c:2349
PetscBool is_function_allowed(const char *functionName)
Checks if a given function is in the allow-list.
Definition logging.c:186
@ LOG_VERBOSE
Extremely detailed logs, typically for development use only.
Definition logging.h:34
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 421 of file grid.c.

422{
423 const BCFace neg_faces[3] = {BC_FACE_NEG_X, BC_FACE_NEG_Y, BC_FACE_NEG_Z};
424 const BCFace pos_faces[3] = {BC_FACE_POS_X, BC_FACE_POS_Y, BC_FACE_POS_Z};
425 const char axis_names[3] = {'X', 'Y', 'Z'};
426 const Cmpnts ***coor = NULL;
427 Vec lcoor = NULL;
428 DMDALocalInfo info;
429
430 PetscFunctionBeginUser;
432 PetscCall(DMDAGetLocalInfo(user->da, &info));
433 PetscCall(DMGetCoordinatesLocal(user->da, &lcoor));
434 PetscCheck(lcoor != NULL, PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONGSTATE,
435 "Cannot validate periodic geometry before local coordinates are assigned.");
436 for (PetscInt axis = 0; axis < 3; axis++) {
437 const PetscBool neg_periodic =
438 user->boundary_faces[neg_faces[axis]].mathematical_type == PERIODIC;
439 const PetscBool pos_periodic =
440 user->boundary_faces[pos_faces[axis]].mathematical_type == PERIODIC;
441 PetscReal local_min[3] = {PETSC_MAX_REAL, PETSC_MAX_REAL, PETSC_MAX_REAL};
442 PetscReal local_max[3] = {-PETSC_MAX_REAL, -PETSC_MAX_REAL, -PETSC_MAX_REAL};
443 PetscReal global_min[3], global_max[3];
444 PetscInt local_count = 0, global_count = 0;
445
446 user->periodic_translation_valid[axis] = PETSC_FALSE;
447 user->periodic_translation[axis] = (Cmpnts){0.0, 0.0, 0.0};
448 if (!neg_periodic && !pos_periodic) continue;
449
450 PetscCheck(neg_periodic && pos_periodic, PETSC_COMM_WORLD, PETSC_ERR_USER_INPUT,
451 "Periodic geometry in the %c direction requires paired negative and positive faces.",
452 axis_names[axis]);
453 const PetscInt axis_size = axis == 0 ? info.mx : (axis == 1 ? info.my : info.mz);
454 PetscCheck(axis_size >= 5, PETSC_COMM_WORLD, PETSC_ERR_USER_INPUT,
455 "%c-periodic geometry on block %d level %d requires at least four physical "
456 "nodes in that direction; found %d.",
457 axis_names[axis], user->_this, user->thislevel, axis_size - 1);
458
459 PetscCall(DMDAVecGetArrayRead(user->fda, lcoor, &coor));
460 if (axis == 0 && info.xs == 0) {
461 for (PetscInt k = PetscMax(info.zs, 0); k < PetscMin(info.zs + info.zm, info.mz - 1); k++) {
462 for (PetscInt j = PetscMax(info.ys, 0); j < PetscMin(info.ys + info.ym, info.my - 1); j++) {
463 const Cmpnts delta = {
464 coor[k][j][-2].x - coor[k][j][0].x,
465 coor[k][j][-2].y - coor[k][j][0].y,
466 coor[k][j][-2].z - coor[k][j][0].z
467 };
468 for (PetscInt c = 0; c < 3; c++) {
469 local_min[c] = PetscMin(local_min[c], CoordinateComponent(delta, c));
470 local_max[c] = PetscMax(local_max[c], CoordinateComponent(delta, c));
471 }
472 local_count++;
473 }
474 }
475 } else if (axis == 1 && info.ys == 0) {
476 for (PetscInt k = PetscMax(info.zs, 0); k < PetscMin(info.zs + info.zm, info.mz - 1); k++) {
477 for (PetscInt i = PetscMax(info.xs, 0); i < PetscMin(info.xs + info.xm, info.mx - 1); i++) {
478 const Cmpnts delta = {
479 coor[k][-2][i].x - coor[k][0][i].x,
480 coor[k][-2][i].y - coor[k][0][i].y,
481 coor[k][-2][i].z - coor[k][0][i].z
482 };
483 for (PetscInt c = 0; c < 3; c++) {
484 local_min[c] = PetscMin(local_min[c], CoordinateComponent(delta, c));
485 local_max[c] = PetscMax(local_max[c], CoordinateComponent(delta, c));
486 }
487 local_count++;
488 }
489 }
490 } else if (axis == 2 && info.zs == 0) {
491 for (PetscInt j = PetscMax(info.ys, 0); j < PetscMin(info.ys + info.ym, info.my - 1); j++) {
492 for (PetscInt i = PetscMax(info.xs, 0); i < PetscMin(info.xs + info.xm, info.mx - 1); i++) {
493 const Cmpnts delta = {
494 coor[-2][j][i].x - coor[0][j][i].x,
495 coor[-2][j][i].y - coor[0][j][i].y,
496 coor[-2][j][i].z - coor[0][j][i].z
497 };
498 for (PetscInt c = 0; c < 3; c++) {
499 local_min[c] = PetscMin(local_min[c], CoordinateComponent(delta, c));
500 local_max[c] = PetscMax(local_max[c], CoordinateComponent(delta, c));
501 }
502 local_count++;
503 }
504 }
505 }
506 PetscCall(DMDAVecRestoreArrayRead(user->fda, lcoor, &coor));
507
508 PetscCallMPI(MPI_Allreduce(local_min, global_min, 3, MPIU_REAL, MPI_MIN, PETSC_COMM_WORLD));
509 PetscCallMPI(MPI_Allreduce(local_max, global_max, 3, MPIU_REAL, MPI_MAX, PETSC_COMM_WORLD));
510 PetscCallMPI(MPI_Allreduce(&local_count, &global_count, 1, MPIU_INT, MPI_SUM, PETSC_COMM_WORLD));
511 PetscCheck(global_count > 0, PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONGSTATE,
512 "No physical seam nodes were available to validate %c-periodic geometry.",
513 axis_names[axis]);
514
515 PetscReal translation[3];
516 PetscReal scale = 1.0;
517 PetscReal max_mismatch = 0.0;
518 for (PetscInt c = 0; c < 3; c++) {
519 translation[c] = 0.5 * (global_min[c] + global_max[c]);
520 scale = PetscMax(scale, PetscAbsReal(translation[c]));
521 max_mismatch = PetscMax(max_mismatch, global_max[c] - global_min[c]);
522 }
523 const PetscReal tolerance = 1.0e-9 * scale + 100.0 * PETSC_MACHINE_EPSILON;
524 const PetscReal magnitude = PetscSqrtReal(
525 PetscSqr(translation[0]) + PetscSqr(translation[1]) + PetscSqr(translation[2]));
526
527 PetscCheck(max_mismatch <= tolerance, PETSC_COMM_WORLD, PETSC_ERR_USER_INPUT,
528 "Unsupported %c-periodic geometry on block %d level %d: opposite physical "
529 "surfaces are not related by one constant translation. Maximum component "
530 "mismatch is %.12e (tolerance %.12e).",
531 axis_names[axis], user->_this, user->thislevel,
532 (double)max_mismatch, (double)tolerance);
533 PetscCheck(magnitude > tolerance, PETSC_COMM_WORLD, PETSC_ERR_USER_INPUT,
534 "Unsupported %c-periodic geometry on block %d level %d: seam translation "
535 "magnitude %.12e is zero or too small.",
536 axis_names[axis], user->_this, user->thislevel, (double)magnitude);
537
538 user->periodic_translation[axis] =
539 (Cmpnts){translation[0], translation[1], translation[2]};
540 user->periodic_translation_valid[axis] = PETSC_TRUE;
542 "Validated %c-periodic geometry for block %d level %d with translation "
543 "(%.12e, %.12e, %.12e).\n",
544 axis_names[axis], user->_this, user->thislevel,
545 (double)translation[0], (double)translation[1], (double)translation[2]);
546 }
547
549 PetscFunctionReturn(0);
550}
static PetscReal CoordinateComponent(Cmpnts value, PetscInt component)
Returns one Cartesian component from a coordinate/vector value.
Definition grid.c:407
@ PERIODIC
Definition variables.h:292
BoundaryFaceConfig boundary_faces[6]
Definition variables.h:931
PetscInt _this
Definition variables.h:924
PetscScalar x
Definition variables.h:103
PetscInt thislevel
Definition variables.h:988
PetscScalar z
Definition variables.h:103
PetscScalar y
Definition variables.h:103
Cmpnts periodic_translation[3]
Definition variables.h:927
PetscBool periodic_translation_valid[3]
Definition variables.h:928
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
A 3D point or vector with PetscScalar components.
Definition variables.h:102
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 850 of file grid.c.

851{
852 PetscErrorCode ierr;
853 PetscInt i, j, k;
854 PetscMPIInt rank;
855 PetscInt xs, ys, zs, xe, ye, ze;
856 DMDALocalInfo info;
857 Vec coordinates;
858 Cmpnts ***coordArray;
859 Cmpnts minCoords, maxCoords;
860
861 PetscFunctionBeginUser;
862
864
865 // Start of function execution
866 LOG_ALLOW(GLOBAL, LOG_INFO, "Entering the function.\n");
867
868 // Validate input Pointers
869 if (!user) {
870 LOG_ALLOW(LOCAL, LOG_ERROR, "Input 'user' Pointer is NULL.\n");
872 return PETSC_ERR_ARG_NULL;
873 }
874 if (!localBBox) {
875 LOG_ALLOW(LOCAL, LOG_ERROR, "Output 'localBBox' Pointer is NULL.\n");
877 return PETSC_ERR_ARG_NULL;
878 }
879
880 // Get MPI rank
881 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
882
883 // Get the local coordinates vector from the DMDA
884 ierr = DMGetCoordinatesLocal(user->da, &coordinates);
885 if (ierr) {
886 LOG_ALLOW(LOCAL, LOG_ERROR, "Error getting local coordinates vector.\n");
888 return ierr;
889 }
890
891 if (!coordinates) {
892 LOG_ALLOW(LOCAL, LOG_ERROR, "Coordinates vector is NULL.\n");
894 return PETSC_ERR_ARG_NULL;
895 }
896
897 // Access the coordinate array for reading
898 ierr = DMDAVecGetArrayRead(user->fda, coordinates, &coordArray);
899 if (ierr) {
900 LOG_ALLOW(LOCAL, LOG_ERROR, "Error accessing coordinate array.\n");
902 return ierr;
903 }
904
905 // Get the local grid information (indices and sizes)
906 ierr = DMDAGetLocalInfo(user->da, &info);
907 if (ierr) {
908 LOG_ALLOW(LOCAL, LOG_ERROR, "Error getting DMDA local info.\n");
910 return ierr;
911 }
912
913
914 xs = info.gxs; xe = xs + info.gxm;
915 ys = info.gys; ye = ys + info.gym;
916 zs = info.gzs; ze = zs + info.gzm;
917
918 /*
919 xs = info.xs; xe = xs + info.xm;
920 ys = info.ys; ye = ys + info.ym;
921 zs = info.zs; ze = zs + info.zm;
922 */
923
924 // Initialize min and max coordinates with extreme values
925 minCoords.x = minCoords.y = minCoords.z = PETSC_MAX_REAL;
926 maxCoords.x = maxCoords.y = maxCoords.z = PETSC_MIN_REAL;
927
928 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);
929
930 // Iterate over the local grid to find min and max coordinates
931 for (k = zs; k < ze; k++) {
932 for (j = ys; j < ye; j++) {
933 for (i = xs; i < xe; i++) {
934 // Only consider nodes within the physical domain.
935 if(i < user->IM && j < user->JM && k < user->KM){
936 Cmpnts coord = coordArray[k][j][i];
937
938 // Update min and max coordinates
939 if (coord.x < minCoords.x) minCoords.x = coord.x;
940 if (coord.y < minCoords.y) minCoords.y = coord.y;
941 if (coord.z < minCoords.z) minCoords.z = coord.z;
942
943 if (coord.x > maxCoords.x) maxCoords.x = coord.x;
944 if (coord.y > maxCoords.y) maxCoords.y = coord.y;
945 if (coord.z > maxCoords.z) maxCoords.z = coord.z;
946 }
947 }
948 }
949 }
950
951
952 // Add tolerance to bboxes.
953 minCoords.x = minCoords.x - BBOX_TOLERANCE;
954 minCoords.y = minCoords.y - BBOX_TOLERANCE;
955 minCoords.z = minCoords.z - BBOX_TOLERANCE;
956
957 maxCoords.x = maxCoords.x + BBOX_TOLERANCE;
958 maxCoords.y = maxCoords.y + BBOX_TOLERANCE;
959 maxCoords.z = maxCoords.z + BBOX_TOLERANCE;
960
961 LOG_ALLOW(LOCAL,LOG_DEBUG," Tolerance added to the limits: %.8e .\n",(PetscReal)BBOX_TOLERANCE);
962
963 // Log the computed min and max coordinates
964 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);
965
966
967
968 // Restore the coordinate array
969 ierr = DMDAVecRestoreArrayRead(user->fda, coordinates, &coordArray);
970 if (ierr) {
971 LOG_ALLOW(LOCAL, LOG_ERROR, "Error restoring coordinate array.\n");
973 return ierr;
974 }
975
976 // Set the local bounding box
977 localBBox->min_coords = minCoords;
978 localBBox->max_coords = maxCoords;
979
980 // Update the bounding box inside the UserCtx for consistency
981 user->bbox = *localBBox;
982
983 LOG_ALLOW(GLOBAL, LOG_INFO, "Exiting the function successfully.\n");
984
986
987 PetscFunctionReturn(0);
988}
#define BBOX_TOLERANCE
Definition grid.c:7
@ LOG_ERROR
Critical errors that may halt the program.
Definition logging.h:29
Cmpnts max_coords
Maximum x, y, z coordinates of the bounding box.
Definition variables.h:173
Cmpnts min_coords
Minimum x, y, z coordinates of the bounding box.
Definition variables.h:172
BoundingBox bbox
Definition variables.h:922
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 999 of file grid.c.

1000{
1001 PetscErrorCode ierr;
1002 PetscMPIInt rank, size;
1003 BoundingBox *bboxArray = NULL;
1004 BoundingBox localBBox;
1005
1006 PetscFunctionBeginUser;
1007
1009
1010 /* Validate */
1011 if (!user || !allBBoxes) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
1012 "GatherAllBoundingBoxes: NULL pointer");
1013
1014 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRMPI(ierr);
1015 ierr = MPI_Comm_size(PETSC_COMM_WORLD, &size); CHKERRMPI(ierr);
1016
1017 /* Compute local bbox */
1018 ierr = ComputeLocalBoundingBox(user, &localBBox); CHKERRQ(ierr);
1019
1020 /* Ensure everyone is synchronized before the gather */
1021 MPI_Barrier(PETSC_COMM_WORLD);
1023 "Rank %d: about to MPI_Gather(localBBox)\n", rank);
1024
1025 /* Allocate on root */
1026 if (rank == 0) {
1027 bboxArray = (BoundingBox*)malloc(size * sizeof(BoundingBox));
1028 if (!bboxArray) SETERRABORT(PETSC_COMM_WORLD, PETSC_ERR_MEM,
1029 "GatherAllBoundingBoxes: malloc failed");
1030 }
1031
1032 /* Collective: every rank must call */
1033 ierr = MPI_Gather(&localBBox, sizeof(BoundingBox), MPI_BYTE,
1034 bboxArray, sizeof(BoundingBox), MPI_BYTE,
1035 0, PETSC_COMM_WORLD);
1036 CHKERRMPI(ierr);
1037
1038 MPI_Barrier(PETSC_COMM_WORLD);
1040 "Rank %d: completed MPI_Gather(localBBox)\n", rank);
1041
1042 /* Return result */
1043 if (rank == 0) {
1044 *allBBoxes = bboxArray;
1045 } else {
1046 *allBBoxes = NULL;
1047 }
1048
1050
1051 PetscFunctionReturn(0);
1052}
PetscErrorCode ComputeLocalBoundingBox(UserCtx *user, BoundingBox *localBBox)
Implementation of ComputeLocalBoundingBox().
Definition grid.c:850
Defines a 3D axis-aligned bounding box.
Definition variables.h:171
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 1061 of file grid.c.

1062{
1063 PetscErrorCode ierr;
1064 (void)user;
1065 PetscMPIInt rank, size;
1066
1067 PetscFunctionBeginUser;
1068
1070
1071 if (!bboxlist) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
1072 "BroadcastAllBoundingBoxes: NULL pointer");
1073
1074 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRMPI(ierr);
1075 ierr = MPI_Comm_size(PETSC_COMM_WORLD, &size); CHKERRMPI(ierr);
1076
1077 /* Non-root ranks must allocate before the Bcast */
1078 if (rank != 0) {
1079 *bboxlist = (BoundingBox*)malloc(size * sizeof(BoundingBox));
1080 if (!*bboxlist) SETERRABORT(PETSC_COMM_WORLD, PETSC_ERR_MEM,
1081 "BroadcastAllBoundingBoxes: malloc failed");
1082 }
1083
1084 MPI_Barrier(PETSC_COMM_WORLD);
1086 "Rank %d: about to MPI_Bcast(%d boxes)\n", rank, size);
1087
1088 /* Collective: every rank must call */
1089 ierr = MPI_Bcast(*bboxlist, size * sizeof(BoundingBox), MPI_BYTE,
1090 0, PETSC_COMM_WORLD);
1091 CHKERRMPI(ierr);
1092
1093 MPI_Barrier(PETSC_COMM_WORLD);
1095 "Rank %d: completed MPI_Bcast(%d boxes)\n", rank, size);
1096
1097
1099
1100 PetscFunctionReturn(0);
1101}
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 1111 of file grid.c.

1112{
1113 PetscErrorCode ierr;
1114 BCFace inlet_face_id = -1;
1115 PetscBool inlet_found = PETSC_FALSE;
1116
1117 PetscFunctionBeginUser;
1119
1120 // 1. Identify the primary inlet face from the configuration
1121 for (int i = 0; i < 6; i++) {
1122 if (user->boundary_faces[i].mathematical_type == INLET) {
1123 inlet_face_id = user->boundary_faces[i].face_id;
1124 inlet_found = PETSC_TRUE;
1125 break; // Use the first inlet found
1126 }
1127 }
1128
1129 if (!inlet_found) {
1130 LOG_ALLOW(GLOBAL, LOG_INFO, "No INLET face found. Skipping inlet center calculation.\n");
1132 PetscFunctionReturn(0);
1133 }
1134
1135 Cmpnts inlet_center;
1136 PetscReal inlet_area;
1137
1138 // 2. Call the generic utility to compute the center and area of any face.
1139 ierr = CalculateFaceCenterAndArea(user,inlet_face_id,&inlet_center,&inlet_area); CHKERRQ(ierr);
1140
1141 // 3. Store results in the SimCtx
1142 user->simCtx->CMx_c = inlet_center.x;
1143 user->simCtx->CMy_c = inlet_center.y;
1144 user->simCtx->CMz_c = inlet_center.z;
1145 user->simCtx->AreaInSum = inlet_area;
1146
1148 "Rank[%d] Inlet Center: (%.6f, %.6f, %.6f), Area: %.6f\n",
1149 user->simCtx->rank, inlet_center.x, inlet_center.y, inlet_center.z, inlet_area);
1150
1152 PetscFunctionReturn(0);
1153
1154}
PetscErrorCode CalculateFaceCenterAndArea(UserCtx *user, BCFace face_id, Cmpnts *face_center, PetscReal *face_area)
Implementation of CalculateFaceCenterAndArea().
Definition grid.c:1207
@ INLET
Definition variables.h:290
SimCtx * simCtx
Back-pointer to the master simulation context.
Definition variables.h:909
PetscReal CMy_c
Definition variables.h:783
PetscReal CMz_c
Definition variables.h:783
PetscReal AreaInSum
Definition variables.h:815
PetscReal CMx_c
Definition variables.h:783
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 1164 of file grid.c.

1165{
1166 PetscErrorCode ierr;
1167 BCFace outlet_face_id = -1;
1168 PetscBool outlet_found = PETSC_FALSE;
1169 PetscFunctionBeginUser;
1171 // 1. Identify the primary outlet face from the configuration
1172 for (int i = 0; i < 6; i++) {
1173 if (user->boundary_faces[i].mathematical_type == OUTLET) {
1174 outlet_face_id = user->boundary_faces[i].face_id;
1175 outlet_found = PETSC_TRUE;
1176 break; // Use the first outlet found
1177 }
1178 }
1179 if (!outlet_found) {
1180 LOG_ALLOW(GLOBAL, LOG_INFO, "No OUTLET face found. Skipping outlet center calculation.\n");
1182 PetscFunctionReturn(0);
1183 }
1184 PetscReal outlet_area;
1185 Cmpnts outlet_center;
1186 // 2. Call the generic utility to compute the center and area of any face
1187 ierr = CalculateFaceCenterAndArea(user,outlet_face_id,&outlet_center,&outlet_area); CHKERRQ(ierr);
1188 // 3. Store results in the SimCtx
1189 user->simCtx->AreaOutSum = outlet_area;
1190
1192 "Outlet Center: (%.6f, %.6f, %.6f), Area: %.6f\n",
1193 outlet_center.x, outlet_center.y, outlet_center.z, outlet_area);
1194
1196 PetscFunctionReturn(0);
1197}
@ OUTLET
Definition variables.h:289
PetscReal AreaOutSum
Definition variables.h:815
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:1207

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 1207 of file grid.c.

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

◆ CreateCompatibleBlockDM()

PetscErrorCode CreateCompatibleBlockDM ( DM  source,
PetscInt  dof,
DM *  result 
)

Creates a DMDA sharing a block's decomposition at a different degree of freedom.

Fields whose component count is neither one nor three still have to live on the same index space as the block, so a pointwise loop can read da and write them at the same (i,j,k). Copying the source DM's sizes, boundary types, stencil, and — critically — its explicit per-rank ownership ranges makes the decomposition identical rather than merely similar. Letting PETSc re-decide the split would be free to disagree, and the loop would misalign silently.

Parameters
[in]sourceExisting block DMDA whose decomposition is mirrored.
[in]dofDegrees of freedom the new DM carries; must be positive.
[out]resultCreated DM; the caller owns it and must destroy it.
Returns
Zero on success, or a PETSc error for a null argument or non-positive dof.

Creates a DMDA sharing a block's decomposition at a different degree of freedom.

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

See also
CreateCompatibleBlockDM()

Definition at line 109 of file grid.c.

110{
111 PetscErrorCode ierr;
112 PetscInt dim = 0, M = 0, N = 0, P = 0, m = 0, n = 0, p = 0, source_dof = 0, stencil_width = 0;
113 DMBoundaryType bx = DM_BOUNDARY_NONE, by = DM_BOUNDARY_NONE, bz = DM_BOUNDARY_NONE;
114 DMDAStencilType stencil_type = DMDA_STENCIL_BOX;
115 const PetscInt *lx = NULL, *ly = NULL, *lz = NULL;
116
117 PetscFunctionBeginUser;
119 PetscCheck(source != NULL && result != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
120 "Source DM and output pointer are required.");
121 PetscCheck(dof > 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE,
122 "A compatible DM needs a positive degree of freedom, got %" PetscInt_FMT ".", dof);
123
124 ierr = DMDAGetInfo(source, &dim, &M, &N, &P, &m, &n, &p, &source_dof, &stencil_width,
125 &bx, &by, &bz, &stencil_type); CHKERRQ(ierr);
126 ierr = DMDAGetOwnershipRanges(source, &lx, &ly, &lz); CHKERRQ(ierr);
127 ierr = DMDACreate3d(PetscObjectComm((PetscObject)source), bx, by, bz, stencil_type,
128 M, N, P, m, n, p, dof, stencil_width, lx, ly, lz, result); CHKERRQ(ierr);
129 ierr = DMSetUp(*result); CHKERRQ(ierr);
131 "Created a dof-%d DM mirroring the block decomposition (%dx%dx%d ranks).\n",
132 (int)dof, (int)m, (int)n, (int)p);
134 PetscFunctionReturn(0);
135}
Here is the caller graph for this function: