/*
Copyright 2018 Equinor ASA.
Copyright 2017 Statoil ASA.
This file is part of the Open Porous Media Project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see .
*/
#if HAVE_CONFIG_H
#include
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace {
std::vector
unscaledTwoPt(const std::vector& min,
const std::vector& max)
{
assert ((min.size() == max.size()) && "Internal Logic Error");
assert ((! min.empty()) && "Internal Logic Error");
using TEP = Opm::SatFunc::EPSEvalInterface::TableEndPoints;
auto tep = std::vector{};
tep.reserve(min.size());
for (auto n = min.size(), i = 0*n; i < n; ++i) {
const auto smin = min[i];
// Ignore 'sdisp' in the two-point scaling.
tep.push_back(TEP{ smin, smin, max[i] });
}
return tep;
}
std::vector
unscaledThreePt(const std::vector& min ,
const std::vector& disp,
const std::vector& max )
{
assert ((min.size() == max .size()) && "Internal Logic Error");
assert ((min.size() == disp.size()) && "Internal Logic Error");
assert ((! min.empty()) && "Internal Logic Error");
using TEP = Opm::SatFunc::EPSEvalInterface::TableEndPoints;
auto tep = std::vector{};
tep.reserve(min.size());
for (auto n = min.size(), i = 0*n; i < n; ++i) {
tep.push_back(TEP{ min[i], disp[i], max[i] });
}
return tep;
}
bool haveKeywordData(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const std::string& vector)
{
const auto& grids = G.activeGrids();
return std::any_of(std::begin(grids), std::end(grids),
[&init, &vector](const std::string& gridID)
{
return init.haveKeywordData(vector, gridID);
});
}
template
std::vector
gridDefaultedVector(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const std::string& vector,
const std::vector& dflt,
CvrtVal&& cvrt,
const std::vector& fallback
= std::vector{})
{
auto ret = std::vector(G.numCells());
const auto sentinel = 1.0e20;
assert (! dflt.empty() && "Internal Error");
auto cellID = std::vector::size_type{0};
for (const auto& gridID : G.activeGrids()) {
const auto nc = G.numCells(gridID);
const auto& snum = init.haveKeywordData("SATNUM", gridID)
? G.rawLinearisedCellData(init, "SATNUM", gridID)
: std::vector(nc, 1);
const auto& val = init.haveKeywordData(vector, gridID)
? G.rawLinearisedCellData(init, vector, gridID)
: std::vector(nc, -1.0e21);
for (auto c = 0*nc; c < nc; ++c, ++cellID) {
const auto fb = fallback.empty()
? -sentinel : fallback[c];
auto& r = ret[cellID];
if (std::abs(val[c]) < sentinel) {
r = cvrt(val[c]);
}
else if (std::abs(fb) < sentinel) {
r = cvrt(fb);
}
else {
r = dflt[snum[c] - 1];
}
}
}
return ret;
}
std::vector
sgCrit(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const ::Opm::SatFunc::CreateEPS::RawTableEndPoints& tep)
{
return gridDefaultedVector(G, init, "SGCR", tep.crit.gas,
[](const double s) { return s; });
}
std::vector
sogCrit(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const ::Opm::SatFunc::CreateEPS::RawTableEndPoints& tep)
{
return gridDefaultedVector(G, init, "SOGCR", tep.crit.oil_in_gas,
[](const double s) { return s; });
}
std::vector
sowCrit(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const ::Opm::SatFunc::CreateEPS::RawTableEndPoints& tep)
{
return gridDefaultedVector(G, init, "SOWCR", tep.crit.oil_in_water,
[](const double s) { return s; });
}
std::vector
swCrit(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const ::Opm::SatFunc::CreateEPS::RawTableEndPoints& tep)
{
return gridDefaultedVector(G, init, "SWCR", tep.crit.water,
[](const double s) { return s; });
}
std::vector
sgMax(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const ::Opm::SatFunc::CreateEPS::RawTableEndPoints& tep)
{
return gridDefaultedVector(G, init, "SGU", tep.smax.gas,
[](const double s) { return s; });
}
std::vector
soMax(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const ::Opm::SatFunc::CreateEPS::RawTableEndPoints& tep)
{
auto smax = std::vector(G.numCells());
const auto sgl = ::Opm::SatFunc::scaledConnateGas (G, init, tep);
const auto swl = ::Opm::SatFunc::scaledConnateWater(G, init, tep);
std::transform(std::begin(sgl), std::end(sgl), std::begin(swl),
std::begin(smax),
[](const double sg, const double sw) -> double
{
return 1.0 - (sg + sw);
});
return smax;
}
std::vector
swMax(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const ::Opm::SatFunc::CreateEPS::RawTableEndPoints& tep)
{
return gridDefaultedVector(G, init, "SWU", tep.smax.water,
[](const double s) { return s; });
}
double defaultedScaledSaturation(const double s, const double dflt)
{
// Use input scaled saturation ('s') if not defaulted (|s| < 1e20),
// default scaled saturation otherwise. The sentinel value 1e20 is
// the common value used to represent unset/defaulted values in ECL
// result sets.
return (std::abs(s) < 1.0e+20) ? s : dflt;
}
bool
haveScaledRelPermAtCritSat(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const ::Opm::SatFunc::CreateEPS::EPSOptions& opt)
{
switch (opt.thisPh) {
case ::Opm::ECLPhaseIndex::Aqua:
return haveKeywordData(G, init, "KRWR");
case ::Opm::ECLPhaseIndex::Liquid:
return (opt.subSys == ::Opm::SatFunc::CreateEPS::SubSystem::OilGas)
? haveKeywordData(G, init, "KRORG")
: haveKeywordData(G, init, "KRORW");
case ::Opm::ECLPhaseIndex::Vapour:
return haveKeywordData(G, init, "KRGR");
}
return false;
}
}
// ---------------------------------------------------------------------
// Class Opm::TwoPointScaling::Impl
// ---------------------------------------------------------------------
class Opm::SatFunc::TwoPointScaling::Impl
{
public:
Impl(std::vector smin,
std::vector smax)
: smin_(std::move(smin)), smax_(std::move(smax))
{
if (this->smin_.size() != this->smax_.size()) {
throw std::invalid_argument {
"Size Mismatch Between Minimum and "
"Maximum Saturation Arrays"
};
}
}
std::vector
eval(const TableEndPoints& tep,
const SaturationPoints& sp) const;
std::vector
reverse(const TableEndPoints& tep,
const SaturationPoints& sp) const;
private:
std::vector smin_;
std::vector smax_;
double sMin(const std::vector::size_type cell,
const TableEndPoints& tep) const
{
// Use model's scaled connate saturation if defined, otherwise fall
// back to table's unscaled connate saturation.
return defaultedScaledSaturation(this->smin_[cell], tep.low);
}
double sMax(const std::vector::size_type cell,
const TableEndPoints& tep) const
{
// Use model's scaled maximum saturation if defined, otherwise fall
// back to table's unscaled maximum saturation.
return defaultedScaledSaturation(this->smax_[cell], tep.high);
}
};
std::vector
Opm::SatFunc::TwoPointScaling::
Impl::eval(const TableEndPoints& tep,
const SaturationPoints& sp) const
{
const auto srng = tep.high - tep.low;
auto effsat = std::vector{};
effsat.reserve(sp.size());
for (const auto& eval_pt : sp) {
const auto cell = eval_pt.cell;
const auto sLO = this->sMin(cell, tep);
const auto sHI = this->sMax(cell, tep);
effsat.push_back(0.0);
auto& s_eff = effsat.back();
if (! (eval_pt.sat > sLO)) {
// s <= sLO
s_eff = tep.low;
}
else if (! (eval_pt.sat < sHI)) {
// s >= sHI
s_eff = tep.high;
}
else {
// s \in (sLO, sHI)
const auto scaled_sat =
((eval_pt.sat - sLO) / (sHI - sLO)) * srng;
s_eff = tep.low + scaled_sat;
}
}
return effsat;
}
std::vector
Opm::SatFunc::TwoPointScaling::
Impl::reverse(const TableEndPoints& tep,
const SaturationPoints& sp) const
{
const auto srng = tep.high - tep.low;
auto unscaledsat = std::vector{};
unscaledsat.reserve(sp.size());
for (const auto& eval_pt : sp) {
const auto cell = eval_pt.cell;
const auto sLO = this->sMin(cell, tep);
const auto sHI = this->sMax(cell, tep);
unscaledsat.push_back(0.0);
auto& s_unsc = unscaledsat.back();
if (! (eval_pt.sat > tep.low)) {
// s <= minimum tabulated saturation.
// Map to Minimum Input Saturation in cell (sLO).
s_unsc = sLO;
}
else if (! (eval_pt.sat < tep.high)) {
// s >= maximum tabulated saturation.
// Map to Maximum Input Saturation in cell (sHI).
s_unsc = sHI;
}
else {
// s in tabulated interval (tep.low, tep.high)
// Map to Input Saturation in (sLO, sHI)
const auto t =
(eval_pt.sat - tep.low) / srng;
s_unsc = sLO + t*(sHI - sLO);
}
}
return unscaledsat;
}
// ---------------------------------------------------------------------
// Class Opm::SatFunc::PureVerticalScaling::Impl
// ---------------------------------------------------------------------
class Opm::SatFunc::PureVerticalScaling::Impl
{
public:
explicit Impl(std::vector fmax)
: fmax_(std::move(fmax))
, inf_ (std::numeric_limits::has_infinity
? std::numeric_limits::infinity()
: std::numeric_limits::max())
{}
std::vector
vertScale(const FunctionValues& f,
const SaturationPoints& sp,
std::vector val) const;
private:
std::vector fmax_;
double inf_;
std::vector
zeroMaxVal(const SaturationPoints& sp) const;
std::vector
nonZeroMaxVal(const SaturationPoints& sp,
const double maxVal,
std::vector val) const;
};
std::vector
Opm::SatFunc::PureVerticalScaling::Impl::
vertScale(const FunctionValues& f,
const SaturationPoints& sp,
std::vector val) const
{
assert (sp.size() == val.size() && "Internal Error in Vertical Scaling");
const auto maxVal = f.max.val;
if (! (std::abs(maxVal) > 0.0)) {
return this->zeroMaxVal(sp);
}
return this->nonZeroMaxVal(sp, maxVal, std::move(val));
}
std::vector
Opm::SatFunc::PureVerticalScaling::Impl::
zeroMaxVal(const SaturationPoints& sp) const
{
auto ret = std::vector(sp.size(), 0.0);
for (auto n = sp.size(), i = 0*n; i < n; ++i) {
const auto fmax = this->fmax_[ sp[i].cell ];
ret[i] = (std::abs(fmax) > 0.0)
? (std::signbit(fmax) ? -this->inf_ : this->inf_)
: 0.0;
}
return ret;
}
std::vector
Opm::SatFunc::PureVerticalScaling::Impl::
nonZeroMaxVal(const SaturationPoints& sp,
const double maxVal,
std::vector val) const
{
auto ret = std::move(val);
for (auto n = sp.size(), i = 0*n; i < n; ++i) {
ret[i] *= this->fmax_[ sp[i].cell ] / maxVal;
}
return ret;
}
// ---------------------------------------------------------------------
// Class Opm::SatFunc::CritSatVerticalScaling::Impl
// ---------------------------------------------------------------------
class Opm::SatFunc::CritSatVerticalScaling::Impl
{
public:
explicit Impl(std::vector sdisp,
std::vector fdisp,
std::vector smax,
std::vector fmax)
: sdisp_(std::move(sdisp))
, fdisp_(std::move(fdisp))
, smax_ (std::move(smax))
, fmax_ (std::move(fmax))
{}
std::vector
vertScale(const FunctionValues& f,
const SaturationPoints& sp,
std::vector val) const;
private:
std::vector sdisp_;
std::vector fdisp_;
std::vector smax_;
std::vector fmax_;
};
std::vector
Opm::SatFunc::CritSatVerticalScaling::Impl::
vertScale(const FunctionValues& f,
const SaturationPoints& sp,
std::vector val) const
{
assert ((sp.size() == val.size()) && "Internal Error in Vertical Scaling");
auto ret = std::move(val);
const auto fdisp = f.disp.val;
const auto fmax = std::max(f.disp.val, f.max.val);
const auto sepfv = fmax > fdisp;
for (auto n = sp.size(), i = 0*n; i < n; ++i) {
auto& y = ret[i];
const auto c = sp[i].cell;
const auto s = sp[i].sat;
const auto sr = std::min(this->sdisp_[c], this->smax_[c]);
const auto fr = this->fdisp_[c];
const auto sm = this->smax_ [c];
const auto fm = this->fmax_ [c];
if (s < sr) {
// s < sr: Pure vertical scaling in left interval.
y *= fr / fdisp;
}
else if (sepfv) {
// s \in [sr, sm), sm > sr; normal case: Kr(Smax) > Kr(Sr).
//
// Linear function between (sr,fr) and (sm,fm) in terms of
// function value 'y'. This usually alters the shape of the
// relative permeability function in this interval (e.g.,
// roughly quadratic to linear).
const auto t = (y - fdisp) / (fmax - fdisp);
y = fr + t*(fm - fr);
}
else if (s < sm) {
// s \in [sr, sm), sm > sr; special case: Kr(Smax) == Kr(Sr).
//
// Use linear function between (sr,fr) and (sm,fm) in terms of
// saturation value 's'. This usually alters the shape of the
// relative permeability function in this interval (e.g.,
// roughly quadratic to linear).
const auto t = (s - sr) / (sm - sr);
y = fr + t*(fm - fr);
}
else {
// sm == sr (pure scaling). Almost arbitrarily pick fmax_[c].
y = fm;
}
}
return ret;
}
// ---------------------------------------------------------------------
// Class Opm::ThreePointScaling::Impl
// ---------------------------------------------------------------------
class Opm::SatFunc::ThreePointScaling::Impl
{
public:
Impl(std::vector smin ,
std::vector sdisp,
std::vector smax )
: smin_(std::move(smin)), sdisp_(std::move(sdisp)), smax_(std::move(smax))
{
if ((this->sdisp_.size() != this->smin_.size()) ||
(this->sdisp_.size() != this->smax_.size()))
{
throw std::invalid_argument {
"Size Mismatch Between Minimum, Displacing "
"and Maximum Saturation Arrays"
};
}
}
std::vector
eval(const TableEndPoints& tep,
const SaturationPoints& sp) const;
std::vector
reverse(const TableEndPoints& tep,
const SaturationPoints& sp) const;
private:
std::vector smin_;
std::vector sdisp_;
std::vector smax_;
double sMin(const std::vector::size_type cell,
const TableEndPoints& tep) const
{
// Use model's scaled connate saturation if defined, otherwise fall
// back to table's unscaled connate saturation.
return defaultedScaledSaturation(this->smin_[cell], tep.low);
}
double sDisp(const std::vector::size_type cell,
const TableEndPoints& tep) const
{
// Use model's scaled displacing saturation if defined, otherwise
// fall back to table's unscaled displacing saturation.
return defaultedScaledSaturation(this->sdisp_[cell], tep.disp);
}
double sMax(const std::vector::size_type cell,
const TableEndPoints& tep) const
{
// Use model's scaled maximum saturation if defined, otherwise fall
// back to table's unscaled maximum saturation.
return defaultedScaledSaturation(this->smax_[cell], tep.high);
}
};
std::vector
Opm::SatFunc::ThreePointScaling::
Impl::eval(const TableEndPoints& tep,
const SaturationPoints& sp) const
{
auto effsat = std::vector{};
effsat.reserve(sp.size());
for (const auto& eval_pt : sp) {
const auto cell = eval_pt.cell;
const auto sLO = this->sMin (cell, tep);
const auto sR = this->sDisp(cell, tep);
const auto sHI = this->sMax (cell, tep);
effsat.push_back(0.0);
auto& s_eff = effsat.back();
if (! (eval_pt.sat > sLO)) {
// s <= sLO
s_eff = tep.low;
}
else if (eval_pt.sat < std::min(sR, sHI)) {
// s in scaled interval [sLO, sR)
// Map to tabulated saturation in [tep.low, tep.disp)
const auto t = (eval_pt.sat - sLO) / (sR - sLO);
s_eff = tep.low + t*(tep.disp - tep.low);
}
else if (eval_pt.sat < sHI) {
// s in scaled interval [sR, sHI)
// Map to tabulated saturation in [tep.disp, tep.high)
assert (sHI > sR);
const auto t = (eval_pt.sat - sR) / (sHI - sR);
s_eff = tep.disp + t*(tep.high - tep.disp);
}
else {
// s >= sHI
s_eff = tep.high;
}
}
return effsat;
}
std::vector
Opm::SatFunc::ThreePointScaling::
Impl::reverse(const TableEndPoints& tep,
const SaturationPoints& sp) const
{
auto unscaledsat = std::vector{};
unscaledsat.reserve(sp.size());
for (const auto& eval_pt : sp) {
const auto cell = eval_pt.cell;
const auto sLO = this->sMin (cell, tep);
const auto sR = this->sDisp(cell, tep);
const auto sHI = this->sMax (cell, tep);
unscaledsat.push_back(0.0);
auto& s_unsc = unscaledsat.back();
if (! (eval_pt.sat > tep.low)) {
// s <= minimum tabulated saturation.
// Map to Minimum Input Saturation in cell (sLO).
s_unsc = sLO;
}
else if (eval_pt.sat < tep.disp) {
// s in tabulated interval [tep.low, tep.disp)
// Map to Input Saturation in (sLO, sR)
const auto t =
(eval_pt.sat - tep.low)
/ (tep.disp - tep.low);
s_unsc = std::min(sLO + t*(sR - sLO), sHI);
}
else if (eval_pt.sat < tep.high) {
// s in tabulated interval [tep.disp, tep.high)
// Map to Input Saturation in [sR, sHI)
assert (tep.high > tep.disp);
const auto t =
(eval_pt.sat - tep.disp)
/ (tep.high - tep.disp);
s_unsc = std::min(sR + t*std::max(sHI - sR, 0.0), sHI);
}
else {
// s >= maximum tabulated saturation.
//
// Map to Maximum Input Saturation in cell (sHI) if maximum
// tabulated saturation is strictly greater than critical
// displacing saturation--otherwise map to critical displacing
// saturation.
//
// Needed to handle cases in which \code tep.disp==tep.high
// \endcode but scaled versions of these might differ, i.e. when
// sR < sHI, but the corresponding saturation points in the
// underlying input table coincide.
s_unsc = (tep.high > tep.disp) ? sHI : sR;
}
}
return unscaledsat;
}
// ---------------------------------------------------------------------
// EPS factory functions for two-point and three-point scaling options
// ---------------------------------------------------------------------
namespace Create {
using EPSOpt = ::Opm::SatFunc::CreateEPS::EPSOptions;
using RTEP = ::Opm::SatFunc::CreateEPS::RawTableEndPoints;
using TEP = ::Opm::SatFunc::EPSEvalInterface::TableEndPoints;
namespace TwoPoint {
using EPS = ::Opm::SatFunc::TwoPointScaling;
using EPSPtr = std::unique_ptr;
struct Kr {
static EPSPtr
G(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const RTEP& tep);
static EPSPtr
OG(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const RTEP& tep);
static EPSPtr
OW(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const RTEP& tep);
static EPSPtr
W(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const RTEP& tep);
};
struct Pc {
static EPSPtr
GO(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const RTEP& tep);
static EPSPtr
OW(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const RTEP& tep);
};
static EPSPtr
scalingFunction(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const EPSOpt& opt,
const RTEP& tep);
static std::vector
unscaledEndPoints(const RTEP& ep, const EPSOpt& opt);
} // namespace TwoPoint
namespace ThreePoint {
using EPS = ::Opm::SatFunc::ThreePointScaling;
using EPSPtr = std::unique_ptr;
struct Kr {
static EPSPtr
G(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const RTEP& tep);
static EPSPtr
OG(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const RTEP& tep);
static EPSPtr
OW(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const RTEP& tep);
static EPSPtr
W(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const RTEP& tep);
};
static EPSPtr
scalingFunction(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const EPSOpt& opt,
const RTEP& tep);
static std::vector
unscaledEndPoints(const RTEP& ep, const EPSOpt& opt);
} // namespace ThreePoint
namespace PureVertical {
using Scaling = ::Opm::SatFunc::PureVerticalScaling;
using EPSOpt = ::Opm::SatFunc::CreateEPS::EPSOptions;
using FValVec = ::Opm::SatFunc::CreateEPS::Vertical::FuncValVector;
using ScalPtr = std::unique_ptr;
struct Kr {
static ScalPtr
G(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const std::vector& dflt);
static ScalPtr
O(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const std::vector& dflt);
static ScalPtr
W(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const std::vector& dflt);
};
struct Pc {
static ScalPtr
GO(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const std::vector& dflt);
static ScalPtr
OW(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const std::vector& dflt);
};
static ScalPtr
scalingFunction(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const EPSOpt& opt,
const FValVec& fvals);
} // namespace PureVertical
namespace CritSatVertical {
using Scaling = ::Opm::SatFunc::CritSatVerticalScaling;
using EPSOpt = ::Opm::SatFunc::CreateEPS::EPSOptions;
using FValVec = ::Opm::SatFunc::CreateEPS::Vertical::FuncValVector;
using ScalPtr = std::unique_ptr;
namespace CritDispSat {
struct KrG {
static std::vector
twoPointMethod(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const RTEP& tep,
const bool activeOil);
static std::vector
alternateMethod(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const RTEP& tep,
const bool activeOil);
};
struct KrGO {
static std::vector
twoPointMethod(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const RTEP& tep);
static std::vector
alternateMethod(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const RTEP& tep);
};
struct KrOW {
static std::vector
twoPointMethod(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const RTEP& tep);
static std::vector
alternateMethod(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const RTEP& tep);
};
struct KrW {
static std::vector
twoPointMethod(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const RTEP& tep,
const bool activeOil);
static std::vector
alternateMethod(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const RTEP& tep,
const bool activeOil);
};
std::vector
transformedCritSat(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const std::vector& t,
const std::vector& left,
const std::vector& right);
} // namespace CritDispSat
struct Kr {
static ScalPtr
G(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
std::vector&& sdisp,
std::vector&& smax,
const FValVec& fval);
static ScalPtr
GO(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
std::vector&& sdisp,
std::vector&& smax,
const FValVec& fval);
static ScalPtr
OW(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
std::vector&& sdisp,
std::vector&& smax,
const FValVec& fval);
static ScalPtr
W(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
std::vector&& sdisp,
std::vector&& smax,
const FValVec& fval);
};
static ScalPtr
scalingFunction(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const EPSOpt& opt,
const RTEP& tep,
const FValVec& fvals);
} // namespace CritSatVertical
} // namespace Create
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Implementation of Create::TwoPoint::scalingFunction()
Create::TwoPoint::EPSPtr
Create::TwoPoint::Kr::G(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const RTEP& tep)
{
auto sgcr = sgCrit(G, init, tep);
auto sgu = sgMax (G, init, tep);
if ((sgcr.size() != sgu.size()) ||
(sgcr.size() != G.numCells()))
{
throw std::invalid_argument {
"Missing or Mismatching Gas End-Point "
"Specifications (SGCR and/or SGU)"
};
}
return EPSPtr {
new EPS { std::move(sgcr), std::move(sgu) }
};
}
Create::TwoPoint::EPSPtr
Create::TwoPoint::Kr::OG(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const RTEP& tep)
{
auto sogcr = sogCrit(G, init, tep);
if (sogcr.size() != G.numCells()) {
throw std::invalid_argument {
"Missing or Mismatching Critical Oil "
"Saturation in Oil/Gas System"
};
}
auto smax = soMax(G, init, tep);
return EPSPtr {
new EPS { std::move(sogcr), std::move(smax) }
};
}
Create::TwoPoint::EPSPtr
Create::TwoPoint::Kr::OW(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const RTEP& tep)
{
auto sowcr = sowCrit(G, init, tep);
if (sowcr.size() != G.numCells()) {
throw std::invalid_argument {
"Missing or Mismatching Critical Oil "
"Saturation in Oil/Water System"
};
}
auto smax = soMax(G, init, tep);
return EPSPtr {
new EPS { std::move(sowcr), std::move(smax) }
};
}
Create::TwoPoint::EPSPtr
Create::TwoPoint::Kr::W(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const RTEP& tep)
{
auto swcr = swCrit(G, init, tep);
auto swu = swMax (G, init, tep);
if (swcr.empty() || swu.empty()) {
throw std::invalid_argument {
"Missing Water End-Point Specifications (SWCR and/or SWU)"
};
}
return EPSPtr {
new EPS { std::move(swcr), std::move(swu) }
};
}
Create::TwoPoint::EPSPtr
Create::TwoPoint::Pc::GO(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const RTEP& tep)
{
// Use dedicated scaled Sg_conn for Pc if defined in at least one
// subgrid. Use SGL otherwise. Same default value (i.e., table's
// connate gas saturation) for both vectors.
const auto sgl = ::Opm::SatFunc::scaledConnateGas(G, init, tep);
auto sglpc = haveKeywordData(G, init, "SGLPC")
? gridDefaultedVector(G, init, "SGLPC", tep.conn.gas,
[](const double s) { return s; }, sgl)
: sgl;
auto sgu = sgMax(G, init, tep);
if ((sgl.size() != sgu.size()) ||
(sgl.size() != G.numCells()))
{
throw std::invalid_argument {
"Missing or Mismatching Connate or Maximum Gas "
"Saturation in Pcgo EPS"
};
}
return EPSPtr {
new EPS { std::move(sglpc), std::move(sgu) }
};
}
Create::TwoPoint::EPSPtr
Create::TwoPoint::Pc::OW(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const RTEP& tep)
{
// Use dedicated scaled Sw_conn for Pc if defined in at least one
// subgrid. Use SWL otherwise. Same default value (i.e., table's
// connate water saturation) for both vectors.
const auto swl = ::Opm::SatFunc::scaledConnateWater(G, init, tep);
auto swlpc = haveKeywordData(G, init, "SWLPC")
? gridDefaultedVector(G, init, "SWLPC", tep.conn.water,
[](const double s) { return s; }, swl)
: swl;
auto swu = swMax(G, init, tep);
if ((swl.size() != swu.size()) ||
(swl.size() != G.numCells()))
{
throw std::invalid_argument {
"Missing or Mismatching Connate or Maximum Water "
"Saturation in Pcow EPS"
};
}
return EPSPtr {
new EPS { std::move(swlpc), std::move(swu) }
};
}
Create::TwoPoint::EPSPtr
Create::TwoPoint::
scalingFunction(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const EPSOpt& opt,
const RTEP& tep)
{
using FCat = ::Opm::SatFunc::CreateEPS::FunctionCategory;
using SSys = ::Opm::SatFunc::CreateEPS::SubSystem;
using PhIdx = ::Opm::ECLPhaseIndex;
assert (((! opt.use3PtScaling) || (opt.curve == FCat::CapPress))
&& "Internal Error Selecting EPS Family");
if (opt.curve == FCat::Relperm) {
if (opt.subSys == SSys::OilWater) {
if (opt.thisPh == PhIdx::Vapour) {
throw std::invalid_argument {
"Cannot Create an EPS for Gas Relperm "
"in an Oil/Water System"
};
}
if (opt.thisPh == PhIdx::Aqua) {
return Create::TwoPoint::Kr::W(G, init, tep);
}
return Create::TwoPoint::Kr::OW(G, init, tep);
}
if (opt.subSys == SSys::OilGas) {
if (opt.thisPh == PhIdx::Aqua) {
throw std::invalid_argument {
"Cannot Create an EPS for Water Relperm "
"in an Oil/Gas System"
};
}
if (opt.thisPh == PhIdx::Vapour) {
return Create::TwoPoint::Kr::G(G, init, tep);
}
return Create::TwoPoint::Kr::OG(G, init, tep);
}
}
if (opt.curve == FCat::CapPress) {
if (opt.thisPh == PhIdx::Liquid) {
throw std::invalid_argument {
"Creating Capillary Pressure EPS as a Function "
"of Oil Saturation is not Supported"
};
}
if (opt.thisPh == PhIdx::Vapour) {
return Create::TwoPoint::Pc::GO(G, init, tep);
}
if (opt.thisPh == PhIdx::Aqua) {
return Create::TwoPoint::Pc::OW(G, init, tep);
}
}
// Invalid.
return EPSPtr{};
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Implementation of Create::TwoPoint::unscaledEndPoints()
std::vector
Create::TwoPoint::unscaledEndPoints(const RTEP& ep, const EPSOpt& opt)
{
using FCat = ::Opm::SatFunc::CreateEPS::FunctionCategory;
using SSys = ::Opm::SatFunc::CreateEPS::SubSystem;
using PhIdx = ::Opm::ECLPhaseIndex;
assert (((opt.curve == FCat::CapPress) ||
(! opt.use3PtScaling)) && "Internal Logic Error");
if (opt.curve == FCat::CapPress) {
// Left node is connate saturation, right node is max saturation.
if (opt.thisPh == PhIdx::Liquid) {
throw std::invalid_argument {
"No Capillary Pressure Function for Oil"
};
}
if (opt.thisPh == PhIdx::Aqua) {
return unscaledTwoPt(ep.conn.water, ep.smax.water);
}
if (opt.thisPh == PhIdx::Vapour) {
return unscaledTwoPt(ep.conn.gas, ep.smax.gas);
}
}
if (opt.curve == FCat::Relperm) {
// Left node is critical saturation, right node is max saturation.
if (opt.subSys == SSys::OilGas) {
if (opt.thisPh == PhIdx::Aqua) {
throw std::invalid_argument {
"Void Request for Unscaled Water Saturation "
"End-Points in Oil-Gas System"
};
}
if (opt.thisPh == PhIdx::Liquid) {
return unscaledTwoPt(ep.crit.oil_in_gas, ep.smax.oil);
}
if (opt.thisPh == PhIdx::Vapour) {
return unscaledTwoPt(ep.crit.gas, ep.smax.gas);
}
}
if (opt.subSys == SSys::OilWater) {
if (opt.thisPh == PhIdx::Aqua) {
return unscaledTwoPt(ep.crit.water, ep.smax.water);
}
if (opt.thisPh == PhIdx::Liquid) {
return unscaledTwoPt(ep.crit.oil_in_water, ep.smax.oil);
}
if (opt.thisPh == PhIdx::Vapour) {
throw std::invalid_argument {
"Void Request for Unscaled Gas Saturation "
"End-Points in Oil-Water System"
};
}
}
}
// Invalid.
return {};
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Implementation of Create::ThreePoint::scalingFunction()
Create::ThreePoint::EPSPtr
Create::ThreePoint::Kr::G(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const RTEP& tep)
{
auto sgcr = sgCrit(G, init, tep);
auto sgu = sgMax (G, init, tep);
if ((sgcr.size() != sgu.size()) ||
(sgcr.size() != G.numCells()))
{
throw std::invalid_argument {
"Missing or Mismatching Gas End-Point "
"Specifications (SGCR and/or SGU)"
};
}
auto sdisp = CritSatVertical::CritDispSat::
KrG::alternateMethod(G, init, tep, true);
return EPSPtr {
new EPS {
std::move(sgcr), std::move(sdisp), std::move(sgu)
}
};
}
Create::ThreePoint::EPSPtr
Create::ThreePoint::Kr::OG(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const RTEP& tep)
{
auto sogcr = sogCrit(G, init, tep);
if (sogcr.size() != G.numCells()) {
throw std::invalid_argument {
"Missing or Mismatching Critical Oil "
"Saturation in Oil/Gas System"
};
}
auto sdisp = CritSatVertical::CritDispSat::
KrGO::alternateMethod(G, init, tep);
auto smax = soMax(G, init, tep);
return EPSPtr {
new EPS {
std::move(sogcr), std::move(sdisp), std::move(smax)
}
};
}
Create::ThreePoint::EPSPtr
Create::ThreePoint::Kr::OW(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const RTEP& tep)
{
auto sowcr = sowCrit(G, init, tep);
if (sowcr.size() != G.numCells()) {
throw std::invalid_argument {
"Missing or Mismatching Critical Oil "
"Saturation in Oil/Water System"
};
}
auto sdisp = CritSatVertical::CritDispSat::
KrOW::alternateMethod(G, init, tep);
auto smax = soMax(G, init, tep);
return EPSPtr {
new EPS {
std::move(sowcr), std::move(sdisp), std::move(smax)
}
};
}
Create::ThreePoint::EPSPtr
Create::ThreePoint::Kr::W(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const RTEP& tep)
{
auto swcr = swCrit(G, init, tep);
auto swu = swMax (G, init, tep);
if ((swcr.size() != G.numCells()) ||
(swcr.size() != swu.size()))
{
throw std::invalid_argument {
"Missing Water End-Point Specifications (SWCR and/or SWU)"
};
}
auto sdisp = CritSatVertical::CritDispSat::
KrW::alternateMethod(G, init, tep, true);
return EPSPtr {
new EPS {
std::move(swcr), std::move(sdisp), std::move(swu)
}
};
}
Create::ThreePoint::EPSPtr
Create::ThreePoint::
scalingFunction(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const EPSOpt& opt,
const RTEP& tep)
{
#if !defined(NDEBUG)
using FCat = ::Opm::SatFunc::CreateEPS::FunctionCategory;
#endif // !defined(NDEBUG)
using SSys = ::Opm::SatFunc::CreateEPS::SubSystem;
using PhIdx = ::Opm::ECLPhaseIndex;
assert ((opt.use3PtScaling && (opt.curve == FCat::Relperm))
&& "Internal Error Selecting EPS Family");
if (opt.subSys == SSys::OilWater) {
if (opt.thisPh == PhIdx::Vapour) {
throw std::invalid_argument {
"Cannot Create a Three-Point EPS for "
"Gas Relperm in an Oil/Water System"
};
}
if (opt.thisPh == PhIdx::Aqua) {
return Create::ThreePoint::Kr::W(G, init, tep);
}
return Create::ThreePoint::Kr::OW(G, init, tep);
}
if (opt.subSys == SSys::OilGas) {
if (opt.thisPh == PhIdx::Aqua) {
throw std::invalid_argument {
"Cannot Create a Three-Point EPS for "
"Water Relperm in an Oil/Gas System"
};
}
if (opt.thisPh == PhIdx::Vapour) {
return Create::ThreePoint::Kr::G(G, init, tep);
}
return Create::ThreePoint::Kr::OG(G, init, tep);
}
// Invalid.
return EPSPtr{};
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Implementation of Create::ThreePoint::unscaledEndPoints()
std::vector
Create::ThreePoint::unscaledEndPoints(const RTEP& ep, const EPSOpt& opt)
{
#if !defined(NDEBUG)
using FCat = ::Opm::SatFunc::CreateEPS::FunctionCategory;
#endif // !defined(NDEBUG)
using SSys = ::Opm::SatFunc::CreateEPS::SubSystem;
using PhIdx = ::Opm::ECLPhaseIndex;
assert ((opt.use3PtScaling && (opt.curve == FCat::Relperm))
&& "Internal Error Selecting EPS Family");
auto sdisp = [](const std::vector& s1,
const std::vector& s2)
-> std::vector
{
auto sr = std::vector(s1.size(), 1.0);
for (auto n = s1.size(), i = 0*n; i < n; ++i) {
sr[i] -= s1[i] + s2[i];
}
return sr;
};
// Left node is critical saturation, middle node is displacing critical
// saturation, and right node is maximum saturation.
if (opt.subSys == SSys::OilGas) {
if (opt.thisPh == PhIdx::Aqua) {
throw std::invalid_argument {
"Void Request for Unscaled Water Saturation "
"End-Points in Oil-Gas System"
};
}
if (opt.thisPh == PhIdx::Liquid) {
return unscaledThreePt(ep.crit.oil_in_gas,
sdisp(ep.crit.gas, ep.conn.water),
ep.smax.oil);
}
if (opt.thisPh == PhIdx::Vapour) {
return unscaledThreePt(ep.crit.gas,
sdisp(ep.crit.oil_in_gas, ep.conn.water),
ep.smax.gas);
}
}
if (opt.subSys == SSys::OilWater) {
if (opt.thisPh == PhIdx::Aqua) {
return unscaledThreePt(ep.crit.water,
sdisp(ep.crit.oil_in_water, ep.conn.gas),
ep.smax.water);
}
if (opt.thisPh == PhIdx::Liquid) {
return unscaledThreePt(ep.crit.oil_in_water,
sdisp(ep.crit.water, ep.conn.gas),
ep.smax.oil);
}
if (opt.thisPh == PhIdx::Vapour) {
throw std::invalid_argument {
"Void Request for Unscaled Gas Saturation "
"End-Points in Oil-Water System"
};
}
}
// Invalid.
return {};
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Implementation of Create::PureVertical::scalingFunction()
namespace {
Create::PureVertical::ScalPtr
pureVerticalRelpermScaling(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const std::vector& dflt,
const std::string& vector)
{
auto scaledMaxKr =
gridDefaultedVector(G, init, vector, dflt,
[](const double kr) { return kr; });
return Create::PureVertical::ScalPtr {
new ::Opm::SatFunc::PureVerticalScaling(std::move(scaledMaxKr))
};
}
Create::PureVertical::ScalPtr
pureVerticalCapPressScaling(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const std::vector& dflt,
const std::string& vector)
{
const auto& ih = init.keywordData(INTEHEAD_KW);
const auto pscale = ::Opm::ECLUnits::
createUnitSystem(ih[ INTEHEAD_UNIT_INDEX ])->pressure();
auto scaledMaxPc =
gridDefaultedVector(G, init, vector, dflt,
[pscale](const double pc)
{
return ::ImportedOpm::unit::convert::from(pc, pscale);
});
return Create::PureVertical::ScalPtr {
new ::Opm::SatFunc::PureVerticalScaling(std::move(scaledMaxPc))
};
}
} // Anonymous
Create::PureVertical::ScalPtr
Create::PureVertical::Kr::G(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const std::vector& dflt)
{
return pureVerticalRelpermScaling(G, init, dflt, "KRG");
}
Create::PureVertical::ScalPtr
Create::PureVertical::Kr::O(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const std::vector& dflt)
{
return pureVerticalRelpermScaling(G, init, dflt, "KRO");
}
Create::PureVertical::ScalPtr
Create::PureVertical::Kr::W(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const std::vector& dflt)
{
return pureVerticalRelpermScaling(G, init, dflt, "KRW");
}
Create::PureVertical::ScalPtr
Create::PureVertical::Pc::GO(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const std::vector& dflt)
{
return pureVerticalCapPressScaling(G, init, dflt, "PCG");
}
Create::PureVertical::ScalPtr
Create::PureVertical::Pc::OW(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const std::vector& dflt)
{
return pureVerticalCapPressScaling(G, init, dflt, "PCW");
}
Create::PureVertical::ScalPtr
Create::PureVertical::
scalingFunction(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const EPSOpt& opt,
const FValVec& fvals)
{
using FCat = ::Opm::SatFunc::CreateEPS::FunctionCategory;
using SSys = ::Opm::SatFunc::CreateEPS::SubSystem;
using PhIdx = ::Opm::ECLPhaseIndex;
using FVal = ::Opm::SatFunc::VerticalScalingInterface::FunctionValues;
auto dfltVals = std::vector(fvals.size(), 0.0);
std::transform(std::begin(fvals), std::end(fvals),
std::begin(dfltVals),
[](const FVal& fv)
{
return fv.max.val;
});
if (opt.curve == FCat::Relperm) {
if (opt.subSys == SSys::OilGas) {
if (opt.thisPh == PhIdx::Aqua) {
throw std::invalid_argument {
"Cannot Create Vertical Scaling for "
"Water Relperm in an Oil/Gas System"
};
}
if (opt.thisPh == PhIdx::Vapour) {
return Create::PureVertical::Kr::G(G, init, dfltVals);
}
return Create::PureVertical::Kr::O(G, init, dfltVals);
}
if (opt.subSys == SSys::OilWater) {
if (opt.thisPh == PhIdx::Vapour) {
throw std::invalid_argument {
"Cannot Create Vertical Scaling for "
"Gas Relperm in an Oil/Water System"
};
}
if (opt.thisPh == PhIdx::Aqua) {
return Create::PureVertical::Kr::W(G, init, dfltVals);
}
return Create::PureVertical::Kr::O(G, init, dfltVals);
}
}
if (opt.curve == FCat::CapPress) {
if (opt.thisPh == PhIdx::Liquid) {
throw std::invalid_argument {
"Creating Capillary Pressure Vertical Scaling "
"as a Function of Oil Saturation is not Supported"
};
}
if (opt.thisPh == PhIdx::Vapour) {
return Create::PureVertical::Pc::GO(G, init, dfltVals);
}
if (opt.thisPh == PhIdx::Aqua) {
return Create::PureVertical::Pc::OW(G, init, dfltVals);
}
}
// Invalid.
return {};
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Implementation of Create::CritSatVertical::scalingFunction()
std::vector
Create::CritSatVertical::CritDispSat::
KrG::twoPointMethod(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const RTEP& tep,
const bool activeOil)
{
const auto& sgcr = tep.crit.gas;
const auto& sgu = tep.smax.gas;
auto t = std::vector(sgcr.size(), 0.0);
if (activeOil) {
// G/O or G/O/W system
for (auto n = sgcr.size(), i = 0*n; i < n; ++i) {
const auto sr = 1.0 - (tep.crit.oil_in_gas[i] +
tep.conn.water [i]);
t[i] = (sr - sgcr[i]) / (sgu[i] - sgcr[i]);
}
}
else {
// G/W system.
for (auto n = sgcr.size(), i = 0*n; i < n; ++i) {
const auto sr = 1.0 - tep.crit.water[i];
t[i] = (sr - sgcr[i]) / (sgu[i] - sgcr[i]);
}
}
return transformedCritSat(G, init, t,
sgCrit(G, init, tep),
sgMax (G, init, tep));
}
std::vector
Create::CritSatVertical::CritDispSat::
KrG::alternateMethod(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const RTEP& tep,
const bool activeOil)
{
auto sdisp = std::vector(G.numCells(), 0.0);
if (activeOil) {
// G/O or G/O/W system.
const auto sogcr = sogCrit(G, init, tep);
const auto swl = ::Opm::SatFunc::scaledConnateWater(G, init, tep);
std::transform(std::begin(sogcr), std::end(sogcr), std::begin(swl),
std::begin(sdisp),
[](const double so, const double sw) -> double
{
return 1.0 - (so + sw);
});
}
else {
// G/W system.
const auto swcr = swCrit(G, init, tep);
std::transform(std::begin(swcr), std::end(swcr),
std::begin(sdisp),
[](const double sw) -> double
{
return 1.0 - sw;
});
}
return sdisp;
}
std::vector
Create::CritSatVertical::CritDispSat::
KrGO::twoPointMethod(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const RTEP& tep)
{
const auto& sogcr = tep.crit.oil_in_gas;
auto t = std::vector(sogcr.size(), 0.0);
{
const auto& sgco = tep.conn.gas;
const auto& swco = tep.conn.water;
const auto& sgcr = tep.crit.gas;
for (auto n = sgcr.size(), i = 0*n; i < n; ++i) {
const auto sr = 1.0 - (sgcr[i] + swco[i]);
const auto smax = 1.0 - (sgco[i] + swco[i]); // >= sr
t[i] = (sr - sogcr[i]) / (smax - sogcr[i]);
}
}
return transformedCritSat(G, init, t,
sogCrit(G, init, tep),
soMax (G, init, tep));
}
std::vector
Create::CritSatVertical::CritDispSat::
KrGO::alternateMethod(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const RTEP& tep)
{
auto sdisp = std::vector(G.numCells(), 0.0);
const auto sgcr = sgCrit(G, init, tep);
const auto swl = ::Opm::SatFunc::scaledConnateWater(G, init, tep);
std::transform(std::begin(sgcr), std::end(sgcr), std::begin(swl),
std::begin(sdisp),
[](const double sg, const double sw) -> double
{
return 1.0 - (sg + sw);
});
return sdisp;
}
std::vector
Create::CritSatVertical::CritDispSat::
KrOW::twoPointMethod(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const RTEP& tep)
{
const auto& sowcr = tep.crit.oil_in_water;
auto t = std::vector(sowcr.size(), 0.0);
{
const auto& sgco = tep.conn.gas;
const auto& swco = tep.conn.water;
const auto& swcr = tep.crit.water;
for (auto n = swcr.size(), i = 0*n; i < n; ++i) {
const auto sr = 1.0 - (swcr[i] + sgco[i]);
const auto smax = 1.0 - (swco[i] + sgco[i]); // >= sr
t[i] = (sr - sowcr[i]) / (smax - sowcr[i]);
}
}
return transformedCritSat(G, init, t,
sowCrit(G, init, tep),
soMax (G, init, tep));
}
std::vector
Create::CritSatVertical::CritDispSat::
KrOW::alternateMethod(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const RTEP& tep)
{
auto sdisp = std::vector(G.numCells(), 0.0);
const auto swcr = swCrit(G, init, tep);
const auto sgl = ::Opm::SatFunc::scaledConnateGas(G, init, tep);
std::transform(std::begin(swcr), std::end(swcr), std::begin(sgl),
std::begin(sdisp),
[](const double sw, const double sg) -> double
{
return 1.0 - (sg + sw);
});
return sdisp;
}
std::vector
Create::CritSatVertical::CritDispSat::
KrW::twoPointMethod(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const RTEP& tep,
const bool activeOil)
{
const auto& swcr = tep.crit.water;
const auto& swu = tep.smax.water;
auto t = std::vector(swcr.size(), 0.0);
if (activeOil) {
// G/O or G/O/W system
for (auto n = swcr.size(), i = 0*n; i < n; ++i) {
const auto sr = 1.0 - (tep.crit.oil_in_water[i] +
tep.conn.gas [i]);
t[i] = (sr - swcr[i]) / (swu[i] - swcr[i]);
}
}
else {
// G/W system.
for (auto n = swcr.size(), i = 0*n; i < n; ++i) {
const auto sr = 1.0 - tep.crit.gas[i];
t[i] = (sr - swcr[i]) / (swu[i] - swcr[i]);
}
}
return transformedCritSat(G, init, t,
swCrit(G, init, tep),
swMax (G, init, tep));
}
std::vector
Create::CritSatVertical::CritDispSat::
KrW::alternateMethod(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const RTEP& tep,
const bool activeOil)
{
auto sdisp = std::vector(G.numCells(), 0.0);
if (activeOil) {
// G/O or G/O/W system.
const auto sowcr = sowCrit(G, init, tep);
const auto sgl = ::Opm::SatFunc::scaledConnateGas(G, init, tep);
std::transform(std::begin(sowcr), std::end(sowcr), std::begin(sgl),
std::begin(sdisp),
[](const double so, const double sg) -> double
{
return 1.0 - (so + sg);
});
}
else {
// G/W system.
const auto sgcr = sgCrit(G, init, tep);
std::transform(std::begin(sgcr), std::end(sgcr),
std::begin(sdisp),
[](const double sg) -> double
{
return 1.0 - sg;
});
}
return sdisp;
}
std::vector
Create::CritSatVertical::CritDispSat::
transformedCritSat(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const std::vector& t,
const std::vector& left,
const std::vector& right)
{
auto sdisp = std::vector(G.numCells(), 0.0);
auto cellID = std::vector::size_type{0};
for (const auto& gridID : G.activeGrids()) {
const auto nc = G.numCells(gridID);
const auto& snum = init.haveKeywordData("SATNUM", gridID)
? G.rawLinearisedCellData(init, "SATNUM", gridID)
: std::vector(nc, 1);
for (auto c = 0*nc; c < nc; ++c, ++cellID) {
const auto x = t[snum[c] - 1];
sdisp[cellID] = (1.0 - x)*left[cellID] + x*right[cellID];
}
}
return sdisp;
}
namespace {
std::vector
critDispSat(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const ::Opm::SatFunc::CreateEPS::EPSOptions& opt,
const ::Opm::SatFunc::CreateEPS::RawTableEndPoints& rtep)
{
namespace CDS = Create::CritSatVertical::CritDispSat;
if (opt.curve != ::Opm::SatFunc::CreateEPS::FunctionCategory::Relperm) {
return {};
}
const auto& ih = init.keywordData(INTEHEAD_KW);
const auto activeOil =
(ih[INTEHEAD_PHASE_INDEX] & (1u << 0u)) != 0;
if (opt.subSys == ::Opm::SatFunc::CreateEPS::SubSystem::OilGas) {
if (opt.thisPh == ::Opm::ECLPhaseIndex::Aqua) {
throw std::invalid_argument {
"Cannot request Critical Scaled Saturation "
"for water in Gas/Oil system"
};
}
if (opt.thisPh == ::Opm::ECLPhaseIndex::Liquid) {
return !opt.use3PtScaling
? CDS::KrGO::twoPointMethod (G, init, rtep)
: CDS::KrGO::alternateMethod(G, init, rtep);
}
return !opt.use3PtScaling
? CDS::KrG::twoPointMethod (G, init, rtep, activeOil)
: CDS::KrG::alternateMethod(G, init, rtep, activeOil);
}
if (opt.subSys == ::Opm::SatFunc::CreateEPS::SubSystem::OilWater) {
if (opt.thisPh == ::Opm::ECLPhaseIndex::Vapour) {
throw std::invalid_argument {
"Cannot request Critical Scaled Saturation "
"for gas in Oil/Water system"
};
}
if (opt.thisPh == ::Opm::ECLPhaseIndex::Liquid) {
return !opt.use3PtScaling
? CDS::KrOW::twoPointMethod (G, init, rtep)
: CDS::KrOW::alternateMethod(G, init, rtep);
}
return !opt.use3PtScaling
? CDS::KrW::twoPointMethod (G, init, rtep, activeOil)
: CDS::KrW::alternateMethod(G, init, rtep, activeOil);
}
// Invalid
return {};
}
std::vector
maximumSat(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const ::Opm::SatFunc::CreateEPS::EPSOptions& opt,
const ::Opm::SatFunc::CreateEPS::RawTableEndPoints& tep)
{
switch (opt.thisPh) {
case ::Opm::ECLPhaseIndex::Aqua: return swMax(G, init, tep);
case ::Opm::ECLPhaseIndex::Liquid: return soMax(G, init, tep);
case ::Opm::ECLPhaseIndex::Vapour: return sgMax(G, init, tep);
}
throw std::invalid_argument {
"Unsupported Phase Index"
};
}
}
Create::CritSatVertical::ScalPtr
Create::CritSatVertical::Kr::G(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
std::vector&& sdisp,
std::vector&& smax,
const FValVec& fval)
{
using FVal = ::Opm::SatFunc::VerticalScalingInterface::FunctionValues;
auto dflt_fdisp = std::vector(fval.size(), 0.0);
std::transform(std::begin(fval), std::end(fval),
std::begin(dflt_fdisp),
[](const FVal& fv) { return fv.disp.val;});
auto fdisp =
gridDefaultedVector(G, init, "KRGR", dflt_fdisp,
[](const double kr) { return kr; });
auto dflt_fmax = std::vector(fval.size(), 0.0);
std::transform(std::begin(fval), std::end(fval),
std::begin(dflt_fmax),
[](const FVal& fv) { return fv.max.val; });
auto fmax =
gridDefaultedVector(G, init, "KRG", dflt_fmax,
[](const double kr) { return kr; });
return ScalPtr {
new ::Opm::SatFunc::CritSatVerticalScaling {
std::move(sdisp), std::move(fdisp),
std::move(smax) , std::move(fmax)
}
};
}
Create::CritSatVertical::ScalPtr
Create::CritSatVertical::Kr::GO(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
std::vector&& sdisp,
std::vector&& smax,
const FValVec& fval)
{
using FVal = ::Opm::SatFunc::VerticalScalingInterface::FunctionValues;
auto dflt_fdisp = std::vector(fval.size(), 0.0);
std::transform(std::begin(fval), std::end(fval),
std::begin(dflt_fdisp),
[](const FVal& fv) { return fv.disp.val;});
auto fdisp =
gridDefaultedVector(G, init, "KRORG", dflt_fdisp,
[](const double kr) { return kr; });
auto dflt_fmax = std::vector(fval.size(), 0.0);
std::transform(std::begin(fval), std::end(fval),
std::begin(dflt_fmax),
[](const FVal& fv) { return fv.max.val; });
auto fmax =
gridDefaultedVector(G, init, "KRO", dflt_fmax,
[](const double kr) { return kr; });
return ScalPtr {
new ::Opm::SatFunc::CritSatVerticalScaling {
std::move(sdisp), std::move(fdisp),
std::move(smax) , std::move(fmax)
}
};
}
Create::CritSatVertical::ScalPtr
Create::CritSatVertical::Kr::OW(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
std::vector&& sdisp,
std::vector&& smax,
const FValVec& fval)
{
using FVal = ::Opm::SatFunc::VerticalScalingInterface::FunctionValues;
auto dflt_fdisp = std::vector(fval.size(), 0.0);
std::transform(std::begin(fval), std::end(fval),
std::begin(dflt_fdisp),
[](const FVal& fv) { return fv.disp.val;});
auto fdisp =
gridDefaultedVector(G, init, "KRORW", dflt_fdisp,
[](const double kr) { return kr; });
auto dflt_fmax = std::vector(fval.size(), 0.0);
std::transform(std::begin(fval), std::end(fval),
std::begin(dflt_fmax),
[](const FVal& fv) { return fv.max.val; });
auto fmax =
gridDefaultedVector(G, init, "KRO", dflt_fmax,
[](const double kr) { return kr; });
return ScalPtr {
new ::Opm::SatFunc::CritSatVerticalScaling {
std::move(sdisp), std::move(fdisp),
std::move(smax) , std::move(fmax)
}
};
}
Create::CritSatVertical::ScalPtr
Create::CritSatVertical::Kr::W(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
std::vector&& sdisp,
std::vector&& smax,
const FValVec& fval)
{
using FVal = ::Opm::SatFunc::VerticalScalingInterface::FunctionValues;
auto dflt_fdisp = std::vector(fval.size(), 0.0);
std::transform(std::begin(fval), std::end(fval),
std::begin(dflt_fdisp),
[](const FVal& fv) { return fv.disp.val;});
auto fdisp =
gridDefaultedVector(G, init, "KRWR", dflt_fdisp,
[](const double kr) { return kr; });
auto dflt_fmax = std::vector(fval.size(), 0.0);
std::transform(std::begin(fval), std::end(fval),
std::begin(dflt_fmax),
[](const FVal& fv) { return fv.max.val; });
auto fmax =
gridDefaultedVector(G, init, "KRW", dflt_fmax,
[](const double kr) { return kr; });
return ScalPtr {
new ::Opm::SatFunc::CritSatVerticalScaling {
std::move(sdisp), std::move(fdisp),
std::move(smax) , std::move(fmax)
}
};
}
Create::CritSatVertical::ScalPtr
Create::CritSatVertical::
scalingFunction(const ::Opm::ECLGraph& G,
const ::Opm::ECLInitFileData& init,
const EPSOpt& opt,
const RTEP& tep,
const FValVec& fvals)
{
using SSys = ::Opm::SatFunc::CreateEPS::SubSystem;
using PhIdx = ::Opm::ECLPhaseIndex;
auto scr = critDispSat(G, init, opt, tep);
auto smax = maximumSat (G, init, opt, tep);
if (opt.subSys == SSys::OilWater) {
if (opt.thisPh == PhIdx::Vapour) {
throw std::invalid_argument {
"Cannot Create Critical Saturation Vertical "
"Scaling for Gas Relperm in an Oil/Water System"
};
}
if (opt.thisPh == PhIdx::Aqua) {
return Create::CritSatVertical::
Kr::W(G, init, std::move(scr), std::move(smax), fvals);
}
return Create::CritSatVertical::
Kr::OW(G, init, std::move(scr), std::move(smax), fvals);
}
if (opt.subSys == SSys::OilGas) {
if (opt.thisPh == PhIdx::Aqua) {
throw std::invalid_argument {
"Cannot Create Critical Saturation Vertical "
"Scaling for Water Relperm in an Oil/Gas System"
};
}
if (opt.thisPh == PhIdx::Vapour) {
return Create::CritSatVertical::
Kr::G(G, init, std::move(scr), std::move(smax), fvals);
}
return Create::CritSatVertical::
Kr::GO(G, init, std::move(scr), std::move(smax), fvals);
}
// Invalid.
return {};
}
// #####################################################################
// =====================================================================
// Public Interface Below Separator
// =====================================================================
// #####################################################################
// Class Opm::SatFunc::EPSEvalInterface
Opm::SatFunc::EPSEvalInterface::~EPSEvalInterface()
{}
// ---------------------------------------------------------------------
// Class Opm::SatFunc::VerticalScalingInterface
Opm::SatFunc::VerticalScalingInterface::~VerticalScalingInterface()
{}
// ---------------------------------------------------------------------
// Class Opm::SatFunc::TwoPointScaling
Opm::SatFunc::TwoPointScaling::
TwoPointScaling(std::vector smin,
std::vector smax)
: pImpl_(new Impl(std::move(smin), std::move(smax)))
{}
Opm::SatFunc::TwoPointScaling::~TwoPointScaling()
{}
Opm::SatFunc::TwoPointScaling::
TwoPointScaling(const TwoPointScaling& rhs)
: pImpl_(new Impl(*rhs.pImpl_))
{}
Opm::SatFunc::TwoPointScaling::
TwoPointScaling(TwoPointScaling&& rhs)
: pImpl_(std::move(rhs.pImpl_))
{}
Opm::SatFunc::TwoPointScaling&
Opm::SatFunc::TwoPointScaling::operator=(const TwoPointScaling& rhs)
{
this->pImpl_.reset(new Impl(*rhs.pImpl_));
return *this;
}
Opm::SatFunc::TwoPointScaling&
Opm::SatFunc::TwoPointScaling::operator=(TwoPointScaling&& rhs)
{
this->pImpl_ = std::move(rhs.pImpl_);
return *this;
}
std::vector
Opm::SatFunc::TwoPointScaling::eval(const TableEndPoints& tep,
const SaturationPoints& sp) const
{
return this->pImpl_->eval(tep, sp);
}
std::vector
Opm::SatFunc::TwoPointScaling::reverse(const TableEndPoints& tep,
const SaturationPoints& sp) const
{
return this->pImpl_->reverse(tep, sp);
}
std::unique_ptr
Opm::SatFunc::TwoPointScaling::clone() const
{
return std::unique_ptr(new TwoPointScaling(*this));
}
// ---------------------------------------------------------------------
// Class Opm::SatFunc::PureVerticalScaling
Opm::SatFunc::PureVerticalScaling::
PureVerticalScaling(std::vector fmax)
: pImpl_(new Impl(std::move(fmax)))
{}
Opm::SatFunc::PureVerticalScaling::~PureVerticalScaling()
{}
Opm::SatFunc::PureVerticalScaling::
PureVerticalScaling(const PureVerticalScaling& rhs)
: pImpl_(new Impl(*rhs.pImpl_))
{}
Opm::SatFunc::PureVerticalScaling::
PureVerticalScaling(PureVerticalScaling&& rhs)
: pImpl_(std::move(rhs.pImpl_))
{}
Opm::SatFunc::PureVerticalScaling&
Opm::SatFunc::PureVerticalScaling::operator=(const PureVerticalScaling& rhs)
{
this->pImpl_.reset(new Impl(*rhs.pImpl_));
return *this;
}
Opm::SatFunc::PureVerticalScaling&
Opm::SatFunc::PureVerticalScaling::operator=(PureVerticalScaling&& rhs)
{
this->pImpl_ = std::move(rhs.pImpl_);
return *this;
}
std::vector
Opm::SatFunc::PureVerticalScaling::
vertScale(const FunctionValues& f,
const SaturationPoints& sp,
const std::vector& val) const
{
return this->pImpl_->vertScale(f, sp, val);
}
std::unique_ptr
Opm::SatFunc::PureVerticalScaling::clone() const
{
return std::unique_ptr(new PureVerticalScaling(*this));
}
// ---------------------------------------------------------------------
// Class Opm::SatFunc::ThreePointScaling
Opm::SatFunc::ThreePointScaling::
ThreePointScaling(std::vector smin,
std::vector sdisp,
std::vector smax)
: pImpl_(new Impl(std::move(smin), std::move(sdisp), std::move(smax)))
{}
Opm::SatFunc::ThreePointScaling::~ThreePointScaling()
{}
Opm::SatFunc::ThreePointScaling::
ThreePointScaling(const ThreePointScaling& rhs)
: pImpl_(new Impl(*rhs.pImpl_))
{}
Opm::SatFunc::ThreePointScaling::ThreePointScaling(ThreePointScaling&& rhs)
: pImpl_(std::move(rhs.pImpl_))
{}
Opm::SatFunc::ThreePointScaling&
Opm::SatFunc::ThreePointScaling::operator=(const ThreePointScaling& rhs)
{
this->pImpl_.reset(new Impl(*rhs.pImpl_));
return *this;
}
Opm::SatFunc::ThreePointScaling&
Opm::SatFunc::ThreePointScaling::operator=(ThreePointScaling&& rhs)
{
this->pImpl_ = std::move(rhs.pImpl_);
return *this;
}
std::vector
Opm::SatFunc::ThreePointScaling::eval(const TableEndPoints& tep,
const SaturationPoints& sp) const
{
return this->pImpl_->eval(tep, sp);
}
std::vector
Opm::SatFunc::ThreePointScaling::reverse(const TableEndPoints& tep,
const SaturationPoints& sp) const
{
return this->pImpl_->reverse(tep, sp);
}
std::unique_ptr
Opm::SatFunc::ThreePointScaling::clone() const
{
return std::unique_ptr(new ThreePointScaling(*this));
}
// ---------------------------------------------------------------------
// Class Opm::SatFunc::CritSatVerticalScaling
Opm::SatFunc::CritSatVerticalScaling::
CritSatVerticalScaling(std::vector sdisp,
std::vector fdisp,
std::vector smax,
std::vector fmax)
: pImpl_(new Impl(std::move(sdisp), std::move(fdisp),
std::move(smax) , std::move(fmax)))
{}
Opm::SatFunc::CritSatVerticalScaling::~CritSatVerticalScaling()
{}
Opm::SatFunc::CritSatVerticalScaling::
CritSatVerticalScaling(const CritSatVerticalScaling& rhs)
: pImpl_(new Impl(*rhs.pImpl_))
{}
Opm::SatFunc::CritSatVerticalScaling::
CritSatVerticalScaling(CritSatVerticalScaling&& rhs)
: pImpl_(std::move(rhs.pImpl_))
{}
Opm::SatFunc::CritSatVerticalScaling&
Opm::SatFunc::CritSatVerticalScaling::
operator=(const CritSatVerticalScaling& rhs)
{
this->pImpl_.reset(new Impl(*rhs.pImpl_));
return *this;
}
Opm::SatFunc::CritSatVerticalScaling&
Opm::SatFunc::CritSatVerticalScaling::
operator=(CritSatVerticalScaling&& rhs)
{
this->pImpl_ = std::move(rhs.pImpl_);
return *this;
}
std::vector
Opm::SatFunc::CritSatVerticalScaling::
vertScale(const FunctionValues& f,
const SaturationPoints& sp,
const std::vector& val) const
{
return this->pImpl_->vertScale(f, sp, val);
}
std::unique_ptr
Opm::SatFunc::CritSatVerticalScaling::clone() const
{
return std::unique_ptr {
new CritSatVerticalScaling(*this)
};
}
// ---------------------------------------------------------------------
// Factory function Opm::SatFunc::CreateEPS::Horizontal::fromECLOutput()
std::unique_ptr
Opm::SatFunc::CreateEPS::Horizontal::
fromECLOutput(const ECLGraph& G,
const ECLInitFileData& init,
const EPSOptions& opt,
const RawTableEndPoints& tep)
{
if ((opt.curve == FunctionCategory::CapPress) ||
(! opt.use3PtScaling))
{
return Create::TwoPoint::scalingFunction(G, init, opt, tep);
}
if ((opt.curve == FunctionCategory::Relperm) && opt.use3PtScaling)
{
return Create::ThreePoint::scalingFunction(G, init, opt, tep);
}
// Invalid
return std::unique_ptr{};
}
// ---------------------------------------------------------------------
// Factory function Opm::SatFunc::CreateEPS::Horizontal::unscaledEndPoints()
std::vector
Opm::SatFunc::CreateEPS::Horizontal::
unscaledEndPoints(const EPSOptions& opt,
const RawTableEndPoints& ep)
{
if ((opt.curve == FunctionCategory::CapPress) ||
(! opt.use3PtScaling))
{
return Create::TwoPoint::unscaledEndPoints(ep, opt);
}
if ((opt.curve == FunctionCategory::Relperm) && opt.use3PtScaling)
{
return Create::ThreePoint::unscaledEndPoints(ep, opt);
}
// Invalid
return {};
}
// ---------------------------------------------------------------------
// Factory function Opm::SatFunc::CreateEPS::Vertical::fromECLOutput()
std::unique_ptr
Opm::SatFunc::CreateEPS::Vertical::
fromECLOutput(const ECLGraph& G,
const ECLInitFileData& init,
const EPSOptions& opt,
const RawTableEndPoints& tep,
const FuncValVector& fvals)
{
const auto haveScaleCRS = haveScaledRelPermAtCritSat(G, init, opt);
if ((opt.curve == FunctionCategory::CapPress) || (! haveScaleCRS))
{
return Create::PureVertical::
scalingFunction(G, init, opt, fvals);
}
if ((opt.curve == FunctionCategory::Relperm) && haveScaleCRS)
{
return Create::CritSatVertical::
scalingFunction(G, init, opt, tep, fvals);
}
// Invalid
return {};
}
// ---------------------------------------------------------------------
// Factory function Opm::SatFunc::CreateEPS::Vertical::unscaledFunctionValues()
std::vector
Opm::SatFunc::CreateEPS::Vertical::
unscaledFunctionValues(const ECLGraph& G,
const ECLInitFileData& init,
const RawTableEndPoints& ep,
const EPSOptions& opt,
const SatFuncEvaluator& evalSF)
{
auto ret = std::vector{};
const auto haveScaleCRS = haveScaledRelPermAtCritSat(G, init, opt);
if ((opt.curve == FunctionCategory::CapPress) || (! haveScaleCRS)) {
auto opt_cpy = opt;
opt_cpy.use3PtScaling = false;
const auto uep =
Create::TwoPoint::unscaledEndPoints(ep, opt_cpy);
ret.resize(uep.size());
for (auto n = uep.size(), i = 0*n; i < n; ++i) {
ret[i].disp.sat = uep[i].disp;
ret[i].disp.val = evalSF(static_cast(i), ret[i].disp.sat);
ret[i].max.sat = uep[i].high;
ret[i].max.val = evalSF(static_cast(i), ret[i].max.sat);
}
}
else {
auto opt_cpy = opt;
opt_cpy.use3PtScaling = true;
const auto uep =
Create::ThreePoint::unscaledEndPoints(ep, opt_cpy);
ret.resize(uep.size());
for (auto n = uep.size(), i = 0*n; i < n; ++i) {
ret[i].disp.sat = uep[i].disp;
ret[i].disp.val = evalSF(static_cast(i), ret[i].disp.sat);
ret[i].max.sat = uep[i].high;
ret[i].max.val = evalSF(static_cast(i), ret[i].max.sat);
}
}
return ret;
}
// ---------------------------------------------------------------------
// Factory functions Opm::SatFunc::scaledConnate*()
std::vector
Opm::SatFunc::scaledConnateGas(const ECLGraph& G,
const ECLInitFileData& init,
const CreateEPS::RawTableEndPoints& tep)
{
return gridDefaultedVector(G, init, "SGL", tep.conn.gas,
[](const double s) { return s; });
}
std::vector
Opm::SatFunc::scaledConnateWater(const ECLGraph& G,
const ECLInitFileData& init,
const CreateEPS::RawTableEndPoints& tep)
{
return gridDefaultedVector(G, init, "SWL", tep.conn.water,
[](const double s) { return s; });
}