/* Copyright 2015 Andreas Lauser 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 . */ #ifndef OPM_THERMAL_WATER_PVT_WRAPPER_HPP #define OPM_THERMAL_WATER_PVT_WRAPPER_HPP #include #include #include #include namespace Opm { /// Class which wraps another (i.e., isothermal) PVT object into one which adds /// temperature dependence of water class ThermalWaterPvtWrapper : public PvtInterface { public: ThermalWaterPvtWrapper() {} /// set the tables which specify the temperature dependence of the water viscosity void initFromDeck(std::shared_ptr isothermalPvt, Opm::DeckConstPtr deck, Opm::EclipseStateConstPtr eclipseState) { isothermalPvt_ = isothermalPvt; watvisctTables_ = 0; // stuff which we need to get from the PVTW keyword Opm::DeckKeywordConstPtr pvtwKeyword = deck->getKeyword("PVTW"); int numRegions = pvtwKeyword->size(); pvtwRefPress_.resize(numRegions); pvtwRefB_.resize(numRegions); pvtwCompressibility_.resize(numRegions); pvtwViscosity_.resize(numRegions); pvtwViscosibility_.resize(numRegions); for (int regionIdx = 0; regionIdx < numRegions; ++ regionIdx) { Opm::DeckRecordConstPtr pvtwRecord = pvtwKeyword->getRecord(regionIdx); pvtwRefPress_[regionIdx] = pvtwRecord->getItem("P_REF")->getSIDouble(0); pvtwRefB_[regionIdx] = pvtwRecord->getItem("WATER_VOL_FACTOR")->getSIDouble(0); pvtwViscosity_[regionIdx] = pvtwRecord->getItem("WATER_VISCOSITY")->getSIDouble(0); pvtwViscosibility_[regionIdx] = pvtwRecord->getItem("WATER_VISCOSIBILITY")->getSIDouble(0); } // quantities required for the temperature dependence of the viscosity // (basically we expect well-behaved VISCREF and WATVISCT keywords.) if (deck->hasKeyword("VISCREF")) { watvisctTables_ = &eclipseState->getWatvisctTables(); Opm::DeckKeywordConstPtr viscrefKeyword = deck->getKeyword("VISCREF"); assert(int(watvisctTables_->size()) == numRegions); assert(int(viscrefKeyword->size()) == numRegions); viscrefPress_.resize(numRegions); for (int regionIdx = 0; regionIdx < numRegions; ++ regionIdx) { Opm::DeckRecordConstPtr viscrefRecord = viscrefKeyword->getRecord(regionIdx); viscrefPress_[regionIdx] = viscrefRecord->getItem("REFERENCE_PRESSURE")->getSIDouble(0); } } // quantities required for the temperature dependence of the density if (deck->hasKeyword("WATDENT")) { DeckKeywordConstPtr watdentKeyword = deck->getKeyword("WATDENT"); assert(int(watdentKeyword->size()) == numRegions); watdentRefTemp_.resize(numRegions); watdentCT1_.resize(numRegions); watdentCT2_.resize(numRegions); for (int regionIdx = 0; regionIdx < numRegions; ++regionIdx) { Opm::DeckRecordConstPtr watdentRecord = watdentKeyword->getRecord(regionIdx); watdentRefTemp_[regionIdx] = watdentRecord->getItem("REFERENCE_TEMPERATURE")->getSIDouble(0); watdentCT1_[regionIdx] = watdentRecord->getItem("EXPANSION_COEFF_LINEAR")->getSIDouble(0); watdentCT2_[regionIdx] = watdentRecord->getItem("EXPANSION_COEFF_QUADRATIC")->getSIDouble(0); } } } virtual void mu(const int n, const int* pvtRegionIdx, const double* p, const double* T, const double* z, double* output_mu) const { if (watvisctTables_) // TODO: temperature dependence for viscosity depending on z OPM_THROW(std::runtime_error, "temperature dependent viscosity as a function of z " "is not yet implemented!"); // compute the isothermal viscosity isothermalPvt_->mu(n, pvtRegionIdx, p, T, z, output_mu); } virtual void mu(const int n, const int* pvtRegionIdx, const double* p, const double* T, const double* r, double* output_mu, double* output_dmudp, double* output_dmudr) const { // compute the isothermal viscosity and its derivatives isothermalPvt_->mu(n, pvtRegionIdx, p, T, r, output_mu, output_dmudp, output_dmudr); if (!watvisctTables_) // isothermal case return; // temperature dependence for (int i = 0; i < n; ++i) { int tableIdx = getTableIndex_(pvtRegionIdx, i); // calculate the viscosity of the isothermal keyword for the reference // pressure given by the VISCREF keyword. double x = -pvtwViscosibility_[tableIdx]*(viscrefPress_[tableIdx] - pvtwRefPress_[tableIdx]); double muRef = pvtwViscosity_[tableIdx]/(1.0 + x + 0.5*x*x); // compute the viscosity deviation due to temperature double muWatvisct = (*watvisctTables_)[tableIdx].evaluate("Viscosity", T[i]); double alpha = muWatvisct/muRef; output_mu[i] *= alpha; output_dmudp[i] *= alpha; output_dmudr[i] *= alpha; // TODO (?): derivative of viscosity w.r.t. temperature. } } virtual void mu(const int n, const int* pvtRegionIdx, const double* p, const double* T, const double* r, const PhasePresence* cond, double* output_mu, double* output_dmudp, double* output_dmudr) const { // compute the isothermal viscosity and its derivatives isothermalPvt_->mu(n, pvtRegionIdx, p, T, r, cond, output_mu, output_dmudp, output_dmudr); if (!watvisctTables_) // isothermal case return; // temperature dependence for (int i = 0; i < n; ++i) { int tableIdx = getTableIndex_(pvtRegionIdx, i); // calculate the viscosity of the isothermal keyword for the reference // pressure given by the VISCREF keyword. double x = -pvtwViscosibility_[tableIdx]*(viscrefPress_[tableIdx] - pvtwRefPress_[tableIdx]); double muRef = pvtwViscosity_[tableIdx]/(1.0 + x + 0.5*x*x); // compute the viscosity deviation due to temperature double muWatvisct = (*watvisctTables_)[tableIdx].evaluate("Viscosity", T[i]); double alpha = muWatvisct/muRef; output_mu[i] *= alpha; output_dmudp[i] *= alpha; output_dmudr[i] *= alpha; // TODO (?): derivative of viscosity w.r.t. temperature. } } virtual void B(const int n, const int* pvtRegionIdx, const double* p, const double* T, const double* z, double* output_B) const { if (watdentRefTemp_.empty()) { // isothermal case isothermalPvt_->B(n, pvtRegionIdx, p, T, z, output_B); return; } // This changes how the water density depends on pressure compared to what's // used for the PVTW keyword, but it seems to be what Eclipse does. For // details, see the documentation for the WATDENT keyword in the Eclipse RM. for (int i = 0; i < n; ++i) { int tableIdx = getTableIndex_(pvtRegionIdx, i); double BwRef = pvtwRefB_[tableIdx]; double TRef = watdentRefTemp_[tableIdx]; double X = pvtwCompressibility_[tableIdx]*(p[i] - pvtwRefPress_[tableIdx]); double cT1 = watdentCT1_[tableIdx]; double cT2 = watdentCT2_[tableIdx]; double Y = T[i] - TRef; double Bw = BwRef*(1 - X)*(1 + cT1*Y + cT2*Y*Y); output_B[i] = Bw; } } virtual void dBdp(const int n, const int* pvtRegionIdx, const double* p, const double* T, const double* z, double* output_B, double* output_dBdp) const { if (watdentRefTemp_.empty()) { // isothermal case isothermalPvt_->dBdp(n, pvtRegionIdx, p, T, z, output_B, output_dBdp); return; } // This changes how the water density depends on pressure. This is awkward, // but it seems to be what Eclipse does. See the documentation for the // WATDENT keyword in the Eclipse RM for (int i = 0; i < n; ++i) { int tableIdx = getTableIndex_(pvtRegionIdx, i); double BwRef = pvtwRefB_[tableIdx]; double TRef = watdentRefTemp_[tableIdx]; double X = pvtwCompressibility_[tableIdx]*(p[i] - pvtwRefPress_[tableIdx]); double cT1 = watdentCT1_[tableIdx]; double cT2 = watdentCT2_[tableIdx]; double Y = T[i] - TRef; double Bw = BwRef*(1 - X)*(1 + cT1*Y + cT2*Y*Y); output_B[i] = Bw; } std::fill(output_dBdp, output_dBdp + n, 0.0); } virtual void b(const int n, const int* pvtRegionIdx, const double* p, const double* T, const double* r, double* output_b, double* output_dbdp, double* output_dbdr) const { if (watdentRefTemp_.empty()) { // isothermal case isothermalPvt_->b(n, pvtRegionIdx, p, T, r, output_b, output_dbdp, output_dbdr); return; } // This changes how the water density depends on pressure. This is awkward, // but it seems to be what Eclipse does. See the documentation for the // WATDENT keyword in the Eclipse RM for (int i = 0; i < n; ++i) { int tableIdx = getTableIndex_(pvtRegionIdx, i); double BwRef = pvtwRefB_[tableIdx]; double TRef = watdentRefTemp_[tableIdx]; double X = pvtwCompressibility_[tableIdx]*(p[i] - pvtwRefPress_[tableIdx]); double cT1 = watdentCT1_[tableIdx]; double cT2 = watdentCT2_[tableIdx]; double Y = T[i] - TRef; double Bw = BwRef*(1 - X)*(1 + cT1*Y + cT2*Y*Y); output_b[i] = 1.0/Bw; } std::fill(output_dbdp, output_dbdp + n, 0.0); std::fill(output_dbdr, output_dbdr + n, 0.0); } virtual void b(const int n, const int* pvtRegionIdx, const double* p, const double* T, const double* r, const PhasePresence* cond, double* output_b, double* output_dbdp, double* output_dbdr) const { if (watdentRefTemp_.empty()) { // isothermal case isothermalPvt_->b(n, pvtRegionIdx, p, T, r, cond, output_b, output_dbdp, output_dbdr); return; } // This changes pressure dependence of the water density, but it seems to be // what Eclipse does. See the documentation for the WATDENT keyword in the // Eclipse RM for (int i = 0; i < n; ++i) { int tableIdx = getTableIndex_(pvtRegionIdx, i); double BwRef = pvtwRefB_[tableIdx]; double TRef = watdentRefTemp_[tableIdx]; double X = pvtwCompressibility_[tableIdx]*(p[i] - pvtwRefPress_[tableIdx]); double cT1 = watdentCT1_[tableIdx]; double cT2 = watdentCT2_[tableIdx]; double Y = T[i] - TRef; double Bw = BwRef*(1 - X)*(1 + cT1*Y + cT2*Y*Y); output_b[i] = 1.0/Bw; } std::fill(output_dbdp, output_dbdp + n, 0.0); std::fill(output_dbdr, output_dbdr + n, 0.0); } virtual void rsSat(const int n, const int* pvtRegionIdx, const double* p, double* output_rsSat, double* output_drsSatdp) const { isothermalPvt_->rsSat(n, pvtRegionIdx, p, output_rsSat, output_drsSatdp); } virtual void rvSat(const int n, const int* pvtRegionIdx, const double* p, double* output_rvSat, double* output_drvSatdp) const { isothermalPvt_->rvSat(n, pvtRegionIdx, p, output_rvSat, output_drvSatdp); } virtual void R(const int n, const int* pvtRegionIdx, const double* p, const double* z, double* output_R) const { isothermalPvt_->R(n, pvtRegionIdx, p, z, output_R); } virtual void dRdp(const int n, const int* pvtRegionIdx, const double* p, const double* z, double* output_R, double* output_dRdp) const { isothermalPvt_->dRdp(n, pvtRegionIdx, p, z, output_R, output_dRdp); } private: int getTableIndex_(const int* pvtTableIdx, int cellIdx) const { if (!pvtTableIdx) return 0; return pvtTableIdx[cellIdx]; } // the PVT propertied for the isothermal case std::shared_ptr isothermalPvt_; // The PVT properties needed for temperature dependence. We need to store one // value per PVT region. std::vector viscrefPress_; std::vector watdentRefTemp_; std::vector watdentCT1_; std::vector watdentCT2_; std::vector pvtwRefPress_; std::vector pvtwRefB_; std::vector pvtwCompressibility_; std::vector pvtwViscosity_; std::vector pvtwViscosibility_; const std::vector* watvisctTables_; }; } #endif