diff --git a/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp b/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp index bfadd262c..8238875ce 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp @@ -248,7 +248,7 @@ namespace Opm 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 , const UnitSystem& unit_system); + void handleWELSPECS( const SCHEDULESection&, size_t, size_t , const UnitSystem& unit_system, const ParseContext& parseContext, ErrorGuard& errors); void handleWCONHIST( const DeckKeyword& keyword, size_t currentStep, const ParseContext& parseContext, ErrorGuard& errors); void handleWCONPROD( const DeckKeyword& keyword, size_t currentStep, const ParseContext& parseContext, ErrorGuard& errors); void handleWGRUPCON( const DeckKeyword& keyword, size_t currentStep); @@ -273,7 +273,7 @@ namespace Opm void handleGEFAC( const DeckKeyword& keyword, size_t currentStep, const ParseContext& parseContext, ErrorGuard& errors); void handleWEFAC( const DeckKeyword& keyword, size_t currentStep, const ParseContext& parseContext, ErrorGuard& errors); void handleTUNING( const DeckKeyword& keyword, size_t currentStep); - void handleGRUPTREE( const DeckKeyword& keyword, size_t currentStep, const UnitSystem& unit_system); + void handleGRUPTREE( const DeckKeyword& keyword, size_t currentStep, const UnitSystem& unit_system, const ParseContext& parseContext, ErrorGuard& errors); void handleGRUPNET( const DeckKeyword& keyword, size_t currentStep, const UnitSystem& unit_system); void handleWRFT( const DeckKeyword& keyword, size_t currentStep); void handleWTEST( const DeckKeyword& keyword, size_t currentStep, const ParseContext& parseContext, ErrorGuard& errors); diff --git a/opm/parser/eclipse/Parser/ParseContext.hpp b/opm/parser/eclipse/Parser/ParseContext.hpp index 010c0348e..15a78c5a7 100644 --- a/opm/parser/eclipse/Parser/ParseContext.hpp +++ b/opm/parser/eclipse/Parser/ParseContext.hpp @@ -271,6 +271,14 @@ namespace Opm { */ const static std::string PARSE_MISSING_SECTIONS; + /* + When defining wells and groups with the WELSPECS and GRUPTREE keywords + we do not allow leading or trailing spaces. The code in Schedule.cpp + will *unconditionally* remove the spaces, but with PARSE_WGNAME_SPACE + setting you can additionally configure the normal IGNORE|WARN|ERROR + behavior. + */ + const static std::string PARSE_WGNAME_SPACE; /* If you have configured a specific well in the summary section, diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp index ba89f8323..a9909369f 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp @@ -75,6 +75,30 @@ namespace { return (fnmatch(pattern.c_str(), name.c_str(), flags) == 0); } + + /* + The function trim_wgname() is used to trim the leading and trailing spaces + away from the group and well arguments given in the WELSPECS and GRUPTREE + keywords. If the deck argument contains a leading or trailing space that is + treated as an input error, and the action taken is regulated by the setting + ParseContext::PARSE_WGNAME_SPACE. + + Observe that the spaces are trimmed *unconditionally* - i.e. if the + ParseContext::PARSE_WGNAME_SPACE setting is set to InputError::IGNORE that + means that we do not inform the user about "our fix", but it is *not* possible + to configure the parser to leave the spaces intact. + */ + + std::string trim_wgname(const DeckKeyword& keyword, const std::string& wgname_arg, const ParseContext& parseContext, ErrorGuard errors) { + std::string wgname = boost::algorithm::trim_copy(wgname_arg); + if (wgname != wgname_arg) { + std::string msg = "Illegal space: \"" + wgname_arg + "\" found when defining WELL/GROUP in keyword: " + keyword.name() + " at " + keyword.getFileName() + ":" + std::to_string(keyword.getLineNumber()); + parseContext.handleError(ParseContext::PARSE_WGNAME_SPACE, msg, errors); + } + return wgname; + } + + } Schedule::Schedule( const Deck& deck, @@ -228,7 +252,7 @@ namespace { handleWLIST( keyword, currentStep ); else if (keyword.name() == "WELSPECS") - handleWELSPECS( section, keywordIdx, currentStep, unit_system); + handleWELSPECS( section, keywordIdx, currentStep, unit_system, parseContext, errors); else if (keyword.name() == "WHISTCTL") handleWHISTCTL(keyword, currentStep, parseContext, errors); @@ -291,7 +315,7 @@ namespace { handleWELTARG(section, keyword, currentStep, parseContext, errors); else if (keyword.name() == "GRUPTREE") - handleGRUPTREE(keyword, currentStep, unit_system); + handleGRUPTREE(keyword, currentStep, unit_system, parseContext, errors); else if (keyword.name() == "GRUPNET") handleGRUPNET(keyword, currentStep, unit_system); @@ -527,7 +551,9 @@ namespace { void Schedule::handleWELSPECS( const SCHEDULESection& section, size_t index, size_t currentStep, - const UnitSystem& unit_system) { + const UnitSystem& unit_system, + const ParseContext& parseContext, + ErrorGuard& errors) { const auto COMPORD_in_timestep = [&]() -> const DeckKeyword* { auto itr = section.begin() + index; for( ; itr != section.end(); ++itr ) { @@ -543,8 +569,8 @@ namespace { for (size_t recordNr = 0; recordNr < keyword.size(); recordNr++) { const auto& record = keyword.getRecord(recordNr); - const std::string& wellName = record.getItem("WELL").getTrimmedString(0); - const std::string& groupName = record.getItem("GROUP").getTrimmedString(0); + const std::string& wellName = trim_wgname(keyword, record.getItem("WELL").get(0), parseContext, errors); + const std::string& groupName = trim_wgname(keyword, record.getItem("GROUP").get(0), parseContext, errors); if (!hasGroup(groupName)) addGroup(groupName , currentStep, unit_system); @@ -1761,10 +1787,10 @@ namespace { } } - void Schedule::handleGRUPTREE( const DeckKeyword& keyword, size_t currentStep, const UnitSystem& unit_system) { +void Schedule::handleGRUPTREE( const DeckKeyword& keyword, size_t currentStep, const UnitSystem& unit_system, const ParseContext& parseContext, ErrorGuard& errors) { for( const auto& record : keyword ) { - const std::string& childName = record.getItem("CHILD_GROUP").getTrimmedString(0); - const std::string& parentName = record.getItem("PARENT_GROUP").getTrimmedString(0); + const std::string& childName = trim_wgname(keyword, record.getItem("CHILD_GROUP").get(0), parseContext, errors); + const std::string& parentName = trim_wgname(keyword, record.getItem("PARENT_GROUP").get(0), parseContext, errors); if (!hasGroup(parentName)) addGroup( parentName , currentStep, unit_system ); diff --git a/src/opm/parser/eclipse/Parser/ParseContext.cpp b/src/opm/parser/eclipse/Parser/ParseContext.cpp index 56f8f2919..aaaa309d6 100644 --- a/src/opm/parser/eclipse/Parser/ParseContext.cpp +++ b/src/opm/parser/eclipse/Parser/ParseContext.cpp @@ -78,6 +78,7 @@ namespace Opm { addKey(PARSE_EXTRA_DATA, InputError::THROW_EXCEPTION); addKey(PARSE_MISSING_INCLUDE, InputError::EXIT1); addKey(PARSE_LONG_KEYWORD, InputError::WARN); + addKey(PARSE_WGNAME_SPACE, InputError::THROW_EXCEPTION); addKey(UNIT_SYSTEM_MISMATCH, InputError::THROW_EXCEPTION); @@ -315,6 +316,7 @@ namespace Opm { const std::string ParseContext::PARSE_MISSING_SECTIONS = "PARSE_MISSING_SECTIONS"; const std::string ParseContext::PARSE_MISSING_INCLUDE = "PARSE_MISSING_INCLUDE"; const std::string ParseContext::PARSE_LONG_KEYWORD = "PARSE_LONG_KEYWORD"; + const std::string ParseContext::PARSE_WGNAME_SPACE = "PARSE_WGNAME_SPACE"; const std::string ParseContext::UNIT_SYSTEM_MISMATCH = "UNIT_SYSTEM_MISMATCH"; diff --git a/tests/SPE9_CP_PACKED.DATA b/tests/SPE9_CP_PACKED.DATA index dd2045d7c..b6eb28369 100644 --- a/tests/SPE9_CP_PACKED.DATA +++ b/tests/SPE9_CP_PACKED.DATA @@ -6665,15 +6665,15 @@ TUNING WELSPECS - 'INJE1 ' 'P' 24 25 9110 'WATER' 60 / - 'PRODU2 ' 'P' 5 1 9110 'OIL' 60 / - 'PRODU3 ' 'P' 8 2 9110 'OIL' 60 / - 'PRODU4 ' 'P' 11 3 9110 'OIL' 60 / - 'PRODU5 ' 'P' 10 4 9110 'OIL' 60 / - 'PRODU6 ' 'P' 12 5 9110 'OIL' 60 / - 'PRODU7 ' 'P' 4 6 9110 'OIL' 60 / - 'PRODU8 ' 'P' 8 7 9110 'OIL' 60 / - 'PRODU9 ' 'P' 14 8 9110 'OIL' 60 / + 'INJE1' 'P' 24 25 9110 'WATER' 60 / + 'PRODU2' 'P' 5 1 9110 'OIL' 60 / + 'PRODU3' 'P' 8 2 9110 'OIL' 60 / + 'PRODU4' 'P' 11 3 9110 'OIL' 60 / + 'PRODU5' 'P' 10 4 9110 'OIL' 60 / + 'PRODU6' 'P' 12 5 9110 'OIL' 60 / + 'PRODU7' 'P' 4 6 9110 'OIL' 60 / + 'PRODU8' 'P' 8 7 9110 'OIL' 60 / + 'PRODU9' 'P' 14 8 9110 'OIL' 60 / 'PRODU10' 'P' 11 9 9110 'OIL' 60 / 'PRODU11' 'P' 12 10 9110 'OIL' 60 / 'PRODU12' 'P' 10 11 9110 'OIL' 60 / diff --git a/tests/parser/ScheduleTests.cpp b/tests/parser/ScheduleTests.cpp index fd7250241..45c30e3f8 100644 --- a/tests/parser/ScheduleTests.cpp +++ b/tests/parser/ScheduleTests.cpp @@ -1094,6 +1094,58 @@ BOOST_AUTO_TEST_CASE(createDeckWithWPIMULT) { BOOST_CHECK_EQUAL(year, 2011); } +BOOST_AUTO_TEST_CASE(WELSPECS_WGNAME_SPACE) { + Opm::Parser parser; + const std::string input = R"( + START -- 0 + 10 'JAN' 2000 / + RUNSPEC + DIMENS + 10 10 10 / + GRID + DX + 1000*0.25 / + DY + 1000*0.25 / + DZ + 1000*0.25 / + TOPS + 100*0.25 / + SCHEDULE + DATES -- 1 + 10 OKT 2008 / + / + WELSPECS + ' PROD1' 'G1' 1 1 10 'OIL' / + 'PROD2' 'G2' 2 2 10 'OIL' / + 'PROD3' 'H1' 3 3 10 'OIL' / + / + GCONPROD + 'G1' 'ORAT' 1000 / + / + DATES -- 2 + 10 NOV 2008 / + / + GCONPROD + 'G*' 'ORAT' 2000 / + / + )"; + + auto deck = parser.parseString(input); + EclipseGrid grid( deck ); + TableManager table ( deck ); + Eclipse3DProperties eclipseProperties ( deck , table, grid); + Runspec runspec (deck); + ParseContext parseContext; + ErrorGuard errors; + + parseContext.update(ParseContext::PARSE_WGNAME_SPACE, InputError::THROW_EXCEPTION); + BOOST_CHECK_THROW( Opm::Schedule(deck, grid, eclipseProperties, runspec, parseContext, errors), std::invalid_argument); + + parseContext.update(ParseContext::PARSE_WGNAME_SPACE, InputError::IGNORE); + BOOST_CHECK_NO_THROW( Opm::Schedule(deck, grid, eclipseProperties, runspec, parseContext, errors)); +} + BOOST_AUTO_TEST_CASE(createDeckModifyMultipleGCONPROD) { Opm::Parser parser; const std::string input = R"( diff --git a/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_WELLS_AND_GROUPS b/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_WELLS_AND_GROUPS index da0c580e8..8a5dc3836 100644 --- a/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_WELLS_AND_GROUPS +++ b/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_WELLS_AND_GROUPS @@ -21,8 +21,7 @@ SCHEDULE WELSPECS 'W_1' 'GROUP1' 30 37 1* 'OIL' 7* / --- the spaces around the names are intentional! - ' W_2 ' ' GROUP1 ' 20 51 1* 'OIL' 7* / + 'W_2' 'GROUP1' 20 51 1* 'OIL' 7* / / TSTEP diff --git a/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_WELSPECS_GRUPTREE b/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_WELSPECS_GRUPTREE index e6993be27..bd60f5ef1 100644 --- a/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_WELSPECS_GRUPTREE +++ b/tests/parser/data/integration_tests/SCHEDULE/SCHEDULE_WELSPECS_GRUPTREE @@ -23,8 +23,7 @@ SCHEDULE GRUPTREE 'INJE' 'FIELD' / --- the spaces around the names are intentional! - ' PROD ' ' FIELD ' / + 'PROD' 'FIELD' / 'MANI-PROD' 'PROD' / 'MANI-INJ' 'INJE' / 'DUMMY-PROD' 'MANI-PROD' /