diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index 7d922e567..78c3de8d2 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -108,6 +108,9 @@ if(ENABLE_ECL_INPUT) src/opm/parser/eclipse/EclipseState/Tables/Tables.cpp src/opm/parser/eclipse/EclipseState/Tables/VFPInjTable.cpp src/opm/parser/eclipse/EclipseState/Tables/VFPProdTable.cpp + src/opm/parser/eclipse/EclipseState/UDQConfig.cpp + src/opm/parser/eclipse/EclipseState/Schedule/UDQ.cpp + src/opm/parser/eclipse/EclipseState/Schedule/UDQExpression.cpp src/opm/parser/eclipse/Parser/ParseContext.cpp src/opm/parser/eclipse/Parser/Parser.cpp src/opm/parser/eclipse/Parser/ParserEnums.cpp @@ -209,6 +212,7 @@ if(ENABLE_ECL_INPUT) tests/parser/TimeMapTest.cpp tests/parser/TransMultTests.cpp tests/parser/TuningTests.cpp + tests/parser/UDQTests.cpp tests/parser/UnitTests.cpp tests/parser/ValueTests.cpp tests/parser/WellSolventTests.cpp @@ -444,6 +448,9 @@ if(ENABLE_ECL_INPUT) opm/parser/eclipse/EclipseState/IOConfig/IOConfig.hpp opm/parser/eclipse/EclipseState/checkDeck.hpp opm/parser/eclipse/EclipseState/Runspec.hpp + opm/parser/eclipse/EclipseState/Schedule/UDQ.hpp + opm/parser/eclipse/EclipseState/UDQConfig.hpp + opm/parser/eclipse/EclipseState/Schedule/UDQExpression.hpp opm/parser/eclipse/Deck/DeckItem.hpp opm/parser/eclipse/Deck/Deck.hpp opm/parser/eclipse/Deck/Section.hpp diff --git a/opm/parser/eclipse/EclipseState/Runspec.hpp b/opm/parser/eclipse/EclipseState/Runspec.hpp index e12b09e8c..5aa52af27 100644 --- a/opm/parser/eclipse/EclipseState/Runspec.hpp +++ b/opm/parser/eclipse/EclipseState/Runspec.hpp @@ -24,6 +24,7 @@ #include #include +#include namespace Opm { class Deck; @@ -110,6 +111,7 @@ class Runspec { public: explicit Runspec( const Deck& ); + const UDQConfig& udqConfig() const noexcept; const Phases& phases() const noexcept; const Tabdims& tabdims() const noexcept; const EndpointScaling& endpointScaling() const noexcept; @@ -123,6 +125,7 @@ class Runspec { EndpointScaling endscale; Welldims welldims; WellSegmentDims wsegdims; + UDQConfig udq_config; }; } diff --git a/opm/parser/eclipse/EclipseState/Schedule/UDQ.hpp b/opm/parser/eclipse/EclipseState/Schedule/UDQ.hpp new file mode 100644 index 000000000..dc7b70a1c --- /dev/null +++ b/opm/parser/eclipse/EclipseState/Schedule/UDQ.hpp @@ -0,0 +1,41 @@ +/* + Copyright 2018 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 UDQState_HPP_ +#define UDQState_HPP_ + +#include +#include + + +namespace Opm { + + class UDQ{ + public: + UDQ(const UDQConfig& config, const Deck& deck); + const std::vector& expressions() const noexcept; + private: + std::vector m_expressions; + }; +} + + + +#endif diff --git a/opm/parser/eclipse/EclipseState/Schedule/UDQExpression.hpp b/opm/parser/eclipse/EclipseState/Schedule/UDQExpression.hpp new file mode 100644 index 000000000..30315fcb0 --- /dev/null +++ b/opm/parser/eclipse/EclipseState/Schedule/UDQExpression.hpp @@ -0,0 +1,48 @@ +/* + Copyright 2018 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 UDQ_EXPRESSION_HPP_ +#define UDQ_EXPRESSION_HPP_ + +#include + +#include + + +namespace Opm { + + enum class UDQAction {ASSIGN, DEFINE, UNITS, UPDATE}; + + + class UDQExpression { + public: + UDQExpression(const std::string& action, const std::string& keyword, const std::vector& data); + UDQExpression(const DeckRecord& expression); + const std::vector& tokens() const; + private: + UDQAction action; + std::string keyword; + std::vector data; + }; +} + + + +#endif diff --git a/opm/parser/eclipse/EclipseState/UDQConfig.hpp b/opm/parser/eclipse/EclipseState/UDQConfig.hpp new file mode 100644 index 000000000..66b12bd8f --- /dev/null +++ b/opm/parser/eclipse/EclipseState/UDQConfig.hpp @@ -0,0 +1,49 @@ +/* + Copyright 2018 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 OPM_UDQ_CONFIG_HPP +#define OPM_UDQ_CONFIG_HPP + + +namespace Opm { + + class Deck; + + class UDQConfig + { + public: + explicit UDQConfig(const Deck& deck); + UDQConfig(); + + bool reseedRNG() const noexcept; + int randomSeed() const noexcept; + double range() const noexcept; + double undefinedValue() const noexcept; + double cmpEpsilon() const noexcept; + + private: + bool reseed_rng; + int random_seed; + double value_range; + double undefined_value; + double cmp_eps; + }; +} + +#endif diff --git a/src/opm/parser/eclipse/EclipseState/Runspec.cpp b/src/opm/parser/eclipse/EclipseState/Runspec.cpp index 03ecfd9de..f1824ee8c 100644 --- a/src/opm/parser/eclipse/EclipseState/Runspec.cpp +++ b/src/opm/parser/eclipse/EclipseState/Runspec.cpp @@ -121,7 +121,8 @@ Runspec::Runspec( const Deck& deck ) : m_tabdims( deck ), endscale( deck ), welldims( deck ), - wsegdims( deck ) + wsegdims( deck ), + udq_config( deck ) {} const Phases& Runspec::phases() const noexcept { @@ -156,4 +157,9 @@ int Runspec::eclPhaseMask( ) const noexcept { | ( active_phases.active( Phase::GAS ) ? ECL_GAS_PHASE : 0 ); } + +const UDQConfig& Runspec::udqConfig() const noexcept { + return this->udq_config; +} + } diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/UDQ.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/UDQ.cpp new file mode 100644 index 000000000..ebf6e05f7 --- /dev/null +++ b/src/opm/parser/eclipse/EclipseState/Schedule/UDQ.cpp @@ -0,0 +1,38 @@ +/* + Copyright 2018 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 +#include +#include + +namespace Opm { + + UDQ::UDQ(const UDQConfig& config, const Deck& deck) { + if (deck.hasKeyword("UDQ")) { + const auto& kw = deck.getKeyword("UDQ"); + for (const auto& record : kw) + this->m_expressions.emplace_back( record ); + } + } + + const std::vector& UDQ::expressions() const noexcept { + return this->m_expressions; + } + +} diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/UDQExpression.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/UDQExpression.cpp new file mode 100644 index 000000000..c3907a976 --- /dev/null +++ b/src/opm/parser/eclipse/EclipseState/Schedule/UDQExpression.cpp @@ -0,0 +1,126 @@ +/* + Copyright 2018 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 +#include +#include + +#include +#include +#include + + + + + + +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"; + + if (valid_start.find(keyword[0]) == std::string::npos) + throw std::invalid_argument("Leading character must be in set: " + valid_start); + + if (!(keyword[1] == 'U')) + throw std::invalid_argument("Second character in UDQ keyword must be 'U'"); + } + } + + + + /* + The tokenizer algorithm has two problems with '*': + + 1. When used to specify a wildcard set - like 'P*' for all well starting + with 'P' the tokenizer will interpret the '*' as a multiplication sign + and split into independent tokens 'P' and '*'. + + 2. For items like '2*(1+WBHP)' the parsing code will expand the 2* + operator to the repeated tokens : (1+WBHP), (1+WBHP) + */ + UDQExpression::UDQExpression(const std::string& action, const std::string& keyword, const std::vector& input_data) { + assertKeyword(keyword); + + this->action = actionString2Enum(action); + this->keyword = keyword; + + for (const std::string& item : input_data) { + if (RawConsts::is_quote()(item[0])) { + this->data.push_back(item.substr(1, item.size() - 2)); + continue; + } + + const std::vector splitters = {"TU*[]", "(", ")", "[", "]", ",", "+", "-", "/", "*", "==", "!=", "^", ">=", "<=", ">", "<"}; + size_t offset = 0; + size_t pos = 0; + while (pos < item.size()) { + size_t splitter_index = 0; + while (splitter_index < splitters.size()) { + const std::string& splitter = splitters[splitter_index]; + size_t find_pos = item.find(splitter, pos); + if (find_pos == pos) { + if (pos > offset) + this->data.push_back(item.substr(offset, pos - offset)); + this->data.push_back(splitter); + pos = find_pos + 1; + offset = pos; + break; + } + splitter_index++; + } + if (splitter_index == splitters.size()) + pos += 1; + } + if (pos > offset) + this->data.push_back(item.substr(offset, pos - offset)); + } + } + + UDQExpression::UDQExpression(const DeckRecord& record) : + UDQExpression(record.getItem("ACTION").get(0), + record.getItem("QUANTITY").get(0), + record.getItem("DATA").getData()) + + { + } + + + const std::vector& UDQExpression::tokens() const { + return this->data; + } +} diff --git a/src/opm/parser/eclipse/EclipseState/UDQConfig.cpp b/src/opm/parser/eclipse/EclipseState/UDQConfig.cpp new file mode 100644 index 000000000..b5dcddf43 --- /dev/null +++ b/src/opm/parser/eclipse/EclipseState/UDQConfig.cpp @@ -0,0 +1,79 @@ +/* + Copyright 2018 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 +#include +#include + +namespace Opm { + + /* + The UDQDIMS keyword contains a long list MAX_XXXX items which probably + originate in an ECLIPSE implementation detail. Our implementation does not + require these max values, we therefor ignore them completely. + */ + UDQConfig::UDQConfig() : + reseed_rng(false), + random_seed(ParserKeywords::UDQPARAM::RANDOM_SEED::defaultValue), + value_range(ParserKeywords::UDQPARAM::RANGE::defaultValue), + undefined_value(ParserKeywords::UDQPARAM::UNDEFINED_VALUE::defaultValue), + cmp_eps(ParserKeywords::UDQPARAM::CMP_EPSILON::defaultValue) + {} + + + UDQConfig::UDQConfig(const Deck& deck) : + UDQConfig() + { + if (deck.hasKeyword("UDQDIMS")) { + const auto& record = deck.getKeyword("UDQDIMS").getRecord(0); + const auto& item = record.getItem("RESTART_NEW_SEED"); + const auto& bool_string = item.get(0); + + reseed_rng = (std::toupper(bool_string[0]) == 'Y'); + } + + if (deck.hasKeyword("UDQPARAM")) { + const auto& record = deck.getKeyword("UDQPARAM").getRecord(0); + random_seed = record.getItem("RANDOM_SEED").get(0); + value_range = record.getItem("RANGE").get(0); + undefined_value = record.getItem("UNDEFINED_VALUE").get(0); + cmp_eps = record.getItem("CMP_EPSILON").get(0); + } + } + + + bool UDQConfig::reseedRNG() const noexcept { + return this->reseed_rng; + } + + int UDQConfig::randomSeed() const noexcept { + return this->random_seed; + } + + double UDQConfig::range() const noexcept { + return this->value_range; + } + + double UDQConfig::undefinedValue() const noexcept { + return this->undefined_value; + } + + double UDQConfig::cmpEpsilon() const noexcept { + return this->cmp_eps; + } +} diff --git a/src/opm/parser/eclipse/share/keywords/000_Eclipse100/U/UDQPARAM b/src/opm/parser/eclipse/share/keywords/000_Eclipse100/U/UDQPARAM new file mode 100644 index 000000000..882990065 --- /dev/null +++ b/src/opm/parser/eclipse/share/keywords/000_Eclipse100/U/UDQPARAM @@ -0,0 +1,7 @@ +{"name" : "UDQPARAM" , "sections" : ["RUNSPEC"], +"size" : 1 , +"items" : [ + {"name" : "RANDOM_SEED", "value_type" : "INT" , "default" : 1}, + {"name" : "RANGE", "value_type" : "DOUBLE" , "default" : 1e20}, + {"name" : "UNDEFINED_VALUE", "value_type" : "DOUBLE" , "default" : 0}, + {"name" : "CMP_EPSILON", "value_type" : "DOUBLE" , "default" : 1e-4}]} diff --git a/src/opm/parser/eclipse/share/keywords/keyword_list.cmake b/src/opm/parser/eclipse/share/keywords/keyword_list.cmake index 67ac0a2ec..4e5be0338 100644 --- a/src/opm/parser/eclipse/share/keywords/keyword_list.cmake +++ b/src/opm/parser/eclipse/share/keywords/keyword_list.cmake @@ -337,7 +337,9 @@ set( keywords 000_Eclipse100/T/TVDP 000_Eclipse100/U/UDQ 000_Eclipse100/U/UDADIMS + 000_Eclipse100/U/UDQ 000_Eclipse100/U/UDQDIMS + 000_Eclipse100/U/UDQPARAM 000_Eclipse100/U/UNIFIN 000_Eclipse100/U/UNIFOUT 000_Eclipse100/V/VAPOIL diff --git a/tests/parser/UDQTests.cpp b/tests/parser/UDQTests.cpp new file mode 100644 index 000000000..120464385 --- /dev/null +++ b/tests/parser/UDQTests.cpp @@ -0,0 +1,127 @@ +/* + Copyright 2018 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 . +*/ + +#define BOOST_TEST_MODULE UDQTests +#include + +#include +#include +#include +#include +#include + +using namespace Opm; + +BOOST_AUTO_TEST_CASE(KEYWORDS) { + const std::string input = R"( +RUNSPEC + +UDQDIMS + 10* 'Y'/ + +UDQPARAM + 3* 0.25 / + +)"; + Parser parser; + ParseContext parseContext; + + auto deck = parser.parseString(input, parseContext); + auto runspec = Runspec(deck); + auto udq_config = runspec.udqConfig(); + + BOOST_CHECK(udq_config.reseedRNG()); + BOOST_CHECK_EQUAL(0.25, udq_config.cmpEpsilon()); +} + + +BOOST_AUTO_TEST_CASE(UDQ_KEWYORDS) { + 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/ +/ +)"; + + Parser parser; + ParseContext parseContext; + + auto deck = parser.parseString(input, parseContext); + auto udq_config = UDQConfig(deck); + auto udq = UDQ(udq_config, deck); + BOOST_CHECK_EQUAL(3, udq.expressions().size()); +} + +BOOST_AUTO_TEST_CASE(UDQ_KEYWORD) { + // Invalid action + BOOST_REQUIRE_THROW( UDQExpression("INVALID_ACTION", "WUBHP" , {"DATA1" ,"1"}), std::invalid_argument); + + // Invalid keyword + BOOST_REQUIRE_THROW( UDQExpression("ASSIGN", "INVALID_KEYWORD", {}), std::invalid_argument); + + BOOST_CHECK_NO_THROW(UDQExpression("ASSIGN" ,"WUBHP", {"1"})); +} + + +BOOST_AUTO_TEST_CASE(UDQ_DATA) { + const std::string input = R"( +RUNSPEC + +UDQDIMS + 10* 'Y'/ + +UDQPARAM + 3* 0.25 / + +SCHEDULE + +UDQ +ASSIGN CUMW1 P12 10 12 1 (4.0 + 6*(4 - 2)) / +DEFINE WUMW1 WBHP 'P*1*' UMAX WBHP 'P*4*' / +/ + + +)"; + Parser parser; + ParseContext parseContext; + + auto deck = parser.parseString(input, parseContext); + auto udq_config = UDQConfig(deck); + auto udq = UDQ(udq_config, deck); + const auto& records = udq.expressions(); + const auto& rec0 = records[0]; + const auto& rec1 = records[1]; + const std::vector exp0 = {"P12", "10", "12", "1", "(", "4.0", "+", "6", "*", "(", "4", "-", "2", ")", ")"}; + 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()); +} + +