Skip to content

Commit fe9e060

Browse files
committed
Implement multi-segment well handling in COMPTRAJ
1 parent 30353cd commit fe9e060

18 files changed

+762
-35
lines changed

CMakeLists_files.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -735,6 +735,7 @@ if(ENABLE_ECL_OUTPUT)
735735
tests/SPE1CASE1B.DATA
736736
tests/SPE1CASE1_WELTRAJ.DATA
737737
tests/SPE1CASE1_WELTRAJ_2.DATA
738+
tests/SPE1CASE1_WELTRAJ_MSW.DATA
738739
tests/props_spe1case1b.inc
739740
tests/SPE9_CP_PACKED.DATA
740741
tests/SOFR_TEST.DATA

opm/input/eclipse/Schedule/HandlerContext.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,13 @@ void HandlerContext::compsegs_handled(const std::string& well_name)
8383
}
8484
}
8585

86+
void HandlerContext::comptraj_handled(const std::string& well_name)
87+
{
88+
if (comptraj_wells) {
89+
comptraj_wells->insert(well_name);
90+
}
91+
}
92+
8693
ScheduleState& HandlerContext::state()
8794
{
8895
return schedule_.snapshots[currentStep];

opm/input/eclipse/Schedule/HandlerContext.hpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ class HandlerContext
6868
/// \param wpimult_global_factor_ Global well production index multipliers
6969
/// \param welsegs_wells_ All wells with a WELSEGS entry for checks.
7070
/// \param compsegs_wells_ All wells with a COMPSEGS entry for checks.
71+
/// \param comptraj_wells_ All wells with a COMPTRAJ entry for checks.
7172
HandlerContext(Schedule& schedule,
7273
const ScheduleBlock& block_,
7374
const DeckKeyword& keyword_,
@@ -81,7 +82,8 @@ class HandlerContext
8182
const std::unordered_map<std::string, double>* target_wellpi_,
8283
std::unordered_map<std::string, double>& wpimult_global_factor_,
8384
WelSegsSet* welsegs_wells_,
84-
std::set<std::string>* compsegs_wells_)
85+
std::set<std::string>* compsegs_wells_,
86+
std::set<std::string>* comptraj_wells_)
8587
: block(block_)
8688
, keyword(keyword_)
8789
, currentStep(currentStep_)
@@ -94,6 +96,7 @@ class HandlerContext
9496
, target_wellpi(target_wellpi_)
9597
, welsegs_wells(welsegs_wells_)
9698
, compsegs_wells(compsegs_wells_)
99+
, comptraj_wells(comptraj_wells_)
97100
, sim_update(sim_update_)
98101
, schedule_(schedule)
99102
{}
@@ -122,6 +125,9 @@ class HandlerContext
122125
/// \brief Mark that the well occured in a COMPSEGS keyword.
123126
void compsegs_handled(const std::string& well_name);
124127

128+
/// \brief Mark that the well occured in a COMPTRAJ keyword.
129+
void comptraj_handled(const std::string& well_name);
130+
125131
//! \brief Set exit code.
126132
void setExitCode(int code);
127133

@@ -208,6 +214,7 @@ class HandlerContext
208214
const std::unordered_map<std::string, double>* target_wellpi{nullptr};
209215
WelSegsSet* welsegs_wells{nullptr};
210216
std::set<std::string>* compsegs_wells{nullptr};
217+
std::set<std::string>* comptraj_wells{nullptr};
211218
SimulatorUpdate* sim_update{nullptr};
212219
Schedule& schedule_;
213220
};

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

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -310,14 +310,11 @@ namespace {
310310

311311

312312
std::pair<WellConnections, WellSegments>
313-
processCOMPSEGS(const DeckKeyword& compsegs,
313+
processCOMPSEGSfromRecords(const std::vector<Record>& compsegs_vector,
314314
const WellConnections& input_connections,
315315
const WellSegments& input_segments,
316-
const ScheduleGrid& grid,
317-
const ParseContext& parseContext,
318-
ErrorGuard& errors)
316+
const ScheduleGrid& grid)
319317
{
320-
const auto& compsegs_vector = Compsegs::compsegsFromCOMPSEGSKeyword( compsegs, input_segments, grid, parseContext, errors);
321318
WellSegments new_segment_set = input_segments;
322319
WellConnections new_connection_set = input_connections;
323320

@@ -351,6 +348,43 @@ namespace {
351348
WellSegments( std::move(new_segment_set)));
352349
}
353350

351+
std::pair<WellConnections, WellSegments>
352+
processCOMPSEGS(const DeckKeyword& compsegs,
353+
const WellConnections& input_connections,
354+
const WellSegments& input_segments,
355+
const ScheduleGrid& grid,
356+
const ParseContext& parseContext,
357+
ErrorGuard& errors)
358+
{
359+
const auto& compsegs_vector = Compsegs::compsegsFromCOMPSEGSKeyword(compsegs, input_segments, grid, parseContext, errors);
360+
return processCOMPSEGSfromRecords(compsegs_vector, input_connections, input_segments, grid);
361+
}
362+
363+
std::pair<WellConnections, WellSegments>
364+
processCOMPSEGS(std::vector<std::tuple<double, double, std::array<int, 3>>>& segments_md_and_ijk,
365+
const WellSegments& segments,
366+
const WellConnections& input_connections,
367+
const WellSegments& input_segments,
368+
const ScheduleGrid& grid)
369+
{
370+
std::vector<Record> compsegs;
371+
372+
for (auto [startMD, endMD, ijk] : segments_md_and_ijk) {
373+
// Defaulted values:
374+
auto direction = Connection::Direction::X;
375+
const double center_depth = 0.0;
376+
int segment_number = 0;
377+
const int branch = 1;
378+
379+
std::size_t seqIndex = compsegs.size();
380+
compsegs.emplace_back(
381+
ijk[0], ijk[1], ijk[2], branch, startMD, endMD, direction, center_depth, segment_number, seqIndex);
382+
}
383+
384+
processCOMPSEGS__(compsegs, segments);
385+
return processCOMPSEGSfromRecords(compsegs, input_connections, input_segments, grid);
386+
}
387+
354388
namespace {
355389
// Duplicated from Well.cpp
356390
Connection::Order order_from_int(int int_value) {

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ namespace Compsegs {
5858
const ParseContext& parseContext,
5959
ErrorGuard& errors);
6060

61+
std::pair<WellConnections, WellSegments>
62+
processCOMPSEGS(std::vector<std::tuple<double, double, std::array<int, 3>>>& segments_md_and_ijk,
63+
const WellSegments& segments,
64+
const WellConnections& input_connections,
65+
const WellSegments& input_segments,
66+
const ScheduleGrid& grid);
67+
6168

6269
std::pair<WellConnections, WellSegments>
6370
rstUpdate(const RestartIO::RstWell& rst_well,

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,24 @@ WelSegsSet::difference(const std::set<std::string>& compsegs,
5858
return difference;
5959
}
6060

61+
std::vector<WelSegsSet::Entry>
62+
WelSegsSet::intersection(const std::set<std::string>& wells1,
63+
const std::set<std::string>& wells2) const
64+
{
65+
std::vector<Entry> intersection;
66+
intersection.reserve(entries_.size());
67+
68+
for (const auto& entry : entries_) {
69+
if (std::count(wells1.begin(), wells1.end(), std::get<0>(entry)) &&
70+
std::count(wells2.begin(), wells2.end(), std::get<0>(entry))) {
71+
intersection.push_back(entry);
72+
}
73+
}
74+
75+
return intersection;
76+
}
77+
78+
6179
bool WelSegsSet::PairComp::
6280
operator()(const Entry& pair, const std::string& str) const
6381
{

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ class WelSegsSet
4040

4141
std::vector<Entry> difference(const std::set<std::string>& compsegs,
4242
const std::vector<Well>& wells) const;
43+
std::vector<Entry> intersection(const std::set<std::string>& compsegs,
44+
const std::set<std::string>& comptraj) const;
4345

4446
private:
4547
struct PairComp

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

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,14 @@ namespace Opm {
6363
}
6464

6565

66+
WellSegments::WellSegments(const std::string &wname,
67+
std::vector<std::pair<double, double>>& lengths_and_depths,
68+
double diameter, const UnitSystem& unit_system)
69+
{
70+
this->addWellSegmentsFromLengthsAndDepths(wname, lengths_and_depths, diameter, unit_system);
71+
}
72+
73+
6674
WellSegments WellSegments::serializationTestObject()
6775
{
6876
WellSegments result;
@@ -185,6 +193,44 @@ namespace Opm {
185193
}
186194

187195

196+
void WellSegments::addWellSegmentsFromLengthsAndDepths(const std::string &wname,
197+
std::vector<std::pair<double, double>>& lengths_and_depths,
198+
double diameter, const UnitSystem& unit_system)
199+
{
200+
// Only LengthDepth::ABS is supported:
201+
assert (this->m_length_depth_type == WellSegments::LengthDepth::ABS);
202+
203+
const int branchID = 1; // Only main branch for now.
204+
205+
const double roughness = 0.0; // Defaulted: ROUGHNESS in WELSEGS.
206+
const double area = M_PI * diameter * diameter / 4.0;
207+
const double volume = Segment::invalidValue();
208+
209+
// Add segments:
210+
int segmentID = 2;
211+
for (auto [length, depth]: lengths_and_depths) {
212+
this->addSegment(
213+
segmentID, branchID, segmentID - 1, depth, length, diameter,
214+
roughness, area, volume, true, 0.0, 0.0
215+
);
216+
segmentID += 1;
217+
}
218+
219+
// Fix inlets:
220+
for (const auto& segment : this->m_segments) {
221+
const int outlet_segment = segment.outletSegment();
222+
if (outlet_segment <= 0) { // no outlet segment
223+
continue;
224+
}
225+
226+
const int outlet_segment_index = segment_number_to_index[outlet_segment];
227+
m_segments[outlet_segment_index].addInletSegment(segment.segmentNumber());
228+
}
229+
230+
this->process(wname, unit_system, WellSegments::LengthDepth::ABS, this->depthTopSegment(), this->lengthTopSegment());
231+
}
232+
233+
188234
void WellSegments::loadWELSEGS(const DeckKeyword& welsegsKeyword, const UnitSystem& unit_system)
189235
{
190236
// For the first record, which provides the information for the top
@@ -204,6 +250,8 @@ namespace Opm {
204250
const auto nodeX_top = record1.getItem("TOP_X").getSIDouble(0);
205251
const auto nodeY_top = record1.getItem("TOP_Y").getSIDouble(0);
206252

253+
this->m_length_depth_type = length_depth_type;
254+
207255
// The main branch is 1 instead of 0. The segment number for top
208256
// segment is also 1.
209257
if (length_depth_type == LengthDepth::INC) {

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@ namespace Opm {
7171
WellSegments(CompPressureDrop compDrop,
7272
const std::vector<Segment>& segments);
7373
void loadWELSEGS( const DeckKeyword& welsegsKeyword, const UnitSystem& unit_system);
74+
explicit WellSegments(const std::string &wname,
75+
std::vector<std::pair<double, double>>& lengths_and_depths,
76+
double diameter, const UnitSystem& unit_system);
77+
void addWellSegmentsFromLengthsAndDepths(const std::string &wname,
78+
std::vector<std::pair<double, double>>& lengths_and_depths,
79+
double diameter, const UnitSystem& unit_system);
7480

7581
static WellSegments serializationTestObject();
7682

@@ -120,6 +126,12 @@ namespace Opm {
120126
serializer(m_comp_pressure_drop);
121127
serializer(m_segments);
122128
serializer(segment_number_to_index);
129+
serializer(m_length_depth_type);
130+
}
131+
132+
LengthDepth getLengthDepthType() const
133+
{
134+
return this->m_length_depth_type;
123135
}
124136

125137
private:
@@ -151,6 +163,9 @@ namespace Opm {
151163
// the mapping from the segment number to the
152164
// storage index in the vector
153165
std::map<int, int> segment_number_to_index{};
166+
167+
// The length/depth type, incremental or absolute
168+
LengthDepth m_length_depth_type{LengthDepth::ABS};
154169
};
155170
}
156171

opm/input/eclipse/Schedule/Schedule.cpp

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -430,12 +430,13 @@ namespace Opm {
430430
const std::unordered_map<std::string, double>* target_wellpi,
431431
std::unordered_map<std::string, double>& wpimult_global_factor,
432432
WelSegsSet* welsegs_wells,
433-
std::set<std::string>* compsegs_wells)
433+
std::set<std::string>* compsegs_wells,
434+
std::set<std::string>* comptraj_wells)
434435
{
435436
HandlerContext handlerContext { *this, block, keyword, grid, currentStep,
436437
matches, action_mode,
437438
parseContext, errors, sim_update, target_wellpi,
438-
wpimult_global_factor, welsegs_wells, compsegs_wells};
439+
wpimult_global_factor, welsegs_wells, compsegs_wells, comptraj_wells};
439440

440441
if (!KeywordHandlers::getInstance().handleKeyword(handlerContext)) {
441442
OpmLog::warning(fmt::format("No handler registered for keyword {} "
@@ -580,30 +581,47 @@ class ScheduleLogger
580581

581582
namespace
582583
{
583-
/// \brief Check whether each MS well has COMPSEGS entry andissue error if not.
584-
/// \param welsegs All wells with a WELSEGS entry together with the location.
585-
/// \param compsegs All wells with a COMPSEGS entry
586-
void check_compsegs_consistency(const Opm::WelSegsSet& welsegs,
587-
const std::set<std::string>& compsegs,
588-
const std::vector<::Opm::Well>& wells)
589-
{
590-
const auto difference = welsegs.difference(compsegs, wells);
591584

592-
if (!difference.empty()) {
585+
void report_welsegs_error(const std::vector<Opm::WelSegsSet::Entry>& segments, std::string format_str)
586+
{
587+
if (!segments.empty()) {
593588
std::string well_str = "well";
594-
if (difference.size() > 1) {
589+
if (segments.size() > 1) {
595590
well_str.append("s");
596591
}
597592
well_str.append(":");
598593

599-
for(const auto& [name, location] : difference) {
594+
for(const auto& [name, location] : segments) {
600595
well_str.append(fmt::format("\n {} in {} at line {}",
601596
name, location.filename, location.lineno));
602597
}
603-
auto msg = fmt::format("Missing COMPSEGS keyword for the following multisegment {}.", well_str);
604-
throw Opm::OpmInputError(msg, std::get<1>(difference[0]));
598+
auto msg = fmt::format(format_str, well_str);
599+
throw Opm::OpmInputError(msg, std::get<1>(segments[0]));
605600
}
606601
}
602+
603+
/// \brief Check whether each MS well has COMPSEGS entry andissue error if not.
604+
/// \param welsegs All wells with a WELSEGS entry together with the location.
605+
/// \param compsegs All wells with a COMPSEGS entry
606+
/// \param comptraj All wells with a COMPTRAJ entry
607+
void check_compsegs_and_comptraj_consistency(const Opm::WelSegsSet& welsegs,
608+
const std::set<std::string>& compsegs,
609+
const std::set<std::string>& comptraj,
610+
const std::vector<::Opm::Well>& wells)
611+
{
612+
std::set<std::string> comp = compsegs;
613+
comp.insert(comptraj.begin(), comptraj.end());
614+
const auto difference = welsegs.difference(comp, wells);
615+
report_welsegs_error(
616+
difference,"Missing COMPSEGS keyword for the following multisegment {}."
617+
);
618+
619+
const auto intersection = welsegs.intersection(compsegs, comptraj);
620+
report_welsegs_error(
621+
intersection, "Overlapping COMPSEGS and COMPTRAJ keywords for the following multisegment {}."
622+
);
623+
}
624+
607625
}// end anonymous namespace
608626

609627
namespace Opm
@@ -666,6 +684,7 @@ void Schedule::iterateScheduleSection(std::size_t load_start, std::size_t load_e
666684
}
667685

668686
std::set<std::string> compsegs_wells;
687+
std::set<std::string> comptraj_wells;
669688
WelSegsSet welsegs_wells;
670689

671690
const auto matches = Action::Result { false }.matches();
@@ -759,12 +778,13 @@ void Schedule::iterateScheduleSection(std::size_t load_start, std::size_t load_e
759778
target_wellpi,
760779
wpimult_global_factor,
761780
&welsegs_wells,
762-
&compsegs_wells);
781+
&compsegs_wells,
782+
&comptraj_wells);
763783
keyword_index++;
764784
}
765785

766786
this->updateICDScalingFactors();
767-
check_compsegs_consistency(welsegs_wells, compsegs_wells, this->getWells(report_step));
787+
check_compsegs_and_comptraj_consistency(welsegs_wells, compsegs_wells, comptraj_wells, this->getWells(report_step));
768788
this->applyGlobalWPIMULT(wpimult_global_factor);
769789
this->end_report(report_step);
770790

0 commit comments

Comments
 (0)