Shamrock 2025.10.0
Astrophysical Code
Loading...
Searching...
No Matches
SinkParticlesUpdate.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
17
34#include <stdexcept>
35#include <vector>
36
37#define NODE_EDGES(X_RO, X_RW) \
38 /* ------------------- (param) inputs ------------------- */ \
39 X_RO(shamrock::solvergraph::IDataEdge<Tscal>, constant_G) \
40 X_RO(shamrock::solvergraph::IDataEdge<Tscal>, gpart_mass) \
41 \
42 /* ------------------- (field) inputs ------------------- */ \
43 X_RO(shamrock::solvergraph::Indexes<u32>, part_counts) \
44 X_RO(shamrock::solvergraph::IFieldSpan<Tvec>, positions) \
45 \
46 /* ------------------- (sink) inputs ------------------- */ \
47 X_RO(shamrock::solvergraph::IDataEdge<std::vector<Tvec>>, sink_positions) \
48 X_RO(shamrock::solvergraph::IDataEdge<std::vector<Tscal>>, sink_mass) \
49 X_RO(shamrock::solvergraph::IDataEdge<std::vector<Tscal>>, sink_accr_radii) \
50 \
51 /* ------------------- outputs ------------------- */ \
52 X_RW(shamrock::solvergraph::IFieldSpan<Tvec>, accel_ext) \
53 X_RW(shamrock::solvergraph::IDataEdge<std::vector<Tvec>>, sink_acc_sph)
54
55namespace {
56
64 template<class Tvec>
65 class SinkParticlesAddSPHForces : public shamrock::solvergraph::INode {
66
67 using Tscal = shambase::VecComponent<Tvec>;
68
69 public:
70 SinkParticlesAddSPHForces() = default;
71
72 EXPAND_NODE_EDGES(NODE_EDGES)
73
74 void _impl_evaluate_internal();
75
76 inline virtual std::string _impl_get_label() const { return "SinkParticlesAddSPHForces"; }
77
78 virtual std::string _impl_get_tex() const;
79 };
80
81 template<class Tvec>
82 void SinkParticlesAddSPHForces<Tvec>::_impl_evaluate_internal() {
83
85
86 auto edges = get_edges();
87
88 auto dev_sched = shamsys::instance::get_compute_scheduler_ptr();
89 sham::DeviceQueue &q = shambase::get_check_ref(dev_sched).get_queue();
90
91 Tscal G = edges.constant_G.data;
92 Tscal gpart_mass = edges.gpart_mass.data;
93
94 const std::vector<Tvec> &sink_positions = edges.sink_positions.data;
95 const std::vector<Tscal> &sink_mass = edges.sink_mass.data;
96 const std::vector<Tscal> &sink_accr_radii = edges.sink_accr_radii.data;
97 std::vector<Tvec> &sink_acc_sph = edges.sink_acc_sph.data;
98
99 size_t sink_count = sink_positions.size();
100
101 if (sink_mass.size() != sink_count || sink_accr_radii.size() != sink_count
102 || sink_acc_sph.size() != sink_count) {
104 "sink edges size mismatch: pos={}, mass={}, accretion_radius={}, acc_sph={}",
105 sink_count,
106 sink_mass.size(),
107 sink_accr_radii.size(),
108 sink_acc_sph.size()));
109 }
110
111 edges.positions.check_sizes(edges.part_counts.indexes);
112 edges.accel_ext.check_sizes(edges.part_counts.indexes);
113
114 auto &pos_spans = edges.positions.get_spans();
115 auto &accel_ext_spans = edges.accel_ext.get_spans();
116
117 sham::DeviceBuffer<Tvec> buf_sync_axyz(0, dev_sched);
118
119 std::vector<Tvec> result_acc_sinks{};
120
121 for (size_t sink_id = 0; sink_id < sink_count; sink_id++) {
122
123 Tvec sph_acc_sink = {};
124
125 Tscal s_mass = sink_mass[sink_id];
126 Tscal s_racc = sink_accr_radii[sink_id];
127 Tvec s_pos = sink_positions[sink_id];
128
129 edges.part_counts.indexes.for_each([&](u64 id_patch, u32 part_count) {
130 buf_sync_axyz.resize(part_count);
131
133 q,
134 sham::MultiRef{pos_spans.get(id_patch)},
135 sham::MultiRef{accel_ext_spans.get(id_patch), buf_sync_axyz},
136 part_count,
137 [s_pos, G, s_mass, s_racc, gpart_mass](
138 u32 id_a,
139 const Tvec *__restrict xyz,
140 Tvec *__restrict axyz_ext,
141 Tvec *__restrict axyz_sync) {
142 Tvec r_a = xyz[id_a];
143
144 Tvec delta = r_a - s_pos;
145 Tscal d = sycl::length(delta);
146
147 Tvec force = G * delta / (d * d * d);
148
149 // This is a hack to avoid the sink kaboom effect
150 // when the particle is being advected close to the sink before
151 // being accreted
152 if (d < s_racc) {
153 force = {0, 0, 0};
154 }
155
156 axyz_sync[id_a] = force * gpart_mass;
157 axyz_ext[id_a] += -force * s_mass;
158 });
159
160 sph_acc_sink += shamalgs::primitives::sum(dev_sched, buf_sync_axyz, 0, part_count);
161 });
162
163 result_acc_sinks.push_back(sph_acc_sink);
164 }
165
166 std::vector<Tvec> gathered_result_acc_sinks{};
168 result_acc_sinks, gathered_result_acc_sinks, MPI_COMM_WORLD);
169
170 for (size_t id_s = 0; id_s < sink_count; id_s++) {
171
172 sink_acc_sph[id_s] = {};
173
174 for (u32 rid = 0; rid < shamcomm::world_size(); rid++) {
175 sink_acc_sph[id_s] += gathered_result_acc_sinks[rid * sink_count + id_s];
176 }
177 }
178 }
179
180 template<class Tvec>
181 std::string SinkParticlesAddSPHForces<Tvec>::_impl_get_tex() const {
182
183 auto constant_G = get_ro_edge_base(0).get_tex_symbol();
184 auto gpart_mass = get_ro_edge_base(1).get_tex_symbol();
185 auto positions = get_ro_edge_base(3).get_tex_symbol();
186 auto sink_positions = get_ro_edge_base(4).get_tex_symbol();
187 auto sink_mass = get_ro_edge_base(5).get_tex_symbol();
188 auto sink_accr_radii = get_ro_edge_base(6).get_tex_symbol();
189 auto axyz_ext = get_rw_edge_base(0).get_tex_symbol();
190 auto sink_acc_sph = get_rw_edge_base(1).get_tex_symbol();
191
192 std::string tex = R"tex(
193 Add sink / SPH particles gravitational interaction
194
195 \begin{align}
196 {\bf f}_{a,s} &= {constant_G} \frac{{positions}_a - {sink_positions}_s}{\vert {positions}_a - {sink_positions}_s \vert^3}
197 \quad (0 \text{ if } \vert {positions}_a - {sink_positions}_s \vert < {sink_accr_radii}_s) \\
198 {axyz_ext}_a &\mathrel{+}= - \sum_s {sink_mass}_s {\bf f}_{a,s} \\
199 {sink_acc_sph}_s &= \sum_a {gpart_mass} {\bf f}_{a,s}
200 \end{align}
201 )tex";
202
203 shambase::replace_all(tex, "{constant_G}", constant_G);
204 shambase::replace_all(tex, "{gpart_mass}", gpart_mass);
205 shambase::replace_all(tex, "{positions}", positions);
206 shambase::replace_all(tex, "{sink_positions}", sink_positions);
207 shambase::replace_all(tex, "{sink_mass}", sink_mass);
208 shambase::replace_all(tex, "{sink_accr_radii}", sink_accr_radii);
209 shambase::replace_all(tex, "{axyz_ext}", axyz_ext);
210 shambase::replace_all(tex, "{sink_acc_sph}", sink_acc_sph);
211
212 return tex;
213 }
214
215} // namespace
216
217#undef NODE_EDGES
218
219template<class Tvec, template<class> class SPHKernel>
220void shammodels::sph::modules::SinkParticlesUpdate<Tvec, SPHKernel>::compute_sph_forces() {
221
222 StackEntry stack_loc{};
223
224 auto &sync = scheduler().synchronized_data;
225 if (!has_sinks<Tvec>(sync)) {
226 return;
227 }
228
229 using namespace shamrock;
230 using namespace shamrock::patch;
231 using namespace shamrock::solvergraph;
232
233 PatchDataLayerLayout &pdl = scheduler().pdl_old();
234 const u32 ixyz = pdl.get_field_idx<Tvec>("xyz");
235 const u32 iaxyz_ext = pdl.get_field_idx<Tvec>("axyz_ext");
236
237 // map the patchdata fields onto field refs
238 auto part_counts = Indexes<u32>::make_shared("part_counts", "N_{\\rm part}");
239 auto xyz_refs = FieldRefs<Tvec>::make_shared("xyz", "\\mathbf{r}");
240 auto axyz_ext_refs = FieldRefs<Tvec>::make_shared("axyz_ext", "\\mathbf{a}_{\\rm ext}");
241
242 DDPatchDataFieldRef<Tvec> xyz_field_refs = {};
243 DDPatchDataFieldRef<Tvec> axyz_ext_field_refs = {};
244 part_counts->indexes = {};
245
246 scheduler().for_each_patchdata_nonempty([&](const Patch p, PatchDataLayer &pdat) {
247 part_counts->indexes.add_obj(p.id_patch, pdat.get_obj_cnt());
248 xyz_field_refs.add_obj(p.id_patch, std::ref(pdat.get_field<Tvec>(ixyz)));
249 axyz_ext_field_refs.add_obj(p.id_patch, std::ref(pdat.get_field<Tvec>(iaxyz_ext)));
250 });
251
252 xyz_refs->set_refs(xyz_field_refs);
253 axyz_ext_refs->set_refs(axyz_ext_field_refs);
254
255 auto constant_G = IDataEdge<Tscal>::make_shared("constant_G", "G");
256 auto gpart_mass = IDataEdge<Tscal>::make_shared("gpart_mass", "m_{\\rm part}");
257 constant_G->data = solver_config.get_constant_G();
258 gpart_mass->data = solver_config.gpart_mass;
259
260 // sink edges
261 auto sink_pos
262 = sync.template get_edge_ptr<IDataEdgeSerializable<std::vector<Tvec>>>("sink_pos");
263 auto sink_mass
264 = sync.template get_edge_ptr<IDataEdgeSerializable<std::vector<Tscal>>>("sink_mass");
265 auto sink_accr_radii = sync.template get_edge_ptr<IDataEdgeSerializable<std::vector<Tscal>>>(
266 "sink_accretion_radius");
267 auto sink_acc_sph
268 = sync.template get_edge_ptr<IDataEdgeSerializable<std::vector<Tvec>>>("sink_acc_sph");
269
270 SinkParticlesAddSPHForces<Tvec> add_sph_forces{};
271 add_sph_forces.set_edges(
272 constant_G,
273 gpart_mass,
274 part_counts,
275 xyz_refs,
276 sink_pos,
277 sink_mass,
278 sink_accr_radii,
279 axyz_ext_refs,
280 sink_acc_sph);
281 add_sph_forces.evaluate();
282}
283
284using namespace shammath;
288
#define NODE_EDGES(X_RO, X_RW)
declare the list of edges for this node
constexpr const char * xyz
Position field (3D coordinates).
constexpr const char * part_counts
Particle counts per patch.
shambase::DistributedData< PatchDataFieldRef< T > > DDPatchDataFieldRef
Alias for a DistributedData of PatchDataFieldRefs.
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).
A SYCL queue associated with a device and a context.
iterator add_obj(u64 id, T &&obj)
Adds a new object to the collection.
u32 get_field_idx(const std::string &field_name) const
Get the field id if matching name & type.
PatchDataLayer container class, the layout is described in patchdata_layout.
u32 get_obj_cnt() const
get the number of objects (particles) stored in this layer
Inode is node between data edges, takes multiple inputs, multiple outputs.
Definition INode.hpp:31
This header file contains utility functions related to exception handling in the code.
std::vector< int > vector_allgatherv(const std::vector< T > &send_vec, const MPI_Datatype &send_type, std::vector< T > &recv_vec, const MPI_Datatype &recv_type, const MPI_Comm comm)
allgatherv on vector with size query (size querying variant of vector_allgatherv_ks) //TODO add fault...
Definition exchanges.hpp:98
void kernel_call(sham::DeviceQueue &q, RefIn in, RefOut in_out, u32 n, Functor &&func, SourceLocation &&callsite=SourceLocation{})
Submit a kernel to a SYCL queue.
T sum(const sham::DeviceScheduler_ptr &sched, const sham::DeviceBuffer< T > &buf1, u32 start_id, u32 end_id)
Compute the sum of elements in a device buffer within a specified range.
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:106
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:112
ExcptTypes make_except_with_loc(std::string message, SourceLocation loc=SourceLocation{})
Create an exception with a message and a location.
i32 world_size()
Gives the size of the MPI communicator.
Definition worldInfo.cpp:39
namespace for math utility
Definition AABB.hpp:26
namespace for the main framework
Definition __init__.py:1
Helpers to access SPH sink particles stored as SoA synchronized data edges.
sph kernels
#define __shamrock_stack_entry()
Macro to create a stack entry.
shambase::details::BasicStackEntry StackEntry
Alias for shambase::details::BasicStackEntry.
A class that references multiple buffers or similar objects.
Definition MultiRef.hpp:33
Patch object that contain generic patch information.
Definition Patch.hpp:33
Functions related to the MPI communicator.