Skip to content

Commit 096cab2

Browse files
committed
Improve performance of flat neighborhood search
1 parent 04f9ce7 commit 096cab2

File tree

1 file changed

+76
-37
lines changed

1 file changed

+76
-37
lines changed

splashsurf_lib/src/neighborhood_search.rs

Lines changed: 76 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -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)]
646650
fn 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)]
674713
fn parallel_generate_cell_to_particle_map<I: Index, R: Real>(
675714
grid: &UniformGrid<I, R>,

0 commit comments

Comments
 (0)