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