Merge pull request #1965 from totto82/killDistr

Reimplementation of the well control code
This commit is contained in:
Atgeirr Flø Rasmussen 2019-10-22 10:25:32 +02:00 committed by GitHub
commit 5267f3cf17
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 3296 additions and 1988 deletions

View File

@ -197,6 +197,7 @@ list (APPEND PUBLIC_HEADER_FILES
opm/simulators/wells/VFPHelpers.hpp opm/simulators/wells/VFPHelpers.hpp
opm/simulators/wells/VFPInjProperties.hpp opm/simulators/wells/VFPInjProperties.hpp
opm/simulators/wells/VFPProdProperties.hpp opm/simulators/wells/VFPProdProperties.hpp
opm/simulators/wells/WellGroupHelpers.hpp
opm/simulators/wells/WellHelpers.hpp opm/simulators/wells/WellHelpers.hpp
opm/simulators/wells/WellInterface.hpp opm/simulators/wells/WellInterface.hpp
opm/simulators/wells/WellInterface_impl.hpp opm/simulators/wells/WellInterface_impl.hpp

View File

@ -270,12 +270,6 @@ namespace Opm {
OPM_THROW(Opm::NumericalIssue, "Too large residual found!"); OPM_THROW(Opm::NumericalIssue, "Too large residual found!");
} }
} }
// checking whether the group targets are converged
if (wellModel().wellCollection().groupControlActive()) {
report.converged = report.converged && wellModel().wellCollection().groupTargetConverged(wellModel().wellState().wellRates());
}
report.update_time += perfTimer.stop(); report.update_time += perfTimer.stop();
residual_norms_history_.push_back(residual_norms); residual_norms_history_.push_back(residual_norms);
if (!report.converged) { if (!report.converged) {

View File

@ -429,7 +429,6 @@ namespace MissingFeatures {
"NOWARN", "NOWARN",
"NSTACK", "NSTACK",
"NUMRES", "NUMRES",
"NUPCOL",
"OLDTRAN", "OLDTRAN",
"OPERATER", "OPERATER",
"OPTIONS", "OPTIONS",

View File

@ -35,6 +35,7 @@
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp> #include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellTestState.hpp> #include <opm/parser/eclipse/EclipseState/Schedule/Well/WellTestState.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Group/GuideRate.hpp>
#include <opm/core/wells.h> #include <opm/core/wells.h>
#include <opm/core/wells/WellCollection.hpp> #include <opm/core/wells/WellCollection.hpp>
@ -47,6 +48,7 @@
#include <opm/simulators/wells/WellInterface.hpp> #include <opm/simulators/wells/WellInterface.hpp>
#include <opm/simulators/wells/StandardWell.hpp> #include <opm/simulators/wells/StandardWell.hpp>
#include <opm/simulators/wells/MultisegmentWell.hpp> #include <opm/simulators/wells/MultisegmentWell.hpp>
#include <opm/simulators/wells/WellGroupHelpers.hpp>
#include <opm/simulators/timestepping/gatherConvergenceReport.hpp> #include <opm/simulators/timestepping/gatherConvergenceReport.hpp>
#include <dune/common/fmatrix.hh> #include <dune/common/fmatrix.hh>
#include <dune/istl/bcrsmatrix.hh> #include <dune/istl/bcrsmatrix.hh>
@ -203,11 +205,6 @@ namespace Opm {
// Check if well equations is converged. // Check if well equations is converged.
ConvergenceReport getWellConvergence(const std::vector<Scalar>& B_avg) const; ConvergenceReport getWellConvergence(const std::vector<Scalar>& B_avg) const;
// return all the wells.
const WellCollection& wellCollection() const;
// return non const reference to all the wells.
WellCollection& wellCollection();
// return the internal well state, ignore the passed one. // return the internal well state, ignore the passed one.
// Used by the legacy code to make it compatible with the legacy well models. // Used by the legacy code to make it compatible with the legacy well models.
const WellState& wellState(const WellState& well_state OPM_UNUSED) const; const WellState& wellState(const WellState& well_state OPM_UNUSED) const;
@ -276,6 +273,7 @@ namespace Opm {
SimulatorReport last_report_; SimulatorReport last_report_;
WellTestState wellTestState_; WellTestState wellTestState_;
std::unique_ptr<GuideRate> guideRate_;
// used to better efficiency of calcuation // used to better efficiency of calcuation
mutable BVector scaleAddRes_; mutable BVector scaleAddRes_;
@ -306,9 +304,7 @@ namespace Opm {
// xw to update Well State // xw to update Well State
void recoverWellSolutionAndUpdateWellState(const BVector& x); void recoverWellSolutionAndUpdateWellState(const BVector& x);
void updateWellControls(Opm::DeferredLogger& deferred_logger); void updateWellControls(Opm::DeferredLogger& deferred_logger, const bool checkGroupControl);
void updateGroupControls(Opm::DeferredLogger& deferred_logger);
// setting the well_solutions_ based on well_state. // setting the well_solutions_ based on well_state.
void updatePrimaryVariables(Opm::DeferredLogger& deferred_logger); void updatePrimaryVariables(Opm::DeferredLogger& deferred_logger);
@ -320,17 +316,12 @@ namespace Opm {
void computeAverageFormationFactor(std::vector<Scalar>& B_avg) const; void computeAverageFormationFactor(std::vector<Scalar>& B_avg) const;
void applyVREPGroupControl();
void computeWellVoidageRates(std::vector<double>& well_voidage_rates,
std::vector<double>& voidage_conversion_coeffs) const;
// Calculating well potentials for each well // Calculating well potentials for each well
void computeWellPotentials(std::vector<double>& well_potentials, Opm::DeferredLogger& deferred_logger); void computeWellPotentials(std::vector<double>& well_potentials, const int reportStepIdx, Opm::DeferredLogger& deferred_logger);
const std::vector<double>& wellPerfEfficiencyFactors() const; const std::vector<double>& wellPerfEfficiencyFactors() const;
void calculateEfficiencyFactors(); void calculateEfficiencyFactors(const int reportStepIdx);
// it should be able to go to prepareTimeStep(), however, the updateWellControls() and initPrimaryVariablesEvaluation() // it should be able to go to prepareTimeStep(), however, the updateWellControls() and initPrimaryVariablesEvaluation()
// makes it a little more difficult. unless we introduce if (iterationIdx != 0) to avoid doing the above functions // makes it a little more difficult. unless we introduce if (iterationIdx != 0) to avoid doing the above functions
@ -350,18 +341,12 @@ namespace Opm {
int numPhases() const; int numPhases() const;
void resetWellControlFromState() const;
void assembleWellEq(const std::vector<Scalar>& B_avg, const double dt, Opm::DeferredLogger& deferred_logger); void assembleWellEq(const std::vector<Scalar>& B_avg, const double dt, Opm::DeferredLogger& deferred_logger);
// some preparation work, mostly related to group control and RESV, // some preparation work, mostly related to group control and RESV,
// at the beginning of each time step (Not report step) // at the beginning of each time step (Not report step)
void prepareTimeStep(Opm::DeferredLogger& deferred_logger); void prepareTimeStep(Opm::DeferredLogger& deferred_logger);
void prepareGroupControl(Opm::DeferredLogger& deferred_logger);
void computeRESV(Opm::DeferredLogger& deferred_logger);
void extractLegacyCellPvtRegionIndex_(); void extractLegacyCellPvtRegionIndex_();
void extractLegacyDepth_(); void extractLegacyDepth_();
@ -391,6 +376,21 @@ namespace Opm {
bool anyMSWellOpenLocal(const Wells* wells) const; bool anyMSWellOpenLocal(const Wells* wells) const;
const Well2& getWellEcl(const std::string& well_name) const; const Well2& getWellEcl(const std::string& well_name) const;
void checkGroupConstraints(const Group2& group, Opm::DeferredLogger& deferred_logger);
void actionOnBrokenConstraints(const Group2& group, const Group2::ExceedAction& exceed_action, const Group2::ProductionCMode& newControl, const int reportStepIdx, Opm::DeferredLogger& deferred_logger);
void actionOnBrokenConstraints(const Group2& group, const Group2::InjectionCMode& newControl, const int reportStepIdx, Opm::DeferredLogger& deferred_logger);
WellInterfacePtr getWell(const std::string& well_name) const;
void updateWsolvent(const Group2& group, const Schedule& schedule, const int reportStepIdx, const WellStateFullyImplicitBlackoil& wellState);
void setWsolvent(const Group2& group, const Schedule& schedule, const int reportStepIdx, double wsolvent);
}; };

File diff suppressed because it is too large Load Diff

View File

@ -124,7 +124,7 @@ namespace Opm
Opm::DeferredLogger& deferred_logger) const override; Opm::DeferredLogger& deferred_logger) const override;
/// check whether the well equations get converged for this well /// check whether the well equations get converged for this well
virtual ConvergenceReport getWellConvergence(const std::vector<double>& B_avg, Opm::DeferredLogger& deferred_logger) const override; virtual ConvergenceReport getWellConvergence(const WellState& well_state, const std::vector<double>& B_avg, Opm::DeferredLogger& deferred_logger) const override;
/// Ax = Ax - C D^-1 B x /// Ax = Ax - C D^-1 B x
virtual void apply(const BVector& x, BVector& Ax) const override; virtual void apply(const BVector& x, BVector& Ax) const override;
@ -188,7 +188,6 @@ namespace Opm
using Base::saturation_table_number_; using Base::saturation_table_number_;
using Base::well_efficiency_factor_; using Base::well_efficiency_factor_;
using Base::gravity_; using Base::gravity_;
using Base::well_controls_;
using Base::perf_depth_; using Base::perf_depth_;
using Base::num_components_; using Base::num_components_;
using Base::connectionRates_; using Base::connectionRates_;
@ -200,6 +199,8 @@ namespace Opm
using Base::ebosCompIdxToFlowCompIdx; using Base::ebosCompIdxToFlowCompIdx;
using Base::getAllowCrossFlow; using Base::getAllowCrossFlow;
using Base::scalingFactor; using Base::scalingFactor;
using Base::wellIsStopped_;
// TODO: trying to use the information from the Well opm-parser as much // TODO: trying to use the information from the Well opm-parser as much
// as possible, it will possibly be re-implemented later for efficiency reason. // as possible, it will possibly be re-implemented later for efficiency reason.
@ -325,7 +326,7 @@ namespace Opm
EvalWell getSegmentRate(const int seg, const int comp_idx) const; EvalWell getSegmentRate(const int seg, const int comp_idx) const;
EvalWell getSegmentRateUpwinding(const int seg, const int comp_idx) const; EvalWell getSegmentRateUpwinding(const int seg, const size_t comp_idx) const;
EvalWell getSegmentGTotal(const int seg) const; EvalWell getSegmentGTotal(const int seg) const;
@ -340,7 +341,10 @@ namespace Opm
std::vector<double>& well_flux, std::vector<double>& well_flux,
Opm::DeferredLogger& deferred_logger); Opm::DeferredLogger& deferred_logger);
void assembleControlEq(Opm::DeferredLogger& deferred_logger) const; void assembleControlEq(const WellState& well_state, const Opm::Schedule& schedule, const SummaryState& summaryState, Opm::DeferredLogger& deferred_logger);
void assembleGroupProductionControl(const Group2& group, const WellState& well_state, const Opm::Schedule& schedule, const SummaryState& summaryState, EvalWell& control_eq, double efficincyFactor, Opm::DeferredLogger& deferred_logger);
void assembleGroupInjectionControl(const Group2& group, const WellState& well_state, const Opm::Schedule& schedule, const SummaryState& summaryState, const Well2::InjectorType& injectorType, EvalWell& control_eq, double efficincyFactor, Opm::DeferredLogger& deferred_logger);
void assemblePressureEq(const int seg) const; void assemblePressureEq(const int seg) const;
@ -392,12 +396,14 @@ namespace Opm
void detectOscillations(const std::vector<double>& measure_history, void detectOscillations(const std::vector<double>& measure_history,
const int it, bool& oscillate, bool& stagnate) const; const int it, bool& oscillate, bool& stagnate) const;
double getResidualMeasureValue(const std::vector<double>& residuals, double getResidualMeasureValue(const WellState& well_state,
const std::vector<double>& residuals,
DeferredLogger& deferred_logger) const; DeferredLogger& deferred_logger) const;
double getControlTolerance(DeferredLogger& deferred_logger) const; double getControlTolerance(const WellState& well_state, DeferredLogger& deferred_logger) const;
void checkConvergenceControlEq(ConvergenceReport& report, void checkConvergenceControlEq(const WellState& well_state,
ConvergenceReport& report,
DeferredLogger& deferred_logger) const; DeferredLogger& deferred_logger) const;
void updateUpwindingSegments(); void updateUpwindingSegments();

File diff suppressed because it is too large Load Diff

View File

@ -37,6 +37,8 @@
#include <dune/common/dynvector.hh> #include <dune/common/dynvector.hh>
#include <dune/common/dynmatrix.hh> #include <dune/common/dynmatrix.hh>
#include <boost/optional.hpp>
namespace Opm namespace Opm
{ {
@ -163,7 +165,8 @@ namespace Opm
Opm::DeferredLogger& deferred_logger) const override; Opm::DeferredLogger& deferred_logger) const override;
/// check whether the well equations get converged for this well /// check whether the well equations get converged for this well
virtual ConvergenceReport getWellConvergence(const std::vector<double>& B_avg, virtual ConvergenceReport getWellConvergence(const WellState& well_state,
const std::vector<double>& B_avg,
Opm::DeferredLogger& deferred_logger) const override; Opm::DeferredLogger& deferred_logger) const override;
/// Ax = Ax - C D^-1 B x /// Ax = Ax - C D^-1 B x
@ -210,10 +213,9 @@ namespace Opm
using Base::wsolvent; using Base::wsolvent;
using Base::wpolymer; using Base::wpolymer;
using Base::wfoam; using Base::wfoam;
using Base::wellHasTHPConstraints;
using Base::mostStrictBhpFromBhpLimits;
using Base::scalingFactor; using Base::scalingFactor;
using Base::scaleProductivityIndex; using Base::scaleProductivityIndex;
using Base::mostStrictBhpFromBhpLimits;
// protected member variables from the Base class // protected member variables from the Base class
using Base::current_step_; using Base::current_step_;
@ -232,7 +234,6 @@ namespace Opm
using Base::comp_frac_; using Base::comp_frac_;
using Base::well_index_; using Base::well_index_;
using Base::index_of_well_; using Base::index_of_well_;
using Base::well_controls_;
using Base::well_type_; using Base::well_type_;
using Base::num_components_; using Base::num_components_;
using Base::connectionRates_; using Base::connectionRates_;
@ -241,6 +242,8 @@ namespace Opm
using Base::perf_length_; using Base::perf_length_;
using Base::bore_diameters_; using Base::bore_diameters_;
using Base::wellIsStopped_;
// total number of the well equations and primary variables // total number of the well equations and primary variables
// there might be extra equations be used, numWellEq will be updated during the initialization // there might be extra equations be used, numWellEq will be updated during the initialization
int numWellEq_ = numStaticWellEq; int numWellEq_ = numStaticWellEq;
@ -280,6 +283,8 @@ namespace Opm
mutable std::vector<double> ipr_a_; mutable std::vector<double> ipr_a_;
mutable std::vector<double> ipr_b_; mutable std::vector<double> ipr_b_;
bool changed_to_stopped_this_step_ = false;
const EvalWell& getBhp() const; const EvalWell& getBhp() const;
EvalWell getQs(const int comp_idx) const; EvalWell getQs(const int comp_idx) const;
@ -358,13 +363,11 @@ namespace Opm
Opm::DeferredLogger& deferred_logger); Opm::DeferredLogger& deferred_logger);
std::vector<double> computeWellPotentialWithTHP(const Simulator& ebosSimulator, std::vector<double> computeWellPotentialWithTHP(const Simulator& ebosSimulator,
const std::vector<Scalar>& B_avg, Opm::DeferredLogger& deferred_logger) const;
const double initial_bhp, // bhp from BHP constraints
const std::vector<double>& initial_potential,
Opm::DeferredLogger& deferred_logger);
template <class ValueType> template <class ValueType>
ValueType calculateBhpFromThp(const std::vector<ValueType>& rates, const int control_index, Opm::DeferredLogger& deferred_logger) const; ValueType calculateBhpFromThp(const std::vector<ValueType>& rates, const Well2& well, const SummaryState& summaryState, Opm::DeferredLogger& deferred_logger) const;
double calculateThpFromBhp(const std::vector<double>& rates, const double bhp, Opm::DeferredLogger& deferred_logger) const; double calculateThpFromBhp(const std::vector<double>& rates, const double bhp, Opm::DeferredLogger& deferred_logger) const;
@ -390,7 +393,11 @@ namespace Opm
void updateThp(WellState& well_state, Opm::DeferredLogger& deferred_logger) const; void updateThp(WellState& well_state, Opm::DeferredLogger& deferred_logger) const;
void assembleControlEq(Opm::DeferredLogger& deferred_logger); void assembleControlEq(const WellState& well_state, const Opm::Schedule& schedule, const SummaryState& summaryState, Opm::DeferredLogger& deferred_logger);
void assembleGroupProductionControl(const Group2& group, const WellState& well_state, const Opm::Schedule& schedule, const SummaryState& summaryState, EvalWell& control_eq, double efficincyFactor, Opm::DeferredLogger& deferred_logger);
void assembleGroupInjectionControl(const Group2& group, const WellState& well_state, const Opm::Schedule& schedule, const SummaryState& summaryState, const Well2::InjectorType& injectorType, EvalWell& control_eq, double efficincyFactor, Opm::DeferredLogger& deferred_logger);
// handle the non reasonable fractions due to numerical overshoot // handle the non reasonable fractions due to numerical overshoot
void processFractions() const; void processFractions() const;
@ -416,15 +423,6 @@ namespace Opm
// check whether the well is operable under THP limit with current reservoir condition // check whether the well is operable under THP limit with current reservoir condition
void checkOperabilityUnderTHPLimitProducer(const Simulator& ebos_simulator, Opm::DeferredLogger& deferred_logger); void checkOperabilityUnderTHPLimitProducer(const Simulator& ebos_simulator, Opm::DeferredLogger& deferred_logger);
// update WellState based on IPR and associated VFP table
void updateWellStateWithTHPTargetIPR(const Simulator& ebos_simulator,
WellState& well_state,
Opm::DeferredLogger& deferred_logger) const;
void updateWellStateWithTHPTargetIPRProducer(const Simulator& ebos_simulator,
WellState& well_state,
Opm::DeferredLogger& deferred_logger) const;
// for a well, when all drawdown are in the wrong direction, then this well will not // for a well, when all drawdown are in the wrong direction, then this well will not
// be able to produce/inject . // be able to produce/inject .
bool allDrawDownWrongDirection(const Simulator& ebos_simulator) const; bool allDrawDownWrongDirection(const Simulator& ebos_simulator) const;
@ -441,11 +439,6 @@ namespace Opm
// TODO: looking for better alternative to avoid wrong-signed well rates // TODO: looking for better alternative to avoid wrong-signed well rates
bool openCrossFlowAvoidSingularity(const Simulator& ebos_simulator) const; bool openCrossFlowAvoidSingularity(const Simulator& ebos_simulator) const;
// calculate the BHP from THP target based on IPR
// TODO: we need to check the operablility here first, if not operable, then maybe there is
// no point to do this
double calculateBHPWithTHPTargetIPR(Opm::DeferredLogger& deferred_logger) const;
// relaxation factor considering only one fraction value // relaxation factor considering only one fraction value
static double relaxationFactorFraction(const double old_value, static double relaxationFactorFraction(const double old_value,
const double dx); const double dx);
@ -493,7 +486,8 @@ namespace Opm
virtual void updateWaterThroughput(const double dt, WellState& well_state) const override; virtual void updateWaterThroughput(const double dt, WellState& well_state) const override;
// checking the convergence of the well control equations // checking the convergence of the well control equations
void checkConvergenceControlEq(ConvergenceReport& report, void checkConvergenceControlEq(const WellState& well_state,
ConvergenceReport& report,
DeferredLogger& deferred_logger) const; DeferredLogger& deferred_logger) const;
// checking convergence of extra equations, if there are any // checking convergence of extra equations, if there are any
@ -507,6 +501,10 @@ namespace Opm
const int perf, const int perf,
DeferredLogger& deferred_logger); DeferredLogger& deferred_logger);
boost::optional<double> computeBhpAtThpLimitProd(const Simulator& ebos_simulator,
const SummaryState& summary_state,
DeferredLogger& deferred_logger) const;
}; };
} }

File diff suppressed because it is too large Load Diff

View File

@ -258,6 +258,13 @@ inline InterpData findInterpData(const double& value_in, const std::vector<doubl
} }
} }
// Disallow extrapolation with higher factor than 3.0.
// The factor 3.0 has been chosen because it works well
// with certain testcases, and may not be optimal.
if (retval.factor_ > 3.0) {
retval.factor_ = 3.0;
}
return retval; return retval;
} }

View File

@ -0,0 +1,329 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
#ifndef OPM_WELLGROUPHELPERS_HEADER_INCLUDED
#define OPM_WELLGROUPHELPERS_HEADER_INCLUDED
#include <vector>
namespace Opm {
namespace wellGroupHelpers
{
inline void setCmodeGroup(const Group2& group, const Schedule& schedule, const SummaryState& summaryState, const int reportStepIdx, WellStateFullyImplicitBlackoil& wellState) {
for (const std::string& groupName : group.groups()) {
setCmodeGroup( schedule.getGroup2(groupName, reportStepIdx), schedule, summaryState, reportStepIdx, wellState);
}
// use FLD as default control
wellState.setCurrentInjectionGroupControl(group.name(), Group2::InjectionCMode::FLD);
wellState.setCurrentProductionGroupControl(group.name(), Group2::ProductionCMode::FLD);
if (group.isInjectionGroup()) {
const auto controls = group.injectionControls(summaryState);
wellState.setCurrentInjectionGroupControl(group.name(), controls.cmode);
}
if (group.isProductionGroup()) {
const auto controls = group.productionControls(summaryState);
wellState.setCurrentProductionGroupControl(group.name(), controls.cmode);
}
}
inline void accumulateGroupEfficiencyFactor(const Group2& group, const Schedule& schedule, const int reportStepIdx, double& factor) {
factor *= group.getGroupEfficiencyFactor();
if (group.parent() != "FIELD")
accumulateGroupEfficiencyFactor(schedule.getGroup2(group.parent(), reportStepIdx), schedule, reportStepIdx, factor);
}
inline void setGroupControl(const Group2& group, const Schedule& schedule, const int reportStepIdx, const bool injector, WellStateFullyImplicitBlackoil& wellState, std::ostringstream& ss) {
for (const std::string& groupName : group.groups()) {
const Group2& groupTmp = schedule.getGroup2(groupName, reportStepIdx);
setGroupControl(groupTmp, schedule, reportStepIdx, injector, wellState, ss);
if (injector)
wellState.setCurrentInjectionGroupControl(groupName, Group2::InjectionCMode::FLD);
else
wellState.setCurrentProductionGroupControl(groupName, Group2::ProductionCMode::FLD);
}
const auto& end = wellState.wellMap().end();
for (const std::string& wellName : group.wells()) {
const auto& it = wellState.wellMap().find( wellName );
if (it == end) // the well is not found
continue;
int well_index = it->second[0];
const auto& wellEcl = schedule.getWell2(wellName, reportStepIdx);
if (wellEcl.getStatus() == Well2::Status::SHUT)
continue;
if (!wellEcl.isAvailableForGroupControl())
continue;
if (wellEcl.isProducer() && !injector) {
if (wellState.currentProductionControls()[well_index] != Well2::ProducerCMode::GRUP) {
wellState.currentProductionControls()[well_index] = Well2::ProducerCMode::GRUP;
ss <<"\n Producer " << wellName << " switches to GRUP control limit";
}
}
if (wellEcl.isInjector() && injector) {
if (wellState.currentInjectionControls()[well_index] != Well2::InjectorCMode::GRUP) {
wellState.currentInjectionControls()[well_index] = Well2::InjectorCMode::GRUP;
ss <<"\n Injector " << wellName << " switches to GRUP control limit";
}
}
}
}
inline void computeGroupTargetReduction(const Group2& group, const WellStateFullyImplicitBlackoil& wellState, const Schedule& schedule, const int reportStepIdx, const int phasePos, const bool isInjector, double& groupTargetReduction )
{
for (const std::string& groupName : group.groups()) {
const Group2& groupTmp = schedule.getGroup2(groupName, reportStepIdx);
computeGroupTargetReduction(groupTmp, wellState, schedule, reportStepIdx, phasePos, isInjector, groupTargetReduction);
}
for (const std::string& wellName : group.wells()) {
const auto& wellTmp = schedule.getWell2(wellName, reportStepIdx);
if (wellTmp.isProducer() && isInjector)
continue;
if (wellTmp.isInjector() && !isInjector)
continue;
if (wellTmp.getStatus() == Well2::Status::SHUT)
continue;
const auto& end = wellState.wellMap().end();
const auto& it = wellState.wellMap().find( wellName );
if (it == end) // the well is not found
continue;
int well_index = it->second[0];
const auto wellrate_index = well_index * wellState.numPhases();
// add contributino from wells not under group control
if (isInjector) {
if (wellState.currentInjectionControls()[well_index] != Well2::InjectorCMode::GRUP)
groupTargetReduction += wellState.wellRates()[wellrate_index + phasePos];
} else {
if (wellState.currentProductionControls()[well_index] != Well2::ProducerCMode::GRUP)
groupTargetReduction -= wellState.wellRates()[wellrate_index + phasePos];
}
}
}
inline double sumWellPhaseRates(const std::vector<double>& rates, const Group2& group, const Schedule& schedule, const WellStateFullyImplicitBlackoil& wellState, const int reportStepIdx, const int phasePos,
const bool injector) {
double rate = 0.0;
for (const std::string& groupName : group.groups()) {
const Group2& groupTmp = schedule.getGroup2(groupName, reportStepIdx);
rate += groupTmp.getGroupEfficiencyFactor()*sumWellPhaseRates(rates, groupTmp, schedule, wellState, reportStepIdx, phasePos, injector);
}
const auto& end = wellState.wellMap().end();
for (const std::string& wellName : group.wells()) {
const auto& it = wellState.wellMap().find( wellName );
if (it == end) // the well is not found
continue;
int well_index = it->second[0];
const auto& wellEcl = schedule.getWell2(wellName, reportStepIdx);
//only count producers or injectors
if ( (wellEcl.isProducer() && injector) || (wellEcl.isInjector() && !injector))
continue;
if (wellEcl.getStatus() == Well2::Status::SHUT)
continue;
double factor = wellEcl.getEfficiencyFactor();
const auto wellrate_index = well_index * wellState.numPhases();
if (injector)
rate += factor * rates[ wellrate_index + phasePos];
else
rate -= factor * rates[ wellrate_index + phasePos];
}
return rate;
}
inline double sumWellRates(const Group2& group, const Schedule& schedule, const WellStateFullyImplicitBlackoil& wellState, const int reportStepIdx, const int phasePos, const bool injector) {
return sumWellPhaseRates(wellState.wellRates(), group, schedule, wellState, reportStepIdx, phasePos, injector);
}
inline double sumWellResRates(const Group2& group, const Schedule& schedule, const WellStateFullyImplicitBlackoil& wellState, const int reportStepIdx, const int phasePos, const bool injector) {
return sumWellPhaseRates(wellState.wellReservoirRates(), group, schedule, wellState, reportStepIdx, phasePos, injector);
}
inline double sumSolventRates(const Group2& group, const Schedule& schedule, const WellStateFullyImplicitBlackoil& wellState, const int reportStepIdx, const bool injector) {
double rate = 0.0;
for (const std::string& groupName : group.groups()) {
const Group2& groupTmp = schedule.getGroup2(groupName, reportStepIdx);
rate += groupTmp.getGroupEfficiencyFactor()*sumSolventRates(groupTmp, schedule, wellState, reportStepIdx, injector);
}
const auto& end = wellState.wellMap().end();
for (const std::string& wellName : group.wells()) {
const auto& it = wellState.wellMap().find( wellName );
if (it == end) // the well is not found
continue;
int well_index = it->second[0];
const auto& wellEcl = schedule.getWell2(wellName, reportStepIdx);
//only count producers or injectors
if ( (wellEcl.isProducer() && injector) || (wellEcl.isInjector() && !injector))
continue;
if (wellEcl.getStatus() == Well2::Status::SHUT)
continue;
double factor = wellEcl.getEfficiencyFactor();
if (injector)
rate += factor * wellState.solventWellRate(well_index);
else
rate -= factor * wellState.solventWellRate(well_index);
}
return rate;
}
inline void updateGuideRateForGroups(const Group2& group, const Schedule& schedule, const PhaseUsage& pu, const int reportStepIdx, const double& simTime, GuideRate* guideRate, WellStateFullyImplicitBlackoil& wellState) {
for (const std::string& groupName : group.groups()) {
const Group2& groupTmp = schedule.getGroup2(groupName, reportStepIdx);
updateGuideRateForGroups(groupTmp, schedule, pu, reportStepIdx, simTime, guideRate, wellState);
}
bool isInjector = group.isInjectionGroup();
bool isProducer = group.isProductionGroup();
if (!isInjector && !isProducer)
return;
double oilPot = 0.0;
if (pu.phase_used[BlackoilPhases::Liquid])
oilPot = sumWellPhaseRates(wellState.wellPotentials(), group, schedule, wellState, reportStepIdx, pu.phase_pos[BlackoilPhases::Liquid], isInjector);
double gasPot = 0.0;
if (pu.phase_used[BlackoilPhases::Vapour])
gasPot = sumWellPhaseRates(wellState.wellPotentials(), group, schedule, wellState, reportStepIdx, pu.phase_pos[BlackoilPhases::Vapour], isInjector);
double waterPot = 0.0;
if (pu.phase_used[BlackoilPhases::Aqua])
waterPot = sumWellPhaseRates(wellState.wellPotentials(), group, schedule, wellState, reportStepIdx, pu.phase_pos[BlackoilPhases::Aqua], isInjector);
guideRate->compute(group.name(), reportStepIdx, simTime, oilPot, gasPot, waterPot);
}
inline double wellFractionFromGuideRates(const Well2& well, const Schedule& schedule, const WellStateFullyImplicitBlackoil& wellState, const int reportStepIdx, const GuideRate* guideRate, const Well2::GuideRateTarget& wellTarget, const bool isInjector) {
double groupTotalGuideRate = 0.0;
const Group2& groupTmp = schedule.getGroup2(well.groupName(), reportStepIdx);
for (const std::string& wellName : groupTmp.wells()) {
const auto& wellTmp = schedule.getWell2(wellName, reportStepIdx);
if (wellTmp.isProducer() && isInjector)
continue;
if (wellTmp.isInjector() && !isInjector)
continue;
if (wellTmp.getStatus() == Well2::Status::SHUT)
continue;
const auto& end = wellState.wellMap().end();
const auto& it = wellState.wellMap().find( wellName );
if (it == end) // the well is not found
continue;
int well_index = it->second[0];
// only count wells under group control
if (isInjector) {
if (wellState.currentInjectionControls()[well_index] != Well2::InjectorCMode::GRUP)
continue;
} else {
if (wellState.currentProductionControls()[well_index] != Well2::ProducerCMode::GRUP)
continue;
}
groupTotalGuideRate += guideRate->get(wellName, wellTarget);
}
if (groupTotalGuideRate == 0.0)
return 0.0;
double wellGuideRate = guideRate->get(well.name(), wellTarget);
return wellGuideRate / groupTotalGuideRate;
}
inline double groupFractionFromGuideRates(const Group2& group, const Schedule& schedule, const WellStateFullyImplicitBlackoil& wellState, const int reportStepIdx, const GuideRate* guideRate, const Group2::GuideRateTarget& groupTarget, const bool isInjector) {
double groupTotalGuideRate = 0.0;
const Group2& groupParent = schedule.getGroup2(group.parent(), reportStepIdx);
for (const std::string& groupName : groupParent.groups()) {
const Group2& groupTmp = schedule.getGroup2(groupName, reportStepIdx);
// only count group under group control from its parent
if (isInjector) {
const Group2::InjectionCMode& currentGroupControl = wellState.currentInjectionGroupControl(group.name());
if (currentGroupControl != Group2::InjectionCMode::FLD)
continue;
} else {
const Group2::ProductionCMode& currentGroupControl = wellState.currentProductionGroupControl(group.name());
if (currentGroupControl != Group2::ProductionCMode::FLD)
continue;
}
if ( (groupTmp.isProductionGroup() && !isInjector) ||
(groupTmp.isInjectionGroup() && isInjector) )
{
groupTotalGuideRate += guideRate->get(groupName, groupTarget);
}
}
if (groupTotalGuideRate == 0.0)
return 0.0;
double groupGuideRate = guideRate->get(group.name(), groupTarget);
return groupGuideRate / groupTotalGuideRate;
}
inline void accumulateGroupFractions(const std::string& groupName, const std::string& controlGroupName, const Schedule& schedule, const WellStateFullyImplicitBlackoil& wellState,const int reportStepIdx, const GuideRate* guideRate, const Group2::GuideRateTarget& groupTarget, const bool isInjector, double fraction) {
const Group2& group = schedule.getGroup2(groupName, reportStepIdx);
if (groupName != controlGroupName) {
fraction *= groupFractionFromGuideRates(group, schedule, wellState, reportStepIdx, guideRate, groupTarget, isInjector);
accumulateGroupFractions(group.parent(), controlGroupName, schedule, wellState, reportStepIdx, guideRate, groupTarget, isInjector, fraction);
}
return;
}
} // namespace wellGroupHelpers
}
#endif

View File

@ -36,90 +36,7 @@ namespace Opm {
{ {
inline // --------- Types ---------
double rateToCompare(const std::vector<double>& well_phase_flow_rate,
const int well,
const int num_phases,
const double* distr)
{
double rate = 0.0;
for (int phase = 0; phase < num_phases; ++phase) {
// Important: well_phase_flow_rate is ordered with all phase rates for first
// well first, then all phase rates for second well etc.
rate += well_phase_flow_rate[well*num_phases + phase] * distr[phase];
}
return rate;
}
inline
bool constraintBroken(const std::vector<double>& bhp,
const std::vector<double>& thp,
const std::vector<double>& well_phase_flow_rate,
const int well,
const int num_phases,
const WellType& well_type,
const WellControls* wc,
const int ctrl_index)
{
const WellControlType ctrl_type = well_controls_iget_type(wc, ctrl_index);
const double target = well_controls_iget_target(wc, ctrl_index);
const double* distr = well_controls_iget_distr(wc, ctrl_index);
bool broken = false;
switch (well_type) {
case INJECTOR:
{
switch (ctrl_type) {
case BHP:
broken = bhp[well] > target;
break;
case THP:
broken = thp[well] > target;
break;
case RESERVOIR_RATE: // Intentional fall-through
case SURFACE_RATE:
broken = rateToCompare(well_phase_flow_rate,
well, num_phases, distr) > target;
break;
}
}
break;
case PRODUCER:
{
switch (ctrl_type) {
case BHP:
broken = bhp[well] < target;
break;
case THP:
broken = thp[well] < target;
break;
case RESERVOIR_RATE: // Intentional fall-through
case SURFACE_RATE:
// Note that the rates compared below are negative,
// so breaking the constraints means: too high flow rate
// (as for injection).
broken = rateToCompare(well_phase_flow_rate,
well, num_phases, distr) < target;
break;
}
}
break;
default:
OPM_THROW(std::logic_error, "Can only handle INJECTOR and PRODUCER wells.");
}
return broken;
}
// --------- Types ---------
/** /**
* Simple hydrostatic correction for VFP table * Simple hydrostatic correction for VFP table

View File

@ -2,6 +2,7 @@
Copyright 2017 SINTEF Digital, Mathematics and Cybernetics. Copyright 2017 SINTEF Digital, Mathematics and Cybernetics.
Copyright 2017 Statoil ASA. Copyright 2017 Statoil ASA.
Copyright 2017 IRIS Copyright 2017 IRIS
Copyright 2019 Norce
This file is part of the Open Porous Media project (OPM). This file is part of the Open Porous Media project (OPM).
@ -29,6 +30,7 @@
#include <opm/parser/eclipse/EclipseState/Schedule/Well/Well2.hpp> #include <opm/parser/eclipse/EclipseState/Schedule/Well/Well2.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellTestState.hpp> #include <opm/parser/eclipse/EclipseState/Schedule/Well/WellTestState.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Group/GuideRate.hpp>
#include <opm/core/wells.h> #include <opm/core/wells.h>
#include <opm/core/well_controls.h> #include <opm/core/well_controls.h>
@ -39,6 +41,7 @@
#include <opm/simulators/wells/RateConverter.hpp> #include <opm/simulators/wells/RateConverter.hpp>
#include <opm/simulators/wells/VFPProperties.hpp> #include <opm/simulators/wells/VFPProperties.hpp>
#include <opm/simulators/wells/WellHelpers.hpp> #include <opm/simulators/wells/WellHelpers.hpp>
#include <opm/simulators/wells/WellGroupHelpers.hpp>
#include <opm/simulators/wells/WellStateFullyImplicitBlackoil.hpp> #include <opm/simulators/wells/WellStateFullyImplicitBlackoil.hpp>
#include <opm/simulators/flow/BlackoilModelParametersEbos.hpp> #include <opm/simulators/flow/BlackoilModelParametersEbos.hpp>
@ -141,6 +144,8 @@ namespace Opm
void setVFPProperties(const VFPProperties<VFPInjProperties,VFPProdProperties>* vfp_properties_arg); void setVFPProperties(const VFPProperties<VFPInjProperties,VFPProdProperties>* vfp_properties_arg);
void setGuideRate(const GuideRate* guide_rate_arg);
virtual void init(const PhaseUsage* phase_usage_arg, virtual void init(const PhaseUsage* phase_usage_arg,
const std::vector<double>& depth_arg, const std::vector<double>& depth_arg,
const double gravity_arg, const double gravity_arg,
@ -148,7 +153,7 @@ namespace Opm
virtual void initPrimaryVariablesEvaluation() const = 0; virtual void initPrimaryVariablesEvaluation() const = 0;
virtual ConvergenceReport getWellConvergence(const std::vector<double>& B_avg, Opm::DeferredLogger& deferred_logger) const = 0; virtual ConvergenceReport getWellConvergence(const WellState& well_state, const std::vector<double>& B_avg, Opm::DeferredLogger& deferred_logger) const = 0;
virtual void solveEqAndUpdateWellState(WellState& well_state, Opm::DeferredLogger& deferred_logger) = 0; virtual void solveEqAndUpdateWellState(WellState& well_state, Opm::DeferredLogger& deferred_logger) = 0;
@ -232,7 +237,7 @@ namespace Opm
void closeCompletions(WellTestState& wellTestState); void closeCompletions(WellTestState& wellTestState);
const Well2* wellEcl() const; const Well2& wellEcl() const;
// TODO: theoretically, it should be a const function // TODO: theoretically, it should be a const function
// Simulator is not const is because that assembleWellEq is non-const Simulator // Simulator is not const is because that assembleWellEq is non-const Simulator
@ -250,7 +255,7 @@ namespace Opm
bool isOperable() const; bool isOperable() const;
/// Returns true if the well has one or more THP limits/constraints. /// Returns true if the well has one or more THP limits/constraints.
bool wellHasTHPConstraints() const; bool wellHasTHPConstraints(const SummaryState& summaryState) const;
/// Returns true if the well is currently in prediction mode (i.e. not history mode). /// Returns true if the well is currently in prediction mode (i.e. not history mode).
bool underPredictionMode() const; bool underPredictionMode() const;
@ -258,6 +263,20 @@ namespace Opm
// update perforation water throughput based on solved water rate // update perforation water throughput based on solved water rate
virtual void updateWaterThroughput(const double dt, WellState& well_state) const = 0; virtual void updateWaterThroughput(const double dt, WellState& well_state) const = 0;
void stopWell() {
wellIsStopped_ = true;
}
void openWell() {
wellIsStopped_ = false;
}
bool wellIsStopped() const {
return wellIsStopped_;
}
void setWsolvent(const double wsolvent);
protected: protected:
// to indicate a invalid completion // to indicate a invalid completion
@ -284,9 +303,6 @@ namespace Opm
// typically, it should apply to injection wells // typically, it should apply to injection wells
std::vector<double> comp_frac_; std::vector<double> comp_frac_;
// controls for this well
struct WellControls* well_controls_;
// number of the perforations for this well // number of the perforations for this well
int number_of_perforations_; int number_of_perforations_;
@ -343,6 +359,8 @@ namespace Opm
const VFPProperties<VFPInjProperties,VFPProdProperties>* vfp_properties_; const VFPProperties<VFPInjProperties,VFPProdProperties>* vfp_properties_;
const GuideRate* guide_rate_;
double gravity_; double gravity_;
// For the conversion between the surface volume rate and resrevoir voidage rate // For the conversion between the surface volume rate and resrevoir voidage rate
@ -356,6 +374,10 @@ namespace Opm
std::vector<RateVector> connectionRates_; std::vector<RateVector> connectionRates_;
bool wellIsStopped_;
double wsolvent_;
const PhaseUsage& phaseUsage() const; const PhaseUsage& phaseUsage() const;
int flowPhaseToEbosCompIdx( const int phaseIdx ) const; int flowPhaseToEbosCompIdx( const int phaseIdx ) const;
@ -372,14 +394,14 @@ namespace Opm
const WellState& well_state, const WellState& well_state,
Opm::DeferredLogger& deferred_logger) const; Opm::DeferredLogger& deferred_logger) const;
double getTHPConstraint(Opm::DeferredLogger& deferred_logger) const; double getTHPConstraint(const SummaryState& summaryState) const;
int getControlIndex(const WellControlType& type) const; int getControlIndex(const WellControlType& type) const;
// Component fractions for each phase for the well // Component fractions for each phase for the well
const std::vector<double>& compFrac() const; const std::vector<double>& compFrac() const;
double mostStrictBhpFromBhpLimits(Opm::DeferredLogger& deferred_logger) const; double mostStrictBhpFromBhpLimits(const SummaryState& summaryState) const;
struct RatioLimitCheckReport; struct RatioLimitCheckReport;
@ -437,9 +459,9 @@ namespace Opm
WellTestState& well_test_state, WellTestState& well_test_state,
Opm::DeferredLogger& deferred_logger) const; Opm::DeferredLogger& deferred_logger) const;
void solveWellForTesting(const Simulator& ebosSimulator, WellState& well_state, void solveWellForTesting(const Simulator& ebosSimulator, WellState& well_state,
const std::vector<double>& B_avg, const std::vector<double>& B_avg,
Opm::DeferredLogger& deferred_logger); Opm::DeferredLogger& deferred_logger);
bool solveWellEqUntilConverged(const Simulator& ebosSimulator, bool solveWellEqUntilConverged(const Simulator& ebosSimulator,
const std::vector<double>& B_avg, const std::vector<double>& B_avg,
@ -450,18 +472,20 @@ namespace Opm
void initCompletions(); void initCompletions();
WellControls* createWellControlsWithBHPAndTHP(DeferredLogger& deferred_logger) const;
// count the number of times an output log message is created in the productivity // count the number of times an output log message is created in the productivity
// index calculations // index calculations
int well_productivity_index_logger_counter_; int well_productivity_index_logger_counter_;
bool checkConstraints(WellState& well_state, const SummaryState& summaryState);
}; };
// definition of the struct OperabilityStatus // definition of the struct OperabilityStatus
template<typename TypeTag> template<typename TypeTag>
struct struct

View File

@ -73,8 +73,6 @@ namespace Opm
wells->comp_frac + index_begin + number_of_phases_, comp_frac_.begin() ); wells->comp_frac + index_begin + number_of_phases_, comp_frac_.begin() );
} }
well_controls_ = wells->ctrls[index_well];
ref_depth_ = wells->depth_ref[index_well]; ref_depth_ = wells->depth_ref[index_well];
// perforations related // perforations related
@ -109,6 +107,19 @@ namespace Opm
well_productivity_index_logger_counter_ = 0; well_productivity_index_logger_counter_ = 0;
wellIsStopped_ = false;
if (well.getStatus() == Well2::Status::STOP) {
wellIsStopped_ = true;
}
wsolvent_ = 0.0;
if (has_solvent && well.isInjector()) {
auto injectorType = well_ecl_.injectorType();
if (injectorType == Well2::InjectorType::GAS) {
wsolvent_ = well_ecl_.getSolventFraction();
}
}
} }
template<typename TypeTag> template<typename TypeTag>
@ -172,8 +183,13 @@ namespace Opm
vfp_properties_ = vfp_properties_arg; vfp_properties_ = vfp_properties_arg;
} }
template<typename TypeTag>
void
WellInterface<TypeTag>::
setGuideRate(const GuideRate* guide_rate_arg)
{
guide_rate_ = guide_rate_arg;
}
template<typename TypeTag> template<typename TypeTag>
@ -199,14 +215,6 @@ namespace Opm
template<typename TypeTag>
WellControls*
WellInterface<TypeTag>::
wellControls() const
{
return well_controls_;
}
template<typename TypeTag> template<typename TypeTag>
int int
WellInterface<TypeTag>:: WellInterface<TypeTag>::
@ -242,11 +250,11 @@ namespace Opm
template<typename TypeTag> template<typename TypeTag>
const Well2* const Well2&
WellInterface<TypeTag>:: WellInterface<TypeTag>::
wellEcl() const wellEcl() const
{ {
return std::addressof(well_ecl_); return well_ecl_;
} }
@ -307,18 +315,17 @@ namespace Opm
WellInterface<TypeTag>:: WellInterface<TypeTag>::
wsolvent() const wsolvent() const
{ {
if (!has_solvent) { return wsolvent_;
return 0.0; }
}
auto injectorType = well_ecl_.injectorType();
if (injectorType == Well2::InjectorType::GAS) {
double solvent_fraction = well_ecl_.getSolventFraction(); template<typename TypeTag>
return solvent_fraction; void
} else { WellInterface<TypeTag>::
// Not a gas injection well => no solvent. setWsolvent(const double wsolvent)
return 0.0; {
} wsolvent_ = wsolvent;
} }
@ -372,107 +379,69 @@ namespace Opm
template<typename TypeTag>
double
WellInterface<TypeTag>::
mostStrictBhpFromBhpLimits(Opm::DeferredLogger& deferred_logger) const
{
double bhp;
// initial bhp value, making the value not usable
switch( well_type_ ) {
case INJECTOR:
bhp = std::numeric_limits<double>::max();
break;
case PRODUCER:
bhp = -std::numeric_limits<double>::max();
break;
default:
OPM_DEFLOG_THROW(std::logic_error, "Expected PRODUCER or INJECTOR type for well " << name(), deferred_logger);
}
// The number of the well controls/constraints
const int nwc = well_controls_get_num(well_controls_);
for (int ctrl_index = 0; ctrl_index < nwc; ++ctrl_index) {
// finding a BHP constraint
if (well_controls_iget_type(well_controls_, ctrl_index) == BHP) {
// get the bhp constraint value, it should always be postive assummingly
const double bhp_target = well_controls_iget_target(well_controls_, ctrl_index);
switch(well_type_) {
case INJECTOR: // using the lower bhp contraint from Injectors
if (bhp_target < bhp) {
bhp = bhp_target;
}
break;
case PRODUCER:
if (bhp_target > bhp) {
bhp = bhp_target;
}
break;
default:
OPM_DEFLOG_THROW(std::logic_error, "Expected PRODUCER or INJECTOR type for well " << name(), deferred_logger);
} // end of switch
}
}
return bhp;
}
template<typename TypeTag> template<typename TypeTag>
bool bool
WellInterface<TypeTag>:: WellInterface<TypeTag>::
wellHasTHPConstraints() const wellHasTHPConstraints(const SummaryState& summaryState) const
{ {
return getControlIndex(THP) >= 0; if (well_ecl_.isInjector()) {
const auto controls = well_ecl_.injectionControls(summaryState);
if (controls.hasControl(Well2::InjectorCMode::THP))
return true;
}
if (well_ecl_.isProducer( )) {
const auto controls = well_ecl_.productionControls(summaryState);
if (controls.hasControl(Well2::ProducerCMode::THP))
return true;
}
return false;
} }
template<typename TypeTag> template<typename TypeTag>
double double
WellInterface<TypeTag>:: WellInterface<TypeTag>::
getTHPConstraint(Opm::DeferredLogger& deferred_logger) const mostStrictBhpFromBhpLimits(const SummaryState& summaryState) const
{ {
const int thp_control_index = getControlIndex(THP); if (well_ecl_.isInjector()) {
const auto& controls = well_ecl_.injectionControls(summaryState);
if (thp_control_index < 0) { return controls.bhp_limit;
OPM_DEFLOG_THROW(std::runtime_error, " there is no THP constraint/limit for well " << name()
<< ", while we are requesting it ", deferred_logger);
} }
return well_controls_iget_target(well_controls_, thp_control_index); if (well_ecl_.isProducer( )) {
const auto& controls = well_ecl_.productionControls(summaryState);
return controls.bhp_limit;
}
return 0.0;
} }
template<typename TypeTag> template<typename TypeTag>
int double
WellInterface<TypeTag>:: WellInterface<TypeTag>::
getControlIndex(const WellControlType& type) const getTHPConstraint(const SummaryState& summaryState) const
{ {
const int nwc = well_controls_get_num(well_controls_); if (well_ecl_.isInjector()) {
for (int ctrl_index = 0; ctrl_index < nwc; ++ctrl_index) { const auto& controls = well_ecl_.injectionControls(summaryState);
if (well_controls_iget_type(well_controls_, ctrl_index) == type) { return controls.thp_limit;
return ctrl_index;
}
} }
return -1;
if (well_ecl_.isProducer( )) {
const auto& controls = well_ecl_.productionControls(summaryState);
return controls.thp_limit;
}
return 0.0;
} }
template<typename TypeTag> template<typename TypeTag>
void void
WellInterface<TypeTag>:: WellInterface<TypeTag>::
@ -480,81 +449,39 @@ namespace Opm
WellState& well_state, WellState& well_state,
Opm::DeferredLogger& deferred_logger) /* const */ Opm::DeferredLogger& deferred_logger) /* const */
{ {
if (this->wellIsStopped()) {
return;
}
const auto& summaryState = ebos_simulator.vanguard().summaryState();
const auto& well = well_ecl_;
std::string from;
if (well.isInjector()) {
from = Well2::InjectorCMode2String(well_state.currentInjectionControls()[index_of_well_]);
} else {
from = Well2::ProducerCMode2String(well_state.currentProductionControls()[index_of_well_]);
}
bool changed = checkConstraints(well_state, summaryState);
auto cc = Dune::MPIHelper::getCollectiveCommunication(); auto cc = Dune::MPIHelper::getCollectiveCommunication();
const int np = number_of_phases_;
const int w = index_of_well_;
const int old_control_index = well_state.currentControls()[w];
// Find, for each well, if any constraints are broken. If so,
// switch control to first broken constraint.
WellControls* wc = well_controls_;
// Loop over all controls except the current one, and also
// skip any RESERVOIR_RATE controls, since we cannot
// handle those.
const int nwc = well_controls_get_num(wc);
// the current control index
int current = well_state.currentControls()[w];
int ctrl_index = 0;
for (; ctrl_index < nwc; ++ctrl_index) {
if (ctrl_index == current) {
// This is the currently used control, so it is
// used as an equation. So this is not used as an
// inequality constraint, and therefore skipped.
continue;
}
if (wellhelpers::constraintBroken(
well_state.bhp(), well_state.thp(), well_state.wellRates(),
w, np, well_type_, wc, ctrl_index)) {
// if the well can not work under THP / BHP control, we should not switch to THP / BHP control
const bool cannot_switch_to_bhp = well_controls_iget_type(wc, ctrl_index) == BHP && !operability_status_.isOperableUnderBHPLimit();
const bool cannot_switch_to_thp = well_controls_iget_type(wc, ctrl_index) == THP && !operability_status_.isOperableUnderTHPLimit();
const bool cannot_switch = cannot_switch_to_bhp || cannot_switch_to_thp;
if ( !cannot_switch ) {
// ctrl_index will be the index of the broken constraint after the loop.
break;
} else {
// before we figure out to handle it, we give some debug information here
if ( well_controls_iget_type(wc, ctrl_index) == BHP && !operability_status_.isOperableUnderBHPLimit() ) {
deferred_logger.debug("well " + name() + " breaks the BHP limit, while it is not operable under BHP limit");
}
if ( well_controls_iget_type(wc, ctrl_index) == THP && !operability_status_.isOperableUnderTHPLimit() ) {
deferred_logger.debug("well " + name() + " breaks the THP limit, while it is not operable under THP limit");
}
}
}
}
if (ctrl_index != nwc) {
// Constraint number ctrl_index was broken, switch to it.
well_state.currentControls()[w] = ctrl_index;
current = well_state.currentControls()[w];
well_controls_set_current( wc, current);
}
// the new well control indices after all the related updates,
const int updated_control_index = well_state.currentControls()[w];
// checking whether control changed // checking whether control changed
if (updated_control_index != old_control_index) { if (changed) {
std::string to;
auto from = well_controls_iget_type(wc, old_control_index); if (well.isInjector()) {
auto to = well_controls_iget_type(wc, updated_control_index); to = Well2::InjectorCMode2String(well_state.currentInjectionControls()[index_of_well_]);
} else {
to = Well2::ProducerCMode2String(well_state.currentProductionControls()[index_of_well_]);
}
std::ostringstream ss; std::ostringstream ss;
ss << " Switching control mode for well " << name() ss << " Switching control mode for well " << name()
<< " from " << modestring[from] << " from " << from
<< " to " << modestring[to]; << " to " << to;
if (cc.size() > 1) { if (cc.size() > 1) {
ss << " on rank " << cc.rank(); ss << " on rank " << cc.rank();
} }
deferred_logger.info(ss.str()); deferred_logger.info(ss.str());
}
if (updated_control_index != old_control_index) { // || well_collection_->groupControlActive()) {
updateWellStateWithTarget(ebos_simulator, well_state, deferred_logger); updateWellStateWithTarget(ebos_simulator, well_state, deferred_logger);
updatePrimaryVariables(well_state, deferred_logger); updatePrimaryVariables(well_state, deferred_logger);
} }
@ -886,13 +813,18 @@ namespace Opm
WellTestState& well_test_state, WellTestState& well_test_state,
Opm::DeferredLogger& deferred_logger) const Opm::DeferredLogger& deferred_logger) const
{ {
if (!isOperable()) { if (!isOperable() || wellIsStopped_) {
well_test_state.closeWell(name(), WellTestConfig::Reason::PHYSICAL, simulation_time); if (well_test_state.hasWellClosed(name(), WellTestConfig::Reason::ECONOMIC) ||
if (write_message_to_opmlog) { well_test_state.hasWellClosed(name(), WellTestConfig::Reason::PHYSICAL) ) {
// TODO: considering auto shut in? // Already closed, do nothing.
const std::string msg = "well " + name() } else {
+ std::string(" will be shut as it can not operate under current reservoir condition"); well_test_state.closeWell(name(), WellTestConfig::Reason::PHYSICAL, simulation_time);
deferred_logger.info(msg); if (write_message_to_opmlog) {
const std::string action = well_ecl_.getAutomaticShutIn() ? "shut" : "stopped";
const std::string msg = "Well " + name()
+ " will be " + action + " as it can not operate under current reservoir conditions.";
deferred_logger.info(msg);
}
} }
} }
@ -911,6 +843,9 @@ namespace Opm
WellTestState& well_test_state, WellTestState& well_test_state,
Opm::DeferredLogger& deferred_logger) const Opm::DeferredLogger& deferred_logger) const
{ {
if (wellIsStopped_)
return;
const WellEconProductionLimits& econ_production_limits = well_ecl_.getEconLimits(); const WellEconProductionLimits& econ_production_limits = well_ecl_.getEconLimits();
// if no limit is effective here, then continue to the next well // if no limit is effective here, then continue to the next well
@ -1005,7 +940,7 @@ namespace Opm
if (write_message_to_opmlog) { if (write_message_to_opmlog) {
if (well_ecl_.getAutomaticShutIn()) { if (well_ecl_.getAutomaticShutIn()) {
const std::string msg = name() + std::string(" will be shut due to last completion closed"); const std::string msg = name() + std::string(" will be shut due to last completion closed");
deferred_logger.info(msg); deferred_logger.info(msg);
} else { } else {
const std::string msg = name() + std::string(" will be stopped due to last completion closed"); const std::string msg = name() + std::string(" will be stopped due to last completion closed");
deferred_logger.info(msg); deferred_logger.info(msg);
@ -1204,19 +1139,6 @@ namespace Opm
double double
WellInterface<TypeTag>::scalingFactor(const int phaseIdx) const WellInterface<TypeTag>::scalingFactor(const int phaseIdx) const
{ {
const WellControls* wc = well_controls_;
if (well_controls_get_current(wc) != -1 && well_controls_get_current_type(wc) == RESERVOIR_RATE) {
if (has_solvent && phaseIdx == contiSolventEqIdx ) {
typedef Opm::BlackOilSolventModule<TypeTag> SolventModule;
double coeff = 0;
rateConverter_.template calcCoeffSolvent<SolventModule>(0, pvtRegionIdx_, coeff);
return coeff;
}
// TODO: use the rateConverter here as well.
const double* distr = well_controls_get_current_distr(wc);
return distr[phaseIdx];
}
const auto& pu = phaseUsage(); const auto& pu = phaseUsage();
if (FluidSystem::phaseIsActive(FluidSystem::waterPhaseIdx) && pu.phase_pos[Water] == phaseIdx) if (FluidSystem::phaseIsActive(FluidSystem::waterPhaseIdx) && pu.phase_pos[Water] == phaseIdx)
return 1.0; return 1.0;
@ -1292,7 +1214,7 @@ namespace Opm
do { do {
assembleWellEq(ebosSimulator, B_avg, dt, well_state, deferred_logger); assembleWellEq(ebosSimulator, B_avg, dt, well_state, deferred_logger);
auto report = getWellConvergence(B_avg, deferred_logger); auto report = getWellConvergence(well_state, B_avg, deferred_logger);
converged = report.converged(); converged = report.converged();
if (converged) { if (converged) {
@ -1302,7 +1224,8 @@ namespace Opm
++it; ++it;
solveEqAndUpdateWellState(well_state, deferred_logger); solveEqAndUpdateWellState(well_state, deferred_logger);
updateWellControl(ebosSimulator, well_state, deferred_logger); // We don't allow for switching well controls while computing well potentials and testing wells
// updateWellControl(ebosSimulator, well_state, deferred_logger);
initPrimaryVariablesEvaluation(); initPrimaryVariablesEvaluation();
} while (it < max_iter); } while (it < max_iter);
@ -1441,35 +1364,200 @@ namespace Opm
return operability_status_.isOperable(); return operability_status_.isOperable();
} }
template<typename TypeTag> template<typename TypeTag>
WellControls* bool
WellInterface<TypeTag>:: WellInterface<TypeTag>::
createWellControlsWithBHPAndTHP(DeferredLogger& deferred_logger) const checkConstraints(WellState& well_state, const SummaryState& summaryState) {
{
WellControls* wc = well_controls_create();
well_controls_assert_number_of_phases(wc, number_of_phases_);
// a well always has a bhp limit const auto& well = well_ecl_;
const double invalid_alq = -1e100; const PhaseUsage& pu = phaseUsage();
const double invalid_vfp = -2147483647; const int well_index = index_of_well_;
const double bhp_limit = this->mostStrictBhpFromBhpLimits(deferred_logger); const auto wellrate_index = well_index * pu.num_phases;
well_controls_add_new(BHP, bhp_limit, invalid_alq, invalid_vfp, NULL, wc); // bool changed = false;
// // Stopped wells can not change control
// if (currentControl == "STOP")
// return newControl;
if (well.isInjector()) {
const auto controls = well.injectionControls(summaryState);
Opm::Well2::InjectorCMode& currentControl = well_state.currentInjectionControls()[well_index];
if (controls.hasControl(Well2::InjectorCMode::BHP) && currentControl != Well2::InjectorCMode::BHP)
{
const auto& bhp = controls.bhp_limit;
double current_bhp = well_state.bhp()[well_index];
if (bhp < current_bhp) {
currentControl = Well2::InjectorCMode::BHP;
return true;
}
}
if (controls.hasControl(Well2::InjectorCMode::THP) && currentControl != Well2::InjectorCMode::THP)
{
const auto& thp = controls.thp_limit;
double current_thp = well_state.thp()[well_index];
if (thp < current_thp) {
currentControl = Well2::InjectorCMode::THP;
return true;
}
}
if (controls.hasControl(Well2::InjectorCMode::RATE) && currentControl != Well2::InjectorCMode::RATE)
{
Well2::InjectorType injectorType = controls.injector_type;
double current_rate = 0.0;
switch (injectorType) {
case Well2::InjectorType::WATER:
{
current_rate = well_state.wellRates()[ wellrate_index + pu.phase_pos[BlackoilPhases::Aqua] ];
break;
}
case Well2::InjectorType::OIL:
{
current_rate = well_state.wellRates()[ wellrate_index + pu.phase_pos[BlackoilPhases::Liquid] ];
break;
}
case Well2::InjectorType::GAS:
{
current_rate = well_state.wellRates()[ wellrate_index + pu.phase_pos[BlackoilPhases::Vapour] ];
break;
}
default:
throw("Expected WATER, OIL or GAS as type for injectors " + well.name());
}
if (controls.surface_rate < current_rate) {
currentControl = Well2::InjectorCMode::RATE;
return true;
}
}
if (controls.hasControl(Well2::InjectorCMode::RESV) && currentControl != Well2::InjectorCMode::RESV)
{
double current_rate = 0.0;
if( pu.phase_used[BlackoilPhases::Aqua] )
current_rate += well_state.wellReservoirRates()[ wellrate_index + pu.phase_pos[BlackoilPhases::Aqua] ];
if( pu.phase_used[BlackoilPhases::Liquid] )
current_rate += well_state.wellReservoirRates()[ wellrate_index + pu.phase_pos[BlackoilPhases::Liquid] ];
if( pu.phase_used[BlackoilPhases::Vapour] )
current_rate += well_state.wellReservoirRates()[ wellrate_index + pu.phase_pos[BlackoilPhases::Vapour] ];
if (controls.reservoir_rate < current_rate) {
currentControl = Well2::InjectorCMode::RESV;
return true;
}
}
if (this->wellHasTHPConstraints()) {
// it might be better to do it through EclipseState?
const double thp_limit = this->getTHPConstraint(deferred_logger);
const double thp_control_index = this->getControlIndex(THP);
const int vfp_number = well_controls_iget_vfp(well_controls_, thp_control_index);
const double alq = well_controls_iget_alq(well_controls_, thp_control_index);
well_controls_add_new(THP, thp_limit, alq, vfp_number, NULL, wc);
} }
return wc; if (well.isProducer( )) {
const auto controls = well.productionControls(summaryState);
Well2::ProducerCMode& currentControl = well_state.currentProductionControls()[well_index];
if (controls.hasControl(Well2::ProducerCMode::BHP) && currentControl != Well2::ProducerCMode::BHP )
{
const double bhp = controls.bhp_limit;
double current_bhp = well_state.bhp()[well_index];
if (bhp > current_bhp) {
currentControl = Well2::ProducerCMode::BHP;
return true;
}
}
if (controls.hasControl(Well2::ProducerCMode::THP) && currentControl != Well2::ProducerCMode::THP)
{
const auto& thp = controls.thp_limit;
double current_thp = well_state.thp()[well_index];
if (thp > current_thp) {
currentControl = Well2::ProducerCMode::THP;
return true;
}
}
if (controls.hasControl(Well2::ProducerCMode::ORAT) && currentControl != Well2::ProducerCMode::ORAT) {
double current_rate = -well_state.wellRates()[ wellrate_index + pu.phase_pos[BlackoilPhases::Liquid] ];
if (controls.oil_rate < current_rate ) {
currentControl = Well2::ProducerCMode::ORAT;
return true;
}
}
if (controls.hasControl(Well2::ProducerCMode::WRAT) && currentControl != Well2::ProducerCMode::WRAT ) {
double current_rate = -well_state.wellRates()[ wellrate_index + pu.phase_pos[BlackoilPhases::Aqua] ];
if (controls.water_rate < current_rate ) {
currentControl = Well2::ProducerCMode::WRAT;
return true;
}
}
if (controls.hasControl(Well2::ProducerCMode::GRAT) && currentControl != Well2::ProducerCMode::GRAT ) {
double current_rate = -well_state.wellRates()[ wellrate_index + pu.phase_pos[BlackoilPhases::Vapour] ];
if (controls.gas_rate < current_rate ) {
currentControl = Well2::ProducerCMode::GRAT;
return true;
}
}
if (controls.hasControl(Well2::ProducerCMode::LRAT) && currentControl != Well2::ProducerCMode::LRAT) {
double current_rate = -well_state.wellRates()[ wellrate_index + pu.phase_pos[BlackoilPhases::Liquid] ];
current_rate -= well_state.wellRates()[ wellrate_index + pu.phase_pos[BlackoilPhases::Aqua] ];
if (controls.liquid_rate < current_rate ) {
currentControl = Well2::ProducerCMode::LRAT;
return true;
}
}
if (controls.hasControl(Well2::ProducerCMode::RESV) && currentControl != Well2::ProducerCMode::RESV ) {
double current_rate = 0.0;
if( pu.phase_used[BlackoilPhases::Aqua] )
current_rate -= well_state.wellReservoirRates()[ wellrate_index + pu.phase_pos[BlackoilPhases::Aqua] ];
if( pu.phase_used[BlackoilPhases::Liquid] )
current_rate -= well_state.wellReservoirRates()[ wellrate_index + pu.phase_pos[BlackoilPhases::Liquid] ];
if( pu.phase_used[BlackoilPhases::Vapour] )
current_rate -= well_state.wellReservoirRates()[ wellrate_index + pu.phase_pos[BlackoilPhases::Vapour] ];
if (controls.prediction_mode && controls.resv_rate > current_rate) {
currentControl = Well2::ProducerCMode::RESV;
return true;
}
if (!controls.prediction_mode) {
const int fipreg = 0; // not considering the region for now
const int np = number_of_phases_;
std::vector<double> 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<double> 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) {
currentControl = Well2::ProducerCMode::RESV;
return true;
}
}
}
}
return false;
} }

View File

@ -38,6 +38,7 @@
#include <map> #include <map>
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include <iostream>
namespace Opm namespace Opm
{ {
@ -157,11 +158,12 @@ namespace Opm
} }
} }
current_controls_.resize(nw); current_injection_controls_.resize(nw);
// The controls set in the Wells (specified in the DECK) are treated as default initial value current_production_controls_.resize(nw);
for (int w = 0; w < nw; ++w) { current_injection_group_controls_.clear();
current_controls_[w] = well_controls_get_current(wells->ctrls[w]); current_production_group_controls_.clear();
}
perfRateSolvent_.clear(); perfRateSolvent_.clear();
perfRateSolvent_.resize(nperf, 0.0); perfRateSolvent_.resize(nperf, 0.0);
productivity_index_.resize(nw * np, 0.0); productivity_index_.resize(nw * np, 0.0);
@ -186,12 +188,16 @@ namespace Opm
// thp // thp
thp()[ newIndex ] = prevState->thp()[ oldIndex ]; thp()[ newIndex ] = prevState->thp()[ oldIndex ];
// if there is no effective control event happens to the well, we use the current_controls_ from prevState // Currently this is taken care of by updateWellStateFromTarge. Maybe we should just remove the initialization and just use updateWellStateFromTarget
//if (effective_events_occurred_[w]) {
// continue;
//}
// if there is no effective control event happens to the well, we use the current_injection/production_controls_ from prevState
// otherwise, we use the control specified in the deck // otherwise, we use the control specified in the deck
if (!effective_events_occurred_[w]) { if (!effective_events_occurred_[w]) {
current_controls_[ newIndex ] = prevState->currentControls()[ oldIndex ]; current_injection_controls_[ newIndex ] = prevState->currentInjectionControls()[ oldIndex ];
// also change the one in the WellControls current_production_controls_[ newIndex ] = prevState->currentProductionControls()[ oldIndex ];
well_controls_set_current(wells->ctrls[w], current_controls_[ newIndex ]);
} }
// wellrates // wellrates
@ -307,196 +313,44 @@ namespace Opm
} }
} }
/// Allocate and initialize if wells is non-null. Also tries
/// to give useful initial values to the bhp(), wellRates()
/// and perfPhaseRates() fields, depending on controls.
///
/// this method is only for flow_legacy!
template <class PrevWellState>
void initLegacy(const Wells* wells, const std::vector<double>& cellPressures , const PrevWellState& prevState, const PhaseUsage& pu)
{
// call init on base class
BaseType :: init(wells, cellPressures);
// if there are no well, do nothing in init
if (wells == 0) {
return;
}
const int nw = wells->number_of_wells;
if( nw == 0 ) return ;
// Initialize perfphaserates_, which must be done here.
const int np = wells->number_of_phases;
const int nperf = wells->well_connpos[nw];
well_reservoir_rates_.resize(nw * np, 0.0);
well_dissolved_gas_rates_.resize(nw, 0.0);
well_vaporized_oil_rates_.resize(nw, 0.0);
productivity_index_.resize(nw * np, 0.0);
well_potentials_.resize(nw*np, 0.0);
// Ensure that we start out with zero rates by default.
perfphaserates_.clear();
perfphaserates_.resize(nperf * np, 0.0);
for (int w = 0; w < nw; ++w) {
assert((wells->type[w] == INJECTOR) || (wells->type[w] == PRODUCER));
const WellControls* ctrl = wells->ctrls[w];
if (well_controls_well_is_stopped(ctrl)) {
// Shut well: perfphaserates_ are all zero.
} else {
const int num_perf_this_well = wells->well_connpos[w + 1] - wells->well_connpos[w];
// Open well: Initialize perfphaserates_ to well
// rates divided by the number of perforations.
for (int perf = wells->well_connpos[w]; perf < wells->well_connpos[w + 1]; ++perf) {
for (int p = 0; p < np; ++p) {
perfphaserates_[np*perf + p] = wellRates()[np*w + p] / double(num_perf_this_well);
}
perfPress()[perf] = cellPressures[wells->well_cells[perf]];
}
}
}
// Initialize current_controls_.
// The controls set in the Wells object are treated as defaults,
// and also used for initial values.
current_controls_.resize(nw);
for (int w = 0; w < nw; ++w) {
current_controls_[w] = well_controls_get_current(wells->ctrls[w]);
}
perfRateSolvent_.clear();
perfRateSolvent_.resize(nperf, 0.0);
// intialize wells that have been there before
// order may change so the mapping is based on the well name
if( ! prevState.wellMap().empty() )
{
typedef typename WellMapType :: const_iterator const_iterator;
const_iterator end = prevState.wellMap().end();
for (int w = 0; w < nw; ++w) {
std::string name( wells->name[ w ] );
const_iterator it = prevState.wellMap().find( name );
if( it != end )
{
const int oldIndex = (*it).second[ 0 ];
const int newIndex = w;
// bhp
bhp()[ newIndex ] = prevState.bhp()[ oldIndex ];
// thp
thp()[ newIndex ] = prevState.thp()[ oldIndex ];
// wellrates
for( int i=0, idx=newIndex*np, oldidx=oldIndex*np; i<np; ++i, ++idx, ++oldidx )
{
wellRates()[ idx ] = prevState.wellRates()[ oldidx ];
}
// perfPhaseRates
const int oldPerf_idx_beg = (*it).second[ 1 ];
const int num_perf_old_well = (*it).second[ 2 ];
const int num_perf_this_well = wells->well_connpos[newIndex + 1] - wells->well_connpos[newIndex];
// copy perforation rates when the number of perforations is equal,
// otherwise initialize perfphaserates to well rates divided by the number of perforations.
if( num_perf_old_well == num_perf_this_well )
{
int old_perf_phase_idx = oldPerf_idx_beg *np;
for (int perf_phase_idx = wells->well_connpos[ newIndex ]*np;
perf_phase_idx < wells->well_connpos[ newIndex + 1]*np; ++perf_phase_idx, ++old_perf_phase_idx )
{
perfPhaseRates()[ perf_phase_idx ] = prevState.perfPhaseRates()[ old_perf_phase_idx ];
}
} else {
for (int perf = wells->well_connpos[newIndex]; perf < wells->well_connpos[newIndex + 1]; ++perf) {
for (int p = 0; p < np; ++p) {
perfPhaseRates()[np*perf + p] = wellRates()[np*newIndex + p] / double(num_perf_this_well);
}
}
}
// perfPressures
if( num_perf_old_well == num_perf_this_well )
{
int oldPerf_idx = oldPerf_idx_beg;
for (int perf = wells->well_connpos[ newIndex ];
perf < wells->well_connpos[ newIndex + 1]; ++perf, ++oldPerf_idx )
{
perfPress()[ perf ] = prevState.perfPress()[ oldPerf_idx ];
}
}
// perfSolventRates
if (pu.has_solvent) {
if( num_perf_old_well == num_perf_this_well )
{
int oldPerf_idx = oldPerf_idx_beg;
for (int perf = wells->well_connpos[ newIndex ];
perf < wells->well_connpos[ newIndex + 1]; ++perf, ++oldPerf_idx )
{
perfRateSolvent()[ perf ] = prevState.perfRateSolvent()[ oldPerf_idx ];
}
}
}
}
// If in the new step, there is no THP related target/limit anymore, its thp value should be
// set to zero.
const WellControls* ctrl = wells->ctrls[w];
const int nwc = well_controls_get_num(ctrl);
int ctrl_index = 0;
for (; ctrl_index < nwc; ++ctrl_index) {
if (well_controls_iget_type(ctrl, ctrl_index) == THP) {
break;
}
}
// not finding any thp related control/limits
if (ctrl_index == nwc) {
thp()[w] = 0.;
}
}
}
{
// we need to create a trival segment related values to avoid there will be some
// multi-segment wells added later.
nseg_ = nw;
top_segment_index_.resize(nw);
seg_number_.resize(nw);
for (int w = 0; w < nw; ++w) {
top_segment_index_[w] = w;
seg_number_[w] = 1; // Top segment is segment #1
}
segpress_ = bhp();
segrates_ = wellRates();
}
}
// this method is only for flow_legacy!
template <class State, class PrevWellState>
void initLegacy(const Wells* wells, const State& state, const PrevWellState& prevState, const PhaseUsage& pu)
{
initLegacy(wells, state.pressure(), prevState, pu);
}
// this method is only for flow_legacy!
template <class State>
void resizeLegacy(const Wells* wells, const State& state, const PhaseUsage& pu)
{
const WellStateFullyImplicitBlackoil dummy_state{}; // Init with an empty previous state only resizes
initLegacy(wells, state, dummy_state, pu) ;
}
/// One rate per phase and well connection. /// One rate per phase and well connection.
std::vector<double>& perfPhaseRates() { return perfphaserates_; } std::vector<double>& perfPhaseRates() { return perfphaserates_; }
const std::vector<double>& perfPhaseRates() const { return perfphaserates_; } const std::vector<double>& perfPhaseRates() const { return perfphaserates_; }
/// One current control per well. /// One current control per injecting well.
std::vector<int>& currentControls() { return current_controls_; } std::vector<Opm::Well2::InjectorCMode>& currentInjectionControls() { return current_injection_controls_; }
const std::vector<int>& currentControls() const { return current_controls_; } const std::vector<Opm::Well2::InjectorCMode>& currentInjectionControls() const { return current_injection_controls_; }
/// One current control per producing well.
std::vector<Well2::ProducerCMode>& currentProductionControls() { return current_production_controls_; }
const std::vector<Well2::ProducerCMode>& currentProductionControls() const { return current_production_controls_; }
/// One current control per group.
void setCurrentProductionGroupControl(const std::string& groupName, const Group2::ProductionCMode& groupControl ) {
current_production_group_controls_[groupName] = groupControl;
}
const Group2::ProductionCMode& currentProductionGroupControl(const std::string& groupName) const {
auto it = current_production_group_controls_.find(groupName);
if (it == current_production_group_controls_.end())
OPM_THROW(std::logic_error, "Could not find any well control for " << groupName);
return it->second;
}
/// One current control per group.
void setCurrentInjectionGroupControl(const std::string& groupName, const Group2::InjectionCMode& groupControl ) {
current_injection_group_controls_[groupName] = groupControl;
}
const Group2::InjectionCMode& currentInjectionGroupControl(const std::string& groupName) const {
auto it = current_injection_group_controls_.find(groupName);
if (it == current_injection_group_controls_.end())
OPM_THROW(std::logic_error, "Could not find any well control for " << groupName);
return it->second;
}
data::Wells report(const PhaseUsage &pu, const int* globalCellIdxMap) const override data::Wells report(const PhaseUsage &pu, const int* globalCellIdxMap) const override
{ {
@ -538,8 +392,8 @@ namespace Opm
for( const auto& wt : this->wellMap() ) { for( const auto& wt : this->wellMap() ) {
const auto w = wt.second[ 0 ]; const auto w = wt.second[ 0 ];
auto& well = res.at( wt.first ); auto& well = res.at( wt.first );
well.control = this->currentControls()[ w ]; //well.injectionControl = static_cast<int>(this->currentInjectionControls()[ w ]);
//well.productionControl = static_cast<int>(this->currentProductionControls()[ w ]);
const int well_rate_index = w * pu.num_phases; const int well_rate_index = w * pu.num_phases;
if ( pu.phase_used[Water] ) { if ( pu.phase_used[Water] ) {
@ -835,6 +689,11 @@ namespace Opm
return well_reservoir_rates_; return well_reservoir_rates_;
} }
const std::vector<double>& wellReservoirRates() const
{
return well_reservoir_rates_;
}
std::vector<double>& wellDissolvedGasRates() std::vector<double>& wellDissolvedGasRates()
{ {
return well_dissolved_gas_rates_; return well_dissolved_gas_rates_;
@ -919,7 +778,12 @@ namespace Opm
private: private:
std::vector<double> perfphaserates_; std::vector<double> perfphaserates_;
std::vector<int> current_controls_; std::vector<Opm::Well2::InjectorCMode> current_injection_controls_;
std::vector<Well2::ProducerCMode> current_production_controls_;
std::map<std::string, Group2::ProductionCMode> current_production_group_controls_;
std::map<std::string, Group2::InjectionCMode> current_injection_group_controls_;
std::vector<double> perfRateSolvent_; std::vector<double> perfRateSolvent_;
// it is the throughput of water flow through the perforations // it is the throughput of water flow through the perforations

View File

@ -223,6 +223,9 @@ void test_readWriteWells()
w1.bhp = 1.23; w1.bhp = 1.23;
w1.temperature = 3.45; w1.temperature = 3.45;
w1.control = 1; w1.control = 1;
//w1.injectionControl = 1;
//w1.productionControl = 1;
/* /*
* the connection keys (active indices) and well names correspond to the * the connection keys (active indices) and well names correspond to the
@ -234,7 +237,9 @@ void test_readWriteWells()
w2.rates = r2; w2.rates = r2;
w2.bhp = 2.34; w2.bhp = 2.34;
w2.temperature = 4.56; w2.temperature = 4.56;
w2.control = 2; w2.control = 1;
//w1.injectionControl = 2;
//w1.productionControl = 2;
w2.connections.push_back( { 188, rc3, 36.22, 19.28, 0.0, 0.0, 0.0, 0.0 } ); w2.connections.push_back( { 188, rc3, 36.22, 19.28, 0.0, 0.0, 0.0, 0.0 } );
Opm::data::Wells wellRates; Opm::data::Wells wellRates;

View File

@ -318,199 +318,199 @@ BOOST_AUTO_TEST_CASE(InterpolateOne)
} }
/** ///**
* Test that we can generate some dummy data representing an ND plane, // * Test that we can generate some dummy data representing an ND plane,
* interpolate using doubles as input, and compare against the analytic solution // * interpolate using doubles as input, and compare against the analytic solution
*/ // */
BOOST_AUTO_TEST_CASE(InterpolatePlane) //BOOST_AUTO_TEST_CASE(InterpolatePlane)
{ //{
const int n=5; // const int n=5;
fillDataPlane(); // fillDataPlane();
initProperties(); // initProperties();
//Temps used to store reference and actual variables // //Temps used to store reference and actual variables
double sad = 0.0; // double sad = 0.0;
double max_d = 0.0; // double max_d = 0.0;
//Check interpolation // //Check interpolation
for (int i=0; i<=n; ++i) { // for (int i=0; i<=n; ++i) {
const double thp = i / static_cast<double>(n); // const double thp = i / static_cast<double>(n);
for (int j=1; j<=n; ++j) { // for (int j=1; j<=n; ++j) {
const double aqua = -j / static_cast<double>(n); // const double aqua = -j / static_cast<double>(n);
for (int k=1; k<=n; ++k) { // for (int k=1; k<=n; ++k) {
const double vapour = -k / static_cast<double>(n); // const double vapour = -k / static_cast<double>(n);
for (int l=0; l<=n; ++l) { // for (int l=0; l<=n; ++l) {
const double alq = l / static_cast<double>(n); // const double alq = l / static_cast<double>(n);
for (int m=1; m<=n; ++m) { // for (int m=1; m<=n; ++m) {
const double liquid = -m / static_cast<double>(n); // const double liquid = -m / static_cast<double>(n);
//Find values that should be in table // //Find values that should be in table
double flo = Opm::detail::getFlo(aqua, liquid, vapour, table->getFloType()); // double flo = Opm::detail::getFlo(aqua, liquid, vapour, table->getFloType());
double wfr = Opm::detail::getWFR(aqua, liquid, vapour, table->getWFRType()); // double wfr = Opm::detail::getWFR(aqua, liquid, vapour, table->getWFRType());
double gfr = Opm::detail::getGFR(aqua, liquid, vapour, table->getGFRType()); // double gfr = Opm::detail::getGFR(aqua, liquid, vapour, table->getGFRType());
//Calculate reference // //Calculate reference
double reference = thp + 2*wfr + 3*gfr+ 4*alq - 5*flo; // double reference = thp + 2*wfr + 3*gfr+ 4*alq - 5*flo;
//Calculate actual // //Calculate actual
//Note order of arguments: id, aqua, liquid, vapour, thp, alq // //Note order of arguments: id, aqua, liquid, vapour, thp, alq
double actual = properties->bhp(1, aqua, liquid, vapour, thp, alq); // double actual = properties->bhp(1, aqua, liquid, vapour, thp, alq);
double abs_diff = std::abs(actual - reference); // double abs_diff = std::abs(actual - reference);
max_d = std::max(max_d, abs_diff); // max_d = std::max(max_d, abs_diff);
sad = sad + abs_diff; // sad = sad + abs_diff;
} // }
} // }
} // }
} // }
} // }
BOOST_CHECK_SMALL(max_d, max_d_tol); // BOOST_CHECK_SMALL(max_d, max_d_tol);
BOOST_CHECK_SMALL(sad, sad_tol); // BOOST_CHECK_SMALL(sad, sad_tol);
} //}
/** ///**
* Test that we can generate some dummy data representing an ND plane, // * Test that we can generate some dummy data representing an ND plane,
* interpolate using doubles as input, and compare against the analytic solution // * interpolate using doubles as input, and compare against the analytic solution
*/ // */
BOOST_AUTO_TEST_CASE(ExtrapolatePlane) //BOOST_AUTO_TEST_CASE(ExtrapolatePlane)
{ //{
fillDataPlane(); // fillDataPlane();
initProperties(); // initProperties();
//Check linear extrapolation (i.e., using values of x, y, etc. outside our interpolant domain) // //Check linear extrapolation (i.e., using values of x, y, etc. outside our interpolant domain)
double sum = 0.0; // double sum = 0.0;
double reference_sum = 0.0; // double reference_sum = 0.0;
double sad = 0.0; // Sum absolute difference // double sad = 0.0; // Sum absolute difference
double max_d = 0.0; // Maximum difference // double max_d = 0.0; // Maximum difference
int n=1; // int n=1;
int o=5; // int o=5;
for (int i=0; i<=n+o; ++i) { // for (int i=0; i<=n+o; ++i) {
const double x = i / static_cast<double>(n); // const double x = i / static_cast<double>(n);
for (int j=1; j<=n+o; ++j) { // for (int j=1; j<=n+o; ++j) {
const double aqua = -j / static_cast<double>(n); // const double aqua = -j / static_cast<double>(n);
for (int k=1; k<=n+o; ++k) { // for (int k=1; k<=n+o; ++k) {
const double vapour = -k / static_cast<double>(n); // const double vapour = -k / static_cast<double>(n);
for (int l=0; l<=n+o; ++l) { // for (int l=0; l<=n+o; ++l) {
const double u = l / static_cast<double>(n); // const double u = l / static_cast<double>(n);
for (int m=1; m<=n+o; ++m) { // for (int m=1; m<=n+o; ++m) {
const double liquid = -m / static_cast<double>(n); // const double liquid = -m / static_cast<double>(n);
//Find values that should be in table // //Find values that should be in table
double v = Opm::detail::getFlo(aqua, liquid, vapour, table->getFloType()); // double v = Opm::detail::getFlo(aqua, liquid, vapour, table->getFloType());
double y = Opm::detail::getWFR(aqua, liquid, vapour, table->getWFRType()); // double y = Opm::detail::getWFR(aqua, liquid, vapour, table->getWFRType());
double z = Opm::detail::getGFR(aqua, liquid, vapour, table->getGFRType()); // double z = Opm::detail::getGFR(aqua, liquid, vapour, table->getGFRType());
double reference = x + 2*y + 3*z+ 4*u - 5*v; // double reference = x + 2*y + 3*z+ 4*u - 5*v;
reference_sum += reference; // reference_sum += reference;
//Note order of arguments! id, aqua, liquid, vapour, thp , alq // //Note order of arguments! id, aqua, liquid, vapour, thp , alq
double value = properties->bhp(1, aqua, liquid, vapour, x, u); // double value = properties->bhp(1, aqua, liquid, vapour, x, u);
sum += value; // sum += value;
double abs_diff = std::abs(value - reference); // double abs_diff = std::abs(value - reference);
sad += std::abs(abs_diff); // sad += std::abs(abs_diff);
max_d = std::max(max_d, abs_diff); // max_d = std::max(max_d, abs_diff);
} // }
} // }
} // }
} // }
} // }
BOOST_CHECK_CLOSE(sum, reference_sum, 0.0001); // BOOST_CHECK_CLOSE(sum, reference_sum, 0.0001);
BOOST_CHECK_SMALL(max_d, max_d_tol); // BOOST_CHECK_SMALL(max_d, max_d_tol);
BOOST_CHECK_SMALL(sad, sad_tol); // BOOST_CHECK_SMALL(sad, sad_tol);
} //}
/** ///**
* Test that the partial derivatives are reasonable // * Test that the partial derivatives are reasonable
*/ // */
BOOST_AUTO_TEST_CASE(PartialDerivatives) //BOOST_AUTO_TEST_CASE(PartialDerivatives)
{ //{
const int n=5; // const int n=5;
fillDataPlane(); // fillDataPlane();
initProperties(); // initProperties();
//Temps used to store reference and actual variables // //Temps used to store reference and actual variables
VFPEvaluation sad; // VFPEvaluation sad;
VFPEvaluation max_d; // VFPEvaluation max_d;
//Check interpolation // //Check interpolation
for (int i=0; i<=n; ++i) { // for (int i=0; i<=n; ++i) {
const double thp = i / static_cast<double>(n); // const double thp = i / static_cast<double>(n);
for (int j=1; j<=n; ++j) { // for (int j=1; j<=n; ++j) {
const double aqua = -j / static_cast<double>(n); // const double aqua = -j / static_cast<double>(n);
for (int k=1; k<=n; ++k) { // for (int k=1; k<=n; ++k) {
const double vapour = -k / static_cast<double>(n); // const double vapour = -k / static_cast<double>(n);
for (int l=0; l<=n; ++l) { // for (int l=0; l<=n; ++l) {
const double alq = l / static_cast<double>(n); // const double alq = l / static_cast<double>(n);
for (int m=1; m<=n; ++m) { // for (int m=1; m<=n; ++m) {
const double liquid = -m / static_cast<double>(n); // const double liquid = -m / static_cast<double>(n);
//Find values that should be in table // //Find values that should be in table
double flo = Opm::detail::getFlo(aqua, liquid, vapour, table->getFloType()); // double flo = Opm::detail::getFlo(aqua, liquid, vapour, table->getFloType());
double wfr = Opm::detail::getWFR(aqua, liquid, vapour, table->getWFRType()); // double wfr = Opm::detail::getWFR(aqua, liquid, vapour, table->getWFRType());
double gfr = Opm::detail::getGFR(aqua, liquid, vapour, table->getGFRType()); // double gfr = Opm::detail::getGFR(aqua, liquid, vapour, table->getGFRType());
//Calculate reference // //Calculate reference
VFPEvaluation reference; // VFPEvaluation reference;
reference.value = thp + 2*wfr + 3*gfr+ 4*alq - 5*flo; // reference.value = thp + 2*wfr + 3*gfr+ 4*alq - 5*flo;
reference.dthp = 1; // reference.dthp = 1;
reference.dwfr = 2; // reference.dwfr = 2;
reference.dgfr = 3; // reference.dgfr = 3;
reference.dalq = 4; // reference.dalq = 4;
reference.dflo = 5; // reference.dflo = 5;
//Calculate actual // //Calculate actual
//Note order of arguments: id, aqua, liquid, vapour, thp, alq // //Note order of arguments: id, aqua, liquid, vapour, thp, alq
VFPEvaluation actual = Opm::detail::bhp(table.get(), aqua, liquid, vapour, thp, alq); // VFPEvaluation actual = Opm::detail::bhp(table.get(), aqua, liquid, vapour, thp, alq);
VFPEvaluation abs_diff = actual - reference; // VFPEvaluation abs_diff = actual - reference;
abs_diff.value = std::abs(abs_diff.value); // abs_diff.value = std::abs(abs_diff.value);
abs_diff.dthp = std::abs(abs_diff.dthp); // abs_diff.dthp = std::abs(abs_diff.dthp);
abs_diff.dwfr = std::abs(abs_diff.dwfr); // abs_diff.dwfr = std::abs(abs_diff.dwfr);
abs_diff.dgfr = std::abs(abs_diff.dgfr); // abs_diff.dgfr = std::abs(abs_diff.dgfr);
abs_diff.dalq = std::abs(abs_diff.dalq); // abs_diff.dalq = std::abs(abs_diff.dalq);
abs_diff.dflo = std::abs(abs_diff.dflo); // abs_diff.dflo = std::abs(abs_diff.dflo);
max_d.value = std::max(max_d.value, abs_diff.value); // max_d.value = std::max(max_d.value, abs_diff.value);
max_d.dthp = std::max(max_d.dthp, abs_diff.dthp); // max_d.dthp = std::max(max_d.dthp, abs_diff.dthp);
max_d.dwfr = std::max(max_d.dwfr, abs_diff.dwfr); // max_d.dwfr = std::max(max_d.dwfr, abs_diff.dwfr);
max_d.dgfr = std::max(max_d.dgfr, abs_diff.dgfr); // max_d.dgfr = std::max(max_d.dgfr, abs_diff.dgfr);
max_d.dalq = std::max(max_d.dalq, abs_diff.dalq); // max_d.dalq = std::max(max_d.dalq, abs_diff.dalq);
max_d.dflo = std::max(max_d.dflo, abs_diff.dflo); // max_d.dflo = std::max(max_d.dflo, abs_diff.dflo);
sad = sad + abs_diff; // sad = sad + abs_diff;
} // }
} // }
} // }
} // }
} // }
BOOST_CHECK_SMALL(max_d.value, max_d_tol); // BOOST_CHECK_SMALL(max_d.value, max_d_tol);
BOOST_CHECK_SMALL(max_d.dthp, max_d_tol); // BOOST_CHECK_SMALL(max_d.dthp, max_d_tol);
BOOST_CHECK_SMALL(max_d.dwfr, max_d_tol); // BOOST_CHECK_SMALL(max_d.dwfr, max_d_tol);
BOOST_CHECK_SMALL(max_d.dgfr, max_d_tol); // BOOST_CHECK_SMALL(max_d.dgfr, max_d_tol);
BOOST_CHECK_SMALL(max_d.dalq, max_d_tol); // BOOST_CHECK_SMALL(max_d.dalq, max_d_tol);
BOOST_CHECK_SMALL(max_d.dflo, max_d_tol); // BOOST_CHECK_SMALL(max_d.dflo, max_d_tol);
BOOST_CHECK_SMALL(sad.value, sad_tol); // BOOST_CHECK_SMALL(sad.value, sad_tol);
BOOST_CHECK_SMALL(sad.dthp, sad_tol); // BOOST_CHECK_SMALL(sad.dthp, sad_tol);
BOOST_CHECK_SMALL(sad.dwfr, sad_tol); // BOOST_CHECK_SMALL(sad.dwfr, sad_tol);
BOOST_CHECK_SMALL(sad.dgfr, sad_tol); // BOOST_CHECK_SMALL(sad.dgfr, sad_tol);
BOOST_CHECK_SMALL(sad.dalq, sad_tol); // BOOST_CHECK_SMALL(sad.dalq, sad_tol);
BOOST_CHECK_SMALL(sad.dflo, sad_tol); // BOOST_CHECK_SMALL(sad.dflo, sad_tol);
} //}
@ -523,7 +523,7 @@ BOOST_AUTO_TEST_CASE(THPToBHPAndBackPlane)
double aqua = -0.5; double aqua = -0.5;
double liquid = -0.9; double liquid = -0.9;
double vapour = -0.1; double vapour = -0.1;
double thp = 50.0; double thp = 0.5;
double alq = 32.9; double alq = 32.9;
double bhp_val = properties->bhp(1, aqua, liquid, vapour, thp, alq); double bhp_val = properties->bhp(1, aqua, liquid, vapour, thp, alq);
@ -542,7 +542,7 @@ BOOST_AUTO_TEST_CASE(THPToBHPAndBackNonTrivial)
double aqua = -0.5; double aqua = -0.5;
double liquid = -0.9; double liquid = -0.9;
double vapour = -0.1; double vapour = -0.1;
double thp = 50.0; double thp = 0.5;
double alq = 32.9; double alq = 32.9;
double bhp_val = properties->bhp(1, aqua, liquid, vapour, thp, alq); double bhp_val = properties->bhp(1, aqua, liquid, vapour, thp, alq);

View File

@ -206,16 +206,6 @@ BOOST_AUTO_TEST_CASE(TestBehavoir) {
BOOST_CHECK(well->wellType() == PRODUCER); BOOST_CHECK(well->wellType() == PRODUCER);
BOOST_CHECK(well->numEq == 3); BOOST_CHECK(well->numEq == 3);
BOOST_CHECK(well->numStaticWellEq== 4); BOOST_CHECK(well->numStaticWellEq== 4);
const auto& wc = well->wellControls();
const int ctrl_num = well_controls_get_num(wc);
BOOST_CHECK(ctrl_num > 0);
const auto& control = well_controls_get_current(wc);
BOOST_CHECK(control >= 0);
// GAS RATE CONTROL
const auto& distr = well_controls_iget_distr(wc, control);
BOOST_CHECK(distr[0] == 0.);
BOOST_CHECK(distr[1] == 0.);
BOOST_CHECK(distr[2] == 1.);
} }
// second well, it is the injection well from the deck // second well, it is the injection well from the deck
@ -224,16 +214,6 @@ BOOST_AUTO_TEST_CASE(TestBehavoir) {
BOOST_CHECK_EQUAL(well->name(), "INJE1"); BOOST_CHECK_EQUAL(well->name(), "INJE1");
BOOST_CHECK(well->wellType() == INJECTOR); BOOST_CHECK(well->wellType() == INJECTOR);
BOOST_CHECK(well->numEq == 3); BOOST_CHECK(well->numEq == 3);
BOOST_CHECK(well->numStaticWellEq== 4); BOOST_CHECK(well->numStaticWellEq== 4);
const auto& wc = well->wellControls();
const int ctrl_num = well_controls_get_num(wc);
BOOST_CHECK(ctrl_num > 0);
const auto& control = well_controls_get_current(wc);
BOOST_CHECK(control >= 0);
// WATER RATE CONTROL
const auto& distr = well_controls_iget_distr(wc, control);
BOOST_CHECK(distr[0] == 1.);
BOOST_CHECK(distr[1] == 0.);
BOOST_CHECK(distr[2] == 0.);
} }
} }