OutputBlackoilModule.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 This file is part of the Open Porous Media project (OPM).
5
6 OPM is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 2 of the License, or
9 (at your option) any later version.
10
11 OPM is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with OPM. If not, see <http://www.gnu.org/licenses/>.
18
19 Consult the COPYING file in the top-level source directory of this
20 module for the precise wording of the license and the list of
21 copyright holders.
22*/
27#ifndef OPM_OUTPUT_BLACK_OIL_MODULE_HPP
28#define OPM_OUTPUT_BLACK_OIL_MODULE_HPP
29
30#include <dune/common/fvector.hh>
31
33
34#include <opm/common/Exceptions.hpp>
35#include <opm/common/TimingMacros.hpp>
36#include <opm/common/OpmLog/OpmLog.hpp>
37#include <opm/common/utility/Visitor.hpp>
38
39#include <opm/input/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp>
40
41#include <opm/material/common/Valgrind.hpp>
42#include <opm/material/fluidmatrixinteractions/EclEpsScalingPoints.hpp>
43#include <opm/material/fluidstates/BlackOilFluidState.hpp>
44#include <opm/material/fluidsystems/BlackOilFluidSystem.hpp>
45
51
52#include <opm/output/data/Cells.hpp>
53#include <opm/output/eclipse/EclipseIO.hpp>
54#include <opm/output/eclipse/Inplace.hpp>
55
60
61#include <algorithm>
62#include <array>
63#include <cassert>
64#include <cstddef>
65#include <functional>
66#include <stdexcept>
67#include <string>
68#include <type_traits>
69#include <utility>
70#include <vector>
71
72namespace Opm {
73
74// forward declaration
75template <class TypeTag>
76class EcfvDiscretization;
77
84template <class TypeTag>
85class OutputBlackOilModule : public GenericOutputBlackoilModule<GetPropType<TypeTag, Properties::FluidSystem>>
86{
96 using FluidState = typename IntensiveQuantities::FluidState;
98 using Element = typename GridView::template Codim<0>::Entity;
99 using ElementIterator = typename GridView::template Codim<0>::Iterator;
102 using Dir = FaceDir::DirEnum;
108
109 static constexpr int conti0EqIdx = Indices::conti0EqIdx;
110 static constexpr int numPhases = FluidSystem::numPhases;
111 static constexpr int oilPhaseIdx = FluidSystem::oilPhaseIdx;
112 static constexpr int gasPhaseIdx = FluidSystem::gasPhaseIdx;
113 static constexpr int waterPhaseIdx = FluidSystem::waterPhaseIdx;
114 static constexpr int gasCompIdx = FluidSystem::gasCompIdx;
115 static constexpr int oilCompIdx = FluidSystem::oilCompIdx;
116 static constexpr int waterCompIdx = FluidSystem::waterCompIdx;
117 static constexpr EnergyModules energyModuleType = getPropValue<TypeTag, Properties::EnergyModuleType>();
118 enum { enableBioeffects = getPropValue<TypeTag, Properties::EnableBioeffects>() };
119 enum { enableMICP = Indices::enableMICP };
120 enum { enableVapwat = getPropValue<TypeTag, Properties::EnableVapwat>() };
121 enum { enableDisgasInWater = getPropValue<TypeTag, Properties::EnableDisgasInWater>() };
122 enum { enableDissolvedGas = Indices::compositionSwitchIdx >= 0 };
123
124 template<class VectorType>
125 static Scalar value_or_zero(int idx, const VectorType& v)
126 {
127 if (idx == -1) {
128 return 0.0;
129 }
130 return v.empty() ? 0.0 : v[idx];
131 }
132
133public:
134 OutputBlackOilModule(const Simulator& simulator,
135 const SummaryConfig& smryCfg,
136 const CollectDataOnIORankType& collectOnIORank)
137 : BaseType(simulator.vanguard().eclState(),
138 simulator.vanguard().schedule(),
139 smryCfg,
140 simulator.vanguard().summaryState(),
142 [this](const int idx)
143 { return simulator_.problem().eclWriter().collectOnIORank().localIdxToGlobalIdx(idx); },
144 simulator.vanguard().grid().comm(),
145 energyModuleType == EnergyModules::FullyImplicitThermal ||
146 energyModuleType == EnergyModules::SequentialImplicitThermal,
147 energyModuleType == EnergyModules::ConstantTemperature,
148 getPropValue<TypeTag, Properties::EnableMech>(),
149 getPropValue<TypeTag, Properties::EnableSolvent>(),
150 getPropValue<TypeTag, Properties::EnablePolymer>(),
151 getPropValue<TypeTag, Properties::EnableFoam>(),
152 getPropValue<TypeTag, Properties::EnableBrine>(),
153 getPropValue<TypeTag, Properties::EnableSaltPrecipitation>(),
154 getPropValue<TypeTag, Properties::EnableExtbo>(),
155 getPropValue<TypeTag, Properties::EnableBioeffects>())
156 , simulator_(simulator)
157 , collectOnIORank_(collectOnIORank)
158 {
159 for (auto& region_pair : this->regions_) {
160 this->createLocalRegion_(region_pair.second);
161 }
162
163 auto isCartIdxOnThisRank = [&collectOnIORank](const int idx) {
164 return collectOnIORank.isCartIdxOnThisRank(idx);
165 };
166
167 this->setupBlockData(isCartIdxOnThisRank);
168
169 if (! Parameters::Get<Parameters::OwnerCellsFirst>()) {
170 const std::string msg = "The output code does not support --owner-cells-first=false.";
171 if (collectOnIORank.isIORank()) {
172 OpmLog::error(msg);
173 }
174 OPM_THROW_NOLOG(std::runtime_error, msg);
175 }
176
177 if (smryCfg.match("[FB]PP[OGW]") || smryCfg.match("RPP[OGW]*")) {
178 auto rset = this->eclState_.fieldProps().fip_regions();
179 rset.push_back("PVTNUM");
180
181 // Note: We explicitly use decltype(auto) here because the
182 // default scheme (-> auto) will deduce an undesirable type. We
183 // need the "reference to vector" semantics in this instance.
185 .emplace(this->simulator_.gridView().comm(),
186 FluidSystem::numPhases, rset,
187 [fp = std::cref(this->eclState_.fieldProps())]
188 (const std::string& rsetName) -> decltype(auto)
189 { return fp.get().get_int(rsetName); });
190 }
191 }
192
197 void
198 allocBuffers(const unsigned bufferSize,
199 const unsigned reportStepNum,
200 const bool substep,
201 const bool log,
202 const bool isRestart)
203 {
204 if (! std::is_same<Discretization, EcfvDiscretization<TypeTag>>::value) {
205 return;
206 }
207
208 const auto& problem = this->simulator_.problem();
209
210 this->doAllocBuffers(bufferSize,
211 reportStepNum,
212 substep,
213 log,
214 isRestart,
215 &problem.materialLawManager()->hysteresisConfig(),
216 problem.eclWriter().getOutputNnc().size());
217 }
218
220 void setupExtractors(const bool isSubStep,
221 const int reportStepNum)
222 {
223 this->setupElementExtractors_();
224 this->setupBlockExtractors_(isSubStep, reportStepNum);
225 }
226
229 {
230 this->extractors_.clear();
231 this->blockExtractors_.clear();
232 this->extraBlockExtractors_.clear();
233 }
234
239 void processElement(const ElementContext& elemCtx)
240 {
241 OPM_TIMEBLOCK_LOCAL(processElement, Subsystem::Output);
242 if (!std::is_same<Discretization, EcfvDiscretization<TypeTag>>::value) {
243 return;
244 }
245
246 if (this->extractors_.empty()) {
247 assert(0);
248 }
249
250 const auto& matLawManager = simulator_.problem().materialLawManager();
251
252 typename Extractor::HysteresisParams hysterParams;
253 for (unsigned dofIdx = 0; dofIdx < elemCtx.numPrimaryDof(/*timeIdx=*/0); ++dofIdx) {
254 const auto& intQuants = elemCtx.intensiveQuantities(dofIdx, /*timeIdx=*/0);
255 const auto& fs = intQuants.fluidState();
256
257 const typename Extractor::Context ectx{
258 elemCtx.globalSpaceIndex(dofIdx, /*timeIdx=*/0),
259 elemCtx.primaryVars(dofIdx, /*timeIdx=*/0).pvtRegionIndex(),
260 elemCtx.simulator().episodeIndex(),
261 fs,
262 intQuants,
263 hysterParams
264 };
265
266 if (matLawManager->enableHysteresis()) {
267 if (FluidSystem::phaseIsActive(oilPhaseIdx) && FluidSystem::phaseIsActive(waterPhaseIdx)) {
268 matLawManager->oilWaterHysteresisParams(hysterParams.somax,
269 hysterParams.swmax,
270 hysterParams.swmin,
271 ectx.globalDofIdx);
272 }
273 if (FluidSystem::phaseIsActive(oilPhaseIdx) && FluidSystem::phaseIsActive(gasPhaseIdx)) {
274 matLawManager->gasOilHysteresisParams(hysterParams.sgmax,
275 hysterParams.shmax,
276 hysterParams.somin,
277 ectx.globalDofIdx);
278 }
279 }
280
281 Extractor::process(ectx, extractors_);
282 }
283 }
284
285 void processElementBlockData(const ElementContext& elemCtx)
286 {
287 OPM_TIMEBLOCK_LOCAL(processElementBlockData, Subsystem::Output);
288 if (!std::is_same<Discretization, EcfvDiscretization<TypeTag>>::value) {
289 return;
290 }
291
292 if (this->blockExtractors_.empty() && this->extraBlockExtractors_.empty()) {
293 return;
294 }
295
296 for (unsigned dofIdx = 0; dofIdx < elemCtx.numPrimaryDof(/*timeIdx=*/0); ++dofIdx) {
297 // Adding block data
298 const auto globalDofIdx = elemCtx.globalSpaceIndex(dofIdx, /*timeIdx=*/0);
299 const auto cartesianIdx = elemCtx.simulator().vanguard().cartesianIndex(globalDofIdx);
300
301 const auto be_it = this->blockExtractors_.find(cartesianIdx);
302 const auto bee_it = this->extraBlockExtractors_.find(cartesianIdx);
303 if (be_it == this->blockExtractors_.end() &&
304 bee_it == this->extraBlockExtractors_.end())
305 {
306 continue;
307 }
308
309 const auto& intQuants = elemCtx.intensiveQuantities(dofIdx, /*timeIdx=*/0);
310 const auto& fs = intQuants.fluidState();
311
312 const typename BlockExtractor::Context ectx{
313 globalDofIdx,
314 dofIdx,
315 fs,
316 intQuants,
317 elemCtx,
318 };
319
320 if (be_it != this->blockExtractors_.end()) {
321 BlockExtractor::process(be_it->second, ectx);
322 }
323 if (bee_it != this->extraBlockExtractors_.end()) {
324 BlockExtractor::process(bee_it->second, ectx);
325 }
326 }
327 }
328
329 void outputFipAndResvLog(const Inplace& inplace,
330 const std::size_t reportStepNum,
331 double elapsed,
332 boost::posix_time::ptime currentDate,
333 const bool substep,
334 const Parallel::Communication& comm)
335 {
336
337 if (comm.rank() != 0) {
338 return;
339 }
340
341 // For report step 0 we use the RPTSOL config, else derive from RPTSCHED
342 std::unique_ptr<FIPConfig> fipSched;
343 if (reportStepNum > 0) {
344 const auto& rpt = this->schedule_[reportStepNum-1].rpt_config.get();
345 fipSched = std::make_unique<FIPConfig>(rpt);
346 }
347 const FIPConfig& fipc = reportStepNum == 0 ? this->eclState_.getEclipseConfig().fip()
348 : *fipSched;
349
350 if (!substep && !this->forceDisableFipOutput_ && fipc.output(FIPConfig::OutputField::FIELD)) {
351
352 this->logOutput_.timeStamp("BALANCE", elapsed, reportStepNum, currentDate);
353
354 const auto& initial_inplace = this->initialInplace().value();
355 this->logOutput_.fip(inplace, initial_inplace, "");
356
357 if (fipc.output(FIPConfig::OutputField::FIPNUM)) {
358 this->logOutput_.fip(inplace, initial_inplace, "FIPNUM");
359
360 if (fipc.output(FIPConfig::OutputField::RESV))
361 this->logOutput_.fipResv(inplace, "FIPNUM");
362 }
363
364 if (fipc.output(FIPConfig::OutputField::FIP)) {
365 for (const auto& reg : this->regions_) {
366 if (reg.first != "FIPNUM") {
367 std::ostringstream ss;
368 ss << "BAL" << reg.first.substr(3);
369 this->logOutput_.timeStamp(ss.str(), elapsed, reportStepNum, currentDate);
370 this->logOutput_.fip(inplace, initial_inplace, reg.first);
371
372 if (fipc.output(FIPConfig::OutputField::RESV))
373 this->logOutput_.fipResv(inplace, reg.first);
374 }
375 }
376 }
377 }
378 }
379
380 void outputFipAndResvLogToCSV(const std::size_t reportStepNum,
381 const bool substep,
382 const Parallel::Communication& comm)
383 {
384 if (comm.rank() != 0) {
385 return;
386 }
387
388 if ((reportStepNum == 0) && (!substep) &&
389 (this->schedule_.initialReportConfiguration().has_value()) &&
390 (this->schedule_.initialReportConfiguration()->contains("CSVFIP"))) {
391
392 std::ostringstream csv_stream;
393
394 this->logOutput_.csv_header(csv_stream);
395
396 const auto& initial_inplace = this->initialInplace().value();
397
398 this->logOutput_.fip_csv(csv_stream, initial_inplace, "FIPNUM");
399
400 for (const auto& reg : this->regions_) {
401 if (reg.first != "FIPNUM") {
402 this->logOutput_.fip_csv(csv_stream, initial_inplace, reg.first);
403 }
404 }
405
406 const IOConfig& io = this->eclState_.getIOConfig();
407 auto csv_fname = io.getOutputDir() + "/" + io.getBaseName() + ".CSV";
408
409 std::ofstream outputFile(csv_fname);
410
411 outputFile << csv_stream.str();
412
413 outputFile.close();
414 }
415 }
416
445 template <class ActiveIndex, class CartesianIndex>
446 void processFluxes(const ElementContext& elemCtx,
447 ActiveIndex&& activeIndex,
448 CartesianIndex&& cartesianIndex)
449 {
450 OPM_TIMEBLOCK_LOCAL(processFluxes, Subsystem::Output);
451 const auto identifyCell = [&activeIndex, &cartesianIndex](const Element& elem)
453 {
454 const auto cellIndex = activeIndex(elem);
455
456 return {
457 static_cast<int>(cellIndex),
458 cartesianIndex(cellIndex),
459 elem.partitionType() == Dune::InteriorEntity
460 };
461 };
462
463 const auto timeIdx = 0u;
464 const auto& stencil = elemCtx.stencil(timeIdx);
465 const auto numInteriorFaces = elemCtx.numInteriorFaces(timeIdx);
466
467 for (auto scvfIdx = 0 * numInteriorFaces; scvfIdx < numInteriorFaces; ++scvfIdx) {
468 const auto& face = stencil.interiorFace(scvfIdx);
469 const auto left = identifyCell(stencil.element(face.interiorIndex()));
470 const auto right = identifyCell(stencil.element(face.exteriorIndex()));
471
472 const auto rates = this->
473 getComponentSurfaceRates(elemCtx, face.area(), scvfIdx, timeIdx);
474
475 this->interRegionFlows_.addConnection(left, right, rates);
476 }
477 }
478
484 {
485 // Inter-region flow rates. Note: ".clear()" prepares to accumulate
486 // contributions per bulk connection between FIP regions.
487 this->interRegionFlows_.clear();
488 }
489
494 {
496 }
497
502 {
503 return this->interRegionFlows_;
504 }
505
506 template <class FluidState>
507 void assignToFluidState(FluidState& fs, unsigned elemIdx) const
508 {
509 for (unsigned phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) {
510 if (this->saturation_[phaseIdx].empty())
511 continue;
512
513 fs.setSaturation(phaseIdx, this->saturation_[phaseIdx][elemIdx]);
514 }
515
516 if (!this->fluidPressure_.empty()) {
517 // this assumes that capillary pressures only depend on the phase saturations
518 // and possibly on temperature. (this is always the case for ECL problems.)
519 std::array<Scalar, numPhases> pc = {0};
520 const MaterialLawParams& matParams = simulator_.problem().materialLawParams(elemIdx);
521 MaterialLaw::capillaryPressures(pc, matParams, fs);
522 Valgrind::CheckDefined(this->fluidPressure_[elemIdx]);
523 Valgrind::CheckDefined(pc);
524 const auto& pressure = this->fluidPressure_[elemIdx];
525 for (unsigned phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) {
526 if (!FluidSystem::phaseIsActive(phaseIdx))
527 continue;
528
529 if (Indices::oilEnabled)
530 fs.setPressure(phaseIdx, pressure + (pc[phaseIdx] - pc[oilPhaseIdx]));
531 else if (Indices::gasEnabled)
532 fs.setPressure(phaseIdx, pressure + (pc[phaseIdx] - pc[gasPhaseIdx]));
533 else if (Indices::waterEnabled)
534 //single (water) phase
535 fs.setPressure(phaseIdx, pressure);
536 }
537 }
538
539 if constexpr (energyModuleType != EnergyModules::NoTemperature) {
540 if (!this->temperature_.empty())
541 fs.setTemperature(this->temperature_[elemIdx]);
542 }
543 if constexpr (enableDissolvedGas) {
544 if (!this->rs_.empty())
545 fs.setRs(this->rs_[elemIdx]);
546 if (!this->rv_.empty())
547 fs.setRv(this->rv_[elemIdx]);
548 }
549 if constexpr (enableDisgasInWater) {
550 if (!this->rsw_.empty())
551 fs.setRsw(this->rsw_[elemIdx]);
552 }
553 if constexpr (enableVapwat) {
554 if (!this->rvw_.empty())
555 fs.setRvw(this->rvw_[elemIdx]);
556 }
557 }
558
559 void initHysteresisParams(Simulator& simulator, unsigned elemIdx) const
560 {
561 if (!this->soMax_.empty())
562 simulator.problem().setMaxOilSaturation(elemIdx, this->soMax_[elemIdx]);
563
564 if (simulator.problem().materialLawManager()->enableHysteresis()) {
565 auto matLawManager = simulator.problem().materialLawManager();
566
567 if (FluidSystem::phaseIsActive(oilPhaseIdx)
568 && FluidSystem::phaseIsActive(waterPhaseIdx)) {
569 Scalar somax = 2.0;
570 Scalar swmax = -2.0;
571 Scalar swmin = 2.0;
572
573 if (matLawManager->enableNonWettingHysteresis()) {
574 if (!this->soMax_.empty()) {
575 somax = this->soMax_[elemIdx];
576 }
577 }
578 if (matLawManager->enableWettingHysteresis()) {
579 if (!this->swMax_.empty()) {
580 swmax = this->swMax_[elemIdx];
581 }
582 }
583 if (matLawManager->enablePCHysteresis()) {
584 if (!this->swmin_.empty()) {
585 swmin = this->swmin_[elemIdx];
586 }
587 }
588 matLawManager->setOilWaterHysteresisParams(
589 somax, swmax, swmin, elemIdx);
590 }
591 if (FluidSystem::phaseIsActive(oilPhaseIdx)
592 && FluidSystem::phaseIsActive(gasPhaseIdx)) {
593 Scalar sgmax = 2.0;
594 Scalar shmax = -2.0;
595 Scalar somin = 2.0;
596
597 if (matLawManager->enableNonWettingHysteresis()) {
598 if (!this->sgmax_.empty()) {
599 sgmax = this->sgmax_[elemIdx];
600 }
601 }
602 if (matLawManager->enableWettingHysteresis()) {
603 if (!this->shmax_.empty()) {
604 shmax = this->shmax_[elemIdx];
605 }
606 }
607 if (matLawManager->enablePCHysteresis()) {
608 if (!this->somin_.empty()) {
609 somin = this->somin_[elemIdx];
610 }
611 }
612 matLawManager->setGasOilHysteresisParams(
613 sgmax, shmax, somin, elemIdx);
614 }
615
616 }
617
618 if (simulator_.vanguard().eclState().fieldProps().has_double("SWATINIT")) {
619 simulator.problem().materialLawManager()
620 ->applyRestartSwatInit(elemIdx, this->ppcw_[elemIdx]);
621 }
622 }
623
624 void updateFluidInPlace(const ElementContext& elemCtx)
625 {
626 for (unsigned dofIdx = 0; dofIdx < elemCtx.numPrimaryDof(/*timeIdx=*/0); ++dofIdx) {
627 updateFluidInPlace_(elemCtx, dofIdx);
628 }
629 }
630
631 void updateFluidInPlace(const unsigned globalDofIdx,
632 const IntensiveQuantities& intQuants,
633 const double totVolume)
634 {
635 this->updateFluidInPlace_(globalDofIdx, intQuants, totVolume);
636 }
637
638private:
639 template <typename T>
640 using RemoveCVR = std::remove_cv_t<std::remove_reference_t<T>>;
641
642 template <typename, class = void>
643 struct HasGeoMech : public std::false_type {};
644
645 template <typename Problem>
646 struct HasGeoMech<
647 Problem, std::void_t<decltype(std::declval<Problem>().geoMechModel())>
648 > : public std::true_type {};
649
650 bool isDefunctParallelWell(const std::string& wname) const override
651 {
652 if (simulator_.gridView().comm().size() == 1)
653 return false;
654 const auto& parallelWells = simulator_.vanguard().parallelWells();
655 std::pair<std::string, bool> value {wname, true};
656 auto candidate = std::lower_bound(parallelWells.begin(), parallelWells.end(), value);
657 return candidate == parallelWells.end() || *candidate != value;
658 }
659
660 bool isOwnedByCurrentRank(const std::string& wname) const override
661 {
662 return this->simulator_.problem().wellModel().isOwner(wname);
663 }
664
665 bool isOnCurrentRank(const std::string& wname) const override
666 {
667 return this->simulator_.problem().wellModel().hasLocalCells(wname);
668 }
669
670 void updateFluidInPlace_(const ElementContext& elemCtx, const unsigned dofIdx)
671 {
672 const auto& intQuants = elemCtx.intensiveQuantities(dofIdx, /*timeIdx=*/0);
673 const unsigned globalDofIdx = elemCtx.globalSpaceIndex(dofIdx, /*timeIdx=*/0);
674 const auto totVolume = elemCtx.simulator().model().dofTotalVolume(globalDofIdx);
675
676 this->updateFluidInPlace_(globalDofIdx, intQuants, totVolume);
677 }
678
679 void updateFluidInPlace_(const unsigned globalDofIdx,
680 const IntensiveQuantities& intQuants,
681 const double totVolume)
682 {
683 OPM_TIMEBLOCK_LOCAL(updateFluidInPlace, Subsystem::Output);
684
685 this->updateTotalVolumesAndPressures_(globalDofIdx, intQuants, totVolume);
686
687 if (this->computeFip_) {
688 this->updatePhaseInplaceVolumes_(globalDofIdx, intQuants, totVolume);
689 }
690 }
691
692 void createLocalRegion_(std::vector<int>& region)
693 {
694 // For CpGrid with LGRs, where level zero grid has been distributed,
695 // resize region is needed, since in this case the total amount of
696 // element - per process - in level zero grid and leaf grid do not
697 // coincide, in general.
698 region.resize(simulator_.gridView().size(0));
699 std::size_t elemIdx = 0;
700 for (const auto& elem : elements(simulator_.gridView())) {
701 if (elem.partitionType() != Dune::InteriorEntity) {
702 region[elemIdx] = 0;
703 }
704
705 ++elemIdx;
706 }
707 }
708
709 template <typename FluidState>
710 void aggregateAverageDensityContributions_(const FluidState& fs,
711 const unsigned int globalDofIdx,
712 const double porv)
713 {
714 auto pvCellValue = RegionPhasePoreVolAverage::CellValue{};
715 pvCellValue.porv = porv;
716
717 for (auto phaseIdx = 0*FluidSystem::numPhases;
718 phaseIdx < FluidSystem::numPhases; ++phaseIdx)
719 {
720 if (! FluidSystem::phaseIsActive(phaseIdx)) {
721 continue;
722 }
723
724 pvCellValue.value = getValue(fs.density(phaseIdx));
725 pvCellValue.sat = getValue(fs.saturation(phaseIdx));
726
728 ->addCell(globalDofIdx,
730 pvCellValue);
731 }
732 }
733
749 data::InterRegFlowMap::FlowRates
750 getComponentSurfaceRates(const ElementContext& elemCtx,
751 const Scalar faceArea,
752 const std::size_t scvfIdx,
753 const std::size_t timeIdx) const
754 {
755 using Component = data::InterRegFlowMap::Component;
756
757 auto rates = data::InterRegFlowMap::FlowRates {};
758
759 const auto& extQuant = elemCtx.extensiveQuantities(scvfIdx, timeIdx);
760
761 const auto alpha = getValue(extQuant.extrusionFactor()) * faceArea;
762
763 if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
764 const auto& up = elemCtx
765 .intensiveQuantities(extQuant.upstreamIndex(oilPhaseIdx), timeIdx);
766
767 const auto pvtReg = up.pvtRegionIndex();
768
769 const auto bO = getValue(getInvB_<FluidSystem, FluidState, Scalar>
770 (up.fluidState(), oilPhaseIdx, pvtReg));
771
772 const auto qO = alpha * bO * getValue(extQuant.volumeFlux(oilPhaseIdx));
773
774 rates[Component::Oil] += qO;
775
776 if (FluidSystem::phaseIsActive(gasPhaseIdx)) {
777 const auto Rs = getValue(
778 BlackOil::getRs_<FluidSystem, FluidState, Scalar>
779 (up.fluidState(), pvtReg));
780
781 rates[Component::Gas] += qO * Rs;
782 rates[Component::Disgas] += qO * Rs;
783 }
784 }
785
786 if (FluidSystem::phaseIsActive(gasPhaseIdx)) {
787 const auto& up = elemCtx
788 .intensiveQuantities(extQuant.upstreamIndex(gasPhaseIdx), timeIdx);
789
790 const auto pvtReg = up.pvtRegionIndex();
791
792 const auto bG = getValue(getInvB_<FluidSystem, FluidState, Scalar>
793 (up.fluidState(), gasPhaseIdx, pvtReg));
794
795 const auto qG = alpha * bG * getValue(extQuant.volumeFlux(gasPhaseIdx));
796
797 rates[Component::Gas] += qG;
798
799 if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
800 const auto Rv = getValue(
801 BlackOil::getRv_<FluidSystem, FluidState, Scalar>
802 (up.fluidState(), pvtReg));
803
804 rates[Component::Oil] += qG * Rv;
805 rates[Component::Vapoil] += qG * Rv;
806 }
807 }
808
809 if (FluidSystem::phaseIsActive(waterPhaseIdx)) {
810 const auto& up = elemCtx
811 .intensiveQuantities(extQuant.upstreamIndex(waterPhaseIdx), timeIdx);
812
813 const auto pvtReg = up.pvtRegionIndex();
814
815 const auto bW = getValue(getInvB_<FluidSystem, FluidState, Scalar>
816 (up.fluidState(), waterPhaseIdx, pvtReg));
817
818 rates[Component::Water] +=
819 alpha * bW * getValue(extQuant.volumeFlux(waterPhaseIdx));
820 }
821
822 return rates;
823 }
824
825 template <typename FluidState>
826 Scalar hydroCarbonFraction(const FluidState& fs) const
827 {
828 if (this->eclState_.runspec().co2Storage()) {
829 // CO2 storage: Hydrocarbon volume is full pore-volume.
830 return 1.0;
831 }
832
833 // Common case. Hydrocarbon volume is fraction occupied by actual
834 // hydrocarbons.
835 auto hydrocarbon = Scalar {0};
836 if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
837 hydrocarbon += getValue(fs.saturation(oilPhaseIdx));
838 }
839
840 if (FluidSystem::phaseIsActive(gasPhaseIdx)) {
841 hydrocarbon += getValue(fs.saturation(gasPhaseIdx));
842 }
843
844 return hydrocarbon;
845 }
846
847 void updateTotalVolumesAndPressures_(const unsigned globalDofIdx,
848 const IntensiveQuantities& intQuants,
849 const double totVolume)
850 {
851 const auto& fs = intQuants.fluidState();
852
853 const double pv = totVolume * intQuants.porosity().value();
854 const auto hydrocarbon = this->hydroCarbonFraction(fs);
855
856 if (! this->hydrocarbonPoreVolume_.empty()) {
857 this->fipC_.assignPoreVolume(globalDofIdx,
858 totVolume * intQuants.referencePorosity());
859
860 this->dynamicPoreVolume_[globalDofIdx] = pv;
861 this->hydrocarbonPoreVolume_[globalDofIdx] = pv * hydrocarbon;
862 }
863
864 if (!this->pressureTimesHydrocarbonVolume_.empty() &&
865 !this->pressureTimesPoreVolume_.empty())
866 {
867 assert(this->hydrocarbonPoreVolume_.size() == this->pressureTimesHydrocarbonVolume_.size());
868 assert(this->fipC_.get(Inplace::Phase::PoreVolume).size() == this->pressureTimesPoreVolume_.size());
869
870 if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
871 this->pressureTimesPoreVolume_[globalDofIdx] =
872 getValue(fs.pressure(oilPhaseIdx)) * pv;
873
874 this->pressureTimesHydrocarbonVolume_[globalDofIdx] =
875 this->pressureTimesPoreVolume_[globalDofIdx] * hydrocarbon;
876 }
877 else if (FluidSystem::phaseIsActive(gasPhaseIdx)) {
878 this->pressureTimesPoreVolume_[globalDofIdx] =
879 getValue(fs.pressure(gasPhaseIdx)) * pv;
880
881 this->pressureTimesHydrocarbonVolume_[globalDofIdx] =
882 this->pressureTimesPoreVolume_[globalDofIdx] * hydrocarbon;
883 }
884 else if (FluidSystem::phaseIsActive(waterPhaseIdx)) {
885 this->pressureTimesPoreVolume_[globalDofIdx] =
886 getValue(fs.pressure(waterPhaseIdx)) * pv;
887 }
888 }
889 }
890
891 void updatePhaseInplaceVolumes_(const unsigned globalDofIdx,
892 const IntensiveQuantities& intQuants,
893 const double totVolume)
894 {
895 std::array<Scalar, FluidSystem::numPhases> fip {};
896 std::array<Scalar, FluidSystem::numPhases> fipr{}; // at reservoir condition
897
898 const auto& fs = intQuants.fluidState();
899 const auto pv = totVolume * intQuants.porosity().value();
900
901 for (unsigned phaseIdx = 0; phaseIdx < FluidSystem::numPhases; ++phaseIdx) {
902 if (!FluidSystem::phaseIsActive(phaseIdx)) {
903 continue;
904 }
905
906 const auto b = getValue(fs.invB(phaseIdx));
907 const auto s = getValue(fs.saturation(phaseIdx));
908
909 fipr[phaseIdx] = s * pv;
910 fip [phaseIdx] = b * fipr[phaseIdx];
911 }
912
913 this->fipC_.assignVolumesSurface(globalDofIdx, fip);
914 this->fipC_.assignVolumesReservoir(globalDofIdx,
915 fs.saltConcentration().value(),
916 fipr);
917
918 if (FluidSystem::phaseIsActive(oilPhaseIdx) &&
919 FluidSystem::phaseIsActive(gasPhaseIdx))
920 {
921 this->updateOilGasDistribution(globalDofIdx, fs, fip);
922 }
923
924 if (FluidSystem::phaseIsActive(waterPhaseIdx) &&
925 FluidSystem::phaseIsActive(gasPhaseIdx))
926 {
927 this->updateGasWaterDistribution(globalDofIdx, fs, fip);
928 }
929
930 if (FluidSystem::phaseIsActive(gasPhaseIdx) &&
931 this->fipC_.hasCo2InGas())
932 {
933 this->updateCO2InGas(globalDofIdx, pv, intQuants);
934 }
935
936 if (this->fipC_.hasCo2InWater() &&
937 (FluidSystem::phaseIsActive(waterPhaseIdx) ||
938 FluidSystem::phaseIsActive(oilPhaseIdx)))
939 {
940 this->updateCO2InWater(globalDofIdx, pv, fs);
941 }
942
943 if constexpr(enableBioeffects) {
944 const auto surfVolWat = pv * getValue(fs.saturation(waterPhaseIdx)) *
945 getValue(fs.invB(waterPhaseIdx));
946 if (this->fipC_.hasMicrobialMass()) {
947 this->updateMicrobialMass(globalDofIdx, intQuants, surfVolWat);
948 }
949 if (this->fipC_.hasBiofilmMass()) {
950 this->updateBiofilmMass(globalDofIdx, intQuants, totVolume);
951 }
952 if constexpr(enableMICP) {
953 if (this->fipC_.hasOxygenMass()) {
954 this->updateOxygenMass(globalDofIdx, intQuants, surfVolWat);
955 }
956 if (this->fipC_.hasUreaMass()) {
957 this->updateUreaMass(globalDofIdx, intQuants, surfVolWat);
958 }
959 if (this->fipC_.hasCalciteMass()) {
960 this->updateCalciteMass(globalDofIdx, intQuants, totVolume);
961 }
962 }
963 }
964
965 if (this->fipC_.hasWaterMass() && FluidSystem::phaseIsActive(waterPhaseIdx))
966 {
967 this->updateWaterMass(globalDofIdx, fs, fip);
968 }
969 }
970
971 template <typename FluidState, typename FIPArray>
972 void updateOilGasDistribution(const unsigned globalDofIdx,
973 const FluidState& fs,
974 const FIPArray& fip)
975 {
976 // Gas dissolved in oil and vaporized oil
977 const auto gasInPlaceLiquid = getValue(fs.Rs()) * fip[oilPhaseIdx];
978 const auto oilInPlaceGas = getValue(fs.Rv()) * fip[gasPhaseIdx];
979
980 this->fipC_.assignOilGasDistribution(globalDofIdx, gasInPlaceLiquid, oilInPlaceGas);
981 }
982
983 template <typename FluidState, typename FIPArray>
984 void updateGasWaterDistribution(const unsigned globalDofIdx,
985 const FluidState& fs,
986 const FIPArray& fip)
987 {
988 // Gas dissolved in water and vaporized water
989 const auto gasInPlaceWater = getValue(fs.Rsw()) * fip[waterPhaseIdx];
990 const auto waterInPlaceGas = getValue(fs.Rvw()) * fip[gasPhaseIdx];
991
992 this->fipC_.assignGasWater(globalDofIdx, fip, gasInPlaceWater, waterInPlaceGas);
993 }
994
995 template <typename IntensiveQuantities>
996 void updateCO2InGas(const unsigned globalDofIdx,
997 const double pv,
998 const IntensiveQuantities& intQuants)
999 {
1000 const auto& scaledDrainageInfo = this->simulator_.problem().materialLawManager()
1001 ->oilWaterScaledEpsInfoDrainage(globalDofIdx);
1002
1003 const auto& fs = intQuants.fluidState();
1004 Scalar sgcr = scaledDrainageInfo.Sgcr;
1005 if (this->simulator_.problem().materialLawManager()->enableHysteresis()) {
1006 const auto& matParams = simulator_.problem().materialLawParams(globalDofIdx);
1007 sgcr = MaterialLaw::trappedGasSaturation(matParams, /*maximumTrapping*/false);
1008 }
1009
1010 Scalar trappedGasSaturation = scaledDrainageInfo.Sgcr;
1011 if (this->fipC_.has(Inplace::Phase::CO2MassInGasPhaseMaximumTrapped) ||
1012 this->fipC_.has(Inplace::Phase::CO2MassInGasPhaseMaximumUnTrapped))
1013 {
1014 if (this->simulator_.problem().materialLawManager()->enableHysteresis()) {
1015 const auto& matParams = simulator_.problem().materialLawParams(globalDofIdx);
1016 // Get the maximum trapped gas saturation
1017 trappedGasSaturation = MaterialLaw::trappedGasSaturation(matParams, /*maximumTrapping*/true);
1018 }
1019 }
1020
1021 const Scalar sg = getValue(fs.saturation(gasPhaseIdx));
1022 Scalar strandedGasSaturation = scaledDrainageInfo.Sgcr;
1023 if (this->fipC_.has(Inplace::Phase::CO2MassInGasPhaseEffectiveTrapped) ||
1024 this->fipC_.has(Inplace::Phase::CO2MassInGasPhaseEffectiveUnTrapped))
1025 {
1026 if (this->simulator_.problem().materialLawManager()->enableHysteresis()) {
1027 const auto& matParams = simulator_.problem().materialLawParams(globalDofIdx);
1028 const double krg = getValue(intQuants.relativePermeability(gasPhaseIdx));
1029 strandedGasSaturation = MaterialLaw::strandedGasSaturation(matParams, sg, krg);
1030 }
1031 }
1032
1033 const typename FIPContainer<FluidSystem>::Co2InGasInput v{
1034 pv,
1035 sg,
1036 sgcr,
1037 getValue(fs.density(gasPhaseIdx)),
1038 FluidSystem::phaseIsActive(waterPhaseIdx)
1039 ? FluidSystem::convertRvwToXgW(getValue(fs.Rvw()), fs.pvtRegionIndex())
1040 : FluidSystem::convertRvToXgO(getValue(fs.Rv()), fs.pvtRegionIndex()),
1041 FluidSystem::molarMass(gasCompIdx, fs.pvtRegionIndex()),
1042 trappedGasSaturation,
1043 strandedGasSaturation,
1044 };
1045
1046 this->fipC_.assignCo2InGas(globalDofIdx, v);
1047 }
1048
1049 template <typename FluidState>
1050 void updateCO2InWater(const unsigned globalDofIdx,
1051 const double pv,
1052 const FluidState& fs)
1053 {
1054 const auto co2InWater = FluidSystem::phaseIsActive(oilPhaseIdx)
1055 ? this->co2InWaterFromOil(fs, pv)
1056 : this->co2InWaterFromWater(fs, pv);
1057
1058 const Scalar mM = FluidSystem::molarMass(gasCompIdx, fs.pvtRegionIndex());
1059
1060 this->fipC_.assignCo2InWater(globalDofIdx, co2InWater, mM);
1061 }
1062
1063 template <typename FluidState>
1064 Scalar co2InWaterFromWater(const FluidState& fs, const double pv) const
1065 {
1066 const double rhow = getValue(fs.density(waterPhaseIdx));
1067 const double sw = getValue(fs.saturation(waterPhaseIdx));
1068 const double xwG = FluidSystem::convertRswToXwG(getValue(fs.Rsw()), fs.pvtRegionIndex());
1069
1070 const Scalar mM = FluidSystem::molarMass(gasCompIdx, fs.pvtRegionIndex());
1071
1072 return xwG * pv * rhow * sw / mM;
1073 }
1074
1075 template <typename FluidState>
1076 Scalar co2InWaterFromOil(const FluidState& fs, const double pv) const
1077 {
1078 const double rhoo = getValue(fs.density(oilPhaseIdx));
1079 const double so = getValue(fs.saturation(oilPhaseIdx));
1080 const double xoG = FluidSystem::convertRsToXoG(getValue(fs.Rs()), fs.pvtRegionIndex());
1081
1082 const Scalar mM = FluidSystem::molarMass(gasCompIdx, fs.pvtRegionIndex());
1083
1084 return xoG * pv * rhoo * so / mM;
1085 }
1086
1087 template <typename FluidState, typename FIPArray>
1088 void updateWaterMass(const unsigned globalDofIdx,
1089 const FluidState& fs,
1090 const FIPArray& fip
1091 )
1092 {
1093 const Scalar rhoW = FluidSystem::referenceDensity(waterPhaseIdx, fs.pvtRegionIndex());
1094
1095 this->fipC_.assignWaterMass(globalDofIdx, fip, rhoW);
1096 }
1097
1098 template <typename IntensiveQuantities>
1099 void updateMicrobialMass(const unsigned globalDofIdx,
1100 const IntensiveQuantities& intQuants,
1101 const double surfVolWat)
1102 {
1103 const Scalar mass = surfVolWat * intQuants.microbialConcentration().value();
1104
1105 this->fipC_.assignMicrobialMass(globalDofIdx, mass);
1106 }
1107
1108 template <typename IntensiveQuantities>
1109 void updateOxygenMass(const unsigned globalDofIdx,
1110 const IntensiveQuantities& intQuants,
1111 const double surfVolWat)
1112 {
1113 const Scalar mass = surfVolWat * intQuants.oxygenConcentration().value();
1114
1115 this->fipC_.assignOxygenMass(globalDofIdx, mass);
1116 }
1117
1118 template <typename IntensiveQuantities>
1119 void updateUreaMass(const unsigned globalDofIdx,
1120 const IntensiveQuantities& intQuants,
1121 const double surfVolWat)
1122 {
1123 const Scalar mass = surfVolWat * intQuants.ureaConcentration().value();
1124
1125 this->fipC_.assignUreaMass(globalDofIdx, mass);
1126 }
1127
1128 template <typename IntensiveQuantities>
1129 void updateBiofilmMass(const unsigned globalDofIdx,
1130 const IntensiveQuantities& intQuants,
1131 const double totVolume)
1132 {
1133 const Scalar mass = totVolume * intQuants.biofilmMass().value();
1134
1135 this->fipC_.assignBiofilmMass(globalDofIdx, mass);
1136 }
1137
1138 template <typename IntensiveQuantities>
1139 void updateCalciteMass(const unsigned globalDofIdx,
1140 const IntensiveQuantities& intQuants,
1141 const double totVolume)
1142 {
1143 const Scalar mass = totVolume * intQuants.calciteMass().value();
1144
1145 this->fipC_.assignCalciteMass(globalDofIdx, mass);
1146 }
1147
1149 void setupElementExtractors_()
1150 {
1151 using Entry = typename Extractor::Entry;
1152 using Context = typename Extractor::Context;
1153 using ScalarEntry = typename Extractor::ScalarEntry;
1154 using PhaseEntry = typename Extractor::PhaseEntry;
1155
1156 const bool hasResidual = simulator_.model().linearizer().residual().size() > 0;
1157 const auto& hysteresisConfig = simulator_.problem().materialLawManager()->hysteresisConfig();
1158
1159 auto extractors = std::array{
1160 Entry{PhaseEntry{&this->saturation_,
1161 [](const unsigned phase, const Context& ectx)
1162 { return getValue(ectx.fs.saturation(phase)); }
1163 }
1164 },
1165 Entry{PhaseEntry{&this->invB_,
1166 [](const unsigned phase, const Context& ectx)
1167 { return getValue(ectx.fs.invB(phase)); }
1168 }
1169 },
1170 Entry{PhaseEntry{&this->density_,
1171 [](const unsigned phase, const Context& ectx)
1172 { return getValue(ectx.fs.density(phase)); }
1173 }
1174 },
1175 Entry{PhaseEntry{&this->relativePermeability_,
1176 [](const unsigned phase, const Context& ectx)
1177 { return getValue(ectx.intQuants.relativePermeability(phase)); }
1178 }
1179 },
1180 Entry{PhaseEntry{&this->viscosity_,
1181 [this](const unsigned phaseIdx, const Context& ectx)
1182 {
1183 if (this->extboC_.allocated() && phaseIdx == oilPhaseIdx) {
1184 return getValue(ectx.intQuants.oilViscosity());
1185 }
1186 else if (this->extboC_.allocated() && phaseIdx == gasPhaseIdx) {
1187 return getValue(ectx.intQuants.gasViscosity());
1188 }
1189 else {
1190 return getValue(ectx.fs.viscosity(phaseIdx));
1191 }
1192 }
1193 }
1194 },
1195 Entry{PhaseEntry{&this->residual_,
1196 [&modelResid = this->simulator_.model().linearizer().residual()]
1197 (const unsigned phaseIdx, const Context& ectx)
1198 {
1199 const unsigned sIdx = FluidSystem::solventComponentIndex(phaseIdx);
1200 const unsigned activeCompIdx = FluidSystem::canonicalToActiveCompIdx(sIdx);
1201 return modelResid[ectx.globalDofIdx][activeCompIdx];
1202 }
1203 },
1204 hasResidual
1205 },
1206 Entry{ScalarEntry{&this->rockCompPorvMultiplier_,
1207 [&problem = this->simulator_.problem()](const Context& ectx)
1208 {
1209 return problem.template
1210 rockCompPoroMultiplier<Scalar>(ectx.intQuants,
1211 ectx.globalDofIdx);
1212 }
1213 }
1214 },
1215 Entry{ScalarEntry{&this->rockCompTransMultiplier_,
1216 [&problem = this->simulator_.problem()](const Context& ectx)
1217 {
1218 return problem.
1219 template rockCompTransMultiplier<Scalar>(ectx.intQuants,
1220 ectx.globalDofIdx);
1221 }}
1222 },
1223 Entry{ScalarEntry{&this->minimumOilPressure_,
1224 [&problem = this->simulator_.problem()](const Context& ectx)
1225 {
1226 return std::min(getValue(ectx.fs.pressure(oilPhaseIdx)),
1227 problem.minOilPressure(ectx.globalDofIdx));
1228 }
1229 }
1230 },
1231 Entry{ScalarEntry{&this->bubblePointPressure_,
1232 [&failedCells = this->failedCellsPb_,
1233 &vanguard = this->simulator_.vanguard()](const Context& ectx)
1234 {
1235 try {
1236 return getValue(
1237 FluidSystem::bubblePointPressure(ectx.fs,
1238 ectx.intQuants.pvtRegionIndex())
1239 );
1240 } catch (const NumericalProblem&) {
1241 const auto cartesianIdx = vanguard.cartesianIndex(ectx.globalDofIdx);
1242 failedCells.push_back(cartesianIdx);
1243 return Scalar{0};
1244 }
1245 }
1246 }
1247 },
1248 Entry{ScalarEntry{&this->dewPointPressure_,
1249 [&failedCells = this->failedCellsPd_,
1250 &vanguard = this->simulator_.vanguard()](const Context& ectx)
1251 {
1252 try {
1253 return getValue(
1254 FluidSystem::dewPointPressure(ectx.fs,
1255 ectx.intQuants.pvtRegionIndex())
1256 );
1257 } catch (const NumericalProblem&) {
1258 const auto cartesianIdx = vanguard.cartesianIndex(ectx.globalDofIdx);
1259 failedCells.push_back(cartesianIdx);
1260 return Scalar{0};
1261 }
1262 }
1263 }
1264 },
1265 Entry{ScalarEntry{&this->overburdenPressure_,
1266 [&problem = simulator_.problem()](const Context& ectx)
1267 { return problem.overburdenPressure(ectx.globalDofIdx); }
1268 }
1269 },
1270 Entry{ScalarEntry{&this->temperature_,
1271 [](const Context& ectx)
1272 { return getValue(ectx.fs.temperature(oilPhaseIdx)); }
1273 }
1274 },
1275 Entry{ScalarEntry{&this->sSol_,
1276 [](const Context& ectx)
1277 { return getValue(ectx.intQuants.solventSaturation()); }
1278 }
1279 },
1280 Entry{ScalarEntry{&this->rswSol_,
1281 [](const Context& ectx)
1282 { return getValue(ectx.intQuants.rsSolw()); }
1283 }
1284 },
1285 Entry{ScalarEntry{&this->cPolymer_,
1286 [](const Context& ectx)
1287 { return getValue(ectx.intQuants.polymerConcentration()); }
1288 }
1289 },
1290 Entry{ScalarEntry{&this->cFoam_,
1291 [](const Context& ectx)
1292 { return getValue(ectx.intQuants.foamConcentration()); }
1293 }
1294 },
1295 Entry{ScalarEntry{&this->cSalt_,
1296 [](const Context& ectx)
1297 { return getValue(ectx.fs.saltConcentration()); }
1298 }
1299 },
1300 Entry{ScalarEntry{&this->pSalt_,
1301 [](const Context& ectx)
1302 { return getValue(ectx.fs.saltSaturation()); }
1303 }
1304 },
1305 Entry{ScalarEntry{&this->permFact_,
1306 [](const Context& ectx)
1307 { return getValue(ectx.intQuants.permFactor()); }
1308 }
1309 },
1310 Entry{ScalarEntry{&this->rPorV_,
1311 [&model = this->simulator_.model()](const Context& ectx)
1312 {
1313 const auto totVolume = model.dofTotalVolume(ectx.globalDofIdx);
1314 return totVolume * getValue(ectx.intQuants.porosity());
1315 }
1316 }
1317 },
1318 Entry{ScalarEntry{&this->rs_,
1319 [](const Context& ectx)
1320 { return getValue(ectx.fs.Rs()); }
1321 }
1322 },
1323 Entry{ScalarEntry{&this->rv_,
1324 [](const Context& ectx)
1325 { return getValue(ectx.fs.Rv()); }
1326 }
1327 },
1328 Entry{ScalarEntry{&this->rsw_,
1329 [](const Context& ectx)
1330 { return getValue(ectx.fs.Rsw()); }
1331 }
1332 },
1333 Entry{ScalarEntry{&this->rvw_,
1334 [](const Context& ectx)
1335 { return getValue(ectx.fs.Rvw()); }
1336 }
1337 },
1338 Entry{ScalarEntry{&this->ppcw_,
1339 [&matLawManager = *this->simulator_.problem().materialLawManager()]
1340 (const Context& ectx)
1341 {
1342 return matLawManager.
1343 oilWaterScaledEpsInfoDrainage(ectx.globalDofIdx).maxPcow;
1344 }
1345 }
1346 },
1347 Entry{ScalarEntry{&this->drsdtcon_,
1348 [&problem = this->simulator_.problem()](const Context& ectx)
1349 {
1350 return problem.drsdtcon(ectx.globalDofIdx,
1351 ectx.episodeIndex);
1352 }
1353 }
1354 },
1355 Entry{ScalarEntry{&this->pcgw_,
1356 [](const Context& ectx)
1357 {
1358 return getValue(ectx.fs.pressure(gasPhaseIdx)) -
1359 getValue(ectx.fs.pressure(waterPhaseIdx));
1360 }
1361 }
1362 },
1363 Entry{ScalarEntry{&this->pcow_,
1364 [](const Context& ectx)
1365 {
1366 return getValue(ectx.fs.pressure(oilPhaseIdx)) -
1367 getValue(ectx.fs.pressure(waterPhaseIdx));
1368 }
1369 }
1370 },
1371 Entry{ScalarEntry{&this->pcog_,
1372 [](const Context& ectx)
1373 {
1374 return getValue(ectx.fs.pressure(gasPhaseIdx)) -
1375 getValue(ectx.fs.pressure(oilPhaseIdx));
1376 }
1377 }
1378 },
1379 Entry{ScalarEntry{&this->fluidPressure_,
1380 [](const Context& ectx)
1381 {
1382 if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
1383 // Output oil pressure as default
1384 return getValue(ectx.fs.pressure(oilPhaseIdx));
1385 }
1386 else if (FluidSystem::phaseIsActive(gasPhaseIdx)) {
1387 // Output gas if oil is not present
1388 return getValue(ectx.fs.pressure(gasPhaseIdx));
1389 }
1390 else {
1391 // Output water if neither oil nor gas is present
1392 return getValue(ectx.fs.pressure(waterPhaseIdx));
1393 }
1394 }
1395 }
1396 },
1397 Entry{ScalarEntry{&this->gasDissolutionFactor_,
1398 [&problem = this->simulator_.problem()](const Context& ectx)
1399 {
1400 const Scalar SoMax = problem.maxOilSaturation(ectx.globalDofIdx);
1401 return FluidSystem::template
1402 saturatedDissolutionFactor<FluidState, Scalar>(ectx.fs,
1403 oilPhaseIdx,
1404 ectx.pvtRegionIdx,
1405 SoMax);
1406 }
1407 }
1408 },
1409 Entry{ScalarEntry{&this->oilVaporizationFactor_,
1410 [&problem = this->simulator_.problem()](const Context& ectx)
1411 {
1412 const Scalar SoMax = problem.maxOilSaturation(ectx.globalDofIdx);
1413 return FluidSystem::template
1414 saturatedDissolutionFactor<FluidState, Scalar>(ectx.fs,
1415 gasPhaseIdx,
1416 ectx.pvtRegionIdx,
1417 SoMax);
1418 }
1419 }
1420 },
1421 Entry{ScalarEntry{&this->gasDissolutionFactorInWater_,
1422 [&problem = this->simulator_.problem()](const Context& ectx)
1423 {
1424 const Scalar SwMax = problem.maxWaterSaturation(ectx.globalDofIdx);
1425 return FluidSystem::template
1426 saturatedDissolutionFactor<FluidState, Scalar>(ectx.fs,
1427 waterPhaseIdx,
1428 ectx.pvtRegionIdx,
1429 SwMax);
1430 }
1431 }
1432 },
1433 Entry{ScalarEntry{&this->waterVaporizationFactor_,
1434 [](const Context& ectx)
1435 {
1436 return FluidSystem::template
1437 saturatedVaporizationFactor<FluidState, Scalar>(ectx.fs,
1438 gasPhaseIdx,
1439 ectx.pvtRegionIdx);
1440 }
1441 }
1442 },
1443 Entry{ScalarEntry{&this->gasFormationVolumeFactor_,
1444 [](const Context& ectx)
1445 {
1446 return 1.0 / FluidSystem::template
1447 inverseFormationVolumeFactor<FluidState, Scalar>(ectx.fs,
1448 gasPhaseIdx,
1449 ectx.pvtRegionIdx);
1450 }
1451 }
1452 },
1453 Entry{ScalarEntry{&this->saturatedOilFormationVolumeFactor_,
1454 [](const Context& ectx)
1455 {
1456 return 1.0 / FluidSystem::template
1457 saturatedInverseFormationVolumeFactor<FluidState, Scalar>(ectx.fs,
1458 oilPhaseIdx,
1459 ectx.pvtRegionIdx);
1460 }
1461 }
1462 },
1463 Entry{ScalarEntry{&this->oilSaturationPressure_,
1464 [](const Context& ectx)
1465 {
1466 return FluidSystem::template
1467 saturationPressure<FluidState, Scalar>(ectx.fs,
1468 oilPhaseIdx,
1469 ectx.pvtRegionIdx);
1470 }
1471 }
1472 },
1473 Entry{ScalarEntry{&this->soMax_,
1474 [&problem = this->simulator_.problem()](const Context& ectx)
1475 {
1476 return std::max(getValue(ectx.fs.saturation(oilPhaseIdx)),
1477 problem.maxOilSaturation(ectx.globalDofIdx));
1478 }
1479 },
1480 !hysteresisConfig.enableHysteresis()
1481 },
1482 Entry{ScalarEntry{&this->swMax_,
1483 [&problem = this->simulator_.problem()](const Context& ectx)
1484 {
1485 return std::max(getValue(ectx.fs.saturation(waterPhaseIdx)),
1486 problem.maxWaterSaturation(ectx.globalDofIdx));
1487 }
1488 },
1489 !hysteresisConfig.enableHysteresis()
1490 },
1491 Entry{ScalarEntry{&this->soMax_,
1492 [](const Context& ectx)
1493 { return ectx.hParams.somax; }
1494 },
1495 hysteresisConfig.enableHysteresis() &&
1496 hysteresisConfig.enableNonWettingHysteresis() &&
1497 FluidSystem::phaseIsActive(oilPhaseIdx) &&
1498 FluidSystem::phaseIsActive(waterPhaseIdx)
1499 },
1500 Entry{ScalarEntry{&this->swMax_,
1501 [](const Context& ectx)
1502 { return ectx.hParams.swmax; }
1503 },
1504 hysteresisConfig.enableHysteresis() &&
1505 hysteresisConfig.enableWettingHysteresis() &&
1506 FluidSystem::phaseIsActive(oilPhaseIdx) &&
1507 FluidSystem::phaseIsActive(waterPhaseIdx)
1508 },
1509 Entry{ScalarEntry{&this->swmin_,
1510 [](const Context& ectx)
1511 { return ectx.hParams.swmin; }
1512 },
1513 hysteresisConfig.enableHysteresis() &&
1514 hysteresisConfig.enablePCHysteresis() &&
1515 FluidSystem::phaseIsActive(oilPhaseIdx) &&
1516 FluidSystem::phaseIsActive(waterPhaseIdx)
1517 },
1518 Entry{ScalarEntry{&this->sgmax_,
1519 [](const Context& ectx)
1520 { return ectx.hParams.sgmax; }
1521 },
1522 hysteresisConfig.enableHysteresis() &&
1523 hysteresisConfig.enableNonWettingHysteresis() &&
1524 FluidSystem::phaseIsActive(oilPhaseIdx) &&
1525 FluidSystem::phaseIsActive(gasPhaseIdx)
1526 },
1527 Entry{ScalarEntry{&this->shmax_,
1528 [](const Context& ectx)
1529 { return ectx.hParams.shmax; }
1530 },
1531 hysteresisConfig.enableHysteresis() &&
1532 hysteresisConfig.enableWettingHysteresis() &&
1533 FluidSystem::phaseIsActive(oilPhaseIdx) &&
1534 FluidSystem::phaseIsActive(gasPhaseIdx)
1535 },
1536 Entry{ScalarEntry{&this->somin_,
1537 [](const Context& ectx)
1538 { return ectx.hParams.somin; }
1539 },
1540 hysteresisConfig.enableHysteresis() &&
1541 hysteresisConfig.enablePCHysteresis() &&
1542 FluidSystem::phaseIsActive(oilPhaseIdx) &&
1543 FluidSystem::phaseIsActive(gasPhaseIdx)
1544 },
1545 Entry{[&model = this->simulator_.model(), this](const Context& ectx)
1546 {
1547 // Note: We intentionally exclude effects of rock
1548 // compressibility by using referencePorosity() here.
1549 const auto porv = ectx.intQuants.referencePorosity()
1550 * model.dofTotalVolume(ectx.globalDofIdx);
1551
1552 this->aggregateAverageDensityContributions_(ectx.fs, ectx.globalDofIdx,
1553 static_cast<double>(porv));
1554 }, this->regionAvgDensity_.has_value()
1555 },
1556 Entry{[&extboC = this->extboC_](const Context& ectx)
1557 {
1558 extboC.assignVolumes(ectx.globalDofIdx,
1559 ectx.intQuants.xVolume().value(),
1560 ectx.intQuants.yVolume().value());
1561 extboC.assignZFraction(ectx.globalDofIdx,
1562 ectx.intQuants.zFraction().value());
1563
1564 const Scalar stdVolOil = getValue(ectx.fs.saturation(oilPhaseIdx)) *
1565 getValue(ectx.fs.invB(oilPhaseIdx)) +
1566 getValue(ectx.fs.saturation(gasPhaseIdx)) *
1567 getValue(ectx.fs.invB(gasPhaseIdx)) *
1568 getValue(ectx.fs.Rv());
1569 const Scalar stdVolGas = getValue(ectx.fs.saturation(gasPhaseIdx)) *
1570 getValue(ectx.fs.invB(gasPhaseIdx)) *
1571 (1.0 - ectx.intQuants.yVolume().value()) +
1572 getValue(ectx.fs.saturation(oilPhaseIdx)) *
1573 getValue(ectx.fs.invB(oilPhaseIdx)) *
1574 getValue(ectx.fs.Rs()) *
1575 (1.0 - ectx.intQuants.xVolume().value());
1576 const Scalar stdVolCo2 = getValue(ectx.fs.saturation(gasPhaseIdx)) *
1577 getValue(ectx.fs.invB(gasPhaseIdx)) *
1578 ectx.intQuants.yVolume().value() +
1579 getValue(ectx.fs.saturation(oilPhaseIdx)) *
1580 getValue(ectx.fs.invB(oilPhaseIdx)) *
1581 getValue(ectx.fs.Rs()) *
1582 ectx.intQuants.xVolume().value();
1583 const Scalar rhoO = FluidSystem::referenceDensity(gasPhaseIdx, ectx.pvtRegionIdx);
1584 const Scalar rhoG = FluidSystem::referenceDensity(gasPhaseIdx, ectx.pvtRegionIdx);
1585 const Scalar rhoCO2 = ectx.intQuants.zRefDensity();
1586 const Scalar stdMassTotal = 1.0e-10 + stdVolOil * rhoO + stdVolGas * rhoG + stdVolCo2 * rhoCO2;
1587 extboC.assignMassFractions(ectx.globalDofIdx,
1588 stdVolGas * rhoG / stdMassTotal,
1589 stdVolOil * rhoO / stdMassTotal,
1590 stdVolCo2 * rhoCO2 / stdMassTotal);
1591 }, this->extboC_.allocated()
1592 },
1593 Entry{[&bioeffectsC = this->bioeffectsC_](const Context& ectx)
1594 {
1595 bioeffectsC.assign(ectx.globalDofIdx,
1596 ectx.intQuants.microbialConcentration().value(),
1597 ectx.intQuants.biofilmVolumeFraction().value());
1598 if (Indices::enableMICP) {
1599 bioeffectsC.assign(ectx.globalDofIdx,
1600 ectx.intQuants.oxygenConcentration().value(),
1601 ectx.intQuants.ureaConcentration().value(),
1602 ectx.intQuants.calciteVolumeFraction().value());
1603 }
1604 }, this->bioeffectsC_.allocated()
1605 },
1606 Entry{[&rftC = this->rftC_,
1607 &vanguard = this->simulator_.vanguard()](const Context& ectx)
1608 {
1609 const auto cartesianIdx = vanguard.cartesianIndex(ectx.globalDofIdx);
1610 rftC.assign(cartesianIdx,
1611 [&fs = ectx.fs]() { return getValue(fs.pressure(oilPhaseIdx)); },
1612 [&fs = ectx.fs]() { return getValue(fs.saturation(waterPhaseIdx)); },
1613 [&fs = ectx.fs]() { return getValue(fs.saturation(gasPhaseIdx)); });
1614 }
1615 },
1616 Entry{[&tC = this->tracerC_,
1617 &tM = this->simulator_.problem().tracerModel()](const Context& ectx)
1618 {
1619 tC.assignFreeConcentrations(ectx.globalDofIdx,
1620 [gIdx = ectx.globalDofIdx, &tM](const unsigned tracerIdx)
1621 { return tM.freeTracerConcentration(tracerIdx, gIdx); });
1622 tC.assignSolConcentrations(ectx.globalDofIdx,
1623 [gIdx = ectx.globalDofIdx, &tM](const unsigned tracerIdx)
1624 { return tM.solTracerConcentration(tracerIdx, gIdx); });
1625 }
1626 },
1627 Entry{[&flowsInf = this->simulator_.problem().model().linearizer().getFlowsInfo(),
1628 &flowsC = this->flowsC_](const Context& ectx)
1629 {
1630 const auto gas_idx = Indices::gasEnabled ?
1631 conti0EqIdx + FluidSystem::canonicalToActiveCompIdx(gasCompIdx) : -1;
1632 const auto oil_idx = Indices::oilEnabled ?
1633 conti0EqIdx + FluidSystem::canonicalToActiveCompIdx(oilCompIdx) : -1;
1634 const auto water_idx = Indices::waterEnabled ?
1635 conti0EqIdx + FluidSystem::canonicalToActiveCompIdx(waterCompIdx) : -1;
1636 const auto& flowsInfos = flowsInf[ectx.globalDofIdx];
1637 for (const auto& flowsInfo : flowsInfos) {
1638 flowsC.assignFlows(ectx.globalDofIdx,
1639 flowsInfo.faceId,
1640 flowsInfo.nncId,
1641 value_or_zero(gas_idx, flowsInfo.flow),
1642 value_or_zero(oil_idx, flowsInfo.flow),
1643 value_or_zero(water_idx, flowsInfo.flow));
1644 }
1645 }, !this->simulator_.problem().model().linearizer().getFlowsInfo().empty()
1646 },
1647 Entry{[&floresInf = this->simulator_.problem().model().linearizer().getFloresInfo(),
1648 &flowsC = this->flowsC_](const Context& ectx)
1649 {
1650 const auto gas_idx = Indices::gasEnabled ?
1651 conti0EqIdx + FluidSystem::canonicalToActiveCompIdx(gasCompIdx) : -1;
1652 const auto oil_idx = Indices::oilEnabled ?
1653 conti0EqIdx + FluidSystem::canonicalToActiveCompIdx(oilCompIdx) : -1;
1654 const auto water_idx = Indices::waterEnabled ?
1655 conti0EqIdx + FluidSystem::canonicalToActiveCompIdx(waterCompIdx) : -1;
1656 const auto& floresInfos = floresInf[ectx.globalDofIdx];
1657 for (const auto& floresInfo : floresInfos) {
1658 flowsC.assignFlores(ectx.globalDofIdx,
1659 floresInfo.faceId,
1660 floresInfo.nncId,
1661 value_or_zero(gas_idx, floresInfo.flow),
1662 value_or_zero(oil_idx, floresInfo.flow),
1663 value_or_zero(water_idx, floresInfo.flow));
1664 }
1665 }, !this->simulator_.problem().model().linearizer().getFloresInfo().empty()
1666 },
1667 // hack to make the intial output of rs and rv Ecl compatible.
1668 // For cells with swat == 1 Ecl outputs; rs = rsSat and rv=rvSat, in all but the initial step
1669 // where it outputs rs and rv values calculated by the initialization. To be compatible we overwrite
1670 // rs and rv with the values computed in the initially.
1671 // Volume factors, densities and viscosities need to be recalculated with the updated rs and rv values.
1672 Entry{ScalarEntry{&this->rv_,
1673 [&problem = this->simulator_.problem()](const Context& ectx)
1674 { return problem.initialFluidState(ectx.globalDofIdx).Rv(); }
1675 },
1676 simulator_.episodeIndex() < 0 &&
1677 FluidSystem::phaseIsActive(oilPhaseIdx) &&
1678 FluidSystem::phaseIsActive(gasPhaseIdx)
1679 },
1680 Entry{ScalarEntry{&this->rs_,
1681 [&problem = this->simulator_.problem()](const Context& ectx)
1682 { return problem.initialFluidState(ectx.globalDofIdx).Rs(); }
1683 },
1684 simulator_.episodeIndex() < 0 &&
1685 FluidSystem::phaseIsActive(oilPhaseIdx) &&
1686 FluidSystem::phaseIsActive(gasPhaseIdx)
1687 },
1688 Entry{ScalarEntry{&this->rsw_,
1689 [&problem = this->simulator_.problem()](const Context& ectx)
1690 { return problem.initialFluidState(ectx.globalDofIdx).Rsw(); }
1691 },
1692 simulator_.episodeIndex() < 0 &&
1693 FluidSystem::phaseIsActive(oilPhaseIdx) &&
1694 FluidSystem::phaseIsActive(gasPhaseIdx)
1695 },
1696 Entry{ScalarEntry{&this->rvw_,
1697 [&problem = this->simulator_.problem()](const Context& ectx)
1698 { return problem.initialFluidState(ectx.globalDofIdx).Rvw(); }
1699 },
1700 simulator_.episodeIndex() < 0 &&
1701 FluidSystem::phaseIsActive(oilPhaseIdx) &&
1702 FluidSystem::phaseIsActive(gasPhaseIdx)
1703 },
1704 // re-compute the volume factors, viscosities and densities if asked for
1705 Entry{PhaseEntry{&this->density_,
1706 [&problem = this->simulator_.problem()](const unsigned phase,
1707 const Context& ectx)
1708 {
1709 const auto& fsInitial = problem.initialFluidState(ectx.globalDofIdx);
1710 return FluidSystem::density(fsInitial,
1711 phase,
1712 ectx.intQuants.pvtRegionIndex());
1713 }
1714 },
1715 simulator_.episodeIndex() < 0 &&
1716 FluidSystem::phaseIsActive(oilPhaseIdx) &&
1717 FluidSystem::phaseIsActive(gasPhaseIdx)
1718 },
1719 Entry{PhaseEntry{&this->invB_,
1720 [&problem = this->simulator_.problem()](const unsigned phase,
1721 const Context& ectx)
1722 {
1723 const auto& fsInitial = problem.initialFluidState(ectx.globalDofIdx);
1724 return FluidSystem::inverseFormationVolumeFactor(fsInitial,
1725 phase,
1726 ectx.intQuants.pvtRegionIndex());
1727 }
1728 },
1729 simulator_.episodeIndex() < 0 &&
1730 FluidSystem::phaseIsActive(oilPhaseIdx) &&
1731 FluidSystem::phaseIsActive(gasPhaseIdx)
1732 },
1733 Entry{PhaseEntry{&this->viscosity_,
1734 [&problem = this->simulator_.problem()](const unsigned phase,
1735 const Context& ectx)
1736 {
1737 const auto& fsInitial = problem.initialFluidState(ectx.globalDofIdx);
1738 return FluidSystem::viscosity(fsInitial,
1739 phase,
1740 ectx.intQuants.pvtRegionIndex());
1741 }
1742 },
1743 simulator_.episodeIndex() < 0 &&
1744 FluidSystem::phaseIsActive(oilPhaseIdx) &&
1745 FluidSystem::phaseIsActive(gasPhaseIdx)
1746 },
1747 };
1748
1749 // Setup active extractors
1750 this->extractors_ = Extractor::removeInactive(extractors);
1751
1752 if constexpr (getPropValue<TypeTag, Properties::EnableMech>()) {
1753 if (this->mech_.allocated()) {
1754 this->extractors_.push_back(
1755 Entry{[&mech = this->mech_,
1756 &model = simulator_.problem().geoMechModel()](const Context& ectx)
1757 {
1758 mech.assignDelStress(ectx.globalDofIdx,
1759 model.delstress(ectx.globalDofIdx));
1760
1761 mech.assignDisplacement(ectx.globalDofIdx,
1762 model.disp(ectx.globalDofIdx, /*include_fracture*/true));
1763
1764 // is the tresagii stress which make rock fracture
1765 mech.assignFracStress(ectx.globalDofIdx,
1766 model.fractureStress(ectx.globalDofIdx));
1767
1768 mech.assignLinStress(ectx.globalDofIdx,
1769 model.linstress(ectx.globalDofIdx));
1770
1771 mech.assignPotentialForces(ectx.globalDofIdx,
1772 model.mechPotentialForce(ectx.globalDofIdx),
1773 model.mechPotentialPressForce(ectx.globalDofIdx),
1774 model.mechPotentialTempForce(ectx.globalDofIdx));
1775
1776 mech.assignStrain(ectx.globalDofIdx,
1777 model.strain(ectx.globalDofIdx, /*include_fracture*/true));
1778
1779 // Total stress is not stored but calculated result is Voigt notation
1780 mech.assignStress(ectx.globalDofIdx,
1781 model.stress(ectx.globalDofIdx, /*include_fracture*/true));
1782 }
1783 }
1784 );
1785 }
1786 }
1787 }
1788
1790 void setupBlockExtractors_(const bool isSubStep,
1791 const int reportStepNum)
1792 {
1793 using Entry = typename BlockExtractor::Entry;
1794 using Context = typename BlockExtractor::Context;
1795 using PhaseEntry = typename BlockExtractor::PhaseEntry;
1796 using ScalarEntry = typename BlockExtractor::ScalarEntry;
1797
1798 using namespace std::string_view_literals;
1799
1800 const auto pressure_handler =
1801 Entry{ScalarEntry{std::vector{"BPR"sv, "BPRESSUR"sv},
1802 [](const Context& ectx)
1803 {
1804 if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
1805 return getValue(ectx.fs.pressure(oilPhaseIdx));
1806 }
1807 else if (FluidSystem::phaseIsActive(gasPhaseIdx)) {
1808 return getValue(ectx.fs.pressure(gasPhaseIdx));
1809 }
1810 else { //if (FluidSystem::phaseIsActive(waterPhaseIdx))
1811 return getValue(ectx.fs.pressure(waterPhaseIdx));
1812 }
1813 }
1814 }
1815 };
1816
1817 const auto handlers = std::array{
1818 pressure_handler,
1819 Entry{PhaseEntry{std::array{
1820 std::array{"BWSAT"sv, "BOSAT"sv, "BGSAT"sv},
1821 std::array{"BSWAT"sv, "BSOIL"sv, "BSGAS"sv}
1822 },
1823 [](const unsigned phaseIdx, const Context& ectx)
1824 {
1825 return getValue(ectx.fs.saturation(phaseIdx));
1826 }
1827 }
1828 },
1829 Entry{ScalarEntry{"BNSAT",
1830 [](const Context& ectx)
1831 {
1832 return ectx.intQuants.solventSaturation().value();
1833 }
1834 }
1835 },
1836 Entry{ScalarEntry{std::vector{"BTCNFHEA"sv, "BTEMP"sv},
1837 [](const Context& ectx)
1838 {
1839 if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
1840 return getValue(ectx.fs.temperature(oilPhaseIdx));
1841 }
1842 else if (FluidSystem::phaseIsActive(gasPhaseIdx)) {
1843 return getValue(ectx.fs.temperature(gasPhaseIdx));
1844 }
1845 else { //if (FluidSystem::phaseIsActive(waterPhaseIdx))
1846 return getValue(ectx.fs.temperature(waterPhaseIdx));
1847 }
1848 }
1849 }
1850 },
1851 Entry{PhaseEntry{std::array{
1852 std::array{"BWKR"sv, "BOKR"sv, "BGKR"sv},
1853 std::array{"BKRW"sv, "BKRO"sv, "BKRG"sv}
1854 },
1855 [](const unsigned phaseIdx, const Context& ectx)
1856 {
1857 return getValue(ectx.intQuants.relativePermeability(phaseIdx));
1858 }
1859 }
1860 },
1861 Entry{ScalarEntry{"BKROG",
1862 [&problem = this->simulator_.problem()](const Context& ectx)
1863 {
1864 const auto& materialParams =
1865 problem.materialLawParams(ectx.elemCtx,
1866 ectx.dofIdx,
1867 /* timeIdx = */ 0);
1868 return getValue(MaterialLaw::template
1869 relpermOilInOilGasSystem<Evaluation>(materialParams,
1870 ectx.fs));
1871 }
1872 }
1873 },
1874 Entry{ScalarEntry{"BKROW",
1875 [&problem = this->simulator_.problem()](const Context& ectx)
1876 {
1877 const auto& materialParams = problem.materialLawParams(ectx.elemCtx,
1878 ectx.dofIdx,
1879 /* timeIdx = */ 0);
1880 return getValue(MaterialLaw::template
1881 relpermOilInOilWaterSystem<Evaluation>(materialParams,
1882 ectx.fs));
1883 }
1884 }
1885 },
1886 Entry{ScalarEntry{"BWPC",
1887 [](const Context& ectx)
1888 {
1889 return getValue(ectx.fs.pressure(oilPhaseIdx)) -
1890 getValue(ectx.fs.pressure(waterPhaseIdx));
1891 }
1892 }
1893 },
1894 Entry{ScalarEntry{"BGPC",
1895 [](const Context& ectx)
1896 {
1897 return getValue(ectx.fs.pressure(gasPhaseIdx)) -
1898 getValue(ectx.fs.pressure(oilPhaseIdx));
1899 }
1900 }
1901 },
1902 Entry{ScalarEntry{"BWPR",
1903 [](const Context& ectx)
1904 {
1905 return getValue(ectx.fs.pressure(waterPhaseIdx));
1906 }
1907 }
1908 },
1909 Entry{ScalarEntry{"BGPR",
1910 [](const Context& ectx)
1911 {
1912 return getValue(ectx.fs.pressure(gasPhaseIdx));
1913 }
1914 }
1915 },
1916 Entry{PhaseEntry{std::array{
1917 std::array{"BVWAT"sv, "BVOIL"sv, "BVGAS"sv},
1918 std::array{"BWVIS"sv, "BOVIS"sv, "BGVIS"sv}
1919 },
1920 [](const unsigned phaseIdx, const Context& ectx)
1921 {
1922 return getValue(ectx.fs.viscosity(phaseIdx));
1923 }
1924 }
1925 },
1926 Entry{PhaseEntry{std::array{
1927 std::array{"BWDEN"sv, "BODEN"sv, "BGDEN"sv},
1928 std::array{"BDENW"sv, "BDENO"sv, "BDENG"sv}
1929 },
1930 [](const unsigned phaseIdx, const Context& ectx)
1931 {
1932 return getValue(ectx.fs.density(phaseIdx));
1933 }
1934 }
1935 },
1936 Entry{ScalarEntry{"BFLOWI",
1937 [&flowsC = this->flowsC_](const Context& ectx)
1938 {
1939 return flowsC.getFlow(ectx.globalDofIdx, Dir::XPlus, waterCompIdx);
1940 }
1941 }
1942 },
1943 Entry{ScalarEntry{"BFLOWJ",
1944 [&flowsC = this->flowsC_](const Context& ectx)
1945 {
1946 return flowsC.getFlow(ectx.globalDofIdx, Dir::YPlus, waterCompIdx);
1947 }
1948 }
1949 },
1950 Entry{ScalarEntry{"BFLOWK",
1951 [&flowsC = this->flowsC_](const Context& ectx)
1952 {
1953 return flowsC.getFlow(ectx.globalDofIdx, Dir::ZPlus, waterCompIdx);
1954 }
1955 }
1956 },
1957 Entry{ScalarEntry{"BRPV",
1958 [&model = this->simulator_.model()](const Context& ectx)
1959 {
1960 return getValue(ectx.intQuants.porosity()) *
1961 model.dofTotalVolume(ectx.globalDofIdx);
1962 }
1963 }
1964 },
1965 Entry{PhaseEntry{std::array{"BWPV"sv, "BOPV"sv, "BGPV"sv},
1966 [&model = this->simulator_.model()](const unsigned phaseIdx,
1967 const Context& ectx)
1968 {
1969 return getValue(ectx.fs.saturation(phaseIdx)) *
1970 getValue(ectx.intQuants.porosity()) *
1971 model.dofTotalVolume(ectx.globalDofIdx);
1972 }
1973 }
1974 },
1975 Entry{ScalarEntry{"BRS",
1976 [](const Context& ectx)
1977 {
1978 return getValue(ectx.fs.Rs());
1979 }
1980 }
1981 },
1982 Entry{ScalarEntry{"BRV",
1983 [](const Context& ectx)
1984 {
1985 return getValue(ectx.fs.Rv());
1986 }
1987 }
1988 },
1989 Entry{ScalarEntry{"BOIP",
1990 [&model = this->simulator_.model()](const Context& ectx)
1991 {
1992 return (getValue(ectx.fs.invB(oilPhaseIdx)) *
1993 getValue(ectx.fs.saturation(oilPhaseIdx)) +
1994 getValue(ectx.fs.Rv()) *
1995 getValue(ectx.fs.invB(gasPhaseIdx)) *
1996 getValue(ectx.fs.saturation(gasPhaseIdx))) *
1997 model.dofTotalVolume(ectx.globalDofIdx) *
1998 getValue(ectx.intQuants.porosity());
1999 }
2000 }
2001 },
2002 Entry{ScalarEntry{"BGIP",
2003 [&model = this->simulator_.model()](const Context& ectx)
2004 {
2005 Scalar result = getValue(ectx.fs.invB(gasPhaseIdx)) *
2006 getValue(ectx.fs.saturation(gasPhaseIdx));
2007
2008 if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
2009 result += getValue(ectx.fs.Rs()) *
2010 getValue(ectx.fs.invB(oilPhaseIdx)) *
2011 getValue(ectx.fs.saturation(oilPhaseIdx));
2012 }
2013 else {
2014 result += getValue(ectx.fs.Rsw()) *
2015 getValue(ectx.fs.invB(waterPhaseIdx)) *
2016 getValue(ectx.fs.saturation(waterPhaseIdx));
2017 }
2018
2019 return result *
2020 model.dofTotalVolume(ectx.globalDofIdx) *
2021 getValue(ectx.intQuants.porosity());
2022 }
2023 }
2024 },
2025 Entry{ScalarEntry{"BWIP",
2026 [&model = this->simulator_.model()](const Context& ectx)
2027 {
2028 return getValue(ectx.fs.invB(waterPhaseIdx)) *
2029 getValue(ectx.fs.saturation(waterPhaseIdx)) *
2030 model.dofTotalVolume(ectx.globalDofIdx) *
2031 getValue(ectx.intQuants.porosity());
2032 }
2033 }
2034 },
2035 Entry{ScalarEntry{"BOIPL",
2036 [&model = this->simulator_.model()](const Context& ectx)
2037 {
2038 return getValue(ectx.fs.invB(oilPhaseIdx)) *
2039 getValue(ectx.fs.saturation(oilPhaseIdx)) *
2040 model.dofTotalVolume(ectx.globalDofIdx) *
2041 getValue(ectx.intQuants.porosity());
2042 }
2043 }
2044 },
2045 Entry{ScalarEntry{"BGIPL",
2046 [&model = this->simulator_.model()](const Context& ectx)
2047 {
2048 Scalar result;
2049 if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
2050 result = getValue(ectx.fs.Rs()) *
2051 getValue(ectx.fs.invB(oilPhaseIdx)) *
2052 getValue(ectx.fs.saturation(oilPhaseIdx));
2053 }
2054 else {
2055 result = getValue(ectx.fs.Rsw()) *
2056 getValue(ectx.fs.invB(waterPhaseIdx)) *
2057 getValue(ectx.fs.saturation(waterPhaseIdx));
2058 }
2059 return result *
2060 model.dofTotalVolume(ectx.globalDofIdx) *
2061 getValue(ectx.intQuants.porosity());
2062 }
2063 }
2064 },
2065 Entry{ScalarEntry{"BGIPG",
2066 [&model = this->simulator_.model()](const Context& ectx)
2067 {
2068 return getValue(ectx.fs.invB(gasPhaseIdx)) *
2069 getValue(ectx.fs.saturation(gasPhaseIdx)) *
2070 model.dofTotalVolume(ectx.globalDofIdx) *
2071 getValue(ectx.intQuants.porosity());
2072 }
2073 }
2074 },
2075 Entry{ScalarEntry{"BOIPG",
2076 [&model = this->simulator_.model()](const Context& ectx)
2077 {
2078 return getValue(ectx.fs.Rv()) *
2079 getValue(ectx.fs.invB(gasPhaseIdx)) *
2080 getValue(ectx.fs.saturation(gasPhaseIdx)) *
2081 model.dofTotalVolume(ectx.globalDofIdx) *
2082 getValue(ectx.intQuants.porosity());
2083 }
2084 }
2085 },
2086 Entry{PhaseEntry{std::array{"BPPW"sv, "BPPO"sv, "BPPG"sv},
2087 [&simConfig = this->eclState_.getSimulationConfig(),
2088 &grav = this->simulator_.problem().gravity(),
2089 &regionAvgDensity = this->regionAvgDensity_,
2090 &problem = this->simulator_.problem(),
2091 &regions = this->regions_](const unsigned phaseIdx, const Context& ectx)
2092 {
2093 auto phase = RegionPhasePoreVolAverage::Phase{};
2094 phase.ix = phaseIdx;
2095
2096 // Note different region handling here. FIPNUM is
2097 // one-based, but we need zero-based lookup in
2098 // DatumDepth. On the other hand, pvtRegionIndex is
2099 // zero-based but we need one-based lookup in
2100 // RegionPhasePoreVolAverage.
2101
2102 // Subtract one to convert FIPNUM to region index.
2103 const auto datum = simConfig.datumDepths()(regions["FIPNUM"][ectx.dofIdx] - 1);
2104
2105 // Add one to convert region index to region ID.
2106 const auto region = RegionPhasePoreVolAverage::Region {
2107 ectx.elemCtx.primaryVars(ectx.dofIdx, /*timeIdx=*/0).pvtRegionIndex() + 1
2108 };
2109
2110 const auto density = regionAvgDensity->value("PVTNUM", phase, region);
2111
2112 const auto press = getValue(ectx.fs.pressure(phase.ix));
2113 const auto dz = problem.dofCenterDepth(ectx.globalDofIdx) - datum;
2114 return press - density*dz*grav[GridView::dimensionworld - 1];
2115 }
2116 }
2117 },
2118 Entry{ScalarEntry{"BAMIP",
2119 [&model = this->simulator_.model()](const Context& ectx)
2120 {
2121 const Scalar rhoW = FluidSystem::referenceDensity(waterPhaseIdx,
2122 ectx.intQuants.pvtRegionIndex());
2123 return getValue(ectx.fs.invB(waterPhaseIdx)) *
2124 getValue(ectx.fs.saturation(waterPhaseIdx)) *
2125 rhoW *
2126 model.dofTotalVolume(ectx.globalDofIdx) *
2127 getValue(ectx.intQuants.porosity());
2128 }
2129 }
2130 },
2131 Entry{ScalarEntry{"BMMIP",
2132 [&model = this->simulator_.model()](const Context& ectx)
2133 {
2134 return getValue(ectx.intQuants.microbialConcentration()) *
2135 getValue(ectx.fs.saturation(waterPhaseIdx)) *
2136 getValue(ectx.intQuants.porosity()) *
2137 model.dofTotalVolume(ectx.globalDofIdx);
2138 }
2139 }
2140 },
2141 Entry{ScalarEntry{"BMOIP",
2142 [&model = this->simulator_.model()](const Context& ectx)
2143 {
2144 return getValue(ectx.intQuants.oxygenConcentration()) *
2145 getValue(ectx.intQuants.porosity()) *
2146 model.dofTotalVolume(ectx.globalDofIdx);
2147 }
2148 }
2149 },
2150 Entry{ScalarEntry{"BMUIP",
2151 [&model = this->simulator_.model()](const Context& ectx)
2152 {
2153 return getValue(ectx.intQuants.ureaConcentration()) *
2154 getValue(ectx.intQuants.porosity()) *
2155 model.dofTotalVolume(ectx.globalDofIdx) * 1;
2156 }
2157 }
2158 },
2159 Entry{ScalarEntry{"BMBIP",
2160 [&model = this->simulator_.model()](const Context& ectx)
2161 {
2162 return model.dofTotalVolume(ectx.globalDofIdx) *
2163 getValue(ectx.intQuants.biofilmMass());
2164 }
2165 }
2166 },
2167 Entry{ScalarEntry{"BMCIP",
2168 [&model = this->simulator_.model()](const Context& ectx)
2169 {
2170 return model.dofTotalVolume(ectx.globalDofIdx) *
2171 getValue(ectx.intQuants.calciteMass());
2172 }
2173 }
2174 },
2175 Entry{ScalarEntry{"BGMIP",
2176 [&model = this->simulator_.model()](const Context& ectx)
2177 {
2178 Scalar result = getValue(ectx.fs.invB(gasPhaseIdx)) *
2179 getValue(ectx.fs.saturation(gasPhaseIdx));
2180
2181 if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
2182 result += getValue(ectx.fs.Rs()) *
2183 getValue(ectx.fs.invB(oilPhaseIdx)) *
2184 getValue(ectx.fs.saturation(oilPhaseIdx));
2185 }
2186 else {
2187 result += getValue(ectx.fs.Rsw()) *
2188 getValue(ectx.fs.invB(waterPhaseIdx)) *
2189 getValue(ectx.fs.saturation(waterPhaseIdx));
2190 }
2191 const Scalar rhoG = FluidSystem::referenceDensity(gasPhaseIdx,
2192 ectx.intQuants.pvtRegionIndex());
2193 return result *
2194 model.dofTotalVolume(ectx.globalDofIdx) *
2195 getValue(ectx.intQuants.porosity()) *
2196 rhoG;
2197 }
2198 }
2199 },
2200 Entry{ScalarEntry{"BGMGP",
2201 [&model = this->simulator_.model()](const Context& ectx)
2202 {
2203 const Scalar rhoG = FluidSystem::referenceDensity(gasPhaseIdx,
2204 ectx.intQuants.pvtRegionIndex());
2205 return getValue(ectx.fs.invB(gasPhaseIdx)) *
2206 getValue(ectx.fs.saturation(gasPhaseIdx)) *
2207 model.dofTotalVolume(ectx.globalDofIdx) *
2208 getValue(ectx.intQuants.porosity()) *
2209 rhoG;
2210 }
2211 }
2212 },
2213 Entry{ScalarEntry{"BGMDS",
2214 [&model = this->simulator_.model()](const Context& ectx)
2215 {
2216 Scalar result;
2217 if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
2218 result = getValue(ectx.fs.Rs()) *
2219 getValue(ectx.fs.invB(oilPhaseIdx)) *
2220 getValue(ectx.fs.saturation(oilPhaseIdx));
2221 }
2222 else {
2223 result = getValue(ectx.fs.Rsw()) *
2224 getValue(ectx.fs.invB(waterPhaseIdx)) *
2225 getValue(ectx.fs.saturation(waterPhaseIdx));
2226 }
2227 const Scalar rhoG = FluidSystem::referenceDensity(gasPhaseIdx,
2228 ectx.intQuants.pvtRegionIndex());
2229 return result *
2230 model.dofTotalVolume(ectx.globalDofIdx) *
2231 getValue(ectx.intQuants.porosity()) *
2232 rhoG;
2233 }
2234 }
2235 },
2236 Entry{ScalarEntry{"BGMST",
2237 [&model = this->simulator_.model(),
2238 &problem = this->simulator_.problem()](const Context& ectx)
2239 {
2240 const auto& scaledDrainageInfo = problem.materialLawManager()
2241 ->oilWaterScaledEpsInfoDrainage(ectx.dofIdx);
2242 const Scalar sg = getValue(ectx.fs.saturation(gasPhaseIdx));
2243 Scalar strandedGas = scaledDrainageInfo.Sgcr;
2244 if (problem.materialLawManager()->enableHysteresis()) {
2245 const auto& matParams = problem.materialLawParams(ectx.dofIdx);
2246 const Scalar krg = getValue(ectx.intQuants.relativePermeability(gasPhaseIdx));
2247 strandedGas = MaterialLaw::strandedGasSaturation(matParams, sg, krg);
2248 }
2249 const Scalar xgW = FluidSystem::phaseIsActive(waterPhaseIdx) ?
2250 FluidSystem::convertRvwToXgW(getValue(ectx.fs.Rvw()), ectx.intQuants.pvtRegionIndex())
2251 : FluidSystem::convertRvToXgO(getValue(ectx.fs.Rv()), ectx.intQuants.pvtRegionIndex());
2252 return (1.0 - xgW) *
2253 model.dofTotalVolume(ectx.globalDofIdx) *
2254 getValue(ectx.intQuants.porosity()) *
2255 getValue(ectx.fs.density(gasPhaseIdx)) *
2256 std::min(strandedGas, sg);
2257 }
2258 }
2259 },
2260 Entry{ScalarEntry{"BGMUS",
2261 [&model = this->simulator_.model(),
2262 &problem = this->simulator_.problem()](const Context& ectx)
2263 {
2264 const auto& scaledDrainageInfo = problem.materialLawManager()
2265 ->oilWaterScaledEpsInfoDrainage(ectx.dofIdx);
2266 const Scalar sg = getValue(ectx.fs.saturation(gasPhaseIdx));
2267 Scalar strandedGas = scaledDrainageInfo.Sgcr;
2268 if (problem.materialLawManager()->enableHysteresis()) {
2269 const auto& matParams = problem.materialLawParams(ectx.dofIdx);
2270 const Scalar krg = getValue(ectx.intQuants.relativePermeability(gasPhaseIdx));
2271 strandedGas = MaterialLaw::strandedGasSaturation(matParams, sg, krg);
2272 }
2273 const Scalar xgW = FluidSystem::phaseIsActive(waterPhaseIdx) ?
2274 FluidSystem::convertRvwToXgW(getValue(ectx.fs.Rvw()), ectx.intQuants.pvtRegionIndex())
2275 : FluidSystem::convertRvToXgO(getValue(ectx.fs.Rv()), ectx.intQuants.pvtRegionIndex());
2276 return (1.0 - xgW) *
2277 model.dofTotalVolume(ectx.globalDofIdx) *
2278 getValue(ectx.intQuants.porosity()) *
2279 getValue(ectx.fs.density(gasPhaseIdx)) *
2280 std::max(Scalar{0.0}, sg - strandedGas);
2281 }
2282 }
2283 },
2284 Entry{ScalarEntry{"BGMTR",
2285 [&model = this->simulator_.model(),
2286 &problem = this->simulator_.problem()](const Context& ectx)
2287 {
2288 const auto& scaledDrainageInfo = problem.materialLawManager()
2289 ->oilWaterScaledEpsInfoDrainage(ectx.dofIdx);
2290 Scalar trappedGas = scaledDrainageInfo.Sgcr;
2291 if (problem.materialLawManager()->enableHysteresis()) {
2292 const auto& matParams = problem.materialLawParams(ectx.dofIdx);
2293 trappedGas = MaterialLaw::trappedGasSaturation(matParams, /*maxTrapping*/true);
2294 }
2295 const Scalar xgW = FluidSystem::phaseIsActive(waterPhaseIdx) ?
2296 FluidSystem::convertRvwToXgW(getValue(ectx.fs.Rvw()), ectx.intQuants.pvtRegionIndex())
2297 : FluidSystem::convertRvToXgO(getValue(ectx.fs.Rv()), ectx.intQuants.pvtRegionIndex());
2298 return (1.0 - xgW) *
2299 model.dofTotalVolume(ectx.globalDofIdx) *
2300 getValue(ectx.intQuants.porosity()) *
2301 getValue(ectx.fs.density(gasPhaseIdx)) *
2302 std::min(trappedGas, getValue(ectx.fs.saturation(gasPhaseIdx)));
2303 }
2304 }
2305 },
2306 Entry{ScalarEntry{"BGMMO",
2307 [&model = this->simulator_.model(),
2308 &problem = this->simulator_.problem()](const Context& ectx)
2309 {
2310 const auto& scaledDrainageInfo = problem.materialLawManager()
2311 ->oilWaterScaledEpsInfoDrainage(ectx.dofIdx);
2312 Scalar trappedGas = scaledDrainageInfo.Sgcr;
2313 if (problem.materialLawManager()->enableHysteresis()) {
2314 const auto& matParams = problem.materialLawParams(ectx.dofIdx);
2315 trappedGas = MaterialLaw::trappedGasSaturation(matParams, /*maxTrapping*/true);
2316 }
2317 const Scalar xgW = FluidSystem::phaseIsActive(waterPhaseIdx) ?
2318 FluidSystem::convertRvwToXgW(getValue(ectx.fs.Rvw()), ectx.intQuants.pvtRegionIndex())
2319 : FluidSystem::convertRvToXgO(getValue(ectx.fs.Rv()), ectx.intQuants.pvtRegionIndex());
2320 return (1.0 - xgW) *
2321 model.dofTotalVolume(ectx.globalDofIdx) *
2322 getValue(ectx.intQuants.porosity()) *
2323 getValue(ectx.fs.density(gasPhaseIdx)) *
2324 std::max(Scalar{0.0}, getValue(ectx.fs.saturation(gasPhaseIdx)) - trappedGas);
2325 }
2326 }
2327 },
2328 Entry{ScalarEntry{"BGKTR",
2329 [&model = this->simulator_.model(),
2330 &problem = this->simulator_.problem()](const Context& ectx)
2331 {
2332 const auto& scaledDrainageInfo = problem.materialLawManager()
2333 ->oilWaterScaledEpsInfoDrainage(ectx.dofIdx);
2334 const Scalar sg = getValue(ectx.fs.saturation(gasPhaseIdx));
2335 Scalar sgcr = scaledDrainageInfo.Sgcr;
2336 if (problem.materialLawManager()->enableHysteresis()) {
2337 const auto& matParams = problem.materialLawParams(ectx.dofIdx);
2338 sgcr = MaterialLaw::trappedGasSaturation(matParams, /*maxTrapping*/false);
2339 }
2340 if (sg > sgcr) {
2341 return 0.0;
2342 }
2343 else {
2344 const Scalar xgW = FluidSystem::phaseIsActive(waterPhaseIdx) ?
2345 FluidSystem::convertRvwToXgW(getValue(ectx.fs.Rvw()), ectx.intQuants.pvtRegionIndex())
2346 : FluidSystem::convertRvToXgO(getValue(ectx.fs.Rv()), ectx.intQuants.pvtRegionIndex());
2347 return (1.0 - xgW) *
2348 model.dofTotalVolume(ectx.globalDofIdx) *
2349 getValue(ectx.intQuants.porosity()) *
2350 getValue(ectx.fs.density(gasPhaseIdx)) *
2351 getValue(ectx.fs.saturation(gasPhaseIdx));
2352 }
2353 }
2354 }
2355 },
2356 Entry{ScalarEntry{"BGKMO",
2357 [&model = this->simulator_.model(),
2358 &problem = this->simulator_.problem()](const Context& ectx)
2359 {
2360 const auto& scaledDrainageInfo = problem.materialLawManager()
2361 ->oilWaterScaledEpsInfoDrainage(ectx.dofIdx);
2362 const Scalar sg = getValue(ectx.fs.saturation(gasPhaseIdx));
2363 Scalar sgcr = scaledDrainageInfo.Sgcr;
2364 if (problem.materialLawManager()->enableHysteresis()) {
2365 const auto& matParams = problem.materialLawParams(ectx.dofIdx);
2366 sgcr = MaterialLaw::trappedGasSaturation(matParams, /*maxTrapping*/false);
2367 }
2368 if (sgcr >= sg) {
2369 return 0.0;
2370 }
2371 else {
2372 const Scalar xgW = FluidSystem::phaseIsActive(waterPhaseIdx) ?
2373 FluidSystem::convertRvwToXgW(getValue(ectx.fs.Rvw()), ectx.intQuants.pvtRegionIndex())
2374 : FluidSystem::convertRvToXgO(getValue(ectx.fs.Rv()), ectx.intQuants.pvtRegionIndex());
2375 return (1.0 - xgW) *
2376 model.dofTotalVolume(ectx.globalDofIdx) *
2377 getValue(ectx.intQuants.porosity()) *
2378 getValue(ectx.fs.density(gasPhaseIdx)) *
2379 getValue(ectx.fs.saturation(gasPhaseIdx));
2380 }
2381 }
2382 }
2383 },
2384 Entry{ScalarEntry{"BGCDI",
2385 [&model = this->simulator_.model(),
2386 &problem = this->simulator_.problem()](const Context& ectx)
2387 {
2388 const auto& scaledDrainageInfo = problem.materialLawManager()
2389 ->oilWaterScaledEpsInfoDrainage(ectx.dofIdx);
2390 Scalar sgcr = scaledDrainageInfo.Sgcr;
2391 if (problem.materialLawManager()->enableHysteresis()) {
2392 const auto& matParams = problem.materialLawParams(ectx.dofIdx);
2393 sgcr = MaterialLaw::trappedGasSaturation(matParams, /*maxTrapping*/false);
2394 }
2395 const Scalar xgW = FluidSystem::phaseIsActive(waterPhaseIdx) ?
2396 FluidSystem::convertRvwToXgW(getValue(ectx.fs.Rvw()), ectx.intQuants.pvtRegionIndex())
2397 : FluidSystem::convertRvToXgO(getValue(ectx.fs.Rv()), ectx.intQuants.pvtRegionIndex());
2398 return (1.0 - xgW) *
2399 model.dofTotalVolume(ectx.globalDofIdx) *
2400 getValue(ectx.intQuants.porosity()) *
2401 getValue(ectx.fs.density(gasPhaseIdx)) *
2402 std::min(sgcr, getValue(ectx.fs.saturation(gasPhaseIdx))) /
2403 FluidSystem::molarMass(gasCompIdx, ectx.intQuants.pvtRegionIndex());
2404 }
2405 }
2406 },
2407 Entry{ScalarEntry{"BGCDM",
2408 [&model = this->simulator_.model(),
2409 &problem = this->simulator_.problem()](const Context& ectx)
2410 {
2411 const auto& scaledDrainageInfo = problem.materialLawManager()
2412 ->oilWaterScaledEpsInfoDrainage(ectx.dofIdx);
2413 Scalar sgcr = scaledDrainageInfo.Sgcr;
2414 if (problem.materialLawManager()->enableHysteresis()) {
2415 const auto& matParams = problem.materialLawParams(ectx.dofIdx);
2416 sgcr = MaterialLaw::trappedGasSaturation(matParams, /*maxTrapping*/false);
2417 }
2418 const Scalar xgW = FluidSystem::phaseIsActive(waterPhaseIdx) ?
2419 FluidSystem::convertRvwToXgW(getValue(ectx.fs.Rvw()), ectx.intQuants.pvtRegionIndex())
2420 : FluidSystem::convertRvToXgO(getValue(ectx.fs.Rv()), ectx.intQuants.pvtRegionIndex());
2421 return (1.0 - xgW) *
2422 model.dofTotalVolume(ectx.globalDofIdx) *
2423 getValue(ectx.intQuants.porosity()) *
2424 getValue(ectx.fs.density(gasPhaseIdx)) *
2425 std::max(Scalar{0.0}, getValue(ectx.fs.saturation(gasPhaseIdx)) - sgcr) /
2426 FluidSystem::molarMass(gasCompIdx, ectx.intQuants.pvtRegionIndex());
2427 }
2428 }
2429 },
2430 Entry{ScalarEntry{"BGKDI",
2431 [&model = this->simulator_.model(),
2432 &problem = this->simulator_.problem()](const Context& ectx)
2433 {
2434 const auto& scaledDrainageInfo = problem.materialLawManager()
2435 ->oilWaterScaledEpsInfoDrainage(ectx.dofIdx);
2436 const Scalar sg = getValue(ectx.fs.saturation(gasPhaseIdx));
2437 Scalar sgcr = scaledDrainageInfo.Sgcr;
2438 if (problem.materialLawManager()->enableHysteresis()) {
2439 const auto& matParams = problem.materialLawParams(ectx.dofIdx);
2440 sgcr = MaterialLaw::trappedGasSaturation(matParams, /*maxTrapping*/false);
2441 }
2442 if (sg > sgcr) {
2443 return 0.0;
2444 }
2445 else {
2446 const Scalar xgW = FluidSystem::phaseIsActive(waterPhaseIdx) ?
2447 FluidSystem::convertRvwToXgW(getValue(ectx.fs.Rvw()), ectx.intQuants.pvtRegionIndex())
2448 : FluidSystem::convertRvToXgO(getValue(ectx.fs.Rv()), ectx.intQuants.pvtRegionIndex());
2449 return (1.0 - xgW) *
2450 model.dofTotalVolume(ectx.globalDofIdx) *
2451 getValue(ectx.intQuants.porosity()) *
2452 getValue(ectx.fs.density(gasPhaseIdx)) *
2453 getValue(ectx.fs.saturation(gasPhaseIdx)) /
2454 FluidSystem::molarMass(gasCompIdx, ectx.intQuants.pvtRegionIndex());
2455 }
2456 }
2457 }
2458 },
2459 Entry{ScalarEntry{"BGKDM",
2460 [&model = this->simulator_.model(),
2461 &problem = this->simulator_.problem()](const Context& ectx)
2462 {
2463 const auto& scaledDrainageInfo = problem.materialLawManager()
2464 ->oilWaterScaledEpsInfoDrainage(ectx.dofIdx);
2465 const Scalar sg = getValue(ectx.fs.saturation(gasPhaseIdx));
2466 Scalar sgcr = scaledDrainageInfo.Sgcr;
2467 if (problem.materialLawManager()->enableHysteresis()) {
2468 const auto& matParams = problem.materialLawParams(ectx.dofIdx);
2469 sgcr = MaterialLaw::trappedGasSaturation(matParams, /*maxTrapping*/false);
2470 }
2471 if (sgcr >= sg) {
2472 return 0.0;
2473 }
2474 else {
2475 const Scalar xgW = FluidSystem::phaseIsActive(waterPhaseIdx) ?
2476 FluidSystem::convertRvwToXgW(getValue(ectx.fs.Rvw()), ectx.intQuants.pvtRegionIndex())
2477 : FluidSystem::convertRvToXgO(getValue(ectx.fs.Rv()), ectx.intQuants.pvtRegionIndex());
2478 return (1.0 - xgW) *
2479 model.dofTotalVolume(ectx.globalDofIdx) *
2480 getValue(ectx.intQuants.porosity()) *
2481 getValue(ectx.fs.density(gasPhaseIdx)) *
2482 getValue(ectx.fs.saturation(gasPhaseIdx)) /
2483 FluidSystem::molarMass(gasCompIdx, ectx.intQuants.pvtRegionIndex());
2484 }
2485 }
2486 }
2487 },
2488 Entry{ScalarEntry{"BWCD",
2489 [&model = this->simulator_.model()](const Context& ectx)
2490 {
2491 Scalar result;
2492 if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
2493 result = getValue(ectx.fs.Rs()) *
2494 getValue(ectx.fs.invB(oilPhaseIdx)) *
2495 getValue(ectx.fs.saturation(oilPhaseIdx));
2496 }
2497 else {
2498 result = getValue(ectx.fs.Rsw()) *
2499 getValue(ectx.fs.invB(waterPhaseIdx)) *
2500 getValue(ectx.fs.saturation(waterPhaseIdx));
2501 }
2502 const Scalar rhoG = FluidSystem::referenceDensity(gasPhaseIdx,
2503 ectx.intQuants.pvtRegionIndex());
2504 return result *
2505 model.dofTotalVolume(ectx.globalDofIdx) *
2506 getValue(ectx.intQuants.porosity()) *
2507 rhoG /
2508 FluidSystem::molarMass(gasCompIdx, ectx.intQuants.pvtRegionIndex());
2509 }
2510 }
2511 },
2512 Entry{ScalarEntry{"BWIPG",
2513 [&model = this->simulator_.model()](const Context& ectx)
2514 {
2515 Scalar result = 0.0;
2516 if (FluidSystem::phaseIsActive(gasPhaseIdx)) {
2517 result = getValue(ectx.fs.Rvw()) *
2518 getValue(ectx.fs.invB(gasPhaseIdx)) *
2519 getValue(ectx.fs.saturation(gasPhaseIdx));
2520 }
2521 return result *
2522 model.dofTotalVolume(ectx.globalDofIdx) *
2523 getValue(ectx.intQuants.porosity());
2524 }
2525 }
2526 },
2527 Entry{ScalarEntry{"BWIPL",
2528 [&model = this->simulator_.model()](const Context& ectx)
2529 {
2530 return getValue(ectx.fs.invB(waterPhaseIdx)) *
2531 getValue(ectx.fs.saturation(waterPhaseIdx)) *
2532 model.dofTotalVolume(ectx.globalDofIdx) *
2533 getValue(ectx.intQuants.porosity());
2534 }
2535 }
2536 },
2537 };
2538
2539 this->blockExtractors_ = BlockExtractor::setupExecMap(this->blockData_, handlers);
2540
2541 this->extraBlockData_.clear();
2542 if (reportStepNum > 0 && !isSubStep) {
2543 // check we need extra block pressures for RPTSCHED
2544 const auto& rpt = this->schedule_[reportStepNum - 1].rpt_config.get();
2545 if (rpt.contains("WELLS") && rpt.at("WELLS") > 1) {
2546 this->setupExtraBlockData(reportStepNum,
2547 [&c = this->collectOnIORank_](const int idx)
2548 { return c.isCartIdxOnThisRank(idx); });
2549
2550 const auto extraHandlers = std::array{
2551 pressure_handler,
2552 };
2553
2554 this->extraBlockExtractors_ = BlockExtractor::setupExecMap(this->extraBlockData_, extraHandlers);
2555 }
2556 }
2557 }
2558
2559 const Simulator& simulator_;
2560 const CollectDataOnIORankType& collectOnIORank_;
2561 std::vector<typename Extractor::Entry> extractors_;
2562 typename BlockExtractor::ExecMap blockExtractors_;
2563 typename BlockExtractor::ExecMap extraBlockExtractors_;
2564};
2565
2566} // namespace Opm
2567
2568#endif // OPM_OUTPUT_BLACK_OIL_MODULE_HPP
Contains the classes required to extend the black-oil model by energy.
Declares the properties required by the black oil model.
Definition: CollectDataOnIORank.hpp:56
The base class for the element-centered finite-volume discretization scheme.
Definition: ecfvdiscretization.hh:160
void assignMicrobialMass(const unsigned globalDofIdx, const Scalar microbialMass)
void assignCalciteMass(const unsigned globalDofIdx, const Scalar calciteMass)
bool hasCo2InGas() const
void assignCo2InWater(const unsigned globalDofIdx, const Scalar co2InWater, const Scalar mM)
void assignVolumesSurface(const unsigned globalDofIdx, const std::array< Scalar, numPhases > &fip)
bool has(const Inplace::Phase phase) const
bool hasMicrobialMass() const
void assignWaterMass(const unsigned globalDofIdx, const std::array< Scalar, numPhases > &fip, const Scalar rhoW)
void assignCo2InGas(const unsigned globalDofIdx, const Co2InGasInput &v)
bool hasOxygenMass() const
void assignVolumesReservoir(const unsigned globalDofIdx, const Scalar saltConcentration, const std::array< Scalar, numPhases > &fipr)
void assignPoreVolume(const unsigned globalDofIdx, const Scalar value)
void assignOxygenMass(const unsigned globalDofIdx, const Scalar oxygenMass)
bool hasUreaMass() const
void assignOilGasDistribution(const unsigned globalDofIdx, const Scalar gasInPlaceLiquid, const Scalar oilInPlaceGas)
void assignBiofilmMass(const unsigned globalDofIdx, const Scalar biofilmMass)
bool hasWaterMass() const
bool hasCo2InWater() const
void assignUreaMass(const unsigned globalDofIdx, const Scalar ureaMass)
bool hasCalciteMass() const
bool hasBiofilmMass() const
const std::vector< Scalar > & get(const Inplace::Phase phase) const
void assignGasWater(const unsigned globalDofIdx, const std::array< Scalar, numPhases > &fip, const Scalar gasInPlaceWater, const Scalar waterInPlaceGas)
Definition: GenericOutputBlackoilModule.hpp:76
std::map< std::pair< std::string, int >, double > blockData_
Definition: GenericOutputBlackoilModule.hpp:456
std::array< ScalarBuffer, numPhases > relativePermeability_
Definition: GenericOutputBlackoilModule.hpp:445
ScalarBuffer fluidPressure_
Definition: GenericOutputBlackoilModule.hpp:399
std::array< ScalarBuffer, numPhases > density_
Definition: GenericOutputBlackoilModule.hpp:443
ScalarBuffer saturatedOilFormationVolumeFactor_
Definition: GenericOutputBlackoilModule.hpp:431
ScalarBuffer overburdenPressure_
Definition: GenericOutputBlackoilModule.hpp:405
ScalarBuffer gasDissolutionFactorInWater_
Definition: GenericOutputBlackoilModule.hpp:425
const EclipseState & eclState_
Definition: GenericOutputBlackoilModule.hpp:358
ScalarBuffer swmin_
Definition: GenericOutputBlackoilModule.hpp:421
ScalarBuffer rockCompPorvMultiplier_
Definition: GenericOutputBlackoilModule.hpp:429
RFTContainer< GetPropType< TypeTag, Properties::FluidSystem > > rftC_
Definition: GenericOutputBlackoilModule.hpp:453
ScalarBuffer dewPointPressure_
Definition: GenericOutputBlackoilModule.hpp:428
LogOutputHelper< Scalar > logOutput_
Definition: GenericOutputBlackoilModule.hpp:365
std::vector< int > failedCellsPb_
Definition: GenericOutputBlackoilModule.hpp:390
ScalarBuffer permFact_
Definition: GenericOutputBlackoilModule.hpp:414
ScalarBuffer rsw_
Definition: GenericOutputBlackoilModule.hpp:402
ScalarBuffer pcog_
Definition: GenericOutputBlackoilModule.hpp:436
std::optional< RegionPhasePoreVolAverage > regionAvgDensity_
Definition: GenericOutputBlackoilModule.hpp:464
std::array< ScalarBuffer, numPhases > invB_
Definition: GenericOutputBlackoilModule.hpp:442
ScalarBuffer pSalt_
Definition: GenericOutputBlackoilModule.hpp:413
ScalarBuffer cFoam_
Definition: GenericOutputBlackoilModule.hpp:411
ScalarBuffer bubblePointPressure_
Definition: GenericOutputBlackoilModule.hpp:427
ScalarBuffer temperature_
Definition: GenericOutputBlackoilModule.hpp:400
ScalarBuffer ppcw_
Definition: GenericOutputBlackoilModule.hpp:422
FIPContainer< GetPropType< TypeTag, Properties::FluidSystem > > fipC_
Definition: GenericOutputBlackoilModule.hpp:383
ScalarBuffer rockCompTransMultiplier_
Definition: GenericOutputBlackoilModule.hpp:432
MechContainer< Scalar > mech_
Definition: GenericOutputBlackoilModule.hpp:439
ScalarBuffer dynamicPoreVolume_
Definition: GenericOutputBlackoilModule.hpp:397
ScalarBuffer minimumOilPressure_
Definition: GenericOutputBlackoilModule.hpp:430
ScalarBuffer gasFormationVolumeFactor_
Definition: GenericOutputBlackoilModule.hpp:393
std::array< ScalarBuffer, numPhases > residual_
Definition: GenericOutputBlackoilModule.hpp:449
void doAllocBuffers(unsigned bufferSize, unsigned reportStepNum, const bool substep, const bool log, const bool isRestart, const EclHysteresisConfig *hysteresisConfig, unsigned numOutputNnc=0, std::map< std::string, int > rstKeywords={})
ScalarBuffer shmax_
Definition: GenericOutputBlackoilModule.hpp:419
BioeffectsContainer< Scalar > bioeffectsC_
Definition: GenericOutputBlackoilModule.hpp:433
const Schedule & schedule_
Definition: GenericOutputBlackoilModule.hpp:359
FlowsContainer< GetPropType< TypeTag, Properties::FluidSystem > > flowsC_
Definition: GenericOutputBlackoilModule.hpp:451
ExtboContainer< Scalar > extboC_
Definition: GenericOutputBlackoilModule.hpp:415
void setupExtraBlockData(const std::size_t reportStepNum, std::function< bool(int)> isCartIdxOnThisRank)
ScalarBuffer oilSaturationPressure_
Definition: GenericOutputBlackoilModule.hpp:406
InterRegFlowMap interRegionFlows_
Definition: GenericOutputBlackoilModule.hpp:364
ScalarBuffer pcgw_
Definition: GenericOutputBlackoilModule.hpp:434
ScalarBuffer cPolymer_
Definition: GenericOutputBlackoilModule.hpp:410
void setupBlockData(std::function< bool(int)> isCartIdxOnThisRank)
ScalarBuffer rvw_
Definition: GenericOutputBlackoilModule.hpp:404
std::array< ScalarBuffer, numPhases > saturation_
Definition: GenericOutputBlackoilModule.hpp:441
std::unordered_map< std::string, std::vector< int > > regions_
Definition: GenericOutputBlackoilModule.hpp:384
ScalarBuffer rPorV_
Definition: GenericOutputBlackoilModule.hpp:398
ScalarBuffer oilVaporizationFactor_
Definition: GenericOutputBlackoilModule.hpp:424
std::vector< int > failedCellsPd_
Definition: GenericOutputBlackoilModule.hpp:391
ScalarBuffer rs_
Definition: GenericOutputBlackoilModule.hpp:401
ScalarBuffer drsdtcon_
Definition: GenericOutputBlackoilModule.hpp:407
ScalarBuffer sSol_
Definition: GenericOutputBlackoilModule.hpp:408
std::map< std::pair< std::string, int >, double > extraBlockData_
Definition: GenericOutputBlackoilModule.hpp:459
ScalarBuffer pressureTimesPoreVolume_
Definition: GenericOutputBlackoilModule.hpp:395
ScalarBuffer gasDissolutionFactor_
Definition: GenericOutputBlackoilModule.hpp:423
std::array< ScalarBuffer, numPhases > viscosity_
Definition: GenericOutputBlackoilModule.hpp:444
bool forceDisableFipOutput_
Definition: GenericOutputBlackoilModule.hpp:379
ScalarBuffer soMax_
Definition: GenericOutputBlackoilModule.hpp:416
ScalarBuffer sgmax_
Definition: GenericOutputBlackoilModule.hpp:418
ScalarBuffer somin_
Definition: GenericOutputBlackoilModule.hpp:420
ScalarBuffer hydrocarbonPoreVolume_
Definition: GenericOutputBlackoilModule.hpp:394
const std::optional< Inplace > & initialInplace() const
Definition: GenericOutputBlackoilModule.hpp:246
ScalarBuffer waterVaporizationFactor_
Definition: GenericOutputBlackoilModule.hpp:426
ScalarBuffer cSalt_
Definition: GenericOutputBlackoilModule.hpp:412
TracerContainer< GetPropType< TypeTag, Properties::FluidSystem > > tracerC_
Definition: GenericOutputBlackoilModule.hpp:447
ScalarBuffer rv_
Definition: GenericOutputBlackoilModule.hpp:403
ScalarBuffer pcow_
Definition: GenericOutputBlackoilModule.hpp:435
ScalarBuffer swMax_
Definition: GenericOutputBlackoilModule.hpp:417
ScalarBuffer pressureTimesHydrocarbonVolume_
Definition: GenericOutputBlackoilModule.hpp:396
ScalarBuffer rswSol_
Definition: GenericOutputBlackoilModule.hpp:409
Inter-region flow accumulation maps for all region definition arrays.
Definition: InterRegFlows.hpp:179
void addConnection(const Cell &source, const Cell &destination, const data::InterRegFlowMap::FlowRates &rates)
void clear()
Clear all internal buffers, but preserve allocated capacity.
Output module for the results black oil model writing in ECL binary format.
Definition: OutputBlackoilModule.hpp:86
void processElement(const ElementContext &elemCtx)
Modify the internal buffers according to the intensive quanties relevant for an element.
Definition: OutputBlackoilModule.hpp:239
void initializeFluxData()
Prepare for capturing connection fluxes, particularly to account for inter-region flows.
Definition: OutputBlackoilModule.hpp:483
void setupExtractors(const bool isSubStep, const int reportStepNum)
Setup list of active element-level data extractors.
Definition: OutputBlackoilModule.hpp:220
void allocBuffers(const unsigned bufferSize, const unsigned reportStepNum, const bool substep, const bool log, const bool isRestart)
Allocate memory for the scalar fields we would like to write to ECL output files.
Definition: OutputBlackoilModule.hpp:198
void processFluxes(const ElementContext &elemCtx, ActiveIndex &&activeIndex, CartesianIndex &&cartesianIndex)
Capture connection fluxes, particularly to account for inter-region flows.
Definition: OutputBlackoilModule.hpp:446
void clearExtractors()
Clear list of active element-level data extractors.
Definition: OutputBlackoilModule.hpp:228
void outputFipAndResvLogToCSV(const std::size_t reportStepNum, const bool substep, const Parallel::Communication &comm)
Definition: OutputBlackoilModule.hpp:380
void assignToFluidState(FluidState &fs, unsigned elemIdx) const
Definition: OutputBlackoilModule.hpp:507
void initHysteresisParams(Simulator &simulator, unsigned elemIdx) const
Definition: OutputBlackoilModule.hpp:559
void updateFluidInPlace(const ElementContext &elemCtx)
Definition: OutputBlackoilModule.hpp:624
OutputBlackOilModule(const Simulator &simulator, const SummaryConfig &smryCfg, const CollectDataOnIORankType &collectOnIORank)
Definition: OutputBlackoilModule.hpp:134
void outputFipAndResvLog(const Inplace &inplace, const std::size_t reportStepNum, double elapsed, boost::posix_time::ptime currentDate, const bool substep, const Parallel::Communication &comm)
Definition: OutputBlackoilModule.hpp:329
const InterRegFlowMap & getInterRegFlows() const
Get read-only access to collection of inter-region flows.
Definition: OutputBlackoilModule.hpp:501
void processElementBlockData(const ElementContext &elemCtx)
Definition: OutputBlackoilModule.hpp:285
void finalizeFluxData()
Finalize capturing connection fluxes.
Definition: OutputBlackoilModule.hpp:493
void updateFluidInPlace(const unsigned globalDofIdx, const IntensiveQuantities &intQuants, const double totVolume)
Definition: OutputBlackoilModule.hpp:631
Declare the properties used by the infrastructure code of the finite volume discretizations.
Dune::Communication< MPIComm > Communication
Definition: ParallelCommunication.hpp:30
Phase
Phase indices for reservoir coupling, we currently only support black-oil phases (oil,...
Definition: ReservoirCoupling.hpp:141
Definition: blackoilbioeffectsmodules.hh:43
std::string moduleVersionName()
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
This file provides the infrastructure to retrieve run-time parameters.
The Opm property system, traits with inheritance.
Minimal characteristics of a cell from a simulation grid.
Definition: InterRegFlows.hpp:50
Context passed to element extractor functions.
Definition: OutputExtractor.hpp:206
Wrapping struct holding types used for block-level data extraction.
Definition: OutputExtractor.hpp:193
std::unordered_map< int, std::vector< Exec > > ExecMap
A map of extraction executors, keyed by cartesian cell index.
Definition: OutputExtractor.hpp:260
std::variant< ScalarEntry, PhaseEntry > Entry
Descriptor for extractors.
Definition: OutputExtractor.hpp:245
static ExecMap setupExecMap(std::map< std::pair< std::string, int >, double > &blockData, const std::array< Entry, size > &handlers)
Setup an extractor executor map from a map of evaluations to perform.
Definition: OutputExtractor.hpp:264
static void process(const std::vector< Exec > &blockExtractors, const Context &ectx)
Process a list of block extractors.
Definition: OutputExtractor.hpp:367
Context passed to extractor functions.
Definition: OutputExtractor.hpp:74
int episodeIndex
Current report step.
Definition: OutputExtractor.hpp:77
Struct holding hysteresis parameters.
Definition: OutputExtractor.hpp:63
Scalar somin
Min oil saturation.
Definition: OutputExtractor.hpp:69
Scalar swmin
Min water saturation.
Definition: OutputExtractor.hpp:66
Scalar swmax
Max water saturation.
Definition: OutputExtractor.hpp:65
Scalar shmax
Max something.
Definition: OutputExtractor.hpp:68
Scalar sgmax
Max gas saturation.
Definition: OutputExtractor.hpp:67
Scalar somax
Max oil saturation.
Definition: OutputExtractor.hpp:64
Wrapping struct holding types used for element-level data extraction.
Definition: OutputExtractor.hpp:54
static void process(const Context &ectx, const std::vector< Entry > &extractors)
Process the given extractor entries.
Definition: OutputExtractor.hpp:158
static std::vector< Entry > removeInactive(std::array< Entry, size > &input)
Obtain vector of active extractors from an array of extractors.
Definition: OutputExtractor.hpp:120