Add GuideRate state/cache object

This commit is contained in:
Joakim Hove
2019-09-06 13:55:30 +02:00
parent dd609e857a
commit a1f35584a9
11 changed files with 423 additions and 25 deletions

View File

@@ -91,6 +91,7 @@ if(ENABLE_ECL_INPUT)
src/opm/parser/eclipse/EclipseState/Schedule/ArrayDimChecker.cpp
src/opm/parser/eclipse/EclipseState/Schedule/Events.cpp
src/opm/parser/eclipse/EclipseState/Schedule/Group/Group2.cpp
src/opm/parser/eclipse/EclipseState/Schedule/Group/GuideRate.cpp
src/opm/parser/eclipse/EclipseState/Schedule/Group/GuideRateConfig.cpp
src/opm/parser/eclipse/EclipseState/Schedule/Group/GuideRateModel.cpp
src/opm/parser/eclipse/EclipseState/Schedule/Group/GTNode.cpp
@@ -564,6 +565,7 @@ if(ENABLE_ECL_INPUT)
opm/parser/eclipse/EclipseState/Schedule/Tuning.hpp
opm/parser/eclipse/EclipseState/Schedule/Group/GTNode.hpp
opm/parser/eclipse/EclipseState/Schedule/Group/Group2.hpp
opm/parser/eclipse/EclipseState/Schedule/Group/GuideRate.hpp
opm/parser/eclipse/EclipseState/Schedule/Group/GuideRateConfig.hpp
opm/parser/eclipse/EclipseState/Schedule/Group/GuideRateModel.hpp
opm/parser/eclipse/EclipseState/Schedule/MessageLimits.hpp

View File

@@ -0,0 +1,71 @@
/*
Copyright 2019 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 GUIDE_RATE_HPP
#define GUIDE_RATE_HPP
#include <string>
#include <cstddef>
#include <ctime>
#include <unordered_map>
namespace Opm {
class Schedule;
class GuideRate {
struct GuideRateValue {
GuideRateValue() = default;
GuideRateValue(std::time_t t, double v):
sim_time(t),
value(v)
{}
bool operator==(const GuideRateValue& other) const {
return (this->sim_time == other.sim_time &&
this->value == other.value);
}
bool operator!=(const GuideRateValue& other) const {
return !(*this == other);
}
std::time_t sim_time;
double value;
};
public:
GuideRate(const Schedule& schedule);
double update(const std::string& wgname, size_t report_step, double sim_time, double oil_pot, double gas_pot, double wat_pot);
private:
void well_update(const std::string& wgname, size_t report_step, double sim_time, double oil_pot, double gas_pot, double wat_pot);
void group_update(const std::string& wgname, size_t report_step, double sim_time, double oil_pot, double gas_pot, double wat_pot);
double get(const std::string& wgname, size_t report_step) const;
double eval_form(const GuideRateModel& model, double oil_pot, double gas_pot, double wat_pot, const GuideRateValue * prev) const;
double eval_group_pot() const;
double eval_group_resvinj() const;
std::unordered_map<std::string,GuideRateValue> values;
const Schedule& schedule;
};
}
#endif

View File

@@ -35,13 +35,13 @@ public:
struct Well {
double guide_rate;
Well2::GuideRateTarget phase;
Well2::GuideRateTarget target;
double scaling_factor;
};
struct Group {
double guide_rate;
Group2::GuideRateTarget phase;
Group2::GuideRateTarget target;
};
const GuideRateModel& model() const;

View File

@@ -20,6 +20,8 @@
#ifndef GUIDE_RATE_MODEL_HPP
#define GUIDE_RATE_MODEL_HPP
#include <opm/parser/eclipse/Deck/UDAValue.hpp>
namespace Opm {
class GuideRateModel {
@@ -49,28 +51,34 @@ public:
bool use_free_gas_arg);
GuideRateModel() = default;
double eval(double pot, double R1, double R2) const;
bool updateLINCOM(const UDAValue& alpha, const UDAValue& beta, const UDAValue& gamma);
double eval(double oil_pot, double gas_pot, double wat_pot) const;
double update_delay() const;
bool allow_increase() const;
double damping_factor() const;
bool operator==(const GuideRateModel& other) const;
bool operator!=(const GuideRateModel& other) const;
Target target() const;
private:
/*
Unfortunately the default values will give a GuideRateModel which can not
be evaluated, due to a division by zero problem.
*/
double time_interval = 0;
Target target = Target::NONE;
Target m_target = Target::NONE;
double A = 0;
double B = 0;
double C = 0;
double D = 0;
double E = 0;
double F = 0;
bool allow_increase = true;
double damping_factor = 1.0;
bool allow_increase_ = true;
double damping_factor_ = 1.0;
bool use_free_gas = false;
bool default_model = true;
UDAValue alpha;
UDAValue beta;
UDAValue gamma;
};
}

View File

@@ -281,6 +281,7 @@ namespace Opm
void handleGCONPROD( const DeckKeyword& keyword, size_t currentStep, const ParseContext& parseContext, ErrorGuard& errors);
void handleGEFAC( const DeckKeyword& keyword, size_t currentStep, const ParseContext& parseContext, ErrorGuard& errors);
void handleGUIDERAT( const DeckKeyword& keyword, size_t currentStep);
void handleLINCOM( const DeckKeyword& keyword, size_t currentStep);
void handleWEFAC( const DeckKeyword& keyword, size_t currentStep, const ParseContext& parseContext, ErrorGuard& errors);
void handleTUNING( const DeckKeyword& keyword, size_t currentStep);
void handleNUPCOL( const DeckKeyword& keyword, size_t currentStep);

View File

@@ -0,0 +1,137 @@
/*
Copyright 2019 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 <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Group/GuideRate.hpp>
namespace Opm {
GuideRate::GuideRate(const Schedule& schedule_arg) :
schedule(schedule_arg)
{}
double GuideRate::get(const std::string& wgname, size_t report_step) const {
const auto& value = this->values.at(wgname);
return value.value;
}
double GuideRate::update(const std::string& wgname, size_t report_step, double sim_time, double oil_pot, double gas_pot, double wat_pot) {
const auto& config = this->schedule.guideRateConfig(report_step);
if (config.has_well(wgname))
this->well_update(wgname, report_step, sim_time, oil_pot, gas_pot, wat_pot);
else if (config.has_group(wgname)) {
this->group_update(wgname, report_step, sim_time, oil_pot, gas_pot, wat_pot);
} else
throw std::out_of_range("No such well/group: ");
return this->get(wgname, report_step);
}
void GuideRate::group_update(const std::string& wgname, size_t report_step, double sim_time, double oil_pot, double gas_pot, double wat_pot) {
const auto& config = this->schedule.guideRateConfig(report_step);
const auto& group = config.group(wgname);
auto iter = this->values.find(wgname);
// If the FORM mode is used we check if the last computation is recent enough;
// then we just return.
if (iter != this->values.end()) {
const auto& grv = iter->second;
if (group.target == Group2::GuideRateTarget::FORM) {
auto time_diff = sim_time - grv.sim_time;
if (config.model().update_delay() > time_diff)
return;
}
}
double guide_rate = group.guide_rate;
if (group.guide_rate == 0 || group.target == Group2::GuideRateTarget::POTN)
guide_rate = this->eval_group_pot();
if (group.target == Group2::GuideRateTarget::INJV)
guide_rate = this->eval_group_resvinj();
if (group.target == Group2::GuideRateTarget::FORM) {
if (iter != this->values.end())
guide_rate = this->eval_form(config.model(), oil_pot, gas_pot, wat_pot, nullptr);
else
guide_rate = this->eval_form(config.model(), oil_pot, gas_pot, wat_pot, std::addressof(iter->second));
}
this->values[wgname] = GuideRateValue{sim_time, guide_rate};
}
void GuideRate::well_update(const std::string& wgname, size_t report_step, double sim_time, double oil_pot, double gas_pot, double wat_pot) {
const auto& config = this->schedule.guideRateConfig(report_step);
const auto& well = config.well(wgname);
if (well.guide_rate > 0)
this->values[wgname] = GuideRateValue( sim_time, well.guide_rate );
else {
auto iter = this->values.find(wgname);
if (iter != this->values.end()) {
const auto& grv = iter->second;
auto time_diff = sim_time - grv.sim_time;
if (config.model().update_delay() > time_diff)
return;
}
double guide_rate;
if (iter == this->values.end())
guide_rate = this->eval_form(config.model(), oil_pot, gas_pot, wat_pot, nullptr);
else
guide_rate = this->eval_form(config.model(), oil_pot, gas_pot, wat_pot, std::addressof(iter->second));
this->values[wgname] = GuideRateValue{sim_time, guide_rate};
}
}
double GuideRate::eval_form(const GuideRateModel& model, double oil_pot, double gas_pot, double wat_pot, const GuideRateValue * prev) const {
double pot = 0;
double R1 = 0.5;
double R2 = 0.5;
double new_guide_rate = model.eval(pot, R1, R2);
if (!prev)
return new_guide_rate;
if (new_guide_rate > prev->value && !model.allow_increase())
new_guide_rate = prev->value;
auto damping_factor = model.damping_factor();
return damping_factor * new_guide_rate + (1 - damping_factor) * prev->value;
}
double GuideRate::eval_group_pot() const {
return 0;
}
double GuideRate::eval_group_resvinj() const {
return 0;
}
}

View File

@@ -41,7 +41,7 @@ void GuideRateConfig::update_well(const Well2& well) {
if (well.isAvailableForGroupControl()) {
auto& well_node = this->wells[well.name()];
well_node.guide_rate = well.getGuideRate();
well_node.phase = well.getGuideRatePhase();
well_node.target= well.getGuideRatePhase();
well_node.scaling_factor = well.getGuideRateScalingFactor();
} else
this->wells.erase(well.name());
@@ -64,7 +64,7 @@ void GuideRateConfig::update_group(const Group2& group) {
auto& group_node = this->groups[group.name()];
group_node.guide_rate = properties.guide_rate;
group_node.phase = guide_target;
group_node.target = guide_target;
}
const GuideRateConfig::Group& GuideRateConfig::group(const std::string& group) const {

View File

@@ -20,6 +20,7 @@
#include <cmath>
#include <string>
#include <opm/parser/eclipse/Parser/ParserKeywords/L.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Group/GuideRateModel.hpp>
namespace Opm {
@@ -36,17 +37,20 @@ GuideRateModel::GuideRateModel(double time_interval_arg,
double damping_factor_arg,
bool use_free_gas_arg) :
time_interval(time_interval_arg),
target(target_arg),
m_target(target_arg),
A(A_arg),
B(B_arg),
C(C_arg),
D(D_arg),
E(E_arg),
F(F_arg),
allow_increase(allow_increase_arg),
damping_factor(damping_factor_arg),
allow_increase_(allow_increase_arg),
damping_factor_(damping_factor_arg),
use_free_gas(use_free_gas_arg),
default_model(false)
default_model(false),
alpha(UDAValue(ParserKeywords::LINCOM::ALPHA::defaultValue)),
beta(UDAValue(ParserKeywords::LINCOM::BETA::defaultValue)),
gamma(UDAValue(ParserKeywords::LINCOM::GAMMA::defaultValue))
{
if (this->A > 3 || this->A < -3)
throw std::invalid_argument("Invalid value for A must be in interval [-3,3]");
@@ -59,18 +63,52 @@ GuideRateModel::GuideRateModel(double time_interval_arg,
if (this->F > 3 || this->F < -3)
throw std::invalid_argument("Invalid value for F must be in interval [-3,3]");
if (this->m_target == Target::COMB)
throw std::logic_error("Sorry - the 'COMB' mode is not supported");
}
double GuideRateModel::update_delay() const {
return this->time_interval;
}
double GuideRateModel::eval(double pot, double R1, double R2) const {
double GuideRateModel::eval(double oil_pot, double gas_pot, double wat_pot) const {
if (this->default_model)
throw std::invalid_argument("The default GuideRateModel can not be evaluated - must enter GUIDERAT information explicitly.");
double pot;
double R1;
double R2;
switch (this->m_target) {
case Target::OIL:
pot = oil_pot;
R1 = wat_pot / oil_pot;
R2 = gas_pot / oil_pot;
break;
case Target::LIQ:
pot = oil_pot + wat_pot;;
R1 = oil_pot / (oil_pot + wat_pot);
R2 = wat_pot / (oil_pot + wat_pot);
break;
case Target::GAS:
pot = gas_pot;
R1 = wat_pot / gas_pot;
R2 = oil_pot / gas_pot;
break;
case Target::COMB:
throw std::logic_error("Not implemented - don't have a clue?");
case Target::RES:
throw std::logic_error("Not implemented - don't have a clue?");
default:
throw std::logic_error("Hmmm - should not be here?");
}
double denom = this->B + this->C*std::pow(R1, this->D) + this->E*std::pow(R2, this->F);
/*
The values pot, R1 and R2 are runtime simulation results, so here
@@ -85,15 +123,15 @@ double GuideRateModel::eval(double pot, double R1, double R2) const {
bool GuideRateModel::operator==(const GuideRateModel& other) const {
return (this->time_interval == other.time_interval) &&
(this->target == other.target) &&
(this->m_target == other.m_target) &&
(this->A == other.A) &&
(this->B == other.B) &&
(this->C == other.C) &&
(this->D == other.D) &&
(this->E == other.E) &&
(this->F == other.F) &&
(this->allow_increase == other.allow_increase) &&
(this->damping_factor == other.damping_factor) &&
(this->allow_increase_ == other.allow_increase_) &&
(this->damping_factor_ == other.damping_factor_) &&
(this->use_free_gas == other.use_free_gas);
}
@@ -102,6 +140,23 @@ bool GuideRateModel::operator!=(const GuideRateModel& other) const {
}
double GuideRateModel::update_delay() const {
return this->time_interval;
}
double GuideRateModel::damping_factor() const {
return this->damping_factor_;
}
bool GuideRateModel::allow_increase() const {
return this->allow_increase_;
}
GuideRateModel::Target GuideRateModel::target() const {
return this->m_target;
}
GuideRateModel::Target GuideRateModel::TargetFromString(const std::string& s) {
if (s == "OIL")
return Target::OIL;
@@ -124,4 +179,26 @@ GuideRateModel::Target GuideRateModel::TargetFromString(const std::string& s) {
throw std::invalid_argument("Could not convert: " + s + " to a valid Target enum value");
}
/*
The COMB target - which requires parameters from the LINCOM keyword, is not
supported. There are at least two pieces of missing functionality:
1. The parameters in the LINCOM come with unit specified by the LCUNIT
keyword; seemingly decoupled from the unit system in the rest of deck.
This functionality is not supported.
2. The values in the LINCOM kewyords are UDA values, have not yet integrated
the necessary SummaryState into this.
*/
bool GuideRateModel::updateLINCOM(const UDAValue& , const UDAValue& , const UDAValue& ) {
if (this->m_target == Target::COMB)
throw std::logic_error("The LINCOM keyword is not supported - at all!");
return true;
}
}

View File

@@ -35,6 +35,7 @@
#include <opm/parser/eclipse/Parser/ParseContext.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/C.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/G.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/L.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/V.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/W.hpp>
@@ -338,6 +339,9 @@ namespace {
else if (keyword.name() == "GUIDERAT")
handleGUIDERAT(keyword, currentStep);
else if (keyword.name() == "LINCOM")
handleLINCOM(keyword, currentStep);
else if (keyword.name() == "TUNING")
handleTUNING(keyword, currentStep);
@@ -1611,6 +1615,21 @@ namespace {
}
void Schedule::handleLINCOM( const DeckKeyword& keyword, size_t currentStep) {
const auto& record = keyword.getRecord(0);
auto alpha = record.getItem<ParserKeywords::LINCOM::ALPHA>().get<UDAValue>(0);
auto beta = record.getItem<ParserKeywords::LINCOM::BETA>().get<UDAValue>(0);
auto gamma = record.getItem<ParserKeywords::LINCOM::GAMMA>().get<UDAValue>(0);
auto new_config = std::make_shared<GuideRateConfig>( this->guideRateConfig(currentStep) );
auto new_model = new_config->model();
if (new_model.updateLINCOM(alpha, beta, gamma)) {
new_config->update_model(new_model);
this->guide_rate_config.update( currentStep, new_config );
}
}
void Schedule::handleTUNING( const DeckKeyword& keyword, size_t currentStep) {
int numrecords = keyword.size();

View File

@@ -33,6 +33,7 @@
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Group/Group2.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Group/GuideRateModel.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Group/GuideRate.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/SummaryState.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellProductionProperties.hpp>
@@ -299,9 +300,81 @@ BOOST_AUTO_TEST_CASE(createDeckWithGCONPROD) {
BOOST_AUTO_TEST_CASE(TESTGuideRateModel) {
Opm::GuideRateModel grc_default;
BOOST_CHECK_THROW(Opm::GuideRateModel(0.0,GuideRateModel::Target::NONE, -5,0,0,0,0,0,true,1,true), std::invalid_argument);
BOOST_CHECK_THROW(Opm::GuideRateModel(0.0,GuideRateModel::Target::OIL, -5,0,0,0,0,0,true,1,true), std::invalid_argument);
BOOST_CHECK_THROW(grc_default.eval(1,0.50,0.50), std::invalid_argument);
Opm::GuideRateModel grc_delay(10, GuideRateModel::Target::NONE, 1,1,0,0,0,0,true,1,true);
Opm::GuideRateModel grc_delay(10, GuideRateModel::Target::OIL, 1,1,0,0,0,0,true,1,true);
BOOST_CHECK_NO_THROW(grc_delay.eval(1.0, 0.5, 0.5));
}
BOOST_AUTO_TEST_CASE(TESTGuideRateLINCOM) {
Parser parser;
std::string input = R"(
START -- 0
31 AUG 1993 /
SCHEDULE
GRUPTREE
'G1' 'FIELD' /
'G2' 'FIELD' /
/
GCONPROD
'G1' 'ORAT' 10000 3* 'CON' /
'G2' 'RESV' 10000 3* 'CON' /
/
GUIDERAT
1* 'COMB' 1.0 1.0 /
LINCOM
1 2 'WWCT:OPX' /
)";
auto deck = parser.parseString(input);
EclipseGrid grid(10,10,10);
TableManager table ( deck );
Eclipse3DProperties eclipseProperties ( deck , table, grid);
Runspec runspec (deck );
/* The 'COMB' target mode is not supported */
BOOST_CHECK_THROW(Opm::Schedule schedule(deck, grid, eclipseProperties, runspec), std::logic_error);
}
BOOST_AUTO_TEST_CASE(TESTGuideRate) {
Parser parser;
std::string input = R"(
START -- 0
31 AUG 1993 /
SCHEDULE
GRUPTREE
'G1' 'FIELD' /
'G2' 'FIELD' /
/
GCONPROD
'G1' 'ORAT' 10000 3* 'CON' /
'G2' 'RESV' 10000 3* 'CON' /
/
GUIDERAT
1* 'OIL' 1.0 1.0 /
LINCOM
1 2 'WWCT:OPX' /
TSTEP
1 1 1 1 1 1 1 1 1 1 1 /
)";
auto deck = parser.parseString(input);
EclipseGrid grid(10,10,10);
TableManager table ( deck );
Eclipse3DProperties eclipseProperties ( deck , table, grid);
Runspec runspec (deck );
Schedule schedule(deck, grid, eclipseProperties, runspec);
GuideRate gr(schedule);
}

View File

@@ -48,6 +48,7 @@
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellProductionProperties.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellInjectionProperties.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Group/GuideRateConfig.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Group/GuideRate.hpp>
using namespace Opm;
@@ -3459,10 +3460,10 @@ DATES -- 4
{
const auto& grc = schedule.guideRateConfig(0);
const auto& w1_node = grc.well("W1");
BOOST_CHECK(w1_node.phase == Well2::GuideRateTarget::OIL);
BOOST_CHECK(w1_node.target == Well2::GuideRateTarget::OIL);
const auto& w2_node = grc.well("W2");
BOOST_CHECK(w2_node.phase == Well2::GuideRateTarget::GAS);
BOOST_CHECK(w2_node.target == Well2::GuideRateTarget::GAS);
BOOST_CHECK(!grc.has_group("G1"));
BOOST_CHECK(grc.has_group("G2"));
@@ -3470,7 +3471,7 @@ DATES -- 4
{
const auto& grc = schedule.guideRateConfig(2);
const auto& w1_node = grc.well("W1");
BOOST_CHECK(w1_node.phase == Well2::GuideRateTarget::WAT);
BOOST_CHECK(w1_node.target == Well2::GuideRateTarget::WAT);
BOOST_CHECK_EQUAL(w1_node.guide_rate, 0.75);
BOOST_CHECK(grc.has_well("W1"));
@@ -3480,4 +3481,13 @@ DATES -- 4
BOOST_CHECK(grc.has_group("G1"));
BOOST_CHECK(!grc.has_group("G2"));
}
GuideRate gr(schedule);
double oil_pot = 1;
double gas_pot = 1;
double wat_pot = 1;
BOOST_CHECK_THROW(gr.update("XYZ",1, 1.0, oil_pot, gas_pot, wat_pot), std::out_of_range);
}