373 lines
17 KiB
C++
373 lines
17 KiB
C++
#include <iostream>
|
|
#include <cassert>
|
|
#include <cmath>
|
|
#include <map>
|
|
|
|
#include <opm/parser/eclipse/EclipseState/Schedule/Segment.hpp>
|
|
#include <opm/parser/eclipse/EclipseState/Schedule/SegmentSet.hpp>
|
|
|
|
|
|
namespace Opm {
|
|
|
|
SegmentSet::SegmentSet() {
|
|
}
|
|
|
|
std::string SegmentSet::wellName() const {
|
|
return m_well_name;
|
|
}
|
|
|
|
int SegmentSet::numberBranch() const {
|
|
return m_number_branch;
|
|
}
|
|
|
|
int SegmentSet::numberSegment() const {
|
|
return m_segments.size();
|
|
}
|
|
|
|
double SegmentSet::depthTopSegment() const {
|
|
return m_depth_top;
|
|
}
|
|
|
|
double SegmentSet::lengthTopSegment() const {
|
|
return m_length_top;
|
|
}
|
|
|
|
double SegmentSet::volumeTopSegment() const {
|
|
return m_volume_top;
|
|
}
|
|
|
|
WellSegment::LengthDepthEnum SegmentSet::lengthDepthType() const {
|
|
return m_length_depth_type;
|
|
}
|
|
|
|
WellSegment::CompPressureDropEnum SegmentSet::compPressureDrop() const {
|
|
return m_comp_pressure_drop;
|
|
}
|
|
|
|
WellSegment::MultiPhaseModelEnum SegmentSet::multiPhaseModel() const {
|
|
return m_multiphase_model;
|
|
}
|
|
|
|
SegmentConstPtr SegmentSet::operator[](size_t idx) const {
|
|
return m_segments[idx];
|
|
}
|
|
|
|
int SegmentSet::numberToLocation(const int segment_number) const {
|
|
auto it = m_number_to_location.find(segment_number);
|
|
if (it != m_number_to_location.end()) {
|
|
return it->second;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
void SegmentSet::addSegment(SegmentConstPtr new_segment) {
|
|
// decide whether to push_back or insert
|
|
int segment_number = new_segment->segmentNumber();
|
|
|
|
const int segment_location = numberToLocation(segment_number);
|
|
|
|
if (segment_location < 0) { // it is a new segment
|
|
m_number_to_location[segment_number] = numberSegment();
|
|
m_segments.push_back(new_segment);
|
|
} else { // the segment already exists
|
|
m_segments[segment_location] = new_segment;
|
|
}
|
|
}
|
|
|
|
SegmentSet* SegmentSet::shallowCopy() const {
|
|
SegmentSet* copy = new SegmentSet();
|
|
copy->m_well_name = m_well_name;
|
|
copy->m_number_branch = m_number_branch;
|
|
copy->m_depth_top = m_depth_top;
|
|
copy->m_length_top = m_length_top;
|
|
copy->m_volume_top = m_volume_top;
|
|
copy->m_length_depth_type = m_length_depth_type;
|
|
copy->m_comp_pressure_drop = m_comp_pressure_drop;
|
|
copy->m_multiphase_model = m_multiphase_model;
|
|
copy->m_number_to_location = m_number_to_location;
|
|
copy->m_segments.resize(m_segments.size());
|
|
for (int i = 0; i < int(m_segments.size()); ++i) {
|
|
copy->m_segments[i] = m_segments[i];
|
|
}
|
|
return copy;
|
|
}
|
|
|
|
void SegmentSet::segmentsFromWELSEGSKeyword(DeckKeywordConstPtr welsegsKeyword) {
|
|
|
|
// for the first record, which provides the information for the top segment
|
|
// and information for the whole segment set
|
|
DeckRecordConstPtr 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 = WellSegment::LengthDepthEnumFromString(record1->getItem("INFO_TYPE")->getTrimmedString(0));
|
|
m_volume_top = record1->getItem("WELLBORE_VOLUME")->getSIDouble(0);
|
|
m_comp_pressure_drop = WellSegment::CompPressureDropEnumFromString(record1->getItem("PRESSURE_COMPONENTS")->getTrimmedString(0));
|
|
m_multiphase_model = WellSegment::MultiPhaseModelEnumFromString(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 == WellSegment::INC) {
|
|
SegmentConstPtr top_segment(new Segment(1, 1, 0, 0., 0., invalid_value, invalid_value, invalid_value,
|
|
m_volume_top, false));
|
|
m_segments.push_back(top_segment);
|
|
} else if (m_length_depth_type == WellSegment::ABS) {
|
|
SegmentConstPtr top_segment(new Segment(1, 1, 0, m_length_top, m_depth_top, invalid_value, invalid_value,
|
|
invalid_value, m_volume_top, true));
|
|
m_segments.push_back(top_segment);
|
|
}
|
|
|
|
// read all the information out from the DECK first then process to get all the required information
|
|
for (size_t recordIndex = 1; recordIndex < welsegsKeyword->size(); ++recordIndex) {
|
|
DeckRecordConstPtr record = welsegsKeyword->getRecord(recordIndex);
|
|
const int segment1 = record->getItem("SEGMENT1")->getInt(0);
|
|
const int segment2 = record->getItem("SEGMENT2")->getInt(0);
|
|
if ((segment1 < 2) || (segment2 < segment1)) {
|
|
throw std::logic_error("illegal segment number input is found in WELSEGS!\n");
|
|
}
|
|
|
|
// how to handle the logical relations between lateral branches and parent branches.
|
|
// so far, the branch number has not been used.
|
|
const int branch = record->getItem("BRANCH")->getInt(0);
|
|
if ((branch < 1)) {
|
|
throw std::logic_error("illegal branch number input is found in WELSEGS!\n");
|
|
}
|
|
const int outlet_segment_readin = record->getItem("JOIN_SEGMENT")->getInt(0);
|
|
double diameter = record->getItem("DIAMETER")->getSIDouble(0);
|
|
DeckItemConstPtr itemArea = record->getItem("AREA");
|
|
double area;
|
|
if (itemArea->hasValue(0)) {
|
|
area = itemArea->getSIDouble(0);
|
|
} else {
|
|
area = M_PI * diameter * diameter / 4.0;
|
|
}
|
|
|
|
// if the values are incremental values, then we can just use the values
|
|
// if the values are absolute values, then we need to calculate them during the next process
|
|
// only the value for the last segment in the range is recorded
|
|
const double segment_length = record->getItem("SEGMENT_LENGTH")->getSIDouble(0);
|
|
// the naming is a little confusing here.
|
|
// naming following the definition from the current keyword for the moment
|
|
const double depth_change = record->getItem("DEPTH_CHANGE")->getSIDouble(0);
|
|
|
|
DeckItemConstPtr itemVolume = record->getItem("VOLUME");
|
|
double volume;
|
|
if (itemVolume->hasValue(0)) {
|
|
volume = itemVolume->getSIDouble(0);
|
|
} else if (m_length_depth_type == WellSegment::INC) {
|
|
volume = area * segment_length;
|
|
} else {
|
|
volume = invalid_value; // A * L, while L is not determined yet
|
|
}
|
|
|
|
const double roughness = record->getItem("ROUGHNESS")->getSIDouble(0);
|
|
|
|
for (int i = segment1; i <= segment2; ++i) {
|
|
// for the first or the only segment in the range is the one specified in the WELSEGS
|
|
// from the second segment in the range, the outlet segment is the previous segment in the range
|
|
int outlet_segment = -1;
|
|
if (i == segment1) {
|
|
outlet_segment = outlet_segment_readin;
|
|
} else {
|
|
outlet_segment = i - 1;
|
|
}
|
|
|
|
if (m_length_depth_type == WellSegment::INC) {
|
|
m_segments.push_back(std::make_shared<const Segment>(i, branch, outlet_segment, segment_length, depth_change,
|
|
diameter, roughness, area, volume, false));
|
|
} else if (i == segment2) {
|
|
m_segments.push_back(std::make_shared<const Segment>(i, branch, outlet_segment, segment_length, depth_change,
|
|
diameter, roughness, area, volume, true));
|
|
} else {
|
|
m_segments.push_back(std::make_shared<const Segment>(i, branch, outlet_segment, invalid_value, invalid_value,
|
|
diameter, roughness, area, volume, false));
|
|
}
|
|
}
|
|
}
|
|
|
|
for (size_t i_segment = 0; i_segment < m_segments.size(); ++i_segment){
|
|
const int segment_number = m_segments[i_segment]->segmentNumber();
|
|
const int location = numberToLocation(segment_number);
|
|
if (location >= 0) { // found in the existing m_segments already
|
|
throw std::logic_error("Segments with same segment number are found!\n");
|
|
}
|
|
m_number_to_location[segment_number] = i_segment;
|
|
}
|
|
|
|
}
|
|
|
|
void SegmentSet::processABS() {
|
|
const double invalid_value = Segment::invalidValue(); // meaningless value to indicate unspecified/uncompleted values
|
|
|
|
orderSegments();
|
|
|
|
int current_loc = 1;
|
|
while (current_loc < numberSegment()) {
|
|
if (m_segments[current_loc]->dataReady()) {
|
|
current_loc ++;
|
|
continue;
|
|
}
|
|
|
|
const int range_begin = current_loc;
|
|
const int outlet_segment = m_segments[range_begin]->outletSegment();
|
|
const int outlet_loc = numberToLocation(outlet_segment);
|
|
|
|
assert(m_segments[outlet_loc]->dataReady() == true);
|
|
|
|
int range_end = range_begin + 1;
|
|
for (; range_end < numberSegment(); ++range_end) {
|
|
if (m_segments[range_end]->dataReady() == true) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (range_end >= numberSegment()) {
|
|
throw std::logic_error(" One range records in WELSEGS is wrong. ");
|
|
}
|
|
|
|
// set the length and depth values in the range.
|
|
int number_segments = range_end - range_begin + 1;
|
|
assert(number_segments > 1); //if only 1, the information should be complete
|
|
|
|
const double length_outlet = m_segments[outlet_loc]->totalLength();
|
|
const double depth_outlet = m_segments[outlet_loc]->depth();
|
|
|
|
const double length_last = m_segments[range_end]->totalLength();
|
|
const double depth_last = m_segments[range_end]->depth();
|
|
|
|
// incremental length and depth for the segments within the range
|
|
const double length_inc = (length_last - length_outlet) / number_segments;
|
|
const double depth_inc = (depth_last - depth_outlet) / number_segments;
|
|
const double volume_segment = m_segments[range_end]->crossArea() * length_inc;
|
|
|
|
for (int k = range_begin; k <= range_end; ++k) {
|
|
SegmentPtr new_segment = std::make_shared<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);
|
|
}
|
|
|
|
if (new_segment->volume() < 0.5 * invalid_value) {
|
|
new_segment->setVolume(volume_segment);
|
|
}
|
|
addSegment(new_segment);
|
|
}
|
|
current_loc = range_end + 1;
|
|
}
|
|
|
|
// then update the volume for all the segments except the top segment
|
|
// this is for the segments specified individually while the volume is not specified.
|
|
for (int i = 1; i < numberSegment(); ++i) {
|
|
assert(m_segments[i]->dataReady());
|
|
if (m_segments[i]->volume() == invalid_value) {
|
|
SegmentPtr new_segment = std::make_shared<Segment>(m_segments[i]);
|
|
const int outlet_segment = m_segments[i]->outletSegment();
|
|
const int outlet_location = numberToLocation(outlet_segment);
|
|
const double segment_length = m_segments[i]->totalLength() - m_segments[outlet_location]->totalLength();
|
|
const double segment_volume = m_segments[i]->crossArea() * segment_length;
|
|
new_segment->setVolume(segment_volume);
|
|
addSegment(new_segment);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SegmentSet::processINC(const bool first_time) {
|
|
|
|
// update the information inside the SegmentSet to be in ABS way
|
|
if (first_time) {
|
|
SegmentPtr new_top_segment = std::make_shared<Segment>((*this)[0]);
|
|
new_top_segment->setDepthAndLength(depthTopSegment(), lengthTopSegment());
|
|
this->addSegment(new_top_segment);
|
|
}
|
|
|
|
orderSegments();
|
|
|
|
// begin with the second segment
|
|
for (int i_loc = 0; i_loc < numberSegment(); ++i_loc) {
|
|
if (m_segments[i_loc]->dataReady() == false) {
|
|
// find its outlet segment
|
|
const int outlet_segment = m_segments[i_loc]->outletSegment();
|
|
const int outlet_loc = numberToLocation(outlet_segment);
|
|
|
|
// assert some information of the outlet_segment
|
|
assert(outlet_loc >= 0);
|
|
assert(m_segments[outlet_loc]->dataReady());
|
|
|
|
const double outlet_depth = m_segments[outlet_loc]->depth();
|
|
const double outlet_length = m_segments[outlet_loc]->totalLength();
|
|
const double temp_depth = outlet_depth + m_segments[i_loc]->depth();
|
|
const double temp_length = outlet_length + m_segments[i_loc]->totalLength();
|
|
|
|
// applying the calculated length and depth to the current segment
|
|
SegmentPtr new_segment = std::make_shared<Segment>(m_segments[i_loc]);
|
|
new_segment->setDepthAndLength(temp_depth, temp_length);
|
|
addSegment(new_segment);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SegmentSet::orderSegments() {
|
|
// re-ordering the segments to make later use easier.
|
|
// two principles
|
|
// 1. the location of the outlet segment will be stored in the lower location than the segment.
|
|
// 2. the segments belong to the same branch will be continuously stored.
|
|
|
|
// top segment will always be the first one
|
|
// before this location, the reordering is done.
|
|
int current_loc = 1;
|
|
|
|
// clear the mapping from segment number to store location
|
|
m_number_to_location.clear();
|
|
// for the top segment
|
|
m_number_to_location[1] = 0;
|
|
|
|
while (current_loc < numberSegment()) {
|
|
// the branch number of the last segment that is done re-ordering
|
|
const int last_branch_number = m_segments[current_loc-1]->branchNumber();
|
|
// the one need to be swapped to the current_loc.
|
|
int target_segment_loc = -1;
|
|
|
|
// looking for target_segment_loc
|
|
for (int i_loc = current_loc; i_loc < numberSegment(); ++i_loc) {
|
|
const int outlet_segment_number = m_segments[i_loc]->outletSegment();
|
|
const int outlet_segment_location = numberToLocation(outlet_segment_number);
|
|
if (outlet_segment_location < 0) { // not found the outlet_segment in the done re-ordering segments
|
|
continue;
|
|
}
|
|
if (target_segment_loc < 0) { // first time found a candidate
|
|
target_segment_loc = i_loc;
|
|
} else { // there is already a candidate, chosing the one with the same branch number with last_branch_number
|
|
const int old_target_segment_loc_branch = m_segments[target_segment_loc]->branchNumber();
|
|
const int new_target_segment_loc_branch = m_segments[i_loc]->branchNumber();
|
|
if (new_target_segment_loc_branch == last_branch_number) {
|
|
if (old_target_segment_loc_branch != last_branch_number) {
|
|
target_segment_loc = i_loc;
|
|
} else {
|
|
throw std::logic_error("two segments in the same branch share the same outlet segment !!\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (target_segment_loc < 0) {
|
|
throw std::logic_error("could not find candidate segment to swap in before the re-odering process get done !!\n");
|
|
}
|
|
assert(target_segment_loc >= current_loc);
|
|
if (target_segment_loc > current_loc) {
|
|
std::swap(m_segments[current_loc], m_segments[target_segment_loc]);
|
|
}
|
|
const int segment_number = m_segments[current_loc]->segmentNumber();
|
|
m_number_to_location[segment_number] = current_loc;
|
|
current_loc++;
|
|
}
|
|
}
|
|
|
|
}
|