11//! Octree for spatially partitioning particle sets
22
33use crate :: generic_tree:: * ;
4- use crate :: mesh:: { HexMesh3d , TriMesh3d } ;
4+ use crate :: marching_cubes:: SurfacePatch ;
5+ use crate :: mesh:: { HexMesh3d , MeshWithData , TriMesh3d } ;
6+ use crate :: topology:: { Axis , Direction } ;
57use crate :: uniform_grid:: { PointIndex , UniformGrid } ;
68use crate :: utils:: { ChunkSize , ParallelPolicy } ;
79use crate :: {
@@ -14,7 +16,9 @@ use nalgebra::Vector3;
1416use octant_helper:: { HalfspaceFlags , Octant , OctantAxisDirections } ;
1517use rayon:: prelude:: * ;
1618use smallvec:: SmallVec ;
19+ use split_criterion:: { default_split_criterion, LeafSplitCriterion } ;
1720use std:: cell:: RefCell ;
21+ use std:: sync:: atomic:: { AtomicUsize , Ordering } ;
1822use thread_local:: ThreadLocal ;
1923
2024// TODO: Make margin an Option
@@ -31,12 +35,17 @@ pub enum SubdivisionCriterion {
3135/// Data structure for octree based spatial subdivision of particles sets, for tree iteration/visitation use the [`root`](Self::root) [`OctreeNode`]
3236#[ derive( Clone , Debug ) ]
3337pub struct Octree < I : Index , R : Real > {
38+ /// Root node of the tree
3439 root : OctreeNode < I , R > ,
40+ /// Counter for assigning ids to subdivided nodes
41+ next_id : usize ,
3542}
3643
3744/// Represents a node in the octree hierarchy and stores child nodes, implements tree iteration/visitation from the [`generic_tree`](crate::generic_tree) module
3845#[ derive( Clone , Debug ) ]
3946pub struct OctreeNode < I : Index , R : Real > {
47+ /// Id of the node used to identify it for debugging
48+ id : usize ,
4049 /// All child nodes of this octree node
4150 children : ArrayVec < [ Box < Self > ; 8 ] > ,
4251 /// Lower corner point of the octree node on the background grid
@@ -108,15 +117,12 @@ impl<I: Index, R: Real> SurfacePatchWrapper<I, R> {
108117
109118type OctreeNodeParticleStorage = SmallVec < [ usize ; 6 ] > ;
110119
111- use crate :: marching_cubes:: SurfacePatch ;
112- use crate :: topology:: { Axis , Direction } ;
113- use split_criterion:: { default_split_criterion, LeafSplitCriterion } ;
114-
115120impl < I : Index , R : Real > Octree < I , R > {
116121 /// Creates a new octree with a single leaf node containing all vertices
117122 pub fn new ( grid : & UniformGrid < I , R > , n_particles : usize ) -> Self {
118123 Self {
119124 root : OctreeNode :: new_root ( grid, n_particles) ,
125+ next_id : 0 ,
120126 }
121127 }
122128
@@ -135,6 +141,8 @@ impl<I: Index, R: Real> Octree<I, R> {
135141 ) -> Self {
136142 let mut tree = Octree :: new ( & grid, particle_positions. len ( ) ) ;
137143
144+ println ! ( "Margin: {}" , margin) ;
145+
138146 if enable_multi_threading {
139147 tree. subdivide_recursively_margin_par (
140148 grid,
@@ -156,11 +164,6 @@ impl<I: Index, R: Real> Octree<I, R> {
156164 tree
157165 }
158166
159- /// Creates a new octree with the given node as root
160- pub fn with_root ( root : OctreeNode < I , R > ) -> Self {
161- Self { root }
162- }
163-
164167 /// Returns a reference to the root node of the octree
165168 pub fn root ( & self ) -> & OctreeNode < I , R > {
166169 & self . root
@@ -188,15 +191,17 @@ impl<I: Index, R: Real> Octree<I, R> {
188191 enable_stitching,
189192 ) ;
190193
194+ let next_id = AtomicUsize :: new ( 0 ) ;
191195 self . root . visit_mut_bfs ( |node| {
192196 // Stop recursion if split criterion is not fulfilled
193197 if !split_criterion. split_leaf ( node) {
194198 return ;
195199 }
196200
197201 // Perform one octree split on the node
198- node. subdivide_with_margin ( grid, particle_positions, margin) ;
202+ node. subdivide_with_margin ( grid, particle_positions, margin, & next_id ) ;
199203 } ) ;
204+ self . next_id = next_id. into_inner ( ) ;
200205 }
201206
202207 /// Subdivide the octree recursively and in parallel using the given splitting criterion and a margin to add ghost particles
@@ -217,39 +222,55 @@ impl<I: Index, R: Real> Octree<I, R> {
217222 ) ;
218223 let parallel_policy = ParallelPolicy :: default ( ) ;
219224
220- let visitor = move |node : & mut OctreeNode < I , R > | {
221- // Stop recursion if split criterion is not fulfilled
222- if !split_criterion. split_leaf ( node) {
223- return ;
224- }
225+ let next_id = AtomicUsize :: new ( 0 ) ;
226+ let visitor = {
227+ let next_id = & next_id;
228+ move |node : & mut OctreeNode < I , R > | {
229+ // Stop recursion if split criterion is not fulfilled
230+ if !split_criterion. split_leaf ( node) {
231+ return ;
232+ }
225233
226- // Perform one octree split on the leaf
227- if node
228- . data
229- . particle_set ( )
230- . expect ( "Node is not a leaf" )
231- . particles
232- . len ( )
233- < parallel_policy. min_task_size
234- {
235- node. subdivide_with_margin ( grid, particle_positions, margin) ;
236- } else {
237- node. subdivide_with_margin_par ( grid, particle_positions, margin, & parallel_policy) ;
234+ // Perform one octree split on the leaf
235+ if node
236+ . data
237+ . particle_set ( )
238+ . expect ( "Node is not a leaf" )
239+ . particles
240+ . len ( )
241+ < parallel_policy. min_task_size
242+ {
243+ node. subdivide_with_margin ( grid, particle_positions, margin, & next_id) ;
244+ } else {
245+ node. subdivide_with_margin_par (
246+ grid,
247+ particle_positions,
248+ margin,
249+ & parallel_policy,
250+ & next_id,
251+ ) ;
252+ }
238253 }
239254 } ;
240255
241256 self . root . par_visit_mut_bfs ( visitor) ;
257+ self . next_id = next_id. into_inner ( ) ;
242258 }
243259
244260 /// Constructs a hex mesh visualizing the cells of the octree, may contain hanging and duplicate vertices as cells are not connected
245- pub fn hexmesh ( & self , grid : & UniformGrid < I , R > , only_non_empty : bool ) -> HexMesh3d < R > {
261+ pub fn hexmesh (
262+ & self ,
263+ grid : & UniformGrid < I , R > ,
264+ only_non_empty : bool ,
265+ ) -> MeshWithData < HexMesh3d < R > , ( ) , u64 > {
246266 profile ! ( "convert octree into hexmesh" ) ;
247267
248268 let mut mesh = HexMesh3d {
249269 vertices : Vec :: new ( ) ,
250270 cells : Vec :: new ( ) ,
251271 } ;
252272
273+ let mut ids = Vec :: new ( ) ;
253274 self . root . dfs_iter ( ) . for_each ( |node| {
254275 if node. children ( ) . is_empty ( ) {
255276 if only_non_empty
@@ -290,16 +311,18 @@ impl<I: Index, R: Real> Octree<I, R> {
290311
291312 mesh. vertices . extend ( vertices) ;
292313 mesh. cells . push ( cell) ;
314+ ids. push ( node. id as u64 ) ;
293315 }
294316 } ) ;
295317
296- mesh
318+ assert_eq ! ( mesh. cells. len( ) , ids. len( ) ) ;
319+ MeshWithData :: with_cell_data ( mesh, ids)
297320 }
298321}
299322
300323impl < I : Index , R : Real > OctreeNode < I , R > {
301- pub fn new ( min_corner : PointIndex < I > , max_corner : PointIndex < I > ) -> Self {
302- Self :: with_data ( min_corner, max_corner, NodeData :: None )
324+ pub fn new ( id : usize , min_corner : PointIndex < I > , max_corner : PointIndex < I > ) -> Self {
325+ Self :: with_data ( id , min_corner, max_corner, NodeData :: None )
303326 }
304327
305328 fn new_root ( grid : & UniformGrid < I , R > , n_particles : usize ) -> Self {
@@ -312,6 +335,7 @@ impl<I: Index, R: Real> OctreeNode<I, R> {
312335 ] ;
313336
314337 Self :: with_data (
338+ 0 ,
315339 grid. get_point ( min_point)
316340 . expect ( "Cannot get lower corner of grid" ) ,
317341 grid. get_point ( max_point)
@@ -321,18 +345,25 @@ impl<I: Index, R: Real> OctreeNode<I, R> {
321345 }
322346
323347 fn with_data (
348+ id : usize ,
324349 min_corner : PointIndex < I > ,
325350 max_corner : PointIndex < I > ,
326351 data : NodeData < I , R > ,
327352 ) -> Self {
328353 Self {
354+ id,
329355 children : Default :: default ( ) ,
330356 min_corner,
331357 max_corner,
332358 data,
333359 }
334360 }
335361
362+ /// Returns the id of the node
363+ pub fn id ( & self ) -> usize {
364+ self . id
365+ }
366+
336367 /// Returns a reference to the data stored in the node
337368 pub fn data ( & self ) -> & NodeData < I , R > {
338369 & self . data
@@ -385,6 +416,7 @@ impl<I: Index, R: Real> OctreeNode<I, R> {
385416 grid : & UniformGrid < I , R > ,
386417 particle_positions : & [ Vector3 < R > ] ,
387418 margin : R ,
419+ next_id : & AtomicUsize ,
388420 ) {
389421 // Convert node body from Leaf to Children
390422 if let NodeData :: ParticleSet ( particle_set) = self . data . take ( ) {
@@ -460,6 +492,7 @@ impl<I: Index, R: Real> OctreeNode<I, R> {
460492 assert_eq ! ( octant_particles. len( ) , octant_particle_count) ;
461493
462494 let child = Box :: new ( OctreeNode :: with_data (
495+ next_id. fetch_add ( 1 , Ordering :: SeqCst ) ,
463496 min_corner,
464497 max_corner,
465498 NodeData :: new_particle_set (
@@ -485,6 +518,7 @@ impl<I: Index, R: Real> OctreeNode<I, R> {
485518 particle_positions : & [ Vector3 < R > ] ,
486519 margin : R ,
487520 parallel_policy : & ParallelPolicy ,
521+ next_id : & AtomicUsize ,
488522 ) {
489523 // Convert node body from Leaf to Children
490524 if let NodeData :: ParticleSet ( particle_set) = self . data . take ( ) {
@@ -594,6 +628,7 @@ impl<I: Index, R: Real> OctreeNode<I, R> {
594628 assert_eq ! ( octant_particles. len( ) , octant_particle_count) ;
595629
596630 let child = Box :: new ( OctreeNode :: with_data (
631+ next_id. fetch_add ( 1 , Ordering :: SeqCst ) ,
597632 min_corner,
598633 max_corner,
599634 NodeData :: new_particle_set (
0 commit comments