ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
P3MFFT.hpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2024-2026 The ESPResSo project
3 *
4 * This file is part of ESPResSo.
5 *
6 * ESPResSo is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * ESPResSo is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#pragma once
21
22#include "communication.hpp"
23
25
26#include <utils/Vector.hpp>
27#include <utils/index.hpp>
28
29#include <boost/mpi/communicator.hpp>
30
31#include <heffte.h>
32#include <heffte_backends.h>
33
34#include <algorithm>
35#include <array>
36#include <initializer_list>
37#include <memory>
38
39#if defined(__CUDACC__)
40#include "cuda/utils.cuh"
41#endif
42
43/**
44 * @brief FFT manager.
45 */
46template <typename FloatType, Arch Architecture, class FFTConfig> class P3MFFT {
47public:
48 using OutputType = typename heffte::fft_output<FloatType>::type;
49 using backend =
50 std::conditional_t<Architecture == Arch::CPU, heffte::backend::fftw,
51 heffte::backend::cufft>;
52 template <class T = OutputType>
53 using buffer_container = heffte::fft3d<backend>::template buffer_container<T>;
54
55private:
56 using FFT3D =
57 std::conditional_t<FFTConfig::use_r2c, heffte::fft3d_r2c<backend>,
58 heffte::fft3d<backend>>;
59 using Box = heffte::box3d<>;
60 using stream_type =
61 heffte::backend::device_instance<heffte::tag::gpu>::stream_type;
62
63 /* input box */
64 std::unique_ptr<Box> in_box;
65 /* output box */
66 std::unique_ptr<Box> out_box;
67 /* workspace for the FFT */
69 /* FFT backend */
70 std::unique_ptr<FFT3D> fft3d;
71 std::shared_ptr<boost::mpi::environment> m_mpi_env_lock;
72
73 template <typename T, std::size_t N>
74 static auto to_array(Utils::Vector<T, N> const &vec) {
75 std::array<T, N> res{};
76 std::ranges::copy(vec, res.begin());
77 return res;
78 }
79
80public:
82 fft3d.reset();
83 m_mpi_env_lock.reset();
84 }
85 P3MFFT(stream_type gpu_stream, boost::mpi::communicator comm,
89 Utils::Vector3i const &node_grid) {
90 auto constexpr row_major_order = std::array<int, 3>{2, 1, 0};
91 auto constexpr col_major_order = std::array<int, 3>{0, 1, 2};
92 auto constexpr in_box_order =
93 (FFTConfig::r_space_order == Utils::MemoryOrder::ROW_MAJOR)
96 auto constexpr out_box_order =
97 (FFTConfig::k_space_order == Utils::MemoryOrder::ROW_MAJOR)
100 auto const n_procs = Utils::product(node_grid);
101 auto const high = to_array(global_mesh - Utils::Vector3i::broadcast(1));
102 auto const global_out_box_full = Box({0, 0, 0}, high, out_box_order);
103 auto const global_out_box =
104 FFTConfig::use_r2c ? global_out_box_full.r2c(FFTConfig::r2c_dir)
106 auto best_grid = node_grid;
107 for (auto i : {0u, 1u, 2u}) {
108 if (global_mesh[i] % (2 * n_procs) == 0) {
109 best_grid = {n_procs, 1, 1};
110 break;
111 }
112 }
113 // use optimal output box decomposition based on prime factors
114 auto out_boxes = heffte::split_world(global_out_box, to_array(best_grid));
115 out_box = std::make_unique<Box>(out_boxes[comm.rank()]);
116
117 in_box = std::make_unique<Box>(
118 to_array(rs_local_ld_index),
121
122 // at this stage we can manually adjust some HeFFTe options
123 heffte::plan_options options = heffte::default_options<backend>();
124
125 // use strided 1-D FFT operations
126 // some backends work just as well when the entries of the data are not
127 // contiguous then there is no need to reorder the data in the intermediate
128 // stages which saves time
129 options.use_reorder = true;
130
131 // use point-to-point communications
132 // collaborative all-to-all and individual point-to-point communications are
133 // two alternatives one may be better than the other depending on the
134 // version of MPI, the hardware interconnect, and the problem size
135 options.algorithm = heffte::reshape_algorithm::p2p_plined;
136
137 // in the intermediate steps, the data can be shapes as either 2-D slabs or
138 // 1-D pencils for sufficiently large problem, it is expected that the
139 // pencil decomposition is better but for smaller problems, the slabs may
140 // perform better (depending on hardware and backend)
141 options.use_pencils = true;
142#if defined(__CUDACC__)
143 if constexpr (Architecture == Arch::CUDA) {
144 options.use_gpu_aware = ::communication_environment->is_mpi_gpu_aware();
145 }
146#endif
147#ifdef ESPRESSO_FPE
148 // cuFFT builds device kernels using CUDA-JIT
149 // (https://docs.nvidia.com/cuda/archive/13.1.1/cufft/#plan-initialization-time)
150 // but this operation is not guaranteed to succeed for all mesh sizes,
151 // and in rare cases, it can send the SIGFPE signal
152 auto const trap_pause = (Architecture == Arch::CUDA)
154 : nullptr;
155#endif
156
157 if constexpr (FFTConfig::use_r2c) {
158 fft3d = std::make_unique<FFT3D>(gpu_stream, *in_box, *out_box,
159 FFTConfig::r2c_dir, comm, options);
160 } else {
161 fft3d =
162 std::make_unique<FFT3D>(gpu_stream, *in_box, *out_box, comm, options);
163 }
164 m_workspace = decltype(m_workspace)(fft3d->size_workspace());
165 // MPI communicator is needed to destroy the FFT plans
166 m_mpi_env_lock = ::communication_environment->get_mpi_env();
167 }
168
170 return Utils::Vector3i(out_box->low);
171 }
173 return Utils::Vector3i(out_box->high) + Utils::Vector3i::broadcast(1);
174 }
179 return Utils::Vector3i(in_box->high) + Utils::Vector3i::broadcast(1) -
180 Utils::Vector3i(in_box->low);
181 }
182 void forward(auto &&in, auto &&out) {
183 fft3d->forward(in, out, m_workspace.data());
184 }
185 void backward(auto &&in, auto &&out) {
186 fft3d->backward(in, out, m_workspace.data());
187 }
188};
Vector implementation and trait types for boost qvm interoperability.
FFT manager.
Definition P3MFFT.hpp:46
void backward(auto &&in, auto &&out)
Definition P3MFFT.hpp:185
Utils::Vector3i ks_local_size() const
Definition P3MFFT.hpp:175
void forward(auto &&in, auto &&out)
Definition P3MFFT.hpp:182
Utils::Vector3i ks_local_ur_index() const
Definition P3MFFT.hpp:172
Utils::Vector3i rs_local_size() const
Definition P3MFFT.hpp:178
~P3MFFT()
Definition P3MFFT.hpp:81
typename heffte::fft_output< FloatType >::type OutputType
Definition P3MFFT.hpp:48
heffte::fft3d< backend >::template buffer_container< T > buffer_container
Definition P3MFFT.hpp:53
P3MFFT(stream_type gpu_stream, boost::mpi::communicator comm, Utils::Vector3i const &global_mesh, Utils::Vector3i const &rs_local_ld_index, Utils::Vector3i const &rs_local_ur_index, Utils::Vector3i const &node_grid)
Definition P3MFFT.hpp:85
std::conditional_t< Architecture==Arch::CPU, heffte::backend::fftw, heffte::backend::cufft > backend
Definition P3MFFT.hpp:51
Utils::Vector3i ks_local_ld_index() const
Definition P3MFFT.hpp:169
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.
Definition Vector.hpp:131
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 ...
Definition fe_trap.cpp:144
cudaStream_t stream[1]
CUDA streams for parallel computing on CPU and GPU.
std::unique_ptr< CommunicationEnvironment > communication_environment
T product(Vector< T, N > const &v)
Definition Vector.hpp:380
VectorXi< 3 > Vector3i
Definition Vector.hpp:201