StandardWell_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 2016 - 2017 IRIS AS.
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// Improve IDE experience
23#ifndef OPM_STANDARDWELL_HEADER_INCLUDED
24#define OPM_STANDARDWELL_IMPL_HEADER_INCLUDED
25#include <config.h>
27#endif
28
29#include <opm/common/Exceptions.hpp>
30
31#include <opm/input/eclipse/Units/Units.hpp>
32
33#include <opm/material/densead/EvaluationFormat.hpp>
34
40
41#include <fmt/format.h>
42
43#include <algorithm>
44#include <cstddef>
45#include <functional>
46#include <numeric>
47
48namespace {
49
50template<class dValue, class Value>
51auto dValueError(const dValue& d,
52 const std::string& name,
53 const std::string& methodName,
54 const Value& Rs,
55 const Value& Rv,
56 const Value& pressure)
57{
58 return fmt::format("Problematic d value {} obtained for well {}"
59 " during {} calculations with rs {}"
60 ", rv {} and pressure {}."
61 " Continue as if no dissolution (rs = 0) and vaporization (rv = 0) "
62 " for this connection.", d, name, methodName, Rs, Rv, pressure);
63}
64
65}
66
67namespace Opm
68{
69
70 template<typename TypeTag>
72 StandardWell(const Well& well,
73 const ParallelWellInfo& pw_info,
74 const int time_step,
75 const ModelParameters& param,
76 const RateConverterType& rate_converter,
77 const int pvtRegionIdx,
78 const int num_components,
79 const int num_phases,
80 const int index_of_well,
81 const std::vector<PerforationData>& perf_data)
82 : Base(well, pw_info, time_step, param, rate_converter, pvtRegionIdx, num_components, num_phases, index_of_well, perf_data)
83 , StdWellEval(static_cast<const WellInterfaceIndices<FluidSystem,Indices>&>(*this))
84 , regularize_(false)
85 {
87 }
88
89
90
91
92
93 template<typename TypeTag>
94 void
96 init(const PhaseUsage* phase_usage_arg,
97 const std::vector<double>& depth_arg,
98 const double gravity_arg,
99 const int num_cells,
100 const std::vector< Scalar >& B_avg,
101 const bool changed_to_open_this_step)
102 {
103 Base::init(phase_usage_arg, depth_arg, gravity_arg, num_cells, B_avg, changed_to_open_this_step);
104 this->StdWellEval::init(this->perf_depth_, depth_arg, num_cells, Base::has_polymermw);
105 }
106
107
108
109
110
111 template<typename TypeTag>
114 {
115 this->primary_variables_.init();
116 }
117
118
119
120
121
122 template<typename TypeTag>
123 template<class Value>
124 void
127 const std::vector<Value>& mob,
128 const Value& bhp,
129 const std::vector<Scalar>& Tw,
130 const int perf,
131 const bool allow_cf,
132 std::vector<Value>& cq_s,
133 PerforationRates& perf_rates,
134 DeferredLogger& deferred_logger) const
135 {
136 auto obtain = [this](const Eval& value)
137 {
138 if constexpr (std::is_same_v<Value, Scalar>) {
139 static_cast<void>(this); // suppress clang warning
140 return getValue(value);
141 } else {
142 return this->extendEval(value);
143 }
144 };
145 auto obtainN = [](const auto& value)
146 {
147 if constexpr (std::is_same_v<Value, Scalar>) {
148 return getValue(value);
149 } else {
150 return value;
151 }
152 };
153 auto zeroElem = [this]()
154 {
155 if constexpr (std::is_same_v<Value, Scalar>) {
156 static_cast<void>(this); // suppress clang warning
157 return 0.0;
158 } else {
159 return Value{this->primary_variables_.numWellEq() + Indices::numEq, 0.0};
160 }
161 };
162
163 const auto& fs = intQuants.fluidState();
164 const Value pressure = obtain(this->getPerfCellPressure(fs));
165 const Value rs = obtain(fs.Rs());
166 const Value rv = obtain(fs.Rv());
167 const Value rvw = obtain(fs.Rvw());
168 const Value rsw = obtain(fs.Rsw());
169
170 std::vector<Value> b_perfcells_dense(this->numComponents(), zeroElem());
171 for (unsigned phaseIdx = 0; phaseIdx < FluidSystem::numPhases; ++phaseIdx) {
172 if (!FluidSystem::phaseIsActive(phaseIdx)) {
173 continue;
174 }
175 const unsigned compIdx = Indices::canonicalToActiveComponentIndex(FluidSystem::solventComponentIndex(phaseIdx));
176 b_perfcells_dense[compIdx] = obtain(fs.invB(phaseIdx));
177 }
178 if constexpr (has_solvent) {
179 b_perfcells_dense[Indices::contiSolventEqIdx] = obtain(intQuants.solventInverseFormationVolumeFactor());
180 }
181
182 if constexpr (has_zFraction) {
183 if (this->isInjector()) {
184 const unsigned gasCompIdx = Indices::canonicalToActiveComponentIndex(FluidSystem::gasCompIdx);
185 b_perfcells_dense[gasCompIdx] *= (1.0 - this->wsolvent());
186 b_perfcells_dense[gasCompIdx] += this->wsolvent()*intQuants.zPureInvFormationVolumeFactor().value();
187 }
188 }
189
190 Value skin_pressure = zeroElem();
191 if (has_polymermw) {
192 if (this->isInjector()) {
193 const int pskin_index = Bhp + 1 + this->numPerfs() + perf;
194 skin_pressure = obtainN(this->primary_variables_.eval(pskin_index));
195 }
196 }
197
198 // surface volume fraction of fluids within wellbore
199 std::vector<Value> cmix_s(this->numComponents(), zeroElem());
200 for (int componentIdx = 0; componentIdx < this->numComponents(); ++componentIdx) {
201 cmix_s[componentIdx] = obtainN(this->primary_variables_.surfaceVolumeFraction(componentIdx));
202 }
203
204 computePerfRate(mob,
205 pressure,
206 bhp,
207 rs,
208 rv,
209 rvw,
210 rsw,
211 b_perfcells_dense,
212 Tw,
213 perf,
214 allow_cf,
215 skin_pressure,
216 cmix_s,
217 cq_s,
218 perf_rates,
219 deferred_logger);
220 }
221
222 template<typename TypeTag>
223 template<class Value>
224 void
226 computePerfRate(const std::vector<Value>& mob,
227 const Value& pressure,
228 const Value& bhp,
229 const Value& rs,
230 const Value& rv,
231 const Value& rvw,
232 const Value& rsw,
233 std::vector<Value>& b_perfcells_dense,
234 const std::vector<Scalar>& Tw,
235 const int perf,
236 const bool allow_cf,
237 const Value& skin_pressure,
238 const std::vector<Value>& cmix_s,
239 std::vector<Value>& cq_s,
240 PerforationRates& perf_rates,
241 DeferredLogger& deferred_logger) const
242 {
243 // Pressure drawdown (also used to determine direction of flow)
244 const Value well_pressure = bhp + this->connections_.pressure_diff(perf);
245 Value drawdown = pressure - well_pressure;
246 if (this->isInjector()) {
247 drawdown += skin_pressure;
248 }
249
250 // producing perforations
251 if (drawdown > 0) {
252 // Do nothing if crossflow is not allowed
253 if (!allow_cf && this->isInjector()) {
254 return;
255 }
256
257 // compute component volumetric rates at standard conditions
258 for (int componentIdx = 0; componentIdx < this->numComponents(); ++componentIdx) {
259 const Value cq_p = - Tw[componentIdx] * (mob[componentIdx] * drawdown);
260 cq_s[componentIdx] = b_perfcells_dense[componentIdx] * cq_p;
261 }
262
263 if (FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx) && FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx)) {
264 gasOilPerfRateProd(cq_s, perf_rates, rv, rs, rvw);
265 } else if (FluidSystem::phaseIsActive(FluidSystem::waterPhaseIdx) && FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx)) {
266 gasWaterPerfRateProd(cq_s, perf_rates, rvw, rsw);
267 }
268 } else {
269 // Do nothing if crossflow is not allowed
270 if (!allow_cf && this->isProducer()) {
271 return;
272 }
273
274 // Using total mobilities
275 Value total_mob_dense = mob[0];
276 for (int componentIdx = 1; componentIdx < this->numComponents(); ++componentIdx) {
277 total_mob_dense += mob[componentIdx];
278 }
279
280 // compute volume ratio between connection at standard conditions
281 Value volumeRatio = bhp * 0.0; // initialize it with the correct type
282;
283 if (FluidSystem::enableVaporizedWater() && FluidSystem::enableDissolvedGasInWater()) {
284 disOilVapWatVolumeRatio(volumeRatio, rvw, rsw, pressure,
285 cmix_s, b_perfcells_dense, deferred_logger);
286 } else {
287 if (FluidSystem::phaseIsActive(FluidSystem::waterPhaseIdx)) {
288 const unsigned waterCompIdx = Indices::canonicalToActiveComponentIndex(FluidSystem::waterCompIdx);
289 volumeRatio += cmix_s[waterCompIdx] / b_perfcells_dense[waterCompIdx];
290 }
291 }
292
293 if constexpr (Indices::enableSolvent) {
294 volumeRatio += cmix_s[Indices::contiSolventEqIdx] / b_perfcells_dense[Indices::contiSolventEqIdx];
295 }
296
297 if (FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx) && FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx)) {
298 gasOilVolumeRatio(volumeRatio, rv, rs, pressure,
299 cmix_s, b_perfcells_dense, deferred_logger);
300 }
301 else {
302 if (FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx)) {
303 const unsigned oilCompIdx = Indices::canonicalToActiveComponentIndex(FluidSystem::oilCompIdx);
304 volumeRatio += cmix_s[oilCompIdx] / b_perfcells_dense[oilCompIdx];
305 }
306 if (FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx)) {
307 const unsigned gasCompIdx = Indices::canonicalToActiveComponentIndex(FluidSystem::gasCompIdx);
308 volumeRatio += cmix_s[gasCompIdx] / b_perfcells_dense[gasCompIdx];
309 }
310 }
311
312 // injecting connections total volumerates at standard conditions
313 for (int componentIdx = 0; componentIdx < this->numComponents(); ++componentIdx) {
314 const Value cqt_i = - Tw[componentIdx] * (total_mob_dense * drawdown);
315 Value cqt_is = cqt_i / volumeRatio;
316 cq_s[componentIdx] = cmix_s[componentIdx] * cqt_is;
317 }
318
319 // calculating the perforation solution gas rate and solution oil rates
320 if (this->isProducer()) {
321 if (FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx) && FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx)) {
322 gasOilPerfRateInj(cq_s, perf_rates,
323 rv, rs, pressure, rvw, deferred_logger);
324 }
325 if (FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx) && FluidSystem::phaseIsActive(FluidSystem::waterPhaseIdx)) {
326 //no oil
327 gasWaterPerfRateInj(cq_s, perf_rates, rvw, rsw,
328 pressure, deferred_logger);
329 }
330 }
331 }
332 }
333
334
335 template<typename TypeTag>
336 void
339 const double dt,
340 const Well::InjectionControls& inj_controls,
341 const Well::ProductionControls& prod_controls,
342 WellState<Scalar>& well_state,
343 const GroupState<Scalar>& group_state,
344 DeferredLogger& deferred_logger)
345 {
346 // TODO: only_wells should be put back to save some computation
347 // for example, the matrices B C does not need to update if only_wells
348 if (!this->isOperableAndSolvable() && !this->wellIsStopped()) return;
349
350 // clear all entries
351 this->linSys_.clear();
352
353 assembleWellEqWithoutIterationImpl(simulator, dt, inj_controls,
354 prod_controls, well_state,
355 group_state, deferred_logger);
356 }
357
358
359
360
361 template<typename TypeTag>
362 void
365 const double dt,
366 const Well::InjectionControls& inj_controls,
367 const Well::ProductionControls& prod_controls,
368 WellState<Scalar>& well_state,
369 const GroupState<Scalar>& group_state,
370 DeferredLogger& deferred_logger)
371 {
372 // try to regularize equation if the well does not converge
373 const Scalar regularization_factor = this->regularize_? this->param_.regularization_factor_wells_ : 1.0;
374 const double volume = 0.1 * unit::cubic(unit::feet) * regularization_factor;
375
376 auto& ws = well_state.well(this->index_of_well_);
377 ws.phase_mixing_rates.fill(0.0);
378
379
380 const int np = this->number_of_phases_;
381
382 std::vector<RateVector> connectionRates = this->connectionRates_; // Copy to get right size.
383
384 auto& perf_data = ws.perf_data;
385 auto& perf_rates = perf_data.phase_rates;
386 for (int perf = 0; perf < this->number_of_perforations_; ++perf) {
387 // Calculate perforation quantities.
388 std::vector<EvalWell> cq_s(this->num_components_, {this->primary_variables_.numWellEq() + Indices::numEq, 0.0});
389 EvalWell water_flux_s{this->primary_variables_.numWellEq() + Indices::numEq, 0.0};
390 EvalWell cq_s_zfrac_effective{this->primary_variables_.numWellEq() + Indices::numEq, 0.0};
391 calculateSinglePerf(simulator, perf, well_state, connectionRates,
392 cq_s, water_flux_s, cq_s_zfrac_effective, deferred_logger);
393
394 // Equation assembly for this perforation.
395 if constexpr (has_polymer && Base::has_polymermw) {
396 if (this->isInjector()) {
397 handleInjectivityEquations(simulator, well_state, perf,
398 water_flux_s, deferred_logger);
399 }
400 }
401 const int cell_idx = this->well_cells_[perf];
402 for (int componentIdx = 0; componentIdx < this->num_components_; ++componentIdx) {
403 // the cq_s entering mass balance equations need to consider the efficiency factors.
404 const EvalWell cq_s_effective = cq_s[componentIdx] * this->well_efficiency_factor_;
405
406 connectionRates[perf][componentIdx] = Base::restrictEval(cq_s_effective);
407
409 assemblePerforationEq(cq_s_effective,
410 componentIdx,
411 cell_idx,
412 this->primary_variables_.numWellEq(),
413 this->linSys_);
414
415 // Store the perforation phase flux for later usage.
416 if (has_solvent && componentIdx == Indices::contiSolventEqIdx) {
417 auto& perf_rate_solvent = perf_data.solvent_rates;
418 perf_rate_solvent[perf] = cq_s[componentIdx].value();
419 } else {
420 perf_rates[perf*np + this->modelCompIdxToFlowCompIdx(componentIdx)] = cq_s[componentIdx].value();
421 }
422 }
423
424 if constexpr (has_zFraction) {
426 assembleZFracEq(cq_s_zfrac_effective,
427 cell_idx,
428 this->primary_variables_.numWellEq(),
429 this->linSys_);
430 }
431 }
432 // Update the connection
433 this->connectionRates_ = connectionRates;
434
435 // Accumulate dissolved gas and vaporized oil flow rates across all
436 // ranks sharing this well (this->index_of_well_).
437 {
438 const auto& comm = this->parallel_well_info_.communication();
439 comm.sum(ws.phase_mixing_rates.data(), ws.phase_mixing_rates.size());
440 }
441
442 // accumulate resWell_ and duneD_ in parallel to get effects of all perforations (might be distributed)
443 this->linSys_.sumDistributed(this->parallel_well_info_.communication());
444
445 // add vol * dF/dt + Q to the well equations;
446 for (int componentIdx = 0; componentIdx < numWellConservationEq; ++componentIdx) {
447 // TODO: following the development in MSW, we need to convert the volume of the wellbore to be surface volume
448 // since all the rates are under surface condition
449 EvalWell resWell_loc(this->primary_variables_.numWellEq() + Indices::numEq, 0.0);
450 if (FluidSystem::numActivePhases() > 1) {
451 assert(dt > 0);
452 resWell_loc += (this->primary_variables_.surfaceVolumeFraction(componentIdx) -
453 this->F0_[componentIdx]) * volume / dt;
454 }
455 resWell_loc -= this->primary_variables_.getQs(componentIdx) * this->well_efficiency_factor_;
457 assembleSourceEq(resWell_loc,
458 componentIdx,
459 this->primary_variables_.numWellEq(),
460 this->linSys_);
461 }
462
463 const auto& summaryState = simulator.vanguard().summaryState();
464 const Schedule& schedule = simulator.vanguard().schedule();
466 assembleControlEq(well_state, group_state,
467 schedule, summaryState,
468 inj_controls, prod_controls,
469 this->primary_variables_,
470 this->connections_.rho(),
471 this->linSys_,
472 deferred_logger);
473
474
475 // do the local inversion of D.
476 try {
477 this->linSys_.invert();
478 } catch( ... ) {
479 OPM_DEFLOG_PROBLEM(NumericalProblem, "Error when inverting local well equations for well " + name(), deferred_logger);
480 }
481 }
482
483
484
485
486 template<typename TypeTag>
487 void
489 calculateSinglePerf(const Simulator& simulator,
490 const int perf,
491 WellState<Scalar>& well_state,
492 std::vector<RateVector>& connectionRates,
493 std::vector<EvalWell>& cq_s,
494 EvalWell& water_flux_s,
495 EvalWell& cq_s_zfrac_effective,
496 DeferredLogger& deferred_logger) const
497 {
498 const bool allow_cf = this->getAllowCrossFlow() || openCrossFlowAvoidSingularity(simulator);
499 const EvalWell& bhp = this->primary_variables_.eval(Bhp);
500 const int cell_idx = this->well_cells_[perf];
501 const auto& intQuants = simulator.model().intensiveQuantities(cell_idx, /*timeIdx=*/ 0);
502 std::vector<EvalWell> mob(this->num_components_, {this->primary_variables_.numWellEq() + Indices::numEq, 0.});
503 getMobility(simulator, perf, mob, deferred_logger);
504
505 PerforationRates perf_rates;
506 double trans_mult = simulator.problem().template wellTransMultiplier<double>(intQuants, cell_idx);
507 const auto& wellstate_nupcol = simulator.problem().wellModel().nupcolWellState().well(this->index_of_well_);
508 const std::vector<Scalar> Tw = this->wellIndex(perf, intQuants, trans_mult, wellstate_nupcol);
509 computePerfRate(intQuants, mob, bhp, Tw, perf, allow_cf,
510 cq_s, perf_rates, deferred_logger);
511
512 auto& ws = well_state.well(this->index_of_well_);
513 auto& perf_data = ws.perf_data;
514 if constexpr (has_polymer && Base::has_polymermw) {
515 if (this->isInjector()) {
516 // Store the original water flux computed from the reservoir quantities.
517 // It will be required to assemble the injectivity equations.
518 const unsigned water_comp_idx = Indices::canonicalToActiveComponentIndex(FluidSystem::waterCompIdx);
519 water_flux_s = cq_s[water_comp_idx];
520 // Modify the water flux for the rest of this function to depend directly on the
521 // local water velocity primary variable.
522 handleInjectivityRate(simulator, perf, cq_s);
523 }
524 }
525
526 // updating the solution gas rate and solution oil rate
527 if (this->isProducer()) {
528 ws.phase_mixing_rates[ws.dissolved_gas] += perf_rates.dis_gas;
529 ws.phase_mixing_rates[ws.dissolved_gas_in_water] += perf_rates.dis_gas_in_water;
530 ws.phase_mixing_rates[ws.vaporized_oil] += perf_rates.vap_oil;
531 ws.phase_mixing_rates[ws.vaporized_water] += perf_rates.vap_wat;
532 perf_data.phase_mixing_rates[perf][ws.dissolved_gas] = perf_rates.dis_gas;
533 perf_data.phase_mixing_rates[perf][ws.dissolved_gas_in_water] = perf_rates.dis_gas_in_water;
534 perf_data.phase_mixing_rates[perf][ws.vaporized_oil] = perf_rates.vap_oil;
535 perf_data.phase_mixing_rates[perf][ws.vaporized_water] = perf_rates.vap_wat;
536 }
537
538 if constexpr (has_energy) {
539 connectionRates[perf][Indices::contiEnergyEqIdx] =
540 connectionRateEnergy(simulator.problem().maxOilSaturation(cell_idx),
541 cq_s, intQuants, deferred_logger);
542 }
543
544 if constexpr (has_polymer) {
545 std::variant<Scalar,EvalWell> polymerConcentration;
546 if (this->isInjector()) {
547 polymerConcentration = this->wpolymer();
548 } else {
549 polymerConcentration = this->extendEval(intQuants.polymerConcentration() *
550 intQuants.polymerViscosityCorrection());
551 }
552
553 [[maybe_unused]] EvalWell cq_s_poly;
554 std::tie(connectionRates[perf][Indices::contiPolymerEqIdx],
555 cq_s_poly) =
556 this->connections_.connectionRatePolymer(perf_data.polymer_rates[perf],
557 cq_s, polymerConcentration);
558
559 if constexpr (Base::has_polymermw) {
560 updateConnectionRatePolyMW(cq_s_poly, intQuants, well_state,
561 perf, connectionRates, deferred_logger);
562 }
563 }
564
565 if constexpr (has_foam) {
566 std::variant<Scalar,EvalWell> foamConcentration;
567 if (this->isInjector()) {
568 foamConcentration = this->wfoam();
569 } else {
570 foamConcentration = this->extendEval(intQuants.foamConcentration());
571 }
572 connectionRates[perf][Indices::contiFoamEqIdx] =
573 this->connections_.connectionRateFoam(cq_s, foamConcentration,
574 FoamModule::transportPhase(),
575 deferred_logger);
576 }
577
578 if constexpr (has_zFraction) {
579 std::variant<Scalar,std::array<EvalWell,2>> solventConcentration;
580 if (this->isInjector()) {
581 solventConcentration = this->wsolvent();
582 } else {
583 solventConcentration = std::array{this->extendEval(intQuants.xVolume()),
584 this->extendEval(intQuants.yVolume())};
585 }
586 std::tie(connectionRates[perf][Indices::contiZfracEqIdx],
587 cq_s_zfrac_effective) =
588 this->connections_.connectionRatezFraction(perf_data.solvent_rates[perf],
589 perf_rates.dis_gas, cq_s,
590 solventConcentration);
591 }
592
593 if constexpr (has_brine) {
594 std::variant<Scalar,EvalWell> saltConcentration;
595 if (this->isInjector()) {
596 saltConcentration = this->wsalt();
597 } else {
598 saltConcentration = this->extendEval(intQuants.fluidState().saltConcentration());
599 }
600
601 connectionRates[perf][Indices::contiBrineEqIdx] =
602 this->connections_.connectionRateBrine(perf_data.brine_rates[perf],
603 perf_rates.vap_wat, cq_s,
604 saltConcentration);
605 }
606
607 if constexpr (has_micp) {
608 std::variant<Scalar,EvalWell> microbialConcentration;
609 std::variant<Scalar,EvalWell> oxygenConcentration;
610 std::variant<Scalar,EvalWell> ureaConcentration;
611 if (this->isInjector()) {
612 microbialConcentration = this->wmicrobes();
613 oxygenConcentration = this->woxygen();
614 ureaConcentration = this->wurea();
615 } else {
616 microbialConcentration = this->extendEval(intQuants.microbialConcentration());
617 oxygenConcentration = this->extendEval(intQuants.oxygenConcentration());
618 ureaConcentration = this->extendEval(intQuants.ureaConcentration());
619 }
620 std::tie(connectionRates[perf][Indices::contiMicrobialEqIdx],
621 connectionRates[perf][Indices::contiOxygenEqIdx],
622 connectionRates[perf][Indices::contiUreaEqIdx]) =
623 this->connections_.connectionRatesMICP(cq_s,
624 microbialConcentration,
625 oxygenConcentration,
626 ureaConcentration);
627 }
628
629 // Store the perforation pressure for later usage.
630 perf_data.pressure[perf] = ws.bhp + this->connections_.pressure_diff(perf);
631 }
632
633
634
635 template<typename TypeTag>
636 template<class Value>
637 void
639 getMobility(const Simulator& simulator,
640 const int perf,
641 std::vector<Value>& mob,
642 DeferredLogger& deferred_logger) const
643 {
644 auto obtain = [this](const Eval& value)
645 {
646 if constexpr (std::is_same_v<Value, Scalar>) {
647 static_cast<void>(this); // suppress clang warning
648 return getValue(value);
649 } else {
650 return this->extendEval(value);
651 }
652 };
653 WellInterface<TypeTag>::getMobility(simulator, perf, mob,
654 obtain, deferred_logger);
655
656 // modify the water mobility if polymer is present
657 if constexpr (has_polymer) {
658 if (!FluidSystem::phaseIsActive(FluidSystem::waterPhaseIdx)) {
659 OPM_DEFLOG_THROW(std::runtime_error, "Water is required when polymer is active", deferred_logger);
660 }
661
662 // for the cases related to polymer molecular weight, we assume fully mixing
663 // as a result, the polymer and water share the same viscosity
664 if constexpr (!Base::has_polymermw) {
665 if constexpr (std::is_same_v<Value, Scalar>) {
666 std::vector<EvalWell> mob_eval(this->num_components_, {this->primary_variables_.numWellEq() + Indices::numEq, 0.});
667 for (std::size_t i = 0; i < mob.size(); ++i) {
668 mob_eval[i].setValue(mob[i]);
669 }
670 updateWaterMobilityWithPolymer(simulator, perf, mob_eval, deferred_logger);
671 for (std::size_t i = 0; i < mob.size(); ++i) {
672 mob[i] = getValue(mob_eval[i]);
673 }
674 } else {
675 updateWaterMobilityWithPolymer(simulator, perf, mob, deferred_logger);
676 }
677 }
678 }
679
680 // if the injecting well has WINJMULT setup, we update the mobility accordingly
681 if (this->isInjector() && this->well_ecl_.getInjMultMode() != Well::InjMultMode::NONE) {
682 const double bhp = this->primary_variables_.value(Bhp);
683 const double perf_press = bhp + this->connections_.pressure_diff(perf);
684 const double multiplier = this->getInjMult(perf, bhp, perf_press);
685 for (std::size_t i = 0; i < mob.size(); ++i) {
686 mob[i] *= multiplier;
687 }
688 }
689 }
690
691
692 template<typename TypeTag>
693 void
695 updateWellState(const SummaryState& summary_state,
696 const BVectorWell& dwells,
697 WellState<Scalar>& well_state,
698 DeferredLogger& deferred_logger)
699 {
700 if (!this->isOperableAndSolvable() && !this->wellIsStopped()) return;
701
702 const bool stop_or_zero_rate_target = this->stopppedOrZeroRateTarget(summary_state, well_state);
703 updatePrimaryVariablesNewton(dwells, stop_or_zero_rate_target, deferred_logger);
704
705 updateWellStateFromPrimaryVariables(stop_or_zero_rate_target, well_state, summary_state, deferred_logger);
706 Base::calculateReservoirRates(well_state.well(this->index_of_well_));
707 }
708
709
710
711
712
713 template<typename TypeTag>
714 void
717 const bool stop_or_zero_rate_target,
718 DeferredLogger& deferred_logger)
719 {
720 const double dFLimit = this->param_.dwell_fraction_max_;
721 const double dBHPLimit = this->param_.dbhp_max_rel_;
722 this->primary_variables_.updateNewton(dwells, stop_or_zero_rate_target, dFLimit, dBHPLimit, deferred_logger);
723
724 // for the water velocity and skin pressure
725 if constexpr (Base::has_polymermw) {
726 this->primary_variables_.updateNewtonPolyMW(dwells);
727 }
728
729 this->primary_variables_.checkFinite(deferred_logger);
730 }
731
732
733
734
735
736 template<typename TypeTag>
737 void
739 updateWellStateFromPrimaryVariables(const bool stop_or_zero_rate_target,
740 WellState<Scalar>& well_state,
741 const SummaryState& summary_state,
742 DeferredLogger& deferred_logger) const
743 {
744 this->StdWellEval::updateWellStateFromPrimaryVariables(stop_or_zero_rate_target, well_state, summary_state, deferred_logger);
745
746 // other primary variables related to polymer injectivity study
747 if constexpr (Base::has_polymermw) {
748 this->primary_variables_.copyToWellStatePolyMW(well_state);
749 }
750 }
751
752
753
754
755
756 template<typename TypeTag>
757 void
759 updateIPR(const Simulator& simulator, DeferredLogger& deferred_logger) const
760 {
761 // TODO: not handling solvent related here for now
762
763 // initialize all the values to be zero to begin with
764 std::fill(this->ipr_a_.begin(), this->ipr_a_.end(), 0.);
765 std::fill(this->ipr_b_.begin(), this->ipr_b_.end(), 0.);
766
767 for (int perf = 0; perf < this->number_of_perforations_; ++perf) {
768 std::vector<Scalar> mob(this->num_components_, 0.0);
769 getMobility(simulator, perf, mob, deferred_logger);
770
771 const int cell_idx = this->well_cells_[perf];
772 const auto& int_quantities = simulator.model().intensiveQuantities(cell_idx, /*timeIdx=*/ 0);
773 const auto& fs = int_quantities.fluidState();
774 // the pressure of the reservoir grid block the well connection is in
775 double p_r = this->getPerfCellPressure(fs).value();
776
777 // calculating the b for the connection
778 std::vector<double> b_perf(this->num_components_);
779 for (std::size_t phase = 0; phase < FluidSystem::numPhases; ++phase) {
780 if (!FluidSystem::phaseIsActive(phase)) {
781 continue;
782 }
783 const unsigned comp_idx = Indices::canonicalToActiveComponentIndex(FluidSystem::solventComponentIndex(phase));
784 b_perf[comp_idx] = fs.invB(phase).value();
785 }
786 if constexpr (has_solvent) {
787 b_perf[Indices::contiSolventEqIdx] = int_quantities.solventInverseFormationVolumeFactor().value();
788 }
789
790 // the pressure difference between the connection and BHP
791 const double h_perf = this->connections_.pressure_diff(perf);
792 const double pressure_diff = p_r - h_perf;
793
794 // Let us add a check, since the pressure is calculated based on zero value BHP
795 // it should not be negative anyway. If it is negative, we might need to re-formulate
796 // to taking into consideration the crossflow here.
797 if ( (this->isProducer() && pressure_diff < 0.) || (this->isInjector() && pressure_diff > 0.) ) {
798 deferred_logger.debug("CROSSFLOW_IPR",
799 "cross flow found when updateIPR for well " + name()
800 + " . The connection is ignored in IPR calculations");
801 // we ignore these connections for now
802 continue;
803 }
804
805 // the well index associated with the connection
806 double trans_mult = simulator.problem().template wellTransMultiplier<double>(int_quantities, cell_idx);
807 const auto& wellstate_nupcol = simulator.problem().wellModel().nupcolWellState().well(this->index_of_well_);
808 const std::vector<Scalar> tw_perf = this->wellIndex(perf, int_quantities, trans_mult, wellstate_nupcol);
809 std::vector<double> ipr_a_perf(this->ipr_a_.size());
810 std::vector<double> ipr_b_perf(this->ipr_b_.size());
811 for (int comp_idx = 0; comp_idx < this->num_components_; ++comp_idx) {
812 const double tw_mob = tw_perf[comp_idx] * mob[comp_idx] * b_perf[comp_idx];
813 ipr_a_perf[comp_idx] += tw_mob * pressure_diff;
814 ipr_b_perf[comp_idx] += tw_mob;
815 }
816
817 // we need to handle the rs and rv when both oil and gas are present
818 if (FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx) && FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx)) {
819 const unsigned oil_comp_idx = Indices::canonicalToActiveComponentIndex(FluidSystem::oilCompIdx);
820 const unsigned gas_comp_idx = Indices::canonicalToActiveComponentIndex(FluidSystem::gasCompIdx);
821 const double rs = (fs.Rs()).value();
822 const double rv = (fs.Rv()).value();
823
824 const double dis_gas_a = rs * ipr_a_perf[oil_comp_idx];
825 const double vap_oil_a = rv * ipr_a_perf[gas_comp_idx];
826
827 ipr_a_perf[gas_comp_idx] += dis_gas_a;
828 ipr_a_perf[oil_comp_idx] += vap_oil_a;
829
830 const double dis_gas_b = rs * ipr_b_perf[oil_comp_idx];
831 const double vap_oil_b = rv * ipr_b_perf[gas_comp_idx];
832
833 ipr_b_perf[gas_comp_idx] += dis_gas_b;
834 ipr_b_perf[oil_comp_idx] += vap_oil_b;
835 }
836
837 for (std::size_t comp_idx = 0; comp_idx < ipr_a_perf.size(); ++comp_idx) {
838 this->ipr_a_[comp_idx] += ipr_a_perf[comp_idx];
839 this->ipr_b_[comp_idx] += ipr_b_perf[comp_idx];
840 }
841 }
842 this->parallel_well_info_.communication().sum(this->ipr_a_.data(), this->ipr_a_.size());
843 this->parallel_well_info_.communication().sum(this->ipr_b_.data(), this->ipr_b_.size());
844 }
845
846 template<typename TypeTag>
847 void
849 updateIPRImplicit(const Simulator& simulator,
850 WellState<Scalar>& well_state,
851 DeferredLogger& deferred_logger)
852 {
853 // Compute IPR based on *converged* well-equation:
854 // For a component rate r the derivative dr/dbhp is obtained by
855 // dr/dbhp = - (partial r/partial x) * inv(partial Eq/partial x) * (partial Eq/partial bhp_target)
856 // where Eq(x)=0 is the well equation setup with bhp control and primary variables x
857
858 // We shouldn't have zero rates at this stage, but check
859 bool zero_rates;
860 auto rates = well_state.well(this->index_of_well_).surface_rates;
861 zero_rates = true;
862 for (std::size_t p = 0; p < rates.size(); ++p) {
863 zero_rates &= rates[p] == 0.0;
864 }
865 auto& ws = well_state.well(this->index_of_well_);
866 if (zero_rates) {
867 const auto msg = fmt::format("updateIPRImplicit: Well {} has zero rate, IPRs might be problematic", this->name());
868 deferred_logger.debug(msg);
869 /*
870 // could revert to standard approach here:
871 updateIPR(simulator, deferred_logger);
872 for (int comp_idx = 0; comp_idx < this->num_components_; ++comp_idx){
873 const int idx = this->modelCompIdxToFlowCompIdx(comp_idx);
874 ws.implicit_ipr_a[idx] = this->ipr_a_[comp_idx];
875 ws.implicit_ipr_b[idx] = this->ipr_b_[comp_idx];
876 }
877 return;
878 */
879 }
880 const auto& group_state = simulator.problem().wellModel().groupState();
881
882 std::fill(ws.implicit_ipr_a.begin(), ws.implicit_ipr_a.end(), 0.);
883 std::fill(ws.implicit_ipr_b.begin(), ws.implicit_ipr_b.end(), 0.);
884
885 auto inj_controls = Well::InjectionControls(0);
886 auto prod_controls = Well::ProductionControls(0);
887 prod_controls.addControl(Well::ProducerCMode::BHP);
888 prod_controls.bhp_limit = well_state.well(this->index_of_well_).bhp;
889
890 // Set current control to bhp, and bhp value in state, modify bhp limit in control object.
891 const auto cmode = ws.production_cmode;
892 ws.production_cmode = Well::ProducerCMode::BHP;
893 const double dt = simulator.timeStepSize();
894 assembleWellEqWithoutIteration(simulator, dt, inj_controls, prod_controls, well_state, group_state, deferred_logger);
895
896 const size_t nEq = this->primary_variables_.numWellEq();
897 BVectorWell rhs(1);
898 rhs[0].resize(nEq);
899 // rhs = 0 except -1 for control eq
900 for (size_t i=0; i < nEq; ++i){
901 rhs[0][i] = 0.0;
902 }
903 rhs[0][Bhp] = -1.0;
904
905 BVectorWell x_well(1);
906 x_well[0].resize(nEq);
907 this->linSys_.solve(rhs, x_well);
908
909 for (int comp_idx = 0; comp_idx < this->num_components_; ++comp_idx){
910 EvalWell comp_rate = this->primary_variables_.getQs(comp_idx);
911 const int idx = this->modelCompIdxToFlowCompIdx(comp_idx);
912 for (size_t pvIdx = 0; pvIdx < nEq; ++pvIdx) {
913 // well primary variable derivatives in EvalWell start at position Indices::numEq
914 ws.implicit_ipr_b[idx] -= x_well[0][pvIdx]*comp_rate.derivative(pvIdx+Indices::numEq);
915 }
916 ws.implicit_ipr_a[idx] = ws.implicit_ipr_b[idx]*ws.bhp - comp_rate.value();
917 }
918 // reset cmode
919 ws.production_cmode = cmode;
920 }
921
922 template<typename TypeTag>
923 void
926 const Simulator& simulator,
927 DeferredLogger& deferred_logger)
928 {
929 const auto& summaryState = simulator.vanguard().summaryState();
930 const double bhp_limit = WellBhpThpCalculator(*this).mostStrictBhpFromBhpLimits(summaryState);
931 // Crude but works: default is one atmosphere.
932 // TODO: a better way to detect whether the BHP is defaulted or not
933 const bool bhp_limit_not_defaulted = bhp_limit > 1.5 * unit::barsa;
934 if ( bhp_limit_not_defaulted || !this->wellHasTHPConstraints(summaryState) ) {
935 // if the BHP limit is not defaulted or the well does not have a THP limit
936 // we need to check the BHP limit
937 double total_ipr_mass_rate = 0.0;
938 for (unsigned phaseIdx = 0; phaseIdx < FluidSystem::numPhases; ++phaseIdx)
939 {
940 if (!FluidSystem::phaseIsActive(phaseIdx)) {
941 continue;
942 }
943
944 const unsigned compIdx = Indices::canonicalToActiveComponentIndex(FluidSystem::solventComponentIndex(phaseIdx));
945 const double ipr_rate = this->ipr_a_[compIdx] - this->ipr_b_[compIdx] * bhp_limit;
946
947 const double rho = FluidSystem::referenceDensity( phaseIdx, Base::pvtRegionIdx() );
948 total_ipr_mass_rate += ipr_rate * rho;
949 }
950 if ( (this->isProducer() && total_ipr_mass_rate < 0.) || (this->isInjector() && total_ipr_mass_rate > 0.) ) {
951 this->operability_status_.operable_under_only_bhp_limit = false;
952 }
953
954 // checking whether running under BHP limit will violate THP limit
955 if (this->operability_status_.operable_under_only_bhp_limit && this->wellHasTHPConstraints(summaryState)) {
956 // option 1: calculate well rates based on the BHP limit.
957 // option 2: stick with the above IPR curve
958 // we use IPR here
959 std::vector<double> well_rates_bhp_limit;
960 computeWellRatesWithBhp(simulator, bhp_limit, well_rates_bhp_limit, deferred_logger);
961
962 this->adaptRatesForVFP(well_rates_bhp_limit);
963 const double thp_limit = this->getTHPConstraint(summaryState);
964 const double thp = WellBhpThpCalculator(*this).calculateThpFromBhp(well_rates_bhp_limit,
965 bhp_limit,
966 this->connections_.rho(),
967 this->getALQ(well_state),
968 thp_limit,
969 deferred_logger);
970 if ( (this->isProducer() && thp < thp_limit) || (this->isInjector() && thp > thp_limit) ) {
971 this->operability_status_.obey_thp_limit_under_bhp_limit = false;
972 }
973 }
974 } else {
975 // defaulted BHP and there is a THP constraint
976 // default BHP limit is about 1 atm.
977 // when applied the hydrostatic pressure correction dp,
978 // most likely we get a negative value (bhp + dp)to search in the VFP table,
979 // which is not desirable.
980 // we assume we can operate under defaulted BHP limit and will violate the THP limit
981 // when operating under defaulted BHP limit.
982 this->operability_status_.operable_under_only_bhp_limit = true;
983 this->operability_status_.obey_thp_limit_under_bhp_limit = false;
984 }
985 }
986
987
988
989
990
991 template<typename TypeTag>
992 void
995 const WellState<Scalar>& well_state,
996 DeferredLogger& deferred_logger)
997 {
998 const auto& summaryState = simulator.vanguard().summaryState();
999 const auto obtain_bhp = this->isProducer() ? computeBhpAtThpLimitProd(well_state, simulator, summaryState, deferred_logger)
1000 : computeBhpAtThpLimitInj(simulator, summaryState, deferred_logger);
1001
1002 if (obtain_bhp) {
1003 this->operability_status_.can_obtain_bhp_with_thp_limit = true;
1004
1005 const double bhp_limit = WellBhpThpCalculator(*this).mostStrictBhpFromBhpLimits(summaryState);
1006 this->operability_status_.obey_bhp_limit_with_thp_limit = this->isProducer() ?
1007 *obtain_bhp >= bhp_limit : *obtain_bhp <= bhp_limit ;
1008
1009 const double thp_limit = this->getTHPConstraint(summaryState);
1010 if (this->isProducer() && *obtain_bhp < thp_limit) {
1011 const std::string msg = " obtained bhp " + std::to_string(unit::convert::to(*obtain_bhp, unit::barsa))
1012 + " bars is SMALLER than thp limit "
1013 + std::to_string(unit::convert::to(thp_limit, unit::barsa))
1014 + " bars as a producer for well " + name();
1015 deferred_logger.debug(msg);
1016 }
1017 else if (this->isInjector() && *obtain_bhp > thp_limit) {
1018 const std::string msg = " obtained bhp " + std::to_string(unit::convert::to(*obtain_bhp, unit::barsa))
1019 + " bars is LARGER than thp limit "
1020 + std::to_string(unit::convert::to(thp_limit, unit::barsa))
1021 + " bars as a injector for well " + name();
1022 deferred_logger.debug(msg);
1023 }
1024 } else {
1025 this->operability_status_.can_obtain_bhp_with_thp_limit = false;
1026 this->operability_status_.obey_bhp_limit_with_thp_limit = false;
1027 if (!this->wellIsStopped()) {
1028 const double thp_limit = this->getTHPConstraint(summaryState);
1029 deferred_logger.debug(" could not find bhp value at thp limit "
1030 + std::to_string(unit::convert::to(thp_limit, unit::barsa))
1031 + " bar for well " + name() + ", the well might need to be closed ");
1032 }
1033 }
1034 }
1035
1036
1037
1038
1039
1040 template<typename TypeTag>
1041 bool
1043 allDrawDownWrongDirection(const Simulator& simulator) const
1044 {
1045 bool all_drawdown_wrong_direction = true;
1046
1047 for (int perf = 0; perf < this->number_of_perforations_; ++perf) {
1048 const int cell_idx = this->well_cells_[perf];
1049 const auto& intQuants = simulator.model().intensiveQuantities(cell_idx, /*timeIdx=*/0);
1050 const auto& fs = intQuants.fluidState();
1051
1052 const double pressure = this->getPerfCellPressure(fs).value();
1053 const double bhp = this->primary_variables_.eval(Bhp).value();
1054
1055 // Pressure drawdown (also used to determine direction of flow)
1056 const double well_pressure = bhp + this->connections_.pressure_diff(perf);
1057 const double drawdown = pressure - well_pressure;
1058
1059 // for now, if there is one perforation can produce/inject in the correct
1060 // direction, we consider this well can still produce/inject.
1061 // TODO: it can be more complicated than this to cause wrong-signed rates
1062 if ( (drawdown < 0. && this->isInjector()) ||
1063 (drawdown > 0. && this->isProducer()) ) {
1064 all_drawdown_wrong_direction = false;
1065 break;
1066 }
1067 }
1068
1069 const auto& comm = this->parallel_well_info_.communication();
1070 if (comm.size() > 1)
1071 {
1072 all_drawdown_wrong_direction =
1073 (comm.min(all_drawdown_wrong_direction ? 1 : 0) == 1);
1074 }
1075
1076 return all_drawdown_wrong_direction;
1077 }
1078
1079
1080
1081
1082 template<typename TypeTag>
1083 bool
1086 const WellState<Scalar>& well_state,
1087 DeferredLogger& deferred_logger)
1088 {
1089 const double bhp = well_state.well(this->index_of_well_).bhp;
1090 std::vector<double> well_rates;
1091 computeWellRatesWithBhp(simulator, bhp, well_rates, deferred_logger);
1092
1093 const double sign = (this->isProducer()) ? -1. : 1.;
1094 const double threshold = sign * std::numeric_limits<double>::min();
1095
1096 bool can_produce_inject = false;
1097 for (const auto value : well_rates) {
1098 if (this->isProducer() && value < threshold) {
1099 can_produce_inject = true;
1100 break;
1101 } else if (this->isInjector() && value > threshold) {
1102 can_produce_inject = true;
1103 break;
1104 }
1105 }
1106
1107 if (!can_produce_inject) {
1108 deferred_logger.debug(" well " + name() + " CANNOT produce or inejct ");
1109 }
1110
1111 return can_produce_inject;
1112 }
1113
1114
1115
1116
1117
1118 template<typename TypeTag>
1119 bool
1121 openCrossFlowAvoidSingularity(const Simulator& simulator) const
1122 {
1123 return !this->getAllowCrossFlow() && allDrawDownWrongDirection(simulator);
1124 }
1125
1126
1127
1128
1129 template<typename TypeTag>
1130 void
1133 const WellState<Scalar>& well_state,
1134 WellConnectionProps& props) const
1135 {
1136 std::function<Scalar(int,int)> getTemperature =
1137 [&simulator](int cell_idx, int phase_idx)
1138 {
1139 return simulator.model().intensiveQuantities(cell_idx, 0).fluidState().temperature(phase_idx).value();
1140 };
1141 std::function<Scalar(int)> getSaltConcentration =
1142 [&simulator](int cell_idx)
1143 {
1144 return simulator.model().intensiveQuantities(cell_idx, 0).fluidState().saltConcentration().value();
1145 };
1146 std::function<int(int)> getPvtRegionIdx =
1147 [&simulator](int cell_idx)
1148 {
1149 return simulator.model().intensiveQuantities(cell_idx, 0).fluidState().pvtRegionIndex();
1150 };
1151 std::function<Scalar(int)> getInvFac =
1152 [&simulator](int cell_idx)
1153 {
1154 return simulator.model().intensiveQuantities(cell_idx, 0).solventInverseFormationVolumeFactor().value();
1155 };
1156 std::function<Scalar(int)> getSolventDensity =
1157 [&simulator](int cell_idx)
1158 {
1159 return simulator.model().intensiveQuantities(cell_idx, 0).solventRefDensity();
1160 };
1161
1162 this->connections_.computePropertiesForPressures(well_state,
1163 getTemperature,
1164 getSaltConcentration,
1165 getPvtRegionIdx,
1166 getInvFac,
1167 getSolventDensity,
1168 props);
1169 }
1170
1171
1172
1173
1174
1175 template<typename TypeTag>
1178 getWellConvergence(const SummaryState& summary_state,
1179 const WellState<Scalar>& well_state,
1180 const std::vector<double>& B_avg,
1181 DeferredLogger& deferred_logger,
1182 const bool relax_tolerance) const
1183 {
1184 // the following implementation assume that the polymer is always after the w-o-g phases
1185 // For the polymer, energy and foam cases, there is one more mass balance equations of reservoir than wells
1186 assert((int(B_avg.size()) == this->num_components_) || has_polymer || has_energy || has_foam || has_brine || has_zFraction || has_micp);
1187
1188 double tol_wells = this->param_.tolerance_wells_;
1189 // use stricter tolerance for stopped wells and wells under zero rate target control.
1190 constexpr double stopped_factor = 1.e-4;
1191 // use stricter tolerance for dynamic thp to ameliorate network convergence
1192 constexpr double dynamic_thp_factor = 1.e-1;
1193 if (this->stopppedOrZeroRateTarget(summary_state, well_state)) {
1194 tol_wells = tol_wells*stopped_factor;
1195 } else if (this->getDynamicThpLimit()) {
1196 tol_wells = tol_wells*dynamic_thp_factor;
1197 }
1198
1199 std::vector<double> res;
1200 ConvergenceReport report = this->StdWellEval::getWellConvergence(well_state,
1201 B_avg,
1202 this->param_.max_residual_allowed_,
1203 tol_wells,
1204 this->param_.relaxed_tolerance_flow_well_,
1205 relax_tolerance,
1206 this->wellIsStopped(),
1207 res,
1208 deferred_logger);
1209
1210 checkConvergenceExtraEqs(res, report);
1211
1212 return report;
1213 }
1214
1215
1216
1217
1218
1219 template<typename TypeTag>
1220 void
1222 updateProductivityIndex(const Simulator& simulator,
1223 const WellProdIndexCalculator& wellPICalc,
1224 WellState<Scalar>& well_state,
1225 DeferredLogger& deferred_logger) const
1226 {
1227 auto fluidState = [&simulator, this](const int perf)
1228 {
1229 const auto cell_idx = this->well_cells_[perf];
1230 return simulator.model()
1231 .intensiveQuantities(cell_idx, /*timeIdx=*/ 0).fluidState();
1232 };
1233
1234 const int np = this->number_of_phases_;
1235 auto setToZero = [np](double* x) -> void
1236 {
1237 std::fill_n(x, np, 0.0);
1238 };
1239
1240 auto addVector = [np](const double* src, double* dest) -> void
1241 {
1242 std::transform(src, src + np, dest, dest, std::plus<>{});
1243 };
1244
1245 auto& ws = well_state.well(this->index_of_well_);
1246 auto& perf_data = ws.perf_data;
1247 auto* wellPI = ws.productivity_index.data();
1248 auto* connPI = perf_data.prod_index.data();
1249
1250 setToZero(wellPI);
1251
1252 const auto preferred_phase = this->well_ecl_.getPreferredPhase();
1253 auto subsetPerfID = 0;
1254
1255 for (const auto& perf : *this->perf_data_) {
1256 auto allPerfID = perf.ecl_index;
1257
1258 auto connPICalc = [&wellPICalc, allPerfID](const double mobility) -> double
1259 {
1260 return wellPICalc.connectionProdIndStandard(allPerfID, mobility);
1261 };
1262
1263 std::vector<Scalar> mob(this->num_components_, 0.0);
1264 getMobility(simulator, static_cast<int>(subsetPerfID), mob, deferred_logger);
1265
1266 const auto& fs = fluidState(subsetPerfID);
1267 setToZero(connPI);
1268
1269 if (this->isInjector()) {
1270 this->computeConnLevelInjInd(fs, preferred_phase, connPICalc,
1271 mob, connPI, deferred_logger);
1272 }
1273 else { // Production or zero flow rate
1274 this->computeConnLevelProdInd(fs, connPICalc, mob, connPI);
1275 }
1276
1277 addVector(connPI, wellPI);
1278
1279 ++subsetPerfID;
1280 connPI += np;
1281 }
1282
1283 // Sum with communication in case of distributed well.
1284 const auto& comm = this->parallel_well_info_.communication();
1285 if (comm.size() > 1) {
1286 comm.sum(wellPI, np);
1287 }
1288
1289 assert ((static_cast<int>(subsetPerfID) == this->number_of_perforations_) &&
1290 "Internal logic error in processing connections for PI/II");
1291 }
1292
1293
1294
1295 template<typename TypeTag>
1296 void
1299 const WellState<Scalar>& well_state,
1300 const WellConnectionProps& props,
1301 DeferredLogger& deferred_logger)
1302 {
1303 std::function<Scalar(int,int)> invB =
1304 [&simulator](int cell_idx, int phase_idx)
1305 {
1306 return simulator.model().intensiveQuantities(cell_idx, 0).fluidState().invB(phase_idx).value();
1307 };
1308 std::function<Scalar(int,int)> mobility =
1309 [&simulator](int cell_idx, int phase_idx)
1310 {
1311 return simulator.model().intensiveQuantities(cell_idx, 0).mobility(phase_idx).value();
1312 };
1313 std::function<Scalar(int)> invFac =
1314 [&simulator](int cell_idx)
1315 {
1316 return simulator.model().intensiveQuantities(cell_idx, 0).solventInverseFormationVolumeFactor().value();
1317 };
1318 std::function<Scalar(int)> solventMobility =
1319 [&simulator](int cell_idx)
1320 {
1321 return simulator.model().intensiveQuantities(cell_idx, 0).solventMobility().value();
1322 };
1323
1324 this->connections_.computeProperties(well_state,
1325 invB,
1326 mobility,
1327 invFac,
1328 solventMobility,
1329 props,
1330 deferred_logger);
1331 }
1332
1333
1334
1335
1336
1337 template<typename TypeTag>
1338 void
1341 const WellState<Scalar>& well_state,
1342 DeferredLogger& deferred_logger)
1343 {
1344 // 1. Compute properties required by computePressureDelta().
1345 // Note that some of the complexity of this part is due to the function
1346 // taking std::vector<double> arguments, and not Eigen objects.
1347 WellConnectionProps props;
1348 computePropertiesForWellConnectionPressures(simulator, well_state, props);
1349 computeWellConnectionDensitesPressures(simulator, well_state,
1350 props, deferred_logger);
1351 }
1352
1353
1354
1355
1356
1357 template<typename TypeTag>
1358 void
1360 solveEqAndUpdateWellState(const SummaryState& summary_state,
1361 WellState<Scalar>& well_state,
1362 DeferredLogger& deferred_logger)
1363 {
1364 if (!this->isOperableAndSolvable() && !this->wellIsStopped()) return;
1365
1366 // We assemble the well equations, then we check the convergence,
1367 // which is why we do not put the assembleWellEq here.
1368 BVectorWell dx_well(1);
1369 dx_well[0].resize(this->primary_variables_.numWellEq());
1370 this->linSys_.solve( dx_well);
1371
1372 updateWellState(summary_state, dx_well, well_state, deferred_logger);
1373 }
1374
1375
1376
1377
1378
1379 template<typename TypeTag>
1380 void
1383 const WellState<Scalar>& well_state,
1384 DeferredLogger& deferred_logger)
1385 {
1386 const auto& summary_state = simulator.vanguard().summaryState();
1387 updatePrimaryVariables(summary_state, well_state, deferred_logger);
1388 initPrimaryVariablesEvaluation();
1389 computeWellConnectionPressures(simulator, well_state, deferred_logger);
1390 this->computeAccumWell();
1391 }
1392
1393
1394
1395 template<typename TypeTag>
1396 void
1398 apply(const BVector& x, BVector& Ax) const
1399 {
1400 if (!this->isOperableAndSolvable() && !this->wellIsStopped()) return;
1401
1402 if (this->param_.matrix_add_well_contributions_)
1403 {
1404 // Contributions are already in the matrix itself
1405 return;
1406 }
1407
1408 this->linSys_.apply(x, Ax);
1409 }
1410
1411
1412
1413
1414 template<typename TypeTag>
1415 void
1417 apply(BVector& r) const
1418 {
1419 if (!this->isOperableAndSolvable() && !this->wellIsStopped()) return;
1420
1421 this->linSys_.apply(r);
1422 }
1423
1424
1425
1426
1427 template<typename TypeTag>
1428 void
1430 recoverWellSolutionAndUpdateWellState(const SummaryState& summary_state,
1431 const BVector& x,
1432 WellState<Scalar>& well_state,
1433 DeferredLogger& deferred_logger)
1434 {
1435 if (!this->isOperableAndSolvable() && !this->wellIsStopped()) return;
1436
1437 BVectorWell xw(1);
1438 xw[0].resize(this->primary_variables_.numWellEq());
1439
1440 this->linSys_.recoverSolutionWell(x, xw);
1441 updateWellState(summary_state, xw, well_state, deferred_logger);
1442 }
1443
1444
1445
1446
1447 template<typename TypeTag>
1448 void
1450 computeWellRatesWithBhp(const Simulator& simulator,
1451 const double& bhp,
1452 std::vector<double>& well_flux,
1453 DeferredLogger& deferred_logger) const
1454 {
1455
1456 const int np = this->number_of_phases_;
1457 well_flux.resize(np, 0.0);
1458
1459 const bool allow_cf = this->getAllowCrossFlow();
1460
1461 for (int perf = 0; perf < this->number_of_perforations_; ++perf) {
1462 const int cell_idx = this->well_cells_[perf];
1463 const auto& intQuants = simulator.model().intensiveQuantities(cell_idx, /*timeIdx=*/ 0);
1464 // flux for each perforation
1465 std::vector<Scalar> mob(this->num_components_, 0.);
1466 getMobility(simulator, perf, mob, deferred_logger);
1467 double trans_mult = simulator.problem().template wellTransMultiplier<double>(intQuants, cell_idx);
1468 const auto& wellstate_nupcol = simulator.problem().wellModel().nupcolWellState().well(this->index_of_well_);
1469 const std::vector<Scalar> Tw = this->wellIndex(perf, intQuants, trans_mult, wellstate_nupcol);
1470
1471 std::vector<Scalar> cq_s(this->num_components_, 0.);
1472 PerforationRates perf_rates;
1473 computePerfRate(intQuants, mob, bhp, Tw, perf, allow_cf,
1474 cq_s, perf_rates, deferred_logger);
1475
1476 for(int p = 0; p < np; ++p) {
1477 well_flux[this->modelCompIdxToFlowCompIdx(p)] += cq_s[p];
1478 }
1479
1480 // the solvent contribution is added to the gas potentials
1481 if constexpr (has_solvent) {
1482 const auto& pu = this->phaseUsage();
1483 assert(pu.phase_used[Gas]);
1484 const int gas_pos = pu.phase_pos[Gas];
1485 well_flux[gas_pos] += cq_s[Indices::contiSolventEqIdx];
1486 }
1487 }
1488 this->parallel_well_info_.communication().sum(well_flux.data(), well_flux.size());
1489 }
1490
1491
1492
1493 template<typename TypeTag>
1494 void
1497 const double& bhp,
1498 std::vector<double>& well_flux,
1499 DeferredLogger& deferred_logger) const
1500 {
1501 // creating a copy of the well itself, to avoid messing up the explicit information
1502 // during this copy, the only information not copied properly is the well controls
1503 StandardWell<TypeTag> well_copy(*this);
1504
1505 // iterate to get a more accurate well density
1506 // create a copy of the well_state to use. If the operability checking is sucessful, we use this one
1507 // to replace the original one
1508 WellState<Scalar> well_state_copy = simulator.problem().wellModel().wellState();
1509 const auto& group_state = simulator.problem().wellModel().groupState();
1510
1511 // Get the current controls.
1512 const auto& summary_state = simulator.vanguard().summaryState();
1513 auto inj_controls = well_copy.well_ecl_.isInjector()
1514 ? well_copy.well_ecl_.injectionControls(summary_state)
1515 : Well::InjectionControls(0);
1516 auto prod_controls = well_copy.well_ecl_.isProducer()
1517 ? well_copy.well_ecl_.productionControls(summary_state) :
1518 Well::ProductionControls(0);
1519
1520 // Set current control to bhp, and bhp value in state, modify bhp limit in control object.
1521 auto& ws = well_state_copy.well(this->index_of_well_);
1522 if (well_copy.well_ecl_.isInjector()) {
1523 inj_controls.bhp_limit = bhp;
1524 ws.injection_cmode = Well::InjectorCMode::BHP;
1525 } else {
1526 prod_controls.bhp_limit = bhp;
1527 ws.production_cmode = Well::ProducerCMode::BHP;
1528 }
1529 ws.bhp = bhp;
1530
1531 // initialized the well rates with the potentials i.e. the well rates based on bhp
1532 const int np = this->number_of_phases_;
1533 const double sign = this->well_ecl_.isInjector() ? 1.0 : -1.0;
1534 for (int phase = 0; phase < np; ++phase){
1535 well_state_copy.wellRates(this->index_of_well_)[phase]
1536 = sign * ws.well_potentials[phase];
1537 }
1538 well_copy.updatePrimaryVariables(summary_state, well_state_copy, deferred_logger);
1540 well_copy.computeAccumWell();
1541
1542 const double dt = simulator.timeStepSize();
1543 const bool converged = well_copy.iterateWellEqWithControl(simulator, dt, inj_controls, prod_controls, well_state_copy, group_state, deferred_logger);
1544 if (!converged) {
1545 const std::string msg = " well " + name() + " did not get converged during well potential calculations "
1546 " potentials are computed based on unconverged solution";
1547 deferred_logger.debug(msg);
1548 }
1549 well_copy.updatePrimaryVariables(summary_state, well_state_copy, deferred_logger);
1550 well_copy.computeWellConnectionPressures(simulator, well_state_copy, deferred_logger);
1552 well_copy.computeWellRatesWithBhp(simulator, bhp, well_flux, deferred_logger);
1553 }
1554
1555
1556
1557
1558 template<typename TypeTag>
1559 std::vector<double>
1562 DeferredLogger& deferred_logger,
1563 const WellState<Scalar>& well_state) const
1564 {
1565 std::vector<double> potentials(this->number_of_phases_, 0.0);
1566 const auto& summary_state = simulator.vanguard().summaryState();
1567
1568 const auto& well = this->well_ecl_;
1569 if (well.isInjector()){
1570 const auto& controls = this->well_ecl_.injectionControls(summary_state);
1571 auto bhp_at_thp_limit = computeBhpAtThpLimitInj(simulator, summary_state, deferred_logger);
1572 if (bhp_at_thp_limit) {
1573 const double bhp = std::min(*bhp_at_thp_limit, controls.bhp_limit);
1574 computeWellRatesWithBhp(simulator, bhp, potentials, deferred_logger);
1575 } else {
1576 deferred_logger.warning("FAILURE_GETTING_CONVERGED_POTENTIAL",
1577 "Failed in getting converged thp based potential calculation for well "
1578 + name() + ". Instead the bhp based value is used");
1579 const double bhp = controls.bhp_limit;
1580 computeWellRatesWithBhp(simulator, bhp, potentials, deferred_logger);
1581 }
1582 } else {
1583 computeWellRatesWithThpAlqProd(
1584 simulator, summary_state,
1585 deferred_logger, potentials, this->getALQ(well_state)
1586 );
1587 }
1588
1589 return potentials;
1590 }
1591
1592 template<typename TypeTag>
1593 bool
1596 std::vector<double>& well_potentials,
1597 DeferredLogger& deferred_logger) const
1598 {
1599 // Create a copy of the well.
1600 // TODO: check if we can avoid taking multiple copies. Call from updateWellPotentials
1601 // is allready a copy, but not from other calls.
1602 StandardWell<TypeTag> well_copy(*this);
1603
1604 // store a copy of the well state, we don't want to update the real well state
1605 WellState<Scalar> well_state_copy = simulator.problem().wellModel().wellState();
1606 const auto& group_state = simulator.problem().wellModel().groupState();
1607 auto& ws = well_state_copy.well(this->index_of_well_);
1608
1609 // get current controls
1610 const auto& summary_state = simulator.vanguard().summaryState();
1611 auto inj_controls = well_copy.well_ecl_.isInjector()
1612 ? well_copy.well_ecl_.injectionControls(summary_state)
1613 : Well::InjectionControls(0);
1614 auto prod_controls = well_copy.well_ecl_.isProducer()
1615 ? well_copy.well_ecl_.productionControls(summary_state) :
1616 Well::ProductionControls(0);
1617
1618 // prepare/modify well state and control
1619 well_copy.prepareForPotentialCalculations(summary_state, well_state_copy, inj_controls, prod_controls);
1620
1621 // initialize rates from previous potentials
1622 const int np = this->number_of_phases_;
1623 bool trivial = true;
1624 for (int phase = 0; phase < np; ++phase){
1625 trivial = trivial && (ws.well_potentials[phase] == 0.0) ;
1626 }
1627 if (!trivial) {
1628 const double sign = well_copy.well_ecl_.isInjector() ? 1.0 : -1.0;
1629 for (int phase = 0; phase < np; ++phase) {
1630 ws.surface_rates[phase] = sign * ws.well_potentials[phase];
1631 }
1632 }
1633
1634 well_copy.calculateExplicitQuantities(simulator, well_state_copy, deferred_logger);
1635 const double dt = simulator.timeStepSize();
1636 // iterate to get a solution at the given bhp.
1637 bool converged = false;
1638 if (this->well_ecl_.isProducer() && this->wellHasTHPConstraints(summary_state)) {
1639 converged = well_copy.solveWellWithTHPConstraint(simulator, dt, inj_controls, prod_controls, well_state_copy, group_state, deferred_logger);
1640 } else {
1641 converged = well_copy.iterateWellEqWithSwitching(simulator, dt, inj_controls, prod_controls, well_state_copy, group_state, deferred_logger);
1642 }
1643
1644 // fetch potentials (sign is updated on the outside).
1645 well_potentials.clear();
1646 well_potentials.resize(np, 0.0);
1647 for (int compIdx = 0; compIdx < this->num_components_; ++compIdx) {
1648 const EvalWell rate = well_copy.primary_variables_.getQs(compIdx);
1649 well_potentials[this->modelCompIdxToFlowCompIdx(compIdx)] = rate.value();
1650 }
1651 return converged;
1652 }
1653
1654
1655 template<typename TypeTag>
1656 double
1659 const SummaryState &summary_state,
1660 DeferredLogger &deferred_logger,
1661 std::vector<double> &potentials,
1662 double alq) const
1663 {
1664 double bhp;
1665 auto bhp_at_thp_limit = computeBhpAtThpLimitProdWithAlq(
1666 simulator, summary_state, alq, deferred_logger);
1667 if (bhp_at_thp_limit) {
1668 const auto& controls = this->well_ecl_.productionControls(summary_state);
1669 bhp = std::max(*bhp_at_thp_limit, controls.bhp_limit);
1670 computeWellRatesWithBhp(simulator, bhp, potentials, deferred_logger);
1671 }
1672 else {
1673 deferred_logger.warning("FAILURE_GETTING_CONVERGED_POTENTIAL",
1674 "Failed in getting converged thp based potential calculation for well "
1675 + name() + ". Instead the bhp based value is used");
1676 const auto& controls = this->well_ecl_.productionControls(summary_state);
1677 bhp = controls.bhp_limit;
1678 computeWellRatesWithBhp(simulator, bhp, potentials, deferred_logger);
1679 }
1680 return bhp;
1681 }
1682
1683 template<typename TypeTag>
1684 void
1687 const SummaryState& summary_state,
1688 DeferredLogger& deferred_logger,
1689 std::vector<double>& potentials,
1690 double alq) const
1691 {
1692 /*double bhp =*/
1693 computeWellRatesAndBhpWithThpAlqProd(simulator,
1694 summary_state,
1695 deferred_logger,
1696 potentials,
1697 alq);
1698 }
1699
1700 template<typename TypeTag>
1701 void
1703 computeWellPotentials(const Simulator& simulator,
1704 const WellState<Scalar>& well_state,
1705 std::vector<double>& well_potentials,
1706 DeferredLogger& deferred_logger) // const
1707 {
1708 const auto [compute_potential, bhp_controlled_well] =
1709 this->WellInterfaceGeneric::computeWellPotentials(well_potentials, well_state);
1710
1711 if (!compute_potential) {
1712 return;
1713 }
1714
1715 bool converged_implicit = false;
1716 if (this->param_.local_well_solver_control_switching_) {
1717 converged_implicit = computeWellPotentialsImplicit(simulator, well_potentials, deferred_logger);
1718 }
1719 if (!converged_implicit) {
1720 // does the well have a THP related constraint?
1721 const auto& summaryState = simulator.vanguard().summaryState();
1722 if (!Base::wellHasTHPConstraints(summaryState) || bhp_controlled_well) {
1723 // get the bhp value based on the bhp constraints
1724 double bhp = WellBhpThpCalculator(*this).mostStrictBhpFromBhpLimits(summaryState);
1725
1726 // In some very special cases the bhp pressure target are
1727 // temporary violated. This may lead to too small or negative potentials
1728 // that could lead to premature shutting of wells.
1729 // As a remedy the bhp that gives the largest potential is used.
1730 // For converged cases, ws.bhp <=bhp for injectors and ws.bhp >= bhp,
1731 // and the potentials will be computed using the limit as expected.
1732 const auto& ws = well_state.well(this->index_of_well_);
1733 if (this->isInjector())
1734 bhp = std::max(ws.bhp, bhp);
1735 else
1736 bhp = std::min(ws.bhp, bhp);
1737
1738 assert(std::abs(bhp) != std::numeric_limits<double>::max());
1739 computeWellRatesWithBhpIterations(simulator, bhp, well_potentials, deferred_logger);
1740 } else {
1741 // the well has a THP related constraint
1742 well_potentials = computeWellPotentialWithTHP(simulator, deferred_logger, well_state);
1743 }
1744 }
1745
1746 this->checkNegativeWellPotentials(well_potentials,
1747 this->param_.check_well_operability_,
1748 deferred_logger);
1749 }
1750
1751
1752
1753
1754
1755
1756
1757 template<typename TypeTag>
1758 double
1760 connectionDensity([[maybe_unused]] const int globalConnIdx,
1761 const int openConnIdx) const
1762 {
1763 return (openConnIdx < 0)
1764 ? 0.0
1765 : this->connections_.rho(openConnIdx);
1766 }
1767
1768
1769
1770
1771
1772 template<typename TypeTag>
1773 void
1775 updatePrimaryVariables(const SummaryState& summary_state,
1776 const WellState<Scalar>& well_state,
1777 DeferredLogger& deferred_logger)
1778 {
1779 if (!this->isOperableAndSolvable() && !this->wellIsStopped()) return;
1780
1781 const bool stop_or_zero_rate_target = this->stopppedOrZeroRateTarget(summary_state, well_state);
1782 this->primary_variables_.update(well_state, stop_or_zero_rate_target, deferred_logger);
1783
1784 // other primary variables related to polymer injection
1785 if constexpr (Base::has_polymermw) {
1786 this->primary_variables_.updatePolyMW(well_state);
1787 }
1788
1789 this->primary_variables_.checkFinite(deferred_logger);
1790 }
1791
1792
1793
1794
1795 template<typename TypeTag>
1796 double
1798 getRefDensity() const
1799 {
1800 return this->connections_.rho();
1801 }
1802
1803
1804
1805
1806 template<typename TypeTag>
1807 void
1810 const int perf,
1811 std::vector<EvalWell>& mob,
1812 DeferredLogger& deferred_logger) const
1813 {
1814 const int cell_idx = this->well_cells_[perf];
1815 const auto& int_quant = simulator.model().intensiveQuantities(cell_idx, /*timeIdx=*/ 0);
1816 const EvalWell polymer_concentration = this->extendEval(int_quant.polymerConcentration());
1817
1818 // TODO: not sure should based on the well type or injecting/producing peforations
1819 // it can be different for crossflow
1820 if (this->isInjector()) {
1821 // assume fully mixing within injecting wellbore
1822 const auto& visc_mult_table = PolymerModule::plyviscViscosityMultiplierTable(int_quant.pvtRegionIndex());
1823 const unsigned waterCompIdx = Indices::canonicalToActiveComponentIndex(FluidSystem::waterCompIdx);
1824 mob[waterCompIdx] /= (this->extendEval(int_quant.waterViscosityCorrection()) * visc_mult_table.eval(polymer_concentration, /*extrapolate=*/true) );
1825 }
1826
1827 if (PolymerModule::hasPlyshlog()) {
1828 // we do not calculate the shear effects for injection wells when they do not
1829 // inject polymer.
1830 if (this->isInjector() && this->wpolymer() == 0.) {
1831 return;
1832 }
1833 // compute the well water velocity with out shear effects.
1834 // TODO: do we need to turn on crossflow here?
1835 const bool allow_cf = this->getAllowCrossFlow() || openCrossFlowAvoidSingularity(simulator);
1836 const EvalWell& bhp = this->primary_variables_.eval(Bhp);
1837
1838 std::vector<EvalWell> cq_s(this->num_components_, {this->primary_variables_.numWellEq() + Indices::numEq, 0.});
1839 PerforationRates perf_rates;
1840 double trans_mult = simulator.problem().template wellTransMultiplier<double>(int_quant, cell_idx);
1841 const auto& wellstate_nupcol = simulator.problem().wellModel().nupcolWellState().well(this->index_of_well_);
1842 const std::vector<Scalar> Tw = this->wellIndex(perf, int_quant, trans_mult, wellstate_nupcol);
1843 computePerfRate(int_quant, mob, bhp, Tw, perf, allow_cf, cq_s,
1844 perf_rates, deferred_logger);
1845 // TODO: make area a member
1846 const double area = 2 * M_PI * this->perf_rep_radius_[perf] * this->perf_length_[perf];
1847 const auto& material_law_manager = simulator.problem().materialLawManager();
1848 const auto& scaled_drainage_info =
1849 material_law_manager->oilWaterScaledEpsInfoDrainage(cell_idx);
1850 const double swcr = scaled_drainage_info.Swcr;
1851 const EvalWell poro = this->extendEval(int_quant.porosity());
1852 const EvalWell sw = this->extendEval(int_quant.fluidState().saturation(FluidSystem::waterPhaseIdx));
1853 // guard against zero porosity and no water
1854 const EvalWell denom = max( (area * poro * (sw - swcr)), 1e-12);
1855 const unsigned waterCompIdx = Indices::canonicalToActiveComponentIndex(FluidSystem::waterCompIdx);
1856 EvalWell water_velocity = cq_s[waterCompIdx] / denom * this->extendEval(int_quant.fluidState().invB(FluidSystem::waterPhaseIdx));
1857
1858 if (PolymerModule::hasShrate()) {
1859 // the equation for the water velocity conversion for the wells and reservoir are from different version
1860 // of implementation. It can be changed to be more consistent when possible.
1861 water_velocity *= PolymerModule::shrate( int_quant.pvtRegionIndex() ) / this->bore_diameters_[perf];
1862 }
1863 const EvalWell shear_factor = PolymerModule::computeShearFactor(polymer_concentration,
1864 int_quant.pvtRegionIndex(),
1865 water_velocity);
1866 // modify the mobility with the shear factor.
1867 mob[waterCompIdx] /= shear_factor;
1868 }
1869 }
1870
1871 template<typename TypeTag>
1872 void
1874 {
1875 this->linSys_.extract(jacobian);
1876 }
1877
1878
1879 template <typename TypeTag>
1880 void
1882 const BVector& weights,
1883 const int pressureVarIndex,
1884 const bool use_well_weights,
1885 const WellState<Scalar>& well_state) const
1886 {
1887 this->linSys_.extractCPRPressureMatrix(jacobian,
1888 weights,
1889 pressureVarIndex,
1890 use_well_weights,
1891 *this,
1892 Bhp,
1893 well_state);
1894 }
1895
1896
1897
1898 template<typename TypeTag>
1901 pskinwater(const double throughput,
1902 const EvalWell& water_velocity,
1903 DeferredLogger& deferred_logger) const
1904 {
1905 if constexpr (Base::has_polymermw) {
1906 const int water_table_id = this->polymerWaterTable_();
1907 if (water_table_id <= 0) {
1908 OPM_DEFLOG_THROW(std::runtime_error,
1909 fmt::format("Unused SKPRWAT table id used for well {}", name()),
1910 deferred_logger);
1911 }
1912 const auto& water_table_func = PolymerModule::getSkprwatTable(water_table_id);
1913 const EvalWell throughput_eval(this->primary_variables_.numWellEq() + Indices::numEq, throughput);
1914 // the skin pressure when injecting water, which also means the polymer concentration is zero
1915 EvalWell pskin_water(this->primary_variables_.numWellEq() + Indices::numEq, 0.0);
1916 pskin_water = water_table_func.eval(throughput_eval, water_velocity);
1917 return pskin_water;
1918 } else {
1919 OPM_DEFLOG_THROW(std::runtime_error,
1920 fmt::format("Polymermw is not activated, while injecting "
1921 "skin pressure is requested for well {}", name()),
1922 deferred_logger);
1923 }
1924 }
1925
1926
1927
1928
1929
1930 template<typename TypeTag>
1933 pskin(const double throughput,
1934 const EvalWell& water_velocity,
1935 const EvalWell& poly_inj_conc,
1936 DeferredLogger& deferred_logger) const
1937 {
1938 if constexpr (Base::has_polymermw) {
1939 const double sign = water_velocity >= 0. ? 1.0 : -1.0;
1940 const EvalWell water_velocity_abs = abs(water_velocity);
1941 if (poly_inj_conc == 0.) {
1942 return sign * pskinwater(throughput, water_velocity_abs, deferred_logger);
1943 }
1944 const int polymer_table_id = this->polymerTable_();
1945 if (polymer_table_id <= 0) {
1946 OPM_DEFLOG_THROW(std::runtime_error,
1947 fmt::format("Unavailable SKPRPOLY table id used for well {}", name()),
1948 deferred_logger);
1949 }
1950 const auto& skprpolytable = PolymerModule::getSkprpolyTable(polymer_table_id);
1951 const double reference_concentration = skprpolytable.refConcentration;
1952 const EvalWell throughput_eval(this->primary_variables_.numWellEq() + Indices::numEq, throughput);
1953 // the skin pressure when injecting water, which also means the polymer concentration is zero
1954 EvalWell pskin_poly(this->primary_variables_.numWellEq() + Indices::numEq, 0.0);
1955 pskin_poly = skprpolytable.table_func.eval(throughput_eval, water_velocity_abs);
1956 if (poly_inj_conc == reference_concentration) {
1957 return sign * pskin_poly;
1958 }
1959 // poly_inj_conc != reference concentration of the table, then some interpolation will be required
1960 const EvalWell pskin_water = pskinwater(throughput, water_velocity_abs, deferred_logger);
1961 const EvalWell pskin = pskin_water + (pskin_poly - pskin_water) / reference_concentration * poly_inj_conc;
1962 return sign * pskin;
1963 } else {
1964 OPM_DEFLOG_THROW(std::runtime_error,
1965 fmt::format("Polymermw is not activated, while injecting "
1966 "skin pressure is requested for well {}", name()),
1967 deferred_logger);
1968 }
1969 }
1970
1971
1972
1973
1974
1975 template<typename TypeTag>
1978 wpolymermw(const double throughput,
1979 const EvalWell& water_velocity,
1980 DeferredLogger& deferred_logger) const
1981 {
1982 if constexpr (Base::has_polymermw) {
1983 const int table_id = this->polymerInjTable_();
1984 const auto& table_func = PolymerModule::getPlymwinjTable(table_id);
1985 const EvalWell throughput_eval(this->primary_variables_.numWellEq() + Indices::numEq, throughput);
1986 EvalWell molecular_weight(this->primary_variables_.numWellEq() + Indices::numEq, 0.);
1987 if (this->wpolymer() == 0.) { // not injecting polymer
1988 return molecular_weight;
1989 }
1990 molecular_weight = table_func.eval(throughput_eval, abs(water_velocity));
1991 return molecular_weight;
1992 } else {
1993 OPM_DEFLOG_THROW(std::runtime_error,
1994 fmt::format("Polymermw is not activated, while injecting "
1995 "polymer molecular weight is requested for well {}", name()),
1996 deferred_logger);
1997 }
1998 }
1999
2000
2001
2002
2003
2004 template<typename TypeTag>
2005 void
2007 updateWaterThroughput(const double dt, WellState<Scalar>& well_state) const
2008 {
2009 if constexpr (Base::has_polymermw) {
2010 if (this->isInjector()) {
2011 auto& ws = well_state.well(this->index_of_well_);
2012 auto& perf_water_throughput = ws.perf_data.water_throughput;
2013 for (int perf = 0; perf < this->number_of_perforations_; ++perf) {
2014 const double perf_water_vel = this->primary_variables_.value(Bhp + 1 + perf);
2015 // we do not consider the formation damage due to water flowing from reservoir into wellbore
2016 if (perf_water_vel > 0.) {
2017 perf_water_throughput[perf] += perf_water_vel * dt;
2018 }
2019 }
2020 }
2021 }
2022 }
2023
2024
2025
2026
2027
2028 template<typename TypeTag>
2029 void
2031 handleInjectivityRate(const Simulator& simulator,
2032 const int perf,
2033 std::vector<EvalWell>& cq_s) const
2034 {
2035 const int cell_idx = this->well_cells_[perf];
2036 const auto& int_quants = simulator.model().intensiveQuantities(cell_idx, /*timeIdx=*/ 0);
2037 const auto& fs = int_quants.fluidState();
2038 const EvalWell b_w = this->extendEval(fs.invB(FluidSystem::waterPhaseIdx));
2039 const double area = M_PI * this->bore_diameters_[perf] * this->perf_length_[perf];
2040 const int wat_vel_index = Bhp + 1 + perf;
2041 const unsigned water_comp_idx = Indices::canonicalToActiveComponentIndex(FluidSystem::waterCompIdx);
2042
2043 // water rate is update to use the form from water velocity, since water velocity is
2044 // a primary variable now
2045 cq_s[water_comp_idx] = area * this->primary_variables_.eval(wat_vel_index) * b_w;
2046 }
2047
2048
2049
2050
2051 template<typename TypeTag>
2052 void
2054 handleInjectivityEquations(const Simulator& simulator,
2055 const WellState<Scalar>& well_state,
2056 const int perf,
2057 const EvalWell& water_flux_s,
2058 DeferredLogger& deferred_logger)
2059 {
2060 const int cell_idx = this->well_cells_[perf];
2061 const auto& int_quants = simulator.model().intensiveQuantities(cell_idx, /*timeIdx=*/ 0);
2062 const auto& fs = int_quants.fluidState();
2063 const EvalWell b_w = this->extendEval(fs.invB(FluidSystem::waterPhaseIdx));
2064 const EvalWell water_flux_r = water_flux_s / b_w;
2065 const double area = M_PI * this->bore_diameters_[perf] * this->perf_length_[perf];
2066 const EvalWell water_velocity = water_flux_r / area;
2067 const int wat_vel_index = Bhp + 1 + perf;
2068
2069 // equation for the water velocity
2070 const EvalWell eq_wat_vel = this->primary_variables_.eval(wat_vel_index) - water_velocity;
2071
2072 const auto& ws = well_state.well(this->index_of_well_);
2073 const auto& perf_data = ws.perf_data;
2074 const auto& perf_water_throughput = perf_data.water_throughput;
2075 const double throughput = perf_water_throughput[perf];
2076 const int pskin_index = Bhp + 1 + this->number_of_perforations_ + perf;
2077
2078 EvalWell poly_conc(this->primary_variables_.numWellEq() + Indices::numEq, 0.0);
2079 poly_conc.setValue(this->wpolymer());
2080
2081 // equation for the skin pressure
2082 const EvalWell eq_pskin = this->primary_variables_.eval(pskin_index)
2083 - pskin(throughput, this->primary_variables_.eval(wat_vel_index), poly_conc, deferred_logger);
2084
2086 assembleInjectivityEq(eq_pskin,
2087 eq_wat_vel,
2088 pskin_index,
2089 wat_vel_index,
2090 cell_idx,
2091 this->primary_variables_.numWellEq(),
2092 this->linSys_);
2093 }
2094
2095
2096
2097
2098
2099 template<typename TypeTag>
2100 void
2102 checkConvergenceExtraEqs(const std::vector<double>& res,
2103 ConvergenceReport& report) const
2104 {
2105 // if different types of extra equations are involved, this function needs to be refactored further
2106
2107 // checking the convergence of the extra equations related to polymer injectivity
2108 if constexpr (Base::has_polymermw) {
2109 WellConvergence(*this).
2110 checkConvergencePolyMW(res, Bhp, this->param_.max_residual_allowed_, report);
2111 }
2112 }
2113
2114
2115
2116
2117
2118 template<typename TypeTag>
2119 void
2121 updateConnectionRatePolyMW(const EvalWell& cq_s_poly,
2122 const IntensiveQuantities& int_quants,
2123 const WellState<Scalar>& well_state,
2124 const int perf,
2125 std::vector<RateVector>& connectionRates,
2126 DeferredLogger& deferred_logger) const
2127 {
2128 // the source term related to transport of molecular weight
2129 EvalWell cq_s_polymw = cq_s_poly;
2130 if (this->isInjector()) {
2131 const int wat_vel_index = Bhp + 1 + perf;
2132 const EvalWell water_velocity = this->primary_variables_.eval(wat_vel_index);
2133 if (water_velocity > 0.) { // injecting
2134 const auto& ws = well_state.well(this->index_of_well_);
2135 const auto& perf_water_throughput = ws.perf_data.water_throughput;
2136 const double throughput = perf_water_throughput[perf];
2137 const EvalWell molecular_weight = wpolymermw(throughput, water_velocity, deferred_logger);
2138 cq_s_polymw *= molecular_weight;
2139 } else {
2140 // we do not consider the molecular weight from the polymer
2141 // going-back to the wellbore through injector
2142 cq_s_polymw *= 0.;
2143 }
2144 } else if (this->isProducer()) {
2145 if (cq_s_polymw < 0.) {
2146 cq_s_polymw *= this->extendEval(int_quants.polymerMoleWeight() );
2147 } else {
2148 // we do not consider the molecular weight from the polymer
2149 // re-injecting back through producer
2150 cq_s_polymw *= 0.;
2151 }
2152 }
2153 connectionRates[perf][Indices::contiPolymerMWEqIdx] = Base::restrictEval(cq_s_polymw);
2154 }
2155
2156
2157
2158
2159
2160
2161 template<typename TypeTag>
2162 std::optional<double>
2165 const Simulator& simulator,
2166 const SummaryState& summary_state,
2167 DeferredLogger& deferred_logger) const
2168 {
2169 return computeBhpAtThpLimitProdWithAlq(simulator,
2170 summary_state,
2171 this->getALQ(well_state),
2172 deferred_logger);
2173 }
2174
2175 template<typename TypeTag>
2176 std::optional<double>
2179 const SummaryState& summary_state,
2180 const double alq_value,
2181 DeferredLogger& deferred_logger) const
2182 {
2183 // Make the frates() function.
2184 auto frates = [this, &simulator, &deferred_logger](const double bhp) {
2185 // Not solving the well equations here, which means we are
2186 // calculating at the current Fg/Fw values of the
2187 // well. This does not matter unless the well is
2188 // crossflowing, and then it is likely still a good
2189 // approximation.
2190 std::vector<double> rates(3);
2191 computeWellRatesWithBhp(simulator, bhp, rates, deferred_logger);
2192 this->adaptRatesForVFP(rates);
2193 return rates;
2194 };
2195
2196 double max_pressure = 0.0;
2197 for (int perf = 0; perf < this->number_of_perforations_; ++perf) {
2198 const int cell_idx = this->well_cells_[perf];
2199 const auto& int_quants = simulator.model().intensiveQuantities(cell_idx, /*timeIdx=*/ 0);
2200 const auto& fs = int_quants.fluidState();
2201 double pressure_cell = this->getPerfCellPressure(fs).value();
2202 max_pressure = std::max(max_pressure, pressure_cell);
2203 }
2204 auto bhpAtLimit = WellBhpThpCalculator(*this).computeBhpAtThpLimitProd(frates,
2205 summary_state,
2206 max_pressure,
2207 this->connections_.rho(),
2208 alq_value,
2209 this->getTHPConstraint(summary_state),
2210 deferred_logger);
2211
2212 if (bhpAtLimit) {
2213 auto v = frates(*bhpAtLimit);
2214 if (std::all_of(v.cbegin(), v.cend(), [](double i){ return i <= 0; }) ) {
2215 return bhpAtLimit;
2216 }
2217 }
2218
2219
2220 auto fratesIter = [this, &simulator, &deferred_logger](const double bhp) {
2221 // Solver the well iterations to see if we are
2222 // able to get a solution with an update
2223 // solution
2224 std::vector<double> rates(3);
2225 computeWellRatesWithBhpIterations(simulator, bhp, rates, deferred_logger);
2226 this->adaptRatesForVFP(rates);
2227 return rates;
2228 };
2229
2230 bhpAtLimit = WellBhpThpCalculator(*this).computeBhpAtThpLimitProd(fratesIter,
2231 summary_state,
2232 max_pressure,
2233 this->connections_.rho(),
2234 alq_value,
2235 this->getTHPConstraint(summary_state),
2236 deferred_logger);
2237
2238
2239 if (bhpAtLimit) {
2240 // should we use fratesIter here since fratesIter is used in computeBhpAtThpLimitProd above?
2241 auto v = frates(*bhpAtLimit);
2242 if (std::all_of(v.cbegin(), v.cend(), [](double i){ return i <= 0; }) ) {
2243 return bhpAtLimit;
2244 }
2245 }
2246
2247 // we still don't get a valied solution.
2248 return std::nullopt;
2249 }
2250
2251
2252
2253 template<typename TypeTag>
2254 std::optional<double>
2256 computeBhpAtThpLimitInj(const Simulator& simulator,
2257 const SummaryState& summary_state,
2258 DeferredLogger& deferred_logger) const
2259 {
2260 // Make the frates() function.
2261 auto frates = [this, &simulator, &deferred_logger](const double bhp) {
2262 // Not solving the well equations here, which means we are
2263 // calculating at the current Fg/Fw values of the
2264 // well. This does not matter unless the well is
2265 // crossflowing, and then it is likely still a good
2266 // approximation.
2267 std::vector<double> rates(3);
2268 computeWellRatesWithBhp(simulator, bhp, rates, deferred_logger);
2269 return rates;
2270 };
2271
2272 return WellBhpThpCalculator(*this).computeBhpAtThpLimitInj(frates,
2273 summary_state,
2274 this->connections_.rho(),
2275 1e-6,
2276 50,
2277 true,
2278 deferred_logger);
2279 }
2280
2281
2282
2283
2284
2285 template<typename TypeTag>
2286 bool
2288 iterateWellEqWithControl(const Simulator& simulator,
2289 const double dt,
2290 const Well::InjectionControls& inj_controls,
2291 const Well::ProductionControls& prod_controls,
2292 WellState<Scalar>& well_state,
2293 const GroupState<Scalar>& group_state,
2294 DeferredLogger& deferred_logger)
2295 {
2296 const int max_iter = this->param_.max_inner_iter_wells_;
2297 int it = 0;
2298 bool converged;
2299 bool relax_convergence = false;
2300 this->regularize_ = false;
2301 const auto& summary_state = simulator.vanguard().summaryState();
2302 do {
2303 assembleWellEqWithoutIteration(simulator, dt, inj_controls, prod_controls, well_state, group_state, deferred_logger);
2304
2305 if (it > this->param_.strict_inner_iter_wells_) {
2306 relax_convergence = true;
2307 this->regularize_ = true;
2308 }
2309
2310 auto report = getWellConvergence(summary_state, well_state, Base::B_avg_, deferred_logger, relax_convergence);
2311
2312 converged = report.converged();
2313 if (converged) {
2314 break;
2315 }
2316
2317 ++it;
2318 solveEqAndUpdateWellState(summary_state, well_state, deferred_logger);
2319
2320 // TODO: when this function is used for well testing purposes, will need to check the controls, so that we will obtain convergence
2321 // under the most restrictive control. Based on this converged results, we can check whether to re-open the well. Either we refactor
2322 // this function or we use different functions for the well testing purposes.
2323 // We don't allow for switching well controls while computing well potentials and testing wells
2324 // updateWellControl(simulator, well_state, deferred_logger);
2325 initPrimaryVariablesEvaluation();
2326 } while (it < max_iter);
2327
2328 return converged;
2329 }
2330
2331
2332 template<typename TypeTag>
2333 bool
2335 iterateWellEqWithSwitching(const Simulator& simulator,
2336 const double dt,
2337 const Well::InjectionControls& inj_controls,
2338 const Well::ProductionControls& prod_controls,
2339 WellState<Scalar>& well_state,
2340 const GroupState<Scalar>& group_state,
2341 DeferredLogger& deferred_logger,
2342 const bool fixed_control /*false*/,
2343 const bool fixed_status /*false*/)
2344 {
2345 const int max_iter = this->param_.max_inner_iter_wells_;
2346 int it = 0;
2347 bool converged = false;
2348 bool relax_convergence = false;
2349 this->regularize_ = false;
2350 const auto& summary_state = simulator.vanguard().summaryState();
2351
2352 // Always take a few (more than one) iterations after a switch before allowing a new switch
2353 // The optimal number here is subject to further investigation, but it has been observerved
2354 // that unless this number is >1, we may get stuck in a cycle
2355 constexpr int min_its_after_switch = 4;
2356 int its_since_last_switch = min_its_after_switch;
2357 int switch_count= 0;
2358 // if we fail to solve eqs, we reset status/operability before leaving
2359 const auto well_status_orig = this->wellStatus_;
2360 const auto operability_orig = this->operability_status_;
2361 auto well_status_cur = well_status_orig;
2362 int status_switch_count = 0;
2363 // don't allow opening wells that are stopped from schedule or has a stopped well state
2364 const bool allow_open = this->well_ecl_.getStatus() == WellStatus::OPEN &&
2365 well_state.well(this->index_of_well_).status == WellStatus::OPEN;
2366 // don't allow switcing for wells under zero rate target or requested fixed status and control
2367 const bool allow_switching = !this->wellUnderZeroRateTarget(summary_state, well_state) &&
2368 (!fixed_control || !fixed_status) && allow_open;
2369 bool changed = false;
2370 bool final_check = false;
2371 // well needs to be set operable or else solving/updating of re-opened wells is skipped
2372 this->operability_status_.resetOperability();
2373 this->operability_status_.solvable = true;
2374 do {
2375 its_since_last_switch++;
2376 if (allow_switching && its_since_last_switch >= min_its_after_switch){
2377 const double wqTotal = this->primary_variables_.eval(WQTotal).value();
2378 changed = this->updateWellControlAndStatusLocalIteration(simulator, well_state, group_state, inj_controls, prod_controls, wqTotal, deferred_logger, fixed_control, fixed_status);
2379 if (changed){
2380 its_since_last_switch = 0;
2381 switch_count++;
2382 if (well_status_cur != this->wellStatus_) {
2383 well_status_cur = this->wellStatus_;
2384 status_switch_count++;
2385 }
2386 }
2387 if (!changed && final_check) {
2388 break;
2389 } else {
2390 final_check = false;
2391 }
2392 }
2393
2394 assembleWellEqWithoutIteration(simulator, dt, inj_controls, prod_controls, well_state, group_state, deferred_logger);
2395
2396 if (it > this->param_.strict_inner_iter_wells_) {
2397 relax_convergence = true;
2398 this->regularize_ = true;
2399 }
2400
2401 auto report = getWellConvergence(summary_state, well_state, Base::B_avg_, deferred_logger, relax_convergence);
2402
2403 converged = report.converged();
2404 if (converged) {
2405 // if equations are sufficiently linear they might converge in less than min_its_after_switch
2406 // in this case, make sure all constraints are satisfied before returning
2407 if (switch_count > 0 && its_since_last_switch < min_its_after_switch) {
2408 final_check = true;
2409 its_since_last_switch = min_its_after_switch;
2410 } else {
2411 break;
2412 }
2413 }
2414
2415 ++it;
2416 solveEqAndUpdateWellState(summary_state, well_state, deferred_logger);
2417 initPrimaryVariablesEvaluation();
2418
2419 } while (it < max_iter);
2420
2421 if (converged) {
2422 if (allow_switching){
2423 // update operability if status change
2424 const bool is_stopped = this->wellIsStopped();
2425 if (this->wellHasTHPConstraints(summary_state)){
2426 this->operability_status_.can_obtain_bhp_with_thp_limit = !is_stopped;
2427 this->operability_status_.obey_thp_limit_under_bhp_limit = !is_stopped;
2428 } else {
2429 this->operability_status_.operable_under_only_bhp_limit = !is_stopped;
2430 }
2431 }
2432 } else {
2433 this->wellStatus_ = well_status_orig;
2434 this->operability_status_ = operability_orig;
2435 const std::string message = fmt::format(" Well {} did not converge in {} inner iterations ("
2436 "{} switches, {} status changes).", this->name(), it, switch_count, status_switch_count);
2437 deferred_logger.debug(message);
2438 // add operability here as well ?
2439 }
2440 return converged;
2441 }
2442
2443 template<typename TypeTag>
2444 std::vector<double>
2446 computeCurrentWellRates(const Simulator& simulator,
2447 DeferredLogger& deferred_logger) const
2448 {
2449 // Calculate the rates that follow from the current primary variables.
2450 std::vector<double> well_q_s(this->num_components_, 0.);
2451 const EvalWell& bhp = this->primary_variables_.eval(Bhp);
2452 const bool allow_cf = this->getAllowCrossFlow() || openCrossFlowAvoidSingularity(simulator);
2453 for (int perf = 0; perf < this->number_of_perforations_; ++perf) {
2454 const int cell_idx = this->well_cells_[perf];
2455 const auto& intQuants = simulator.model().intensiveQuantities(cell_idx, /*timeIdx=*/ 0);
2456 std::vector<Scalar> mob(this->num_components_, 0.);
2457 getMobility(simulator, perf, mob, deferred_logger);
2458 std::vector<Scalar> cq_s(this->num_components_, 0.);
2459 double trans_mult = simulator.problem().template wellTransMultiplier<double>(intQuants, cell_idx);
2460 const auto& wellstate_nupcol = simulator.problem().wellModel().nupcolWellState().well(this->index_of_well_);
2461 const std::vector<Scalar> Tw = this->wellIndex(perf, intQuants, trans_mult, wellstate_nupcol);
2462 PerforationRates perf_rates;
2463 computePerfRate(intQuants, mob, bhp.value(), Tw, perf, allow_cf,
2464 cq_s, perf_rates, deferred_logger);
2465 for (int comp = 0; comp < this->num_components_; ++comp) {
2466 well_q_s[comp] += cq_s[comp];
2467 }
2468 }
2469 const auto& comm = this->parallel_well_info_.communication();
2470 if (comm.size() > 1)
2471 {
2472 comm.sum(well_q_s.data(), well_q_s.size());
2473 }
2474 return well_q_s;
2475 }
2476
2477
2478
2479 template <typename TypeTag>
2480 std::vector<double>
2482 getPrimaryVars() const
2483 {
2484 const int num_pri_vars = this->primary_variables_.numWellEq();
2485 std::vector<double> retval(num_pri_vars);
2486 for (int ii = 0; ii < num_pri_vars; ++ii) {
2487 retval[ii] = this->primary_variables_.value(ii);
2488 }
2489 return retval;
2490 }
2491
2492
2493
2494
2495
2496 template <typename TypeTag>
2497 int
2499 setPrimaryVars(std::vector<double>::const_iterator it)
2500 {
2501 const int num_pri_vars = this->primary_variables_.numWellEq();
2502 for (int ii = 0; ii < num_pri_vars; ++ii) {
2503 this->primary_variables_.setValue(ii, it[ii]);
2504 }
2505 return num_pri_vars;
2506 }
2507
2508
2509 template <typename TypeTag>
2512 connectionRateEnergy(const double maxOilSaturation,
2513 const std::vector<EvalWell>& cq_s,
2514 const IntensiveQuantities& intQuants,
2515 DeferredLogger& deferred_logger) const
2516 {
2517 auto fs = intQuants.fluidState();
2518 Eval result = 0;
2519 for (unsigned phaseIdx = 0; phaseIdx < FluidSystem::numPhases; ++phaseIdx) {
2520 if (!FluidSystem::phaseIsActive(phaseIdx)) {
2521 continue;
2522 }
2523
2524 // convert to reservoir conditions
2525 EvalWell cq_r_thermal(this->primary_variables_.numWellEq() + Indices::numEq, 0.);
2526 const unsigned activeCompIdx = Indices::canonicalToActiveComponentIndex(FluidSystem::solventComponentIndex(phaseIdx));
2527 const bool both_oil_gas = FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx) && FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx);
2528 if (!both_oil_gas || FluidSystem::waterPhaseIdx == phaseIdx) {
2529 cq_r_thermal = cq_s[activeCompIdx] / this->extendEval(fs.invB(phaseIdx));
2530 } else {
2531 // remove dissolved gas and vapporized oil
2532 const unsigned oilCompIdx = Indices::canonicalToActiveComponentIndex(FluidSystem::oilCompIdx);
2533 const unsigned gasCompIdx = Indices::canonicalToActiveComponentIndex(FluidSystem::gasCompIdx);
2534 // q_os = q_or * b_o + rv * q_gr * b_g
2535 // q_gs = q_gr * g_g + rs * q_or * b_o
2536 // q_gr = 1 / (b_g * d) * (q_gs - rs * q_os)
2537 // d = 1.0 - rs * rv
2538 const EvalWell d = this->extendEval(1.0 - fs.Rv() * fs.Rs());
2539 if (d <= 0.0) {
2540 deferred_logger.debug(
2541 fmt::format("Problematic d value {} obtained for well {}"
2542 " during calculateSinglePerf with rs {}"
2543 ", rv {}. Continue as if no dissolution (rs = 0) and"
2544 " vaporization (rv = 0) for this connection.",
2545 d, this->name(), fs.Rs(), fs.Rv()));
2546 cq_r_thermal = cq_s[activeCompIdx] / this->extendEval(fs.invB(phaseIdx));
2547 } else {
2548 if (FluidSystem::gasPhaseIdx == phaseIdx) {
2549 cq_r_thermal = (cq_s[gasCompIdx] -
2550 this->extendEval(fs.Rs()) * cq_s[oilCompIdx]) /
2551 (d * this->extendEval(fs.invB(phaseIdx)) );
2552 } else if (FluidSystem::oilPhaseIdx == phaseIdx) {
2553 // q_or = 1 / (b_o * d) * (q_os - rv * q_gs)
2554 cq_r_thermal = (cq_s[oilCompIdx] - this->extendEval(fs.Rv()) *
2555 cq_s[gasCompIdx]) /
2556 (d * this->extendEval(fs.invB(phaseIdx)) );
2557 }
2558 }
2559 }
2560
2561 // change temperature for injecting fluids
2562 if (this->isInjector() && cq_s[activeCompIdx] > 0.0){
2563 // only handles single phase injection now
2564 assert(this->well_ecl_.injectorType() != InjectorType::MULTI);
2565 fs.setTemperature(this->well_ecl_.temperature());
2566 typedef typename std::decay<decltype(fs)>::type::Scalar FsScalar;
2567 typename FluidSystem::template ParameterCache<FsScalar> paramCache;
2568 const unsigned pvtRegionIdx = intQuants.pvtRegionIndex();
2569 paramCache.setRegionIndex(pvtRegionIdx);
2570 paramCache.setMaxOilSat(maxOilSaturation);
2571 paramCache.updatePhase(fs, phaseIdx);
2572
2573 const auto& rho = FluidSystem::density(fs, paramCache, phaseIdx);
2574 fs.setDensity(phaseIdx, rho);
2575 const auto& h = FluidSystem::enthalpy(fs, paramCache, phaseIdx);
2576 fs.setEnthalpy(phaseIdx, h);
2577 cq_r_thermal *= this->extendEval(fs.enthalpy(phaseIdx)) * this->extendEval(fs.density(phaseIdx));
2578 result += getValue(cq_r_thermal);
2579 } else {
2580 // compute the thermal flux
2581 cq_r_thermal *= this->extendEval(fs.enthalpy(phaseIdx)) * this->extendEval(fs.density(phaseIdx));
2582 result += Base::restrictEval(cq_r_thermal);
2583 }
2584 }
2585
2586 return result;
2587 }
2588
2589
2590 template <typename TypeTag>
2591 template<class Value>
2592 void
2593 StandardWell<TypeTag>::
2594 gasOilPerfRateInj(const std::vector<Value>& cq_s,
2595 PerforationRates& perf_rates,
2596 const Value& rv,
2597 const Value& rs,
2598 const Value& pressure,
2599 const Value& rvw,
2600 DeferredLogger& deferred_logger) const
2601 {
2602 const unsigned oilCompIdx = Indices::canonicalToActiveComponentIndex(FluidSystem::oilCompIdx);
2603 const unsigned gasCompIdx = Indices::canonicalToActiveComponentIndex(FluidSystem::gasCompIdx);
2604 // TODO: the formulations here remain to be tested with cases with strong crossflow through production wells
2605 // s means standard condition, r means reservoir condition
2606 // q_os = q_or * b_o + rv * q_gr * b_g
2607 // q_gs = q_gr * b_g + rs * q_or * b_o
2608 // d = 1.0 - rs * rv
2609 // q_or = 1 / (b_o * d) * (q_os - rv * q_gs)
2610 // q_gr = 1 / (b_g * d) * (q_gs - rs * q_os)
2611
2612 const double d = 1.0 - getValue(rv) * getValue(rs);
2613
2614 if (d <= 0.0) {
2615 deferred_logger.debug(dValueError(d, this->name(),
2616 "gasOilPerfRateInj",
2617 rs, rv, pressure));
2618 } else {
2619 // vaporized oil into gas
2620 // rv * q_gr * b_g = rv * (q_gs - rs * q_os) / d
2621 perf_rates.vap_oil = getValue(rv) * (getValue(cq_s[gasCompIdx]) - getValue(rs) * getValue(cq_s[oilCompIdx])) / d;
2622 // dissolved of gas in oil
2623 // rs * q_or * b_o = rs * (q_os - rv * q_gs) / d
2624 perf_rates.dis_gas = getValue(rs) * (getValue(cq_s[oilCompIdx]) - getValue(rv) * getValue(cq_s[gasCompIdx])) / d;
2625 }
2626
2627 if (FluidSystem::phaseIsActive(FluidSystem::waterPhaseIdx)) {
2628 // q_ws = q_wr * b_w + rvw * q_gr * b_g
2629 // q_wr = 1 / b_w * (q_ws - rvw * q_gr * b_g) = 1 / b_w * (q_ws - rvw * 1 / d (q_gs - rs * q_os))
2630 // vaporized water in gas
2631 // rvw * q_gr * b_g = q_ws -q_wr *b_w = rvw * (q_gs -rs *q_os) / d
2632 perf_rates.vap_wat = getValue(rvw) * (getValue(cq_s[gasCompIdx]) - getValue(rs) * getValue(cq_s[oilCompIdx])) / d;
2633 }
2634 }
2635
2636
2637
2638 template <typename TypeTag>
2639 template<class Value>
2640 void
2641 StandardWell<TypeTag>::
2642 gasOilPerfRateProd(std::vector<Value>& cq_s,
2643 PerforationRates& perf_rates,
2644 const Value& rv,
2645 const Value& rs,
2646 const Value& rvw) const
2647 {
2648 const unsigned oilCompIdx = Indices::canonicalToActiveComponentIndex(FluidSystem::oilCompIdx);
2649 const unsigned gasCompIdx = Indices::canonicalToActiveComponentIndex(FluidSystem::gasCompIdx);
2650 const Value cq_sOil = cq_s[oilCompIdx];
2651 const Value cq_sGas = cq_s[gasCompIdx];
2652 const Value dis_gas = rs * cq_sOil;
2653 const Value vap_oil = rv * cq_sGas;
2654
2655 cq_s[gasCompIdx] += dis_gas;
2656 cq_s[oilCompIdx] += vap_oil;
2657
2658 // recording the perforation solution gas rate and solution oil rates
2659 if (this->isProducer()) {
2660 perf_rates.dis_gas = getValue(dis_gas);
2661 perf_rates.vap_oil = getValue(vap_oil);
2662 }
2663
2664 if (FluidSystem::phaseIsActive(FluidSystem::waterPhaseIdx)) {
2665 const unsigned waterCompIdx = Indices::canonicalToActiveComponentIndex(FluidSystem::waterCompIdx);
2666 const Value vap_wat = rvw * cq_sGas;
2667 cq_s[waterCompIdx] += vap_wat;
2668 if (this->isProducer())
2669 perf_rates.vap_wat = getValue(vap_wat);
2670 }
2671 }
2672
2673
2674 template <typename TypeTag>
2675 template<class Value>
2676 void
2677 StandardWell<TypeTag>::
2678 gasWaterPerfRateProd(std::vector<Value>& cq_s,
2679 PerforationRates& perf_rates,
2680 const Value& rvw,
2681 const Value& rsw) const
2682 {
2683 const unsigned waterCompIdx = Indices::canonicalToActiveComponentIndex(FluidSystem::waterCompIdx);
2684 const unsigned gasCompIdx = Indices::canonicalToActiveComponentIndex(FluidSystem::gasCompIdx);
2685 const Value cq_sWat = cq_s[waterCompIdx];
2686 const Value cq_sGas = cq_s[gasCompIdx];
2687 const Value vap_wat = rvw * cq_sGas;
2688 const Value dis_gas_wat = rsw * cq_sWat;
2689 cq_s[waterCompIdx] += vap_wat;
2690 cq_s[gasCompIdx] += dis_gas_wat;
2691 if (this->isProducer()) {
2692 perf_rates.vap_wat = getValue(vap_wat);
2693 perf_rates.dis_gas_in_water = getValue(dis_gas_wat);
2694 }
2695 }
2696
2697
2698 template <typename TypeTag>
2699 template<class Value>
2700 void
2701 StandardWell<TypeTag>::
2702 gasWaterPerfRateInj(const std::vector<Value>& cq_s,
2703 PerforationRates& perf_rates,
2704 const Value& rvw,
2705 const Value& rsw,
2706 const Value& pressure,
2707 DeferredLogger& deferred_logger) const
2708
2709 {
2710 const unsigned gasCompIdx = Indices::canonicalToActiveComponentIndex(FluidSystem::gasCompIdx);
2711 const unsigned waterCompIdx = Indices::canonicalToActiveComponentIndex(FluidSystem::waterCompIdx);
2712
2713 const double dw = 1.0 - getValue(rvw) * getValue(rsw);
2714
2715 if (dw <= 0.0) {
2716 deferred_logger.debug(dValueError(dw, this->name(),
2717 "gasWaterPerfRateInj",
2718 rsw, rvw, pressure));
2719 } else {
2720 // vaporized water into gas
2721 // rvw * q_gr * b_g = rvw * (q_gs - rsw * q_ws) / dw
2722 perf_rates.vap_wat = getValue(rvw) * (getValue(cq_s[gasCompIdx]) - getValue(rsw) * getValue(cq_s[waterCompIdx])) / dw;
2723 // dissolved gas in water
2724 // rsw * q_wr * b_w = rsw * (q_ws - rvw * q_gs) / dw
2725 perf_rates.dis_gas_in_water = getValue(rsw) * (getValue(cq_s[waterCompIdx]) - getValue(rvw) * getValue(cq_s[gasCompIdx])) / dw;
2726 }
2727 }
2728
2729
2730 template <typename TypeTag>
2731 template<class Value>
2732 void
2733 StandardWell<TypeTag>::
2734 disOilVapWatVolumeRatio(Value& volumeRatio,
2735 const Value& rvw,
2736 const Value& rsw,
2737 const Value& pressure,
2738 const std::vector<Value>& cmix_s,
2739 const std::vector<Value>& b_perfcells_dense,
2740 DeferredLogger& deferred_logger) const
2741 {
2742 const unsigned waterCompIdx = Indices::canonicalToActiveComponentIndex(FluidSystem::waterCompIdx);
2743 const unsigned gasCompIdx = Indices::canonicalToActiveComponentIndex(FluidSystem::gasCompIdx);
2744 // Incorporate RSW/RVW factors if both water and gas active
2745 const Value d = 1.0 - rvw * rsw;
2746
2747 if (d <= 0.0) {
2748 deferred_logger.debug(dValueError(d, this->name(),
2749 "disOilVapWatVolumeRatio",
2750 rsw, rvw, pressure));
2751 }
2752 const Value tmp_wat = d > 0.0 ? (cmix_s[waterCompIdx] - rvw * cmix_s[gasCompIdx]) / d
2753 : cmix_s[waterCompIdx];
2754 volumeRatio += tmp_wat / b_perfcells_dense[waterCompIdx];
2755
2756 const Value tmp_gas = d > 0.0 ? (cmix_s[gasCompIdx] - rsw * cmix_s[waterCompIdx]) / d
2757 : cmix_s[waterCompIdx];
2758 volumeRatio += tmp_gas / b_perfcells_dense[gasCompIdx];
2759 }
2760
2761
2762 template <typename TypeTag>
2763 template<class Value>
2764 void
2765 StandardWell<TypeTag>::
2766 gasOilVolumeRatio(Value& volumeRatio,
2767 const Value& rv,
2768 const Value& rs,
2769 const Value& pressure,
2770 const std::vector<Value>& cmix_s,
2771 const std::vector<Value>& b_perfcells_dense,
2772 DeferredLogger& deferred_logger) const
2773 {
2774 const unsigned oilCompIdx = Indices::canonicalToActiveComponentIndex(FluidSystem::oilCompIdx);
2775 const unsigned gasCompIdx = Indices::canonicalToActiveComponentIndex(FluidSystem::gasCompIdx);
2776 // Incorporate RS/RV factors if both oil and gas active
2777 const Value d = 1.0 - rv * rs;
2778
2779 if (d <= 0.0) {
2780 deferred_logger.debug(dValueError(d, this->name(),
2781 "gasOilVolumeRatio",
2782 rs, rv, pressure));
2783 }
2784 const Value tmp_oil = d > 0.0? (cmix_s[oilCompIdx] - rv * cmix_s[gasCompIdx]) / d : cmix_s[oilCompIdx];
2785 volumeRatio += tmp_oil / b_perfcells_dense[oilCompIdx];
2786
2787 const Value tmp_gas = d > 0.0? (cmix_s[gasCompIdx] - rs * cmix_s[oilCompIdx]) / d : cmix_s[gasCompIdx];
2788 volumeRatio += tmp_gas / b_perfcells_dense[gasCompIdx];
2789 }
2790
2791} // namespace Opm
#define OPM_DEFLOG_THROW(Exception, message, deferred_logger)
Definition: DeferredLoggingErrorHelpers.hpp:45
#define OPM_DEFLOG_PROBLEM(Exception, message, deferred_logger)
Definition: DeferredLoggingErrorHelpers.hpp:61
Definition: ConvergenceReport.hpp:38
Definition: DeferredLogger.hpp:57
void warning(const std::string &tag, const std::string &message)
void debug(const std::string &tag, const std::string &message)
Definition: GroupState.hpp:35
Class encapsulating some information about parallel wells.
Definition: ParallelWellInfo.hpp:184
Class handling assemble of the equation system for StandardWell.
Definition: StandardWellAssemble.hpp:43
PrimaryVariables primary_variables_
Primary variables for well.
Definition: StandardWellEval.hpp:101
Definition: StandardWell.hpp:59
void computeWellConnectionPressures(const Simulator &simulator, const WellState< Scalar > &well_state, DeferredLogger &deferred_logger)
Definition: StandardWell_impl.hpp:1340
typename StdWellEval::EvalWell EvalWell
Definition: StandardWell.hpp:120
typename StdWellEval::BVectorWell BVectorWell
Definition: StandardWell.hpp:121
void checkOperabilityUnderBHPLimit(const WellState< Scalar > &well_state, const Simulator &simulator, DeferredLogger &deferred_logger) override
Definition: StandardWell_impl.hpp:925
bool iterateWellEqWithControl(const Simulator &simulator, const double dt, const Well::InjectionControls &inj_controls, const Well::ProductionControls &prod_controls, WellState< Scalar > &well_state, const GroupState< Scalar > &group_state, DeferredLogger &deferred_logger) override
Definition: StandardWell_impl.hpp:2288
std::vector< double > getPrimaryVars() const override
Definition: StandardWell_impl.hpp:2482
std::optional< double > computeBhpAtThpLimitProdWithAlq(const Simulator &simulator, const SummaryState &summary_state, const double alq_value, DeferredLogger &deferred_logger) const override
Definition: StandardWell_impl.hpp:2178
double connectionDensity(const int globalConnIdx, const int openConnIdx) const override
Definition: StandardWell_impl.hpp:1760
void updatePrimaryVariables(const SummaryState &summary_state, const WellState< Scalar > &well_state, DeferredLogger &deferred_logger) override
Definition: StandardWell_impl.hpp:1775
void addWellContributions(SparseMatrixAdapter &mat) const override
Definition: StandardWell_impl.hpp:1873
void updateProductivityIndex(const Simulator &simulator, const WellProdIndexCalculator &wellPICalc, WellState< Scalar > &well_state, DeferredLogger &deferred_logger) const override
Definition: StandardWell_impl.hpp:1222
EvalWell pskinwater(const double throughput, const EvalWell &water_velocity, DeferredLogger &deferred_logger) const
Definition: StandardWell_impl.hpp:1901
void initPrimaryVariablesEvaluation() override
Definition: StandardWell_impl.hpp:113
void checkConvergenceExtraEqs(const std::vector< double > &res, ConvergenceReport &report) const
Definition: StandardWell_impl.hpp:2102
void updateIPRImplicit(const Simulator &simulator, WellState< Scalar > &well_state, DeferredLogger &deferred_logger) override
Definition: StandardWell_impl.hpp:849
std::vector< double > computeWellPotentialWithTHP(const Simulator &simulator, DeferredLogger &deferred_logger, const WellState< Scalar > &well_state) const
Definition: StandardWell_impl.hpp:1561
void computePropertiesForWellConnectionPressures(const Simulator &simulator, const WellState< Scalar > &well_state, WellConnectionProps &props) const
Definition: StandardWell_impl.hpp:1132
EvalWell pskin(const double throuhgput, const EvalWell &water_velocity, const EvalWell &poly_inj_conc, DeferredLogger &deferred_logger) const
Definition: StandardWell_impl.hpp:1933
void updateWaterMobilityWithPolymer(const Simulator &simulator, const int perf, std::vector< EvalWell > &mob_water, DeferredLogger &deferred_logger) const
Definition: StandardWell_impl.hpp:1809
bool iterateWellEqWithSwitching(const Simulator &simulator, const double dt, const Well::InjectionControls &inj_controls, const Well::ProductionControls &prod_controls, WellState< Scalar > &well_state, const GroupState< Scalar > &group_state, DeferredLogger &deferred_logger, const bool fixed_control=false, const bool fixed_status=false) override
Definition: StandardWell_impl.hpp:2335
void updateWaterThroughput(const double dt, WellState< Scalar > &well_state) const override
Definition: StandardWell_impl.hpp:2007
int setPrimaryVars(std::vector< double >::const_iterator it) override
Definition: StandardWell_impl.hpp:2499
std::optional< double > computeBhpAtThpLimitInj(const Simulator &simulator, const SummaryState &summary_state, DeferredLogger &deferred_logger) const
Definition: StandardWell_impl.hpp:2256
void updatePrimaryVariablesNewton(const BVectorWell &dwells, const bool stop_or_zero_rate_target, DeferredLogger &deferred_logger)
Definition: StandardWell_impl.hpp:716
void calculateExplicitQuantities(const Simulator &simulator, const WellState< Scalar > &well_state, DeferredLogger &deferred_logger) override
Definition: StandardWell_impl.hpp:1382
void computeWellRatesWithThpAlqProd(const Simulator &simulator, const SummaryState &summary_state, DeferredLogger &deferred_logger, std::vector< double > &potentials, double alq) const
Definition: StandardWell_impl.hpp:1686
std::vector< double > computeCurrentWellRates(const Simulator &simulator, DeferredLogger &deferred_logger) const override
Definition: StandardWell_impl.hpp:2446
StandardWell(const Well &well, const ParallelWellInfo &pw_info, const int time_step, const ModelParameters &param, const RateConverterType &rate_converter, const int pvtRegionIdx, const int num_components, const int num_phases, const int index_of_well, const std::vector< PerforationData > &perf_data)
Definition: StandardWell_impl.hpp:72
double computeWellRatesAndBhpWithThpAlqProd(const Simulator &simulator, const SummaryState &summary_state, DeferredLogger &deferred_logger, std::vector< double > &potentials, double alq) const
Definition: StandardWell_impl.hpp:1658
typename StdWellEval::StdWellConnections::Properties WellConnectionProps
Definition: StandardWell.hpp:273
double getRefDensity() const override
Definition: StandardWell_impl.hpp:1798
void updateWellState(const SummaryState &summary_state, const BVectorWell &dwells, WellState< Scalar > &well_state, DeferredLogger &deferred_logger)
Definition: StandardWell_impl.hpp:695
void getMobility(const Simulator &simulator, const int perf, std::vector< Value > &mob, DeferredLogger &deferred_logger) const
Definition: StandardWell_impl.hpp:639
virtual void init(const PhaseUsage *phase_usage_arg, const std::vector< double > &depth_arg, const double gravity_arg, const int num_cells, const std::vector< Scalar > &B_avg, const bool changed_to_open_this_step) override
Definition: StandardWell_impl.hpp:96
void computePerfRate(const IntensiveQuantities &intQuants, const std::vector< Value > &mob, const Value &bhp, const std::vector< Scalar > &Tw, const int perf, const bool allow_cf, std::vector< Value > &cq_s, PerforationRates &perf_rates, DeferredLogger &deferred_logger) const
Definition: StandardWell_impl.hpp:126
void updateIPR(const Simulator &simulator, DeferredLogger &deferred_logger) const override
Definition: StandardWell_impl.hpp:759
void assembleWellEqWithoutIteration(const Simulator &simulator, const double dt, const Well::InjectionControls &inj_controls, const Well::ProductionControls &prod_controls, WellState< Scalar > &well_state, const GroupState< Scalar > &group_state, DeferredLogger &deferred_logger) override
Definition: StandardWell_impl.hpp:338
void computeWellConnectionDensitesPressures(const Simulator &simulator, const WellState< Scalar > &well_state, const WellConnectionProps &props, DeferredLogger &deferred_logger)
Definition: StandardWell_impl.hpp:1298
virtual void apply(const BVector &x, BVector &Ax) const override
Ax = Ax - C D^-1 B x.
Definition: StandardWell_impl.hpp:1398
typename StdWellEval::Eval Eval
Definition: StandardWell.hpp:119
void computeWellPotentials(const Simulator &simulator, const WellState< Scalar > &well_state, std::vector< double > &well_potentials, DeferredLogger &deferred_logger) override
computing the well potentials for group control
Definition: StandardWell_impl.hpp:1703
void updateConnectionRatePolyMW(const EvalWell &cq_s_poly, const IntensiveQuantities &int_quants, const WellState< Scalar > &well_state, const int perf, std::vector< RateVector > &connectionRates, DeferredLogger &deferred_logger) const
Definition: StandardWell_impl.hpp:2121
bool openCrossFlowAvoidSingularity(const Simulator &simulator) const
Definition: StandardWell_impl.hpp:1121
std::optional< double > computeBhpAtThpLimitProd(const WellState< Scalar > &well_state, const Simulator &simulator, const SummaryState &summary_state, DeferredLogger &deferred_logger) const
Definition: StandardWell_impl.hpp:2164
bool allDrawDownWrongDirection(const Simulator &simulator) const
Definition: StandardWell_impl.hpp:1043
void recoverWellSolutionAndUpdateWellState(const SummaryState &summary_state, const BVector &x, WellState< Scalar > &well_state, DeferredLogger &deferred_logger) override
Definition: StandardWell_impl.hpp:1430
void updateWellStateFromPrimaryVariables(const bool stop_or_zero_rate_target, WellState< Scalar > &well_state, const SummaryState &summary_state, DeferredLogger &deferred_logger) const
Definition: StandardWell_impl.hpp:739
static constexpr int numWellConservationEq
Definition: StandardWell.hpp:95
void handleInjectivityEquations(const Simulator &simulator, const WellState< Scalar > &well_state, const int perf, const EvalWell &water_flux_s, DeferredLogger &deferred_logger)
Definition: StandardWell_impl.hpp:2054
virtual ConvergenceReport getWellConvergence(const SummaryState &summary_state, const WellState< Scalar > &well_state, const std::vector< double > &B_avg, DeferredLogger &deferred_logger, const bool relax_tolerance) const override
check whether the well equations get converged for this well
Definition: StandardWell_impl.hpp:1178
void computeWellRatesWithBhp(const Simulator &simulator, const double &bhp, std::vector< double > &well_flux, DeferredLogger &deferred_logger) const override
Definition: StandardWell_impl.hpp:1450
void assembleWellEqWithoutIterationImpl(const Simulator &simulator, const double dt, const Well::InjectionControls &inj_controls, const Well::ProductionControls &prod_controls, WellState< Scalar > &well_state, const GroupState< Scalar > &group_state, DeferredLogger &deferred_logger)
Definition: StandardWell_impl.hpp:364
void checkOperabilityUnderTHPLimit(const Simulator &simulator, const WellState< Scalar > &well_state, DeferredLogger &deferred_logger) override
Definition: StandardWell_impl.hpp:994
void addWellPressureEquations(PressureMatrix &mat, const BVector &x, const int pressureVarIndex, const bool use_well_weights, const WellState< Scalar > &well_state) const override
Definition: StandardWell_impl.hpp:1881
bool canProduceInjectWithCurrentBhp(const Simulator &simulator, const WellState< Scalar > &well_state, DeferredLogger &deferred_logger)
Definition: StandardWell_impl.hpp:1085
void handleInjectivityRate(const Simulator &simulator, const int perf, std::vector< EvalWell > &cq_s) const
Definition: StandardWell_impl.hpp:2031
EvalWell wpolymermw(const double throughput, const EvalWell &water_velocity, DeferredLogger &deferred_logger) const
Definition: StandardWell_impl.hpp:1978
bool computeWellPotentialsImplicit(const Simulator &simulator, std::vector< double > &well_potentials, DeferredLogger &deferred_logger) const
Definition: StandardWell_impl.hpp:1595
void solveEqAndUpdateWellState(const SummaryState &summary_state, WellState< Scalar > &well_state, DeferredLogger &deferred_logger) override
Definition: StandardWell_impl.hpp:1360
void computeWellRatesWithBhpIterations(const Simulator &simulator, const double &bhp, std::vector< double > &well_flux, DeferredLogger &deferred_logger) const override
Definition: StandardWell_impl.hpp:1496
void calculateSinglePerf(const Simulator &simulator, const int perf, WellState< Scalar > &well_state, std::vector< RateVector > &connectionRates, std::vector< EvalWell > &cq_s, EvalWell &water_flux_s, EvalWell &cq_s_zfrac_effective, DeferredLogger &deferred_logger) const
Definition: StandardWell_impl.hpp:489
EvalWell getQs(const int compIdx) const
Returns scaled rate for a component.
Class for computing BHP limits.
Definition: WellBhpThpCalculator.hpp:42
std::optional< double > computeBhpAtThpLimitProd(const std::function< std::vector< double >(const double)> &frates, const SummaryState &summary_state, const double maxPerfPress, const double rho, const double alq_value, const double thp_limit, DeferredLogger &deferred_logger) const
Compute BHP from THP limit for a producer.
double mostStrictBhpFromBhpLimits(const SummaryState &summaryState) const
Obtain the most strict BHP from BHP limits.
double calculateThpFromBhp(const std::vector< double > &rates, const double bhp, const double rho, const std::optional< double > &alq, const double thp_limit, DeferredLogger &deferred_logger) const
Calculates THP from BHP.
std::optional< double > computeBhpAtThpLimitInj(const std::function< std::vector< double >(const double)> &frates, const SummaryState &summary_state, const double rho, const double flo_rel_tol, const int max_iteration, const bool throwOnError, DeferredLogger &deferred_logger) const
Compute BHP from THP limit for an injector.
Definition: WellConvergence.hpp:37
std::pair< bool, bool > computeWellPotentials(std::vector< double > &well_potentials, const WellState< double > &well_state)
void prepareForPotentialCalculations(const SummaryState &summary_state, WellState< double > &well_state, Well::InjectionControls &inj_controls, Well::ProductionControls &prod_controls) const
const int num_components_
Definition: WellInterfaceGeneric.hpp:311
Well well_ecl_
Definition: WellInterfaceGeneric.hpp:302
Definition: WellInterfaceIndices.hpp:35
Definition: WellInterface.hpp:75
Dune::BCRSMatrix< Opm::MatrixBlock< double, 1, 1 > > PressureMatrix
Definition: WellInterface.hpp:102
GetPropType< TypeTag, Properties::Simulator > Simulator
Definition: WellInterface.hpp:82
void getMobility(const Simulator &simulator, const int perf, std::vector< Value > &mob, Callback &extendEval, DeferredLogger &deferred_logger) const
Definition: WellInterface_impl.hpp:1691
typename WellInterfaceFluidSystem< FluidSystem >::RateConverterType RateConverterType
Definition: WellInterface.hpp:105
GetPropType< TypeTag, Properties::IntensiveQuantities > IntensiveQuantities
Definition: WellInterface.hpp:85
GetPropType< TypeTag, Properties::Scalar > Scalar
Definition: WellInterface.hpp:96
Dune::BlockVector< VectorBlockType > BVector
Definition: WellInterface.hpp:101
GetPropType< TypeTag, Properties::FluidSystem > FluidSystem
Definition: WellInterface.hpp:83
GetPropType< TypeTag, Properties::Indices > Indices
Definition: WellInterface.hpp:84
bool solveWellWithTHPConstraint(const Simulator &simulator, const double dt, const Well::InjectionControls &inj_controls, const Well::ProductionControls &prod_controls, WellState< Scalar > &well_state, const GroupState< Scalar > &group_state, DeferredLogger &deferred_logger)
Definition: WellInterface_impl.hpp:486
GetPropType< TypeTag, Properties::SparseMatrixAdapter > SparseMatrixAdapter
Definition: WellInterface.hpp:87
Definition: WellProdIndexCalculator.hpp:36
double connectionProdIndStandard(const std::size_t connIdx, const double connMobility) const
Definition: WellState.hpp:62
const SingleWellState< Scalar > & well(std::size_t well_index) const
Definition: WellState.hpp:300
std::vector< Scalar > & wellRates(std::size_t well_index)
One rate per well and phase.
Definition: WellState.hpp:265
@ NONE
Definition: DeferredLogger.hpp:46
VFPEvaluation bhp(const VFPProdTable &table, const double aqua, const double liquid, const double vapour, const double thp, const double alq, const double explicit_wfr, const double explicit_gfr, const bool use_vfpexplicit)
Definition: BlackoilPhases.hpp:27
PhaseUsage phaseUsage(const Phases &phases)
Determine the active phases.
std::string to_string(const ConvergenceReport::ReservoirFailure::Type t)
Solver parameters for the BlackoilModel.
Definition: BlackoilModelParameters.hpp:484
Definition: PerforationData.hpp:40
double dis_gas
Definition: PerforationData.hpp:41
double vap_oil
Definition: PerforationData.hpp:43
double vap_wat
Definition: PerforationData.hpp:44
double dis_gas_in_water
Definition: PerforationData.hpp:42
Definition: BlackoilPhases.hpp:46
double value
Definition: VFPHelpers.hpp:105