23#ifndef OPM_BLACKOILWELLMODEL_NETWORK_IMPL_HEADER_INCLUDED
24#define OPM_BLACKOILWELLMODEL_NETWORK_IMPL_HEADER_INCLUDED
27#ifndef OPM_BLACKOILWELLMODEL_NETWORK_HEADER_INCLUDED
32#include <opm/common/TimingMacros.hpp>
33#include <opm/common/utility/numeric/RootFinders.hpp>
35#include <opm/input/eclipse/Units/Units.hpp>
43#include <fmt/format.h>
47template<
typename TypeTag>
51 , well_model_(well_model)
54template<
typename TypeTag>
60 const double dt = well_model_.simulator().timeStepSize();
63 auto& well_state = well_model_.wellState();
65 const bool changed_well_group =
66 well_model_.updateWellControlsAndNetwork(
true, dt, deferred_logger);
67 well_model_.assembleWellEqWithoutIteration(dt, deferred_logger);
68 const bool converged =
69 well_model_.getWellConvergence(well_model_.B_avg(),
true).converged() &&
73 for (
auto& well : this->well_model_) {
74 well->solveEqAndUpdateWellState(well_model_.simulator(),
75 well_model_.groupStateHelper(),
80 well_model_.simulator().vanguard().grid().comm());
83 const std::string msg =
84 fmt::format(
"Initial (pre-step) network balance did not converge.");
89template<
typename TypeTag>
90std::tuple<bool, typename BlackoilWellModelNetwork<TypeTag>::Scalar>
92update(
const bool mandatory_network_balance,
94 const bool relax_network_tolerance)
97 const int episodeIdx = well_model_.simulator().episodeIndex();
98 const auto& network = well_model_.schedule()[episodeIdx].network();
99 if (!well_model_.wellsActive() && !network.active()) {
103 const int iterationIdx = well_model_.simulator().model().newtonMethod().numIterations();
104 const auto& comm = well_model_.simulator().vanguard().grid().comm();
107 Scalar network_imbalance = 0.0;
108 bool more_network_update =
false;
109 if (this->shouldBalance(episodeIdx, iterationIdx) || mandatory_network_balance) {
110 OPM_TIMEBLOCK(BalanceNetwork);
111 const double dt = well_model_.simulator().timeStepSize();
113 const bool well_group_thp_updated = computeWellGroupThp(dt, deferred_logger);
114 const int max_number_of_sub_iterations =
115 well_model_.param().network_max_sub_iterations_;
116 const Scalar network_pressure_update_damping_factor =
117 well_model_.param().network_pressure_update_damping_factor_;
118 const Scalar network_max_pressure_update =
119 well_model_.param().network_max_pressure_update_in_bars_ * unit::barsa;
120 bool more_network_sub_update =
false;
121 for (
int i = 0; i < max_number_of_sub_iterations; i++) {
122 const auto local_network_imbalance =
123 this->updatePressures(episodeIdx,
124 network_pressure_update_damping_factor,
125 network_max_pressure_update);
126 network_imbalance = comm.max(local_network_imbalance);
127 const auto& balance = well_model_.schedule()[episodeIdx].network_balance();
128 constexpr Scalar relaxation_factor = 10.0;
129 const Scalar tolerance =
130 relax_network_tolerance ? relaxation_factor * balance.pressure_tolerance()
131 : balance.pressure_tolerance();
132 more_network_sub_update = this->active() && network_imbalance > tolerance;
133 if (!more_network_sub_update) {
137 for (
const auto& well : well_model_) {
138 if (well->isInjector() || !well->wellEcl().predictionMode()) {
142 const auto it = this->node_pressures_.find(well->wellEcl().groupName());
143 if (it != this->node_pressures_.end()) {
144 well->prepareWellBeforeAssembling(well_model_.simulator(),
146 well_model_.groupStateHelper(),
147 well_model_.wellState(),
151 well_model_.updateAndCommunicateGroupData(episodeIdx,
153 well_model_.param().nupcol_group_rate_tolerance_,
157 more_network_update = more_network_sub_update || well_group_thp_updated;
159 return { more_network_update, network_imbalance };
162template <
typename TypeTag>
168 const int reportStepIdx = well_model_.simulator().episodeIndex();
169 const auto& network = well_model_.schedule()[reportStepIdx].network();
170 const auto& balance = well_model_.schedule()[reportStepIdx].network_balance();
171 const Scalar thp_tolerance = balance.thp_tolerance();
173 if (!network.active()) {
177 auto& well_state = well_model_.wellState();
178 auto& group_state = well_model_.groupState();
180 bool well_group_thp_updated =
false;
181 for (
const std::string& nodeName : network.node_names()) {
182 const bool has_choke = network.node(nodeName).as_choke();
184 const auto& summary_state = well_model_.simulator().vanguard().summaryState();
185 const Group& group = well_model_.schedule().getGroup(nodeName, reportStepIdx);
188 std::vector<Scalar> resv_coeff(Indices::numPhases, 1.0);
190 const auto ctrl = group.productionControls(summary_state);
191 auto cmode_tmp = ctrl.cmode;
192 Scalar target_tmp{0.0};
193 bool fld_none =
false;
198 const Scalar efficiencyFactor = 1.0;
199 const Group& parentGroup = well_model_.schedule().getGroup(group.parent(), reportStepIdx);
203 well_model_.groupStateHelper(),
204 well_model_.schedule(),
209 &well_model_.guideRate(),
210 local_deferredLogger);
211 target_tmp = target.first;
212 cmode_tmp = target.second;
215 TargetCalculatorType tcalc{well_model_.groupStateHelper(), resv_coeff, group};
219 target_tmp = tcalc.
groupTarget(local_deferredLogger);
222 const Scalar orig_target = target_tmp;
224 auto mismatch = [&] (
auto group_thp) {
225 Scalar group_rate(0.0);
227 for (
auto& well : well_model_) {
228 std::string well_name = well->name();
229 auto& ws = well_state.well(well_name);
230 if (group.hasWell(well_name)) {
231 well->setDynamicThpLimit(group_thp);
232 const Well& well_ecl = well_model_.eclWells()[well->indexOfWell()];
233 const auto inj_controls = Well::InjectionControls(0);
234 const auto prod_controls = well_ecl.productionControls(summary_state);
235 well->iterateWellEqWithSwitching(well_model_.simulator(),
239 well_model_.groupStateHelper(),
241 local_deferredLogger,
245 rate = -tcalc.calcModeRateFromRates(ws.surface_rates);
249 return (group_rate - orig_target)/orig_target;
252 const auto upbranch = network.uptree_branch(nodeName);
253 const auto it = this->node_pressures_.find((*upbranch).uptree_node());
254 const Scalar nodal_pressure = it->second;
255 Scalar well_group_thp = nodal_pressure;
257 std::optional<Scalar> autochoke_thp;
258 if (
auto iter = this->well_group_thp_calc_.find(nodeName);
259 iter != this->well_group_thp_calc_.end())
261 autochoke_thp = this->well_group_thp_calc_.at(nodeName);
266 std::array<Scalar, 2> range_initial;
267 if (!autochoke_thp.has_value()){
268 Scalar min_thp, max_thp;
270 std::string node_name = nodeName;
271 while (!network.node(node_name).terminal_pressure().has_value()) {
272 auto branch = network.uptree_branch(node_name).value();
273 node_name = branch.uptree_node();
275 min_thp = network.node(node_name).terminal_pressure().value();
276 WellBhpThpCalculatorType::bruteForceBracketCommonTHP(mismatch, min_thp, max_thp);
279 std::array<Scalar, 2> range = {Scalar{0.9}*min_thp, Scalar{1.1}*max_thp};
280 std::optional<Scalar> appr_sol;
281 WellBhpThpCalculatorType::bruteForceBracketCommonTHP(mismatch,
287 local_deferredLogger);
290 range_initial = {min_thp, max_thp};
293 if (!autochoke_thp.has_value() || autochoke_thp.value() > nodal_pressure) {
296 std::array<Scalar, 2> range = autochoke_thp.has_value() ?
297 std::array<Scalar, 2>{Scalar{0.9} * autochoke_thp.value(),
298 Scalar{1.1} * autochoke_thp.value()} : range_initial;
300 std::optional<Scalar> approximate_solution;
301 const Scalar tolerance1 = thp_tolerance;
302 local_deferredLogger.
debug(
"Using brute force search to bracket the group THP");
303 const bool finding_bracket = WellBhpThpCalculatorType::
304 bruteForceBracketCommonTHP(mismatch,
308 approximate_solution,
310 local_deferredLogger);
312 if (approximate_solution.has_value()) {
313 autochoke_thp = *approximate_solution;
314 local_deferredLogger.
debug(
"Approximate group THP value found: " +
316 }
else if (finding_bracket) {
317 const Scalar tolerance2 = thp_tolerance;
318 const int max_iteration_solve = 100;
320 autochoke_thp = RegulaFalsiBisection<ThrowOnError>::
332 autochoke_thp.reset();
333 local_deferredLogger.
debug(
"Group THP solve failed due to bracketing failure");
336 if (autochoke_thp.has_value()) {
337 well_group_thp_calc_[nodeName] = autochoke_thp.value();
341 well_group_thp = std::max(autochoke_thp.value(), nodal_pressure);
344 for (
auto& well : well_model_) {
345 std::string well_name = well->name();
347 if (well->isInjector() || !well->wellEcl().predictionMode())
350 if (group.hasWell(well_name)) {
351 well->setDynamicThpLimit(well_group_thp);
353 const auto& ws = well_model_.wellState().well(well->indexOfWell());
354 const bool thp_is_limit = ws.production_cmode == Well::ProducerCMode::THP;
356 well->prepareWellBeforeAssembling(well_model_.simulator(),
358 well_model_.groupStateHelper(),
359 well_model_.wellState(),
360 local_deferredLogger);
365 const auto& current_well_group_thp = group_state.is_autochoke_group(nodeName)
366 ? group_state.well_group_thp(nodeName)
368 if (std::abs(current_well_group_thp - well_group_thp) > balance.pressure_tolerance()) {
369 well_group_thp_updated =
true;
370 group_state.update_well_group_thp(nodeName, well_group_thp);
374 return well_group_thp_updated;
#define OPM_END_PARALLEL_TRY_CATCH(prefix, comm)
Catch exception and throw in a parallel try-catch clause.
Definition: DeferredLoggingErrorHelpers.hpp:192
#define OPM_BEGIN_PARALLEL_TRY_CATCH()
Macro to setup the try of a parallel try-catch.
Definition: DeferredLoggingErrorHelpers.hpp:158
Class for handling the blackoil well network model.
Definition: BlackoilWellModelNetworkGeneric.hpp:49
bool computeWellGroupThp(const double dt, DeferredLogger &local_deferredLogger)
Definition: BlackoilWellModelNetwork_impl.hpp:165
std::tuple< bool, Scalar > update(const bool mandatory_network_balance, DeferredLogger &deferred_logger, const bool relax_network_tolerance=false)
Definition: BlackoilWellModelNetwork_impl.hpp:92
BlackoilWellModelNetwork(BlackoilWellModel< TypeTag > &well_model)
Definition: BlackoilWellModelNetwork_impl.hpp:49
void doPreStepRebalance(DeferredLogger &deferred_logger)
Definition: BlackoilWellModelNetwork_impl.hpp:57
Class for handling the blackoil well model.
Definition: BlackoilWellModel.hpp:98
Definition: DeferredLogger.hpp:57
void warning(const std::string &tag, const std::string &message)
void debug(const std::string &tag, const std::string &message)
Definition: TargetCalculator.hpp:45
Scalar groupTarget(DeferredLogger &deferred_logger) const
Class for computing BHP limits.
Definition: WellBhpThpCalculator.hpp:41
static std::pair< Scalar, Group::ProductionCMode > getAutoChokeGroupProductionTargetRate(const Group &bottom_group, const Group &group, const GroupStateHelperType &groupStateHelper, const Schedule &schedule, const SummaryState &summaryState, const std::vector< Scalar > &resv_coeff, Scalar efficiencyFactor, const int reportStepIdx, const GuideRate *guideRate, DeferredLogger &deferred_logger)
@ NONE
Definition: DeferredLogger.hpp:46
Definition: blackoilbioeffectsmodules.hh:43
std::string to_string(const ConvergenceReport::ReservoirFailure::Type t)