From 5bab785056e36fb1f74fcacb8ebfd330112b7dd6 Mon Sep 17 00:00:00 2001 From: Tor Harald Sandve Date: Thu, 12 Dec 2024 08:46:29 +0100 Subject: [PATCH 1/4] Fix trivial NETV This commit includes several fixes related to supporting NETV guide rates that are zero --- opm/simulators/wells/FractionCalculator.cpp | 51 +++++++++++++-------- opm/simulators/wells/FractionCalculator.hpp | 8 ++-- opm/simulators/wells/WellGroupHelpers.cpp | 19 ++++---- 3 files changed, 47 insertions(+), 31 deletions(-) diff --git a/opm/simulators/wells/FractionCalculator.cpp b/opm/simulators/wells/FractionCalculator.cpp index 2a7230c66..9cd3ff467 100644 --- a/opm/simulators/wells/FractionCalculator.cpp +++ b/opm/simulators/wells/FractionCalculator.cpp @@ -77,18 +77,25 @@ Scalar FractionCalculator:: localFraction(const std::string& name, const std::string& always_included_child) { - const Scalar my_guide_rate = guideRate(name, always_included_child); - const Group& parent_group = schedule_.getGroup(parent(name), report_step_); - const Scalar total_guide_rate = guideRateSum(parent_group, always_included_child); + bool only_use_potentials = false; + const Scalar my_guide_rate = guideRate(name, always_included_child, only_use_potentials); - // the total guide gate is the same as my_guide rate - // the well/group is probably on its own, i.e. return 1 - // even is its guide_rate is zero - const Scalar guide_rate_epsilon = 1e-12; - if ( std::abs(my_guide_rate - total_guide_rate) < guide_rate_epsilon ) + const Group& parent_group = schedule_.getGroup(parent(name), report_step_); + const auto [total_guide_rate, num_active_groups] = guideRateSum(parent_group, always_included_child, only_use_potentials); + + // the group/well "name" is the only active group/well we therefore return 1 as the fraction + // even though my_guide_rate may be zero + if (num_active_groups == 1) return 1.0; - assert(total_guide_rate > my_guide_rate); + if (total_guide_rate == 0 ) { + // if the total guide rate is zero (for instance due to netv = 0) we use the potentials + // to distribute the group rate + only_use_potentials = true; + const Scalar my_pot = guideRate(name, always_included_child, only_use_potentials); + const Scalar my_total_pot = guideRateSum(parent_group, always_included_child, only_use_potentials).first; + return my_pot / my_total_pot; + } return my_guide_rate / total_guide_rate; } @@ -104,11 +111,13 @@ parent(const std::string& name) } template -Scalar FractionCalculator:: +std::pair FractionCalculator:: guideRateSum(const Group& group, - const std::string& always_included_child) + const std::string& always_included_child, + const bool use_potentials) { Scalar total_guide_rate = 0.0; + int number_of_included_well_or_group = 0; for (const std::string& child_group : group.groups()) { bool included = (child_group == always_included_child); if (is_producer_) { @@ -122,7 +131,10 @@ guideRateSum(const Group& group, (ctrl == Group::InjectionCMode::NONE); } if (included) { - total_guide_rate += guideRate(child_group, always_included_child); + if(groupControlledWells(child_group, always_included_child) > 0) { + number_of_included_well_or_group++; + total_guide_rate += guideRate(child_group, always_included_child, use_potentials); + } } } @@ -133,34 +145,35 @@ guideRateSum(const Group& group, } else { included |= well_state_.isInjectionGrup(child_well); } - if (included) { - total_guide_rate += guideRate(child_well, always_included_child); + number_of_included_well_or_group++; + total_guide_rate += guideRate(child_well, always_included_child, use_potentials); } } - return total_guide_rate; + return {total_guide_rate, number_of_included_well_or_group}; } template Scalar FractionCalculator:: guideRate(const std::string& name, - const std::string& always_included_child) + const std::string& always_included_child, + const bool use_potentials) { if (schedule_.hasWell(name, report_step_)) { return WellGroupHelpers::getGuideRate(name, schedule_, well_state_, group_state_, report_step_, guide_rate_, target_, pu_); } else { if (groupControlledWells(name, always_included_child) > 0) { - if (is_producer_ && guide_rate_->has(name)) { + if (is_producer_ && guide_rate_->has(name) && !use_potentials) { return guide_rate_->get(name, target_, getGroupRateVector(name)); - } else if (!is_producer_ && guide_rate_->has(name, injection_phase_)) { + } else if (!is_producer_ && guide_rate_->has(name, injection_phase_) && !use_potentials) { return guide_rate_->get(name, injection_phase_); } else { // We are a group, with default guide rate. // Compute guide rate by accumulating our children's guide rates. const Group& group = schedule_.getGroup(name, report_step_); const double eff = group.getGroupEfficiencyFactor(); - return eff * guideRateSum(group, always_included_child); + return eff * guideRateSum(group, always_included_child, use_potentials).first; } } else { // No group-controlled subordinate wells. diff --git a/opm/simulators/wells/FractionCalculator.hpp b/opm/simulators/wells/FractionCalculator.hpp index 06962a393..dbc13360d 100644 --- a/opm/simulators/wells/FractionCalculator.hpp +++ b/opm/simulators/wells/FractionCalculator.hpp @@ -56,10 +56,12 @@ public: private: std::string parent(const std::string& name); - Scalar guideRateSum(const Group& group, - const std::string& always_included_child); + std::pair guideRateSum(const Group& group, + const std::string& always_included_child, + const bool use_potentials); Scalar guideRate(const std::string& name, - const std::string& always_included_child); + const std::string& always_included_child, + const bool use_potentials); int groupControlledWells(const std::string& group_name, const std::string& always_included_child); GuideRate::RateVector getGroupRateVector(const std::string& group_name); diff --git a/opm/simulators/wells/WellGroupHelpers.cpp b/opm/simulators/wells/WellGroupHelpers.cpp index 2d3e0f944..29192f943 100644 --- a/opm/simulators/wells/WellGroupHelpers.cpp +++ b/opm/simulators/wells/WellGroupHelpers.cpp @@ -300,14 +300,14 @@ updateGuideRatesForInjectionGroups(const Group& group, if(!group.hasInjectionControl(phase)) continue; - Scalar guideRateValue = 0.0; + std::optional guideRateValue; const auto& controls = group.injectionControls(phase, summaryState); switch (controls.guide_rate_def){ case Group::GuideRateInjTarget::RATE: break; case Group::GuideRateInjTarget::VOID: { - guideRateValue = group_state.injection_vrep_rate(group.name()); + guideRateValue = std::max(Scalar(0.0), group_state.injection_vrep_rate(group.name())); break; } case Group::GuideRateInjTarget::NETV: @@ -315,17 +315,16 @@ updateGuideRatesForInjectionGroups(const Group& group, guideRateValue = group_state.injection_vrep_rate(group.name()); const std::vector& injRES = group_state.injection_reservoir_rates(group.name()); if (phase != Phase::OIL && pu.phase_used[BlackoilPhases::Liquid]) - guideRateValue -= injRES[pu.phase_pos[BlackoilPhases::Liquid]]; + guideRateValue = *guideRateValue - injRES[pu.phase_pos[BlackoilPhases::Liquid]]; if (phase != Phase::GAS && pu.phase_used[BlackoilPhases::Vapour]) - guideRateValue -= injRES[pu.phase_pos[BlackoilPhases::Vapour]]; + guideRateValue = *guideRateValue - injRES[pu.phase_pos[BlackoilPhases::Vapour]]; if (phase != Phase::WATER && pu.phase_used[BlackoilPhases::Aqua]) - guideRateValue -= injRES[pu.phase_pos[BlackoilPhases::Aqua]]; + guideRateValue = *guideRateValue - injRES[pu.phase_pos[BlackoilPhases::Aqua]]; + + guideRateValue = std::max(Scalar(0.0), *guideRateValue); break; } case Group::GuideRateInjTarget::RESV: - OPM_DEFLOG_THROW(std::runtime_error, "GUIDE PHASE RESV not implemented. Group " + group.name(), deferred_logger); - case Group::GuideRateInjTarget::POTN: - break; case Group::GuideRateInjTarget::NO_GUIDE_RATE: break; default: @@ -335,7 +334,9 @@ updateGuideRatesForInjectionGroups(const Group& group, } const UnitSystem& unit_system = schedule.getUnits(); - guideRateValue = unit_system.from_si(UnitSystem::measure::rate, guideRateValue); + if (guideRateValue) { + guideRateValue = unit_system.from_si(UnitSystem::measure::rate, *guideRateValue); + } guideRate->compute(group.name(), phase, reportStepIdx, guideRateValue); } } From 177aa397917d6ea7e6806a28af9c5b5c2c5c170a Mon Sep 17 00:00:00 2001 From: Tor Harald Sandve Date: Fri, 20 Dec 2024 11:57:07 +0100 Subject: [PATCH 2/4] dont use implicit potentials for newly opened wells with target zero --- opm/simulators/wells/StandardWell_impl.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/opm/simulators/wells/StandardWell_impl.hpp b/opm/simulators/wells/StandardWell_impl.hpp index 679b503c9..08143da71 100644 --- a/opm/simulators/wells/StandardWell_impl.hpp +++ b/opm/simulators/wells/StandardWell_impl.hpp @@ -1758,7 +1758,10 @@ namespace Opm } bool converged_implicit = false; - if (this->param_.local_well_solver_control_switching_) { + // for newly opened wells we dont compute the potentials implicit + // group controlled wells with defaulted guiderates will have zero targets as + // the potentials are used to compute the well fractions. + if (this->param_.local_well_solver_control_switching_ && !(this->changed_to_open_this_step_ && this->wellUnderZeroRateTarget(simulator, well_state, deferred_logger))) { converged_implicit = computeWellPotentialsImplicit(simulator, well_potentials, deferred_logger); } if (!converged_implicit) { From a8eeae7204a95b06bb7cef9f02032157e49b5e5a Mon Sep 17 00:00:00 2001 From: Tor Harald Sandve Date: Fri, 20 Dec 2024 16:04:23 +0100 Subject: [PATCH 3/4] add tuning to 9_3D_GINJ_GAS_MAX_EXPORT_STW.DATA --time-step-after-event-in-days=1 --- regressionTests.cmake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/regressionTests.cmake b/regressionTests.cmake index 524c9e58b..db684fe0b 100644 --- a/regressionTests.cmake +++ b/regressionTests.cmake @@ -947,7 +947,8 @@ add_test_compareECLFiles(CASENAME 9_3d_grpctl_stw_model2 SIMULATOR flow ABS_TOL ${abs_tol} REL_TOL ${rel_tol} - DIR model2) + DIR model2 + TEST_ARGS --time-step-after-event-in-days=1) add_test_compareECLFiles(CASENAME 9_3d_grpctl_msw_model2 FILENAME 9_3D_GINJ_GAS_MAX_EXPORT_MSW From 90bde0561562145adcb833e5cdc688248eee6d77 Mon Sep 17 00:00:00 2001 From: Tor Harald Sandve Date: Wed, 8 Jan 2025 15:18:50 +0100 Subject: [PATCH 4/4] some cleanup from review --- opm/simulators/wells/FractionCalculator.cpp | 36 ++++++++++----------- opm/simulators/wells/FractionCalculator.hpp | 7 ++-- opm/simulators/wells/WellGroupHelpers.cpp | 1 + 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/opm/simulators/wells/FractionCalculator.cpp b/opm/simulators/wells/FractionCalculator.cpp index 9cd3ff467..f2747ea15 100644 --- a/opm/simulators/wells/FractionCalculator.cpp +++ b/opm/simulators/wells/FractionCalculator.cpp @@ -77,11 +77,11 @@ Scalar FractionCalculator:: localFraction(const std::string& name, const std::string& always_included_child) { - bool only_use_potentials = false; - const Scalar my_guide_rate = guideRate(name, always_included_child, only_use_potentials); + bool always_use_potentials = false; + const Scalar my_guide_rate = guideRate(name, always_included_child, always_use_potentials); const Group& parent_group = schedule_.getGroup(parent(name), report_step_); - const auto [total_guide_rate, num_active_groups] = guideRateSum(parent_group, always_included_child, only_use_potentials); + const auto [total_guide_rate, num_active_groups] = guideRateSum(parent_group, always_included_child, always_use_potentials); // the group/well "name" is the only active group/well we therefore return 1 as the fraction // even though my_guide_rate may be zero @@ -91,9 +91,9 @@ localFraction(const std::string& name, if (total_guide_rate == 0 ) { // if the total guide rate is zero (for instance due to netv = 0) we use the potentials // to distribute the group rate - only_use_potentials = true; - const Scalar my_pot = guideRate(name, always_included_child, only_use_potentials); - const Scalar my_total_pot = guideRateSum(parent_group, always_included_child, only_use_potentials).first; + always_use_potentials = true; + const Scalar my_pot = guideRate(name, always_included_child, always_use_potentials); + const Scalar my_total_pot = guideRateSum(parent_group, always_included_child, always_use_potentials).first; return my_pot / my_total_pot; } return my_guide_rate / total_guide_rate; @@ -114,10 +114,10 @@ template std::pair FractionCalculator:: guideRateSum(const Group& group, const std::string& always_included_child, - const bool use_potentials) + const bool always_use_potentials) { Scalar total_guide_rate = 0.0; - int number_of_included_well_or_group = 0; + int number_of_included_well_or_groups = 0; for (const std::string& child_group : group.groups()) { bool included = (child_group == always_included_child); if (is_producer_) { @@ -131,9 +131,9 @@ guideRateSum(const Group& group, (ctrl == Group::InjectionCMode::NONE); } if (included) { - if(groupControlledWells(child_group, always_included_child) > 0) { - number_of_included_well_or_group++; - total_guide_rate += guideRate(child_group, always_included_child, use_potentials); + if (groupControlledWells(child_group, always_included_child) > 0) { + number_of_included_well_or_groups++; + total_guide_rate += guideRate(child_group, always_included_child, always_use_potentials); } } } @@ -146,34 +146,34 @@ guideRateSum(const Group& group, included |= well_state_.isInjectionGrup(child_well); } if (included) { - number_of_included_well_or_group++; - total_guide_rate += guideRate(child_well, always_included_child, use_potentials); + number_of_included_well_or_groups++; + total_guide_rate += guideRate(child_well, always_included_child, always_use_potentials); } } - return {total_guide_rate, number_of_included_well_or_group}; + return {total_guide_rate, number_of_included_well_or_groups}; } template Scalar FractionCalculator:: guideRate(const std::string& name, const std::string& always_included_child, - const bool use_potentials) + const bool always_use_potentials) { if (schedule_.hasWell(name, report_step_)) { return WellGroupHelpers::getGuideRate(name, schedule_, well_state_, group_state_, report_step_, guide_rate_, target_, pu_); } else { if (groupControlledWells(name, always_included_child) > 0) { - if (is_producer_ && guide_rate_->has(name) && !use_potentials) { + if (is_producer_ && guide_rate_->has(name) && !always_use_potentials) { return guide_rate_->get(name, target_, getGroupRateVector(name)); - } else if (!is_producer_ && guide_rate_->has(name, injection_phase_) && !use_potentials) { + } else if (!is_producer_ && guide_rate_->has(name, injection_phase_) && !always_use_potentials) { return guide_rate_->get(name, injection_phase_); } else { // We are a group, with default guide rate. // Compute guide rate by accumulating our children's guide rates. const Group& group = schedule_.getGroup(name, report_step_); const double eff = group.getGroupEfficiencyFactor(); - return eff * guideRateSum(group, always_included_child, use_potentials).first; + return eff * guideRateSum(group, always_included_child, always_use_potentials).first; } } else { // No group-controlled subordinate wells. diff --git a/opm/simulators/wells/FractionCalculator.hpp b/opm/simulators/wells/FractionCalculator.hpp index dbc13360d..9b23564b0 100644 --- a/opm/simulators/wells/FractionCalculator.hpp +++ b/opm/simulators/wells/FractionCalculator.hpp @@ -56,12 +56,15 @@ public: private: std::string parent(const std::string& name); + + // returns the sum of the guiderates of the given group + // and the number of sub-groups/wells that contributed to the sum std::pair guideRateSum(const Group& group, const std::string& always_included_child, - const bool use_potentials); + const bool always_use_potentials); Scalar guideRate(const std::string& name, const std::string& always_included_child, - const bool use_potentials); + const bool always_use_potentials); int groupControlledWells(const std::string& group_name, const std::string& always_included_child); GuideRate::RateVector getGroupRateVector(const std::string& group_name); diff --git a/opm/simulators/wells/WellGroupHelpers.cpp b/opm/simulators/wells/WellGroupHelpers.cpp index 29192f943..e54bb5783 100644 --- a/opm/simulators/wells/WellGroupHelpers.cpp +++ b/opm/simulators/wells/WellGroupHelpers.cpp @@ -325,6 +325,7 @@ updateGuideRatesForInjectionGroups(const Group& group, break; } case Group::GuideRateInjTarget::RESV: + OPM_DEFLOG_THROW(std::runtime_error, "GUIDE PHASE RESV not implemented. Group " + group.name(), deferred_logger); case Group::GuideRateInjTarget::NO_GUIDE_RATE: break; default: