diff --git a/opm/parser/eclipse/EclipseState/Schedule/Completion.hpp b/opm/parser/eclipse/EclipseState/Schedule/Completion.hpp index a2c2ebd84..caa4c36ef 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/Completion.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/Completion.hpp @@ -53,6 +53,7 @@ namespace Opm { Completion(const Completion&, WellCompletion::StateEnum newStatus); Completion(const Completion&, double wellPi); Completion(const Completion&, int complnum ); + Completion(const Completion& completion_initial, int segment_number, double center_depth); bool sameCoordinate(const Completion& other) const; bool sameCoordinate(const int i, const int j, const int k) const; @@ -72,7 +73,6 @@ namespace Opm { void shift_complnum( int ); int getSegmentNumber() const; double getCenterDepth() const; - void attachSegment(const int segmentNumber , const double centerDepth); bool attachedToSegment() const; WellCompletion::DirectionEnum getDirection() const; diff --git a/opm/parser/eclipse/EclipseState/Schedule/MSW/Segment.hpp b/opm/parser/eclipse/EclipseState/Schedule/MSW/Segment.hpp index 8fccb91b9..dc6f11ece 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/MSW/Segment.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/MSW/Segment.hpp @@ -21,6 +21,7 @@ #define SEGMENT_HPP_HEADER_INCLUDED #include +#include namespace Opm { @@ -45,6 +46,9 @@ namespace Opm { void setVolume(const double volume_in); void setDepthAndLength(const double depth_in, const double length_in); + const std::vector& inletSegments() const; + void addInletSegment(const int segment_number); + static double invalidValue(); bool operator==( const Segment& ) const; @@ -61,6 +65,8 @@ namespace Opm { // the outlet junction segment // for top segment, it should be -1 int m_outlet_segment; + // the segments whose outlet segments are the current segment + std::vector m_inlet_segments; // length of the segment node to the bhp reference point. // when reading in from deck, with 'INC', // it will be incremental length before processing. diff --git a/opm/parser/eclipse/EclipseState/Schedule/MSW/SegmentSet.hpp b/opm/parser/eclipse/EclipseState/Schedule/MSW/SegmentSet.hpp index 195a8edd6..662ce7a1e 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/MSW/SegmentSet.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/MSW/SegmentSet.hpp @@ -45,11 +45,15 @@ namespace Opm { WellSegment::CompPressureDropEnum compPressureDrop() const; WellSegment::MultiPhaseModelEnum multiPhaseModel() const; - int numberToLocation(const int segment_number) 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 segmentsFromWELSEGSKeyword( const DeckKeyword& welsegsKeyword); + const Segment& getFromSegmentNumber(const int segment_number) const; + const Segment& operator[](size_t idx) const; void orderSegments(); void processABS(); @@ -86,8 +90,8 @@ namespace Opm { std::vector< Segment > m_segments; // the mapping from the segment number to the - // storage location in the vector - std::map m_number_to_location; + // storage index in the vector + std::map m_segment_number_to_index; }; } diff --git a/opm/parser/eclipse/EclipseState/Schedule/MSW/updatingCompletionsWithSegments.hpp b/opm/parser/eclipse/EclipseState/Schedule/MSW/updatingCompletionsWithSegments.hpp new file mode 100644 index 000000000..37b8f709d --- /dev/null +++ b/opm/parser/eclipse/EclipseState/Schedule/MSW/updatingCompletionsWithSegments.hpp @@ -0,0 +1,32 @@ +/* + Copyright 2017 SINTEF Digital, Mathematics and Cybernetics. + + 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 . +*/ + +#ifndef UPDATING_COMPLETIONS_WITH_SEGMENTS +#define UPDATING_COMPLETIONS_WITH_SEGMENTS + +#include + +#include +#include + +namespace Opm { + CompletionSet updatingCompletionsWithSegments(const DeckKeyword& compsegs, const CompletionSet& input_completions, const SegmentSet& segments); +} + +#endif diff --git a/src/opm/parser/eclipse/CMakeLists.txt b/src/opm/parser/eclipse/CMakeLists.txt index ad8ba6f83..2f5dbfaab 100644 --- a/src/opm/parser/eclipse/CMakeLists.txt +++ b/src/opm/parser/eclipse/CMakeLists.txt @@ -82,6 +82,7 @@ set(opmparser_SOURCES Deck/Deck.cpp EclipseState/Schedule/MSW/Compsegs.cpp EclipseState/Schedule/MSW/Segment.cpp EclipseState/Schedule/MSW/SegmentSet.cpp + EclipseState/Schedule/MSW/updatingCompletionsWithSegments.cpp EclipseState/Schedule/OilVaporizationProperties.cpp EclipseState/Schedule/Schedule.cpp EclipseState/Schedule/ScheduleEnums.cpp @@ -229,6 +230,7 @@ foreach(test ADDREGTests MessageContainerTest MessageLimitTests MultiRegTests + MultisegmentWellTests MULTREGTScannerTests OrderedMapTests ParseContextTests diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/Completion.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/Completion.cpp index d09412de6..b19590691 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/Completion.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/Completion.cpp @@ -77,6 +77,14 @@ namespace Opm { this->m_complnum = num; } + Completion::Completion(const Completion& completion_initial, int segment_number, double center_depth) + : Completion(completion_initial) + { + assert(segment_number > 0); + this->m_segment_number = segment_number; + this->m_center_depth = center_depth; + } + bool Completion::sameCoordinate(const Completion& other) const { if ((m_i == other.m_i) && (m_j == other.m_j) && @@ -301,13 +309,6 @@ namespace Opm { return m_center_depth; } - void Completion::attachSegment(int segmentNumber , double centerDepth) { - assert(segmentNumber > 0); - - m_segment_number = segmentNumber; - m_center_depth = centerDepth; - } - bool Completion::attachedToSegment() const { return (m_segment_number > 0); } diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/MSW/Compsegs.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/MSW/Compsegs.cpp index 47a46bff2..aeff3cd53 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/MSW/Compsegs.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/MSW/Compsegs.cpp @@ -23,7 +23,7 @@ #include #include #include -#include +#include "Compsegs.hpp" #include #include #include @@ -168,18 +168,66 @@ namespace Opm { } compseg.m_segment_number = segment_number; + + // when depth is default or zero, we obtain the depth of the completion based on the information + // of the related segments if (compseg.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); - compseg.m_center_depth = segment_set[segment_location].depth(); + compseg.calculateCenterDepthWithSegments(segment_set); } } } + void Compsegs::calculateCenterDepthWithSegments(const SegmentSet& segment_set) { + + // the depth and distance of the segment to the well head + const Segment& segment = segment_set.getFromSegmentNumber(m_segment_number); + const double segment_depth = segment.depth(); + const double segment_distance = segment.totalLength(); + + // for top segment, no interpolation is needed + if (m_segment_number == 1) { + m_center_depth = segment_depth; + return; + } + + // for other cases, interpolation between two segments is needed. + // looking for the other segment needed for interpolation + // by default, it uses the outlet segment to do the interpolation + int interpolation_segment_number = segment.outletSegment(); + + const double center_distance = (m_distance_start + m_distance_end) / 2.0; + // if the perforation is further than the segment and the segment has inlet segments in the same branch + // we use the inlet segment to do the interpolation + if (center_distance > segment_distance) { + for (const int inlet : segment.inletSegments()) { + const int inlet_index = segment_set.segmentNumberToIndex(inlet); + if (segment_set[inlet_index].branchNumber() == m_branch_number) { + interpolation_segment_number = inlet; + break; + } + } + } + + if (interpolation_segment_number == 0) { + throw std::runtime_error("Failed in finding a segment to do the interpolation with segment " + + std::to_string(m_segment_number)); + } + + // performing the interpolation + const Segment& interpolation_segment = segment_set.getFromSegmentNumber(interpolation_segment_number); + const double interpolation_detph = interpolation_segment.depth(); + const double interpolation_distance = interpolation_segment.totalLength(); + + const double depth_change_segment = segment_depth - interpolation_detph; + const double segment_length = segment_distance - interpolation_distance; + + if (segment_length == 0.) { + throw std::runtime_error("Zero segment length is botained when doing interpolation between segment " + + std::to_string(m_segment_number) + " and segment " + std::to_string(interpolation_segment_number) ); + } + + m_center_depth = segment_depth + (center_distance - segment_distance) / segment_length * depth_change_segment; + } void Compsegs::updateCompletionsWithSegment(const std::vector< Compsegs >& compsegs, CompletionSet& completion_set) { @@ -189,9 +237,8 @@ namespace Opm { const int j = compseg.m_j; const int k = compseg.m_k; - auto new_completion = completion_set.getFromIJK( i, j, k ); - new_completion.attachSegment(compseg.m_segment_number, compseg.m_center_depth); - completion_set.add(new_completion); + const Completion& completion = completion_set.getFromIJK( i, j, k ); + completion_set.add(Completion(completion, compseg.m_segment_number, compseg.m_center_depth) ); } for (size_t ic = 0; ic < completion_set.size(); ++ic) { diff --git a/opm/parser/eclipse/EclipseState/Schedule/MSW/Compsegs.hpp b/src/opm/parser/eclipse/EclipseState/Schedule/MSW/Compsegs.hpp similarity index 96% rename from opm/parser/eclipse/EclipseState/Schedule/MSW/Compsegs.hpp rename to src/opm/parser/eclipse/EclipseState/Schedule/MSW/Compsegs.hpp index 34af35a33..05e216510 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/MSW/Compsegs.hpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/MSW/Compsegs.hpp @@ -52,6 +52,8 @@ namespace Opm { 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); + void calculateCenterDepthWithSegments(const SegmentSet& segment_set); + static std::vector< Compsegs > compsegsFromCOMPSEGSKeyword( const DeckKeyword& compsegsKeyword ); // get the segment number information and depth information based on the information from SegmentSet diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/MSW/Segment.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/MSW/Segment.cpp index 49df3acb0..24a8b16ff 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/MSW/Segment.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/MSW/Segment.cpp @@ -109,6 +109,14 @@ namespace Opm { m_volume = volume_in; } + const std::vector& Segment::inletSegments() const { + return m_inlet_segments; + } + + void Segment::addInletSegment(const int segment_number) { + m_inlet_segments.push_back(segment_number); + } + double Segment::invalidValue() { return invalid_value; } diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/MSW/SegmentSet.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/MSW/SegmentSet.cpp index 62866f1a3..659d25e1c 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/MSW/SegmentSet.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/MSW/SegmentSet.cpp @@ -22,7 +22,7 @@ #include #ifdef _WIN32 -#define _USE_MATH_DEFINES +#define _USE_MATH_DEFINES #include #endif @@ -75,9 +75,9 @@ namespace Opm { 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()) { + int SegmentSet::segmentNumberToIndex(const int segment_number) const { + const auto it = m_segment_number_to_index.find(segment_number); + if (it != m_segment_number_to_index.end()) { return it->second; } else { return -1; @@ -86,15 +86,15 @@ namespace Opm { void SegmentSet::addSegment( Segment new_segment ) { // decide whether to push_back or insert - int segment_number = new_segment.segmentNumber(); + const int segment_number = new_segment.segmentNumber(); - const int segment_location = numberToLocation(segment_number); + const int segment_index = segmentNumberToIndex(segment_number); - if (segment_location < 0) { // it is a new segment - m_number_to_location[segment_number] = numberSegment(); + if (segment_index < 0) { // it is a new segment + m_segment_number_to_index[segment_number] = numberSegment(); m_segments.push_back(new_segment); } else { // the segment already exists - m_segments[segment_location] = new_segment; + m_segments[segment_index] = new_segment; } } @@ -197,15 +197,35 @@ namespace Opm { } } - for (size_t i_segment = 0; i_segment < m_segments.size(); ++i_segment){ + 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 + const int index = segmentNumberToIndex(segment_number); + if (index >= 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; + m_segment_number_to_index[segment_number] = i_segment; } + for (size_t i_segment = 0; i_segment < m_segments.size(); ++i_segment) { + const int segment_number = m_segments[i_segment].segmentNumber(); + const int outlet_segment = m_segments[i_segment].outletSegment(); + if (outlet_segment <= 0) { // no outlet segment + continue; + } + const int outlet_segment_index = m_segment_number_to_index[outlet_segment]; + m_segments[outlet_segment_index].addInletSegment(segment_number); + } + + } + + const Segment& SegmentSet::getFromSegmentNumber(const int segment_number) const { + // the index of segment in the vector of segments + const int segment_index = segmentNumberToIndex(segment_number); + if (segment_index < 0) { + throw std::runtime_error("Could not indexate the segment " + std::to_string(segment_number) + + " when trying to get the segment "); + } + return m_segments[segment_index]; } void SegmentSet::processABS() { @@ -213,18 +233,18 @@ namespace Opm { orderSegments(); - int current_loc = 1; - while (current_loc < numberSegment()) { - if (m_segments[current_loc].dataReady()) { - current_loc ++; + int current_index= 1; + while (current_index< numberSegment()) { + if (m_segments[current_index].dataReady()) { + current_index++; continue; } - const int range_begin = current_loc; + const int range_begin = current_index; const int outlet_segment = m_segments[range_begin].outletSegment(); - const int outlet_loc = numberToLocation(outlet_segment); + const int outlet_index= segmentNumberToIndex(outlet_segment); - assert(m_segments[outlet_loc].dataReady() == true); + assert(m_segments[outlet_index].dataReady() == true); int range_end = range_begin + 1; for (; range_end < numberSegment(); ++range_end) { @@ -241,8 +261,8 @@ namespace Opm { 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_outlet = m_segments[outlet_index].totalLength(); + const double depth_outlet = m_segments[outlet_index].depth(); const double length_last = m_segments[range_end].totalLength(); const double depth_last = m_segments[range_end].depth(); @@ -265,7 +285,7 @@ namespace Opm { } addSegment(new_segment); } - current_loc = range_end + 1; + current_index= range_end + 1; } // then update the volume for all the segments except the top segment @@ -275,8 +295,8 @@ namespace Opm { if (m_segments[i].volume() == invalid_value) { Segment new_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 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); addSegment(new_segment); @@ -296,24 +316,24 @@ namespace Opm { orderSegments(); // begin with the second segment - for (int i_loc = 1; i_loc < numberSegment(); ++i_loc) { - if( m_segments[i_loc].dataReady() ) continue; + for (int i_index= 1; i_index< numberSegment(); ++i_index) { + if( m_segments[i_index].dataReady() ) continue; // find its outlet segment - const int outlet_segment = m_segments[i_loc].outletSegment(); - const int outlet_loc = numberToLocation(outlet_segment); + const int outlet_segment = m_segments[i_index].outletSegment(); + const int outlet_index= segmentNumberToIndex(outlet_segment); // assert some information of the outlet_segment - assert(outlet_loc >= 0); - assert(m_segments[outlet_loc].dataReady()); + assert(outlet_index>= 0); + assert(m_segments[outlet_index].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(); + const double outlet_depth = m_segments[outlet_index].depth(); + const double outlet_length = m_segments[outlet_index].totalLength(); + const double temp_depth = outlet_depth + m_segments[i_index].depth(); + 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_loc]; + Segment new_segment = this->m_segments[i_index]; new_segment.setDepthAndLength(temp_depth, temp_length); addSegment(new_segment); } @@ -322,39 +342,39 @@ namespace Opm { 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. + // 1. the index of the outlet segment will be stored in the lower index 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; + // before this index, the reordering is done. + int current_index= 1; - // clear the mapping from segment number to store location - m_number_to_location.clear(); + // clear the mapping from segment number to store index + m_segment_number_to_index.clear(); // for the top segment - m_number_to_location[1] = 0; + m_segment_number_to_index[1] = 0; - while (current_loc < numberSegment()) { + while (current_index< 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; + const int last_branch_number = m_segments[current_index-1].branchNumber(); + // the one need to be swapped to the current_index. + int target_segment_index= -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 + // looking for target_segment_index + for (int i_index= current_index; i_index< numberSegment(); ++i_index) { + const int outlet_segment_number = m_segments[i_index].outletSegment(); + const int outlet_segment_index = segmentNumberToIndex(outlet_segment_number); + if (outlet_segment_index < 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; + if (target_segment_index< 0) { // first time found a candidate + target_segment_index= i_index; } 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; + const int old_target_segment_index_branch = m_segments[target_segment_index].branchNumber(); + const int new_target_segment_index_branch = m_segments[i_index].branchNumber(); + if (new_target_segment_index_branch == last_branch_number) { + if (old_target_segment_index_branch != last_branch_number) { + target_segment_index= i_index; } else { throw std::logic_error("two segments in the same branch share the same outlet segment !!\n"); } @@ -362,16 +382,16 @@ namespace Opm { } } - if (target_segment_loc < 0) { + if (target_segment_index< 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]); + assert(target_segment_index>= current_index); + if (target_segment_index> current_index) { + std::swap(m_segments[current_index], m_segments[target_segment_index]); } - const int segment_number = m_segments[current_loc].segmentNumber(); - m_number_to_location[segment_number] = current_loc; - current_loc++; + const int segment_number = m_segments[current_index].segmentNumber(); + m_segment_number_to_index[segment_number] = current_index; + current_index++; } } @@ -385,13 +405,13 @@ namespace Opm { && this->m_comp_pressure_drop == rhs.m_comp_pressure_drop && this->m_multiphase_model == rhs.m_multiphase_model && this->m_segments.size() == rhs.m_segments.size() - && this->m_number_to_location.size() == rhs.m_number_to_location.size() + && this->m_segment_number_to_index.size() == rhs.m_segment_number_to_index.size() && std::equal( this->m_segments.begin(), this->m_segments.end(), rhs.m_segments.begin() ) - && std::equal( this->m_number_to_location.begin(), - this->m_number_to_location.end(), - rhs.m_number_to_location.begin() ); + && std::equal( this->m_segment_number_to_index.begin(), + this->m_segment_number_to_index.end(), + rhs.m_segment_number_to_index.begin() ); } bool SegmentSet::operator!=( const SegmentSet& rhs ) const { diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/MSW/updatingCompletionsWithSegments.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/MSW/updatingCompletionsWithSegments.cpp new file mode 100644 index 000000000..4a9327616 --- /dev/null +++ b/src/opm/parser/eclipse/EclipseState/Schedule/MSW/updatingCompletionsWithSegments.cpp @@ -0,0 +1,37 @@ +/* + Copyright 2017 SINTEF Digital, Mathematics and Cybernetics. + + 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 . +*/ + +#include +#include "Compsegs.hpp" + + +namespace Opm { + + CompletionSet updatingCompletionsWithSegments(const DeckKeyword& compsegs, + const CompletionSet& input_completions, + const SegmentSet& segment_set) + { + CompletionSet new_completion_set(input_completions); + + std::vector compsegs_vector = Compsegs::compsegsFromCOMPSEGSKeyword( compsegs ); + Compsegs::processCOMPSEGS(compsegs_vector, segment_set); + Compsegs::updateCompletionsWithSegment(compsegs_vector, new_completion_set); + return new_completion_set; + } +} diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp index ca3b315ff..72ecc4acc 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp @@ -39,8 +39,8 @@ #include #include #include -#include #include +#include #include #include @@ -1254,15 +1254,11 @@ namespace Opm { const std::string& well_name = record1.getItem("WELL").getTrimmedString(0); auto& well = this->m_wells.get( well_name ); - auto compsegs_vector = Compsegs::compsegsFromCOMPSEGSKeyword( keyword ); + const auto& segment_set = well.getSegmentSet(currentStep); + const auto& completion_set = well.getCompletions( currentStep ); + const CompletionSet new_completion_set = updatingCompletionsWithSegments(keyword, completion_set, segment_set); - const auto& current_segmentSet = well.getSegmentSet(currentStep); - Compsegs::processCOMPSEGS(compsegs_vector, current_segmentSet); - - // it is necessary to update the segment related information for some completions. - auto new_completionSet = well.getCompletions( currentStep ); - Compsegs::updateCompletionsWithSegment(compsegs_vector, new_completionSet); - well.addCompletionSet(currentStep, new_completionSet); + well.addCompletionSet(currentStep, new_completion_set); } void Schedule::handleWGRUPCON( const DeckKeyword& keyword, size_t currentStep) { diff --git a/tests/parser/MultisegmentWellTests.cpp b/tests/parser/MultisegmentWellTests.cpp new file mode 100644 index 000000000..c1e378bf8 --- /dev/null +++ b/tests/parser/MultisegmentWellTests.cpp @@ -0,0 +1,123 @@ +/* + Copyright 2017 SINTEF Digital, Mathematics and Cybernetics. + + 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 . + */ + +#include +#include +#include + +#define BOOST_TEST_MODULE CompletionSetTests +#include +#include + +#include + + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +BOOST_AUTO_TEST_CASE(MultisegmentWellTest) { + Opm::CompletionSet completion_set; + completion_set.add(Opm::Completion( 19, 0, 0, 1, 0.0, Opm::WellCompletion::OPEN , Opm::Value("ConnectionTransmissibilityFactor", 200.), Opm::Value("D", 0.5), Opm::Value("SKIN", 0.), 0) ); + completion_set.add(Opm::Completion( 19, 0, 1, 1, 0.0, Opm::WellCompletion::OPEN , Opm::Value("ConnectionTransmissibilityFactor", 200.), Opm::Value("D", 0.5), Opm::Value("SKIN", 0.), 0) ); + completion_set.add(Opm::Completion( 19, 0, 2, 1, 0.0, Opm::WellCompletion::OPEN , Opm::Value("ConnectionTransmissibilityFactor", 200.), Opm::Value("D", 0.4), Opm::Value("SKIN", 0.), 0) ); + completion_set.add(Opm::Completion( 18, 0, 1, 1, 0.0, Opm::WellCompletion::OPEN , Opm::Value("ConnectionTransmissibilityFactor", 200.), Opm::Value("D", 0.4), Opm::Value("SKIN", 0.), 0, Opm::WellCompletion::DirectionEnum::X) ); + completion_set.add(Opm::Completion( 17, 0, 1, 1, 0.0, Opm::WellCompletion::OPEN , Opm::Value("ConnectionTransmissibilityFactor", 200.), Opm::Value("D", 0.4), Opm::Value("SKIN", 0.), 0, Opm::WellCompletion::DirectionEnum::X) ); + completion_set.add(Opm::Completion( 16, 0, 1, 1, 0.0, Opm::WellCompletion::OPEN , Opm::Value("ConnectionTransmissibilityFactor", 200.), Opm::Value("D", 0.4), Opm::Value("SKIN", 0.), 0, Opm::WellCompletion::DirectionEnum::X) ); + completion_set.add(Opm::Completion( 15, 0, 1, 1, 0.0, Opm::WellCompletion::OPEN , Opm::Value("ConnectionTransmissibilityFactor", 200.), Opm::Value("D", 0.4), Opm::Value("SKIN", 0.), 0, Opm::WellCompletion::DirectionEnum::X) ); + + BOOST_CHECK_EQUAL( 7U , completion_set.size() ); + + const std::string compsegs_string = + "WELSEGS \n" + "'PROD01' 2512.5 2512.5 1.0e-5 'ABS' 'H--' 'HO' /\n" + "2 2 1 1 2537.5 2537.5 0.3 0.00010 /\n" + "3 3 1 2 2562.5 2562.5 0.2 0.00010 /\n" + "4 4 2 2 2737.5 2537.5 0.2 0.00010 /\n" + "6 6 2 4 3037.5 2539.5 0.2 0.00010 /\n" + "7 7 2 6 3337.5 2534.5 0.2 0.00010 /\n" + "/\n" + "\n" + "COMPSEGS\n" + "PROD01 / \n" + "20 1 1 1 2512.5 2525.0 /\n" + "20 1 2 1 2525.0 2550.0 /\n" + "20 1 3 1 2550.0 2575.0 /\n" + "19 1 2 2 2637.5 2837.5 /\n" + "18 1 2 2 2837.5 3037.5 /\n" + "17 1 2 2 3037.5 3237.5 /\n" + "16 1 2 2 3237.5 3437.5 /\n" + "/\n"; + + Opm::Parser parser; + Opm::Deck deck = parser.parseString(compsegs_string, Opm::ParseContext()); + + const Opm::DeckKeyword compsegs = deck.getKeyword("COMPSEGS"); + BOOST_CHECK_EQUAL( 8U, compsegs.size() ); + + Opm::SegmentSet segment_set; + const Opm::DeckKeyword welsegs = deck.getKeyword("WELSEGS"); + segment_set.segmentsFromWELSEGSKeyword(welsegs); + + BOOST_CHECK_EQUAL(6U, segment_set.numberSegment()); + + const Opm::CompletionSet new_completion_set = Opm::updatingCompletionsWithSegments(compsegs, completion_set, segment_set); + + BOOST_CHECK_EQUAL(7U, new_completion_set.size()); + + const Opm::Completion& completion1 = new_completion_set.get(0); + const int segment_number_completion1 = completion1.getSegmentNumber(); + const double center_depth_completion1 = completion1.getCenterDepth(); + BOOST_CHECK_EQUAL(segment_number_completion1, 1); + BOOST_CHECK_EQUAL(center_depth_completion1, 2512.5); + + const Opm::Completion& completion3 = new_completion_set.get(2); + const int segment_number_completion3 = completion3.getSegmentNumber(); + const double center_depth_completion3 = completion3.getCenterDepth(); + BOOST_CHECK_EQUAL(segment_number_completion3, 3); + BOOST_CHECK_EQUAL(center_depth_completion3, 2562.5); + + const Opm::Completion& completion5 = new_completion_set.get(4); + const int segment_number_completion5 = completion5.getSegmentNumber(); + const double center_depth_completion5 = completion5.getCenterDepth(); + BOOST_CHECK_EQUAL(segment_number_completion5, 6); + BOOST_CHECK_CLOSE(center_depth_completion5, 2538.83, 0.001); + + const Opm::Completion& completion6 = new_completion_set.get(5); + const int segment_number_completion6 = completion6.getSegmentNumber(); + const double center_depth_completion6 = completion6.getCenterDepth(); + BOOST_CHECK_EQUAL(segment_number_completion6, 6); + BOOST_CHECK_CLOSE(center_depth_completion6, 2537.83, 0.001); + + const Opm::Completion& completion7 = new_completion_set.get(6); + const int segment_number_completion7 = completion7.getSegmentNumber(); + const double center_depth_completion7 = completion7.getCenterDepth(); + BOOST_CHECK_EQUAL(segment_number_completion7, 7); + BOOST_CHECK_EQUAL(center_depth_completion7, 2534.5); +} + diff --git a/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_MULTISEGMENT_WELL b/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_MULTISEGMENT_WELL index 654b4e6f4..6a3017392 100644 --- a/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_MULTISEGMENT_WELL +++ b/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_MULTISEGMENT_WELL @@ -4,9 +4,31 @@ -- License: http://opendatacommons.org/licenses/dbcl/1.0/ -- Copyright (C) 2015 SINTEF ICT, Applied Mathematics + +RUNSPEC + +DIMENS +20 1 5 / + START 06 'NOV' 2011 / +GRID + +DX +100*200 / +DY +100*200 / +DZ +100*25 / + +TOPS +20*2500 +20*2525 +20*2550 +20*2575 +20*2600 / + SCHEDULE WELSPECS @@ -22,6 +44,7 @@ COMPDAT 'PROD01' 19 1 2 2 'OPEN' 1* 200. 0.4 / 'PROD01' 18 1 2 2 'OPEN' 1* 200. 0.4 / 'PROD01' 17 1 2 2 'OPEN' 1* 200. 0.4 / + 'PROD01' 16 1 2 2 'OPEN' 1* 200. 0.4 / / WELSEGS @@ -29,8 +52,8 @@ WELSEGS 2 2 1 1 2537.5 2537.5 0.3 0.00010 / 3 3 1 2 2562.5 2562.5 0.2 0.00010 / 4 4 2 2 2737.5 2537.5 0.2 0.00010 / - 5 5 2 4 2937.5 2537.5 0.2 0.00010 / - 6 6 2 5 3137.5 2537.5 0.2 0.00010 / + 6 6 2 4 3037.5 2539.5 0.2 0.00010 / + 7 7 2 6 3337.5 2534.5 0.2 0.00010 / / COMPSEGS @@ -41,4 +64,5 @@ COMPSEGS 19 1 2 2 2637.5 2837.5 / 18 1 2 2 2837.5 3037.5 / 17 1 2 2 3037.5 3237.5 / + 16 1 2 2 3237.5 3437.5 / / diff --git a/tests/parser/integration/ParseKEYWORD.cpp b/tests/parser/integration/ParseKEYWORD.cpp index 53d37b0ad..bb01936fb 100644 --- a/tests/parser/integration/ParseKEYWORD.cpp +++ b/tests/parser/integration/ParseKEYWORD.cpp @@ -440,9 +440,16 @@ BOOST_AUTO_TEST_CASE( MULTREGT_ECLIPSE_STATE ) { } BOOST_AUTO_TEST_CASE( MULTISEGMENT_ABS ) { - Parser parser; - std::string deckFile(pathprefix() + "SCHEDULE/SCHEDULE_MULTISEGMENT_WELL"); - auto deck = parser.parseFile(deckFile, ParseContext()); + const Parser parser; + const std::string deckFile(pathprefix() + "SCHEDULE/SCHEDULE_MULTISEGMENT_WELL"); + const auto deck = parser.parseFile(deckFile, ParseContext()); + const ParseContext parseContext; + const EclipseState state(deck, parseContext); + const auto& grid = state.getInputGrid(); + const TableManager table ( deck ); + const Eclipse3DProperties eclipseProperties ( deck , table, grid); + const Schedule sched(parseContext, grid, eclipseProperties, deck, Phases(true, true, true) ); + // for WELSEGS keyword const auto& kw = deck.getKeyword("WELSEGS"); @@ -495,7 +502,7 @@ BOOST_AUTO_TEST_CASE( MULTISEGMENT_ABS ) { } { - const auto& rec6 = kw.getRecord(5); + const auto& rec6 = kw.getRecord(4); const int segment1 = rec6.getItem("SEGMENT2").get< int >(0); const int segment2 = rec6.getItem("SEGMENT2").get< int >(0); BOOST_CHECK_EQUAL( 6, segment1 ); @@ -507,9 +514,9 @@ BOOST_AUTO_TEST_CASE( MULTISEGMENT_ABS ) { const double diameter = rec6.getItem("DIAMETER").get< double >(0); const double roughness = rec6.getItem("ROUGHNESS").get< double >(0); BOOST_CHECK_EQUAL( 2, branch ); - BOOST_CHECK_EQUAL( 5, outlet_segment ); - BOOST_CHECK_EQUAL( 3137.5, segment_length ); - BOOST_CHECK_EQUAL( 2537.5, depth_change ); + BOOST_CHECK_EQUAL( 4, outlet_segment ); + BOOST_CHECK_EQUAL( 3037.5, segment_length ); + BOOST_CHECK_EQUAL( 2539.5, depth_change ); BOOST_CHECK_EQUAL( 0.2, diameter ); BOOST_CHECK_EQUAL( 0.0001, roughness ); } @@ -517,7 +524,7 @@ BOOST_AUTO_TEST_CASE( MULTISEGMENT_ABS ) { // for COMPSEG keyword const auto& kw1 = deck.getKeyword("COMPSEGS"); // check the size of the keywords - BOOST_CHECK_EQUAL( 7, kw1.size() ); + BOOST_CHECK_EQUAL( 8, kw1.size() ); // first record only contains the well name { const auto& rec1 = kw1.getRecord(0); @@ -559,6 +566,39 @@ BOOST_AUTO_TEST_CASE( MULTISEGMENT_ABS ) { BOOST_CHECK_EQUAL( 3037.5, distance_start ); BOOST_CHECK_EQUAL( 3237.5, distance_end ); } + + // checking the relation between segments and completions + // and also the depth of completions + { + BOOST_CHECK(sched.hasWell("PROD01")); + const auto* well = sched.getWell("PROD01"); + const auto& completions = well->getCompletions(0); + BOOST_CHECK_EQUAL(7U, completions.size()); + + const Completion& completion5 = completions.get(4); + const int seg_number_completion5 = completion5.getSegmentNumber(); + const double completion5_depth = completion5.getCenterDepth(); + BOOST_CHECK_EQUAL(seg_number_completion5, 6); + BOOST_CHECK_CLOSE(completion5_depth, 2538.83, 0.001); + + const Completion& completion6 = completions.get(5); + const int seg_number_completion6 = completion6.getSegmentNumber(); + const double completion6_depth = completion6.getCenterDepth(); + BOOST_CHECK_EQUAL(seg_number_completion6, 6); + BOOST_CHECK_CLOSE(completion6_depth, 2537.83, 0.001); + + const Completion& completion1 = completions.get(0); + const int seg_number_completion1 = completion1.getSegmentNumber(); + const double completion1_depth = completion1.getCenterDepth(); + BOOST_CHECK_EQUAL(seg_number_completion1, 1); + BOOST_CHECK_EQUAL(completion1_depth, 2512.5); + + const Completion& completion3 = completions.get(2); + const int seg_number_completion3 = completion3.getSegmentNumber(); + const double completion3_depth = completion3.getCenterDepth(); + BOOST_CHECK_EQUAL(seg_number_completion3, 3); + BOOST_CHECK_EQUAL(completion3_depth, 2562.5); + } } BOOST_AUTO_TEST_CASE( PLYADS ) {