BlackoilWellModel_impl.hpp
Go to the documentation of this file.
1/*
2 Copyright 2016 - 2019 SINTEF Digital, Mathematics & Cybernetics.
3 Copyright 2016 - 2018 Equinor ASA.
4 Copyright 2017 Dr. Blatt - HPC-Simulation-Software & Services
5 Copyright 2016 - 2018 Norce AS
6
7 This file is part of the Open Porous Media project (OPM).
8
9 OPM is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 OPM is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with OPM. If not, see <http://www.gnu.org/licenses/>.
21*/
22
23#ifndef OPM_BLACKOILWELLMODEL_IMPL_HEADER_INCLUDED
24#define OPM_BLACKOILWELLMODEL_IMPL_HEADER_INCLUDED
25
26// Improve IDE experience
27#ifndef OPM_BLACKOILWELLMODEL_HEADER_INCLUDED
28#include <config.h>
30#endif
31
32#include <opm/grid/utility/cartesianToCompressed.hpp>
33#include <opm/common/utility/numeric/RootFinders.hpp>
34
35#include <opm/input/eclipse/Schedule/Network/Balance.hpp>
36#include <opm/input/eclipse/Schedule/Network/ExtNetwork.hpp>
37#include <opm/input/eclipse/Schedule/Well/PAvgDynamicSourceData.hpp>
38#include <opm/input/eclipse/Schedule/Well/WellMatcher.hpp>
39#include <opm/input/eclipse/Schedule/Well/WellTestConfig.hpp>
40#include <opm/input/eclipse/Schedule/Well/WellEconProductionLimits.hpp>
41
42#include <opm/input/eclipse/Units/UnitSystem.hpp>
43
54
57
58#if COMPILE_GPU_BRIDGE
60#endif
61
62#include <algorithm>
63#include <cassert>
64#include <iomanip>
65#include <utility>
66#include <optional>
67
68#include <fmt/format.h>
69
70namespace Opm {
71 template<typename TypeTag>
74 : WellConnectionModule(*this, simulator.gridView().comm())
75 , BlackoilWellModelGeneric<Scalar, IndexTraits>(simulator.vanguard().schedule(),
76 gaslift_,
77 simulator.vanguard().summaryState(),
78 simulator.vanguard().eclState(),
79 FluidSystem::phaseUsage(),
80 simulator.gridView().comm())
81 , simulator_(simulator)
82 , guide_rate_handler_{
83 *this,
84 simulator.vanguard().schedule(),
85 simulator.vanguard().summaryState(),
86 simulator.vanguard().grid().comm()
87 }
88 , gaslift_(this->terminal_output_)
89 {
90 local_num_cells_ = simulator_.gridView().size(0);
91
92 // Number of cells the global grid view
93 global_num_cells_ = simulator_.vanguard().globalNumCells();
94
95 {
96 auto& parallel_wells = simulator.vanguard().parallelWells();
97
98 this->parallel_well_info_.reserve(parallel_wells.size());
99 for( const auto& name_bool : parallel_wells) {
100 this->parallel_well_info_.emplace_back(name_bool, grid().comm());
101 }
102 }
103
105 Parameters::Get<Parameters::AlternativeWellRateInit>();
106
107 using SourceDataSpan =
108 typename PAvgDynamicSourceData<Scalar>::template SourceDataSpan<Scalar>;
109
111 [this](const std::size_t globalIndex)
112 { return this->compressedIndexForInterior(globalIndex); },
113 [this](const int localCell, SourceDataSpan sourceTerms)
114 {
115 using Item = typename SourceDataSpan::Item;
116
117 const auto* intQuants = this->simulator_.model()
118 .cachedIntensiveQuantities(localCell, /*timeIndex = */0);
119 const auto& fs = intQuants->fluidState();
120
121 sourceTerms
122 .set(Item::PoreVol, intQuants->porosity().value() *
123 this->simulator_.model().dofTotalVolume(localCell))
124 .set(Item::Depth, this->depth_[localCell]);
125
126 constexpr auto io = FluidSystem::oilPhaseIdx;
127 constexpr auto ig = FluidSystem::gasPhaseIdx;
128 constexpr auto iw = FluidSystem::waterPhaseIdx;
129
130 // Ideally, these would be 'constexpr'.
131 const auto haveOil = FluidSystem::phaseIsActive(io);
132 const auto haveGas = FluidSystem::phaseIsActive(ig);
133 const auto haveWat = FluidSystem::phaseIsActive(iw);
134
135 auto weightedPhaseDensity = [&fs](const auto ip)
136 {
137 return fs.saturation(ip).value() * fs.density(ip).value();
138 };
139
140 if (haveOil) { sourceTerms.set(Item::Pressure, fs.pressure(io).value()); }
141 else if (haveGas) { sourceTerms.set(Item::Pressure, fs.pressure(ig).value()); }
142 else { sourceTerms.set(Item::Pressure, fs.pressure(iw).value()); }
143
144 // Strictly speaking, assumes SUM(s[p]) == 1.
145 auto rho = 0.0;
146 if (haveOil) { rho += weightedPhaseDensity(io); }
147 if (haveGas) { rho += weightedPhaseDensity(ig); }
148 if (haveWat) { rho += weightedPhaseDensity(iw); }
149
150 sourceTerms.set(Item::MixtureDensity, rho);
151 }
152 );
153 }
154
155 template<typename TypeTag>
156 void
158 init()
159 {
160 extractLegacyCellPvtRegionIndex_();
161 extractLegacyDepth_();
162
163 gravity_ = simulator_.problem().gravity()[2];
164
165 this->initial_step_ = true;
166
167 // add the eWoms auxiliary module for the wells to the list
168 simulator_.model().addAuxiliaryModule(this);
169
170 is_cell_perforated_.resize(local_num_cells_, false);
171 }
172
173
174 template<typename TypeTag>
175 void
177 initWellContainer(const int reportStepIdx)
178 {
179 const uint64_t effective_events_mask = ScheduleEvents::WELL_STATUS_CHANGE
180 + ScheduleEvents::NEW_WELL;
181 const auto& events = this->schedule()[reportStepIdx].wellgroup_events();
182 for (auto& wellPtr : this->well_container_) {
183 const bool well_opened_this_step = this->report_step_starts_ &&
184 events.hasEvent(wellPtr->name(),
185 effective_events_mask);
186 wellPtr->init(this->depth_, this->gravity_,
187 this->B_avg_, well_opened_this_step);
188 }
189 }
190
191 template<typename TypeTag>
192 void
194 beginReportStep(const int timeStepIdx)
195 {
196 DeferredLogger local_deferredLogger{};
197
198 this->wgHelper().setReportStep(timeStepIdx);
199 this->report_step_starts_ = true;
200 this->report_step_start_events_ = this->schedule()[timeStepIdx].wellgroup_events();
201
202 this->rateConverter_ = std::make_unique<RateConverterType>
203 (std::vector<int>(this->local_num_cells_, 0));
204
205 {
206 // WELPI scaling runs at start of report step.
207 const auto enableWellPIScaling = true;
208 this->initializeLocalWellStructure(timeStepIdx, enableWellPIScaling);
209 }
210
211 this->initializeGroupStructure(timeStepIdx);
212
213 const auto& comm = this->simulator_.vanguard().grid().comm();
214
216 {
217 // Create facility for calculating reservoir voidage volumes for
218 // purpose of RESV controls.
219 this->rateConverter_->template defineState<ElementContext>(this->simulator_);
220
221 // Update VFP properties.
222 {
223 const auto& sched_state = this->schedule()[timeStepIdx];
224
225 this->vfp_properties_ = std::make_unique<VFPProperties<Scalar, IndexTraits>>
226 (sched_state.vfpinj(), sched_state.vfpprod(), this->wellState());
227 }
228 }
229 OPM_END_PARALLEL_TRY_CATCH_LOG(local_deferredLogger,
230 "beginReportStep() failed: ",
231 this->terminal_output_, comm)
232
233 // Store the current well and group states in order to recover in
234 // the case of failed iterations
235 this->commitWGState();
236
237 this->wellStructureChangedDynamically_ = false;
238 }
239
240
241
242
243
244 template <typename TypeTag>
245 void
247 initializeLocalWellStructure(const int reportStepIdx,
248 const bool enableWellPIScaling)
249 {
250 DeferredLogger local_deferredLogger{};
251
252 const auto& comm = this->simulator_.vanguard().grid().comm();
253
254 // Wells_ecl_ holds this rank's wells, both open and stopped/shut.
255 this->wells_ecl_ = this->getLocalWells(reportStepIdx);
256 this->local_parallel_well_info_ =
257 this->createLocalParallelWellInfo(this->wells_ecl_);
258
259 // At least initializeWellState() might be throw an exception in
260 // UniformTabulated2DFunction. Playing it safe by extending the
261 // scope a bit.
263 {
264 this->initializeWellPerfData();
265 this->initializeWellState(reportStepIdx);
266 this->wbp_.initializeWBPCalculationService();
267
268 if (this->param_.use_multisegment_well_ && this->anyMSWellOpenLocal()) {
269 this->wellState().initWellStateMSWell(this->wells_ecl_, &this->prevWellState());
270 }
271
272 this->initializeWellProdIndCalculators();
273
274 if (enableWellPIScaling && this->schedule()[reportStepIdx].events()
275 .hasEvent(ScheduleEvents::Events::WELL_PRODUCTIVITY_INDEX))
276 {
277 this->runWellPIScaling(reportStepIdx, local_deferredLogger);
278 }
279 }
280 OPM_END_PARALLEL_TRY_CATCH_LOG(local_deferredLogger,
281 "Failed to initialize local well structure: ",
282 this->terminal_output_, comm)
283 }
284
285
286
287
288
289 template <typename TypeTag>
290 void
292 initializeGroupStructure(const int reportStepIdx)
293 {
294 DeferredLogger local_deferredLogger{};
295
296 const auto& comm = this->simulator_.vanguard().grid().comm();
297
299 {
300 const auto& fieldGroup =
301 this->schedule().getGroup("FIELD", reportStepIdx);
302
303 this->wgHelper().setCmodeGroup(fieldGroup, this->groupState());
304
305 // Define per region average pressure calculators for use by
306 // pressure maintenance groups (GPMAINT keyword).
307 if (this->schedule()[reportStepIdx].has_gpmaint()) {
308 this->wgHelper().setRegionAveragePressureCalculator(
309 fieldGroup,
310 this->eclState_.fieldProps(),
311 this->regionalAveragePressureCalculator_
312 );
313 }
314 }
315 OPM_END_PARALLEL_TRY_CATCH_LOG(local_deferredLogger,
316 "Failed to initialize group structure: ",
317 this->terminal_output_, comm)
318 }
319
320
321
322
323
324 // called at the beginning of a time step
325 template<typename TypeTag>
326 void
329 {
330 OPM_TIMEBLOCK(beginTimeStep);
331
332 this->updateAverageFormationFactor();
333
334 DeferredLogger local_deferredLogger;
335
336 this->switched_prod_groups_.clear();
337 this->switched_inj_groups_.clear();
338
339 if (this->wellStructureChangedDynamically_) {
340 // Something altered the well structure/topology. Possibly
341 // WELSPECS/COMPDAT and/or WELOPEN run from an ACTIONX block.
342 // Reconstruct the local wells to account for the new well
343 // structure.
344 const auto reportStepIdx =
345 this->simulator_.episodeIndex();
346
347 // Disable WELPI scaling when well structure is updated in the
348 // middle of a report step.
349 const auto enableWellPIScaling = false;
350
351 this->initializeLocalWellStructure(reportStepIdx, enableWellPIScaling);
352 this->initializeGroupStructure(reportStepIdx);
353
354 this->commitWGState();
355
356 // Reset topology flag to signal that we've handled this
357 // structure change. That way we don't end up here in
358 // subsequent calls to beginTimeStep() unless there's a new
359 // dynamic change to the well structure during a report step.
360 this->wellStructureChangedDynamically_ = false;
361 }
362
363 this->resetWGState();
364 const int reportStepIdx = simulator_.episodeIndex();
365
366 this->wellState().updateWellsDefaultALQ(this->schedule(), reportStepIdx, this->summaryState());
367 this->wellState().gliftTimeStepInit();
368
369 const double simulationTime = simulator_.time();
371 {
372 // test wells
373 wellTesting(reportStepIdx, simulationTime, local_deferredLogger);
374
375 // create the well container
376 createWellContainer(reportStepIdx);
377
378 // we need to update the group data after the well is created
379 // to make sure we get the correct mapping.
380 this->updateAndCommunicateGroupData(reportStepIdx,
381 simulator_.model().newtonMethod().numIterations(),
382 param_.nupcol_group_rate_tolerance_, /*update_wellgrouptarget*/ false,
383 local_deferredLogger);
384
385 // Wells are active if they are active wells on at least one process.
386 const Grid& grid = simulator_.vanguard().grid();
387 this->wells_active_ = grid.comm().max(!this->well_container_.empty());
388
389 // do the initialization for all the wells
390 // TODO: to see whether we can postpone of the intialization of the well containers to
391 // optimize the usage of the following several member variables
392 this->initWellContainer(reportStepIdx);
393
394 // update the updated cell flag
395 std::fill(is_cell_perforated_.begin(), is_cell_perforated_.end(), false);
396 for (auto& well : well_container_) {
397 well->updatePerforatedCell(is_cell_perforated_);
398 }
399
400 // calculate the efficiency factors for each well
401 this->calculateEfficiencyFactors(reportStepIdx);
402
403 if constexpr (has_polymer_)
404 {
405 if (PolymerModule::hasPlyshlog() || getPropValue<TypeTag, Properties::EnablePolymerMW>() ) {
406 this->setRepRadiusPerfLength();
407 }
408 }
409
410 }
411
412 OPM_END_PARALLEL_TRY_CATCH_LOG(local_deferredLogger, "beginTimeStep() failed: ",
413 this->terminal_output_, simulator_.vanguard().grid().comm());
414
415 for (auto& well : well_container_) {
416 well->setVFPProperties(this->vfp_properties_.get());
417 well->setGuideRate(&this->guideRate_);
418 }
419
420 this->updateFiltrationModelsPreStep(local_deferredLogger);
421
422 // Close completions due to economic reasons
423 for (auto& well : well_container_) {
424 well->closeCompletions(this->wellTestState());
425 }
426
427 // we need the inj_multiplier from the previous time step
428 this->initInjMult();
429
430 if (alternative_well_rate_init_) {
431 // Update the well rates of well_state_, if only single-phase rates, to
432 // have proper multi-phase rates proportional to rates at bhp zero.
433 // This is done only for producers, as injectors will only have a single
434 // nonzero phase anyway.
435 for (const auto& well : well_container_) {
436 if (well->isProducer() && !well->wellIsStopped()) {
437 well->initializeProducerWellState(simulator_, this->wellState(), local_deferredLogger);
438 }
439 }
440 }
441
442 for (const auto& well : well_container_) {
443 if (well->isVFPActive(local_deferredLogger)){
444 well->setPrevSurfaceRates(this->wellState(), this->prevWellState());
445 }
446 }
447 try {
448 this->updateWellPotentials(reportStepIdx,
449 /*onlyAfterEvent*/true,
450 simulator_.vanguard().summaryConfig(),
451 local_deferredLogger);
452 } catch ( std::runtime_error& e ) {
453 const std::string msg = "A zero well potential is returned for output purposes. ";
454 local_deferredLogger.warning("WELL_POTENTIAL_CALCULATION_FAILED", msg);
455 }
456 this->guide_rate_handler_.setLogger(&local_deferredLogger);
457#ifdef RESERVOIR_COUPLING_ENABLED
458 if (this->isReservoirCouplingMaster()) {
459 this->guide_rate_handler_.receiveMasterGroupPotentialsFromSlaves();
460 }
461#endif
462 //update guide rates
463 this->guide_rate_handler_.updateGuideRates(
464 reportStepIdx, simulationTime, this->wellState(), this->groupState()
465 );
466#ifdef RESERVOIR_COUPLING_ENABLED
467 if (this->isReservoirCouplingSlave()) {
468 this->guide_rate_handler_.sendSlaveGroupPotentialsToMaster(this->groupState());
469 }
470#endif
471 std::string exc_msg;
472 auto exc_type = ExceptionType::NONE;
473 // update gpmaint targets
474 if (this->schedule_[reportStepIdx].has_gpmaint()) {
475 for (const auto& calculator : regionalAveragePressureCalculator_) {
476 calculator.second->template defineState<ElementContext>(simulator_);
477 }
478 const double dt = simulator_.timeStepSize();
479 const Group& fieldGroup = this->schedule().getGroup("FIELD", reportStepIdx);
480 this->wgHelper().updateGpMaintTargetForGroups(fieldGroup,
481 regionalAveragePressureCalculator_,
482 dt,
483 this->groupState());
484 }
485
486 this->updateAndCommunicateGroupData(reportStepIdx,
487 simulator_.model().newtonMethod().numIterations(),
488 param_.nupcol_group_rate_tolerance_,
489 /*update_wellgrouptarget*/ true,
490 local_deferredLogger);
491 try {
492 // Compute initial well solution for new wells and injectors that change injection type i.e. WAG.
493 for (auto& well : well_container_) {
494 const uint64_t effective_events_mask = ScheduleEvents::WELL_STATUS_CHANGE
495 + ScheduleEvents::INJECTION_TYPE_CHANGED
496 + ScheduleEvents::WELL_SWITCHED_INJECTOR_PRODUCER
497 + ScheduleEvents::NEW_WELL;
498
499 const auto& events = this->schedule()[reportStepIdx].wellgroup_events();
500 const bool event = this->report_step_starts_ && events.hasEvent(well->name(), effective_events_mask);
501 const bool dyn_status_change = this->wellState().well(well->name()).status
502 != this->prevWellState().well(well->name()).status;
503
504 if (event || dyn_status_change) {
505 try {
506 well->scaleSegmentRatesAndPressure(this->wellState());
507 well->calculateExplicitQuantities(simulator_, this->wellState(), local_deferredLogger);
508 well->updateWellStateWithTarget(simulator_, this->wgHelper(), this->wellState(), local_deferredLogger);
509 well->updatePrimaryVariables(simulator_, this->wellState(), local_deferredLogger);
510 well->solveWellEquation(
511 simulator_, this->wgHelper(), this->wellState(), local_deferredLogger
512 );
513 } catch (const std::exception& e) {
514 const std::string msg = "Compute initial well solution for new well " + well->name() + " failed. Continue with zero initial rates";
515 local_deferredLogger.warning("WELL_INITIAL_SOLVE_FAILED", msg);
516 }
517 }
518 }
519 }
520 // Catch clauses for all errors setting exc_type and exc_msg
521 OPM_PARALLEL_CATCH_CLAUSE(exc_type, exc_msg);
522
523 if (exc_type != ExceptionType::NONE) {
524 const std::string msg = "Compute initial well solution for new wells failed. Continue with zero initial rates";
525 local_deferredLogger.warning("WELL_INITIAL_SOLVE_FAILED", msg);
526 }
527
528 const auto& comm = simulator_.vanguard().grid().comm();
529 logAndCheckForExceptionsAndThrow(local_deferredLogger,
530 exc_type, "beginTimeStep() failed: " + exc_msg, this->terminal_output_, comm);
531
532 }
533
534 template<typename TypeTag>
535 void
537 const double simulationTime,
538 DeferredLogger& deferred_logger)
539 {
540 for (const std::string& well_name : this->getWellsForTesting(timeStepIdx, simulationTime)) {
541 const Well& wellEcl = this->schedule().getWell(well_name, timeStepIdx);
542 if (wellEcl.getStatus() == Well::Status::SHUT)
543 continue;
544
545 WellInterfacePtr well = createWellForWellTest(well_name, timeStepIdx, deferred_logger);
546 // some preparation before the well can be used
547 well->init(depth_, gravity_, B_avg_, true);
548
549 Scalar well_efficiency_factor = wellEcl.getEfficiencyFactor() *
550 this->wellState().getGlobalEfficiencyScalingFactor(well_name);
551 this->wgHelper().accumulateGroupEfficiencyFactor(
552 this->schedule().getGroup(wellEcl.groupName(), timeStepIdx),
553 well_efficiency_factor
554 );
555
556 well->setWellEfficiencyFactor(well_efficiency_factor);
557 well->setVFPProperties(this->vfp_properties_.get());
558 well->setGuideRate(&this->guideRate_);
559
560 // initialize rates/previous rates to prevent zero fractions in vfp-interpolation
561 if (well->isProducer() && alternative_well_rate_init_) {
562 well->initializeProducerWellState(simulator_, this->wellState(), deferred_logger);
563 }
564 if (well->isVFPActive(deferred_logger)) {
565 well->setPrevSurfaceRates(this->wellState(), this->prevWellState());
566 }
567
568 const auto& network = this->schedule()[timeStepIdx].network();
569 if (network.active() && !this->node_pressures_.empty()) {
570 if (well->isProducer()) {
571 const auto it = this->node_pressures_.find(well->wellEcl().groupName());
572 if (it != this->node_pressures_.end()) {
573 // The well belongs to a group which has a network nodal pressure,
574 // set the dynamic THP constraint based on the network nodal pressure
575 const Scalar nodal_pressure = it->second;
576 well->setDynamicThpLimit(nodal_pressure);
577 }
578 }
579 }
580 try {
581 using GLiftEclWells = typename GasLiftGroupInfo<Scalar, IndexTraits>::GLiftEclWells;
582 GLiftEclWells ecl_well_map;
583 gaslift_.initGliftEclWellMap(well_container_, ecl_well_map);
584 well->wellTesting(simulator_,
585 simulationTime,
586 this->wgHelper(),
587 this->wellState(),
588 this->wellTestState(),
589 ecl_well_map,
590 this->well_open_times_,
591 deferred_logger);
592 } catch (const std::exception& e) {
593 const std::string msg = fmt::format("Exception during testing of well: {}. The well will not open.\n Exception message: {}", wellEcl.name(), e.what());
594 deferred_logger.warning("WELL_TESTING_FAILED", msg);
595 }
596 }
597 }
598
599
600
601
602
603 // called at the end of a report step
604 template<typename TypeTag>
605 void
608 {
609 // Clear the communication data structures for above values.
610 for (auto&& pinfo : this->local_parallel_well_info_)
611 {
612 pinfo.get().clear();
613 }
614 }
615
616
617
618
619
620 // called at the end of a report step
621 template<typename TypeTag>
624 lastReport() const {return last_report_; }
625
626
627
628
629
630 // called at the end of a time step
631 template<typename TypeTag>
632 void
634 timeStepSucceeded(const double simulationTime, const double dt)
635 {
636 this->closed_this_step_.clear();
637
638 // time step is finished and we are not any more at the beginning of an report step
639 this->report_step_starts_ = false;
640 const int reportStepIdx = simulator_.episodeIndex();
641
642 DeferredLogger local_deferredLogger;
643 for (const auto& well : well_container_) {
644 if (getPropValue<TypeTag, Properties::EnablePolymerMW>() && well->isInjector()) {
645 well->updateWaterThroughput(dt, this->wellState());
646 }
647 }
648 // update connection transmissibility factor and d factor (if applicable) in the wellstate
649 for (const auto& well : well_container_) {
650 well->updateConnectionTransmissibilityFactor(simulator_, this->wellState().well(well->indexOfWell()));
651 well->updateConnectionDFactor(simulator_, this->wellState().well(well->indexOfWell()));
652 }
653
654 if (Indices::waterEnabled) {
655 this->updateFiltrationModelsPostStep(dt, FluidSystem::waterPhaseIdx, local_deferredLogger);
656 }
657
658 // WINJMULT: At the end of the time step, update the inj_multiplier saved in WellState for later use
659 this->updateInjMult(local_deferredLogger);
660
661 // report well switching
662 for (const auto& well : well_container_) {
663 well->reportWellSwitching(this->wellState().well(well->indexOfWell()), local_deferredLogger);
664 }
665 // report group switching
666 if (this->terminal_output_) {
667 this->reportGroupSwitching(local_deferredLogger);
668 }
669
670 // update the rate converter with current averages pressures etc in
671 rateConverter_->template defineState<ElementContext>(simulator_);
672
673 // calculate the well potentials
674 try {
675 this->updateWellPotentials(reportStepIdx,
676 /*onlyAfterEvent*/false,
677 simulator_.vanguard().summaryConfig(),
678 local_deferredLogger);
679 } catch ( std::runtime_error& e ) {
680 const std::string msg = "A zero well potential is returned for output purposes. ";
681 local_deferredLogger.warning("WELL_POTENTIAL_CALCULATION_FAILED", msg);
682 }
683
684 updateWellTestState(simulationTime, this->wellTestState());
685
686 // check group sales limits at the end of the timestep
687 const Group& fieldGroup = this->schedule_.getGroup("FIELD", reportStepIdx);
688 this->checkGEconLimits(fieldGroup, simulationTime,
689 simulator_.episodeIndex(), local_deferredLogger);
690 this->checkGconsaleLimits(fieldGroup, this->wellState(),
691 simulator_.episodeIndex(), local_deferredLogger);
692
693 this->calculateProductivityIndexValues(local_deferredLogger);
694
695 this->commitWGState();
696
697 const Opm::Parallel::Communication& comm = grid().comm();
698 DeferredLogger global_deferredLogger = gatherDeferredLogger(local_deferredLogger, comm);
699 if (this->terminal_output_) {
700 global_deferredLogger.logMessages();
701 }
702
703 //reporting output temperatures
704 this->computeWellTemperature();
705 }
706
707
708 template<typename TypeTag>
709 void
712 unsigned elemIdx) const
713 {
714 rate = 0;
715
716 if (!is_cell_perforated_[elemIdx] || cellRates_.count(elemIdx) == 0) {
717 return;
718 }
719
720 rate = cellRates_.at(elemIdx);
721 }
722
723
724 template<typename TypeTag>
725 template <class Context>
726 void
729 const Context& context,
730 unsigned spaceIdx,
731 unsigned timeIdx) const
732 {
733 rate = 0;
734 int elemIdx = context.globalSpaceIndex(spaceIdx, timeIdx);
735
736 if (!is_cell_perforated_[elemIdx] || cellRates_.count(elemIdx) == 0) {
737 return;
738 }
739
740 rate = cellRates_.at(elemIdx);
741 }
742
743
744
745 template<typename TypeTag>
746 void
748 initializeWellState(const int timeStepIdx)
749 {
750 const auto pressIx = []()
751 {
752 if (Indices::oilEnabled) { return FluidSystem::oilPhaseIdx; }
753 if (Indices::waterEnabled) { return FluidSystem::waterPhaseIdx; }
754
755 return FluidSystem::gasPhaseIdx;
756 }();
757
758 auto cellPressures = std::vector<Scalar>(this->local_num_cells_, Scalar{0});
759 auto cellTemperatures = std::vector<Scalar>(this->local_num_cells_, Scalar{0});
760
761 auto elemCtx = ElementContext { this->simulator_ };
762 const auto& gridView = this->simulator_.vanguard().gridView();
763
765 for (const auto& elem : elements(gridView, Dune::Partitions::interior)) {
766 elemCtx.updatePrimaryStencil(elem);
767 elemCtx.updatePrimaryIntensiveQuantities(/*timeIdx=*/0);
768
769 const auto ix = elemCtx.globalSpaceIndex(/*spaceIdx=*/0, /*timeIdx=*/0);
770 const auto& fs = elemCtx.intensiveQuantities(/*spaceIdx=*/0, /*timeIdx=*/0).fluidState();
771
772 cellPressures[ix] = fs.pressure(pressIx).value();
773 cellTemperatures[ix] = fs.temperature(0).value();
774 }
775 OPM_END_PARALLEL_TRY_CATCH("BlackoilWellModel::initializeWellState() failed: ",
776 this->simulator_.vanguard().grid().comm());
777
778 this->wellState().init(cellPressures, cellTemperatures, this->schedule(), this->wells_ecl_,
779 this->local_parallel_well_info_, timeStepIdx,
780 &this->prevWellState(), this->well_perf_data_,
781 this->summaryState(), simulator_.vanguard().enableDistributedWells());
782 }
783
784
785
786
787
788 template<typename TypeTag>
789 void
791 createWellContainer(const int report_step)
792 {
793 DeferredLogger local_deferredLogger;
794
795 const int nw = this->numLocalWells();
796
797 well_container_.clear();
798
799 if (nw > 0) {
800 well_container_.reserve(nw);
801
802 const auto& wmatcher = this->schedule().wellMatcher(report_step);
803 const auto& wcycle = this->schedule()[report_step].wcycle.get();
804
805 // First loop and check for status changes. This is necessary
806 // as wcycle needs the updated open/close times.
807 std::for_each(this->wells_ecl_.begin(), this->wells_ecl_.end(),
808 [this, &wg_events = this->report_step_start_events_](const auto& well_ecl)
809 {
810 if (!well_ecl.hasConnections()) {
811 // No connections in this well. Nothing to do.
812 return;
813 }
814
815 constexpr auto events_mask = ScheduleEvents::WELL_STATUS_CHANGE |
816 ScheduleEvents::REQUEST_OPEN_WELL |
817 ScheduleEvents::REQUEST_SHUT_WELL;
818 const bool well_event =
819 this->report_step_starts_ &&
820 wg_events.hasEvent(well_ecl.name(), events_mask);
821 // WCYCLE is suspendended by explicit SHUT events by the user.
822 // and restarted after explicit OPEN events.
823 // Note: OPEN or SHUT event does not necessary mean the well
824 // actually opened or shut at this point as the simulator could
825 // have done this by operabilty checks and well testing. This
826 // may need further testing and imply code changes to cope with
827 // these corner cases.
828 if (well_event) {
829 if (well_ecl.getStatus() == WellStatus::OPEN) {
830 this->well_open_times_.insert_or_assign(well_ecl.name(),
831 this->simulator_.time());
832 this->well_close_times_.erase(well_ecl.name());
833 } else if (well_ecl.getStatus() == WellStatus::SHUT) {
834 this->well_close_times_.insert_or_assign(well_ecl.name(),
835 this->simulator_.time());
836 this->well_open_times_.erase(well_ecl.name());
837 }
838 }
839 });
840
841 // Grab wcycle states. This needs to run before the schedule gets processed
842 const auto cycle_states = wcycle.wellStatus(this->simulator_.time(),
843 wmatcher,
844 this->well_open_times_,
845 this->well_close_times_);
846
847 for (int w = 0; w < nw; ++w) {
848 const Well& well_ecl = this->wells_ecl_[w];
849
850 if (!well_ecl.hasConnections()) {
851 // No connections in this well. Nothing to do.
852 continue;
853 }
854
855 const std::string& well_name = well_ecl.name();
856 const auto well_status = this->schedule()
857 .getWell(well_name, report_step).getStatus();
858
859 const bool shut_event = this->wellState().well(w).events.hasEvent(ScheduleEvents::WELL_STATUS_CHANGE)
860 && well_status == Well::Status::SHUT;
861 const bool open_event = this->wellState().well(w).events.hasEvent(ScheduleEvents::WELL_STATUS_CHANGE)
862 && well_status == Well::Status::OPEN;
863 const auto& ws = this->wellState().well(well_name);
864
865 if (shut_event && ws.status != Well::Status::SHUT) {
866 this->closed_this_step_.insert(well_name);
867 this->wellState().shutWell(w);
868 } else if (open_event && ws.status != Well::Status::OPEN) {
869 this->wellState().openWell(w);
870 }
871
872 // A new WCON keywords can re-open a well that was closed/shut due to Physical limit
873 if (this->wellTestState().well_is_closed(well_name)) {
874 // The well was shut this timestep, we are most likely retrying
875 // a timestep without the well in question, after it caused
876 // repeated timestep cuts. It should therefore not be opened,
877 // even if it was new or received new targets this report step.
878 const bool closed_this_step = (this->wellTestState().lastTestTime(well_name) == simulator_.time());
879 // TODO: more checking here, to make sure this standard more specific and complete
880 // maybe there is some WCON keywords will not open the well
881 auto& events = this->wellState().well(w).events;
882 if (events.hasEvent(ScheduleEvents::REQUEST_OPEN_WELL)) {
883 if (!closed_this_step) {
884 this->wellTestState().open_well(well_name);
885 this->wellTestState().open_completions(well_name);
886 this->well_open_times_.insert_or_assign(well_name,
887 this->simulator_.time());
888 this->well_close_times_.erase(well_name);
889 }
890 events.clearEvent(ScheduleEvents::REQUEST_OPEN_WELL);
891 }
892 }
893
894 // TODO: should we do this for all kinds of closing reasons?
895 // something like wellTestState().hasWell(well_name)?
896 if (this->wellTestState().well_is_closed(well_name))
897 {
898 if (well_ecl.getAutomaticShutIn()) {
899 // shut wells are not added to the well container
900 this->wellState().shutWell(w);
901 this->well_close_times_.erase(well_name);
902 this->well_open_times_.erase(well_name);
903 continue;
904 } else {
905 if (!well_ecl.getAllowCrossFlow()) {
906 // stopped wells where cross flow is not allowed
907 // are not added to the well container
908 this->wellState().shutWell(w);
909 this->well_close_times_.erase(well_name);
910 this->well_open_times_.erase(well_name);
911 continue;
912 }
913 // stopped wells are added to the container but marked as stopped
914 this->wellState().stopWell(w);
915 }
916 }
917
918 // shut wells with zero rante constraints and disallowing
919 if (!well_ecl.getAllowCrossFlow()) {
920 const bool any_zero_rate_constraint = well_ecl.isProducer()
921 ? well_ecl.productionControls(this->summaryState_).anyZeroRateConstraint()
922 : well_ecl.injectionControls(this->summaryState_).anyZeroRateConstraint();
923 if (any_zero_rate_constraint) {
924 // Treat as shut, do not add to container.
925 local_deferredLogger.debug(fmt::format(" Well {} gets shut due to having zero rate constraint and disallowing crossflow ", well_ecl.name()) );
926 this->wellState().shutWell(w);
927 this->well_close_times_.erase(well_name);
928 this->well_open_times_.erase(well_name);
929 continue;
930 }
931 }
932
933 if (!wcycle.empty()) {
934 const auto it = cycle_states.find(well_name);
935 if (it != cycle_states.end()) {
936 if (!it->second || well_status == Well::Status::SHUT) {
937 // If well is shut in schedule we keep it shut
938 if (well_status == Well::Status::SHUT) {
939 this->well_open_times_.erase(well_name);
940 this->well_close_times_.erase(well_name);
941 }
942 this->wellState().shutWell(w);
943 continue;
944 } else {
945 this->wellState().openWell(w);
946 }
947 }
948 }
949
950 // We dont add SHUT wells to the container
951 if (ws.status == Well::Status::SHUT) {
952 continue;
953 }
954
955 well_container_.emplace_back(this->createWellPointer(w, report_step));
956
957 if (ws.status == Well::Status::STOP) {
958 well_container_.back()->stopWell();
959 this->well_close_times_.erase(well_name);
960 this->well_open_times_.erase(well_name);
961 }
962 }
963
964 if (!wcycle.empty()) {
965 const auto schedule_open =
966 [&wg_events = this->report_step_start_events_](const std::string& name)
967 {
968 return wg_events.hasEvent(name, ScheduleEvents::REQUEST_OPEN_WELL);
969 };
970 for (const auto& [wname, wscale] : wcycle.efficiencyScale(this->simulator_.time(),
971 this->simulator_.timeStepSize(),
972 wmatcher,
973 this->well_open_times_,
974 schedule_open))
975 {
976 this->wellState().updateEfficiencyScalingFactor(wname, wscale);
977 this->schedule_.add_event(ScheduleEvents::WELLGROUP_EFFICIENCY_UPDATE, report_step);
978 }
979 }
980 }
981
982 // Collect log messages and print.
983
984 const Opm::Parallel::Communication& comm = grid().comm();
985 DeferredLogger global_deferredLogger = gatherDeferredLogger(local_deferredLogger, comm);
986 if (this->terminal_output_) {
987 global_deferredLogger.logMessages();
988 }
989
990 this->well_container_generic_.clear();
991 for (auto& w : well_container_)
992 this->well_container_generic_.push_back(w.get());
993
994 const auto& network = this->schedule()[report_step].network();
995 if (network.active() && !this->node_pressures_.empty()) {
996 for (auto& well: this->well_container_generic_) {
997 // Producers only, since we so far only support the
998 // "extended" network model (properties defined by
999 // BRANPROP and NODEPROP) which only applies to producers.
1000 if (well->isProducer()) {
1001 const auto it = this->node_pressures_.find(well->wellEcl().groupName());
1002 if (it != this->node_pressures_.end()) {
1003 // The well belongs to a group which has a network nodal pressure,
1004 // set the dynamic THP constraint based on the network nodal pressure
1005 const Scalar nodal_pressure = it->second;
1006 well->setDynamicThpLimit(nodal_pressure);
1007 }
1008 }
1009 }
1010 }
1011
1012 this->wbp_.registerOpenWellsForWBPCalculation();
1013 }
1014
1015
1016
1017
1018
1019 template <typename TypeTag>
1020 typename BlackoilWellModel<TypeTag>::WellInterfacePtr
1022 createWellPointer(const int wellID, const int report_step) const
1023 {
1024 const auto is_multiseg = this->wells_ecl_[wellID].isMultiSegment();
1025
1026 if (! (this->param_.use_multisegment_well_ && is_multiseg)) {
1027 return this->template createTypedWellPointer<StandardWell<TypeTag>>(wellID, report_step);
1028 }
1029 else {
1030 return this->template createTypedWellPointer<MultisegmentWell<TypeTag>>(wellID, report_step);
1031 }
1032 }
1033
1034
1035
1036
1037
1038 template <typename TypeTag>
1039 template <typename WellType>
1040 std::unique_ptr<WellType>
1042 createTypedWellPointer(const int wellID, const int time_step) const
1043 {
1044 // Use the pvtRegionIdx from the top cell
1045 const auto& perf_data = this->well_perf_data_[wellID];
1046
1047 // Cater for case where local part might have no perforations.
1048 const auto pvtreg = perf_data.empty()
1049 ? 0 : this->pvt_region_idx_[perf_data.front().cell_index];
1050
1051 const auto& parallel_well_info = this->local_parallel_well_info_[wellID].get();
1052 const auto global_pvtreg = parallel_well_info.broadcastFirstPerforationValue(pvtreg);
1053
1054 return std::make_unique<WellType>(this->wells_ecl_[wellID],
1055 parallel_well_info,
1056 time_step,
1057 this->param_,
1058 *this->rateConverter_,
1059 global_pvtreg,
1060 this->numConservationQuantities(),
1061 this->numPhases(),
1062 wellID,
1063 perf_data);
1064 }
1065
1066
1067
1068
1069
1070 template<typename TypeTag>
1073 createWellForWellTest(const std::string& well_name,
1074 const int report_step,
1075 DeferredLogger& deferred_logger) const
1076 {
1077 // Finding the location of the well in wells_ecl
1078 const auto it = std::find_if(this->wells_ecl_.begin(),
1079 this->wells_ecl_.end(),
1080 [&well_name](const auto& w)
1081 { return well_name == w.name(); });
1082 // It should be able to find in wells_ecl.
1083 if (it == this->wells_ecl_.end()) {
1084 OPM_DEFLOG_THROW(std::logic_error,
1085 fmt::format("Could not find well {} in wells_ecl ", well_name),
1086 deferred_logger);
1087 }
1088
1089 const int pos = static_cast<int>(std::distance(this->wells_ecl_.begin(), it));
1090 return this->createWellPointer(pos, report_step);
1091 }
1092
1093
1094
1095 template<typename TypeTag>
1096 void
1099 {
1100 OPM_TIMEFUNCTION();
1101 const double dt = this->simulator_.timeStepSize();
1102 // TODO: should we also have the group and network backed-up here in case the solution did not get converged?
1103 auto& well_state = this->wellState();
1104
1105 const bool changed_well_group = updateWellControlsAndNetwork(true, dt, deferred_logger);
1106 assembleWellEqWithoutIteration(dt, deferred_logger);
1107 const bool converged = this->getWellConvergence(this->B_avg_, true).converged() && !changed_well_group;
1108
1110 for (auto& well : this->well_container_) {
1111 well->solveEqAndUpdateWellState(simulator_, well_state, deferred_logger);
1112 }
1113 OPM_END_PARALLEL_TRY_CATCH("BlackoilWellModel::doPreStepNetworkRebalance() failed: ",
1114 this->simulator_.vanguard().grid().comm());
1115
1116 if (!converged) {
1117 const std::string msg = fmt::format("Initial (pre-step) network balance did not converge.");
1118 deferred_logger.warning(msg);
1119 }
1120 }
1121
1122
1123
1124
1125 template<typename TypeTag>
1126 void
1128 assemble(const int iterationIdx,
1129 const double dt)
1130 {
1131 OPM_TIMEFUNCTION();
1132 DeferredLogger local_deferredLogger;
1133
1134 this->guide_rate_handler_.setLogger(&local_deferredLogger);
1136 if (gaslift_.terminalOutput()) {
1137 const std::string msg =
1138 fmt::format("assemble() : iteration {}" , iterationIdx);
1139 gaslift_.gliftDebug(msg, local_deferredLogger);
1140 }
1141 }
1142 last_report_ = SimulatorReportSingle();
1143 Dune::Timer perfTimer;
1144 perfTimer.start();
1145 this->closed_offending_wells_.clear();
1146
1147 {
1148 const int episodeIdx = simulator_.episodeIndex();
1149 const auto& network = this->schedule()[episodeIdx].network();
1150 if (!this->wellsActive() && !network.active()) {
1151 return;
1152 }
1153 }
1154
1155 if (iterationIdx == 0 && this->wellsActive()) {
1156 OPM_TIMEBLOCK(firstIterationAssmble);
1157 // try-catch is needed here as updateWellControls
1158 // contains global communication and has either to
1159 // be reached by all processes or all need to abort
1160 // before.
1162 {
1163 calculateExplicitQuantities(local_deferredLogger);
1164 prepareTimeStep(local_deferredLogger);
1165 }
1166 OPM_END_PARALLEL_TRY_CATCH_LOG(local_deferredLogger,
1167 "assemble() failed (It=0): ",
1168 this->terminal_output_, grid().comm());
1169 }
1170
1171 const bool well_group_control_changed = updateWellControlsAndNetwork(false, dt, local_deferredLogger);
1172
1173 // even when there is no wells active, the network nodal pressure still need to be updated through updateWellControlsAndNetwork()
1174 // but there is no need to assemble the well equations
1175 if ( ! this->wellsActive() ) {
1176 return;
1177 }
1178
1179 assembleWellEqWithoutIteration(dt, local_deferredLogger);
1180 // Pre-compute cell rates to we don't have to do this for every cell during linearization...
1181 cellRates_.clear();
1182 for (const auto& well : well_container_)
1183 well->addCellRates(cellRates_);
1184
1185 // if group or well control changes we don't consider the
1186 // case converged
1187 last_report_.well_group_control_changed = well_group_control_changed;
1188 last_report_.assemble_time_well += perfTimer.stop();
1189 }
1190
1191
1192
1193
1194 template<typename TypeTag>
1195 bool
1197 updateWellControlsAndNetwork(const bool mandatory_network_balance,
1198 const double dt,
1199 DeferredLogger& local_deferredLogger)
1200 {
1201 OPM_TIMEFUNCTION();
1202 // not necessarily that we always need to update once of the network solutions
1203 bool do_network_update = true;
1204 bool well_group_control_changed = false;
1205 Scalar network_imbalance = 0.0;
1206 // after certain number of the iterations, we use relaxed tolerance for the network update
1207 const std::size_t iteration_to_relax = param_.network_max_strict_outer_iterations_;
1208 // after certain number of the iterations, we terminate
1209 const std::size_t max_iteration = param_.network_max_outer_iterations_;
1210 std::size_t network_update_iteration = 0;
1211 network_needs_more_balancing_force_another_newton_iteration_ = false;
1212 while (do_network_update) {
1213 if (network_update_iteration >= max_iteration ) {
1214 // only output to terminal if we at the last newton iterations where we try to balance the network.
1215 const int episodeIdx = simulator_.episodeIndex();
1216 const int iterationIdx = simulator_.model().newtonMethod().numIterations();
1217 if (this->shouldBalanceNetwork(episodeIdx, iterationIdx + 1)) {
1218 if (this->terminal_output_) {
1219 const std::string msg = fmt::format("Maximum of {:d} network iterations has been used and we stop the update, \n"
1220 "and try again after the next Newton iteration (imbalance = {:.2e} bar, ctrl_change = {})",
1221 max_iteration, network_imbalance*1.0e-5, well_group_control_changed);
1222 local_deferredLogger.debug(msg);
1223 }
1224 // To avoid stopping the newton iterations too early, before the network is converged,
1225 // we need to report it
1226 network_needs_more_balancing_force_another_newton_iteration_ = true;
1227 } else {
1228 if (this->terminal_output_) {
1229 const std::string msg = fmt::format("Maximum of {:d} network iterations has been used and we stop the update. \n"
1230 "The simulator will continue with unconverged network results (imbalance = {:.2e} bar, ctrl_change = {})",
1231 max_iteration, network_imbalance*1.0e-5, well_group_control_changed);
1232 local_deferredLogger.info(msg);
1233 }
1234 }
1235 break;
1236 }
1237 if (this->terminal_output_ && (network_update_iteration == iteration_to_relax) ) {
1238 local_deferredLogger.debug("We begin using relaxed tolerance for network update now after " + std::to_string(iteration_to_relax) + " iterations ");
1239 }
1240 const bool relax_network_balance = network_update_iteration >= iteration_to_relax;
1241 // Never optimize gas lift in last iteration, to allow network convergence (unless max_iter < 2)
1242 const bool optimize_gas_lift = ( (network_update_iteration + 1) < std::max(max_iteration, static_cast<std::size_t>(2)) );
1243 std::tie(well_group_control_changed, do_network_update, network_imbalance) =
1244 updateWellControlsAndNetworkIteration(mandatory_network_balance, relax_network_balance, optimize_gas_lift, dt,local_deferredLogger);
1245 ++network_update_iteration;
1246 }
1247 return well_group_control_changed;
1248 }
1249
1250
1251
1252
1253 template<typename TypeTag>
1254 std::tuple<bool, bool, typename BlackoilWellModel<TypeTag>::Scalar>
1256 updateWellControlsAndNetworkIteration(const bool mandatory_network_balance,
1257 const bool relax_network_tolerance,
1258 const bool optimize_gas_lift,
1259 const double dt,
1260 DeferredLogger& local_deferredLogger)
1261 {
1262 OPM_TIMEFUNCTION();
1263 const int iterationIdx = simulator_.model().newtonMethod().numIterations();
1264 const int reportStepIdx = simulator_.episodeIndex();
1265 this->updateAndCommunicateGroupData(reportStepIdx, iterationIdx,
1266 param_.nupcol_group_rate_tolerance_, /*update_wellgrouptarget*/ true, local_deferredLogger);
1267 // We need to call updateWellControls before we update the network as
1268 // network updates are only done on thp controlled wells.
1269 // Note that well controls are allowed to change during updateNetwork
1270 // and in prepareWellsBeforeAssembling during well solves.
1271 bool well_group_control_changed = updateWellControls(local_deferredLogger);
1272 const auto [more_inner_network_update, network_imbalance] =
1273 updateNetworks(mandatory_network_balance,
1274 local_deferredLogger,
1275 relax_network_tolerance);
1276
1277
1278 bool alq_updated = false;
1280 {
1281 if (optimize_gas_lift) {
1282 // we need to update the potentials if the thp limit as been modified by
1283 // the network balancing
1284 const bool updatePotentials = (this->shouldBalanceNetwork(reportStepIdx, iterationIdx) || mandatory_network_balance);
1285 alq_updated = gaslift_.maybeDoGasLiftOptimize(simulator_,
1286 well_container_,
1287 this->node_pressures_,
1288 updatePotentials,
1289 this->wellState(),
1290 this->groupState(),
1291 local_deferredLogger);
1292 }
1293 prepareWellsBeforeAssembling(dt, local_deferredLogger);
1294 }
1295 OPM_END_PARALLEL_TRY_CATCH_LOG(local_deferredLogger,
1296 "updateWellControlsAndNetworkIteration() failed: ",
1297 this->terminal_output_, grid().comm());
1298
1299 // update guide rates
1300 if (alq_updated || BlackoilWellModelGuideRates(*this).
1301 guideRateUpdateIsNeeded(reportStepIdx)) {
1302 const double simulationTime = simulator_.time();
1303 // NOTE: For reservoir coupling: Slave group potentials are only communicated
1304 // at the start of the time step, see beginTimeStep(). Here, we assume those
1305 // potentials remain unchanged during the time step when updating guide rates below.
1306 this->guide_rate_handler_.updateGuideRates(
1307 reportStepIdx, simulationTime, this->wellState(), this->groupState()
1308 );
1309 }
1310 // we need to re-iterate the network when the well group controls changed or gaslift/alq is changed or
1311 // the inner iterations are did not converge
1312 const bool more_network_update = this->shouldBalanceNetwork(reportStepIdx, iterationIdx) &&
1313 (more_inner_network_update || well_group_control_changed || alq_updated);
1314 return {well_group_control_changed, more_network_update, network_imbalance};
1315 }
1316
1317 // This function is to be used for well groups in an extended network that act as a subsea manifold
1318 // The wells of such group should have a common THP and total phase rate(s) obeying (if possible)
1319 // the well group constraint set by GCONPROD
1320 template <typename TypeTag>
1321 bool
1323 computeWellGroupThp(const double dt, DeferredLogger& local_deferredLogger)
1324 {
1325 OPM_TIMEFUNCTION();
1326 const int reportStepIdx = this->simulator_.episodeIndex();
1327 const auto& network = this->schedule()[reportStepIdx].network();
1328 const auto& balance = this->schedule()[reportStepIdx].network_balance();
1329 const Scalar thp_tolerance = balance.thp_tolerance();
1330
1331 if (!network.active()) {
1332 return false;
1333 }
1334
1335 auto& well_state = this->wellState();
1336 auto& group_state = this->groupState();
1337
1338 bool well_group_thp_updated = false;
1339 for (const std::string& nodeName : network.node_names()) {
1340 const bool has_choke = network.node(nodeName).as_choke();
1341 if (has_choke) {
1342 const auto& summary_state = this->simulator_.vanguard().summaryState();
1343 const Group& group = this->schedule().getGroup(nodeName, reportStepIdx);
1344
1345 //TODO: Auto choke combined with RESV control is not supported
1346 std::vector<Scalar> resv_coeff(Indices::numPhases, 1.0);
1347 Scalar gratTargetFromSales = 0.0;
1348 if (group_state.has_grat_sales_target(group.name()))
1349 gratTargetFromSales = group_state.grat_sales_target(group.name());
1350
1351 const auto ctrl = group.productionControls(summary_state);
1352 auto cmode_tmp = ctrl.cmode;
1353 Scalar target_tmp{0.0};
1354 bool fld_none = false;
1355 if (cmode_tmp == Group::ProductionCMode::FLD || cmode_tmp == Group::ProductionCMode::NONE) {
1356 fld_none = true;
1357 // Target is set for an ancestor group. Target for autochoke group to be
1358 // derived via group guide rates
1359 const Scalar efficiencyFactor = 1.0;
1360 const Group& parentGroup = this->schedule().getGroup(group.parent(), reportStepIdx);
1362 group.name(),
1363 parentGroup,
1364 this->wgHelper(),
1365 this->schedule(),
1366 summary_state,
1367 resv_coeff,
1368 efficiencyFactor,
1369 reportStepIdx,
1370 &this->guideRate_,
1371 local_deferredLogger);
1372 target_tmp = target.first;
1373 cmode_tmp = target.second;
1374 }
1375 const auto cmode = cmode_tmp;
1376 using TargetCalculatorType = WGHelpers::TargetCalculator<Scalar, IndexTraits>;
1377 TargetCalculatorType tcalc(cmode, FluidSystem::phaseUsage(), resv_coeff,
1378 gratTargetFromSales, nodeName, group_state,
1379 group.has_gpmaint_control(cmode));
1380 if (!fld_none)
1381 {
1382 // Target is set for the autochoke group itself
1383 target_tmp = tcalc.groupTarget(ctrl, local_deferredLogger);
1384 }
1385
1386 const Scalar orig_target = target_tmp;
1387
1388 auto mismatch = [&] (auto group_thp) {
1389 Scalar group_rate(0.0);
1390 Scalar rate(0.0);
1391 for (auto& well : this->well_container_) {
1392 std::string well_name = well->name();
1393 auto& ws = well_state.well(well_name);
1394 if (group.hasWell(well_name)) {
1395 well->setDynamicThpLimit(group_thp);
1396 const Well& well_ecl = this->wells_ecl_[well->indexOfWell()];
1397 const auto inj_controls = Well::InjectionControls(0);
1398 const auto prod_controls = well_ecl.productionControls(summary_state);
1399 well->iterateWellEqWithSwitching(
1400 this->simulator_, dt, inj_controls, prod_controls, this->wgHelper(), this->wellState(), local_deferredLogger, false, false
1401 );
1402 rate = -tcalc.calcModeRateFromRates(ws.surface_rates);
1403 group_rate += rate;
1404 }
1405 }
1406 return (group_rate - orig_target)/orig_target;
1407 };
1408
1409 const auto upbranch = network.uptree_branch(nodeName);
1410 const auto it = this->node_pressures_.find((*upbranch).uptree_node());
1411 const Scalar nodal_pressure = it->second;
1412 Scalar well_group_thp = nodal_pressure;
1413
1414 std::optional<Scalar> autochoke_thp;
1415 if (auto iter = this->well_group_thp_calc_.find(nodeName); iter != this->well_group_thp_calc_.end()) {
1416 autochoke_thp = this->well_group_thp_calc_.at(nodeName);
1417 }
1418
1419 using WellBhpThpCalculatorType = WellBhpThpCalculator<Scalar, IndexTraits>;
1420 //Find an initial bracket
1421 std::array<Scalar, 2> range_initial;
1422 if (!autochoke_thp.has_value()){
1423 Scalar min_thp, max_thp;
1424 // Retrieve the terminal pressure of the associated root of the manifold group
1425 std::string node_name = nodeName;
1426 while (!network.node(node_name).terminal_pressure().has_value()) {
1427 auto branch = network.uptree_branch(node_name).value();
1428 node_name = branch.uptree_node();
1429 }
1430 min_thp = network.node(node_name).terminal_pressure().value();
1431 WellBhpThpCalculatorType::bruteForceBracketCommonTHP(mismatch, min_thp, max_thp);
1432 // Narrow down the bracket
1433 Scalar low1, high1;
1434 std::array<Scalar, 2> range = {Scalar{0.9}*min_thp, Scalar{1.1}*max_thp};
1435 std::optional<Scalar> appr_sol;
1436 WellBhpThpCalculatorType::bruteForceBracketCommonTHP(mismatch, range, low1, high1, appr_sol, 0.0, local_deferredLogger);
1437 min_thp = low1;
1438 max_thp = high1;
1439 range_initial = {min_thp, max_thp};
1440 }
1441
1442 if (!autochoke_thp.has_value() || autochoke_thp.value() > nodal_pressure) {
1443 // The bracket is based on the initial bracket or on a range based on a previous calculated group thp
1444 std::array<Scalar, 2> range = autochoke_thp.has_value() ?
1445 std::array<Scalar, 2>{Scalar{0.9} * autochoke_thp.value(),
1446 Scalar{1.1} * autochoke_thp.value()} : range_initial;
1447 Scalar low, high;
1448 std::optional<Scalar> approximate_solution;
1449 const Scalar tolerance1 = thp_tolerance;
1450 local_deferredLogger.debug("Using brute force search to bracket the group THP");
1451 const bool finding_bracket = WellBhpThpCalculatorType::bruteForceBracketCommonTHP(mismatch, range, low, high, approximate_solution, tolerance1, local_deferredLogger);
1452
1453 if (approximate_solution.has_value()) {
1454 autochoke_thp = *approximate_solution;
1455 local_deferredLogger.debug("Approximate group THP value found: " + std::to_string(autochoke_thp.value()));
1456 } else if (finding_bracket) {
1457 const Scalar tolerance2 = thp_tolerance;
1458 const int max_iteration_solve = 100;
1459 int iteration = 0;
1460 autochoke_thp = RegulaFalsiBisection<ThrowOnError>::
1461 solve(mismatch, low, high, max_iteration_solve, tolerance2, iteration);
1462 local_deferredLogger.debug(" bracket = [" + std::to_string(low) + ", " + std::to_string(high) + "], " +
1463 "iteration = " + std::to_string(iteration));
1464 local_deferredLogger.debug("Group THP value = " + std::to_string(autochoke_thp.value()));
1465 } else {
1466 autochoke_thp.reset();
1467 local_deferredLogger.debug("Group THP solve failed due to bracketing failure");
1468 }
1469 }
1470 if (autochoke_thp.has_value()) {
1471 well_group_thp_calc_[nodeName] = autochoke_thp.value();
1472 // Note: The node pressure of the auto-choke node is set to well_group_thp in computeNetworkPressures()
1473 // and must be larger or equal to the pressure of the uptree node of its branch.
1474 well_group_thp = std::max(autochoke_thp.value(), nodal_pressure);
1475 }
1476
1477 for (auto& well : this->well_container_) {
1478 std::string well_name = well->name();
1479
1480 if (well->isInjector() || !well->wellEcl().predictionMode())
1481 continue;
1482
1483 if (group.hasWell(well_name)) {
1484 well->setDynamicThpLimit(well_group_thp);
1485 }
1486 const auto& ws = this->wellState().well(well->indexOfWell());
1487 const bool thp_is_limit = ws.production_cmode == Well::ProducerCMode::THP;
1488 if (thp_is_limit) {
1489 well->prepareWellBeforeAssembling(
1490 this->simulator_, dt, this->wgHelper(), this->wellState(), local_deferredLogger
1491 );
1492 }
1493 }
1494
1495 // Use the group THP in computeNetworkPressures().
1496 const auto& current_well_group_thp = group_state.is_autochoke_group(nodeName) ? group_state.well_group_thp(nodeName) : 1e30;
1497 if (std::abs(current_well_group_thp - well_group_thp) > balance.pressure_tolerance()) {
1498 well_group_thp_updated = true;
1499 group_state.update_well_group_thp(nodeName, well_group_thp);
1500 }
1501 }
1502 }
1503 return well_group_thp_updated;
1504 }
1505
1506 template<typename TypeTag>
1507 void
1509 assembleWellEq(const double dt, DeferredLogger& deferred_logger)
1510 {
1511 OPM_TIMEFUNCTION();
1512 for (auto& well : well_container_) {
1513 well->assembleWellEq(simulator_, dt, this->wgHelper(), deferred_logger);
1514 }
1515 }
1516
1517
1518 template<typename TypeTag>
1519 void
1521 prepareWellsBeforeAssembling(const double dt, DeferredLogger& deferred_logger)
1522 {
1523 OPM_TIMEFUNCTION();
1524 for (auto& well : well_container_) {
1525 well->prepareWellBeforeAssembling(
1526 simulator_, dt, this->wgHelper(), this->wellState(), deferred_logger
1527 );
1528 }
1529 }
1530
1531
1532 template<typename TypeTag>
1533 void
1535 assembleWellEqWithoutIteration(const double dt, DeferredLogger& deferred_logger)
1536 {
1537 OPM_TIMEFUNCTION();
1538 // We make sure that all processes throw in case there is an exception
1539 // on one of them (WetGasPvt::saturationPressure might throw if not converged)
1541
1542 for (auto& well: well_container_) {
1543 well->assembleWellEqWithoutIteration(simulator_, dt, this->wellState(), this->groupState(),
1544 deferred_logger);
1545 }
1546 OPM_END_PARALLEL_TRY_CATCH_LOG(deferred_logger, "BlackoilWellModel::assembleWellEqWithoutIteration failed: ",
1547 this->terminal_output_, grid().comm());
1548
1549 }
1550
1551#if COMPILE_GPU_BRIDGE
1552 template<typename TypeTag>
1553 void
1556 {
1557 // prepare for StandardWells
1559
1560 for(unsigned int i = 0; i < well_container_.size(); i++){
1561 auto& well = well_container_[i];
1562 auto derived = dynamic_cast<StandardWell<TypeTag>*>(well.get());
1563 if (derived) {
1564 wellContribs.addNumBlocks(derived->linSys().getNumBlocks());
1565 }
1566 }
1567
1568 // allocate memory for data from StandardWells
1569 wellContribs.alloc();
1570
1571 for(unsigned int i = 0; i < well_container_.size(); i++){
1572 auto& well = well_container_[i];
1573 // maybe WellInterface could implement addWellContribution()
1574 auto derived_std = dynamic_cast<StandardWell<TypeTag>*>(well.get());
1575 if (derived_std) {
1576 derived_std->linSys().extract(derived_std->numStaticWellEq, wellContribs);
1577 } else {
1578 auto derived_ms = dynamic_cast<MultisegmentWell<TypeTag>*>(well.get());
1579 if (derived_ms) {
1580 derived_ms->linSys().extract(wellContribs);
1581 } else {
1582 OpmLog::warning("Warning unknown type of well");
1583 }
1584 }
1585 }
1586 }
1587#endif
1588
1589 template<typename TypeTag>
1590 void
1592 addWellContributions(SparseMatrixAdapter& jacobian) const
1593 {
1594 for ( const auto& well: well_container_ ) {
1595 well->addWellContributions(jacobian);
1596 }
1597 }
1598
1599 template<typename TypeTag>
1600 void
1603 const BVector& weights,
1604 const bool use_well_weights) const
1605 {
1606 int nw = this->numLocalWellsEnd();
1607 int rdofs = local_num_cells_;
1608 for ( int i = 0; i < nw; i++ ) {
1609 int wdof = rdofs + i;
1610 jacobian[wdof][wdof] = 1.0;// better scaling ?
1611 }
1612
1613 for (const auto& well : well_container_) {
1614 well->addWellPressureEquations(jacobian,
1615 weights,
1616 pressureVarIndex,
1617 use_well_weights,
1618 this->wellState());
1619 }
1620 }
1621
1622 template <typename TypeTag>
1624 addReservoirSourceTerms(GlobalEqVector& residual,
1625 const std::vector<typename SparseMatrixAdapter::MatrixBlock*>& diagMatAddress) const
1626 {
1627 // NB this loop may write multiple times to the same element
1628 // if a cell is perforated by more than one well, so it should
1629 // not be OpenMP-parallelized.
1630 for (const auto& well : well_container_) {
1631 if (!well->isOperableAndSolvable() && !well->wellIsStopped()) {
1632 continue;
1633 }
1634 const auto& cells = well->cells();
1635 const auto& rates = well->connectionRates();
1636 for (unsigned perfIdx = 0; perfIdx < rates.size(); ++perfIdx) {
1637 unsigned cellIdx = cells[perfIdx];
1638 auto rate = rates[perfIdx];
1639 rate *= -1.0;
1640 VectorBlockType res(0.0);
1641 using MatrixBlockType = typename SparseMatrixAdapter::MatrixBlock;
1642 MatrixBlockType bMat(0.0);
1643 simulator_.model().linearizer().setResAndJacobi(res, bMat, rate);
1644 residual[cellIdx] += res;
1645 *diagMatAddress[cellIdx] += bMat;
1646 }
1647 }
1648 }
1649
1650
1651 template<typename TypeTag>
1652 void
1655 {
1656 int nw = this->numLocalWellsEnd();
1657 int rdofs = local_num_cells_;
1658 for (int i = 0; i < nw; ++i) {
1659 int wdof = rdofs + i;
1660 jacobian.entry(wdof,wdof) = 1.0;// better scaling ?
1661 }
1662 const auto wellconnections = this->getMaxWellConnections();
1663 for (int i = 0; i < nw; ++i) {
1664 const auto& perfcells = wellconnections[i];
1665 for (int perfcell : perfcells) {
1666 int wdof = rdofs + i;
1667 jacobian.entry(wdof, perfcell) = 0.0;
1668 jacobian.entry(perfcell, wdof) = 0.0;
1669 }
1670 }
1671 }
1672
1673
1674 template<typename TypeTag>
1675 void
1678 {
1679 DeferredLogger local_deferredLogger;
1681 {
1682 for (const auto& well : well_container_) {
1683 const auto& cells = well->cells();
1684 x_local_.resize(cells.size());
1685
1686 for (size_t i = 0; i < cells.size(); ++i) {
1687 x_local_[i] = x[cells[i]];
1688 }
1689 well->recoverWellSolutionAndUpdateWellState(simulator_, x_local_,
1690 this->wellState(), local_deferredLogger);
1691 }
1692 }
1693 OPM_END_PARALLEL_TRY_CATCH_LOG(local_deferredLogger,
1694 "recoverWellSolutionAndUpdateWellState() failed: ",
1695 this->terminal_output_, simulator_.vanguard().grid().comm());
1696 }
1697
1698
1699 template<typename TypeTag>
1700 void
1702 recoverWellSolutionAndUpdateWellStateDomain(const BVector& x, const int domainIdx)
1703 {
1704 if (!nldd_) {
1705 OPM_THROW(std::logic_error, "Attempt to call NLDD method without a NLDD solver");
1706 }
1707
1708 return nldd_->recoverWellSolutionAndUpdateWellState(x, domainIdx);
1709 }
1710
1711
1712 template<typename TypeTag>
1715 getWellConvergence(const std::vector<Scalar>& B_avg, bool checkWellGroupControlsAndNetwork) const
1716 {
1717
1718 DeferredLogger local_deferredLogger;
1719 // Get global (from all processes) convergence report.
1720 ConvergenceReport local_report;
1721 const int iterationIdx = simulator_.model().newtonMethod().numIterations();
1722 for (const auto& well : well_container_) {
1723 if (well->isOperableAndSolvable() || well->wellIsStopped()) {
1724 local_report += well->getWellConvergence(
1725 simulator_, this->wellState(), B_avg, local_deferredLogger,
1726 iterationIdx > param_.strict_outer_iter_wells_);
1727 } else {
1728 ConvergenceReport report;
1729 using CR = ConvergenceReport;
1730 report.setWellFailed({CR::WellFailure::Type::Unsolvable, CR::Severity::Normal, -1, well->name()});
1731 local_report += report;
1732 }
1733 }
1734
1735 const Opm::Parallel::Communication comm = grid().comm();
1736 DeferredLogger global_deferredLogger = gatherDeferredLogger(local_deferredLogger, comm);
1737 ConvergenceReport report = gatherConvergenceReport(local_report, comm);
1738
1739 if (checkWellGroupControlsAndNetwork) {
1740 // the well_group_control_changed info is already communicated
1741 report.setWellGroupTargetsViolated(this->lastReport().well_group_control_changed);
1742 report.setNetworkNotYetBalancedForceAnotherNewtonIteration(network_needs_more_balancing_force_another_newton_iteration_);
1743 }
1744
1745 if (this->terminal_output_) {
1746 global_deferredLogger.logMessages();
1747
1748 // Log debug messages for NaN or too large residuals.
1749 for (const auto& f : report.wellFailures()) {
1750 if (f.severity() == ConvergenceReport::Severity::NotANumber) {
1751 OpmLog::debug("NaN residual found with phase " + std::to_string(f.phase()) + " for well " + f.wellName());
1752 } else if (f.severity() == ConvergenceReport::Severity::TooLarge) {
1753 OpmLog::debug("Too large residual found with phase " + std::to_string(f.phase()) + " for well " + f.wellName());
1754 }
1755 }
1756 }
1757 return report;
1758 }
1759
1760
1761
1762
1763
1764 template<typename TypeTag>
1765 void
1767 calculateExplicitQuantities(DeferredLogger& deferred_logger) const
1768 {
1769 // TODO: checking isOperableAndSolvable() ?
1770 for (auto& well : well_container_) {
1771 well->calculateExplicitQuantities(simulator_, this->wellState(), deferred_logger);
1772 }
1773 }
1774
1775
1776
1777
1778
1779 template<typename TypeTag>
1780 bool
1782 updateWellControls(DeferredLogger& deferred_logger)
1783 {
1784 OPM_TIMEFUNCTION();
1785 if (!this->wellsActive()) {
1786 return false;
1787 }
1788 const int episodeIdx = simulator_.episodeIndex();
1789 const int iterationIdx = simulator_.model().newtonMethod().numIterations();
1790 const auto& comm = simulator_.vanguard().grid().comm();
1791 size_t iter = 0;
1792 bool changed_well_group = false;
1793 const Group& fieldGroup = this->schedule().getGroup("FIELD", episodeIdx);
1794 // Check group individual constraints.
1795 // iterate a few times to make sure all constraints are honored
1796 const std::size_t max_iter = param_.well_group_constraints_max_iterations_;
1797 while(!changed_well_group && iter < max_iter) {
1798 changed_well_group = updateGroupControls(fieldGroup, deferred_logger, episodeIdx, iterationIdx);
1799
1800 // Check wells' group constraints and communicate.
1801 bool changed_well_to_group = false;
1802 {
1803 OPM_TIMEBLOCK(UpdateWellControls);
1804 // For MS Wells a linear solve is performed below and the matrix might be singular.
1805 // We need to communicate the exception thrown to the others and rethrow.
1807 for (const auto& well : well_container_) {
1809 const bool changed_well = well->updateWellControl(
1810 simulator_, mode, this->wgHelper(), this->wellState(), deferred_logger
1811 );
1812 if (changed_well) {
1813 changed_well_to_group = changed_well || changed_well_to_group;
1814 }
1815 }
1816 OPM_END_PARALLEL_TRY_CATCH("BlackoilWellModel: updating well controls failed: ",
1817 simulator_.gridView().comm());
1818 }
1819
1820 changed_well_to_group = comm.sum(static_cast<int>(changed_well_to_group));
1821 if (changed_well_to_group) {
1822 updateAndCommunicate(episodeIdx, iterationIdx, deferred_logger);
1823 changed_well_group = true;
1824 }
1825
1826 // Check individual well constraints and communicate.
1827 bool changed_well_individual = false;
1828 {
1829 // For MS Wells a linear solve is performed below and the matrix might be singular.
1830 // We need to communicate the exception thrown to the others and rethrow.
1832 for (const auto& well : well_container_) {
1834 const bool changed_well = well->updateWellControl(
1835 simulator_, mode, this->wgHelper(), this->wellState(), deferred_logger
1836 );
1837 if (changed_well) {
1838 changed_well_individual = changed_well || changed_well_individual;
1839 }
1840 }
1841 OPM_END_PARALLEL_TRY_CATCH("BlackoilWellModel: updating well controls failed: ",
1842 simulator_.gridView().comm());
1843 }
1844
1845 changed_well_individual = comm.sum(static_cast<int>(changed_well_individual));
1846 if (changed_well_individual) {
1847 updateAndCommunicate(episodeIdx, iterationIdx, deferred_logger);
1848 changed_well_group = true;
1849 }
1850 iter++;
1851 }
1852
1853 // update wsolvent fraction for REIN wells
1854 this->updateWsolvent(fieldGroup, episodeIdx, this->nupcolWellState());
1855
1856 return changed_well_group;
1857 }
1858
1859
1860 template<typename TypeTag>
1861 std::tuple<bool, typename BlackoilWellModel<TypeTag>::Scalar>
1863 updateNetworks(const bool mandatory_network_balance,
1864 DeferredLogger& deferred_logger,
1865 const bool relax_network_tolerance)
1866 {
1867 OPM_TIMEFUNCTION();
1868 const int episodeIdx = simulator_.episodeIndex();
1869 const auto& network = this->schedule()[episodeIdx].network();
1870 if (!this->wellsActive() && !network.active()) {
1871 return {false, 0.0};
1872 }
1873
1874 const int iterationIdx = simulator_.model().newtonMethod().numIterations();
1875 const auto& comm = simulator_.vanguard().grid().comm();
1876
1877 // network related
1878 Scalar network_imbalance = 0.0;
1879 bool more_network_update = false;
1880 if (this->shouldBalanceNetwork(episodeIdx, iterationIdx) || mandatory_network_balance) {
1881 OPM_TIMEBLOCK(BalanceNetwork);
1882 const double dt = this->simulator_.timeStepSize();
1883 // Calculate common THP for subsea manifold well group (item 3 of NODEPROP set to YES)
1884 const bool well_group_thp_updated = computeWellGroupThp(dt, deferred_logger);
1885 const int max_number_of_sub_iterations = param_.network_max_sub_iterations_;
1886 const Scalar network_pressure_update_damping_factor = param_.network_pressure_update_damping_factor_;
1887 const Scalar network_max_pressure_update = param_.network_max_pressure_update_in_bars_ * unit::barsa;
1888 bool more_network_sub_update = false;
1889 for (int i = 0; i < max_number_of_sub_iterations; i++) {
1890 const auto local_network_imbalance = this->updateNetworkPressures(episodeIdx, network_pressure_update_damping_factor, network_max_pressure_update);
1891 network_imbalance = comm.max(local_network_imbalance);
1892 const auto& balance = this->schedule()[episodeIdx].network_balance();
1893 constexpr Scalar relaxation_factor = 10.0;
1894 const Scalar tolerance = relax_network_tolerance ? relaxation_factor * balance.pressure_tolerance() : balance.pressure_tolerance();
1895 more_network_sub_update = this->networkActive() && network_imbalance > tolerance;
1896 if (!more_network_sub_update)
1897 break;
1898
1899 for (const auto& well : well_container_) {
1900 if (well->isInjector() || !well->wellEcl().predictionMode())
1901 continue;
1902
1903 const auto it = this->node_pressures_.find(well->wellEcl().groupName());
1904 if (it != this->node_pressures_.end()) {
1905 well->prepareWellBeforeAssembling(this->simulator_, dt, this->wgHelper(), this->wellState(), deferred_logger);
1906 }
1907 }
1908 this->updateAndCommunicateGroupData(episodeIdx, iterationIdx, param_.nupcol_group_rate_tolerance_,
1909 /*update_wellgrouptarget*/ true, deferred_logger);
1910 }
1911 more_network_update = more_network_sub_update || well_group_thp_updated;
1912 }
1913 return { more_network_update, network_imbalance };
1914 }
1915
1916
1917 template<typename TypeTag>
1918 void
1920 updateAndCommunicate(const int reportStepIdx,
1921 const int iterationIdx,
1922 DeferredLogger& deferred_logger)
1923 {
1924 this->updateAndCommunicateGroupData(reportStepIdx,
1925 iterationIdx,
1926 param_.nupcol_group_rate_tolerance_,
1927 /*update_wellgrouptarget*/ true,
1928 deferred_logger);
1929
1930 // updateWellStateWithTarget might throw for multisegment wells hence we
1931 // have a parallel try catch here to thrown on all processes.
1933 // if a well or group change control it affects all wells that are under the same group
1934 for (const auto& well : well_container_) {
1935 // We only want to update wells under group-control here
1936 const auto& ws = this->wellState().well(well->indexOfWell());
1937 if (ws.production_cmode == Well::ProducerCMode::GRUP ||
1938 ws.injection_cmode == Well::InjectorCMode::GRUP)
1939 {
1940 well->updateWellStateWithTarget(
1941 simulator_, this->wgHelper(), this->wellState(), deferred_logger
1942 );
1943 }
1944 }
1945 OPM_END_PARALLEL_TRY_CATCH("BlackoilWellModel::updateAndCommunicate failed: ",
1946 simulator_.gridView().comm())
1947 this->updateAndCommunicateGroupData(reportStepIdx,
1948 iterationIdx,
1949 param_.nupcol_group_rate_tolerance_,
1950 /*update_wellgrouptarget*/ true,
1951 deferred_logger);
1952 }
1953
1954 template<typename TypeTag>
1955 bool
1957 updateGroupControls(const Group& group,
1958 DeferredLogger& deferred_logger,
1959 const int reportStepIdx,
1960 const int iterationIdx)
1961 {
1962 OPM_TIMEFUNCTION();
1963 bool changed = false;
1964 // restrict the number of group switches but only after nupcol iterations.
1965 const int nupcol = this->schedule()[reportStepIdx].nupcol();
1966 const int max_number_of_group_switches = param_.max_number_of_group_switches_;
1967 const bool update_group_switching_log = iterationIdx >= nupcol;
1968 const bool changed_hc = this->checkGroupHigherConstraints(group, deferred_logger, reportStepIdx, max_number_of_group_switches, update_group_switching_log);
1969 if (changed_hc) {
1970 changed = true;
1971 updateAndCommunicate(reportStepIdx, iterationIdx, deferred_logger);
1972 }
1973
1974 bool changed_individual =
1976 updateGroupIndividualControl(group,
1977 reportStepIdx,
1978 max_number_of_group_switches,
1979 update_group_switching_log,
1980 this->switched_inj_groups_,
1981 this->switched_prod_groups_,
1982 this->closed_offending_wells_,
1983 this->groupState(),
1984 this->wellState(),
1985 deferred_logger);
1986
1987 if (changed_individual) {
1988 changed = true;
1989 updateAndCommunicate(reportStepIdx, iterationIdx, deferred_logger);
1990 }
1991 // call recursively down the group hierarchy
1992 for (const std::string& groupName : group.groups()) {
1993 bool changed_this = updateGroupControls( this->schedule().getGroup(groupName, reportStepIdx), deferred_logger, reportStepIdx,iterationIdx);
1994 changed = changed || changed_this;
1995 }
1996 return changed;
1997 }
1998
1999 template<typename TypeTag>
2000 void
2002 updateWellTestState(const double simulationTime, WellTestState& wellTestState)
2003 {
2004 OPM_TIMEFUNCTION();
2005 DeferredLogger local_deferredLogger;
2006 for (const auto& well : well_container_) {
2007 const auto& wname = well->name();
2008 const auto wasClosed = wellTestState.well_is_closed(wname);
2009 well->checkWellOperability(simulator_,
2010 this->wellState(),
2011 this->wgHelper(),
2012 local_deferredLogger);
2013 const bool under_zero_target =
2014 well->wellUnderZeroGroupRateTarget(this->simulator_,
2015 this->wellState(),
2016 local_deferredLogger);
2017 well->updateWellTestState(this->wellState().well(wname),
2018 simulationTime,
2019 /*writeMessageToOPMLog=*/ true,
2020 under_zero_target,
2021 wellTestState,
2022 local_deferredLogger);
2023
2024 if (!wasClosed && wellTestState.well_is_closed(wname)) {
2025 this->closed_this_step_.insert(wname);
2026
2027 // maybe open a new well
2028 const WellEconProductionLimits& econ_production_limits = well->wellEcl().getEconLimits();
2029 if (econ_production_limits.validFollowonWell()) {
2030 const auto episode_idx = simulator_.episodeIndex();
2031 const auto follow_on_well = econ_production_limits.followonWell();
2032 if (!this->schedule().hasWell(follow_on_well, episode_idx)) {
2033 const auto msg = fmt::format("Well {} was closed. But the given follow on well {} does not exist."
2034 "The simulator continues without opening a follow on well.",
2035 wname, follow_on_well);
2036 local_deferredLogger.warning(msg);
2037 }
2038 auto& ws = this->wellState().well(follow_on_well);
2039 const bool success = ws.updateStatus(WellStatus::OPEN);
2040 if (success) {
2041 const auto msg = fmt::format("Well {} was closed. The follow on well {} opens instead.", wname, follow_on_well);
2042 local_deferredLogger.info(msg);
2043 } else {
2044 const auto msg = fmt::format("Well {} was closed. The follow on well {} is already open.", wname, follow_on_well);
2045 local_deferredLogger.warning(msg);
2046 }
2047 }
2048
2049 }
2050 }
2051
2052 for (const auto& [group_name, to] : this->closed_offending_wells_) {
2053 if (this->hasOpenLocalWell(to.second) &&
2054 !this->wasDynamicallyShutThisTimeStep(to.second))
2055 {
2056 wellTestState.close_well(to.second,
2057 WellTestConfig::Reason::GROUP,
2058 simulationTime);
2059 this->updateClosedWellsThisStep(to.second);
2060 const std::string msg =
2061 fmt::format("Procedure on exceeding {} limit is WELL for group {}. "
2062 "Well {} is {}.",
2063 to.first,
2064 group_name,
2065 to.second,
2066 "shut");
2067 local_deferredLogger.info(msg);
2068 }
2069 }
2070
2071 const Opm::Parallel::Communication comm = grid().comm();
2072 DeferredLogger global_deferredLogger =
2073 gatherDeferredLogger(local_deferredLogger, comm);
2074
2075 if (this->terminal_output_) {
2076 global_deferredLogger.logMessages();
2077 }
2078 }
2079
2080
2081 template<typename TypeTag>
2082 void
2084 const WellState<Scalar, IndexTraits>& well_state_copy,
2085 std::string& exc_msg,
2086 ExceptionType::ExcEnum& exc_type,
2087 DeferredLogger& deferred_logger)
2088 {
2089 OPM_TIMEFUNCTION();
2090 const int np = this->numPhases();
2091 std::vector<Scalar> potentials;
2092 const auto& well = well_container_[widx];
2093 std::string cur_exc_msg;
2094 auto cur_exc_type = ExceptionType::NONE;
2095 try {
2096 well->computeWellPotentials(simulator_, well_state_copy, this->wgHelper(), potentials, deferred_logger);
2097 }
2098 // catch all possible exception and store type and message.
2099 OPM_PARALLEL_CATCH_CLAUSE(cur_exc_type, cur_exc_msg);
2100 if (cur_exc_type != ExceptionType::NONE) {
2101 exc_msg += fmt::format("\nFor well {}: {}", well->name(), cur_exc_msg);
2102 }
2103 exc_type = std::max(exc_type, cur_exc_type);
2104 // Store it in the well state
2105 // potentials is resized and set to zero in the beginning of well->ComputeWellPotentials
2106 // and updated only if sucessfull. i.e. the potentials are zero for exceptions
2107 auto& ws = this->wellState().well(well->indexOfWell());
2108 for (int p = 0; p < np; ++p) {
2109 // make sure the potentials are positive
2110 ws.well_potentials[p] = std::max(Scalar{0.0}, potentials[p]);
2111 }
2112 }
2113
2114
2115
2116 template <typename TypeTag>
2117 void
2120 {
2121 for (const auto& wellPtr : this->well_container_) {
2122 this->calculateProductivityIndexValues(wellPtr.get(), deferred_logger);
2123 }
2124 }
2125
2126
2127
2128
2129
2130 template <typename TypeTag>
2131 void
2133 calculateProductivityIndexValuesShutWells(const int reportStepIdx,
2134 DeferredLogger& deferred_logger)
2135 {
2136 // For the purpose of computing PI/II values, it is sufficient to
2137 // construct StandardWell instances only. We don't need to form
2138 // well objects that honour the 'isMultisegment()' flag of the
2139 // corresponding "this->wells_ecl_[shutWell]".
2140
2141 for (const auto& shutWell : this->local_shut_wells_) {
2142 if (!this->wells_ecl_[shutWell].hasConnections()) {
2143 // No connections in this well. Nothing to do.
2144 continue;
2145 }
2146
2147 auto wellPtr = this->template createTypedWellPointer
2148 <StandardWell<TypeTag>>(shutWell, reportStepIdx);
2149
2150 wellPtr->init(this->depth_, this->gravity_, this->B_avg_, true);
2151
2152 this->calculateProductivityIndexValues(wellPtr.get(), deferred_logger);
2153 }
2154 }
2155
2156
2157
2158
2159
2160 template <typename TypeTag>
2161 void
2164 DeferredLogger& deferred_logger)
2165 {
2166 wellPtr->updateProductivityIndex(this->simulator_,
2167 this->prod_index_calc_[wellPtr->indexOfWell()],
2168 this->wellState(),
2169 deferred_logger);
2170 }
2171
2172
2173
2174 template<typename TypeTag>
2175 void
2177 prepareTimeStep(DeferredLogger& deferred_logger)
2178 {
2179 // Check if there is a network with active prediction wells at this time step.
2180 const auto episodeIdx = simulator_.episodeIndex();
2181 this->updateNetworkActiveState(episodeIdx);
2182
2183 // Rebalance the network initially if any wells in the network have status changes
2184 // (Need to check this before clearing events)
2185 const bool do_prestep_network_rebalance = param_.pre_solve_network_ && this->needPreStepNetworkRebalance(episodeIdx);
2186
2187 for (const auto& well : well_container_) {
2188 auto& events = this->wellState().well(well->indexOfWell()).events;
2189 if (events.hasEvent(WellState<Scalar, IndexTraits>::event_mask)) {
2190 well->updateWellStateWithTarget(
2191 simulator_, this->wgHelper(), this->wellState(), deferred_logger
2192 );
2193 well->updatePrimaryVariables(simulator_, this->wellState(), deferred_logger);
2194 // There is no new well control change input within a report step,
2195 // so next time step, the well does not consider to have effective events anymore.
2197 }
2198 // these events only work for the first time step within the report step
2199 if (events.hasEvent(ScheduleEvents::REQUEST_OPEN_WELL)) {
2200 events.clearEvent(ScheduleEvents::REQUEST_OPEN_WELL);
2201 }
2202 // solve the well equation initially to improve the initial solution of the well model
2203 if (param_.solve_welleq_initially_ && well->isOperableAndSolvable()) {
2204 try {
2205 well->solveWellEquation(
2206 simulator_, this->wgHelper(), this->wellState(), deferred_logger
2207 );
2208 } catch (const std::exception& e) {
2209 const std::string msg = "Compute initial well solution for " + well->name() + " initially failed. Continue with the previous rates";
2210 deferred_logger.warning("WELL_INITIAL_SOLVE_FAILED", msg);
2211 }
2212 }
2213 // If we're using local well solves that include control switches, they also update
2214 // operability, so reset before main iterations begin
2215 well->resetWellOperability();
2216 }
2217 updatePrimaryVariables(deferred_logger);
2218
2219 // Actually do the pre-step network rebalance, using the updated well states and initial solutions
2220 if (do_prestep_network_rebalance) doPreStepNetworkRebalance(deferred_logger);
2221 }
2222
2223 template<typename TypeTag>
2224 void
2227 {
2228 std::vector< Scalar > B_avg(numConservationQuantities(), Scalar() );
2229 const auto& grid = simulator_.vanguard().grid();
2230 const auto& gridView = grid.leafGridView();
2231 ElementContext elemCtx(simulator_);
2232
2234 for (const auto& elem : elements(gridView, Dune::Partitions::interior)) {
2235 elemCtx.updatePrimaryStencil(elem);
2236 elemCtx.updatePrimaryIntensiveQuantities(/*timeIdx=*/0);
2237
2238 const auto& intQuants = elemCtx.intensiveQuantities(/*spaceIdx=*/0, /*timeIdx=*/0);
2239 const auto& fs = intQuants.fluidState();
2240
2241 for (unsigned phaseIdx = 0; phaseIdx < FluidSystem::numPhases; ++phaseIdx)
2242 {
2243 if (!FluidSystem::phaseIsActive(phaseIdx)) {
2244 continue;
2245 }
2246
2247 const unsigned compIdx = FluidSystem::canonicalToActiveCompIdx(FluidSystem::solventComponentIndex(phaseIdx));
2248 auto& B = B_avg[ compIdx ];
2249
2250 B += 1 / fs.invB(phaseIdx).value();
2251 }
2252 if constexpr (has_solvent_) {
2253 auto& B = B_avg[solventSaturationIdx];
2254 B += 1 / intQuants.solventInverseFormationVolumeFactor().value();
2255 }
2256 }
2257 OPM_END_PARALLEL_TRY_CATCH("BlackoilWellModel::updateAverageFormationFactor() failed: ", grid.comm())
2258
2259 // compute global average
2260 grid.comm().sum(B_avg.data(), B_avg.size());
2261 B_avg_.resize(B_avg.size());
2262 std::transform(B_avg.begin(), B_avg.end(), B_avg_.begin(),
2263 [gcells = global_num_cells_](const auto bval)
2264 { return bval / gcells; });
2265 }
2266
2267
2268
2269
2270
2271 template<typename TypeTag>
2272 void
2275 {
2276 for (const auto& well : well_container_) {
2277 well->updatePrimaryVariables(simulator_, this->wellState(), deferred_logger);
2278 }
2279 }
2280
2281 template<typename TypeTag>
2282 void
2284 {
2285 const auto& grid = simulator_.vanguard().grid();
2286 const auto& eclProblem = simulator_.problem();
2287 const unsigned numCells = grid.size(/*codim=*/0);
2288
2289 this->pvt_region_idx_.resize(numCells);
2290 for (unsigned cellIdx = 0; cellIdx < numCells; ++cellIdx) {
2291 this->pvt_region_idx_[cellIdx] =
2292 eclProblem.pvtRegionIndex(cellIdx);
2293 }
2294 }
2295
2296 // The number of components in the model.
2297 template<typename TypeTag>
2298 int
2300 {
2301 // The numPhases() functions returns 1-3, depending on which
2302 // of the (oil, water, gas) phases are active. For each of those phases,
2303 // if the phase is active the corresponding component is present and
2304 // conserved.
2305 // Apart from (oil, water, gas), in the current well model only solvent
2306 // is explicitly modelled as a conserved quantity (polymer, energy, salt
2307 // etc. are not), unlike the reservoir part where all such quantities are
2308 // conserved. This function must therefore be updated when/if we add
2309 // more conserved quantities in the well model.
2310 return this->numPhases() + has_solvent_;
2311 }
2312
2313 template<typename TypeTag>
2314 void
2316 {
2317 const auto& eclProblem = simulator_.problem();
2318 depth_.resize(local_num_cells_);
2319 for (unsigned cellIdx = 0; cellIdx < local_num_cells_; ++cellIdx) {
2320 depth_[cellIdx] = eclProblem.dofCenterDepth(cellIdx);
2321 }
2322 }
2323
2324 template<typename TypeTag>
2327 getWell(const std::string& well_name) const
2328 {
2329 // finding the iterator of the well in wells_ecl
2330 auto well = std::find_if(well_container_.begin(),
2331 well_container_.end(),
2332 [&well_name](const WellInterfacePtr& elem)->bool {
2333 return elem->name() == well_name;
2334 });
2335
2336 assert(well != well_container_.end());
2337
2338 return **well;
2339 }
2340
2341 template <typename TypeTag>
2342 int
2344 reportStepIndex() const
2345 {
2346 return std::max(this->simulator_.episodeIndex(), 0);
2347 }
2348
2349
2350
2351
2352
2353 template<typename TypeTag>
2354 void
2356 calcResvCoeff(const int fipnum,
2357 const int pvtreg,
2358 const std::vector<Scalar>& production_rates,
2359 std::vector<Scalar>& resv_coeff)
2360 {
2361 rateConverter_->calcCoeff(fipnum, pvtreg, production_rates, resv_coeff);
2362 }
2363
2364 template<typename TypeTag>
2365 void
2367 calcInjResvCoeff(const int fipnum,
2368 const int pvtreg,
2369 std::vector<Scalar>& resv_coeff)
2370 {
2371 rateConverter_->calcInjCoeff(fipnum, pvtreg, resv_coeff);
2372 }
2373
2374
2375 template <typename TypeTag>
2376 void
2379 {
2380 if constexpr (has_energy_) {
2381 int np = this->numPhases();
2382 Scalar cellInternalEnergy;
2383 Scalar cellBinv;
2384 Scalar cellDensity;
2385 Scalar perfPhaseRate;
2386 const int nw = this->numLocalWells();
2387 for (auto wellID = 0*nw; wellID < nw; ++wellID) {
2388 const Well& well = this->wells_ecl_[wellID];
2389 auto& ws = this->wellState().well(wellID);
2390 if (well.isInjector()) {
2391 if (ws.status != WellStatus::STOP) {
2392 this->wellState().well(wellID).temperature = well.inj_temperature();
2393 continue;
2394 }
2395 }
2396
2397 std::array<Scalar,2> weighted{0.0,0.0};
2398 auto& [weighted_temperature, total_weight] = weighted;
2399
2400 auto& well_info = this->local_parallel_well_info_[wellID].get();
2401 auto& perf_data = ws.perf_data;
2402 auto& perf_phase_rate = perf_data.phase_rates;
2403
2404 using int_type = decltype(this->well_perf_data_[wellID].size());
2405 for (int_type perf = 0, end_perf = this->well_perf_data_[wellID].size(); perf < end_perf; ++perf) {
2406 const int cell_idx = this->well_perf_data_[wellID][perf].cell_index;
2407 const auto& intQuants = simulator_.model().intensiveQuantities(cell_idx, /*timeIdx=*/0);
2408 const auto& fs = intQuants.fluidState();
2409
2410 // we on only have one temperature pr cell any phaseIdx will do
2411 Scalar cellTemperatures = fs.temperature(/*phaseIdx*/0).value();
2412
2413 Scalar weight_factor = 0.0;
2414 for (unsigned phaseIdx = 0; phaseIdx < FluidSystem::numPhases; ++phaseIdx) {
2415 if (!FluidSystem::phaseIsActive(phaseIdx)) {
2416 continue;
2417 }
2418 cellInternalEnergy = fs.enthalpy(phaseIdx).value() -
2419 fs.pressure(phaseIdx).value() / fs.density(phaseIdx).value();
2420 cellBinv = fs.invB(phaseIdx).value();
2421 cellDensity = fs.density(phaseIdx).value();
2422 perfPhaseRate = perf_phase_rate[perf*np + phaseIdx];
2423 weight_factor += cellDensity * perfPhaseRate / cellBinv * cellInternalEnergy / cellTemperatures;
2424 }
2425 weight_factor = std::abs(weight_factor) + 1e-13;
2426 total_weight += weight_factor;
2427 weighted_temperature += weight_factor * cellTemperatures;
2428 }
2429 well_info.communication().sum(weighted.data(), 2);
2430 this->wellState().well(wellID).temperature = weighted_temperature / total_weight;
2431 }
2432 }
2433 }
2434
2435
2436 template <typename TypeTag>
2438 assignWellTracerRates(data::Wells& wsrpt) const
2439 {
2440 const auto reportStepIdx = static_cast<unsigned int>(this->reportStepIndex());
2441 const auto& trMod = this->simulator_.problem().tracerModel();
2442
2443 BlackoilWellModelGeneric<Scalar, IndexTraits>::assignWellTracerRates(wsrpt, trMod.getWellTracerRates(), reportStepIdx);
2444 BlackoilWellModelGeneric<Scalar, IndexTraits>::assignWellTracerRates(wsrpt, trMod.getWellFreeTracerRates(), reportStepIdx);
2445 BlackoilWellModelGeneric<Scalar, IndexTraits>::assignWellTracerRates(wsrpt, trMod.getWellSolTracerRates(), reportStepIdx);
2446
2447 this->assignMswTracerRates(wsrpt, trMod.getMswTracerRates(), reportStepIdx);
2448 }
2449
2450} // namespace Opm
2451
2452#endif // OPM_BLACKOILWELLMODEL_IMPL_HEADER_INCLUDED
#define OPM_END_PARALLEL_TRY_CATCH_LOG(obptc_logger, obptc_prefix, obptc_output, comm)
Catch exception, log, and throw in a parallel try-catch clause.
Definition: DeferredLoggingErrorHelpers.hpp:202
#define OPM_DEFLOG_THROW(Exception, message, deferred_logger)
Definition: DeferredLoggingErrorHelpers.hpp:45
#define OPM_END_PARALLEL_TRY_CATCH(prefix, comm)
Catch exception and throw in a parallel try-catch clause.
Definition: DeferredLoggingErrorHelpers.hpp:192
#define OPM_PARALLEL_CATCH_CLAUSE(obptc_exc_type, obptc_exc_msg)
Inserts catch classes for the parallel try-catch.
Definition: DeferredLoggingErrorHelpers.hpp:166
#define OPM_BEGIN_PARALLEL_TRY_CATCH()
Macro to setup the try of a parallel try-catch.
Definition: DeferredLoggingErrorHelpers.hpp:158
void logAndCheckForExceptionsAndThrow(Opm::DeferredLogger &deferred_logger, Opm::ExceptionType::ExcEnum exc_type, const std::string &message, const bool terminal_output, Opm::Parallel::Communication comm)
Definition: DeferredLoggingErrorHelpers.hpp:111
Class for handling constraints for the blackoil well model.
Definition: BlackoilWellModelConstraints.hpp:42
Class for handling the gaslift in the blackoil well model.
Definition: BlackoilWellModelGasLift.hpp:96
Class for handling the blackoil well model.
Definition: BlackoilWellModelGeneric.hpp:95
BlackoilWellModelWBP< GetPropType< TypeTag, Properties::Scalar >, GetPropType< TypeTag, Properties::FluidSystem >::IndexTraitsType > wbp_
Definition: BlackoilWellModelGeneric.hpp:530
std::vector< ParallelWellInfo< GetPropType< TypeTag, Properties::Scalar > > > parallel_well_info_
Definition: BlackoilWellModelGeneric.hpp:559
Class for handling the guide rates in the blackoil well model.
Definition: BlackoilWellModelGuideRates.hpp:47
Class for handling the blackoil well model.
Definition: BlackoilWellModel.hpp:102
void initializeGroupStructure(const int reportStepIdx)
Definition: BlackoilWellModel_impl.hpp:292
void init()
Definition: BlackoilWellModel_impl.hpp:158
const Simulator & simulator() const
Definition: BlackoilWellModel.hpp:372
std::vector< Scalar > depth_
Definition: BlackoilWellModel.hpp:431
std::size_t global_num_cells_
Definition: BlackoilWellModel.hpp:427
GetPropType< TypeTag, Properties::Scalar > Scalar
Definition: BlackoilWellModel.hpp:111
void initWellContainer(const int reportStepIdx) override
Definition: BlackoilWellModel_impl.hpp:177
void beginReportStep(const int time_step)
Definition: BlackoilWellModel_impl.hpp:194
GetPropType< TypeTag, Properties::FluidSystem > FluidSystem
Definition: BlackoilWellModel.hpp:107
Dune::FieldVector< Scalar, numEq > VectorBlockType
Definition: BlackoilWellModel.hpp:132
GetPropType< TypeTag, Properties::ElementContext > ElementContext
Definition: BlackoilWellModel.hpp:108
GetPropType< TypeTag, Properties::Grid > Grid
Definition: BlackoilWellModel.hpp:105
GetPropType< TypeTag, Properties::Simulator > Simulator
Definition: BlackoilWellModel.hpp:110
void initializeWellState(const int timeStepIdx)
Definition: BlackoilWellModel_impl.hpp:748
const Grid & grid() const
Definition: BlackoilWellModel.hpp:369
const SimulatorReportSingle & lastReport() const
Definition: BlackoilWellModel_impl.hpp:624
void addWellContributions(SparseMatrixAdapter &jacobian) const
Definition: BlackoilWellModel_impl.hpp:1592
Dune::BCRSMatrix< Opm::MatrixBlock< Scalar, 1, 1 > > PressureMatrix
Definition: BlackoilWellModel.hpp:297
bool empty() const
Definition: BlackoilWellModel.hpp:342
void computeTotalRatesForDof(RateVector &rate, unsigned globalIdx) const
Definition: BlackoilWellModel_impl.hpp:711
void beginTimeStep()
Definition: BlackoilWellModel_impl.hpp:328
GetPropType< TypeTag, Properties::RateVector > RateVector
Definition: BlackoilWellModel.hpp:112
void calculateExplicitQuantities(DeferredLogger &deferred_logger) const
Definition: BlackoilWellModel_impl.hpp:1767
void updatePrimaryVariables(DeferredLogger &deferred_logger)
Definition: BlackoilWellModel_impl.hpp:2274
void initializeLocalWellStructure(const int reportStepIdx, const bool enableWellPIScaling)
Definition: BlackoilWellModel_impl.hpp:247
Dune::BlockVector< VectorBlockType > BVector
Definition: BlackoilWellModel.hpp:133
BlackoilWellModel(Simulator &simulator)
Definition: BlackoilWellModel_impl.hpp:73
void wellTesting(const int timeStepIdx, const double simulationTime, DeferredLogger &deferred_logger)
Definition: BlackoilWellModel_impl.hpp:536
typename FluidSystem::IndexTraitsType IndexTraits
Definition: BlackoilWellModel.hpp:118
std::size_t local_num_cells_
Definition: BlackoilWellModel.hpp:429
bool alternative_well_rate_init_
Definition: BlackoilWellModel.hpp:432
void timeStepSucceeded(const double simulationTime, const double dt)
Definition: BlackoilWellModel_impl.hpp:634
Simulator & simulator_
Definition: BlackoilWellModel.hpp:401
void createWellContainer(const int report_step) override
Definition: BlackoilWellModel_impl.hpp:791
std::unique_ptr< WellInterface< TypeTag > > WellInterfacePtr
Definition: BlackoilWellModel.hpp:190
int compressedIndexForInterior(int cartesian_cell_idx) const override
get compressed index for interior cells (-1, otherwise
Definition: BlackoilWellModel.hpp:350
void endReportStep()
Definition: BlackoilWellModel_impl.hpp:607
void initializeSources(typename ParallelWBPCalculation< Scalar >::GlobalToLocal index, typename ParallelWBPCalculation< Scalar >::Evaluator eval)
Definition: ConvergenceReport.hpp:38
void setWellFailed(const WellFailure &wf)
Definition: ConvergenceReport.hpp:272
void setWellGroupTargetsViolated(const bool wellGroupTargetsViolated)
Definition: ConvergenceReport.hpp:290
const std::vector< WellFailure > & wellFailures() const
Definition: ConvergenceReport.hpp:380
void setNetworkNotYetBalancedForceAnotherNewtonIteration(const bool network_needs_more_balancing_force_another_newton_iteration)
Definition: ConvergenceReport.hpp:295
Definition: DeferredLogger.hpp:57
void info(const std::string &tag, const std::string &message)
void warning(const std::string &tag, const std::string &message)
void debug(const std::string &tag, const std::string &message)
std::map< std::string, std::pair< const Well *, int > > GLiftEclWells
Definition: GasLiftGroupInfo.hpp:64
Definition: StandardWell.hpp:60
virtual void init(const std::vector< Scalar > &depth_arg, const Scalar gravity_arg, const std::vector< Scalar > &B_avg, const bool changed_to_open_this_step) override
Definition: StandardWell_impl.hpp:76
Definition: TargetCalculator.hpp:43
Class for computing BHP limits.
Definition: WellBhpThpCalculator.hpp:41
Definition: WellContributions.hpp:51
void alloc()
Allocate memory for the StandardWells.
void setBlockSize(unsigned int dim, unsigned int dim_wells)
void addNumBlocks(unsigned int numBlocks)
Class for computing well group controls.
Definition: WellGroupControls.hpp:49
int indexOfWell() const
Index of well in the wells struct and wellState.
Definition: WellInterface.hpp:77
bool updateWellControl(const Simulator &simulator, const IndividualOrGroup iog, const WellGroupHelperType &wgHelper, WellStateType &well_state, DeferredLogger &deferred_logger)
Definition: WellInterface_impl.hpp:189
virtual void updateProductivityIndex(const Simulator &simulator, const WellProdIndexCalculator< Scalar > &wellPICalc, WellStateType &well_state, DeferredLogger &deferred_logger) const =0
Definition: WellState.hpp:66
ExcEnum
Definition: DeferredLogger.hpp:45
@ NONE
Definition: DeferredLogger.hpp:46
Dune::Communication< MPIComm > Communication
Definition: ParallelCommunication.hpp:30
Definition: blackoilbioeffectsmodules.hh:43
Opm::DeferredLogger gatherDeferredLogger(const Opm::DeferredLogger &local_deferredlogger, Parallel::Communication communicator)
Create a global log combining local logs.
ConvergenceReport gatherConvergenceReport(const ConvergenceReport &local_report, Parallel::Communication communicator)
std::string to_string(const ConvergenceReport::ReservoirFailure::Type t)
A struct for returning timing data from a simulator to its caller.
Definition: SimulatorReport.hpp:34