1- # Copyright (C) 2013-2025 Anders Logg, Jørgen S. Dokken, Chris Richardson
1+ # Copyright (C) 2013-2026 Anders Logg, Jørgen S. Dokken, Chris Richardson
22#
33# This file is part of DOLFINx (https://www.fenicsproject.org)
44#
1717 compute_collisions_points ,
1818 compute_collisions_trees ,
1919 compute_distance_gjk ,
20+ compute_distances_gjk ,
2021 create_midpoint_tree ,
2122 determine_point_ownership ,
2223)
@@ -50,47 +51,47 @@ def extract_geometricial_data(mesh, dim, entities):
5051 return mesh_nodes
5152
5253
53- def expand_bbox (bbox , dtype ):
54- """Expand min max bbox to convex hull."""
55- return np .array (
56- [
57- [bbox [0 ][0 ], bbox [0 ][1 ], bbox [0 ][2 ]],
58- [bbox [0 ][0 ], bbox [0 ][1 ], bbox [1 ][2 ]],
59- [bbox [0 ][0 ], bbox [1 ][1 ], bbox [0 ][2 ]],
60- [bbox [1 ][0 ], bbox [0 ][1 ], bbox [0 ][2 ]],
61- [bbox [1 ][0 ], bbox [0 ][1 ], bbox [1 ][2 ]],
62- [bbox [1 ][0 ], bbox [1 ][1 ], bbox [0 ][2 ]],
63- [bbox [0 ][0 ], bbox [1 ][1 ], bbox [1 ][2 ]],
64- [bbox [1 ][0 ], bbox [1 ][1 ], bbox [1 ][2 ]],
65- ],
66- dtype = dtype ,
67- )
54+ def expand_bboxes (bboxes , dtype ):
55+ """Expand an array of min/max bboxes to convex hulls."""
56+ if len (bboxes .shape ) == 2 :
57+ bboxes = bboxes .reshape (1 , * bboxes .shape )
58+ idx_x = [0 , 0 , 0 , 1 , 1 , 1 , 0 , 1 ]
59+ idx_y = [0 , 0 , 1 , 0 , 0 , 1 , 1 , 1 ]
60+ idx_z = [0 , 1 , 0 , 0 , 1 , 0 , 1 , 1 ]
61+
62+ x = bboxes [:, idx_x , 0 ]
63+ y = bboxes [:, idx_y , 1 ]
64+ z = bboxes [:, idx_z , 2 ]
65+ return np .stack ((x , y , z ), axis = - 1 ).astype (dtype )
6866
6967
70- def find_colliding_cells (mesh , bbox , dtype ):
68+ def find_colliding_cells (mesh , bbox , dtype , num_threads ):
7169 """Given a mesh and a bounding box((xmin, ymin, zmin), (xmax, ymax,
7270 zmax)) find all colliding cells.
7371 """
7472 # Find actual cells using known bounding box tree
75- colliding_cells = []
7673 num_cells = mesh .topology .index_map (mesh .topology .dim ).size_local
7774 x_indices = entities_to_geometry (
7875 mesh , mesh .topology .dim , np .arange (num_cells , dtype = np .int32 ), False
7976 )
8077 points = mesh .geometry .x
81- bounding_box = expand_bbox (bbox , dtype )
82- for cell in range (num_cells ):
83- vertex_coords = points [x_indices [cell ]]
84- bbox_cell = np .array ([vertex_coords [0 ], vertex_coords [0 ]])
85- # Create bounding box for cell
86- for i in range (1 , vertex_coords .shape [0 ]):
87- for j in range (3 ):
88- bbox_cell [0 , j ] = min (bbox_cell [0 , j ], vertex_coords [i , j ])
89- bbox_cell [1 , j ] = max (bbox_cell [1 , j ], vertex_coords [i , j ])
90- distance = compute_distance_gjk (expand_bbox (bbox_cell , dtype ), bounding_box )
91- if np .dot (distance , distance ) < 1e-16 :
92- colliding_cells .append (cell )
93-
78+ bounding_box = expand_bboxes (bbox , dtype )[0 ]
79+
80+ # Pack the data for each of the cell bounding boxes
81+ # First pack min and max values for each cell
82+ min_in_cell = np .min (points [x_indices ], axis = 1 )
83+ max_in_cell = np .max (points [x_indices ], axis = 1 )
84+ bboxes = np .zeros ((num_cells , 2 , 3 ))
85+ bboxes [:, 0 , :] = min_in_cell
86+ bboxes [:, 1 , :] = max_in_cell
87+ # Expand min and max values to bounding box
88+ body_1 = list (expand_bboxes (bboxes , dtype ))
89+
90+ # Compute distances and check for collision
91+ distances = compute_distances_gjk (body_1 , bounding_box , num_threads )
92+ norm_dist = np .linalg .norm (distances , axis = 1 ) ** 2
93+ tol = 10 * np .finfo (dtype ).eps
94+ colliding_cells = np .flatnonzero (norm_dist < tol ).astype (np .int32 )
9495 return colliding_cells
9596
9697
@@ -224,9 +225,10 @@ def locator_B(x):
224225
225226
226227@pytest .mark .skip_in_parallel
228+ @pytest .mark .parametrize ("num_threads" , [1 , 2 ])
227229@pytest .mark .parametrize ("point" , [np .array ([0.52 , 0.51 , 0.0 ]), np .array ([0.9 , - 0.9 , 0.0 ])])
228230@pytest .mark .parametrize ("dtype" , [np .float32 , np .float64 ])
229- def test_compute_collisions_tree_2d (point , dtype ):
231+ def test_compute_collisions_tree_2d (point , dtype , num_threads ):
230232 mesh_A = create_unit_square (MPI .COMM_WORLD , 3 , 3 , dtype = dtype )
231233 mesh_B = create_unit_square (MPI .COMM_WORLD , 5 , 5 , dtype = dtype )
232234 bgeom = mesh_B .geometry .x
@@ -237,18 +239,24 @@ def test_compute_collisions_tree_2d(point, dtype):
237239
238240 entities_A = np .sort (np .unique ([q [0 ] for q in entities ]))
239241 entities_B = np .sort (np .unique ([q [1 ] for q in entities ]))
240- cells_A = find_colliding_cells (mesh_A , tree_B .get_bbox (tree_B .num_bboxes - 1 ), dtype )
241- cells_B = find_colliding_cells (mesh_B , tree_A .get_bbox (tree_A .num_bboxes - 1 ), dtype )
242+ cells_A = find_colliding_cells (
243+ mesh_A , tree_B .get_bbox (tree_B .num_bboxes - 1 ), dtype , num_threads
244+ )
245+ cells_B = find_colliding_cells (
246+ mesh_B , tree_A .get_bbox (tree_A .num_bboxes - 1 ), dtype , num_threads
247+ )
242248 assert np .allclose (entities_A , cells_A )
243249 assert np .allclose (entities_B , cells_B )
244250
245251
246252@pytest .mark .skip_in_parallel
253+ @pytest .mark .parametrize ("num_threads" , [1 , 2 ])
247254@pytest .mark .parametrize ("point" , [np .array ([0.52 , 0.51 , 0.3 ]), np .array ([0.9 , - 0.9 , 0.3 ])])
248255@pytest .mark .parametrize ("dtype" , [np .float32 , np .float64 ])
249- def test_compute_collisions_tree_3d (point , dtype ):
250- mesh_A = create_unit_cube (MPI .COMM_WORLD , 2 , 2 , 2 , dtype = dtype )
251- mesh_B = create_unit_cube (MPI .COMM_WORLD , 2 , 2 , 2 , dtype = dtype )
256+ def test_compute_collisions_tree_3d (point , dtype , num_threads ):
257+ M = 10
258+ mesh_A = create_unit_cube (MPI .COMM_WORLD , M , M , M , dtype = dtype )
259+ mesh_B = create_unit_cube (MPI .COMM_WORLD , M , M , M , dtype = dtype )
252260
253261 bgeom = mesh_B .geometry .x
254262 bgeom += point
@@ -258,8 +266,12 @@ def test_compute_collisions_tree_3d(point, dtype):
258266 entities = compute_collisions_trees (tree_A , tree_B )
259267 entities_A = np .sort (np .unique ([q [0 ] for q in entities ]))
260268 entities_B = np .sort (np .unique ([q [1 ] for q in entities ]))
261- cells_A = find_colliding_cells (mesh_A , tree_B .get_bbox (tree_B .num_bboxes - 1 ), dtype )
262- cells_B = find_colliding_cells (mesh_B , tree_A .get_bbox (tree_A .num_bboxes - 1 ), dtype )
269+ cells_A = find_colliding_cells (
270+ mesh_A , tree_B .get_bbox (tree_B .num_bboxes - 1 ), dtype , num_threads
271+ )
272+ cells_B = find_colliding_cells (
273+ mesh_B , tree_A .get_bbox (tree_A .num_bboxes - 1 ), dtype , num_threads
274+ )
263275 assert np .allclose (entities_A , cells_A )
264276 assert np .allclose (entities_B , cells_B )
265277
0 commit comments