Output: Add Container for Transporting Guiderate Values

This commit introduces a container

    Opm::data::GuideRateValue

that packages a 'std::array' and 'std::bitset' into that array.
This container is intended as the main vehicle for transporting
per-phase guiderate values (Oil, Gas, Water, and Resvoir Voidage
Volume supported initially) calculated by the simulator to the
output layer.  We support the serialization operations read and
write in order to plug into the collective communications layer used
in the simulator.

Add unit tests to exercise the new container.
This commit is contained in:
Bård Skaflestad
2020-07-03 11:44:45 +02:00
parent dc3dd89e09
commit 4f7d41ee10
3 changed files with 329 additions and 0 deletions

View File

@@ -388,6 +388,7 @@ if(ENABLE_ECL_OUTPUT)
tests/test_AggregateConnectionData.cpp
tests/test_AggregateUDQData.cpp
tests/test_ArrayDimChecker.cpp
tests/test_data_GuideRateValue.cpp
tests/test_EclipseIO.cpp
tests/test_DoubHEAD.cpp
tests/test_InteHEAD.cpp
@@ -778,6 +779,7 @@ if(ENABLE_ECL_OUTPUT)
opm/io/eclipse/rst/well.hpp
opm/output/data/Aquifer.hpp
opm/output/data/Cells.hpp
opm/output/data/GuideRateValue.hpp
opm/output/data/Groups.hpp
opm/output/data/Solution.hpp
opm/output/data/Wells.hpp

View File

@@ -0,0 +1,137 @@
#ifndef OPM_OUTPUT_DATA_GUIDERATEVALUE_HPP
#define OPM_OUTPUT_DATA_GUIDERATEVALUE_HPP
#include <array>
#include <bitset>
#include <cstddef>
#include <stdexcept>
#include <string>
namespace Opm { namespace data {
class GuideRateValue {
public:
enum class Item : std::size_t {
Oil, Gas, Water, ResV,
// -- Must be last enumerator --
NumItems,
};
void clear()
{
this->mask_.reset();
this->value_.fill(0.0);
}
constexpr bool has(const Item p) const
{
const auto i = this->index(p);
return (i < Size) && this->mask_[i];
}
bool operator==(const GuideRateValue& vec) const
{
return (this->mask_ == vec.mask_)
&& (this->value_ == vec.value_);
}
double get(const Item p) const
{
if (! this->has(p)) {
throw std::invalid_argument {
"Request for Unset Item Value for " + this->itemName(p)
};
}
return this->value_[ this->index(p) ];
}
GuideRateValue& set(const Item p, const double value)
{
const auto i = this->index(p);
if (i >= Size) {
throw std::invalid_argument {
"Cannot Assign Item Value for Unsupported Item '"
+ this->itemName(p) + '\''
};
}
this->mask_.set(i);
this->value_[i] = value;
return *this;
}
GuideRateValue& operator+=(const GuideRateValue& rhs)
{
for (auto i = 0*Size; i < Size; ++i) {
if (rhs.mask_[i]) {
this->mask_.set(i);
this->value_[i] += rhs.value_[i];
}
}
return *this;
}
template <class MessageBufferType>
void write(MessageBufferType& buffer) const
{
auto maskrep = this->mask_.to_ullong();
buffer.write(maskrep);
for (const auto& x : this->value_) {
buffer.write(x);
}
}
template <class MessageBufferType>
void read(MessageBufferType& buffer)
{
this->clear();
{
auto mask = 0ull;
buffer.read(mask);
this->mask_ = std::bitset<Size>(mask);
}
for (auto& x : this->value_) {
buffer.read(x);
}
}
private:
enum { Size = static_cast<std::size_t>(Item::NumItems) };
std::bitset<Size> mask_{};
std::array<double, Size> value_{};
constexpr std::size_t index(const Item p) const noexcept
{
return static_cast<std::size_t>(p);
}
std::string itemName(const Item p) const
{
switch (p) {
case Item::Oil: return "Oil";
case Item::Gas: return "Gas";
case Item::Water: return "Water";
case Item::ResV: return "ResV";
case Item::NumItems:
return "Out of bounds (NumItems)";
}
return "Unknown (" + std::to_string(this->index(p)) + ')';
}
};
}} // namespace Opm::data
#endif // OPM_OUTPUT_DATA_GUIDERATEVALUE_HPP

View File

@@ -0,0 +1,190 @@
#define BOOST_TEST_MODULE data_GuideRateValue
#include <boost/test/unit_test.hpp>
#include <opm/output/data/GuideRateValue.hpp>
BOOST_AUTO_TEST_SUITE(GuideRate_Values)
namespace {
class MessageBuffer
{
private:
std::stringstream str_{};
public:
template <class T>
void read(T& value)
{
this->str_.read(reinterpret_cast<char *>(&value), sizeof value);
}
template <class T>
void write(T const& value)
{
this->str_.write(reinterpret_cast<const char *>(&value), sizeof value);
}
void write(const std::string& str)
{
const int size = str.size();
this->write(size);
for (int k = 0; k < size; ++k) {
this->write(str[k]);
}
}
void read(std::string& str)
{
int size = 0;
this->read(size);
str.resize(size);
for (int k = 0; k < size; ++k) {
this->read(str[k]);
}
}
};
}
BOOST_AUTO_TEST_CASE(Construct)
{
using GRValue = ::Opm::data::GuideRateValue;
auto grvalue = GRValue{};
BOOST_CHECK_MESSAGE(! grvalue.has(GRValue::Item::Oil), "Default constructed GuideRateValue must not have Oil");
BOOST_CHECK_MESSAGE(! grvalue.has(GRValue::Item::Gas), "Default constructed GuideRateValue must not have Gas");
BOOST_CHECK_MESSAGE(! grvalue.has(GRValue::Item::Water), "Default constructed GuideRateValue must not have Water");
BOOST_CHECK_MESSAGE(! grvalue.has(GRValue::Item::ResV), "Default constructed GuideRateValue must not have ResV");
BOOST_CHECK_MESSAGE(! grvalue.has(static_cast<GRValue::Item>(1729)),
"Default constructed GuideRateValue must not have out-of-bounds phase 1729");
BOOST_CHECK_MESSAGE(! grvalue.has(static_cast<GRValue::Item>(-1)),
"Default constructed GuideRateValue must not have out-of-bounds phase -1");
}
BOOST_AUTO_TEST_CASE(Set_and_Get)
{
using GRValue = ::Opm::data::GuideRateValue;
auto grvalue = GRValue{};
BOOST_CHECK_THROW(grvalue.get(GRValue::Item::Oil), std::invalid_argument);
BOOST_CHECK_THROW(grvalue.get(static_cast<GRValue::Item>(1729)), std::invalid_argument);
BOOST_CHECK_THROW(grvalue.get(static_cast<GRValue::Item>(-1)), std::invalid_argument);
grvalue.set(GRValue::Item::Oil, 123.456);
grvalue.set(GRValue::Item::Water, -0.98765);
grvalue.set(GRValue::Item::ResV, 567.89);
BOOST_CHECK_THROW(grvalue.set(static_cast<GRValue::Item>(355113), 0.1234),
std::invalid_argument);
BOOST_CHECK_CLOSE(grvalue.get(GRValue::Item::Oil), 123.456, 1.0e-10);
BOOST_CHECK_CLOSE(grvalue.get(GRValue::Item::Water), -0.98765, 1.0e-10);
BOOST_CHECK_CLOSE(grvalue.get(GRValue::Item::ResV), 567.89, 1.0e-10);
BOOST_CHECK_THROW(grvalue.get(GRValue::Item::Gas), std::invalid_argument);
}
BOOST_AUTO_TEST_CASE(Copy_and_Assignment)
{
using GRValue = ::Opm::data::GuideRateValue;
auto grvalue1 = GRValue{};
grvalue1.set(GRValue::Item::Oil, 123.456);
grvalue1.set(GRValue::Item::Water, -0.98765);
grvalue1.set(GRValue::Item::ResV, 567.89);
const auto grvalue2{ grvalue1 };
BOOST_CHECK_CLOSE(grvalue2.get(GRValue::Item::Oil), 123.456, 1.0e-10);
BOOST_CHECK_CLOSE(grvalue2.get(GRValue::Item::Water), -0.98765, 1.0e-10);
BOOST_CHECK_CLOSE(grvalue2.get(GRValue::Item::ResV), 567.89, 1.0e-10);
BOOST_CHECK_THROW(grvalue2.get(GRValue::Item::Gas), std::invalid_argument);
auto grvalue3 = GRValue{};
grvalue3 = grvalue2;
BOOST_CHECK_CLOSE(grvalue3.get(GRValue::Item::Oil), 123.456, 1.0e-10);
BOOST_CHECK_CLOSE(grvalue3.get(GRValue::Item::Water), -0.98765, 1.0e-10);
BOOST_CHECK_CLOSE(grvalue3.get(GRValue::Item::ResV), 567.89, 1.0e-10);
BOOST_CHECK_THROW(grvalue3.get(GRValue::Item::Gas), std::invalid_argument);
}
BOOST_AUTO_TEST_CASE(Addition)
{
using GRValue = ::Opm::data::GuideRateValue;
auto grvalue1 = GRValue{};
grvalue1.set(GRValue::Item::Oil, 123.456);
grvalue1.set(GRValue::Item::ResV, 567.89);
auto grvalue2 = GRValue{};
grvalue2.set(GRValue::Item::Water, -0.98765);
grvalue2.set(GRValue::Item::Oil, 123.321);
grvalue1 += grvalue2;
BOOST_CHECK_MESSAGE(grvalue1.has(GRValue::Item::Water),
"Operator '+=' must assign unset items");
BOOST_CHECK_CLOSE(grvalue1.get(GRValue::Item::Oil), 246.777, 1.0e-10);
BOOST_CHECK_CLOSE(grvalue1.get(GRValue::Item::Water), -0.98765, 1.0e-10);
BOOST_CHECK_CLOSE(grvalue1.get(GRValue::Item::ResV), 567.89, 1.0e-10);
}
BOOST_AUTO_TEST_CASE(Clear)
{
using GRValue = ::Opm::data::GuideRateValue;
auto grvalue = GRValue{};
grvalue.set(GRValue::Item::Oil, 123.456);
grvalue.set(GRValue::Item::Water, -0.98765);
grvalue.set(GRValue::Item::ResV, 567.89);
grvalue.clear();
BOOST_CHECK_MESSAGE(! grvalue.has(GRValue::Item::Oil), "Cleared GuideRateValue must not have Oil");
BOOST_CHECK_MESSAGE(! grvalue.has(GRValue::Item::Gas), "Cleared GuideRateValue must not have Gas");
BOOST_CHECK_MESSAGE(! grvalue.has(GRValue::Item::Water), "Cleared GuideRateValue must not have Water");
BOOST_CHECK_MESSAGE(! grvalue.has(GRValue::Item::ResV), "Cleared GuideRateValue must not have ResV");
}
BOOST_AUTO_TEST_CASE(Serialize_ReadWrite)
{
using GRValue = ::Opm::data::GuideRateValue;
auto grvalue1 = GRValue{};
grvalue1.set(GRValue::Item::Oil, 123.456);
grvalue1.set(GRValue::Item::Water, -0.98765);
grvalue1.set(GRValue::Item::ResV, 567.89);
auto buffer = MessageBuffer{};
grvalue1.write(buffer);
auto grvalue2 = GRValue{};
grvalue2.read(buffer);
BOOST_CHECK_MESSAGE( grvalue2.has(GRValue::Item::Oil), "Serialized GuideRateValue must have Oil");
BOOST_CHECK_MESSAGE(! grvalue2.has(GRValue::Item::Gas), "Serialized GuideRateValue must NOT have Gas");
BOOST_CHECK_MESSAGE( grvalue2.has(GRValue::Item::Water), "Serialized GuideRateValue must have Water");
BOOST_CHECK_MESSAGE( grvalue2.has(GRValue::Item::ResV), "Serialized GuideRateValue must have Voidage");
BOOST_CHECK_MESSAGE(! grvalue2.has(static_cast<GRValue::Item>(1729)),
"Serialized GuideRateValue must not have out-of-bounds phase 1729");
BOOST_CHECK_MESSAGE(! grvalue2.has(static_cast<GRValue::Item>(-1)),
"Serialized GuideRateValue must not have out-of-bounds phase -1");
BOOST_CHECK_CLOSE(grvalue2.get(GRValue::Item::Oil), 123.456, 1.0e-10);
BOOST_CHECK_CLOSE(grvalue2.get(GRValue::Item::Water), -0.98765, 1.0e-10);
BOOST_CHECK_CLOSE(grvalue2.get(GRValue::Item::ResV), 567.89, 1.0e-10);
BOOST_CHECK_THROW(grvalue2.get(GRValue::Item::Gas), std::invalid_argument);
BOOST_CHECK_MESSAGE(grvalue1 == grvalue2, "Serialized GuideRateValue must equal its original value");
}
BOOST_AUTO_TEST_SUITE_END()