diff --git a/src/opm/output/eclipse/Summary.cpp b/src/opm/output/eclipse/Summary.cpp index 39f1da41e..e9563d69f 100644 --- a/src/opm/output/eclipse/Summary.cpp +++ b/src/opm/output/eclipse/Summary.cpp @@ -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>& 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 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 diff --git a/tests/SUMMARY_EFF_FAC.DATA b/tests/SUMMARY_EFF_FAC.DATA new file mode 100644 index 000000000..0af7a9a96 --- /dev/null +++ b/tests/SUMMARY_EFF_FAC.DATA @@ -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 / diff --git a/tests/test_Summary.cpp b/tests/test_Summary.cpp index 2f7572d6b..a4404a74a 100644 --- a/tests/test_Summary.cpp +++ b/tests/test_Summary.cpp @@ -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 ); +}