int main(int argc, char** argv) try { if (argc == 1) { std::cout << "Usage: cpregularize gridfilename=filename.grdecl [ires=5] [jres=5] [zres=5] " << std::endl; std::cout << " [imin=] [imax=] [jmin=] [jmax=] [zmin=] [zmax=] " << std::endl; std::cout << " [z_tolerance=0.0] [minperm=1e-9] " << std::endl; std::cout << " [resultgrid=regularizedgrid.grdecl]" << std::endl; exit(1); } Dune::MPIHelper::instance(argc, argv); Opm::parameter::ParameterGroup param(argc, argv); std::string gridfilename = param.get<std::string>("gridfilename"); Opm::CornerPointChopper ch(gridfilename); // The cells with i coordinate in [imin, imax) are included, similar for j. // The z limits may be changed inside the chopper to match actual min/max z. const int* dims = ch.dimensions(); int imin = param.getDefault("imin", 0); int imax = param.getDefault("imax", dims[0]); int jmin = param.getDefault("jmin", 0); int jmax = param.getDefault("jmax", dims[1]); double zmin = param.getDefault("zmin", ch.zLimits().first); double zmax = param.getDefault("zmax", ch.zLimits().second); int ires = param.getDefault("ires", 1); int jres = param.getDefault("jres", 1); int zres = param.getDefault("zres", 1); std::string resultgrid = param.getDefault<std::string>("resultgrid", "regularizedgrid.grdecl"); double minperm = param.getDefault("minperm", 1e-9); double minpermSI = Opm::unit::convert::from(minperm, Opm::prefix::milli*Opm::unit::darcy); double z_tolerance = param.getDefault("z_tolerance", 1e-8); double residual_tolerance = param.getDefault("residual_tolerance", 1e-8); double linsolver_verbosity = param.getDefault("linsolver_verbosity", 0); double linsolver_type = param.getDefault("linsolver_type", 1); // Check for unused parameters (potential typos). if (param.anyUnused()) { std::cout << "***** WARNING: Unused parameters: *****\n"; param.displayUsage(); } // Check that we do not have any user input // that goes outside the coordinates described in // the cornerpoint file (runtime-exception will be thrown in case of error) // (ilen, jlen and zlen set to zero, does not apply here) ch.verifyInscribedShoebox(imin, 0, imax, jmin, 0, jmax, zmin, 0, zmax); // Storage for properties for regularized cells std::vector<double> poro; std::vector<double> permx; std::vector<double> permy; std::vector<double> permz; // Original x/y resolution in terms of coordinate values (not indices) Opm::ParserPtr parser(new Opm::Parser); Opm::DeckConstPtr deck(parser->parseFile(gridfilename)); // TODO: REFACTOR!!!! it is stupid to parse this again Opm::EclipseGridInspector gridinspector(deck); std::array<double, 6> gridlimits=gridinspector.getGridLimits(); double finegridxresolution = (gridlimits[1]-gridlimits[0])/dims[0]; double finegridyresolution = (gridlimits[3]-gridlimits[2])/dims[1]; // Construct mapping from coarse i and j indices to fine // and COORDS values for regularized pillars. std::vector<int> iidx_f, jidx_f; std::vector<double> newcoords_x; int finesprcoarse_i = floor(dims[0] / ires); int remainder_i = dims[0] - ires*finesprcoarse_i; for (int iidx_c=0; iidx_c < remainder_i+1; ++iidx_c) { iidx_f.push_back(iidx_c*(finesprcoarse_i + 1)); // Spread remainder evenly } for (int iidx_c=remainder_i + 1; iidx_c < ires; ++iidx_c) { iidx_f.push_back(iidx_c*finesprcoarse_i + remainder_i); } iidx_f.push_back(imax); // endpoint needed below int finesprcoarse_j = floor(dims[1] / jres); int remainder_j = dims[1] - jres*finesprcoarse_j; for (int jidx_c=0; jidx_c < remainder_j+1; ++jidx_c) { jidx_f.push_back(jidx_c*(finesprcoarse_j + 1)); // Spread remainder evenly } for (int jidx_c=remainder_j + 1; jidx_c < jres; ++jidx_c) { jidx_f.push_back(jidx_c*finesprcoarse_j + remainder_j); } jidx_f.push_back(jmax); // endpoint needed below // Construct new ZCORN for regular grid std::vector<double> zcorn_c; for (int zidx_c=0; zidx_c < zres; ++zidx_c) { zcorn_c.push_back(zmin + zidx_c * (zmax-zmin)/zres); } zcorn_c.push_back(zmax); // Run through the new regular grid to find its properties for (int zidx_c=0; zidx_c < zres; ++zidx_c) { for (int jidx_c=0; jidx_c < jres; ++jidx_c) { for (int iidx_c=0; iidx_c < ires; ++iidx_c) { ch.chop(iidx_f[iidx_c], iidx_f[iidx_c+1], jidx_f[jidx_c], jidx_f[jidx_c+1], zcorn_c[zidx_c], zcorn_c[zidx_c+1], false); OPM_THROW(std::logic_error, "Sub-decks not are not implemented by opm-parser. Refactor the calling code!?"); try { Opm::DeckConstPtr subdeck = ch.subDeck(); Opm::SinglePhaseUpscaler upscaler; upscaler.init(subdeck, Opm::SinglePhaseUpscaler::Fixed, minpermSI, z_tolerance, residual_tolerance, linsolver_verbosity, linsolver_type, false); Opm::SinglePhaseUpscaler::permtensor_t upscaled_K = upscaler.upscaleSinglePhase(); upscaled_K *= (1.0/(Opm::prefix::milli*Opm::unit::darcy)); poro.push_back(upscaler.upscalePorosity()); permx.push_back(upscaled_K(0,0)); permy.push_back(upscaled_K(1,1)); permz.push_back(upscaled_K(2,2)); } catch (...) { std::cout << "Warning: Upscaling for cell failed to convert, values set to zero\n"; poro.push_back(0.0); permx.push_back(0.0); permy.push_back(0.0); permz.push_back(0.0); } } } } // Write regularized grid to outputfile std::ofstream out(resultgrid.c_str()); if (!out) { std::cerr << "Could not open file " << resultgrid << "\n"; throw std::runtime_error("Could not open output file."); } out << "SPECGRID\n" << ires << ' ' << jres << ' ' << zres << " 1 F\n/\n\n"; out << "COORD\n"; for (int j = 0; j <= jres; ++j) { for (int i = 0; i <= ires; ++i) { out << finegridxresolution*iidx_f[i] << " " << finegridyresolution*jidx_f[j] << " " << zmin << " " << finegridxresolution*iidx_f[i] << " " << finegridyresolution*jidx_f[j] << " " << zmax << "\n"; } } out << "/\n\n"; /* Write ZCORN, that is the Z-coordinates along the pillars, specifying the eight corners of each cell. Each corner is specified for each cell, even though it is the same corner that is used in other cells. We loop over corners in each grid cell, directions: z, y, x (x innermost). The code here *IS* redundant, but the grid is also very redundant for a grid that is really regular.. */ out << "ZCORN\n"; double zlen = zmax-zmin; for (int zidx=0; zidx < zres; ++zidx) { for (int j = 0; j < jres; ++j) { for (int i = 0; i < ires; ++i) { out << zlen/zres*zidx << " " << zlen/zres*zidx << " "; } out << "\n"; for (int i = 0; i < ires; ++i) { out << zlen/zres*zidx << " " << zlen/zres*zidx << " "; } } for (int j = 0; j < jres; ++j) { for (int i = 0; i < ires; ++i) { out << zlen/zres*(zidx+1) << " " << zlen/zres*(zidx+1) << " "; } out << "\n"; for (int i = 0; i < ires; ++i) { out << zlen/zres*(zidx+1) << " " << zlen/zres*(zidx+1) << " "; } } } out << "/\n\n"; out << "PORO\n"; for (size_t idx=0; idx < (size_t)poro.size(); ++idx) { out << poro[idx] << std::endl; } out << "/\n\n"; out << "PERMX\n"; for (size_t idx=0; idx < (size_t)permx.size(); ++idx) { out << permx[idx] << std::endl; } out << "/\n\n"; out << "PERMY\n\n"; for (size_t idx=0; idx < (size_t)permy.size(); ++idx) { out << permy[idx] << std::endl; } out << "/\n\n"; out << "PERMZ\n\n"; for (size_t idx=0; idx < (size_t)permz.size(); ++idx) { out << permz[idx] << std::endl; } out << "/\n"; out.close(); } catch (const std::exception &e) { std::cerr << "Program threw an exception: " << e.what() << "\n"; throw; }
int main(int argc, char** argv) try { if (argc == 1) { std::cout << "Usage: cpchop gridfilename=filename.grdecl [subsamples=10] [ilen=5] [jlen=5] " << std::endl; std::cout << " [zlen=5] [imin=] [imax=] [jmin=] [jmax=] [upscale=true] [bc=fixed]" << std::endl; std::cout << " [resettoorigin=true] [seed=111] [z_tolerance=0.0] [minperm=1e-9] " << std::endl; std::cout << " [dips=false] [azimuthdisplacement=] [satnumvolumes=false] [mincellvolume=1e-9]" << std::endl; std::cout << " [filebase=] [resultfile=] [endpoints=false] [cappres=false]" << std::endl; std::cout << " [rock_list=] [anisotropicrocks=false]" << std::endl; exit(1); } Opm::parameter::ParameterGroup param(argc, argv); std::string gridfilename = param.get<std::string>("gridfilename"); Opm::CornerPointChopper ch(gridfilename); // The cells with i coordinate in [imin, imax) are included, similar for j. // The z limits may be changed inside the chopper to match actual min/max z. const int* dims = ch.dimensions(); int imin = param.getDefault("imin", 0); int imax = param.getDefault("imax", dims[0]); int jmin = param.getDefault("jmin", 0); int jmax = param.getDefault("jmax", dims[1]); double zmin = param.getDefault("zmin", ch.zLimits().first); double zmax = param.getDefault("zmax", ch.zLimits().second); int subsamples = param.getDefault("subsamples", 1); int ilen = param.getDefault("ilen", imax - imin); int jlen = param.getDefault("jlen", jmax - jmin); double zlen = param.getDefault("zlen", zmax - zmin); bool upscale = param.getDefault("upscale", true); std::string bc = param.getDefault<std::string>("bc", "fixed"); bool resettoorigin = param.getDefault("resettoorigin", true); boost::mt19937::result_type userseed = param.getDefault("seed", 0); int outputprecision = param.getDefault("outputprecision", 8); std::string filebase = param.getDefault<std::string>("filebase", ""); std::string resultfile = param.getDefault<std::string>("resultfile", ""); double minperm = param.getDefault("minperm", 1e-9); double minpermSI = Opm::unit::convert::from(minperm, Opm::prefix::milli*Opm::unit::darcy); // Following two options are for dip upscaling (slope of cell top and bottom edges) bool dips = param.getDefault("dips", false); // whether to do dip averaging double azimuthdisplacement = param.getDefault("azimuthdisplacement", 0.0); // posibility to add/subtract a value to/from azimuth for dip plane. double mincellvolume = param.getDefault("mincellvolume", 1e-9); // ignore smaller cells for dip calculations bool satnumvolumes = param.getDefault("satnumvolumes", false); // whether to count volumes pr. satnum // upscaling of endpoints and capillary pressure // Conversion factor, multiply mD numbers with this to get m² numbers const double milliDarcyToSqMetre = 9.869233e-16; // Input for surfaceTension is dynes/cm, SI units are Joules/square metre const double surfaceTension = param.getDefault("surfaceTension", 11.0) * 1e-3; // multiply with 10^-3 to obtain SI units bool endpoints = param.getDefault("endpoints", false); // whether to upscale saturation endpoints bool cappres = param.getDefault("cappres", false); // whether to upscale capillary pressure if (cappres) { endpoints = true; } std::string rock_list = param.getDefault<std::string>("rock_list", "no_list"); bool anisorocks = param.getDefault("anisotropicrocks", false); std::vector<std::vector<double> > rocksatendpoints_; std::vector<std::vector<double> > jfuncendpoints_; // Used if isotropic rock input int nsatpoints = 5; // nuber of saturation points in upscaled capillary pressure function per subsample double saturationThreshold = 0.00001; // For isotropic input rocks: std::vector<Opm::MonotCubicInterpolator> InvJfunctions; // Holds the inverse of the loaded J-functions. // For anisotropic input rocks: std::vector<Opm::MonotCubicInterpolator> SwPcfunctions; // Holds Sw(Pc) for each rocktype. // Read rock data from files specifyed in rock_list if (endpoints) { if (!rock_list.compare("no_list")) { std::cout << "Can't do endponts without rock list (" << rock_list << ")" << std::endl; throw std::exception(); } // Code copied from ReservoirPropertyCommon.hpp for file reading 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); rocksatendpoints_.resize(num_rocks); jfuncendpoints_.resize(num_rocks); // Loop through rock files defined in rock_list and store the data we need for (int i = 0; i < num_rocks; ++i) { 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; 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) Opm::MonotCubicInterpolator Jtmp; try { Jtmp = Opm::MonotCubicInterpolator(rockname, 1, 4); } catch (const char * errormessage) { std::cerr << "Error: " << errormessage << std::endl; std::cerr << "Check filename" << std::endl; exit(1); } // Invert J-function, now we get saturation as a function of pressure: if (Jtmp.isStrictlyMonotone()) { InvJfunctions.push_back(Opm::MonotCubicInterpolator(Jtmp.get_fVector(), Jtmp.get_xVector())); } else { std::cerr << "Error: Jfunction " << i+1 << " in rock file " << rockname << " was not invertible." << std::endl; exit(1); } jfuncendpoints_[i][0] = Jtmp.getMinimumX().second; jfuncendpoints_[i][1] = Jtmp.getMaximumX().second; rocksatendpoints_[i][0] = Jtmp.getMinimumX().first; rocksatendpoints_[i][1] = Jtmp.getMaximumX().first; if (rocksatendpoints_[i][0] < 0 || rocksatendpoints_[i][0] > 1) { OPM_THROW(std::runtime_error, "Minimum rock saturation (" << rocksatendpoints_[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) Opm::MonotCubicInterpolator Pctmp; try { Pctmp = Opm::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); } if (cappres) { // Invert Pc(Sw) curve into Sw(Pc): if (Pctmp.isStrictlyMonotone()) { SwPcfunctions.push_back(Opm::MonotCubicInterpolator(Pctmp.get_fVector(), Pctmp.get_xVector())); } else { std::cerr << "Error: Pc(Sw) curve " << i+1 << " in rock file " << rockname << " was not invertible." << std::endl; exit(1); } } rocksatendpoints_[i][0] = Pctmp.getMinimumX().first; rocksatendpoints_[i][1] = Pctmp.getMaximumX().first; } } } double z_tolerance = param.getDefault("z_tolerance", 0.0); double residual_tolerance = param.getDefault("residual_tolerance", 1e-8); int linsolver_verbosity = param.getDefault("linsolver_verbosity", 0); int linsolver_type = param.getDefault("linsolver_type", 1); // Guarantee initialization double Pcmax = -DBL_MAX, Pcmin = DBL_MAX; // Check that we do not have any user input // that goes outside the coordinates described in // the cornerpoint file (runtime-exception will be thrown in case of error) ch.verifyInscribedShoebox(imin, ilen, imax, jmin, jlen, jmax, zmin, zlen, zmax); // Random number generator from boost. boost::mt19937 gen; // Seed the random number generators with the current time, unless specified on command line // Warning: Current code does not allow 0 for the seed!! boost::mt19937::result_type autoseed = time(NULL); if (userseed == 0) { gen.seed(autoseed); } else { gen.seed(userseed); } Opm::SinglePhaseUpscaler::BoundaryConditionType bctype = Opm::SinglePhaseUpscaler::Fixed; bool isFixed, isPeriodic; isFixed = isPeriodic = false; if (upscale) { if (bc == "fixed") { isFixed = true; bctype = Opm::SinglePhaseUpscaler::Fixed; } else if (bc == "periodic") { isPeriodic = true; bctype = Opm::SinglePhaseUpscaler::Periodic; } else { std::cout << "Boundary condition type (bc=" << bc << ") not allowed." << std::endl; std::cout << "Only bc=fixed or bc=periodic implemented." << std::endl; throw std::exception(); } } // Check for unused parameters (potential typos). if (param.anyUnused()) { std::cout << "***** WARNING: Unused parameters: *****\n"; param.displayUsage(); } // Note that end is included in interval for uniform_int. boost::uniform_int<> disti(imin, imax - ilen); boost::uniform_int<> distj(jmin, jmax - jlen); boost::uniform_real<> distz(zmin, std::max(zmax - zlen, zmin)); boost::variate_generator<boost::mt19937&, boost::uniform_int<> > ri(gen, disti); boost::variate_generator<boost::mt19937&, boost::uniform_int<> > rj(gen, distj); boost::variate_generator<boost::mt19937&, boost::uniform_real<> > rz(gen, distz); // Storage for results std::vector<double> porosities; std::vector<double> permxs; std::vector<double> permys; std::vector<double> permzs; std::vector<double> permyzs; std::vector<double> permxzs; std::vector<double> permxys; std::vector<double> minsws, maxsws; std::vector<std::vector<double> > pcvalues; std::vector<double> dipangs, azimuths; // Initialize a matrix for subsample satnum volumes. // Outer index is subsample index, inner index is SATNUM-value std::vector<std::vector<double> > rockvolumes; int maxSatnum = 0; // This value is determined from the chopped cells. int finished_subsamples = 0; // keep explicit count of successful subsamples for (int sample = 1; sample <= subsamples; ++sample) { int istart = ri(); int jstart = rj(); double zstart = rz(); ch.chop(istart, istart + ilen, jstart, jstart + jlen, zstart, zstart + zlen, resettoorigin); std::string subsampledgrdecl = filebase; // Output grdecl-data to file if a filebase is supplied. if (filebase != "") { std::ostringstream oss; if ((size_t) subsamples > 1) { // Only add number to filename if more than one sample is asked for oss << 'R' << std::setw(4) << std::setfill('0') << sample; subsampledgrdecl += oss.str(); } subsampledgrdecl += ".grdecl"; ch.writeGrdecl(subsampledgrdecl); } try { /* The upscaling may fail to converge on icky grids, lets just pass by those */ if (upscale) { Opm::EclipseGridParser subparser = ch.subparser(); subparser.convertToSI(); Opm::SinglePhaseUpscaler upscaler; upscaler.init(subparser, bctype, minpermSI, z_tolerance, residual_tolerance, linsolver_verbosity, linsolver_type, false); Opm::SinglePhaseUpscaler::permtensor_t upscaled_K = upscaler.upscaleSinglePhase(); upscaled_K *= (1.0/(Opm::prefix::milli*Opm::unit::darcy)); porosities.push_back(upscaler.upscalePorosity()); permxs.push_back(upscaled_K(0,0)); permys.push_back(upscaled_K(1,1)); permzs.push_back(upscaled_K(2,2)); permyzs.push_back(upscaled_K(1,2)); permxzs.push_back(upscaled_K(0,2)); permxys.push_back(upscaled_K(0,1)); } if (endpoints) { // Calculate minimum and maximum water volume in each cell // Create single-phase upscaling object to get poro and perm values from the grid Opm::EclipseGridParser subparser = ch.subparser(); std::vector<double> perms = subparser.getFloatingPointValue("PERMX"); subparser.convertToSI(); Opm::SinglePhaseUpscaler upscaler; upscaler.init(subparser, bctype, minpermSI, z_tolerance, residual_tolerance, linsolver_verbosity, linsolver_type, false); std::vector<int> satnums = subparser.getIntegerValue("SATNUM"); std::vector<double> poros = subparser.getFloatingPointValue("PORO"); std::vector<double> cellVolumes, cellPoreVolumes; cellVolumes.resize(satnums.size(), 0.0); cellPoreVolumes.resize(satnums.size(), 0.0); int tesselatedCells = 0; //double maxSinglePhasePerm = 0; double Swirvolume = 0; double Sworvolume = 0; const std::vector<int>& ecl_idx = upscaler.grid().globalCell(); Dune::CpGrid::Codim<0>::LeafIterator c = upscaler.grid().leafbegin<0>(); for (; c != upscaler.grid().leafend<0>(); ++c) { unsigned int cell_idx = ecl_idx[c->index()]; if (satnums[cell_idx] > 0) { // Satnum zero is "no rock" cellVolumes[cell_idx] = c->geometry().volume(); cellPoreVolumes[cell_idx] = cellVolumes[cell_idx] * poros[cell_idx]; double Pcmincandidate = 0.0, Pcmaxcandidate = 0.0, minSw, maxSw; if (!anisorocks) { if (cappres) { Pcmincandidate = jfuncendpoints_[int(satnums[cell_idx])-1][1] / sqrt(perms[cell_idx] * milliDarcyToSqMetre/poros[cell_idx]) * surfaceTension; Pcmaxcandidate = jfuncendpoints_[int(satnums[cell_idx])-1][0] / sqrt(perms[cell_idx] * milliDarcyToSqMetre/poros[cell_idx]) * surfaceTension; } minSw = rocksatendpoints_[int(satnums[cell_idx])-1][0]; maxSw = rocksatendpoints_[int(satnums[cell_idx])-1][1]; } else { // anisotropic input, we do not to J-function scaling if (cappres) { Pcmincandidate = SwPcfunctions[int(satnums[cell_idx])-1].getMinimumX().first; Pcmaxcandidate = SwPcfunctions[int(satnums[cell_idx])-1].getMaximumX().first; } minSw = rocksatendpoints_[int(satnums[cell_idx])-1][0]; maxSw = rocksatendpoints_[int(satnums[cell_idx])-1][1]; } if (cappres) { Pcmin = std::min(Pcmincandidate, Pcmin); Pcmax = std::max(Pcmaxcandidate, Pcmax); } Swirvolume += minSw * cellPoreVolumes[cell_idx]; Sworvolume += maxSw * cellPoreVolumes[cell_idx]; } ++tesselatedCells; // keep count. } // If upscling=false, we still (may) want to have porosities together with endpoints if (!upscale) { porosities.push_back(upscaler.upscalePorosity()); } // Total porevolume and total volume -> upscaled porosity: double poreVolume = std::accumulate(cellPoreVolumes.begin(), cellPoreVolumes.end(), 0.0); double Swir = Swirvolume/poreVolume; double Swor = Sworvolume/poreVolume; minsws.push_back(Swir); maxsws.push_back(Swor); if (cappres) { // Upscale capillary pressure function Opm::MonotCubicInterpolator WaterSaturationVsCapPressure; double largestSaturationInterval = Swor-Swir; double Ptestvalue = Pcmax; while (largestSaturationInterval > (Swor-Swir)/double(nsatpoints)) { if (Pcmax == Pcmin) { // This is a dummy situation, we go through once and then // we are finished (this will be triggered by zero permeability) Ptestvalue = Pcmin; largestSaturationInterval = 0; } else if (WaterSaturationVsCapPressure.getSize() == 0) { /* No data values previously computed */ Ptestvalue = Pcmax; } else if (WaterSaturationVsCapPressure.getSize() == 1) { /* If only one point has been computed, it was for Pcmax. So now do Pcmin */ Ptestvalue = Pcmin; } else { /* Search for largest saturation interval in which there are no computed saturation points (and estimate the capillary pressure that will fall in the center of this saturation interval) */ std::pair<double,double> SatDiff = WaterSaturationVsCapPressure.getMissingX(); Ptestvalue = SatDiff.first; largestSaturationInterval = SatDiff.second; } // Check for saneness of Ptestvalue: if (std::isnan(Ptestvalue) || std::isinf(Ptestvalue)) { std::cerr << "ERROR: Ptestvalue was inf or nan" << std::endl; break; // Jump out of while-loop, just print out the results // up to now and exit the program } double waterVolume = 0.0; for (unsigned int i = 0; i < ecl_idx.size(); ++i) { unsigned int cell_idx = ecl_idx[i]; double waterSaturationCell = 0.0; if (satnums[cell_idx] > 0) { // handle "no rock" cells with satnum zero double PtestvalueCell; PtestvalueCell = Ptestvalue; if (!anisorocks) { double Jvalue = sqrt(perms[cell_idx] * milliDarcyToSqMetre /poros[cell_idx]) * PtestvalueCell / surfaceTension; waterSaturationCell = InvJfunctions[int(satnums[cell_idx])-1].evaluate(Jvalue); } else { // anisotropic_input, then we do not do J-function-scaling waterSaturationCell = SwPcfunctions[int(satnums[cell_idx])-1].evaluate(PtestvalueCell); } } waterVolume += waterSaturationCell * cellPoreVolumes[cell_idx]; } WaterSaturationVsCapPressure.addPair(Ptestvalue, waterVolume/poreVolume); } WaterSaturationVsCapPressure.chopFlatEndpoints(saturationThreshold); std::vector<double> wattest = WaterSaturationVsCapPressure.get_fVector(); std::vector<double> cprtest = WaterSaturationVsCapPressure.get_xVector(); Opm::MonotCubicInterpolator CapPressureVsWaterSaturation(WaterSaturationVsCapPressure.get_fVector(), WaterSaturationVsCapPressure.get_xVector()); std::vector<double> pcs; for (int satp=0; satp<nsatpoints; ++satp) { pcs.push_back(CapPressureVsWaterSaturation.evaluate(Swir+(Swor-Swir)/(nsatpoints-1)*satp)); } pcvalues.push_back(pcs); } } if (dips) { Opm::EclipseGridParser subparser = ch.subparser(); std::vector<int> griddims = subparser.getSPECGRID().dimensions; std::vector<double> xdips_subsample, ydips_subsample; Opm::EclipseGridInspector gridinspector(subparser); for (int k=0; k < griddims[2]; ++k) { for (int j=0; j < griddims[1]; ++j) { for (int i=0; i < griddims[0]; ++i) { if (gridinspector.cellVolumeVerticalPillars(i, j, k) > mincellvolume) { std::pair<double,double> xydip = gridinspector.cellDips(i, j, k); xdips_subsample.push_back(xydip.first); ydips_subsample.push_back(xydip.second); } } } } // double azimuth = atan(xydip.first/xydip.second); // double dip = acos(1.0/sqrt(pow(xydip.first,2.0)+pow(xydip.second,2.0)+1.0)); // dips_subsample.push_back( xydip.first ); // azims_subsample.push_back(atan(xydip.first/xydip.second)); // Average xdips and ydips double xdipaverage = accumulate(xdips_subsample.begin(), xdips_subsample.end(), 0.0)/xdips_subsample.size(); double ydipaverage = accumulate(ydips_subsample.begin(), ydips_subsample.end(), 0.0)/ydips_subsample.size(); // Convert to dip and azimuth double azimuth = atan(xdipaverage/ydipaverage)+azimuthdisplacement; double dip = acos(1.0/sqrt(pow(xdipaverage,2.0)+pow(ydipaverage,2.0)+1.0)); dipangs.push_back(dip); azimuths.push_back(azimuth); } if (satnumvolumes) { Opm::EclipseGridParser subparser = ch.subparser(); Opm::EclipseGridInspector subinspector(subparser); std::vector<int> griddims = subparser.getSPECGRID().dimensions; int number_of_subsamplecells = griddims[0] * griddims[1] * griddims[2]; // If SATNUM is non-existent in input grid, this will fail: std::vector<int> satnums = subparser.getIntegerValue("SATNUM"); std::vector<double> rockvolumessubsample; for (int cell_idx=0; cell_idx < number_of_subsamplecells; ++cell_idx) { maxSatnum = std::max(maxSatnum, int(satnums[cell_idx])); rockvolumessubsample.resize(maxSatnum); // Ensure long enough vector rockvolumessubsample[int(satnums[cell_idx])-1] += subinspector.cellVolumeVerticalPillars(cell_idx); } // Normalize volumes to obtain relative volumes: double subsamplevolume = std::accumulate(rockvolumessubsample.begin(), rockvolumessubsample.end(), 0.0); std::vector<double> rockvolumessubsample_normalized; for (size_t satnum_idx = 0; satnum_idx < rockvolumessubsample.size(); ++satnum_idx) { rockvolumessubsample_normalized.push_back(rockvolumessubsample[satnum_idx]/subsamplevolume); } rockvolumes.push_back(rockvolumessubsample_normalized); } finished_subsamples++; } catch (...) { std::cerr << "Warning: Upscaling chopped subsample nr. " << sample << " failed, proceeding to next subsample\n"; } } // Make stream of output data, to be outputted to screen and optionally to file std::stringstream outputtmp; outputtmp << "################################################################################################" << std::endl; outputtmp << "# Results from property analysis on subsamples" << std::endl; outputtmp << "#" << std::endl; time_t now = time(NULL); outputtmp << "# Finished: " << asctime(localtime(&now)); utsname hostname; uname(&hostname); outputtmp << "# Hostname: " << hostname.nodename << std::endl; outputtmp << "#" << std::endl; outputtmp << "# Options used:" << std::endl; outputtmp << "# gridfilename: " << gridfilename << std::endl; outputtmp << "# i; min,len,max: " << imin << " " << ilen << " " << imax << std::endl; outputtmp << "# j; min,len,max: " << jmin << " " << jlen << " " << jmax << std::endl; outputtmp << "# z; min,len,max: " << zmin << " " << zlen << " " << zmax << std::endl; outputtmp << "# subsamples: " << subsamples << std::endl; if (userseed == 0) { outputtmp << "# (auto) seed: " << autoseed << std::endl; } else { outputtmp << "# (manual) seed: " << userseed << std::endl; } outputtmp << "################################################################################################" << std::endl; outputtmp << "# id"; if (upscale) { if (isPeriodic) { outputtmp << " porosity permx permy permz permyz permxz permxy"; } else if (isFixed) { outputtmp << " porosity permx permy permz"; } } if (endpoints) { if (!upscale) { outputtmp << " porosity"; } outputtmp << " Swir Swor"; if (cappres) { outputtmp << " Pc(Swir) Pc2 Pc3 Pc4 Pc(Swor)"; } } if (dips) { outputtmp << " dip azim(displacement:" << azimuthdisplacement << ")"; } if (satnumvolumes) { for (int satnumidx = 0; satnumidx < maxSatnum; ++satnumidx) { outputtmp << " satnum_" << satnumidx+1; } } outputtmp << std::endl; const int fieldwidth = outputprecision + 8; for (int sample = 1; sample <= finished_subsamples; ++sample) { outputtmp << sample << '\t'; if (upscale) { outputtmp << std::showpoint << std::setw(fieldwidth) << std::setprecision(outputprecision) << porosities[sample-1] << '\t' << std::showpoint << std::setw(fieldwidth) << std::setprecision(outputprecision) << permxs[sample-1] << '\t' << std::showpoint << std::setw(fieldwidth) << std::setprecision(outputprecision) << permys[sample-1] << '\t' << std::showpoint << std::setw(fieldwidth) << std::setprecision(outputprecision) << permzs[sample-1] << '\t'; if (isPeriodic) { outputtmp << std::showpoint << std::setw(fieldwidth) << std::setprecision(outputprecision) << permyzs[sample-1] << '\t' << std::showpoint << std::setw(fieldwidth) << std::setprecision(outputprecision) << permxzs[sample-1] << '\t' << std::showpoint << std::setw(fieldwidth) << std::setprecision(outputprecision) << permxys[sample-1] << '\t'; } } if (endpoints) { if (!upscale) { outputtmp << std::showpoint << std::setw(fieldwidth) << std::setprecision(outputprecision) << porosities[sample-1] << '\t'; } outputtmp << std::showpoint << std::setw(fieldwidth) << std::setprecision(outputprecision) << minsws[sample-1] << '\t' << std::showpoint << std::setw(fieldwidth) << std::setprecision(outputprecision) << maxsws[sample-1]; if (cappres) { outputtmp << std::showpoint << std::setw(fieldwidth) << std::setprecision(outputprecision) << pcvalues[sample-1][0] << '\t' << std::showpoint << std::setw(fieldwidth) << std::setprecision(outputprecision) << pcvalues[sample-1][1] << '\t' << std::showpoint << std::setw(fieldwidth) << std::setprecision(outputprecision) << pcvalues[sample-1][2] << '\t' << std::showpoint << std::setw(fieldwidth) << std::setprecision(outputprecision) << pcvalues[sample-1][3] << '\t' << std::showpoint << std::setw(fieldwidth) << std::setprecision(outputprecision) << pcvalues[sample-1][4]; } } if (dips) { outputtmp << std::showpoint << std::setw(fieldwidth) << std::setprecision(outputprecision) << dipangs[sample-1] << '\t' << std::showpoint << std::setw(fieldwidth) << std::setprecision(outputprecision) << azimuths[sample-1]; } if (satnumvolumes) { rockvolumes[sample-1].resize(maxSatnum, 0.0); for (int satnumidx = 0; satnumidx < maxSatnum; ++satnumidx) { outputtmp << std::showpoint << std::setw(fieldwidth) << std::setprecision(outputprecision) << rockvolumes[sample-1][satnumidx] << '\t'; } } outputtmp << std::endl; } if (resultfile != "") { std::cout << "Writing results to " << resultfile << std::endl; std::ofstream outfile; outfile.open(resultfile.c_str(), std::ios::out | std::ios::trunc); outfile << outputtmp.str(); outfile.close(); } std::cout << outputtmp.str(); } catch (const std::exception &e) { std::cerr << "Program threw an exception: " << e.what() << "\n"; throw; }
int main(int argc, char** argv) { if (argc == 1) { std::cout << "Usage: cpchop_depthtrend gridfilename=filename.grdecl [zresolution=1] [zlen=1] [ilen=5] [jlen=5] " << std::endl; std::cout << " [zlen=5] [imin=] [imax=] [jmin=] [jmax=] [upscale=true] [resettoorigin=true]" << std::endl; std::cout << " [seed=111] [z_tolerance=0.0] [minperm=1e-9] " << std::endl; exit(1); } Opm::parameter::ParameterGroup param(argc, argv); std::string gridfilename = param.get<std::string>("gridfilename"); Opm::CornerPointChopper ch(gridfilename); // The cells with i coordinate in [imin, imax) are included, similar for j. // The z limits may be changed inside the chopper to match actual min/max z. const int* dims = ch.dimensions(); int imin = param.getDefault("imin", 0); int imax = param.getDefault("imax", dims[0]); int jmin = param.getDefault("jmin", 0); int jmax = param.getDefault("jmax", dims[1]); double zmin = param.getDefault("zmin", ch.zLimits().first); double zmax = param.getDefault("zmax", ch.zLimits().second); int ilen = param.getDefault("ilen", imax - imin); int jlen = param.getDefault("jlen", jmax - jmin); double zresolution = param.getDefault("zresolution", 1.0); double zlen = param.getDefault("zlen", zresolution); bool upscale = param.getDefault("upscale", true); bool resettoorigin = param.getDefault("resettoorigin", true); boost::mt19937::result_type userseed = param.getDefault("seed", 0); int outputprecision = param.getDefault("outputprecision", 8); std::string filebase = param.getDefault<std::string>("filebase", ""); std::string resultfile = param.getDefault<std::string>("resultfile", ""); double minperm = param.getDefault("minperm", 1e-9); double minpermSI = Opm::unit::convert::from(minperm, Opm::prefix::milli*Opm::unit::darcy); double z_tolerance = param.getDefault("z_tolerance", 0.0); double residual_tolerance = param.getDefault("residual_tolerance", 1e-8); double linsolver_verbosity = param.getDefault("linsolver_verbosity", 0); double linsolver_type = param.getDefault("linsolver_type", 1); // Check for unused parameters (potential typos). if (param.anyUnused()) { std::cout << "***** WARNING: Unused parameters: *****\n"; param.displayUsage(); } // Check that we do not have any user input // that goes outside the coordinates described in // the cornerpoint file (runtime-exception will be thrown in case of error) ch.verifyInscribedShoebox(imin, ilen, imax, jmin, jlen, jmax, zmin, zlen, zmax); // Random number generator from boost. boost::mt19937 gen; // Seed the random number generators with the current time, unless specified on command line // Warning: Current code does not allow 0 for the seed!! if (userseed == 0) { gen.seed(time(NULL)); } else { gen.seed(userseed); } // Note that end is included in interval for uniform_int. boost::uniform_int<> disti(imin, imax - ilen); boost::uniform_int<> distj(jmin, jmax - jlen); boost::variate_generator<boost::mt19937&, boost::uniform_int<> > ri(gen, disti); boost::variate_generator<boost::mt19937&, boost::uniform_int<> > rj(gen, distj); // Storage for results std::vector<double> zstarts; std::vector<double> porosities; std::vector<double> permxs; std::vector<double> permys; std::vector<double> permzs; /* z_start is the topmost point of the subsample to extract */ for (double zstart = 0.0; zstart <= zmax-zlen; zstart += zresolution) { /* Horizontally, we pick by random, even though default behaviour is to have ilen=imax-min so that there is no randomness */ int istart = ri(); int jstart = rj(); ch.chop(istart, istart + ilen, jstart, jstart + jlen, zstart, std::min(zstart + zlen,zmax), resettoorigin); std::string subsampledgrdecl = filebase; // Output grdecl-data to file if a filebase is supplied. if (filebase != "") { std::ostringstream oss; oss << 'Z' << std::setw(4) << std::setfill('0') << zstart; subsampledgrdecl += oss.str(); subsampledgrdecl += ".grdecl"; ch.writeGrdecl(subsampledgrdecl); } try { /* The upscaling may fail to converge on icky grids, lets just pass by those */ if (upscale) { Opm::EclipseGridParser subparser = ch.subparser(); subparser.convertToSI(); Opm::SinglePhaseUpscaler upscaler; upscaler.init(subparser, Opm::SinglePhaseUpscaler::Fixed, minpermSI, z_tolerance, residual_tolerance, linsolver_verbosity, linsolver_type, false); Opm::SinglePhaseUpscaler::permtensor_t upscaled_K = upscaler.upscaleSinglePhase(); upscaled_K *= (1.0/(Opm::prefix::milli*Opm::unit::darcy)); zstarts.push_back(zstart); porosities.push_back(upscaler.upscalePorosity()); permxs.push_back(upscaled_K(0,0)); permys.push_back(upscaled_K(1,1)); permzs.push_back(upscaled_K(2,2)); } } catch (...) { std::cerr << "Warning: Upscaling chopped subsample at z=" << zstart << "failed, proceeding to next subsample\n"; } } if (upscale) { // Make stream of output data, to be outputted to screen and optionally to file std::stringstream outputtmp; outputtmp << "################################################################################################" << std::endl; outputtmp << "# Results from depth trend analysis on subsamples" << std::endl; outputtmp << "#" << std::endl; time_t now = time(NULL); outputtmp << "# Finished: " << asctime(localtime(&now)); utsname hostname; uname(&hostname); outputtmp << "# Hostname: " << hostname.nodename << std::endl; outputtmp << "#" << std::endl; outputtmp << "# Options used:" << std::endl; outputtmp << "# gridfilename: " << gridfilename << std::endl; outputtmp << "# i; min,len,max: " << imin << " " << ilen << " " << imax << std::endl; outputtmp << "# j; min,len,max: " << jmin << " " << jlen << " " << jmax << std::endl; outputtmp << "# z; min,len,max: " << zmin << " " << zlen << " " << zmax << std::endl; outputtmp << "# zresolution: " << zresolution << std::endl; outputtmp << "################################################################################################" << std::endl; outputtmp << "# zstart porosity permx permy permz" << std::endl; const int fieldwidth = outputprecision + 8; for (size_t sample = 1; sample <= porosities.size(); ++sample) { outputtmp << std::showpoint << std::setw(fieldwidth) << std::setprecision(outputprecision) << zstarts[sample-1] << '\t' << std::showpoint << std::setw(fieldwidth) << std::setprecision(outputprecision) << porosities[sample-1] << '\t' << std::showpoint << std::setw(fieldwidth) << std::setprecision(outputprecision) << permxs[sample-1] << '\t' << std::showpoint << std::setw(fieldwidth) << std::setprecision(outputprecision) << permys[sample-1] << '\t' << std::showpoint << std::setw(fieldwidth) << std::setprecision(outputprecision) << permzs[sample-1] << '\t' << std::endl; } if (resultfile != "") { std::cout << "Writing results to " << resultfile << std::endl; std::ofstream outfile; outfile.open(resultfile.c_str(), std::ios::out | std::ios::trunc); outfile << outputtmp.str(); outfile.close(); } std::cout << outputtmp.str(); } }