#1994 Update opm-flowdiagnostics-applications to cd614100f3d5b8fcf1fd783e929630f11251682e

This commit is contained in:
Jacob Støren 2017-11-29 11:34:44 +01:00
parent 5efd583390
commit 2a41b4fbb8
17 changed files with 5643 additions and 283 deletions

View File

@ -14,7 +14,7 @@ set(ERT_GITHUB_SHA "2e36798b43daf18c112b91aa3febbf2fccd4a95f")
set(OPM_FLOWDIAGNOSTICS_SHA "7e2be931d430796ed42efcfb5c6b67a8d5962f7f") set(OPM_FLOWDIAGNOSTICS_SHA "7e2be931d430796ed42efcfb5c6b67a8d5962f7f")
# https://github.com/OPM/opm-flowdiagnostics-applications # https://github.com/OPM/opm-flowdiagnostics-applications
set(OPM_FLOWDIAGNOSTICS_APPLICATIONS_SHA "75b333335f6cd055d3130d460c6d87444fb7aed4") set(OPM_FLOWDIAGNOSTICS_APPLICATIONS_SHA "cd614100f3d5b8fcf1fd783e929630f11251682e")
# https://github.com/OPM/opm-parser/blob/master/opm/parser/eclipse/Units/Units.hpp # https://github.com/OPM/opm-parser/blob/master/opm/parser/eclipse/Units/Units.hpp
# This file was moved from opm-core to opm-parser october 2016 # This file was moved from opm-core to opm-parser october 2016

View File

@ -25,6 +25,7 @@ list (APPEND MAIN_SOURCE_FILES
opm/utility/ECLEndPointScaling.cpp opm/utility/ECLEndPointScaling.cpp
opm/utility/ECLFluxCalc.cpp opm/utility/ECLFluxCalc.cpp
opm/utility/ECLGraph.cpp opm/utility/ECLGraph.cpp
opm/utility/ECLPropertyUnitConversion.cpp
opm/utility/ECLPropTable.cpp opm/utility/ECLPropTable.cpp
opm/utility/ECLPvtCommon.cpp opm/utility/ECLPvtCommon.cpp
opm/utility/ECLPvtCurveCollection.cpp opm/utility/ECLPvtCurveCollection.cpp
@ -41,6 +42,7 @@ list (APPEND MAIN_SOURCE_FILES
list (APPEND TEST_SOURCE_FILES list (APPEND TEST_SOURCE_FILES
tests/test_eclendpointscaling.cpp tests/test_eclendpointscaling.cpp
tests/test_eclpropertyunitconversion.cpp
tests/test_eclproptable.cpp tests/test_eclproptable.cpp
tests/test_eclpvtcommon.cpp tests/test_eclpvtcommon.cpp
tests/test_eclregionmapping.cpp tests/test_eclregionmapping.cpp
@ -54,6 +56,7 @@ list (APPEND EXAMPLE_SOURCE_FILES
examples/computePhaseFluxes.cpp examples/computePhaseFluxes.cpp
examples/computeToFandTracers.cpp examples/computeToFandTracers.cpp
examples/computeTracers.cpp examples/computeTracers.cpp
examples/dynamicCellProperty.cpp
examples/extractFromRestart.cpp examples/extractFromRestart.cpp
examples/extractPropCurves.cpp examples/extractPropCurves.cpp
tests/runAcceptanceTest.cpp tests/runAcceptanceTest.cpp
@ -68,6 +71,7 @@ list (APPEND PUBLIC_HEADER_FILES
opm/utility/ECLGraph.hpp opm/utility/ECLGraph.hpp
opm/utility/ECLPhaseIndex.hpp opm/utility/ECLPhaseIndex.hpp
opm/utility/ECLPiecewiseLinearInterpolant.hpp opm/utility/ECLPiecewiseLinearInterpolant.hpp
opm/utility/ECLPropertyUnitConversion.hpp
opm/utility/ECLPropTable.hpp opm/utility/ECLPropTable.hpp
opm/utility/ECLPvtCommon.hpp opm/utility/ECLPvtCommon.hpp
opm/utility/ECLPvtCurveCollection.hpp opm/utility/ECLPvtCurveCollection.hpp

View File

@ -0,0 +1,273 @@
/*
Copyright 2017 SINTEF ICT, Applied Mathematics.
Copyright 2017 Statoil ASA.
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include <examples/exampleSetup.hpp>
#include <opm/utility/ECLCaseUtilities.hpp>
#include <opm/utility/ECLPhaseIndex.hpp>
#include <opm/utility/ECLPvtCommon.hpp>
#include <opm/utility/ECLPvtCurveCollection.hpp>
#include <opm/utility/ECLResultData.hpp>
#include <opm/utility/ECLUnitHandling.hpp>
#include <cstddef>
#include <exception>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <boost/filesystem.hpp>
namespace {
void openRestartSet(const Opm::ECLCaseUtilities::ResultSet& rset,
const int step,
std::unique_ptr<Opm::ECLRestartData>& rstrt)
{
if (! (rset.isUnifiedRestart() && rstrt)) {
// Not a unified restart file or this is the first time we're
// seeing the result set.
rstrt.reset(new Opm::ECLRestartData(rset.restartFile(step)));
}
}
}
struct CellState
{
CellState(const Opm::ECLGraph& G,
const Opm::ECLCaseUtilities::ResultSet& rset,
const int cellID_);
int cellID;
std::vector<double> time;
std::vector<double> Po;
std::vector<double> Rs;
std::vector<double> Rv;
};
CellState::CellState(const Opm::ECLGraph& G,
const Opm::ECLCaseUtilities::ResultSet& rset,
const int cellID_)
: cellID(cellID_)
{
const auto rsteps = rset.reportStepIDs();
this->time.reserve(rsteps.size());
this->Po .reserve(rsteps.size());
this->Rs .reserve(rsteps.size());
this->Rv .reserve(rsteps.size());
auto rstrt = std::unique_ptr<Opm::ECLRestartData>{};
for (const auto& step : rsteps) {
openRestartSet(rset, step, rstrt);
rstrt->selectReportStep(step);
this->time.push_back(example::simulationTime(*rstrt));
{
const auto& press =
G.rawLinearisedCellData<double>(*rstrt, "PRESSURE");
this->Po.push_back(press[cellID]);
}
{
const auto& R =
G.rawLinearisedCellData<double>(*rstrt, "RS");
this->Rs.push_back(R.empty() ? 0.0 : R[cellID]);
}
{
const auto& R =
G.rawLinearisedCellData<double>(*rstrt, "RV");
this->Rv.push_back(R.empty() ? 0.0 : R[cellID]);
}
}
}
struct Property
{
std::string name;
std::vector<double> data;
};
namespace {
// -----------------------------------------------------------------------
// Gas Properties
std::vector<double>
Bg(const Opm::ECLPVT::ECLPvtCurveCollection& pvtCC,
const CellState& x)
{
using RC = Opm::ECLPVT::RawCurve;
using PI = Opm::ECLPhaseIndex;
return pvtCC.getDynamicPropertyNative(RC::FVF, PI::Vapour,
x.cellID, x.Po, x.Rv);
}
std::vector<double>
mu_g(const Opm::ECLPVT::ECLPvtCurveCollection& pvtCC,
const CellState& x)
{
using RC = Opm::ECLPVT::RawCurve;
using PI = Opm::ECLPhaseIndex;
return pvtCC.getDynamicPropertyNative(RC::Viscosity, PI::Vapour,
x.cellID, x.Po, x.Rv);
}
// -----------------------------------------------------------------------
// Oil Properties
std::vector<double>
Bo(const Opm::ECLPVT::ECLPvtCurveCollection& pvtCC,
const CellState& x)
{
using RC = Opm::ECLPVT::RawCurve;
using PI = Opm::ECLPhaseIndex;
return pvtCC.getDynamicPropertyNative(RC::FVF, PI::Liquid,
x.cellID, x.Po, x.Rs);
}
std::vector<double>
mu_o(const Opm::ECLPVT::ECLPvtCurveCollection& pvtCC,
const CellState& x)
{
using RC = Opm::ECLPVT::RawCurve;
using PI = Opm::ECLPhaseIndex;
return pvtCC.getDynamicPropertyNative(RC::Viscosity, PI::Liquid,
x.cellID, x.Po, x.Rs);
}
// ---------------------------------------------------------------------
// Command-line argument processing and property output.
using DynProp = std::vector<double>
(*)(const Opm::ECLPVT::ECLPvtCurveCollection& pvtCC,
const CellState& x);
std::map<std::string, DynProp> enumerateProperties()
{
return {
{ "Bg" , &Bg },
{ "mu_g" , &mu_g },
{ "Bo" , &Bo },
{ "mu_o" , &mu_o },
};
}
void writeVector(const std::vector<double>& x, const std::string& var)
{
std::cout << var << " = [\n";
for (const auto& xi : x) {
std::cout << " " << xi << '\n';
}
std::cout << "]\n\n";
}
void writeResults(const CellState& x, const std::vector<Property>& props)
{
writeVector(x.time, "time");
writeVector(x.Po , "Po");
writeVector(x.Rs , "Rs");
writeVector(x.Rv , "Rv");
for (const auto& prop : props) {
writeVector(prop.data, prop.name);
}
}
std::unique_ptr<const Opm::ECLUnits::UnitSystem>
makeUnits(const std::string& unit,
const Opm::ECLInitFileData& init)
{
if ((unit == "si") || (unit == "SI") || (unit == "internal")) {
return {}; // No conversion needed.
}
if ((unit == "metric") || (unit == "Metric") || (unit == "METRIC")) {
return Opm::ECLUnits::metricUnitConventions();
}
if ((unit == "field") || (unit == "Field") || (unit == "FIELD")) {
return Opm::ECLUnits::fieldUnitConventions();
}
if ((unit == "lab") || (unit == "Lab") || (unit == "LAB")) {
return Opm::ECLUnits::labUnitConventions();
}
if ((unit == "pvt-m") || (unit == "PVT-M") || (unit == "PVTM")) {
return Opm::ECLUnits::pvtmUnitConventions();
}
std::cerr << "Unit convention '" << unit << "' not recognized\n"
<< "Using 'native' (input/serialised) conventions.\n";
return Opm::ECLUnits::serialisedUnitConventions(init);
}
} // namespace Anonymous
int main(int argc, char* argv[])
try {
const auto prm = example::initParam(argc, argv);
const auto cellID = prm.getDefault("cell", 0);
const auto rset = example::identifyResultSet(prm);
const auto init = Opm::ECLInitFileData(rset.initFile());
const auto graph = Opm::ECLGraph::load(rset.gridFile(), init);
auto pvtCC = Opm::ECLPVT::ECLPvtCurveCollection(graph, init);
if (prm.has("unit")) {
pvtCC.setOutputUnits(makeUnits(prm.get<std::string>("unit"), init));
}
const auto x = CellState{ graph, rset, cellID };
auto props = std::vector<Property>{};
for (const auto& prop : enumerateProperties()) {
if (prm.getDefault(prop.first, false)) {
props.push_back(Property{ prop.first, (*prop.second)(pvtCC, x) });
}
}
if (! props.empty()) {
std::cout.precision(16);
std::cout.setf(std::ios_base::scientific);
writeResults(x, props);
}
}
catch (const std::exception& e) {
std::cerr << "Caught Exception: " << e.what() << '\n';
return EXIT_FAILURE;
}

View File

@ -179,6 +179,19 @@ namespace example {
inline double simulationTime(const Opm::ECLRestartData& rstrt)
{
if (! rstrt.haveKeywordData("DOUBHEAD")) {
return -1.0;
}
const auto& doubhead = rstrt.keywordData<double>("DOUBHEAD");
// First item (.front()) is simulation time in days
return doubhead.front();
}
inline Opm::FlowDiagnostics::Toolbox inline Opm::FlowDiagnostics::Toolbox
initToolbox(const Opm::ECLGraph& G) initToolbox(const Opm::ECLGraph& G)

View File

@ -0,0 +1,437 @@
/*
Copyright 2017 Statoil ASA.
This file is part of the Open Porous Media Project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include <opm/utility/ECLPropertyUnitConversion.hpp>
#include <opm/utility/ECLResultData.hpp>
#include <opm/utility/ECLUnitHandling.hpp>
#include <opm/parser/eclipse/Units/Units.hpp>
#include <ert/ecl/ecl_kw_magic.h>
#include <exception>
#include <stdexcept>
namespace detail {
template <class ResultSet>
std::unique_ptr<const Opm::ECLUnits::UnitSystem>
serialisedUnitConventions(const ResultSet& rset)
{
// Use INTEHEAD from Main grid. Reasonably safe.
const auto& ih = rset.template keywordData<int>(INTEHEAD_KW);
return ::Opm::ECLUnits::createUnitSystem(ih[ INTEHEAD_UNIT_INDEX ]);
}
struct SIUnits : public ::Opm::ECLUnits::UnitSystem
{
public:
virtual std::unique_ptr<Opm::ECLUnits::UnitSystem>
clone() const override
{
return std::unique_ptr<Opm::ECLUnits::UnitSystem> {
new SIUnits(*this)
};
}
virtual double density() const override
{
return 1.0;
}
virtual double depth() const override
{
return 1.0;
}
virtual double pressure() const override
{
return 1.0;
}
virtual double reservoirRate() const override
{
return 1.0;
}
virtual double reservoirVolume() const override
{
return 1.0;
}
virtual double surfaceVolumeGas() const override
{
return 1.0;
}
virtual double surfaceVolumeLiquid() const override
{
return 1.0;
}
virtual double time() const override
{
return 1.0;
}
virtual double transmissibility() const override
{
return 1.0;
}
virtual double viscosity() const override
{
return 1.0;
}
};
}
std::unique_ptr<const Opm::ECLUnits::UnitSystem>
Opm::ECLUnits::serialisedUnitConventions(const ECLRestartData& rstrt)
{
return detail::serialisedUnitConventions(rstrt);
}
std::unique_ptr<const Opm::ECLUnits::UnitSystem>
Opm::ECLUnits::serialisedUnitConventions(const ECLInitFileData& init)
{
return detail::serialisedUnitConventions(init);
}
std::unique_ptr<const Opm::ECLUnits::UnitSystem>
Opm::ECLUnits::internalUnitConventions()
{
using UPtr = std::unique_ptr<const UnitSystem>;
return UPtr{ new detail::SIUnits{} };
}
std::unique_ptr<const Opm::ECLUnits::UnitSystem>
Opm::ECLUnits::metricUnitConventions()
{
return ::Opm::ECLUnits::createUnitSystem(1);
}
std::unique_ptr<const Opm::ECLUnits::UnitSystem>
Opm::ECLUnits::fieldUnitConventions()
{
return ::Opm::ECLUnits::createUnitSystem(2);
}
std::unique_ptr<const Opm::ECLUnits::UnitSystem>
Opm::ECLUnits::labUnitConventions()
{
return ::Opm::ECLUnits::createUnitSystem(3);
}
std::unique_ptr<const Opm::ECLUnits::UnitSystem>
Opm::ECLUnits::pvtmUnitConventions()
{
return ::Opm::ECLUnits::createUnitSystem(4);
}
// =====================================================================
// Class Convert::PhysicalQuantity::Impl
// =====================================================================
namespace {
std::unique_ptr<Opm::ECLUnits::UnitSystem>
conditionalClone(const Opm::ECLUnits::UnitSystem* usys)
{
if (usys == nullptr) {
return std::unique_ptr<Opm::ECLUnits::UnitSystem>{};
}
return usys->clone();
}
}
class Opm::ECLUnits::Convert::PhysicalQuantity::Impl
{
public:
Impl() {}
Impl(const Impl& rhs)
: from_(conditionalClone(rhs.from_.get()))
, to_ (conditionalClone(rhs.to_ .get()))
{}
Impl(Impl&& rhs)
: from_(std::move(rhs.from_))
, to_ (std::move(rhs.to_))
{}
Impl& operator=(const Impl& rhs)
{
this->from_ = conditionalClone(rhs.from_.get());
this->to_ = conditionalClone(rhs.to_ .get());
return *this;
}
Impl& operator=(Impl&& rhs)
{
this->from_ = std::move(rhs.from_);
this->to_ = std::move(rhs.to_);
return *this;
}
void from(const UnitSystem& usys)
{
this->from_ = usys.clone();
}
void to(const UnitSystem& usys)
{
this->to_ = usys.clone();
}
const UnitSystem* from() const
{
return this->from_.get();
}
const UnitSystem* to() const
{
return this->to_.get();
}
private:
std::unique_ptr<UnitSystem> from_{ nullptr };
std::unique_ptr<UnitSystem> to_ { nullptr };
};
// =====================================================================
// Class Convert::PhysicalQuantity
// =====================================================================
Opm::ECLUnits::Convert::PhysicalQuantity::PhysicalQuantity()
: pImpl_(new Impl{})
{}
Opm::ECLUnits::Convert::PhysicalQuantity::~PhysicalQuantity()
{}
Opm::ECLUnits::Convert::PhysicalQuantity::
PhysicalQuantity(const PhysicalQuantity& rhs)
: pImpl_(new Impl(*rhs.pImpl_))
{}
Opm::ECLUnits::Convert::PhysicalQuantity::
PhysicalQuantity(PhysicalQuantity&& rhs)
: pImpl_(std::move(rhs.pImpl_))
{}
Opm::ECLUnits::Convert::PhysicalQuantity&
Opm::ECLUnits::Convert::PhysicalQuantity::operator=(const PhysicalQuantity& rhs)
{
this->pImpl_.reset(new Impl(*rhs.pImpl_));
return *this;
}
Opm::ECLUnits::Convert::PhysicalQuantity&
Opm::ECLUnits::Convert::PhysicalQuantity::operator=(PhysicalQuantity&& rhs)
{
this->pImpl_ = std::move(rhs.pImpl_);
return *this;
}
Opm::ECLUnits::Convert::PhysicalQuantity&
Opm::ECLUnits::Convert::PhysicalQuantity::from(const UnitSystem& usys)
{
this->pImpl_->from(usys);
return *this;
}
Opm::ECLUnits::Convert::PhysicalQuantity&
Opm::ECLUnits::Convert::PhysicalQuantity::to(const UnitSystem& usys)
{
this->pImpl_->to(usys);
return *this;
}
const Opm::ECLUnits::UnitSystem*
Opm::ECLUnits::Convert::PhysicalQuantity::from() const
{
return this->pImpl_->from();
}
const Opm::ECLUnits::UnitSystem*
Opm::ECLUnits::Convert::PhysicalQuantity::to() const
{
return this->pImpl_->to();
}
// =====================================================================
namespace {
void rescaleVector(const double factor, std::vector<double>& x)
{
for (auto& xi : x) {
xi *= factor;
}
}
void validateUnitConventions(const std::string& name,
const Opm::ECLUnits::UnitSystem* from,
const Opm::ECLUnits::UnitSystem* to)
{
if (from == nullptr) {
throw std::invalid_argument {
"Cannot Perform " + name + " Value Unit Conversion "
"Without Known 'From' Unit System Convention"
};
}
if (to == nullptr) {
throw std::invalid_argument {
"Cannot Perform " + name + " Value Unit Conversion "
"Without Known 'To' Unit System Convention"
};
}
}
double calculateScaleFactor(const double from, const double to)
{
using namespace ::Opm::unit;
// "return from / to", essentially.
return convert::to(convert::from(1.0, from), to);
}
} // namespace Anonymous
// =====================================================================
// class Convert::Pressure
// =====================================================================
void
Opm::ECLUnits::Convert::Pressure::appliedTo(std::vector<double>& press) const
{
const auto* from = this->from();
const auto* to = this->to();
validateUnitConventions("Pressure", from, to);
const auto scaling =
calculateScaleFactor(from->pressure(), to->pressure());
rescaleVector(scaling, press);
}
// =====================================================================
// class Convert::Viscosity
// =====================================================================
void
Opm::ECLUnits::Convert::Viscosity::appliedTo(std::vector<double>& mu) const
{
const auto* from = this->from();
const auto* to = this->to();
validateUnitConventions("Viscosity", from, to);
const auto scaling =
calculateScaleFactor(from->viscosity(), to->viscosity());
rescaleVector(scaling, mu);
}
// =====================================================================
// class Convert::GasFVF
// =====================================================================
void
Opm::ECLUnits::Convert::GasFVF::appliedTo(std::vector<double>& Bg) const
{
const auto* from = this->from();
const auto* to = this->to();
validateUnitConventions("GasFVF", from, to);
const auto scaling =
calculateScaleFactor(from->reservoirVolume() / from->surfaceVolumeGas(),
to ->reservoirVolume() / to ->surfaceVolumeGas());
rescaleVector(scaling, Bg);
}
// =====================================================================
// class Convert::OilFVF
// =====================================================================
void
Opm::ECLUnits::Convert::OilFVF::appliedTo(std::vector<double>& Bo) const
{
const auto* from = this->from();
const auto* to = this->to();
validateUnitConventions("OilFVF", from, to);
const auto scaling =
calculateScaleFactor(from->reservoirVolume() / from->surfaceVolumeLiquid(),
to ->reservoirVolume() / to ->surfaceVolumeLiquid());
rescaleVector(scaling, Bo);
}
// =====================================================================
// class Convert::DissolvedGasOilRatio
// =====================================================================
void
Opm::ECLUnits::Convert::DissolvedGasOilRatio::
appliedTo(std::vector<double>& Rs) const
{
const auto* from = this->from();
const auto* to = this->to();
validateUnitConventions("DissolvedGasOilRatio", from, to);
const auto scaling =
calculateScaleFactor(from->dissolvedGasOilRat(),
to ->dissolvedGasOilRat());
rescaleVector(scaling, Rs);
}
// =====================================================================
// class Convert::VaporisedOilGasRatio
// =====================================================================
void
Opm::ECLUnits::Convert::VaporisedOilGasRatio::
appliedTo(std::vector<double>& Rv) const
{
const auto* from = this->from();
const auto* to = this->to();
validateUnitConventions("VaporisedOilGasRatio", from, to);
const auto scaling =
calculateScaleFactor(from->vaporisedOilGasRat(),
to ->vaporisedOilGasRat());
rescaleVector(scaling, Rv);
}

View File

@ -0,0 +1,467 @@
/*
Copyright 2017 Statoil ASA.
This file is part of the Open Porous Media Project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef OPM_ECLPROPERTYUNITCONVERSION_HEADER_INCLUDED
#define OPM_ECLPROPERTYUNITCONVERSION_HEADER_INCLUDED
#include <memory>
#include <vector>
namespace Opm {
class ECLRestartData;
class ECLInitFileData;
namespace ECLUnits {
struct UnitSystem;
/// Construct a Unit System object corresponding to the main grid's
/// unit convention of a Restart result set.
///
/// \param[in] rstrt ECL Restart data set from which to extract unit
/// system convention (e.g., METRIC or FIELD).
///
/// \return Unit System object corresponding to the main grid's
/// system convention of the currently selected restart step.
std::unique_ptr<const UnitSystem>
serialisedUnitConventions(const ECLRestartData& rstrt);
/// Construct a Unit System object corresponding to the main grid's
/// unit convention of an INIT file result set.
///
/// \param[in] init ECL INIT file data set from which to extract
/// unit system convention (e.g., METRIC or FIELD).
///
/// \return Unit System object corresponding to the main grid's
/// system convention of the INIT file result set.
std::unique_ptr<const UnitSystem>
serialisedUnitConventions(const ECLInitFileData& init);
/// Construct a Unit System object corresponding to the module's
/// internal unit system convention (strict SI).
///
/// \return Unit System object corresponding to the module's
/// internal unit system convention (i.e., strict SI).
std::unique_ptr<const UnitSystem> internalUnitConventions();
/// Construct a Unit System object corresponding to ECLIPSE's
/// "METRIC" unit system convention.
///
/// \return Unit System object corresponding to ECLIPSE's
/// "METRIC" unit system convention.
std::unique_ptr<const UnitSystem> metricUnitConventions();
/// Construct a Unit System object corresponding to ECLIPSE's
/// "FIELD" unit system convention.
///
/// \return Unit System object corresponding to ECLIPSE's
/// "FIELD" unit system convention.
std::unique_ptr<const UnitSystem> fieldUnitConventions();
/// Construct a Unit System object corresponding to ECLIPSE's
/// "LAB" unit system convention.
///
/// \return Unit System object corresponding to ECLIPSE's
/// "LAB" unit system convention.
std::unique_ptr<const UnitSystem> labUnitConventions();
/// Construct a Unit System object corresponding to ECLIPSE's
/// "PVT-M" unit system convention.
///
/// \return Unit System object corresponding to ECLIPSE's
/// "PVT-M" unit system convention.
std::unique_ptr<const UnitSystem> pvtmUnitConventions();
/// Facility for converting the units of measure of selected
/// physcial quanties between user-specified systems of unit
/// convention.
struct Convert
{
/// Representation of a physical quantity
///
/// Base class. Resource management and operations common to
/// all particular/specific physical quantities.
class PhysicalQuantity
{
public:
/// Default constructor.
PhysicalQuantity();
/// Destructor.
virtual ~PhysicalQuantity();
/// Copy constructor.
///
/// \param[in] rhs Existing physical quantity.
PhysicalQuantity(const PhysicalQuantity& rhs);
/// Move constructor.
///
/// Subsumes the implementation of an existing Physical
/// Quantity.
///
/// \param[in] rhs Existing physical quantity. Does not
/// have a valid implementation when the constructor
/// completes.
PhysicalQuantity(PhysicalQuantity&& rhs);
/// Assignment operator
///
/// \param[in] rhs Existing Physical Quantity.
///
/// \return \code *this \endcode.
PhysicalQuantity& operator=(const PhysicalQuantity& rhs);
/// Move assignment operator.
///
/// Subsumes the implementation of an existing object.
///
/// \param[in] rhs Existing Physical Quantity. Does not
/// have a valid implementation when the assignment
/// completes.
///
/// \return \code *this \endcode.
PhysicalQuantity& operator=(PhysicalQuantity&& rhs);
/// Specify collection of units of measure of the inputs.
///
/// \param[in] usys Collection of units of measure of inputs.
///
/// \return \code *this \endcode.
PhysicalQuantity& from(const UnitSystem& usys);
/// Specify collection of units of measure of the output.
///
/// \param[in] usys Collection of units of measure of outputs.
///
/// \return \code *this \endcode.
PhysicalQuantity& to(const UnitSystem& usys);
/// Convert a physical quantity from its input unit of
/// measure to its output unit of measure.
///
/// Must be overridden in derived classes.
///
/// \param[in,out] x On input, a sequence of values of a
/// physical quantities assumed to be defined in the
/// input unit specified through \code from(usys)
/// \endcode. On output, the same seqeuence of values
/// converted to the output unit of measure specified
/// through \code to(usys) \endcode.
virtual void appliedTo(std::vector<double>& x) const = 0;
protected:
/// Retrieve input unit system.
///
/// Exists for the benefit of derived classes.
///
/// \return Input unit system. Null if not specified by
/// caller.
const UnitSystem* from() const;
/// Retrieve output unit system.
///
/// Exists for the benefit of derived classes.
///
/// \return Output unit system. Null if not specified by
/// caller.
const UnitSystem* to() const;
private:
/// Implementation class.
class Impl;
/// Pointer to implementation.
std::unique_ptr<Impl> pImpl_;
};
/// Facility for converting pressure values between
/// user-selected units of measure.
class Pressure : public PhysicalQuantity {
public:
/// Convert a sequence of pressure values from its input
/// unit of measure to its output unit of measure.
///
/// Will throw an exception of type \code
/// std::invalid_argument \endcode unless both of the input
/// and output unit system conventions have been previously
/// specified.
///
/// Example: Convert LGR-1's oil pressure values on restart
/// step 123 from serialised format on disk (unified
/// restart) to internal units of measure (SI).
/// \code
/// const auto rstrt = ECLRestartData{ "Case.UNRST" };
/// const auto native = serialisedUnitConventions(rstrt);
/// const auto si = internalUnitConvention();
///
/// rstrt.selectReportStep(123);
/// auto press = rstrt.keywordData<double>("PRESSURE", "LGR-1");
/// Convert::Pressure().from(*native).to(*si).appliedTo(press);
/// \endcode
///
/// \param[in,out] press On input, a sequence of pressure
/// values assumed to be defined in the input unit
/// specified through \code from(usys) \endcode. On
/// output, the same seqeuence of pressure values
/// converted to the output unit of measure specified
/// through \code to(usys) \endcode.
void appliedTo(std::vector<double>& press) const override;
};
/// Facility for converting viscosity values between
/// user-selected units of measure.
class Viscosity : public PhysicalQuantity {
public:
/// Convert a sequence of fluid phase viscosity values from
/// its input unit of measure to its output unit of measure.
///
/// Will throw an exception of type \code
/// std::invalid_argument \endcode unless both of the input
/// and output unit system conventions have been previously
/// specified.
///
/// Example: Compute the dynamic gas viscosity value in cell
/// 27182 on restart step 10 and report it in LAB units.
/// \code
/// const auto rset = ResultSet("Case.EGRID");
/// const auto init = ECLInitFileData{ rset.initFile() };
/// const auto G = ECLGraph.load(rset.gridFile(), init);
/// const auto rstrt = ECLRestartData{ rset.restartFile(10) };
/// const auto si = internalUnitConvention();
/// const auto lab = labUnitConvention();
///
/// rstrt.selectReportStep(10);
/// const auto press = G.linearisedCellData<double>
/// (rstrt, "PRESSURE", &UnitSystem::pressure);
///
/// const auto rv = G.linearisedCellData<double>
/// (rstrt, "RV", &UnitSystem::vaporisedOilGasRat);
///
/// const auto pvtCC = ECLPvtCurveCollection(G, init);
///
/// auto mu = pvtCC.getDynamicProperty(RawCurve::Viscosity,
/// ECLPhaseIndex::Vapour, 27182,
/// std::vector<double>{ press[27182] },
/// std::vector<double>{ rv.empty() ? 0.0 : rv[27182] });
///
/// Convert::Viscosity().to(*lab).from(*si).appliedTo(mu);
/// \endcode
///
/// \param[in,out] mu On input, a sequence of viscosity
/// values assumed to be defined in the input unit
/// specified through \code from(usys) \endcode. On
/// output, the same seqeuence of viscosity values
/// converted to the output unit of measure specified
/// through \code to(usys) \endcode.
void appliedTo(std::vector<double>& mu) const override;
};
/// Facility for converting gas phase formation volume factor
/// values between user-selected units of measure.
class GasFVF : public PhysicalQuantity {
public:
/// Convert a sequence of formation volume factor values for
/// the gas/vapour phase from its input unit of measure to
/// its output unit of measure.
///
/// Will throw an exception of type \code
/// std::invalid_argument \endcode unless both of the input
/// and output unit system conventions have been previously
/// specified.
///
/// Example: Compute the dynamic gas formation volume factor
/// value in cell 57721 on restart step 314 and report it
/// in Field units.
/// \code
/// const auto rset = ResultSet("Case.EGRID");
/// const auto init = ECLInitFileData{ rset.initFile() };
/// const auto G = ECLGraph.load(rset.gridFile(), init);
/// const auto rstrt = ECLRestartData{ rset.restartFile(10) };
/// const auto si = internalUnitConvention();
/// const auto field = fieldUnitConvention();
///
/// rstrt.selectReportStep(314);
/// const auto press = G.linearisedCellData<double>
/// (rstrt, "PRESSURE", &UnitSystem::pressure);
///
/// const auto rv = G.linearisedCellData<double>
/// (rstrt, "RV", &UnitSystem::vaporisedOilGasRat);
///
/// const auto pvtCC = ECLPvtCurveCollection(G, init);
///
/// auto Bg = pvtCC.getDynamicProperty(RawCurve::FVF,
/// ECLPhaseIndex::Vapour, 57721,
/// std::vector<double>{ press[57721] },
/// std::vector<double>{ rv.empty() ? 0.0 : rv[57721] });
///
/// Convert::GasFVF().to(*field).from(*si).appliedTo(Bg);
/// \endcode
///
/// \param[in,out] Bg On input, a sequence of formation
/// volume factor values for the gas phase assumed to be
/// defined in the input unit specified through \code
/// from(usys) \endcode. On output, the same seqeuence
/// of gas FVF values converted to the output unit of
/// measure specified through \code to(usys) \endcode.
void appliedTo(std::vector<double>& Bg) const override;
};
/// Facility for converting oil phase formation volume factor
/// values between user-selected units of measure.
class OilFVF : public PhysicalQuantity {
public:
/// Convert a sequence of formation volume factor values for
/// the oil/liquid phase from its input unit of measure to
/// its output unit of measure.
///
/// Will throw an exception of type \code
/// std::invalid_argument \endcode unless both of the input
/// and output unit system conventions have been previously
/// specified.
///
/// Example: Extract the dynamic oil formation volume factor
/// curve in cell 355,113 in native units (convention of
/// serialised result set).
/// \code
/// const auto rset = ResultSet("Case.EGRID");
/// const auto init = ECLInitFileData{ rset.initFile() };
/// const auto G = ECLGraph.load(rset.gridFile(), init);
/// const auto si = internalUnitConvention();
/// const auto native = serialisedUnitConventions(init);
///
/// const auto pvtCC = ECLPvtCurveCollection(G, init);
///
/// auto BoCurves = pvtCC.getPvtCurve(RawCurve::FVF,
/// ECLPhaseIndex::Liquid, 355113);
///
/// const auto cvrtPress =
/// Convert::Pressure().to(*native).from(*si);
/// const auto cvrtBo =
/// Convert::OilFVF().to(*native).from(*si);
///
/// for (auto& curve : BoCurves) {
/// cvrtPress.appliedTo(curve.first);
/// cvrtBo .appliedTo(curve.second);
/// }
/// \endcode
///
/// \param[in,out] Bo On input, a sequence of formation
/// volume factor values for the oil phase assumed to be
/// defined in the input unit specified through \code
/// from(usys) \endcode. On output, the same seqeuence
/// of oil FVF values converted to the output unit of
/// measure specified through \code to(usys) \endcode.
void appliedTo(std::vector<double>& Bo) const override;
};
/// Facility for converting dissolved gas/oil ratio (Rs) values
/// between user-selected units of measure.
class DissolvedGasOilRatio : public PhysicalQuantity {
public:
/// Convert a sequence of dissolved gas/oil ratio values
/// from its input unit of measure to its output unit of
/// measure.
///
/// Will throw an exception of type \code
/// std::invalid_argument \endcode unless both of the input
/// and output unit system conventions have been previously
/// specified.
///
/// Example: Extract the saturated state curve for oil in
/// cell 14142 in PVT-M units.
/// \code
/// const auto rset = ResultSet("Case.EGRID");
/// const auto init = ECLInitFileData{ rset.initFile() };
/// const auto G = ECLGraph.load(rset.gridFile(), init);
/// const auto si = internalUnitConvention();
/// const auto pvt_m = pvtmUnitConventions();
///
/// const auto pvtCC = ECLPvtCurveCollection(G, init);
///
/// const auto satState =
/// pvtCC.getPvtCurve(RawCurve::SaturatedState,
/// ECLPhaseIndex::Liquid, 14142);
///
/// Convert::DissolvedGasOilRatio().to(*pvt_m).from(*si)
/// .appliedTo(satState.first);
///
/// Convert::Pressure().to(*pvt_m).from(*si)
/// .appliedTo(satState.second);
/// \endcode
///
/// \param[in,out] Rs On input, a sequence of dissolved
/// gas/oil ratio values assumed to be defined in the
/// input unit specified through \code from(usys)
/// \endcode. On output, the same seqeuence of oil Rs
/// values converted to the output unit of measure
/// specified through \code to(usys) \endcode.
void appliedTo(std::vector<double>& Rs) const override;
};
/// Facility for converting vaporised oil/gas ratio (Rv) values
/// between user-selected units of measure.
class VaporisedOilGasRatio : public PhysicalQuantity {
public:
/// Convert a sequence of vaporised oil/gas ratio values
/// from its input unit of measure to its output unit of
/// measure.
///
/// Will throw an exception of type \code
/// std::invalid_argument \endcode unless both of the input
/// and output unit system conventions have been previously
/// specified.
///
/// Example: Extract the saturated state curve for gas in
/// cell 161803 in Metric units.
/// \code
/// const auto rset = ResultSet("Case.EGRID");
/// const auto init = ECLInitFileData{ rset.initFile() };
/// const auto G = ECLGraph.load(rset.gridFile(), init);
/// const auto si = internalUnitConvention();
/// const auto metric = metricUnitConventions();
///
/// const auto pvtCC = ECLPvtCurveCollection(G, init);
///
/// const auto satState =
/// pvtCC.getPvtCurve(RawCurve::SaturatedState,
/// ECLPhaseIndex::Vapour, 161803);
///
/// Convert::Pressure().to(*metric).from(*si)
/// .appliedTo(satState.first);
///
/// Convert::VaporisedOilGasRatio().to(*metric).from(*si)
/// .appliedTo(satState.second);
/// \endcode
///
/// \param[in,out] Rv On input, a sequence of vapourised
/// oil/gas ratio values assumed to be defined in the
/// input unit specified through \code from(usys)
/// \endcode. On output, the same seqeuence of gas Rv
/// values converted to the output unit of measure
/// specified through \code to(usys) \endcode.
void appliedTo(std::vector<double>& Rv) const override;
};
};
} // namespace ECLUnits
} // namespace Opm
#endif // OPM_ECLPROPERTYUNITCONVERSION_HEADER_INCLUDED

View File

@ -265,6 +265,11 @@ Opm::ECLPVT::PVDx::viscosity(const std::vector<double>& p) const
Opm::FlowDiagnostics::Graph Opm::FlowDiagnostics::Graph
Opm::ECLPVT::PVDx::getPvtCurve(const RawCurve curve) const Opm::ECLPVT::PVDx::getPvtCurve(const RawCurve curve) const
{ {
if (curve == RawCurve::SaturatedState) {
// Not applicable to dry gas or dead oil. Return empty.
return FlowDiagnostics::Graph{};
}
return extractRawPVTCurve(this->interp_, curve); return extractRawPVTCurve(this->interp_, curve);
} }

View File

@ -669,28 +669,11 @@ namespace Opm { namespace ECLPVT {
std::vector<FlowDiagnostics::Graph> std::vector<FlowDiagnostics::Graph>
getPvtCurve(const RawCurve curve) const getPvtCurve(const RawCurve curve) const
{ {
auto ret = std::vector<FlowDiagnostics::Graph>{}; if (curve == RawCurve::SaturatedState) {
ret.reserve(this->propInterp_.size()); return this->saturatedStateCurve();
for (const auto& interp : this->propInterp_) {
ret.push_back(extractRawPVTCurve(interp, curve));
} }
return ret; return this->mainPvtCurve(curve);
}
std::vector<double> getSaturatedPoints() const
{
auto y = std::vector<double>{};
y.reserve(this->propInterp_.size());
for (const auto& interp : this->propInterp_) {
const auto& yi = interp.independentVariable();
y.push_back(yi[0]);
}
return y;
} }
private: private:
@ -844,6 +827,37 @@ namespace Opm { namespace ECLPVT {
return result; return result;
} }
std::vector<FlowDiagnostics::Graph>
mainPvtCurve(const RawCurve curve) const
{
auto ret = std::vector<FlowDiagnostics::Graph>{};
ret.reserve(this->propInterp_.size());
for (const auto& interp : this->propInterp_) {
ret.push_back(extractRawPVTCurve(interp, curve));
}
return ret;
}
std::vector<FlowDiagnostics::Graph> saturatedStateCurve() const
{
auto y = std::vector<double>{};
y.reserve(this->propInterp_.size());
for (const auto& interp : this->propInterp_) {
const auto& yi = interp.independentVariable();
y.push_back(yi[0]);
}
return {
FlowDiagnostics::Graph {
this->key_, std::move(y)
}
};
}
}; };
/// Extract component mass density at surface conditions. /// Extract component mass density at surface conditions.

View File

@ -19,8 +19,10 @@
#include <opm/utility/ECLPvtCurveCollection.hpp> #include <opm/utility/ECLPvtCurveCollection.hpp>
#include <opm/utility/ECLPropertyUnitConversion.hpp>
#include <opm/utility/ECLResultData.hpp> #include <opm/utility/ECLResultData.hpp>
#include <cassert>
#include <initializer_list> #include <initializer_list>
#include <vector> #include <vector>
@ -42,61 +44,252 @@ namespace {
return pvtnum; return pvtnum;
} }
std::unique_ptr<const Opm::ECLUnits::UnitSystem>
createUnitSystem(const ::Opm::ECLInitFileData& init)
{
const auto& ih = init.keywordData<int>(INTEHEAD_KW);
return ::Opm::ECLUnits::createUnitSystem(ih[ INTEHEAD_UNIT_INDEX ]);
}
std::vector<Opm::FlowDiagnostics::Graph> emptyFDGraph() std::vector<Opm::FlowDiagnostics::Graph> emptyFDGraph()
{ {
return { Opm::FlowDiagnostics::Graph{} }; return { Opm::FlowDiagnostics::Graph{} };
} }
template <class PvtPtr> template <class PVTInterp>
std::vector<Opm::FlowDiagnostics::Graph> std::vector<Opm::FlowDiagnostics::Graph>
rawPvtCurve(PvtPtr& pvt, // ref to shared_ptr<> rawPvtCurve(const PVTInterp* pvt,
const Opm::ECLPVT::RawCurve curve, const Opm::ECLPVT::RawCurve curve,
const int regID, const int regID)
const Opm::ECLUnits::UnitSystem& usys)
{ {
if (pvt != nullptr) { if (pvt == nullptr) {
return pvt->getPvtCurve(curve, regID, usys); // Result set does not provide requisite tabulated properties.
// Return empty.
return emptyFDGraph();
} }
// Result set does not provide requisite tabulated properties. return pvt->getPvtCurve(curve, regID);
// Return empty. }
return emptyFDGraph();
template <class PVTInterp, class Pressure, class MixRatio>
std::vector<double>
formationVolumeFactor(const PVTInterp* pvt,
const int regID,
const Pressure& press,
const MixRatio& R)
{
assert (pvt != nullptr);
return pvt->formationVolumeFactor(regID, R, press);
}
template <class PVTInterp, class Pressure, class MixRatio>
std::vector<double>
viscosity(const PVTInterp* pvt,
const int regID,
const Pressure& press,
const MixRatio& R)
{
assert (pvt != nullptr);
return pvt->viscosity(regID, R, press);
}
std::vector<double>
gasProperty(const Opm::ECLPVT::Gas* pvt,
const Opm::ECLPVT::RawCurve property,
const int regID,
const std::vector<double>& Pg,
const std::vector<double>& Rv)
{
if (pvt == nullptr) {
// No such property interpolant. Return empty.
return {};
}
assert ((property == Opm::ECLPVT::RawCurve::FVF) ||
(property == Opm::ECLPVT::RawCurve::Viscosity));
const auto pg = Opm::ECLPVT::Gas::GasPressure { Pg };
auto rv = Opm::ECLPVT::Gas::VaporizedOil{ Rv };
if (rv.data.empty()) {
rv.data.assign(pg.data.size(), 0.0);
}
if (property == Opm::ECLPVT::RawCurve::FVF) {
return formationVolumeFactor(pvt, regID, pg, rv);
}
return viscosity(pvt, regID, pg, rv);
}
std::vector<double>
oilProperty(const Opm::ECLPVT::Oil* pvt,
const Opm::ECLPVT::RawCurve property,
const int regID,
const std::vector<double>& Po,
const std::vector<double>& Rs)
{
if (pvt == nullptr) {
// No such property interpolant. Return empty.
return {};
}
assert ((property == Opm::ECLPVT::RawCurve::FVF) ||
(property == Opm::ECLPVT::RawCurve::Viscosity));
const auto po = Opm::ECLPVT::Oil::OilPressure { Po };
auto rs = Opm::ECLPVT::Oil::DissolvedGas{ Rs };
if (rs.data.empty()) {
rs.data.assign(po.data.size(), 0.0);
}
if (property == Opm::ECLPVT::RawCurve::FVF) {
return formationVolumeFactor(pvt, regID, po, rs);
}
return viscosity(pvt, regID, po, rs);
}
std::vector<Opm::FlowDiagnostics::Graph>
convertCurve(std::vector<Opm::FlowDiagnostics::Graph>&& curves,
const Opm::ECLUnits::Convert::PhysicalQuantity& cvrt_x,
const Opm::ECLUnits::Convert::PhysicalQuantity& cvrt_y)
{
for (auto& curve : curves) {
cvrt_x.appliedTo(curve.first);
cvrt_y.appliedTo(curve.second);
}
return curves;
}
std::vector<Opm::FlowDiagnostics::Graph>
convertFvfCurve(std::vector<Opm::FlowDiagnostics::Graph>&& curve,
const Opm::ECLPhaseIndex phase,
const Opm::ECLUnits::UnitSystem& usysFrom,
const Opm::ECLUnits::UnitSystem& usysTo)
{
assert ((phase == Opm::ECLPhaseIndex::Liquid) ||
(phase == Opm::ECLPhaseIndex::Vapour));
if (phase == Opm::ECLPhaseIndex::Liquid) {
// Oil FVF. First column is pressure, second column is Bo.
const auto& cvrt_x = Opm::ECLUnits::Convert::Pressure()
.from(usysFrom).to(usysTo);
const auto& cvrt_y = Opm::ECLUnits::Convert::OilFVF()
.from(usysFrom).to(usysTo);
return convertCurve(std::move(curve), cvrt_x, cvrt_y);
}
// Gas FVF. Need to distinguish miscible from immiscible cases. In
// the former, the first column is Rv (vapourised oil/gas ratio) and
// in the second case the first column is the gas pressure.
//
// Immiscible case identified by curve.size() <= 1.
const auto& cvrt_y = Opm::ECLUnits::Convert::GasFVF()
.from(usysFrom).to(usysTo);
if (curve.size() <= 1) {
// Immiscible Gas FVF. First column is Pg.
const auto& cvrt_x = Opm::ECLUnits::Convert::Pressure()
.from(usysFrom).to(usysTo);
return convertCurve(std::move(curve), cvrt_x, cvrt_y);
}
// Miscible Gas FVF. First column is Rv.
const auto& cvrt_x = Opm::ECLUnits::Convert::VaporisedOilGasRatio()
.from(usysFrom).to(usysTo);
return convertCurve(std::move(curve), cvrt_x, cvrt_y);
}
std::vector<Opm::FlowDiagnostics::Graph>
convertViscosityCurve(std::vector<Opm::FlowDiagnostics::Graph>&& curve,
const Opm::ECLPhaseIndex phase,
const Opm::ECLUnits::UnitSystem& usysFrom,
const Opm::ECLUnits::UnitSystem& usysTo)
{
assert ((phase == Opm::ECLPhaseIndex::Liquid) ||
(phase == Opm::ECLPhaseIndex::Vapour));
// This is the viscosity curve. Second column is always viscosity
// irrespective of phase or miscible/immiscible fluids.
const auto& cvrt_y = Opm::ECLUnits::Convert::Viscosity()
.from(usysFrom).to(usysTo);
if ((phase == Opm::ECLPhaseIndex::Liquid) || (curve.size() <= 1)) {
// Graph is oil viscosity or immiscible gas viscosity. First
// column is pressure.
const auto& cvrt_x = Opm::ECLUnits::Convert::Pressure()
.from(usysFrom).to(usysTo);
return convertCurve(std::move(curve), cvrt_x, cvrt_y);
}
// Miscible Gas viscosity. First column is Rv (vapourised oil/gas
// ratio).
const auto& cvrt_x = Opm::ECLUnits::Convert::VaporisedOilGasRatio()
.from(usysFrom).to(usysTo);
return convertCurve(std::move(curve), cvrt_x, cvrt_y);
}
std::vector<Opm::FlowDiagnostics::Graph>
convertSatStateCurve(std::vector<Opm::FlowDiagnostics::Graph>&& curve,
const Opm::ECLPhaseIndex phase,
const Opm::ECLUnits::UnitSystem& usysFrom,
const Opm::ECLUnits::UnitSystem& usysTo)
{
assert ((phase == Opm::ECLPhaseIndex::Liquid) ||
(phase == Opm::ECLPhaseIndex::Vapour));
// First column is pressure (Po or Pg).
const auto& cvrt_x = Opm::ECLUnits::Convert::Pressure()
.from(usysFrom).to(usysTo);
// Second column is Rs or Rv depending on 'phase'.
if (phase == Opm::ECLPhaseIndex::Liquid) {
// Saturated state curve for miscible oil. Second column is Rs
// (dissolved gas/oil ratio).
const auto& cvrt_y = Opm::ECLUnits::Convert::
DissolvedGasOilRatio().from(usysFrom).to(usysTo);
return convertCurve(std::move(curve), cvrt_x, cvrt_y);
}
// Saturated state curve for miscible gas. Second column is Rv
// (vapourised oil/gas ratio).
const auto& cvrt_y = Opm::ECLUnits::Convert::
VaporisedOilGasRatio().from(usysFrom).to(usysTo);
return convertCurve(std::move(curve), cvrt_x, cvrt_y);
} }
} }
Opm::ECLPVT::ECLPvtCurveCollection:: Opm::ECLPVT::ECLPvtCurveCollection::
ECLPvtCurveCollection(const ECLGraph& G, ECLPvtCurveCollection(const ECLGraph& G,
const ECLInitFileData& init) const ECLInitFileData& init)
: pvtnum_(pvtnumVector(G, init)) : pvtnum_ (pvtnumVector(G, init))
, gas_ (CreateGasPVTInterpolant::fromECLOutput(init)) // u_p<> -> s_p<> , gas_ (CreateGasPVTInterpolant::fromECLOutput(init))
, oil_ (CreateOilPVTInterpolant::fromECLOutput(init)) // u_p<> -> s_p<> , oil_ (CreateOilPVTInterpolant::fromECLOutput(init))
, usys_ (createUnitSystem(init)) // u_p<> -> s_p<> , usys_native_ (ECLUnits::serialisedUnitConventions(init))
, usys_internal_(ECLUnits::internalUnitConventions())
{} {}
void
Opm::ECLPVT::ECLPvtCurveCollection::
setOutputUnits(std::unique_ptr<const ECLUnits::UnitSystem> usys)
{
this->usys_output_ = std::move(usys);
}
std::vector<Opm::FlowDiagnostics::Graph> std::vector<Opm::FlowDiagnostics::Graph>
Opm::ECLPVT::ECLPvtCurveCollection:: Opm::ECLPVT::ECLPvtCurveCollection::
getPvtCurve(const RawCurve curve, getPvtCurve(const RawCurve curve,
const ECLPhaseIndex phase, const ECLPhaseIndex phase,
const int activeCell) const const int activeCell) const
{ {
if (phase == ECLPhaseIndex::Aqua) { if (! this->isValidRequest(phase, activeCell)) {
// Not supported at this time. // Not a supported phase or cell index out of bounds. Not a valid
// Return empty. // request so return empty.
return emptyFDGraph();
}
if (static_cast<decltype(this->pvtnum_.size())>(activeCell)
>= this->pvtnum_.size())
{
// Active cell index out of bounds. Return empty.
return emptyFDGraph(); return emptyFDGraph();
} }
@ -106,9 +299,180 @@ getPvtCurve(const RawCurve curve,
if (phase == ECLPhaseIndex::Liquid) { if (phase == ECLPhaseIndex::Liquid) {
// Caller requests oil properties. // Caller requests oil properties.
return rawPvtCurve(this->oil_, curve, regID, *this->usys_); return this->convertToOutputUnits(
rawPvtCurve(this->oil_.get(), curve, regID), curve, phase);
} }
// Call requests gas properties. // Caller requests gas properties.
return rawPvtCurve(this->gas_, curve, regID, *this->usys_); assert ((phase == ECLPhaseIndex::Vapour) &&
"Internal Logic Error Identifying Supported Phases");
return this->convertToOutputUnits(
rawPvtCurve(this->gas_.get(), curve, regID), curve, phase);
}
std::vector<double>
Opm::ECLPVT::ECLPvtCurveCollection::
getDynamicPropertySI(const RawCurve property,
const ECLPhaseIndex phase,
const int activeCell,
const std::vector<double>& phasePress,
const std::vector<double>& mixRatio) const
{
if (! this->isValidRequest(phase, activeCell) ||
(property == RawCurve::SaturatedState))
{
// Not a supported phase, cell index out of bounds, or caller
// requests dynamically evaluating the saturated state curve. Not a
// valid request so return empty.
return {};
}
// PVTNUM is traditional one-based region identifier. Subtract one to
// form valid index into std::vector<>s.
const auto regID = this->pvtnum_[activeCell] - 1;
if (phase == ECLPhaseIndex::Liquid) {
// Caller requests oil properties.
return oilProperty(this->oil_.get(), property,
regID, phasePress, mixRatio);
}
// Caller requests gas properties.
assert ((phase == ECLPhaseIndex::Vapour) &&
"Internal Logic Error Identifying Supported Phases");
return gasProperty(this->gas_.get(), property,
regID, phasePress, mixRatio);
}
std::vector<double>
Opm::ECLPVT::ECLPvtCurveCollection::
getDynamicPropertyNative(const RawCurve property,
const ECLPhaseIndex phase,
const int activeCell,
std::vector<double> phasePress,
std::vector<double> mixRatio) const
{
if (! this->isValidRequest(phase, activeCell) ||
(property == RawCurve::SaturatedState))
{
// Not a supported phase, cell index out of bounds, or caller
// requests dynamically evaluating the saturated state curve. Not a
// valid request so return empty.
return {};
}
assert (this->usys_native_ != nullptr);
assert (this->usys_internal_ != nullptr);
// 1) Convert inputs from native to internal (SI) units of measurement.
::Opm::ECLUnits::Convert::Pressure()
.from(*this->usys_native_)
.to (*this->usys_internal_).appliedTo(phasePress);
if (phase == ECLPhaseIndex::Liquid) {
::Opm::ECLUnits::Convert::DissolvedGasOilRatio()
.from(*this->usys_native_)
.to (*this->usys_internal_).appliedTo(mixRatio);
}
else {
assert (phase == ECLPhaseIndex::Vapour);
::Opm::ECLUnits::Convert::VaporisedOilGasRatio()
.from(*this->usys_native_)
.to (*this->usys_internal_).appliedTo(mixRatio);
}
// 2) Evaluate requested property in strict SI units.
auto prop = this->getDynamicPropertySI(property, phase, activeCell,
phasePress, mixRatio);
// 3) Convert property values to user's requested system of units.
if (this->usys_output_ == nullptr) {
// No user-defined system of units for outputs. Use PropertySI()
// directly.
return prop;
}
// Caller has defined a particular system of units for outputs. Convert
// 'prop' accordingly.
if (property == RawCurve::Viscosity) {
// The 'prop' values represent viscosities.
::Opm::ECLUnits::Convert::Viscosity()
.from(*this->usys_internal_)
.to (*this->usys_output_).appliedTo(prop);
}
else {
assert (property == RawCurve::FVF);
if (phase == ECLPhaseIndex::Vapour) {
// The 'prop' values represent Bg.
::Opm::ECLUnits::Convert::GasFVF()
.from(*this->usys_internal_)
.to (*this->usys_output_).appliedTo(prop);
}
else {
assert (phase == ECLPhaseIndex::Liquid);
::Opm::ECLUnits::Convert::OilFVF()
.from(*this->usys_internal_)
.to (*this->usys_output_).appliedTo(prop);
}
}
return prop;
}
bool
Opm::ECLPVT::ECLPvtCurveCollection::
isValidRequest(const ECLPhaseIndex phase,
const int activeCell) const
{
if (! ((phase == ECLPhaseIndex::Liquid) ||
(phase == ECLPhaseIndex::Vapour)))
{
// We support "liquid" and "vapour" phase (oil/gas) properties only.
return false;
}
// Check if cell index is within bounds.
return static_cast<decltype(this->pvtnum_.size())>(activeCell)
< this->pvtnum_.size();
}
std::vector<Opm::FlowDiagnostics::Graph>
Opm::ECLPVT::ECLPvtCurveCollection::
convertToOutputUnits(std::vector<FlowDiagnostics::Graph>&& graph,
const RawCurve curve,
const ECLPhaseIndex phase) const
{
if (this->usys_output_ == nullptr) {
// No defined system of units for outputs. Return unconverted (SI).
return graph;
}
assert ((phase == ECLPhaseIndex::Liquid) ||
(phase == ECLPhaseIndex::Vapour));
if (curve == RawCurve::FVF) {
return convertFvfCurve(std::move(graph), phase,
*this->usys_internal_, // from
*this->usys_output_); // to
}
if (curve == RawCurve::Viscosity) {
return convertViscosityCurve(std::move(graph), phase,
*this->usys_internal_, // from
*this->usys_output_); // to
}
if (curve == RawCurve::SaturatedState) {
return convertSatStateCurve(std::move(graph), phase,
*this->usys_internal_, // from
*this->usys_output_); // to
}
throw std::invalid_argument { "Internal Logic Error" };
} }

View File

@ -22,6 +22,7 @@
#include <opm/utility/ECLGraph.hpp> #include <opm/utility/ECLGraph.hpp>
#include <opm/utility/ECLPhaseIndex.hpp> #include <opm/utility/ECLPhaseIndex.hpp>
#include <opm/utility/ECLPropertyUnitConversion.hpp>
#include <opm/utility/ECLPvtCommon.hpp> #include <opm/utility/ECLPvtCommon.hpp>
#include <opm/utility/ECLPvtGas.hpp> #include <opm/utility/ECLPvtGas.hpp>
#include <opm/utility/ECLPvtOil.hpp> #include <opm/utility/ECLPvtOil.hpp>
@ -58,6 +59,20 @@ namespace Opm { namespace ECLPVT {
ECLPvtCurveCollection(const ECLGraph& G, ECLPvtCurveCollection(const ECLGraph& G,
const ECLInitFileData& init); const ECLInitFileData& init);
/// Define a collection of units of measure for output purposes.
///
/// PVT property curves will be reported in the appropriate units of
/// this system. If this function is never called (or called with
/// null pointer), then the output units are implicitly set to the
/// flow-diagnostics module's internal units of measurement (meaning
/// all properties and curves will be reported in strict SI units).
///
/// \param[in] usys Collection of units of measure for output
/// purposes. Typically the return value from one of the \code
/// *UnitConvention() \endcode functions of the \code ECLUnits
/// \endcode namespace.
void setOutputUnits(std::unique_ptr<const ECLUnits::UnitSystem> usys);
/// Retrieve 2D graph representation of Phase PVT property function /// Retrieve 2D graph representation of Phase PVT property function
/// in a specific active cell. /// in a specific active cell.
/// ///
@ -66,14 +81,16 @@ namespace Opm { namespace ECLPVT {
/// \param[in] phase Phase for which to compute extract graph /// \param[in] phase Phase for which to compute extract graph
/// representation of PVT property function. /// representation of PVT property function.
/// ///
/// \param[in] activeCell Index of particular active cell in model.. /// \param[in] activeCell Index of particular active cell in model.
/// ///
/// \return Collection of 2D graphs for PVT property curve /// \return Collection of 2D graphs for PVT property curve
/// identified by requests represented by \p curve, \p phase and /// identified by requests represented by \p curve, \p phase and
/// \p activeCell. One curve (vector element) for each tabulated /// \p activeCell. One curve (vector element) for each tabulated
/// node of the primary look-up key. Single curve (i.e., a /// node of the primary look-up key. Single curve (i.e., a
/// single element vector) in the case of dry gas (no vaporised /// single element vector) in the case of dry gas (no vaporised
/// oil) or dead oil (no dissolved gas). /// oil) or dead oil (no dissolved gas). Return values provided
/// in the system of units specified by setOutputUnits(). Strict
/// SI if no output units have been defined.
/// ///
/// No curves for water or dead oil with constant compressibility /// No curves for water or dead oil with constant compressibility
/// (i.e., keyword 'PVCDO' in the input deck). /// (i.e., keyword 'PVCDO' in the input deck).
@ -91,6 +108,110 @@ namespace Opm { namespace ECLPVT {
const ECLPhaseIndex phase, const ECLPhaseIndex phase,
const int activeCell) const; const int activeCell) const;
/// Compute a single dynamic property in a single active cell for a
/// collection of cell states.
///
/// Note: Phase property inputs (phase pressure and mixing ratios)
/// must be in strict SI units of measure (Pascal and sm^3/sm^3,
/// respectively).
///
/// \param[in] property Named property. Must be one of \code
/// RawCurve::FVF \endcode or \code RawCurve::Viscosity \endcode.
/// All other values return an empty result.
///
/// \param[in] phase Phase for which to compute the property value.
/// Must be \code ECLPhaseIndex::Vapour \endcode or \code
/// ECLPhaseIndex::Liquid \endcode. All other values return an
/// empty result.
///
/// \param[in] activeCell Index of particular active cell in model.
///
/// \param[in] phasePress Sequence of phase pressure values
/// pertaining to \p activeCell. Could, for instance, be the
/// entire time-series of oil pressure values in that cell.
///
/// \param[in] mixRatio Sequence of phase mixing ratio values
/// pertaining to \p activeCell. Could, for instance, be the
/// entire time-series of dissolved gas/oil ratio values in that
/// cell. Must be empty or match the size of \p phasePress. If
/// empty, treated as \code
/// std::vector<double>(phasePress.size(), 0.0) \endcode which is
/// typically appropriate only for dry gas or dead oil cases.
///
/// \return Sequence of dynamic property values corresponding to the
/// requested property name of the identified phase in the
/// particular active cell. Empty for invalid requests, number
/// of elements equal to \code phasePress.size() \endcode
/// otherwise. Return values are in strict SI units of
/// measure--i.e., rm^3/sm^3 for the formation volume factors and
/// Pascal seconds for the viscosities.
std::vector<double>
getDynamicPropertySI(const RawCurve property,
const ECLPhaseIndex phase,
const int activeCell,
const std::vector<double>& phasePress,
const std::vector<double>& mixRatio
= std::vector<double>()) const;
/// Compute a single dynamic property in a single active cell for a
/// collection of cell states.
///
/// This interface is intended for direct calculation based on raw
/// (unconverted) data vectors from an ECL result set.
/// Consequently, phase property inputs (phase pressure and mixing
/// ratios) must be in the result set's native/serialised collection
/// of units of measure (e.g., pressures in Atmospheres and mixing
/// ratios in scm^3/scm^3 for the "LAB" system of units).
///
/// \param[in] property Named property. Must be one of \code
/// RawCurve::FVF \endcode or \code RawCurve::Viscosity \endcode.
/// All other values return an empty result.
///
/// \param[in] phase Phase for which to compute the property value.
/// Must be \code ECLPhaseIndex::Vapour \endcode or \code
/// ECLPhaseIndex::Liquid \endcode. All other values return an
/// empty result.
///
/// \param[in] activeCell Index of particular active cell in model.
///
/// \param[in] phasePress Sequence of phase pressure values
/// pertaining to \p activeCell. Could, for instance, be the
/// entire time-series of oil pressure values in that cell. Must
/// be in the serialised system of units (i.e., Bars for METRIC,
/// Psi for FIELD, and Atm for LAB and PVT-M).
///
/// \param[in] mixRatio Sequence of phase mixing ratio values
/// pertaining to \p activeCell. Could, for instance, be the
/// entire time-series of dissolved gas/oil ratio values in that
/// cell. Must be empty or match the size of \p phasePress. If
/// empty, treated as \code
/// std::vector<double>(phasePress.size(), 0.0) \endcode which is
/// typically appropriate only for dry gas or dead oil cases.
///
/// Must be in the serialised system of units. In other words
/// when the \p mixRatio represents the dissolved gas/oil ratio
/// (Rs), then the input must be given in sm^3/sm^3 for METRIC,
/// Mscf/stb for FIELD, scm^3/scm^3 for LAB and sm^3/sm^3 for
/// PVT-M. Similarly, when the \p mixRatio represents the
/// vapourised oil/gas ratio (Rv), then the input must be given
/// in sm^3/sm^3 for METRIC, stb/Mscf for FIELD, scm^3/scm^3 for
/// LAB and sm^3/sm^3 for PVT-M).
///
/// \return Sequence of dynamic property values corresponding to the
/// requested property name of the identified phase in the
/// particular active cell. Empty for invalid requests, number
/// of elements equal to \code phasePress.size() \endcode
/// otherwise. Return values provided in the system of units
/// specified by setOutputUnits(). Strict SI if no output units
/// have been defined.
std::vector<double>
getDynamicPropertyNative(const RawCurve property,
const ECLPhaseIndex phase,
const int activeCell,
std::vector<double> phasePress, // Mutable copy
std::vector<double> mixRatio // Mutable copy
= std::vector<double>()) const;
private: private:
/// Forward map: Cell -> PVT Region ID /// Forward map: Cell -> PVT Region ID
std::vector<int> pvtnum_; std::vector<int> pvtnum_;
@ -101,8 +222,44 @@ namespace Opm { namespace ECLPVT {
/// Oil PVT property evaluator. /// Oil PVT property evaluator.
std::shared_ptr<Oil> oil_; std::shared_ptr<Oil> oil_;
/// Unit handling (SI -> result-set convention) /// Native unit system of INIT file. Used in the implementation of
std::shared_ptr<const ECLUnits::UnitSystem> usys_; /// member function getDynamicPropertyNative().
std::shared_ptr<const ECLUnits::UnitSystem> usys_native_;
/// Explicit representation of internal system of units. Strict SI.
/// Used in getDynamicPropertyNative().
std::shared_ptr<const ECLUnits::UnitSystem> usys_internal_;
/// User-specified system of units for output of properties. Used
/// by getPvtCurve() and getDynamicPropertyNative().
std::shared_ptr<const ECLUnits::UnitSystem> usys_output_{nullptr};
/// Determine if a particular request can be met by the internal
/// implementation.
///
/// \param[in] phase Phase for which to compute a property.
///
/// \param[in] activeCell Index of particular active cell in model.
///
/// \return True if \p phase is supported and \p activeCell is
/// within range of the currently defined model. False otherwise.
bool isValidRequest(const ECLPhaseIndex phase,
const int activeCell) const;
/// Convert a sequence of 2D graphs to user-defined system of units.
///
/// \param[in] graph Sequence of 2D graphs corresponding to a
/// particular curve request.
///
/// \param[in] property Named property.
///
/// \param[in] phase Phase for which to compute the property curve.
/// Must be \code ECLPhaseIndex::Vapour \endcode or \code
/// ECLPhaseIndex::Liquid \endcode.
std::vector<FlowDiagnostics::Graph>
convertToOutputUnits(std::vector<FlowDiagnostics::Graph>&& graph,
const RawCurve curve,
const ECLPhaseIndex phase) const;
}; };
}} // Opm::ECLPVT }} // Opm::ECLPVT

View File

@ -99,8 +99,7 @@ public:
const std::vector<double>& pg) const = 0; const std::vector<double>& pg) const = 0;
virtual std::vector<Opm::FlowDiagnostics::Graph> virtual std::vector<Opm::FlowDiagnostics::Graph>
getPvtCurve(const Opm::ECLPVT::RawCurve curve, getPvtCurve(const Opm::ECLPVT::RawCurve curve) const = 0;
const Opm::ECLUnits::UnitSystem& usys) const = 0;
virtual std::unique_ptr<PVxGBase> clone() const = 0; virtual std::unique_ptr<PVxGBase> clone() const = 0;
}; };
@ -135,30 +134,9 @@ public:
} }
virtual std::vector<Opm::FlowDiagnostics::Graph> virtual std::vector<Opm::FlowDiagnostics::Graph>
getPvtCurve(const Opm::ECLPVT::RawCurve curve, getPvtCurve(const Opm::ECLPVT::RawCurve curve) const override
const Opm::ECLUnits::UnitSystem& usys) const override
{ {
if (curve == Opm::ECLPVT::RawCurve::SaturatedState) { return { this->interpolant_.getPvtCurve(curve) };
// Not applicable to dry gas. Return empty.
return { Opm::FlowDiagnostics::Graph{} };
}
auto pvtcurve = this->interpolant_.getPvtCurve(curve);
const auto x_unit = usys.pressure();
const auto y_unit = (curve == ::Opm::ECLPVT::RawCurve::FVF)
? (usys.reservoirVolume() / usys.surfaceVolumeGas())
: usys.viscosity();
auto& x = pvtcurve.first;
auto& y = pvtcurve.second;
for (auto n = x.size(), i = 0*n; i < n; ++i) {
x[i] = ::Opm::unit::convert::to(x[i], x_unit);
y[i] = ::Opm::unit::convert::to(y[i], y_unit);
}
return { std::move(pvtcurve) };
} }
virtual std::unique_ptr<PVxGBase> clone() const override virtual std::unique_ptr<PVxGBase> clone() const override
@ -183,8 +161,7 @@ public:
WetGas(std::vector<double> key, WetGas(std::vector<double> key,
std::vector<SubtableInterpolant> propInterp) std::vector<SubtableInterpolant> propInterp)
: key_ (key) // Copy : interp_(std::move(key), std::move(propInterp))
, interp_(std::move(key), std::move(propInterp))
{} {}
virtual std::vector<double> virtual std::vector<double>
@ -214,14 +191,9 @@ public:
} }
virtual std::vector<Opm::FlowDiagnostics::Graph> virtual std::vector<Opm::FlowDiagnostics::Graph>
getPvtCurve(const Opm::ECLPVT::RawCurve curve, getPvtCurve(const Opm::ECLPVT::RawCurve curve) const override
const Opm::ECLUnits::UnitSystem& usys) const override
{ {
if (curve == ::Opm::ECLPVT::RawCurve::SaturatedState) { return this->interp_.getPvtCurve(curve);
return this->saturatedStateCurve(usys);
}
return this->mainPvtCurve(curve, usys);
} }
virtual std::unique_ptr<PVxGBase> clone() const override virtual std::unique_ptr<PVxGBase> clone() const override
@ -232,68 +204,9 @@ public:
private: private:
using TableInterpolant = ::Opm::ECLPVT::PVTx<SubtableInterpolant>; using TableInterpolant = ::Opm::ECLPVT::PVTx<SubtableInterpolant>;
std::vector<double> key_; TableInterpolant interp_;
TableInterpolant interp_;
std::vector<Opm::FlowDiagnostics::Graph>
mainPvtCurve(const Opm::ECLPVT::RawCurve curve,
const Opm::ECLUnits::UnitSystem& usys) const;
std::vector<Opm::FlowDiagnostics::Graph>
saturatedStateCurve(const Opm::ECLUnits::UnitSystem& usys) const;
}; };
std::vector<Opm::FlowDiagnostics::Graph>
WetGas::mainPvtCurve(const Opm::ECLPVT::RawCurve curve,
const Opm::ECLUnits::UnitSystem& usys) const
{
auto curves = this->interp_.getPvtCurve(curve);
const auto x_unit = usys.vaporisedOilGasRat();
const auto y_unit = (curve == ::Opm::ECLPVT::RawCurve::FVF)
? (usys.reservoirVolume() / usys.surfaceVolumeGas())
: usys.viscosity();
for (auto& crv : curves) {
auto& x = crv.first;
auto& y = crv.second;
for (auto n = x.size(), i = 0*n; i < n; ++i) {
x[i] = ::Opm::unit::convert::to(x[i], x_unit);
y[i] = ::Opm::unit::convert::to(y[i], y_unit);
}
}
return curves;
}
std::vector<Opm::FlowDiagnostics::Graph>
WetGas::saturatedStateCurve(const Opm::ECLUnits::UnitSystem& usys) const
{
const auto key_unit = usys.pressure();
const auto rv_unit = usys.vaporisedOilGasRat();
auto rv = this->interp_.getSaturatedPoints();
auto p = this->key_;
if (rv.size() != p.size()) {
throw std::invalid_argument {
"Inconsistent table sizes of saturated gas function (RvSat(p))"
};
}
for (auto n = rv.size(), i = 0*n; i < n; ++i) {
p [i] = ::Opm::unit::convert::to(p [i], key_unit);
rv[i] = ::Opm::unit::convert::to(rv[i], rv_unit);
}
auto graph = Opm::FlowDiagnostics::Graph {
std::move(p), std::move(rv)
};
return { std::move(graph) };
}
// ##################################################################### // #####################################################################
namespace { namespace {
@ -479,9 +392,8 @@ public:
} }
std::vector<FlowDiagnostics::Graph> std::vector<FlowDiagnostics::Graph>
getPvtCurve(const RegIdx region, getPvtCurve(const RegIdx region,
const RawCurve curve, const RawCurve curve) const;
const ECLUnits::UnitSystem& usys) const;
private: private:
std::vector<EvalPtr> eval_; std::vector<EvalPtr> eval_;
@ -550,13 +462,12 @@ viscosity(const RegIdx region,
std::vector<Opm::FlowDiagnostics::Graph> std::vector<Opm::FlowDiagnostics::Graph>
Opm::ECLPVT::Gas::Impl:: Opm::ECLPVT::Gas::Impl::
getPvtCurve(const RegIdx region, getPvtCurve(const RegIdx region,
const RawCurve curve, const RawCurve curve) const
const ECLUnits::UnitSystem& usys) const
{ {
this->validateRegIdx(region); this->validateRegIdx(region);
return this->eval_[region]->getPvtCurve(curve, usys); return this->eval_[region]->getPvtCurve(curve);
} }
void void
@ -634,11 +545,10 @@ double Opm::ECLPVT::Gas::surfaceMassDensity(const int region) const
std::vector<Opm::FlowDiagnostics::Graph> std::vector<Opm::FlowDiagnostics::Graph>
Opm::ECLPVT::Gas:: Opm::ECLPVT::Gas::
getPvtCurve(const RawCurve curve, getPvtCurve(const RawCurve curve,
const int region, const int region) const
const ECLUnits::UnitSystem& usys) const
{ {
return this->pImpl_->getPvtCurve(region, curve, usys); return this->pImpl_->getPvtCurve(region, curve);
} }
// ===================================================================== // =====================================================================

View File

@ -162,11 +162,6 @@ namespace Opm { namespace ECLPVT {
/// \param[in] region Region ID. Non-negative integer typically /// \param[in] region Region ID. Non-negative integer typically
/// derived from the PVTNUM mapping vector. /// derived from the PVTNUM mapping vector.
/// ///
/// \param[in] usys Unit system. Collection of units to which the
/// raw, tabulated PVT curve data will be converted. Usually
/// created by function \code ECLUnits::createUnitSystem()
/// \endcode or a similar facility.
///
/// \return Collection of 2D graphs for PVT property curve /// \return Collection of 2D graphs for PVT property curve
/// identified by requests represented by \p func and \p region. /// identified by requests represented by \p func and \p region.
/// One curve (vector element) for each pressure node. Single /// One curve (vector element) for each pressure node. Single
@ -181,9 +176,8 @@ namespace Opm { namespace ECLPVT {
/// pvtGas.getPvtCurve(ECLPVT::RawCurve::FVF, 0); /// pvtGas.getPvtCurve(ECLPVT::RawCurve::FVF, 0);
/// \endcode /// \endcode
std::vector<FlowDiagnostics::Graph> std::vector<FlowDiagnostics::Graph>
getPvtCurve(const RawCurve curve, getPvtCurve(const RawCurve curve,
const int region, const int region) const;
const ECLUnits::UnitSystem& usys) const;
private: private:
/// Implementation class. /// Implementation class.

View File

@ -96,8 +96,7 @@ public:
const std::vector<double>& po) const = 0; const std::vector<double>& po) const = 0;
virtual std::vector<Opm::FlowDiagnostics::Graph> virtual std::vector<Opm::FlowDiagnostics::Graph>
getPvtCurve(const Opm::ECLPVT::RawCurve curve, getPvtCurve(const Opm::ECLPVT::RawCurve curve) const = 0;
const Opm::ECLUnits::UnitSystem& usys) const = 0;
virtual std::unique_ptr<PVxOBase> clone() const = 0; virtual std::unique_ptr<PVxOBase> clone() const = 0;
}; };
@ -132,30 +131,9 @@ public:
} }
virtual std::vector<Opm::FlowDiagnostics::Graph> virtual std::vector<Opm::FlowDiagnostics::Graph>
getPvtCurve(const Opm::ECLPVT::RawCurve curve, getPvtCurve(const Opm::ECLPVT::RawCurve curve) const override
const Opm::ECLUnits::UnitSystem& usys) const override
{ {
if (curve == Opm::ECLPVT::RawCurve::SaturatedState) { return { this->interpolant_.getPvtCurve(curve) };
// Not applicable to dead oil. Return empty.
return { Opm::FlowDiagnostics::Graph{} };
}
auto pvtcurve = this->interpolant_.getPvtCurve(curve);
const auto x_unit = usys.pressure();
const auto y_unit = (curve == ::Opm::ECLPVT::RawCurve::FVF)
? (usys.reservoirVolume() / usys.surfaceVolumeLiquid())
: usys.viscosity();
auto& x = pvtcurve.first;
auto& y = pvtcurve.second;
for (auto n = x.size(), i = 0*n; i < n; ++i) {
x[i] = ::Opm::unit::convert::to(x[i], x_unit);
y[i] = ::Opm::unit::convert::to(y[i], y_unit);
}
return { std::move(pvtcurve) };
} }
virtual std::unique_ptr<PVxOBase> clone() const override virtual std::unique_ptr<PVxOBase> clone() const override
@ -180,8 +158,7 @@ public:
LiveOil(std::vector<double> key, LiveOil(std::vector<double> key,
std::vector<SubtableInterpolant> propInterp) std::vector<SubtableInterpolant> propInterp)
: key_ (key) // Copy : interp_(std::move(key), std::move(propInterp))
, interp_(std::move(key), std::move(propInterp))
{} {}
virtual std::vector<double> virtual std::vector<double>
@ -211,14 +188,9 @@ public:
} }
virtual std::vector<Opm::FlowDiagnostics::Graph> virtual std::vector<Opm::FlowDiagnostics::Graph>
getPvtCurve(const Opm::ECLPVT::RawCurve curve, getPvtCurve(const Opm::ECLPVT::RawCurve curve) const override
const Opm::ECLUnits::UnitSystem& usys) const override
{ {
if (curve == ::Opm::ECLPVT::RawCurve::SaturatedState) { return this->interp_.getPvtCurve(curve);
return this->saturatedStateCurve(usys);
}
return this->mainPvtCurve(curve, usys);
} }
virtual std::unique_ptr<PVxOBase> clone() const override virtual std::unique_ptr<PVxOBase> clone() const override
@ -229,67 +201,26 @@ public:
private: private:
using TableInterpolant = ::Opm::ECLPVT::PVTx<SubtableInterpolant>; using TableInterpolant = ::Opm::ECLPVT::PVTx<SubtableInterpolant>;
std::vector<double> key_; TableInterpolant interp_;
TableInterpolant interp_;
std::vector<Opm::FlowDiagnostics::Graph> std::vector<Opm::FlowDiagnostics::Graph>
mainPvtCurve(const Opm::ECLPVT::RawCurve curve, repackagePvtCurve(std::vector<Opm::FlowDiagnostics::Graph>&& graphs,
const Opm::ECLUnits::UnitSystem& usys) const; const Opm::ECLPVT::RawCurve curve) const
{
std::vector<Opm::FlowDiagnostics::Graph> if (curve != Opm::ECLPVT::RawCurve::SaturatedState) {
saturatedStateCurve(const Opm::ECLUnits::UnitSystem& usys) const; // Not saturated state curve. Nothing to do here.
}; return graphs;
std::vector<Opm::FlowDiagnostics::Graph>
LiveOil::mainPvtCurve(const Opm::ECLPVT::RawCurve curve,
const Opm::ECLUnits::UnitSystem& usys) const
{
auto curves = this->interp_.getPvtCurve(curve);
const auto x_unit = usys.pressure();
const auto y_unit = (curve == ::Opm::ECLPVT::RawCurve::FVF)
? (usys.reservoirVolume() / usys.surfaceVolumeLiquid())
: usys.viscosity();
for (auto& crv : curves) {
auto& x = crv.first;
auto& y = crv.second;
for (auto n = x.size(), i = 0*n; i < n; ++i) {
x[i] = ::Opm::unit::convert::to(x[i], x_unit);
y[i] = ::Opm::unit::convert::to(y[i], y_unit);
} }
// Saturated state curve for live oil. Columns are (Rs, Po). Swap
// first and second columns to normalised form: (Po, Rs).
for (auto& graph : graphs) {
graph.first.swap(graph.second);
}
return graphs;
} }
};
return curves;
}
std::vector<Opm::FlowDiagnostics::Graph>
LiveOil::saturatedStateCurve(const Opm::ECLUnits::UnitSystem& usys) const
{
const auto press_unit = usys.pressure();
const auto rs_unit = usys.dissolvedGasOilRat();
auto rs = this->key_;
auto p = this->interp_.getSaturatedPoints();
if (rs.size() != p.size()) {
throw std::invalid_argument {
"Inconsistent table sizes of saturated gas function (RsSat(p))"
};
}
for (auto n = rs.size(), i = 0*n; i < n; ++i) {
p [i] = ::Opm::unit::convert::to(p [i], press_unit);
rs[i] = ::Opm::unit::convert::to(rs[i], rs_unit);
}
auto graph = Opm::FlowDiagnostics::Graph {
std::move(p), std::move(rs)
};
return { std::move(graph) };
}
// ##################################################################### // #####################################################################
@ -476,9 +407,8 @@ public:
} }
std::vector<FlowDiagnostics::Graph> std::vector<FlowDiagnostics::Graph>
getPvtCurve(const RegIdx region, getPvtCurve(const RegIdx region,
const RawCurve curve, const RawCurve curve) const;
const ECLUnits::UnitSystem& usys) const;
private: private:
std::vector<EvalPtr> eval_; std::vector<EvalPtr> eval_;
@ -547,13 +477,12 @@ viscosity(const RegIdx region,
std::vector<Opm::FlowDiagnostics::Graph> std::vector<Opm::FlowDiagnostics::Graph>
Opm::ECLPVT::Oil::Impl:: Opm::ECLPVT::Oil::Impl::
getPvtCurve(const RegIdx region, getPvtCurve(const RegIdx region,
const RawCurve curve, const RawCurve curve) const
const ECLUnits::UnitSystem& usys) const
{ {
this->validateRegIdx(region); this->validateRegIdx(region);
return this->eval_[region]->getPvtCurve(curve, usys); return this->eval_[region]->getPvtCurve(curve);
} }
void void
@ -631,11 +560,10 @@ double Opm::ECLPVT::Oil::surfaceMassDensity(const int region) const
std::vector<Opm::FlowDiagnostics::Graph> std::vector<Opm::FlowDiagnostics::Graph>
Opm::ECLPVT::Oil:: Opm::ECLPVT::Oil::
getPvtCurve(const RawCurve curve, getPvtCurve(const RawCurve curve,
const int region, const int region) const
const ECLUnits::UnitSystem& usys) const
{ {
return this->pImpl_->getPvtCurve(region, curve, usys); return this->pImpl_->getPvtCurve(region, curve);
} }
// ===================================================================== // =====================================================================

View File

@ -159,11 +159,6 @@ namespace Opm { namespace ECLPVT {
/// \param[in] region Region ID. Non-negative integer typically /// \param[in] region Region ID. Non-negative integer typically
/// derived from the PVTNUM mapping vector. /// derived from the PVTNUM mapping vector.
/// ///
/// \param[in] usys Unit system. Collection of units to which the
/// raw, tabulated PVT curve data will be converted. Usually
/// created by function \code ECLUnits::createUnitSystem()
/// \endcode or a similar facility.
///
/// \return Collection of 2D graphs for PVT property curve /// \return Collection of 2D graphs for PVT property curve
/// identified by requests represented by \p func and \p region. /// identified by requests represented by \p func and \p region.
/// One curve (vector element) for each dissolved gas/oil ratio /// One curve (vector element) for each dissolved gas/oil ratio
@ -178,9 +173,8 @@ namespace Opm { namespace ECLPVT {
/// pvtOil.getPvtCurve(ECLPVT::RawCurve::Viscosity, 3); /// pvtOil.getPvtCurve(ECLPVT::RawCurve::Viscosity, 3);
/// \endcode /// \endcode
std::vector<FlowDiagnostics::Graph> std::vector<FlowDiagnostics::Graph>
getPvtCurve(const RawCurve curve, getPvtCurve(const RawCurve curve,
const int region, const int region) const;
const ECLUnits::UnitSystem& usys) const;
private: private:
/// Implementation class. /// Implementation class.

View File

@ -42,6 +42,14 @@ namespace Opm { namespace ECLUnits {
class USys<ECL_METRIC_UNITS> : public ::Opm::ECLUnits::UnitSystem class USys<ECL_METRIC_UNITS> : public ::Opm::ECLUnits::UnitSystem
{ {
public: public:
virtual std::unique_ptr<Opm::ECLUnits::UnitSystem>
clone() const override
{
return std::unique_ptr<Opm::ECLUnits::UnitSystem> {
new USys<ECL_METRIC_UNITS>(*this)
};
}
virtual double density() const override virtual double density() const override
{ {
return Metric::Density; return Metric::Density;
@ -97,6 +105,14 @@ namespace Opm { namespace ECLUnits {
class USys<ECL_FIELD_UNITS> : public ::Opm::ECLUnits::UnitSystem class USys<ECL_FIELD_UNITS> : public ::Opm::ECLUnits::UnitSystem
{ {
public: public:
virtual std::unique_ptr<Opm::ECLUnits::UnitSystem>
clone() const override
{
return std::unique_ptr<Opm::ECLUnits::UnitSystem> {
new USys<ECL_FIELD_UNITS>(*this)
};
}
virtual double density() const override virtual double density() const override
{ {
return Field::Density; return Field::Density;
@ -152,6 +168,14 @@ namespace Opm { namespace ECLUnits {
class USys<ECL_LAB_UNITS> : public ::Opm::ECLUnits::UnitSystem class USys<ECL_LAB_UNITS> : public ::Opm::ECLUnits::UnitSystem
{ {
public: public:
virtual std::unique_ptr<Opm::ECLUnits::UnitSystem>
clone() const override
{
return std::unique_ptr<Opm::ECLUnits::UnitSystem> {
new USys<ECL_LAB_UNITS>(*this)
};
}
virtual double density() const override virtual double density() const override
{ {
return Lab::Density; return Lab::Density;
@ -207,6 +231,14 @@ namespace Opm { namespace ECLUnits {
class USys<ECL_PVT_M_UNITS> : public ::Opm::ECLUnits::UnitSystem class USys<ECL_PVT_M_UNITS> : public ::Opm::ECLUnits::UnitSystem
{ {
public: public:
virtual std::unique_ptr<Opm::ECLUnits::UnitSystem>
clone() const override
{
return std::unique_ptr<Opm::ECLUnits::UnitSystem> {
new USys<ECL_PVT_M_UNITS>(*this)
};
}
virtual double density() const override virtual double density() const override
{ {
using namespace prefix; using namespace prefix;

View File

@ -28,6 +28,8 @@ namespace Opm {
struct UnitSystem struct UnitSystem
{ {
virtual std::unique_ptr<UnitSystem> clone() const = 0;
virtual double density() const = 0; virtual double density() const = 0;
virtual double depth() const = 0; virtual double depth() const = 0;
virtual double pressure() const = 0; virtual double pressure() const = 0;