Skip to main content

Rocky Solver SDK 2024 R2

api_device_sph

Last update: 17.07.2025
1#pragma once
2
3#include <rocky20/api/rocky_api_sph_functions.h>
4#include <rocky20/api/rocky_model_api.hpp>
5#include <rocky20/api/sph/rocky_sph_element_api.hpp>
6#include <rocky20/api/sph/rocky_sph_interaction_api.hpp>
7#include <rocky20/api/sph/rocky_sph_model_api.hpp>
8
9namespace device {
10
11template <class Backend>
12struct DefaultElementsOperator
13{
14 template <typename Callable>
15 __host__ __device__ void operator()(
16 Callable function_ptr,
17 IRockyDeviceModel& device_model_api,
18 IRockySPHDeviceModel& sph_model,
19 IRockySPHElementInteraction& sph_interaction_api,
20 void* udf_data)
21 {
22 function_ptr(device_model_api, sph_model, sph_interaction_api, udf_data);
23 }
24};
25
26template <class Backend>
27struct DefaultTrianglesOperator
28{
29 template <typename Callable>
30 __host__ __device__ void operator()(
31 Callable function_ptr,
32 IRockyDeviceModel& device_model_api,
33 IRockySPHDeviceModel& sph_model,
34 IRockySPHTriangleInteraction& sph_interaction_api,
35 void* udf_data)
36 {
37 function_ptr(device_model_api, sph_model, sph_interaction_api, udf_data);
38 }
39};
40
41template <class Backend>
42struct DefaultOperator
43{
44 template <typename Callable>
45 __host__ __device__ void operator()(
46 Callable function_ptr,
47 IRockyDeviceModel& device_model_api,
48 IRockySPHDeviceModel& sph_model,
49 IRockySPHElement& sph_element_api,
50 void* udf_data)
51 {
52 function_ptr(device_model_api, sph_model, sph_element_api, udf_data);
53 }
54};
55
56template <class Backend>
57struct HeatTransferElementsOperator
58{
59 template <typename Callable>
60 __host__ __device__ void operator()(
61 Callable function_ptr,
62 IRockyDeviceModel& device_model_api,
63 IRockySPHDeviceModel& sph_model,
64 IRockySPHElementInteraction& sph_interaction_api,
65 void* udf_data)
66 {
67 // Compute and add heat transfer
68 real_sph heat_transfer =
69 function_ptr(device_model_api, sph_model, sph_interaction_api, udf_data);
70 auto home_sph_element = sph_interaction_api.get_home_element();
71 auto near_sph_element = sph_interaction_api.get_near_element();
72 home_sph_element.get_scalars().add_heat_transfer(heat_transfer);
73 near_sph_element.get_scalars().add_heat_transfer(-heat_transfer);
74
75 // Recording coupled heat transfer
76 if (home_sph_element.is_dem_coupled())
77 {
78 auto dem_particle = home_sph_element.get_linked_dem_particle();
79 auto dem_scalars = dem_particle.get_transfer_scalars();
80 dem_scalars.add_heat_transfer(heat_transfer);
81 }
82
83 if (near_sph_element.is_dem_coupled())
84 {
85 auto dem_particle = near_sph_element.get_linked_dem_particle();
86 auto dem_scalars = dem_particle.get_transfer_scalars();
87 dem_scalars.add_heat_transfer(-heat_transfer);
88 }
89 }
90};
91
92template <class Backend>
93struct HeatTransferTrianglesOperator
94{
95 template <typename Callable>
96 __host__ __device__ void operator()(
97 Callable function_ptr,
98 IRockyDeviceModel& device_model_api,
99 IRockySPHDeviceModel& sph_model,
100 IRockySPHTriangleInteraction& sph_interaction_api,
101 void* udf_data)
102 {
103 // Compute and add heat transfer
104 real_sph heat_transfer =
105 function_ptr(device_model_api, sph_model, sph_interaction_api, udf_data);
106
107 // reduction factor for SPH elements with multiple triangle interactions
108 // (1 / number of simultaneous triangle interactions)
109 heat_transfer /= max(1.0f, sph_model._impl.device_model->bnd_force_coefficient[sph_interaction_api._impl->home_index]);
110
111 sph_interaction_api.get_home_element().get_scalars().add_heat_transfer(heat_transfer);
112
113 // Recording data for SPH Boundary Statistics
114 const auto& data_recorder = sph_model._impl.device_model->boundary_data_recorder;
115 if (data_recorder
116 && data_recorder->is_enabled(SPHBoundaryDataRecorder::tSPHTriangleHeatTransfer))
117 {
118 const int index =
119 data_recorder->get_scalar_index(SPHBoundaryDataRecorder::tSPHTriangleHeatTransfer);
120 auto triangle_scalars = sph_interaction_api.get_near_triangle().get_scalars();
121 triangle_scalars.add_scalar(index, heat_transfer * device_model_api.get_timestep());
122 }
123
124 // Accumulation in a scalar used for heat coupling (positive when triangles receive heat)
125 auto* triangle_scalars = device_model_api._impl.model->triangle_scalars;
126 if (triangle_scalars && triangle_scalars->is_heat_exchanged_enabled())
127 {
128 Backend::AtomicAdd(
129 &triangle_scalars->get_heat_exchanged(sph_interaction_api._impl->near_index),
130 static_cast<double>(-heat_transfer * device_model_api.get_timestep()));
131 }
132 }
133};
134
135template <class Backend>
136struct ForceElementInteractionOperator
137{
138 template <typename Callable>
139 __host__ __device__ void operator()(
140 Callable function_ptr,
141 IRockyDeviceModel& device_model_api,
142 IRockySPHDeviceModel& sph_model,
143 IRockySPHElementInteraction& sph_interaction_api,
144 void* udf_data)
145 {
146 // custom calculation of the element-to-element acceleration
147 const real_sph3 acceleration =
148 function_ptr(device_model_api, sph_model, sph_interaction_api, udf_data);
149
150 sph_interaction_api.add_acceleration(acceleration);
151 }
152};
153
154template <class Backend>
155struct ForceTriangleInteractionOperator
156{
157 template <typename Callable>
158 __host__ __device__ void operator()(
159 Callable function_ptr,
160 IRockyDeviceModel& device_model_api,
161 IRockySPHDeviceModel& sph_model,
162 IRockySPHTriangleInteraction& sph_interaction_api,
163 void* udf_data)
164 {
165 // custom calculation of the element-to-triangle acceleration
166 const real_sph3 acceleration =
167 function_ptr(device_model_api, sph_model, sph_interaction_api, udf_data);
168
169 sph_interaction_api.add_acceleration(acceleration);
170 }
171};
172
173template <class Backend>
174struct ForceElementOperator
175{
176 template <typename Callable>
177 __host__ __device__ void operator()(
178 Callable function_ptr,
179 IRockyDeviceModel& device_model_api,
180 IRockySPHDeviceModel& sph_model,
181 IRockySPHElement& sph_element_api,
182 void* udf_data)
183 {
184 // custom calculation of the element acceleration (due to a body force)
185 const real_sph3 acceleration =
186 function_ptr(device_model_api, sph_model, sph_element_api, udf_data);
187
188 sph_element_api.add_acceleration(acceleration);
189 }
190};
191
192template <class backend, typename Operator, typename Callable>
193__host__ __device__ void launch_sph_particles_function(
194 SDeviceModel* device_data,
195 SPHDeviceModel* sph_device,
196 double current_time,
197 Callable function_ptr,
198 void* udf_data)
199{
200 IRockyDeviceModel device_model_api;
201 device_model_api._impl.model = device_data;
202 device_model_api._impl.current_time = current_time;
203 IRockySPHDeviceModel sph_model(device_data, sph_device->gen_sph, sph_device);
204 cuda_particle_sph* sph_elements = sph_device->prt_sph;
205
206 int thread_start, thread_step, thread_stop;
207 backend::ThreadIteration(
208 thread_start, thread_step, thread_stop, sph_device->gen_sph->nprt_active);
209 for (int i = thread_start; i < thread_stop; i += thread_step)
210 {
211 if (sph_elements[i].enabled() && !sph_elements[i].ghost())
212 {
213 // Particle Interface
214 IRockySPHElement sph_particle_api { device_data, sph_device, i };
215
216 // Trigger user kernel
217 Operator caller{};
218 caller(function_ptr, device_model_api, sph_model, sph_particle_api, udf_data);
219 }
220 }
221}
222
223template <class Backend, typename Operator, typename Callable>
224__host__ __device__ void launch_sph_interactions_particles(
225 SDeviceModel* device_data,
226 SPHDeviceModel* sph_device,
227 double current_time,
228 Callable function_ptr,
229 void* udf_data,
230 IterationType it_type)
231{
232 IRockyDeviceModel device_model_api;
233 device_model_api._impl.model = device_data;
234 device_model_api._impl.current_time = current_time;
235 IRockySPHDeviceModel sph_model(device_data, sph_device->gen_sph, sph_device);
236
237 RockySPHElementInteraction sph_interaction(device_data, sph_device, current_time);
238 IRockySPHElementInteraction sph_interaction_api { &sph_interaction };
239
240 cuda_general_sph* gen_sph = sph_device->gen_sph;
241 const auto& periodic_domain = device_data->gen->periodic_domain;
242
243 // Local storage for interactions and periodics
244 int* nn_list = nullptr;
245 PeriodicPosition* nn_periodic = nullptr;
246 if (!gen_sph->use_particles_neighbors_list)
247 {
248 nn_list = sph_device->local_n_list + Backend::ThreadIndex() * gen_sph->num_neighbors_max;
249 if (periodic_domain.enabled())
250 nn_periodic =
251 sph_device->local_n_periodic + Backend::ThreadIndex() * gen_sph->num_neighbors_max;
252 }
253
254 // Thread
255 // TODO (ROCKY-20361): This will no longer be needed once we use SPH Search Handlers
256 const int n_entities = [&gen_sph, it_type] {
257 if (!gen_sph->use_particles_neighbors_list)
258 return gen_sph->nprt_active;
259 if (it_type == itFreeAndFreeCoupled)
260 {
261 return gen_sph->ncont_ff_p_int + gen_sph->ncont_fc_p_int;
262 }
263 else if (it_type == itFreeCoupledOnly)
264 {
265 return gen_sph->ncont_fc_p_int;
266 }
267 else // itAll
268 {
269 return gen_sph->ncont_ff_p_int + gen_sph->ncont_fc_p_int + gen_sph->ncont_cc_p_int;
270 }
271 }();
272 int thread_start, thread_step, thread_stop;
273 Backend::ThreadIteration(thread_start, thread_step, thread_stop, n_entities);
274 for (int index = thread_start; index < thread_stop; index += thread_step)
275 {
276 // Handling different interactionhoods structures
277 cuda_sph_contact* _sph_interaction = nullptr;
278 int n_iter, home_index, near_index, offset = 0;
279 if (gen_sph->use_particles_neighbors_list)
280 {
281 _sph_interaction = (it_type == itFreeCoupledOnly)
282 ? sph_device->cnt_sph_p + sph_device->ind_sph_p[index + gen_sph->ncont_ff_p_int]
283 : sph_device->cnt_sph_p + sph_device->ind_sph_p[index];
284
285 home_index = _sph_interaction->nh;
286 near_index = _sph_interaction->nn;
287 n_iter = 1;
288 }
289 else
290 {
291 int nn_ff, nn_fc, nn_cc;
292 if (periodic_domain.enabled())
293 {
294 sph::get_periodic_neighbors(home_index, device_data->ind_per[index], device_data->gen, sph_device, current_time, 0.0f,
295 nn_ff, nn_fc, nn_cc, nn_list, nn_periodic);
296 if(gen_sph->nprt_active_plus_halo > gen_sph->nprt_active)
297 sph::get_periodic_multi_GPU_neighbors(home_index, device_data->ind_per[index], device_data->gen, sph_device, current_time, 0.0f,
298 nn_ff, nn_fc, nn_cc, nn_list, nn_periodic);
299 sph::partition_array(home_index, sph_device, nn_ff, nn_fc, nn_cc, nn_list, nn_periodic);
300 }
301 else
302 {
303 sph::get_non_periodic_neighbors(home_index, index, device_data->gen, sph_device, current_time, 0.0f, nn_ff, nn_fc, nn_cc, nn_list);
304 if(gen_sph->nprt_active_plus_halo > gen_sph->nprt_active)
305 sph::get_non_periodic_multi_GPU_neighbors(home_index, device_data->gen, sph_device, current_time, 0.0f, nn_ff, nn_fc, nn_cc, nn_list);
306 sph::partition_array(home_index, sph_device, nn_ff, nn_fc, nn_cc, nn_list, nullptr);
307 }
308 if (it_type == itFreeAndFreeCoupled)
309 {
310 n_iter = nn_ff + nn_fc;
311 }
312 else if (it_type == itFreeCoupledOnly)
313 {
314 n_iter = nn_fc;
315 offset = nn_ff;
316 }
317 else // itAll
318 {
319 n_iter = nn_ff + nn_fc + nn_cc;
320 }
321 }
322
323 // Home Interaction Interface
324 sph_interaction.reset_home_element(home_index);
325
326 // Loop over number of active interactions
327 for (int i = 0; i < n_iter; ++i)
328 {
329 // Periodic Stuff
330 PeriodicPosition cnt_per {};
331 if (!gen_sph->use_particles_neighbors_list)
332 {
333 near_index = nn_list[i + offset];
334 if (periodic_domain.enabled())
335 cnt_per = nn_periodic[i + offset];
336 }
337 else if (periodic_domain.enabled())
338 cnt_per = (it_type == itFreeCoupledOnly)
339 ? sph_device
340 ->per_sph_cnt_p[sph_device->ind_sph_p[index + gen_sph->ncont_ff_p_int]]
341 : sph_device->per_sph_cnt_p[sph_device->ind_sph_p[index]];
342
343 // Near Interaction Interface
344 sph_interaction.reset_near_element(near_index, cnt_per);
345
346 // Trigger user kernel
347 Operator caller {};
348 caller(function_ptr, device_model_api, sph_model, sph_interaction_api, udf_data);
349 }
350 }
351}
352
353template <class Backend, typename Operator, typename Callable>
354__host__ __device__ void launch_sph_interactions_triangles(
355 SDeviceModel* device_data,
356 SPHDeviceModel* sph_device,
357 double current_time,
358 Callable function_ptr,
359 void* udf_data)
360{
361 IRockyDeviceModel device_model_api;
362 device_model_api._impl.model = device_data;
363 device_model_api._impl.current_time = current_time;
364 IRockySPHDeviceModel sph_model(device_data, sph_device->gen_sph, sph_device);
365
366 RockySPHTriangleInteraction sph_interaction(device_data, sph_device, current_time);
367 IRockySPHTriangleInteraction sph_interaction_api { &sph_interaction };
368
369 const auto& periodic_domain = device_data->gen->periodic_domain;
370
371 // loop only over element/triangle pairs with projection inside the triangle
372 int thread_start, thread_step, thread_stop;
373 Backend::ThreadIteration(thread_start, thread_step, thread_stop, sph_device->gen_sph->n_triangle_force_pairs);
374 for (int i = thread_start; i < thread_stop; i += thread_step)
375 {
376 const int index = sph_device->triangle_force_pairs[i];
377 cuda_sph_contact* _sph_interaction = sph_device->cnt_sph_t + index;
378 PeriodicPosition cnt_per {};
379 if (periodic_domain.enabled())
380 cnt_per = sph_device->per_sph_cnt_t[index];
381
382 // Interaction Interface
383 sph_interaction.reset_triangle_contact(_sph_interaction, cnt_per);
384
385 // ROCKY-17943: For periodic cases, skipping interaction w/ triangles, such that
386 // the projected point is positioned outside periodic domain
387 if (periodic_domain.enabled()
388 && periodic_domain.is_outside_cartesian_periodic_bounds(sph_interaction.inside_point))
389 {
390 continue;
391 }
392
393 // Trigger user kernel
394 Operator caller {};
395 caller(function_ptr, device_model_api, sph_model, sph_interaction_api, udf_data);
396 }
397}
398
399} // namespace device
Definition rocky_model_api.hpp:19
ROCKY_FUNCTIONS double get_timestep() const
Definition rocky_model_api.hpp:85
ROCKY_FUNCTIONS IRockyTriangleScalars get_scalars()
Definition rocky_sph_model_api.hpp:146
Definition rocky_sph_interaction_api.hpp:18
ROCKY_FUNCTIONS IRockySPHElement get_near_element() const
Definition rocky_sph_interaction_api.hpp:112
ROCKY_FUNCTIONS void add_acceleration(float3 acceleration)
Definition rocky_sph_interaction_api.hpp:122
ROCKY_FUNCTIONS IRockySPHElement get_home_element() const
Definition rocky_sph_interaction_api.hpp:107
ROCKY_FUNCTIONS void add_heat_transfer(const float value)
Definition rocky_sph_element_scalars.hpp:243
Definition rocky_sph_element_api.hpp:106
ROCKY_FUNCTIONS IRockySPHElementScalars get_scalars()
ROCKY_FUNCTIONS void add_acceleration(const float3 &acceleration) const
Definition rocky_sph_element_api.hpp:146
Definition rocky_sph_interaction_api.hpp:137
ROCKY_FUNCTIONS void add_acceleration(float3 acceleration)
Definition rocky_sph_interaction_api.hpp:251
ROCKY_FUNCTIONS IRockyGeometryTriangle get_near_triangle() const
Definition rocky_sph_interaction_api.hpp:257
ROCKY_FUNCTIONS IRockySPHElement get_home_element() const
Definition rocky_sph_interaction_api.hpp:241
ROCKY_FUNCTIONS void add_scalar(int scalar_index, double value) const

Connect with Ansys