From 11fe1d6e03fed33f281ad9b82355a81f4295c4fa Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Mon, 24 Jun 2019 16:04:48 +0200 Subject: [PATCH] Maintain insert order in UDQINput --- .../EclipseState/Schedule/UDQ/UDQAssign.hpp | 19 ++- .../EclipseState/Schedule/UDQ/UDQEnums.hpp | 3 +- .../EclipseState/Schedule/UDQ/UDQInput.hpp | 51 +++++++- .../EclipseState/Schedule/UDQ/UDQAssign.cpp | 33 +++-- .../EclipseState/Schedule/UDQ/UDQInput.cpp | 117 ++++++++++++++---- tests/parser/UDQTests.cpp | 106 ++++++++++++++-- 6 files changed, 264 insertions(+), 65 deletions(-) diff --git a/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQAssign.hpp b/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQAssign.hpp index 26ad31f1c..628814fbc 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQAssign.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQAssign.hpp @@ -29,19 +29,30 @@ namespace Opm { + class UDQAssign{ public: + + /* + If the same keyword is assigned several times the different assignment + records are assembled in one UDQAssign instance. This is an attempt to + support restart in a situation where a full UDQ ASSIGN statement can be + swapped with a UDQ DEFINE statement. + */ + struct AssignRecord { + std::vector selector; + double value; + }; + UDQAssign(const std::string& keyword, const std::vector& selector, double value); const std::string& keyword() const; - double value() const; UDQVarType var_type() const; - const std::vector& selector() const; + void add_record(const std::vector& selector, double value); UDQSet eval(const std::vector& wells) const; private: std::string m_keyword; UDQVarType m_var_type; - std::vector m_selector; - double m_value; + std::vector records; }; } diff --git a/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQEnums.hpp b/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQEnums.hpp index fe1b707ae..f2cd81e76 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQEnums.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQEnums.hpp @@ -97,7 +97,8 @@ enum class UDQAction { ASSIGN, DEFINE, UNITS, - UPDATE}; + UPDATE +}; diff --git a/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQInput.hpp b/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQInput.hpp index 30cb7e79f..04e3de147 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQInput.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQInput.hpp @@ -30,6 +30,7 @@ #include #include #include +#include namespace Opm { @@ -46,20 +47,60 @@ namespace Opm { void add_record(const DeckRecord& record); void assign_unit(const std::string& keyword, const std::string& unit); - const std::vector& definitions() const; + std::vector definitions() const; std::vector definitions(UDQVarType var_type) const; - const std::vector& assignments() const; + /* + The input_definitions() function is written to supply the information + needed when writing the restart file. The return value is a list of + pairs, where the first element in the pair is the index in the deck + for a particular UDQ keyword, and then the corresponding keyword. + Assume a deck keyword which looks like this: + + UDQ + ASSIGN WUX 10 / + UNITS WUX 'BARSA' / + DEFINE WUPR SUM(WOPR) * 0.75 / + DEFINE FUCK MAX(WOPR) * 1.25 / + ASSIGN FUX 100 / + DEFINE BUPR ?? / + / + + Then the return value from input_definitions() will be: + + {{1, UDQDefine("WUPR")}, + {2, UDQDefine("FUCK")}, + {4, UDQDefine("BUPR")} + + + Where the the numerical index is the index in a fictious vector + consisting of only the ASSIGN and DEFINE keywords, in input order. + */ + std::vector> input_definitions() const; + + std::vector assignments() const; std::vector assignments(UDQVarType var_type) const; const UDQParams& params() const; const UDQFunctionTable& function_table() const; private: UDQParams udq_params; UDQFunctionTable udqft; - std::vector m_definitions; - std::vector m_assignments; + + + /* + The choices of datastructures are strongly motivated by the + constraints imposed by the Eclipse formatted restart files; for + writing restart files it is essential to keep meticolous control over + the ordering of the keywords. In this class the ordering is mainly + maintained by the input_index map which keeps track of the insert + order of each keyword, and whether the keyword is currently DEFINE'ed + or ASSIGN'ed. + */ + std::unordered_map m_definitions; + std::unordered_map m_assignments; std::unordered_map units; - std::unordered_set keywords; + + OrderedMap> input_index; }; } diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQAssign.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQAssign.cpp index 585098671..ede52301f 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQAssign.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQAssign.cpp @@ -24,36 +24,35 @@ namespace Opm { UDQAssign::UDQAssign(const std::string& keyword, const std::vector& selector, double value) : m_keyword(keyword), - m_var_type(UDQ::varType(keyword)), - m_selector(selector), - m_value(value) -{} + m_var_type(UDQ::varType(keyword)) +{ + this->add_record(selector, value); +} +void UDQAssign::add_record(const std::vector& selector, double value) { + this->records.push_back({selector, value}); +} const std::string& UDQAssign::keyword() const { return this->m_keyword; } -const std::vector& UDQAssign::selector() const { - return this->m_selector; -} - -double UDQAssign::value() const { - return this->m_value; -} - UDQVarType UDQAssign::var_type() const { return this->m_var_type; } UDQSet UDQAssign::eval(const std::vector& wells) const { if (this->m_var_type == UDQVarType::WELL_VAR) { - UDQSet ws= UDQSet::wells(this->m_keyword, wells); + UDQSet ws = UDQSet::wells(this->m_keyword, wells); - if (this->m_selector.empty()) - ws.assign(this->m_value); - else - ws.assign(this->m_selector[0], this->m_value); + for (const auto& record : this->records) { + const auto& selector = record.selector; + double value = record.value; + if (selector.empty()) + ws.assign(value); + else + ws.assign(selector[0], value); + } return ws; } diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQInput.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQInput.cpp index 923bc406b..e50681ccd 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQInput.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQInput.cpp @@ -45,57 +45,105 @@ namespace Opm { return this->udq_params; } - void UDQInput::add_record(const DeckRecord& record) { auto action = UDQ::actionType(record.getItem("ACTION").get(0)); const auto& quantity = record.getItem("QUANTITY").get(0); const auto& data = record.getItem("DATA").getData(); + if (action == UDQAction::UPDATE) + throw std::invalid_argument("The UDQ action UPDATE is not yet implemented in opm/flow"); + if (action == UDQAction::UNITS) this->assign_unit( quantity, data[0] ); - else if (action == UDQAction::ASSIGN) { - std::vector selector(data.begin(), data.end() - 1); - double value = std::stod(data.back()); - this->m_assignments.emplace_back( quantity, selector, value ); - } else - this->m_definitions.emplace_back(this->udq_params, quantity, data); - this->keywords.insert(quantity); + else { + auto index_iter = this->input_index.find(quantity); + if (this->input_index.find(quantity) == this->input_index.end()) + this->input_index[quantity] = std::make_pair(this->input_index.size(), action); + else + index_iter->second.second = action; + + + if (action == UDQAction::ASSIGN) { + std::vector selector(data.begin(), data.end() - 1); + double value = std::stod(data.back()); + auto assignment = this->m_assignments.find(quantity); + if (assignment == this->m_assignments.end()) + this->m_assignments.insert( std::make_pair(quantity, UDQAssign(quantity, selector, value ))); + else + assignment->second.add_record(selector, value); + } else if (action == UDQAction::DEFINE) + this->m_definitions.insert( std::make_pair(quantity, UDQDefine(this->udq_params, quantity, data))); + else + throw std::runtime_error("Internal error - should not be here"); + } } - const std::vector& UDQInput::definitions() const { - return this->m_definitions; + std::vector UDQInput::definitions() const { + std::vector ret; + for (const auto& index_pair : this->input_index) { + if (index_pair.second.second == UDQAction::DEFINE) { + const std::string& key = index_pair.first; + ret.push_back(this->m_definitions.at(key)); + } + } + return ret; } + std::vector UDQInput::definitions(UDQVarType var_type) const { std::vector filtered_defines; - - std::copy_if(this->m_definitions.begin(), - this->m_definitions.end(), - std::back_inserter(filtered_defines), - [&var_type](const UDQDefine& def) { return def.var_type() == var_type; }); - + for (const auto& index_pair : this->input_index) { + if (index_pair.second.second == UDQAction::DEFINE) { + const std::string& key = index_pair.first; + const auto& udq_define = this->m_definitions.at(key); + if (udq_define.var_type() == var_type) + filtered_defines.push_back(udq_define); + } + } return filtered_defines; } - const std::vector& UDQInput::assignments() const { - return this->m_assignments; + std::vector> UDQInput::input_definitions() const { + std::vector> res; + for (const auto& index_pair : this->input_index) { + if (index_pair.second.second == UDQAction::DEFINE) { + const std::string& key = index_pair.first; + res.emplace_back(index_pair.second.first, this->m_definitions.at(key)); + } + } + return res; + } + + + std::vector UDQInput::assignments() const { + std::vector ret; + for (const auto& index_pair : this->input_index) { + if (index_pair.second.second == UDQAction::ASSIGN) { + const std::string& key = index_pair.first; + ret.push_back(this->m_assignments.at(key)); + } + } + return ret; } std::vector UDQInput::assignments(UDQVarType var_type) const { - std::vector filtered_assignments; - - std::copy_if(this->m_assignments.begin(), - this->m_assignments.end(), - std::back_inserter(filtered_assignments), - [&var_type](const UDQAssign& assignment) { return assignment.var_type() == var_type; }); - - return filtered_assignments; + std::vector filtered_defines; + for (const auto& index_pair : this->input_index) { + if (index_pair.second.second == UDQAction::ASSIGN) { + const std::string& key = index_pair.first; + const auto& udq_define = this->m_assignments.at(key); + if (udq_define.var_type() == var_type) + filtered_defines.push_back(udq_define); + } + } + return filtered_defines; } + const std::string& UDQInput::unit(const std::string& key) const { const auto pair_ptr = this->units.find(key); if (pair_ptr == this->units.end()) @@ -121,10 +169,25 @@ namespace Opm { return (this->units.count(keyword) > 0); } + bool UDQInput::has_keyword(const std::string& keyword) const { - return (this->keywords.count(keyword) > 0); + if (this->m_assignments.count(keyword) > 0) + return true; + + if (this->m_definitions.count(keyword) > 0) + return true; + + /* + That a keyword is mentioned with UNITS is enough to consider it + as a keyword which is present. + */ + if (this->units.count(keyword) > 0) + return true; + + return false; } + const UDQFunctionTable& UDQInput::function_table() const { return this->udqft; } diff --git a/tests/parser/UDQTests.cpp b/tests/parser/UDQTests.cpp index 597907966..318114f12 100644 --- a/tests/parser/UDQTests.cpp +++ b/tests/parser/UDQTests.cpp @@ -48,6 +48,7 @@ Schedule make_schedule(const std::string& input) { return Schedule(deck, grid , eclipseProperties, runspec); } + BOOST_AUTO_TEST_CASE(MIX_SCALAR) { UDQFunctionTable udqft; UDQParams udqp; @@ -276,7 +277,6 @@ BOOST_AUTO_TEST_CASE(ENUM_CONVERSION) { } - BOOST_AUTO_TEST_CASE(UDQ_KEWYORDS) { const std::string input = R"( RUNSPEC @@ -418,18 +418,18 @@ ASSIGN WU2 8.0 / const auto& assignments = udq.assignments(); const auto& ass0 = assignments[0]; const auto& ass1 = assignments[1]; + auto w1 = ass0.eval({"P1", "P2", "P12"}); + auto w2 = ass1.eval({"P1", "P2", "P12"}); + BOOST_CHECK_EQUAL(w1.name(), "WU1"); + BOOST_CHECK_EQUAL(w2.name(), "WU2"); + BOOST_CHECK_EQUAL( w1["P12"].value(), 4.0 ); + BOOST_CHECK_EQUAL( w1["P1"].defined(), false ); + BOOST_CHECK_EQUAL( w1["P2"].defined(), false ); - BOOST_CHECK_EQUAL(ass0.keyword(), "WU1"); - BOOST_CHECK_EQUAL(ass1.keyword(), "WU2"); - - BOOST_CHECK_EQUAL(ass0.value(), 4.0 ); - BOOST_CHECK_EQUAL(ass1.value(), 8.0 ); - - std::vector sel0 = {"P12"}; - std::vector sel1 = {}; - BOOST_CHECK_EQUAL_COLLECTIONS(ass0.selector().begin(), ass0.selector().end(), sel0.begin(), sel0.end()); - BOOST_CHECK_EQUAL_COLLECTIONS(ass1.selector().begin(), ass1.selector().end(), sel1.begin(), sel1.end()); + BOOST_CHECK_EQUAL( w2["P12"].value(), 8.0 ); + BOOST_CHECK_EQUAL( w2["P1"].value(), 8.0 ); + BOOST_CHECK_EQUAL( w2["P2"].value(), 8.0 ); } @@ -1144,3 +1144,87 @@ BOOST_AUTO_TEST_CASE(UDA_VALUE_DIM) { value0.set_dim( dim ); BOOST_CHECK_EQUAL( value0.get(), 10); } + + +BOOST_AUTO_TEST_CASE(UDQ_INPUT_BASIC) { + std::string deck_string = R"( +SCHEDULE + +UDQ + ASSIGN WUBHP1 11 / + ASSIGN WUOPR 20 / + ASSIGN WUBHP2 P2 12 / + UNITS WUBHP 'BARSA' / + UNITS WUOPR 'SM3/DAY' / + DEFINE WUWCT WWPR / (WWPR + WOPR) / + UNITS WUWCT '1' / + DEFINE FUOPR SUM(WOPR) / + UNITS FUOPR 'SM3/DAY' / + UNITS FUXXX 'SM3/DAY' / +/ + +UDQ + ASSIGN WUBHPX P2 12 / + DEFINE FUOPRX SUM(WOPR) / +/ + +)"; + auto schedule = make_schedule(deck_string); + const auto& udq = schedule.getUDQConfig(0); + + + const auto& def_input = udq.input_definitions(); + const auto& def = udq.definitions(); + BOOST_CHECK_EQUAL(def_input.size(), 3); + for (std::size_t i = 0; i < def_input.size(); i++) + BOOST_CHECK_EQUAL(def[i].input_string(), def_input[i].second.input_string()); + + BOOST_CHECK_EQUAL(def_input[0].first, 3); + BOOST_CHECK_EQUAL(def_input[1].first, 4); + BOOST_CHECK_EQUAL(def_input[2].first, 6); + + + BOOST_CHECK_EQUAL(def[0].keyword(), "WUWCT"); + BOOST_CHECK_EQUAL(def[1].keyword(), "FUOPR"); + BOOST_CHECK_EQUAL(def[2].keyword(), "FUOPRX"); + + BOOST_CHECK( udq.has_keyword("FUXXX") ); +} + + +BOOST_AUTO_TEST_CASE(UDQ_INPUT_OVERWRITE) { + std::string deck_string = R"( +SCHEDULE + +UDQ + ASSIGN WUBHP1 11 / + ASSIGN WUOPR 20 / + ASSIGN WUBHP2 P2 12 / + UNITS WUBHP 'BARSA' / + UNITS WUOPR 'SM3/DAY' / + DEFINE WUWCT WWPR / (WWPR + WOPR) / + UNITS WUWCT '1' / + DEFINE FUOPR SUM(WOPR) / + UNITS FUOPR 'SM3/DAY' / +/ + +UDQ + DEFINE WUBHP1 SUM(WOPR) / +/ + +)"; + auto schedule = make_schedule(deck_string); + const auto& udq = schedule.getUDQConfig(0); + + + const auto& def_input = udq.input_definitions(); + const auto& def = udq.definitions(); + BOOST_CHECK_EQUAL(def_input.size(), 3); + for (std::size_t i = 0; i < def_input.size(); i++) + BOOST_CHECK_EQUAL(def[i].input_string(), def_input[i].second.input_string()); + + BOOST_CHECK_EQUAL(def_input[0].first, 0); + BOOST_CHECK_EQUAL(def_input[1].first, 3); + BOOST_CHECK_EQUAL(def_input[2].first, 4); +} +