@@ -56,7 +56,62 @@ __global__ void makeTriangleSandwich(deme::DEMSimParams* simParams,
5656 }
5757}
5858
59- inline __device__ void figureOutNodeAndBoundingBox (deme::DEMSimParams* simParams,
59+
60+ // Compute triangle AABB -> bin index bounds using mixed precision (FP32 fast path + FP64 fallback).
61+ // This mirrors the sphere binning approach (axis_bounds) to avoid precision issues when a bound lies
62+ // close to a bin boundary.
63+ inline __device__ bool boundingBoxIntersectBinAxisBounds (deme::binID_t* L,
64+ deme::binID_t* U,
65+ const float3 & vA,
66+ const float3 & vB,
67+ const float3 & vC,
68+ deme::DEMSimParams* simParams) {
69+ float3 min_pt;
70+ min_pt.x = DEME_MIN (vA.x , DEME_MIN (vB.x , vC.x ));
71+ min_pt.y = DEME_MIN (vA.y , DEME_MIN (vB.y , vC.y ));
72+ min_pt.z = DEME_MIN (vA.z , DEME_MIN (vB.z , vC.z ));
73+
74+ float3 max_pt;
75+ max_pt.x = DEME_MAX (vA.x , DEME_MAX (vB.x , vC.x ));
76+ max_pt.y = DEME_MAX (vA.y , DEME_MAX (vB.y , vC.y ));
77+ max_pt.z = DEME_MAX (vA.z , DEME_MAX (vB.z , vC.z ));
78+
79+ // Enlarge bounding box, so that no triangle lies right between 2 layers of bins
80+ const float enlarge = (float )DEME_BIN_ENLARGE_RATIO_FOR_FACETS * (float )simParams->dyn .binSize ;
81+ min_pt -= enlarge;
82+ max_pt += enlarge;
83+
84+ const double invBinSize = simParams->dyn .inv_binSize ;
85+ const int nbX = (int )simParams->nbX ;
86+ const int nbY = (int )simParams->nbY ;
87+ const int nbZ = (int )simParams->nbZ ;
88+
89+ // Convert [min,max] to (center, half-range) and use axis_bounds (FP32 fast path with FP64 fallback).
90+ const double cx = 0.5 * ((double )min_pt.x + (double )max_pt.x );
91+ const double rx = 0.5 * ((double )max_pt.x - (double )min_pt.x );
92+ const deme::AxisBounds bx = axis_bounds (cx, rx, nbX, invBinSize);
93+ if (bx.imax < bx.imin ) return false ;
94+
95+ const double cy = 0.5 * ((double )min_pt.y + (double )max_pt.y );
96+ const double ry = 0.5 * ((double )max_pt.y - (double )min_pt.y );
97+ const deme::AxisBounds by = axis_bounds (cy, ry, nbY, invBinSize);
98+ if (by.imax < by.imin ) return false ;
99+
100+ const double cz = 0.5 * ((double )min_pt.z + (double )max_pt.z );
101+ const double rz = 0.5 * ((double )max_pt.z - (double )min_pt.z );
102+ const deme::AxisBounds bz = axis_bounds (cz, rz, nbZ, invBinSize);
103+ if (bz.imax < bz.imin ) return false ;
104+
105+ L[0 ] = (deme::binID_t)bx.imin ;
106+ U[0 ] = (deme::binID_t)bx.imax ;
107+ L[1 ] = (deme::binID_t)by.imin ;
108+ U[1 ] = (deme::binID_t)by.imax ;
109+ L[2 ] = (deme::binID_t)bz.imin ;
110+ U[2 ] = (deme::binID_t)bz.imax ;
111+ return true ;
112+ }
113+
114+ inline __device__ bool figureOutNodeAndBoundingBox (deme::DEMSimParams* simParams,
60115 deme::DEMDataKT* granData,
61116 const deme::bodyID_t& triID,
62117 float3 & vA,
@@ -85,7 +140,7 @@ inline __device__ void figureOutNodeAndBoundingBox(deme::DEMSimParams* simParams
85140 vB = ownerXYZ + loc_vB;
86141 vC = ownerXYZ + loc_vC;
87142
88- boundingBoxIntersectBin (L, U, vA, vB, vC, simParams);
143+ return boundingBoxIntersectBinAxisBounds (L, U, vA, vB, vC, simParams);
89144}
90145
91146__global__ void getNumberOfBinsEachTriangleTouches (deme::DEMSimParams* simParams,
@@ -106,38 +161,64 @@ __global__ void getNumberOfBinsEachTriangleTouches(deme::DEMSimParams* simParams
106161 // bin-based locations don't need that)
107162 float3 vA1, vB1, vC1, vA2, vB2, vC2;
108163 deme::binID_t L1 [3 ], L2 [3 ], U1 [3 ], U2 [3 ];
109- figureOutNodeAndBoundingBox (simParams, granData, triID, vA1, vB1, vC1, L1 , U1 , nodeA1[triID], nodeB1[triID],
110- nodeC1[triID]);
111- figureOutNodeAndBoundingBox (simParams, granData, triID, vA2, vB2, vC2, L2 , U2 , nodeA2[triID], nodeB2[triID],
112- nodeC2[triID]);
113- L1 [0 ] = DEME_MIN (L1 [0 ], L2 [0 ]);
114- L1 [1 ] = DEME_MIN (L1 [1 ], L2 [1 ]);
115- L1 [2 ] = DEME_MIN (L1 [2 ], L2 [2 ]);
116- U1 [0 ] = DEME_MAX (U1 [0 ], U2 [0 ]);
117- U1 [1 ] = DEME_MAX (U1 [1 ], U2 [1 ]);
118- U1 [2 ] = DEME_MAX (U1 [2 ], U2 [2 ]);
164+ const bool ok1 = figureOutNodeAndBoundingBox (simParams, granData, triID, vA1, vB1, vC1, L1 , U1 , nodeA1[triID],
165+ nodeB1[triID], nodeC1[triID]);
166+ const bool ok2 = figureOutNodeAndBoundingBox (simParams, granData, triID, vA2, vB2, vC2, L2 , U2 , nodeA2[triID],
167+ nodeB2[triID], nodeC2[triID]);
168+
169+ // If neither triangle sandwich intersects the bin grid, it cannot touch any bin.
170+ if (!ok1 && !ok2) {
171+ numBinsTriTouches[triID] = 0 ;
172+ if (meshUniversalContact) {
173+ numAnalGeoTriTouches[triID] = 0 ;
174+ }
175+ return ;
176+ }
177+
178+ // Merge bounds (or take the valid one, if only one is valid).
179+ if (ok1 && ok2) {
180+ L1 [0 ] = DEME_MIN (L1 [0 ], L2 [0 ]);
181+ L1 [1 ] = DEME_MIN (L1 [1 ], L2 [1 ]);
182+ L1 [2 ] = DEME_MIN (L1 [2 ], L2 [2 ]);
183+ U1 [0 ] = DEME_MAX (U1 [0 ], U2 [0 ]);
184+ U1 [1 ] = DEME_MAX (U1 [1 ], U2 [1 ]);
185+ U1 [2 ] = DEME_MAX (U1 [2 ], U2 [2 ]);
186+ } else if (!ok1) {
187+ L1 [0 ] = L2 [0 ];
188+ L1 [1 ] = L2 [1 ];
189+ L1 [2 ] = L2 [2 ];
190+ U1 [0 ] = U2 [0 ];
191+ U1 [1 ] = U2 [1 ];
192+ U1 [2 ] = U2 [2 ];
193+ }
119194
120195 unsigned int numSDsTouched = 0 ;
121196 // Triangle may span a collection of bins...
122197 // BTW, I don't know why Chrono::GPU had to check the so-called 3 cases, and create thread divergence like that.
123198 // Just sweep through all potential bins and you are fine.
124199 float BinCenter[3 ];
125- float BinHalfSizes[3 ];
126- BinHalfSizes[0 ] = simParams->dyn .binSize / 2 . + DEME_BIN_ENLARGE_RATIO_FOR_FACETS * simParams->dyn .binSize ;
127- BinHalfSizes[1 ] = simParams->dyn .binSize / 2 . + DEME_BIN_ENLARGE_RATIO_FOR_FACETS * simParams->dyn .binSize ;
128- BinHalfSizes[2 ] = simParams->dyn .binSize / 2 . + DEME_BIN_ENLARGE_RATIO_FOR_FACETS * simParams->dyn .binSize ;
129- for (deme::binID_t i = L1 [0 ]; i <= U1 [0 ]; i++) {
200+ const float binSizeF = (float )simParams->dyn .binSize ;
201+ const float binHalfSpan = binSizeF * (0 .5f + (float )DEME_BIN_ENLARGE_RATIO_FOR_FACETS );
202+ float BinHalfSizes[3 ] = {binHalfSpan, binHalfSpan, binHalfSpan};
203+ const float startX = binSizeF * (float )L1 [0 ] + 0 .5f * binSizeF;
204+ const float startY = binSizeF * (float )L1 [1 ] + 0 .5f * binSizeF;
205+ const float startZ = binSizeF * (float )L1 [2 ] + 0 .5f * binSizeF;
206+ for (deme::binID_t i = L1 [0 ], ix = 0 ; i <= U1 [0 ]; i++, ix++) {
207+ float cy0 = startY;
208+ BinCenter[0 ] = startX + ix * binSizeF;
130209 for (deme::binID_t j = L1 [1 ]; j <= U1 [1 ]; j++) {
210+ float cz = startZ;
211+ BinCenter[1 ] = cy0;
131212 for (deme::binID_t k = L1 [2 ]; k <= U1 [2 ]; k++) {
132- BinCenter[0 ] = simParams->dyn .binSize * i + simParams->dyn .binSize / 2 .;
133- BinCenter[1 ] = simParams->dyn .binSize * j + simParams->dyn .binSize / 2 .;
134- BinCenter[2 ] = simParams->dyn .binSize * k + simParams->dyn .binSize / 2 .;
213+ BinCenter[2 ] = cz;
135214
136215 if (check_TriangleBoxOverlap (BinCenter, BinHalfSizes, vA1, vB1, vC1) ||
137216 check_TriangleBoxOverlap (BinCenter, BinHalfSizes, vA2, vB2, vC2)) {
138217 numSDsTouched++;
139218 }
219+ cz += binSizeF;
140220 }
221+ cy0 += binSizeF;
141222 }
142223 }
143224 numBinsTriTouches[triID] = numSDsTouched;
@@ -221,36 +302,56 @@ __global__ void populateBinTriangleTouchingPairs(deme::DEMSimParams* simParams,
221302 // 3 vertices of the triangle
222303 float3 vA1, vB1, vC1, vA2, vB2, vC2;
223304 deme::binID_t L1 [3 ], L2 [3 ], U1 [3 ], U2 [3 ];
224- figureOutNodeAndBoundingBox (simParams, granData, triID, vA1, vB1, vC1, L1 , U1 , nodeA1[triID], nodeB1[triID],
225- nodeC1[triID]);
226- figureOutNodeAndBoundingBox (simParams, granData, triID, vA2, vB2, vC2, L2 , U2 , nodeA2[triID], nodeB2[triID],
227- nodeC2[triID]);
228- L1 [0 ] = DEME_MIN (L1 [0 ], L2 [0 ]);
229- L1 [1 ] = DEME_MIN (L1 [1 ], L2 [1 ]);
230- L1 [2 ] = DEME_MIN (L1 [2 ], L2 [2 ]);
231- U1 [0 ] = DEME_MAX (U1 [0 ], U2 [0 ]);
232- U1 [1 ] = DEME_MAX (U1 [1 ], U2 [1 ]);
233- U1 [2 ] = DEME_MAX (U1 [2 ], U2 [2 ]);
305+ const bool ok1 = figureOutNodeAndBoundingBox (simParams, granData, triID, vA1, vB1, vC1, L1 , U1 , nodeA1[triID],
306+ nodeB1[triID], nodeC1[triID]);
307+ const bool ok2 = figureOutNodeAndBoundingBox (simParams, granData, triID, vA2, vB2, vC2, L2 , U2 , nodeA2[triID],
308+ nodeB2[triID], nodeC2[triID]);
309+
310+ // If neither triangle sandwich intersects the bin grid, it cannot touch any bin.
311+ if (!ok1 && !ok2) {
312+ return ;
313+ }
314+
315+ // Merge bounds (or take the valid one, if only one is valid).
316+ if (ok1 && ok2) {
317+ L1 [0 ] = DEME_MIN (L1 [0 ], L2 [0 ]);
318+ L1 [1 ] = DEME_MIN (L1 [1 ], L2 [1 ]);
319+ L1 [2 ] = DEME_MIN (L1 [2 ], L2 [2 ]);
320+ U1 [0 ] = DEME_MAX (U1 [0 ], U2 [0 ]);
321+ U1 [1 ] = DEME_MAX (U1 [1 ], U2 [1 ]);
322+ U1 [2 ] = DEME_MAX (U1 [2 ], U2 [2 ]);
323+ } else if (!ok1) {
324+ L1 [0 ] = L2 [0 ];
325+ L1 [1 ] = L2 [1 ];
326+ L1 [2 ] = L2 [2 ];
327+ U1 [0 ] = U2 [0 ];
328+ U1 [1 ] = U2 [1 ];
329+ U1 [2 ] = U2 [2 ];
330+ }
234331
235332 deme::binsTriangleTouchPairs_t myReportOffset = numBinsTriTouchesScan[triID];
236333 // In case this sweep does not agree with the previous one, we need to intercept such potential segfaults
237334 const deme::binsTriangleTouchPairs_t myReportOffset_end = numBinsTriTouchesScan[triID + 1 ];
238335
239336 // Triangle may span a collection of bins...
240337 float BinCenter[3 ];
241- float BinHalfSizes[3 ];
242- BinHalfSizes[0 ] = simParams->dyn .binSize / 2 . + DEME_BIN_ENLARGE_RATIO_FOR_FACETS * simParams->dyn .binSize ;
243- BinHalfSizes[1 ] = simParams->dyn .binSize / 2 . + DEME_BIN_ENLARGE_RATIO_FOR_FACETS * simParams->dyn .binSize ;
244- BinHalfSizes[2 ] = simParams->dyn .binSize / 2 . + DEME_BIN_ENLARGE_RATIO_FOR_FACETS * simParams->dyn .binSize ;
245- for (deme::binID_t i = L1 [0 ]; i <= U1 [0 ]; i++) {
338+ const float binSizeF = (float )simParams->dyn .binSize ;
339+ const float binHalfSpan = binSizeF * (0 .5f + (float )DEME_BIN_ENLARGE_RATIO_FOR_FACETS );
340+ float BinHalfSizes[3 ] = {binHalfSpan, binHalfSpan, binHalfSpan};
341+ const float startX = binSizeF * (float )L1 [0 ] + 0 .5f * binSizeF;
342+ const float startY = binSizeF * (float )L1 [1 ] + 0 .5f * binSizeF;
343+ const float startZ = binSizeF * (float )L1 [2 ] + 0 .5f * binSizeF;
344+ for (deme::binID_t i = L1 [0 ], ix = 0 ; i <= U1 [0 ]; i++, ix++) {
345+ BinCenter[0 ] = startX + ix * binSizeF;
346+ float cy0 = startY;
246347 for (deme::binID_t j = L1 [1 ]; j <= U1 [1 ]; j++) {
348+ BinCenter[1 ] = cy0;
349+ float cz = startZ;
247350 for (deme::binID_t k = L1 [2 ]; k <= U1 [2 ]; k++) {
248351 if (myReportOffset >= myReportOffset_end) {
249352 continue ; // Don't step on the next triangle's domain
250353 }
251- BinCenter[0 ] = simParams->dyn .binSize * i + simParams->dyn .binSize / 2 .;
252- BinCenter[1 ] = simParams->dyn .binSize * j + simParams->dyn .binSize / 2 .;
253- BinCenter[2 ] = simParams->dyn .binSize * k + simParams->dyn .binSize / 2 .;
354+ BinCenter[2 ] = cz;
254355
255356 if (check_TriangleBoxOverlap (BinCenter, BinHalfSizes, vA1, vB1, vC1) ||
256357 check_TriangleBoxOverlap (BinCenter, BinHalfSizes, vA2, vB2, vC2)) {
@@ -259,7 +360,9 @@ __global__ void populateBinTriangleTouchingPairs(deme::DEMSimParams* simParams,
259360 triIDsEachBinTouches[myReportOffset] = triID;
260361 myReportOffset++;
261362 }
363+ cz += binSizeF;
262364 }
365+ cy0 += binSizeF;
263366 }
264367 }
265368 // This can happen for like 1 in 10^9 chance, for the tri--bin contact algorithm has stochasticity on GPU
0 commit comments