// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- // vi: set et ts=4 sw=4 sts=4: /* This file is part of the Open Porous Media project (OPM). OPM 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 2 of the License, or (at your option) any later version. OPM 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 OPM. If not, see . Consult the COPYING file in the top-level source directory of this module for the precise wording of the license and the list of copyright holders. */ /*! * \file * \copydoc Opm::Linear::OverlappingBlockVector */ #ifndef EWOMS_OVERLAPPING_BLOCK_VECTOR_HH #define EWOMS_OVERLAPPING_BLOCK_VECTOR_HH #include "overlaptypes.hh" #include #include #include #include #include #include #include namespace Opm { namespace Linear { /*! * \brief An overlap aware block vector. */ template class OverlappingBlockVector : public Dune::BlockVector { typedef Dune::BlockVector ParentType; typedef Dune::BlockVector BlockVector; public: /*! * \brief Given a domestic overlap object, create an overlapping * block vector coherent to it. */ OverlappingBlockVector(const Overlap& overlap) : ParentType(overlap.numDomestic()), overlap_(&overlap) { createBuffers_(); } /*! * \brief Copy constructor. */ OverlappingBlockVector(const OverlappingBlockVector& obv) : ParentType(obv) , numIndicesSendBuff_(obv.numIndicesSendBuff_) , indicesSendBuff_(obv.indicesSendBuff_) , indicesRecvBuff_(obv.indicesRecvBuff_) , valuesSendBuff_(obv.valuesSendBuff_) , valuesRecvBuff_(obv.valuesRecvBuff_) , overlap_(obv.overlap_) {} /*! * \brief Default constructor. */ OverlappingBlockVector() {} //! \cond SKIP /*! * \brief Recycle the assignment operators of Dune::BlockVector */ using ParentType::operator=; //! \endcond /*! * \brief Assignment operator. */ OverlappingBlockVector& operator=(const OverlappingBlockVector& obv) { ParentType::operator=(obv); numIndicesSendBuff_ = obv.numIndicesSendBuff_; indicesSendBuff_ = obv.indicesSendBuff_; indicesRecvBuff_ = obv.indicesRecvBuff_; valuesSendBuff_ = obv.valuesSendBuff_; valuesRecvBuff_ = obv.valuesRecvBuff_; overlap_ = obv.overlap_; return *this; } /*! * \brief Assign an overlapping block vector from a * non-overlapping one, border entries are added. */ template void assignAddBorder(const BlockVector& nativeBlockVector) { size_t numDomestic = overlap_->numDomestic(); // assign the local rows from the non-overlapping block vector for (unsigned domRowIdx = 0; domRowIdx < numDomestic; ++domRowIdx) { Index nativeRowIdx = overlap_->domesticToNative(static_cast(domRowIdx)); if (nativeRowIdx < 0) (*this)[domRowIdx] = 0.0; else (*this)[domRowIdx] = nativeBlockVector[nativeRowIdx]; } // add up the contents of the overlapping rows. Since non-native rows are set to // zero above, the addition is only different from an assignment if the row is // shared amongst multiple ranks. syncAdd(); } /*! * \brief Assign an overlapping block vector from a non-overlapping one, border * entries are assigned using their respective master ranks. */ template void assign(const NativeBlockVector& nativeBlockVector) { Index numDomestic = overlap_->numDomestic(); // assign the local rows from the non-overlapping block vector for (Index domRowIdx = 0; domRowIdx < numDomestic; ++domRowIdx) { Index nativeRowIdx = overlap_->domesticToNative(domRowIdx); if (nativeRowIdx < 0) (*this)[static_cast(domRowIdx)] = 0.0; else (*this)[static_cast(domRowIdx)] = nativeBlockVector[static_cast(nativeRowIdx)]; } // add up the contents of border rows, for the remaining rows, // get the values from their respective master process. sync(); } /*! * \brief Assign the local values to a non-overlapping block * vector. */ template void assignTo(NativeBlockVector& nativeBlockVector) const { // assign the local rows size_t numNative = overlap_->numNative(); nativeBlockVector.resize(numNative); for (unsigned nativeRowIdx = 0; nativeRowIdx < numNative; ++nativeRowIdx) { Index domRowIdx = overlap_->nativeToDomestic(static_cast(nativeRowIdx)); if (domRowIdx < 0) nativeBlockVector[nativeRowIdx] = 0.0; else nativeBlockVector[nativeRowIdx] = (*this)[static_cast(domRowIdx)]; } } /*! * \brief Syncronize all values of the block vector from their * master process. */ void sync() { // send all entries to all peers for (const auto peerRank: overlap_->peerSet()) sendEntries_(peerRank); // recieve all entries to the peers for (const auto peerRank: overlap_->peerSet()) receiveFromMaster_(peerRank); // wait until we have send everything waitSendFinished_(); } /*! * \brief Syncronize all values of the block vector by adding up * the values of all peer ranks. */ void syncAdd() { // send all entries to all peers for (const auto peerRank: overlap_->peerSet()) sendEntries_(peerRank); // recieve all entries to the peers for (const auto peerRank: overlap_->peerSet()) receiveAdd_(peerRank); // wait until we have send everything waitSendFinished_(); } void print() const { for (unsigned i = 0; i < this->size(); ++i) { std::cout << "row " << i << (overlap_->isLocal(i) ? " " : "*") << ": " << (*this)[i] << "\n" << std::flush; } } private: void createBuffers_() { #if HAVE_MPI // create array for the front indices typename PeerSet::const_iterator peerIt; typename PeerSet::const_iterator peerEndIt = overlap_->peerSet().end(); // send all indices to the peers peerIt = overlap_->peerSet().begin(); for (; peerIt != peerEndIt; ++peerIt) { ProcessRank peerRank = *peerIt; size_t numEntries = overlap_->foreignOverlapSize(peerRank); numIndicesSendBuff_[peerRank] = std::make_shared >(1); indicesSendBuff_[peerRank] = std::make_shared >(numEntries); valuesSendBuff_[peerRank] = std::make_shared >(numEntries); // fill the indices buffer with global indices MpiBuffer& indicesSendBuff = *indicesSendBuff_[peerRank]; for (unsigned i = 0; i < numEntries; ++i) { Index domRowIdx = overlap_->foreignOverlapOffsetToDomesticIdx(peerRank, i); indicesSendBuff[i] = overlap_->domesticToGlobal(domRowIdx); } // first, send the number of indices (*numIndicesSendBuff_[peerRank])[0] = static_cast(numEntries); numIndicesSendBuff_[peerRank]->send(peerRank); // then, send the indices themselfs indicesSendBuff.send(peerRank); } // receive the indices from the peers peerIt = overlap_->peerSet().begin(); for (; peerIt != peerEndIt; ++peerIt) { ProcessRank peerRank = *peerIt; // receive size of overlap to peer MpiBuffer numRowsRecvBuff(1); numRowsRecvBuff.receive(peerRank); unsigned numRows = numRowsRecvBuff[0]; // then, create the MPI buffers indicesRecvBuff_[peerRank] = std::shared_ptr >( new MpiBuffer(numRows)); valuesRecvBuff_[peerRank] = std::shared_ptr >( new MpiBuffer(numRows)); MpiBuffer& indicesRecvBuff = *indicesRecvBuff_[peerRank]; // next, receive the actual indices indicesRecvBuff.receive(peerRank); // finally, translate the global indices to domestic ones for (unsigned i = 0; i != numRows; ++i) { Index globalRowIdx = indicesRecvBuff[i]; Index domRowIdx = overlap_->globalToDomestic(globalRowIdx); indicesRecvBuff[i] = domRowIdx; } } // wait for all send operations to complete peerIt = overlap_->peerSet().begin(); for (; peerIt != peerEndIt; ++peerIt) { ProcessRank peerRank = *peerIt; numIndicesSendBuff_[peerRank]->wait(); indicesSendBuff_[peerRank]->wait(); // convert the global indices of the send buffer to // domestic ones MpiBuffer& indicesSendBuff = *indicesSendBuff_[peerRank]; for (unsigned i = 0; i < indicesSendBuff.size(); ++i) { indicesSendBuff[i] = overlap_->globalToDomestic(indicesSendBuff[i]); } } #endif // HAVE_MPI } void sendEntries_(ProcessRank peerRank) { // copy the values into the send buffer const MpiBuffer& indices = *indicesSendBuff_[peerRank]; MpiBuffer& values = *valuesSendBuff_[peerRank]; for (unsigned i = 0; i < indices.size(); ++i) values[i] = (*this)[static_cast(indices[i])]; values.send(peerRank); } void waitSendFinished_() { typename PeerSet::const_iterator peerIt; typename PeerSet::const_iterator peerEndIt = overlap_->peerSet().end(); // send all entries to all peers peerIt = overlap_->peerSet().begin(); for (; peerIt != peerEndIt; ++peerIt) { ProcessRank peerRank = *peerIt; valuesSendBuff_[peerRank]->wait(); } } void receiveFromMaster_(ProcessRank peerRank) { const MpiBuffer& indices = *indicesRecvBuff_[peerRank]; MpiBuffer& values = *valuesRecvBuff_[peerRank]; // receive the values from the peer values.receive(peerRank); // copy them into the block vector for (unsigned j = 0; j < indices.size(); ++j) { Index domRowIdx = indices[j]; if (overlap_->masterRank(domRowIdx) == peerRank) { (*this)[static_cast(domRowIdx)] = values[j]; } } } void receiveAdd_(ProcessRank peerRank) { const MpiBuffer& indices = *indicesRecvBuff_[peerRank]; MpiBuffer& values = *valuesRecvBuff_[peerRank]; // receive the values from the peer values.receive(peerRank); // add up the values of rows on the shared boundary for (unsigned j = 0; j < indices.size(); ++j) { Index domRowIdx = indices[j]; (*this)[static_cast(domRowIdx)] += values[j]; } } std::map > > numIndicesSendBuff_; std::map > > indicesSendBuff_; std::map > > indicesRecvBuff_; std::map > > valuesSendBuff_; std::map > > valuesRecvBuff_; const Overlap *overlap_; }; } // namespace Linear } // namespace Opm #endif