Add Guiderate Summary Output

This commit adds support for outputting the guiderate summary
vectors

    [GW][OGWV]PGR, [GW][GW]IGR

under the assumption that the values are fully calculated at another
level and that we therefore only need to extract the numerical
values and convert the rate units to output conventions.  We assume
that such values are communicated to the summary output layer by
means of a 'GuideRateValue' object.

The assumption of values already being calculated leads to a small
change in the 'need_wells()' function.  We're now able to exclude
guiderate values at the group level from those vectors that require
setting up a well vector.  This is a (tiny) performance improvement.
This commit is contained in:
Bård Skaflestad
2020-07-30 23:56:57 +02:00
parent 746bb15681
commit 084aa69641
4 changed files with 176 additions and 19 deletions

View File

@@ -43,6 +43,7 @@
#include <opm/io/eclipse/OutputStream.hpp>
#include <opm/output/data/Groups.hpp>
#include <opm/output/data/GuideRateValue.hpp>
#include <opm/output/data/Wells.hpp>
#include <opm/output/eclipse/RegionCache.hpp>
@@ -396,6 +397,9 @@ measure rate_unit() { return measure::liquid_surface_rate; }
template< Opm::Phase > constexpr
measure rate_unit() { return measure::liquid_surface_rate; }
template <Opm::data::GuideRateValue::Item>
measure rate_unit() { return measure::liquid_surface_rate; }
template<> constexpr
measure rate_unit< rt::gas >() { return measure::gas_surface_rate; }
template<> constexpr
@@ -431,6 +435,12 @@ measure rate_unit< rt::well_potential_oil >() { return measure::liquid_surface_r
template<> constexpr
measure rate_unit< rt::well_potential_gas >() { return measure::gas_surface_rate; }
template <> constexpr
measure rate_unit<Opm::data::GuideRateValue::Item::Gas>() { return measure::gas_surface_rate; }
template <> constexpr
measure rate_unit<Opm::data::GuideRateValue::Item::ResV>() { return measure::rate; }
double efac( const std::vector<std::pair<std::string,double>>& eff_factors, const std::string& name ) {
auto it = std::find_if( eff_factors.begin(), eff_factors.end(),
[&] ( const std::pair< std::string, double > elem )
@@ -842,6 +852,45 @@ inline quantity well_control_mode( const fn_args& args ) {
return { static_cast<double>(wmctl), unit };
}
template <Opm::data::GuideRateValue::Item i>
quantity guiderate_value(const ::Opm::data::GuideRateValue& grvalue)
{
return { !grvalue.has(i) ? 0.0 : grvalue.get(i), rate_unit<i>() };
}
template <bool injection, Opm::data::GuideRateValue::Item i>
quantity group_guiderate(const fn_args& args)
{
auto xgPos = args.groups.find(args.group_name);
if (xgPos == args.groups.end()) {
return { 0.0, rate_unit<i>() };
}
return injection
? guiderate_value<i>(xgPos->second.guideRates.injection)
: guiderate_value<i>(xgPos->second.guideRates.production);
}
template <bool injection, Opm::data::GuideRateValue::Item i>
quantity well_guiderate(const fn_args& args)
{
if (args.schedule_wells.empty()) {
return { 0.0, rate_unit<i>() };
}
const auto& well = args.schedule_wells.front();
if (well.isInjector() != injection) {
return { 0.0, rate_unit<i>() };
}
auto xwPos = args.wells.find(well.name());
if (xwPos == args.wells.end()) {
return { 0.0, rate_unit<i>() };
}
return guiderate_value<i>(xwPos->second.guide_rates);
}
/*
* A small DSL, really poor man's function composition, to avoid massive
* repetition when declaring the handlers for each individual keyword. bin_op
@@ -887,6 +936,8 @@ static const std::unordered_map< std::string, ofun > funs = {
{ "WSIR", rate< rt::brine, injector > },
{ "WVIR", sum( sum( rate< rt::reservoir_water, injector >, rate< rt::reservoir_oil, injector > ),
rate< rt::reservoir_gas, injector > ) },
{ "WGIGR", well_guiderate<injector, Opm::data::GuideRateValue::Item::Gas> },
{ "WWIGR", well_guiderate<injector, Opm::data::GuideRateValue::Item::Water> },
{ "WWIT", mul( rate< rt::wat, injector >, duration ) },
{ "WOIT", mul( rate< rt::oil, injector >, duration ) },
@@ -906,6 +957,11 @@ static const std::unordered_map< std::string, ofun > funs = {
{ "WCPC", div( rate< rt::polymer, producer >, rate< rt::wat, producer >) },
{ "WSPC", div( rate< rt::brine, producer >, rate< rt::wat, producer >) },
{ "WOPGR", well_guiderate<producer, Opm::data::GuideRateValue::Item::Oil> },
{ "WGPGR", well_guiderate<producer, Opm::data::GuideRateValue::Item::Gas> },
{ "WWPGR", well_guiderate<producer, Opm::data::GuideRateValue::Item::Water> },
{ "WVPGR", well_guiderate<producer, Opm::data::GuideRateValue::Item::ResV> },
{ "WGPRS", rate< rt::dissolved_gas, producer > },
{ "WGPRF", sub( rate< rt::gas, producer >, rate< rt::dissolved_gas, producer > ) },
{ "WOPRS", rate< rt::vaporized_oil, producer > },
@@ -957,6 +1013,10 @@ static const std::unordered_map< std::string, ofun > funs = {
{ "GSIR", rate< rt::brine, injector > },
{ "GVIR", sum( sum( rate< rt::reservoir_water, injector >, rate< rt::reservoir_oil, injector > ),
rate< rt::reservoir_gas, injector > ) },
{ "GGIGR", group_guiderate<injector, Opm::data::GuideRateValue::Item::Gas> },
{ "GWIGR", group_guiderate<injector, Opm::data::GuideRateValue::Item::Water> },
{ "GWIT", mul( rate< rt::wat, injector >, duration ) },
{ "GOIT", mul( rate< rt::oil, injector >, duration ) },
{ "GGIT", mul( rate< rt::gas, injector >, duration ) },
@@ -980,6 +1040,11 @@ static const std::unordered_map< std::string, ofun > funs = {
{ "GVPR", sum( sum( rate< rt::reservoir_water, producer >, rate< rt::reservoir_oil, producer > ),
rate< rt::reservoir_gas, producer > ) },
{ "GOPGR", group_guiderate<producer, Opm::data::GuideRateValue::Item::Oil> },
{ "GGPGR", group_guiderate<producer, Opm::data::GuideRateValue::Item::Gas> },
{ "GWPGR", group_guiderate<producer, Opm::data::GuideRateValue::Item::Water> },
{ "GVPGR", group_guiderate<producer, Opm::data::GuideRateValue::Item::ResV> },
{ "GWPT", mul( rate< rt::wat, producer >, duration ) },
{ "GOPT", mul( rate< rt::oil, producer >, duration ) },
{ "GGPT", mul( rate< rt::gas, producer >, duration ) },
@@ -1379,21 +1444,31 @@ inline std::vector<Opm::Well> find_wells( const Opm::Schedule& schedule,
throw std::runtime_error("Unhandled summary node category in find_wells");
}
bool need_wells(const Opm::EclIO::SummaryNode& node) {
bool need_wells(const Opm::EclIO::SummaryNode& node)
{
static const std::regex region_keyword_regex { "R[OGW][IP][RT]" };
static const std::regex group_guiderate_regex { "G[OGWV][IP]GR" };
using Cat = Opm::EclIO::SummaryNode::Category;
switch (node.category) {
case Opm::EclIO::SummaryNode::Category::Connection: [[fallthrough]];
case Opm::EclIO::SummaryNode::Category::Field: [[fallthrough]];
case Opm::EclIO::SummaryNode::Category::Group: [[fallthrough]];
case Opm::EclIO::SummaryNode::Category::Segment: [[fallthrough]];
case Opm::EclIO::SummaryNode::Category::Well:
return true;
case Opm::EclIO::SummaryNode::Category::Region:
case Cat::Connection: [[fallthrough]];
case Cat::Field: [[fallthrough]];
case Cat::Group: [[fallthrough]];
case Cat::Segment: [[fallthrough]];
case Cat::Well:
// Need to capture wells for anything other than guiderates at group
// level. Those are directly available in the solution values from
// the simulator and don't need aggregation from well level.
return (node.category != Cat::Group)
|| !std::regex_match(node.keyword, group_guiderate_regex);
case Cat::Region:
return std::regex_match(node.keyword, region_keyword_regex);
case Opm::EclIO::SummaryNode::Category::Aquifer: [[fallthrough]];
case Opm::EclIO::SummaryNode::Category::Miscellaneous: [[fallthrough]];
case Opm::EclIO::SummaryNode::Category::Block:
case Cat::Aquifer: [[fallthrough]];
case Cat::Miscellaneous: [[fallthrough]];
case Cat::Block:
return false;
}

View File

@@ -187,11 +187,13 @@ namespace {
bool is_rate(const std::string& keyword) {
static const keyword_set ratekw {
"OPR", "GPR", "WPR", "LPR", "NPR", "VPR",
"OPGR", "GPGR", "WPGR", "VPGR",
"OPRH", "GPRH", "WPRH", "LPRH",
"OVPR", "GVPR", "WVPR",
"OPRS", "GPRS", "OPRF", "GPRF",
"OIR", "GIR", "WIR", "LIR", "NIR", "VIR",
"OIGR", "GIGR", "WIGR",
"OIRH", "GIRH", "WIRH",
"OVIR", "GVIR", "WVIR",

View File

@@ -443,6 +443,27 @@ GSPR
/
GSPT
/
-- Production and injection guide rates (group level)
GOPGR
G_1 /
GGPGR
G_1 /
GWPGR
G_1 /
GVPGR
G_1 /
GGIGR
G_1 /
GWIGR
G_1 /
-- Well Data
-- Production Rates
WWPR
@@ -648,6 +669,26 @@ WSPR
WSPT
/
-- Production and injection guide rates (well level)
WOPGR
/
WGPGR
/
WWPGR
/
WVPGR
/
WGIGR
/
WWIGR
/
-- Water injection per connection
CWIR
* /

View File

@@ -32,6 +32,7 @@
#include <ctime>
#include <opm/output/data/Groups.hpp>
#include <opm/output/data/GuideRateValue.hpp>
#include <opm/output/data/Wells.hpp>
#include <opm/output/eclipse/Summary.hpp>
@@ -89,7 +90,8 @@ static const int day = 24 * 60 * 60;
This is quite misleading, because the values prepared in the test
input deck are NOT used.
*/
static data::Wells result_wells(const bool w3_injector = true) {
data::Wells result_wells(const bool w3_injector = true)
{
/* populate with the following pattern:
*
* Wells are named W_1, W_2 etc, i.e. wells are 1 indexed.
@@ -257,18 +259,23 @@ static data::Wells result_wells(const bool w3_injector = true) {
rates1, 0.1 * ps, 0.2 * ps, 0.3 * ps, 1,
{ {well1_comp1} },
{ { segment.segNumber, segment } },
data::CurrentControl{}
data::CurrentControl{},
data::GuideRateValue{}
};
well1.current_control.isProducer = true;
well1.current_control.prod = ::Opm::Well::ProducerCMode::THP;
well1.guide_rates.set(data::GuideRateValue::Item::Oil, 123.456*sm3_pr_day())
.set(data::GuideRateValue::Item::Gas, 2345.67*sm3_pr_day());
using SegRes = decltype(well1.segments);
using Ctrl = data::CurrentControl;
using GRValue = data::GuideRateValue;
data::Well well2 { rates2, 1.1 * ps, 1.2 * ps, 1.3 * ps, 2, { {well2_comp1 , well2_comp2} }, SegRes{}, Ctrl{} };
data::Well well2 { rates2, 1.1 * ps, 1.2 * ps, 1.3 * ps, 2, { {well2_comp1 , well2_comp2} }, SegRes{}, Ctrl{}, GRValue{} };
well2.current_control.prod = ::Opm::Well::ProducerCMode::ORAT;
well2.guide_rates.set(GRValue::Item::Water, 654.321*sm3_pr_day());
data::Well well3 { rates3, 2.1 * ps, 2.2 * ps, 2.3 * ps, 3, { {well3_comp1} }, SegRes{}, Ctrl{} };
data::Well well3 { rates3, 2.1 * ps, 2.2 * ps, 2.3 * ps, 3, { {well3_comp1} }, SegRes{}, Ctrl{}, GRValue{} };
well3.current_control.isProducer = !w3_injector;
if (! well3.current_control.isProducer) { // W_3 is injector
well3.current_control.inj = ::Opm::Well::InjectorCMode::BHP;
@@ -277,9 +284,13 @@ static data::Wells result_wells(const bool w3_injector = true) {
well3.current_control.prod = ::Opm::Well::ProducerCMode::BHP;
}
data::Well well6 { rates6, 2.1 * ps, 2.2 * ps, 2.3 * ps, 3, { {well6_comp1} }, SegRes{}, Ctrl{} };
well3.guide_rates.set(GRValue::Item::ResV, 355.113*sm3_pr_day());
data::Well well6 { rates6, 2.1 * ps, 2.2 * ps, 2.3 * ps, 3, { {well6_comp1} }, SegRes{}, Ctrl{}, GRValue{} };
well6.current_control.isProducer = false;
well6.current_control.inj = ::Opm::Well::InjectorCMode::GRUP;
well6.guide_rates.set(GRValue::Item::Gas, 222.333*sm3_pr_day())
.set(GRValue::Item::Water, 333.444*sm3_pr_day());
data::Wells wellrates;
@@ -294,13 +305,28 @@ static data::Wells result_wells(const bool w3_injector = true) {
return wellrates;
}
static data::GroupValues result_groups() {
data::GroupValues result_groups()
{
using GRValue = data::GuideRateValue;
data::GroupValues groups;
data::GroupConstraints cgc_group;
cgc_group.set(p_cmode::NONE, i_cmode::VREP, i_cmode::RATE);
groups["G_1"].currentControl = cgc_group;
{
auto& grp = groups["G_1"];
grp.currentControl = cgc_group;
grp.guideRates.production
.set(GRValue::Item::Oil, 1111.2222*sm3_pr_day())
.set(GRValue::Item::Gas, 2222.3333*sm3_pr_day())
.set(GRValue::Item::Water, 3333.4444*sm3_pr_day())
.set(GRValue::Item::ResV, 4444.5555*sm3_pr_day());
grp.guideRates.injection
.set(GRValue::Item::Gas, 9999.8888*sm3_pr_day())
.set(GRValue::Item::Water, 8888.7777*sm3_pr_day());
}
cgc_group.set(p_cmode::ORAT, i_cmode::RESV, i_cmode::FLD);
groups["G_2"].currentControl = cgc_group;
@@ -312,7 +338,6 @@ static data::GroupValues result_groups() {
groups["FIELD"].currentControl = cgc_group;
return groups;
}
std::unique_ptr< EclIO::ESmry > readsum( const std::string& base ) {
@@ -489,6 +514,12 @@ BOOST_AUTO_TEST_CASE(well_keywords) {
BOOST_CHECK_CLOSE( 30.13, ecl_sum_get_well_var( resp, 1, "W_3", "WWPI" ), 1e-5 );
BOOST_CHECK_CLOSE( 60.15, ecl_sum_get_well_var( resp, 1, "W_6", "WGPI" ), 1e-5 );
BOOST_CHECK_CLOSE( 123.456, ecl_sum_get_well_var(resp, 1, "W_1", "WOPGR"), 1.0e-5);
BOOST_CHECK_CLOSE(2345.67 , ecl_sum_get_well_var(resp, 1, "W_1", "WGPGR"), 1.0e-5);
BOOST_CHECK_CLOSE( 654.321, ecl_sum_get_well_var(resp, 1, "W_2", "WWPGR"), 1.0e-5);
BOOST_CHECK_CLOSE( 222.333, ecl_sum_get_well_var(resp, 1, "W_6", "WGIGR"), 1.0e-5);
BOOST_CHECK_CLOSE( 333.444, ecl_sum_get_well_var(resp, 1, "W_6", "WWIGR"), 1.0e-5);
/* Production totals */
BOOST_CHECK_CLOSE( 10.0, ecl_sum_get_well_var( resp, 1, "W_1", "WWPT" ), 1e-5 );
BOOST_CHECK_CLOSE( 20.0, ecl_sum_get_well_var( resp, 1, "W_2", "WWPT" ), 1e-5 );
@@ -712,6 +743,14 @@ BOOST_AUTO_TEST_CASE(group_keywords) {
BOOST_CHECK_CLOSE( 10.16 + 20.16, ecl_sum_get_group_var( resp, 1, "G_1", "GCPR" ), 1e-5 );
BOOST_CHECK_CLOSE( 10.17 + 20.17, ecl_sum_get_group_var( resp, 1, "G_1", "GSPR" ), 1e-5 );
BOOST_CHECK_CLOSE(1111.2222, ecl_sum_get_group_var(resp, 1, "G_1", "GOPGR"), 1.0e-5);
BOOST_CHECK_CLOSE(2222.3333, ecl_sum_get_group_var(resp, 1, "G_1", "GGPGR"), 1.0e-5);
BOOST_CHECK_CLOSE(3333.4444, ecl_sum_get_group_var(resp, 1, "G_1", "GWPGR"), 1.0e-5);
BOOST_CHECK_CLOSE(4444.5555, ecl_sum_get_group_var(resp, 1, "G_1", "GVPGR"), 1.0e-5);
BOOST_CHECK_CLOSE(9999.8888, ecl_sum_get_group_var(resp, 1, "G_1", "GGIGR"), 1.0e-5);
BOOST_CHECK_CLOSE(8888.7777, ecl_sum_get_group_var(resp, 1, "G_1", "GWIGR"), 1.0e-5);
/* Production totals */
BOOST_CHECK_CLOSE( 10.0 + 20.0, ecl_sum_get_group_var( resp, 1, "G_1", "GWPT" ), 1e-5 );
BOOST_CHECK_CLOSE( 10.1 + 20.1, ecl_sum_get_group_var( resp, 1, "G_1", "GOPT" ), 1e-5 );