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