32#define ESPRESSO_P3M_GPU_FLOAT
35#ifdef ESPRESSO_P3M_GPU_FLOAT
36#define REAL_TYPE float
37#define FFT_TYPE_COMPLEX cufftComplex
38#define FFT_FORW_FFT cufftExecR2C
39#define FFT_BACK_FFT cufftExecC2R
40#define FFT_PLAN_FORW_FLAG CUFFT_R2C
41#define FFT_PLAN_BACK_FLAG CUFFT_C2R
44#ifdef ESPRESSO_P3M_GPU_REAL_DOUBLE
45#define REAL_TYPE double
46#define FFT_TYPE_COMPLEX cufftDoubleComplex
47#define FFT_FORW_FFT cufftExecD2Z
48#define FFT_BACK_FFT cufftExecZ2D
49#define FFT_PLAN_FORW_FLAG CUFFT_D2Z
50#define FFT_PLAN_BACK_FLAG CUFFT_Z2D
57#include "system/System.hpp"
76#if defined(OMPI_MPI_H) || defined(_MPI_H)
77#error CU-file includes mpi.h! This should not happen!
127 auto const free_device_pointer = [](
auto *&ptr) {
128 if (ptr !=
nullptr) {
146 auto constexpr max_threads_per_block = 1024u;
147 auto const cao3 =
static_cast<unsigned>(Utils::int_pow<3>(cao));
148 auto const parts_per_block = max_threads_per_block / cao3;
149 assert((n_part / parts_per_block) < std::numeric_limits<unsigned>::max());
150 auto n =
static_cast<unsigned int>(n_part / parts_per_block);
151 auto n_blocks = ((n_part % parts_per_block) == 0u) ? std::max(1u, n) : n + 1u;
152 return std::make_pair(parts_per_block,
static_cast<unsigned>(n_blocks));
156 dim3 grid(n_blocks, 1u, 1u);
157 while (grid.x > 65536u) {
159 if ((n_blocks % grid.y) == 0u)
160 grid.x = std::max(1u, n_blocks / grid.y);
162 grid.x = n_blocks / grid.y + 1u;
178 dim3
const block(parts_per_block * cao, cao, cao);
180 auto const data_length = std::size_t(3u) *
181 static_cast<std::size_t
>(parts_per_block) *
182 static_cast<std::size_t
>(cao) *
sizeof(
REAL_TYPE);
183 return std::make_tuple(
block, grid, parts_per_block, data_length);
198 for (
int i = 0; i < 3; ++i) {
199 Leni[i] = 1.0f / p.
box[i];
203 Zaehler[0] = Zaehler[1] = Zaehler[2] = *Nenner = 0.0;
208 S1 = int_pow<2 * cao>(
math::sinc(Meshi[0] * NMX));
211 ((NY > p.
mesh[1] / 2) ? NY - p.
mesh[1] : NY) + p.
mesh[1] * MY);
212 S2 = S1 * int_pow<2 * cao>(
math::sinc(Meshi[1] * NMY));
215 ((NZ > p.
mesh[2] / 2) ? NZ - p.
mesh[2] : NZ) + p.
mesh[2] * MZ);
216 S3 = S2 * int_pow<2 * cao>(
math::sinc(Meshi[2] * NMZ));
218 NM2 = sqr(NMX * Leni[0]) + sqr(NMY * Leni[1]) + sqr(NMZ * Leni[2]);
221 TE = exp(-sqr(std::numbers::pi_v<REAL_TYPE> / (p.
alpha)) * NM2);
223 Zaehler[0] += NMX * zwi * Leni[0];
224 Zaehler[1] += NMY * zwi * Leni[1];
225 Zaehler[2] += NMZ * zwi * Leni[2];
235 const auto NX =
static_cast<int>(blockDim.x * blockIdx.x + threadIdx.x);
236 const auto NY =
static_cast<int>(blockDim.y * blockIdx.y + threadIdx.y);
237 const auto NZ =
static_cast<int>(blockDim.z * blockIdx.z + threadIdx.z);
239 REAL_TYPE Zaehler[3] = {0.0, 0.0, 0.0}, Nenner = 0.0;
243 for (
int i = 0; i < 3; ++i) {
247 if ((NX >= p.
mesh[0]) || (NY >= p.
mesh[1]) || (NZ >= (p.
mesh[2] / 2 + 1)))
250 index = NX * p.
mesh[1] * (p.
mesh[2] / 2 + 1) + NY * (p.
mesh[2] / 2 + 1) + NZ;
252 if (((NX == 0) && (NY == 0) && (NZ == 0)) ||
253 ((NX % (p.
mesh[0] / 2) == 0) && (NY % (p.
mesh[1] / 2) == 0) &&
254 (NZ % (p.
mesh[2] / 2) == 0))) {
257 Aliasing_sums_ik<cao>(p, NX, NY, NZ, Zaehler, &Nenner);
263 zwi = Dnx * Zaehler[0] * Leni[0] + Dny * Zaehler[1] * Leni[1] +
264 Dnz * Zaehler[2] * Leni[2];
265 zwi /= ((sqr(Dnx * Leni[0]) + sqr(Dny * Leni[1]) + sqr(Dnz * Leni[2])) *
267 p.
G_hat[index] =
REAL_TYPE{2} * zwi / std::numbers::pi_v<REAL_TYPE>;
280 return static_cast<unsigned int>(p.
mesh[1] * (p.
mesh[2] / 2 + 1) * i +
281 (p.
mesh[2] / 2 + 1) * j + k);
286 auto const linear_index =
linear_index_k(p,
static_cast<int>(blockIdx.x),
287 static_cast<int>(blockIdx.y),
288 static_cast<int>(threadIdx.x));
290 auto const bidx =
static_cast<int>(blockIdx.x);
291 auto const bidy =
static_cast<int>(blockIdx.y);
292 auto const nx = (bidx > p.
mesh[0] / 2) ? bidx - p.
mesh[0] : bidx;
293 auto const ny = (bidy > p.
mesh[1] / 2) ? bidy - p.
mesh[1] : bidy;
294 auto const nz =
static_cast<int>(threadIdx.x);
298 buf.x =
REAL_TYPE(-2) * std::numbers::pi_v<REAL_TYPE> * meshw.y;
299 buf.y =
REAL_TYPE(+2) * std::numbers::pi_v<REAL_TYPE> * meshw.x;
302 static_cast<decltype(FFT_TYPE_COMPLEX::x)
>(nx) * buf.x / p.
box[0];
304 static_cast<decltype(FFT_TYPE_COMPLEX::x)
>(nx) * buf.y / p.
box[0];
307 static_cast<decltype(FFT_TYPE_COMPLEX::x)
>(ny) * buf.x / p.
box[1];
309 static_cast<decltype(FFT_TYPE_COMPLEX::x)
>(ny) * buf.y / p.
box[1];
312 static_cast<decltype(FFT_TYPE_COMPLEX::x)
>(nz) * buf.x / p.
box[2];
314 static_cast<decltype(FFT_TYPE_COMPLEX::x)
>(nz) * buf.y / p.
box[2];
317__device__
inline int wrap_index(
const int ind,
const int mesh) {
326 auto const linear_index =
linear_index_k(p,
static_cast<int>(blockIdx.x),
327 static_cast<int>(blockIdx.y),
328 static_cast<int>(threadIdx.x));
334template <
int cao,
bool shared>
336 float const *
const __restrict__ part_pos,
337 float const *
const __restrict__ part_q,
338 unsigned int const parts_per_block) {
339 auto const part_in_block = threadIdx.x /
static_cast<unsigned int>(cao);
340 auto const cao_id_x =
341 threadIdx.x - part_in_block *
static_cast<unsigned int>(cao);
344 parts_per_block * (blockIdx.x * gridDim.y + blockIdx.y) + part_in_block;
350 int nmp_x, nmp_y, nmp_z;
354 m_pos[0] = part_pos[3 *
id + 0] * params.
hi[0] - params.
pos_shift;
355 m_pos[1] = part_pos[3 *
id + 1] * params.
hi[1] - params.
pos_shift;
356 m_pos[2] = part_pos[3 *
id + 2] * params.
hi[2] - params.
pos_shift;
358 nmp_x =
static_cast<int>(floorf(m_pos[0] + 0.5f));
359 nmp_y =
static_cast<int>(floorf(m_pos[1] + 0.5f));
360 nmp_z =
static_cast<int>(floorf(m_pos[2] + 0.5f));
362 m_pos[0] -=
static_cast<REAL_TYPE>(nmp_x);
363 m_pos[1] -=
static_cast<REAL_TYPE>(nmp_y);
364 m_pos[2] -=
static_cast<REAL_TYPE>(nmp_z);
366 nmp_x =
wrap_index(nmp_x +
static_cast<int>(cao_id_x), params.
mesh[0]);
367 nmp_y =
wrap_index(nmp_y +
static_cast<int>(threadIdx.y), params.
mesh[1]);
368 nmp_z =
wrap_index(nmp_z +
static_cast<int>(threadIdx.z), params.
mesh[2]);
372 extern __shared__
float weights[];
375 auto const offset =
static_cast<unsigned int>(cao) * part_in_block;
376 if ((threadIdx.y < 3u) && (threadIdx.z == 0u)) {
377 weights[3u * offset + 3u * cao_id_x + threadIdx.y] =
378 Utils::bspline<cao>(
static_cast<int>(cao_id_x), m_pos[threadIdx.y]);
383 auto const c = weights[3u * offset + 3u * cao_id_x] *
384 weights[3u * offset + 3u * threadIdx.y + 1u] *
385 weights[3u * offset + 3u * threadIdx.z + 2u] * part_q[id];
386 atomicAdd(&(charge_mesh[index]),
REAL_TYPE(c));
390 Utils::bspline<cao>(
static_cast<int>(cao_id_x), m_pos[0]) * part_q[id] *
391 Utils::bspline<cao>(
static_cast<int>(threadIdx.y), m_pos[1]) *
392 Utils::bspline<cao>(
static_cast<int>(threadIdx.z), m_pos[2]);
393 atomicAdd(&(charge_mesh[index]),
REAL_TYPE(c));
398 float const *
const __restrict__ part_pos,
399 float const *
const __restrict__ part_q) {
400 auto const cao =
static_cast<unsigned int>(params.
cao);
401 auto const [
block, grid, parts_per_block, data_length] =
404 switch (params.
cao) {
406 (assign_charge_kernel<1, false>)<<<grid,
block, std::size_t(0u),
nullptr>>>(
407 params, part_pos, part_q, parts_per_block);
410 (assign_charge_kernel<2, false>)<<<grid,
block, std::size_t(0u),
nullptr>>>(
411 params, part_pos, part_q, parts_per_block);
414 (assign_charge_kernel<3, true>)<<<grid,
block, data_length,
nullptr>>>(
415 params, part_pos, part_q, parts_per_block);
418 (assign_charge_kernel<4, true>)<<<grid,
block, data_length,
nullptr>>>(
419 params, part_pos, part_q, parts_per_block);
422 (assign_charge_kernel<5, true>)<<<grid,
block, data_length,
nullptr>>>(
423 params, part_pos, part_q, parts_per_block);
426 (assign_charge_kernel<6, true>)<<<grid,
block, data_length,
nullptr>>>(
427 params, part_pos, part_q, parts_per_block);
430 (assign_charge_kernel<7, true>)<<<grid,
block, data_length,
nullptr>>>(
431 params, part_pos, part_q, parts_per_block);
439template <
int cao,
bool shared>
441 float const *
const __restrict__ part_pos,
442 float const *
const __restrict__ part_q,
443 float *
const __restrict__ part_f,
445 unsigned int const parts_per_block) {
446 auto const part_in_block = threadIdx.x /
static_cast<unsigned int>(cao);
447 auto const cao_id_x =
448 threadIdx.x - part_in_block *
static_cast<unsigned int>(cao);
451 parts_per_block * (blockIdx.x * gridDim.y + blockIdx.y) + part_in_block;
452 if (
id >=
static_cast<unsigned>(params.
n_part))
457 int nmp_x, nmp_y, nmp_z;
459 m_pos[0] = part_pos[3u *
id + 0u] * params.
hi[0] - params.
pos_shift;
460 m_pos[1] = part_pos[3u *
id + 1u] * params.
hi[1] - params.
pos_shift;
461 m_pos[2] = part_pos[3u *
id + 2u] * params.
hi[2] - params.
pos_shift;
463 nmp_x =
static_cast<int>(floorf(m_pos[0] +
REAL_TYPE{0.5}));
464 nmp_y =
static_cast<int>(floorf(m_pos[1] +
REAL_TYPE{0.5}));
465 nmp_z =
static_cast<int>(floorf(m_pos[2] +
REAL_TYPE{0.5}));
467 m_pos[0] -=
static_cast<REAL_TYPE>(nmp_x);
468 m_pos[1] -=
static_cast<REAL_TYPE>(nmp_y);
469 m_pos[2] -=
static_cast<REAL_TYPE>(nmp_z);
471 nmp_x =
wrap_index(nmp_x +
static_cast<int>(cao_id_x), params.
mesh[0]);
472 nmp_y =
wrap_index(nmp_y +
static_cast<int>(threadIdx.y), params.
mesh[1]);
473 nmp_z =
wrap_index(nmp_z +
static_cast<int>(threadIdx.z), params.
mesh[2]);
477 extern __shared__
float weights[];
481 auto const offset =
static_cast<unsigned int>(cao) * part_in_block;
482 if ((threadIdx.y < 3u) && (threadIdx.z == 0u)) {
483 weights[3u * offset + 3u * cao_id_x + threadIdx.y] =
484 Utils::bspline<cao>(
static_cast<int>(cao_id_x), m_pos[threadIdx.y]);
489 c *=
REAL_TYPE(weights[3u * offset + 3u * cao_id_x] *
490 weights[3u * offset + 3u * threadIdx.y + 1u] *
491 weights[3u * offset + 3u * threadIdx.z + 2u]);
494 REAL_TYPE(Utils::bspline<cao>(
static_cast<int>(cao_id_x), m_pos[0]) *
495 Utils::bspline<cao>(
static_cast<int>(threadIdx.y), m_pos[1]) *
496 Utils::bspline<cao>(
static_cast<int>(threadIdx.z), m_pos[2]));
503 atomicAdd(&(part_f[3u *
id + 0u]),
float(c * force_mesh_x[index]));
504 atomicAdd(&(part_f[3u *
id + 1u]),
float(c * force_mesh_y[index]));
505 atomicAdd(&(part_f[3u *
id + 2u]),
float(c * force_mesh_z[index]));
509 float const *
const __restrict__ part_pos,
510 float const *
const __restrict__ part_q,
511 float *
const __restrict__ part_f,
513 auto const cao =
static_cast<unsigned int>(params.
cao);
514 auto const [
block, grid, parts_per_block, data_length] =
517 switch (params.
cao) {
519 (assign_forces_kernel<1, false>)<<<grid,
block, std::size_t(0u),
nullptr>>>(
520 params, part_pos, part_q, part_f, prefactor, parts_per_block);
523 (assign_forces_kernel<2, false>)<<<grid,
block, std::size_t(0u),
nullptr>>>(
524 params, part_pos, part_q, part_f, prefactor, parts_per_block);
527 (assign_forces_kernel<3, true>)<<<grid,
block, data_length,
nullptr>>>(
528 params, part_pos, part_q, part_f, prefactor, parts_per_block);
531 (assign_forces_kernel<4, true>)<<<grid,
block, data_length,
nullptr>>>(
532 params, part_pos, part_q, part_f, prefactor, parts_per_block);
535 (assign_forces_kernel<5, true>)<<<grid,
block, data_length,
nullptr>>>(
536 params, part_pos, part_q, part_f, prefactor, parts_per_block);
539 (assign_forces_kernel<6, true>)<<<grid,
block, data_length,
nullptr>>>(
540 params, part_pos, part_q, part_f, prefactor, parts_per_block);
543 (assign_forces_kernel<7, true>)<<<grid,
block, data_length,
nullptr>>>(
544 params, part_pos, part_q, part_f, prefactor, parts_per_block);
565 data = std::make_shared<P3MGpuParams>();
568 auto &p3m_gpu_data = data->p3m_gpu_data;
569 bool do_reinit =
false, mesh_changed =
false;
570 assert(n_part <= std::numeric_limits<unsigned int>::max());
571 p3m_gpu_data.n_part =
static_cast<unsigned>(n_part);
573 if (not data->is_initialized or p3m_gpu_data.alpha != alpha) {
574 p3m_gpu_data.alpha =
static_cast<REAL_TYPE>(alpha);
578 if (not data->is_initialized or p3m_gpu_data.cao != cao) {
579 p3m_gpu_data.cao = cao;
581 p3m_gpu_data.pos_shift =
static_cast<REAL_TYPE>((p3m_gpu_data.cao - 1) / 2);
585 if (not data->is_initialized or mesh !=
Utils::Vector3i(p3m_gpu_data.mesh)) {
586 std::ranges::copy(mesh, p3m_gpu_data.mesh);
591 if (
auto constexpr eps =
592 static_cast<double>(std::numeric_limits<float>::epsilon());
593 not data->is_initialized or
595 std::ranges::copy(box_l, p3m_gpu_data.box);
599 p3m_gpu_data.mesh_z_padded = (mesh[2] / 2 + 1) * 2;
600 p3m_gpu_data.mesh_size = mesh[0] * mesh[1] * p3m_gpu_data.mesh_z_padded;
602 for (
auto i = 0u; i < 3u; ++i) {
604 static_cast<REAL_TYPE>(p3m_gpu_data.mesh[i]) / p3m_gpu_data.box[i];
607 if (data->is_initialized and mesh_changed) {
608 data->free_device_memory();
609 data->is_initialized =
false;
612 if (not data->is_initialized and p3m_gpu_data.mesh_size > 0) {
614 auto const cmesh_size =
615 static_cast<std::size_t
>(p3m_gpu_data.mesh[0]) *
616 static_cast<std::size_t
>(p3m_gpu_data.mesh[1]) *
617 static_cast<std::size_t
>(p3m_gpu_data.mesh[2] / 2 + 1);
619 cuda_safe_mem(cudaMalloc((
void **)&(p3m_gpu_data.charge_mesh), mesh_len));
620 cuda_safe_mem(cudaMalloc((
void **)&(p3m_gpu_data.force_mesh_x), mesh_len));
621 cuda_safe_mem(cudaMalloc((
void **)&(p3m_gpu_data.force_mesh_y), mesh_len));
622 cuda_safe_mem(cudaMalloc((
void **)&(p3m_gpu_data.force_mesh_z), mesh_len));
634 if (cufftPlan3d(&(data->p3m_fft.forw_plan), mesh[0], mesh[1], mesh[2],
636 cufftPlan3d(&(data->p3m_fft.back_plan), mesh[0], mesh[1], mesh[2],
638 throw std::runtime_error(
"Unable to create fft plan");
643 if ((do_reinit or not data->is_initialized) and p3m_gpu_data.mesh_size > 0) {
646 block.x =
static_cast<unsigned>(512 / mesh[0] + 1);
647 block.y =
static_cast<unsigned>(mesh[1]);
649 grid.x =
static_cast<unsigned>(mesh[0]) /
block.x + 1;
650 grid.z =
static_cast<unsigned>(mesh[2]) / 2 + 1;
652 switch (p3m_gpu_data.cao) {
683 if (p3m_gpu_data.mesh_size > 0)
684 data->is_initialized =
true;
691 double prefactor, std::size_t n_part) {
693 p3m_gpu_data.
n_part =
static_cast<unsigned>(n_part);
702 dim3 gridConv(
static_cast<unsigned>(p3m_gpu_data.mesh[0]),
703 static_cast<unsigned>(p3m_gpu_data.mesh[1]), 1u);
704 dim3 threadsConv(
static_cast<unsigned>(p3m_gpu_data.mesh[2] / 2 + 1), 1u, 1u);
711 static_cast<std::size_t
>(p3m_gpu_data.mesh_size) *
720 p3m_gpu_data.charge_mesh) != CUFFT_SUCCESS) {
721 throw std::runtime_error(
"CUFFT error: Forward FFT failed");
739 assign_forces(p3m_gpu_data, positions_device, charges_device, forces_device,
Particle data communication manager for the GPU.
float * get_particle_charges_device() const
float * get_particle_forces_device() const
float * get_particle_positions_device() const
static DEVICE_QUALIFIER constexpr Vector< T, N > broadcast(typename Base::value_type const &value) noexcept
Create a vector that has all entries set to the same value.
static std::shared_ptr< scoped_pause > make_shared_pause_scoped()
Generate a shared handle to temporarily disable any currently active exception trap for the lifetime ...
void cuda_check_errors_exit(const dim3 &block, const dim3 &grid, const char *function, const char *file, unsigned int line)
In case of error during a CUDA operation, print the error message and exit.
static double * block(double *p, std::size_t index, std::size_t size)
#define P3M_BRILLOUIN
P3M: Number of Brillouin zones taken into account in the calculation of the optimal influence functio...
T product(Vector< T, N > const &v)
DEVICE_QUALIFIER constexpr T sqr(T x)
Calculates the SQuaRe of x.
DEVICE_QUALIFIER constexpr T int_pow(T x)
Calculate integer powers.
__device__ auto linear_index_k(P3MGpuData const &p, int i, int j, int k)
__device__ auto linear_index_r(P3MGpuData const &p, int i, int j, int k)
DEVICE_QUALIFIER auto sinc(T x)
Calculate the function .
__device__ static void Aliasing_sums_ik(const P3MGpuData p, int NX, int NY, int NZ, REAL_TYPE *Zaehler, REAL_TYPE *Nenner)
__global__ void assign_charge_kernel(P3MGpuData const params, float const *const __restrict__ part_pos, float const *const __restrict__ part_q, unsigned int const parts_per_block)
void p3m_gpu_add_farfield_force(P3MGpuParams &data, GpuParticleData &gpu, double prefactor, std::size_t n_part)
The long-range part of the P3M algorithm.
__global__ void apply_influence_function(const P3MGpuData p)
void assign_charges(P3MGpuData const ¶ms, float const *const __restrict__ part_pos, float const *const __restrict__ part_q)
#define FFT_PLAN_FORW_FLAG
__global__ void apply_diff_op(const P3MGpuData p)
void p3m_gpu_init(std::shared_ptr< P3MGpuParams > &data, int cao, Utils::Vector3i const &mesh, double alpha, Utils::Vector3d const &box_l, std::size_t n_part)
Initialize the internal data structure of the P3M GPU.
static auto p3m_calc_blocks(unsigned int cao, std::size_t n_part)
void assign_forces(P3MGpuData const ¶ms, float const *const __restrict__ part_pos, float const *const __restrict__ part_q, float *const __restrict__ part_f, REAL_TYPE const prefactor)
__global__ void calculate_influence_function_device(const P3MGpuData p)
static auto p3m_calc_topology(unsigned int cao, std::size_t n_part)
Calculate the topology of a kernel launch.
__device__ int wrap_index(const int ind, const int mesh)
#define FFT_PLAN_BACK_FLAG
dim3 p3m_make_grid(unsigned int n_blocks)
__global__ void assign_forces_kernel(P3MGpuData const params, float const *const __restrict__ part_pos, float const *const __restrict__ part_q, float *const __restrict__ part_f, REAL_TYPE prefactor, unsigned int const parts_per_block)
int mesh[3]
Mesh dimensions.
REAL_TYPE pos_shift
Position shift.
REAL_TYPE * G_hat
Influence Function.
FFT_TYPE_COMPLEX * force_mesh_x
Force meshes.
int mesh_z_padded
Padded size.
int mesh_size
Total number of mesh points (including padding)
int cao
Charge assignment order.
unsigned int n_part
Number of particles.
FFT_TYPE_COMPLEX * force_mesh_z
REAL_TYPE hi[3]
Inverse mesh spacing.
FFT_TYPE_COMPLEX * charge_mesh
Charge mesh.
REAL_TYPE box[3]
Box size.
FFT_TYPE_COMPLEX * force_mesh_y
REAL_TYPE alpha
Ewald parameter.
cufftHandle forw_plan
Forward FFT plan.
cufftHandle back_plan
Backward FFT plan.
void free_device_memory()
#define KERNELCALL(_function, _grid, _block,...)