Merge pull request #3696 from bska/msw-rst-fixup

Fix Multiple Issues in MSW Output Array Creation Logic
This commit is contained in:
Bård Skaflestad 2023-10-04 09:55:02 +02:00 committed by GitHub
commit a8d3dff2a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 846 additions and 303 deletions

View File

@ -69,6 +69,7 @@ namespace Opm {
// Status: OPEN or SHUT
ICDStatus status() const;
int ecl_status() const;
void setConMaxCrossArea(const double area);

View File

@ -36,39 +36,18 @@ namespace Opm {
namespace Opm { namespace RestartIO { namespace Helpers {
struct BranchSegmentPar {
int outletS;
int noSegInBranch;
int firstSeg;
int lastSeg;
int branch;
};
struct SegmentSetSourceSinkTerms {
std::vector<double> qosc;
std::vector<double> qwsc;
std::vector<double> qgsc;
};
struct SegmentSetFlowRates {
std::vector<double> sofr;
std::vector<double> swfr;
std::vector<double> sgfr;
};
class AggregateMSWData
{
public:
explicit AggregateMSWData(const std::vector<int>& inteHead);
void captureDeclaredMSWData(const Opm::Schedule& sched,
const std::size_t rptStep,
const Opm::UnitSystem& units,
const std::vector<int>& inteHead,
const Opm::EclipseGrid& grid,
const Opm::SummaryState& smry,
const Opm::data::Wells& wr
);
void captureDeclaredMSWData(const Opm::Schedule& sched,
const std::size_t rptStep,
const Opm::UnitSystem& units,
const std::vector<int>& inteHead,
const Opm::EclipseGrid& grid,
const Opm::SummaryState& smry,
const Opm::data::Wells& wr);
/// Retrieve Integer Multisegment well data Array.
const std::vector<int>& getISeg() const
@ -94,7 +73,6 @@ namespace Opm { namespace RestartIO { namespace Helpers {
return this->iLBR_.data();
}
private:
/// Aggregate 'ISEG' array (Integer) for all multisegment wells
WindowedArray<int> iSeg_;
@ -106,8 +84,7 @@ namespace Opm { namespace RestartIO { namespace Helpers {
WindowedArray<int> iLBS_;
/// Aggregate 'ILBR' array (Integer) for all multisegment wells
WindowedArray<int> iLBR_;
WindowedMatrix<int> iLBR_;
};
}}} // Opm::RestartIO::Helpers

View File

@ -40,6 +40,16 @@ namespace Opm { namespace RestartIO { namespace Helpers { namespace VectorItems
} // ISeg
namespace ILbr {
enum index : std::vector<int>::size_type {
OutletSegment = 0, // Branch's outlet segment (one-based)
NumBranchSegments = 1, // Number of segments on branch
FirstSegment = 2, // First segment on branch (kick-off, heel)
LastSegment = 3, // Last segment on branch (toe)
KickOffDiscoveryOffset = 4, // Segment traversal order at which this branch was encountered
};
} // ILbr
namespace RSeg {
enum index : std::vector<double>::size_type {
DistOutlet = 0, // Segment's distance to outlet

View File

@ -18,9 +18,11 @@
*/
#include <opm/input/eclipse/Schedule/MSW/Valve.hpp>
#include <opm/input/eclipse/Deck/DeckRecord.hpp>
#include <opm/input/eclipse/Deck/DeckKeyword.hpp>
#include "icd_convert.hpp"
namespace Opm {
@ -130,6 +132,11 @@ namespace Opm {
return m_status;
}
int Valve::ecl_status() const
{
return to_int(this->status());
}
double Valve::conFlowCoefficient() const {
return m_con_flow_coeff;
}

View File

@ -18,31 +18,43 @@
*/
#include <opm/output/eclipse/AggregateMSWData.hpp>
#include <opm/output/eclipse/InteHEAD.hpp>
#include <opm/output/eclipse/VectorItems/msw.hpp>
#include <opm/input/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <opm/input/eclipse/Schedule/MSW/AICD.hpp>
#include <opm/input/eclipse/Schedule/MSW/Segment.hpp>
#include <opm/input/eclipse/Schedule/MSW/SICD.hpp>
#include <opm/input/eclipse/Schedule/MSW/Valve.hpp>
#include <opm/input/eclipse/Schedule/SummaryState.hpp>
#include <opm/input/eclipse/Schedule/MSW/WellSegments.hpp>
#include <opm/input/eclipse/Schedule/Schedule.hpp>
#include <opm/input/eclipse/Schedule/SummaryState.hpp>
#include <opm/input/eclipse/Schedule/Well/Connection.hpp>
#include <opm/input/eclipse/Schedule/Well/Well.hpp>
#include <opm/input/eclipse/Schedule/Well/WellConnections.hpp>
#include <opm/input/eclipse/Schedule/MSW/Segment.hpp>
#include <opm/input/eclipse/Schedule/MSW/WellSegments.hpp>
#include <opm/input/eclipse/Units/UnitSystem.hpp>
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstddef>
#include <cstring>
#include <exception>
#include <deque>
#include <functional>
#include <iostream>
#include <limits>
#include <optional>
#include <queue>
#include <stdexcept>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include <fmt/format.h>
// #####################################################################
// Class Opm::RestartIO::Helpers::AggregateMSWData
@ -50,6 +62,18 @@
namespace {
struct SegmentSetSourceSinkTerms {
std::vector<double> qosc;
std::vector<double> qwsc;
std::vector<double> qgsc;
};
struct SegmentSetFlowRates {
std::vector<double> sofr;
std::vector<double> swfr;
std::vector<double> sgfr;
};
std::size_t nswlmx(const std::vector<int>& inteHead)
{
// inteHead(175) = NSWLMX
@ -75,56 +99,38 @@ namespace {
}
std::vector<std::size_t>
inflowSegmentsIndex(const Opm::WellSegments& segSet, const std::size_t& segIndex) {
const auto& segNumber = segSet[segIndex].segmentNumber();
inflowSegmentsIndex(const Opm::WellSegments& segSet,
const std::size_t segIndex)
{
std::vector<std::size_t> inFlowSegInd;
const auto segNumber = segSet[segIndex].segmentNumber();
for (std::size_t ind = 0; ind < segSet.size(); ind++) {
const auto& i_outletSeg = segSet[ind].outletSegment();
if (segNumber == i_outletSeg) {
inFlowSegInd.push_back(ind);
}
}
return inFlowSegInd;
}
Opm::RestartIO::Helpers::BranchSegmentPar
getBranchSegmentParam(const Opm::WellSegments& segSet, const int branch)
std::vector<std::size_t>
segmentIndFromOrderedSegmentInd(const Opm::WellSegments& segSet,
const std::vector<std::size_t>& ordSegNo)
{
int noSegInBranch = 0;
int firstSeg = -1;
int lastSeg = -1;
int outletS = 0;
for (std::size_t segInd = 0; segInd < segSet.size(); segInd++) {
const auto& segNo = segSet[segInd].segmentNumber();
const auto& i_branch = segSet[segInd].branchNumber();
const auto& i_outS = segSet[segInd].outletSegment();
if (i_branch == branch) {
noSegInBranch +=1;
if (firstSeg < 0) {
firstSeg = segNo;
outletS = (branch > 1) ? i_outS : 0;
}
lastSeg = segNo;
}
}
return {
outletS,
noSegInBranch,
firstSeg,
lastSeg,
branch
};
}
std::vector <std::size_t> segmentIndFromOrderedSegmentInd(const Opm::WellSegments& segSet, const std::vector<std::size_t>& ordSegNo) {
std::vector <std::size_t> sNFOSN (segSet.size(),0);
for (std::size_t segInd = 0; segInd < segSet.size(); segInd++) {
sNFOSN[ordSegNo[segInd]] = segInd;
}
return sNFOSN;
}
std::vector<std::size_t> segmentOrder(const Opm::WellSegments& segSet, const std::size_t segIndex) {
std::vector<std::size_t>
segmentOrder(const Opm::WellSegments& segSet,
const std::size_t segIndex)
{
std::vector<std::size_t> ordSegNumber;
std::vector<std::size_t> segIndCB;
// Store "heel" segment since that will not always be at the end of the list
@ -177,16 +183,18 @@ namespace {
return ordSegNumber;
}
std::vector<std::size_t> segmentOrder(const Opm::WellSegments& segSet) {
std::vector<std::size_t>
segmentOrder(const Opm::WellSegments& segSet)
{
return segmentOrder(segSet, 0);
}
/// Accumulate connection flow rates (surface conditions) to their connecting segment.
Opm::RestartIO::Helpers::SegmentSetSourceSinkTerms
getSegmentSetSSTerms(const Opm::WellSegments& segSet,
SegmentSetSourceSinkTerms
getSegmentSetSSTerms(const Opm::WellSegments& segSet,
const std::vector<Opm::data::Connection>& rateConns,
const Opm::WellConnections& welConns,
const Opm::UnitSystem& units)
const Opm::WellConnections& welConns,
const Opm::UnitSystem& units)
{
std::vector<double> qosc (segSet.size(), 0.);
std::vector<double> qwsc (segSet.size(), 0.);
@ -232,11 +240,11 @@ namespace {
};
}
Opm::RestartIO::Helpers::SegmentSetFlowRates
getSegmentSetFlowRates(const Opm::WellSegments& segSet,
SegmentSetFlowRates
getSegmentSetFlowRates(const Opm::WellSegments& segSet,
const std::vector<Opm::data::Connection>& rateConns,
const Opm::WellConnections& welConns,
const Opm::UnitSystem& units)
const Opm::WellConnections& welConns,
const Opm::UnitSystem& units)
{
std::vector<double> sofr (segSet.size(), 0.);
std::vector<double> swfr (segSet.size(), 0.);
@ -273,31 +281,6 @@ namespace {
};
}
std::vector<std::size_t> SegmentSetBranches(const Opm::WellSegments& segSet) {
std::vector<std::size_t> branches;
for (std::size_t segInd = 0; segInd < segSet.size(); segInd++) {
const auto& i_branch = segSet[segInd].branchNumber();
if (std::find(branches.begin(), branches.end(), i_branch) == branches.end()) {
branches.push_back(i_branch);
}
}
return branches;
}
int firstSegmentInBranch(const Opm::WellSegments& segSet, const int branch) {
int firstSegInd = 0;
std::size_t segInd = 0;
while ((segInd < segSet.size()) && (firstSegInd == 0)) {
const auto& i_branch = segSet[segInd].branchNumber();
if (branch == i_branch) {
firstSegInd = segInd;
}
segInd+=1;
}
return firstSegInd;
}
int noConnectionsSegment(const Opm::WellConnections& compSet,
const Opm::WellSegments& segSet,
const std::size_t segIndex)
@ -332,9 +315,12 @@ namespace {
return sumConn;
}
int noInFlowBranches(const Opm::WellSegments& segSet, std::size_t segIndex) {
const auto& segNumber = segSet[segIndex].segmentNumber();
const auto& branch = segSet[segIndex].branchNumber();
int noInFlowBranches(const Opm::WellSegments& segSet,
const std::size_t segIndex)
{
const auto segNumber = segSet[segIndex].segmentNumber();
const auto branch = segSet[segIndex].branchNumber();
int noIFBr = 0;
for (std::size_t ind = 0; ind < segSet.size(); ind++) {
const auto& o_segNum = segSet[ind].outletSegment();
@ -343,12 +329,16 @@ namespace {
noIFBr+=1;
}
}
return noIFBr;
}
//find the number of inflow branch-segments (segments that has a branch) from the
// first segment to the current segment for segments that has at least one inflow branch
// Segments with no inflow branches get the value zero
int sumNoInFlowBranches(const Opm::WellSegments& segSet, const std::size_t& segIndex) {
int sumNoInFlowBranches(const Opm::WellSegments& segSet,
const std::size_t segIndex)
{
int sumIFB = 0;
//auto segInd = segIndex;
for (int segInd = static_cast<int>(segIndex); segInd >= 0; segInd--) {
@ -362,15 +352,19 @@ namespace {
}
}
}
// check if the segment has inflow branches - if yes return sumIFB else return zero
return (noInFlowBranches(segSet, segIndex) >= 1)
return (noInFlowBranches(segSet, segIndex) >= 1)
? sumIFB : 0;
}
int inflowSegmentCurBranch(const std::string& wname,
const Opm::WellSegments& segSet,
const std::size_t segIndex)
{
const auto branch = segSet[segIndex].branchNumber();
const auto segNumber = segSet[segIndex].segmentNumber();
int inflowSegmentCurBranch(const std::string& wname, const Opm::WellSegments& segSet, std::size_t segIndex) {
const auto& branch = segSet[segIndex].branchNumber();
const auto& segNumber = segSet[segIndex].segmentNumber();
int inFlowSegInd = -1;
for (std::size_t ind = 0; ind < segSet.size(); ind++) {
const auto& i_segNum = segSet[ind].segmentNumber();
@ -390,6 +384,7 @@ namespace {
}
}
}
return (inFlowSegInd == -1) ? 0 : inFlowSegInd;
}
@ -435,7 +430,6 @@ namespace {
VectorItems::ISeg::index;
const auto& sicd = segment.spiralICD();
iSeg[baseIndex + Ix::SegmentType] = segment.ecl_type_id();
iSeg[baseIndex + Ix::ICDScalingMode] = sicd.methodFlowScaling();
iSeg[baseIndex + Ix::ICDOpenShutFlag] = sicd.ecl_status();
}
@ -449,11 +443,21 @@ namespace {
VectorItems::ISeg::index;
const auto& aicd = segment.autoICD();
iSeg[baseIndex + Ix::SegmentType] = segment.ecl_type_id();
iSeg[baseIndex + Ix::ICDScalingMode] = aicd.methodFlowScaling();
iSeg[baseIndex + Ix::ICDOpenShutFlag] = aicd.ecl_status();
}
template <class ISegArray>
void assignValveCharacteristics(const Opm::Segment& segment,
const std::size_t baseIndex,
ISegArray& iSeg)
{
using Ix = ::Opm::RestartIO::Helpers::VectorItems::ISeg::index;
const auto& valve = segment.valve();
iSeg[baseIndex + Ix::ICDOpenShutFlag] = valve.ecl_status();
}
template <class ISegArray>
void assignSegmentTypeCharacteristics(const Opm::Segment& segment,
const std::size_t baseIndex,
@ -462,9 +466,14 @@ namespace {
if (segment.isSpiralICD()) {
assignSpiralICDCharacteristics(segment, baseIndex, iSeg);
}
if (segment.isAICD()) {
assignAICDCharacteristics(segment, baseIndex, iSeg);
}
if (segment.isValve()) {
assignValveCharacteristics(segment, baseIndex, iSeg);
}
}
template <class ISegArray>
@ -491,7 +500,7 @@ namespace {
const auto& segment = welSegSet[ind];
auto segNumber = segment.segmentNumber();
auto iS = (segNumber-1)*noElmSeg;
iSeg[iS + Ix::SegNo] = welSegSet[orderedSegmentNo[ind]].segmentNumber();
iSeg[ind*noElmSeg + Ix::SegNo] = welSegSet[orderedSegmentNo[ind]].segmentNumber();
iSeg[iS + Ix::OutSeg] = segment.outletSegment();
iSeg[iS + Ix::InSegCurBranch] = (inflowSegmentCurBranch(well.name(), welSegSet, ind) == 0) ? 0 : welSegSet[inflowSegmentCurBranch(well.name(), welSegSet, ind)].segmentNumber();
iSeg[iS + Ix::BranchNo] = segment.branchNumber();
@ -764,6 +773,7 @@ namespace {
bool haveWellRes = (well.getStatus() != Opm::Well::Status::SHUT) ? (wRatesIt != wr.end()) : false;
const auto volFromLengthUnitConv = units.from_si(M::length, units.from_si(M::length, units.from_si(M::length, 1.)));
const auto areaFromLengthUnitConv = units.from_si(M::length, units.from_si(M::length, 1.));
//
//Initialize temporary variables
double temp_o = 0.;
@ -771,10 +781,9 @@ namespace {
double temp_g = 0.;
// find well connections and calculate segment rates based on well connection production/injection terms
auto sSFR = Opm::RestartIO::Helpers::SegmentSetFlowRates{};
if (haveWellRes) {
sSFR = getSegmentSetFlowRates(welSegSet, wRatesIt->second.connections, welConns, units);
}
const auto sSFR = haveWellRes
? getSegmentSetFlowRates(welSegSet, wRatesIt->second.connections, welConns, units)
: SegmentSetFlowRates{};
auto get = [&smry, &wname](const std::string& vector, const std::string& segment_nr)
{
@ -782,7 +791,6 @@ namespace {
return smry.get(key, 0.0);
};
// Treat the top segment individually
{
const int segNumber = segment0.segmentNumber();
@ -904,6 +912,287 @@ namespace {
}
} // RSeg
namespace LateralBranch {
/// Discover segment and branch tree structure through traversal.
/// Uses Segment::inletSegments() as the primary link in the tree
/// structure.
///
/// Information conveyed to the user through callback routines.
///
/// As an example, the segments in the following tree will be
/// visited in the order
///
/// 1, 2, 3, 4, 5, 6 -- Branch (1)
/// 11, 12, 13, 14, 15, 16 -- Branch (2)
/// 7, 8, 9, 10 -- Branch (3)
/// 20, 22, 23, 24 -- Branch (5)
/// 21, -- Branch (6)
/// 17, 18, 19 -- Branch (4)
///
/// +------------------------------------------------------------+
/// | |
/// | 12 13 14 15 16 |
/// | o----o----o-----o------o-----o (2) |
/// | 11 / 20 \ 21 \ |
/// | / o o (6) |
/// | / \ |
/// | / 22 \ 23 24 |
/// | 1 2 3 / 4 5 6 o----o----o (5) |
/// | ---o---o---o-----o---o---o (1) |
/// | \ |
/// | 7 \ 8 9 10 |
/// | o---o---o-----o (3) |
/// | \ |
/// | 17 \ 18 19 |
/// | o----o----o (4) |
/// | |
/// +------------------------------------------------------------+
///
class Topology
{
public:
/// Callback for discovering/visiting a new segment.
using NewSegmentCallback = std::function<void(const ::Opm::Segment& seg)>;
/// Callback for discovering/creating a new branch.
using NewBranchCallback = std::function<
void(std::string_view well,
const int branchId,
const int kickOffSegment,
const int outletSegment)
>;
/// Constructor.
///
/// \param[in] well Name of current MS well.
/// \param[in] segSet Well's segments and branches.
explicit Topology(std::string_view well,
const ::Opm::WellSegments& segSet)
: well_ { well }
, segSet_ { std::cref(segSet) }
{}
/// Set callback for visiting a new segment.
///
/// \param[in] callback New callback routine.
/// \return \c *this.
Topology& setNewSegmentCallback(NewSegmentCallback callback)
{
this->runNewSegmentCallback_ = std::move(callback);
return *this;
}
/// Set callback for visiting a new branch.
///
/// \param[in] callback New callback routine.
/// \return \c *this.
Topology& setNewBranchCallback(NewBranchCallback callback)
{
this->runNewBranchCallback_ = std::move(callback);
return *this;
}
/// Walk well's segment tree from top (segment 1, branch 1).
///
/// New branches and segments are searched in a depth first
/// order. Furthermore, new branches are visited in order of
/// discovery, but we always search to the end of the current
/// branch, visiting all of its segments, before visiting the
/// first segment of a new branch. Each segment is visited
/// exactly once.
///
/// Invokes the user-defined callback routines for new segments
/// and branches and imparts segment tree structure to caller
/// through these routines.
void traverseStructure();
private:
/// Name of well being explored.
std::string_view well_{};
/// Well's segments and branches.
std::reference_wrapper<const ::Opm::WellSegments> segSet_;
/// Callback routine for visiting a new segment.
NewSegmentCallback runNewSegmentCallback_{};
/// Callback routine for visiting a new branch.
NewBranchCallback runNewBranchCallback_{};
/// Segments from which to kick off searching new branches.
std::queue<int> kickOffSegments_{};
/// One-based segment number of currently visited segment.
int currentSegment_{};
/// Find all segments on current branch, in order from heel to
/// toe.
///
/// Invokes new segment callback, once for each segment.
void buildCurrentBranch();
/// Enqueue collection of new branches from common kick-off
/// point.
///
/// \param[in] outletSegment Segment from which new branches
/// kick off.
///
/// \param[in] children Collection of new branch start segments.
/// One child/kick-off segment for each new branch.
void discoverNewBranches(const int outletSegment,
const std::vector<int>& children);
/// Enqueue new branch.
///
/// Will be visited later. Invokes new branch callback.
///
/// \param[in] branchId Branch number for new branch.
///
/// \param[in] kickOffSegment First segment on new branch.
///
/// \param[in] outletSegment Segment on branch from which the
/// new branch kicks off.
void discoverNewBranch(const int branchId,
const int kickOffSegment,
const int outletSegment);
/// Split child segments of current segment into groups
/// based on their associate branch number.
///
/// \return Child segment grouping. The \c .first group
/// contains child segments associated to branches different to
/// that of the current segment. This collection is empty if
/// there are no child segments on other branches. The \c
/// .second group is the single child segment on the same branch
/// as the current segment. This will be \c nullopt if there is
/// no such child segment, thus signifiying the end of the
/// current branch.
std::pair<std::vector<int>, std::optional<int>>
characteriseChildSegments() const;
/// Get current segment object.
const Opm::Segment& currentSegment() const;
/// Get segment object from one-based segment number.
///
/// \param[in] segNum One-based segment number.
///
/// \return Segment object corresponding to \p segNum.
const Opm::Segment& segment(const int segNum) const;
};
void Topology::traverseStructure()
{
this->kickOffSegments_.push(1);
while (! this->kickOffSegments_.empty()) {
this->currentSegment_ = this->kickOffSegments_.front();
this->kickOffSegments_.pop();
this->buildCurrentBranch();
}
}
void Topology::buildCurrentBranch()
{
while (true) {
const auto& seg = this->currentSegment();
this->runNewSegmentCallback_(seg);
const auto& [newBranchChildren, sameBranchChild] =
this->characteriseChildSegments();
this->discoverNewBranches(seg.segmentNumber(), newBranchChildren);
if (sameBranchChild.has_value()) {
// Child on same branch as currentSegment(). This child
// will be the next segment in our search order.
this->currentSegment_ = *sameBranchChild;
}
else {
// Branch completed.
return;
}
}
}
void Topology::discoverNewBranches(const int outletSegment,
const std::vector<int>& children)
{
for (const auto& child : children) {
const auto branch = this->segment(child).branchNumber();
this->discoverNewBranch(branch, child, outletSegment);
}
}
void Topology::discoverNewBranch(const int branchId,
const int kickOffSegment,
const int outletSegment)
{
this->runNewBranchCallback_(this->well_,
branchId,
kickOffSegment,
outletSegment);
this->kickOffSegments_.push(kickOffSegment);
}
std::pair<std::vector<int>, std::optional<int>>
Topology::characteriseChildSegments() const
{
auto children = this->currentSegment().inletSegments();
auto sameBranchPos =
std::stable_partition(children.begin(), children.end(),
[this, currBranch = this->currentSegment().branchNumber()]
(const int segNum)
{
return this->segment(segNum).branchNumber() != currBranch;
});
if (sameBranchPos == children.end()) {
// Every child is on another branch--or there are no children
return {
std::piecewise_construct,
std::forward_as_tuple(std::move(children)),
std::forward_as_tuple()
};
}
else {
if (const auto numSameBranch = std::distance(sameBranchPos, children.end());
numSameBranch != std::vector<int>::difference_type{1})
{
throw std::invalid_argument {
fmt::format("Segment {} of well {} has {} "
"inlet segments on branch {}",
this->currentSegment().segmentNumber(),
this->well_, numSameBranch,
this->currentSegment().branchNumber())
};
}
// Common case: The segment at *sameBranchPos continues the
// current branch. All other child segments start new
// branches.
return {
std::piecewise_construct,
std::forward_as_tuple(children.begin(), sameBranchPos),
std::forward_as_tuple(*sameBranchPos)
};
}
}
const Opm::Segment& Topology::currentSegment() const
{
return this->segment(this->currentSegment_);
}
const Opm::Segment& Topology::segment(const int segNum) const
{
return this->segSet_.get().getFromSegmentNumber(segNum);
}
} // LateralBranch
namespace ILBS {
std::size_t entriesPerMSW(const std::vector<int>& inteHead)
@ -922,70 +1211,46 @@ namespace {
WV::WindowSize{ entriesPerMSW(inteHead) }
};
}
template <class ILBSArray>
void staticContrib(const Opm::Well& well,
ILBSArray& iLBS)
{
if (well.isMultiSegment()) {
//
// Store the segment number of the first segment in branch for branch number
// 2 and upwards
const auto& welSegSet = well.getSegments();
const auto& branches = SegmentSetBranches(welSegSet);
for (auto it = branches.begin()+1; it != branches.end(); it++){
iLBS[*it-2] = welSegSet[firstSegmentInBranch(welSegSet, *it)].segmentNumber();
}
}
else {
throw std::invalid_argument("No such multisegment well: " + well.name());
}
}
} // ILBS
namespace ILBR {
std::size_t entriesPerMSW(const std::vector<int>& inteHead)
class Array
{
// inteHead(177) = NLBRMX
// inteHead(180) = NILBRZ
return inteHead[177] * inteHead[180];
public:
using Matrix = Opm::RestartIO::Helpers::WindowedMatrix<int>;
explicit Array(Matrix& ilbr,
const Matrix::Idx msWellID)
: ilbr_ { std::ref(ilbr) }
, well_ { msWellID }
{}
decltype(auto) operator[](const Matrix::Idx branch)
{
return this->ilbr_.get()(this->well_, branch - 1);
}
private:
std::reference_wrapper<Matrix> ilbr_;
Matrix::Idx well_;
};
std::size_t maxBranchesPerMSWell(const std::vector<int>& inteHead)
{
return inteHead[177];
}
Opm::RestartIO::Helpers::WindowedArray<int>
Opm::RestartIO::Helpers::WindowedMatrix<int>
allocate(const std::vector<int>& inteHead)
{
using WV = Opm::RestartIO::Helpers::WindowedArray<int>;
using WM = Opm::RestartIO::Helpers::WindowedMatrix<int>;
return WV {
WV::NumWindows{ nswlmx(inteHead) },
WV::WindowSize{ entriesPerMSW(inteHead) }
return WM {
WM::NumRows { nswlmx(inteHead) },
WM::NumCols { maxBranchesPerMSWell(inteHead) },
WM::WindowSize{ nilbrz(inteHead) }
};
}
template <class ILBRArray>
void staticContrib(const Opm::Well& well,
const std::vector<int>& inteHead,
ILBRArray& iLBR)
{
if (well.isMultiSegment()) {
//
const auto& welSegSet = well.getSegments();
const auto& branches = SegmentSetBranches(welSegSet);
const auto& noElmBranch = nilbrz(inteHead);
for (auto it = branches.begin(); it != branches.end(); it++){
const auto iB = (*it-1)*noElmBranch;
const auto& branchParam = getBranchSegmentParam(welSegSet, *it);
iLBR[iB ] = branchParam.outletS;
iLBR[iB+1] = branchParam.noSegInBranch;
iLBR[iB+2] = branchParam.firstSeg;
iLBR[iB+3] = branchParam.lastSeg;
iLBR[iB+4] = branchParam.branch - 1;
}
}
else {
throw std::invalid_argument("No such multisegment well: " + well.name());
}
}
} // ILBR
} // Anonymous
@ -1004,60 +1269,90 @@ AggregateMSWData(const std::vector<int>& inteHead)
void
Opm::RestartIO::Helpers::AggregateMSWData::
captureDeclaredMSWData(const Schedule& sched,
const std::size_t rptStep,
const Opm::UnitSystem& units,
const std::vector<int>& inteHead,
captureDeclaredMSWData(const Schedule& sched,
const std::size_t rptStep,
const Opm::UnitSystem& units,
const std::vector<int>& inteHead,
const Opm::EclipseGrid& grid,
const Opm::SummaryState& smry,
const Opm::data::Wells& wr
)
const Opm::data::Wells& wr)
{
const auto& wells = sched.getWells(rptStep);
auto msw = std::vector<const Opm::Well*>{};
//msw.reserve(wells.size());
for (const auto& well : wells) {
if (well.isMultiSegment())
if (well.isMultiSegment()) {
msw.push_back(&well);
}
}
// Extract Contributions to ISeg Array
{
MSWLoop(msw, [&inteHead, this]
(const Well& well, const std::size_t mswID) -> void
{
auto imsw = this->iSeg_[mswID];
ISeg::staticContrib(well, inteHead, imsw);
});
}
// Extract Contributions to RSeg Array
// Extract contributions to the ISEG and RSEG arrays.
MSWLoop(msw, [&units, &inteHead, &sched, &grid, &smry, &wr, this]
(const Well& well, const std::size_t mswID)
{
MSWLoop(msw, [&units, &inteHead, &sched, &grid, &smry, this, &wr]
(const Well& well, const std::size_t mswID) -> void
{
auto rmsw = this->rSeg_[mswID];
RSeg::staticContrib_useMSW(sched.runspec(), well, inteHead, grid, units, smry, wr, rmsw);
});
}
// Extract Contributions to ILBS Array
{
MSWLoop(msw, [this]
(const Well& well, const std::size_t mswID) -> void
{
auto ilbs_msw = this->iLBS_[mswID];
auto imsw = this->iSeg_[mswID];
auto rmsw = this->rSeg_[mswID];
ILBS::staticContrib(well, ilbs_msw);
});
}
// Extract Contributions to ILBR Array
{
MSWLoop(msw, [&inteHead, this]
(const Well& well, const std::size_t mswID) -> void
{
auto ilbr_msw = this->iLBR_[mswID];
ISeg::staticContrib(well, inteHead, imsw);
RSeg::staticContrib_useMSW(sched.runspec(), well, inteHead,
grid, units, smry, wr, rmsw);
});
ILBR::staticContrib(well, inteHead, ilbr_msw);
});
}
// Extract contributions to the ILBS and ILBR arrays.
MSWLoop(msw, [this](const Well& well, const std::size_t mswID)
{
using Ix = VectorItems::ILbr::index;
auto ilbs = this->iLBS_[mswID];
auto ilbr = ILBR::Array { this->iLBR_, mswID };
// The top segment (segment 1) is always the first segment of branch
// 1, at an offset of 0 with no outlet segment. Describe it as such.
ilbr[1][Ix::OutletSegment] = 0;
ilbr[1][Ix::NumBranchSegments] = 0;
ilbr[1][Ix::FirstSegment] = 1;
ilbr[1][Ix::KickOffDiscoveryOffset] = 0;
LateralBranch::Topology { well.name(), well.getSegments() }
.setNewSegmentCallback([&ilbr](const Segment& seg)
{
auto currBranch = ilbr[seg.branchNumber()];
// Attribute 'seg' to current branch. The LastSegment is
// intentionally updated on every call since every new
// segment is the last segment along the branch until it
// isn't anymore.
//
// We do it this way since the branch traversal visits each
// segment exactly once.
currBranch[Ix::LastSegment] = seg.segmentNumber();
currBranch[Ix::NumBranchSegments] += 1;
})
.setNewBranchCallback([&ilbr, &ilbs, insertIndex = 0]
(std::string_view wellName,
const int newBranchId,
const int kickOffSegment,
const int outletSegment) mutable
{
ilbs[insertIndex] = kickOffSegment;
auto newBranch = ilbr[newBranchId];
// Add one to the kick-off discovery offset to account for
// branch 1 which is not in ILBS.
newBranch[Ix::OutletSegment] = outletSegment;
newBranch[Ix::FirstSegment] = kickOffSegment;
newBranch[Ix::KickOffDiscoveryOffset] = insertIndex + 1;
if (newBranch[Ix::NumBranchSegments] > 0) {
throw std::invalid_argument {
fmt::format("Looped branch {} for well {} "
"is not supported", newBranchId, wellName)
};
}
++insertIndex;
})
.traverseStructure();
});
}

View File

@ -18,41 +18,50 @@
*/
#define BOOST_TEST_MODULE Aggregate_MSW_Data
#include <opm/output/eclipse/AggregateMSWData.hpp>
#include <opm/output/eclipse/WriteRestartHelpers.hpp>
#include <boost/test/unit_test.hpp>
#include <opm/output/eclipse/AggregateWellData.hpp>
#include <opm/input/eclipse/Python/Python.hpp>
#include <opm/output/eclipse/VectorItems/intehead.hpp>
#include <opm/output/eclipse/VectorItems/well.hpp>
#include <opm/output/eclipse/VectorItems/msw.hpp>
#include <opm/output/eclipse/VectorItems/well.hpp>
#include <opm/output/eclipse/WriteRestartHelpers.hpp>
#include <opm/output/data/Wells.hpp>
#include <opm/io/eclipse/rst/segment.hpp>
#include <opm/input/eclipse/Deck/Deck.hpp>
#include <opm/input/eclipse/Parser/Parser.hpp>
#include <opm/input/eclipse/EclipseState/EclipseState.hpp>
#include <opm/input/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <opm/input/eclipse/Schedule/Schedule.hpp>
#include <opm/input/eclipse/Schedule/SummaryState.hpp>
#include <opm/common/utility/TimeService.hpp>
#include <exception>
#include <opm/input/eclipse/Python/Python.hpp>
#include <opm/input/eclipse/Deck/Deck.hpp>
#include <opm/input/eclipse/Parser/Parser.hpp>
#include <cmath>
#include <cstddef>
#include <memory>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
#include <iostream>
#include <cstddef>
namespace {
namespace VI = ::Opm::RestartIO::Helpers::VectorItems;
Opm::Deck first_sim(std::string fname) {
return Opm::Parser {} .parseFile(fname);
Opm::Deck first_sim(const std::string& fname)
{
return Opm::Parser {}.parseFile(fname);
}
Opm::SummaryState sim_state()
@ -92,6 +101,7 @@ Opm::SummaryState sim_state()
state.update_well_var("WINJ", "WBHP", 234.);
return state;
}
Opm::data::Wells wr()
{
using o = ::Opm::data::Rates::opt;
@ -142,21 +152,110 @@ Opm::data::Wells wr()
}
return xw;
}
//------------------------------------------------------------------+
// Models a multi-lateral well with the following segment structure |
//------------------------------------------------------------------+
// |
// 12 13 14 15 16 |
// o----o----o-----o------o-----o (2) |
// 11 / 20 \ 21 \ |
// / o o (6) |
// / \ |
// / 22 \ 23 24 |
// 1 2 3 / 4 5 6 o----o----o (5) |
// ---o---o---o-----o---o---o (1) |
// \ |
// 7 \ 8 9 10 |
// o---o---o-----o (3) |
// \ |
// 17 \ 18 19 |
// o----o----o (4) |
//------------------------------------------------------------------+
// Branch (1): 1, 2, 3, 4, 5, 6 |
// Branch (2): 11, 12, 13, 14, 15, 16 |
// Branch (3): 7, 8, 9, 10 |
// Branch (4): 17, 18, 19 |
// Branch (5): 20, 22, 23, 24 |
// Branch (6): 21 |
//------------------------------------------------------------------+
Opm::Deck multilaterals()
{
return Opm::Parser{}.parseString(R"(RUNSPEC
START
29 'SEP' 2023 /
DIMENS
10 10 3 /
OIL
GAS
WATER
DISGAS
VAPOIL
GRID
DXV
10*100.0 /
DYV
10*100.0 /
DZV
3*5.0 /
PERMX
300*100.0 /
COPY
PERMX PERMY /
PERMX PERMZ /
/
MULTIPLY
PERMZ 0.1 /
/
PORO
300*0.3 /
DEPTHZ
121*2000.0 /
SCHEDULE
WELSPECS
'MLP' 'G' 10 10 2002.5 'OIL' /
/
COMPDAT
'MLP' 10 10 3 3 'OPEN' 1* 123.4 /
/
WELSEGS
'MLP' 2002.5 0.0 1* 'INC' 'H--' /
--
2 6 1 1 0.1 0.1 0.2 0.01 /
7 10 3 5 0.1 0.1 0.2 0.01 /
11 16 2 3 0.1 0.1 0.2 0.01 /
17 19 4 10 0.1 0.1 0.2 0.01 /
20 20 5 14 0.1 0.1 0.2 0.01 /
21 21 6 15 0.1 0.1 0.2 0.01 /
22 24 5 20 0.1 0.1 0.2 0.01 /
/
COMPSEGS
'MLP' /
--
10 10 3 5 0.0 1.0 'Z' /
/
WCONPROD
'MLP' 'OPEN' 'ORAT' 321.0 4* 10.0 /
/
TSTEP
5*30 /
END
)");
}
} // Anonymous namespace
struct SimulationCase
{
explicit SimulationCase(const Opm::Deck& deck)
: es ( deck )
, grid ( deck )
, python( std::make_shared<Opm::Python>() )
, sched( deck, es, python )
: es (deck)
, grid (deck)
, sched(deck, es, std::make_shared<Opm::Python>())
{}
// Order requirement: 'es' must be declared/initialised before 'sched'.
Opm::EclipseState es;
Opm::EclipseGrid grid;
std::shared_ptr<Opm::Python> python;
Opm::Schedule sched;
};
@ -164,7 +263,6 @@ struct SimulationCase
BOOST_AUTO_TEST_SUITE(Aggregate_MSW)
// test dimensions of multisegment data
BOOST_AUTO_TEST_CASE (Constructor)
{
@ -191,46 +289,36 @@ BOOST_AUTO_TEST_CASE (Constructor)
const auto nrsegz = VI::intehead::NRSEGZ;
const auto nlbrmx = VI::intehead::NLBRMX;
const auto nilbrz = VI::intehead::NILBRZ;
BOOST_CHECK_EQUAL(static_cast<int>(amswd.getISeg().size()), ih[nswlmx] * ih[nsegmx] * ih[nisegz]);
BOOST_CHECK_EQUAL(static_cast<int>(amswd.getRSeg().size()), ih[nswlmx] * ih[nsegmx] * ih[nrsegz]);
BOOST_CHECK_EQUAL(static_cast<int>(amswd.getILBs().size()), ih[nswlmx] * ih[nlbrmx]);
BOOST_CHECK_EQUAL(static_cast<int>(amswd.getILBr().size()), ih[nswlmx] * ih[nlbrmx] * ih[nilbrz]);
}
BOOST_AUTO_TEST_CASE (Declared_MSW_Data)
{
const auto simCase = SimulationCase {first_sim("TEST_AGGREGATE_MSW.DATA")};
Opm::EclipseState es = simCase.es;
Opm::Runspec rspec = es.runspec();
Opm::SummaryState smry = sim_state();
Opm::Schedule sched = simCase.sched;
Opm::EclipseGrid grid = simCase.grid;
const auto& units = es.getUnits();
const auto& es = simCase.es;
const auto& grid = simCase.grid;
const auto& sched = simCase.sched;
const auto& units = es.getUnits();
const auto smry = sim_state();
// Report Step 1: 2008-10-10 --> 2011-01-20
const auto rptStep = std::size_t {1};
double secs_elapsed = 3.1536E07;
const double secs_elapsed = 3.1536E07;
const auto ih = Opm::RestartIO::Helpers::
createInteHead(es, grid, sched, secs_elapsed,
rptStep, rptStep+1, rptStep);
//BOOST_CHECK_EQUAL(ih.nwells, MockIH::Sz{2});
createInteHead(es, grid, sched, secs_elapsed,
rptStep, rptStep + 1, rptStep);
const Opm::data::Wells wrc = wr();
auto amswd = Opm::RestartIO::Helpers::AggregateMSWData {ih};
amswd.captureDeclaredMSWData(simCase.sched,
rptStep,
units,
ih,
grid,
smry,
wrc
);
amswd.captureDeclaredMSWData(sched, rptStep, units,
ih, grid, smry, wrc);
// ISEG (PROD)
{
@ -409,39 +497,214 @@ BOOST_AUTO_TEST_CASE (Declared_MSW_Data)
}
}
// The segments and branches must appear in the following order in the
// ILBS/ILBR output arrays.
//
// 1, 2, 3, 4, 5, 6 -- Branch (1)
// 11, 12, 13, 14, 15, 16 -- Branch (2)
// 7, 8, 9, 10 -- Branch (3)
// 20, 22, 23, 24 -- Branch (5)
// 21, -- Branch (6)
// 17, 18, 19 -- Branch (4)
//
BOOST_AUTO_TEST_CASE(Multilateral_Branches)
{
const auto cse = SimulationCase { multilaterals() };
BOOST_AUTO_TEST_CASE(MSW_AICD) {
const auto& es = cse.es;
const auto& grid = cse.grid;
const auto& sched = cse.sched;
const auto& units = es.getUnits();
const auto smry = Opm::SummaryState { Opm::TimeService::now() };
// Report Step 1: 2023-09-29 --> 2023-10-23
const auto rptStep = std::size_t {1};
const double secs_elapsed = 30 * 86'400.0;
const auto ih = Opm::RestartIO::Helpers::
createInteHead(es, grid, sched, secs_elapsed,
rptStep, rptStep + 1, rptStep);
const auto xw = Opm::data::Wells {};
auto amswd = Opm::RestartIO::Helpers::AggregateMSWData {ih};
amswd.captureDeclaredMSWData(sched, rptStep, units,
ih, grid, smry, xw);
// ILBS--First segment on each branch other than branch 1. Ordered by
// discovery.
{
const auto& ilbs = amswd.getILBs();
// No WSEGDIMS => size = maximum branch number
BOOST_CHECK_EQUAL(ilbs.size(), std::vector<int>::size_type{6});
const auto expect = std::vector {
11, 7, 20, 21, 17, 0,
};
BOOST_CHECK_EQUAL_COLLECTIONS(ilbs .begin(), ilbs .end(),
expect.begin(), expect.end());
}
auto ilbrOffset = [&ih](const int branch)
{
return ih[VI::intehead::NILBRZ] * (branch - 1);
};
// ILBR, branch 1
{
const auto* ilbr = &amswd.getILBr()[ilbrOffset(1)];
BOOST_CHECK_EQUAL(ilbr[VI::ILbr::OutletSegment], 0);
BOOST_CHECK_EQUAL(ilbr[VI::ILbr::NumBranchSegments], 6);
BOOST_CHECK_EQUAL(ilbr[VI::ILbr::FirstSegment], 1);
BOOST_CHECK_EQUAL(ilbr[VI::ILbr::LastSegment], 6);
BOOST_CHECK_EQUAL(ilbr[VI::ILbr::KickOffDiscoveryOffset], 0);
}
// ILBR, branch 2
{
const auto* ilbr = &amswd.getILBr()[ilbrOffset(2)];
BOOST_CHECK_EQUAL(ilbr[VI::ILbr::OutletSegment], 3);
BOOST_CHECK_EQUAL(ilbr[VI::ILbr::NumBranchSegments], 6);
BOOST_CHECK_EQUAL(ilbr[VI::ILbr::FirstSegment], 11);
BOOST_CHECK_EQUAL(ilbr[VI::ILbr::LastSegment], 16);
BOOST_CHECK_EQUAL(ilbr[VI::ILbr::KickOffDiscoveryOffset], 1);
}
// ILBR, branch 3
{
const auto* ilbr = &amswd.getILBr()[ilbrOffset(3)];
BOOST_CHECK_EQUAL(ilbr[VI::ILbr::OutletSegment], 5);
BOOST_CHECK_EQUAL(ilbr[VI::ILbr::NumBranchSegments], 4);
BOOST_CHECK_EQUAL(ilbr[VI::ILbr::FirstSegment], 7);
BOOST_CHECK_EQUAL(ilbr[VI::ILbr::LastSegment], 10);
BOOST_CHECK_EQUAL(ilbr[VI::ILbr::KickOffDiscoveryOffset], 2);
}
// ILBR, branch 4
{
const auto* ilbr = &amswd.getILBr()[ilbrOffset(4)];
BOOST_CHECK_EQUAL(ilbr[VI::ILbr::OutletSegment], 10);
BOOST_CHECK_EQUAL(ilbr[VI::ILbr::NumBranchSegments], 3);
BOOST_CHECK_EQUAL(ilbr[VI::ILbr::FirstSegment], 17);
BOOST_CHECK_EQUAL(ilbr[VI::ILbr::LastSegment], 19);
BOOST_CHECK_EQUAL(ilbr[VI::ILbr::KickOffDiscoveryOffset], 5);
}
// ILBR, branch 5
{
const auto* ilbr = &amswd.getILBr()[ilbrOffset(5)];
BOOST_CHECK_EQUAL(ilbr[VI::ILbr::OutletSegment], 14);
BOOST_CHECK_EQUAL(ilbr[VI::ILbr::NumBranchSegments], 4);
BOOST_CHECK_EQUAL(ilbr[VI::ILbr::FirstSegment], 20);
BOOST_CHECK_EQUAL(ilbr[VI::ILbr::LastSegment], 24);
BOOST_CHECK_EQUAL(ilbr[VI::ILbr::KickOffDiscoveryOffset], 3);
}
// ILBR, branch 6
{
const auto* ilbr = &amswd.getILBr()[ilbrOffset(6)];
BOOST_CHECK_EQUAL(ilbr[VI::ILbr::OutletSegment], 15);
BOOST_CHECK_EQUAL(ilbr[VI::ILbr::NumBranchSegments], 1);
BOOST_CHECK_EQUAL(ilbr[VI::ILbr::FirstSegment], 21);
BOOST_CHECK_EQUAL(ilbr[VI::ILbr::LastSegment], 21);
BOOST_CHECK_EQUAL(ilbr[VI::ILbr::KickOffDiscoveryOffset], 4);
}
}
// The segments must appear in the following depth first search toe-to-heel
// order in ISEG[0]. We furthermore, go along kick-off branches before
// searching the main branch. Note that this order is *different* from
// ILBS/ILBR.
//
// 24, 23, 22, 20, -- Branch (5)
// 21, -- Branch (6)
// 16, 15, 14, 13, 12, 11, -- Branch (2)
// 19, 18, 17, -- Branch (4)
// 10, 9, 8, 7, -- Branch (3)
// 6, 5, 4, 3, 2, 1, -- Branch (1)
//
BOOST_AUTO_TEST_CASE(Multilateral_Segments_ISEG_0)
{
const auto cse = SimulationCase { multilaterals() };
const auto& es = cse.es;
const auto& grid = cse.grid;
const auto& sched = cse.sched;
const auto& units = es.getUnits();
const auto smry = Opm::SummaryState { Opm::TimeService::now() };
// Report Step 1: 2023-09-29 --> 2023-10-23
const auto rptStep = std::size_t {1};
const double secs_elapsed = 30 * 86'400.0;
const auto ih = Opm::RestartIO::Helpers::
createInteHead(es, grid, sched, secs_elapsed,
rptStep, rptStep + 1, rptStep);
const auto xw = Opm::data::Wells {};
auto amswd = Opm::RestartIO::Helpers::AggregateMSWData {ih};
amswd.captureDeclaredMSWData(sched, rptStep, units,
ih, grid, smry, xw);
auto isegOffset = [&ih](const int ix)
{
return ih[VI::intehead::NISEGZ] * ix;
};
const auto expect = std::vector {
24, 23, 22, 20, // Branch (5)
21, // Branch (6)
16, 15, 14, 13, 12, 11, // Branch (2)
19, 18, 17, // Branch (4)
10, 9, 8, 7, // Branch (3)
6, 5, 4, 3, 2, 1, // Branch (1)
};
const auto& iseg = amswd.getISeg();
for (auto i = 0*expect.size(); i < expect.size(); ++i) {
BOOST_CHECK_MESSAGE(iseg[isegOffset(i)] == expect[i],
"ISEG[0](" << i << ") == "
<< iseg[isegOffset(i)]
<< " differs from expected value "
<< expect[i]);
}
}
BOOST_AUTO_TEST_CASE(MSW_AICD)
{
const auto simCase = SimulationCase {first_sim("TEST_AGGREGATE_MSW.DATA")};
Opm::EclipseState es = simCase.es;
Opm::Runspec rspec = es.runspec();
Opm::SummaryState smry = sim_state();
Opm::Schedule sched = simCase.sched;
Opm::EclipseGrid grid = simCase.grid;
const auto& units = es.getUnits();
const auto& es = simCase.es;
const auto& grid = simCase.grid;
const auto& sched = simCase.sched;
const auto& units = es.getUnits();
const auto smry = sim_state();
// Report Step 1: 2008-10-10 --> 2011-01-20
const auto rptStep = std::size_t {1};
double secs_elapsed = 3.1536E07;
const double secs_elapsed = 3.1536E07;
const auto ih = Opm::RestartIO::Helpers::
createInteHead(es, grid, sched, secs_elapsed,
rptStep, rptStep+1, rptStep);
createInteHead(es, grid, sched, secs_elapsed,
rptStep, rptStep + 1, rptStep);
const Opm::data::Wells wrc = wr();
auto amswd = Opm::RestartIO::Helpers::AggregateMSWData {ih};
amswd.captureDeclaredMSWData(simCase.sched,
rptStep,
units,
ih,
grid,
smry,
wrc
);
// ISEG (PROD)
auto amswd = Opm::RestartIO::Helpers::AggregateMSWData {ih};
amswd.captureDeclaredMSWData(sched, rptStep, units,
ih, grid, smry, wrc);
// ISEG (PROD)
{
const auto& iSeg = amswd.getISeg();
auto start = 7*ih[VI::intehead::NISEGZ];
@ -507,48 +770,38 @@ BOOST_AUTO_TEST_CASE(MSW_AICD) {
BOOST_CHECK_CLOSE(rseg[i0 + VI::RSeg::index::flowFractionOilViscosityExponent], 1.01 , 1.0e-10);
BOOST_CHECK_CLOSE(rseg[i0 + VI::RSeg::index::flowFractionWaterViscosityExponent], 1.02 , 1.0e-10);
BOOST_CHECK_CLOSE(rseg[i0 + VI::RSeg::index::flowFractionGasViscosityExponent], 1.03 , 1.0e-10);
}
}
BOOST_AUTO_TEST_CASE(MSW_RST) {
BOOST_AUTO_TEST_CASE(MSW_RST)
{
const auto simCase = SimulationCase {first_sim("TEST_AGGREGATE_MSW.DATA")};
Opm::EclipseState es = simCase.es;
Opm::Runspec rspec = es.runspec();
Opm::SummaryState smry = sim_state();
Opm::Schedule sched = simCase.sched;
Opm::EclipseGrid grid = simCase.grid;
const auto& units = es.getUnits();
const auto& es = simCase.es;
const auto& grid = simCase.grid;
const auto& sched = simCase.sched;
const auto& units = es.getUnits();
const auto smry = sim_state();
// Report Step 1: 2008-10-10 --> 2011-01-20
const auto rptStep = std::size_t {1};
double secs_elapsed = 3.1536E07;
const double secs_elapsed = 3.1536E07;
const auto ih = Opm::RestartIO::Helpers::
createInteHead(es, grid, sched, secs_elapsed,
rptStep, rptStep+1, rptStep);
createInteHead(es, grid, sched, secs_elapsed,
rptStep, rptStep + 1, rptStep);
const Opm::data::Wells wrc = wr();
auto amswd = Opm::RestartIO::Helpers::AggregateMSWData {ih};
amswd.captureDeclaredMSWData(simCase.sched,
rptStep,
units,
ih,
grid,
smry,
wrc
);
amswd.captureDeclaredMSWData(sched, rptStep, units,
ih, grid, smry, wrc);
const auto& iseg = amswd.getISeg();
const auto& rseg = amswd.getRSeg();
auto segment = Opm::RestartIO::RstSegment(simCase.es.getUnits(), 1, iseg.data(), rseg.data());
auto segment = Opm::RestartIO::RstSegment(simCase.es.getUnits(), 1,
iseg.data(), rseg.data());
}
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE_END() // Aggregate_MSW