PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
Loading...
Searching...
No Matches
ParticleSwarm.c
Go to the documentation of this file.
1 // ParticleSwarm.c
2
3#include "ParticleSwarm.h"
4
5#define INTERPOLATION_DISTANCE_TOLERANCE 1.0e-14
6
7#undef __FUNCT__
8#define __FUNCT__ "InitializeSwarm"
9/**
10 * @brief Initializes the DMSwarm object within the UserCtx structure.
11 *
12 * This function creates the DMSwarm, sets its type and dimension, and configures basic swarm properties.
13 *
14 * @param[in,out] user Pointer to the UserCtx structure containing simulation context.
15 *
16 * @return PetscErrorCode Returns 0 on success, non-zero on failure.
17 */
18PetscErrorCode InitializeSwarm(UserCtx* user) {
19 PetscErrorCode ierr; // Error code for PETSc functions
20
21 PetscFunctionBeginUser;
23 // Create the DMSwarm object for particle management
24 ierr = DMCreate(PETSC_COMM_WORLD, &user->swarm); CHKERRQ(ierr);
25 ierr = DMSetType(user->swarm, DMSWARM); CHKERRQ(ierr);
26 ierr = DMSetDimension(user->swarm, 3); CHKERRQ(ierr);
27 ierr = DMSwarmSetType(user->swarm, DMSWARM_BASIC); CHKERRQ(ierr);
28 LOG_ALLOW(LOCAL,LOG_INFO, "DMSwarm created and configured.\n");
29
31 PetscFunctionReturn(0);
32}
33
34#undef __FUNCT__
35#define __FUNCT__ "RegisterSwarmField"
36
37/**
38 * @brief Registers a swarm field without finalizing registration.
39 *
40 * This function calls DMSwarmRegisterPetscDatatypeField for the given field,
41 * but does not finalize the registration. The finalization is deferred until
42 * all fields have been registered.
43 *
44 * @param swarm [in] The DMSwarm object.
45 * @param fieldName [in] Name of the field to register.
46 * @param fieldDim [in] Dimension of the field (1 for scalar, 3 for vector, etc.).
47 * @param dtype [in] The datatype of the swarm field being registered.
48 * @return PetscErrorCode Returns 0 on success, non-zero on failure.
49 */
50PetscErrorCode RegisterSwarmField(DM swarm, const char *fieldName, PetscInt fieldDim, PetscDataType dtype)
51{
52 PetscErrorCode ierr;
53 PetscFunctionBeginUser;
54
55 ierr = DMSwarmRegisterPetscDatatypeField(swarm, fieldName, fieldDim, dtype); CHKERRQ(ierr);
56 // PetscDataTypes is an extern char* [] defined in petscsystypes.h that gives string names for PetscDataType enums
57 LOG_ALLOW(LOCAL,LOG_DEBUG,"Registered field '%s' with dimension=%d, type=%s.\n",
58 fieldName, fieldDim, PetscDataTypes[dtype]);
59
60 PetscFunctionReturn(0);
61}
62
63#undef __FUNCT__
64#define __FUNCT__ "RegisterParticleFields"
65
66/**
67 * @brief Registers necessary particle fields within the DMSwarm.
68 *
69 * This function registers fields such as position, velocity, CellID, and weight for each particle.
70 *
71 * @param[in,out] swarm The DMSwarm object managing the particle swarm.
72 *
73 * @return PetscErrorCode Returns 0 on success, non-zero on failure.
74 */
75
76PetscErrorCode RegisterParticleFields(DM swarm)
77{
78 PetscErrorCode ierr;
79 PetscFunctionBeginUser;
80
81 // Register each field using the helper function
82 ierr = RegisterSwarmField(swarm, "position", 3 ,PETSC_REAL); CHKERRQ(ierr);
83 LOG_ALLOW(LOCAL,LOG_DEBUG, "Registered field 'position'.\n");
84
85 ierr = RegisterSwarmField(swarm, "velocity", 3, PETSC_REAL); CHKERRQ(ierr);
86 LOG_ALLOW(LOCAL,LOG_DEBUG,"Registered field 'velocity'.\n");
87
88 ierr = RegisterSwarmField(swarm, "DMSwarm_CellID", 3, PETSC_INT); CHKERRQ(ierr);
89 LOG_ALLOW(LOCAL,LOG_DEBUG,"Registered field 'DMSwarm_CellID'.\n");
90
91 ierr = RegisterSwarmField(swarm, "weight", 3,PETSC_REAL); CHKERRQ(ierr);
92 LOG_ALLOW(LOCAL,LOG_DEBUG,"Registered field 'weight'.\n");
93
94 ierr = RegisterSwarmField(swarm,"Psi", 1,PETSC_REAL); CHKERRQ(ierr);
95 LOG_ALLOW(LOCAL,LOG_DEBUG,"Registered field 'Psi' - Scalar.\n");
96
97 ierr = RegisterSwarmField(swarm,"DMSwarm_location_status",1,PETSC_INT);CHKERRQ(ierr);
98 LOG_ALLOW(LOCAL,LOG_DEBUG,"Registered field 'DMSwarm_location_status' - Status of Location of Particle(located,lost etc).\n");
99
100 // Finalize the field registration after all fields have been added
101 ierr = DMSwarmFinalizeFieldRegister(swarm); CHKERRQ(ierr);
102 LOG_ALLOW(LOCAL,LOG_INFO,"RegisterParticleFields - Finalized field registration.\n");
103
104 PetscFunctionReturn(0);
105}
106
107#undef __FUNCT__
108#define __FUNCT__ "DetermineVolumetricInitializationParameters"
109/**
110 * @brief Determines cell selection and intra-cell logical coordinates for volumetric initialization (Mode 1).
111 *
112 * This function is called when `simCtx->ParticleInitialization == 1`. It randomly selects
113 * an *owned cell* on the current MPI rank and then generates random intra-cell logical
114 * coordinates `[0,1)^3` within that chosen cell.
115 *
116 * The process involves:
117 * 1. Checking if the current MPI rank owns any 3D cells.
118 * 2. If it does, it randomly selects an *owned cell index* in each logical direction (i, j, k)
119 * by scaling a `[0,1)` random number with the number of owned cells in that direction.
120 * 3. These local owned cell indices are then converted to the *local node indices*
121 * (`ci/cj/ck_metric_lnode_out`) corresponding to the origin of the selected cell,
122 * for use with `MetricLogicalToPhysical`. This conversion uses `xs/ys/zs_gnode`.
123 * 4. All three intra-cell logical coordinates (`xi/eta/zta_metric_logic_out`) for
124 * `MetricLogicalToPhysical` are chosen randomly within the `[0,1)` range.
125 * 5. A flag (`can_place_in_volume_out`) indicates if a valid placement could be determined.
126 *
127 * **Important Note on `DMDALocalInfo info` members (same as for surface init):**
128 * - `info->xs, info->ys, info->ks`: Global starting indices of *owned cells*.
129 * - `info->mx, info->my, info->mz`: Number of *grid points (nodes)* in each local dimension on this process.
130 * Therefore, the number of *owned cells* in a dimension is `info->mX - 1` (if `info->mX > 0`).
131 *
132 * @param[in] user Pointer to `UserCtx`. (Currently not used in this specific helper, but kept for API consistency).
133 * @param[in] info Pointer to `DMDALocalInfo` for the current rank's grid portion.
134 * @param[in] xs_gnode, ys_gnode, zs_gnode Local indices (in the ghosted array) of the first *owned node*.
135 * @param[in] rand_logic_i_ptr Pointer to the RNG for i-dimension tasks [0,1).
136 * @param[in] rand_logic_j_ptr Pointer to the RNG for j-dimension tasks [0,1).
137 * @param[in] rand_logic_k_ptr Pointer to the RNG for k-dimension tasks [0,1).
138 * @param[out] ci_metric_lnode_out Pointer to store the local i-node index of the selected cell's origin.
139 * @param[out] cj_metric_lnode_out Pointer to store the local j-node index of the selected cell's origin.
140 * @param[out] ck_metric_lnode_out Pointer to store the local k-node index of the selected cell's origin.
141 * @param[out] xi_metric_logic_out Pointer to store the intra-cell logical xi-coordinate [0,1).
142 * @param[out] eta_metric_logic_out Pointer to store the intra-cell logical eta-coordinate [0,1).
143 * @param[out] zta_metric_logic_out Pointer to store the intra-cell logical zeta-coordinate [0,1).
144 * @param[out] can_place_in_volume_out PETSC_TRUE if placement parameters were successfully determined, PETSC_FALSE otherwise.
145 * @return PetscErrorCode 0 on success, or a PETSc error code.
146 */
148 UserCtx *user, DMDALocalInfo *info,
149 PetscInt xs_gnode, PetscInt ys_gnode, PetscInt zs_gnode,
150 PetscRandom *rand_logic_i_ptr, PetscRandom *rand_logic_j_ptr, PetscRandom *rand_logic_k_ptr, /* Pointers to RNGs */
151 PetscInt *ci_metric_lnode_out, PetscInt *cj_metric_lnode_out, PetscInt *ck_metric_lnode_out,
152 PetscReal *xi_metric_logic_out, PetscReal *eta_metric_logic_out, PetscReal *zta_metric_logic_out,
153 PetscBool *can_place_in_volume_out)
154{
155 PetscErrorCode ierr = 0;
156 PetscReal r_val; // Temporary for random numbers from [0,1) RNGs
157 PetscInt local_owned_cell_idx_i, local_owned_cell_idx_j, local_owned_cell_idx_k;
158 PetscMPIInt rank_for_logging; // For logging if needed
159
160 PetscFunctionBeginUser;
161
163
164 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank_for_logging); CHKERRQ(ierr);
165
166 *can_place_in_volume_out = PETSC_FALSE; // Default to: cannot place
167
168 // Default intra-cell logicals and cell node indices (e.g. if placement fails)
169 *xi_metric_logic_out = 0.5; *eta_metric_logic_out = 0.5; *zta_metric_logic_out = 0.5;
170 *ci_metric_lnode_out = xs_gnode; *cj_metric_lnode_out = ys_gnode; *ck_metric_lnode_out = zs_gnode;
171
172 // Calculate number of owned cells in each direction from node counts in info
173 // Assumes info->mx, info->my, info->mz are node counts on this process for each dimension.
174 // Number of cells = Number of nodes - 1 (if > 0 nodes).
175 PetscInt num_owned_cells_i = (info->mx > 1) ? info->mx - 1 : 0;
176 PetscInt num_owned_cells_j = (info->my > 1) ? info->my - 1 : 0;
177 PetscInt num_owned_cells_k = (info->mz > 1) ? info->mz - 1 : 0;
178
179 if (num_owned_cells_i > 0 && num_owned_cells_j > 0 && num_owned_cells_k > 0) { // If rank owns any 3D cells
180 *can_place_in_volume_out = PETSC_TRUE;
181
182 // --- 1. Select a Random Owned Cell ---
183 // The selected index will be a 0-based index relative to the start of this rank's owned cells.
184
185 // Select random local owned cell index in I-direction
186 ierr = PetscRandomGetValueReal(*rand_logic_i_ptr, &r_val); CHKERRQ(ierr); // Dereference RNG pointer
187 local_owned_cell_idx_i = (PetscInt)(r_val * num_owned_cells_i);
188 // Clamp to be safe: local_owned_cell_idx_i should be in [0, num_owned_cells_i - 1]
189 local_owned_cell_idx_i = PetscMin(PetscMax(0, local_owned_cell_idx_i), num_owned_cells_i - 1);
190 *ci_metric_lnode_out = xs_gnode + local_owned_cell_idx_i; // Convert to local node index for cell origin
191
192 // Select random local owned cell index in J-direction
193 ierr = PetscRandomGetValueReal(*rand_logic_j_ptr, &r_val); CHKERRQ(ierr); // Dereference RNG pointer
194 local_owned_cell_idx_j = (PetscInt)(r_val * num_owned_cells_j);
195 local_owned_cell_idx_j = PetscMin(PetscMax(0, local_owned_cell_idx_j), num_owned_cells_j - 1);
196 *cj_metric_lnode_out = ys_gnode + local_owned_cell_idx_j;
197
198 // Select random local owned cell index in K-direction
199 ierr = PetscRandomGetValueReal(*rand_logic_k_ptr, &r_val); CHKERRQ(ierr); // Dereference RNG pointer
200 local_owned_cell_idx_k = (PetscInt)(r_val * num_owned_cells_k);
201 local_owned_cell_idx_k = PetscMin(PetscMax(0, local_owned_cell_idx_k), num_owned_cells_k - 1);
202 *ck_metric_lnode_out = zs_gnode + local_owned_cell_idx_k;
203
204 LOG_ALLOW(LOCAL, LOG_DEBUG, "DVP - Rank %d: Selected Cell (Owned Idx: %d,%d,%d -> LNodeStart: %d,%d,%d). OwnedCells(i,j,k): (%d,%d,%d). GhostNodeStarts(xs,ys,zs): (%d,%d,%d) \n",
205 rank_for_logging, local_owned_cell_idx_i, local_owned_cell_idx_j, local_owned_cell_idx_k,
206 *ci_metric_lnode_out, *cj_metric_lnode_out, *ck_metric_lnode_out,
207 num_owned_cells_i, num_owned_cells_j, num_owned_cells_k,
208 xs_gnode, ys_gnode, zs_gnode);
209
210
211 // --- 2. Generate Random Intra-Cell Logical Coordinates [0,1) for MetricLogicalToPhysical ---
212 ierr = PetscRandomGetValueReal(*rand_logic_i_ptr, xi_metric_logic_out); CHKERRQ(ierr); // Re-use RNGs
213 ierr = PetscRandomGetValueReal(*rand_logic_j_ptr, eta_metric_logic_out); CHKERRQ(ierr);
214 ierr = PetscRandomGetValueReal(*rand_logic_k_ptr, zta_metric_logic_out); CHKERRQ(ierr);
215
216 // Ensure logical coordinates are strictly within [0,1) for robustness with MetricLogicalToPhysical
217 *xi_metric_logic_out = PetscMin(*xi_metric_logic_out, 1.0 - 1.0e-7);
218 *eta_metric_logic_out = PetscMin(*eta_metric_logic_out, 1.0 - 1.0e-7);
219 *zta_metric_logic_out = PetscMin(*zta_metric_logic_out, 1.0 - 1.0e-7);
220 // Ensure they are not negative either (though [0,1) RNGs shouldn't produce this)
221 *xi_metric_logic_out = PetscMax(*xi_metric_logic_out, 0.0);
222 *eta_metric_logic_out = PetscMax(*eta_metric_logic_out, 0.0);
223 *zta_metric_logic_out = PetscMax(*zta_metric_logic_out, 0.0);
224
225 } else {
226 // This rank does not own any 3D cells (e.g., in a 1D or 2D decomposition,
227 // or if the global domain itself is not 3D in terms of cells).
228 // *can_place_in_volume_out remains PETSC_FALSE.
229 LOG_ALLOW(LOCAL, LOG_WARNING, "DVP - Rank %d: Cannot place particle volumetrically. Rank has zero owned cells in at least one dimension (owned cells i,j,k: %d,%d,%d).\n",
230 rank_for_logging, num_owned_cells_i, num_owned_cells_j, num_owned_cells_k);
231 }
232
234 PetscFunctionReturn(0);
235}
236
237#undef __FUNCT__
238#define __FUNCT__ "InitializeParticleBasicProperties"
239
240/**
241 * @brief Initializes basic properties for particles on the local process.
242 *
243 * This function assigns initial physical positions, Particle IDs (PIDs), and placeholder
244 * cell IDs to particles. The method of position initialization depends on
245 * `simCtx->ParticleInitialization`:
246 * - Mode 0 (Surface): Particles are placed on a designated inlet surface if the
247 * current rank services that surface. Otherwise, they are placed at (0,0,0)
248 * to be migrated to the correct rank later.
249 * - Mode 1 (Volumetric): Particles are placed randomly within a cell owned by
250 * the current rank.
251 *
252 * The logical coordinates for placement are generated using provided random number
253 * generators. These logical coordinates are then transformed to physical coordinates
254 * using `MetricLogicalToPhysical`.
255 *
256 * @param user Pointer to the UserCtx structure, containing simulation settings and grid information.
257 * @param particlesPerProcess The number of particles to initialize on this MPI rank.
258 * @param rand_logic_i Pointer to a PetscRandom generator for the xi logical coordinate.
259 * @param rand_logic_j Pointer to a PetscRandom generator for the eta logical coordinate.
260 * @param rand_logic_k Pointer to a PetscRandom generator for the zeta logical coordinate.
261 * @param bboxlist (Unused in this function for placement) Pointer to the bounding box list;
262 * provided for API consistency but not used for determining initial positions here.
263 * Particle positions are determined by logical-to-physical mapping based on rank's owned cells.
264 * @return PetscErrorCode 0 on success, non-zero on failure.
265 */
266static PetscErrorCode InitializeParticleBasicProperties(UserCtx *user,
267 PetscInt particlesPerProcess,
268 PetscRandom *rand_logic_i,
269 PetscRandom *rand_logic_j,
270 PetscRandom *rand_logic_k,
271 BoundingBox *bboxlist) // bboxlist unused for placement
272{
273 PetscErrorCode ierr;
274 DM swarm = user->swarm;
275 PetscReal *positions_field = NULL; // Pointer to swarm field for physical positions (x,y,z)
276 PetscInt64 *particleIDs = NULL; // Pointer to swarm field for Particle IDs
277 PetscInt *cellIDs_petsc = NULL; // Pointer to swarm field for DMSwarm_CellID (i,j,k of containing cell)
278 PetscInt *status_field = NULL; // Pointer to swarm field for DMSwarm_location_status(NEEDS_LOCATION etc)
279 PetscMPIInt rank,size; // MPI rank of the current process, and total number of ranks.
280 const Cmpnts ***coor_nodes_local_array; // Read-only access to local node coordinates (from user->da)
281 Vec Coor_local; // Local vector for node coordinates
282 DMDALocalInfo info; // Local grid information (node-based) from user->da
283 PetscInt xs_gnode_rank, ys_gnode_rank, zs_gnode_rank; // Local starting node indices (incl. ghosts) of rank's DA patch
284 PetscInt IM_nodes_global, JM_nodes_global, KM_nodes_global; // Global node counts in each direction
285
286 // Variables for surface initialization (Mode 0)
287 PetscBool can_this_rank_service_inlet = PETSC_FALSE;
288
289 PetscFunctionBeginUser;
290
292
293 SimCtx *simCtx = user->simCtx;
294
295 // --- 1. Input Validation and Basic Setup ---
296 if (!user || !rand_logic_i || !rand_logic_j || !rand_logic_k) {
297 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Null user or RNG pointer.");
298 }
299 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
300 ierr = MPI_Comm_size(PETSC_COMM_WORLD,&size); CHKERRQ(ierr);
301
302 // Get DMDA information for the node-centered coordinate grid (user->da)
303 ierr = DMGetCoordinatesLocal(user->da, &Coor_local); CHKERRQ(ierr);
304 if (!Coor_local) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "DMGetCoordinatesLocal for user->da returned NULL Coor_local.");
305 ierr = DMDAVecGetArrayRead(user->fda, Coor_local, (void*)&coor_nodes_local_array); CHKERRQ(ierr);
306 ierr = DMDAGetLocalInfo(user->da, &info); CHKERRQ(ierr);
307 ierr = DMDAGetGhostCorners(user->da, &xs_gnode_rank, &ys_gnode_rank, &zs_gnode_rank, NULL, NULL, NULL); CHKERRQ(ierr);
308 ierr = DMDAGetInfo(user->da, NULL, &IM_nodes_global, &JM_nodes_global, &KM_nodes_global, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); CHKERRQ(ierr);
309
310 // Modification to IM_nodes_global etc. to account for 1-cell halo in each direction.
311 IM_nodes_global -= 1; JM_nodes_global -= 1; KM_nodes_global -= 1;
312
313 const PetscInt IM_cells_global = IM_nodes_global > 0 ? IM_nodes_global - 1 : 0;
314 const PetscInt JM_cells_global = JM_nodes_global > 0 ? JM_nodes_global - 1 : 0;
315 const PetscInt KM_cells_global = KM_nodes_global > 0 ? KM_nodes_global - 1 : 0;
316
317 LOG_ALLOW(LOCAL, LOG_INFO, "Rank %d: Initializing %d particles. Mode: %s.\n",
318 rank, particlesPerProcess, ParticleInitializationToString(simCtx->ParticleInitialization));
319
320 // --- 2. Pre-computation for Surface Initialization (Mode 0) ---
321 if (simCtx->ParticleInitialization == 0 || simCtx->ParticleInitialization == 3) { // Surface initialization
322 ierr = CanRankServiceInletFace(user, &info, IM_nodes_global, JM_nodes_global, KM_nodes_global, &can_this_rank_service_inlet); CHKERRQ(ierr);
323 if (can_this_rank_service_inlet) {
324 LOG_ALLOW(LOCAL, LOG_INFO, "Rank %d: Will attempt to place particles on inlet face %s.\n", rank, BCFaceToString((BCFace)user->identifiedInletBCFace));
325 } else {
326 LOG_ALLOW(LOCAL, LOG_INFO, "Rank %d: Cannot service inlet face %s. Particles will be at Inlet Center (%.6f,%.6f,%.6f) and rely on migration.\n", rank, BCFaceToString((BCFace)user->identifiedInletBCFace),user->simCtx->CMx_c,user->simCtx->CMy_c,user->simCtx->CMz_c);
327 }
328 }
329
330 // --- 3. Get Access to Swarm Fields ---
331 ierr = DMSwarmGetField(swarm, "position", NULL, NULL, (void**)&positions_field); CHKERRQ(ierr);
332 ierr = DMSwarmGetField(swarm, "DMSwarm_pid", NULL, NULL, (void**)&particleIDs); CHKERRQ(ierr);
333 ierr = DMSwarmGetField(swarm, "DMSwarm_CellID", NULL, NULL, (void**)&cellIDs_petsc); CHKERRQ(ierr);
334 ierr = DMSwarmGetField(swarm, "DMSwarm_location_status",NULL,NULL,(void**)&status_field); CHKERRQ(ierr);
335
336 // --- 4. Determine Starting Global PID for this Rank ---
337 PetscInt particles_per_rank_ideal = simCtx->np / size; // Assumes user->size is PETSC_COMM_WORLD size
338 PetscInt remainder_particles = simCtx->np % size;
339 PetscInt base_pid_for_rank = rank * particles_per_rank_ideal + PetscMin(rank, remainder_particles);
340 // This calculation must match how particlesPerProcess was determined (e.g., in DistributeParticles).
341
342 // --- 5. Loop Over Particles to Initialize ---
343 for (PetscInt p = 0; p < particlesPerProcess; p++) {
344 PetscInt idx = p;
345 PetscInt ci_metric_lnode, cj_metric_lnode, ck_metric_lnode;
346 PetscReal xi_metric_logic, eta_metric_logic, zta_metric_logic;
347 Cmpnts phys_coords = {0.0, 0.0, 0.0};
348 PetscBool particle_placed_by_this_rank = PETSC_FALSE;
349
350 if (simCtx->ParticleInitialization == 0) { // --- 5.a. Surface Random Initialization ---
351 if (can_this_rank_service_inlet) {
352 ierr = GetRandomCellAndLogicalCoordsOnInletFace(user, &info, xs_gnode_rank, ys_gnode_rank, zs_gnode_rank,
353 IM_nodes_global, JM_nodes_global, KM_nodes_global,
354 rand_logic_i, rand_logic_j, rand_logic_k,
355 &ci_metric_lnode, &cj_metric_lnode, &ck_metric_lnode,
356 &xi_metric_logic, &eta_metric_logic, &zta_metric_logic); CHKERRQ(ierr);
357 ierr = MetricLogicalToPhysical(user, coor_nodes_local_array,
358 ci_metric_lnode, cj_metric_lnode, ck_metric_lnode,
359 xi_metric_logic, eta_metric_logic, zta_metric_logic,
360 &phys_coords); CHKERRQ(ierr);
361 particle_placed_by_this_rank = PETSC_TRUE;
362 }else{
363 // Rank cannot service inlet - place at inlet center to be migrated later
364 phys_coords.x = user->simCtx->CMx_c;
365 phys_coords.y = user->simCtx->CMy_c;
366 phys_coords.z = user->simCtx->CMz_c;
367 particle_placed_by_this_rank = PETSC_FALSE; // Relies on migration
368 }
369 }else if(simCtx->ParticleInitialization == 3) { // --- 5.a1. Custom Initialization DEBUG (Mode 3) ---
370 if(can_this_rank_service_inlet) {
371 PetscInt64 particle_global_id = (PetscInt64)(base_pid_for_rank + p);
372 ierr = GetDeterministicFaceGridLocation(user,&info,xs_gnode_rank, ys_gnode_rank, zs_gnode_rank,
373 IM_cells_global, JM_cells_global, KM_cells_global,
374 particle_global_id,
375 &ci_metric_lnode, &cj_metric_lnode, &ck_metric_lnode,
376 &xi_metric_logic, &eta_metric_logic, &zta_metric_logic,
377 &particle_placed_by_this_rank); CHKERRQ(ierr);
378
379 ierr = MetricLogicalToPhysical(user, coor_nodes_local_array,
380 ci_metric_lnode, cj_metric_lnode, ck_metric_lnode,
381 xi_metric_logic, eta_metric_logic, zta_metric_logic,
382 &phys_coords); CHKERRQ(ierr);
383
384 }else{
385 // Rank cannot service inlet - place at inlet center to be migrated later
386 phys_coords.x = user->simCtx->CMx_c;
387 phys_coords.y = user->simCtx->CMy_c;
388 phys_coords.z = user->simCtx->CMz_c;
389 particle_placed_by_this_rank = PETSC_FALSE; // Relies on migration
390 }
391 }else if(simCtx->ParticleInitialization == 1){ // --- 5.b. Volumetric Initialization (simCtx->ParticleInitialization == 1) ---
392 PetscBool can_place_volumetrically;
393 ierr = DetermineVolumetricInitializationParameters(user, &info, xs_gnode_rank, ys_gnode_rank, zs_gnode_rank,
394 rand_logic_i, rand_logic_j, rand_logic_k,
395 &ci_metric_lnode, &cj_metric_lnode, &ck_metric_lnode,
396 &xi_metric_logic, &eta_metric_logic, &zta_metric_logic,
397 &can_place_volumetrically); CHKERRQ(ierr);
398 if(can_place_volumetrically){
399 ierr = MetricLogicalToPhysical(user, coor_nodes_local_array,
400 ci_metric_lnode, cj_metric_lnode, ck_metric_lnode,
401 xi_metric_logic, eta_metric_logic, zta_metric_logic,
402 &phys_coords); CHKERRQ(ierr);
403 particle_placed_by_this_rank = PETSC_TRUE;
404 } else {
406 "Rank %d: PID %lld (idx %ld) (Volumetric Mode %d) - DetermineVolumetric... returned false. Default Phys: (%.2f,%.2f,%.2f).\n",
407 rank, (long long)(base_pid_for_rank + p), (long)p, simCtx->ParticleInitialization, phys_coords.x, phys_coords.y, phys_coords.z);
408 }
409 }else {
410 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Unknown ParticleInitialization mode %d.", simCtx->ParticleInitialization);
411 }
412
413 // --- 5.c. Store Particle Properties ---
414 positions_field[3*p+0] = phys_coords.x;
415 positions_field[3*p+1] = phys_coords.y;
416 positions_field[3*p+2] = phys_coords.z;
417
418 particleIDs[p] = (PetscInt64)base_pid_for_rank + p;
419 cellIDs_petsc[3*p+0] = -1; cellIDs_petsc[3*p+1] = -1; cellIDs_petsc[3*p+2] = -1;
420 status_field[p] = UNINITIALIZED;
421
422 // --- 5.d. Logging for this particle ---
423 if (particle_placed_by_this_rank) {
424 LOG_LOOP_ALLOW(LOCAL, LOG_VERBOSE, idx, user->simCtx->LoggingFrequency,//(particlesPerProcess > 20 ? particlesPerProcess/10 : 1),
425 "Rank %d: PID %lld (idx %ld) PLACED. Mode %s. Embedded Cell:(%d,%d,%d). Logical Coords: (%.2e,%.2f,%.2f).\n Final Coords: (%.6f,%.6f,%.6f).\n",
426 rank, (long long)particleIDs[p], (long)p, ParticleInitializationToString(simCtx->ParticleInitialization),
427 ci_metric_lnode, cj_metric_lnode, ck_metric_lnode,
428 xi_metric_logic, eta_metric_logic, zta_metric_logic,
429 phys_coords.x, phys_coords.y, phys_coords.z);
430
431 } else {
432 LOG_LOOP_ALLOW(LOCAL, LOG_WARNING, idx, user->simCtx->LoggingFrequency, //(particlesPerProcess > 20 ? particlesPerProcess/10 : 1),
433 "Rank %d: PID %lld (idx %ld) Mode %s NOT placed by this rank's logic. Default Coor: (%.2f,%.2f,%.2f). Relies on migration.\n",
434 rank, (long long)particleIDs[p], (long)p, ParticleInitializationToString(simCtx->ParticleInitialization),
435 phys_coords.x, phys_coords.y, phys_coords.z);
436 }
437 }
438
439 // --- 6. Restore Pointers and Cleanup ---
440 ierr = DMSwarmRestoreField(swarm, "position", NULL, NULL, (void**)&positions_field); CHKERRQ(ierr);
441 ierr = DMSwarmRestoreField(swarm, "DMSwarm_pid", NULL, NULL, (void**)&particleIDs); CHKERRQ(ierr);
442 ierr = DMSwarmRestoreField(swarm, "DMSwarm_CellID", NULL, NULL, (void**)&cellIDs_petsc); CHKERRQ(ierr);
443 ierr = DMSwarmRestoreField(swarm, "DMSwarm_location_status", NULL, NULL, (void**)&status_field); CHKERRQ(ierr);
444 ierr = DMDAVecRestoreArrayRead(user->fda, Coor_local, (void*)&coor_nodes_local_array); CHKERRQ(ierr);
445
446 LOG_ALLOW(LOCAL, LOG_INFO, "Rank %d: Completed processing for %d particles.\n",
447 rank, particlesPerProcess);
448
450
451 PetscFunctionReturn(0);
452}
453
454#undef __FUNCT__
455#define __FUNCT__ "InitializeSwarmFieldValue"
456/**
457 * @brief Helper function to Initialize a given particle’s field value.
458 *
459 * This function performs conditional, point-level Initialization for a swarm field based on its name.
460 * For example, you might want to initialize the "velocity" field to 0.0, but the "temperature"
461 * field to a nonzero default (e.g., 300.0). This function can be extended for other fields.
462 *
463 * @param[in] fieldName Name of the swarm field.
464 * @param[in] p Particle index.
465 * @param[in] fieldDim Dimension of the field.
466 * @param[out] fieldData Pointer to the field’s data array.
467 *
468 * @return PetscErrorCode Returns 0 on success.
469 */
470static PetscErrorCode InitializeSwarmFieldValue(const char *fieldName, PetscInt p, PetscInt fieldDim, PetscReal *fieldData)
471{
472 PetscFunctionBeginUser;
473
475
476 if (strcmp(fieldName, "velocity") == 0) {
477 // For velocity, initialize all components to zero
478 for (PetscInt d = 0; d < fieldDim; d++) {
479 fieldData[fieldDim * p + d] = 0.0;
480 }
481 } else if (strcmp(fieldName, "temperature") == 0) {
482 // For temperature, for example, initialize to a default value (e.g., 300.0)
483 for (PetscInt d = 0; d < fieldDim; d++) {
484 fieldData[fieldDim * p + d] = 300.0;
485 }
486 } else if (strcmp(fieldName, "Psi") == 0) {
487 for (PetscInt d = 0; d < fieldDim; d++) {
488 fieldData[fieldDim * p + d] = 0.0;
489 }
490 } else {
491 // Default: initialize all components to zero
492 for (PetscInt d = 0; d < fieldDim; d++) {
493 fieldData[fieldDim * p + d] = 0.0;
494 }
495 }
496
498 PetscFunctionReturn(0);
499}
500
501
502#undef __FUNCT__
503#define __FUNCT__ "AssignInitialFieldToSwarm"
504/**
505 * @brief Initializes a generic swarm field with point-level updates.
506 *
507 * This field-agnostic function retrieves the specified swarm field (which may be
508 * scalar or multi-component) and initializes each particle's entry using a helper
509 * that performs conditional updates based on the field name.
510 *
511 * @param[in,out] user Pointer to the UserCtx structure containing the swarm.
512 * @param[in] fieldName Name of the swarm field to initialize.
513 * @param[in] fieldDim Dimension of the field (e.g., 1 for scalar, 3 for vector).
514 *
515 * @return PetscErrorCode Returns 0 on success, non-zero on failure.
516 */
517static PetscErrorCode AssignInitialFieldToSwarm(UserCtx *user, const char *fieldName, PetscInt fieldDim)
518{
519 PetscErrorCode ierr;
520 DM swarm = user->swarm;
521 PetscReal *fieldData = NULL;
522 PetscInt nLocal;
523
524 PetscFunctionBeginUser;
525
527
528 // Get the number of local particles
529 ierr = DMSwarmGetLocalSize(swarm, &nLocal); CHKERRQ(ierr);
530 LOG_ALLOW(LOCAL,LOG_INFO, "%d local particles found.\n", nLocal);
531
532 // Retrieve the swarm field pointer for the specified fieldName
533 ierr = DMSwarmGetField(swarm, fieldName, NULL, NULL, (void**)&fieldData); CHKERRQ(ierr);
534 LOG_ALLOW(LOCAL,LOG_DEBUG, "Retrieved field '%s'.\n", fieldName);
535
536 // Loop over all particles and update the field using the helper function
537 for (PetscInt p = 0; p < nLocal; p++) {
538 ierr = InitializeSwarmFieldValue(fieldName, p, fieldDim, fieldData); CHKERRQ(ierr);
539 PetscReal disp_data[fieldDim];
540
541 for (PetscInt d = 0; d < fieldDim; d++) {
542 disp_data[d] = fieldData[fieldDim* p + d];
543 }
544 LOG_LOOP_ALLOW(LOCAL,LOG_VERBOSE,p, 100," Particle %d: %s[%d] = [%.6f, ...,%.6f].\n", p,fieldName,fieldDim,disp_data[0],disp_data[fieldDim-1]);
545 }
546
547 // Restore the swarm field pointer
548 ierr = DMSwarmRestoreField(swarm, fieldName, NULL, NULL, (void**)&fieldData); CHKERRQ(ierr);
549 LOG_ALLOW(LOCAL,LOG_INFO, "Initialization of field '%s' complete.\n", fieldName);
550
551
553
554 PetscFunctionReturn(0);
555}
556
557#undef __FUNCT__
558#define __FUNCT__ "AssignInitialPropertiesToSwarm"
559
560/**
561 * @brief Initializes all particle properties in the swarm.
562 *
563 * This function orchestrates the initialization of particle properties.
564 * It first determines the inlet face if surface initialization (Mode 0) is selected
565 * by parsing "bcs.dat".
566 * Then, it initializes basic particle properties (physical position, Particle ID,
567 * and placeholder Cell IDs) by calling `InitializeParticleBasicProperties`. This call
568 * uses the provided `rand_logic_i/j/k` RNGs, which must be pre-initialized for [0,1).
569 * The `rand_phys_x/y/z` RNGs (physically bounded) are passed but may not be used by
570 * `InitializeParticleBasicProperties` for position setting if all initialization paths
571 * use logical-to-physical mapping.
572 * Finally, it calls helper functions to initialize other registered swarm fields
573 * like "velocity", "weight", and "Psi" (scalar) to default values.
574 *
575 * @param[in,out] user Pointer to the `UserCtx` structure.
576 * @param[in] particlesPerProcess Number of particles assigned to this MPI process.
577 * @param[in] rand_phys_x RNG for physical x-coordinates (from `InitializeRandomGenerators`).
578 * @param[in] rand_phys_y RNG for physical y-coordinates (from `InitializeRandomGenerators`).
579 * @param[in] rand_phys_z RNG for physical z-coordinates (from `InitializeRandomGenerators`).
580 * @param[in] rand_logic_i RNG for i-logical dimension tasks [0,1) (from `InitializeLogicalSpaceRNGs`).
581 * @param[in] rand_logic_j RNG for j-logical dimension tasks [0,1) (from `InitializeLogicalSpaceRNGs`).
582 * @param[in] rand_logic_k RNG for k-logical dimension tasks [0,1) (from `InitializeLogicalSpaceRNGs`).
583 * @param[in] bboxlist Array of BoundingBox structures (potentially unused by IPBP).
584 *
585 * @return PetscErrorCode Returns 0 on success, non-zero on failure.
586 */
588 PetscInt particlesPerProcess,
589 PetscRandom *rand_phys_x, // RNG from original InitializeRandomGenerators
590 PetscRandom *rand_phys_y, // RNG from original InitializeRandomGenerators
591 PetscRandom *rand_phys_z, // RNG from original InitializeRandomGenerators
592 PetscRandom *rand_logic_i, // RNG from InitializeLogicalSpaceRNGs
593 PetscRandom *rand_logic_j, // RNG from InitializeLogicalSpaceRNGs
594 PetscRandom *rand_logic_k, // RNG from InitializeLogicalSpaceRNGs
595 BoundingBox *bboxlist)
596{
597 PetscErrorCode ierr;
598 PetscFunctionBeginUser;
599
601
602 SimCtx *simCtx = user->simCtx;
603
604 // --- 0. Input Validation ---
605 if (!user || !bboxlist || !rand_logic_i || !rand_logic_j || !rand_logic_k || !rand_phys_x || !rand_phys_y || !rand_phys_z) {
606 // Check all RNGs now as they are passed in
607 LOG_ALLOW(GLOBAL, LOG_ERROR, "Null user, bboxlist, or RNG pointer.\n");
608 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Null input detected.");
609 }
610
611 LOG_ALLOW(GLOBAL, LOG_INFO, "Initializing swarm with %d particles per process. Mode: %s.\n",
612 particlesPerProcess, ParticleInitializationToString(simCtx->ParticleInitialization));
613
614 // --- 1. Parse BCS File for Inlet Information (if Mode 0) ---
615 if (simCtx->ParticleInitialization == 0 || simCtx->ParticleInitialization == 3) { // Surface initialization
616 if(user->inletFaceDefined == PETSC_FALSE){
617 LOG_ALLOW(GLOBAL, LOG_ERROR, "Particle Initialization on inlet surface selected, but no INLET face was identified from bcs.dat. Cannot proceed.\n");
618 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONGSTATE, "ParticleInitialization Mode 0 requires an INLET face to be defined in bcs.dat.");
619 }else{
620 LOG_ALLOW(GLOBAL, LOG_INFO, "After Parsing BCS file for Inlet, Inlet face = %s\n", BCFaceToString((BCFace)user->identifiedInletBCFace));
621 }
622 }
623
624 // --- 2. Initialize Basic Particle Properties (Position, PID, Cell IDs placeholder) ---
625 // The rand_logic_i/j/k are now passed directly.
626 // The rand_phys_x/y/z are passed but InitializeParticleBasicProperties (refactored version)
627 // will not use them for setting positions if all its paths use logical-to-physical mapping.
628 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Calling InitializeParticleBasicProperties.\n");
629 ierr = InitializeParticleBasicProperties(user, particlesPerProcess,
630 rand_logic_i, rand_logic_j, rand_logic_k,
631 bboxlist); // bboxlist passed along
632 CHKERRQ(ierr);
633 LOG_ALLOW(GLOBAL, LOG_INFO, "Successfully initialized basic particle properties.\n");
634
635 // Note: The logical RNGs (rand_logic_i/j/k) are NOT destroyed here.
636 // They were created externally (e.g., by InitializeLogicalSpaceRNGs) and
637 // should be destroyed externally (e.g., in FinalizeSwarmSetup).
638 // Same for rand_phys_x/y/z.
639
640 // --- 3. Initialize Other Swarm Fields (Velocity, Weight, Pressure, etc.) ---
641 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Initializing 'velocity' field.\n");
642 ierr = AssignInitialFieldToSwarm(user, "velocity", 3); CHKERRQ(ierr);
643 LOG_ALLOW(LOCAL, LOG_INFO, "'velocity' field initialization complete.\n");
644
645 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Initializing 'weight' field.\n");
646 ierr = AssignInitialFieldToSwarm(user, "weight", 3); CHKERRQ(ierr); // Assuming weight is vec3
647 LOG_ALLOW(LOCAL, LOG_INFO, "'weight' field initialization complete.\n");
648
649 LOG_ALLOW(GLOBAL, LOG_DEBUG, "Initializing 'P' (Pressure) field.\n");
650 ierr = AssignInitialFieldToSwarm(user, "Psi", 1); CHKERRQ(ierr);
651 LOG_ALLOW(GLOBAL, LOG_INFO, "'P' field initialization complete.\n");
652
653 LOG_ALLOW(GLOBAL, LOG_INFO, "Successfully completed all swarm property initialization.\n");
654
655
657
658 PetscFunctionReturn(0);
659}
660
661
662#undef __FUNCT__
663#define __FUNCT__ "DistributeParticles"
664/**
665 * @brief Distributes particles evenly across MPI processes, handling any remainders.
666 *
667 * This function calculates the number of particles each MPI process should handle,
668 * distributing the remainder particles to the first few ranks if necessary.
669 *
670 * @param[in] numParticles Total number of particles to create across all MPI processes.
671 * @param[in] rank MPI rank of the current process.
672 * @param[in] size Total number of MPI processes.
673 * @param[out] particlesPerProcess Number of particles assigned to the current MPI process.
674 * @param[out] remainder Remainder particles when dividing numParticles by size.
675 *
676 * @return PetscErrorCode Returns 0 on success, non-zero on failure.
677 */
678PetscErrorCode DistributeParticles(PetscInt numParticles, PetscMPIInt rank, PetscMPIInt size, PetscInt* particlesPerProcess, PetscInt* remainder) {
679
680 PetscFunctionBeginUser;
681
683 // Calculate the base number of particles per process
684 *particlesPerProcess = numParticles / size;
685 *remainder = numParticles % size;
686
687 // Distribute the remainder particles to the first 'remainder' ranks
688 if (rank < *remainder) {
689 *particlesPerProcess += 1;
690 LOG_ALLOW_SYNC(GLOBAL,LOG_INFO,"Rank %d receives an extra particle. Total: %d\n", rank, *particlesPerProcess);
691 } else {
692 LOG_ALLOW_SYNC(GLOBAL,LOG_INFO, "Rank %d receives %d particles.\n", rank, *particlesPerProcess);
693 }
694
696 PetscFunctionReturn(0);
697}
698
699
700#undef __FUNCT__
701#define __FUNCT__ "FinalizeSwarmSetup"
702/**
703 * @brief Finalizes the swarm setup by destroying random generators and logging completion.
704 *
705 * This function cleans up resources by destroying random number generators and LOG_ALLOWs the completion of swarm setup.
706 *
707 * @param[in] randx Random number generator for the x-coordinate.
708 * @param[in] randy Random number generator for the y-coordinate.
709 * @param[in] randz Random number generator for the z-coordinate.
710 * @param[in] rand_logic_i Random number generator for the xi-coordinate.
711 * @param[in] rand_logic_j Random number generator for the eta-coordinate.
712 * @param[in] rand_logic_k Random number generator for the zeta-coordinate.
713 *
714 * @return PetscErrorCode Returns 0 on success, non-zero on failure.
715 */
716PetscErrorCode FinalizeSwarmSetup(PetscRandom *randx, PetscRandom *randy, PetscRandom *randz, PetscRandom *rand_logic_i, PetscRandom *rand_logic_j, PetscRandom *rand_logic_k) {
717 PetscErrorCode ierr; // Error code for PETSc functions
718 PetscFunctionBeginUser;
720 // Destroy random number generators to free resources
721 // Physical space
722 ierr = PetscRandomDestroy(randx); CHKERRQ(ierr);
723 ierr = PetscRandomDestroy(randy); CHKERRQ(ierr);
724 ierr = PetscRandomDestroy(randz); CHKERRQ(ierr);
725 // Logical space
726 ierr = PetscRandomDestroy(rand_logic_i); CHKERRQ(ierr);
727 ierr = PetscRandomDestroy(rand_logic_j); CHKERRQ(ierr);
728 ierr = PetscRandomDestroy(rand_logic_k); CHKERRQ(ierr);
729
730 LOG_ALLOW(LOCAL,LOG_DEBUG,"Destroyed all random number generators.\n");
731
733 PetscFunctionReturn(0);
734}
735
736#undef __FUNCT__
737#define __FUNCT__ "CreateParticleSwarm"
738/**
739 * @brief Creates and initializes a Particle Swarm.
740 *
741 * This function sets up a DMSwarm within the provided UserCtx structure, initializes
742 * particle fields, and distributes particles across MPI processes. It ensures that
743 * the number of particles is evenly divided among the available MPI ranks. If the total
744 * number of particles isn't divisible by the number of processes, the remainder is distributed
745 * to the first few ranks.
746 *.
747*
748* @param[in,out] user Pointer to the UserCtx structure containing the simulation context.
749* @param[in] numParticles Total number of particles to create across all MPI processes.
750* @param[in] bboxlist Pointer to an array of BoundingBox structures, one per rank.
751*
752* @param[in] particlesPerProcess
753* @return PetscErrorCode Returns 0 on success, non-zero on failure.
754*
755* @note
756* - Ensure that `numParticles` is a positive integer.
757* - The `control.dat` file should contain necessary PETSc options.
758* - The `bboxlist` array should be properly populated before calling this function.
759*/
760PetscErrorCode CreateParticleSwarm(UserCtx *user, PetscInt numParticles, PetscInt *particlesPerProcess, BoundingBox *bboxlist) {
761 PetscErrorCode ierr; // PETSc error handling variable
762 PetscMPIInt rank, size; // Variables to store MPI rank and size
763 PetscInt remainder = 0; // Remainder of particles after division
764
765 PetscFunctionBeginUser;
767 // Validate input parameters
768 if (numParticles <= 0) {
769 LOG_ALLOW(GLOBAL,LOG_DEBUG, "Number of particles must be positive. Given: %d\n", numParticles);
770 return PETSC_ERR_ARG_OUTOFRANGE;
771 }
772
773 // Retrieve MPI rank and size
774 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
775 ierr = MPI_Comm_size(PETSC_COMM_WORLD, &size); CHKERRQ(ierr);
776 LOG_ALLOW(GLOBAL,LOG_INFO," Domain dimensions: [%.2f,%.2f],[%.2f,%.2f],[%.2f,%.2f] \n",
777 user->Min_X,user->Max_X,user->Min_Y,user->Max_Y, user->Min_Z,user->Max_Z);
778 LOG_ALLOW_SYNC(GLOBAL,LOG_DEBUG, "[Rank %d] Local Bounding Box: [%.2f,%.2f],[%.2f,%.2f],[%.2f,%.2f] \n",
779 rank,user->bbox.min_coords.x,user->bbox.max_coords.x,
780 user->bbox.min_coords.y,user->bbox.max_coords.y,
781 user->bbox.min_coords.z,user->bbox.max_coords.z);
782 // Distribute particles among MPI processes
783 ierr = DistributeParticles(numParticles, rank, size, particlesPerProcess, &remainder); CHKERRQ(ierr);
784
785 // Initialize the DMSwarm - creates the swarm, sets the type and dimension
786 ierr = InitializeSwarm(user); CHKERRQ(ierr);
787
788 if (user->da) {
789 ierr = DMSwarmSetCellDM(user->swarm, user->da); CHKERRQ(ierr);
790 LOG_ALLOW(LOCAL,LOG_INFO,"Associated DMSwarm with Cell DM (user->da).\n");
791 } else {
792 // If user->da is essential for your simulation logic with particles, this should be a fatal error.
793 LOG_ALLOW(GLOBAL, LOG_WARNING, "user->da (Cell DM for Swarm) is NULL. Cell-based swarm operations might fail.\n");
794 // SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_ARG_WRONGSTATE, "user->da (Cell DM) is NULL but required.");
795 }
796
797 // Register particle fields (position, velocity, CellID, weight, etc.)
798 ierr = RegisterParticleFields(user->swarm); CHKERRQ(ierr);
799
800 // Set the local number of particles for this rank and additional buffer for particle migration
801 ierr = DMSwarmSetLocalSizes(user->swarm, *particlesPerProcess, numParticles); CHKERRQ(ierr);
802 LOG_ALLOW(GLOBAL,LOG_INFO, "Set local swarm size: %d particles.\n", *particlesPerProcess);
803
804 // Optionally, LOG_ALLOW detailed DM info in debug mode
805 if (get_log_level() == LOG_DEBUG && is_function_allowed(__func__)) {
806 LOG_ALLOW(GLOBAL,LOG_DEBUG,"Viewing DMSwarm:\n");
807 ierr = DMView(user->swarm, PETSC_VIEWER_STDOUT_WORLD); CHKERRQ(ierr);
808 }
809
810 LOG_ALLOW(GLOBAL,LOG_INFO, "Particle swarm creation and initialization complete.\n");
811
813 PetscFunctionReturn(0);
814}
815
816
817#undef __FUNCT__
818#define __FUNCT__ "UnpackSwarmFields"
819
820/**
821 * @brief Initializes a Particle struct with data from DMSwarm fields.
822 *
823 * This helper function populates a Particle structure using data retrieved from DMSwarm fields.
824 *
825 * @param[in] i Index of the particle in the DMSwarm.
826 * @param[in] PIDs Pointer to the array of particle IDs.
827 * @param[in] weights Pointer to the array of particle weights.
828 * @param[in] positions Pointer to the array of particle positions.
829 * @param[in] cellIndices Pointer to the array of particle cell indices.
830 * @param[in] velocities Pointer to the array of particle velocities.
831 * @param[in] LocStatus Pointer to the array of cell location status indicators.
832 * @param[out] particle Pointer to the Particle struct to initialize.
833 *
834 * @return PetscErrorCode Returns `0` on success, non-zero on failure.
835 */
836PetscErrorCode UnpackSwarmFields(PetscInt i, const PetscInt64 *PIDs, const PetscReal *weights,
837 const PetscReal *positions, const PetscInt *cellIndices,
838 PetscReal *velocities,PetscInt *LocStatus,Particle *particle) {
839 PetscFunctionBeginUser;
840
842
843 PetscMPIInt rank;
844 PetscErrorCode ierr;
845
846 ierr = MPI_Comm_rank(PETSC_COMM_WORLD,&rank); CHKERRQ(ierr);
847
848 if (particle == NULL) {
849 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Output Particle pointer is NULL. \n");
850 }
851
852 // logging the start of particle initialization
853 LOG_ALLOW(LOCAL,LOG_DEBUG, "[Rank %d]Unpacking Particle [%d] with PID: %ld.\n",rank, i, PIDs[i]);
854
855 // Initialize PID
856 particle->PID = PIDs[i];
857 LOG_ALLOW(LOCAL,LOG_VERBOSE, "[Rank %d]Particle [%d] PID set to: %ld.\n", rank,i, particle->PID);
858
859 // Initialize weights
860 particle->weights.x = weights[3 * i];
861 particle->weights.y = weights[3 * i + 1];
862 particle->weights.z = weights[3 * i + 2];
863 LOG_ALLOW(LOCAL,LOG_VERBOSE, "[Rank %d]Particle [%d] weights set to: (%.6f, %.6f, %.6f).\n",
864 rank,i, particle->weights.x, particle->weights.y, particle->weights.z);
865
866 // Initialize locations
867 particle->loc.x = positions[3 * i];
868 particle->loc.y = positions[3 * i + 1];
869 particle->loc.z = positions[3 * i + 2];
870 LOG_ALLOW(LOCAL,LOG_VERBOSE, "[Rank %d]Particle [%d] location set to: (%.6f, %.6f, %.6f).\n",
871 rank,i, particle->loc.x, particle->loc.y, particle->loc.z);
872
873 // Initialize velocities (assuming default zero; modify if necessary)
874 particle->vel.x = velocities[3 * i];
875 particle->vel.y = velocities[3 * i + 1];
876 particle->vel.z = velocities[3 * i + 2];
877 LOG_ALLOW(LOCAL,LOG_VERBOSE,"[Rank %d]Particle [%d] velocities unpacked to: [%.6f,%.6f,%.6f].\n",rank, i,particle->vel.x,particle->vel.y,particle->vel.z);
878
879 // Initialize cell indices
880 particle->cell[0] = cellIndices[3 * i];
881 particle->cell[1] = cellIndices[3 * i + 1];
882 particle->cell[2] = cellIndices[3 * i + 2];
883 LOG_ALLOW(LOCAL,LOG_VERBOSE,"[Rank %d]Particle [%d] cell indices set to: [%d, %d, %d].\n",rank,i, particle->cell[0], particle->cell[1], particle->cell[2]);
884
885 particle->location_status = (ParticleLocationStatus)LocStatus[i];
886 LOG_ALLOW(LOCAL,LOG_VERBOSE, "[Rank %d]Particle [%d] Status set to: %d.\n",rank, i, particle->location_status);
887
888 // The destination_rank is only set by the location search, not read from the swarm,
889 // so we initialize it to a known invalid state.
890 particle->destination_rank = MPI_PROC_NULL;
891
892 // logging the completion of particle initialization
893 LOG_ALLOW(LOCAL,LOG_DEBUG,"[Rank %d]Completed initialization of Particle [%d]. \n", rank,i);
894
895
897
898 PetscFunctionReturn(0);
899}
900
901
902#undef __FUNCT__
903#define __FUNCT__ "UpdateSwarmFields"
904
905/**
906 * @brief Updates DMSwarm fields with data from a Particle struct.
907 *
908 * This helper function writes back the modified Particle data to the corresponding DMSwarm fields.
909 *
910 * @param[in] i Index of the particle in the DMSwarm.
911 * @param[in] particle Pointer to the Particle struct containing updated data.
912 * @param[in,out] weights Pointer to the array of particle weights.
913 * @param[in,out] cellIndices Pointer to the array of particle cell indices.
914 * @param[in,out] LocStatus Pointer to the array of cell location status indicators.
915 *
916 * @return PetscErrorCode Returns `0` on success, non-zero on failure.
917 */
918PetscErrorCode UpdateSwarmFields(PetscInt i, const Particle *particle,
919 PetscReal *weights, PetscInt *cellIndices, PetscInt *status_field) {
920 PetscFunctionBeginUser;
922
923 if (particle == NULL) {
924 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Input Particle pointer is NULL.\n");
925 }
926
927 // logging the start of swarm fields update
928 LOG_ALLOW(LOCAL,LOG_DEBUG,"Updating DMSwarm fields for Particle [%d].\n", i);
929
930 // Update weights
931 weights[3 * i] = particle->weights.x;
932 weights[3 * i + 1] = particle->weights.y;
933 weights[3 * i + 2] = particle->weights.z;
934 LOG_ALLOW(LOCAL,LOG_VERBOSE,"Updated weights for Particle [%d]: (%.6f, %.6f, %.6f).\n",
935 i, weights[3 * i], weights[3 * i + 1], weights[3 * i + 2]);
936
937 // Update cell indices
938 cellIndices[3 * i] = particle->cell[0];
939 cellIndices[3 * i + 1] = particle->cell[1];
940 cellIndices[3 * i + 2] = particle->cell[2];
941 LOG_ALLOW(LOCAL,LOG_VERBOSE, "Updated cell indices for Particle [%d]: [%d, %d, %d].\n",
942 i, cellIndices[3 * i], cellIndices[3 * i + 1], cellIndices[3 * i + 2]);
943
944 status_field[i] = particle->location_status;
945 LOG_ALLOW(LOCAL,LOG_VERBOSE, "Updated location status for Particle [%d]: [%d].\n",
946 i, status_field[i]);
947
948 // logging the completion of swarm fields update
949 LOG_ALLOW(LOCAL,LOG_DEBUG,"Completed updating DMSwarm fields for Particle [%d].\n", i);
950
951
953 PetscFunctionReturn(0);
954}
955
956
957#undef __FUNCT__
958#define __FUNCT__ "IsParticleInsideBoundingBox"
959/**
960 * @brief Checks if a particle's location is within a specified bounding box.
961 *
962 * This function determines whether the given particle's location lies inside the provided bounding box.
963 * It performs an axis-aligned bounding box (AABB) check by comparing the particle's coordinates to the
964 * minimum and maximum coordinates of the bounding box in each dimension (x, y, z).
965 *
966 * logging statements are included to provide detailed information about the function's execution.
967 *
968 * @param[in] bbox Pointer to the BoundingBox structure containing minimum and maximum coordinates.
969 * @param[in] particle Pointer to the Particle structure containing the particle's location and identifier.
970 *
971 * @return PetscBool Returns `PETSC_TRUE` if the particle is inside the bounding box, `PETSC_FALSE` otherwise.
972 *
973 * @note
974 * - The function assumes that the `bbox` and `particle` pointers are valid and non-NULL.
975 * - The function includes logging statements that start with the function name.
976 * - The `LOG_ALLOW_SCOPE` variable is used to distinguish between `GLOBAL` and `LOCAL` LOG_ALLOW outputs.
977 * - Be cautious when logging in performance-critical code sections, especially if the function is called frequently.
978 */
979PetscBool IsParticleInsideBoundingBox(const BoundingBox *bbox, const Particle *particle)
980{
981 PetscFunctionBeginUser;
983
984 // Validate input pointers
985 if (!bbox) {
986 // LOG_ALLOW error message and return PETSC_FALSE
987 LOG_ALLOW(LOCAL,LOG_ERROR, "Error - 'bbox' pointer is NULL.");
989 return PETSC_FALSE;
990 }
991 if (!particle) {
992 LOG_ALLOW(LOCAL,LOG_ERROR,"Error - 'particle' pointer is NULL.");
994 return PETSC_FALSE;
995 }
996
997 // Extract particle location and bounding box coordinates
998 const Cmpnts loc = particle->loc;
999 const Cmpnts min_coords = bbox->min_coords;
1000 const Cmpnts max_coords = bbox->max_coords;
1001
1002 // LOG_ALLOW the particle location and bounding box coordinates for debugging
1003 LOG_ALLOW_SYNC(LOCAL, LOG_VERBOSE, "Particle PID %ld location: (%.6f, %.6f, %.6f).\n",particle->PID, loc.x, loc.y, loc.z);
1004 LOG_ALLOW_SYNC(LOCAL, LOG_VERBOSE, "BoundingBox min_coords: (%.6f, %.6f, %.6f), max_coords: (%.6f, %.6f, %.6f).\n",
1005 min_coords.x, min_coords.y, min_coords.z, max_coords.x, max_coords.y, max_coords.z);
1006
1007 // Check if the particle's location is within the bounding box
1008 if ((loc.x >= min_coords.x && loc.x <= max_coords.x) &&
1009 (loc.y >= min_coords.y && loc.y <= max_coords.y) &&
1010 (loc.z >= min_coords.z && loc.z <= max_coords.z)) {
1011 // Particle is inside the bounding box
1012 LOG_ALLOW_SYNC(LOCAL,LOG_VERBOSE, "Particle PID %ld is inside the bounding box.\n",particle->PID);
1014 return PETSC_TRUE;
1015 }
1016
1017 // Particle is outside the bounding box
1018 LOG_ALLOW_SYNC(LOCAL, LOG_VERBOSE,"Particle PID %ld is outside the bounding box.\n",particle->PID);
1020 return PETSC_FALSE;
1021}
1022
1023
1024#undef __FUNCT__
1025#define __FUNCT__ "UpdateParticleWeights"
1026/**
1027 * @brief Updates a particle's interpolation weights based on distances to cell faces.
1028 *
1029 * This function computes interpolation weights using distances to the six
1030 * cell faces (`d`) and updates the `weight` field of the provided particle.
1031 *
1032 * @param[in] d Pointer to an array of distances to the six cell faces.
1033 * @param[out] particle Pointer to the Particle structure whose weights are to be updated.
1034 *
1035 * @return PetscErrorCode Returns 0 on success, or a non-zero error code on failure.
1036 */
1037PetscErrorCode UpdateParticleWeights(PetscReal *d, Particle *particle) {
1038
1039 PetscFunctionBeginUser;
1041
1042 // Validate input pointers
1043 if (!d || !particle) {
1044 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
1045 "Null pointer argument (d or particle).");
1046 }
1047
1048
1049 // Validate distances
1050 for (PetscInt i = LEFT; i < NUM_FACES; i++) {
1053 "face distance d[%d] = %f <= %f; "
1054 "clamping to 1e-14 to avoid zero/negative.\n",
1055 i, (double)d[i], INTERPOLATION_DISTANCE_TOLERANCE);
1057 }
1058 }
1059
1060 // LOG_ALLOW the input distances
1062 "Calculating weights with distances: "
1063 "[LEFT=%f, RIGHT=%f, BOTTOM=%f, TOP=%f, FRONT=%f, BACK=%f].\n",
1064 d[LEFT], d[RIGHT], d[BOTTOM], d[TOP], d[FRONT], d[BACK]);
1065
1066 // Compute and update the particle's weights
1067 particle->weights.x = d[LEFT] / (d[LEFT] + d[RIGHT]);
1068 particle->weights.y = d[BOTTOM] / (d[BOTTOM] + d[TOP]);
1069 particle->weights.z = d[BACK] / (d[FRONT] + d[BACK]);
1070
1071 // LOG_ALLOW the updated weights
1073 "Updated particle weights: x=%f, y=%f, z=%f.\n",
1074 particle->weights.x, particle->weights.y, particle->weights.z);
1075
1076
1078 PetscFunctionReturn(0);
1079}
1080
1081/**
1082 * @brief Initializes or loads the particle swarm based on the simulation context.
1083 *
1084 * This function is the central point for setting up the DMSwarm. Its behavior
1085 * depends on the simulation context (simCtx):
1086 *
1087 * 1. **Fresh Start (simCtx->StartStep == 0):** A new particle population is
1088 * generated according to the specified initial conditions.
1089 *
1090 * 2. **Restart (simCtx->StartStep > 0):**
1091 * - If `simCtx->particleRestartMode` is "init", a new particle population
1092 * is generated, just like a fresh start. This allows injecting fresh
1093 * particles into a pre-computed flow field.
1094 * - If `simCtx->particleRestartMode` is "load", the particle state is loaded
1095 * from restart files corresponding to the StartStep.
1096 *
1097 * @param[in,out] simCtx Pointer to the main SimulationContext, which contains all
1098 * configuration and provides access to the UserCtx.
1099 * @return PetscErrorCode Returns 0 on success, non-zero on failure.
1100 */
1101#undef __FUNCT__
1102#define __FUNCT__ "InitializeParticleSwarm"
1103PetscErrorCode InitializeParticleSwarm(SimCtx *simCtx)
1104{
1105 PetscErrorCode ierr;
1106 PetscInt particlesPerProcess = 0;
1107 UserCtx *user = simCtx->usermg.mgctx[simCtx->usermg.mglevels - 1].user;
1108
1109 PetscFunctionBeginUser;
1111
1112 LOG_ALLOW(GLOBAL, LOG_INFO, "Starting particle swarm setup for %d particles.\n", simCtx->np);
1113
1114 // --- Phase 1: Create the DMSwarm Object (Always required) ---
1115 // This creates the container and registers the fields. It does not add particles yet.
1116 ierr = CreateParticleSwarm(user, simCtx->np, &particlesPerProcess, simCtx->bboxlist); CHKERRQ(ierr);
1117 LOG_ALLOW(GLOBAL, LOG_INFO, "DMSwarm object and fields created successfully.\n");
1118
1119
1120 // --- Phase 2: Decide whether to Initialize new particles or Load existing ones ---
1121 PetscBool should_initialize_new_particles = PETSC_FALSE;
1122 if(simCtx->exec_mode == EXEC_MODE_POSTPROCESSOR){
1123 should_initialize_new_particles = PETSC_TRUE;
1124 }else{
1125 if (simCtx->StartStep == 0) {
1126 should_initialize_new_particles = PETSC_TRUE; // Standard fresh start
1127 } else {
1128 // It's a restart, so check the user's requested particle mode.
1129 if (strcmp(simCtx->particleRestartMode, "init") == 0) {
1130 should_initialize_new_particles = PETSC_TRUE; // User wants to re-initialize particles in a restarted flow.
1131 }
1132 }
1133 }
1134
1135 // --- Phase 3: Execute the chosen particle setup path ---
1136 if (should_initialize_new_particles) {
1137 // --- PATH A: Generate a fresh population of particles ---
1138 LOG_ALLOW(GLOBAL, LOG_INFO, "Mode: INITIALIZE. Generating new particle population.\n");
1139 PetscRandom randx, randy, randz;
1140 PetscRandom rand_logic_i, rand_logic_j, rand_logic_k;
1141
1142 ierr = InitializeRandomGenerators(user, &randx, &randy, &randz); CHKERRQ(ierr);
1143 ierr = InitializeLogicalSpaceRNGs(&rand_logic_i, &rand_logic_j, &rand_logic_k); CHKERRQ(ierr);
1144 ierr = AssignInitialPropertiesToSwarm(user, particlesPerProcess, &randx, &randy, &randz, &rand_logic_i, &rand_logic_j, &rand_logic_k, simCtx->bboxlist); CHKERRQ(ierr);
1145 ierr = FinalizeSwarmSetup(&randx, &randy, &randz, &rand_logic_i, &rand_logic_j, &rand_logic_k); CHKERRQ(ierr);
1146
1147 } else {
1148 // --- PATH B: Load particle population from restart files ---
1149 // This path is only taken if simCtx->StartStep > 0 AND simCtx->particleRestartMode == "load"
1150 LOG_ALLOW(GLOBAL, LOG_INFO, "Mode: LOAD. Loading particle population from files for step %d.\n", simCtx->StartStep);
1151
1152 ierr = PreCheckAndResizeSwarm(user, simCtx->StartStep, "dat"); CHKERRQ(ierr);
1153
1154 ierr = ReadAllSwarmFields(user, simCtx->StartStep); CHKERRQ(ierr);
1155 // Note: We check for file-open errors inside ReadAllSwarmFields now.
1156
1157 LOG_ALLOW(GLOBAL, LOG_INFO, "Particle data loaded. CellID and status are preserved from file.\n");
1158 }
1159
1160 LOG_ALLOW_SYNC(GLOBAL, LOG_INFO, "Particle swarm setup complete.\n");
1161
1163 PetscFunctionReturn(0);
1164}
1165
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...
Definition Boundaries.c:459
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,...
Definition Boundaries.c:25
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.
Definition Boundaries.c:267
PetscErrorCode MetricLogicalToPhysical(UserCtx *user, const Cmpnts ***X, PetscInt i, PetscInt j, PetscInt k, PetscReal xi, PetscReal eta, PetscReal zta, Cmpnts *Xp)
Public wrapper: map (cell index, ξ,η,ζ) to (x,y,z).
Definition Metric.c:77
PetscErrorCode PreCheckAndResizeSwarm(UserCtx *user, PetscInt ti, const char *ext)
Checks particle count in the reference file and resizes the swarm if needed.
PetscErrorCode UpdateParticleWeights(PetscReal *d, Particle *particle)
Updates a particle's interpolation weights based on distances to cell faces.
PetscErrorCode CreateParticleSwarm(UserCtx *user, PetscInt numParticles, PetscInt *particlesPerProcess, BoundingBox *bboxlist)
Creates and initializes a Particle Swarm.
PetscErrorCode FinalizeSwarmSetup(PetscRandom *randx, PetscRandom *randy, PetscRandom *randz, PetscRandom *rand_logic_i, PetscRandom *rand_logic_j, PetscRandom *rand_logic_k)
Finalizes the swarm setup by destroying random generators and logging completion.
PetscErrorCode DistributeParticles(PetscInt numParticles, PetscMPIInt rank, PetscMPIInt size, PetscInt *particlesPerProcess, PetscInt *remainder)
Distributes particles evenly across MPI processes, handling any remainders.
static PetscErrorCode AssignInitialFieldToSwarm(UserCtx *user, const char *fieldName, PetscInt fieldDim)
Initializes a generic swarm field with point-level updates.
#define INTERPOLATION_DISTANCE_TOLERANCE
static PetscErrorCode InitializeSwarmFieldValue(const char *fieldName, PetscInt p, PetscInt fieldDim, PetscReal *fieldData)
Helper function to Initialize a given particle’s field value.
PetscBool IsParticleInsideBoundingBox(const BoundingBox *bbox, const Particle *particle)
Checks if a particle's location is within a specified bounding box.
static PetscErrorCode InitializeParticleBasicProperties(UserCtx *user, PetscInt particlesPerProcess, PetscRandom *rand_logic_i, PetscRandom *rand_logic_j, PetscRandom *rand_logic_k, BoundingBox *bboxlist)
Initializes basic properties for particles on the local process.
PetscErrorCode UnpackSwarmFields(PetscInt i, const PetscInt64 *PIDs, const PetscReal *weights, const PetscReal *positions, const PetscInt *cellIndices, PetscReal *velocities, PetscInt *LocStatus, Particle *particle)
Initializes a Particle struct with data from DMSwarm fields.
PetscErrorCode InitializeSwarm(UserCtx *user)
Initializes the DMSwarm object within the UserCtx structure.
PetscErrorCode UpdateSwarmFields(PetscInt i, const Particle *particle, PetscReal *weights, PetscInt *cellIndices, PetscInt *status_field)
Updates DMSwarm fields with data from a Particle struct.
PetscErrorCode InitializeParticleSwarm(SimCtx *simCtx)
Perform particle swarm initialization, particle-grid interaction, and related operations.
PetscErrorCode RegisterSwarmField(DM swarm, const char *fieldName, PetscInt fieldDim, PetscDataType dtype)
Registers a swarm field without finalizing registration.
static PetscErrorCode DetermineVolumetricInitializationParameters(UserCtx *user, DMDALocalInfo *info, PetscInt xs_gnode, PetscInt ys_gnode, PetscInt zs_gnode, 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, PetscBool *can_place_in_volume_out)
Determines cell selection and intra-cell logical coordinates for volumetric initialization (Mode 1).
PetscErrorCode RegisterParticleFields(DM swarm)
Registers necessary particle fields within the DMSwarm.
PetscErrorCode AssignInitialPropertiesToSwarm(UserCtx *user, PetscInt particlesPerProcess, PetscRandom *rand_phys_x, PetscRandom *rand_phys_y, PetscRandom *rand_phys_z, PetscRandom *rand_logic_i, PetscRandom *rand_logic_j, PetscRandom *rand_logic_k, BoundingBox *bboxlist)
Initializes all particle properties in the swarm.
Header file for Particle Swarm management functions.
PetscErrorCode ReadAllSwarmFields(UserCtx *user, PetscInt ti)
Reads multiple fields (positions, velocity, CellID, and weight) into a DMSwarm.
Definition io.c:1327
#define LOG_LOOP_ALLOW(scope, level, iterVar, interval, fmt,...)
Logs a message inside a loop, but only every interval iterations.
Definition logging.h:313
PetscBool is_function_allowed(const char *functionName)
Checks if a given function is in the allow-list.
Definition logging.c:157
#define LOG_ALLOW_SYNC(scope, level, fmt,...)
----— DEBUG ---------------------------------------— #define LOG_ALLOW(scope, level,...
Definition logging.h:268
#define LOCAL
Logging scope definitions for controlling message output.
Definition logging.h:46
#define GLOBAL
Scope for global logging across all processes.
Definition logging.h:47
const char * BCFaceToString(BCFace face)
Helper function to convert BCFace enum to a string representation.
Definition logging.c:643
#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:201
#define PROFILE_FUNCTION_END
Marks the end of a profiled code block.
Definition logging.h:740
const char * ParticleInitializationToString(PetscInt ParticleInitialization)
Helper function to convert ParticleInitialization to a string representation.
Definition logging.c:675
LogLevel get_log_level()
Retrieves the current logging level from the environment variable LOG_LEVEL.
Definition logging.c:39
@ LOG_ERROR
Critical errors that may halt the program.
Definition logging.h:29
@ LOG_INFO
Informational messages about program execution.
Definition logging.h:32
@ LOG_WARNING
Non-critical issues that warrant attention.
Definition logging.h:30
@ LOG_DEBUG
Detailed debugging information.
Definition logging.h:33
@ LOG_VERBOSE
Extremely detailed logs, typically for development use only.
Definition logging.h:35
#define PROFILE_FUNCTION_BEGIN
Marks the beginning of a profiled code block (typically a function).
Definition logging.h:731
PetscErrorCode InitializeRandomGenerators(UserCtx *user, PetscRandom *randx, PetscRandom *randy, PetscRandom *randz)
Initializes random number generators for assigning particle properties.
Definition setup.c:2549
PetscErrorCode InitializeLogicalSpaceRNGs(PetscRandom *rand_logic_i, PetscRandom *rand_logic_j, PetscRandom *rand_logic_k)
Initializes random number generators for logical space operations [0.0, 1.0).
Definition setup.c:2600
Cmpnts vel
Definition variables.h:169
UserCtx * user
Definition variables.h:427
PetscBool inletFaceDefined
Definition variables.h:680
BCFace identifiedInletBCFace
Definition variables.h:681
PetscInt cell[3]
Definition variables.h:167
SimCtx * simCtx
Back-pointer to the master simulation context.
Definition variables.h:664
ParticleLocationStatus
Defines the state of a particle with respect to its location and migration status during the iterativ...
Definition variables.h:135
@ UNINITIALIZED
Definition variables.h:140
PetscReal CMy_c
Definition variables.h:589
PetscReal Min_X
Definition variables.h:671
UserMG usermg
Definition variables.h:631
Cmpnts max_coords
Maximum x, y, z coordinates of the bounding box.
Definition variables.h:156
PetscInt np
Definition variables.h:616
PetscReal Max_Y
Definition variables.h:671
PetscInt StartStep
Definition variables.h:548
Cmpnts min_coords
Minimum x, y, z coordinates of the bounding box.
Definition variables.h:155
PetscScalar x
Definition variables.h:101
Cmpnts loc
Definition variables.h:168
PetscMPIInt destination_rank
Definition variables.h:172
ParticleLocationStatus location_status
Definition variables.h:171
char particleRestartMode[16]
Definition variables.h:621
BoundingBox * bboxlist
Definition variables.h:619
PetscReal CMz_c
Definition variables.h:589
PetscScalar z
Definition variables.h:101
PetscInt mglevels
Definition variables.h:434
PetscReal Min_Z
Definition variables.h:671
PetscReal Max_X
Definition variables.h:671
PetscReal Min_Y
Definition variables.h:671
PetscScalar y
Definition variables.h:101
@ EXEC_MODE_POSTPROCESSOR
Definition variables.h:512
Cmpnts weights
Definition variables.h:170
@ TOP
Definition variables.h:145
@ FRONT
Definition variables.h:145
@ BOTTOM
Definition variables.h:145
@ BACK
Definition variables.h:145
@ LEFT
Definition variables.h:145
@ NUM_FACES
Definition variables.h:145
@ RIGHT
Definition variables.h:145
MGCtx * mgctx
Definition variables.h:437
ExecutionMode exec_mode
Definition variables.h:556
PetscInt ParticleInitialization
Definition variables.h:620
BoundingBox bbox
Definition variables.h:672
PetscReal Max_Z
Definition variables.h:671
PetscInt64 PID
Definition variables.h:166
PetscInt LoggingFrequency
Definition variables.h:636
PetscReal CMx_c
Definition variables.h:589
BCFace
Identifies the six logical faces of a structured computational block.
Definition variables.h:200
Defines a 3D axis-aligned bounding box.
Definition variables.h:154
A 3D point or vector with PetscScalar components.
Definition variables.h:100
Defines a particle's core properties for Lagrangian tracking.
Definition variables.h:165
The master context for the entire simulation.
Definition variables.h:538
User-defined context containing data specific to a single computational grid level.
Definition variables.h:661