diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index 50300fa53..4356abb9c 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -177,6 +177,7 @@ if(ENABLE_ECL_INPUT) src/opm/input/eclipse/Schedule/Group/GConSump.cpp src/opm/input/eclipse/Schedule/Group/GroupEconProductionLimits.cpp src/opm/input/eclipse/Schedule/Group/GTNode.cpp + src/opm/input/eclipse/Schedule/HandlerContext.cpp src/opm/input/eclipse/Schedule/KeywordHandlers.cpp src/opm/input/eclipse/Schedule/MessageLimits.cpp src/opm/input/eclipse/Schedule/MSW/icd.cpp @@ -184,6 +185,7 @@ if(ENABLE_ECL_INPUT) src/opm/input/eclipse/Schedule/MSW/Segment.cpp src/opm/input/eclipse/Schedule/MSW/SegmentMatcher.cpp src/opm/input/eclipse/Schedule/MSW/WellSegments.cpp + src/opm/input/eclipse/Schedule/MSW/WelSegsSet.cpp src/opm/input/eclipse/Schedule/MSW/AICD.cpp src/opm/input/eclipse/Schedule/MSW/SICD.cpp src/opm/input/eclipse/Schedule/MSW/Valve.cpp diff --git a/opm/input/eclipse/Schedule/Schedule.hpp b/opm/input/eclipse/Schedule/Schedule.hpp index 269b2d414..2e8908004 100644 --- a/opm/input/eclipse/Schedule/Schedule.hpp +++ b/opm/input/eclipse/Schedule/Schedule.hpp @@ -61,6 +61,7 @@ namespace Opm class GTNode; class GuideRateConfig; class GuideRateModel; + class HandlerContext; enum class InputErrorAction; class ParseContext; class Python; @@ -76,6 +77,7 @@ namespace Opm class WellMatcher; enum class WellProducerCMode; enum class WellStatus; + class WelSegsSet; class WellTestConfig; namespace RestartIO { struct RstState; } @@ -132,28 +134,6 @@ namespace Opm class Schedule { public: - - struct PairComp - { - bool operator()(const std::pair& pair, - const std::string& str) const - { - return std::get<0>(pair) < str; - } - bool operator()(const std::pair& pair1, - const std::pair& pair2) const - { - return std::get<0>(pair1) < std::get<0>(pair2); - } - bool operator()(const std::string& str, - const std::pair& pair) const - { - return str < std::get<0>(pair); - } - }; - - using WelSegsSet = std::set,PairComp>; - Schedule() = default; explicit Schedule(std::shared_ptr python_handle); Schedule(const Deck& deck, @@ -510,71 +490,6 @@ namespace Opm void dump_deck(std::ostream& os) const; private: - struct HandlerContext { - - const ScheduleBlock& block; - const DeckKeyword& keyword; - const std::size_t currentStep; - const std::vector& matching_wells; - const bool actionx_mode; - const ParseContext& parseContext; - ErrorGuard& errors; - SimulatorUpdate* sim_update{nullptr}; - const std::unordered_map* target_wellpi{nullptr}; - std::unordered_map* wpimult_global_factor{nullptr}; - WelSegsSet* welsegs_wells{nullptr}; - std::set* compsegs_wells{nullptr}; - const ScheduleGrid& grid; - - /// \param welsegs_wells All wells with a WELSEGS entry for checks. - /// \param compegs_wells All wells with a COMPSEGS entry for checks. - HandlerContext(const ScheduleBlock& block_, - const DeckKeyword& keyword_, - const ScheduleGrid& grid_, - const std::size_t currentStep_, - const std::vector& matching_wells_, - bool actionx_mode_, - const ParseContext& parseContext_, - ErrorGuard& errors_, - SimulatorUpdate* sim_update_, - const std::unordered_map* target_wellpi_, - std::unordered_map* wpimult_global_factor_, - WelSegsSet* welsegs_wells_, - std::set* compsegs_wells_) - : block(block_) - , keyword(keyword_) - , currentStep(currentStep_) - , matching_wells(matching_wells_) - , actionx_mode(actionx_mode_) - , parseContext(parseContext_) - , errors(errors_) - , sim_update(sim_update_) - , target_wellpi(target_wellpi_) - , wpimult_global_factor(wpimult_global_factor_) - , welsegs_wells(welsegs_wells_) - , compsegs_wells(compsegs_wells_) - , grid(grid_) - {} - - void affected_well(const std::string& well_name); - void record_well_structure_change(); - - /// \brief Mark that the well occured in a WELSEGS keyword - void welsegs_handled(const std::string& well_name) - { - if (welsegs_wells) - welsegs_wells->insert({well_name, keyword.location()}); - } - - /// \brief Mark that the well occured in a COMPSEGS keyword - void compsegs_handled(const std::string& well_name) - { - if (compsegs_wells) - compsegs_wells->insert(well_name); - } - - }; - // Please update the member functions // - operator==(const Schedule&) const // - serializationTestObject() @@ -641,7 +556,7 @@ namespace Opm bool actionx_mode, SimulatorUpdate* sim_update, const std::unordered_map* target_wellpi, - std::unordered_map* wpimult_global_factor = nullptr, + std::unordered_map& wpimult_global_factor, WelSegsSet* welsegs_wells = nullptr, std::set* compsegs_wells = nullptr); diff --git a/src/opm/input/eclipse/Schedule/HandlerContext.cpp b/src/opm/input/eclipse/Schedule/HandlerContext.cpp new file mode 100644 index 000000000..b07752be0 --- /dev/null +++ b/src/opm/input/eclipse/Schedule/HandlerContext.cpp @@ -0,0 +1,81 @@ +/* + Copyright 2013 Statoil ASA. + + 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 "HandlerContext.hpp" + +#include + +#include + +#include "MSW/WelSegsSet.hpp" + +#include + +namespace Opm { + +void HandlerContext::affected_well(const std::string& well_name) +{ + if (sim_update) { + sim_update->affected_wells.insert(well_name); + } +} + +void HandlerContext::record_tran_change() +{ + if (sim_update) { + sim_update->tran_update = true; + } +} + +void HandlerContext::record_well_structure_change() +{ + if (sim_update) { + sim_update->well_structure_changed = true; + } +} + +void HandlerContext::welsegs_handled(const std::string& well_name) +{ + if (welsegs_wells) { + welsegs_wells->insert(well_name, keyword.location()); + } +} + +void HandlerContext::compsegs_handled(const std::string& well_name) +{ + if (compsegs_wells) { + compsegs_wells->insert(well_name); + } +} + +double HandlerContext::getWellPI(const std::string& well_name) const +{ + if (!target_wellpi) { + throw std::logic_error("Lookup of well PI with no map available"); + } + + auto wellpi_iter = target_wellpi->find(well_name); + if (wellpi_iter == target_wellpi->end()) { + throw std::logic_error("Missing current PI for well " + well_name); + } + + return wellpi_iter->second; +} + +} diff --git a/src/opm/input/eclipse/Schedule/HandlerContext.hpp b/src/opm/input/eclipse/Schedule/HandlerContext.hpp new file mode 100644 index 000000000..344bf36af --- /dev/null +++ b/src/opm/input/eclipse/Schedule/HandlerContext.hpp @@ -0,0 +1,111 @@ +/* + Copyright 2013 Statoil ASA. + + 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 HANDLER_CONTEXT_HPP +#define HANDLER_CONTEXT_HPP + +#include + +#include +#include +#include +#include +#include + +namespace Opm { + +class DeckKeyword; +class ErrorGuard; +class ParseContext; +class ScheduleBlock; +class ScheduleGrid; +class ScheduleState; +struct SimulatorUpdate; +class WelSegsSet; + +class HandlerContext +{ +public: + /// \param welsegs_wells All wells with a WELSEGS entry for checks. + /// \param compegs_wells All wells with a COMPSEGS entry for checks. + HandlerContext(const ScheduleBlock& block_, + const DeckKeyword& keyword_, + const ScheduleGrid& grid_, + const std::size_t currentStep_, + const std::vector& matching_wells_, + bool actionx_mode_, + const ParseContext& parseContext_, + ErrorGuard& errors_, + SimulatorUpdate* sim_update_, + const std::unordered_map* target_wellpi_, + std::unordered_map& wpimult_global_factor_, + WelSegsSet* welsegs_wells_, + std::set* compsegs_wells_) + : block(block_) + , keyword(keyword_) + , currentStep(currentStep_) + , matching_wells(matching_wells_) + , actionx_mode(actionx_mode_) + , parseContext(parseContext_) + , errors(errors_) + , wpimult_global_factor(wpimult_global_factor_) + , grid(grid_) + , target_wellpi(target_wellpi_) + , welsegs_wells(welsegs_wells_) + , compsegs_wells(compsegs_wells_) + , sim_update(sim_update_) + {} + + //! \brief Mark that a well has changed. + void affected_well(const std::string& well_name); + + //! \brief Mark that transmissibilities must be recalculated. + void record_tran_change(); + + //! \brief Mark that well structure has changed. + void record_well_structure_change(); + + /// \brief Mark that the well occured in a WELSEGS keyword. + void welsegs_handled(const std::string& well_name); + + /// \brief Mark that the well occured in a COMPSEGS keyword. + void compsegs_handled(const std::string& well_name); + + //! \brief Obtain PI for a well. + double getWellPI(const std::string& well_name) const; + + const ScheduleBlock& block; + const DeckKeyword& keyword; + const std::size_t currentStep; + const std::vector& matching_wells; + const bool actionx_mode; + const ParseContext& parseContext; + ErrorGuard& errors; + std::unordered_map& wpimult_global_factor; + const ScheduleGrid& grid; + +private: + const std::unordered_map* target_wellpi{nullptr}; + WelSegsSet* welsegs_wells{nullptr}; + std::set* compsegs_wells{nullptr}; + SimulatorUpdate* sim_update{nullptr}; +}; + +} // end namespace Opm + +#endif // HANDLER_CONTEXT_HPP diff --git a/src/opm/input/eclipse/Schedule/KeywordHandlers.cpp b/src/opm/input/eclipse/Schedule/KeywordHandlers.cpp index 7bfaa6909..3390b6bed 100644 --- a/src/opm/input/eclipse/Schedule/KeywordHandlers.cpp +++ b/src/opm/input/eclipse/Schedule/KeywordHandlers.cpp @@ -96,6 +96,7 @@ #include #include +#include "HandlerContext.hpp" #include "Well/injection.hpp" #include @@ -150,7 +151,7 @@ namespace { throw OpmInputError("AQUFETP is not supported as SCHEDULE keyword", handlerContext.keyword.location()); } - void Schedule::handleAQUFLUX(Schedule::HandlerContext& handlerContext) { + void Schedule::handleAQUFLUX(HandlerContext& handlerContext) { // auto& aqufluxs = this->snapshots.back().aqufluxs; auto& aqufluxs = this->snapshots.back().aqufluxs; for (const auto& record : handlerContext.keyword) { @@ -159,7 +160,7 @@ namespace { } } - void Schedule::handleBCProp(Schedule::HandlerContext& handlerContext) { + void Schedule::handleBCProp(HandlerContext& handlerContext) { auto& bcprop = this->snapshots.back().bcprop; for (const auto& record : handlerContext.keyword) { bcprop.updateBCProp(record); @@ -989,8 +990,7 @@ File {} line {}.)", wname, location.keyword, location.filename, location.lineno) void Schedule::handleGEOKeyword(HandlerContext& handlerContext) { this->snapshots.back().geo_keywords().push_back(handlerContext.keyword); this->snapshots.back().events().addEvent( ScheduleEvents::GEO_MODIFIER ); - if (handlerContext.sim_update) - handlerContext.sim_update->tran_update = true; + handlerContext.record_tran_change(); } void Schedule::handleMXUNSUPP(HandlerContext& handlerContext) { @@ -1773,14 +1773,10 @@ File {} line {}.)", wname, location.keyword, location.filename, location.lineno) const auto targetPI = record.getItem().get(0); std::vector scalingApplicable; - const auto& current_wellpi = *handlerContext.target_wellpi; for (const auto& well_name : well_names) { - auto wellpi_iter = current_wellpi.find(well_name); - if (wellpi_iter == current_wellpi.end()) - throw std::logic_error(fmt::format("Missing current PI for well {}", well_name)); - auto new_well = this->getWell(well_name, report_step); - auto scalingFactor = new_well.convertDeckPI(targetPI) / wellpi_iter->second; + auto scalingFactor = new_well.convertDeckPI(targetPI) / + handlerContext.getWellPI(well_name); new_well.updateWellProductivityIndex(); new_well.applyWellProdIndexScaling(scalingFactor, scalingApplicable); this->snapshots.back().wells.update( std::move(new_well) ); @@ -2122,7 +2118,7 @@ Well{0} entered with 'FIELD' parent group: } } - void Schedule::handleWINJMULT(Opm::Schedule::HandlerContext& handlerContext) { + void Schedule::handleWINJMULT(HandlerContext& handlerContext) { for (const auto& record : handlerContext.keyword) { const std::string& wellNamePattern = record.getItem("WELL_NAME").getTrimmedString(0); const auto well_names = wellNames(wellNamePattern); @@ -2295,13 +2291,10 @@ Well{0} entered with 'FIELD' parent group: // whether it is the last one. const bool default_con_comp = defaultConCompRec(record); if (default_con_comp) { - auto wpimult_global_factor = handlerContext.wpimult_global_factor; - if (!wpimult_global_factor) { - throw std::runtime_error(" wpimult_global_factor is nullptr in function handleWPIMULT "); - } + auto& wpimult_global_factor = handlerContext.wpimult_global_factor; const auto scaling_factor = record.getItem("WELLPI").get(0); for (const auto& wname : well_names) { - (*wpimult_global_factor)[wname] = scaling_factor; + wpimult_global_factor.insert_or_assign(wname, scaling_factor); } continue; } diff --git a/src/opm/input/eclipse/Schedule/MSW/WelSegsSet.cpp b/src/opm/input/eclipse/Schedule/MSW/WelSegsSet.cpp new file mode 100644 index 000000000..490e91bf5 --- /dev/null +++ b/src/opm/input/eclipse/Schedule/MSW/WelSegsSet.cpp @@ -0,0 +1,79 @@ +/* + Copyright 2013 Statoil ASA. + + 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 "WelSegsSet.hpp" + +#include +#include + +#include + +namespace Opm { + +void WelSegsSet::insert(const std::string& well_name, + const KeywordLocation& location) +{ + entries_.emplace(well_name, location); +} + +std::vector +WelSegsSet::difference(const std::set& compsegs, + const std::vector& wells) const +{ + std::vector difference; + difference.reserve(entries_.size()); + std::set_difference(entries_.begin(), entries_.end(), + compsegs.begin(), compsegs.end(), + std::back_inserter(difference), + PairComp()); + + // Ignore wells without connections + const auto empty_conn = [&wells](const Entry &x) { + return std::any_of(wells.begin(), wells.end(), + [wname = x.first](const Well& well) + { return (well.name() == wname) && well.getConnections().empty(); }); + }; + + difference.erase(std::remove_if(difference.begin(), + difference.end(), empty_conn), + difference.end()); + + return difference; +} + +bool WelSegsSet::PairComp:: +operator()(const Entry& pair, const std::string& str) const +{ + return std::get<0>(pair) < str; +} + +bool WelSegsSet::PairComp:: +operator()(const Entry& pair1, const Entry& pair2) const +{ + return std::get<0>(pair1) < std::get<0>(pair2); +} + + +bool WelSegsSet::PairComp:: +operator()(const std::string& str, const Entry& pair) const +{ + return str < std::get<0>(pair); +} + +} // end namespace Opm diff --git a/src/opm/input/eclipse/Schedule/MSW/WelSegsSet.hpp b/src/opm/input/eclipse/Schedule/MSW/WelSegsSet.hpp new file mode 100644 index 000000000..0d3e99dbc --- /dev/null +++ b/src/opm/input/eclipse/Schedule/MSW/WelSegsSet.hpp @@ -0,0 +1,57 @@ +/* + Copyright 2013 Statoil ASA. + + 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 WEL_SEGS_SET_HPP +#define WEL_SEGS_SET_HPP + +#include + +#include +#include +#include +#include + +namespace Opm { + +class Well; + +class WelSegsSet +{ +public: + using Entry = std::pair; + + void insert(const std::string& well_name, + const KeywordLocation& location); + + std::vector difference(const std::set& compsegs, + const std::vector& wells) const; + +private: + struct PairComp + { + bool operator()(const Entry& pair, const std::string& str) const; + bool operator()(const Entry& pair1, const Entry& pair2) const; + bool operator()(const std::string& str, const Entry& pair) const; + }; + + std::set entries_; +}; + +} // end namespace Opm + +#endif // WEL_SEGS_SET_HPP diff --git a/src/opm/input/eclipse/Schedule/Schedule.cpp b/src/opm/input/eclipse/Schedule/Schedule.cpp index 19fdbea92..31ae7f202 100644 --- a/src/opm/input/eclipse/Schedule/Schedule.cpp +++ b/src/opm/input/eclipse/Schedule/Schedule.cpp @@ -91,8 +91,10 @@ #include #include -#include "Well/injection.hpp" +#include "HandlerContext.hpp" #include "MSW/Compsegs.hpp" +#include "MSW/WelSegsSet.hpp" +#include "Well/injection.hpp" #include #include @@ -370,7 +372,7 @@ Schedule::Schedule(const Deck& deck, const EclipseState& es, const std::optional bool actionx_mode, SimulatorUpdate* sim_update, const std::unordered_map* target_wellpi, - std::unordered_map* wpimult_global_factor, + std::unordered_map& wpimult_global_factor, WelSegsSet* welsegs_wells, std::set* compsegs_wells) { @@ -521,34 +523,22 @@ namespace /// \brief Check whether each MS well has COMPSEGS entry andissue error if not. /// \param welsegs All wells with a WELSEGS entry together with the location. /// \param compegs All wells with a COMPSEGS entry -void check_compsegs_consistency(::Opm::Schedule::WelSegsSet& welsegs, - std::set& compsegs, +void check_compsegs_consistency(Opm::WelSegsSet& welsegs, + const std::set& compsegs, const std::vector<::Opm::Well>& wells) { - std::vector> difference; - difference.reserve(welsegs.size()); - std::set_difference(welsegs.begin(), welsegs.end(), - compsegs.begin(), compsegs.end(), - std::back_inserter(difference), - ::Opm::Schedule::PairComp()); - // Ignore wells without connections - const auto empty_conn = [&wells](const std::pair &x) -> bool { - return std::any_of(wells.begin(), wells.end(), - [wname = x.first](const ::Opm::Well& well) { - return (well.name() == wname) && well.getConnections().empty(); } - ); - }; - difference.erase(std::remove_if(difference.begin(), difference.end(), empty_conn), difference.end()); + const auto difference = welsegs.difference(compsegs, wells); - if (difference.size()) { + if (!difference.empty()) { std::string well_str = "well"; - if (difference.size()>1) { + if (difference.size() > 1) { well_str.append("s"); } well_str.append(":"); for(const auto& [name, location] : difference) { - well_str.append(fmt::format("\n {} in {} at line {}", name, location.filename, location.lineno)); + well_str.append(fmt::format("\n {} in {} at line {}", + name, location.filename, location.lineno)); } auto msg = fmt::format("Missing COMPSEGS keyword for the following multisegment {}.", well_str); throw Opm::OpmInputError(msg, std::get<1>(difference[0])); @@ -700,7 +690,7 @@ void Schedule::iterateScheduleSection(std::size_t load_start, std::size_t load_e false, nullptr, target_wellpi, - &wpimult_global_factor, + wpimult_global_factor, &welsegs_wells, &compsegs_wells); keyword_index++; @@ -1532,7 +1522,7 @@ File {} line {}.)", pattern, location.keyword, location.filename, location.linen /*actionx_mode=*/false, &sim_update, &target_wellpi, - &wpimult_global_factor); + wpimult_global_factor); } this->applyGlobalWPIMULT(wpimult_global_factor); this->end_report(reportStep); @@ -1587,7 +1577,7 @@ File {} line {}.)", pattern, location.keyword, location.filename, location.linen true, &sim_update, &target_wellpi, - &wpimult_global_factor); + wpimult_global_factor); } this->applyGlobalWPIMULT(wpimult_global_factor); @@ -2469,17 +2459,4 @@ std::ostream& operator<<(std::ostream& os, const Schedule& sched) return os; } -void Schedule::HandlerContext::affected_well(const std::string& well_name) -{ - if (this->sim_update) - this->sim_update->affected_wells.insert(well_name); -} - -void Schedule::HandlerContext::record_well_structure_change() -{ - if (this->sim_update != nullptr) { - this->sim_update->well_structure_changed = true; - } -} - }