(git:3f0d10b)
Loading...
Searching...
No Matches
grid_cpu_collint.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#include <assert.h>
9#include <limits.h>
10#include <math.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14
15#if defined(__AVX2__) && defined(__FMA__)
16#include <immintrin.h>
17#endif
18
22
23#define GRID_MAX_LP_OPTIMIZED 9
24
25#if (GRID_DO_COLLOCATE)
26#define GRID_CONST_WHEN_COLLOCATE const
27#define GRID_CONST_WHEN_INTEGRATE
28#else
29#define GRID_CONST_WHEN_COLLOCATE
30#define GRID_CONST_WHEN_INTEGRATE const
31#endif
32
33/*******************************************************************************
34 * \brief Simple loop body for ortho_cx_to_grid using plain C.
35 * \author Ole Schuett
36 ******************************************************************************/
37static inline void __attribute__((always_inline))
38ortho_cx_to_grid_scalar(const int lp, const int cmax, const int i,
39 const double pol[3][lp + 1][2 * cmax + 1],
45
46#if (GRID_DO_COLLOCATE)
47 // collocate
48 double reg[4] = {0.0, 0.0, 0.0, 0.0};
49#pragma omp simd reduction(+ : reg)
50 for (int lxp = 0; lxp <= lp; lxp++) {
51 const double p = pol[0][lxp][i + cmax];
52 reg[0] += cx[lxp * 4 + 0] * p;
53 reg[1] += cx[lxp * 4 + 1] * p;
54 reg[2] += cx[lxp * 4 + 2] * p;
55 reg[3] += cx[lxp * 4 + 3] * p;
56 }
57 *grid_0 += reg[0];
58 *grid_1 += reg[1];
59 *grid_2 += reg[2];
60 *grid_3 += reg[3];
61
62#else
63 // integrate
64 const double reg[4] = {*grid_0, *grid_1, *grid_2, *grid_3};
65#pragma omp simd
66 for (int lxp = 0; lxp <= lp; lxp++) {
67 const double p = pol[0][lxp][i + cmax];
68 cx[lxp * 4 + 0] += reg[0] * p;
69 cx[lxp * 4 + 1] += reg[1] * p;
70 cx[lxp * 4 + 2] += reg[2] * p;
71 cx[lxp * 4 + 3] += reg[3] * p;
72 }
73#endif
74}
75
76/*******************************************************************************
77 * \brief Optimized loop body for ortho_cx_to_grid using AVX2 Intel Intrinsics.
78 * This routine always processes four consecutive grid elements at once.
79 * \author Ole Schuett
80 ******************************************************************************/
81#if defined(__AVX2__) && defined(__FMA__)
82static inline void __attribute__((always_inline))
83ortho_cx_to_grid_avx2(const int lp, const int cmax, const int i,
84 const double pol[3][lp + 1][2 * cmax + 1],
90
91 const int icmax = i + cmax;
92
93#if (GRID_DO_COLLOCATE)
94 // collocate
95 // First iteration for lxp == 0 does not need add instructions.
96 __m256d p_vec = _mm256_loadu_pd(&pol[0][0][icmax]);
97 __m256d r_vec_0 = _mm256_mul_pd(p_vec, _mm256_set1_pd(cx[0]));
98 __m256d r_vec_1 = _mm256_mul_pd(p_vec, _mm256_set1_pd(cx[1]));
99 __m256d r_vec_2 = _mm256_mul_pd(p_vec, _mm256_set1_pd(cx[2]));
100 __m256d r_vec_3 = _mm256_mul_pd(p_vec, _mm256_set1_pd(cx[3]));
101
102 // Remaining iterations for lxp > 0 use fused multiply adds.
104 for (int lxp = 1; lxp <= lp; lxp++) {
105 const double *cx_base = &cx[lxp * 4];
106 p_vec = _mm256_loadu_pd(&pol[0][lxp][icmax]);
107 r_vec_0 = _mm256_fmadd_pd(p_vec, _mm256_set1_pd(cx_base[0]), r_vec_0);
108 r_vec_1 = _mm256_fmadd_pd(p_vec, _mm256_set1_pd(cx_base[1]), r_vec_1);
109 r_vec_2 = _mm256_fmadd_pd(p_vec, _mm256_set1_pd(cx_base[2]), r_vec_2);
110 r_vec_3 = _mm256_fmadd_pd(p_vec, _mm256_set1_pd(cx_base[3]), r_vec_3);
111 }
112
113 // Add vectors to grid one at a time, because they can aliase when cube wraps.
114 _mm256_storeu_pd(grid_0, _mm256_add_pd(_mm256_loadu_pd(grid_0), r_vec_0));
115 _mm256_storeu_pd(grid_1, _mm256_add_pd(_mm256_loadu_pd(grid_1), r_vec_1));
116 _mm256_storeu_pd(grid_2, _mm256_add_pd(_mm256_loadu_pd(grid_2), r_vec_2));
117 _mm256_storeu_pd(grid_3, _mm256_add_pd(_mm256_loadu_pd(grid_3), r_vec_3));
118
119#else
120 // integrate
121 __m256d grid_vec_0 = _mm256_loadu_pd(grid_0);
122 __m256d grid_vec_1 = _mm256_loadu_pd(grid_1);
123 __m256d grid_vec_2 = _mm256_loadu_pd(grid_2);
124 __m256d grid_vec_3 = _mm256_loadu_pd(grid_3);
125
127 for (int lxp = 0; lxp <= lp; lxp++) {
128 __m256d p_vec = _mm256_loadu_pd(&pol[0][lxp][icmax]);
129
130 // Do 4 dot products at once. https://stackoverflow.com/a/10454420
131 __m256d xy0 = _mm256_mul_pd(p_vec, grid_vec_0);
132 __m256d xy1 = _mm256_mul_pd(p_vec, grid_vec_1);
133 __m256d xy2 = _mm256_mul_pd(p_vec, grid_vec_2);
134 __m256d xy3 = _mm256_mul_pd(p_vec, grid_vec_3);
135
136 // low to high: xy00+xy01 xy10+xy11 xy02+xy03 xy12+xy13
137 __m256d temp01 = _mm256_hadd_pd(xy0, xy1);
138
139 // low to high: xy20+xy21 xy30+xy31 xy22+xy23 xy32+xy33
140 __m256d temp23 = _mm256_hadd_pd(xy2, xy3);
141
142 // low to high: xy02+xy03 xy12+xy13 xy20+xy21 xy30+xy31
143 __m256d swapped = _mm256_permute2f128_pd(temp01, temp23, 0x21);
144
145 // low to high: xy00+xy01 xy10+xy11 xy22+xy23 xy32+xy33
146 __m256d blended = _mm256_blend_pd(temp01, temp23, 0b1100);
147
148 __m256d r_vec = _mm256_add_pd(swapped, blended);
149
150 // cx += r_vec
151 double *cx_base = &cx[lxp * 4];
152 _mm256_storeu_pd(cx_base, _mm256_add_pd(r_vec, _mm256_loadu_pd(cx_base)));
153 }
154#endif
155}
156#endif // __AVX2__ && __FMA__
157
158/*******************************************************************************
159 * \brief Collocates coefficients C_x onto the grid for orthorhombic case.
160 * \author Ole Schuett
161 ******************************************************************************/
162static inline void __attribute__((always_inline))
163ortho_cx_to_grid(const int lp, const int kg1, const int kg2, const int jg1,
164 const int jg2, const int cmax,
165 const double pol[3][lp + 1][2 * cmax + 1],
166 const int map[3][2 * cmax + 1],
167 const int sections[3][2 * cmax + 1], const int npts_local[3],
170
171 // Lower and upper sphere bounds relative to center, ie. in cube coordinates.
172 const int lb = *((*sphere_bounds_iter)++);
173 const int ub = 1 - lb;
174
175 // AVX instructions can only load/store from evenly spaced memory locations.
176 // Since the sphere bounds can wrap around due to the grid's periodicity,
177 // the inner loop runs over sections with homogeneous cube to grid mapping.
178 for (int istart = lb; istart <= ub; istart++) {
179 const int istop = imin(ub, istart + sections[0][istart + cmax]);
180 const int cube2grid = map[0][istart + cmax] - istart;
181
182 const int stride = npts_local[1] * npts_local[0];
183 const int grid_index_0 = kg1 * stride + jg1 * npts_local[0];
184 const int grid_index_1 = kg2 * stride + jg1 * npts_local[0];
185 const int grid_index_2 = kg1 * stride + jg2 * npts_local[0];
186 const int grid_index_3 = kg2 * stride + jg2 * npts_local[0];
187 GRID_CONST_WHEN_INTEGRATE double *grid_base_0 = &grid[grid_index_0];
188 GRID_CONST_WHEN_INTEGRATE double *grid_base_1 = &grid[grid_index_1];
189 GRID_CONST_WHEN_INTEGRATE double *grid_base_2 = &grid[grid_index_2];
190 GRID_CONST_WHEN_INTEGRATE double *grid_base_3 = &grid[grid_index_3];
191
192 // Use AVX2 to process grid points in chunks of four, ie. 256 bit vectors.
193#if defined(__AVX2__) && defined(__FMA__)
194 const int istop_vec = istart + 4 * ((istop - istart + 1) / 4) - 1;
195 for (int i = istart; i <= istop_vec; i += 4) {
196 const int ig = i + cube2grid;
197 ortho_cx_to_grid_avx2(lp, cmax, i, pol, cx, &grid_base_0[ig],
198 &grid_base_1[ig], &grid_base_2[ig],
199 &grid_base_3[ig]);
200 }
201 istart = istop_vec + 1;
202#endif
203
204 // Process up to 3 remaining points - or everything if AVX2 isn't available.
205 for (int i = istart; i <= istop; i++) {
206 const int ig = i + cube2grid;
207 ortho_cx_to_grid_scalar(lp, cmax, i, pol, cx, &grid_base_0[ig],
208 &grid_base_1[ig], &grid_base_2[ig],
209 &grid_base_3[ig]);
210 }
211 istart = istop;
212 }
213}
214
215/*******************************************************************************
216 * \brief Transforms coefficients C_xy into C_x by fixing grid index j.
217 * \author Ole Schuett
218 ******************************************************************************/
219static inline void __attribute__((always_inline))
220ortho_cxy_to_cx(const int lp, const int j1, const int j2, const int cmax,
221 const double pol[3][lp + 1][2 * cmax + 1],
224
225 for (int lyp = 0; lyp <= lp; lyp++) {
226 for (int lxp = 0; lxp <= lp - lyp; lxp++) {
227 const double p1 = pol[1][lyp][j1 + cmax];
228 const double p2 = pol[1][lyp][j2 + cmax];
229 const int cxy_index = lyp * (lp + 1) * 2 + lxp * 2; // [lyp, lxp, 0]
230
231#if (GRID_DO_COLLOCATE)
232 // collocate
233 cx[lxp * 4 + 0] += cxy[cxy_index + 0] * p1;
234 cx[lxp * 4 + 1] += cxy[cxy_index + 1] * p1;
235 cx[lxp * 4 + 2] += cxy[cxy_index + 0] * p2;
236 cx[lxp * 4 + 3] += cxy[cxy_index + 1] * p2;
237#else
238 // integrate
239 cxy[cxy_index + 0] += cx[lxp * 4 + 0] * p1;
240 cxy[cxy_index + 1] += cx[lxp * 4 + 1] * p1;
241 cxy[cxy_index + 0] += cx[lxp * 4 + 2] * p2;
242 cxy[cxy_index + 1] += cx[lxp * 4 + 3] * p2;
243#endif
244 }
245 }
246}
247
248/*******************************************************************************
249 * \brief Loop body of ortho_cxy_to_grid to be inlined for low values of lp.
250 * \author Ole Schuett
251 ******************************************************************************/
252static inline void __attribute__((always_inline)) ortho_cxy_to_grid_low(
253 const int lp, const int j1, const int j2, const int kg1, const int kg2,
254 const int jg1, const int jg2, const int cmax,
255 const double pol[3][lp + 1][2 * cmax + 1], const int map[3][2 * cmax + 1],
256 const int sections[3][2 * cmax + 1], const int npts_local[3],
257 int **sphere_bounds_iter, double *cx, GRID_CONST_WHEN_COLLOCATE double *cxy,
259
260#if (GRID_DO_COLLOCATE)
261 // collocate
262 ortho_cxy_to_cx(lp, j1, j2, cmax, pol, cxy, cx);
265#else
266 // integrate
269 ortho_cxy_to_cx(lp, j1, j2, cmax, pol, cxy, cx);
270#endif
271}
272
273/*******************************************************************************
274 * \brief Collocates coefficients C_xy onto the grid for orthorhombic case.
275 * \author Ole Schuett
276 ******************************************************************************/
277static inline void ortho_cxy_to_grid(
278 const int lp, const int kg1, const int kg2, const int cmax,
279 const double pol[3][lp + 1][2 * cmax + 1], const int map[3][2 * cmax + 1],
280 const int sections[3][2 * cmax + 1], const int npts_local[3],
283
284 // The cube contains an even number of grid points in each direction and
285 // collocation is always performed on a pair of two opposing grid points.
286 // Hence, the points with index 0 and 1 are both assigned distance zero via
287 // the formular distance=(2*index-1)/2.
288
289 const int jstart = *((*sphere_bounds_iter)++);
290 const size_t cx_size = (lp + 1) * 4;
291 double cx[cx_size];
292 for (int j1 = jstart; j1 <= 0; j1++) {
293 const int j2 = 1 - j1;
294 const int jg1 = map[1][j1 + cmax];
295 const int jg2 = map[1][j2 + cmax];
296
297 memset(cx, 0, cx_size * sizeof(double));
298
299 // Generate separate branches for low values of lp gives up to 30% speedup.
300 if (lp <= GRID_MAX_LP_OPTIMIZED) {
302 for (int ilp = 0; ilp <= GRID_MAX_LP_OPTIMIZED; ilp++) {
303 if (lp == ilp) {
304 ortho_cxy_to_grid_low(ilp, j1, j2, kg1, kg2, jg1, jg2, cmax, pol, map,
306 cxy, grid);
307 }
308 }
309 } else {
310 ortho_cxy_to_grid_low(lp, j1, j2, kg1, kg2, jg1, jg2, cmax, pol, map,
312 grid);
313 }
314 }
315}
316
317/*******************************************************************************
318 * \brief Transforms coefficients C_xyz into C_xz by fixing grid index k.
319 * \author Ole Schuett
320 ******************************************************************************/
321static inline void ortho_cxyz_to_cxy(const int lp, const int k1, const int k2,
322 const int cmax,
323 const double pol[3][lp + 1][2 * cmax + 1],
324 GRID_CONST_WHEN_COLLOCATE double *cxyz,
326
327 for (int lzp = 0; lzp <= lp; lzp++) {
328 for (int lyp = 0; lyp <= lp - lzp; lyp++) {
329 for (int lxp = 0; lxp <= lp - lzp - lyp; lxp++) {
330 const double p1 = pol[2][lzp][k1 + cmax];
331 const double p2 = pol[2][lzp][k2 + cmax];
332 const int cxyz_index =
333 lzp * (lp + 1) * (lp + 1) + lyp * (lp + 1) + lxp; // [lzp, lyp, lxp]
334 const int cxy_index = lyp * (lp + 1) * 2 + lxp * 2; // [lyp, lxp, 0]
335
336#if (GRID_DO_COLLOCATE)
337 // collocate
338 cxy[cxy_index + 0] += cxyz[cxyz_index] * p1;
339 cxy[cxy_index + 1] += cxyz[cxyz_index] * p2;
340#else
341 // integrate
342 cxyz[cxyz_index] += cxy[cxy_index + 0] * p1;
343 cxyz[cxyz_index] += cxy[cxy_index + 1] * p2;
344#endif
345 }
346 }
347 }
348}
349
350/*******************************************************************************
351 * \brief Collocates coefficients C_xyz onto the grid for orthorhombic case.
352 * \author Ole Schuett
353 ******************************************************************************/
354static inline void
355ortho_cxyz_to_grid(const int lp, const double zetp, const double dh[3][3],
356 const double dh_inv[3][3], const double rp[3],
357 const int npts_global[3], const int npts_local[3],
358 const int shift_local[3], const double radius,
359 GRID_CONST_WHEN_COLLOCATE double *cxyz,
361
362 // *** position of the gaussian product
363 //
364 // this is the actual definition of the position on the grid
365 // i.e. a point rp(:) gets here grid coordinates
366 // MODULO(rp(:)/dr(:),npts_global(:))+1
367 // hence (0.0,0.0,0.0) in real space is rsgrid%lb on the rsgrid in Fortran
368 // and (1,1,1) on grid here in C.
369
370 // cubecenter(:) = FLOOR(MATMUL(dh_inv, rp))
371 int cubecenter[3];
372 for (int i = 0; i < 3; i++) {
373 double dh_inv_rp = 0.0;
374 for (int j = 0; j < 3; j++) {
375 dh_inv_rp += dh_inv[j][i] * rp[j];
376 }
377 cubecenter[i] = (int)floor(dh_inv_rp);
378 }
379
380 double roffset[3];
381 for (int i = 0; i < 3; i++) {
382 roffset[i] = rp[i] - ((double)cubecenter[i]) * dh[i][i];
383 }
384
385 // Lookup loop bounds for spherical cutoff.
386 int *sphere_bounds;
387 double disr_radius;
388 grid_sphere_cache_lookup(radius, dh, dh_inv, &sphere_bounds, &disr_radius);
389 int **sphere_bounds_iter = &sphere_bounds;
390
391 // Cube bounds.
392 int lb_cube[3], ub_cube[3];
393 for (int i = 0; i < 3; i++) {
394 lb_cube[i] = (int)ceil(-1e-8 - disr_radius * dh_inv[i][i]);
395 ub_cube[i] = 1 - lb_cube[i];
396 // If grid is not period check that cube fits without wrapping.
397 if (npts_global[i] != npts_local[i]) {
398 const int offset =
399 modulo(cubecenter[i] + lb_cube[i] - shift_local[i], npts_global[i]) -
400 lb_cube[i];
401 assert(offset + ub_cube[i] < npts_local[i]);
402 assert(offset + lb_cube[i] >= 0);
403 }
404 }
405
406 // cmax = MAXVAL(ub_cube)
407 const int cmax = imax(imax(ub_cube[0], ub_cube[1]), ub_cube[2]);
408
409 // Precompute (x-xp)**lp*exp(..) for each direction.
410 double pol_mutable[3][lp + 1][2 * cmax + 1];
411 for (int idir = 0; idir < 3; idir++) {
412 const double dr = dh[idir][idir];
413 const double ro = roffset[idir];
414 // Reuse the result from the previous gridpoint to avoid to many exps:
415 // exp( -a*(x+d)**2) = exp(-a*x**2)*exp(-2*a*x*d)*exp(-a*d**2)
416 // exp(-2*a*(x+d)*d) = exp(-2*a*x*d)*exp(-2*a*d**2)
417 const double t_exp_1 = exp(-zetp * pow(dr, 2));
418 const double t_exp_2 = pow(t_exp_1, 2);
419 double t_exp_min_1 = exp(-zetp * pow(+dr - ro, 2));
420 double t_exp_min_2 = exp(-2 * zetp * (+dr - ro) * (-dr));
421 for (int ig = 0; ig >= lb_cube[idir]; ig--) {
422 const double rpg = ig * dr - ro;
423 t_exp_min_1 *= t_exp_min_2 * t_exp_1;
424 t_exp_min_2 *= t_exp_2;
425 double pg = t_exp_min_1;
426 for (int icoef = 0; icoef <= lp; icoef++) {
427 pol_mutable[idir][icoef][ig + cmax] = pg; // exp(-zetp*rpg**2)
428 pg *= rpg;
429 }
430 }
431 double t_exp_plus_1 = exp(-zetp * pow(-ro, 2));
432 double t_exp_plus_2 = exp(-2 * zetp * (-ro) * (+dr));
433 for (int ig = 0; ig >= lb_cube[idir]; ig--) {
434 const double rpg = (1 - ig) * dr - ro;
435 t_exp_plus_1 *= t_exp_plus_2 * t_exp_1;
436 t_exp_plus_2 *= t_exp_2;
437 double pg = t_exp_plus_1;
438 for (int icoef = 0; icoef <= lp; icoef++) {
439 pol_mutable[idir][icoef][1 - ig + cmax] = pg; // exp(-zetp*rpg**2)
440 pg *= rpg;
441 }
442 }
443 }
444 const double (*pol)[lp + 1][2 * cmax + 1] =
445 (const double (*)[lp + 1][2 * cmax + 1]) pol_mutable;
446
447 // Precompute mapping from cube to grid indices for each direction
448 int map_mutable[3][2 * cmax + 1];
449 for (int i = 0; i < 3; i++) {
450 for (int k = -cmax; k <= +cmax; k++) {
451 map_mutable[i][k + cmax] =
452 modulo(cubecenter[i] + k - shift_local[i], npts_global[i]);
453 }
454 }
455 const int (*map)[2 * cmax + 1] = (const int (*)[2 * cmax + 1]) map_mutable;
456
457 // Precompute length of sections with homogeneous cube to grid mapping.
458 int sections_mutable[3][2 * cmax + 1];
459 for (int i = 0; i < 3; i++) {
460 for (int kg = 2 * cmax; kg >= 0; kg--) {
461 if (kg == 2 * cmax || map[i][kg] != map[i][kg + 1] - 1) {
462 sections_mutable[i][kg] = 0;
463 } else {
464 sections_mutable[i][kg] = sections_mutable[i][kg + 1] + 1;
465 }
466 }
467 }
468 const int (*sections)[2 * cmax + 1] =
469 (const int (*)[2 * cmax + 1]) sections_mutable;
470
471 // Loop over k dimension of the cube.
472 const int kstart = *((*sphere_bounds_iter)++);
473 const size_t cxy_size = (lp + 1) * (lp + 1) * 2;
474 double cxy[cxy_size];
475 for (int k1 = kstart; k1 <= 0; k1++) {
476 const int k2 = 1 - k1;
477 const int kg1 = map[2][k1 + cmax];
478 const int kg2 = map[2][k2 + cmax];
479
480 memset(cxy, 0, cxy_size * sizeof(double));
481
482#if (GRID_DO_COLLOCATE)
483 // collocate
484 ortho_cxyz_to_cxy(lp, k1, k2, cmax, pol, cxyz, cxy);
487#else
488 // integrate
491 ortho_cxyz_to_cxy(lp, k1, k2, cmax, pol, cxyz, cxy);
492#endif
493 }
494}
495
496/*******************************************************************************
497 * \brief Collocates coefficients C_i onto the grid for general case.
498 * \author Ole Schuett
499 ******************************************************************************/
500static inline void __attribute__((always_inline)) general_ci_to_grid(
501 const int lp, const int jg, const int kg, const int ismin, const int ismax,
502 const int npts_local[3], const int index_min[3], const int index_max[3],
503 const int map_i[], const int sections_i[], const double gp[3], const int k,
504 const int j, const double exp_ij[], const double exp_jk[],
505 const double exp_ik[], GRID_CONST_WHEN_COLLOCATE double *ci,
507
508 const int base = kg * npts_local[1] * npts_local[0] + jg * npts_local[0];
509 const int stride_i = index_max[0] - index_min[0] + 1;
510 const int stride_j = index_max[1] - index_min[1] + 1;
511 const int idx_jk = (k - index_min[2]) * stride_j + j - index_min[1];
512
513#if defined(__AVX2__) && defined(__FMA__)
514 const __m256d v_gp0 = _mm256_set1_pd(gp[0]);
515 const __m256d v_four = _mm256_set1_pd(4.0);
516 const __m256d v_ejk = _mm256_set1_pd(exp_jk[idx_jk]);
517 __m256d v_ci[lp + 1];
519 for (int il = 0; il <= lp; il++) {
520#if (GRID_DO_COLLOCATE)
521 v_ci[il] = _mm256_set1_pd(ci[il]);
522#else
523 v_ci[il] = _mm256_setzero_pd();
524#endif
525 }
526#endif
527
528 // AVX instructions can only load/store from evenly spaced memory locations.
529 // Since the cube can wrap around due to the grid's periodicity,
530 // the inner loop runs over sections with homogeneous cube to grid mapping.
531 for (int istart = ismin; istart <= ismax; istart++) {
532 const int istop = imin(ismax, istart + sections_i[istart - index_min[0]]);
533 if (map_i[istart - index_min[0]] < 0) {
534 istart = istop; // skip over out-of-bounds indicies
535 continue;
536 }
537
538 const int cube2grid = map_i[istart - index_min[0]] - istart;
539
540#if defined(__AVX2__) && defined(__FMA__)
541 const int istart_tail = istart + ((istop - istart + 1) / 4) * 4;
542 __m256d v_i = _mm256_set_pd(istart + 3, istart + 2, istart + 1, istart);
543 for (int i = istart; i < istart_tail; i += 4) {
544 const int idx_ij = (j - index_min[1]) * stride_i + i - index_min[0];
545 const int idx_ik = (k - index_min[2]) * stride_i + i - index_min[0];
546
547 const __m256d v_di = _mm256_sub_pd(v_i, v_gp0);
548 v_i = _mm256_add_pd(v_i, v_four);
549 const __m256d v_eij = _mm256_loadu_pd(&exp_ij[idx_ij]);
550 const __m256d v_eik = _mm256_loadu_pd(&exp_ik[idx_ik]);
551 __m256d v_dip = _mm256_mul_pd(_mm256_mul_pd(v_eij, v_ejk), v_eik);
552
553 const int grid_index = base + i + cube2grid;
554#if (GRID_DO_COLLOCATE)
555 __m256d v_reg = _mm256_mul_pd(v_ci[0], v_dip);
557 for (int il = 1; il <= lp; il++) {
558 v_dip = _mm256_mul_pd(v_dip, v_di);
559 v_reg = _mm256_fmadd_pd(v_ci[il], v_dip, v_reg);
560 }
561 __m256d v_grid = _mm256_loadu_pd(&grid[grid_index]);
562 _mm256_storeu_pd(&grid[grid_index], _mm256_add_pd(v_grid, v_reg));
563#else
564 const __m256d v_reg = _mm256_loadu_pd(&grid[grid_index]);
565 v_ci[0] = _mm256_fmadd_pd(v_reg, v_dip, v_ci[0]);
567 for (int il = 1; il <= lp; il++) {
568 v_dip = _mm256_mul_pd(v_dip, v_di);
569 v_ci[il] = _mm256_fmadd_pd(v_reg, v_dip, v_ci[il]);
570 }
571#endif
572 }
573#else
574 const int istart_tail = istart;
575#endif
576
577 for (int i = istart_tail; i <= istop; i++) {
578 const int ig = i + cube2grid;
579 const double di = i - gp[0];
580
581 const int idx_ij = (j - index_min[1]) * stride_i + i - index_min[0];
582 const int idx_ik = (k - index_min[2]) * stride_i + i - index_min[0];
583
584 // Mathieu's trick: Calculate 3D Gaussian from three precomputed 2D tables
585 //
586 // r = (i-gp[0])*dh[0,:] + (j-gp[1])*dh[1,:] + (k-gp[2])*dh[2,:]
587 // = a + b + c
588 //
589 // r**2 = (a + b + c)**2 = a**2 + b**2 + c**2 + 2ab + 2bc + 2ca
590 //
591 // exp(-r**2) = exp(-0.5*a**2 - 2ab - 0.5*b**2)
592 // * exp(-0.5*b**2 - 2bc - 0.5*c**2)
593 // * exp(-0.5*a**2 - 2ac - 0.5*c**2)
594 //
595 const double gaussian = exp_ij[idx_ij] * exp_jk[idx_jk] * exp_ik[idx_ik];
596
597 const int grid_index = base + ig; // [kg, jg, ig]
598 double dip = gaussian;
599
600#if (GRID_DO_COLLOCATE)
601 // collocate
602 double reg = 0.0;
603 for (int il = 0; il <= lp; il++) {
604 reg += ci[il] * dip;
605 dip *= di;
606 }
607 grid[grid_index] += reg;
608#else
609 // integrate
610 const double reg = grid[grid_index];
611 for (int il = 0; il <= lp; il++) {
612 ci[il] += reg * dip;
613 dip *= di;
614 }
615#endif
616 }
617 istart = istop;
618 }
619
620#if defined(__AVX2__) && defined(__FMA__) && (!GRID_DO_COLLOCATE)
622 for (int il = 0; il <= lp; il++) {
623 __m128d v_low = _mm256_castpd256_pd128(v_ci[il]);
624 __m128d v_high = _mm256_extractf128_pd(v_ci[il], 1);
625 __m128d v_sum2 = _mm_add_pd(v_low, v_high);
626 __m128d v_shuf = _mm_unpackhi_pd(v_sum2, v_sum2);
627 ci[il] += _mm_cvtsd_f64(_mm_add_sd(v_sum2, v_shuf));
628 }
629#endif
630}
631
632/*******************************************************************************
633 * \brief Transforms coefficients C_ij into C_i by fixing grid index j.
634 * \author Ole Schuett
635 ******************************************************************************/
636static inline void __attribute__((always_inline))
637general_cij_to_ci(const int lp, const double dj,
638 GRID_CONST_WHEN_COLLOCATE double *cij,
639 GRID_CONST_WHEN_INTEGRATE double *ci) {
640 double djp = 1.0;
641 for (int jl = 0; jl <= lp; jl++) {
642 for (int il = 0; il <= lp - jl; il++) {
643 const int cij_index = jl * (lp + 1) + il; // [jl, il]
644#if (GRID_DO_COLLOCATE)
645 ci[il] += cij[cij_index] * djp; // collocate
646#else
647 cij[cij_index] += ci[il] * djp; // integrate
648#endif
649 }
650 djp *= dj;
651 }
652}
653
654/*******************************************************************************
655 * \brief Loop body of general_cij_to_grid to be inlined for low values of lp.
656 * \author Ole Schuett
657 ******************************************************************************/
658static inline void __attribute__((always_inline)) general_cij_to_grid_low(
659 const int lp, const int jg, const int kg, const int ismin, const int ismax,
660 const int npts_local[3], const int index_min[3], const int index_max[3],
661 const int map_i[], const int sections_i[], const double gp[3], const int k,
662 const int j, const double exp_ij[], const double exp_jk[],
663 const double exp_ik[], const double dj, double *ci,
664 GRID_CONST_WHEN_COLLOCATE double *cij,
666
667#if (GRID_DO_COLLOCATE)
668 // collocate
669 general_cij_to_ci(lp, dj, cij, ci);
670 general_ci_to_grid(lp, jg, kg, ismin, ismax, npts_local, index_min, index_max,
671 map_i, sections_i, gp, k, j, exp_ij, exp_jk, exp_ik, ci,
672 grid);
673#else
674 // integrate
675 general_ci_to_grid(lp, jg, kg, ismin, ismax, npts_local, index_min, index_max,
676 map_i, sections_i, gp, k, j, exp_ij, exp_jk, exp_ik, ci,
677 grid);
678 general_cij_to_ci(lp, dj, cij, ci);
679#endif
680}
681
682/*******************************************************************************
683 * \brief Collocates coefficients C_ij onto the grid for j in [jsmin, jsmax].
684 * \author Ole Schuett
685 ******************************************************************************/
686static inline void __attribute__((always_inline)) general_cij_to_grid(
687 const int lp, const int k, const int kg, const int jsmin, const int jsmax,
688 const int npts_local[3], const int index_min[3], const int index_max[3],
689 const int map_i[], const int map_j[], const int sections_i[],
690 const int sections_j[], const double dh[3][3], const double gp[3],
691 const double radius, const double exp_ij[], const double exp_jk[],
692 const double exp_ik[], GRID_CONST_WHEN_COLLOCATE double *cij,
694
695 const double a =
696 dh[0][0] * dh[0][0] + dh[0][1] * dh[0][1] + dh[0][2] * dh[0][2];
697 const double inv_2a = 1.0 / (2.0 * a);
698 const double radius2 = radius * radius;
699 const double gp0_dh0[3] = {(0 - gp[0]) * dh[0][0], (0 - gp[0]) * dh[0][1],
700 (0 - gp[0]) * dh[0][2]};
701 const double dk_dh2[3] = {(k - gp[2]) * dh[2][0], (k - gp[2]) * dh[2][1],
702 (k - gp[2]) * dh[2][2]};
703
704 for (int j = jsmin; j <= jsmax; j++) {
705 const int jg = map_j[j - index_min[1]];
706 if (jg < 0) {
707 j += sections_j[j - index_min[1]]; // skip over out-of-bounds indicies
708 continue;
709 }
710
711 //--------------------------------------------------------------------
712 // Find bounds for the inner loop based on a quadratic equation in i.
713 //
714 // The real-space vector from the center of the gaussian to the
715 // grid point i,j,k is given by:
716 // r = (i-gp[0])*dh[0,:] + (j-gp[1])*dh[1,:] + (k-gp[2])*dh[2,:]
717 //
718 // Separating the term that depends on i:
719 // r = i*dh[0,:] - gp[0]*dh[0,:] + (j-gp[1])*dh[1,:] + (k-gp[2])*dh[2,:]
720 // = i*dh[0,:] + v
721 //
722 // The squared distance works out to:
723 // r**2 = dh[0,:]**2 * i**2 + 2 * v * dh[0,:] * i + v**2
724 // = a * i**2 + b * i + c
725 //
726 // Solving r**2==radius**2 for i yields:
727 // d = b**2 - 4 * a * (c - radius**2)
728 // i = (-b \pm sqrt(d)) / (2*a)
729 //
730 const double dj = j - gp[1];
731 double b = 0.0, c = 0.0;
732 for (int i = 0; i < 3; i++) {
733 const double v = (gp0_dh0[i] + dj * dh[1][i]) + dk_dh2[i];
734 b += 2.0 * v * dh[0][i];
735 c += v * v;
736 }
737 const double d = b * b - 4.0 * a * (c - radius2);
738
739 if (0.0 < d) {
740 const double sqrt_d = sqrt(d);
741 const int ismin = (int)ceil((-b - sqrt_d) * inv_2a);
742 const int ismax = (int)floor((-b + sqrt_d) * inv_2a);
743 if (ismin > ismax)
744 continue;
745
746 double ci[lp + 1];
747 memset(ci, 0, sizeof(ci));
748
749 // Separate branches for low values of lp are generated in
750 // general_cijk_to_grid.
751 general_cij_to_grid_low(lp, jg, kg, ismin, ismax, npts_local, index_min,
752 index_max, map_i, sections_i, gp, k, j, exp_ij,
753 exp_jk, exp_ik, dj, ci, cij, grid);
754 }
755 }
756}
757
758/*******************************************************************************
759 * \brief Transforms coefficients C_ijk into C_ij by fixing grid index k.
760 * \author Ole Schuett
761 ******************************************************************************/
762static inline void general_cijk_to_cij(const int lp, const double dk,
763 GRID_CONST_WHEN_COLLOCATE double *cijk,
764 GRID_CONST_WHEN_INTEGRATE double *cij) {
765 double dkp = 1.0;
766 for (int kl = 0; kl <= lp; kl++) {
767 for (int jl = 0; jl <= lp - kl; jl++) {
768 for (int il = 0; il <= lp - kl - jl; il++) {
769 const int cij_index = jl * (lp + 1) + il; // [jl, il]
770 const int cijk_index =
771 kl * (lp + 1) * (lp + 1) + jl * (lp + 1) + il; // [kl, jl, il]
772#if (GRID_DO_COLLOCATE)
773 cij[cij_index] += cijk[cijk_index] * dkp; // collocate
774#else
775 cijk[cijk_index] += cij[cij_index] * dkp; // integrate
776#endif
777 }
778 }
779 dkp *= dk;
780 }
781}
782
783/*******************************************************************************
784 * \brief Precompute mapping of grid indices and its homogeneous sections.
785 * Returns false if no index in [index_min, index_max] maps into bounds.
786 * \author Ole Schuett
787 ******************************************************************************/
788static inline bool
789general_precompute_mapping(const int index_min, const int index_max,
790 const int shift_local, const int npts_global,
791 const int bounds[2], int map[], int sections[]) {
792
793 // Precompute mapping from continous grid indices to pbc wraped.
794 bool has_valid = false;
795 for (int k = index_min; k <= index_max; k++) {
796 const int kg = modulo(k - shift_local, npts_global);
797 if (bounds[0] <= kg && kg <= bounds[1]) {
798 map[k - index_min] = kg;
799 has_valid = true;
800 } else {
801 map[k - index_min] = INT_MIN; // out of bounds - not mapped
802 }
803 }
804
805 if (!has_valid) {
806 return false;
807 }
808
809 // Precompute length of sections with homogeneous cube to grid mapping.
810 const int range = index_max - index_min + 1;
811 for (int kg = range - 1; kg >= 0; kg--) {
812 if (kg == range - 1 || map[kg] != map[kg + 1] - 1) {
813 sections[kg] = 0;
814 } else {
815 sections[kg] = sections[kg + 1] + 1;
816 }
817 }
818 return true;
819}
820
821/*******************************************************************************
822 * \brief Fill one of the 2D tables that speedup 3D Gaussian (Mathieu's trick).
823 * \author Ole Schuett
824 ******************************************************************************/
825static inline void
826general_fill_exp_table(const int idir, const int jdir, const int index_min[3],
827 const int index_max[3], const double zetp,
828 const double dh[3][3], const double gp[3],
829 double exp_table[]) {
830
831 const int stride_i = index_max[idir] - index_min[idir] + 1;
832 const double h_ii = dh[idir][0] * dh[idir][0] + dh[idir][1] * dh[idir][1] +
833 dh[idir][2] * dh[idir][2];
834 const double h_jj = dh[jdir][0] * dh[jdir][0] + dh[jdir][1] * dh[jdir][1] +
835 dh[jdir][2] * dh[jdir][2];
836 const double h_ij = dh[idir][0] * dh[jdir][0] + dh[idir][1] * dh[jdir][1] +
837 dh[idir][2] * dh[jdir][2];
838
839 double exp_half_i[stride_i];
840 for (int i = index_min[idir]; i <= index_max[idir]; i++) {
841 const double di = i - gp[idir];
842 exp_half_i[i - index_min[idir]] = exp(-0.5 * zetp * di * di * h_ii);
843 }
844
845 for (int j = index_min[jdir]; j <= index_max[jdir]; j++) {
846 const double dj = j - gp[jdir];
847 const double rjj = 0.5 * dj * dj * h_jj;
848 const double rij_unit = dj * h_ij;
849 const double exp_ij_unit = exp(-zetp * 2.0 * rij_unit);
850
851 // compute exponentials symmetrically around cube center
852 const int i_center = (int)gp[idir];
853 const double di_center = i_center - gp[idir];
854 const double rij_center = di_center * rij_unit;
855 const double exp_ij_center = exp(-zetp * (rjj + 2.0 * rij_center));
856
857 // above center
858 double exp_ij = exp_ij_center;
859 for (int i = i_center; i <= index_max[idir]; i++) {
860 const int idx = (j - index_min[jdir]) * stride_i + i - index_min[idir];
861 exp_table[idx] = exp_ij * exp_half_i[i - index_min[idir]];
862 exp_ij *= exp_ij_unit;
863 }
864
865 // below center
866 const double exp_ij_unit_inv = 1.0 / exp_ij_unit;
867 exp_ij = exp_ij_center * exp_ij_unit_inv;
868 for (int i = i_center - 1; i >= index_min[idir]; i--) {
869 const int idx = (j - index_min[jdir]) * stride_i + i - index_min[idir];
870 exp_table[idx] = exp_ij * exp_half_i[i - index_min[idir]];
871 exp_ij *= exp_ij_unit_inv;
872 }
873 }
874}
875
876/*******************************************************************************
877 * \brief K-loop body of general_cijk_to_grid to be inlined for low values of
878 * lp.
879 ******************************************************************************/
880static inline void __attribute__((always_inline)) general_cijk_kloop_low(
881 const int lp, const int npts_local[3], const int index_min[3],
882 const int index_max[3], const int map_i[], const int map_j[],
883 const int map_k[], const int sections_i[], const int sections_j[],
884 const int sections_k[], const double dh[3][3], const double gp[3],
885 const double radius, const double exp_ij[], const double exp_jk[],
886 const double exp_ik[], double *cij, GRID_CONST_WHEN_COLLOCATE double *cijk,
888
889 const double *h0 = dh[0], *h1 = dh[1], *h2 = dh[2];
890 const double h00 = h0[0] * h0[0] + h0[1] * h0[1] + h0[2] * h0[2];
891 const double h11 = h1[0] * h1[0] + h1[1] * h1[1] + h1[2] * h1[2];
892 const double h22 = h2[0] * h2[0] + h2[1] * h2[1] + h2[2] * h2[2];
893 const double c01[3] = {h0[1] * h1[2] - h0[2] * h1[1],
894 h0[2] * h1[0] - h0[0] * h1[2],
895 h0[0] * h1[1] - h0[1] * h1[0]};
896 const double c02[3] = {h0[1] * h2[2] - h0[2] * h2[1],
897 h0[2] * h2[0] - h0[0] * h2[2],
898 h0[0] * h2[1] - h0[1] * h2[0]};
899 const double det_dh = c01[0] * h2[0] + c01[1] * h2[1] + c01[2] * h2[2];
900 // Minimizing ||di*dh[0] + dj*dh[1] + dk*dh[2]||^2 <= radius^2 over real
901 // di = i - gp[0] projects onto the plane orthogonal to dh[0], giving
902 // ||dj*c01 + dk*c02||^2 <= h00*radius^2 with c01 = dh[0] x dh[1],
903 // c02 = dh[0] x dh[2], and ||c01 x c02||^2 = h00*det(dh)^2. Fall back to
904 // [index_min[1], index_max[1]] if any cell angle or elevation has
905 // sin^2 <= 1e-6; +/-1 pad absorbs rounding and j_disc_rtol skips
906 // outward-rounded endpoint k-planes outside the sphere.
907 const double j_sin2_min = 1e-6;
908 const double j_disc_rtol = 1e-9;
909 const double A_j = c01[0] * c01[0] + c01[1] * c01[1] + c01[2] * c01[2];
910 const double c02_norm2 = c02[0] * c02[0] + c02[1] * c02[1] + c02[2] * c02[2];
911 const double det_dh2 = det_dh * det_dh;
912 const bool has_j_bound = (A_j > j_sin2_min * h00 * h11) &&
913 (c02_norm2 > j_sin2_min * h00 * h22) &&
914 (det_dh2 > j_sin2_min * h00 * h11 * h22);
915 const double inv_Aj = has_j_bound ? (1.0 / A_j) : 0.0;
916 const double Bj_coeff =
917 -(c01[0] * c02[0] + c01[1] * c02[1] + c01[2] * c02[2]);
918 const double disc_r2 = h00 * A_j * radius * radius;
919 const double disc_k2_coeff = h00 * det_dh2;
920
921 const int cij_size = (lp + 1) * (lp + 1);
922 for (int k = index_min[2]; k <= index_max[2]; k++) {
923 const int kg = map_k[k - index_min[2]];
924 if (kg < 0) {
925 k += sections_k[k - index_min[2]]; // skip over out-of-bounds indicies
926 continue;
927 }
928
929 const double dk = (double)k - gp[2];
930 int jsmin = index_min[1];
931 int jsmax = index_max[1];
932 if (has_j_bound) {
933 const double dk2_term = dk * dk * disc_k2_coeff;
934 const double disc_j = disc_r2 - dk2_term;
935 if (disc_j < -j_disc_rtol * (disc_r2 + dk2_term))
936 continue;
937 const double B_j = dk * Bj_coeff;
938 const double s_j = sqrt(fmax(0.0, disc_j));
939 const double j_lo =
940 fmax((double)jsmin, ceil(gp[1] + (B_j - s_j) * inv_Aj) - 1.0);
941 const double j_hi =
942 fmin((double)jsmax, floor(gp[1] + (B_j + s_j) * inv_Aj) + 1.0);
943 if (j_lo > j_hi)
944 continue;
945 jsmin = (int)j_lo;
946 jsmax = (int)j_hi;
947 }
948
949 // zero coef_xyt
950 memset(cij, 0, cij_size * sizeof(double));
951
952#if (GRID_DO_COLLOCATE)
953 // collocate
954 general_cijk_to_cij(lp, dk, cijk, cij);
955 general_cij_to_grid(lp, k, kg, jsmin, jsmax, npts_local, index_min,
956 index_max, map_i, map_j, sections_i, sections_j, dh, gp,
957 radius, exp_ij, exp_jk, exp_ik, cij, grid);
958#else
959 // integrate
960 general_cij_to_grid(lp, k, kg, jsmin, jsmax, npts_local, index_min,
961 index_max, map_i, map_j, sections_i, sections_j, dh, gp,
962 radius, exp_ij, exp_jk, exp_ik, cij, grid);
963 general_cijk_to_cij(lp, dk, cijk, cij);
964#endif
965 }
966}
967
968/*******************************************************************************
969 * \brief Collocates coefficients C_ijk onto the grid for general case.
970 * \author Ole Schuett
971 ******************************************************************************/
972// Keep out-of-line (called once per task) so GCC optimizes the 11 unrolled
973// general k-loop lp branches as a standalone function and does not exhaust
974// the inlining budget in grid_cpu_*_pgf_product (ortho_cxy_to_grid).
975static void __attribute__((noinline))
976general_cijk_to_grid(const int border_mask, const int lp, const double zetp,
977 const double dh[3][3], const double dh_inv[3][3],
978 const double rp[3], const int npts_global[3],
979 const int npts_local[3], const int shift_local[3],
980 const int border_width[3], const double radius,
981 GRID_CONST_WHEN_COLLOCATE double *cijk,
983
984 // Default for border_mask == 0.
985 int bounds_i[2] = {0, npts_local[0] - 1};
986 int bounds_j[2] = {0, npts_local[1] - 1};
987 int bounds_k[2] = {0, npts_local[2] - 1};
988
989 // See also rs_find_node() in task_list_methods.F.
990 // If the bit is set then we need to exclude the border in that direction.
991 if (border_mask & (1 << 0))
992 bounds_i[0] += border_width[0];
993 if (border_mask & (1 << 1))
994 bounds_i[1] -= border_width[0];
995 if (border_mask & (1 << 2))
996 bounds_j[0] += border_width[1];
997 if (border_mask & (1 << 3))
998 bounds_j[1] -= border_width[1];
999 if (border_mask & (1 << 4))
1000 bounds_k[0] += border_width[2];
1001 if (border_mask & (1 << 5))
1002 bounds_k[1] -= border_width[2];
1003
1004 if (bounds_i[0] > bounds_i[1] || bounds_j[0] > bounds_j[1] ||
1005 bounds_k[0] > bounds_k[1]) {
1006 return;
1007 }
1008
1009 // center in grid coords
1010 // gp = MATMUL(dh_inv, rp)
1011 double gp[3] = {0.0, 0.0, 0.0};
1012 for (int i = 0; i < 3; i++) {
1013 for (int j = 0; j < 3; j++) {
1014 gp[i] += dh_inv[j][i] * rp[j];
1015 }
1016 }
1017
1018 // Get the min max indices that contain the sphere around rp of radius radius.
1019 // Project the sphere onto each grid axis via the reciprocal-lattice vector
1020 // norms ||dh_inv[:,idir]||_2 so non-orthogonal cells avoid excess box
1021 // corners.
1022 // Guard against floating-point rounding in sqrt/norm when gp +/- rad_idx hits
1023 // an int.
1024 const double sphere_bound_eps = 1.0 + 1e-12;
1025 int index_min[3];
1026 int index_max[3];
1027 for (int idir = 0; idir < 3; idir++) {
1028 const double norm = sqrt(dh_inv[0][idir] * dh_inv[0][idir] +
1029 dh_inv[1][idir] * dh_inv[1][idir] +
1030 dh_inv[2][idir] * dh_inv[2][idir]);
1031 const double rad_idx = radius * norm * sphere_bound_eps;
1032 index_min[idir] = (int)floor(gp[idir] - rad_idx);
1033 index_max[idir] = (int)ceil(gp[idir] + rad_idx);
1034 }
1035
1036 // Mappings and exponential tables live in thread local heap scratch instead
1037 // of VLAs, because large radii on fine grids can exceed the default stack.
1038 const int range_i = index_max[0] - index_min[0] + 1;
1039 const int range_j = index_max[1] - index_min[1] + 1;
1040 const int range_k = index_max[2] - index_min[2] + 1;
1041 const size_t exp_count = (size_t)range_i * range_j +
1042 (size_t)range_j * range_k +
1043 (size_t)range_k * range_i;
1044 const size_t map_count = 2 * ((size_t)range_i + range_j + range_k);
1045 double *const scratch = (double *)grid_library_get_scratch(
1046 exp_count * sizeof(double) + map_count * sizeof(int));
1047 double *restrict const exp_ij = scratch;
1048 double *restrict const exp_jk = exp_ij + (size_t)range_i * range_j;
1049 double *restrict const exp_ik = exp_jk + (size_t)range_j * range_k;
1050 int *restrict const map_i = (int *)(exp_ik + (size_t)range_k * range_i);
1051 int *restrict const sections_i = map_i + range_i;
1052 int *restrict const map_j = sections_i + range_i;
1053 int *restrict const sections_j = map_j + range_j;
1054 int *restrict const map_k = sections_j + range_j;
1055 int *restrict const sections_k = map_k + range_k;
1056 if (!general_precompute_mapping(index_min[0], index_max[0], shift_local[0],
1057 npts_global[0], bounds_i, map_i,
1058 sections_i)) {
1059 return;
1060 }
1061 if (!general_precompute_mapping(index_min[1], index_max[1], shift_local[1],
1062 npts_global[1], bounds_j, map_j,
1063 sections_j)) {
1064 return;
1065 }
1066 if (!general_precompute_mapping(index_min[2], index_max[2], shift_local[2],
1067 npts_global[2], bounds_k, map_k,
1068 sections_k)) {
1069 return;
1070 }
1071
1072 // Precompute exponentials
1073 general_fill_exp_table(0, 1, index_min, index_max, zetp, dh, gp, exp_ij);
1074 general_fill_exp_table(1, 2, index_min, index_max, zetp, dh, gp, exp_jk);
1075 general_fill_exp_table(0, 2, index_min, index_max, zetp, dh, gp, exp_ik);
1076
1077 // go over the grid, but cycle if the point is not within the radius
1078 const int cij_size = (lp + 1) * (lp + 1);
1079 double cij[cij_size];
1080 // Generate separate branches for low values of lp.
1081 if (lp <= GRID_MAX_LP_OPTIMIZED) {
1083 for (int ilp = 0; ilp <= GRID_MAX_LP_OPTIMIZED; ilp++) {
1084 if (lp == ilp) {
1085 general_cijk_kloop_low(ilp, npts_local, index_min, index_max, map_i,
1086 map_j, map_k, sections_i, sections_j, sections_k,
1087 dh, gp, radius, exp_ij, exp_jk, exp_ik, cij,
1088 cijk, grid);
1089 }
1090 }
1091 } else {
1092 general_cijk_kloop_low(lp, npts_local, index_min, index_max, map_i, map_j,
1093 map_k, sections_i, sections_j, sections_k, dh, gp,
1094 radius, exp_ij, exp_jk, exp_ik, cij, cijk, grid);
1095 }
1096}
1097
1098/*******************************************************************************
1099 * \brief Contracts (x, y) multinomials between C_xyz and scratch tensor T_xy.
1100 ******************************************************************************/
1101static inline void __attribute__((always_inline))
1102general_cxyz_to_txy(const int lp,
1103 const double trinom[3][lp + 1][lp + 1][lp + 1],
1104 GRID_CONST_WHEN_COLLOCATE double *restrict cxyz,
1105 GRID_CONST_WHEN_INTEGRATE double *restrict t_xy_raw) {
1106 const int lp1 = lp + 1;
1107 GRID_CONST_WHEN_INTEGRATE double (*restrict const t_xy)[lp1][lp1][lp1] =
1108 (GRID_CONST_WHEN_INTEGRATE double (*)[lp1][lp1][lp1])t_xy_raw;
1109 for (int klx = 0; klx <= lp; klx++) {
1110 for (int jlx = 0; jlx <= lp - klx; jlx++) {
1111 for (int ilx = 0; ilx <= lp - klx - jlx; ilx++) {
1112 const double px = trinom[0][klx][jlx][ilx];
1113 if (px == 0.0)
1114 continue;
1115 const int lx = ilx + jlx + klx;
1116 const int lpy = lp - lx;
1117 for (int kly = 0; kly <= lpy; kly++) {
1118 for (int jly = 0; jly <= lpy - kly; jly++) {
1119 for (int ily = 0; ily <= lpy - kly - jly; ily++) {
1120 const double py = trinom[1][kly][jly][ily];
1121 if (py == 0.0)
1122 continue;
1123 const int ly = ily + jly + kly;
1124 const double pxy = px * py;
1125 const int lpz = lp - lx - ly;
1126 const int cxyz_base = ly * lp1 + lx;
1127 GRID_CONST_WHEN_INTEGRATE double *restrict t_row =
1128 t_xy[klx + kly][jlx + jly][ilx + ily];
1129 for (int lz = 0; lz <= lpz; lz++) {
1130#if (GRID_DO_COLLOCATE)
1131 t_row[lz] += pxy * cxyz[lz * lp1 * lp1 + cxyz_base];
1132#else
1133 cxyz[lz * lp1 * lp1 + cxyz_base] += pxy * t_row[lz];
1134#endif
1135 }
1136 }
1137 }
1138 }
1139 }
1140 }
1141 }
1142}
1143
1144/*******************************************************************************
1145 * \brief Contracts z multinomials between scratch tensor T_xy and C_ijk.
1146 ******************************************************************************/
1147static inline void __attribute__((always_inline))
1148general_txy_to_cijk(const int lp,
1149 const double trinom[3][lp + 1][lp + 1][lp + 1],
1150 GRID_CONST_WHEN_COLLOCATE double *restrict t_xy_raw,
1151 GRID_CONST_WHEN_INTEGRATE double *restrict cijk) {
1152 const int lp1 = lp + 1;
1153 GRID_CONST_WHEN_COLLOCATE double (*restrict const t_xy)[lp1][lp1][lp1] =
1154 (GRID_CONST_WHEN_COLLOCATE double (*)[lp1][lp1][lp1])t_xy_raw;
1155 for (int kl_xy = 0; kl_xy <= lp; kl_xy++) {
1156 for (int jl_xy = 0; jl_xy <= lp - kl_xy; jl_xy++) {
1157 for (int il_xy = 0; il_xy <= lp - kl_xy - jl_xy; il_xy++) {
1158 const int lpz = lp - (il_xy + jl_xy + kl_xy);
1159 GRID_CONST_WHEN_COLLOCATE double *restrict t_row =
1160 t_xy[kl_xy][jl_xy][il_xy];
1161 for (int klz = 0; klz <= lpz; klz++) {
1162 const int kl_base = (kl_xy + klz) * lp1 * lp1;
1163 for (int jlz = 0; jlz <= lpz - klz; jlz++) {
1164 GRID_CONST_WHEN_INTEGRATE double *restrict cijk_row =
1165 &cijk[kl_base + (jl_xy + jlz) * lp1 + il_xy];
1166 for (int ilz = 0; ilz <= lpz - klz - jlz; ilz++) {
1167 const double pz = trinom[2][klz][jlz][ilz];
1168#if (GRID_DO_COLLOCATE)
1169 cijk_row[ilz] += t_row[ilz + jlz + klz] * pz;
1170#else
1171 t_row[ilz + jlz + klz] += cijk_row[ilz] * pz;
1172#endif
1173 }
1174 }
1175 }
1176 }
1177 }
1178 }
1179}
1180
1181/*******************************************************************************
1182 * \brief Transforms coefficients C_xyz into C_ijk.
1183 * \author Ole Schuett
1184 ******************************************************************************/
1185static inline void
1186general_cxyz_to_cijk(const int lp, const double dh[3][3],
1187 GRID_CONST_WHEN_COLLOCATE double *cxyz,
1188 GRID_CONST_WHEN_INTEGRATE double *cijk) {
1189
1190 // transform P_{lxp,lyp,lzp} into a P_{lip,ljp,lkp} such that
1191 // sum_{lxp,lyp,lzp} P_{lxp,lyp,lzp} (x-x_p)**lxp (y-y_p)**lyp (z-z_p)**lzp =
1192 // sum_{lip,ljp,lkp} P_{lip,ljp,lkp} (i-i_p)**lip (j-j_p)**ljp (k-k_p)**lkp
1193
1194 // transform using multinomials
1195 const int lp1 = lp + 1;
1196 double hmatgridp[lp1][3][3];
1197 for (int i = 0; i < 3; i++) {
1198 for (int j = 0; j < 3; j++) {
1199 hmatgridp[0][j][i] = 1.0;
1200 for (int k = 1; k <= lp; k++) {
1201 hmatgridp[k][j][i] = hmatgridp[k - 1][j][i] * dh[j][i];
1202 }
1203 }
1204 }
1205
1206 double trinom[3][lp1][lp1][lp1];
1207 for (int d = 0; d < 3; d++) {
1208 for (int kl = 0; kl <= lp; kl++) {
1209 for (int jl = 0; jl <= lp - kl; jl++) {
1210 for (int il = 0; il <= lp - kl - jl; il++) {
1211 const int l = il + jl + kl;
1212 trinom[d][kl][jl][il] = hmatgridp[il][0][d] * hmatgridp[jl][1][d] *
1213 hmatgridp[kl][2][d] * fac(l) /
1214 (fac(il) * fac(jl) * fac(kl));
1215 }
1216 }
1217 }
1218 }
1219 const double (*trinom_const)[lp1][lp1][lp1] =
1220 (const double (*)[lp1][lp1][lp1])trinom;
1221
1222 // Factor the 6D multinomial sum through T_xy[kl_xy][jl_xy][il_xy][lz] in
1223 // thread-local heap scratch (reused sequentially by general_cijk_to_grid).
1224 const size_t t_xy_bytes = (size_t)lp1 * lp1 * lp1 * lp1 * sizeof(double);
1225 double *const scratch = (double *)grid_library_get_scratch(t_xy_bytes);
1226 memset(scratch, 0, t_xy_bytes);
1227
1228#if (GRID_DO_COLLOCATE)
1229 general_cxyz_to_txy(lp, trinom_const, cxyz, scratch);
1230 general_txy_to_cijk(lp, trinom_const, scratch, cijk);
1231#else
1232 general_txy_to_cijk(lp, trinom_const, scratch, cijk);
1233 general_cxyz_to_txy(lp, trinom_const, cxyz, scratch);
1234#endif
1235}
1236
1237/*******************************************************************************
1238 * \brief Collocates coefficients C_xyz onto the grid for general case.
1239 * \author Ole Schuett
1240 ******************************************************************************/
1241static inline void
1242general_cxyz_to_grid(const int border_mask, const int lp, const double zetp,
1243 const double dh[3][3], const double dh_inv[3][3],
1244 const double rp[3], const int npts_global[3],
1245 const int npts_local[3], const int shift_local[3],
1246 const int border_width[3], const double radius,
1247 GRID_CONST_WHEN_COLLOCATE double *cxyz,
1249
1250 const size_t cijk_size = (lp + 1) * (lp + 1) * (lp + 1);
1251 double cijk[cijk_size];
1252 memset(cijk, 0, cijk_size * sizeof(double));
1253
1254#if (GRID_DO_COLLOCATE)
1255 // collocate
1256 general_cxyz_to_cijk(lp, dh, cxyz, cijk);
1257 general_cijk_to_grid(border_mask, lp, zetp, dh, dh_inv, rp, npts_global,
1258 npts_local, shift_local, border_width, radius, cijk,
1259 grid);
1260#else
1261 // integrate
1262 general_cijk_to_grid(border_mask, lp, zetp, dh, dh_inv, rp, npts_global,
1263 npts_local, shift_local, border_width, radius, cijk,
1264 grid);
1265 general_cxyz_to_cijk(lp, dh, cxyz, cijk);
1266#endif
1267}
1268
1269/*******************************************************************************
1270 * \brief Collocates coefficients C_xyz onto the grid.
1271 * \author Ole Schuett
1272 ******************************************************************************/
1273static inline void
1274cxyz_to_grid(const bool orthorhombic, const int border_mask, const int lp,
1275 const double zetp, const double dh[3][3],
1276 const double dh_inv[3][3], const double rp[3],
1277 const int npts_global[3], const int npts_local[3],
1278 const int shift_local[3], const int border_width[3],
1279 const double radius, GRID_CONST_WHEN_COLLOCATE double *cxyz,
1281
1282 enum grid_library_kernel k;
1283 if (orthorhombic && border_mask == 0) {
1285 ortho_cxyz_to_grid(lp, zetp, dh, dh_inv, rp, npts_global, npts_local,
1286 shift_local, radius, cxyz, grid);
1287 } else {
1289 general_cxyz_to_grid(border_mask, lp, zetp, dh, dh_inv, rp, npts_global,
1290 npts_local, shift_local, border_width, radius, cxyz,
1291 grid);
1292 }
1294}
1295
1296/*******************************************************************************
1297 * \brief Transforms coefficients C_ab into C_xyz.
1298 * \author Ole Schuett
1299 ******************************************************************************/
1300static inline void cab_to_cxyz(const int la_max, const int la_min,
1301 const int lb_max, const int lb_min,
1302 const double prefactor, const double ra[3],
1303 const double rb[3], const double rp[3],
1304 GRID_CONST_WHEN_COLLOCATE double *cab,
1305 GRID_CONST_WHEN_INTEGRATE double *cxyz) {
1306
1307 // Computes the polynomial expansion coefficients:
1308 // (x-a)**lxa (x-b)**lxb -> sum_{ls} alpha(ls,lxa,lxb,1)*(x-p)**ls
1309 const int lp = la_max + lb_max;
1310 const int lp1 = lp + 1;
1311 double alpha[3][lb_max + 1][la_max + 1][lp + 1];
1312
1313 for (int i = 0; i < 3; i++) {
1314 const double drpa = rp[i] - ra[i];
1315 const double drpb = rp[i] - rb[i];
1316 alpha[i][0][0][0] = 1.0;
1317 for (int lxa = 1; lxa <= la_max; lxa++) {
1318 alpha[i][0][lxa][0] = drpa * alpha[i][0][lxa - 1][0];
1319 for (int k = 1; k < lxa; k++) {
1320 alpha[i][0][lxa][k] =
1321 alpha[i][0][lxa - 1][k - 1] + drpa * alpha[i][0][lxa - 1][k];
1322 }
1323 alpha[i][0][lxa][lxa] = 1.0;
1324 }
1325 for (int lxb = 1; lxb <= lb_max; lxb++) {
1326 for (int lxa = 0; lxa <= la_max; lxa++) {
1327 const int n = lxa + lxb;
1328 alpha[i][lxb][lxa][0] = drpb * alpha[i][lxb - 1][lxa][0];
1329 for (int k = 1; k < n; k++) {
1330 alpha[i][lxb][lxa][k] =
1331 alpha[i][lxb - 1][lxa][k - 1] + drpb * alpha[i][lxb - 1][lxa][k];
1332 }
1333 alpha[i][lxb][lxa][n] = 1.0;
1334 }
1335 }
1336 }
1337
1338 // *** initialise the coefficient matrix, we transform the sum
1339 //
1340 // sum_{lxa,lya,lza,lxb,lyb,lzb} P_{lxa,lya,lza,lxb,lyb,lzb} *
1341 // (x-a_x)**lxa (y-a_y)**lya (z-a_z)**lza (x-b_x)**lxb (y-a_y)**lya
1342 // (z-a_z)**lza
1343 //
1344 // into
1345 //
1346 // sum_{lxp,lyp,lzp} P_{lxp,lyp,lzp} (x-p_x)**lxp (y-p_y)**lyp (z-p_z)**lzp
1347 //
1348 // where p is center of the product gaussian, and lp = la_max + lb_max
1349 // (current implementation is l**7)
1350 //
1351 // Factor the 3D transformation (lxa,lya,lza,lxb,lyb,lzb) <-> (lxp,lyp,lzp)
1352 // into three separable 1D steps along x, y, z.
1353 const int ncoset_a = ncoset(la_max);
1354 double txy[lp1 * lp1];
1355 double tx[lp1];
1356
1357#if (GRID_DO_COLLOCATE)
1358 for (int lzb = 0; lzb <= lb_max; lzb++) {
1359 for (int lza = 0; lza <= la_max; lza++) {
1360 const int lz_max = lza + lzb;
1361 const int lyp_max = lp - lz_max;
1362 memset(txy, 0, (lyp_max + 1) * lp1 * sizeof(double));
1363 for (int lyb = 0; lyb <= lb_max - lzb; lyb++) {
1364 for (int lya = 0; lya <= la_max - lza; lya++) {
1365 const int ly_max = lya + lyb;
1366 const int lxb_min = imax(lb_min - lzb - lyb, 0);
1367 const int lxa_min = imax(la_min - lza - lya, 0);
1368 const int lxb_max = lb_max - lzb - lyb;
1369 const int lxa_max = la_max - lza - lya;
1370 const int lxp_max = lxa_max + lxb_max;
1371 memset(tx, 0, (lxp_max + 1) * sizeof(double));
1372 for (int lxb = lxb_min; lxb <= lxb_max; lxb++) {
1373 const int jco = coset(lxb, lyb, lzb);
1374 for (int lxa = lxa_min; lxa <= lxa_max; lxa++) {
1375 const int ico = coset(lxa, lya, lza);
1376 const int cab_index = jco * ncoset_a + ico; // [jco, ico]
1377 const double cab_val = cab[cab_index] * prefactor;
1378 for (int lxp = 0; lxp <= lxa + lxb; lxp++) {
1379 tx[lxp] += cab_val * alpha[0][lxb][lxa][lxp];
1380 }
1381 }
1382 }
1383 for (int lyp = 0; lyp <= ly_max; lyp++) {
1384 const double ay = alpha[1][lyb][lya][lyp];
1385 double *txy_row = &txy[lyp * lp1];
1386 for (int lxp = 0; lxp <= lxp_max; lxp++) {
1387 txy_row[lxp] += tx[lxp] * ay;
1388 }
1389 }
1390 }
1391 }
1392 for (int lzp = 0; lzp <= lz_max; lzp++) {
1393 const double az = alpha[2][lzb][lza][lzp];
1394 double *cxyz_z = &cxyz[lzp * lp1 * lp1];
1395 for (int lyp = 0; lyp <= lyp_max; lyp++) {
1396 const double *txy_row = &txy[lyp * lp1];
1397 double *cxyz_zy = &cxyz_z[lyp * lp1];
1398 for (int lxp = 0; lxp <= lyp_max - lyp; lxp++) {
1399 cxyz_zy[lxp] += txy_row[lxp] * az;
1400 }
1401 }
1402 }
1403 }
1404 }
1405#else
1406 for (int lzb = 0; lzb <= lb_max; lzb++) {
1407 for (int lza = 0; lza <= la_max; lza++) {
1408 const int lz_max = lza + lzb;
1409 const int lyp_max = lp - lz_max;
1410 memset(txy, 0, (lyp_max + 1) * lp1 * sizeof(double));
1411 for (int lzp = 0; lzp <= lz_max; lzp++) {
1412 const double az = alpha[2][lzb][lza][lzp] * prefactor;
1413 const double *cxyz_z = &cxyz[lzp * lp1 * lp1];
1414 for (int lyp = 0; lyp <= lyp_max; lyp++) {
1415 double *txy_row = &txy[lyp * lp1];
1416 const double *cxyz_zy = &cxyz_z[lyp * lp1];
1417 for (int lxp = 0; lxp <= lyp_max - lyp; lxp++) {
1418 txy_row[lxp] += cxyz_zy[lxp] * az;
1419 }
1420 }
1421 }
1422 for (int lyb = 0; lyb <= lb_max - lzb; lyb++) {
1423 for (int lya = 0; lya <= la_max - lza; lya++) {
1424 const int ly_max = lya + lyb;
1425 const int lxb_min = imax(lb_min - lzb - lyb, 0);
1426 const int lxa_min = imax(la_min - lza - lya, 0);
1427 const int lxb_max = lb_max - lzb - lyb;
1428 const int lxa_max = la_max - lza - lya;
1429 const int lxp_max = lxa_max + lxb_max;
1430 memset(tx, 0, (lxp_max + 1) * sizeof(double));
1431 for (int lyp = 0; lyp <= ly_max; lyp++) {
1432 const double ay = alpha[1][lyb][lya][lyp];
1433 const double *txy_row = &txy[lyp * lp1];
1434 for (int lxp = 0; lxp <= lxp_max; lxp++) {
1435 tx[lxp] += txy_row[lxp] * ay;
1436 }
1437 }
1438 for (int lxb = lxb_min; lxb <= lxb_max; lxb++) {
1439 const int jco = coset(lxb, lyb, lzb);
1440 for (int lxa = lxa_min; lxa <= lxa_max; lxa++) {
1441 const int ico = coset(lxa, lya, lza);
1442 const int cab_index = jco * ncoset_a + ico; // [jco, ico]
1443 double sum_x = 0.0;
1444 for (int lxp = 0; lxp <= lxa + lxb; lxp++) {
1445 sum_x += tx[lxp] * alpha[0][lxb][lxa][lxp];
1446 }
1447 cab[cab_index] += sum_x;
1448 }
1449 }
1450 }
1451 }
1452 }
1453 }
1454#endif
1455}
1456
1457/*******************************************************************************
1458 * \brief Collocates coefficients C_ab onto the grid.
1459 * \author Ole Schuett
1460 ******************************************************************************/
1461static inline void
1462cab_to_grid(const bool orthorhombic, const int border_mask, const int la_max,
1463 const int la_min, const int lb_max, const int lb_min,
1464 const double zeta, const double zetb, const double rscale,
1465 const double dh[3][3], const double dh_inv[3][3],
1466 const double ra[3], const double rab[3], const int npts_global[3],
1467 const int npts_local[3], const int shift_local[3],
1468 const int border_width[3], const double radius,
1469 GRID_CONST_WHEN_COLLOCATE double *cab,
1471
1472 // Check if radius is too small to be mapped onto grid of given resolution.
1473 double dh_max = 0.0;
1474 for (int i = 0; i < 3; i++) {
1475 for (int j = 0; j < 3; j++) {
1476 dh_max = fmax(dh_max, fabs(dh[i][j]));
1477 }
1478 }
1479 if (2.0 * radius < dh_max) {
1480 return;
1481 }
1482
1483 const double zetp = zeta + zetb;
1484 const double f = zetb / zetp;
1485 const double rab2 = rab[0] * rab[0] + rab[1] * rab[1] + rab[2] * rab[2];
1486 const double prefactor = rscale * exp(-zeta * f * rab2);
1487 double rp[3], rb[3];
1488 for (int i = 0; i < 3; i++) {
1489 rp[i] = ra[i] + f * rab[i];
1490 rb[i] = ra[i] + rab[i];
1491 }
1492
1493 const int lp = la_max + lb_max;
1494 const size_t cxyz_size = (lp + 1) * (lp + 1) * (lp + 1);
1495 double cxyz[cxyz_size];
1496 memset(cxyz, 0, cxyz_size * sizeof(double));
1497
1498#if (GRID_DO_COLLOCATE)
1499 // collocate
1500 cab_to_cxyz(la_max, la_min, lb_max, lb_min, prefactor, ra, rb, rp, cab, cxyz);
1501 cxyz_to_grid(orthorhombic, border_mask, lp, zetp, dh, dh_inv, rp, npts_global,
1502 npts_local, shift_local, border_width, radius, cxyz, grid);
1503#else
1504 // integrate
1505 cxyz_to_grid(orthorhombic, border_mask, lp, zetp, dh, dh_inv, rp, npts_global,
1506 npts_local, shift_local, border_width, radius, cxyz, grid);
1507 cab_to_cxyz(la_max, la_min, lb_max, lb_min, prefactor, ra, rb, rp, cab, cxyz);
1508#endif
1509}
1510
1511// EOF
static int imax(int x, int y)
Returns the larger of two given integers (missing from the C standard).
static int imin(int x, int y)
Returns the smaller of the two integers (missing from the C standard).
Definition dbm_miniapp.c:37
static GRID_HOST_DEVICE int coset(int lx, int ly, int lz)
Maps three angular momentum components to a single zero based index.
Definition grid_common.h:96
static GRID_HOST_DEVICE int ncoset(const int l)
Number of Cartesian orbitals up to given angular momentum quantum.
Definition grid_common.h:82
#define GRID_PRAGMA_UNROLL_UP_TO(N)
Definition grid_common.h:44
static GRID_HOST_DEVICE int modulo(int a, int m)
Equivalent of Fortran's MODULO, which always return a positive number. https://gcc....
static GRID_HOST_DEVICE double fac(const int i)
Factorial function, e.g. fac(5) = 5! = 120.
Definition grid_common.h:57
static GRID_HOST_DEVICE int idx(const orbital a)
Return coset index of given orbital angular momentum.
#define GRID_PRAGMA_UNROLL(N)
Definition grid_common.h:43
@ GRID_BACKEND_CPU
static void const int const int const int const int const int const double const int const int const int int GRID_CONST_WHEN_COLLOCATE double GRID_CONST_WHEN_INTEGRATE double * grid
#define GRID_CONST_WHEN_COLLOCATE
static void const int const int const double GRID_CONST_WHEN_COLLOCATE double GRID_CONST_WHEN_INTEGRATE double GRID_CONST_WHEN_INTEGRATE double GRID_CONST_WHEN_INTEGRATE double * grid_2
static void const int j1
static void const int const int const int jg1
static void const int const int j2
static void const int const int const double GRID_CONST_WHEN_COLLOCATE double GRID_CONST_WHEN_INTEGRATE double GRID_CONST_WHEN_INTEGRATE double * grid_1
static void const int const int const int const int const int const double const int const int const int int ** sphere_bounds_iter
static void const int const int kg2
static void const int const int const int const int const int const double const int const int sections[3][2 *cmax+1]
static void const int const int i
static void const int const int const int const double GRID_CONST_WHEN_COLLOCATE double * cxy
static void __attribute__((always_inline)) ortho_cx_to_grid_scalar(const int lp
Simple loop body for ortho_cx_to_grid using plain C.
static void const int cmax
static void const int kg1
static void const int const int const double pol[3][lp+1][2 *cmax+1]
const int ub
static void const int const int const int const int const int const double const int const int const int npts_local[3]
#define GRID_MAX_LP_OPTIMIZED
static void const int const int const int const int jg2
static void const int const int const double GRID_CONST_WHEN_COLLOCATE double GRID_CONST_WHEN_INTEGRATE double * grid_0
for(int lxp=0;lxp<=lp;lxp++)
static void const int const int const double GRID_CONST_WHEN_COLLOCATE double GRID_CONST_WHEN_INTEGRATE double GRID_CONST_WHEN_INTEGRATE double GRID_CONST_WHEN_INTEGRATE double GRID_CONST_WHEN_INTEGRATE double * grid_3
static void const int const int const int const int const int const double const int map[3][2 *cmax+1]
#define GRID_CONST_WHEN_INTEGRATE
static void const int const int const double GRID_CONST_WHEN_COLLOCATE double * cx
#define GRID_DO_COLLOCATE
void exp_ij(const double alpha, const int offset_i, const int imin, const int imax, const int offset_j, const int jmin, const int jmax, tensor *exp_ij_)
void * grid_library_get_scratch(const size_t bytes)
Returns a pointer to a thread local scratch buffer with at least the given size. The buffer grows on ...
void grid_library_counter_add(const int lp, const enum grid_backend backend, const enum grid_library_kernel kernel, const int increment)
Adds given increment to counter specified by lp, backend, and kernel.
grid_library_kernel
Various kernels provided by the grid library.
@ GRID_INTEGRATE_GENERAL
@ GRID_COLLOCATE_ORTHO
@ GRID_COLLOCATE_GENERAL
@ GRID_INTEGRATE_ORTHO
static void cxyz_to_grid(const bool orthorhombic, const int border_mask, const int lp, const double zetp, const double dh[3][3], const double dh_inv[3][3], const double rp[3], const int npts_global[3], const int npts_local[3], const int shift_local[3], const int border_width[3], const double radius, GRID_CONST_WHEN_COLLOCATE double *cxyz, GRID_CONST_WHEN_INTEGRATE double *grid)
Collocates coefficients C_xyz onto the grid.
static void cab_to_cxyz(const int la_max, const int la_min, const int lb_max, const int lb_min, const double prefactor, const double ra[3], const double rb[3], const double rp[3], GRID_CONST_WHEN_COLLOCATE double *cab, GRID_CONST_WHEN_INTEGRATE double *cxyz)
Transforms coefficients C_ab into C_xyz.
static void ortho_cxy_to_grid(const int lp, const int k1, const int k2, const int cmax, const double pol[3][lp+1][2 *cmax+1], const int map[3][2 *cmax+1], const double dh[3][3], const double dh_inv[3][3], const double disr_radius, const int npts_local[3], GRID_CONST_WHEN_COLLOCATE double *cxy, GRID_CONST_WHEN_INTEGRATE double *grid)
Collocates coefficients C_xy onto the grid for orthorhombic case.
static void general_cij_to_ci(const int lp, const double dj, GRID_CONST_WHEN_COLLOCATE double *cij, GRID_CONST_WHEN_INTEGRATE double *ci)
Transforms coefficients C_ij into C_i by fixing grid index j.
static void general_cijk_to_cij(const int lp, const double dk, GRID_CONST_WHEN_COLLOCATE double *cijk, GRID_CONST_WHEN_INTEGRATE double *cij)
Transforms coefficients C_ijk into C_ij by fixing grid index k.
static void general_precompute_mapping(const int index_min, const int index_max, const int shift_local, const int npts_global, const int bounds[2], int map[])
Precompute mapping of grid indices for general case.
static void ortho_cxy_to_cx(const int lp, const int j1, const int j2, const int cmax, const double pol[3][lp+1][2 *cmax+1], GRID_CONST_WHEN_COLLOCATE double *cxy, GRID_CONST_WHEN_INTEGRATE double *cx)
Transforms coefficients C_xy into C_x by fixing grid index j.
static void general_cxyz_to_cijk(const int lp, const double dh[3][3], GRID_CONST_WHEN_COLLOCATE double *cxyz, GRID_CONST_WHEN_INTEGRATE double *cijk)
Transforms coefficients C_xyz into C_ijk.
static void general_cij_to_grid(const int lp, const int k, const int kg, const int npts_local[3], const int index_min[3], const int index_max[3], const int map_i[], const int map_j[], const double dh[3][3], const double gp[3], const double radius, const double exp_ij[], const double exp_jk[], const double exp_ki[], GRID_CONST_WHEN_COLLOCATE double *cij, GRID_CONST_WHEN_INTEGRATE double *grid)
Collocates coefficients C_ij onto the grid for general case.
static void general_cijk_to_grid(const int border_mask, const int lp, const double zetp, const double dh[3][3], const double dh_inv[3][3], const double rp[3], const int npts_global[3], const int npts_local[3], const int shift_local[3], const int border_width[3], const double radius, GRID_CONST_WHEN_COLLOCATE double *cijk, GRID_CONST_WHEN_INTEGRATE double *grid)
Collocates coefficients C_ijk onto the grid for general case.
static void cab_to_grid(const bool orthorhombic, const int border_mask, const int la_max, const int la_min, const int lb_max, const int lb_min, const double zeta, const double zetb, const double rscale, const double dh[3][3], const double dh_inv[3][3], const double ra[3], const double rab[3], const int npts_global[3], const int npts_local[3], const int shift_local[3], const int border_width[3], const double radius, GRID_CONST_WHEN_COLLOCATE double *cab, GRID_CONST_WHEN_INTEGRATE double *grid)
Collocates coefficients C_ab onto the grid.
static void general_ci_to_grid(const int lp, const int jg, const int kg, const int ismin, const int ismax, const int npts_local[3], const int index_min[3], const int index_max[3], const int map_i[], const double gp[3], const int k, const int j, const double exp_ij[], const double exp_jk[], const double exp_ki[], GRID_CONST_WHEN_COLLOCATE double *ci, GRID_CONST_WHEN_INTEGRATE double *grid)
Collocates coefficients C_i onto the grid for general case.
static void ortho_cx_to_grid(const int lp, const int k1, const int k2, const int j1, const int j2, const int cmax, const double pol[3][lp+1][2 *cmax+1], const int map[3][2 *cmax+1], const double dh[3][3], const double dh_inv[3][3], const double kremain, const int npts_local[3], GRID_CONST_WHEN_COLLOCATE double *cx, GRID_CONST_WHEN_INTEGRATE double *grid)
Collocates coefficients C_x onto the grid for orthorhombic case.
static void ortho_cxyz_to_cxy(const int lp, const int k1, const int k2, const int cmax, const double pol[3][lp+1][2 *cmax+1], GRID_CONST_WHEN_COLLOCATE double *cxyz, GRID_CONST_WHEN_INTEGRATE double *cxy)
Transforms coefficients C_xyz into C_xz by fixing grid index k.
static void general_cxyz_to_grid(const int border_mask, const int lp, const double zetp, const double dh[3][3], const double dh_inv[3][3], const double rp[3], const int npts_global[3], const int npts_local[3], const int shift_local[3], const int border_width[3], const double radius, GRID_CONST_WHEN_COLLOCATE double *cxyz, GRID_CONST_WHEN_INTEGRATE double *grid)
Collocates coefficients C_xyz onto the grid for general case.
static void ortho_cxyz_to_grid(const int lp, const double zetp, const double dh[3][3], const double dh_inv[3][3], const double rp[3], const int npts_global[3], const int npts_local[3], const int shift_local[3], const double radius, GRID_CONST_WHEN_COLLOCATE double *cxyz, GRID_CONST_WHEN_INTEGRATE double *grid)
Collocates coefficients C_xyz onto the grid for orthorhombic case.
static void general_fill_exp_table(const int idir, const int jdir, const int index_min[3], const int index_max[3], const double zetp, const double dh[3][3], const double gp[3], double exp_table[])
Fill one of the 2D tables that speedup 3D Gaussian (Mathieu's trick).
void grid_sphere_cache_lookup(const double radius, const double dh[3][3], const double dh_inv[3][3], int **sphere_bounds, double *discr_radius)
Lookup the sphere bound from cache and compute them as needed. See grid_sphere_cache....
real(dp), dimension(3) c
real(dp), dimension(3) a
real(dp), dimension(3) d
real(dp), dimension(3) b
integer, parameter, public gaussian