Shamrock 2025.10.0
Astrophysical Code
Loading...
Searching...
No Matches
UpdateDerivs.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
28
30#include "shambackends/math.hpp"
40
41template<class Tvec, template<class> class SPHKernel>
43 StackEntry stack_loc{};
44
45 Cfg_Riemann cfg_riemann = solver_config.riemann_config;
46
47 if (Iterative *v = std::get_if<Iterative>(&cfg_riemann.config)) {
48 update_derivs_iterative(*v);
49 } else if (HLLC *v = std::get_if<HLLC>(&cfg_riemann.config)) {
50 update_derivs_hllc(*v);
51 } else if (Exact *v = std::get_if<Exact>(&cfg_riemann.config)) {
52 update_derivs_exact(*v);
53 } else {
54 shambase::throw_unimplemented("Riemann solver type not supported by UpdateDerivs");
55 }
56}
57
58template<class Tvec, template<class> class SPHKernel>
59void shammodels::gsph::modules::UpdateDerivs<Tvec, SPHKernel>::update_derivs_iterative(
60 Iterative cfg) {
61
62 StackEntry stack_loc{};
63
64 using namespace shamrock;
65 using namespace shamrock::patch;
66
67 PatchDataLayerLayout &pdl = scheduler().pdl_old();
68
69 // Get field indices from the patch data layout
70 const u32 ixyz = pdl.get_field_idx<Tvec>(gsph::names::common::xyz);
71 const u32 ivxyz = pdl.get_field_idx<Tvec>(gsph::names::newtonian::vxyz);
72 const u32 iaxyz = pdl.get_field_idx<Tvec>(gsph::names::newtonian::axyz);
73 const u32 ihpart = pdl.get_field_idx<Tscal>(gsph::names::common::hpart);
74
75 // Optional internal energy fields (for adiabatic EOS)
76 const bool has_uint = solver_config.has_field_uint();
77 const u32 iuint = has_uint ? pdl.get_field_idx<Tscal>(gsph::names::newtonian::uint) : 0;
78 const u32 iduint = has_uint ? pdl.get_field_idx<Tscal>(gsph::names::newtonian::duint) : 0;
79
80 // Ghost layout for neighbor data
82 = shambase::get_check_ref(storage.ghost_layout.get());
83 u32 ihpart_interf = ghost_layout.get_field_idx<Tscal>(gsph::names::common::hpart);
84 u32 ivxyz_interf = ghost_layout.get_field_idx<Tvec>(gsph::names::newtonian::vxyz);
85 u32 iomega_interf = ghost_layout.get_field_idx<Tscal>(gsph::names::newtonian::omega);
86 u32 idensity_interf = ghost_layout.get_field_idx<Tscal>(gsph::names::newtonian::density);
87 u32 iuint_interf
88 = has_uint ? ghost_layout.get_field_idx<Tscal>(gsph::names::newtonian::uint) : 0;
89
90 // Get merged data and caches from storage
91 auto &merged_xyzh = storage.merged_xyzh.get();
93 shambase::DistributedData<PatchDataLayer> &mpdats = storage.merged_patchdata_ghost.get();
94
95 // Get pressure and soundspeed from storage (includes ghosts)
96 shamrock::solvergraph::Field<Tscal> &pressure_field = shambase::get_check_ref(storage.pressure);
98 = shambase::get_check_ref(storage.soundspeed);
99
100 // Iterate over all non-empty patches
101 scheduler().for_each_patchdata_nonempty([&](Patch cur_p, PatchDataLayer &pdat) {
102 PatchDataLayer &mpdat = mpdats.get(cur_p.id_patch);
103
104 // Get buffers for local and ghost data
106 = merged_xyzh.get(cur_p.id_patch).template get_field_buf_ref<Tvec>(0);
107 sham::DeviceBuffer<Tvec> &buf_axyz = pdat.get_field_buf_ref<Tvec>(iaxyz);
108 sham::DeviceBuffer<Tvec> &buf_vxyz = mpdat.get_field_buf_ref<Tvec>(ivxyz_interf);
109 sham::DeviceBuffer<Tscal> &buf_hpart = mpdat.get_field_buf_ref<Tscal>(ihpart_interf);
110 sham::DeviceBuffer<Tscal> &buf_omega = mpdat.get_field_buf_ref<Tscal>(iomega_interf);
111 sham::DeviceBuffer<Tscal> &buf_pressure
112 = pressure_field.get_field(cur_p.id_patch).get_buf();
113 sham::DeviceBuffer<Tscal> &buf_cs = soundspeed_field.get_field(cur_p.id_patch).get_buf();
114
115 // Get neighbor cache for this patch
116 tree::ObjectCache &pcache
117 = shambase::get_check_ref(storage.neigh_cache).get_cache(cur_p.id_patch);
118
119 // Set up SYCL queue and event tracking
120 sham::DeviceQueue &q = shamsys::instance::get_compute_scheduler().get_queue();
121 sham::EventList depends_list;
122
123 // Get density from merged ghost data (SPH summation density)
124 sham::DeviceBuffer<Tscal> &buf_density = mpdat.get_field_buf_ref<Tscal>(idensity_interf);
125
126 // Get buffer accessors
127 auto xyz = buf_xyz.get_read_access(depends_list);
128 auto axyz = buf_axyz.get_write_access(depends_list);
129 auto vxyz = buf_vxyz.get_read_access(depends_list);
130 auto hpart = buf_hpart.get_read_access(depends_list);
131 auto omega_acc = buf_omega.get_read_access(depends_list);
132 auto density_acc = buf_density.get_read_access(depends_list);
133 auto pressure_acc = buf_pressure.get_read_access(depends_list);
134 auto cs_acc = buf_cs.get_read_access(depends_list);
135 auto ploop_ptrs = pcache.get_read_access(depends_list);
136
137 // Optional: internal energy
138 sham::DeviceBuffer<Tscal> *buf_duint_ptr = nullptr;
139 Tscal *duint_acc = nullptr;
140 if (has_uint) {
141 buf_duint_ptr = &pdat.get_field_buf_ref<Tscal>(iduint);
142 duint_acc = buf_duint_ptr->get_write_access(depends_list);
143 }
144
145 auto e = q.submit(depends_list, [&](sycl::handler &cgh) {
146 const Tscal pmass = solver_config.gpart_mass;
147 const Tscal gamma = solver_config.get_eos_gamma();
148 const Tscal tol = cfg.tol;
149 const u32 max_iter = cfg.max_iter;
150 const bool do_energy = has_uint;
151 const bool use_inutsuka_v2 = solver_config.is_force_inutsuka_v2();
152
153 // Use shamrock's ObjectCacheIterator for neighbor traversal
154 tree::ObjectCacheIterator particle_looper(ploop_ptrs);
155
156 constexpr Tscal Rker2 = Kernel::Rkern * Kernel::Rkern;
157
158 // InutsukaV2 evaluates the kernel gradient at sqrt(2)*h, so the local
159 // kernel-support filter below must match the widened cache radius
160 // (see start_neighbors_cache()/compute_presteps_rint() in Solver.cpp),
161 // or valid cached pairs beyond h*Rkern get dropped here regardless.
162 const Tscal Rker2_factor = use_inutsuka_v2 ? Tscal{2} : Tscal{1};
163
164 shambase::parallel_for(cgh, pdat.get_obj_cnt(), "GSPH derivs iterative", [=](u64 gid) {
165 u32 id_a = (u32) gid;
166
167 using namespace shamrock::sph;
168
169 // Initialize accumulators
170 Tvec sum_axyz = {0, 0, 0};
171 Tscal sum_du_a = 0;
172
173 // Particle a state
174 const Tscal h_a = hpart[id_a];
175 const Tvec xyz_a = xyz[id_a];
176 const Tvec vxyz_a = vxyz[id_a];
177 const Tscal omega_a = omega_acc[id_a];
178
179 // Use SPH-summation density
180 const Tscal rho_a = sycl::max(density_acc[id_a], Tscal(1e-30));
181
182 // Use pressure and soundspeed from storage
183 const Tscal P_a = sycl::max(pressure_acc[id_a], Tscal(1e-30));
184 const Tscal cs_a = sycl::max(cs_acc[id_a], Tscal(1e-10));
185
186 // Loop over neighbors
187 particle_looper.for_each_object(id_a, [&](u32 id_b) {
188 if (id_a == id_b)
189 return; // Skip self
190
191 // Distance and kernel support check
192 const Tvec dr = xyz_a - xyz[id_b];
193 const Tscal rab2 = sycl::dot(dr, dr);
194 const Tscal h_b = hpart[id_b];
195
196 // Skip if outside kernel support
197 if (rab2 > h_a * h_a * Rker2 * Rker2_factor
198 && rab2 > h_b * h_b * Rker2 * Rker2_factor) {
199 return;
200 }
201
202 const Tscal rab = sycl::sqrt(rab2);
203 const Tvec vxyz_b = vxyz[id_b];
204 const Tscal omega_b = omega_acc[id_b];
205
206 // Use SPH-summation density
207 const Tscal rho_b = sycl::max(density_acc[id_b], Tscal(1e-30));
208
209 // Use pressure and soundspeed from storage
210 const Tscal P_b = sycl::max(pressure_acc[id_b], Tscal(1e-30));
211 const Tscal cs_b = sycl::max(cs_acc[id_b], Tscal(1e-10));
212
213 // Unit vector from a to b
214 const Tscal rab_inv = sham::inv_sat_positive(rab);
215 const Tvec r_ab_unit = dr * rab_inv;
216
217 // Project velocities onto pair axis for 1D Riemann problem
218 const Tscal u_a_proj = sycl::dot(vxyz_a, r_ab_unit);
219 const Tscal u_b_proj = sycl::dot(vxyz_b, r_ab_unit);
220
221 // Solve 1D Riemann problem using iterative solver
222 // Convention: Left state = neighbor b, Right state = current a
223 auto riemann_result = riemann::iterative_solver<Tscal>(
224 u_b_proj,
225 rho_b,
226 P_b, // Left = neighbor
227 u_a_proj,
228 rho_a,
229 P_a, // Right = current
230 gamma,
231 tol,
232 max_iter);
233 const Tscal p_star = riemann_result.p_star;
234 const Tscal v_star = riemann_result.v_star;
235
237 use_inutsuka_v2,
238 pmass,
239 p_star,
240 v_star,
241 rho_a,
242 rho_b,
243 omega_a,
244 omega_b,
245 rab,
246 rab_inv,
247 h_a,
248 h_b,
249 r_ab_unit,
250 vxyz_a,
251 sum_axyz,
252 sum_du_a);
253 });
254
255 // Write accumulated derivatives
256 axyz[id_a] = sum_axyz;
257 if (duint_acc != nullptr) {
258 duint_acc[id_a] = sum_du_a;
259 }
260 });
261 });
262
263 // Complete event states for all buffers
264 buf_xyz.complete_event_state(e);
265 buf_axyz.complete_event_state(e);
266 buf_vxyz.complete_event_state(e);
267 buf_hpart.complete_event_state(e);
268 buf_omega.complete_event_state(e);
269 buf_density.complete_event_state(e);
270 buf_pressure.complete_event_state(e);
271 buf_cs.complete_event_state(e);
272
273 if (has_uint && buf_duint_ptr) {
274 buf_duint_ptr->complete_event_state(e);
275 }
276
277 sham::EventList resulting_events;
278 resulting_events.add_event(e);
279 pcache.complete_event_state(resulting_events);
280 });
281}
282
283template<class Tvec, template<class> class SPHKernel>
284void shammodels::gsph::modules::UpdateDerivs<Tvec, SPHKernel>::update_derivs_exact(Exact cfg) {
285
286 StackEntry stack_loc{};
287
288 using namespace shamrock;
289 using namespace shamrock::patch;
290
291 PatchDataLayerLayout &pdl = scheduler().pdl_old();
292
293 // Get field indices from the patch data layout
294 const u32 ixyz = pdl.get_field_idx<Tvec>(gsph::names::common::xyz);
295 const u32 ivxyz = pdl.get_field_idx<Tvec>(gsph::names::newtonian::vxyz);
296 const u32 iaxyz = pdl.get_field_idx<Tvec>(gsph::names::newtonian::axyz);
297 const u32 ihpart = pdl.get_field_idx<Tscal>(gsph::names::common::hpart);
298
299 // Optional internal energy fields (for adiabatic EOS)
300 const bool has_uint = solver_config.has_field_uint();
301 const u32 iuint = has_uint ? pdl.get_field_idx<Tscal>(gsph::names::newtonian::uint) : 0;
302 const u32 iduint = has_uint ? pdl.get_field_idx<Tscal>(gsph::names::newtonian::duint) : 0;
303
304 // Ghost layout for neighbor data
306 = shambase::get_check_ref(storage.ghost_layout.get());
307 u32 ihpart_interf = ghost_layout.get_field_idx<Tscal>(gsph::names::common::hpart);
308 u32 ivxyz_interf = ghost_layout.get_field_idx<Tvec>(gsph::names::newtonian::vxyz);
309 u32 iomega_interf = ghost_layout.get_field_idx<Tscal>(gsph::names::newtonian::omega);
310 u32 idensity_interf = ghost_layout.get_field_idx<Tscal>(gsph::names::newtonian::density);
311 u32 iuint_interf
312 = has_uint ? ghost_layout.get_field_idx<Tscal>(gsph::names::newtonian::uint) : 0;
313
314 // Get merged data and caches from storage
315 auto &merged_xyzh = storage.merged_xyzh.get();
317 shambase::DistributedData<PatchDataLayer> &mpdats = storage.merged_patchdata_ghost.get();
318
319 // Get pressure and soundspeed from storage (includes ghosts)
320 shamrock::solvergraph::Field<Tscal> &pressure_field = shambase::get_check_ref(storage.pressure);
322 = shambase::get_check_ref(storage.soundspeed);
323
324 // Iterate over all non-empty patches
325 scheduler().for_each_patchdata_nonempty([&](Patch cur_p, PatchDataLayer &pdat) {
326 PatchDataLayer &mpdat = mpdats.get(cur_p.id_patch);
327
328 // Get buffers for local and ghost data
330 = merged_xyzh.get(cur_p.id_patch).template get_field_buf_ref<Tvec>(0);
331 sham::DeviceBuffer<Tvec> &buf_axyz = pdat.get_field_buf_ref<Tvec>(iaxyz);
332 sham::DeviceBuffer<Tvec> &buf_vxyz = mpdat.get_field_buf_ref<Tvec>(ivxyz_interf);
333 sham::DeviceBuffer<Tscal> &buf_hpart = mpdat.get_field_buf_ref<Tscal>(ihpart_interf);
334 sham::DeviceBuffer<Tscal> &buf_omega = mpdat.get_field_buf_ref<Tscal>(iomega_interf);
335 sham::DeviceBuffer<Tscal> &buf_pressure
336 = pressure_field.get_field(cur_p.id_patch).get_buf();
337 sham::DeviceBuffer<Tscal> &buf_cs = soundspeed_field.get_field(cur_p.id_patch).get_buf();
338
339 // Get neighbor cache for this patch
340 tree::ObjectCache &pcache
341 = shambase::get_check_ref(storage.neigh_cache).get_cache(cur_p.id_patch);
342
343 // Set up SYCL queue and event tracking
344 sham::DeviceQueue &q = shamsys::instance::get_compute_scheduler().get_queue();
345 sham::EventList depends_list;
346
347 // Get density from merged ghost data (SPH summation density)
348 sham::DeviceBuffer<Tscal> &buf_density = mpdat.get_field_buf_ref<Tscal>(idensity_interf);
349
350 // Get buffer accessors
351 auto xyz = buf_xyz.get_read_access(depends_list);
352 auto axyz = buf_axyz.get_write_access(depends_list);
353 auto vxyz = buf_vxyz.get_read_access(depends_list);
354 auto hpart = buf_hpart.get_read_access(depends_list);
355 auto omega_acc = buf_omega.get_read_access(depends_list);
356 auto density_acc = buf_density.get_read_access(depends_list);
357 auto pressure_acc = buf_pressure.get_read_access(depends_list);
358 auto cs_acc = buf_cs.get_read_access(depends_list);
359 auto ploop_ptrs = pcache.get_read_access(depends_list);
360
361 // Optional: internal energy
362 sham::DeviceBuffer<Tscal> *buf_duint_ptr = nullptr;
363 Tscal *duint_acc = nullptr;
364 if (has_uint) {
365 buf_duint_ptr = &pdat.get_field_buf_ref<Tscal>(iduint);
366 duint_acc = buf_duint_ptr->get_write_access(depends_list);
367 }
368
369 auto e = q.submit(depends_list, [&](sycl::handler &cgh) {
370 const Tscal pmass = solver_config.gpart_mass;
371 const Tscal gamma = solver_config.get_eos_gamma();
372 const Tscal tol = cfg.tol;
373 const u32 max_iter = cfg.max_iter;
374 const bool do_energy = has_uint;
375 const bool use_inutsuka_v2 = solver_config.is_force_inutsuka_v2();
376
377 // Use shamrock's ObjectCacheIterator for neighbor traversal
378 tree::ObjectCacheIterator particle_looper(ploop_ptrs);
379
380 constexpr Tscal Rker2 = Kernel::Rkern * Kernel::Rkern;
381
382 // InutsukaV2 evaluates the kernel gradient at sqrt(2)*h, so the local
383 // kernel-support filter below must match the widened cache radius
384 // (see start_neighbors_cache()/compute_presteps_rint() in Solver.cpp),
385 // or valid cached pairs beyond h*Rkern get dropped here regardless.
386 const Tscal Rker2_factor = use_inutsuka_v2 ? Tscal{2} : Tscal{1};
387
388 shambase::parallel_for(cgh, pdat.get_obj_cnt(), "GSPH derivs exact", [=](u64 gid) {
389 u32 id_a = (u32) gid;
390
391 using namespace shamrock::sph;
392
393 // Initialize accumulators
394 Tvec sum_axyz = {0, 0, 0};
395 Tscal sum_du_a = 0;
396
397 // Particle a state
398 const Tscal h_a = hpart[id_a];
399 const Tvec xyz_a = xyz[id_a];
400 const Tvec vxyz_a = vxyz[id_a];
401 const Tscal omega_a = omega_acc[id_a];
402
403 // Use SPH-summation density
404 const Tscal rho_a = sycl::max(density_acc[id_a], Tscal(1e-30));
405
406 // Use pressure and soundspeed from storage
407 const Tscal P_a = sycl::max(pressure_acc[id_a], Tscal(1e-30));
408 const Tscal cs_a = sycl::max(cs_acc[id_a], Tscal(1e-10));
409
410 // Loop over neighbors
411 particle_looper.for_each_object(id_a, [&](u32 id_b) {
412 if (id_a == id_b)
413 return; // Skip self
414
415 // Distance and kernel support check
416 const Tvec dr = xyz_a - xyz[id_b];
417 const Tscal rab2 = sycl::dot(dr, dr);
418 const Tscal h_b = hpart[id_b];
419
420 // Skip if outside kernel support
421 if (rab2 > h_a * h_a * Rker2 * Rker2_factor
422 && rab2 > h_b * h_b * Rker2 * Rker2_factor) {
423 return;
424 }
425
426 const Tscal rab = sycl::sqrt(rab2);
427 const Tvec vxyz_b = vxyz[id_b];
428 const Tscal omega_b = omega_acc[id_b];
429
430 // Use SPH-summation density
431 const Tscal rho_b = sycl::max(density_acc[id_b], Tscal(1e-30));
432
433 // Use pressure and soundspeed from storage
434 const Tscal P_b = sycl::max(pressure_acc[id_b], Tscal(1e-30));
435 const Tscal cs_b = sycl::max(cs_acc[id_b], Tscal(1e-10));
436
437 // Unit vector from a to b
438 const Tscal rab_inv = sham::inv_sat_positive(rab);
439 const Tvec r_ab_unit = dr * rab_inv;
440
441 // Project velocities onto pair axis for 1D Riemann problem
442 const Tscal u_a_proj = sycl::dot(vxyz_a, r_ab_unit);
443 const Tscal u_b_proj = sycl::dot(vxyz_b, r_ab_unit);
444
445 // Solve 1D Riemann problem exactly (Toro)
446 // Convention: Left state = neighbor b, Right state = current a
447 auto riemann_result = riemann::exact_solver<Tscal>(
448 u_b_proj,
449 rho_b,
450 P_b, // Left = neighbor
451 u_a_proj,
452 rho_a,
453 P_a, // Right = current
454 gamma,
455 tol,
456 max_iter);
457 const Tscal p_star = riemann_result.p_star;
458 const Tscal v_star = riemann_result.v_star;
459
461 use_inutsuka_v2,
462 pmass,
463 p_star,
464 v_star,
465 rho_a,
466 rho_b,
467 omega_a,
468 omega_b,
469 rab,
470 rab_inv,
471 h_a,
472 h_b,
473 r_ab_unit,
474 vxyz_a,
475 sum_axyz,
476 sum_du_a);
477 });
478
479 // Write accumulated derivatives
480 axyz[id_a] = sum_axyz;
481 if (duint_acc != nullptr) {
482 duint_acc[id_a] = sum_du_a;
483 }
484 });
485 });
486
487 // Complete event states for all buffers
488 buf_xyz.complete_event_state(e);
489 buf_axyz.complete_event_state(e);
490 buf_vxyz.complete_event_state(e);
491 buf_hpart.complete_event_state(e);
492 buf_omega.complete_event_state(e);
493 buf_density.complete_event_state(e);
494 buf_pressure.complete_event_state(e);
495 buf_cs.complete_event_state(e);
496
497 if (has_uint && buf_duint_ptr) {
498 buf_duint_ptr->complete_event_state(e);
499 }
500
501 sham::EventList resulting_events;
502 resulting_events.add_event(e);
503 pcache.complete_event_state(resulting_events);
504 });
505}
506
507template<class Tvec, template<class> class SPHKernel>
508void shammodels::gsph::modules::UpdateDerivs<Tvec, SPHKernel>::update_derivs_hllc(HLLC cfg) {
509
510 StackEntry stack_loc{};
511
512 using namespace shamrock;
513 using namespace shamrock::patch;
514
515 PatchDataLayerLayout &pdl = scheduler().pdl_old();
516
517 // Get field indices
518 const u32 ixyz = pdl.get_field_idx<Tvec>(gsph::names::common::xyz);
519 const u32 ivxyz = pdl.get_field_idx<Tvec>(gsph::names::newtonian::vxyz);
520 const u32 iaxyz = pdl.get_field_idx<Tvec>(gsph::names::newtonian::axyz);
521 const u32 ihpart = pdl.get_field_idx<Tscal>(gsph::names::common::hpart);
522
523 const bool has_uint = solver_config.has_field_uint();
524 const u32 iuint = has_uint ? pdl.get_field_idx<Tscal>(gsph::names::newtonian::uint) : 0;
525 const u32 iduint = has_uint ? pdl.get_field_idx<Tscal>(gsph::names::newtonian::duint) : 0;
526
527 // Ghost layout
529 = shambase::get_check_ref(storage.ghost_layout.get());
530 u32 ihpart_interf = ghost_layout.get_field_idx<Tscal>(gsph::names::common::hpart);
531 u32 ivxyz_interf = ghost_layout.get_field_idx<Tvec>(gsph::names::newtonian::vxyz);
532 u32 iomega_interf = ghost_layout.get_field_idx<Tscal>(gsph::names::newtonian::omega);
533 u32 idensity_interf = ghost_layout.get_field_idx<Tscal>(gsph::names::newtonian::density);
534 u32 iuint_interf
535 = has_uint ? ghost_layout.get_field_idx<Tscal>(gsph::names::newtonian::uint) : 0;
536
537 auto &merged_xyzh = storage.merged_xyzh.get();
539 shambase::DistributedData<PatchDataLayer> &mpdats = storage.merged_patchdata_ghost.get();
540
541 // Get pressure and soundspeed from storage (includes ghosts)
542 shamrock::solvergraph::Field<Tscal> &pressure_field = shambase::get_check_ref(storage.pressure);
544 = shambase::get_check_ref(storage.soundspeed);
545
546 scheduler().for_each_patchdata_nonempty([&](Patch cur_p, PatchDataLayer &pdat) {
547 PatchDataLayer &mpdat = mpdats.get(cur_p.id_patch);
548
550 = merged_xyzh.get(cur_p.id_patch).template get_field_buf_ref<Tvec>(0);
551 sham::DeviceBuffer<Tvec> &buf_axyz = pdat.get_field_buf_ref<Tvec>(iaxyz);
552 sham::DeviceBuffer<Tvec> &buf_vxyz = mpdat.get_field_buf_ref<Tvec>(ivxyz_interf);
553 sham::DeviceBuffer<Tscal> &buf_hpart = mpdat.get_field_buf_ref<Tscal>(ihpart_interf);
554 sham::DeviceBuffer<Tscal> &buf_omega = mpdat.get_field_buf_ref<Tscal>(iomega_interf);
555 sham::DeviceBuffer<Tscal> &buf_pressure
556 = pressure_field.get_field(cur_p.id_patch).get_buf();
557 sham::DeviceBuffer<Tscal> &buf_cs = soundspeed_field.get_field(cur_p.id_patch).get_buf();
558
559 tree::ObjectCache &pcache
560 = shambase::get_check_ref(storage.neigh_cache).get_cache(cur_p.id_patch);
561
562 sham::DeviceQueue &q = shamsys::instance::get_compute_scheduler().get_queue();
563 sham::EventList depends_list;
564
565 // Get density from merged ghost data
566 sham::DeviceBuffer<Tscal> &buf_density = mpdat.get_field_buf_ref<Tscal>(idensity_interf);
567
568 auto xyz = buf_xyz.get_read_access(depends_list);
569 auto axyz = buf_axyz.get_write_access(depends_list);
570 auto vxyz = buf_vxyz.get_read_access(depends_list);
571 auto hpart = buf_hpart.get_read_access(depends_list);
572 auto omega_acc = buf_omega.get_read_access(depends_list);
573 auto density_acc = buf_density.get_read_access(depends_list);
574 auto pressure_acc = buf_pressure.get_read_access(depends_list);
575 auto cs_acc = buf_cs.get_read_access(depends_list);
576 auto ploop_ptrs = pcache.get_read_access(depends_list);
577
578 sham::DeviceBuffer<Tscal> *buf_duint_ptr = nullptr;
579 Tscal *duint_acc = nullptr;
580 if (has_uint) {
581 buf_duint_ptr = &pdat.get_field_buf_ref<Tscal>(iduint);
582 duint_acc = buf_duint_ptr->get_write_access(depends_list);
583 }
584
585 auto e = q.submit(depends_list, [&](sycl::handler &cgh) {
586 const Tscal pmass = solver_config.gpart_mass;
587 const Tscal gamma = solver_config.get_eos_gamma();
588 const bool do_energy = has_uint;
589
590 tree::ObjectCacheIterator particle_looper(ploop_ptrs);
591
592 constexpr Tscal Rker2 = Kernel::Rkern * Kernel::Rkern;
593
594 shambase::parallel_for(cgh, pdat.get_obj_cnt(), "GSPH derivs HLLC", [=](u64 gid) {
595 u32 id_a = (u32) gid;
596
597 using namespace shamrock::sph;
598
599 Tvec sum_axyz = {0, 0, 0};
600 Tscal sum_du_a = 0;
601
602 const Tscal h_a = hpart[id_a];
603 const Tvec xyz_a = xyz[id_a];
604 const Tvec vxyz_a = vxyz[id_a];
605 const Tscal omega_a = omega_acc[id_a];
606
607 // Use SPH-summation density
608 const Tscal rho_a = sycl::max(density_acc[id_a], Tscal(1e-30));
609
610 // Use pressure and soundspeed from storage
611 const Tscal P_a = sycl::max(pressure_acc[id_a], Tscal(1e-30));
612 const Tscal cs_a = sycl::max(cs_acc[id_a], Tscal(1e-10));
613
614 particle_looper.for_each_object(id_a, [&](u32 id_b) {
615 if (id_a == id_b)
616 return;
617
618 const Tvec dr = xyz_a - xyz[id_b];
619 const Tscal rab2 = sycl::dot(dr, dr);
620 const Tscal h_b = hpart[id_b];
621
622 if (rab2 > h_a * h_a * Rker2 && rab2 > h_b * h_b * Rker2) {
623 return;
624 }
625
626 const Tscal rab = sycl::sqrt(rab2);
627 const Tvec vxyz_b = vxyz[id_b];
628 const Tscal omega_b = omega_acc[id_b];
629
630 // Use SPH-summation density
631 const Tscal rho_b = sycl::max(density_acc[id_b], Tscal(1e-30));
632
633 // Use pressure and soundspeed from storage
634 const Tscal P_b = sycl::max(pressure_acc[id_b], Tscal(1e-30));
635 const Tscal cs_b = sycl::max(cs_acc[id_b], Tscal(1e-10));
636
637 const Tscal rab_inv = sham::inv_sat_positive(rab);
638 const Tvec r_ab_unit = dr * rab_inv;
639
640 // Project velocities onto pair axis for 1D Riemann problem
641 const Tscal u_a_proj = sycl::dot(vxyz_a, r_ab_unit);
642 const Tscal u_b_proj = sycl::dot(vxyz_b, r_ab_unit);
643
644 // Use HLLC approximate Riemann solver
645 // Convention: Left state = neighbor b, Right state = current a
646 auto riemann_result = riemann::hllc_solver<Tscal>(
647 u_b_proj,
648 rho_b,
649 P_b, // Left = neighbor
650 u_a_proj,
651 rho_a,
652 P_a, // Right = current
653 gamma);
654 const Tscal p_star = riemann_result.p_star;
655 const Tscal v_star = riemann_result.v_star;
656
657 const Tscal Fab_a = Kernel::dW_3d(rab, h_a);
658 const Tscal Fab_b = Kernel::dW_3d(rab, h_b);
659
660 // GSPH force contribution
662 pmass,
663 p_star,
664 v_star,
665 rho_a,
666 rho_b,
667 omega_a,
668 omega_b,
669 Fab_a,
670 Fab_b,
671 r_ab_unit,
672 vxyz_a,
673 sum_axyz,
674 sum_du_a);
675 });
676
677 axyz[id_a] = sum_axyz;
678 if (duint_acc != nullptr) {
679 duint_acc[id_a] = sum_du_a;
680 }
681 });
682 });
683
684 buf_xyz.complete_event_state(e);
685 buf_axyz.complete_event_state(e);
686 buf_vxyz.complete_event_state(e);
687 buf_hpart.complete_event_state(e);
688 buf_omega.complete_event_state(e);
689 buf_density.complete_event_state(e);
690 buf_pressure.complete_event_state(e);
691 buf_cs.complete_event_state(e);
692
693 if (has_uint && buf_duint_ptr) {
694 buf_duint_ptr->complete_event_state(e);
695 }
696
697 sham::EventList resulting_events;
698 resulting_events.add_event(e);
699 pcache.complete_event_state(resulting_events);
700 });
701}
702
703// Explicit template instantiations
704// M-spline kernels (Monaghan)
705using namespace shammath;
709
710// Wendland kernels (C2, C4, C6)
Constants for field names in GSPH solver, organized by physics mode.
constexpr const char * duint
Time derivative of internal energy du/dt.
constexpr const char * axyz
3-acceleration field
constexpr const char * uint
Specific internal energy u.
constexpr const char * vxyz
3-velocity field
constexpr const char * xyz
Position field (3D coordinates).
constexpr const char * density
Density \rho (derived from h).
constexpr const char * hpart
Smoothing length field.
constexpr const char * omega
Grad-h correction factor \Omega.
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.
T * get_write_access(sham::EventList &depends_list, SourceLocation src_loc=SourceLocation{})
Get a read-write pointer to the buffer's data.
const T * get_read_access(sham::EventList &depends_list, SourceLocation src_loc=SourceLocation{}) const
Get a read-only pointer to the buffer's data.
A SYCL queue associated with a device and a context.
sycl::event submit(Fct &&fct)
Submits a kernel to the SYCL queue.
Class to manage a list of SYCL events.
Definition EventList.hpp:31
void add_event(sycl::event e)
Add an event to the list of events.
Definition EventList.hpp:87
Represents a collection of objects distributed across patches identified by a u64 id.
T & get(u64 id)
Returns a reference to an object in the collection.
GSPH derivative update module.
void update_derivs()
Update all derivatives using GSPH Riemann solver approach.
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.
PatchDataField< T > & get_field(u64 id) const
Get the underlying PatchDataField at the given id.
Exact Riemann solver for GSPH (Toro 2009).
GSPH force computation using Riemann solver results.
void accumulate_gsph_pair_force(bool use_inutsuka_v2, Tscal pmass, Tscal p_star, Tscal v_star, Tscal rho_a, Tscal rho_b, Tscal omega_a, Tscal omega_b, Tscal rab, Tscal rab_inv, Tscal h_a, Tscal h_b, Tvec r_ab_unit, Tvec vxyz_a, Tvec &sum_axyz, Tscal &sum_du_a)
Dispatch a single neighbor pair's force contribution to ChaWhitworth or InutsukaV2,...
Definition forces.hpp:232
void add_gsph_force_contribution(Tscal m_b, Tscal p_star, Tscal v_star, Tscal rho_a, Tscal rho_b, Tscal omega_a, Tscal omega_b, Tscal Fab_a, Tscal Fab_b, Tvec r_ab_unit, Tvec v_a, Tvec &dv_dt, Tscal &du_dt)
Add GSPH force contribution from a single neighbor pair.
Definition forces.hpp:121
GSPH derivative update module.
Iterative Riemann solver for GSPH (van Leer 1997).
T inv_sat_positive(T v, T minvsat=T{1e-9}, T satval=T{0.}) noexcept
inverse saturated (positive numbers only)
Definition math.hpp:841
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 math utility
Definition AABB.hpp:26
namespace for the main framework
Definition __init__.py:1
Effective face (volume element) interpolation for the Inutsuka (2002) GSPH formulation.
sph kernels
shambase::details::BasicStackEntry StackEntry
Alias for shambase::details::BasicStackEntry.
Patch object that contain generic patch information.
Definition Patch.hpp:33
u64 id_patch
unique key that identify the patch
Definition Patch.hpp:86