(git:98357aa)
Loading...
Searching...
No Matches
grid_gpu_internal_header.h
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 * Authors :
10 - Dr Mathieu Taillefumier (ETH Zurich / CSCS)
11 - Advanced Micro Devices, Inc.
12*/
13
14#ifndef GRID_GPU_INTERNAL_HEADER_H
15#define GRID_GPU_INTERNAL_HEADER_H
16
17#include <algorithm>
18#include <assert.h>
19#include <limits.h>
20#include <math.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24
25#define GRID_DEVICE __device__
26extern "C" {
27#include "../common/grid_basis_set.h"
28#include "../common/grid_constants.h"
29}
30
31#include "grid_gpu_context.h"
32
33namespace rocm_backend {
34
35#if defined(__OFFLOAD_CUDA) || defined(__HIP_PLATFORM_NVIDIA__)
36#if __CUDA_ARCH__ < 600
37__device__ __inline__ double atomicAdd(double *address, double val) {
38 unsigned long long int *address_as_ull = (unsigned long long int *)address;
39 unsigned long long int old = *address_as_ull, assumed;
40
41 do {
42 assumed = old;
43 old = atomicCAS(address_as_ull, assumed,
44 __double_as_longlong(val + __longlong_as_double(assumed)));
45
46 // Note: uses integer comparison to avoid hang in case of NaN (since NaN !=
47 // NaN)
48 } while (assumed != old);
49
50 return __longlong_as_double(old);
51}
52#endif
53#endif
54
55/*******************************************************************************
56 * \brief Orbital angular momentum.
57 ******************************************************************************/
58struct orbital {
59 int l[3];
60};
61
62__constant__ orbital coset_inv[1330];
63__constant__ int binomial_coef[19][19];
64
65/*******************************************************************************
66 * \brief Differences in angular momentum.
67 ******************************************************************************/
73};
74
75/*******************************************************************************
76 * \brief data needed for calculating the coefficients, forces, and stress
77 ******************************************************************************/
78template <typename T> struct smem_task {
80 T ra[3];
81 T rb[3];
82 T rp[3];
83 T rab[3];
89 char la_max;
90 char lb_max;
91 char la_min;
92 char lb_min;
93 char lp;
94 // size of the cab matrix
95 unsigned short int n1;
96 unsigned short int n2;
97 // size of entire spherical basis
98 unsigned short int nsgfa;
99 unsigned short int nsgfb;
100 // size of spherical set
101 unsigned short int nsgf_seta;
102 unsigned short int nsgf_setb;
103 // start of decontracted set, ie. pab and hab
106 // size of decontracted set, ie. pab and hab
109 // strides of the sphi transformation matrices
112 // pointers matrices
116 // integrate
120};
121
122/*******************************************************************************
123 * \brief data needed for collocate and integrate kernels
124 ******************************************************************************/
125template <typename T, typename T3> struct smem_task_reduced {
126 // radius: true cutoff used for the sphere-membership test.
127 // discrete_radius: box-aligned cutoff (always >= radius) used only for
128 // sizing the cube / trimming box boundaries.
135 char lp;
137};
138
139/*******************************************************************************
140 * \brief Factorial function, e.g. fac(5) = 5! = 120.
141 * \author Ole Schuett
142 ******************************************************************************/
143__device__ __inline__ double fac(const int i) {
144 static const double table[] = {
145 0.10000000000000000000E+01, 0.10000000000000000000E+01,
146 0.20000000000000000000E+01, 0.60000000000000000000E+01,
147 0.24000000000000000000E+02, 0.12000000000000000000E+03,
148 0.72000000000000000000E+03, 0.50400000000000000000E+04,
149 0.40320000000000000000E+05, 0.36288000000000000000E+06,
150 0.36288000000000000000E+07, 0.39916800000000000000E+08,
151 0.47900160000000000000E+09, 0.62270208000000000000E+10,
152 0.87178291200000000000E+11, 0.13076743680000000000E+13,
153 0.20922789888000000000E+14, 0.35568742809600000000E+15,
154 0.64023737057280000000E+16, 0.12164510040883200000E+18,
155 0.24329020081766400000E+19, 0.51090942171709440000E+20,
156 0.11240007277776076800E+22, 0.25852016738884976640E+23,
157 0.62044840173323943936E+24, 0.15511210043330985984E+26,
158 0.40329146112660563558E+27, 0.10888869450418352161E+29,
159 0.30488834461171386050E+30, 0.88417619937397019545E+31,
160 0.26525285981219105864E+33};
161 return table[i];
162}
163
164/*******************************************************************************
165 * \brief Number of Cartesian orbitals up to given angular momentum quantum.
166 * \author Ole Schuett
167 ******************************************************************************/
168__host__ __device__ __inline__ int ncoset(const int l) {
169 static const int table[] = {1, // l=0
170 4, // l=1
171 10, // l=2 ...
172 20, 35, 56, 84, 120, 165, 220, 286,
173 364, 455, 560, 680, 816, 969, 1140, 1330};
174 return table[l];
175}
176
177/*******************************************************************************
178 * \brief Maps three angular momentum components to a single zero based index.
179 ******************************************************************************/
180__host__ __device__ __inline__ int coset(int lx, int ly, int lz) {
181 const int l = lx + ly + lz;
182 if (l == 0) {
183 return 0;
184 } else {
185 return ncoset(l - 1) + ((l - lx) * (l - lx + 1)) / 2 + lz;
186 }
187}
188
189/*******************************************************************************
190 * \brief Increase i'th component of given orbital angular momentum.
191 ******************************************************************************/
192__device__ __inline__ orbital up(const int i, const orbital &a) {
193 orbital b = a;
194 b.l[i] += 1;
195 return b;
196}
197
198/*******************************************************************************
199 * \brief Decrease i'th component of given orbital angular momentum.
200 ******************************************************************************/
201__inline__ __device__ orbital down(const int i, const orbital &a) {
202 orbital b = a;
203 b.l[i] = max(0, a.l[i] - 1);
204 return b;
205}
206
207/*******************************************************************************
208 * \brief Return coset index of given orbital angular momentum.
209 ******************************************************************************/
210__inline__ __device__ int idx(const orbital a) {
211 return coset(a.l[0], a.l[1], a.l[2]);
212}
213
214__device__ __inline__ double power(const double x, const int expo) {
215 double tmp = 1.0;
216 for (int i = 1; i <= expo; i++)
217 tmp *= x;
218 return tmp;
219}
220
221/*******************************************************************************
222 * \brief Adds given value to matrix element cab[idx(b)][idx(a)].
223 ******************************************************************************/
224template <typename T = double>
225__device__ __inline__ void prep_term(const orbital a, const orbital b,
226 const T value, const int n, T *cab) {
227 atomicAdd(&cab[idx(b) * n + idx(a)], value);
228}
229
230/*******************************************************************************
231 * \brief Initializes the device's constant memory.
232 * \author Ole Schuett
233 ******************************************************************************/
234inline static void init_constant_memory() {
235 static bool initialized = false;
236 if (initialized) {
237 return; // constant memory has to be initialized only once
238 }
239
240 // Inverse coset mapping
241 orbital coset_inv_host[1330];
242 for (int lx = 0; lx <= 18; lx++) {
243 for (int ly = 0; ly <= 18 - lx; ly++) {
244 for (int lz = 0; lz <= 18 - lx - ly; lz++) {
245 const int i = coset(lx, ly, lz);
246 coset_inv_host[i] = {{lx, ly, lz}};
247 }
248 }
249 }
250
251 offloadMemcpyToSymbol(coset_inv, &coset_inv_host, sizeof(coset_inv_host));
252
253 // Binomial coefficient
254 int binomial_coef_host[19][19] = {
255 {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
256 {1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
257 {1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
258 {1, 3, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
259 {1, 4, 6, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
260 {1, 5, 10, 10, 5, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
261 {1, 6, 15, 20, 15, 6, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
262 {1, 7, 21, 35, 35, 21, 7, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
263 {1, 8, 28, 56, 70, 56, 28, 8, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
264 {1, 9, 36, 84, 126, 126, 84, 36, 9, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
265 {1, 10, 45, 120, 210, 252, 210, 120, 45, 10, 1, 0, 0, 0, 0, 0, 0, 0, 0},
266 {1, 11, 55, 165, 330, 462, 462, 330, 165, 55, 11, 1, 0, 0, 0, 0, 0, 0, 0},
267 {1, 12, 66, 220, 495, 792, 924, 792, 495, 220, 66, 12, 1, 0, 0, 0, 0, 0,
268 0},
269 {1, 13, 78, 286, 715, 1287, 1716, 1716, 1287, 715, 286, 78, 13, 1, 0, 0,
270 0, 0, 0},
271 {1, 14, 91, 364, 1001, 2002, 3003, 3432, 3003, 2002, 1001, 364, 91, 14, 1,
272 0, 0, 0, 0},
273 {1, 15, 105, 455, 1365, 3003, 5005, 6435, 6435, 5005, 3003, 1365, 455,
274 105, 15, 1, 0, 0, 0},
275 {1, 16, 120, 560, 1820, 4368, 8008, 11440, 12870, 11440, 8008, 4368, 1820,
276 560, 120, 16, 1, 0, 0},
277 {1, 17, 136, 680, 2380, 6188, 12376, 19448, 24310, 24310, 19448, 12376,
278 6188, 2380, 680, 136, 17, 1, 0},
279 {1, 18, 153, 816, 3060, 8568, 18564, 31824, 43758, 48620, 43758, 31824,
280 18564, 8568, 3060, 816, 153, 18, 1}};
281 offloadMemcpyToSymbol(binomial_coef, &binomial_coef_host[0][0],
282 sizeof(binomial_coef_host));
283 initialized = true;
284}
285
286// calculate the global index of a thread block
287__inline__ __device__ unsigned int block_index() {
288 return blockIdx.x + gridDim.x * (blockIdx.y + gridDim.y * blockIdx.z);
289}
290
291// Calculating the global index in the grid of any given device thread
292__inline__ __device__ unsigned int thread_global_index() {
293 return threadIdx.x + blockDim.x * (threadIdx.y + blockDim.y * threadIdx.z);
294}
295
296__inline__ __device__ double3
297compute_coordinates(const double *__restrict__ dh_, const double x,
298 const double y, const double z) {
299
300 double3 r3;
301 // I make no distinction between orthorhombic and non orthorhombic
302 // cases
303
304 r3.x = z * dh_[6] + y * dh_[3] + x * dh_[0];
305 r3.y = z * dh_[7] + y * dh_[4] + x * dh_[1];
306 r3.z = z * dh_[8] + y * dh_[5] + x * dh_[2];
307 return r3;
308}
309
310__inline__ __device__ float3 compute_coordinates(const float *__restrict__ dh_,
311 const float x, const float y,
312 const float z) {
313
314 float3 r3;
315 // I make no distinction between orthorhombic and non orthorhombic
316 // cases
317
318 r3.x = z * dh_[6] + y * dh_[3] + x * dh_[0];
319 r3.y = z * dh_[7] + y * dh_[4] + x * dh_[1];
320 r3.z = z * dh_[8] + y * dh_[5] + x * dh_[2];
321 return r3;
322}
323
324/*******************************************************************************
325 * \brief Computes the polynomial expansion coefficients:
326 * (x-a)**lxa (x-b)**lxb -> sum_{ls} alpha(ls,lxa,lxb,1)*(x-p)**ls
327 ******************************************************************************/
328template <typename T>
329__inline__ __device__ void compute_alpha(const smem_task<T> &task,
330 T *__restrict__ alpha) {
331 // strides for accessing alpha
332 const int s3 = (task.lp + 1);
333 const int s2 = (task.la_max + 1) * s3;
334 const int s1 = (task.lb_max + 1) * s2;
335 const int tid = thread_global_index();
336
337 for (int i = tid; i < 3 * s1; i += blockDim.x * blockDim.y * blockDim.z)
338 alpha[i] = 0.0;
339
340 __syncthreads();
341
342 for (int idir = threadIdx.z; idir < 3; idir += blockDim.z) {
343 const T drpa = task.rp[idir] - task.ra[idir];
344 const T drpb = task.rp[idir] - task.rb[idir];
345 for (int la = threadIdx.y; la <= task.la_max; la += blockDim.y) {
346 for (int lb = threadIdx.x; lb <= task.lb_max; lb += blockDim.x) {
347 T a = 1.0;
348 for (int k = 0; k <= la; k++) {
349 T b = 1.0;
350 const int base = idir * s1 + lb * s2 + la * s3;
351 for (int l = 0; l <= lb; l++) {
352 alpha[base + la - l + lb - k] +=
353 a * b * binomial_coef[la][k] * binomial_coef[lb][l];
354 b *= drpb;
355 }
356 a *= drpa;
357 }
358 }
359 }
360 }
361}
362
363__host__ __inline__ __device__ void
364convert_to_lattice_coordinates(const double *dh_inv_,
365 const double3 *__restrict__ const rp,
366 double3 *__restrict__ rp_c) {
367 rp_c->x = dh_inv_[0] * rp->x + dh_inv_[3] * rp->y + dh_inv_[6] * rp->z;
368 rp_c->y = dh_inv_[1] * rp->x + dh_inv_[4] * rp->y + dh_inv_[7] * rp->z;
369 rp_c->z = dh_inv_[2] * rp->x + dh_inv_[5] * rp->y + dh_inv_[8] * rp->z;
370}
371
372__host__ __inline__ __device__ void
374 const double *__restrict__ dh_, const double3 *__restrict__ const rp,
375 double3 *__restrict__ rp_c) {
376 rp_c->x = dh_[0] * rp->x + dh_[3] * rp->y + dh_[6] * rp->z;
377 rp_c->y = dh_[1] * rp->x + dh_[4] * rp->y + dh_[7] * rp->z;
378 rp_c->z = dh_[2] * rp->x + dh_[5] * rp->y + dh_[8] * rp->z;
379}
380
381__host__ __inline__ __device__ void
383 const float3 *__restrict__ const rp,
384 float3 *__restrict__ rp_c) {
385 rp_c->x = dh_inv_[0] * rp->x + dh_inv_[3] * rp->y + dh_inv_[6] * rp->z;
386 rp_c->y = dh_inv_[1] * rp->x + dh_inv_[4] * rp->y + dh_inv_[7] * rp->z;
387 rp_c->z = dh_inv_[2] * rp->x + dh_inv_[5] * rp->y + dh_inv_[8] * rp->z;
388}
389
390__host__ __inline__ __device__ void
392 const float *__restrict__ dh_, const float3 *__restrict__ const rp,
393 float3 *__restrict__ rp_c) {
394 rp_c->x = dh_[0] * rp->x + dh_[3] * rp->y + dh_[6] * rp->z;
395 rp_c->y = dh_[1] * rp->x + dh_[4] * rp->y + dh_[7] * rp->z;
396 rp_c->z = dh_[2] * rp->x + dh_[5] * rp->y + dh_[8] * rp->z;
397}
398
399template <typename T, typename T3, bool orthorhombic_>
401 const T radius, const T *const __restrict__ dh_,
402 const T *const __restrict__ dh_inv_, const T3 *__restrict__ rp,
403 T3 *__restrict__ roffset, int3 *__restrict__ cubecenter,
404 int3 *__restrict__ lb_cube, int3 *__restrict__ cube_size) {
405
406 /* center of the gaussian in the lattice coordinates */
407 T3 rp1, rp2, rp3;
408
409 /* it is in the lattice vector frame */
410 convert_to_lattice_coordinates(dh_inv_, rp, &rp1);
411
412 /* compute the grid point that is the closest to the sphere center. */
413 cubecenter->x = std::floor(rp1.x);
414 cubecenter->y = std::floor(rp1.y);
415 cubecenter->z = std::floor(rp1.z);
416
417 /* seting up the cube parameters */
418
419 if (orthorhombic_) {
420
421 // the cube is actually slightly bigger than the sphere of radius r. that's
422 // why we need to discretize it to get the cube size "right".
423
424 // disc_radius >= radius always. somehow despite the fact that we compile
425 // things with a c++ compiler on the host side, we need to use fmin instead
426 // of std::min since std::min is not allowed on the device side
427
428 // We assume no specific form for the orthogonal matrix. (can be diaognal or
429 // completely full, the only constraint is that the tree vectors are
430 // orthogonal)
431
432 T norm1, norm2, norm3;
433 norm1 = dh_[0] * dh_[0] + dh_[1] * dh_[1] + dh_[2] * dh_[2];
434 norm2 = dh_[3] * dh_[3] + dh_[4] * dh_[4] + dh_[5] * dh_[5];
435 norm3 = dh_[6] * dh_[6] + dh_[7] * dh_[7] + dh_[8] * dh_[8];
436
437 norm1 = std::sqrt(norm1);
438 norm2 = std::sqrt(norm2);
439 norm3 = std::sqrt(norm3);
440
441 const T disr_radius =
442 std::min(norm1, std::min(norm2, norm3)) *
443 (std::max(1,
444 (int)ceil(radius / std::min(norm1, std::min(norm2, norm3)))));
445
446 rp2.x = cubecenter->x;
447 rp2.y = cubecenter->y;
448 rp2.z = cubecenter->z;
449
450 /* convert the cube center from grid points coordinates to cartesian */
452 /* cube center */
453 roffset->x -= rp->x;
454 roffset->y -= rp->y;
455 roffset->z -= rp->z;
456
457 /* This potentially makes the cube bigger than necessary because disc_radius
458 is derived with the smallest lattice parameter. It is not an issue when
459 the lattice is cubic but could lead to more calculations than necessary
460 when the lattice is orthorhombic */
461 rp2.x = disr_radius;
462 rp2.y = disr_radius;
463 rp2.z = disr_radius;
464
465 /* it is in the lattice vector frame */
466 convert_to_lattice_coordinates(dh_inv_, &rp2, &rp3);
467 /* lower and upper bounds */
468 lb_cube->x = std::ceil(-1e-8 - rp3.x);
469 lb_cube->y = std::ceil(-1e-8 - rp3.y);
470 lb_cube->z = std::ceil(-1e-8 - rp3.z);
471
472 /* it is in the lattice vector frame */
473 convert_to_lattice_coordinates(dh_inv_, roffset, &rp2);
474
475 /* Express the offset in lattice coordinates */
476 roffset->x = rp2.x;
477 roffset->y = rp2.y;
478 roffset->z = rp2.z;
479
480 /* compute the cube size ignoring periodicity */
481 /* the interval is not symmetrical for some curious reasons. it should go
482 * from [-L..L+1] so the number of points is multiple of two */
483 cube_size->x = 2 - 2 * lb_cube->x;
484 cube_size->y = 2 - 2 * lb_cube->y;
485 cube_size->z = 2 - 2 * lb_cube->z;
486 return disr_radius;
487 } else {
488 int3 ub_cube;
489
490 lb_cube->x = INT_MAX;
491 ub_cube.x = INT_MIN;
492 lb_cube->y = INT_MAX;
493 ub_cube.y = INT_MIN;
494 lb_cube->z = INT_MAX;
495 ub_cube.z = INT_MIN;
496
497 for (int i = -1; i <= 1; i++) {
498 for (int j = -1; j <= 1; j++) {
499 for (int k = -1; k <= 1; k++) {
500 T3 r;
501 r.x = rp->x + ((T)i) * radius;
502 r.y = rp->y + ((T)j) * radius;
503 r.z = rp->z + ((T)k) * radius;
504 convert_to_lattice_coordinates(dh_inv_, &r, roffset);
505
506 lb_cube->x = std::min(lb_cube->x, (int)std::floor(roffset->x));
507 ub_cube.x = std::max(ub_cube.x, (int)std::ceil(roffset->x));
508
509 lb_cube->y = std::min(lb_cube->y, (int)std::floor(roffset->y));
510 ub_cube.y = std::max(ub_cube.y, (int)std::ceil(roffset->y));
511
512 lb_cube->z = std::min(lb_cube->z, (int)std::floor(roffset->z));
513 ub_cube.z = std::max(ub_cube.z, (int)std::ceil(roffset->z));
514 }
515 }
516 }
517 /* compute the cube size ignoring periodicity */
518 cube_size->x = ub_cube.x - lb_cube->x;
519 cube_size->y = ub_cube.y - lb_cube->y;
520 cube_size->z = ub_cube.z - lb_cube->z;
521
522 /* compute the offset in lattice coordinates */
523
524 roffset->x = cubecenter->x - rp1.x;
525 roffset->y = cubecenter->y - rp1.y;
526 roffset->z = cubecenter->z - rp1.z;
527
528 // shift the boundaries compared to the cube center so that the
529 // specialization ortho / non ortho is minimal
530 lb_cube->x -= cubecenter->x;
531 lb_cube->y -= cubecenter->y;
532 lb_cube->z -= cubecenter->z;
533
534 return radius;
535 }
536}
537
538__inline__ __device__ void compute_window_size(const int3 grid_size,
539 const int border_mask,
540 const int3 &border_width,
541 int3 &window_size,
542 int3 &window_shift) {
543 window_shift.x = 0;
544 window_shift.y = 0;
545 window_shift.z = 0;
546
547 window_size.x = grid_size.x - 1;
548 window_size.y = grid_size.y - 1;
549 window_size.z = grid_size.z - 1;
550
551 if (border_mask & (1 << 0))
552 window_shift.x += border_width.x;
553 if (border_mask & (1 << 1))
554 window_size.x -= border_width.x;
555 if (border_mask & (1 << 2))
556 window_shift.y += border_width.y;
557 if (border_mask & (1 << 3))
558 window_size.y -= border_width.y;
559 if (border_mask & (1 << 4))
560 window_shift.z += border_width.z;
561 if (border_mask & (1 << 5))
562 window_size.z -= border_width.z;
563}
564
565/*******************************************************************************
566 * \brief Wraps a cube-local coordinate into the periodic full grid along one
567 * axis. Single point of entry shared by collocate_kernel and
568 * integrate_kernel.
569 ******************************************************************************/
570__inline__ __device__ int wrap_grid_index(const int idx, const int full_size) {
571 int idx2 = idx % full_size;
572 if (idx2 < 0)
573 idx2 += full_size;
574 return idx2;
575}
576
577/*******************************************************************************
578 * \brief Shifts task.cube_center into the local grid's coordinate system and,
579 * for distributed grids, derives the border window. Single point of
580 * entry shared by collocate_kernel and integrate_kernel so the two
581 * cannot drift apart on which axis maps to which.
582 ******************************************************************************/
583template <typename T, typename T3, bool distributed__>
584__device__ __inline__ void
587 task.cube_center.x += task.lb_cube.x - dev_.grid_lower_corner_.x;
588 task.cube_center.y += task.lb_cube.y - dev_.grid_lower_corner_.y;
589 task.cube_center.z += task.lb_cube.z - dev_.grid_lower_corner_.z;
590
591 if (distributed__) {
592 if (task.apply_border_mask) {
594 dev_.grid_local_size_,
595 dev_.tasks[dev_.first_task + block_index()].border_mask,
597 }
598 }
599}
600
601/*******************************************************************************
602 * \brief Transforms coefficients C_ab into C_xyz.
603 ******************************************************************************/
604template <typename T>
605__device__ __inline__ static void
606cab_to_cxyz(const smem_task<T> &task, const T *__restrict__ alpha,
607 const T *__restrict__ cab, T *__restrict__ cxyz) {
608
609 // *** initialise the coefficient matrix, we transform the sum
610 //
611 // sum_{lxa,lya,lza,lxb,lyb,lzb} P_{lxa,lya,lza,lxb,lyb,lzb} *
612 // (x-a_x)**lxa (y-a_y)**lya (z-a_z)**lza (x-b_x)**lxb (y-a_y)**lya
613 // (z-a_z)**lza
614 //
615 // into
616 //
617 // sum_{lxp,lyp,lzp} P_{lxp,lyp,lzp} (x-p_x)**lxp (y-p_y)**lyp (z-p_z)**lzp
618 //
619 // where p is center of the product gaussian, and lp = la_max + lb_max
620 // (current implementation is l**7)
621
622 // strides for accessing alpha
623 const int s3 = (task.lp + 1);
624 const int s2 = (task.la_max + 1) * s3;
625 const int s1 = (task.lb_max + 1) * s2;
626
627 // TODO: Maybe we can transpose alpha to index it directly with ico and jco.
628 const int tid =
629 threadIdx.x + blockDim.x * (threadIdx.y + blockDim.y * threadIdx.z);
630
631 for (int i = tid; i < ncoset(task.lp);
632 i += blockDim.x * blockDim.y * blockDim.z) {
633 auto &co = coset_inv[i];
634 T reg = 0.0; // accumulate into a register
635 for (int jco = 0; jco < ncoset(task.lb_max); jco++) {
636 const auto &b = coset_inv[jco];
637 for (int ico = 0; ico < ncoset(task.la_max); ico++) {
638 const auto &a = coset_inv[ico];
639 const T p = alpha[0 * s1 + b.l[0] * s2 + a.l[0] * s3 + co.l[0]] *
640 alpha[1 * s1 + b.l[1] * s2 + a.l[1] * s3 + co.l[1]] *
641 alpha[2 * s1 + b.l[2] * s2 + a.l[2] * s3 + co.l[2]];
642 reg += p * cab[jco * task.n1 + ico]; // collocate
643 }
644 }
645
646 cxyz[i] = task.prefactor * reg;
647 }
648 __syncthreads(); // because of concurrent writes to cxyz / cab
649}
650
651/*******************************************************************************
652 * \brief Transforms coefficients C_xyz into C_ab.
653 ******************************************************************************/
654template <typename T>
655__device__ __inline__ static void
656cxyz_to_cab(const smem_task<T> &task, const T *__restrict__ alpha,
657 const T *__restrict__ cxyz, T *__restrict__ cab) {
658
659 // *** initialise the coefficient matrix, we transform the sum
660 //
661 // sum_{lxa,lya,lza,lxb,lyb,lzb} P_{lxa,lya,lza,lxb,lyb,lzb} *
662 // (x-a_x)**lxa (y-a_y)**lya (z-a_z)**lza (x-b_x)**lxb (y-a_y)**lya
663 // (z-a_z)**lza
664 //
665 // into
666 //
667 // sum_{lxp,lyp,lzp} P_{lxp,lyp,lzp} (x-p_x)**lxp (y-p_y)**lyp (z-p_z)**lzp
668 //
669 // where p is center of the product gaussian, and lp = la_max + lb_max
670 // (current implementation is l**7)
671
672 // strides for accessing alpha
673 const int s3 = (task.lp + 1);
674 const int s2 = (task.la_max + 1) * s3;
675 const int s1 = (task.lb_max + 1) * s2;
676
677 const int tid =
678 threadIdx.x + blockDim.x * (threadIdx.y + blockDim.y * threadIdx.z);
679 for (int jco = tid / 8; jco < ncoset(task.lb_max); jco += 8) {
680 const orbital b = coset_inv[jco];
681 for (int ico = tid % 8; ico < ncoset(task.la_max); ico += 8) {
682 const auto &a = coset_inv[ico];
683 T reg = 0.0; // accumulate into a register
684 for (int ic = 0; ic < ncoset(task.lp); ic++) {
685 const auto &co = coset_inv[ic];
686 const T p = alpha[b.l[0] * s2 + a.l[0] * s3 + co.l[0]] *
687 alpha[s1 + b.l[1] * s2 + a.l[1] * s3 + co.l[1]] *
688 alpha[2 * s1 + b.l[2] * s2 + a.l[2] * s3 + co.l[2]];
689
690 reg += p * cxyz[ic]; // integrate
691 }
692 cab[jco * task.n1 + ico] =
693 task.prefactor * reg; // partial loop coverage -> zero it
694 }
695 }
696}
697
698/*******************************************************************************
699 * \brief Copies a task from global to shared memory.
700 ******************************************************************************/
701
702/* Collocate and integrate do not care about the sphi etc... computing the
703 * coefficients do not care about the exponentials (the parameters are still
704 * needed), so simplify the amount of information to save shared memory (it is
705 * crucial on AMD GPU to max out occupancy) */
706template <typename T, typename T3>
707__device__ __inline__ void
708fill_smem_task_reduced(const kernel_params &dev, const int task_id,
710 if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0) {
711 const auto &glb_task = dev.tasks[task_id];
712 task.zetp = glb_task.zeta + glb_task.zetb;
713 task.radius = glb_task.radius;
714 task.discrete_radius = glb_task.discrete_radius;
715
716 // angular momentum range for the actual collocate/integrate operation.
717 task.lp =
718 glb_task.la_max + dev.la_max_diff + glb_task.lb_max + dev.lb_max_diff;
719
720 task.cube_size.x = glb_task.cube_size.x;
721 task.cube_size.y = glb_task.cube_size.y;
722 task.cube_size.z = glb_task.cube_size.z;
723
724 task.cube_center.x = glb_task.cube_center.x;
725 task.cube_center.y = glb_task.cube_center.y;
726 task.cube_center.z = glb_task.cube_center.z;
727
728 task.roffset.x = glb_task.roffset.x;
729 task.roffset.y = glb_task.roffset.y;
730 task.roffset.z = glb_task.roffset.z;
731
732 task.lb_cube.x = glb_task.lb_cube.x;
733 task.lb_cube.y = glb_task.lb_cube.y;
734 task.lb_cube.z = glb_task.lb_cube.z;
735
736 task.norm_lattice_vector_z_2 = dev.dh_[6] * dev.dh_[6] +
737 dev.dh_[7] * dev.dh_[7] +
738 dev.dh_[8] * dev.dh_[8];
739 task.norm_lattice_vector_y_2 = dev.dh_[3] * dev.dh_[3] +
740 dev.dh_[4] * dev.dh_[4] +
741 dev.dh_[5] * dev.dh_[5];
742
744 sqrt(dev.dh_inv_[3] * dev.dh_inv_[3] + dev.dh_inv_[4] * dev.dh_inv_[4] +
745 dev.dh_inv_[5] * dev.dh_inv_[5]);
747 sqrt(dev.dh_inv_[0] * dev.dh_inv_[0] + dev.dh_inv_[1] * dev.dh_inv_[1] +
748 dev.dh_inv_[2] * dev.dh_inv_[2]);
749
750 task.apply_border_mask = glb_task.apply_border_mask;
751 }
752 __syncthreads();
753}
754
755/*******************************************************************************
756 * \brief Copies a task from global to shared memory and does precomputations.
757 ******************************************************************************/
758
759/* computing the coefficients do not care about many of the exponentials
760 * parameters, etc.. so */
761template <typename T>
762__device__ __inline__ void fill_smem_task_coef(const kernel_params &dev,
763 const int task_id,
764 smem_task<T> &task) {
765 if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0) {
766 const auto &glb_task = dev.tasks[task_id];
767 const int iatom = glb_task.iatom;
768 const int jatom = glb_task.jatom;
769 task.zeta = glb_task.zeta;
770 task.zetb = glb_task.zetb;
771
772 for (int i = 0; i < 3; i++) {
773 task.rab[i] = glb_task.rab[i];
774 task.ra[i] = glb_task.ra[i];
775 task.rb[i] = task.ra[i] + task.rab[i];
776 task.rp[i] =
777 task.ra[i] + task.rab[i] * task.zetb / (task.zeta + task.zetb);
778 }
779
780 task.prefactor = glb_task.prefactor;
781 task.off_diag_twice = glb_task.off_diag_twice;
782
783 // angular momentum range of basis set
784 const int la_max_basis = glb_task.la_max;
785 const int lb_max_basis = glb_task.lb_max;
786 const int la_min_basis = glb_task.la_min;
787 const int lb_min_basis = glb_task.lb_min;
788
789 // angular momentum range for the actual collocate/integrate opteration.
790 task.la_max = la_max_basis + dev.la_max_diff;
791 task.lb_max = lb_max_basis + dev.lb_max_diff;
792 task.la_min = max(la_min_basis + (int)dev.la_min_diff, 0);
793 task.lb_min = max(lb_min_basis + (int)dev.lb_min_diff, 0);
794 task.lp = task.la_max + task.lb_max;
795
796 // start of decontracted set, ie. pab and hab
797 task.first_coseta = (la_min_basis > 0) ? ncoset(la_min_basis - 1) : 0;
798 task.first_cosetb = (lb_min_basis > 0) ? ncoset(lb_min_basis - 1) : 0;
799
800 // size of decontracted set, ie. pab and hab
801 task.ncoseta = ncoset(la_max_basis);
802 task.ncosetb = ncoset(lb_max_basis);
803
804 // size of the cab matrix
805 task.n1 = ncoset(task.la_max);
806 task.n2 = ncoset(task.lb_max);
807
808 // size of entire spherical basis
809 task.nsgfa = glb_task.nsgfa; // ibasis.nsgf;
810 task.nsgfb = glb_task.nsgfb;
811
812 // size of spherical set
813 task.nsgf_seta = glb_task.nsgf_seta;
814 task.nsgf_setb = glb_task.nsgf_setb;
815
816 // strides of the sphi transformation matrices
817 task.maxcoa = glb_task.maxcoa;
818 task.maxcob = glb_task.maxcob;
819
820 // transformations from contracted spherical to primitive cartesian basis
821 task.sphia = &dev.sphi_dev[glb_task.ikind][glb_task.sgfa * task.maxcoa +
822 glb_task.ipgf * task.ncoseta];
823 task.sphib = &dev.sphi_dev[glb_task.jkind][glb_task.sgfb * task.maxcob +
824 glb_task.jpgf * task.ncosetb];
825
826 // Locate current matrix block within the buffer.
827 const int block_offset = dev.block_offsets[glb_task.block_num];
828 task.block_transposed = glb_task.block_transposed;
829 task.pab_block =
830 dev.buffers_dev.pab_block + block_offset + glb_task.subblock_offset;
831
832 if (dev.buffers_dev.hab_block != nullptr) {
833 task.hab_block =
834 dev.buffers_dev.hab_block + block_offset + glb_task.subblock_offset;
835 if (dev.buffers_dev.forces != nullptr) {
836 task.forces_a = &dev.buffers_dev.forces[3 * iatom];
837 task.forces_b = &dev.buffers_dev.forces[3 * jatom];
838 }
839 }
840 }
841 __syncthreads();
842}
843
845private:
846 int la_max_{-1};
847 int lb_max_{-1};
848 int smem_per_block_{0};
849 int alpha_size_{-1};
850 int cab_size_{-1};
851 int lp_max_{-1};
852 ldiffs_value ldiffs_;
853 int lp_diff_{-1};
854
855public:
856 smem_parameters(const ldiffs_value ldiffs, const int lmax) {
857 ldiffs_ = ldiffs;
859 la_max_ = lmax + ldiffs.la_max_diff;
860 lb_max_ = lmax + ldiffs.lb_max_diff;
861 lp_max_ = la_max_ + lb_max_;
862
863 // NB: cab is allocated in global memory not shared memory. Each block has
864 // its own cab space
865
866 cab_size_ = (rocm_backend::ncoset(la_max_) * rocm_backend::ncoset(lb_max_));
867 alpha_size_ = 3 * (lb_max_ + 1) * (la_max_ + 1) * (lp_max_ + 1);
868 smem_per_block_ = std::max(alpha_size_, 64) * sizeof(double);
869
870 if (smem_per_block_ > 64 * 1024) {
871 fprintf(stderr,
872 "ERROR: Not enough shared memory in grid_gpu_collocate.\n");
873 fprintf(stderr, "alpha_len: %i, ", alpha_size_);
874 fprintf(stderr, "total smem_per_block: %f kB\n\n",
875 smem_per_block_ / 1024.0);
876 abort();
877 }
878 }
879
881
882 // copy and move are trivial
883
884 inline int alpha_size() const { return alpha_size_; }
885
886 inline int cab_size() const { return cab_size_; }
887
888 inline int smem_per_block() const { return smem_per_block_; }
889
890 inline int lp_diff() const { return lp_diff_; }
891
892 inline ldiffs_value ldiffs() const { return ldiffs_; }
893
894 inline int lp_max() const { return lp_max_; }
895
896 inline int cxyz_size() const { return ncoset(lp_max_); }
897};
898template <typename T>
899__inline__ __device__ T *allocate_workspace(const kernel_params &dev_) {
900 unsigned int offset = dev_.cab_block_offset_dev[block_index()];
901 return (T *)(dev_.buffers_dev.cab + offset);
902}
903
904template <typename T, typename T3>
905__device__ __inline__ T
907 int &ymin, int &ymax) {
908 T kremain = 0.0;
909 ymin = (2 * (z + task.lb_cube.z) - 1) / 2;
910 ymin *= ymin;
911 kremain = task.discrete_radius * task.discrete_radius -
912 ((T)ymin) * task.norm_lattice_vector_z_2;
913 ymin = ceil(-1.0e-8 -
914 sqrt(fmax(0.0, kremain)) * task.norm_inverse_lattice_vector_y);
915 ymax = 1 - ymin - task.lb_cube.y;
916 ymin = ymin - task.lb_cube.y;
917 return kremain;
918}
919
920template <typename T, typename T3>
921__device__ __inline__ void
923 const T kremain, int &xmin, int &xmax) {
924 xmin = (2 * (y + task.lb_cube.y) - 1) / 2;
925 xmin *= xmin;
926 xmin = ceil(-1.0e-8 -
927 sqrt(fmax(0.0, kremain - xmin * task.norm_lattice_vector_y_2)) *
929 xmax = 1 - xmin - task.lb_cube.x;
930 xmin -= task.lb_cube.x;
931}
932
933} // namespace rocm_backend
934#endif
smem_parameters(const ldiffs_value ldiffs, const int lmax)
static void const int const int i
#define idx2(a, i, j)
__host__ __inline__ __device__ void convert_from_lattice_coordinates_to_cartesian(const double *__restrict__ dh_, const double3 *__restrict__ const rp, double3 *__restrict__ rp_c)
__device__ static __inline__ void cab_to_cxyz(const smem_task< T > &task, const T *__restrict__ alpha, const T *__restrict__ cab, T *__restrict__ cxyz)
Transforms coefficients C_ab into C_xyz.
__device__ __inline__ void calculate_xmin_xmax_boundaries(smem_task_reduced< T, T3 > &task, const int y, const T kremain, int &xmin, int &xmax)
__host__ __device__ __inline__ int ncoset(const int l)
Number of Cartesian orbitals up to given angular momentum quantum.
__inline__ __device__ unsigned int block_index()
__device__ __inline__ void prep_term(const orbital a, const orbital b, const T value, const int n, T *cab)
Adds given value to matrix element cab[idx(b)][idx(a)].
__inline__ __device__ void compute_window_size(const int3 grid_size, const int border_mask, const int3 &border_width, int3 &window_size, int3 &window_shift)
__device__ __inline__ void setup_task_cube_center(const kernel_params &dev_, smem_task_reduced< T, T3 > &task)
Shifts task.cube_center into the local grid's coordinate system and, for distributed grids,...
__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.
__device__ __inline__ double power(const double x, const int expo)
__inline__ __device__ orbital down(const int i, const orbital &a)
Decrease i'th component of given orbital angular momentum.
__inline__ __device__ T * allocate_workspace(const kernel_params &dev_)
__inline__ __device__ unsigned int thread_global_index()
__host__ __inline__ __device__ void convert_to_lattice_coordinates(const double *dh_inv_, const double3 *__restrict__ const rp, double3 *__restrict__ rp_c)
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.
__inline__ __device__ int idx(const orbital a)
Return coset index of given orbital angular momentum.
__constant__ orbital coset_inv[1330]
__device__ __inline__ orbital up(const int i, const orbital &a)
Increase i'th component of given orbital angular momentum.
__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)
__constant__ int binomial_coef[19][19]
__inline__ T compute_cube_properties(const T radius, const T *const __restrict__ dh_, const T *const __restrict__ dh_inv_, const T3 *__restrict__ rp, T3 *__restrict__ roffset, int3 *__restrict__ cubecenter, int3 *__restrict__ lb_cube, int3 *__restrict__ cube_size)
__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.
Orbital angular momentum.
data needed for collocate and integrate kernels
data needed for calculating the coefficients, forces, and stress