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