2017-05-29 06:08:32 -05:00
|
|
|
/*
|
2018-10-23 08:58:30 -05:00
|
|
|
Copyright 2018 Equinor ASA.
|
2017-05-29 06:08:32 -05:00
|
|
|
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 <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#if HAVE_CONFIG_H
|
|
|
|
#include <config.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <opm/utility/ECLEndPointScaling.hpp>
|
|
|
|
|
|
|
|
#include <opm/utility/ECLGraph.hpp>
|
|
|
|
#include <opm/utility/ECLPhaseIndex.hpp>
|
|
|
|
#include <opm/utility/ECLResultData.hpp>
|
|
|
|
|
2018-05-07 05:55:46 -05:00
|
|
|
#include <opm/utility/ECLUnitHandling.hpp>
|
|
|
|
#include <opm/utility/imported/Units.hpp>
|
|
|
|
|
|
|
|
#include <algorithm>
|
2017-05-29 06:08:32 -05:00
|
|
|
#include <cassert>
|
2017-12-13 03:05:33 -06:00
|
|
|
#include <cmath>
|
2017-05-29 06:08:32 -05:00
|
|
|
#include <exception>
|
2017-12-11 04:12:58 -06:00
|
|
|
#include <iterator>
|
2018-10-23 08:58:30 -05:00
|
|
|
#include <limits>
|
2017-12-11 04:12:58 -06:00
|
|
|
#include <numeric>
|
2017-05-29 06:08:32 -05:00
|
|
|
#include <stdexcept>
|
|
|
|
#include <utility>
|
|
|
|
#include <vector>
|
|
|
|
|
2018-05-07 05:55:46 -05:00
|
|
|
#include <ert/ecl/ecl_kw_magic.h>
|
|
|
|
|
2017-05-29 06:08:32 -05:00
|
|
|
namespace {
|
|
|
|
std::vector<Opm::SatFunc::EPSEvalInterface::TableEndPoints>
|
|
|
|
unscaledTwoPt(const std::vector<double>& min,
|
|
|
|
const std::vector<double>& 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>{};
|
|
|
|
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<Opm::SatFunc::EPSEvalInterface::TableEndPoints>
|
|
|
|
unscaledThreePt(const std::vector<double>& min ,
|
|
|
|
const std::vector<double>& disp,
|
|
|
|
const std::vector<double>& 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>{};
|
|
|
|
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;
|
|
|
|
}
|
2017-12-11 04:12:58 -06:00
|
|
|
|
2018-06-12 07:36:08 -05:00
|
|
|
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);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-05-07 05:55:46 -05:00
|
|
|
template <class CvrtVal>
|
|
|
|
std::vector<double>
|
|
|
|
gridDefaultedVector(const ::Opm::ECLGraph& G,
|
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const std::string& vector,
|
|
|
|
const std::vector<double>& dflt,
|
2018-11-12 17:36:51 -06:00
|
|
|
CvrtVal&& cvrt,
|
|
|
|
const std::vector<double>& fallback
|
|
|
|
= std::vector<double>{})
|
2018-05-07 05:55:46 -05:00
|
|
|
{
|
|
|
|
auto ret = std::vector<double>(G.numCells());
|
|
|
|
|
2018-11-12 17:36:51 -06:00
|
|
|
const auto sentinel = 1.0e20;
|
|
|
|
|
2018-05-07 05:55:46 -05:00
|
|
|
assert (! dflt.empty() && "Internal Error");
|
|
|
|
|
|
|
|
auto cellID = std::vector<double>::size_type{0};
|
|
|
|
for (const auto& gridID : G.activeGrids()) {
|
|
|
|
const auto nc = G.numCells(gridID);
|
|
|
|
|
|
|
|
const auto& snum = init.haveKeywordData("SATNUM", gridID)
|
|
|
|
? G.rawLinearisedCellData<int>(init, "SATNUM", gridID)
|
|
|
|
: std::vector<int>(nc, 1);
|
|
|
|
|
|
|
|
const auto& val = init.haveKeywordData(vector, gridID)
|
|
|
|
? G.rawLinearisedCellData<double>(init, vector, gridID)
|
|
|
|
: std::vector<double>(nc, -1.0e21);
|
|
|
|
|
|
|
|
for (auto c = 0*nc; c < nc; ++c, ++cellID) {
|
2018-11-12 17:36:51 -06:00
|
|
|
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];
|
|
|
|
}
|
2018-05-07 05:55:46 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-06-12 07:36:08 -05:00
|
|
|
std::vector<double>
|
|
|
|
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<double>
|
|
|
|
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<double>
|
|
|
|
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<double>
|
|
|
|
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<double>
|
|
|
|
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<double>
|
|
|
|
soMax(const ::Opm::ECLGraph& G,
|
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const ::Opm::SatFunc::CreateEPS::RawTableEndPoints& tep)
|
|
|
|
{
|
|
|
|
auto smax = std::vector<double>(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<double>
|
|
|
|
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; });
|
|
|
|
}
|
|
|
|
|
2018-01-24 09:37:32 -06:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-06-12 07:36:08 -05:00
|
|
|
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;
|
|
|
|
}
|
2017-05-29 06:08:32 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
// Class Opm::TwoPointScaling::Impl
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
|
|
|
|
class Opm::SatFunc::TwoPointScaling::Impl
|
|
|
|
{
|
|
|
|
public:
|
2018-05-07 05:55:46 -05:00
|
|
|
Impl(std::vector<double> smin,
|
|
|
|
std::vector<double> smax)
|
2018-10-23 08:58:30 -05:00
|
|
|
: smin_(std::move(smin)), smax_(std::move(smax))
|
2017-05-29 06:08:32 -05:00
|
|
|
{
|
|
|
|
if (this->smin_.size() != this->smax_.size()) {
|
|
|
|
throw std::invalid_argument {
|
|
|
|
"Size Mismatch Between Minimum and "
|
|
|
|
"Maximum Saturation Arrays"
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<double>
|
|
|
|
eval(const TableEndPoints& tep,
|
|
|
|
const SaturationPoints& sp) const;
|
|
|
|
|
2017-10-11 11:02:00 -05:00
|
|
|
std::vector<double>
|
|
|
|
reverse(const TableEndPoints& tep,
|
|
|
|
const SaturationPoints& sp) const;
|
|
|
|
|
2017-05-29 06:08:32 -05:00
|
|
|
private:
|
|
|
|
std::vector<double> smin_;
|
|
|
|
std::vector<double> smax_;
|
2017-12-11 04:12:58 -06:00
|
|
|
|
2018-01-24 09:37:32 -06:00
|
|
|
double sMin(const std::vector<int>::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<int>::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);
|
|
|
|
}
|
2017-05-29 06:08:32 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
std::vector<double>
|
|
|
|
Opm::SatFunc::TwoPointScaling::
|
|
|
|
Impl::eval(const TableEndPoints& tep,
|
|
|
|
const SaturationPoints& sp) const
|
|
|
|
{
|
|
|
|
const auto srng = tep.high - tep.low;
|
|
|
|
|
|
|
|
auto effsat = std::vector<double>{};
|
|
|
|
effsat.reserve(sp.size());
|
|
|
|
|
|
|
|
for (const auto& eval_pt : sp) {
|
|
|
|
const auto cell = eval_pt.cell;
|
|
|
|
|
2018-01-24 09:37:32 -06:00
|
|
|
const auto sLO = this->sMin(cell, tep);
|
|
|
|
const auto sHI = this->sMax(cell, tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-10-11 11:02:00 -05:00
|
|
|
std::vector<double>
|
|
|
|
Opm::SatFunc::TwoPointScaling::
|
|
|
|
Impl::reverse(const TableEndPoints& tep,
|
|
|
|
const SaturationPoints& sp) const
|
|
|
|
{
|
|
|
|
const auto srng = tep.high - tep.low;
|
|
|
|
|
|
|
|
auto unscaledsat = std::vector<double>{};
|
|
|
|
unscaledsat.reserve(sp.size());
|
|
|
|
|
|
|
|
for (const auto& eval_pt : sp) {
|
|
|
|
const auto cell = eval_pt.cell;
|
|
|
|
|
2018-01-24 09:37:32 -06:00
|
|
|
const auto sLO = this->sMin(cell, tep);
|
|
|
|
const auto sHI = this->sMax(cell, tep);
|
2017-10-11 11:02:00 -05:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-05-07 05:55:46 -05:00
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
// Class Opm::SatFunc::PureVerticalScaling::Impl
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
|
|
|
|
class Opm::SatFunc::PureVerticalScaling::Impl
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
explicit Impl(std::vector<double> fmax)
|
|
|
|
: fmax_(std::move(fmax))
|
2018-10-23 08:58:30 -05:00
|
|
|
, inf_ (std::numeric_limits<double>::has_infinity
|
|
|
|
? std::numeric_limits<double>::infinity()
|
|
|
|
: std::numeric_limits<double>::max())
|
2018-05-07 05:55:46 -05:00
|
|
|
{}
|
|
|
|
|
|
|
|
std::vector<double>
|
|
|
|
vertScale(const FunctionValues& f,
|
|
|
|
const SaturationPoints& sp,
|
|
|
|
std::vector<double> val) const;
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::vector<double> fmax_;
|
2018-10-23 08:58:30 -05:00
|
|
|
|
|
|
|
double inf_;
|
|
|
|
|
|
|
|
std::vector<double>
|
|
|
|
zeroMaxVal(const SaturationPoints& sp) const;
|
|
|
|
|
|
|
|
std::vector<double>
|
|
|
|
nonZeroMaxVal(const SaturationPoints& sp,
|
|
|
|
const double maxVal,
|
|
|
|
std::vector<double> val) const;
|
2018-05-07 05:55:46 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
std::vector<double>
|
|
|
|
Opm::SatFunc::PureVerticalScaling::Impl::
|
|
|
|
vertScale(const FunctionValues& f,
|
|
|
|
const SaturationPoints& sp,
|
|
|
|
std::vector<double> val) const
|
|
|
|
{
|
|
|
|
assert (sp.size() == val.size() && "Internal Error in Vertical Scaling");
|
|
|
|
|
|
|
|
const auto maxVal = f.max.val;
|
|
|
|
|
2018-10-23 08:58:30 -05:00
|
|
|
if (! (std::abs(maxVal) > 0.0)) {
|
|
|
|
return this->zeroMaxVal(sp);
|
|
|
|
}
|
|
|
|
|
|
|
|
return this->nonZeroMaxVal(sp, maxVal, std::move(val));
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<double>
|
|
|
|
Opm::SatFunc::PureVerticalScaling::Impl::
|
|
|
|
zeroMaxVal(const SaturationPoints& sp) const
|
|
|
|
{
|
|
|
|
auto ret = std::vector<double>(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<double>
|
|
|
|
Opm::SatFunc::PureVerticalScaling::Impl::
|
|
|
|
nonZeroMaxVal(const SaturationPoints& sp,
|
|
|
|
const double maxVal,
|
|
|
|
std::vector<double> val) const
|
|
|
|
{
|
|
|
|
auto ret = std::move(val);
|
|
|
|
|
2018-05-07 05:55:46 -05:00
|
|
|
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<double> sdisp,
|
|
|
|
std::vector<double> fdisp,
|
2018-06-12 07:36:08 -05:00
|
|
|
std::vector<double> smax,
|
2018-05-07 05:55:46 -05:00
|
|
|
std::vector<double> fmax)
|
|
|
|
: sdisp_(std::move(sdisp))
|
|
|
|
, fdisp_(std::move(fdisp))
|
2018-06-12 07:36:08 -05:00
|
|
|
, smax_ (std::move(smax))
|
2018-05-07 05:55:46 -05:00
|
|
|
, fmax_ (std::move(fmax))
|
|
|
|
{}
|
|
|
|
|
|
|
|
std::vector<double>
|
|
|
|
vertScale(const FunctionValues& f,
|
|
|
|
const SaturationPoints& sp,
|
|
|
|
std::vector<double> val) const;
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::vector<double> sdisp_;
|
|
|
|
std::vector<double> fdisp_;
|
2018-06-12 07:36:08 -05:00
|
|
|
std::vector<double> smax_;
|
2018-05-07 05:55:46 -05:00
|
|
|
std::vector<double> fmax_;
|
|
|
|
};
|
|
|
|
|
|
|
|
std::vector<double>
|
|
|
|
Opm::SatFunc::CritSatVerticalScaling::Impl::
|
|
|
|
vertScale(const FunctionValues& f,
|
|
|
|
const SaturationPoints& sp,
|
|
|
|
std::vector<double> val) const
|
|
|
|
{
|
2018-11-12 17:41:50 -06:00
|
|
|
assert ((sp.size() == val.size()) && "Internal Error in Vertical Scaling");
|
2018-05-07 05:55:46 -05:00
|
|
|
|
|
|
|
auto ret = std::move(val);
|
|
|
|
|
2018-06-12 07:36:08 -05:00
|
|
|
const auto fdisp = f.disp.val;
|
2018-11-12 17:41:50 -06:00
|
|
|
const auto fmax = std::max(f.disp.val, f.max.val);
|
2018-06-12 07:36:08 -05:00
|
|
|
const auto sepfv = fmax > fdisp;
|
2018-05-07 05:55:46 -05:00
|
|
|
|
|
|
|
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;
|
2018-10-23 08:58:30 -05:00
|
|
|
const auto sr = std::min(this->sdisp_[c], this->smax_[c]);
|
2018-05-07 05:55:46 -05:00
|
|
|
const auto fr = this->fdisp_[c];
|
2018-06-12 07:36:08 -05:00
|
|
|
const auto sm = this->smax_ [c];
|
2018-05-07 05:55:46 -05:00
|
|
|
const auto fm = this->fmax_ [c];
|
|
|
|
|
2018-10-23 08:58:30 -05:00
|
|
|
if (s < sr) {
|
|
|
|
// s < sr: Pure vertical scaling in left interval.
|
2018-05-07 05:55:46 -05:00
|
|
|
y *= fr / fdisp;
|
|
|
|
}
|
|
|
|
else if (sepfv) {
|
2018-10-23 08:58:30 -05:00
|
|
|
// s \in [sr, sm), sm > sr; normal case: Kr(Smax) > Kr(Sr).
|
|
|
|
//
|
|
|
|
// Linear function between (sr,fr) and (sm,fm) in terms of
|
2018-10-24 06:03:59 -05:00
|
|
|
// function value 'y'. This usually alters the shape of the
|
|
|
|
// relative permeability function in this interval (e.g.,
|
|
|
|
// roughly quadratic to linear).
|
2018-05-07 05:55:46 -05:00
|
|
|
const auto t = (y - fdisp) / (fmax - fdisp);
|
|
|
|
|
|
|
|
y = fr + t*(fm - fr);
|
|
|
|
}
|
2018-06-12 07:36:08 -05:00
|
|
|
else if (s < sm) {
|
2018-10-23 08:58:30 -05:00
|
|
|
// s \in [sr, sm), sm > sr; special case: Kr(Smax) == Kr(Sr).
|
|
|
|
//
|
|
|
|
// Use linear function between (sr,fr) and (sm,fm) in terms of
|
2018-10-24 06:03:59 -05:00
|
|
|
// saturation value 's'. This usually alters the shape of the
|
|
|
|
// relative permeability function in this interval (e.g.,
|
|
|
|
// roughly quadratic to linear).
|
2018-06-12 07:36:08 -05:00
|
|
|
const auto t = (s - sr) / (sm - sr);
|
2018-05-07 05:55:46 -05:00
|
|
|
|
|
|
|
y = fr + t*(fm - fr);
|
|
|
|
}
|
|
|
|
else {
|
2018-06-12 07:36:08 -05:00
|
|
|
// sm == sr (pure scaling). Almost arbitrarily pick fmax_[c].
|
2018-05-07 05:55:46 -05:00
|
|
|
y = fm;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-05-29 06:08:32 -05:00
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
// Class Opm::ThreePointScaling::Impl
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
|
|
|
|
class Opm::SatFunc::ThreePointScaling::Impl
|
|
|
|
{
|
|
|
|
public:
|
2018-05-07 05:55:46 -05:00
|
|
|
Impl(std::vector<double> smin ,
|
|
|
|
std::vector<double> sdisp,
|
|
|
|
std::vector<double> smax )
|
2018-10-23 08:58:30 -05:00
|
|
|
: smin_(std::move(smin)), sdisp_(std::move(sdisp)), smax_(std::move(smax))
|
2017-05-29 06:08:32 -05:00
|
|
|
{
|
|
|
|
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<double>
|
|
|
|
eval(const TableEndPoints& tep,
|
|
|
|
const SaturationPoints& sp) const;
|
|
|
|
|
2017-10-11 11:02:00 -05:00
|
|
|
std::vector<double>
|
|
|
|
reverse(const TableEndPoints& tep,
|
|
|
|
const SaturationPoints& sp) const;
|
|
|
|
|
2017-05-29 06:08:32 -05:00
|
|
|
private:
|
|
|
|
std::vector<double> smin_;
|
|
|
|
std::vector<double> sdisp_;
|
|
|
|
std::vector<double> smax_;
|
2017-12-11 04:12:58 -06:00
|
|
|
|
2018-01-24 09:37:32 -06:00
|
|
|
double sMin(const std::vector<int>::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<int>::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<int>::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);
|
|
|
|
}
|
2017-05-29 06:08:32 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
std::vector<double>
|
|
|
|
Opm::SatFunc::ThreePointScaling::
|
|
|
|
Impl::eval(const TableEndPoints& tep,
|
|
|
|
const SaturationPoints& sp) const
|
|
|
|
{
|
|
|
|
auto effsat = std::vector<double>{};
|
|
|
|
effsat.reserve(sp.size());
|
|
|
|
|
|
|
|
for (const auto& eval_pt : sp) {
|
|
|
|
const auto cell = eval_pt.cell;
|
|
|
|
|
2018-01-24 09:37:32 -06:00
|
|
|
const auto sLO = this->sMin (cell, tep);
|
|
|
|
const auto sR = this->sDisp(cell, tep);
|
|
|
|
const auto sHI = this->sMax (cell, tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
|
2017-12-11 04:12:58 -06:00
|
|
|
effsat.push_back(0.0);
|
|
|
|
auto& s_eff = effsat.back();
|
|
|
|
|
2017-05-29 06:08:32 -05:00
|
|
|
if (! (eval_pt.sat > sLO)) {
|
|
|
|
// s <= sLO
|
|
|
|
s_eff = tep.low;
|
|
|
|
}
|
2018-10-23 08:58:30 -05:00
|
|
|
else if (eval_pt.sat < std::min(sR, sHI)) {
|
|
|
|
// s in scaled interval [sLO, sR)
|
|
|
|
// Map to tabulated saturation in [tep.low, tep.disp)
|
2017-05-29 06:08:32 -05:00
|
|
|
const auto t = (eval_pt.sat - sLO) / (sR - sLO);
|
|
|
|
|
|
|
|
s_eff = tep.low + t*(tep.disp - tep.low);
|
|
|
|
}
|
2018-10-23 08:58:30 -05:00
|
|
|
else if (eval_pt.sat < sHI) {
|
|
|
|
// s in scaled interval [sR, sHI)
|
|
|
|
// Map to tabulated saturation in [tep.disp, tep.high)
|
|
|
|
assert (sHI > sR);
|
|
|
|
|
2017-05-29 06:08:32 -05:00
|
|
|
const auto t = (eval_pt.sat - sR) / (sHI - sR);
|
|
|
|
|
|
|
|
s_eff = tep.disp + t*(tep.high - tep.disp);
|
|
|
|
}
|
2018-10-23 08:58:30 -05:00
|
|
|
else {
|
|
|
|
// s >= sHI
|
|
|
|
s_eff = tep.high;
|
|
|
|
}
|
2017-05-29 06:08:32 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return effsat;
|
|
|
|
}
|
|
|
|
|
2017-10-11 11:02:00 -05:00
|
|
|
std::vector<double>
|
|
|
|
Opm::SatFunc::ThreePointScaling::
|
|
|
|
Impl::reverse(const TableEndPoints& tep,
|
|
|
|
const SaturationPoints& sp) const
|
|
|
|
{
|
|
|
|
auto unscaledsat = std::vector<double>{};
|
|
|
|
unscaledsat.reserve(sp.size());
|
|
|
|
|
|
|
|
for (const auto& eval_pt : sp) {
|
|
|
|
const auto cell = eval_pt.cell;
|
|
|
|
|
2018-01-24 09:37:32 -06:00
|
|
|
const auto sLO = this->sMin (cell, tep);
|
|
|
|
const auto sR = this->sDisp(cell, tep);
|
|
|
|
const auto sHI = this->sMax (cell, tep);
|
2017-10-11 11:02:00 -05:00
|
|
|
|
2017-12-13 03:05:33 -06:00
|
|
|
unscaledsat.push_back(0.0);
|
|
|
|
auto& s_unsc = unscaledsat.back();
|
|
|
|
|
2017-10-11 11:02:00 -05:00
|
|
|
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) {
|
2018-10-23 08:58:30 -05:00
|
|
|
// s in tabulated interval [tep.low, tep.disp)
|
2017-10-11 11:02:00 -05:00
|
|
|
// Map to Input Saturation in (sLO, sR)
|
|
|
|
const auto t =
|
|
|
|
(eval_pt.sat - tep.low)
|
|
|
|
/ (tep.disp - tep.low);
|
|
|
|
|
2018-10-23 08:58:30 -05:00
|
|
|
s_unsc = std::min(sLO + t*(sR - sLO), sHI);
|
2017-10-11 11:02:00 -05:00
|
|
|
}
|
2018-10-23 08:58:30 -05:00
|
|
|
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);
|
|
|
|
|
2017-10-11 11:02:00 -05:00
|
|
|
const auto t =
|
|
|
|
(eval_pt.sat - tep.disp)
|
|
|
|
/ (tep.high - tep.disp);
|
|
|
|
|
2018-10-23 08:58:30 -05:00
|
|
|
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;
|
2017-10-11 11:02:00 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return unscaledsat;
|
|
|
|
}
|
|
|
|
|
2017-05-29 06:08:32 -05:00
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
// 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<EPS>;
|
|
|
|
|
|
|
|
struct Kr {
|
|
|
|
static EPSPtr
|
|
|
|
G(const ::Opm::ECLGraph& G,
|
2018-06-12 07:36:08 -05:00
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const RTEP& tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
|
|
|
|
static EPSPtr
|
|
|
|
OG(const ::Opm::ECLGraph& G,
|
2018-06-12 07:36:08 -05:00
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const RTEP& tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
|
|
|
|
static EPSPtr
|
|
|
|
OW(const ::Opm::ECLGraph& G,
|
2018-06-12 07:36:08 -05:00
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const RTEP& tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
|
|
|
|
static EPSPtr
|
|
|
|
W(const ::Opm::ECLGraph& G,
|
2018-06-12 07:36:08 -05:00
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const RTEP& tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
struct Pc {
|
|
|
|
static EPSPtr
|
|
|
|
GO(const ::Opm::ECLGraph& G,
|
2018-06-12 07:36:08 -05:00
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const RTEP& tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
|
|
|
|
static EPSPtr
|
|
|
|
OW(const ::Opm::ECLGraph& G,
|
2018-06-12 07:36:08 -05:00
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const RTEP& tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
static EPSPtr
|
|
|
|
scalingFunction(const ::Opm::ECLGraph& G,
|
|
|
|
const ::Opm::ECLInitFileData& init,
|
2018-06-12 07:36:08 -05:00
|
|
|
const EPSOpt& opt,
|
|
|
|
const RTEP& tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
|
|
|
|
static std::vector<TEP>
|
|
|
|
unscaledEndPoints(const RTEP& ep, const EPSOpt& opt);
|
|
|
|
} // namespace TwoPoint
|
|
|
|
|
|
|
|
namespace ThreePoint {
|
|
|
|
using EPS = ::Opm::SatFunc::ThreePointScaling;
|
|
|
|
using EPSPtr = std::unique_ptr<EPS>;
|
|
|
|
|
|
|
|
struct Kr {
|
|
|
|
static EPSPtr
|
|
|
|
G(const ::Opm::ECLGraph& G,
|
2018-06-12 07:36:08 -05:00
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const RTEP& tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
|
|
|
|
static EPSPtr
|
|
|
|
OG(const ::Opm::ECLGraph& G,
|
2018-06-12 07:36:08 -05:00
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const RTEP& tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
|
|
|
|
static EPSPtr
|
|
|
|
OW(const ::Opm::ECLGraph& G,
|
2018-06-12 07:36:08 -05:00
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const RTEP& tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
|
|
|
|
static EPSPtr
|
|
|
|
W(const ::Opm::ECLGraph& G,
|
2018-06-12 07:36:08 -05:00
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const RTEP& tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
static EPSPtr
|
2018-06-12 07:36:08 -05:00
|
|
|
scalingFunction(const ::Opm::ECLGraph& G,
|
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const EPSOpt& opt,
|
|
|
|
const RTEP& tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
|
|
|
|
static std::vector<TEP>
|
|
|
|
unscaledEndPoints(const RTEP& ep, const EPSOpt& opt);
|
|
|
|
} // namespace ThreePoint
|
2018-05-07 05:55:46 -05:00
|
|
|
|
|
|
|
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<Scaling>;
|
|
|
|
|
|
|
|
struct Kr {
|
|
|
|
static ScalPtr
|
|
|
|
G(const ::Opm::ECLGraph& G,
|
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const std::vector<double>& dflt);
|
|
|
|
|
|
|
|
static ScalPtr
|
|
|
|
O(const ::Opm::ECLGraph& G,
|
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const std::vector<double>& dflt);
|
|
|
|
|
|
|
|
static ScalPtr
|
|
|
|
W(const ::Opm::ECLGraph& G,
|
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const std::vector<double>& dflt);
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Pc {
|
|
|
|
static ScalPtr
|
|
|
|
GO(const ::Opm::ECLGraph& G,
|
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const std::vector<double>& dflt);
|
|
|
|
|
|
|
|
static ScalPtr
|
|
|
|
OW(const ::Opm::ECLGraph& G,
|
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const std::vector<double>& 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<Scaling>;
|
|
|
|
|
2018-06-12 07:36:08 -05:00
|
|
|
namespace CritDispSat {
|
|
|
|
struct KrG {
|
|
|
|
static std::vector<double>
|
|
|
|
twoPointMethod(const ::Opm::ECLGraph& G,
|
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const RTEP& tep,
|
|
|
|
const bool activeOil);
|
|
|
|
|
|
|
|
static std::vector<double>
|
|
|
|
alternateMethod(const ::Opm::ECLGraph& G,
|
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const RTEP& tep,
|
|
|
|
const bool activeOil);
|
|
|
|
};
|
|
|
|
|
|
|
|
struct KrGO {
|
|
|
|
static std::vector<double>
|
|
|
|
twoPointMethod(const ::Opm::ECLGraph& G,
|
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const RTEP& tep);
|
|
|
|
|
|
|
|
static std::vector<double>
|
|
|
|
alternateMethod(const ::Opm::ECLGraph& G,
|
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const RTEP& tep);
|
|
|
|
};
|
|
|
|
|
|
|
|
struct KrOW {
|
|
|
|
static std::vector<double>
|
|
|
|
twoPointMethod(const ::Opm::ECLGraph& G,
|
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const RTEP& tep);
|
|
|
|
|
|
|
|
static std::vector<double>
|
|
|
|
alternateMethod(const ::Opm::ECLGraph& G,
|
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const RTEP& tep);
|
|
|
|
};
|
|
|
|
|
|
|
|
struct KrW {
|
|
|
|
static std::vector<double>
|
|
|
|
twoPointMethod(const ::Opm::ECLGraph& G,
|
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const RTEP& tep,
|
|
|
|
const bool activeOil);
|
|
|
|
|
|
|
|
static std::vector<double>
|
|
|
|
alternateMethod(const ::Opm::ECLGraph& G,
|
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const RTEP& tep,
|
|
|
|
const bool activeOil);
|
|
|
|
};
|
|
|
|
|
|
|
|
std::vector<double>
|
|
|
|
transformedCritSat(const ::Opm::ECLGraph& G,
|
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const std::vector<double>& t,
|
|
|
|
const std::vector<double>& left,
|
|
|
|
const std::vector<double>& right);
|
|
|
|
} // namespace CritDispSat
|
|
|
|
|
2018-05-07 05:55:46 -05:00
|
|
|
struct Kr {
|
|
|
|
static ScalPtr
|
|
|
|
G(const ::Opm::ECLGraph& G,
|
|
|
|
const ::Opm::ECLInitFileData& init,
|
2018-06-12 07:36:08 -05:00
|
|
|
std::vector<double>&& sdisp,
|
|
|
|
std::vector<double>&& smax,
|
2018-05-07 05:55:46 -05:00
|
|
|
const FValVec& fval);
|
|
|
|
|
|
|
|
static ScalPtr
|
|
|
|
GO(const ::Opm::ECLGraph& G,
|
|
|
|
const ::Opm::ECLInitFileData& init,
|
2018-06-12 07:36:08 -05:00
|
|
|
std::vector<double>&& sdisp,
|
|
|
|
std::vector<double>&& smax,
|
2018-05-07 05:55:46 -05:00
|
|
|
const FValVec& fval);
|
|
|
|
|
|
|
|
static ScalPtr
|
|
|
|
OW(const ::Opm::ECLGraph& G,
|
|
|
|
const ::Opm::ECLInitFileData& init,
|
2018-06-12 07:36:08 -05:00
|
|
|
std::vector<double>&& sdisp,
|
|
|
|
std::vector<double>&& smax,
|
2018-05-07 05:55:46 -05:00
|
|
|
const FValVec& fval);
|
|
|
|
|
|
|
|
static ScalPtr
|
|
|
|
W(const ::Opm::ECLGraph& G,
|
|
|
|
const ::Opm::ECLInitFileData& init,
|
2018-06-12 07:36:08 -05:00
|
|
|
std::vector<double>&& sdisp,
|
|
|
|
std::vector<double>&& smax,
|
2018-05-07 05:55:46 -05:00
|
|
|
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
|
2017-05-29 06:08:32 -05:00
|
|
|
} // namespace Create
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
// Implementation of Create::TwoPoint::scalingFunction()
|
|
|
|
Create::TwoPoint::EPSPtr
|
|
|
|
Create::TwoPoint::Kr::G(const ::Opm::ECLGraph& G,
|
2018-06-12 07:36:08 -05:00
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const RTEP& tep)
|
2017-05-29 06:08:32 -05:00
|
|
|
{
|
2018-06-12 07:36:08 -05:00
|
|
|
auto sgcr = sgCrit(G, init, tep);
|
|
|
|
auto sgu = sgMax (G, init, tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
|
|
|
|
if ((sgcr.size() != sgu.size()) ||
|
|
|
|
(sgcr.size() != G.numCells()))
|
|
|
|
{
|
|
|
|
throw std::invalid_argument {
|
|
|
|
"Missing or Mismatching Gas End-Point "
|
|
|
|
"Specifications (SGCR and/or SGU)"
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2017-12-11 04:12:58 -06:00
|
|
|
return EPSPtr {
|
2018-05-07 05:55:46 -05:00
|
|
|
new EPS { std::move(sgcr), std::move(sgu) }
|
2017-12-11 04:12:58 -06:00
|
|
|
};
|
2017-05-29 06:08:32 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
Create::TwoPoint::EPSPtr
|
|
|
|
Create::TwoPoint::Kr::OG(const ::Opm::ECLGraph& G,
|
2018-06-12 07:36:08 -05:00
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const RTEP& tep)
|
2017-05-29 06:08:32 -05:00
|
|
|
{
|
2018-06-12 07:36:08 -05:00
|
|
|
auto sogcr = sogCrit(G, init, tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
|
|
|
|
if (sogcr.size() != G.numCells()) {
|
|
|
|
throw std::invalid_argument {
|
|
|
|
"Missing or Mismatching Critical Oil "
|
|
|
|
"Saturation in Oil/Gas System"
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2018-06-12 07:36:08 -05:00
|
|
|
auto smax = soMax(G, init, tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
|
2017-12-11 04:12:58 -06:00
|
|
|
return EPSPtr {
|
2018-05-07 05:55:46 -05:00
|
|
|
new EPS { std::move(sogcr), std::move(smax) }
|
2017-12-11 04:12:58 -06:00
|
|
|
};
|
2017-05-29 06:08:32 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
Create::TwoPoint::EPSPtr
|
|
|
|
Create::TwoPoint::Kr::OW(const ::Opm::ECLGraph& G,
|
2018-06-12 07:36:08 -05:00
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const RTEP& tep)
|
2017-05-29 06:08:32 -05:00
|
|
|
{
|
2018-06-12 07:36:08 -05:00
|
|
|
auto sowcr = sowCrit(G, init, tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
|
|
|
|
if (sowcr.size() != G.numCells()) {
|
|
|
|
throw std::invalid_argument {
|
|
|
|
"Missing or Mismatching Critical Oil "
|
|
|
|
"Saturation in Oil/Water System"
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2018-06-12 07:36:08 -05:00
|
|
|
auto smax = soMax(G, init, tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
|
2017-12-11 04:12:58 -06:00
|
|
|
return EPSPtr {
|
2018-05-07 05:55:46 -05:00
|
|
|
new EPS { std::move(sowcr), std::move(smax) }
|
2017-12-11 04:12:58 -06:00
|
|
|
};
|
2017-05-29 06:08:32 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
Create::TwoPoint::EPSPtr
|
|
|
|
Create::TwoPoint::Kr::W(const ::Opm::ECLGraph& G,
|
2018-06-12 07:36:08 -05:00
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const RTEP& tep)
|
2017-05-29 06:08:32 -05:00
|
|
|
{
|
2018-06-12 07:36:08 -05:00
|
|
|
auto swcr = swCrit(G, init, tep);
|
|
|
|
auto swu = swMax (G, init, tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
|
|
|
|
if (swcr.empty() || swu.empty()) {
|
|
|
|
throw std::invalid_argument {
|
|
|
|
"Missing Water End-Point Specifications (SWCR and/or SWU)"
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2017-12-11 04:12:58 -06:00
|
|
|
return EPSPtr {
|
2018-05-07 05:55:46 -05:00
|
|
|
new EPS { std::move(swcr), std::move(swu) }
|
2017-12-11 04:12:58 -06:00
|
|
|
};
|
2017-05-29 06:08:32 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
Create::TwoPoint::EPSPtr
|
|
|
|
Create::TwoPoint::Pc::GO(const ::Opm::ECLGraph& G,
|
2018-06-12 07:36:08 -05:00
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const RTEP& tep)
|
2017-05-29 06:08:32 -05:00
|
|
|
{
|
2018-06-12 07:36:08 -05:00
|
|
|
// 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.
|
2018-11-12 17:36:51 -06:00
|
|
|
const auto sgl = ::Opm::SatFunc::scaledConnateGas(G, init, tep);
|
|
|
|
|
|
|
|
auto sglpc = haveKeywordData(G, init, "SGLPC")
|
2018-06-12 07:36:08 -05:00
|
|
|
? gridDefaultedVector(G, init, "SGLPC", tep.conn.gas,
|
2018-11-12 17:36:51 -06:00
|
|
|
[](const double s) { return s; }, sgl)
|
|
|
|
: sgl;
|
2018-01-24 09:37:32 -06:00
|
|
|
|
2018-06-12 07:36:08 -05:00
|
|
|
auto sgu = sgMax(G, init, tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
|
|
|
|
if ((sgl.size() != sgu.size()) ||
|
|
|
|
(sgl.size() != G.numCells()))
|
|
|
|
{
|
|
|
|
throw std::invalid_argument {
|
|
|
|
"Missing or Mismatching Connate or Maximum Gas "
|
|
|
|
"Saturation in Pcgo EPS"
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2017-12-11 04:12:58 -06:00
|
|
|
return EPSPtr {
|
2018-11-12 17:36:51 -06:00
|
|
|
new EPS { std::move(sglpc), std::move(sgu) }
|
2017-12-11 04:12:58 -06:00
|
|
|
};
|
2017-05-29 06:08:32 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
Create::TwoPoint::EPSPtr
|
|
|
|
Create::TwoPoint::Pc::OW(const ::Opm::ECLGraph& G,
|
2018-06-12 07:36:08 -05:00
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const RTEP& tep)
|
2017-05-29 06:08:32 -05:00
|
|
|
{
|
2018-06-12 07:36:08 -05:00
|
|
|
// 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.
|
2018-11-12 17:36:51 -06:00
|
|
|
const auto swl = ::Opm::SatFunc::scaledConnateWater(G, init, tep);
|
|
|
|
|
|
|
|
auto swlpc = haveKeywordData(G, init, "SWLPC")
|
2018-06-12 07:36:08 -05:00
|
|
|
? gridDefaultedVector(G, init, "SWLPC", tep.conn.water,
|
2018-11-12 17:36:51 -06:00
|
|
|
[](const double s) { return s; }, swl)
|
|
|
|
: swl;
|
2018-01-24 09:37:32 -06:00
|
|
|
|
2018-06-12 07:36:08 -05:00
|
|
|
auto swu = swMax(G, init, tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
|
|
|
|
if ((swl.size() != swu.size()) ||
|
|
|
|
(swl.size() != G.numCells()))
|
|
|
|
{
|
|
|
|
throw std::invalid_argument {
|
|
|
|
"Missing or Mismatching Connate or Maximum Water "
|
|
|
|
"Saturation in Pcow EPS"
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2017-12-11 04:12:58 -06:00
|
|
|
return EPSPtr {
|
2018-11-12 17:36:51 -06:00
|
|
|
new EPS { std::move(swlpc), std::move(swu) }
|
2017-12-11 04:12:58 -06:00
|
|
|
};
|
2017-05-29 06:08:32 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
Create::TwoPoint::EPSPtr
|
|
|
|
Create::TwoPoint::
|
2018-06-12 07:36:08 -05:00
|
|
|
scalingFunction(const ::Opm::ECLGraph& G,
|
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const EPSOpt& opt,
|
|
|
|
const RTEP& tep)
|
2017-05-29 06:08:32 -05:00
|
|
|
{
|
|
|
|
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) {
|
2018-06-12 07:36:08 -05:00
|
|
|
return Create::TwoPoint::Kr::W(G, init, tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
}
|
|
|
|
|
2018-06-12 07:36:08 -05:00
|
|
|
return Create::TwoPoint::Kr::OW(G, init, tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2018-06-12 07:36:08 -05:00
|
|
|
return Create::TwoPoint::Kr::G(G, init, tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
}
|
|
|
|
|
2018-06-12 07:36:08 -05:00
|
|
|
return Create::TwoPoint::Kr::OG(G, init, tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2018-06-12 07:36:08 -05:00
|
|
|
return Create::TwoPoint::Pc::GO(G, init, tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (opt.thisPh == PhIdx::Aqua) {
|
2018-06-12 07:36:08 -05:00
|
|
|
return Create::TwoPoint::Pc::OW(G, init, tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Invalid.
|
|
|
|
return EPSPtr{};
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
// Implementation of Create::TwoPoint::unscaledEndPoints()
|
|
|
|
std::vector<Create::TEP>
|
|
|
|
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,
|
2018-06-12 07:36:08 -05:00
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const RTEP& tep)
|
2017-05-29 06:08:32 -05:00
|
|
|
{
|
2018-06-12 07:36:08 -05:00
|
|
|
auto sgcr = sgCrit(G, init, tep);
|
|
|
|
auto sgu = sgMax (G, init, tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
|
|
|
|
if ((sgcr.size() != sgu.size()) ||
|
|
|
|
(sgcr.size() != G.numCells()))
|
|
|
|
{
|
|
|
|
throw std::invalid_argument {
|
|
|
|
"Missing or Mismatching Gas End-Point "
|
|
|
|
"Specifications (SGCR and/or SGU)"
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2018-06-12 07:36:08 -05:00
|
|
|
auto sdisp = CritSatVertical::CritDispSat::
|
|
|
|
KrG::alternateMethod(G, init, tep, true);
|
2017-05-29 06:08:32 -05:00
|
|
|
|
|
|
|
return EPSPtr {
|
2017-12-11 04:12:58 -06:00
|
|
|
new EPS {
|
2018-06-12 07:36:08 -05:00
|
|
|
std::move(sgcr), std::move(sdisp), std::move(sgu)
|
2017-12-11 04:12:58 -06:00
|
|
|
}
|
2017-05-29 06:08:32 -05:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
Create::ThreePoint::EPSPtr
|
|
|
|
Create::ThreePoint::Kr::OG(const ::Opm::ECLGraph& G,
|
2018-06-12 07:36:08 -05:00
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const RTEP& tep)
|
2017-05-29 06:08:32 -05:00
|
|
|
{
|
2018-06-12 07:36:08 -05:00
|
|
|
auto sogcr = sogCrit(G, init, tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
|
|
|
|
if (sogcr.size() != G.numCells()) {
|
|
|
|
throw std::invalid_argument {
|
|
|
|
"Missing or Mismatching Critical Oil "
|
|
|
|
"Saturation in Oil/Gas System"
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2018-06-12 07:36:08 -05:00
|
|
|
auto sdisp = CritSatVertical::CritDispSat::
|
|
|
|
KrGO::alternateMethod(G, init, tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
|
2018-06-12 07:36:08 -05:00
|
|
|
auto smax = soMax(G, init, tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
|
|
|
|
return EPSPtr {
|
2017-12-11 04:12:58 -06:00
|
|
|
new EPS {
|
2018-05-07 05:55:46 -05:00
|
|
|
std::move(sogcr), std::move(sdisp), std::move(smax)
|
2017-12-11 04:12:58 -06:00
|
|
|
}
|
2017-05-29 06:08:32 -05:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
Create::ThreePoint::EPSPtr
|
|
|
|
Create::ThreePoint::Kr::OW(const ::Opm::ECLGraph& G,
|
2018-06-12 07:36:08 -05:00
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const RTEP& tep)
|
2017-05-29 06:08:32 -05:00
|
|
|
{
|
2018-06-12 07:36:08 -05:00
|
|
|
auto sowcr = sowCrit(G, init, tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
|
|
|
|
if (sowcr.size() != G.numCells()) {
|
|
|
|
throw std::invalid_argument {
|
|
|
|
"Missing or Mismatching Critical Oil "
|
|
|
|
"Saturation in Oil/Water System"
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2018-06-12 07:36:08 -05:00
|
|
|
auto sdisp = CritSatVertical::CritDispSat::
|
|
|
|
KrOW::alternateMethod(G, init, tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
|
2018-06-12 07:36:08 -05:00
|
|
|
auto smax = soMax(G, init, tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
|
|
|
|
return EPSPtr {
|
2017-12-11 04:12:58 -06:00
|
|
|
new EPS {
|
2018-05-07 05:55:46 -05:00
|
|
|
std::move(sowcr), std::move(sdisp), std::move(smax)
|
2017-12-11 04:12:58 -06:00
|
|
|
}
|
2017-05-29 06:08:32 -05:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
Create::ThreePoint::EPSPtr
|
|
|
|
Create::ThreePoint::Kr::W(const ::Opm::ECLGraph& G,
|
2018-06-12 07:36:08 -05:00
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const RTEP& tep)
|
2017-05-29 06:08:32 -05:00
|
|
|
{
|
2018-06-12 07:36:08 -05:00
|
|
|
auto swcr = swCrit(G, init, tep);
|
|
|
|
auto swu = swMax (G, init, tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
|
|
|
|
if ((swcr.size() != G.numCells()) ||
|
|
|
|
(swcr.size() != swu.size()))
|
|
|
|
{
|
|
|
|
throw std::invalid_argument {
|
|
|
|
"Missing Water End-Point Specifications (SWCR and/or SWU)"
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2018-06-12 07:36:08 -05:00
|
|
|
auto sdisp = CritSatVertical::CritDispSat::
|
|
|
|
KrW::alternateMethod(G, init, tep, true);
|
2017-05-29 06:08:32 -05:00
|
|
|
|
|
|
|
return EPSPtr {
|
2017-12-11 04:12:58 -06:00
|
|
|
new EPS {
|
2018-05-07 05:55:46 -05:00
|
|
|
std::move(swcr), std::move(sdisp), std::move(swu)
|
2017-12-11 04:12:58 -06:00
|
|
|
}
|
2017-05-29 06:08:32 -05:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
Create::ThreePoint::EPSPtr
|
|
|
|
Create::ThreePoint::
|
2018-06-12 07:36:08 -05:00
|
|
|
scalingFunction(const ::Opm::ECLGraph& G,
|
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const EPSOpt& opt,
|
|
|
|
const RTEP& tep)
|
2017-05-29 06:08:32 -05:00
|
|
|
{
|
2017-11-22 08:28:38 -06:00
|
|
|
#if !defined(NDEBUG)
|
2017-05-29 06:08:32 -05:00
|
|
|
using FCat = ::Opm::SatFunc::CreateEPS::FunctionCategory;
|
2017-11-22 08:28:38 -06:00
|
|
|
#endif // !defined(NDEBUG)
|
|
|
|
|
2017-05-29 06:08:32 -05:00
|
|
|
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) {
|
2018-06-12 07:36:08 -05:00
|
|
|
return Create::ThreePoint::Kr::W(G, init, tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
}
|
|
|
|
|
2018-06-12 07:36:08 -05:00
|
|
|
return Create::ThreePoint::Kr::OW(G, init, tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2018-06-12 07:36:08 -05:00
|
|
|
return Create::ThreePoint::Kr::G(G, init, tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
}
|
|
|
|
|
2018-06-12 07:36:08 -05:00
|
|
|
return Create::ThreePoint::Kr::OG(G, init, tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Invalid.
|
|
|
|
return EPSPtr{};
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
// Implementation of Create::ThreePoint::unscaledEndPoints()
|
|
|
|
std::vector<Create::TEP>
|
|
|
|
Create::ThreePoint::unscaledEndPoints(const RTEP& ep, const EPSOpt& opt)
|
|
|
|
{
|
2017-11-22 08:28:38 -06:00
|
|
|
#if !defined(NDEBUG)
|
2017-05-29 06:08:32 -05:00
|
|
|
using FCat = ::Opm::SatFunc::CreateEPS::FunctionCategory;
|
2017-11-22 08:28:38 -06:00
|
|
|
#endif // !defined(NDEBUG)
|
|
|
|
|
2017-05-29 06:08:32 -05:00
|
|
|
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<double>& s1,
|
|
|
|
const std::vector<double>& s2)
|
|
|
|
-> std::vector<double>
|
|
|
|
{
|
|
|
|
auto sr = std::vector<double>(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 {};
|
|
|
|
}
|
|
|
|
|
2018-05-07 05:55:46 -05:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
// Implementation of Create::PureVertical::scalingFunction()
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
Create::PureVertical::ScalPtr
|
|
|
|
pureVerticalRelpermScaling(const ::Opm::ECLGraph& G,
|
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const std::vector<double>& 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<double>& dflt,
|
|
|
|
const std::string& vector)
|
|
|
|
{
|
|
|
|
const auto& ih = init.keywordData<int>(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<double>& 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<double>& 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<double>& 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<double>& 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<double>& 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<double>(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()
|
|
|
|
|
2018-06-12 07:36:08 -05:00
|
|
|
std::vector<double>
|
|
|
|
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<double>(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<double>
|
|
|
|
Create::CritSatVertical::CritDispSat::
|
|
|
|
KrG::alternateMethod(const ::Opm::ECLGraph& G,
|
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const RTEP& tep,
|
|
|
|
const bool activeOil)
|
|
|
|
{
|
|
|
|
auto sdisp = std::vector<double>(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<double>
|
|
|
|
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<double>(sogcr.size(), 0.0);
|
2018-05-07 05:55:46 -05:00
|
|
|
{
|
2018-06-12 07:36:08 -05:00
|
|
|
const auto& sgco = tep.conn.gas;
|
2018-10-23 08:58:30 -05:00
|
|
|
const auto& swco = tep.conn.water;
|
2018-06-12 07:36:08 -05:00
|
|
|
const auto& sgcr = tep.crit.gas;
|
2018-05-07 05:55:46 -05:00
|
|
|
|
2018-06-12 07:36:08 -05:00
|
|
|
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
|
2018-05-07 05:55:46 -05:00
|
|
|
|
2018-06-12 07:36:08 -05:00
|
|
|
t[i] = (sr - sogcr[i]) / (smax - sogcr[i]);
|
|
|
|
}
|
2018-05-07 05:55:46 -05:00
|
|
|
}
|
2018-06-12 07:36:08 -05:00
|
|
|
|
|
|
|
return transformedCritSat(G, init, t,
|
|
|
|
sogCrit(G, init, tep),
|
|
|
|
soMax (G, init, tep));
|
2018-05-07 05:55:46 -05:00
|
|
|
}
|
|
|
|
|
2018-06-12 07:36:08 -05:00
|
|
|
std::vector<double>
|
|
|
|
Create::CritSatVertical::CritDispSat::
|
|
|
|
KrGO::alternateMethod(const ::Opm::ECLGraph& G,
|
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const RTEP& tep)
|
2018-05-07 05:55:46 -05:00
|
|
|
{
|
2018-06-12 07:36:08 -05:00
|
|
|
auto sdisp = std::vector<double>(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<double>
|
|
|
|
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<double>(sowcr.size(), 0.0);
|
|
|
|
{
|
|
|
|
const auto& sgco = tep.conn.gas;
|
2018-10-23 08:58:30 -05:00
|
|
|
const auto& swco = tep.conn.water;
|
2018-06-12 07:36:08 -05:00
|
|
|
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<double>
|
|
|
|
Create::CritSatVertical::CritDispSat::
|
|
|
|
KrOW::alternateMethod(const ::Opm::ECLGraph& G,
|
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const RTEP& tep)
|
|
|
|
{
|
|
|
|
auto sdisp = std::vector<double>(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);
|
|
|
|
});
|
2018-05-07 05:55:46 -05:00
|
|
|
|
2018-06-12 07:36:08 -05:00
|
|
|
return sdisp;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<double>
|
|
|
|
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<double>(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]);
|
|
|
|
}
|
|
|
|
}
|
2018-05-07 05:55:46 -05:00
|
|
|
|
2018-06-12 07:36:08 -05:00
|
|
|
return transformedCritSat(G, init, t,
|
|
|
|
swCrit(G, init, tep),
|
|
|
|
swMax (G, init, tep));
|
|
|
|
}
|
2018-05-07 05:55:46 -05:00
|
|
|
|
2018-06-12 07:36:08 -05:00
|
|
|
std::vector<double>
|
|
|
|
Create::CritSatVertical::CritDispSat::
|
|
|
|
KrW::alternateMethod(const ::Opm::ECLGraph& G,
|
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const RTEP& tep,
|
|
|
|
const bool activeOil)
|
|
|
|
{
|
|
|
|
auto sdisp = std::vector<double>(G.numCells(), 0.0);
|
2018-05-07 05:55:46 -05:00
|
|
|
|
2018-06-12 07:36:08 -05:00
|
|
|
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);
|
2018-05-07 05:55:46 -05:00
|
|
|
|
2018-06-12 07:36:08 -05:00
|
|
|
std::transform(std::begin(sowcr), std::end(sowcr), std::begin(sgl),
|
|
|
|
std::begin(sdisp),
|
|
|
|
[](const double so, const double sg) -> double
|
2018-05-07 05:55:46 -05:00
|
|
|
{
|
2018-06-12 07:36:08 -05:00
|
|
|
return 1.0 - (so + sg);
|
2018-05-07 05:55:46 -05:00
|
|
|
});
|
|
|
|
}
|
2018-06-12 07:36:08 -05:00
|
|
|
else {
|
|
|
|
// G/W system.
|
|
|
|
const auto sgcr = sgCrit(G, init, tep);
|
2018-05-07 05:55:46 -05:00
|
|
|
|
2018-06-12 07:36:08 -05:00
|
|
|
std::transform(std::begin(sgcr), std::end(sgcr),
|
2018-05-07 05:55:46 -05:00
|
|
|
std::begin(sdisp),
|
2018-06-12 07:36:08 -05:00
|
|
|
[](const double sg) -> double
|
2018-05-07 05:55:46 -05:00
|
|
|
{
|
2018-06-12 07:36:08 -05:00
|
|
|
return 1.0 - sg;
|
2018-05-07 05:55:46 -05:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-06-12 07:36:08 -05:00
|
|
|
return sdisp;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<double>
|
|
|
|
Create::CritSatVertical::CritDispSat::
|
|
|
|
transformedCritSat(const ::Opm::ECLGraph& G,
|
|
|
|
const ::Opm::ECLInitFileData& init,
|
|
|
|
const std::vector<double>& t,
|
|
|
|
const std::vector<double>& left,
|
|
|
|
const std::vector<double>& right)
|
|
|
|
{
|
|
|
|
auto sdisp = std::vector<double>(G.numCells(), 0.0);
|
|
|
|
|
|
|
|
auto cellID = std::vector<double>::size_type{0};
|
|
|
|
for (const auto& gridID : G.activeGrids()) {
|
|
|
|
const auto nc = G.numCells(gridID);
|
|
|
|
|
|
|
|
const auto& snum = init.haveKeywordData("SATNUM", gridID)
|
|
|
|
? G.rawLinearisedCellData<int>(init, "SATNUM", gridID)
|
|
|
|
: std::vector<int>(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<double>
|
|
|
|
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<int>(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<double>
|
|
|
|
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<double>&& sdisp,
|
|
|
|
std::vector<double>&& smax,
|
|
|
|
const FValVec& fval)
|
|
|
|
{
|
|
|
|
using FVal = ::Opm::SatFunc::VerticalScalingInterface::FunctionValues;
|
|
|
|
|
2018-05-07 05:55:46 -05:00
|
|
|
auto dflt_fdisp = std::vector<double>(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<double>(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 {
|
2018-06-12 07:36:08 -05:00
|
|
|
std::move(sdisp), std::move(fdisp),
|
|
|
|
std::move(smax) , std::move(fmax)
|
2018-05-07 05:55:46 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
Create::CritSatVertical::ScalPtr
|
|
|
|
Create::CritSatVertical::Kr::GO(const ::Opm::ECLGraph& G,
|
|
|
|
const ::Opm::ECLInitFileData& init,
|
2018-06-12 07:36:08 -05:00
|
|
|
std::vector<double>&& sdisp,
|
|
|
|
std::vector<double>&& smax,
|
2018-05-07 05:55:46 -05:00
|
|
|
const FValVec& fval)
|
|
|
|
{
|
|
|
|
using FVal = ::Opm::SatFunc::VerticalScalingInterface::FunctionValues;
|
|
|
|
|
|
|
|
auto dflt_fdisp = std::vector<double>(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<double>(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 {
|
2018-06-12 07:36:08 -05:00
|
|
|
std::move(sdisp), std::move(fdisp),
|
|
|
|
std::move(smax) , std::move(fmax)
|
2018-05-07 05:55:46 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
Create::CritSatVertical::ScalPtr
|
|
|
|
Create::CritSatVertical::Kr::OW(const ::Opm::ECLGraph& G,
|
|
|
|
const ::Opm::ECLInitFileData& init,
|
2018-06-12 07:36:08 -05:00
|
|
|
std::vector<double>&& sdisp,
|
|
|
|
std::vector<double>&& smax,
|
2018-05-07 05:55:46 -05:00
|
|
|
const FValVec& fval)
|
|
|
|
{
|
|
|
|
using FVal = ::Opm::SatFunc::VerticalScalingInterface::FunctionValues;
|
|
|
|
|
|
|
|
auto dflt_fdisp = std::vector<double>(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<double>(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 {
|
2018-06-12 07:36:08 -05:00
|
|
|
std::move(sdisp), std::move(fdisp),
|
|
|
|
std::move(smax) , std::move(fmax)
|
2018-05-07 05:55:46 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
Create::CritSatVertical::ScalPtr
|
|
|
|
Create::CritSatVertical::Kr::W(const ::Opm::ECLGraph& G,
|
|
|
|
const ::Opm::ECLInitFileData& init,
|
2018-06-12 07:36:08 -05:00
|
|
|
std::vector<double>&& sdisp,
|
|
|
|
std::vector<double>&& smax,
|
2018-05-07 05:55:46 -05:00
|
|
|
const FValVec& fval)
|
|
|
|
{
|
|
|
|
using FVal = ::Opm::SatFunc::VerticalScalingInterface::FunctionValues;
|
|
|
|
|
|
|
|
auto dflt_fdisp = std::vector<double>(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<double>(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 {
|
2018-06-12 07:36:08 -05:00
|
|
|
std::move(sdisp), std::move(fdisp),
|
|
|
|
std::move(smax) , std::move(fmax)
|
2018-05-07 05:55:46 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2018-06-12 07:36:08 -05:00
|
|
|
auto scr = critDispSat(G, init, opt, tep);
|
|
|
|
auto smax = maximumSat (G, init, opt, tep);
|
|
|
|
|
2018-05-07 05:55:46 -05:00
|
|
|
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) {
|
2018-06-12 07:36:08 -05:00
|
|
|
return Create::CritSatVertical::
|
|
|
|
Kr::W(G, init, std::move(scr), std::move(smax), fvals);
|
2018-05-07 05:55:46 -05:00
|
|
|
}
|
|
|
|
|
2018-06-12 07:36:08 -05:00
|
|
|
return Create::CritSatVertical::
|
|
|
|
Kr::OW(G, init, std::move(scr), std::move(smax), fvals);
|
2018-05-07 05:55:46 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2018-06-12 07:36:08 -05:00
|
|
|
return Create::CritSatVertical::
|
|
|
|
Kr::G(G, init, std::move(scr), std::move(smax), fvals);
|
2018-05-07 05:55:46 -05:00
|
|
|
}
|
|
|
|
|
2018-06-12 07:36:08 -05:00
|
|
|
return Create::CritSatVertical::
|
|
|
|
Kr::GO(G, init, std::move(scr), std::move(smax), fvals);
|
2018-05-07 05:55:46 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Invalid.
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2017-05-29 06:08:32 -05:00
|
|
|
// #####################################################################
|
|
|
|
// =====================================================================
|
|
|
|
// Public Interface Below Separator
|
|
|
|
// =====================================================================
|
|
|
|
// #####################################################################
|
|
|
|
|
|
|
|
// Class Opm::SatFunc::EPSEvalInterface
|
|
|
|
Opm::SatFunc::EPSEvalInterface::~EPSEvalInterface()
|
|
|
|
{}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
|
2018-05-07 05:55:46 -05:00
|
|
|
// Class Opm::SatFunc::VerticalScalingInterface
|
|
|
|
Opm::SatFunc::VerticalScalingInterface::~VerticalScalingInterface()
|
|
|
|
{}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
|
2017-05-29 06:08:32 -05:00
|
|
|
// Class Opm::SatFunc::TwoPointScaling
|
|
|
|
Opm::SatFunc::TwoPointScaling::
|
2018-05-07 05:55:46 -05:00
|
|
|
TwoPointScaling(std::vector<double> smin,
|
|
|
|
std::vector<double> smax)
|
|
|
|
: pImpl_(new Impl(std::move(smin), std::move(smax)))
|
2017-05-29 06:08:32 -05:00
|
|
|
{}
|
|
|
|
|
|
|
|
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<double>
|
|
|
|
Opm::SatFunc::TwoPointScaling::eval(const TableEndPoints& tep,
|
|
|
|
const SaturationPoints& sp) const
|
|
|
|
{
|
|
|
|
return this->pImpl_->eval(tep, sp);
|
|
|
|
}
|
|
|
|
|
2017-10-11 11:02:00 -05:00
|
|
|
std::vector<double>
|
|
|
|
Opm::SatFunc::TwoPointScaling::reverse(const TableEndPoints& tep,
|
|
|
|
const SaturationPoints& sp) const
|
|
|
|
{
|
|
|
|
return this->pImpl_->reverse(tep, sp);
|
|
|
|
}
|
|
|
|
|
2017-05-29 06:08:32 -05:00
|
|
|
std::unique_ptr<Opm::SatFunc::EPSEvalInterface>
|
|
|
|
Opm::SatFunc::TwoPointScaling::clone() const
|
|
|
|
{
|
|
|
|
return std::unique_ptr<TwoPointScaling>(new TwoPointScaling(*this));
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
|
2018-05-07 05:55:46 -05:00
|
|
|
// Class Opm::SatFunc::PureVerticalScaling
|
|
|
|
|
|
|
|
Opm::SatFunc::PureVerticalScaling::
|
|
|
|
PureVerticalScaling(std::vector<double> 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<double>
|
|
|
|
Opm::SatFunc::PureVerticalScaling::
|
|
|
|
vertScale(const FunctionValues& f,
|
|
|
|
const SaturationPoints& sp,
|
|
|
|
const std::vector<double>& val) const
|
|
|
|
{
|
|
|
|
return this->pImpl_->vertScale(f, sp, val);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<Opm::SatFunc::VerticalScalingInterface>
|
|
|
|
Opm::SatFunc::PureVerticalScaling::clone() const
|
|
|
|
{
|
|
|
|
return std::unique_ptr<PureVerticalScaling>(new PureVerticalScaling(*this));
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
|
2017-05-29 06:08:32 -05:00
|
|
|
// Class Opm::SatFunc::ThreePointScaling
|
|
|
|
Opm::SatFunc::ThreePointScaling::
|
2018-05-07 05:55:46 -05:00
|
|
|
ThreePointScaling(std::vector<double> smin,
|
|
|
|
std::vector<double> sdisp,
|
|
|
|
std::vector<double> smax)
|
|
|
|
: pImpl_(new Impl(std::move(smin), std::move(sdisp), std::move(smax)))
|
2017-05-29 06:08:32 -05:00
|
|
|
{}
|
|
|
|
|
|
|
|
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<double>
|
|
|
|
Opm::SatFunc::ThreePointScaling::eval(const TableEndPoints& tep,
|
|
|
|
const SaturationPoints& sp) const
|
|
|
|
{
|
|
|
|
return this->pImpl_->eval(tep, sp);
|
|
|
|
}
|
|
|
|
|
2017-10-11 11:02:00 -05:00
|
|
|
std::vector<double>
|
|
|
|
Opm::SatFunc::ThreePointScaling::reverse(const TableEndPoints& tep,
|
|
|
|
const SaturationPoints& sp) const
|
|
|
|
{
|
|
|
|
return this->pImpl_->reverse(tep, sp);
|
|
|
|
}
|
|
|
|
|
2017-05-29 06:08:32 -05:00
|
|
|
std::unique_ptr<Opm::SatFunc::EPSEvalInterface>
|
|
|
|
Opm::SatFunc::ThreePointScaling::clone() const
|
|
|
|
{
|
|
|
|
return std::unique_ptr<ThreePointScaling>(new ThreePointScaling(*this));
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------
|
2018-05-07 05:55:46 -05:00
|
|
|
|
|
|
|
// Class Opm::SatFunc::CritSatVerticalScaling
|
|
|
|
Opm::SatFunc::CritSatVerticalScaling::
|
|
|
|
CritSatVerticalScaling(std::vector<double> sdisp,
|
|
|
|
std::vector<double> fdisp,
|
2018-06-12 07:36:08 -05:00
|
|
|
std::vector<double> smax,
|
2018-05-07 05:55:46 -05:00
|
|
|
std::vector<double> fmax)
|
2018-06-12 07:36:08 -05:00
|
|
|
: pImpl_(new Impl(std::move(sdisp), std::move(fdisp),
|
|
|
|
std::move(smax) , std::move(fmax)))
|
2018-05-07 05:55:46 -05:00
|
|
|
{}
|
|
|
|
|
|
|
|
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<double>
|
|
|
|
Opm::SatFunc::CritSatVerticalScaling::
|
|
|
|
vertScale(const FunctionValues& f,
|
|
|
|
const SaturationPoints& sp,
|
|
|
|
const std::vector<double>& val) const
|
|
|
|
{
|
|
|
|
return this->pImpl_->vertScale(f, sp, val);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<Opm::SatFunc::VerticalScalingInterface>
|
|
|
|
Opm::SatFunc::CritSatVerticalScaling::clone() const
|
|
|
|
{
|
|
|
|
return std::unique_ptr<CritSatVerticalScaling> {
|
|
|
|
new CritSatVerticalScaling(*this)
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
// Factory function Opm::SatFunc::CreateEPS::Horizontal::fromECLOutput()
|
2017-05-29 06:08:32 -05:00
|
|
|
|
|
|
|
std::unique_ptr<Opm::SatFunc::EPSEvalInterface>
|
2018-05-07 05:55:46 -05:00
|
|
|
Opm::SatFunc::CreateEPS::Horizontal::
|
2018-06-12 07:36:08 -05:00
|
|
|
fromECLOutput(const ECLGraph& G,
|
|
|
|
const ECLInitFileData& init,
|
|
|
|
const EPSOptions& opt,
|
|
|
|
const RawTableEndPoints& tep)
|
2017-05-29 06:08:32 -05:00
|
|
|
{
|
|
|
|
if ((opt.curve == FunctionCategory::CapPress) ||
|
|
|
|
(! opt.use3PtScaling))
|
|
|
|
{
|
2018-06-12 07:36:08 -05:00
|
|
|
return Create::TwoPoint::scalingFunction(G, init, opt, tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((opt.curve == FunctionCategory::Relperm) && opt.use3PtScaling)
|
|
|
|
{
|
2018-06-12 07:36:08 -05:00
|
|
|
return Create::ThreePoint::scalingFunction(G, init, opt, tep);
|
2017-05-29 06:08:32 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Invalid
|
|
|
|
return std::unique_ptr<Opm::SatFunc::EPSEvalInterface>{};
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------
|
2018-05-07 05:55:46 -05:00
|
|
|
// Factory function Opm::SatFunc::CreateEPS::Horizontal::unscaledEndPoints()
|
2017-05-29 06:08:32 -05:00
|
|
|
|
|
|
|
std::vector<Opm::SatFunc::EPSEvalInterface::TableEndPoints>
|
2018-05-07 05:55:46 -05:00
|
|
|
Opm::SatFunc::CreateEPS::Horizontal::
|
2018-06-12 07:36:08 -05:00
|
|
|
unscaledEndPoints(const EPSOptions& opt,
|
|
|
|
const RawTableEndPoints& ep)
|
2017-05-29 06:08:32 -05:00
|
|
|
{
|
|
|
|
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 {};
|
|
|
|
}
|
2018-05-07 05:55:46 -05:00
|
|
|
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
// Factory function Opm::SatFunc::CreateEPS::Vertical::fromECLOutput()
|
|
|
|
|
|
|
|
std::unique_ptr<Opm::SatFunc::VerticalScalingInterface>
|
|
|
|
Opm::SatFunc::CreateEPS::Vertical::
|
|
|
|
fromECLOutput(const ECLGraph& G,
|
|
|
|
const ECLInitFileData& init,
|
|
|
|
const EPSOptions& opt,
|
|
|
|
const RawTableEndPoints& tep,
|
|
|
|
const FuncValVector& fvals)
|
|
|
|
{
|
2018-06-12 07:36:08 -05:00
|
|
|
const auto haveScaleCRS = haveScaledRelPermAtCritSat(G, init, opt);
|
2018-05-07 05:55:46 -05:00
|
|
|
|
|
|
|
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::VerticalScalingInterface::FunctionValues>
|
|
|
|
Opm::SatFunc::CreateEPS::Vertical::
|
|
|
|
unscaledFunctionValues(const ECLGraph& G,
|
|
|
|
const ECLInitFileData& init,
|
|
|
|
const RawTableEndPoints& ep,
|
|
|
|
const EPSOptions& opt,
|
|
|
|
const SatFuncEvaluator& evalSF)
|
|
|
|
{
|
|
|
|
auto ret = std::vector<VerticalScalingInterface::FunctionValues>{};
|
|
|
|
|
2018-06-12 07:36:08 -05:00
|
|
|
const auto haveScaleCRS = haveScaledRelPermAtCritSat(G, init, opt);
|
2018-05-07 05:55:46 -05:00
|
|
|
|
|
|
|
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) {
|
2018-11-12 17:46:15 -06:00
|
|
|
ret[i].disp.sat = uep[i].disp;
|
|
|
|
ret[i].disp.val = evalSF(static_cast<int>(i), ret[i].disp.sat);
|
|
|
|
|
2018-05-07 05:55:46 -05:00
|
|
|
ret[i].max.sat = uep[i].high;
|
|
|
|
ret[i].max.val = evalSF(static_cast<int>(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<int>(i), ret[i].disp.sat);
|
|
|
|
|
|
|
|
ret[i].max.sat = uep[i].high;
|
|
|
|
ret[i].max.val = evalSF(static_cast<int>(i), ret[i].max.sat);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2018-06-12 07:36:08 -05:00
|
|
|
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
// Factory functions Opm::SatFunc::scaledConnate*()
|
|
|
|
|
|
|
|
std::vector<double>
|
|
|
|
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<double>
|
|
|
|
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; });
|
|
|
|
}
|