Skip to content

Commit 3f91810

Browse files
committed
Add file for grid independent well keywords
1 parent 4073a74 commit 3f91810

12 files changed

+274
-150
lines changed

CMakeLists_files.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ if(ENABLE_ECL_INPUT)
318318
opm/input/eclipse/Schedule/Well/Connection.cpp
319319
opm/input/eclipse/Schedule/Well/FilterCake.cpp
320320
opm/input/eclipse/Schedule/Well/injection.cpp
321+
opm/input/eclipse/Schedule/Well/GridIndependentWellKeywordHandlers.cpp
321322
opm/input/eclipse/Schedule/Well/NameOrder.cpp
322323
opm/input/eclipse/Schedule/Well/PAvg.cpp
323324
opm/input/eclipse/Schedule/Well/PAvgCalculator.cpp

opm/input/eclipse/Schedule/KeywordHandlers.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
#include "ResCoup/ReservoirCouplingKeywordHandlers.hpp"
5555
#include "RXXKeywordHandlers.hpp"
5656
#include "UDQ/UDQKeywordHandlers.hpp"
57+
#include "Well/GridIndependentWellKeywordHandlers.hpp"
5758
#include "Well/WellCompletionKeywordHandlers.hpp"
5859
#include "Well/WellKeywordHandlers.hpp"
5960
#include "Well/WellPropertiesKeywordHandlers.hpp"
@@ -395,6 +396,7 @@ KeywordHandlers::KeywordHandlers()
395396
}
396397
{
397398
for (const auto& handlerFactory : {getGasLiftOptHandlers,
399+
getGridIndependentWellKeywordHandlers,
398400
getGroupHandlers,
399401
getGuideRateHandlers,
400402
getMixingRateControlHandlers,

opm/input/eclipse/Schedule/MSW/Compsegs.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ namespace {
362362
}
363363

364364
std::pair<WellConnections, WellSegments>
365-
getConnectionsAndSegmentsFromTrajectory(std::vector<std::tuple<double, double, std::array<int, 3>>>& segments_md_and_ijk,
365+
getConnectionsAndSegmentsFromTrajectory(const std::vector<std::tuple<double, double, std::array<int, 3>>>& segments_md_and_ijk,
366366
const WellSegments& segments,
367367
const WellConnections& input_connections,
368368
const WellSegments& input_segments,

opm/input/eclipse/Schedule/MSW/Compsegs.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ namespace Compsegs {
5959
ErrorGuard& errors);
6060

6161
std::pair<WellConnections, WellSegments>
62-
getConnectionsAndSegmentsFromTrajectory(std::vector<std::tuple<double, double, std::array<int, 3>>>& segments_md_and_ijk,
62+
getConnectionsAndSegmentsFromTrajectory(const std::vector<std::tuple<double, double, std::array<int, 3>>>& segments_md_and_ijk,
6363
const WellSegments& segments,
6464
const WellConnections& input_connections,
6565
const WellSegments& input_segments,

opm/input/eclipse/Schedule/MSW/WellSegments.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ namespace Opm {
186186

187187

188188
void WellSegments::addWellSegmentsFromLengthsAndDepths(const std::string &wname,
189-
std::vector<std::pair<double, double>>& lengths_and_depths,
189+
const std::vector<std::pair<double, double>>& lengths_and_depths,
190190
double diameter, const UnitSystem& unit_system)
191191
{
192192
// Only LengthDepth::ABS is supported:

opm/input/eclipse/Schedule/MSW/WellSegments.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ namespace Opm {
7272
const std::vector<Segment>& segments);
7373
void loadWELSEGS( const DeckKeyword& welsegsKeyword, const UnitSystem& unit_system);
7474
void addWellSegmentsFromLengthsAndDepths(const std::string &wname,
75-
std::vector<std::pair<double, double>>& lengths_and_depths,
75+
const std::vector<std::pair<double, double>>& lengths_and_depths,
7676
double diameter, const UnitSystem& unit_system);
7777

7878
static WellSegments serializationTestObject();
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
/*
2+
Copyright 2020 Statoil ASA.
3+
4+
This file is part of the Open Porous Media project (OPM).
5+
6+
OPM is free software: you can redistribute it and/or modify
7+
it under the terms of the GNU General Public License as published by
8+
the Free Software Foundation, either version 3 of the License, or
9+
(at your option) any later version.
10+
11+
OPM is distributed in the hope that it will be useful,
12+
but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
GNU General Public License for more details.
15+
16+
You should have received a copy of the GNU General Public License
17+
along with OPM. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
#include "GridIndependentWellKeywordHandlers.hpp"
21+
22+
#include <opm/common/OpmLog/OpmLog.hpp>
23+
#include <opm/common/utility/OpmInputError.hpp>
24+
25+
#include <opm/input/eclipse/Deck/DeckKeyword.hpp>
26+
#include <opm/input/eclipse/EclipseState/Grid/EclipseGrid.hpp>
27+
28+
#include <opm/input/eclipse/Schedule/ScheduleGrid.hpp>
29+
#include <opm/input/eclipse/Schedule/ScheduleState.hpp>
30+
#include <opm/input/eclipse/Schedule/Well/Well.hpp>
31+
#include <opm/input/eclipse/Schedule/Well/WellConnections.hpp>
32+
#include <opm/input/eclipse/Schedule/MSW/Compsegs.hpp>
33+
#include <opm/input/eclipse/Schedule/MSW/WellSegments.hpp>
34+
35+
#include <opm/input/eclipse/Parser/ParserKeywords/C.hpp>
36+
#include <opm/input/eclipse/Parser/ParserKeywords/W.hpp>
37+
38+
#include <external/resinsight/LibGeometry/cvfBoundingBoxTree.h>
39+
40+
41+
#include "../HandlerContext.hpp"
42+
43+
#include <fmt/format.h>
44+
45+
#include <unordered_set>
46+
47+
namespace Opm {
48+
49+
namespace {
50+
51+
52+
auto get_segment_geometries(HandlerContext& handlerContext,
53+
const std::vector<external::WellPathCellIntersectionInfo>& intersections,
54+
const external::cvf::ref<external::RigWellPath>& wellPathGeometry)
55+
{
56+
std::vector<std::tuple<double, double, std::array<int, 3>>> intersections_md_and_ijk;
57+
std::vector<std::pair<double, double>> cell_md_and_tvd;
58+
intersections_md_and_ijk.reserve(intersections.size());
59+
cell_md_and_tvd.reserve(intersections.size());
60+
const auto& ecl_grid = handlerContext.grid.get_grid();
61+
for (const auto& intersection: intersections) {
62+
const auto ijk = ecl_grid->getIJK(intersection.globCellIndex);
63+
intersections_md_and_ijk.emplace_back(intersection.startMD, intersection.endMD, ijk);
64+
double cell_md = 0.5 * (intersection.startMD + intersection.endMD);
65+
double cell_tvd = wellPathGeometry->interpolatedPointAlongWellPath(cell_md)[2];
66+
cell_md_and_tvd.emplace_back(cell_md, cell_tvd);
67+
}
68+
return std::pair{intersections_md_and_ijk, cell_md_and_tvd};
69+
}
70+
71+
72+
void add_segments(HandlerContext& handlerContext,
73+
const std::string& name,
74+
const std::vector<std::pair<double, double>>& cell_md_and_tvd,
75+
double diameter)
76+
{
77+
auto well = handlerContext.state().wells.get(name);
78+
well.addWellSegmentsFromLengthsAndDepths(cell_md_and_tvd, diameter, handlerContext.keyword.location());
79+
handlerContext.state().wells.update(std::move(well));
80+
handlerContext.record_well_structure_change();
81+
}
82+
83+
84+
void process_connections(HandlerContext& handlerContext,
85+
const std::string& name,
86+
const std::vector<std::tuple<double, double, std::array<int, 3>>> intersections_md_and_ijk)
87+
{
88+
auto well = handlerContext.state().wells.get(name);
89+
auto [new_connections, new_segments] = Compsegs::getConnectionsAndSegmentsFromTrajectory(
90+
intersections_md_and_ijk, well.getSegments(), well.getConnections(), well.getSegments(), handlerContext.grid
91+
);
92+
well.updateConnections(std::make_shared<WellConnections>(std::move(new_connections)), false);
93+
well.updateSegments(std::make_shared<WellSegments>(std::move(new_segments)));
94+
handlerContext.state().wells.update(std::move(well));
95+
handlerContext.record_well_structure_change();
96+
}
97+
98+
99+
void process_segments(HandlerContext& handlerContext, const std::string& name,
100+
const std::vector<external::WellPathCellIntersectionInfo>& intersections,
101+
const external::cvf::ref<external::RigWellPath>& wellPathGeometry,
102+
double diameter)
103+
{
104+
const auto& well = handlerContext.state().wells.get(name);
105+
if (well.isMultiSegment()) {
106+
// COMPTRAJ is in absolute units, INC in WELSEGS is not supported:
107+
if (well.getSegments().getLengthDepthType() == WellSegments::LengthDepth::INC) {
108+
const auto msg = fmt::format(" WELSEGS/{} defines segments as incremental (INC): only ABS allowed", name);
109+
throw Opm::OpmInputError(msg, handlerContext.keyword.location());
110+
}
111+
// For now, no segments may be defined via WELSEGS, except for the top:
112+
if (well.getSegments().size() > 1) {
113+
const auto msg = fmt::format(" {} already defines segments with the WELSEGS keyword", name);
114+
throw Opm::OpmInputError(msg, handlerContext.keyword.location());
115+
}
116+
117+
auto [intersections_md_and_ijk, cell_md_and_tvd] = get_segment_geometries(handlerContext, intersections, wellPathGeometry);
118+
add_segments(handlerContext, name, cell_md_and_tvd, diameter);
119+
process_connections(handlerContext, name, intersections_md_and_ijk);
120+
}
121+
122+
}
123+
124+
125+
void handleCOMPTRAJ(HandlerContext& handlerContext)
126+
{
127+
// Keyword WELTRAJ must be read first
128+
std::unordered_set<std::string> wells;
129+
external::cvf::ref<external::cvf::BoundingBoxTree> cellSearchTree{};
130+
131+
for (const auto& record : handlerContext.keyword) {
132+
const auto wellNamePattern = record.getItem("WELL").getTrimmedString(0);
133+
const auto wellnames = handlerContext.wellNames(wellNamePattern, false);
134+
135+
for (const auto& name : wellnames) {
136+
auto well2 = handlerContext.state().wells.get(name);
137+
auto connections = std::make_shared<WellConnections>(well2.getConnections());
138+
external::cvf::ref<external::RigWellPath> wellPathGeometry { new external::RigWellPath };
139+
140+
// cellsearchTree is calculated only once and is used to
141+
// calculated cell intersections of the perforations
142+
// specified in COMPTRAJ
143+
auto intersections = connections->loadCOMPTRAJ(
144+
record, handlerContext.grid, name, handlerContext.keyword.location(), cellSearchTree, wellPathGeometry
145+
);
146+
147+
// In the case that defaults are used in WELSPECS for
148+
// headI/J the headI/J are calculated based on the well
149+
// trajectory data
150+
well2.updateHead(connections->getHeadI(), connections->getHeadJ());
151+
if (well2.updateConnections(connections, handlerContext.grid)) {
152+
handlerContext.state().wells.update( well2 );
153+
wells.insert( name );
154+
}
155+
156+
if (connections->empty() && well2.getConnections().empty()) {
157+
const auto& location = handlerContext.keyword.location();
158+
const auto msg = fmt::format(R"(Problem with COMPTRAJ/{}
159+
In {} line {}
160+
Well {} is not connected to grid - will remain SHUT)",
161+
name, location.filename,
162+
location.lineno, name);
163+
OpmLog::warning(msg);
164+
}
165+
166+
double diameter = record.getItem("DIAMETER").getSIDouble(0));
167+
process_segments(handlerContext, name, intersections, wellPathGeometry, diameter);
168+
169+
handlerContext.state().wellgroup_events().addEvent(name, ScheduleEvents::COMPLETION_CHANGE);
170+
}
171+
}
172+
173+
handlerContext.state().events().addEvent(ScheduleEvents::COMPLETION_CHANGE);
174+
175+
// In the case the wells reference depth has been defaulted in the
176+
// WELSPECS keyword we need to force a calculation of the wells
177+
// reference depth exactly when the COMPTRAJ keyword has been
178+
// completely processed.
179+
for (const auto& wname : wells) {
180+
auto well = handlerContext.state().wells.get(wname);
181+
well.updateRefDepth();
182+
183+
handlerContext.state().wells.update(std::move(well));
184+
handlerContext.comptraj_handled(wname);
185+
}
186+
187+
if (! wells.empty()) {
188+
handlerContext.record_well_structure_change();
189+
}
190+
}
191+
192+
193+
void handleWELTRAJ(HandlerContext& handlerContext)
194+
{
195+
for (const auto& record : handlerContext.keyword) {
196+
const std::string& wellNamePattern = record.getItem("WELL").getTrimmedString(0);
197+
const auto wellnames = handlerContext.wellNames(wellNamePattern, false);
198+
199+
for (const auto& name : wellnames) {
200+
auto well2 = handlerContext.state().wells.get(name);
201+
auto connections = std::make_shared<WellConnections>(WellConnections(well2.getConnections()));
202+
connections->loadWELTRAJ(record, handlerContext.grid, name, handlerContext.keyword.location());
203+
if (well2.updateConnections(connections, handlerContext.grid)) {
204+
handlerContext.state().wells.update( well2 );
205+
handlerContext.record_well_structure_change();
206+
}
207+
handlerContext.state().wellgroup_events().addEvent( name, ScheduleEvents::COMPLETION_CHANGE);
208+
const auto& md = connections->getMD();
209+
if (!std::is_sorted(std::begin(md), std::end(md))) {
210+
auto msg = fmt::format("Well {} measured depth column is not strictly increasing", name);
211+
throw OpmInputError(msg, handlerContext.keyword.location());
212+
}
213+
}
214+
}
215+
handlerContext.state().events().addEvent(ScheduleEvents::COMPLETION_CHANGE);
216+
}
217+
218+
219+
} // Anonymous namespace
220+
221+
std::vector<std::pair<std::string,KeywordHandlers::handler_function>>
222+
getGridIndependentWellKeywordHandlers()
223+
{
224+
return {
225+
{ "COMPTRAJ", &handleCOMPTRAJ },
226+
{ "WELTRAJ" , &handleWELTRAJ },
227+
};
228+
}
229+
230+
} // namespace Opm
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
Copyright 2020 Statoil ASA.
3+
4+
This file is part of the Open Porous Media project (OPM).
5+
6+
OPM is free software: you can redistribute it and/or modify
7+
it under the terms of the GNU General Public License as published by
8+
the Free Software Foundation, either version 3 of the License, or
9+
(at your option) any later version.
10+
11+
OPM is distributed in the hope that it will be useful,
12+
but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
GNU General Public License for more details.
15+
16+
You should have received a copy of the GNU General Public License
17+
along with OPM. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
#ifndef GRID_INDEPENDENT_WELL_KEYWORD_HANDLERS_HPP
20+
#define GRID_INDEPENDENT_WELL_KEYWORD_HANDLERS_HPP
21+
22+
#include "../KeywordHandlers.hpp"
23+
24+
#include <string>
25+
#include <utility>
26+
#include <vector>
27+
28+
namespace Opm {
29+
30+
//! \brief Obtain a list of well completion keyword handlers.
31+
std::vector<std::pair<std::string,KeywordHandlers::handler_function>> getGridIndependentWellKeywordHandlers();
32+
33+
}
34+
35+
#endif

opm/input/eclipse/Schedule/Well/Well.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1837,7 +1837,7 @@ bool Well::handleWELSEGS(const DeckKeyword& keyword)
18371837
return true;
18381838
}
18391839

1840-
void Well::addWellSegmentsFromLengthsAndDepths(std::vector<std::pair<double, double>>& lengths_and_depths, double diameter, const KeywordLocation& location)
1840+
void Well::addWellSegmentsFromLengthsAndDepths(const std::vector<std::pair<double, double>>& lengths_and_depths, double diameter, const KeywordLocation& location)
18411841
{
18421842
if (this->segments == nullptr) {
18431843
throw OpmInputError{

opm/input/eclipse/Schedule/Well/Well.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -572,7 +572,7 @@ class Well {
572572
void setFilterConc(const UDAValue& conc);
573573
double evalFilterConc(const SummaryState& summary_sate) const;
574574
bool applyGlobalWPIMULT(double scale_factor);
575-
void addWellSegmentsFromLengthsAndDepths(std::vector<std::pair<double, double>>& lengths_and_depths, double diameter, const KeywordLocation& location);
575+
void addWellSegmentsFromLengthsAndDepths(const std::vector<std::pair<double, double>>& lengths_and_depths, double diameter, const KeywordLocation& location);
576576

577577
void filterConnections(const ActiveGridCells& grid);
578578
ProductionControls productionControls(const SummaryState& st) const;

0 commit comments

Comments
 (0)