Shamrock 2025.10.0
Astrophysical Code
Loading...
Searching...
No Matches
BlockNeighToCellNeigh.cpp
Go to the documentation of this file.
1// -------------------------------------------------------//
2//
3// SHAMROCK code for hydrodynamics
4// Copyright (c) 2021-2026 Timothée David--Cléris <tim.shamrock@proton.me>
5// SPDX-License-Identifier: CeCILL Free Software License Agreement v2.1
6// Shamrock is licensed under the CeCILL 2.1 License, see LICENSE for more information
7//
8// -------------------------------------------------------//
9
20#include "shammath/AABB.hpp"
29
31
32 // here if we want to find the stencil instead of just the common face, we can just the change
33 // lowering mask to the cube offseted in the wanted direction instead of just checking if we
34 // have a common surface
35
36 // like on the above case with block Nside 2 we get 12^3 - 12^2 = 1584 link count
37 // it is a really good possible test
38
39 template<class Tvec, class TgridVec, class Tmorton>
40 template<class AMRBlock>
41 class BlockNeighToCellNeigh<Tvec, TgridVec, Tmorton>::AMRLowering {
42 public:
43 AMRGraph &block_graph;
44 sham::DeviceBuffer<TgridVec> &buf_block_min;
45 sham::DeviceBuffer<TgridVec> &buf_block_max;
46 TgridVec dir_offset;
47
48 AMRLowering(
49 AMRGraph &block_graph,
50 sham::DeviceBuffer<TgridVec> &buf_block_min,
51 sham::DeviceBuffer<TgridVec> &buf_block_max,
52 TgridVec dir_offset)
53 : block_graph(block_graph), buf_block_min(buf_block_min), buf_block_max(buf_block_max),
54 dir_offset(dir_offset) {}
55
56 struct ro_acces;
57 inline ro_acces get_read_access(sham::EventList &e) {
58 return {
59 block_graph.get_read_access(e),
60 buf_block_min.get_read_access(e),
61 buf_block_max.get_read_access(e),
62 dir_offset};
63 }
64
65 void complete_event_state(sycl::event &e) {
66 block_graph.complete_event_state(e);
67 buf_block_min.complete_event_state(e);
68 buf_block_max.complete_event_state(e);
69 }
70
71 struct ro_acces {
72
73 AMRGraph::ro_access graph_iter;
74
75 const TgridVec *acc_block_min;
76 const TgridVec *acc_block_max;
77
78 TgridVec dir_offset;
79
80 template<class IndexFunctor>
81 void for_each_other_index_safe(u32 id_a, IndexFunctor &&fct) const {
82
83 const u32 cell_global_id = (u32) id_a;
84
85 const u32 block_id = cell_global_id / AMRBlock::block_size;
86 const u32 cell_loc_id = cell_global_id % AMRBlock::block_size;
87
88 // fetch current block info
89 const TgridVec cblock_min = acc_block_min[block_id];
90 const TgridVec cblock_max = acc_block_max[block_id];
91 const TgridVec delta_cell = (cblock_max - cblock_min) / AMRBlock::Nside;
92
93 // Compute wanted neighbourg cell bounds
94 auto get_cell_local_coord = [&]() -> TgridVec {
95 std::array<u32, 3> lcoord_arr = AMRBlock::get_coord(cell_loc_id);
96 return {lcoord_arr[0], lcoord_arr[1], lcoord_arr[2]};
97 };
98
99 TgridVec lcoord = get_cell_local_coord();
100
101 shammath::AABB<TgridVec> current_cell_aabb
102 = {cblock_min + lcoord * delta_cell,
103 cblock_min + (lcoord + TgridVec{1, 1, 1}) * delta_cell};
104
105 const shammath::AABB<TgridVec> current_cell_aabb_shifted
106 = {current_cell_aabb.lower + dir_offset, current_cell_aabb.upper + dir_offset};
107
108 auto for_each_possible_blocks = [&](auto &&functor) {
109 functor(block_id);
110 graph_iter.for_each_object_link(block_id, [&](u32 block_b) {
111 functor(block_b);
112 });
113 };
114
115 for_each_possible_blocks([&](u32 block_b) {
116 TgridVec block_b_min = acc_block_min[block_b];
117 TgridVec block_b_max = acc_block_max[block_b];
118
119 const TgridVec delta_cell_b = (block_b_max - block_b_min) / AMRBlock::Nside;
120
121 for (u32 lx = 0; lx < AMRBlock::Nside; lx++) {
122 for (u32 ly = 0; ly < AMRBlock::Nside; ly++) {
123 for (u32 lz = 0; lz < AMRBlock::Nside; lz++) {
124
125 shammath::AABB<TgridVec> found_cell
126 = {TgridVec{block_b_min + TgridVec{lx, ly, lz} * delta_cell_b},
127 TgridVec{
128 block_b_min
129 + TgridVec{lx + 1, ly + 1, lz + 1} * delta_cell_b}};
130
131 u32 idx = block_b * AMRBlock::block_size
132 + AMRBlock::get_index({lx, ly, lz});
133
134 bool overlap = found_cell.get_intersect(current_cell_aabb_shifted)
136 && id_a != idx;
137
138 if (overlap) {
139 fct(idx);
140 }
141 }
142 }
143 }
144 });
145 }
146
147 template<class IndexFunctor>
148 void for_each_other_index_full(u32 id_a, IndexFunctor &&fct) const {
149
150 const u32 cell_global_id = (u32) id_a;
151
152 const u32 block_id = cell_global_id / AMRBlock::block_size;
153 const u32 cell_loc_id = cell_global_id % AMRBlock::block_size;
154
155 // fetch current block info
156 const TgridVec cblock_min = acc_block_min[block_id];
157 const TgridVec cblock_max = acc_block_max[block_id];
158 const TgridVec delta_cell = (cblock_max - cblock_min) / AMRBlock::Nside;
159
160 // Compute wanted neighbourg cell bounds
161 auto get_cell_local_coord = [&]() -> TgridVec {
162 std::array<u32, 3> lcoord_arr = AMRBlock::get_coord(cell_loc_id);
163 return {lcoord_arr[0], lcoord_arr[1], lcoord_arr[2]};
164 };
165
166 const TgridVec lcoord = get_cell_local_coord();
167
168 const shammath::AABB<TgridVec> current_cell_aabb
169 = {cblock_min + lcoord * delta_cell,
170 cblock_min + (lcoord + TgridVec{1, 1, 1}) * delta_cell};
171
172 const shammath::AABB<TgridVec> current_cell_aabb_shifted
173 = {current_cell_aabb.lower + dir_offset * delta_cell,
174 current_cell_aabb.upper + dir_offset * delta_cell};
175
176 // by default we assume that we are in our block
177 // the next function checks if the wanted block is in another blocks
178 TgridVec wanted_block_min = cblock_min;
179 TgridVec wanted_block_max = cblock_max;
180 u32 wanted_block = block_id;
181
182 graph_iter.for_each_object_link(block_id, [&](u32 block_b) {
183 TgridVec int_wanted_block_min = acc_block_min[block_b];
184 TgridVec int_wanted_block_max = acc_block_max[block_b];
185
186 bool overlap
187 = shammath::AABB<TgridVec>{int_wanted_block_min, int_wanted_block_max}
188 .get_intersect(current_cell_aabb_shifted)
190
191 if (overlap) {
192 wanted_block_min = int_wanted_block_min;
193 wanted_block_max = int_wanted_block_max;
194 wanted_block = block_b;
195 }
196 });
197
198 bool overlap = shammath::AABB<TgridVec>{wanted_block_min, wanted_block_max}
199 .get_intersect(current_cell_aabb_shifted)
201
202 if (!overlap) {
203 return;
204 }
205
206 const TgridVec wanted_block_delta_cell
207 = (wanted_block_max - wanted_block_min) / AMRBlock::Nside;
208
209 // at this point the block having the wanted neighbour is in `wanted_block`
210 // now we need to find the local coordinates within wanted block of
211 // `current_cell_aabb_shifted`, this will give away the indexes
212
213 TgridVec wanted_block_current_cell_shifted
214 = current_cell_aabb_shifted.lower - wanted_block_min;
215
216 std::array<u32, 3> wanted_block_index_range_min
217 = {u32(wanted_block_current_cell_shifted.x() / wanted_block_delta_cell.x()),
218 u32(wanted_block_current_cell_shifted.y() / wanted_block_delta_cell.y()),
219 u32(wanted_block_current_cell_shifted.z() / wanted_block_delta_cell.z())};
220
221 std::array<u32, 3> wanted_block_index_range_max
222 = {u32((wanted_block_current_cell_shifted.x() + delta_cell.x())
223 / wanted_block_delta_cell.x()),
224 u32((wanted_block_current_cell_shifted.y() + delta_cell.y())
225 / wanted_block_delta_cell.y()),
226 u32((wanted_block_current_cell_shifted.z() + delta_cell.z())
227 / wanted_block_delta_cell.z())};
228
229 // now if range size < 1 expand to 1 (case where wanted block is larger)
230 if (wanted_block_index_range_max[0] - wanted_block_index_range_min[0] < 1)
231 wanted_block_index_range_max[0] = wanted_block_index_range_min[0] + 1;
232 if (wanted_block_index_range_max[1] - wanted_block_index_range_min[1] < 1)
233 wanted_block_index_range_max[1] = wanted_block_index_range_min[1] + 1;
234 if (wanted_block_index_range_max[2] - wanted_block_index_range_min[2] < 1)
235 wanted_block_index_range_max[2] = wanted_block_index_range_min[2] + 1;
236
237 for (u32 x = wanted_block_index_range_min[0]; x < wanted_block_index_range_max[0];
238 x++) {
239 for (u32 y = wanted_block_index_range_min[1];
240 y < wanted_block_index_range_max[1];
241 y++) {
242 for (u32 z = wanted_block_index_range_min[2];
243 z < wanted_block_index_range_max[2];
244 z++) {
245
246 shammath::AABB<TgridVec> found_cell = {
247 TgridVec{wanted_block_min + TgridVec{x, y, z} * delta_cell},
248 TgridVec{
249 wanted_block_min + TgridVec{x + 1, y + 1, z + 1} * delta_cell}};
250
251 bool overlap = found_cell.get_intersect(current_cell_aabb_shifted)
253
254 if (overlap) {
255 u32 idx = wanted_block * AMRBlock::block_size
256 + AMRBlock::get_index({x, y, z});
257
258 fct(idx);
259 }
260 }
261 }
262 }
263 }
264
265 template<class IndexFunctor>
266 void for_each_other_index(u32 id_a, IndexFunctor &&fct) const {
267 // Possible performance regression here, ideally i should fix the full mode for AMR
268 // as i expect it to outperform the safe one
269
270 // for_each_other_index_full(id_a, fct);
271 for_each_other_index_safe(id_a, fct);
272 }
273 };
274 };
275
276 template<class Tvec, class TgridVec, class Tmorton>
278 StackEntry stack_loc{};
279 auto edges = get_edges();
280
281 edges.spans_block_min.check_sizes(edges.sizes.indexes);
282 edges.spans_block_max.check_sizes(edges.sizes.indexes);
283
284 using AMRBlock = amr::AMRBlock<Tvec, TgridVec, 1>;
285
286 if (block_nside_pow != 1) {
287 shambase::throw_unimplemented("block_nside_pow != 1");
288 }
289
291
292 edges.block_neigh_graph.graph.for_each([&](u64 id,
293 const OrientedAMRGraph &oriented_block_graph) {
294 OrientedAMRGraph result;
295
296 PatchDataField<TgridVec> &block_min = edges.spans_block_min.get_refs().get(id);
297 PatchDataField<TgridVec> &block_max = edges.spans_block_max.get_refs().get(id);
298
299 sham::DeviceBuffer<TgridVec> &buf_block_min = block_min.get_buf();
300 sham::DeviceBuffer<TgridVec> &buf_block_max = block_max.get_buf();
301
302 for (u32 dir = 0; dir < 6; dir++) {
303
304 TgridVec dir_offset = result.offset_check[dir];
305
306 AMRGraph &block_graph
307 = shambase::get_check_ref(oriented_block_graph.graph_links[dir]);
308
309 u32 cell_count = (edges.sizes.indexes.get(id)) * AMRBlock::block_size;
310
311 AMRGraph rslt = details::compute_neigh_graph<AMRLowering<AMRBlock>>(
312 shamsys::instance::get_compute_scheduler_ptr(),
313 cell_count,
314 block_graph,
315 buf_block_min,
316 buf_block_max,
317 dir_offset);
318
319 shamlog_debug_ln(
320 "AMR Cell Graph", "Patch", id, "direction", dir, "link cnt", rslt.link_count);
321
322 std::unique_ptr<AMRGraph> tmp_graph = std::make_unique<AMRGraph>(std::move(rslt));
323
324 result.graph_links[dir] = std::move(tmp_graph);
325 }
326
327 cell_graph_links.add_obj(id, std::move(result));
328 });
329
330 shamlog_debug_ln("[AMR cell graph]", "compute antecedent map");
331 cell_graph_links.for_each([&](u64 id, OrientedAMRGraph &oriented_block_graph) {
332 auto ptr = shamsys::instance::get_compute_scheduler_ptr();
333 u32 cell_count = (edges.sizes.indexes.get(id)) * AMRBlock::block_size;
334 for (u32 dir = 0; dir < 6; dir++) {
335 oriented_block_graph.graph_links[dir]->compute_antecedent(ptr);
336 }
337 });
338
339 edges.cell_neigh_graph.graph = std::move(cell_graph_links);
340 }
341
342 template<class Tvec, class TgridVec, class Tmorton>
344
345 std::string sizes = get_ro_edge_base(0).get_tex_symbol();
346 std::string block_min = get_ro_edge_base(1).get_tex_symbol();
347 std::string block_max = get_ro_edge_base(2).get_tex_symbol();
348 std::string block_neigh_graph = get_ro_edge_base(3).get_tex_symbol();
349 std::string cell_neigh_graph = get_rw_edge_base(0).get_tex_symbol();
350
351 std::string tex = R"tex(
352 Find neighbour blocks
353
354 \begin{align}
355 {cell_neigh_graph} &= \text{BlockNeighToCellNeigh}({block_neigh_graph}, {sizes}, {block_min}, {block_max})
356 \end{align}
357 )tex";
358
359 shambase::replace_all(tex, "{sizes}", sizes);
360 shambase::replace_all(tex, "{block_min}", block_min);
361 shambase::replace_all(tex, "{block_max}", block_max);
362 shambase::replace_all(tex, "{block_neigh_graph}", block_neigh_graph);
363 shambase::replace_all(tex, "{cell_neigh_graph}", cell_neigh_graph);
364
365 return tex;
366 }
367
368} // namespace shammodels::basegodunov::modules
369
utility to manipulate AMR blocks
Field variant object to instanciate a variant on the patch types.
Header file describing a Node Instance.
std::uint32_t u32
32 bit unsigned integer
std::uint64_t u64
64 bit unsigned integer
A buffer allocated in USM (Unified Shared Memory)
void complete_event_state(sycl::event e) const
Complete the event state of the buffer.
const T * get_read_access(sham::EventList &depends_list, SourceLocation src_loc=SourceLocation{}) const
Get a read-only pointer to the buffer's data.
Class to manage a list of SYCL events.
Definition EventList.hpp:31
Represents a collection of objects distributed across patches identified by a u64 id.
iterator add_obj(u64 id, T &&obj)
Adds a new object to the collection.
void for_each(std::function< void(u64, T &)> &&f)
Applies a function to each object in the collection.
virtual std::string _impl_get_tex() const
get the tex of the node
This header file contains utility functions related to exception handling in the code.
void replace_all(std::string &inout, std::string_view what, std::string_view with)
replace all occurence of a search string with another
Definition string.hpp:183
T & get_check_ref(const std::unique_ptr< T > &ptr, SourceLocation loc=SourceLocation())
Takes a std::unique_ptr and returns a reference to the object it holds. It throws a std::runtime_erro...
Definition memory.hpp:110
void throw_unimplemented(SourceLocation loc=SourceLocation{})
Throw a std::runtime_error saying that the function is unimplemented.
namespace for the basegodunov model modules
Axis-Aligned bounding box.
Definition AABB.hpp:99
bool is_volume_not_null() const noexcept
Checks if the AABB has a non-zero volume.
Definition AABB.hpp:280
T lower
Lower bound of the AABB.
Definition AABB.hpp:104
T upper
Upper bound of the AABB.
Definition AABB.hpp:105
AABB get_intersect(AABB other) const noexcept
Compute the intersection of two AABB.
Definition AABB.hpp:234
utility class to handle AMR blocks
Definition AMRBlock.hpp:35