@@ -241,66 +241,38 @@ def _initialize_hash_table(self):
241241 num_hash_per_face = (nx * ny * nz ).astype (
242242 np .int32 , copy = False
243243 ) # Since nx, ny, nz are in the 10-bit range, their product fits in int32
244- total_hash_entries = int (num_hash_per_face .sum ())
245-
246- # Preallocate output arrays
247- morton_codes = np .zeros (total_hash_entries , dtype = np .uint32 )
248-
249- # Compute the j, i indices corresponding to each hash entry
244+ # Sums over faces can exceed int32, so accumulate in int64
245+ total_hash_entries = int (num_hash_per_face .sum (dtype = np .int64 ))
246+ # Entry indices fit in int32 for all but extreme cases; fall back to int64 when needed
247+ idx_dtype = np .int64 if total_hash_entries > np .iinfo (np .int32 ).max else np .int32
248+
249+ # Every face overlaps at least one hash cell (nx, ny, nz >= 1 since quantization
250+ # is monotone), and contributes one hash entry per cell of its quantized bounding
251+ # box. Entries are generated in face-major order: face_ids maps each entry to its
252+ # face, and intra enumerates the cells of that face's box (0..num_hash_per_face-1).
250253 nface = np .size (self ._xlow )
251254 face_ids = np .repeat (np .arange (nface , dtype = np .int32 ), num_hash_per_face )
252- offsets = np .concatenate (([0 ], np .cumsum (num_hash_per_face ))).astype (np .int32 )[:- 1 ]
255+ face_starts = np .concatenate (([0 ], np .cumsum (num_hash_per_face , dtype = np .int64 )))[:- 1 ]
256+ intra = np .arange (total_hash_entries , dtype = idx_dtype ) - np .repeat (
257+ face_starts .astype (idx_dtype , copy = False ), num_hash_per_face
258+ )
253259
254- valid = num_hash_per_face != 0
255- if not np .any (valid ):
256- # nothing to do
257- pass
258- else :
259- # Grab only valid faces to avoid empty arrays
260- nx_v = np .asarray (nx [valid ], dtype = np .int32 )
261- ny_v = np .asarray (ny [valid ], dtype = np .int32 )
262- nz_v = np .asarray (nz [valid ], dtype = np .int32 )
263- xlow_v = np .asarray (xqlow [valid ], dtype = np .int32 )
264- ylow_v = np .asarray (yqlow [valid ], dtype = np .int32 )
265- zlow_v = np .asarray (zqlow [valid ], dtype = np .int32 )
266- starts_v = np .asarray (offsets [valid ], dtype = np .int32 )
267-
268- # Count of elements per valid face (should match num_hash_per_face[valid])
269- counts = (nx_v * ny_v * nz_v ).astype (np .int32 )
270- total = int (counts .sum ())
271-
272- # Map each global element to its face and output position
273- start_for_elem = np .repeat (starts_v , counts ) # shape (total,)
274-
275- # Intra-face linear index for each element (0..counts_i-1)
276- # Offsets per face within the concatenation of valid faces:
277- face_starts_local = np .cumsum (np .r_ [0 , counts [:- 1 ]])
278- intra = np .arange (total , dtype = np .int32 ) - np .repeat (face_starts_local , counts )
279-
280- # Derive (zi, yi, xi) from intra using per-face sizes
281- ny_nz = np .repeat (ny_v * nz_v , counts )
282- nz_rep = np .repeat (nz_v , counts )
283-
284- xi = intra // ny_nz
285- rem = intra % ny_nz
286- yi = rem // nz_rep
287- zi = rem % nz_rep
288-
289- # Add per-face lows
290- x0 = np .repeat (xlow_v , counts )
291- y0 = np .repeat (ylow_v , counts )
292- z0 = np .repeat (zlow_v , counts )
293-
294- xq = x0 + xi
295- yq = y0 + yi
296- zq = z0 + zi
297-
298- # Vectorized morton encode for all elements at once
299- codes_all = _encode_quantized_morton3d (xq , yq , zq )
300-
301- # Scatter into the preallocated output using computed absolute indices
302- out_idx = start_for_elem + intra
303- morton_codes [out_idx ] = codes_all
260+ # Derive (xi, yi, zi) cell offsets within each face's box from intra,
261+ # then shift by the per-face low corner to get quantized cell coordinates
262+ ny_nz = np .repeat (ny * nz , num_hash_per_face )
263+ nz_rep = np .repeat (nz , num_hash_per_face )
264+
265+ xi = intra // ny_nz
266+ rem = intra % ny_nz
267+ yi = rem // nz_rep
268+ zi = rem % nz_rep
269+
270+ xq = np .repeat (xqlow , num_hash_per_face ) + xi
271+ yq = np .repeat (yqlow , num_hash_per_face ) + yi
272+ zq = np .repeat (zqlow , num_hash_per_face ) + zi
273+
274+ # Vectorized morton encode for all entries at once, already in face-major order
275+ morton_codes = _encode_quantized_morton3d (xq , yq , zq )
304276
305277 # Sort face indices by morton code
306278 order = np .argsort (morton_codes )
0 commit comments