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