예제 #1
0
int main(int argc, char** argv)
{
    Opm::parameter::ParameterGroup param(argc, argv);
    // MPIHelper::instance(argc,argv);
    SinglePhaseUpscaler upscaler;
    upscaler.init(param);
    SinglePhaseUpscaler::permtensor_t upscaled_K = upscaler.upscaleSinglePhase();
    upscaled_K *= (1.0/(milli*darcy));
    std::cout.precision(15);
    std::cout << "Upscaled K in millidarcy:\n" << upscaled_K << std::endl;
}
예제 #2
0
int main(int varnum, char** vararg)
try
{ 

   /******************************************************************************
    * Step 1:
    * Process command line options
    */

    Dune::MPIHelper::instance(varnum, vararg);

   if (varnum == 1) { /* If no arguments supplied ("upscale_cap" is the first ('zero')  "argument") */
      usage();
      exit(1);
   }

   /*
     Populate options-map with default values
   */
   map<string,string> options;
   options.insert(make_pair("points",             "50"   )); // Number of saturation points (uniformly distributed within saturation endpoints)
   options.insert(make_pair("jFunctionCurve",     "4")); // Which column in the rock type file is the J-function curve
   options.insert(make_pair("output",             "")); // If this is set, output goes to screen and to this file. 
   options.insert(make_pair("outputprecision",    "8")); // number of decimals to print
   options.insert(make_pair("surfaceTension",     "11")); // Surface tension given in dynes/cm 
   options.insert(make_pair("maxPermContrast",    "1e7")); // maximum allowed contrast in each single-phase computation
   options.insert(make_pair("minPerm",            "1e-12")); // absoluted minimum allowed minimal cell permeability
   options.insert(make_pair("minPoro",            "0.0001")); // this limit is necessary for pcmin/max computation
   options.insert(make_pair("linsolver_tolerance", "1e-12"));  // used for swir/swmax check in upscale_cap

   // Conversion factor, multiply mD numbers with this to get m² numbers
   const double milliDarcyToSqMetre = 9.869233e-16;
   // Reference: http://www.spe.org/spe-site/spe/spe/papers/authors/Metric_Standard.pdf


   /* Check first if there is anything on the command line to look for */
   if (varnum == 1) {
      cout << "Error: No Eclipsefile or stonefiles found on command line." << endl;
      usageandexit();
   }


   /* Loop over all command line options in order to look 
      for options. 

      argidx loops over all the arguments here, and updates the
      variable 'argeclindex' *if* it finds any legal options,
      'argeclindex' is so that vararg[argeclindex] = the eclipse
      filename. If options are illegal, argeclindex will be wrong, 
      
   */
   int argeclindex = 0;
   for (int argidx = 1; argidx < varnum; argidx += 2)  {
       if (string(vararg[argidx]).substr(0,1) == "-")    {
           string searchfor = string(vararg[argidx]).substr(1); // Chop off leading '-'
           /* Check if it is a match */
           if (options.count(searchfor) == 1) {
               options[searchfor] = string(vararg[argidx+1]);
               cout << "Parsed command line option: " << searchfor << " := " << vararg[argidx+1] << endl;
               argeclindex = argidx + 2;
           }
           else {
               cout << "Option -" << searchfor << " unrecognized." << endl;
               usageandexit();
           }
       }
       else { 
           // if vararg[argidx] does not start in '-', 
           // assume we have found the position of the Eclipse-file.
           argeclindex = argidx;
           break; // out of for-loop, 
       }
   }
     
   // argeclindex should now point to the eclipse file
   static char* ECLIPSEFILENAME(vararg[argeclindex]);
   argeclindex += 1; // argeclindex jumps to next input argument, now it points to the stone files.
   
   // argeclindex now points to the first J-function. This index is not
   // to be touched now.
   static int JFindex = argeclindex;
   

   /* Check if at least one J-function is supplied on command line */
   if (varnum <= JFindex) {
       cerr << "Error: No J-functions found on command line." << endl;
       usageandexit();
   }
    
   
   /***********************************************************************
    * Step 2:
    * Load geometry and data from Eclipse file
    */
   
   // Read data from the Eclipse file and 
   // populate our vectors with data from the file

   const double emptycellvolumecutoff = 1e-10;
      
   // Test if filename exists and is readable
   ifstream eclipsefile(ECLIPSEFILENAME, ios::in);
   if (eclipsefile.fail()) {
       cerr << "Error: Filename " << ECLIPSEFILENAME << " not found or not readable." << endl;
       usageandexit();
   }
   eclipsefile.close(); 

   cout << "Parsing Eclipse file <" << ECLIPSEFILENAME << "> ... " << endl;
   Opm::EclipseGridParser eclParser(ECLIPSEFILENAME, false);
   
   // Check that we have the information we need from the eclipse file:  
   if (! (eclParser.hasField("SPECGRID") && eclParser.hasField("COORD") && eclParser.hasField("ZCORN")  
          && eclParser.hasField("PORO") && eclParser.hasField("PERMX") && eclParser.hasField("SATNUM"))) {  
       cerr << "Error: Did not find SPECGRID, COORD and ZCORN in Eclipse file " << ECLIPSEFILENAME << endl;  
       usageandexit();  
   }  
   
   vector<int>   satnums = eclParser.getIntegerValue("SATNUM");  
   vector<double>  poros = eclParser.getFloatingPointValue("PORO");  
   vector<double> permxs = eclParser.getFloatingPointValue("PERMX");  
   vector<int>  griddims = eclParser.getSPECGRID().dimensions;
    
   unsigned int maxSatnum = 0;
   const double maxPermContrast = atof(options["maxPermContrast"].c_str());
   const double minPerm = atof(options["minPerm"].c_str());
   const double minPoro = atof(options["minPoro"].c_str());

   /* Sanity check/fix on input for each cell:
      - Check that SATNUM are set sensibly, that is => 0 and < 1000, error if not.
      - Check that porosity is between 0 and 1, error if not.
        Set to minPoro if zero or less than minPoro (due to pcmin/max computation)
      - Check that permeability is zero or positive. Error if negative. 
        Set to minPerm if zero or less than minPerm.
      - Check maximum number of SATNUM values (can be number of rock types present)
   */
   for (unsigned int i = 0; i < satnums.size(); ++i) {
       if (satnums[i] < 0 || satnums[i] > 1000) { 
           cerr << "satnums[" << i << "] = " << satnums[i] << ", not sane, quitting." << endl;
           usageandexit();
       }
       if (satnums[i] > (int)maxSatnum) {
           maxSatnum = satnums[i];
       }
       if ((poros[i] >= 0) && (poros[i] < minPoro)) { // Truncate porosity from below
           poros[i] = minPoro;
       }
       if (poros[i] < 0 || poros[i] > 1) {
           cerr << "poros[" << i <<"] = " << poros[i] << ", not sane, quitting." << endl;
           usageandexit();
       }
       if ((permxs[i] >= 0) && (permxs[i] < minPerm)) { // Truncate permeability from below
           permxs[i] = minPerm;
       }
       if (permxs[i] < 0) {
           cerr << "permx[" << i <<"] = " << permxs[i] << ", not sane, quitting." << endl;
           usageandexit();
       }
       // Explicitly handle "no rock" cells, set them to minimum perm and zero porosity.
       if (satnums[i] == 0) {
           permxs[i] = minPerm;
           poros[i] = 0; // zero poro is fine for these cells, as they are not 
                         // used in pcmin/max computation.
       }
   }  


   /***************************************************************************
    * Step 3:
    * Load relperm- and J-function-curves for the stone types.
    * We read columns from text-files, syntax allowed is determined 
    * by MonotCubicInterpolator which actually opens and parses the 
    * text files.
    */

   // Number of stone-types is max(satnums):
   
   // If there is only one J-function supplied on the command line,
   // use that for all stone types.

   int stone_types = int(*(max_element(satnums.begin(), satnums.end())));
   std::vector<MonotCubicInterpolator> InvJfunctions; // Holds the inverse of the loaded J-functions.
   
   std::vector<string> JfunctionNames; // Placeholder for the names of the loaded J-functions.

   // Input for surfaceTension is dynes/cm
   // SI units are Joules/square metre
   const double surfaceTension     = atof(options["surfaceTension"].c_str()) * 1e-3; // multiply with 10^-3 to obtain SI units 
   const int jFunctionCurve        = atoi(options["jFunctionCurve"].c_str());
   const int interpolationPoints   = atoi(options["points"].c_str());
   const int outputprecision       = atoi(options["outputprecision"].c_str());

   // Handle two command line input formats, either one J-function for all stone types
   // or one each. If there is only one stone type, both code blocks below are equivalent.
   
   if (varnum == JFindex + stone_types) {
      for (int i=0 ; i < stone_types; ++i) {
         const char* ROCKFILENAME = vararg[JFindex+i];
         // Check if rock file exists and is readable:
         ifstream rockfile(ROCKFILENAME, ios::in);
         if (rockfile.fail()) {
            cerr << "Error: Filename " << ROCKFILENAME << " not found or not readable." << endl;
            usageandexit();
         }
         rockfile.close(); 
         MonotCubicInterpolator Jtmp;
         try {
             Jtmp = MonotCubicInterpolator(ROCKFILENAME, 1, jFunctionCurve); 
         }
         catch (const char * errormessage) {
             cerr << "Error: " << errormessage << endl;
             cerr << "Check filename and -jFunctionCurve" << endl;
             usageandexit();
         }
         // Invert J-function, now we get saturation as a function of pressure:
         if (Jtmp.isStrictlyMonotone()) {
            InvJfunctions.push_back(MonotCubicInterpolator(Jtmp.get_fVector(), Jtmp.get_xVector()));
            JfunctionNames.push_back(vararg[JFindex + i]);
         }
         else {
             cerr << "Error: Jfunction " << i+1 << " in rock file " << ROCKFILENAME << " was not invertible." << endl;
             usageandexit();
         }
      } 
   }
   
   else if (varnum == JFindex + 1) {
      for (int i=0; i < stone_types; ++i) {
         const char* ROCKFILENAME = vararg[JFindex];
         // Check if rock file exists and is readable:
         ifstream rockfile(ROCKFILENAME, ios::in);
         if (rockfile.fail()) {
            cerr << "Error: Filename " << ROCKFILENAME << " not found or not readable." << endl;
            usageandexit();
         }
         rockfile.close(); 
         MonotCubicInterpolator Jtmp;
         try {
             Jtmp = MonotCubicInterpolator(ROCKFILENAME, 1, jFunctionCurve);
         }
         catch(const char * errormessage) {
             cerr << "Error: " << errormessage << endl;
             cerr << "Check filename and -jFunctionCurve" << endl;
             usageandexit();
         }
         // Invert J-function, now we get saturation as a function of pressure:
         if (Jtmp.isStrictlyMonotone()) {
            InvJfunctions.push_back(MonotCubicInterpolator(Jtmp.get_fVector(), Jtmp.get_xVector()));
            JfunctionNames.push_back(vararg[JFindex]);
         }
         else {
            cerr << "Error: Jfunction " << i+1 << " in rock file " << ROCKFILENAME << " was not invertible." << endl;
            usageandexit();
         }
      }
   }
   else {
      cerr << "Error:  Wrong number of stone-functions provided. " << endl;
      usageandexit();
   }
   

   /******************************************************************************
    * Step 5:
    * Go through each cell and calculate the minimum and
    * maximum capillary pressure possible in the cell, given poro,
    * perm and the J-function for the cell.  This depends on the
    * J-function in that they represent all possible saturations,
    * ie. we do not want to extrapolate the J-functions (but we might
    * have to do that later in the computations).
    */

   if (maxPermContrast == 0) {
       cout << "Illegal contrast value" << endl;
       usageandexit();
   }
   

   // Construct an object for single-phase upscaling, since we need to get some
   // information from the grid.
   SinglePhaseUpscaler upscaler;
   eclParser.convertToSI();
   upscaler.init(eclParser, SinglePhaseUpscaler::Fixed,
                 Opm::unit::convert::from(minPerm, Opm::prefix::milli*Opm::unit::darcy),
                 0.0, 1e-8, 0, 1, false);  // options on this line are noops for upscale_cap

   vector<double> cellVolumes, cellPoreVolumes; 
   cellVolumes.resize(satnums.size(), 0.0);
   cellPoreVolumes.resize(satnums.size(), 0.0);

   /* Volumes/saturations pr. rocktype */
   vector<double> cellporevolume_rocktype;
   cellporevolume_rocktype.resize(maxSatnum + 1, 0.0);

   vector<double> watervolume_rocktype;
   watervolume_rocktype.resize(maxSatnum + 1, 0.0);


   /* Find minimium and maximum capillary pressure values in each
      cell, and use the global min/max as the two initial pressure
      points for computations.
   
      Also find max single-phase permeability, used to obey the 
      maxPermContrast option.

      Also find properly upscaled saturation endpoints, these are
      printed out to stdout for reference during computations, but will 
      automatically appear as the lowest and highest saturation points
      in finished output.
   */
   int tesselatedCells = 0; // for counting "active" cells (Sintef interpretation of "active")
   double Pcmax = -DBL_MAX, Pcmin = DBL_MAX;
   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 = InvJfunctions[int(satnums[cell_idx])-1].getMinimumX().first
	       / sqrt(permxs[cell_idx] * milliDarcyToSqMetre/poros[cell_idx]) * surfaceTension;
	   Pcmin = min(Pcmincandidate, Pcmin);
           
	   double Pcmaxcandidate = InvJfunctions[int(satnums[cell_idx])-1].getMaximumX().first
	       / sqrt(permxs[cell_idx] * milliDarcyToSqMetre/poros[cell_idx]) * surfaceTension;
	   Pcmax = max(Pcmaxcandidate, Pcmax);
           
	   maxSinglePhasePerm = max( maxSinglePhasePerm, permxs[cell_idx]);
           
	   cellporevolume_rocktype[satnums[cell_idx]] += cellPoreVolumes[cell_idx];
	   double minSw = InvJfunctions[int(satnums[cell_idx])-1].getMinimumF().second;
	   double maxSw = InvJfunctions[int(satnums[cell_idx])-1].getMaximumF().second;
           
	   // cout << "minSwc: " << minSw << endl;
	   // cout << "maxSwc: " << maxSw << endl;
           
	   // Add irreducible water saturation volume
	   Swirvolume += minSw * cellPoreVolumes[cell_idx];
	   Sworvolume += maxSw * cellPoreVolumes[cell_idx];
           
       }
       ++tesselatedCells; // keep count.
   }

   //double minSinglePhasePerm = max(maxSinglePhasePerm/maxPermContrast, minPerm);
   
   cout << "Pcmin:    " << Pcmin << endl;
   cout << "Pcmax:    " << Pcmax << endl;

   if (Pcmin > Pcmax) {
       cerr << "ERROR: No legal capillary pressures found for this system. Exiting..." << endl;
       usageandexit();
   }

   // Total porevolume and total volume -> upscaled porosity:
   double poreVolume = std::accumulate(cellPoreVolumes.begin(), 
                                       cellPoreVolumes.end(),
                                       0.0);
   double volume = std::accumulate(cellVolumes.begin(),
                                   cellVolumes.end(),
                                   0.0);

   double Swir = Swirvolume/poreVolume;
   double Swor = Sworvolume/poreVolume;

   cout << "LF Pore volume:    " << poreVolume << endl;
   cout << "LF Volume:         " << volume << endl;
   cout << "Upscaled porosity: " << poreVolume/volume << endl;
   cout << "Upscaled Swir:     " << Swir << endl;
   cout << "Upscaled Swmax:    " << Swor << endl; //Swor=1-Swmax

   // Sometimes, if Swmax=1 or Swir=0 in the input tables, the upscaled 
   // values can be a little bit larger (within machine precision) and
   // the check below fails. Hence, check if these values are within the 
   // the [0 1] interval within some precision
   double linsolver_tolerance = atof(options["linsolver_tolerance"].c_str());
   if (Swor > 1.0 && Swor - linsolver_tolerance < 1.0) {
       Swor = 1.0;
   }
   if (Swir < 0.0 && Swir + linsolver_tolerance > 0.0) {
       Swir = 0.0;
   }
   if (Swir < 0.0 || Swir > 1.0 || Swor < 0.0 || Swor > 1.0) {
       cerr << "ERROR: Swir/Swor unsensible. Check your input. Exiting";
       usageandexit();
   }      

   /***************************************************************************
    * Step 6:
    * Upscale capillary pressure function.
    *
    * This is upscaled in advance in order to be able to have uniformly distributed
    * saturation points for which upscaling is performed.
    *
    * Capillary pressure points are chosen heuristically in order to
    * ensure largest saturation interval between two saturation points
    * is 1/1000 of the saturation interval. Monotone cubic interpolation
    * will be used afterwards for accessing the tabulated values.
    */

   MonotCubicInterpolator WaterSaturationVsCapPressure;
   
   double largestSaturationInterval = Swor-Swir;
   
   double Ptestvalue = Pcmax;

   vector<MonotCubicInterpolator> watersaturation_rocktype;
   for (unsigned int satidx=0; satidx <= maxSatnum; ++satidx) {
       MonotCubicInterpolator tmp;
       watersaturation_rocktype.push_back(tmp);
   }
   
   while (largestSaturationInterval > (Swor-Swir)/200.0) {
       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)
           */
           pair<double,double> SatDiff = WaterSaturationVsCapPressure.getMissingX();
           Ptestvalue = SatDiff.first;
           largestSaturationInterval = SatDiff.second;
       }
       
       // Check for saneness of Ptestvalue:
       if (std::isnan(Ptestvalue) || std::isinf(Ptestvalue)) {
           cerr << "ERROR: Ptestvalue was inf or nan" << endl;
           break; // Jump out of while-loop, just print out the results
           // up to now and exit the program
       }

       // Reset array to zero.
       for (unsigned int satidx = 0; satidx <= maxSatnum; ++satidx) {
           watervolume_rocktype[satidx] = 0.0;
       }
     
       double waterVolume = 0.0;
       for (unsigned int cell_idx = 0; cell_idx < satnums.size(); ++cell_idx) {
           if (cellVolumes[cell_idx] > emptycellvolumecutoff) {
               double waterSaturationCell = 0.0;
               if (satnums[cell_idx] > 0) { // handle "no rock" cells with satnum zero
                   double PtestvalueCell;
                   PtestvalueCell = Ptestvalue;
                   double Jvalue = sqrt(permxs[cell_idx] * milliDarcyToSqMetre/poros[cell_idx]) 
                       * PtestvalueCell / surfaceTension;
                   //cout << "JvalueCell: " << Jvalue << endl;
                   waterSaturationCell 
                       = InvJfunctions[int(satnums[cell_idx])-1].evaluate(Jvalue);
               }
               waterVolume += waterSaturationCell  * cellPoreVolumes[cell_idx];
               watervolume_rocktype[satnums[cell_idx]] += waterSaturationCell * cellPoreVolumes[cell_idx];
               
           }
       }
       WaterSaturationVsCapPressure.addPair(Ptestvalue, waterVolume/poreVolume);
       //       cout << "Ptestvalue " << Ptestvalue << " sat: " << waterVolume/poreVolume << endl;
       for (unsigned int satidx = 1; satidx <= maxSatnum; ++satidx) {
           // cout << "satidx "<< satidx << " " << watervolume_rocktype[satidx]/cellporevolume_rocktype[satidx] << endl;
           //cout << "watvol: " << watervolume_rocktype[satidx] << " porevol " << cellporevolume_rocktype[satidx] << endl;
           if (cellporevolume_rocktype[satidx] > 0) {
               watersaturation_rocktype[satidx].addPair(Ptestvalue, watervolume_rocktype[satidx]/cellporevolume_rocktype[satidx]);
           }
           else {
               watersaturation_rocktype[satidx].addPair(Ptestvalue, 0.0);
           }
       }

   }

   // Check if the saturation vs cap pressure curve is monotone
   // If not, it would have been a problem for upscale_relperm, but
   // it is not as critical here, so we only issue a warning
   // (upscale_relperm solves this by issung chopFlatEndpoints and possibly shrinkFlatAreas, 
   // but this is trickier to implement in this code due to watersaturation_rocktype[satidx])
   if (!WaterSaturationVsCapPressure.isStrictlyMonotone()) {
       {
           cerr << "Warning: Upscaled water saturation not strictly monotone in capillary pressure." << endl;
           cerr << "         Unphysical input data?." << endl;
       }
   }
   MonotCubicInterpolator CapPressureVsWaterSaturation(WaterSaturationVsCapPressure.get_fVector(), 
                                                       WaterSaturationVsCapPressure.get_xVector());

   
   /*********************************************************************************
    *  Step 9
    *
    * Output results to stdout and optionally to file. Note, we only output to
    * file if the '-outputWater'-option and/or '-outputOil' has been set, as this option is an
    * empty string by default.
    */
   vector<double> Pvalues = WaterSaturationVsCapPressure.get_xVector(); 
   vector<double> Satvalues = WaterSaturationVsCapPressure.get_fVector(); 
   
   vector<vector<double> > watersaturation_rocktype_values;
   vector<double> tmp;
   watersaturation_rocktype_values.push_back(tmp); // dummy zero index element
   for (unsigned int satidx=1; satidx <= maxSatnum; ++satidx) {
       watersaturation_rocktype_values.push_back(watersaturation_rocktype[satidx].get_fVector());
   }
   stringstream outputtmp;
   
   // Print a table of all computed values:
   outputtmp << "######################################################################" << endl;
   outputtmp << "# Results from upscaling capillary pressure and water saturations."<< endl;
   outputtmp << "#" << endl;
   time_t now = std::time(NULL);
   outputtmp << "# Finished: " << asctime(localtime(&now));
   
   utsname hostname;   uname(&hostname);
   outputtmp << "# Hostname: " << hostname.nodename << endl;

   outputtmp << "#" << endl;
   outputtmp << "# Eclipse file: " << ECLIPSEFILENAME << endl;
   outputtmp << "#        cells: " << tesselatedCells << endl;
   outputtmp << "#  Pore volume: " << poreVolume << endl;
   outputtmp << "#       volume: " << volume << endl;
   outputtmp << "#     Porosity: " << poreVolume/volume << endl;
   outputtmp << "#" << endl;
   for (int i=0; i < stone_types ; ++i) {
       outputtmp << "# Stone " << i+1 << ": " << JfunctionNames[i] << " (" << InvJfunctions[i].getSize() << " points)" <<  endl;
   }
   outputtmp << "# " << endl;
   outputtmp << "# Options used:" << endl;
   outputtmp << "#          jFunctionCurve: " << options["jFunctionCurve"] << endl;
   outputtmp << "#                  points: " << options["points"] << endl;
   outputtmp << "#         maxPermContrast: " << options["maxPermContrast"] << endl;
   outputtmp << "#          surfaceTension: " << options["surfaceTension"] << endl;   
   outputtmp << "######################################################################" << endl;
   outputtmp << "#         Pc (Pa)         Sw              Sw1           Sw2       Sw3 etc.." << endl; 
   
   
  // If user wants interpolated output, do monotone cubic interpolation
   // by modifying the data vectors that are to be printed
   if (interpolationPoints > 0) {
       // Find min and max for saturation values
       double xmin = +DBL_MAX;
       double xmax = -DBL_MAX;
       for (unsigned int i = 0; i < Satvalues.size(); ++i) {
           if (Satvalues[i] < xmin) {
               xmin = Satvalues[i];
           }
           if (Satvalues[i] > xmax) {
               xmax = Satvalues[i];
           }
       }
       // Make uniform grid in saturation axis
       vector<double> SatvaluesInterp;
       for (int i = 0; i < interpolationPoints; ++i) {
           SatvaluesInterp.push_back(xmin + ((double)i)/((double)interpolationPoints-1)*(xmax-xmin));
       }
       // Now capillary pressure and computed relperm-values must be viewed as functions
       // of saturation, and then interpolated on the uniform saturation grid.

       // Now overwrite existing Pvalues and saturation-data with interpolated data:
       MonotCubicInterpolator PvaluesVsSaturation(Satvalues, Pvalues);
       Pvalues.clear();
       for (int i = 0; i < interpolationPoints; ++i) {
           Pvalues.push_back(PvaluesVsSaturation.evaluate(SatvaluesInterp[i]));
       }
       for (unsigned int satidx = 1; satidx <= maxSatnum; ++satidx) {
           MonotCubicInterpolator WaterSaturationRocktypeVsSaturation(Satvalues, watersaturation_rocktype_values[satidx]);
           watersaturation_rocktype_values[satidx].clear();
           for (int i=0; i < interpolationPoints; ++i) {
               watersaturation_rocktype_values[satidx].push_back(WaterSaturationRocktypeVsSaturation.evaluate(SatvaluesInterp[i]));
           }
       }
       // Now also overwrite Satvalues
       Satvalues.clear();
       Satvalues = SatvaluesInterp;
   }


   const int fieldwidth = outputprecision + 8;
   for (unsigned int i=0; i < Satvalues.size(); ++i) {
       outputtmp << showpoint << setw(fieldwidth) << setprecision(outputprecision) << Pvalues[i]; 
       outputtmp << showpoint << setw(fieldwidth) << setprecision(outputprecision) << Satvalues[i]; 
       for (unsigned int satidx = 1; satidx <= maxSatnum; ++satidx) { 
           outputtmp << showpoint << setw(fieldwidth) << setprecision(outputprecision) 
                     << watersaturation_rocktype_values[satidx][i]; 
       } 
       outputtmp << endl; 
       
   }
   
   cout << outputtmp.str();
   
   if (options["output"] != "") {
       cout << "Writing results to " << options["output"] << endl;
       ofstream outfile;
       outfile.open(options["output"].c_str(), ios::out | ios::trunc);
       outfile << outputtmp.str();
       outfile.close();      
   }


   return 0;
}
catch (const std::exception &e) {
    std::cerr << "Program threw an exception: " << e.what() << "\n";
    throw;
}
예제 #3
0
/**
   @brief Computes simple statistics.
*/
int main(int varnum, char** vararg) try {        

    Dune::MPIHelper::instance(varnum, vararg);

    const double emptycellvolumecutoff = 1e-10;

    bool anisotropic_input = false;

    if (varnum ==  1) { // If no arguments supplied ("upscale_avg" is the first argument)
        cout << "Error: No eclipsefile provided" << endl;
        usage();
        exit(1);
    } 


   /*
     Populate options-map with default values
   */
   map<string,string> options;
   options.insert(make_pair("use_actnum",   "0"     )); //Use ACTNUM as indicator for which cells to be included
                                                        //Default is not to use actnum for this
                                                        //When use_actnum is used the cell volumes of the cells with actnum 0 is set to 0

   /* Loop over all command line options in order to look 
      for options. 

      argidx loops over all the arguments here, and updates the
      variable 'argeclindex' *if* it finds any legal options,
      'argeclindex' is so that vararg[argeclindex] = the eclipse
      filename. If options are illegal, argeclindex will be wrong, 
      
   */
   int argeclindex = 0;
   for (int argidx = 1; argidx < varnum; argidx += 2)  {
       if (string(vararg[argidx]).substr(0,1) == "-")    {
           string searchfor = string(vararg[argidx]).substr(1); // Chop off leading '-'
           /* Check if it is a match */
           if (options.count(searchfor) == 1) {
               options[searchfor] = string(vararg[argidx+1]);
               cout << "Parsed command line option: " << searchfor << " := " << vararg[argidx+1] << endl;
               argeclindex = argidx + 2;
           }
           else {
               cout << "Option -" << searchfor << " unrecognized." << endl;
               usageandexit();
           }
       }
       else { 
           // if vararg[argidx] does not start in '-', 
           // assume we have found the position of the Eclipse-file.
           argeclindex = argidx;
           break; // out of for-loop, 
       }
   }

    const char* ECLIPSEFILENAME(vararg[argeclindex]);

    // Test if filename exists and is readable
    ifstream eclipsefile(ECLIPSEFILENAME, ios::in);
    if (eclipsefile.fail()) {
        cerr << "Error: Filename " << ECLIPSEFILENAME << " not found or not readable." << endl;
        usage();
        exit(1);
    }
    eclipsefile.close();
    
    // Variables for timing/profiling
    clock_t start, finish;
    double timeused = 0;
        
    /***********************************************************************
    * Step X
    * Load geometry and data from Eclipse file
    */
    cout << "Parsing Eclipse file <" << ECLIPSEFILENAME << "> ... ";
    flush(cout);   start = clock();
    // eclParser_p is here a pointer to an object of type Opm::EclipseGridParser
    // (this pointer trick is necessary for the try-catch-clause to work)
    
   Opm::ParseContext parseMode;
   Opm::ParserPtr parser(new Opm::Parser());
   Opm::addNonStandardUpscalingKeywords(parser);
   Opm::DeckConstPtr deck(parser->parseFile(ECLIPSEFILENAME , parseMode));

    Opm::EclipseGridInspector eclInspector(deck);

    finish = clock();   timeused = (double(finish)-double(start))/CLOCKS_PER_SEC;
    cout << " (" << timeused <<" secs)" << endl;
    
    // Check that we have the information we need from the eclipse file, we will check PERM-fields later
    if (! (deck->hasKeyword("SPECGRID") && deck->hasKeyword("COORD") && deck->hasKeyword("ZCORN"))) {  
        cerr << "Error: Did not find SPECGRID, COORD and ZCORN in Eclipse file " << ECLIPSEFILENAME << endl;  
        usage();  
        exit(1);  
    }

   SinglePhaseUpscaler upscaler;
   upscaler.init(deck, SinglePhaseUpscaler::Fixed,
                 Opm::unit::convert::from(1e-9, Opm::prefix::milli*Opm::unit::darcy),
                 1e-8, 0, 1, false);


    bool use_actnum = false;
    vector<int> actnum;
    if (atoi(options["use_actnum"].c_str())>0) {
        if (deck->hasKeyword("ACTNUM")) {
            use_actnum = true;
            actnum = deck->getKeyword("ACTNUM").getIntData();
            cout << actnum[0] << " " << actnum[1] << endl;
        }
        else {
            cout << "Error: option use_actnum set to 1 but Gridfile " << ECLIPSEFILENAME << " does not include any field ACTNUM." << endl;
            usage();
            exit(1);            
        }
    }

    // Print header for the output
    
    cout << "Statistics for filename: " << ECLIPSEFILENAME << endl;
    cout << "-----------------------------------------------------" << endl;
    bool doporosity = false;
    if (deck->hasKeyword("PORO")) {
        doporosity = true;
    }
    bool dontg = false;
    if (deck->hasKeyword("NTG")) {
        // Ntg only used together with PORO
        if (deck->hasKeyword("PORO")) dontg = true;
    }    

    bool doperm = false;
    if (deck->hasKeyword("PERMX")) {
        doperm = true;
        if (deck->hasKeyword("PERMY") && deck->hasKeyword("PERMZ")) {
            anisotropic_input = true;
            cout << "Info: PERMY and PERMZ present in data file." << endl;
        }
    }

    // Global number of cells (includes inactive cells)
    const auto& specgridRecord = deck->getKeyword("SPECGRID").getRecord(0);
    vector<int>  griddims(3);
    griddims[0] = specgridRecord.getItem("NX").get< int >(0);
    griddims[1] = specgridRecord.getItem("NY").get< int >(0);
    griddims[2] = specgridRecord.getItem("NZ").get< int >(0);

    int num_eclipse_cells = griddims[0] * griddims[1] * griddims[2];


    cout << "Active and inactive cells: " << num_eclipse_cells << " (" <<
      griddims[0] << " x " <<
      griddims[1] << " x " <<
      griddims[2] << ")" << endl;
    int pillars = (griddims[0]+1) * (griddims[1]+1);
    cout << "                  Pillars: " << pillars << " (" << griddims[0]+1 << 
        " x " << griddims[1]+1 << ")" << endl;
    
    // Find max and min in x-, y- and z-directions
    std::array<double, 6> gridlimits = eclInspector.getGridLimits();
    cout << "                 x-limits: " << gridlimits[0] << " -- " << gridlimits[1] << endl;
    cout << "                 y-limits: " << gridlimits[2] << " -- " << gridlimits[3] << endl;
    cout << "                 z-limits: " << gridlimits[4] << " -- " << gridlimits[5] << endl;

    // First do overall statistics
    vector<double> cellVolumes, cellPoreVolumes, netCellVolumes, netCellPoreVolumes;
    cellVolumes.resize(num_eclipse_cells, 0.0);
    cellPoreVolumes.resize(num_eclipse_cells, 0.0);
    netCellVolumes.resize(num_eclipse_cells, 0.0);
    netCellPoreVolumes.resize(num_eclipse_cells, 0.0);
    int active_cell_count = 0;
    vector<double> poros, ntgs;
    vector<double> permxs, permys, permzs;
    if (doporosity) {
        poros = deck->getKeyword("PORO").getRawDoubleData();
    }
    if (dontg) {
        ntgs = deck->getKeyword("NTG").getRawDoubleData();
    }
    if (doperm) {
        permxs = deck->getKeyword("PERMX").getRawDoubleData();
        if (anisotropic_input) {
            permys = deck->getKeyword("PERMY").getRawDoubleData();
            permzs = deck->getKeyword("PERMZ").getRawDoubleData();
        }
        
    }
    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) {
      size_t cell_idx = ecl_idx[c->index()];
      
      //for (size_t cell_idx = 0; cell_idx < num_eclipse_cells; ++cell_idx) {
        if (!use_actnum){
          cellVolumes[cell_idx] = c->geometry().volume();
        }
        else {
          cellVolumes[cell_idx] = c->geometry().volume() * actnum[cell_idx];
        }
        if (cellVolumes[cell_idx] > emptycellvolumecutoff) {
            ++active_cell_count;
            if (doporosity) {
                cellPoreVolumes[cell_idx] = cellVolumes[cell_idx] * poros[cell_idx];
            }
            if (dontg) {
                netCellPoreVolumes[cell_idx] = cellVolumes[cell_idx] * poros[cell_idx] * ntgs[cell_idx];
                netCellVolumes[cell_idx] = cellVolumes[cell_idx] * ntgs[cell_idx];
            }
        }
    }
    
    cout << "             Active cells: " << active_cell_count << " (" << (double)active_cell_count/(double)num_eclipse_cells*100.0 << "%)" << endl;
    double volume = std::accumulate(cellVolumes.begin(),
                                    cellVolumes.end(),
                                    0.0);
    cout << "             Total volume: " << volume << endl;
    if (doporosity) {
        double poreVolume = std::accumulate(cellPoreVolumes.begin(), 
                                            cellPoreVolumes.end(),
                                            0.0);
        cout << "         Total porevolume: " << poreVolume << endl;
        cout << "        Upscaled porosity: " << poreVolume/volume << endl;
        
        int zeroporocells = 0;
        int negativeporocells = 0;
        for (size_t cell_idx = 0; cell_idx < (size_t)num_eclipse_cells; ++cell_idx) {
            if  (poros[cell_idx] == 0) {
                ++zeroporocells;
            }
            if (poros[cell_idx] < 0 ) {
                ++negativeporocells;
            }
        }
        if  (zeroporocells > 0) {
            cout << " Cells with zero porosity: " << zeroporocells << endl;
        }
        if  (negativeporocells > 0) {
            cout << "Cells with negative porosity: " << negativeporocells << endl;
        }
    }
    if (dontg) {
        double netVolume = std::accumulate(netCellVolumes.begin(),
                                               netCellVolumes.end(),
                                               0.0);
        cout << "         Total net volume: " << netVolume << endl;
        cout << "             Upscaled NTG: " << netVolume/volume << endl;        
        double netPoreVolume = std::accumulate(netCellPoreVolumes.begin(), 
                                               netCellPoreVolumes.end(),
                                               0.0);
        cout << "     Total net porevolume: " << netPoreVolume << endl;
        cout << "    Upscaled net porosity: " << netPoreVolume/netVolume << endl;        
    }
    
    double permxsum = 0.0, permysum = 0.0, permzsum = 0.0;
    double invpermxsum = 0.0, invpermysum = 0.0, invpermzsum = 0.0;
    double volpermxsum = 0.0, volpermysum = 0.0, volpermzsum = 0.0;
    double invvolpermxsum = 0.0, invvolpermysum = 0.0, invvolpermzsum = 0.0;
    double logpermxsum = 0.0, logpermysum = 0.0, logpermzsum = 0.0;
    double logvolpermxsum = 0.0, logvolpermysum = 0.0, logvolpermzsum=0.0; 
    if (doperm) {
        int zeropermcells = 0;
        int negativepermcells = 0;
        for (size_t cell_idx = 0; cell_idx < (size_t)num_eclipse_cells; ++cell_idx) {
            if (cellVolumes[cell_idx] > emptycellvolumecutoff) {
                permxsum += permxs[cell_idx];
                volpermxsum += permxs[cell_idx] * cellVolumes[cell_idx];
                invpermxsum += 1.0/permxs[cell_idx];
                invvolpermxsum += cellVolumes[cell_idx] / permxs[cell_idx];
                logpermxsum += log(permxs[cell_idx]);
                logvolpermxsum += log(permxs[cell_idx]) * cellVolumes[cell_idx];
                if (permxs[cell_idx] == 0) {
                    ++zeropermcells;
                }
                if (permxs[cell_idx] < 0) {
                    ++negativepermcells;
                }
                if (anisotropic_input) {
                    permysum += permys[cell_idx];
                    volpermysum += permys[cell_idx] * cellVolumes[cell_idx];
                    invpermysum += 1.0/permys[cell_idx];
                    invvolpermysum += cellVolumes[cell_idx] / permys[cell_idx];
                    logpermysum += log(permys[cell_idx]);
                    logvolpermysum += log(permys[cell_idx]) * cellVolumes[cell_idx];
                    
                    permzsum += permzs[cell_idx];
                    volpermzsum += permzs[cell_idx] * cellVolumes[cell_idx];
                    invpermzsum += 1.0/permzs[cell_idx];
                    invvolpermzsum += cellVolumes[cell_idx] / permzs[cell_idx];
                    logpermzsum += log(permzs[cell_idx]);
                    logvolpermzsum += log(permzs[cell_idx]) * cellVolumes[cell_idx];
                }
            }
        }
        
        cout << "Total arithmetic permeability average: " << volpermxsum/volume << endl;
        cout << "  Total harmonic permeability average: " << volume/invvolpermxsum << endl;
        cout << " Total geometric permeability average: " << exp(logvolpermxsum/volume) << endl;
        cout << "Total arithmetic permeability average: " << permxsum/((double)active_cell_count) << " (not volume-weighted)" << endl;
        cout << "  Total harmonic permeability average: " << ((double)active_cell_count)/invpermxsum << " (not volume-weighted)" << endl;
        cout << " Total geometric permeability average: " << exp(logpermxsum/(double)active_cell_count) << " (not volume-weighted)" << endl;
        
        if (anisotropic_input) {
            cout << endl;
            cout << "Total arithmetic permeability (y) average: " << volpermysum/volume << endl;
            cout << "  Total harmonic permeability (y) average: " << volume/invvolpermysum << endl;
            cout << " Total geometric permeability (y) average: " << exp(logvolpermysum/volume) << endl;
            cout << "Total arithmetic permeability (y) average: " << permysum/((double)active_cell_count) << " (not volume-weighted)" << endl;
            cout << "  Total harmonic permeability (y) average: " << ((double)active_cell_count)/invpermysum << " (not volume-weighted)" << endl;
            cout << " Total geometric permeability (y) average: " << exp(logpermysum/(double)active_cell_count) << " (not volume-weighted)" << endl;
            cout << endl;
            cout << "Total arithmetic permeability (z) average: " << volpermzsum/volume << endl;
            cout << "  Total harmonic permeability (z) average: " << volume/invvolpermzsum << endl;
            cout << " Total geometric permeability (z) average: " << exp(logvolpermzsum/volume) << endl;
            cout << "Total arithmetic permeability (z) average: " << permzsum/((double)active_cell_count) << " (not volume-weighted)" << endl;
            cout << "  Total harmonic permeability (z) average: " << ((double)active_cell_count)/invpermzsum << " (not volume-weighted)" << endl;
            cout << " Total geometric permeability (z) average: " << exp(logpermzsum/(double)active_cell_count) << " (not volume-weighted)" << endl;
            
        }

        if  (zeropermcells > 0) {
            cout << endl <<  "     Cells with zero (x) permeability: " << zeropermcells << endl;
        }
        if  (negativepermcells > 0) {
            cout << " Cells with negative (x) permeability: " << negativepermcells << endl;
        }
    }

    // Then do statistics on rocktype by rocktype basis    
    bool dosatnums = false;
    vector<int> satnums;
    if (deck->hasKeyword("SATNUM")) {
        dosatnums = true;
        satnums = deck->getKeyword("SATNUM").getIntData();
    } // If SATNUM was not present, maybe ROCKTYPE is there, 
    // if so, we will use it as SATNUM.
    else if (deck->hasKeyword("ROCKTYPE")) {
        dosatnums = true;
        satnums = deck->getKeyword("ROCKTYPE").getIntData();
    }


    if (dosatnums) {
        int maxsatnumvalue = 0;
        // Check that SATNUM are set sensibly, that is > 0 and < 1000, and find number
        // of unique satnums present ( = number of rocktypes)
        for (size_t i = 0; i < (size_t)satnums.size(); ++i) {
            if (satnums[i] > maxsatnumvalue) {
                maxsatnumvalue = satnums[i];
            }
            if (satnums[i] < 0 || satnums[i] > 1000) { 
                cerr << "satnums[" << i << "] = " << satnums[i] << ", not sane, quitting." << endl;
                exit(1);
            }
        }

        vector<double> permxsum_rocktype;
        permxsum_rocktype.resize(maxsatnumvalue+1, 0.0);
        
        vector<double> invpermxsum_rocktype;
        invpermxsum_rocktype.resize(maxsatnumvalue+1, 0.0); // for harmonic average

        vector<double> volpermxsum_rocktype;              // volume weighted
        volpermxsum_rocktype.resize(maxsatnumvalue+1, 0.0);
        
        vector<double> invvolpermxsum_rocktype;           // volume weighted    
        invvolpermxsum_rocktype.resize(maxsatnumvalue+1, 0.0); // for harmonic average

        vector<double> porevolumesum_rocktype;
        porevolumesum_rocktype.resize(maxsatnumvalue+1, 0.0);

        vector<double> porosityvariancesum_rocktype; // for estimation of porosity variance within rocktype
        porosityvariancesum_rocktype.resize(maxsatnumvalue+1, 0.0);

        vector<double> volumesum_rocktype;
        volumesum_rocktype.resize(maxsatnumvalue+1, 0.0);

        vector<int> totalcellcount_rocktype;
        totalcellcount_rocktype.resize(maxsatnumvalue+1, 0);

        vector<int> activecellcount_rocktype;
        activecellcount_rocktype.resize(maxsatnumvalue+1, 0);

        // Now loop over cells to collect statistics pr. rocktype
        for (size_t cell_idx = 0; cell_idx < (size_t)num_eclipse_cells; ++cell_idx) {
            ++totalcellcount_rocktype[satnums[cell_idx]];
            if (cellVolumes[cell_idx] > emptycellvolumecutoff) {
                ++activecellcount_rocktype[satnums[cell_idx]];
                volumesum_rocktype[satnums[cell_idx]] += cellVolumes[cell_idx];
                if (doperm) {
                    permxsum_rocktype[satnums[cell_idx]] += permxs[cell_idx];
                    invpermxsum_rocktype[satnums[cell_idx]] += 1.0/permxs[cell_idx];

                    volpermxsum_rocktype[satnums[cell_idx]] += permxs[cell_idx] * cellVolumes[cell_idx];
                    invvolpermxsum_rocktype[satnums[cell_idx]] += cellVolumes[cell_idx] / permxs[cell_idx];
                    
                }
                if (doporosity) {
                    porevolumesum_rocktype[satnums[cell_idx]] += cellVolumes[cell_idx] * poros[cell_idx];
                }
            }
        }

        // Compute the sample variance in porosity per rock type        
        /*
          phi_avg = 1/V*sum(v_i*phi_i)
          phi_var = 1/V*sum(v_i*(phi_i-phi_avg)^2)
        */
        if (doporosity) {
            for (size_t cell_idx = 0; cell_idx < (size_t)num_eclipse_cells; ++cell_idx) {
                if (cellVolumes[cell_idx] > emptycellvolumecutoff) {
                    porosityvariancesum_rocktype[satnums[cell_idx]] += cellVolumes[cell_idx] 
                        * pow((poros[cell_idx]-porevolumesum_rocktype[satnums[cell_idx]]/volumesum_rocktype[satnums[cell_idx]]),2);
                                       
                }
            }  
        }      
        
        // Now loop over rocktypes in order to print statistics

        // Does SATNUM/ROCKTYPE start with 0 or 1?
        for (int rocktype_idx = 0; rocktype_idx <= maxsatnumvalue; ++rocktype_idx) {
            if (activecellcount_rocktype[rocktype_idx] > 0) {
                cout << endl << "Statistics for rocktype " << rocktype_idx << endl;
                
                cout     << "         Volume: " << volumesum_rocktype[rocktype_idx] << " (" << volumesum_rocktype[rocktype_idx]/volume*100 << "%)" << endl;
                cout     << "    Total cells: " << totalcellcount_rocktype[rocktype_idx] << " (" << (double)totalcellcount_rocktype[rocktype_idx]/(double)num_eclipse_cells*100 << "%)" << endl;
                cout     << "   Active cells: " << activecellcount_rocktype[rocktype_idx] << " (" << 
                    (double)activecellcount_rocktype[rocktype_idx]/(double)totalcellcount_rocktype[rocktype_idx]*100.0 << "% within rocktype)" << endl;
                if (doporosity) {
                    cout << "     Porevolume: " << porevolumesum_rocktype[rocktype_idx] << endl;
                    cout << "       Porosity: " << porevolumesum_rocktype[rocktype_idx]/volumesum_rocktype[rocktype_idx] << endl;
                    cout << "   Porosity var: " << porosityvariancesum_rocktype[rocktype_idx]/volumesum_rocktype[rocktype_idx] << endl;
                }
                if (doperm) {
                    cout << "Perm arith. avg: " << volpermxsum_rocktype[rocktype_idx]/volumesum_rocktype[rocktype_idx] << endl;
                    cout << " Perm harm. avg: " << volumesum_rocktype[rocktype_idx]/invvolpermxsum_rocktype[rocktype_idx] << endl;
                    cout << "Perm arith. avg: " << permxsum_rocktype[rocktype_idx]/activecellcount_rocktype[rocktype_idx] << " (not volume-weighted)" << endl;
                    cout << " Perm harm. avg: " << activecellcount_rocktype[rocktype_idx]/invpermxsum_rocktype[rocktype_idx] << " (not volume-weighted)" << endl;
                }                
            }
        }
    }
    
    
    return 0;
    
}
catch (const std::exception &e) {
    std::cerr << "Program threw an exception: " << e.what() << "\n";
    throw;
}