From 91275cc0a5927acbdd717b2b3d0f5b2815c76b00 Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Tue, 17 Nov 2020 10:17:45 +0100 Subject: [PATCH] Automatically shut wells with no connections --- .../EclipseState/Schedule/Schedule.hpp | 3 +- .../EclipseState/Schedule/Well/Well.hpp | 4 +- .../Schedule/Well/WellConnections.hpp | 6 ++- .../EclipseState/Schedule/KeywordHandlers.cpp | 38 ++++++++++++++----- .../EclipseState/Schedule/Schedule.cpp | 15 +++++++- .../EclipseState/Schedule/Well/Well.cpp | 14 +++---- .../Schedule/Well/WellConnections.cpp | 15 ++++++-- tests/parser/ConnectionTests.cpp | 3 +- tests/parser/WellTests.cpp | 2 +- 9 files changed, 68 insertions(+), 32 deletions(-) diff --git a/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp b/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp index 50a7d2628..bc200cc15 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -400,7 +401,7 @@ namespace Opm void updateGroup(std::shared_ptr group, std::size_t reportStep); bool checkGroups(const ParseContext& parseContext, ErrorGuard& errors); void updateUDQActive( std::size_t timeStep, std::shared_ptr udq ); - bool updateWellStatus( const std::string& well, std::size_t reportStep , Well::Status status, bool update_connections); + bool updateWellStatus( const std::string& well, std::size_t reportStep , Well::Status status, bool update_connections, std::optional = {}); void addWellToGroup( const std::string& group_name, const std::string& well_name , std::size_t timeStep); void iterateScheduleSection(std::shared_ptr python, const std::string& input_path, const ParseContext& parseContext , ErrorGuard& errors, const SCHEDULESection& , const EclipseGrid& grid, const FieldPropsManager& fp); diff --git a/opm/parser/eclipse/EclipseState/Schedule/Well/Well.hpp b/opm/parser/eclipse/EclipseState/Schedule/Well/Well.hpp index a367231fb..31bd03be2 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/Well/Well.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/Well/Well.hpp @@ -541,7 +541,7 @@ public: bool updateRefDepth(const std::optional& ref_dpeth); bool updateDrainageRadius(double drainage_radius); void updateSegments(std::shared_ptr segments_arg); - bool updateConnections(std::shared_ptr connections); + bool updateConnections(std::shared_ptr connections, bool force = false); bool updateConnections(std::shared_ptr connections, const EclipseGrid& grid, const std::vector& pvtnum); bool updateStatus(Status status, bool update_connections); bool updateGroup(const std::string& group); @@ -567,8 +567,6 @@ public: bool handleCOMPLUMP(const DeckRecord& record); bool handleWPIMULT(const DeckRecord& record); - void forceUpdateConnections(std::shared_ptr connections_arg); - void filterConnections(const ActiveGridCells& grid); ProductionControls productionControls(const SummaryState& st) const; InjectionControls injectionControls(const SummaryState& st) const; diff --git a/opm/parser/eclipse/EclipseState/Schedule/Well/WellConnections.hpp b/opm/parser/eclipse/EclipseState/Schedule/Well/WellConnections.hpp index 2147dcf2b..3409d14b5 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/Well/WellConnections.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/Well/WellConnections.hpp @@ -35,6 +35,7 @@ namespace Opm { class DeckRecord; class EclipseGrid; class FieldPropsManager; + class KeywordLocation; class WellConnections { public: @@ -62,7 +63,7 @@ namespace Opm { const Connection::CTFKind ctf_kind = Connection::CTFKind::DeckValue, const std::size_t seqIndex = 0, const bool defaultSatTabId = true); - void loadCOMPDAT(const DeckRecord& record, const EclipseGrid& grid, const FieldPropsManager& field_properties); + void loadCOMPDAT(const DeckRecord& record, const EclipseGrid& grid, const FieldPropsManager& field_properties, const KeywordLocation& location); using const_iterator = std::vector< Connection >::const_iterator; @@ -154,7 +155,8 @@ namespace Opm { const std::vector* permx, const std::vector* permy, const std::vector* permz, - const std::vector& ntg); + const std::vector& ntg, + const KeywordLocation& location); size_t findClosestConnection(int oi, int oj, double oz, size_t start_pos); void orderTRACK(); diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/KeywordHandlers.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/KeywordHandlers.cpp index 35aa4fa86..9e6158e42 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/KeywordHandlers.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/KeywordHandlers.cpp @@ -153,11 +153,19 @@ namespace { for (const auto& name : wellnames) { auto well2 = std::shared_ptr(new Well( this->getWell(name, handlerContext.currentStep))); auto connections = std::shared_ptr( new WellConnections( well2->getConnections())); - connections->loadCOMPDAT(record, handlerContext.grid, handlerContext.fieldPropsManager); + connections->loadCOMPDAT(record, handlerContext.grid, handlerContext.fieldPropsManager, handlerContext.keyword.location()); if (well2->updateConnections(connections, handlerContext.grid, handlerContext.fieldPropsManager.get_int("PVTNUM"))) { this->updateWell(std::move(well2), handlerContext.currentStep); wells.insert( name ); } + + if (connections->empty() && well2->getConnections().empty()) { + const auto& location = handlerContext.keyword.location(); + auto msg = fmt::format("Problem with COMPDAT/{}\n" + "In {} line {}\n" + "Well {} is not connected to grid - will remain SHUT", name, location.filename, location.lineno, name); + OpmLog::warning(msg); + } this->addWellGroupEvent(name, ScheduleEvents::COMPLETION_CHANGE, handlerContext.currentStep); } } @@ -210,6 +218,16 @@ namespace { auto& dynamic_state = this->wells_static.at(well_name); auto well_ptr = std::make_shared( *dynamic_state[handlerContext.currentStep] ); + + if (well_ptr->getConnections().empty()) { + const auto& location = handlerContext.keyword.location(); + auto msg = fmt::format("Problem with COMPSEGS/{0}\n" + "In {1} line {2}\n" + "Well {0} is not connected to grid - COMPSEGS will be ignored", well_name, location.filename, location.lineno); + OpmLog::warning(msg); + return; + } + if (well_ptr->handleCOMPSEGS(handlerContext.keyword, handlerContext.grid, parseContext, errors)) this->updateWell(std::move(well_ptr), handlerContext.currentStep); } @@ -852,7 +870,7 @@ namespace { const Well::Status status = Well::StatusFromString(record.getItem("STATUS").getTrimmedString(0)); for (const auto& well_name : well_names) { - updateWellStatus( well_name , handlerContext.currentStep , status, false ); + this->updateWellStatus( well_name , handlerContext.currentStep , status, false, handlerContext.keyword.location() ); const auto table_nr = record.getItem("VFP_TABLE").get< int >(0); std::optional alq_type; @@ -901,7 +919,7 @@ namespace { "Well " + well2->name() + " is a history matched well with zero rate where crossflow is banned. " + "This well will be closed at " + std::to_string(m_timeMap.getTimePassedUntil(handlerContext.currentStep) / (60*60*24)) + " days"; OpmLog::note(msg); - updateWellStatus( well_name, handlerContext.currentStep, Well::Status::SHUT, false ); + this->updateWellStatus( well_name, handlerContext.currentStep, Well::Status::SHUT, false ); } } } @@ -918,7 +936,7 @@ namespace { const Well::Status status = Well::StatusFromString(record.getItem("STATUS").getTrimmedString(0)); for (const auto& well_name : well_names) { - updateWellStatus(well_name, handlerContext.currentStep, status, false); + this->updateWellStatus(well_name, handlerContext.currentStep, status, false, handlerContext.keyword.location()); const auto table_nr = record.getItem("VFP_TABLE").get< int >(0); std::optional alq_type; auto& dynamic_state = this->wells_static.at(well_name); @@ -969,7 +987,7 @@ namespace { const Well::Status status = Well::StatusFromString(record.getItem("STATUS").getTrimmedString(0)); for (const auto& well_name : well_names) { - updateWellStatus(well_name, handlerContext.currentStep, status, false); + this->updateWellStatus(well_name, handlerContext.currentStep, status, false, handlerContext.keyword.location()); bool update_well = false; auto& dynamic_state = this->wells_static.at(well_name); @@ -1002,14 +1020,14 @@ namespace { if (injection->surfaceInjectionRate.is()) { if (injection->hasInjectionControl(Well::InjectorCMode::RATE) && injection->surfaceInjectionRate.zero()) { OpmLog::note(msg); - updateWellStatus( well_name, handlerContext.currentStep, Well::Status::SHUT, false ); + this->updateWellStatus( well_name, handlerContext.currentStep, Well::Status::SHUT, false ); } } if (injection->reservoirInjectionRate.is()) { if (injection->hasInjectionControl(Well::InjectorCMode::RESV) && injection->reservoirInjectionRate.zero()) { OpmLog::note(msg); - updateWellStatus( well_name, handlerContext.currentStep, Well::Status::SHUT, false ); + this->updateWellStatus( well_name, handlerContext.currentStep, Well::Status::SHUT, false ); } } } @@ -1031,7 +1049,7 @@ namespace { const Well::Status status = Well::StatusFromString( record.getItem("STATUS").getTrimmedString(0)); for (const auto& well_name : well_names) { - updateWellStatus(well_name, handlerContext.currentStep, status, false); + this->updateWellStatus(well_name, handlerContext.currentStep, status, false, handlerContext.keyword.location()); bool update_well = false; auto& dynamic_state = this->wells_static.at(well_name); @@ -1059,7 +1077,7 @@ namespace { "Well " + well_name + " is an injector with zero rate where crossflow is banned. " + "This well will be closed at " + std::to_string ( m_timeMap.getTimePassedUntil(handlerContext.currentStep) / (60*60*24) ) + " days"; OpmLog::note(msg); - updateWellStatus( well_name, handlerContext.currentStep, Well::Status::SHUT, false ); + this->updateWellStatus( well_name, handlerContext.currentStep, Well::Status::SHUT, false ); } } } @@ -1140,7 +1158,7 @@ namespace { // Well::updateWellProductivityIndex() implicitly mutates // internal state in the WellConnections class. auto connections = std::make_shared(well2->getConnections()); - well2->forceUpdateConnections(std::move(connections)); + well2->updateConnections(std::move(connections), true); if (well2->updateWellProductivityIndex(rawProdIndex)) this->updateWell(std::move(well2), report_step); diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp index 4ba14e35b..41e2764d3 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp @@ -594,10 +594,21 @@ private: Function is quite dangerous - because if this is called while holding a Well pointer that will go stale and needs to be refreshed. */ - bool Schedule::updateWellStatus( const std::string& well_name, std::size_t reportStep , Well::Status status, bool update_connections) { - bool update = false; + bool Schedule::updateWellStatus( const std::string& well_name, std::size_t reportStep , Well::Status status, bool update_connections, std::optional location) { auto& dynamic_state = this->wells_static.at(well_name); auto well2 = std::make_shared(*dynamic_state[reportStep]); + if (well2->getConnections().empty() && status == Well::Status::OPEN) { + if (location) { + auto msg = fmt::format("Problem with{}\n", + "In {} line{}\n" + "Well {} has no connections to grid and will remain SHUT", location->keyword, location->filename, location->lineno, well_name); + OpmLog::warning(msg); + } else + OpmLog::warning(fmt::format("Well {} has no connections to grid and will remain SHUT", well_name)); + return false; + } + + bool update = false; if (well2->updateStatus(status, update_connections)) { m_events.addEvent( ScheduleEvents::WELL_STATUS_CHANGE, reportStep ); this->addWellGroupEvent( well2->name(), ScheduleEvents::WELL_STATUS_CHANGE, reportStep); diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/Well/Well.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/Well/Well.cpp index 411d3a6cf..e8bdb8be5 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/Well/Well.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/Well/Well.cpp @@ -688,13 +688,15 @@ bool Well::updateAutoShutin(bool auto_shutin) { } -bool Well::updateConnections(std::shared_ptr connections_arg) { +bool Well::updateConnections(std::shared_ptr connections_arg, bool force) { connections_arg->order( ); - if (*this->connections != *connections_arg) { + if (force || *this->connections != *connections_arg) { this->connections = connections_arg; + if (this->connections->empty()) + this->status = Status::SHUT; + return true; } - return false; } @@ -720,7 +722,7 @@ bool Well::updateSolventFraction(double solvent_fraction_arg) { bool Well::handleCOMPSEGS(const DeckKeyword& keyword, const EclipseGrid& grid, - const ParseContext& parseContext, ErrorGuard& errors) { + const ParseContext& parseContext, ErrorGuard& errors) { auto [new_connections, new_segments] = Compsegs::processCOMPSEGS(keyword, *this->connections, *this->segments , grid, parseContext, errors); @@ -1145,10 +1147,6 @@ bool Well::updateWSEGVALV(const std::vector >& valve_pairs return false; } -void Well::forceUpdateConnections(std::shared_ptr connections_arg) { - connections_arg->order(); - this->connections = std::move(connections_arg); -} void Well::filterConnections(const ActiveGridCells& grid) { this->connections->filter(grid); diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/Well/WellConnections.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/Well/WellConnections.cpp index 35554f8ec..f5bad5d99 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/Well/WellConnections.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/Well/WellConnections.cpp @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -270,14 +271,14 @@ inline std::array< size_t, 3> directionIndices(const Opm::Connection::Direction defaultSatTabId); } - void WellConnections::loadCOMPDAT(const DeckRecord& record, const EclipseGrid& grid, const FieldPropsManager& field_properties) { + void WellConnections::loadCOMPDAT(const DeckRecord& record, const EclipseGrid& grid, const FieldPropsManager& field_properties, const KeywordLocation& location) { const auto permx = field_properties.try_get("PERMX"); const auto permy = field_properties.try_get("PERMY"); const auto permz = field_properties.try_get("PERMZ"); const auto& ntg = field_properties.get_double("NTG"); const auto& satnum_data = field_properties.get_int("SATNUM"); - this->loadCOMPDAT(record, grid, satnum_data, permx, permy, permz, ntg); + this->loadCOMPDAT(record, grid, satnum_data, permx, permy, permz, ntg, location); } void WellConnections::loadCOMPDAT(const DeckRecord& record, @@ -286,7 +287,8 @@ inline std::array< size_t, 3> directionIndices(const Opm::Connection::Direction const std::vector* permx, const std::vector* permy, const std::vector* permz, - const std::vector& ntg) { + const std::vector& ntg, + const KeywordLocation& location) { const auto& itemI = record.getItem( "I" ); const auto defaulted_I = itemI.defaultApplied( 0 ) || itemI.get< int >( 0 ) == 0; @@ -326,8 +328,13 @@ inline std::array< size_t, 3> directionIndices(const Opm::Connection::Direction rw = 0.5*unit::feet; for (int k = K1; k <= K2; k++) { - if (!grid.cellActive(I, J, k)) + if (!grid.cellActive(I, J, k)) { + auto msg = fmt::format("Problem with COMPDAT keyword\n" + "In {} line {}\n" + "The cell ({},{},{}) is not active and the connection will be ignored", location.filename, location.lineno, I,J,k); + OpmLog::warning(msg); continue; + } size_t active_index = grid.activeIndex(I,J,k); double CF = -1; diff --git a/tests/parser/ConnectionTests.cpp b/tests/parser/ConnectionTests.cpp index 3df6d7b9d..00c21a5d2 100644 --- a/tests/parser/ConnectionTests.cpp +++ b/tests/parser/ConnectionTests.cpp @@ -42,6 +42,7 @@ #include #include +#include #include @@ -61,7 +62,7 @@ Opm::WellConnections loadCOMPDAT(const std::string& compdat_keyword) { const auto& keyword = deck.getKeyword("COMPDAT", 0); Opm::WellConnections connections(Opm::Connection::Order::TRACK, 10,10); for (const auto& rec : keyword) - connections.loadCOMPDAT(rec, grid, field_props); + connections.loadCOMPDAT(rec, grid, field_props, {}); return connections; } diff --git a/tests/parser/WellTests.cpp b/tests/parser/WellTests.cpp index 5b13955fd..046a3ad5d 100644 --- a/tests/parser/WellTests.cpp +++ b/tests/parser/WellTests.cpp @@ -1343,7 +1343,7 @@ END "P and Q must have the same internal connections pointers"); auto connQ = std::make_shared(wellP.getConnections()); - wellQ.forceUpdateConnections(std::move(connQ)); + wellQ.updateConnections(std::move(connQ), true); BOOST_CHECK_MESSAGE(! wellP.hasSameConnectionsPointers(wellQ), "P and Q must NOT have the same internal connections pointers " "after forcibly updating the connections structure");