BlackoilWellModelNetwork_impl.hpp
Go to the documentation of this file.
1/*
2 Copyright 2016 - 2019 SINTEF Digital, Mathematics & Cybernetics.
3 Copyright 2016 - 2018 Equinor ASA.
4 Copyright 2017 Dr. Blatt - HPC-Simulation-Software & Services
5 Copyright 2016 - 2018 Norce AS
6
7 This file is part of the Open Porous Media project (OPM).
8
9 OPM is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 OPM is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with OPM. If not, see <http://www.gnu.org/licenses/>.
21*/
22
23#ifndef OPM_BLACKOILWELLMODEL_NETWORK_IMPL_HEADER_INCLUDED
24#define OPM_BLACKOILWELLMODEL_NETWORK_IMPL_HEADER_INCLUDED
25
26// Improve IDE experience
27#ifndef OPM_BLACKOILWELLMODEL_NETWORK_HEADER_INCLUDED
28#include <config.h>
30#endif
31
32#include <opm/common/TimingMacros.hpp>
33#include <opm/common/utility/numeric/RootFinders.hpp>
34
35#include <opm/input/eclipse/Units/Units.hpp>
36
42
43#include <fmt/format.h>
44
45namespace Opm {
46
47template<typename TypeTag>
50 : BaseType(well_model)
51 , well_model_(well_model)
52{}
53
54template<typename TypeTag>
55void
57doPreStepRebalance(DeferredLogger& deferred_logger)
58{
59 OPM_TIMEFUNCTION();
60 const double dt = well_model_.simulator().timeStepSize();
61 // TODO: should we also have the group and network backed-up
62 // here in case the solution did not get converged?
63 auto& well_state = well_model_.wellState();
64
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() &&
70 !changed_well_group;
71
73 for (auto& well : this->well_model_) {
74 well->solveEqAndUpdateWellState(well_model_.simulator(),
75 well_state,
76 deferred_logger);
77 }
78 OPM_END_PARALLEL_TRY_CATCH("BlackoilWellModelNetwork::doPreStepRebalance() failed: ",
79 well_model_.simulator().vanguard().grid().comm());
80
81 if (!converged) {
82 const std::string msg =
83 fmt::format("Initial (pre-step) network balance did not converge.");
84 deferred_logger.warning(msg);
85 }
86}
87
88template<typename TypeTag>
89std::tuple<bool, typename BlackoilWellModelNetwork<TypeTag>::Scalar>
91update(const bool mandatory_network_balance,
92 DeferredLogger& deferred_logger,
93 const bool relax_network_tolerance)
94{
95 OPM_TIMEFUNCTION();
96 const int episodeIdx = well_model_.simulator().episodeIndex();
97 const auto& network = well_model_.schedule()[episodeIdx].network();
98 if (!well_model_.wellsActive() && !network.active()) {
99 return {false, 0.0};
100 }
101
102 const int iterationIdx = well_model_.simulator().model().newtonMethod().numIterations();
103 const auto& comm = well_model_.simulator().vanguard().grid().comm();
104
105 // network related
106 Scalar network_imbalance = 0.0;
107 bool more_network_update = false;
108 if (this->shouldBalance(episodeIdx, iterationIdx) || mandatory_network_balance) {
109 OPM_TIMEBLOCK(BalanceNetwork);
110 const double dt = well_model_.simulator().timeStepSize();
111 // Calculate common THP for subsea manifold well group (item 3 of NODEPROP set to YES)
112 const bool well_group_thp_updated = computeWellGroupThp(dt, deferred_logger);
113 const int max_number_of_sub_iterations =
114 well_model_.param().network_max_sub_iterations_;
115 const Scalar network_pressure_update_damping_factor =
116 well_model_.param().network_pressure_update_damping_factor_;
117 const Scalar network_max_pressure_update =
118 well_model_.param().network_max_pressure_update_in_bars_ * unit::barsa;
119 bool more_network_sub_update = false;
120 for (int i = 0; i < max_number_of_sub_iterations; i++) {
121 const auto local_network_imbalance =
122 this->updatePressures(episodeIdx,
123 network_pressure_update_damping_factor,
124 network_max_pressure_update);
125 network_imbalance = comm.max(local_network_imbalance);
126 const auto& balance = well_model_.schedule()[episodeIdx].network_balance();
127 constexpr Scalar relaxation_factor = 10.0;
128 const Scalar tolerance =
129 relax_network_tolerance ? relaxation_factor * balance.pressure_tolerance()
130 : balance.pressure_tolerance();
131 more_network_sub_update = this->active() && network_imbalance > tolerance;
132 if (!more_network_sub_update) {
133 break;
134 }
135
136 for (const auto& well : well_model_) {
137 if (well->isInjector() || !well->wellEcl().predictionMode()) {
138 continue;
139 }
140
141 const auto it = this->node_pressures_.find(well->wellEcl().groupName());
142 if (it != this->node_pressures_.end()) {
143 well->prepareWellBeforeAssembling(well_model_.simulator(),
144 dt,
145 well_model_.groupStateHelper(),
146 well_model_.wellState(),
147 deferred_logger);
148 }
149 }
150 well_model_.updateAndCommunicateGroupData(episodeIdx,
151 iterationIdx,
152 well_model_.param().nupcol_group_rate_tolerance_,
153 /*update_wellgrouptarget*/ true,
154 deferred_logger);
155 }
156 more_network_update = more_network_sub_update || well_group_thp_updated;
157 }
158 return { more_network_update, network_imbalance };
159}
160
161template <typename TypeTag>
162bool
164computeWellGroupThp(const double dt, DeferredLogger& local_deferredLogger)
165{
166 OPM_TIMEFUNCTION();
167 const int reportStepIdx = well_model_.simulator().episodeIndex();
168 const auto& network = well_model_.schedule()[reportStepIdx].network();
169 const auto& balance = well_model_.schedule()[reportStepIdx].network_balance();
170 const Scalar thp_tolerance = balance.thp_tolerance();
171
172 if (!network.active()) {
173 return false;
174 }
175
176 auto& well_state = well_model_.wellState();
177 auto& group_state = well_model_.groupState();
178
179 bool well_group_thp_updated = false;
180 for (const std::string& nodeName : network.node_names()) {
181 const bool has_choke = network.node(nodeName).as_choke();
182 if (has_choke) {
183 const auto& summary_state = well_model_.simulator().vanguard().summaryState();
184 const Group& group = well_model_.schedule().getGroup(nodeName, reportStepIdx);
185
186 //TODO: Auto choke combined with RESV control is not supported
187 std::vector<Scalar> resv_coeff(Indices::numPhases, 1.0);
188 Scalar gratTargetFromSales = 0.0;
189 if (group_state.has_grat_sales_target(group.name()))
190 gratTargetFromSales = group_state.grat_sales_target(group.name());
191
192 const auto ctrl = group.productionControls(summary_state);
193 auto cmode_tmp = ctrl.cmode;
194 Scalar target_tmp{0.0};
195 bool fld_none = false;
196 if (cmode_tmp == Group::ProductionCMode::FLD || cmode_tmp == Group::ProductionCMode::NONE) {
197 fld_none = true;
198 // Target is set for an ancestor group. Target for autochoke group to be
199 // derived via group guide rates
200 const Scalar efficiencyFactor = 1.0;
201 const Group& parentGroup = well_model_.schedule().getGroup(group.parent(), reportStepIdx);
204 parentGroup,
205 well_model_.groupStateHelper(),
206 well_model_.schedule(),
207 summary_state,
208 resv_coeff,
209 efficiencyFactor,
210 reportStepIdx,
211 &well_model_.guideRate(),
212 local_deferredLogger);
213 target_tmp = target.first;
214 cmode_tmp = target.second;
215 }
216 const auto cmode = cmode_tmp;
218 TargetCalculatorType tcalc(cmode, FluidSystem::phaseUsage(), resv_coeff,
219 gratTargetFromSales, nodeName, group_state,
220 group.has_gpmaint_control(cmode));
221 if (!fld_none)
222 {
223 // Target is set for the autochoke group itself
224 target_tmp = tcalc.groupTarget(ctrl, local_deferredLogger);
225 }
226
227 const Scalar orig_target = target_tmp;
228
229 auto mismatch = [&] (auto group_thp) {
230 Scalar group_rate(0.0);
231 Scalar rate(0.0);
232 for (auto& well : well_model_) {
233 std::string well_name = well->name();
234 auto& ws = well_state.well(well_name);
235 if (group.hasWell(well_name)) {
236 well->setDynamicThpLimit(group_thp);
237 const Well& well_ecl = well_model_.eclWells()[well->indexOfWell()];
238 const auto inj_controls = Well::InjectionControls(0);
239 const auto prod_controls = well_ecl.productionControls(summary_state);
240 well->iterateWellEqWithSwitching(well_model_.simulator(),
241 dt,
242 inj_controls,
243 prod_controls,
244 well_model_.groupStateHelper(),
245 well_state,
246 local_deferredLogger,
247 /*fixed_control=*/false,
248 /*fixed_status=*/false,
249 /*solving_with_zero_rate=*/false);
250 rate = -tcalc.calcModeRateFromRates(ws.surface_rates);
251 group_rate += rate;
252 }
253 }
254 return (group_rate - orig_target)/orig_target;
255 };
256
257 const auto upbranch = network.uptree_branch(nodeName);
258 const auto it = this->node_pressures_.find((*upbranch).uptree_node());
259 const Scalar nodal_pressure = it->second;
260 Scalar well_group_thp = nodal_pressure;
261
262 std::optional<Scalar> autochoke_thp;
263 if (auto iter = this->well_group_thp_calc_.find(nodeName);
264 iter != this->well_group_thp_calc_.end())
265 {
266 autochoke_thp = this->well_group_thp_calc_.at(nodeName);
267 }
268
269 using WellBhpThpCalculatorType = WellBhpThpCalculator<Scalar, IndexTraits>;
270 //Find an initial bracket
271 std::array<Scalar, 2> range_initial;
272 if (!autochoke_thp.has_value()){
273 Scalar min_thp, max_thp;
274 // Retrieve the terminal pressure of the associated root of the manifold group
275 std::string node_name = nodeName;
276 while (!network.node(node_name).terminal_pressure().has_value()) {
277 auto branch = network.uptree_branch(node_name).value();
278 node_name = branch.uptree_node();
279 }
280 min_thp = network.node(node_name).terminal_pressure().value();
281 WellBhpThpCalculatorType::bruteForceBracketCommonTHP(mismatch, min_thp, max_thp);
282 // Narrow down the bracket
283 Scalar low1, high1;
284 std::array<Scalar, 2> range = {Scalar{0.9}*min_thp, Scalar{1.1}*max_thp};
285 std::optional<Scalar> appr_sol;
286 WellBhpThpCalculatorType::bruteForceBracketCommonTHP(mismatch,
287 range,
288 low1,
289 high1,
290 appr_sol,
291 0.0,
292 local_deferredLogger);
293 min_thp = low1;
294 max_thp = high1;
295 range_initial = {min_thp, max_thp};
296 }
297
298 if (!autochoke_thp.has_value() || autochoke_thp.value() > nodal_pressure) {
299 // The bracket is based on the initial bracket or
300 // on a range based on a previous calculated group thp
301 std::array<Scalar, 2> range = autochoke_thp.has_value() ?
302 std::array<Scalar, 2>{Scalar{0.9} * autochoke_thp.value(),
303 Scalar{1.1} * autochoke_thp.value()} : range_initial;
304 Scalar low, high;
305 std::optional<Scalar> approximate_solution;
306 const Scalar tolerance1 = thp_tolerance;
307 local_deferredLogger.debug("Using brute force search to bracket the group THP");
308 const bool finding_bracket = WellBhpThpCalculatorType::
309 bruteForceBracketCommonTHP(mismatch,
310 range,
311 low,
312 high,
313 approximate_solution,
314 tolerance1,
315 local_deferredLogger);
316
317 if (approximate_solution.has_value()) {
318 autochoke_thp = *approximate_solution;
319 local_deferredLogger.debug("Approximate group THP value found: " +
320 std::to_string(autochoke_thp.value()));
321 } else if (finding_bracket) {
322 const Scalar tolerance2 = thp_tolerance;
323 const int max_iteration_solve = 100;
324 int iteration = 0;
325 autochoke_thp = RegulaFalsiBisection<ThrowOnError>::
326 solve(mismatch,
327 low,
328 high,
329 max_iteration_solve,
330 tolerance2,
331 iteration);
332 local_deferredLogger.debug(" bracket = [" + std::to_string(low) + ", " +
333 std::to_string(high) + "], " +
334 "iteration = " + std::to_string(iteration));
335 local_deferredLogger.debug("Group THP value = " + std::to_string(autochoke_thp.value()));
336 } else {
337 autochoke_thp.reset();
338 local_deferredLogger.debug("Group THP solve failed due to bracketing failure");
339 }
340 }
341 if (autochoke_thp.has_value()) {
342 well_group_thp_calc_[nodeName] = autochoke_thp.value();
343 // Note: The node pressure of the auto-choke node is set
344 // to well_group_thp in computeNetworkPressures()
345 // and must be larger or equal to the pressure of the uptree node of its branch.
346 well_group_thp = std::max(autochoke_thp.value(), nodal_pressure);
347 }
348
349 for (auto& well : well_model_) {
350 std::string well_name = well->name();
351
352 if (well->isInjector() || !well->wellEcl().predictionMode())
353 continue;
354
355 if (group.hasWell(well_name)) {
356 well->setDynamicThpLimit(well_group_thp);
357 }
358 const auto& ws = well_model_.wellState().well(well->indexOfWell());
359 const bool thp_is_limit = ws.production_cmode == Well::ProducerCMode::THP;
360 if (thp_is_limit) {
361 well->prepareWellBeforeAssembling(well_model_.simulator(),
362 dt,
363 well_model_.groupStateHelper(),
364 well_model_.wellState(),
365 local_deferredLogger);
366 }
367 }
368
369 // Use the group THP in computeNetworkPressures().
370 const auto& current_well_group_thp = group_state.is_autochoke_group(nodeName)
371 ? group_state.well_group_thp(nodeName)
372 : 1e30;
373 if (std::abs(current_well_group_thp - well_group_thp) > balance.pressure_tolerance()) {
374 well_group_thp_updated = true;
375 group_state.update_well_group_thp(nodeName, well_group_thp);
376 }
377 }
378 }
379 return well_group_thp_updated;
380}
381
382} // namespace Opm
383
384#endif // OPM_BLACKOILWELLMODEL_NETWORK_IMPL_HEADER_INCLUDED
#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:48
bool computeWellGroupThp(const double dt, DeferredLogger &local_deferredLogger)
Definition: BlackoilWellModelNetwork_impl.hpp:164
std::tuple< bool, Scalar > update(const bool mandatory_network_balance, DeferredLogger &deferred_logger, const bool relax_network_tolerance=false)
Definition: BlackoilWellModelNetwork_impl.hpp:91
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:104
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:44
Class for computing BHP limits.
Definition: WellBhpThpCalculator.hpp:41
static std::pair< Scalar, Group::ProductionCMode > getAutoChokeGroupProductionTargetRate(const std::string &name, 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)