mirror of
https://github.com/OPM/opm-upscaling.git
synced 2025-02-25 18:45:23 -06:00
This means that PINCH must be used in the input deck, whereas before, z_tolerance was available as a program parameter (but only for some programs). A benefit is that all programs will now uniformly accept PINCH in the input decks.
452 lines
19 KiB
C++
452 lines
19 KiB
C++
//===========================================================================
|
|
//
|
|
// File: steadystate_test.cpp
|
|
//
|
|
// Created: Fri Aug 28 14:11:03 2009
|
|
//
|
|
// Author(s): Kari B. Skjerve <karbor@statoil.com>
|
|
//
|
|
// $Date$
|
|
//
|
|
// $Revision$
|
|
//
|
|
//===========================================================================
|
|
|
|
/*
|
|
Copyright 2009, 2010 SINTEF ICT, Applied Mathematics.
|
|
Copyright 2009, 2010 Statoil ASA.
|
|
|
|
This file is part of The Open Reservoir Simulator Project (OpenRS).
|
|
|
|
OpenRS 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.
|
|
|
|
OpenRS 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 OpenRS. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include <config.h>
|
|
|
|
|
|
//#define VERBOSE
|
|
#include <opm/upscaling/SteadyStateUpscalerImplicit.hpp>
|
|
#include <opm/upscaling/SteadyStateUpscalerManagerImplicit.hpp>
|
|
#include <opm/porsol/euler/EulerUpstreamImplicit.hpp>
|
|
#include <opm/porsol/common/SimulatorTraits.hpp>
|
|
#include <opm/core/utility/MonotCubicInterpolator.hpp>
|
|
#include <opm/upscaling/SinglePhaseUpscaler.hpp>
|
|
#include <opm/upscaling/ParserAdditions.hpp>
|
|
#include <sys/utsname.h>
|
|
#include <iostream>
|
|
|
|
#include <dune/common/version.hh>
|
|
#if DUNE_VERSION_NEWER(DUNE_COMMON, 2, 3)
|
|
#include <dune/common/parallel/mpihelper.hh>
|
|
#else
|
|
#include <dune/common/mpihelper.hh>
|
|
#endif
|
|
|
|
namespace Opm{
|
|
template <class IsotropyPolicy>
|
|
struct Implicit
|
|
{
|
|
template <class GridInterface, class BoundaryConditions>
|
|
struct TransportSolver
|
|
{
|
|
//enum { Dimension = GridInterface::Dimension };
|
|
enum { Dimension = GridInterface::Dimension };
|
|
typedef typename IsotropyPolicy::template ResProp<Dimension>::Type RP;
|
|
|
|
typedef EulerUpstreamImplicit<GridInterface,
|
|
RP,
|
|
BoundaryConditions> Type;
|
|
|
|
};
|
|
};
|
|
typedef SimulatorTraits<Isotropic, Implicit> UpscalingTraitsBasicImplicit;
|
|
}
|
|
using namespace Opm;
|
|
|
|
void usage()
|
|
{
|
|
std::cout << "Usage: upscale_steadystate_implicit gridfilename=filename.grdecl " << std::endl;
|
|
std::cout << " rock_list=rocklist.txt [outputWater=] [outputOil=] " << std::endl;
|
|
std::cout << " [bc=fixed] [num_sats=10] [num_pdrops=10] " << std::endl;
|
|
std::cout << " [anisotropicrocks=false]" << std::endl;
|
|
}
|
|
|
|
void usageandexit() {
|
|
usage();
|
|
exit(1);
|
|
}
|
|
|
|
// Assumes that permtensor_t use C ordering.
|
|
double getVoigtValue(const SinglePhaseUpscaler::permtensor_t& K, int voigt_idx)
|
|
{
|
|
assert(K.numRows() == 3 && K.numCols() == 3);
|
|
switch (voigt_idx) {
|
|
case 0: return K.data()[0];
|
|
case 1: return K.data()[4];
|
|
case 2: return K.data()[8];
|
|
case 3: return K.data()[5];
|
|
case 4: return K.data()[2];
|
|
case 5: return K.data()[1];
|
|
case 6: return K.data()[7];
|
|
case 7: return K.data()[6];
|
|
case 8: return K.data()[3];
|
|
default:
|
|
std::cout << "Voigt index out of bounds (only 0-8 allowed)" << std::endl;
|
|
throw std::exception();
|
|
}
|
|
}
|
|
|
|
std::vector<std::vector<double> > getExtremeSats(std::string rock_list, std::vector<std::string>& rockfilelist, bool anisorocks=false) {
|
|
if (!rock_list.compare("no_list")) {
|
|
std::cout << "Need rock_list to compute saturation limits (" << rock_list << ")" << std::endl;
|
|
throw std::exception();
|
|
}
|
|
std::ifstream rl(rock_list.c_str());
|
|
if (!rl) {
|
|
OPM_THROW(std::runtime_error, "Could not open file " << rock_list);
|
|
}
|
|
int num_rocks = -1;
|
|
rl >> num_rocks;
|
|
assert(num_rocks >= 1);
|
|
std::vector<std::vector<double> > rocksatendp;
|
|
rocksatendp.resize(num_rocks);
|
|
for (int i = 0; i < num_rocks; ++i) {
|
|
rocksatendp[i].resize(2);
|
|
std::string spec;
|
|
while (spec.empty()) {
|
|
std::getline(rl, spec);
|
|
}
|
|
// Read the contents of the i'th rock
|
|
std::istringstream specstream(spec);
|
|
std::string rockname;
|
|
specstream >> rockname;
|
|
std::string rockfilename = rockname;
|
|
rockfilelist.push_back(rockfilename);
|
|
std::ifstream rock_stream(rockfilename.c_str());
|
|
if (!rock_stream) {
|
|
OPM_THROW(std::runtime_error, "Could not open file " << rockfilename);
|
|
}
|
|
|
|
if (! anisorocks) { //Isotropic input rocks (Sw Krw Kro J)
|
|
MonotCubicInterpolator Jtmp;
|
|
try {
|
|
Jtmp = MonotCubicInterpolator(rockname, 1, 4);
|
|
}
|
|
catch (const char * errormessage) {
|
|
std::cerr << "Error: " << errormessage << std::endl;
|
|
std::cerr << "Check filename" << std::endl;
|
|
exit(1);
|
|
}
|
|
rocksatendp[i][0] = Jtmp.getMinimumX().first;
|
|
rocksatendp[i][1] = Jtmp.getMaximumX().first;
|
|
if (rocksatendp[i][0] < 0 || rocksatendp[i][0] > 1) {
|
|
OPM_THROW(std::runtime_error, "Minimum rock saturation (" << rocksatendp[i][0] << ") not sane for rock "
|
|
<< rockfilename << "." << std::endl << "Did you forget to specify anisotropicrocks=true ?");
|
|
}
|
|
}
|
|
else { //Anisotropic input rocks (Pc Sw Krxx Kryy Krzz)
|
|
MonotCubicInterpolator Pctmp;
|
|
try {
|
|
Pctmp = MonotCubicInterpolator(rockname, 2, 1);
|
|
}
|
|
catch (const char * errormessage) {
|
|
std::cerr << "Error: " << errormessage << std::endl;
|
|
std::cerr << "Check filename and columns 1 and 2 (Pc and Sw)" << std::endl;
|
|
exit(1);
|
|
}
|
|
rocksatendp[i][0] = Pctmp.getMinimumX().first;
|
|
rocksatendp[i][1] = Pctmp.getMaximumX().first;
|
|
}
|
|
}
|
|
return rocksatendp;
|
|
}
|
|
|
|
template <typename T>
|
|
std::string toString(T const& value) {
|
|
std::stringstream sstr;
|
|
sstr << value;
|
|
return sstr.str();
|
|
}
|
|
|
|
|
|
int main(int argc, char** argv)
|
|
try
|
|
{
|
|
Dune::MPIHelper::instance(argc, argv);
|
|
|
|
if (argc == 1) {
|
|
usageandexit();
|
|
}
|
|
// Initialize.
|
|
Opm::parameter::ParameterGroup param(argc, argv);
|
|
std::string gridfilename = param.get<std::string>("gridfilename");
|
|
Opm::ParserPtr parser(new Opm::Parser());
|
|
Opm::addNonStandardUpscalingKeywords(parser);
|
|
Opm::DeckConstPtr deck(parser->parseFile(gridfilename));
|
|
|
|
// Check that we have the information we need from the eclipse file:
|
|
if (! (deck->hasKeyword("SPECGRID") && deck->hasKeyword("COORD") && deck->hasKeyword("ZCORN")
|
|
&& deck->hasKeyword("PORO") && deck->hasKeyword("PERMX"))) {
|
|
std::cerr << "Error: Did not find SPECGRID, COORD, ZCORN, PORO and PERMX in Eclipse file " << gridfilename << std::endl;
|
|
usageandexit();
|
|
}
|
|
|
|
// Set default values if not given as input
|
|
double linsolver_tolerance = param.getDefault("residual_tolerance", 1e-8);
|
|
int linsolver_verbosity = param.getDefault("linsolver_verbosity", 0);
|
|
int linsolver_type = param.getDefault("linsolver_type", 1);
|
|
int linsolver_maxit = param.getDefault("linsolver_max_iterations", 0);
|
|
int linsolver_smooth_steps = param.getDefault("linsolver_smooth_steps", 2);
|
|
double linsolver_prolongate_factor = param.getDefault("linsolver_prolongate_factor", 1.6);
|
|
std::string bc=param.getDefault<std::string>("bc","fixed");
|
|
//double gravity = param.getDefault("gravity", 0);
|
|
double surfaceTension = param.getDefault("surfaceTension", 11);
|
|
int num_sats = param.getDefault("num_sats", 10);
|
|
int num_pdrops = param.getDefault("num_pdrops", 10);
|
|
double log_min_pdrop = std::log(param.getDefault("min_pdrop", 1e2));
|
|
double log_max_pdrop = std::log(param.getDefault("max_pdrop", 1e6));
|
|
std::string flowdir = param.getDefault<std::string>("flowdir","x");
|
|
int fldir;
|
|
if (flowdir == "x") {
|
|
//param.insertParameter("flow_direction", "0");
|
|
fldir = 0;
|
|
}
|
|
else if (flowdir == "y") {
|
|
//param.insertParameter("flow_direction", "1");
|
|
fldir = 1;
|
|
}
|
|
else if (flowdir == "z") {
|
|
//param.insertParameter("flow_direction", "2");
|
|
fldir = 2;
|
|
}
|
|
else {
|
|
std::cerr << "flowdir " << flowdir << " not valid. flowdir must be either x, y or z" << std::endl;
|
|
usageandexit();
|
|
}
|
|
bool start_from_cl = param.getDefault("start_from_cl", true);
|
|
//if (!param.has("outputWater")) {
|
|
// param.insertParameter("outputWater","");
|
|
//}
|
|
//if (!param.has("outputOil")) {
|
|
// param.insertParameter("outputOil","");
|
|
//}
|
|
|
|
int tensorElementCount; // Number of independent elements in resulting tensor
|
|
// Set boundary condition
|
|
if (bc == "fixed" || bc == "f") {
|
|
param.insertParameter("boundary_condition_type","0");
|
|
tensorElementCount = 3; // Diagonal
|
|
}
|
|
else if (bc == "periodic" || bc == "p") {
|
|
param.insertParameter("boundary_condition_type","2");
|
|
tensorElementCount = 6; // Symmetric
|
|
}
|
|
else if (bc == "linear" || bc == "l") {
|
|
param.insertParameter("boundary_condition_type","1");
|
|
tensorElementCount = 9; // Full tensor
|
|
}
|
|
else {
|
|
std::cerr << "Boundary conditions (bc) must be either fixed, periodic or linear. Value is " << bc << std::endl;
|
|
usageandexit();
|
|
}
|
|
|
|
// Compute minimum and maximum (large scale) saturations
|
|
std::string rock_list = param.getDefault<std::string>("rock_list", "no_list");
|
|
std::vector<std::string> rockfiles;
|
|
std::vector<std::vector<double> > rocksatendpoints_ = getExtremeSats(rock_list,rockfiles);
|
|
|
|
std::vector<double> poros = deck->getKeyword("PORO")->getSIDoubleData();
|
|
// Anisotropic relperm not yet implemented in steadystate_implicit
|
|
//bool anisorocks = param.getDefault("anisotropicrocks", false);
|
|
std::vector<int> satnums(poros.size(), 1);
|
|
if (deck->hasKeyword("SATNUM")) {
|
|
satnums = deck->getKeyword("SATNUM")->getIntData();
|
|
}
|
|
else if (deck->hasKeyword("ROCKTYPE")) {
|
|
satnums = deck->getKeyword("ROCKTYPE")->getIntData();
|
|
}
|
|
else {
|
|
std::cout << "Warning: SATNUM or ROCKTYPE not found in input file, assuming only one rocktype" << std::endl;
|
|
}
|
|
// check that number of rock types in rock_list matches number of rock types in grid
|
|
int num_rock_types_grid = int(*(max_element(satnums.begin(), satnums.end())));
|
|
int num_rock_types_rocklist = rocksatendpoints_.size();
|
|
if (num_rock_types_grid != num_rock_types_rocklist) {
|
|
std::cerr << "Number of rock types in rock_list " << rock_list << "(" << num_rock_types_rocklist << ") does not match" << std::endl
|
|
<< "the number of rock types in grid(" << num_rock_types_grid << ")." << std::endl;
|
|
usageandexit();
|
|
}
|
|
Opm::SinglePhaseUpscaler spupscaler; // needed to access porosities and cell volumes
|
|
spupscaler.init(deck, Opm::SinglePhaseUpscaler::Fixed,
|
|
0.0, linsolver_tolerance, linsolver_verbosity, linsolver_type, false, linsolver_maxit,
|
|
linsolver_prolongate_factor, linsolver_smooth_steps);
|
|
std::vector<double> cellPoreVolumes;
|
|
cellPoreVolumes.resize(satnums.size(), 0.0);
|
|
double swirvolume = 0.0;
|
|
double sworvolume = 0.0;
|
|
// cell_idx is the eclipse index.
|
|
const std::vector<int>& ecl_idx = spupscaler.grid().globalCell();
|
|
Dune::CpGrid::Codim<0>::LeafIterator c = spupscaler.grid().leafbegin<0>();
|
|
for (; c != spupscaler.grid().leafend<0>(); ++c) {
|
|
unsigned int cell_idx = ecl_idx[c->index()];
|
|
if (satnums[cell_idx] > 0) { // Satnum zero is "no rock"
|
|
cellPoreVolumes[cell_idx] = c->geometry().volume() * poros[cell_idx];
|
|
swirvolume += rocksatendpoints_[int(satnums[cell_idx])-1][0] * cellPoreVolumes[cell_idx];
|
|
sworvolume += rocksatendpoints_[int(satnums[cell_idx])-1][1] * cellPoreVolumes[cell_idx];
|
|
}
|
|
}
|
|
// Total porevolume and total volume -> upscaled porosity:
|
|
double poreVolume = std::accumulate(cellPoreVolumes.begin(),
|
|
cellPoreVolumes.end(),
|
|
0.0);
|
|
double min_sat = swirvolume/poreVolume;
|
|
double max_sat = sworvolume/poreVolume;
|
|
// Insert computed Swir and Swor as min_sat and max_sat in param object
|
|
//param.insertParameter("min_sat",toString(min_sat));
|
|
//param.insertParameter("max_sat",toString(max_sat));
|
|
|
|
std::vector<double> saturations;
|
|
Opm::SparseTable<double> all_pdrops;
|
|
// Linear range of saturations
|
|
saturations.resize(num_sats);
|
|
for (int i = 0; i < num_sats; ++i) {
|
|
double factor = num_sats == 1 ? 0 : double(i)/double(num_sats - 1);
|
|
saturations[i] = (1.0 - factor)*min_sat + factor*max_sat;
|
|
}
|
|
// Logarithmic range of pressure drops
|
|
std::vector<double> pdrops;
|
|
pdrops.resize(num_pdrops);
|
|
for (int i = 0; i < num_pdrops; ++i) {
|
|
double factor = num_pdrops == 1 ? 0 : double(i)/double(num_pdrops - 1);
|
|
pdrops[i] = std::exp((1.0 - factor)*log_min_pdrop + factor*log_max_pdrop);
|
|
}
|
|
// Assign the same pressure drops to all saturations.
|
|
for (int i = 0; i < num_sats; ++i) {
|
|
all_pdrops.appendRow(pdrops.begin(), pdrops.end());
|
|
}
|
|
typedef SteadyStateUpscalerImplicit<UpscalingTraitsBasicImplicit> Upscaler;
|
|
typedef Upscaler::permtensor_t permtensor_t;
|
|
Upscaler upscaler;
|
|
upscaler.init(param);
|
|
|
|
// Compute single phase permeability
|
|
permtensor_t upscaled_K = upscaler.upscaleSinglePhase();
|
|
permtensor_t singlephaseperm = upscaled_K;
|
|
singlephaseperm *= (1.0/(Opm::prefix::milli*Opm::unit::darcy));
|
|
double porosity = upscaler.upscalePorosity();
|
|
int num_cells = upscaler.grid().size(0);
|
|
|
|
// Holders for upscaled relpermvalues for each saturation-pressure-point
|
|
std::vector<std::vector<std::vector<double> > > RelPermPhase1, RelPermPhase2;
|
|
for (int satidx=0; satidx<num_sats; ++satidx) {
|
|
RelPermPhase1.push_back(std::vector<std::vector<double> >(all_pdrops[satidx].size(),std::vector<double>(tensorElementCount,0.0)));
|
|
RelPermPhase2.push_back(std::vector<std::vector<double> >(all_pdrops[satidx].size(),std::vector<double>(tensorElementCount,0.0)));
|
|
}
|
|
|
|
for (int satidx = 0; satidx < num_sats; ++satidx) {
|
|
std::vector<double> init_sat;
|
|
if (start_from_cl) {
|
|
try {
|
|
upscaler.setToCapillaryLimit(saturations[satidx], init_sat);
|
|
}catch (...){
|
|
init_sat.resize(num_cells, saturations[satidx]);
|
|
std::cout << "Failed to initialize with capillary limit for s = " << saturations[satidx]
|
|
<< ". Init with uniform distribution." << std::endl;
|
|
}
|
|
} {
|
|
init_sat.resize(num_cells, saturations[satidx]);
|
|
}
|
|
const Opm::SparseTable<double>::row_type pdrops = all_pdrops[satidx];
|
|
int num_pdrops = pdrops.size();
|
|
for (int pidx = 0; pidx < num_pdrops; ++pidx) {
|
|
upscaler.initSatLimits(init_sat);
|
|
double pdrop = pdrops[pidx];
|
|
bool success = false;
|
|
std::pair<permtensor_t, permtensor_t> lambda
|
|
= upscaler.upscaleSteadyState(fldir, init_sat, saturations[satidx], pdrop, upscaled_K, success);
|
|
double usat = upscaler.lastSaturationUpscaled();
|
|
std::cout << "metn: " << saturations[satidx] << " " << usat << std::endl;
|
|
if(! success){
|
|
std::cout << "Upscaling failed for " << usat << std::endl;
|
|
}else{
|
|
init_sat = upscaler.lastSaturationState();
|
|
}
|
|
// Store upscaled values
|
|
for (int voigtIdx=0; voigtIdx<tensorElementCount; ++voigtIdx) {
|
|
RelPermPhase1[satidx][pidx][voigtIdx] = getVoigtValue(lambda.first,voigtIdx);
|
|
RelPermPhase2[satidx][pidx][voigtIdx] = getVoigtValue(lambda.second,voigtIdx);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Output results to stdout and optionally to file.
|
|
std::stringstream outputtmp;
|
|
outputtmp << "######################################################################" << std::endl;
|
|
outputtmp << "Results from steady state upscaling of relative permeability (impes)" << std::endl;
|
|
outputtmp << "#" << std::endl;
|
|
time_t now = std::time(NULL);
|
|
outputtmp << "# Finished: " << asctime(localtime(&now));
|
|
|
|
utsname hostname; uname(&hostname);
|
|
outputtmp << "# Hostname: " << hostname.nodename << std::endl;
|
|
outputtmp << "#" << std::endl;
|
|
outputtmp << "# Eclipse file: " << gridfilename << std::endl;
|
|
outputtmp << "# cells: " << num_cells << std::endl;
|
|
outputtmp << "# Pore volume: " << poreVolume << std::endl;
|
|
outputtmp << "# Porosity: " << porosity << std::endl;
|
|
outputtmp << "#" << std::endl;
|
|
outputtmp << "# Rock list: " << rock_list << std::endl;
|
|
outputtmp << "# with the following rock files: " << std::endl;
|
|
for (std::size_t ridx=0; ridx<rockfiles.size(); ++ridx) {
|
|
outputtmp << "# " << rockfiles[ridx] << std::endl;
|
|
}
|
|
outputtmp << "#" << std::endl;
|
|
|
|
outputtmp << "# Options used:" << std::endl;
|
|
outputtmp << "# Boundary conditions: " << bc << std::endl;
|
|
outputtmp << "# flow direction: " << flowdir << std::endl;
|
|
outputtmp << "# saturation points: " << num_sats << std::endl;
|
|
outputtmp << "# pressure drop points: " << num_pdrops << std::endl;
|
|
//outputtmp << "# maxPermContrast: " << options["maxPermContrast"] << std::endl;
|
|
//outputtmp << "# minPerm: " << options["minPerm"] << std::endl;
|
|
//outputtmp << "# minPoro: " << options["minPoro"] << std::endl;
|
|
outputtmp << "# surfaceTension: " << surfaceTension << " dynes/cm" << std::endl;
|
|
//if (includeGravity) {
|
|
// outputtmp << "# gravity: " << options["gravity"] << " m/s²" << endl;
|
|
// // Output values of water/gas and oil densities
|
|
//}
|
|
outputtmp << "#" << std::endl;
|
|
outputtmp << "# Single phase permeability" << std::endl;
|
|
outputtmp << "# |Kxx Kxy Kxz| = " << singlephaseperm(0,0) << " " << singlephaseperm(0,1) << " " << singlephaseperm(0,2) << std::endl;
|
|
outputtmp << "# |Kyx Kyy Kyz| = " << singlephaseperm(1,0) << " " << singlephaseperm(1,1) << " " << singlephaseperm(1,2) << std::endl;
|
|
outputtmp << "# |Kzx Kzy Kzz| = " << singlephaseperm(2,0) << " " << singlephaseperm(2,1) << " " << singlephaseperm(2,2) << std::endl;
|
|
outputtmp << "# " << std::endl;
|
|
|
|
std::cout << outputtmp.str();
|
|
|
|
// MPIHelper::instance(argc,argv) ;
|
|
//typedef SteadyStateUpscalerImplicit<UpscalingTraitsBasicImplicit> upscaler_t;
|
|
//SteadyStateUpscalerManagerImplicit<upscaler_t> mgr;
|
|
//mgr.upscale(param);
|
|
|
|
}
|
|
catch (const std::exception &e) {
|
|
std::cerr << "Program threw an exception: " << e.what() << "\n";
|
|
throw;
|
|
}
|
|
|
|
// TODO
|
|
// * Include gravity?
|
|
// * Include possibility for maxPermContrast, minPerm, minPoro
|
|
// * Include possibility for interpolating relpermcurves (as functions of saturation)
|