208 lines
10 KiB
C++
208 lines
10 KiB
C++
/*
|
|
Copyright 2015 SINTEF ICT, Applied Mathematics.
|
|
|
|
This file is part of the Open Porous Media project (OPM).
|
|
|
|
OPM is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
OPM is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <opm/parser/eclipse/EclipseState/Schedule/Compsegs.hpp>
|
|
#include <opm/parser/eclipse/EclipseState/Schedule/ScheduleEnums.hpp>
|
|
#include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp>
|
|
#include <cmath>
|
|
|
|
namespace Opm {
|
|
|
|
|
|
Compsegs::Compsegs(int i_in, int j_in, int k_in, int branch_number_in, double distance_start_in, double distance_end_in,
|
|
WellCompletion::DirectionEnum dir_in, double center_depth_in, int segment_number_in)
|
|
: m_i(i_in),
|
|
m_j(j_in),
|
|
m_k(k_in),
|
|
m_branch_number(branch_number_in),
|
|
m_distance_start(distance_start_in),
|
|
m_distance_end(distance_end_in),
|
|
m_dir(dir_in),
|
|
m_center_depth(center_depth_in),
|
|
m_segment_number(segment_number_in)
|
|
{
|
|
}
|
|
|
|
std::vector<CompsegsPtr> Compsegs::compsegsFromCOMPSEGSKeyword(DeckKeywordConstPtr compsegsKeyword,
|
|
EclipseGridConstPtr grid) {
|
|
// the thickness of grid cells will be required in the future for more complete support.
|
|
// Silence warning about unused argument
|
|
static_cast<void>(grid);
|
|
|
|
// only handle the second record here
|
|
// The first record here only contains the well name
|
|
std::vector<CompsegsPtr> compsegs;
|
|
|
|
for (size_t recordIndex = 1; recordIndex < compsegsKeyword->size(); ++recordIndex) {
|
|
DeckRecordConstPtr record = compsegsKeyword->getRecord(recordIndex);
|
|
// following the coordinate rule for completions
|
|
const int I = record->getItem("I")->getInt(0) - 1;
|
|
const int J = record->getItem("J")->getInt(0) - 1;
|
|
const int K = record->getItem("K")->getInt(0) - 1;
|
|
const int branch = record->getItem("BRANCH")->getInt(0);
|
|
|
|
double distance_start;
|
|
double distance_end;
|
|
if (record->getItem("DISTANCE_START")->hasValue(0)) {
|
|
distance_start = record->getItem("DISTANCE_START")->getSIDouble(0);
|
|
} else if (recordIndex == 1) {
|
|
distance_start = 0.;
|
|
} else {
|
|
// TODO: the end of the previous connection or range
|
|
// 'previous' should be in term of the input order
|
|
// since basically no specific order for the completions
|
|
throw std::runtime_error("this way to obtain DISTANCE_START not implemented yet!");
|
|
}
|
|
if (record->getItem("DISTANCE_END")->hasValue(0)) {
|
|
distance_end = record->getItem("DISTANCE_END")->getSIDouble(0);
|
|
} else {
|
|
// TODO: the distance_start plus the thickness of the grid block
|
|
throw std::runtime_error("this way to obtain DISTANCE_END not implemented yet!");
|
|
}
|
|
|
|
WellCompletion::DirectionEnum direction;
|
|
if (record->getItem("DIRECTION")->hasValue(0)) {
|
|
direction = WellCompletion::DirectionEnumFromString(record->getItem("DIRECTION")->getString(0));
|
|
} else if (!record->getItem("DISTANCE_END")->hasValue(0)) {
|
|
throw std::runtime_error("the direction has to be specified when DISTANCE_END in the record is not specified");
|
|
}
|
|
|
|
int end_IJK;
|
|
if (record->getItem("END_IJK")->hasValue(0)) {
|
|
// following the coordinate rule for completions
|
|
end_IJK = record->getItem("END_IJK")->getInt(0) - 1;
|
|
if (!record->getItem("DIRECTION")->hasValue(0)) {
|
|
throw std::runtime_error("the direction has to be specified when END_IJK in the record is specified");
|
|
}
|
|
} else {
|
|
// only one completion is specified here
|
|
end_IJK = -1;
|
|
}
|
|
|
|
double center_depth;
|
|
if (!record->getItem("CENTER_DEPTH")->defaultApplied(0)) {
|
|
center_depth = record->getItem("CENTER_DEPTH")->getSIDouble(0);
|
|
} else {
|
|
// 0.0 is also the defaulted value
|
|
// which is used to indicate to obtain the final value through related segment
|
|
center_depth = 0.;
|
|
}
|
|
|
|
if (center_depth < 0.) {
|
|
//TODO: get the depth from COMPDAT data.
|
|
throw std::runtime_error("this way to obtain CENTER_DISTANCE not implemented yet either!");
|
|
}
|
|
|
|
int segment_number;
|
|
if (record->getItem("SEGMENT_NUMBER")->hasValue(0)) {
|
|
segment_number = record->getItem("SEGMENT_NUMBER")->getInt(0);
|
|
} else {
|
|
segment_number = 0;
|
|
// will decide the segment number based on the distance in a process later.
|
|
}
|
|
|
|
if (end_IJK < 0) { // only one compsegs
|
|
CompsegsPtr new_compsegs = std::make_shared<Compsegs>(I, J, K, branch, distance_start, distance_end,
|
|
direction, center_depth, segment_number);
|
|
compsegs.push_back(new_compsegs);
|
|
} else { // a range is defined. genrate a range of Compsegs
|
|
throw std::runtime_error("entering COMPSEGS entries with a range is not supported yet!");
|
|
}
|
|
}
|
|
|
|
return compsegs;
|
|
}
|
|
|
|
void Compsegs::processCOMPSEGS(std::vector<CompsegsPtr>& compsegs, SegmentSetConstPtr segment_set) {
|
|
// for the current cases we have at the moment, the distance information is specified explicitly,
|
|
// while the depth information is defaulted though, which need to be obtained from the related segment
|
|
for (size_t i_comp = 0; i_comp < compsegs.size(); ++i_comp) {
|
|
if (compsegs[i_comp]->m_segment_number == 0) { // need to determine the related segment number first
|
|
const double center_distance = (compsegs[i_comp]->m_distance_start + compsegs[i_comp]->m_distance_end) / 2.0;
|
|
const int branch_number = compsegs[i_comp]->m_branch_number;
|
|
|
|
int segment_number = 0;
|
|
double min_distance_difference = 1.e100; // begin with a big value
|
|
for (int i_segment = 0; i_segment < segment_set->numberSegment(); ++i_segment) {
|
|
SegmentConstPtr current_segment = (*segment_set)[i_segment];
|
|
if (branch_number == current_segment->branchNumber()) {
|
|
const double distance = current_segment->totalLength();
|
|
const double distance_difference = std::abs(center_distance - distance);
|
|
if (distance_difference < min_distance_difference) {
|
|
min_distance_difference = distance_difference;
|
|
segment_number = current_segment->segmentNumber();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (segment_number != 0) {
|
|
compsegs[i_comp]->m_segment_number = segment_number;
|
|
if (compsegs[i_comp]->m_center_depth == 0.) {
|
|
// using the depth of the segment node as the depth of the completion
|
|
// TODO: now only one completion for one segment is hanlded,
|
|
// TODO: later we will try to handle more than one completion for each segment,
|
|
// TODO: which will be a linear interpolation based on the segment node depth
|
|
// TODO: in the same branch, while the actually way is not clear yet
|
|
const int segment_location = segment_set->numberToLocation(segment_number);
|
|
compsegs[i_comp]->m_center_depth = (*segment_set)[segment_location]->depth();
|
|
} else if (compsegs[i_comp]->m_center_depth < 0.) {
|
|
throw std::runtime_error(" obtaining perforation depth from COMPDAT data is not supported yet \n");
|
|
}
|
|
} else {
|
|
throw std::runtime_error(" the perforation failed in finding a related segment \n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void Compsegs::updateCompletionsWithSegment(const std::vector<CompsegsPtr>& compsegs,
|
|
CompletionSetPtr completion_set) {
|
|
|
|
for (size_t i_comp = 0; i_comp < compsegs.size(); ++i_comp) {
|
|
const int i = compsegs[i_comp]->m_i;
|
|
const int j = compsegs[i_comp]->m_j;
|
|
const int k = compsegs[i_comp]->m_k;
|
|
|
|
size_t ic;
|
|
for (ic = 0; ic < completion_set->size(); ++ic) {
|
|
if (completion_set->get(ic)->sameCoordinate(i, j, k)) {
|
|
break; // the completion is found
|
|
}
|
|
}
|
|
|
|
if (ic == completion_set->size()) { // the completion is not found
|
|
throw std::runtime_error(" the completion specified in COMPSEGS is not found in the completionSet \n");
|
|
}
|
|
|
|
CompletionPtr new_completion = std::make_shared<Completion>(completion_set->get(ic));
|
|
new_completion->setSegmentNumber(compsegs[i_comp]->m_segment_number);
|
|
new_completion->setCenterDepth(compsegs[i_comp]->m_center_depth);
|
|
completion_set->add(new_completion);
|
|
}
|
|
|
|
for (size_t ic = 0; ic < completion_set->size(); ++ic) {
|
|
if (completion_set->get(ic)->getSegmentNumber() == -1) {
|
|
throw std::runtime_error(" not all the completions are specified with a segment,\n the information from COMPSEGS are not complete");
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|