Use std::variant<> to hold ICD alternatives

This commit is contained in:
Joakim Hove
2020-05-29 08:12:17 +02:00
parent b73f4aa5dc
commit b1250c2f7a
5 changed files with 125 additions and 104 deletions

View File

@@ -21,13 +21,14 @@
#define SEGMENT_HPP_HEADER_INCLUDED
#include <optional>
#include <memory>
#include <variant>
#include <vector>
namespace Opm {
class SICD;
class Valve;
#include <opm/parser/eclipse/EclipseState/Schedule/MSW/Valve.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/MSW/SICD.hpp>
namespace Opm {
namespace RestartIO {
struct RstSegment;
}
@@ -35,6 +36,29 @@ namespace Opm {
namespace Opm {
/*
The current serialization of std::variant<> requires that all the types in
the variant have serializeOp() method, that is why this RegularSegment
type is introduced, ideally the icd variant should just have
std::monostate to represent the regular non ICD segment.
*/
struct RegularSegment : std::monostate {
template<class Serializer>
void serializeOp(Serializer& serializer) {
}
static RegularSegment serializeObject() {
return RegularSegment();
}
bool operator==(const RegularSegment& ) {
return true;
}
};
class Segment {
public:
@@ -51,7 +75,7 @@ namespace Opm {
Segment(const Segment& src, double new_depth, double new_length);
Segment(const Segment& src, double new_volume);
Segment(int segment_number_in, int branch_in, int outlet_segment_in, double length_in, double depth_in,
double internal_diameter_in, double roughness_in, double cross_area_in, double volume_in, bool data_ready_in, SegmentType segment_type_in);
double internal_diameter_in, double roughness_in, double cross_area_in, double volume_in, bool data_ready_in);
Segment(const RestartIO::RstSegment& rst_segment);
static Segment serializeObject();
@@ -80,7 +104,7 @@ namespace Opm {
bool operator==( const Segment& ) const;
bool operator!=( const Segment& ) const;
const std::shared_ptr<SICD>& spiralICD() const;
const SICD& spiralICD() const;
const Valve* valve() const;
void updatePerfLength(double perf_length);
@@ -89,6 +113,21 @@ namespace Opm {
void updateValve(const Valve& valve);
void addInletSegment(const int segment_number);
bool isRegular() const
{
return std::holds_alternative<RegularSegment>(this->m_icd);
}
inline bool isSpiralICD() const
{
return std::holds_alternative<SICD>(this->m_icd);
}
inline bool isValve() const
{
return std::holds_alternative<Valve>(this->m_icd);
}
template<class Serializer>
void serializeOp(Serializer& serializer)
{
@@ -104,9 +143,7 @@ namespace Opm {
serializer(m_volume);
serializer(m_data_ready);
serializer(m_perf_length);
serializer(m_segment_type);
serializer(m_spiral_icd);
serializer(m_valve);
serializer(m_icd);
}
private:
@@ -157,17 +194,7 @@ namespace Opm {
bool m_data_ready;
std::optional<double> m_perf_length;
// indicate the type of the segment
// regular, spiral ICD, or Valve.
SegmentType m_segment_type;
// information related to SpiralICD. It is nullptr for segments are not
// spiral ICD type
std::shared_ptr<SICD> m_spiral_icd;
// information related to sub-critical valve. It is nullptr for segments are not
// of type of Valve
std::shared_ptr<Valve> m_valve;
std::variant<RegularSegment, SICD, Valve> m_icd;
// We are not handling the length of segment projected onto the X-axis and Y-axis.
// They are not used in the simulations and we are not supporting the plotting.
@@ -175,20 +202,6 @@ namespace Opm {
// while they are not supported by the keyword at the moment.
};
inline bool isRegular(const Segment& segment)
{
return segment.segmentType() == Segment::SegmentType::REGULAR;
}
inline bool isSpiralICD(const Segment& segment)
{
return segment.segmentType() == Segment::SegmentType::SICD;
}
inline bool isValve(const Segment& segment)
{
return segment.segmentType() == Segment::SegmentType::VALVE;
}
}
#endif

View File

@@ -424,8 +424,8 @@ namespace {
VectorItems::ISeg::index;
const auto& sicd = segment.spiralICD();
iSeg[baseIndex + Ix::ICDScalingMode] = sicd->methodFlowScaling();
iSeg[baseIndex + Ix::ICDOpenShutFlag] = sicd->ecl_status();
iSeg[baseIndex + Ix::ICDScalingMode] = sicd.methodFlowScaling();
iSeg[baseIndex + Ix::ICDOpenShutFlag] = sicd.ecl_status();
}
template <class ISegArray>
@@ -433,7 +433,7 @@ namespace {
const std::size_t baseIndex,
ISegArray& iSeg)
{
if (isSpiralICD(segment)) {
if (segment.isSpiralICD()) {
assignSpiralICDCharacteristics(segment, baseIndex, iSeg);
}
}
@@ -474,7 +474,7 @@ namespace {
iSeg[iS + 8] = seg_reorder[ind];
iSeg[iS + Ix::SegmentType] = segment.ecl_type_id();
if (! isRegular(segment)) {
if (! segment.isRegular()) {
assignSegmentTypeCharacteristics(segment, iS, iSeg);
}
}
@@ -577,27 +577,27 @@ namespace {
const auto& sicd = segment.spiralICD();
rSeg[baseIndex + Ix::DeviceBaseStrength] =
usys.from_si(M::icd_strength, sicd->strength());
usys.from_si(M::icd_strength, sicd.strength());
rSeg[baseIndex + Ix::CalibrFluidDensity] =
usys.from_si(M::density, sicd->densityCalibration());
usys.from_si(M::density, sicd.densityCalibration());
rSeg[baseIndex + Ix::CalibrFluidViscosity] =
usys.from_si(M::viscosity, sicd->viscosityCalibration());
usys.from_si(M::viscosity, sicd.viscosityCalibration());
rSeg[baseIndex + Ix::CriticalWaterFraction] = sicd->criticalValue();
rSeg[baseIndex + Ix::CriticalWaterFraction] = sicd.criticalValue();
rSeg[baseIndex + Ix::TransitionRegWidth] =
sicd->widthTransitionRegion();
sicd.widthTransitionRegion();
rSeg[baseIndex + Ix::MaxEmulsionRatio] =
sicd->maxViscosityRatio();
sicd.maxViscosityRatio();
rSeg[baseIndex + Ix::MaxValidFlowRate] =
usys.from_si(M::geometric_volume_rate, sicd->maxAbsoluteRate());
usys.from_si(M::geometric_volume_rate, sicd.maxAbsoluteRate());
rSeg[baseIndex + Ix::ICDLength] =
usys.from_si(M::length, sicd->length());
usys.from_si(M::length, sicd.length());
}
template <class RSegArray>
@@ -606,11 +606,11 @@ namespace {
const int baseIndex,
RSegArray& rSeg)
{
if (isSpiralICD(segment)) {
if (segment.isSpiralICD()) {
assignSpiralICDCharacteristics(segment, usys, baseIndex, rSeg);
}
if (isValve(segment)) {
if (segment.isValve()) {
assignValveCharacteristics(segment, usys, baseIndex, rSeg);
}
}
@@ -760,7 +760,7 @@ namespace {
rSeg[iS + Ix::item110] = 1.0;
rSeg[iS + Ix::item111] = 1.0;
if (! isRegular(segment)) {
if (! segment.isRegular()) {
assignSegmentTypeCharacteristics(segment, units, iS, rSeg);
}
}

View File

@@ -71,10 +71,9 @@ namespace {
m_roughness(if_invalid_value(rst_segment.roughness)),
m_cross_area(if_invalid_value(rst_segment.area)),
m_volume(rst_segment.volume),
m_data_ready(true),
m_segment_type(rst_segment.segment_type)
m_data_ready(true)
{
if (this->m_segment_type == SegmentType::SICD) {
if (rst_segment.segment_type == SegmentType::SICD) {
double scalingFactor = -1; // The scaling factor will be and updated from the simulator.
SICD icd(rst_segment.base_strength,
@@ -92,7 +91,7 @@ namespace {
this->updateSpiralICD(icd);
}
if (this->m_segment_type == SegmentType::VALVE) {
if (rst_segment.segment_type == SegmentType::VALVE) {
/*
These three variables are currently not stored in the restart
file; here we initialize with the default values, but if they have
@@ -121,7 +120,7 @@ namespace {
Segment::Segment(int segment_number_in, int branch_in, int outlet_segment_in, double length_in, double depth_in,
double internal_diameter_in, double roughness_in, double cross_area_in,
double volume_in, bool data_ready_in, SegmentType segment_type_in)
double volume_in, bool data_ready_in)
: m_segment_number(segment_number_in),
m_branch(branch_in),
m_outlet_segment(outlet_segment_in),
@@ -131,8 +130,7 @@ namespace {
m_roughness(roughness_in),
m_cross_area(cross_area_in),
m_volume(volume_in),
m_data_ready(data_ready_in),
m_segment_type(segment_type_in)
m_data_ready(data_ready_in)
{
}
@@ -170,10 +168,7 @@ namespace {
result.m_cross_area = 10.0;
result.m_volume = 11.0;
result.m_data_ready = true;
result.m_segment_type = SegmentType::SICD;
result.m_spiral_icd = std::make_shared<SICD>(SICD::serializeObject());
result.m_valve = std::make_shared<Valve>(Valve::serializeObject());
result.m_icd = SICD::serializeObject();
return result;
}
@@ -228,7 +223,16 @@ namespace {
}
Segment::SegmentType Segment::segmentType() const {
return m_segment_type;
if (this->isRegular())
return SegmentType::REGULAR;
if (this->isSpiralICD())
return SegmentType::SICD;
if (this->isValve())
return SegmentType::VALVE;
throw std::logic_error("This just should not happen ");
}
const std::vector<int>& Segment::inletSegments() const {
@@ -254,6 +258,7 @@ namespace {
&& this->m_cross_area == rhs.m_cross_area
&& this->m_volume == rhs.m_volume
&& this->m_perf_length == rhs.m_perf_length
&& this->m_icd == rhs.m_icd
&& this->m_data_ready == rhs.m_data_ready;
}
@@ -262,46 +267,48 @@ namespace {
}
void Segment::updateSpiralICD(const SICD& spiral_icd) {
m_segment_type = SegmentType::SICD;
m_spiral_icd = std::make_shared<SICD>(spiral_icd);
this->m_icd = spiral_icd;
}
const std::shared_ptr<SICD>& Segment::spiralICD() const {
return m_spiral_icd;
const SICD& Segment::spiralICD() const {
return std::get<SICD>(this->m_icd);
}
void Segment::updateValve(const Valve& valve) {
if (valve.pipeAdditionalLength() < 0)
throw std::logic_error("Bug in handling of pipe length for valves");
void Segment::updateValve(const Valve& input_valve, const double segment_length) {
// we need to update some values for the vale
auto valve_ptr = std::make_shared<Valve>(valve);
auto valve = input_valve;
if (valve_ptr->pipeDiameter() < 0.) {
valve_ptr->setPipeDiameter(m_internal_diameter);
if (valve.pipeAdditionalLength() < 0.) { // defaulted for this
valve.setPipeAdditionalLength(segment_length);
}
if (valve.pipeDiameter() < 0.) {
valve.setPipeDiameter(m_internal_diameter);
} else {
this->m_internal_diameter = valve_ptr->pipeDiameter();
this->m_internal_diameter = valve.pipeDiameter();
}
if (valve_ptr->pipeRoughness() < 0.) {
valve_ptr->setPipeRoughness(m_roughness);
if (valve.pipeRoughness() < 0.) {
valve.setPipeRoughness(m_roughness);
} else {
this->m_roughness = valve_ptr->pipeRoughness();
this->m_roughness = valve.pipeRoughness();
}
if (valve_ptr->pipeCrossArea() < 0.) {
valve_ptr->setPipeCrossArea(m_cross_area);
if (valve.pipeCrossArea() < 0.) {
valve.setPipeCrossArea(m_cross_area);
} else {
this->m_cross_area = valve_ptr->pipeCrossArea();
this->m_cross_area = valve.pipeCrossArea();
}
if (valve_ptr->conMaxCrossArea() < 0.) {
valve_ptr->setConMaxCrossArea(valve_ptr->pipeCrossArea());
if (valve.conMaxCrossArea() < 0.) {
valve.setConMaxCrossArea(valve.pipeCrossArea());
}
this->m_valve = valve_ptr;
m_segment_type = SegmentType::VALVE;
this->m_icd= valve;
}
@@ -324,11 +331,12 @@ namespace {
const Valve* Segment::valve() const {
return m_valve.get();
return &std::get<Valve>(this->m_icd);
//return m_valve.get();
}
int Segment::ecl_type_id() const {
switch (this->m_segment_type) {
switch (this->segmentType()) {
case SegmentType::REGULAR:
return -1;
case SegmentType::SICD:

View File

@@ -142,12 +142,12 @@ namespace Opm {
if (length_depth_type == LengthDepth::INC) {
m_segments.emplace_back( 1, 1, 0, 0., 0.,
invalid_value, invalid_value, invalid_value,
volume_top, false , Segment::SegmentType::REGULAR);
volume_top, false);
} else if (length_depth_type == LengthDepth::ABS) {
m_segments.emplace_back( 1, 1, 0, length_top, depth_top,
invalid_value, invalid_value, invalid_value,
volume_top, true , Segment::SegmentType::REGULAR);
volume_top, true);
}
// read all the information out from the DECK first then process to get all the required information
@@ -207,13 +207,13 @@ namespace Opm {
if (length_depth_type == LengthDepth::INC) {
m_segments.emplace_back( i, branch, outlet_segment, segment_length, depth_change,
diameter, roughness, area, volume, false , Segment::SegmentType::REGULAR);
diameter, roughness, area, volume, false);
} else if (i == segment2) {
m_segments.emplace_back( i, branch, outlet_segment, segment_length, depth_change,
diameter, roughness, area, volume, true , Segment::SegmentType::REGULAR);
diameter, roughness, area, volume, true);
} else {
m_segments.emplace_back( i, branch, outlet_segment, invalid_value, invalid_value,
diameter, roughness, area, volume, false , Segment::SegmentType::REGULAR);
diameter, roughness, area, volume, false);
}
}
}

View File

@@ -126,30 +126,30 @@ BOOST_AUTO_TEST_CASE(MultisegmentWellTest) {
const auto& sicd_vector = it->second;
BOOST_CHECK_EQUAL(1U, sicd_vector.size());
const int segment_number = sicd_vector[0].first;
const Opm::SICD& sicd = sicd_vector[0].second;
const Opm::SICD& sicd0 = sicd_vector[0].second;
BOOST_CHECK_EQUAL(8, segment_number);
Opm::Segment segment = segment_set.getFromSegmentNumber(segment_number);
segment.updateSpiralICD(sicd);
segment.updateSpiralICD(sicd0);
BOOST_CHECK(Opm::Segment::SegmentType::SICD==segment.segmentType());
const std::shared_ptr<Opm::SICD> sicd_ptr = segment.spiralICD();
BOOST_CHECK_GT(sicd_ptr->maxAbsoluteRate(), 1.e99);
BOOST_CHECK(sicd_ptr->status()==Opm::ICDStatus::SHUT);
auto sicd = segment.spiralICD();
BOOST_CHECK_GT(sicd.maxAbsoluteRate(), 1.e99);
BOOST_CHECK(sicd.status()==Opm::ICDStatus::SHUT);
// 0.002 bars*day*day/Volume^2
BOOST_CHECK_EQUAL(sicd_ptr->strength(), 0.002*1.e5*86400.*86400.);
BOOST_CHECK_EQUAL(sicd_ptr->length(), -0.7);
BOOST_CHECK_EQUAL(sicd_ptr->densityCalibration(), 1000.25);
BOOST_CHECK_EQUAL(sicd.strength(), 0.002*1.e5*86400.*86400.);
BOOST_CHECK_EQUAL(sicd.length(), -0.7);
BOOST_CHECK_EQUAL(sicd.densityCalibration(), 1000.25);
// 1.45 cp
BOOST_CHECK_EQUAL(sicd_ptr->viscosityCalibration(), 1.45 * 0.001);
BOOST_CHECK_EQUAL(sicd_ptr->criticalValue(), 0.6);
BOOST_CHECK_EQUAL(sicd_ptr->widthTransitionRegion(), 0.05);
BOOST_CHECK_EQUAL(sicd_ptr->maxViscosityRatio(), 5.0);
BOOST_CHECK_EQUAL(sicd_ptr->methodFlowScaling(), -1);
BOOST_CHECK_EQUAL(sicd.viscosityCalibration(), 1.45 * 0.001);
BOOST_CHECK_EQUAL(sicd.criticalValue(), 0.6);
BOOST_CHECK_EQUAL(sicd.widthTransitionRegion(), 0.05);
BOOST_CHECK_EQUAL(sicd.maxViscosityRatio(), 5.0);
BOOST_CHECK_EQUAL(sicd.methodFlowScaling(), -1);
// the scaling factor has not been updated properly, so it will throw
BOOST_CHECK_THROW(sicd_ptr->scalingFactor(), std::runtime_error);
BOOST_CHECK_THROW(sicd.scalingFactor(), std::runtime_error);
const int outlet_segment_number = segment.outletSegment();
const double outlet_segment_length = segment_set.segmentLength(outlet_segment_number);
@@ -157,11 +157,11 @@ BOOST_AUTO_TEST_CASE(MultisegmentWellTest) {
const Opm::Connection& connection = new_connection_set.getFromIJK(15, 0, 1);
const auto& perf_range = connection.perf_range();
const auto connection_length = perf_range->second - perf_range->first;
sicd_ptr->updateScalingFactor(outlet_segment_length, connection_length);
sicd.updateScalingFactor(outlet_segment_length, connection_length);
// updated, so it should not throw
BOOST_CHECK_NO_THROW(sicd_ptr->scalingFactor());
BOOST_CHECK_EQUAL(0.7, sicd_ptr->scalingFactor());
BOOST_CHECK_NO_THROW(sicd.scalingFactor());
BOOST_CHECK_EQUAL(0.7, sicd.scalingFactor());
BOOST_CHECK_EQUAL(7U, new_connection_set.size());