File parafaftplanholder.h
File List > code_source > templat > include > TempLat > fft > external > parafaft > parafaftplanholder.h
Go to the documentation of this file
#ifndef TEMPLAT_FFT_EXTERNAL_PARAFAFT_PARAFAFTPLANHOLDER_H
#define TEMPLAT_FFT_EXTERNAL_PARAFAFT_PARAFAFTPLANHOLDER_H
/* This file is part of TempLat, available at https://cosmolattice.github.io/templat .
Copyright 2021-2026 The TempLat authors, see AUTHORS.md.
Released under the MIT license, see LICENSE.md. */
// File info: Main contributor(s): Adrien Florio, Year: 2026
#ifndef NOFFT
#ifdef HAVE_MPI
#ifdef HAVE_PARAFAFT
#include <parafaft_r2c.hpp>
#endif
#endif
#endif
#include "TempLat/util/exception.h"
#include "TempLat/fft/fftlibraryinterface.h"
#include "TempLat/parallel/mpi/cartesian/mpicartesiangroup.h"
#include "TempLat/lattice/memory/memoryblock.h"
#include <memory>
#include "TempLat/parallel/device.h"
#include "TempLat/parallel/device_iteration.h"
namespace TempLat
{
// Depending on the device, we alias ParaFaFT_Backend to the appropriate backend type. Parafaft supports multiple
// backends (FFTW, cuFFT, HIPFFT), and we select the one based on compile-time definitions set by CMake when detecting
// the device and available libraries. This allows us to write device-agnostic code in the ParafaftPlanHolder, while
// still leveraging the performance benefits of the appropriate backend for the target platform. Each backend is
// itself a class template parameterized on the floating-point precision, so the alias is a template alias.
#ifdef DEVICE_CUDA
template <typename T> using ParaFaFT_Backend = parafaft::CuFFTBackend<T>;
#elif defined(DEVICE_HIP)
template <typename T> using ParaFaFT_Backend = parafaft::HipFFTBackend<T>;
#else
template <typename T> using ParaFaFT_Backend = parafaft::FFTWBackend<T>;
#endif
MakeException(ParafaftPlanHolderException);
MakeException(ParafaftCompiledWithoutSinglePrecisionSupport);
template <typename T, size_t NDim> class ParafaftPlanHolder : public FFTPlanInterface<T, NDim>
{
public:
using Complex = complex<T>;
#ifdef HAVE_MPI
#ifdef HAVE_PARAFAFT
ParafaftPlanHolder(MPICartesianGroup group,
std::shared_ptr<parafaft::ParaFaFT_R2C<NDim, ParaFaFT_Backend<T>>> parafaftObj)
: mGroup(group), mParafaft(parafaftObj)
{
}
#endif
#endif
virtual ~ParafaftPlanHolder()
{
// Parafaft cleans up internally via its destructor
}
virtual void c2r(MemoryBlock<T, NDim> &mBlock) override { execute_c2r(mBlock); }
virtual void r2c(MemoryBlock<T, NDim> &mBlock) override { execute_r2c(mBlock); }
private:
#ifdef HAVE_MPI
#ifdef HAVE_PARAFAFT
// Keep group alive for MPI communicator lifetime
MPICartesianGroup mGroup;
// Shared pointer to parafaft object
std::shared_ptr<parafaft::ParaFaFT_R2C<NDim, ParaFaFT_Backend<T>>> mParafaft;
#endif
#endif
void execute_r2c(MemoryBlock<T, NDim> &mBlock)
{
#ifdef HAVE_MPI
#ifdef HAVE_PARAFAFT
// ParaFaFT reads/writes mBlock.data() through its own (cuFFT/transpose)
// streams, which are not ordered against the Kokkos execution space that
// produced the buffer. Fence before so any pending device writes to the
// buffer are complete before ParaFaFT reads it, and after so ParaFaFT's
// writes are complete before downstream Kokkos work reads them. Without
// this, the async fill and the FFT race (host-serial backend: no-op).
// Mirrors KokkosFFTPlanHolder.
device::iteration::fence();
// Parafaft's forward_in_place accepts padded buffers directly
// Buffer layout: [N0_local][N1_local][2*(N/2+1)] - matches CosmoLattice
mParafaft->forward_in_place(mBlock.data());
device::iteration::fence();
#endif
#endif
}
void execute_c2r(MemoryBlock<T, NDim> &mBlock)
{
#ifdef HAVE_MPI
#ifdef HAVE_PARAFAFT
// See execute_r2c: fence around the ParaFaFT call to order it against the
// Kokkos execution space (no-op on the host-serial backend).
device::iteration::fence();
// Parafaft's backward_in_place accepts padded buffers directly
// Buffer layout: [N0_local][N1_local][2*(N/2+1)] - matches CosmoLattice
mParafaft->backward_in_place(mBlock.data());
device::iteration::fence();
#endif
#endif
}
};
} // namespace TempLat
#endif