WellInterface_impl.hpp
Go to the documentation of this file.
1/*
2 Copyright 2017 SINTEF Digital, Mathematics and Cybernetics.
3 Copyright 2017 Statoil ASA.
4 Copyright 2018 IRIS
5
6 This file is part of the Open Porous Media project (OPM).
7
8 OPM is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 OPM is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with OPM. If not, see <http://www.gnu.org/licenses/>.
20*/
21
22#ifndef OPM_WELLINTERFACE_IMPL_HEADER_INCLUDED
23#define OPM_WELLINTERFACE_IMPL_HEADER_INCLUDED
24
25// Improve IDE experience
26#ifndef OPM_WELLINTERFACE_HEADER_INCLUDED
27#include <config.h>
29#endif
30
31#include <opm/common/Exceptions.hpp>
32
33#include <opm/input/eclipse/Schedule/ScheduleTypes.hpp>
34#include <opm/input/eclipse/Schedule/Well/WDFAC.hpp>
35
37
42
43#include <dune/common/version.hh>
44
45#include <algorithm>
46#include <cassert>
47#include <cstddef>
48#include <numbers>
49#include <utility>
50
51#include <fmt/format.h>
52
53namespace Opm
54{
55
56
57 template<typename TypeTag>
59 WellInterface(const Well& well,
60 const ParallelWellInfo<Scalar>& pw_info,
61 const int time_step,
62 const ModelParameters& param,
63 const RateConverterType& rate_converter,
64 const int pvtRegionIdx,
65 const int num_conservation_quantities,
66 const int num_phases,
67 const int index_of_well,
68 const std::vector<PerforationData<Scalar>>& perf_data)
70 pw_info,
71 time_step,
72 param,
73 rate_converter,
74 pvtRegionIdx,
75 num_conservation_quantities,
76 num_phases,
77 index_of_well,
78 perf_data)
79 {
81
82 if constexpr (has_solvent || has_zFraction) {
83 if (well.isInjector()) {
84 auto injectorType = this->well_ecl_.injectorType();
85 if (injectorType == InjectorType::GAS) {
86 this->wsolvent_ = this->well_ecl_.getSolventFraction();
87 }
88 }
89 }
90 }
91
92
93 template<typename TypeTag>
94 void
96 init(const std::vector<Scalar>& /* depth_arg */,
97 const Scalar gravity_arg,
98 const std::vector<Scalar>& B_avg,
99 const bool changed_to_open_this_step)
100 {
101 this->gravity_ = gravity_arg;
102 B_avg_ = B_avg;
103 this->changed_to_open_this_step_ = changed_to_open_this_step;
104 }
105
106
107
108
109 template<typename TypeTag>
112 wpolymer() const
113 {
114 if constexpr (has_polymer) {
115 return this->wpolymer_();
116 }
117
118 return 0.0;
119 }
120
121
122
123
124
125 template<typename TypeTag>
128 wfoam() const
129 {
130 if constexpr (has_foam) {
131 return this->wfoam_();
132 }
133
134 return 0.0;
135 }
136
137
138
139 template<typename TypeTag>
142 wsalt() const
143 {
144 if constexpr (has_brine) {
145 return this->wsalt_();
146 }
147
148 return 0.0;
149 }
150
151 template<typename TypeTag>
154 wmicrobes() const
155 {
156 if constexpr (has_micp) {
157 return this->wmicrobes_();
158 }
159
160 return 0.0;
161 }
162
163 template<typename TypeTag>
166 woxygen() const
167 {
168 if constexpr (has_micp) {
169 return this->woxygen_();
170 }
171
172 return 0.0;
173 }
174
175 template<typename TypeTag>
178 wurea() const
179 {
180 if constexpr (has_micp) {
181 return this->wurea_();
182 }
183
184 return 0.0;
185 }
186
187 template<typename TypeTag>
188 bool
190 updateWellControl(const Simulator& simulator,
191 const IndividualOrGroup iog,
192 const GroupStateHelperType& groupStateHelper,
193 WellStateType& well_state) /* const */
194 {
195 auto& deferred_logger = groupStateHelper.deferredLogger();
196 OPM_TIMEFUNCTION();
197 if (stoppedOrZeroRateTarget(groupStateHelper)) {
198 return false;
199 }
200
201 const auto& summaryState = simulator.vanguard().summaryState();
202 const auto& schedule = simulator.vanguard().schedule();
203 const auto& well = this->well_ecl_;
204 auto& ws = well_state.well(this->index_of_well_);
205 std::string from;
206 bool is_grup = false;
207 if (well.isInjector()) {
208 from = WellInjectorCMode2String(ws.injection_cmode);
209 is_grup = ws.injection_cmode == Well::InjectorCMode::GRUP;
210 } else {
211 from = WellProducerCMode2String(ws.production_cmode);
212 is_grup = ws.production_cmode == Well::ProducerCMode::GRUP;
213 }
214
215 const int episodeIdx = simulator.episodeIndex();
216 const auto& iterCtx = simulator.problem().iterationContext();
217 const int nupcol = schedule[episodeIdx].nupcol();
218 const bool oscillating =
219 std::ranges::count(this->well_control_log_, from) >= this->param_.max_number_of_well_switches_;
220 if (oscillating && !is_grup) { // we would like to avoid ending up as GRUP
221 // only output first time
222 const bool output =
223 std::ranges::count(this->well_control_log_, from) == this->param_.max_number_of_well_switches_;
224 if (output) {
225 const auto msg = fmt::format(" The control mode for well {} is oscillating. \n"
226 "We don't allow for more than {} switches after NUPCOL iterations. (NUPCOL = {}) \n"
227 "The control is kept at {}.",
228 this->name(), this->param_.max_number_of_well_switches_, nupcol, from);
229 deferred_logger.info(msg);
230 // add one more to avoid outputting the same info again
231 this->well_control_log_.push_back(from);
232 }
233 return false;
234 }
235 bool changed = false;
236 if (iog == IndividualOrGroup::Individual) {
237 changed = this->checkIndividualConstraints(ws, summaryState, deferred_logger);
238 } else if (iog == IndividualOrGroup::Group) {
239 changed = this->checkGroupConstraints(
240 groupStateHelper, schedule, summaryState, true, well_state
241 );
242 } else {
243 assert(iog == IndividualOrGroup::Both);
244 changed = this->checkConstraints(groupStateHelper, schedule, summaryState, well_state);
245 }
246 Parallel::Communication cc = simulator.vanguard().grid().comm();
247 // checking whether control changed
248 if (changed) {
249 std::string to;
250 if (well.isInjector()) {
251 to = WellInjectorCMode2String(ws.injection_cmode);
252 } else {
253 to = WellProducerCMode2String(ws.production_cmode);
254 }
255 std::ostringstream ss;
256 ss << " Switching control mode for well " << this->name()
257 << " from " << from
258 << " to " << to;
259 if (iterCtx.inLocalSolve()) {
260 ss << " (NLDD domain solve)";
261 }
262 if (cc.size() > 1) {
263 ss << " on rank " << cc.rank();
264 }
265 deferred_logger.debug(ss.str());
266
267 // We always store the current control as it is used for output
268 // and only after iteration >= nupcol
269 // we log all switches to check if the well controls oscillates.
270 // Skip logging during NLDD local solves to avoid exhausting the
271 // global oscillation budget with provisional domain-level switches.
272 if (!iterCtx.inLocalSolve()) {
273 if (!iterCtx.withinNupcol(nupcol) || this->well_control_log_.empty()) {
274 this->well_control_log_.push_back(from);
275 }
276 }
277 updateWellStateWithTarget(simulator, groupStateHelper, well_state);
278 updatePrimaryVariables(groupStateHelper);
279 }
280
281 return changed;
282 }
283
284 template<typename TypeTag>
285 bool
288 const GroupStateHelperType& groupStateHelper,
289 const Well::InjectionControls& inj_controls,
290 const Well::ProductionControls& prod_controls,
291 const Scalar wqTotal,
292 WellStateType& well_state,
293 const bool fixed_control,
294 const bool fixed_status,
295 const bool solving_with_zero_rate)
296 {
297 OPM_TIMEFUNCTION();
298 auto& deferred_logger = groupStateHelper.deferredLogger();
299 const auto& summary_state = simulator.vanguard().summaryState();
300 const auto& schedule = simulator.vanguard().schedule();
301 auto& ws = well_state.well(this->index_of_well_);
302 std::string from;
303 if (this->isInjector()) {
304 from = WellInjectorCMode2String(ws.injection_cmode);
305 } else {
306 from = WellProducerCMode2String(ws.production_cmode);
307 }
308 const bool oscillating =
309 std::ranges::count(this->well_control_log_, from) >= this->param_.max_number_of_well_switches_;
310
311 if (oscillating || this->wellUnderZeroRateTarget(groupStateHelper) || !(well_state.well(this->index_of_well_).status == WellStatus::OPEN)) {
312 return false;
313 }
314
315 const Scalar sgn = this->isInjector() ? 1.0 : -1.0;
316 if (!this->wellIsStopped()){
317 if (wqTotal*sgn <= 0.0 && !fixed_status){
318 this->stopWell();
319 return true;
320 } else {
321 bool changed = false;
322 if (!fixed_control) {
323 // When solving_with_zero_rate=true, fixed_control=true, so this block should never
324 // be entered.
325 if (solving_with_zero_rate) {
326 OPM_DEFLOG_THROW(std::runtime_error, fmt::format(
327 "Well {}: solving_with_zero_rate should not be true when fixed_control is false",
328 this->name()), deferred_logger);
329 }
330
331 // Changing to group controls here may lead to inconsistencies in the group handling which in turn
332 // may result in excessive back and forth switching. However, we currently allow this by default.
333 // The switch check_group_constraints_inner_well_iterations_ is a temporary solution.
334 const bool hasGroupControl = this->isInjector() ? inj_controls.hasControl(Well::InjectorCMode::GRUP) :
335 prod_controls.hasControl(Well::ProducerCMode::GRUP);
336 bool isGroupControl = ws.production_cmode == Well::ProducerCMode::GRUP || ws.injection_cmode == Well::InjectorCMode::GRUP;
337 if (! (isGroupControl && !this->param_.check_group_constraints_inner_well_iterations_)) {
338 changed = this->checkIndividualConstraints(ws, summary_state, deferred_logger, inj_controls, prod_controls);
339 }
340 if (hasGroupControl && this->param_.check_group_constraints_inner_well_iterations_) {
341 changed = changed || this->checkGroupConstraints(
342 groupStateHelper, schedule, summary_state, false, well_state
343 );
344 }
345
346 if (changed) {
347 const bool thp_controlled = this->isInjector() ? ws.injection_cmode == Well::InjectorCMode::THP :
348 ws.production_cmode == Well::ProducerCMode::THP;
349 if (thp_controlled){
350 ws.thp = this->getTHPConstraint(summary_state);
351 } else {
352 // don't call for thp since this might trigger additional local solve
353 updateWellStateWithTarget(simulator, groupStateHelper, well_state);
354 }
355 updatePrimaryVariables(groupStateHelper);
356 }
357 }
358 return changed;
359 }
360 } else if (!fixed_status){
361 // well is stopped, check if current bhp allows reopening
362 const Scalar bhp = well_state.well(this->index_of_well_).bhp;
363 Scalar prod_limit = prod_controls.bhp_limit;
364 Scalar inj_limit = inj_controls.bhp_limit;
365 const bool has_thp = this->wellHasTHPConstraints(summary_state);
366 if (has_thp){
367 std::vector<Scalar> rates(this->num_conservation_quantities_);
368 if (this->isInjector()){
369 const Scalar bhp_thp = WellBhpThpCalculator(*this).
370 calculateBhpFromThp(well_state, rates,
371 this->well_ecl_,
372 summary_state,
373 this->getRefDensity(),
374 deferred_logger);
375 inj_limit = std::min(bhp_thp, static_cast<Scalar>(inj_controls.bhp_limit));
376 } else {
377 // if the well can operate, it must at least be able to produce
378 // at the lowest bhp of the bhp-curve (explicit fractions)
379 const Scalar bhp_min = WellBhpThpCalculator(*this).
380 calculateMinimumBhpFromThp(well_state,
381 this->well_ecl_,
382 summary_state,
383 this->getRefDensity());
384 prod_limit = std::max(bhp_min, static_cast<Scalar>(prod_controls.bhp_limit));
385 }
386 }
387 const Scalar bhp_diff = (this->isInjector())? inj_limit - bhp: bhp - prod_limit;
388 if (bhp_diff > 0){
389 this->openWell();
390 well_state.well(this->index_of_well_).bhp = (this->isInjector())? inj_limit : prod_limit;
391 if (has_thp) {
392 well_state.well(this->index_of_well_).thp = this->getTHPConstraint(summary_state);
393 }
394 return true;
395 } else {
396 return false;
397 }
398 } else {
399 return false;
400 }
401 }
402
403 template<typename TypeTag>
404 void
406 wellTesting(const Simulator& simulator,
407 const double simulation_time,
408 const GroupStateHelperType& groupStateHelper,
409 WellStateType& well_state,
410 WellTestState& well_test_state,
411 GLiftEclWells& ecl_well_map,
412 std::map<std::string, double>& open_times)
413 {
414 OPM_TIMEFUNCTION();
415 auto& deferred_logger = groupStateHelper.deferredLogger();
416 const auto& group_state = groupStateHelper.groupState();
417 deferred_logger.info(" well " + this->name() + " is being tested");
418
419 GroupStateHelperType groupStateHelper_copy = groupStateHelper;
420 WellStateType well_state_copy = well_state;
421 // Ensure that groupStateHelper uses well_state_copy as WellState for the well testing
422 // and the guard ensures that the original well state is restored at scope exit, i.e. at
423 // the end of this function.
424 auto guard = groupStateHelper_copy.pushWellState(well_state_copy);
425 auto& ws = well_state_copy.well(this->indexOfWell());
426
427 const auto& summary_state = simulator.vanguard().summaryState();
428 const bool has_thp_limit = this->wellHasTHPConstraints(summary_state);
429 if (this->isProducer()) {
430 ws.production_cmode = has_thp_limit ? Well::ProducerCMode::THP : Well::ProducerCMode::BHP;
431 } else {
432 ws.injection_cmode = has_thp_limit ? Well::InjectorCMode::THP : Well::InjectorCMode::BHP;
433 }
434 // We test the well as an open well during the well testing
435 ws.open();
436
437 scaleSegmentRatesAndPressure(well_state_copy);
438 calculateExplicitQuantities(simulator, groupStateHelper_copy);
439 updatePrimaryVariables(groupStateHelper_copy);
440
441 if (this->isProducer()) {
442 const auto& schedule = simulator.vanguard().schedule();
443 const auto report_step = simulator.episodeIndex();
444 const auto& glo = schedule.glo(report_step);
445 if (glo.active()) {
446 gliftBeginTimeStepWellTestUpdateALQ(simulator,
447 well_state_copy,
448 group_state,
449 ecl_well_map,
450 deferred_logger);
451 }
452 }
453
454 WellTestState welltest_state_temp;
455
456 bool testWell = true;
457 // if a well is closed because all completions are closed, we need to check each completion
458 // individually. We first open all completions, then we close one by one by calling updateWellTestState
459 // untill the number of closed completions do not increase anymore.
460 while (testWell) {
461 const std::size_t original_number_closed_completions = welltest_state_temp.num_closed_completions();
462 bool converged = solveWellForTesting(simulator, groupStateHelper_copy, well_state_copy);
463 if (!converged) {
464 const auto msg = fmt::format("WTEST: Well {} is not solvable (physical)", this->name());
465 deferred_logger.debug(msg);
466 return;
467 }
468
469
470 updateWellOperability(simulator, well_state_copy, groupStateHelper_copy);
471 if ( !this->isOperableAndSolvable() ) {
472 const auto msg = fmt::format("WTEST: Well {} is not operable (physical)", this->name());
473 deferred_logger.debug(msg);
474 return;
475 }
476 std::vector<Scalar> potentials;
477 try {
478 computeWellPotentials(simulator, well_state_copy, groupStateHelper_copy, potentials);
479 } catch (const std::exception& e) {
480 const std::string msg = fmt::format("well {}: computeWellPotentials() "
481 "failed during testing for re-opening: ",
482 this->name(), e.what());
483 deferred_logger.info(msg);
484 return;
485 }
486 const int np = well_state_copy.numPhases();
487 for (int p = 0; p < np; ++p) {
488 ws.well_potentials[p] = std::max(Scalar{0.0}, potentials[p]);
489 }
490 const bool under_zero_target = this->wellUnderZeroGroupRateTarget(groupStateHelper_copy);
491 this->updateWellTestState(well_state_copy.well(this->indexOfWell()),
492 simulation_time,
493 /*writeMessageToOPMLog=*/ false,
494 under_zero_target,
495 welltest_state_temp,
496 deferred_logger);
497 this->closeCompletions(welltest_state_temp);
498
499 // Stop testing if the well is closed or shut due to all completions shut
500 // Also check if number of completions has increased. If the number of closed completions do not increased
501 // we stop the testing.
502 // TODO: it can be tricky here, if the well is shut/closed due to other reasons
503 if ( welltest_state_temp.num_closed_wells() > 0 ||
504 (original_number_closed_completions == welltest_state_temp.num_closed_completions()) ) {
505 testWell = false; // this terminates the while loop
506 }
507 }
508
509 // update wellTestState if the well test succeeds
510 if (!welltest_state_temp.well_is_closed(this->name())) {
511 well_test_state.open_well(this->name());
512
513 std::string msg = std::string("well ") + this->name() + std::string(" is re-opened");
514 deferred_logger.info(msg);
515
516 // also reopen completions
517 for (const auto& completion : this->well_ecl_.getCompletions()) {
518 if (!welltest_state_temp.completion_is_closed(this->name(), completion.first))
519 well_test_state.open_completion(this->name(), completion.first);
520 }
521 well_state = well_state_copy;
522 open_times.try_emplace(this->name(), well_test_state.lastTestTime(this->name()));
523 }
524 }
525
526
527
528
529 template<typename TypeTag>
530 bool
532 iterateWellEquations(const Simulator& simulator,
533 const double dt,
534 const GroupStateHelperType& groupStateHelper,
535 WellStateType& well_state)
536 {
537 OPM_TIMEFUNCTION();
538 auto& deferred_logger = groupStateHelper.deferredLogger();
539
540 const auto& summary_state = simulator.vanguard().summaryState();
541 const auto inj_controls = this->well_ecl_.isInjector() ? this->well_ecl_.injectionControls(summary_state) : Well::InjectionControls(0);
542 const auto prod_controls = this->well_ecl_.isProducer() ? this->well_ecl_.productionControls(summary_state) : Well::ProductionControls(0);
543 const auto& ws = well_state.well(this->indexOfWell());
544 const auto pmode_orig = ws.production_cmode;
545 const auto imode_orig = ws.injection_cmode;
546 bool converged = false;
547 try {
548 // TODO: the following two functions will be refactored to be one to reduce the code duplication
549 if (!this->param_.local_well_solver_control_switching_){
550 converged = this->iterateWellEqWithControl(simulator, dt, inj_controls, prod_controls, groupStateHelper, well_state);
551 } else {
552 if (this->param_.use_implicit_ipr_ && this->well_ecl_.isProducer() && (well_state.well(this->index_of_well_).status == WellStatus::OPEN)) {
553 converged = solveWellWithOperabilityCheck(
554 simulator, dt, inj_controls, prod_controls, groupStateHelper, well_state
555 );
556 } else {
557 converged = this->iterateWellEqWithSwitching(
558 simulator, dt, inj_controls, prod_controls, groupStateHelper, well_state,
559 /*fixed_control=*/false, /*fixed_status=*/false, /*solving_with_zero_rate=*/false
560 );
561 }
562 }
563
564 } catch (NumericalProblem& e ) {
565 const std::string msg = "Inner well iterations failed for well " + this->name() + " Treat the well as unconverged. ";
566 deferred_logger.warning("INNER_ITERATION_FAILED", msg);
567 converged = false;
568 }
569 if (converged) {
570 // Add debug info for switched controls
571 if (ws.production_cmode != pmode_orig || ws.injection_cmode != imode_orig) {
572 std::string from,to;
573 if (this->isInjector()) {
574 from = WellInjectorCMode2String(imode_orig);
575 to = WellInjectorCMode2String(ws.injection_cmode);
576 } else {
577 from = WellProducerCMode2String(pmode_orig);
578 to = WellProducerCMode2String(ws.production_cmode);
579 }
580 const auto msg = fmt::format(" Well {} switched from {} to {} during local solve", this->name(), from, to);
581 deferred_logger.debug(msg);
582 const int episodeIdx = simulator.episodeIndex();
583 const auto& iterCtx = simulator.problem().iterationContext();
584 const auto& schedule = simulator.vanguard().schedule();
585 const int nupcol = schedule[episodeIdx].nupcol();
586 // We always store the current control as it is used for output
587 // and only after iteration >= nupcol
588 // we log all switches to check if the well controls oscillates
589 if (!iterCtx.withinNupcol(nupcol) || this->well_control_log_.empty()) {
590 this->well_control_log_.push_back(from);
591 }
592 }
593 }
594
595 return converged;
596 }
597
598 template<typename TypeTag>
599 bool
602 const double dt,
603 const Well::InjectionControls& inj_controls,
604 const Well::ProductionControls& prod_controls,
605 const GroupStateHelperType& groupStateHelper,
606 WellStateType& well_state)
607 {
608 OPM_TIMEFUNCTION();
609 auto& deferred_logger = groupStateHelper.deferredLogger();
610
611 const auto& summary_state = simulator.vanguard().summaryState();
612 bool converged = true;
613 auto& ws = well_state.well(this->index_of_well_);
614 // if well is stopped, check if we can reopen with explicit fraction
615 if (this->wellIsStopped()) {
616 this->openWell();
617 const bool use_vfpexplicit = this->operability_status_.use_vfpexplicit;
618 this->operability_status_.use_vfpexplicit = true;
619 auto bhp_target = estimateOperableBhp(simulator, dt, groupStateHelper, summary_state, well_state);
620 if (!bhp_target.has_value()) {
621 // no intersection with ipr
622 const auto msg = fmt::format("estimateOperableBhp: Did not find operable BHP for well {}", this->name());
623 deferred_logger.debug(msg);
624 // well can't operate using explicit fractions stop the well
625 // solve with zero rates
626 converged = solveWellWithZeroRate(simulator, dt, groupStateHelper, well_state);
627 this->stopWell();
628 this->operability_status_.can_obtain_bhp_with_thp_limit = false;
629 this->operability_status_.obey_thp_limit_under_bhp_limit = false;
630 return converged;
631 } else {
632 // solve well with the estimated target bhp (or limit)
633 ws.thp = this->getTHPConstraint(summary_state);
634 const Scalar bhp = std::max(bhp_target.value(),
635 static_cast<Scalar>(prod_controls.bhp_limit));
636 solveWellWithBhp(simulator, dt, bhp, groupStateHelper, well_state);
637 this->operability_status_.use_vfpexplicit = use_vfpexplicit;
638 }
639 }
640 // solve well-equation
641 converged = this->iterateWellEqWithSwitching(
642 simulator, dt, inj_controls, prod_controls, groupStateHelper, well_state,
643 /*fixed_control=*/false, /*fixed_status=*/false, /*solving_with_zero_rate=*/false
644 );
645
646
647 const bool isThp = ws.production_cmode == Well::ProducerCMode::THP;
648 // check stability of solution under thp-control
649 if (converged && !stoppedOrZeroRateTarget(groupStateHelper) && isThp) {
650 auto rates = well_state.well(this->index_of_well_).surface_rates;
651 this->adaptRatesForVFP(rates);
652 this->updateIPRImplicit(simulator, groupStateHelper, well_state);
653 bool is_stable = WellBhpThpCalculator(*this).isStableSolution(well_state, this->well_ecl_, rates, summary_state);
654 if (!is_stable) {
655 // solution converged to an unstable point!
656 this->operability_status_.use_vfpexplicit = true;
657 auto bhp_stable = WellBhpThpCalculator(*this).estimateStableBhp(well_state, this->well_ecl_, rates, this->getRefDensity(), summary_state);
658 // if we find an intersection with a sufficiently lower bhp, re-solve equations
659 const Scalar reltol = 1e-3;
660 const Scalar cur_bhp = ws.bhp;
661 if (bhp_stable.has_value() && cur_bhp - bhp_stable.value() > cur_bhp*reltol){
662 const auto msg = fmt::format("Well {} converged to an unstable solution, re-solving", this->name());
663 deferred_logger.debug(msg);
664 solveWellWithBhp(
665 simulator, dt, bhp_stable.value(), groupStateHelper, well_state
666 );
667 // re-solve with hopefully good initial guess
668 ws.thp = this->getTHPConstraint(summary_state);
669 converged = this->iterateWellEqWithSwitching(
670 simulator, dt, inj_controls, prod_controls, groupStateHelper, well_state,
671 /*fixed_control=*/false, /*fixed_status=*/false, /*solving_with_zero_rate=*/false
672 );
673 }
674 }
675 }
676
677 if (!converged) {
678 // Well did not converge, switch to explicit fractions
679 this->operability_status_.use_vfpexplicit = true;
680 this->openWell();
681 auto bhp_target = estimateOperableBhp(
682 simulator, dt, groupStateHelper, summary_state, well_state
683 );
684 if (!bhp_target.has_value()) {
685 // solve with zero rate
686 // well can't operate using explicit fractions stop the well
687 converged = solveWellWithZeroRate(simulator, dt, groupStateHelper, well_state);
688 this->stopWell();
689 this->operability_status_.can_obtain_bhp_with_thp_limit = false;
690 this->operability_status_.obey_thp_limit_under_bhp_limit = false;
691 return converged;
692 } else {
693 // solve well with the estimated target bhp (or limit)
694 const Scalar bhp = std::max(bhp_target.value(),
695 static_cast<Scalar>(prod_controls.bhp_limit));
696 solveWellWithBhp(
697 simulator, dt, bhp, groupStateHelper, well_state
698 );
699 ws.thp = this->getTHPConstraint(summary_state);
700 const auto msg = fmt::format("Well {} did not converge, re-solving with explicit fractions for VFP caculations.", this->name());
701 deferred_logger.debug(msg);
702 converged = this->iterateWellEqWithSwitching(simulator, dt,
703 inj_controls,
704 prod_controls,
705 groupStateHelper,
706 well_state,
707 /*fixed_control=*/false,
708 /*fixed_status=*/false,
709 /*solving_with_zero_rate=*/false);
710 }
711 }
712 // update operability
713 this->operability_status_.can_obtain_bhp_with_thp_limit = !this->wellIsStopped();
714 this->operability_status_.obey_thp_limit_under_bhp_limit = !this->wellIsStopped();
715 return converged;
716 }
717
718 template<typename TypeTag>
719 std::optional<typename WellInterface<TypeTag>::Scalar>
721 estimateOperableBhp(const Simulator& simulator,
722 const double dt,
723 const GroupStateHelperType& groupStateHelper,
724 const SummaryState& summary_state,
725 WellStateType& well_state)
726 {
727 if (!this->wellHasTHPConstraints(summary_state)) {
728 const Scalar bhp_limit = WellBhpThpCalculator(*this).mostStrictBhpFromBhpLimits(summary_state);
729 const bool converged = solveWellWithBhp(
730 simulator, dt, bhp_limit, groupStateHelper, well_state
731 );
732 if (!converged || this->wellIsStopped()) {
733 return std::nullopt;
734 }
735
736 return bhp_limit;
737 }
738 OPM_TIMEFUNCTION();
739 // Given an unconverged well or closed well, estimate an operable bhp (if any)
740 // Get minimal bhp from vfp-curve
741 Scalar bhp_min = WellBhpThpCalculator(*this).calculateMinimumBhpFromThp(well_state, this->well_ecl_, summary_state, this->getRefDensity());
742 // Solve
743 const bool converged = solveWellWithBhp(
744 simulator, dt, bhp_min, groupStateHelper, well_state
745 );
746 if (!converged || this->wellIsStopped()) {
747 return std::nullopt;
748 }
749 this->updateIPRImplicit(simulator, groupStateHelper, well_state);
750 auto rates = well_state.well(this->index_of_well_).surface_rates;
751 this->adaptRatesForVFP(rates);
752 return WellBhpThpCalculator(*this).estimateStableBhp(well_state, this->well_ecl_, rates, this->getRefDensity(), summary_state);
753 }
754
755 template<typename TypeTag>
756 bool
758 solveWellWithBhp(const Simulator& simulator,
759 const double dt,
760 const Scalar bhp,
761 const GroupStateHelperType& groupStateHelper,
762 WellStateType& well_state)
763 {
764 OPM_TIMEFUNCTION();
765
766 // Solve a well using single bhp-constraint (but close if not operable under this)
767 auto group_state = GroupState<Scalar>(); // empty group
768 GroupStateHelperType groupStateHelper_copy = groupStateHelper;
769 // Ensure that groupStateHelper_copy uses the empty group state as GroupState for iterateWellEqWithSwitching()
770 // and the guard ensures that the original group state is restored at scope exit, i.e. at
771 // the end of this function.
772 auto group_guard = groupStateHelper_copy.pushGroupState(group_state);
773
774 auto inj_controls = Well::InjectionControls(0);
775 auto prod_controls = Well::ProductionControls(0);
776 auto& ws = well_state.well(this->index_of_well_);
777 auto cmode_inj = ws.injection_cmode;
778 auto cmode_prod = ws.production_cmode;
779 if (this->isInjector()) {
780 inj_controls.addControl(Well::InjectorCMode::BHP);
781 inj_controls.bhp_limit = bhp;
782 inj_controls.cmode = Well::InjectorCMode::BHP;
783 ws.injection_cmode = Well::InjectorCMode::BHP;
784 } else {
785 prod_controls.addControl(Well::ProducerCMode::BHP);
786 prod_controls.bhp_limit = bhp;
787 prod_controls.cmode = Well::ProducerCMode::BHP;
788 ws.production_cmode = Well::ProducerCMode::BHP;
789 }
790 // update well-state
791 ws.bhp = bhp;
792 // solve
793 const bool converged = this->iterateWellEqWithSwitching(
794 simulator, dt, inj_controls, prod_controls, groupStateHelper_copy,
795 well_state,
796 /*fixed_control=*/true,
797 /*fixed_status=*/false,
798 /*solving_with_zero_rate=*/false
799 );
800 ws.injection_cmode = cmode_inj;
801 ws.production_cmode = cmode_prod;
802 return converged;
803 }
804
805 template<typename TypeTag>
806 bool
808 solveWellWithZeroRate(const Simulator& simulator,
809 const double dt,
810 const GroupStateHelperType& groupStateHelper,
811 WellStateType& well_state)
812 {
813 OPM_TIMEFUNCTION();
814
815 // Solve a well as stopped with isolation (empty group state for assembly)
816 const auto well_status_orig = this->wellStatus_;
817 this->stopWell();
818
819 auto inj_controls = Well::InjectionControls(0);
820 auto prod_controls = Well::ProductionControls(0);
821
822 // Solve with well isolation - the flag "solving_with_zero_rate=true" will be passed down to
823 // assembleWellEqWithoutIterationImpl() to create an empty group state when assembling the
824 // well equations.
825 const bool converged = this->iterateWellEqWithSwitching(
826 simulator, dt, inj_controls, prod_controls,
827 groupStateHelper,
828 well_state,
829 /*fixed_control*/true,
830 /*fixed_status*/true,
831 /*solving_with_zero_rate*/true
832 );
833 this->wellStatus_ = well_status_orig;
834 return converged;
835 }
836
837 template<typename TypeTag>
838 bool
840 solveWellForTesting(const Simulator& simulator,
841 const GroupStateHelperType& groupStateHelper,
842 WellStateType& well_state)
843 {
844 OPM_TIMEFUNCTION();
845 auto& deferred_logger = groupStateHelper.deferredLogger();
846
847 const double dt = simulator.timeStepSize();
848
849 const auto& summary_state = simulator.vanguard().summaryState();
850 auto inj_controls = this->well_ecl_.isInjector() ? this->well_ecl_.injectionControls(summary_state) : Well::InjectionControls(0);
851 auto prod_controls = this->well_ecl_.isProducer() ? this->well_ecl_.productionControls(summary_state) : Well::ProductionControls(0);
852 this->onlyKeepBHPandTHPcontrols(summary_state, well_state, inj_controls, prod_controls);
853
854 bool converged = false;
855 try {
856 // TODO: the following two functions will be refactored to be one to reduce the code duplication
857 if (!this->param_.local_well_solver_control_switching_){
858 converged = this->iterateWellEqWithControl(
859 simulator, dt, inj_controls, prod_controls, groupStateHelper, well_state
860 );
861 } else {
862 if (this->param_.use_implicit_ipr_ && this->well_ecl_.isProducer() && (well_state.well(this->index_of_well_).status == WellStatus::OPEN)) {
863 converged = this->solveWellWithOperabilityCheck(
864 simulator, dt, inj_controls, prod_controls, groupStateHelper, well_state
865 );
866 } else {
867 converged = this->iterateWellEqWithSwitching(
868 simulator, dt, inj_controls, prod_controls, groupStateHelper, well_state,
869 /*fixed_control=*/false,
870 /*fixed_status=*/false,
871 /*solving_with_zero_rate=*/false
872 );
873 }
874 }
875
876 } catch (NumericalProblem& e ) {
877 const std::string msg = "Inner well iterations failed for well " + this->name() + " Treat the well as unconverged. ";
878 deferred_logger.warning("INNER_ITERATION_FAILED", msg);
879 converged = false;
880 }
881
882 if (converged) {
883 deferred_logger.debug("WellTest: Well equation for well " + this->name() + " converged");
884 return true;
885 }
886 const int max_iter = this->param_.max_welleq_iter_;
887 deferred_logger.debug("WellTest: Well equation for well " + this->name() + " failed converging in "
888 + std::to_string(max_iter) + " iterations");
889 return false;
890 }
891
892
893 template<typename TypeTag>
894 void
896 solveWellEquation(const Simulator& simulator,
897 const GroupStateHelperType& groupStateHelper,
898 WellStateType& well_state)
899 {
900 OPM_TIMEFUNCTION();
901 auto& deferred_logger = groupStateHelper.deferredLogger();
902 if (!this->isOperableAndSolvable() && !this->wellIsStopped())
903 return;
904
905 // keep a copy of the original well state
906 const WellStateType well_state0 = well_state;
907 const double dt = simulator.timeStepSize();
908 bool converged = iterateWellEquations(simulator, dt, groupStateHelper, well_state);
909
910 // Newly opened wells with THP control sometimes struggles to
911 // converge due to bad initial guess. Or due to the simple fact
912 // that the well needs to change to another control.
913 // We therefore try to solve the well with BHP control to get
914 // an better initial guess.
915 // If the well is supposed to operate under THP control
916 // "updateWellControl" will switch it back to THP later.
917 if (!converged) {
918 auto& ws = well_state.well(this->indexOfWell());
919 bool thp_control = false;
920 if (this->well_ecl_.isInjector()) {
921 thp_control = ws.injection_cmode == Well::InjectorCMode::THP;
922 if (thp_control) {
923 ws.injection_cmode = Well::InjectorCMode::BHP;
924 if (this->well_control_log_.empty()) { // only log the first control
925 this->well_control_log_.push_back(WellInjectorCMode2String(Well::InjectorCMode::THP));
926 }
927 }
928 } else {
929 thp_control = ws.production_cmode == Well::ProducerCMode::THP;
930 if (thp_control) {
931 ws.production_cmode = Well::ProducerCMode::BHP;
932 if (this->well_control_log_.empty()) { // only log the first control
933 this->well_control_log_.push_back(WellProducerCMode2String(Well::ProducerCMode::THP));
934 }
935 }
936 }
937 if (thp_control) {
938 const std::string msg = std::string("The newly opened well ") + this->name()
939 + std::string(" with THP control did not converge during inner iterations, we try again with bhp control");
940 deferred_logger.debug(msg);
941 converged = this->iterateWellEquations(simulator, dt, groupStateHelper, well_state);
942 }
943 }
944
945 if (!converged) {
946 const int max_iter = this->param_.max_welleq_iter_;
947 deferred_logger.debug("Compute initial well solution for well " + this->name() + ". Failed to converge in "
948 + std::to_string(max_iter) + " iterations");
949 well_state = well_state0;
950 }
951 }
952
953
954
955 template <typename TypeTag>
956 void
958 assembleWellEq(const Simulator& simulator,
959 const double dt,
960 const GroupStateHelperType& groupStateHelper,
961 WellStateType& well_state)
962 {
963 OPM_TIMEFUNCTION();
964 prepareWellBeforeAssembling(simulator, dt, groupStateHelper, well_state);
965 assembleWellEqWithoutIteration(simulator, groupStateHelper, dt, well_state,
966 /*solving_with_zero_rate=*/false);
967 }
968
969
970
971 template <typename TypeTag>
972 void
975 const GroupStateHelperType& groupStateHelper,
976 const double dt,
977 WellStateType& well_state,
978 const bool solving_with_zero_rate)
979 {
980 OPM_TIMEFUNCTION();
981 const auto& summary_state = simulator.vanguard().summaryState();
982 const auto inj_controls = this->well_ecl_.isInjector() ? this->well_ecl_.injectionControls(summary_state) : Well::InjectionControls(0);
983 const auto prod_controls = this->well_ecl_.isProducer() ? this->well_ecl_.productionControls(summary_state) : Well::ProductionControls(0);
984 // TODO: the reason to have inj_controls and prod_controls in the arguments, is that we want to change the control used for the well functions
985 // TODO: maybe we can use std::optional or pointers to simplify here
986 assembleWellEqWithoutIteration(simulator, groupStateHelper, dt, inj_controls, prod_controls, well_state, solving_with_zero_rate);
987 }
988
989
990
991 template<typename TypeTag>
992 void
995 const double dt,
996 const GroupStateHelperType& groupStateHelper,
997 WellStateType& well_state)
998 {
999 OPM_TIMEFUNCTION();
1000 auto& deferred_logger = groupStateHelper.deferredLogger();
1001 const bool old_well_operable = this->operability_status_.isOperableAndSolvable();
1002
1003 if (this->param_.check_well_operability_iter_)
1004 checkWellOperability(simulator, well_state, groupStateHelper);
1005
1006 // only use inner well iterations for the first newton iterations.
1007 const auto& iterCtx = simulator.problem().iterationContext();
1008 if (iterCtx.shouldRunInnerWellIterations(this->param_.max_niter_inner_well_iter_)) {
1009 const auto& ws = well_state.well(this->indexOfWell());
1010 const bool nonzero_rate_original =
1011 std::any_of(ws.surface_rates.begin(),
1012 ws.surface_rates.begin() + well_state.numPhases(),
1013 [](Scalar rate) { return rate != Scalar(0.0); });
1014
1015 this->operability_status_.solvable = true;
1016 if (number_of_well_reopenings_ >= this->param_.max_well_status_switch_) {
1017 // only output the first time
1018 if (number_of_well_reopenings_ == this->param_.max_well_status_switch_) {
1019 const std::string msg = fmt::format("well {} is oscillating between open and stop. \n"
1020 "We don't allow for more than {} re-openings "
1021 "and the well is therefore kept stopped.",
1022 this->name(), number_of_well_reopenings_);
1023 deferred_logger.debug(msg);
1024 }
1025 this->stopWell();
1026 changed_to_stopped_this_step_ = true;
1027 bool converged_zero_rate = this->solveWellWithZeroRate(
1028 simulator, dt, groupStateHelper, well_state
1029 );
1030 if (this->param_.shut_unsolvable_wells_ && !converged_zero_rate ) {
1031 this->operability_status_.solvable = false;
1032 }
1033 // we increse the number of reopenings to avoid output in the next iteration
1034 number_of_well_reopenings_++;
1035 return;
1036 }
1037 bool converged = this->iterateWellEquations(
1038 simulator, dt, groupStateHelper, well_state
1039 );
1040
1041 if (converged) {
1042 const bool zero_target = this->wellUnderZeroRateTarget(groupStateHelper);
1043 if (this->wellIsStopped() && !zero_target && nonzero_rate_original) {
1044 // Well had non-zero rate, but was stopped during local well-solve. We re-open the well
1045 // for the next global iteration, but if the zero rate persists, it will be stopped.
1046 // This logic is introduced to prevent/ameliorate stopped/revived oscillations
1047 this->operability_status_.resetOperability();
1048 this->openWell();
1049 deferred_logger.debug(" " + this->name() + " is re-opened after being stopped during local solve");
1050 number_of_well_reopenings_++;
1051 }
1052 } else {
1053 // unsolvable wells are treated as not operable and will not be solved for in this iteration.
1054 if (this->param_.shut_unsolvable_wells_) {
1055 this->operability_status_.solvable = false;
1056 }
1057 }
1058 }
1059 if (this->operability_status_.has_negative_potentials) {
1060 auto well_state_copy = well_state;
1061 std::vector<Scalar> potentials;
1062 try {
1063 computeWellPotentials(simulator, well_state_copy, groupStateHelper, potentials);
1064 } catch (const std::exception& e) {
1065 const std::string msg = fmt::format("well {}: computeWellPotentials() failed "
1066 "during attempt to recompute potentials for well: ",
1067 this->name(), e.what());
1068 deferred_logger.info(msg);
1069 this->operability_status_.has_negative_potentials = true;
1070 }
1071 auto& ws = well_state.well(this->indexOfWell());
1072 const int np = well_state.numPhases();
1073 for (int p = 0; p < np; ++p) {
1074 ws.well_potentials[p] = std::max(Scalar{0.0}, potentials[p]);
1075 }
1076 }
1077 this->changed_to_open_this_step_ = false;
1078 changed_to_stopped_this_step_ = false;
1079
1080 const bool well_operable = this->operability_status_.isOperableAndSolvable();
1081 if (!well_operable) {
1082 this->stopWell();
1083 try {
1084 this->solveWellWithZeroRate(
1085 simulator, dt, groupStateHelper, well_state
1086 );
1087 } catch (const std::exception& e) {
1088 const std::string msg = fmt::format("well {}: solveWellWithZeroRate() failed "
1089 "during attempt to solve with zero rate for well: ",
1090 this->name(), e.what());
1091 deferred_logger.info(msg);
1092 // we set the rate to zero to make sure the well dont contribute to the group rate
1093 auto& ws = well_state.well(this->indexOfWell());
1094 const int np = well_state.numPhases();
1095 for (int p = 0; p < np; ++p) {
1096 ws.surface_rates[p] = Scalar{0.0};
1097 }
1098 }
1099 if (old_well_operable) {
1100 const std::string ctx = iterCtx.inLocalSolve() ? " (NLDD domain solve)" : "";
1101 deferred_logger.debug(" well " + this->name() + " gets STOPPED during iteration" + ctx);
1102 changed_to_stopped_this_step_ = true;
1103 }
1104 } else if (well_state.isOpen(this->name())) {
1105 this->openWell();
1106 if (!old_well_operable) {
1107 const std::string ctx = iterCtx.inLocalSolve() ? " (NLDD domain solve)" : "";
1108 deferred_logger.debug(" well " + this->name() + " gets REVIVED during iteration" + ctx);
1109 this->changed_to_open_this_step_ = true;
1110 }
1111 }
1112 }
1113
1114 template<typename TypeTag>
1115 void
1116 WellInterface<TypeTag>::addCellRates(std::map<int, RateVector>& cellRates_) const
1117 {
1118 if(!this->operability_status_.solvable)
1119 return;
1120
1121 for (int perfIdx = 0; perfIdx < this->number_of_local_perforations_; ++perfIdx) {
1122 const auto cellIdx = this->cells()[perfIdx];
1123 const auto it = cellRates_.find(cellIdx);
1124 RateVector rates = (it == cellRates_.end()) ? 0.0 : it->second;
1125 for (auto i=0*RateVector::dimension; i < RateVector::dimension; ++i)
1126 {
1127 rates[i] += connectionRates_[perfIdx][i];
1128 }
1129 cellRates_.insert_or_assign(cellIdx, rates);
1130 }
1131 }
1132
1133 template<typename TypeTag>
1136 {
1137 for (int perfIdx = 0; perfIdx < this->number_of_local_perforations_; ++perfIdx) {
1138 if (this->cells()[perfIdx] == cellIdx) {
1139 const unsigned activeCompIdx = FluidSystem::canonicalToActiveCompIdx(FluidSystem::solventComponentIndex(phaseIdx));
1140 return connectionRates_[perfIdx][activeCompIdx].value();
1141 }
1142 }
1143 // this is not thread safe
1144 OPM_THROW(std::invalid_argument, "The well with name " + this->name()
1145 + " does not perforate cell " + std::to_string(cellIdx));
1146 return 0.0;
1147 }
1148
1149
1150
1151
1152 template<typename TypeTag>
1153 void
1155 checkWellOperability(const Simulator& simulator,
1156 const WellStateType& well_state,
1157 const GroupStateHelperType& groupStateHelper)
1158 {
1159 auto& deferred_logger = groupStateHelper.deferredLogger();
1160 OPM_TIMEFUNCTION();
1161 if (!this->param_.check_well_operability_) {
1162 return;
1163 }
1164
1165 if (this->wellIsStopped() && !changed_to_stopped_this_step_) {
1166 return;
1167 }
1168
1169 updateWellOperability(simulator, well_state, groupStateHelper);
1170 if (!this->operability_status_.isOperableAndSolvable()) {
1171 this->operability_status_.use_vfpexplicit = true;
1172 deferred_logger.debug("EXPLICIT_LOOKUP_VFP",
1173 "well not operable, trying with explicit vfp lookup: " + this->name());
1174 updateWellOperability(simulator, well_state, groupStateHelper);
1175 }
1176 }
1177
1178
1179
1180 template<typename TypeTag>
1181 void
1184 WellStateType& well_state,
1185 const GroupState<Scalar>& group_state,
1186 GLiftEclWells& ecl_well_map,
1187 DeferredLogger& deferred_logger)
1188 {
1189 OPM_TIMEFUNCTION();
1190 const auto& summary_state = simulator.vanguard().summaryState();
1191 const auto& well_name = this->name();
1192 if (!this->wellHasTHPConstraints(summary_state)) {
1193 const std::string msg = fmt::format("GLIFT WTEST: Well {} does not have THP constraints", well_name);
1194 deferred_logger.info(msg);
1195 return;
1196 }
1197 const auto& schedule = simulator.vanguard().schedule();
1198 const auto report_step_idx = simulator.episodeIndex();
1199 const auto& glo = schedule.glo(report_step_idx);
1200 if (!glo.has_well(well_name)) {
1201 const std::string msg = fmt::format(
1202 "GLIFT WTEST: Well {} : Gas lift not activated: "
1203 "WLIFTOPT is probably missing. Skipping.", well_name);
1204 deferred_logger.info(msg);
1205 return;
1206 }
1207 const auto& gl_well = glo.well(well_name);
1208
1209 // Use gas lift optimization to get ALQ for well test
1210 std::unique_ptr<GasLiftSingleWell> glift =
1211 initializeGliftWellTest_<GasLiftSingleWell>(simulator,
1212 well_state,
1213 group_state,
1214 ecl_well_map,
1215 deferred_logger);
1216 auto [wtest_alq, success] = glift->wellTestALQ();
1217 std::string msg;
1218 const auto& unit_system = schedule.getUnits();
1219 if (success) {
1220 well_state.well(well_name).alq_state.set(wtest_alq);
1221 msg = fmt::format(
1222 "GLIFT WTEST: Well {} : Setting ALQ to optimized value = {}",
1223 well_name, unit_system.from_si(UnitSystem::measure::gas_surface_rate, wtest_alq));
1224 }
1225 else {
1226 if (!gl_well.use_glo()) {
1227 msg = fmt::format(
1228 "GLIFT WTEST: Well {} : Gas lift optimization deactivated. Setting ALQ to WLIFTOPT item 3 = {}",
1229 well_name,
1230 unit_system.from_si(UnitSystem::measure::gas_surface_rate, well_state.well(well_name).alq_state.get()));
1231
1232 }
1233 else {
1234 msg = fmt::format(
1235 "GLIFT WTEST: Well {} : Gas lift optimization failed, no ALQ set.",
1236 well_name);
1237 }
1238 }
1239 deferred_logger.info(msg);
1240 }
1241
1242 template<typename TypeTag>
1243 void
1245 updateWellOperability(const Simulator& simulator,
1246 const WellStateType& well_state,
1247 const GroupStateHelperType& groupStateHelper)
1248 {
1249 auto& deferred_logger = groupStateHelper.deferredLogger();
1250 OPM_TIMEFUNCTION();
1251 if (this->param_.local_well_solver_control_switching_) {
1252 const bool success = updateWellOperabilityFromWellEq(simulator, groupStateHelper);
1253 if (!success) {
1254 this->operability_status_.solvable = false;
1255 deferred_logger.debug("Operability check using well equations did not converge for well "
1256 + this->name() + ". Mark the well as unsolvable." );
1257 }
1258 return;
1259 }
1260 this->operability_status_.resetOperability();
1261
1262 bool thp_controlled = this->isInjector() ? well_state.well(this->index_of_well_).injection_cmode == Well::InjectorCMode::THP:
1263 well_state.well(this->index_of_well_).production_cmode == Well::ProducerCMode::THP;
1264 bool bhp_controlled = this->isInjector() ? well_state.well(this->index_of_well_).injection_cmode == Well::InjectorCMode::BHP:
1265 well_state.well(this->index_of_well_).production_cmode == Well::ProducerCMode::BHP;
1266
1267 // Operability checking is not free
1268 // Only check wells under BHP and THP control
1269 bool check_thp = thp_controlled || this->operability_status_.thp_limit_violated_but_not_switched;
1270 if (check_thp || bhp_controlled) {
1271 updateIPR(simulator, deferred_logger);
1272 checkOperabilityUnderBHPLimit(well_state, simulator, deferred_logger);
1273 }
1274 // we do some extra checking for wells under THP control.
1275 if (check_thp) {
1276 checkOperabilityUnderTHPLimit(simulator, well_state, groupStateHelper);
1277 }
1278 }
1279
1280 template<typename TypeTag>
1281 bool
1284 const GroupStateHelperType& groupStateHelper)
1285 {
1286 OPM_TIMEFUNCTION();
1287 // only makes sense if we're using this parameter is true
1288 assert(this->param_.local_well_solver_control_switching_);
1289 this->operability_status_.resetOperability();
1290 GroupStateHelperType groupStateHelper_copy = groupStateHelper;
1291 WellStateType well_state_copy = groupStateHelper_copy.wellState();
1292 const double dt = simulator.timeStepSize();
1293 // Ensure that groupStateHelper uses well_state_copy as WellState for iterateWellEquations()
1294 // and the guard ensures that the original well state is restored at scope exit, i.e. at
1295 // the end of this function.
1296 auto guard = groupStateHelper_copy.pushWellState(well_state_copy);
1297 // equations should be converged at this stage, so only one it is needed
1298 bool converged = iterateWellEquations(simulator, dt, groupStateHelper_copy, well_state_copy);
1299 return converged;
1300 }
1301
1302 template<typename TypeTag>
1303 void
1305 scaleSegmentRatesAndPressure([[maybe_unused]] WellStateType& well_state) const
1306 {
1307 // only relevant for MSW
1308 }
1309
1310 template<typename TypeTag>
1311 void
1313 updateWellStateWithTarget(const Simulator& simulator,
1314 const GroupStateHelperType& groupStateHelper,
1315 WellStateType& well_state) const
1316 {
1317 OPM_TIMEFUNCTION();
1318 auto& deferred_logger = groupStateHelper.deferredLogger();
1319 // only bhp and wellRates are used to initilize the primaryvariables for standard wells
1320 const auto& well = this->well_ecl_;
1321 const int well_index = this->index_of_well_;
1322 auto& ws = well_state.well(well_index);
1323 const int np = well_state.numPhases();
1324 const auto& summaryState = simulator.vanguard().summaryState();
1325 const auto& schedule = simulator.vanguard().schedule();
1326
1327 // Discard old primary variables, the new well state
1328 // may not be anywhere near the old one.
1329 ws.primaryvar.resize(0);
1330
1331 if (this->wellIsStopped()) {
1332 for (int p = 0; p<np; ++p) {
1333 ws.surface_rates[p] = 0;
1334 }
1335 ws.thp = 0;
1336 return;
1337 }
1338
1339 if (this->isInjector() )
1340 {
1341 const auto& controls = well.injectionControls(summaryState);
1342
1343 InjectorType injectorType = controls.injector_type;
1344 int phasePos;
1345 switch (injectorType) {
1346 case InjectorType::WATER:
1347 {
1348 phasePos = FluidSystem::canonicalToActivePhaseIdx(FluidSystem::waterPhaseIdx);
1349 break;
1350 }
1351 case InjectorType::OIL:
1352 {
1353 phasePos = FluidSystem::canonicalToActivePhaseIdx(FluidSystem::oilPhaseIdx);
1354 break;
1355 }
1356 case InjectorType::GAS:
1357 {
1358 phasePos = FluidSystem::canonicalToActivePhaseIdx(FluidSystem::gasPhaseIdx);
1359 break;
1360 }
1361 default:
1362 OPM_DEFLOG_THROW(std::runtime_error, "Expected WATER, OIL or GAS as type for injectors " + this->name(), deferred_logger );
1363 }
1364
1365 const auto current = ws.injection_cmode;
1366
1367 switch (current) {
1368 case Well::InjectorCMode::RATE:
1369 {
1370 ws.surface_rates[phasePos] = (1.0 - this->rsRvInj()) * controls.surface_rate;
1371 if(this->rsRvInj() > 0) {
1372 if (injectorType == InjectorType::OIL && FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx)) {
1373 const int gas_pos = FluidSystem::canonicalToActivePhaseIdx(FluidSystem::gasPhaseIdx);
1374 ws.surface_rates[gas_pos] = controls.surface_rate * this->rsRvInj();
1375 } else if (injectorType == InjectorType::GAS && FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx)) {
1376 const int oil_pos = FluidSystem::canonicalToActivePhaseIdx(FluidSystem::oilPhaseIdx);
1377 ws.surface_rates[oil_pos] = controls.surface_rate * this->rsRvInj();
1378 } else {
1379 OPM_DEFLOG_THROW(std::runtime_error, "Expected OIL or GAS as type for injectors when RS/RV (item 10) is non-zero " + this->name(), deferred_logger );
1380 }
1381 }
1382 break;
1383 }
1384
1385 case Well::InjectorCMode::RESV:
1386 {
1387 std::vector<Scalar> convert_coeff(this->number_of_phases_, 1.0);
1388 this->rateConverter_.calcCoeff(/*fipreg*/ 0, this->pvtRegionIdx_, convert_coeff);
1389 const Scalar coeff = convert_coeff[phasePos];
1390 ws.surface_rates[phasePos] = controls.reservoir_rate/coeff;
1391 break;
1392 }
1393
1394 case Well::InjectorCMode::THP:
1395 {
1396 auto rates = ws.surface_rates;
1397 Scalar bhp = WellBhpThpCalculator(*this).calculateBhpFromThp(well_state,
1398 rates,
1399 well,
1400 summaryState,
1401 this->getRefDensity(),
1402 deferred_logger);
1403 ws.bhp = bhp;
1404 ws.thp = this->getTHPConstraint(summaryState);
1405
1406 // if the total rates are negative or zero
1407 // we try to provide a better intial well rate
1408 // using the well potentials
1409 Scalar total_rate = std::accumulate(rates.begin(), rates.end(), 0.0);
1410 if (total_rate <= 0.0)
1411 ws.surface_rates = ws.well_potentials;
1412
1413 break;
1414 }
1415 case Well::InjectorCMode::BHP:
1416 {
1417 ws.bhp = controls.bhp_limit;
1418 Scalar total_rate = 0.0;
1419 for (int p = 0; p<np; ++p) {
1420 total_rate += ws.surface_rates[p];
1421 }
1422 // if the total rates are negative or zero
1423 // we try to provide a better intial well rate
1424 // using the well potentials
1425 if (total_rate <= 0.0)
1426 ws.surface_rates = ws.well_potentials;
1427
1428 break;
1429 }
1430 case Well::InjectorCMode::GRUP:
1431 {
1432 assert(well.isAvailableForGroupControl());
1433 const auto& group = schedule.getGroup(well.groupName(), this->currentStep());
1434 const Scalar efficiencyFactor = well.getEfficiencyFactor() *
1435 well_state[well.name()].efficiency_scaling_factor;
1436 std::optional<Scalar> target =
1437 this->getGroupInjectionTargetRate(group,
1438 groupStateHelper,
1439 injectorType,
1440 efficiencyFactor);
1441 if (target)
1442 ws.surface_rates[phasePos] = *target;
1443 break;
1444 }
1445 case Well::InjectorCMode::CMODE_UNDEFINED:
1446 {
1447 OPM_DEFLOG_THROW(std::runtime_error, "Well control must be specified for well " + this->name(), deferred_logger );
1448 }
1449
1450 }
1451 // for wells with zero injection rate, if we assign exactly zero rate,
1452 // we will have to assume some trivial composition in the wellbore.
1453 // here, we use some small value (about 0.01 m^3/day ~= 1.e-7) to initialize
1454 // the zero rate target, then we can use to retain the composition information
1455 // within the wellbore from the previous result, and hopefully it is a good
1456 // initial guess for the zero rate target.
1457 ws.surface_rates[phasePos] = std::max(Scalar{1.e-7}, ws.surface_rates[phasePos]);
1458
1459 if (ws.bhp == 0.) {
1460 ws.bhp = controls.bhp_limit;
1461 }
1462 }
1463 //Producer
1464 else
1465 {
1466 const auto current = ws.production_cmode;
1467 const auto& controls = well.productionControls(summaryState);
1468 switch (current) {
1469 case Well::ProducerCMode::ORAT:
1470 {
1471 const int oil_pos = FluidSystem::canonicalToActivePhaseIdx(FluidSystem::oilPhaseIdx);
1472 Scalar current_rate = -ws.surface_rates[oil_pos];
1473 // for trivial rates or opposite direction we don't just scale the rates
1474 // but use either the potentials or the mobility ratio to initial the well rates
1475 if (current_rate > 0.0) {
1476 for (int p = 0; p<np; ++p) {
1477 ws.surface_rates[p] *= controls.oil_rate/current_rate;
1478 }
1479 } else {
1480 const std::vector<Scalar> fractions = initialWellRateFractions(simulator, well_state);
1481 double control_fraction = fractions[oil_pos];
1482 if (control_fraction != 0.0) {
1483 for (int p = 0; p<np; ++p) {
1484 ws.surface_rates[p] = - fractions[p] * controls.oil_rate/control_fraction;
1485 }
1486 }
1487 }
1488 break;
1489 }
1490 case Well::ProducerCMode::WRAT:
1491 {
1492 const int water_pos = FluidSystem::canonicalToActivePhaseIdx(FluidSystem::waterPhaseIdx);
1493 Scalar current_rate = -ws.surface_rates[water_pos];
1494 // for trivial rates or opposite direction we don't just scale the rates
1495 // but use either the potentials or the mobility ratio to initial the well rates
1496 if (current_rate > 0.0) {
1497 for (int p = 0; p<np; ++p) {
1498 ws.surface_rates[p] *= controls.water_rate/current_rate;
1499 }
1500 } else {
1501 const std::vector<Scalar> fractions = initialWellRateFractions(simulator, well_state);
1502 const Scalar control_fraction = fractions[water_pos];
1503 if (control_fraction != 0.0) {
1504 for (int p = 0; p<np; ++p) {
1505 ws.surface_rates[p] = - fractions[p] * controls.water_rate / control_fraction;
1506 }
1507 }
1508 }
1509 break;
1510 }
1511 case Well::ProducerCMode::GRAT:
1512 {
1513 const int gas_pos = FluidSystem::canonicalToActivePhaseIdx(FluidSystem::gasPhaseIdx);
1514 Scalar current_rate = -ws.surface_rates[gas_pos];
1515 // or trivial rates or opposite direction we don't just scale the rates
1516 // but use either the potentials or the mobility ratio to initial the well rates
1517 if (current_rate > 0.0) {
1518 for (int p = 0; p<np; ++p) {
1519 ws.surface_rates[p] *= controls.gas_rate/current_rate;
1520 }
1521 } else {
1522 const std::vector<Scalar > fractions = initialWellRateFractions(simulator, well_state);
1523 const Scalar control_fraction = fractions[gas_pos];
1524 if (control_fraction != 0.0) {
1525 for (int p = 0; p<np; ++p) {
1526 ws.surface_rates[p] = - fractions[p] * controls.gas_rate / control_fraction;
1527 }
1528 }
1529 }
1530
1531 break;
1532
1533 }
1534 case Well::ProducerCMode::LRAT:
1535 {
1536 const int water_pos = FluidSystem::canonicalToActivePhaseIdx(FluidSystem::waterPhaseIdx);
1537 const int oil_pos = FluidSystem::canonicalToActivePhaseIdx(FluidSystem::oilPhaseIdx);
1538 Scalar current_rate = - ws.surface_rates[water_pos]
1539 - ws.surface_rates[oil_pos];
1540 // or trivial rates or opposite direction we don't just scale the rates
1541 // but use either the potentials or the mobility ratio to initial the well rates
1542 if (current_rate > 0.0) {
1543 for (int p = 0; p<np; ++p) {
1544 ws.surface_rates[p] *= controls.liquid_rate/current_rate;
1545 }
1546 } else {
1547 const std::vector<Scalar> fractions = initialWellRateFractions(simulator, well_state);
1548 const Scalar control_fraction = fractions[water_pos] + fractions[oil_pos];
1549 if (control_fraction != 0.0) {
1550 for (int p = 0; p<np; ++p) {
1551 ws.surface_rates[p] = - fractions[p] * controls.liquid_rate / control_fraction;
1552 }
1553 }
1554 }
1555 break;
1556 }
1557 case Well::ProducerCMode::CRAT:
1558 {
1559 OPM_DEFLOG_THROW(std::runtime_error,
1560 fmt::format("CRAT control not supported, well {}", this->name()),
1561 deferred_logger);
1562 }
1563 case Well::ProducerCMode::RESV:
1564 {
1565 std::vector<Scalar> convert_coeff(this->number_of_phases_, 1.0);
1566 this->rateConverter_.calcCoeff(/*fipreg*/ 0, this->pvtRegionIdx_, ws.surface_rates, convert_coeff);
1567 Scalar total_res_rate = 0.0;
1568 for (int p = 0; p<np; ++p) {
1569 total_res_rate -= ws.surface_rates[p] * convert_coeff[p];
1570 }
1571 if (controls.prediction_mode) {
1572 // or trivial rates or opposite direction we don't just scale the rates
1573 // but use either the potentials or the mobility ratio to initial the well rates
1574 if (total_res_rate > 0.0) {
1575 for (int p = 0; p<np; ++p) {
1576 ws.surface_rates[p] *= controls.resv_rate/total_res_rate;
1577 }
1578 } else {
1579 const std::vector<Scalar> fractions = initialWellRateFractions(simulator, well_state);
1580 for (int p = 0; p<np; ++p) {
1581 ws.surface_rates[p] = - fractions[p] * controls.resv_rate / convert_coeff[p];
1582 }
1583 }
1584 } else {
1585 std::vector<Scalar> hrates(this->number_of_phases_,0.);
1586 if (FluidSystem::phaseIsActive(FluidSystem::waterPhaseIdx)) {
1587 const int phase_pos = FluidSystem::canonicalToActivePhaseIdx(FluidSystem::waterPhaseIdx);
1588 hrates[phase_pos] = controls.water_rate;
1589 }
1590 if (FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx)) {
1591 const int phase_pos = FluidSystem::canonicalToActivePhaseIdx(FluidSystem::oilPhaseIdx);
1592 hrates[phase_pos] = controls.oil_rate;
1593 }
1594 if (FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx)) {
1595 const int phase_pos = FluidSystem::canonicalToActivePhaseIdx(FluidSystem::gasPhaseIdx);
1596 hrates[phase_pos] = controls.gas_rate;
1597 }
1598 std::vector<Scalar> hrates_resv(this->number_of_phases_,0.);
1599 this->rateConverter_.calcReservoirVoidageRates(/*fipreg*/ 0, this->pvtRegionIdx_, hrates, hrates_resv);
1600 Scalar target = std::accumulate(hrates_resv.begin(), hrates_resv.end(), 0.0);
1601 // or trivial rates or opposite direction we don't just scale the rates
1602 // but use either the potentials or the mobility ratio to initial the well rates
1603 if (total_res_rate > 0.0) {
1604 for (int p = 0; p<np; ++p) {
1605 ws.surface_rates[p] *= target/total_res_rate;
1606 }
1607 } else {
1608 const std::vector<Scalar> fractions = initialWellRateFractions(simulator, well_state);
1609 for (int p = 0; p<np; ++p) {
1610 ws.surface_rates[p] = - fractions[p] * target / convert_coeff[p];
1611 }
1612 }
1613 }
1614 break;
1615 }
1616 case Well::ProducerCMode::BHP:
1617 {
1618 ws.bhp = controls.bhp_limit;
1619 Scalar total_rate = 0.0;
1620 for (int p = 0; p<np; ++p) {
1621 total_rate -= ws.surface_rates[p];
1622 }
1623 // if the total rates are negative or zero
1624 // we try to provide a better intial well rate
1625 // using the well potentials
1626 if (total_rate <= 0.0){
1627 for (int p = 0; p<np; ++p) {
1628 ws.surface_rates[p] = -ws.well_potentials[p];
1629 }
1630 }
1631 break;
1632 }
1633 case Well::ProducerCMode::THP:
1634 {
1635 const bool update_success = updateWellStateWithTHPTargetProd(simulator, well_state, groupStateHelper);
1636
1637 if (!update_success) {
1638 // the following is the original way of initializing well state with THP constraint
1639 // keeping it for robust reason in case that it fails to get a bhp value with THP constraint
1640 // more sophisticated design might be needed in the future
1641 auto rates = ws.surface_rates;
1642 this->adaptRatesForVFP(rates);
1644 well_state, rates, well, summaryState, this->getRefDensity(), deferred_logger);
1645 ws.bhp = bhp;
1646 ws.thp = this->getTHPConstraint(summaryState);
1647 // if the total rates are negative or zero
1648 // we try to provide a better initial well rate
1649 // using the well potentials
1650 const Scalar total_rate = -std::accumulate(rates.begin(), rates.end(), 0.0);
1651 if (total_rate <= 0.0) {
1652 for (int p = 0; p < this->number_of_phases_; ++p) {
1653 ws.surface_rates[p] = -ws.well_potentials[p];
1654 }
1655 }
1656 }
1657 break;
1658 }
1659 case Well::ProducerCMode::GRUP:
1660 {
1661 assert(well.isAvailableForGroupControl());
1662 const auto& group = schedule.getGroup(well.groupName(), this->currentStep());
1663 const Scalar efficiencyFactor = well.getEfficiencyFactor() *
1664 well_state[well.name()].efficiency_scaling_factor;
1665 Scalar scale = this->getGroupProductionTargetRate(group,
1666 groupStateHelper,
1667 efficiencyFactor);
1668
1669 // we don't want to scale with zero and get zero rates.
1670 if (scale > 0) {
1671 for (int p = 0; p<np; ++p) {
1672 ws.surface_rates[p] *= scale;
1673 }
1674 ws.trivial_group_target = false;
1675 } else {
1676 // If group target is trivial we dont want to flip to other controls. To avoid oscillation we store
1677 // this information in the well state and explicitly check for this condition when evaluating well controls.
1678 ws.trivial_group_target = true;
1679 }
1680 break;
1681 }
1682 case Well::ProducerCMode::CMODE_UNDEFINED:
1684 {
1685 OPM_DEFLOG_THROW(std::runtime_error, "Well control must be specified for well " + this->name() , deferred_logger);
1686 break;
1687 }
1688 } // end of switch
1689
1690 if (ws.bhp == 0.) {
1691 ws.bhp = controls.bhp_limit;
1692 }
1693 }
1694 }
1695
1696 template<typename TypeTag>
1697 bool
1699 wellUnderZeroRateTarget(const GroupStateHelperType& groupStateHelper) const
1700 {
1701 OPM_TIMEFUNCTION();
1702 const auto& well_state = groupStateHelper.wellState();
1703 // Check if well is under zero rate control, either directly or from group
1704 const bool isGroupControlled = this->wellUnderGroupControl(well_state.well(this->index_of_well_));
1705 if (!isGroupControlled) {
1706 // well is not under group control, check "individual" version
1707 const auto& summaryState = groupStateHelper.summaryState();
1708 return this->wellUnderZeroRateTargetIndividual(summaryState, well_state);
1709 } else {
1710 return this->wellUnderZeroGroupRateTarget(groupStateHelper, isGroupControlled);
1711 }
1712 }
1713
1714 template <typename TypeTag>
1715 bool
1717 const std::optional<bool> group_control) const
1718 {
1719 const auto& well_state = groupStateHelper.wellState();
1720 // Check if well is under zero rate target from group
1721 const bool isGroupControlled = group_control.value_or(this->wellUnderGroupControl(well_state.well(this->index_of_well_)));
1722 if (isGroupControlled) {
1723 return this->zeroGroupRateTarget(groupStateHelper);
1724 }
1725 return false;
1726 }
1727
1728 template<typename TypeTag>
1729 bool
1731 stoppedOrZeroRateTarget(const GroupStateHelperType& groupStateHelper) const
1732 {
1733 // Check if well is stopped or under zero rate control, either
1734 // directly or from group.
1735 return this->wellIsStopped()
1736 || this->wellUnderZeroRateTarget(groupStateHelper);
1737 }
1738
1739 template<typename TypeTag>
1740 std::vector<typename WellInterface<TypeTag>::Scalar>
1742 initialWellRateFractions(const Simulator& simulator,
1743 const WellStateType& well_state) const
1744 {
1745 OPM_TIMEFUNCTION();
1746 const int np = this->number_of_phases_;
1747 std::vector<Scalar> scaling_factor(np);
1748 const auto& ws = well_state.well(this->index_of_well_);
1749
1750 Scalar total_potentials = 0.0;
1751 for (int p = 0; p<np; ++p) {
1752 total_potentials += ws.well_potentials[p];
1753 }
1754 if (total_potentials > 0) {
1755 for (int p = 0; p<np; ++p) {
1756 scaling_factor[p] = ws.well_potentials[p] / total_potentials;
1757 }
1758 return scaling_factor;
1759 }
1760 // if we don't have any potentials we weight it using the mobilites
1761 // We only need approximation so we don't bother with the vapporized oil and dissolved gas
1762 Scalar total_tw = 0;
1763 const int nperf = this->number_of_local_perforations_;
1764 for (int perf = 0; perf < nperf; ++perf) {
1765 total_tw += this->well_index_[perf];
1766 }
1767 total_tw = this->parallelWellInfo().communication().sum(total_tw);
1768
1769 for (int perf = 0; perf < nperf; ++perf) {
1770 const int cell_idx = this->well_cells_[perf];
1771 const auto& intQuants = simulator.model().intensiveQuantities(cell_idx, /*timeIdx=*/0);
1772 const auto& fs = intQuants.fluidState();
1773 const Scalar well_tw_fraction = this->well_index_[perf] / total_tw;
1774 Scalar total_mobility = 0.0;
1775 for (int p = 0; p < np; ++p) {
1776 const int canonical_phase_idx = FluidSystem::activeToCanonicalPhaseIdx(p);
1777 total_mobility += fs.invB(canonical_phase_idx).value() * intQuants.mobility(canonical_phase_idx).value();
1778 }
1779 for (int p = 0; p < np; ++p) {
1780 const int canonical_phase_idx = FluidSystem::activeToCanonicalPhaseIdx(p);
1781 scaling_factor[p] += well_tw_fraction * fs.invB(canonical_phase_idx).value() * intQuants.mobility(canonical_phase_idx).value() / total_mobility;
1782 }
1783 }
1784 return scaling_factor;
1785 }
1786
1787
1788
1789 template <typename TypeTag>
1790 void
1793 WellStateType& well_state,
1794 DeferredLogger& deferred_logger) const
1795 {
1796 assert(this->isProducer());
1797 OPM_TIMEFUNCTION();
1798 // Check if the rates of this well only are single-phase, do nothing
1799 // if more than one nonzero rate.
1800 auto& ws = well_state.well(this->index_of_well_);
1801 int nonzero_rate_index = -1;
1802 const Scalar floating_point_error_epsilon = 1e-14;
1803 for (int p = 0; p < this->number_of_phases_; ++p) {
1804 if (std::abs(ws.surface_rates[p]) > floating_point_error_epsilon) {
1805 if (nonzero_rate_index == -1) {
1806 nonzero_rate_index = p;
1807 } else {
1808 // More than one nonzero rate.
1809 return;
1810 }
1811 }
1812 }
1813
1814 // Calculate rates at bhp limit, or 1 bar if no limit.
1815 std::vector<Scalar> well_q_s(this->number_of_phases_, 0.0);
1816 bool rates_evaluated_at_1bar = false;
1817 {
1818 const auto& summary_state = simulator.vanguard().summaryState();
1819 const auto& prod_controls = this->well_ecl_.productionControls(summary_state);
1820 const double bhp_limit = std::max(prod_controls.bhp_limit, 1.0 * unit::barsa);
1821 this->computeWellRatesWithBhp(simulator, bhp_limit, well_q_s, deferred_logger);
1822 // Remember of we evaluated the rates at (approx.) 1 bar or not.
1823 rates_evaluated_at_1bar = (bhp_limit < 1.1 * unit::barsa);
1824 // Check that no rates are positive.
1825 if (std::ranges::any_of(well_q_s, [](Scalar q) { return q > 0.0; })) {
1826 // Did we evaluate at 1 bar? If not, then we can try again at 1 bar.
1827 if (!rates_evaluated_at_1bar) {
1828 this->computeWellRatesWithBhp(simulator, 1.0 * unit::barsa, well_q_s, deferred_logger);
1829 rates_evaluated_at_1bar = true;
1830 }
1831 // At this point we can only set the wrong-direction (if any) values to zero.
1832 for (auto& q : well_q_s) {
1833 q = std::min(q, Scalar{0.0});
1834 }
1835 }
1836 }
1837
1838 if (nonzero_rate_index == -1) {
1839 // No nonzero rates on input.
1840 // Use the computed rate directly, or scaled by a factor
1841 // 0.5 (to avoid too high values) if it was evaluated at 1 bar.
1842 const Scalar factor = rates_evaluated_at_1bar ? 0.5 : 1.0;
1843 for (int p = 0; p < this->number_of_phases_; ++p) {
1844 ws.surface_rates[p] = factor * well_q_s[p];
1845 }
1846 return;
1847 }
1848
1849 // If we are here, we had a single nonzero rate for the well,
1850 // typically from a rate constraint. We must make sure it is
1851 // respected, so if it was lower than the calculated rate for
1852 // the same phase we scale all rates to match.
1853 const Scalar initial_nonzero_rate = ws.surface_rates[nonzero_rate_index];
1854 const Scalar computed_rate = well_q_s[nonzero_rate_index];
1855 if (std::abs(initial_nonzero_rate) < std::abs(computed_rate)) {
1856 // Note that both rates below are negative. The factor should be < 1.0.
1857 const Scalar factor = initial_nonzero_rate / computed_rate;
1858 assert(factor < 1.0);
1859 for (int p = 0; p < this->number_of_phases_; ++p) {
1860 // We skip the nonzero_rate_index, as that should remain as it was.
1861 if (p != nonzero_rate_index) {
1862 ws.surface_rates[p] = factor * well_q_s[p];
1863 }
1864 }
1865 return;
1866 }
1867
1868 // If we are here, we had a single nonzero rate, but it was
1869 // higher than the one calculated from the bhp limit, so we
1870 // use the calculated rates.
1871 for (int p = 0; p < this->number_of_phases_; ++p) {
1872 ws.surface_rates[p] = well_q_s[p];
1873 }
1874 }
1875
1876 template <typename TypeTag>
1877 template<class Value>
1878 void
1880 getTw(std::vector<Value>& Tw,
1881 const int perf,
1882 const IntensiveQuantities& intQuants,
1883 const Value& trans_mult,
1884 const SingleWellStateType& ws) const
1885 {
1886 OPM_TIMEFUNCTION_LOCAL(Subsystem::Wells);
1887 // Add a Forchheimer term to the gas phase CTF if the run uses
1888 // either of the WDFAC or the WDFACCOR keywords.
1889 if (static_cast<std::size_t>(perf) >= this->well_cells_.size()) {
1890 OPM_THROW(std::invalid_argument,"The perforation index exceeds the size of the local containers - possibly wellIndex was called with a global instead of a local perforation index!");
1891 }
1892
1893 if constexpr (! Indices::gasEnabled) {
1894 return;
1895 }
1896
1897 const auto& wdfac = this->well_ecl_.getWDFAC();
1898
1899 if (! wdfac.useDFactor() || (this->well_index_[perf] == 0.0)) {
1900 return;
1901 }
1902
1903 const Scalar d = this->computeConnectionDFactor(perf, intQuants, ws);
1904 if (d < 1.0e-15) {
1905 return;
1906 }
1907
1908 // Solve quadratic equations for connection rates satisfying the ipr and the flow-dependent skin.
1909 // If more than one solution, pick the one corresponding to lowest absolute rate (smallest skin).
1910 const auto& connection = this->well_ecl_.getConnections()[ws.perf_data.ecl_index[perf]];
1911 const Scalar Kh = connection.Kh();
1912 const Scalar scaling = std::numbers::pi * Kh * connection.wpimult();
1913 const unsigned gas_comp_idx = FluidSystem::canonicalToActiveCompIdx(FluidSystem::gasCompIdx);
1914
1915 const Scalar connection_pressure = ws.perf_data.pressure[perf];
1916 const Scalar cell_pressure = getValue(intQuants.fluidState().pressure(FluidSystem::gasPhaseIdx));
1917 const Scalar drawdown = cell_pressure - connection_pressure;
1918 const Scalar invB = getValue(intQuants.fluidState().invB(FluidSystem::gasPhaseIdx));
1919 const Scalar mob_g = getValue(intQuants.mobility(FluidSystem::gasPhaseIdx)) * invB;
1920 const Scalar a = d;
1921 const Scalar b = 2 * scaling / getValue(Tw[gas_comp_idx]);
1922 const Scalar c = -2 * scaling * mob_g * drawdown;
1923
1924 Scalar consistent_Q = -1.0e20;
1925 // Find and check negative solutions (a --> -a)
1926 const Scalar r2n = b*b + 4*a*c;
1927 if (r2n >= 0) {
1928 const Scalar rn = std::sqrt(r2n);
1929 const Scalar xn1 = (b-rn)*0.5/a;
1930 if (xn1 <= 0) {
1931 consistent_Q = xn1;
1932 }
1933 const Scalar xn2 = (b+rn)*0.5/a;
1934 if (xn2 <= 0 && xn2 > consistent_Q) {
1935 consistent_Q = xn2;
1936 }
1937 }
1938 // Find and check positive solutions
1939 consistent_Q *= -1;
1940 const Scalar r2p = b*b - 4*a*c;
1941 if (r2p >= 0) {
1942 const Scalar rp = std::sqrt(r2p);
1943 const Scalar xp1 = (rp-b)*0.5/a;
1944 if (xp1 > 0 && xp1 < consistent_Q) {
1945 consistent_Q = xp1;
1946 }
1947 const Scalar xp2 = -(rp+b)*0.5/a;
1948 if (xp2 > 0 && xp2 < consistent_Q) {
1949 consistent_Q = xp2;
1950 }
1951 }
1952 Tw[gas_comp_idx] = 1.0 / (1.0 / (trans_mult * this->well_index_[perf]) + (consistent_Q/2 * d / scaling));
1953 }
1954
1955 template <typename TypeTag>
1956 void
1958 updateConnectionDFactor(const Simulator& simulator,
1959 SingleWellStateType& ws) const
1960 {
1961 if (! this->well_ecl_.getWDFAC().useDFactor()) {
1962 return;
1963 }
1964
1965 auto& d_factor = ws.perf_data.connection_d_factor;
1966
1967 for (int perf = 0; perf < this->number_of_local_perforations_; ++perf) {
1968 const int cell_idx = this->well_cells_[perf];
1969 const auto& intQuants = simulator.model().intensiveQuantities(cell_idx, /*timeIdx=*/ 0);
1970
1971 d_factor[perf] = this->computeConnectionDFactor(perf, intQuants, ws);
1972 }
1973 }
1974
1975 template <typename TypeTag>
1978 computeConnectionDFactor(const int perf,
1979 const IntensiveQuantities& intQuants,
1980 const SingleWellStateType& ws) const
1981 {
1982 auto rhoGS = [regIdx = this->pvtRegionIdx()]() {
1983 return FluidSystem::referenceDensity(FluidSystem::gasPhaseIdx, regIdx);
1984 };
1985
1986 // Viscosity is evaluated at connection pressure.
1987 auto gas_visc = [connection_pressure = ws.perf_data.pressure[perf],
1988 temperature = ws.temperature,
1989 regIdx = this->pvtRegionIdx(), &intQuants]()
1990 {
1991 const auto rv = getValue(intQuants.fluidState().Rv());
1992
1993 const auto& gasPvt = FluidSystem::gasPvt();
1994
1995 // Note that rv here is from grid block with typically
1996 // p_block > connection_pressure
1997 // so we may very well have rv > rv_sat
1998 const Scalar rv_sat = gasPvt.saturatedOilVaporizationFactor
1999 (regIdx, temperature, connection_pressure);
2000
2001 if (! (rv < rv_sat)) {
2002 return gasPvt.saturatedViscosity(regIdx, temperature,
2003 connection_pressure);
2004 }
2005
2006 return gasPvt.viscosity(regIdx, temperature, connection_pressure,
2007 rv, getValue(intQuants.fluidState().Rvw()));
2008 };
2009
2010 const auto& connection = this->well_ecl_.getConnections()
2011 [ws.perf_data.ecl_index[perf]];
2012
2013 return this->well_ecl_.getWDFAC().getDFactor(rhoGS, gas_visc, connection);
2014 }
2015
2016
2017 template <typename TypeTag>
2018 void
2021 SingleWellStateType& ws) const
2022 {
2023 auto connCF = [&connIx = std::as_const(ws.perf_data.ecl_index),
2024 &conns = this->well_ecl_.getConnections()]
2025 (const int perf)
2026 {
2027 return conns[connIx[perf]].CF();
2028 };
2029
2030 auto obtain = [](const Eval& value)
2031 {
2032 return getValue(value);
2033 };
2034
2035 auto& tmult = ws.perf_data.connection_compaction_tmult;
2036 auto& ctf = ws.perf_data.connection_transmissibility_factor;
2037
2038 for (int perf = 0; perf < this->number_of_local_perforations_; ++perf) {
2039 const int cell_idx = this->well_cells_[perf];
2040 Scalar trans_mult(0.0);
2041 getTransMult(trans_mult, simulator, cell_idx, obtain);
2042 tmult[perf] = trans_mult;
2043
2044 ctf[perf] = connCF(perf) * tmult[perf];
2045 }
2046 }
2047
2048
2049 template<typename TypeTag>
2052 {
2053 if constexpr (Indices::oilEnabled) {
2054 return fs.pressure(FluidSystem::oilPhaseIdx);
2055 } else if constexpr (Indices::gasEnabled) {
2056 return fs.pressure(FluidSystem::gasPhaseIdx);
2057 } else {
2058 return fs.pressure(FluidSystem::waterPhaseIdx);
2059 }
2060 }
2061
2062 template <typename TypeTag>
2063 template<class Value, class Callback>
2064 void
2066 getTransMult(Value& trans_mult,
2067 const Simulator& simulator,
2068 const int cell_idx,
2069 Callback& extendEval) const
2070 {
2071 const auto& intQuants = simulator.model().intensiveQuantities(cell_idx, /*timeIdx=*/ 0);
2072 trans_mult = simulator.problem().template wellTransMultiplier<Value>(intQuants, cell_idx, extendEval);
2073 }
2074
2075 template <typename TypeTag>
2076 template<class Value, class Callback>
2077 void
2079 getMobility(const Simulator& simulator,
2080 const int local_perf_index,
2081 std::vector<Value>& mob,
2082 Callback& extendEval,
2083 [[maybe_unused]] DeferredLogger& deferred_logger) const
2084 {
2085 auto relpermArray = []()
2086 {
2087 if constexpr (std::is_same_v<Value, Scalar>) {
2088 return std::array<Scalar,3>{};
2089 } else {
2090 return std::array<Eval,3>{};
2091 }
2092 };
2093 if (static_cast<std::size_t>(local_perf_index) >= this->well_cells_.size()) {
2094 OPM_THROW(std::invalid_argument,"The perforation index exceeds the size of the local containers - possibly getMobility was called with a global instead of a local perforation index!");
2095 }
2096 const int cell_idx = this->well_cells_[local_perf_index];
2097 assert (int(mob.size()) == this->num_conservation_quantities_);
2098 const auto& intQuants = simulator.model().intensiveQuantities(cell_idx, /*timeIdx=*/0);
2099 const auto& materialLawManager = simulator.problem().materialLawManager();
2100
2101 // either use mobility of the perforation cell or calculate its own
2102 // based on passing the saturation table index
2103 const int satid = this->saturation_table_number_[local_perf_index] - 1;
2104 const int satid_elem = materialLawManager->satnumRegionIdx(cell_idx);
2105 if (satid == satid_elem) { // the same saturation number is used. i.e. just use the mobilty from the cell
2106 for (unsigned phaseIdx = 0; phaseIdx < FluidSystem::numPhases; ++phaseIdx) {
2107 if (!FluidSystem::phaseIsActive(phaseIdx)) {
2108 continue;
2109 }
2110
2111 const unsigned activeCompIdx = FluidSystem::canonicalToActiveCompIdx(FluidSystem::solventComponentIndex(phaseIdx));
2112 mob[activeCompIdx] = extendEval(intQuants.mobility(phaseIdx));
2113 }
2114 if constexpr (has_solvent) {
2115 mob[Indices::contiSolventEqIdx] = extendEval(intQuants.solventMobility());
2116 }
2117 } else {
2118 const auto& paramsCell = materialLawManager->connectionMaterialLawParams(satid, cell_idx);
2119 auto relativePerms = relpermArray();
2120 MaterialLaw::relativePermeabilities(relativePerms, paramsCell, intQuants.fluidState());
2121
2122 // reset the satnumvalue back to original
2123 materialLawManager->connectionMaterialLawParams(satid_elem, cell_idx);
2124
2125 // compute the mobility
2126 for (unsigned phaseIdx = 0; phaseIdx < FluidSystem::numPhases; ++phaseIdx) {
2127 if (!FluidSystem::phaseIsActive(phaseIdx)) {
2128 continue;
2129 }
2130
2131 const unsigned activeCompIdx = FluidSystem::canonicalToActiveCompIdx(FluidSystem::solventComponentIndex(phaseIdx));
2132 mob[activeCompIdx] = extendEval(relativePerms[phaseIdx] / intQuants.fluidState().viscosity(phaseIdx));
2133 }
2134
2135 if constexpr (has_solvent) {
2136 const auto Fsolgas = intQuants.solventSaturation() / (intQuants.solventSaturation() + intQuants.fluidState().saturation(FluidSystem::gasPhaseIdx));
2137 using SolventModule = BlackOilSolventModule<TypeTag>;
2138 if (Fsolgas > SolventModule::cutOff) { // same cutoff as in the solvent model to avoid division by zero
2139 const unsigned activeGasCompIdx = FluidSystem::canonicalToActiveCompIdx(FluidSystem::solventComponentIndex(FluidSystem::gasPhaseIdx));
2140 const auto& ssfnKrg = SolventModule::ssfnKrg(satid);
2141 const auto& ssfnKrs = SolventModule::ssfnKrs(satid);
2142 mob[activeGasCompIdx] *= extendEval(ssfnKrg.eval(1-Fsolgas, /*extrapolate=*/true));
2143 mob[Indices::contiSolventEqIdx] = extendEval(ssfnKrs.eval(Fsolgas, /*extrapolate=*/true) * relativePerms[activeGasCompIdx] / intQuants.solventViscosity());
2144 }
2145 }
2146 }
2147
2148 if (this->isInjector() && !this->inj_fc_multiplier_.empty()) {
2149 const auto perf_ecl_index = this->perforationData()[local_perf_index].ecl_index;
2150 const auto& connections = this->well_ecl_.getConnections();
2151 const auto& connection = connections[perf_ecl_index];
2152 if (connection.filterCakeActive()) {
2153 std::ranges::transform(mob, mob.begin(),
2154 [mult = this->inj_fc_multiplier_[local_perf_index]]
2155 (const auto val)
2156 { return val * mult; });
2157 }
2158 }
2159 }
2160
2161
2162 template<typename TypeTag>
2163 bool
2166 WellStateType& well_state,
2167 const GroupStateHelperType& groupStateHelper) const
2168 {
2169 auto& deferred_logger = groupStateHelper.deferredLogger();
2170 OPM_TIMEFUNCTION();
2171 const auto& summary_state = simulator.vanguard().summaryState();
2172
2173 auto bhp_at_thp_limit = computeBhpAtThpLimitProdWithAlq(
2174 simulator, groupStateHelper, summary_state, this->getALQ(well_state), /*iterate_if_no_solution */ false);
2175 if (bhp_at_thp_limit) {
2176 std::vector<Scalar> rates(this->number_of_phases_, 0.0);
2177 if (thp_update_iterations) {
2178 computeWellRatesWithBhpIterations(simulator, *bhp_at_thp_limit,
2179 groupStateHelper, rates);
2180 } else {
2181 computeWellRatesWithBhp(simulator, *bhp_at_thp_limit,
2182 rates, deferred_logger);
2183 }
2184 auto& ws = well_state.well(this->name());
2185 ws.surface_rates = rates;
2186 ws.bhp = *bhp_at_thp_limit;
2187 ws.thp = this->getTHPConstraint(summary_state);
2188 return true;
2189 } else {
2190 return false;
2191 }
2192 }
2193
2194 template<typename TypeTag>
2195 std::optional<typename WellInterface<TypeTag>::Scalar>
2198 const WellStateType& well_state,
2199 Scalar bhp,
2200 const SummaryState& summary_state,
2201 const Scalar alq_value)
2202 {
2203 OPM_TIMEFUNCTION();
2204 WellStateType well_state_copy = well_state;
2205 const auto& groupStateHelper = simulator.problem().wellModel().groupStateHelper();
2206 GroupStateHelperType groupStateHelper_copy = groupStateHelper;
2207 auto well_guard = groupStateHelper_copy.pushWellState(well_state_copy);
2208 const double dt = simulator.timeStepSize();
2209 const bool converged = this->solveWellWithBhp(
2210 simulator, dt, bhp, groupStateHelper_copy, well_state_copy
2211 );
2212
2213 bool zero_rates;
2214 auto rates = well_state_copy.well(this->index_of_well_).surface_rates;
2215 zero_rates = true;
2216 for (std::size_t p = 0; p < rates.size(); ++p) {
2217 zero_rates &= rates[p] == 0.0;
2218 }
2219 // For zero rates or unconverged bhp the implicit IPR is problematic.
2220 // Use the old approach for now
2221 if (zero_rates || !converged) {
2222 return this->computeBhpAtThpLimitProdWithAlq(simulator, groupStateHelper_copy, summary_state, alq_value, /*iterate_if_no_solution */ false);
2223 }
2224 this->updateIPRImplicit(simulator, groupStateHelper_copy, well_state_copy);
2225 this->adaptRatesForVFP(rates);
2226 return WellBhpThpCalculator(*this).estimateStableBhp(well_state_copy, this->well_ecl_, rates, this->getRefDensity(), summary_state, alq_value);
2227 }
2228
2229 template <typename TypeTag>
2230 void
2233 const std::function<Scalar(const Scalar)>& connPICalc,
2234 const std::vector<Scalar>& mobility,
2235 Scalar* connPI) const
2236 {
2237 const int np = this->number_of_phases_;
2238 for (int p = 0; p < np; ++p) {
2239 // Note: E100's notion of PI value phase mobility includes
2240 // the reciprocal FVF.
2241 const int canonical_phase_idx = FluidSystem::activeToCanonicalPhaseIdx(p);
2242 const auto connMob =
2243 mobility[FluidSystem::activePhaseToActiveCompIdx(p)] * fs.invB(canonical_phase_idx).value();
2244
2245 connPI[p] = connPICalc(connMob);
2246 }
2247
2248 if (FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx) &&
2249 FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx))
2250 {
2251 const auto io = FluidSystem::canonicalToActivePhaseIdx(FluidSystem::oilPhaseIdx);
2252 const auto ig = FluidSystem::canonicalToActivePhaseIdx(FluidSystem::gasPhaseIdx);
2253
2254 const auto vapoil = connPI[ig] * fs.Rv().value();
2255 const auto disgas = connPI[io] * fs.Rs().value();
2256
2257 connPI[io] += vapoil;
2258 connPI[ig] += disgas;
2259 }
2260 }
2261
2262
2263 template <typename TypeTag>
2264 void
2267 const Phase preferred_phase,
2268 const std::function<Scalar(const Scalar)>& connIICalc,
2269 const std::vector<Scalar>& mobility,
2270 Scalar* connII,
2271 DeferredLogger& deferred_logger) const
2272 {
2273 auto phase_pos = 0;
2274 if (preferred_phase == Phase::GAS) {
2275 phase_pos = FluidSystem::canonicalToActivePhaseIdx(FluidSystem::gasPhaseIdx);
2276 }
2277 else if (preferred_phase == Phase::OIL) {
2278 phase_pos = FluidSystem::canonicalToActivePhaseIdx(FluidSystem::oilPhaseIdx);
2279 }
2280 else if (preferred_phase == Phase::WATER) {
2281 phase_pos = FluidSystem::canonicalToActivePhaseIdx(FluidSystem::waterPhaseIdx);
2282 }
2283 else {
2284 OPM_DEFLOG_THROW(NotImplemented,
2285 fmt::format("Unsupported Injector Type ({}) "
2286 "for well {} during connection I.I. calculation",
2287 static_cast<int>(preferred_phase), this->name()),
2288 deferred_logger);
2289 }
2290
2291 const auto mt = std::accumulate(mobility.begin(), mobility.end(), 0.0);
2292 const int canonicalPhaseIdx = FluidSystem::activeToCanonicalPhaseIdx(phase_pos);
2293 connII[phase_pos] = connIICalc(mt * fs.invB(canonicalPhaseIdx).value());
2294 }
2295
2296 template<typename TypeTag>
2297 template<class GasLiftSingleWell>
2298 std::unique_ptr<GasLiftSingleWell>
2300 initializeGliftWellTest_(const Simulator& simulator,
2301 WellStateType& well_state,
2302 const GroupState<Scalar>& group_state,
2303 GLiftEclWells& ecl_well_map,
2304 DeferredLogger& deferred_logger)
2305 {
2306 // Instantiate group info object (without initialization) since it is needed in GasLiftSingleWell
2307 auto& comm = simulator.vanguard().grid().comm();
2308 ecl_well_map.try_emplace(this->name(), &(this->wellEcl()), this->indexOfWell());
2309 const auto& iterCtx = simulator.problem().iterationContext();
2311 ecl_well_map,
2312 simulator.vanguard().schedule(),
2313 simulator.vanguard().summaryState(),
2314 simulator.episodeIndex(),
2315 iterCtx,
2316 deferred_logger,
2317 well_state,
2318 group_state,
2319 comm,
2320 false
2321 };
2322
2323 // Return GasLiftSingleWell object to use the wellTestALQ() function
2324 std::set<int> sync_groups;
2325 const auto& summary_state = simulator.vanguard().summaryState();
2326 return std::make_unique<GasLiftSingleWell>(*this,
2327 simulator,
2328 summary_state,
2329 deferred_logger,
2330 well_state,
2331 group_state,
2332 group_info,
2333 sync_groups,
2334 comm,
2335 false);
2336
2337 }
2338
2339} // namespace Opm
2340
2341#endif
#define OPM_DEFLOG_THROW(Exception, message, deferred_logger)
Definition: DeferredLoggingErrorHelpers.hpp:45
Contains the high level supplements required to extend the black oil model by solvents.
Definition: blackoilsolventmodules.hh:69
Definition: DeferredLogger.hpp:57
void info(const std::string &tag, const std::string &message)
Definition: GasLiftGroupInfo.hpp:47
Definition: GroupStateHelper.hpp:57
GroupState< Scalar > & groupState() const
Definition: GroupStateHelper.hpp:298
const SummaryState & summaryState() const
Definition: GroupStateHelper.hpp:415
const WellState< Scalar, IndexTraits > & wellState() const
Definition: GroupStateHelper.hpp:494
DeferredLogger & deferredLogger() const
Get the deferred logger.
Definition: GroupStateHelper.hpp:234
WellStateGuard pushWellState(WellState< Scalar, IndexTraits > &well_state)
Definition: GroupStateHelper.hpp:354
GroupStateGuard pushGroupState(GroupState< Scalar > &group_state)
Definition: GroupStateHelper.hpp:331
Definition: GroupState.hpp:41
Class encapsulating some information about parallel wells.
Definition: ParallelWellInfo.hpp:198
Definition: SingleWellState.hpp:43
Scalar temperature
Definition: SingleWellState.hpp:105
PerfData< Scalar > perf_data
Definition: SingleWellState.hpp:143
Class for computing BHP limits.
Definition: WellBhpThpCalculator.hpp:41
Scalar calculateMinimumBhpFromThp(const WellState< Scalar, IndexTraits > &well_state, const Well &well, const SummaryState &summaryState, const Scalar rho) const
Scalar mostStrictBhpFromBhpLimits(const SummaryState &summaryState) const
Obtain the most strict BHP from BHP limits.
bool isStableSolution(const WellState< Scalar, IndexTraits > &well_state, const Well &well, const std::vector< Scalar > &rates, const SummaryState &summaryState) const
EvalWell calculateBhpFromThp(const WellState< Scalar, IndexTraits > &well_state, const std::vector< EvalWell > &rates, const Well &well, const SummaryState &summaryState, const Scalar rho, DeferredLogger &deferred_logger) const
std::optional< Scalar > estimateStableBhp(const WellState< Scalar, IndexTraits > &well_state, const Well &well, const std::vector< Scalar > &rates, const Scalar rho, const SummaryState &summaryState) const
int number_of_local_perforations_
Definition: WellInterfaceGeneric.hpp:341
FluidSystem::Scalar wsolvent_
Definition: WellInterfaceGeneric.hpp:381
Definition: WellInterfaceIndices.hpp:34
bool stoppedOrZeroRateTarget(const GroupStateHelperType &groupStateHelper) const
Definition: WellInterface_impl.hpp:1731
bool updateWellOperabilityFromWellEq(const Simulator &simulator, const GroupStateHelperType &groupStateHelper)
Definition: WellInterface_impl.hpp:1283
void checkWellOperability(const Simulator &simulator, const WellStateType &well_state, const GroupStateHelperType &groupStateHelper)
Definition: WellInterface_impl.hpp:1155
void updateWellOperability(const Simulator &simulator, const WellStateType &well_state, const GroupStateHelperType &groupStateHelper)
Definition: WellInterface_impl.hpp:1245
bool solveWellWithOperabilityCheck(const Simulator &simulator, const double dt, const Well::InjectionControls &inj_controls, const Well::ProductionControls &prod_controls, const GroupStateHelperType &groupStateHelper, WellStateType &well_state)
Definition: WellInterface_impl.hpp:601
Scalar woxygen() const
Definition: WellInterface_impl.hpp:166
IndividualOrGroup
Definition: WellInterface.hpp:245
GetPropType< TypeTag, Properties::Simulator > Simulator
Definition: WellInterface.hpp:82
void assembleWellEqWithoutIteration(const Simulator &simulator, const GroupStateHelperType &groupStateHelper, const double dt, WellStateType &well_state, const bool solving_with_zero_rate)
Definition: WellInterface_impl.hpp:974
Scalar computeConnectionDFactor(const int perf, const IntensiveQuantities &intQuants, const SingleWellStateType &ws) const
Definition: WellInterface_impl.hpp:1978
typename WellInterfaceFluidSystem< FluidSystem >::RateConverterType RateConverterType
Definition: WellInterface.hpp:105
Scalar wfoam() const
Definition: WellInterface_impl.hpp:128
bool updateWellControlAndStatusLocalIteration(const Simulator &simulator, const GroupStateHelperType &groupStateHelper, const Well::InjectionControls &inj_controls, const Well::ProductionControls &prod_controls, const Scalar WQTotal, WellStateType &well_state, const bool fixed_control, const bool fixed_status, const bool solving_with_zero_rate)
Definition: WellInterface_impl.hpp:287
void getTransMult(Value &trans_mult, const Simulator &simulator, const int cell_idx, Callback &extendEval) const
Definition: WellInterface_impl.hpp:2066
std::vector< RateVector > connectionRates_
Definition: WellInterface.hpp:363
bool solveWellForTesting(const Simulator &simulator, const GroupStateHelperType &groupStateHelper, WellStateType &well_state)
Definition: WellInterface_impl.hpp:840
void computeConnLevelProdInd(const FluidState &fs, const std::function< Scalar(const Scalar)> &connPICalc, const std::vector< Scalar > &mobility, Scalar *connPI) const
Definition: WellInterface_impl.hpp:2232
void gliftBeginTimeStepWellTestUpdateALQ(const Simulator &simulator, WellStateType &well_state, const GroupState< Scalar > &group_state, GLiftEclWells &ecl_well_map, DeferredLogger &deferred_logger)
Definition: WellInterface_impl.hpp:1183
Scalar volumetricSurfaceRateForConnection(int cellIdx, int phaseIdx) const
Definition: WellInterface_impl.hpp:1135
virtual void init(const std::vector< Scalar > &depth_arg, const Scalar gravity_arg, const std::vector< Scalar > &B_avg, const bool changed_to_open_this_step)
Definition: WellInterface_impl.hpp:96
std::optional< Scalar > computeBhpAtThpLimitProdWithAlqUsingIPR(const Simulator &simulator, const WellStateType &well_state, Scalar bhp, const SummaryState &summary_state, const Scalar alq_value)
Definition: WellInterface_impl.hpp:2197
void getTw(std::vector< Value > &wi, const int perf, const IntensiveQuantities &intQuants, const Value &trans_mult, const SingleWellStateType &ws) const
Definition: WellInterface_impl.hpp:1880
void getMobility(const Simulator &simulator, const int local_perf_index, std::vector< Value > &mob, Callback &extendEval, DeferredLogger &deferred_logger) const
Definition: WellInterface_impl.hpp:2079
GetPropType< TypeTag, Properties::IntensiveQuantities > IntensiveQuantities
Definition: WellInterface.hpp:87
GetPropType< TypeTag, Properties::Scalar > Scalar
Definition: WellInterface.hpp:83
std::vector< Scalar > initialWellRateFractions(const Simulator &ebosSimulator, const WellStateType &well_state) const
Definition: WellInterface_impl.hpp:1742
void solveWellEquation(const Simulator &simulator, const GroupStateHelperType &groupStateHelper, WellStateType &well_state)
Definition: WellInterface_impl.hpp:896
void updateConnectionDFactor(const Simulator &simulator, SingleWellStateType &ws) const
Definition: WellInterface_impl.hpp:1958
Eval getPerfCellPressure(const FluidState &fs) const
Definition: WellInterface_impl.hpp:2051
void initializeProducerWellState(const Simulator &simulator, WellStateType &well_state, DeferredLogger &deferred_logger) const
Definition: WellInterface_impl.hpp:1792
virtual void updateWellStateWithTarget(const Simulator &simulator, const GroupStateHelperType &groupStateHelper, WellStateType &well_state) const
Definition: WellInterface_impl.hpp:1313
void addCellRates(std::map< int, RateVector > &cellRates_) const
Definition: WellInterface_impl.hpp:1116
typename Base::ModelParameters ModelParameters
Definition: WellInterface.hpp:111
GetPropType< TypeTag, Properties::FluidSystem > FluidSystem
Definition: WellInterface.hpp:84
static constexpr bool has_solvent
Definition: WellInterface.hpp:113
bool wellUnderZeroRateTarget(const GroupStateHelperType &groupStateHelper) const
Definition: WellInterface_impl.hpp:1699
GetPropType< TypeTag, Properties::RateVector > RateVector
Definition: WellInterface.hpp:90
void updateConnectionTransmissibilityFactor(const Simulator &simulator, SingleWellStateType &ws) const
Definition: WellInterface_impl.hpp:2020
void computeConnLevelInjInd(const FluidState &fs, const Phase preferred_phase, const std::function< Scalar(const Scalar)> &connIICalc, const std::vector< Scalar > &mobility, Scalar *connII, DeferredLogger &deferred_logger) const
Definition: WellInterface_impl.hpp:2266
typename GasLiftGroupInfo< Scalar, IndexTraits >::GLiftEclWells GLiftEclWells
Definition: WellInterface.hpp:92
std::unique_ptr< GasLiftSingleWell > initializeGliftWellTest_(const Simulator &simulator, WellStateType &well_state, const GroupState< Scalar > &group_state, GLiftEclWells &ecl_well_map, DeferredLogger &deferred_logger)
Definition: WellInterface_impl.hpp:2300
std::optional< Scalar > estimateOperableBhp(const Simulator &ebos_simulator, const double dt, const GroupStateHelperType &groupStateHelper, const SummaryState &summary_state, WellStateType &well_state)
Definition: WellInterface_impl.hpp:721
Scalar wsalt() const
Definition: WellInterface_impl.hpp:142
bool solveWellWithZeroRate(const Simulator &simulator, const double dt, const GroupStateHelperType &groupStateHelper, WellStateType &well_state)
Definition: WellInterface_impl.hpp:808
bool wellUnderZeroGroupRateTarget(const GroupStateHelperType &groupStateHelper, const std::optional< bool > group_control=std::nullopt) const
Definition: WellInterface_impl.hpp:1716
bool solveWellWithBhp(const Simulator &simulator, const double dt, const Scalar bhp, const GroupStateHelperType &groupStateHelper, WellStateType &well_state)
Definition: WellInterface_impl.hpp:758
void prepareWellBeforeAssembling(const Simulator &simulator, const double dt, const GroupStateHelperType &groupStateHelper, WellStateType &well_state)
Definition: WellInterface_impl.hpp:994
void wellTesting(const Simulator &simulator, const double simulation_time, const GroupStateHelperType &groupStateHelper, WellStateType &well_state, WellTestState &welltest_state, GLiftEclWells &ecl_well_map, std::map< std::string, double > &open_times)
Definition: WellInterface_impl.hpp:406
typename Base::Eval Eval
Definition: WellInterface.hpp:96
WellInterface(const Well &well, const ParallelWellInfo< Scalar > &pw_info, const int time_step, const ModelParameters &param, const RateConverterType &rate_converter, const int pvtRegionIdx, const int num_conservation_quantities, const int num_phases, const int index_of_well, const std::vector< PerforationData< Scalar > > &perf_data)
Constructor.
Definition: WellInterface_impl.hpp:59
bool updateWellStateWithTHPTargetProd(const Simulator &simulator, WellStateType &well_state, const GroupStateHelperType &groupStateHelper) const
Definition: WellInterface_impl.hpp:2165
bool iterateWellEquations(const Simulator &simulator, const double dt, const GroupStateHelperType &groupStateHelper, WellStateType &well_state)
Definition: WellInterface_impl.hpp:532
Scalar wpolymer() const
Definition: WellInterface_impl.hpp:112
GetPropType< TypeTag, Properties::Indices > Indices
Definition: WellInterface.hpp:86
bool updateWellControl(const Simulator &simulator, const IndividualOrGroup iog, const GroupStateHelperType &groupStateHelper, WellStateType &well_state)
Definition: WellInterface_impl.hpp:190
Scalar wurea() const
Definition: WellInterface_impl.hpp:178
void assembleWellEq(const Simulator &simulator, const double dt, const GroupStateHelperType &groupStateHelper, WellStateType &well_state)
Definition: WellInterface_impl.hpp:958
BlackOilFluidState< Eval, FluidSystem, energyModuleType !=EnergyModules::NoTemperature, energyModuleType==EnergyModules::FullyImplicitThermal, Indices::compositionSwitchIdx >=0, has_watVapor, has_brine, has_saltPrecip, has_disgas_in_water, has_solvent, Indices::numPhases > FluidState
Definition: WellInterface.hpp:140
Scalar wmicrobes() const
Definition: WellInterface_impl.hpp:154
virtual void scaleSegmentRatesAndPressure(WellStateType &well_state) const
Definition: WellInterface_impl.hpp:1305
static constexpr bool has_zFraction
Definition: WellInterface.hpp:114
Definition: WellState.hpp:66
constexpr int numPhases() const
The number of phases present.
Definition: WellState.hpp:249
const SingleWellState< Scalar, IndexTraits > & well(std::size_t well_index) const
Definition: WellState.hpp:290
bool isOpen(const std::string &name) const
Definition: WellState.hpp:197
@ NONE
Definition: DeferredLogger.hpp:46
Dune::Communication< MPIComm > Communication
Definition: ParallelCommunication.hpp:30
Phase
Phase indices for reservoir coupling, we currently only support black-oil phases (oil,...
Definition: ReservoirCoupling.hpp:156
Definition: blackoilbioeffectsmodules.hh:45
std::string to_string(const ConvergenceReport::ReservoirFailure::Type t)
Static data associated with a well perforation.
Definition: PerforationData.hpp:30