/*
Copyright 2014 Statoil ASA.
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace Opm {
EclipseGrid::EclipseGrid(std::array& dims ,
const std::vector& coord ,
const std::vector& zcorn ,
const int * actnum,
const double * mapaxes)
: m_minpvValue(0),
m_minpvMode(MinpvMode::ModeEnum::Inactive),
m_pinch("PINCH"),
m_pinchoutMode(PinchMode::ModeEnum::TOPBOT),
m_multzMode(PinchMode::ModeEnum::TOP)
{
initCornerPointGrid( dims, coord , zcorn , actnum , mapaxes );
}
/**
Will create an EclipseGrid instance based on an existing
GRID/EGRID file.
*/
EclipseGrid::EclipseGrid(const std::string& filename )
: GridDims(),
m_minpvValue(0),
m_minpvMode(MinpvMode::ModeEnum::Inactive),
m_pinch("PINCH"),
m_pinchoutMode(PinchMode::ModeEnum::TOPBOT),
m_multzMode(PinchMode::ModeEnum::TOP)
{
ecl_grid_type * new_ptr = ecl_grid_load_case__( filename.c_str() , false );
if (new_ptr)
m_grid.reset( new_ptr );
else
throw std::invalid_argument("Could not load grid from binary file: " + filename);
m_nx = ecl_grid_get_nx( c_ptr() );
m_ny = ecl_grid_get_ny( c_ptr() );
m_nz = ecl_grid_get_nz( c_ptr() );
}
/* Copy constructor */
EclipseGrid::EclipseGrid(const EclipseGrid& src)
: GridDims(src.getNX(), src.getNY(), src.getNZ()),
m_messages( src.m_messages ),
m_minpvValue( src.m_minpvValue ),
m_minpvMode( src.m_minpvMode ),
m_pinch( src.m_pinch ),
m_pinchoutMode( src.m_pinchoutMode ),
m_multzMode( src.m_multzMode )
{
m_grid.reset( ecl_grid_alloc_copy( src.c_ptr() ) );
}
EclipseGrid::EclipseGrid(size_t nx, size_t ny , size_t nz,
double dx, double dy, double dz)
: GridDims(nx, ny, nz),
m_minpvValue(0),
m_minpvMode(MinpvMode::ModeEnum::Inactive),
m_pinch("PINCH"),
m_pinchoutMode(PinchMode::ModeEnum::TOPBOT),
m_multzMode(PinchMode::ModeEnum::TOP)
{
m_grid.reset(ecl_grid_alloc_rectangular(nx, ny, nz, dx, dy, dz, NULL));
}
EclipseGrid::EclipseGrid(const std::shared_ptr& deckptr, const int * actnum)
:
EclipseGrid(*deckptr, actnum)
{}
/*
This is the main EclipseGrid constructor, it will inspect the
input Deck for grid keywords, either the corner point keywords
COORD and ZCORN, or the various rectangular keywords like DX,DY
and DZ.
Actnum is treated specially:
1. If an actnum pointer is passed in that should be a pointer
to 0 and 1 values which will be used as actnum mask.
2. If the actnum pointer is not present the constructor will
look in the deck for an actnum keyword, and use that if it
is found. This is a best effort which will work in many
cases, but if the ACTNUM keyword is manipulated in the deck
those manipulations will be silently lost; if the ACTNUM
keyword has size different from nx*ny*nz it will also be
silently ignored.
With a mutable EclipseGrid instance you can later call the
EclipseGrid::resetACTNUM() method when you have complete actnum
information. The EclipseState based construction of EclipseGrid
is a two-pass operation, which guarantees that actnum is handled
correctly.
*/
EclipseGrid::EclipseGrid(const Deck& deck, const int * actnum)
: GridDims(deck),
m_minpvValue(0),
m_minpvMode(MinpvMode::ModeEnum::Inactive),
m_pinch("PINCH"),
m_pinchoutMode(PinchMode::ModeEnum::TOPBOT),
m_multzMode(PinchMode::ModeEnum::TOP)
{
const std::array dims = getNXYZ();
initGrid(dims, deck);
if (actnum != nullptr)
resetACTNUM(actnum);
else {
if (deck.hasKeyword()) {
const auto& actnumData = deck.getKeyword().getIntData();
if (actnumData.size() == getCartesianSize())
resetACTNUM( actnumData.data());
else {
const std::string msg = "The ACTNUM keyword has " + std::to_string( actnumData.size() ) + " elements - expected : " + std::to_string( getCartesianSize()) + " - ignored.";
m_messages.warning(msg);
}
}
}
}
void EclipseGrid::initGrid( const std::array& dims, const Deck& deck) {
if (hasCornerPointKeywords(deck)) {
initCornerPointGrid(dims , deck);
} else if (hasCartesianKeywords(deck)) {
initCartesianGrid(dims , deck);
} else {
throw std::invalid_argument("EclipseGrid needs cornerpoint or cartesian keywords.");
}
if (deck.hasKeyword()) {
const auto& record = deck.getKeyword( ).getRecord(0);
const auto& item = record.getItem( );
m_pinch.setValue( item.getSIDouble(0) );
auto pinchoutString = record.getItem().get< std::string >(0);
m_pinchoutMode = PinchMode::PinchModeFromString(pinchoutString);
auto multzString = record.getItem().get< std::string >(0);
m_multzMode = PinchMode::PinchModeFromString(multzString);
}
if (deck.hasKeyword() && deck.hasKeyword()) {
throw std::invalid_argument("Can not have both MINPV and MINPVFIL in deck.");
}
if (deck.hasKeyword()) {
const auto& record = deck.getKeyword( ).getRecord(0);
const auto& item = record.getItem( );
m_minpvValue = item.getSIDouble(0);
m_minpvMode = MinpvMode::ModeEnum::EclSTD;
}
if (deck.hasKeyword()) {
const auto& record = deck.getKeyword( ).getRecord(0);
const auto& item = record.getItem( );
m_minpvValue = item.getSIDouble(0);
m_minpvMode = MinpvMode::ModeEnum::OpmFIL;
}
}
size_t EclipseGrid::activeIndex(size_t i, size_t j, size_t k) const {
return activeIndex( getGlobalIndex( i,j,k ));
}
size_t EclipseGrid::activeIndex(size_t globalIndex) const {
int active_index = ecl_grid_get_active_index1( m_grid.get() , globalIndex );
if (active_index < 0)
throw std::invalid_argument("Input argument does not correspond to an active cell");
return static_cast( active_index );
}
bool EclipseGrid::isPinchActive( ) const {
return m_pinch.hasValue();
}
double EclipseGrid::getPinchThresholdThickness( ) const {
return m_pinch.getValue();
}
PinchMode::ModeEnum EclipseGrid::getPinchOption( ) const {
return m_pinchoutMode;
}
PinchMode::ModeEnum EclipseGrid::getMultzOption( ) const {
return m_multzMode;
}
MinpvMode::ModeEnum EclipseGrid::getMinpvMode() const {
return m_minpvMode;
}
double EclipseGrid::getMinpvValue( ) const {
return m_minpvValue;
}
void EclipseGrid::initCartesianGrid(const std::array& dims , const Deck& deck) {
if (hasDVDEPTHZKeywords( deck ))
initDVDEPTHZGrid( dims , deck );
else if (hasDTOPSKeywords(deck))
initDTOPSGrid( dims ,deck );
else
throw std::invalid_argument("Tried to initialize cartesian grid without all required keywords");
}
void EclipseGrid::initDVDEPTHZGrid(const std::array& dims, const Deck& deck) {
const std::vector& DXV = deck.getKeyword().getSIDoubleData();
const std::vector& DYV = deck.getKeyword().getSIDoubleData();
const std::vector& DZV = deck.getKeyword().getSIDoubleData();
const std::vector& DEPTHZ = deck.getKeyword().getSIDoubleData();
assertVectorSize( DEPTHZ , static_cast( (dims[0] + 1)*(dims[1] +1 )) , "DEPTHZ");
assertVectorSize( DXV , static_cast( dims[0] ) , "DXV");
assertVectorSize( DYV , static_cast( dims[1] ) , "DYV");
assertVectorSize( DZV , static_cast( dims[2] ) , "DZV");
m_grid.reset( ecl_grid_alloc_dxv_dyv_dzv_depthz( dims[0] , dims[1] , dims[2] , DXV.data() , DYV.data() , DZV.data() , DEPTHZ.data() , nullptr ) );
}
void EclipseGrid::initDTOPSGrid(const std::array& dims , const Deck& deck) {
std::vector DX = createDVector( dims , 0 , "DX" , "DXV" , deck);
std::vector DY = createDVector( dims , 1 , "DY" , "DYV" , deck);
std::vector DZ = createDVector( dims , 2 , "DZ" , "DZV" , deck);
std::vector TOPS = createTOPSVector( dims , DZ , deck );
m_grid.reset( ecl_grid_alloc_dx_dy_dz_tops( dims[0] , dims[1] , dims[2] , DX.data() , DY.data() , DZ.data() , TOPS.data() , nullptr ) );
}
void EclipseGrid::initCornerPointGrid(const std::array& dims ,
const std::vector& coord ,
const std::vector& zcorn ,
const int * actnum,
const double * mapaxes)
{
const std::vector zcorn_float( zcorn.begin() , zcorn.end() );
const std::vector coord_float( coord.begin() , coord.end() );
float * mapaxes_float = nullptr;
if (mapaxes) {
mapaxes_float = new float[6];
for (size_t i=0; i < 6; i++)
mapaxes_float[i] = mapaxes[i];
}
m_grid.reset( ecl_grid_alloc_GRDECL_data(dims[0] ,
dims[1] ,
dims[2] ,
zcorn_float.data() ,
coord_float.data() ,
actnum ,
false, // We do not apply the MAPAXES transformations
mapaxes_float) );
if (mapaxes)
delete[] mapaxes_float;
}
void EclipseGrid::initCornerPointGrid(const std::array& dims, const Deck& deck) {
assertCornerPointKeywords( dims , deck);
{
const auto& ZCORNKeyWord = deck.getKeyword();
const auto& COORDKeyWord = deck.getKeyword();
const std::vector& zcorn = ZCORNKeyWord.getSIDoubleData();
const std::vector& coord = COORDKeyWord.getSIDoubleData();
double * mapaxes = nullptr;
if (deck.hasKeyword()) {
const auto& mapaxesKeyword = deck.getKeyword();
const auto& record = mapaxesKeyword.getRecord(0);
mapaxes = new double[6];
for (size_t i = 0; i < 6; i++) {
mapaxes[i] = record.getItem( i ).getSIDouble( 0 );
}
}
initCornerPointGrid( dims, coord , zcorn , nullptr , mapaxes );
if (mapaxes)
delete[] mapaxes;
}
}
bool EclipseGrid::hasCornerPointKeywords(const Deck& deck) {
if (deck.hasKeyword() && deck.hasKeyword())
return true;
else
return false;
}
void EclipseGrid::assertCornerPointKeywords(const std::array& dims , const Deck& deck)
{
const int nx = dims[0];
const int ny = dims[1];
const int nz = dims[2];
{
const auto& ZCORNKeyWord = deck.getKeyword();
if (ZCORNKeyWord.getDataSize() != static_cast(8*nx*ny*nz)) {
const std::string msg =
"Wrong size of the ZCORN keyword: Expected 8*x*ny*nz = "
+ std::to_string(static_cast(8*nx*ny*nz)) + " is "
+ std::to_string(static_cast(ZCORNKeyWord.getDataSize()));
m_messages.error(msg);
throw std::invalid_argument(msg);
}
}
{
const auto& COORDKeyWord = deck.getKeyword();
if (COORDKeyWord.getDataSize() != static_cast(6*(nx + 1)*(ny + 1))) {
const std::string msg =
"Wrong size of the COORD keyword: Expected 6*(nx + 1)*(ny + 1) = "
+ std::to_string(static_cast(6*(nx + 1)*(ny + 1))) + " is "
+ std::to_string(static_cast(COORDKeyWord.getDataSize()));
m_messages.error(msg);
throw std::invalid_argument(msg);
}
}
}
bool EclipseGrid::hasCartesianKeywords(const Deck& deck) {
if (hasDVDEPTHZKeywords( deck ))
return true;
else
return hasDTOPSKeywords(deck);
}
bool EclipseGrid::hasDVDEPTHZKeywords(const Deck& deck) {
if (deck.hasKeyword() &&
deck.hasKeyword() &&
deck.hasKeyword() &&
deck.hasKeyword())
return true;
else
return false;
}
bool EclipseGrid::hasDTOPSKeywords(const Deck& deck) {
if ((deck.hasKeyword() || deck.hasKeyword()) &&
(deck.hasKeyword() || deck.hasKeyword()) &&
(deck.hasKeyword() || deck.hasKeyword()) &&
deck.hasKeyword())
return true;
else
return false;
}
void EclipseGrid::assertVectorSize(const std::vector& vector , size_t expectedSize , const std::string& vectorName) {
if (vector.size() != expectedSize)
throw std::invalid_argument("Wrong size for keyword: " + vectorName + ". Expected: " + boost::lexical_cast(expectedSize) + " got: " + boost::lexical_cast(vector.size()));
}
/*
The body of the for loop in this method looks slightly
peculiar. The situation is as follows:
1. The EclipseGrid class will assemble the necessary keywords
and create an ert ecl_grid instance.
2. The ecl_grid instance will export ZCORN, COORD and ACTNUM
data which will be used by the UnstructureGrid constructor
in opm-core. If the ecl_grid is created with ZCORN as an
input keyword that data is retained in the ecl_grid
structure, otherwise the ZCORN data is created based on the
internal cell geometries.
3. When constructing the UnstructuredGrid structure strict
numerical comparisons of ZCORN values are used to detect
cells in contact, if all the elements the elements in the
TOPS vector are specified[1] we will typically not get
bitwise equality between the bottom of one cell and the top
of the next.
To remedy this we enforce bitwise equality with the
construction:
if (std::abs(nextValue - TOPS[targetIndex]) < z_tolerance)
TOPS[targetIndex] = nextValue;
[1]: This is of course assuming the intention is to construct a
fully connected space covering grid - if that is indeed
not the case the barriers must be thicker than 1e-6m to be
retained.
*/
std::vector EclipseGrid::createTOPSVector(const std::array& dims,
const std::vector& DZ, const Deck& deck)
{
double z_tolerance = 1e-6;
size_t volume = dims[0] * dims[1] * dims[2];
size_t area = dims[0] * dims[1];
const auto& TOPSKeyWord = deck.getKeyword();
std::vector TOPS = TOPSKeyWord.getSIDoubleData();
if (TOPS.size() >= area) {
size_t initialTOPSize = TOPS.size();
TOPS.resize( volume );
for (size_t targetIndex = area; targetIndex < volume; targetIndex++) {
size_t sourceIndex = targetIndex - area;
double nextValue = TOPS[sourceIndex] + DZ[sourceIndex];
if (targetIndex >= initialTOPSize)
TOPS[targetIndex] = nextValue;
else {
if (std::abs(nextValue - TOPS[targetIndex]) < z_tolerance)
TOPS[targetIndex] = nextValue;
}
}
}
if (TOPS.size() != volume)
throw std::invalid_argument("TOPS size mismatch");
return TOPS;
}
std::vector EclipseGrid::createDVector(const std::array& dims, size_t dim, const std::string& DKey,
const std::string& DVKey, const Deck& deck)
{
size_t volume = dims[0] * dims[1] * dims[2];
size_t area = dims[0] * dims[1];
std::vector D;
if (deck.hasKeyword(DKey)) {
D = deck.getKeyword( DKey ).getSIDoubleData();
if (D.size() >= area && D.size() < volume) {
/*
Only the top layer is required; for layers below the
top layer the value from the layer above is used.
*/
size_t initialDSize = D.size();
D.resize( volume );
for (size_t targetIndex = initialDSize; targetIndex < volume; targetIndex++) {
size_t sourceIndex = targetIndex - area;
D[targetIndex] = D[sourceIndex];
}
}
if (D.size() != volume)
throw std::invalid_argument(DKey + " size mismatch");
} else {
const auto& DVKeyWord = deck.getKeyword(DVKey);
const std::vector& DV = DVKeyWord.getSIDoubleData();
if (DV.size() != (size_t) dims[dim])
throw std::invalid_argument(DVKey + " size mismatch");
D.resize( volume );
scatterDim( dims , dim , DV , D );
}
return D;
}
void EclipseGrid::scatterDim(const std::array& dims , size_t dim , const std::vector& DV , std::vector& D) {
int index[3];
for (index[2] = 0; index[2] < dims[2]; index[2]++) {
for (index[1] = 0; index[1] < dims[1]; index[1]++) {
for (index[0] = 0; index[0] < dims[0]; index[0]++) {
size_t globalIndex = index[2] * dims[1] * dims[0] + index[1] * dims[0] + index[0];
D[globalIndex] = DV[ index[dim] ];
}
}
}
}
const ecl_grid_type * EclipseGrid::c_ptr() const {
return m_grid.get();
}
const MessageContainer& EclipseGrid::getMessageContainer() const {
return m_messages;
}
MessageContainer& EclipseGrid::getMessageContainer() {
return m_messages;
}
bool EclipseGrid::equal(const EclipseGrid& other) const {
bool status = (m_pinch.equal( other.m_pinch ) && (ecl_grid_compare( c_ptr() , other.c_ptr() , true , false , false )) && (m_minpvMode == other.getMinpvMode()));
if(m_minpvMode!=MinpvMode::ModeEnum::Inactive){
status = status && (m_minpvValue == other.getMinpvValue());
}
return status;
}
size_t EclipseGrid::getNumActive( ) const {
return static_cast(ecl_grid_get_nactive( c_ptr() ));
}
bool EclipseGrid::cellActive( size_t globalIndex ) const {
assertGlobalIndex( globalIndex );
return ecl_grid_cell_active1( c_ptr() , static_cast(globalIndex));
}
bool EclipseGrid::cellActive( size_t i , size_t j , size_t k ) const {
assertIJK(i,j,k);
return ecl_grid_cell_active3( c_ptr() , static_cast(i),static_cast(j),static_cast(k));
}
double EclipseGrid::getCellVolume(size_t globalIndex) const {
assertGlobalIndex( globalIndex );
return ecl_grid_get_cell_volume1( c_ptr() , static_cast(globalIndex));
}
double EclipseGrid::getCellVolume(size_t i , size_t j , size_t k) const {
assertIJK(i,j,k);
return ecl_grid_get_cell_volume3( c_ptr() , static_cast(i),static_cast(j),static_cast(k));
}
double EclipseGrid::getCellThicknes(size_t i , size_t j , size_t k) const {
assertIJK(i,j,k);
return ecl_grid_get_cell_thickness3( c_ptr() , static_cast(i),static_cast(j),static_cast(k));
}
double EclipseGrid::getCellThicknes(size_t globalIndex) const {
assertGlobalIndex( globalIndex );
return ecl_grid_get_cell_thickness1( c_ptr() , static_cast(globalIndex));
}
std::array EclipseGrid::getCellDims(size_t globalIndex) const {
assertGlobalIndex( globalIndex );
{
double dx = ecl_grid_get_cell_dx1( c_ptr() , globalIndex);
double dy = ecl_grid_get_cell_dy1( c_ptr() , globalIndex);
double dz = ecl_grid_get_cell_thickness1( c_ptr() , globalIndex);
return std::array{ {dx , dy , dz }};
}
}
std::array EclipseGrid::getCellDims(size_t i , size_t j , size_t k) const {
assertIJK(i,j,k);
{
size_t globalIndex = getGlobalIndex( i,j,k );
double dx = ecl_grid_get_cell_dx1( c_ptr() , globalIndex);
double dy = ecl_grid_get_cell_dy1( c_ptr() , globalIndex);
double dz = ecl_grid_get_cell_thickness1( c_ptr() , globalIndex);
return std::array{ {dx , dy , dz }};
}
}
std::array EclipseGrid::getCellCenter(size_t globalIndex) const {
assertGlobalIndex( globalIndex );
{
double x,y,z;
ecl_grid_get_xyz1( c_ptr() , static_cast(globalIndex) , &x , &y , &z);
return std::array{{x,y,z}};
}
}
std::array EclipseGrid::getCellCenter(size_t i,size_t j, size_t k) const {
assertIJK(i,j,k);
{
double x,y,z;
ecl_grid_get_xyz3( c_ptr() , static_cast(i),static_cast(j),static_cast(k), &x , &y , &z);
return std::array{{x,y,z}};
}
}
double EclipseGrid::getCellDepth(size_t globalIndex) const {
assertGlobalIndex( globalIndex );
return ecl_grid_get_cdepth1( c_ptr() , static_cast(globalIndex));
}
double EclipseGrid::getCellDepth(size_t i,size_t j, size_t k) const {
assertIJK(i,j,k);
return ecl_grid_get_cdepth3( c_ptr() , static_cast(i),static_cast(j),static_cast(k));
}
void EclipseGrid::exportACTNUM( std::vector& actnum) const {
size_t volume = getNX() * getNY() * getNZ();
if (getNumActive() == volume)
actnum.resize(0);
else {
actnum.resize( volume );
ecl_grid_init_actnum_data( c_ptr() , actnum.data() );
}
}
void EclipseGrid::exportMAPAXES( std::vector& mapaxes) const {
if (ecl_grid_use_mapaxes( c_ptr())) {
mapaxes.resize(6);
ecl_grid_init_mapaxes_data_double( c_ptr() , mapaxes.data() );
} else {
mapaxes.resize(0);
}
}
void EclipseGrid::exportCOORD( std::vector& coord) const {
coord.resize( ecl_grid_get_coord_size( c_ptr() ));
ecl_grid_init_coord_data_double( c_ptr() , coord.data() );
}
void EclipseGrid::exportZCORN( std::vector& zcorn) const {
zcorn.resize( ecl_grid_get_zcorn_size( c_ptr() ));
ecl_grid_init_zcorn_data_double( c_ptr() , zcorn.data() );
}
const std::vector& EclipseGrid::getActiveMap() const {
if( !this->activeMap.empty() ) return this->activeMap;
this->activeMap.resize( this->getNumActive() );
const auto size = int(this->getCartesianSize());
for( int global_index = 0; global_index < size; global_index++) {
// Using the low level C function to get the active index, because the C++
// version will throw for inactive cells.
int active_index = ecl_grid_get_active_index1( m_grid.get() , global_index );
if (active_index >= 0)
this->activeMap[ active_index ] = global_index;
}
return this->activeMap;
}
void EclipseGrid::resetACTNUM( const int * actnum) {
ecl_grid_reset_actnum( m_grid.get() , actnum );
/* re-build the active map cache */
this->activeMap.clear();
this->getActiveMap();
}
}