@@ -371,7 +371,7 @@ pub fn neighborhood_search_spatial_hashing_flat_filtered<I: Index, R: Real>(
371371 . expect ( "Failed to construct grid for neighborhood search!" ) ;
372372 // Map for spatially hashed storage of all particles (map from cell -> enclosed particles)
373373 let particles_per_cell =
374- sequential_generate_cell_to_particle_map :: < I , R > ( & grid, particle_positions) ;
374+ sequential_generate_cell_to_particle_map_with_positions :: < I , R > ( & grid, particle_positions) ;
375375
376376 {
377377 neighborhood_list. neighbor_ptr . clear ( ) ;
@@ -382,11 +382,12 @@ pub fn neighborhood_search_spatial_hashing_flat_filtered<I: Index, R: Real>(
382382 }
383383
384384 {
385- profile ! ( "write particle neighbors" ) ;
386- let mut counter = 0 ;
385+ profile ! ( "collect particle neighbors" ) ;
386+ let mut cell_buffer = Vec :: with_capacity ( 27 ) ;
387+ let mut candidate_buffer = Vec :: with_capacity ( 1000 ) ;
387388 for ( particle_i, pos_i) in particle_positions. iter ( ) . enumerate ( ) {
388- // Store start of current particle neighbor list
389- neighborhood_list. neighbor_ptr [ particle_i] = counter ;
389+ // Store start index of the current particle neighbor list
390+ neighborhood_list. neighbor_ptr [ particle_i] = neighborhood_list . neighbors . len ( ) ;
390391
391392 if !filter ( particle_i) {
392393 continue ;
@@ -395,45 +396,48 @@ pub fn neighborhood_search_spatial_hashing_flat_filtered<I: Index, R: Real>(
395396 // Cell of the current particle
396397 let current_cell = grid. get_cell ( grid. enclosing_cell ( pos_i) ) . unwrap ( ) ;
397398
398- let mut neighbor_test = |particle_j| {
399- let pos_j: & Vector3 < R > = & particle_positions[ particle_j] ;
400- if ( pos_j - pos_i) . norm_squared ( ) < search_radius_squared {
401- // A neighbor was found
402- neighborhood_list. neighbors . push ( particle_j) ;
403- counter += 1 ;
404- }
405- } ;
399+ // Collect indices of cells to check
400+ cell_buffer. clear ( ) ;
401+ cell_buffer. extend (
402+ grid. cells_adjacent_to_cell ( & current_cell)
403+ . chain ( std:: iter:: once ( current_cell) )
404+ . map ( |c| grid. flatten_cell_index ( & c) ) ,
405+ ) ;
406406
407- // Loop over adjacent cells
408- grid. cells_adjacent_to_cell ( & current_cell)
409- . filter_map ( |c| {
410- let flat_cell_index = grid. flatten_cell_index ( & c) ;
411- particles_per_cell. get ( & flat_cell_index)
412- } )
413- . flatten ( )
414- . copied ( )
415- . for_each ( & mut neighbor_test) ;
416-
417- // Loop over current cell
418- std:: iter:: once ( current_cell)
419- . filter_map ( |c| {
420- let flat_cell_index = grid. flatten_cell_index ( & c) ;
421- particles_per_cell. get ( & flat_cell_index)
422- } )
423- . flatten ( )
424- . copied ( )
425- . for_each ( |particle_j| {
426- if particle_i != particle_j {
427- neighbor_test ( particle_j) ;
407+ // Compute the total number of particles that are neighbor candidates
408+ let num_candidates: usize = cell_buffer
409+ . iter ( )
410+ . filter_map ( |c| particles_per_cell. get ( & c) )
411+ . map ( |( indices, _) | indices. len ( ) )
412+ . sum ( ) ;
413+ candidate_buffer. resize ( num_candidates, ( 0 , R :: zero ( ) ) ) ;
414+
415+ // Compute distances to all neighbor candidates
416+ let mut counter = 0 ;
417+ cell_buffer
418+ . iter ( )
419+ . filter_map ( |c| particles_per_cell. get ( & c) )
420+ . for_each ( |( indices, positions) | {
421+ for ( & particle_j, pos_j) in indices. iter ( ) . zip ( positions. iter ( ) ) {
422+ candidate_buffer[ counter] = ( particle_j, ( pos_j - pos_i) . norm_squared ( ) ) ;
423+ counter += 1 ;
428424 }
429425 } ) ;
426+
427+ // Filter for particles that are actually neighbors
428+ neighborhood_list. neighbors . extend (
429+ candidate_buffer
430+ . iter ( )
431+ . filter ( |( idx, dist) | * idx != particle_i && * dist < search_radius_squared)
432+ . map ( |( idx, _) | idx) ,
433+ ) ;
430434 }
431435 // Store end of the last neighbor list
432- * neighborhood_list. neighbor_ptr . last_mut ( ) . unwrap ( ) = counter ;
436+ * neighborhood_list. neighbor_ptr . last_mut ( ) . unwrap ( ) = neighborhood_list . neighbors . len ( ) ;
433437 }
434438}
435439
436- /// Performs a neighborhood search (multi-threaded implementation)
440+ /// Performs a neighborhood search (multithreaded implementation)
437441///
438442/// Returns the indices of all neighboring particles in the given search radius per particle as a `Vec<Vec<usize>>`.
439443#[ inline( never) ]
@@ -641,7 +645,7 @@ pub fn compute_neigborhood_stats(neighborhood_list: &Vec<Vec<usize>>) -> Neighbo
641645 }
642646}
643647
644- // Generates a map for spatially hashed indices of all particles (map from cell -> enclosed particles)
648+ /// Generates a map for spatially hashed indices of all particles (map from cell -> enclosed particles)
645649#[ inline( never) ]
646650fn sequential_generate_cell_to_particle_map < I : Index , R : Real > (
647651 grid : & UniformGrid < I , R > ,
@@ -670,6 +674,41 @@ fn sequential_generate_cell_to_particle_map<I: Index, R: Real>(
670674 particles_per_cell
671675}
672676
677+ /// Generates a map for spatially hashed indices and positions of all particles (map from cell -> enclosed particles)
678+ #[ inline( never) ]
679+ fn sequential_generate_cell_to_particle_map_with_positions < I : Index , R : Real > (
680+ grid : & UniformGrid < I , R > ,
681+ particle_positions : & [ Vector3 < R > ] ,
682+ ) -> MapType < I , ( Vec < usize > , Vec < Vector3 < R > > ) > {
683+ profile ! ( "sequential_generate_cell_to_particle_map_with_positions" ) ;
684+ let mut particles_per_cell = new_map ( ) ;
685+
686+ // Compute average particle density for initial cell capacity
687+ let cell_dims = grid. cells_per_dim ( ) ;
688+ let n_cells = cell_dims[ 0 ] * cell_dims[ 1 ] * cell_dims[ 2 ] ;
689+ let avg_density = particle_positions. len ( ) / n_cells. to_usize ( ) . unwrap_or ( 1 ) ;
690+
691+ // Assign all particles to enclosing cells
692+ for ( particle_i, particle) in particle_positions. iter ( ) . enumerate ( ) {
693+ let cell_ijk = grid. enclosing_cell ( particle) ;
694+ let cell = grid. get_cell ( cell_ijk) . unwrap ( ) ;
695+ let flat_cell_index = grid. flatten_cell_index ( & cell) ;
696+
697+ let ( cell_indices, cell_positions) = particles_per_cell
698+ . entry ( flat_cell_index)
699+ . or_insert_with ( || {
700+ (
701+ Vec :: with_capacity ( avg_density) ,
702+ Vec :: with_capacity ( avg_density) ,
703+ )
704+ } ) ;
705+ cell_indices. push ( particle_i) ;
706+ cell_positions. push ( particle. clone ( ) ) ;
707+ }
708+
709+ particles_per_cell
710+ }
711+
673712#[ inline( never) ]
674713fn parallel_generate_cell_to_particle_map < I : Index , R : Real > (
675714 grid : & UniformGrid < I , R > ,
0 commit comments