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