opm-simulators
FlowProblemBlackoil.hpp
Go to the documentation of this file.
1 // -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 // vi: set et ts=4 sw=4 sts=4:
3 /*
4  Copyright 2023 INRIA
5  Copyright 2024 SINTEF Digital
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 2 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  Consult the COPYING file in the top-level source directory of this
23  module for the precise wording of the license and the list of
24  copyright holders.
25 */
31 #ifndef OPM_FLOW_PROBLEM_BLACK_HPP
32 #define OPM_FLOW_PROBLEM_BLACK_HPP
33 
34 #include <opm/material/fluidsystems/BlackOilFluidSystem.hpp>
35 #include <opm/material/fluidsystems/blackoilpvt/DryGasPvt.hpp>
36 #include <opm/material/fluidsystems/blackoilpvt/WetGasPvt.hpp>
37 #include <opm/material/fluidsystems/blackoilpvt/LiveOilPvt.hpp>
38 #include <opm/material/fluidsystems/blackoilpvt/DeadOilPvt.hpp>
39 #include <opm/material/fluidsystems/blackoilpvt/ConstantCompressibilityOilPvt.hpp>
40 #include <opm/material/fluidsystems/blackoilpvt/ConstantCompressibilityWaterPvt.hpp>
41 #include <opm/material/fluidsystems/blackoilpvt/ConstantRsDeadOilPvt.hpp>
42 
44 #include <opm/models/blackoil/blackoilmoduleparams.hh>
46 
47 #include <opm/output/eclipse/EclipseIO.hpp>
48 
49 #include <opm/input/eclipse/Units/Units.hpp>
50 
51 #include <opm/simulators/flow/ActionHandler.hpp>
58 #include <opm/simulators/flow/HybridNewton.hpp>
59 #include <opm/simulators/flow/HybridNewtonConfig.hpp>
60 
61 #include <opm/simulators/utils/satfunc/SatfuncConsistencyCheckManager.hpp>
62 
63 #if HAVE_DAMARIS
65 #endif
66 
67 #include <algorithm>
68 #include <cstddef>
69 #include <functional>
70 #include <limits>
71 #include <memory>
72 #include <stdexcept>
73 #include <string>
74 #include <string_view>
75 #include <vector>
76 
77 namespace Opm {
78 
85 template <class TypeTag>
86 class FlowProblemBlackoil : public FlowProblem<TypeTag>
87 {
88  // TODO: the naming of the Types might be able to be adjusted
89 public:
91 
92 private:
93  using typename FlowProblemType::Scalar;
94  using typename FlowProblemType::Simulator;
95  using typename FlowProblemType::GridView;
96  using typename FlowProblemType::FluidSystem;
97  using typename FlowProblemType::Vanguard;
98  using typename FlowProblemType::GlobalEqVector;
99  using typename FlowProblemType::EqVector;
100  using FlowProblemType::dim;
101  using FlowProblemType::dimWorld;
102  using FlowProblemType::numEq;
103  using FlowProblemType::numPhases;
104  using FlowProblemType::numComponents;
105 
106  // TODO: potentially some cleaning up depending on the usage later here
107  using FlowProblemType::enableBioeffects;
108  using FlowProblemType::enableBrine;
109  using FlowProblemType::enableConvectiveMixing;
110  using FlowProblemType::enableDiffusion;
111  using FlowProblemType::enableDispersion;
112  using FlowProblemType::energyModuleType;
113  using FlowProblemType::enableExperiments;
114  using FlowProblemType::enableExtbo;
115  using FlowProblemType::enableFoam;
116  using FlowProblemType::enableMICP;
117  using FlowProblemType::enablePolymer;
118  using FlowProblemType::enablePolymerMolarWeight;
119  using FlowProblemType::enableSaltPrecipitation;
120  using FlowProblemType::enableSolvent;
121  using FlowProblemType::enableThermalFluxBoundaries;
122 
123  using FlowProblemType::gasPhaseIdx;
124  using FlowProblemType::oilPhaseIdx;
125  using FlowProblemType::waterPhaseIdx;
126 
127  using FlowProblemType::waterCompIdx;
128  using FlowProblemType::oilCompIdx;
129  using FlowProblemType::gasCompIdx;
130 
131  using BoundaryRateVector = GetPropType<TypeTag, Properties::BoundaryRateVector>;
132  using typename FlowProblemType::RateVector;
133  using typename FlowProblemType::PrimaryVariables;
134  using typename FlowProblemType::Indices;
135  using typename FlowProblemType::IntensiveQuantities;
136  using typename FlowProblemType::ElementContext;
137 
138  using typename FlowProblemType::MaterialLaw;
139  using typename FlowProblemType::DimMatrix;
140 
141  static constexpr bool enableDissolvedGas =
142  Indices::compositionSwitchIdx != std::numeric_limits<unsigned>::max();
143  enum { enableVapwat = getPropValue<TypeTag, Properties::EnableVapwat>() };
144  enum { enableDisgasInWater = getPropValue<TypeTag, Properties::EnableDisgasInWater>() };
145  enum { enableGeochemistry = getPropValue<TypeTag, Properties::EnableGeochemistry>() };
146  enum { enableMech = getPropValue<TypeTag, Properties::EnableMech>() };
147 
148  using BioeffectsModule = BlackOilBioeffectsModule<TypeTag, enableBioeffects>;
149  using BrineModule = BlackOilBrineModule<TypeTag, enableBrine>;
150  using ConvectiveMixingModule = BlackOilConvectiveMixingModule<TypeTag, enableConvectiveMixing>;
153  using ExtboModule = BlackOilExtboModule<TypeTag, enableExtbo>;
155  using PolymerModule = BlackOilPolymerModule<TypeTag, enablePolymer>;
156  using SolventModule = BlackOilSolventModule<TypeTag, enableSolvent>;
157 
159  using IndexTraits = typename FluidSystem::IndexTraitsType;
160  using InitialFluidState = typename EquilInitializer<TypeTag>::ScalarFluidState;
163 
164 #if HAVE_DAMARIS
165  using DamarisWriterType = DamarisWriter<TypeTag>;
166 #endif
167 
168 public:
171 
175  static void registerParameters()
176  {
178 
179  EclWriterType::registerParameters();
180 #if HAVE_DAMARIS
181  DamarisWriterType::registerParameters();
182 #endif
184  }
185 
189  explicit FlowProblemBlackoil(Simulator& simulator)
190  : FlowProblemType(simulator)
191  , thresholdPressures_(simulator)
192  , mixControls_(simulator.vanguard().schedule())
193  , actionHandler_(simulator.vanguard().eclState(),
194  simulator.vanguard().schedule(),
195  simulator.vanguard().actionState(),
196  simulator.vanguard().summaryState(),
197  this->wellModel_,
198  simulator.vanguard().grid().comm())
199  , hybridNewton_(simulator)
200  {
201  this->model().addOutputModule(std::make_unique<VtkTracerModule<TypeTag>>(simulator));
202 
203  // Tell the black-oil extensions to initialize their internal data structures
204  const auto& vanguard = simulator.vanguard();
205 
206  if constexpr (enableBrine) {
207  BlackOilBrineParams<Scalar> brineParams;
208  brineParams.template initFromState<enableBrine,
209  enableSaltPrecipitation>(vanguard.eclState());
210  BrineModule::setParams(std::move(brineParams));
211  }
212 
213  if constexpr (enableDiffusion) {
214  DiffusionModule::initFromState(vanguard.eclState());
215  }
216 
217  if constexpr (enableDispersion) {
218  DispersionModule::initFromState(vanguard.eclState());
219  }
220 
221  if constexpr (enableExtbo) {
222  BlackOilExtboParams<Scalar> extboParams;
223  extboParams.template initFromState<enableExtbo>(vanguard.eclState());
224  ExtboModule::setParams(std::move(extboParams));
225  }
226 
227  if constexpr (enableFoam) {
228  BlackOilFoamParams<Scalar> foamParams;
229  foamParams.template initFromState<enableFoam>(vanguard.eclState());
230  FoamModule::setParams(std::move(foamParams));
231  }
232 
233  if constexpr (enableBioeffects) {
234  BlackOilBioeffectsParams<Scalar> bioeffectsParams;
235  bioeffectsParams.template initFromState<enableBioeffects, enableMICP>(vanguard.eclState());
236  BioeffectsModule::setParams(std::move(bioeffectsParams));
237  }
238 
239  if constexpr (enablePolymer) {
240  BlackOilPolymerParams<Scalar> polymerParams;
241  polymerParams.template initFromState<enablePolymer, enablePolymerMolarWeight>(vanguard.eclState());
242  PolymerModule::setParams(std::move(polymerParams));
243  }
244 
245  if constexpr (enableSolvent) {
246  BlackOilSolventParams<Scalar> solventParams;
247  solventParams.template initFromState<enableSolvent>(vanguard.eclState(), vanguard.schedule());
248  SolventModule::setParams(std::move(solventParams));
249  }
250 
251  // create the ECL writer
252  eclWriter_ = std::make_unique<EclWriterType>(simulator);
253  enableEclOutput_ = Parameters::Get<Parameters::EnableEclOutput>();
254 
255  // Safeguard against geochemistry since it exsist in a separate module with a separate problem class
256  if constexpr (!enableGeochemistry) {
257  if (vanguard.eclState().runspec().geochem().enabled()) {
258  throw std::runtime_error("GEOCHEM keyword in the deck but geochemistry module "
259  "was disabled at compile time!");
260  }
261  }
262 
263  // Safeguard against TPSA-geomechanics since it requires FlowProblemTPSA
264  if constexpr (!enableMech) {
265  const auto& rspec = vanguard.eclState().runspec();
266  if (rspec.mech() && rspec.mechSolver().tpsa()) {
267  throw std::runtime_error("TPSA solver enabled in the deck, but geomechanics "
268  "module was disabled at compile time!");
269  }
270  }
271 
272 #if HAVE_DAMARIS
273  // create Damaris writer
274  damarisWriter_ = std::make_unique<DamarisWriterType>(simulator);
275  enableDamarisOutput_ = Parameters::Get<Parameters::EnableDamarisOutput>();
276 #endif
277  }
278 
282  void beginEpisode() override
283  {
285 
286  auto& simulator = this->simulator();
287 
288  const int episodeIdx = simulator.episodeIndex();
289  const auto& schedule = simulator.vanguard().schedule();
290 
291  // Evaluate UDQ assign statements to make sure the settings are
292  // available as UDA controls for the current report step.
293  this->actionHandler_
294  .evalUDQAssignments(episodeIdx, simulator.vanguard().udqState());
295 
296  if (episodeIdx >= 0) {
297  const auto& oilVap = schedule[episodeIdx].oilvap();
298  if (oilVap.getType() == OilVaporizationProperties::OilVaporization::VAPPARS) {
299  FluidSystem::setVapPars(oilVap.vap1(), oilVap.vap2());
300  }
301  else {
302  FluidSystem::setVapPars(0.0, 0.0);
303  }
304 
305  if constexpr (enableConvectiveMixing) {
306  ConvectiveMixingModule::beginEpisode(simulator.vanguard().eclState(), schedule, episodeIdx,
307  this->moduleParams_.convectiveMixingModuleParam);
308  }
309  }
310  }
311 
315  void beginTimeStep() override
316  {
318  hybridNewton_.tryApplyHybridNewton();
319  }
320 
324  void finishInit()
325  {
326  // TODO: there should be room to remove duplication for this
327  // function, but there is relatively complicated logic in the
328  // function calls here. Some refactoring is needed.
329  FlowProblemType::finishInit();
330 
331  auto& simulator = this->simulator();
332 
333  auto finishTransmissibilities = [updated = false, this]() mutable
334  {
335  if (updated) { return; }
336 
337  this->transmissibilities_.finishInit([&vg = this->simulator().vanguard()](const unsigned int it) {
338  return vg.gridIdxToEquilGridIdx(it);
339  });
340 
341  updated = true;
342  };
343 
344  // calculating the TRANX, TRANY, TRANZ and NNC for output purpose
345  // for parallel running, it is based on global trans_
346  // for serial running, it is based on the transmissibilities_
347  // we try to avoid for the parallel running, has both global trans_ and transmissibilities_ allocated at the same time
348  if (enableEclOutput_) {
349  if (simulator.vanguard().grid().comm().size() > 1) {
350  if (simulator.vanguard().grid().comm().rank() == 0)
351  eclWriter_->setTransmissibilities(&simulator.vanguard().globalTransmissibility());
352  } else {
353  finishTransmissibilities();
354  eclWriter_->setTransmissibilities(&simulator.problem().eclTransmissibilities());
355  }
356 
357  std::function<unsigned int(unsigned int)> equilGridToGrid = [&simulator](unsigned int i) {
358  return simulator.vanguard().gridEquilIdxToGridIdx(i);
359  };
360 
361  this->eclWriter_->extractOutputTransAndNNC(equilGridToGrid);
362  }
363  simulator.vanguard().releaseGlobalTransmissibilities();
364 
365  const auto& eclState = simulator.vanguard().eclState();
366  const auto& schedule = simulator.vanguard().schedule();
367 
368  // Set the start time of the simulation
369  simulator.setStartTime(schedule.getStartTime());
370  simulator.setEndTime(schedule.simTime(schedule.size() - 1));
371 
372  // We want the episode index to be the same as the report step index to make
373  // things simpler, so we have to set the episode index to -1 because it is
374  // incremented by endEpisode(). The size of the initial time step and
375  // length of the initial episode is set to zero for the same reason.
376  simulator.setEpisodeIndex(-1);
377  simulator.setEpisodeLength(0.0);
378 
379  // the "NOGRAV" keyword from Frontsim or setting the EnableGravity to false
380  // disables gravity, else the standard value of the gravity constant at sea level
381  // on earth is used
382  this->gravity_ = 0.0;
383  if (Parameters::Get<Parameters::EnableGravity>() &&
384  eclState.getInitConfig().hasGravity())
385  {
386  // unit::gravity is 9.80665 m^2/s--i.e., standard measure at Tellus equator.
387  this->gravity_[dim - 1] = unit::gravity;
388  }
389 
390  if (this->enableTuning_) {
391  // if support for the TUNING keyword is enabled, we get the initial time
392  // steping parameters from it instead of from command line parameters
393  const auto& tuning = schedule[0].tuning();
394  this->initialTimeStepSize_ = tuning.TSINIT.has_value() ? tuning.TSINIT.value() : -1.0;
395  this->maxTimeStepAfterWellEvent_ = tuning.TMAXWC;
396  }
397 
398  // conserve inner energy instead of enthalpy if TEMP is used
399  // or THERMAL and parameter ConserveInnerEnergyThermal is true (default false)
400  bool isThermal = eclState.getSimulationConfig().isThermal();
401  bool isTemp = eclState.getSimulationConfig().isTemp();
402  bool conserveInnerEnergy = isTemp || (isThermal && Parameters::Get<Parameters::ConserveInnerEnergyThermal>());
403  FluidSystem::setEnergyEqualEnthalpy(conserveInnerEnergy);
404 
405  if (FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx) &&
406  FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx)) {
407  this->maxOilSaturation_.resize(this->model().numGridDof(), 0.0);
408  }
409 
410  this->readRockParameters_(simulator.vanguard().cellCenterDepths(),
411  [&simulator](const unsigned idx)
412  {
413  std::array<int,dim> coords;
414  simulator.vanguard().cartesianCoordinate(idx, coords);
415  std::ranges::transform(coords, coords.begin(),
416  [](const auto c) { return c + 1; });
417  return coords;
418  });
419 
420  this->readMaterialParameters_();
421  this->readThermalParameters_();
422 
423  // write the static output files (EGRID, INIT)
424  if (enableEclOutput_) {
425  this->eclWriter_->writeInit();
426  }
427 
428  finishTransmissibilities();
429 
430  const auto& initconfig = eclState.getInitConfig();
431  this->tracerModel_.init(initconfig.restartRequested());
432  if (initconfig.restartRequested()) {
433  this->readEclRestartSolution_();
434  }
435  else {
436  this->readInitialCondition_();
437  }
438  this->temperatureModel_.init();
439  this->tracerModel_.prepareTracerBatches();
440 
441  this->updatePffDofData_();
442 
443  if constexpr (getPropValue<TypeTag, Properties::EnablePolymer>()) {
444  const auto& vanguard = this->simulator().vanguard();
445  const auto& gridView = vanguard.gridView();
446  const int numElements = gridView.size(/*codim=*/0);
447  this->polymer_.maxAdsorption.resize(numElements, 0.0);
448  }
449 
450  this->readBoundaryConditions_();
451 
452  // compute and set eq weights based on initial b values
453  this->computeAndSetEqWeights_();
454 
455  if (this->enableDriftCompensation_ || this->enableDriftCompensationTemp_) {
456  this->drift_.resize(this->model().numGridDof());
457  this->drift_ = 0.0;
458  }
459 
460  // after finishing the initialization and writing the initial solution, we move
461  // to the first "real" episode/report step
462  // for restart the episode index and start is already set
463  if (!initconfig.restartRequested() && !eclState.getIOConfig().initOnly()) {
464  simulator.startNextEpisode(schedule.seconds(1));
465  simulator.setEpisodeIndex(0);
466  simulator.setTimeStepIndex(0);
467  }
468 
469  if (Parameters::Get<Parameters::CheckSatfuncConsistency>() &&
470  ! this->satfuncConsistencyRequirementsMet())
471  {
472  // User requested that saturation functions be checked for
473  // consistency and essential/critical requirements are not met.
474  // Abort simulation run.
475  //
476  // Note: We need synchronisation here lest ranks other than the
477  // I/O rank throw exceptions too early thereby risking an
478  // incomplete failure report being shown to the user.
479  this->simulator().vanguard().grid().comm().barrier();
480 
481  throw std::domain_error {
482  "Saturation function end-points do not "
483  "meet requisite consistency conditions"
484  };
485  }
486 
487  // TODO: move to the end for later refactoring of the function finishInit()
488  //
489  // deal with DRSDT
490  this->mixControls_.init(this->model().numGridDof(),
491  this->episodeIndex(),
492  eclState.runspec().tabdims().getNumPVTTables());
493 
494  if (this->enableVtkOutput_() && eclState.getIOConfig().initOnly()) {
495  simulator.setTimeStepSize(0.0);
496  simulator.model().applyInitialSolution();
498  }
499 
500  if (!eclState.getIOConfig().initOnly()) {
501  if (!this->enableTuning_ && eclState.getSimulationConfig().anyTUNING()) {
502  OpmLog::info("\nThe deck has TUNING in the SCHEDULE section, but "
503  "it is ignored due\nto the flag --enable-tuning=false. "
504  "Set this flag to true to activate it.\n"
505  "Manually tuning the simulator with the TUNING keyword may "
506  "increase run time.\nIt is recommended using the simulator's "
507  "default tuning (--enable-tuning=false).");
508  }
509  }
510  }
511 
515  void endTimeStep() override
516  {
518  this->endStepApplyAction();
519  }
520 
521  void endStepApplyAction()
522  {
523  // After the solution is updated, the values in output module needs
524  // also updated.
525  this->eclWriter().mutableOutputModule().invalidateLocalData();
526 
527  // For CpGrid with LGRs, ecl/vtk output is not supported yet.
528  const auto& grid = this->simulator().vanguard().gridView().grid();
529 
530  using GridType = std::remove_cv_t<std::remove_reference_t<decltype(grid)>>;
531  constexpr bool isCpGrid = std::is_same_v<GridType, Dune::CpGrid>;
532  if (!isCpGrid || (grid.maxLevel() == 0)) {
533  this->eclWriter_->evalSummaryState(!this->episodeWillBeOver());
534  }
535 
536  {
537  OPM_TIMEBLOCK(applyActions);
538 
539  const int episodeIdx = this->episodeIndex();
540  auto& simulator = this->simulator();
541 
542  // Clear out any existing events as these have already been
543  // processed when we're running an action block
544  this->simulator().vanguard().schedule().clearEvents(episodeIdx);
545 
546  // Re-ordering in case of Alugrid
547  this->actionHandler_
548  .applyActions(episodeIdx, simulator.time() + simulator.timeStepSize(),
549  [this](const bool global)
550  {
551  using TransUpdateQuantities = typename
552  Vanguard::TransmissibilityType::TransUpdateQuantities;
553 
554  this->transmissibilities_
555  .update(global, TransUpdateQuantities::All,
556  [&vg = this->simulator().vanguard()]
557  (const unsigned int i)
558  {
559  return vg.gridIdxToEquilGridIdx(i);
560  });
561  });
562  }
563  }
564 
568  void endEpisode() override
569  {
570  OPM_TIMEBLOCK(endEpisode);
571 
572  // Rerun UDQ assignents following action processing on the final
573  // time step of this episode to make sure that any UDQ ASSIGN
574  // operations triggered in action blocks take effect. This is
575  // mainly to work around a shortcoming of the ScheduleState copy
576  // constructor which clears pending UDQ assignments under the
577  // assumption that all such assignments have been processed. If an
578  // action block happens to trigger on the final time step of an
579  // episode and that action block runs a UDQ assignment, then that
580  // assignment would be dropped and the rest of the simulator will
581  // never see its effect without this hack.
582  this->actionHandler_
583  .evalUDQAssignments(this->episodeIndex(), this->simulator().vanguard().udqState());
584 
586  }
587 
588  void writeReports(const SimulatorTimer& timer)
589  {
590  if (this->enableEclOutput_) {
591  this->eclWriter_->writeReports(timer);
592  }
593  }
594 
595 
600  void writeOutput(const bool verbose) override
601  {
603 
604  const auto isSubStep = !this->episodeWillBeOver();
605 
606  auto localCellData = data::Solution {};
607 
608 #if HAVE_DAMARIS
609  // N.B. the Damaris output has to be done before the ECL output as the ECL one
610  // does all kinds of std::move() relocation of data
611  if (this->enableDamarisOutput_ && (this->damarisWriter_ != nullptr)) {
612  this->damarisWriter_->writeOutput(localCellData, isSubStep);
613  }
614 #endif
615 
616  if (this->enableEclOutput_ && (this->eclWriter_ != nullptr)) {
617  this->eclWriter_->writeOutput(std::move(localCellData), isSubStep,
618  this->simulator().vanguard().schedule()
619  .exitStatus().has_value());
620  }
621  }
622 
623  void finalizeOutput()
624  {
625  OPM_TIMEBLOCK(finalizeOutput);
626  // this will write all pending output to disk
627  // to avoid corruption of output files
628  eclWriter_.reset();
629  }
630 
631 
635  void initialSolutionApplied() override
636  {
638 
639  // let the object for threshold pressures initialize itself. this is done only at
640  // this point, because determining the threshold pressures may require to access
641  // the initial solution.
642  this->thresholdPressures_.finishInit();
643 
644  // For CpGrid with LGRs, ecl-output is not supported yet.
645  const auto& grid = this->simulator().vanguard().gridView().grid();
646 
647  using GridType = std::remove_cv_t<std::remove_reference_t<decltype(grid)>>;
648  constexpr bool isCpGrid = std::is_same_v<GridType, Dune::CpGrid>;
649  // Skip - for now - calculate the initial fip values for CpGrid with LGRs.
650  if (!isCpGrid || (grid.maxLevel() == 0)) {
651  if (this->simulator().episodeIndex() == 0) {
652  eclWriter_->writeInitialFIPReport();
653  }
654  }
655  }
656 
657  void addToSourceDense(RateVector& rate,
658  unsigned globalDofIdx,
659  unsigned timeIdx) const override
660  {
661  this->aquiferModel_.addToSource(rate, globalDofIdx, timeIdx);
662 
663  // Add source term from deck
664  const auto& source = this->simulator().vanguard().schedule()[this->episodeIndex()].source();
665  std::array<int,3> ijk;
666  this->simulator().vanguard().cartesianCoordinate(globalDofIdx, ijk);
667 
668  if (source.hasSource(ijk)) {
669  const int pvtRegionIdx = this->pvtRegionIndex(globalDofIdx);
670  static std::array<SourceComponent, 3> sc_map = {SourceComponent::WATER, SourceComponent::OIL, SourceComponent::GAS};
671  static std::array<int, 3> phidx_map = {FluidSystem::waterPhaseIdx, FluidSystem::oilPhaseIdx, FluidSystem::gasPhaseIdx};
672  static std::array<int, 3> cidx_map = {waterCompIdx, oilCompIdx, gasCompIdx};
673 
674  for (unsigned i = 0; i < phidx_map.size(); ++i) {
675  const auto phaseIdx = phidx_map[i];
676  const auto sourceComp = sc_map[i];
677  const auto compIdx = cidx_map[i];
678  if (!FluidSystem::phaseIsActive(phaseIdx)) {
679  continue;
680  }
681  Scalar mass_rate = source.rate(ijk, sourceComp) / this->model().dofTotalVolume(globalDofIdx);
682  if constexpr (getPropValue<TypeTag, Properties::BlackoilConserveSurfaceVolume>()) {
683  mass_rate /= FluidSystem::referenceDensity(phaseIdx, pvtRegionIdx);
684  }
685  rate[FluidSystem::canonicalToActiveCompIdx(compIdx)] += mass_rate;
686  }
687 
688  if constexpr (enableSolvent) {
689  Scalar mass_rate = source.rate(ijk, SourceComponent::SOLVENT) / this->model().dofTotalVolume(globalDofIdx);
690  if constexpr (getPropValue<TypeTag, Properties::BlackoilConserveSurfaceVolume>()) {
691  const auto& solventPvt = SolventModule::solventPvt();
692  mass_rate /= solventPvt.referenceDensity(pvtRegionIdx);
693  }
694  rate[Indices::contiSolventEqIdx] += mass_rate;
695  }
696  if constexpr (enablePolymer) {
697  rate[Indices::polymerConcentrationIdx] += source.rate(ijk, SourceComponent::POLYMER) / this->model().dofTotalVolume(globalDofIdx);
698  }
699  if constexpr (enableMICP) {
700  rate[Indices::microbialConcentrationIdx] += source.rate(ijk, SourceComponent::MICR) / this->model().dofTotalVolume(globalDofIdx);
701  rate[Indices::oxygenConcentrationIdx] += source.rate(ijk, SourceComponent::OXYG) / this->model().dofTotalVolume(globalDofIdx);
702  rate[Indices::ureaConcentrationIdx] += source.rate(ijk, SourceComponent::UREA) / (this->model().dofTotalVolume(globalDofIdx));
703  }
704  if constexpr (energyModuleType == EnergyModules::FullyImplicitThermal) {
705  for (unsigned i = 0; i < phidx_map.size(); ++i) {
706  const auto phaseIdx = phidx_map[i];
707  if (!FluidSystem::phaseIsActive(phaseIdx)) {
708  continue;
709  }
710  const auto sourceComp = sc_map[i];
711  const auto source_hrate = source.hrate(ijk, sourceComp);
712  if (source_hrate) {
713  rate[Indices::contiEnergyEqIdx] += source_hrate.value() / this->model().dofTotalVolume(globalDofIdx);
714  } else {
715  const auto& intQuants = this->simulator().model().intensiveQuantities(globalDofIdx, /*timeIdx*/ 0);
716  auto fs = intQuants.fluidState();
717  // if temperature is not set, use cell temperature as default
718  const auto source_temp = source.temperature(ijk, sourceComp);
719  if (source_temp) {
720  Scalar temperature = source_temp.value();
721  fs.setTemperature(temperature);
722  }
723  const auto& h = FluidSystem::enthalpy(fs, phaseIdx, pvtRegionIdx);
724  Scalar mass_rate = source.rate(ijk, sourceComp)/ this->model().dofTotalVolume(globalDofIdx);
725  Scalar energy_rate = getValue(h)*mass_rate;
726  rate[Indices::contiEnergyEqIdx] += energy_rate;
727  }
728  }
729  }
730  }
731 
732  // if requested, compensate systematic mass loss for cells which were "well
733  // behaved" in the last time step
734  if (this->enableDriftCompensation_) {
735  const auto& simulator = this->simulator();
736  const auto& model = this->model();
737 
738  // we use a lower tolerance for the compensation too
739  // assure the added drift from the last step does not
740  // cause convergence issues on the current step
741  Scalar maxCompensation = model.newtonMethod().tolerance()/10;
742  Scalar poro = this->porosity(globalDofIdx, timeIdx);
743  Scalar dt = simulator.timeStepSize();
744  EqVector dofDriftRate = this->drift_[globalDofIdx];
745  dofDriftRate /= dt*model.dofTotalVolume(globalDofIdx);
746 
747  // restrict drift compensation to the CNV tolerance
748  for (unsigned eqIdx = 0; eqIdx < numEq; ++ eqIdx) {
749  Scalar cnv = std::abs(dofDriftRate[eqIdx])*dt*model.eqWeight(globalDofIdx, eqIdx)/poro;
750  if (cnv > maxCompensation) {
751  dofDriftRate[eqIdx] *= maxCompensation/cnv;
752  }
753  }
754 
755  for (unsigned eqIdx = 0; eqIdx < numEq; ++ eqIdx)
756  rate[eqIdx] -= dofDriftRate[eqIdx];
757  }
758  }
759 
763  template <class LhsEval, class Callback>
764  LhsEval permFactTransMultiplier(const IntensiveQuantities& intQuants, unsigned elementIdx, Callback& obtain) const
765  {
766  OPM_TIMEBLOCK_LOCAL(permFactTransMultiplier, Subsystem::PvtProps);
767  if constexpr (enableSaltPrecipitation) {
768  const auto& fs = intQuants.fluidState();
769  unsigned tableIdx = this->simulator().problem().satnumRegionIndex(elementIdx);
770  LhsEval porosityFactor = obtain(1. - fs.saltSaturation());
771  porosityFactor = min(porosityFactor, 1.0);
772  const auto& permfactTable = BrineModule::permfactTable(tableIdx);
773  return permfactTable.eval(porosityFactor, /*extrapolation=*/true);
774  }
775  else if constexpr (enableBioeffects) {
776  return obtain(intQuants.permFactor());
777  }
778  else {
779  return 1.0;
780  }
781  }
782 
783  // temporary solution to facilitate output of initial state from flow
784  const InitialFluidState& initialFluidState(unsigned globalDofIdx) const
785  { return initialFluidStates_[globalDofIdx]; }
786 
787  std::vector<InitialFluidState>& initialFluidStates()
788  { return initialFluidStates_; }
789 
790  const std::vector<InitialFluidState>& initialFluidStates() const
791  { return initialFluidStates_; }
792 
793  const EclipseIO& eclIO() const
794  { return eclWriter_->eclIO(); }
795 
796  void setSubStepReport(const SimulatorReportSingle& report)
797  { return eclWriter_->setSubStepReport(report); }
798 
799  void setSimulationReport(const SimulatorReport& report)
800  { return eclWriter_->setSimulationReport(report); }
801 
802  InitialFluidState boundaryFluidState(unsigned globalDofIdx, const int directionId) const
803  {
804  OPM_TIMEBLOCK_LOCAL(boundaryFluidState, Subsystem::Assembly);
805  const auto& bcprop = this->simulator().vanguard().schedule()[this->episodeIndex()].bcprop;
806  if (bcprop.size() > 0) {
807  FaceDir::DirEnum dir = FaceDir::FromIntersectionIndex(directionId);
808 
809  // index == 0: no boundary conditions for this
810  // global cell and direction
811  if (this->bcindex_(dir)[globalDofIdx] == 0)
812  return initialFluidStates_[globalDofIdx];
813 
814  const auto& bc = bcprop[this->bcindex_(dir)[globalDofIdx]];
815  if (bc.bctype == BCType::DIRICHLET )
816  {
817  InitialFluidState fluidState;
818  const int pvtRegionIdx = this->pvtRegionIndex(globalDofIdx);
819  fluidState.setPvtRegionIndex(pvtRegionIdx);
820 
821  switch (bc.component) {
822  case BCComponent::OIL:
823  if (!FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx))
824  throw std::logic_error("oil is not active and you're trying to add oil BC");
825 
826  fluidState.setSaturation(FluidSystem::oilPhaseIdx, 1.0);
827  break;
828  case BCComponent::GAS:
829  if (!FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx))
830  throw std::logic_error("gas is not active and you're trying to add gas BC");
831 
832  fluidState.setSaturation(FluidSystem::gasPhaseIdx, 1.0);
833  break;
834  case BCComponent::WATER:
835  if (!FluidSystem::phaseIsActive(FluidSystem::waterPhaseIdx))
836  throw std::logic_error("water is not active and you're trying to add water BC");
837 
838  fluidState.setSaturation(FluidSystem::waterPhaseIdx, 1.0);
839  break;
840  case BCComponent::SOLVENT:
841  case BCComponent::POLYMER:
842  case BCComponent::MICR:
843  case BCComponent::OXYG:
844  case BCComponent::UREA:
845  case BCComponent::NONE:
846  throw std::logic_error("you need to specify a valid component (OIL, WATER or GAS) when DIRICHLET type is set in BC");
847  }
848  fluidState.setTotalSaturation(1.0);
849  double pressure = initialFluidStates_[globalDofIdx].pressure(this->refPressurePhaseIdx_());
850  const auto pressure_input = bc.pressure;
851  if (pressure_input) {
852  pressure = *pressure_input;
853  }
854 
855  std::array<Scalar, numPhases> pc = {0};
856  const auto& matParams = this->materialLawParams(globalDofIdx);
857  MaterialLaw::capillaryPressures(pc, matParams, fluidState);
858  Valgrind::CheckDefined(pressure);
859  Valgrind::CheckDefined(pc);
860  for (unsigned activePhaseIdx = 0; activePhaseIdx < FluidSystem::numActivePhases(); ++activePhaseIdx) {
861  const auto phaseIdx = FluidSystem::activeToCanonicalPhaseIdx(activePhaseIdx);
862  if (Indices::oilEnabled)
863  fluidState.setPressure(phaseIdx, pressure + (pc[phaseIdx] - pc[oilPhaseIdx]));
864  else if (Indices::gasEnabled)
865  fluidState.setPressure(phaseIdx, pressure + (pc[phaseIdx] - pc[gasPhaseIdx]));
866  else if (Indices::waterEnabled)
867  //single (water) phase
868  fluidState.setPressure(phaseIdx, pressure);
869  }
870  if constexpr (energyModuleType != EnergyModules::NoTemperature) {
871  double temperature = initialFluidStates_[globalDofIdx].temperature(0); // we only have one temperature
872  const auto temperature_input = bc.temperature;
873  if(temperature_input)
874  temperature = *temperature_input;
875  fluidState.setTemperature(temperature);
876  }
877 
878  if constexpr (enableDissolvedGas) {
879  if (FluidSystem::enableDissolvedGas()) {
880  fluidState.setRs(0.0);
881  fluidState.setRv(0.0);
882  }
883  }
884  if constexpr (enableDisgasInWater) {
885  if (FluidSystem::enableDissolvedGasInWater()) {
886  fluidState.setRsw(0.0);
887  }
888  }
889  if constexpr (enableVapwat) {
890  if (FluidSystem::enableVaporizedWater()) {
891  fluidState.setRvw(0.0);
892  }
893  }
894 
895  for (unsigned activePhaseIdx = 0; activePhaseIdx < FluidSystem::numActivePhases(); ++activePhaseIdx) {
896  const auto phaseIdx = FluidSystem::activeToCanonicalPhaseIdx(activePhaseIdx);
897 
898  const auto& b = FluidSystem::inverseFormationVolumeFactor(fluidState, phaseIdx, pvtRegionIdx);
899  fluidState.setInvB(phaseIdx, b);
900 
901  const auto& rho = FluidSystem::density(fluidState, phaseIdx, pvtRegionIdx);
902  fluidState.setDensity(phaseIdx, rho);
903  if constexpr (energyModuleType == EnergyModules::FullyImplicitThermal) {
904  const auto& h = FluidSystem::enthalpy(fluidState, phaseIdx, pvtRegionIdx);
905  fluidState.setEnthalpy(phaseIdx, h);
906  }
907  }
908  fluidState.checkDefined();
909  return fluidState;
910  }
911  }
912  return initialFluidStates_[globalDofIdx];
913  }
914 
915 
916  const EclWriterType& eclWriter() const
917  { return *eclWriter_; }
918 
919  EclWriterType& eclWriter()
920  { return *eclWriter_; }
921 
926  Scalar maxGasDissolutionFactor(unsigned timeIdx, unsigned globalDofIdx) const
927  {
928  return this->mixControls_.maxGasDissolutionFactor(timeIdx, globalDofIdx,
929  this->episodeIndex(),
930  this->pvtRegionIndex(globalDofIdx));
931  }
932 
937  Scalar maxOilVaporizationFactor(unsigned timeIdx, unsigned globalDofIdx) const
938  {
939  return this->mixControls_.maxOilVaporizationFactor(timeIdx, globalDofIdx,
940  this->episodeIndex(),
941  this->pvtRegionIndex(globalDofIdx));
942  }
943 
956  {
957  const auto& rspec = this->simulator().vanguard().eclState().runspec();
958  const bool tpsaActive = rspec.mech() && rspec.mechSolver().tpsa();
959  if (tpsaActive) {
960  return false;
961  }
962 
963  int episodeIdx = this->episodeIndex();
964  return !this->mixControls_.drsdtActive(episodeIdx) &&
965  !this->mixControls_.drvdtActive(episodeIdx) &&
966  this->rockCompPoroMultWc_.empty() &&
967  this->rockCompPoroMult_.empty();
968  }
969 
976  template <class Context>
977  void initial(PrimaryVariables& values, const Context& context, unsigned spaceIdx, unsigned timeIdx) const
978  {
979  unsigned globalDofIdx = context.globalSpaceIndex(spaceIdx, timeIdx);
980 
981  values.setPvtRegionIndex(pvtRegionIndex(context, spaceIdx, timeIdx));
982  values.assignNaive(initialFluidStates_[globalDofIdx]);
983 
984  if constexpr (enableSolvent) {
985  SolventModule::assignPrimaryVars(values,
986  this->solventSaturation_[globalDofIdx],
987  this->solventRsw_[globalDofIdx]);
988  }
989 
990  if constexpr (enablePolymer) {
991  values[Indices::polymerConcentrationIdx] = this->polymer_.concentration[globalDofIdx];
992  }
993 
994  if constexpr (enablePolymerMolarWeight) {
995  values[Indices::polymerMoleWeightIdx]= this->polymer_.moleWeight[globalDofIdx];
996  }
997 
998  if constexpr (enableBrine) {
999  if (enableSaltPrecipitation && values.primaryVarsMeaningBrine() == PrimaryVariables::BrineMeaning::Sp) {
1000  values[Indices::saltConcentrationIdx] = initialFluidStates_[globalDofIdx].saltSaturation();
1001  }
1002  else {
1003  values[Indices::saltConcentrationIdx] = initialFluidStates_[globalDofIdx].saltConcentration();
1004  }
1005  }
1006 
1007  if constexpr (enableBioeffects) {
1008  values[Indices::microbialConcentrationIdx] = this->bioeffects_.microbialConcentration[globalDofIdx];
1009  values[Indices::biofilmVolumeFractionIdx] = this->bioeffects_.biofilmVolumeFraction[globalDofIdx];
1010  if constexpr (enableMICP) {
1011  values[Indices::oxygenConcentrationIdx] = this->bioeffects_.oxygenConcentration[globalDofIdx];
1012  values[Indices::ureaConcentrationIdx] = this->bioeffects_.ureaConcentration[globalDofIdx];
1013  values[Indices::calciteVolumeFractionIdx] = this->bioeffects_.calciteVolumeFraction[globalDofIdx];
1014  }
1015  }
1016 
1017  values.checkDefined();
1018  }
1019 
1020 
1021  Scalar drsdtcon(unsigned elemIdx, int episodeIdx) const
1022  {
1023  return this->mixControls_.drsdtcon(elemIdx, episodeIdx,
1024  this->pvtRegionIndex(elemIdx));
1025  }
1026 
1027  bool drsdtconIsActive(unsigned elemIdx, int episodeIdx) const
1028  {
1029  return this->mixControls_.drsdtConvective(episodeIdx, this->pvtRegionIndex(elemIdx));
1030  }
1031 
1037  template <class Context>
1038  void boundary(BoundaryRateVector& values,
1039  const Context& context,
1040  unsigned spaceIdx,
1041  unsigned timeIdx) const
1042  {
1043  OPM_TIMEBLOCK_LOCAL(eclProblemBoundary, Subsystem::Assembly);
1044  if (!context.intersection(spaceIdx).boundary())
1045  return;
1046 
1047  if constexpr (energyModuleType != EnergyModules::FullyImplicitThermal || !enableThermalFluxBoundaries)
1048  values.setNoFlow();
1049  else {
1050  // in the energy case we need to specify a non-trivial boundary condition
1051  // because the geothermal gradient needs to be maintained. for this, we
1052  // simply assume the initial temperature at the boundary and specify the
1053  // thermal flow accordingly. in this context, "thermal flow" means energy
1054  // flow due to a temerature gradient while assuming no-flow for mass
1055  unsigned interiorDofIdx = context.interiorScvIndex(spaceIdx, timeIdx);
1056  unsigned globalDofIdx = context.globalSpaceIndex(interiorDofIdx, timeIdx);
1057  values.setThermalFlow(context, spaceIdx, timeIdx, this->initialFluidStates_[globalDofIdx] );
1058  }
1059 
1060  if (this->nonTrivialBoundaryConditions()) {
1061  unsigned indexInInside = context.intersection(spaceIdx).indexInInside();
1062  unsigned interiorDofIdx = context.interiorScvIndex(spaceIdx, timeIdx);
1063  unsigned globalDofIdx = context.globalSpaceIndex(interiorDofIdx, timeIdx);
1064  unsigned pvtRegionIdx = pvtRegionIndex(context, spaceIdx, timeIdx);
1065  const auto [type, massrate] = this->boundaryCondition(globalDofIdx, indexInInside);
1066  if (type == BCType::THERMAL)
1067  values.setThermalFlow(context, spaceIdx, timeIdx, this->boundaryFluidState(globalDofIdx, indexInInside));
1068  else if (type == BCType::FREE || type == BCType::DIRICHLET)
1069  values.setFreeFlow(context, spaceIdx, timeIdx, this->boundaryFluidState(globalDofIdx, indexInInside));
1070  else if (type == BCType::RATE)
1071  values.setMassRate(massrate, pvtRegionIdx);
1072  }
1073  }
1074 
1079  void readSolutionFromOutputModule(const int restart_step, bool fip_init)
1080  {
1081  auto& simulator = this->simulator();
1082  const auto& eclState = simulator.vanguard().eclState();
1083 
1084  std::size_t numElems = this->model().numGridDof();
1085  this->initialFluidStates_.resize(numElems);
1086  if constexpr (enableSolvent) {
1087  this->solventSaturation_.resize(numElems, 0.0);
1088  this->solventRsw_.resize(numElems, 0.0);
1089  }
1090 
1091  if constexpr (enablePolymer)
1092  this->polymer_.concentration.resize(numElems, 0.0);
1093 
1094  if constexpr (enablePolymerMolarWeight) {
1095  const std::string msg {"Support of the RESTART for polymer molecular weight "
1096  "is not implemented yet. The polymer weight value will be "
1097  "zero when RESTART begins"};
1098  OpmLog::warning("NO_POLYMW_RESTART", msg);
1099  this->polymer_.moleWeight.resize(numElems, 0.0);
1100  }
1101 
1102  if constexpr (enableBioeffects) {
1103  this->bioeffects_.resize(numElems);
1104  }
1105 
1106  // Initialize mixing controls before trying to set any lastRx valuesx
1107  this->mixControls_.init(numElems, restart_step, eclState.runspec().tabdims().getNumPVTTables());
1108 
1109  if constexpr (enableBioeffects) {
1110  this->bioeffects_ = this->eclWriter_->outputModule().getBioeffects().getSolution();
1111  }
1112 
1113  for (std::size_t elemIdx = 0; elemIdx < numElems; ++elemIdx) {
1114  auto& elemFluidState = this->initialFluidStates_[elemIdx];
1115  elemFluidState.setPvtRegionIndex(pvtRegionIndex(elemIdx));
1116  this->eclWriter_->outputModule().initHysteresisParams(simulator, elemIdx);
1117  this->eclWriter_->outputModule().assignToFluidState(elemFluidState, elemIdx);
1118 
1119  // Note: Function processRestartSaturations_() mutates the
1120  // 'ssol' argument--the value from the restart file--if solvent
1121  // is enabled. Then, store the updated solvent saturation into
1122  // 'solventSaturation_'. Otherwise, just pass a dummy value to
1123  // the function and discard the unchanged result. Do not index
1124  // into 'solventSaturation_' unless solvent is enabled.
1125  {
1126  auto ssol = enableSolvent
1127  ? this->eclWriter_->outputModule().getSolventSaturation(elemIdx)
1128  : Scalar(0);
1129 
1130  this->processRestartSaturations_(elemFluidState, ssol);
1131 
1132  if constexpr (enableSolvent) {
1133  this->solventSaturation_[elemIdx] = ssol;
1134  this->solventRsw_[elemIdx] = this->eclWriter_->outputModule().getSolventRsw(elemIdx);
1135  }
1136  }
1137 
1138  // For CO2STORE and H2STORE we need to set the initial temperature for isothermal simulations
1139  if constexpr (energyModuleType != EnergyModules::NoTemperature) {
1140  bool needTemperature = (eclState.runspec().co2Storage() || eclState.runspec().h2Storage());
1141  if (needTemperature) {
1142  const auto& fp = simulator.vanguard().eclState().fieldProps();
1143  elemFluidState.setTemperature(fp.get_double("TEMPI")[elemIdx]);
1144  }
1145  }
1146 
1147  this->mixControls_.updateLastValues(elemIdx, elemFluidState.Rs(), elemFluidState.Rv());
1148 
1149  if constexpr (enablePolymer)
1150  this->polymer_.concentration[elemIdx] = this->eclWriter_->outputModule().getPolymerConcentration(elemIdx);
1151  // if we need to restart for polymer molecular weight simulation, we need to add related here
1152  }
1153 
1154  const int episodeIdx = this->episodeIndex();
1155  this->mixControls_.updateMaxValues(episodeIdx, simulator.timeStepSize());
1156 
1157  // assign the restart solution to the current solution. note that we still need
1158  // to compute real initial solution after this because the initial fluid states
1159  // need to be correct for stuff like boundary conditions.
1160  auto& sol = this->model().solution(/*timeIdx=*/0);
1161  const auto& gridView = this->gridView();
1162  ElementContext elemCtx(simulator);
1163  for (const auto& elem : elements(gridView, Dune::Partitions::interior)) {
1164  elemCtx.updatePrimaryStencil(elem);
1165  int elemIdx = elemCtx.globalSpaceIndex(/*spaceIdx=*/0, /*timeIdx=*/0);
1166  this->initial(sol[elemIdx], elemCtx, /*spaceIdx=*/0, /*timeIdx=*/0);
1167  }
1168 
1169  // make sure that the ghost and overlap entities exhibit the correct
1170  // solution. alternatively, this could be done in the loop above by also
1171  // considering non-interior elements. Since the initial() method might not work
1172  // 100% correctly for such elements, let's play safe and explicitly synchronize
1173  // using message passing.
1174  this->model().syncOverlap();
1175 
1176  if (fip_init) {
1177  this->updateReferencePorosity_();
1178  this->mixControls_.init(this->model().numGridDof(),
1179  this->episodeIndex(),
1180  eclState.runspec().tabdims().getNumPVTTables());
1181  }
1182  }
1183 
1187  Scalar thresholdPressure(unsigned elem1Idx, unsigned elem2Idx) const
1188  { return thresholdPressures_.thresholdPressure(elem1Idx, elem2Idx); }
1189 
1190  const FlowThresholdPressure<TypeTag>& thresholdPressure() const
1191  { return thresholdPressures_; }
1192 
1193  FlowThresholdPressure<TypeTag>& thresholdPressure()
1194  { return thresholdPressures_; }
1195 
1196  const ModuleParams& moduleParams() const
1197  {
1198  return moduleParams_;
1199  }
1200 
1201  template<class Serializer>
1202  void serializeOp(Serializer& serializer)
1203  {
1204  serializer(static_cast<FlowProblemType&>(*this));
1205  serializer(mixControls_);
1206  serializer(*eclWriter_);
1207  }
1208 
1209 protected:
1210  void updateExplicitQuantities_(int episodeIdx, int timeStepSize, const bool first_step_after_restart) override
1211  {
1212  this->updateExplicitQuantities_(first_step_after_restart);
1213 
1214  if constexpr (getPropValue<TypeTag, Properties::EnablePolymer>())
1215  updateMaxPolymerAdsorption_();
1216 
1217  mixControls_.updateExplicitQuantities(episodeIdx, timeStepSize);
1218  }
1219 
1220  void updateMaxPolymerAdsorption_()
1221  {
1222  // we need to update the max polymer adsoption data for all elements
1223  this->updateProperty_("FlowProblemBlackoil::updateMaxPolymerAdsorption_() failed:",
1224  [this](unsigned compressedDofIdx, const IntensiveQuantities& iq)
1225  {
1226  this->updateMaxPolymerAdsorption_(compressedDofIdx,iq);
1227  });
1228  }
1229 
1230  bool updateMaxPolymerAdsorption_(unsigned compressedDofIdx, const IntensiveQuantities& iq)
1231  {
1232  const Scalar pa = scalarValue(iq.polymerAdsorption());
1233  auto& mpa = this->polymer_.maxAdsorption;
1234  if (mpa[compressedDofIdx] < pa) {
1235  mpa[compressedDofIdx] = pa;
1236  return true;
1237  } else {
1238  return false;
1239  }
1240  }
1241 
1242  void computeAndSetEqWeights_()
1243  {
1244  std::vector<Scalar> sumInvB(numPhases, 0.0);
1245  const auto& gridView = this->gridView();
1246  ElementContext elemCtx(this->simulator());
1247  for(const auto& elem: elements(gridView, Dune::Partitions::interior)) {
1248  elemCtx.updatePrimaryStencil(elem);
1249  int elemIdx = elemCtx.globalSpaceIndex(/*spaceIdx=*/0, /*timeIdx=*/0);
1250  const auto& dofFluidState = this->initialFluidStates_[elemIdx];
1251  for (unsigned phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) {
1252  if (!FluidSystem::phaseIsActive(phaseIdx))
1253  continue;
1254 
1255  sumInvB[phaseIdx] += dofFluidState.invB(phaseIdx);
1256  }
1257  }
1258 
1259  std::size_t numDof = this->model().numGridDof();
1260  const auto& comm = this->simulator().vanguard().grid().comm();
1261  comm.sum(sumInvB.data(),sumInvB.size());
1262  Scalar numTotalDof = comm.sum(numDof);
1263 
1264  for (unsigned phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) {
1265  if (!FluidSystem::phaseIsActive(phaseIdx))
1266  continue;
1267 
1268  Scalar avgB = numTotalDof / sumInvB[phaseIdx];
1269  const unsigned solventCompIdx = FluidSystem::solventComponentIndex(phaseIdx);
1270  const unsigned activeSolventCompIdx = FluidSystem::canonicalToActiveCompIdx(solventCompIdx);
1271  this->model().setEqWeight(activeSolventCompIdx, avgB);
1272  }
1273  }
1274 
1275  // update the parameters needed for DRSDT and DRVDT
1276  bool updateCompositionChangeLimits_()
1277  {
1278  OPM_TIMEBLOCK(updateCompositionChangeLimits);
1279  // update the "last Rs" values for all elements, including the ones in the ghost
1280  // and overlap regions
1281  int episodeIdx = this->episodeIndex();
1282  std::array<bool,3> active{this->mixControls_.drsdtConvective(episodeIdx),
1283  this->mixControls_.drsdtActive(episodeIdx),
1284  this->mixControls_.drvdtActive(episodeIdx)};
1285  if (!active[0] && !active[1] && !active[2]) {
1286  return false;
1287  }
1288 
1289  this->updateProperty_("FlowProblemBlackoil::updateCompositionChangeLimits_()) failed:",
1290  [this,episodeIdx,active](unsigned compressedDofIdx,
1291  const IntensiveQuantities& iq)
1292  {
1293  const DimMatrix& perm = this->intrinsicPermeability(compressedDofIdx);
1294  const Scalar distZ = active[0] ? this->simulator().vanguard().cellThickness(compressedDofIdx) : 0.0;
1295  const int pvtRegionIdx = this->pvtRegionIndex(compressedDofIdx);
1296  this->mixControls_.update(compressedDofIdx,
1297  iq,
1298  episodeIdx,
1299  this->gravity_[dim - 1],
1300  perm[dim - 1][dim - 1],
1301  distZ,
1302  pvtRegionIdx);
1303  }
1304  );
1305 
1306  return true;
1307  }
1308 
1309  void readEclRestartSolution_()
1310  {
1311  // Throw an exception if the grid has LGRs. Refined grid are not supported for restart.
1312  if(this->simulator().vanguard().grid().maxLevel() > 0) {
1313  throw std::invalid_argument("Refined grids are not yet supported for restart ");
1314  }
1315 
1316  // Set the start time of the simulation
1317  auto& simulator = this->simulator();
1318  const auto& schedule = simulator.vanguard().schedule();
1319  const auto& eclState = simulator.vanguard().eclState();
1320  const auto& initconfig = eclState.getInitConfig();
1321  const int restart_step = initconfig.getRestartStep();
1322  {
1323  simulator.setTime(schedule.seconds(restart_step));
1324 
1325  simulator.startNextEpisode(simulator.startTime() + simulator.time(),
1326  schedule.stepLength(restart_step));
1327  simulator.setEpisodeIndex(restart_step);
1328  }
1329  this->eclWriter_->beginRestart();
1330 
1331  Scalar dt = std::min(this->eclWriter_->restartTimeStepSize(), simulator.episodeLength());
1332  simulator.setTimeStepSize(dt);
1333 
1334  this->readSolutionFromOutputModule(restart_step, false);
1335 
1336  this->eclWriter_->endRestart();
1337  }
1338 
1339  void readEquilInitialCondition_() override
1340  {
1341  const auto& simulator = this->simulator();
1342 
1343  // initial condition corresponds to hydrostatic conditions.
1344  EquilInitializer<TypeTag> equilInitializer(simulator, *(this->materialLawManager_));
1345 
1346  std::size_t numElems = this->model().numGridDof();
1347  this->initialFluidStates_.resize(numElems);
1348  for (std::size_t elemIdx = 0; elemIdx < numElems; ++elemIdx) {
1349  auto& elemFluidState = this->initialFluidStates_[elemIdx];
1350  elemFluidState.assign(equilInitializer.initialFluidState(elemIdx));
1351  }
1352  }
1353 
1354  void readExplicitInitialCondition_() override
1355  {
1356  const auto& simulator = this->simulator();
1357  const auto& vanguard = simulator.vanguard();
1358  const auto& eclState = vanguard.eclState();
1359  const auto& fp = eclState.fieldProps();
1360  bool has_swat = fp.has_double("SWAT");
1361  bool has_sgas = fp.has_double("SGAS");
1362  bool has_rs = fp.has_double("RS");
1363  bool has_rsw = fp.has_double("RSW");
1364  bool has_rv = fp.has_double("RV");
1365  bool has_rvw = fp.has_double("RVW");
1366  bool has_pressure = fp.has_double("PRESSURE");
1367  bool has_salt = fp.has_double("SALT");
1368  bool has_saltp = fp.has_double("SALTP");
1369 
1370  // make sure all required quantities are enables
1371  if (Indices::numPhases > 1) {
1372  if (FluidSystem::phaseIsActive(waterPhaseIdx) && !has_swat)
1373  throw std::runtime_error("The ECL input file requires the presence of the SWAT keyword if "
1374  "the water phase is active");
1375  if (FluidSystem::phaseIsActive(gasPhaseIdx) && !has_sgas && FluidSystem::phaseIsActive(oilPhaseIdx))
1376  throw std::runtime_error("The ECL input file requires the presence of the SGAS keyword if "
1377  "the gas phase is active");
1378  }
1379  if (!has_pressure)
1380  throw std::runtime_error("The ECL input file requires the presence of the PRESSURE "
1381  "keyword if the model is initialized explicitly");
1382  if (FluidSystem::enableDissolvedGas() && !has_rs)
1383  throw std::runtime_error("The ECL input file requires the RS keyword to be present if"
1384  " dissolved gas is enabled and the model is initialized explicitly");
1385  if (FluidSystem::enableDissolvedGasInWater() && !has_rsw)
1386  OpmLog::warning("The model is initialized explicitly and the RSW keyword is not present in the"
1387  " ECL input file. The RSW values are set equal to 0");
1388  if (FluidSystem::enableVaporizedOil() && !has_rv)
1389  throw std::runtime_error("The ECL input file requires the RV keyword to be present if"
1390  " vaporized oil is enabled and the model is initialized explicitly");
1391  if (FluidSystem::enableVaporizedWater() && !has_rvw)
1392  throw std::runtime_error("The ECL input file requires the RVW keyword to be present if"
1393  " vaporized water is enabled and the model is initialized explicitly");
1394  if (enableBrine && !has_salt)
1395  throw std::runtime_error("The ECL input file requires the SALT keyword to be present if"
1396  " brine is enabled and the model is initialized explicitly");
1397  if (enableSaltPrecipitation && !has_saltp)
1398  throw std::runtime_error("The ECL input file requires the SALTP keyword to be present if"
1399  " salt precipitation is enabled and the model is initialized explicitly");
1400 
1401  std::size_t numDof = this->model().numGridDof();
1402 
1403  initialFluidStates_.resize(numDof);
1404 
1405  std::vector<double> waterSaturationData;
1406  std::vector<double> gasSaturationData;
1407  std::vector<double> pressureData;
1408  std::vector<double> rsData;
1409  std::vector<double> rswData;
1410  std::vector<double> rvData;
1411  std::vector<double> rvwData;
1412  std::vector<double> tempiData;
1413  std::vector<double> saltData;
1414  std::vector<double> saltpData;
1415 
1416  if (FluidSystem::phaseIsActive(waterPhaseIdx) && Indices::numPhases > 1)
1417  waterSaturationData = fp.get_double("SWAT");
1418  else
1419  waterSaturationData.resize(numDof);
1420 
1421  if (FluidSystem::phaseIsActive(gasPhaseIdx) && FluidSystem::phaseIsActive(oilPhaseIdx))
1422  gasSaturationData = fp.get_double("SGAS");
1423  else
1424  gasSaturationData.resize(numDof);
1425 
1426  pressureData = fp.get_double("PRESSURE");
1427  if (FluidSystem::enableDissolvedGas())
1428  rsData = fp.get_double("RS");
1429 
1430  if (FluidSystem::enableDissolvedGasInWater() && has_rsw)
1431  rswData = fp.get_double("RSW");
1432 
1433  if (FluidSystem::enableVaporizedOil())
1434  rvData = fp.get_double("RV");
1435 
1436  if (FluidSystem::enableVaporizedWater())
1437  rvwData = fp.get_double("RVW");
1438 
1439  // initial reservoir temperature
1440  tempiData = fp.get_double("TEMPI");
1441 
1442  // initial salt concentration data
1443  if constexpr (enableBrine)
1444  saltData = fp.get_double("SALT");
1445 
1446  // initial precipitated salt saturation data
1447  if constexpr (enableSaltPrecipitation)
1448  saltpData = fp.get_double("SALTP");
1449 
1450  // calculate the initial fluid states
1451  for (std::size_t dofIdx = 0; dofIdx < numDof; ++dofIdx) {
1452  auto& dofFluidState = initialFluidStates_[dofIdx];
1453 
1454  dofFluidState.setPvtRegionIndex(pvtRegionIndex(dofIdx));
1455 
1457  // set temperature
1459  if constexpr (energyModuleType != EnergyModules::NoTemperature) {
1460  Scalar temperatureLoc = tempiData[dofIdx];
1461  if (!std::isfinite(temperatureLoc) || temperatureLoc <= 0)
1462  temperatureLoc = FluidSystem::surfaceTemperature;
1463  dofFluidState.setTemperature(temperatureLoc);
1464  }
1465 
1467  // set salt concentration
1469  if constexpr (enableBrine)
1470  dofFluidState.setSaltConcentration(saltData[dofIdx]);
1471 
1473  // set precipitated salt saturation
1475  if constexpr (enableSaltPrecipitation)
1476  dofFluidState.setSaltSaturation(saltpData[dofIdx]);
1477 
1479  // set saturations
1481  if (FluidSystem::phaseIsActive(FluidSystem::waterPhaseIdx))
1482  dofFluidState.setSaturation(FluidSystem::waterPhaseIdx,
1483  waterSaturationData[dofIdx]);
1484 
1485  if (FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx)){
1486  if (!FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx)){
1487  dofFluidState.setSaturation(FluidSystem::gasPhaseIdx,
1488  1.0
1489  - waterSaturationData[dofIdx]);
1490  }
1491  else
1492  dofFluidState.setSaturation(FluidSystem::gasPhaseIdx,
1493  gasSaturationData[dofIdx]);
1494  }
1495  if (FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx)) {
1496  const Scalar soil = 1.0 - waterSaturationData[dofIdx] - gasSaturationData[dofIdx];
1497  if (soil < smallSaturationTolerance_) {
1498  dofFluidState.setSaturation(FluidSystem::oilPhaseIdx, 0.0);
1499  }
1500  else {
1501  dofFluidState.setSaturation(FluidSystem::oilPhaseIdx, soil);
1502  }
1503  }
1504 
1506  // set phase pressures
1508  Scalar pressure = pressureData[dofIdx]; // oil pressure (or gas pressure for water-gas system or water pressure for single phase)
1509 
1510  // this assumes that capillary pressures only depend on the phase saturations
1511  // and possibly on temperature. (this is always the case for ECL problems.)
1512  std::array<Scalar, numPhases> pc = {0};
1513  const auto& matParams = this->materialLawParams(dofIdx);
1514  MaterialLaw::capillaryPressures(pc, matParams, dofFluidState);
1515  Valgrind::CheckDefined(pressure);
1516  Valgrind::CheckDefined(pc);
1517  for (unsigned phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) {
1518  if (!FluidSystem::phaseIsActive(phaseIdx))
1519  continue;
1520 
1521  if (Indices::oilEnabled)
1522  dofFluidState.setPressure(phaseIdx, pressure + (pc[phaseIdx] - pc[oilPhaseIdx]));
1523  else if (Indices::gasEnabled)
1524  dofFluidState.setPressure(phaseIdx, pressure + (pc[phaseIdx] - pc[gasPhaseIdx]));
1525  else if (Indices::waterEnabled)
1526  //single (water) phase
1527  dofFluidState.setPressure(phaseIdx, pressure);
1528  }
1529 
1530  if constexpr (enableDissolvedGas) {
1531  if (FluidSystem::enableDissolvedGas())
1532  dofFluidState.setRs(rsData[dofIdx]);
1533  else if (Indices::gasEnabled && Indices::oilEnabled)
1534  dofFluidState.setRs(0.0);
1535  if (FluidSystem::enableVaporizedOil())
1536  dofFluidState.setRv(rvData[dofIdx]);
1537  else if (Indices::gasEnabled && Indices::oilEnabled)
1538  dofFluidState.setRv(0.0);
1539  }
1540 
1541  if constexpr (enableDisgasInWater) {
1542  if (FluidSystem::enableDissolvedGasInWater() && has_rsw)
1543  dofFluidState.setRsw(rswData[dofIdx]);
1544  }
1545 
1546  if constexpr (enableVapwat) {
1547  if (FluidSystem::enableVaporizedWater())
1548  dofFluidState.setRvw(rvwData[dofIdx]);
1549  }
1550 
1552  // set invB_
1554  for (unsigned phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) {
1555  if (!FluidSystem::phaseIsActive(phaseIdx))
1556  continue;
1557 
1558  const auto& b = FluidSystem::inverseFormationVolumeFactor(dofFluidState, phaseIdx, pvtRegionIndex(dofIdx));
1559  dofFluidState.setInvB(phaseIdx, b);
1560 
1561  const auto& rho = FluidSystem::density(dofFluidState, phaseIdx, pvtRegionIndex(dofIdx));
1562  dofFluidState.setDensity(phaseIdx, rho);
1563 
1564  }
1565  }
1566  }
1567 
1568 
1569  void processRestartSaturations_(InitialFluidState& elemFluidState, Scalar& solventSaturation)
1570  {
1571  // each phase needs to be above certain value to be claimed to be existing
1572  // this is used to recover some RESTART running with the defaulted single-precision format
1573  Scalar sumSaturation = 0.0;
1574  for (std::size_t phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) {
1575  if (FluidSystem::phaseIsActive(phaseIdx)) {
1576  if (elemFluidState.saturation(phaseIdx) < smallSaturationTolerance_)
1577  elemFluidState.setSaturation(phaseIdx, 0.0);
1578 
1579  sumSaturation += elemFluidState.saturation(phaseIdx);
1580  }
1581 
1582  }
1583  if constexpr (enableSolvent) {
1584  if (solventSaturation < smallSaturationTolerance_)
1585  solventSaturation = 0.0;
1586 
1587  sumSaturation += solventSaturation;
1588  }
1589 
1590  assert(sumSaturation > 0.0);
1591 
1592  for (std::size_t phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) {
1593  if (FluidSystem::phaseIsActive(phaseIdx)) {
1594  const Scalar saturation = elemFluidState.saturation(phaseIdx) / sumSaturation;
1595  elemFluidState.setSaturation(phaseIdx, saturation);
1596  }
1597  }
1598  if constexpr (enableSolvent) {
1599  solventSaturation = solventSaturation / sumSaturation;
1600  }
1601  }
1602 
1603  void readInitialCondition_() override
1604  {
1605  FlowProblemType::readInitialCondition_();
1606 
1607  if constexpr (enableSolvent || enablePolymer || enablePolymerMolarWeight || enableBioeffects)
1608  this->readBlackoilExtentionsInitialConditions_(this->model().numGridDof(),
1609  enableSolvent,
1610  enablePolymer,
1611  enablePolymerMolarWeight,
1612  enableBioeffects,
1613  enableMICP);
1614 
1615  }
1616 
1617  void handleSolventBC(const BCProp::BCFace& bc, RateVector& rate) const override
1618  {
1619  if constexpr (!enableSolvent)
1620  throw std::logic_error("solvent is disabled and you're trying to add solvent to BC");
1621 
1622  rate[Indices::solventSaturationIdx] = bc.rate;
1623  }
1624 
1625  void handlePolymerBC(const BCProp::BCFace& bc, RateVector& rate) const override
1626  {
1627  if constexpr (!enablePolymer)
1628  throw std::logic_error("polymer is disabled and you're trying to add polymer to BC");
1629 
1630  rate[Indices::polymerConcentrationIdx] = bc.rate;
1631  }
1632 
1633  void handleMicrBC(const BCProp::BCFace& bc, RateVector& rate) const override
1634  {
1635  if constexpr (!enableMICP)
1636  throw std::logic_error("MICP is disabled and you're trying to add microbes to BC");
1637 
1638  rate[Indices::microbialConcentrationIdx] = bc.rate;
1639  }
1640 
1641  void handleOxygBC(const BCProp::BCFace& bc, RateVector& rate) const override
1642  {
1643  if constexpr (!enableMICP)
1644  throw std::logic_error("MICP is disabled and you're trying to add oxygen to BC");
1645 
1646  rate[Indices::oxygenConcentrationIdx] = bc.rate;
1647  }
1648 
1649  void handleUreaBC(const BCProp::BCFace& bc, RateVector& rate) const override
1650  {
1651  if constexpr (!enableMICP)
1652  throw std::logic_error("MICP is disabled and you're trying to add urea to BC");
1653 
1654  rate[Indices::ureaConcentrationIdx] = bc.rate;
1655  // since the urea concentration can be much larger than 1, then we apply a scaling factor
1656  rate[Indices::ureaConcentrationIdx] *= getPropValue<TypeTag, Properties::BlackOilUreaScalingFactor>();
1657  }
1658 
1659  void updateExplicitQuantities_(const bool first_step_after_restart)
1660  {
1661  OPM_TIMEBLOCK(updateExplicitQuantities);
1662  const bool invalidateFromMaxWaterSat = this->updateMaxWaterSaturation_();
1663  const bool invalidateFromMinPressure = this->updateMinPressure_();
1664 
1665  // update hysteresis and max oil saturation used in vappars
1666  const bool invalidateFromHyst = this->updateHysteresis_();
1667  const bool invalidateFromMaxOilSat = this->updateMaxOilSaturation_();
1668 
1669  // deal with DRSDT and DRVDT
1670  const bool invalidateDRDT = !first_step_after_restart && this->updateCompositionChangeLimits_();
1671 
1672  // the derivatives may have changed
1673  const bool invalidateIntensiveQuantities
1674  = invalidateFromMaxWaterSat || invalidateFromMinPressure || invalidateFromHyst || invalidateFromMaxOilSat || invalidateDRDT;
1675  if (invalidateIntensiveQuantities) {
1676  OPM_TIMEBLOCK(beginTimeStepInvalidateIntensiveQuantities);
1677  this->model().invalidateAndUpdateIntensiveQuantities(/*timeIdx=*/0);
1678  }
1679 
1680  this->updateRockCompTransMultVal_();
1681  }
1682 
1683  bool satfuncConsistencyRequirementsMet() const
1684  {
1685  if (const auto nph = FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx)
1686  + FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx)
1687  + FluidSystem::phaseIsActive(FluidSystem::waterPhaseIdx);
1688  nph < 2)
1689  {
1690  // Single phase runs don't need saturation functions and there's
1691  // nothing to do here. Return 'true' to tell caller that the
1692  // consistency requirements are Met.
1693  return true;
1694  }
1695 
1696  const auto numSamplePoints = static_cast<std::size_t>
1697  (Parameters::Get<Parameters::NumSatfuncConsistencySamplePoints>());
1698 
1699  auto sfuncConsistencyChecks =
1700  Satfunc::PhaseChecks::SatfuncConsistencyCheckManager<Scalar> {
1701  numSamplePoints, this->simulator().vanguard().eclState(),
1702  [&cmap = this->simulator().vanguard().cartesianIndexMapper()](const int elemIdx)
1703  { return cmap.cartesianIndex(elemIdx); }
1704  };
1705 
1706  const auto ioRank = 0;
1707  const auto isIoRank = this->simulator().vanguard()
1708  .grid().comm().rank() == ioRank;
1709 
1710  // Note: Run saturation function consistency checks on main grid
1711  // only (i.e., levelGridView(0)). These checks are not supported
1712  // for LGRs at this time.
1713  sfuncConsistencyChecks.collectFailuresTo(ioRank)
1714  .run(this->simulator().vanguard().grid().levelGridView(0),
1715  [&vg = this->simulator().vanguard(),
1716  &emap = this->simulator().model().elementMapper()]
1717  (const auto& elem)
1718  { return vg.gridIdxToEquilGridIdx(emap.index(elem)); });
1719 
1720  using ViolationLevel = typename Satfunc::PhaseChecks::
1722 
1723  auto reportFailures = [&sfuncConsistencyChecks]
1724  (const ViolationLevel level)
1725  {
1726  sfuncConsistencyChecks.reportFailures
1727  (level, [](std::string_view record)
1728  { OpmLog::info(std::string { record }); });
1729  };
1730 
1731  if (sfuncConsistencyChecks.anyFailedStandardChecks()) {
1732  if (isIoRank) {
1733  OpmLog::warning("Saturation Function "
1734  "End-point Consistency Problems");
1735 
1736  reportFailures(ViolationLevel::Standard);
1737  }
1738  }
1739 
1740  if (sfuncConsistencyChecks.anyFailedCriticalChecks()) {
1741  if (isIoRank) {
1742  OpmLog::error("Saturation Function "
1743  "End-point Consistency Failures");
1744 
1745  reportFailures(ViolationLevel::Critical);
1746  }
1747 
1748  // There are "critical" check failures. Report that consistency
1749  // requirements are not Met.
1750  return false;
1751  }
1752 
1753  // If we get here then there are no critical failures. Report
1754  // Met = true, i.e., that the consistency requirements ARE met.
1755  return true;
1756  }
1757 
1758  FlowThresholdPressure<TypeTag> thresholdPressures_;
1759 
1760  std::vector<InitialFluidState> initialFluidStates_;
1761 
1762  bool enableEclOutput_;
1763  std::unique_ptr<EclWriterType> eclWriter_;
1764 
1765  const Scalar smallSaturationTolerance_ = 1.e-6;
1766 #if HAVE_DAMARIS
1767  bool enableDamarisOutput_ = false ;
1768  std::unique_ptr<DamarisWriterType> damarisWriter_;
1769 #endif
1770  MixingRateControls<FluidSystem> mixControls_;
1771 
1772  ActionHandler<Scalar, IndexTraits> actionHandler_;
1773 
1774  ModuleParams moduleParams_;
1775 
1776  HybridNewton hybridNewton_;
1777 
1778 private:
1789  bool episodeWillBeOver() const override
1790  {
1791  const auto currTime = this->simulator().time()
1792  + this->simulator().timeStepSize();
1793 
1794  const auto nextReportStep =
1795  this->simulator().vanguard().schedule()
1796  .seconds(this->simulator().episodeIndex() + 1);
1797 
1798  const auto isSubStep = (nextReportStep - currTime)
1799  > (2 * std::numeric_limits<float>::epsilon()) * nextReportStep;
1800 
1801  return !isSubStep;
1802  }
1803 };
1804 
1805 } // namespace Opm
1806 
1807 #endif // OPM_FLOW_PROBLEM_BLACK_HPP
This problem simulates an input file given in the data format used by the commercial ECLiPSE simulato...
Definition: FlowProblemBlackoil.hpp:86
This problem simulates an input file given in the data format used by the commercial ECLiPSE simulato...
This class calculates the threshold pressure for grid faces according to the Eclipse Reference Manual...
Scalar maxGasDissolutionFactor(unsigned timeIdx, unsigned globalDofIdx) const
Returns the maximum value of the gas dissolution factor at the current time for a given degree of fre...
Definition: FlowProblemBlackoil.hpp:926
void boundary(BoundaryRateVector &values, const Context &context, unsigned spaceIdx, unsigned timeIdx) const
Evaluate the boundary conditions for a boundary segment.
Definition: FlowProblemBlackoil.hpp:1038
typename Properties::Detail::GetPropImpl< TypeTag, Property >::type::type GetPropType
get the type alias defined in the property (equivalent to old macro GET_PROP_TYPE(...))
Definition: propertysystem.hh:233
Definition: blackoilfoammodules.hh:57
void tryApplyHybridNewton()
Attempt to apply the Hybrid Newton correction at the current timestep.
Definition: HybridNewton.hpp:101
This problem simulates an input file given in the data format used by the commercial ECLiPSE simulato...
Definition: FlowProblem.hpp:92
unsigned pvtRegionIndex(const Context &context, unsigned spaceIdx, unsigned timeIdx) const
Returns the index of the relevant region for thermodynmic properties.
Definition: FlowProblem.hpp:901
void initial(PrimaryVariables &values, const Context &context, unsigned spaceIdx, unsigned timeIdx) const
Evaluate the initial value for a control volume.
Definition: FlowProblemBlackoil.hpp:977
const MaterialLawParams & materialLawParams(const Context &context, unsigned spaceIdx, unsigned timeIdx) const
Definition: FlowProblem.hpp:806
virtual void beginTimeStep()
Called by the simulator before each time integration.
Definition: FlowProblem.hpp:366
Scalar temperature(const Context &context, unsigned spaceIdx, unsigned timeIdx) const
Definition: FlowProblem.hpp:947
This class calculates the threshold pressure for grid faces according to the Eclipse Reference Manual...
Definition: FlowThresholdPressure.hpp:55
void beginTimeStep() override
Called by the simulator before each time integration.
Definition: FlowProblemBlackoil.hpp:315
Struct holding the parameters for the BlackOilBioeffectsModule class.
Definition: blackoilbioeffectsparams.hpp:41
void endEpisode() override
Called by the simulator after the end of an episode.
Definition: FlowProblemBlackoil.hpp:568
Scalar maxOilVaporizationFactor(unsigned timeIdx, unsigned globalDofIdx) const
Returns the maximum value of the oil vaporization factor at the current time for a given degree of fr...
Definition: FlowProblemBlackoil.hpp:937
FlowProblemBlackoil(Simulator &simulator)
Definition: FlowProblemBlackoil.hpp:189
VTK output module for the tracer model&#39;s parameters.
Definition: VtkTracerModule.hpp:57
void writeOutput(const bool verbose) override
Write the requested quantities of the current solution into the output files.
Definition: FlowProblemBlackoil.hpp:600
Hybrid Newton solver extension for the black-oil model.
Definition: HybridNewton.hpp:59
Collects necessary output values and pass them to Damaris server processes.
Definition: DamarisWriter.hpp:85
LhsEval permFactTransMultiplier(const IntensiveQuantities &intQuants, unsigned elementIdx, Callback &obtain) const
Calculate the transmissibility multiplier due to porosity reduction.
Definition: FlowProblemBlackoil.hpp:764
Structs needed for tpfalinearizer and its gpuparams struct extracted to be defined in one place that ...
Definition: blackoilbioeffectsmodules.hh:45
Scalar porosity(const Context &context, unsigned spaceIdx, unsigned timeIdx) const
Definition: FlowProblem.hpp:701
Contains classes extending the black-oil model.
void beginEpisode() override
Called by the simulator before an episode begins.
Definition: FlowProblemBlackoil.hpp:282
Struct holding the parameters for the BlackoilFoamModule class.
Definition: blackoilfoamparams.hpp:43
void endTimeStep() override
Called by the simulator after each time integration.
Definition: FlowProblemBlackoil.hpp:515
Classes required for dynamic convective mixing.
void finishInit()
Called by the Opm::Simulator in order to initialize the problem.
Definition: FlowProblemBlackoil.hpp:324
Scalar thresholdPressure(unsigned elem1Idx, unsigned elem2Idx) const
Definition: FlowProblemBlackoil.hpp:1187
const DimMatrix & intrinsicPermeability(const Context &context, unsigned spaceIdx, unsigned timeIdx) const
Definition: FlowProblem.hpp:534
Struct holding the parameters for the BlackoilBrineModule class.
Definition: blackoilbrineparams.hpp:41
static void registerParameters()
Registers all available parameters for the problem and the model.
Definition: FlowProblemBlackoil.hpp:175
virtual void beginEpisode()
Called by the simulator before an episode begins.
Definition: FlowProblem.hpp:307
Collects necessary output values and pass them to Damaris server processes.
VTK output module for the tracer model&#39;s parameters.
Collects necessary output values and pass it to opm-common&#39;s ECL output.
Definition: EclWriter.hpp:114
This problem simulates an input file given in the data format used by the commercial ECLiPSE simulato...
virtual void endTimeStep()
Called by the simulator after each time integration.
Definition: FlowProblem.hpp:438
Struct holding the parameters for the BlackOilPolymerModule class.
Definition: blackoilpolymerparams.hpp:43
Provides the auxiliary methods required for consideration of the dispersion equation.
void source(RateVector &rate, const Context &context, unsigned spaceIdx, unsigned timeIdx) const
Evaluate the source term for all phases within a given sub-control-volume.
Definition: FlowProblem.hpp:1052
Struct holding the parameters for the BlackOilSolventModule class.
Definition: blackoilsolventparams.hpp:46
virtual void endEpisode()
Called by the simulator after the end of an episode.
Definition: FlowProblem.hpp:495
Struct holding the parameters for the BlackoilExtboModule class.
Definition: blackoilextboparams.hpp:46
static void registerParameters()
Registers all available parameters for the problem and the model.
Definition: FlowProblem.hpp:186
Output module for the results black oil model writing in ECL binary format.
typename SatfuncConsistencyChecks< Scalar >::ViolationLevel ViolationLevel
Severity level for consistency condition violation.
Definition: SatfuncConsistencyCheckManager.hpp:71
void initialSolutionApplied() override
Callback used by the model to indicate that the initial solution has been determined for all degrees ...
Definition: FlowProblemBlackoil.hpp:635
virtual void initialSolutionApplied()
Callback used by the model to indicate that the initial solution has been determined for all degrees ...
Definition: FlowProblem.hpp:1019
virtual void writeOutput(bool verbose)
Write the requested quantities of the current solution into the output files.
Definition: FlowProblem.hpp:518
Scalar solventSaturation(unsigned elemIdx) const
Returns the initial solvent saturation for a given a cell index.
Definition: FlowGenericProblem_impl.hpp:649
Definition: SimulatorTimer.hpp:38
void readSolutionFromOutputModule(const int restart_step, bool fip_init)
Read simulator solution state from the outputmodule (used with restart)
Definition: FlowProblemBlackoil.hpp:1079
static void registerParameters()
Register all run-time parameters for the tracer VTK output module.
Definition: VtkTracerModule.hpp:84
Provides the auxiliary methods required for consideration of the diffusion equation.
bool recycleFirstIterationStorage() const
Return if the storage term of the first iteration is identical to the storage term for the solution o...
Definition: FlowProblemBlackoil.hpp:955