Merge pull request #1460 from joakim-hove/wellsegments-state

Wellsegments state
This commit is contained in:
Kai Bao 2020-02-13 14:39:29 +01:00 committed by GitHub
commit 8ddf71a67f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 105 additions and 154 deletions

View File

@ -42,6 +42,9 @@ namespace Opm {
Segment();
Segment(const Segment& src, double new_depth, double new_length, double new_volume);
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);
@ -76,11 +79,7 @@ namespace Opm {
int ecl_type_id() const;
void setVolume(const double volume_in);
void setDepthAndLength(const double depth_in, const double length_in);
const std::vector<int>& inletSegments() const;
void addInletSegment(const int segment_number);
static double invalidValue();
static SegmentType type_from_int(int ecl_id);
@ -88,15 +87,13 @@ namespace Opm {
bool operator==( const Segment& ) const;
bool operator!=( const Segment& ) const;
void updateSpiralICD(const SpiralICD& spiral_icd);
const std::shared_ptr<SpiralICD>& spiralICD() const;
const std::shared_ptr<Valve>& getValve() const;
void updateValve(const Valve& valve, const double segment_length);
const Valve* valve() const;
void updateSpiralICD(const SpiralICD& spiral_icd);
void updateValve(const Valve& valve, const double segment_length);
void addInletSegment(const int segment_number);
private:
// segment number
// it should work as a ID.

View File

@ -62,38 +62,27 @@ namespace Opm {
WellSegments() = default;
WellSegments(const std::string& wname,
double depthTopSeg,
double lengthTopSeg,
double volumeTopSeg,
LengthDepth lenDepType,
CompPressureDrop compDrop,
MultiPhaseModel multiPhase,
WellSegments(const DeckKeyword& keyword);
WellSegments(CompPressureDrop compDrop,
const std::vector<Segment>& segments,
const std::map<int,int>& segmentNumberIdx);
const std::string& wellName() const;
int size() const;
double depthTopSegment() const;
double lengthTopSegment() const;
double volumeTopSegment() const;
CompPressureDrop compPressureDrop() const;
LengthDepth lengthDepthType() const;
MultiPhaseModel multiPhaseModel() const;
// mapping the segment number to the index in the vector of segments
int segmentNumberToIndex(const int segment_number) const;
void addSegment(Segment new_segment);
void loadWELSEGS( const DeckKeyword& welsegsKeyword);
const Segment& getFromSegmentNumber(const int segment_number) const;
const Segment& operator[](size_t idx) const;
void orderSegments();
void process(bool first_time);
bool operator==( const WellSegments& ) const;
bool operator!=( const WellSegments& ) const;
@ -109,23 +98,13 @@ namespace Opm {
private:
void processABS();
void processINC(const bool first_time);
void processINC(double depth_top, double length_top);
void process(LengthDepth length_depth, double depth_top, double length_top);
void addSegment(const Segment& new_segment);
void loadWELSEGS( const DeckKeyword& welsegsKeyword);
std::string m_well_name;
// depth of the nodal point of the top segment
// it is taken as the BHP reference depth of the well
// BHP reference depth data from elsewhere will be ignored for multi-segmented wells
double m_depth_top;
// length down the tubing to the nodal point of the top segment
double m_length_top;
// effective wellbore volume of the top segment
double m_volume_top;
// type of the tubing length and depth information
LengthDepth m_length_depth_type;
// components of the pressure drop to be included
CompPressureDrop m_comp_pressure_drop;
// multi-phase flow model
MultiPhaseModel m_multiphase_model;
// There are X and Y cooridnate of the nodal point of the top segment
// Since they are not used for simulations and we are not supporting plotting,
// we are not handling them at the moment.

View File

@ -178,7 +178,7 @@ namespace {
Opm::RestartIO::Helpers::SegmentSetSourceSinkTerms
getSegmentSetSSTerms(const Opm::WellSegments& segSet, const std::vector<Opm::data::Connection>& rateConns,
getSegmentSetSSTerms(const std::string& wname, const Opm::WellSegments& segSet, const std::vector<Opm::data::Connection>& rateConns,
const Opm::WellConnections& welConns, const Opm::UnitSystem& units)
{
std::vector<double> qosc (segSet.size(), 0.);
@ -194,7 +194,7 @@ namespace {
throw std::invalid_argument {
"Inconsistent number of open connections I in Opm::WellConnections (" +
std::to_string(welConns.size()) + ") and vector<Opm::data::Connection> (" +
std::to_string(rateConns.size()) + ") in Well " + segSet.wellName()
std::to_string(rateConns.size()) + ") in Well " + wname
};
}
for (auto nConn = openConnections.size(), connID = 0*nConn; connID < nConn; connID++) {
@ -222,7 +222,7 @@ namespace {
}
Opm::RestartIO::Helpers::SegmentSetFlowRates
getSegmentSetFlowRates(const Opm::WellSegments& segSet, const std::vector<Opm::data::Connection>& rateConns,
getSegmentSetFlowRates(const std::string& wname, const Opm::WellSegments& segSet, const std::vector<Opm::data::Connection>& rateConns,
const Opm::WellConnections& welConns, const Opm::UnitSystem& units)
{
std::vector<double> sofr (segSet.size(), 0.);
@ -230,7 +230,7 @@ namespace {
std::vector<double> sgfr (segSet.size(), 0.);
//
//call function to calculate the individual segment source/sink terms
auto sSSST = getSegmentSetSSTerms(segSet, rateConns, welConns, units);
auto sSSST = getSegmentSetSSTerms(wname, segSet, rateConns, welConns, units);
// find an ordered list of segments
std::size_t segmentInd = 0;
@ -355,7 +355,7 @@ namespace {
}
int inflowSegmentCurBranch(const Opm::WellSegments& segSet, std::size_t segIndex) {
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;
@ -368,12 +368,12 @@ namespace {
inFlowSegInd = segSet.segmentNumberToIndex(i_segNum);
}
else {
std::cout << "Non-unique inflow segment in same branch, Well: " << segSet.wellName() << std::endl;
std::cout << "Non-unique inflow segment in same branch, Well: " << wname << std::endl;
std::cout << "Segment number: " << segNumber << std::endl;
std::cout << "Branch number: " << branch << std::endl;
std::cout << "Inflow segment number 1: " << segSet[inFlowSegInd].segmentNumber() << std::endl;
std::cout << "Inflow segment number 2: " << segSet[ind].segmentNumber() << std::endl;
throw std::invalid_argument("Non-unique inflow segment in same branch, Well " + segSet.wellName());
throw std::invalid_argument("Non-unique inflow segment in same branch, Well " + wname);
}
}
}
@ -464,7 +464,7 @@ namespace {
auto iS = (segNumber-1)*noElmSeg;
iSeg[iS + 0] = welSegSet[orderedSegmentNo[ind]].segmentNumber();
iSeg[iS + 1] = segment.outletSegment();
iSeg[iS + 2] = (inflowSegmentCurBranch(welSegSet, ind) == 0) ? 0 : welSegSet[inflowSegmentCurBranch(welSegSet, ind)].segmentNumber();
iSeg[iS + 2] = (inflowSegmentCurBranch(well.name(), welSegSet, ind) == 0) ? 0 : welSegSet[inflowSegmentCurBranch(well.name(), welSegSet, ind)].segmentNumber();
iSeg[iS + 3] = segment.branchNumber();
iSeg[iS + 4] = noInFlowBranches(welSegSet, ind);
iSeg[iS + 5] = sumNoInFlowBranches(welSegSet, ind);
@ -652,7 +652,7 @@ namespace {
// 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);
sSFR = getSegmentSetFlowRates(well.name(), welSegSet, wRatesIt->second.connections, welConns, units);
}
// 'stringSegNum' is one-based (1 .. #segments inclusive)
std::string stringSegNum = std::to_string(segNumber);

View File

@ -88,6 +88,27 @@ static constexpr double invalid_value = -1.e100;
{
}
Segment::Segment(const Segment& src, double new_depth, double new_length):
Segment(src)
{
this->m_depth = new_depth;
this->m_total_length = new_length;
this->m_data_ready = true;
}
Segment::Segment(const Segment& src, double new_depth, double new_length, double new_volume):
Segment(src, new_depth, new_length)
{
this->m_volume = new_volume;
}
Segment::Segment(const Segment& src, double new_volume):
Segment(src)
{
this->m_volume = new_volume;
}
int Segment::segmentNumber() const {
return m_segment_number;
@ -140,16 +161,6 @@ static constexpr double invalid_value = -1.e100;
return m_segment_type;
}
void Segment::setDepthAndLength(const double depth_in, const double length_in) {
m_total_length = length_in;
m_depth = depth_in;
m_data_ready = true;
}
void Segment::setVolume(const double volume_in) {
m_volume = volume_in;
}
const std::vector<int>& Segment::inletSegments() const {
return m_inlet_segments;
}

View File

@ -36,46 +36,35 @@
namespace Opm {
WellSegments::WellSegments(const std::string& wname,
double depthTopSeg,
double lengthTopSeg,
double volumeTopSeg,
LengthDepth lenDepType,
CompPressureDrop compDrop,
MultiPhaseModel multiPhase,
WellSegments::WellSegments(CompPressureDrop compDrop,
const std::vector<Segment>& segments,
const std::map<int,int>& segmentNumberIdx)
: m_well_name(wname)
, m_depth_top(depthTopSeg)
, m_length_top(lengthTopSeg)
, m_volume_top(volumeTopSeg)
, m_length_depth_type(lenDepType)
, m_comp_pressure_drop(compDrop)
, m_multiphase_model(multiPhase)
: m_comp_pressure_drop(compDrop)
, m_segments(segments)
, segment_number_to_index(segmentNumberIdx)
{
}
const std::string& WellSegments::wellName() const {
return m_well_name;
WellSegments::WellSegments(const DeckKeyword& keyword) {
this->loadWELSEGS(keyword);
}
int WellSegments::size() const {
int WellSegments::size() const {
return m_segments.size();
}
double WellSegments::depthTopSegment() const {
return m_depth_top;
return this->m_segments[0].depth();
}
double WellSegments::lengthTopSegment() const {
return m_length_top;
return this->m_segments[0].totalLength();
}
double WellSegments::volumeTopSegment() const {
return m_volume_top;
return this->m_segments[0].volume();
}
@ -83,10 +72,6 @@ namespace Opm {
return m_comp_pressure_drop;
}
WellSegments::MultiPhaseModel WellSegments::multiPhaseModel() const {
return m_multiphase_model;
}
const Segment& WellSegments::operator[](size_t idx) const {
return m_segments[idx];
}
@ -100,7 +85,7 @@ namespace Opm {
}
}
void WellSegments::addSegment( Segment new_segment ) {
void WellSegments::addSegment( const Segment& new_segment ) {
// decide whether to push_back or insert
const int segment_number = new_segment.segmentNumber();
@ -119,30 +104,25 @@ namespace Opm {
// for the first record, which provides the information for the top segment
// and information for the whole segment set
const auto& record1 = welsegsKeyword.getRecord(0);
m_well_name = record1.getItem("WELL").getTrimmedString(0);
m_segments.clear();
const double invalid_value = Segment::invalidValue(); // meaningless value to indicate unspecified values
m_depth_top = record1.getItem("DEPTH").getSIDouble(0);
m_length_top = record1.getItem("LENGTH").getSIDouble(0);
m_length_depth_type = LengthDepthFromString(record1.getItem("INFO_TYPE").getTrimmedString(0));
m_volume_top = record1.getItem("WELLBORE_VOLUME").getSIDouble(0);
const double depth_top = record1.getItem("DEPTH").getSIDouble(0);
const double length_top = record1.getItem("LENGTH").getSIDouble(0);
const double volume_top = record1.getItem("WELLBORE_VOLUME").getSIDouble(0);
const LengthDepth length_depth_type = LengthDepthFromString(record1.getItem("INFO_TYPE").getTrimmedString(0));
m_comp_pressure_drop = CompPressureDropFromString(record1.getItem("PRESSURE_COMPONENTS").getTrimmedString(0));
m_multiphase_model = MultiPhaseModelFromString(record1.getItem("FLOW_MODEL").getTrimmedString(0));
// the main branch is 1 instead of 0
// the segment number for top segment is also 1
if (m_length_depth_type == LengthDepth::INC) {
if (length_depth_type == LengthDepth::INC) {
m_segments.emplace_back( 1, 1, 0, 0., 0.,
invalid_value, invalid_value, invalid_value,
m_volume_top, false , Segment::SegmentType::REGULAR);
volume_top, false , Segment::SegmentType::REGULAR);
} else if (m_length_depth_type == LengthDepth::ABS) {
m_segments.emplace_back( 1, 1, 0, m_length_top, m_depth_top,
} else if (length_depth_type == LengthDepth::ABS) {
m_segments.emplace_back( 1, 1, 0, length_top, depth_top,
invalid_value, invalid_value, invalid_value,
m_volume_top, true , Segment::SegmentType::REGULAR);
volume_top, true , Segment::SegmentType::REGULAR);
}
// read all the information out from the DECK first then process to get all the required information
@ -182,7 +162,7 @@ namespace Opm {
double volume;
if (itemVolume.hasValue(0)) {
volume = itemVolume.getSIDouble(0);
} else if (m_length_depth_type == LengthDepth::INC) {
} else if (length_depth_type == LengthDepth::INC) {
volume = area * segment_length;
} else {
volume = invalid_value; // A * L, while L is not determined yet
@ -200,7 +180,7 @@ namespace Opm {
outlet_segment = i - 1;
}
if (m_length_depth_type == LengthDepth::INC) {
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);
} else if (i == segment2) {
@ -232,6 +212,9 @@ namespace Opm {
m_segments[outlet_segment_index].addInletSegment(segment_number);
}
this->process(length_depth_type, depth_top, length_top);
}
const Segment& WellSegments::getFromSegmentNumber(const int segment_number) const {
@ -244,11 +227,11 @@ namespace Opm {
return m_segments[segment_index];
}
void WellSegments::process(bool first_time) {
if (this->m_length_depth_type == LengthDepth::ABS)
void WellSegments::process(LengthDepth length_depth, double depth_top, double length_top) {
if (length_depth == LengthDepth::ABS)
this->processABS();
else if (this->m_length_depth_type == LengthDepth::INC)
this->processINC(first_time);
else if (length_depth == LengthDepth::INC)
this->processINC(depth_top, length_top);
else
throw std::logic_error("Invalid llength/depth/type in segment data structure");
}
@ -299,16 +282,22 @@ namespace Opm {
const double volume_segment = m_segments[range_end].crossArea() * length_inc;
for (int k = range_begin; k <= range_end; ++k) {
Segment new_segment = m_segments[k];
const double temp_length = length_outlet + (k - range_begin + 1) * length_inc;
const double temp_depth = depth_outlet + (k - range_end + 1) * depth_inc;
if (k != range_end) {
new_segment.setDepthAndLength(temp_depth, temp_length);
const auto& old_segment = this->m_segments[k];
double new_volume, new_length, new_depth;
if (k == range_end) {
new_length = old_segment.totalLength();
new_depth = old_segment.depth();
} else {
new_length = length_outlet + (k - range_begin + 1) * length_inc;
new_depth = depth_outlet + (k - range_end + 1) * depth_inc;
}
if (new_segment.volume() < 0.5 * invalid_value) {
new_segment.setVolume(volume_segment);
}
if (old_segment.volume() < 0.5 * invalid_value)
new_volume = volume_segment;
else
new_volume = old_segment.volume();
Segment new_segment(old_segment, new_length, new_depth, new_volume);
addSegment(new_segment);
}
current_index= range_end + 1;
@ -319,26 +308,23 @@ namespace Opm {
for (int i = 1; i < size(); ++i) {
assert(m_segments[i].dataReady());
if (m_segments[i].volume() == invalid_value) {
Segment new_segment = m_segments[i];
const auto& old_segment = this->m_segments[i];
const int outlet_segment = m_segments[i].outletSegment();
const int outlet_index = segmentNumberToIndex(outlet_segment);
const double segment_length = m_segments[i].totalLength() - m_segments[outlet_index].totalLength();
const double segment_volume = m_segments[i].crossArea() * segment_length;
new_segment.setVolume(segment_volume);
Segment new_segment(old_segment, segment_volume);
addSegment(new_segment);
}
}
}
void WellSegments::processINC(const bool first_time) {
void WellSegments::processINC(double depth_top, double length_top) {
// update the information inside the WellSegments to be in ABS way
if (first_time) {
Segment new_top_segment = (*this)[0];
new_top_segment.setDepthAndLength(depthTopSegment(), lengthTopSegment());
this->addSegment(new_top_segment);
}
Segment new_top_segment(this->m_segments[0], depth_top, length_top);
this->addSegment(new_top_segment);
orderSegments();
// begin with the second segment
@ -359,8 +345,7 @@ namespace Opm {
const double temp_length = outlet_length + m_segments[i_index].totalLength();
// applying the calculated length and depth to the current segment
Segment new_segment = this->m_segments[i_index];
new_segment.setDepthAndLength(temp_depth, temp_length);
Segment new_segment(this->m_segments[i_index], temp_depth, temp_length);
addSegment(new_segment);
}
}
@ -422,13 +407,7 @@ namespace Opm {
}
bool WellSegments::operator==( const WellSegments& rhs ) const {
return this->m_well_name == rhs.m_well_name
&& this->m_depth_top == rhs.m_depth_top
&& this->m_length_top == rhs.m_length_top
&& this->m_volume_top == rhs.m_volume_top
&& this->m_length_depth_type == rhs.m_length_depth_type
&& this->m_comp_pressure_drop == rhs.m_comp_pressure_drop
&& this->m_multiphase_model == rhs.m_multiphase_model
return this->m_comp_pressure_drop == rhs.m_comp_pressure_drop
&& this->m_segments.size() == rhs.m_segments.size()
&& this->segment_number_to_index.size() == rhs.segment_number_to_index.size()
&& std::equal( this->m_segments.begin(),
@ -462,8 +441,7 @@ namespace Opm {
bool WellSegments::updateWSEGSICD(const std::vector<std::pair<int, SpiralICD> >& sicd_pairs) {
if (m_comp_pressure_drop == CompPressureDrop::H__) {
const std::string msg = "to use spiral ICD segment for well " + m_well_name
+ " , you have to activate the frictional pressure drop calculation";
const std::string msg = "to use spiral ICD segment you have to activate the frictional pressure drop calculation";
throw std::runtime_error(msg);
}
@ -481,8 +459,7 @@ namespace Opm {
bool WellSegments::updateWSEGVALV(const std::vector<std::pair<int, Valve> >& valve_pairs) {
if (m_comp_pressure_drop == CompPressureDrop::H__) {
const std::string msg = "to use WSEGVALV segment for well " + m_well_name
+ " , you have to activate the frictional pressure drop calculation";
const std::string msg = "to use WSEGVALV segment you have to activate the frictional pressure drop calculation";
throw std::runtime_error(msg);
}
@ -576,10 +553,6 @@ WellSegments::MultiPhaseModel WellSegments::MultiPhaseModelFromString(const std:
}
}
WellSegments::LengthDepth WellSegments::lengthDepthType() const {
return m_length_depth_type;
}
const std::vector<Segment>& WellSegments::segments() const {
return m_segments;
}

View File

@ -758,16 +758,9 @@ bool Well::handleWELSEGS(const DeckKeyword& keyword) {
if( this->segments )
throw std::logic_error("re-entering WELSEGS for a well is not supported yet!!.");
auto new_segmentset = std::make_shared<WellSegments>();
new_segmentset->loadWELSEGS(keyword);
new_segmentset->process(true);
if (new_segmentset != this->segments) {
this->segments = new_segmentset;
this->ref_depth = new_segmentset->depthTopSegment();
return true;
} else
return false;
this->segments = std::make_shared<WellSegments>(keyword);
this->ref_depth = this->segments->depthTopSegment();
return true;
}

View File

@ -90,10 +90,8 @@ BOOST_AUTO_TEST_CASE(MultisegmentWellTest) {
const Opm::DeckKeyword compsegs = deck.getKeyword("COMPSEGS");
BOOST_CHECK_EQUAL( 8U, compsegs.size() );
Opm::WellSegments segment_set;
const Opm::DeckKeyword welsegs = deck.getKeyword("WELSEGS");
segment_set.loadWELSEGS(welsegs);
Opm::WellSegments segment_set(welsegs);
BOOST_CHECK_EQUAL(7U, segment_set.size());
@ -237,9 +235,8 @@ BOOST_AUTO_TEST_CASE(WrongDistanceCOMPSEGS) {
const Opm::DeckKeyword compsegs = deck.getKeyword("COMPSEGS");
BOOST_CHECK_EQUAL( 8U, compsegs.size() );
Opm::WellSegments segment_set;
const Opm::DeckKeyword welsegs = deck.getKeyword("WELSEGS");
segment_set.loadWELSEGS(welsegs);
Opm::WellSegments segment_set(welsegs);
BOOST_CHECK_EQUAL(6U, segment_set.size());
@ -295,9 +292,8 @@ BOOST_AUTO_TEST_CASE(NegativeDepthCOMPSEGS) {
const Opm::DeckKeyword compsegs = deck.getKeyword("COMPSEGS");
BOOST_CHECK_EQUAL( 8U, compsegs.size() );
Opm::WellSegments segment_set;
const Opm::DeckKeyword welsegs = deck.getKeyword("WELSEGS");
segment_set.loadWELSEGS(welsegs);
Opm::WellSegments segment_set(welsegs);
BOOST_CHECK_EQUAL(6U, segment_set.size());
@ -360,10 +356,8 @@ BOOST_AUTO_TEST_CASE(testwsegvalv) {
const Opm::DeckKeyword compsegs = deck.getKeyword("COMPSEGS");
BOOST_CHECK_EQUAL( 8U, compsegs.size() );
Opm::WellSegments segment_set;
const Opm::DeckKeyword welsegs = deck.getKeyword("WELSEGS");
segment_set.loadWELSEGS(welsegs);
Opm::WellSegments segment_set(welsegs);
BOOST_CHECK_EQUAL(8U, segment_set.size());

View File

@ -17,6 +17,9 @@
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include <iostream>
#include <opm/io/eclipse/rst/state.hpp>
#include <opm/io/eclipse/ERst.hpp>
@ -28,6 +31,7 @@ int main(int argc, char ** argv) {
Opm::EclIO::ERst rst_file(argv[iarg]);
for (int report_step : rst_file.listOfReportStepNumbers()) {
if (report_step > 0) {
std::cout << "Loading restart step: " << report_step << std::endl;
const auto& state = Opm::RestartIO::RstState::load(rst_file, report_step);
static_cast<void>(state); // Suppress unused variable warning.
}