Merge pull request #2101 from joakim-hove/wpave

Internalize WPAVE and WWPAVE keywords
This commit is contained in:
Joakim Hove 2020-11-13 07:00:14 +01:00 committed by GitHub
commit 5c30077cd4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 514 additions and 3 deletions

View File

@ -132,6 +132,7 @@ if(ENABLE_ECL_INPUT)
src/opm/parser/eclipse/EclipseState/Schedule/TimeMap.cpp
src/opm/parser/eclipse/EclipseState/Schedule/Well/Connection.cpp
src/opm/parser/eclipse/EclipseState/Schedule/Well/injection.cpp
src/opm/parser/eclipse/EclipseState/Schedule/Well/PAvg.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
@ -351,6 +352,7 @@ if(ENABLE_ECL_INPUT)
tests/parser/ParseContextTests.cpp
tests/parser/ParseContext_EXIT1.cpp
tests/parser/ParseDATAWithDefault.cpp
tests/parser/PAvgTests.cpp
tests/parser/PYACTION.cpp
tests/parser/RawKeywordTests.cpp
tests/parser/test_ReportConfig.cpp
@ -707,6 +709,7 @@ if(ENABLE_ECL_INPUT)
opm/parser/eclipse/EclipseState/Schedule/Well/Connection.hpp
opm/parser/eclipse/EclipseState/Schedule/Well/ProductionControls.hpp
opm/parser/eclipse/EclipseState/Schedule/Well/InjectionControls.hpp
opm/parser/eclipse/EclipseState/Schedule/Well/PAvg.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

View File

@ -42,6 +42,7 @@
#include <opm/parser/eclipse/EclipseState/Schedule/VFPInjTable.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/VFPProdTable.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Network/ExtNetwork.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/PAvg.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/Well.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellTestConfig.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellMatcher.hpp>
@ -325,6 +326,7 @@ namespace Opm
m_actions.serializeOp(serializer);
m_network.serializeOp(serializer);
m_glo.serializeOp(serializer);
m_pavg.serializeOp(serializer);
rft_config.serializeOp(serializer);
m_nupcol.template serializeOp<Serializer, false>(serializer);
restart_config.serializeOp(serializer);
@ -363,6 +365,7 @@ namespace Opm
DynamicState<std::shared_ptr<Action::Actions>> m_actions;
DynamicState<std::shared_ptr<Network::ExtNetwork>> m_network;
DynamicState<std::shared_ptr<GasLiftOpt>> m_glo;
DynamicState<std::shared_ptr<PAvg>> m_pavg;
RFTConfig rft_config;
DynamicState<int> m_nupcol;
RestartConfig restart_config;
@ -387,6 +390,7 @@ namespace Opm
Well::GasInflowEquation gas_inflow,
std::size_t timeStep,
Connection::Order wellConnectionOrder);
bool updateWPAVE(const std::string& wname, std::size_t report_step, const PAvg& pavg);
DynamicState<std::shared_ptr<RPTConfig>> rpt_config;
void updateNetwork(std::shared_ptr<Network::ExtNetwork> network, std::size_t report_step);
@ -549,6 +553,8 @@ namespace Opm
void handleWINJTEMP (const HandlerContext&, const ParseContext&, ErrorGuard&);
void handleWLIFTOPT (const HandlerContext&, const ParseContext&, ErrorGuard&);
void handleWLIST (const HandlerContext&, const ParseContext&, ErrorGuard&);
void handleWPAVE (const HandlerContext&, const ParseContext&, ErrorGuard&);
void handleWWPAVE (const HandlerContext&, const ParseContext&, ErrorGuard&);
void handleWPIMULT (const HandlerContext&, const ParseContext&, ErrorGuard&);
void handleWPMITAB (const HandlerContext&, const ParseContext&, ErrorGuard&);
void handleWPOLYMER (const HandlerContext&, const ParseContext&, ErrorGuard&);

View File

@ -0,0 +1,68 @@
/*
Copyright 2020 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 <http://www.gnu.org/licenses/>.
*/
#ifndef PAVE_HPP
#define PAVE_HPP
namespace Opm {
class DeckRecord;
class PAvg {
public:
enum class DepthCorrection {
WELL = 1,
RES = 2,
NONE = 3
};
PAvg();
explicit PAvg(const DeckRecord& record);
PAvg(double inner_weight, double conn_weight, DepthCorrection depth_correction, bool use_open_connections);
double inner_weight() const;
double conn_weight() const;
bool use_porv() const;
bool open_connections() const;
DepthCorrection depth_correction() const;
template<class Serializer>
void serializeOp(Serializer& serializer) {
serializer(m_inner_weight);
serializer(m_conn_weight);
serializer(m_depth_correction);
serializer(m_open_connections);
}
static PAvg serializeObject();
bool operator==(const PAvg& other) const;
bool operator!=(const PAvg& other) const;
private:
double m_inner_weight;
double m_conn_weight;
DepthCorrection m_depth_correction;
bool m_open_connections;
};
}
#endif

View File

@ -37,6 +37,7 @@
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellConnections.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/MSW/WellSegments.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/ScheduleTypes.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/PAvg.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/ProductionControls.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/InjectionControls.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellFoamProperties.hpp>
@ -558,6 +559,7 @@ public:
bool updateWellProductivityIndex(const double prodIndex);
bool updateWSEGSICD(const std::vector<std::pair<int, SICD> >& sicd_pairs);
bool updateWSEGVALV(const std::vector<std::pair<int, Valve> >& valve_pairs);
bool updateWPAVE(const PAvg& pavg);
bool handleWELSEGS(const DeckKeyword& keyword);
bool handleCOMPSEGS(const DeckKeyword& keyword, const EclipseGrid& grid, const ParseContext& parseContext, ErrorGuard& errors);
@ -588,6 +590,7 @@ public:
double getWellPIScalingFactor(const double currentEffectivePI) const;
void applyWellProdIndexScaling(const double scalingFactor,
std::vector<bool>& scalingApplicable);
const PAvg& pavg() const;
template<class Serializer>
void serializeOp(Serializer& serializer)
@ -624,6 +627,7 @@ public:
serializer(production);
serializer(injection);
serializer(segments);
m_pavg.serializeOp(serializer);
}
private:
@ -663,6 +667,7 @@ private:
std::shared_ptr<WellProductionProperties> production;
std::shared_ptr<WellInjectionProperties> injection;
std::shared_ptr<WellSegments> segments;
PAvg m_pavg;
};
std::ostream& operator<<( std::ostream&, const Well::WellInjectionProperties& );

View File

@ -1755,6 +1755,27 @@ namespace {
}
}
void Schedule::handleWPAVE(const HandlerContext& handlerContext, const ParseContext&, ErrorGuard&) {
auto wpave = std::make_shared<PAvg>( handlerContext.keyword.getRecord(0) );
for (const auto& wname : this->wellNames(handlerContext.currentStep))
this->updateWPAVE(wname, handlerContext.currentStep, *wpave );
this->m_pavg.update( handlerContext.currentStep, std::move(wpave) );
}
void Schedule::handleWWPAVE(const HandlerContext& handlerContext, const ParseContext& parseContext, ErrorGuard& errors) {
for (const auto& record : handlerContext.keyword) {
const std::string& wellNamePattern = record.getItem("WELL").getTrimmedString(0);
const auto well_names = wellNames(wellNamePattern, handlerContext.currentStep);
if (well_names.empty())
invalidNamePattern(wellNamePattern, handlerContext.currentStep, parseContext, errors, handlerContext.keyword);
auto wpave = PAvg(record);
for (const auto& well_name : well_names)
this->updateWPAVE(well_name, handlerContext.currentStep, wpave);
}
}
bool Schedule::handleNormalKeyword(const HandlerContext& handlerContext, const ParseContext& parseContext, ErrorGuard& errors) {
using handler_function = void (Schedule::*)(const HandlerContext&, const ParseContext&, ErrorGuard&);
@ -1821,6 +1842,8 @@ namespace {
{ "WINJTEMP", &Schedule::handleWINJTEMP },
{ "WLIFTOPT", &Schedule::handleWLIFTOPT },
{ "WLIST" , &Schedule::handleWLIST },
{ "WPAVE" , &Schedule::handleWPAVE },
{ "WWPAVE" , &Schedule::handleWWPAVE },
{ "WPIMULT" , &Schedule::handleWPIMULT },
{ "WPMITAB" , &Schedule::handleWPMITAB },
{ "WPOLYMER", &Schedule::handleWPOLYMER },

View File

@ -127,6 +127,7 @@ namespace {
m_actions(this->m_timeMap, std::make_shared<Action::Actions>()),
m_network(this->m_timeMap, std::make_shared<Network::ExtNetwork>()),
m_glo(this->m_timeMap, std::make_shared<GasLiftOpt>()),
m_pavg(this->m_timeMap, std::make_shared<PAvg>()),
rft_config(this->m_timeMap),
m_nupcol(this->m_timeMap, runspec.nupcol()),
restart_config(m_timeMap, deck, parseContext, errors),
@ -608,6 +609,20 @@ private:
return update;
}
bool Schedule::updateWPAVE(const std::string& wname, std::size_t report_step, const PAvg& pavg) {
const auto& well = this->getWell(wname, report_step);
if (well.pavg() != pavg) {
auto& dynamic_state = this->wells_static.at(wname);
auto new_well = std::make_shared<Well>(*dynamic_state[report_step]);
new_well->updateWPAVE( pavg );
this->updateWell(new_well, report_step);
return true;
}
return false;
}
/*
This routine is called when UDQ keywords is added in an ACTIONX block.
*/
@ -891,6 +906,9 @@ private:
gas_inflow);
this->addWell( std::move(well), timeStep );
const auto& pavg_ptr = this->m_pavg.get(timeStep);
this->updateWPAVE( wellName, timeStep, *pavg_ptr );
}

View File

@ -0,0 +1,133 @@
/*
Copyright 2020 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 <http://www.gnu.org/licenses/>.
*/
#include <fmt/format.h>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/PAvg.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/W.hpp>
#include <opm/parser/eclipse/Deck/DeckRecord.hpp>
namespace Opm {
namespace {
PAvg::DepthCorrection depthCorrectionFromString(const std::string& s) {
if (s == "WELL")
return PAvg::DepthCorrection::WELL;
if (s == "RES")
return PAvg::DepthCorrection::RES;
if (s == "NONE")
return PAvg::DepthCorrection::NONE;
throw std::invalid_argument(fmt::format("{} not recognized as depth correction mode", s));
}
bool openConnectionsFromString(const std::string& s) {
if (s == "OPEN")
return true;
if (s == "ALL")
return false;
throw std::invalid_argument(fmt::format("{} not recognized as connection indicator", s));
}
}
PAvg::PAvg() :
m_inner_weight(ParserKeywords::WPAVE::F1::defaultValue),
m_conn_weight(ParserKeywords::WPAVE::F2::defaultValue)
{
m_depth_correction = depthCorrectionFromString( ParserKeywords::WPAVE::DEPTH_CORRECTION::defaultValue );
m_open_connections = openConnectionsFromString( ParserKeywords::WPAVE::CONNECTION::defaultValue );
}
PAvg::PAvg(double inner_weight, double conn_weight, DepthCorrection depth_correction, bool use_open_connections) :
m_inner_weight(inner_weight),
m_conn_weight(conn_weight),
m_depth_correction(depth_correction),
m_open_connections(use_open_connections)
{}
static PAvg serializeObject() {
return PAvg(0.10, 0.30, PAvg::DepthCorrection::NONE, false);
}
PAvg::PAvg(const DeckRecord& record)
: PAvg()
{
/*
This code uses the WPAVE keyword to access the content of the the record,
but the record can equally well come from a WWPAVE keyword - i.e. it is a
HARD assumption that the same item names is used both for WPAVE and
WWPAVE.
*/
using WPAVE = ParserKeywords::WPAVE;
const auto& item_inner_weight = record.getItem<WPAVE::F1>();
const auto& item_conn_weight = record.getItem<WPAVE::F2>();
const auto& item_depth_correction = record.getItem<WPAVE::DEPTH_CORRECTION>();
const auto& item_connections = record.getItem<WPAVE::CONNECTION>();
this->m_inner_weight = item_inner_weight.get<double>(0);
this->m_conn_weight = item_conn_weight.get<double>(0);
if (!item_depth_correction.defaultApplied(0))
this->m_depth_correction = depthCorrectionFromString( item_depth_correction.get<std::string>(0) );
if (!item_connections.defaultApplied(0))
this->m_open_connections = openConnectionsFromString( item_connections.get<std::string>(0) );
}
double PAvg::inner_weight() const {
return this->m_inner_weight;
}
double PAvg::conn_weight() const {
return this->m_conn_weight;
}
PAvg::DepthCorrection PAvg::depth_correction() const {
return this->m_depth_correction;
}
bool PAvg::open_connections() const {
return this->m_open_connections;
}
bool PAvg::use_porv() const {
return this->m_conn_weight != 1.0;
}
bool PAvg::operator==(const PAvg& other) const {
return this->m_inner_weight == other.m_inner_weight &&
this->m_conn_weight == other.m_conn_weight &&
this->m_depth_correction == other.m_depth_correction &&
this->m_open_connections == other.m_open_connections;
}
bool PAvg::operator!=(const PAvg& other) const {
return !(*this == other);
}
}

View File

@ -387,10 +387,21 @@ Well Well::serializeObject()
result.production = std::make_shared<Well::WellProductionProperties>(Well::WellProductionProperties::serializeObject());
result.injection = std::make_shared<Well::WellInjectionProperties>(Well::WellInjectionProperties::serializeObject());
result.segments = std::make_shared<WellSegments>(WellSegments::serializeObject());
result.m_pavg = PAvg();
return result;
}
bool Well::updateWPAVE(const PAvg& pavg) {
if (this->m_pavg == pavg)
return false;
this->m_pavg = pavg;
return true;
}
bool Well::updateEfficiencyFactor(double efficiency_factor_arg) {
if (this->efficiency_factor != efficiency_factor_arg) {
this->efficiency_factor = efficiency_factor_arg;
@ -947,6 +958,10 @@ Well::Status Well::getStatus() const {
return this->status;
}
const PAvg& Well::pavg() const {
return this->m_pavg;
}
std::map<int, std::vector<Connection>> Well::getCompletions() const {
std::map<int, std::vector<Connection>> completions;
@ -1614,6 +1629,7 @@ bool Well::operator==(const Well& data) const {
this->productivity_index == data.productivity_index &&
this->getTracerProperties() == data.getTracerProperties() &&
this->getProductionProperties() == data.getProductionProperties() &&
this->m_pavg == data.m_pavg &&
this->getInjectionProperties() == data.getInjectionProperties();
}
@ -1694,3 +1710,4 @@ int Opm::eclipseControlMode(const Well& well,
return eclipseControlMode(ctrl.cmode, well.injectorType());
}
}

View File

@ -6,12 +6,12 @@
"size": 1,
"items": [
{
"name": "WEIGTH_FACTOR1",
"name": "F1",
"value_type": "DOUBLE",
"default": 0.5
},
{
"name": "WEIGTH_FACTOR2",
"name": "F2",
"value_type": "DOUBLE",
"default": 1
},

View File

@ -24,7 +24,7 @@
"default": "WELL"
},
{
"name": "WELL_CONNECTION",
"name": "CONNECTION",
"value_type": "STRING",
"default": "OPEN"
}

View File

@ -0,0 +1,96 @@
/*
Copyright 2020 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 <http://www.gnu.org/licenses/>.
*/
#define BOOST_TEST_MODULE PAvgTests
#include <exception>
#include <boost/test/unit_test.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/PAvg.hpp>
#include <opm/parser/eclipse/Parser/Parser.hpp>
#include <opm/parser/eclipse/Deck/Deck.hpp>
using namespace Opm;
BOOST_AUTO_TEST_CASE(DEFAULT_PAVG) {
PAvg pavg;
BOOST_CHECK_EQUAL(pavg.inner_weight(), 0.50);
BOOST_CHECK_EQUAL(pavg.conn_weight(), 1.00);
BOOST_CHECK_EQUAL(pavg.use_porv(), false);
BOOST_CHECK(pavg.depth_correction() == PAvg::DepthCorrection::WELL);
BOOST_CHECK(pavg.open_connections());
}
void invalid_deck(const std::string& deck_string, const std::string& kw) {
Parser parser;
auto deck = parser.parseString(deck_string);
BOOST_CHECK_THROW( PAvg(deck.getKeyword(kw).getRecord(0)), std::exception );
}
void valid_deck(const std::string& deck_string, const std::string& kw) {
Parser parser;
auto deck = parser.parseString(deck_string);
BOOST_CHECK_NO_THROW( PAvg(deck.getKeyword(kw).getRecord(0)));
}
BOOST_AUTO_TEST_CASE(PAVG_FROM_DECK) {
std::string invalid_deck1 = R"(
WPAVE
2* Well /
WWPAVE
W 2* Well /
/
)";
std::string invalid_deck2 = R"(
WPAVE
2* WELL all /
WWPAVE
W 2* WELL all /
/
)";
std::string valid_input = R"(
WPAVE
0.25 0.50 WELL ALL /
WWPAVE
W 2* WELL ALL /
/
)";
invalid_deck(invalid_deck1, "WPAVE");
invalid_deck(invalid_deck1, "WWPAVE");
invalid_deck(invalid_deck2, "WPAVE");
invalid_deck(invalid_deck2, "WWPAVE");
valid_deck(valid_input, "WPAVE");
valid_deck(valid_input, "WWPAVE");
Parser parser;
PAvg pavg( parser.parseString(valid_input).getKeyword("WPAVE").getRecord(0) );
BOOST_CHECK_EQUAL( pavg.inner_weight(), 0.25);
BOOST_CHECK_EQUAL( pavg.conn_weight(), 0.5);
BOOST_CHECK( pavg.use_porv() );
}

View File

@ -42,6 +42,7 @@
#include <opm/parser/eclipse/EclipseState/Schedule/GasLiftOpt.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/SummaryState.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellMatcher.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/PAvg.hpp>
#include <opm/parser/eclipse/Deck/Deck.hpp>
#include <opm/parser/eclipse/Deck/DeckItem.hpp>
@ -4231,3 +4232,122 @@ BOOST_AUTO_TEST_CASE(VFPPROD_SCALING) {
cmp_vector(gfr, vfp_table.getGFRAxis());
cmp_vector(alq, vfp_table.getALQAxis());
}
BOOST_AUTO_TEST_CASE(WPAVE) {
const std::string deck_string = R"(
START
7 OCT 2020 /
DIMENS
10 10 3 /
GRID
DXV
10*100.0 /
DYV
10*100.0 /
DZV
3*10.0 /
DEPTHZ
121*2000.0 /
PORO
300*0.3 /
SCHEDULE
WELSPECS -- 0
'P1' 'G' 10 10 2005 'LIQ' /
'P2' 'G' 1 10 2005 'LIQ' /
'P3' 'G' 2 10 2005 'LIQ' /
'P4' 'G' 3 10 2005 'LIQ' /
/
TSTEP -- 1
10
/
WPAVE -- PAVG1
0.75 0.25 /
TSTEP -- 2
10
/
WWPAVE
P1 0.30 0.60 / -- PAVG2
P3 0.40 0.70 / -- PAVG3
/
TSTEP -- 3
10
/
WPAVE -- PAVG4
0.10 0.10 /
TSTEP -- 4
10
/
TSTEP -- 5
10
/
END
)";
const auto deck = Parser{}.parseString(deck_string);
const auto es = EclipseState{ deck };
auto sched = Schedule{ deck, es };
PAvg pavg0;
PAvg pavg1( deck.getKeyword("WPAVE", 0).getRecord(0) );
PAvg pavg2( deck.getKeyword("WWPAVE", 0).getRecord(0) );
PAvg pavg3( deck.getKeyword("WWPAVE", 0).getRecord(1) );
PAvg pavg4( deck.getKeyword("WPAVE", 1).getRecord(0) );
{
const auto& w1 = sched.getWell("P1", 0);
const auto& w4 = sched.getWell("P4", 0);
BOOST_CHECK(w1.pavg() == pavg0);
BOOST_CHECK(w4.pavg() == pavg0);
}
{
const auto& w1 = sched.getWell("P1", 1);
const auto& w4 = sched.getWell("P4", 1);
BOOST_CHECK(w1.pavg() == pavg1);
BOOST_CHECK(w4.pavg() == pavg1);
}
{
const auto& w1 = sched.getWell("P1", 2);
const auto& w3 = sched.getWell("P3", 2);
const auto& w4 = sched.getWell("P4", 2);
BOOST_CHECK(w1.pavg() == pavg2);
BOOST_CHECK(w3.pavg() == pavg3);
BOOST_CHECK(w4.pavg() == pavg1);
}
{
const auto& w1 = sched.getWell("P1", 3);
const auto& w2 = sched.getWell("P2", 3);
const auto& w3 = sched.getWell("P3", 3);
const auto& w4 = sched.getWell("P4", 3);
BOOST_CHECK(w1.pavg() == pavg4);
BOOST_CHECK(w2.pavg() == pavg4);
BOOST_CHECK(w3.pavg() == pavg4);
BOOST_CHECK(w4.pavg() == pavg4);
}
}

View File

@ -1319,3 +1319,25 @@ COPRL
BOOST_AUTO_TEST_CASE( WBP ) {
const std::string input = R"(
WBP
/
WBP4
/
WBP5
/
WBP9
/
)";
const auto& summary_config = createSummary(input);
BOOST_CHECK(summary_config.hasKeyword("WBP"));
BOOST_CHECK(summary_config.hasKeyword("WBP4"));
BOOST_CHECK(summary_config.hasKeyword("WBP5"));
BOOST_CHECK(summary_config.hasKeyword("WBP9"));
}