Support for SGWFN sat functions (format type 2)

This commit is contained in:
David Landa Marban 2023-10-10 20:37:29 +02:00 committed by Markus Blatt
parent c419f8951d
commit 9696d58498
6 changed files with 430 additions and 56 deletions

View File

@ -343,8 +343,8 @@ public:
enum class KeywordFamily {
Family_I, // SGOF, SWOF, SLGOF
Family_II, // SGFN, SOF{2,3}, SWFN
Family_III, // GSF, WSF
Family_II, // SGFN, SOF{2,3}, SWFN, SGWFN
Family_III, // GSF, WSF
Undefined,
};

View File

@ -23,6 +23,7 @@
#include <opm/input/eclipse/EclipseState/Tables/SgfnTable.hpp>
#include <opm/input/eclipse/EclipseState/Tables/SgofTable.hpp>
#include <opm/input/eclipse/EclipseState/Tables/SlgofTable.hpp>
#include <opm/input/eclipse/EclipseState/Tables/SgwfnTable.hpp>
#include <opm/input/eclipse/EclipseState/Tables/Sof2Table.hpp>
#include <opm/input/eclipse/EclipseState/Tables/Sof3Table.hpp>
#include <opm/input/eclipse/EclipseState/Tables/SwfnTable.hpp>
@ -70,8 +71,8 @@ namespace {
* Description; there are several alternative families of keywords which
* can be used to enter relperm and capillary pressure tables.
*
* If SWOF and SGOF are specified in the deck it return I
* If SWFN, SGFN and SOF3 are specified in the deck it return II
* If SWOF and SGOF are specified in the deck it returns I
* If SGWFN, SWFN, SGFN and SOF3 are specified in the deck it returns II
* If keywords are missing or mixed, an error is given.
*/
enum class SatfuncFamily { none = 0, I = 1, II = 2, III = 3 };
@ -97,8 +98,8 @@ namespace {
(wat && (tm.hasTables("SWOF") || !tm.getSwofletTable().empty()));
// note: we allow for SOF2 to be part of family1 for threeP + solvent simulations.
const auto family2 = // SGFN, SOF{2,3}, SWFN
(gas && tm.hasTables("SGFN")) ||
const auto family2 = // SGFN, SOF{2,3}, SWFN, SGWFN
(gas && (tm.hasTables("SGFN") || tm.hasTables("SGWFN"))) ||
(oil && ((threeP && tm.hasTables("SOF3")) ||
(twoP && tm.hasTables("SOF2")))) ||
(wat && tm.hasTables("SWFN"));
@ -132,7 +133,7 @@ namespace {
throw std::invalid_argument {
"Saturation functions must be specified using "
"either family 1 or family 2 keywords\n"
"Use either SGOF (or SLGOF) and/or SWOF or SGFN/SWFN and SOF2/SOF3"
"Use either SGOF (or SLGOF) and/or SWOF or SGFN/SWFN or SGWFN and SOF2/SOF3"
};
}
@ -148,6 +149,7 @@ namespace {
const auto& swofTables = tm.getSwofTables();
const auto& swofLetTables = tm.getSwofletTable();
const auto& swfnTables = tm.getSwfnTables();
const auto& sgwfnTables = tm.getSgwfnTables();
const auto& wsfTables = tm.getWsfTables();
const auto famI = [&swofTables]( int i ) {
@ -161,6 +163,9 @@ namespace {
const auto famII = [&swfnTables]( int i ) {
return swfnTables.getTable<Opm::SwfnTable>( i ).getSwColumn().front();
};
const auto famII_sgwfn = [&sgwfnTables]( int i ) {
return 1.0 - sgwfnTables.getTable<Opm::SgwfnTable>( i ).getSgColumn().back();
};
const auto famIII = [&wsfTables]( int i ) {
return wsfTables.getTable<Opm::WsfTable>( i ).getSwColumn().front();
};
@ -173,7 +178,11 @@ namespace {
else
throw std::domain_error("Either SWOF or SWOFLET tables must be provided");
case SatfuncFamily::II: return map( famII, Opm::fun::iota( num_tables ) );
case SatfuncFamily::II:
if( !swfnTables.empty() )
return map( famII, Opm::fun::iota( num_tables ) );
else
return map( famII_sgwfn, Opm::fun::iota( num_tables ) );
case SatfuncFamily::III: return map( famIII, Opm::fun::iota( num_tables ) );
default:
throw std::domain_error("No valid saturation keyword family specified");
@ -192,6 +201,7 @@ namespace {
const auto& swofTables = tm.getSwofTables();
const auto& swofLetTables = tm.getSwofletTable();
const auto& swfnTables = tm.getSwfnTables();
const auto& sgwfnTables = tm.getSgwfnTables();
const auto& wsfTables = tm.getWsfTables();
const auto famI = [&swofTables]( int i ) {
@ -205,6 +215,9 @@ namespace {
const auto famII = [&swfnTables]( int i ) {
return swfnTables.getTable<Opm::SwfnTable>( i ).getSwColumn().back();
};
const auto famII_sgwfn = [&sgwfnTables]( int i ) {
return 1.0 - sgwfnTables.getTable<Opm::SgwfnTable>( i ).getSgColumn().front();
};
const auto famIII = [&wsfTables]( int i ) {
return wsfTables.getTable<Opm::WsfTable>( i ).getSwColumn().back();
};
@ -217,7 +230,11 @@ namespace {
else
throw std::domain_error("Either SWOF or SWOFLET tables must be provided");
case SatfuncFamily::II: return map( famII, Opm::fun::iota( num_tables ) );
case SatfuncFamily::II:
if( !swfnTables.empty() )
return map( famII, Opm::fun::iota( num_tables ) );
else
return map( famII_sgwfn, Opm::fun::iota( num_tables ) );
case SatfuncFamily::III: return map( famIII, Opm::fun::iota( num_tables ) );
default:
@ -238,6 +255,7 @@ namespace {
const auto& sgofLetTables = tm.getSgofletTable();
const auto& slgofTables = tm.getSlgofTables();
const auto& sgfnTables = tm.getSgfnTables();
const auto& sgwfnTables = tm.getSgwfnTables();
const auto& gsfTables = tm.getGsfTables();
const auto famI_sgof = [&sgofTables]( int i ) {
@ -255,6 +273,9 @@ namespace {
const auto famII = [&sgfnTables]( int i ) {
return sgfnTables.getTable<Opm::SgfnTable>( i ).getSgColumn().front();
};
const auto famII_sgwfn = [&sgwfnTables]( int i ) {
return sgwfnTables.getTable<Opm::SgwfnTable>( i ).getSgColumn().front();
};
const auto famIII = [&gsfTables]( int i ) {
return gsfTables.getTable<Opm::GsfTable>( i ).getSgColumn().front();
};
@ -271,7 +292,10 @@ namespace {
return Opm::fun::map( famI_slgof, Opm::fun::iota( num_tables ) );
case SatfuncFamily::II:
return Opm::fun::map( famII, Opm::fun::iota( num_tables ) );
if( !sgfnTables.empty() )
return Opm::fun::map( famII, Opm::fun::iota( num_tables ) );
else
return Opm::fun::map( famII_sgwfn, Opm::fun::iota( num_tables ) );
case SatfuncFamily::III:
return Opm::fun::map( famIII, Opm::fun::iota( num_tables ) );
@ -294,6 +318,7 @@ namespace {
const auto& swofLetTables = tm.getSwofletTable();
const auto& slgofTables = tm.getSlgofTables();
const auto& sgfnTables = tm.getSgfnTables();
const auto& sgwfnTables = tm.getSgwfnTables();
const auto& gsfTables = tm.getGsfTables();
const auto famI_sgof = [&sgofTables]( int i ) {
@ -312,6 +337,9 @@ namespace {
const auto famII = [&sgfnTables]( int i ) {
return sgfnTables.getTable<Opm::SgfnTable>( i ).getSgColumn().back();
};
const auto famII_sgwfn = [&sgwfnTables]( int i ) {
return sgwfnTables.getTable<Opm::SgwfnTable>( i ).getSgColumn().back();
};
const auto famIII = [&gsfTables]( int i ) {
return gsfTables.getTable<Opm::GsfTable>( i ).getSgColumn().back();
};
@ -329,7 +357,10 @@ namespace {
return Opm::fun::map( famI_slgof, Opm::fun::iota( num_tables ) );
case SatfuncFamily::II:
return Opm::fun::map( famII, Opm::fun::iota( num_tables ) );
if( !sgfnTables.empty() )
return Opm::fun::map( famII, Opm::fun::iota( num_tables ) );
else
return Opm::fun::map( famII_sgwfn, Opm::fun::iota( num_tables ) );
case SatfuncFamily::III:
return Opm::fun::map( famIII, Opm::fun::iota( num_tables ) );
@ -395,6 +426,21 @@ namespace {
table.getKrwColumn(), tolcrit);
}
/// Maximum water saturation for which Krgw(1-Sw) <= tolcrit.
///
/// Expected Table Format:
/// [1-Sw, Krgw(1-Sw), ...other...]
///
/// Krgw decreasing.
double critical_water_sgwfn(const Opm::SgwfnTable& sgwfnTable, const double tolcrit)
{
const auto sg_at_crit_krgw =
crit_sat_decreasing_KR(sgwfnTable.getSgColumn(),
sgwfnTable.getKrgwColumn(), tolcrit);
// Sw = 1 - Sg
return 1.0 - sg_at_crit_krgw;
}
std::vector<double>
findCriticalWater(const Opm::TableManager& tm,
const Opm::Phases& ph,
@ -408,6 +454,7 @@ namespace {
const auto& swofTables = tm.getSwofTables();
const auto& swofLetTables = tm.getSwofletTable();
const auto& swfnTables = tm.getSwfnTables();
const auto& sgwfnTables = tm.getSgwfnTables();
const auto& wsfTables = tm.getWsfTables();
const auto famI = [&swofTables, tolcrit](const int i) -> double
@ -423,6 +470,10 @@ namespace {
{
return critical_water(swfnTables.getTable<Opm::SwfnTable>(i), tolcrit);
};
const auto famII_sgwfn = [&sgwfnTables, tolcrit](const int i) -> double
{
return critical_water_sgwfn(sgwfnTables.getTable<Opm::SgwfnTable>(i), tolcrit);
};
const auto famIII = [&wsfTables, tolcrit](const int i) -> double
{
return critical_water(wsfTables.getTable<Opm::WsfTable>(i), tolcrit);
@ -436,7 +487,11 @@ namespace {
else
throw std::domain_error("Either SWOF or SWOFLET tables must be provided");
case SatfuncFamily::II: return Opm::fun::map( famII, Opm::fun::iota( num_tables ) );
case SatfuncFamily::II:
if( !swfnTables.empty() )
return Opm::fun::map( famII, Opm::fun::iota( num_tables ) );
else
return Opm::fun::map( famII_sgwfn, Opm::fun::iota( num_tables ) );
case SatfuncFamily::III: return Opm::fun::map( famIII, Opm::fun::iota( num_tables ) );
default: throw std::domain_error("No valid saturation keyword family specified");
@ -484,6 +539,7 @@ namespace {
return std::vector<double>(num_tables, 0.0);
const auto& sgfnTables = tm.getSgfnTables();
const auto& sgwfnTables = tm.getSgwfnTables();
const auto& sgofTables = tm.getSgofTables();
const auto& sgofLetTables = tm.getSgofletTable();
const auto& slgofTables = tm.getSlgofTables();
@ -508,6 +564,11 @@ namespace {
return critical_gas(sgfnTables.getTable<Opm::SgfnTable>(i), tolcrit);
};
const auto famII_sgwfn = [&sgwfnTables, tolcrit](const int i) -> double
{
return critical_gas(sgwfnTables.getTable<Opm::SgwfnTable>(i), tolcrit);
};
const auto famIII = [&gsfTables, tolcrit](const int i) -> double
{
return critical_gas(gsfTables.getTable<Opm::GsfTable>(i), tolcrit);
@ -526,7 +587,10 @@ namespace {
return Opm::fun::map( famI_slgof, Opm::fun::iota( num_tables ) );
case SatfuncFamily::II:
return Opm::fun::map( famII, Opm::fun::iota( num_tables ) );
if( !sgfnTables.empty() )
return Opm::fun::map( famII, Opm::fun::iota( num_tables ) );
else
return Opm::fun::map( famII_sgwfn, Opm::fun::iota( num_tables ) );
case SatfuncFamily::III:
return Opm::fun::map( famIII, Opm::fun::iota( num_tables ) );
@ -742,6 +806,7 @@ namespace {
const auto& sgofLetTables = tm.getSgofletTable();
const auto& slgofTables = tm.getSlgofTables();
const auto& sgfnTables = tm.getSgfnTables();
const auto& sgwfnTables = tm.getSgwfnTables();
const auto& gsfTables = tm.getGsfTables();
const auto& famI_sgof = [&sgofTables]( int i ) {
@ -760,6 +825,10 @@ namespace {
return sgfnTables.getTable<Opm::SgfnTable>( i ).getKrgColumn().back();
};
const auto& famII_sgwfn = [&sgwfnTables]( int i ) {
return sgwfnTables.getTable<Opm::SgwfnTable>( i ).getKrgColumn().back();
};
const auto& famIII = [&gsfTables]( int i ) {
return gsfTables.getTable<Opm::GsfTable>( i ).getKrgColumn().back();
};
@ -775,7 +844,10 @@ namespace {
else
return Opm::fun::map( famI_slgof, Opm::fun::iota( num_tables ) );
case SatfuncFamily::II:
return Opm::fun::map( famII, Opm::fun::iota( num_tables ) );
if( !sgfnTables.empty() )
return Opm::fun::map( famII, Opm::fun::iota( num_tables ) );
else
return Opm::fun::map( famII_sgwfn, Opm::fun::iota( num_tables ) );
case SatfuncFamily::III:
return Opm::fun::map( famIII, Opm::fun::iota( num_tables ) );
default:
@ -797,6 +869,7 @@ namespace {
const auto& sgofLetTables = tm.getSgofletTable();
const auto& slgofTables = tm.getSlgofTables();
const auto& sgfnTables = tm.getSgfnTables();
const auto& sgwfnTables = tm.getSgwfnTables();
const auto& gsfTables = tm.getGsfTables();
auto sr = std::vector<double>(num_tables, 0.0);
@ -843,6 +916,14 @@ namespace {
return sgfn.getKrgColumn().eval(ix);
};
const auto famII_sgwfn = [&sgwfnTables, &sr](const int i) -> double
{
const auto& sgwfn = sgwfnTables.getTable<Opm::SgwfnTable>(i);
const auto ix = sgwfn.getSgColumn().lookup(sr[i]);
return sgwfn.getKrgColumn().eval(ix);
};
const auto famIII = [&gsfTables, &sr](const int i) -> double
{
const auto& gsf = gsfTables.getTable<Opm::GsfTable>(i);
@ -862,7 +943,10 @@ namespace {
else
return Opm::fun::map( famI_slgof, Opm::fun::iota( num_tables ) );
case SatfuncFamily::II:
return Opm::fun::map( famII, Opm::fun::iota( num_tables ) );
if( !sgfnTables.empty() )
return Opm::fun::map( famII, Opm::fun::iota( num_tables ) );
else
return Opm::fun::map( famII_sgwfn, Opm::fun::iota( num_tables ) );
case SatfuncFamily::III:
return Opm::fun::map( famIII, Opm::fun::iota( num_tables ) );
default:
@ -883,6 +967,7 @@ namespace {
const auto& swofTables = tm.getSwofTables();
const auto& swofLetTables = tm.getSwofletTable();
const auto& swfnTables = tm.getSwfnTables();
const auto& sgwfnTables = tm.getSgwfnTables();
const auto& wsfTables = tm.getWsfTables();
auto sr = std::vector<double>(num_tables, 0.0);
@ -921,6 +1006,14 @@ namespace {
return swfn.getKrwColumn().eval(ix);
};
const auto& famII_sgwfn = [&sgwfnTables, &sr](const int i) -> double
{
const auto& sgwfn = sgwfnTables.getTable<Opm::SgwfnTable>(i);
const auto ix = sgwfn.getSgColumn().lookup(1. - sr[i]);
return sgwfn.getKrgwColumn().eval(ix);
};
const auto& famIII = [&wsfTables, &sr](const int i) -> double
{
const auto& wsf = wsfTables.getTable<Opm::WsfTable>(i);
@ -939,7 +1032,10 @@ namespace {
throw std::domain_error("Either SWOF or SWOFLET tables must be provided");
case SatfuncFamily::II:
return Opm::fun::map( famII, Opm::fun::iota( num_tables ) );
if( !swfnTables.empty() )
return Opm::fun::map( famII, Opm::fun::iota( num_tables ) );
else
return Opm::fun::map( famII_sgwfn, Opm::fun::iota( num_tables ) );
case SatfuncFamily::III:
return Opm::fun::map( famIII, Opm::fun::iota( num_tables ) );
default:
@ -1115,6 +1211,7 @@ namespace {
const auto& sgofLetTables = tm.getSgofletTable();
const auto& slgofTables = tm.getSlgofTables();
const auto& sgfnTables = tm.getSgfnTables();
const auto& sgwfnTables = tm.getSgwfnTables();
const auto& gsfTables = tm.getGsfTables();
const auto& famI_sgof = [&sgofTables]( int i ) {
@ -1133,6 +1230,10 @@ namespace {
return sgfnTables.getTable<Opm::SgfnTable>( i ).getPcogColumn().back();
};
const auto& famII_sgwfn = [&sgwfnTables]( int i ) {
return sgwfnTables.getTable<Opm::SgwfnTable>( i ).getPcgwColumn().back();
};
const auto& famIII = [&gsfTables]( int i ) {
return gsfTables.getTable<Opm::GsfTable>( i ).getPcgwColumn().back();
};
@ -1148,7 +1249,10 @@ namespace {
else
return Opm::fun::map( famI_slgof, Opm::fun::iota( num_tables ) );
case SatfuncFamily::II:
return Opm::fun::map( famII, Opm::fun::iota( num_tables ) );
if( !sgfnTables.empty() )
return Opm::fun::map( famII, Opm::fun::iota( num_tables ) );
else
return Opm::fun::map( famII_sgwfn, Opm::fun::iota( num_tables ) );
case SatfuncFamily::III:
return Opm::fun::map( famIII, Opm::fun::iota( num_tables ) );
default:
@ -1162,7 +1266,8 @@ namespace {
{
const auto num_tables = tm.getTabdims().getNumSatTables();
if (! ph.active(::Opm::Phase::WATER))
if (! ph.active(::Opm::Phase::WATER) ||
! ph.active(::Opm::Phase::OIL))
return std::vector<double>(num_tables, 0.0);
const auto& swofTables = tm.getSwofTables();
@ -1279,6 +1384,7 @@ namespace {
const auto& swofTables = tm.getSwofTables();
const auto& swofLetTables = tm.getSwofletTable();
const auto& swfnTables = tm.getSwfnTables();
const auto& sgwfnTables = tm.getSgwfnTables();
const auto& wsfTables = tm.getWsfTables();
const auto& famI = [&swofTables]( int i ) {
@ -1293,6 +1399,10 @@ namespace {
return swfnTables.getTable<Opm::SwfnTable>( i ).getKrwColumn().back();
};
const auto& famII_sgwfn = [&sgwfnTables]( int i ) {
return sgwfnTables.getTable<Opm::SgwfnTable>( i ).getKrgwColumn().front();
};
const auto& famIII = [&wsfTables]( int i ) {
return wsfTables.getTable<Opm::WsfTable>( i ).getKrwColumn().back();
};
@ -1305,10 +1415,11 @@ namespace {
return Opm::fun::map( famI_let, Opm::fun::iota( num_tables ) );
else
throw std::domain_error("Either SWOF or SWOFLET tables must be provided");
case SatfuncFamily::II:
return Opm::fun::map( famII, Opm::fun::iota( num_tables ) );
if( !swfnTables.empty() )
return Opm::fun::map( famII, Opm::fun::iota( num_tables ) );
else
return Opm::fun::map( famII_sgwfn, Opm::fun::iota( num_tables ) );
case SatfuncFamily::III:
return Opm::fun::map( famIII, Opm::fun::iota( num_tables ) );
default:

View File

@ -96,11 +96,13 @@ namespace {
// note: we allow for SOF2 to be part of family1 for threeP +
// solvent simulations.
const auto family2 = // SGFN, SOF{2,3}, SWFN
(gas && deck.hasKeyword<Opm::ParserKeywords::SGFN>()) ||
const auto family2 = // SGFN, SOF{2,3}, SWFN, SGWFN
(gas && (deck.hasKeyword<Opm::ParserKeywords::SGFN>() ||
deck.hasKeyword<Opm::ParserKeywords::SGWFN>())) ||
(oil && ((threeP && deck.hasKeyword<Opm::ParserKeywords::SOF3>()) ||
(twoP && deck.hasKeyword<Opm::ParserKeywords::SOF2>()))) ||
(wat && deck.hasKeyword<Opm::ParserKeywords::SWFN>());
(wat && (deck.hasKeyword<Opm::ParserKeywords::SWFN>() ||
deck.hasKeyword<Opm::ParserKeywords::SGWFN>()));
const auto family3 = //WSF, GSF gas-water CO2STORE case
deck.hasKeyword<Opm::ParserKeywords::GSF>() &&
deck.hasKeyword<Opm::ParserKeywords::WSF>();

View File

@ -21,6 +21,7 @@
#include <opm/material/fluidmatrixinteractions/EclMaterialLawManager.hpp>
#include <opm/input/eclipse/EclipseState/Tables/SgfnTable.hpp>
#include <opm/input/eclipse/EclipseState/Tables/SgofTable.hpp>
#include <opm/input/eclipse/EclipseState/Tables/SgwfnTable.hpp>
#include <opm/input/eclipse/EclipseState/Tables/SlgofTable.hpp>
#include <opm/input/eclipse/EclipseState/Tables/Sof2Table.hpp>
#include <opm/input/eclipse/EclipseState/Tables/Sof3Table.hpp>
@ -276,25 +277,34 @@ readGasWaterParameters_(GasWaterEffectiveParamVector& dest, unsigned satRegionId
case SatFuncControls::KeywordFamily::Family_II:
{
//Todo: allow also for Sgwfn table input as alternative to Sgfn and Swfn table input
const SgfnTable& sgfnTable = tableManager.getSgfnTables().template getTable<SgfnTable>( satRegionIdx );
const SwfnTable& swfnTable = tableManager.getSwfnTables().template getTable<SwfnTable>( satRegionIdx );
const TableContainer& sgwfnTables = tableManager.getSgwfnTables();
effParams.setApproach(SatCurveMultiplexerApproach::PiecewiseLinear);
auto& realParams = effParams.template getRealParams<SatCurveMultiplexerApproach::PiecewiseLinear>();
if (!sgwfnTables.empty()){
const SgwfnTable& sgwfnTable = tableManager.getSgwfnTables().template getTable<SgwfnTable>( satRegionIdx );
std::vector<double> SwSamples(sgwfnTable.numRows());
for (size_t sampleIdx = 0; sampleIdx < sgwfnTable.numRows(); ++ sampleIdx)
SwSamples[sampleIdx] = 1 - sgwfnTable.get("SG", sampleIdx);
realParams.setKrwSamples(SwSamples, normalizeKrValues_(tolcrit, sgwfnTable.getColumn("KRGW")));
realParams.setKrnSamples(SwSamples, normalizeKrValues_(tolcrit, sgwfnTable.getColumn("KRG")));
realParams.setPcnwSamples(SwSamples, sgwfnTable.getColumn("PCGW").vectorCopy());
}
else {
const SgfnTable& sgfnTable = tableManager.getSgfnTables().template getTable<SgfnTable>( satRegionIdx );
const SwfnTable& swfnTable = tableManager.getSwfnTables().template getTable<SwfnTable>( satRegionIdx );
std::vector<double> SwColumn = swfnTable.getColumn("SW").vectorCopy();
std::vector<double> SwColumn = swfnTable.getColumn("SW").vectorCopy();
realParams.setKrwSamples(SwColumn, normalizeKrValues_(tolcrit, swfnTable.getColumn("KRW")));
std::vector<double> SwSamples(sgfnTable.numRows());
for (size_t sampleIdx = 0; sampleIdx < sgfnTable.numRows(); ++ sampleIdx)
SwSamples[sampleIdx] = 1 - sgfnTable.get("SG", sampleIdx);
realParams.setKrnSamples(SwSamples, normalizeKrValues_(tolcrit, sgfnTable.getColumn("KRG")));
//Capillary pressure is read from SWFN.
//For gas-water system the capillary pressure column values are set to 0 in SGFN
realParams.setPcnwSamples(SwColumn, swfnTable.getColumn("PCOW").vectorCopy());
realParams.setKrwSamples(SwColumn, normalizeKrValues_(tolcrit, swfnTable.getColumn("KRW")));
std::vector<double> SwSamples(sgfnTable.numRows());
for (size_t sampleIdx = 0; sampleIdx < sgfnTable.numRows(); ++ sampleIdx)
SwSamples[sampleIdx] = 1 - sgfnTable.get("SG", sampleIdx);
realParams.setKrnSamples(SwSamples, normalizeKrValues_(tolcrit, sgfnTable.getColumn("KRG")));
//Capillary pressure is read from SWFN.
//For gas-water system the capillary pressure column values are set to 0 in SGFN
realParams.setPcnwSamples(SwColumn, swfnTable.getColumn("PCOW").vectorCopy());
}
realParams.finalize();
break;
}

View File

@ -30,6 +30,7 @@
#include <opm/input/eclipse/EclipseState/Tables/PvtoTable.hpp>
#include <opm/input/eclipse/EclipseState/Tables/SgfnTable.hpp>
#include <opm/input/eclipse/EclipseState/Tables/SgofTable.hpp>
#include <opm/input/eclipse/EclipseState/Tables/SgwfnTable.hpp>
#include <opm/input/eclipse/EclipseState/Tables/Sof2Table.hpp>
#include <opm/input/eclipse/EclipseState/Tables/Sof3Table.hpp>
#include <opm/input/eclipse/EclipseState/Tables/SwfnTable.hpp>
@ -500,6 +501,83 @@ namespace { namespace SatFunc {
return numActRows;
});
}
/// Create linearised and padded 'TAB' vector entries of normalised
/// SGFN tables for all saturation function regions from Family Two
/// table data (SGWFN keyword).
///
/// \param[in] numRows Number of rows to allocate in the output
/// vector for each table. Expected to be equal to the number of
/// declared saturation nodes in the simulation run's TABDIMS
/// keyword (Item 3).
///
/// \param tolcrit Minimum relative permeability threshold value for
/// phase to be considered mobile. Values less than this threshold
/// are output as zero.
///
/// \param[in] units Active unit system. Needed to convert SI
/// convention capillary pressure values (Pascal) to declared
/// conventions of the run specification.
///
/// \param[in] gsf Collection of SGFN tables for all saturation
/// regions.
///
/// \return Linearised and padded 'TAB' vector values for output
/// SGFN tables. Corresponds to unit-converted copies of columns
/// 1, 2, and 4--with added derivatives--of the input SGWFN tables.
std::vector<double>
fromSGWFN(const std::size_t numRows,
const double tolcrit,
const Opm::UnitSystem& units,
const Opm::TableContainer& gsf)
{
using Sgwfn = ::Opm::SgwfnTable;
const auto numTab = gsf.size();
const auto numDep = std::size_t{2}; // Krg, Pcgw
return detail::createSatfuncTable(numTab, numRows, numDep,
[tolcrit, &units, &gsf](const std::size_t tableID,
const std::size_t primID,
Opm::LinearisedOutputTable& linTable)
-> std::size_t
{
const auto& t = gsf.getTable<Sgwfn>(tableID);
auto numActRows = std::size_t{0};
// Sg
{
const auto& Sg = t.getSgColumn();
numActRows = Sg.size();
std::copy(std::begin(Sg), std::end(Sg),
linTable.column(tableID, primID, 0));
}
// Krg(Sg)
detail::outputRelperm(t.getKrgColumn(), tolcrit,
linTable.column(tableID, primID, 1));
// Pcgw(Sg)
{
constexpr auto uPress = ::Opm::UnitSystem::measure::pressure;
const auto& pc = t.getPcgwColumn();
std::transform(std::begin(pc), std::end(pc),
linTable.column(tableID, primID, 2),
[&units](const double Pc) -> double
{
return units.from_si(uPress, Pc);
});
}
// Inform createSatfuncTable() of number of active rows in
// this table. Needed to compute slopes of piecewise linear
// interpolants.
return numActRows;
});
}
} // Gas
/// Functions to create linearised, padded, and normalised SOFN output
@ -1245,6 +1323,87 @@ namespace { namespace SatFunc {
});
}
/// Create linearised and padded 'TAB' vector entries of normalised
/// SWFN tables for all saturation function regions from Family Two
/// table data (SGWFN keyword).
///
/// \param[in] numRows Number of rows to allocate for each table in
/// the output vector. Expected to be equal to the number of
/// declared saturation nodes in the simulation run's TABDIMS
/// keyword (Item 3).
///
/// \param tolcrit Minimum relative permeability threshold value for
/// phase to be considered mobile. Values less than this threshold
/// are output as zero.
///
/// \param[in] swof Collection of SWOF tables for all saturation
/// regions.
///
/// \return Linearised and padded 'TAB' vector values for output
/// SWFN tables. Corresponds to unit-converted copies of columns
/// 1, 3, and 4 --with added derivatives--of the input SGWFN tables.
std::vector<double>
fromSGWFN(const std::size_t numRows,
const double tolcrit,
const Opm::TableContainer& swfn)
{
using SWFN = ::Opm::SgwfnTable;
const auto numTab = swfn.size();
const auto numDep = std::size_t{2}; // Krw, Pcow
return detail::createSatfuncTable(numTab, numRows, numDep,
[tolcrit, &swfn]
(const std::size_t tableID,
const std::size_t primID,
Opm::LinearisedOutputTable& linTable)
-> std::size_t
{
const auto& t = swfn.getTable<SWFN>(tableID);
auto numActRows = std::size_t{0};
// Sw
{
const auto& Sg = t.getSgColumn();
numActRows = Sg.size();
auto Sw = std::vector<double>{};
Sw.reserve(numActRows);
std::transform(std::begin(Sg), std::end(Sg),
std::back_inserter(Sw),
[](const auto sg) { return 1.0 - sg; });
std::copy(Sw.rbegin(), Sw.rend(),
linTable.column(tableID, primID, 0));
}
// Krgw(Sw)
{
const auto& kr = t.getKrgwColumn();
const auto krgw = std::vector<double> {
std::begin(kr), std::end(kr)
};
detail::outputRelperm(krgw.rbegin(), krgw.rend(), tolcrit,
linTable.column(tableID, primID, 1));
}
// Pcow(Sw) = zero
{
const auto& pc = t.getPcgwColumn();
std::transform(std::begin(pc), std::end(pc),
linTable.column(tableID, primID, 2),
[](const double) -> double
{
return 0.0;
});
}
// Inform createSatfuncTable() of number of active rows in
// this table. Needed to compute slopes of piecewise linear
// interpolants.
return numActRows;
});
}
/// Create linearised and padded 'TAB' vector entries of normalised
/// WSF tables for all saturation function regions from Family Three
/// table data (WSF keyword).
@ -2456,8 +2615,8 @@ namespace Opm {
(gas && !tabMgr.getSgofletTable().empty()) ||
(wat && !tabMgr.getSwofletTable().empty());
const auto famII = // SGFN, SOF{2,3}, SWFN
(gas && tabMgr.hasTables("SGFN")) ||
const auto famII = // SGFN, SOF{2,3}, SWFN, SGWFN
(gas && (tabMgr.hasTables("SGFN") || tabMgr.hasTables("SGWFN"))) ||
(oil && ((threeP && tabMgr.hasTables("SOF3")) ||
tabMgr.hasTables("SOF2"))) ||
(wat && tabMgr.hasTables("SWFN"));
@ -2621,15 +2780,25 @@ namespace Opm {
.minimumRelpermMobilityThreshold();
if (gas) {
const auto& tables = tabMgr.getSgfnTables();
if ( !tabMgr.getSgfnTables().empty() ) {
const auto& tables = tabMgr.getSgfnTables();
const auto sgfn =
SatFunc::Gas::fromSGFN(nssfun, tolcrit,
this->units, tables);
this->addData(Ix::SgfnTableStart, sgfn);
this->m_tabdims[Ix::SgfnNumSatNodes] = nssfun;
this->m_tabdims[Ix::SgfnNumTables] = tables.size();
const auto sgfn =
SatFunc::Gas::fromSGFN(nssfun, tolcrit,
this->units, tables);
this->addData(Ix::SgfnTableStart, sgfn);
this->m_tabdims[Ix::SgfnNumSatNodes] = nssfun;
this->m_tabdims[Ix::SgfnNumTables] = tables.size();
}
else {
const auto& tables = tabMgr.getSgwfnTables();
const auto sgfn =
SatFunc::Gas::fromSGWFN(nssfun, tolcrit,
this->units, tables);
this->addData(Ix::SgfnTableStart, sgfn);
this->m_tabdims[Ix::SgfnNumSatNodes] = nssfun;
this->m_tabdims[Ix::SgfnNumTables] = tables.size();
}
}
if (oil) {
@ -2656,14 +2825,22 @@ namespace Opm {
}
if (wat) {
const auto& tables = tabMgr.getSwfnTables();
const auto swfn =
SatFunc::Water::fromSWFN(nssfun, tolcrit, this->units, tables);
this->addData(Ix::SwfnTableStart, swfn);
this->m_tabdims[Ix::SwfnNumSatNodes] = nssfun;
this->m_tabdims[Ix::SwfnNumTables] = tables.size();
if ( !tabMgr.getSwfnTables().empty() ) {
const auto& tables = tabMgr.getSwfnTables();
const auto swfn =
SatFunc::Water::fromSWFN(nssfun, tolcrit, this->units, tables);
this->m_tabdims[Ix::SwfnNumTables] = tables.size();
this->addData(Ix::SwfnTableStart, swfn);
this->m_tabdims[Ix::SwfnNumSatNodes] = nssfun;
}
else {
const auto& tables = tabMgr.getSgwfnTables();
const auto swfn =
SatFunc::Water::fromSGWFN(nssfun, tolcrit, tables);
this->m_tabdims[Ix::SwfnNumTables] = tables.size();
this->addData(Ix::SwfnTableStart, swfn);
this->m_tabdims[Ix::SwfnNumSatNodes] = nssfun;
}
}
}

View File

@ -787,6 +787,80 @@ SOGCR -- Requires 'OIL'
)"), Opm::OpmInputError);
}
BOOST_AUTO_TEST_CASE(SGWFN) {
std::string deck_string = R"(
RUNSPEC
DIMENS
6 6 3 /
WATER
GAS
CO2STORE
TABDIMS
/
GRID
DXV
6*100.0 /
DYV
6*100.0 /
DZV
3*5.0 /
TOPS
36*2000.0 /
PERMX
108*100.0 /
PERMY
108*100.0 /
PERMZ
108*10.0 /
PORO
108*0.3 /
PROPS
SGWFN
0.00 0.00 0.9 0.0
0.05 0.02 0.8 5.
0.10 0.03 0.5 10.0
0.80 1.00 0.0 20.0
/
)";
const auto es = ::Opm::EclipseState {
::Opm::Parser{}.parseString(deck_string)
};
const auto& tm = es.getTableManager();
const auto& ph = es.runspec().phases();
const auto tolcrit = 0.0;
auto rtepPtr = satfunc::getRawTableEndpoints(tm, ph, tolcrit);
// Water end-points
{
const auto swl = rtepPtr.connate .water;
const auto swcr = rtepPtr.critical.water;
const auto swu = rtepPtr.maximum .water;
BOOST_CHECK_CLOSE(swl [0], 0.2, 1.0e-10);
BOOST_CHECK_CLOSE(swcr[0], 0.2, 1.0e-10);
BOOST_CHECK_CLOSE(swu [0], 1.0, 1.0e-10);
}
// Gas end-points
{
const auto sgl = rtepPtr.connate .gas;
const auto sgcr = rtepPtr.critical.gas;
const auto sgu = rtepPtr.maximum .gas;
BOOST_CHECK_CLOSE(sgl [0], 0.0, 1.0e-10);
BOOST_CHECK_CLOSE(sgcr[0], 0.0, 1.0e-10);
BOOST_CHECK_CLOSE(sgu [0], 0.8, 1.0e-10);
}
}
namespace {
std::string satfunc_model_setup()
{