Merge pull request #1592 from jalvestad/opm_data_Groups

Changes in opm-common for current Production/Injection Group Control parameters
This commit is contained in:
Bård Skaflestad
2020-03-19 16:53:07 +01:00
committed by GitHub
9 changed files with 1159 additions and 64 deletions

View File

@@ -380,6 +380,7 @@ if(ENABLE_ECL_OUTPUT)
tests/test_rst.cpp
tests/test_Solution.cpp
tests/test_Summary.cpp
tests/test_Summary_Group.cpp
tests/test_Tables.cpp
tests/test_Wells.cpp
tests/test_WindowedArray.cpp
@@ -411,6 +412,7 @@ if(ENABLE_ECL_OUTPUT)
tests/SOFR_TEST.DATA
tests/UDQ_TEST_WCONPROD_IUAD-2.DATA
tests/UDQ_ACTIONX_TEST1.DATA
tests/UDQ_ACTIONX_TEST1_U.DATA
tests/include_example_pvt.txt
tests/include_example_summary.txt
tests/include_sgof.txt
@@ -727,6 +729,7 @@ if(ENABLE_ECL_OUTPUT)
opm/output/data/Cells.hpp
opm/output/data/Solution.hpp
opm/output/data/Wells.hpp
opm/output/data/Groups.hpp
opm/output/eclipse/VectorItems/aquifer.hpp
opm/output/eclipse/VectorItems/connection.hpp
opm/output/eclipse/VectorItems/group.hpp

View File

@@ -24,6 +24,7 @@
#include <opm/output/eclipse/Summary.hpp>
#include <opm/output/data/Solution.hpp>
#include <opm/output/data/Wells.hpp>
#include <opm/output/data/Groups.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/SummaryState.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Action/ActionContext.hpp>
@@ -91,6 +92,8 @@ void msim::run_step(const Schedule& schedule, SummaryState& st, data::Solution&
this->simulate(schedule, st, sol, well_data, report_step, seconds_elapsed, time_step);
Opm::data::Group group_data;
seconds_elapsed += time_step;
io.summary().eval(st,
@@ -99,6 +102,7 @@ void msim::run_step(const Schedule& schedule, SummaryState& st, data::Solution&
this->state,
schedule,
well_data,
group_data,
{});
this->output(st,

121
opm/output/data/Groups.hpp Normal file
View File

@@ -0,0 +1,121 @@
/*
Copyright 2016 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 OPM_OUTPUT_GROUPS_HPP
#define OPM_OUTPUT_GROUPS_HPP
#include <algorithm>
#include <cstddef>
#include <initializer_list>
#include <map>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <vector>
#include <opm/parser/eclipse/EclipseState/Schedule/Group/Group.hpp>
namespace Opm {
namespace data {
struct currentGroupConstraints {
Opm::Group::ProductionCMode currentProdConstraint;
Opm::Group::InjectionCMode currentGasInjectionConstraint;
Opm::Group::InjectionCMode currentWaterInjectionConstraint;
template <class MessageBufferType>
void write(MessageBufferType& buffer) const;
template <class MessageBufferType>
void read(MessageBufferType& buffer);
inline currentGroupConstraints& set( Opm::Group::ProductionCMode cpc,
Opm::Group::InjectionCMode cgic,
Opm::Group::InjectionCMode cwic);
inline bool has();
};
class Group : public std::map<std::string, Opm::data::currentGroupConstraints> {
public:
template <class MessageBufferType>
void write(MessageBufferType& buffer) const {
unsigned int size = this->size();
buffer.write(size);
for (const auto& witr : *this) {
const std::string& name = witr.first;
buffer.write(name);
const auto& pi_constr = witr.second;
pi_constr.write(buffer);
}
}
template <class MessageBufferType>
void read(MessageBufferType& buffer) {
unsigned int size;
buffer.read(size);
for (size_t i = 0; i < size; ++i) {
std::string name;
buffer.read(name);
currentGroupConstraints cgc;
cgc.read(buffer);
this->emplace(name, cgc);
}
}
};
/* IMPLEMENTATIONS */
template <class MessageBufferType>
void currentGroupConstraints::write(MessageBufferType& buffer) const {
buffer.write(this->currentProdConstraint);
buffer.write(this->currentGasInjectionConstraint);
buffer.write(this->currentWaterInjectionConstraint);
}
template <class MessageBufferType>
void currentGroupConstraints::read(MessageBufferType& buffer) {
buffer.read(this->currentProdConstraint);
buffer.read(this->currentGasInjectionConstraint);
buffer.read(this->currentWaterInjectionConstraint);
}
inline currentGroupConstraints& currentGroupConstraints::set( Opm::Group::ProductionCMode cpc,
Opm::Group::InjectionCMode cgic,
Opm::Group::InjectionCMode cwic) {
this->currentGasInjectionConstraint = cgic;
this->currentWaterInjectionConstraint = cwic;
this->currentProdConstraint = cpc;
return *this;
}
inline bool currentGroupConstraints::has() {
return ((&this->currentGasInjectionConstraint != nullptr) && (&this->currentGasInjectionConstraint != nullptr)
&& (&this->currentProdConstraint != nullptr));
}
}} // Opm::data
#endif //OPM_OUTPUT_GROUPS_HPP

View File

@@ -20,6 +20,8 @@
#ifndef OPM_OUTPUT_SUMMARY_HPP
#define OPM_OUTPUT_SUMMARY_HPP
#include <opm/parser/eclipse/EclipseState/Schedule/Group/Group.hpp>
#include <map>
#include <memory>
#include <string>
@@ -36,6 +38,7 @@ namespace Opm {
namespace Opm { namespace data {
class WellRates;
class Group;
}} // namespace Opm::data
namespace Opm { namespace out {
@@ -62,12 +65,14 @@ public:
const EclipseState& es,
const Schedule& schedule,
const data::WellRates& well_solution,
const data::Group& group_solution,
const GlobalProcessParameters& single_values,
const RegionParameters& region_values = {},
const BlockValues& block_values = {}) const;
void write() const;
private:
class SummaryImplementation;
std::unique_ptr<SummaryImplementation> pImpl_;

View File

@@ -46,7 +46,7 @@ namespace Opm {
};
enum class Type {
Rate, Total, Ratio, Pressure, Count,
Rate, Total, Ratio, Pressure, Count, Mode,
Undefined,
};

View File

@@ -42,6 +42,7 @@
#include <opm/io/eclipse/OutputStream.hpp>
#include <opm/output/data/Wells.hpp>
#include <opm/output/data/Groups.hpp>
#include <opm/output/eclipse/RegionCache.hpp>
#include <algorithm>
@@ -70,6 +71,31 @@ namespace {
Opm::SummaryConfigNode::Type type;
};
using p_cmode = Opm::Group::ProductionCMode;
const std::map<p_cmode, int> pCModeToPCntlMode = {
{p_cmode::NONE, 0},
{p_cmode::ORAT, 1},
{p_cmode::WRAT, 2},
{p_cmode::GRAT, 3},
{p_cmode::LRAT, 4},
{p_cmode::CRAT, 9},
{p_cmode::RESV, 5},
{p_cmode::PRBL, 6},
{p_cmode::FLD, 0}, // same as NONE
};
using i_cmode = Opm::Group::InjectionCMode;
const std::map<i_cmode, int> iCModeToICntlMode = {
{i_cmode::NONE, 0},
{i_cmode::RATE, 1},
{i_cmode::RESV, 2},
{i_cmode::REIN, 3},
{i_cmode::VREP, 4},
{i_cmode::FLD, 0}, // same as NONE
{i_cmode::SALE, 0}, // not used in E100
};
std::vector<ParamCTorArgs> requiredRestartVectors()
{
using Type = ::Opm::SummaryConfigNode::Type;
@@ -104,7 +130,7 @@ namespace {
ParamCTorArgs{ "WIT" , Type::Total },
ParamCTorArgs{ "GIT" , Type::Total },
ParamCTorArgs{ "WITH", Type::Total },
ParamCTorArgs{ "GITH", Type::Total },
ParamCTorArgs{ "GITH", Type::Total }
};
}
@@ -147,12 +173,37 @@ namespace {
}
for (const auto& grp_name : sched.groupNames()) {
if (grp_name != "FIELD")
if (grp_name != "FIELD") {
makeEntities('G', SN::Category::Group, grp_name);
entities.emplace_back("GMCTP", SN::Category::Group, ::Opm::Location());
entities.back().namedEntity(grp_name)
.parameterType(SN::Type::Mode);
entities.emplace_back("GMCTW", SN::Category::Group, ::Opm::Location());
entities.back().namedEntity(grp_name)
.parameterType(SN::Type::Mode);
entities.emplace_back("GMCTG", SN::Category::Group, ::Opm::Location());
entities.back().namedEntity(grp_name)
.parameterType(SN::Type::Mode);
}
}
makeEntities('F', SN::Category::Field, "FIELD");
entities.emplace_back("FMCTP", SN::Category::Field, ::Opm::Location());
entities.back().namedEntity("FIELD")
.parameterType(SN::Type::Mode);
entities.emplace_back("FMCTW", SN::Category::Field, ::Opm::Location());
entities.back().namedEntity("FIELD")
.parameterType(SN::Type::Mode);
entities.emplace_back("FMCTG", SN::Category::Field, ::Opm::Location());
entities.back().namedEntity("FIELD")
.parameterType(SN::Type::Mode);
return entities;
}
@@ -310,6 +361,7 @@ struct quantity {
}
};
/*
* All functions must have the same parameters, so they're gathered in a struct
* and functions use whatever information they care about.
@@ -319,11 +371,13 @@ struct quantity {
*/
struct fn_args {
const std::vector<Opm::Well>& schedule_wells;
const std::string group_name;
double duration;
const int sim_step;
int num;
const Opm::SummaryState& st;
const Opm::data::Wells& wells;
const Opm::data::Group& group;
const Opm::out::RegionCache& regionCache;
const Opm::EclipseGrid& grid;
const std::vector< std::pair< std::string, double > > eff_factors;
@@ -688,6 +742,70 @@ inline quantity potential_rate( const fn_args& args ) {
return { sum, rate_unit< phase >() };
}
template < bool isGroup, bool Producer, bool waterInjector, bool gasInjector>
inline quantity group_control( const fn_args& args ) {
std::string g_name = "";
if (isGroup) {
const quantity zero = { static_cast<double>(0), Opm::UnitSystem::measure::identity};
if( args.group_name.empty() ) return zero;
g_name = args.group_name;
}
else {
g_name = "FIELD";
}
int cntl_mode = 0;
// production control
if (Producer) {
const auto it_g = args.group.find(g_name);
if (it_g != args.group.end()) {
const auto& value = it_g->second.currentProdConstraint;
auto it_c = pCModeToPCntlMode.find(value);
if (it_c == pCModeToPCntlMode.end()) {
std::stringstream str;
str << "unknown control CMode: " << static_cast<int>(value);
throw std::invalid_argument(str.str());
}
cntl_mode = it_c->second;
}
}
// water injection control
else if (waterInjector){
const auto it_g = args.group.find(g_name);
if (it_g != args.group.end()) {
const auto& value = it_g->second.currentWaterInjectionConstraint;
auto it_c = iCModeToICntlMode.find(value);
if (it_c == iCModeToICntlMode.end()) {
std::stringstream str;
str << "unknown control CMode: " << static_cast<int>(value);
throw std::invalid_argument(str.str());
}
cntl_mode = it_c->second;
}
}
// gas injection control
else if (gasInjector){
const auto it_g = args.group.find(g_name);
if (it_g != args.group.end()) {
const auto& value = it_g->second.currentGasInjectionConstraint;
auto it_c = iCModeToICntlMode.find(value);
if (it_c == iCModeToICntlMode.end()) {
std::stringstream str;
str << "unknown control CMode: " << static_cast<int>(value);
throw std::invalid_argument(str.str());
}
cntl_mode = it_c->second;
}
}
return {static_cast<double>(cntl_mode), Opm::UnitSystem::measure::identity};
}
/*
* A small DSL, really poor man's function composition, to avoid massive
* repetition when declaring the handlers for each individual keyword. bin_op
@@ -831,6 +949,11 @@ static const std::unordered_map< std::string, ofun > funs = {
{ "GOPI", potential_rate< rt::well_potential_oil , false, true>},
{ "GGPI", potential_rate< rt::well_potential_gas , false, true>},
//Group control mode
{ "GMCTP", group_control< true, true, false, false >},
{ "GMCTW", group_control< true, false, true, false >},
{ "GMCTG", group_control< true, false, false, true >},
{ "WWPRH", production_history< Opm::Phase::WATER > },
{ "WOPRH", production_history< Opm::Phase::OIL > },
{ "WGPRH", production_history< Opm::Phase::GAS > },
@@ -1024,6 +1147,11 @@ static const std::unordered_map< std::string, ofun > funs = {
{ "FMWPR", flowing< producer > },
{ "FVPRT", res_vol_production_target },
//Field control mode
{ "FMCTP", group_control< false, true, false, false >},
{ "FMCTW", group_control< false, false, true, false >},
{ "FMCTG", group_control< false, false, false, true >},
/* Region properties */
{ "ROIR" , region_rate< rt::oil, injector > },
{ "RGIR" , region_rate< rt::gas, injector > },
@@ -1358,6 +1486,7 @@ namespace Evaluator {
struct SimulatorResults
{
const Opm::data::WellRates& wellSol;
const Opm::data::Group& groupSol;
const std::map<std::string, double>& single;
const std::map<std::string, std::vector<double>>& region;
const std::map<std::pair<std::string, int>, double>& block;
@@ -1402,13 +1531,15 @@ namespace Evaluator {
// wells apply at this sim_step. Nothing to do.
return;
std::string group_name = this->node_.category() == Opm::SummaryConfigNode::Category::Group ? this->node_.namedEntity() : "";
EfficiencyFactor efac{};
efac.setFactors(this->node_, input.sched, wells, sim_step);
const fn_args args {
wells, stepSize, static_cast<int>(sim_step),
wells, group_name, stepSize, static_cast<int>(sim_step),
std::max(0, this->node_.number()),
st, simRes.wellSol, input.reg, input.grid,
st, simRes.wellSol, simRes.groupSol, input.reg, input.grid,
std::move(efac.factors)
};
@@ -1798,8 +1929,8 @@ namespace Evaluator {
const auto reg = Opm::out::RegionCache{};
const fn_args args {
{}, 0.0, 0, std::max(0, this->node_->number()),
this->st_, {}, reg, this->grid_,
{}, "", 0.0, 0, std::max(0, this->node_->number()),
this->st_, {}, {}, reg, this->grid_,
{}
};
@@ -2028,6 +2159,7 @@ public:
const int sim_step,
const double duration,
const data::WellRates& well_solution,
const data::Group& group_solution,
const GlobalProcessParameters& single_values,
const RegionParameters& region_values,
const BlockValues& block_values,
@@ -2129,6 +2261,7 @@ eval(const EclipseState& es,
const int sim_step,
const double duration,
const data::WellRates& well_solution,
const data::Group& group_solution,
const GlobalProcessParameters& single_values,
const RegionParameters& region_values,
const BlockValues& block_values,
@@ -2139,7 +2272,7 @@ eval(const EclipseState& es,
};
const Evaluator::SimulatorResults simRes {
well_solution, single_values, region_values, block_values
well_solution, group_solution, single_values, region_values, block_values
};
for (auto& evalPtr : this->outputParameters_.getEvaluators()) {
@@ -2404,6 +2537,7 @@ void Summary::eval(SummaryState& st,
const EclipseState& es,
const Schedule& schedule,
const data::WellRates& well_solution,
const data::Group& group_solution,
const GlobalProcessParameters& single_values,
const RegionParameters& region_values,
const BlockValues& block_values) const
@@ -2419,7 +2553,7 @@ void Summary::eval(SummaryState& st,
const auto sim_step = std::max( 0, report_step - 1 );
this->pImpl_->eval(es, schedule, sim_step, duration,
well_solution, single_values,
well_solution, group_solution, single_values,
region_values, block_values, st);
eval_udq(schedule, sim_step, st);

View File

@@ -0,0 +1,526 @@
-- This reservoir simulation deck is made available under the Open Database
-- License: http://opendatacommons.org/licenses/odbl/1.0/. Any rights in
-- individual contents of the database are licensed under the Database Contents
-- License: http://opendatacommons.org/licenses/dbcl/1.0/
-- Copyright (C) 2018 Equinor
-- This deck uses User defined quantities (UDQ) together with the ACTIONX
-- keyword to shut well with highest water cut when number of wells on stream
-- exceeds a defined number.
--------------------------------------------------------
-- *****************************************************
RUNSPEC
-- *****************************************************
-- Simulation run title
TITLE
Generic Reservoir
NOECHO
--
-- ----------------------------------------------------
-- Simulation grid dimension (Imax, Jmax, Kmax)
DIMENS
3 5 4 /
--
-- ----------------------------------------------------
-- Simulation run start
START
22 'AUG' 2018 /
--
-- ----------------------------------------------------
--Activate "Data Check Only" option
--NOSIM
--
--
-- ----------------------------------------------------
-- Fluid phases present
OIL
GAS
WATER
DISGAS
--
-- ----------------------------------------------------
-- Measurement unit used
METRIC
--
--
--Table dimensions
TABDIMS
-- NTSFUN NTPVT NSSFUN NPPVT NTFIP NRPVT
1 1 130 24 1 20 /
--
-- ----------------------------------------------------
-- Dimensions for equilibration tables
EQLDIMS
2 100 20 /
--
--
-- ----------------------------------------------------
--Dimension for well data
WELLDIMS
6 5 5 6 /
--Dimensions for ACTIONX data
ACTDIMS
4 20 80 4 /
--
--
--
-- ----------------------------------------------------
-- Input and output files format
--FMTIN
--FMTOUT
UNIFIN
UNIFOUT
-- Dimensions for used defined quantity facility
-- max functions permitted in a quantity definition
-- max arguments permitted in a quantity definition
-- max user defined connection quantities
-- max user defined field quantities
-- max user defined group quantities
-- max user defined region quantities
-- max user defined segment quantities
-- max user defined well quantities
-- max user defined aquifer quantities
-- max user defined block quantities
-- whether new randon number generator seed computed for restart runs
UDQDIMS
50 25 0 50 50 0 0 50 0 20 /
-- Dimensions for the user defined arguments facility
-- number of keyword arguments in which UDQs replace numerical values
-- ratained for back-compatibility
-- total number of unique instances in which a UDQ is used in a keyword argument
UDADIMS
10 1* 10 /
--PARALLEL
-- 2 /
-- *************************************************************************
-- In this section simulation grid and static reservoir parameters are given
-- *************************************************************************
GRID
-- ****************************************************
-------------------------------------------------------
--
--Disable echoing of the input file
NOECHO
--
--Requests output of an INIT file
INIT
--
--Control output of the Grid geometry file
GRIDFILE
0 1 /
--
--Input of pre-processor map origin (X1, Y1, X2, Y2, X3, Y3)
--X1 Y1 The X and Y coordinates of one point of the grid Y-axis relative to the map
--X2 Y2 The X and Y coordinates of the grid origin relative to the map origin
--X3 Y3 The X and Y coordinates of one point of the grid X-axis relative to the map
MAPAXES
0.0 100.0 0.0 0.0 100.0 0.0 /
--
--
NOECHO
--
-- ----------------------------------------------------
--Include simulation grid
INCLUDE
'include_grid_3x5x4.grdecl' /
PORO
15*0.25
15*0.20
15*0.23
15*0.18
/
PERMX
15*500
15*100
15*1000
15*250
/
-- ---------------------------------------------------
-- Copy PERMX to PERMY & PERMZ
COPY
PERMX PERMY /
PERMX PERMZ /
/
--
-- ---------------------------------------------------
-- Set Kv/Kh
MULTIPLY
PERMZ 0.1 /
/
MULTZ
15*1.0
15*0.0
30*1.0
/
-- ***************************************************
-- In this section simulation grid parameters are edited
-- ***************************************************
EDIT
-- ***************************************************
-- ***************************************************
-- In this section fluid-rock properties and
-- relative permabilities are given
-- ***************************************************
PROPS
-- ***************************************************
INCLUDE
'include_sgof.txt' /
INCLUDE
'include_swof.txt' /
-- ---------------------------------------------------
-- Include PVT data
INCLUDE
'include_example_pvt.txt' /
-- ***********************************************************
-- In this section simulation grid region parameters are given
-- ***********************************************************
REGIONS
-- ***************************************************
EQLNUM
30*1 30*2 /
--
--
-- ***************************************************
-- In this section the initialization parameters and
-- dynamic parameters are defined
-- ***************************************************
SOLUTION
-- ***************************************************
------------------------------------------------------
--
--Simulation model initialisation data
--
-- DATUM DATUM OWC OWC GOC GOC RSVD RVVD SOLN
-- Depth Pres. Depth Pcow Depth Pcog Table Table Method
EQUIL
2030 382.4 2030 0.0 500 0.0 1 1 0 /
2050 382.4 2050 0.0 500 0.0 1 1 0 /
--
-- ---------------------------------------------------
-- Dissolved gas-oil ratio versus depth,
RSVD
1500 180.0
4000 180.0 /
1500 180.0
4000 180.0 /
-- ---------------------------------------------------
--Controls on output to the RESTART file
--RPTRST
-- ALLPROPS=2 BASIC=2 FIP /
RPTRST
'BASIC=2' 'PBPD' /
--
-- **************************************************************************************
-- In this section simulation output data to be written to sumTESTy file are defined
-- **************************************************************************************
SUMMARY
-- ***************************************************
-- ---------------------------------------------------
-- Summary data to be written to summary file
--
--
-- **************************************************************************************
-- In this section data required to describe history and prediction is given
-- - well completions, well production/injection, well constraints
-- - platform/production unit constraints, etc.
-- **************************************************************************************
INCLUDE
'include_example_summary.txt' /
FMWIN
FMWPR
GMWPR
'UPPER' 'LOWER' /
GMWIN
'UPPER' 'LOWER' /
WUPR1
'OP*' /
WUPR3
'OP*' /
FMCTP
FMCTW
FMCTG
GMCTP
/
GMCTW
/
GMCTG
/
SCHEDULE
GRUPTREE
'UPPER' 'TEST' /
'LOWER' 'TEST' /
/
-- ***************************************************
WELSPECS
'OPU01' 'UPPER' 1 2 2002 'OIL' 0.00 'STD' 'SHUT' 'YES' 0 'SEG' /
'OPU02' 'UPPER' 1 4 2002 'OIL' 0.00 'STD' 'SHUT' 'YES' 0 'SEG' /
'OPL01' 'LOWER' 1 2 2025 'OIL' 0.00 'STD' 'SHUT' 'YES' 0 'SEG' /
'OPL02' 'LOWER' 1 4 2025 'OIL' 0.00 'STD' 'SHUT' 'YES' 0 'SEG' /
/
WELSPECS
'WIU01' 'UPPER' 3 3 2030 'WATER' 0.00 'STD' 'SHUT' 'YES' 0 'SEG' /
'WIL01' 'LOWER' 3 3 2050 'WATER' 0.00 'STD' 'SHUT' 'YES' 0 'SEG' /
/
COMPDAT
-- --------------------------------------------------------------------------------------------------
'OPU01' 1 2 1 1 'OPEN' 0 1* 0.241 1* 2.50 0.0 'Z' 1* /
'OPU02' 1 4 1 1 'OPEN' 0 1* 0.241 1* 2.50 0.0 'Z' 1* /
'OPL01' 1 2 3 3 'OPEN' 0 1* 0.241 1* 2.50 0.0 'Z' 1* /
'OPL02' 1 4 3 3 'OPEN' 0 1* 0.241 1* 2.50 0.0 'Z' 1* /
'WIU01' 3 3 2 2 'OPEN' 0 1* 0.241 1* 2.50 0.0 'Z' 1* /
'WIL01' 3 3 4 4 'OPEN' 0 1* 0.241 1* 2.50 0.0 'Z' 1* /
/
-- Well production rate targets/limits:
WCONPROD
-- name status ctrl qo qw qg ql qr bhp thp vfp alq
'OPU*' 'SHUT' 'GRUP' 1500. 1* 1* 2500. 1* 60.0 / single wells
'OPL*' 'SHUT' 'GRUP' 1500. 1* 1* 2500. 1* 60.0 / single wells
/
WCONINJE
-- name inj type status ctrl surface_qw res_qw BHPmax
'WIU*' 'WATER' 'SHUT' 'GRUP' 10500. 1* 500. /
'WIL*' 'WATER' 'SHUT' 'GRUP' 10500. 1* 500. /
/
UDQ
-- WUPR3 sorts production wells from poorest (highest wct) to best. ACTIONX will shut #1 in this list
DEFINE WUPR1 1/(0.01 + WWCT 'OP*') /
DEFINE WUPR3 SORTA(WUPR1) /
-- units ignored
/
--start files/gconprod0.tmpl
GCONPROD
'TEST' 'LRAT' 6000 1* 1* 6000 'RATE' 'NO' 9* /
'LOWER' 'FLD' 6000 1* 1* 6000 'RATE' 'YES' 1* 'FORM' 7* /
'UPPER' 'FLD' 3000 1* 1* 6000 'RATE' 'YES' 1* 'FORM' 7* /
/
GCONINJE
'LOWER' 'WATER' 'VREP' 3* 1.2 /
'UPPER' 'WATER' 'VREP' 3* 2.0 /
/
-- Well proportions
GUIDERAT
-- int phase A B C D E F incr. damp
0 'OIL' 1 0.5 1 1 0 0 'YES' 0.5 / equal to 1/(0.5+WWCT)
--start files/actionxprod.tmpl
ACTIONX
ACT01 10 /
WWPR 'OP*' > 17 OR /
GMWPR 'T*' > 14 AND /
DAY > 3 /
/
WELOPEN
'?' SHUT 0 0 0 2* /
/
ENDACTIO
ACTIONX
ACT02 11 /
FMWPR > 25 AND /
WGPR 'OPL02' > GGPR 'LOWER' AND /
MNTH > NOV /
/
WELOPEN
'?' 'SHUT' 0 0 0 2* /
/
WELOPEN
'OPL01' 'OPEN' 5* /
/
ENDACTIO
DATES
1 'SEP' 2018 /
/
ACTIONX
ACT03 13 /
WWPR 'OPU02' > WWPR 'OPU01' OR /
GMWPR 'T*' > 39 AND /
YEAR > 2019 /
/
WELOPEN
'?' SHUT 0 0 0 2* /
/
ENDACTIO
--start files/actionxprod.tmpl
ACTIONX
ACT01 10 /
FMWPR > 45 AND /
WUPR3 'OP*' > 46 OR /
MNTH > OCT /
/
WELOPEN
'?' SHUT 0 0 0 2* /
/
WELOPEN
'OPU02' 'OPEN' 5* /
/
WELOPEN
'OPL02' 'OPEN' 5* /
/
ENDACTIO
WELOPEN
'OPL01' 'OPEN' 5* /
/
DATES
1 'OCT' 2018 /
/
WELOPEN
'WIL01' 'OPEN' 5* /
/
DATES
1 'NOV' 2018 /
/
END
WELOPEN
'OPL02' 'OPEN' 5* /
/
DATES
1 'DEC' 2018 /
/
WELOPEN
'OPU01' 'OPEN' 5* /
/
DATES
1 'MAY' 2019 /
/
WELOPEN
'OPU02' 'OPEN' 5* /
/
DATES
1 'JUN' 2019 /
/
WELOPEN
'WIU01' 'OPEN' 5* /
/
DATES
1 'JAN' 2021 /
/

View File

@@ -32,6 +32,7 @@
#include <ctime>
#include <opm/output/data/Wells.hpp>
#include <opm/output/data/Groups.hpp>
#include <opm/output/eclipse/Summary.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/SummaryState.hpp>
@@ -51,6 +52,8 @@
using namespace Opm;
using rt = data::Rates::opt;
using p_cmode = Opm::Group::ProductionCMode;
using i_cmode = Opm::Group::InjectionCMode;
namespace {
double sm3_pr_day()
@@ -271,6 +274,27 @@ static data::Wells result_wells() {
return wellrates;
}
static data::Group result_groups() {
data::Group groups;
data::currentGroupConstraints cgc_group;
cgc_group.set(p_cmode::NONE, i_cmode::VREP, i_cmode::RATE);
groups.emplace("G_1", cgc_group);
cgc_group.set(p_cmode::ORAT, i_cmode::RESV, i_cmode::FLD);
groups.emplace("G_2", cgc_group);
cgc_group.set(p_cmode::GRAT, i_cmode::REIN, i_cmode::VREP);
groups.emplace("G_3", cgc_group);
cgc_group.set(p_cmode::NONE, i_cmode::NONE, i_cmode::NONE);
groups.emplace("FIELD", cgc_group);
return groups;
}
std::unique_ptr< EclIO::ESmry > readsum( const std::string& base ) {
return std::make_unique<EclIO::ESmry>(base);
}
@@ -349,6 +373,7 @@ struct setup {
Schedule schedule;
SummaryConfig config;
data::Wells wells;
data::Group groups;
std::string name;
WorkArea ta;
@@ -361,6 +386,7 @@ struct setup {
schedule( deck, es),
config( deck, schedule, es.getTableManager()),
wells( result_wells() ),
groups( result_groups() ),
name( toupper(std::move(fname)) ),
ta( "summary_test" )
{}
@@ -383,13 +409,13 @@ BOOST_AUTO_TEST_CASE(well_keywords) {
SummaryState st(std::chrono::system_clock::now());
out::Summary writer( cfg.es, cfg.config, cfg.grid, cfg.schedule , cfg.name );
writer.eval(st, 0, 0*day, cfg.es, cfg.schedule, cfg.wells, {});
writer.eval(st, 0, 0*day, cfg.es, cfg.schedule, cfg.wells, cfg.groups, {});
writer.add_timestep( st, 0);
writer.eval(st, 1, 1*day, cfg.es, cfg.schedule, cfg.wells, {});
writer.eval(st, 1, 1*day, cfg.es, cfg.schedule, cfg.wells, cfg.groups, {});
writer.add_timestep( st, 1);
writer.eval(st, 2, 2*day, cfg.es, cfg.schedule, cfg.wells, {});
writer.eval(st, 2, 2*day, cfg.es, cfg.schedule, cfg.wells, cfg.groups, {});
writer.add_timestep( st, 2);
writer.write();
@@ -598,11 +624,11 @@ BOOST_AUTO_TEST_CASE(udq_keywords) {
out::Summary writer( cfg.es, cfg.config, cfg.grid, cfg.schedule , cfg.name );
SummaryState st(std::chrono::system_clock::now());
writer.eval( st, 0, 0 * day, cfg.es, cfg.schedule, cfg.wells , {});
writer.eval( st, 0, 0 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, {});
writer.add_timestep( st, 0);
writer.eval( st, 1, 1 * day, cfg.es, cfg.schedule, cfg.wells , {});
writer.eval( st, 1, 1 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, {});
writer.add_timestep( st, 1);
writer.eval( st, 2, 2 * day, cfg.es, cfg.schedule, cfg.wells , {});
writer.eval( st, 2, 2 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, {});
writer.add_timestep( st, 2);
writer.write();
@@ -623,13 +649,13 @@ BOOST_AUTO_TEST_CASE(group_keywords) {
out::Summary writer( cfg.es, cfg.config, cfg.grid, cfg.schedule, cfg.name );
SummaryState st(std::chrono::system_clock::now());
writer.eval( st, 0, 0 * day, cfg.es, cfg.schedule, cfg.wells , {});
writer.eval( st, 0, 0 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, {});
writer.add_timestep( st, 0);
writer.eval( st, 1, 1 * day, cfg.es, cfg.schedule, cfg.wells , {});
writer.eval( st, 1, 1 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, {});
writer.add_timestep( st, 1);
writer.eval( st, 2, 2 * day, cfg.es, cfg.schedule, cfg.wells , {});
writer.eval( st, 2, 2 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, {});
writer.add_timestep( st, 2);
writer.write();
@@ -767,11 +793,11 @@ BOOST_AUTO_TEST_CASE(group_group) {
out::Summary writer( cfg.es, cfg.config, cfg.grid, cfg.schedule, cfg.name );
SummaryState st(std::chrono::system_clock::now());
writer.eval( st, 0, 0 * day, cfg.es, cfg.schedule, cfg.wells , {});
writer.eval( st, 0, 0 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, {});
writer.add_timestep( st, 0);
writer.eval( st, 1, 1 * day, cfg.es, cfg.schedule, cfg.wells , {});
writer.eval( st, 1, 1 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, {});
writer.add_timestep( st, 1);
writer.eval( st, 2, 2 * day, cfg.es, cfg.schedule, cfg.wells , {});
writer.eval( st, 2, 2 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, {});
writer.add_timestep( st, 2);
writer.write();
@@ -823,11 +849,11 @@ BOOST_AUTO_TEST_CASE(completion_kewords) {
out::Summary writer( cfg.es, cfg.config, cfg.grid, cfg.schedule, cfg.name );
SummaryState st(std::chrono::system_clock::now());
writer.eval( st, 0, 0 * day, cfg.es, cfg.schedule, cfg.wells , {});
writer.eval( st, 0, 0 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, {});
writer.add_timestep( st, 0);
writer.eval( st, 1, 1 * day, cfg.es, cfg.schedule, cfg.wells , {});
writer.eval( st, 1, 1 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, {});
writer.add_timestep( st, 1);
writer.eval( st, 2, 2 * day, cfg.es, cfg.schedule, cfg.wells , {});
writer.eval( st, 2, 2 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, {});
writer.add_timestep( st, 2);
writer.write();
@@ -885,11 +911,11 @@ BOOST_AUTO_TEST_CASE(field_keywords) {
out::Summary writer( cfg.es, cfg.config, cfg.grid, cfg.schedule, cfg.name );
SummaryState st(std::chrono::system_clock::now());
writer.eval( st, 0, 0 * day, cfg.es, cfg.schedule, cfg.wells , {});
writer.eval( st, 0, 0 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, {});
writer.add_timestep( st, 0);
writer.eval( st, 1, 1 * day, cfg.es, cfg.schedule, cfg.wells , {});
writer.eval( st, 1, 1 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, {});
writer.add_timestep( st, 1);
writer.eval( st, 2, 2 * day, cfg.es, cfg.schedule, cfg.wells , {});
writer.eval( st, 2, 2 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, {});
writer.add_timestep( st, 2);
writer.write();
@@ -1015,11 +1041,11 @@ BOOST_AUTO_TEST_CASE(report_steps_time) {
out::Summary writer( cfg.es, cfg.config, cfg.grid, cfg.schedule, cfg.name );
SummaryState st(std::chrono::system_clock::now());
writer.eval( st, 1, 2 * day, cfg.es, cfg.schedule, cfg.wells , {});
writer.eval( st, 1, 2 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, {});
writer.add_timestep( st, 1);
writer.eval( st, 1, 5 * day, cfg.es, cfg.schedule, cfg.wells , {});
writer.eval( st, 1, 5 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, {});
writer.add_timestep( st, 1);
writer.eval( st, 2, 10 * day, cfg.es, cfg.schedule, cfg.wells , {});
writer.eval( st, 2, 10 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, {});
writer.add_timestep( st, 2);
writer.write();
@@ -1042,11 +1068,11 @@ BOOST_AUTO_TEST_CASE(skip_unknown_var) {
out::Summary writer( cfg.es, cfg.config, cfg.grid, cfg.schedule, cfg.name );
SummaryState st(std::chrono::system_clock::now());
writer.eval( st, 1, 2 * day, cfg.es, cfg.schedule, cfg.wells , {});
writer.eval( st, 1, 2 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, {});
writer.add_timestep( st, 1);
writer.eval( st, 1, 5 * day, cfg.es, cfg.schedule, cfg.wells , {});
writer.eval( st, 1, 5 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, {});
writer.add_timestep( st, 1);
writer.eval( st, 2, 10 * day, cfg.es, cfg.schedule, cfg.wells , {});
writer.eval( st, 2, 10 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, {});
writer.add_timestep( st, 2);
writer.write();
@@ -1153,11 +1179,11 @@ BOOST_AUTO_TEST_CASE(region_vars) {
{
out::Summary writer( cfg.es, cfg.config, cfg.grid, cfg.schedule, cfg.name );
SummaryState st(std::chrono::system_clock::now());
writer.eval( st, 1, 2 * day, cfg.es, cfg.schedule, cfg.wells, {}, region_values);
writer.eval( st, 1, 2 * day, cfg.es, cfg.schedule, cfg.wells, cfg.groups, {}, region_values);
writer.add_timestep( st, 1);
writer.eval( st, 1, 5 * day, cfg.es, cfg.schedule, cfg.wells, {}, region_values);
writer.eval( st, 1, 5 * day, cfg.es, cfg.schedule, cfg.wells, cfg.groups, {}, region_values);
writer.add_timestep( st, 1);
writer.eval( st, 2, 10 * day, cfg.es, cfg.schedule, cfg.wells, {}, region_values);
writer.eval( st, 2, 10 * day, cfg.es, cfg.schedule, cfg.wells, cfg.groups, {}, region_values);
writer.add_timestep( st, 2);
writer.write();
}
@@ -1204,11 +1230,11 @@ BOOST_AUTO_TEST_CASE(region_production) {
{
out::Summary writer( cfg.es, cfg.config, cfg.grid, cfg.schedule, cfg.name );
SummaryState st(std::chrono::system_clock::now());
writer.eval( st, 0, 0 * day, cfg.es, cfg.schedule, cfg.wells , {});
writer.eval( st, 0, 0 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, {});
writer.add_timestep( st, 0);
writer.eval( st, 1, 1 * day, cfg.es, cfg.schedule, cfg.wells , {});
writer.eval( st, 1, 1 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, {});
writer.add_timestep( st, 1);
writer.eval( st, 2, 2 * day, cfg.es, cfg.schedule, cfg.wells , {});
writer.eval( st, 2, 2 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, {});
writer.add_timestep( st, 2);
writer.write();
}
@@ -1236,11 +1262,11 @@ BOOST_AUTO_TEST_CASE(region_injection) {
out::Summary writer( cfg.es, cfg.config, cfg.grid, cfg.schedule, cfg.name );
SummaryState st(std::chrono::system_clock::now());
writer.eval( st, 0, 0 * day, cfg.es, cfg.schedule, cfg.wells , {});
writer.eval( st, 0, 0 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, {});
writer.add_timestep( st, 0);
writer.eval( st, 1, 1 * day, cfg.es, cfg.schedule, cfg.wells , {});
writer.eval( st, 1, 1 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, {});
writer.add_timestep( st, 1);
writer.eval( st, 2, 2 * day, cfg.es, cfg.schedule, cfg.wells , {});
writer.eval( st, 2, 2 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, {});
writer.add_timestep( st, 2);
writer.write();
@@ -1292,15 +1318,15 @@ BOOST_AUTO_TEST_CASE(BLOCK_VARIABLES) {
out::Summary writer( cfg.es, cfg.config, cfg.grid, cfg.schedule, cfg.name );
SummaryState st(std::chrono::system_clock::now());
writer.eval( st, 0, 0 * day, cfg.es, cfg.schedule, cfg.wells , {},{}, block_values);
writer.eval( st, 0, 0 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, {},{}, block_values);
writer.add_timestep( st, 0);
writer.eval( st, 1, 1 * day, cfg.es, cfg.schedule, cfg.wells , {},{}, block_values);
writer.eval( st, 1, 1 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, {},{}, block_values);
writer.add_timestep( st, 1);
writer.eval( st, 2, 2 * day, cfg.es, cfg.schedule, cfg.wells , {},{}, block_values);
writer.eval( st, 2, 2 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, {},{}, block_values);
writer.add_timestep( st, 2);
writer.eval( st, 3, 2 * day, cfg.es, cfg.schedule, cfg.wells , {},{}, block_values);
writer.eval( st, 3, 2 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, {},{}, block_values);
writer.add_timestep( st, 3);
writer.eval( st, 4, 2 * day, cfg.es, cfg.schedule, cfg.wells , {},{}, block_values);
writer.eval( st, 4, 2 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, {},{}, block_values);
writer.add_timestep( st, 4);
writer.write();
@@ -1389,11 +1415,11 @@ BOOST_AUTO_TEST_CASE(MISC) {
out::Summary writer( cfg.es, cfg.config, cfg.grid, cfg.schedule , cfg.name );
SummaryState st(std::chrono::system_clock::now());
writer.eval( st, 0, 0 * day, cfg.es, cfg.schedule, cfg.wells , {});
writer.eval( st, 0, 0 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, {});
writer.add_timestep( st, 0);
writer.eval( st, 1, 1 * day, cfg.es, cfg.schedule, cfg.wells , {});
writer.eval( st, 1, 1 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, {});
writer.add_timestep( st, 1);
writer.eval( st, 2, 2 * day, cfg.es, cfg.schedule, cfg.wells , {});
writer.eval( st, 2, 2 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, {});
writer.add_timestep( st, 2);
writer.write();
@@ -1409,19 +1435,19 @@ BOOST_AUTO_TEST_CASE(EXTRA) {
{
out::Summary writer( cfg.es, cfg.config, cfg.grid, cfg.schedule , cfg.name );
SummaryState st(std::chrono::system_clock::now());
writer.eval( st, 0, 0 * day, cfg.es, cfg.schedule, cfg.wells , { {"TCPU" , 0 }});
writer.eval( st, 0, 0 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, { {"TCPU" , 0 }});
writer.add_timestep( st, 0);
writer.eval( st, 1, 1 * day, cfg.es, cfg.schedule, cfg.wells , { {"TCPU" , 1 }});
writer.eval( st, 1, 1 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, { {"TCPU" , 1 }});
writer.add_timestep( st, 1);
writer.eval( st, 2, 2 * day, cfg.es, cfg.schedule, cfg.wells , { {"TCPU" , 2}});
writer.eval( st, 2, 2 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, { {"TCPU" , 2}});
writer.add_timestep( st, 2);
/* Add a not-recognized key; that is OK */
BOOST_CHECK_NO_THROW( writer.eval( st, 3, 3 * day, cfg.es, cfg.schedule, cfg.wells , { {"MISSING" , 2 }}));
BOOST_CHECK_NO_THROW( writer.eval( st, 3, 3 * day, cfg.es, cfg.schedule, cfg.wells , cfg.groups, { {"MISSING" , 2 }}));
BOOST_CHECK_NO_THROW( writer.add_timestep( st, 3));
/* Override a NOT MISC variable - ignored. */
writer.eval( st, 4, 4 * day, cfg.es, cfg.schedule, cfg.wells, {});
writer.eval( st, 4, 4 * day, cfg.es, cfg.schedule, cfg.wells, cfg.groups, {});
writer.add_timestep( st, 4);
writer.write();
}
@@ -1513,11 +1539,11 @@ BOOST_AUTO_TEST_CASE(efficiency_factor) {
out::Summary writer( cfg.es, cfg.config, cfg.grid, cfg.schedule, cfg.name );
SummaryState st(std::chrono::system_clock::now());
writer.eval( st, 0, 0 * day, cfg.es, cfg.schedule, cfg.wells, {});
writer.eval( st, 0, 0 * day, cfg.es, cfg.schedule, cfg.wells, cfg.groups, {});
writer.add_timestep( st, 0);
writer.eval( st, 1, 1 * day, cfg.es, cfg.schedule, cfg.wells, {});
writer.eval( st, 1, 1 * day, cfg.es, cfg.schedule, cfg.wells, cfg.groups, {});
writer.add_timestep( st, 1);
writer.eval( st, 2, 2 * day, cfg.es, cfg.schedule, cfg.wells, {});
writer.eval( st, 2, 2 * day, cfg.es, cfg.schedule, cfg.wells, cfg.groups, {});
writer.add_timestep( st, 2);
writer.write();
auto res = readsum( cfg.name );
@@ -1650,11 +1676,11 @@ namespace {
};
SummaryState st(std::chrono::system_clock::now());
smry.eval(st, 0, 0*day, config.es, config.schedule, config.wells, {});
smry.eval(st, 0, 0*day, config.es, config.schedule, config.wells, config.groups, {});
smry.add_timestep(st, 0);
smry.eval(st, 1, 1*day, config.es, config.schedule, config.wells, {});
smry.eval(st, 1, 1*day, config.es, config.schedule, config.wells, config.groups, {});
smry.add_timestep(st, 1);
smry.eval(st, 2, 2*day, config.es, config.schedule, config.wells, {});
smry.eval(st, 2, 2*day, config.es, config.schedule, config.wells, config.groups, {});
smry.add_timestep(st, 2);
return st;
@@ -2646,11 +2672,11 @@ BOOST_AUTO_TEST_CASE(Write_Read)
};
SummaryState st(std::chrono::system_clock::now());
writer.eval(st, 0, 0*day, config.es, config.schedule, config.wells, {});
writer.eval(st, 0, 0*day, config.es, config.schedule, config.wells, config.groups, {});
writer.add_timestep(st, 0);
writer.eval(st, 1, 1*day, config.es, config.schedule, config.wells, {});
writer.eval(st, 1, 1*day, config.es, config.schedule, config.wells, config.groups, {});
writer.add_timestep(st, 1);
writer.eval(st, 2, 2*day, config.es, config.schedule, config.wells, {});
writer.eval(st, 2, 2*day, config.es, config.schedule, config.wells, config.groups, {});
writer.add_timestep(st, 2);
writer.write();

View File

@@ -0,0 +1,276 @@
/*
Copyright 2016 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 "config.h"
#define BOOST_TEST_MODULE Wells
#include <boost/test/unit_test.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <cstddef>
#include <exception>
#include <memory>
#include <stdexcept>
#include <unordered_map>
#include <cctype>
#include <ctime>
#include <opm/output/data/Wells.hpp>
#include <opm/output/data/Groups.hpp>
#include <opm/output/eclipse/Summary.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/SummaryState.hpp>
#include <opm/parser/eclipse/Deck/Deck.hpp>
#include <opm/parser/eclipse/Units/UnitSystem.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
#include <opm/parser/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp>
#include <opm/parser/eclipse/Parser/Parser.hpp>
#include <opm/parser/eclipse/Units/Units.hpp>
#include <opm/io/eclipse/ESmry.hpp>
#include <tests/WorkArea.cpp>
using namespace Opm;
using rt = data::Rates::opt;
namespace {
double sm3_pr_day()
{
return unit::cubic(unit::meter) / unit::day;
}
std::string toupper(std::string input)
{
for (auto& c : input) {
const auto uc = std::toupper(static_cast<unsigned char>(c));
c = static_cast<std::string::value_type>(uc);
}
return input;
}
bool ecl_sum_has_group_var( const EclIO::ESmry* smry,
const std::string& groupname,
const std::string& variable )
{
return smry->hasKey(variable + ':' + groupname);
}
double ecl_sum_get_group_var( const EclIO::ESmry* smry,
const int timeIdx,
const std::string& groupname,
const std::string& variable )
{
return smry->get(variable + ':' + groupname)[timeIdx];
}
} // Anonymous
namespace {
/* conversion factor for whenever 'day' is the unit of measure, whereas we
* expect input in SI units (seconds)
*/
std::unique_ptr< EclIO::ESmry > readsum( const std::string& base ) {
return std::make_unique<EclIO::ESmry>(base);
}
using p_cmode = Opm::Group::ProductionCMode;
using i_cmode = Opm::Group::InjectionCMode;
static const int day = 24 * 60 * 60;
static data::Wells result_wells() {
/* populate with the following pattern:
*
* Wells are named W_1, W_2 etc, i.e. wells are 1 indexed.
*
* rates on a well are populated with 10 * wellidx . type (where type is
* 0-1-2 from owg)
*
* bhp is wellidx.1
* bhp is wellidx.2
*
* completions are 100*wellidx . type
*/
// conversion factor Pascal (simulator output) <-> barsa
const double ps = 100000;
data::Rates rates1;
rates1.set( rt::wat, -10.0 / day );
rates1.set( rt::oil, -10.1 / day );
rates1.set( rt::gas, -10.2 / day );
rates1.set( rt::solvent, -10.3 / day );
rates1.set( rt::dissolved_gas, -10.4 / day );
rates1.set( rt::vaporized_oil, -10.5 / day );
rates1.set( rt::reservoir_water, -10.6 / day );
rates1.set( rt::reservoir_oil, -10.7 / day );
rates1.set( rt::reservoir_gas, -10.8 / day );
rates1.set( rt::productivity_index_water, -10.9 / day );
rates1.set( rt::productivity_index_oil, -10.11 / day );
rates1.set( rt::productivity_index_gas, -10.12 / day );
rates1.set( rt::well_potential_water, -10.13 / day );
rates1.set( rt::well_potential_oil, -10.14 / day );
rates1.set( rt::well_potential_gas, -10.15 / day );
/* completion rates */
data::Rates crates1;
crates1.set( rt::wat, -100.0 / day );
crates1.set( rt::oil, -100.1 / day );
crates1.set( rt::gas, -100.2 / day );
crates1.set( rt::solvent, -100.3 / day );
crates1.set( rt::dissolved_gas, -100.4 / day );
crates1.set( rt::vaporized_oil, -100.5 / day );
crates1.set( rt::reservoir_water, -100.6 / day );
crates1.set( rt::reservoir_oil, -100.7 / day );
crates1.set( rt::reservoir_gas, -100.8 / day );
// Segment vectors
auto segment = ::Opm::data::Segment{};
segment.rates.set(rt::wat, 123.45*sm3_pr_day());
segment.rates.set(rt::oil, 543.21*sm3_pr_day());
segment.rates.set(rt::gas, 1729.496*sm3_pr_day());
segment.pressure = 314.159*unit::barsa;
segment.segNumber = 1;
/*
The global index assigned to the completion must be manually
syncronized with the global index in the COMPDAT keyword in the
input deck.
*/
data::Connection well1_comp1 { 0 , crates1, 1.9 , 123.4, 314.15, 0.35, 0.25, 2.718e2};
/*
The completions
*/
data::Well well1 {
rates1, 0.1 * ps, 0.2 * ps, 0.3 * ps, 1,
{ {well1_comp1} },
{ { segment.segNumber, segment } },
data::CurrentControl{}
};
well1.current_control.isProducer = false;
well1.current_control.inj =::Opm::Well::InjectorCMode::BHP;
data::Wells wellrates;
wellrates["OPU01"] = well1;
return wellrates;
}
static data::Group result_groups() {
data::Group groups;
data::currentGroupConstraints cgc_group;
cgc_group.set(p_cmode::NONE, i_cmode::VREP, i_cmode::RATE);
groups.emplace("TEST", cgc_group);
cgc_group.set(p_cmode::ORAT, i_cmode::RESV, i_cmode::REIN);
groups.emplace("LOWER", cgc_group);
cgc_group.set(p_cmode::GRAT, i_cmode::REIN, i_cmode::VREP);
groups.emplace("UPPER", cgc_group);
cgc_group.set(p_cmode::NONE, i_cmode::NONE, i_cmode::NONE);
groups.emplace("FIELD", cgc_group);
return groups;
}
struct setup {
Deck deck;
EclipseState es;
const EclipseGrid& grid;
Schedule schedule;
SummaryConfig config;
data::Wells wells;
data::Group groups;
std::string name;
WorkArea ta;
/*-----------------------------------------------------------------*/
setup(std::string fname, const std::string& path = "UDQ_ACTIONX_TEST1_U.DATA") :
deck( Parser().parseFile( path) ),
es( deck ),
grid( es.getInputGrid() ),
schedule( deck, es),
config( deck, schedule, es.getTableManager()),
wells( result_wells() ),
groups( result_groups() ),
name( toupper(std::move(fname)) ),
ta( "test_summary_group_constraints" )
{}
};
} // Anonymous namespace
BOOST_AUTO_TEST_SUITE(Summary)
/*
* Tests works by reading the Deck, write the summary output, then immediately
* read it again (with ERT), and compare the read values with the input.
*/
BOOST_AUTO_TEST_CASE(group_keywords) {
setup cfg( "test_summary_group_constraints");
// Force to run in a directory, to make sure the basename with
// leading path works.
cfg.ta.makeSubDir( "PATH" );
cfg.name = "PATH/CASE";
SummaryState st(std::chrono::system_clock::now());
out::Summary writer( cfg.es, cfg.config, cfg.grid, cfg.schedule , cfg.name );
writer.eval(st, 0, 0*day, cfg.es, cfg.schedule, cfg.wells, cfg.groups, {});
writer.add_timestep( st, 0);
writer.eval(st, 1, 1*day, cfg.es, cfg.schedule, cfg.wells, cfg.groups, {});
writer.add_timestep( st, 1);
writer.write();
auto res = readsum( cfg.name );
const auto* resp = res.get();
//BOOST_CHECK( ecl_sum_has_report_step( resp, 1 ) );
BOOST_CHECK( ecl_sum_has_group_var( resp, "TEST", "GMCTP" ) );
// Integer flag indicating current active group control
BOOST_CHECK_EQUAL( static_cast<int>(ecl_sum_get_group_var( resp, 1, "TEST", "GMCTP" )), 0 );
BOOST_CHECK_EQUAL( static_cast<int>(ecl_sum_get_group_var( resp, 1, "LOWER", "GMCTW" )), 3 );
BOOST_CHECK_EQUAL( static_cast<int>(ecl_sum_get_group_var( resp, 1, "LOWER", "GMCTP" )), 1 );
BOOST_CHECK_EQUAL( static_cast<int>(ecl_sum_get_group_var( resp, 1, "UPPER", "GMCTP" )), 3 );
BOOST_CHECK_EQUAL( static_cast<int>(ecl_sum_get_group_var( resp, 1, "UPPER", "GMCTW" )), 4 );
BOOST_CHECK_EQUAL( static_cast<int>(ecl_sum_get_group_var( resp, 1, "UPPER", "GMCTG" )), 3 );
}
BOOST_AUTO_TEST_SUITE_END()