diff --git a/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp b/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp index f10be9d41..2832a69e0 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp @@ -59,6 +59,7 @@ namespace Opm class UnitSystem; class ErrorGuard; class WListManager; + class UDQ; class Schedule { public: @@ -136,6 +137,7 @@ namespace Opm const WellTestConfig& wtestConfig(size_t timestep) const; const WListManager& getWListManager(size_t timeStep) const; + const UDQ& getUDQConfig(size_t timeStep) const; const Actions& actions() const; void evalAction(const SummaryState& summary_state, size_t timeStep); @@ -178,6 +180,7 @@ namespace Opm std::map>> vfpinj_tables; DynamicState> wtest_config; DynamicState> wlist_manager; + DynamicState> udq_config; WellProducer::ControlModeEnum m_controlModeWHISTCTL; Actions m_actions; @@ -192,6 +195,7 @@ namespace Opm bool handleGroupFromWELSPECS(const std::string& groupName, GroupTree& newTree) const; void addGroup(const std::string& groupName , size_t timeStep); void addWell(const std::string& wellName, const DeckRecord& record, size_t timeStep, WellCompletion::CompletionOrderEnum wellCompletionOrder); + void handleUDQ(const DeckKeyword& keyword, size_t currentStep); void handleWLIST(const DeckKeyword& keyword, size_t currentStep); void handleCOMPORD(const ParseContext& parseContext, ErrorGuard& errors, const DeckKeyword& compordKeyword, size_t currentStep); void handleWELSPECS( const SCHEDULESection&, size_t, size_t ); diff --git a/opm/parser/eclipse/EclipseState/Schedule/UDQ.hpp b/opm/parser/eclipse/EclipseState/Schedule/UDQ.hpp index 099a4e68e..529e223fb 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/UDQ.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/UDQ.hpp @@ -21,6 +21,9 @@ #ifndef UDQState_HPP_ #define UDQState_HPP_ +#include +#include + #include @@ -28,10 +31,13 @@ namespace Opm { class UDQ{ public: - explicit UDQ(const Deck& deck); + void add_record(const DeckRecord& record); const std::vector& expressions() const noexcept; + const std::string& unit(const std::string& key) const; + void assign_unit(const std::string& keyword, const std::string& unit); private: std::vector m_expressions; + std::unordered_map units; }; } diff --git a/opm/parser/eclipse/EclipseState/Schedule/UDQExpression.hpp b/opm/parser/eclipse/EclipseState/Schedule/UDQExpression.hpp index d6518c3ee..c767588a7 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/UDQExpression.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/UDQExpression.hpp @@ -36,9 +36,12 @@ namespace Opm { UDQExpression(const std::string& action, const std::string& keyword, const std::vector& data); explicit UDQExpression(const DeckRecord& expression); const std::vector& tokens() const; + UDQAction action() const; + const std::string& keyword() const; + static UDQAction actionString2Enum(const std::string& action_string); private: - UDQAction action; - std::string keyword; + UDQAction m_action; + std::string m_keyword; std::vector data; }; } diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp index 8a7c4d9ae..b3a0af402 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp @@ -50,6 +50,7 @@ #include #include +#include #include #include #include @@ -84,7 +85,8 @@ namespace Opm { m_messageLimits( this->m_timeMap ), m_runspec( runspec ), wtest_config(this->m_timeMap, std::make_shared() ), - wlist_manager( this->m_timeMap, std::make_shared()) + wlist_manager( this->m_timeMap, std::make_shared()), + udq_config(this->m_timeMap, std::make_shared()) { m_controlModeWHISTCTL = WellProducer::CMODE_UNDEFINED; addGroup( "FIELD", 0 ); @@ -212,6 +214,9 @@ namespace Opm { currentStep += keyword.getRecord(0).getItem(0).size(); // This is a bit weird API. } + else if (keyword.name() == "UDQ") + handleUDQ(keyword, currentStep); + else if (keyword.name() == "WLIST") handleWLIST( keyword, currentStep ); @@ -938,6 +943,17 @@ namespace Opm { this->wlist_manager.update(currentStep, new_wlm); } + void Schedule::handleUDQ(const DeckKeyword& keyword, size_t currentStep) { + const auto& current = *this->udq_config.get(currentStep); + std::shared_ptr new_udq = std::make_shared(current); + + for (const auto& record : keyword) + new_udq->add_record(record); + + this->udq_config.update(currentStep, new_udq); + } + + void Schedule::handleWTEST(const DeckKeyword& keyword, size_t currentStep, const ParseContext& parseContext, ErrorGuard& errors) { const auto& current = *this->wtest_config.get(currentStep); std::shared_ptr new_config(new WellTestConfig(current)); @@ -2139,6 +2155,11 @@ namespace Opm { return *ptr; } + const UDQ& Schedule::getUDQConfig(size_t timeStep) const { + const auto& ptr = this->udq_config.get(timeStep); + return *ptr; + } + size_t Schedule::size() const { return this->m_timeMap.size(); diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/UDQ.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/UDQ.cpp index 1db757057..43c90c817 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/UDQ.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/UDQ.cpp @@ -22,16 +22,51 @@ namespace Opm { - UDQ::UDQ(const Deck& deck) { - if (deck.hasKeyword("UDQ")) { - const auto& kw = deck.getKeyword("UDQ"); - for (const auto& record : kw) - this->m_expressions.emplace_back( record ); + namespace { + std::string strip_quotes(const std::string& s) { + if (s[0] == '\'') + return s.substr(1, s.size() - 2); + else + return s; } + } + + void UDQ::add_record(const DeckRecord& record) { + const auto& action = record.getItem("ACTION").get(0); + const auto& quantity = record.getItem("QUANTITY").get(0); + const auto& data = record.getItem("DATA").getData(); + + if (UDQExpression::actionString2Enum(action) == UDQAction::UNITS) + this->assign_unit( quantity, data[0] ); + else + this->m_expressions.emplace_back(action, quantity, data); + } + + const std::vector& UDQ::expressions() const noexcept { return this->m_expressions; } + const std::string& UDQ::unit(const std::string& key) const { + const auto pair_ptr = this->units.find(key); + if (pair_ptr == this->units.end()) + throw std::invalid_argument("No such UDQ quantity: " + key); + + return pair_ptr->second; + } + + + void UDQ::assign_unit(const std::string& keyword, const std::string& quoted_unit) { + const std::string unit = strip_quotes(quoted_unit); + const auto pair_ptr = this->units.find(keyword); + if (pair_ptr != this->units.end()) { + if (pair_ptr->second != unit) + throw std::invalid_argument("Illegal to change unit of UDQ keyword runtime"); + + return; + } + this->units[keyword] = unit; + } } diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/UDQExpression.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/UDQExpression.cpp index d42a5ca79..a5471bfff 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/UDQExpression.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/UDQExpression.cpp @@ -33,22 +33,6 @@ namespace Opm { namespace { - UDQAction actionString2Enum(const std::string& action_string) { - if (action_string == "ASSIGN") - return UDQAction::ASSIGN; - - if (action_string == "DEFINE") - return UDQAction::DEFINE; - - if (action_string == "UNITS") - return UDQAction::UNITS; - - if (action_string == "UPDATE") - return UDQAction::UPDATE; - - throw std::invalid_argument("Invalid action string " + action_string); - } - void assertKeyword(const std::string& keyword) { const std::string valid_start = "CFGRSWAB"; @@ -76,8 +60,8 @@ namespace Opm { UDQExpression::UDQExpression(const std::string& action_in, const std::string& keyword_in, const std::vector& input_data) { assertKeyword(keyword_in); - this->action = actionString2Enum(action_in); - this->keyword = keyword_in; + this->m_action = actionString2Enum(action_in); + this->m_keyword = keyword_in; for (const std::string& item : input_data) { if (RawConsts::is_quote()(item[0])) { @@ -123,4 +107,35 @@ namespace Opm { const std::vector& UDQExpression::tokens() const { return this->data; } + + + UDQAction UDQExpression::action() const { + return this->m_action; + } + + + const std::string& UDQExpression::keyword() const { + return this->m_keyword; + } + + + UDQAction UDQExpression::actionString2Enum(const std::string& action_string) { + if (action_string == "ASSIGN") + return UDQAction::ASSIGN; + + if (action_string == "DEFINE") + return UDQAction::DEFINE; + + if (action_string == "UNITS") + return UDQAction::UNITS; + + if (action_string == "UPDATE") + return UDQAction::UPDATE; + + throw std::invalid_argument("Invalid action string " + action_string); + } + + + } + diff --git a/tests/parser/UDQTests.cpp b/tests/parser/UDQTests.cpp index d00e94bd2..4a50d598d 100644 --- a/tests/parser/UDQTests.cpp +++ b/tests/parser/UDQTests.cpp @@ -23,11 +23,25 @@ #include #include #include +#include #include #include using namespace Opm; + +Schedule make_schedule(const std::string& input) { + Parser parser; + + auto deck = parser.parseString(input); + EclipseGrid grid(10,10,10); + TableManager table ( deck ); + Eclipse3DProperties eclipseProperties ( deck , table, grid); + Runspec runspec (deck); + return Schedule(deck, grid , eclipseProperties, runspec); +} + + BOOST_AUTO_TEST_CASE(KEYWORDS) { const std::string input = R"( RUNSPEC @@ -67,17 +81,63 @@ UDQ UNITS WUBHP 'BARSA' / DEFINE FUOPR AVEG(WOPR) + 1/ / + +DATES + 10 'JAN' 2010 / +/ + +UDQ + ASSIGN WUBHP 0.0 / + DEFINE FUOPR AVEG(WOPR) + 1/ + UNITS WUBHP 'BARSA' / -- Repeating the same unit multiple times is superfluous but OK +/ )"; - Parser parser; + auto schedule = make_schedule(input); + const auto& udq = schedule.getUDQConfig(0); + BOOST_CHECK_EQUAL(2, udq.expressions().size()); - auto deck = parser.parseString(input); - auto udq_params = UDQParams(deck); - auto udq = UDQ(deck); - BOOST_CHECK_EQUAL(0.25, udq_params.cmpEpsilon()); - BOOST_CHECK_EQUAL(3, udq.expressions().size()); + BOOST_CHECK_THROW( udq.unit("NO_SUCH_KEY"), std::invalid_argument ); + BOOST_CHECK_EQUAL( udq.unit("WUBHP"), "BARSA"); } +BOOST_AUTO_TEST_CASE(UDQ_CHANGE_UNITS_ILLEGAL) { + const std::string input = R"( +RUNSPEC + +UDQDIMS + 10* 'Y'/ + +UDQPARAM + 3* 0.25 / + +SCHEDULE + +UDQ + ASSIGN WUBHP 0.0 / + UNITS WUBHP 'BARSA' / + DEFINE FUOPR AVEG(WOPR) + 1/ +/ + +DATES + 10 'JAN' 2010 / +/ + +UDQ + ASSIGN WUBHP 0.0 / + DEFINE FUOPR AVEG(WOPR) + 1/ + UNITS WUBHP 'HOURS' / -- Changing unit runtime is *not* supported +/ +)"; + + BOOST_CHECK_THROW( make_schedule(input), std::invalid_argument); +} + + + + + + BOOST_AUTO_TEST_CASE(UDQ_KEYWORD) { // Invalid action BOOST_REQUIRE_THROW( UDQExpression("INVALID_ACTION", "WUBHP" , {"DATA1" ,"1"}), std::invalid_argument); @@ -108,11 +168,8 @@ DEFINE WUMW1 WBHP 'P*1*' UMAX WBHP 'P*4*' / )"; - Parser parser; - - auto deck = parser.parseString(input); - auto udq_params = UDQParams(deck); - auto udq = UDQ(deck); + const auto schedule = make_schedule(input); + const auto& udq = schedule.getUDQConfig(0); const auto& records = udq.expressions(); const auto& rec0 = records[0]; const auto& rec1 = records[1]; @@ -120,7 +177,6 @@ DEFINE WUMW1 WBHP 'P*1*' UMAX WBHP 'P*4*' / const std::vector exp1 = {"WBHP", "P*1*", "UMAX", "WBHP" , "P*4*"}; BOOST_CHECK_EQUAL_COLLECTIONS(rec0.tokens().begin(), rec0.tokens().end(), exp0.begin(), exp0.end()); BOOST_CHECK_EQUAL_COLLECTIONS(rec1.tokens().begin(), rec1.tokens().end(), exp1.begin(), exp1.end()); - BOOST_CHECK_EQUAL(0.25, udq_params.cmpEpsilon()); }