Shamrock 2025.10.0
Astrophysical Code
Loading...
Searching...
No Matches
GhostZones.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
18#include "shambase/memory.hpp"
20#include "shambase/string.hpp"
21#include "shamalgs/numeric.hpp"
23#include "shammath/AABB.hpp"
30
38 template<class Tvec, class TgridVec>
40
41 using namespace shamrock::patch;
42 using namespace shammath;
43
46 using InterfaceBuildInfos = typename GZData::InterfaceBuildInfos;
47 using GeneratorMap = typename GZData::GeneratorMap;
48
49 StackEntry stack_loc{};
50
51 i32 repetition_x = 1;
52 i32 repetition_y = 1;
53 i32 repetition_z = 1;
54
55 GeneratorMap results;
56
57 shamrock::patch::SimulationBoxInfo &sim_box = sched.get_sim_box();
58
59 PatchCoordTransform<TgridVec> patch_coord_transf = sim_box.get_patch_transform<TgridVec>();
60 TgridVec bsize = sim_box.get_bounding_box_size<TgridVec>();
61
62 for (i32 xoff = -repetition_x; xoff <= repetition_x; xoff++) {
63 for (i32 yoff = -repetition_y; yoff <= repetition_y; yoff++) {
64 for (i32 zoff = -repetition_z; zoff <= repetition_z; zoff++) {
65
66 // sender translation
67 TgridVec periodic_offset
68 = TgridVec{xoff * bsize.x(), yoff * bsize.y(), zoff * bsize.z()};
69
70 sched.for_each_local_patch([&](const Patch &psender) {
71 CoordRange<TgridVec> sender_bsize
72 = patch_coord_transf.to_obj_coord(psender);
73 CoordRange<TgridVec> sender_bsize_off
74 = sender_bsize.add_offset(periodic_offset);
75
76 shammath::AABB<TgridVec> sender_bsize_off_aabb{
77 sender_bsize_off.lower, sender_bsize_off.upper};
78
79 using PtNode = typename SerialPatchTree<TgridVec>::PtNode;
80
81 shamlog_debug_sycl_ln(
82 "AMR:interf",
83 "find_interfaces -",
84 psender.id_patch,
85 sender_bsize_off_aabb.lower,
86 sender_bsize_off_aabb.upper);
87
88 sptree.host_for_each_leafs(
89 [&](u64 tree_id, PtNode n) {
90 shammath::AABB<TgridVec> tree_cell{n.box_min, n.box_max};
91
92 bool result
93 = tree_cell.get_intersect(sender_bsize_off_aabb).is_not_empty();
94
95 return result;
96 },
97 [&](u64 id_found, PtNode n) {
98 if ((id_found == psender.id_patch) && (xoff == 0) && (yoff == 0)
99 && (zoff == 0)) {
100 return;
101 }
102
103 InterfaceBuildInfos ret{
104 periodic_offset,
105 {xoff, yoff, zoff},
107 n.box_min - periodic_offset, n.box_max - periodic_offset}};
108
109 results.add_obj(psender.id_patch, id_found, std::move(ret));
110 });
111 });
112 }
113 }
114 }
115
116 return results;
117 }
118} // namespace shammodels::zeus::modules
119
120template<class Tvec, class TgridVec>
122
123 StackEntry stack_loc{};
124
125 using GZData = GhostZonesData<Tvec, TgridVec>;
126
127 storage.ghost_zone_infos.set(GZData{});
128 GZData &gen_ghost = storage.ghost_zone_infos.get();
129
130 // get ids of cells that will be on the surface of another patch.
131 // for cells corresponding to fixed boundary they will be generated after the exhange
132 // and appended to the interface list a poosteriori
133
134 gen_ghost.ghost_gen_infos
135 = find_interfaces<Tvec, TgridVec>(scheduler(), storage.serial_patch_tree.get());
136
137 using InterfaceBuildInfos = typename GZData::InterfaceBuildInfos;
138 using InterfaceIdTable = typename GZData::InterfaceIdTable;
139
140 // if(logger::log_debug);
141 gen_ghost.ghost_gen_infos.for_each([&](u64 sender, u64 receiver, InterfaceBuildInfos &build) {
142 std::string log;
143
144 log = shambase::format(
145 "{} -> {} : off = {}, {} -> {}",
146 sender,
147 receiver,
148 build.offset,
149 build.volume_target.lower,
150 build.volume_target.upper);
151
152 shamlog_debug_ln("AMRZeus", log);
153 });
154
155 sham::DeviceQueue &q = shamsys::instance::get_compute_scheduler().get_queue();
156
157 gen_ghost.ghost_gen_infos.for_each([&](u64 sender, u64 receiver, InterfaceBuildInfos &build) {
158 shamrock::patch::PatchDataLayer &src = scheduler().patch_data.get_pdat(sender);
159
160 sycl::buffer<u32> is_in_interf{src.get_obj_cnt()};
161
162 sham::EventList depends_list;
163
164 auto cell_min = src.get_field_buf_ref<TgridVec>(0).get_read_access(depends_list);
165 auto cell_max = src.get_field_buf_ref<TgridVec>(1).get_read_access(depends_list);
166
167 auto e = q.submit(depends_list, [&](sycl::handler &cgh) {
168 sycl::accessor flag{is_in_interf, cgh, sycl::write_only, sycl::no_init};
169
170 shammath::AABB<TgridVec> check_volume = build.volume_target;
171
172 shambase::parallel_for(cgh, src.get_obj_cnt(), "check if in interf", [=](u32 id_a) {
173 flag[id_a] = shammath::AABB<TgridVec>(cell_min[id_a], cell_max[id_a])
174 .get_intersect(check_volume)
175 .is_not_empty();
176 });
177 });
178
179 src.get_field_buf_ref<TgridVec>(0).complete_event_state(e);
180 src.get_field_buf_ref<TgridVec>(1).complete_event_state(e);
181
182 auto resut = shamalgs::numeric::stream_compact(q.q, is_in_interf, src.get_obj_cnt());
183 f64 ratio = f64(std::get<1>(resut)) / f64(src.get_obj_cnt());
184
185 std::string s = shambase::format(
186 "{} -> {} : off = {}, test volume = {} -> {}",
187 sender,
188 receiver,
189 build.offset,
190 build.volume_target.lower,
191 build.volume_target.upper);
192 s += shambase::format("\n found N = {}, ratio = {} %", std::get<1>(resut), ratio);
193
194 shamlog_debug_ln("AMR interf", s);
195
196 std::unique_ptr<sycl::buffer<u32>> ids
197 = std::make_unique<sycl::buffer<u32>>(shambase::extract_value(std::get<0>(resut)));
198
199 gen_ghost.ghost_id_build_map.add_obj(
200 sender, receiver, InterfaceIdTable{build, std::move(ids), ratio});
201 });
202}
203
204template<class Tvec, class TgridVec>
206 GhostZones<Tvec, TgridVec>::communicate_pdat(
207 const std::shared_ptr<shamrock::patch::PatchDataLayerLayout> &pdl_ptr,
209 StackEntry stack_loc{};
210
212
214 shamalgs::collective::serialize_sparse_comm<shamrock::patch::PatchDataLayer>(
215 shamsys::instance::get_compute_scheduler_ptr(),
217 recv_dat,
218 [&](u64 id) {
219 return scheduler().get_patch_rank_owner(id);
220 },
222 shamalgs::SerializeHelper ser(shamsys::instance::get_compute_scheduler_ptr());
223 ser.allocate(pdat.serialize_buf_byte_size());
224 pdat.serialize_buf(ser);
225 return ser.finalize();
226 },
227 [&](sham::DeviceBuffer<u8> &&buf) {
228 // exchange the buffer held by the distrib data and give it to the serializer
230 shamsys::instance::get_compute_scheduler_ptr(),
231 std::forward<sham::DeviceBuffer<u8>>(buf));
232 return shamrock::patch::PatchDataLayer::deserialize_buf(ser, pdl_ptr);
233 },
234 cache);
235
236 return recv_dat;
237}
238
239template<class Tvec, class TgridVec>
240template<class T>
242 Tvec,
243 TgridVec>::communicate_pdat_field(shambase::DistributedDataShared<PatchDataField<T>> &&interf) {
244 StackEntry stack_loc{};
245
247
249
250 shamalgs::collective::serialize_sparse_comm<PatchDataField<T>>(
251 shamsys::instance::get_compute_scheduler_ptr(),
252 std::forward<shambase::DistributedDataShared<PatchDataField<T>>>(interf),
253 recv_dat,
254 [&](u64 id) {
255 return scheduler().get_patch_rank_owner(id);
256 },
257 [](PatchDataField<T> &pdat) {
258 shamalgs::SerializeHelper ser(shamsys::instance::get_compute_scheduler_ptr());
259 ser.allocate(pdat.serialize_full_byte_size());
260 pdat.serialize_buf(ser);
261 return ser.finalize();
262 },
263 [&](sham::DeviceBuffer<u8> &&buf) {
264 // exchange the buffer held by the distrib data and give it to the serializer
266 shamsys::instance::get_compute_scheduler_ptr(),
267 std::forward<sham::DeviceBuffer<u8>>(buf));
269 },
270 cache);
271
272 return recv_dat;
273}
274
275template<class Tvec, class TgridVec>
276template<class T, class Tmerged>
280 std::function<Tmerged(const shamrock::patch::Patch, shamrock::patch::PatchDataLayer &pdat)>
281 init,
282 std::function<void(Tmerged &, T &)> appender) {
283
284 StackEntry stack_loc{};
285
287
288 scheduler().for_each_patchdata_nonempty(
290 Tmerged tmp_merge = init(p, pdat);
291
292 interfs.for_each([&](u64 sender, u64 receiver, T &interface) {
293 if (receiver == p.id_patch) {
294 appender(tmp_merge, interface);
295 }
296 });
297
298 merge_f.add_obj(p.id_patch, std::move(tmp_merge));
299 });
300
301 return merge_f;
302}
303
304template<class Tvec, class TgridVec>
306
307 StackEntry stack_loc{};
308
309 shambase::Timer timer_interf;
310 timer_interf.start();
311
312 using namespace shamrock::patch;
313 using namespace shamrock;
314 using namespace shammath;
315
316 using GZData = GhostZonesData<Tvec, TgridVec>;
317 using InterfaceBuildInfos = typename GZData::InterfaceBuildInfos;
318 using InterfaceIdTable = typename GZData::InterfaceIdTable;
319
320 using AMRBlock = typename Config::AMRBlock;
321
322 // setup ghost layout
323 storage.ghost_layout.set(std::make_shared<shamrock::patch::PatchDataLayerLayout>());
324 std::shared_ptr<shamrock::patch::PatchDataLayerLayout> ghost_layout_ptr
325 = storage.ghost_layout.get();
326 shamrock::patch::PatchDataLayerLayout &ghost_layout = shambase::get_check_ref(ghost_layout_ptr);
327
328 ghost_layout.add_field<TgridVec>("cell_min", 1);
329 ghost_layout.add_field<TgridVec>("cell_max", 1);
330 ghost_layout.add_field<Tscal>("rho", AMRBlock::block_size);
331 ghost_layout.add_field<Tscal>("eint", AMRBlock::block_size);
332 ghost_layout.add_field<Tvec>("vel", AMRBlock::block_size);
333
334 u32 icell_min_interf = ghost_layout.get_field_idx<TgridVec>("cell_min");
335 u32 icell_max_interf = ghost_layout.get_field_idx<TgridVec>("cell_max");
336 u32 irho_interf = ghost_layout.get_field_idx<Tscal>("rho");
337 u32 ieint_interf = ghost_layout.get_field_idx<Tscal>("eint");
338 u32 ivel_interf = ghost_layout.get_field_idx<Tvec>("vel");
339
340 // load layout info
341 PatchDataLayerLayout &pdl = scheduler().pdl_old();
342
343 const u32 icell_min = pdl.get_field_idx<TgridVec>("cell_min");
344 const u32 icell_max = pdl.get_field_idx<TgridVec>("cell_max");
345 const u32 irho = pdl.get_field_idx<Tscal>("rho");
346 const u32 ieint = pdl.get_field_idx<Tscal>("eint");
347 const u32 ivel = pdl.get_field_idx<Tvec>("vel");
348
349 // generate send buffers
350 GZData &gen_ghost = storage.ghost_zone_infos.get();
351 auto pdat_interf = gen_ghost.template build_interface_native<PatchDataLayer>(
352 [&](u64 sender, u64, InterfaceBuildInfos binfo, sycl::buffer<u32> &buf_idx, u32 cnt) {
353 PatchDataLayer &sender_patch = scheduler().patch_data.get_pdat(sender);
354
355 PatchDataLayer pdat(ghost_layout_ptr);
356
357 pdat.reserve(cnt);
358
359 sender_patch.get_field<TgridVec>(icell_min).append_subset_to(
360 buf_idx, cnt, pdat.get_field<TgridVec>(icell_min_interf));
361
362 sender_patch.get_field<TgridVec>(icell_max).append_subset_to(
363 buf_idx, cnt, pdat.get_field<TgridVec>(icell_max_interf));
364
365 sender_patch.get_field<Tscal>(irho).append_subset_to(
366 buf_idx, cnt, pdat.get_field<Tscal>(irho_interf));
367
368 sender_patch.get_field<Tscal>(ieint).append_subset_to(
369 buf_idx, cnt, pdat.get_field<Tscal>(ieint_interf));
370
371 sender_patch.get_field<Tvec>(ivel).append_subset_to(
372 buf_idx, cnt, pdat.get_field<Tvec>(ivel_interf));
373
375
376 pdat.get_field<TgridVec>(icell_min_interf).apply_offset(binfo.offset);
377 pdat.get_field<TgridVec>(icell_max_interf).apply_offset(binfo.offset);
378
379 return pdat;
380 });
381
382 // communicate buffers
384 = communicate_pdat(ghost_layout_ptr, std::move(pdat_interf));
385
386 std::map<u64, u64> sz_interf_map;
387 interf_pdat.for_each([&](u64 s, u64 r, PatchDataLayer &pdat_interf) {
388 sz_interf_map[r] += pdat_interf.get_obj_cnt();
389 });
390
391 storage.merged_patchdata_ghost.set(
392 merge_native<PatchDataLayer, MergedPatchData>(
393 std::move(interf_pdat),
395 shamlog_debug_ln("Merged patch init", p.id_patch);
396
397 PatchDataLayer pdat_new(ghost_layout_ptr);
398
399 u32 or_elem = pdat.get_obj_cnt();
400 pdat_new.reserve(or_elem + sz_interf_map[p.id_patch]);
401 u32 total_elements = or_elem;
402
403 pdat_new.get_field<TgridVec>(icell_min_interf)
404 .insert(pdat.get_field<TgridVec>(icell_min));
405 pdat_new.get_field<TgridVec>(icell_max_interf)
406 .insert(pdat.get_field<TgridVec>(icell_max));
407 pdat_new.get_field<Tscal>(irho_interf).insert(pdat.get_field<Tscal>(irho));
408 pdat_new.get_field<Tscal>(ieint_interf).insert(pdat.get_field<Tscal>(ieint));
409 pdat_new.get_field<Tvec>(ivel_interf).insert(pdat.get_field<Tvec>(ivel));
410
411 pdat_new.check_field_obj_cnt_match();
412
413 return MergedPatchData{or_elem, total_elements, std::move(pdat_new), ghost_layout};
414 },
415 [](MergedPatchData &mpdat, PatchDataLayer &pdat_interf) {
416 mpdat.total_elements += pdat_interf.get_obj_cnt();
417 mpdat.pdat.insert_elements(pdat_interf);
418 }));
419
420 storage.merged_patchdata_ghost.get().for_each([](u64 id, shamrock::MergedPatchData &mpdat) {
421 shamlog_debug_ln(
422 "Merged patch", id, ",", mpdat.original_elements, "->", mpdat.total_elements);
423 });
424
425 timer_interf.end();
426 storage.timings_details.interface += timer_interf.elasped_sec();
427}
428
429template<class Tvec, class TgridVec>
430template<class T>
433
434 StackEntry stack_loc{};
435
436 shambase::Timer timer_interf;
437 timer_interf.start();
438
439 using namespace shamrock::patch;
440 using namespace shamrock;
441 using namespace shammath;
442
443 using GZData = GhostZonesData<Tvec, TgridVec>;
444 using InterfaceBuildInfos = typename GZData::InterfaceBuildInfos;
445 using InterfaceIdTable = typename GZData::InterfaceIdTable;
446
447 using AMRBlock = typename Config::AMRBlock;
448
449 // generate send buffers
450 GZData &gen_ghost = storage.ghost_zone_infos.get();
451 auto pdat_interf = gen_ghost.template build_interface_native<PatchDataField<T>>(
452 [&](u64 sender, u64, InterfaceBuildInfos binfo, sycl::buffer<u32> &buf_idx, u32 cnt) {
453 PatchDataField<T> &sender_patch = in.get_field(sender);
454
455 PatchDataField<T> pdat(sender_patch.get_name(), sender_patch.get_nvar(), cnt);
456
457 return pdat;
458 });
459
460 // communicate buffers
462 = communicate_pdat_field<T>(std::move(pdat_interf));
463
464 std::map<u64, u64> sz_interf_map;
465 interf_pdat.for_each([&](u64 s, u64 r, PatchDataField<T> &pdat_interf) {
466 sz_interf_map[r] += pdat_interf.get_obj_cnt();
467 });
468
469 ComputeField<T> out;
470 scheduler().for_each_patchdata_nonempty(
472 PatchDataField<T> &receiver_patch = in.get_field(p.id_patch);
473
474 PatchDataField<T> new_pdat(receiver_patch);
475
476 interf_pdat.for_each([&](u64 sender, u64 receiver, PatchDataField<T> &interface) {
477 if (receiver == p.id_patch) {
478 new_pdat.insert(interface);
479 }
480 });
481
482 out.field_data.add_obj(p.id_patch, std::move(new_pdat));
483 });
484
485 timer_interf.end();
486 storage.timings_details.interface += timer_interf.elasped_sec();
487 return out;
488}
489
490// doxygen does not have a clue of what is happenning here
491// like ... come on ...
492#ifndef DOXYGEN
494
497 template class GhostZones<f64_3, i64_3>;
498 template shamrock::ComputeField<f64_8> GhostZones<f64_3, i64_3>::exchange_compute_field<f64_8>(
500
503 template shambase::DistributedDataShared<PatchDataField<f64_8>> GhostZones<f64_3, i64_3>::
504 communicate_pdat_field<f64_8>(
506
507} // namespace shammodels::zeus::modules
508#endif
Container for objects shared between two distributed data patches.
Header file describing a Node Instance.
double f64
Alias for double.
std::uint32_t u32
32 bit unsigned integer
std::uint64_t u64
64 bit unsigned integer
std::int32_t i32
32 bit integer
static PatchDataField deserialize_full(shamalgs::SerializeHelper &serializer)
deserialize a field inverse of serialize_full
The MPI scheduler.
A buffer allocated in USM (Unified Shared Memory)
A SYCL queue associated with a device and a context.
sycl::queue q
The SYCL queue associated with this context.
sycl::event submit(Fct &&fct)
Submits a kernel to the SYCL queue.
DeviceQueue & get_queue(u32 id=0)
Get a reference to a DeviceQueue.
Class to manage a list of SYCL events.
Definition EventList.hpp:31
Container for objects shared between two distributed data elements.
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.
Class Timer measures the time elapsed since the timer was started.
Definition time.hpp:96
void end()
Stops the timer and stores the elapsed time in nanoseconds.
Definition time.hpp:111
f64 elasped_sec() const
Converts the stored nanosecond time to a floating point representation in seconds.
Definition time.hpp:123
void start()
Starts the timer.
Definition time.hpp:106
Class to hold information related to ghost zones.
u32 get_field_idx(const std::string &field_name) const
Get the field id if matching name & type.
void add_field(const std::string &field_name, u32 nvar, SourceLocation loc=SourceLocation{})
add a field of type T to the layout
PatchDataLayer container class, the layout is described in patchdata_layout.
void check_field_obj_cnt_match()
check that all contained field have the same obj cnt
Store the information related to the size of the simulation box to convert patch integer coordinates ...
Definition SimBox.hpp:35
T get_bounding_box_size() const
Get the size of the stored bounding box of the domain.
Definition SimBox.hpp:87
PatchCoordTransform< T > get_patch_transform() const
Get a PatchCoordTransform object that describes the conversion between patch coordinates and domain c...
Definition SimBox.hpp:285
std::tuple< std::optional< sycl::buffer< u32 > >, u32 > stream_compact(sycl::queue &q, sycl::buffer< u32 > &buf_flags, u32 len)
Stream compaction algorithm.
Definition numeric.cpp:84
void append_subset_to(const sham::DeviceBuffer< T > &buf, const sham::DeviceBuffer< u32 > &idxs_buf, u32 nvar, sham::DeviceBuffer< T > &buf_other, u32 start_enque)
Appends a subset of elements from one buffer to another.
auto extract_value(std::optional< T > &o, SourceLocation loc=SourceLocation()) -> T
Extracts the content out of an optional.
Definition memory.hpp:211
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
namespace for math utility
Definition AABB.hpp:26
namespace for the zeus model modules
Definition AMRTree.hpp:23
auto find_interfaces(PatchScheduler &sched, SerialPatchTree< TgridVec > &sptree)
find interfaces corresponding to shared surface between domains
namespace for the main framework
Definition __init__.py:1
This file contains the definition for the stacktrace related functionality.
Axis-Aligned bounding box.
Definition AABB.hpp:99
Patch object that contain generic patch information.
Definition Patch.hpp:33
u64 id_patch
unique key that identify the patch
Definition Patch.hpp:86