GeoProps.hpp
Go to the documentation of this file.
1 /*
2  Copyright 2013 SINTEF ICT, Applied Mathematics.
3 
4  This file is part of the Open Porous Media project (OPM).
5 
6  OPM is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  OPM is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with OPM. If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #ifndef OPM_GEOPROPS_HEADER_INCLUDED
21 #define OPM_GEOPROPS_HEADER_INCLUDED
22 
23 #include <opm/core/grid.h>
25 #include <opm/common/ErrorMacros.hpp>
26 //#include <opm/core/pressure/tpfa/trans_tpfa.h>
27 #include <opm/core/pressure/tpfa/TransTpfa.hpp>
28 
29 #include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
30 #include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp>
31 #include <opm/common/utility/platform_dependent/disable_warnings.h>
32 
33 #include <Eigen/Eigen>
34 
35 #ifdef HAVE_DUNE_CORNERPOINT
36 #include <dune/common/version.hh>
37 #include <dune/grid/CpGrid.hpp>
38 #include <dune/grid/common/mcmgmapper.hh>
39 #endif
40 
41 #include <opm/common/utility/platform_dependent/reenable_warnings.h>
42 
43 #include <cstddef>
44 
45 namespace Opm
46 {
47 
54  {
55  public:
56  typedef Eigen::ArrayXd Vector;
57 
60  template <class Props, class Grid>
61  DerivedGeology(const Grid& grid,
62  const Props& props ,
63  Opm::EclipseStateConstPtr eclState,
64  const bool use_local_perm,
65  const double* grav = 0
66 
67  )
68  : pvol_ (Opm::AutoDiffGrid::numCells(grid))
69  , trans_(Opm::AutoDiffGrid::numFaces(grid))
70  , gpot_ (Vector::Zero(Opm::AutoDiffGrid::cell2Faces(grid).noEntries(), 1))
71  , z_(Opm::AutoDiffGrid::numCells(grid))
72  {
73  int numCells = AutoDiffGrid::numCells(grid);
74  int numFaces = AutoDiffGrid::numFaces(grid);
75  const int *cartDims = AutoDiffGrid::cartDims(grid);
76  int numCartesianCells =
77  cartDims[0]
78  * cartDims[1]
79  * cartDims[2];
80 
81  // get the pore volume multipliers from the EclipseState
82  std::vector<double> multpv(numCartesianCells, 1.0);
83  if (eclState->hasDoubleGridProperty("MULTPV")) {
84  multpv = eclState->getDoubleGridProperty("MULTPV")->getData();
85  }
86 
87  // get the net-to-gross cell thickness from the EclipseState
88  std::vector<double> ntg(numCartesianCells, 1.0);
89  if (eclState->hasDoubleGridProperty("NTG")) {
90  ntg = eclState->getDoubleGridProperty("NTG")->getData();
91  }
92 
93  // get grid from parser.
94 
95  // Get original grid cell volume.
96  EclipseGridConstPtr eclgrid = eclState->getEclipseGrid();
97  // Pore volume.
98  // New keywords MINPVF will add some PV due to OPM cpgrid process algorithm.
99  // But the default behavior is to get the comparable pore volume with ECLIPSE.
100  for (int cellIdx = 0; cellIdx < numCells; ++cellIdx) {
101  int cartesianCellIdx = AutoDiffGrid::globalCell(grid)[cellIdx];
102  pvol_[cellIdx] =
103  props.porosity()[cellIdx]
104  * multpv[cartesianCellIdx]
105  * ntg[cartesianCellIdx];
106  if (eclgrid->getMinpvMode() == MinpvMode::ModeEnum::OpmFIL) {
107  pvol_[cellIdx] *= AutoDiffGrid::cellVolume(grid, cellIdx);
108  } else {
109  pvol_[cellIdx] *= eclgrid->getCellVolume(cartesianCellIdx);
110  }
111  }
112  // Use volume weighted arithmetic average of the NTG values for
113  // the cells effected by the current OPM cpgrid process algorithm
114  // for MINPV. Note that the change does not effect the pore volume calculations
115  // as the pore volume is currently defaulted to be comparable to ECLIPSE, but
116  // only the transmissibility calculations.
117  minPvFillProps_(grid, eclState,ntg);
118 
119  // Transmissibility
120 
121  Vector htrans(AutoDiffGrid::numCellFaces(grid));
122  Grid* ug = const_cast<Grid*>(& grid);
123 
124  if (! use_local_perm) {
125  tpfa_htrans_compute(ug, props.permeability(), htrans.data());
126  }
127  else {
128  tpfa_loc_trans_compute_(grid,props.permeability(),htrans);
129  }
130 
131  std::vector<double> mult;
132  multiplyHalfIntersections_(grid, eclState, ntg, htrans, mult);
133 
134  // combine the half-face transmissibilites into the final face
135  // transmissibilites.
136  tpfa_trans_compute(ug, htrans.data(), trans_.data());
137 
138  // multiply the face transmissibilities with their appropriate
139  // transmissibility multipliers
140  for (int faceIdx = 0; faceIdx < numFaces; faceIdx++) {
141  trans_[faceIdx] *= mult[faceIdx];
142  }
143 
144  // Compute z coordinates
145  for (int c = 0; c<numCells; ++c){
146  z_[c] = Opm::UgGridHelpers::cellCentroidCoordinate(grid, c, 2);
147  }
148 
149 
150  // Gravity potential
151  std::fill(gravity_, gravity_ + 3, 0.0);
152  if (grav != 0) {
153  const typename Vector::Index nd = AutoDiffGrid::dimensions(grid);
154  typedef typename AutoDiffGrid::ADCell2FacesTraits<Grid>::Type Cell2Faces;
155  Cell2Faces c2f=AutoDiffGrid::cell2Faces(grid);
156 
157  std::size_t i = 0;
158  for (typename Vector::Index c = 0; c < numCells; ++c) {
159  const double* const cc = AutoDiffGrid::cellCentroid(grid, c);
160 
161  typename Cell2Faces::row_type faces=c2f[c];
162  typedef typename Cell2Faces::row_type::iterator Iter;
163 
164  for (Iter f=faces.begin(), end=faces.end(); f!=end; ++f, ++i) {
165  auto fc = AutoDiffGrid::faceCentroid(grid, *f);
166 
167  for (typename Vector::Index d = 0; d < nd; ++d) {
168  gpot_[i] += grav[d] * (fc[d] - cc[d]);
169  }
170  }
171  }
172  std::copy(grav, grav + nd, gravity_);
173  }
174  }
175 
176  const Vector& poreVolume() const { return pvol_ ;}
177  const Vector& transmissibility() const { return trans_ ;}
178  const Vector& gravityPotential() const { return gpot_ ;}
179  const Vector& z() const { return z_ ;}
180  const double* gravity() const { return gravity_;}
181  Vector& poreVolume() { return pvol_ ;}
182  Vector& transmissibility() { return trans_ ;}
183 
184  private:
185  template <class Grid>
186  void multiplyHalfIntersections_(const Grid &grid,
187  Opm::EclipseStateConstPtr eclState,
188  const std::vector<double> &ntg,
189  Vector &halfIntersectTransmissibility,
190  std::vector<double> &intersectionTransMult);
191 
192  template <class Grid>
193  void tpfa_loc_trans_compute_(const Grid &grid,
194  const double* perm,
195  Vector &hTrans);
196 
197  template <class Grid>
198  void minPvFillProps_(const Grid &grid,
199  Opm::EclipseStateConstPtr eclState,
200  std::vector<double> &ntg);
201 
202  Vector pvol_ ;
203  Vector trans_;
204  Vector gpot_ ;
205  Vector z_;
206  double gravity_[3]; // Size 3 even if grid is 2-dim.
207 
208 
209 
210 
211  };
212 
213  template <class GridType>
214  inline void DerivedGeology::minPvFillProps_(const GridType &grid,
215  Opm::EclipseStateConstPtr eclState,
216  std::vector<double> &ntg)
217  {
218 
219  int numCells = Opm::AutoDiffGrid::numCells(grid);
220  const int* global_cell = Opm::UgGridHelpers::globalCell(grid);
221  const int* cartdims = Opm::UgGridHelpers::cartDims(grid);
222  EclipseGridConstPtr eclgrid = eclState->getEclipseGrid();
223  std::vector<double> porv = eclState->getDoubleGridProperty("PORV")->getData();
224  for (int cellIdx = 0; cellIdx < numCells; ++cellIdx) {
225  const int nx = cartdims[0];
226  const int ny = cartdims[1];
227  const int cartesianCellIdx = global_cell[cellIdx];
228 
229  const double cellVolume = eclgrid->getCellVolume(cartesianCellIdx);
230  ntg[cartesianCellIdx] *= cellVolume;
231  double totalCellVolume = cellVolume;
232 
233  // Average properties as long as there exist cells above
234  // that has pore volume less than the MINPV threshold
235  int cartesianCellIdxAbove = cartesianCellIdx - nx*ny;
236  while ( cartesianCellIdxAbove >= 0 &&
237  porv[cartesianCellIdxAbove] > 0 &&
238  porv[cartesianCellIdxAbove] < eclgrid->getMinpvValue() ) {
239 
240  // Volume weighted arithmetic average of NTG
241  const double cellAboveVolume = eclgrid->getCellVolume(cartesianCellIdxAbove);
242  totalCellVolume += cellAboveVolume;
243  ntg[cartesianCellIdx] += ntg[cartesianCellIdxAbove]*cellAboveVolume;
244  cartesianCellIdxAbove -= nx*ny;
245  }
246  ntg[cartesianCellIdx] /= totalCellVolume;
247  }
248  }
249 
250  template <class GridType>
251  inline void DerivedGeology::multiplyHalfIntersections_(const GridType &grid,
252  Opm::EclipseStateConstPtr eclState,
253  const std::vector<double> &ntg,
254  Vector &halfIntersectTransmissibility,
255  std::vector<double> &intersectionTransMult)
256  {
257  int numCells = Opm::AutoDiffGrid::numCells(grid);
258 
259  int numIntersections = Opm::AutoDiffGrid::numFaces(grid);
260  intersectionTransMult.resize(numIntersections);
261  std::fill(intersectionTransMult.begin(), intersectionTransMult.end(), 1.0);
262 
263  std::shared_ptr<const Opm::TransMult> multipliers = eclState->getTransMult();
264  auto cell2Faces = Opm::UgGridHelpers::cell2Faces(grid);
265  auto faceCells = Opm::AutoDiffGrid::faceCells(grid);
266  const int* global_cell = Opm::UgGridHelpers::globalCell(grid);
267  int cellFaceIdx = 0;
268 
269  for (int cellIdx = 0; cellIdx < numCells; ++cellIdx) {
270  // loop over all logically-Cartesian faces of the current cell
271  auto cellFacesRange = cell2Faces[cellIdx];
272 
273  for(auto cellFaceIter = cellFacesRange.begin(), cellFaceEnd = cellFacesRange.end();
274  cellFaceIter != cellFaceEnd; ++cellFaceIter, ++cellFaceIdx)
275  {
276  // the index of the current cell in arrays for the logically-Cartesian grid
277  int cartesianCellIdx = global_cell[cellIdx];
278 
279  // The index of the face in the compressed grid
280  int faceIdx = *cellFaceIter;
281 
282  // the logically-Cartesian direction of the face
283  int faceTag = Opm::UgGridHelpers::faceTag(grid, cellFaceIter);
284 
285  // Translate the C face tag into the enum used by opm-parser's TransMult class
286  Opm::FaceDir::DirEnum faceDirection;
287  if (faceTag == 0) // left
288  faceDirection = Opm::FaceDir::XMinus;
289  else if (faceTag == 1) // right
290  faceDirection = Opm::FaceDir::XPlus;
291  else if (faceTag == 2) // back
292  faceDirection = Opm::FaceDir::YMinus;
293  else if (faceTag == 3) // front
294  faceDirection = Opm::FaceDir::YPlus;
295  else if (faceTag == 4) // bottom
296  faceDirection = Opm::FaceDir::ZMinus;
297  else if (faceTag == 5) // top
298  faceDirection = Opm::FaceDir::ZPlus;
299  else
300  OPM_THROW(std::logic_error, "Unhandled face direction: " << faceTag);
301 
302  // Account for NTG in horizontal one-sided transmissibilities
303  switch (faceDirection) {
304  case Opm::FaceDir::XMinus:
305  case Opm::FaceDir::XPlus:
306  case Opm::FaceDir::YMinus:
307  case Opm::FaceDir::YPlus:
308  halfIntersectTransmissibility[cellFaceIdx] *= ntg[cartesianCellIdx];
309  break;
310  default:
311  // do nothing for the top and bottom faces
312  break;
313  }
314 
315  // Multiplier contribution on this face for MULT[XYZ] logical cartesian multipliers
316  intersectionTransMult[faceIdx] *=
317  multipliers->getMultiplier(cartesianCellIdx, faceDirection);
318 
319  // Multiplier contribution on this fase for region multipliers
320  const int cellIdxInside = faceCells(faceIdx, 0);
321  const int cellIdxOutside = faceCells(faceIdx, 1);
322 
323  // Do not apply region multipliers in the case of boundary connections
324  if (cellIdxInside < 0 || cellIdxOutside < 0) {
325  continue;
326  }
327  const int cartesianCellIdxInside = global_cell[cellIdxInside];
328  const int cartesianCellIdxOutside = global_cell[cellIdxOutside];
329  // Only apply the region multipliers from the inside
330  if (cartesianCellIdx == cartesianCellIdxInside) {
331  intersectionTransMult[faceIdx] *= multipliers->getRegionMultiplier(cartesianCellIdxInside,cartesianCellIdxOutside,faceDirection);
332  }
333 
334 
335  }
336  }
337  }
338 
339  template <class GridType>
340  inline void DerivedGeology::tpfa_loc_trans_compute_(const GridType& grid,
341  const double* perm,
342  Vector& hTrans){
343 
344  // Using Local coordinate system for the transmissibility calculations
345  // hTrans(cellFaceIdx) = K(cellNo,j) * sum( C(:,i) .* N(:,j), 2) / sum(C.*C, 2)
346  // where K is a diagonal permeability tensor, C is the distance from cell centroid
347  // to face centroid and N is the normal vector pointing outwards with norm equal to the face area.
348  // Off-diagonal permeability values are ignored without warning
349  int numCells = AutoDiffGrid::numCells(grid);
350  int cellFaceIdx = 0;
351  auto cell2Faces = Opm::UgGridHelpers::cell2Faces(grid);
352  auto faceCells = Opm::UgGridHelpers::faceCells(grid);
353 
354  for (int cellIdx = 0; cellIdx < numCells; ++cellIdx) {
355  // loop over all logically-Cartesian faces of the current cell
356  auto cellFacesRange = cell2Faces[cellIdx];
357 
358  for(auto cellFaceIter = cellFacesRange.begin(), cellFaceEnd = cellFacesRange.end();
359  cellFaceIter != cellFaceEnd; ++cellFaceIter, ++cellFaceIdx)
360  {
361  // The index of the face in the compressed grid
362  const int faceIdx = *cellFaceIter;
363 
364  // the logically-Cartesian direction of the face
365  const int faceTag = Opm::UgGridHelpers::faceTag(grid, cellFaceIter);
366 
367  // d = 0: XPERM d = 4: YPERM d = 8: ZPERM ignores off-diagonal permeability values.
368  const int d = std::floor(faceTag/2) * 4;
369 
370  // compute the half transmissibility
371  double dist = 0.0;
372  double cn = 0.0;
373  double sgn = 2.0 * (faceCells(faceIdx, 0) == cellIdx) - 1;
374  const int dim = Opm::UgGridHelpers::dimensions(grid);
375 
376  const double* faceNormal = Opm::UgGridHelpers::faceNormal(grid, faceIdx);
377 #if HAVE_DUNE_CORNERPOINT
378  assert( dim <= 3 );
379  Dune::FieldVector< double, 3 > scaledFaceNormal( 0 );
380  for (int indx = 0; indx < dim; ++indx) {
381  scaledFaceNormal[ indx ] = faceNormal[ indx ];
382  }
383  // compute unit normal incase the normal is already scaled
384  scaledFaceNormal /= scaledFaceNormal.two_norm();
385  // compute proper normal scaled with face area
386  scaledFaceNormal *= Opm::UgGridHelpers::faceArea(grid, faceIdx);
387 #else
388  const double* scaledFaceNormal = faceNormal;
389 #endif
390 
391  for (int indx = 0; indx < dim; ++indx) {
392  const double Ci = Opm::UgGridHelpers::faceCentroid(grid, faceIdx)[indx] -
393  Opm::UgGridHelpers::cellCentroidCoordinate(grid, cellIdx, indx);
394  dist += Ci*Ci;
395  cn += sgn * Ci * scaledFaceNormal[ indx ]; //Opm::UgGridHelpers::faceNormal(grid, faceIdx)[indx];
396  }
397 
398  if (cn < 0){
399  switch (d) {
400  case 0:
401  OPM_MESSAGE("Warning: negative X-transmissibility value in cell: " << cellIdx << " replace by absolute value") ;
402  break;
403  case 4:
404  OPM_MESSAGE("Warning: negative Y-transmissibility value in cell: " << cellIdx << " replace by absolute value") ;
405  break;
406  case 8:
407  OPM_MESSAGE("Warning: negative Z-transmissibility value in cell: " << cellIdx << " replace by absolute value") ;
408  break;
409  default:
410  OPM_THROW(std::logic_error, "Inconsistency in the faceTag in cell: " << cellIdx);
411 
412  }
413  cn = -cn;
414  }
415  hTrans[cellFaceIdx] = perm[cellIdx*dim*dim + d] * cn / dist;
416  }
417  }
418 
419  }
420 
421 }
422 
423 
424 
425 #endif // OPM_GEOPROPS_HEADER_INCLUDED
Definition: GeoProps.hpp:53
const Vector & transmissibility() const
Definition: GeoProps.hpp:177
Definition: AdditionalObjectDeleter.hpp:22
DerivedGeology(const Grid &grid, const Props &props, Opm::EclipseStateConstPtr eclState, const bool use_local_perm, const double *grav=0)
Definition: GeoProps.hpp:61
Mapping of the grid type to the type of the cell to faces mapping.
Definition: GridHelpers.hpp:64
Vector & poreVolume()
Definition: GeoProps.hpp:181
const Vector & poreVolume() const
Definition: GeoProps.hpp:176
const Vector & gravityPotential() const
Definition: GeoProps.hpp:178
Eigen::ArrayXd Vector
Definition: GeoProps.hpp:56
const double * gravity() const
Definition: GeoProps.hpp:180
const Vector & z() const
Definition: GeoProps.hpp:179
Vector & transmissibility()
Definition: GeoProps.hpp:182