diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index 4c30092ab..4cb71f66f 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -104,6 +104,7 @@ list (APPEND MAIN_SOURCE_FILES opm/simulators/wells/VFPProdProperties.cpp opm/simulators/wells/VFPInjProperties.cpp opm/simulators/wells/WellBhpThpCalculator.cpp + opm/simulators/wells/WellConstraints.cpp opm/simulators/wells/WellConvergence.cpp opm/simulators/wells/WellGroupConstraints.cpp opm/simulators/wells/WellGroupControls.cpp @@ -388,6 +389,7 @@ list (APPEND PUBLIC_HEADER_FILES opm/simulators/wells/VFPProperties.hpp opm/simulators/wells/WellBhpThpCalculator.hpp opm/simulators/wells/WellConnectionAuxiliaryModule.hpp + opm/simulators/wells/WellConstraints.hpp opm/simulators/wells/WellConvergence.hpp opm/simulators/wells/WellGroupConstraints.hpp opm/simulators/wells/WellGroupControls.hpp diff --git a/opm/simulators/wells/WellConstraints.cpp b/opm/simulators/wells/WellConstraints.cpp new file mode 100644 index 000000000..3df31f0b9 --- /dev/null +++ b/opm/simulators/wells/WellConstraints.cpp @@ -0,0 +1,159 @@ +/* + Copyright 2017 SINTEF Digital, Mathematics and Cybernetics. + Copyright 2017 Statoil ASA. + Copyright 2018 IRIS + + 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 +#include + +#include + +#include + +#include +#include + +namespace Opm +{ + +Well::ProducerCMode WellConstraints:: +activeProductionConstraint(const SingleWellState& ws, + const SummaryState& summaryState, + const RateConvFunc& calcReservoirVoidageRates, + bool& thp_limit_violated_but_not_switched, + DeferredLogger& deferred_logger) const +{ + const PhaseUsage& pu = well_.phaseUsage(); + const auto controls = well_.wellEcl().productionControls(summaryState); + const auto currentControl = ws.production_cmode; + + if (controls.hasControl(Well::ProducerCMode::BHP) && currentControl != Well::ProducerCMode::BHP) { + const double bhp_limit = controls.bhp_limit; + double current_bhp = ws.bhp; + if (bhp_limit > current_bhp) + return Well::ProducerCMode::BHP; + } + + if (controls.hasControl(Well::ProducerCMode::ORAT) && currentControl != Well::ProducerCMode::ORAT) { + double current_rate = -ws.surface_rates[pu.phase_pos[BlackoilPhases::Liquid]]; + if (controls.oil_rate < current_rate) + return Well::ProducerCMode::ORAT; + } + + if (controls.hasControl(Well::ProducerCMode::WRAT) && currentControl != Well::ProducerCMode::WRAT) { + double current_rate = -ws.surface_rates[pu.phase_pos[BlackoilPhases::Aqua]]; + if (controls.water_rate < current_rate) + return Well::ProducerCMode::WRAT; + } + + if (controls.hasControl(Well::ProducerCMode::GRAT) && currentControl != Well::ProducerCMode::GRAT) { + double current_rate = -ws.surface_rates[pu.phase_pos[BlackoilPhases::Vapour]]; + if (controls.gas_rate < current_rate) + return Well::ProducerCMode::GRAT; + } + + if (controls.hasControl(Well::ProducerCMode::LRAT) && currentControl != Well::ProducerCMode::LRAT) { + double current_rate = -ws.surface_rates[pu.phase_pos[BlackoilPhases::Liquid]]; + current_rate -= ws.surface_rates[pu.phase_pos[BlackoilPhases::Aqua]]; + + bool skip = false; + if (controls.liquid_rate == controls.oil_rate) { + const double current_water_rate = ws.surface_rates[pu.phase_pos[BlackoilPhases::Aqua]]; + if (std::abs(current_water_rate) < 1e-12) { + skip = true; + deferred_logger.debug("LRAT_ORAT_WELL", "Well " + well_.name() + " The LRAT target is equal the ORAT target and the water rate is zero, skip checking LRAT"); + } + } + if (!skip && controls.liquid_rate < current_rate) + return Well::ProducerCMode::LRAT; + } + + if (controls.hasControl(Well::ProducerCMode::RESV) && currentControl != Well::ProducerCMode::RESV) { + double current_rate = 0.0; + if (pu.phase_used[BlackoilPhases::Aqua]) + current_rate -= ws.reservoir_rates[pu.phase_pos[BlackoilPhases::Aqua]]; + + if (pu.phase_used[BlackoilPhases::Liquid]) + current_rate -= ws.reservoir_rates[pu.phase_pos[BlackoilPhases::Liquid]]; + + if (pu.phase_used[BlackoilPhases::Vapour]) + current_rate -= ws.reservoir_rates[pu.phase_pos[BlackoilPhases::Vapour]]; + + if (controls.prediction_mode && controls.resv_rate < current_rate) + return Well::ProducerCMode::RESV; + + if (!controls.prediction_mode) { + const int fipreg = 0; // not considering the region for now + const int np = well_.numPhases(); + + std::vector surface_rates(np, 0.0); + if (pu.phase_used[BlackoilPhases::Aqua]) + surface_rates[pu.phase_pos[BlackoilPhases::Aqua]] = controls.water_rate; + if (pu.phase_used[BlackoilPhases::Liquid]) + surface_rates[pu.phase_pos[BlackoilPhases::Liquid]] = controls.oil_rate; + if (pu.phase_used[BlackoilPhases::Vapour]) + surface_rates[pu.phase_pos[BlackoilPhases::Vapour]] = controls.gas_rate; + + std::vector voidage_rates(np, 0.0); + calcReservoirVoidageRates(fipreg, well_.pvtRegionIdx(), surface_rates, voidage_rates); + + double resv_rate = 0.0; + for (int p = 0; p < np; ++p) + resv_rate += voidage_rates[p]; + + if (resv_rate < current_rate) + return Well::ProducerCMode::RESV; + } + } + + if (controls.hasControl(Well::ProducerCMode::THP) && currentControl != Well::ProducerCMode::THP) { + const auto& thp = well_.getTHPConstraint(summaryState); + double current_thp = ws.thp; + if (thp > current_thp && !ws.trivial_target) { + // If WVFPEXP item 4 is set to YES1 or YES2 + // switching to THP is prevented if the well will + // produce at a higher rate with THP control + const auto& wvfpexp = well_.wellEcl().getWVFPEXP(); + bool rate_less_than_potential = true; + if (wvfpexp.prevent()) { + for (int p = 0; p < well_.numPhases(); ++p) { + // Currently we use the well potentials here computed before the iterations. + // We may need to recompute the well potentials to get a more + // accurate check here. + rate_less_than_potential = rate_less_than_potential && (-ws.surface_rates[p]) <= ws.well_potentials[p]; + } + } + if (!wvfpexp.prevent() || !rate_less_than_potential) { + thp_limit_violated_but_not_switched = false; + return Well::ProducerCMode::THP; + } else { + thp_limit_violated_but_not_switched = true; + deferred_logger.info("NOT_SWITCHING_TO_THP", + "The THP limit is violated for producer " + + well_.name() + + ". But the rate will increase if switched to THP. " + + "The well is therefore kept at " + Well::ProducerCMode2String(currentControl)); + } + } + } + + return currentControl; +} + +} // namespace Opm diff --git a/opm/simulators/wells/WellConstraints.hpp b/opm/simulators/wells/WellConstraints.hpp new file mode 100644 index 000000000..ab6f3afad --- /dev/null +++ b/opm/simulators/wells/WellConstraints.hpp @@ -0,0 +1,65 @@ +/* + Copyright 2017 SINTEF Digital, Mathematics and Cybernetics. + Copyright 2017 Statoil ASA. + Copyright 2017 IRIS + Copyright 2019 Norce + + 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_WELL_CONSTRAINTS_HEADER_INCLUDED +#define OPM_WELL_CONSTRAINTS_HEADER_INCLUDED + +#include + +#include +#include +#include + +namespace Opm +{ + +class DeferredLogger; +using RegionId = int; +class Rates; +class SingleWellState; +class WellInterfaceGeneric; + +//! \brief Class for computing well group constraints. +class WellConstraints { +public: + //! \brief Constructor sets reference to well. + WellConstraints(const WellInterfaceGeneric& well) : well_(well) {} + + using RateConvFunc = std::function&, + std::vector&)>; + + Well::ProducerCMode + activeProductionConstraint(const SingleWellState& ws, + const SummaryState& summaryState, + const RateConvFunc& calcReservoirVoidageRates, + bool& thp_limit_violated_but_not_switched, + DeferredLogger& deferred_logger) const; + +private: + const WellInterfaceGeneric& well_; //!< Reference to well interface +}; + +} + +#endif // OPM_WELL_CONSTRAINTS_HEADER_INCLUDED diff --git a/opm/simulators/wells/WellInterfaceFluidSystem.cpp b/opm/simulators/wells/WellInterfaceFluidSystem.cpp index ed927046f..b0ae52205 100644 --- a/opm/simulators/wells/WellInterfaceFluidSystem.cpp +++ b/opm/simulators/wells/WellInterfaceFluidSystem.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -81,132 +82,6 @@ calculateReservoirRates(SingleWellState& ws) const ws.reservoir_rates = voidage_rates; } - -template -Well::ProducerCMode -WellInterfaceFluidSystem:: -activeProductionConstraint(const SingleWellState& ws, - const SummaryState& summaryState, - DeferredLogger& deferred_logger) const -{ - const PhaseUsage& pu = this->phaseUsage(); - const auto controls = this->well_ecl_.productionControls(summaryState); - const auto currentControl = ws.production_cmode; - - if (controls.hasControl(Well::ProducerCMode::BHP) && currentControl != Well::ProducerCMode::BHP) { - const double bhp_limit = controls.bhp_limit; - double current_bhp = ws.bhp; - if (bhp_limit > current_bhp) - return Well::ProducerCMode::BHP; - } - - if (controls.hasControl(Well::ProducerCMode::ORAT) && currentControl != Well::ProducerCMode::ORAT) { - double current_rate = -ws.surface_rates[pu.phase_pos[BlackoilPhases::Liquid]]; - if (controls.oil_rate < current_rate) - return Well::ProducerCMode::ORAT; - } - - if (controls.hasControl(Well::ProducerCMode::WRAT) && currentControl != Well::ProducerCMode::WRAT) { - double current_rate = -ws.surface_rates[pu.phase_pos[BlackoilPhases::Aqua]]; - if (controls.water_rate < current_rate) - return Well::ProducerCMode::WRAT; - } - - if (controls.hasControl(Well::ProducerCMode::GRAT) && currentControl != Well::ProducerCMode::GRAT) { - double current_rate = -ws.surface_rates[pu.phase_pos[BlackoilPhases::Vapour]]; - if (controls.gas_rate < current_rate) - return Well::ProducerCMode::GRAT; - } - - if (controls.hasControl(Well::ProducerCMode::LRAT) && currentControl != Well::ProducerCMode::LRAT) { - double current_rate = -ws.surface_rates[pu.phase_pos[BlackoilPhases::Liquid]]; - current_rate -= ws.surface_rates[pu.phase_pos[BlackoilPhases::Aqua]]; - - bool skip = false; - if (controls.liquid_rate == controls.oil_rate) { - const double current_water_rate = ws.surface_rates[pu.phase_pos[BlackoilPhases::Aqua]]; - if (std::abs(current_water_rate) < 1e-12) { - skip = true; - deferred_logger.debug("LRAT_ORAT_WELL", "Well " + this->name() + " The LRAT target is equal the ORAT target and the water rate is zero, skip checking LRAT"); - } - } - if (!skip && controls.liquid_rate < current_rate) - return Well::ProducerCMode::LRAT; - } - - if (controls.hasControl(Well::ProducerCMode::RESV) && currentControl != Well::ProducerCMode::RESV) { - double current_rate = 0.0; - if (pu.phase_used[BlackoilPhases::Aqua]) - current_rate -= ws.reservoir_rates[pu.phase_pos[BlackoilPhases::Aqua]]; - - if (pu.phase_used[BlackoilPhases::Liquid]) - current_rate -= ws.reservoir_rates[pu.phase_pos[BlackoilPhases::Liquid]]; - - if (pu.phase_used[BlackoilPhases::Vapour]) - current_rate -= ws.reservoir_rates[pu.phase_pos[BlackoilPhases::Vapour]]; - - if (controls.prediction_mode && controls.resv_rate < current_rate) - return Well::ProducerCMode::RESV; - - if (!controls.prediction_mode) { - const int fipreg = 0; // not considering the region for now - const int np = number_of_phases_; - - std::vector surface_rates(np, 0.0); - if (pu.phase_used[BlackoilPhases::Aqua]) - surface_rates[pu.phase_pos[BlackoilPhases::Aqua]] = controls.water_rate; - if (pu.phase_used[BlackoilPhases::Liquid]) - surface_rates[pu.phase_pos[BlackoilPhases::Liquid]] = controls.oil_rate; - if (pu.phase_used[BlackoilPhases::Vapour]) - surface_rates[pu.phase_pos[BlackoilPhases::Vapour]] = controls.gas_rate; - - std::vector voidage_rates(np, 0.0); - rateConverter_.calcReservoirVoidageRates(fipreg, pvtRegionIdx_, surface_rates, voidage_rates); - - double resv_rate = 0.0; - for (int p = 0; p < np; ++p) - resv_rate += voidage_rates[p]; - - if (resv_rate < current_rate) - return Well::ProducerCMode::RESV; - } - } - - if (controls.hasControl(Well::ProducerCMode::THP) && currentControl != Well::ProducerCMode::THP) { - const auto& thp = getTHPConstraint(summaryState); - double current_thp = ws.thp; - if (thp > current_thp && !ws.trivial_target) { - // If WVFPEXP item 4 is set to YES1 or YES2 - // switching to THP is prevented if the well will - // produce at a higher rate with THP control - const auto& wvfpexp = this->well_ecl_.getWVFPEXP(); - bool rate_less_than_potential = true; - if (wvfpexp.prevent()) { - for (int p = 0; p < number_of_phases_; ++p) { - // Currently we use the well potentials here computed before the iterations. - // We may need to recompute the well potentials to get a more - // accurate check here. - rate_less_than_potential = rate_less_than_potential && (-ws.surface_rates[p]) <= ws.well_potentials[p]; - } - } - if(!wvfpexp.prevent() || !rate_less_than_potential) { - this->operability_status_.thp_limit_violated_but_not_switched = false; - return Well::ProducerCMode::THP; - } else { - this->operability_status_.thp_limit_violated_but_not_switched = true; - deferred_logger.info("NOT_SWITCHING_TO_THP", - "The THP limit is violated for producer " + - this->name() + - ". But the rate will increase if switched to THP. " + - "The well is therefore kept at " + Well::ProducerCMode2String(currentControl)); - } - } - } - - return currentControl; -} - - template Well::InjectorCMode WellInterfaceFluidSystem:: @@ -308,8 +183,20 @@ checkIndividualConstraints(SingleWellState& ws, const SummaryState& summaryState, DeferredLogger& deferred_logger) const { + auto rRates = [this](const int fipreg, + const int pvtRegion, + const std::vector& surface_rates, + std::vector& voidage_rates) + { + return rateConverter_.calcReservoirVoidageRates(fipreg, pvtRegion, + surface_rates, voidage_rates); + }; + if (this->well_ecl_.isProducer()) { - auto new_cmode = this->activeProductionConstraint(ws, summaryState, deferred_logger); + auto new_cmode = WellConstraints(*this).activeProductionConstraint(ws, summaryState, + rRates, + this->operability_status_.thp_limit_violated_but_not_switched, + deferred_logger); if (new_cmode != ws.production_cmode) { ws.production_cmode = new_cmode; return true; diff --git a/opm/simulators/wells/WellInterfaceFluidSystem.hpp b/opm/simulators/wells/WellInterfaceFluidSystem.hpp index 18f2ec8eb..b1427e227 100644 --- a/opm/simulators/wells/WellInterfaceFluidSystem.hpp +++ b/opm/simulators/wells/WellInterfaceFluidSystem.hpp @@ -85,10 +85,6 @@ protected: const SummaryState& summaryState, DeferredLogger& deferred_logger) const; - Well::ProducerCMode activeProductionConstraint(const SingleWellState& ws, - const SummaryState& summaryState, - DeferredLogger& deferred_logger) const; - bool checkGroupConstraints(WellState& well_state, const GroupState& group_state, const Schedule& schedule,