Files
opm-common/tests/test_pengrobinson.cpp
Andreas Lauser 6cb7df3541 remove the Opm::FluidSystems namespace
this has mildly annoyed me for quite some time, and finally managed to
bring myself to changing it: The Opm::FluidSystems namespace is pretty
useless because the number of classes contained within it is quite
small and mismatch between the naming convention of the file names the
actual classes is somewhat confusing IMO. Thus, this patch changes the
naming of fluid systems from `Opm::FluidSystems::Foo` to
`Opm::FooFluidSystem`.

(also, flat hierarchies currently seem to be popular with the cool
people!?)

this patch requires some simple mop-ups for `ewoms` and `opm-simulators`.
2018-07-27 12:57:09 +02:00

496 lines
20 KiB
C++

// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
// vi: set et ts=4 sw=4 sts=4:
/*
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 2 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/>.
Consult the COPYING file in the top-level source directory of this
module for the precise wording of the license and the list of
copyright holders.
*/
/*!
* \file
*
* \brief This is test for the SPE5 fluid system (which uses the
* Peng-Robinson EOS) and the NCP flash solver.
*/
#include "config.h"
#include <opm/material/densead/Evaluation.hpp>
#include <opm/material/constraintsolvers/ComputeFromReferencePhase.hpp>
#include <opm/material/constraintsolvers/NcpFlash.hpp>
#include <opm/material/fluidstates/CompositionalFluidState.hpp>
#include <opm/material/fluidsystems/Spe5FluidSystem.hpp>
#include <opm/material/fluidmatrixinteractions/LinearMaterial.hpp>
#include <opm/material/fluidmatrixinteractions/MaterialTraits.hpp>
#include <dune/common/parallel/mpihelper.hh>
template <class FluidSystem, class FluidState>
void createSurfaceGasFluidSystem(FluidState& gasFluidState)
{
static const int gasPhaseIdx = FluidSystem::gasPhaseIdx;
// temperature
gasFluidState.setTemperature(273.15 + 20);
// gas pressure
gasFluidState.setPressure(gasPhaseIdx, 1e5);
// gas saturation
gasFluidState.setSaturation(gasPhaseIdx, 1.0);
// gas composition: mostly methane, a bit of propane
gasFluidState.setMoleFraction(gasPhaseIdx, FluidSystem::H2OIdx, 0.0);
gasFluidState.setMoleFraction(gasPhaseIdx, FluidSystem::C1Idx, 0.94);
gasFluidState.setMoleFraction(gasPhaseIdx, FluidSystem::C3Idx, 0.06);
gasFluidState.setMoleFraction(gasPhaseIdx, FluidSystem::C6Idx, 0.00);
gasFluidState.setMoleFraction(gasPhaseIdx, FluidSystem::C10Idx, 0.00);
gasFluidState.setMoleFraction(gasPhaseIdx, FluidSystem::C15Idx, 0.00);
gasFluidState.setMoleFraction(gasPhaseIdx, FluidSystem::C20Idx, 0.00);
// gas density
typename FluidSystem::template ParameterCache<typename FluidState::Scalar> paramCache;
paramCache.updatePhase(gasFluidState, gasPhaseIdx);
gasFluidState.setDensity(gasPhaseIdx,
FluidSystem::density(gasFluidState, paramCache, gasPhaseIdx));
}
template <class Scalar, class FluidSystem, class FluidState>
Scalar computeSumxg(FluidState& resultFluidState,
const FluidState& prestineFluidState,
const FluidState& gasFluidState,
Scalar additionalGas)
{
static const int oilPhaseIdx = FluidSystem::oilPhaseIdx;
static const int gasPhaseIdx = FluidSystem::gasPhaseIdx;
static const int numComponents = FluidSystem::numComponents;
typedef Dune::FieldVector<Scalar, numComponents> ComponentVector;
typedef Opm::NcpFlash<Scalar, FluidSystem> Flash;
resultFluidState.assign(prestineFluidState);
// add a bit of additional gas components
ComponentVector totalMolarities;
for (unsigned compIdx = 0; compIdx < FluidSystem::numComponents; ++ compIdx)
totalMolarities =
prestineFluidState.molarity(oilPhaseIdx, compIdx)
+ additionalGas*gasFluidState.moleFraction(gasPhaseIdx, compIdx);
// "flash" the modified fluid state
typename FluidSystem::ParameterCache paramCache;
Flash::solve(resultFluidState, totalMolarities);
Scalar sumxg = 0;
for (unsigned compIdx = 0; compIdx < FluidSystem::numComponents; ++compIdx)
sumxg += resultFluidState.moleFraction(gasPhaseIdx, compIdx);
return sumxg;
}
template <class Scalar, class FluidSystem, class FluidState>
void makeOilSaturated(FluidState& fluidState, const FluidState& gasFluidState)
{
static const int gasPhaseIdx = FluidSystem::gasPhaseIdx;
FluidState prestineFluidState;
prestineFluidState.assign(fluidState);
Scalar sumxg = 0;
for (unsigned compIdx = 0; compIdx < FluidSystem::numComponents; ++compIdx)
sumxg += fluidState.moleFraction(gasPhaseIdx, compIdx);
// Newton method
Scalar tol = 1e-8;
Scalar additionalGas = 0; // [mol]
for (int i = 0; std::abs(sumxg - 1) > tol; ++i) {
if (i > 50)
throw std::runtime_error("Newton method did not converge after 50 iterations");
Scalar eps = std::max(1e-8, additionalGas*1e-8);
Scalar f = 1 - computeSumxg<Scalar, FluidSystem>(prestineFluidState,
fluidState,
gasFluidState,
additionalGas);
Scalar fStar = 1 - computeSumxg<Scalar, FluidSystem>(prestineFluidState,
fluidState,
gasFluidState,
additionalGas + eps);
Scalar fPrime = (fStar - f)/eps;
additionalGas -= f/fPrime;
};
}
template <class FluidSystem, class FluidState>
void guessInitial(FluidState& fluidState, unsigned phaseIdx)
{
if (phaseIdx == FluidSystem::gasPhaseIdx) {
fluidState.setMoleFraction(phaseIdx, FluidSystem::H2OIdx, 0.0);
fluidState.setMoleFraction(phaseIdx, FluidSystem::C1Idx, 0.74785);
fluidState.setMoleFraction(phaseIdx, FluidSystem::C3Idx, 0.0121364);
fluidState.setMoleFraction(phaseIdx, FluidSystem::C6Idx, 0.00606028);
fluidState.setMoleFraction(phaseIdx, FluidSystem::C10Idx, 0.00268136);
fluidState.setMoleFraction(phaseIdx, FluidSystem::C15Idx, 0.000204256);
fluidState.setMoleFraction(phaseIdx, FluidSystem::C20Idx, 8.78291e-06);
}
else if (phaseIdx == FluidSystem::oilPhaseIdx) {
fluidState.setMoleFraction(phaseIdx, FluidSystem::H2OIdx, 0.0);
fluidState.setMoleFraction(phaseIdx, FluidSystem::C1Idx, 0.50);
fluidState.setMoleFraction(phaseIdx, FluidSystem::C3Idx, 0.03);
fluidState.setMoleFraction(phaseIdx, FluidSystem::C6Idx, 0.07);
fluidState.setMoleFraction(phaseIdx, FluidSystem::C10Idx, 0.20);
fluidState.setMoleFraction(phaseIdx, FluidSystem::C15Idx, 0.15);
fluidState.setMoleFraction(phaseIdx, FluidSystem::C20Idx, 0.05);
}
else {
assert(phaseIdx == FluidSystem::waterPhaseIdx);
}
}
template <class Scalar, class FluidSystem, class FluidState>
Scalar bringOilToSurface(FluidState& surfaceFluidState, Scalar alpha, const FluidState& reservoirFluidState, bool guessInitial)
{
enum {
numPhases = FluidSystem::numPhases,
waterPhaseIdx = FluidSystem::waterPhaseIdx,
gasPhaseIdx = FluidSystem::gasPhaseIdx,
oilPhaseIdx = FluidSystem::oilPhaseIdx,
numComponents = FluidSystem::numComponents
};
typedef Opm::NcpFlash<Scalar, FluidSystem> Flash;
typedef Opm::ThreePhaseMaterialTraits<Scalar, waterPhaseIdx, oilPhaseIdx, gasPhaseIdx> MaterialTraits;
typedef Opm::LinearMaterial<MaterialTraits> MaterialLaw;
typedef typename MaterialLaw::Params MaterialLawParams;
typedef Dune::FieldVector<Scalar, numComponents> ComponentVector;
const Scalar refPressure = 1.0135e5; // [Pa]
// set the parameters for the capillary pressure law
MaterialLawParams matParams;
for (unsigned phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) {
matParams.setPcMinSat(phaseIdx, 0.0);
matParams.setPcMaxSat(phaseIdx, 0.0);
}
matParams.finalize();
// retieve the global volumetric component molarities
surfaceFluidState.setTemperature(273.15 + 20);
ComponentVector molarities;
for (unsigned compIdx = 0; compIdx < numComponents; ++ compIdx)
molarities[compIdx] = reservoirFluidState.molarity(oilPhaseIdx, compIdx);
if (guessInitial) {
// we start at a fluid state with reservoir oil.
for (unsigned phaseIdx = 0; phaseIdx < numPhases; ++ phaseIdx) {
for (unsigned compIdx = 0; compIdx < numComponents; ++ compIdx) {
surfaceFluidState.setMoleFraction(phaseIdx,
compIdx,
reservoirFluidState.moleFraction(phaseIdx, compIdx));
}
surfaceFluidState.setDensity(phaseIdx, reservoirFluidState.density(phaseIdx));
surfaceFluidState.setPressure(phaseIdx, reservoirFluidState.pressure(phaseIdx));
surfaceFluidState.setSaturation(phaseIdx, 0.0);
}
surfaceFluidState.setSaturation(oilPhaseIdx, 1.0);
surfaceFluidState.setSaturation(gasPhaseIdx, 1.0 - surfaceFluidState.saturation(oilPhaseIdx));
}
typename FluidSystem::template ParameterCache<Scalar> paramCache;
paramCache.updateAll(surfaceFluidState);
// increase volume until we are at surface pressure. use the
// newton method for this
ComponentVector tmpMolarities;
for (int i = 0;; ++i) {
if (i >= 20)
throw Opm::NumericalIssue("Newton method did not converge after 20 iterations");
// calculate the deviation from the standard pressure
tmpMolarities = molarities;
tmpMolarities /= alpha;
Flash::template solve<MaterialLaw>(surfaceFluidState, matParams, paramCache, tmpMolarities);
Scalar f = surfaceFluidState.pressure(gasPhaseIdx) - refPressure;
// calculate the derivative of the deviation from the standard
// pressure
Scalar eps = alpha*1e-10;
tmpMolarities = molarities;
tmpMolarities /= alpha + eps;
Flash::template solve<MaterialLaw>(surfaceFluidState, matParams, paramCache, tmpMolarities);
Scalar fStar = surfaceFluidState.pressure(gasPhaseIdx) - refPressure;
Scalar fPrime = (fStar - f)/eps;
// newton update
Scalar delta = f/fPrime;
alpha -= delta;
if (std::abs(delta) < std::abs(alpha)*1e-9) {
break;
}
}
// calculate the final result
tmpMolarities = molarities;
tmpMolarities /= alpha;
Flash::template solve<MaterialLaw>(surfaceFluidState, matParams, paramCache, tmpMolarities);
return alpha;
}
template <class RawTable>
void printResult(const RawTable& rawTable,
const std::string& fieldName,
size_t firstIdx,
size_t secondIdx,
double hiresThres)
{
std::cout << "std::vector<std::pair<Scalar, Scalar> > "<<fieldName<<" = {\n";
size_t sampleIdx = 0;
size_t numSamples = 20;
size_t numRawHires = 0;
for (; rawTable[numRawHires][firstIdx] > hiresThres; ++numRawHires)
{}
for (; sampleIdx < numSamples; ++sampleIdx) {
size_t rawIdx = sampleIdx*numRawHires/numSamples;
std::cout << "{ " << rawTable[rawIdx][firstIdx] << ", "
<< rawTable[rawIdx][secondIdx] << " }"
<< ",\n";
}
numSamples = 15;
for (sampleIdx = 0; sampleIdx < numSamples; ++sampleIdx) {
size_t rawIdx = sampleIdx*(rawTable.size() - numRawHires)/numSamples + numRawHires;
std::cout << "{ " << rawTable[rawIdx][firstIdx] << ", "
<< rawTable[rawIdx][secondIdx] << " }";
if (sampleIdx < numSamples - 1)
std::cout << ",\n";
else
std::cout << "\n";
}
std::cout << "};\n";
}
template <class Scalar>
inline void testAll()
{
typedef Opm::Spe5FluidSystem<Scalar> FluidSystem;
enum {
numPhases = FluidSystem::numPhases,
waterPhaseIdx = FluidSystem::waterPhaseIdx,
gasPhaseIdx = FluidSystem::gasPhaseIdx,
oilPhaseIdx = FluidSystem::oilPhaseIdx,
numComponents = FluidSystem::numComponents,
H2OIdx = FluidSystem::H2OIdx,
C1Idx = FluidSystem::C1Idx,
C3Idx = FluidSystem::C3Idx,
C6Idx = FluidSystem::C6Idx,
C10Idx = FluidSystem::C10Idx,
C15Idx = FluidSystem::C15Idx,
C20Idx = FluidSystem::C20Idx
};
typedef Opm::NcpFlash<Scalar, FluidSystem> Flash;
typedef Dune::FieldVector<Scalar, numComponents> ComponentVector;
typedef Opm::CompositionalFluidState<Scalar, FluidSystem> FluidState;
typedef Opm::ThreePhaseMaterialTraits<Scalar, waterPhaseIdx, oilPhaseIdx, gasPhaseIdx> MaterialTraits;
typedef Opm::LinearMaterial<MaterialTraits> MaterialLaw;
typedef typename MaterialLaw::Params MaterialLawParams;
typedef typename FluidSystem::template ParameterCache<Scalar> ParameterCache;
////////////
// Initialize the fluid system and create the capillary pressure
// parameters
////////////
Scalar T = 273.15 + 20; // 20 deg Celsius
FluidSystem::init(/*minTemperature=*/T - 1,
/*maxTemperature=*/T + 1,
/*minPressure=*/1.0e4,
/*maxTemperature=*/40.0e6);
// set the parameters for the capillary pressure law
MaterialLawParams matParams;
for (unsigned phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) {
matParams.setPcMinSat(phaseIdx, 0.0);
matParams.setPcMaxSat(phaseIdx, 0.0);
}
matParams.finalize();
////////////
// Create a fluid state
////////////
FluidState gasFluidState;
createSurfaceGasFluidSystem<FluidSystem>(gasFluidState);
FluidState fluidState;
ParameterCache paramCache;
// temperature
fluidState.setTemperature(T);
// oil pressure
fluidState.setPressure(oilPhaseIdx, 4000 * 6894.7573); // 4000 PSI
// oil saturation
fluidState.setSaturation(oilPhaseIdx, 1.0);
fluidState.setSaturation(gasPhaseIdx, 1.0 - fluidState.saturation(oilPhaseIdx));
// oil composition: SPE-5 reservoir oil
fluidState.setMoleFraction(oilPhaseIdx, H2OIdx, 0.0);
fluidState.setMoleFraction(oilPhaseIdx, C1Idx, 0.50);
fluidState.setMoleFraction(oilPhaseIdx, C3Idx, 0.03);
fluidState.setMoleFraction(oilPhaseIdx, C6Idx, 0.07);
fluidState.setMoleFraction(oilPhaseIdx, C10Idx, 0.20);
fluidState.setMoleFraction(oilPhaseIdx, C15Idx, 0.15);
fluidState.setMoleFraction(oilPhaseIdx, C20Idx, 0.05);
//makeOilSaturated<Scalar, FluidSystem>(fluidState, gasFluidState);
// set the saturations and pressures of the other phases
for (unsigned phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) {
if (phaseIdx != oilPhaseIdx) {
fluidState.setSaturation(phaseIdx, 0.0);
fluidState.setPressure(phaseIdx, fluidState.pressure(oilPhaseIdx));
}
// initial guess for the composition (needed by the ComputeFromReferencePhase
// constraint solver. TODO: bug in ComputeFromReferencePhase?)
guessInitial<FluidSystem>(fluidState, phaseIdx);
}
typedef Opm::ComputeFromReferencePhase<Scalar, FluidSystem> CFRP;
CFRP::solve(fluidState,
paramCache,
/*refPhaseIdx=*/oilPhaseIdx,
/*setViscosity=*/false,
/*setEnthalpy=*/false);
////////////
// Calculate the total molarities of the components
////////////
ComponentVector totalMolarities;
for (unsigned compIdx = 0; compIdx < numComponents; ++ compIdx)
totalMolarities[compIdx] = fluidState.saturation(oilPhaseIdx)*fluidState.molarity(oilPhaseIdx, compIdx);
////////////
// Gradually increase the volume for and calculate the gas
// formation factor, oil formation volume factor and gas formation
// volume factor.
////////////
FluidState flashFluidState, surfaceFluidState;
flashFluidState.assign(fluidState);
//Flash::guessInitial(flashFluidState, totalMolarities);
Flash::template solve<MaterialLaw>(flashFluidState, matParams, paramCache, totalMolarities);
Scalar surfaceAlpha = 1;
surfaceAlpha = bringOilToSurface<Scalar, FluidSystem>(surfaceFluidState, surfaceAlpha, flashFluidState, /*guessInitial=*/true);
Scalar rho_gRef = surfaceFluidState.density(gasPhaseIdx);
Scalar rho_oRef = surfaceFluidState.density(oilPhaseIdx);
std::vector<std::array<Scalar, 10> > resultTable;
Scalar minAlpha = 0.98;
Scalar maxAlpha = surfaceAlpha;
std::cout << "alpha[-] p[Pa] S_g[-] rho_o[kg/m^3] rho_g[kg/m^3] <M_o>[kg/mol] <M_g>[kg/mol] R_s[m^3/m^3] B_g[-] B_o[-]\n";
int n = 300;
for (int i = 0; i < n; ++i) {
// ratio between the original and the current volume
Scalar alpha = minAlpha + (maxAlpha - minAlpha)*i/(n - 1);
// increasing the volume means decreasing the molartity
ComponentVector curTotalMolarities = totalMolarities;
curTotalMolarities /= alpha;
// "flash" the modified reservoir oil
Flash::template solve<MaterialLaw>(flashFluidState, matParams, paramCache, curTotalMolarities);
surfaceAlpha = bringOilToSurface<Scalar, FluidSystem>(surfaceFluidState,
surfaceAlpha,
flashFluidState,
/*guessInitial=*/false);
Scalar Rs =
surfaceFluidState.saturation(gasPhaseIdx)
/ surfaceFluidState.saturation(oilPhaseIdx);
std::cout << alpha << " "
<< flashFluidState.pressure(oilPhaseIdx) << " "
<< flashFluidState.saturation(gasPhaseIdx) << " "
<< flashFluidState.density(oilPhaseIdx) << " "
<< flashFluidState.density(gasPhaseIdx) << " "
<< flashFluidState.averageMolarMass(oilPhaseIdx) << " "
<< flashFluidState.averageMolarMass(gasPhaseIdx) << " "
<< Rs << " "
<< rho_gRef/flashFluidState.density(gasPhaseIdx) << " "
<< rho_oRef/flashFluidState.density(oilPhaseIdx) << " "
<< "\n";
std::array<Scalar, 10> tmp;
tmp[0] = alpha;
tmp[1] = flashFluidState.pressure(oilPhaseIdx);
tmp[2] = flashFluidState.saturation(gasPhaseIdx);
tmp[3] = flashFluidState.density(oilPhaseIdx);
tmp[4] = flashFluidState.density(gasPhaseIdx);
tmp[5] = flashFluidState.averageMolarMass(oilPhaseIdx);
tmp[6] = flashFluidState.averageMolarMass(gasPhaseIdx);
tmp[7] = Rs;
tmp[8] = rho_gRef/flashFluidState.density(gasPhaseIdx);
tmp[9] = rho_oRef/flashFluidState.density(oilPhaseIdx);
resultTable.push_back(tmp);
}
std::cout << "reference density oil [kg/m^3]: " << rho_oRef << "\n";
std::cout << "reference density gas [kg/m^3]: " << rho_gRef << "\n";
Scalar hiresThresholdPressure = resultTable[20][1];
printResult(resultTable,
"Bg", /*firstIdx=*/1, /*secondIdx=*/8,
/*hiresThreshold=*/hiresThresholdPressure);
printResult(resultTable,
"Bo", /*firstIdx=*/1, /*secondIdx=*/9,
/*hiresThreshold=*/hiresThresholdPressure);
printResult(resultTable,
"Rs", /*firstIdx=*/1, /*secondIdx=*/7,
/*hiresThreshold=*/hiresThresholdPressure);
}
int main(int argc, char **argv)
{
Dune::MPIHelper::instance(argc, argv);
testAll<double>();
// the Peng-Robinson test currently does not work with single-precision floating
// point scalars because of precision issues. (these are caused by the fact that the
// test uses finite differences to calculate derivatives.)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunreachable-code"
while (0) testAll<float>();
#pragma GCC diagnostic pop
return 0;
}