diff --git a/src/common/communicator.hh b/src/common/communicator.hh index 20f5916..6cb6e84 100644 --- a/src/common/communicator.hh +++ b/src/common/communicator.hh @@ -1,102 +1,103 @@ /** * @file communicator.hh * * @author Lars Pastewka <lars.pastewka@imtek.uni-freiburg.de> * * @date 07 Mar 2018 * * @brief abstraction layer for the distributed memory communicator object * * Copyright © 2017 Till Junge * * µSpectre is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3, or (at * your option) any later version. * * µSpectre is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Emacs; see the file COPYING. If not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef COMMUNICATOR_H #define COMMUNICATOR_H #ifdef WITH_MPI #include <mpi.h> #endif namespace muSpectre { #ifdef WITH_MPI template<typename T> decltype(auto) mpi_type() { }; template<> inline decltype(auto) mpi_type<char>() { return MPI_CHAR; } template<> inline decltype(auto) mpi_type<short>() { return MPI_SHORT; } template<> inline decltype(auto) mpi_type<int>() { return MPI_INT; } template<> inline decltype(auto) mpi_type<long>() { return MPI_LONG; } template<> inline decltype(auto) mpi_type<unsigned char>() { return MPI_UNSIGNED_CHAR; } template<> inline decltype(auto) mpi_type<unsigned short>() { return MPI_UNSIGNED_SHORT; } template<> inline decltype(auto) mpi_type<unsigned int>() { return MPI_UNSIGNED; } template<> inline decltype(auto) mpi_type<unsigned long>() { return MPI_UNSIGNED_LONG; } template<> inline decltype(auto) mpi_type<float>() { return MPI_FLOAT; } template<> inline decltype(auto) mpi_type<double>() { return MPI_DOUBLE; } //! lightweight abstraction for the MPI communicator object class Communicator { public: - Communicator(MPI_Comm comm=MPI_COMM_NULL): comm{comm} {}; + using MPI_Comm_ref = std::remove_pointer_t<MPI_Comm>&; + Communicator(MPI_Comm comm=MPI_COMM_NULL): comm{*comm} {}; ~Communicator() {}; //! get rank of present process int rank() const { - if (comm == MPI_COMM_NULL) return 0; + if (&comm == MPI_COMM_NULL) return 0; int res; - MPI_Comm_rank(this->comm, &res); + MPI_Comm_rank(&this->comm, &res); return res; } //! sum reduction on scalar types template<typename T> T sum(const T &arg) const { - if (comm == MPI_COMM_NULL) return arg; + if (&comm == MPI_COMM_NULL) return arg; T res; - MPI_Allreduce(&arg, &res, 1, mpi_type<T>(), MPI_SUM, this->comm); + MPI_Allreduce(&arg, &res, 1, mpi_type<T>(), MPI_SUM, &this->comm); return res; } - MPI_Comm get_mpi_comm() { return this->comm; } + MPI_Comm get_mpi_comm() { return &this->comm; } private: - MPI_Comm comm; + MPI_Comm_ref comm; }; #else /* WITH_MPI */ //! stub communicator object that doesn't communicate anything class Communicator { public: Communicator() {}; ~Communicator() {}; //! get rank of present process int rank() { return 0; } //! sum reduction on scalar types template<typename T> T sum(const T &arg) { return arg; } }; #endif } #endif /* COMMUNICATOR_H */ diff --git a/src/fft/fftwmpi_engine.cc b/src/fft/fftwmpi_engine.cc index 00e442b..ac554ef 100644 --- a/src/fft/fftwmpi_engine.cc +++ b/src/fft/fftwmpi_engine.cc @@ -1,211 +1,211 @@ /** * @file fftwmpi_engine.cc * * @author Lars Pastewka <lars.pastewka@imtek.uni-freiburg.de> * * @date 06 Mar 2017 * * @brief implements the MPI-parallel fftw engine * * Copyright © 2017 Till Junge * * µSpectre is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3, or (at * your option) any later version. * * µSpectre is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Emacs; see the file COPYING. If not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "common/ccoord_operations.hh" #include "fft/fftwmpi_engine.hh" namespace muSpectre { template <Dim_t DimsS, Dim_t DimM> int FFTWMPIEngine<DimsS, DimM>::nb_engines{0}; template <Dim_t DimS, Dim_t DimM> FFTWMPIEngine<DimS, DimM>::FFTWMPIEngine(Ccoord resolutions, Rcoord lengths, Communicator comm) :Parent{resolutions, lengths, comm}, real_workspace{nullptr} { if (!this->nb_engines) fftw_mpi_init(); this->nb_engines++; std::array<ptrdiff_t, DimS> narr; std::copy(this->domain_resolutions.begin(), this->domain_resolutions.end(), narr.begin()); narr[DimS-1] = this->domain_resolutions[DimS-1]/2+1; ptrdiff_t res_x, loc_x, res_y, loc_y; this->workspace_size = fftw_mpi_local_size_many_transposed(DimS, narr.data(), Field_t::nb_components, FFTW_MPI_DEFAULT_BLOCK, FFTW_MPI_DEFAULT_BLOCK, this->comm.get_mpi_comm(), &res_x, &loc_x, &res_y, &loc_y); this->fourier_resolutions[1] = this->fourier_resolutions[0]; this->fourier_locations[1] = this->fourier_locations[0]; this->resolutions[0] = res_x; this->locations[0] = loc_x; this->fourier_resolutions[0] = res_y; this->fourier_locations[0] = loc_y; for (auto && pixel: CcoordOps::Pixels<DimS, true>(this->fourier_resolutions, this->fourier_locations)) { this->work_space_container.add_pixel(pixel); } } /* ---------------------------------------------------------------------- */ template <Dim_t DimS, Dim_t DimM> void FFTWMPIEngine<DimS, DimM>::initialise(FFT_PlanFlags plan_flags) { if (this->initialised) { throw std::runtime_error("double initialisation, will leak memory"); } // Initialize parent after local resolutions have been determined and - // work space has been initialized + // work space has been initialized Parent::initialise(plan_flags); this->real_workspace = fftw_alloc_real(2*this->workspace_size); // We need to check whether the workspace provided by our field is large // enough. MPI parallel FFTW may request a workspace size larger than the // nominal size of the complex buffer. if (long(this->work.size()*Field_t::nb_components) < this->workspace_size) { this->work.set_pad_size(this->workspace_size - Field_t::nb_components*this->work.size()); } - + unsigned int flags; switch (plan_flags) { case FFT_PlanFlags::estimate: { flags = FFTW_ESTIMATE; break; } case FFT_PlanFlags::measure: { flags = FFTW_MEASURE; break; } case FFT_PlanFlags::patient: { flags = FFTW_PATIENT; break; } default: throw std::runtime_error("unknown planner flag type"); break; } std::array<ptrdiff_t, DimS> narr; std::copy(this->domain_resolutions.begin(), this->domain_resolutions.end(), narr.begin()); Real * in = this->real_workspace; fftw_complex * out = reinterpret_cast<fftw_complex*>(this->work.data()); this->plan_fft = fftw_mpi_plan_many_dft_r2c( DimS, narr.data(), Field_t::nb_components, FFTW_MPI_DEFAULT_BLOCK, FFTW_MPI_DEFAULT_BLOCK, in, out, this->comm.get_mpi_comm(), FFTW_MPI_TRANSPOSED_OUT | flags); if (this->plan_fft == nullptr) { throw std::runtime_error("r2c plan failed"); } fftw_mpi_execute_dft_r2c(this->plan_fft, in, out); fftw_complex * i_in = reinterpret_cast<fftw_complex*>(this->work.data()); Real * i_out = this->real_workspace; this->plan_ifft = fftw_mpi_plan_many_dft_c2r( DimS, narr.data(), Field_t::nb_components, FFTW_MPI_DEFAULT_BLOCK, FFTW_MPI_DEFAULT_BLOCK, i_in, i_out, this->comm.get_mpi_comm(), FFTW_MPI_TRANSPOSED_IN | flags); if (this->plan_ifft == nullptr) { throw std::runtime_error("c2r plan failed"); } this->initialised = true; } /* ---------------------------------------------------------------------- */ template <Dim_t DimS, Dim_t DimM> FFTWMPIEngine<DimS, DimM>::~FFTWMPIEngine<DimS, DimM>() noexcept { if (this->real_workspace != nullptr) fftw_free(this->real_workspace); if (this->plan_fft != nullptr) fftw_destroy_plan(this->plan_fft); if (this->plan_ifft != nullptr) fftw_destroy_plan(this->plan_ifft); this->nb_engines--; if (!this->nb_engines) fftw_mpi_cleanup(); } /* ---------------------------------------------------------------------- */ template <Dim_t DimS, Dim_t DimM> typename FFTWMPIEngine<DimS, DimM>::Workspace_t & FFTWMPIEngine<DimS, DimM>::fft (Field_t & field) { if (this->plan_fft == nullptr) { throw std::runtime_error("fft plan not initialised"); } if (field.size() != CcoordOps::get_size(this->resolutions)) { throw std::runtime_error("size mismatch"); } // Copy non-padded field to padded real_workspace. // Transposed output of M x N x L transform for >= 3 dimensions is padded // M x N x 2*(L/2+1). ptrdiff_t fstride = Field_t::nb_components*this->resolutions[DimS-1]; ptrdiff_t wstride = Field_t::nb_components*2*(this->resolutions[DimS-1]/2+1); ptrdiff_t n = field.size()/this->resolutions[DimS-1]; - + auto fdata = field.data(); auto wdata = this->real_workspace; for (int i = 0; i < n; i++) { std::copy(fdata, fdata+fstride, wdata); fdata += fstride; wdata += wstride; } // Compute FFT fftw_mpi_execute_dft_r2c( this->plan_fft, this->real_workspace, reinterpret_cast<fftw_complex*>(this->work.data())); return this->work; } /* ---------------------------------------------------------------------- */ template <Dim_t DimS, Dim_t DimM> void FFTWMPIEngine<DimS, DimM>::ifft (Field_t & field) const { if (this->plan_ifft == nullptr) { throw std::runtime_error("ifft plan not initialised"); } if (field.size() != CcoordOps::get_size(this->resolutions)) { throw std::runtime_error("size mismatch"); } // Compute inverse FFT fftw_mpi_execute_dft_c2r( this->plan_ifft, reinterpret_cast<fftw_complex*>(this->work.data()), this->real_workspace); // Copy non-padded field to padded real_workspace. // Transposed output of M x N x L transform for >= 3 dimensions is padded // M x N x 2*(L/2+1). ptrdiff_t fstride = Field_t::nb_components*this->resolutions[DimS-1]; ptrdiff_t wstride = Field_t::nb_components*2*(this->resolutions[DimS-1]/2+1); ptrdiff_t n = field.size()/this->resolutions[DimS-1]; - + auto fdata = field.data(); auto wdata = this->real_workspace; for (int i = 0; i < n; i++) { std::copy(wdata, wdata+fstride, fdata); fdata += fstride; wdata += wstride; } } template class FFTWMPIEngine<twoD, twoD>; template class FFTWMPIEngine<twoD, threeD>; template class FFTWMPIEngine<threeD, threeD>; } // muSpectre diff --git a/src/fft/fftwmpi_engine.hh b/src/fft/fftwmpi_engine.hh index 6fcfa1b..be160f9 100644 --- a/src/fft/fftwmpi_engine.hh +++ b/src/fft/fftwmpi_engine.hh @@ -1,95 +1,95 @@ /** * @file fftwmpi_engine.hh * * @author Lars Pastewka <lars.pastewka@imtek.uni-freiburg.de> * * @date 06 Mar 2017 * * @brief FFT engine using MPI-parallel FFTW * * Copyright © 2017 Till Junge * * µSpectre is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3, or (at * your option) any later version. * * µSpectre is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Emacs; see the file COPYING. If not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef FFTWMPI_ENGINE_H #define FFTWMPI_ENGINE_H #include "fft/fft_engine_base.hh" #include <fftw3-mpi.h> namespace muSpectre { /** * implements the `muSpectre::FFTEngineBase` interface using the * FFTW library */ template <Dim_t DimS, Dim_t DimM> class FFTWMPIEngine: public FFTEngineBase<DimS, DimM> { public: using Parent = FFTEngineBase<DimS, DimM>; //!< base class using Ccoord = typename Parent::Ccoord; //!< cell coordinates type using Rcoord = typename Parent::Rcoord; //!< spatial coordinates type //! field for Fourier transform of second-order tensor using Workspace_t = typename Parent::Workspace_t; //! real-valued second-order tensor using Field_t = typename Parent::Field_t; //! Default constructor FFTWMPIEngine() = delete; //! Constructor with system resolutions FFTWMPIEngine(Ccoord resolutions, Rcoord lengths, Communicator comm=Communicator()); //! Copy constructor FFTWMPIEngine(const FFTWMPIEngine &other) = delete; //! Move constructor FFTWMPIEngine(FFTWMPIEngine &&other) = default; //! Destructor virtual ~FFTWMPIEngine() noexcept; //! Copy assignment operator FFTWMPIEngine& operator=(const FFTWMPIEngine &other) = delete; //! Move assignment operator FFTWMPIEngine& operator=(FFTWMPIEngine &&other) = default; // compute the plan, etc virtual void initialise(FFT_PlanFlags plan_flags) override; //! forward transform virtual Workspace_t & fft(Field_t & field) override; //! inverse transform virtual void ifft(Field_t & field) const override; protected: static int nb_engines; fftw_plan plan_fft{}; //!< holds the plan for forward fourier transform fftw_plan plan_ifft{}; //!< holds the plan for inverse fourier transform - ptrdiff_t workspace_size; + ptrdiff_t workspace_size{}; Real *real_workspace; bool initialised{false}; //!< to prevent double initialisation private: }; } // muSpectre #endif /* FFTWMPI_ENGINE_H */