Apply efficiency factors

Efficiency factors are multiplied when specified at multiple levels of
the well/group hierarchy.

The factors are included as follows:

* Well Rate        - No efficiency factor
* Well Total       - WEFAC & GEFAC (whole hierarchy)
* Group Rate       - WEFAC & GEFAC (only subgroups)
* Group Total      - WEFAC & GEFAC (whole hierarchy)
* Field Rate       - WEFAC & GEFAC (whole hierarchy)
* Field Total      - WEFAC & GEFAC (whole hierarchy)
* Region Rate      - WEFAC & GEFAC (whole hierarchy)
* Region Total     - WEFAC & GEFAC (whole hierarchy)
* Completion Rate  - No efficiency factor
* Completion Total - WEFAC & GEFAC (whole hierarchy)

Authored by Sveinung Rundhovde and Lars Petter Hauge
This commit is contained in:
Lars Petter Øren Hauge 2018-01-31 08:16:31 +01:00 committed by Sveinung Rundhovde
parent 22ebda2ea7
commit fdb06aa77f
3 changed files with 298 additions and 4 deletions

View File

@ -156,6 +156,7 @@ struct fn_args {
const data::Wells& wells; const data::Wells& wells;
const out::RegionCache& regionCache; const out::RegionCache& regionCache;
const EclipseGrid& grid; const EclipseGrid& grid;
const std::vector< std::pair< std::string, double > > eff_factors;
}; };
/* Since there are several enums in opm scattered about more-or-less /* Since there are several enums in opm scattered about more-or-less
@ -185,6 +186,15 @@ measure rate_unit< rt::reservoir_oil >() { return measure::rate; }
template<> constexpr template<> constexpr
measure rate_unit< rt::reservoir_gas >() { return measure::rate; } measure rate_unit< rt::reservoir_gas >() { return measure::rate; }
double efac( const std::vector<std::pair<std::string,double>>& eff_factors, const std::string& name ) {
auto it = std::find_if( eff_factors.begin(), eff_factors.end(),
[&] ( const std::pair< std::string, double > elem )
{ return elem.first == name; }
);
return (it != eff_factors.end()) ? it->second : 1;
}
template< rt phase, bool injection = true > template< rt phase, bool injection = true >
inline quantity rate( const fn_args& args ) { inline quantity rate( const fn_args& args ) {
double sum = 0.0; double sum = 0.0;
@ -192,7 +202,11 @@ inline quantity rate( const fn_args& args ) {
for( const auto* sched_well : args.schedule_wells ) { for( const auto* sched_well : args.schedule_wells ) {
const auto& name = sched_well->name(); const auto& name = sched_well->name();
if( args.wells.count( name ) == 0 ) continue; if( args.wells.count( name ) == 0 ) continue;
const auto v = args.wells.at( name ).rates.get( phase, 0.0 );
double eff_fac = efac( args.eff_factors, name );
const auto v = args.wells.at(name).rates.get(phase, 0.0) * eff_fac;
if( ( v > 0 ) == injection ) if( ( v > 0 ) == injection )
sum += v; sum += v;
} }
@ -239,7 +253,10 @@ inline quantity crate( const fn_args& args ) {
} ); } );
if( completion == well.completions.end() ) return zero; if( completion == well.completions.end() ) return zero;
const auto v = completion->rates.get( phase, 0.0 );
double eff_fac = efac( args.eff_factors, name );
const auto v = completion->rates.get( phase, 0.0 ) * eff_fac;
if( ( v > 0 ) != injection ) return zero; if( ( v > 0 ) != injection ) return zero;
if( !injection ) return { -v, rate_unit< phase >() }; if( !injection ) return { -v, rate_unit< phase >() };
@ -376,8 +393,12 @@ template<rt phase , bool injection>
quantity region_rate( const fn_args& args ) { quantity region_rate( const fn_args& args ) {
double sum = 0; double sum = 0;
const auto& well_completions = args.regionCache.completions( args.num ); const auto& well_completions = args.regionCache.completions( args.num );
for (const auto& pair : well_completions) { for (const auto& pair : well_completions) {
double rate = args.wells.get( pair.first , pair.second , phase );
double eff_fac = efac( args.eff_factors, pair.first );
double rate = args.wells.get( pair.first , pair.second , phase ) * eff_fac;
// We are asking for the production rate in an injector - or // We are asking for the production rate in an injector - or
// opposite. We just clamp to zero. // opposite. We just clamp to zero.
@ -945,6 +966,66 @@ Summary::Summary( const EclipseState& st,
} }
} }
/*
* The well efficiency factor will not impact the well rate itself, but is
* rather applied for accumulated values.The WEFAC can be considered to shut
* and open the well for short intervals within the same timestep, and the well
* is therefore solved at full speed.
*
* Groups are treated similarly as wells. The group's GEFAC is not applied for
* rates, only for accumulated volumes. When GEFAC is set for a group, it is
* considered that all wells are taken down simultaneously, and GEFAC is
* therefore not applied to the group's rate. However, any efficiency factors
* applied to the group's wells or sub-groups must be included.
*
* Regions and fields will have the well and group efficiency applied for both
* rates and accumulated values.
*
*/
std::vector< std::pair< std::string, double > >
well_efficiency_factors( const smspec_node_type* type,
const Schedule& schedule,
const std::vector< const Well* >& schedule_wells,
size_t timestep ) {
std::vector< std::pair< std::string, double > > efac;
if( smspec_node_get_var_type(type) != ECL_SMSPEC_GROUP_VAR
&& smspec_node_get_var_type(type) != ECL_SMSPEC_FIELD_VAR
&& smspec_node_get_var_type(type) != ECL_SMSPEC_REGION_VAR
&& !smspec_node_is_total( type ) ) {
return efac;
}
const bool is_group = smspec_node_get_var_type(type) == ECL_SMSPEC_GROUP_VAR;
const bool is_rate = !smspec_node_is_total( type );
const auto &groupTree = schedule.getGroupTree(timestep);
for( const auto* well : schedule_wells ) {
double eff_factor = well->getEfficiencyFactor(timestep);
if ( !well->hasBeenDefined( timestep ) )
continue;
const auto* node = &schedule.getGroup(well->getGroupName(timestep));
while(true){
if(( is_group
&& is_rate
&& node->name() == smspec_node_get_wgname(type) ))
break;
eff_factor *= node->getGroupEfficiencyFactor( timestep );
const auto& parent = groupTree.parent( node->name() );
if( !schedule.hasGroup( parent ) )
break;
node = &schedule.getGroup( parent );
}
efac.emplace_back( well->name(), eff_factor );
}
return efac;
}
void Summary::add_timestep( int report_step, void Summary::add_timestep( int report_step,
double secs_elapsed, double secs_elapsed,
const EclipseState& es, const EclipseState& es,
@ -963,13 +1044,16 @@ void Summary::add_timestep( int report_step,
const auto* genkey = smspec_node_get_gen_key1( f.first ); const auto* genkey = smspec_node_get_gen_key1( f.first );
const auto schedule_wells = find_wells( schedule, f.first, timestep, this->regionCache ); const auto schedule_wells = find_wells( schedule, f.first, timestep, this->regionCache );
auto eff_factors = well_efficiency_factors( f.first, schedule, schedule_wells, timestep );
const auto val = f.second( { schedule_wells, const auto val = f.second( { schedule_wells,
duration, duration,
timestep, timestep,
num, num,
wells, wells,
this->regionCache, this->regionCache,
this->grid}); this->grid,
eff_factors});
const auto unit_applied_val = es.getUnits().from_si( val.unit, val.value ); const auto unit_applied_val = es.getUnits().from_si( val.unit, val.value );
const auto res = smspec_node_is_total( f.first ) && prev_tstep const auto res = smspec_node_is_total( f.first ) && prev_tstep

138
tests/SUMMARY_EFF_FAC.DATA Normal file
View File

@ -0,0 +1,138 @@
START
10 MAI 2007 /
RUNSPEC
TITLE
SUMMARYTESTS
-- A simple 10x10x10 cube. Simple to reason about, large enough for all tests
DIMENS
10 10 10 /
REGDIMS
10 /
OIL
GAS
WATER
GRID
DX
1000*1 /
DY
1000*1 /
DZ
1000*1 /
TOPS
100*1 /
PORO
1000*0.2 /
REGIONS
FIPNUM
1*2
/
SUMMARY
DATE
ROPR
/
RWIR
/
WOPR
/
WOPT
/
WOIR
/
WOIT
/
GOPR
/
GOPT
/
GOIR
/
GOIT
/
GWCT
/
GGOR
/
FOPR
FOPT
FOIR
FOIT
COPR
'W_2' /
/
COPT
'W_2' /
/
SCHEDULE
GRUPTREE
'G_1' 'G' /
'G_2' 'G' /
'G_3' 'G_4' /
/
WELSPECS
'W_1' 'G_1' 1 1 3.33 'OIL' 7* /
'W_2' 'G_2' 2 1 3.33 'OIL' 7* /
'W_3' 'G_3' 2 1 3.33 'OIL' 7* /
/
WEFAC
'W_2' 0.2 /
'W_3' 0.3 /
/
GEFAC
G_2 0.01 /
G_3 0.02 /
G_4 0.03 /
/
WCONPROD
W_1 'OPEN' ORAT 10.1 /
W_2 'OPEN' ORAT 10.1 /
W_3 'OPEN' ORAT 10.1 /
/
-- Completion data.
COMPDAT
-- 'Well' I J K1 K2
-- Passing 0 to I/J means they'll get the well head I/J
W_1 0 0 1 1 / -- Active index: 0
W_2 0 0 1 1 / -- Active index: 1
W_2 0 0 2 2 / -- Active index: 101
W_3 0 0 1 1 / -- Active index: 2
/
TSTEP
-- register time steps (in days). This allows us to write *two* report steps (1
-- and 2. Without this, totals/accumulations would fail (segfault) when looking
-- up historical rates and volumes. These volumes however don't change, i.e.
-- every time step has the same set of values
10 10 /
TSTEP
10 10 /

View File

@ -1150,3 +1150,75 @@ BOOST_AUTO_TEST_CASE(READ_WRITE_WELLDATA) {
BOOST_CHECK_CLOSE( wellRatesCopy.get( "W_2" , 101 , rt::wat) , wellRates.get( "W_2" , 101 , rt::wat), 1e-16); BOOST_CHECK_CLOSE( wellRatesCopy.get( "W_2" , 101 , rt::wat) , wellRates.get( "W_2" , 101 , rt::wat), 1e-16);
} }
BOOST_AUTO_TEST_CASE(efficiency_factor) {
setup cfg( "test_efficiency_factor", "SUMMARY_EFF_FAC.DATA" );
out::Summary writer( cfg.es, cfg.config, cfg.grid, cfg.schedule, cfg.name );
writer.add_timestep( 0, 0 * day, cfg.es, cfg.schedule, cfg.wells, {});
writer.add_timestep( 1, 1 * day, cfg.es, cfg.schedule, cfg.wells, {});
writer.add_timestep( 2, 2 * day, cfg.es, cfg.schedule, cfg.wells, {});
writer.write();
auto res = readsum( cfg.name );
const auto* resp = res.get();
/* No WEFAC assigned to W_1 */
BOOST_CHECK_CLOSE( 10.1, ecl_sum_get_well_var( resp, 1, "W_1", "WOPR" ), 1e-5 );
BOOST_CHECK_CLOSE( 10.1, ecl_sum_get_well_var( resp, 1, "W_1", "WOPT" ), 1e-5 );
BOOST_CHECK_CLOSE( 2 * 10.1, ecl_sum_get_well_var( resp, 2, "W_1", "WOPT" ), 1e-5 );
/* WEFAC 0.2 assigned to W_2.
* W_2 assigned to group G2. GEFAC G2 = 0.01 */
BOOST_CHECK_CLOSE( 20.1, ecl_sum_get_well_var( resp, 1, "W_2", "WOPR" ), 1e-5 );
BOOST_CHECK_CLOSE( 20.1 * 0.2 * 0.01, ecl_sum_get_well_var( resp, 1, "W_2", "WOPT" ), 1e-5 );
BOOST_CHECK_CLOSE( 2 * 20.1 * 0.2 * 0.01, ecl_sum_get_well_var( resp, 2, "W_2", "WOPT" ), 1e-5 );
/* WEFAC 0.3 assigned to W_3.
* W_3 assigned to group G3. GEFAC G_3 = 0.02
* G_3 assigned to group G4. GEFAC G_4 = 0.03*/
BOOST_CHECK_CLOSE( 30.1, ecl_sum_get_well_var( resp, 1, "W_3", "WOIR" ), 1e-5 );
BOOST_CHECK_CLOSE( 30.1 * 0.3 * 0.02 * 0.03, ecl_sum_get_well_var( resp, 1, "W_3", "WOIT" ), 1e-5 );
BOOST_CHECK_CLOSE( 2 * 30.1 * 0.3 * 0.02 * 0.03, ecl_sum_get_well_var( resp, 2, "W_3", "WOIT" ), 1e-5 );
/* WEFAC 0.2 assigned to W_2.
* W_2 assigned to group G2. GEFAC G2 = 0.01 */
BOOST_CHECK_CLOSE( 20.1 * 0.2, ecl_sum_get_group_var( resp, 1, "G_2", "GOPR" ), 1e-5 );
BOOST_CHECK_CLOSE( 20.1 * 0.2 * 0.01, ecl_sum_get_group_var( resp, 1, "G_2", "GOPT" ), 1e-5 );
BOOST_CHECK_CLOSE( 2 * 20.1 * 0.2 * 0.01, ecl_sum_get_group_var( resp, 2, "G_2", "GOPT" ), 1e-5 );
/* WEFAC 0.3 assigned to W_3.
* W_3 assigned to group G3. GEFAC G_3 = 0.02
* G_3 assigned to group G4. GEFAC G_4 = 0.03*/
BOOST_CHECK_CLOSE( 30.1 * 0.3, ecl_sum_get_group_var( resp, 1, "G_3", "GOIR" ), 1e-5 );
BOOST_CHECK_CLOSE( 30.1 * 0.3 * 0.02 * 0.03, ecl_sum_get_group_var( resp, 1, "G_3", "GOIT" ), 1e-5 );
BOOST_CHECK_CLOSE( 2 * 30.1 * 0.3 * 0.02 * 0.03, ecl_sum_get_group_var( resp, 2, "G_3", "GOIT" ), 1e-5 );
/* WEFAC 0.3 assigned to W_3.
* W_3 assigned to group G3. GEFAC G_3 = 0.02
* G_3 assigned to group G4. GEFAC G_4 = 0.03
* The rate for a group is calculated including WEFAC and GEFAC for subgroups */
BOOST_CHECK_CLOSE( 30.1 * 0.3 * 0.02, ecl_sum_get_group_var( resp, 1, "G_4", "GOIR" ), 1e-5 );
BOOST_CHECK_CLOSE( 30.1 * 0.3 * 0.02 * 0.03, ecl_sum_get_group_var( resp, 1, "G_4", "GOIT" ), 1e-5 );
BOOST_CHECK_CLOSE( 2 * 30.1 * 0.3 * 0.02 * 0.03, ecl_sum_get_group_var( resp, 2, "G_4", "GOIT" ), 1e-5 );
BOOST_CHECK_CLOSE( 10.1 + 20.1 * 0.2 * 0.01, ecl_sum_get_field_var( resp, 1, "FOPR" ), 1e-5 );
BOOST_CHECK_CLOSE( 10.1 + 20.1 * 0.2 * 0.01, ecl_sum_get_field_var( resp, 1, "FOPT" ), 1e-5 );
BOOST_CHECK_CLOSE( 2 * (10.1 + 20.1 * 0.2 * 0.01), ecl_sum_get_field_var( resp, 2, "FOPT" ), 1e-5 );
BOOST_CHECK_CLOSE( 30.1 * 0.3 * 0.02 * 0.03, ecl_sum_get_field_var( resp, 1, "FOIR" ), 1e-5 );
BOOST_CHECK_CLOSE( 30.1 * 0.3 * 0.02 * 0.03, ecl_sum_get_field_var( resp, 1, "FOIT" ), 1e-5 );
BOOST_CHECK_CLOSE( 2 * 30.1 * 0.3 * 0.02 * 0.03, ecl_sum_get_field_var( resp, 2, "FOIT" ), 1e-5 );
BOOST_CHECK_CLOSE( 30.1 * 0.3 * 0.02 * 0.03, ecl_sum_get_field_var( resp, 1, "FOIR" ), 1e-5 );
BOOST_CHECK_CLOSE( 30.1 * 0.3 * 0.02 * 0.03, ecl_sum_get_field_var( resp, 1, "FOIT" ), 1e-5 );
BOOST_CHECK_CLOSE( 2 * 30.1 * 0.3 * 0.02 * 0.03, ecl_sum_get_field_var( resp, 2, "FOIT" ), 1e-5 );
BOOST_CHECK_CLOSE( 200.1 * 0.2 * 0.01, ecl_sum_get_general_var( resp , 1 , "ROPR:1" ) , 1e-5);
BOOST_CHECK_CLOSE( 100.1, ecl_sum_get_general_var( resp , 1 , "ROPR:2" ) , 1e-5);
BOOST_CHECK_CLOSE( 300 * 0.2 * 0.01, ecl_sum_get_general_var( resp , 1 , "RWIR:1" ) , 1e-5);
BOOST_CHECK_CLOSE( 200.1, ecl_sum_get_well_completion_var( resp, 1, "W_2", "COPR", 2 ), 1e-5 );
BOOST_CHECK_CLOSE( 200.1 * 0.2 * 0.01, ecl_sum_get_well_completion_var( resp, 1, "W_2", "COPT", 2 ), 1e-5 );
}