ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
PoissonSolverFFT.hpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2025-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
25
26#include "greens_function.hpp"
27
30#if defined(__CUDACC__)
33#endif
34
35#include <utils/Vector.hpp>
36
37#include <blockforest/communication/UniformBufferedScheme.h>
38#include <domain_decomposition/BlockDataID.h>
39#include <field/AddToStorage.h>
40#include <field/GhostLayerField.h>
41#include <field/communication/PackInfo.h>
42#include <field/vtk/VTKWriter.h>
43#include <stencil/D3Q27.h>
44#include <waLBerlaDefinitions.h>
45#if defined(__CUDACC__)
46#include <gpu/AddGPUFieldToStorage.h>
47#include <gpu/FieldAccessor.h>
48#include <gpu/FieldIndexing.h>
49#include <gpu/GPUField.h>
50#include <gpu/Kernel.h>
51#include <gpu/communication/MemcpyPackInfo.h>
52#include <gpu/communication/UniformGPUScheme.h>
53#endif
54
55#if defined(__clang__)
56#pragma clang diagnostic push
57#pragma clang diagnostic ignored "-Wfloat-conversion"
58#pragma clang diagnostic ignored "-Wimplicit-float-conversion"
59#elif defined(__GNUC__) or defined(__GNUG__)
60#pragma GCC diagnostic push
61#pragma GCC diagnostic ignored "-Wfloat-conversion"
62#endif
63
64#include <heffte.h>
65#include <heffte_backends.h>
66#include <heffte_geometry.h>
67
68#if defined(__clang__)
69#pragma clang diagnostic pop
70#elif defined(__GNUC__) or defined(__GNUG__)
71#pragma GCC diagnostic pop
72#endif
73
74#include <algorithm>
75#include <array>
76#include <complex>
77#include <cstddef>
78#include <functional>
79#include <memory>
80#include <optional>
81#include <ranges>
82#include <string>
83#include <type_traits>
84#include <utility>
85#include <vector>
86
87namespace walberla {
88
89template <typename T, std::size_t N>
91 std::array<T, N> res{};
92 std::ranges::copy(vec, res.begin());
93 return res;
94}
95
96inline int pos_to_linear_index(int x, int y, int z, auto const &dim) {
97 return (z * dim[1] + y) * dim[0] + x;
98}
99
100template <typename FloatType, lbmpy::Arch Architecture>
102private:
103 template <typename T> FloatType FloatType_c(T t) {
104 return numeric_cast<FloatType>(t);
105 }
106
107protected:
108 template <typename FT, lbmpy::Arch AT = lbmpy::Arch::CPU> struct FieldTrait {
109 using ComplexType = std::complex<FloatType>;
110 using PotentialField = field::GhostLayerField<FT, 1u>;
111 template <class Field>
112 using PackInfo = field::communication::PackInfo<Field>;
113 template <class Stencil>
115 blockforest::communication::UniformBufferedScheme<Stencil>;
116 };
117
118#if defined(__CUDACC__)
119 template <typename FT> struct FieldTrait<FT, lbmpy::Arch::GPU> {
120 using ComplexType = std::conditional_t<std::is_same_v<FloatType, float>,
122 using PotentialField = gpu::GPUField<FT>;
123 template <class Field>
124 using PackInfo = gpu::communication::MemcpyPackInfo<Field>;
125 template <class Stencil>
126 using FullCommunicator = gpu::communication::UniformGPUScheme<Stencil>;
127 };
128#endif
129
130public:
134 FieldTrait<FloatType,
136 template <class Field>
137 using PackInfo =
139
140protected:
141 template <typename ComplexType> struct heffte_container {
142#if defined(__CUDACC__)
143 using backend =
144 std::conditional_t<Architecture == lbmpy::Arch::GPU,
145 heffte::backend::cufft, heffte::backend::fftw>;
146#else // __CUDACC__
147 using backend = heffte::backend::fftw;
148#endif // __CUDACC__
149 std::unique_ptr<heffte::box3d<>> box_in;
150 std::unique_ptr<heffte::box3d<>> box_out;
151 std::unique_ptr<heffte::fft3d<backend>> fft;
152 std::unique_ptr<
153 typename heffte::fft3d<backend>::template buffer_container<ComplexType>>
155
156 heffte_container(heffte::plan_options const &options,
157 auto const &grid_range) {
158 auto const offset_vec = Utils::Vector3i({1, 1, 1});
159 auto const order = Utils::Vector3i({0, 1, 2});
160 box_in = std::make_unique<heffte::box3d<>>(
163 box_out = std::make_unique<heffte::box3d<>>(
166 fft = std::make_unique<heffte::fft3d<backend>>(*box_in, *box_out,
168 buffer = std::make_unique<typename heffte::fft3d<
170 fft->size_workspace());
171 }
172 };
173
174private:
175 template <typename FT, lbmpy::Arch AT = lbmpy::Arch::CPU> struct Green {
176 std::vector<FloatType> m_greens;
177 std::vector<FloatType> m_potential;
178 std::vector<ComplexType> m_potential_fourier;
179 };
180
181#if defined(__CUDACC__)
182 template <typename FT> struct Green<FT, lbmpy::Arch::GPU> {
183 using GreenFunctionField = gpu::GPUField<FloatType>;
184 using PotentialFourier = gpu::GPUField<ComplexType>;
185 BlockDataID m_potential_field_id;
188 walberla::gpu::Kernel<void (*)(walberla::gpu::FieldAccessor<ComplexType>,
189 walberla::gpu::FieldAccessor<FloatType>)>
190 kernel_greens = gpu::make_kernel(
192 walberla::gpu::Kernel<void (*)(walberla::gpu::FieldAccessor<FloatType>,
193 walberla::gpu::FieldAccessor<FloatType>)>
194 kernel_move_fields = gpu::make_kernel(move_field<FloatType>);
195 };
196#endif
197
198 BlockDataID m_potential_field_with_ghosts_id;
199
200 Green<FloatType, Architecture> m_green;
201
202 std::unique_ptr<heffte_container<ComplexType>> heffte;
203 std::shared_ptr<FullCommunicator> m_full_communication;
204
205public:
206 ~PoissonSolverFFT() override = default;
207 PoissonSolverFFT(std::shared_ptr<LatticeWalberla> lattice,
208 double permittivity)
209 : PoissonSolver(std::move(lattice), permittivity) {
210 auto blocks = get_lattice().get_blocks();
211#if defined(__CUDACC__)
212 if constexpr (Architecture == lbmpy::Arch::GPU) {
213 using GreenFunctionField =
214 Green<FloatType, lbmpy::Arch::GPU>::GreenFunctionField;
215 using PotentialFourier =
216 Green<FloatType, lbmpy::Arch::GPU>::PotentialFourier;
217 m_green.m_potential_field_id = gpu::addGPUFieldToStorage<PotentialField>(
218 blocks, "potential field", 1u, field::fzyx, 0u, false),
219 m_green.m_greens_function_field_id =
220 gpu::addGPUFieldToStorage<GreenFunctionField>(
221 blocks, "greens function", 1u, field::fzyx, 0u, false),
222 m_green.m_potential_fourier_id =
223 gpu::addGPUFieldToStorage<PotentialFourier>(
224 blocks, "fourier field", 1u, field::fzyx, 0u, false),
225 m_potential_field_with_ghosts_id =
226 gpu::addGPUFieldToStorage<PotentialField>(
227 blocks, "potential field with ghosts", 1u, field::fzyx,
228 get_lattice().get_ghost_layers());
229
232 for (auto &block : *blocks) {
234 m_green.m_greens_function_field_id);
235 auto kernel = gpu::make_kernel(create_greens_function<FloatType>);
236 kernel.addFieldIndexingParam(
237 gpu::FieldIndexing<FloatType>::xyz(*green_field));
238 kernel.addParam(grid_range.first[0]);
239 kernel.addParam(grid_range.first[1]);
240 kernel.addParam(grid_range.first[2]);
241 kernel.addParam(grid_range.second[0]);
242 kernel.addParam(grid_range.second[1]);
243 kernel.addParam(grid_range.second[2]);
244 kernel.addParam(global_dim[0]);
245 kernel.addParam(global_dim[1]);
246 kernel.addParam(global_dim[2]);
247 kernel();
248
250 m_green.m_potential_field_id);
252 m_potential_field_with_ghosts_id);
254 m_green.m_greens_function_field_id);
256 m_green.m_potential_fourier_id);
257
260
261 m_green.kernel_greens = gpu::make_kernel(
263 m_green.kernel_greens.addFieldIndexingParam(
264 gpu::FieldIndexing<ComplexType>::allInner(*fourier));
265 m_green.kernel_greens.addFieldIndexingParam(
266 gpu::FieldIndexing<FloatType>::allInner(*green));
267
268 m_green.kernel_move_fields = gpu::make_kernel(move_field<FloatType>);
269 m_green.kernel_move_fields.addFieldIndexingParam(
270 gpu::FieldIndexing<FloatType>::xyz(*potential_ghosts));
271 m_green.kernel_move_fields.addFieldIndexingParam(
272 gpu::FieldIndexing<FloatType>::xyz(*potential));
273 }
274 }
275#endif // __CUDACC__
276
277 if constexpr (Architecture == lbmpy::Arch::CPU) {
278 m_potential_field_with_ghosts_id = field::addToStorage<PotentialField>(
279 blocks, "potential field with ghosts", FloatType{0}, field::fzyx,
281 }
282
283 m_full_communication = std::make_shared<FullCommunicator>(blocks);
284 m_full_communication->addPackInfo(
285 std::make_shared<PackInfo<PotentialField>>(
286 m_potential_field_with_ghosts_id));
287 }
288
289 [[nodiscard]] bool is_gpu() const noexcept override {
291 }
292
294 return std::is_same_v<FloatType, double>;
295 }
296
297 std::size_t get_potential_field_id() const noexcept override {
298 return static_cast<std::size_t>(m_potential_field_with_ghosts_id);
299 }
300
301 void setup_fft([[maybe_unused]] bool use_gpu_aware) override {
303 heffte::plan_options options = heffte::default_options<
305#if defined(__CUDACC__)
306 if constexpr (Architecture == lbmpy::Arch::GPU) {
307 options.use_reorder = false;
308 options.algorithm = heffte::reshape_algorithm::p2p_plined;
309 options.use_pencils = true;
310 options.use_gpu_aware = use_gpu_aware;
311 }
312#endif
313 heffte =
314 std::make_unique<heffte_container<ComplexType>>(options, grid_range);
315 if constexpr (Architecture == lbmpy::Arch::CPU) {
316 m_green.m_potential = std::vector<FloatType>(heffte->fft->size_inbox());
317 m_green.m_greens = std::vector<FloatType>(heffte->fft->size_outbox());
318 m_green.m_potential_fourier =
319 std::vector<ComplexType>(heffte->fft->size_outbox());
320 auto const dim = grid_range.second - grid_range.first;
322 for (int x = 0; x < dim[0]; x++) {
323 for (int y = 0; y < dim[1]; y++) {
324 for (int z = 0; z < dim[2]; z++) {
325 m_green.m_greens[pos_to_linear_index(x, y, z, dim)] =
327 y + grid_range.first[1],
328 z + grid_range.first[2], global_dim);
329 }
330 }
331 }
332 }
334 if constexpr (Architecture == lbmpy::Arch::CPU) {
335 // make FFT plan
336 heffte->fft->forward(m_green.m_potential.data(),
337 m_green.m_potential_fourier.data(),
338 heffte->buffer->data());
339 }
340#if defined(__CUDACC__)
341 if constexpr (Architecture == lbmpy::Arch::GPU) {
342 using PotentialFourier =
343 Green<FloatType, lbmpy::Arch::GPU>::PotentialFourier;
344 for (auto &block : *get_lattice().get_blocks()) {
346 m_green.m_potential_field_id);
348 m_green.m_potential_fourier_id);
349 FloatType *_data_potential = potential->dataAt(0, 0, 0, 0);
350 ComplexType *_data_fourier = fourier->dataAt(0, 0, 0, 0);
351 // make FFT plan
352 heffte->fft->forward(_data_potential, _data_fourier,
353 heffte->buffer->data());
354 }
355 }
356#endif
357 }
358
359 [[nodiscard]] std::optional<double>
361 bool consider_ghosts = false) override {
363
364 if (not bc or get_potential_field_id() == 0u)
365 return std::nullopt;
366
367 auto const potential_field = bc->block->template getData<PotentialField>(
368 m_potential_field_with_ghosts_id);
369 return {double_c(
371 }
372
373 bool set_node_potential(Utils::Vector3i const &, double) override {
374 throw std::runtime_error("Setting potential is not supported by EKFFT");
375 }
376
377 [[nodiscard]] std::vector<double>
379 Utils::Vector3i const &upper_corner) const override {
380 std::vector<double> out;
381#ifndef NDEBUG
383#endif
384 auto const &lattice = get_lattice();
385 if (auto const ci = get_interval(lattice, lower_corner, upper_corner)) {
386 out = std::vector<double>(ci->numCells());
387 for (auto &block : *lattice.get_blocks()) {
388 auto const block_offset = lattice.get_block_corner(block, true);
389 if (auto const bci = get_block_interval(
391 auto const potential_field = block.template getData<PotentialField>(
392 m_potential_field_with_ghosts_id);
394 assert(values.size() == bci->numCells());
395#ifndef NDEBUG
396 values_size += bci->numCells();
397#endif
398 auto kernel = [&values, &out](unsigned const block_index,
399 unsigned const local_index,
400 Utils::Vector3i const &) {
402 };
403
405 }
406 }
407 assert(values_size == ci->numCells());
408 }
409 return out;
410 }
411
413 std::vector<double> const &) override {
414 throw std::runtime_error("Setting potential is not supported by EKFFT");
415 }
416
417 void solve() override {
418 if constexpr (Architecture == lbmpy::Arch::CPU) {
420 auto dim = grid_range.second - grid_range.first;
421 heffte->fft->forward(m_green.m_potential.data(),
422 m_green.m_potential_fourier.data(),
423 heffte->buffer->data());
424 std::ranges::transform(m_green.m_potential_fourier, m_green.m_greens,
425 m_green.m_potential_fourier.begin(),
426 std::multiplies<>{});
427 heffte->fft->backward(m_green.m_potential_fourier.data(),
428 m_green.m_potential.data(), heffte->buffer->data());
429
430 for (auto &block : *get_lattice().get_blocks()) {
432 m_potential_field_with_ghosts_id);
433 for (int x = 0; x < dim[0]; x++) {
434 for (int y = 0; y < dim[1]; y++) {
435 for (int z = 0; z < dim[2]; z++) {
436 potential_with_ghosts->get(x, y, z) =
437 m_green.m_potential[pos_to_linear_index(x, y, z, dim)];
438 }
439 }
440 }
441 }
442 }
443#if defined(__CUDACC__)
444 if constexpr (Architecture == lbmpy::Arch::GPU) {
445 using PotentialFourier =
446 Green<FloatType, lbmpy::Arch::GPU>::PotentialFourier;
447 for (auto &block : *get_lattice().get_blocks()) {
449 m_green.m_potential_field_id);
451 m_green.m_potential_fourier_id);
452 FloatType *_data_potential = potential->dataAt(0, 0, 0, 0);
453 ComplexType *_data_fourier = fourier->dataAt(0, 0, 0, 0);
454 heffte->fft->forward(_data_potential, _data_fourier,
455 heffte->buffer->data());
456 m_green.kernel_greens();
457 heffte->fft->backward(_data_fourier, _data_potential,
458 heffte->buffer->data());
459 m_green.kernel_move_fields();
460 }
461 }
462#endif
465 }
466
467 void add_charge_to_field(std::size_t id, double valency) override {
468 auto const factor = FloatType_c(valency / get_permittivity());
469 auto const density_id = BlockDataID(id);
470 if constexpr (Architecture == lbmpy::Arch::CPU) {
472 auto dim = grid_range.second - grid_range.first;
473 for (auto &block : *get_lattice().get_blocks()) {
475 for (int x = 0; x < dim[0]; x++) {
476 for (int y = 0; y < dim[1]; y++) {
477 for (int z = 0; z < dim[2]; z++) {
478 m_green.m_potential[pos_to_linear_index(x, y, z, dim)] +=
479 factor * density_field->get(x, y, z);
480 }
481 }
482 }
483 }
484 }
485#if defined(__CUDACC__)
486 if constexpr (Architecture == lbmpy::Arch::GPU) {
487 for (auto &block : *get_lattice().get_blocks()) {
488 auto field = block.template getData<PotentialField>(
489 m_green.m_potential_field_id);
490 auto density_field =
492 add_fields(field, density_field, FloatType_c(factor));
493 }
494 }
495#endif
496 }
497
498 void reset_charge_field() override {
499 if constexpr (Architecture == lbmpy::Arch::CPU) {
501 auto const dim = grid_range.second - grid_range.first;
502 for (int x = 0; x < dim[0]; x++) {
503 for (int i = 0; i < m_green.m_potential_fourier.size(); i++) {
504 m_green.m_potential_fourier[i] *= m_green.m_greens[i];
505 }
506 for (int y = 0; y < dim[1]; y++) {
507 for (int z = 0; z < dim[2]; z++) {
508 m_green.m_potential[pos_to_linear_index(x, y, z, dim)] =
509 FloatType{0};
510 }
511 }
512 }
513 }
514#if defined(__CUDACC__)
515 if constexpr (Architecture == lbmpy::Arch::GPU) {
516 // the FFT-solver re-uses the potential field for the charge
517 for (auto &block : *get_lattice().get_blocks()) {
518 auto field = block.template getData<PotentialField>(
519 m_green.m_potential_field_id);
520 ek::accessor::Scalar::initialize(field, FloatType{0});
521 }
522 }
523#endif
524 }
525
526 void ghost_communication() override { (*m_full_communication)(); }
527
528protected:
529 void integrate_vtk_writers() override {
530 for (auto const &vtk_handle : m_vtk_auto | std::views::values) {
531 if (vtk_handle->enabled) {
532 vtk::writeFiles(vtk_handle->ptr)();
533 vtk_handle->execution_count++;
534 }
535 }
536 }
537
538protected:
539 template <typename VecType, uint_t F_SIZE_ARG, typename OutputType>
540 class VTKWriter : public vtk::BlockCellDataWriter<OutputType, F_SIZE_ARG> {
541 public:
542 VTKWriter(ConstBlockDataID const &block_id, std::string const &id,
543 FloatType unit_conversion)
544 : vtk::BlockCellDataWriter<OutputType, F_SIZE_ARG>(id),
546
547 protected:
548 void configure() override { WALBERLA_ASSERT_NOT_NULLPTR(this->block_); }
549
550 std::size_t get_first_index(cell_idx_t const x, cell_idx_t const y,
551 cell_idx_t const z) {
552 return (static_cast<std::size_t>(x) * m_dims[2] * m_dims[1] +
553 static_cast<std::size_t>(y) * m_dims[2] +
554 static_cast<std::size_t>(z)) *
556 }
557
558 FloatType m_conversion;
561
562 public:
564
565 void set_dims(Vector3<uint_t> dims) { m_dims = dims; }
566 };
567
568 template <typename OutputType = float>
570 : public VTKWriter<std::vector<FloatType>, 1u, OutputType> {
571 public:
572 using Base = VTKWriter<std::vector<FloatType>, 1u, OutputType>;
573 using Base::Base;
574 using Base::evaluate;
575
576 protected:
577 OutputType evaluate(cell_idx_t const x, cell_idx_t const y,
578 cell_idx_t const z, cell_idx_t const) override {
579 WALBERLA_ASSERT(!this->m_content.empty());
580 auto const potential = this->m_content[this->get_first_index(x, y, z)];
582 }
583 };
584
585public:
586 void register_vtk_field_writers(walberla::vtk::VTKOutput &vtk_obj,
588 int flag_observables) override {
589 if (flag_observables & static_cast<int>(EKPoissonOutputVTK::potential)) {
590 auto const unit_conversion = FloatType_c(units.at("potential"));
591 auto const blocks = get_lattice().get_blocks();
594 m_potential_field_with_ghosts_id, "potential", unit_conversion);
595 auto before_function = [this, blocks, potential_writer]() {
596 for (auto &block : *blocks) {
598 m_potential_field_with_ghosts_id);
599 auto const bci = potential_field->xyzSize();
600 potential_writer->set_content(
603 uint_c(bci.xSize()), uint_c(bci.ySize()), uint_c(bci.zSize())));
604 }
605 };
606 vtk_obj.addBeforeFunction(std::move(before_function));
607 vtk_obj.addCellDataWriter(potential_writer);
608 }
609 }
610
611private:
612#if defined(__CUDACC__)
614 gpu::GPUField<FloatType> *field_add, FloatType factor) {
615 auto kernel = gpu::make_kernel(add_fields_with_factor<FloatType>);
616 kernel.addFieldIndexingParam(
617 gpu::FieldIndexing<FloatType>::xyz(*field_out));
618 kernel.addFieldIndexingParam(
619 gpu::FieldIndexing<FloatType>::xyz(*field_add));
620 kernel.addParam(factor);
621 kernel();
622 }
623#endif
624};
625
626} // namespace walberla
Vector implementation and trait types for boost qvm interoperability.
std::map< std::string, std::shared_ptr< VTKHandle > > m_vtk_auto
VTK writers that are executed automatically.
std::unordered_map< std::string, double > units_map
std::pair< Utils::Vector3i, Utils::Vector3i > get_local_grid_range(bool with_halo=false) const
OutputType evaluate(cell_idx_t const x, cell_idx_t const y, cell_idx_t const z, cell_idx_t const) override
std::size_t get_first_index(cell_idx_t const x, cell_idx_t const y, cell_idx_t const z)
void set_dims(Vector3< uint_t > dims)
VTKWriter(ConstBlockDataID const &block_id, std::string const &id, FloatType unit_conversion)
FieldTrait< FloatType, Architecture >::template FullCommunicator< stencil::D3Q27 > FullCommunicator
bool is_gpu() const noexcept override
bool set_node_potential(Utils::Vector3i const &, double) override
bool is_double_precision() const noexcept override
std::vector< double > get_slice_potential(Utils::Vector3i const &lower_corner, Utils::Vector3i const &upper_corner) const override
PoissonSolverFFT(std::shared_ptr< LatticeWalberla > lattice, double permittivity)
void setup_fft(bool use_gpu_aware) override
FieldTrait< FloatType, Architecture >::ComplexType ComplexType
std::optional< double > get_node_potential(Utils::Vector3i const &node, bool consider_ghosts=false) override
void add_charge_to_field(std::size_t id, double valency) override
FieldTrait< FloatType, Architecture >::PotentialField PotentialField
void register_vtk_field_writers(walberla::vtk::VTKOutput &vtk_obj, LatticeModel::units_map const &units, int flag_observables) override
void set_slice_potential(Utils::Vector3i const &, Utils::Vector3i const &, std::vector< double > const &) override
FieldTrait< FloatType, Architecture >::template PackInfo< Field > PackInfo
~PoissonSolverFFT() override=default
std::size_t get_potential_field_id() const noexcept override
LatticeWalberla const & get_lattice() const noexcept override
virtual double get_permittivity() const noexcept
cudaStream_t stream[1]
CUDA streams for parallel computing on CPU and GPU.
static double * block(double *p, std::size_t index, std::size_t size)
Definition elc.cpp:175
VectorXi< 3 > Vector3i
Definition Vector.hpp:201
Definition fft.cpp:76
STL namespace.
void initialize(GhostLayerField< double, 1u > *scalar_field, double const &value)
auto get(GhostLayerField< double, 1u > const *scalar_field, Cell const &cell)
\file PackInfoPdfDoublePrecision.cpp \author pystencils
int pos_to_linear_index(int x, int y, int z, auto const &dim)
auto to_array(Utils::Vector< T, N > const &vec)
void copy_block_buffer(CellInterval const &bci, CellInterval const &ci, Utils::Vector3i const &block_offset, Utils::Vector3i const &lower_corner, auto &&kernel)
Synchronize data between a sliced block and a container.
std::optional< BlockAndCell > get_block_and_cell(::LatticeWalberla const &lattice, signed_integral_vector auto const &node, bool consider_ghost_layers)
std::optional< walberla::cell::CellInterval > get_block_interval(::LatticeWalberla const &lattice, Utils::Vector3i const &lower_corner, Utils::Vector3i const &upper_corner, Utils::Vector3i const &block_offset, IBlock const &block)
std::optional< walberla::cell::CellInterval > get_interval(::LatticeWalberla const &lattice, Utils::Vector3i const &lower_corner, Utils::Vector3i const &upper_corner)
blockforest::communication::UniformBufferedScheme< Stencil > FullCommunicator
field::GhostLayerField< FT, 1u > PotentialField
field::communication::PackInfo< Field > PackInfo
std::unique_ptr< typename heffte::fft3d< backend >::template buffer_container< ComplexType > > buffer
std::unique_ptr< heffte::fft3d< backend > > fft
std::unique_ptr< heffte::box3d<> > box_in
std::unique_ptr< heffte::box3d<> > box_out
heffte_container(heffte::plan_options const &options, auto const &grid_range)