(git:98357aa)
Loading...
Searching...
No Matches
grid_gpu_integrate.cu
Go to the documentation of this file.
1/*----------------------------------------------------------------------------*/
2/* CP2K: A general program to perform molecular dynamics simulations */
3/* Copyright 2000-2026 CP2K developers group <https://cp2k.org> */
4/* */
5/* SPDX-License-Identifier: BSD-3-Clause */
6/*----------------------------------------------------------------------------*/
7
8/*
9 * inspirations from the gpu backend
10 * Authors :
11 - Mathieu Taillefumier (ETH Zurich / CSCS)
12 - Advanced Micro Devices, Inc.
13 - Ole Schuett
14*/
15
16#include <algorithm>
17#include <assert.h>
18#include <limits.h>
19#include <math.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23
24#include "grid_gpu_context.h"
27
28#if defined(_OMP_H)
29#error "OpenMP should not be used in .cu files to accommodate HIP."
30#endif
31
32namespace rocm_backend {
33
34// do a block reduction for a block of size 64 and return the final sum to
35// thread_id = 0
36template <typename T>
37__device__ __inline__ T block_reduce_64(T *table, const T val_, const int tid) {
38 // AMD GPU have warp size of 64 while nvidia GPUs have warpSize of 32 so the
39 // first step is common to both platforms.
40 table[tid] = val_;
41 __syncthreads();
42
43 T val = 0.0;
44
45 if (tid < 32) {
46 val = table[tid] + table[tid + 32];
47
48 for (int offset = 16; offset > 0; offset >>= 1) {
49#if defined(__CUDACC__)
50 val += __shfl_down_sync(0xffffffff, val, offset);
51#else
52 val += __shfl_down(val, offset);
53#endif
54 }
55 }
56
57 // prevents threads >= 32 (which never touch table[]) from racing ahead and
58 // overwriting it for the next call before the reduction above has read it.
59 __syncthreads();
60 return val;
61}
62
63template <typename T, bool COMPUTE_TAU>
64__global__ __launch_bounds__(64) void compute_hab(const kernel_params dev_) {
65 // Copy task from global to shared memory and precompute some stuff.
66 extern __shared__ T shared_memory[];
67 // T *smem_cab = &shared_memory[dev_.smem_cab_offset];
68 const int number_of_tasks = dev_.num_tasks_per_block_dev[block_index()];
69
70 if (number_of_tasks == 0)
71 return;
72
73 T *smem_alpha = &shared_memory[0];
74 const int tid = thread_global_index();
75 const int offset = dev_.sorted_blocks_offset_dev[block_index()];
76
77 T *__restrict__ smem_cab = nullptr;
78
79 smem_cab = allocate_workspace<T>(dev_);
80
81 for (int tk = 0; tk < number_of_tasks; tk++) {
82 __shared__ smem_task<T> task;
83 const int task_id = dev_.task_sorted_by_blocks_dev[offset + tk];
84 if (dev_.tasks[task_id].skip_task)
85 continue;
86
87 // all warps need to be synchronized here before modifying the task
88 // information.
89 __syncthreads();
90 fill_smem_task_coef(dev_, task_id, task);
91
92 T *__restrict__ coef_ = reinterpret_cast<T *>(__builtin_assume_aligned(
93 &dev_.buffers_dev.coef[dev_.tasks[task_id].coef_offset], 32));
94
95 __syncthreads();
96 compute_alpha(task, smem_alpha);
97 __syncthreads();
98 cxyz_to_cab(task, smem_alpha, coef_, smem_cab);
99 __syncthreads();
100
101 for (int i = tid / 8; i < task.nsgf_setb; i += 8) {
102 for (int j = tid % 8; j < task.nsgf_seta; j += 8) {
103 T tmp = 0.0;
104 for (int jco = task.first_cosetb; jco < task.ncosetb; jco++) {
105 const T sphib = task.sphib[i * task.maxcob + jco];
106 const auto &b = coset_inv[jco];
107 for (int ico = task.first_coseta; ico < task.ncoseta; ico++) {
108 const auto &a = coset_inv[ico];
109 const T hab = get_hab<COMPUTE_TAU, T>(a, b, task.zeta, task.zetb,
110 task.n1, smem_cab);
111 const T sphia_times_sphib =
112 task.sphia[j * task.maxcoa + ico] * sphib;
113 tmp += hab * sphia_times_sphib;
114 }
115 }
116 if (task.block_transposed) {
117 task.hab_block[j * task.nsgfb + i] += tmp;
118 } else {
119 task.hab_block[i * task.nsgfa + j] += tmp;
120 }
121 }
122 }
123 }
124}
125
126template <typename T, typename T3, bool COMPUTE_TAU>
127__global__
128__launch_bounds__(64) void compute_hab_forces(const kernel_params dev_) {
129 // Copy task from global to shared memory and precompute some stuff.
130 extern __shared__ T shared_memory[];
131 const int number_of_tasks = dev_.num_tasks_per_block_dev[block_index()];
132
133 if (number_of_tasks == 0)
134 return;
135
136 T *smem_alpha = &shared_memory[0];
137 const int tid = thread_global_index();
138 const int offset = dev_.sorted_blocks_offset_dev[block_index()];
139
140 T *__restrict__ smem_cab = allocate_workspace<T>(dev_);
141
142 T fa[3], fb[3];
143 T virial[9];
144
145 fa[0] = 0.0;
146 fa[1] = 0.0;
147 fa[2] = 0.0;
148 fb[0] = 0.0;
149 fb[1] = 0.0;
150 fb[2] = 0.0;
151
152 virial[0] = 0.0;
153 virial[1] = 0.0;
154 virial[2] = 0.0;
155 virial[3] = 0.0;
156 virial[4] = 0.0;
157 virial[5] = 0.0;
158 virial[6] = 0.0;
159 virial[7] = 0.0;
160 virial[8] = 0.0;
161
162 for (int tk = 0; tk < number_of_tasks; tk++) {
163 __shared__ smem_task<T> task;
164 const int task_id = dev_.task_sorted_by_blocks_dev[offset + tk];
165 if (dev_.tasks[task_id].skip_task)
166 continue;
167 fill_smem_task_coef(dev_, task_id, task);
168
169 T *__restrict__ coef_ =
170 &dev_.buffers_dev.coef[dev_.tasks[task_id].coef_offset];
171 __syncthreads();
172 compute_alpha(task, smem_alpha);
173 __syncthreads();
174 cxyz_to_cab(task, smem_alpha, coef_, smem_cab);
175 __syncthreads();
176
177 for (int i = tid / 8; i < task.nsgf_setb; i += 8) {
178 for (int j = tid % 8; j < task.nsgf_seta; j += 8) {
179 T tmp = 0.0;
180 T block_value = 0.0;
181 if (task.block_transposed) {
182 block_value =
183 task.pab_block[j * task.nsgfb + i] * task.off_diag_twice;
184 } else {
185 block_value =
186 task.pab_block[i * task.nsgfa + j] * task.off_diag_twice;
187 }
188 for (int jco = task.first_cosetb; jco < task.ncosetb; jco++) {
189 const T sphib = task.sphib[i * task.maxcob + jco];
190 const auto &b = coset_inv[jco];
191 for (int ico = task.first_coseta; ico < task.ncoseta; ico++) {
192 const auto &a = coset_inv[ico];
193 const T hab = get_hab<COMPUTE_TAU, T>(a, b, task.zeta, task.zetb,
194 task.n1, smem_cab);
195 T sphia_times_sphib = task.sphia[j * task.maxcoa + ico] * sphib;
196 tmp += hab * sphia_times_sphib;
197
198 sphia_times_sphib *= block_value;
199 fa[0] += sphia_times_sphib *
200 get_force_a<COMPUTE_TAU, T>(a, b, 0, task.zeta, task.zetb,
201 task.n1, smem_cab);
202 fa[1] += sphia_times_sphib *
203 get_force_a<COMPUTE_TAU, T>(a, b, 1, task.zeta, task.zetb,
204 task.n1, smem_cab);
205 fa[2] += sphia_times_sphib *
206 get_force_a<COMPUTE_TAU, T>(a, b, 2, task.zeta, task.zetb,
207 task.n1, smem_cab);
208
209 fb[0] += sphia_times_sphib *
210 get_force_b<COMPUTE_TAU, T>(a, b, 0, task.zeta, task.zetb,
211 task.rab, task.n1, smem_cab);
212 fb[1] += sphia_times_sphib *
213 get_force_b<COMPUTE_TAU, T>(a, b, 1, task.zeta, task.zetb,
214 task.rab, task.n1, smem_cab);
215 fb[2] += sphia_times_sphib *
216 get_force_b<COMPUTE_TAU, T>(a, b, 2, task.zeta, task.zetb,
217 task.rab, task.n1, smem_cab);
218
219 if (dev_.buffers_dev.virial != nullptr) {
220 virial[0] +=
221 sphia_times_sphib *
222 (get_virial_a<COMPUTE_TAU, T>(a, b, 0, 0, task.zeta,
223 task.zetb, task.n1, smem_cab) +
224 get_virial_b<COMPUTE_TAU, T>(a, b, 0, 0, task.zeta,
225 task.zetb, task.rab, task.n1,
226 smem_cab));
227 virial[1] +=
228 sphia_times_sphib *
229 (get_virial_a<COMPUTE_TAU, T>(a, b, 0, 1, task.zeta,
230 task.zetb, task.n1, smem_cab) +
231 get_virial_b<COMPUTE_TAU, T>(a, b, 0, 1, task.zeta,
232 task.zetb, task.rab, task.n1,
233 smem_cab));
234 virial[2] +=
235 sphia_times_sphib *
236 (get_virial_a<COMPUTE_TAU, T>(a, b, 0, 2, task.zeta,
237 task.zetb, task.n1, smem_cab) +
238 get_virial_b<COMPUTE_TAU, T>(a, b, 0, 2, task.zeta,
239 task.zetb, task.rab, task.n1,
240 smem_cab));
241 virial[3] +=
242 sphia_times_sphib *
243 (get_virial_a<COMPUTE_TAU, T>(a, b, 1, 0, task.zeta,
244 task.zetb, task.n1, smem_cab) +
245 get_virial_b<COMPUTE_TAU, T>(a, b, 1, 0, task.zeta,
246 task.zetb, task.rab, task.n1,
247 smem_cab));
248 virial[4] +=
249 sphia_times_sphib *
250 (get_virial_a<COMPUTE_TAU, T>(a, b, 1, 1, task.zeta,
251 task.zetb, task.n1, smem_cab) +
252 get_virial_b<COMPUTE_TAU, T>(a, b, 1, 1, task.zeta,
253 task.zetb, task.rab, task.n1,
254 smem_cab));
255 virial[5] +=
256 sphia_times_sphib *
257 (get_virial_a<COMPUTE_TAU, T>(a, b, 1, 2, task.zeta,
258 task.zetb, task.n1, smem_cab) +
259 get_virial_b<COMPUTE_TAU, T>(a, b, 1, 2, task.zeta,
260 task.zetb, task.rab, task.n1,
261 smem_cab));
262 virial[6] +=
263 sphia_times_sphib *
264 (get_virial_a<COMPUTE_TAU, T>(a, b, 2, 0, task.zeta,
265 task.zetb, task.n1, smem_cab) +
266 get_virial_b<COMPUTE_TAU, T>(a, b, 2, 0, task.zeta,
267 task.zetb, task.rab, task.n1,
268 smem_cab));
269 virial[7] +=
270 sphia_times_sphib *
271 (get_virial_a<COMPUTE_TAU, T>(a, b, 2, 1, task.zeta,
272 task.zetb, task.n1, smem_cab) +
273 get_virial_b<COMPUTE_TAU, T>(a, b, 2, 1, task.zeta,
274 task.zetb, task.rab, task.n1,
275 smem_cab));
276 virial[8] +=
277 sphia_times_sphib *
278 (get_virial_a<COMPUTE_TAU, T>(a, b, 2, 2, task.zeta,
279 task.zetb, task.n1, smem_cab) +
280 get_virial_b<COMPUTE_TAU, T>(a, b, 2, 2, task.zeta,
281 task.zetb, task.rab, task.n1,
282 smem_cab));
283 }
284 }
285 }
286
287 if (task.block_transposed) {
288 task.hab_block[j * task.nsgfb + i] += tmp;
289 } else {
290 task.hab_block[i * task.nsgfa + j] += tmp;
291 }
292 }
293 }
294 __syncthreads();
295 }
296
297 // theoretically not needed
298 __syncthreads();
299
300 const int task_id = dev_.task_sorted_by_blocks_dev[offset];
301 const auto &glb_task = dev_.tasks[task_id];
302 const int iatom = glb_task.iatom;
303 const int jatom = glb_task.jatom;
304 T *forces_a = &dev_.buffers_dev.forces[3 * iatom];
305 T *forces_b = &dev_.buffers_dev.forces[3 * jatom];
306
307 T *sum = (T *)shared_memory;
308 if (dev_.buffers_dev.virial != nullptr) {
309
310 for (int i = 0; i < 9; i++) {
311 virial[i] = block_reduce_64<T>(sum, virial[i], tid);
312
313 if (tid == 0)
314 atomicAdd(dev_.buffers_dev.virial + i, virial[i]);
315 }
316 }
317
318 for (int i = 0; i < 3; i++) {
319 fa[i] = block_reduce_64<T>(sum, fa[i], tid);
320
321 if (tid == 0)
322 atomicAdd(forces_a + i, fa[i]);
323
324 fb[i] = block_reduce_64<T>(sum, fb[i], tid);
325
326 if (tid == 0)
327 atomicAdd(forces_b + i, fb[i]);
328 }
329}
330
331/*******************************************************************************
332 * Cuda kernel for calcualting the coefficients of a potential (density,
333 etc...) for a given pair of gaussian basis sets
334
335We compute the discretized version of the following integral
336
337$$\int _\infty ^\infty V_{ijk} P^\alpha_iP^beta_j P ^ \gamma _ k Exp(- \eta
338|r_{ijk} - r_c|^2)$$
339
340where in practice the summation is over a finite domain. The discrete form has
341this shape
342
343$$
344\sum_{ijk < dmoain} V_{ijk} (x_i - x_c)^\alpha (y_j - y_c)^\beta (z_k - z_c) ^
345\gamma Exp(- \eta |r_{ijk} - r_c|^2)
346$$
347
348where $0 \le \alpha + \beta + \gamma \le lmax$
349
350It is formely the same operation than collocate (from a discrete point of view)
351but the implementation differ because of technical reasons.
352
353So most of the code is the same except the core of the routine that is
354specialized to the integration.
355
356******************************************************************************/
357template <typename T, typename T3, bool distributed__, bool orthogonal_,
358 int lbatch = 20>
359__global__
360__launch_bounds__(64) void integrate_kernel(const kernel_params dev_) {
361 if (dev_.tasks[dev_.first_task + block_index()].skip_task)
362 return;
363
364 const int tid = thread_global_index();
365
366 // __shared__ T dh_inv_[9];
367 __shared__ T dh_[9];
368
369 // for (int d = tid; d < 9; d += blockDim.x * blockDim.y * blockDim.z)
370 // dh_inv_[d] = dev_.dh_inv_[d];
371
372 if (tid < 9)
373 dh_[tid] = dev_.dh_[tid];
374
375 __shared__ smem_task_reduced<T, T3> task;
376 fill_smem_task_reduced(dev_, dev_.first_task + block_index(), task);
377
378 if (tid == 0) {
379 setup_task_cube_center<T, T3, distributed__>(dev_, task);
380 }
381 __syncthreads();
382
383 __shared__ T accumulator[lbatch][64];
384
385 // we use a multi pass algorithm here because shared memory usage (or
386 // register) would become too high for high angular momentum
387
388 const short int size_loop =
389 (ncoset(task.lp) / lbatch + ((ncoset(task.lp) % lbatch) != 0)) * lbatch;
390 const short int length = ncoset(task.lp);
391 for (int ico = 0; ico < size_loop; ico += lbatch) {
392#pragma unroll lbatch
393 for (int i = 0; i < lbatch; i++)
394 accumulator[i][tid] = 0.0;
395
396 __syncthreads();
397
398 for (int z = threadIdx.z; z < task.cube_size.z; z += blockDim.z) {
399 int z2 = wrap_grid_index(z + task.cube_center.z, dev_.grid_full_size_.z);
400
401 if (distributed__) {
402 // known at compile time. Will be stripped away
403 if (task.apply_border_mask) {
404 /* check if the point is within the window */
405 if ((z2 < task.window_shift.z) || (z2 > task.window_size.z)) {
406 continue;
407 }
408 }
409 }
410
411 /* compute the coordinates of the point in atomic coordinates */
412 int ymin = 0;
413 int ymax = task.cube_size.y - 1;
414 T kremain = 0.0;
415
416 if (orthogonal_ && !task.apply_border_mask) {
417 kremain = calculate_ymix_ymax_boundaries(task, z, ymin, ymax);
418 }
419
420 for (int y = ymin + threadIdx.y; y <= ymax; y += blockDim.y) {
421 int y2 =
422 wrap_grid_index(y + task.cube_center.y, dev_.grid_full_size_.y);
423
424 if (distributed__) {
425 /* check if the point is within the window */
426 if (task.apply_border_mask) {
427 if ((y2 < task.window_shift.y) || (y2 > task.window_size.y)) {
428 continue;
429 }
430 }
431 }
432
433 int xmin = 0;
434 int xmax = task.cube_size.x - 1;
435
436 if (orthogonal_ && !task.apply_border_mask) {
437 calculate_xmin_xmax_boundaries<T, T3>(task, y, kremain, xmin, xmax);
438 }
439
440 for (int x = xmin + threadIdx.x; x <= xmax; x += blockDim.x) {
441 int x2 =
442 wrap_grid_index(x + task.cube_center.x, dev_.grid_full_size_.x);
443
444 if (distributed__) {
445 /* check if the point is within the window */
446 if (task.apply_border_mask) {
447 if ((x2 < task.window_shift.x) || (x2 > task.window_size.x)) {
448 continue;
449 }
450 }
451 }
452
453 // I make no distinction between orthogonal and non orthogonal
454 // cases
455 T3 r3;
456
457 r3 = compute_coordinates(dh_, (x + task.lb_cube.x + task.roffset.x),
458 (y + task.lb_cube.y + task.roffset.y),
459 (z + task.lb_cube.z + task.roffset.z));
460 // check if the point is inside the sphere or not. Note that it does
461 // not apply for the orthogonal case when the full sphere is inside
462 // the region of interest.
463 const T r3x2 = r3.x * r3.x;
464 const T r3y2 = r3.y * r3.y;
465 const T r3z2 = r3.z * r3.z;
466
467 if (distributed__) {
468 // check if the point is inside the sphere or not. Note that it does
469 // not apply for the orthorhombic case when the full sphere is
470 // inside the region of interest.
471
472 if (((task.radius * task.radius) <= (r3x2 + r3y2 + r3z2)) &&
473 (!orthogonal_ || task.apply_border_mask))
474 continue;
475 } else {
476 // we do not need to do this test for the orthorhombic case
477 if ((!orthogonal_) &&
478 ((task.radius * task.radius) <= (r3x2 + r3y2 + r3z2)))
479 continue;
480 }
481
482 // read the next point assuming that reading is non blocking untill
483 // the register is actually needed for computation. This is true on
484 // NVIDIA hardware
485
486 const int grid_index =
487 (z2 * dev_.grid_local_size_.y + y2) * dev_.grid_local_size_.x +
488 x2;
489 T grid_value = __ldg(&dev_.buffers_dev.grid[grid_index]);
490
491 const T r3xy = r3.x * r3.y;
492 const T r3xz = r3.x * r3.z;
493 const T r3yz = r3.y * r3.z;
494
495 grid_value *= exp(-(r3x2 + r3y2 + r3z2) * task.zetp);
496
497 switch (ico / lbatch) {
498 case 0: {
499 accumulator[0][tid] += grid_value;
500
501 if (task.lp >= 1) {
502 accumulator[1][tid] += grid_value * r3.x;
503 accumulator[2][tid] += grid_value * r3.y;
504 accumulator[3][tid] += grid_value * r3.z;
505 }
506
507 if (task.lp >= 2) {
508 accumulator[4][tid] += grid_value * r3x2;
509 accumulator[5][tid] += grid_value * r3xy;
510 accumulator[6][tid] += grid_value * r3xz;
511 accumulator[7][tid] += grid_value * r3y2;
512 accumulator[8][tid] += grid_value * r3yz;
513 accumulator[9][tid] += grid_value * r3z2;
514 }
515 if (task.lp >= 3) {
516 T tmp = grid_value * r3x2;
517 accumulator[10][tid] += tmp * r3.x;
518 accumulator[11][tid] += tmp * r3.y;
519 accumulator[12][tid] += tmp * r3.z;
520 tmp = grid_value * r3.x;
521 accumulator[13][tid] += tmp * r3y2;
522 accumulator[14][tid] += tmp * r3yz;
523 accumulator[15][tid] += tmp * r3z2;
524 tmp = grid_value * r3y2;
525 accumulator[16][tid] += tmp * r3.y;
526 accumulator[17][tid] += tmp * r3.z;
527 tmp = grid_value * r3z2;
528 accumulator[18][tid] += tmp * r3.y;
529 accumulator[19][tid] += tmp * r3.z;
530 }
531 } break;
532 case 1: {
533 if (task.lp >= 4) {
534 T tmp = grid_value * r3x2;
535 accumulator[0][tid] += tmp * r3x2;
536 accumulator[1][tid] += tmp * r3xy;
537 accumulator[2][tid] += tmp * r3xz;
538 accumulator[3][tid] += tmp * r3y2;
539 accumulator[4][tid] += tmp * r3yz;
540 accumulator[5][tid] += tmp * r3z2;
541 tmp = grid_value * r3y2;
542 accumulator[6][tid] += tmp * r3xy;
543 accumulator[7][tid] += tmp * r3xz;
544 tmp = grid_value * r3z2;
545 accumulator[8][tid] += tmp * r3xy;
546 accumulator[9][tid] += tmp * r3xz;
547 }
548 if (task.lp >= 4) {
549 T tmp = grid_value * r3y2;
550 accumulator[10][tid] += tmp * r3y2;
551 accumulator[11][tid] += tmp * r3yz;
552 accumulator[12][tid] += tmp * r3z2;
553 accumulator[13][tid] += grid_value * r3yz * r3z2;
554 accumulator[14][tid] += grid_value * r3z2 * r3z2;
555 }
556 if (task.lp >= 5) {
557 T tmp = grid_value * r3x2 * r3x2;
558 accumulator[15][tid] += tmp * r3.x;
559 accumulator[16][tid] += tmp * r3.y;
560 accumulator[17][tid] += tmp * r3.z;
561 tmp = grid_value * r3x2;
562 accumulator[18][tid] += tmp * r3.x * r3y2;
563 accumulator[19][tid] += tmp * r3xy * r3.z;
564 }
565 } break;
566 case 2: {
567 T tmp = grid_value * r3x2;
568 accumulator[0][tid] += tmp * r3.x * r3z2;
569 accumulator[1][tid] += tmp * r3y2 * r3.y;
570 accumulator[2][tid] += tmp * r3y2 * r3.z;
571 accumulator[3][tid] += tmp * r3.y * r3z2;
572 accumulator[4][tid] += tmp * r3z2 * r3.z;
573 tmp = grid_value * r3.x * r3y2;
574 accumulator[5][tid] += tmp * r3y2;
575 accumulator[6][tid] += tmp * r3yz;
576 accumulator[7][tid] += tmp * r3z2;
577 tmp = grid_value * r3.x * r3z2;
578 accumulator[8][tid] += tmp * r3yz;
579 accumulator[9][tid] += tmp * r3z2;
580 tmp = grid_value * r3y2 * r3.y;
581 accumulator[10][tid] += tmp * r3y2;
582 accumulator[11][tid] += tmp * r3yz;
583 accumulator[12][tid] += tmp * r3z2;
584 accumulator[13][tid] += grid_value * r3y2 * r3z2 * r3.z;
585 accumulator[14][tid] += grid_value * r3.y * r3z2 * r3z2;
586 accumulator[15][tid] += grid_value * r3z2 * r3z2 * r3.z;
587 if (task.lp >= 6) {
588 tmp = grid_value * r3x2 * r3x2;
589 accumulator[16][tid] += tmp * r3x2; // x^6
590 accumulator[17][tid] += tmp * r3xy; // x^5 y
591 accumulator[18][tid] += tmp * r3xz; // x^5 z
592 accumulator[19][tid] += tmp * r3y2; // x^4 y^2
593 }
594 } break;
595 case 3: {
596 T tmp = grid_value * r3x2;
597 accumulator[0][tid] += tmp * r3x2 * r3yz; // x^4 y z
598 accumulator[1][tid] += tmp * r3x2 * r3z2; // x^4 z^2
599 accumulator[2][tid] += tmp * r3y2 * r3xy; // x^3 y^3
600 accumulator[3][tid] += tmp * r3y2 * r3xz; // x^3 y^2 z
601 accumulator[4][tid] += tmp * r3xy * r3z2; // x^3 y z^2
602 accumulator[5][tid] += tmp * r3z2 * r3xz; // x^3 z^3
603 accumulator[6][tid] += tmp * r3y2 * r3y2; // x^2 y^4
604 accumulator[7][tid] += tmp * r3y2 * r3yz; // x^3 y^2 z
605 accumulator[8][tid] += tmp * r3y2 * r3z2; // x^2 y^2 z^2
606 accumulator[9][tid] += tmp * r3z2 * r3yz; // x^2 y z^3
607 accumulator[10][tid] += tmp * r3z2 * r3z2; // x^2 z^4
608 tmp = grid_value * r3y2 * r3y2;
609 accumulator[11][tid] += tmp * r3xy; // x y^5
610 accumulator[12][tid] += tmp * r3xz; // x y^4 z
611 accumulator[13][tid] +=
612 grid_value * r3y2 * r3xy * r3z2; // x y^3 z^2
613 accumulator[14][tid] +=
614 grid_value * r3y2 * r3z2 * r3xz; // x y^2 z^3
615 accumulator[15][tid] += grid_value * r3xy * r3z2 * r3z2; // x y z^4
616 accumulator[16][tid] += grid_value * r3z2 * r3z2 * r3xz; // x z^5
617 accumulator[17][tid] += tmp * r3y2; // y^6
618 accumulator[18][tid] += tmp * r3yz; // y^5 z
619 accumulator[19][tid] += tmp * r3z2; // y^4 z^2
620 } break;
621 default:
622 for (int ic = 0; (ic < lbatch) && ((ic + ico) < length); ic++) {
623 auto &co = coset_inv[ic + ico];
624 T tmp = 1.0;
625 for (int po = 0; po < (co.l[2] >> 1); po++)
626 tmp *= r3z2;
627 if (co.l[2] & 0x1)
628 tmp *= r3.z;
629 for (int po = 0; po < (co.l[1] >> 1); po++)
630 tmp *= r3y2;
631 if (co.l[1] & 0x1)
632 tmp *= r3.y;
633 for (int po = 0; po < (co.l[0] >> 1); po++)
634 tmp *= r3x2;
635 if (co.l[0] & 0x1)
636 tmp *= r3.x;
637 accumulator[ic][tid] += tmp * grid_value;
638 }
639 break;
640 }
641 }
642 }
643 }
644
645 const int max_i = min(length - ico, lbatch);
646
647 __syncthreads();
648
649 // we know there is only 1 wavefront in each block lbatch threads could
650 // reduce the values saved by all threads of the warp and save results do a
651 // shuffle_down by hand
652
653 if (tid < 32) {
654 for (int i = 0; i < max_i; i++) {
655 // Load local value
656 T val = accumulator[i][tid] + accumulator[i][tid + 32];
657
658 // Warp-level reduction (32 lanes) After this loop, lane 0 of warp 0
659 // holds the result All threads should execute this operation so
660 // __activemask() is incorrect here.
661
662 // The ifdef statement is needed because hip does not necessarily
663 // support the instruction either. Reverting back to __shfl_down is
664 // required.
665 //
666 for (int offset = 16; offset > 0; offset >>= 1) {
667#if defined(__CUDACC__)
668 val += __shfl_down_sync(0xffffffff, val, offset);
669#else
670 val += __shfl_down(val, offset);
671#endif
672 }
673
674 if (tid == 0)
675 accumulator[i][0] = val;
676 }
677#if defined(__CUDACC__)
678 __syncwarp();
679#endif
680 }
681
682#if !defined(__CUDACC__)
683 __syncthreads();
684#endif
685
686 if (tid < min(length - ico, lbatch)) {
687 const size_t coef_offset =
688 dev_.tasks[dev_.first_task + block_index()].coef_offset;
689 dev_.buffers_dev.coef[coef_offset + tid + ico] = accumulator[tid][0];
690 }
691 __syncthreads();
692 }
693}
694
695/*******************************************************************************
696 * \brief Launches the Cuda kernel that integrates all tasks of one grid level.
697 ******************************************************************************/
698void context_info::integrate_one_grid_level(const int level, int *lp_diff) {
699 if (number_of_tasks_per_level_[level] == 0)
700 return;
702
703 // Compute max angular momentum.
704 const ldiffs_value ldiffs =
706
707 smem_parameters smem_params(ldiffs, lmax());
708
709 *lp_diff = smem_params.lp_diff();
711
712 kernel_params params = set_kernel_parameters(level, smem_params);
713
714 /* WARNING : if you change the block size please be aware of that the number
715 * of warps is hardcoded when we do the block reduction in the integrate
716 * kernel. The number of warps should be explicitly indicated in the
717 * templating parameters or simply adjust the switch statements inside the
718 * integrate kernels */
719
720 const dim3 threads_per_block(4, 4, 4);
721 if (grid_[level].is_distributed()) {
722 if (grid_[level].is_orthogonal()) {
723 integrate_kernel<double, double3, true, true, 20>
724 <<<number_of_tasks_per_level_[level], threads_per_block, 0,
725 level_streams[level]>>>(params);
726 } else {
727 integrate_kernel<double, double3, true, false, 20>
728 <<<number_of_tasks_per_level_[level], threads_per_block, 0,
729 level_streams[level]>>>(params);
730 }
731 } else {
732 if (grid_[level].is_orthogonal()) {
733 integrate_kernel<double, double3, false, true, 20>
734 <<<number_of_tasks_per_level_[level], threads_per_block, 0,
735 level_streams[level]>>>(params);
736 } else {
737 integrate_kernel<double, double3, false, false, 20>
738 <<<number_of_tasks_per_level_[level], threads_per_block, 0,
739 level_streams[level]>>>(params);
740 }
741 }
742}
743
745
747
748 // Compute max angular momentum.
749 const ldiffs_value ldiffs =
751
752 smem_parameters smem_params(ldiffs, lmax());
754
755 kernel_params params = set_kernel_parameters(-1, smem_params);
756
757 /* WARNING if you change the block size. The number
758 * of warps is hardcoded when we do the block reduction in the integrate
759 * kernel. */
760
761 const dim3 threads_per_block(4, 4, 4);
762
763 if (!compute_tau && !calculate_forces) {
764 compute_hab<double, false>
765 <<<this->nblocks, threads_per_block, smem_params.smem_per_block(),
766 this->main_stream>>>(params);
767 return;
768 }
769
771 compute_hab<double, true>
772 <<<this->nblocks, threads_per_block, smem_params.smem_per_block(),
773 this->main_stream>>>(params);
774 }
775
777 compute_hab_forces<double, double3, false>
778 <<<this->nblocks, threads_per_block, smem_params.smem_per_block(),
779 this->main_stream>>>(params);
780 return;
781 }
782
784 compute_hab_forces<double, double3, true>
785 <<<this->nblocks, threads_per_block, smem_params.smem_per_block(),
786 this->main_stream>>>(params);
787 }
788}
789}; // namespace rocm_backend
std::vector< grid_info< double > > grid_
std::vector< offloadStream_t > level_streams
std::vector< int > number_of_tasks_per_level_
void integrate_one_grid_level(const int level, int *lp_diff)
Launches the Cuda kernel that integrates all tasks of one grid level.
static void const int const int i
__device__ __inline__ T block_reduce_64(T *table, const T val_, const int tid)
ldiffs_value process_get_ldiffs(bool calculate_forces, bool calculate_virial, bool compute_tau)
Returns difference in angular momentum range for given flags.
__inline__ __device__ unsigned int block_index()
__device__ __inline__ void fill_smem_task_coef(const kernel_params &dev, const int task_id, smem_task< T > &task)
Copies a task from global to shared memory and does precomputations.
__inline__ __device__ unsigned int thread_global_index()
static void init_constant_memory()
Initializes the device's constant memory.
__device__ static __inline__ void cxyz_to_cab(const smem_task< T > &task, const T *__restrict__ alpha, const T *__restrict__ cxyz, T *__restrict__ cab)
Transforms coefficients C_xyz into C_ab.
__constant__ orbital coset_inv[1330]
__inline__ __device__ int wrap_grid_index(const int idx, const int full_size)
Wraps a cube-local coordinate into the periodic full grid along one axis. Single point of entry share...
__inline__ __device__ void compute_alpha(const smem_task< T > &task, T *__restrict__ alpha)
Computes the polynomial expansion coefficients: (x-a)**lxa (x-b)**lxb -> sum_{ls} alpha(ls,...
__device__ __inline__ void fill_smem_task_reduced(const kernel_params &dev, const int task_id, smem_task_reduced< T, T3 > &task)
Copies a task from global to shared memory.
__inline__ __device__ double3 compute_coordinates(const double *__restrict__ dh_, const double x, const double y, const double z)
__global__ __launch_bounds__(64) void calculate_coefficients(const kernel_params dev_)
__device__ __inline__ T calculate_ymix_ymax_boundaries(smem_task_reduced< T, T3 > &task, const int z, int &ymin, int &ymax)
Parameters of the collocate kernel.
Differences in angular momentum.
data needed for collocate and integrate kernels
data needed for calculating the coefficients, forces, and stress