23#ifndef OPM_INIT_STATE_EQUIL_IMPL_HPP
24#define OPM_INIT_STATE_EQUIL_IMPL_HPP
26#include <dune/grid/common/mcmgmapper.hh>
28#include <opm/common/OpmLog/OpmLog.hpp>
30#include <opm/grid/utility/RegionMapping.hpp>
31#include <opm/grid/LookUpData.hh>
33#include <opm/input/eclipse/EclipseState/EclipseState.hpp>
34#include <opm/input/eclipse/EclipseState/Tables/PbvdTable.hpp>
35#include <opm/input/eclipse/EclipseState/Tables/PdvdTable.hpp>
36#include <opm/input/eclipse/EclipseState/Tables/RsconstTable.hpp>
37#include <opm/input/eclipse/EclipseState/Tables/RsvdTable.hpp>
38#include <opm/input/eclipse/EclipseState/Tables/RtempvdTable.hpp>
39#include <opm/input/eclipse/EclipseState/Tables/RvvdTable.hpp>
40#include <opm/input/eclipse/EclipseState/Tables/RvwvdTable.hpp>
41#include <opm/input/eclipse/EclipseState/Tables/SaltvdTable.hpp>
42#include <opm/input/eclipse/EclipseState/Tables/SaltpvdTable.hpp>
44#include <opm/input/eclipse/Units/UnitSystem.hpp>
46#include <opm/material/fluidmatrixinteractions/EclMaterialLawManager.hpp>
47#include <opm/material/fluidsystems/BlackOilFluidSystem.hpp>
54#include <fmt/format.h>
69template <
typename CellRange,
class Scalar>
71 const std::vector<std::pair<Scalar, Scalar>>&
cellZMinMax,
73 std::array<Scalar,2>& span)
75 span[0] = std::numeric_limits<Scalar>::max();
76 span[1] = std::numeric_limits<Scalar>::lowest();
86 for (
const auto& cell : cells) {
90 span[0] = comm.min(span[0]);
91 span[1] = comm.max(span[1]);
97 const int numIntervals,
98 std::vector<std::pair<Scalar, Scalar>>& subdiv)
100 const auto h = (right - left) / numIntervals;
103 for (
auto i = 0*numIntervals; i < numIntervals; ++i) {
104 const auto start = end;
105 end = left + (i + 1)*h;
107 subdiv.emplace_back((start + end) / 2, h);
111template <
typename CellID,
typename Scalar>
112std::vector<std::pair<Scalar, Scalar>>
114 const std::pair<Scalar, Scalar> topbot,
115 const int numIntervals)
117 auto subdiv = std::vector<std::pair<Scalar, Scalar>>{};
118 subdiv.reserve(2 * numIntervals);
120 if (topbot.first > topbot.second) {
121 throw std::out_of_range {
122 "Negative thickness (inverted top/bottom faces) in cell "
128 2*numIntervals, subdiv);
133template <
class Scalar,
class Element>
136 typedef typename Element::Geometry Geometry;
137 static constexpr int zCoord = Element::dimension - 1;
140 const Geometry& geometry = element.geometry();
141 const int corners = geometry.corners();
142 for (
int i=0; i < corners; ++i)
143 zz += geometry.corner(i)[zCoord];
148template <
class Scalar,
class Element>
151 typedef typename Element::Geometry Geometry;
152 static constexpr int xCoord = Element::dimension - 3;
153 static constexpr int yCoord = Element::dimension - 2;
158 const Geometry& geometry = element.geometry();
159 const int corners = geometry.corners();
160 for (
int i=0; i < corners; ++i) {
161 xx += geometry.corner(i)[xCoord];
162 yy += geometry.corner(i)[yCoord];
164 return std::make_pair(xx/corners, yy/corners);
167template <
class Scalar,
class Element>
168std::pair<Scalar,Scalar>
cellZSpan(
const Element& element)
170 typedef typename Element::Geometry Geometry;
171 static constexpr int zCoord = Element::dimension - 1;
175 const Geometry& geometry = element.geometry();
176 const int corners = geometry.corners();
177 assert(corners == 8);
178 for (
int i=0; i < 4; ++i)
179 bot += geometry.corner(i)[zCoord];
180 for (
int i=4; i < corners; ++i)
181 top += geometry.corner(i)[zCoord];
183 return std::make_pair(bot/4, top/4);
186template <
class Scalar,
class Element>
189 typedef typename Element::Geometry Geometry;
190 static constexpr int zCoord = Element::dimension - 1;
191 const Geometry& geometry = element.geometry();
192 const int corners = geometry.corners();
193 assert(corners == 8);
194 auto min = std::numeric_limits<Scalar>::max();
195 auto max = std::numeric_limits<Scalar>::lowest();
198 for (
int i=0; i < corners; ++i) {
199 min = std::min(min,
static_cast<Scalar
>(geometry.corner(i)[zCoord]));
200 max = std::max(max,
static_cast<Scalar
>(geometry.corner(i)[zCoord]));
202 return std::make_pair(min, max);
205template<
class Scalar>
207 Scalar& dipAngle, Scalar& dipAzimuth)
209 const auto& Xc = cellCorners.
X;
210 const auto& Yc = cellCorners.
Y;
211 const auto& Zc = cellCorners.
Z;
213 Scalar v1x = Xc[1] - Xc[0];
214 Scalar v1y = Yc[1] - Yc[0];
215 Scalar v1z = Zc[1] - Zc[0];
217 Scalar v2x = Xc[2] - Xc[0];
218 Scalar v2y = Yc[2] - Yc[0];
219 Scalar v2z = Zc[2] - Zc[0];
222 Scalar nx = v1y * v2z - v1z * v2y;
223 Scalar ny = v1z * v2x - v1x * v2z;
224 Scalar nz = v1x * v2y - v1y * v2x;
227 Scalar norm = std::hypot(nx, ny, nz);
235 dipAngle = std::acos(std::abs(nz));
238 if (std::abs(nx) > 1e-10 || std::abs(ny) > 1e-10) {
239 dipAzimuth = std::atan2(ny, nx);
241 dipAzimuth = std::fmod(dipAzimuth + 2*std::numbers::pi_v<Scalar>, 2*std::numbers::pi_v<Scalar>);
247 const Scalar maxDip = std::numbers::pi_v<Scalar>/2 -
static_cast<Scalar
>(1e-6);
248 dipAngle = std::min(dipAngle, maxDip);
256template <
class Scalar,
class Element>
259 typedef typename Element::Geometry Geometry;
260 const Geometry& geometry = element.geometry();
261 static constexpr int zCoord = Element::dimension - 1;
262 static constexpr int yCoord = Element::dimension - 2;
263 static constexpr int xCoord = Element::dimension - 3;
264 const int corners = geometry.corners();
265 assert(corners == 8);
266 std::array<Scalar, 8> X {};
267 std::array<Scalar, 8> Y {};
268 std::array<Scalar, 8> Z {};
270 for (
int i = 0; i < corners; ++i) {
271 auto corner = geometry.corner(i);
272 X[i] = corner[xCoord];
273 Y[i] = corner[yCoord];
274 Z[i] = corner[zCoord];
280template<
class Scalar>
282 Scalar dipAngle, Scalar dipAzimuth,
283 const std::array<Scalar, 3>& referencePoint)
290 Scalar dx = x - referencePoint[0];
291 Scalar dy = y - referencePoint[1];
292 Scalar dz = z - referencePoint[2];
295 if (std::abs(dipAngle) < 1e-10) {
296 return referencePoint[2] + dz;
300 Scalar pointAzimuth = std::atan2(dy, dx);
303 Scalar azimuthDiff = pointAzimuth - dipAzimuth;
306 Scalar lateralDist = std::hypot(dx, dy);
309 Scalar lateralInDipDir = lateralDist * std::cos(azimuthDiff);
314 Scalar tvd = referencePoint[2] + dz * std::cos(dipAngle) + lateralInDipDir * std::sin(dipAngle);
319template<
class Scalar,
class RHS>
321 const std::array<Scalar,2>& span,
332 const Scalar h = stepsize();
333 const Scalar h2 = h / 2;
334 const Scalar h6 = h / 6;
340 f_.push_back(f(span_[0], y0));
342 for (
int i = 0; i < N; ++i) {
343 const Scalar x = span_[0] + i*h;
344 const Scalar y = y_.back();
346 const Scalar k1 = f_[i];
347 const Scalar k2 = f(x + h2, y + h2*k1);
348 const Scalar k3 = f(x + h2, y + h2*k2);
349 const Scalar k4 = f(x + h, y + h*k3);
351 y_.push_back(y + h6*(k1 + 2*(k2 + k3) + k4));
352 f_.push_back(f(x + h, y_.back()));
355 assert (y_.size() ==
typename std::vector<Scalar>::size_type(N + 1));
358template<
class Scalar,
class RHS>
364 const Scalar h = stepsize();
365 int i = (x - span_[0]) / h;
368 if (i < 0) { i = 0; }
369 if (N_ <= i) { i = N_ - 1; }
373 const Scalar t = (x - (span_[0] + i*h)) / h;
375 const Scalar y0 = y_[i], y1 = y_[i + 1];
376 const Scalar f0 = f_[i], f1 = f_[i + 1];
378 Scalar u = (1 - 2*t) * (y1 - y0);
379 u += h * ((t - 1)*f0 + t*f1);
381 u += (1 - t)*y0 + t*y1;
386template<
class Scalar,
class RHS>
390 return (span_[1] - span_[0]) / N_;
393namespace PhasePressODE {
395template<
class Flu
idSystem>
397Water(
const TabulatedFunction& tempVdTable,
398 const TabulatedFunction& saltVdTable,
399 const int pvtRegionIdx,
400 const Scalar normGrav)
401 : tempVdTable_(tempVdTable)
402 , saltVdTable_(saltVdTable)
403 , pvtRegionIdx_(pvtRegionIdx)
408template<
class Flu
idSystem>
409typename Water<FluidSystem>::Scalar
412 const Scalar press)
const
414 return this->density(depth, press) * g_;
417template<
class Flu
idSystem>
418typename Water<FluidSystem>::Scalar
421 const Scalar press)
const
424 Scalar saltConcentration = saltVdTable_.eval(depth,
true);
425 Scalar temp = tempVdTable_.eval(depth,
true);
426 Scalar rho = FluidSystem::waterPvt().inverseFormationVolumeFactor(pvtRegionIdx_,
431 rho *= FluidSystem::referenceDensity(FluidSystem::waterPhaseIdx, pvtRegionIdx_);
435template<
class Flu
idSystem,
class RS>
437Oil(
const TabulatedFunction& tempVdTable,
439 const int pvtRegionIdx,
440 const Scalar normGrav)
441 : tempVdTable_(tempVdTable)
443 , pvtRegionIdx_(pvtRegionIdx)
448template<
class Flu
idSystem,
class RS>
449typename Oil<FluidSystem,RS>::Scalar
452 const Scalar press)
const
454 return this->density(depth, press) * g_;
457template<
class Flu
idSystem,
class RS>
458typename Oil<FluidSystem,RS>::Scalar
461 const Scalar press)
const
463 const Scalar temp = tempVdTable_.eval(depth,
true);
465 if (FluidSystem::enableDissolvedGas() || FluidSystem::enableConstantRs())
466 rs = rs_(depth, press, temp);
469 if (rs >= FluidSystem::oilPvt().saturatedGasDissolutionFactor(pvtRegionIdx_, temp, press)) {
470 bOil = FluidSystem::oilPvt().saturatedInverseFormationVolumeFactor(pvtRegionIdx_, temp, press);
473 bOil = FluidSystem::oilPvt().inverseFormationVolumeFactor(pvtRegionIdx_, temp, press, rs);
475 Scalar rho = bOil * FluidSystem::referenceDensity(FluidSystem::oilPhaseIdx, pvtRegionIdx_);
476 if (FluidSystem::enableDissolvedGas() || FluidSystem::enableConstantRs()) {
477 rho += rs * bOil * FluidSystem::referenceDensity(FluidSystem::gasPhaseIdx, pvtRegionIdx_);
483template<
class Flu
idSystem,
class RV,
class RVW>
485Gas(
const TabulatedFunction& tempVdTable,
488 const int pvtRegionIdx,
489 const Scalar normGrav)
490 : tempVdTable_(tempVdTable)
493 , pvtRegionIdx_(pvtRegionIdx)
498template<
class Flu
idSystem,
class RV,
class RVW>
499typename Gas<FluidSystem,RV,RVW>::Scalar
502 const Scalar press)
const
504 return this->density(depth, press) * g_;
507template<
class Flu
idSystem,
class RV,
class RVW>
508typename Gas<FluidSystem,RV,RVW>::Scalar
511 const Scalar press)
const
513 const Scalar temp = tempVdTable_.eval(depth,
true);
515 if (FluidSystem::enableVaporizedOil())
516 rv = rv_(depth, press, temp);
519 if (FluidSystem::enableVaporizedWater())
520 rvw = rvw_(depth, press, temp);
524 if (FluidSystem::enableVaporizedOil() && FluidSystem::enableVaporizedWater()) {
525 if (rv >= FluidSystem::gasPvt().saturatedOilVaporizationFactor(pvtRegionIdx_, temp, press)
526 && rvw >= FluidSystem::gasPvt().saturatedWaterVaporizationFactor(pvtRegionIdx_, temp, press))
528 bGas = FluidSystem::gasPvt().saturatedInverseFormationVolumeFactor(pvtRegionIdx_, temp, press);
530 bGas = FluidSystem::gasPvt().inverseFormationVolumeFactor(pvtRegionIdx_, temp, press, rv, rvw);
532 Scalar rho = bGas * FluidSystem::referenceDensity(FluidSystem::gasPhaseIdx, pvtRegionIdx_);
533 rho += rv * bGas * FluidSystem::referenceDensity(FluidSystem::oilPhaseIdx, pvtRegionIdx_)
534 + rvw * bGas * FluidSystem::referenceDensity(FluidSystem::waterPhaseIdx, pvtRegionIdx_);
538 if (FluidSystem::enableVaporizedOil()){
539 if (rv >= FluidSystem::gasPvt().saturatedOilVaporizationFactor(pvtRegionIdx_, temp, press)) {
540 bGas = FluidSystem::gasPvt().saturatedInverseFormationVolumeFactor(pvtRegionIdx_, temp, press);
542 bGas = FluidSystem::gasPvt().inverseFormationVolumeFactor(pvtRegionIdx_,
548 Scalar rho = bGas * FluidSystem::referenceDensity(FluidSystem::gasPhaseIdx, pvtRegionIdx_);
549 rho += rv * bGas * FluidSystem::referenceDensity(FluidSystem::oilPhaseIdx, pvtRegionIdx_);
553 if (FluidSystem::enableVaporizedWater()){
554 if (rvw >= FluidSystem::gasPvt().saturatedWaterVaporizationFactor(pvtRegionIdx_, temp, press)) {
555 bGas = FluidSystem::gasPvt().saturatedInverseFormationVolumeFactor(pvtRegionIdx_, temp, press);
558 bGas = FluidSystem::gasPvt().inverseFormationVolumeFactor(pvtRegionIdx_,
564 Scalar rho = bGas * FluidSystem::referenceDensity(FluidSystem::gasPhaseIdx, pvtRegionIdx_);
565 rho += rvw * bGas * FluidSystem::referenceDensity(FluidSystem::waterPhaseIdx, pvtRegionIdx_);
570 bGas = FluidSystem::gasPvt().inverseFormationVolumeFactor(pvtRegionIdx_, temp,
574 Scalar rho = bGas * FluidSystem::referenceDensity(FluidSystem::gasPhaseIdx, pvtRegionIdx_);
581template<
class Flu
idSystem,
class Region>
583PressureTable<FluidSystem,Region>::
584PressureFunction<ODE>::PressureFunction(
const ODE& ode,
590 this->value_[Direction::Up] = std::make_unique<Distribution>
591 (ode, VSpan {{ ic.depth, span[0] }}, ic.pressure, nsample);
593 this->value_[Direction::Down] = std::make_unique<Distribution>
594 (ode, VSpan {{ ic.depth, span[1] }}, ic.pressure, nsample);
597template<
class Flu
idSystem,
class Region>
599PressureTable<FluidSystem,Region>::
600PressureFunction<ODE>::PressureFunction(
const PressureFunction& rhs)
601 : initial_(rhs.initial_)
603 this->value_[Direction::Up] =
604 std::make_unique<Distribution>(*rhs.value_[Direction::Up]);
606 this->value_[Direction::Down] =
607 std::make_unique<Distribution>(*rhs.value_[Direction::Down]);
610template<
class Flu
idSystem,
class Region>
612typename PressureTable<FluidSystem,Region>::template PressureFunction<ODE>&
617 this->initial_ = rhs.initial_;
619 this->value_[Direction::Up] =
620 std::make_unique<Distribution>(*rhs.value_[Direction::Up]);
622 this->value_[Direction::Down] =
623 std::make_unique<Distribution>(*rhs.value_[Direction::Down]);
628template<
class Flu
idSystem,
class Region>
630typename PressureTable<FluidSystem,Region>::template PressureFunction<ODE>&
635 this->initial_ = rhs.initial_;
636 this->value_ = std::move(rhs.value_);
641template<
class Flu
idSystem,
class Region>
644PressureTable<FluidSystem,Region>::
645PressureFunction<ODE>::
646value(
const Scalar depth)
const
648 if (depth < this->initial_.depth) {
650 return (*this->value_[Direction::Up])(depth);
652 else if (depth > this->initial_.depth) {
654 return (*this->value_[Direction::Down])(depth);
658 return this->initial_.pressure;
663template<
class Flu
idSystem,
class Region>
664template<
typename PressFunc>
665void PressureTable<FluidSystem,Region>::
666checkPtr(
const PressFunc* phasePress,
667 const std::string& phaseName)
const
669 if (phasePress !=
nullptr) {
return; }
671 throw std::invalid_argument {
672 "Phase pressure function for \"" + phaseName
673 +
"\" most not be null"
677template<
class Flu
idSystem,
class Region>
678typename PressureTable<FluidSystem,Region>::Strategy
679PressureTable<FluidSystem,Region>::
680selectEquilibrationStrategy(
const Region& reg)
const
682 if (!this->oilActive()) {
683 if (reg.datum() > reg.zwoc()) {
684 return &PressureTable::equil_WOG;
686 return &PressureTable::equil_GOW;
689 if (reg.datum() > reg.zwoc()) {
690 return &PressureTable::equil_WOG;
692 else if (reg.datum() < reg.zgoc()) {
693 return &PressureTable::equil_GOW;
696 return &PressureTable::equil_OWG;
700template<
class Flu
idSystem,
class Region>
701void PressureTable<FluidSystem,Region>::
702copyInPointers(
const PressureTable& rhs)
704 if (rhs.oil_ !=
nullptr) {
705 this->oil_ = std::make_unique<OPress>(*rhs.oil_);
708 if (rhs.gas_ !=
nullptr) {
709 this->gas_ = std::make_unique<GPress>(*rhs.gas_);
712 if (rhs.wat_ !=
nullptr) {
713 this->wat_ = std::make_unique<WPress>(*rhs.wat_);
717template <
class MaterialLawManager,
class Flu
idSystem,
class Region,
typename CellID>
720 const std::vector<Scalar>& swatInit)
721 : matLawMgr_(matLawMgr)
722 , swatInit_ (swatInit)
726template <
class MaterialLawManager,
class Flu
idSystem,
class Region,
typename CellID>
729 : matLawMgr_(rhs.matLawMgr_)
730 , swatInit_ (rhs.swatInit_)
732 , press_ (rhs.press_)
735 this->setEvaluationPoint(*rhs.evalPt_.position,
737 *rhs.evalPt_.ptable);
740template <
class MaterialLawManager,
class Flu
idSystem,
class Region,
typename CellID>
747 this->setEvaluationPoint(x, reg, ptable);
748 this->initializePhaseQuantities();
750 if (ptable.
gasActive()) { this->deriveGasSat(); }
752 if (ptable.
waterActive()) { this->deriveWaterSat(); }
755 if (this->isOverlappingTransition()) {
756 this->fixUnphysicalTransition();
759 if (ptable.
oilActive()) { this->deriveOilSat(); }
761 this->accountForScaledSaturations();
766template <
class MaterialLawManager,
class Flu
idSystem,
class Region,
typename CellID>
770 const PTable& ptable)
772 this->evalPt_.position = &x;
773 this->evalPt_.region = ®
774 this->evalPt_.ptable = &ptable;
777template <
class MaterialLawManager,
class Flu
idSystem,
class Region,
typename CellID>
778void PhaseSaturations<MaterialLawManager,FluidSystem,Region,CellID>::
779initializePhaseQuantities()
782 this->press_.reset();
784 const auto depth = this->evalPt_.position->depth;
785 const auto& ptable = *this->evalPt_.ptable;
787 if (ptable.oilActive()) {
788 this->press_.oil = ptable.oil(depth);
791 if (ptable.gasActive()) {
792 this->press_.gas = ptable.gas(depth);
795 if (ptable.waterActive()) {
796 this->press_.water = ptable.water(depth);
800template <
class MaterialLawManager,
class Flu
idSystem,
class Region,
typename CellID>
801void PhaseSaturations<MaterialLawManager, FluidSystem, Region, CellID>::deriveOilSat()
803 this->sat_.oil = 1.0 - this->sat_.water - this->sat_.gas;
806template <
class MaterialLawManager,
class Flu
idSystem,
class Region,
typename CellID>
807void PhaseSaturations<MaterialLawManager, FluidSystem, Region, CellID>::deriveGasSat()
809 auto& sg = this->sat_.gas;
811 const auto isIncr =
true;
812 const auto oilActive = this->evalPt_.ptable->oilActive();
814 if (this->isConstCapPress(this->gasPos())) {
818 const auto gas_contact = oilActive? this->evalPt_.region->zgoc() : this->evalPt_.region->zwoc();
819 sg = this->fromDepthTable(gas_contact,
820 this->gasPos(), isIncr);
830 const auto pw = oilActive? this->press_.oil : this->press_.water;
831 const auto pcgo = this->press_.gas - pw;
832 sg = this->invertCapPress(pcgo, this->gasPos(), isIncr);
836template <
class MaterialLawManager,
class Flu
idSystem,
class Region,
typename CellID>
837void PhaseSaturations<MaterialLawManager, FluidSystem, Region, CellID>::deriveWaterSat()
839 auto& sw = this->sat_.water;
841 const auto oilActive = this->evalPt_.ptable->oilActive();
844 sw = 1.0 - this->sat_.gas;
847 const auto isIncr =
false;
849 if (this->isConstCapPress(this->waterPos())) {
853 sw = this->fromDepthTable(this->evalPt_.region->zwoc(),
854 this->waterPos(), isIncr);
866 const auto pcow = this->press_.oil - this->press_.water;
868 if (this->swatInit_.empty()) {
869 sw = this->invertCapPress(pcow, this->waterPos(), isIncr);
872 auto [swout, newSwatInit] = this->applySwatInit(pcow);
874 sw = this->invertCapPress(pcow, this->waterPos(), isIncr);
883template <
class MaterialLawManager,
class Flu
idSystem,
class Region,
typename CellID>
884void PhaseSaturations<MaterialLawManager, FluidSystem, Region, CellID>::
885fixUnphysicalTransition()
887 auto& sg = this->sat_.gas;
888 auto& sw = this->sat_.water;
896 const auto pcgw = this->press_.gas - this->press_.water;
897 if (! this->swatInit_.empty()) {
901 auto [swout, newSwatInit] = this->applySwatInit(pcgw, sw);
903 const auto isIncr =
false;
904 sw = this->invertCapPress(pcgw, this->waterPos(), isIncr);
911 sw = satFromSumOfPcs<FluidSystem>
912 (this->matLawMgr_, this->waterPos(), this->gasPos(),
913 this->evalPt_.position->cell, pcgw);
916 this->fluidState_.setSaturation(this->oilPos(), 1.0 - sw - sg);
917 this->fluidState_.setSaturation(this->gasPos(), sg);
918 this->fluidState_.setSaturation(this->waterPos(), this->evalPt_
919 .ptable->waterActive() ? sw : 0.0);
922 this->computeMaterialLawCapPress();
923 this->press_.oil = this->press_.gas - this->materialLawCapPressGasOil();
926template <
class MaterialLawManager,
class Flu
idSystem,
class Region,
typename CellID>
927void PhaseSaturations<MaterialLawManager, FluidSystem, Region, CellID>::
928accountForScaledSaturations()
930 const auto gasActive = this->evalPt_.ptable->gasActive();
931 const auto watActive = this->evalPt_.ptable->waterActive();
932 const auto oilActive = this->evalPt_.ptable->oilActive();
934 auto sg = gasActive? this->sat_.gas : 0.0;
935 auto sw = watActive? this->sat_.water : 0.0;
936 auto so = oilActive? this->sat_.oil : 0.0;
938 this->fluidState_.setSaturation(this->waterPos(), sw);
939 this->fluidState_.setSaturation(this->oilPos(), so);
940 this->fluidState_.setSaturation(this->gasPos(), sg);
942 const auto& scaledDrainageInfo = this->matLawMgr_
943 .oilWaterScaledEpsInfoDrainage(this->evalPt_.position->cell);
945 const auto thresholdSat = 1.0e-6;
946 if (watActive && ((sw + thresholdSat) > scaledDrainageInfo.Swu)) {
950 this->fluidState_.setSaturation(this->waterPos(), scaledDrainageInfo.Swu);
952 this->fluidState_.setSaturation(this->oilPos(), so + sw - scaledDrainageInfo.Swu);
953 }
else if (gasActive) {
954 this->fluidState_.setSaturation(this->gasPos(), sg + sw - scaledDrainageInfo.Swu);
956 sw = scaledDrainageInfo.Swu;
957 this->computeMaterialLawCapPress();
961 this->press_.oil = this->press_.water + this->materialLawCapPressOilWater();
964 this->press_.gas = this->press_.water + this->materialLawCapPressGasWater();
968 if (gasActive && ((sg + thresholdSat) > scaledDrainageInfo.Sgu)) {
972 this->fluidState_.setSaturation(this->gasPos(), scaledDrainageInfo.Sgu);
974 this->fluidState_.setSaturation(this->oilPos(), so + sg - scaledDrainageInfo.Sgu);
975 }
else if (watActive) {
976 this->fluidState_.setSaturation(this->waterPos(), sw + sg - scaledDrainageInfo.Sgu);
978 sg = scaledDrainageInfo.Sgu;
979 this->computeMaterialLawCapPress();
983 this->press_.oil = this->press_.gas - this->materialLawCapPressGasOil();
986 this->press_.water = this->press_.gas - this->materialLawCapPressGasWater();
990 if (watActive && ((sw - thresholdSat) < scaledDrainageInfo.Swl)) {
994 this->fluidState_.setSaturation(this->waterPos(), scaledDrainageInfo.Swl);
996 this->fluidState_.setSaturation(this->oilPos(), so + sw - scaledDrainageInfo.Swl);
997 }
else if (gasActive) {
998 this->fluidState_.setSaturation(this->gasPos(), sg + sw - scaledDrainageInfo.Swl);
1000 sw = scaledDrainageInfo.Swl;
1001 this->computeMaterialLawCapPress();
1005 this->press_.water = this->press_.oil - this->materialLawCapPressOilWater();
1008 this->press_.water = this->press_.gas - this->materialLawCapPressGasWater();
1012 if (gasActive && ((sg - thresholdSat) < scaledDrainageInfo.Sgl)) {
1016 this->fluidState_.setSaturation(this->gasPos(), scaledDrainageInfo.Sgl);
1018 this->fluidState_.setSaturation(this->oilPos(), so + sg - scaledDrainageInfo.Sgl);
1019 }
else if (watActive) {
1020 this->fluidState_.setSaturation(this->waterPos(), sw + sg - scaledDrainageInfo.Sgl);
1022 sg = scaledDrainageInfo.Sgl;
1023 this->computeMaterialLawCapPress();
1027 this->press_.gas = this->press_.oil + this->materialLawCapPressGasOil();
1030 this->press_.gas = this->press_.water + this->materialLawCapPressGasWater();
1035template <
class MaterialLawManager,
class Flu
idSystem,
class Region,
typename CellID>
1036std::pair<typename FluidSystem::Scalar, bool>
1037PhaseSaturations<MaterialLawManager, FluidSystem, Region, CellID>::
1038applySwatInit(
const Scalar pcow)
1040 return this->applySwatInit(pcow, this->swatInit_[this->evalPt_.position->cell]);
1043template <
class MaterialLawManager,
class Flu
idSystem,
class Region,
typename CellID>
1044std::pair<typename FluidSystem::Scalar, bool>
1045PhaseSaturations<MaterialLawManager, FluidSystem, Region, CellID>::
1046applySwatInit(
const Scalar pcow,
const Scalar sw)
1048 return this->matLawMgr_.applySwatinit(this->evalPt_.position->cell, pcow, sw);
1051template <
class MaterialLawManager,
class Flu
idSystem,
class Region,
typename CellID>
1052void PhaseSaturations<MaterialLawManager, FluidSystem, Region, CellID>::
1053computeMaterialLawCapPress()
1055 const auto& matParams = this->matLawMgr_
1056 .materialLawParams(this->evalPt_.position->cell);
1058 this->matLawCapPress_.fill(0.0);
1059 MaterialLaw::capillaryPressures(this->matLawCapPress_,
1060 matParams, this->fluidState_);
1063template <
class MaterialLawManager,
class Flu
idSystem,
class Region,
typename CellID>
1064typename FluidSystem::Scalar
1065PhaseSaturations<MaterialLawManager, FluidSystem, Region, CellID>::
1066materialLawCapPressGasOil()
const
1068 return this->matLawCapPress_[this->oilPos()]
1069 + this->matLawCapPress_[this->gasPos()];
1072template <
class MaterialLawManager,
class Flu
idSystem,
class Region,
typename CellID>
1073typename FluidSystem::Scalar
1074PhaseSaturations<MaterialLawManager, FluidSystem, Region, CellID>::
1075materialLawCapPressOilWater()
const
1077 return this->matLawCapPress_[this->oilPos()]
1078 - this->matLawCapPress_[this->waterPos()];
1081template <
class MaterialLawManager,
class Flu
idSystem,
class Region,
typename CellID>
1082typename FluidSystem::Scalar
1083PhaseSaturations<MaterialLawManager, FluidSystem, Region, CellID>::
1084materialLawCapPressGasWater()
const
1086 return this->matLawCapPress_[this->gasPos()]
1087 - this->matLawCapPress_[this->waterPos()];
1090template <
class MaterialLawManager,
class Flu
idSystem,
class Region,
typename CellID>
1091bool PhaseSaturations<MaterialLawManager, FluidSystem, Region, CellID>::
1092isConstCapPress(
const PhaseIdx phaseIdx)
const
1094 return isConstPc<FluidSystem>
1095 (this->matLawMgr_, phaseIdx, this->evalPt_.position->cell);
1098template <
class MaterialLawManager,
class Flu
idSystem,
class Region,
typename CellID>
1099bool PhaseSaturations<MaterialLawManager, FluidSystem, Region, CellID>::
1100isOverlappingTransition()
const
1102 return this->evalPt_.ptable->gasActive()
1103 && this->evalPt_.ptable->waterActive()
1104 && ((this->sat_.gas + this->sat_.water) > 1.0);
1107template <
class MaterialLawManager,
class Flu
idSystem,
class Region,
typename CellID>
1108typename FluidSystem::Scalar
1109PhaseSaturations<MaterialLawManager, FluidSystem, Region, CellID>::
1110fromDepthTable(
const Scalar contactdepth,
1111 const PhaseIdx phasePos,
1112 const bool isincr)
const
1114 return satFromDepth<FluidSystem>
1115 (this->matLawMgr_, this->evalPt_.position->depth,
1116 contactdepth,
static_cast<int>(phasePos),
1117 this->evalPt_.position->cell, isincr);
1120template <
class MaterialLawManager,
class Flu
idSystem,
class Region,
typename CellID>
1121typename FluidSystem::Scalar
1122PhaseSaturations<MaterialLawManager, FluidSystem, Region, CellID>::
1123invertCapPress(
const Scalar pc,
1124 const PhaseIdx phasePos,
1125 const bool isincr)
const
1127 return satFromPc<FluidSystem>
1128 (this->matLawMgr_,
static_cast<int>(phasePos),
1129 this->evalPt_.position->cell, pc, isincr);
1132template<
class Flu
idSystem,
class Region>
1135 const int samplePoints)
1137 , nsample_(samplePoints)
1141template <
class Flu
idSystem,
class Region>
1144 : gravity_(rhs.gravity_)
1145 , nsample_(rhs.nsample_)
1147 this->copyInPointers(rhs);
1150template <
class Flu
idSystem,
class Region>
1153 : gravity_(rhs.gravity_)
1154 , nsample_(rhs.nsample_)
1155 , oil_ (std::move(rhs.oil_))
1156 , gas_ (std::move(rhs.gas_))
1157 , wat_ (std::move(rhs.wat_))
1161template <
class Flu
idSystem,
class Region>
1166 this->gravity_ = rhs.gravity_;
1167 this->nsample_ = rhs.nsample_;
1168 this->copyInPointers(rhs);
1173template <
class Flu
idSystem,
class Region>
1178 this->gravity_ = rhs.gravity_;
1179 this->nsample_ = rhs.nsample_;
1181 this->oil_ = std::move(rhs.oil_);
1182 this->gas_ = std::move(rhs.gas_);
1183 this->wat_ = std::move(rhs.wat_);
1188template <
class Flu
idSystem,
class Region>
1194 auto equil = this->selectEquilibrationStrategy(reg);
1196 (this->*equil)(reg, span);
1199template <
class Flu
idSystem,
class Region>
1203 return FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx);
1206template <
class Flu
idSystem,
class Region>
1210 return FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx);
1213template <
class Flu
idSystem,
class Region>
1217 return FluidSystem::phaseIsActive(FluidSystem::waterPhaseIdx);
1220template <
class Flu
idSystem,
class Region>
1221typename FluidSystem::Scalar
1225 this->checkPtr(this->oil_.get(),
"OIL");
1227 return this->oil_->value(depth);
1230template <
class Flu
idSystem,
class Region>
1231typename FluidSystem::Scalar
1235 this->checkPtr(this->gas_.get(),
"GAS");
1237 return this->gas_->value(depth);
1241template <
class Flu
idSystem,
class Region>
1242typename FluidSystem::Scalar
1246 this->checkPtr(this->wat_.get(),
"WATER");
1248 return this->wat_->value(depth);
1251template <
class Flu
idSystem,
class Region>
1253equil_WOG(
const Region& reg,
const VSpan& span)
1258 if (! this->waterActive()) {
1259 throw std::invalid_argument {
1260 "Don't know how to interpret EQUIL datum depth in "
1261 "WATER zone in model without active water phase"
1266 const auto ic =
typename WPress::InitCond {
1267 reg.datum(), reg.pressure()
1270 this->makeWatPressure(ic, reg, span);
1273 if (this->oilActive()) {
1275 const auto ic =
typename OPress::InitCond {
1277 this->water(reg.zwoc()) + reg.pcowWoc()
1280 this->makeOilPressure(ic, reg, span);
1283 if (this->gasActive() && this->oilActive()) {
1285 const auto ic =
typename GPress::InitCond {
1287 this->oil(reg.zgoc()) + reg.pcgoGoc()
1290 this->makeGasPressure(ic, reg, span);
1291 }
else if (this->gasActive() && !this->oilActive()) {
1293 const auto ic =
typename GPress::InitCond {
1295 this->water(reg.zwoc()) + reg.pcowWoc()
1297 this->makeGasPressure(ic, reg, span);
1301template <
class Flu
idSystem,
class Region>
1302void PressureTable<FluidSystem, Region>::
1303equil_GOW(
const Region& reg,
const VSpan& span)
1308 if (! this->gasActive()) {
1309 throw std::invalid_argument {
1310 "Don't know how to interpret EQUIL datum depth in "
1311 "GAS zone in model without active gas phase"
1316 const auto ic =
typename GPress::InitCond {
1317 reg.datum(), reg.pressure()
1320 this->makeGasPressure(ic, reg, span);
1323 if (this->oilActive()) {
1325 const auto ic =
typename OPress::InitCond {
1327 this->gas(reg.zgoc()) - reg.pcgoGoc()
1329 this->makeOilPressure(ic, reg, span);
1332 if (this->waterActive() && this->oilActive()) {
1334 const auto ic =
typename WPress::InitCond {
1336 this->oil(reg.zwoc()) - reg.pcowWoc()
1339 this->makeWatPressure(ic, reg, span);
1340 }
else if (this->waterActive() && !this->oilActive()) {
1342 const auto ic =
typename WPress::InitCond {
1344 this->gas(reg.zwoc()) - reg.pcowWoc()
1346 this->makeWatPressure(ic, reg, span);
1350template <
class Flu
idSystem,
class Region>
1351void PressureTable<FluidSystem, Region>::
1352equil_OWG(
const Region& reg,
const VSpan& span)
1357 if (! this->oilActive()) {
1358 throw std::invalid_argument {
1359 "Don't know how to interpret EQUIL datum depth in "
1360 "OIL zone in model without active oil phase"
1365 const auto ic =
typename OPress::InitCond {
1366 reg.datum(), reg.pressure()
1369 this->makeOilPressure(ic, reg, span);
1372 if (this->waterActive()) {
1374 const auto ic =
typename WPress::InitCond {
1376 this->oil(reg.zwoc()) - reg.pcowWoc()
1379 this->makeWatPressure(ic, reg, span);
1382 if (this->gasActive()) {
1384 const auto ic =
typename GPress::InitCond {
1386 this->oil(reg.zgoc()) + reg.pcgoGoc()
1388 this->makeGasPressure(ic, reg, span);
1392template <
class Flu
idSystem,
class Region>
1393void PressureTable<FluidSystem, Region>::
1394makeOilPressure(
const typename OPress::InitCond& ic,
1398 const auto drho = OilPressODE {
1399 reg.tempVdTable(), reg.dissolutionCalculator(),
1400 reg.pvtIdx(), this->gravity_
1403 this->oil_ = std::make_unique<OPress>(drho, ic, this->nsample_, span);
1406template <
class Flu
idSystem,
class Region>
1407void PressureTable<FluidSystem, Region>::
1408makeGasPressure(
const typename GPress::InitCond& ic,
1412 const auto drho = GasPressODE {
1413 reg.tempVdTable(), reg.evaporationCalculator(), reg.waterEvaporationCalculator(),
1414 reg.pvtIdx(), this->gravity_
1417 this->gas_ = std::make_unique<GPress>(drho, ic, this->nsample_, span);
1420template <
class Flu
idSystem,
class Region>
1421void PressureTable<FluidSystem, Region>::
1422makeWatPressure(
const typename WPress::InitCond& ic,
1426 const auto drho = WatPressODE {
1427 reg.tempVdTable(), reg.saltVdTable(), reg.pvtIdx(), this->gravity_
1430 this->wat_ = std::make_unique<WPress>(drho, ic, this->nsample_, span);
1435namespace DeckDependent {
1437std::vector<EquilRecord>
1440 const auto& init = state.getInitConfig();
1442 if(!init.hasEquil()) {
1443 throw std::domain_error(
"Deck does not provide equilibration data.");
1446 const auto& equil = init.getEquil();
1447 return { equil.begin(), equil.end() };
1450template<
class Gr
idView>
1453 const GridView& gridview)
1455 std::vector<int> eqlnum(gridview.size(0), 0);
1457 if (eclipseState.fieldProps().has_int(
"EQLNUM")) {
1468 eqlnum = lookUpData.template assignFieldPropsIntOnLeaf<int>(
1469 eclipseState.fieldProps(),
"EQLNUM",
true);
1472 const int num_regions = eclipseState.getTableManager().getEqldims().getNumEquilRegions();
1473 if (std::ranges::any_of(eqlnum, [num_regions](
int n){
return n >= num_regions;})) {
1474 throw std::runtime_error(
"Values larger than maximum Equil regions " +
1477 if (std::ranges::any_of(eqlnum, [](
int n){
return n < 0;})) {
1478 throw std::runtime_error(
"zero or negative values provided in EQLNUM");
1485template<
class FluidSystem,
1488 class ElementMapper,
1489 class CartesianIndexMapper>
1490template<
class MaterialLawManager>
1491InitialStateComputer<FluidSystem,
1495 CartesianIndexMapper>::
1496InitialStateComputer(MaterialLawManager& materialLawManager,
1497 const EclipseState& eclipseState,
1499 const GridView& gridView,
1500 const CartesianIndexMapper& cartMapper,
1502 const int num_pressure_points,
1503 const bool applySwatInit)
1504 : temperature_(grid.size(0), eclipseState.getTableManager().rtemp()),
1505 saltConcentration_(grid.size(0)),
1506 saltSaturation_(grid.size(0)),
1507 pp_(FluidSystem::numPhases,
1508 std::vector<Scalar>(grid.size(0))),
1509 sat_(FluidSystem::numPhases,
1510 std::vector<Scalar>(grid.size(0))),
1514 cartesianIndexMapper_(cartMapper),
1515 num_pressure_points_(num_pressure_points)
1518 if (applySwatInit) {
1519 if (eclipseState.fieldProps().has_double(
"SWATINIT")) {
1526 lookUpData.assignFieldPropsDoubleOnLeaf(eclipseState.fieldProps(),
"SWATINIT");
1527 if constexpr (std::is_same_v<Scalar, double>) {
1528 swatInit_ = std::move(input);
1530 swatInit_.assign(input.begin(), input.end());
1537 const auto& num_aquifers = eclipseState.aquifer().numericalAquifers();
1538 updateCellProps_(gridView, num_aquifers);
1541 const std::vector<EquilRecord> rec =
getEquil(eclipseState);
1542 const auto& tables = eclipseState.getTableManager();
1544 const RegionMapping<> eqlmap(
equilnum(eclipseState, gridView));
1545 const int invalidRegion = -1;
1546 regionPvtIdx_.resize(rec.size(), invalidRegion);
1547 setRegionPvtIdx(eclipseState, gridView, eqlmap);
1550 rsFunc_.reserve(rec.size());
1552 auto getArray = [](
const std::vector<double>& input)
1554 if constexpr (std::is_same_v<Scalar,double>) {
1557 std::vector<Scalar> output;
1558 output.resize(input.size());
1559 std::ranges::copy(input, output.begin());
1564 if (FluidSystem::enableDissolvedGas()) {
1565 for (std::size_t i = 0; i < rec.size(); ++i) {
1566 if (eqlmap.cells(i).empty()) {
1570 const int pvtIdx = regionPvtIdx_[i];
1571 if (!rec[i].liveOilInitConstantRs()) {
1572 const TableContainer& rsvdTables = tables.getRsvdTables();
1573 const TableContainer& pbvdTables = tables.getPbvdTables();
1574 if (rsvdTables.size() > 0) {
1575 const RsvdTable& rsvdTable = rsvdTables.getTable<RsvdTable>(i);
1576 auto depthColumn = getArray(rsvdTable.getColumn(
"DEPTH").vectorCopy());
1577 auto rsColumn = getArray(rsvdTable.getColumn(
"RS").vectorCopy());
1579 depthColumn, rsColumn));
1580 }
else if (pbvdTables.size() > 0) {
1581 const PbvdTable& pbvdTable = pbvdTables.getTable<PbvdTable>(i);
1582 auto depthColumn = getArray(pbvdTable.getColumn(
"DEPTH").vectorCopy());
1583 auto pbubColumn = getArray(pbvdTable.getColumn(
"PBUB").vectorCopy());
1585 depthColumn, pbubColumn));
1588 throw std::runtime_error(
"Cannot initialise: RSVD or PBVD table not available.");
1593 if (rec[i].gasOilContactDepth() != rec[i].datumDepth()) {
1594 throw std::runtime_error(
"Cannot initialise: when no explicit RSVD table is given, \n"
1595 "datum depth must be at the gas-oil-contact. "
1596 "In EQUIL region "+
std::to_string(i + 1)+
" (counting from 1), this does not hold.");
1598 const Scalar pContact = rec[i].datumDepthPressure();
1599 const Scalar TContact = 273.15 + 20;
1604 else if (FluidSystem::enableConstantRs() && tables.hasTables(
"RSCONST")) {
1605 const auto& rsconstTables = tables.getRsconstTables();
1607 if (rsconstTables.empty()) {
1608 for (std::size_t i = 0; i < rec.size(); ++i) {
1614 const auto& rsconstTable = rsconstTables.getTable<RsconstTable>(0);
1616 const auto rsConst = rsconstTable.getRsColumn().front();
1617 const auto pBub = rsconstTable.getPbubColumn().front();
1619 const auto& units = eclipseState.getUnits();
1621 OpmLog::info(fmt::format(
"Using RSCONST keyword: Rs = {:.2} [{}], Pb = {:.2} [{}]",
1622 units.from_si(UnitSystem::measure::gas_oil_ratio, rsConst),
1623 units.name (UnitSystem::measure::gas_oil_ratio),
1624 units.from_si(UnitSystem::measure::pressure, pBub),
1625 units.name (UnitSystem::measure::pressure)));
1627 for (std::size_t i = 0; i < rec.size(); ++i) {
1633 for (std::size_t i = 0; i < rec.size(); ++i) {
1639 rvFunc_.reserve(rec.size());
1640 if (FluidSystem::enableVaporizedOil()) {
1641 for (std::size_t i = 0; i < rec.size(); ++i) {
1642 if (eqlmap.cells(i).empty()) {
1646 const int pvtIdx = regionPvtIdx_[i];
1647 if (!rec[i].wetGasInitConstantRv()) {
1648 const TableContainer& rvvdTables = tables.getRvvdTables();
1649 const TableContainer& pdvdTables = tables.getPdvdTables();
1651 if (rvvdTables.size() > 0) {
1652 const RvvdTable& rvvdTable = rvvdTables.getTable<RvvdTable>(i);
1653 auto depthColumn = getArray(rvvdTable.getColumn(
"DEPTH").vectorCopy());
1654 auto rvColumn = getArray(rvvdTable.getColumn(
"RV").vectorCopy());
1656 depthColumn, rvColumn));
1657 }
else if (pdvdTables.size() > 0) {
1658 const PdvdTable& pdvdTable = pdvdTables.getTable<PdvdTable>(i);
1659 auto depthColumn = getArray(pdvdTable.getColumn(
"DEPTH").vectorCopy());
1660 auto pdewColumn = getArray(pdvdTable.getColumn(
"PDEW").vectorCopy());
1662 depthColumn, pdewColumn));
1664 throw std::runtime_error(
"Cannot initialise: RVVD or PDCD table not available.");
1668 if (rec[i].gasOilContactDepth() != rec[i].datumDepth()) {
1669 throw std::runtime_error(
1670 "Cannot initialise: when no explicit RVVD table is given, \n"
1671 "datum depth must be at the gas-oil-contact. "
1672 "In EQUIL region "+
std::to_string(i + 1)+
" (counting from 1), this does not hold.");
1674 const Scalar pContact = rec[i].datumDepthPressure() + rec[i].gasOilContactCapillaryPressure();
1675 const Scalar TContact = 273.15 + 20;
1681 for (std::size_t i = 0; i < rec.size(); ++i) {
1686 rvwFunc_.reserve(rec.size());
1687 if (FluidSystem::enableVaporizedWater()) {
1688 for (std::size_t i = 0; i < rec.size(); ++i) {
1689 if (eqlmap.cells(i).empty()) {
1693 const int pvtIdx = regionPvtIdx_[i];
1694 if (!rec[i].humidGasInitConstantRvw()) {
1695 const TableContainer& rvwvdTables = tables.getRvwvdTables();
1697 if (rvwvdTables.size() > 0) {
1698 const RvwvdTable& rvwvdTable = rvwvdTables.getTable<RvwvdTable>(i);
1699 auto depthColumn = getArray(rvwvdTable.getColumn(
"DEPTH").vectorCopy());
1700 auto rvwvdColumn = getArray(rvwvdTable.getColumn(
"RVWVD").vectorCopy());
1702 depthColumn, rvwvdColumn));
1704 throw std::runtime_error(
"Cannot initialise: RVWVD table not available.");
1708 const auto oilActive = FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx);
1710 if (rec[i].gasOilContactDepth() != rec[i].datumDepth()) {
1712 const auto msg =
"No explicit RVWVD table is given for EQUIL region " +
std::to_string(i + 1) +
". \n"
1713 "and datum depth is not at the gas-oil-contact. \n"
1714 "Rvw is set to 0.0 in all cells. \n";
1715 OpmLog::warning(msg);
1719 const Scalar pContact = rec[i].datumDepthPressure() + rec[i].gasOilContactCapillaryPressure();
1720 const Scalar TContact = 273.15 + 20;
1727 if (rec[i].waterOilContactDepth() != rec[i].datumDepth()) {
1729 const auto msg =
"No explicit RVWVD table is given for EQUIL region " +
std::to_string(i + 1) +
". \n"
1730 "and datum depth is not at the gas-water-contact. \n"
1731 "Rvw is set to 0.0 in all cells. \n";
1732 OpmLog::warning(msg);
1735 const Scalar pContact = rec[i].datumDepthPressure() + rec[i].waterOilContactCapillaryPressure();
1736 const Scalar TContact = 273.15 + 20;
1744 for (std::size_t i = 0; i < rec.size(); ++i) {
1750 updateInitialTemperature_(eclipseState, eqlmap);
1753 updateInitialSaltConcentration_(eclipseState, eqlmap);
1756 updateInitialSaltSaturation_(eclipseState, eqlmap);
1759 const auto& comm = grid.comm();
1760 calcPressSatRsRv(eqlmap, rec, materialLawManager, gridView, comm, grav);
1763 applyNumericalAquifers_(gridView, num_aquifers,
1764 eclipseState.runspec().co2Storage() ||
1765 eclipseState.runspec().h2Storage());
1771template<
class FluidSystem,
1774 class ElementMapper,
1775 class CartesianIndexMapper>
1781 CartesianIndexMapper>::
1782updateInitialTemperature_(
const EclipseState& eclState,
const RMap& reg)
1784 const int numEquilReg = rsFunc_.size();
1785 tempVdTable_.resize(numEquilReg);
1786 const auto& tables = eclState.getTableManager();
1787 if (!tables.hasTables(
"RTEMPVD")) {
1788 std::vector<Scalar> x = {0.0,1.0};
1789 std::vector<Scalar> y = {
static_cast<Scalar
>(tables.rtemp()),
1790 static_cast<Scalar
>(tables.rtemp())};
1791 for (
auto& table : this->tempVdTable_) {
1792 table.setXYContainers(x, y);
1795 const TableContainer& tempvdTables = tables.getRtempvdTables();
1796 for (std::size_t i = 0; i < tempvdTables.size(); ++i) {
1797 const RtempvdTable& tempvdTable = tempvdTables.getTable<RtempvdTable>(i);
1798 tempVdTable_[i].setXYContainers(tempvdTable.getDepthColumn(), tempvdTable.getTemperatureColumn());
1799 const auto& cells = reg.cells(i);
1800 for (
const auto& cell : cells) {
1801 const Scalar depth = cellCenterDepth_[cell];
1802 this->temperature_[cell] = tempVdTable_[i].eval(depth,
true);
1808template<
class FluidSystem,
1811 class ElementMapper,
1812 class CartesianIndexMapper>
1814void InitialStateComputer<FluidSystem,
1818 CartesianIndexMapper>::
1819updateInitialSaltConcentration_(
const EclipseState& eclState,
const RMap& reg)
1821 const int numEquilReg = rsFunc_.size();
1822 saltVdTable_.resize(numEquilReg);
1823 const auto& tables = eclState.getTableManager();
1824 const TableContainer& saltvdTables = tables.getSaltvdTables();
1827 if (saltvdTables.empty()) {
1828 std::vector<Scalar> x = {0.0,1.0};
1829 std::vector<Scalar> y = {0.0,0.0};
1830 for (
auto& table : this->saltVdTable_) {
1831 table.setXYContainers(x, y);
1834 for (std::size_t i = 0; i < saltvdTables.size(); ++i) {
1835 const SaltvdTable& saltvdTable = saltvdTables.getTable<SaltvdTable>(i);
1836 saltVdTable_[i].setXYContainers(saltvdTable.getDepthColumn(), saltvdTable.getSaltColumn());
1838 const auto& cells = reg.cells(i);
1839 for (
const auto& cell : cells) {
1840 const Scalar depth = cellCenterDepth_[cell];
1841 this->saltConcentration_[cell] = saltVdTable_[i].eval(depth,
true);
1847template<
class FluidSystem,
1850 class ElementMapper,
1851 class CartesianIndexMapper>
1853void InitialStateComputer<FluidSystem,
1857 CartesianIndexMapper>::
1858updateInitialSaltSaturation_(
const EclipseState& eclState,
const RMap& reg)
1860 const int numEquilReg = rsFunc_.size();
1861 saltpVdTable_.resize(numEquilReg);
1862 const auto& tables = eclState.getTableManager();
1863 const TableContainer& saltpvdTables = tables.getSaltpvdTables();
1865 for (std::size_t i = 0; i < saltpvdTables.size(); ++i) {
1866 const SaltpvdTable& saltpvdTable = saltpvdTables.getTable<SaltpvdTable>(i);
1867 saltpVdTable_[i].setXYContainers(saltpvdTable.getDepthColumn(), saltpvdTable.getSaltpColumn());
1869 const auto& cells = reg.cells(i);
1870 for (
const auto& cell : cells) {
1871 const Scalar depth = cellCenterDepth_[cell];
1872 this->saltSaturation_[cell] = saltpVdTable_[i].eval(depth,
true);
1877template<
class FluidSystem,
1880 class ElementMapper,
1881 class CartesianIndexMapper>
1882void InitialStateComputer<FluidSystem,
1886 CartesianIndexMapper>::
1887updateCellProps_(
const GridView& gridView,
1888 const NumericalAquifers& aquifer)
1890 ElementMapper elemMapper(gridView, Dune::mcmgElementLayout());
1891 int numElements = gridView.size(0);
1892 cellCenterDepth_.resize(numElements);
1893 cellCenterXY_.resize(numElements);
1894 cellCorners_.resize(numElements);
1895 cellZSpan_.resize(numElements);
1896 cellZMinMax_.resize(numElements);
1898 auto elemIt = gridView.template begin<0>();
1899 const auto& elemEndIt = gridView.template end<0>();
1900 const auto num_aqu_cells = aquifer.allAquiferCells();
1901 for (; elemIt != elemEndIt; ++elemIt) {
1902 const Element& element = *elemIt;
1903 const unsigned int elemIdx = elemMapper.index(element);
1904 cellCenterDepth_[elemIdx] = Details::cellCenterDepth<Scalar>(element);
1905 cellCenterXY_[elemIdx] = Details::cellCenterXY<Scalar>(element);
1906 cellCorners_[elemIdx] = Details::getCellCornerXY<Scalar>(element);
1907 const auto cartIx = cartesianIndexMapper_.cartesianIndex(elemIdx);
1908 cellZSpan_[elemIdx] = Details::cellZSpan<Scalar>(element);
1909 cellZMinMax_[elemIdx] = Details::cellZMinMax<Scalar>(element);
1910 if (!num_aqu_cells.empty()) {
1911 const auto search = num_aqu_cells.find(cartIx);
1912 if (search != num_aqu_cells.end()) {
1913 const auto* aqu_cell = num_aqu_cells.at(cartIx);
1914 const Scalar depth_change_num_aqu = aqu_cell->depth - cellCenterDepth_[elemIdx];
1915 cellCenterDepth_[elemIdx] += depth_change_num_aqu;
1916 cellZSpan_[elemIdx].first += depth_change_num_aqu;
1917 cellZSpan_[elemIdx].second += depth_change_num_aqu;
1918 cellZMinMax_[elemIdx].first += depth_change_num_aqu;
1919 cellZMinMax_[elemIdx].second += depth_change_num_aqu;
1925template<
class FluidSystem,
1928 class ElementMapper,
1929 class CartesianIndexMapper>
1930void InitialStateComputer<FluidSystem,
1934 CartesianIndexMapper>::
1935applyNumericalAquifers_(
const GridView& gridView,
1936 const NumericalAquifers& aquifer,
1937 const bool co2store_or_h2store)
1939 const auto num_aqu_cells = aquifer.allAquiferCells();
1940 if (num_aqu_cells.empty())
return;
1943 bool oil_as_brine = co2store_or_h2store && FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx);
1944 const auto watPos = oil_as_brine? FluidSystem::oilPhaseIdx : FluidSystem::waterPhaseIdx;
1945 if (!FluidSystem::phaseIsActive(watPos)){
1946 throw std::logic_error {
"Water phase has to be active for numerical aquifer case" };
1949 ElementMapper elemMapper(gridView, Dune::mcmgElementLayout());
1950 auto elemIt = gridView.template begin<0>();
1951 const auto& elemEndIt = gridView.template end<0>();
1952 const auto oilPos = FluidSystem::oilPhaseIdx;
1953 const auto gasPos = FluidSystem::gasPhaseIdx;
1954 for (; elemIt != elemEndIt; ++elemIt) {
1955 const Element& element = *elemIt;
1956 const unsigned int elemIdx = elemMapper.index(element);
1957 const auto cartIx = cartesianIndexMapper_.cartesianIndex(elemIdx);
1958 const auto search = num_aqu_cells.find(cartIx);
1959 if (search != num_aqu_cells.end()) {
1961 this->sat_[watPos][elemIdx] = 1.;
1963 if (!co2store_or_h2store && FluidSystem::phaseIsActive(oilPos)) {
1964 this->sat_[oilPos][elemIdx] = 0.;
1967 if (FluidSystem::phaseIsActive(gasPos)) {
1968 this->sat_[gasPos][elemIdx] = 0.;
1970 const auto* aqu_cell = num_aqu_cells.at(cartIx);
1971 const auto msg = fmt::format(
"FOR AQUIFER CELL AT ({}, {}, {}) OF NUMERICAL "
1972 "AQUIFER {}, WATER SATURATION IS SET TO BE UNITY",
1973 aqu_cell->I+1, aqu_cell->J+1, aqu_cell->K+1, aqu_cell->aquifer_id);
1978 if (aqu_cell->init_pressure) {
1979 const Scalar pres = *(aqu_cell->init_pressure);
1980 this->pp_[watPos][elemIdx] = pres;
1981 if (FluidSystem::phaseIsActive(gasPos)) {
1982 this->pp_[gasPos][elemIdx] = pres;
1984 if (FluidSystem::phaseIsActive(oilPos)) {
1985 this->pp_[oilPos][elemIdx] = pres;
1992template<
class FluidSystem,
1995 class ElementMapper,
1996 class CartesianIndexMapper>
1998void InitialStateComputer<FluidSystem,
2002 CartesianIndexMapper>::
2003setRegionPvtIdx(
const EclipseState& eclState,
const GridView& gridView,
const RMap& reg)
2012 const LookUpData<typename GridView::Grid, GridView> lookUpData(gridView);
2013 const auto pvtnumData = lookUpData.template assignFieldPropsIntOnLeaf<int>(
2014 eclState.fieldProps(),
"PVTNUM",
true);
2016 for (
const auto& r : reg.activeRegions()) {
2017 const auto& cells = reg.cells(r);
2018 regionPvtIdx_[r] = pvtnumData[*cells.begin()];
2022template<
class FluidSystem,
2025 class ElementMapper,
2026 class CartesianIndexMapper>
2027template<
class RMap,
class MaterialLawManager,
class Comm>
2028void InitialStateComputer<FluidSystem,
2032 CartesianIndexMapper>::
2033calcPressSatRsRv(
const RMap& reg,
2034 const std::vector<EquilRecord>& rec,
2035 MaterialLawManager& materialLawManager,
2036 const GridView& gridView,
2040 using PhaseSat = Details::PhaseSaturations<
2041 MaterialLawManager, FluidSystem, EquilReg<Scalar>,
typename RMap::CellId
2044 auto ptable = Details::PressureTable<FluidSystem, EquilReg<Scalar>>{ grav, this->num_pressure_points_ };
2045 auto psat = PhaseSat { materialLawManager, this->swatInit_ };
2046 auto vspan = std::array<Scalar, 2>{};
2048 std::vector<int> regionIsEmpty(rec.size(), 0);
2049 for (std::size_t r = 0; r < rec.size(); ++r) {
2050 const auto& cells = reg.cells(r);
2054 const auto acc = rec[r].initializationTargetAccuracy();
2058 if (cells.empty()) {
2059 regionIsEmpty[r] = 1;
2062 const auto eqreg = EquilReg {
2063 rec[r], this->rsFunc_[r], this->rvFunc_[r], this->rvwFunc_[r],
2064 this->tempVdTable_[r], this->saltVdTable_[r], this->regionPvtIdx_[r]
2067 vspan[0] = std::min(vspan[0], std::min(eqreg.zgoc(), eqreg.zwoc()));
2068 vspan[1] = std::max(vspan[1], std::max(eqreg.zgoc(), eqreg.zwoc()));
2069 ptable.equilibrate(eqreg, vspan);
2072 this->equilibrateTiltedFaultBlock(cells, eqreg, gridView, acc, ptable, psat);
2074 else if (acc == 0) {
2075 if (cells.empty()) {
2076 regionIsEmpty[r] = 1;
2079 const auto eqreg = EquilReg {
2080 rec[r], this->rsFunc_[r], this->rvFunc_[r], this->rvwFunc_[r],
2081 this->tempVdTable_[r], this->saltVdTable_[r], this->regionPvtIdx_[r]
2083 vspan[0] = std::min(vspan[0], std::min(eqreg.zgoc(), eqreg.zwoc()));
2084 vspan[1] = std::max(vspan[1], std::max(eqreg.zgoc(), eqreg.zwoc()));
2085 ptable.equilibrate(eqreg, vspan);
2087 this->equilibrateCellCentres(cells, eqreg, ptable, psat);
2090 if (cells.empty()) {
2091 regionIsEmpty[r] = 1;
2094 const auto eqreg = EquilReg {
2095 rec[r], this->rsFunc_[r], this->rvFunc_[r], this->rvwFunc_[r],
2096 this->tempVdTable_[r], this->saltVdTable_[r], this->regionPvtIdx_[r]
2098 vspan[0] = std::min(vspan[0], std::min(eqreg.zgoc(), eqreg.zwoc()));
2099 vspan[1] = std::max(vspan[1], std::max(eqreg.zgoc(), eqreg.zwoc()));
2100 ptable.equilibrate(eqreg, vspan);
2102 this->equilibrateHorizontal(cells, eqreg, -acc, ptable, psat);
2105 comm.min(regionIsEmpty.data(),regionIsEmpty.size());
2106 if (comm.rank() == 0) {
2107 for (std::size_t r = 0; r < rec.size(); ++r) {
2108 if (regionIsEmpty[r])
2110 +
" has no active cells");
2115template<
class FluidSystem,
2118 class ElementMapper,
2119 class CartesianIndexMapper>
2120template<
class CellRange,
class EquilibrationMethod>
2121void InitialStateComputer<FluidSystem,
2125 CartesianIndexMapper>::
2126cellLoop(
const CellRange& cells,
2127 EquilibrationMethod&& eqmethod)
2129 const auto oilPos = FluidSystem::oilPhaseIdx;
2130 const auto gasPos = FluidSystem::gasPhaseIdx;
2131 const auto watPos = FluidSystem::waterPhaseIdx;
2133 const auto oilActive = FluidSystem::phaseIsActive(oilPos);
2134 const auto gasActive = FluidSystem::phaseIsActive(gasPos);
2135 const auto watActive = FluidSystem::phaseIsActive(watPos);
2137 auto pressures = Details::PhaseQuantityValue<Scalar>{};
2138 auto saturations = Details::PhaseQuantityValue<Scalar>{};
2143 for (
const auto& cell : cells) {
2144 eqmethod(cell, pressures, saturations, Rs, Rv, Rvw);
2147 this->pp_ [oilPos][cell] = pressures.oil;
2148 this->sat_[oilPos][cell] = saturations.oil;
2152 this->pp_ [gasPos][cell] = pressures.gas;
2153 this->sat_[gasPos][cell] = saturations.gas;
2157 this->pp_ [watPos][cell] = pressures.water;
2158 this->sat_[watPos][cell] = saturations.water;
2161 if (oilActive && gasActive) {
2162 this->rs_[cell] =
Rs;
2163 this->rv_[cell] =
Rv;
2166 if (watActive && gasActive) {
2167 this->rvw_[cell] =
Rvw;
2172template<
class FluidSystem,
2175 class ElementMapper,
2176 class CartesianIndexMapper>
2177template<
class CellRange,
class PressTable,
class PhaseSat>
2178void InitialStateComputer<FluidSystem,
2182 CartesianIndexMapper>::
2183equilibrateCellCentres(
const CellRange& cells,
2184 const EquilReg<Scalar>& eqreg,
2185 const PressTable& ptable,
2188 using CellPos =
typename PhaseSat::Position;
2189 using CellID = std::remove_cv_t<std::remove_reference_t<
2190 decltype(std::declval<CellPos>().cell)>>;
2191 this->cellLoop(cells, [
this, &eqreg, &ptable, &psat]
2193 Details::PhaseQuantityValue<Scalar>& pressures,
2194 Details::PhaseQuantityValue<Scalar>& saturations,
2197 Scalar& Rvw) ->
void
2199 const auto pos = CellPos {
2200 cell, cellCenterDepth_[cell]
2203 saturations = psat.deriveSaturations(pos, eqreg, ptable);
2204 pressures = psat.correctedPhasePressures();
2206 const auto temp = this->temperature_[cell];
2208 Rs = eqreg.dissolutionCalculator()
2209 (pos.depth, pressures.oil, temp, saturations.gas);
2211 Rv = eqreg.evaporationCalculator()
2212 (pos.depth, pressures.gas, temp, saturations.oil);
2214 Rvw = eqreg.waterEvaporationCalculator()
2215 (pos.depth, pressures.gas, temp, saturations.water);
2219template<
class FluidSystem,
2222 class ElementMapper,
2223 class CartesianIndexMapper>
2224template<
class CellRange,
class PressTable,
class PhaseSat>
2225void InitialStateComputer<FluidSystem,
2229 CartesianIndexMapper>::
2230equilibrateHorizontal(
const CellRange& cells,
2231 const EquilReg<Scalar>& eqreg,
2233 const PressTable& ptable,
2236 using CellPos =
typename PhaseSat::Position;
2237 using CellID = std::remove_cv_t<std::remove_reference_t<
2238 decltype(std::declval<CellPos>().cell)>>;
2240 this->cellLoop(cells, [
this, acc, &eqreg, &ptable, &psat]
2242 Details::PhaseQuantityValue<Scalar>& pressures,
2243 Details::PhaseQuantityValue<Scalar>& saturations,
2246 Scalar& Rvw) ->
void
2249 saturations.reset();
2251 Scalar totfrac = 0.0;
2253 const auto pos = CellPos { cell, depth };
2255 saturations.axpy(psat.deriveSaturations(pos, eqreg, ptable), frac);
2256 pressures .axpy(psat.correctedPhasePressures(), frac);
2262 saturations /= totfrac;
2263 pressures /= totfrac;
2266 const auto pos = CellPos {
2267 cell, cellCenterDepth_[cell]
2270 saturations = psat.deriveSaturations(pos, eqreg, ptable);
2271 pressures = psat.correctedPhasePressures();
2274 const auto temp = this->temperature_[cell];
2275 const auto cz = cellCenterDepth_[cell];
2277 Rs = eqreg.dissolutionCalculator()
2278 (cz, pressures.oil, temp, saturations.gas);
2280 Rv = eqreg.evaporationCalculator()
2281 (cz, pressures.gas, temp, saturations.oil);
2283 Rvw = eqreg.waterEvaporationCalculator()
2284 (cz, pressures.gas, temp, saturations.water);
2288template<
class Flu
idSystem,
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper>
2289template<
class CellRange,
class PressTable,
class PhaseSat>
2290void InitialStateComputer<FluidSystem, Grid, GridView, ElementMapper, CartesianIndexMapper>::
2291equilibrateTiltedFaultBlockSimple(
const CellRange& cells,
2292 const EquilReg<Scalar>& eqreg,
2293 const GridView& gridView,
2295 const PressTable& ptable,
2298 using CellPos =
typename PhaseSat::Position;
2299 using CellID = std::remove_cv_t<std::remove_reference_t<
2300 decltype(std::declval<CellPos>().cell)>>;
2302 this->cellLoop(cells, [
this, acc, &eqreg, &ptable, &psat, &gridView]
2304 Details::PhaseQuantityValue<Scalar>& pressures,
2305 Details::PhaseQuantityValue<Scalar>& saturations,
2308 Scalar& Rvw) ->
void
2311 saturations.reset();
2312 Scalar totalWeight = 0.0;
2315 const auto& [zmin, zmax] = cellZMinMax_[cell];
2316 const Scalar cellThickness = zmax - zmin;
2317 const Scalar halfThickness = cellThickness / 2.0;
2320 Scalar dipAngle, dipAzimuth;
2324 std::array<Scalar, 3> referencePoint = {
2325 cellCenterXY_[cell].first,
2326 cellCenterXY_[cell].second,
2327 cellCenterDepth_[cell]
2331 const int numLevelsPerHalf = std::min(20, acc);
2334 std::vector<std::pair<Scalar, Scalar>> levels;
2337 for (
int side = 0; side < 2; ++side) {
2338 Scalar halfStart = (side == 0) ? zmin : zmin + halfThickness;
2340 for (
int i = 0; i < numLevelsPerHalf; ++i) {
2342 Scalar depth = halfStart + (i + 0.5) * (halfThickness / numLevelsPerHalf);
2346 Scalar crossSectionWeight = (halfThickness / numLevelsPerHalf);
2349 if (std::abs(dipAngle) > 1e-10) {
2350 crossSectionWeight /= std::cos(dipAngle);
2353 levels.emplace_back(depth, crossSectionWeight);
2357 for (
const auto& [depth, weight] : levels) {
2359 const auto& [x, y] = cellCenterXY_[cell];
2361 depth, x, y, dipAngle, dipAzimuth, referencePoint);
2363 const auto pos = CellPos{cell, tvd};
2365 auto localSaturations = psat.deriveSaturations(pos, eqreg, ptable);
2366 auto localPressures = psat.correctedPhasePressures();
2369 saturations.axpy(localSaturations, weight);
2370 pressures.axpy(localPressures, weight);
2371 totalWeight += weight;
2375 if (totalWeight > 1e-10) {
2376 saturations /= totalWeight;
2377 pressures /= totalWeight;
2380 const auto& [x, y] = cellCenterXY_[cell];
2382 cellCenterDepth_[cell], x, y, dipAngle, dipAzimuth, referencePoint);
2383 const auto pos = CellPos{cell, tvdCenter};
2384 saturations = psat.deriveSaturations(pos, eqreg, ptable);
2385 pressures = psat.correctedPhasePressures();
2389 const auto temp = this->temperature_[cell];
2390 const auto& [x, y] = cellCenterXY_[cell];
2392 cellCenterDepth_[cell], x, y, dipAngle, dipAzimuth, referencePoint);
2394 Rs = eqreg.dissolutionCalculator()(tvdCenter, pressures.oil, temp, saturations.gas);
2395 Rv = eqreg.evaporationCalculator()(tvdCenter, pressures.gas, temp, saturations.oil);
2396 Rvw = eqreg.waterEvaporationCalculator()(tvdCenter, pressures.gas, temp, saturations.water);
2400template<
class Flu
idSystem,
class Gr
id,
class Gr
idView,
class ElementMapper,
class CartesianIndexMapper>
2401template<
class CellRange,
class PressTable,
class PhaseSat>
2402void InitialStateComputer<FluidSystem, Grid, GridView, ElementMapper, CartesianIndexMapper>::
2403equilibrateTiltedFaultBlock(
const CellRange& cells,
2404 const EquilReg<Scalar>& eqreg,
2405 const GridView& gridView,
2407 const PressTable& ptable,
2410 using CellPos =
typename PhaseSat::Position;
2411 using CellID = std::remove_cv_t<std::remove_reference_t<
2412 decltype(std::declval<CellPos>().cell)>>;
2414 std::vector<typename GridView::template Codim<0>::Entity> entityMap(gridView.size(0));
2415 for (
const auto& entity : entities(gridView, Dune::Codim<0>())) {
2416 CellID idx = gridView.indexSet().index(entity);
2417 entityMap[idx] = entity;
2421 auto polygonArea = [](
const std::vector<std::array<Scalar, 2>>& pts) {
2422 if (pts.size() < 3)
return Scalar(0);
2424 for (
size_t i = 0; i < pts.size(); ++i) {
2425 size_t j = (i + 1) % pts.size();
2426 area += pts[i][0] * pts[j][1] - pts[j][0] * pts[i][1];
2428 return std::abs(area) * Scalar(0.5);
2432 auto computeCrossSectionArea = [&](
const CellID cell, Scalar depth) -> Scalar {
2434 const auto& entity = entityMap[cell];
2435 const auto& geometry = entity.geometry();
2436 const int numCorners = geometry.corners();
2438 std::vector<std::array<Scalar, 3>> corners(numCorners);
2439 for (
int i = 0; i < numCorners; ++i) {
2440 const auto& corner = geometry.corner(i);
2441 corners[i] = {
static_cast<Scalar
>(corner[0]),
static_cast<Scalar
>(corner[1]),
static_cast<Scalar
>(corner[2])};
2445 std::vector<std::array<Scalar, 2>> intersectionPoints;
2446 const Scalar tol = 1e-10;
2449 for (
size_t i = 0; i < corners.size(); ++i) {
2450 for (
size_t j = i + 1; j < corners.size(); ++j) {
2451 Scalar za = corners[i][2];
2452 Scalar zb = corners[j][2];
2454 if ((za - depth) * (zb - depth) <= 0.0 && std::abs(za - zb) > tol) {
2456 Scalar t = (depth - za) / (zb - za);
2457 Scalar x = corners[i][0] + t * (corners[j][0] - corners[i][0]);
2458 Scalar y = corners[i][1] + t * (corners[j][1] - corners[i][1]);
2459 intersectionPoints.push_back({x, y});
2465 if (intersectionPoints.size() > 1) {
2466 auto pointsEqual = [tol](
const std::array<Scalar, 2>& a,
const std::array<Scalar, 2>& b) {
2467 return std::abs(a[0] - b[0]) < tol && std::abs(a[1] - b[1]) < tol;
2470 intersectionPoints.erase(
2471 std::unique(intersectionPoints.begin(), intersectionPoints.end(), pointsEqual),
2472 intersectionPoints.end()
2476 if (intersectionPoints.size() < 3) {
2482 Scalar cx = 0, cy = 0;
2483 for (
const auto& p : intersectionPoints) {
2484 cx += p[0]; cy += p[1];
2486 cx /= intersectionPoints.size();
2487 cy /= intersectionPoints.size();
2490 auto angleCompare = [cx, cy](
const std::array<Scalar, 2>& a,
const std::array<Scalar, 2>& b) {
2491 return std::atan2(a[1] - cy, a[0] - cx) < std::atan2(b[1] - cy, b[0] - cx);
2494 std::ranges::sort(intersectionPoints, angleCompare);
2496 return polygonArea(intersectionPoints);
2498 }
catch (
const std::exception& e) {
2503 auto cellProcessor = [
this, acc, &eqreg, &ptable, &psat, &computeCrossSectionArea]
2505 Details::PhaseQuantityValue<Scalar>& pressures,
2506 Details::PhaseQuantityValue<Scalar>& saturations,
2509 Scalar&
Rvw) ->
void
2512 saturations.reset();
2513 Scalar totalWeight = 0.0;
2515 const auto& zmin = this->cellZMinMax_[cell].first;
2516 const auto& zmax = this->cellZMinMax_[cell].second;
2517 const Scalar cellThickness = zmax - zmin;
2518 const Scalar halfThickness = cellThickness / 2.0;
2521 Scalar dipAngle, dipAzimuth;
2525 std::array<Scalar, 3> referencePoint = {
2526 this->cellCenterXY_[cell].first,
2527 this->cellCenterXY_[cell].second,
2528 cellCenterDepth_[cell]
2532 const int numLevelsPerHalf = std::min(20, acc);
2535 std::vector<std::pair<Scalar, Scalar>> levels;
2538 for (
int side = 0; side < 2; ++side) {
2539 Scalar halfStart = (side == 0) ? zmin : zmin + halfThickness;
2541 for (
int i = 0; i < numLevelsPerHalf; ++i) {
2543 Scalar depth = halfStart + (i + 0.5) * (halfThickness / numLevelsPerHalf);
2546 Scalar crossSectionArea = computeCrossSectionArea(cell, depth);
2549 Scalar weight = crossSectionArea * (halfThickness / numLevelsPerHalf);
2551 levels.emplace_back(depth, weight);
2556 bool hasValidAreas =
false;
2557 for (
const auto& level : levels) {
2558 if (level.second > 1e-10) {
2559 hasValidAreas =
true;
2564 if (!hasValidAreas) {
2567 for (
int side = 0; side < 2; ++side) {
2568 Scalar halfStart = (side == 0) ? zmin : zmin + halfThickness;
2569 for (
int i = 0; i < numLevelsPerHalf; ++i) {
2570 Scalar depth = halfStart + (i + 0.5) * (halfThickness / numLevelsPerHalf);
2571 Scalar weight = (halfThickness / numLevelsPerHalf);
2572 if (std::abs(dipAngle) > 1e-10) {
2573 weight /= std::cos(dipAngle);
2575 levels.emplace_back(depth, weight);
2580 for (
const auto& level : levels) {
2581 Scalar depth = level.first;
2582 Scalar weight = level.second;
2585 const auto& xy = this->cellCenterXY_[cell];
2587 depth, xy.first, xy.second, dipAngle, dipAzimuth, referencePoint);
2589 const auto pos = CellPos{cell, tvd};
2591 auto localSaturations = psat.deriveSaturations(pos, eqreg, ptable);
2592 auto localPressures = psat.correctedPhasePressures();
2595 saturations.axpy(localSaturations, weight);
2596 pressures.axpy(localPressures, weight);
2597 totalWeight += weight;
2600 if (totalWeight > 1e-10) {
2601 saturations /= totalWeight;
2602 pressures /= totalWeight;
2605 const auto& xy = this->cellCenterXY_[cell];
2607 this->cellCenterDepth_[cell], xy.first, xy.second, dipAngle, dipAzimuth, referencePoint);
2608 const auto pos = CellPos{cell, tvdCenter};
2609 saturations = psat.deriveSaturations(pos, eqreg, ptable);
2610 pressures = psat.correctedPhasePressures();
2614 const auto temp = this->temperature_[cell];
2615 const auto& xy = this->cellCenterXY_[cell];
2617 this->cellCenterDepth_[cell], xy.first, xy.second, dipAngle, dipAzimuth, referencePoint);
2619 Rs = eqreg.dissolutionCalculator()(tvdCenter, pressures.oil, temp, saturations.gas);
2620 Rv = eqreg.evaporationCalculator()(tvdCenter, pressures.gas, temp, saturations.oil);
2621 Rvw = eqreg.waterEvaporationCalculator()(tvdCenter, pressures.gas, temp, saturations.water);
2624 this->cellLoop(cells, cellProcessor);
#define OPM_END_PARALLEL_TRY_CATCH(prefix, comm)
Catch exception and throw in a parallel try-catch clause.
Definition: DeferredLoggingErrorHelpers.hpp:197
#define OPM_BEGIN_PARALLEL_TRY_CATCH()
Macro to setup the try of a parallel try-catch.
Definition: DeferredLoggingErrorHelpers.hpp:160
Auxiliary routines that to solve the ODEs that emerge from the hydrostatic equilibrium problem.
Dune::OwnerOverlapCopyCommunication< int, int > Comm
Definition: FlexibleSolver_impl.hpp:394
Routines that actually solve the ODEs that emerge from the hydrostatic equilibrium problem.
Definition: InitStateEquil.hpp:704
Definition: InitStateEquil.hpp:151
Gas(const TabulatedFunction &tempVdTable, const RV &rv, const RVW &rvw, const int pvtRegionIdx, const Scalar normGrav)
Definition: InitStateEquil_impl.hpp:485
Scalar operator()(const Scalar depth, const Scalar press) const
Definition: InitStateEquil_impl.hpp:501
Definition: InitStateEquil.hpp:126
Oil(const TabulatedFunction &tempVdTable, const RS &rs, const int pvtRegionIdx, const Scalar normGrav)
Definition: InitStateEquil_impl.hpp:437
Scalar operator()(const Scalar depth, const Scalar press) const
Definition: InitStateEquil_impl.hpp:451
Definition: InitStateEquil.hpp:101
Scalar operator()(const Scalar depth, const Scalar press) const
Definition: InitStateEquil_impl.hpp:411
Water(const TabulatedFunction &tempVdTable, const TabulatedFunction &saltVdTable, const int pvtRegionIdx, const Scalar normGrav)
Definition: InitStateEquil_impl.hpp:397
Definition: InitStateEquil.hpp:399
const PhaseQuantityValue< Scalar > & deriveSaturations(const Position &x, const Region ®, const PTable &ptable)
Definition: InitStateEquil_impl.hpp:743
PhaseSaturations(MaterialLawManager &matLawMgr, const std::vector< Scalar > &swatInit)
Definition: InitStateEquil_impl.hpp:719
Definition: InitStateEquil.hpp:180
PressureTable & operator=(const PressureTable &rhs)
Definition: InitStateEquil_impl.hpp:1164
Scalar water(const Scalar depth) const
Definition: InitStateEquil_impl.hpp:1244
Scalar gas(const Scalar depth) const
Definition: InitStateEquil_impl.hpp:1233
bool waterActive() const
Predicate for whether or not water is an active phase.
Definition: InitStateEquil_impl.hpp:1215
bool gasActive() const
Predicate for whether or not gas is an active phase.
Definition: InitStateEquil_impl.hpp:1208
Scalar oil(const Scalar depth) const
Definition: InitStateEquil_impl.hpp:1223
std::array< Scalar, 2 > VSpan
Definition: InitStateEquil.hpp:183
bool oilActive() const
Predicate for whether or not oil is an active phase.
Definition: InitStateEquil_impl.hpp:1201
typename FluidSystem::Scalar Scalar
Definition: InitStateEquil.hpp:182
void equilibrate(const Region ®, const VSpan &span)
Definition: InitStateEquil_impl.hpp:1190
PressureTable(const Scalar gravity, const int samplePoints=2000)
Definition: InitStateEquil_impl.hpp:1134
Definition: InitStateEquil.hpp:80
Scalar operator()(const Scalar x) const
Definition: InitStateEquil_impl.hpp:360
RK4IVP(const RHS &f, const std::array< Scalar, 2 > &span, const Scalar y0, const int N)
Definition: InitStateEquil_impl.hpp:320
Definition: EquilibrationHelpers.hpp:135
Definition: EquilibrationHelpers.hpp:216
Definition: EquilibrationHelpers.hpp:269
Definition: EquilibrationHelpers.hpp:612
Definition: EquilibrationHelpers.hpp:162
Definition: EquilibrationHelpers.hpp:322
Definition: EquilibrationHelpers.hpp:376
Definition: FlowGenericProblem.hpp:51
std::vector< EquilRecord > getEquil(const EclipseState &state)
Definition: InitStateEquil_impl.hpp:1438
std::vector< int > equilnum(const EclipseState &eclipseState, const GridView &gridview)
Definition: InitStateEquil_impl.hpp:1452
std::pair< Scalar, Scalar > cellZMinMax(const Element &element)
Definition: InitStateEquil_impl.hpp:187
Scalar cellCenterDepth(const Element &element)
Definition: InitStateEquil_impl.hpp:134
std::pair< Scalar, Scalar > cellZSpan(const Element &element)
Definition: InitStateEquil_impl.hpp:168
CellCornerData< Scalar > getCellCornerXY(const Element &element)
Definition: InitStateEquil_impl.hpp:257
void verticalExtent(const CellRange &cells, const std::vector< std::pair< Scalar, Scalar > > &cellZMinMax, const Parallel::Communication &comm, std::array< Scalar, 2 > &span)
Definition: InitStateEquil_impl.hpp:70
std::pair< Scalar, Scalar > cellCenterXY(const Element &element)
Definition: InitStateEquil_impl.hpp:149
Scalar calculateTrueVerticalDepth(Scalar z, Scalar x, Scalar y, Scalar dipAngle, Scalar dipAzimuth, const std::array< Scalar, 3 > &referencePoint)
Definition: InitStateEquil_impl.hpp:281
void subdivisionCentrePoints(const Scalar left, const Scalar right, const int numIntervals, std::vector< std::pair< Scalar, Scalar > > &subdiv)
Definition: InitStateEquil_impl.hpp:95
std::vector< std::pair< Scalar, Scalar > > horizontalSubdivision(const CellID cell, const std::pair< Scalar, Scalar > topbot, const int numIntervals)
Definition: InitStateEquil_impl.hpp:113
void computeBlockDip(const CellCornerData< Scalar > &cellCorners, Scalar &dipAngle, Scalar &dipAzimuth)
Definition: InitStateEquil_impl.hpp:206
Dune::Communication< MPIComm > Communication
Definition: ParallelCommunication.hpp:30
Definition: blackoilbioeffectsmodules.hh:45
std::string to_string(const ConvergenceReport::ReservoirFailure::Type t)
Definition: InitStateEquil.hpp:61
std::array< Scalar, 8 > X
Definition: InitStateEquil.hpp:62
std::array< Scalar, 8 > Y
Definition: InitStateEquil.hpp:63
std::array< Scalar, 8 > Z
Definition: InitStateEquil.hpp:64
Simple set of per-phase (named by primary component) quantities.
Definition: InitStateEquil.hpp:351
Definition: InitStateEquil.hpp:405