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