api_device_grid_search Last update: 17.07.2025 1#pragma once 2 3#include <rocky20/core/utils/rocky_math.hpp> 4 5#include <cstddef> 6#include <vector_types.h> 7 8using namespace rocky::vector; 9 10template <typename NeighborhoodOperator> 11__host__ __device__ size_t iterate_over_grid_cells( 12 NeighborhoodOperator& neighborhood, 13 const size_t* grid_starting_index, 14 const size_t* grid_to_original_map, 15 const longlong3& block_start, 16 const longlong3& block_end, 17 const longlong3& grid_size, 18 const double3& home_position, 19 const size_t home_entity_id, 20 const double search_radius, 21 const bool not_in_block) 22{ 23 const double squared_search_radius = search_radius * search_radius; 24 const size_t grid_volume = grid_size.x * grid_size.y * grid_size.z; 25 const size_t near_count = neighborhood.get_near_count(); 26 size_t n_thread_vicinities = 0; 27 for (size_t x = block_start.x; x < block_end.x; ++x) 28 { 29 const size_t x_offset = x * grid_size.y * grid_size.z; 30 for (size_t y = block_start.y; y < block_end.y; ++y) 31 { 32 const size_t xy_offset = x_offset + (y * grid_size.z); 33 const size_t first_cell_id = xy_offset + block_start.z; 34 const size_t last_cell_id = xy_offset + block_end.z; 35 const size_t index_start = grid_starting_index[first_cell_id]; 36 const size_t index_end = 37 (last_cell_id < grid_volume) ? grid_starting_index[last_cell_id] : near_count; 38 39 for (size_t prt_id_grid = index_start; prt_id_grid < index_end; ++prt_id_grid) 40 { 41 const size_t near_entity_id = grid_to_original_map[prt_id_grid]; 42 if (neighborhood.is_valid(home_entity_id, near_entity_id, not_in_block)) 43 { 44 const double3 near_position = neighborhood.get_near_centroid(near_entity_id); 45 const double squared_distance = rocky::vector::dot(home_position - near_position); 46 if (squared_distance <= squared_search_radius) 47 { 48 neighborhood.operate(home_entity_id, near_entity_id); 49 n_thread_vicinities++; 50 } 51 } 52 } 53 } 54 } 55 return n_thread_vicinities; 56} 57 58template <typename NeighborhoodOperator, typename GridStartingIndex> 59__host__ __device__ size_t iterate_over_grid_cells_2( 60 NeighborhoodOperator& neighborhood, 61 const GridStartingIndex grid_starting_index, 62 const unsigned int* __restrict__ grid_to_original_map, 63 const uint3& block_start, 64 const uint3& block_end, 65 const uint3& grid_size, 66 const unsigned int home_entity_id, 67 const bool not_in_block) 68{ 69 const size_t grid_volume = size_t(grid_size.x) * grid_size.y * grid_size.z; 70 const unsigned int near_count = neighborhood.get_near_count(); 71 unsigned int n_thread_vicinities = 0; 72 for (unsigned int x = block_start.x; x < block_end.x; ++x) 73 { 74 const size_t x_offset = size_t(x) * grid_size.y * grid_size.z; 75 for (unsigned int y = block_start.y; y < block_end.y; ++y) 76 { 77 const size_t xy_offset = x_offset + (size_t(y) * grid_size.z); 78 const size_t first_cell_id = xy_offset + block_start.z; 79 const size_t last_cell_id = xy_offset + block_end.z; 80 unsigned int index_start, index_end; 81 if constexpr (std::is_pointer_v<GridStartingIndex>) 82 { 83 index_start = grid_starting_index[first_cell_id]; 84 index_end = 85 (last_cell_id < grid_volume) ? grid_starting_index[last_cell_id] : near_count; 86 } 87 else 88 { 89 grid_starting_index.get_range( 90 first_cell_id, last_cell_id, index_start, index_end, grid_volume); 91 } 92 93 for (unsigned int prt_id_grid = index_start; prt_id_grid < index_end; ++prt_id_grid) 94 { 95 const unsigned int near_entity_id = grid_to_original_map[prt_id_grid]; 96 if (neighborhood.is_valid(home_entity_id, near_entity_id, not_in_block)) 97 { 98 neighborhood.operate(home_entity_id, near_entity_id); 99 n_thread_vicinities++; 100 } 101 } 102 } 103 } 104 return n_thread_vicinities; 105} 106 123template <typename Backend, typename NeighborhoodOperator> 124__host__ __device__ void travel_vicinities_outsider( 125 NeighborhoodOperator& neighborhood, 126 const size_t* grid_starting_index, 127 const size_t* grid_to_original_map, 128 const double cell_size, 129 const double search_radius, 130 const longlong3 grid_size, 131 double3 grid_start, 132 size_t* n_vicinities) 133{ 134 auto clamp_to_grid = [&grid_size](const longlong3& position) -> longlong3 { 135 constexpr longlong3 origin { 0, 0, 0 }; 136 return clamp_vector(position, origin, grid_size); 137 }; 138 139 // reduce grid start by an epsilon to avoid summing it on every position calculation 140 grid_start -= (cell_size * 0.01); 141 const double cell_size_inverse = 1.0 / cell_size; 142 size_t n_thread_vicinities = 0; 143 int start, step, stop; 144 145 Backend::ThreadIteration(start, step, stop, neighborhood.get_home_count()); 146 for (size_t home_entity_id = start; home_entity_id < stop; home_entity_id += step) 147 { 148 const double3 home_position = neighborhood.get_home_centroid(home_entity_id); 149 const auto home_grid_position { rocky::vector::cast<longlong3>( 150 (home_position - grid_start) * cell_size_inverse) }; 151 152 const longlong3 block_start = clamp_to_grid(home_grid_position - 2); 153 const longlong3 block_end = clamp_to_grid(home_grid_position + 3); 154 155 n_thread_vicinities += iterate_over_grid_cells( 156 neighborhood, 157 grid_starting_index, 158 grid_to_original_map, 159 block_start, 160 block_end, 161 grid_size, 162 home_position, 163 home_entity_id, 164 search_radius, 165 false); 166 } 167 if (n_vicinities != nullptr && n_thread_vicinities > 0) 168 { 169 Backend::AtomicAdd(n_vicinities, n_thread_vicinities); 170 } 171} 172 189template <typename Backend, typename NeighborhoodOperator> 190__host__ __device__ void travel_vicinities_insider( 191 NeighborhoodOperator& neighborhood, 192 const size_t* grid_starting_index, 193 const size_t* grid_to_original_map, 194 const double cell_size, 195 const double search_radius, 196 const longlong3 grid_size, 197 double3 grid_start, 198 size_t* n_vicinities) 199{ 200 // reduce grid start by an epsilon to avoid summing it on every position calculation 201 grid_start -= (cell_size * 0.01); 202 const double cell_size_inverse = 1.0 / cell_size; 203 size_t n_thread_vicinities = 0; 204 int start, step, stop; 205 206 double3 home_position; 207 size_t home_entity_id; 208 auto dispatch_on_block = [&](const longlong3& _block_start, 209 const longlong3& _block_end, 210 const bool different_cells = true) -> void { 211 n_thread_vicinities += iterate_over_grid_cells( 212 neighborhood, 213 grid_starting_index, 214 grid_to_original_map, 215 _block_start, 216 _block_end, 217 grid_size, 218 home_position, 219 home_entity_id, 220 search_radius, 221 different_cells); 222 }; 223 224 Backend::ThreadIteration(start, step, stop, neighborhood.get_home_count()); 225 for (size_t prt_id_grid = start; prt_id_grid < stop; prt_id_grid += step) 226 { 227 home_entity_id = grid_to_original_map[prt_id_grid]; 228 if (home_entity_id == INVALID_ENTITY_ID) 229 { 230 continue; 231 } 232 233 home_position = neighborhood.get_home_centroid(home_entity_id); 234 const auto home_coordinate { rocky::vector::cast<longlong3>( 235 (home_position - grid_start) * cell_size_inverse) }; 236 237 const longlong3 block_start { max_vector({ 0ll, 0ll, 0ll }, home_coordinate - 2) }; 238 const longlong3 block_diag { min_vector(grid_size, home_coordinate + 1) }; 239 const longlong3 block_end { min_vector(grid_size, home_coordinate + 3) }; 240 241 // xyz block above 242 dispatch_on_block({ block_diag.x, block_start.y, block_start.z }, block_end); 243 // right side of yz plane 244 dispatch_on_block( 245 { home_coordinate.x, block_diag.y, block_start.z }, 246 { block_diag.x, block_end.y, block_end.z }); 247 // all the way back to z end on this line 248 dispatch_on_block( 249 { home_coordinate.x, home_coordinate.y, block_diag.z }, 250 { block_diag.x, block_diag.y, block_end.z }); 251 // same grid cell. Can't skip element ID check to avoid duplicate vicinities. 252 dispatch_on_block(home_coordinate, block_diag, false); 253 } 254 if (n_vicinities != nullptr && n_thread_vicinities > 0) 255 { 256 Backend::AtomicAdd(n_vicinities, n_thread_vicinities); 257 } 258} 259 260template <typename Backend, typename NeighborhoodOperator, typename GridStartingIndex> 261__host__ __device__ unsigned int travel_vicinities_insider_full( 262 NeighborhoodOperator& neighborhood, 263 const GridStartingIndex grid_starting_index, 264 const unsigned int* __restrict__ grid_to_original_map, 265 const float cell_size, 266 const float search_radius, 267 const uint3& grid_size, 268 float3 grid_start) 269{ 270 const float epsilon = cell_size * 0.01f; 271 const float cell_size_inverse = 1.f / cell_size; 272 const unsigned int steps = std::ceil(search_radius / cell_size); 273 unsigned int n_thread_vicinities = 0; 274 int start, step, stop; 275 276 unsigned int home_entity_id; 277 auto dispatch_on_block = [&](const uint3& _block_start, 278 const uint3& _block_end, 279 const bool different_cells = true) -> void { 280 n_thread_vicinities += iterate_over_grid_cells_2( 281 neighborhood, 282 grid_starting_index, 283 grid_to_original_map, 284 _block_start, 285 _block_end, 286 grid_size, 287 home_entity_id, 288 different_cells); 289 }; 290 291 Backend::ThreadIteration(start, step, stop, neighborhood.get_home_count()); 292 for (home_entity_id = start; home_entity_id < stop; home_entity_id += step) 293 { 294 neighborhood.start_element(home_entity_id); 295 296 const auto home_coordinate = rocky::vector::cast<uint3>( 297 ((neighborhood.get_home_centroid(home_entity_id) - grid_start) + epsilon) 298 * cell_size_inverse); 299 300 const uint3 block_start { max_vector(uint3 { steps, steps, steps }, home_coordinate) - steps }; 301 const uint3 block_diag { min_vector(grid_size, home_coordinate + 1) }; 302 const uint3 block_end { min_vector(grid_size, home_coordinate + (steps + 1)) }; 303 304 // xyz block above 305 dispatch_on_block({ block_diag.x, block_start.y, block_start.z }, block_end); 306 // right side of yz plane 307 dispatch_on_block( 308 { home_coordinate.x, block_diag.y, block_start.z }, 309 { block_diag.x, block_end.y, block_end.z }); 310 // all the way back to z end on this line 311 dispatch_on_block( 312 { home_coordinate.x, home_coordinate.y, block_diag.z }, 313 { block_diag.x, block_diag.y, block_end.z }); 314 // same grid cell. Can't skip element ID check to avoid duplicate vicinities. 315 dispatch_on_block(home_coordinate, block_diag, false); 316 317 neighborhood.finish_element(home_entity_id); 318 } 319 return n_thread_vicinities; 320}