diff --git a/include/grid_ghosts.hh b/include/grid_ghosts.hh new file mode 100644 index 0000000..be31ede --- /dev/null +++ b/include/grid_ghosts.hh @@ -0,0 +1,209 @@ +// This file is part of monofonIC (MUSIC2) +// A software package to generate ICs for cosmological simulations +// Copyright (C) 2022 by Oliver Hahn +// +// monofonIC 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 of the License, or +// (at your option) any later version. +// +// monofonIC 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 this program. If not, see . +#pragma once + +#include +#include + +#include + +#include +#include + +template +struct grid_with_ghosts +{ + using data_t = typename grid_t::data_t; + using vec3 = std::array; + + static constexpr bool is_distributed_trait = grid_t::is_distributed_trait; + static constexpr int num_ghosts = numghosts; + static constexpr bool have_left = haveleft, have_right = haveright; + + std::vector boundary_left_, boundary_right_; + std::vector local0starts_; + const grid_t &gridref; + size_t nx_, ny_, nz_, nzp_; + + + //... determine communication offsets + std::vector offsets_, sizes_; + + int get_task(ptrdiff_t index) const + { + int itask = 0; + while (itask < MPI::get_size() - 1 && offsets_[itask + 1] <= index) + ++itask; + return itask; + } + + explicit grid_with_ghosts(const grid_t &g) + : gridref(g), nx_(g.n_[0]), ny_(g.n_[1]), nz_(g.n_[2]), nzp_(g.n_[2]+2) + { + if (is_distributed_trait) + { + int ntasks(MPI::get_size()); + + offsets_.assign(ntasks+1, 0); + sizes_.assign(ntasks, 0); + + MPI_Allgather(&g.local_0_size_, 1, MPI_LONG_LONG, &sizes_[0], 1, + MPI_LONG_LONG, MPI_COMM_WORLD); + MPI_Allgather(&g.local_0_start_, 1, MPI_LONG_LONG, &offsets_[0], 1, + MPI_LONG_LONG, MPI_COMM_WORLD); + + for( int i=0; i< CONFIG::MPI_task_size; i++ ){ + if( offsets_[i+1] < offsets_[i] + sizes_[i] ) offsets_[i+1] = offsets_[i] + sizes_[i]; + } + + update_ghosts_allow_multiple( g ); + } + } + + void update_ghosts_allow_multiple( const grid_t &g ) + { + #if defined(USE_MPI) + //... exchange boundary + if( have_left ) boundary_left_.assign(num_ghosts * ny_ * nzp_, data_t{0.0}); + if( have_right ) boundary_right_.assign(num_ghosts * ny_ * nzp_, data_t{0.0}); + + size_t slicesz = ny_ * nzp_; + + MPI_Status status; + std::vector req; + MPI_Request temp_req; + + if( have_right ){ + for( int itask=0; itask= g.local_0_start_ && iglobal_request < g.local_0_start_ + g.local_0_size_ ){ + size_t ii = iglobal_request - g.local_0_start_; + MPI_Isend( &g.relem(ii*slicesz), slicesz, MPI::get_datatype(), itask, iglobal_request, MPI_COMM_WORLD, &temp_req); + req.push_back(temp_req); + } + } + } + + //--- receive data ------------------------------------------------------------ + #pragma omp parallel if(CONFIG::MPI_threads_ok) + { + MPI_Status status; + + #pragma omp for + for( size_t i=0; i(), recvfrom, (int)iglobal_request, MPI_COMM_WORLD, &status); + assert(status.MPI_ERROR == MPI_SUCCESS); + } + } + } + } + + MPI_Barrier( MPI_COMM_WORLD ); + + if( have_left ){ + for( int itask=0; itask= g.local_0_start_ && iglobal_request < g.local_0_start_ + g.local_0_size_ ){ + size_t ii = iglobal_request - g.local_0_start_; + MPI_Isend( &g.relem(ii*slicesz), slicesz, MPI::get_datatype(), itask, iglobal_request, MPI_COMM_WORLD, &temp_req); + req.push_back(temp_req); + } + } + } + + //--- receive data ------------------------------------------------------------ + #pragma omp parallel if(CONFIG::MPI_threads_ok) + { + MPI_Status status; + + #pragma omp for + for( size_t i=0; i(), recvfrom, (int)iglobal_request, MPI_COMM_WORLD, &status); + assert(status.MPI_ERROR == MPI_SUCCESS); + } + } + } + } + + MPI_Barrier( MPI_COMM_WORLD ); + + for (size_t i = 0; i < req.size(); ++i) + { + // need to set status as wait does not necessarily modify it + // c.f. http://www.open-mpi.org/community/lists/devel/2007/04/1402.php + status.MPI_ERROR = MPI_SUCCESS; + // std::cout << "task " << CONFIG::MPI_task_rank << " : checking request No" << i << std::endl; + int flag(1); + MPI_Test(&req[i], &flag, &status); + if( !flag ){ + std::cout << "task " << CONFIG::MPI_task_rank << " : request No" << i << " unsuccessful" << std::endl; + } + + MPI_Wait(&req[i], &status); + // std::cout << "---> ok!" << std::endl; + assert(status.MPI_ERROR == MPI_SUCCESS); + } + + MPI_Barrier(MPI_COMM_WORLD); + + #endif + } + + data_t relem(const ptrdiff_t& i, const ptrdiff_t& j, const ptrdiff_t&k ) const noexcept + { + return this->relem({i,j,k}); + } + + data_t relem(const std::array &pos) const noexcept + { + const ptrdiff_t ix = pos[0]; + const ptrdiff_t iy = (pos[1]+gridref.n_[1])%gridref.n_[1]; + const ptrdiff_t iz = (pos[2]+gridref.n_[2])%gridref.n_[2]; + + if( is_distributed_trait ){ + const ptrdiff_t localix = ix; + if( localix < 0 ){ + return boundary_left_[((localix+num_ghosts)*ny_+iy)*nzp_+iz]; + }else if( localix >= gridref.local_0_size_ ){ + return boundary_right_[((localix-gridref.local_0_size_)*ny_+iy)*nzp_+iz]; + }else{ + return gridref.relem(localix, iy, iz); + } + } + + return gridref.relem((ix+gridref.n_[0])%gridref.n_[0], iy, iz); + } +}; +