2012-01-04 16:28:54 -06:00
|
|
|
/*
|
|
|
|
Copyright 2012 SINTEF ICT, Applied Mathematics.
|
|
|
|
|
|
|
|
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/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-04-10 05:56:14 -05:00
|
|
|
#include "config.h"
|
2013-03-14 04:29:42 -05:00
|
|
|
#include <opm/core/props/pvt/BlackoilPvtProperties.hpp>
|
|
|
|
#include <opm/core/props/pvt/SinglePvtDead.hpp>
|
|
|
|
#include <opm/core/props/pvt/SinglePvtDeadSpline.hpp>
|
|
|
|
#include <opm/core/props/pvt/SinglePvtLiveOil.hpp>
|
|
|
|
#include <opm/core/props/pvt/SinglePvtLiveGas.hpp>
|
|
|
|
#include <opm/core/props/pvt/SinglePvtConstCompr.hpp>
|
|
|
|
#include <opm/core/props/phaseUsageFromDeck.hpp>
|
2013-03-07 15:59:06 -06:00
|
|
|
#include <opm/core/io/eclipse/EclipseGridParser.hpp>
|
2012-01-04 16:28:54 -06:00
|
|
|
#include <opm/core/utility/Units.hpp>
|
|
|
|
#include <opm/core/utility/ErrorMacros.hpp>
|
2013-03-22 09:33:07 -05:00
|
|
|
#include <opm/core/utility/linearInterpolation.hpp>
|
2012-01-04 16:28:54 -06:00
|
|
|
|
2014-01-24 05:03:21 -06:00
|
|
|
#include <opm/parser/eclipse/Utility/PvtwTable.hpp>
|
|
|
|
#include <opm/parser/eclipse/Utility/PvdcoTable.hpp>
|
|
|
|
#include <opm/parser/eclipse/Deck/Deck.hpp>
|
2012-01-04 16:28:54 -06:00
|
|
|
|
|
|
|
namespace Opm
|
|
|
|
{
|
|
|
|
|
2012-01-05 02:34:05 -06:00
|
|
|
BlackoilPvtProperties::BlackoilPvtProperties()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2012-09-03 08:07:03 -05:00
|
|
|
void BlackoilPvtProperties::init(const EclipseGridParser& deck, const int samples)
|
2012-01-04 16:28:54 -06:00
|
|
|
{
|
|
|
|
// If we need multiple regions, this class and the SinglePvt* classes must change.
|
2012-09-05 04:28:54 -05:00
|
|
|
region_number_ = 0;
|
2012-01-04 16:28:54 -06:00
|
|
|
|
2012-01-05 02:34:05 -06:00
|
|
|
phase_usage_ = phaseUsageFromDeck(deck);
|
2012-01-04 16:28:54 -06:00
|
|
|
|
2012-09-05 04:28:54 -05:00
|
|
|
// Surface densities. Accounting for different orders in eclipse and our code.
|
|
|
|
if (deck.hasField("DENSITY")) {
|
|
|
|
const std::vector<double>& d = deck.getDENSITY().densities_[region_number_];
|
|
|
|
enum { ECL_oil = 0, ECL_water = 1, ECL_gas = 2 };
|
2012-01-05 04:40:29 -06:00
|
|
|
if (phase_usage_.phase_used[Aqua]) {
|
|
|
|
densities_[phase_usage_.phase_pos[Aqua]] = d[ECL_water];
|
|
|
|
}
|
|
|
|
if (phase_usage_.phase_used[Vapour]) {
|
|
|
|
densities_[phase_usage_.phase_pos[Vapour]] = d[ECL_gas];
|
|
|
|
}
|
|
|
|
if (phase_usage_.phase_used[Liquid]) {
|
|
|
|
densities_[phase_usage_.phase_pos[Liquid]] = d[ECL_oil];
|
|
|
|
}
|
2012-09-05 04:28:54 -05:00
|
|
|
} else {
|
2013-08-28 06:59:03 -05:00
|
|
|
OPM_THROW(std::runtime_error, "Input is missing DENSITY\n");
|
2012-09-05 04:28:54 -05:00
|
|
|
}
|
2012-01-04 16:28:54 -06:00
|
|
|
|
|
|
|
// Set the properties.
|
2012-01-05 02:34:05 -06:00
|
|
|
props_.resize(phase_usage_.num_phases);
|
2012-01-04 16:28:54 -06:00
|
|
|
// Water PVT
|
2012-01-05 02:34:05 -06:00
|
|
|
if (phase_usage_.phase_used[Aqua]) {
|
2012-01-04 16:28:54 -06:00
|
|
|
if (deck.hasField("PVTW")) {
|
2012-01-05 02:34:05 -06:00
|
|
|
props_[phase_usage_.phase_pos[Aqua]].reset(new SinglePvtConstCompr(deck.getPVTW().pvtw_));
|
2012-01-04 16:28:54 -06:00
|
|
|
} else {
|
|
|
|
// Eclipse 100 default.
|
2012-01-19 06:50:57 -06:00
|
|
|
props_[phase_usage_.phase_pos[Aqua]].reset(new SinglePvtConstCompr(0.5*Opm::prefix::centi*Opm::unit::Poise));
|
2012-01-04 16:28:54 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// Oil PVT
|
2012-01-05 02:34:05 -06:00
|
|
|
if (phase_usage_.phase_used[Liquid]) {
|
2012-01-04 16:28:54 -06:00
|
|
|
if (deck.hasField("PVDO")) {
|
2012-09-04 04:42:31 -05:00
|
|
|
if (samples > 0) {
|
|
|
|
props_[phase_usage_.phase_pos[Liquid]].reset(new SinglePvtDeadSpline(deck.getPVDO().pvdo_, samples));
|
2012-08-27 09:48:21 -05:00
|
|
|
} else {
|
|
|
|
props_[phase_usage_.phase_pos[Liquid]].reset(new SinglePvtDead(deck.getPVDO().pvdo_));
|
|
|
|
}
|
2012-01-04 16:28:54 -06:00
|
|
|
} else if (deck.hasField("PVTO")) {
|
2012-01-05 02:34:05 -06:00
|
|
|
props_[phase_usage_.phase_pos[Liquid]].reset(new SinglePvtLiveOil(deck.getPVTO().pvto_));
|
2012-01-04 16:28:54 -06:00
|
|
|
} else if (deck.hasField("PVCDO")) {
|
2012-01-05 02:34:05 -06:00
|
|
|
props_[phase_usage_.phase_pos[Liquid]].reset(new SinglePvtConstCompr(deck.getPVCDO().pvcdo_));
|
2012-01-04 16:28:54 -06:00
|
|
|
} else {
|
2013-08-28 06:59:03 -05:00
|
|
|
OPM_THROW(std::runtime_error, "Input is missing PVDO or PVTO\n");
|
2012-01-04 16:28:54 -06:00
|
|
|
}
|
|
|
|
}
|
2012-09-05 04:28:54 -05:00
|
|
|
// Gas PVT
|
2012-01-05 02:34:05 -06:00
|
|
|
if (phase_usage_.phase_used[Vapour]) {
|
2012-01-04 16:28:54 -06:00
|
|
|
if (deck.hasField("PVDG")) {
|
2012-09-04 04:42:31 -05:00
|
|
|
if (samples > 0) {
|
|
|
|
props_[phase_usage_.phase_pos[Vapour]].reset(new SinglePvtDeadSpline(deck.getPVDG().pvdg_, samples));
|
2012-08-27 09:48:21 -05:00
|
|
|
} else {
|
|
|
|
props_[phase_usage_.phase_pos[Vapour]].reset(new SinglePvtDead(deck.getPVDG().pvdg_));
|
|
|
|
}
|
2012-01-04 16:28:54 -06:00
|
|
|
} else if (deck.hasField("PVTG")) {
|
2012-01-05 02:34:05 -06:00
|
|
|
props_[phase_usage_.phase_pos[Vapour]].reset(new SinglePvtLiveGas(deck.getPVTG().pvtg_));
|
2012-01-04 16:28:54 -06:00
|
|
|
} else {
|
2013-08-28 06:59:03 -05:00
|
|
|
OPM_THROW(std::runtime_error, "Input is missing PVDG or PVTG\n");
|
2012-01-04 16:28:54 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Must inform pvt property objects of phase structure.
|
2012-01-05 02:34:05 -06:00
|
|
|
for (int i = 0; i < phase_usage_.num_phases; ++i) {
|
|
|
|
props_[i]->setPhaseConfiguration(phase_usage_.num_phases, phase_usage_.phase_pos);
|
2012-01-04 16:28:54 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-24 05:03:21 -06:00
|
|
|
void BlackoilPvtProperties::init(Opm::DeckConstPtr newParserDeck, int samples)
|
|
|
|
{
|
|
|
|
// If we need multiple regions, this class and the SinglePvt* classes must change.
|
|
|
|
region_number_ = 0;
|
|
|
|
|
|
|
|
phase_usage_ = phaseUsageFromDeck(newParserDeck);
|
|
|
|
|
|
|
|
// Surface densities. Accounting for different orders in eclipse and our code.
|
|
|
|
if (newParserDeck->hasKeyword("DENSITY")) {
|
|
|
|
Opm::DeckKeywordConstPtr densityKeyword = newParserDeck->getKeyword("DENSITY");
|
2014-03-18 12:04:40 -05:00
|
|
|
if (phase_usage_.phase_used[Liquid]) {
|
|
|
|
densities_[phase_usage_.phase_pos[Liquid]]
|
|
|
|
= densityKeyword->getRecord(region_number_)->getItem("OIL")->getSIDouble(0);
|
|
|
|
}
|
2014-01-24 05:03:21 -06:00
|
|
|
if (phase_usage_.phase_used[Aqua]) {
|
2014-03-18 12:04:40 -05:00
|
|
|
densities_[phase_usage_.phase_pos[Aqua]]
|
|
|
|
= densityKeyword->getRecord(region_number_)->getItem("WATER")->getSIDouble(0);
|
2014-01-24 05:03:21 -06:00
|
|
|
}
|
|
|
|
if (phase_usage_.phase_used[Vapour]) {
|
2014-03-18 12:04:40 -05:00
|
|
|
densities_[phase_usage_.phase_pos[Vapour]]
|
|
|
|
= densityKeyword->getRecord(region_number_)->getItem("GAS")->getSIDouble(0);
|
2014-01-24 05:03:21 -06:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
OPM_THROW(std::runtime_error, "Input is missing DENSITY\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the properties.
|
|
|
|
props_.resize(phase_usage_.num_phases);
|
|
|
|
// Water PVT
|
|
|
|
if (phase_usage_.phase_used[Aqua]) {
|
|
|
|
if (newParserDeck->hasKeyword("PVTW")) {
|
|
|
|
Opm::PvtwTable pvtwTable(newParserDeck->getKeyword("PVTW"));
|
|
|
|
|
|
|
|
props_[phase_usage_.phase_pos[Aqua]].reset(new SinglePvtConstCompr(pvtwTable));
|
|
|
|
} else {
|
|
|
|
// Eclipse 100 default.
|
|
|
|
props_[phase_usage_.phase_pos[Aqua]].reset(new SinglePvtConstCompr(0.5*Opm::prefix::centi*Opm::unit::Poise));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Oil PVT
|
|
|
|
if (phase_usage_.phase_used[Liquid]) {
|
|
|
|
if (newParserDeck->hasKeyword("PVDO")) {
|
|
|
|
Opm::PvdoTable pvdoTable(newParserDeck->getKeyword("PVDO"), region_number_);
|
2014-03-03 02:17:11 -06:00
|
|
|
if (samples > 0) {
|
|
|
|
props_[phase_usage_.phase_pos[Liquid]].reset(new SinglePvtDeadSpline(pvdoTable, samples));
|
|
|
|
} else {
|
|
|
|
props_[phase_usage_.phase_pos[Liquid]].reset(new SinglePvtDead(pvdoTable));
|
|
|
|
}
|
2014-01-24 05:03:21 -06:00
|
|
|
} else if (newParserDeck->hasKeyword("PVTO")) {
|
|
|
|
Opm::PvtoTable pvtoTable(newParserDeck->getKeyword("PVTO"), /*tableIdx=*/0);
|
|
|
|
|
|
|
|
props_[phase_usage_.phase_pos[Liquid]].reset(new SinglePvtLiveOil(pvtoTable));
|
|
|
|
} else if (newParserDeck->hasKeyword("PVCDO")) {
|
|
|
|
Opm::PvdcoTable pvcdoTable(newParserDeck->getKeyword("PVCDO"));
|
|
|
|
|
|
|
|
props_[phase_usage_.phase_pos[Liquid]].reset(new SinglePvtConstCompr(pvcdoTable));
|
|
|
|
} else {
|
|
|
|
OPM_THROW(std::runtime_error, "Input is missing PVDO or PVTO\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Gas PVT
|
|
|
|
if (phase_usage_.phase_used[Vapour]) {
|
|
|
|
if (newParserDeck->hasKeyword("PVDG")) {
|
|
|
|
Opm::PvdgTable pvdgTable(newParserDeck->getKeyword("PVDG"), region_number_);
|
2014-03-03 02:17:11 -06:00
|
|
|
if (samples > 0) {
|
|
|
|
props_[phase_usage_.phase_pos[Vapour]].reset(new SinglePvtDeadSpline(pvdgTable, samples));
|
|
|
|
} else {
|
|
|
|
props_[phase_usage_.phase_pos[Vapour]].reset(new SinglePvtDead(pvdgTable));
|
|
|
|
}
|
2014-01-24 05:03:21 -06:00
|
|
|
} else if (newParserDeck->hasKeyword("PVTG")) {
|
|
|
|
Opm::PvtgTable pvtgTable(newParserDeck->getKeyword("PVTG"), /*tableIdx=*/0);
|
|
|
|
|
|
|
|
props_[phase_usage_.phase_pos[Vapour]].reset(new SinglePvtLiveGas(pvtgTable));
|
|
|
|
} else {
|
|
|
|
OPM_THROW(std::runtime_error, "Input is missing PVDG or PVTG\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Must inform pvt property objects of phase structure.
|
|
|
|
for (int i = 0; i < phase_usage_.num_phases; ++i) {
|
|
|
|
props_[i]->setPhaseConfiguration(phase_usage_.num_phases, phase_usage_.phase_pos);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-04 16:28:54 -06:00
|
|
|
const double* BlackoilPvtProperties::surfaceDensities() const
|
|
|
|
{
|
|
|
|
return densities_;
|
|
|
|
}
|
2012-01-05 06:12:27 -06:00
|
|
|
|
|
|
|
|
2013-05-13 09:20:00 -05:00
|
|
|
PhaseUsage BlackoilPvtProperties::phaseUsage() const
|
|
|
|
{
|
|
|
|
return phase_usage_;
|
|
|
|
}
|
|
|
|
|
2012-01-05 06:12:27 -06:00
|
|
|
int BlackoilPvtProperties::numPhases() const
|
|
|
|
{
|
|
|
|
return phase_usage_.num_phases;
|
|
|
|
}
|
2012-01-05 14:55:19 -06:00
|
|
|
|
|
|
|
const int* BlackoilPvtProperties::phaseUsed() const
|
|
|
|
{
|
|
|
|
return phase_usage_.phase_used;
|
|
|
|
}
|
|
|
|
|
|
|
|
const int* BlackoilPvtProperties::phasePosition() const
|
|
|
|
{
|
|
|
|
return phase_usage_.phase_pos;
|
|
|
|
}
|
2012-01-04 16:28:54 -06:00
|
|
|
|
|
|
|
|
|
|
|
void BlackoilPvtProperties::mu(const int n,
|
|
|
|
const double* p,
|
|
|
|
const double* z,
|
|
|
|
double* output_mu) const
|
|
|
|
{
|
|
|
|
data1_.resize(n);
|
2012-01-05 02:34:05 -06:00
|
|
|
for (int phase = 0; phase < phase_usage_.num_phases; ++phase) {
|
2012-01-04 16:28:54 -06:00
|
|
|
props_[phase]->mu(n, p, z, &data1_[0]);
|
2012-04-10 08:46:24 -05:00
|
|
|
// #pragma omp parallel for
|
2012-01-04 16:28:54 -06:00
|
|
|
for (int i = 0; i < n; ++i) {
|
2012-01-05 02:34:05 -06:00
|
|
|
output_mu[phase_usage_.num_phases*i + phase] = data1_[i];
|
2012-01-04 16:28:54 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void BlackoilPvtProperties::B(const int n,
|
|
|
|
const double* p,
|
|
|
|
const double* z,
|
|
|
|
double* output_B) const
|
|
|
|
{
|
|
|
|
data1_.resize(n);
|
2012-01-05 02:34:05 -06:00
|
|
|
for (int phase = 0; phase < phase_usage_.num_phases; ++phase) {
|
2012-01-04 16:28:54 -06:00
|
|
|
props_[phase]->B(n, p, z, &data1_[0]);
|
2012-04-10 08:46:24 -05:00
|
|
|
// #pragma omp parallel for
|
2012-01-04 16:28:54 -06:00
|
|
|
for (int i = 0; i < n; ++i) {
|
2012-01-05 02:34:05 -06:00
|
|
|
output_B[phase_usage_.num_phases*i + phase] = data1_[i];
|
2012-01-04 16:28:54 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void BlackoilPvtProperties::dBdp(const int n,
|
|
|
|
const double* p,
|
|
|
|
const double* z,
|
|
|
|
double* output_B,
|
|
|
|
double* output_dBdp) const
|
|
|
|
{
|
|
|
|
data1_.resize(n);
|
|
|
|
data2_.resize(n);
|
2012-01-05 02:34:05 -06:00
|
|
|
for (int phase = 0; phase < phase_usage_.num_phases; ++phase) {
|
2012-01-04 16:28:54 -06:00
|
|
|
props_[phase]->dBdp(n, p, z, &data1_[0], &data2_[0]);
|
2012-04-10 08:46:24 -05:00
|
|
|
// #pragma omp parallel for
|
2012-01-04 16:28:54 -06:00
|
|
|
for (int i = 0; i < n; ++i) {
|
2012-01-05 02:34:05 -06:00
|
|
|
output_B[phase_usage_.num_phases*i + phase] = data1_[i];
|
|
|
|
output_dBdp[phase_usage_.num_phases*i + phase] = data2_[i];
|
2012-01-04 16:28:54 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void BlackoilPvtProperties::R(const int n,
|
|
|
|
const double* p,
|
|
|
|
const double* z,
|
|
|
|
double* output_R) const
|
|
|
|
{
|
|
|
|
data1_.resize(n);
|
2012-01-05 02:34:05 -06:00
|
|
|
for (int phase = 0; phase < phase_usage_.num_phases; ++phase) {
|
2012-01-04 16:28:54 -06:00
|
|
|
props_[phase]->R(n, p, z, &data1_[0]);
|
2012-04-10 08:46:24 -05:00
|
|
|
// #pragma omp parallel for
|
2012-01-04 16:28:54 -06:00
|
|
|
for (int i = 0; i < n; ++i) {
|
2012-01-05 02:34:05 -06:00
|
|
|
output_R[phase_usage_.num_phases*i + phase] = data1_[i];
|
2012-01-04 16:28:54 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void BlackoilPvtProperties::dRdp(const int n,
|
|
|
|
const double* p,
|
|
|
|
const double* z,
|
|
|
|
double* output_R,
|
|
|
|
double* output_dRdp) const
|
|
|
|
{
|
|
|
|
data1_.resize(n);
|
|
|
|
data2_.resize(n);
|
2012-01-05 02:34:05 -06:00
|
|
|
for (int phase = 0; phase < phase_usage_.num_phases; ++phase) {
|
2012-01-04 16:28:54 -06:00
|
|
|
props_[phase]->dRdp(n, p, z, &data1_[0], &data2_[0]);
|
2012-04-10 08:46:24 -05:00
|
|
|
// #pragma omp parallel for
|
2012-01-04 16:28:54 -06:00
|
|
|
for (int i = 0; i < n; ++i) {
|
2012-01-05 02:34:05 -06:00
|
|
|
output_R[phase_usage_.num_phases*i + phase] = data1_[i];
|
|
|
|
output_dRdp[phase_usage_.num_phases*i + phase] = data2_[i];
|
2012-01-04 16:28:54 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Opm
|