diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index 02d13581a16..5fdb279ae3f 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -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 diff --git a/msim/src/msim.cpp b/msim/src/msim.cpp index aecbf923b46..66201d438b4 100644 --- a/msim/src/msim.cpp +++ b/msim/src/msim.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -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, diff --git a/opm/output/data/Groups.hpp b/opm/output/data/Groups.hpp new file mode 100644 index 00000000000..e4d5c24abc9 --- /dev/null +++ b/opm/output/data/Groups.hpp @@ -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 . + */ + +#ifndef OPM_OUTPUT_GROUPS_HPP +#define OPM_OUTPUT_GROUPS_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace Opm { + + namespace data { + + struct currentGroupConstraints { + Opm::Group::ProductionCMode currentProdConstraint; + Opm::Group::InjectionCMode currentGasInjectionConstraint; + Opm::Group::InjectionCMode currentWaterInjectionConstraint; + + template + void write(MessageBufferType& buffer) const; + template + 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 { + public: + + template + 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 + 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 + void currentGroupConstraints::write(MessageBufferType& buffer) const { + buffer.write(this->currentProdConstraint); + buffer.write(this->currentGasInjectionConstraint); + buffer.write(this->currentWaterInjectionConstraint); + } + + template + 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 diff --git a/opm/output/eclipse/Summary.hpp b/opm/output/eclipse/Summary.hpp index d56ef56270e..178e0711789 100644 --- a/opm/output/eclipse/Summary.hpp +++ b/opm/output/eclipse/Summary.hpp @@ -20,6 +20,8 @@ #ifndef OPM_OUTPUT_SUMMARY_HPP #define OPM_OUTPUT_SUMMARY_HPP +#include + #include #include #include @@ -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 @@ class Summary { 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 pImpl_; diff --git a/opm/parser/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp b/opm/parser/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp index 34ce860a543..eab1b48de42 100644 --- a/opm/parser/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp +++ b/opm/parser/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp @@ -46,7 +46,7 @@ namespace Opm { }; enum class Type { - Rate, Total, Ratio, Pressure, Count, + Rate, Total, Ratio, Pressure, Count, Mode, Undefined, }; diff --git a/src/opm/output/eclipse/Summary.cpp b/src/opm/output/eclipse/Summary.cpp index 69460d42094..2a8ad8aa50e 100644 --- a/src/opm/output/eclipse/Summary.cpp +++ b/src/opm/output/eclipse/Summary.cpp @@ -42,6 +42,7 @@ #include #include +#include #include #include @@ -70,6 +71,31 @@ namespace { Opm::SummaryConfigNode::Type type; }; + using p_cmode = Opm::Group::ProductionCMode; + const std::map 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 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 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& 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(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(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(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(value); + throw std::invalid_argument(str.str()); + } + cntl_mode = it_c->second; + } + } + + return {static_cast(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 > }, @@ -1018,6 +1141,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 > }, @@ -1352,6 +1480,7 @@ namespace Evaluator { struct SimulatorResults { const Opm::data::WellRates& wellSol; + const Opm::data::Group& groupSol; const std::map& single; const std::map>& region; const std::map, double>& block; @@ -1396,13 +1525,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(sim_step), + wells, group_name, stepSize, static_cast(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) }; @@ -1792,8 +1923,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_, {} }; @@ -2022,6 +2153,7 @@ class Opm::out::Summary::SummaryImplementation 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, @@ -2123,6 +2255,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, @@ -2133,7 +2266,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()) { @@ -2398,6 +2531,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 @@ -2413,7 +2547,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); diff --git a/tests/UDQ_ACTIONX_TEST1_U.DATA b/tests/UDQ_ACTIONX_TEST1_U.DATA new file mode 100644 index 00000000000..7a209afe5d1 --- /dev/null +++ b/tests/UDQ_ACTIONX_TEST1_U.DATA @@ -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 / +/ diff --git a/tests/test_Summary.cpp b/tests/test_Summary.cpp index 35e469d22bd..7210b4a6248 100644 --- a/tests/test_Summary.cpp +++ b/tests/test_Summary.cpp @@ -32,6 +32,7 @@ #include #include +#include #include #include @@ -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(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(); diff --git a/tests/test_Summary_Group.cpp b/tests/test_Summary_Group.cpp new file mode 100644 index 00000000000..7f57632e407 --- /dev/null +++ b/tests/test_Summary_Group.cpp @@ -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 . + */ + +#include "config.h" + +#define BOOST_TEST_MODULE Wells +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +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(c)); + c = static_cast(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(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(ecl_sum_get_group_var( resp, 1, "TEST", "GMCTP" )), 0 ); + BOOST_CHECK_EQUAL( static_cast(ecl_sum_get_group_var( resp, 1, "LOWER", "GMCTW" )), 3 ); + BOOST_CHECK_EQUAL( static_cast(ecl_sum_get_group_var( resp, 1, "LOWER", "GMCTP" )), 1 ); + + BOOST_CHECK_EQUAL( static_cast(ecl_sum_get_group_var( resp, 1, "UPPER", "GMCTP" )), 3 ); + BOOST_CHECK_EQUAL( static_cast(ecl_sum_get_group_var( resp, 1, "UPPER", "GMCTW" )), 4 ); + BOOST_CHECK_EQUAL( static_cast(ecl_sum_get_group_var( resp, 1, "UPPER", "GMCTG" )), 3 ); + + +} + +BOOST_AUTO_TEST_SUITE_END()