PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
Loading...
Searching...
No Matches
Functions
Boundaries.h File Reference
#include <petscpf.h>
#include <petscdmswarm.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <petsctime.h>
#include <petscsys.h>
#include <petscdmcomposite.h>
#include <petscsystypes.h>
#include "variables.h"
#include "ParticleSwarm.h"
#include "walkingsearch.h"
#include "grid.h"
#include "logging.h"
#include "io.h"
#include "interpolation.h"
#include "ParticleMotion.h"
#include "BC_Handlers.h"
#include "wallfunction.h"
Include dependency graph for Boundaries.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

PetscErrorCode BoundarySystem_Validate (UserCtx *user)
 (Public) Validates the consistency and compatibility of the parsed boundary condition system.
 
PetscErrorCode BoundaryCondition_Create (BCHandlerType handler_type, BoundaryCondition **new_bc_ptr)
 (Private) Creates and configures a specific BoundaryCondition handler object.
 
PetscErrorCode BoundarySystem_Initialize (UserCtx *user, const char *bcs_filename)
 Initializes the entire boundary system.
 
PetscErrorCode PropagateBoundaryConfigToCoarserLevels (SimCtx *simCtx)
 Propagates boundary condition configuration from finest to all coarser multigrid levels.
 
PetscErrorCode BoundarySystem_ExecuteStep (UserCtx *user)
 Executes one full boundary condition update cycle for a time step.
 
PetscErrorCode BoundarySystem_RefreshUbcs (UserCtx *user)
 (Private) A lightweight execution engine that calls the UpdateUbcs() method on all relevant handlers.
 
PetscErrorCode BoundarySystem_Destroy (UserCtx *user)
 Cleans up and destroys all boundary system resources.
 
PetscErrorCode CanRankServiceInletFace (UserCtx *user, const DMDALocalInfo *info, PetscInt IM_nodes_global, PetscInt JM_nodes_global, PetscInt KM_nodes_global, PetscBool *can_service_inlet_out)
 Determines if the current MPI rank owns any part of the globally defined inlet face, making it responsible for placing particles on that portion of the surface.
 
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.
 
PetscErrorCode GetDeterministicFaceGridLocation (UserCtx *user, const DMDALocalInfo *info, PetscInt xs_gnode_rank, PetscInt ys_gnode_rank, PetscInt zs_gnode_rank, PetscInt IM_cells_global, PetscInt JM_cells_global, PetscInt KM_cells_global, PetscInt64 particle_global_id, PetscInt *ci_metric_lnode_out, PetscInt *cj_metric_lnode_out, PetscInt *ck_metric_lnode_out, PetscReal *xi_metric_logic_out, PetscReal *eta_metric_logic_out, PetscReal *zta_metric_logic_out, PetscBool *placement_successful_out)
 Places particles in a deterministic grid/raster pattern on a specified domain face.
 
PetscErrorCode GetRandomCellAndLogicalCoordsOnInletFace (UserCtx *user, const DMDALocalInfo *info, PetscInt xs_gnode_rank, PetscInt ys_gnode_rank, PetscInt zs_gnode_rank, PetscInt IM_nodes_global, PetscInt JM_nodes_global, PetscInt KM_nodes_global, PetscRandom *rand_logic_i_ptr, PetscRandom *rand_logic_j_ptr, PetscRandom *rand_logic_k_ptr, PetscInt *ci_metric_lnode_out, PetscInt *cj_metric_lnode_out, PetscInt *ck_metric_lnode_out, PetscReal *xi_metric_logic_out, PetscReal *eta_metric_logic_out, PetscReal *zta_metric_logic_out)
 Assuming the current rank services the inlet face, this function selects a random cell (owned by this rank on that face) and random logical coordinates within that cell, suitable for placing a particle on the inlet surface.
 
PetscErrorCode EnforceRHSBoundaryConditions (UserCtx *user)
 Enforces boundary conditions on the momentum equation's Right-Hand-Side (RHS) vector.
 
PetscErrorCode TransferPeriodicFieldByDirection (UserCtx *user, const char *field_name, char direction)
 (Private Worker) Copies periodic data for a SINGLE field in a SINGLE direction.
 
PetscErrorCode SynchronizePeriodicCellFields (UserCtx *user, PetscInt num_fields, const char *field_names[])
 Synchronizes periodic endpoint cells for a list of cell-centered fields.
 
PetscErrorCode TransferPeriodicFaceFieldByDirection (UserCtx *user, const char *field_name, char face_direction, char periodic_direction)
 Transfers one persistent single-face-family field in one periodic direction.
 
PetscErrorCode SynchronizePeriodicFaceFields (UserCtx *user, char face_direction, PetscInt num_fields, const char *field_names[])
 Synchronizes persistent fields belonging to one face family.
 
PetscErrorCode TransferPeriodicStaggeredFieldByDirection (UserCtx *user, const char *field_name, char periodic_direction)
 Transfers one persistent component-staggered field in one periodic direction.
 
PetscErrorCode SynchronizePeriodicStaggeredFields (UserCtx *user, PetscInt num_fields, const char *field_names[])
 Synchronizes persistent component-staggered vector fields.
 
PetscErrorCode PreparePeriodicQuickStencilFields (UserCtx *user, Vec local_vector_field, Vec local_scalar_field)
 Repairs the outer adjacent periodic ghosts used by QUICK cell stencils.
 
PetscErrorCode SynchronizePeriodicLocalStaggeredField (UserCtx *user, Vec local_field)
 Synchronizes one local-only component-staggered periodic work field.
 
PetscErrorCode TransferPeriodicField (UserCtx *user, const char *field_name)
 Legacy monolithic periodic endpoint transfer for one cell-centered field.
 
PetscErrorCode TransferPeriodicFaceField (UserCtx *user, const char *field_name)
 (Primitive) Copies periodic data from the interior to the local ghost cell region for a single field.
 
PetscErrorCode ApplyMetricsPeriodicBCs (UserCtx *user)
 (Orchestrator) Updates all metric-related fields in the local ghost cell regions for periodic boundaries.
 
PetscErrorCode ApplyPeriodicBCs (UserCtx *user)
 Applies periodic boundary conditions by copying data across domain boundaries for all relevant fields.
 
PetscErrorCode UpdateDummyCells (UserCtx *user)
 Updates the dummy cells (ghost nodes) on the faces of the local domain for NON-PERIODIC boundaries.
 
PetscErrorCode UpdateCornerNodes (UserCtx *user)
 Updates the corner and edge ghost nodes of the local domain by averaging.
 
PetscErrorCode UpdatePeriodicCornerNodes (UserCtx *user, PetscInt num_fields, const char *field_names[])
 Legacy sequential periodic-corner update for a list of fields.
 
PetscErrorCode ApplyWallFunction (UserCtx *user)
 Applies wall function modeling to near-wall velocities for all wall-type boundaries.
 
PetscErrorCode FinalizePostProjectionCellFields (UserCtx *user)
 Finalizes cell-centered fields after the projection step.
 
PetscErrorCode ApplyBoundaryConditions (UserCtx *user)
 Main boundary-condition orchestrator executed during solver timestepping.
 

Function Documentation

◆ BoundarySystem_Validate()

PetscErrorCode BoundarySystem_Validate ( UserCtx user)

(Public) Validates the consistency and compatibility of the parsed boundary condition system.

This function is the main entry point for all boundary condition validation. It should be called from the main setup sequence AFTER the configuration file has been parsed by ParseAllBoundaryConditions but BEFORE any BoundaryCondition handler objects are created.

It acts as a dispatcher, calling specialized private sub-validators for different complex BC setups (like driven flow) to ensure the combination of mathematical_type and handler_type across all six faces is physically and numerically valid. This provides a "fail-fast" mechanism to prevent users from running improperly configured simulations.

Parameters
userThe UserCtx for a single block, containing the populated boundary_faces configuration.
Returns
PetscErrorCode 0 on success, non-zero PETSc error code on failure.

(Public) Validates the consistency and compatibility of the parsed boundary condition system.

Local to this translation unit.

Definition at line 830 of file Boundaries.c.

831{
832 PetscErrorCode ierr;
833 const BCFace neg_faces[3] = {BC_FACE_NEG_X, BC_FACE_NEG_Y, BC_FACE_NEG_Z};
834 const BCFace pos_faces[3] = {BC_FACE_POS_X, BC_FACE_POS_Y, BC_FACE_POS_Z};
835 const char axis_names[3] = {'X', 'Y', 'Z'};
836 DMBoundaryType bx, by, bz;
837 PetscBool dm_periodic[3];
838 PetscFunctionBeginUser;
839
840 LOG_ALLOW(GLOBAL, LOG_INFO, "Validating parsed boundary condition configuration...\n");
841 ierr = DMDAGetInfo(user->da, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
842 &bx, &by, &bz, NULL); CHKERRQ(ierr);
843 dm_periodic[0] = (PetscBool)(bx == DM_BOUNDARY_PERIODIC);
844 dm_periodic[1] = (PetscBool)(by == DM_BOUNDARY_PERIODIC);
845 dm_periodic[2] = (PetscBool)(bz == DM_BOUNDARY_PERIODIC);
846
847 // --- Rule Set 1: Geometric periodic faces must be paired and match the DM topology. ---
848 for (PetscInt axis = 0; axis < 3; axis++) {
849 const PetscBool neg_periodic =
850 user->boundary_faces[neg_faces[axis]].mathematical_type == PERIODIC;
851 const PetscBool pos_periodic =
852 user->boundary_faces[pos_faces[axis]].mathematical_type == PERIODIC;
853
854 PetscCheck(neg_periodic == pos_periodic, PETSC_COMM_WORLD, PETSC_ERR_USER_INPUT,
855 "Configuration Error: Periodic boundaries in the %c direction must be paired; "
856 "%s is %s while %s is %s.",
857 axis_names[axis],
858 BCFaceToString(neg_faces[axis]), neg_periodic ? "PERIODIC" : "not periodic",
859 BCFaceToString(pos_faces[axis]), pos_periodic ? "PERIODIC" : "not periodic");
860 PetscCheck(dm_periodic[axis] == neg_periodic, PETSC_COMM_WORLD, PETSC_ERR_USER_INPUT,
861 "Configuration Error: The %c-direction DM periodic flag (%d) does not match "
862 "the paired boundary configuration (%s).",
863 axis_names[axis], (int)dm_periodic[axis], neg_periodic ? "PERIODIC" : "not periodic");
864 }
865
866 // --- Rule Set 2: Driven Flow Handler Consistency ---
867 // This specialized validator will check all rules related to driven flow handlers.
868 ierr = Validate_DrivenFlowConfiguration(user); CHKERRQ(ierr);
869
870 // --- Rule Set 3: (Future Extension) Overset Interface Consistency ---
871 // ierr = Validate_OversetConfiguration(user); CHKERRQ(ierr);
872
873 LOG_ALLOW(GLOBAL, LOG_INFO, "Boundary configuration is valid.\n");
874
875 PetscFunctionReturn(0);
876}
PetscErrorCode Validate_DrivenFlowConfiguration(UserCtx *user)
(Private) Validates all consistency rules for a driven flow (channel/pipe) setup.
Definition BC_Handlers.c:15
#define GLOBAL
Scope for global logging across all processes.
Definition logging.h:45
const char * BCFaceToString(BCFace face)
Helper function to convert BCFace enum to a string representation.
Definition logging.c:669
#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
@ LOG_INFO
Informational messages about program execution.
Definition logging.h:30
@ PERIODIC
Definition variables.h:290
BoundaryFaceConfig boundary_faces[6]
Definition variables.h:896
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
Here is the call graph for this function:
Here is the caller graph for this function:

◆ BoundaryCondition_Create()

PetscErrorCode BoundaryCondition_Create ( BCHandlerType  handler_type,
BoundaryCondition **  new_bc_ptr 
)

(Private) Creates and configures a specific BoundaryCondition handler object.

This function acts as a factory. Based on the requested handler_type, it allocates a BoundaryCondition object and populates it with the correct set of function pointers corresponding to that specific behavior.

Parameters
handler_typeThe specific handler to create (e.g., BC_HANDLER_WALL_NOSLIP).
[out]new_bc_ptrA pointer to where the newly created BoundaryCondition object's address will be stored.
Returns
PetscErrorCode 0 on success.

(Private) Creates and configures a specific BoundaryCondition handler object.

Local to this translation unit.

Definition at line 744 of file Boundaries.c.

745{
746 PetscErrorCode ierr;
747 PetscFunctionBeginUser;
748
749 const char* handler_name = BCHandlerTypeToString(handler_type);
750 LOG_ALLOW(LOCAL, LOG_DEBUG, "Factory called for handler type %s. \n", handler_name);
751
752 ierr = PetscMalloc1(1, new_bc_ptr); CHKERRQ(ierr);
753 BoundaryCondition *bc = *new_bc_ptr;
754
755 bc->type = handler_type;
756 bc->priority = -1; // Default priority; can be overridden in specific handlers
757 bc->data = NULL;
758 bc->Initialize = NULL;
759 bc->PreStep = NULL;
760 bc->Apply = NULL;
761 bc->PostStep = NULL;
762 bc->UpdateUbcs = NULL;
763 bc->Destroy = NULL;
764
765 LOG_ALLOW(LOCAL, LOG_DEBUG, "Allocated generic handler object at address %p.\n", (void*)bc);
766
767 switch (handler_type) {
768
770 LOG_ALLOW(LOCAL, LOG_DEBUG, "Dispatching to Create_OutletConservation().\n");
771 ierr = Create_OutletConservation(bc); CHKERRQ(ierr);
772 break;
773
775 LOG_ALLOW(LOCAL, LOG_DEBUG, "Dispatching to Create_WallNoSlip().\n");
776 ierr = Create_WallNoSlip(bc); CHKERRQ(ierr);
777 break;
778
780 LOG_ALLOW(LOCAL, LOG_DEBUG, "Dispatching to Create_InletConstantVelocity().\n");
781 ierr = Create_InletConstantVelocity(bc); CHKERRQ(ierr);
782 break;
783
785 LOG_ALLOW(LOCAL,LOG_DEBUG,"Dispatching to Create_PeriodicGeometric().\n");
786 ierr = Create_PeriodicGeometric(bc);
787 break;
788
790 LOG_ALLOW(LOCAL,LOG_DEBUG,"Dispatching to Create_PeriodicDrivenConstant().\n");
792 break;
793
794 //case BC_HANDLER_PERIODIC_DRIVEN_INITIAL_FLUX:
795 // LOG_ALLOW(LOCAL,LOG_DEBUG,"Dispatching to Create_PeriodicDrivenInitial().\n");
796 // ierr = Create_PeriodicDrivenInitial(bc);
797 // break;
798
800 LOG_ALLOW(LOCAL, LOG_DEBUG, "Dispatching to Create_InletParabolicProfile().\n");
801 ierr = Create_InletParabolicProfile(bc); CHKERRQ(ierr);
802 break;
803
805 LOG_ALLOW(LOCAL, LOG_DEBUG, "Dispatching to Create_InletProfileFromFile().\n");
806 ierr = Create_InletProfileFromFile(bc); CHKERRQ(ierr);
807 break;
808 //Add cases for other handlers here in future phases
809
810 default:
811 LOG_ALLOW(GLOBAL, LOG_ERROR, "Handler type (%s) is not recognized or implemented in the factory.\n", handler_name);
812 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_UNKNOWN_TYPE, "Boundary handler type %d (%s) not recognized in factory.\n", handler_type, handler_name);
813 }
814
815 if(bc->priority < 0) {
816 LOG_ALLOW(GLOBAL, LOG_ERROR, "Handler type %d (%s) did not set a valid priority during creation.\n", handler_type, handler_name);
817 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_UNKNOWN_TYPE, "Boundary handler type %d (%s) did not set a valid priority during creation.\n", handler_type, handler_name);
818 }
819
820 LOG_ALLOW(LOCAL, LOG_DEBUG, "Successfully created and configured handler for %s.\n", handler_name);
821 PetscFunctionReturn(0);
822}
PetscErrorCode Create_InletConstantVelocity(BoundaryCondition *bc)
Configures a BoundaryCondition object to behave as a constant velocity inlet.
PetscErrorCode Create_InletProfileFromFile(BoundaryCondition *bc)
Configures a BoundaryCondition object for a file-prescribed inlet profile.
PetscErrorCode Create_PeriodicGeometric(BoundaryCondition *bc)
Configures a BoundaryCondition object for geometric periodic coupling.
PetscErrorCode Create_InletParabolicProfile(BoundaryCondition *bc)
Configures a BoundaryCondition object for a parabolic inlet profile.
PetscErrorCode Create_PeriodicDrivenConstant(BoundaryCondition *bc)
Configures a BoundaryCondition object for periodic driven-flow forcing.
PetscErrorCode Create_WallNoSlip(BoundaryCondition *bc)
Configures a BoundaryCondition object to behave as a no-slip, stationary wall.
PetscErrorCode Create_OutletConservation(BoundaryCondition *bc)
Configures a BoundaryCondition object for conservative outlet treatment.
const char * BCHandlerTypeToString(BCHandlerType handler_type)
Converts a BCHandlerType enum to its string representation.
Definition logging.c:792
#define LOCAL
Logging scope definitions for controlling message output.
Definition logging.h:44
@ LOG_ERROR
Critical errors that may halt the program.
Definition logging.h:28
@ LOG_DEBUG
Detailed debugging information.
Definition logging.h:31
The "virtual table" struct for a boundary condition handler object.
Definition variables.h:351
PetscErrorCode(* PostStep)(BoundaryCondition *self, BCContext *ctx, PetscReal *local_inflow, PetscReal *local_outflow)
Definition variables.h:358
PetscErrorCode(* PreStep)(BoundaryCondition *self, BCContext *ctx, PetscReal *local_inflow, PetscReal *local_outflow)
Definition variables.h:356
BCHandlerType type
Definition variables.h:352
PetscErrorCode(* Destroy)(BoundaryCondition *self)
Definition variables.h:360
PetscErrorCode(* Initialize)(BoundaryCondition *self, BCContext *ctx)
Definition variables.h:355
PetscErrorCode(* UpdateUbcs)(BoundaryCondition *self, BCContext *ctx)
Definition variables.h:359
PetscErrorCode(* Apply)(BoundaryCondition *self, BCContext *ctx)
Definition variables.h:357
BCPriorityType priority
Definition variables.h:353
@ BC_HANDLER_PERIODIC_GEOMETRIC
Definition variables.h:314
@ BC_HANDLER_INLET_PARABOLIC
Definition variables.h:307
@ BC_HANDLER_INLET_CONSTANT_VELOCITY
Definition variables.h:306
@ BC_HANDLER_PERIODIC_DRIVEN_CONSTANT_FLUX
Definition variables.h:316
@ BC_HANDLER_INLET_PROFILE_FROM_FILE
Definition variables.h:308
@ BC_HANDLER_WALL_NOSLIP
Definition variables.h:303
@ BC_HANDLER_OUTLET_CONSERVATION
Definition variables.h:312
Here is the call graph for this function:
Here is the caller graph for this function:

◆ BoundarySystem_Initialize()

PetscErrorCode BoundarySystem_Initialize ( UserCtx user,
const char *  bcs_filename 
)

Initializes the entire boundary system.

Parameters
userThe
bcs_filenameThe
Returns
PetscErrorCode 0 on success.

Initializes the entire boundary system.

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

See also
BoundarySystem_Initialize()

Definition at line 891 of file Boundaries.c.

892{
893 PetscErrorCode ierr;
894 PetscFunctionBeginUser;
895
896 LOG_ALLOW(GLOBAL, LOG_INFO, "Starting creation and initialization of all boundary handlers.\n");
897
898 // =========================================================================
899 // Step 0: Clear any existing boundary handlers (if re-initializing).
900 // This ensures no memory leaks if this function is called multiple times.
901 // =========================================================================
902 for (int i = 0; i < 6; i++) {
903 BoundaryFaceConfig *face_cfg = &user->boundary_faces[i];
904 if (face_cfg->handler) {
905 LOG_ALLOW(LOCAL, LOG_DEBUG, "Destroying existing handler on Face %s before re-initialization.\n", BCFaceToString((BCFace)i));
906 if (face_cfg->handler->Destroy) {
907 ierr = face_cfg->handler->Destroy(face_cfg->handler); CHKERRQ(ierr);
908 }
909 ierr = PetscFree(face_cfg->handler); CHKERRQ(ierr);
910 face_cfg->handler = NULL;
911 }
912 }
913 // =========================================================================
914
915 // Step 0.1: Initiate flux sums to zero
916 user->simCtx->FluxInSum = 0.0;
917 user->simCtx->FluxOutSum = 0.0;
918 user->simCtx->FarFluxInSum = 0.0;
919 user->simCtx->FarFluxOutSum = 0.0;
920 // =========================================================================
921
922 // Step 1: Parse the configuration file to determine user intent.
923 // This function, defined in io.c, populates the configuration enums and parameter
924 // lists within the user->boundary_faces array on all MPI ranks.
925 ierr = ParseAllBoundaryConditions(user, bcs_filename); CHKERRQ(ierr);
926 LOG_ALLOW(GLOBAL, LOG_INFO, "Configuration file '%s' parsed successfully.\n", bcs_filename);
927
928 // Step 1.1: Validate the parsed configuration to ensure there are no Boundary Condition conflicts
929 ierr = BoundarySystem_Validate(user); CHKERRQ(ierr);
930
931 // Step 2: Create and Initialize the handler object for each of the 6 faces.
932 for (int i = 0; i < 6; i++) {
933 BoundaryFaceConfig *face_cfg = &user->boundary_faces[i];
934
935 const char *face_name = BCFaceToString(face_cfg->face_id);
936 const char *type_name = BCTypeToString(face_cfg->mathematical_type);
937 const char *handler_name = BCHandlerTypeToString(face_cfg->handler_type);
938
939 LOG_ALLOW(LOCAL, LOG_DEBUG, "Creating handler for Face %s with Type %s and handler '%s'.\n", face_name, type_name,handler_name);
940
941 // Use the private factory to construct the correct handler object based on the parsed type.
942 // The factory returns a pointer to the new handler object, which we store in the config struct.
943 ierr = BoundaryCondition_Create(face_cfg->handler_type, &face_cfg->handler); CHKERRQ(ierr);
944
945 // Step 3: Call the specific Initialize() method for the newly created handler.
946 // This allows the handler to perform its own setup, like reading parameters from the
947 // face_cfg->params list and setting the initial field values on its face.
948 if (face_cfg->handler && face_cfg->handler->Initialize) {
949 LOG_ALLOW(LOCAL, LOG_DEBUG, "Calling Initialize() method for handler %s(%s) on Face %s.\n",type_name,handler_name,face_name);
950
951 // Prepare the context needed by the Initialize() function.
952 BCContext ctx = {
953 .user = user,
954 .face_id = face_cfg->face_id,
955 .global_inflow_sum = &user->simCtx->FluxInSum, // Global flux sums are not relevant during initialization.
956 .global_outflow_sum = &user->simCtx->FluxOutSum,
957 .global_farfield_inflow_sum = &user->simCtx->FarFluxInSum,
958 .global_farfield_outflow_sum = &user->simCtx->FarFluxOutSum
959 };
960
961 ierr = face_cfg->handler->Initialize(face_cfg->handler, &ctx); CHKERRQ(ierr);
962 } else {
963 LOG_ALLOW(LOCAL, LOG_DEBUG, "Handler %s(%s) for Face %s has no Initialize() method, skipping.\n", type_name,handler_name,face_name);
964 }
965 }
966 // =========================================================================
967 // NO SYNCHRONIZATION NEEDED HERE
968 // =========================================================================
969 // Initialize() only reads parameters and allocates memory.
970 // It does NOT modify field values (Ucat, Ucont, Ubcs).
971 // Field values are set by:
972 // 1. Initial conditions (before this function)
973 // 2. Apply() during timestepping (after this function)
974 // The first call to ApplyBoundaryConditions() will handle synchronization.
975 // =========================================================================
976
977 LOG_ALLOW(GLOBAL, LOG_INFO, "All boundary handlers created and initialized successfully.\n");
978 PetscFunctionReturn(0);
979}
PetscErrorCode BoundarySystem_Validate(UserCtx *user)
Internal helper implementation: BoundarySystem_Validate().
Definition Boundaries.c:830
PetscErrorCode BoundaryCondition_Create(BCHandlerType handler_type, BoundaryCondition **new_bc_ptr)
Internal helper implementation: BoundaryCondition_Create().
Definition Boundaries.c:744
PetscErrorCode ParseAllBoundaryConditions(UserCtx *user, const char *bcs_input_filename)
Parses the boundary conditions file to configure the type, handler, and any associated parameters for...
Definition io.c:450
const char * BCTypeToString(BCType type)
Helper function to convert BCType enum to a string representation.
Definition logging.c:772
PetscReal FarFluxInSum
Definition variables.h:777
PetscReal FarFluxOutSum
Definition variables.h:777
SimCtx * simCtx
Back-pointer to the master simulation context.
Definition variables.h:879
PetscReal FluxOutSum
Definition variables.h:777
BCHandlerType handler_type
Definition variables.h:367
UserCtx * user
Definition variables.h:342
PetscReal FluxInSum
Definition variables.h:777
BoundaryCondition * handler
Definition variables.h:369
Provides execution context for a boundary condition handler.
Definition variables.h:341
Holds the complete configuration for one of the six boundary faces.
Definition variables.h:364
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PropagateBoundaryConfigToCoarserLevels()

PetscErrorCode PropagateBoundaryConfigToCoarserLevels ( SimCtx simCtx)

Propagates boundary condition configuration from finest to all coarser multigrid levels.

Coarser levels need BC type information for geometric operations (e.g., periodic corrections) but do NOT need full handler objects since timestepping only occurs at the finest level. This function copies the boundary_faces configuration down the hierarchy.

Parameters
simCtxThe master SimCtx containing the multigrid hierarchy
Returns
PetscErrorCode 0 on success

Propagates boundary condition configuration from finest to all coarser multigrid levels.

Local to this translation unit.

Definition at line 988 of file Boundaries.c.

989{
990 PetscErrorCode ierr;
991 UserMG *usermg = &simCtx->usermg;
992
993 PetscFunctionBeginUser;
995
996 LOG_ALLOW(GLOBAL, LOG_INFO, "Propagating BC configuration from finest to coarser multigrid levels...\n");
997
998 // Loop from second-finest down to coarsest
999 for (PetscInt level = usermg->mglevels - 2; level >= 0; level--) {
1000 for (PetscInt bi = 0; bi < simCtx->block_number; bi++) {
1001 UserCtx *user_coarse = &usermg->mgctx[level].user[bi];
1002 UserCtx *user_fine = &usermg->mgctx[level + 1].user[bi];
1003
1004 LOG_ALLOW_SYNC(LOCAL, LOG_DEBUG, "Rank %d: Copying BC config from level %d to level %d, block %d\n",
1005 simCtx->rank, level + 1, level, bi);
1006
1007 // Copy the 6 boundary face configurations
1008 for (int face_i = 0; face_i < 6; face_i++) {
1009 user_coarse->boundary_faces[face_i].face_id = user_fine->boundary_faces[face_i].face_id;
1010 user_coarse->boundary_faces[face_i].mathematical_type = user_fine->boundary_faces[face_i].mathematical_type;
1011 user_coarse->boundary_faces[face_i].handler_type = user_fine->boundary_faces[face_i].handler_type;
1012
1013 // Copy parameter list (deep copy)
1014 FreeBC_ParamList(user_coarse->boundary_faces[face_i].params); // Clear any existing
1015 user_coarse->boundary_faces[face_i].params = NULL;
1016
1017 BC_Param **dst_next = &user_coarse->boundary_faces[face_i].params;
1018 for (BC_Param *src = user_fine->boundary_faces[face_i].params; src; src = src->next) {
1019 BC_Param *new_param;
1020 ierr = PetscMalloc1(1, &new_param); CHKERRQ(ierr);
1021 ierr = PetscStrallocpy(src->key, &new_param->key); CHKERRQ(ierr);
1022 ierr = PetscStrallocpy(src->value, &new_param->value); CHKERRQ(ierr);
1023 new_param->next = NULL;
1024 *dst_next = new_param;
1025 dst_next = &new_param->next;
1026 }
1027
1028 // IMPORTANT: Do NOT create handler objects for coarser levels
1029 // Handlers are only needed at finest level for timestepping Apply() calls
1030 user_coarse->boundary_faces[face_i].handler = NULL;
1031 }
1032
1033 // Propagate the particle inlet lookup fields to coarse levels as well.
1034 user_coarse->inletFaceDefined = user_fine->inletFaceDefined;
1035 user_coarse->identifiedInletBCFace = user_fine->identifiedInletBCFace;
1036 }
1037 }
1038
1039 LOG_ALLOW(GLOBAL, LOG_INFO, "BC configuration propagation complete.\n");
1040
1042 PetscFunctionReturn(0);
1043}
void FreeBC_ParamList(BC_Param *head)
Frees an entire linked list of boundary-condition parameters.
Definition io.c:302
#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 PROFILE_FUNCTION_END
Marks the end of a profiled code block.
Definition logging.h:827
#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
PetscBool inletFaceDefined
Definition variables.h:897
PetscMPIInt rank
Definition variables.h:687
PetscInt block_number
Definition variables.h:768
BCFace identifiedInletBCFace
Definition variables.h:898
struct BC_Param_s * next
Definition variables.h:337
char * key
Definition variables.h:335
UserMG usermg
Definition variables.h:821
char * value
Definition variables.h:336
BC_Param * params
Definition variables.h:368
PetscInt mglevels
Definition variables.h:576
MGCtx * mgctx
Definition variables.h:579
A node in a linked list for storing key-value parameters from the bcs.dat file.
Definition variables.h:334
User-defined context containing data specific to a single computational grid level.
Definition variables.h:876
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:

◆ BoundarySystem_ExecuteStep()

PetscErrorCode BoundarySystem_ExecuteStep ( UserCtx user)

Executes one full boundary condition update cycle for a time step.

Parameters
userThe
Returns
PetscErrorCode 0 on success.

Executes one full boundary condition update cycle for a time step.

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

See also
BoundarySystem_ExecuteStep()

Definition at line 1059 of file Boundaries.c.

1060{
1061 PetscErrorCode ierr;
1062 PetscFunctionBeginUser;
1064
1065 LOG_ALLOW(LOCAL, LOG_DEBUG, "Starting.\n");
1066
1067 // =========================================================================
1068 // PRIORITY 0: INLETS
1069 // =========================================================================
1070
1071 PetscReal local_inflow_pre = 0.0;
1072 PetscReal local_inflow_post = 0.0;
1073 PetscReal global_inflow_pre = 0.0;
1074 PetscReal global_inflow_post = 0.0;
1075 PetscInt num_handlers[3] = {0,0,0};
1076
1077 LOG_ALLOW(LOCAL, LOG_TRACE, " (INLETS): Begin.\n");
1078
1079 // Phase 1: PreStep - Preparation (e.g., calculate profiles, read files)
1080 for (int i = 0; i < 6; i++) {
1081 BoundaryCondition *handler = user->boundary_faces[i].handler;
1082 if (!handler || handler->priority != BC_PRIORITY_INLET) continue;
1083 if (!handler->PreStep) continue;
1084
1085 num_handlers[0]++;
1086 BCContext ctx = {
1087 .user = user,
1088 .face_id = (BCFace)i,
1089 .global_inflow_sum = NULL,
1090 .global_outflow_sum = NULL,
1091 .global_farfield_inflow_sum = &user->simCtx->FarFluxInSum,
1092 .global_farfield_outflow_sum = &user->simCtx->FarFluxOutSum
1093 };
1094
1095 LOG_ALLOW(LOCAL, LOG_TRACE, " PreStep: Face %d (%s)\n", i, BCFaceToString((BCFace)i));
1096 ierr = handler->PreStep(handler, &ctx, &local_inflow_pre, NULL); CHKERRQ(ierr);
1097 }
1098
1099 // Optional: Global communication for PreStep (for debugging)
1100 if (local_inflow_pre != 0.0) {
1101 ierr = MPI_Allreduce(&local_inflow_pre, &global_inflow_pre, 1, MPIU_REAL,
1102 MPI_SUM, PETSC_COMM_WORLD); CHKERRQ(ierr);
1103 LOG_ALLOW(GLOBAL, LOG_TRACE, " PreStep predicted flux: %.6e\n", global_inflow_pre);
1104 }
1105
1106 // Phase 2: Apply - Set boundary conditions
1107 for (int i = 0; i < 6; i++) {
1108 BoundaryCondition *handler = user->boundary_faces[i].handler;
1109 if (!handler || handler->priority != BC_PRIORITY_INLET) continue;
1110 if(!handler->Apply) continue; // For example Periodic BCs
1111
1112 num_handlers[1]++;
1113
1114 BCContext ctx = {
1115 .user = user,
1116 .face_id = (BCFace)i,
1117 .global_inflow_sum = NULL,
1118 .global_outflow_sum = NULL,
1119 .global_farfield_inflow_sum = &user->simCtx->FarFluxInSum,
1120 .global_farfield_outflow_sum = &user->simCtx->FarFluxOutSum
1121 };
1122
1123 LOG_ALLOW(LOCAL, LOG_TRACE, " Apply: Face %d (%s)\n", i, BCFaceToString((BCFace)i));
1124 ierr = handler->Apply(handler, &ctx); CHKERRQ(ierr);
1125 }
1126
1127 // Phase 3: PostStep - Measure actual flux
1128 for (int i = 0; i < 6; i++) {
1129 BoundaryCondition *handler = user->boundary_faces[i].handler;
1130 if (!handler || handler->priority != BC_PRIORITY_INLET) continue;
1131 if (!handler->PostStep) continue;
1132
1133 num_handlers[2]++;
1134
1135 BCContext ctx = {
1136 .user = user,
1137 .face_id = (BCFace)i,
1138 .global_inflow_sum = NULL,
1139 .global_outflow_sum = NULL,
1140 .global_farfield_inflow_sum = &user->simCtx->FarFluxInSum,
1141 .global_farfield_outflow_sum = &user->simCtx->FarFluxOutSum
1142 };
1143
1144 LOG_ALLOW(LOCAL, LOG_TRACE, " PostStep: Face %d (%s)\n", i, BCFaceToString((BCFace)i));
1145 ierr = handler->PostStep(handler, &ctx, &local_inflow_post, NULL); CHKERRQ(ierr);
1146 }
1147
1148 // Phase 4: Global communication - Sum flux for other priorities to use
1149 ierr = MPI_Allreduce(&local_inflow_post, &global_inflow_post, 1, MPIU_REAL,
1150 MPI_SUM, PETSC_COMM_WORLD); CHKERRQ(ierr);
1151
1152 // Store for next priority levels
1153 user->simCtx->FluxInSum = global_inflow_post;
1154
1156 " (INLETS): %d Prestep(s), %d Application(s), %d Poststep(s), FluxInSum = %.6e\n",
1157 num_handlers[0],num_handlers[1],num_handlers[2], global_inflow_post);
1158
1159 // =========================================================================
1160 // PRIORITY 1: FARFIELD
1161 // =========================================================================
1162
1163 PetscReal local_farfield_in_pre = 0.0;
1164 PetscReal local_farfield_out_pre = 0.0;
1165 PetscReal local_farfield_in_post = 0.0;
1166 PetscReal local_farfield_out_post = 0.0;
1167 PetscReal global_farfield_in_pre = 0.0;
1168 PetscReal global_farfield_out_pre = 0.0;
1169 PetscReal global_farfield_in_post = 0.0;
1170 PetscReal global_farfield_out_post = 0.0;
1171 memset(num_handlers,0,sizeof(num_handlers));
1172
1173 LOG_ALLOW(LOCAL, LOG_TRACE, " (FARFIELD): Begin.\n");
1174
1175 // Phase 1: PreStep - Analyze flow direction, measure initial flux
1176 for (int i = 0; i < 6; i++) {
1177 BoundaryCondition *handler = user->boundary_faces[i].handler;
1178 if (!handler || handler->priority != BC_PRIORITY_FARFIELD) continue;
1179 if (!handler->PreStep) continue;
1180
1181 num_handlers[0]++;
1182 BCContext ctx = {
1183 .user = user,
1184 .face_id = (BCFace)i,
1185 .global_inflow_sum = &user->simCtx->FluxInSum, // Available from Priority 0
1186 .global_outflow_sum = NULL,
1187 .global_farfield_inflow_sum = &user->simCtx->FarFluxInSum,
1188 .global_farfield_outflow_sum = &user->simCtx->FarFluxOutSum
1189 };
1190
1191 LOG_ALLOW(LOCAL, LOG_TRACE, " PreStep: Face %d (%s)\n", i, BCFaceToString((BCFace)i));
1192 ierr = handler->PreStep(handler, &ctx, &local_farfield_in_pre, &local_farfield_out_pre);
1193 CHKERRQ(ierr);
1194 }
1195
1196 // Phase 2: Global communication (optional, for debugging)
1197 if (local_farfield_in_pre != 0.0 || local_farfield_out_pre != 0.0) {
1198 ierr = MPI_Allreduce(&local_farfield_in_pre, &global_farfield_in_pre, 1, MPIU_REAL,
1199 MPI_SUM, PETSC_COMM_WORLD); CHKERRQ(ierr);
1200 ierr = MPI_Allreduce(&local_farfield_out_pre, &global_farfield_out_pre, 1, MPIU_REAL,
1201 MPI_SUM, PETSC_COMM_WORLD); CHKERRQ(ierr);
1202
1204 " Farfield pre-analysis: In=%.6e, Out=%.6e\n",
1205 global_farfield_in_pre, global_farfield_out_pre);
1206 }
1207
1208 // Phase 3: Apply - Set farfield boundary conditions
1209 for (int i = 0; i < 6; i++) {
1210 BoundaryCondition *handler = user->boundary_faces[i].handler;
1211 if (!handler || handler->priority != BC_PRIORITY_FARFIELD) continue;
1212 if(!handler->Apply) continue; // For example Periodic BCs
1213
1214 num_handlers[1]++;
1215
1216 BCContext ctx = {
1217 .user = user,
1218 .face_id = (BCFace)i,
1219 .global_inflow_sum = &user->simCtx->FluxInSum,
1220 .global_outflow_sum = NULL,
1221 .global_farfield_inflow_sum = &user->simCtx->FarFluxInSum,
1222 .global_farfield_outflow_sum = &user->simCtx->FarFluxOutSum
1223 };
1224
1225 LOG_ALLOW(LOCAL, LOG_TRACE, " Apply: Face %d (%s)\n", i, BCFaceToString((BCFace)i));
1226 ierr = handler->Apply(handler, &ctx); CHKERRQ(ierr);
1227 }
1228
1229 // Phase 4: PostStep - Measure actual farfield fluxes
1230 for (int i = 0; i < 6; i++) {
1231 BoundaryCondition *handler = user->boundary_faces[i].handler;
1232 if (!handler || handler->priority != BC_PRIORITY_FARFIELD) continue;
1233 if (!handler->PostStep) continue;
1234
1235 num_handlers[2]++;
1236
1237 BCContext ctx = {
1238 .user = user,
1239 .face_id = (BCFace)i,
1240 .global_inflow_sum = &user->simCtx->FluxInSum,
1241 .global_outflow_sum = NULL,
1242 .global_farfield_inflow_sum = &user->simCtx->FarFluxInSum,
1243 .global_farfield_outflow_sum = &user->simCtx->FarFluxOutSum
1244 };
1245
1246 LOG_ALLOW(LOCAL, LOG_TRACE, " PostStep: Face %d (%s)\n", i, BCFaceToString((BCFace)i));
1247 ierr = handler->PostStep(handler, &ctx, &local_farfield_in_post, &local_farfield_out_post);
1248 CHKERRQ(ierr);
1249 }
1250
1251 // Phase 5: Global communication - Store for outlet priority
1252 if (num_handlers > 0) {
1253 ierr = MPI_Allreduce(&local_farfield_in_post, &global_farfield_in_post, 1, MPIU_REAL,
1254 MPI_SUM, PETSC_COMM_WORLD); CHKERRQ(ierr);
1255 ierr = MPI_Allreduce(&local_farfield_out_post, &global_farfield_out_post, 1, MPIU_REAL,
1256 MPI_SUM, PETSC_COMM_WORLD); CHKERRQ(ierr);
1257
1258 // Store for outlet handlers to use
1259 user->simCtx->FarFluxInSum = global_farfield_in_post;
1260 user->simCtx->FarFluxOutSum = global_farfield_out_post;
1261
1263 " (FARFIELD): %d Prestep(s), %d Application(s), %d Poststep(s) , InFlux=%.6e, OutFlux=%.6e\n",
1264 num_handlers[0],num_handlers[1],num_handlers[2], global_farfield_in_post, global_farfield_out_post);
1265 } else {
1266 // No farfield handlers - zero out the fluxes
1267 user->simCtx->FarFluxInSum = 0.0;
1268 user->simCtx->FarFluxOutSum = 0.0;
1269 }
1270
1271
1272 // =========================================================================
1273 // PRIORITY 2: WALLS
1274 // =========================================================================
1275
1276 memset(num_handlers,0,sizeof(num_handlers));
1277
1278 LOG_ALLOW(LOCAL, LOG_TRACE, " (WALLS): Begin.\n");
1279
1280 // Phase 1: PreStep - Preparation (usually no-op for walls)
1281 for (int i = 0; i < 6; i++) {
1282 BoundaryCondition *handler = user->boundary_faces[i].handler;
1283 if (!handler || handler->priority != BC_PRIORITY_WALL) continue;
1284 if (!handler->PreStep) continue;
1285
1286 num_handlers[0]++;
1287 BCContext ctx = {
1288 .user = user,
1289 .face_id = (BCFace)i,
1290 .global_inflow_sum = &user->simCtx->FluxInSum,
1291 .global_outflow_sum = NULL,
1292 .global_farfield_inflow_sum = &user->simCtx->FarFluxInSum,
1293 .global_farfield_outflow_sum = &user->simCtx->FarFluxOutSum
1294 };
1295
1296 LOG_ALLOW(LOCAL, LOG_TRACE, " PreStep: Face %d (%s)\n", i, BCFaceToString((BCFace)i));
1297 ierr = handler->PreStep(handler, &ctx, NULL, NULL); CHKERRQ(ierr);
1298 }
1299
1300 // No global communication needed for walls
1301
1302 // Phase 2: Apply - Set boundary conditions
1303 for (int i = 0; i < 6; i++) {
1304 BoundaryCondition *handler = user->boundary_faces[i].handler;
1305 if (!handler || handler->priority != BC_PRIORITY_WALL) continue;
1306 if(!handler->Apply) continue; // For example Periodic BCs
1307
1308 num_handlers[1]++;
1309
1310 BCContext ctx = {
1311 .user = user,
1312 .face_id = (BCFace)i,
1313 .global_inflow_sum = &user->simCtx->FluxInSum,
1314 .global_outflow_sum = NULL,
1315 .global_farfield_inflow_sum = &user->simCtx->FarFluxInSum,
1316 .global_farfield_outflow_sum = &user->simCtx->FarFluxOutSum
1317 };
1318
1319 LOG_ALLOW(LOCAL, LOG_TRACE, " Apply: Face %d (%s)\n", i, BCFaceToString((BCFace)i));
1320 ierr = handler->Apply(handler, &ctx); CHKERRQ(ierr);
1321 }
1322
1323 // Phase 3: PostStep - Post-application processing (usually no-op for walls)
1324 for (int i = 0; i < 6; i++) {
1325 BoundaryCondition *handler = user->boundary_faces[i].handler;
1326 if (!handler || handler->priority != BC_PRIORITY_WALL) continue;
1327 if (!handler->PostStep) continue;
1328
1329 num_handlers[2]++;
1330
1331 BCContext ctx = {
1332 .user = user,
1333 .face_id = (BCFace)i,
1334 .global_inflow_sum = &user->simCtx->FluxInSum,
1335 .global_outflow_sum = NULL,
1336 .global_farfield_inflow_sum = &user->simCtx->FarFluxInSum,
1337 .global_farfield_outflow_sum = &user->simCtx->FarFluxOutSum
1338 };
1339
1340 LOG_ALLOW(LOCAL, LOG_TRACE, " PostStep: Face %d (%s)\n", i, BCFaceToString((BCFace)i));
1341 ierr = handler->PostStep(handler, &ctx, NULL, NULL); CHKERRQ(ierr);
1342 }
1343
1344 // No global communication needed for walls
1345
1346 LOG_ALLOW(GLOBAL, LOG_INFO, " (WALLS): %d Prestep(s), %d Application(s), %d Poststep(s) applied.\n",
1347 num_handlers[0],num_handlers[1],num_handlers[2]);
1348
1349
1350 // =========================================================================
1351 // PRIORITY 3: OUTLETS
1352 // =========================================================================
1353
1354 PetscReal local_outflow_pre = 0.0;
1355 PetscReal local_outflow_post = 0.0;
1356 PetscReal global_outflow_pre = 0.0;
1357 PetscReal global_outflow_post = 0.0;
1358 memset(num_handlers,0,sizeof(num_handlers));
1359
1360 LOG_ALLOW(LOCAL, LOG_TRACE, " (OUTLETS): Begin.\n");
1361
1362 // Phase 1: PreStep - Measure uncorrected outflow (from ucat)
1363 for (int i = 0; i < 6; i++) {
1364 BoundaryCondition *handler = user->boundary_faces[i].handler;
1365 if (!handler || handler->priority != BC_PRIORITY_OUTLET) continue;
1366 if (!handler->PreStep) continue;
1367
1368 num_handlers[0]++;
1369 BCContext ctx = {
1370 .user = user,
1371 .face_id = (BCFace)i,
1372 .global_inflow_sum = &user->simCtx->FluxInSum, // From Priority 0
1373 .global_outflow_sum = NULL,
1374 .global_farfield_inflow_sum = &user->simCtx->FarFluxInSum,
1375 .global_farfield_outflow_sum = &user->simCtx->FarFluxOutSum
1376 };
1377
1378 LOG_ALLOW(LOCAL, LOG_TRACE, " PreStep: Face %d (%s)\n", i, BCFaceToString((BCFace)i));
1379 ierr = handler->PreStep(handler, &ctx, NULL, &local_outflow_pre); CHKERRQ(ierr);
1380 }
1381
1382 // Phase 2: Global communication - Get uncorrected outflow sum
1383 ierr = MPI_Allreduce(&local_outflow_pre, &global_outflow_pre, 1, MPIU_REAL,
1384 MPI_SUM, PETSC_COMM_WORLD); CHKERRQ(ierr);
1385
1386 // Calculate total inflow (inlet + farfield inflow)
1387 PetscReal total_inflow = user->simCtx->FluxInSum + user->simCtx->FarFluxInSum;
1388
1390 " Uncorrected outflow: %.6e, Total inflow: %.6e (Inlet: %.6e + Farfield: %.6e)\n",
1391 global_outflow_pre, total_inflow, user->simCtx->FluxInSum,
1392 user->simCtx->FarFluxInSum);
1393
1394 // Phase 3: Apply - Set corrected boundary conditions
1395 for (int i = 0; i < 6; i++) {
1396 BoundaryCondition *handler = user->boundary_faces[i].handler;
1397 if (!handler || handler->priority != BC_PRIORITY_OUTLET) continue;
1398 if(!handler->Apply) continue; // For example Periodic BCs
1399
1400 num_handlers[1]++;
1401
1402 BCContext ctx = {
1403 .user = user,
1404 .face_id = (BCFace)i,
1405 .global_inflow_sum = &user->simCtx->FluxInSum, // From Priority 0
1406 .global_outflow_sum = &global_outflow_pre, // From PreStep above
1407 .global_farfield_inflow_sum = &user->simCtx->FarFluxInSum,
1408 .global_farfield_outflow_sum = &user->simCtx->FarFluxOutSum
1409 };
1410
1411 LOG_ALLOW(LOCAL, LOG_TRACE, " Apply: Face %d (%s)\n", i, BCFaceToString((BCFace)i));
1412 ierr = handler->Apply(handler, &ctx); CHKERRQ(ierr);
1413 }
1414
1415 // Phase 4: PostStep - Measure corrected outflow (verification)
1416 for (int i = 0; i < 6; i++) {
1417 BoundaryCondition *handler = user->boundary_faces[i].handler;
1418 if (!handler || handler->priority != BC_PRIORITY_OUTLET) continue;
1419 if (!handler->PostStep) continue;
1420
1421 num_handlers[2]++;
1422
1423 BCContext ctx = {
1424 .user = user,
1425 .face_id = (BCFace)i,
1426 .global_inflow_sum = &user->simCtx->FluxInSum,
1427 .global_outflow_sum = &global_outflow_pre,
1428 .global_farfield_inflow_sum = &user->simCtx->FarFluxInSum,
1429 .global_farfield_outflow_sum = &user->simCtx->FarFluxOutSum
1430 };
1431
1432 LOG_ALLOW(LOCAL, LOG_TRACE, " PostStep: Face %d (%s)\n", i, BCFaceToString((BCFace)i));
1433 ierr = handler->PostStep(handler, &ctx, NULL, &local_outflow_post); CHKERRQ(ierr);
1434 }
1435
1436 // Phase 5: Global communication - Verify conservation
1437 ierr = MPI_Allreduce(&local_outflow_post, &global_outflow_post, 1, MPIU_REAL,
1438 MPI_SUM, PETSC_COMM_WORLD); CHKERRQ(ierr);
1439
1440 // Store for global reporting.
1441 user->simCtx->FluxOutSum = global_outflow_post;
1442
1443 // Conservation check (compare total outflow vs total inflow)
1444 PetscReal total_outflow = global_outflow_post + user->simCtx->FarFluxOutSum;
1445 PetscReal flux_error = PetscAbsReal(total_outflow - total_inflow);
1446 PetscReal relative_error = (total_inflow > 1e-16) ?
1447 flux_error / total_inflow : flux_error;
1448
1450 " (OUTLETS): %d Prestep(s), %d Application(s), %d Poststep(s), FluxOutSum = %.6e\n",
1451 num_handlers[0],num_handlers[1],num_handlers[2], global_outflow_post);
1453 " Conservation: Total In=%.6e, Total Out=%.6e, Error=%.3e (%.2e)%%)\n",
1454 total_inflow, total_outflow, flux_error, relative_error * 100.0);
1455
1456 if (relative_error > 1e-6) {
1458 " WARNING: Large mass conservation error (%.2e%%)!\n",
1459 relative_error * 100.0);
1460 }
1461
1462
1463 LOG_ALLOW(LOCAL, LOG_VERBOSE, "Complete.\n");
1464
1466 PetscFunctionReturn(0);
1467}
@ 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
@ LOG_VERBOSE
Extremely detailed logs, typically for development use only.
Definition logging.h:33
@ BC_PRIORITY_OUTLET
Definition variables.h:326
@ BC_PRIORITY_FARFIELD
Definition variables.h:324
@ BC_PRIORITY_WALL
Definition variables.h:325
@ BC_PRIORITY_INLET
Definition variables.h:323
Here is the call graph for this function:
Here is the caller graph for this function:

◆ BoundarySystem_RefreshUbcs()

PetscErrorCode BoundarySystem_RefreshUbcs ( UserCtx user)

(Private) A lightweight execution engine that calls the UpdateUbcs() method on all relevant handlers.

This function's sole purpose is to re-evaluate the target boundary values (ubcs) for flow-dependent boundary conditions (e.g., Symmetry, Outlets) after the interior velocity field has changed, such as after the projection step.

It operates based on a "pull" model: it iterates through all boundary handlers and executes their UpdateUbcs method only if the handler has provided one. This makes the system extensible, as new flow-dependent handlers can be added without changing this engine. Handlers for fixed boundary conditions (e.g., a wall with a constant velocity) will have their UpdateUbcs pointer set to NULL and will be skipped automatically.

Note
This function is a critical part of the post-projection refresh. It intentionally does NOT modify ucont and does NOT perform flux balancing.
Parameters
userThe main UserCtx struct.
Returns
PetscErrorCode 0 on success.

(Private) A lightweight execution engine that calls the UpdateUbcs() method on all relevant handlers.

Local to this translation unit.

Definition at line 1481 of file Boundaries.c.

1482{
1483 PetscErrorCode ierr;
1484 PetscFunctionBeginUser;
1485
1486 LOG_ALLOW(GLOBAL, LOG_TRACE, "Refreshing `ubcs` targets for flow-dependent boundaries...\n");
1487
1488 // Loop through all 6 faces of the domain
1489 for (int i = 0; i < 6; i++) {
1490 BoundaryCondition *handler = user->boundary_faces[i].handler;
1491
1492 // THE FILTER:
1493 // This is the core logic. We only act if a handler exists for the face
1494 // AND that handler has explicitly implemented the `UpdateUbcs` method.
1495 if (handler && handler->UpdateUbcs) {
1496
1497 const char *face_name = BCFaceToString((BCFace)i);
1498 LOG_ALLOW(LOCAL, LOG_TRACE, " Calling UpdateUbcs() for handler on Face %s.\n", face_name);
1499
1500 // Prepare the context. For this refresh step, we don't need to pass flux sums.
1501 BCContext ctx = {
1502 .user = user,
1503 .face_id = (BCFace)i,
1504 .global_inflow_sum = NULL,
1505 .global_outflow_sum = NULL,
1506 .global_farfield_inflow_sum = NULL,
1507 .global_farfield_outflow_sum = NULL
1508 };
1509
1510 // Call the handler's specific UpdateUbcs function pointer.
1511 ierr = handler->UpdateUbcs(handler, &ctx); CHKERRQ(ierr);
1512 }
1513 }
1514
1515 PetscFunctionReturn(0);
1516}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ BoundarySystem_Destroy()

PetscErrorCode BoundarySystem_Destroy ( UserCtx user)

Cleans up and destroys all boundary system resources.

Parameters
userThe
Returns
PetscErrorCode 0 on success.

Cleans up and destroys all boundary system resources.

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

See also
BoundarySystem_Destroy()

Definition at line 1531 of file Boundaries.c.

1532{
1533 PetscErrorCode ierr;
1534 PetscFunctionBeginUser;
1535
1536
1537
1538 LOG_ALLOW(GLOBAL, LOG_INFO, "Starting destruction of all boundary handlers. \n");
1539
1540 for (int i = 0; i < 6; i++) {
1541 BoundaryFaceConfig *face_cfg = &user->boundary_faces[i];
1542 const char *face_name = BCFaceToString(face_cfg->face_id);
1543
1544 // --- Step 1: Free the parameter linked list associated with this face ---
1545 if (face_cfg->params) {
1546 LOG_ALLOW(LOCAL, LOG_DEBUG, " Freeing parameter list for Face %d (%s). \n", i, face_name);
1547 FreeBC_ParamList(face_cfg->params);
1548 face_cfg->params = NULL; // Good practice to nullify dangling pointers
1549 }
1550
1551 // --- Step 2: Destroy the handler object itself ---
1552 if (face_cfg->handler) {
1553 const char *handler_name = BCHandlerTypeToString(face_cfg->handler->type);
1554 LOG_ALLOW(LOCAL, LOG_DEBUG, " Destroying handler '%s' on Face %d (%s).\n", handler_name, i, face_name);
1555
1556 // Call the handler's specific cleanup function first, if it exists.
1557 // This will free any memory stored in the handler's private `data` pointer.
1558 if (face_cfg->handler->Destroy) {
1559 ierr = face_cfg->handler->Destroy(face_cfg->handler); CHKERRQ(ierr);
1560 }
1561
1562 // Finally, free the generic BoundaryCondition object itself.
1563 ierr = PetscFree(face_cfg->handler); CHKERRQ(ierr);
1564 face_cfg->handler = NULL;
1565 }
1566 }
1567
1568 LOG_ALLOW(GLOBAL, LOG_INFO, "Destruction complete.\n");
1569 PetscFunctionReturn(0);
1570}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ CanRankServiceInletFace()

PetscErrorCode CanRankServiceInletFace ( UserCtx user,
const DMDALocalInfo *  info,
PetscInt  IM_nodes_global,
PetscInt  JM_nodes_global,
PetscInt  KM_nodes_global,
PetscBool *  can_service_inlet_out 
)

Determines if the current MPI rank owns any part of the globally defined inlet face, making it responsible for placing particles on that portion of the surface.

The determination is based on the rank's owned nodes (from DMDALocalInfo) and the global node counts, in conjunction with the user->identifiedInletBCFace. A rank can service an inlet face if it owns the cells adjacent to that global boundary and has a non-zero extent (owns cells) in the tangential dimensions of that face.

Parameters
userPointer to the UserCtx structure, containing identifiedInletBCFace.
infoPointer to the DMDALocalInfo for the current rank's DA (node-based).
IM_nodes_globalGlobal number of nodes in the I-direction (e.g., user->IM + 1 if user->IM is cell count).
JM_nodes_globalGlobal number of nodes in the J-direction.
KM_nodes_globalGlobal number of nodes in the K-direction.
[out]can_service_inlet_outPointer to a PetscBool; set to PETSC_TRUE if the rank services (part of) the inlet, PETSC_FALSE otherwise.
Returns
PetscErrorCode 0 on success, non-zero on failure.

Determines if the current MPI rank owns any part of the globally defined inlet face, making it responsible for placing particles on that portion of the surface.

Local to this translation unit.

Definition at line 11 of file Boundaries.c.

14{
15 PetscErrorCode ierr;
16 PetscMPIInt rank_for_logging; // For detailed debugging logs
17 PetscFunctionBeginUser;
19
20 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank_for_logging); CHKERRQ(ierr);
21
22 *can_service_inlet_out = PETSC_FALSE; // Default to no service
23
24 if (!user->inletFaceDefined) {
25 LOG_ALLOW(LOCAL, LOG_DEBUG, "[Rank %d]: Inlet face not defined in user context. Cannot service.\n", rank_for_logging);
26 PetscFunctionReturn(0);
27 }
28
29 // Get the range of cells owned by this rank in each dimension
30 PetscInt owned_start_cell_i, num_owned_cells_on_rank_i;
31 PetscInt owned_start_cell_j, num_owned_cells_on_rank_j;
32 PetscInt owned_start_cell_k, num_owned_cells_on_rank_k;
33
34 ierr = GetOwnedCellRange(info, 0, &owned_start_cell_i, &num_owned_cells_on_rank_i); CHKERRQ(ierr);
35 ierr = GetOwnedCellRange(info, 1, &owned_start_cell_j, &num_owned_cells_on_rank_j); CHKERRQ(ierr);
36 ierr = GetOwnedCellRange(info, 2, &owned_start_cell_k, &num_owned_cells_on_rank_k); CHKERRQ(ierr);
37
38 // Determine the global index of the last cell (0-indexed) in each direction.
39 // Example: If IM_nodes_global = 11 (nodes 0-10), there are 10 cells (0-9). Last cell index is 9.
40 // Formula: global_nodes - 1 (num cells) - 1 (0-indexed) = global_nodes - 2.
41 PetscInt last_global_cell_idx_i = (IM_nodes_global > 1) ? (IM_nodes_global - 2) : -1; // -1 if 0 or 1 node (i.e., 0 cells)
42 PetscInt last_global_cell_idx_j = (JM_nodes_global > 1) ? (JM_nodes_global - 2) : -1;
43 PetscInt last_global_cell_idx_k = (KM_nodes_global > 1) ? (KM_nodes_global - 2) : -1;
44
45 switch (user->identifiedInletBCFace) {
46 case BC_FACE_NEG_X: // Inlet on the global I-minimum face (face of cell C_i=0)
47 // Rank services if its first owned node is global node 0 (info->xs == 0),
48 // and it owns cells in I, J, and K directions.
49 if (info->xs == 0 && num_owned_cells_on_rank_i > 0 &&
50 num_owned_cells_on_rank_j > 0 && num_owned_cells_on_rank_k > 0) {
51 *can_service_inlet_out = PETSC_TRUE;
52 }
53 break;
54 case BC_FACE_POS_X: // Inlet on the global I-maximum face (face of cell C_i=last_global_cell_idx_i)
55 // Rank services if it owns the last cell in I-direction,
56 // and has extent in J and K.
57 if (last_global_cell_idx_i >= 0 && /* Check for valid global domain */
58 (owned_start_cell_i + num_owned_cells_on_rank_i - 1) == last_global_cell_idx_i && /* Rank's last cell is the global last cell */
59 num_owned_cells_on_rank_j > 0 && num_owned_cells_on_rank_k > 0) {
60 *can_service_inlet_out = PETSC_TRUE;
61 }
62 break;
63 case BC_FACE_NEG_Y:
64 if (info->ys == 0 && num_owned_cells_on_rank_j > 0 &&
65 num_owned_cells_on_rank_i > 0 && num_owned_cells_on_rank_k > 0) {
66 *can_service_inlet_out = PETSC_TRUE;
67 }
68 break;
69 case BC_FACE_POS_Y:
70 if (last_global_cell_idx_j >= 0 &&
71 (owned_start_cell_j + num_owned_cells_on_rank_j - 1) == last_global_cell_idx_j &&
72 num_owned_cells_on_rank_i > 0 && num_owned_cells_on_rank_k > 0) {
73 *can_service_inlet_out = PETSC_TRUE;
74 }
75 break;
76 case BC_FACE_NEG_Z:
77 if (info->zs == 0 && num_owned_cells_on_rank_k > 0 &&
78 num_owned_cells_on_rank_i > 0 && num_owned_cells_on_rank_j > 0) {
79 *can_service_inlet_out = PETSC_TRUE;
80 }
81 break;
82 case BC_FACE_POS_Z:
83 if (last_global_cell_idx_k >= 0 &&
84 (owned_start_cell_k + num_owned_cells_on_rank_k - 1) == last_global_cell_idx_k &&
85 num_owned_cells_on_rank_i > 0 && num_owned_cells_on_rank_j > 0) {
86 *can_service_inlet_out = PETSC_TRUE;
87 }
88 break;
89 default:
90 LOG_ALLOW(LOCAL, LOG_WARNING, "[Rank %d]: Unknown inlet face %s.\n", rank_for_logging, BCFaceToString((BCFace)user->identifiedInletBCFace));
91 break;
92 }
93
95 "[Rank %d] Check Service for Inlet %s:\n"
96 " - Local Domain: starts at cell (%d,%d,%d), has (%d,%d,%d) cells.\n"
97 " - Global Domain: has (%d,%d,%d) nodes, so last cell is (%d,%d,%d).\n",
98 rank_for_logging,
100 owned_start_cell_i, owned_start_cell_j, owned_start_cell_k,
101 num_owned_cells_on_rank_i, num_owned_cells_on_rank_j, num_owned_cells_on_rank_k,
102 IM_nodes_global, JM_nodes_global, KM_nodes_global,
103 last_global_cell_idx_i, last_global_cell_idx_j, last_global_cell_idx_k);
104
105 LOG_ALLOW(LOCAL, LOG_INFO,"[Rank %d] Inlet Face %s Service Check Result: %s | Owned Cells (I,J,K): (%d,%d,%d) | Starts at Cell (%d,%d,%d)\n",
106 rank_for_logging,
108 (*can_service_inlet_out) ? "CAN SERVICE" : "CANNOT SERVICE",
109 num_owned_cells_on_rank_i, num_owned_cells_on_rank_j, num_owned_cells_on_rank_k,
110 owned_start_cell_i, owned_start_cell_j, owned_start_cell_k);
111
113
114 PetscFunctionReturn(0);
115}
PetscErrorCode GetOwnedCellRange(const DMDALocalInfo *info_nodes, PetscInt dim, PetscInt *xs_cell_global_out, PetscInt *xm_cell_local_out)
Determines the global starting index and number of CELLS owned by the current processor in a specifie...
Definition setup.c:2382
Here is the call graph for this function:
Here is the caller graph for this function:

◆ CanRankServiceFace()

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.

This function is a general utility for parallel boundary operations. It checks if the local domain of the current MPI rank is adjacent to a specified global boundary face. A rank "services" a face if it owns the cells adjacent to that global boundary and has a non-zero extent (i.e., owns at least one cell) in the tangential dimensions of that face.

Parameters
infoPointer to the DMDALocalInfo for the current rank's DA.
IM_nodes_globalGlobal number of nodes in the I-direction (e.g., user->IM + 1 if user->IM is cell count).
JM_nodes_globalGlobal number of nodes in the J-direction.
KM_nodes_globalGlobal number of nodes in the K-direction.
face_idThe specific global face (e.g., BC_FACE_NEG_Z) to check.
[out]can_service_outPointer to a PetscBool; set to PETSC_TRUE if the rank services the face, PETSC_FALSE otherwise.
Returns
PetscErrorCode 0 on success.

Determines if the current MPI rank owns any part of a specified global face.

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

See also
CanRankServiceFace()

Definition at line 126 of file Boundaries.c.

128{
129 PetscErrorCode ierr;
130 PetscMPIInt rank_for_logging;
131 PetscFunctionBeginUser;
132
134
135 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank_for_logging); CHKERRQ(ierr);
136
137 *can_service_out = PETSC_FALSE; // Default to no service
138
139 // Get the range of cells owned by this rank
140 PetscInt owned_start_cell_i, num_owned_cells_on_rank_i;
141 PetscInt owned_start_cell_j, num_owned_cells_on_rank_j;
142 PetscInt owned_start_cell_k, num_owned_cells_on_rank_k;
143 ierr = GetOwnedCellRange(info, 0, &owned_start_cell_i, &num_owned_cells_on_rank_i); CHKERRQ(ierr);
144 ierr = GetOwnedCellRange(info, 1, &owned_start_cell_j, &num_owned_cells_on_rank_j); CHKERRQ(ierr);
145 ierr = GetOwnedCellRange(info, 2, &owned_start_cell_k, &num_owned_cells_on_rank_k); CHKERRQ(ierr);
146
147 // Determine the global index of the last cell (0-indexed) in each direction.
148 PetscInt last_global_cell_idx_i = (IM_nodes_global > 1) ? (IM_nodes_global - 2) : -1;
149 PetscInt last_global_cell_idx_j = (JM_nodes_global > 1) ? (JM_nodes_global - 2) : -1;
150 PetscInt last_global_cell_idx_k = (KM_nodes_global > 1) ? (KM_nodes_global - 2) : -1;
151
152 switch (face_id) {
153 case BC_FACE_NEG_X:
154 if (info->xs == 0 && num_owned_cells_on_rank_i > 0 &&
155 num_owned_cells_on_rank_j > 0 && num_owned_cells_on_rank_k > 0) {
156 *can_service_out = PETSC_TRUE;
157 }
158 break;
159 case BC_FACE_POS_X:
160 if (last_global_cell_idx_i >= 0 &&
161 (owned_start_cell_i + num_owned_cells_on_rank_i - 1) == last_global_cell_idx_i &&
162 num_owned_cells_on_rank_j > 0 && num_owned_cells_on_rank_k > 0) {
163 *can_service_out = PETSC_TRUE;
164 }
165 break;
166 case BC_FACE_NEG_Y:
167 if (info->ys == 0 && num_owned_cells_on_rank_j > 0 &&
168 num_owned_cells_on_rank_i > 0 && num_owned_cells_on_rank_k > 0) {
169 *can_service_out = PETSC_TRUE;
170 }
171 break;
172 case BC_FACE_POS_Y:
173 if (last_global_cell_idx_j >= 0 &&
174 (owned_start_cell_j + num_owned_cells_on_rank_j - 1) == last_global_cell_idx_j &&
175 num_owned_cells_on_rank_i > 0 && num_owned_cells_on_rank_k > 0) {
176 *can_service_out = PETSC_TRUE;
177 }
178 break;
179 case BC_FACE_NEG_Z:
180 if (info->zs == 0 && num_owned_cells_on_rank_k > 0 &&
181 num_owned_cells_on_rank_i > 0 && num_owned_cells_on_rank_j > 0) {
182 *can_service_out = PETSC_TRUE;
183 }
184 break;
185 case BC_FACE_POS_Z:
186 if (last_global_cell_idx_k >= 0 &&
187 (owned_start_cell_k + num_owned_cells_on_rank_k - 1) == last_global_cell_idx_k &&
188 num_owned_cells_on_rank_i > 0 && num_owned_cells_on_rank_j > 0) {
189 *can_service_out = PETSC_TRUE;
190 }
191 break;
192 default:
193 LOG_ALLOW(LOCAL, LOG_WARNING, "Rank %d: Unknown face enum %d. \n", rank_for_logging, face_id);
194 break;
195 }
196
197 LOG_ALLOW(LOCAL, LOG_DEBUG, "Rank %d check for face %s: Result=%s. \n",
198 rank_for_logging, BCFaceToString((BCFace)face_id), (*can_service_out ? "TRUE" : "FALSE"));
199
201
202 PetscFunctionReturn(0);
203}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ GetDeterministicFaceGridLocation()

PetscErrorCode GetDeterministicFaceGridLocation ( UserCtx user,
const DMDALocalInfo *  info,
PetscInt  xs_gnode_rank,
PetscInt  ys_gnode_rank,
PetscInt  zs_gnode_rank,
PetscInt  IM_cells_global,
PetscInt  JM_cells_global,
PetscInt  KM_cells_global,
PetscInt64  particle_global_id,
PetscInt *  ci_metric_lnode_out,
PetscInt *  cj_metric_lnode_out,
PetscInt *  ck_metric_lnode_out,
PetscReal *  xi_metric_logic_out,
PetscReal *  eta_metric_logic_out,
PetscReal *  zta_metric_logic_out,
PetscBool *  placement_successful_out 
)

Places particles in a deterministic grid/raster pattern on a specified domain face.

This function creates a set of equidistant, parallel lines of particles near the four edges of the face specified by user->identifiedInletBCFace. The number of lines drawn from each edge is hardcoded within this function (default is 2). For example, if grid_layers=2 on face BC_FACE_NEG_X, the function will create particle lines at:

  • y ~ 0*dy, y ~ 1*dy (parallel to the Z-axis, starting from the J=0 edge)
  • y ~ y_max, y ~ y_max-dy (parallel to the Z-axis, starting from the J=max edge)
  • z ~ 0*dz, z ~ 1*dz (parallel to the Y-axis, starting from the K=0 edge)
  • z ~ z_max, z ~ z_max-dz (parallel to the Y-axis, starting from the K=max edge) The particle's final position is set just inside the target cell face to ensure it is correctly located. The total number of particles (simCtx->np) is distributed as evenly as possible among all generated lines. The function includes extensive validation to stop with an error if the requested grid placement is geometrically impossible (e.g., in a 2D domain or if layers would overlap). It also issues warnings for non-fatal but potentially unintended configurations.
Parameters
userPointer
infoPointer
xs_gnode_rankParameter xs_gnode_rank passed to GetDeterministicFaceGridLocation().
ys_gnode_rankParameter ys_gnode_rank passed to GetDeterministicFaceGridLocation().
zs_gnode_rankParameter zs_gnode_rank passed to GetDeterministicFaceGridLocation().
IM_cells_globalParameter IM_cells_global passed to GetDeterministicFaceGridLocation().
JM_cells_globalParameter JM_cells_global passed to GetDeterministicFaceGridLocation().
KM_cells_globalParameter KM_cells_global passed to GetDeterministicFaceGridLocation().
particle_global_idThe
ci_metric_lnode_outLocal
cj_metric_lnode_outLocal
ck_metric_lnode_outLocal
xi_metric_logic_outLogical
eta_metric_logic_outLogical
zta_metric_logic_outLogical
placement_successful_outPETSC_TRUE
Returns
PetscErrorCode

Places particles in a deterministic grid/raster pattern on a specified domain face.

Local to this translation unit.

Definition at line 212 of file Boundaries.c.

220{
221 SimCtx *simCtx = user->simCtx;
222 PetscReal global_logic_i = 0.0, global_logic_j = 0.0, global_logic_k = 0.0;
223 PetscErrorCode ierr;
224 PetscMPIInt rank_for_logging;
225
226 PetscFunctionBeginUser;
227 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank_for_logging); CHKERRQ(ierr);
228
229 *placement_successful_out = PETSC_FALSE; // Default to failure
230
231 // --- Step 1: Configuration and Input Validation ---
232
233 // *** Hardcoded number of grid layers. Change this value to alter the pattern. ***
234 const PetscInt grid_layers = 2;
235
237 "[Rank %d] Placing particle %lld on face %s with grid_layers=%d in global domain (%d,%d,%d) cells.\n",
238 rank_for_logging, (long long)particle_global_id, BCFaceToString(user->identifiedInletBCFace), grid_layers,
239 IM_cells_global, JM_cells_global, KM_cells_global);
240
241 const char *face_name = BCFaceToString(user->identifiedInletBCFace);
242
243 // Fatal Error Checks: Ensure the requested grid is geometrically possible.
244 // The total layers from opposite faces (2 * grid_layers) must be less than the domain size.
245 switch (user->identifiedInletBCFace) {
246 case BC_FACE_NEG_X: case BC_FACE_POS_X:
247 if (JM_cells_global <= 1 || KM_cells_global <= 1) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cannot place grid on face %s for a 2D/1D domain (J-cells=%d, K-cells=%d).", face_name, JM_cells_global, KM_cells_global);
248 if (2 * grid_layers >= JM_cells_global || 2 * grid_layers >= KM_cells_global) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Grid layers (%d) from opposing J/K faces would overlap in this domain (J-cells=%d, K-cells=%d).", grid_layers, JM_cells_global, KM_cells_global);
249 break;
250 case BC_FACE_NEG_Y: case BC_FACE_POS_Y:
251 if (IM_cells_global <= 1 || KM_cells_global <= 1) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cannot place grid on face %s for a 2D/1D domain (I-cells=%d, K-cells=%d).", face_name, IM_cells_global, KM_cells_global);
252 if (2 * grid_layers >= IM_cells_global || 2 * grid_layers >= KM_cells_global) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Grid layers (%d) from opposing I/K faces would overlap in this domain (I-cells=%d, K-cells=%d).", grid_layers, IM_cells_global, KM_cells_global);
253 break;
254 case BC_FACE_NEG_Z: case BC_FACE_POS_Z:
255 if (IM_cells_global <= 1 || JM_cells_global <= 1) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cannot place grid on face %s for a 2D/1D domain (I-cells=%d, J-cells=%d).", face_name, IM_cells_global, JM_cells_global);
256 if (2 * grid_layers >= IM_cells_global || 2 * grid_layers >= JM_cells_global) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Grid layers (%d) from opposing I/J faces would overlap in this domain (I-cells=%d, J-cells=%d).", grid_layers, IM_cells_global, JM_cells_global);
257 break;
258 default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Invalid identifiedInletBCFace specified: %d", user->identifiedInletBCFace);
259 }
260
261 const PetscInt num_lines_total = 4 * grid_layers;
262 if (simCtx->np < num_lines_total) {
263 LOG_ALLOW(GLOBAL, LOG_WARNING, "Warning: Total particle count (%lld) is less than the number of grid lines requested (%d). Some lines may be empty.\n", (long long)simCtx->np, num_lines_total);
264 }
265 if (simCtx->np > 0 && simCtx->np % num_lines_total != 0) {
266 LOG_ALLOW(GLOBAL, LOG_WARNING, "Warning: Total particle count (%lld) is not evenly divisible by the number of grid lines (%d). Distribution will be uneven.\n", (long long)simCtx->np, num_lines_total);
267 }
268
269 // --- Step 2: Map global particle ID to a line and a point on that line ---
270 if (simCtx->np == 0) PetscFunctionReturn(0); // Nothing to do
271
272 LOG_ALLOW(LOCAL, LOG_TRACE, "[Rank %d] Distributing %lld particles over %d lines on face %s.\n",
273 rank_for_logging, (long long)simCtx->np, num_lines_total, face_name);
274
275 const PetscInt points_per_line = PetscMax(1, simCtx->np / num_lines_total);
276 PetscInt line_index = particle_global_id / points_per_line;
277 PetscInt point_index_on_line = particle_global_id % points_per_line;
278 line_index = PetscMin(line_index, num_lines_total - 1); // Clamp to handle uneven division
279
280 // Decode the line_index into an edge group (0-3) and a layer within that group (0 to grid_layers-1)
281 const PetscInt edge_group = line_index / grid_layers;
282 const PetscInt layer_index = line_index % grid_layers;
283
284 // --- Step 3: Calculate placement coordinates based on the decoded indices ---
285 const PetscReal layer_spacing_norm_i = (IM_cells_global > 0) ? 1.0 / (PetscReal)IM_cells_global : 0.0;
286 const PetscReal layer_spacing_norm_j = (JM_cells_global > 0) ? 1.0 / (PetscReal)JM_cells_global : 0.0;
287 const PetscReal layer_spacing_norm_k = (KM_cells_global > 0) ? 1.0 / (PetscReal)KM_cells_global : 0.0;
288
289 // Grid-aware epsilon: scale with minimum cell size to keep particles away from rank boundaries
290 const PetscReal min_layer_spacing = PetscMin(layer_spacing_norm_i, PetscMin(layer_spacing_norm_j, layer_spacing_norm_k));
291 const PetscReal epsilon = 0.5 * min_layer_spacing; // Keep particles 10% of cell width from boundaries
292
293 PetscReal variable_coord; // The coordinate that varies along a line
294 if (points_per_line <= 1) {
295 variable_coord = 0.5; // Place single point in the middle
296 } else {
297 variable_coord = ((PetscReal)point_index_on_line + 0.5)/ (PetscReal)(points_per_line);
298 }
299 variable_coord = PetscMin(1.0 - epsilon, PetscMax(epsilon, variable_coord)); // Clamp within [eps, 1-eps]
300
301 // Main logic switch to determine the three global logical coordinates
302 switch (user->identifiedInletBCFace) {
303 case BC_FACE_NEG_X:
304 global_logic_i = 0.5 * layer_spacing_norm_i; // Place near the face, in the middle of the first cell
305 if (edge_group == 0) { global_logic_j = (PetscReal)layer_index * layer_spacing_norm_j + epsilon; global_logic_k = variable_coord; }
306 else if (edge_group == 1) { global_logic_j = 1.0 - ((PetscReal)layer_index * layer_spacing_norm_j) - epsilon; global_logic_k = variable_coord; }
307 else if (edge_group == 2) { global_logic_k = (PetscReal)layer_index * layer_spacing_norm_k + epsilon; global_logic_j = variable_coord; }
308 else /* edge_group == 3 */ { global_logic_k = 1.0 - ((PetscReal)layer_index * layer_spacing_norm_k) - epsilon; global_logic_j = variable_coord; }
309 break;
310 case BC_FACE_POS_X:
311 global_logic_i = 1.0 - (0.5 * layer_spacing_norm_i); // Place near the face, in the middle of the last cell
312 if (edge_group == 0) { global_logic_j = (PetscReal)layer_index * layer_spacing_norm_j + epsilon; global_logic_k = variable_coord; }
313 else if (edge_group == 1) { global_logic_j = 1.0 - ((PetscReal)layer_index * layer_spacing_norm_j) - epsilon; global_logic_k = variable_coord; }
314 else if (edge_group == 2) { global_logic_k = (PetscReal)layer_index * layer_spacing_norm_k + epsilon; global_logic_j = variable_coord; }
315 else /* edge_group == 3 */ { global_logic_k = 1.0 - ((PetscReal)layer_index * layer_spacing_norm_k) - epsilon; global_logic_j = variable_coord; }
316 break;
317 case BC_FACE_NEG_Y:
318 global_logic_j = 0.5 * layer_spacing_norm_j;
319 if (edge_group == 0) { global_logic_i = (PetscReal)layer_index * layer_spacing_norm_i + epsilon; global_logic_k = variable_coord; }
320 else if (edge_group == 1) { global_logic_i = 1.0 - ((PetscReal)layer_index * layer_spacing_norm_i) - epsilon; global_logic_k = variable_coord; }
321 else if (edge_group == 2) { global_logic_k = (PetscReal)layer_index * layer_spacing_norm_k + epsilon; global_logic_i = variable_coord; }
322 else /* edge_group == 3 */ { global_logic_k = 1.0 - ((PetscReal)layer_index * layer_spacing_norm_k) - epsilon; global_logic_i = variable_coord; }
323 break;
324 case BC_FACE_POS_Y:
325 global_logic_j = 1.0 - (0.5 * layer_spacing_norm_j);
326 if (edge_group == 0) { global_logic_i = (PetscReal)layer_index * layer_spacing_norm_i + epsilon; global_logic_k = variable_coord; }
327 else if (edge_group == 1) { global_logic_i = 1.0 - ((PetscReal)layer_index * layer_spacing_norm_i) - epsilon; global_logic_k = variable_coord; }
328 else if (edge_group == 2) { global_logic_k = (PetscReal)layer_index * layer_spacing_norm_k + epsilon; global_logic_i = variable_coord; }
329 else /* edge_group == 3 */ { global_logic_k = 1.0 - ((PetscReal)layer_index * layer_spacing_norm_k) - epsilon; global_logic_i = variable_coord; }
330 break;
331 case BC_FACE_NEG_Z:
332 global_logic_k = 0.5 * layer_spacing_norm_k;
333 if (edge_group == 0) { global_logic_i = (PetscReal)layer_index * layer_spacing_norm_i + epsilon; global_logic_j = variable_coord; }
334 else if (edge_group == 1) { global_logic_i = 1.0 - ((PetscReal)layer_index * layer_spacing_norm_i) - epsilon; global_logic_j = variable_coord; }
335 else if (edge_group == 2) { global_logic_j = (PetscReal)layer_index * layer_spacing_norm_j + epsilon; global_logic_i = variable_coord; }
336 else /* edge_group == 3 */ { global_logic_j = 1.0 - ((PetscReal)layer_index * layer_spacing_norm_j) - epsilon; global_logic_i = variable_coord; }
337 break;
338 case BC_FACE_POS_Z:
339 global_logic_k = 1.0 - (0.5 * layer_spacing_norm_k);
340 if (edge_group == 0) { global_logic_i = (PetscReal)layer_index * layer_spacing_norm_i + epsilon; global_logic_j = variable_coord; }
341 else if (edge_group == 1) { global_logic_i = 1.0 - ((PetscReal)layer_index * layer_spacing_norm_i) - epsilon; global_logic_j = variable_coord; }
342 else if (edge_group == 2) { global_logic_j = (PetscReal)layer_index * layer_spacing_norm_j + epsilon; global_logic_i = variable_coord; }
343 else /* edge_group == 3 */ { global_logic_j = 1.0 - ((PetscReal)layer_index * layer_spacing_norm_j) - epsilon; global_logic_i = variable_coord; }
344 break;
345 }
346
348 "[Rank %d] Particle %lld assigned to line %d (edge group %d, layer %d) with variable_coord=%.4f.\n"
349 " -> Global logical coords: (i,j,k) = (%.6f, %.6f, %.6f)\n",
350 rank_for_logging, (long long)particle_global_id, line_index, edge_group, layer_index, variable_coord,
351 global_logic_i, global_logic_j, global_logic_k);
352
353 // --- Step 4: Convert global logical coordinate to global cell index and intra-cell logicals ---
354 PetscReal global_cell_coord_i = global_logic_i * IM_cells_global;
355 PetscInt I_g = (PetscInt)global_cell_coord_i;
356 *xi_metric_logic_out = global_cell_coord_i - I_g;
357
358 PetscReal global_cell_coord_j = global_logic_j * JM_cells_global;
359 PetscInt J_g = (PetscInt)global_cell_coord_j;
360 *eta_metric_logic_out = global_cell_coord_j - J_g;
361
362 PetscReal global_cell_coord_k = global_logic_k * KM_cells_global;
363 PetscInt K_g = (PetscInt)global_cell_coord_k;
364 *zta_metric_logic_out = global_cell_coord_k - K_g;
365
366 // --- Step 5: Check if this rank owns the target cell and finalize outputs ---
367 if ((I_g >= info->xs && I_g < info->xs + info->xm) &&
368 (J_g >= info->ys && J_g < info->ys + info->ym) &&
369 (K_g >= info->zs && K_g < info->zs + info->zm))
370 {
371 // Convert global cell index to the local node index for this rank's DA patch
372 *ci_metric_lnode_out = (I_g - info->xs) + xs_gnode_rank;
373 *cj_metric_lnode_out = (J_g - info->ys) + ys_gnode_rank;
374 *ck_metric_lnode_out = (K_g - info->zs) + zs_gnode_rank;
375 *placement_successful_out = PETSC_TRUE;
376 }
377
379 "[Rank %d] Particle %lld placement %s.\n",
380 rank_for_logging, (long long)particle_global_id,
381 (*placement_successful_out ? "SUCCESSFUL" : "NOT ON THIS RANK"));
382
383 if(*placement_successful_out){
384 LOG_ALLOW(LOCAL,LOG_TRACE,"Local cell origin node: (I,J,K) = (%d,%d,%d), intra-cell logicals: (xi,eta,zta)=(%.6f,%.6f,%.6f)\n",
385 *ci_metric_lnode_out, *cj_metric_lnode_out, *ck_metric_lnode_out,
386 *xi_metric_logic_out, *eta_metric_logic_out, *zta_metric_logic_out);
387 }
388
389 PetscFunctionReturn(0);
390}
PetscInt np
Definition variables.h:796
The master context for the entire simulation.
Definition variables.h:684
Here is the call graph for this function:
Here is the caller graph for this function:

◆ GetRandomCellAndLogicalCoordsOnInletFace()

PetscErrorCode GetRandomCellAndLogicalCoordsOnInletFace ( UserCtx user,
const DMDALocalInfo *  info,
PetscInt  xs_gnode_rank,
PetscInt  ys_gnode_rank,
PetscInt  zs_gnode_rank,
PetscInt  IM_nodes_global,
PetscInt  JM_nodes_global,
PetscInt  KM_nodes_global,
PetscRandom *  rand_logic_i_ptr,
PetscRandom *  rand_logic_j_ptr,
PetscRandom *  rand_logic_k_ptr,
PetscInt *  ci_metric_lnode_out,
PetscInt *  cj_metric_lnode_out,
PetscInt *  ck_metric_lnode_out,
PetscReal *  xi_metric_logic_out,
PetscReal *  eta_metric_logic_out,
PetscReal *  zta_metric_logic_out 
)

Assuming the current rank services the inlet face, this function selects a random cell (owned by this rank on that face) and random logical coordinates within that cell, suitable for placing a particle on the inlet surface.

It is the caller's responsibility to ensure CanRankServiceInletFace returned true.

Parameters
userPointer to UserCtx.
infoPointer to DMDALocalInfo for the current rank (node-based).
xs_gnode_rankLocal i-start node index (including ghosts) for this rank.
ys_gnode_rankLocal j-start node index (including ghosts) for this rank.
zs_gnode_rankLocal k-start node index (including ghosts) for this rank.
IM_nodes_globalGlobal node count in i.
JM_nodes_globalGlobal node count in j.
KM_nodes_globalGlobal node count in k.
rand_logic_i_ptrRNG handle for sampling local logical xi.
rand_logic_j_ptrRNG handle for sampling local logical eta.
rand_logic_k_ptrRNG handle for sampling local logical zta.
[out]ci_metric_lnode_outLocal i node index of selected cell origin.
[out]cj_metric_lnode_outLocal j node index of selected cell origin.
[out]ck_metric_lnode_outLocal k node index of selected cell origin.
[out]xi_metric_logic_outLogical xi coordinate in [0,1].
[out]eta_metric_logic_outLogical eta coordinate in [0,1].
[out]zta_metric_logic_outLogical zta coordinate in [0,1].
Returns
PetscErrorCode

Assuming the current rank services the inlet face, this function selects a random cell (owned by this rank on that face) and random logical coordinates within that cell, suitable for placing a particle on the inlet surface.

Local to this translation unit.

Definition at line 399 of file Boundaries.c.

406{
407 PetscErrorCode ierr = 0;
408 PetscReal r_val_i_sel, r_val_j_sel, r_val_k_sel;
409 PetscInt local_cell_idx_on_face_dim1 = 0; // 0-indexed relative to owned cells on face
410 PetscInt local_cell_idx_on_face_dim2 = 0;
411 PetscMPIInt rank_for_logging;
412
413 PetscFunctionBeginUser;
414
416
417 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank_for_logging); CHKERRQ(ierr);
418
419 // Get number of cells this rank owns in each dimension (tangential to the face mainly)
420 PetscInt owned_start_cell_i, num_owned_cells_on_rank_i;
421 PetscInt owned_start_cell_j, num_owned_cells_on_rank_j;
422 PetscInt owned_start_cell_k, num_owned_cells_on_rank_k;
423
424 ierr = GetOwnedCellRange(info, 0, &owned_start_cell_i, &num_owned_cells_on_rank_i); CHKERRQ(ierr);
425 ierr = GetOwnedCellRange(info, 1, &owned_start_cell_j, &num_owned_cells_on_rank_j); CHKERRQ(ierr);
426 ierr = GetOwnedCellRange(info, 2, &owned_start_cell_k, &num_owned_cells_on_rank_k); CHKERRQ(ierr);
427
428 // Defaults for cell origin node (local index for the rank's DA patch, including ghosts)
429 *ci_metric_lnode_out = xs_gnode_rank; *cj_metric_lnode_out = ys_gnode_rank; *ck_metric_lnode_out = zs_gnode_rank;
430 // Defaults for logical coordinates
431 *xi_metric_logic_out = 0.5; *eta_metric_logic_out = 0.5; *zta_metric_logic_out = 0.5;
432
433 // Index of the last cell (0-indexed) in each global direction
434 PetscInt last_global_cell_idx_i = (IM_nodes_global > 1) ? (IM_nodes_global - 2) : -1;
435 PetscInt last_global_cell_idx_j = (JM_nodes_global > 1) ? (JM_nodes_global - 2) : -1;
436 PetscInt last_global_cell_idx_k = (KM_nodes_global > 1) ? (KM_nodes_global - 2) : -1;
437
438 LOG_ALLOW(LOCAL, LOG_INFO, "PARTICLE_INIT_DEBUG Rank %d: Inlet face %s.\n"
439 " Owned cells (i,j,k): (%d,%d,%d)\n"
440 " Global nodes (I,J,K): (%d,%d,%d)\n"
441 " info->xs,ys,zs (first owned node GLOBAL): (%d,%d,%d)\n"
442 " info->xm,ym,zm (num owned nodes GLOBAL): (%d,%d,%d)\n"
443 " xs_gnode_rank,ys_gnode_rank,zs_gnode_rank (DMDAGetCorners): (%d,%d,%d)\n"
444 " owned_start_cell (i,j,k) GLOBAL: (%d,%d,%d)\n"
445 " last_global_cell_idx (i,j,k): (%d,%d,%d)\n",
446 rank_for_logging, BCFaceToString((BCFace)user->identifiedInletBCFace),
447 num_owned_cells_on_rank_i,num_owned_cells_on_rank_j,num_owned_cells_on_rank_k,
448 IM_nodes_global,JM_nodes_global,KM_nodes_global,
449 info->xs, info->ys, info->zs,
450 info->xm, info->ym, info->zm,
451 xs_gnode_rank,ys_gnode_rank,zs_gnode_rank,
452 owned_start_cell_i, owned_start_cell_j, owned_start_cell_k,
453 last_global_cell_idx_i, last_global_cell_idx_j, last_global_cell_idx_k);
454
455
456 switch (user->identifiedInletBCFace) {
457 case BC_FACE_NEG_X: // Particle on -X face of cell C_0 (origin node N_0)
458 // Cell origin node is the first owned node in I by this rank (global index info->xs).
459 // Its local index within the rank's DA (incl ghosts) is xs_gnode_rank.
460 *ci_metric_lnode_out = xs_gnode_rank;
461 *xi_metric_logic_out = 1.0e-6;
462
463 // Tangential dimensions are J and K. Select an owned cell randomly on this face.
464 // num_owned_cells_on_rank_j/k must be > 0 (checked by CanRankServiceInletFace)
465 ierr = PetscRandomGetValueReal(*rand_logic_j_ptr, &r_val_j_sel); CHKERRQ(ierr);
466 local_cell_idx_on_face_dim1 = (PetscInt)(r_val_j_sel * num_owned_cells_on_rank_j); // Index among owned J-cells
467 local_cell_idx_on_face_dim1 = PetscMin(PetscMax(0, local_cell_idx_on_face_dim1), num_owned_cells_on_rank_j - 1);
468 *cj_metric_lnode_out = ys_gnode_rank + local_cell_idx_on_face_dim1; // Offset from start of rank's J-nodes
469
470 ierr = PetscRandomGetValueReal(*rand_logic_k_ptr, &r_val_k_sel); CHKERRQ(ierr);
471 local_cell_idx_on_face_dim2 = (PetscInt)(r_val_k_sel * num_owned_cells_on_rank_k);
472 local_cell_idx_on_face_dim2 = PetscMin(PetscMax(0, local_cell_idx_on_face_dim2), num_owned_cells_on_rank_k - 1);
473 *ck_metric_lnode_out = zs_gnode_rank + local_cell_idx_on_face_dim2;
474
475 ierr = PetscRandomGetValueReal(*rand_logic_j_ptr, eta_metric_logic_out); CHKERRQ(ierr);
476 ierr = PetscRandomGetValueReal(*rand_logic_k_ptr, zta_metric_logic_out); CHKERRQ(ierr);
477 break;
478
479 case BC_FACE_POS_X: // Particle on +X face of cell C_last_I (origin node N_last_I_origin)
480 // Origin node of the last I-cell is global_node_idx = last_global_cell_idx_i.
481 // Its local index in rank's DA: (last_global_cell_idx_i - info->xs) + xs_gnode_rank
482 *ci_metric_lnode_out = xs_gnode_rank + (last_global_cell_idx_i - info->xs);
483 *xi_metric_logic_out = 1.0 - 1.0e-6;
484
485 ierr = PetscRandomGetValueReal(*rand_logic_j_ptr, &r_val_j_sel); CHKERRQ(ierr);
486 local_cell_idx_on_face_dim1 = (PetscInt)(r_val_j_sel * num_owned_cells_on_rank_j);
487 local_cell_idx_on_face_dim1 = PetscMin(PetscMax(0, local_cell_idx_on_face_dim1), num_owned_cells_on_rank_j - 1);
488 *cj_metric_lnode_out = ys_gnode_rank + local_cell_idx_on_face_dim1;
489
490 ierr = PetscRandomGetValueReal(*rand_logic_k_ptr, &r_val_k_sel); CHKERRQ(ierr);
491 local_cell_idx_on_face_dim2 = (PetscInt)(r_val_k_sel * num_owned_cells_on_rank_k);
492 local_cell_idx_on_face_dim2 = PetscMin(PetscMax(0, local_cell_idx_on_face_dim2), num_owned_cells_on_rank_k - 1);
493 *ck_metric_lnode_out = zs_gnode_rank + local_cell_idx_on_face_dim2;
494
495 ierr = PetscRandomGetValueReal(*rand_logic_j_ptr, eta_metric_logic_out); CHKERRQ(ierr);
496 ierr = PetscRandomGetValueReal(*rand_logic_k_ptr, zta_metric_logic_out); CHKERRQ(ierr);
497 break;
498 // ... (Cases for Y and Z faces, following the same pattern) ...
499 case BC_FACE_NEG_Y:
500 *cj_metric_lnode_out = ys_gnode_rank;
501 *eta_metric_logic_out = 1.0e-6;
502 ierr = PetscRandomGetValueReal(*rand_logic_i_ptr, &r_val_i_sel); CHKERRQ(ierr);
503 local_cell_idx_on_face_dim1 = (PetscInt)(r_val_i_sel * num_owned_cells_on_rank_i);
504 local_cell_idx_on_face_dim1 = PetscMin(PetscMax(0, local_cell_idx_on_face_dim1), num_owned_cells_on_rank_i - 1);
505 *ci_metric_lnode_out = xs_gnode_rank + local_cell_idx_on_face_dim1;
506 ierr = PetscRandomGetValueReal(*rand_logic_k_ptr, &r_val_k_sel); CHKERRQ(ierr);
507 local_cell_idx_on_face_dim2 = (PetscInt)(r_val_k_sel * num_owned_cells_on_rank_k);
508 local_cell_idx_on_face_dim2 = PetscMin(PetscMax(0, local_cell_idx_on_face_dim2), num_owned_cells_on_rank_k - 1);
509 *ck_metric_lnode_out = zs_gnode_rank + local_cell_idx_on_face_dim2;
510 ierr = PetscRandomGetValueReal(*rand_logic_i_ptr, xi_metric_logic_out); CHKERRQ(ierr);
511 ierr = PetscRandomGetValueReal(*rand_logic_k_ptr, zta_metric_logic_out); CHKERRQ(ierr);
512 break;
513 case BC_FACE_POS_Y:
514 *cj_metric_lnode_out = ys_gnode_rank + (last_global_cell_idx_j - info->ys);
515 *eta_metric_logic_out = 1.0 - 1.0e-6;
516 ierr = PetscRandomGetValueReal(*rand_logic_i_ptr, &r_val_i_sel); CHKERRQ(ierr);
517 local_cell_idx_on_face_dim1 = (PetscInt)(r_val_i_sel * num_owned_cells_on_rank_i);
518 local_cell_idx_on_face_dim1 = PetscMin(PetscMax(0, local_cell_idx_on_face_dim1), num_owned_cells_on_rank_i - 1);
519 *ci_metric_lnode_out = xs_gnode_rank + local_cell_idx_on_face_dim1;
520 ierr = PetscRandomGetValueReal(*rand_logic_k_ptr, &r_val_k_sel); CHKERRQ(ierr);
521 local_cell_idx_on_face_dim2 = (PetscInt)(r_val_k_sel * num_owned_cells_on_rank_k);
522 local_cell_idx_on_face_dim2 = PetscMin(PetscMax(0, local_cell_idx_on_face_dim2), num_owned_cells_on_rank_k - 1);
523 *ck_metric_lnode_out = zs_gnode_rank + local_cell_idx_on_face_dim2;
524 ierr = PetscRandomGetValueReal(*rand_logic_i_ptr, xi_metric_logic_out); CHKERRQ(ierr);
525 ierr = PetscRandomGetValueReal(*rand_logic_k_ptr, zta_metric_logic_out); CHKERRQ(ierr);
526 break;
527 case BC_FACE_NEG_Z: // Your example case
528 *ck_metric_lnode_out = zs_gnode_rank; // Cell origin is the first owned node in K by this rank
529 *zta_metric_logic_out = 1.0e-6; // Place particle slightly inside this cell from its -Z face
530 // Tangential dimensions are I and J
531 ierr = PetscRandomGetValueReal(*rand_logic_i_ptr, &r_val_i_sel); CHKERRQ(ierr);
532 local_cell_idx_on_face_dim1 = (PetscInt)(r_val_i_sel * num_owned_cells_on_rank_i);
533 local_cell_idx_on_face_dim1 = PetscMin(PetscMax(0, local_cell_idx_on_face_dim1), num_owned_cells_on_rank_i - 1);
534 *ci_metric_lnode_out = xs_gnode_rank + local_cell_idx_on_face_dim1;
535
536 ierr = PetscRandomGetValueReal(*rand_logic_j_ptr, &r_val_j_sel); CHKERRQ(ierr);
537 local_cell_idx_on_face_dim2 = (PetscInt)(r_val_j_sel * num_owned_cells_on_rank_j);
538 local_cell_idx_on_face_dim2 = PetscMin(PetscMax(0, local_cell_idx_on_face_dim2), num_owned_cells_on_rank_j - 1);
539 *cj_metric_lnode_out = ys_gnode_rank + local_cell_idx_on_face_dim2;
540
541 ierr = PetscRandomGetValueReal(*rand_logic_i_ptr, xi_metric_logic_out); CHKERRQ(ierr); // Intra-cell logical for I
542 ierr = PetscRandomGetValueReal(*rand_logic_j_ptr, eta_metric_logic_out); CHKERRQ(ierr); // Intra-cell logical for J
543 break;
544 case BC_FACE_POS_Z:
545 *ck_metric_lnode_out = zs_gnode_rank + (last_global_cell_idx_k - info->zs);
546 *zta_metric_logic_out = 1.0 - 1.0e-6;
547 ierr = PetscRandomGetValueReal(*rand_logic_i_ptr, &r_val_i_sel); CHKERRQ(ierr);
548 local_cell_idx_on_face_dim1 = (PetscInt)(r_val_i_sel * num_owned_cells_on_rank_i);
549 local_cell_idx_on_face_dim1 = PetscMin(PetscMax(0, local_cell_idx_on_face_dim1), num_owned_cells_on_rank_i - 1);
550 *ci_metric_lnode_out = xs_gnode_rank + local_cell_idx_on_face_dim1;
551 ierr = PetscRandomGetValueReal(*rand_logic_j_ptr, &r_val_j_sel); CHKERRQ(ierr);
552 local_cell_idx_on_face_dim2 = (PetscInt)(r_val_j_sel * num_owned_cells_on_rank_j);
553 local_cell_idx_on_face_dim2 = PetscMin(PetscMax(0, local_cell_idx_on_face_dim2), num_owned_cells_on_rank_j - 1);
554 *cj_metric_lnode_out = ys_gnode_rank + local_cell_idx_on_face_dim2;
555 ierr = PetscRandomGetValueReal(*rand_logic_i_ptr, xi_metric_logic_out); CHKERRQ(ierr);
556 ierr = PetscRandomGetValueReal(*rand_logic_j_ptr, eta_metric_logic_out); CHKERRQ(ierr);
557 break;
558 default:
559 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "GetRandomCellAndLogicOnInletFace: Invalid user->identifiedInletBCFace %d. \n", user->identifiedInletBCFace);
560 }
561
562 PetscReal eps = 1.0e-7;
564 *eta_metric_logic_out = PetscMin(PetscMax(0.0, *eta_metric_logic_out), 1.0 - eps);
565 *zta_metric_logic_out = PetscMin(PetscMax(0.0, *zta_metric_logic_out), 1.0 - eps);
567 *xi_metric_logic_out = PetscMin(PetscMax(0.0, *xi_metric_logic_out), 1.0 - eps);
568 *zta_metric_logic_out = PetscMin(PetscMax(0.0, *zta_metric_logic_out), 1.0 - eps);
569 } else {
570 *xi_metric_logic_out = PetscMin(PetscMax(0.0, *xi_metric_logic_out), 1.0 - eps);
571 *eta_metric_logic_out = PetscMin(PetscMax(0.0, *eta_metric_logic_out), 1.0 - eps);
572 }
573
574 LOG_ALLOW(LOCAL, LOG_VERBOSE, "Rank %d: Target Cell Node =(%d,%d,%d). (xi,et,zt)=(%.2e,%.2f,%.2f). \n",
575 rank_for_logging, *ci_metric_lnode_out, *cj_metric_lnode_out, *ck_metric_lnode_out,
576 *xi_metric_logic_out, *eta_metric_logic_out, *zta_metric_logic_out);
577
579
580 PetscFunctionReturn(0);
581}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ EnforceRHSBoundaryConditions()

PetscErrorCode EnforceRHSBoundaryConditions ( UserCtx user)

Enforces boundary conditions on the momentum equation's Right-Hand-Side (RHS) vector.

This function performs two critical roles based on the legacy implementation:

  1. Strong BC Enforcement for Physical Boundaries: For non-periodic boundaries (e.g., walls, inlets), it zeroes the normal component of the RHS in a "buffer" layer of cells just inside the domain (e.g., at i=mx-2). This strongly enforces Dirichlet conditions on velocity by preventing the time-stepping scheme from altering the boundary values set by ApplyBoundaryConditions.
  2. Ghost Cell Sanitization: For all boundary faces (i=0, i=mx-1, etc.), it zeroes out all components of the RHS. Since the RHS is a cell-centered quantity in this architecture, these locations correspond to ghost cells. This step sanitizes these unused locations, ensuring they do not contain garbage data that could affect diagnostics or other routines. This sanitization is performed for ALL boundary types, including periodic ones.

This function should be called immediately after the RHS vector is fully assembled (spatial + temporal terms) and before it is used in a time-stepping update.

Parameters
userThe UserCtx for the specific block being computed.
Returns
PetscErrorCode 0 on success.

Enforces boundary conditions on the momentum equation's Right-Hand-Side (RHS) vector.

Local to this translation unit.

Definition at line 591 of file Boundaries.c.

592{
593 PetscErrorCode ierr;
594 DMDALocalInfo info = user->info;
595 Cmpnts ***rhs;
596
597 // --- Grid extents for this MPI rank and global grid dimensions ---
598 const PetscInt xs = info.xs, xe = xs + info.xm;
599 const PetscInt ys = info.ys, ye = ys + info.ym;
600 const PetscInt zs = info.zs, ze = zs + info.zm;
601 const PetscInt mx = info.mx, my = info.my, mz = info.mz;
602
603 PetscFunctionBeginUser;
605
606 // Get a writable pointer to the local data of the global RHS vector.
607 ierr = DMDAVecGetArray(user->fda, user->Rhs, &rhs); CHKERRQ(ierr);
608
609 // ========================================================================
610 // --- I-DIRECTION (X-FACES) ---
611 // ========================================================================
612
613 // --- Negative X Face (i=0, the first physical face) ---
614 if (xs == 0) {
615 // This logic applies ONLY to physical (non-periodic) boundaries.
617 const PetscInt i = 0;
618 for (PetscInt k = zs; k < ze; k++) {
619 for (PetscInt j = ys; j < ye; j++) {
620 rhs[k][j][i].x = 0.0;
621 rhs[k][j][i].y = 0.0;
622 rhs[k][j][i].z = 0.0;
623 }
624 }
625 }
626 }
627
628 // --- Positive X Face (physical face i=mx-2, dummy location i=mx-1) ---
629 if (xe == mx) {
630 // Step 1: Enforce strong BC on the LAST PHYSICAL face (i=mx-2) for non-periodic cases.
632 const PetscInt i = mx - 2;
633 for (PetscInt k = zs; k < ze; k++) {
634 for (PetscInt j = ys; j < ye; j++) {
635 rhs[k][j][i].x = 0.0;
636 }
637 }
638 }
639 // Step 2: Unconditionally sanitize the DUMMY location (i=mx-1).
640 const PetscInt i = mx - 1;
641 for (PetscInt k = zs; k < ze; k++) {
642 for (PetscInt j = ys; j < ye; j++) {
643 rhs[k][j][i].x = 0.0;
644 rhs[k][j][i].y = 0.0;
645 rhs[k][j][i].z = 0.0;
646 }
647 }
648 }
649
650 // ========================================================================
651 // --- J-DIRECTION (Y-FACES) ---
652 // ========================================================================
653
654 // --- Negative Y Face (j=0, the first physical face) ---
655 if (ys == 0) {
657 const PetscInt j = 0;
658 for (PetscInt k = zs; k < ze; k++) {
659 for (PetscInt i = xs; i < xe; i++) {
660 rhs[k][j][i].x = 0.0;
661 rhs[k][j][i].y = 0.0;
662 rhs[k][j][i].z = 0.0;
663 }
664 }
665 }
666 }
667
668 // --- Positive Y Face (physical face j=my-2, dummy location j=my-1) ---
669 if (ye == my) {
671 const PetscInt j = my - 2;
672 for (PetscInt k = zs; k < ze; k++) {
673 for (PetscInt i = xs; i < xe; i++) {
674 rhs[k][j][i].y = 0.0;
675 }
676 }
677 }
678 const PetscInt j = my - 1;
679 for (PetscInt k = zs; k < ze; k++) {
680 for (PetscInt i = xs; i < xe; i++) {
681 rhs[k][j][i].x = 0.0;
682 rhs[k][j][i].y = 0.0;
683 rhs[k][j][i].z = 0.0;
684 }
685 }
686 }
687
688 // ========================================================================
689 // --- K-DIRECTION (Z-FACES) ---
690 // ========================================================================
691
692 // --- Negative Z Face (k=0, the first physical face) ---
693 if (zs == 0) {
695 const PetscInt k = 0;
696 for (PetscInt j = ys; j < ye; j++) {
697 for (PetscInt i = xs; i < xe; i++) {
698 rhs[k][j][i].x = 0.0;
699 rhs[k][j][i].y = 0.0;
700 rhs[k][j][i].z = 0.0;
701 }
702 }
703 }
704 }
705
706 // --- Positive Z Face (physical face k=mz-2, dummy location k=mz-1) ---
707 if (ze == mz) {
709 const PetscInt k = mz - 2;
710 for (PetscInt j = ys; j < ye; j++) {
711 for (PetscInt i = xs; i < xe; i++) {
712 rhs[k][j][i].z = 0.0;
713 }
714 }
715 }
716 const PetscInt k = mz - 1;
717 for (PetscInt j = ys; j < ye; j++) {
718 for (PetscInt i = xs; i < xe; i++) {
719 rhs[k][j][i].x = 0.0;
720 rhs[k][j][i].y = 0.0;
721 rhs[k][j][i].z = 0.0;
722 }
723 }
724 }
725
726 // --- Release the pointer to the local data ---
727 ierr = DMDAVecRestoreArray(user->fda, user->Rhs, &rhs); CHKERRQ(ierr);
728
729 LOG_ALLOW(LOCAL, LOG_TRACE, "Rank %d, Block %d: Finished enforcing RHS boundary conditions.\n",
730 user->simCtx->rank, user->_this);
731
733
734 PetscFunctionReturn(0);
735}
Vec Rhs
Definition variables.h:912
PetscInt _this
Definition variables.h:889
PetscScalar x
Definition variables.h:101
PetscScalar z
Definition variables.h:101
DMDALocalInfo info
Definition variables.h:883
PetscScalar y
Definition variables.h:101
A 3D point or vector with PetscScalar components.
Definition variables.h:100
Here is the caller graph for this function:

◆ TransferPeriodicFieldByDirection()

PetscErrorCode TransferPeriodicFieldByDirection ( UserCtx user,
const char *  field_name,
char  direction 
)

(Private Worker) Copies periodic data for a SINGLE field in a SINGLE direction.

This is a low-level helper that performs the memory copy from the local ghost array to the global array for a specified field and direction ('i', 'j', or 'k'). It contains NO communication logic; that is handled by the orchestrator.

Parameters
userThe main UserCtx struct.
field_nameThe string identifier for the field to transfer (e.g., "Ucat").
directionThe character 'i', 'j', or 'k' specifying the direction.
Returns
PetscErrorCode 0 on success.

(Private Worker) Copies periodic data for a SINGLE field in a SINGLE direction.

Local to this translation unit.

Definition at line 1578 of file Boundaries.c.

1579{
1580 PetscErrorCode ierr;
1581 DMDALocalInfo info = user->info;
1582 PetscInt xs = info.xs, xe = info.xs + info.xm;
1583 PetscInt ys = info.ys, ye = info.ys + info.ym;
1584 PetscInt zs = info.zs, ze = info.zs + info.zm;
1585 PetscInt mx = info.mx, my = info.my, mz = info.mz;
1586
1587 // --- Dispatcher to get DM, Vecs, and DoF for the specified field ---
1588 DM dm;
1589 Vec global_vec;
1590 Vec local_vec;
1591 PetscInt dof;
1592 // (This dispatcher is identical to your TransferPeriodicField function)
1593 if (strcmp(field_name, "Ucat") == 0) {
1594 dm = user->fda; global_vec = user->Ucat; local_vec = user->lUcat; dof = 3;
1595 } else if (strcmp(field_name, "P") == 0) {
1596 dm = user->da; global_vec = user->P; local_vec = user->lP; dof = 1;
1597 } else if (strcmp(field_name, "Phi") == 0) {
1598 dm = user->da; global_vec = user->Phi; local_vec = user->lPhi; dof = 1;
1599 } else if (strcmp(field_name, "Nvert") == 0) {
1600 dm = user->da; global_vec = user->Nvert; local_vec = user->lNvert; dof = 1;
1601 } else if (strcmp(field_name, "Nu_t") == 0 || strcmp(field_name, "Eddy Viscosity") == 0) {
1602 dm = user->da; global_vec = user->Nu_t; local_vec = user->lNu_t; dof = 1;
1603 } else if (strcmp(field_name, "CS") == 0 || strcmp(field_name, "Cs") == 0) {
1604 dm = user->da; global_vec = user->CS; local_vec = user->lCs; dof = 1;
1605 } else if (strcmp(field_name, "Diffusivity") == 0) {
1606 dm = user->da; global_vec = user->Diffusivity; local_vec = user->lDiffusivity; dof = 1;
1607 } else if (strcmp(field_name, "Aj") == 0) {
1608 dm = user->da; global_vec = user->Aj; local_vec = user->lAj; dof = 1;
1609 } else {
1610 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_UNKNOWN_TYPE, "Unknown field name '%s'", field_name);
1611 }
1612
1613 PetscFunctionBeginUser;
1614
1615 // --- Execute the copy logic based on DoF and Direction ---
1616 if (dof == 1) { // --- Handle SCALAR fields (PetscReal) ---
1617 PetscReal ***g_array, ***l_array;
1618 ierr = DMDAVecGetArray(dm, global_vec, &g_array); CHKERRQ(ierr);
1619 ierr = DMDAVecGetArrayRead(dm, local_vec, (void*)&l_array); CHKERRQ(ierr); // Use Read for safety
1620
1621 switch (direction) {
1622 case 'i':
1623 if (user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && xs == 0) for (PetscInt k=zs; k<ze; k++) for (PetscInt j=ys; j<ye; j++) g_array[k][j][xs] = l_array[k][j][xs-2];
1624 if (user->boundary_faces[BC_FACE_POS_X].mathematical_type == PERIODIC && xe == mx) for (PetscInt k=zs; k<ze; k++) for (PetscInt j=ys; j<ye; j++) g_array[k][j][xe-1] = l_array[k][j][xe+1];
1625 break;
1626 case 'j':
1627 if (user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && ys == 0) for (PetscInt k=zs; k<ze; k++) for (PetscInt i=xs; i<xe; i++) g_array[k][ys][i] = l_array[k][ys-2][i];
1628 if (user->boundary_faces[BC_FACE_POS_Y].mathematical_type == PERIODIC && ye == my) for (PetscInt k=zs; k<ze; k++) for (PetscInt i=xs; i<xe; i++) g_array[k][ye-1][i] = l_array[k][ye+1][i];
1629 break;
1630 case 'k':
1631 if (user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && zs == 0) for (PetscInt j=ys; j<ye; j++) for (PetscInt i=xs; i<xe; i++) g_array[zs][j][i] = l_array[zs-2][j][i];
1632 if (user->boundary_faces[BC_FACE_POS_Z].mathematical_type == PERIODIC && ze == mz) for (PetscInt j=ys; j<ye; j++) for (PetscInt i=xs; i<xe; i++) g_array[ze-1][j][i] = l_array[ze+1][j][i];
1633 break;
1634 default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Invalid direction '%c'", direction);
1635 }
1636 ierr = DMDAVecRestoreArray(dm, global_vec, &g_array); CHKERRQ(ierr);
1637 ierr = DMDAVecRestoreArrayRead(dm, local_vec, (void*)&l_array); CHKERRQ(ierr);
1638
1639 } else if (dof == 3) { // --- Handle VECTOR fields (Cmpnts) ---
1640 Cmpnts ***g_array, ***l_array;
1641 ierr = DMDAVecGetArray(dm, global_vec, &g_array); CHKERRQ(ierr);
1642 ierr = DMDAVecGetArrayRead(dm, local_vec, (void*)&l_array); CHKERRQ(ierr);
1643
1644 switch (direction) {
1645 case 'i':
1646 if (user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && xs == 0) for (PetscInt k=zs; k<ze; k++) for (PetscInt j=ys; j<ye; j++) g_array[k][j][xs] = l_array[k][j][xs-2];
1647 if (user->boundary_faces[BC_FACE_POS_X].mathematical_type == PERIODIC && xe == mx) for (PetscInt k=zs; k<ze; k++) for (PetscInt j=ys; j<ye; j++) g_array[k][j][xe-1] = l_array[k][j][xe+1];
1648 break;
1649 case 'j':
1650 if (user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && ys == 0) for (PetscInt k=zs; k<ze; k++) for (PetscInt i=xs; i<xe; i++) g_array[k][ys][i] = l_array[k][ys-2][i];
1651 if (user->boundary_faces[BC_FACE_POS_Y].mathematical_type == PERIODIC && ye == my) for (PetscInt k=zs; k<ze; k++) for (PetscInt i=xs; i<xe; i++) g_array[k][ye-1][i] = l_array[k][ye+1][i];
1652 break;
1653 case 'k':
1654 if (user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && zs == 0) for (PetscInt j=ys; j<ye; j++) for (PetscInt i=xs; i<xe; i++) g_array[zs][j][i] = l_array[zs-2][j][i];
1655 if (user->boundary_faces[BC_FACE_POS_Z].mathematical_type == PERIODIC && ze == mz) for (PetscInt j=ys; j<ye; j++) for (PetscInt i=xs; i<xe; i++) g_array[ze-1][j][i] = l_array[ze+1][j][i];
1656 break;
1657 default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Invalid direction '%c'", direction);
1658 }
1659 ierr = DMDAVecRestoreArray(dm, global_vec, &g_array); CHKERRQ(ierr);
1660 ierr = DMDAVecRestoreArrayRead(dm, local_vec, (void*)&l_array); CHKERRQ(ierr);
1661 }
1662
1663 PetscFunctionReturn(0);
1664}
Vec lNvert
Definition variables.h:904
Vec Phi
Definition variables.h:904
Vec lCs
Definition variables.h:935
Vec lPhi
Definition variables.h:904
Vec lNu_t
Definition variables.h:935
Vec Nu_t
Definition variables.h:935
Vec Ucat
Definition variables.h:904
Vec Diffusivity
Definition variables.h:907
Vec lAj
Definition variables.h:927
Vec lUcat
Definition variables.h:904
Vec Nvert
Definition variables.h:904
Vec lDiffusivity
Definition variables.h:907
Here is the caller graph for this function:

◆ SynchronizePeriodicCellFields()

PetscErrorCode SynchronizePeriodicCellFields ( UserCtx user,
PetscInt  num_fields,
const char *  field_names[] 
)

Synchronizes periodic endpoint cells for a list of cell-centered fields.

The fields are first communicated from global to local storage. Each periodic direction is then transferred in i-j-k order, with an intermediate ghost refresh after every active direction so periodic edges and corners inherit the values established by earlier directions. Only global duplicate planes in active periodic directions are repaired; non-periodic directions are untouched. The routine is a no-op, including no local refresh, when every direction is nonperiodic. During active periodic synchronization it internally refreshes the local vectors, but it is not a general replacement for UpdateLocalGhosts().

Supported fields are Ucat, P, Phi, Nvert, Nu_t, CS, Diffusivity, and Aj (Eddy Viscosity and Cs are accepted as compatibility aliases for Nu_t and CS, respectively).

Parameters
userThe main UserCtx struct.
num_fieldsThe number of entries in field_names.
field_namesThe cell-centered fields to synchronize.
Returns
PetscErrorCode 0 on success.

Synchronizes periodic endpoint cells for a list of cell-centered fields.

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

Definition at line 1673 of file Boundaries.c.

1674{
1675 PetscErrorCode ierr;
1676 PetscBool periodic_i;
1677 PetscBool periodic_j;
1678 PetscBool periodic_k;
1679
1680 PetscFunctionBeginUser;
1681
1682 if (num_fields == 0) PetscFunctionReturn(0);
1683
1684 periodic_i =
1687 periodic_j =
1690 periodic_k =
1693
1694 if (!periodic_i && !periodic_j && !periodic_k) PetscFunctionReturn(0);
1695
1696 for (PetscInt field = 0; field < num_fields; field++) {
1697 ierr = UpdateLocalGhosts(user, field_names[field]); CHKERRQ(ierr);
1698 }
1699
1700 if (periodic_i) {
1701 for (PetscInt field = 0; field < num_fields; field++) {
1702 ierr = TransferPeriodicFieldByDirection(user, field_names[field], 'i'); CHKERRQ(ierr);
1703 }
1704 for (PetscInt field = 0; field < num_fields; field++) {
1705 ierr = UpdateLocalGhosts(user, field_names[field]); CHKERRQ(ierr);
1706 }
1707 }
1708
1709 if (periodic_j) {
1710 for (PetscInt field = 0; field < num_fields; field++) {
1711 ierr = TransferPeriodicFieldByDirection(user, field_names[field], 'j'); CHKERRQ(ierr);
1712 }
1713 for (PetscInt field = 0; field < num_fields; field++) {
1714 ierr = UpdateLocalGhosts(user, field_names[field]); CHKERRQ(ierr);
1715 }
1716 }
1717
1718 if (periodic_k) {
1719 for (PetscInt field = 0; field < num_fields; field++) {
1720 ierr = TransferPeriodicFieldByDirection(user, field_names[field], 'k'); CHKERRQ(ierr);
1721 }
1722 for (PetscInt field = 0; field < num_fields; field++) {
1723 ierr = UpdateLocalGhosts(user, field_names[field]); CHKERRQ(ierr);
1724 }
1725 }
1726
1727 PetscFunctionReturn(0);
1728}
PetscErrorCode TransferPeriodicFieldByDirection(UserCtx *user, const char *field_name, char direction)
Internal helper implementation: TransferPeriodicFieldByDirection().
PetscErrorCode UpdateLocalGhosts(UserCtx *user, const char *fieldName)
Updates the local vector (including ghost points) from its corresponding global vector.
Definition setup.c:1755
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TransferPeriodicFaceFieldByDirection()

PetscErrorCode TransferPeriodicFaceFieldByDirection ( UserCtx user,
const char *  field_name,
char  face_direction,
char  periodic_direction 
)

Transfers one persistent single-face-family field in one periodic direction.

This lower-level helper updates owned global endpoint/dummy values from an up-to-date local ghosted vector. face_direction identifies the field's storage family (‘'i’,'j', or'k');periodic_direction` selects the direction transferred during this call.

Parameters
userThe main UserCtx struct.
field_nameRegistered persistent I/J/K-face field name.
face_directionFace family containing the field (‘'i’,'j', or'k'). @param periodic_direction Periodic direction to transfer ('i','j', or'k'`).
Returns
PetscErrorCode 0 on success.

Transfers one persistent single-face-family field in one periodic direction.

Definition at line 1841 of file Boundaries.c.

1843{
1844 PetscErrorCode ierr;
1845 DMDALocalInfo info = user->info;
1846 PetscInt xs = info.xs, xe = info.xs + info.xm;
1847 PetscInt ys = info.ys, ye = info.ys + info.ym;
1848 PetscInt zs = info.zs, ze = info.zs + info.zm;
1849 PetscInt mx = info.mx, my = info.my, mz = info.mz;
1850 DM dm;
1851 Vec global_vec, local_vec;
1852 PetscInt dof;
1853
1854 PetscFunctionBeginUser;
1855 PetscCheck(periodic_direction == 'i' || periodic_direction == 'j' || periodic_direction == 'k',
1856 PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG,
1857 "Invalid periodic direction '%c'; expected 'i', 'j', or 'k'.", periodic_direction);
1858 PetscCall(GetPersistentFaceField(user, field_name, face_direction, &dm, &global_vec, &local_vec, &dof));
1859
1860 if (dof == 1) {
1861 PetscReal ***global_array, ***local_array;
1862 ierr = DMDAVecGetArray(dm, global_vec, &global_array); CHKERRQ(ierr);
1863 ierr = DMDAVecGetArrayRead(dm, local_vec, &local_array); CHKERRQ(ierr);
1864
1865 if (periodic_direction == 'i') {
1866 if (user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && xs == 0)
1867 for (PetscInt k=zs; k<ze; k++) for (PetscInt j=ys; j<ye; j++) global_array[k][j][0] = local_array[k][j][-2];
1868 if (user->boundary_faces[BC_FACE_POS_X].mathematical_type == PERIODIC && xe == mx)
1869 for (PetscInt k=zs; k<ze; k++) for (PetscInt j=ys; j<ye; j++) global_array[k][j][mx-1] = local_array[k][j][mx+1];
1870 } else if (periodic_direction == 'j') {
1871 if (user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && ys == 0)
1872 for (PetscInt k=zs; k<ze; k++) for (PetscInt i=xs; i<xe; i++) global_array[k][0][i] = local_array[k][-2][i];
1873 if (user->boundary_faces[BC_FACE_POS_Y].mathematical_type == PERIODIC && ye == my)
1874 for (PetscInt k=zs; k<ze; k++) for (PetscInt i=xs; i<xe; i++) global_array[k][my-1][i] = local_array[k][my+1][i];
1875 } else {
1876 if (user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && zs == 0)
1877 for (PetscInt j=ys; j<ye; j++) for (PetscInt i=xs; i<xe; i++) global_array[0][j][i] = local_array[-2][j][i];
1878 if (user->boundary_faces[BC_FACE_POS_Z].mathematical_type == PERIODIC && ze == mz)
1879 for (PetscInt j=ys; j<ye; j++) for (PetscInt i=xs; i<xe; i++) global_array[mz-1][j][i] = local_array[mz+1][j][i];
1880 }
1881 ierr = DMDAVecRestoreArrayRead(dm, local_vec, &local_array); CHKERRQ(ierr);
1882 ierr = DMDAVecRestoreArray(dm, global_vec, &global_array); CHKERRQ(ierr);
1883 } else {
1884 Cmpnts ***global_array, ***local_array;
1885 ierr = DMDAVecGetArray(dm, global_vec, &global_array); CHKERRQ(ierr);
1886 ierr = DMDAVecGetArrayRead(dm, local_vec, &local_array); CHKERRQ(ierr);
1887
1888 if (periodic_direction == 'i') {
1889 if (user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && xs == 0)
1890 for (PetscInt k=zs; k<ze; k++) for (PetscInt j=ys; j<ye; j++) global_array[k][j][0] = local_array[k][j][-2];
1891 if (user->boundary_faces[BC_FACE_POS_X].mathematical_type == PERIODIC && xe == mx)
1892 for (PetscInt k=zs; k<ze; k++) for (PetscInt j=ys; j<ye; j++) global_array[k][j][mx-1] = local_array[k][j][mx+1];
1893 } else if (periodic_direction == 'j') {
1894 if (user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && ys == 0)
1895 for (PetscInt k=zs; k<ze; k++) for (PetscInt i=xs; i<xe; i++) global_array[k][0][i] = local_array[k][-2][i];
1896 if (user->boundary_faces[BC_FACE_POS_Y].mathematical_type == PERIODIC && ye == my)
1897 for (PetscInt k=zs; k<ze; k++) for (PetscInt i=xs; i<xe; i++) global_array[k][my-1][i] = local_array[k][my+1][i];
1898 } else {
1899 if (user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && zs == 0)
1900 for (PetscInt j=ys; j<ye; j++) for (PetscInt i=xs; i<xe; i++) global_array[0][j][i] = local_array[-2][j][i];
1901 if (user->boundary_faces[BC_FACE_POS_Z].mathematical_type == PERIODIC && ze == mz)
1902 for (PetscInt j=ys; j<ye; j++) for (PetscInt i=xs; i<xe; i++) global_array[mz-1][j][i] = local_array[mz+1][j][i];
1903 }
1904 ierr = DMDAVecRestoreArrayRead(dm, local_vec, &local_array); CHKERRQ(ierr);
1905 ierr = DMDAVecRestoreArray(dm, global_vec, &global_array); CHKERRQ(ierr);
1906 }
1907
1908 PetscFunctionReturn(0);
1909}
static PetscErrorCode GetPersistentFaceField(UserCtx *user, const char *field_name, char face_direction, DM *dm, Vec *global_vec, Vec *local_vec, PetscInt *dof)
Resolves one registered persistent single-face-family field.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ SynchronizePeriodicFaceFields()

PetscErrorCode SynchronizePeriodicFaceFields ( UserCtx user,
char  face_direction,
PetscInt  num_fields,
const char *  field_names[] 
)

Synchronizes persistent fields belonging to one face family.

The function performs deterministic I/J/K directional passes with an intermediate ghost refresh after each active periodic direction. It updates persistent global seam/dummy values only; face-specific local stencil repair remains a separate operation.

Parameters
userThe main UserCtx struct.
face_directionFace family shared by every field (‘'i’,'j', or'k'). @param num_fields Number of entries infield_names`.
field_namesRegistered persistent face-field names.
Returns
PetscErrorCode 0 on success.

Synchronizes persistent fields belonging to one face family.

Definition at line 1916 of file Boundaries.c.

1918{
1919 PetscErrorCode ierr;
1920 const char periodic_directions[3] = {'i', 'j', 'k'};
1921 const BCFace negative_faces[3] = {BC_FACE_NEG_X, BC_FACE_NEG_Y, BC_FACE_NEG_Z};
1922 const BCFace positive_faces[3] = {BC_FACE_POS_X, BC_FACE_POS_Y, BC_FACE_POS_Z};
1923
1924 PetscFunctionBeginUser;
1925 PetscCheck(num_fields >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE,
1926 "Number of face fields cannot be negative.");
1927 if (num_fields == 0) PetscFunctionReturn(0);
1928 PetscCheck(field_names != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
1929 "Face field-name array cannot be NULL.");
1930
1931 for (PetscInt field = 0; field < num_fields; field++) {
1932 DM dm;
1933 Vec global_vec, local_vec;
1934 PetscInt dof;
1935 PetscCall(GetPersistentFaceField(user, field_names[field], face_direction,
1936 &dm, &global_vec, &local_vec, &dof));
1937 ierr = UpdateLocalGhosts(user, field_names[field]); CHKERRQ(ierr);
1938 if (IsFaceCenterCoordinateField(field_names[field])) {
1939 ierr = TranslatePeriodicFaceCenterGhosts(user, local_vec); CHKERRQ(ierr);
1940 }
1941 }
1942
1943 for (PetscInt direction = 0; direction < 3; direction++) {
1944 const PetscBool active =
1945 user->boundary_faces[negative_faces[direction]].mathematical_type == PERIODIC ||
1946 user->boundary_faces[positive_faces[direction]].mathematical_type == PERIODIC;
1947 if (!active) continue;
1948
1949 for (PetscInt field = 0; field < num_fields; field++) {
1950 ierr = TransferPeriodicFaceFieldByDirection(user, field_names[field], face_direction,
1951 periodic_directions[direction]); CHKERRQ(ierr);
1952 }
1953 for (PetscInt field = 0; field < num_fields; field++) {
1954 DM dm;
1955 Vec global_vec, local_vec;
1956 PetscInt dof;
1957 ierr = UpdateLocalGhosts(user, field_names[field]); CHKERRQ(ierr);
1958 if (IsFaceCenterCoordinateField(field_names[field])) {
1959 PetscCall(GetPersistentFaceField(user, field_names[field], face_direction,
1960 &dm, &global_vec, &local_vec, &dof));
1961 ierr = TranslatePeriodicFaceCenterGhosts(user, local_vec); CHKERRQ(ierr);
1962 }
1963 }
1964 }
1965
1966 PetscFunctionReturn(0);
1967}
static PetscBool IsFaceCenterCoordinateField(const char *field_name)
Returns whether a registered face field stores physical coordinates.
static PetscErrorCode TranslatePeriodicFaceCenterGhosts(UserCtx *user, Vec local_vec)
Applies geometric translations to wrapped face-center ghost coordinates.
PetscErrorCode TransferPeriodicFaceFieldByDirection(UserCtx *user, const char *field_name, char face_direction, char periodic_direction)
Implementation of TransferPeriodicFaceFieldByDirection().
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TransferPeriodicStaggeredFieldByDirection()

PetscErrorCode TransferPeriodicStaggeredFieldByDirection ( UserCtx user,
const char *  field_name,
char  periodic_direction 
)

Transfers one persistent component-staggered field in one periodic direction.

A component-staggered vector stores its x, y, and z components on I-, J-, and K-faces, respectively. Persistent endpoint synchronization uses the same endpoint copy for all components; component-specific behavior is required later when repairing local stencil ghosts.

Parameters
userThe main UserCtx struct.
field_nameRegistered component-staggered field name.
periodic_directionPeriodic direction to transfer (‘'i’,'j', or'k'`).
Returns
PetscErrorCode 0 on success.

Transfers one persistent component-staggered field in one periodic direction.

Definition at line 1999 of file Boundaries.c.

2001{
2002 PetscErrorCode ierr;
2003 DMDALocalInfo info = user->info;
2004 PetscInt xs = info.xs, xe = info.xs + info.xm;
2005 PetscInt ys = info.ys, ye = info.ys + info.ym;
2006 PetscInt zs = info.zs, ze = info.zs + info.zm;
2007 PetscInt mx = info.mx, my = info.my, mz = info.mz;
2008 DM dm;
2009 Vec global_vec, local_vec;
2010 Cmpnts ***global_array, ***local_array;
2011
2012 PetscFunctionBeginUser;
2013 PetscCheck(periodic_direction == 'i' || periodic_direction == 'j' || periodic_direction == 'k',
2014 PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG,
2015 "Invalid periodic direction '%c'; expected 'i', 'j', or 'k'.", periodic_direction);
2016 PetscCall(GetPersistentStaggeredField(user, field_name, &dm, &global_vec, &local_vec));
2017
2018 ierr = DMDAVecGetArray(dm, global_vec, &global_array); CHKERRQ(ierr);
2019 ierr = DMDAVecGetArrayRead(dm, local_vec, &local_array); CHKERRQ(ierr);
2020
2021 if (periodic_direction == 'i') {
2022 if (user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && xs == 0)
2023 for (PetscInt k=zs; k<ze; k++) for (PetscInt j=ys; j<ye; j++) global_array[k][j][0] = local_array[k][j][-2];
2024 if (user->boundary_faces[BC_FACE_POS_X].mathematical_type == PERIODIC && xe == mx)
2025 for (PetscInt k=zs; k<ze; k++) for (PetscInt j=ys; j<ye; j++) global_array[k][j][mx-1] = local_array[k][j][mx+1];
2026 } else if (periodic_direction == 'j') {
2027 if (user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && ys == 0)
2028 for (PetscInt k=zs; k<ze; k++) for (PetscInt i=xs; i<xe; i++) global_array[k][0][i] = local_array[k][-2][i];
2029 if (user->boundary_faces[BC_FACE_POS_Y].mathematical_type == PERIODIC && ye == my)
2030 for (PetscInt k=zs; k<ze; k++) for (PetscInt i=xs; i<xe; i++) global_array[k][my-1][i] = local_array[k][my+1][i];
2031 } else {
2032 if (user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && zs == 0)
2033 for (PetscInt j=ys; j<ye; j++) for (PetscInt i=xs; i<xe; i++) global_array[0][j][i] = local_array[-2][j][i];
2034 if (user->boundary_faces[BC_FACE_POS_Z].mathematical_type == PERIODIC && ze == mz)
2035 for (PetscInt j=ys; j<ye; j++) for (PetscInt i=xs; i<xe; i++) global_array[mz-1][j][i] = local_array[mz+1][j][i];
2036 }
2037
2038 ierr = DMDAVecRestoreArrayRead(dm, local_vec, &local_array); CHKERRQ(ierr);
2039 ierr = DMDAVecRestoreArray(dm, global_vec, &global_array); CHKERRQ(ierr);
2040 PetscFunctionReturn(0);
2041}
static PetscErrorCode GetPersistentStaggeredField(UserCtx *user, const char *field_name, DM *dm, Vec *global_vec, Vec *local_vec)
Resolves one registered persistent component-staggered field.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ SynchronizePeriodicStaggeredFields()

PetscErrorCode SynchronizePeriodicStaggeredFields ( UserCtx user,
PetscInt  num_fields,
const char *  field_names[] 
)

Synchronizes persistent component-staggered vector fields.

The function performs deterministic I/J/K endpoint transfers with an intermediate ghost refresh after every active periodic direction. Currently Ucont is the only registered component-staggered field.

Parameters
userThe main UserCtx struct.
num_fieldsNumber of entries in field_names.
field_namesRegistered component-staggered field names.
Returns
PetscErrorCode 0 on success.

Synchronizes persistent component-staggered vector fields.

Definition at line 2048 of file Boundaries.c.

2050{
2051 PetscErrorCode ierr;
2052 const char periodic_directions[3] = {'i', 'j', 'k'};
2053 const BCFace negative_faces[3] = {BC_FACE_NEG_X, BC_FACE_NEG_Y, BC_FACE_NEG_Z};
2054 const BCFace positive_faces[3] = {BC_FACE_POS_X, BC_FACE_POS_Y, BC_FACE_POS_Z};
2055
2056 PetscFunctionBeginUser;
2057 PetscCheck(num_fields >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE,
2058 "Number of staggered fields cannot be negative.");
2059 if (num_fields == 0) PetscFunctionReturn(0);
2060 PetscCheck(field_names != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
2061 "Staggered field-name array cannot be NULL.");
2062
2063 for (PetscInt field = 0; field < num_fields; field++) {
2064 DM dm;
2065 Vec global_vec, local_vec;
2066 PetscCall(GetPersistentStaggeredField(user, field_names[field], &dm, &global_vec, &local_vec));
2067 ierr = UpdateLocalGhosts(user, field_names[field]); CHKERRQ(ierr);
2068 }
2069
2070 for (PetscInt direction = 0; direction < 3; direction++) {
2071 const PetscBool active =
2072 user->boundary_faces[negative_faces[direction]].mathematical_type == PERIODIC ||
2073 user->boundary_faces[positive_faces[direction]].mathematical_type == PERIODIC;
2074 if (!active) continue;
2075
2076 for (PetscInt field = 0; field < num_fields; field++) {
2077 ierr = TransferPeriodicStaggeredFieldByDirection(user, field_names[field],
2078 periodic_directions[direction]); CHKERRQ(ierr);
2079 }
2080 for (PetscInt field = 0; field < num_fields; field++) {
2081 ierr = UpdateLocalGhosts(user, field_names[field]); CHKERRQ(ierr);
2082 }
2083 }
2084
2085 PetscFunctionReturn(0);
2086}
PetscErrorCode TransferPeriodicStaggeredFieldByDirection(UserCtx *user, const char *field_name, char periodic_direction)
Implementation of TransferPeriodicStaggeredFieldByDirection().
Here is the call graph for this function:
Here is the caller graph for this function:

◆ PreparePeriodicQuickStencilFields()

PetscErrorCode PreparePeriodicQuickStencilFields ( UserCtx user,
Vec  local_vector_field,
Vec  local_scalar_field 
)

Repairs the outer adjacent periodic ghosts used by QUICK cell stencils.

The supplied local vectors must already contain a current PETSc periodic ghost exchange. The vector and scalar fields are repaired two logical cells across each active periodic seam so QUICK's i-1/i+2 equivalents are valid.

Parameters
userMain block context containing periodic boundary metadata.
local_vector_fieldGhosted three-component cell-centered field.
local_scalar_fieldGhosted scalar cell-centered field.
Returns
PetscErrorCode 0 on success.

Repairs the outer adjacent periodic ghosts used by QUICK cell stencils.

Definition at line 2093 of file Boundaries.c.

2095{
2096 DMDALocalInfo info = user->info;
2097 Cmpnts ***vector_array;
2098 PetscReal ***scalar_array;
2099 const PetscInt xs = info.xs, xe = info.xs + info.xm;
2100 const PetscInt ys = info.ys, ye = info.ys + info.ym;
2101 const PetscInt zs = info.zs, ze = info.zs + info.zm;
2102 const PetscInt gxs = info.gxs, gxe = info.gxs + info.gxm;
2103 const PetscInt gys = info.gys, gye = info.gys + info.gym;
2104 const PetscInt gzs = info.gzs, gze = info.gzs + info.gzm;
2105
2106 PetscFunctionBeginUser;
2107 PetscCheck(local_vector_field && local_scalar_field, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
2108 "QUICK stencil repair requires both local vector and scalar fields.");
2109 PetscCall(DMDAVecGetArray(user->fda, local_vector_field, &vector_array));
2110 PetscCall(DMDAVecGetArray(user->da, local_scalar_field, &scalar_array));
2111
2112 if (user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && xs == 0) {
2113 for (PetscInt k = gzs; k < gze; k++) for (PetscInt j = gys; j < gye; j++) {
2114 vector_array[k][j][-1] = vector_array[k][j][-3];
2115 scalar_array[k][j][-1] = scalar_array[k][j][-3];
2116 }
2117 }
2118 if (user->boundary_faces[BC_FACE_POS_X].mathematical_type == PERIODIC && xe == info.mx) {
2119 for (PetscInt k = gzs; k < gze; k++) for (PetscInt j = gys; j < gye; j++) {
2120 vector_array[k][j][info.mx] = vector_array[k][j][info.mx + 2];
2121 scalar_array[k][j][info.mx] = scalar_array[k][j][info.mx + 2];
2122 }
2123 }
2124 if (user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && ys == 0) {
2125 for (PetscInt k = gzs; k < gze; k++) for (PetscInt i = gxs; i < gxe; i++) {
2126 vector_array[k][-1][i] = vector_array[k][-3][i];
2127 scalar_array[k][-1][i] = scalar_array[k][-3][i];
2128 }
2129 }
2130 if (user->boundary_faces[BC_FACE_POS_Y].mathematical_type == PERIODIC && ye == info.my) {
2131 for (PetscInt k = gzs; k < gze; k++) for (PetscInt i = gxs; i < gxe; i++) {
2132 vector_array[k][info.my][i] = vector_array[k][info.my + 2][i];
2133 scalar_array[k][info.my][i] = scalar_array[k][info.my + 2][i];
2134 }
2135 }
2136 if (user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && zs == 0) {
2137 for (PetscInt j = gys; j < gye; j++) for (PetscInt i = gxs; i < gxe; i++) {
2138 vector_array[-1][j][i] = vector_array[-3][j][i];
2139 scalar_array[-1][j][i] = scalar_array[-3][j][i];
2140 }
2141 }
2142 if (user->boundary_faces[BC_FACE_POS_Z].mathematical_type == PERIODIC && ze == info.mz) {
2143 for (PetscInt j = gys; j < gye; j++) for (PetscInt i = gxs; i < gxe; i++) {
2144 vector_array[info.mz][j][i] = vector_array[info.mz + 2][j][i];
2145 scalar_array[info.mz][j][i] = scalar_array[info.mz + 2][j][i];
2146 }
2147 }
2148
2149 PetscCall(DMDAVecRestoreArray(user->da, local_scalar_field, &scalar_array));
2150 PetscCall(DMDAVecRestoreArray(user->fda, local_vector_field, &vector_array));
2151 PetscFunctionReturn(0);
2152}
Here is the caller graph for this function:

◆ SynchronizePeriodicLocalStaggeredField()

PetscErrorCode SynchronizePeriodicLocalStaggeredField ( UserCtx user,
Vec  local_field 
)

Synchronizes one local-only component-staggered periodic work field.

This helper communicates locally computed owned entries, establishes the normal-component periodic endpoint values, and communicates once more.

Parameters
userMain block context containing periodic boundary metadata.
local_fieldGhosted local component-staggered vector.
Returns
PetscErrorCode 0 on success.

Synchronizes one local-only component-staggered periodic work field.

Definition at line 2159 of file Boundaries.c.

2160{
2161 DMDALocalInfo info = user->info;
2162 Cmpnts ***array;
2163 const PetscInt xs = info.xs, xe = info.xs + info.xm;
2164 const PetscInt ys = info.ys, ye = info.ys + info.ym;
2165 const PetscInt zs = info.zs, ze = info.zs + info.zm;
2166
2167 PetscFunctionBeginUser;
2168 PetscCheck(local_field, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
2169 "Local staggered field cannot be NULL.");
2170 PetscCall(DMLocalToLocalBegin(user->fda, local_field, INSERT_VALUES, local_field));
2171 PetscCall(DMLocalToLocalEnd(user->fda, local_field, INSERT_VALUES, local_field));
2172 PetscCall(DMDAVecGetArray(user->fda, local_field, &array));
2173
2174 if (user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && xs == 0)
2175 for (PetscInt k = zs; k < ze; k++) for (PetscInt j = ys; j < ye; j++) array[k][j][0].x = array[k][j][-2].x;
2176 if (user->boundary_faces[BC_FACE_POS_X].mathematical_type == PERIODIC && xe == info.mx)
2177 for (PetscInt k = zs; k < ze; k++) for (PetscInt j = ys; j < ye; j++) array[k][j][info.mx - 1].x = array[k][j][info.mx + 1].x;
2178 if (user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && ys == 0)
2179 for (PetscInt k = zs; k < ze; k++) for (PetscInt i = xs; i < xe; i++) array[k][0][i].y = array[k][-2][i].y;
2180 if (user->boundary_faces[BC_FACE_POS_Y].mathematical_type == PERIODIC && ye == info.my)
2181 for (PetscInt k = zs; k < ze; k++) for (PetscInt i = xs; i < xe; i++) array[k][info.my - 1][i].y = array[k][info.my + 1][i].y;
2182 if (user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && zs == 0)
2183 for (PetscInt j = ys; j < ye; j++) for (PetscInt i = xs; i < xe; i++) array[0][j][i].z = array[-2][j][i].z;
2184 if (user->boundary_faces[BC_FACE_POS_Z].mathematical_type == PERIODIC && ze == info.mz)
2185 for (PetscInt j = ys; j < ye; j++) for (PetscInt i = xs; i < xe; i++) array[info.mz - 1][j][i].z = array[info.mz + 1][j][i].z;
2186
2187 PetscCall(DMDAVecRestoreArray(user->fda, local_field, &array));
2188 PetscCall(DMLocalToLocalBegin(user->fda, local_field, INSERT_VALUES, local_field));
2189 PetscCall(DMLocalToLocalEnd(user->fda, local_field, INSERT_VALUES, local_field));
2190 PetscFunctionReturn(0);
2191}
Here is the caller graph for this function:

◆ TransferPeriodicField()

PetscErrorCode TransferPeriodicField ( UserCtx user,
const char *  field_name 
)

Legacy monolithic periodic endpoint transfer for one cell-centered field.

Retained for compatibility with older callers. New code should use SynchronizePeriodicCellFields, which provides ordered directional transfers with the required intermediate ghost communication.

Parameters
userThe main UserCtx struct.
field_nameThe string identifier for the field to transfer.
Returns
PetscErrorCode 0 on success.

Legacy monolithic periodic endpoint transfer for one cell-centered field.

Local to this translation unit.

Definition at line 2199 of file Boundaries.c.

2200{
2201 PetscErrorCode ierr;
2202 DMDALocalInfo info = user->info;
2203 PetscInt xs = info.xs, xe = info.xs + info.xm;
2204 PetscInt ys = info.ys, ye = info.ys + info.ym;
2205 PetscInt zs = info.zs, ze = info.zs + info.zm;
2206 PetscInt mx = info.mx, my = info.my, mz = info.mz;
2207
2208 // --- Local variables to hold the specific details of the chosen field ---
2209 DM dm;
2210 Vec global_vec;
2211 Vec local_vec;
2212 PetscInt dof;
2213
2214 PetscFunctionBeginUser;
2216
2217 // --- STEP 1: Dispatcher - Set the specific DM, Vecs, and dof based on field_name ---
2218 // Compatibility implementation retained for legacy callers. New periodic
2219 // cell fields should be added to the directional dispatcher used by
2220 // SynchronizePeriodicCellFields().
2221 if (strcmp(field_name, "Ucat") == 0) {
2222 dm = user->fda;
2223 global_vec = user->Ucat;
2224 local_vec = user->lUcat;
2225 dof = 3;
2226 } else if (strcmp(field_name, "P") == 0) {
2227 dm = user->da;
2228 global_vec = user->P;
2229 local_vec = user->lP;
2230 dof = 1;
2231 } else if (strcmp(field_name, "Nvert") == 0) {
2232 dm = user->da;
2233 global_vec = user->Nvert;
2234 local_vec = user->lNvert;
2235 dof = 1;
2236 } else if (strcmp(field_name, "Eddy Viscosity") == 0) {
2237 dm = user->da;
2238 global_vec = user->Nu_t;
2239 local_vec = user->lNu_t;
2240 dof = 1;
2241 }
2242 /*
2243 // Example for future extension:
2244 else if (strcmp(field_name, "Temperature") == 0) {
2245 dm = user->da; // Assuming Temperature is scalar
2246 global_vec = user->T;
2247 local_vec = user->lT;
2248 dof = 1;
2249 }
2250 */
2251 else {
2252 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_UNKNOWN_TYPE, "Unknown field name '%s' in TransferPeriodicFieldByName.", field_name);
2253 }
2254
2255 LOG_ALLOW(GLOBAL,LOG_TRACE,"Periodic Transform being performed for field: %s with %d DoF.\n",field_name,dof);
2256 // --- STEP 2: Execute the copy logic using the dispatched variables ---
2257 if (dof == 1) { // --- Handle SCALAR fields (PetscReal) ---
2258 PetscReal ***g_array, ***l_array;
2259 ierr = DMDAVecGetArray(dm, global_vec, &g_array); CHKERRQ(ierr);
2260 ierr = DMDAVecGetArray(dm, local_vec, &l_array); CHKERRQ(ierr);
2261
2262 if (user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && xs == 0) for (PetscInt k=zs; k<ze; k++) for (PetscInt j=ys; j<ye; j++) g_array[k][j][xs] = l_array[k][j][xs-2];
2263 if (user->boundary_faces[BC_FACE_POS_X].mathematical_type == PERIODIC && xe == mx) for (PetscInt k=zs; k<ze; k++) for (PetscInt j=ys; j<ye; j++) g_array[k][j][xe-1] = l_array[k][j][xe+1];
2264 if (user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && ys == 0) for (PetscInt k=zs; k<ze; k++) for (PetscInt i=xs; i<xe; i++) g_array[k][ys][i] = l_array[k][ys-2][i];
2265 if (user->boundary_faces[BC_FACE_POS_Y].mathematical_type == PERIODIC && ye == my) for (PetscInt k=zs; k<ze; k++) for (PetscInt i=xs; i<xe; i++) g_array[k][ye-1][i] = l_array[k][ye+1][i];
2266 if (user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && zs == 0) for (PetscInt j=ys; j<ye; j++) for (PetscInt i=xs; i<xe; i++) g_array[zs][j][i] = l_array[zs-2][j][i];
2267 if (user->boundary_faces[BC_FACE_POS_Z].mathematical_type == PERIODIC && ze == mz) for (PetscInt j=ys; j<ye; j++) for (PetscInt i=xs; i<xe; i++) g_array[ze-1][j][i] = l_array[ze+1][j][i];
2268
2269 ierr = DMDAVecRestoreArray(dm, global_vec, &g_array); CHKERRQ(ierr);
2270 ierr = DMDAVecRestoreArray(dm, local_vec, &l_array); CHKERRQ(ierr);
2271
2272 } else if (dof == 3) { // --- Handle VECTOR fields (Cmpnts) ---
2273 Cmpnts ***g_array, ***l_array;
2274 ierr = DMDAVecGetArray(dm, global_vec, &g_array); CHKERRQ(ierr);
2275 ierr = DMDAVecGetArray(dm, local_vec, &l_array); CHKERRQ(ierr);
2276
2277 LOG_ALLOW(GLOBAL,LOG_VERBOSE,"Array %s read successfully (Global and Local).\n",field_name);
2278
2279 if (user->boundary_faces[BC_FACE_NEG_X].mathematical_type == PERIODIC && xs == 0) for (PetscInt k=zs; k<ze; k++) for (PetscInt j=ys; j<ye; j++) g_array[k][j][xs] = l_array[k][j][xs-2];
2280 if (user->boundary_faces[BC_FACE_POS_X].mathematical_type == PERIODIC && xe == mx) for (PetscInt k=zs; k<ze; k++) for (PetscInt j=ys; j<ye; j++) g_array[k][j][xe-1] = l_array[k][j][xe+1];
2281 if (user->boundary_faces[BC_FACE_NEG_Y].mathematical_type == PERIODIC && ys == 0) for (PetscInt k=zs; k<ze; k++) for (PetscInt i=xs; i<xe; i++) g_array[k][ys][i] = l_array[k][ys-2][i];
2282 if (user->boundary_faces[BC_FACE_POS_Y].mathematical_type == PERIODIC && ye == my) for (PetscInt k=zs; k<ze; k++) for (PetscInt i=xs; i<xe; i++) g_array[k][ye-1][i] = l_array[k][ye+1][i];
2283 if (user->boundary_faces[BC_FACE_NEG_Z].mathematical_type == PERIODIC && zs == 0) for (PetscInt j=ys; j<ye; j++) for (PetscInt i=xs; i<xe; i++) g_array[zs][j][i] = l_array[zs-2][j][i];
2284 if (user->boundary_faces[BC_FACE_POS_Z].mathematical_type == PERIODIC && ze == mz) for (PetscInt j=ys; j<ye; j++) for (PetscInt i=xs; i<xe; i++) g_array[ze-1][j][i] = l_array[ze+1][j][i];
2285
2286 ierr = DMDAVecRestoreArray(dm, global_vec, &g_array); CHKERRQ(ierr);
2287 ierr = DMDAVecRestoreArray(dm, local_vec, &l_array); CHKERRQ(ierr);
2288 }
2289 else{
2290 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_UNKNOWN_TYPE, "This function only accepts Fields with 1 or 3 DoF.");
2291 }
2292
2294 PetscFunctionReturn(0);
2295}

◆ TransferPeriodicFaceField()

PetscErrorCode TransferPeriodicFaceField ( UserCtx user,
const char *  field_name 
)

(Primitive) Copies periodic data from the interior to the local ghost cell region for a single field.

This primitive function performs a direct memory copy for a specified field, updating all periodic ghost faces (i, j, and k). It reads data from just inside the periodic boundary and writes it to the corresponding local ghost cells.

The copy is "two-cells deep" to support wider computational stencils.

This function does NOT involve any MPI communication; it operates entirely on local PETSc vectors.

Parameters
userThe main UserCtx struct.
field_nameThe string identifier for the field to update (e.g., "Csi", "Ucont").
Returns
PetscErrorCode 0 on success.

(Primitive) Copies periodic data from the interior to the local ghost cell region for a single field.

Local to this translation unit.

Definition at line 2303 of file Boundaries.c.

2304{
2305 PetscErrorCode ierr;
2306 DMDALocalInfo info = user->info;
2307 PetscInt gxs = info.gxs, gxe = info.gxs + info.gxm;
2308 PetscInt gys = info.gys, gye = info.gys + info.gym;
2309 PetscInt gzs = info.gzs, gze = info.gzs + info.gzm;
2310 PetscInt mx = info.mx, my = info.my, mz = info.mz;
2311
2312 // --- Dispatcher to get the correct DM, Vec, and DoF for the specified field ---
2313 // Field-extension note: add one case here when a new field needs periodic
2314 // ghost-face transfer (typically for stencil-heavy operators).
2315 DM dm;
2316 Vec local_vec;
2317 PetscInt dof;
2318 // (This dispatcher contains all 17 potential fields)
2319 if (strcmp(field_name, "Ucont") == 0) { dm = user->fda; local_vec = user->lUcont; dof = 3; }
2320 else if (strcmp(field_name, "Csi") == 0) { dm = user->fda; local_vec = user->lCsi; dof = 3; }
2321 else if (strcmp(field_name, "Eta") == 0) { dm = user->fda; local_vec = user->lEta; dof = 3; }
2322 else if (strcmp(field_name, "Zet") == 0) { dm = user->fda; local_vec = user->lZet; dof = 3; }
2323 else if (strcmp(field_name, "ICsi") == 0) { dm = user->fda; local_vec = user->lICsi; dof = 3; }
2324 else if (strcmp(field_name, "IEta") == 0) { dm = user->fda; local_vec = user->lIEta; dof = 3; }
2325 else if (strcmp(field_name, "IZet") == 0) { dm = user->fda; local_vec = user->lIZet; dof = 3; }
2326 else if (strcmp(field_name, "JCsi") == 0) { dm = user->fda; local_vec = user->lJCsi; dof = 3; }
2327 else if (strcmp(field_name, "JEta") == 0) { dm = user->fda; local_vec = user->lJEta; dof = 3; }
2328 else if (strcmp(field_name, "JZet") == 0) { dm = user->fda; local_vec = user->lJZet; dof = 3; }
2329 else if (strcmp(field_name, "KCsi") == 0) { dm = user->fda; local_vec = user->lKCsi; dof = 3; }
2330 else if (strcmp(field_name, "KEta") == 0) { dm = user->fda; local_vec = user->lKEta; dof = 3; }
2331 else if (strcmp(field_name, "KZet") == 0) { dm = user->fda; local_vec = user->lKZet; dof = 3; }
2332 else if (strcmp(field_name, "Aj") == 0) { dm = user->da; local_vec = user->lAj; dof = 1; }
2333 else if (strcmp(field_name, "IAj") == 0) { dm = user->da; local_vec = user->lIAj; dof = 1; }
2334 else if (strcmp(field_name, "JAj") == 0) { dm = user->da; local_vec = user->lJAj; dof = 1; }
2335 else if (strcmp(field_name, "KAj") == 0) { dm = user->da; local_vec = user->lKAj; dof = 1; }
2336 else {
2337 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_UNKNOWN_TYPE, "Unknown field name '%s' in TransferPeriodicFaceField.", field_name);
2338 }
2339
2340 PetscFunctionBeginUser;
2341
2342 void *l_array_ptr;
2343 ierr = DMDAVecGetArray(dm, local_vec, &l_array_ptr); CHKERRQ(ierr);
2344
2345 // --- I-DIRECTION ---
2347 for (PetscInt k=gzs; k<gze; k++) for (PetscInt j=gys; j<gye; j++) {
2348 if (dof == 1) {
2349 PetscReal ***arr = (PetscReal***)l_array_ptr;
2350 arr[k][j][0] = arr[k][j][mx-2];
2351 arr[k][j][-1] = arr[k][j][mx-3];
2352 } else {
2353 Cmpnts ***arr = (Cmpnts***)l_array_ptr;
2354 arr[k][j][0] = arr[k][j][mx-2];
2355 arr[k][j][-1] = arr[k][j][mx-3];
2356 }
2357 }
2358 }
2360 for (PetscInt k=gzs; k<gze; k++) for (PetscInt j=gys; j<gye; j++) {
2361 if (dof == 1) {
2362 PetscReal ***arr = (PetscReal***)l_array_ptr;
2363 arr[k][j][mx-1] = arr[k][j][1];
2364 arr[k][j][mx] = arr[k][j][2];
2365 } else {
2366 Cmpnts ***arr = (Cmpnts***)l_array_ptr;
2367 arr[k][j][mx-1] = arr[k][j][1];
2368 arr[k][j][mx] = arr[k][j][2];
2369 }
2370 }
2371 }
2372
2373 // --- J-DIRECTION ---
2375 for (PetscInt k=gzs; k<gze; k++) for (PetscInt i=gxs; i<gxe; i++) {
2376 if (dof == 1) {
2377 PetscReal ***arr = (PetscReal***)l_array_ptr;
2378 arr[k][0][i] = arr[k][my-2][i];
2379 arr[k][-1][i] = arr[k][my-3][i];
2380 } else {
2381 Cmpnts ***arr = (Cmpnts***)l_array_ptr;
2382 arr[k][0][i] = arr[k][my-2][i];
2383 arr[k][-1][i] = arr[k][my-3][i];
2384 }
2385 }
2386 }
2388 for (PetscInt k=gzs; k<gze; k++) for (PetscInt i=gxs; i<gxe; i++) {
2389 if (dof == 1) {
2390 PetscReal ***arr = (PetscReal***)l_array_ptr;
2391 arr[k][my-1][i] = arr[k][1][i];
2392 arr[k][my][i] = arr[k][2][i];
2393 } else {
2394 Cmpnts ***arr = (Cmpnts***)l_array_ptr;
2395 arr[k][my-1][i] = arr[k][1][i];
2396 arr[k][my][i] = arr[k][2][i];
2397 }
2398 }
2399 }
2400
2401 // --- K-DIRECTION ---
2403 for (PetscInt j=gys; j<gye; j++) for (PetscInt i=gxs; i<gxe; i++) {
2404 if (dof == 1) {
2405 PetscReal ***arr = (PetscReal***)l_array_ptr;
2406 arr[0][j][i] = arr[mz-2][j][i];
2407 arr[-1][j][i] = arr[mz-3][j][i];
2408 } else {
2409 Cmpnts ***arr = (Cmpnts***)l_array_ptr;
2410 arr[0][j][i] = arr[mz-2][j][i];
2411 arr[-1][j][i] = arr[mz-3][j][i];
2412 }
2413 }
2414 }
2416 for (PetscInt j=gys; j<gye; j++) for (PetscInt i=gxs; i<gxe; i++) {
2417 if (dof == 1) {
2418 PetscReal ***arr = (PetscReal***)l_array_ptr;
2419 arr[mz-1][j][i] = arr[1][j][i];
2420 arr[mz][j][i] = arr[2][j][i];
2421 } else {
2422 Cmpnts ***arr = (Cmpnts***)l_array_ptr;
2423 arr[mz-1][j][i] = arr[1][j][i];
2424 arr[mz][j][i] = arr[2][j][i];
2425 }
2426 }
2427 }
2428
2429 ierr = DMDAVecRestoreArray(dm, local_vec, &l_array_ptr); CHKERRQ(ierr);
2430 PetscFunctionReturn(0);
2431}
Vec lIEta
Definition variables.h:930
Vec lIZet
Definition variables.h:930
Vec lZet
Definition variables.h:927
Vec lIAj
Definition variables.h:930
Vec lKEta
Definition variables.h:932
Vec lJCsi
Definition variables.h:931
Vec lKZet
Definition variables.h:932
Vec lJEta
Definition variables.h:931
Vec lCsi
Definition variables.h:927
Vec lKCsi
Definition variables.h:932
Vec lJZet
Definition variables.h:931
Vec lUcont
Definition variables.h:904
Vec lICsi
Definition variables.h:930
Vec lEta
Definition variables.h:927
Vec lJAj
Definition variables.h:931
Vec lKAj
Definition variables.h:932
Here is the caller graph for this function:

◆ ApplyMetricsPeriodicBCs()

PetscErrorCode ApplyMetricsPeriodicBCs ( UserCtx user)

(Orchestrator) Updates all metric-related fields in the local ghost cell regions for periodic boundaries.

This function synchronizes cell-centered Aj and the persistent I/J/K metric face families through the canonical MPI-safe synchronizers.

Parameters
userThe main UserCtx struct.
Returns
PetscErrorCode 0 on success.

(Orchestrator) Updates all metric-related fields in the local ghost cell regions for periodic boundaries.

Local to this translation unit.

Definition at line 2439 of file Boundaries.c.

2440{
2441 PetscErrorCode ierr;
2442 PetscFunctionBeginUser;
2444
2445 const char *cell_fields[] = {"Aj"};
2446 const char *i_face_fields[] = {"Centx", "Csi", "ICsi", "IEta", "IZet", "IAj"};
2447 const char *j_face_fields[] = {"Centy", "Eta", "JCsi", "JEta", "JZet", "JAj"};
2448 const char *k_face_fields[] = {"Centz", "Zet", "KCsi", "KEta", "KZet", "KAj"};
2449
2450 ierr = SynchronizePeriodicCellFields(user, 1, cell_fields); CHKERRQ(ierr);
2451 ierr = SynchronizePeriodicFaceFields(user, 'i', 6, i_face_fields); CHKERRQ(ierr);
2452 ierr = SynchronizePeriodicFaceFields(user, 'j', 6, j_face_fields); CHKERRQ(ierr);
2453 ierr = SynchronizePeriodicFaceFields(user, 'k', 6, k_face_fields); CHKERRQ(ierr);
2454
2456 PetscFunctionReturn(0);
2457}
PetscErrorCode SynchronizePeriodicFaceFields(UserCtx *user, char face_direction, PetscInt num_fields, const char *field_names[])
Implementation of SynchronizePeriodicFaceFields().
PetscErrorCode SynchronizePeriodicCellFields(UserCtx *user, PetscInt num_fields, const char *field_names[])
Implementation of SynchronizePeriodicCellFields().
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ApplyPeriodicBCs()

PetscErrorCode ApplyPeriodicBCs ( UserCtx user)

Applies periodic boundary conditions by copying data across domain boundaries for all relevant fields.

This is the canonical periodic orchestrator for geometric consistency. It updates Ucat, P, and Nvert through the generic cell synchronizer and updates staggered Ucont through the component-staggered synchronizer.

Future extension rule: add new periodic variables by extending the existing field string dispatchers and invoking them from this orchestrator.

Parameters
userThe main UserCtx struct.
Returns
PetscErrorCode 0 on success.

Applies periodic boundary conditions by copying data across domain boundaries for all relevant fields.

Local to this translation unit.

Definition at line 2465 of file Boundaries.c.

2466{
2467 PetscErrorCode ierr;
2468 PetscBool is_any_periodic = PETSC_FALSE;
2469
2470 PetscFunctionBeginUser;
2471
2473
2474 for (int i = 0; i < 6; i++) {
2475 if (user->boundary_faces[i].mathematical_type == PERIODIC) {
2476 is_any_periodic = PETSC_TRUE;
2477 break;
2478 }
2479 }
2480
2481 if (!is_any_periodic) {
2482 LOG_ALLOW(GLOBAL,LOG_TRACE, "No periodic boundaries defined; skipping ApplyPeriodicBCs.\n");
2484 PetscFunctionReturn(0);
2485 }
2486
2487 LOG_ALLOW(GLOBAL, LOG_TRACE, "Applying periodic boundary conditions for all fields.\n");
2488
2489 // STEP 1: Synchronize periodic cell-centered fields in deterministic direction order.
2490 const char *cell_fields[] = {"Ucat", "P", "Nvert"};
2491 ierr = SynchronizePeriodicCellFields(user, 3, cell_fields); CHKERRQ(ierr);
2492
2493 /* if (user->solve_temperature) { ierr = UpdateLocalGhosts(user, "Temperature"); CHKERRQ(ierr); } */
2494
2495 // STEP 2: Synchronize persistent staggered endpoints and repair local
2496 // component-normal ghosts through UpdateLocalGhosts().
2497 const char *staggered_fields[] = {"Ucont"};
2498 ierr = SynchronizePeriodicStaggeredFields(user, 1, staggered_fields); CHKERRQ(ierr);
2499
2500 // FUTURE EXTENSION: Add new cell fields through SynchronizePeriodicCellFields().
2501 /*
2502 if (user->solve_temperature) {
2503 const char *temperature_field[] = {"Temperature"};
2504 ierr = SynchronizePeriodicCellFields(user, 1, temperature_field); CHKERRQ(ierr);
2505 }
2506 */
2507
2509 PetscFunctionReturn(0);
2510}
PetscErrorCode SynchronizePeriodicStaggeredFields(UserCtx *user, PetscInt num_fields, const char *field_names[])
Implementation of SynchronizePeriodicStaggeredFields().
Here is the call graph for this function:
Here is the caller graph for this function:

◆ UpdateDummyCells()

PetscErrorCode UpdateDummyCells ( UserCtx user)

Updates the dummy cells (ghost nodes) on the faces of the local domain for NON-PERIODIC boundaries.

This function's role is to apply a second-order extrapolation to set the ghost cell values based on the boundary condition value (stored in ubcs) and the first interior cell.

NOTE: This function deliberately IGNORES periodic boundaries. It is part of a larger workflow where ApplyPeriodicBCs handles periodic faces first.

CRITICAL DETAIL: This function uses shrunken loop ranges (lxs, lxe, etc.) to intentionally update only the flat part of the faces, avoiding the edges and

corners. The edges and corners are then handled separately by UpdateCornerNodes. This precisely replicates the logic of the original FormBCS function.

Parameters
userThe main UserCtx struct containing all necessary data.
Returns
PetscErrorCode 0 on success.

Updates the dummy cells (ghost nodes) on the faces of the local domain for NON-PERIODIC boundaries.

Local to this translation unit.

Definition at line 2518 of file Boundaries.c.

2519{
2520 PetscErrorCode ierr;
2521 DM fda = user->fda;
2522 DMDALocalInfo info = user->info;
2523 PetscInt xs = info.xs, xe = info.xs + info.xm;
2524 PetscInt ys = info.ys, ye = info.ys + info.ym;
2525 PetscInt zs = info.zs, ze = info.zs + info.zm;
2526 PetscInt mx = info.mx, my = info.my, mz = info.mz;
2527
2528 // --- Calculate shrunken loop ranges to avoid edges and corners ---
2529 PetscInt lxs = xs, lxe = xe;
2530 PetscInt lys = ys, lye = ye;
2531 PetscInt lzs = zs, lze = ze;
2532
2533 if (xs == 0) lxs = xs + 1;
2534 if (ys == 0) lys = ys + 1;
2535 if (zs == 0) lzs = zs + 1;
2536
2537 if (xe == mx) lxe = xe - 1;
2538 if (ye == my) lye = ye - 1;
2539 if (ze == mz) lze = ze - 1;
2540
2541 Cmpnts ***ucat, ***ubcs;
2542 PetscFunctionBeginUser;
2543
2544 ierr = DMDAVecGetArray(fda, user->Bcs.Ubcs, &ubcs); CHKERRQ(ierr);
2545 ierr = DMDAVecGetArray(fda, user->Ucat, &ucat); CHKERRQ(ierr);
2546
2547 // -X Face
2548 if (user->boundary_faces[BC_FACE_NEG_X].mathematical_type != PERIODIC && xs == 0) {
2549 for (PetscInt k = lzs; k < lze; k++) for (PetscInt j = lys; j < lye; j++) {
2550 ucat[k][j][xs].x = 2.0 * ubcs[k][j][xs].x - ucat[k][j][xs + 1].x;
2551 ucat[k][j][xs].y = 2.0 * ubcs[k][j][xs].y - ucat[k][j][xs + 1].y;
2552 ucat[k][j][xs].z = 2.0 * ubcs[k][j][xs].z - ucat[k][j][xs + 1].z;
2553 }
2554 }
2555 // +X Face
2556 if (user->boundary_faces[BC_FACE_POS_X].mathematical_type != PERIODIC && xe == mx) {
2557 for (PetscInt k = lzs; k < lze; k++) for (PetscInt j = lys; j < lye; j++) {
2558 ucat[k][j][xe-1].x = 2.0 * ubcs[k][j][xe-1].x - ucat[k][j][xe - 2].x;
2559 ucat[k][j][xe-1].y = 2.0 * ubcs[k][j][xe-1].y - ucat[k][j][xe - 2].y;
2560 ucat[k][j][xe-1].z = 2.0 * ubcs[k][j][xe-1].z - ucat[k][j][xe - 2].z;
2561 }
2562 }
2563
2564 // -Y Face
2565 if (user->boundary_faces[BC_FACE_NEG_Y].mathematical_type != PERIODIC && ys == 0) {
2566 for (PetscInt k = lzs; k < lze; k++) for (PetscInt i = lxs; i < lxe; i++) {
2567 ucat[k][ys][i].x = 2.0 * ubcs[k][ys][i].x - ucat[k][ys + 1][i].x;
2568 ucat[k][ys][i].y = 2.0 * ubcs[k][ys][i].y - ucat[k][ys + 1][i].y;
2569 ucat[k][ys][i].z = 2.0 * ubcs[k][ys][i].z - ucat[k][ys + 1][i].z;
2570 }
2571 }
2572 // +Y Face
2573 if (user->boundary_faces[BC_FACE_POS_Y].mathematical_type != PERIODIC && ye == my) {
2574 for (PetscInt k = lzs; k < lze; k++) for (PetscInt i = lxs; i < lxe; i++) {
2575 ucat[k][ye-1][i].x = 2.0 * ubcs[k][ye-1][i].x - ucat[k][ye-2][i].x;
2576 ucat[k][ye-1][i].y = 2.0 * ubcs[k][ye-1][i].y - ucat[k][ye-2][i].y;
2577 ucat[k][ye-1][i].z = 2.0 * ubcs[k][ye-1][i].z - ucat[k][ye-2][i].z;
2578 }
2579 }
2580
2581 // -Z Face
2582 if (user->boundary_faces[BC_FACE_NEG_Z].mathematical_type != PERIODIC && zs == 0) {
2583 for (PetscInt j = lys; j < lye; j++) for (PetscInt i = lxs; i < lxe; i++) {
2584 ucat[zs][j][i].x = 2.0 * ubcs[zs][j][i].x - ucat[zs + 1][j][i].x;
2585 ucat[zs][j][i].y = 2.0 * ubcs[zs][j][i].y - ucat[zs + 1][j][i].y;
2586 ucat[zs][j][i].z = 2.0 * ubcs[zs][j][i].z - ucat[zs + 1][j][i].z;
2587 }
2588 }
2589 // +Z Face
2590 if (user->boundary_faces[BC_FACE_POS_Z].mathematical_type != PERIODIC && ze == mz) {
2591 for (PetscInt j = lys; j < lye; j++) for (PetscInt i = lxs; i < lxe; i++) {
2592 ucat[ze-1][j][i].x = 2.0 * ubcs[ze-1][j][i].x - ucat[ze-2][j][i].x;
2593 ucat[ze-1][j][i].y = 2.0 * ubcs[ze-1][j][i].y - ucat[ze-2][j][i].y;
2594 ucat[ze-1][j][i].z = 2.0 * ubcs[ze-1][j][i].z - ucat[ze-2][j][i].z;
2595 }
2596 }
2597
2598 ierr = DMDAVecRestoreArray(fda, user->Bcs.Ubcs, &ubcs); CHKERRQ(ierr);
2599 ierr = DMDAVecRestoreArray(fda, user->Ucat, &ucat); CHKERRQ(ierr);
2600
2601 PetscFunctionReturn(0);
2602}
Vec Ubcs
Physical Cartesian velocity at boundary faces. Full 3D array but only boundary-face entries are meani...
Definition variables.h:121
BCS Bcs
Definition variables.h:899
Here is the caller graph for this function:

◆ UpdateCornerNodes()

PetscErrorCode UpdateCornerNodes ( UserCtx user)

Updates the corner and edge ghost nodes of the local domain by averaging.

This function should be called AFTER the face ghost nodes are finalized by both ApplyPeriodicBCs and UpdateDummyCells. It resolves the values at shared edges and corners by averaging the values of adjacent, previously-computed ghost nodes.

The logic is generic and works correctly regardless of the boundary types on the adjacent faces (e.g., it will correctly average a periodic face neighbor with a wall face neighbor).

Parameters
userThe main UserCtx struct containing all necessary data.
Returns
PetscErrorCode 0 on success.

Updates the corner and edge ghost nodes of the local domain by averaging.

Local to this translation unit.

Definition at line 2610 of file Boundaries.c.

2611{
2612 PetscErrorCode ierr;
2613 DM da = user->da, fda = user->fda;
2614 DMDALocalInfo info = user->info;
2615 PetscInt xs = info.xs, xe = info.xs + info.xm;
2616 PetscInt ys = info.ys, ye = info.ys + info.ym;
2617 PetscInt zs = info.zs, ze = info.zs + info.zm;
2618 PetscInt mx = info.mx, my = info.my, mz = info.mz;
2619
2620 Cmpnts ***ucat;
2621 PetscReal ***p;
2622
2623 PetscFunctionBeginUser;
2624
2625 ierr = DMDAVecGetArray(fda, user->Ucat, &ucat); CHKERRQ(ierr);
2626 ierr = DMDAVecGetArray(da, user->P, &p); CHKERRQ(ierr);
2627
2628 // --- Update Edges and Corners by Averaging ---
2629 // The order of these blocks ensures that corners (where 3 faces meet) are
2630 // computed using data from edges (where 2 faces meet), which are computed first.
2631// Edges connected to the -Z face (k=zs)
2632 if (zs == 0) {
2633 if (xs == 0) {
2634 for (PetscInt j = ys; j < ye; j++) {
2635 p[zs][j][xs] = 0.5 * (p[zs+1][j][xs] + p[zs][j][xs+1]);
2636 ucat[zs][j][xs].x = 0.5 * (ucat[zs+1][j][xs].x + ucat[zs][j][xs+1].x);
2637 ucat[zs][j][xs].y = 0.5 * (ucat[zs+1][j][xs].y + ucat[zs][j][xs+1].y);
2638 ucat[zs][j][xs].z = 0.5 * (ucat[zs+1][j][xs].z + ucat[zs][j][xs+1].z);
2639 }
2640 }
2641 if (xe == mx) {
2642 for (PetscInt j = ys; j < ye; j++) {
2643 p[zs][j][mx-1] = 0.5 * (p[zs+1][j][mx-1] + p[zs][j][mx-2]);
2644 ucat[zs][j][mx-1].x = 0.5 * (ucat[zs+1][j][mx-1].x + ucat[zs][j][mx-2].x);
2645 ucat[zs][j][mx-1].y = 0.5 * (ucat[zs+1][j][mx-1].y + ucat[zs][j][mx-2].y);
2646 ucat[zs][j][mx-1].z = 0.5 * (ucat[zs+1][j][mx-1].z + ucat[zs][j][mx-2].z);
2647 }
2648 }
2649 if (ys == 0) {
2650 for (PetscInt i = xs; i < xe; i++) {
2651 p[zs][ys][i] = 0.5 * (p[zs+1][ys][i] + p[zs][ys+1][i]);
2652 ucat[zs][ys][i].x = 0.5 * (ucat[zs+1][ys][i].x + ucat[zs][ys+1][i].x);
2653 ucat[zs][ys][i].y = 0.5 * (ucat[zs+1][ys][i].y + ucat[zs][ys+1][i].y);
2654 ucat[zs][ys][i].z = 0.5 * (ucat[zs+1][ys][i].z + ucat[zs][ys+1][i].z);
2655 }
2656 }
2657 if (ye == my) {
2658 for (PetscInt i = xs; i < xe; i++) {
2659 p[zs][my-1][i] = 0.5 * (p[zs+1][my-1][i] + p[zs][my-2][i]);
2660 ucat[zs][my-1][i].x = 0.5 * (ucat[zs+1][my-1][i].x + ucat[zs][my-2][i].x);
2661 ucat[zs][my-1][i].y = 0.5 * (ucat[zs+1][my-1][i].y + ucat[zs][my-2][i].y);
2662 ucat[zs][my-1][i].z = 0.5 * (ucat[zs+1][my-1][i].z + ucat[zs][my-2][i].z);
2663 }
2664 }
2665 }
2666
2667 // Edges connected to the +Z face (k=ze-1)
2668 if (ze == mz) {
2669 if (xs == 0) {
2670 for (PetscInt j = ys; j < ye; j++) {
2671 p[mz-1][j][xs] = 0.5 * (p[mz-2][j][xs] + p[mz-1][j][xs+1]);
2672 ucat[mz-1][j][xs].x = 0.5 * (ucat[mz-2][j][xs].x + ucat[mz-1][j][xs+1].x);
2673 ucat[mz-1][j][xs].y = 0.5 * (ucat[mz-2][j][xs].y + ucat[mz-1][j][xs+1].y);
2674 ucat[mz-1][j][xs].z = 0.5 * (ucat[mz-2][j][xs].z + ucat[mz-1][j][xs+1].z);
2675 }
2676 }
2677 if (xe == mx) {
2678 for (PetscInt j = ys; j < ye; j++) {
2679 p[mz-1][j][mx-1] = 0.5 * (p[mz-2][j][mx-1] + p[mz-1][j][mx-2]);
2680 ucat[mz-1][j][mx-1].x = 0.5 * (ucat[mz-2][j][mx-1].x + ucat[mz-1][j][mx-2].x);
2681 ucat[mz-1][j][mx-1].y = 0.5 * (ucat[mz-2][j][mx-1].y + ucat[mz-1][j][mx-2].y);
2682 ucat[mz-1][j][mx-1].z = 0.5 * (ucat[mz-2][j][mx-1].z + ucat[mz-1][j][mx-2].z);
2683 }
2684 }
2685 if (ys == 0) {
2686 for (PetscInt i = xs; i < xe; i++) {
2687 p[mz-1][ys][i] = 0.5 * (p[mz-2][ys][i] + p[mz-1][ys+1][i]);
2688 ucat[mz-1][ys][i].x = 0.5 * (ucat[mz-2][ys][i].x + ucat[mz-1][ys+1][i].x);
2689 ucat[mz-1][ys][i].y = 0.5 * (ucat[mz-2][ys][i].y + ucat[mz-1][ys+1][i].y);
2690 ucat[mz-1][ys][i].z = 0.5 * (ucat[mz-2][ys][i].z + ucat[mz-1][ys+1][i].z);
2691 }
2692 }
2693 if (ye == my) {
2694 for (PetscInt i = xs; i < xe; i++) {
2695 p[mz-1][my-1][i] = 0.5 * (p[mz-2][my-1][i] + p[mz-1][my-2][i]);
2696 ucat[mz-1][my-1][i].x = 0.5 * (ucat[mz-2][my-1][i].x + ucat[mz-1][my-2][i].x);
2697 ucat[mz-1][my-1][i].y = 0.5 * (ucat[mz-2][my-1][i].y + ucat[mz-1][my-2][i].y);
2698 ucat[mz-1][my-1][i].z = 0.5 * (ucat[mz-2][my-1][i].z + ucat[mz-1][my-2][i].z);
2699 }
2700 }
2701 }
2702
2703 // Remaining edges on the XY plane (that are not on Z faces)
2704 if (ys == 0) {
2705 if (xs == 0) {
2706 for (PetscInt k = zs; k < ze; k++) {
2707 p[k][ys][xs] = 0.5 * (p[k][ys+1][xs] + p[k][ys][xs+1]);
2708 ucat[k][ys][xs].x = 0.5 * (ucat[k][ys+1][xs].x + ucat[k][ys][xs+1].x);
2709 ucat[k][ys][xs].y = 0.5 * (ucat[k][ys+1][xs].y + ucat[k][ys][xs+1].y);
2710 ucat[k][ys][xs].z = 0.5 * (ucat[k][ys+1][xs].z + ucat[k][ys][xs+1].z);
2711 }
2712 }
2713 if (xe == mx) {
2714 for (PetscInt k = zs; k < ze; k++) {
2715 p[k][ys][mx-1] = 0.5 * (p[k][ys+1][mx-1] + p[k][ys][mx-2]);
2716 ucat[k][ys][mx-1].x = 0.5 * (ucat[k][ys+1][mx-1].x + ucat[k][ys][mx-2].x);
2717 ucat[k][ys][mx-1].y = 0.5 * (ucat[k][ys+1][mx-1].y + ucat[k][ys][mx-2].y);
2718 ucat[k][ys][mx-1].z = 0.5 * (ucat[k][ys+1][mx-1].z + ucat[k][ys][mx-2].z);
2719 }
2720 }
2721 }
2722
2723 if (ye == my) {
2724 if (xs == 0) {
2725 for (PetscInt k = zs; k < ze; k++) {
2726 p[k][my-1][xs] = 0.5 * (p[k][my-2][xs] + p[k][my-1][xs+1]);
2727 ucat[k][my-1][xs].x = 0.5 * (ucat[k][my-2][xs].x + ucat[k][my-1][xs+1].x);
2728 ucat[k][my-1][xs].y = 0.5 * (ucat[k][my-2][xs].y + ucat[k][my-1][xs+1].y);
2729 ucat[k][my-1][xs].z = 0.5 * (ucat[k][my-2][xs].z + ucat[k][my-1][xs+1].z);
2730 }
2731 }
2732 if (xe == mx) {
2733 for (PetscInt k = zs; k < ze; k++) {
2734 p[k][my-1][mx-1] = 0.5 * (p[k][my-2][mx-1] + p[k][my-1][mx-2]);
2735 ucat[k][my-1][mx-1].x = 0.5 * (ucat[k][my-2][mx-1].x + ucat[k][my-1][mx-2].x);
2736 ucat[k][my-1][mx-1].y = 0.5 * (ucat[k][my-2][mx-1].y + ucat[k][my-1][mx-2].y);
2737 ucat[k][my-1][mx-1].z = 0.5 * (ucat[k][my-2][mx-1].z + ucat[k][my-1][mx-2].z);
2738 }
2739 }
2740 }
2741
2742 ierr = DMDAVecRestoreArray(fda, user->Ucat, &ucat); CHKERRQ(ierr);
2743 ierr = DMDAVecRestoreArray(da, user->P, &p); CHKERRQ(ierr);
2744
2745 PetscFunctionReturn(0);
2746}
Here is the caller graph for this function:

◆ UpdatePeriodicCornerNodes()

PetscErrorCode UpdatePeriodicCornerNodes ( UserCtx user,
PetscInt  num_fields,
const char *  field_names[] 
)

Legacy sequential periodic-corner update for a list of fields.

Retained for compatibility with older callers. New code should use SynchronizePeriodicCellFields, which additionally skips non-periodic directions and performs the initial ghost refresh.

Parameters
userThe main UserCtx struct.
num_fieldsThe number of fields in the field_names array.
field_namesAn array of strings with the names of fields to update (e.g., ["Ucat", "P"]).
Returns
PetscErrorCode 0 on success.

Legacy sequential periodic-corner update for a list of fields.

Local to this translation unit.

Definition at line 2754 of file Boundaries.c.

2755{
2756 PetscErrorCode ierr;
2757 PetscFunctionBeginUser;
2758
2759 if (num_fields == 0) PetscFunctionReturn(0);
2760
2761 // --- I-DIRECTION ---
2762 for (PetscInt i = 0; i < num_fields; i++) {
2763 ierr = TransferPeriodicFieldByDirection(user, field_names[i], 'i'); CHKERRQ(ierr);
2764 }
2765 // --- SYNC ---
2766 for (PetscInt i = 0; i < num_fields; i++) {
2767 ierr = UpdateLocalGhosts(user, field_names[i]); CHKERRQ(ierr);
2768 }
2769
2770 // --- J-DIRECTION ---
2771 for (PetscInt i = 0; i < num_fields; i++) {
2772 ierr = TransferPeriodicFieldByDirection(user, field_names[i], 'j'); CHKERRQ(ierr);
2773 }
2774 // --- SYNC ---
2775 for (PetscInt i = 0; i < num_fields; i++) {
2776 ierr = UpdateLocalGhosts(user, field_names[i]); CHKERRQ(ierr);
2777 }
2778
2779 // --- K-DIRECTION ---
2780 for (PetscInt i = 0; i < num_fields; i++) {
2781 ierr = TransferPeriodicFieldByDirection(user, field_names[i], 'k'); CHKERRQ(ierr);
2782 }
2783 // --- FINAL SYNC ---
2784 for (PetscInt i = 0; i < num_fields; i++) {
2785 ierr = UpdateLocalGhosts(user, field_names[i]); CHKERRQ(ierr);
2786 }
2787
2788 PetscFunctionReturn(0);
2789}
Here is the call graph for this function:

◆ ApplyWallFunction()

PetscErrorCode ApplyWallFunction ( UserCtx user)

Applies wall function modeling to near-wall velocities for all wall-type boundaries.

This function implements log-law wall functions to model the near-wall velocity profile without fully resolving the viscous sublayer. It is applicable to ALL wall-type boundaries regardless of their specific boundary condition (no-slip, moving wall, slip, etc.), as determined by the mathematical_type being WALL.

MATHEMATICAL BACKGROUND: Wall functions bridge the gap between the wall (y=0) and the first computational cell center by using empirical log-law relationships:

  • Viscous sublayer (y+ < 11.81): u+ = y+
  • Log-law region (y+ > 11.81): u+ = (1/κ) * ln(E * y+) where u+ = u/u_Ï„, y+ = y*u_Ï„/ν, κ = 0.41 (von Karman constant), E = exp(κB)

IMPLEMENTATION DETAILS: Unlike standard boundary conditions that set ghost cell values, wall functions:

  1. Read velocity from the SECOND interior cell (i±2, j±2, k±2)
  2. Compute wall shear stress using log-law
  3. Modify velocity at the FIRST interior cell (i±1, j±1, k±1)
  4. Keep ghost cell boundary values (ubcs, ucont) at zero

WORKFLOW:

  • Called from ApplyBoundaryConditions after standard BC application
  • Operates on ucat (Cartesian velocity)
  • Updates ustar (friction velocity field) for diagnostics/turbulence models
  • Ghost cells remain zero; UpdateDummyCells handles extrapolation afterward

GEOMETRIC QUANTITIES: sb = wall-normal distance from wall to first interior cell center sc = wall-normal distance from wall to second interior cell center
These are computed from cell Jacobians (aj) and face area vectors

APPLICABILITY:

  • Requires simCtx->wallfunction = true
  • Only processes faces where mathematical_type == WALL
  • Skips solid-embedded cells (nvert >= 0.1)
Parameters
userThe UserCtx containing all simulation state and geometry
Returns
PetscErrorCode 0 on success
Note
This function modifies interior cell velocities, NOT ghost cells
Wall roughness (ks) is currently set to 1e-16 (smooth wall)
See also
wall_function_loglaw() in wallfunction.c for the actual log-law implementation
noslip() in wallfunction.c for the initial linear interpolation

Applies wall function modeling to near-wall velocities for all wall-type boundaries.

Local to this translation unit.

Definition at line 2797 of file Boundaries.c.

2798{
2799 PetscErrorCode ierr;
2800 SimCtx *simCtx = user->simCtx;
2801 DMDALocalInfo *info = &user->info;
2802
2803 PetscFunctionBeginUser;
2804
2805 // =========================================================================
2806 // STEP 0: Early exit if wall functions are disabled
2807 // =========================================================================
2808 if (!simCtx->wallfunction) {
2809 PetscFunctionReturn(0);
2810 }
2811
2812 LOG_ALLOW(LOCAL, LOG_DEBUG, "Processing wall function boundaries.\n");
2813
2814 // =========================================================================
2815 // STEP 1: Get read/write access to all necessary field arrays
2816 // =========================================================================
2817 Cmpnts ***velocity_cartesian; // Cartesian velocity (modified)
2818 Cmpnts ***velocity_contravariant; // Contravariant velocity (set to zero at walls)
2819 Cmpnts ***velocity_boundary; // Boundary condition velocity (kept at zero)
2820 Cmpnts ***csi, ***eta, ***zet; // Metric tensor components (face normals)
2821 PetscReal ***node_vertex_flag; // Fluid/solid indicator (0=fluid, 1=solid)
2822 PetscReal ***cell_jacobian; // Grid Jacobian (1/volume)
2823 PetscReal ***friction_velocity; // u_tau (friction velocity field)
2824
2825 ierr = DMDAVecGetArray(user->fda, user->Ucat, &velocity_cartesian); CHKERRQ(ierr);
2826 ierr = DMDAVecGetArray(user->fda, user->Ucont, &velocity_contravariant); CHKERRQ(ierr);
2827 ierr = DMDAVecGetArray(user->fda, user->Bcs.Ubcs, &velocity_boundary); CHKERRQ(ierr);
2828 ierr = DMDAVecGetArrayRead(user->fda, user->lCsi, (const Cmpnts***)&csi); CHKERRQ(ierr);
2829 ierr = DMDAVecGetArrayRead(user->fda, user->lEta, (const Cmpnts***)&eta); CHKERRQ(ierr);
2830 ierr = DMDAVecGetArrayRead(user->fda, user->lZet, (const Cmpnts***)&zet); CHKERRQ(ierr);
2831 ierr = DMDAVecGetArrayRead(user->da, user->lNvert, (const PetscReal***)&node_vertex_flag); CHKERRQ(ierr);
2832 ierr = DMDAVecGetArrayRead(user->da, user->lAj, (const PetscReal***)&cell_jacobian); CHKERRQ(ierr);
2833 ierr = DMDAVecGetArray(user->da, user->lFriction_Velocity, &friction_velocity); CHKERRQ(ierr);
2834
2835 // =========================================================================
2836 // STEP 2: Define loop bounds (owned portion of the grid for this MPI rank)
2837 // =========================================================================
2838 PetscInt grid_start_i = info->xs, grid_end_i = info->xs + info->xm;
2839 PetscInt grid_start_j = info->ys, grid_end_j = info->ys + info->ym;
2840 PetscInt grid_start_k = info->zs, grid_end_k = info->zs + info->zm;
2841 PetscInt grid_size_i = info->mx, grid_size_j = info->my, grid_size_k = info->mz;
2842
2843 // Shrunken loop bounds: exclude domain edges and corners to avoid double-counting
2844 PetscInt loop_start_i = grid_start_i, loop_end_i = grid_end_i;
2845 PetscInt loop_start_j = grid_start_j, loop_end_j = grid_end_j;
2846 PetscInt loop_start_k = grid_start_k, loop_end_k = grid_end_k;
2847
2848 if (grid_start_i == 0) loop_start_i = grid_start_i + 1;
2849 if (grid_end_i == grid_size_i) loop_end_i = grid_end_i - 1;
2850 if (grid_start_j == 0) loop_start_j = grid_start_j + 1;
2851 if (grid_end_j == grid_size_j) loop_end_j = grid_end_j - 1;
2852 if (grid_start_k == 0) loop_start_k = grid_start_k + 1;
2853 if (grid_end_k == grid_size_k) loop_end_k = grid_end_k - 1;
2854
2855 // Wall roughness parameter (smooth wall by default, configurable via -wall_roughness).
2856 const PetscReal wall_roughness_height = user->simCtx->wall_roughness_height;
2857
2858 // =========================================================================
2859 // STEP 3: Process each of the 6 domain faces
2860 // =========================================================================
2861 for (int face_index = 0; face_index < 6; face_index++) {
2862 BCFace current_face_id = (BCFace)face_index;
2863 BoundaryFaceConfig *face_config = &user->boundary_faces[current_face_id];
2864
2865 // Only process faces that are mathematical walls (applies to no-slip, moving, slip, etc.)
2866 if (face_config->mathematical_type != WALL) {
2867 continue;
2868 }
2869
2870 // Check if this MPI rank owns part of this face
2871 PetscBool rank_owns_this_face;
2872 ierr = CanRankServiceFace(info, user->IM, user->JM, user->KM,
2873 current_face_id, &rank_owns_this_face); CHKERRQ(ierr);
2874
2875 if (!rank_owns_this_face) {
2876 continue;
2877 }
2878
2879 LOG_ALLOW(LOCAL, LOG_TRACE, "Processing Face %d (%s)\n",
2880 current_face_id, BCFaceToString(current_face_id));
2881
2882 // =====================================================================
2883 // Process each face with appropriate indexing
2884 // =====================================================================
2885 switch(current_face_id) {
2886
2887 // =================================================================
2888 // NEGATIVE X FACE (i = 0, normal points in +X direction)
2889 // =================================================================
2890 case BC_FACE_NEG_X: {
2891 if (grid_start_i == 0) {
2892 const PetscInt ghost_cell_index = grid_start_i;
2893 const PetscInt first_interior_cell = grid_start_i + 1;
2894 const PetscInt second_interior_cell = grid_start_i + 2;
2895
2896 for (PetscInt k = loop_start_k; k < loop_end_k; k++) {
2897 for (PetscInt j = loop_start_j; j < loop_end_j; j++) {
2898
2899 // Skip if this is a solid cell (embedded boundary)
2900 if (node_vertex_flag[k][j][first_interior_cell] < 0.1) {
2901
2902 // Calculate face area from contravariant metric tensor
2903 PetscReal face_area = sqrt(
2904 csi[k][j][ghost_cell_index].x * csi[k][j][ghost_cell_index].x +
2905 csi[k][j][ghost_cell_index].y * csi[k][j][ghost_cell_index].y +
2906 csi[k][j][ghost_cell_index].z * csi[k][j][ghost_cell_index].z
2907 );
2908
2909 // Compute wall-normal distances using cell Jacobians
2910 // sb = distance from wall to first interior cell center
2911 // sc = distance from wall to second interior cell center
2912 PetscReal distance_to_first_cell = 0.5 / cell_jacobian[k][j][first_interior_cell] / face_area;
2913 PetscReal distance_to_second_cell = 2.0 * distance_to_first_cell +
2914 0.5 / cell_jacobian[k][j][second_interior_cell] / face_area;
2915
2916 // Compute unit normal vector pointing INTO the domain
2917 PetscReal wall_normal[3];
2918 wall_normal[0] = csi[k][j][ghost_cell_index].x / face_area;
2919 wall_normal[1] = csi[k][j][ghost_cell_index].y / face_area;
2920 wall_normal[2] = csi[k][j][ghost_cell_index].z / face_area;
2921
2922 // Define velocities for wall function calculation
2923 Cmpnts wall_velocity; // Ua = velocity at wall (zero for stationary wall)
2924 Cmpnts reference_velocity; // Uc = velocity at second interior cell
2925
2926 wall_velocity.x = wall_velocity.y = wall_velocity.z = 0.0;
2927 reference_velocity = velocity_cartesian[k][j][second_interior_cell];
2928
2929 // Step 1: Linear interpolation (provides initial guess)
2930 noslip(user, distance_to_second_cell, distance_to_first_cell,
2931 wall_velocity, reference_velocity,
2932 &velocity_cartesian[k][j][first_interior_cell],
2933 wall_normal[0], wall_normal[1], wall_normal[2]);
2934
2935 // Step 2: Apply log-law correction (improves near-wall velocity)
2936 wall_function_loglaw(user, wall_roughness_height,
2937 distance_to_second_cell, distance_to_first_cell,
2938 wall_velocity, reference_velocity,
2939 &velocity_cartesian[k][j][first_interior_cell],
2940 &friction_velocity[k][j][first_interior_cell],
2941 wall_normal[0], wall_normal[1], wall_normal[2]);
2942
2943 // Ensure ghost cell BC remains zero (required for proper extrapolation)
2944 velocity_boundary[k][j][ghost_cell_index].x = 0.0;
2945 velocity_boundary[k][j][ghost_cell_index].y = 0.0;
2946 velocity_boundary[k][j][ghost_cell_index].z = 0.0;
2947 velocity_contravariant[k][j][ghost_cell_index].x = 0.0;
2948 }
2949 }
2950 }
2951 }
2952 } break;
2953
2954 // =================================================================
2955 // POSITIVE X FACE (i = mx-1, normal points in -X direction)
2956 // =================================================================
2957 case BC_FACE_POS_X: {
2958 if (grid_end_i == grid_size_i) {
2959 const PetscInt ghost_cell_index = grid_end_i - 1;
2960 const PetscInt first_interior_cell = grid_end_i - 2;
2961 const PetscInt second_interior_cell = grid_end_i - 3;
2962
2963 for (PetscInt k = loop_start_k; k < loop_end_k; k++) {
2964 for (PetscInt j = loop_start_j; j < loop_end_j; j++) {
2965
2966 if (node_vertex_flag[k][j][first_interior_cell] < 0.1) {
2967
2968 PetscReal face_area = sqrt(
2969 csi[k][j][first_interior_cell].x * csi[k][j][first_interior_cell].x +
2970 csi[k][j][first_interior_cell].y * csi[k][j][first_interior_cell].y +
2971 csi[k][j][first_interior_cell].z * csi[k][j][first_interior_cell].z
2972 );
2973
2974 PetscReal distance_to_first_cell = 0.5 / cell_jacobian[k][j][first_interior_cell] / face_area;
2975 PetscReal distance_to_second_cell = 2.0 * distance_to_first_cell +
2976 0.5 / cell_jacobian[k][j][second_interior_cell] / face_area;
2977
2978 // Note: Normal flipped for +X face to point INTO domain
2979 PetscReal wall_normal[3];
2980 wall_normal[0] = -csi[k][j][first_interior_cell].x / face_area;
2981 wall_normal[1] = -csi[k][j][first_interior_cell].y / face_area;
2982 wall_normal[2] = -csi[k][j][first_interior_cell].z / face_area;
2983
2984 Cmpnts wall_velocity, reference_velocity;
2985 wall_velocity.x = wall_velocity.y = wall_velocity.z = 0.0;
2986 reference_velocity = velocity_cartesian[k][j][second_interior_cell];
2987
2988 noslip(user, distance_to_second_cell, distance_to_first_cell,
2989 wall_velocity, reference_velocity,
2990 &velocity_cartesian[k][j][first_interior_cell],
2991 wall_normal[0], wall_normal[1], wall_normal[2]);
2992
2993 wall_function_loglaw(user, wall_roughness_height,
2994 distance_to_second_cell, distance_to_first_cell,
2995 wall_velocity, reference_velocity,
2996 &velocity_cartesian[k][j][first_interior_cell],
2997 &friction_velocity[k][j][first_interior_cell],
2998 wall_normal[0], wall_normal[1], wall_normal[2]);
2999
3000 velocity_boundary[k][j][ghost_cell_index].x = 0.0;
3001 velocity_boundary[k][j][ghost_cell_index].y = 0.0;
3002 velocity_boundary[k][j][ghost_cell_index].z = 0.0;
3003 velocity_contravariant[k][j][first_interior_cell].x = 0.0;
3004 }
3005 }
3006 }
3007 }
3008 } break;
3009
3010 // =================================================================
3011 // NEGATIVE Y FACE (j = 0, normal points in +Y direction)
3012 // =================================================================
3013 case BC_FACE_NEG_Y: {
3014 if (grid_start_j == 0) {
3015 const PetscInt ghost_cell_index = grid_start_j;
3016 const PetscInt first_interior_cell = grid_start_j + 1;
3017 const PetscInt second_interior_cell = grid_start_j + 2;
3018
3019 for (PetscInt k = loop_start_k; k < loop_end_k; k++) {
3020 for (PetscInt i = loop_start_i; i < loop_end_i; i++) {
3021
3022 if (node_vertex_flag[k][first_interior_cell][i] < 0.1) {
3023
3024 PetscReal face_area = sqrt(
3025 eta[k][ghost_cell_index][i].x * eta[k][ghost_cell_index][i].x +
3026 eta[k][ghost_cell_index][i].y * eta[k][ghost_cell_index][i].y +
3027 eta[k][ghost_cell_index][i].z * eta[k][ghost_cell_index][i].z
3028 );
3029
3030 PetscReal distance_to_first_cell = 0.5 / cell_jacobian[k][first_interior_cell][i] / face_area;
3031 PetscReal distance_to_second_cell = 2.0 * distance_to_first_cell +
3032 0.5 / cell_jacobian[k][second_interior_cell][i] / face_area;
3033
3034 PetscReal wall_normal[3];
3035 wall_normal[0] = eta[k][ghost_cell_index][i].x / face_area;
3036 wall_normal[1] = eta[k][ghost_cell_index][i].y / face_area;
3037 wall_normal[2] = eta[k][ghost_cell_index][i].z / face_area;
3038
3039 Cmpnts wall_velocity, reference_velocity;
3040 wall_velocity.x = wall_velocity.y = wall_velocity.z = 0.0;
3041 reference_velocity = velocity_cartesian[k][second_interior_cell][i];
3042
3043 noslip(user, distance_to_second_cell, distance_to_first_cell,
3044 wall_velocity, reference_velocity,
3045 &velocity_cartesian[k][first_interior_cell][i],
3046 wall_normal[0], wall_normal[1], wall_normal[2]);
3047
3048 wall_function_loglaw(user, wall_roughness_height,
3049 distance_to_second_cell, distance_to_first_cell,
3050 wall_velocity, reference_velocity,
3051 &velocity_cartesian[k][first_interior_cell][i],
3052 &friction_velocity[k][first_interior_cell][i],
3053 wall_normal[0], wall_normal[1], wall_normal[2]);
3054
3055 velocity_boundary[k][ghost_cell_index][i].x = 0.0;
3056 velocity_boundary[k][ghost_cell_index][i].y = 0.0;
3057 velocity_boundary[k][ghost_cell_index][i].z = 0.0;
3058 velocity_contravariant[k][ghost_cell_index][i].y = 0.0;
3059 }
3060 }
3061 }
3062 }
3063 } break;
3064
3065 // =================================================================
3066 // POSITIVE Y FACE (j = my-1, normal points in -Y direction)
3067 // =================================================================
3068 case BC_FACE_POS_Y: {
3069 if (grid_end_j == grid_size_j) {
3070 const PetscInt ghost_cell_index = grid_end_j - 1;
3071 const PetscInt first_interior_cell = grid_end_j - 2;
3072 const PetscInt second_interior_cell = grid_end_j - 3;
3073
3074 for (PetscInt k = loop_start_k; k < loop_end_k; k++) {
3075 for (PetscInt i = loop_start_i; i < loop_end_i; i++) {
3076
3077 if (node_vertex_flag[k][first_interior_cell][i] < 0.1) {
3078
3079 PetscReal face_area = sqrt(
3080 eta[k][first_interior_cell][i].x * eta[k][first_interior_cell][i].x +
3081 eta[k][first_interior_cell][i].y * eta[k][first_interior_cell][i].y +
3082 eta[k][first_interior_cell][i].z * eta[k][first_interior_cell][i].z
3083 );
3084
3085 PetscReal distance_to_first_cell = 0.5 / cell_jacobian[k][first_interior_cell][i] / face_area;
3086 PetscReal distance_to_second_cell = 2.0 * distance_to_first_cell +
3087 0.5 / cell_jacobian[k][second_interior_cell][i] / face_area;
3088
3089 PetscReal wall_normal[3];
3090 wall_normal[0] = -eta[k][first_interior_cell][i].x / face_area;
3091 wall_normal[1] = -eta[k][first_interior_cell][i].y / face_area;
3092 wall_normal[2] = -eta[k][first_interior_cell][i].z / face_area;
3093
3094 Cmpnts wall_velocity, reference_velocity;
3095 wall_velocity.x = wall_velocity.y = wall_velocity.z = 0.0;
3096 reference_velocity = velocity_cartesian[k][second_interior_cell][i];
3097
3098 noslip(user, distance_to_second_cell, distance_to_first_cell,
3099 wall_velocity, reference_velocity,
3100 &velocity_cartesian[k][first_interior_cell][i],
3101 wall_normal[0], wall_normal[1], wall_normal[2]);
3102
3103 wall_function_loglaw(user, wall_roughness_height,
3104 distance_to_second_cell, distance_to_first_cell,
3105 wall_velocity, reference_velocity,
3106 &velocity_cartesian[k][first_interior_cell][i],
3107 &friction_velocity[k][first_interior_cell][i],
3108 wall_normal[0], wall_normal[1], wall_normal[2]);
3109
3110 velocity_boundary[k][ghost_cell_index][i].x = 0.0;
3111 velocity_boundary[k][ghost_cell_index][i].y = 0.0;
3112 velocity_boundary[k][ghost_cell_index][i].z = 0.0;
3113 velocity_contravariant[k][first_interior_cell][i].y = 0.0;
3114 }
3115 }
3116 }
3117 }
3118 } break;
3119
3120 // =================================================================
3121 // NEGATIVE Z FACE (k = 0, normal points in +Z direction)
3122 // =================================================================
3123 case BC_FACE_NEG_Z: {
3124 if (grid_start_k == 0) {
3125 const PetscInt ghost_cell_index = grid_start_k;
3126 const PetscInt first_interior_cell = grid_start_k + 1;
3127 const PetscInt second_interior_cell = grid_start_k + 2;
3128
3129 for (PetscInt j = loop_start_j; j < loop_end_j; j++) {
3130 for (PetscInt i = loop_start_i; i < loop_end_i; i++) {
3131
3132 if (node_vertex_flag[first_interior_cell][j][i] < 0.1) {
3133
3134 PetscReal face_area = sqrt(
3135 zet[ghost_cell_index][j][i].x * zet[ghost_cell_index][j][i].x +
3136 zet[ghost_cell_index][j][i].y * zet[ghost_cell_index][j][i].y +
3137 zet[ghost_cell_index][j][i].z * zet[ghost_cell_index][j][i].z
3138 );
3139
3140 PetscReal distance_to_first_cell = 0.5 / cell_jacobian[first_interior_cell][j][i] / face_area;
3141 PetscReal distance_to_second_cell = 2.0 * distance_to_first_cell +
3142 0.5 / cell_jacobian[second_interior_cell][j][i] / face_area;
3143
3144 PetscReal wall_normal[3];
3145 wall_normal[0] = zet[ghost_cell_index][j][i].x / face_area;
3146 wall_normal[1] = zet[ghost_cell_index][j][i].y / face_area;
3147 wall_normal[2] = zet[ghost_cell_index][j][i].z / face_area;
3148
3149 Cmpnts wall_velocity, reference_velocity;
3150 wall_velocity.x = wall_velocity.y = wall_velocity.z = 0.0;
3151 reference_velocity = velocity_cartesian[second_interior_cell][j][i];
3152
3153 noslip(user, distance_to_second_cell, distance_to_first_cell,
3154 wall_velocity, reference_velocity,
3155 &velocity_cartesian[first_interior_cell][j][i],
3156 wall_normal[0], wall_normal[1], wall_normal[2]);
3157
3158 wall_function_loglaw(user, wall_roughness_height,
3159 distance_to_second_cell, distance_to_first_cell,
3160 wall_velocity, reference_velocity,
3161 &velocity_cartesian[first_interior_cell][j][i],
3162 &friction_velocity[first_interior_cell][j][i],
3163 wall_normal[0], wall_normal[1], wall_normal[2]);
3164
3165 velocity_boundary[ghost_cell_index][j][i].x = 0.0;
3166 velocity_boundary[ghost_cell_index][j][i].y = 0.0;
3167 velocity_boundary[ghost_cell_index][j][i].z = 0.0;
3168 velocity_contravariant[ghost_cell_index][j][i].z = 0.0;
3169 }
3170 }
3171 }
3172 }
3173 } break;
3174
3175 // =================================================================
3176 // POSITIVE Z FACE (k = mz-1, normal points in -Z direction)
3177 // =================================================================
3178 case BC_FACE_POS_Z: {
3179 if (grid_end_k == grid_size_k) {
3180 const PetscInt ghost_cell_index = grid_end_k - 1;
3181 const PetscInt first_interior_cell = grid_end_k - 2;
3182 const PetscInt second_interior_cell = grid_end_k - 3;
3183
3184 for (PetscInt j = loop_start_j; j < loop_end_j; j++) {
3185 for (PetscInt i = loop_start_i; i < loop_end_i; i++) {
3186
3187 if (node_vertex_flag[first_interior_cell][j][i] < 0.1) {
3188
3189 PetscReal face_area = sqrt(
3190 zet[first_interior_cell][j][i].x * zet[first_interior_cell][j][i].x +
3191 zet[first_interior_cell][j][i].y * zet[first_interior_cell][j][i].y +
3192 zet[first_interior_cell][j][i].z * zet[first_interior_cell][j][i].z
3193 );
3194
3195 PetscReal distance_to_first_cell = 0.5 / cell_jacobian[first_interior_cell][j][i] / face_area;
3196 PetscReal distance_to_second_cell = 2.0 * distance_to_first_cell +
3197 0.5 / cell_jacobian[second_interior_cell][j][i] / face_area;
3198
3199 PetscReal wall_normal[3];
3200 wall_normal[0] = -zet[first_interior_cell][j][i].x / face_area;
3201 wall_normal[1] = -zet[first_interior_cell][j][i].y / face_area;
3202 wall_normal[2] = -zet[first_interior_cell][j][i].z / face_area;
3203
3204 Cmpnts wall_velocity, reference_velocity;
3205 wall_velocity.x = wall_velocity.y = wall_velocity.z = 0.0;
3206 reference_velocity = velocity_cartesian[second_interior_cell][j][i];
3207
3208 noslip(user, distance_to_second_cell, distance_to_first_cell,
3209 wall_velocity, reference_velocity,
3210 &velocity_cartesian[first_interior_cell][j][i],
3211 wall_normal[0], wall_normal[1], wall_normal[2]);
3212
3213 wall_function_loglaw(user, wall_roughness_height,
3214 distance_to_second_cell, distance_to_first_cell,
3215 wall_velocity, reference_velocity,
3216 &velocity_cartesian[first_interior_cell][j][i],
3217 &friction_velocity[first_interior_cell][j][i],
3218 wall_normal[0], wall_normal[1], wall_normal[2]);
3219
3220 velocity_boundary[ghost_cell_index][j][i].x = 0.0;
3221 velocity_boundary[ghost_cell_index][j][i].y = 0.0;
3222 velocity_boundary[ghost_cell_index][j][i].z = 0.0;
3223 velocity_contravariant[first_interior_cell][j][i].z = 0.0;
3224 }
3225 }
3226 }
3227 }
3228 } break;
3229 }
3230 }
3231
3232 // =========================================================================
3233 // STEP 4: Restore all arrays and release memory
3234 // =========================================================================
3235 ierr = DMDAVecRestoreArray(user->fda, user->Ucat, &velocity_cartesian); CHKERRQ(ierr);
3236 ierr = DMDAVecRestoreArray(user->fda, user->Ucont, &velocity_contravariant); CHKERRQ(ierr);
3237 ierr = DMDAVecRestoreArray(user->fda, user->Bcs.Ubcs, &velocity_boundary); CHKERRQ(ierr);
3238 ierr = DMDAVecRestoreArrayRead(user->fda, user->lCsi, (const Cmpnts***)&csi); CHKERRQ(ierr);
3239 ierr = DMDAVecRestoreArrayRead(user->fda, user->lEta, (const Cmpnts***)&eta); CHKERRQ(ierr);
3240 ierr = DMDAVecRestoreArrayRead(user->fda, user->lZet, (const Cmpnts***)&zet); CHKERRQ(ierr);
3241 ierr = DMDAVecRestoreArrayRead(user->da, user->lNvert, (const PetscReal***)&node_vertex_flag); CHKERRQ(ierr);
3242 ierr = DMDAVecRestoreArrayRead(user->da, user->lAj, (const PetscReal***)&cell_jacobian); CHKERRQ(ierr);
3243 ierr = DMDAVecRestoreArray(user->da, user->lFriction_Velocity, &friction_velocity); CHKERRQ(ierr);
3244
3245 LOG_ALLOW(LOCAL, LOG_DEBUG, "Complete.\n");
3246
3247 PetscFunctionReturn(0);
3248}
PetscErrorCode CanRankServiceFace(const DMDALocalInfo *info, PetscInt IM_nodes_global, PetscInt JM_nodes_global, PetscInt KM_nodes_global, BCFace face_id, PetscBool *can_service_out)
Implementation of CanRankServiceFace().
Definition Boundaries.c:126
Vec lFriction_Velocity
Definition variables.h:900
@ WALL
Definition variables.h:284
PetscInt KM
Definition variables.h:885
Vec Ucont
Definition variables.h:904
PetscReal wall_roughness_height
Definition variables.h:764
PetscInt JM
Definition variables.h:885
PetscInt wallfunction
Definition variables.h:790
PetscInt IM
Definition variables.h:885
void wall_function_loglaw(UserCtx *user, double roughness_height, double distance_reference, double distance_boundary, Cmpnts velocity_wall, Cmpnts velocity_reference, Cmpnts *velocity_boundary, PetscReal *friction_velocity, double normal_x, double normal_y, double normal_z)
Applies log-law wall function with roughness correction.
void noslip(UserCtx *user, double distance_reference, double distance_boundary, Cmpnts velocity_wall, Cmpnts velocity_reference, Cmpnts *velocity_boundary, double normal_x, double normal_y, double normal_z)
Applies no-slip wall boundary condition with linear interpolation.
Here is the call graph for this function:
Here is the caller graph for this function:

◆ FinalizePostProjectionCellFields()

PetscErrorCode FinalizePostProjectionCellFields ( UserCtx user)

Finalizes cell-centered fields after the projection step.

This function completes the cell-centered state derived from the final, divergence-free Ucont produced by Projection. It fills non-periodic Ucat dummy faces, synchronizes periodic Ucat and P endpoints, resolves edges and corners, and refreshes the corresponding local vectors.

This function is fundamentally different from ApplyBoundaryConditions: it does NOT modify Ucont, reapply wall functions, or rerun the full physical boundary-condition workflow.

WORKFLOW:

  1. Refreshes local Ucat and any flow-dependent Ubcs targets.
  2. Fills non-periodic dummy faces and establishes periodic cell endpoints.
  3. Resolves edges/corners, restores exact periodic relationships, and refreshes local Ucat and P.
Parameters
userThe main UserCtx struct, containing all simulation state.
Returns
PetscErrorCode 0 on success.

Finalizes cell-centered fields after the projection step.

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

Definition at line 3257 of file Boundaries.c.

3258{
3259 PetscErrorCode ierr;
3260 const char *cell_fields[] = {"Ucat", "P"};
3261
3262 PetscFunctionBeginUser;
3264
3265 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Finalizing post-projection cell-centered fields.\n");
3266
3267 // Ensure flow-dependent Ubcs handlers see the newly reconstructed Ucat.
3268 ierr = UpdateLocalGhosts(user, "Ucat"); CHKERRQ(ierr);
3269 ierr = BoundarySystem_RefreshUbcs(user); CHKERRQ(ierr);
3270
3271 // Establish flat non-periodic faces and periodic endpoints before corners.
3272 ierr = UpdateDummyCells(user); CHKERRQ(ierr);
3273 ierr = SynchronizePeriodicCellFields(user, 2, cell_fields); CHKERRQ(ierr);
3274
3275 // Corner averaging can overwrite periodic endpoints, so restore them after.
3276 ierr = UpdateCornerNodes(user); CHKERRQ(ierr);
3277 ierr = SynchronizePeriodicCellFields(user, 2, cell_fields); CHKERRQ(ierr);
3278
3279 // Synchronize explicitly because the periodic helper is a no-op when every
3280 // direction is non-periodic.
3281 ierr = UpdateLocalGhosts(user, "Ucat"); CHKERRQ(ierr);
3282 ierr = UpdateLocalGhosts(user, "P"); CHKERRQ(ierr);
3283
3284 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Post-projection cell-centered fields finalized.\n");
3286 PetscFunctionReturn(0);
3287}
PetscErrorCode UpdateDummyCells(UserCtx *user)
Internal helper implementation: UpdateDummyCells().
PetscErrorCode BoundarySystem_RefreshUbcs(UserCtx *user)
Internal helper implementation: BoundarySystem_RefreshUbcs().
PetscErrorCode UpdateCornerNodes(UserCtx *user)
Internal helper implementation: UpdateCornerNodes().
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ApplyBoundaryConditions()

PetscErrorCode ApplyBoundaryConditions ( UserCtx user)

Main boundary-condition orchestrator executed during solver timestepping.

This routine performs the full BC workflow for the current block, including dynamic boundary refresh, periodic transfer, dummy/corner updates, and optional wall-function corrections in the same order expected by the runtime solver. It may iterate boundary updates to enforce coupled boundary dependencies.

Parameters
userThe main UserCtx struct containing field vectors and boundary system state.
Returns
PetscErrorCode 0 on success.

Main boundary-condition orchestrator executed during solver timestepping.

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

See also
ApplyBoundaryConditions()

Definition at line 3297 of file Boundaries.c.

3298{
3299 PetscErrorCode ierr;
3300 const char *staggered_fields[] = {"Ucont"};
3301 PetscFunctionBeginUser;
3303
3304 LOG_ALLOW(GLOBAL,LOG_TRACE,"Boundary Condition Application begins.\n");
3305
3306 // STEP 1: Main iteration loop for applying and converging non-periodic BCs.
3307 // The number of iterations (e.g., 3) allows information to propagate
3308 // between coupled boundaries, like an inlet and a conserving outlet.
3309 for (PetscInt iter = 0; iter < 3; iter++) {
3310 // (a) Execute the boundary system. This phase calculates fluxes across
3311 // the domain and then applies the physical logic for each non-periodic
3312 // handler, setting the `ubcs` (boundary value) array.
3313 ierr = BoundarySystem_ExecuteStep(user); CHKERRQ(ierr);
3314
3315 LOG_ALLOW(GLOBAL,LOG_VERBOSE,"Boundary Condition Setup Executed.\n");
3316
3317 // (b) Synchronize the updated ghost cells across all processors to ensure
3318 // all ucont values are current before updating the dummy cells.
3319 ierr = SynchronizePeriodicStaggeredFields(user, 1, staggered_fields); CHKERRQ(ierr);
3320
3321 // (c) Convert updated Contravariant velocities to Cartesian velocities.
3322 ierr = Contra2Cart(user); CHKERRQ(ierr);
3323
3324 // (d) Synchronize the updated Cartesian velocities across all processors
3325 // to ensure all ucat values are current before updating the dummy cells.
3326 ierr = UpdateLocalGhosts(user, "Ucat"); CHKERRQ(ierr);
3327
3328 // (e) If Wall functions are enabled, apply them now to adjust near-wall velocities.
3329 if(user->simCtx->wallfunction){
3330 // Apply wall function adjustments to the boundary velocities.
3331 ierr = ApplyWallFunction(user); CHKERRQ(ierr);
3332
3333 // Synchronize the updated Cartesian velocities after wall function adjustments.
3334 ierr = UpdateLocalGhosts(user, "Ucat"); CHKERRQ(ierr);
3335
3336 LOG_ALLOW(GLOBAL,LOG_VERBOSE,"Wall Function Applied at Walls.\n");
3337 }
3338
3339 // (f) Update the first layer of ghost cells for non-periodic faces using
3340 // the newly computed `ubcs` values.
3341 ierr = UpdateDummyCells(user); CHKERRQ(ierr);
3342
3343 LOG_ALLOW(GLOBAL,LOG_VERBOSE,"Dummy Cells/Ghost Cells Updated.\n");
3344
3345 // (g) Handle all periodic boundaries. This is a parallel direct copy
3346 // that sets the absolute constraints for the rest of the solve.
3347 // There is a Ghost update happening inside this function.
3348 ierr = ApplyPeriodicBCs(user); CHKERRQ(ierr);
3349
3350 // (h) Update the corner and edge ghost nodes. This routine calculates
3351 // values for corners/edges by averaging their neighbors, which have been
3352 // finalized in the steps above (both periodic and non-periodic).
3353 ierr = UpdateCornerNodes(user); CHKERRQ(ierr);
3354
3355 // (i) Synchronize the updated edge and corner cells across all processors to ensure
3356 // consistency before the next iteration or finalization.
3357 ierr = UpdateLocalGhosts(user, "P"); CHKERRQ(ierr);
3358 ierr = UpdateLocalGhosts(user, "Ucat"); CHKERRQ(ierr);
3359 ierr = SynchronizePeriodicStaggeredFields(user, 1, staggered_fields); CHKERRQ(ierr);
3360
3361 // (j) Ensure All the corners are synchronized with a well defined protocol in case of Periodic boundary conditions
3362 // To avoid race conditions.
3363 const char* all_fields[] = {"Ucat", "P", "Nvert"};
3364 ierr = SynchronizePeriodicCellFields(user, 3, all_fields); CHKERRQ(ierr);
3365
3366 }
3367
3368 // STEP 3: Final ghost node synchronization. This ensures all changes made
3369 // to the global vectors are reflected in the local ghost regions of all
3370 // processors, making the state fully consistent before the next solver stage.
3371 ierr = UpdateLocalGhosts(user, "P"); CHKERRQ(ierr);
3372 ierr = UpdateLocalGhosts(user, "Ucat"); CHKERRQ(ierr);
3373 ierr = SynchronizePeriodicStaggeredFields(user, 1, staggered_fields); CHKERRQ(ierr);
3374
3376 PetscFunctionReturn(0);
3377}
PetscErrorCode ApplyPeriodicBCs(UserCtx *user)
Internal helper implementation: ApplyPeriodicBCs().
PetscErrorCode ApplyWallFunction(UserCtx *user)
Internal helper implementation: ApplyWallFunction().
PetscErrorCode BoundarySystem_ExecuteStep(UserCtx *user)
Implementation of BoundarySystem_ExecuteStep().
PetscErrorCode Contra2Cart(UserCtx *user)
Reconstructs Cartesian velocity (Ucat) at cell centers from contravariant velocity (Ucont) defined on...
Definition setup.c:2746
Here is the call graph for this function:
Here is the caller graph for this function: