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:
parent
22ebda2ea7
commit
fdb06aa77f
@ -156,6 +156,7 @@ struct fn_args {
|
||||
const data::Wells& wells;
|
||||
const out::RegionCache& regionCache;
|
||||
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
|
||||
@ -185,6 +186,15 @@ measure rate_unit< rt::reservoir_oil >() { return measure::rate; }
|
||||
template<> constexpr
|
||||
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 >
|
||||
inline quantity rate( const fn_args& args ) {
|
||||
double sum = 0.0;
|
||||
@ -192,7 +202,11 @@ inline quantity rate( const fn_args& args ) {
|
||||
for( const auto* sched_well : args.schedule_wells ) {
|
||||
const auto& name = sched_well->name();
|
||||
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 )
|
||||
sum += v;
|
||||
}
|
||||
@ -239,7 +253,10 @@ inline quantity crate( const fn_args& args ) {
|
||||
} );
|
||||
|
||||
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( !injection ) return { -v, rate_unit< phase >() };
|
||||
@ -376,8 +393,12 @@ template<rt phase , bool injection>
|
||||
quantity region_rate( const fn_args& args ) {
|
||||
double sum = 0;
|
||||
const auto& well_completions = args.regionCache.completions( args.num );
|
||||
|
||||
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
|
||||
// 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,
|
||||
double secs_elapsed,
|
||||
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 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,
|
||||
duration,
|
||||
timestep,
|
||||
num,
|
||||
wells,
|
||||
this->regionCache,
|
||||
this->grid});
|
||||
this->grid,
|
||||
eff_factors});
|
||||
|
||||
const auto unit_applied_val = es.getUnits().from_si( val.unit, val.value );
|
||||
const auto res = smspec_node_is_total( f.first ) && prev_tstep
|
||||
|
138
tests/SUMMARY_EFF_FAC.DATA
Normal file
138
tests/SUMMARY_EFF_FAC.DATA
Normal 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 /
|
@ -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_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 );
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user