PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
Loading...
Searching...
No Matches
walkingsearch.c
Go to the documentation of this file.
1// walkingsearch.c
2
3#include "walkingsearch.h"
4#include "logging.h"
5#include <petsc.h>
6#include <stdbool.h>
7#include <math.h>
8
9// Define maximum traversal steps to prevent infinite loops
10#define MAX_TRAVERSAL 1000
11#define DISTANCE_THRESHOLD 1e-11
12#define REPEAT_COUNT_THRESHOLD 5
13
14#undef __FUNCT__
15#define __FUNCT__ "GetCellCharacteristicSize"
16/**
17 * @brief Estimates a characteristic length of the cell for threshold scaling.
18 *
19 * For a hexahedral cell with vertices cell->vertices[0..7], we approximate
20 * the cell size by some measure—e.g. average edge length or diagonal.
21 * @param[in] cell A pointer to the Cell structure
22 * @param[out] cellSize A pointer to a PetscReal where the characteristic size is stored.
23 *
24 * @return PetscErrorCode 0 on success, non-zero on failure.
25 */
26PetscErrorCode GetCellCharacteristicSize(const Cell *cell, PetscReal *cellSize)
27{
28 PetscErrorCode ierr = 0;
29 PetscFunctionBeginUser;
31 if (!cell || !cellSize) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
32 "GetCellCharacteristicSize: Null pointer(s).");
33
34 // A simple approach: compute the average of the distances between
35 // all pairs of adjacent vertices that share an edge. That gives
36 // a typical measure of cell dimension. For a uniform grid, you
37 // could do something simpler.
38
39 PetscInt edges[12][2] = {
40 {0,1},{1,2},{2,3},{3,0}, // bottom face edges
41 {4,5},{5,6},{6,7},{7,4}, // top face edges
42 {0,7},{1,6},{2,5},{3,4} // vertical edges
43 };
44
45 PetscReal totalEdgeLen = 0.0;
46 for (PetscInt i=0; i<12; i++) {
47 PetscInt vA = edges[i][0];
48 PetscInt vB = edges[i][1];
49 Cmpnts A = cell->vertices[vA];
50 Cmpnts B = cell->vertices[vB];
51 PetscReal dx = B.x - A.x;
52 PetscReal dy = B.y - A.y;
53 PetscReal dz = B.z - A.z;
54 PetscReal edgeLen = sqrt(dx*dx + dy*dy + dz*dz);
55 totalEdgeLen += edgeLen;
56 }
57
58 // Average edge length
59 *cellSize = totalEdgeLen / 12.0;
60
62 PetscFunctionReturn(ierr);
63}
64
65
66#undef __FUNCT__
67#define __FUNCT__ "ComputeSignedDistanceToPlane"
68/**
69 * @brief Computes the signed distance from a point to the plane approximating a quadrilateral face.
70 *
71 * This function calculates the signed distance from a given point `p_target` to the plane
72 * approximating a quadrilateral face defined by points `v1`, `v2`, `v3`, and `v4`.
73 * The plane's initial normal is determined using two edge vectors emanating from `v1`
74 * (i.e., `v2-v1` and `v4-v1`).
75 *
76 * **Normal Orientation:**
77 * The initial normal's orientation is checked against the cell's interior.
78 * A vector from the face centroid to the cell centroid (`vec_face_to_cell_centroid`) is computed.
79 * If the dot product of the initial normal and `vec_face_to_cell_centroid` is positive,
80 * it means the initial normal is pointing towards the cell's interior (it's an inward normal
81 * relative to the cell). In this case, the normal is flipped to ensure it points outward.
82 *
83 * **Signed Distance Convention:**
84 * The signed distance is the projection of the vector from `p_target` to the face's centroid
85 * onto the (now guaranteed) outward-pointing plane's normal vector.
86 * - `d_signed < 0`: `p_target` is "outside" and "beyond" the face, in the direction of the outward normal.
87 * This is the primary case indicating the particle has crossed this face.
88 * - `d_signed > 0`: `p_target` is "outside" but "behind" the face (on the same side as the cell interior
89 * relative to this face plane).
90 * - `d_signed = 0`: `p_target` is on the face plane (within threshold).
91 *
92 * @param[in] v1 First vertex defining the face (used as origin for initial normal calculation).
93 * @param[in] v2 Second vertex defining the face (used for first edge vector: v2-v1).
94 * @param[in] v3 Third vertex defining the face (used for face centroid calculation).
95 * @param[in] v4 Fourth vertex defining the face (used for second edge vector: v4-v1).
96 * @param[in] cell_centroid The centroid of the 8-vertex cell to which this face belongs.
97 * @param[in] p_target The point from which the distance to the plane is calculated.
98 * @param[out] d_signed Pointer to store the computed signed distance.
99 * @param[in] threshold The threshold below which the absolute distance is considered zero.
100 *
101 * @note
102 * - The order of vertices `v1, v2, v3, v4` is used for calculating the face centroid.
103 * The order of `v1, v2, v4` is used for the initial normal calculation.
104 * - The `cell_centroid` is crucial for robustly determining the outward direction of the normal.
105 *
106 * @return PetscErrorCode Returns 0 on success, PETSC_ERR_ARG_NULL if `d_signed` is NULL,
107 * or PETSC_ERR_USER if a degenerate plane (near-zero normal) is detected.
108 */
109PetscErrorCode ComputeSignedDistanceToPlane(const Cmpnts v1, const Cmpnts v2, const Cmpnts v3, const Cmpnts v4,
110 const Cmpnts cell_centroid,const Cmpnts p_target,
111 PetscReal *d_signed, const PetscReal threshold)
112{
113 PetscErrorCode ierr = 0;
114 PetscMPIInt rank;
115
116 PetscFunctionBeginUser;
118
119 if (d_signed == NULL) {
120 ierr = MPI_Comm_rank(MPI_COMM_WORLD, &rank); CHKERRQ(ierr);
121 LOG_ALLOW(LOCAL, LOG_ERROR, "Output pointer 'd_signed' is NULL on rank %d.\n", rank);
122 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Output pointer 'd_signed' must not be NULL.");
123 }
124
125 LOG_ALLOW(LOCAL, LOG_VERBOSE, "Target point: (%.6e, %.6e, %.6e)\n", p_target.x, p_target.y, p_target.z);
126 LOG_ALLOW(LOCAL, LOG_VERBOSE, " Cell Centroid: (%.6e, %.6e, %.6e)\n", cell_centroid.x, cell_centroid.y, cell_centroid.z);
127 LOG_ALLOW(LOCAL, LOG_VERBOSE, " Face Vertices:\n");
128 LOG_ALLOW(LOCAL, LOG_VERBOSE, " v1: (%.6e, %.6e, %.6e)\n", v1.x, v1.y, v1.z);
129 LOG_ALLOW(LOCAL, LOG_VERBOSE, " v2: (%.6e, %.6e, %.6e)\n", v2.x, v2.y, v2.z);
130 LOG_ALLOW(LOCAL, LOG_VERBOSE, " v3: (%.6e, %.6e, %.6e)\n", v3.x, v3.y, v3.z);
131 LOG_ALLOW(LOCAL, LOG_VERBOSE, " v4: (%.6e, %.6e, %.6e)\n", v4.x, v4.y, v4.z);
132
133 // --- Calculate Edge Vectors for Initial Normal Computation ---
134 PetscReal edge1_x = v2.x - v1.x;
135 PetscReal edge1_y = v2.y - v1.y;
136 PetscReal edge1_z = v2.z - v1.z;
137
138 PetscReal edge2_x = v4.x - v1.x;
139 PetscReal edge2_y = v4.y - v1.y;
140 PetscReal edge2_z = v4.z - v1.z;
141 LOG_ALLOW(LOCAL, LOG_VERBOSE, " Edge1 (v2-v1): (%.6e, %.6e, %.6e)\n", edge1_x, edge1_y, edge1_z);
142 LOG_ALLOW(LOCAL, LOG_VERBOSE, " Edge2 (v4-v1): (%.6e, %.6e, %.6e)\n", edge2_x, edge2_y, edge2_z);
143
144 // --- Compute Initial Normal Vector (Cross Product: edge1 x edge2) ---
145 PetscReal normal_x_initial = edge1_y * edge2_z - edge1_z * edge2_y;
146 PetscReal normal_y_initial = edge1_z * edge2_x - edge1_x * edge2_z;
147 PetscReal normal_z_initial = edge1_x * edge2_y - edge1_y * edge2_x;
148 LOG_ALLOW(LOCAL, LOG_VERBOSE, " Initial Raw Normal (edge1 x edge2): (%.6e, %.6e, %.6e)\n", normal_x_initial, normal_y_initial, normal_z_initial);
149
150 PetscReal normal_magnitude = sqrt(normal_x_initial * normal_x_initial +
151 normal_y_initial * normal_y_initial +
152 normal_z_initial * normal_z_initial);
153 LOG_ALLOW(LOCAL, LOG_VERBOSE, " Initial Normal Magnitude: %.6e\n", normal_magnitude);
154
155 if (normal_magnitude < 1.0e-12) {
156 ierr = MPI_Comm_rank(MPI_COMM_WORLD, &rank); CHKERRQ(ierr);
158 "Degenerate plane detected on rank %d. Normal magnitude (%.3e) is too small.\n",
159 rank, normal_magnitude);
160 LOG_ALLOW(LOCAL, LOG_ERROR, " Offending vertices for normal: v1(%.3e,%.3e,%.3e), v2(%.3e,%.3e,%.3e), v4(%.3e,%.3e,%.3e)\n",
161 v1.x,v1.y,v1.z, v2.x,v2.y,v2.z, v4.x,v4.y,v4.z);
162 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_USER, "Degenerate plane detected (normal vector is near zero).");
163 }
164
165 // --- Calculate the Centroid of the Four Face Vertices (v1, v2, v3, v4) ---
166 PetscReal face_centroid_x = 0.25 * (v1.x + v2.x + v3.x + v4.x);
167 PetscReal face_centroid_y = 0.25 * (v1.y + v2.y + v3.y + v4.y);
168 PetscReal face_centroid_z = 0.25 * (v1.z + v2.z + v3.z + v4.z);
169 LOG_ALLOW(LOCAL, LOG_VERBOSE, " Face Centroid: (%.6e, %.6e, %.6e)\n", face_centroid_x, face_centroid_y, face_centroid_z);
170
171 // --- Orient the Normal to Point Outward from the Cell ---
172 // Vector from face centroid to cell centroid
173 PetscReal vec_fc_to_cc_x = cell_centroid.x - face_centroid_x;
174 PetscReal vec_fc_to_cc_y = cell_centroid.y - face_centroid_y;
175 PetscReal vec_fc_to_cc_z = cell_centroid.z - face_centroid_z;
176 LOG_ALLOW(LOCAL, LOG_VERBOSE, " Vec (FaceCentroid -> CellCentroid): (%.6e, %.6e, %.6e)\n", vec_fc_to_cc_x, vec_fc_to_cc_y, vec_fc_to_cc_z);
177
178 // Dot product of initial normal with vector from face centroid to cell centroid
179 PetscReal dot_prod_orientation = normal_x_initial * vec_fc_to_cc_x +
180 normal_y_initial * vec_fc_to_cc_y +
181 normal_z_initial * vec_fc_to_cc_z;
182 LOG_ALLOW(LOCAL, LOG_VERBOSE, " Dot Product for Orientation (N_initial . Vec_FC_to_CC): %.6e\n", dot_prod_orientation);
183
184 PetscReal normal_x = normal_x_initial;
185 PetscReal normal_y = normal_y_initial;
186 PetscReal normal_z = normal_z_initial;
187
188 // If dot_prod_orientation > 0, initial normal points towards cell interior, so flip it.
189 if (dot_prod_orientation > 1.0e-9) { // Use a small epsilon to avoid issues if dot product is extremely close to zero
190 normal_x = -normal_x_initial;
191 normal_y = -normal_y_initial;
192 normal_z = -normal_z_initial;
193 LOG_ALLOW(LOCAL, LOG_VERBOSE, " Initial normal was inward (dot_prod > 0). Flipped normal.\n");
194 } else if (dot_prod_orientation == 0.0 && normal_magnitude > 1e-12) {
195 // This case is ambiguous or face plane contains cell centroid.
196 // This might happen for highly symmetric cells or if face_centroid IS cell_centroid (e.g. 2D cell).
197 // For now, we keep the original normal direction based on v1,v2,v4 ordering.
198 // A more robust solution for this edge case might be needed if it occurs often.
199 ierr = MPI_Comm_rank(MPI_COMM_WORLD, &rank); CHKERRQ(ierr);
200 LOG_ALLOW(LOCAL, LOG_WARNING, "Rank %d: Dot product for normal orientation is zero. Normal direction might be ambiguous. Keeping initial normal direction from (v2-v1)x(v4-v1).\n", rank);
201 }
202 LOG_ALLOW(LOCAL, LOG_VERBOSE, " Oriented Raw Normal: (%.6e, %.6e, %.6e)\n", normal_x, normal_y, normal_z);
203
204
205 // --- Normalize the (Now Outward-Pointing) Normal Vector ---
206 // Note: normal_magnitude was calculated from initial normal.
207 // If we flipped, magnitude is the same.
208 normal_x /= normal_magnitude;
209 normal_y /= normal_magnitude;
210 normal_z /= normal_magnitude;
211 LOG_ALLOW(LOCAL, LOG_VERBOSE, " Normalized Outward Normal: (%.6f, %.6f, %.6f)\n", normal_x, normal_y, normal_z);
212
213
214 // --- Compute Vector from Target Point to Face Centroid ---
215 PetscReal vec_p_to_fc_x = face_centroid_x - p_target.x;
216 PetscReal vec_p_to_fc_y = face_centroid_y - p_target.y;
217 PetscReal vec_p_to_fc_z = face_centroid_z - p_target.z;
218
219 // --- Compute Signed Distance ---
220 *d_signed = vec_p_to_fc_x * normal_x +
221 vec_p_to_fc_y * normal_y +
222 vec_p_to_fc_z * normal_z;
223
224 LOG_ALLOW(LOCAL, LOG_VERBOSE, " Raw Signed Distance (using outward normal): %.15e\n", *d_signed);
225
226 // --- Apply Threshold ---
227 if (fabs(*d_signed) < threshold) {
228 LOG_ALLOW(LOCAL, LOG_VERBOSE, " Distance %.15e is less than threshold %.1e. Setting to 0.0.\n", *d_signed, threshold);
229 *d_signed = 0.0;
230 }
231
232 LOG_ALLOW(LOCAL, LOG_VERBOSE, "Final Signed Distance: %.15e\n", *d_signed);
233
235 PetscFunctionReturn(0);
236}
237
238
239#undef __FUNCT__
240#define __FUNCT__ "CalculateDistancesToCellFaces"
241/**
242 * @brief Computes the signed distances from a point to each face of a cubic cell.
243 *
244 * This function calculates the signed distances from a specified point `p` to each of the six
245 * faces of a cubic cell. The cell is defined by its eight vertices, and the distances are
246 * stored in the memory location pointed to by `d`. Each distance corresponds to a specific
247 * face of the cell, as enumerated by the `Face` enumeration. A threshold value is used to
248 * determine when a distance should be considered effectively zero, accounting for floating-point
249 * precision limitations.
250 *
251 * @param[in] p The target point in 3D space for which distances to the cell's faces
252 * are to be calculated. Represented by the `Cmpnts` structure.
253 * @param[in] cell A pointer to a `Cell` structure that defines the cubic cell via its
254 * eight vertices. The vertices must be ordered consistently to
255 * ensure correct normal directions for each face.
256 * @param[out] d A pointer to an array of six `PetscReal` values where the computed
257 * signed distances will be stored. Each index in the array corresponds
258 * to a specific face as defined by the `Face` enumeration:
259 * - d[LEFT]: Distance to the Left Face of the cell.
260 * - d[RIGHT]: Distance to the Right Face of the cell.
261 * - d[BOTTOM]: Distance to the Bottom Face of the cell.
262 * - d[TOP]: Distance to the Top Face of the cell.
263 * - d[FRONT]: Distance to the Front Face of the cell.
264 * - d[BACK]: Distance to the Back Face of the cell.
265 * @param[in] threshold A `PetscReal` value that specifies the minimum distance below which
266 * a computed distance is considered to be zero. This helps in
267 * mitigating issues arising from floating-point arithmetic inaccuracies.
268 *
269 * @return PetscErrorCode Returns 0 if the function executes successfully. If an error occurs,
270 * a non-zero error code is returned, indicating the type of failure.
271 *
272 * @note
273 * - The vertices of the cell must be ordered in a counter-clockwise manner when viewed from
274 * outside the cell. This ordering ensures that the normal vectors computed for each face
275 * point outward, which is essential for correctly determining the sign of the distances.
276 * - All four vertices defining a face must lie on a non-degenerate (i.e., non-flat) plane.
277 * Degenerate planes can lead to undefined normal vectors and incorrect distance calculations.
278 * - The `threshold` parameter provides flexibility in handling cases where the point `p` is
279 * extremely close to a face, allowing the user to define what constitutes "close enough" to
280 * be treated as zero distance based on the specific requirements of their application.
281 */
282PetscErrorCode CalculateDistancesToCellFaces(const Cmpnts p, const Cell *cell, PetscReal *d, const PetscReal threshold)
283{
284 PetscErrorCode ierr;
285 PetscFunctionBeginUser;
287
288 // Validate that the 'cell' pointer is not NULL to prevent dereferencing a null pointer.
289 if (cell == NULL) {
290 LOG_ALLOW(LOCAL,LOG_ERROR, "'cell' is NULL.\n");
291 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "'cell' is NULL.");
292 }
293
294 // Validate that the 'd' pointer is not NULL to ensure there is memory allocated for distance storage.
295 if (d == NULL) {
296 LOG_ALLOW(LOCAL,LOG_ERROR, "'d' is NULL.\n");
297 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "'d' is NULL.");
298 }
299
300 // Compute the centroid of the entire cell
301 Cmpnts cell_centroid = {0.0, 0.0, 0.0};
302 for (int i = 0; i < 8; ++i) {
303 cell_centroid.x += cell->vertices[i].x;
304 cell_centroid.y += cell->vertices[i].y;
305 cell_centroid.z += cell->vertices[i].z;
306 }
307 cell_centroid.x /= 8.0;
308 cell_centroid.y /= 8.0;
309 cell_centroid.z /= 8.0;
310
311 LOG_ALLOW(LOCAL,LOG_DEBUG, "Cell Centroid: (%.6e, %.6e, %.6e)\n",
312 cell_centroid.x, cell_centroid.y, cell_centroid.z);
313
314
315 // Compute the signed distance from point 'p' to the BACK face of the cell.
316 // The BACK face is defined by vertices 0, 3, 2, and 1, with its normal vector pointing in the -z direction.
318 cell->vertices[0], // Vertex 0
319 cell->vertices[3], // Vertex 3
320 cell->vertices[2], // Vertex 2
321 cell->vertices[1], // Vertex 1
322 cell_centroid, // Cell centroid
323 p, // Target point
324 &d[BACK], // Storage location for the BACK face distance
325 threshold // Threshold for zero distance
326 ); CHKERRQ(ierr);
327
328 // Compute the signed distance from point 'p' to the FRONT face of the cell.
329 // The FRONT face is defined by vertices 4, 7, 6, and 5, with its normal vector pointing in the +z direction.
331 cell->vertices[4], // Vertex 4
332 cell->vertices[7], // Vertex 7
333 cell->vertices[6], // Vertex 6
334 cell->vertices[5], // Vertex 5
335 cell_centroid, // Cell centroid
336 p, // Target point
337 &d[FRONT], // Storage location for the FRONT face distance
338 threshold // Threshold for zero distance
339 ); CHKERRQ(ierr);
340
341 // Compute the signed distance from point 'p' to the BOTTOM face of the cell.
342 // The BOTTOM face is defined by vertices 0, 1, 6, and 7, with its normal vector pointing in the -y direction.
344 cell->vertices[0], // Vertex 0
345 cell->vertices[1], // Vertex 1
346 cell->vertices[6], // Vertex 6
347 cell->vertices[7], // Vertex 7
348 cell_centroid, // Cell centroid
349 p, // Target point
350 &d[BOTTOM], // Storage location for the BOTTOM face distance
351 threshold // Threshold for zero distance
352 ); CHKERRQ(ierr);
353
354 // Compute the signed distance from point 'p' to the TOP face of the cell.
355 // The TOP face is defined by vertices 3, 4, 5, and 2, with its normal vector pointing in the +y direction.
357 cell->vertices[3], // Vertex 3
358 cell->vertices[4], // Vertex 4
359 cell->vertices[5], // Vertex 5
360 cell->vertices[2], // Vertex 2
361 cell_centroid, // Cell centroid
362 p, // Target point
363 &d[TOP], // Storage location for the TOP face distance
364 threshold // Threshold for zero distance
365 ); CHKERRQ(ierr);
366
367 // Compute the signed distance from point 'p' to the LEFT face of the cell.
368 // The LEFT face is defined by vertices 0, 7, 4, and 3, with its normal vector pointing in the -x direction.
370 cell->vertices[0], // Vertex 0
371 cell->vertices[7], // Vertex 7
372 cell->vertices[4], // Vertex 4
373 cell->vertices[3], // Vertex 3
374 cell_centroid, // Cell centroid
375 p, // Target point
376 &d[LEFT], // Storage location for the LEFT face distance
377 threshold // Threshold for zero distance
378 ); CHKERRQ(ierr);
379
380 // Compute the signed distance from point 'p' to the RIGHT face of the cell.
381 // The RIGHT face is defined by vertices 1, 2, 5, and 6, with its normal vector pointing in the +x direction.
383 cell->vertices[1], // Vertex 1
384 cell->vertices[2], // Vertex 2
385 cell->vertices[5], // Vertex 5
386 cell->vertices[6], // Vertex 6
387 cell_centroid, // Cell centroid
388 p, // Target point
389 &d[RIGHT], // Storage location for the RIGHT face distance
390 threshold // Threshold for zero distance
391 ); CHKERRQ(ierr);
392
393 LOG_ALLOW(LOCAL, LOG_DEBUG, "Populated d: "
394 "d[LEFT=%d]=%.3e, d[RIGHT=%d]=%.3e, d[BOTTOM=%d]=%.3e, "
395 "d[TOP=%d]=%.3e, d[FRONT=%d]=%.3e, d[BACK=%d]=%.3e\n",
396 LEFT, d[LEFT], RIGHT, d[RIGHT], BOTTOM, d[BOTTOM],
397 TOP, d[TOP], FRONT, d[FRONT], BACK, d[BACK]);
398 LOG_ALLOW(LOCAL, LOG_DEBUG, "Raw d: "
399 "[%.3e, %.3e, %.3e, %.3e, %.3e, %.3e]\n",
400 d[0], d[1], d[2], d[3], d[4], d[5]);
401
403 PetscFunctionReturn(0); // Indicate successful execution of the function.
404}
405
406#undef __FUNCT__
407#define __FUNCT__ "DeterminePointPosition"
408/**
409 * @brief Classifies a point based on precomputed face distances.
410 *
411 * Given an array of six distances d[NUM_FACES] from the point to each face
412 * of a hexahedral cell, this function determines:
413 * 0 => inside
414 * 1 => on a single face
415 * 2 => on an edge (2 faces = 0)
416 * 3 => on a corner (3+ faces = 0)
417 * -1 => outside
418 *
419 * @param[in] d Array of six face distances.
420 * @param[out] result Pointer to an integer classification: {0,1,2,3,-1}.
421 *
422 * @return PetscErrorCode Returns 0 on success, non-zero on failure.
423 */
424PetscErrorCode DeterminePointPosition(PetscReal *d, PetscInt *result)
425{
426
427 PetscFunctionBeginUser;
429 // Validate input pointers
430 if (d == NULL) {
431 LOG_ALLOW(LOCAL,LOG_ERROR, "'d' is NULL.\n");
432 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Input parameter 'd' is NULL.");
433 }
434 if (result == NULL) {
435 LOG_ALLOW(LOCAL,LOG_ERROR, "'result' is NULL.\n");
436 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Output parameter 'result' is NULL.");
437 }
438
439 // Initialize flags
440 PetscBool isInside = PETSC_TRUE;
441 PetscBool isOnBoundary = PETSC_FALSE;
442 PetscInt IntersectionCount = 0; // Counts the number of intersections of the point with various planes of the cell.
443
444 // Analyze distances to determine position
445 for(int i = 0; i < NUM_FACES; i++) {
446 if(d[i] < 0.0) {
447 isInside = PETSC_FALSE; // Point is outside in at least one direction
448 }
449 if(d[i] == 0.0) {
450 isOnBoundary = PETSC_TRUE; // Point is exactly at least one face
451 IntersectionCount++;
452 }
453 }
454
455 // Set the result based on flags
456 if(isInside) {
457 *result = 0; // Inside the cell
458 LOG_ALLOW(LOCAL,LOG_VERBOSE, "Particle is inside the cell.\n");
459 }
460 else if(isOnBoundary) {
461 if(IntersectionCount == 1){
462 *result = 1; // on face
463 LOG_ALLOW(LOCAL,LOG_VERBOSE, "Particle is on a face of the cell.\n");
464 }
465 else if(IntersectionCount == 2){
466 *result = 2; // on edge
467 LOG_ALLOW(LOCAL,LOG_VERBOSE, "Particle is on an edge of the cell.\n");
468 }
469 else if(IntersectionCount >= 3){
470 *result = 3; // on corner
471 LOG_ALLOW(LOCAL,LOG_VERBOSE, "Particle is on a corner of the cell.\n");
472 }
473 }
474 else {
475 *result = -1; // Outside the cell
476 LOG_ALLOW(LOCAL,LOG_VERBOSE, "Particle is outside the cell.\n");
477 }
478
480 PetscFunctionReturn(0); // Indicate successful execution
481}
482
483#undef __FUNCT__
484#define __FUNCT__ "GetCellVerticesFromGrid"
485/**
486 * @brief Retrieves the coordinates of the eight vertices of a cell based on grid indices.
487 *
488 * This function populates the `cell` structure with the coordinates of the eight vertices
489 * of a hexahedral cell given its grid indices `(idx, idy, idz)`. The vertex numbering follows
490 * the convention depicted in the provided figure:
491 *
492 * ```
493 * (i,j+1,k)
494 * 3---------------------------2(i+1,j+1,k)
495 * /| /|
496 * / | / |
497 * / | / |
498 * / | / |
499 * / | / |
500 * / | / |
501 * / | / |
502 * 4-------|------------------5 |
503 * | | | |
504 * |(i,j+1,k+1) | |
505 * | | | |
506 * | 0-------------------|-------1 (i+1,j,k)
507 * | / | /
508 * | / | /
509 * | / | /
510 * | / | /
511 * | / | /
512 * | / | /
513 * | / |/
514 * 7---------------------------6
515 * (i,j,k+1) (i+1,j,k+1)
516 * ```
517 *
518 * **Vertex Numbering and Positions:**
519 * - **Vertex 0**: `(i, j, k)` → `cell->vertices[0]`
520 * - **Vertex 1**: `(i+1, j, k)` → `cell->vertices[1]`
521 * - **Vertex 2**: `(i+1, j+1, k)` → `cell->vertices[2]`
522 * - **Vertex 3**: `(i, j+1, k)` → `cell->vertices[3]`
523 * - **Vertex 4**: `(i, j+1, k+1)` → `cell->vertices[4]`
524 * - **Vertex 5**: `(i+1, j+1, k+1)` → `cell->vertices[5]`
525 * - **Vertex 6**: `(i+1, j, k+1)` → `cell->vertices[6]`
526 * - **Vertex 7**: `(i, j, k+1)` → `cell->vertices[7]`
527 *
528 * @param[in] coor Pointer to a 3D array of `Cmpnts` structures representing the grid coordinates.
529 * @param[in] idx The i-index of the cell.
530 * @param[in] idy The j-index of the cell.
531 * @param[in] idz The k-index of the cell.
532 * @param[out] cell Pointer to a `Cell` structure to store the cell's vertices.
533 *
534 * @return PetscErrorCode Returns 0 to indicate successful execution. Non-zero on failure.
535 *
536 * @note
537 * - It is assumed that the grid indices `(idx, idy, idz)` are within valid bounds.
538 * - The `coor` array should be properly initialized with accurate coordinates for all grid points.
539 * - The function assumes unit spacing between grid points. Modify the coordinate assignments if your grid spacing differs.
540 * - The boundary checks have been removed as they are performed before calling this function.
541 */
542PetscErrorCode GetCellVerticesFromGrid(Cmpnts ***coor, PetscInt idx, PetscInt idy, PetscInt idz,
543 Cell *cell)
544{
545
546 PetscFunctionBeginUser;
548 // Validate input pointers
549 if (coor == NULL) {
550 LOG_ALLOW(LOCAL,LOG_ERROR, "'coor' is NULL.\n");
551 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Input array 'coor' is NULL.");
552 }
553 if (cell == NULL) {
554 LOG_ALLOW(LOCAL,LOG_ERROR, "'cell' is NULL.\n");
555 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Output parameter 'cell' is NULL.");
556 }
557
558 // Assign vertices based on grid indices matching the figure
559 // Vertex numbering follows the convention depicted in the figure
560 cell->vertices[0] = coor[idz][idy][idx]; // Vertex 0: (i, j, k)
561 cell->vertices[1] = coor[idz][idy][idx+1]; // Vertex 1: (i+1, j, k)
562 cell->vertices[2] = coor[idz][idy+1][idx+1]; // Vertex 2: (i+1, j+1, k)
563 cell->vertices[3] = coor[idz][idy+1][idx]; // Vertex 3: (i, j+1, k)
564 cell->vertices[4] = coor[idz+1][idy+1][idx]; // Vertex 4: (i, j+1, k+1)
565 cell->vertices[5] = coor[idz+1][idy+1][idx+1]; // Vertex 5: (i+1, j+1, k+1)
566 cell->vertices[6] = coor[idz+1][idy][idx+1]; // Vertex 6: (i+1, j, k+1)
567 cell->vertices[7] = coor[idz+1][idy][idx]; // Vertex 7: (i, j, k+1)
568
569 LOG_ALLOW(LOCAL,LOG_VERBOSE, "Retrieved vertices for cell (%d, %d, %d).\n", idx, idy, idz);
570
572 PetscFunctionReturn(0); // Indicate successful execution
573}
574
575#undef __FUNCT__
576#define __FUNCT__ "InitializeTraversalParameters"
577/**
578 * @brief Initializes traversal parameters for locating a particle.
579 *
580 * This function sets the initial cell indices for the grid search.
581 * - If the particle has valid previous cell indices (not -1,-1,-1), the search
582 * starts from that previous cell.
583 * - Otherwise (e.g., first time locating the particle), the search starts
584 * from the beginning corner (xs, ys, zs) of the local grid domain owned
585 * by the current process.
586 * It also initializes the traversal step counter.
587 *
588 * @param[in] user Pointer to the user-defined context containing grid information.
589 * @param[in] particle Pointer to the Particle structure containing its location and previous cell (if any).
590 * @param[out] idx Pointer to store the initial i-index of the cell.
591 * @param[out] idy Pointer to store the initial j-index of the cell.
592 * @param[out] idz Pointer to store the initial k-index of the cell.
593 * @param[out] traversal_steps Pointer to store the initial traversal step count.
594 *
595 * @return PetscErrorCode Returns 0 on success, non-zero on failure.
596 */
597PetscErrorCode InitializeTraversalParameters(UserCtx *user, Particle *particle, PetscInt *idx, PetscInt *idy, PetscInt *idz, PetscInt *traversal_steps)
598{
599 PetscErrorCode ierr;
600 DMDALocalInfo info;
601
602 PetscFunctionBeginUser;
604
605 // Validate input pointers
606 if (user == NULL || particle == NULL || idx == NULL || idy == NULL || idz == NULL || traversal_steps == NULL) {
607 LOG_ALLOW(LOCAL,LOG_ERROR, "One or more input pointers are NULL.\n");
608 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "One or more input pointers are NULL.");
609 }
610
611 // Get grid information (needed for fallback start and potentially validation)
612 ierr = DMDAGetLocalInfo(user->da, &info); CHKERRQ(ierr);
613
614 // --- Check if the particle has a valid previous cell ID ---
615 // Assuming particle->cell stores the *global* indices
616 if (particle->cell[0] >= 0 && particle->cell[1] >= 0 && particle->cell[2] >= 0) {
617
618 // It sees a valid cell ID exists.
619 // Before using it, it explicitly checks if this cell is accessible.
620 PetscBool is_handoff_cell_valid;
621 ierr = CheckCellWithinLocalGrid(user, particle->cell[0], particle->cell[1], particle->cell[2], &is_handoff_cell_valid); CHKERRQ(ierr);
622
623 if (is_handoff_cell_valid) {
624 // FAST PATH: The check passed. The cell is safe. Use it.
625 *idx = particle->cell[0];
626 *idy = particle->cell[1];
627 *idz = particle->cell[2];
628
629 LOG_ALLOW(LOCAL,LOG_DEBUG, "Particle %lld has previous cell (%d, %d, %d). Starting search there.\n",
630 (long long)particle->PID, *idx, *idy, *idz); // Cast id to long long for printing PetscInt64
631
632 } else {
633 // SAFE FALLBACK: The check failed! The handoff cell is NOT safe.
634 // Discard the unsafe handoff cell and revert to starting at the
635 // guaranteed-safe local corner.
636 *idx = info.xs;
637 *idy = info.ys;
638 *idz = info.zs;
639 }
640 } else {
641 // No valid previous cell ID (e.g., -1,-1,-1 or first time).
642 // Start from the first cell in the local owned grid domain.
643 *idx = info.xs;
644 *idy = info.ys;
645 *idz = info.zs;
646 LOG_ALLOW(LOCAL,LOG_DEBUG, "Particle %lld has no valid previous cell. Starting search at local corner (%d, %d, %d).\n",
647 (long long)particle->PID, *idx, *idy, *idz);
648 }
649
650 // Initialize traversal step counter
651 *traversal_steps = 0;
652
653 // Log the chosen starting point
654 LOG_ALLOW(LOCAL,LOG_INFO, "Traversal for particle %lld initialized to start at cell (%d, %d, %d).\n",
655 (long long)particle->PID, *idx, *idy, *idz);
656
657 PetscFunctionReturn(0);
658}
659
660
661#undef __FUNCT__
662#define __FUNCT__ "CheckCellWithinLocalGrid"
663/**
664 * @brief Checks if the current GLOBAL CELL indices are within the LOCAL GHOSTED grid boundaries
665 * accessible by this MPI process.
666 *
667 * This function determines if the provided global cell indices (idx, idy, idz) fall within the
668 * range of cells covered by the current process's owned and ghost NODES.
669 * A cell C(i,j,k) (origin N(i,j,k)) is considered within this ghosted region if its origin node N(i,j,k)
670 * is within the rank's ghosted nodal region, AND node N(i+1,j+1,k+1) is also within it (to ensure
671 * the entire cell extent is covered by available node data). More simply, we check if the cell's
672 * origin node is within the range of nodes that can form the start of a ghosted cell.
673 *
674 * @param[in] user Pointer to the user-defined context (needs user->fda for node info).
675 * @param[in] idx The global i-index of the cell's origin node.
676 * @param[in] idy The global j-index of the cell's origin node.
677 * @param[in] idz The global k-index of the cell's origin node.
678 * @param[out] is_within Pointer to a PetscBool that will be set to PETSC_TRUE if within ghosted bounds, else PETSC_FALSE.
679 *
680 * @return PetscErrorCode Returns 0 on success, non-zero on failure.
681 */
682PetscErrorCode CheckCellWithinLocalGrid(UserCtx *user, PetscInt idx, PetscInt idy, PetscInt idz, PetscBool *is_within)
683{
684 PetscErrorCode ierr;
685 DMDALocalInfo info_nodes; // Node information from the DMDA that defines ghost regions (user->fda)
686
687 PetscFunctionBeginUser; // Assuming this is part of your PETSc style
689
690 // Validate inputs
691 if (user == NULL || is_within == NULL) {
692 LOG_ALLOW(LOCAL, LOG_ERROR, "Input pointer is NULL.\n");
693 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Input pointer is NULL in CheckCellWithinLocalGrid.");
694 }
695 if (user->fda == NULL) {
696 LOG_ALLOW(LOCAL, LOG_ERROR, "user->fda is NULL.Cannot get ghost info.\n");
697 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "user->fda is NULL. Cannot get ghost info.");
698 }
699
700 // Get node info from user->fda (this DMDA has the ghost layer information for nodes)
701 ierr = DMDAGetLocalInfo(user->fda, &info_nodes); CHKERRQ(ierr);
702
703 // Determine the range of GLOBAL CELL INDICES that are covered by this rank's ghosted NODAL region.
704 // A cell C(i,j,k) has origin node N(i,j,k).
705 // The ghosted nodal region starts at global node index info_nodes.gxs and has info_nodes.gxm nodes.
706
707 // Global starting index of the first cell whose origin node is within the ghosted nodal region.
708 PetscInt gxs_cell_global_start = info_nodes.gxs;
709 PetscInt gys_cell_global_start = info_nodes.gys;
710 PetscInt gzs_cell_global_start = info_nodes.gzs;
711
712 // Number of cells that can be formed starting from nodes within the ghosted nodal region.
713 // If there are N ghosted nodes (info_nodes.gxm), they can be origins for N-1 cells.
714 PetscInt gxm_cell_local_count = (info_nodes.gxm > 0) ? info_nodes.gxm - 1 : 0;
715 PetscInt gym_cell_local_count = (info_nodes.gym > 0) ? info_nodes.gym - 1 : 0;
716 PetscInt gzm_cell_local_count = (info_nodes.gzm > 0) ? info_nodes.gzm - 1 : 0;
717
718 // Global exclusive end index for cells whose origins are in the ghosted nodal region.
719 PetscInt gxe_cell_global_end_exclusive = gxs_cell_global_start + gxm_cell_local_count;
720 PetscInt gye_cell_global_end_exclusive = gys_cell_global_start + gym_cell_local_count;
721 PetscInt gze_cell_global_end_exclusive = gzs_cell_global_start + gzm_cell_local_count;
722
723 // Check if the given global cell index (idx, idy, idz) falls within this range.
724 // This means the origin node of cell (idx,idy,idz) is within the rank's accessible ghosted node region,
725 // and that node can indeed serve as a cell origin (i.e., it's not the very last node in the ghosted region).
726 if (idx >= gxs_cell_global_start && idx < gxe_cell_global_end_exclusive &&
727 idy >= gys_cell_global_start && idy < gye_cell_global_end_exclusive &&
728 idz >= gzs_cell_global_start && idz < gze_cell_global_end_exclusive) {
729 *is_within = PETSC_TRUE;
730 } else {
731 *is_within = PETSC_FALSE;
732 }
733
734 LOG_ALLOW(LOCAL, LOG_DEBUG, "Cell (origin node glob idx) (%d, %d, %d) is %s the ghosted local grid (covered cell origins x:[%d..%d), y:[%d..%d), z:[%d..%d)).\n",
735 idx, idy, idz, (*is_within) ? "within" : "outside",
736 gxs_cell_global_start, gxe_cell_global_end_exclusive,
737 gys_cell_global_start, gye_cell_global_end_exclusive,
738 gzs_cell_global_start, gze_cell_global_end_exclusive);
739
741 PetscFunctionReturn(0);
742}
743
744#undef __FUNCT__
745#define __FUNCT__ "RetrieveCurrentCell"
746/**
747 * @brief Retrieves the coordinates of the eight vertices of the current cell.
748 *
749 * @param[in] user Pointer to the user-defined context containing grid information.
750 * @param[in] idx The i-index of the current cell.
751 * @param[in] idy The j-index of the current cell.
752 * @param[in] idz The k-index of the current cell.
753 * @param[out] cell Pointer to a Cell structure to store the cell's vertices.
754 *
755 * @return PetscErrorCode Returns 0 on success, non-zero on failure.
756 */
757PetscErrorCode RetrieveCurrentCell(UserCtx *user, PetscInt idx, PetscInt idy, PetscInt idz, Cell *cell)
758{
759 PetscErrorCode ierr;
760 Vec Coor;
761 Cmpnts ***coor_array;
762 PetscMPIInt rank;
763 DMDALocalInfo info_nodes;
764
765 PetscFunctionBeginUser;
767
768 // Validate input pointers
769 if (user == NULL || cell == NULL) {
770 LOG_ALLOW(LOCAL,LOG_ERROR, "One or more input pointers are NULL.\n");
771 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "One or more input pointers are NULL.");
772 }
773
774 // Get local coordinates
775 ierr = DMGetCoordinatesLocal(user->da, &Coor); CHKERRQ(ierr);
776 ierr = DMDAVecGetArrayRead(user->fda, Coor, &coor_array); CHKERRQ(ierr);
777
778 // Get the local grid information FOR THE GHOSTED ARRAY from user->fda
779 // This info contains the mapping between global and local indices.
780 ierr = DMDAGetLocalInfo(user->fda, &info_nodes); CHKERRQ(ierr);
781
782 PetscInt idx_local = idx; // - info_nodes.gxs;
783 PetscInt idy_local = idy; // - info_nodes.gys;
784 PetscInt idz_local = idz; // - info_nodes.gzs;
785
786 LOG_ALLOW(LOCAL,LOG_VERBOSE," [Rank %d] Getting vertex coordinates for cell: %d,%d,%d whose local coordinates are %d,%d,%d.\n",rank,idx,idy,idz,idx_local,idy_local,idz_local);
787
788 // Get the current cell's vertices
789 ierr = GetCellVerticesFromGrid(coor_array, idx_local, idy_local, idz_local, cell); CHKERRQ(ierr);
790
791 // Restore array
792 ierr = DMDAVecRestoreArrayRead(user->fda, Coor, &coor_array); CHKERRQ(ierr);
793
794 // Get MPI rank for debugging
795 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
796
797 // Debug: Print cell vertices
798 LOG_ALLOW(LOCAL,LOG_VERBOSE, "Cell (%d, %d, %d) vertices \n", idx, idy, idz);
799 ierr = LOG_CELL_VERTICES(cell, rank); CHKERRQ(ierr);
800
802 PetscFunctionReturn(0);
803}
804
805#undef __FUNCT__
806#define __FUNCT__ "EvaluateParticlePosition"
807/**
808 * @brief Determines the spatial relationship of a particle relative to a cubic cell.
809 *
810 * This function evaluates whether a particle located at a specific point `p` in 3D space
811 * is positioned inside the cell, on the boundary of the cell, or outside the cell. The
812 * determination is based on the signed distances from the particle to each of the six
813 * faces of the cell. The function utilizes the computed distances to ascertain the particle's
814 * position with respect to the cell boundaries, considering a threshold to account for
815 * floating-point precision.
816 *
817 * @param[in] cell A pointer to a `Cell` structure that defines the cubic cell via its
818 * vertices. The cell's geometry is essential for accurately computing
819 * the distances to each face.
820 * @param[in] d A pointer to an array of six `PetscReal` values that store the
821 * signed distances from the particle to each face of the cell. These
822 * distances are typically computed using the `CalculateDistancesToCellFaces`
823 * function.
824 * @param[in] p The location of the particle in 3D space, represented by the `Cmpnts`
825 * structure. This point is the reference for distance calculations to the
826 * cell's faces.
827 * @param[out] position A pointer to an integer that will be set based on the particle's position
828 * relative to the cell:
829 * - `0`: The particle is inside the cell.
830 * - `1`: The particle is on the boundary of the cell.
831 * - `-1`: The particle is outside the cell.
832 * @param[in] threshold A `PetscReal` value that defines the minimum distance below which a
833 * computed distance is considered to be zero. This threshold helps in
834 * mitigating inaccuracies due to floating-point arithmetic, especially
835 * when determining if the particle lies exactly on the boundary.
836 *
837 * @return PetscErrorCode Returns `0` if the function executes successfully. If an error occurs,
838 * a non-zero error code is returned, indicating the type of failure.
839 *
840 * @note
841 * - It is assumed that the `d` array has been properly allocated and contains valid distance
842 * measurements before calling this function.
843 * - The function relies on `CalculateDistancesToCellFaces` to accurately compute the signed
844 * distances to each face. Any inaccuracies in distance calculations can affect the
845 * determination of the particle's position.
846 * - The `threshold` parameter should be chosen based on the specific precision requirements
847 * of the application to balance between sensitivity and robustness against floating-point
848 * errors.
849 * - The function includes a debug statement that prints the face distances, which can be useful
850 * for verifying the correctness of distance computations during development or troubleshooting.
851 */
852PetscErrorCode EvaluateParticlePosition(const Cell *cell, PetscReal *d, const Cmpnts p, PetscInt *position, const PetscReal threshold)
853{
854 PetscErrorCode ierr;
855 PetscReal cellSize;
856 PetscReal cellThreshold;
857 PetscFunctionBeginUser;
859
860 // Validate input pointers to ensure they are not NULL, preventing potential segmentation faults.
861 if (cell == NULL || position == NULL) {
862 LOG_ALLOW(LOCAL,LOG_ERROR, "One or more input pointers are NULL.\n");
863 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "One or more input pointers are NULL.");
864 }
865
866 // Compute a local cell size
867 ierr = GetCellCharacteristicSize(cell, &cellSize); CHKERRQ(ierr);
868
869 // scale base threshold by cell size (ex: if threshold = 1e-6 and cell size = 0.01, cellThreshold = 1e-8)
870 cellThreshold = threshold*cellSize;
871
872 // Invoke the function to calculate signed distances from the particle to each face of the cell.
873 // The distances are stored in the array pointed to by 'd'.
874 ierr = CalculateDistancesToCellFaces(p, cell, d, cellThreshold); CHKERRQ(ierr);
875 CHKERRQ(ierr); // Check for errors in distance calculation.
876
877
878 // Catch degenerate-plane error manually:
879 if (ierr == PETSC_ERR_USER) {
881 "Skipping cell due to degenerate face.\n");
882 // We can set *position = -1 here
883 *position = -1; // treat as outside
884 return 0; // not a fatal error, just skip
885 } else {
886 CHKERRQ(ierr);
887 }
888
889
890 // Debugging output: Print the computed distances to each face for verification purposes.
891 LOG_ALLOW(LOCAL,LOG_DEBUG, "Face Distances:\n");
893 CHKERRQ(ierr); // Check for errors in printing distances.
894
895 // Determine the particle's position relative to the cell based on the computed distances.
896 // The function sets the value pointed to by 'position' accordingly:
897 // 0 for inside, 1 (2 or 3) for on the boundary, and -1 for outside.
898 ierr = DeterminePointPosition(d,position);
899 CHKERRQ(ierr); // Check for errors in position determination.
900
902 PetscFunctionReturn(0); // Indicate successful execution of the function.
903}
904
905#undef __FUNCT__
906#define __FUNCT__ "UpdateCellIndicesBasedOnDistances"
907/**
908 * @brief Updates the cell indices based on the signed distances to each face.
909 *
910 * This function modifies the cell indices (`idx`, `idy`, `idz`) to move towards the direction
911 * where the particle is likely to be located, based on positive distances indicating
912 * that the particle is outside in that particular direction.
913 *
914 * @param[in] d An array of six `PetscReal` values representing the signed distances to each face:
915 * - d[LEFT]: Left Face
916 * - d[RIGHT]: Right Face
917 * - d[BOTTOM]: Bottom Face
918 * - d[TOP]: Top Face
919 * - d[FRONT]: Front Face
920 * - d[BACK]: Back Face
921 * @param[out] idx Pointer to the i-index of the cell to be updated.
922 * @param[out] idy Pointer to the j-index of the cell to be updated.
923 * @param[out] idz Pointer to the k-index of the cell to be updated.
924 * @param[in] info DMDALocalInfo structure that holds local & global domain bounds.
925 *
926 * @return PetscErrorCode Returns 0 on success, non-zero on failure.
927 */
928PetscErrorCode UpdateCellIndicesBasedOnDistances( PetscReal d[NUM_FACES], PetscInt *idx, PetscInt *idy, PetscInt *idz)
929{
930
931 PetscFunctionBeginUser;
933 /*
934 PetscInt cxm,cxs; // maximum & minimum cell ID in x
935 PetscInt cym,cys; // maximum & minimum cell ID in y
936 PetscInt czm,czs; // maximum & minimum cell ID in z
937
938 cxs = info->xs; cxm = cxs + info->xm - 2;
939 cys = info->ys; cym = cys + info->ym - 2;
940 czs = info->zs; czm = czs + info->zm - 2;
941 */
942
943 // LOG_ALLOW(LOCAL, LOG_DEBUG, "Received d: "
944 // "d[LEFT=%d]=%.3e, d[RIGHT=%d]=%.3e, d[BOTTOM=%d]=%.3e, "
945 // "d[TOP=%d]=%.3e, d[FRONT=%d]=%.3e, d[BACK=%d]=%.3e\n",
946 // LEFT, d[LEFT], RIGHT, d[RIGHT], BOTTOM, d[BOTTOM],
947 // TOP, d[TOP], FRONT, d[FRONT], BACK, d[BACK]);
948 // LOG_ALLOW(LOCAL, LOG_DEBUG, "Raw d: "
949 // "[%.3e, %.3e, %.3e, %.3e, %.3e, %.3e]\n",
950 // d[0], d[1], d[2], d[3], d[4], d[5]);
951
952 // Validate input pointers
953 if (d == NULL) {
954 LOG_ALLOW(LOCAL,LOG_ERROR, "'d' is NULL.\n");
955 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Input array 'd' is NULL.");
956 }
957 if (idx == NULL || idy == NULL || idz == NULL) {
958 LOG_ALLOW(LOCAL,LOG_ERROR, "One or more index pointers are NULL.\n");
959 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "One or more index pointers are NULL.");
960 }
961
962 // Debug: Print current face distances
963 LOG_ALLOW(LOCAL,LOG_DEBUG, "Current Face Distances:\n");
965
966 // Update k-direction based on FRONT and BACK distances
967 if (d[FRONT] < 0.0) {
968 LOG_ALLOW(LOCAL,LOG_VERBOSE, "Condition met: d[FRONT] < 0.0, incrementing idz.\n");
969 (*idz) += 1;
970 }
971 else if(d[BACK] < 0.0){
972 LOG_ALLOW(LOCAL,LOG_VERBOSE, "Condition met: d[BACK] < 0.0, decrementing idz.\n");
973 (*idz) -= 1;
974 }
975
976 // Update i-direction based on LEFT and RIGHT distances
977 if (d[LEFT] < 0.0) {
978 LOG_ALLOW(LOCAL,LOG_VERBOSE, "Condition met: d[LEFT] < 0.0, decrementing idx.\n");
979 (*idx) -= 1;
980 }
981 else if (d[RIGHT] < 0.0) {
982 LOG_ALLOW(LOCAL,LOG_VERBOSE, "Condition met: d[RIGHT] < 0.0, incrementing idx.\n");
983 (*idx) += 1;
984 }
985
986 // Update j-direction based on BOTTOM and TOP distances
987 if (d[BOTTOM] < 0.0) {
988 LOG_ALLOW(LOCAL,LOG_VERBOSE, "Condition met: d[BOTTOM] < 0.0, decrementing idy.\n");
989 (*idy) -= 1;
990 }
991 else if (d[TOP] < 0.0) {
992 LOG_ALLOW(LOCAL,LOG_VERBOSE, "Condition met: d[TOP] < 0.0, incrementing idy.\n");
993 (*idy) += 1;
994 }
995
996 /*
997 // The 'cell' corners you can reference go from [xs .. xs+xm-1], but
998 // to form a valid cell in x, you need (idx+1) in range, so max is (xs+xm-2).
999 *idx = PetscMax(cxs, PetscMin(*idx, cxm));
1000 *idy = PetscMax(cys, PetscMin(*idy, cym));
1001 *idz = PetscMax(czs, PetscMin(*idz, czm));
1002 */
1003
1004 LOG_ALLOW(LOCAL,LOG_DEBUG, "Updated Indices - idx, idy, idz: %d, %d, %d\n", *idx, *idy, *idz);
1005
1007 PetscFunctionReturn(0); // Indicate successful execution
1008}
1009
1010
1011#undef __FUNCT__
1012#define __FUNCT__ "FinalizeTraversal"
1013/**
1014 * @brief Finalizes the traversal by reporting the results.
1015 *
1016 * This function prints the outcome of the traversal, indicating whether the particle
1017 * was found within a cell or not, and updates the particle's cell indices accordingly.
1018 *
1019 * @param[in] user Pointer to the user-defined context containing grid information.
1020 * @param[out] particle Pointer to the Particle structure to update with cell indices.
1021 * @param[in] traversal_steps The number of traversal steps taken.
1022 * @param[in] cell_found Flag indicating whether the particle was found within a cell.
1023 * @param[in] idx The i-index of the found cell.
1024 * @param[in] idy The j-index of the found cell.
1025 * @param[in] idz The k-index of the found cell.
1026 *
1027 * @return PetscErrorCode Returns 0 on success, non-zero on failure.
1028 */
1029PetscErrorCode FinalizeTraversal(UserCtx *user, Particle *particle, PetscInt traversal_steps, PetscBool cell_found, PetscInt idx, PetscInt idy, PetscInt idz)
1030{
1031 PetscFunctionBeginUser;
1032 // Validate input pointers
1033 if (user == NULL || particle == NULL) {
1034 LOG_ALLOW(LOCAL,LOG_ERROR, "One or more input pointers are NULL.\n");
1035 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "One or more input pointers are NULL.");
1036 }
1037
1038 if (cell_found) {
1039 LOG_ALLOW(LOCAL,LOG_INFO, "Particle located in cell (%d, %d, %d) after %d traversal steps.\n",
1040 idx, idy, idz, traversal_steps);
1041 }
1042 else {
1043 LOG_ALLOW(LOCAL,LOG_WARNING, "Particle could not be located within the grid after %d traversal steps.\n", (PetscInt)traversal_steps);
1044 particle->cell[0] = -1;
1045 particle->cell[1] = -1;
1046 particle->cell[2] = -1;
1047 }
1048
1049 LOG_ALLOW(LOCAL, LOG_INFO, "Completed final traversal sync across all ranks.\n");
1050
1051
1053 PetscFunctionReturn(0);
1054}
1055
1056#undef __FUNCT__
1057#define __FUNCT__ "FindOwnerOfCell"
1058/**
1059 * @brief Finds the MPI rank that owns a given global cell index.
1060 * @ingroup DomainInfo
1061 *
1062 * This function performs a linear search through the pre-computed decomposition map
1063 * (`user->RankCellInfoMap`) to determine which process is responsible for the cell
1064 * with global indices (i, j, k). It is the definitive method for resolving cell
1065 * ownership in the "Walk and Handoff" migration algorithm.
1066 *
1067 * If the provided indices are outside the range of any rank (e.g., negative or
1068 * beyond the global domain), the function will not find an owner and `owner_rank`
1069 * will be set to -1.
1070 *
1071 * @param[in] user Pointer to the UserCtx structure, which must contain the
1072 * initialized `RankCellInfoMap` and `num_ranks`.
1073 * @param[in] i Global i-index of the cell to find.
1074 * @param[in] j Global j-index of the cell to find.
1075 * @param[in] k Global k-index of the cell to find.
1076 * @param[out] owner_rank Pointer to a `PetscMPIInt` where the resulting owner rank will
1077 * be stored. It is set to -1 if no owner is found.
1078 *
1079 * @return PetscErrorCode 0 on success, or a non-zero PETSc error code on failure.
1080 */
1081PetscErrorCode FindOwnerOfCell(UserCtx *user, PetscInt i, PetscInt j, PetscInt k, PetscMPIInt *owner_rank)
1082{
1083 PetscErrorCode ierr;
1084 PetscMPIInt size;
1085
1086 PetscFunctionBeginUser;
1087
1089
1090 ierr = MPI_Comm_size(PETSC_COMM_WORLD,&size); CHKERRQ(ierr);
1091
1092 // --- 1. Input Validation ---
1093 if (!user || !user->RankCellInfoMap) {
1094 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "UserCtx or RankCellInfoMap is not initialized in FindOwnerOfCell.");
1095 }
1096 if (!owner_rank) {
1097 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Output pointer owner_rank is NULL in FindOwnerOfCell.");
1098 }
1099
1100 // --- 2. Linear Search through the Decomposition Map ---
1101 // Initialize to a "not found" state.
1102 *owner_rank = -1;
1103
1104 // Loop through the map, which contains the ownership info for every rank 'r'.
1105 for (PetscMPIInt r = 0; r < size; ++r) {
1106 const RankCellInfo *info = &user->RankCellInfoMap[r];
1107
1108 // A rank owns a cell if the cell's index is within its start (inclusive)
1109 // and end (exclusive) range for all three dimensions.
1110 if ((i >= info->xs_cell && i < info->xs_cell + info->xm_cell) &&
1111 (j >= info->ys_cell && j < info->ys_cell + info->ym_cell) &&
1112 (k >= info->zs_cell && k < info->zs_cell + info->zm_cell))
1113 {
1114 *owner_rank = r; // We found the owner.
1115 break; // The search is over, exit the loop.
1116 }
1117 }
1118
1119 // --- 3. Logging for Diagnostics ---
1120 // This is extremely useful for debugging particle migration issues.
1121 if (*owner_rank == -1) {
1122 LOG_ALLOW(LOCAL, LOG_DEBUG, "No owner found for global cell (%d, %d, %d). It is likely outside the domain.\n", i, j, k);
1123 } else {
1124 LOG_ALLOW(LOCAL, LOG_DEBUG, "Owner of cell (%d, %d, %d) is Rank %d.\n", i, j, k, *owner_rank);
1125 }
1126
1128 PetscFunctionReturn(0);
1129}
1130
1131#undef __FUNCT__
1132#define __FUNCT__ "LocateParticleOrFindMigrationTarget"
1133
1134/**
1135 * @brief Locates a particle's host cell or identifies its migration target using a robust walk search.
1136 * @ingroup ParticleLocation
1137 *
1138 * This is the core search engine. It starts from a guess cell and walks through
1139 * the grid. It returns a definitive, actionable status indicating the outcome:
1140 * - `ACTIVE_AND_LOCATED`: The particle was found in a cell on the current rank.
1141 * - `MIGRATING_OUT`: The particle was found to belong to another rank. `particle->destination_rank` is set.
1142 * - `LOST`: The search failed to find the particle within the global domain.
1143 *
1144 * This function is globally aware and can walk across MPI rank boundaries. It contains
1145 * robust checks for global domain boundaries and a tie-breaker for numerically "stuck"
1146 * particles on cell faces.
1147 *
1148 * @param[in] user Pointer to the UserCtx containing all grid and domain info.
1149 * @param[in,out] particle Pointer to the Particle struct. Its fields are updated based
1150 * on the search outcome.
1151 * @param[out] status_out The final, actionable status of the particle after the search.
1152 *
1153 * @return PetscErrorCode 0 on success, or a non-zero PETSc error code on failure.
1154 */
1156 Particle *particle,
1157 ParticleLocationStatus *status_out)
1158{
1159 PetscErrorCode ierr;
1160 PetscMPIInt rank;
1161
1162 // --- Search State Variables ---
1163 PetscInt idx, idy, idz; // Current search cell global indices
1164 PetscInt traversal_steps; // Counter to prevent infinite loops
1165 PetscBool search_concluded = PETSC_FALSE; // Flag to terminate the main while loop
1166
1167 // --- Oscillation/Stuck Loop Detection Variables ---
1168 PetscInt repeatedIndexCount = 0;
1169 PetscInt prevIdx = PETSC_MIN_INT, prevIdy = PETSC_MIN_INT, prevIdz = PETSC_MIN_INT;
1170 PetscInt last_position_result = -999;
1171
1172 PetscFunctionBeginUser;
1174 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
1175
1176 // --- 1. Initialize the Search ---
1177 ierr = InitializeTraversalParameters(user, particle, &idx, &idy, &idz, &traversal_steps); CHKERRQ(ierr);
1178
1179 LOG_ALLOW(LOCAL,LOG_VERBOSE, " The Threshold for considering a particle to be at a face is %.16f.\n",DISTANCE_THRESHOLD);
1180
1181 LOG_ALLOW(LOCAL,LOG_TRACE," [PID %lld]Traversal Initiated at : i = %d, j = %d, k = %d.\n",(long long)particle->PID,idx,idy,idz);
1182
1183 // --- 2. Main Walking Search Loop ---
1184 while (!search_concluded && traversal_steps < MAX_TRAVERSAL) {
1185 traversal_steps++;
1186
1187 // --- 2a. GLOBAL Domain Boundary Check ---
1188 if (idx < 0 || idx >= (user->IM - 1) || idy < 0 || idy >= (user->JM - 1) || idz < 0 || idz >= (user->KM - 1)) {
1189 LOG_ALLOW(LOCAL, LOG_WARNING, "[PID %lld]: Walked outside GLOBAL domain boundaries to invalid cell (%d,%d,%d). Search fails.\n",
1190 (long long)particle->PID, idx, idy, idz);
1191 idx = -1; // Invalidate the result to signal failure
1192 break; // Exit the loop immediately
1193 }
1194
1195 // --- 2b. LOCAL GHOST REGION CHECK (PREVENTS SEGV) ---
1196 // Before trying to access cell data, check if we have it.
1197 PetscBool is_cell_local;
1198 ierr = CheckCellWithinLocalGrid(user, idx, idy, idz, &is_cell_local); CHKERRQ(ierr);
1199 if (!is_cell_local) {
1200 // We have walked outside the local rank's ghost region.
1201 // This definitively means the particle belongs to another rank.
1202 // Conclude the search here; the current (idx,idy,idz) is the handoff cell.
1203 LOG_ALLOW(LOCAL, LOG_INFO, "[PID %lld]: Walked outside local ghost region to cell (%d,%d,%d). Concluding search for handoff.\n",
1204 (long long)particle->PID, idx, idy, idz);
1205 search_concluded = PETSC_TRUE;
1206 continue; // Skip the rest of the loop; proceed to finalization.
1207 }
1208
1209 // --- 2c. Stuck Loop Detection & Enhanced Tie-Breaker ---
1210 if (idx == prevIdx && idy == prevIdy && idz == prevIdz) {
1211 repeatedIndexCount++;
1212 if (repeatedIndexCount > REPEAT_COUNT_THRESHOLD) {
1213 // Only apply tie-breaker if we are stuck for the right reason (on a boundary)
1214 if (last_position_result >= 1) {
1215 LOG_ALLOW(LOCAL, LOG_WARNING, "[PID %lld]: Stuck on boundary of cell (%d,%d,%d) for %d steps. Applying enhanced tie-breaker.\n",
1216 (long long)particle->PID, idx, idy, idz, repeatedIndexCount);
1217
1218 // Re-evaluate at the stuck cell to get definitive weights
1219 Cell final_cell;
1220 PetscReal final_d[NUM_FACES];
1221 PetscInt final_position; // Dummy variable
1222
1223 ierr = RetrieveCurrentCell(user, idx, idy, idz, &final_cell); CHKERRQ(ierr);
1224 ierr = EvaluateParticlePosition(&final_cell, final_d, particle->loc, &final_position, DISTANCE_THRESHOLD);
1225
1226 if (ierr == 0) { // If evaluation succeeded
1227 ierr = UpdateParticleWeights(final_d, particle); CHKERRQ(ierr);
1228 search_concluded = PETSC_TRUE; // Conclude search, accepting this cell.
1229 } else { // Evaluation failed (e.g., degenerate cell)
1230 LOG_ALLOW(LOCAL, LOG_ERROR, "[PID %lld]: Tie-breaker failed during final evaluation at cell (%d,%d,%d). Search fails.\n",
1231 (long long)particle->PID, idx, idy, idz);
1232 idx = -1; // Invalidate result
1233 search_concluded = PETSC_TRUE;
1234 }
1235 } else { // Stuck for the wrong reason (not on a boundary)
1236 LOG_ALLOW(LOCAL, LOG_ERROR, "[PID %lld]: Search is stuck at cell (%d,%d,%d) but not on a boundary. This indicates a logic error. Failing search.\n",
1237 (long long)particle->PID, idx, idy, idz);
1238 idx = -1; // Invalidate result
1239 search_concluded = PETSC_TRUE;
1240 }
1241 if(search_concluded) continue;
1242 }
1243 } else {
1244 repeatedIndexCount = 0;
1245 }
1246 prevIdx = idx; prevIdy = idy; prevIdz = idz;
1247
1248 // --- 2d. Geometric Evaluation ---
1249 Cell current_cell;
1250 PetscReal distances[NUM_FACES];
1251 PetscInt position_in_cell;
1252
1253 ierr = RetrieveCurrentCell(user, idx, idy, idz, &current_cell); CHKERRQ(ierr);
1254 ierr = EvaluateParticlePosition(&current_cell, distances, particle->loc, &position_in_cell, DISTANCE_THRESHOLD); CHKERRQ(ierr);
1255 last_position_result = position_in_cell;
1256
1257 // --- 2e. Decision Making ---
1258 if (position_in_cell >= 0) { // Particle is INSIDE or ON THE BOUNDARY
1259 search_concluded = PETSC_TRUE;
1260 ierr = UpdateParticleWeights(distances, particle); CHKERRQ(ierr);
1261 } else { // Particle is OUTSIDE
1262 ierr = UpdateCellIndicesBasedOnDistances(distances, &idx, &idy, &idz); CHKERRQ(ierr);
1263 }
1264 }
1265
1266 // --- 3. Finalize and Determine Actionable Status ---
1267 if (idx == -1 || (!search_concluded && traversal_steps >= MAX_TRAVERSAL)) {
1268 if (idx != -1) {
1269 LOG_ALLOW(LOCAL, LOG_ERROR, "[PID %lld]: Search FAILED, exceeded MAX_TRAVERSAL limit of %d.\n",
1270 (long long)particle->PID, MAX_TRAVERSAL);
1271 }
1272 *status_out = LOST;
1273 particle->cell[0] = -1; particle->cell[1] = -1; particle->cell[2] = -1;
1274 } else {
1275 // Search succeeded in finding a candidate cell, now determine its owner.
1276 PetscMPIInt owner_rank;
1277 ierr = FindOwnerOfCell(user, idx, idy, idz, &owner_rank); CHKERRQ(ierr);
1278
1279 LOG_ALLOW(LOCAL,LOG_TRACE," [PID %ld] Owner rank : %d.\n",particle->PID,owner_rank);
1280
1281 // Always update the particle's cell index. It's a good guess for the receiving rank.
1282 particle->cell[0] = idx; particle->cell[1] = idy; particle->cell[2] = idz;
1283
1284 if (owner_rank == rank) {
1285 *status_out = ACTIVE_AND_LOCATED;
1286 } else if (owner_rank != -1) {
1287 // Particle belongs to another rank. Return the direct, actionable status.
1288 *status_out = MIGRATING_OUT;
1289 particle->destination_rank = owner_rank;
1290 } else { // Found a valid index, but no owner in the map.
1291 *status_out = LOST;
1292 particle->cell[0] = -1; particle->cell[1] = -1; particle->cell[2] = -1;
1293 }
1294 }
1295
1296 LOG_ALLOW(LOCAL,LOG_DEBUG,"[Rank %d][PID %ld] Search complete.\n",rank,particle->PID);
1297
1298 // --- 4. Report the Final Outcome ---
1299 ierr = ReportSearchOutcome(particle, *status_out, traversal_steps); CHKERRQ(ierr);
1300
1302 PetscFunctionReturn(0);
1303}
1304
1305
1306#undef __FUNCT__
1307#define __FUNCT__ "ReportSearchOutcome"
1308/**
1309 * @brief Logs the final outcome of the particle location search.
1310 */
1311PetscErrorCode ReportSearchOutcome(const Particle *particle,
1313 PetscInt traversal_steps)
1314{
1315 PetscFunctionBeginUser;
1317 switch (status) {
1318 case ACTIVE_AND_LOCATED:
1319 LOG_ALLOW(LOCAL, LOG_INFO, "Search SUCCESS [PID %lld]: Located in global cell (%d, %d, %d) after %d steps.\n",
1320 (long long)particle->PID, particle->cell[0], particle->cell[1], particle->cell[2], traversal_steps);
1321 break;
1322 case MIGRATING_OUT:
1323 LOG_ALLOW(LOCAL, LOG_INFO, "Search SUCCESS [PID %lld]: Identified for migration to Rank %d. Handoff cell is (%d, %d, %d) after %d steps.\n",
1324 (long long)particle->PID, particle->destination_rank, particle->cell[0], particle->cell[1], particle->cell[2], traversal_steps);
1325 break;
1326 case LOST:
1327 LOG_ALLOW(LOCAL, LOG_WARNING, "Search FAILED [PID %lld]: Particle is LOST after %d steps.\n",
1328 (long long)particle->PID, traversal_steps);
1329 break;
1330 default:
1331 LOG_ALLOW(LOCAL, LOG_WARNING, "Search ended with unexpected status %d for PID %lld.\n", status, (long long)particle->PID);
1332 break;
1333 }
1335 PetscFunctionReturn(0);
1336}
PetscErrorCode UpdateParticleWeights(PetscReal *d, Particle *particle)
Updates a particle's interpolation weights based on distances to cell faces.
Logging utilities and macros for PETSc-based applications.
PetscBool is_function_allowed(const char *functionName)
Checks if a given function is in the allow-list.
Definition logging.c:157
#define LOCAL
Logging scope definitions for controlling message output.
Definition logging.h:46
#define LOG_ALLOW(scope, level, fmt,...)
Logging macro that checks both the log level and whether the calling function is in the allowed-funct...
Definition logging.h:201
#define PROFILE_FUNCTION_END
Marks the end of a profiled code block.
Definition logging.h:740
LogLevel get_log_level()
Retrieves the current logging level from the environment variable LOG_LEVEL.
Definition logging.c:39
PetscErrorCode LOG_FACE_DISTANCES(PetscReal *d)
Prints the signed distances to each face of the cell.
Definition logging.c:227
@ LOG_ERROR
Critical errors that may halt the program.
Definition logging.h:29
@ LOG_TRACE
Very fine-grained tracing information for in-depth debugging.
Definition logging.h:34
@ 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 LOG_CELL_VERTICES(const Cell *cell, PetscMPIInt rank)
Prints the coordinates of a cell's vertices.
Definition logging.c:187
PetscInt ys_cell
Definition variables.h:184
PetscInt xs_cell
Definition variables.h:184
PetscInt cell[3]
Definition variables.h:167
ParticleLocationStatus
Defines the state of a particle with respect to its location and migration status during the iterativ...
Definition variables.h:135
@ LOST
Definition variables.h:139
@ ACTIVE_AND_LOCATED
Definition variables.h:137
@ MIGRATING_OUT
Definition variables.h:138
PetscInt KM
Definition variables.h:670
PetscInt zm_cell
Definition variables.h:185
PetscInt zs_cell
Definition variables.h:184
PetscScalar x
Definition variables.h:101
Cmpnts loc
Definition variables.h:168
PetscMPIInt destination_rank
Definition variables.h:172
PetscInt xm_cell
Definition variables.h:185
RankCellInfo * RankCellInfoMap
Definition variables.h:728
PetscInt ym_cell
Definition variables.h:185
PetscScalar z
Definition variables.h:101
PetscInt JM
Definition variables.h:670
PetscScalar y
Definition variables.h:101
PetscInt IM
Definition variables.h:670
@ 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
PetscInt64 PID
Definition variables.h:166
Cmpnts vertices[8]
Coordinates of the eight vertices of the cell.
Definition variables.h:161
Defines the vertices of a single hexahedral grid cell.
Definition variables.h:160
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
A lean struct to hold the global cell ownership range for a single MPI rank.
Definition variables.h:183
User-defined context containing data specific to a single computational grid level.
Definition variables.h:661
#define DISTANCE_THRESHOLD
#define MAX_TRAVERSAL
PetscErrorCode FinalizeTraversal(UserCtx *user, Particle *particle, PetscInt traversal_steps, PetscBool cell_found, PetscInt idx, PetscInt idy, PetscInt idz)
Finalizes the traversal by reporting the results.
PetscErrorCode LocateParticleOrFindMigrationTarget(UserCtx *user, Particle *particle, ParticleLocationStatus *status_out)
Locates a particle's host cell or identifies its migration target using a robust walk search.
PetscErrorCode GetCellCharacteristicSize(const Cell *cell, PetscReal *cellSize)
Estimates a characteristic length of the cell for threshold scaling.
PetscErrorCode EvaluateParticlePosition(const Cell *cell, PetscReal *d, const Cmpnts p, PetscInt *position, const PetscReal threshold)
Determines the spatial relationship of a particle relative to a cubic cell.
PetscErrorCode ComputeSignedDistanceToPlane(const Cmpnts v1, const Cmpnts v2, const Cmpnts v3, const Cmpnts v4, const Cmpnts cell_centroid, const Cmpnts p_target, PetscReal *d_signed, const PetscReal threshold)
Computes the signed distance from a point to the plane approximating a quadrilateral face.
PetscErrorCode ReportSearchOutcome(const Particle *particle, ParticleLocationStatus status, PetscInt traversal_steps)
Logs the final outcome of the particle location search.
#define REPEAT_COUNT_THRESHOLD
PetscErrorCode CalculateDistancesToCellFaces(const Cmpnts p, const Cell *cell, PetscReal *d, const PetscReal threshold)
Computes the signed distances from a point to each face of a cubic cell.
PetscErrorCode DeterminePointPosition(PetscReal *d, PetscInt *result)
Classifies a point based on precomputed face distances.
PetscErrorCode UpdateCellIndicesBasedOnDistances(PetscReal d[NUM_FACES], PetscInt *idx, PetscInt *idy, PetscInt *idz)
Updates the cell indices based on the signed distances to each face.
PetscErrorCode FindOwnerOfCell(UserCtx *user, PetscInt i, PetscInt j, PetscInt k, PetscMPIInt *owner_rank)
Finds the MPI rank that owns a given global cell index.
PetscErrorCode RetrieveCurrentCell(UserCtx *user, PetscInt idx, PetscInt idy, PetscInt idz, Cell *cell)
Retrieves the coordinates of the eight vertices of the current cell.
PetscErrorCode GetCellVerticesFromGrid(Cmpnts ***coor, PetscInt idx, PetscInt idy, PetscInt idz, Cell *cell)
Retrieves the coordinates of the eight vertices of a cell based on grid indices.
PetscErrorCode InitializeTraversalParameters(UserCtx *user, Particle *particle, PetscInt *idx, PetscInt *idy, PetscInt *idz, PetscInt *traversal_steps)
Initializes traversal parameters for locating a particle.
PetscErrorCode CheckCellWithinLocalGrid(UserCtx *user, PetscInt idx, PetscInt idy, PetscInt idz, PetscBool *is_within)
Checks if the current GLOBAL CELL indices are within the LOCAL GHOSTED grid boundaries accessible by ...
Header file for particle location functions using the walking search algorithm.