mirror of
https://github.com/OPM/opm-simulators.git
synced 2026-10-26 15:40:09 -05:00
CollectDataOnIORank: add gather handle for LGR block-summary values
Add PackUnPackLgrBlockData, the parallel-gather handle for the tuple-keyed per-LGR-cell block-summary map (keyword, level, level-local Cartesian), mirroring opm-common's Summary::DynamicSimulatorState::LgrBlockValues, plus the globalLgrBlockData_ accumulator and accessor. PackUnPackBlockData (global, pair-keyed) and the new handle now share one std::apply-based pack/unpack body; the global block serialization is byte-identical. The handle is not yet wired into collect(); covered by a round-trip + disjoint-merge unit test.
This commit is contained in:
1 parent
a80adbf263
commit
f6dd4ad9a3
4 files changed
+170
-19
No files matched your search
@@ -496,6 +496,7 @@ list (APPEND TEST_SOURCE_FILES
|
||||
tests/test_interregflows.cpp
|
||||
tests/test_invert.cpp
|
||||
tests/test_keyword_validator.cpp
|
||||
tests/test_LgrBlockData.cpp
|
||||
tests/test_linearleastsquares.cpp
|
||||
tests/test_LogOutputHelper.cpp
|
||||
tests/test_milu.cpp
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -90,6 +91,9 @@ public:
|
||||
const std::map<std::pair<std::string, int>, double>& globalBlockData() const
|
||||
{ return globalBlockData_; }
|
||||
|
||||
const std::map<std::tuple<std::string, int, int>, double>& globalLgrBlockData() const
|
||||
{ return globalLgrBlockData_; }
|
||||
|
||||
const data::Solution& globalCellData() const
|
||||
{ return globalCellData_; }
|
||||
|
||||
@@ -156,6 +160,7 @@ protected:
|
||||
std::vector<int> globalRanks_;
|
||||
data::Solution globalCellData_;
|
||||
std::map<std::pair<std::string, int>, double> globalBlockData_;
|
||||
std::map<std::tuple<std::string, int, int>, double> globalLgrBlockData_;
|
||||
data::Wells globalWellData_;
|
||||
data::WellBlockAveragePressures globalWBPData_;
|
||||
data::GroupAndNetworkValues globalGroupAndNetworkData_;
|
||||
|
||||
@@ -37,6 +37,8 @@
|
||||
#include <cassert>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
@@ -461,6 +463,37 @@ public:
|
||||
{ this->globalGroupAndNetworkData_.read(buffer); }
|
||||
};
|
||||
|
||||
// Shared serialisation for the block-summary gather handles. The global B* map is
|
||||
// keyed by std::pair<keyword, cartesian-index>; the LGR LB* map by
|
||||
// std::tuple<keyword, level, level-local-cartesian-index>. std::apply serialises
|
||||
// either key shape: write the map size, then every key field (pair or tuple) followed
|
||||
// by the value -- one body, thin per-key callers (mirrors the producer-side
|
||||
// makeExtractor de-duplication).
|
||||
template <class KeyedMap, class MessageBufferType>
|
||||
void packKeyedBlockMap(const KeyedMap& localData, MessageBufferType& buffer)
|
||||
{
|
||||
const unsigned int size = localData.size();
|
||||
buffer.write(size);
|
||||
for (const auto& [key, value] : localData) {
|
||||
std::apply([&buffer](const auto&... field) { (buffer.write(field), ...); }, key);
|
||||
buffer.write(value);
|
||||
}
|
||||
}
|
||||
|
||||
template <class KeyedMap, class MessageBufferType>
|
||||
void unpackKeyedBlockMap(KeyedMap& globalData, MessageBufferType& buffer)
|
||||
{
|
||||
unsigned int size = 0;
|
||||
buffer.read(size);
|
||||
for (unsigned int i = 0; i < size; ++i) {
|
||||
typename KeyedMap::key_type key{};
|
||||
std::apply([&buffer](auto&... field) { (buffer.read(field), ...); }, key);
|
||||
typename KeyedMap::mapped_type value{};
|
||||
buffer.read(value);
|
||||
globalData[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
class PackUnPackBlockData : public P2PCommunicatorType::DataHandleInterface
|
||||
{
|
||||
const std::map<std::pair<std::string, int>, double>& localBlockData_;
|
||||
@@ -490,32 +523,53 @@ public:
|
||||
if (link != 0)
|
||||
throw std::logic_error("link in method pack is not 0 as expected");
|
||||
|
||||
// write all block data
|
||||
unsigned int size = localBlockData_.size();
|
||||
buffer.write(size);
|
||||
for (const auto& map : localBlockData_) {
|
||||
buffer.write(map.first.first);
|
||||
buffer.write(map.first.second);
|
||||
buffer.write(map.second);
|
||||
}
|
||||
packKeyedBlockMap(localBlockData_, buffer);
|
||||
}
|
||||
|
||||
// unpack all data associated with link
|
||||
void unpack(int /*link*/, MessageBufferType& buffer)
|
||||
{
|
||||
// read all block data
|
||||
unsigned int size = 0;
|
||||
buffer.read(size);
|
||||
for (std::size_t i = 0; i < size; ++i) {
|
||||
std::string name;
|
||||
int idx;
|
||||
double data;
|
||||
buffer.read(name);
|
||||
buffer.read(idx);
|
||||
buffer.read(data);
|
||||
globalBlockValues_[std::make_pair(name, idx)] = data;
|
||||
unpackKeyedBlockMap(globalBlockValues_, buffer);
|
||||
}
|
||||
};
|
||||
|
||||
class PackUnPackLgrBlockData : public P2PCommunicatorType::DataHandleInterface
|
||||
{
|
||||
const std::map<std::tuple<std::string, int, int>, double>& localLgrBlockData_;
|
||||
std::map<std::tuple<std::string, int, int>, double>& globalLgrBlockValues_;
|
||||
|
||||
public:
|
||||
PackUnPackLgrBlockData(const std::map<std::tuple<std::string, int, int>, double>& localLgrBlockData,
|
||||
std::map<std::tuple<std::string, int, int>, double>& globalLgrBlockValues,
|
||||
bool isIORank)
|
||||
: localLgrBlockData_(localLgrBlockData)
|
||||
, globalLgrBlockValues_(globalLgrBlockValues)
|
||||
{
|
||||
if (isIORank) {
|
||||
MessageBufferType buffer;
|
||||
pack(0, buffer);
|
||||
|
||||
// pass a dummyLink to satisfy virtual class
|
||||
int dummyLink = -1;
|
||||
unpack(dummyLink, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
// pack all data associated with link
|
||||
void pack(int link, MessageBufferType& buffer)
|
||||
{
|
||||
// we should only get one link
|
||||
if (link != 0)
|
||||
throw std::logic_error("link in method pack is not 0 as expected");
|
||||
|
||||
packKeyedBlockMap(localLgrBlockData_, buffer);
|
||||
}
|
||||
|
||||
// unpack all data associated with link
|
||||
void unpack(int /*link*/, MessageBufferType& buffer)
|
||||
{
|
||||
unpackKeyedBlockMap(globalLgrBlockValues_, buffer);
|
||||
}
|
||||
};
|
||||
|
||||
class PackUnPackWBPData : public P2PCommunicatorType::DataHandleInterface
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
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 3 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#define BOOST_TEST_MODULE LgrBlockDataGather
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <opm/simulators/flow/CollectDataOnIORank_impl.hpp>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
namespace {
|
||||
// Key = (summary keyword, LGR level, level-local linearised Cartesian index),
|
||||
// matching opm-common's Summary::DynamicSimulatorState::LgrBlockValues.
|
||||
using Key = std::tuple<std::string, int, int>;
|
||||
using LgrBlockMap = std::map<Key, double>;
|
||||
}
|
||||
|
||||
// Opm::PackUnPackLgrBlockData is the gather handle for the tuple-keyed LB* per-LGR-cell
|
||||
// summary map. Its IO-rank constructor self-packs the local map into a message buffer and
|
||||
// unpacks it back into the supplied global map -- which is exactly how the IO rank folds in
|
||||
// each rank's contribution during collect(). Constructing it with isIORank == true therefore
|
||||
// exercises the pack -> unpack round-trip, and constructing it twice into the same global map
|
||||
// exercises the disjoint union of two ranks' contributions -- all without needing MPI.
|
||||
|
||||
BOOST_AUTO_TEST_CASE(RoundTripPreservesEntries)
|
||||
{
|
||||
const LgrBlockMap local = {
|
||||
{ Key{"LBPR", 1, 0}, 250.0 },
|
||||
{ Key{"LBPR", 1, 3}, 251.5 },
|
||||
{ Key{"LBOSAT", 2, 0}, 0.30 },
|
||||
};
|
||||
|
||||
LgrBlockMap gathered;
|
||||
Opm::PackUnPackLgrBlockData{ local, gathered, /*isIORank=*/true };
|
||||
|
||||
BOOST_CHECK(gathered == local);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(DisjointMergeAccumulatesBothRanks)
|
||||
{
|
||||
const LgrBlockMap rank0 = {
|
||||
{ Key{"LBPR", 1, 0}, 250.0 },
|
||||
{ Key{"LBPR", 1, 3}, 251.5 },
|
||||
};
|
||||
const LgrBlockMap rank1 = {
|
||||
{ Key{"LBPR", 1, 7}, 252.0 },
|
||||
{ Key{"LBOSAT", 2, 0}, 0.30 },
|
||||
};
|
||||
|
||||
LgrBlockMap gathered;
|
||||
Opm::PackUnPackLgrBlockData{ rank0, gathered, /*isIORank=*/true };
|
||||
Opm::PackUnPackLgrBlockData{ rank1, gathered, /*isIORank=*/true };
|
||||
|
||||
LgrBlockMap expected = rank0;
|
||||
expected.insert(rank1.begin(), rank1.end());
|
||||
|
||||
BOOST_CHECK(gathered == expected);
|
||||
BOOST_CHECK_EQUAL(gathered.size(), 4u);
|
||||
BOOST_CHECK_EQUAL(gathered.at(Key{"LBPR", 1, 0}), 250.0);
|
||||
BOOST_CHECK_EQUAL(gathered.at(Key{"LBPR", 1, 7}), 252.0);
|
||||
BOOST_CHECK_EQUAL(gathered.at(Key{"LBOSAT", 2, 0}), 0.30);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(EmptyMapGathersNothing)
|
||||
{
|
||||
const LgrBlockMap empty;
|
||||
|
||||
LgrBlockMap gathered;
|
||||
Opm::PackUnPackLgrBlockData{ empty, gathered, /*isIORank=*/true };
|
||||
|
||||
BOOST_CHECK(gathered.empty());
|
||||
}
|
||||
Reference in new issue
Block a user