From 279871df0573bf7f510b9dd7f8a9c0f2624702ca Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Fri, 30 Oct 2020 13:36:08 +0100 Subject: [PATCH 1/3] Add missing #include --- opm/parser/eclipse/EclipseState/Schedule/Well/WListManager.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/opm/parser/eclipse/EclipseState/Schedule/Well/WListManager.hpp b/opm/parser/eclipse/EclipseState/Schedule/Well/WListManager.hpp index aa759f340..7cb189129 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/Well/WListManager.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/Well/WListManager.hpp @@ -23,11 +23,10 @@ #include #include #include +#include namespace Opm { -class WList; - class WListManager { public: WListManager() = default; From 7ef7e3017e5416a2dc0a9fdced9384abbf26d838 Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Fri, 30 Oct 2020 13:36:41 +0100 Subject: [PATCH 2/3] Add class WellMatcher and use it UDQ evaluation --- CMakeLists_files.cmake | 2 + msim/src/msim.cpp | 2 +- .../EclipseState/Schedule/Schedule.hpp | 2 + .../EclipseState/Schedule/UDQ/UDQConfig.hpp | 3 +- .../EclipseState/Schedule/UDQ/UDQContext.hpp | 5 +- .../Schedule/Well/WellMatcher.hpp | 43 ++++++ .../EclipseState/Schedule/Schedule.cpp | 12 ++ .../EclipseState/Schedule/UDQ/UDQASTNode.cpp | 44 +++--- .../EclipseState/Schedule/UDQ/UDQConfig.cpp | 4 +- .../EclipseState/Schedule/UDQ/UDQContext.cpp | 9 +- .../Schedule/Well/WellMatcher.cpp | 66 ++++++++ tests/parser/ScheduleTests.cpp | 31 ++++ tests/parser/UDQTests.cpp | 144 +++++++++++------- tests/test_Restart.cpp | 2 +- 14 files changed, 289 insertions(+), 80 deletions(-) create mode 100644 opm/parser/eclipse/EclipseState/Schedule/Well/WellMatcher.hpp create mode 100644 src/opm/parser/eclipse/EclipseState/Schedule/Well/WellMatcher.cpp diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index 18f59a365..9821b0730 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -134,6 +134,7 @@ if(ENABLE_ECL_INPUT) src/opm/parser/eclipse/EclipseState/Schedule/Well/injection.cpp src/opm/parser/eclipse/EclipseState/Schedule/Well/Well.cpp src/opm/parser/eclipse/EclipseState/Schedule/Well/WellConnections.cpp + src/opm/parser/eclipse/EclipseState/Schedule/Well/WellMatcher.cpp src/opm/parser/eclipse/EclipseState/Schedule/Well/WList.cpp src/opm/parser/eclipse/EclipseState/Schedule/Well/WListManager.cpp src/opm/parser/eclipse/EclipseState/Schedule/Well/WellEconProductionLimits.cpp @@ -704,6 +705,7 @@ if(ENABLE_ECL_INPUT) opm/parser/eclipse/EclipseState/Schedule/Well/InjectionControls.hpp opm/parser/eclipse/EclipseState/Schedule/Well/Well.hpp opm/parser/eclipse/EclipseState/Schedule/Well/WList.hpp + opm/parser/eclipse/EclipseState/Schedule/Well/WellMatcher.hpp opm/parser/eclipse/EclipseState/Schedule/Well/WListManager.hpp opm/parser/eclipse/EclipseState/Schedule/Well/WellEconProductionLimits.hpp opm/parser/eclipse/EclipseState/Schedule/Well/WellFoamProperties.hpp diff --git a/msim/src/msim.cpp b/msim/src/msim.cpp index e8752f040..325340b92 100644 --- a/msim/src/msim.cpp +++ b/msim/src/msim.cpp @@ -121,7 +121,7 @@ void msim::run_step(const Schedule& schedule, Action::State& action_state, Summa group_nwrk_data, {}); - schedule.getUDQConfig( report_step ).eval(report_step, st, udq_state); + schedule.getUDQConfig( report_step ).eval(report_step, schedule.wellMatcher(report_step), st, udq_state); this->output(action_state, st, diff --git a/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp b/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp index 4b8cf2ed0..0325bbe06 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -193,6 +194,7 @@ namespace Opm bool hasWell(const std::string& wellName) const; bool hasWell(const std::string& wellName, std::size_t timeStep) const; + WellMatcher wellMatcher(std::size_t report_step) const; std::vector wellNames(const std::string& pattern, std::size_t timeStep, const std::vector& matching_wells = {}) const; std::vector wellNames(const std::string& pattern) const; std::vector wellNames(std::size_t timeStep) const; diff --git a/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQConfig.hpp b/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQConfig.hpp index c6e5cc3e2..6c00b8692 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQConfig.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQConfig.hpp @@ -43,6 +43,7 @@ namespace Opm { class SummaryState; class UDQState; class KeywordLocation; + class WellMatcher; class UDQConfig { public: @@ -61,7 +62,7 @@ namespace Opm { void add_assign(const std::string& quantity, const std::vector& selector, double value, std::size_t report_step); void add_define(const std::string& quantity, const KeywordLocation& location, const std::vector& expression); - void eval(std::size_t report_step, SummaryState& st, UDQState& udq_state) const; + void eval(std::size_t report_step, const WellMatcher& wm, SummaryState& st, UDQState& udq_state) const; const UDQDefine& define(const std::string& key) const; std::vector definitions() const; std::vector definitions(UDQVarType var_type) const; diff --git a/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQContext.hpp b/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQContext.hpp index 6ff94712d..961a4aac7 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQContext.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQContext.hpp @@ -29,6 +29,7 @@ #include #include +#include namespace Opm { class SummaryState; @@ -37,7 +38,7 @@ namespace Opm { class UDQContext{ public: - UDQContext(const UDQFunctionTable& udqft, SummaryState& summary_state, UDQState& udq_state); + UDQContext(const UDQFunctionTable& udqft, const WellMatcher& wm, SummaryState& summary_state, UDQState& udq_state); std::optional get(const std::string& key) const; std::optional get_well_var(const std::string& well, const std::string& var) const; std::optional get_group_var(const std::string& group, const std::string& var) const; @@ -46,9 +47,11 @@ namespace Opm { void update_define(const std::string& keyword, const UDQSet& udq_result); const UDQFunctionTable& function_table() const; std::vector wells() const; + std::vector wells(const std::string& pattern) const; std::vector groups() const; private: const UDQFunctionTable& udqft; + WellMatcher well_matcher; SummaryState& summary_state; UDQState& udq_state; //std::unordered_map udq_results; diff --git a/opm/parser/eclipse/EclipseState/Schedule/Well/WellMatcher.hpp b/opm/parser/eclipse/EclipseState/Schedule/Well/WellMatcher.hpp new file mode 100644 index 000000000..d6360cf39 --- /dev/null +++ b/opm/parser/eclipse/EclipseState/Schedule/Well/WellMatcher.hpp @@ -0,0 +1,43 @@ +/* + Copyright 2019 Equinor 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 WELL_MATCHER_HPP +#define WELL_MATCHER_HPP + +#include +#include + +#include + +namespace Opm { + +class WellMatcher { +public: + WellMatcher() = default; + explicit WellMatcher(const std::vector& wells); + WellMatcher(const std::vector& wells, const WListManager& wlm); + const std::vector& wells() const; + std::vector wells(const std::string& pattern) const; + +private: + std::vector m_wells; + WListManager m_wlm; +}; + +} +#endif diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp index de67c69bc..b45a1bae4 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp @@ -1086,6 +1086,18 @@ private: return {}; } + + WellMatcher Schedule::wellMatcher(std::size_t report_step) const { + std::vector wnames; + for (const auto& well_pair : this->wells_static) { + const auto& dynamic_state = well_pair.second; + if (dynamic_state.get(report_step)) + wnames.push_back(well_pair.first); + } + return WellMatcher(wnames, this->getWListManager(report_step)); + } + + std::vector Schedule::wellNames(const std::string& pattern) const { return this->wellNames(pattern, this->size() - 1); } diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQASTNode.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQASTNode.cpp index 971477339..f0fd69895 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQASTNode.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQASTNode.cpp @@ -145,28 +145,34 @@ UDQSet UDQASTNode::eval(UDQVarType target_type, const UDQContext& context) const const auto& string_value = std::get( this->value ); auto data_type = UDQ::targetType(string_value); if (data_type == UDQVarType::WELL_VAR) { - const auto& wells = context.wells(); + const auto& all_wells = context.wells(); + auto res = UDQSet::wells(string_value, all_wells); - if (this->selector.size() > 0) { - const std::string& well_pattern = this->selector[0]; - if (well_pattern.find("*") == std::string::npos) - return this->sign * UDQSet::scalar(string_value, context.get_well_var(well_pattern, string_value)); - else { - auto res = UDQSet::wells(string_value, wells); - int fnmatch_flags = 0; - for (const auto& well : wells) { - if (fnmatch(well_pattern.c_str(), well.c_str(), fnmatch_flags) == 0) - res.assign(well, context.get_well_var(well, string_value)); - } - return this->sign * res; - } - } else { - auto res = UDQSet::wells(string_value, wells); - for (const auto& well : wells) + if (this->selector.empty()) { + for (const auto& well : all_wells) res.assign(well, context.get_well_var(well, string_value)); - - return this->sign * res; + } else { + const std::string& well_pattern = this->selector[0]; + if (well_pattern.find('*') == std::string::npos) + /* + The right hand side is a fully qualified well name without + any '*', in this case the right hand side evaluates to a + *scalar* - and that scalar value is distributed among all + the wells in the result set. + */ + res.assign( context.get_well_var(well_pattern, string_value)); + else { + /* + The right hand side is a set of wells. The result set will + be updated for all wells in the right hand set, wells + missing in the right hand set will be undefined in the + result set. + */ + for (const auto& wname : context.wells(well_pattern)) + res.assign(wname, context.get_well_var(wname, string_value)); + } } + return this->sign * res; } if (data_type == UDQVarType::GROUP_VAR) { diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQConfig.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQConfig.cpp index 60fede6da..37b41795f 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQConfig.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQConfig.cpp @@ -302,9 +302,9 @@ namespace Opm { this->type_count == data.type_count; } - void UDQConfig::eval(std::size_t report_step, SummaryState& st, UDQState& udq_state) const { + void UDQConfig::eval(std::size_t report_step, const WellMatcher& wm, SummaryState& st, UDQState& udq_state) const { const auto& func_table = this->function_table(); - UDQContext context(func_table, st, udq_state); + UDQContext context(func_table, wm, st, udq_state); for (const auto& assign : this->assignments(UDQVarType::WELL_VAR)) { if (udq_state.assign(report_step, assign.keyword())) { diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQContext.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQContext.cpp index 6dd27248e..fa8b13f1f 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQContext.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQContext.cpp @@ -41,8 +41,9 @@ bool is_udq(const std::string& key) { } - UDQContext::UDQContext(const UDQFunctionTable& udqft_arg, SummaryState& summary_state_arg, UDQState& udq_state_arg) : + UDQContext::UDQContext(const UDQFunctionTable& udqft_arg, const WellMatcher& wm, SummaryState& summary_state_arg, UDQState& udq_state_arg) : udqft(udqft_arg), + well_matcher(wm), summary_state(summary_state_arg), udq_state(udq_state_arg) { @@ -117,7 +118,11 @@ bool is_udq(const std::string& key) { } std::vector UDQContext::wells() const { - return this->summary_state.wells(); + return this->well_matcher.wells(); + } + + std::vector UDQContext::wells(const std::string& pattern) const { + return this->well_matcher.wells(pattern); } std::vector UDQContext::groups() const { diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/Well/WellMatcher.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/Well/WellMatcher.cpp new file mode 100644 index 000000000..f052c8781 --- /dev/null +++ b/src/opm/parser/eclipse/EclipseState/Schedule/Well/WellMatcher.cpp @@ -0,0 +1,66 @@ +/* + Copyright 2019 Equinor 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 + +#include +#include + +namespace Opm { + +WellMatcher::WellMatcher(const std::vector& wells) : + m_wells(wells) +{} + +WellMatcher::WellMatcher(const std::vector& wells, const WListManager &wlm) : + m_wells(wells), + m_wlm(wlm) +{} + +const std::vector& WellMatcher::wells() const { + return this->m_wells; +} + +std::vector WellMatcher::wells(const std::string& pattern) const { + if (pattern.size() == 0) + return {}; + + // WLIST + if (pattern[0] == '*' && pattern.size() > 1) + return this->m_wlm.wells(pattern); + + // Normal pattern matching + auto star_pos = pattern.find('*'); + if (star_pos != std::string::npos) { + std::vector names; + for (const auto& wname : this->m_wells) { + int flags = 0; + if (fnmatch(pattern.c_str(), wname.c_str(), flags) == 0) + names.push_back(wname); + } + return names; + } + + auto name_iter = std::find(this->m_wells.begin(), this->m_wells.end(), pattern); + if (name_iter != this->m_wells.end()) + return { pattern }; + + return {}; +} + +} diff --git a/tests/parser/ScheduleTests.cpp b/tests/parser/ScheduleTests.cpp index 20f41f9a7..e483832f3 100644 --- a/tests/parser/ScheduleTests.cpp +++ b/tests/parser/ScheduleTests.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -3305,6 +3306,36 @@ BOOST_AUTO_TEST_CASE(WellNames) { auto abs_all = schedule.wellNames(); BOOST_CHECK_EQUAL(abs_all.size(), 9U); + + + WellMatcher wm0( {}, WListManager{}); + const auto& wml0 = wm0.wells(); + BOOST_CHECK(wml0.empty()); + + WellMatcher wm1( {"W1", "W2", "W3", "P1", "P2", "P3"}, WListManager{}); + const std::vector pwells = {"P1", "P2", "P3"}; + BOOST_CHECK( pwells == wm1.wells("P*")); + + auto wm2 = schedule.wellMatcher(4); + const auto& all_wells = wm2.wells(); + BOOST_CHECK_EQUAL(all_wells.size(), 9); + for (const auto& w : std::vector{"W1", "W2", "W3", "I1", "I2", "I3", "DEFAULT", "ALLOW", "BAN"}) + BOOST_CHECK(has(all_wells, w)); + + const std::vector wwells = {"W1", "W2", "W3"}; + BOOST_CHECK( wwells == wm2.wells("W*")); + BOOST_CHECK( wm2.wells("XYZ*").empty() ); + BOOST_CHECK( wm2.wells("XYZ").empty() ); + + auto def = wm2.wells("DEFAULT"); + BOOST_CHECK_EQUAL(def.size() , 1); + BOOST_CHECK_EQUAL(def[0], "DEFAULT"); + + + auto l2 = wm2.wells("*ILIST"); + BOOST_CHECK_EQUAL( l2.size(), 2U); + BOOST_CHECK( has(l2, "I1")); + BOOST_CHECK( has(l2, "I2")); } diff --git a/tests/parser/UDQTests.cpp b/tests/parser/UDQTests.cpp index f5e62a065..4faeb7ee4 100644 --- a/tests/parser/UDQTests.cpp +++ b/tests/parser/UDQTests.cpp @@ -81,7 +81,7 @@ BOOST_AUTO_TEST_CASE(GROUP_VARIABLES) UDQDefine def_group(udqp, "GUOPRL", location, {"(", "5000", "-", "GOPR", "LOWER", "*", "0.13", "-", "GOPR", "UPPER", "*", "0.15", ")" , "*", "0.89"}); SummaryState st(std::chrono::system_clock::now()); UDQState udq_state(udqp.undefinedValue()); - UDQContext context(udqft, st, udq_state); + UDQContext context(udqft, {}, st, udq_state); double gopr_lower = 1234; double gopr_upper = 4321; @@ -109,7 +109,7 @@ BOOST_AUTO_TEST_CASE(SUBTRACT) SummaryState st(std::chrono::system_clock::now()); UDQState udq_state(udqp.undefinedValue()); - UDQContext context(udqft, st, udq_state); + UDQContext context(udqft, WellMatcher({"P1"}), st, udq_state); st.update_well_var("P1", "WOPR", 4); auto res = def.eval(context); @@ -131,7 +131,7 @@ BOOST_AUTO_TEST_CASE(TEST) SummaryState st(std::chrono::system_clock::now()); UDQState udq_state(udqp.undefinedValue()); - UDQContext context(udqft, st, udq_state); + UDQContext context(udqft, WellMatcher({"P1", "P2"}), st, udq_state); st.update_group_var("MAU", "GOPR", 4); st.update_group_var("XXX", "GOPR", 5); @@ -176,7 +176,7 @@ BOOST_AUTO_TEST_CASE(MIX_SCALAR) { UDQDefine def_add(udqp, "WU", location, {"WOPR", "+", "1"}); SummaryState st(std::chrono::system_clock::now()); UDQState udq_state(udqp.undefinedValue()); - UDQContext context(udqft, st, udq_state); + UDQContext context(udqft, WellMatcher({"P1"}), st, udq_state); st.update_well_var("P1", "WOPR", 1); @@ -200,7 +200,7 @@ BOOST_AUTO_TEST_CASE(UDQFieldSetTest) { UDQFunctionTable udqft(udqp); SummaryState st(std::chrono::system_clock::now()); UDQState udq_state(udqp.undefinedValue()); - UDQContext context(udqft, st, udq_state); + UDQContext context(udqft, WellMatcher({"P1", "P2", "P3", "P4"}), st, udq_state); st.update_well_var("P1", "WOPR", 1.0); st.update_well_var("P2", "WOPR", 2.0); @@ -303,7 +303,7 @@ BOOST_AUTO_TEST_CASE(UDQ_GROUP_TEST) { UDQDefine def_fopr(udqp, "FUOPR", location, {"SUM", "(", "GOPR", ")"}); SummaryState st(std::chrono::system_clock::now()); UDQState udq_state(udqp.undefinedValue()); - UDQContext context(udqft, st, udq_state); + UDQContext context(udqft, {}, st, udq_state); st.update_group_var("G1", "GOPR", 1.0); st.update_group_var("G2", "GOPR", 2.0); @@ -326,7 +326,7 @@ BOOST_AUTO_TEST_CASE(UDQ_DEFINETEST) { UDQDefine def(udqp, "WUBHP", location, {"WBHP"}); SummaryState st(std::chrono::system_clock::now()); UDQState udq_state(udqp.undefinedValue()); - UDQContext context(udqft, st, udq_state); + UDQContext context(udqft, WellMatcher({"W1", "W2", "W3"}), st, udq_state); st.update_well_var("W1", "WBHP", 11); st.update_well_var("W2", "WBHP", 2); @@ -345,7 +345,7 @@ BOOST_AUTO_TEST_CASE(UDQ_DEFINETEST) { UDQDefine def(udqp, "WUBHP", location, {"WBHP" , "'P*'"}); SummaryState st(std::chrono::system_clock::now()); UDQState udq_state(udqp.undefinedValue()); - UDQContext context(udqft, st, udq_state); + UDQContext context(udqft, WellMatcher({"I1", "I2", "P1", "P2"}), st, udq_state); st.update_well_var("P1", "WBHP", 1); @@ -363,7 +363,7 @@ BOOST_AUTO_TEST_CASE(UDQ_DEFINETEST) { UDQDefine def(udqp, "WUBHP", location, {"NINT" , "(", "WBHP", ")"}); SummaryState st(std::chrono::system_clock::now()); UDQState udq_state(udqp.undefinedValue()); - UDQContext context(udqft, st, udq_state); + UDQContext context(udqft, WellMatcher({"P1", "P2", "I1", "I2"}), st, udq_state); st.update_well_var("P1", "WBHP", 4); st.update_well_var("P2", "WBHP", 3); st.update_well_var("I1", "WBHP", 2); @@ -579,7 +579,7 @@ BOOST_AUTO_TEST_CASE(UDQ_CONTEXT) { UDQFunctionTable func_table; UDQParams udqp; UDQState udq_state(udqp.undefinedValue()); - UDQContext ctx(func_table, st, udq_state); + UDQContext ctx(func_table, {}, st, udq_state); BOOST_CHECK_EQUAL(*ctx.get("JAN"), 1.0); BOOST_CHECK_THROW(ctx.get("NO_SUCH_KEY"), std::out_of_range); @@ -990,7 +990,7 @@ BOOST_AUTO_TEST_CASE(UDQ_POW_TEST) { UDQDefine def_pow2(udqp, "WU", location, {"(", "WOPR", "+", "WWPR", ")", "^", "(", "WOPR", "+" , "WGOR", "*", "WWIR", "-", "WBHP", ")"}); SummaryState st(std::chrono::system_clock::now()); UDQState udq_state(udqp.undefinedValue()); - UDQContext context(udqft, st, udq_state); + UDQContext context(udqft, WellMatcher({"P1"}), st, udq_state); st.update_well_var("P1", "WOPR", 1); st.update_well_var("P1", "WWPR", 2); @@ -1011,7 +1011,7 @@ BOOST_AUTO_TEST_CASE(UDQ_CMP_TEST) { UDQDefine def_cmp(udqp, "WU", location, {"WOPR", ">", "WWPR", "+", "WGOR", "*", "WWIR"}); SummaryState st(std::chrono::system_clock::now()); UDQState udq_state(udqp.undefinedValue()); - UDQContext context(udqft, st, udq_state); + UDQContext context(udqft, WellMatcher({"P1", "P2"}), st, udq_state); st.update_well_var("P1", "WOPR", 0); st.update_well_var("P1", "WWPR", 10); @@ -1040,61 +1040,61 @@ BOOST_AUTO_TEST_CASE(UDQ_SCALAR_SET) { UDQFunctionTable udqft; SummaryState st(std::chrono::system_clock::now()); UDQState udq_state(udqp.undefinedValue()); - UDQContext context(udqft, st, udq_state); + UDQContext context(udqft, WellMatcher({"PA1", "PB2", "PC3", "PD4"}), st, udq_state); - st.update_well_var("P1", "WOPR", 1); - st.update_well_var("P2", "WOPR", 2); - st.update_well_var("P3", "WOPR", 3); - st.update_well_var("P4", "WOPR", 4); + st.update_well_var("PA1", "WOPR", 1); + st.update_well_var("PB2", "WOPR", 2); + st.update_well_var("PC3", "WOPR", 3); + st.update_well_var("PD4", "WOPR", 4); - st.update_well_var("P1", "WWPR", 1); - st.update_well_var("P2", "WWPR", 2); - st.update_well_var("P3", "WWPR", 3); - st.update_well_var("P4", "WWPR", 4); + st.update_well_var("PA1", "WWPR", 1); + st.update_well_var("PB2", "WWPR", 2); + st.update_well_var("PC3", "WWPR", 3); + st.update_well_var("PD4", "WWPR", 4); { - UDQDefine def(udqp, "WUOPR", location, {"WOPR", "'*1'"}); + UDQDefine def(udqp, "WUOPR", location, {"WOPR", "'PA*'"}); auto res = def.eval(context); BOOST_CHECK_EQUAL(4U, res.size()); - auto well1 = res["P1"]; + auto well1 = res["PA1"]; BOOST_CHECK( well1.defined() ); BOOST_CHECK_EQUAL(well1.get() , 1); - auto well2 = res["P2"]; + auto well2 = res["PB2"]; BOOST_CHECK( !well2.defined() ); - auto well4 = res["P4"]; + auto well4 = res["PD4"]; BOOST_CHECK( !well4.defined() ); } { UDQDefine def(udqp, "WUOPR", location, {"1"}); auto res = def.eval(context); BOOST_CHECK_EQUAL(4U, res.size()); - auto well1 = res["P1"]; + auto well1 = res["PA1"]; BOOST_CHECK( well1.defined() ); BOOST_CHECK_EQUAL(well1.get() , 1); - auto well2 = res["P2"]; + auto well2 = res["PB2"]; BOOST_CHECK( well2.defined() ); BOOST_CHECK_EQUAL(well2.get() , 1); - auto well4 = res["P4"]; + auto well4 = res["PD4"]; BOOST_CHECK( well4.defined() ); BOOST_CHECK_EQUAL(well4.get() , 1); } { - UDQDefine def(udqp, "WUOPR", location, {"WOPR", "'P1'"}); + UDQDefine def(udqp, "WUOPR", location, {"WOPR", "'PA1'"}); auto res = def.eval(context); BOOST_CHECK_EQUAL(4U, res.size()); - auto well1 = res["P1"]; + auto well1 = res["PA1"]; BOOST_CHECK( well1.defined() ); BOOST_CHECK_EQUAL(well1.get() , 1); - auto well2 = res["P2"]; + auto well2 = res["PB2"]; BOOST_CHECK( well2.defined() ); BOOST_CHECK_EQUAL(well2.get() , 1); - auto well4 = res["P4"]; + auto well4 = res["PD4"]; BOOST_CHECK( well4.defined() ); BOOST_CHECK_EQUAL(well4.get() , 1); @@ -1110,7 +1110,7 @@ BOOST_AUTO_TEST_CASE(UDQ_SORTD_NAN) { UDQDefine def_sort(udqp , "WUPR3", location, {"SORTD", "(", "WUPR1", ")" }); SummaryState st(std::chrono::system_clock::now()); UDQState udq_state(udqp.undefinedValue()); - UDQContext context(udqft, st, udq_state); + UDQContext context(udqft, WellMatcher({"OP1", "OP2", "OP3", "OP4"}), st, udq_state); st.update_well_var("OP1", "WWIR", 1.0); st.update_well_var("OP2", "WWIR", 2.0); @@ -1156,7 +1156,7 @@ BOOST_AUTO_TEST_CASE(UDQ_SORTA) { UDQDefine def_sort(udqp , "WUPR3", location, {"SORTA", "(", "WUPR1", ")" }); SummaryState st(std::chrono::system_clock::now()); UDQState udq_state(udqp.undefinedValue()); - UDQContext context(udqft, st, udq_state); + UDQContext context(udqft, WellMatcher({"OPL01", "OPL02", "OPU01", "OPU02"}), st, udq_state); st.update_well_var("OPL01", "WWCT", 0.7); st.update_well_var("OPL02", "WWCT", 0.8); @@ -1186,7 +1186,7 @@ BOOST_AUTO_TEST_CASE(UDQ_BASIC_MATH_TEST) { UDQDefine def_wuwct(udqp , "WUWCT", location, {"WWPR", "/", "(", "WOPR", "+", "WWPR", ")"}); SummaryState st(std::chrono::system_clock::now()); UDQState udq_state(udqp.undefinedValue()); - UDQContext context(udqft, st, udq_state); + UDQContext context(udqft, WellMatcher({"P1", "P2", "P3", "P4"}), st, udq_state); st.update_well_var("P1", "WOPR", 1); st.update_well_var("P2", "WOPR", 2); @@ -1248,7 +1248,7 @@ BOOST_AUTO_TEST_CASE(DECK_TEST) { UDQDefine def(udqp, "WUOPRL", location, {"(", "WOPR", "OP1", "-", "150", ")", "*", "0.90"}); SummaryState st(std::chrono::system_clock::now()); UDQState udq_state(udqp.undefinedValue()); - UDQContext context(udqft, st, udq_state); + UDQContext context(udqft, WellMatcher({"OP1", "OP2", "OP3"}), st, udq_state); st.update_well_var("OP1", "WOPR", 300); st.update_well_var("OP2", "WOPR", 3000); @@ -1284,7 +1284,7 @@ BOOST_AUTO_TEST_CASE(UDQ_PARSE_ERROR) { SummaryState st(std::chrono::system_clock::now()); UDQFunctionTable udqft(udqp); UDQState udq_state(udqp.undefinedValue()); - UDQContext context(udqft, st, udq_state); + UDQContext context(udqft, WellMatcher({"P1"}), st, udq_state); st.update_well_var("P1", "WBHP", 1); auto res = def1.eval(context); @@ -1310,7 +1310,7 @@ BOOST_AUTO_TEST_CASE(UDQ_TYPE_ERROR) { SummaryState st(std::chrono::system_clock::now()); UDQFunctionTable udqft(udqp); UDQState udq_state(udqp.undefinedValue()); - UDQContext context(udqft, st, udq_state); + UDQContext context(udqft, WellMatcher({"P1", "P2"}), st, udq_state); st.update_well_var("P1", "WBHP", 1); st.update_well_var("P2", "WBHP", 2); @@ -1693,7 +1693,7 @@ UDQ SummaryState st(std::chrono::system_clock::now()); UDQFunctionTable udqft(udqp); UDQState udq_state(udqp.undefinedValue()); - UDQContext context(udqft, st, udq_state); + UDQContext context(udqft, {}, st, udq_state); auto res0 = def0.eval(context); BOOST_CHECK_CLOSE( res0[0].get(), -0.00125*3, 1e-6); @@ -1719,7 +1719,7 @@ UDQ SummaryState st(std::chrono::system_clock::now()); UDQFunctionTable udqft(udqp); UDQState udq_state(udqp.undefinedValue()); - UDQContext context(udqft, st, udq_state); + UDQContext context(udqft, {}, st, udq_state); const double fwpr = 7; const double fopr = 4; const double fgpr = 7; @@ -1761,7 +1761,7 @@ UDQ SummaryState st(std::chrono::system_clock::now()); UDQFunctionTable udqft(udqp); UDQState udq_state(udqp.undefinedValue()); - UDQContext context(udqft, st, udq_state); + UDQContext context(udqft, WellMatcher({"W1", "W2", "W3"}), st, udq_state); st.update_well_var("W1", "WOPR", 1); st.update_well_var("W2", "WOPR", 2); st.update_well_var("W3", "WOPR", 3); @@ -1800,7 +1800,7 @@ UDQ SummaryState st(std::chrono::system_clock::now()); auto undefined_value = udq.params().undefinedValue(); UDQState udq_state(undefined_value); - udq.eval(0, st, udq_state); + udq.eval(0, {}, st, udq_state); BOOST_CHECK_EQUAL( st.get("FU_UADD"), 12); // 10 + 2 @@ -1827,7 +1827,7 @@ DEFINE FU_PAR2 FU_PAR3 / auto undefined_value = udq.params().undefinedValue(); UDQState udq_state(undefined_value); st.update("FMWPR", 100); - udq.eval(0, st, udq_state); + udq.eval(0, {}, st, udq_state); BOOST_CHECK_EQUAL(st.get("FU_PAR2"), 100); } @@ -1845,7 +1845,7 @@ DEFINE FU_PAR3 FU_PAR2 + 1/ SummaryState st(std::chrono::system_clock::now()); auto undefined_value = udq.params().undefinedValue(); UDQState udq_state(undefined_value); - udq.eval(0, st, udq_state); + udq.eval(0, {}, st, udq_state); BOOST_CHECK_EQUAL(st.get("FU_PAR2"), undefined_value); BOOST_CHECK_EQUAL(st.get("FU_PAR3"), undefined_value); @@ -1963,7 +1963,7 @@ DEFINE WUGASRA 750000 - WGLIR '*' / st.update_well_var("W2", "WGLIR", 2); st.update_well_var("W3", "WGLIR", 3); - udq.eval(0, st, udq_state); + udq.eval(0, WellMatcher({"W1", "W2", "W3"}), st, udq_state); { std::unordered_set required_keys; udq.required_summary(required_keys); @@ -2168,7 +2168,7 @@ DEFINE FU_VAR91 GOPR TEST / st.update_well_var("W3", "WGLIR", 3); st.update_group_var("TEST", "GOPR", 1); - udq.eval(0, st, udq_state); + udq.eval(0, {}, st, udq_state); } @@ -2189,7 +2189,7 @@ UDQ UDQState udq_state(undefined_value); SummaryState st(std::chrono::system_clock::now()); - BOOST_CHECK_THROW(udq.eval(0, st, udq_state), std::exception); + BOOST_CHECK_THROW(udq.eval(0, {}, st, udq_state), std::exception); } @@ -2215,7 +2215,7 @@ UDQ BOOST_CHECK(required_keys.empty()); } - udq.eval(0, st, udq_state); + udq.eval(0, {}, st, udq_state); BOOST_CHECK_EQUAL(st.get("FU_VAR1"), 10); } @@ -2256,7 +2256,7 @@ TSTEP // Counting: 1,2,3,4,5 for (std::size_t report_step = 0; report_step < 5; report_step++) { const auto& udq = schedule.getUDQConfig(report_step); - udq.eval(report_step, st, udq_state); + udq.eval(report_step, schedule.wellMatcher(report_step), st, udq_state); auto fu_var1 = st.get("FU_VAR1"); BOOST_CHECK_EQUAL(fu_var1, report_step + 1); } @@ -2264,7 +2264,7 @@ TSTEP // Reset to zero and count: 1,2,3,4,5 for (std::size_t report_step = 5; report_step < 10; report_step++) { const auto& udq = schedule.getUDQConfig(report_step); - udq.eval(report_step, st, udq_state); + udq.eval(report_step, schedule.wellMatcher(report_step), st, udq_state); auto fu_var1 = st.get("FU_VAR1"); BOOST_CHECK_EQUAL(fu_var1, report_step - 4); } @@ -2272,7 +2272,7 @@ TSTEP // Reset to zero and stay there. for (std::size_t report_step = 10; report_step < 15; report_step++) { const auto& udq = schedule.getUDQConfig(report_step); - udq.eval(report_step, st, udq_state); + udq.eval(report_step, schedule.wellMatcher(report_step),st, udq_state); auto fu_var1 = st.get("FU_VAR1"); BOOST_CHECK_EQUAL(fu_var1, 0); } @@ -2286,7 +2286,7 @@ BOOST_AUTO_TEST_CASE(UDQ_DIV_TEST) { UDQDefine def_div(udqp, "FU", location, {"128", "/", "2", "/", "4", "/", "8"}); SummaryState st(std::chrono::system_clock::now()); UDQState udq_state(udqp.undefinedValue()); - UDQContext context(udqft, st, udq_state); + UDQContext context(udqft, {}, st, udq_state); auto res_div = def_div.eval(context); BOOST_CHECK_EQUAL( res_div[0].get() , 2.0); @@ -2314,7 +2314,7 @@ UDQ SummaryState st(std::chrono::system_clock::now()); const auto& udq = schedule.getUDQConfig(0); - udq.eval(0, st, udq_state); + udq.eval(0, {}, st, udq_state); auto fu_var1 = st.get("FU_VAR1"); auto fu_var2 = st.get("FU_VAR2"); auto fu_var3 = st.get("FU_VAR3"); @@ -2327,3 +2327,41 @@ UDQ BOOST_CHECK_CLOSE(fu_var5, -0.00000041232 * 4 + 0.0010395 * 3 + 0.16504, 1e-5); } +BOOST_AUTO_TEST_CASE(UDQ_WLIST) { + std::string deck_string = R"( +SCHEDULE + +WELSPECS + 'P1' 'OP' 20 51 3.92 'OIL' 3* NO / + 'P2' 'OP' 20 51 3.92 'OIL' 3* NO / + 'P3' 'OP' 20 51 3.92 'OIL' 3* NO / + 'P4' 'OP' 20 51 3.92 'OIL' 3* NO / +/ + +WLIST + '*ILIST' 'NEW' P1 P2 P3 / +/ + +UDQ + DEFINE FU_VAR1 SUM(WOPR '*ILIST') / + DEFINE FU_VAR2 SUM(WOPR '*') / +/ + +)"; + + auto schedule = make_schedule(deck_string); + UDQState udq_state(0); + SummaryState st(std::chrono::system_clock::now()); + const auto& udq = schedule.getUDQConfig(0); + st.update_well_var("P1", "WOPR", 1); + st.update_well_var("P2", "WOPR", 2); + st.update_well_var("P3", "WOPR", 3); + st.update_well_var("P4", "WOPR", 4); + + udq.eval(0, schedule.wellMatcher(0), st, udq_state); + auto fu_var1 = st.get("FU_VAR1"); + auto fu_var2 = st.get("FU_VAR2"); + BOOST_CHECK_EQUAL(fu_var1, 6); + BOOST_CHECK_EQUAL(fu_var2, 10); +} + diff --git a/tests/test_Restart.cpp b/tests/test_Restart.cpp index a5f5262d0..a366ac858 100644 --- a/tests/test_Restart.cpp +++ b/tests/test_Restart.cpp @@ -399,7 +399,7 @@ RestartValue first_sim(const Setup& setup, Action::State& action_state, SummaryS RestartValue restart_value(sol, wells, groups); init_st(st); - udq.eval(report_step, st, udq_state); + udq.eval(report_step, setup.schedule.wellMatcher(report_step), st, udq_state); eclWriter.writeTimeStep( action_state, st, udq_state, From 14df089f14ddadab8be7f8a16c62ef668246af8a Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Fri, 30 Oct 2020 22:17:28 +0100 Subject: [PATCH 3/3] Use WellMatcher() in Schedule::wellNames() --- .../EclipseState/Schedule/Schedule.cpp | 32 ++----------------- .../EclipseState/Schedule/UDQ/UDQASTNode.cpp | 8 +++-- tests/parser/UDQTests.cpp | 3 ++ 3 files changed, 10 insertions(+), 33 deletions(-) diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp index b45a1bae4..93e75f876 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp @@ -1050,40 +1050,12 @@ private: std::vector Schedule::wellNames(const std::string& pattern, std::size_t timeStep, const std::vector& matching_wells) const { - if (pattern.size() == 0) - return {}; - - // WLIST - if (pattern[0] == '*' && pattern.size() > 1) { - const auto& wlm = this->getWListManager(timeStep); - return wlm.wells(pattern); - } - - // Normal pattern matching - auto star_pos = pattern.find('*'); - if (star_pos != std::string::npos) { - std::vector names; - for (const auto& well_pair : this->wells_static) { - if (name_match(pattern, well_pair.first)) { - const auto& dynamic_state = well_pair.second; - if (dynamic_state.get(timeStep)) - names.push_back(well_pair.first); - } - } - return names; - } - // ACTIONX handler if (pattern == "?") return { matching_wells.begin(), matching_wells.end() }; - // Normal well name without any special characters - if (this->hasWell(pattern)) { - const auto& dynamic_state = this->wells_static.at(pattern); - if (dynamic_state.get(timeStep)) - return { pattern }; - } - return {}; + auto wm = this->wellMatcher(timeStep); + return wm.wells(pattern); } diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQASTNode.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQASTNode.cpp index f0fd69895..8873e9d9a 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQASTNode.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQASTNode.cpp @@ -146,11 +146,12 @@ UDQSet UDQASTNode::eval(UDQVarType target_type, const UDQContext& context) const auto data_type = UDQ::targetType(string_value); if (data_type == UDQVarType::WELL_VAR) { const auto& all_wells = context.wells(); - auto res = UDQSet::wells(string_value, all_wells); if (this->selector.empty()) { + auto res = UDQSet::wells(string_value, all_wells); for (const auto& well : all_wells) res.assign(well, context.get_well_var(well, string_value)); + return this->sign * res; } else { const std::string& well_pattern = this->selector[0]; if (well_pattern.find('*') == std::string::npos) @@ -160,7 +161,7 @@ UDQSet UDQASTNode::eval(UDQVarType target_type, const UDQContext& context) const *scalar* - and that scalar value is distributed among all the wells in the result set. */ - res.assign( context.get_well_var(well_pattern, string_value)); + return this->sign * UDQSet::scalar(string_value, context.get_well_var(well_pattern, string_value)); else { /* The right hand side is a set of wells. The result set will @@ -168,11 +169,12 @@ UDQSet UDQASTNode::eval(UDQVarType target_type, const UDQContext& context) const missing in the right hand set will be undefined in the result set. */ + auto res = UDQSet::wells(string_value, all_wells); for (const auto& wname : context.wells(well_pattern)) res.assign(wname, context.get_well_var(wname, string_value)); + return this->sign * res; } } - return this->sign * res; } if (data_type == UDQVarType::GROUP_VAR) { diff --git a/tests/parser/UDQTests.cpp b/tests/parser/UDQTests.cpp index 4faeb7ee4..f45bd03d3 100644 --- a/tests/parser/UDQTests.cpp +++ b/tests/parser/UDQTests.cpp @@ -2345,6 +2345,7 @@ WLIST UDQ DEFINE FU_VAR1 SUM(WOPR '*ILIST') / DEFINE FU_VAR2 SUM(WOPR '*') / + DEFINE FU_VAR3 WOPR 'P4' / / )"; @@ -2361,7 +2362,9 @@ UDQ udq.eval(0, schedule.wellMatcher(0), st, udq_state); auto fu_var1 = st.get("FU_VAR1"); auto fu_var2 = st.get("FU_VAR2"); + auto fu_var3 = st.get("FU_VAR3"); BOOST_CHECK_EQUAL(fu_var1, 6); BOOST_CHECK_EQUAL(fu_var2, 10); + BOOST_CHECK_EQUAL(fu_var3, 4); }