PVTO: Add Means of Identifying Non-Monotonic Saturated FVFs

This commit introduces a way of diagnosing an uncommon but possible
issue in the PVTO table.  If the formation volume factor (BO) in the
saturated table (record 1 of each subtable) does not increase
monotonically as a function of the dissolved gas/oil ratio (RS),
then there is a risk that the simulation run will have convergence
problems.

We add a special purpose member function

    PvtoTable::nonMonotonicSaturatedFVF

which checks for this condition and returns a vector records for
which the condition is true.  The new member function

    TableManager::checkPVTOMonotonicity

then collates these records into a formatted string which is printed
to the console and the .PRT file.

Example Diagnostic Output:

    Warning: Non-Monotonic Oil Formation Volume Factor Detected
      * PVTO [PVTNUM = 1]
        Record  9: FVF 1.23 at RS 123 is not greater than FVF 2.34 at RS 120
        Record 10: FVF 1.22 at RS 125 is not greater than FVF 1.23 at RS 123
        Record 11: FVF 1.21 at RS 125 is not greater than FVF 1.22 at RS 125
This commit is contained in:
Bård Skaflestad 2020-09-18 09:35:19 +02:00
parent b5b1588a17
commit 1311afb261
4 changed files with 91 additions and 2 deletions

View File

@ -17,22 +17,34 @@
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef OPM_PARSER_PVTO_TABLE_HPP
#define OPM_PARSER_PVTO_TABLE_HPP
#define OPM_PARSER_PVTO_TABLE_HPP
#include <opm/parser/eclipse/EclipseState/Tables/PvtxTable.hpp>
#include <array>
#include <cstddef>
#include <vector>
namespace Opm {
class DeckKeyword;
class PvtoTable : public PvtxTable {
public:
struct FlippedFVF {
std::size_t i;
std::array<double, std::size_t{2}> Rs;
std::array<double, std::size_t{2}> Bo;
};
PvtoTable() = default;
PvtoTable(const DeckKeyword& keyword, size_t tableIdx);
static PvtoTable serializeObject();
bool operator==(const PvtoTable& data) const;
std::vector<FlippedFVF> nonMonotonicSaturatedFVF() const;
};
}

View File

@ -491,6 +491,8 @@ namespace Opm {
tableVector.emplace_back( tableKeyword , tableIdx );
}
void checkPVTOMonotonicity(const Deck& deck) const;
std::map<std::string , TableContainer> m_simpleTables;
std::vector<PvtgTable> m_pvtgTables;
std::vector<PvtgwTable> m_pvtgwTables;

View File

@ -18,8 +18,13 @@
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cmath>
#include <iomanip>
#include <ios>
#include <memory>
#include <sstream>
#include <opm/common/OpmLog/OpmLog.hpp>
#include <opm/common/OpmLog/LogUtil.hpp>
#include <opm/parser/eclipse/Parser/ParserKeywords/A.hpp>
@ -120,6 +125,10 @@ namespace Opm {
initFullTables(deck, "PVTGWO", m_pvtgwoTables);
initFullTables(deck, "PVTO", m_pvtoTables);
if (deck.hasKeyword<ParserKeywords::PVTO>()) {
this->checkPVTOMonotonicity(deck);
}
if( deck.hasKeyword( "PVTW" ) )
this->m_pvtwTable = PvtwTable( deck.getKeyword( "PVTW" ) );
@ -1195,6 +1204,48 @@ namespace Opm {
}
}
void TableManager::checkPVTOMonotonicity(const Deck& deck) const
{
const auto& usys = deck.getActiveUnitSystem();
const auto& tables = this->getPvtoTables();
using M = UnitSystem::measure;
const auto nDigit = [](const std::size_t n) {
return 1 + static_cast<int>(std::floor(std::log10(n)));
};
const auto nDigit_reg = nDigit(tables.size());
auto tableID = std::size_t{0};
std::ostringstream os;
for (const auto& pvto : tables) {
++tableID; // One-based table ID
const auto& flipped = pvto.nonMonotonicSaturatedFVF();
if (flipped.empty()) { continue; }
const auto nDigit_rec = nDigit(flipped.back().i + 1);
os << " * PVTO [PVTNUM = " << std::setw(nDigit_reg) << tableID << "]\n";
for (const auto& f : flipped) {
os << " Record " << std::setw(nDigit_rec) << (f.i + 1)
<< ": FVF " << std::setprecision(3)
<< usys.from_si(M::oil_formation_volume_factor, f.Bo[1])
<< " at RS "
<< usys.from_si(M::gas_oil_ratio, f.Rs[1])
<< " is not greater than FVF "
<< usys.from_si(M::oil_formation_volume_factor, f.Bo[0])
<< " at RS "
<< usys.from_si(M::gas_oil_ratio, f.Rs[0]) << '\n';
}
}
if (os.tellp() != std::streampos{0}) {
// There were non-monotonic FVFs in saturated table
OpmLog::warning("Non-Monotonic Oil Formation Volume Factor Detected\n" + os.str());
}
}
TableManager::SplitSimpleTables TableManager::splitSimpleTable(std::map<std::string,TableContainer>& simpleTables)
{
SplitSimpleTables result;

View File

@ -191,6 +191,30 @@ bool PvtoTable::operator==(const PvtoTable& data) const {
return static_cast<const PvtxTable&>(*this) == static_cast<const PvtxTable&>(data);
}
std::vector<PvtoTable::FlippedFVF>
PvtoTable::nonMonotonicSaturatedFVF() const
{
auto nonmonoFVF = std::vector<FlippedFVF>{};
const auto& Rs = this->m_saturatedTable.getColumn("RS");
const auto& Bo = this->m_saturatedTable.getColumn("BO");
const auto nrec = Rs.size();
for (auto rec = 1 + 0*nrec; rec < nrec; ++rec) {
if (Bo[rec] > Bo[rec - 1]) {
// Normal case, Bo increases monotonically.
continue;
}
auto& flip = nonmonoFVF.emplace_back();
flip.i = rec;
flip.Rs[0] = Rs[rec - 1]; flip.Rs[1] = Rs[rec];
flip.Bo[0] = Bo[rec - 1]; flip.Bo[1] = Bo[rec];
}
return nonmonoFVF;
}
SpecheatTable::SpecheatTable(const DeckItem& item)
{
@ -791,7 +815,7 @@ const TableColumn& WatvisctTable::getTemperatureColumn() const {
const TableColumn& WatvisctTable::getWaterViscosityColumn() const {
return SimpleTable::getColumn(1);
}
}
GasvisctTable::GasvisctTable( const Deck& deck, const DeckItem& deckItem ) {
int numComponents = deck.getKeyword<ParserKeywords::COMPS>().getRecord(0).getItem(0).get< int >(0);