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
32#include <opm/grid/CpGrid.hpp>
33#include <opm/grid/LookUpData.hh>
34
36
37#include <opm/common/Exceptions.hpp>
38#include <opm/common/TimingMacros.hpp>
39#include <opm/common/OpmLog/OpmLog.hpp>
40#include <opm/common/utility/Visitor.hpp>
41
42#include <opm/input/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp>
43
44#include <opm/material/common/Valgrind.hpp>
45#include <opm/material/fluidmatrixinteractions/EclEpsScalingPoints.hpp>
46#include <opm/material/fluidstates/BlackOilFluidState.hpp>
47#include <opm/material/fluidsystems/BlackOilFluidSystem.hpp>
48
53
54#include <opm/output/data/Cells.hpp>
55#include <opm/output/data/RegionVariableMapping.hpp>
56
57#include <opm/output/eclipse/EclipseIO.hpp>
58#include <opm/output/eclipse/Inplace.hpp>
59#include <opm/output/eclipse/RegionVariableCollection.hpp>
60
65
66#include <algorithm>
67#include <array>
68#include <cassert>
69#include <cstddef>
70#include <functional>
71#include <limits>
72#include <map>
73#include <set>
74#include <span>
75#include <stdexcept>
76#include <string>
77#include <type_traits>
78#include <utility>
79#include <vector>
80
81namespace Opm {
82
83// forward declaration
84template <class TypeTag>
85class EcfvDiscretization;
86
87namespace detail {
89 template <typename... T>
90 constexpr void ignoreUnused(T&&...) noexcept {}
91}
92
99template <class TypeTag>
100class OutputBlackOilModule : public GenericOutputModule<GetPropType<TypeTag, Properties::FluidSystem>>
101{
111 using FluidState = typename IntensiveQuantities::FluidState;
113 using Element = typename GridView::template Codim<0>::Entity;
114 using ElementIterator = typename GridView::template Codim<0>::Iterator;
117 using Dir = FaceDir::DirEnum;
123
124 static constexpr int conti0EqIdx = Indices::conti0EqIdx;
125 static constexpr int numPhases = FluidSystem::numPhases;
126 static constexpr int oilPhaseIdx = FluidSystem::oilPhaseIdx;
127 static constexpr int gasPhaseIdx = FluidSystem::gasPhaseIdx;
128 static constexpr int waterPhaseIdx = FluidSystem::waterPhaseIdx;
129 static constexpr int gasCompIdx = FluidSystem::gasCompIdx;
130 static constexpr int oilCompIdx = FluidSystem::oilCompIdx;
131 static constexpr int waterCompIdx = FluidSystem::waterCompIdx;
132 static constexpr EnergyModules energyModuleType = getPropValue<TypeTag, Properties::EnergyModuleType>();
133 static constexpr bool enableBioeffects = getPropValue<TypeTag, Properties::EnableBioeffects>();
134 static constexpr bool enableExtbo = getPropValue<TypeTag, Properties::EnableExtbo>();
135 static constexpr bool enableFoam = getPropValue<TypeTag, Properties::EnableFoam>();
136 static constexpr bool enablePolymer = getPropValue<TypeTag, Properties::EnablePolymer>();
137 static constexpr bool enableSolvent = getPropValue<TypeTag, Properties::EnableSolvent>();
138 enum { enableMICP = Indices::enableMICP };
139 enum { enableVapwat = getPropValue<TypeTag, Properties::EnableVapwat>() };
140 enum { enableDisgasInWater = getPropValue<TypeTag, Properties::EnableDisgasInWater>() };
141 static constexpr bool enableDissolvedGas =
142 Indices::compositionSwitchIdx != std::numeric_limits<unsigned>::max();
143
144 template<class VectorType>
145 static Scalar value_or_zero(int idx, const VectorType& v)
146 {
147 if (idx == -1) {
148 return 0.0;
149 }
150 return v.empty() ? 0.0 : v[idx];
151 }
152
153public:
154 OutputBlackOilModule(const Simulator& simulator,
155 const SummaryConfig& smryCfg,
156 const CollectDataOnIORankType& collectOnIORank)
157 : BaseType(simulator.vanguard().eclState(),
158 simulator.vanguard().schedule(),
159 smryCfg,
160 simulator.vanguard().summaryState(),
162 [this](const int idx)
163 { return simulator_.problem().eclWriter().collectOnIORank().localIdxToGlobalIdx(idx); },
164 [&collectOnIORank](const int idx)
165 { return collectOnIORank.isCartIdxOnThisRank(idx); },
166 simulator.vanguard().grid().comm(),
167 energyModuleType == EnergyModules::FullyImplicitThermal ||
168 energyModuleType == EnergyModules::SequentialImplicitThermal,
169 energyModuleType == EnergyModules::ConstantTemperature,
170 getPropValue<TypeTag, Properties::EnableMech>(),
171 getPropValue<TypeTag, Properties::EnableSolvent>(),
172 getPropValue<TypeTag, Properties::EnablePolymer>(),
173 getPropValue<TypeTag, Properties::EnableFoam>(),
174 getPropValue<TypeTag, Properties::EnableBrine>(),
175 getPropValue<TypeTag, Properties::EnableSaltPrecipitation>(),
176 getPropValue<TypeTag, Properties::EnableExtbo>(),
177 getPropValue<TypeTag, Properties::EnableBioeffects>(),
178 getPropValue<TypeTag, Properties::EnableGeochemistry>())
179 , simulator_(simulator)
180 , collectOnIORank_(collectOnIORank)
181 {
182 // The region arrays arrive on the (unrefined) input grid, but everything
183 // downstream indexes them by leaf cell. With an LGR the leaf has more
184 // cells, so map each onto it here - a refined cell inherits its parent's
185 // region - before anything looks at them. Identity without LGRs.
186 this->mapRegionsOntoLeaf_();
187
188 for (auto& region_pair : this->regions_) {
189 this->createLocalRegion_(region_pair.second);
190 }
191
192 auto isCartIdxOnThisRank = [&collectOnIORank](const int idx) {
193 return collectOnIORank.isCartIdxOnThisRank(idx);
194 };
195
196 this->setupBlockData(isCartIdxOnThisRank);
197
198 // Allocate slots for LB* summary nodes that name a cell inside an LGR.
199 // Runs per-rank (serial and parallel): each rank allocates only the LGR
200 // leaf cells it owns, so lgrBlockData_ is per-rank disjoint and the
201 // gather to the I/O rank is a clean union. Ownership is taken from the
202 // same InteriorEntity leaf-level decision the fill walk uses, collected
203 // here once. Empty for runs without LB* requests (zero non-LGR cost);
204 // constexpr-elided on non-CpGrid builds (LGRs are CpGrid-only).
205 std::set<std::pair<int, int>> ownedLgrCells;
206 std::map<std::string, int> lgrNameToLevel;
207 if constexpr (std::is_same_v<Grid, Dune::CpGrid>) {
208 // CpGrid name->level map is replicated on every rank (unlike the
209 // root-only EclipseState input grid).
210 lgrNameToLevel = simulator.vanguard().grid().getLgrNameToLevel();
211 for (const auto& element : elements(simulator.gridView())) {
212 const int level = element.level();
213 if (level > 0 && element.partitionType() == Dune::InteriorEntity) {
214 const int levelCompressed = element.getLevelElem().index();
215 const int levelCart = simulator.vanguard()
216 .levelCartesianIndexMapper()
217 .cartesianIndex(levelCompressed, level);
218 ownedLgrCells.emplace(level, levelCart);
219 }
220 }
221 }
222 this->setupLgrBlockData(
223 lgrNameToLevel,
224 [&ownedLgrCells](const int level, const int levelCart) {
225 return ownedLgrCells.count(std::make_pair(level, levelCart)) > 0;
226 });
227
228 if (! Parameters::Get<Parameters::OwnerCellsFirst>()) {
229 const std::string msg = "The output code does not support --owner-cells-first=false.";
230 if (collectOnIORank.isIORank()) {
231 OpmLog::error(msg);
232 }
233 OPM_THROW_NOLOG(std::runtime_error, msg);
234 }
235
236 if (smryCfg.match("[FB]PP[OGW]") || smryCfg.match("RPP[OGW]*")) {
237 auto rset = this->eclState_.fieldProps().fip_regions();
238 rset.push_back("PVTNUM");
239
240 // RegionPhasePoreVolAverage indexes by leaf cell. The FIP entries of
241 // regions_ are already on the leaf (mapRegionsOntoLeaf_ does that), so
242 // serve those directly. PVTNUM is not an FIP region and is deliberately
243 // kept out of regions_, so it needs its own leaf-mapped copy - with LGRs
244 // a refined cell inherits its parent's PVTNUM; identity without.
245 // decltype(auto) keeps the required "reference to vector" semantics.
246 const LookUpData<Grid, GridView> lookUpData(this->simulator_.gridView());
247 auto pvtnum = lookUpData.template assignFieldPropsIntOnLeaf<int>(
248 this->eclState_.fieldProps(), "PVTNUM", /*needsTranslation=*/false);
249
251 .emplace(this->simulator_.gridView().comm(),
252 FluidSystem::numPhases, rset,
253 [&regions = std::as_const(this->regions_),
254 pvtnum = std::move(pvtnum)]
255 (const std::string& rsetName) -> decltype(auto)
256 {
257 return (rsetName == "PVTNUM")
258 ? static_cast<const std::vector<int>&>(pvtnum)
259 : static_cast<const std::vector<int>&>(regions.at(rsetName));
260 });
261 }
262 }
263
268 void
269 allocBuffers(const unsigned bufferSize,
270 const unsigned reportStepNum,
271 const bool substep,
272 const bool log,
273 const bool forceRestartFieldAllocation)
274 {
275 if (! std::is_same<Discretization, EcfvDiscretization<TypeTag>>::value) {
276 return;
277 }
278
279 const auto& problem = this->simulator_.problem();
280
281 this->doAllocBuffers(bufferSize,
282 reportStepNum,
283 substep,
284 log,
285 forceRestartFieldAllocation,
286 &problem.materialLawManager().hysteresisConfig(),
287 problem.eclWriter().getOutputNnc().front().size());
288 }
289
291 void setupExtractors(const bool isSubStep,
292 const int reportStepNum)
293 {
294 this->setupElementExtractors_();
295 this->setupBlockExtractors_(isSubStep, reportStepNum);
296 }
297
300 {
301 this->extractors_.clear();
302 this->blockExtractors_.clear();
303 this->extraBlockExtractors_.clear();
304 this->lgrBlockExtractors_.clear();
305 }
306
310 void assignToSolution(data::Solution& sol) override
311 {
313
314 // Quantities that only the black-oil formulation reports.
315 using M = UnitSystem::measure;
316
317 this->assignBuffer(sol, "BO", M::oil_formation_volume_factor,
318 this->formationVolFactor_[oilPhaseIdx], oilPhaseIdx);
319 this->assignBuffer(sol, "1OVERBO", M::oil_inverse_formation_volume_factor,
320 this->invB_[oilPhaseIdx], oilPhaseIdx);
321
322 this->assignBuffer(sol, "BG", M::gas_formation_volume_factor,
323 this->formationVolFactor_[gasPhaseIdx], gasPhaseIdx);
324 this->assignBuffer(sol, "1OVERBG", M::gas_inverse_formation_volume_factor,
325 this->invB_[gasPhaseIdx], gasPhaseIdx);
326
327 this->assignBuffer(sol, "OILKR", M::identity,
328 this->relativePermeability_[oilPhaseIdx], oilPhaseIdx);
329 this->assignBuffer(sol, "GASKR", M::identity,
330 this->relativePermeability_[gasPhaseIdx], gasPhaseIdx);
331 this->assignBuffer(sol, "PBUB", M::pressure, this->bubblePointPressure_);
332 this->assignBuffer(sol, "PDEW", M::pressure, this->dewPointPressure_);
333 this->assignBuffer(sol, "RS", M::gas_oil_ratio, this->rs_);
334 this->assignBuffer(sol, "RV", M::oil_gas_ratio, this->rv_);
335
336 // avoid output with generic fluid system and disabled water phase
337 if constexpr (numPhases > 2) {
338 this->assignBuffer(sol, "BW", M::water_formation_volume_factor,
339 this->formationVolFactor_[waterPhaseIdx], waterPhaseIdx);
340 this->assignBuffer(sol, "1OVERBW", M::water_inverse_formation_volume_factor,
341 this->invB_[waterPhaseIdx], waterPhaseIdx);
342
343 this->assignBuffer(sol, "WATKR", M::identity,
344 this->relativePermeability_[waterPhaseIdx], waterPhaseIdx);
345 }
346
347 // The phase densities and viscosities are named by the emitting
348 // module: these are the black-oil array names.
350 .oilDensity = "OIL_DEN",
351 .gasDensity = "GAS_DEN",
352 .waterDensity = "WAT_DEN",
353 .oilViscosity = "OIL_VISC",
354 .gasViscosity = "GAS_VISC",
355 .waterViscosity = "WAT_VISC",
356 });
357 }
358
363 void processElement(const ElementContext& elemCtx)
364 {
365 OPM_TIMEBLOCK_LOCAL(processElement, Subsystem::Output);
366 if (!std::is_same<Discretization, EcfvDiscretization<TypeTag>>::value) {
367 return;
368 }
369
370 if (this->extractors_.empty()) {
371 assert(0);
372 }
373
374 const auto& matLawManager = simulator_.problem().materialLawManager();
375
376 for (unsigned dofIdx = 0; dofIdx < elemCtx.numPrimaryDof(/*timeIdx=*/0); ++dofIdx) {
377 const auto& intQuants = elemCtx.intensiveQuantities(dofIdx, /*timeIdx=*/0);
378 const auto& fs = intQuants.fluidState();
379 const auto globalDofIdx = elemCtx.globalSpaceIndex(dofIdx, /*timeIdx=*/0);
380
381 typename Extractor::HysteresisParams hysterParams;
382 if (matLawManager.enableHysteresis()) {
383 if (FluidSystem::phaseIsActive(oilPhaseIdx) && FluidSystem::phaseIsActive(waterPhaseIdx)) {
384 matLawManager.oilWaterHysteresisParams(hysterParams.somax,
385 hysterParams.swmax,
386 hysterParams.swmin,
387 globalDofIdx);
388 }
389 if (FluidSystem::phaseIsActive(oilPhaseIdx) && FluidSystem::phaseIsActive(gasPhaseIdx)) {
390 matLawManager.gasOilHysteresisParams(hysterParams.sgmax,
391 hysterParams.shmax,
392 hysterParams.somin,
393 globalDofIdx);
394 }
395 }
396
397 const typename Extractor::Context ectx{
398 globalDofIdx,
399 elemCtx.primaryVars(dofIdx, /*timeIdx=*/0).pvtRegionIndex(),
400 elemCtx.simulator().episodeIndex(),
401 fs,
402 intQuants,
403 hysterParams
404 };
405
406 Extractor::process(ectx, extractors_);
407 }
408 }
409
410 void processElementBlockData(const ElementContext& elemCtx)
411 {
412 OPM_TIMEBLOCK_LOCAL(processElementBlockData, Subsystem::Output);
413 if (!std::is_same<Discretization, EcfvDiscretization<TypeTag>>::value) {
414 return;
415 }
416
417 if (this->blockExtractors_.empty() &&
418 this->extraBlockExtractors_.empty() &&
419 this->lgrBlockExtractors_.empty())
420 {
421 return;
422 }
423
424 // For EcfvDiscretization there is one degree of freedom per
425 // element, so the element's level determines whether the cell
426 // sits in the global grid (level == 0) or inside a local grid
427 // refinement (level > 0). Branching here keeps the global B*
428 // lookup off the LGR-cell path and vice versa; non-LGR runs
429 // never enter the LGR branch.
430 const auto& element = elemCtx.element();
431 const int level = element.level();
432
433 for (unsigned dofIdx = 0; dofIdx < elemCtx.numPrimaryDof(/*timeIdx=*/0); ++dofIdx) {
434 const auto globalDofIdx = elemCtx.globalSpaceIndex(dofIdx, /*timeIdx=*/0);
435
436 const std::vector<typename BlockExtractor::Exec>* be_extractors = nullptr;
437 const std::vector<typename BlockExtractor::Exec>* bee_extractors = nullptr;
438 const std::vector<typename BlockExtractor::Exec>* lgr_extractors = nullptr;
439
440 if (level == 0) {
441 const auto cartesianIdx = elemCtx.simulator().vanguard().cartesianIndex(globalDofIdx);
442 const auto be_it = this->blockExtractors_.find(cartesianIdx);
443 const auto bee_it = this->extraBlockExtractors_.find(cartesianIdx);
444 if (be_it != this->blockExtractors_.end()) { be_extractors = &be_it->second; }
445 if (bee_it != this->extraBlockExtractors_.end()) { bee_extractors = &bee_it->second; }
446 }
447 else if constexpr (std::is_same_v<Grid, Dune::CpGrid>) {
448 // Cells inside a local grid refinement. LGRs are a
449 // CpGrid-only feature today; ALU and Polyhedral grids
450 // keep element.level() at 0 and never enter this
451 // branch, but the constexpr if filters it out at
452 // compile time on those builds so the CpGrid-specific
453 // getLevelElem() call never has to be instantiated
454 // for an unsupported entity type. Ownership at the
455 // leaf level is decided by Dune::InteriorEntity; the
456 // global-Cartesian isCartIdxOnThisRank predicate used
457 // at setup time does not apply to LGR cells (they do
458 // not inhabit the global Cartesian space).
459 const auto level_it = this->lgrBlockExtractors_.find(level);
460 if (level_it != this->lgrBlockExtractors_.end() &&
461 element.partitionType() == Dune::InteriorEntity)
462 {
463 const int levelCompressed = element.getLevelElem().index();
464 const int levelCart = elemCtx.simulator().vanguard()
465 .levelCartesianIndexMapper()
466 .cartesianIndex(levelCompressed, level);
467 const auto cell_it = level_it->second.find(levelCart);
468 if (cell_it != level_it->second.end()) {
469 lgr_extractors = &cell_it->second;
470 }
471 }
472 }
473
474 if (be_extractors == nullptr &&
475 bee_extractors == nullptr &&
476 lgr_extractors == nullptr)
477 {
478 continue;
479 }
480
481 const auto& intQuants = elemCtx.intensiveQuantities(dofIdx, /*timeIdx=*/0);
482 const auto& fs = intQuants.fluidState();
483
484 const typename BlockExtractor::Context ectx{
485 globalDofIdx,
486 dofIdx,
487 fs,
488 intQuants,
489 elemCtx,
490 };
491
492 if (be_extractors != nullptr) { BlockExtractor::process(*be_extractors, ectx); }
493 if (bee_extractors != nullptr) { BlockExtractor::process(*bee_extractors, ectx); }
494 if (lgr_extractors != nullptr) { BlockExtractor::process(*lgr_extractors, ectx); }
495 }
496 }
497
498 void outputFipAndResvLog(const Inplace& inplace,
499 const std::size_t reportStepNum,
500 double elapsed,
501 boost::posix_time::ptime currentDate,
502 const bool substep,
503 const Parallel::Communication& comm)
504 {
505
506 if (comm.rank() != 0) {
507 return;
508 }
509
510 // For report step 0 we use the RPTSOL config, else derive from RPTSCHED
511 std::unique_ptr<FIPConfig> fipSched;
512 if (reportStepNum > 0) {
513 const auto& rpt = this->schedule_[reportStepNum-1].rpt_config.get();
514 fipSched = std::make_unique<FIPConfig>(rpt);
515 }
516 const FIPConfig& fipc = reportStepNum == 0 ? this->eclState_.getEclipseConfig().fip()
517 : *fipSched;
518
519 if (!substep && !this->forceDisableFipOutput_ && fipc.output(FIPConfig::OutputField::FIELD)) {
520
521 this->logOutput_.timeStamp("BALANCE", elapsed, reportStepNum, currentDate);
522
523 const auto& initial_inplace = *this->initialInplace();
524 this->logOutput_.fip(inplace, initial_inplace, "");
525
526 if (fipc.output(FIPConfig::OutputField::FIPNUM)) {
527 this->logOutput_.fip(inplace, initial_inplace, "FIPNUM");
528
529 if (fipc.output(FIPConfig::OutputField::RESV))
530 this->logOutput_.fipResv(inplace, "FIPNUM");
531 }
532
533 if (fipc.output(FIPConfig::OutputField::FIP)) {
534 for (const auto& reg : this->regions_) {
535 if (reg.first != "FIPNUM") {
536 std::ostringstream ss;
537 ss << "BAL" << reg.first.substr(3);
538 this->logOutput_.timeStamp(ss.str(), elapsed, reportStepNum, currentDate);
539 this->logOutput_.fip(inplace, initial_inplace, reg.first);
540
541 if (fipc.output(FIPConfig::OutputField::RESV))
542 this->logOutput_.fipResv(inplace, reg.first);
543 }
544 }
545 }
546 }
547 }
548
549 void outputFipAndResvLogToCSV(const std::size_t reportStepNum,
550 const bool substep,
551 const Parallel::Communication& comm)
552 {
553 if (comm.rank() != 0) {
554 return;
555 }
556
557 if ((reportStepNum == 0) && (!substep) &&
558 (this->schedule_.initialReportConfiguration().has_value()) &&
559 (this->schedule_.initialReportConfiguration()->contains("CSVFIP"))) {
560
561 std::ostringstream csv_stream;
562
563 this->logOutput_.csv_header(csv_stream);
564
565 const auto& initial_inplace = *this->initialInplace();
566
567 this->logOutput_.fip_csv(csv_stream, initial_inplace, "FIPNUM");
568
569 for (const auto& reg : this->regions_) {
570 if (reg.first != "FIPNUM") {
571 this->logOutput_.fip_csv(csv_stream, initial_inplace, reg.first);
572 }
573 }
574
575 const IOConfig& io = this->eclState_.getIOConfig();
576 auto csv_fname = io.getOutputDir() + "/" + io.getBaseName() + ".CSV";
577
578 std::ofstream outputFile(csv_fname);
579
580 outputFile << csv_stream.str();
581
582 outputFile.close();
583 }
584 }
585
614 template <class ActiveIndex, class CartesianIndex>
615 void processFluxes(const ElementContext& elemCtx,
616 ActiveIndex&& activeIndex,
617 CartesianIndex&& cartesianIndex)
618 {
619 OPM_TIMEBLOCK_LOCAL(processFluxes, Subsystem::Output);
620 const auto identifyCell = [&activeIndex, &cartesianIndex](const Element& elem)
622 {
623 const auto cellIndex = activeIndex(elem);
624
625 return {
626 static_cast<int>(cellIndex),
627 cartesianIndex(cellIndex),
628 elem.partitionType() == Dune::InteriorEntity
629 };
630 };
631
632 const auto timeIdx = 0u;
633 const auto& stencil = elemCtx.stencil(timeIdx);
634 const auto numInteriorFaces = elemCtx.numInteriorFaces(timeIdx);
635
636 for (auto scvfIdx = 0 * numInteriorFaces; scvfIdx < numInteriorFaces; ++scvfIdx) {
637 const auto& face = stencil.interiorFace(scvfIdx);
638 const auto left = identifyCell(stencil.element(face.interiorIndex()));
639 const auto right = identifyCell(stencil.element(face.exteriorIndex()));
640
641 const auto rates = this->
642 getComponentSurfaceRates(elemCtx, face.area(), scvfIdx, timeIdx);
643
644 this->interRegionFlows_.addConnection(left, right, rates);
645 }
646 }
647
653 {
654 // Inter-region flow rates. Note: ".clear()" prepares to accumulate
655 // contributions per bulk connection between FIP regions.
656 this->interRegionFlows_.clear();
657 }
658
663 {
665 }
666
671 {
672 return this->interRegionFlows_;
673 }
674
675 template <class FluidState>
676 void assignToFluidState(FluidState& fs, unsigned elemIdx) const
677 {
678 for (unsigned phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) {
679 if (this->saturation_[phaseIdx].empty())
680 continue;
681
682 fs.setSaturation(phaseIdx, this->saturation_[phaseIdx][elemIdx]);
683 }
684
685 if (!this->fluidPressure_.empty()) {
686 // this assumes that capillary pressures only depend on the phase saturations
687 // and possibly on temperature. (this is always the case for ECL problems.)
688 std::array<Scalar, numPhases> pc = {0};
689 const MaterialLawParams& matParams = simulator_.problem().materialLawParams(elemIdx);
690 MaterialLaw::capillaryPressures(pc, matParams, fs);
691 Valgrind::CheckDefined(this->fluidPressure_[elemIdx]);
692 Valgrind::CheckDefined(pc);
693 const auto& pressure = this->fluidPressure_[elemIdx];
694 for (unsigned phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) {
695 if (!FluidSystem::phaseIsActive(phaseIdx))
696 continue;
697
698 if (Indices::oilEnabled)
699 fs.setPressure(phaseIdx, pressure + (pc[phaseIdx] - pc[oilPhaseIdx]));
700 else if (Indices::gasEnabled)
701 fs.setPressure(phaseIdx, pressure + (pc[phaseIdx] - pc[gasPhaseIdx]));
702 else if (Indices::waterEnabled)
703 //single (water) phase
704 fs.setPressure(phaseIdx, pressure);
705 }
706 }
707
708 if constexpr (energyModuleType != EnergyModules::NoTemperature) {
709 if (!this->temperature_.empty())
710 fs.setTemperature(this->temperature_[elemIdx]);
711 }
712 if constexpr (enableDissolvedGas) {
713 if (!this->rs_.empty())
714 fs.setRs(this->rs_[elemIdx]);
715 if (!this->rv_.empty())
716 fs.setRv(this->rv_[elemIdx]);
717 }
718 if constexpr (enableDisgasInWater) {
719 if (!this->rsw_.empty())
720 fs.setRsw(this->rsw_[elemIdx]);
721 }
722 if constexpr (enableVapwat) {
723 if (!this->rvw_.empty())
724 fs.setRvw(this->rvw_[elemIdx]);
725 }
726 }
727
728 void initHysteresisParams(Simulator& simulator, unsigned elemIdx) const
729 {
730 if (!this->soMax_.empty())
731 simulator.problem().setMaxOilSaturation(elemIdx, this->soMax_[elemIdx]);
732
733 if (simulator.problem().materialLawManager().enableHysteresis()) {
734 auto& matLawManager = simulator.problem().materialLawManager();
735
736 if (FluidSystem::phaseIsActive(oilPhaseIdx)
737 && FluidSystem::phaseIsActive(waterPhaseIdx)) {
738 Scalar somax = 2.0;
739 Scalar swmax = -2.0;
740 Scalar swmin = 2.0;
741
742 if (matLawManager.enableNonWettingHysteresis()) {
743 if (!this->soMax_.empty()) {
744 somax = this->soMax_[elemIdx];
745 }
746 }
747 if (matLawManager.enableWettingHysteresis()) {
748 if (!this->swMax_.empty()) {
749 swmax = this->swMax_[elemIdx];
750 }
751 }
752 if (matLawManager.enablePCHysteresis()) {
753 if (!this->swmin_.empty()) {
754 swmin = this->swmin_[elemIdx];
755 }
756 }
757 matLawManager.setOilWaterHysteresisParams(
758 somax, swmax, swmin, elemIdx);
759 }
760 if (FluidSystem::phaseIsActive(oilPhaseIdx)
761 && FluidSystem::phaseIsActive(gasPhaseIdx)) {
762 Scalar sgmax = 2.0;
763 Scalar shmax = -2.0;
764 Scalar somin = 2.0;
765
766 if (matLawManager.enableNonWettingHysteresis()) {
767 if (!this->sgmax_.empty()) {
768 sgmax = this->sgmax_[elemIdx];
769 }
770 }
771 if (matLawManager.enableWettingHysteresis()) {
772 if (!this->shmax_.empty()) {
773 shmax = this->shmax_[elemIdx];
774 }
775 }
776 if (matLawManager.enablePCHysteresis()) {
777 if (!this->somin_.empty()) {
778 somin = this->somin_[elemIdx];
779 }
780 }
781 matLawManager.setGasOilHysteresisParams(
782 sgmax, shmax, somin, elemIdx);
783 }
784
785 }
786
787 if (simulator_.vanguard().eclState().fieldProps().has_double("SWATINIT")) {
788 simulator.problem().materialLawManager().applyRestartSwatInit(elemIdx, this->ppcw_[elemIdx]);
789 }
790 }
791
792 void updateFluidInPlace(const ElementContext& elemCtx)
793 {
794 for (unsigned dofIdx = 0; dofIdx < elemCtx.numPrimaryDof(/*timeIdx=*/0); ++dofIdx) {
795 updateFluidInPlace_(elemCtx, dofIdx);
796 }
797 }
798
799 void updateFluidInPlace(const unsigned globalDofIdx,
800 const IntensiveQuantities& intQuants,
801 const double totVolume)
802 {
803 this->updateFluidInPlace_(globalDofIdx, intQuants, totVolume);
804 }
805
807 void setRestart(const data::Solution& sol,
808 const unsigned elemIdx,
809 const unsigned globalDofIndex) override
810 {
811 BaseType::setRestart(sol, elemIdx, globalDofIndex);
812
813 auto assign = [&sol, elemIdx, globalDofIndex](const std::string& name,
814 ScalarBuffer& data)
815 {
816 if (!data.empty() && sol.has(name)) {
817 data[elemIdx] = sol.data<double>(name)[globalDofIndex];
818 }
819 };
820
821 assign("RS", rs_);
822 assign("RV", rv_);
823 }
824
825protected:
828 void allocFormulationBuffers(std::map<std::string, int>& rstKeywords,
829 const unsigned bufferSize) override
830 {
831 // RS and RV are allocated whenever the fluid system supports them,
832 // not only when the restart keyword asks for them.
833 BaseType::allocBufferIfRequested(rstKeywords, bufferSize, rs_, "RS",
834 FluidSystem::enableDissolvedGas(), true);
835 BaseType::allocBufferIfRequested(rstKeywords, bufferSize, rv_, "RV",
836 FluidSystem::enableVaporizedOil(), true);
837
838 // PBPD requests the bubble and the dew point pressure together.
839 if (BaseType::allocBufferIfRequested(rstKeywords, bufferSize,
840 bubblePointPressure_, "PBPD", true)) {
841 dewPointPressure_.resize(bufferSize, 0.0);
842 }
843
844 // "WOG" ordering, as used by the shared module's keyword naming.
845 constexpr auto phaseChar = std::string_view{"WOG"};
846 for (unsigned phase = 0; phase < numPhases; ++phase) {
847 if (!FluidSystem::phaseIsActive(phase)) {
848 continue;
849 }
850
851 // The black-oil phase ordering is the one the keyword names
852 // assume, so no reordering is needed here.
853 BaseType::allocBufferIfRequested(rstKeywords, bufferSize,
854 this->formationVolFactor_[phase],
855 std::string("B") + phaseChar[phase], true);
856
857 BaseType::allocBufferIfRequested(rstKeywords, bufferSize, this->invB_[phase],
858 std::string("1OVERB") + phaseChar[phase], true);
859
860 BaseType::allocBufferIfRequested(rstKeywords, bufferSize, relativePermeability_[phase],
861 std::string("KR") + phaseChar[phase], true);
862 }
863 }
864
865private:
866
867 // Buffers of the quantities only the black-oil formulation produces.
868 using ScalarBuffer = typename BaseType::ScalarBuffer;
869 ScalarBuffer rs_;
870 ScalarBuffer rv_;
871 ScalarBuffer bubblePointPressure_;
872 ScalarBuffer dewPointPressure_;
873 std::array<ScalarBuffer, numPhases> formationVolFactor_;
874 std::array<ScalarBuffer, numPhases> invB_;
875 std::array<ScalarBuffer, numPhases> relativePermeability_;
876 template <typename T>
877 using RemoveCVR = std::remove_cv_t<std::remove_reference_t<T>>;
878
879 template <typename, class = void>
880 struct HasGeoMech : public std::false_type {};
881
882 template <typename Problem>
883 struct HasGeoMech<
884 Problem, std::void_t<decltype(std::declval<Problem>().geoMechModel())>
885 > : public std::true_type {};
886
887 template <typename, class = void>
888 struct HasGeochemistry : public std::false_type {};
889
890 template <typename Problem>
891 struct HasGeochemistry<
892 Problem, std::void_t<decltype(std::declval<Problem>().geochemistryModel())>
893 > : public std::true_type {};
894
895 bool isDefunctParallelWell(const std::string& wname) const override
896 {
897 if (simulator_.gridView().comm().size() == 1)
898 return false;
899 const auto& parallelWells = simulator_.vanguard().parallelWells();
900 std::pair<std::string, bool> value {wname, true};
901 auto candidate = std::lower_bound(parallelWells.begin(), parallelWells.end(), value);
902 return candidate == parallelWells.end() || *candidate != value;
903 }
904
905 bool isOwnedByCurrentRank(const std::string& wname) const override
906 {
907 return this->simulator_.problem().wellModel().isOwner(wname);
908 }
909
910 bool isOnCurrentRank(const std::string& wname) const override
911 {
912 return this->simulator_.problem().wellModel().hasLocalCells(wname);
913 }
914
915 void updateFluidInPlace_(const ElementContext& elemCtx, const unsigned dofIdx)
916 {
917 const auto& intQuants = elemCtx.intensiveQuantities(dofIdx, /*timeIdx=*/0);
918 const unsigned globalDofIdx = elemCtx.globalSpaceIndex(dofIdx, /*timeIdx=*/0);
919 const auto totVolume = elemCtx.simulator().model().dofTotalVolume(globalDofIdx);
920
921 this->updateFluidInPlace_(globalDofIdx, intQuants, totVolume);
922 }
923
924 void updateFluidInPlace_(const unsigned globalDofIdx,
925 const IntensiveQuantities& intQuants,
926 const double totVolume)
927 {
928 OPM_TIMEBLOCK_LOCAL(updateFluidInPlace, Subsystem::Output);
929
930 this->updateTotalVolumesAndPressures_(globalDofIdx,
931 intQuants,
932 totVolume,
933 intQuants.referencePorosity());
934
935 if (this->computeFip_) {
936 this->updatePhaseInplaceVolumes_(globalDofIdx, intQuants, totVolume);
937 }
938 }
939
944 void mapRegionsOntoLeaf_()
945 {
946 const LookUpData<Grid, GridView> lookUpData(simulator_.gridView());
947 const auto numLeaf = simulator_.gridView().size(0);
948
949 std::vector<int> onLeaf(numLeaf, 0);
950 for (auto& [name, region] : this->regions_) {
951 std::size_t elemIdx = 0;
952 for (const auto& elem : elements(simulator_.gridView())) {
953 onLeaf[elemIdx++] = lookUpData(elem, region);
954 }
955
956 region = onLeaf;
957 }
958 }
959
960 void createLocalRegion_(std::vector<int>& region)
961 {
962 std::size_t elemIdx = 0;
963 for (const auto& elem : elements(simulator_.gridView())) {
964 if (elem.partitionType() != Dune::InteriorEntity) {
965 region[elemIdx] = 0;
966 }
967
968 ++elemIdx;
969 }
970 }
971
972 template <typename FluidState>
973 void aggregateAverageDensityContributions_(const FluidState& fs,
974 const unsigned int globalDofIdx,
975 const double porv)
976 {
977 auto pvCellValue = RegionPhasePoreVolAverage::CellValue{};
978 pvCellValue.porv = porv;
979
980 for (auto phaseIdx = 0*FluidSystem::numPhases;
981 phaseIdx < FluidSystem::numPhases; ++phaseIdx)
982 {
983 if (! FluidSystem::phaseIsActive(phaseIdx)) {
984 continue;
985 }
986
987 pvCellValue.value = getValue(fs.density(phaseIdx));
988 pvCellValue.sat = getValue(fs.saturation(phaseIdx));
989
991 ->addCell(globalDofIdx,
993 pvCellValue);
994 }
995 }
996
1012 data::InterRegFlowMap::FlowRates
1013 getComponentSurfaceRates(const ElementContext& elemCtx,
1014 const Scalar faceArea,
1015 const std::size_t scvfIdx,
1016 const std::size_t timeIdx) const
1017 {
1018 using Component = data::InterRegFlowMap::Component;
1019
1020 auto rates = data::InterRegFlowMap::FlowRates {};
1021
1022 const auto& extQuant = elemCtx.extensiveQuantities(scvfIdx, timeIdx);
1023
1024 const auto alpha = getValue(extQuant.extrusionFactor()) * faceArea;
1025
1026 if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
1027 const auto& up = elemCtx
1028 .intensiveQuantities(extQuant.upstreamIndex(oilPhaseIdx), timeIdx);
1029
1030 const auto pvtReg = up.pvtRegionIndex();
1031
1032 const auto bO = getValue(getInvB_<FluidSystem, FluidState, Scalar>
1033 (up.fluidState(), oilPhaseIdx, pvtReg));
1034
1035 const auto qO = alpha * bO * getValue(extQuant.volumeFlux(oilPhaseIdx));
1036
1037 rates[Component::Oil] += qO;
1038
1039 if (FluidSystem::phaseIsActive(gasPhaseIdx)) {
1040 const auto Rs = getValue(
1041 BlackOil::getRs_<FluidSystem, FluidState, Scalar>
1042 (up.fluidState(), pvtReg));
1043
1044 rates[Component::Gas] += qO * Rs;
1045 rates[Component::Disgas] += qO * Rs;
1046 }
1047 }
1048
1049 if (FluidSystem::phaseIsActive(gasPhaseIdx)) {
1050 const auto& up = elemCtx
1051 .intensiveQuantities(extQuant.upstreamIndex(gasPhaseIdx), timeIdx);
1052
1053 const auto pvtReg = up.pvtRegionIndex();
1054
1055 const auto bG = getValue(getInvB_<FluidSystem, FluidState, Scalar>
1056 (up.fluidState(), gasPhaseIdx, pvtReg));
1057
1058 const auto qG = alpha * bG * getValue(extQuant.volumeFlux(gasPhaseIdx));
1059
1060 rates[Component::Gas] += qG;
1061
1062 if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
1063 const auto Rv = getValue(
1064 BlackOil::getRv_<FluidSystem, FluidState, Scalar>
1065 (up.fluidState(), pvtReg));
1066
1067 rates[Component::Oil] += qG * Rv;
1068 rates[Component::Vapoil] += qG * Rv;
1069 }
1070 }
1071
1072 if (FluidSystem::phaseIsActive(waterPhaseIdx)) {
1073 const auto& up = elemCtx
1074 .intensiveQuantities(extQuant.upstreamIndex(waterPhaseIdx), timeIdx);
1075
1076 const auto pvtReg = up.pvtRegionIndex();
1077
1078 const auto bW = getValue(getInvB_<FluidSystem, FluidState, Scalar>
1079 (up.fluidState(), waterPhaseIdx, pvtReg));
1080
1081 rates[Component::Water] +=
1082 alpha * bW * getValue(extQuant.volumeFlux(waterPhaseIdx));
1083 }
1084
1085 return rates;
1086 }
1087
1088 void updatePhaseInplaceVolumes_(const unsigned globalDofIdx,
1089 const IntensiveQuantities& intQuants,
1090 const double totVolume)
1091 {
1092 std::array<Scalar, FluidSystem::numPhases> fip {};
1093 std::array<Scalar, FluidSystem::numPhases> fipr{}; // at reservoir condition
1094
1095 const auto& fs = intQuants.fluidState();
1096 const auto pv = totVolume * intQuants.porosity().value();
1097
1098 for (unsigned phaseIdx = 0; phaseIdx < FluidSystem::numPhases; ++phaseIdx) {
1099 if (!FluidSystem::phaseIsActive(phaseIdx)) {
1100 continue;
1101 }
1102
1103 const auto b = getValue(fs.invB(phaseIdx));
1104 const auto s = getValue(fs.saturation(phaseIdx));
1105
1106 fipr[phaseIdx] = s * pv;
1107 fip [phaseIdx] = b * fipr[phaseIdx];
1108 }
1109
1110 this->fipC_.assignVolumesSurface(globalDofIdx, fip);
1111 this->fipC_.assignVolumesReservoir(globalDofIdx,
1112 fs.saltConcentration().value(),
1113 fipr);
1114
1115 if (FluidSystem::phaseIsActive(oilPhaseIdx) &&
1116 FluidSystem::phaseIsActive(gasPhaseIdx))
1117 {
1118 this->updateOilGasDistribution(globalDofIdx, fs, fip);
1119 }
1120
1121 if (FluidSystem::phaseIsActive(waterPhaseIdx) &&
1122 FluidSystem::phaseIsActive(gasPhaseIdx))
1123 {
1124 this->updateGasWaterDistribution(globalDofIdx, fs, fip);
1125 }
1126
1127 if (FluidSystem::phaseIsActive(gasPhaseIdx) &&
1128 this->fipC_.hasCo2InGas())
1129 {
1130 this->updateCO2InGas(globalDofIdx, pv, intQuants);
1131 }
1132
1133 if (this->fipC_.hasCo2InWater() &&
1134 (FluidSystem::phaseIsActive(waterPhaseIdx) ||
1135 FluidSystem::phaseIsActive(oilPhaseIdx)))
1136 {
1137 this->updateCO2InWater(globalDofIdx, pv, fs);
1138 }
1139
1140 if constexpr(enableBioeffects) {
1141 const auto surfVolWat = pv * getValue(fs.saturation(waterPhaseIdx)) *
1142 getValue(fs.invB(waterPhaseIdx));
1143 if (this->fipC_.hasMicrobialMass()) {
1144 this->updateMicrobialMass(globalDofIdx, intQuants, surfVolWat);
1145 }
1146 if (this->fipC_.hasBiofilmMass()) {
1147 this->updateBiofilmMass(globalDofIdx, intQuants, totVolume);
1148 }
1149 if constexpr(enableMICP) {
1150 if (this->fipC_.hasOxygenMass()) {
1151 this->updateOxygenMass(globalDofIdx, intQuants, surfVolWat);
1152 }
1153 if (this->fipC_.hasUreaMass()) {
1154 this->updateUreaMass(globalDofIdx, intQuants, surfVolWat);
1155 }
1156 if (this->fipC_.hasCalciteMass()) {
1157 this->updateCalciteMass(globalDofIdx, intQuants, totVolume);
1158 }
1159 }
1160 }
1161
1162 if (this->fipC_.hasWaterMass() && FluidSystem::phaseIsActive(waterPhaseIdx))
1163 {
1164 this->updateWaterMass(globalDofIdx, fs, fip);
1165 }
1166 }
1167
1168 template <typename FluidState, typename FIPArray>
1169 void updateOilGasDistribution(const unsigned globalDofIdx,
1170 const FluidState& fs,
1171 const FIPArray& fip)
1172 {
1173 // Gas dissolved in oil and vaporized oil
1174 const auto gasInPlaceLiquid = getValue(fs.Rs()) * fip[oilPhaseIdx];
1175 const auto oilInPlaceGas = getValue(fs.Rv()) * fip[gasPhaseIdx];
1176
1177 this->fipC_.assignOilGasDistribution(globalDofIdx, gasInPlaceLiquid, oilInPlaceGas);
1178 }
1179
1180 template <typename FluidState, typename FIPArray>
1181 void updateGasWaterDistribution(const unsigned globalDofIdx,
1182 const FluidState& fs,
1183 const FIPArray& fip)
1184 {
1185 // Gas dissolved in water and vaporized water
1186 const auto gasInPlaceWater = getValue(fs.Rsw()) * fip[waterPhaseIdx];
1187 const auto waterInPlaceGas = getValue(fs.Rvw()) * fip[gasPhaseIdx];
1188
1189 this->fipC_.assignGasWater(globalDofIdx, fip, gasInPlaceWater, waterInPlaceGas);
1190 }
1191
1192 template <typename IntensiveQuantities>
1193 void updateCO2InGas(const unsigned globalDofIdx,
1194 const double pv,
1195 const IntensiveQuantities& intQuants)
1196 {
1197 const auto& materialLawManager = this->simulator_.problem().materialLawManager();
1198 const auto& scaledDrainageInfo = materialLawManager.oilWaterScaledEpsInfoDrainage(globalDofIdx);
1199
1200 const auto& fs = intQuants.fluidState();
1201 Scalar sgcr = scaledDrainageInfo.Sgcr;
1202 if (materialLawManager.enableHysteresis()) {
1203 const auto& matParams = simulator_.problem().materialLawParams(globalDofIdx);
1204 sgcr = MaterialLaw::trappedGasSaturation(matParams, /*maximumTrapping*/false);
1205 }
1206
1207 Scalar trappedGasSaturation = scaledDrainageInfo.Sgcr;
1208 if (this->fipC_.has(Inplace::Phase::CO2MassInGasPhaseMaximumTrapped) ||
1209 this->fipC_.has(Inplace::Phase::CO2MassInGasPhaseMaximumUnTrapped))
1210 {
1211 if (materialLawManager.enableHysteresis()) {
1212 const auto& matParams = simulator_.problem().materialLawParams(globalDofIdx);
1213 // Get the maximum trapped gas saturation
1214 trappedGasSaturation = MaterialLaw::trappedGasSaturation(matParams, /*maximumTrapping*/true);
1215 }
1216 }
1217
1218 const Scalar sg = getValue(fs.saturation(gasPhaseIdx));
1219 Scalar strandedGasSaturation = scaledDrainageInfo.Sgcr;
1220 if (this->fipC_.has(Inplace::Phase::CO2MassInGasPhaseEffectiveTrapped) ||
1221 this->fipC_.has(Inplace::Phase::CO2MassInGasPhaseEffectiveUnTrapped))
1222 {
1223 if (materialLawManager.enableHysteresis()) {
1224 const auto& matParams = simulator_.problem().materialLawParams(globalDofIdx);
1225 const double krg = getValue(intQuants.relativePermeability(gasPhaseIdx));
1226 strandedGasSaturation = MaterialLaw::strandedGasSaturation(matParams, sg, krg);
1227 }
1228 }
1229
1230 const typename FIPContainer<FluidSystem>::Co2InGasInput v{
1231 pv,
1232 sg,
1233 sgcr,
1234 getValue(fs.density(gasPhaseIdx)),
1235 FluidSystem::phaseIsActive(waterPhaseIdx)
1236 ? FluidSystem::convertRvwToXgW(getValue(fs.Rvw()), fs.pvtRegionIndex())
1237 : FluidSystem::convertRvToXgO(getValue(fs.Rv()), fs.pvtRegionIndex()),
1238 FluidSystem::molarMass(gasCompIdx, fs.pvtRegionIndex()),
1239 trappedGasSaturation,
1240 strandedGasSaturation,
1241 };
1242
1243 this->fipC_.assignCo2InGas(globalDofIdx, v);
1244 }
1245
1246 template <typename FluidState>
1247 void updateCO2InWater(const unsigned globalDofIdx,
1248 const double pv,
1249 const FluidState& fs)
1250 {
1251 const auto co2InWater = FluidSystem::phaseIsActive(oilPhaseIdx)
1252 ? this->co2InWaterFromOil(fs, pv)
1253 : this->co2InWaterFromWater(fs, pv);
1254
1255 const Scalar mM = FluidSystem::molarMass(gasCompIdx, fs.pvtRegionIndex());
1256
1257 this->fipC_.assignCo2InWater(globalDofIdx, co2InWater, mM);
1258 }
1259
1260 template <typename FluidState>
1261 Scalar co2InWaterFromWater(const FluidState& fs, const double pv) const
1262 {
1263 const double rhow = getValue(fs.density(waterPhaseIdx));
1264 const double sw = getValue(fs.saturation(waterPhaseIdx));
1265 const double xwG = FluidSystem::convertRswToXwG(getValue(fs.Rsw()), fs.pvtRegionIndex());
1266
1267 const Scalar mM = FluidSystem::molarMass(gasCompIdx, fs.pvtRegionIndex());
1268
1269 return xwG * pv * rhow * sw / mM;
1270 }
1271
1272 template <typename FluidState>
1273 Scalar co2InWaterFromOil(const FluidState& fs, const double pv) const
1274 {
1275 const double rhoo = getValue(fs.density(oilPhaseIdx));
1276 const double so = getValue(fs.saturation(oilPhaseIdx));
1277 const double xoG = FluidSystem::convertRsToXoG(getValue(fs.Rs()), fs.pvtRegionIndex());
1278
1279 const Scalar mM = FluidSystem::molarMass(gasCompIdx, fs.pvtRegionIndex());
1280
1281 return xoG * pv * rhoo * so / mM;
1282 }
1283
1284 template <typename FluidState, typename FIPArray>
1285 void updateWaterMass(const unsigned globalDofIdx,
1286 const FluidState& fs,
1287 const FIPArray& fip
1288 )
1289 {
1290 const Scalar rhoW = FluidSystem::referenceDensity(waterPhaseIdx, fs.pvtRegionIndex());
1291
1292 this->fipC_.assignWaterMass(globalDofIdx, fip, rhoW);
1293 }
1294
1295 template <typename IntensiveQuantities>
1296 void updateMicrobialMass(const unsigned globalDofIdx,
1297 const IntensiveQuantities& intQuants,
1298 const double surfVolWat)
1299 {
1300 const Scalar mass = surfVolWat * intQuants.microbialConcentration().value();
1301
1302 this->fipC_.assignMicrobialMass(globalDofIdx, mass);
1303 }
1304
1305 template <typename IntensiveQuantities>
1306 void updateOxygenMass(const unsigned globalDofIdx,
1307 const IntensiveQuantities& intQuants,
1308 const double surfVolWat)
1309 {
1310 const Scalar mass = surfVolWat * intQuants.oxygenConcentration().value();
1311
1312 this->fipC_.assignOxygenMass(globalDofIdx, mass);
1313 }
1314
1315 template <typename IntensiveQuantities>
1316 void updateUreaMass(const unsigned globalDofIdx,
1317 const IntensiveQuantities& intQuants,
1318 const double surfVolWat)
1319 {
1320 const Scalar mass = surfVolWat * intQuants.ureaConcentration().value();
1321
1322 this->fipC_.assignUreaMass(globalDofIdx, mass);
1323 }
1324
1325 template <typename IntensiveQuantities>
1326 void updateBiofilmMass(const unsigned globalDofIdx,
1327 const IntensiveQuantities& intQuants,
1328 const double totVolume)
1329 {
1330 const Scalar mass = totVolume * intQuants.biofilmMass().value();
1331
1332 this->fipC_.assignBiofilmMass(globalDofIdx, mass);
1333 }
1334
1335 template <typename IntensiveQuantities>
1336 void updateCalciteMass(const unsigned globalDofIdx,
1337 const IntensiveQuantities& intQuants,
1338 const double totVolume)
1339 {
1340 const Scalar mass = totVolume * intQuants.calciteMass().value();
1341
1342 this->fipC_.assignCalciteMass(globalDofIdx, mass);
1343 }
1344
1346 void setupElementExtractors_()
1347 {
1348 using Entry = typename Extractor::Entry;
1349 using Context = typename Extractor::Context;
1350 using ScalarEntry = typename Extractor::ScalarEntry;
1351 using PhaseEntry = typename Extractor::PhaseEntry;
1352
1353 const bool hasResidual = simulator_.model().linearizer().residual().size() > 0;
1354 const auto& hysteresisConfig = simulator_.problem().materialLawManager().hysteresisConfig();
1355
1356 auto extractors = std::array{
1357 Entry{PhaseEntry{&this->saturation_,
1358 [](const unsigned phase, const Context& ectx)
1359 { return getValue(ectx.fs.saturation(phase)); }
1360 }
1361 },
1362 Entry{PhaseEntry{&this->formationVolFactor_,
1363 [](const unsigned phase, const Context& ectx)
1364 {
1365 constexpr auto zero = Scalar{};
1366
1367 const auto recipFVF = getValue(ectx.fs.invB(phase));
1368
1369 return (recipFVF > zero)
1370 ? static_cast<Scalar>(1) / recipFVF
1371 : zero; // Convenient lie. Could possibly 'throw' here.
1372 }
1373 }
1374 },
1375 Entry{PhaseEntry{&this->invB_,
1376 [](const unsigned phase, const Context& ectx)
1377 { return getValue(ectx.fs.invB(phase)); }
1378 }
1379 },
1380 Entry{PhaseEntry{&this->density_,
1381 [](const unsigned phase, const Context& ectx)
1382 { return getValue(ectx.fs.density(phase)); }
1383 }
1384 },
1385 Entry{PhaseEntry{&this->relativePermeability_,
1386 [](const unsigned phase, const Context& ectx)
1387 { return getValue(ectx.intQuants.relativePermeability(phase)); }
1388 }
1389 },
1390 Entry{PhaseEntry{&this->viscosity_,
1391 [this](const unsigned phaseIdx, const Context& ectx)
1392 {
1394 if constexpr (enableExtbo) {
1395 if (this->extboC_.allocated() && phaseIdx == oilPhaseIdx) {
1396 return getValue(ectx.intQuants.oilViscosity());
1397 }
1398 else if (this->extboC_.allocated() && phaseIdx == gasPhaseIdx) {
1399 return getValue(ectx.intQuants.gasViscosity());
1400 }
1401 }
1402 return getValue(ectx.fs.viscosity(phaseIdx));
1403 }
1404 }
1405 },
1406 Entry{PhaseEntry{&this->residual_,
1407 [&modelResid = this->simulator_.model().linearizer().residual()]
1408 (const unsigned phaseIdx, const Context& ectx)
1409 {
1410 const unsigned sIdx = FluidSystem::solventComponentIndex(phaseIdx);
1411 const unsigned activeCompIdx = FluidSystem::canonicalToActiveCompIdx(sIdx);
1412 return modelResid[ectx.globalDofIdx][activeCompIdx];
1413 }
1414 },
1415 hasResidual
1416 },
1417 Entry{ScalarEntry{&this->rockCompPorvMultiplier_,
1418 [&problem = this->simulator_.problem()](const Context& ectx)
1419 {
1420 return problem.template
1421 rockCompPoroMultiplier<Scalar>(ectx.intQuants,
1422 ectx.globalDofIdx);
1423 }
1424 }
1425 },
1426 Entry{ScalarEntry{&this->rockCompTransMultiplier_,
1427 [&problem = this->simulator_.problem()](const Context& ectx)
1428 {
1429 return problem.
1430 template rockCompTransMultiplier<Scalar>(ectx.intQuants,
1431 ectx.globalDofIdx);
1432 }}
1433 },
1434 Entry{ScalarEntry{&this->minimumOilPressure_,
1435 [&problem = this->simulator_.problem()](const Context& ectx)
1436 {
1437 return std::min(getValue(ectx.fs.pressure(oilPhaseIdx)),
1438 problem.minOilPressure(ectx.globalDofIdx));
1439 }
1440 }
1441 },
1442 Entry{ScalarEntry{&this->bubblePointPressure_,
1443 [&failedCells = this->failedCellsPb_,
1444 &vanguard = this->simulator_.vanguard()](const Context& ectx)
1445 {
1446 try {
1447 return getValue(
1448 FluidSystem::bubblePointPressure(ectx.fs,
1449 ectx.intQuants.pvtRegionIndex())
1450 );
1451 } catch (const NumericalProblem&) {
1452 const auto cartesianIdx = vanguard.cartesianIndex(ectx.globalDofIdx);
1453 failedCells.push_back(cartesianIdx);
1454 return Scalar{0};
1455 }
1456 }
1457 }
1458 },
1459 Entry{ScalarEntry{&this->dewPointPressure_,
1460 [&failedCells = this->failedCellsPd_,
1461 &vanguard = this->simulator_.vanguard()](const Context& ectx)
1462 {
1463 try {
1464 return getValue(
1465 FluidSystem::dewPointPressure(ectx.fs,
1466 ectx.intQuants.pvtRegionIndex())
1467 );
1468 } catch (const NumericalProblem&) {
1469 const auto cartesianIdx = vanguard.cartesianIndex(ectx.globalDofIdx);
1470 failedCells.push_back(cartesianIdx);
1471 return Scalar{0};
1472 }
1473 }
1474 }
1475 },
1476 Entry{ScalarEntry{&this->overburdenPressure_,
1477 [&problem = simulator_.problem()](const Context& ectx)
1478 { return problem.overburdenPressure(ectx.globalDofIdx); }
1479 }
1480 },
1481 Entry{ScalarEntry{&this->temperature_,
1482 [](const Context& ectx)
1483 { return getValue(ectx.fs.temperature(oilPhaseIdx)); }
1484 }
1485 },
1486 Entry{ScalarEntry{&this->sSol_,
1487 [](const Context& ectx)
1488 {
1489 if constexpr (enableSolvent) {
1490 return getValue(ectx.intQuants.solventSaturation());
1491 }
1492 else {
1493 return Scalar{0};
1494 }
1495 }
1496 }
1497 },
1498 Entry{ScalarEntry{&this->rswSol_,
1499 [](const Context& ectx)
1500 {
1501 if constexpr (enableSolvent) {
1502 return getValue(ectx.intQuants.rsSolw());
1503 }
1504 else {
1505 return Scalar{0};
1506 }
1507 }
1508 }
1509 },
1510 Entry{ScalarEntry{&this->cPolymer_,
1511 [](const Context& ectx)
1512 {
1513 if constexpr (enablePolymer) {
1514 return getValue(ectx.intQuants.polymerConcentration());
1515 }
1516 else {
1517 return Scalar{0};
1518 }
1519 }
1520 }
1521 },
1522 Entry{ScalarEntry{&this->cFoam_,
1523 [](const Context& ectx)
1524 {
1525 if constexpr (enableFoam) {
1526 return getValue(ectx.intQuants.foamConcentration());
1527 }
1528 else {
1529 return Scalar{0};
1530 }
1531 }
1532 }
1533 },
1534 Entry{ScalarEntry{&this->cSalt_,
1535 [](const Context& ectx)
1536 { return getValue(ectx.fs.saltConcentration()); }
1537 }
1538 },
1539 Entry{ScalarEntry{&this->pSalt_,
1540 [](const Context& ectx)
1541 { return getValue(ectx.fs.saltSaturation()); }
1542 }
1543 },
1544 Entry{ScalarEntry{&this->permFact_,
1545 [](const Context& ectx)
1546 { return getValue(ectx.intQuants.permFactor()); }
1547 }
1548 },
1549 Entry{ScalarEntry{&this->rPorV_,
1550 [&model = this->simulator_.model()](const Context& ectx)
1551 {
1552 const auto totVolume = model.dofTotalVolume(ectx.globalDofIdx);
1553 return totVolume * getValue(ectx.intQuants.porosity());
1554 }
1555 }
1556 },
1557 Entry{ScalarEntry{&this->rs_,
1558 [](const Context& ectx)
1559 { return getValue(ectx.fs.Rs()); }
1560 }
1561 },
1562 Entry{ScalarEntry{&this->rv_,
1563 [](const Context& ectx)
1564 { return getValue(ectx.fs.Rv()); }
1565 }
1566 },
1567 Entry{ScalarEntry{&this->rsw_,
1568 [](const Context& ectx)
1569 { return getValue(ectx.fs.Rsw()); }
1570 }
1571 },
1572 Entry{ScalarEntry{&this->rvw_,
1573 [](const Context& ectx)
1574 { return getValue(ectx.fs.Rvw()); }
1575 }
1576 },
1577 Entry{ScalarEntry{&this->ppcw_,
1578 [&matLawManager = this->simulator_.problem().materialLawManager()]
1579 (const Context& ectx)
1580 {
1581 return matLawManager.
1582 oilWaterScaledEpsInfoDrainage(ectx.globalDofIdx).maxPcow;
1583 }
1584 }
1585 },
1586 Entry{ScalarEntry{&this->drsdtcon_,
1587 [&problem = this->simulator_.problem()](const Context& ectx)
1588 {
1589 return problem.drsdtcon(ectx.globalDofIdx,
1590 ectx.episodeIndex);
1591 }
1592 }
1593 },
1594 Entry{ScalarEntry{&this->pcgw_,
1595 [](const Context& ectx)
1596 {
1597 return getValue(ectx.fs.pressure(gasPhaseIdx)) -
1598 getValue(ectx.fs.pressure(waterPhaseIdx));
1599 }
1600 }
1601 },
1602 Entry{ScalarEntry{&this->pcow_,
1603 [](const Context& ectx)
1604 {
1605 return getValue(ectx.fs.pressure(oilPhaseIdx)) -
1606 getValue(ectx.fs.pressure(waterPhaseIdx));
1607 }
1608 }
1609 },
1610 Entry{ScalarEntry{&this->pcog_,
1611 [](const Context& ectx)
1612 {
1613 return getValue(ectx.fs.pressure(gasPhaseIdx)) -
1614 getValue(ectx.fs.pressure(oilPhaseIdx));
1615 }
1616 }
1617 },
1618 Entry{ScalarEntry{&this->fluidPressure_,
1619 [](const Context& ectx)
1620 {
1621 if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
1622 // Output oil pressure as default
1623 return getValue(ectx.fs.pressure(oilPhaseIdx));
1624 }
1625 else if (FluidSystem::phaseIsActive(gasPhaseIdx)) {
1626 // Output gas if oil is not present
1627 return getValue(ectx.fs.pressure(gasPhaseIdx));
1628 }
1629 else {
1630 // Output water if neither oil nor gas is present
1631 return getValue(ectx.fs.pressure(waterPhaseIdx));
1632 }
1633 }
1634 }
1635 },
1636 Entry{ScalarEntry{&this->gasDissolutionFactor_,
1637 [&problem = this->simulator_.problem()](const Context& ectx)
1638 {
1639 const Scalar SoMax = problem.maxOilSaturation(ectx.globalDofIdx);
1640 return FluidSystem::template
1641 saturatedDissolutionFactor<FluidState, Scalar>(ectx.fs,
1642 oilPhaseIdx,
1643 ectx.pvtRegionIdx,
1644 SoMax);
1645 }
1646 }
1647 },
1648 Entry{ScalarEntry{&this->oilVaporizationFactor_,
1649 [&problem = this->simulator_.problem()](const Context& ectx)
1650 {
1651 const Scalar SoMax = problem.maxOilSaturation(ectx.globalDofIdx);
1652 return FluidSystem::template
1653 saturatedDissolutionFactor<FluidState, Scalar>(ectx.fs,
1654 gasPhaseIdx,
1655 ectx.pvtRegionIdx,
1656 SoMax);
1657 }
1658 }
1659 },
1660 Entry{ScalarEntry{&this->gasDissolutionFactorInWater_,
1661 [&problem = this->simulator_.problem()](const Context& ectx)
1662 {
1663 const Scalar SwMax = problem.maxWaterSaturation(ectx.globalDofIdx);
1664 return FluidSystem::template
1665 saturatedDissolutionFactor<FluidState, Scalar>(ectx.fs,
1666 waterPhaseIdx,
1667 ectx.pvtRegionIdx,
1668 SwMax);
1669 }
1670 }
1671 },
1672 Entry{ScalarEntry{&this->waterVaporizationFactor_,
1673 [](const Context& ectx)
1674 {
1675 return FluidSystem::template
1676 saturatedVaporizationFactor<FluidState, Scalar>(ectx.fs,
1677 gasPhaseIdx,
1678 ectx.pvtRegionIdx);
1679 }
1680 }
1681 },
1682 Entry{ScalarEntry{&this->gasFormationVolumeFactor_,
1683 [](const Context& ectx)
1684 {
1685 return 1.0 / FluidSystem::template
1686 inverseFormationVolumeFactor<FluidState, Scalar>(ectx.fs,
1687 gasPhaseIdx,
1688 ectx.pvtRegionIdx);
1689 }
1690 }
1691 },
1692 Entry{ScalarEntry{&this->saturatedOilFormationVolumeFactor_,
1693 [](const Context& ectx)
1694 {
1695 return 1.0 / FluidSystem::template
1696 saturatedInverseFormationVolumeFactor<FluidState, Scalar>(ectx.fs,
1697 oilPhaseIdx,
1698 ectx.pvtRegionIdx);
1699 }
1700 }
1701 },
1702 Entry{ScalarEntry{&this->oilSaturationPressure_,
1703 [](const Context& ectx)
1704 {
1705 return FluidSystem::template
1706 saturationPressure<FluidState, Scalar>(ectx.fs,
1707 oilPhaseIdx,
1708 ectx.pvtRegionIdx);
1709 }
1710 }
1711 },
1712 Entry{ScalarEntry{&this->soMax_,
1713 [&problem = this->simulator_.problem()](const Context& ectx)
1714 {
1715 return std::max(getValue(ectx.fs.saturation(oilPhaseIdx)),
1716 problem.maxOilSaturation(ectx.globalDofIdx));
1717 }
1718 },
1719 !hysteresisConfig.enableHysteresis()
1720 },
1721 Entry{ScalarEntry{&this->swMax_,
1722 [&problem = this->simulator_.problem()](const Context& ectx)
1723 {
1724 return std::max(getValue(ectx.fs.saturation(waterPhaseIdx)),
1725 problem.maxWaterSaturation(ectx.globalDofIdx));
1726 }
1727 },
1728 !hysteresisConfig.enableHysteresis()
1729 },
1730 Entry{ScalarEntry{&this->soMax_,
1731 [](const Context& ectx)
1732 { return ectx.hParams.somax; }
1733 },
1734 hysteresisConfig.enableHysteresis() &&
1735 hysteresisConfig.enableNonWettingHysteresis() &&
1736 FluidSystem::phaseIsActive(oilPhaseIdx) &&
1737 FluidSystem::phaseIsActive(waterPhaseIdx)
1738 },
1739 Entry{ScalarEntry{&this->swMax_,
1740 [](const Context& ectx)
1741 { return ectx.hParams.swmax; }
1742 },
1743 hysteresisConfig.enableHysteresis() &&
1744 hysteresisConfig.enableWettingHysteresis() &&
1745 FluidSystem::phaseIsActive(oilPhaseIdx) &&
1746 FluidSystem::phaseIsActive(waterPhaseIdx)
1747 },
1748 Entry{ScalarEntry{&this->swmin_,
1749 [](const Context& ectx)
1750 { return ectx.hParams.swmin; }
1751 },
1752 hysteresisConfig.enableHysteresis() &&
1753 hysteresisConfig.enablePCHysteresis() &&
1754 FluidSystem::phaseIsActive(oilPhaseIdx) &&
1755 FluidSystem::phaseIsActive(waterPhaseIdx)
1756 },
1757 Entry{ScalarEntry{&this->sgmax_,
1758 [](const Context& ectx)
1759 { return ectx.hParams.sgmax; }
1760 },
1761 hysteresisConfig.enableHysteresis() &&
1762 hysteresisConfig.enableNonWettingHysteresis() &&
1763 FluidSystem::phaseIsActive(oilPhaseIdx) &&
1764 FluidSystem::phaseIsActive(gasPhaseIdx)
1765 },
1766 Entry{ScalarEntry{&this->shmax_,
1767 [](const Context& ectx)
1768 { return ectx.hParams.shmax; }
1769 },
1770 hysteresisConfig.enableHysteresis() &&
1771 hysteresisConfig.enableWettingHysteresis() &&
1772 FluidSystem::phaseIsActive(oilPhaseIdx) &&
1773 FluidSystem::phaseIsActive(gasPhaseIdx)
1774 },
1775 Entry{ScalarEntry{&this->somin_,
1776 [](const Context& ectx)
1777 { return ectx.hParams.somin; }
1778 },
1779 hysteresisConfig.enableHysteresis() &&
1780 hysteresisConfig.enablePCHysteresis() &&
1781 FluidSystem::phaseIsActive(oilPhaseIdx) &&
1782 FluidSystem::phaseIsActive(gasPhaseIdx)
1783 },
1784 Entry{[&model = this->simulator_.model(), this](const Context& ectx)
1785 {
1786 // Note: We intentionally exclude effects of rock
1787 // compressibility by using referencePorosity() here.
1788 const auto porv = ectx.intQuants.referencePorosity()
1789 * model.dofTotalVolume(ectx.globalDofIdx);
1790
1791 this->aggregateAverageDensityContributions_(ectx.fs, ectx.globalDofIdx,
1792 static_cast<double>(porv));
1793 }, this->regionAvgDensity_.has_value()
1794 },
1795 Entry{[&extboC = this->extboC_](const Context& ectx)
1796 {
1797 detail::ignoreUnused(extboC);
1798 if constexpr (enableExtbo) {
1799 extboC.assignVolumes(ectx.globalDofIdx,
1800 ectx.intQuants.xVolume().value(),
1801 ectx.intQuants.yVolume().value());
1802 extboC.assignZFraction(ectx.globalDofIdx,
1803 ectx.intQuants.zFraction().value());
1804
1805 const Scalar stdVolOil = getValue(ectx.fs.saturation(oilPhaseIdx)) *
1806 getValue(ectx.fs.invB(oilPhaseIdx)) +
1807 getValue(ectx.fs.saturation(gasPhaseIdx)) *
1808 getValue(ectx.fs.invB(gasPhaseIdx)) *
1809 getValue(ectx.fs.Rv());
1810 const Scalar stdVolGas = getValue(ectx.fs.saturation(gasPhaseIdx)) *
1811 getValue(ectx.fs.invB(gasPhaseIdx)) *
1812 (1.0 - ectx.intQuants.yVolume().value()) +
1813 getValue(ectx.fs.saturation(oilPhaseIdx)) *
1814 getValue(ectx.fs.invB(oilPhaseIdx)) *
1815 getValue(ectx.fs.Rs()) *
1816 (1.0 - ectx.intQuants.xVolume().value());
1817 const Scalar stdVolCo2 = getValue(ectx.fs.saturation(gasPhaseIdx)) *
1818 getValue(ectx.fs.invB(gasPhaseIdx)) *
1819 ectx.intQuants.yVolume().value() +
1820 getValue(ectx.fs.saturation(oilPhaseIdx)) *
1821 getValue(ectx.fs.invB(oilPhaseIdx)) *
1822 getValue(ectx.fs.Rs()) *
1823 ectx.intQuants.xVolume().value();
1824 const Scalar rhoO = FluidSystem::referenceDensity(oilPhaseIdx, ectx.pvtRegionIdx);
1825 const Scalar rhoG = FluidSystem::referenceDensity(gasPhaseIdx, ectx.pvtRegionIdx);
1826 const Scalar rhoCO2 = ectx.intQuants.zRefDensity();
1827 const Scalar stdMassTotal = 1.0e-10 + stdVolOil * rhoO + stdVolGas * rhoG + stdVolCo2 * rhoCO2;
1828 extboC.assignMassFractions(ectx.globalDofIdx,
1829 stdVolGas * rhoG / stdMassTotal,
1830 stdVolOil * rhoO / stdMassTotal,
1831 stdVolCo2 * rhoCO2 / stdMassTotal);
1832
1833 if (extboC.phaseMassFractionsRequested()) {
1834 const auto [oil, gas] = extboC.phaseSolventMassFractions({
1835 .oilSaturation = getValue(ectx.fs.saturation(oilPhaseIdx)),
1836 .gasSaturation = getValue(ectx.fs.saturation(gasPhaseIdx)),
1837 .rs = getValue(ectx.fs.Rs()),
1838 .rv = getValue(ectx.fs.Rv()),
1839 .xVolume = ectx.intQuants.xVolume().value(),
1840 .yVolume = ectx.intQuants.yVolume().value(),
1841 .oilDensity = rhoO,
1842 .gasDensity = rhoG,
1843 .solventDensity = rhoCO2,
1844 });
1845 extboC.assignPhaseMassFractions(ectx.globalDofIdx, oil, gas);
1846 }
1847 }
1848 }, this->extboC_.allocated()
1849 },
1850 Entry{[&bioeffectsC = this->bioeffectsC_](const Context& ectx)
1851 {
1852 detail::ignoreUnused(bioeffectsC);
1853 if constexpr (enableBioeffects) {
1854 bioeffectsC.assign(ectx.globalDofIdx,
1855 ectx.intQuants.microbialConcentration().value(),
1856 ectx.intQuants.biofilmVolumeFraction().value());
1857 if (Indices::enableMICP) {
1858 bioeffectsC.assign(ectx.globalDofIdx,
1859 ectx.intQuants.oxygenConcentration().value(),
1860 ectx.intQuants.ureaConcentration().value(),
1861 ectx.intQuants.calciteVolumeFraction().value());
1862 }
1863 }
1864 }, this->bioeffectsC_.allocated()
1865 },
1866 Entry{[&runspec = this->eclState_.runspec(),
1867 &CO2H2C = this->CO2H2C_](const Context& ectx)
1868 {
1869 const auto xwg = FluidSystem::convertRswToXwG(getValue(ectx.fs.Rsw()), ectx.pvtRegionIdx);
1870 const auto xgw = FluidSystem::convertRvwToXgW(getValue(ectx.fs.Rvw()), ectx.pvtRegionIdx);
1871 CO2H2C.assign(ectx.globalDofIdx,
1872 FluidSystem::convertXwGToxwG(xwg, ectx.pvtRegionIdx),
1873 FluidSystem::convertXgWToxgW(xgw, ectx.pvtRegionIdx),
1874 runspec.co2Storage());
1875 }, this->CO2H2C_.allocated()
1876 },
1877 Entry{[&rftC = this->rftC_,
1878 &vanguard = this->simulator_.vanguard()](const Context& ectx)
1879 {
1880 const auto cartesianIdx = vanguard.cartesianIndex(ectx.globalDofIdx);
1881 rftC.assign(cartesianIdx,
1882 [&fs = ectx.fs]()
1883 {
1884 // Reference phase pressure, as for PRESSURE/BPR:
1885 // oil if present, otherwise gas, otherwise water.
1886 if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
1887 return getValue(fs.pressure(oilPhaseIdx));
1888 }
1889 else if (FluidSystem::phaseIsActive(gasPhaseIdx)) {
1890 return getValue(fs.pressure(gasPhaseIdx));
1891 }
1892 else {
1893 return getValue(fs.pressure(waterPhaseIdx));
1894 }
1895 },
1896 [&fs = ectx.fs]() { return getValue(fs.saturation(waterPhaseIdx)); },
1897 [&fs = ectx.fs]() { return getValue(fs.saturation(gasPhaseIdx)); });
1898 }
1899 },
1900 Entry{[&tC = this->tracerC_,
1901 &tM = this->simulator_.problem().tracerModel()](const Context& ectx)
1902 {
1903 tC.assignFreeConcentrations(ectx.globalDofIdx,
1904 [gIdx = ectx.globalDofIdx, &tM](const unsigned tracerIdx)
1905 { return tM.freeTracerConcentration(tracerIdx, gIdx); });
1906 tC.assignSolConcentrations(ectx.globalDofIdx,
1907 [gIdx = ectx.globalDofIdx, &tM](const unsigned tracerIdx)
1908 { return tM.solTracerConcentration(tracerIdx, gIdx); });
1909 }
1910 },
1911 Entry{[&flowsInf = this->simulator_.problem().model().linearizer().getFlowsInfo(),
1912 &flowsC = this->flowsC_,
1913 &vanguard = this->simulator_.vanguard()](const Context& ectx)
1914 {
1915 const auto gas_idx = Indices::gasEnabled ?
1916 conti0EqIdx + FluidSystem::canonicalToActiveCompIdx(gasCompIdx) : -1;
1917 const auto oil_idx = Indices::oilEnabled ?
1918 conti0EqIdx + FluidSystem::canonicalToActiveCompIdx(oilCompIdx) : -1;
1919 const auto water_idx = Indices::waterEnabled ?
1920 conti0EqIdx + FluidSystem::canonicalToActiveCompIdx(waterCompIdx) : -1;
1921 const auto& flowsInfos = flowsInf[ectx.globalDofIdx];
1922 if (!flowsC.blockFlows().empty()) {
1923 const std::vector<int>& blockIdxs = flowsC.blockFlows();
1924 const unsigned cartesianIdx = vanguard.cartesianIndex(ectx.globalDofIdx);
1925 if (std::ranges::binary_search(blockIdxs, cartesianIdx)) {
1926 const auto compIdxs = std::array{ gasCompIdx, oilCompIdx, waterCompIdx };
1927 const auto compEnabled = std::array{ Indices::gasEnabled, Indices::oilEnabled, Indices::waterEnabled };
1928 for (const auto& flowsInfo : flowsInfos) {
1929 if (flowsInfo.faceId < 0) {
1930 continue;
1931 }
1932 for (unsigned ii = 0; ii < compIdxs.size(); ++ii) {
1933 if (!compEnabled[ii]) {
1934 continue;
1935 }
1936 if (flowsC.hasBlockFlowValue(cartesianIdx, flowsInfo.faceId, compIdxs[ii])) {
1937 flowsC.assignBlockFlows(flowsC.blockFlowsIds(cartesianIdx, flowsInfo.faceId, compIdxs[ii]),
1938 flowsInfo.faceId,
1939 compIdxs[ii],
1940 flowsInfo.flow[conti0EqIdx
1941 + FluidSystem::canonicalToActiveCompIdx(compIdxs[ii])]);
1942 }
1943 }
1944 }
1945 }
1946 }
1947 else {
1948 for (const auto& flowsInfo : flowsInfos) {
1949 flowsC.assignFlows(ectx.globalDofIdx,
1950 flowsInfo.faceId,
1951 flowsInfo.nncId,
1952 value_or_zero(gas_idx, flowsInfo.flow),
1953 value_or_zero(oil_idx, flowsInfo.flow),
1954 value_or_zero(water_idx, flowsInfo.flow));
1955 }
1956 }
1957 }, !this->simulator_.problem().model().linearizer().getFlowsInfo().empty()
1958 },
1959 Entry{[&floresInf = this->simulator_.problem().model().linearizer().getFloresInfo(),
1960 &flowsC = this->flowsC_](const Context& ectx)
1961 {
1962 const auto gas_idx = Indices::gasEnabled ?
1963 conti0EqIdx + FluidSystem::canonicalToActiveCompIdx(gasCompIdx) : -1;
1964 const auto oil_idx = Indices::oilEnabled ?
1965 conti0EqIdx + FluidSystem::canonicalToActiveCompIdx(oilCompIdx) : -1;
1966 const auto water_idx = Indices::waterEnabled ?
1967 conti0EqIdx + FluidSystem::canonicalToActiveCompIdx(waterCompIdx) : -1;
1968 const auto& floresInfos = floresInf[ectx.globalDofIdx];
1969 for (const auto& floresInfo : floresInfos) {
1970 flowsC.assignFlores(ectx.globalDofIdx,
1971 floresInfo.faceId,
1972 floresInfo.nncId,
1973 value_or_zero(gas_idx, floresInfo.flow),
1974 value_or_zero(oil_idx, floresInfo.flow),
1975 value_or_zero(water_idx, floresInfo.flow));
1976 }
1977 }, !this->simulator_.problem().model().linearizer().getFloresInfo().empty()
1978 },
1979 Entry{[&velocityInf = this->simulator_.problem().model().linearizer().getVelocityInfo(),
1980 &flowsC = this->flowsC_,
1981 &vanguard = this->simulator_.vanguard()](const Context& ectx)
1982 {
1983 const auto& velocityInfos = velocityInf[ectx.globalDofIdx];
1984 const std::vector<int>& blockIdxs = flowsC.blockVelocity();
1985 const unsigned cartesianIdx = vanguard.cartesianIndex(ectx.globalDofIdx);
1986 if (std::ranges::binary_search(blockIdxs, cartesianIdx)) {
1987 const auto compIdxs = std::array{ gasCompIdx, oilCompIdx, waterCompIdx };
1988 const auto compEnabled = std::array{ Indices::gasEnabled, Indices::oilEnabled, Indices::waterEnabled };
1989 for (const auto& velocityInfo : velocityInfos) {
1990 if (velocityInfo.faceId < 0) {
1991 continue;
1992 }
1993 for (unsigned ii = 0; ii < compIdxs.size(); ++ii) {
1994 if (!compEnabled[ii]) {
1995 continue;
1996 }
1997 if (flowsC.hasBlockVelocityValue(cartesianIdx, velocityInfo.faceId, compIdxs[ii])) {
1998 flowsC.assignBlockVelocity(flowsC.blockVelocityIds(cartesianIdx, velocityInfo.faceId, compIdxs[ii]),
1999 velocityInfo.faceId,
2000 compIdxs[ii],
2001 velocityInfo.velocity[conti0EqIdx
2002 + FluidSystem::canonicalToActiveCompIdx(compIdxs[ii])]);
2003 }
2004 }
2005 }
2006 }
2007 }, !this->flowsC_.blockVelocity().empty() &&
2008 !this->simulator_.problem().model().linearizer().getVelocityInfo().empty()
2009 },
2010 // hack to make the intial output of rs and rv Ecl compatible.
2011 // For cells with swat == 1 Ecl outputs; rs = rsSat and rv=rvSat, in all but the initial step
2012 // where it outputs rs and rv values calculated by the initialization. To be compatible we overwrite
2013 // rs and rv with the values computed in the initially.
2014 // Volume factors, densities and viscosities need to be recalculated with the updated rs and rv values.
2015 Entry{ScalarEntry{&this->rv_,
2016 [&problem = this->simulator_.problem()](const Context& ectx)
2017 { return problem.initialFluidState(ectx.globalDofIdx).Rv(); }
2018 },
2019 simulator_.episodeIndex() < 0 &&
2020 FluidSystem::phaseIsActive(oilPhaseIdx) &&
2021 FluidSystem::phaseIsActive(gasPhaseIdx)
2022 },
2023 Entry{ScalarEntry{&this->rs_,
2024 [&problem = this->simulator_.problem()](const Context& ectx)
2025 { return problem.initialFluidState(ectx.globalDofIdx).Rs(); }
2026 },
2027 simulator_.episodeIndex() < 0 &&
2028 FluidSystem::phaseIsActive(oilPhaseIdx) &&
2029 FluidSystem::phaseIsActive(gasPhaseIdx)
2030 },
2031 Entry{ScalarEntry{&this->rsw_,
2032 [&problem = this->simulator_.problem()](const Context& ectx)
2033 { return problem.initialFluidState(ectx.globalDofIdx).Rsw(); }
2034 },
2035 simulator_.episodeIndex() < 0 &&
2036 FluidSystem::phaseIsActive(oilPhaseIdx) &&
2037 FluidSystem::phaseIsActive(gasPhaseIdx)
2038 },
2039 Entry{ScalarEntry{&this->rvw_,
2040 [&problem = this->simulator_.problem()](const Context& ectx)
2041 { return problem.initialFluidState(ectx.globalDofIdx).Rvw(); }
2042 },
2043 simulator_.episodeIndex() < 0 &&
2044 FluidSystem::phaseIsActive(oilPhaseIdx) &&
2045 FluidSystem::phaseIsActive(gasPhaseIdx)
2046 },
2047 // re-compute the volume factors, viscosities and densities if asked for
2048 Entry{PhaseEntry{&this->density_,
2049 [&problem = this->simulator_.problem()](const unsigned phase,
2050 const Context& ectx)
2051 {
2052 const auto& fsInitial = problem.initialFluidState(ectx.globalDofIdx);
2053 return FluidSystem::density(fsInitial,
2054 phase,
2055 ectx.intQuants.pvtRegionIndex());
2056 }
2057 },
2058 simulator_.episodeIndex() < 0 &&
2059 FluidSystem::phaseIsActive(oilPhaseIdx) &&
2060 FluidSystem::phaseIsActive(gasPhaseIdx)
2061 },
2062 Entry{PhaseEntry{&this->formationVolFactor_,
2063 [&problem = this->simulator_.problem()]
2064 (const unsigned phase, const Context& ectx)
2065 {
2066 constexpr auto zero = Scalar{};
2067
2068 const auto& fsInitial = problem.initialFluidState(ectx.globalDofIdx);
2069
2070 const auto recipFVF = getValue
2071 (FluidSystem::inverseFormationVolumeFactor
2072 (fsInitial, phase, ectx.intQuants.pvtRegionIndex()));
2073
2074 return (recipFVF > zero)
2075 ? static_cast<Scalar>(1) / recipFVF
2076 : zero; // Convenient lie. Could possibly 'throw' here.
2077 }
2078 },
2079 simulator_.episodeIndex() < 0 &&
2080 FluidSystem::phaseIsActive(oilPhaseIdx) &&
2081 FluidSystem::phaseIsActive(gasPhaseIdx)
2082 },
2083 Entry{PhaseEntry{&this->invB_,
2084 [&problem = this->simulator_.problem()](const unsigned phase,
2085 const Context& ectx)
2086 {
2087 const auto& fsInitial = problem.initialFluidState(ectx.globalDofIdx);
2088 return FluidSystem::inverseFormationVolumeFactor(fsInitial,
2089 phase,
2090 ectx.intQuants.pvtRegionIndex());
2091 }
2092 },
2093 simulator_.episodeIndex() < 0 &&
2094 FluidSystem::phaseIsActive(oilPhaseIdx) &&
2095 FluidSystem::phaseIsActive(gasPhaseIdx)
2096 },
2097 Entry{PhaseEntry{&this->viscosity_,
2098 [&problem = this->simulator_.problem()](const unsigned phase,
2099 const Context& ectx)
2100 {
2101 const auto& fsInitial = problem.initialFluidState(ectx.globalDofIdx);
2102 return FluidSystem::viscosity(fsInitial,
2103 phase,
2104 ectx.intQuants.pvtRegionIndex());
2105 }
2106 },
2107 simulator_.episodeIndex() < 0 &&
2108 FluidSystem::phaseIsActive(oilPhaseIdx) &&
2109 FluidSystem::phaseIsActive(gasPhaseIdx)
2110 },
2111 };
2112
2113 // Setup active extractors
2114 this->extractors_ = Extractor::removeInactive(extractors);
2115
2116 // Geochemistry
2117 if constexpr (getPropValue<TypeTag, Properties::EnableGeochemistry>()) {
2118 if (this->geochemC_.allocated()) {
2119 this->extractors_.emplace_back(
2120 [&gC = this->geochemC_,
2121 &gM = this->simulator_.problem().geochemistryModel()](const Context& ectx)
2122 {
2123 gC.assignSpeciesConcentrations(
2124 ectx.globalDofIdx,
2125 [gIdx = ectx.globalDofIdx, &gM](const unsigned speciesIdx)
2126 { return gM.speciesConcentration(speciesIdx, gIdx); }
2127 );
2128 gC.assignMineralConcentrations(
2129 ectx.globalDofIdx,
2130 [gIdx = ectx.globalDofIdx, &gM](const unsigned mineralIdx)
2131 { return gM.mineralConcentration(mineralIdx, gIdx); }
2132 );
2133 gC.assignPH(ectx.globalDofIdx, gM.PH(ectx.globalDofIdx));
2134 },
2135 true
2136 );
2137 }
2138 }
2139
2140 // Geomechanics
2141 if constexpr (getPropValue<TypeTag, Properties::EnableMech>()) {
2142 if (this->mech_.allocated()) {
2143 this->extractors_.emplace_back(
2144 [&mech = this->mech_,
2145 &model = simulator_.problem().geoMechModel()](const Context& ectx)
2146 {
2147 mech.assignDelStress(ectx.globalDofIdx,
2148 model.delstress(ectx.globalDofIdx));
2149
2150 mech.assignDisplacement(ectx.globalDofIdx,
2151 model.disp(ectx.globalDofIdx, /*include_fracture*/true));
2152
2153 // is the tresagii stress which make rock fracture
2154 mech.assignFracStress(ectx.globalDofIdx,
2155 model.fractureStress(ectx.globalDofIdx));
2156
2157 mech.assignLinStress(ectx.globalDofIdx,
2158 model.linstress(ectx.globalDofIdx));
2159
2160 mech.assignPotentialForces(ectx.globalDofIdx,
2161 model.mechPotentialForce(ectx.globalDofIdx),
2162 model.mechPotentialPressForce(ectx.globalDofIdx),
2163 model.mechPotentialTempForce(ectx.globalDofIdx));
2164
2165 mech.assignStrain(ectx.globalDofIdx,
2166 model.strain(ectx.globalDofIdx, /*include_fracture*/true));
2167
2168 // Total stress is not stored but calculated result is Voigt notation
2169 mech.assignStress(ectx.globalDofIdx,
2170 model.stress(ectx.globalDofIdx, /*include_fracture*/true));
2171
2172 // Only assign traction if TRACT/TRACT- is requested
2173 if (mech.enableTraction()) {
2174 mech.assignTraction(ectx.globalDofIdx,
2175 model.traction(ectx.globalDofIdx));
2176 }
2177 },
2178 true
2179 );
2180 }
2181 if (this->tpsaC_.allocated()) {
2182 this->extractors_.emplace_back(
2183 [&tpsaC = this->tpsaC_,
2184 &model = simulator_.problem().geoMechModel()](const Context& ectx) {
2185 tpsaC.assignRotation(ectx.globalDofIdx,
2186 model.rotation(ectx.globalDofIdx));
2187
2188 tpsaC.assignSolidPressure(ectx.globalDofIdx,
2189 model.solidPressure(ectx.globalDofIdx));
2190 },
2191 true
2192 );
2193 }
2194 }
2195 }
2196
2198 void setupBlockExtractors_(const bool isSubStep,
2199 const int reportStepNum)
2200 {
2201 using Entry = typename BlockExtractor::Entry;
2202 using Context = typename BlockExtractor::Context;
2203 using PhaseEntry = typename BlockExtractor::PhaseEntry;
2204 using ScalarEntry = typename BlockExtractor::ScalarEntry;
2205 using TensorEntry = typename BlockExtractor::TensorEntry;
2206
2207 using namespace std::string_view_literals;
2208
2209 const auto pressure_handler =
2210 Entry{ScalarEntry{std::vector{"BPR"sv, "BPRESSUR"sv},
2211 [](const Context& ectx)
2212 {
2213 if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
2214 return getValue(ectx.fs.pressure(oilPhaseIdx));
2215 }
2216 else if (FluidSystem::phaseIsActive(gasPhaseIdx)) {
2217 return getValue(ectx.fs.pressure(gasPhaseIdx));
2218 }
2219 else { //if (FluidSystem::phaseIsActive(waterPhaseIdx))
2220 return getValue(ectx.fs.pressure(waterPhaseIdx));
2221 }
2222 }
2223 }
2224 };
2225
2226 const auto handlers = std::array{
2227 pressure_handler,
2228 Entry{PhaseEntry{std::array{
2229 std::array{"BWSAT"sv, "BOSAT"sv, "BGSAT"sv},
2230 std::array{"BSWAT"sv, "BSOIL"sv, "BSGAS"sv}
2231 },
2232 [](const unsigned phaseIdx, const Context& ectx)
2233 {
2234 return getValue(ectx.fs.saturation(phaseIdx));
2235 }
2236 }
2237 },
2238 Entry{ScalarEntry{"BNSAT",
2239 [](const Context& ectx)
2240 {
2241 if constexpr (enableSolvent) {
2242 return ectx.intQuants.solventSaturation().value();
2243 }
2244 else {
2245 return Scalar{0};
2246 }
2247 }
2248 }
2249 },
2250 Entry{ScalarEntry{std::vector{"BTCNFHEA"sv, "BTEMP"sv},
2251 [](const Context& ectx)
2252 {
2253 if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
2254 return getValue(ectx.fs.temperature(oilPhaseIdx));
2255 }
2256 else if (FluidSystem::phaseIsActive(gasPhaseIdx)) {
2257 return getValue(ectx.fs.temperature(gasPhaseIdx));
2258 }
2259 else { //if (FluidSystem::phaseIsActive(waterPhaseIdx))
2260 return getValue(ectx.fs.temperature(waterPhaseIdx));
2261 }
2262 }
2263 }
2264 },
2265 Entry{PhaseEntry{std::array{
2266 std::array{"BWKR"sv, "BOKR"sv, "BGKR"sv},
2267 std::array{"BKRW"sv, "BKRO"sv, "BKRG"sv}
2268 },
2269 [](const unsigned phaseIdx, const Context& ectx)
2270 {
2271 return getValue(ectx.intQuants.relativePermeability(phaseIdx));
2272 }
2273 }
2274 },
2275 Entry{ScalarEntry{"BKROG",
2276 [&problem = this->simulator_.problem()](const Context& ectx)
2277 {
2278 const auto& materialParams =
2279 problem.materialLawParams(ectx.elemCtx,
2280 ectx.dofIdx,
2281 /* timeIdx = */ 0);
2282 return getValue(MaterialLaw::template
2283 relpermOilInOilGasSystem<Evaluation>(materialParams,
2284 ectx.fs));
2285 }
2286 }
2287 },
2288 Entry{ScalarEntry{"BKROW",
2289 [&problem = this->simulator_.problem()](const Context& ectx)
2290 {
2291 const auto& materialParams = problem.materialLawParams(ectx.elemCtx,
2292 ectx.dofIdx,
2293 /* timeIdx = */ 0);
2294 return getValue(MaterialLaw::template
2295 relpermOilInOilWaterSystem<Evaluation>(materialParams,
2296 ectx.fs));
2297 }
2298 }
2299 },
2300 Entry{ScalarEntry{"BWPC",
2301 [](const Context& ectx)
2302 {
2303 if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
2304 return getValue(ectx.fs.pressure(oilPhaseIdx)) -
2305 getValue(ectx.fs.pressure(waterPhaseIdx));
2306 }
2307 else if (FluidSystem::phaseIsActive(gasPhaseIdx)) {
2308 return getValue(ectx.fs.pressure(gasPhaseIdx)) -
2309 getValue(ectx.fs.pressure(waterPhaseIdx));
2310 }
2311 else {
2312 return Scalar(0.0);
2313 }
2314 }
2315 }
2316 },
2317 Entry{ScalarEntry{"BGPC",
2318 [](const Context& ectx)
2319 {
2320 if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
2321 return getValue(ectx.fs.pressure(gasPhaseIdx)) -
2322 getValue(ectx.fs.pressure(oilPhaseIdx));
2323 }
2324 else if (FluidSystem::phaseIsActive(waterPhaseIdx)) {
2325 return getValue(ectx.fs.pressure(gasPhaseIdx)) -
2326 getValue(ectx.fs.pressure(waterPhaseIdx));
2327 }
2328 else {
2329 return Scalar(0.0);
2330 }
2331 }
2332 }
2333 },
2334 Entry{ScalarEntry{"BWPR",
2335 [](const Context& ectx)
2336 {
2337 return getValue(ectx.fs.pressure(waterPhaseIdx));
2338 }
2339 }
2340 },
2341 Entry{ScalarEntry{"BGPR",
2342 [](const Context& ectx)
2343 {
2344 return getValue(ectx.fs.pressure(gasPhaseIdx));
2345 }
2346 }
2347 },
2348 Entry{PhaseEntry{std::array{
2349 std::array{"BVWAT"sv, "BVOIL"sv, "BVGAS"sv},
2350 std::array{"BWVIS"sv, "BOVIS"sv, "BGVIS"sv}
2351 },
2352 [](const unsigned phaseIdx, const Context& ectx)
2353 {
2354 return getValue(ectx.fs.viscosity(phaseIdx));
2355 }
2356 }
2357 },
2358 Entry{PhaseEntry{std::array{
2359 std::array{"BWDEN"sv, "BODEN"sv, "BGDEN"sv},
2360 std::array{"BDENW"sv, "BDENO"sv, "BDENG"sv}
2361 },
2362 [](const unsigned phaseIdx, const Context& ectx)
2363 {
2364 return getValue(ectx.fs.density(phaseIdx));
2365 }
2366 }
2367 },
2368 Entry{ScalarEntry{"BFLOGI",
2369 [&flowsC = this->flowsC_,
2370 &vanguard = this->simulator_.vanguard()](const Context& ectx)
2371 {
2372 const unsigned index = !flowsC.blockFlows().empty() ?
2373 flowsC.blockFlowsIds(vanguard.cartesianIndex(ectx.globalDofIdx),
2374 FaceDir::ToIntersectionIndex(Dir::XPlus), gasCompIdx) : ectx.globalDofIdx;
2375 return flowsC.getFlow(index, Dir::XPlus, gasCompIdx);
2376 }
2377 }
2378 },
2379 Entry{ScalarEntry{"BFLOGI-",
2380 [&flowsC = this->flowsC_,
2381 &vanguard = this->simulator_.vanguard()](const Context& ectx)
2382 {
2383 const unsigned index = !flowsC.blockFlows().empty() ?
2384 flowsC.blockFlowsIds(vanguard.cartesianIndex(ectx.globalDofIdx),
2385 FaceDir::ToIntersectionIndex(Dir::XMinus), gasCompIdx) : ectx.globalDofIdx;
2386 return flowsC.getFlow(index, Dir::XMinus, gasCompIdx);
2387 }
2388 }
2389 },
2390 Entry{ScalarEntry{"BFLOGJ",
2391 [&flowsC = this->flowsC_,
2392 &vanguard = this->simulator_.vanguard()](const Context& ectx)
2393 {
2394 const unsigned index = !flowsC.blockFlows().empty() ?
2395 flowsC.blockFlowsIds(vanguard.cartesianIndex(ectx.globalDofIdx),
2396 FaceDir::ToIntersectionIndex(Dir::YPlus), gasCompIdx) : ectx.globalDofIdx;
2397 return flowsC.getFlow(index, Dir::YPlus, gasCompIdx);
2398 }
2399 }
2400 },
2401 Entry{ScalarEntry{"BFLOGJ-",
2402 [&flowsC = this->flowsC_,
2403 &vanguard = this->simulator_.vanguard()](const Context& ectx)
2404 {
2405 const unsigned index = !flowsC.blockFlows().empty() ?
2406 flowsC.blockFlowsIds(vanguard.cartesianIndex(ectx.globalDofIdx),
2407 FaceDir::ToIntersectionIndex(Dir::YMinus), gasCompIdx) : ectx.globalDofIdx;
2408 return flowsC.getFlow(index, Dir::YMinus, gasCompIdx);
2409 }
2410 }
2411 },
2412 Entry{ScalarEntry{"BFLOGK",
2413 [&flowsC = this->flowsC_,
2414 &vanguard = this->simulator_.vanguard()](const Context& ectx)
2415 {
2416 const unsigned index = !flowsC.blockFlows().empty() ?
2417 flowsC.blockFlowsIds(vanguard.cartesianIndex(ectx.globalDofIdx),
2418 FaceDir::ToIntersectionIndex(Dir::ZPlus), gasCompIdx) : ectx.globalDofIdx;
2419 return flowsC.getFlow(index, Dir::ZPlus, gasCompIdx);
2420 }
2421 }
2422 },
2423 Entry{ScalarEntry{"BFLOGK-",
2424 [&flowsC = this->flowsC_,
2425 &vanguard = this->simulator_.vanguard()](const Context& ectx)
2426 {
2427 const unsigned index = !flowsC.blockFlows().empty() ?
2428 flowsC.blockFlowsIds(vanguard.cartesianIndex(ectx.globalDofIdx),
2429 FaceDir::ToIntersectionIndex(Dir::ZMinus), gasCompIdx) : ectx.globalDofIdx;
2430 return flowsC.getFlow(index, Dir::ZMinus, gasCompIdx);
2431 }
2432 }
2433 },
2434 Entry{ScalarEntry{"BFLOOI",
2435 [&flowsC = this->flowsC_,
2436 &vanguard = this->simulator_.vanguard()](const Context& ectx)
2437 {
2438 const unsigned index = !flowsC.blockFlows().empty() ?
2439 flowsC.blockFlowsIds(vanguard.cartesianIndex(ectx.globalDofIdx),
2440 FaceDir::ToIntersectionIndex(Dir::XPlus), oilCompIdx) : ectx.globalDofIdx;
2441 return flowsC.getFlow(index, Dir::XPlus, oilCompIdx);
2442 }
2443 }
2444 },
2445 Entry{ScalarEntry{"BFLOOI-",
2446 [&flowsC = this->flowsC_,
2447 &vanguard = this->simulator_.vanguard()](const Context& ectx)
2448 {
2449 const unsigned index = !flowsC.blockFlows().empty() ?
2450 flowsC.blockFlowsIds(vanguard.cartesianIndex(ectx.globalDofIdx),
2451 FaceDir::ToIntersectionIndex(Dir::XMinus), oilCompIdx) : ectx.globalDofIdx;
2452 return flowsC.getFlow(index, Dir::XMinus, oilCompIdx);
2453 }
2454 }
2455 },
2456 Entry{ScalarEntry{"BFLOOJ",
2457 [&flowsC = this->flowsC_,
2458 &vanguard = this->simulator_.vanguard()](const Context& ectx)
2459 {
2460 const unsigned index = !flowsC.blockFlows().empty() ?
2461 flowsC.blockFlowsIds(vanguard.cartesianIndex(ectx.globalDofIdx),
2462 FaceDir::ToIntersectionIndex(Dir::YPlus), oilCompIdx) : ectx.globalDofIdx;
2463 return flowsC.getFlow(index, Dir::YPlus, oilCompIdx);
2464 }
2465 }
2466 },
2467 Entry{ScalarEntry{"BFLOOJ-",
2468 [&flowsC = this->flowsC_,
2469 &vanguard = this->simulator_.vanguard()](const Context& ectx)
2470 {
2471 const unsigned index = !flowsC.blockFlows().empty() ?
2472 flowsC.blockFlowsIds(vanguard.cartesianIndex(ectx.globalDofIdx),
2473 FaceDir::ToIntersectionIndex(Dir::YMinus), oilCompIdx) : ectx.globalDofIdx;
2474 return flowsC.getFlow(index, Dir::YMinus, oilCompIdx);
2475 }
2476 }
2477 },
2478 Entry{ScalarEntry{"BFLOOK",
2479 [&flowsC = this->flowsC_,
2480 &vanguard = this->simulator_.vanguard()](const Context& ectx)
2481 {
2482 const unsigned index = !flowsC.blockFlows().empty() ?
2483 flowsC.blockFlowsIds(vanguard.cartesianIndex(ectx.globalDofIdx),
2484 FaceDir::ToIntersectionIndex(Dir::ZPlus), oilCompIdx) : ectx.globalDofIdx;
2485 return flowsC.getFlow(index, Dir::ZPlus, oilCompIdx);
2486 }
2487 }
2488 },
2489 Entry{ScalarEntry{"BFLOOK-",
2490 [&flowsC = this->flowsC_,
2491 &vanguard = this->simulator_.vanguard()](const Context& ectx)
2492 {
2493 const unsigned index = !flowsC.blockFlows().empty() ?
2494 flowsC.blockFlowsIds(vanguard.cartesianIndex(ectx.globalDofIdx),
2495 FaceDir::ToIntersectionIndex(Dir::ZMinus), oilCompIdx) : ectx.globalDofIdx;
2496 return flowsC.getFlow(index, Dir::ZMinus, oilCompIdx);
2497 }
2498 }
2499 },
2500 Entry{ScalarEntry{"BFLOWI",
2501 [&flowsC = this->flowsC_,
2502 &vanguard = this->simulator_.vanguard()](const Context& ectx)
2503 {
2504 const unsigned index = !flowsC.blockFlows().empty() ?
2505 flowsC.blockFlowsIds(vanguard.cartesianIndex(ectx.globalDofIdx),
2506 FaceDir::ToIntersectionIndex(Dir::XPlus), waterCompIdx) : ectx.globalDofIdx;
2507 return flowsC.getFlow(index, Dir::XPlus, waterCompIdx);
2508 }
2509 }
2510 },
2511 Entry{ScalarEntry{"BFLOWI-",
2512 [&flowsC = this->flowsC_,
2513 &vanguard = this->simulator_.vanguard()](const Context& ectx)
2514 {
2515 const unsigned index = !flowsC.blockFlows().empty() ?
2516 flowsC.blockFlowsIds(vanguard.cartesianIndex(ectx.globalDofIdx),
2517 FaceDir::ToIntersectionIndex(Dir::XMinus), waterCompIdx) : ectx.globalDofIdx;
2518 return flowsC.getFlow(index, Dir::XMinus, waterCompIdx);
2519 }
2520 }
2521 },
2522 Entry{ScalarEntry{"BFLOWJ",
2523 [&flowsC = this->flowsC_,
2524 &vanguard = this->simulator_.vanguard()](const Context& ectx)
2525 {
2526 const unsigned index = !flowsC.blockFlows().empty() ?
2527 flowsC.blockFlowsIds(vanguard.cartesianIndex(ectx.globalDofIdx),
2528 FaceDir::ToIntersectionIndex(Dir::YPlus), waterCompIdx) : ectx.globalDofIdx;
2529 return flowsC.getFlow(index, Dir::YPlus, waterCompIdx);
2530 }
2531 }
2532 },
2533 Entry{ScalarEntry{"BFLOWJ-",
2534 [&flowsC = this->flowsC_,
2535 &vanguard = this->simulator_.vanguard()](const Context& ectx)
2536 {
2537 const unsigned index = !flowsC.blockFlows().empty() ?
2538 flowsC.blockFlowsIds(vanguard.cartesianIndex(ectx.globalDofIdx),
2539 FaceDir::ToIntersectionIndex(Dir::YMinus), waterCompIdx) : ectx.globalDofIdx;
2540 return flowsC.getFlow(index, Dir::YMinus, waterCompIdx);
2541 }
2542 }
2543 },
2544 Entry{ScalarEntry{"BFLOWK",
2545 [&flowsC = this->flowsC_,
2546 &vanguard = this->simulator_.vanguard()](const Context& ectx)
2547 {
2548 const unsigned index = !flowsC.blockFlows().empty() ?
2549 flowsC.blockFlowsIds(vanguard.cartesianIndex(ectx.globalDofIdx),
2550 FaceDir::ToIntersectionIndex(Dir::ZPlus), waterCompIdx) : ectx.globalDofIdx;
2551 return flowsC.getFlow(index, Dir::ZPlus, waterCompIdx);
2552 }
2553 }
2554 },
2555 Entry{ScalarEntry{"BFLOWK-",
2556 [&flowsC = this->flowsC_,
2557 &vanguard = this->simulator_.vanguard()](const Context& ectx)
2558 {
2559 const unsigned index = !flowsC.blockFlows().empty() ?
2560 flowsC.blockFlowsIds(vanguard.cartesianIndex(ectx.globalDofIdx),
2561 FaceDir::ToIntersectionIndex(Dir::ZMinus), waterCompIdx) : ectx.globalDofIdx;
2562 return flowsC.getFlow(index, Dir::ZMinus, waterCompIdx);
2563 }
2564 }
2565 },
2566 Entry{ScalarEntry{"BVELGI",
2567 [&flowsC = this->flowsC_,
2568 &vanguard = this->simulator_.vanguard()](const Context& ectx)
2569 {
2570 const unsigned index = flowsC.blockVelocityIds(vanguard.cartesianIndex(ectx.globalDofIdx),
2571 FaceDir::ToIntersectionIndex(Dir::XPlus), gasCompIdx);
2572 return flowsC.getVelocity(index, Dir::XPlus, gasCompIdx);
2573 }
2574 }
2575 },
2576 Entry{ScalarEntry{"BVELGI-",
2577 [&flowsC = this->flowsC_,
2578 &vanguard = this->simulator_.vanguard()](const Context& ectx)
2579 {
2580 const unsigned index = flowsC.blockVelocityIds(vanguard.cartesianIndex(ectx.globalDofIdx),
2581 FaceDir::ToIntersectionIndex(Dir::XMinus), gasCompIdx);
2582 return flowsC.getVelocity(index, Dir::XMinus, gasCompIdx);
2583 }
2584 }
2585 },
2586 Entry{ScalarEntry{"BVELGJ",
2587 [&flowsC = this->flowsC_,
2588 &vanguard = this->simulator_.vanguard()](const Context& ectx)
2589 {
2590 const unsigned index = flowsC.blockVelocityIds(vanguard.cartesianIndex(ectx.globalDofIdx),
2591 FaceDir::ToIntersectionIndex(Dir::YPlus), gasCompIdx);
2592 return flowsC.getVelocity(index, Dir::YPlus, gasCompIdx);
2593 }
2594 }
2595 },
2596 Entry{ScalarEntry{"BVELGJ-",
2597 [&flowsC = this->flowsC_,
2598 &vanguard = this->simulator_.vanguard()](const Context& ectx)
2599 {
2600 const unsigned index = flowsC.blockVelocityIds(vanguard.cartesianIndex(ectx.globalDofIdx),
2601 FaceDir::ToIntersectionIndex(Dir::YMinus), gasCompIdx);
2602 return flowsC.getVelocity(index, Dir::YMinus, gasCompIdx);
2603 }
2604 }
2605 },
2606 Entry{ScalarEntry{"BVELGK",
2607 [&flowsC = this->flowsC_,
2608 &vanguard = this->simulator_.vanguard()](const Context& ectx)
2609 {
2610 const unsigned index = flowsC.blockVelocityIds(vanguard.cartesianIndex(ectx.globalDofIdx),
2611 FaceDir::ToIntersectionIndex(Dir::ZPlus), gasCompIdx);
2612 return flowsC.getVelocity(index, Dir::ZPlus, gasCompIdx);
2613 }
2614 }
2615 },
2616 Entry{ScalarEntry{"BVELGK-",
2617 [&flowsC = this->flowsC_,
2618 &vanguard = this->simulator_.vanguard()](const Context& ectx)
2619 {
2620 const unsigned index = flowsC.blockVelocityIds(vanguard.cartesianIndex(ectx.globalDofIdx),
2621 FaceDir::ToIntersectionIndex(Dir::ZMinus), gasCompIdx);
2622 return flowsC.getVelocity(index, Dir::ZMinus, gasCompIdx);
2623 }
2624 }
2625 },
2626 Entry{ScalarEntry{"BVELOI",
2627 [&flowsC = this->flowsC_,
2628 &vanguard = this->simulator_.vanguard()](const Context& ectx)
2629 {
2630 const unsigned index = flowsC.blockVelocityIds(vanguard.cartesianIndex(ectx.globalDofIdx),
2631 FaceDir::ToIntersectionIndex(Dir::XPlus), oilCompIdx);
2632 return flowsC.getVelocity(index, Dir::XPlus, oilCompIdx);
2633 }
2634 }
2635 },
2636 Entry{ScalarEntry{"BVELOI-",
2637 [&flowsC = this->flowsC_,
2638 &vanguard = this->simulator_.vanguard()](const Context& ectx)
2639 {
2640 const unsigned index = flowsC.blockVelocityIds(vanguard.cartesianIndex(ectx.globalDofIdx),
2641 FaceDir::ToIntersectionIndex(Dir::XMinus), oilCompIdx);
2642 return flowsC.getVelocity(index, Dir::XMinus, oilCompIdx);
2643 }
2644 }
2645 },
2646 Entry{ScalarEntry{"BVELOJ",
2647 [&flowsC = this->flowsC_,
2648 &vanguard = this->simulator_.vanguard()](const Context& ectx)
2649 {
2650 const unsigned index = flowsC.blockVelocityIds(vanguard.cartesianIndex(ectx.globalDofIdx),
2651 FaceDir::ToIntersectionIndex(Dir::YPlus), oilCompIdx);
2652 return flowsC.getVelocity(index, Dir::YPlus, oilCompIdx);
2653 }
2654 }
2655 },
2656 Entry{ScalarEntry{"BVELOJ-",
2657 [&flowsC = this->flowsC_,
2658 &vanguard = this->simulator_.vanguard()](const Context& ectx)
2659 {
2660 const unsigned index = flowsC.blockVelocityIds(vanguard.cartesianIndex(ectx.globalDofIdx),
2661 FaceDir::ToIntersectionIndex(Dir::YMinus), oilCompIdx);
2662 return flowsC.getVelocity(index, Dir::YMinus, oilCompIdx);
2663 }
2664 }
2665 },
2666 Entry{ScalarEntry{"BVELOK",
2667 [&flowsC = this->flowsC_,
2668 &vanguard = this->simulator_.vanguard()](const Context& ectx)
2669 {
2670 const unsigned index = flowsC.blockVelocityIds(vanguard.cartesianIndex(ectx.globalDofIdx),
2671 FaceDir::ToIntersectionIndex(Dir::ZPlus), oilCompIdx);
2672 return flowsC.getVelocity(index, Dir::ZPlus, oilCompIdx);
2673 }
2674 }
2675 },
2676 Entry{ScalarEntry{"BVELOK-",
2677 [&flowsC = this->flowsC_,
2678 &vanguard = this->simulator_.vanguard()](const Context& ectx)
2679 {
2680 const unsigned index = flowsC.blockVelocityIds(vanguard.cartesianIndex(ectx.globalDofIdx),
2681 FaceDir::ToIntersectionIndex(Dir::ZMinus), oilCompIdx);
2682 return flowsC.getVelocity(index, Dir::ZMinus, oilCompIdx);
2683 }
2684 }
2685 },
2686 Entry{ScalarEntry{"BVELWI",
2687 [&flowsC = this->flowsC_,
2688 &vanguard = this->simulator_.vanguard()](const Context& ectx)
2689 {
2690 const unsigned index = flowsC.blockVelocityIds(vanguard.cartesianIndex(ectx.globalDofIdx),
2691 FaceDir::ToIntersectionIndex(Dir::XPlus), waterCompIdx);
2692 return flowsC.getVelocity(index, Dir::XPlus, waterCompIdx);
2693 }
2694 }
2695 },
2696 Entry{ScalarEntry{"BVELWI-",
2697 [&flowsC = this->flowsC_,
2698 &vanguard = this->simulator_.vanguard()](const Context& ectx)
2699 {
2700 const unsigned index = flowsC.blockVelocityIds(vanguard.cartesianIndex(ectx.globalDofIdx),
2701 FaceDir::ToIntersectionIndex(Dir::XMinus), waterCompIdx);
2702 return flowsC.getVelocity(index, Dir::XMinus, waterCompIdx);
2703 }
2704 }
2705 },
2706 Entry{ScalarEntry{"BVELWJ",
2707 [&flowsC = this->flowsC_,
2708 &vanguard = this->simulator_.vanguard()](const Context& ectx)
2709 {
2710 const unsigned index = flowsC.blockVelocityIds(vanguard.cartesianIndex(ectx.globalDofIdx),
2711 FaceDir::ToIntersectionIndex(Dir::YPlus), waterCompIdx);
2712 return flowsC.getVelocity(index, Dir::YPlus, waterCompIdx);
2713 }
2714 }
2715 },
2716 Entry{ScalarEntry{"BVELWJ-",
2717 [&flowsC = this->flowsC_,
2718 &vanguard = this->simulator_.vanguard()](const Context& ectx)
2719 {
2720 const unsigned index = flowsC.blockVelocityIds(vanguard.cartesianIndex(ectx.globalDofIdx),
2721 FaceDir::ToIntersectionIndex(Dir::YMinus), waterCompIdx);
2722 return flowsC.getVelocity(index, Dir::YMinus, waterCompIdx);
2723 }
2724 }
2725 },
2726 Entry{ScalarEntry{"BVELWK",
2727 [&flowsC = this->flowsC_,
2728 &vanguard = this->simulator_.vanguard()](const Context& ectx)
2729 {
2730 const unsigned index = flowsC.blockVelocityIds(vanguard.cartesianIndex(ectx.globalDofIdx),
2731 FaceDir::ToIntersectionIndex(Dir::ZPlus), waterCompIdx);
2732 return flowsC.getVelocity(index, Dir::ZPlus, waterCompIdx);
2733 }
2734 }
2735 },
2736 Entry{ScalarEntry{"BVELWK-",
2737 [&flowsC = this->flowsC_,
2738 &vanguard = this->simulator_.vanguard()](const Context& ectx)
2739 {
2740 const unsigned index = flowsC.blockVelocityIds(vanguard.cartesianIndex(ectx.globalDofIdx),
2741 FaceDir::ToIntersectionIndex(Dir::ZMinus), waterCompIdx);
2742 return flowsC.getVelocity(index, Dir::ZMinus, waterCompIdx);
2743 }
2744 }
2745 },
2746 Entry{ScalarEntry{"BRPV",
2747 [&model = this->simulator_.model()](const Context& ectx)
2748 {
2749 return getValue(ectx.intQuants.porosity()) *
2750 model.dofTotalVolume(ectx.globalDofIdx);
2751 }
2752 }
2753 },
2754 Entry{PhaseEntry{std::array{"BWPV"sv, "BOPV"sv, "BGPV"sv},
2755 [&model = this->simulator_.model()](const unsigned phaseIdx,
2756 const Context& ectx)
2757 {
2758 return getValue(ectx.fs.saturation(phaseIdx)) *
2759 getValue(ectx.intQuants.porosity()) *
2760 model.dofTotalVolume(ectx.globalDofIdx);
2761 }
2762 }
2763 },
2764 Entry{ScalarEntry{"BRS",
2765 [](const Context& ectx)
2766 {
2767 return getValue(ectx.fs.Rs());
2768 }
2769 }
2770 },
2771 Entry{ScalarEntry{"BRV",
2772 [](const Context& ectx)
2773 {
2774 return getValue(ectx.fs.Rv());
2775 }
2776 }
2777 },
2778 Entry{ScalarEntry{"BOIP",
2779 [&model = this->simulator_.model()](const Context& ectx)
2780 {
2781 return (getValue(ectx.fs.invB(oilPhaseIdx)) *
2782 getValue(ectx.fs.saturation(oilPhaseIdx)) +
2783 getValue(ectx.fs.Rv()) *
2784 getValue(ectx.fs.invB(gasPhaseIdx)) *
2785 getValue(ectx.fs.saturation(gasPhaseIdx))) *
2786 model.dofTotalVolume(ectx.globalDofIdx) *
2787 getValue(ectx.intQuants.porosity());
2788 }
2789 }
2790 },
2791 Entry{ScalarEntry{"BGIP",
2792 [&model = this->simulator_.model()](const Context& ectx)
2793 {
2794 Scalar result = getValue(ectx.fs.invB(gasPhaseIdx)) *
2795 getValue(ectx.fs.saturation(gasPhaseIdx));
2796
2797 if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
2798 result += getValue(ectx.fs.Rs()) *
2799 getValue(ectx.fs.invB(oilPhaseIdx)) *
2800 getValue(ectx.fs.saturation(oilPhaseIdx));
2801 }
2802 else {
2803 result += getValue(ectx.fs.Rsw()) *
2804 getValue(ectx.fs.invB(waterPhaseIdx)) *
2805 getValue(ectx.fs.saturation(waterPhaseIdx));
2806 }
2807
2808 return result *
2809 model.dofTotalVolume(ectx.globalDofIdx) *
2810 getValue(ectx.intQuants.porosity());
2811 }
2812 }
2813 },
2814 Entry{ScalarEntry{"BWIP",
2815 [&model = this->simulator_.model()](const Context& ectx)
2816 {
2817 return getValue(ectx.fs.invB(waterPhaseIdx)) *
2818 getValue(ectx.fs.saturation(waterPhaseIdx)) *
2819 model.dofTotalVolume(ectx.globalDofIdx) *
2820 getValue(ectx.intQuants.porosity());
2821 }
2822 }
2823 },
2824 Entry{ScalarEntry{"BOIPL",
2825 [&model = this->simulator_.model()](const Context& ectx)
2826 {
2827 return getValue(ectx.fs.invB(oilPhaseIdx)) *
2828 getValue(ectx.fs.saturation(oilPhaseIdx)) *
2829 model.dofTotalVolume(ectx.globalDofIdx) *
2830 getValue(ectx.intQuants.porosity());
2831 }
2832 }
2833 },
2834 Entry{ScalarEntry{"BGIPL",
2835 [&model = this->simulator_.model()](const Context& ectx)
2836 {
2837 Scalar result;
2838 if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
2839 result = getValue(ectx.fs.Rs()) *
2840 getValue(ectx.fs.invB(oilPhaseIdx)) *
2841 getValue(ectx.fs.saturation(oilPhaseIdx));
2842 }
2843 else {
2844 result = getValue(ectx.fs.Rsw()) *
2845 getValue(ectx.fs.invB(waterPhaseIdx)) *
2846 getValue(ectx.fs.saturation(waterPhaseIdx));
2847 }
2848 return result *
2849 model.dofTotalVolume(ectx.globalDofIdx) *
2850 getValue(ectx.intQuants.porosity());
2851 }
2852 }
2853 },
2854 Entry{ScalarEntry{"BGIPG",
2855 [&model = this->simulator_.model()](const Context& ectx)
2856 {
2857 return getValue(ectx.fs.invB(gasPhaseIdx)) *
2858 getValue(ectx.fs.saturation(gasPhaseIdx)) *
2859 model.dofTotalVolume(ectx.globalDofIdx) *
2860 getValue(ectx.intQuants.porosity());
2861 }
2862 }
2863 },
2864 Entry{ScalarEntry{"BOIPG",
2865 [&model = this->simulator_.model()](const Context& ectx)
2866 {
2867 return getValue(ectx.fs.Rv()) *
2868 getValue(ectx.fs.invB(gasPhaseIdx)) *
2869 getValue(ectx.fs.saturation(gasPhaseIdx)) *
2870 model.dofTotalVolume(ectx.globalDofIdx) *
2871 getValue(ectx.intQuants.porosity());
2872 }
2873 }
2874 },
2875 Entry{PhaseEntry{std::array{"BPPW"sv, "BPPO"sv, "BPPG"sv},
2876 [&simConfig = this->eclState_.getSimulationConfig(),
2877 &grav = this->simulator_.problem().gravity(),
2878 &regionAvgDensity = this->regionAvgDensity_,
2879 &problem = this->simulator_.problem(),
2880 &regions = this->regions_](const unsigned phaseIdx, const Context& ectx)
2881 {
2882 auto phase = RegionPhasePoreVolAverage::Phase{};
2883 phase.ix = phaseIdx;
2884
2885 // Note different region handling here. FIPNUM is
2886 // one-based, but we need zero-based lookup in
2887 // DatumDepth. On the other hand, pvtRegionIndex is
2888 // zero-based but we need one-based lookup in
2889 // RegionPhasePoreVolAverage.
2890
2891 // Subtract one to convert FIPNUM to region index.
2892 const auto datum = simConfig.datumDepths()(regions["FIPNUM"][ectx.dofIdx] - 1);
2893
2894 // Add one to convert region index to region ID.
2895 const auto region = RegionPhasePoreVolAverage::Region {
2896 ectx.elemCtx.primaryVars(ectx.dofIdx, /*timeIdx=*/0).pvtRegionIndex() + 1
2897 };
2898
2899 const auto density = regionAvgDensity->value("PVTNUM", phase, region);
2900
2901 const auto press = getValue(ectx.fs.pressure(phase.ix));
2902 const auto dz = problem.dofCenterDepth(ectx.globalDofIdx) - datum;
2903 return press - density*dz*grav[GridView::dimensionworld - 1];
2904 }
2905 }
2906 },
2907 Entry{ScalarEntry{"BAMIP",
2908 [&model = this->simulator_.model()](const Context& ectx)
2909 {
2910 const Scalar rhoW = FluidSystem::referenceDensity(waterPhaseIdx,
2911 ectx.intQuants.pvtRegionIndex());
2912 return getValue(ectx.fs.invB(waterPhaseIdx)) *
2913 getValue(ectx.fs.saturation(waterPhaseIdx)) *
2914 rhoW *
2915 model.dofTotalVolume(ectx.globalDofIdx) *
2916 getValue(ectx.intQuants.porosity());
2917 }
2918 }
2919 },
2920 Entry{ScalarEntry{"BMMIP",
2921 [&model = this->simulator_.model()](const Context& ectx)
2922 {
2923 if constexpr (enableBioeffects) {
2924 return getValue(ectx.intQuants.microbialConcentration()) *
2925 getValue(ectx.fs.saturation(waterPhaseIdx)) *
2926 getValue(ectx.intQuants.porosity()) *
2927 model.dofTotalVolume(ectx.globalDofIdx);
2928 }
2929 else {
2930 return Scalar{0};
2931 }
2932 }
2933 }
2934 },
2935 Entry{ScalarEntry{"BMOIP",
2936 [&model = this->simulator_.model()](const Context& ectx)
2937 {
2938 if constexpr (enableBioeffects) {
2939 return getValue(ectx.intQuants.oxygenConcentration()) *
2940 getValue(ectx.intQuants.porosity()) *
2941 model.dofTotalVolume(ectx.globalDofIdx);
2942 }
2943 else {
2944 return Scalar{0};
2945 }
2946 }
2947 }
2948 },
2949 Entry{ScalarEntry{"BMUIP",
2950 [&model = this->simulator_.model()](const Context& ectx)
2951 {
2952 if constexpr (enableBioeffects) {
2953 return getValue(ectx.intQuants.ureaConcentration()) *
2954 getValue(ectx.intQuants.porosity()) *
2955 model.dofTotalVolume(ectx.globalDofIdx);
2956 }
2957 else {
2958 return Scalar{0};
2959 }
2960 }
2961 }
2962 },
2963 Entry{ScalarEntry{"BMBIP",
2964 [&model = this->simulator_.model()](const Context& ectx)
2965 {
2966 if constexpr (enableBioeffects) {
2967 return model.dofTotalVolume(ectx.globalDofIdx) *
2968 getValue(ectx.intQuants.biofilmMass());
2969 }
2970 else {
2971 return Scalar{0};
2972 }
2973 }
2974 }
2975 },
2976 Entry{ScalarEntry{"BMCIP",
2977 [&model = this->simulator_.model()](const Context& ectx)
2978 {
2979 if constexpr (enableBioeffects) {
2980 return model.dofTotalVolume(ectx.globalDofIdx) *
2981 getValue(ectx.intQuants.calciteMass());
2982 }
2983 else {
2984 return Scalar{0};
2985 }
2986 }
2987 }
2988 },
2989 Entry{ScalarEntry{"BGMIP",
2990 [&model = this->simulator_.model()](const Context& ectx)
2991 {
2992 Scalar result = getValue(ectx.fs.invB(gasPhaseIdx)) *
2993 getValue(ectx.fs.saturation(gasPhaseIdx));
2994
2995 if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
2996 result += getValue(ectx.fs.Rs()) *
2997 getValue(ectx.fs.invB(oilPhaseIdx)) *
2998 getValue(ectx.fs.saturation(oilPhaseIdx));
2999 }
3000 else {
3001 result += getValue(ectx.fs.Rsw()) *
3002 getValue(ectx.fs.invB(waterPhaseIdx)) *
3003 getValue(ectx.fs.saturation(waterPhaseIdx));
3004 }
3005 const Scalar rhoG = FluidSystem::referenceDensity(gasPhaseIdx,
3006 ectx.intQuants.pvtRegionIndex());
3007 return result *
3008 model.dofTotalVolume(ectx.globalDofIdx) *
3009 getValue(ectx.intQuants.porosity()) *
3010 rhoG;
3011 }
3012 }
3013 },
3014 Entry{ScalarEntry{"BGMGP",
3015 [&model = this->simulator_.model()](const Context& ectx)
3016 {
3017 const Scalar rhoG = FluidSystem::referenceDensity(gasPhaseIdx,
3018 ectx.intQuants.pvtRegionIndex());
3019 return getValue(ectx.fs.invB(gasPhaseIdx)) *
3020 getValue(ectx.fs.saturation(gasPhaseIdx)) *
3021 model.dofTotalVolume(ectx.globalDofIdx) *
3022 getValue(ectx.intQuants.porosity()) *
3023 rhoG;
3024 }
3025 }
3026 },
3027 Entry{ScalarEntry{"BGMDS",
3028 [&model = this->simulator_.model()](const Context& ectx)
3029 {
3030 Scalar result;
3031 if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
3032 result = getValue(ectx.fs.Rs()) *
3033 getValue(ectx.fs.invB(oilPhaseIdx)) *
3034 getValue(ectx.fs.saturation(oilPhaseIdx));
3035 }
3036 else {
3037 result = getValue(ectx.fs.Rsw()) *
3038 getValue(ectx.fs.invB(waterPhaseIdx)) *
3039 getValue(ectx.fs.saturation(waterPhaseIdx));
3040 }
3041 const Scalar rhoG = FluidSystem::referenceDensity(gasPhaseIdx,
3042 ectx.intQuants.pvtRegionIndex());
3043 return result *
3044 model.dofTotalVolume(ectx.globalDofIdx) *
3045 getValue(ectx.intQuants.porosity()) *
3046 rhoG;
3047 }
3048 }
3049 },
3050 Entry{ScalarEntry{"BGMST",
3051 [&model = this->simulator_.model(),
3052 &problem = this->simulator_.problem()](const Context& ectx)
3053 {
3054 const auto& scaledDrainageInfo = problem.materialLawManager().oilWaterScaledEpsInfoDrainage(ectx.dofIdx);
3055 const Scalar sg = getValue(ectx.fs.saturation(gasPhaseIdx));
3056 Scalar strandedGas = scaledDrainageInfo.Sgcr;
3057 if (problem.materialLawManager().enableHysteresis()) {
3058 const auto& matParams = problem.materialLawParams(ectx.dofIdx);
3059 const Scalar krg = getValue(ectx.intQuants.relativePermeability(gasPhaseIdx));
3060 strandedGas = MaterialLaw::strandedGasSaturation(matParams, sg, krg);
3061 }
3062 const Scalar xgW = FluidSystem::phaseIsActive(waterPhaseIdx) ?
3063 FluidSystem::convertRvwToXgW(getValue(ectx.fs.Rvw()), ectx.intQuants.pvtRegionIndex())
3064 : FluidSystem::convertRvToXgO(getValue(ectx.fs.Rv()), ectx.intQuants.pvtRegionIndex());
3065 return (1.0 - xgW) *
3066 model.dofTotalVolume(ectx.globalDofIdx) *
3067 getValue(ectx.intQuants.porosity()) *
3068 getValue(ectx.fs.density(gasPhaseIdx)) *
3069 std::min(strandedGas, sg);
3070 }
3071 }
3072 },
3073 Entry{ScalarEntry{"BGMUS",
3074 [&model = this->simulator_.model(),
3075 &problem = this->simulator_.problem()](const Context& ectx)
3076 {
3077 const auto& scaledDrainageInfo = problem.materialLawManager().oilWaterScaledEpsInfoDrainage(ectx.dofIdx);
3078 const Scalar sg = getValue(ectx.fs.saturation(gasPhaseIdx));
3079 Scalar strandedGas = scaledDrainageInfo.Sgcr;
3080 if (problem.materialLawManager().enableHysteresis()) {
3081 const auto& matParams = problem.materialLawParams(ectx.dofIdx);
3082 const Scalar krg = getValue(ectx.intQuants.relativePermeability(gasPhaseIdx));
3083 strandedGas = MaterialLaw::strandedGasSaturation(matParams, sg, krg);
3084 }
3085 const Scalar xgW = FluidSystem::phaseIsActive(waterPhaseIdx) ?
3086 FluidSystem::convertRvwToXgW(getValue(ectx.fs.Rvw()), ectx.intQuants.pvtRegionIndex())
3087 : FluidSystem::convertRvToXgO(getValue(ectx.fs.Rv()), ectx.intQuants.pvtRegionIndex());
3088 return (1.0 - xgW) *
3089 model.dofTotalVolume(ectx.globalDofIdx) *
3090 getValue(ectx.intQuants.porosity()) *
3091 getValue(ectx.fs.density(gasPhaseIdx)) *
3092 std::max(Scalar{0.0}, sg - strandedGas);
3093 }
3094 }
3095 },
3096 Entry{ScalarEntry{"BGMTR",
3097 [&model = this->simulator_.model(),
3098 &problem = this->simulator_.problem()](const Context& ectx)
3099 {
3100 const auto& scaledDrainageInfo = problem.materialLawManager().oilWaterScaledEpsInfoDrainage(ectx.dofIdx);
3101 Scalar trappedGas = scaledDrainageInfo.Sgcr;
3102 if (problem.materialLawManager().enableHysteresis()) {
3103 const auto& matParams = problem.materialLawParams(ectx.dofIdx);
3104 trappedGas = MaterialLaw::trappedGasSaturation(matParams, /*maxTrapping*/true);
3105 }
3106 const Scalar xgW = FluidSystem::phaseIsActive(waterPhaseIdx) ?
3107 FluidSystem::convertRvwToXgW(getValue(ectx.fs.Rvw()), ectx.intQuants.pvtRegionIndex())
3108 : FluidSystem::convertRvToXgO(getValue(ectx.fs.Rv()), ectx.intQuants.pvtRegionIndex());
3109 return (1.0 - xgW) *
3110 model.dofTotalVolume(ectx.globalDofIdx) *
3111 getValue(ectx.intQuants.porosity()) *
3112 getValue(ectx.fs.density(gasPhaseIdx)) *
3113 std::min(trappedGas, getValue(ectx.fs.saturation(gasPhaseIdx)));
3114 }
3115 }
3116 },
3117 Entry{ScalarEntry{"BGMMO",
3118 [&model = this->simulator_.model(),
3119 &problem = this->simulator_.problem()](const Context& ectx)
3120 {
3121 const auto& scaledDrainageInfo = problem.materialLawManager().oilWaterScaledEpsInfoDrainage(ectx.dofIdx);
3122 Scalar trappedGas = scaledDrainageInfo.Sgcr;
3123 if (problem.materialLawManager().enableHysteresis()) {
3124 const auto& matParams = problem.materialLawParams(ectx.dofIdx);
3125 trappedGas = MaterialLaw::trappedGasSaturation(matParams, /*maxTrapping*/true);
3126 }
3127 const Scalar xgW = FluidSystem::phaseIsActive(waterPhaseIdx) ?
3128 FluidSystem::convertRvwToXgW(getValue(ectx.fs.Rvw()), ectx.intQuants.pvtRegionIndex())
3129 : FluidSystem::convertRvToXgO(getValue(ectx.fs.Rv()), ectx.intQuants.pvtRegionIndex());
3130 return (1.0 - xgW) *
3131 model.dofTotalVolume(ectx.globalDofIdx) *
3132 getValue(ectx.intQuants.porosity()) *
3133 getValue(ectx.fs.density(gasPhaseIdx)) *
3134 std::max(Scalar{0.0}, getValue(ectx.fs.saturation(gasPhaseIdx)) - trappedGas);
3135 }
3136 }
3137 },
3138 Entry{ScalarEntry{"BGKTR",
3139 [&model = this->simulator_.model(),
3140 &problem = this->simulator_.problem()](const Context& ectx)
3141 {
3142 const auto& scaledDrainageInfo = problem.materialLawManager().oilWaterScaledEpsInfoDrainage(ectx.dofIdx);
3143 const Scalar sg = getValue(ectx.fs.saturation(gasPhaseIdx));
3144 Scalar sgcr = scaledDrainageInfo.Sgcr;
3145 if (problem.materialLawManager().enableHysteresis()) {
3146 const auto& matParams = problem.materialLawParams(ectx.dofIdx);
3147 sgcr = MaterialLaw::trappedGasSaturation(matParams, /*maxTrapping*/false);
3148 }
3149 if (sg > sgcr) {
3150 return 0.0;
3151 }
3152 else {
3153 const Scalar xgW = FluidSystem::phaseIsActive(waterPhaseIdx) ?
3154 FluidSystem::convertRvwToXgW(getValue(ectx.fs.Rvw()), ectx.intQuants.pvtRegionIndex())
3155 : FluidSystem::convertRvToXgO(getValue(ectx.fs.Rv()), ectx.intQuants.pvtRegionIndex());
3156 return (1.0 - xgW) *
3157 model.dofTotalVolume(ectx.globalDofIdx) *
3158 getValue(ectx.intQuants.porosity()) *
3159 getValue(ectx.fs.density(gasPhaseIdx)) *
3160 getValue(ectx.fs.saturation(gasPhaseIdx));
3161 }
3162 }
3163 }
3164 },
3165 Entry{ScalarEntry{"BGKMO",
3166 [&model = this->simulator_.model(),
3167 &problem = this->simulator_.problem()](const Context& ectx)
3168 {
3169 const auto& scaledDrainageInfo = problem.materialLawManager().oilWaterScaledEpsInfoDrainage(ectx.dofIdx);
3170 const Scalar sg = getValue(ectx.fs.saturation(gasPhaseIdx));
3171 Scalar sgcr = scaledDrainageInfo.Sgcr;
3172 if (problem.materialLawManager().enableHysteresis()) {
3173 const auto& matParams = problem.materialLawParams(ectx.dofIdx);
3174 sgcr = MaterialLaw::trappedGasSaturation(matParams, /*maxTrapping*/false);
3175 }
3176 if (sgcr >= sg) {
3177 return 0.0;
3178 }
3179 else {
3180 const Scalar xgW = FluidSystem::phaseIsActive(waterPhaseIdx) ?
3181 FluidSystem::convertRvwToXgW(getValue(ectx.fs.Rvw()), ectx.intQuants.pvtRegionIndex())
3182 : FluidSystem::convertRvToXgO(getValue(ectx.fs.Rv()), ectx.intQuants.pvtRegionIndex());
3183 return (1.0 - xgW) *
3184 model.dofTotalVolume(ectx.globalDofIdx) *
3185 getValue(ectx.intQuants.porosity()) *
3186 getValue(ectx.fs.density(gasPhaseIdx)) *
3187 getValue(ectx.fs.saturation(gasPhaseIdx));
3188 }
3189 }
3190 }
3191 },
3192 Entry{ScalarEntry{"BGCDI",
3193 [&model = this->simulator_.model(),
3194 &problem = this->simulator_.problem()](const Context& ectx)
3195 {
3196 const auto& scaledDrainageInfo = problem.materialLawManager().oilWaterScaledEpsInfoDrainage(ectx.dofIdx);
3197 Scalar sgcr = scaledDrainageInfo.Sgcr;
3198 if (problem.materialLawManager().enableHysteresis()) {
3199 const auto& matParams = problem.materialLawParams(ectx.dofIdx);
3200 sgcr = MaterialLaw::trappedGasSaturation(matParams, /*maxTrapping*/false);
3201 }
3202 const Scalar xgW = FluidSystem::phaseIsActive(waterPhaseIdx) ?
3203 FluidSystem::convertRvwToXgW(getValue(ectx.fs.Rvw()), ectx.intQuants.pvtRegionIndex())
3204 : FluidSystem::convertRvToXgO(getValue(ectx.fs.Rv()), ectx.intQuants.pvtRegionIndex());
3205 return (1.0 - xgW) *
3206 model.dofTotalVolume(ectx.globalDofIdx) *
3207 getValue(ectx.intQuants.porosity()) *
3208 getValue(ectx.fs.density(gasPhaseIdx)) *
3209 std::min(sgcr, getValue(ectx.fs.saturation(gasPhaseIdx))) /
3210 FluidSystem::molarMass(gasCompIdx, ectx.intQuants.pvtRegionIndex());
3211 }
3212 }
3213 },
3214 Entry{ScalarEntry{"BGCDM",
3215 [&model = this->simulator_.model(),
3216 &problem = this->simulator_.problem()](const Context& ectx)
3217 {
3218 const auto& scaledDrainageInfo = problem.materialLawManager().oilWaterScaledEpsInfoDrainage(ectx.dofIdx);
3219 Scalar sgcr = scaledDrainageInfo.Sgcr;
3220 if (problem.materialLawManager().enableHysteresis()) {
3221 const auto& matParams = problem.materialLawParams(ectx.dofIdx);
3222 sgcr = MaterialLaw::trappedGasSaturation(matParams, /*maxTrapping*/false);
3223 }
3224 const Scalar xgW = FluidSystem::phaseIsActive(waterPhaseIdx) ?
3225 FluidSystem::convertRvwToXgW(getValue(ectx.fs.Rvw()), ectx.intQuants.pvtRegionIndex())
3226 : FluidSystem::convertRvToXgO(getValue(ectx.fs.Rv()), ectx.intQuants.pvtRegionIndex());
3227 return (1.0 - xgW) *
3228 model.dofTotalVolume(ectx.globalDofIdx) *
3229 getValue(ectx.intQuants.porosity()) *
3230 getValue(ectx.fs.density(gasPhaseIdx)) *
3231 std::max(Scalar{0.0}, getValue(ectx.fs.saturation(gasPhaseIdx)) - sgcr) /
3232 FluidSystem::molarMass(gasCompIdx, ectx.intQuants.pvtRegionIndex());
3233 }
3234 }
3235 },
3236 Entry{ScalarEntry{"BGKDI",
3237 [&model = this->simulator_.model(),
3238 &problem = this->simulator_.problem()](const Context& ectx)
3239 {
3240 const auto& scaledDrainageInfo = problem.materialLawManager().oilWaterScaledEpsInfoDrainage(ectx.dofIdx);
3241 const Scalar sg = getValue(ectx.fs.saturation(gasPhaseIdx));
3242 Scalar sgcr = scaledDrainageInfo.Sgcr;
3243 if (problem.materialLawManager().enableHysteresis()) {
3244 const auto& matParams = problem.materialLawParams(ectx.dofIdx);
3245 sgcr = MaterialLaw::trappedGasSaturation(matParams, /*maxTrapping*/false);
3246 }
3247 if (sg > sgcr) {
3248 return 0.0;
3249 }
3250 else {
3251 const Scalar xgW = FluidSystem::phaseIsActive(waterPhaseIdx) ?
3252 FluidSystem::convertRvwToXgW(getValue(ectx.fs.Rvw()), ectx.intQuants.pvtRegionIndex())
3253 : FluidSystem::convertRvToXgO(getValue(ectx.fs.Rv()), ectx.intQuants.pvtRegionIndex());
3254 return (1.0 - xgW) *
3255 model.dofTotalVolume(ectx.globalDofIdx) *
3256 getValue(ectx.intQuants.porosity()) *
3257 getValue(ectx.fs.density(gasPhaseIdx)) *
3258 getValue(ectx.fs.saturation(gasPhaseIdx)) /
3259 FluidSystem::molarMass(gasCompIdx, ectx.intQuants.pvtRegionIndex());
3260 }
3261 }
3262 }
3263 },
3264 Entry{ScalarEntry{"BGKDM",
3265 [&model = this->simulator_.model(),
3266 &problem = this->simulator_.problem()](const Context& ectx)
3267 {
3268 const auto& scaledDrainageInfo = problem.materialLawManager().oilWaterScaledEpsInfoDrainage(ectx.dofIdx);
3269 const Scalar sg = getValue(ectx.fs.saturation(gasPhaseIdx));
3270 Scalar sgcr = scaledDrainageInfo.Sgcr;
3271 if (problem.materialLawManager().enableHysteresis()) {
3272 const auto& matParams = problem.materialLawParams(ectx.dofIdx);
3273 sgcr = MaterialLaw::trappedGasSaturation(matParams, /*maxTrapping*/false);
3274 }
3275 if (sgcr >= sg) {
3276 return 0.0;
3277 }
3278 else {
3279 const Scalar xgW = FluidSystem::phaseIsActive(waterPhaseIdx) ?
3280 FluidSystem::convertRvwToXgW(getValue(ectx.fs.Rvw()), ectx.intQuants.pvtRegionIndex())
3281 : FluidSystem::convertRvToXgO(getValue(ectx.fs.Rv()), ectx.intQuants.pvtRegionIndex());
3282 return (1.0 - xgW) *
3283 model.dofTotalVolume(ectx.globalDofIdx) *
3284 getValue(ectx.intQuants.porosity()) *
3285 getValue(ectx.fs.density(gasPhaseIdx)) *
3286 getValue(ectx.fs.saturation(gasPhaseIdx)) /
3287 FluidSystem::molarMass(gasCompIdx, ectx.intQuants.pvtRegionIndex());
3288 }
3289 }
3290 }
3291 },
3292 Entry{ScalarEntry{"BWCD",
3293 [&model = this->simulator_.model()](const Context& ectx)
3294 {
3295 Scalar result;
3296 if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
3297 result = getValue(ectx.fs.Rs()) *
3298 getValue(ectx.fs.invB(oilPhaseIdx)) *
3299 getValue(ectx.fs.saturation(oilPhaseIdx));
3300 }
3301 else {
3302 result = getValue(ectx.fs.Rsw()) *
3303 getValue(ectx.fs.invB(waterPhaseIdx)) *
3304 getValue(ectx.fs.saturation(waterPhaseIdx));
3305 }
3306 const Scalar rhoG = FluidSystem::referenceDensity(gasPhaseIdx,
3307 ectx.intQuants.pvtRegionIndex());
3308 return result *
3309 model.dofTotalVolume(ectx.globalDofIdx) *
3310 getValue(ectx.intQuants.porosity()) *
3311 rhoG /
3312 FluidSystem::molarMass(gasCompIdx, ectx.intQuants.pvtRegionIndex());
3313 }
3314 }
3315 },
3316 Entry{ScalarEntry{"BWIPG",
3317 [&model = this->simulator_.model()](const Context& ectx)
3318 {
3319 Scalar result = 0.0;
3320 if (FluidSystem::phaseIsActive(gasPhaseIdx)) {
3321 result = getValue(ectx.fs.Rvw()) *
3322 getValue(ectx.fs.invB(gasPhaseIdx)) *
3323 getValue(ectx.fs.saturation(gasPhaseIdx));
3324 }
3325 return result *
3326 model.dofTotalVolume(ectx.globalDofIdx) *
3327 getValue(ectx.intQuants.porosity());
3328 }
3329 }
3330 },
3331 Entry{ScalarEntry{"BWIPL",
3332 [&model = this->simulator_.model()](const Context& ectx)
3333 {
3334 return getValue(ectx.fs.invB(waterPhaseIdx)) *
3335 getValue(ectx.fs.saturation(waterPhaseIdx)) *
3336 model.dofTotalVolume(ectx.globalDofIdx) *
3337 getValue(ectx.intQuants.porosity());
3338 }
3339 }
3340 },
3341 };
3342
3343 auto blockHandlers = std::span<const Entry>{handlers};
3344 auto mechHandlers = std::vector<Entry>{};
3345 if constexpr (getPropValue<TypeTag, Properties::EnableMech>()) {
3346 if (this->mech_.allocated()) {
3347 mechHandlers.assign(handlers.begin(), handlers.end());
3348 mechHandlers.push_back(
3349 Entry{TensorEntry{"BSTRSS",
3350 [&model = this->simulator_.problem().geoMechModel()]
3351 (const VoigtIndex index, const Context& ectx)
3352 {
3353 return model.stress(ectx.globalDofIdx, /*include_fracture*/true)[index];
3354 }
3355 }
3356 });
3357 blockHandlers = mechHandlers;
3358 }
3359 }
3360
3361 this->blockExtractors_ = BlockExtractor::setupExecMap(this->blockData_, blockHandlers);
3362
3363 // The LGR-cell extractors reuse the same handler list -- the
3364 // physics is identical, only the cell identification differs.
3365 // Empty for runs without LB* requests (zero non-LGR cost).
3366 this->lgrBlockExtractors_ =
3367 BlockExtractor::setupLgrExecMap(this->lgrBlockData_, blockHandlers);
3368
3369 this->extraBlockData_.clear();
3370 if (reportStepNum > 0 && !isSubStep) {
3371 // check we need extra block pressures for RPTSCHED
3372 const auto& rpt = this->schedule_[reportStepNum - 1].rpt_config.get();
3373 if (rpt.contains("WELLS") && rpt.at("WELLS") > 1) {
3374 this->setupExtraBlockData(reportStepNum,
3375 [&c = this->collectOnIORank_](const int idx)
3376 { return c.isCartIdxOnThisRank(idx); });
3377
3378 const auto extraHandlers = std::array{
3379 pressure_handler,
3380 };
3381
3382 this->extraBlockExtractors_ = BlockExtractor::setupExecMap(this->extraBlockData_, extraHandlers);
3383 }
3384 }
3385 }
3386
3387 const Simulator& simulator_;
3388 const CollectDataOnIORankType& collectOnIORank_;
3389 std::vector<typename Extractor::Entry> extractors_;
3390 typename BlockExtractor::ExecMap blockExtractors_;
3391 typename BlockExtractor::ExecMap extraBlockExtractors_;
3392
3393 // Per-LGR-cell extractor executor map. Outer key is the grid level,
3394 // inner key is the level-local linearised Cartesian cell index, so
3395 // the per-DOF lookup in processElementBlockData short-circuits O(1)
3396 // on levels with no LB* requests. Empty for non-LGR runs.
3397 typename BlockExtractor::LgrExecMap lgrBlockExtractors_;
3398};
3399
3400} // namespace Opm
3401
3402#endif // OPM_OUTPUT_BLACK_OIL_MODULE_HPP
Common output functionality shared by simulator formulations.
Declares the properties required by the black oil model.
Definition: CollectDataOnIORank.hpp:57
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 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
void assignGasWater(const unsigned globalDofIdx, const std::array< Scalar, numPhases > &fip, const Scalar gasInPlaceWater, const Scalar waterInPlaceGas)
const std::vector< int > blockVelocity() const
Definition: FlowsContainer.hpp:103
Definition: GenericOutputModule.hpp:91
const Schedule & schedule_
Definition: GenericOutputModule.hpp:536
ScalarBuffer oilVaporizationFactor_
Definition: GenericOutputModule.hpp:613
bool forceDisableFipOutput_
Definition: GenericOutputModule.hpp:557
RFTContainer< GetPropType< TypeTag, Properties::FluidSystem > > rftC_
Definition: GenericOutputModule.hpp:642
ScalarBuffer saturatedOilFormationVolumeFactor_
Definition: GenericOutputModule.hpp:618
GeochemistryContainer< Scalar > geochemC_
Definition: GenericOutputModule.hpp:634
ScalarBuffer rockCompPorvMultiplier_
Definition: GenericOutputModule.hpp:616
TracerContainer< GetPropType< TypeTag, Properties::FluidSystem > > tracerC_
Definition: GenericOutputModule.hpp:636
void doAllocBuffers(unsigned bufferSize, unsigned reportStepNum, const bool substep, const bool log, const bool forceRestartFieldAllocation, const EclHysteresisConfig *hysteresisConfig, unsigned numOutputNnc=0, std::map< std::string, int > rstKeywords={})
CO2H2Container< Scalar > CO2H2C_
Definition: GenericOutputModule.hpp:621
std::array< ScalarBuffer, numPhases > residual_
Definition: GenericOutputModule.hpp:638
ScalarBuffer sSol_
Definition: GenericOutputModule.hpp:597
std::map< std::tuple< std::string, int, int >, double > lgrBlockData_
Definition: GenericOutputModule.hpp:655
void setupExtraBlockData(const std::size_t reportStepNum, std::function< bool(int)> isCartIdxOnThisRank)
std::vector< int > failedCellsPd_
Definition: GenericOutputModule.hpp:583
ScalarBuffer shmax_
Definition: GenericOutputModule.hpp:608
ScalarBuffer sgmax_
Definition: GenericOutputModule.hpp:607
ScalarBuffer rockCompTransMultiplier_
Definition: GenericOutputModule.hpp:619
void setupBlockData(std::function< bool(int)> isCartIdxOnThisRank)
ScalarBuffer minimumOilPressure_
Definition: GenericOutputModule.hpp:617
ScalarBuffer temperature_
Definition: GenericOutputModule.hpp:591
ScalarBuffer pcow_
Definition: GenericOutputModule.hpp:623
ScalarBuffer drsdtcon_
Definition: GenericOutputModule.hpp:596
std::optional< RegionPhasePoreVolAverage > regionAvgDensity_
Definition: GenericOutputModule.hpp:660
ScalarBuffer permFact_
Definition: GenericOutputModule.hpp:603
ScalarBuffer overburdenPressure_
Definition: GenericOutputModule.hpp:594
TpsaContainer< Scalar > tpsaC_
Definition: GenericOutputModule.hpp:628
ScalarBuffer rPorV_
Definition: GenericOutputModule.hpp:589
std::array< ScalarBuffer, numPhases > viscosity_
Definition: GenericOutputModule.hpp:632
void setupLgrBlockData(const std::map< std::string, int > &lgrNameToLevel, const std::function< bool(int, int)> &isLgrCellOnThisRank)
ScalarBuffer rvw_
Definition: GenericOutputModule.hpp:593
FIPContainer< GetPropType< TypeTag, Properties::FluidSystem > > fipC_
Definition: GenericOutputModule.hpp:570
ScalarBuffer cPolymer_
Definition: GenericOutputModule.hpp:599
void assignBuffer(data::Solution &sol, std::string_view name, UnitSystem::measure measure, std::vector< Scalar > &buffer)
static bool allocBufferIfRequested(std::map< std::string, int > &rstKeywords, unsigned bufferSize, std::vector< Scalar > &buffer, std::string_view kw, bool supported, bool required=false)
std::unordered_map< std::string, std::vector< int > > regions_
Definition: GenericOutputModule.hpp:571
ScalarBuffer swmin_
Definition: GenericOutputModule.hpp:610
FlowsContainer< GetPropType< TypeTag, Properties::FluidSystem > > flowsC_
Definition: GenericOutputModule.hpp:640
std::map< std::pair< std::string, int >, double > blockData_
Definition: GenericOutputModule.hpp:645
ScalarBuffer gasDissolutionFactor_
Definition: GenericOutputModule.hpp:612
MechContainer< Scalar > mech_
Definition: GenericOutputModule.hpp:627
BioeffectsContainer< Scalar > bioeffectsC_
Definition: GenericOutputModule.hpp:620
ExtboContainer< Scalar > extboC_
Definition: GenericOutputModule.hpp:604
ScalarBuffer gasDissolutionFactorInWater_
Definition: GenericOutputModule.hpp:614
ScalarBuffer pcgw_
Definition: GenericOutputModule.hpp:622
LogOutputHelper< Scalar > logOutput_
Definition: GenericOutputModule.hpp:542
void assignPhaseProperties(data::Solution &sol, const PhasePropertyNames &names)
Move the phase density and viscosity buffers to sol under names.
std::vector< Scalar > ScalarBuffer
Definition: GenericOutputModule.hpp:325
ScalarBuffer somin_
Definition: GenericOutputModule.hpp:609
void updateTotalVolumesAndPressures_(const unsigned globalDofIdx, const IntensiveQuantities &intQuants, const double totVolume, const Scalar referencePorosity)
Definition: GenericOutputModule.hpp:362
InterRegFlowMap interRegionFlows_
Definition: GenericOutputModule.hpp:541
ScalarBuffer soMax_
Definition: GenericOutputModule.hpp:605
std::array< ScalarBuffer, numPhases > density_
Definition: GenericOutputModule.hpp:631
ScalarBuffer cFoam_
Definition: GenericOutputModule.hpp:600
ScalarBuffer waterVaporizationFactor_
Definition: GenericOutputModule.hpp:615
std::array< ScalarBuffer, numPhases > saturation_
Definition: GenericOutputModule.hpp:630
ScalarBuffer rsw_
Definition: GenericOutputModule.hpp:592
ScalarBuffer oilSaturationPressure_
Definition: GenericOutputModule.hpp:595
const EclipseState & eclState_
Definition: GenericOutputModule.hpp:535
ScalarBuffer ppcw_
Definition: GenericOutputModule.hpp:611
virtual void assignToSolution(data::Solution &sol)
Move all buffers to data::Solution.
ScalarBuffer cSalt_
Definition: GenericOutputModule.hpp:601
ScalarBuffer pcog_
Definition: GenericOutputModule.hpp:624
const Inplace * initialInplace() const
Definition: GenericOutputModule.hpp:274
ScalarBuffer swMax_
Definition: GenericOutputModule.hpp:606
ScalarBuffer rswSol_
Definition: GenericOutputModule.hpp:598
std::map< std::pair< std::string, int >, double > extraBlockData_
Definition: GenericOutputModule.hpp:648
ScalarBuffer fluidPressure_
Definition: GenericOutputModule.hpp:590
ScalarBuffer gasFormationVolumeFactor_
Definition: GenericOutputModule.hpp:585
virtual void setRestart(const data::Solution &sol, unsigned elemIdx, unsigned globalDofIndex)
std::vector< int > failedCellsPb_
Definition: GenericOutputModule.hpp:582
ScalarBuffer pSalt_
Definition: GenericOutputModule.hpp:602
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:101
void processElement(const ElementContext &elemCtx)
Modify the internal buffers according to the intensive quanties relevant for an element.
Definition: OutputBlackoilModule.hpp:363
void initializeFluxData()
Prepare for capturing connection fluxes, particularly to account for inter-region flows.
Definition: OutputBlackoilModule.hpp:652
void setupExtractors(const bool isSubStep, const int reportStepNum)
Setup list of active element-level data extractors.
Definition: OutputBlackoilModule.hpp:291
void processFluxes(const ElementContext &elemCtx, ActiveIndex &&activeIndex, CartesianIndex &&cartesianIndex)
Capture connection fluxes, particularly to account for inter-region flows.
Definition: OutputBlackoilModule.hpp:615
void setRestart(const data::Solution &sol, const unsigned elemIdx, const unsigned globalDofIndex) override
Restore the buffers this module owns from a restart file.
Definition: OutputBlackoilModule.hpp:807
void clearExtractors()
Clear list of active element-level data extractors.
Definition: OutputBlackoilModule.hpp:299
void allocBuffers(const unsigned bufferSize, const unsigned reportStepNum, const bool substep, const bool log, const bool forceRestartFieldAllocation)
Allocate memory for the scalar fields we would like to write to ECL output files.
Definition: OutputBlackoilModule.hpp:269
void outputFipAndResvLogToCSV(const std::size_t reportStepNum, const bool substep, const Parallel::Communication &comm)
Definition: OutputBlackoilModule.hpp:549
void allocFormulationBuffers(std::map< std::string, int > &rstKeywords, const unsigned bufferSize) override
Allocate the buffers of the quantities only the black-oil formulation produces, keyed on their restar...
Definition: OutputBlackoilModule.hpp:828
void assignToFluidState(FluidState &fs, unsigned elemIdx) const
Definition: OutputBlackoilModule.hpp:676
void assignToSolution(data::Solution &sol) override
Move all buffers to data::Solution.
Definition: OutputBlackoilModule.hpp:310
void initHysteresisParams(Simulator &simulator, unsigned elemIdx) const
Definition: OutputBlackoilModule.hpp:728
void updateFluidInPlace(const ElementContext &elemCtx)
Definition: OutputBlackoilModule.hpp:792
OutputBlackOilModule(const Simulator &simulator, const SummaryConfig &smryCfg, const CollectDataOnIORankType &collectOnIORank)
Definition: OutputBlackoilModule.hpp:154
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:498
const InterRegFlowMap & getInterRegFlows() const
Get read-only access to collection of inter-region flows.
Definition: OutputBlackoilModule.hpp:670
void processElementBlockData(const ElementContext &elemCtx)
Definition: OutputBlackoilModule.hpp:410
void finalizeFluxData()
Finalize capturing connection fluxes.
Definition: OutputBlackoilModule.hpp:662
void updateFluidInPlace(const unsigned globalDofIdx, const IntensiveQuantities &intQuants, const double totVolume)
Definition: OutputBlackoilModule.hpp:799
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:183
constexpr void ignoreUnused(T &&...) noexcept
Utility to silence "unused variable" warnings in lambdas.
Definition: OutputBlackoilModule.hpp:90
Definition: blackoilbioeffectsmodules.hh:45
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.
Definition: GenericOutputModule.hpp:410
std::string_view oilDensity
Definition: GenericOutputModule.hpp:411
Minimal characteristics of a cell from a simulation grid.
Definition: InterRegFlows.hpp:50
Context passed to element extractor functions.
Definition: OutputExtractor.hpp:208
Wrapping struct holding types used for block-level data extraction.
Definition: OutputExtractor.hpp:195
std::variant< ScalarEntry, PhaseEntry, TensorEntry > Entry
Descriptor for extractors.
Definition: OutputExtractor.hpp:262
std::unordered_map< int, std::vector< Exec > > ExecMap
A map of extraction executors, keyed by cartesian cell index.
Definition: OutputExtractor.hpp:277
static LgrExecMap setupLgrExecMap(std::map< std::tuple< std::string, int, int >, double > &lgrBlockData, const std::span< const Entry > handlers)
Setup an LGR-cell extractor executor map.
Definition: OutputExtractor.hpp:463
static ExecMap setupExecMap(std::map< std::pair< std::string, int >, double > &blockData, const std::span< const Entry > handlers)
Setup an extractor executor map from a map of evaluations to perform.
Definition: OutputExtractor.hpp:410
std::unordered_map< int, std::unordered_map< int, std::vector< Exec > > > LgrExecMap
Definition: OutputExtractor.hpp:452
static void process(const std::vector< Exec > &blockExtractors, const Context &ectx)
Process a list of block extractors.
Definition: OutputExtractor.hpp:435
Context passed to extractor functions.
Definition: OutputExtractor.hpp:77
int episodeIndex
Current report step.
Definition: OutputExtractor.hpp:80
Struct holding hysteresis parameters.
Definition: OutputExtractor.hpp:66
Scalar somin
Min oil saturation.
Definition: OutputExtractor.hpp:72
Scalar swmin
Min water saturation.
Definition: OutputExtractor.hpp:69
Scalar swmax
Max water saturation.
Definition: OutputExtractor.hpp:68
Scalar shmax
Max something.
Definition: OutputExtractor.hpp:71
Scalar sgmax
Max gas saturation.
Definition: OutputExtractor.hpp:70
Scalar somax
Max oil saturation.
Definition: OutputExtractor.hpp:67
Wrapping struct holding types used for element-level data extraction.
Definition: OutputExtractor.hpp:57
static void process(const Context &ectx, const std::vector< Entry > &extractors)
Process the given extractor entries.
Definition: OutputExtractor.hpp:160
static std::vector< Entry > removeInactive(std::array< Entry, size > &input)
Obtain vector of active extractors from an array of extractors.
Definition: OutputExtractor.hpp:123