Add Accumulating Mapping Structure for Inter-Region Flows

This commit introduces a new helper facility,

    data::InterRegFlowMap

that knows how to interpret region IDs.  It is a simple wrapper
around a common CSR representation of (source, destination) region
ID pairs with the additional feature that the element type of the
connection is a linear sequence values of arithmetic type.  We've
initially elected to make this sequence out of 'float' in order to
conserve memory since we expect to do little arithmetic to the
sequence itself outside of compression and parallel communication.

Client code will be of two primary categories

  1. Producing side which inserts flow rates from individual
     connections, compresses the result when done, and communicates
     the results to one or more parallel I/O ranks

  2. Consuming side, typically on an I/O rank, which merges the
     contributions from multiple source ranks and extracts values
     pertaining inter-region flows for purpose of outputting time
     series data to a summary file.

The first case will create and populate 'FlowRates' and add these
into the container using the 'addConnection' function and finally
call 'compress' when complete.  Consumers will then use member
function 'getInterRegFlows' to extract a signed view of flow rate
values associated to a particular region ID pair (nullopt if no such
pair is registered).
This commit is contained in:
Bård Skaflestad 2022-01-26 16:47:59 +01:00
parent 7ef3d6c0b3
commit 68975be966
4 changed files with 2109 additions and 0 deletions

View File

@ -306,6 +306,7 @@ if(ENABLE_ECL_OUTPUT)
src/opm/io/eclipse/rst/state.cpp
src/opm/io/eclipse/rst/well.cpp
src/opm/output/data/Aquifer.cpp
src/opm/output/data/InterRegFlowMap.cpp
src/opm/output/data/Solution.cpp
src/opm/output/eclipse/ActiveIndexByColumns.cpp
src/opm/output/eclipse/AggregateActionxData.cpp
@ -461,6 +462,7 @@ if(ENABLE_ECL_OUTPUT)
tests/test_DoubHEAD.cpp
tests/test_InteHEAD.cpp
tests/test_data_InterRegFlow.cpp
tests/test_data_InterRegFlowMap.cpp
tests/test_LinearisedOutputTable.cpp
tests/test_LogiHEAD.cpp
tests/test_LGOData.cpp
@ -937,6 +939,7 @@ if(ENABLE_ECL_OUTPUT)
opm/output/data/GuideRateValue.hpp
opm/output/data/Groups.hpp
opm/output/data/InterRegFlow.hpp
opm/output/data/InterRegFlowMap.hpp
opm/output/data/Solution.hpp
opm/output/data/Wells.hpp
opm/output/eclipse/VectorItems/action.hpp

View File

@ -0,0 +1,533 @@
/*
Copyright 2016 SINTEF ICT, Applied Mathematics.
Copyright 2016 Statoil ASA.
Copyright 2022 Equinor ASA
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/>.
*/
#ifndef OPM_OUTPUT_DATA_INTERREGFLOWMAP_HPP
#define OPM_OUTPUT_DATA_INTERREGFLOWMAP_HPP
#include <opm/output/data/InterRegFlow.hpp>
#include <cstddef>
#include <optional>
#include <utility>
#include <type_traits>
#include <vector>
/// \file
///
/// Facility for converting collection of region ID pairs into a sparse
/// (CSR) adjacency matrix representation of a graph. Supports O(nnz)
/// compression and, if applicable, accumulation of weight values for
/// repeated entity pairs.
namespace Opm { namespace data {
/// Form CSR adjacency matrix representation of inter-region flow rate
/// graph provided as a list of connections between regions.
class InterRegFlowMap
{
private:
/// Representation of neighbouring regions.
using Neighbours = std::vector<int>;
/// Offset into neighbour array.
using Offset = Neighbours::size_type;
/// CSR start pointers.
using Start = std::vector<Offset>;
/// Linear flow rate buffer.
using RateBuffer = std::vector<float>;
/// Internal view of flows between regions.
using Window = InterRegFlow<RateBuffer::iterator>;
public:
/// Client view of flows between specified region pair.
using ReadOnlyWindow = InterRegFlow<std::vector<float>::const_iterator>;
/// Client type through which to define a single inter-region connection.
using FlowRates = Window::FlowRates;
/// Client type through which to identify a component flow of a
/// single inter-region connection.
using Component = Window::Component;
/// Add flow rate connection between regions.
///
/// \param[in] r1 Primary (source) zero-based region index. Used as
/// row index.
///
/// \param[in] r2 Secondary (sink) zero-based region index. Used as
/// column index.
///
/// \param[in] rates Flow rates associated to single connection.
///
/// If both region IDs are the same then this function does nothing.
void addConnection(const int r1, const int r2, const FlowRates& rates);
/// Form CSR adjacency matrix representation of input graph from
/// connections established in previous calls to addConnection().
///
/// \param[in] numRegions Number of rows in resulting CSR matrix.
/// If prior calls to addConnection() supply source entity IDs
/// (row indices) greater than or equal to \p numRows, then
/// method compress() will throw \code std::invalid_argument
/// \endcode.
void compress(const std::size_t numRegions);
/// Retrieve number of rows (source entities) in input graph.
/// Corresponds to value of argument passed to compress(). Valid
/// only after calling compress().
Offset numRegions() const;
/// Retrieve accumulated inter-region flow rates for identified pair
/// of regions.
///
/// \param[in] r1 Primary (source) zero-based region index. Used as
/// row index.
///
/// \param[in] r2 Secondary (sink) zero-based region index. Used as
/// column index.
///
/// \return View of accumulated inter-region flow rates and
/// associated flow direction sign. \code std::nullopt \endcode
/// if no such rates exist.
std::optional<std::pair<ReadOnlyWindow, ReadOnlyWindow::ElmT>>
getInterRegFlows(const int r1, const int r2) const;
// MessageBufferType API should be similar to Dune::MessageBufferIF
template <class MessageBufferType>
void write(MessageBufferType& buffer) const
{
this->csr_.write(buffer);
}
// MessageBufferType API should be similar to Dune::MessageBufferIF
template <class MessageBufferType>
void read(MessageBufferType& buffer)
{
auto other = CSR{};
other.read(buffer);
this->uncompressed_
.add(other.maxRowIdx(),
other.maxColIdx(),
other.coordinateFormatRowIndices(),
other.columnIndices(),
other.values());
}
/// Clear all internal buffers, but preserve allocated capacity.
void clear();
private:
/// Coordinate format representation of individual contributions to
/// inter-region flows.
class Connections
{
public:
/// Add contributions from a single inter-region connection.
///
/// \param[in] r1 Source region. Zero-based region index/ID.
///
/// \param[in] r2 Destination region. Zero-based region index.
///
/// \param[in] rates Flow rates of single inter-region
/// connection.
void add(const int r1, const int r2, const FlowRates& rates);
/// Add contributions from multiple inter-region connections.
///
/// \param[in] maxRowIdx Maximum row (source region) index
/// across all new inter-region connection contributions.
///
/// \param[in] maxColIdx Maximum column (destination region)
/// index across all new inter-region contributions.
///
/// \param[in] rows Source region indices for all new
/// inter-region connection contributions.
///
/// \param[in] cols Destination region indices for all new
/// inter-region connection contributions.
///
/// \param[in] rates Flow rate values for all new inter-region
/// connection contributions.
void add(const int maxRowIdx,
const int maxColIdx,
const Neighbours& rows,
const Neighbours& cols,
const RateBuffer& rates);
/// Clear internal tables. Preserve allocated capacity.
void clear();
/// Predicate.
///
/// \return Whether or not internal tables are empty.
bool empty() const;
/// Whether or not internal tables meet size consistency
/// requirements.
bool isValid() const;
/// Maximum zero-based row (source region) index.
int maxRow() const;
/// Maximum zero-based column (destination region) index.
int maxCol() const;
/// Number of uncompressed contributions in internal tables.
Neighbours::size_type numContributions() const;
/// Read-only access to uncompressed row indices.
const Neighbours& rowIndices() const;
/// Read-only access to uncompressed column indices.
const Neighbours& columnIndices() const;
/// Read-only access to uncompressed flow rate values.
const RateBuffer& values() const;
private:
/// Zero-based row/source region indices.
Neighbours i_{};
/// Zero-based column/destination region indices.
Neighbours j_{};
/// Uncompressed flow rate values. Window::bufferSize() entries
/// per connection.
RateBuffer v_{};
/// Maximum row index in \code this->i_ \endcode.
int max_i_{ -1 };
/// Maximum column index in \code this->j_ \endcode.
int max_j_{ -1 };
};
/// Compressed sparse row representation of inter-region flow rates
///
/// Row and column indices are zero-based region IDs. Column
/// indices ascendingly sorted per row. Value type is window,
/// backed by a pair of iterators, of aggregate flow rates per
/// region pair.
class CSR
{
public:
/// Merge coordinate format into existing CSR map.
///
/// \param[in] conns Coordinate representation of new
/// contributions.
///
/// \param[in] numRegions Maximum number of regions in this
/// region set. Common values/settings are
///
/// -# Maximum one-based region ID on local MPI rank
/// -# Maximum one-based region ID across all MPI ranks
/// -# Maximum *possible* one-based region ID in model
/// ("NTFIP"), from TABDIMS(5) and/or REGDIMS(1).
///
/// If this value is smaller than the maximum one-based
/// region ID on the local MPI rank, then it will be ignored
/// and the local rank's maximum one-based region ID will be
/// used instead.
void merge(const Connections& conns,
const Offset numRegions);
/// Read-only access to flow rates of given region ID pair.
///
/// \param[in] i Source region. Zero-based region ID.
///
/// \param[in] j Destination region. Zero-based region ID.
///
/// \return Flow rates of region ID pair. Nullopt if no such
/// pair exists.
std::optional<ReadOnlyWindow> getWindow(const int i, const int j) const;
/// Total number of rows in compressed map structure.
Offset numRows() const;
/// Maximum zero-based row index encountered mapped structure.
int maxRowIdx() const;
/// Maximum zero-based column index encountered mapped structure.
int maxColIdx() const;
/// Read-only access to compressed structure's start pointers.
const Start& startPointers() const;
/// Read-only access to compressed structure's column indices,
/// ascendingly sorted per rwo.
const Neighbours& columnIndices() const;
/// Read-only access to compressed, unique, linearised flow rate
/// values. \code Window::bufferSize() \endcode entries per
/// non-zero element.
const RateBuffer& values() const;
/// Coordinate format row index vector. Expanded from \code
/// startPointers() \endcode.
Neighbours coordinateFormatRowIndices() const;
// MessageBufferType API should be similar to Dune::MessageBufferIF
template <class MessageBufferType>
void write(MessageBufferType& buffer) const
{
this->writeVector(this->ia_, buffer);
this->writeVector(this->ja_, buffer);
this->writeVector(this->sa_, buffer);
this->writeVector(this->compressedIdx_, buffer);
buffer.write(this->numRows_);
buffer.write(this->numCols_);
}
// MessageBufferType API should be similar to Dune::MessageBufferIF
template <class MessageBufferType>
void read(MessageBufferType& buffer)
{
this->readVector(buffer, this->ia_);
this->readVector(buffer, this->ja_);
this->readVector(buffer, this->sa_);
this->readVector(buffer, this->compressedIdx_);
buffer.read(this->numRows_);
buffer.read(this->numCols_);
}
/// Clear internal tables. Preserve allocated capacity.
void clear();
private:
/// Start pointers.
Start ia_{};
/// Column indices. Ascendingly sorted per row once structure
/// is fully established.
Neighbours ja_{};
/// Compressed, unique, linearised flow rate values. \code
/// Window::bufferSize() \endcode entries per non-zero map
/// element.
RateBuffer sa_{};
/// Destination index in compressed representation. Size NNZ.
Start compressedIdx_{};
/// Number of active rows in compressed map structure.
int numRows_{ 0 };
/// Number of active columns in compressed map structure.
/// Tracked as the maximum column index plus one.
int numCols_{ 0 };
// ---------------------------------------------------------
// Implementation of read()/write()
// ---------------------------------------------------------
template <typename T, class A, class MessageBufferType>
void writeVector(const std::vector<T,A>& vec,
MessageBufferType& buffer) const
{
const auto n = vec.size();
buffer.write(n);
for (const auto& x : vec) {
buffer.write(x);
}
}
template <class MessageBufferType, typename T, class A>
void readVector(MessageBufferType& buffer,
std::vector<T,A>& vec)
{
auto n = 0 * vec.size();
buffer.read(n);
vec.resize(n);
for (auto& x : vec) {
buffer.read(x);
}
}
// ---------------------------------------------------------
// Implementation of merge()
// ---------------------------------------------------------
/// Incorporate new, coordinate format contributions into
/// existing, possibly empty, CSR mapping structure.
///
/// On exit the ia_ array holds the proper start pointers while
/// ja_ holds the corresponding column indices albeit possibly
/// repeated and unsorted.
///
/// \param[in] rows Row indices of all, possibly repeated,
/// coordinate format input contributions. Start pointers \c
/// ia_ updated to account for new entries.
///
/// \param[in] cols Column index of coordinate format intput
/// structure. Inserted into \c ja_ according to its
/// corresponding row index.
///
/// \param[in] maxRowIdx Maximum index in \p rows. Needed to
/// ensure proper size of \c ia_.
///
/// \param[in] maxColIdx Maximum index in \p cols.
void assemble(const Neighbours& rows,
const Neighbours& cols,
const int maxRowIdx,
const int maxColIdx);
/// Sort column indices per row and compress repeated column
/// indices down to a single unique element per row. Sum
/// repeated values
///
/// On exit the \c ia_, \c ja_, and \c sa_ arrays all have their
/// expected, canonical structure.
///
/// \param[in] numRegions Maximum number of regions supported by
/// final compressed mapping structure. Ignored if less than
/// active number of rows.
///
/// \param[in] rates Uncompressed flow rate values from
/// coordinate format contributions.
void compress(const Offset numRegions,
const RateBuffer& rates);
/// Sort column indices within each mapped row.
///
/// On exit \c ja_ has ascendingly sorted column indices, albeit
/// possibly with repeated entries. This function also updates
/// \c compressedIdx_ to account for the new locations of the
/// non-zero elements in the grouped structure.
void sortColumnIndicesPerRow();
/// Condense repeated column indices per row down to a single
/// unique entry for each.
///
/// Assumes that each row has ascendingly sorted column indices
/// in \c ja_ and must therefore be called after member function
/// sortColumnIndicesPerRow(). On exit, \c ja_ has its final
/// canonical structure and \c compressedIdx_ knows the final
/// location of each non-zero contribution in the input
/// coordinate format.
void condenseDuplicates();
/// Sum coordinate format flow rates into compressed map
/// structure.
///
/// Repeated (row,column) index pairs in the input coordinate
/// format add to the same compressed map element. This
/// function assumes that \c compressedIdx_ knows the final
/// compressed location of each non-zero contribution in the
/// input coordinate format and must therefore be called after
/// member function condenseDuplicates(). On exit \c sa_ has
/// incorporated all entries from the input coordinate
/// structure.
///
/// \param[in] v Uncompressed flow rate values from coordinate
/// format contributions.
void accumulateFlowRates(const RateBuffer& v);
// ---------------------------------------------------------
// Implementation of assemble()
// ---------------------------------------------------------
/// Position end pointers at start of row to prepare for column
/// index grouping by corresponding row index.
///
/// Also counts total number of non-zero elements, possibly
/// including repetitions, in \code this->ia_[0] \endcode.
///
/// \param[in] Number of rows in final compressed structure.
/// Used to allocate \code this->ia_ \endcode.
///
/// \param[in] Row indices of all, possibly repeated, coordinate
/// format input contributions. Needed to count the number
/// of possibly repeated column index entries per row.
void preparePushbackRowGrouping(const int numRows,
const Neighbours& rowIdx);
/// Group column indices by corresponding row index and track
/// grouped location of original coordinate format element
///
/// Appends grouped location to \c compressedIdx_.
///
/// \param[in] rowIdx Row index of coordinate format input
/// structure. Used as grouping key.
///
/// \param[in] colIdx Column index of coordinate format intput
/// structure. Inserted into \c ja_ according to its
/// corresponding row index.
void groupAndTrackColumnIndicesByRow(const Neighbours& rowIdx,
const Neighbours& colIdx);
// ---------------------------------------------------------
// General utilities
// ---------------------------------------------------------
/// Transpose connectivity structure.
///
/// Essentially swaps the roles of rows and columns. Also used
/// as a basic building block for sortColumnIndicesPerRow().
void transpose();
/// Condense sequences of repeated column indices in a single
/// map row down to a single copy of each unique column index.
///
/// Appends new unique column indices to \code ja_ \endcode
///
/// Assumes that the map row has ascendingly sorted column
/// indices and therefore has the same requirements as
/// std::unique. Will also update the internal compressedIdx_
/// mapping to record new compressed locations for the current,
/// uncompressed, non-zero map elements.
///
/// \param[in] begin Start of map row that contains possibly
/// repeated column indices.
///
/// \param[in] end One-past-end of map row that contains
/// possibly repeated column indices.
void condenseAndTrackUniqueColumnsForSingleRow(Neighbours::const_iterator begin,
Neighbours::const_iterator end);
/// Update \c compressedIdx_ mapping to account for column index
/// reshuffling.
///
/// \param[in] compressedIdx New compressed index locations of
/// the non-zero map entries.
void remapCompressedIndex(Start&& compressedIdx);
};
/// Accumulated coordinate format contributions that have not yet
/// been added to the final CSR structure.
Connections uncompressed_;
/// Canonical representation of unique inter-region flow rates.
CSR csr_;
};
}} // namespace Opm::data
#endif // OPM_OUTPUT_DATA_INTERREGFLOWMAP_HPP

View File

@ -0,0 +1,595 @@
/*
Copyright 2016 SINTEF ICT, Applied Mathematics.
Copyright 2016 Statoil ASA.
Copyright 2022 Equinor ASA
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/>.
*/
#if HAVE_CONFIG_H
#include <config.h>
#endif // HAVE_CONFIG_H
#include <opm/output/data/InterRegFlowMap.hpp>
#include <opm/output/data/InterRegFlow.hpp>
#include <algorithm>
#include <cassert>
#include <exception>
#include <iterator>
#include <optional>
#include <stdexcept>
#include <utility>
#include <vector>
// ---------------------------------------------------------------------
// Class Opm::data::InterRegFlowMap::Connections
// ---------------------------------------------------------------------
void
Opm::data::InterRegFlowMap::
Connections::add(const int r1, const int r2, const FlowRates& v)
{
using ElmT = Window::ElmT;
const auto one = ElmT{1};
const auto sign = (r1 < r2) ? one : -one;
auto low = r1, high = r2;
if (std::signbit(sign)) {
std::swap(low, high);
}
this->i_.push_back(low);
this->j_.push_back(high);
this->max_i_ = std::max(this->max_i_, this->i_.back());
this->max_j_ = std::max(this->max_j_, this->j_.back());
const auto start = this->v_.size();
this->v_.insert(this->v_.end(), Window::bufferSize(), ElmT{0});
Window{ this->v_.begin() + start, this->v_.end() }.addFlow(sign, v);
}
void
Opm::data::InterRegFlowMap::
Connections::add(const int maxRowIdx,
const int maxColIdx,
const Neighbours& rows,
const Neighbours& cols,
const RateBuffer& rates)
{
if (cols.size() != rows.size()) {
throw std::invalid_argument {
"Coordinate format column index table size does not match "
"row index table size"
};
}
if (rates.size() != Window::bufferSize() * rows.size()) {
throw std::invalid_argument {
"Coordinate format value table size does not match "
"row index table size"
};
}
this->i_.insert(this->i_.end(), rows .begin(), rows .end());
this->j_.insert(this->j_.end(), cols .begin(), cols .end());
this->v_.insert(this->v_.end(), rates.begin(), rates.end());
this->max_i_ = std::max(this->max_i_, maxRowIdx);
this->max_j_ = std::max(this->max_j_, maxColIdx);
}
void Opm::data::InterRegFlowMap::Connections::clear()
{
this->v_.clear();
this->j_.clear();
this->i_.clear();
this->max_i_ = -1;
this->max_j_ = -1;
}
bool Opm::data::InterRegFlowMap::Connections::empty() const
{
return this->i_.empty();
}
bool Opm::data::InterRegFlowMap::Connections::isValid() const
{
return (this->i_.size() == this->j_.size())
&& (this->v_.size() == this->i_.size()*Window::bufferSize());
}
int Opm::data::InterRegFlowMap::Connections::maxRow() const
{
return this->max_i_;
}
int Opm::data::InterRegFlowMap::Connections::maxCol() const
{
return this->max_j_;
}
Opm::data::InterRegFlowMap::Neighbours::size_type
Opm::data::InterRegFlowMap::Connections::numContributions() const
{
return this->i_.size();
}
const Opm::data::InterRegFlowMap::Neighbours&
Opm::data::InterRegFlowMap::Connections::rowIndices() const
{
return this->i_;
}
const Opm::data::InterRegFlowMap::Neighbours&
Opm::data::InterRegFlowMap::Connections::columnIndices() const
{
return this->j_;
}
const Opm::data::InterRegFlowMap::RateBuffer&
Opm::data::InterRegFlowMap::Connections::values() const
{
return this->v_;
}
// =====================================================================
// ---------------------------------------------------------------------
// Class Opm::data::InterRegFlowMap::CSR
// ---------------------------------------------------------------------
void
Opm::data::InterRegFlowMap::
CSR::merge(const Connections& conns, const Offset numRegions)
{
if (! conns.empty() &&
(static_cast<Offset>(conns.maxRow()) >= numRegions))
{
throw std::invalid_argument {
"Input graph contains more "
"source regions than are "
"implied by explicit size of "
"adjacency matrix"
};
}
this->assemble(conns.rowIndices(), conns.columnIndices(),
conns.maxRow(), conns.maxCol());
this->compress(numRegions, conns.values());
}
std::optional<Opm::data::InterRegFlowMap::ReadOnlyWindow>
Opm::data::InterRegFlowMap::CSR::getWindow(const int i, const int j) const
{
if ((i < 0) || (i >= static_cast<int>(this->numRows())) ||
(j < 0) || (j > this->maxColIdx()))
{
// Entity pair IDs out of range.
return std::nullopt;
}
auto begin = this->columnIndices().begin() + this->startPointers()[i + 0];
auto end = this->columnIndices().begin() + this->startPointers()[i + 1];
auto pos = std::lower_bound(begin, end, j);
if ((pos == end) || (*pos > j)) {
// Entity 'j' does not connect to entity 'i'.
return std::nullopt;
}
// Entity 'j' connects to 'i'. Form read-only view into sub-range
// pertaining to this entity pair.
const auto sz = ReadOnlyWindow::bufferSize();
const auto windowID = pos - this->columnIndices().begin();
auto start = this->values().begin() + windowID*sz;
return { ReadOnlyWindow{ start, start + sz } };
}
Opm::data::InterRegFlowMap::Offset
Opm::data::InterRegFlowMap::CSR::numRows() const
{
return this->startPointers().empty()
? 0 : this->startPointers().size() - 1;
}
int Opm::data::InterRegFlowMap::CSR::maxRowIdx() const
{
return this->numRows_ - 1;
}
int Opm::data::InterRegFlowMap::CSR::maxColIdx() const
{
return this->numCols_ - 1;
}
const Opm::data::InterRegFlowMap::Start&
Opm::data::InterRegFlowMap::CSR::startPointers() const
{
return this->ia_;
}
const Opm::data::InterRegFlowMap::Neighbours&
Opm::data::InterRegFlowMap::CSR::columnIndices() const
{
return this->ja_;
}
const Opm::data::InterRegFlowMap::RateBuffer&
Opm::data::InterRegFlowMap::CSR::values() const
{
return this->sa_;
}
std::vector<int>
Opm::data::InterRegFlowMap::CSR::coordinateFormatRowIndices() const
{
auto rowIdx = std::vector<int>{};
if (this->ia_.empty()) {
return rowIdx;
}
rowIdx.reserve(this->ia_.back());
auto row = 0;
const auto m = this->ia_.size() - 1;
for (auto i = 0*m; i < m; ++i, ++row) {
const auto n = this->ia_[i + 1] - this->ia_[i + 0];
rowIdx.insert(rowIdx.end(), n, row);
}
return rowIdx;
}
void Opm::data::InterRegFlowMap::CSR::clear()
{
this->ia_.clear();
this->ja_.clear();
this->sa_.clear();
this->compressedIdx_.clear();
this->numRows_ = 0;
this->numCols_ = 0;
}
void
Opm::data::InterRegFlowMap::
CSR::assemble(const Neighbours& rows,
const Neighbours& cols,
const int maxRowIdx,
const int maxColIdx)
{
auto i = this->coordinateFormatRowIndices();
i.insert(i.end(), rows.begin(), rows.end());
auto j = this->ja_;
j.insert(j.end(), cols.begin(), cols.end());
const auto thisNumRows = std::max(this->numRows_, maxRowIdx + 1);
const auto thisNumCols = std::max(this->numCols_, maxColIdx + 1);
this->preparePushbackRowGrouping(thisNumRows, i);
this->groupAndTrackColumnIndicesByRow(i, j);
this->numRows_ = thisNumRows;
this->numCols_ = thisNumCols;
}
void
Opm::data::InterRegFlowMap::
CSR::compress(const Offset numRegions,
const RateBuffer& rates)
{
this->sortColumnIndicesPerRow();
// Must be called *after* sortColumnIndicesPerRow().
this->condenseDuplicates();
{
auto v = this->values();
v.insert(v.end(), rates.begin(), rates.end());
this->accumulateFlowRates(v);
}
const auto nRows = this->startPointers().size() - 1;
if (nRows < numRegions) {
this->ia_.insert(this->ia_.end(),
numRegions - nRows,
this->startPointers().back());
}
}
void Opm::data::InterRegFlowMap::CSR::sortColumnIndicesPerRow()
{
// Transposition is, in this context, effectively a linear time (O(nnz))
// bucket insertion procedure. In other words transposing the structure
// twice creates a structure with column indices in (ascendingly) sorted
// order.
this->transpose();
this->transpose();
}
void Opm::data::InterRegFlowMap::CSR::condenseDuplicates()
{
// Note: Must be called *after* sort().
const auto colIdx = this->ja_;
auto compressedIdx = this->compressedIdx_;
auto end = colIdx.begin();
this->ja_.clear();
this->compressedIdx_.clear();
const auto numRows = this->ia_.size() - 1;
for (auto row = 0*numRows; row < numRows; ++row) {
auto begin = end;
std::advance(end, this->ia_[row + 1] - this->ia_[row + 0]);
const auto q = this->ja_.size();
this->condenseAndTrackUniqueColumnsForSingleRow(begin, end);
this->ia_[row + 0] = q;
}
this->remapCompressedIndex(std::move(compressedIdx));
// Record final table sizes.
this->ia_.back() = this->ja_.size();
}
void Opm::data::InterRegFlowMap::CSR::accumulateFlowRates(const RateBuffer& v)
{
const auto sz = Window::bufferSize();
if (v.size() != this->compressedIdx_.size()*sz) {
throw std::logic_error {
"Flow rates must be provided for each connection"
};
}
auto dst = [this, sz](const Offset start) -> Window
{
auto begin = this->sa_.begin() + start*sz;
return Window { begin, begin + sz };
};
auto src = [&v, sz](const Offset start) -> ReadOnlyWindow
{
auto begin = v.begin() + start*sz;
return ReadOnlyWindow { begin, begin + sz };
};
this->sa_.assign(this->ja_.size() * sz, Window::ElmT{0});
const auto numRates = this->compressedIdx_.size();
for (auto rateID = 0*numRates; rateID < numRates; ++rateID) {
dst(this->compressedIdx_[rateID]) += src(rateID);
}
}
void
Opm::data::InterRegFlowMap::
CSR::preparePushbackRowGrouping(const int numRows,
const Neighbours& rowIdx)
{
assert (numRows >= 0);
this->ia_.assign(numRows + 1, 0);
for (const auto& row : rowIdx) {
this->ia_[row + 1] += 1;
}
// Note index range: 1..numRows inclusive.
for (Start::size_type i = 1, n = numRows; i <= n; ++i) {
this->ia_[0] += this->ia_[i];
this->ia_[i] = this->ia_[0] - this->ia_[i];
}
assert (this->ia_[0] == rowIdx.size());
}
void
Opm::data::InterRegFlowMap::
CSR::groupAndTrackColumnIndicesByRow(const Neighbours& rowIdx,
const Neighbours& colIdx)
{
assert (this->ia_[0] == rowIdx.size());
const auto nnz = rowIdx.size();
this->ja_.resize(nnz);
this->compressedIdx_.clear();
this->compressedIdx_.reserve(nnz);
for (auto nz = 0*nnz; nz < nnz; ++nz) {
const auto k = this->ia_[rowIdx[nz] + 1] ++;
this->ja_[k] = colIdx[nz];
this->compressedIdx_.push_back(k);
}
this->ia_[0] = 0;
}
void Opm::data::InterRegFlowMap::CSR::transpose()
{
auto compressedIdx = this->compressedIdx_;
{
const auto rowIdx = this->coordinateFormatRowIndices();
const auto colIdx = this->ja_;
this->preparePushbackRowGrouping(this->numCols_, colIdx);
// Note parameter order. Transposition switches role of rows and
// columns.
this->groupAndTrackColumnIndicesByRow(colIdx, rowIdx);
}
this->remapCompressedIndex(std::move(compressedIdx));
std::swap(this->numRows_, this->numCols_);
}
void
Opm::data::InterRegFlowMap::CSR::
condenseAndTrackUniqueColumnsForSingleRow(Neighbours::const_iterator begin,
Neighbours::const_iterator end)
{
// We assume that we're only called *after* sortColumnIndicesPerRow()
// whence duplicate elements appear consecutively in [begin, end).
//
// Note: This is essentially the same as std::unique(begin, end) save
// for the return value and the fact that we additionally record the
// 'compressedIdx_' mapping. That mapping enables subsequent, decoupled
// accumulation of the 'sa_' contributions.
while (begin != end) {
// Note: Order of ja_ and compressedIdx_ matters here.
this->compressedIdx_.push_back(this->ja_.size());
this->ja_ .push_back(*begin);
while ((++begin != end) &&
( *begin == this->ja_.back()))
{
this->compressedIdx_.push_back(this->compressedIdx_.back());
}
}
}
void
Opm::data::InterRegFlowMap::CSR::
remapCompressedIndex(Start&& compressedIdx)
{
for (auto& i : compressedIdx) {
i = this->compressedIdx_[i];
}
this->compressedIdx_.swap(compressedIdx);
}
// =====================================================================
// ---------------------------------------------------------------------
// Class Opm::data::InterRegFlowMap
// ---------------------------------------------------------------------
void
Opm::data::InterRegFlowMap::
addConnection(const int r1,
const int r2,
const FlowRates& rates)
{
if ((r1 < 0) || (r2 < 0)) {
throw std::invalid_argument {
"Region indices must be non-negative. Got (r1,r2) = ("
+ std::to_string(r1) + ", " + std::to_string(r2)
+ ')'
};
}
if (r1 == r2) {
// Internal to a region. Skip.
return;
}
this->uncompressed_.add(r1, r2, rates);
}
void Opm::data::InterRegFlowMap::compress(const std::size_t numRegions)
{
if (! this->uncompressed_.isValid()) {
throw std::logic_error {
"Cannot compress invalid connection list"
};
}
this->csr_.merge(this->uncompressed_, numRegions);
this->uncompressed_.clear();
}
Opm::data::InterRegFlowMap::Offset
Opm::data::InterRegFlowMap::numRegions() const
{
return this->csr_.numRows();
}
std::optional<std::pair<
Opm::data::InterRegFlowMap::ReadOnlyWindow,
Opm::data::InterRegFlowMap::ReadOnlyWindow::ElmT
>>
Opm::data::InterRegFlowMap::getInterRegFlows(const int r1, const int r2) const
{
if ((r1 < 0) || (r2 < 0)) {
throw std::invalid_argument {
"Region indices must be non-negative. Got (r1,r2) = ("
+ std::to_string(r1) + ", " + std::to_string(r2)
+ ')'
};
}
if (r1 == r2) {
// Internal to a region. Skip.
throw std::invalid_argument {
"Region indices must be distinct. Got (r1,r2) = ("
+ std::to_string(r1) + ", " + std::to_string(r2)
+ ')'
};
}
using ElmT = ReadOnlyWindow::ElmT;
const auto one = ElmT{1};
const auto sign = (r1 < r2) ? one : -one;
auto low = r1, high = r2;
if (std::signbit(sign)) {
std::swap(low, high);
}
auto window = this->csr_.getWindow(low, high);
if (! window.has_value()) {
// High is not connected to low.
return std::nullopt;
}
return std::make_pair(std::move(window.value()), sign);
}
void Opm::data::InterRegFlowMap::clear()
{
this->uncompressed_.clear();
this->csr_.clear();
}

View File

@ -0,0 +1,978 @@
/*
Copyright (c) 2022 Equinor ASA
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/>.
*/
#define BOOST_TEST_MODULE data_InterRegFlowMap
#include <boost/test/unit_test.hpp>
#include <opm/output/data/InterRegFlowMap.hpp>
#include <optional>
#include <stdexcept>
#include <utility>
#include <tuple>
#include "tests/MessageBuffer.cpp"
namespace {
Opm::data::InterRegFlowMap::FlowRates conn_1()
{
using Component = Opm::data::InterRegFlowMap::Component;
auto rate = Opm::data::InterRegFlowMap::FlowRates{};
rate[Component::Oil] = 1.0;
rate[Component::Gas] = 2.0;
rate[Component::Water] = 3.0;
rate[Component::Disgas] = 4.0;
rate[Component::Vapoil] = 5.0;
return rate;
}
Opm::data::InterRegFlowMap::FlowRates conn_2()
{
using Component = Opm::data::InterRegFlowMap::Component;
auto rate = Opm::data::InterRegFlowMap::FlowRates{};
rate[Component::Oil] = 0.1;
rate[Component::Gas] = 0.2;
rate[Component::Water] = 0.3;
rate[Component::Disgas] = 0.4;
rate[Component::Vapoil] = 0.5;
return rate;
}
Opm::data::InterRegFlowMap::FlowRates conn_3()
{
using Component = Opm::data::InterRegFlowMap::Component;
auto rate = Opm::data::InterRegFlowMap::FlowRates{};
rate[Component::Oil] = -0.2;
rate[Component::Gas] = -0.4;
rate[Component::Water] = -0.6;
rate[Component::Disgas] = -0.8;
rate[Component::Vapoil] = -1.0;
return rate;
}
}
BOOST_AUTO_TEST_SUITE(InterRegMap)
BOOST_AUTO_TEST_CASE(Basic)
{
using Component = Opm::data::InterRegFlowMap::ReadOnlyWindow::Component;
using Direction = Opm::data::InterRegFlowMap::ReadOnlyWindow::Direction;
auto flowMap = Opm::data::InterRegFlowMap{};
flowMap.addConnection(0, 1, conn_1());
// Invalid source region index
BOOST_CHECK_THROW(flowMap.addConnection(-1, 1, conn_1()),
std::logic_error);
// Invalid destination region index
BOOST_CHECK_THROW(flowMap.addConnection(1, -1, conn_1()),
std::logic_error);
flowMap.compress(2);
BOOST_CHECK_EQUAL(flowMap.numRegions(), 2);
// Invalid source region index
BOOST_CHECK_THROW(std::ignore = flowMap.getInterRegFlows(-1, 0),
std::invalid_argument);
// Invalid destination region index
BOOST_CHECK_THROW(std::ignore = flowMap.getInterRegFlows(0, -1),
std::invalid_argument);
// Invalid region index pair (source == destination)
BOOST_CHECK_THROW(std::ignore = flowMap.getInterRegFlows(0, 0),
std::invalid_argument);
{
auto flows = flowMap.getInterRegFlows(0, 1729);
BOOST_CHECK_MESSAGE(! flows.has_value(),
"Unregistered region pair must NOT have a value");
}
{
auto flows = flowMap.getInterRegFlows(0, 1);
BOOST_REQUIRE_MESSAGE(flows.has_value(),
"Registered region pair must have a value");
const auto& [ iregFlow, sign ] = flows.value();
BOOST_CHECK_EQUAL(sign, 1.0);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil), 1.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas), 2.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water), 3.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas), 4.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil), 5.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Positive), 1.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Positive), 2.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Positive), 3.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Positive), 4.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Positive), 5.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Negative), 0.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Negative), 0.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Negative), 0.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Negative), 0.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Negative), 0.0, 1.0e-6);
}
{
auto flows = flowMap.getInterRegFlows(1, 0);
BOOST_REQUIRE_MESSAGE(flows.has_value(),
"Registered region pair must have a value");
const auto& [ iregFlow, sign ] = flows.value();
BOOST_CHECK_EQUAL(sign, -1.0);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil), 1.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas), 2.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water), 3.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas), 4.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil), 5.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Positive), 1.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Positive), 2.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Positive), 3.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Positive), 4.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Positive), 5.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Negative), 0.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Negative), 0.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Negative), 0.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Negative), 0.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Negative), 0.0, 1.0e-6);
}
}
BOOST_AUTO_TEST_CASE(Basic_Reverse)
{
using Component = Opm::data::InterRegFlowMap::ReadOnlyWindow::Component;
using Direction = Opm::data::InterRegFlowMap::ReadOnlyWindow::Direction;
auto flowMap = Opm::data::InterRegFlowMap{};
flowMap.addConnection(1, 0, conn_1());
flowMap.compress(2);
BOOST_CHECK_EQUAL(flowMap.numRegions(), 2);
{
auto flows = flowMap.getInterRegFlows(0, 1);
BOOST_REQUIRE_MESSAGE(flows.has_value(),
"Registered region pair must have a value");
const auto& [ iregFlow, sign ] = flows.value();
BOOST_CHECK_EQUAL(sign, 1.0);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil), -1.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas), -2.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water), -3.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas), -4.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil), -5.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Positive), 0.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Positive), 0.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Positive), 0.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Positive), 0.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Positive), 0.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Negative), -1.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Negative), -2.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Negative), -3.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Negative), -4.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Negative), -5.0, 1.0e-6);
}
{
auto flows = flowMap.getInterRegFlows(1, 0);
BOOST_REQUIRE_MESSAGE(flows.has_value(),
"Registered region pair must have a value");
const auto& [ iregFlow, sign ] = flows.value();
BOOST_CHECK_EQUAL(sign, -1.0);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil), -1.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas), -2.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water), -3.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas), -4.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil), -5.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Positive), 0.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Positive), 0.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Positive), 0.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Positive), 0.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Positive), 0.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Negative), -1.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Negative), -2.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Negative), -3.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Negative), -4.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Negative), -5.0, 1.0e-6);
}
}
BOOST_AUTO_TEST_CASE(Not_All_Pairs)
{
auto flowMap = Opm::data::InterRegFlowMap{};
flowMap.addConnection(3, 2, conn_1());
flowMap.addConnection(3, 1, conn_1());
flowMap.addConnection(2, 0, conn_1());
flowMap.addConnection(1, 0, conn_1());
flowMap.compress(4);
{
auto flows = flowMap.getInterRegFlows(0, 1);
BOOST_CHECK_MESSAGE(flows.has_value(),
"Registered region pair (0,1) must have a value");
}
{
auto flows = flowMap.getInterRegFlows(0, 2);
BOOST_CHECK_MESSAGE(flows.has_value(),
"Registered region pair (0,2) must have a value");
}
{
auto flows = flowMap.getInterRegFlows(0, 3);
BOOST_CHECK_MESSAGE(! flows.has_value(),
"Unregistered region pair (0,3) must NOT have a value");
}
{
auto flows = flowMap.getInterRegFlows(1, 2);
BOOST_CHECK_MESSAGE(! flows.has_value(),
"Unregistered region pair (1,2) must NOT have a value");
}
{
auto flows = flowMap.getInterRegFlows(1, 3);
BOOST_CHECK_MESSAGE(flows.has_value(),
"Registered region pair (1,3) must have a value");
}
{
auto flows = flowMap.getInterRegFlows(2, 3);
BOOST_CHECK_MESSAGE(flows.has_value(),
"Registered region pair (2,3) must have a value");
}
}
BOOST_AUTO_TEST_CASE(Clear)
{
using Component = Opm::data::InterRegFlowMap::ReadOnlyWindow::Component;
using Direction = Opm::data::InterRegFlowMap::ReadOnlyWindow::Direction;
auto flowMap = Opm::data::InterRegFlowMap{};
flowMap.addConnection(0, 1, conn_1());
flowMap.compress(2);
BOOST_CHECK_EQUAL(flowMap.numRegions(), 2);
flowMap.clear();
BOOST_CHECK_EQUAL(flowMap.numRegions(), 0);
flowMap.addConnection(0, 1, conn_1());
flowMap.compress(2);
BOOST_CHECK_EQUAL(flowMap.numRegions(), 2);
{
auto flows = flowMap.getInterRegFlows(0, 1729);
BOOST_CHECK_MESSAGE(! flows.has_value(),
"Unregistered region pair must NOT have a value");
}
{
auto flows = flowMap.getInterRegFlows(0, 1);
BOOST_REQUIRE_MESSAGE(flows.has_value(),
"Registered region pair must have a value");
const auto& [ iregFlow, sign ] = flows.value();
BOOST_CHECK_EQUAL(sign, 1.0);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil), 1.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas), 2.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water), 3.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas), 4.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil), 5.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Positive), 1.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Positive), 2.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Positive), 3.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Positive), 4.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Positive), 5.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Negative), 0.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Negative), 0.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Negative), 0.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Negative), 0.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Negative), 0.0, 1.0e-6);
}
{
auto flows = flowMap.getInterRegFlows(1, 0);
BOOST_REQUIRE_MESSAGE(flows.has_value(),
"Registered region pair must have a value");
const auto& [ iregFlow, sign ] = flows.value();
BOOST_CHECK_EQUAL(sign, -1.0);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil), 1.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas), 2.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water), 3.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas), 4.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil), 5.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Positive), 1.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Positive), 2.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Positive), 3.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Positive), 4.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Positive), 5.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Negative), 0.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Negative), 0.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Negative), 0.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Negative), 0.0, 1.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Negative), 0.0, 1.0e-6);
}
}
BOOST_AUTO_TEST_CASE(MultiConn_Contrib)
{
using Component = Opm::data::InterRegFlowMap::ReadOnlyWindow::Component;
using Direction = Opm::data::InterRegFlowMap::ReadOnlyWindow::Direction;
auto flowMap = Opm::data::InterRegFlowMap{};
flowMap.addConnection(0, 1, conn_1());
flowMap.addConnection(0, 1, conn_2());
flowMap.addConnection(0, 1, conn_3());
flowMap.addConnection(2, 1, conn_1());
flowMap.addConnection(1, 2, conn_2());
flowMap.addConnection(2, 1, conn_3());
flowMap.compress(4);
{
auto flows = flowMap.getInterRegFlows(0, 1);
BOOST_REQUIRE_MESSAGE(flows.has_value(),
"Registered region pair must have a value");
const auto& [ iregFlow, sign ] = flows.value();
BOOST_CHECK_EQUAL(sign, 1.0);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil), 0.9, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas), 1.8, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water), 2.7, 8.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas), 3.6, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil), 4.5, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Positive), 1.1, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Positive), 2.2, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Positive), 3.3, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Positive), 4.4, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Positive), 5.5, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Negative), -0.2, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Negative), -0.4, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Negative), -0.6, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Negative), -0.8, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Negative), -1.0, 5.0e-6);
}
{
auto flows = flowMap.getInterRegFlows(1, 0);
BOOST_REQUIRE_MESSAGE(flows.has_value(),
"Registered region pair must have a value");
const auto& [ iregFlow, sign ] = flows.value();
BOOST_CHECK_EQUAL(sign, -1.0);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil), 0.9, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas), 1.8, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water), 2.7, 8.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas), 3.6, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil), 4.5, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Positive), 1.1, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Positive), 2.2, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Positive), 3.3, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Positive), 4.4, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Positive), 5.5, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Negative), -0.2, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Negative), -0.4, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Negative), -0.6, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Negative), -0.8, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Negative), -1.0, 5.0e-6);
}
{
auto flows = flowMap.getInterRegFlows(0, 2);
BOOST_CHECK_MESSAGE(! flows.has_value(),
"Registered region pair must have a value");
}
{
auto flows = flowMap.getInterRegFlows(1, 3);
BOOST_CHECK_MESSAGE(! flows.has_value(),
"Registered region pair must have a value");
}
{
auto flows = flowMap.getInterRegFlows(2, 1);
BOOST_REQUIRE_MESSAGE(flows.has_value(),
"Registered region pair must have a value");
const auto& [ iregFlow, sign ] = flows.value();
BOOST_CHECK_EQUAL(sign, -1.0);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil), -0.7, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas), -1.4, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water), -2.1, 8.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas), -2.8, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil), -3.5, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Positive), 0.3, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Positive), 0.6, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Positive), 0.9, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Positive), 1.2, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Positive), 1.5, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Negative), -1.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Negative), -2.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Negative), -3.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Negative), -4.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Negative), -5.0, 5.0e-6);
}
}
BOOST_AUTO_TEST_CASE(Merge_MultiConn)
{
using Component = Opm::data::InterRegFlowMap::ReadOnlyWindow::Component;
using Direction = Opm::data::InterRegFlowMap::ReadOnlyWindow::Direction;
auto flowMap = Opm::data::InterRegFlowMap{};
flowMap.addConnection(0, 1, conn_1());
flowMap.addConnection(0, 1, conn_2());
flowMap.addConnection(0, 1, conn_3());
flowMap.compress(4);
flowMap.addConnection(2, 1, conn_1());
flowMap.addConnection(1, 2, conn_2());
flowMap.addConnection(2, 1, conn_3());
flowMap.compress(4);
flowMap.addConnection(0, 2, conn_1());
flowMap.addConnection(2, 0, conn_2());
flowMap.addConnection(3, 1, conn_3());
flowMap.compress(4);
{
auto flows = flowMap.getInterRegFlows(0, 1);
BOOST_REQUIRE_MESSAGE(flows.has_value(),
"Registered region pair must have a value");
const auto& [ iregFlow, sign ] = flows.value();
BOOST_CHECK_EQUAL(sign, 1.0);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil), 0.9, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas), 1.8, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water), 2.7, 8.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas), 3.6, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil), 4.5, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Positive), 1.1, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Positive), 2.2, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Positive), 3.3, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Positive), 4.4, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Positive), 5.5, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Negative), -0.2, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Negative), -0.4, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Negative), -0.6, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Negative), -0.8, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Negative), -1.0, 5.0e-6);
}
{
auto flows = flowMap.getInterRegFlows(1, 0);
BOOST_REQUIRE_MESSAGE(flows.has_value(),
"Registered region pair must have a value");
const auto& [ iregFlow, sign ] = flows.value();
BOOST_CHECK_EQUAL(sign, -1.0);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil), 0.9, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas), 1.8, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water), 2.7, 8.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas), 3.6, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil), 4.5, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Positive), 1.1, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Positive), 2.2, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Positive), 3.3, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Positive), 4.4, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Positive), 5.5, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Negative), -0.2, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Negative), -0.4, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Negative), -0.6, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Negative), -0.8, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Negative), -1.0, 5.0e-6);
}
{
auto flows = flowMap.getInterRegFlows(2, 1);
BOOST_REQUIRE_MESSAGE(flows.has_value(),
"Registered region pair must have a value");
const auto& [ iregFlow, sign ] = flows.value();
BOOST_CHECK_EQUAL(sign, -1.0);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil), -0.7, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas), -1.4, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water), -2.1, 8.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas), -2.8, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil), -3.5, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Positive), 0.3, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Positive), 0.6, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Positive), 0.9, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Positive), 1.2, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Positive), 1.5, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Negative), -1.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Negative), -2.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Negative), -3.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Negative), -4.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Negative), -5.0, 5.0e-6);
}
{
auto flows = flowMap.getInterRegFlows(2, 0);
BOOST_REQUIRE_MESSAGE(flows.has_value(),
"Registered region pair must have a value");
const auto& [ iregFlow, sign ] = flows.value();
BOOST_CHECK_EQUAL(sign, -1.0);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil), 0.9, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas), 1.8, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water), 2.7, 8.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas), 3.6, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil), 4.5, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Positive), 1.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Positive), 2.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Positive), 3.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Positive), 4.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Positive), 5.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Negative), -0.1, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Negative), -0.2, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Negative), -0.3, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Negative), -0.4, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Negative), -0.5, 5.0e-6);
}
{
auto flows = flowMap.getInterRegFlows(1, 3);
BOOST_CHECK_MESSAGE(flows.has_value(),
"Registered region pair must have a value");
const auto& [ iregFlow, sign ] = flows.value();
BOOST_CHECK_EQUAL(sign, 1.0);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil), 0.2, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas), 0.4, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water), 0.6, 8.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas), 0.8, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil), 1.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Positive), 0.2, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Positive), 0.4, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Positive), 0.6, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Positive), 0.8, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Positive), 1.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Negative), 0.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Negative), 0.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Negative), 0.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Negative), 0.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Negative), 0.0, 5.0e-6);
}
}
BOOST_AUTO_TEST_CASE(LinearBufferReadWrite)
{
using Component = Opm::data::InterRegFlowMap::ReadOnlyWindow::Component;
using Direction = Opm::data::InterRegFlowMap::ReadOnlyWindow::Direction;
auto flowMap1 = Opm::data::InterRegFlowMap{};
flowMap1.addConnection(0, 1, conn_1());
flowMap1.addConnection(0, 1, conn_2());
flowMap1.addConnection(0, 1, conn_3());
flowMap1.addConnection(2, 1, conn_1());
flowMap1.addConnection(1, 2, conn_2());
flowMap1.addConnection(2, 1, conn_3());
flowMap1.addConnection(0, 2, conn_1());
flowMap1.addConnection(2, 0, conn_2());
flowMap1.addConnection(3, 1, conn_3());
flowMap1.compress(4);
auto buffer = MessageBuffer{};
flowMap1.write(buffer);
auto flowMap2 = Opm::data::InterRegFlowMap{};
flowMap2.read(buffer);
flowMap2.compress(4);
{
auto flows = flowMap2.getInterRegFlows(0, 1);
BOOST_REQUIRE_MESSAGE(flows.has_value(),
"Registered region pair must have a value");
const auto& [ iregFlow, sign ] = flows.value();
BOOST_CHECK_EQUAL(sign, 1.0);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil), 0.9, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas), 1.8, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water), 2.7, 8.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas), 3.6, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil), 4.5, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Positive), 1.1, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Positive), 2.2, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Positive), 3.3, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Positive), 4.4, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Positive), 5.5, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Negative), -0.2, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Negative), -0.4, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Negative), -0.6, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Negative), -0.8, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Negative), -1.0, 5.0e-6);
}
{
auto flows = flowMap2.getInterRegFlows(1, 0);
BOOST_REQUIRE_MESSAGE(flows.has_value(),
"Registered region pair must have a value");
const auto& [ iregFlow, sign ] = flows.value();
BOOST_CHECK_EQUAL(sign, -1.0);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil), 0.9, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas), 1.8, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water), 2.7, 8.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas), 3.6, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil), 4.5, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Positive), 1.1, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Positive), 2.2, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Positive), 3.3, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Positive), 4.4, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Positive), 5.5, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Negative), -0.2, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Negative), -0.4, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Negative), -0.6, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Negative), -0.8, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Negative), -1.0, 5.0e-6);
}
{
auto flows = flowMap2.getInterRegFlows(2, 1);
BOOST_REQUIRE_MESSAGE(flows.has_value(),
"Registered region pair must have a value");
const auto& [ iregFlow, sign ] = flows.value();
BOOST_CHECK_EQUAL(sign, -1.0);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil), -0.7, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas), -1.4, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water), -2.1, 8.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas), -2.8, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil), -3.5, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Positive), 0.3, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Positive), 0.6, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Positive), 0.9, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Positive), 1.2, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Positive), 1.5, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Negative), -1.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Negative), -2.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Negative), -3.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Negative), -4.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Negative), -5.0, 5.0e-6);
}
{
auto flows = flowMap2.getInterRegFlows(2, 0);
BOOST_REQUIRE_MESSAGE(flows.has_value(),
"Registered region pair must have a value");
const auto& [ iregFlow, sign ] = flows.value();
BOOST_CHECK_EQUAL(sign, -1.0);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil), 0.9, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas), 1.8, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water), 2.7, 8.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas), 3.6, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil), 4.5, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Positive), 1.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Positive), 2.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Positive), 3.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Positive), 4.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Positive), 5.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Negative), -0.1, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Negative), -0.2, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Negative), -0.3, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Negative), -0.4, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Negative), -0.5, 5.0e-6);
}
{
auto flows = flowMap2.getInterRegFlows(1, 3);
BOOST_CHECK_MESSAGE(flows.has_value(),
"Registered region pair must have a value");
const auto& [ iregFlow, sign ] = flows.value();
BOOST_CHECK_EQUAL(sign, 1.0);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil), 0.2, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas), 0.4, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water), 0.6, 8.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas), 0.8, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil), 1.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Positive), 0.2, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Positive), 0.4, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Positive), 0.6, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Positive), 0.8, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Positive), 1.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Negative), 0.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Negative), 0.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Negative), 0.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Negative), 0.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Negative), 0.0, 5.0e-6);
}
}
BOOST_AUTO_TEST_CASE(LinearBuffer_ManyReadWrite)
{
using Component = Opm::data::InterRegFlowMap::ReadOnlyWindow::Component;
using Direction = Opm::data::InterRegFlowMap::ReadOnlyWindow::Direction;
auto flowMap1 = Opm::data::InterRegFlowMap{};
flowMap1.addConnection(0, 1, conn_1());
flowMap1.addConnection(0, 1, conn_2());
flowMap1.addConnection(0, 1, conn_3());
flowMap1.addConnection(2, 1, conn_1());
flowMap1.addConnection(1, 2, conn_2());
flowMap1.addConnection(2, 1, conn_3());
flowMap1.addConnection(0, 2, conn_1());
flowMap1.addConnection(2, 0, conn_2());
flowMap1.addConnection(3, 1, conn_3());
flowMap1.compress(4);
auto buffer = MessageBuffer{};
flowMap1.write(buffer);
flowMap1.write(buffer);
flowMap1.write(buffer);
flowMap1.write(buffer);
auto flowMap2 = Opm::data::InterRegFlowMap{};
flowMap2.read(buffer);
flowMap2.read(buffer);
flowMap2.read(buffer);
flowMap2.read(buffer);
flowMap2.compress(4);
{
auto flows = flowMap2.getInterRegFlows(0, 1);
BOOST_REQUIRE_MESSAGE(flows.has_value(),
"Registered region pair must have a value");
const auto& [ iregFlow, sign ] = flows.value();
BOOST_CHECK_EQUAL(sign, 1.0);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil), 4 * 0.9, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas), 4 * 1.8, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water), 4 * 2.7, 8.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas), 4 * 3.6, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil), 4 * 4.5, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Positive), 4 * 1.1, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Positive), 4 * 2.2, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Positive), 4 * 3.3, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Positive), 4 * 4.4, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Positive), 4 * 5.5, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Negative), 4 * (-0.2), 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Negative), 4 * (-0.4), 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Negative), 4 * (-0.6), 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Negative), 4 * (-0.8), 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Negative), 4 * (-1.0), 5.0e-6);
}
{
auto flows = flowMap2.getInterRegFlows(1, 0);
BOOST_REQUIRE_MESSAGE(flows.has_value(),
"Registered region pair must have a value");
const auto& [ iregFlow, sign ] = flows.value();
BOOST_CHECK_EQUAL(sign, -1.0);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil), 4 * 0.9, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas), 4 * 1.8, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water), 4 * 2.7, 8.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas), 4 * 3.6, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil), 4 * 4.5, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Positive), 4 * 1.1, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Positive), 4 * 2.2, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Positive), 4 * 3.3, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Positive), 4 * 4.4, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Positive), 4 * 5.5, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Negative), 4 * (-0.2), 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Negative), 4 * (-0.4), 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Negative), 4 * (-0.6), 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Negative), 4 * (-0.8), 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Negative), 4 * (-1.0), 5.0e-6);
}
{
auto flows = flowMap2.getInterRegFlows(2, 1);
BOOST_REQUIRE_MESSAGE(flows.has_value(),
"Registered region pair must have a value");
const auto& [ iregFlow, sign ] = flows.value();
BOOST_CHECK_EQUAL(sign, -1.0);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil), 4 * (-0.7), 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas), 4 * (-1.4), 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water), 4 * (-2.1), 8.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas), 4 * (-2.8), 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil), 4 * (-3.5), 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Positive), 4 * 0.3, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Positive), 4 * 0.6, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Positive), 4 * 0.9, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Positive), 4 * 1.2, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Positive), 4 * 1.5, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Negative), 4 * (-1.0), 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Negative), 4 * (-2.0), 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Negative), 4 * -3.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Negative), 4 * (-4.0), 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Negative), 4 * (-5.0), 5.0e-6);
}
{
auto flows = flowMap2.getInterRegFlows(2, 0);
BOOST_REQUIRE_MESSAGE(flows.has_value(),
"Registered region pair must have a value");
const auto& [ iregFlow, sign ] = flows.value();
BOOST_CHECK_EQUAL(sign, -1.0);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil), 4 * 0.9, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas), 4 * 1.8, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water), 4 * 2.7, 8.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas), 4 * 3.6, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil), 4 * 4.5, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Positive), 4 * 1.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Positive), 4 * 2.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Positive), 4 * 3.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Positive), 4 * 4.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Positive), 4 * 5.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Negative), 4 * (-0.1), 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Negative), 4 * (-0.2), 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Negative), 4 * (-0.3), 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Negative), 4 * (-0.4), 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Negative), 4 * (-0.5), 5.0e-6);
}
{
auto flows = flowMap2.getInterRegFlows(1, 3);
BOOST_CHECK_MESSAGE(flows.has_value(),
"Registered region pair must have a value");
const auto& [ iregFlow, sign ] = flows.value();
BOOST_CHECK_EQUAL(sign, 1.0);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil), 4 * 0.2, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas), 4 * 0.4, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water), 4 * 0.6, 8.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas), 4 * 0.8, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil), 4 * 1.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Positive), 4 * 0.2, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Positive), 4 * 0.4, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Positive), 4 * 0.6, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Positive), 4 * 0.8, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Positive), 4 * 1.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Oil, Direction::Negative), 0.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Gas, Direction::Negative), 0.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Water, Direction::Negative), 0.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Disgas, Direction::Negative), 0.0, 5.0e-6);
BOOST_CHECK_CLOSE(iregFlow.flow(Component::Vapoil, Direction::Negative), 0.0, 5.0e-6);
}
}
BOOST_AUTO_TEST_SUITE_END() // InterRegMap