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