Refactor WellProductionProperties manipulation code

This commit splits the creation of WellProductionProperties objects,
and especially the ad-hoc helper functions historyProperties()
and predictionProperties() out to a separate module,
WellProductionProperties.[hc]pp.  Creating the properties object
from a DeckRecordConstPtr is deferred to two named constructors,

    WellProductionProperties::history() and
    WellProductionProperties::prediction()

that, respectively, assume the roles of historyProperties() and
predictionProperties().  Reimplement handleWCONProducer() in terms
of these named constructors and remove the producerProperties()
helper whose task, inspecting the status and retrieving/setting the
CMODE if not SHUT, can be assumed by handleWCONProducer().

Add a simple test module, WellPropertiesTest.cpp, to enforce the
rather peculiar semantics of the WCONHIST keyword.  Control modes
{O,W,G}RAT, LRAT, and RESV are *always* (unconditionally) supported
in WCONHIST but there is no control mode switching.  The latter is
deferred to client code, depending on the '.predictionMode' flag.

Suggested by: [at] joakim-hove
This commit is contained in:
Bård Skaflestad 2014-07-07 13:28:25 +02:00
parent 93c133a7b8
commit ef5a9e8e1a
8 changed files with 399 additions and 158 deletions

View File

@ -66,6 +66,7 @@ EclipseState/EclipseState.cpp
EclipseState/Schedule/TimeMap.cpp
EclipseState/Schedule/Schedule.cpp
EclipseState/Schedule/Well.cpp
EclipseState/Schedule/WellProductionProperties.cpp
EclipseState/Schedule/WellSet.cpp
EclipseState/Schedule/Group.cpp
EclipseState/Schedule/Completion.cpp
@ -123,6 +124,7 @@ EclipseState/EclipseState.hpp
EclipseState/Schedule/TimeMap.hpp
EclipseState/Schedule/Schedule.hpp
EclipseState/Schedule/Well.hpp
EclipseState/Schedule/WellProductionProperties.hpp
EclipseState/Schedule/WellSet.hpp
EclipseState/Schedule/Group.hpp
EclipseState/Schedule/DynamicState.hpp

View File

@ -19,6 +19,7 @@
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/TimeMap.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/WellProductionProperties.hpp>
#include <opm/parser/eclipse/Deck/Section.hpp>
#include <boost/algorithm/string.hpp>
@ -166,114 +167,6 @@ namespace Opm {
}
}
namespace {
WellProductionProperties
historyProperties(DeckRecordConstPtr record)
{
WellProductionProperties p;
p.predictionMode = false;
// Modes supported in WCONHIST just from {O,W,G}RAT values
//
// Note: The default value of observed {O,W,G}RAT is zero
// (numerically) whence the following control modes are
// unconditionally supported.
const std::vector<std::string> controlModes{
"ORAT", "WRAT", "GRAT", "LRAT", "RESV"
};
for (std::vector<std::string>::const_iterator
mode = controlModes.begin(), end = controlModes.end();
mode != end; ++mode)
{
const WellProducer::ControlModeEnum cmode =
WellProducer::ControlModeFromString(*mode);
p.addProductionControl(cmode);
}
// BHP mode needs explicit value
if (! record->getItem("BHP")->defaultApplied()) {
p.BHPLimit = record->getItem("BHP")->getSIDouble(0);
p.addProductionControl(WellProducer::BHP);
}
return p;
}
WellProductionProperties
predictionProperties(DeckRecordConstPtr record)
{
WellProductionProperties p;
p.LiquidRate = record->getItem("LRAT")->getSIDouble(0);
p.ResVRate = record->getItem("RESV")->getSIDouble(0);
p.BHPLimit = record->getItem("BHP" )->getSIDouble(0);
p.THPLimit = record->getItem("THP" )->getSIDouble(0);
p.predictionMode = true;
const std::vector<std::string> controlModes{
"ORAT", "WRAT", "GRAT", "LRAT",
"RESV", "BHP" , "THP"
};
for (std::vector<std::string>::const_iterator
mode = controlModes.begin(), end = controlModes.end();
mode != end; ++mode)
{
if (! record->getItem(*mode)->defaultApplied()) {
const WellProducer::ControlModeEnum cmode =
WellProducer::ControlModeFromString(*mode);
p.addProductionControl(cmode);
}
}
return p;
}
WellProductionProperties
producerProperties(DeckRecordConstPtr record,
WellCommon::StatusEnum status,
bool is_pred,
const std::string& wname)
{
WellProductionProperties p;
if (is_pred) {
p = predictionProperties(record);
}
else {
p = historyProperties(record);
}
p.WaterRate = record->getItem("WRAT")->getSIDouble(0);
p.OilRate = record->getItem("ORAT")->getSIDouble(0);
p.GasRate = record->getItem("GRAT")->getSIDouble(0);
if (status != WellCommon::SHUT) {
const std::string& cmodeString =
record->getItem("CMODE")->getTrimmedString(0);
WellProducer::ControlModeEnum control =
WellProducer::ControlModeFromString(cmodeString);
if (p.hasProductionControl(control)) {
p.controlMode = control;
}
else {
throw std::invalid_argument("Tried to set invalid control: " +
cmodeString + " for well: " + wname);
}
}
return p;
}
} // namespace anonymous
void Schedule::handleWCONProducer(DeckKeywordConstPtr keyword, size_t currentStep, bool isPredictionMode) {
for (size_t recordNr = 0; recordNr < keyword->size(); recordNr++) {
DeckRecordConstPtr record = keyword->getRecord(recordNr);
@ -284,16 +177,33 @@ namespace Opm {
const WellCommon::StatusEnum status =
WellCommon::StatusFromString(record->getItem("STATUS")->getTrimmedString(0));
const WellProductionProperties& properties =
producerProperties(record, status,
isPredictionMode,
wellNamePattern);
WellProductionProperties properties =
((isPredictionMode)
? WellProductionProperties::prediction(record)
: WellProductionProperties::history (record));
const std::vector<WellPtr>& wells = getWells(wellNamePattern);
for (auto wellIter=wells.begin(); wellIter != wells.end(); ++wellIter) {
WellPtr well = *wellIter;
if (status != WellCommon::SHUT) {
const std::string& cmodeString =
record->getItem("CMODE")->getTrimmedString(0);
WellProducer::ControlModeEnum control =
WellProducer::ControlModeFromString(cmodeString);
if (properties.hasProductionControl(control)) {
properties.controlMode = control;
}
else {
throw std::invalid_argument("Tried to set invalid control: " +
cmodeString + " for well: " +
wellNamePattern);
}
}
well->setStatus( currentStep , status );
well->setProductionProperties(currentStep, properties);
}

View File

@ -25,6 +25,7 @@
#include <opm/parser/eclipse/EclipseState/Schedule/DynamicState.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/CompletionSet.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Completion.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/WellProductionProperties.hpp>
#include <boost/optional.hpp>
@ -34,52 +35,6 @@
namespace Opm {
typedef struct WellProductionProperties {
double OilRate;
double GasRate;
double WaterRate;
double LiquidRate;
double ResVRate;
double BHPLimit;
double THPLimit;
bool predictionMode;
int productionControls;
WellProducer::ControlModeEnum controlMode;
WellProductionProperties() {
OilRate=0.0;
GasRate=0.0;
WaterRate=0.0;
LiquidRate=0.0;
ResVRate=0.0;
BHPLimit=0.0;
THPLimit=0.0;
predictionMode=true;
productionControls=0;
controlMode = WellProducer::ORAT;
}
bool hasProductionControl(WellProducer::ControlModeEnum controlModeArg) const {
if (productionControls & controlModeArg)
return true;
else
return false;
}
void dropProductionControl(WellProducer::ControlModeEnum controlModeArg) {
if ((productionControls & controlModeArg) != 0) {
productionControls -= controlModeArg;
}
}
void addProductionControl(WellProducer::ControlModeEnum controlModeArg) {
if ((productionControls & controlModeArg) == 0) {
productionControls += controlModeArg;
}
}
} WellProductionProperties;
typedef struct WellInjectionProperties {
double surfaceInjectionRate;
double reservoirInjectionRate;

View File

@ -0,0 +1,108 @@
#include <opm/parser/eclipse/EclipseState/Schedule/WellProductionProperties.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/ScheduleEnums.hpp>
#include <string>
#include <vector>
namespace Opm {
WellProductionProperties::
WellProductionProperties()
{
init();
predictionMode = true;
}
WellProductionProperties::
WellProductionProperties(DeckRecordConstPtr record)
{
init();
WaterRate = record->getItem("WRAT")->getSIDouble(0);
OilRate = record->getItem("ORAT")->getSIDouble(0);
GasRate = record->getItem("GRAT")->getSIDouble(0);
}
WellProductionProperties
WellProductionProperties::history(DeckRecordConstPtr record)
{
WellProductionProperties p(record);
p.predictionMode = false;
// Modes supported in WCONHIST just from {O,W,G}RAT values
//
// Note: The default value of observed {O,W,G}RAT is zero
// (numerically) whence the following control modes are
// unconditionally supported.
const std::vector<std::string> controlModes{
"ORAT", "WRAT", "GRAT", "LRAT", "RESV"
};
for (std::vector<std::string>::const_iterator
mode = controlModes.begin(), end = controlModes.end();
mode != end; ++mode)
{
const WellProducer::ControlModeEnum cmode =
WellProducer::ControlModeFromString(*mode);
p.addProductionControl(cmode);
}
// BHP control must be explictly provided.
if (! record->getItem("BHP")->defaultApplied()) {
p.addProductionControl(WellProducer::BHP);
}
return p;
}
WellProductionProperties
WellProductionProperties::prediction(DeckRecordConstPtr record)
{
WellProductionProperties p(record);
p.predictionMode = true;
p.LiquidRate = record->getItem("LRAT")->getSIDouble(0);
p.ResVRate = record->getItem("RESV")->getSIDouble(0);
p.BHPLimit = record->getItem("BHP" )->getSIDouble(0);
p.THPLimit = record->getItem("THP" )->getSIDouble(0);
const std::vector<std::string> controlModes{
"ORAT", "WRAT", "GRAT", "LRAT",
"RESV", "BHP" , "THP"
};
for (std::vector<std::string>::const_iterator
mode = controlModes.begin(), end = controlModes.end();
mode != end; ++mode)
{
if (! record->getItem(*mode)->defaultApplied()) {
const WellProducer::ControlModeEnum cmode =
WellProducer::ControlModeFromString(*mode);
p.addProductionControl(cmode);
}
}
return p;
}
void
WellProductionProperties::init()
{
// public: properties (in order of declaration)
OilRate = 0.0;
GasRate = 0.0;
WaterRate = 0.0;
LiquidRate = 0.0;
ResVRate = 0.0;
BHPLimit = 0.0;
THPLimit = 0.0;
controlMode = WellProducer::ORAT;
// private: property
productionControls = 0;
}
} // namespace Opm

View File

@ -0,0 +1,79 @@
/*
Copyright 2013 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/>.
*/
#ifndef WELLPRODUCTIONPROPERTIES_HPP_HEADER_INCLUDED
#define WELLPRODUCTIONPROPERTIES_HPP_HEADER_INCLUDED
#include <opm/parser/eclipse/EclipseState/Schedule/ScheduleEnums.hpp>
#include <opm/parser/eclipse/Deck/DeckRecord.hpp>
namespace Opm {
struct WellProductionProperties {
double OilRate;
double GasRate;
double WaterRate;
double LiquidRate;
double ResVRate;
double BHPLimit;
double THPLimit;
bool predictionMode;
WellProducer::ControlModeEnum controlMode;
WellProductionProperties();
static WellProductionProperties
history(DeckRecordConstPtr record);
static WellProductionProperties
prediction(DeckRecordConstPtr record);
bool
hasProductionControl(WellProducer::ControlModeEnum controlModeArg) const
{
return (productionControls & controlModeArg) != 0;
}
void
dropProductionControl(WellProducer::ControlModeEnum controlModeArg)
{
if (hasProductionControl(controlModeArg)) {
productionControls -= controlModeArg;
}
}
void
addProductionControl(WellProducer::ControlModeEnum controlModeArg)
{
if (! hasProductionControl(controlModeArg)) {
productionControls += controlModeArg;
}
}
private:
int productionControls;
WellProductionProperties(DeckRecordConstPtr record);
void
init();
};
} // namespace Opm
#endif // WELLPRODUCTIONPROPERTIES_HPP_HEADER_INCLUDED

View File

@ -13,6 +13,11 @@ target_link_libraries(runWellTests Parser ${Boost_LIBRARIES})
add_test(NAME runWellTests WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${TEST_MEMCHECK_TOOL} ${EXECUTABLE_OUTPUT_PATH}/runWellTests )
add_executable(runWellPropertiesTests WellPropertiesTests.cpp)
target_link_libraries(runWellPropertiesTests Parser ${Boost_LIBRARIES})
add_test(NAME runWellPropertiesTests WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${TEST_MEMCHECK_TOOL} ${EXECUTABLE_OUTPUT_PATH}/runWellPropertiesTests )
add_executable(runWellSetTests WellSetTests.cpp)
target_link_libraries(runWellSetTests Parser ${Boost_LIBRARIES})
add_test(NAME runWellSetTests WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${TEST_MEMCHECK_TOOL} ${EXECUTABLE_OUTPUT_PATH}/runWellSetTests )

View File

@ -0,0 +1,181 @@
/*
Copyright 2014 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/>.
*/
#include <opm/parser/eclipse/EclipseState/Schedule/WellProductionProperties.hpp>
#define BOOST_TEST_MODULE WellPropertiesTest
#include <boost/test/unit_test.hpp>
#include <opm/parser/eclipse/Parser/Parser.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/ScheduleEnums.hpp>
#include <string>
namespace {
namespace WCONHIST {
std::string
all_specified()
{
const std::string input =
"WCONHIST\n"
"'P' 'OPEN' 'ORAT' 1 2 3/\n/\n";
return input;
}
std::string
orat_defaulted()
{
const std::string input =
"WCONHIST\n"
"'P' 'OPEN' 'WRAT' 1* 2 3/\n/\n";
return input;
}
std::string
owrat_defaulted()
{
const std::string input =
"WCONHIST\n"
"'P' 'OPEN' 'GRAT' 1* 1* 3/\n/\n";
return input;
}
std::string
all_defaulted()
{
const std::string input =
"WCONHIST\n"
"'P' 'OPEN' 'LRAT'/\n/\n";
return input;
}
std::string
all_defaulted_with_bhp()
{
const std::string input =
"WCONHIST\n"
"-- 1 2 3 4-9 10\n"
" 'P' 'OPEN' 'RESV' 6* 500/\n/\n";
return input;
}
Opm::WellProductionProperties
properties(const std::string& input)
{
Opm::Parser parser;
Opm::DeckPtr deck = parser.parseString(input);
Opm::DeckKeywordConstPtr kwd = deck->getKeyword("WCONHIST");
Opm::DeckRecordConstPtr record = kwd->getRecord(0);
return Opm::WellProductionProperties::history(record);
}
} // namespace WCONHIST
} // namespace anonymous
BOOST_AUTO_TEST_CASE(WCH_All_Specified_BHP_Defaulted)
{
const Opm::WellProductionProperties& p =
WCONHIST::properties(WCONHIST::all_specified());
// WCONHIST always supports {O,W,G}RAT, LRAT, and
// RESV--irrespective of actual specification.
BOOST_REQUIRE(p.hasProductionControl(Opm::WellProducer::ORAT));
BOOST_REQUIRE(p.hasProductionControl(Opm::WellProducer::WRAT));
BOOST_REQUIRE(p.hasProductionControl(Opm::WellProducer::GRAT));
BOOST_REQUIRE(p.hasProductionControl(Opm::WellProducer::LRAT));
BOOST_REQUIRE(p.hasProductionControl(Opm::WellProducer::RESV));
// BHP must be explicitly provided/specified
BOOST_REQUIRE(! p.hasProductionControl(Opm::WellProducer::BHP));
}
BOOST_AUTO_TEST_CASE(WCH_ORAT_Defaulted_BHP_Defaulted)
{
const Opm::WellProductionProperties& p =
WCONHIST::properties(WCONHIST::orat_defaulted());
// WCONHIST always supports {O,W,G}RAT, LRAT, and
// RESV--irrespective of actual specification.
BOOST_REQUIRE(p.hasProductionControl(Opm::WellProducer::ORAT));
BOOST_REQUIRE(p.hasProductionControl(Opm::WellProducer::WRAT));
BOOST_REQUIRE(p.hasProductionControl(Opm::WellProducer::GRAT));
BOOST_REQUIRE(p.hasProductionControl(Opm::WellProducer::LRAT));
BOOST_REQUIRE(p.hasProductionControl(Opm::WellProducer::RESV));
// BHP must be explicitly provided/specified
BOOST_REQUIRE(! p.hasProductionControl(Opm::WellProducer::BHP));
}
BOOST_AUTO_TEST_CASE(WCH_OWRAT_Defaulted_BHP_Defaulted)
{
const Opm::WellProductionProperties& p =
WCONHIST::properties(WCONHIST::owrat_defaulted());
// WCONHIST always supports {O,W,G}RAT, LRAT, and
// RESV--irrespective of actual specification.
BOOST_REQUIRE(p.hasProductionControl(Opm::WellProducer::ORAT));
BOOST_REQUIRE(p.hasProductionControl(Opm::WellProducer::WRAT));
BOOST_REQUIRE(p.hasProductionControl(Opm::WellProducer::GRAT));
BOOST_REQUIRE(p.hasProductionControl(Opm::WellProducer::LRAT));
BOOST_REQUIRE(p.hasProductionControl(Opm::WellProducer::RESV));
// BHP must be explicitly provided/specified
BOOST_REQUIRE(! p.hasProductionControl(Opm::WellProducer::BHP));
}
BOOST_AUTO_TEST_CASE(WCH_Rates_Defaulted_BHP_Defaulted)
{
const Opm::WellProductionProperties& p =
WCONHIST::properties(WCONHIST::all_defaulted());
// WCONHIST always supports {O,W,G}RAT, LRAT, and
// RESV--irrespective of actual specification.
BOOST_REQUIRE(p.hasProductionControl(Opm::WellProducer::ORAT));
BOOST_REQUIRE(p.hasProductionControl(Opm::WellProducer::WRAT));
BOOST_REQUIRE(p.hasProductionControl(Opm::WellProducer::GRAT));
BOOST_REQUIRE(p.hasProductionControl(Opm::WellProducer::LRAT));
BOOST_REQUIRE(p.hasProductionControl(Opm::WellProducer::RESV));
// BHP must be explicitly provided/specified
BOOST_REQUIRE(! p.hasProductionControl(Opm::WellProducer::BHP));
}
BOOST_AUTO_TEST_CASE(WCH_Rates_Defaulted_BHP_Specified)
{
const Opm::WellProductionProperties& p =
WCONHIST::properties(WCONHIST::all_defaulted_with_bhp());
// WCONHIST always supports {O,W,G}RAT, LRAT, and
// RESV--irrespective of actual specification.
BOOST_REQUIRE(p.hasProductionControl(Opm::WellProducer::ORAT));
BOOST_REQUIRE(p.hasProductionControl(Opm::WellProducer::WRAT));
BOOST_REQUIRE(p.hasProductionControl(Opm::WellProducer::GRAT));
BOOST_REQUIRE(p.hasProductionControl(Opm::WellProducer::LRAT));
BOOST_REQUIRE(p.hasProductionControl(Opm::WellProducer::RESV));
// BHP must be explicitly provided/specified
BOOST_REQUIRE(p.hasProductionControl(Opm::WellProducer::BHP));
}

View File

@ -32,6 +32,7 @@
#include <opm/parser/eclipse/EclipseState/Schedule/ScheduleEnums.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/TimeMap.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/WellProductionProperties.hpp>
static Opm::TimeMapPtr createXDaysTimeMap(size_t numDays) {
boost::gregorian::date startDate( 2010 , boost::gregorian::Jan , 1);