Files
ResInsight/ThirdParty/Ert/lib/ecl/ecl_grid.cpp

7090 lines
238 KiB
C++

/*
Copyright (c) 2011 statoil asa, norway.
The file 'ecl_grid.c' is part of ert - ensemble based reservoir tool.
ERT 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.
ERT 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 at <http://www.gnu.org/licenses/gpl.html>
for more details.
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <math.h>
#include <vector>
#include <ert/util/util.h>
#include <ert/util/double_vector.hpp>
#include <ert/util/int_vector.hpp>
#include <ert/util/hash.hpp>
#include <ert/util/vector.hpp>
#include <ert/util/stringlist.hpp>
#include <ert/geometry/geo_util.hpp>
#include <ert/geometry/geo_polygon.hpp>
#include <ert/ecl/ecl_util.hpp>
#include <ert/ecl/ecl_type.hpp>
#include <ert/ecl/ecl_kw.hpp>
#include <ert/ecl/ecl_file.hpp>
#include <ert/ecl/ecl_kw_magic.hpp>
#include <ert/ecl/ecl_endian_flip.hpp>
#include <ert/ecl/ecl_coarse_cell.hpp>
#include <ert/ecl/ecl_grid.hpp>
#include <ert/ecl/grid_dims.hpp>
#include <ert/ecl/nnc_info.hpp>
/**
this function implements functionality to load eclispe grid files,
both .egrid and .grid files - in a transparent fashion.
observe the following convention:
global_index: [0 , nx*ny*nz)
active_index: [0 , nactive)
about indexing
--------------
there are three different ways to index/access a cell:
1. by ijk
2. by global index, [0 , nx*ny*nz)
3. by active index, [0 , nactive)
most of the query functions can take input in several of the
ways. the expected arguments are indicated as the last part of the
function name:
ecl_grid_get_pos3() - 3: this function expects i,j,k
ecl_grid_get_pos1() - 1: this function expects a global index
ecl_grid_get_pos1a() - 1a: this function expects an active index.
*/
/**
note about lgr
--------------
the eclipse local grid refinement (lgr) is organised as follows:
1. you start with a normal grid.
2. some of the cells can be subdivided into further internal
grids, this is the lgr.
this is illustrated below:
+--------------------------------------+
| | | |
| | | |
| x | x | x |
| | | |
| | | |
|------------|------------|------------|
| | | | | |
| | x | x | | |
|-----x------|------x-----| x |
| x | x | x | | |
| | | | | |
-------------|------------|------------|
| | | |
| | | |
| x | | |
| | | |
| | | |
+--------------------------------------+
the grid above shows the following:
1. the coarse (i.e. normal) grid has 9 cells, of which 7 marked
with 'x' are active.
2. two of the cells have been refined into new 2x2 grids. in the
refined cells only three and two of the refined cells are
active.
in a grid file the keywords for this grid will look like like this:
..... __
coords \
corners |
coords |
corners |
coords |
corners | normal coord / corners kewyords
coords | for the nine coarse cells. observe
corners | that when reading in these cells it is
coords | impossible to know that some of the
corners | cells will be subdivieded in a following
coords | lgr definition.
corners |
coords |
corners |
coords |
corners |
coords |
corners __/________________________________________________________
lgr \
lgrilg |
dimens |
coords |
corners | first lgr, with some header information,
coords | and then normal coords/corners keywords for
corners | the four refined cells.
coords |
corners |
coords |
corners __/
lgr \
lgrilg |
dimens |
coords | second lgr.
corners |
coords |
corners |
coords |
corners |
coords |
corners __/
for egrid files it is essentially the same, except for replacing the
keywords coords/corners with coord/zcorn/actnum. also the lgr
headers are somewhat different.
solution data in restart files comes in a similar way, a restart
file with lgr can typically look like this:
..... __
..... \
startsol | all restart data for the ordinary
pressure | grid.
swat |
sgas |
.... |
endsol __/____________________________
lgr \
.... |
startsol | restart data for
pressure | the first lgr.
sgas |
swat |
... |
endsol |
endlgr __/
lgr \
.... |
startsol | restart data for
pressure | the second lgr.
sgas |
swat |
... |
endsol |
endlgr __/
the lgr implementation in is based on the following main principles:
1. when loading a egrid/grid file one ecl_grid_type instance will
be allocated; this grid will contain the main grid, and all the
lgr grids.
2. only one datatype (ecl_grid_type) is used both for the main grid
and the lgr grids.
3. the main grid will own (memory wise) all the lgr grids, this
even applies to nested subgrids whose parent is also a lgr.
4. when it comes to indexing and so on there is no difference
between lgr grid and the main grid.
example:
--------
{
ecl_file_type * restart_data = ecl_file_fread_alloc(restart_filename , true); // load some restart info to inspect
ecl_grid_type * grid = ecl_grid_alloc(grid_filename , true); // bootstrap ecl_grid instance
stringlist_type * lgr_names = ecl_grid_alloc_name_list( grid ); // get a list of all the lgr names.
printf("grid:%s has %d a total of %d lgr's \n", grid_filename , stringlist_get_size( lgr_names ));
for (int lgr_nr = 0; lgr_nr < stringlist_get_size( lgr_names); lgr_nr++) {
ecl_grid_type * lgr_grid = ecl_grid_get_lgr( grid , stringlist_iget( lgr_names , lgr_nr )); // get the ecl_grid instance of the lgr - by name.
ecl_kw_type * pressure_kw;
int nx,ny,nz,active_size;
ecl_grid_get_dims( lgr_grid , &nx , &ny , &nz , &active_size); // get some size info from this lgr.
printf("lgr:%s has %d x %d x %d elements \n",stringlist_iget(lgr_names , lgr_nr ) , nx , ny , nz);
// ok - now we want to extract the solution vector (pressure) corresponding to this lgr:
pressure_kw = ecl_file_iget_named_kw( ecl_file , "pressure" , ecl_grid_get_grid_nr( lgr_grid ));
/|\
|
|
we query the lgr_grid instance to find which
occurence of the solution data we should look
up in the ecl_file instance with restart data. puuhh!!
{
int center_index = ecl_grid_get_global_index3( lgr_grid , nx/2 , ny/2 , nz/2 ); // ask the lgr_grid to get the index at the center of the lgr grid.
printf("the pressure in the middle of %s is %g \n", stinglist_iget( lgr_names , lgr_nr ) , ecl_kw_iget_as_double( pressure_kw , center_index ));
}
}
ecl_file_free( restart_data );
ecl_grid_free( grid );
stringlist_free( lgr_names );
}
*/
/*
About coarse groups
-------------------
It is possible to get ECLIPSE to join several cells together as one
coarse cell, to reduce the size of the numerical problem. In this
implementation this is supported as follows:
1. Each cell has an integer flag - coarse_group, which points to
the coarse group that the current cell is part of, or the value
COARSE_NONE for cells which are not part of a coarsening group.
2. The details of a coarse cell is implemented in ecl_coarse_cell.c.
3. The ecl_grid structure contains a list of ecl_coarse_cell
instances in the coarse_cells vector.
4. The introduction of coarse groups makes the concept of active
cells slightly more complex:
a) All the cells in the coarse group count as one (or zero)
active cells when asking the grid how many active cells
there are.
b) In the case that several of the un-coarsened cells in the
coarse group are active the mapping between global index
and active index will no longer be unique - there will be
several different global indices mapping to the same
active index.
The api for coarse related tasks is briefly:
- int ecl_grid_get_num_coarse_groups( const ecl_grid_type * main_grid )
- bool ecl_grid_have_coarse_cells( const ecl_grid_type * main_grid )
- ecl_coarse_cell_type * ecl_grid_iget_coarse_group( const ecl_grid_type * ecl_grid , int coarse_nr );
- ecl_coarse_cell_type * ecl_grid_get_cell_coarse_group1( const ecl_grid_type * ecl_grid , int global_index);
In addition to the API presented by the ecl_coarse_cell.c implementation.
*/
/*
About dual porosity
-------------------
Eclipse has support for dual porosity systems where the reservoir is
made from an interleaved system of matrix blocks and fractures. The
current implementation has some support for reading such properties
from the grid files:
- The active property of the cells is an integer which is a sum of
the flag values ACTIVE_MATRIX and ACTIVE_FRACTURE.
- All functions operating on fracture properties have 'fracture'
as part of the name. The functions operating on the matrix do
(typically) not have matrix as part of the name. The matrix
properties are the default properties which apply in the single
porosity case.
- In the EGRID files the dual porosity properties are set in the
ACTNUM field in the file. The numerical values are [0,1,2,3] for
inactive cells, matrix active, fracture active and
matrix+fracture active respectively.
- For the GRID files there is abolutely no metadata to tell that
this is a dual porosity run (I think ...) - instead the whole
grid is repeated one more time with cells for the fractures
following after the matrix cells.
Naively the GRID file of a dual porosity run will report that it
contains 2*NZ layers. In the current implementation heuristics
is used to detect the situation, and the grid will only be
loaded as consisting of 'NZ' geometric layers.
- The documentation seems to indicate that the number of active
fracture cells can in general be different from the number of
active matrix cells, and the current implementation takes pains
to support that possibility - but all examples I have come over
have nactive_fracture == nactive_matrix?
- Properties and solution data in restart/init/grdecl files are
not treated here. These properties will just increase (typically
double) in size - and how to treat them will be a question of
convention. The following shows a possible solution:
{
char fracture_kw[9];
char matrix_kw[9];
int matrix_size = ecl_grid_get_nactive( ecl_grid );
int fracture_size = ecl_grid_get_nactive_fracture( ecl_grid );
swat = ecl_file_iget_name_kw( rst_file , "SWAT" , 0);
snsprintf(fracture_kw , 9 , "F-%6s" , ecl_kw_get_header( swat ));
snsprintf(matrix_kw , 9 , "M-%6s" , ecl_kw_get_header( swat ));
ecl_kw_type * M = ecl_kw_alloc_sub_copy( swat , matrix_kw , 0 , matrix_size );
ecl_kw_type * F = ecl_kw_alloc_sub_copy( swat , fracture_kw , matrix_size , fracture_size );
}
*/
/*
About nnc
---------
When loading a grid file the various NNC related keywords are read
in to assemble information of the NNC. The NNC information is
organized as follows:
For cells with NNC's attached the information is kept in a
nnc_info_type structure. For a particular cell the nnc_info
structure keeps track of which other cells this particular cell
is connected to, on a per grid (i.e. LGR) basis.
In the nnc_info structure the different grids are identified
through the lgr_nr.
Example usage:
--------------
ecl_grid_type * grid = ecl_grid_alloc("FILE.EGRID");
// Get a int_vector instance with all the cells which have nnc info
// attached.
const int_vector_type * cells_with_nnc = ecl_grid_get_nnc_index_list( grid );
// Iterate over all the cells with nnc info:
for (int i=0; i < int_vector_size( cells_with_nnc ); i++) {
int cell_index = int_vector_iget( cells_with_nnc , i);
const nnc_info_type * nnc_info = ecl_grid_get_nnc_info1( grid , cell_index);
// Get all the nnc connections from @cell_index to other cells in the same grid
{
const int_vector_type * nnc_list = nnc_info_get_self_index_list( nnc_info );
for (int j=0; j < int_vector_size( nnc_list ); j++)
printf("Cell[%d] -> %d in the same grid \n",cell_index , int_vector_iget(nnc_list , j));
}
{
for (int lgr_index=0; lgr_index < nnc_info_get_size( nnc_info ); lgr_index++) {
nnc_vector_type * nnc_vector = nnc_info_iget_vector( nnc_info , lgr_index );
int lgr_nr = nnc_vector_get_lgr_nr( nnc_vector );
if (lgr_nr != nnc_info_get_lgr_nr( nnc_info )) {
const int_vector_type * nnc_list = nnc_vector_get_index_list( nnc_vector );
for (int j=0; j < int_vector_size( nnc_list ); j++)
printf("Cell[%d] -> %d in lgr:%d/%s \n",cell_index , int_vector_iget(nnc_list , j) , lgr_nr , ecl_grid_get_lgr_name(ecl_grid , lgr_nr));
}
}
}
}
Dual porosity and nnc: In ECLIPSE the connection between the matrix
properties and the fracture properties in a cell is implemented as a
nnc where the fracture cell has global index in the range [nx*ny*nz,
2*nz*ny*nz). In ert we we have not implemented this double covering
in the case of dual porosity models so NNC involving
fracture cells are not considered.
*/
/*
about tetraheder decomposition
------------------------------
the table tetraheder_permutations describe how the cells can be
divided into twelve tetrahedrons. the dimensions in the the table
are as follows:
1. the first dimension is the "way" the cell is divided into
tetrahedrons, there are two different ways. for cells where the
four point corners on a face are not in the same plane, the two
methods will not give the same result. which one is "right"??
2. the second dimension is the tetrahedron number, for each way of
the two ways there are a total of twelve tetrahedrons.
3. the third and list dimension is the point number in this
tetrahedron. when forming a tetrahedron the first input point
should always be the point corresponding to center of the
cell. that is not explicit in this table.
i.e. for instance the third tetrahedron for the first method
consists of the cells:
tetraheheder_permutations[0][2] = {0 , 4 , 5}
in addition to the central point. the value [0..7] correspond the
the number scheme of the corners in a cell used by eclipse:
lower layer: upper layer (higher value of z - i.e. lower down in resrvoir).
2---3 6---7
| | | |
0---1 4---5
table entries are ripped from eclpost code - file: kvpvos.f in
klib/
*/
/*
About ordering of the corners in the cell
-----------------------------------------
This code reads and builds the grid structure from the ECLIPSE
GRID/EGRID files without really considering the question about where
the cells are located in "the real world", the format is quite general
and it should(?) be possible to formulate different conventions
(i.e. handedness and direction of z-axis) with the same format.
The corners in a cell are numbered 0 - 7, where corners 0-3 constitute
one layer and the corners 4-7 consitute the other layer. Observe the
numbering does not follow a consistent rotation around the face:
j
6---7 /|\
| | |
4---5 |
|
o----------> i
2---3
| |
0---1
Many grids are left-handed, i.e. the direction of increasing z will
point down towards the center of the earth. Hence in the figure above
the layer 4-7 will be deeper down in the reservoir than layer 0-3, and
also have higher z-value.
Warning: The main author of this code suspects that the coordinate
system can be right-handed as well, giving a z axis which will
increase 'towards the sky'; the safest is probaly to check this
explicitly if it matters for the case at hand.
Method 0 corresponds to a tetrahedron decomposition which will split
the lower layer along the 1-2 diagonal and the upper layer along the
4-7 diagonal, method 1 corresponds to the alternative decomposition
which splits the lower face along the 0-3 diagnoal and the upper face
along the 5-6 diagonal.
*/
static const int tetrahedron_permutations[2][12][3] = {{
// K-
{0,1,2},
{3,2,1},
// J+
{6,2,7},
{3,7,2},
// I-
{0,2,4},
{6,4,2},
// I+
{3,1,7},
{5,7,1},
// J-
{0,4,1},
{5,1,4},
// K+
{5,4,7},
{6,7,4}
},
{
// K-
{1,3,0},
{2,0,3},
// J+
{2,3,6},
{7,6,3},
// I-
{2,6,0},
{4,0,6},
// I+
{7,3,5},
{1,5,3},
// J-
{1,0,5},
{4,5,0},
// K+
{7,5,6},
{4,6,5}
}};
/*
the implementation is based on a hierarchy of three datatypes:
1. ecl_grid - this is the only exported datatype
2. ecl_cell - internal
3. point - internal
*/
typedef struct point_struct point_type;
struct point_struct {
double x;
double y;
double z;
};
static void point_mapaxes_transform( point_type * p , const double origo[2], const double unit_x[2] , const double unit_y[2]) {
double new_x = origo[0] + p->x*unit_x[0] + p->y*unit_y[0];
double new_y = origo[1] + p->x*unit_x[1] + p->y*unit_y[1];
p->x = new_x;
p->y = new_y;
}
static void point_mapaxes_invtransform( point_type * p , const double origo[2], const double unit_x[2] , const double unit_y[2]) {
double norm = 1.0 / (unit_x[0]*unit_y[1] - unit_x[1] * unit_y[0]);
double dx = p->x - origo[0];
double dy = p->y - origo[1];
double org_x = ( dx*unit_y[1] - dy*unit_y[0]) * norm;
double org_y = (-dx*unit_x[1] + dy*unit_x[0]) * norm;
p->x = org_x;
p->y = org_y;
}
static void point_inplace_add(point_type * point , const point_type * add) {
point->x += add->x;
point->y += add->y;
point->z += add->z;
}
static void point_inplace_sub(point_type * point , const point_type * sub) {
point->x -= sub->x;
point->y -= sub->y;
point->z -= sub->z;
}
static void point_inplace_scale(point_type * point , double scale_factor) {
point->x *= scale_factor;
point->y *= scale_factor;
point->z *= scale_factor;
}
/**
Will compute the vector cross product B x C and store the result in A.
*/
static void point_vector_cross(point_type * A , const point_type * B , const point_type * C) {
A->x = B->y*C->z - B->z*C->y;
A->y = -(B->x*C->z - B->z*C->x);
A->z = B->x*C->y - B->y*C->x;
}
static double point_dot_product( const point_type * v1 , const point_type * v2) {
return v1->x*v2->x + v1->y*v2->y + v1->z*v2->z;
}
static void point_compare( const point_type *p1 , const point_type * p2, bool * equal) {
const double tolerance = 0.001;
double diff_x = (fabs(p1->x - p2->x) / fabs(p1->x + p2->x + 1));
double diff_y = (fabs(p1->y - p2->y) / fabs(p1->y + p2->y + 1));
double diff_z = (fabs(p1->z - p2->z) / fabs(p1->z + p2->z + 1));
if (diff_x + diff_y + diff_z > tolerance)
*equal = false;
}
static void point_dump( const point_type * p , FILE * stream) {
util_fwrite_double( p->x , stream );
util_fwrite_double( p->y , stream );
util_fwrite_double( p->z , stream );
}
static void point_dump_ascii( const point_type * p , FILE * stream , const double * offset) {
if (offset)
fprintf(stream , "(%7.2f, %7.2f, %7.2f) " , p->x - offset[0], p->y - offset[1] , p->z - offset[2]);
else
fprintf(stream , "(%7.2f, %7.2f, %7.2f) " , p->x , p->y , p->z);
}
static void point_set(point_type * p , double x , double y , double z) {
p->x = x;
p->y = y;
p->z = z;
}
static void point_shift(point_type * p , double dx , double dy , double dz) {
p->x += dx;
p->y += dy;
p->z += dz;
}
static void point_copy_values(point_type * p , const point_type * src) {
point_set(p , src->x , src->y , src->z);
}
/*
Indices used in the cell->active_index[] array.
*/
#define MATRIX_INDEX 0
#define FRACTURE_INDEX 1
#define COARSE_GROUP_NONE -1
#define HOST_CELL_NONE -1
#define CELL_FLAG_VALID 1 /* In the case of GRID files not necessarily all cells geometry values set - in that case this will be left as false. */
#define CELL_FLAG_CENTER 2 /* Has the center value been calculated - this is by default not done to speed up loading a tiny bit. */
#define CELL_FLAG_TAINTED 4 /* lazy fucking stupid reservoir engineers make invalid grid
cells - for kicks?? must try to keep those cells out of
real-world calculations with some hysteric heuristics.*/
#define CELL_FLAG_VOLUME 8
typedef struct ecl_cell_struct ecl_cell_type;
#define GET_CELL_FLAG(cell,flag) (((cell->cell_flags & (flag)) == 0) ? false : true)
#define SET_CELL_FLAG(cell,flag) ((cell->cell_flags |= (flag)))
#define METER_TO_FEET_SCALE_FACTOR 3.28084
#define METER_TO_CM_SCALE_FACTOR 100.0
struct ecl_cell_struct {
point_type center;
point_type corner_list[8];
double volume; /* Cache volume - whether it is initialized or not is handled by a cell_flags. */
int active;
int active_index[2]; /* [0]: The active matrix index; [1]: the active fracture index */
const ecl_grid_type *lgr; /* if this cell is part of an lgr; this will point to a grid instance for that lgr; NULL if not part of lgr. */
int host_cell; /* the global index of the host cell for an lgr cell, set to -1 for normal cells. */
int coarse_group; /* The index of the coarse group holding this cell -1 for non-coarsened cells. */
int cell_flags;
nnc_info_type * nnc_info; /* Non-neighbour connection info*/
};
static ert_ecl_unit_enum ecl_grid_check_unit_system(const ecl_kw_type * gridunit_kw);
static void ecl_grid_init_mapaxes_data_float( const ecl_grid_type * grid , float * mapaxes);
float * ecl_grid_alloc_coord_data( const ecl_grid_type * grid );
static const float * ecl_grid_get_mapaxes( const ecl_grid_type * grid );
#define ECL_GRID_ID 991010
struct ecl_grid_struct {
UTIL_TYPE_ID_DECLARATION;
int lgr_nr; /* EGRID files: corresponds to item 4 in gridhead - 0 for the main grid.
GRID files: 0 for the main grid, then 1 -> number of LGRs in order read from file*/
char * name; /* the name of the file for the main grid - name of the lgr for lgrs. */
int ny,nz,nx;
int size; /* == nx*ny*nz */
int total_active;
int total_active_fracture;
bool * visited; /* internal helper struct used when searching for index - can be NULL. */
int * index_map; /* this a list of nx*ny*nz elements, where value -1 means inactive cell .*/
int * inv_index_map; /* this is list of total_active elements - which point back to the index_map. */
int * fracture_index_map; /* For fractures: this a list of nx*ny*nz elements, where value -1 means inactive cell .*/
int * inv_fracture_index_map; /* For fractures: this is list of total_active elements - which point back to the index_map. */
ecl_cell_type * cells;
char * parent_name; /* the name of the parent for a nested lgr - for the main grid, and also a
lgr descending directly from the main grid this will be NULL. */
hash_type * children; /* a table of lgr children for this grid. */
const ecl_grid_type * parent_grid; /* the parent grid for this (lgr) - NULL for the main grid. */
const ecl_grid_type * global_grid; /* the global grid - NULL for the main grid. */
bool coarsening_active;
vector_type * coarse_cells;
/*
the two fields below are for *storing* lgr grid instances. observe
that these fields will be NULL for lgr grids, i.e. grids with
lgr_nr > 0.
*/
vector_type * LGR_list; /* a vector of ecl_grid instances for LGRs - the index corresponds to the order LGRs are read from file*/
int_vector_type * lgr_index_map; /* a vector that maps LGR-nr for EGRID files to index into the LGR_list.*/
hash_type * LGR_hash; /* a hash of pointers to ecl_grid instances - for name based lookup of lgr. */
int parent_box[6]; /* integers i1,i2, j1,j2, k1,k2 of the parent grid region containing this lgr. the indices are inclusive - zero offset */
/* not used yet .. */
int dualp_flag;
bool use_mapaxes;
double unit_x[2];
double unit_y[2];
double origo[2];
float * mapaxes;
/*------------------------------: the fields below this line are used for blocking algorithms - and not allocated by default.*/
int block_dim; /* == 2 for maps and 3 for fields. 0 when not in use. */
int block_size;
int last_block_index;
double_vector_type ** values;
ecl_kw_type * coord_kw; /* Retained for writing the grid to file.
In principal it should be possible to
recalculate this from the cell coordinates,
but in cases with skewed cells this has proved
numerically challenging. */
ert_ecl_unit_enum unit_system;
int eclipse_version;
};
ert_ecl_unit_enum ecl_grid_get_unit_system(const ecl_grid_type * grid) {
return grid->unit_system;
}
static void ecl_cell_compare(const ecl_cell_type * c1 , const ecl_cell_type * c2, bool include_nnc , bool * equal) {
int i;
if (c1->active != c2->active)
*equal = false;
if (c1->active_index[0] != c2->active_index[0])
*equal = false;
if (c1->active_index[1] != c2->active_index[1])
*equal = false;
if (c1->coarse_group != c2->coarse_group)
*equal = false;
if (c1->host_cell != c2->host_cell)
*equal = false;
if (*equal) {
for (i=0; i < 8; i++)
point_compare( &c1->corner_list[i] , &c2->corner_list[i] , equal );
}
if (include_nnc) {
if (*equal)
*equal = nnc_info_equal( c1->nnc_info , c2->nnc_info );
}
}
static void ecl_cell_dump( const ecl_cell_type * cell , FILE * stream) {
int i;
for (i=0; i < 8; i++)
point_dump( &cell->corner_list[i] , stream );
}
static void ecl_cell_assert_center( ecl_cell_type * cell);
static void ecl_cell_dump_ascii( ecl_cell_type * cell , int i , int j , int k , FILE * stream , const double * offset) {
fprintf(stream, "Cell: i:%3d j:%3d k:%3d host_cell:%d CoarseGroup:%4d active_nr:%6d active:%d \nCorners:\n",
i, j, k,
cell->host_cell, cell->coarse_group,
cell->active_index[MATRIX_INDEX],
cell->active);
ecl_cell_assert_center( cell );
fprintf(stream , "Center : ");
point_dump_ascii( &cell->center , stream , offset);
fprintf(stream , "\n");
{
int l;
for (l=0; l < 8; l++) {
fprintf(stream , "Corner %d : ",l);
point_dump_ascii( &cell->corner_list[l] , stream , offset);
fprintf(stream , "\n");
}
}
fprintf(stream , "\n");
}
static void ecl_cell_fwrite_GRID(const ecl_grid_type * grid,
const ecl_cell_type * cell,
bool fracture_cell,
int coords_size,
int i,
int j,
int k,
int global_index,
ecl_kw_type * coords_kw,
ecl_kw_type * corners_kw,
fortio_type * fortio) {
ecl_kw_iset_int( coords_kw , 0 , i + 1);
ecl_kw_iset_int( coords_kw , 1 , j + 1);
ecl_kw_iset_int( coords_kw , 2 , k + 1);
ecl_kw_iset_int( coords_kw , 3 , global_index + 1);
ecl_kw_iset_int( coords_kw , 4 , 0);
if (fracture_cell) {
if (cell->active & CELL_ACTIVE_FRACTURE)
ecl_kw_iset_int( coords_kw , 4 , 1);
} else {
if (cell->active & CELL_ACTIVE_MATRIX)
ecl_kw_iset_int( coords_kw , 4 , 1);
}
if (coords_size == 7) {
ecl_kw_iset_int( coords_kw , 5 , cell->host_cell + 1);
ecl_kw_iset_int( coords_kw , 6 , cell->coarse_group + 1);
}
ecl_kw_fwrite( coords_kw , fortio );
{
float * corners = (float*)ecl_kw_get_void_ptr( corners_kw );
point_type point;
int c;
for (c = 0; c < 8; c++) {
point_copy_values( &point , &cell->corner_list[c] );
if (grid->use_mapaxes)
point_mapaxes_invtransform( &point , grid->origo , grid->unit_x , grid->unit_y );
corners[3*c ] = point.x;
corners[3*c + 1] = point.y;
corners[3*c + 2] = point.z;
}
}
ecl_kw_fwrite( corners_kw , fortio );
}
//static const size_t cellMappingECLRi[8] = { 0, 1, 3, 2, 4, 5, 7, 6 };
static void ecl_cell_ri_export( const ecl_cell_type * cell , double * ri_points) {
int ecl_offset = 4;
int ri_offset = ecl_offset * 3;
{
int point_nr;
// Handling the points 0,1 & 4,5 which map directly between ECLIPSE and RI
for (point_nr =0; point_nr < 2; point_nr++) {
// Points 0 & 1
ri_points[ point_nr * 3 ] = cell->corner_list[point_nr].x;
ri_points[ point_nr * 3 + 1 ] = cell->corner_list[point_nr].y;
ri_points[ point_nr * 3 + 2 ] = -cell->corner_list[point_nr].z;
// Points 4 & 5
ri_points[ ri_offset + point_nr * 3 ] = cell->corner_list[ecl_offset + point_nr].x;
ri_points[ ri_offset + point_nr * 3 + 1 ] = cell->corner_list[ecl_offset + point_nr].y;
ri_points[ ri_offset + point_nr * 3 + 2 ] = -cell->corner_list[ecl_offset + point_nr].z;
}
}
{
int ecl_point;
/*
Handling the points 2,3 & 6,7 which are flipped when (2,3) ->
(3,2) and (6,7) -> (7,6) when going between ECLIPSE and ResInsight.
*/
for (ecl_point =2; ecl_point < 4; ecl_point++) {
int ri_point = 5 - ecl_point;
// Points 2 & 3
ri_points[ ri_point * 3 ] = cell->corner_list[ecl_point].x;
ri_points[ ri_point * 3 + 1 ] = cell->corner_list[ecl_point].y;
ri_points[ ri_point * 3 + 2 ] = -cell->corner_list[ecl_point].z;
// Points 6 & 7
ri_points[ ri_offset + ri_point * 3 ] = cell->corner_list[ecl_offset + ecl_point].x;
ri_points[ ri_offset + ri_point * 3 + 1 ] = cell->corner_list[ecl_offset + ecl_point].y;
ri_points[ ri_offset + ri_point * 3 + 2 ] = -cell->corner_list[ecl_offset + ecl_point].z;
}
}
}
/*****************************************************************/
static double max2( double x1 , double x2) {
return (x1 > x2) ? x1 : x2;
}
static double min2( double x1 , double x2) {
return (x1 < x2) ? x1 : x2;
}
static double min4(double x1 , double x2 , double x3 , double x4) {
return min2( min2(x1 , x2) , min2(x3 , x4 ));
}
static double max4(double x1 , double x2 , double x3 , double x4) {
return max2( max2(x1 , x2) , max2(x3 , x4 ));
}
static double max8( double x1 , double x2 , double x3, double x4 , double x5 , double x6 , double x7 , double x8) {
return max2( max4(x1,x2,x3,x4) , max4(x5,x6,x7,x8));
}
static double min8( double x1 , double x2 , double x3, double x4 , double x5 , double x6 , double x7 , double x8) {
return min2( min4(x1,x2,x3,x4) , min4(x5,x6,x7,x8));
}
/*****************************************************************/
static double ecl_cell_min_z( const ecl_cell_type * cell) {
return min4( cell->corner_list[0].z , cell->corner_list[1].z , cell->corner_list[2].z , cell->corner_list[3].z);
}
static double ecl_cell_max_z( const ecl_cell_type * cell ) {
return max4( cell->corner_list[4].z , cell->corner_list[5].z , cell->corner_list[6].z , cell->corner_list[7].z );
}
/**
the grid can be rotated so that it is not safe to consider only one
plane for the x/y min/max.
*/
static double ecl_cell_min_x( const ecl_cell_type * cell) {
return min8( cell->corner_list[0].x , cell->corner_list[1].x , cell->corner_list[2].x , cell->corner_list[3].x,
cell->corner_list[4].x , cell->corner_list[5].x , cell->corner_list[6].x , cell->corner_list[7].x );
}
static double ecl_cell_max_x( const ecl_cell_type * cell ) {
return max8( cell->corner_list[0].x , cell->corner_list[1].x , cell->corner_list[2].x , cell->corner_list[3].x,
cell->corner_list[4].x , cell->corner_list[5].x , cell->corner_list[6].x , cell->corner_list[7].x );
}
static double ecl_cell_min_y( const ecl_cell_type * cell) {
return min8( cell->corner_list[0].y , cell->corner_list[1].y , cell->corner_list[2].y , cell->corner_list[3].y,
cell->corner_list[4].y , cell->corner_list[5].y , cell->corner_list[6].y , cell->corner_list[7].y );
}
static double ecl_cell_max_y( const ecl_cell_type * cell ) {
return max8( cell->corner_list[0].y , cell->corner_list[1].y , cell->corner_list[2].y , cell->corner_list[3].y,
cell->corner_list[4].y , cell->corner_list[5].y , cell->corner_list[6].y , cell->corner_list[7].y );
}
/**
The problem is that some extremely fucking stupid reservoir
engineers purpousely have made grids with invalid cells. Typically
the cells accomodating numerical aquifers are located at an utm
position (0,0).
Cells which have some pillars located in (0,0) and some cells
located among the rest of the grid become completely warped - with
insane volumes, parts of the reservoir volume doubly covered, and
so on.
To keep these cells out of the real-world (i.e. involving utm
coordinates) computations they are marked as 'tainted' in this
function. The tainting procedure is completely heuristic, and
probably wrong.
--------------
There is second heuristic which marks cells as invalid. In some
cases (which ??) cells outside the area of interest are just set to
have all four corner at the same arbitrary depth; these cells are
inactive and do not affect flow simulations - however the arbitrary
location of the cells warps visualisation of normal inactive cells
completely. We therefore try to invalidate such cells here. The
algorithm used is the same as used for RMS; however RMS will mark
the cells as inactive - whereas we mark already inactive cells as
invalid.
It is very important that this mechanism is NOT used to set cells
to inactive; as that will break completely when it comes to
e.g. data like PRESSURE and SWAT which are defined as a number of
active elements.
*/
static void ecl_cell_taint_cell( ecl_cell_type * cell ) {
int c;
for (c = 0; c < 8; c++) {
const point_type p = cell->corner_list[c];
if ((p.x == 0) && (p.y == 0)) {
SET_CELL_FLAG(cell , CELL_FLAG_TAINTED);
break;
}
}
/*
Second heuristic to invalidate cells.
*/
if (cell->active == CELL_NOT_ACTIVE) {
if (!GET_CELL_FLAG(cell , CELL_FLAG_TAINTED)) {
const point_type p0 = cell->corner_list[0];
int cell_index = 1;
while (true) {
const point_type pi = cell->corner_list[cell_index];
if (pi.z != p0.z)
// There is a difference - the cell is certainly valid.
break;
else {
cell_index++;
if (cell_index == 8) {
// They have all been at the same height up until now;
// the cell is marked as invalid.
SET_CELL_FLAG(cell , CELL_FLAG_TAINTED);
break;
}
}
}
}
}
}
static int ecl_cell_get_twist( const ecl_cell_type * cell ) {
int twist_count = 0;
for (int c = 0; c < 4; c++) {
const point_type * p1 = &cell->corner_list[c];
const point_type * p2 = &cell->corner_list[c + 4];
if ((p2->z - p1->z) < 0)
twist_count += 1;
}
return twist_count;
}
/*****************************************************************/
/**
Observe that when allocating based on a grid file not all cells are
necessarily accessed beyond this function. In general not all cells
will have a coords/corners section in the grid file.
*/
static void ecl_cell_init( ecl_cell_type * cell , bool init_valid) {
cell->active = CELL_NOT_ACTIVE;
cell->lgr = NULL;
cell->host_cell = HOST_CELL_NONE;
cell->coarse_group = COARSE_GROUP_NONE;
cell->cell_flags = 0;
cell->active_index[MATRIX_INDEX] = -1;
cell->active_index[FRACTURE_INDEX] = -1;
if (init_valid)
cell->cell_flags = CELL_FLAG_VALID;
cell->nnc_info = NULL;
}
/*
#define mod(x,n) ((x) % (n))
static int ecl_cell_get_tetrahedron_method( int i , int j , int k) {
return (1 + (1 - 2*mod(i,2)) * (1 - 2 * mod(j,2)) * (1 - 2*mod(k,2))) / 2;
}
#undef mod
*/
static void ecl_cell_set_center( ecl_cell_type * cell) {
point_set(&cell->center , 0 , 0 , 0);
{
int c;
for (c = 0; c < 8; c++)
point_inplace_add(&cell->center , &cell->corner_list[c]);
}
point_inplace_scale(&cell->center , 1.0 / 8.0);
SET_CELL_FLAG( cell , CELL_FLAG_CENTER );
}
static void ecl_cell_assert_center( ecl_cell_type * cell) {
if (!GET_CELL_FLAG(cell , CELL_FLAG_CENTER))
ecl_cell_set_center( cell );
}
static void ecl_cell_memcpy( ecl_cell_type * target_cell , const ecl_cell_type * src_cell ) {
memcpy( target_cell , src_cell , sizeof * target_cell );
}
static void ecl_cell_install_lgr( ecl_cell_type * cell , const ecl_grid_type * lgr_grid) {
cell->lgr = lgr_grid;
}
static double C(double *r,int f1,int f2,int f3){
if (f1 == 0) {
if (f2 == 0) {
if (f3 == 0)
return r[0]; // 000
else
return r[4] - r[0]; // 001
} else {
if (f3 == 0)
return r[2] - r[0]; // 010
else
return r[6] + r[0] - r[4] - r[2]; // 011
}
} else {
if (f2 == 0) {
if (f3 == 0)
return r[1] - r[0]; // 100
else
return r[5]+r[0]-r[4]-r[1]; // 101
} else {
if (f3 == 0)
return r[3]+r[0]-r[2]-r[1]; // 110
else
return r[7]+r[4]+r[2]+r[1]-r[6]-r[5]-r[3]-r[0]; // 111
}
}
}
static double ecl_cell_get_volume( ecl_cell_type * cell ) {
double volume = 0;
int pb,pg,qa,qg,ra,rb;
double X[8];
double Y[8];
double Z[8];
{
int c;
for (c = 0; c < 8; c++) {
X[c] = cell->corner_list[c].x;
Y[c] = cell->corner_list[c].y;
Z[c] = cell->corner_list[c].z;
}
}
for (pb=0;pb<=1;pb++)
for (pg=0;pg<=1;pg++)
for (qa=0;qa<=1;qa++)
for (qg=0;qg<=1;qg++)
for (ra=0;ra<=1;ra++)
for (rb=0;rb<=1;rb++) {
int divisor = (qa+ra+1)*(pb+rb+1)*(pg+qg+1);
double dV = C(X,1,pb,pg) * C(Y,qa,1,qg) * C(Z,ra,rb,1) -
C(X,1,pb,pg) * C(Z,qa,1,qg) * C(Y,ra,rb,1) -
C(Y,1,pb,pg) * C(X,qa,1,qg) * C(Z,ra,rb,1) +
C(Y,1,pb,pg) * C(Z,qa,1,qg) * C(X,ra,rb,1) +
C(Z,1,pb,pg) * C(X,qa,1,qg) * C(Y,ra,rb,1) -
C(Z,1,pb,pg) * C(Y,qa,1,qg) * C(X,ra,rb,1);
volume += dV / divisor;
}
return fabs(volume);
}
typedef struct tetrahedron_struct tetrahedron_type;
struct tetrahedron_struct {
point_type p0;
point_type p1;
point_type p2;
point_type p3;
};
/*
Calculates the volume of a tetrahedron, a normalization of 1/6 is
omitted.
*/
static inline double tetrahedron_volume6( tetrahedron_type tet ) {
point_type bxc;
/* vector subtraction */
tet.p0.x -= tet.p3.x;
tet.p0.y -= tet.p3.y;
tet.p0.z -= tet.p3.z;
tet.p1.x -= tet.p3.x;
tet.p1.y -= tet.p3.y;
tet.p1.z -= tet.p3.z;
tet.p2.x -= tet.p3.x;
tet.p2.y -= tet.p3.y;
tet.p2.z -= tet.p3.z;
/* cross product */
bxc.x = tet.p1.y*tet.p2.z - tet.p1.z*tet.p2.y;
bxc.y = -(tet.p1.x*tet.p2.z - tet.p1.z*tet.p2.x);
bxc.z = tet.p1.x*tet.p2.y - tet.p1.y*tet.p2.x;
/* dot product */
return tet.p0.x*bxc.x + tet.p0.y*bxc.y + tet.p0.z*bxc.z;
}
/*
Returns true if and only if the point p is inside the tetrahedron tet.
*/
static bool tetrahedron_contains(tetrahedron_type tet, const point_type p) {
const double epsilon = 1e-9;
double tetra_volume = fabs(tetrahedron_volume6(tet));
if(tetra_volume < epsilon)
return false;
// Decomposes tetrahedron into 4 new tetrahedrons
point_type tetra_points[4] = {tet.p0, tet.p1, tet.p2, tet.p3};
double decomposition_volume = 0;
for(int i = 0; i < 4; ++i) {
const point_type tmp = tetra_points[i];
tetra_points[i] = p;
// Compute volum of decomposition tetrahedron
tetrahedron_type dec_tet;
dec_tet.p0 = tetra_points[0]; dec_tet.p1 = tetra_points[1];
dec_tet.p2 = tetra_points[2]; dec_tet.p3 = tetra_points[3];
decomposition_volume += fabs(tetrahedron_volume6(dec_tet));
tetra_points[i] = tmp;
}
return (fabs(tetra_volume - decomposition_volume) < epsilon);
}
static double triangle_area(double x1, double y1,
double x2, double y2,
double x3, double y3) {
return fabs(x1*y2 + x2*y3 + x3*y1 - x1*y3 - x3*y2 - x2*y1)*0.5;
}
static bool triangle_contains(const point_type *p0,
const point_type *p1,
const point_type *p2,
double x, double y) {
double epsilon = 1e-10;
double vt = triangle_area(p0->x , p0->y,
p1->x , p1->y,
p2->x , p2->y);
if (vt < epsilon) /* zero size cells do not contain anything. */
return false;
{
double v1 = triangle_area(p0->x , p0->y,
p1->x , p1->y,
x , y);
double v2 = triangle_area(p0->x , p0->y,
x , y,
p2->x , p2->y);
double v3 = triangle_area(x , y,
p1->x , p1->y,
p2->x , p2->y);
if (fabs( vt - (v1 + v2 + v3 )) < epsilon)
return true;
else
return false;
}
}
static double parallelogram_area3d(const point_type * p0, const point_type * p1, const point_type * p2) {
point_type a = *p1;
point_type b = *p2;
point_inplace_sub(&a, p0);
point_inplace_sub(&b, p0);
point_type c;
point_vector_cross(&c, &a, &b);
return sqrt(point_dot_product(&c, &c));
}
/*
* Returns true if and only if point p is contained in the triangle denoted by
* p0, p1 and p2. Note that if the triangle is a line, the function will still
* return true if the point lies on the line segment.
*/
static bool triangle_contains3d(const point_type *p0,
const point_type *p1,
const point_type *p2,
const point_type *p) {
double epsilon = 1e-10;
double vt = parallelogram_area3d(p0, p1, p2);
double v1 = parallelogram_area3d(p0, p1, p);
double v2 = parallelogram_area3d(p0, p2, p);
double v3 = parallelogram_area3d(p1, p2, p);
// p0, p1, p2 represents a line segment and
// p lies on the line this segment represents
if(vt < epsilon && fabs(v1+v2+v3) < epsilon) {
double x_min = util_double_min(p0->x, util_double_min(p1->x, p2->x));
double x_max = util_double_max(p0->x, util_double_max(p1->x, p2->x));
double y_min = util_double_min(p0->y, util_double_min(p1->y, p2->y));
double y_max = util_double_max(p0->y, util_double_max(p1->y, p2->y));
double z_min = util_double_min(p0->z, util_double_min(p1->z, p2->z));
double z_max = util_double_max(p0->z, util_double_max(p1->z, p2->z));
return (
x_min-epsilon <= p->x && p->x <= x_max+epsilon &&
y_min-epsilon <= p->y && p->y <= y_max+epsilon &&
z_min-epsilon <= p->z && p->z <= z_max+epsilon
);
}
return (fabs( vt - (v1 + v2 + v3 )) < epsilon);
}
/*
if the layer defined by the cell corners 0-1-2-3 (lower == true) or
4-5-6-7 (lower == false) contain the point (x,y) the function will
return true - otehrwise false.
the function works by dividing the cell face into two triangles,
which are checked one at a time with the function
triangle_contains().
*/
static bool ecl_cell_layer_contains_xy(const ecl_cell_type * cell,
bool lower_layer,
double x,
double y) {
if (GET_CELL_FLAG(cell,CELL_FLAG_TAINTED))
return false;
{
const point_type *p0,*p1,*p2,*p3;
{
int corner_offset;
if (lower_layer)
corner_offset = 0;
else
corner_offset = 4;
p0 = &cell->corner_list[corner_offset + 0];
p1 = &cell->corner_list[corner_offset + 1];
p2 = &cell->corner_list[corner_offset + 2];
p3 = &cell->corner_list[corner_offset + 3];
}
if (triangle_contains(p0,p1,p2,x,y))
return true;
else
return triangle_contains(p1,p2,p3,x,y);
}
}
/**
lower layer: upper layer
2---3 6---7
| | | |
0---1 4---5
*/
static void ecl_cell_init_regular(ecl_cell_type * cell,
const double * offset,
int i,
int j,
int k,
int global_index,
const double * ivec,
const double * jvec,
const double * kvec,
const int * actnum) {
point_set(&cell->corner_list[0] , offset[0] , offset[1] , offset[2] ); // Point 0
cell->corner_list[1] = cell->corner_list[0]; // Point 1
point_shift(&cell->corner_list[1] , ivec[0] , ivec[1] , ivec[2]);
cell->corner_list[2] = cell->corner_list[0]; // Point 2
point_shift(&cell->corner_list[2] , jvec[0] , jvec[1] , jvec[2]);
cell->corner_list[3] = cell->corner_list[1]; // Point 3
point_shift(&cell->corner_list[3] , jvec[0] , jvec[1] , jvec[2]);
{
int i;
for (i=0; i < 4; i++) {
cell->corner_list[i+4] = cell->corner_list[i]; // Point 4-7
point_shift(&cell->corner_list[i+4] , kvec[0] , kvec[1] , kvec[2]);
}
}
if (actnum != NULL)
cell->active = actnum[global_index];
else
cell->active = CELL_ACTIVE;
}
/* end of cell implementation */
/*****************************************************************/
/* starting on the ecl_grid proper implementation */
UTIL_SAFE_CAST_FUNCTION(ecl_grid , ECL_GRID_ID);
UTIL_IS_INSTANCE_FUNCTION( ecl_grid , ECL_GRID_ID);
/**
this function allocates the internal index_map and inv_index_map fields.
*/
static ecl_cell_type * ecl_grid_get_cell(const ecl_grid_type * grid,
int global_index) {
return &grid->cells[global_index];
}
/**
this function uses heuristics (ahhh - i hate it) in an attempt to
mark cells with fucked geometry - see further comments in the
function ecl_cell_taint_cell() which actually does it.
*/
static void ecl_grid_taint_cells( ecl_grid_type * ecl_grid ) {
int index;
for (index = 0; index < ecl_grid->size; index++) {
ecl_cell_type * cell = ecl_grid_get_cell( ecl_grid , index );
ecl_cell_taint_cell( cell );
}
}
static void ecl_grid_free_cells( ecl_grid_type * grid ) {
if (!grid->cells)
return;
for (int i=0; i < grid->size; i++) {
ecl_cell_type * cell = ecl_grid_get_cell( grid , i );
if (cell->nnc_info)
nnc_info_free(cell->nnc_info);
}
free( grid->cells );
}
static bool ecl_grid_alloc_cells( ecl_grid_type * grid , bool init_valid) {
grid->cells = (ecl_cell_type*)malloc(grid->size * sizeof * grid->cells );
if (!grid->cells)
return false;
{
ecl_cell_type * cell0 = ecl_grid_get_cell( grid , 0 );
ecl_cell_init( cell0 , init_valid );
{
int i;
for (i=1; i < grid->size; i++) {
ecl_cell_type * target_cell = ecl_grid_get_cell( grid , i );
ecl_cell_memcpy( target_cell , cell0 );
}
}
return true;
}
}
/**
will create a new blank grid instance. if the global_grid argument
is != NULL the newly created grid instance will copy the mapaxes
transformations; and set the global_grid pointer of the new grid
instance. apart from that no further lgr-relationsip initialisation
is performed.
*/
static ecl_grid_type * ecl_grid_alloc_empty(ecl_grid_type * global_grid,
ert_ecl_unit_enum unit_system,
int dualp_flag,
int nx,
int ny,
int nz,
int lgr_nr,
bool init_valid) {
ecl_grid_type * grid = (ecl_grid_type*)util_malloc(sizeof * grid );
UTIL_TYPE_ID_INIT(grid , ECL_GRID_ID);
grid->total_active = 0;
grid->total_active_fracture = 0;
grid->nx = nx;
grid->ny = ny;
grid->nz = nz;
grid->size = nx*ny*nz;
grid->lgr_nr = lgr_nr;
grid->global_grid = global_grid;
grid->coarsening_active = false;
grid->mapaxes = NULL;
grid->dualp_flag = dualp_flag;
grid->coord_kw = NULL;
grid->visited = NULL;
grid->inv_index_map = NULL;
grid->index_map = NULL;
grid->fracture_index_map = NULL;
grid->inv_fracture_index_map = NULL;
grid->unit_system = unit_system;
if (global_grid != NULL) {
/*
this is an lgr instance, and we inherit the global grid
transformations from the main grid. The underlying mapaxes
pointer is jest left at NULL for lgr instances.
*/
grid->unit_x[0] = global_grid->unit_x[0];
grid->unit_x[1] = global_grid->unit_x[1];
grid->unit_y[0] = global_grid->unit_y[0];
grid->unit_y[1] = global_grid->unit_y[1];
grid->origo[0] = global_grid->origo[0];
grid->origo[1] = global_grid->origo[1];
grid->use_mapaxes = global_grid->use_mapaxes;
} else {
grid->unit_x[0] = 1;
grid->unit_x[1] = 0;
grid->unit_y[0] = 0;
grid->unit_y[1] = 1;
grid->origo[0] = 0;
grid->origo[1] = 0;
grid->use_mapaxes = false;
}
grid->block_dim = 0;
grid->values = NULL;
if (ECL_GRID_MAINGRID_LGR_NR == lgr_nr) { /* this is the main grid */
grid->LGR_list = vector_alloc_new();
grid->lgr_index_map = int_vector_alloc(0,0);
grid->LGR_hash = hash_alloc();
} else {
grid->LGR_list = NULL;
grid->lgr_index_map = NULL;
grid->LGR_hash = NULL;
}
grid->name = NULL;
grid->parent_name = NULL;
grid->parent_grid = NULL;
grid->children = hash_alloc();
grid->coarse_cells = vector_alloc_new();
grid->eclipse_version = 0;
/* This is the large allocation - which can potentially fail. */
if (!ecl_grid_alloc_cells( grid , init_valid )) {
ecl_grid_free( grid );
grid = NULL;
}
return grid;
}
static int ecl_grid_get_global_index__(const ecl_grid_type * ecl_grid,
int i,
int j,
int k) {
return i + j * ecl_grid->nx + k * ecl_grid->nx * ecl_grid->ny;
}
static void ecl_grid_set_cell_EGRID(ecl_grid_type * ecl_grid , int i, int j , int k ,
double x[4][2] , double y[4][2] , double z[4][2] ,
const int * actnum, const int * corsnum) {
const int global_index = ecl_grid_get_global_index__(ecl_grid , i , j , k );
ecl_cell_type * cell = ecl_grid_get_cell( ecl_grid , global_index );
int ip , iz;
for (iz = 0; iz < 2; iz++) {
for (ip = 0; ip < 4; ip++) {
int c = ip + iz * 4;
point_set(&cell->corner_list[c] , x[ip][iz] , y[ip][iz] , z[ip][iz]);
if (ecl_grid->use_mapaxes)
point_mapaxes_transform( &cell->corner_list[c] , ecl_grid->origo , ecl_grid->unit_x , ecl_grid->unit_y );
}
}
/*
If actnum == NULL that is taken to mean active.
for normal runs actnum will be 1 for active cells,
for dual porosity models it can also be 2 and 3.
*/
if (actnum == NULL)
cell->active = CELL_ACTIVE;
else
cell->active = actnum[global_index];
if (corsnum != NULL)
cell->coarse_group = corsnum[ global_index ] - 1;
}
static void ecl_grid_set_cell_GRID(ecl_grid_type * ecl_grid,
int coords_size,
const int * coords,
const float * corners) {
const int i = coords[0] - 1; /* eclipse 1 offset */
const int j = coords[1] - 1;
int k = coords[2] - 1;
int global_index;
ecl_cell_type * cell;
bool matrix_cell = true;
int active_value = CELL_ACTIVE_MATRIX;
/*
This is the rather hysterical treatment of dual porosity in GRID
files. Cells with k >= nz consitute the fracture part of the
grid. For these cell the cell properties are not recalculated, but
the active flag is updated to include the active|inactive
properties of the fracture.
*/
if (k >= ecl_grid->nz) {
k -= ecl_grid->nz;
matrix_cell = false;
active_value = CELL_ACTIVE_FRACTURE;
}
global_index = ecl_grid_get_global_index__(ecl_grid , i, j , k);
cell = ecl_grid_get_cell( ecl_grid , global_index);
/* the coords keyword can optionally contain 4,5 or 7 elements:
coords[0..2] = i,j,k
coords[3] = global_cell number (not used here)
----
coords[4] = 1,0 for active/inactive cells. Does NOT differentiate between matrix and fracture cells.
coords[5] = 0 for normal cells, icell of host cell for lgr cell.
coords[6] = 0 for normal cells, coarsening group for coarsened cell [not treated yet].
if coords[4] is not present it is assumed that the cell is active.
Note about LGRs: The GRID file format has an additional
keyword called LGRILG which maps out the parent box containing
an LGR - this only duplicates the information which can be
learned from the coords[5] element, and is not used in the
current code - however in good Schlum tradition a file with
4/5 element COORDS keywords and LGRs might come any day - then
we are fucked.
*/
{
int c;
/*
The ECL_GRID_MAINGRID_LGR_NR != ecl_grid->lgr_nr test checks if
this is a LGR; if this test applies we either have bug - or a
GRID file with LGRs and only 4/5 elements in the coords keywords.
In the latter case we must start using the LGRILG keyword.
*/
if ((ECL_GRID_MAINGRID_LGR_NR != ecl_grid->lgr_nr) && (coords_size != 7))
util_abort("%s: Need 7 element coords keywords for LGR - or reimplement to use LGRILG keyword.\n",__func__);
switch(coords_size) {
case 4: /* all cells active */
cell->active += active_value;
break;
case 5: /* only spesific cells active - no lgr */
cell->active += coords[4] * active_value;
break;
case 7:
cell->active += coords[4] * active_value;
cell->host_cell = coords[5] - 1;
cell->coarse_group = coords[6] - 1;
if (cell->coarse_group >= 0)
ecl_grid->coarsening_active = true;
break;
default:
util_abort("%s: coord size:%d unrecognized - should 4,5 or 7.\n",__func__ , coords_size);
}
if (matrix_cell) {
for (c = 0; c < 8; c++) {
point_set(&cell->corner_list[c] , corners[3*c] , corners[3*c + 1] , corners[3*c + 2]);
if (ecl_grid->use_mapaxes)
point_mapaxes_transform( &cell->corner_list[c] , ecl_grid->origo , ecl_grid->unit_x , ecl_grid->unit_y );
}
}
}
SET_CELL_FLAG(cell , CELL_FLAG_VALID );
}
/**
The function ecl_grid_set_active_index() must be called immediately
prior to calling this function, to ensure that
ecl_grid->total_active is correct.
*/
static void ecl_grid_init_index_map__(ecl_grid_type * ecl_grid,
int * index_map,
int * inv_index_map,
int active_mask,
int type_index) {
for (int global_index = 0; global_index < ecl_grid->size; global_index++) {
const ecl_cell_type * cell = ecl_grid_get_cell( ecl_grid , global_index);
if (cell->active & active_mask) {
index_map[global_index] = cell->active_index[type_index];
if (cell->coarse_group == COARSE_GROUP_NONE)
inv_index_map[cell->active_index[type_index]] = global_index;
//else: In the case of coarse groups the inv_index_map is set below.
} else
index_map[global_index] = -1;
}
}
static void ecl_grid_realloc_index_map(ecl_grid_type * ecl_grid) {
/* Creating the inverse mapping for the matrix cells. */
ecl_grid->index_map = (int*)util_realloc(ecl_grid->index_map,
ecl_grid->size * sizeof * ecl_grid->index_map);
ecl_grid->inv_index_map = (int*)util_realloc(ecl_grid->inv_index_map,
ecl_grid->total_active * sizeof * ecl_grid->inv_index_map);
ecl_grid_init_index_map__(ecl_grid,
ecl_grid->index_map,
ecl_grid->inv_index_map,
CELL_ACTIVE_MATRIX,
MATRIX_INDEX);
/* Create the inverse mapping for the fractures. */
if (ecl_grid->dualp_flag != FILEHEAD_SINGLE_POROSITY) {
ecl_grid->fracture_index_map = (int*)util_realloc(ecl_grid->fracture_index_map,
ecl_grid->size * sizeof * ecl_grid->fracture_index_map);
ecl_grid->inv_fracture_index_map = (int*)util_realloc(ecl_grid->inv_fracture_index_map,
ecl_grid->total_active_fracture * sizeof * ecl_grid->inv_fracture_index_map);
ecl_grid_init_index_map__(ecl_grid,
ecl_grid->fracture_index_map,
ecl_grid->inv_fracture_index_map,
CELL_ACTIVE_FRACTURE,
FRACTURE_INDEX);
}
/* Update the inverse map in the case of coarse cells. Observe that
in the case of coarse cells with more than one active cell in the
main grid, the inverse active -> global mapping will map to the
first active cell in the coarse cell.
*/
int size = ecl_grid_get_num_coarse_groups(ecl_grid);
for (int coarse_group = 0; coarse_group < size; coarse_group++) {
ecl_coarse_cell_type * coarse_cell = ecl_grid_iget_coarse_group(ecl_grid, coarse_group);
if (ecl_coarse_cell_get_num_active(coarse_cell) > 0) {
int global_index = ecl_coarse_cell_iget_active_cell_index(coarse_cell, 0);
int active_value = ecl_coarse_cell_iget_active_value(coarse_cell, 0);
int active_index = ecl_coarse_cell_get_active_index(coarse_cell);
int active_fracture_index = ecl_coarse_cell_get_active_fracture_index(coarse_cell);
if (active_value & CELL_ACTIVE_MATRIX) // active->global mapping point to one "random" cell in the coarse group
ecl_grid->inv_index_map[active_index] = global_index;
if (active_value & CELL_ACTIVE_FRACTURE)
ecl_grid->inv_fracture_index_map[active_fracture_index] = global_index;
int coarse_size = ecl_coarse_cell_get_size(coarse_cell);
const int_vector_type * global_index_list = ecl_coarse_cell_get_index_vector(coarse_cell);
for (int ic = 0; ic < coarse_size; ic++) {
int gi = int_vector_iget(global_index_list, ic);
if (active_value & CELL_ACTIVE_MATRIX) // All cells in the coarse group point to the same active index.
ecl_grid->index_map[gi] = active_index;
if (active_value & CELL_ACTIVE_FRACTURE)
ecl_grid->fracture_index_map[gi] = active_fracture_index;
}
} // else the coarse cell does not have any active cells.
}
}
/*
This function goes through the entire grid and sets the active_index
of all the cells. The functione ecl_grid_realloc_index_map()
subsequently reads this to create and initialize the index map.
*/
static void ecl_grid_set_active_index(ecl_grid_type * ecl_grid) {
int global_index;
int active_index = 0;
int active_fracture_index = 0;
if (!ecl_grid_have_coarse_cells( ecl_grid )) {
/* Keeping a fast path for the 99% most common case of no coarse
groups and single porosity. */
for (global_index = 0; global_index < ecl_grid->size; global_index++) {
ecl_cell_type * cell = ecl_grid_get_cell( ecl_grid , global_index);
if (cell->active & CELL_ACTIVE_MATRIX) {
cell->active_index[MATRIX_INDEX] = active_index;
active_index++;
}
}
if (ecl_grid->dualp_flag != FILEHEAD_SINGLE_POROSITY) {
for (global_index = 0; global_index < ecl_grid->size; global_index++) {
ecl_cell_type * cell = ecl_grid_get_cell( ecl_grid , global_index);
if (cell->active & CELL_ACTIVE_FRACTURE) {
cell->active_index[FRACTURE_INDEX] = active_fracture_index;
active_fracture_index++;
}
}
}
} else {
/* --- More involved path in the case of coarsening groups. --- */
/* 1: Go through all the cells and set the active index. In the
case of coarse cells we only set the common active index of
the entire coarse cell.
*/
for (global_index = 0; global_index < ecl_grid->size; global_index++) {
ecl_cell_type * cell = ecl_grid_get_cell( ecl_grid , global_index);
if (cell->active != CELL_NOT_ACTIVE) {
if (cell->coarse_group == COARSE_GROUP_NONE) {
if (cell->active & CELL_ACTIVE_MATRIX) {
cell->active_index[MATRIX_INDEX] = active_index;
active_index++;
}
if (cell->active & CELL_ACTIVE_FRACTURE) {
cell->active_index[FRACTURE_INDEX] = active_fracture_index;
active_fracture_index++;
}
} else {
ecl_coarse_cell_type * coarse_cell = ecl_grid_iget_coarse_group( ecl_grid , cell->coarse_group );
ecl_coarse_cell_update_index(coarse_cell,
global_index,
&active_index,
&active_fracture_index,
cell->active);
}
}
}
/*
2: Go through all the coarse cells and set the active index and
active value of all the cells in the coarse cell to the
common value for the coarse cell.
*/
int size = ecl_grid_get_num_coarse_groups(ecl_grid);
for (int coarse_group = 0; coarse_group < size; coarse_group++) {
ecl_coarse_cell_type * coarse_cell = ecl_grid_iget_coarse_group( ecl_grid , coarse_group );
if (ecl_coarse_cell_get_num_active(coarse_cell) > 0) {
int cell_active_index = ecl_coarse_cell_get_active_index( coarse_cell );
int cell_active_value = ecl_coarse_cell_iget_active_value( coarse_cell , 0);
int group_size = ecl_coarse_cell_get_size( coarse_cell );
const int * coarse_cell_list = ecl_coarse_cell_get_index_ptr( coarse_cell );
for (int i=0; i < group_size; i++) {
global_index = coarse_cell_list[i];
ecl_cell_type * cell = ecl_grid_get_cell( ecl_grid , global_index );
if (cell_active_value & CELL_ACTIVE_MATRIX)
cell->active_index[MATRIX_INDEX] = cell_active_index;
/* Coarse cell and dual porosity - that is probably close to zero measure. */
if (cell_active_value & CELL_ACTIVE_FRACTURE) {
int cell_active_fracture_index = ecl_coarse_cell_get_active_fracture_index( coarse_cell );
cell->active_index[FRACTURE_INDEX] = cell_active_fracture_index;
}
}
}
}
}
ecl_grid->total_active = active_index;
ecl_grid->total_active_fracture = active_fracture_index;
}
static void ecl_grid_update_index( ecl_grid_type * ecl_grid) {
ecl_grid_set_active_index(ecl_grid);
ecl_grid_realloc_index_map(ecl_grid);
}
/*****************************************************************/
/* Coarse cells */
static ecl_coarse_cell_type * ecl_grid_get_or_create_coarse_cell( ecl_grid_type * ecl_grid , int coarse_nr) {
if (vector_safe_iget( ecl_grid->coarse_cells , coarse_nr ) == NULL)
vector_iset_owned_ref( ecl_grid->coarse_cells , coarse_nr , ecl_coarse_cell_alloc() , ecl_coarse_cell_free__);
return (ecl_coarse_cell_type*)vector_iget( ecl_grid->coarse_cells , coarse_nr );
}
static void ecl_grid_init_coarse_cells( ecl_grid_type * ecl_grid ) {
if (ecl_grid->coarsening_active) {
int global_index;
for (global_index = 0; global_index < ecl_grid->size; global_index++) {
ecl_cell_type * cell = ecl_grid_get_cell( ecl_grid , global_index );
if (cell->coarse_group != COARSE_GROUP_NONE) {
ecl_coarse_cell_type * coarse_cell = ecl_grid_get_or_create_coarse_cell( ecl_grid , cell->coarse_group);
int i,j,k;
ecl_grid_get_ijk1( ecl_grid , global_index , &i , &j , &k);
ecl_coarse_cell_update( coarse_cell , i , j , k , global_index );
}
}
}
}
ecl_coarse_cell_type * ecl_grid_iget_coarse_group( const ecl_grid_type * ecl_grid , int coarse_nr ) {
return (ecl_coarse_cell_type*)vector_iget( ecl_grid->coarse_cells , coarse_nr );
}
ecl_coarse_cell_type * ecl_grid_get_cell_coarse_group1( const ecl_grid_type * ecl_grid , int global_index) {
ecl_cell_type * cell = ecl_grid_get_cell( ecl_grid , global_index );
if (cell->coarse_group == COARSE_GROUP_NONE)
return NULL;
else
return ecl_grid_iget_coarse_group( ecl_grid , cell->coarse_group );
}
ecl_coarse_cell_type * ecl_grid_get_cell_coarse_group3( const ecl_grid_type * ecl_grid , int i , int j , int k) {
int global_index = ecl_grid_get_global_index3( ecl_grid , i , j , k );
return ecl_grid_get_cell_coarse_group1( ecl_grid , global_index );
}
bool ecl_grid_cell_in_coarse_group1( const ecl_grid_type * main_grid , int global_index ) {
ecl_cell_type * cell = ecl_grid_get_cell( main_grid , global_index );
if (cell->coarse_group == COARSE_GROUP_NONE )
return false;
else
return true;
}
bool ecl_grid_cell_in_coarse_group3( const ecl_grid_type * main_grid , int i , int j , int k) {
return ecl_grid_cell_in_coarse_group1( main_grid , ecl_grid_get_global_index3( main_grid , i , j , k ));
}
/*****************************************************************/
static void ecl_grid_pillar_cross_planes(const point_type * p0,
double e_x , double e_y , double e_z ,
const double *z , double *x , double *y) {
int k;
if (e_z != 0) {
for (k=0; k < 2; k++) {
double t = (z[k] - p0->z) / e_z;
x[k] = p0->x + t * e_x;
y[k] = p0->y + t * e_y;
}
} else {
for (k=0; k < 2; k++) {
x[k] = p0->x;
y[k] = p0->y;
}
}
}
/**
This function must be run before the cell coordinates are
calculated. This function is only called for the main grid
instance, and not for lgrs; the lgrs will inherit the mapaxes
settings from the global grid.
*/
static void ecl_grid_init_mapaxes( ecl_grid_type * ecl_grid , bool apply_mapaxes, const float * mapaxes) {
if (ecl_grid->global_grid != NULL)
util_abort("%s: hmmmm - this is a major fuck up; trying to grid transformation data from mapaxes for a subgrid(lgr)\n",__func__);
{
const double unit_y[2] = {mapaxes[0] - mapaxes[2] , mapaxes[1] - mapaxes[3]};
const double unit_x[2] = {mapaxes[4] - mapaxes[2] , mapaxes[5] - mapaxes[3]};
{
double norm_x = 1.0/sqrt( unit_x[0]*unit_x[0] + unit_x[1]*unit_x[1] );
double norm_y = 1.0/sqrt( unit_y[0]*unit_y[0] + unit_y[1]*unit_y[1] );
ecl_grid->unit_x[0] = unit_x[0] * norm_x;
ecl_grid->unit_x[1] = unit_x[1] * norm_x;
ecl_grid->unit_y[0] = unit_y[0] * norm_y;
ecl_grid->unit_y[1] = unit_y[1] * norm_y;
}
ecl_grid->origo[0] = mapaxes[2];
ecl_grid->origo[1] = mapaxes[3];
ecl_grid->mapaxes = (float*)util_malloc( 6 * sizeof * ecl_grid->mapaxes );
memcpy( ecl_grid->mapaxes , mapaxes , 6 * sizeof( float ));
/*
If the apply_mapaxes variable is false we will not apply the
transformation; however we will still internalize the mapaxes
information, and also output that if the file is saved again.
*/
ecl_grid->use_mapaxes = apply_mapaxes;
}
}
/**
this function will add a ecl_grid instance as a lgr to the main
grid. the lgr grid as added to two different structures of the main
grid:
1. in the main_grid->lgr_list the lgr instances are inserted in
order of occurence in the grid file. the following equalities
should apply:
occurence number in file == lgr_grid->grid_nr
This 'mostly' agrees with the GRIDHEAD(4) item, but
unfortunately not always. Cases have popped up where the series
of GRIDHEAD(4) values from lgr to lgr have holes :-(
when installed in the lgr_list vector the lgr grid is installed
with a destructor, i.e. the grid is destroyed when the vector
is destroyed.
2. in the main->lgr_hash the lgr instance is installed with the
lgrname as key. only a reference is installed in the hash
table.
observe that this is in principle somewhat different from the
install functions below; here the lgr is added to the top level
grid (i.e. the main grid) which has the storage responsability of
all the lgr instances. the cell->lgr relationship is established
in the _install_egrid / install_grid functions further down.
*/
static void ecl_grid_add_lgr( ecl_grid_type * main_grid , ecl_grid_type * lgr_grid) {
vector_append_owned_ref( main_grid->LGR_list , lgr_grid , ecl_grid_free__);
int_vector_iset(main_grid->lgr_index_map, lgr_grid->lgr_nr, vector_get_size(main_grid->LGR_list)-1);
hash_insert_ref( main_grid->LGR_hash , lgr_grid->name , lgr_grid);
}
static void ecl_grid_install_lgr_common(ecl_grid_type * host_grid , ecl_grid_type * lgr_grid) {
hash_insert_ref( host_grid->children , lgr_grid->name , lgr_grid);
lgr_grid->parent_grid = host_grid;
}
/**
this function will set the lgr pointer of the relevant cells in the
host grid to point to the lgr_grid. observe that the ecl_cell_type
instances do *not* own the lgr_grid - all lgr_grid instances are
owned by the main grid.
*/
static void ecl_grid_install_lgr_EGRID(ecl_grid_type * host_grid , ecl_grid_type * lgr_grid , const int * hostnum) {
int global_lgr_index;
for (global_lgr_index = 0; global_lgr_index < lgr_grid->size; global_lgr_index++) {
int host_index = hostnum[ global_lgr_index ] - 1;
ecl_cell_type * lgr_cell = ecl_grid_get_cell( lgr_grid , global_lgr_index);
ecl_cell_type * host_cell = ecl_grid_get_cell( host_grid , host_index );
ecl_cell_install_lgr( host_cell , lgr_grid );
lgr_cell->host_cell = host_index;
}
ecl_grid_install_lgr_common( host_grid , lgr_grid );
}
/**
similar to ecl_grid_install_lgr_egrid for grid based instances.
*/
static void ecl_grid_install_lgr_GRID(ecl_grid_type * host_grid , ecl_grid_type * lgr_grid) {
int global_lgr_index;
for (global_lgr_index = 0; global_lgr_index < lgr_grid->size; global_lgr_index++) {
ecl_cell_type * lgr_cell = ecl_grid_get_cell( lgr_grid , global_lgr_index);
ecl_cell_type * host_cell = ecl_grid_get_cell( host_grid , lgr_cell->host_cell );
ecl_cell_install_lgr( host_cell , lgr_grid );
}
ecl_grid_install_lgr_common( host_grid , lgr_grid );
}
/**
sets the name of the lgr and the name of the parent, if this is a
nested lgr. for normal lgr descending directly from the coarse grid
the parent_name is set to NULL.
*/
static void ecl_grid_set_lgr_name_EGRID(ecl_grid_type * lgr_grid , const ecl_file_type * ecl_file , int grid_nr) {
ecl_kw_type * lgrname_kw = ecl_file_iget_named_kw( ecl_file , LGR_KW , grid_nr - 1);
lgr_grid->name = (char*)util_alloc_strip_copy( (const char*)ecl_kw_iget_ptr( lgrname_kw , 0) ); /* trailing zeros are stripped away. */
if (ecl_file_has_kw( ecl_file , LGR_PARENT_KW)) {
ecl_kw_type * parent_kw = ecl_file_iget_named_kw( ecl_file , LGR_PARENT_KW , grid_nr -1);
char * parent = (char*)util_alloc_strip_copy( (const char*)ecl_kw_iget_ptr( parent_kw , 0));
if (strlen( parent ) > 0)
lgr_grid->parent_name = parent;
else /* lgr_grid->parent has been initialized to NULL */
free( parent );
}
}
/**
sets the name of the lgr and the name of the parent, if this is a
nested lgr. for lgr descending directly from the parent eclipse
will supply 'global' (whereas for egrid it will return '' -
cool?). anyway global -> NULL.
*/
static void ecl_grid_set_lgr_name_GRID(ecl_grid_type * lgr_grid , const ecl_file_type * ecl_file , int grid_nr) {
ecl_kw_type * lgr_kw = ecl_file_iget_named_kw( ecl_file , LGR_KW , grid_nr - 1);
lgr_grid->name = (char*)util_alloc_strip_copy( (const char*)ecl_kw_iget_ptr( lgr_kw , 0) ); /* trailing zeros are stripped away. */
{
/**
the lgr keyword can have one or two elements; in the case of two elements
the second element will be the name of the parent grid - in the case of
only one element the current lgr is assumed to descend from the main grid
*/
if (ecl_kw_get_size( lgr_kw ) == 2) {
char * parent = (char*)util_alloc_strip_copy( (const char*)ecl_kw_iget_ptr( lgr_kw , 1));
if ((strlen(parent) == 0) || (strcmp(parent , GLOBAL_STRING ) == 0))
free( parent );
else
lgr_grid->parent_name = parent;
}
}
}
/*
This function will calculate the index of a corner in the the zcorn
array. The corner is given with i,j,k indices of the cell, and
corner number [0...8). The function is mainly a convenience function
for testing, and should not be used in a tight inner loop unrolling
the zcorn vector.
*/
int ecl_grid_zcorn_index__(int nx, int ny , int i, int j , int k , int c) {
int zcorn_index = k*8*nx*ny + j*4*nx + 2*i;
if ((c % 2) == 1)
zcorn_index += 1;
if ((c % 4) == 2)
zcorn_index += 2*nx;
if ((c % 4) == 3)
zcorn_index += 2*nx;
if (c >= 4)
zcorn_index += 4*nx*ny;
return zcorn_index;
}
int ecl_grid_zcorn_index(const ecl_grid_type * grid , int i, int j , int k , int c) {
return ecl_grid_zcorn_index__( grid->nx, grid->ny , i , j , k , c );
}
static void ecl_grid_init_GRDECL_data_jslice(ecl_grid_type * ecl_grid,
const double * zcorn,
const double * coord,
const int * actnum,
const int * corsnum,
int j) {
const int nx = ecl_grid->nx;
const int ny = ecl_grid->ny;
const int nz = ecl_grid->nz;
int i;
for (i=0; i < nx; i++) {
point_type pillars[4][2];
int pillar_index[4];
pillar_index[0] = 6 * ( j * (nx + 1) + i );
pillar_index[1] = 6 * ( j * (nx + 1) + i + 1);
pillar_index[2] = 6 * ((j + 1) * (nx + 1) + i );
pillar_index[3] = 6 * ((j + 1) * (nx + 1) + i + 1);
{
int ip;
for (ip = 0; ip < 4; ip++) {
int index = pillar_index[ip];
point_set(&pillars[ip][0] , coord[index] , coord[index + 1] , coord[index + 2]);
index += 3;
point_set(&pillars[ip][1] , coord[index] , coord[index + 1] , coord[index + 2]);
}
}
{
double ex[4];
double ey[4];
double ez[4];
int k;
{
int ip;
for (ip = 0; ip < 4; ip++) {
ex[ip] = pillars[ip][1].x - pillars[ip][0].x;
ey[ip] = pillars[ip][1].y - pillars[ip][0].y;
ez[ip] = pillars[ip][1].z - pillars[ip][0].z;
}
}
for (k=0; k < nz; k++) {
double x[4][2];
double y[4][2];
double z[4][2];
{
int c;
for (c = 0; c < 2; c++) {
z[0][c] = zcorn[k*8*nx*ny + j*4*nx + 2*i + c*4*nx*ny];
z[1][c] = zcorn[k*8*nx*ny + j*4*nx + 2*i + 1 + c*4*nx*ny];
z[2][c] = zcorn[k*8*nx*ny + j*4*nx + 2*nx + 2*i + c*4*nx*ny];
z[3][c] = zcorn[k*8*nx*ny + j*4*nx + 2*nx + 2*i + 1 + c*4*nx*ny];
}
}
{
int ip;
for (ip = 0; ip < 4; ip++)
ecl_grid_pillar_cross_planes(&pillars[ip][0] , ex[ip], ey[ip] , ez[ip] , z[ip] , x[ip] , y[ip]);
}
ecl_grid_set_cell_EGRID(ecl_grid , i , j , k , x , y , z , actnum , corsnum);
}
}
}
}
static void ecl_grid_init_GRDECL_data_jslice(ecl_grid_type * ecl_grid,
const float * zcorn,
const float * coord,
const int * actnum,
const int * corsnum,
int j) {
const int nx = ecl_grid->nx;
const int ny = ecl_grid->ny;
const int nz = ecl_grid->nz;
int i;
for (i=0; i < nx; i++) {
point_type pillars[4][2];
int pillar_index[4];
pillar_index[0] = 6 * ( j * (nx + 1) + i );
pillar_index[1] = 6 * ( j * (nx + 1) + i + 1);
pillar_index[2] = 6 * ((j + 1) * (nx + 1) + i );
pillar_index[3] = 6 * ((j + 1) * (nx + 1) + i + 1);
{
int ip;
for (ip = 0; ip < 4; ip++) {
int index = pillar_index[ip];
point_set(&pillars[ip][0] , coord[index] , coord[index + 1] , coord[index + 2]);
index += 3;
point_set(&pillars[ip][1] , coord[index] , coord[index + 1] , coord[index + 2]);
}
}
{
double ex[4];
double ey[4];
double ez[4];
int k;
{
int ip;
for (ip = 0; ip < 4; ip++) {
ex[ip] = pillars[ip][1].x - pillars[ip][0].x;
ey[ip] = pillars[ip][1].y - pillars[ip][0].y;
ez[ip] = pillars[ip][1].z - pillars[ip][0].z;
}
}
for (k=0; k < nz; k++) {
double x[4][2];
double y[4][2];
double z[4][2];
{
int c;
for (c = 0; c < 2; c++) {
z[0][c] = zcorn[k*8*nx*ny + j*4*nx + 2*i + c*4*nx*ny];
z[1][c] = zcorn[k*8*nx*ny + j*4*nx + 2*i + 1 + c*4*nx*ny];
z[2][c] = zcorn[k*8*nx*ny + j*4*nx + 2*nx + 2*i + c*4*nx*ny];
z[3][c] = zcorn[k*8*nx*ny + j*4*nx + 2*nx + 2*i + 1 + c*4*nx*ny];
}
}
{
int ip;
for (ip = 0; ip < 4; ip++)
ecl_grid_pillar_cross_planes(&pillars[ip][0] , ex[ip], ey[ip] , ez[ip] , z[ip] , x[ip] , y[ip]);
}
ecl_grid_set_cell_EGRID(ecl_grid , i , j , k , x , y , z , actnum , corsnum);
}
}
}
}
void ecl_grid_init_GRDECL_data(ecl_grid_type * ecl_grid,
const double * zcorn,
const double * coord,
const int * actnum,
const int * corsnum) {
const int ny = ecl_grid->ny;
int j;
#pragma omp parallel for
for ( j=0; j < ny; j++)
ecl_grid_init_GRDECL_data_jslice( ecl_grid , zcorn, coord , actnum , corsnum , j );
}
void ecl_grid_init_GRDECL_data(ecl_grid_type * ecl_grid,
const float * zcorn,
const float * coord,
const int * actnum,
const int * corsnum) {
const int ny = ecl_grid->ny;
int j;
#pragma omp parallel for
for ( j=0; j < ny; j++)
ecl_grid_init_GRDECL_data_jslice( ecl_grid , zcorn, coord , actnum , corsnum , j );
}
/*
2---3
| |
0---1
*/
static ecl_grid_type * ecl_grid_alloc_GRDECL_data__(ecl_grid_type * global_grid,
ert_ecl_unit_enum unit_system,
int dualp_flag,
bool apply_mapaxes,
int nx,
int ny,
int nz,
const double * zcorn,
const double * coord,
const int * actnum,
const float * mapaxes,
const int * corsnum,
int lgr_nr) {
ecl_grid_type * ecl_grid = ecl_grid_alloc_empty(global_grid , unit_system, dualp_flag , nx,ny,nz,lgr_nr,true);
if (ecl_grid) {
if (mapaxes != NULL)
ecl_grid_init_mapaxes( ecl_grid , apply_mapaxes, mapaxes );
if (corsnum != NULL)
ecl_grid->coarsening_active = true;
ecl_grid->coord_kw = ecl_kw_alloc("COORD" , 6*(nx + 1) * (ny + 1) , ECL_FLOAT);
{
float * coord_float = (float *)ecl_kw_get_ptr(ecl_grid->coord_kw);
for (int i=0; i < ecl_kw_get_size(ecl_grid->coord_kw); i++)
coord_float[i] = coord[i];
}
ecl_grid_init_GRDECL_data( ecl_grid , zcorn , coord , actnum , corsnum);
ecl_grid_init_coarse_cells( ecl_grid );
ecl_grid_update_index( ecl_grid );
ecl_grid_taint_cells( ecl_grid );
}
return ecl_grid;
}
static ecl_grid_type * ecl_grid_alloc_GRDECL_data__(ecl_grid_type * global_grid,
ert_ecl_unit_enum unit_system,
int dualp_flag,
bool apply_mapaxes,
int nx,
int ny,
int nz,
const float * zcorn,
const float * coord,
const int * actnum,
const float * mapaxes,
const int * corsnum,
int lgr_nr) {
ecl_grid_type * ecl_grid = ecl_grid_alloc_empty(global_grid, unit_system, dualp_flag , nx,ny,nz,lgr_nr,true);
if (ecl_grid) {
if (mapaxes != NULL)
ecl_grid_init_mapaxes( ecl_grid , apply_mapaxes, mapaxes );
if (corsnum != NULL)
ecl_grid->coarsening_active = true;
ecl_grid->coord_kw = ecl_kw_alloc_new("COORD" , 6*(nx + 1) * (ny + 1) , ECL_FLOAT , coord );
ecl_grid_init_GRDECL_data( ecl_grid , zcorn , coord , actnum , corsnum);
ecl_grid_init_coarse_cells( ecl_grid );
ecl_grid_update_index( ecl_grid );
ecl_grid_taint_cells( ecl_grid );
}
return ecl_grid;
}
static void ecl_grid_copy_mapaxes( ecl_grid_type * target_grid , const ecl_grid_type * src_grid ) {
target_grid->use_mapaxes = src_grid->use_mapaxes;
if (src_grid->mapaxes)
target_grid->mapaxes = (float*)util_realloc_copy(target_grid->mapaxes , src_grid->mapaxes , 6 * sizeof * src_grid->mapaxes );
else
target_grid->mapaxes = (float*)util_realloc_copy(target_grid->mapaxes , NULL , 0 );
for (int i=0; i < 2; i++) {
target_grid->unit_x[i] = src_grid->unit_x[i];
target_grid->unit_y[i] = src_grid->unit_y[i];
target_grid->origo[i] = src_grid->origo[i];
}
}
static void ecl_grid_copy_content( ecl_grid_type * target_grid , const ecl_grid_type * src_grid ) {
int global_index;
for (global_index = 0; global_index < src_grid->size; global_index++) {
ecl_cell_type * target_cell = ecl_grid_get_cell( target_grid , global_index);
const ecl_cell_type * src_cell = ecl_grid_get_cell( src_grid , global_index );
ecl_cell_memcpy( target_cell , src_cell );
if (src_cell->nnc_info)
target_cell->nnc_info = nnc_info_alloc_copy( src_cell->nnc_info );
}
ecl_grid_copy_mapaxes( target_grid , src_grid );
target_grid->parent_name = util_alloc_string_copy( src_grid->parent_name );
target_grid->name = util_alloc_string_copy( src_grid->name );
target_grid->coarsening_active = src_grid->coarsening_active;
ecl_grid_init_coarse_cells( target_grid );
}
static ecl_grid_type * ecl_grid_alloc_copy__( const ecl_grid_type * src_grid, ecl_grid_type * main_grid ) {
ecl_grid_type * copy_grid = ecl_grid_alloc_empty( main_grid ,
src_grid->unit_system,
src_grid->dualp_flag ,
ecl_grid_get_nx( src_grid ) ,
ecl_grid_get_ny( src_grid ) ,
ecl_grid_get_nz( src_grid ) ,
0 ,
false );
if (copy_grid) {
ecl_grid_copy_content( copy_grid , src_grid ); // This will handle everything except LGR relationships which is established in the calling routine
ecl_grid_update_index( copy_grid );
}
return copy_grid;
}
ecl_grid_type * ecl_grid_alloc_copy( const ecl_grid_type * src_grid ) {
ecl_grid_type * copy_grid = ecl_grid_alloc_copy__( src_grid , NULL );
{
int grid_nr;
for (grid_nr = 0; grid_nr < vector_get_size( src_grid->LGR_list ); grid_nr++) {
const ecl_grid_type * src_lgr = (const ecl_grid_type*)vector_iget_const( src_grid->LGR_list , grid_nr);
ecl_grid_type * copy_lgr = ecl_grid_alloc_copy__( src_lgr , copy_grid );
ecl_grid_type * host_grid;
ecl_grid_add_lgr( copy_grid , copy_lgr ); // This handles the storage ownership of the LGR.
if (copy_lgr->parent_name == NULL)
host_grid = copy_grid;
else
host_grid = ecl_grid_get_lgr( copy_grid , copy_lgr->parent_name );
{
int global_lgr_index;
for (global_lgr_index = 0; global_lgr_index < copy_lgr->size; global_lgr_index++) {
ecl_cell_type * lgr_cell = ecl_grid_get_cell( copy_lgr , global_lgr_index);
ecl_cell_type * host_cell = ecl_grid_get_cell( host_grid , lgr_cell->host_cell );
ecl_cell_install_lgr( host_cell , copy_lgr );
}
ecl_grid_install_lgr_common( host_grid , copy_lgr );
}
}
}
return copy_grid;
}
/*
Does not handle LGR
*/
ecl_grid_type * ecl_grid_alloc_processed_copy( const ecl_grid_type * src_grid , const double * zcorn , const int * actnum)
{
ecl_grid_type * grid;
if (zcorn == NULL) {
grid = ecl_grid_alloc_copy__( src_grid , NULL );
if (actnum)
ecl_grid_reset_actnum( grid , actnum );
} else {
int nx,ny,nz,na;
int zcorn_size = ecl_grid_get_zcorn_size( src_grid );
float * zcorn_float = (float*)util_malloc( zcorn_size * sizeof * zcorn_float );
float * coord = ecl_grid_alloc_coord_data( src_grid );
float * mapaxes = NULL;
if (ecl_grid_get_mapaxes( src_grid )) {
mapaxes = (float*)util_malloc( 6 * sizeof * mapaxes );
ecl_grid_init_mapaxes_data_float(src_grid, mapaxes);
}
ecl_grid_get_dims( src_grid , &nx, &ny , &nz , &na);
for (int i=0; i < zcorn_size; i++)
zcorn_float[i] = zcorn[i];
grid = ecl_grid_alloc_GRDECL_data( nx , ny , nz , zcorn_float , coord , actnum , src_grid->use_mapaxes, mapaxes );
free( mapaxes );
free( coord );
free( zcorn_float );
}
return grid;
}
/*
If you create/load data for the various fields, this function can be
used to create a GRID instance, without going through a GRID/EGRID
file - currently the implementation does not support the creation of
a lgr hierarchy - or cell coarsening.
*/
ecl_grid_type * ecl_grid_alloc_GRDECL_data(int nx,
int ny,
int nz,
const float * zcorn,
const float * coord,
const int * actnum,
bool apply_mapaxes,
const float * mapaxes) {
ert_ecl_unit_enum unit_system = ECL_METRIC_UNITS;
return ecl_grid_alloc_GRDECL_data__(NULL,
unit_system,
FILEHEAD_SINGLE_POROSITY,
apply_mapaxes,
nx,
ny,
nz,
zcorn,
coord,
actnum,
mapaxes,
NULL,
0);
}
namespace ecl {
ecl_grid_type * ecl_grid_alloc_GRDECL_data(int nx,
int ny,
int nz,
const double * zcorn,
const double * coord,
const int * actnum,
bool apply_mapaxes,
const float * mapaxes) {
ert_ecl_unit_enum unit_system = ECL_METRIC_UNITS;
return ecl_grid_alloc_GRDECL_data__(NULL,
unit_system,
FILEHEAD_SINGLE_POROSITY,
apply_mapaxes,
nx,
ny,
nz,
zcorn,
coord,
actnum,
mapaxes,
NULL,
0);
}
}
const float * ecl_grid_get_mapaxes_from_kw__(const ecl_kw_type * mapaxes_kw) {
const float * mapaxes_data = ecl_kw_get_float_ptr(mapaxes_kw);
float x1 = mapaxes_data[2];
float y1 = mapaxes_data[3];
float x2 = mapaxes_data[4];
float y2 = mapaxes_data[5];
float norm_denominator = x1 * y2 - x2 * y1;
if(norm_denominator == 0.0) {
mapaxes_data = NULL;
}
return mapaxes_data;
}
static ecl_grid_type * ecl_grid_alloc_GRDECL_kw__(ecl_grid_type * global_grid ,
int dualp_flag,
bool apply_mapaxes,
const ecl_kw_type * gridhead_kw ,
const ecl_kw_type * zcorn_kw ,
const ecl_kw_type * coord_kw ,
const ecl_kw_type * gridunit_kw, /* Can be NULL */
const ecl_kw_type * mapaxes_kw , /* Can be NULL */
const ecl_kw_type * corsnum_kw, /* Can be NULL */
const int * actnum_data) { /* Can be NULL */
int gtype, nx,ny,nz, lgr_nr;
ert_ecl_unit_enum unit_system = ECL_METRIC_UNITS;
gtype = ecl_kw_iget_int(gridhead_kw , GRIDHEAD_TYPE_INDEX);
nx = ecl_kw_iget_int(gridhead_kw , GRIDHEAD_NX_INDEX);
ny = ecl_kw_iget_int(gridhead_kw , GRIDHEAD_NY_INDEX);
nz = ecl_kw_iget_int(gridhead_kw , GRIDHEAD_NZ_INDEX);
lgr_nr = ecl_kw_iget_int(gridhead_kw , GRIDHEAD_LGR_INDEX);
/*
The code used to have this test:
if (grid_nr != ecl_kw_iget_int( gridhead_kw , GRIDHEAD_LGR_INDEX))
util_abort("%s: internal error in grid loader - lgr index mismatch\n",__func__);
But then suddenly a EGRID file where this did not apply appeared :-(
*/
if (gtype != GRIDHEAD_GRIDTYPE_CORNERPOINT)
util_abort("%s: gtype:%d fatal error when loading grid - must have corner point grid - aborting\n",__func__ , gtype );
{
const float * mapaxes_data = NULL;
const int * corsnum_data = NULL;
if (mapaxes_kw)
mapaxes_data = ecl_grid_get_mapaxes_from_kw__(mapaxes_kw);
if (corsnum_kw)
corsnum_data = ecl_kw_get_int_ptr( corsnum_kw );
if (gridunit_kw)
unit_system = ecl_grid_check_unit_system(gridunit_kw);
return ecl_grid_alloc_GRDECL_data__(global_grid ,
unit_system,
dualp_flag ,
apply_mapaxes,
nx , ny , nz ,
ecl_kw_get_float_ptr(zcorn_kw) ,
ecl_kw_get_float_ptr(coord_kw) ,
actnum_data,
mapaxes_data,
corsnum_data,
lgr_nr);
}
}
/**
If you create/load ecl_kw instances for the various fields, this
function can be used to create a GRID instance, without going
through a GRID/EGRID file. Does not support LGR or coarsening
hierarchies.
*/
ecl_grid_type * ecl_grid_alloc_GRDECL_kw( int nx, int ny , int nz ,
const ecl_kw_type * zcorn_kw ,
const ecl_kw_type * coord_kw ,
const ecl_kw_type * actnum_kw , /* Can be NULL */
const ecl_kw_type * mapaxes_kw ) { /* Can be NULL */
const int * actnum_data = NULL;
if (actnum_kw)
actnum_data = ecl_kw_get_int_ptr(actnum_kw);
bool apply_mapaxes = true;
ecl_kw_type * gridhead_kw = ecl_grid_alloc_gridhead_kw( nx, ny, nz, 0);
ecl_kw_type * gridunit_kw = NULL;
ecl_grid_type * ecl_grid = ecl_grid_alloc_GRDECL_kw__(NULL,
FILEHEAD_SINGLE_POROSITY,
apply_mapaxes,
gridhead_kw,
zcorn_kw,
coord_kw,
gridunit_kw,
mapaxes_kw,
NULL,
actnum_data);
ecl_kw_free( gridhead_kw );
return ecl_grid;
}
static void ecl_grid_init_cell_nnc_info(ecl_grid_type * ecl_grid, int global_index) {
ecl_cell_type * grid_cell = ecl_grid_get_cell(ecl_grid, global_index);
if (!grid_cell->nnc_info)
grid_cell->nnc_info = nnc_info_alloc(ecl_grid->lgr_nr);
}
/*
The function ecl_grid_add_self_nnc() will add a NNC connection
between two cells in the same grid. Observe that there are two
peculiarities with this implementation:
1. In the ecl_grid structure the nnc information is distributed
among the cells. The main purpose of adding the nnc information
like this is to include the NNC information in the EGRID files
when writing to disk. Before being written to disk the NNC
information is serialized into vectors NNC1 and NNC2. It is the
ordering in the NNC1 and NNC2 vectors which must be correct, and
that is goverened by the nnc_index argument - i.e. the nnc_index
serves as an 'ID' for the NNC connections.
After all NCC information has been entered you should be certain
that all nnc_index values in the range [0,num_nnc] have been
set.
2. To get valid NNC information to load in e.g. Resinsight the
corresponding TRANNNC keyword must be added to the INIT file,
i.e. the calling scope must create a ecl_kw with
transmissibility values in parallell with adding NNC information
to the grid:
fortio_type * init_file = fortio_open_writer( "CASE.INIT" , ...
ecl_grid_type * grid ...
ecl_kw_type * trannnc_kw = ecl_kw_alloc( "TRANNNC" , num_nnc , ECL_FLOAT_TYPE );
for (int i = 0; i < num_nnc; i++) {
int g1 = ...
int g2 = ..
float T = ..
ecl_grid_add_self_nnc( grid , g1 , g2 , i );
ecl_kw_iset( trannnc_kw , i , T );
}
...
ecl_grid_fwrite_EGRID( grid , ... );
ecl_kw_fwrite( trannnc_kw , init_file );
*/
void ecl_grid_add_self_nnc( ecl_grid_type * grid, int cell_index1, int cell_index2, int nnc_index) {
ecl_cell_type * grid_cell = ecl_grid_get_cell(grid, cell_index1);
ecl_grid_init_cell_nnc_info(grid, cell_index1);
nnc_info_add_nnc(grid_cell->nnc_info, grid->lgr_nr, cell_index2, nnc_index);
}
/*
This function will add all the nnc connections given by the g1_list
and g2_list arrays. The ncc connections will be added with
consecutively running nnc_index = [0,num_nnc).
*/
void ecl_grid_add_self_nnc_list( ecl_grid_type * grid, const int * g1_list , const int * g2_list , int num_nnc ) {
int i;
for (i = 0; i < num_nnc; i++)
ecl_grid_add_self_nnc( grid , g1_list[i] , g2_list[i] , i );
}
/*
This function populates nnc_info for cells with non neighbour
connections. For cells C1 and C2 the function will only add the directed link:
C1 -> C2
i.e. it is impossible to go from C2 and back to C1. The functions
will add links as:
NNC1 -> NNC2 For nnc between cells in the same grid.
NNCG -> NNCL For global -> lgr connection
NNA1 -> NNA2 For links between different LGRs
*/
static void ecl_grid_init_nnc_cells( ecl_grid_type * grid1, ecl_grid_type * grid2, const ecl_kw_type * keyword1, const ecl_kw_type * keyword2) {
int * grid1_nnc_cells = ecl_kw_get_int_ptr(keyword1);
int * grid2_nnc_cells = ecl_kw_get_int_ptr(keyword2);
int nnc_count = ecl_kw_get_size(keyword2);
int nnc_index;
for (nnc_index = 0; nnc_index < nnc_count; nnc_index++) {
int grid1_cell_index = grid1_nnc_cells[nnc_index] -1;
int grid2_cell_index = grid2_nnc_cells[nnc_index] -1;
/*
In the ECLIPSE output format grids with dual porosity are (to some
extent ...) modeled as two independent grids stacked on top of
eachother, where the fracture cells have global index in the range
[nx*ny*nz, 2*nx*ny*nz).
The physical connection between the matrix and the fractures in
cell nr c is modelled as an nnc: cell[c] -> cell[c + nx*ny*nz]. In
the ert ecl library we only have cells in the range [0,nx*ny*nz),
and fracture is a property of a cell. We therefore do not include
nnc connections involving fracture cells (i.e. cell_index >=
nx*ny*nz).
*/
if ((FILEHEAD_SINGLE_POROSITY != grid1->dualp_flag) &&
((grid1_cell_index >= grid1->size) ||
(grid2_cell_index >= grid2->size)))
break;
{
ecl_cell_type * grid1_cell = ecl_grid_get_cell(grid1, grid1_cell_index);
ecl_grid_init_cell_nnc_info(grid1, grid1_cell_index);
nnc_info_add_nnc(grid1_cell->nnc_info, grid2->lgr_nr, grid2_cell_index , nnc_index);
}
}
}
/*
This function reads the non-neighbour connection data from file and initializes the grid structure with the the nnc data
*/
static void ecl_grid_init_nnc(ecl_grid_type * main_grid, ecl_file_type * ecl_file) {
int num_nnchead_kw = ecl_file_get_num_named_kw( ecl_file , NNCHEAD_KW );
int i;
/*
NB: There is a bug in Eclipse version 2015.1, for MPI runs with
six or more processors (I think ...) the NNC datastructures
are in an internally inconsistent state; and will lead to a
hard crash. The issue has been fixed in version 2015.2, but
unfortunately it is not possible to test for micro version.
if(num_nnchead_kw > 0 && main_grid->eclipse_version == 2015)
return;
*/
for (i = 0; i < num_nnchead_kw; i++) {
ecl_file_view_type * lgr_view = ecl_file_alloc_global_blockview(ecl_file , NNCHEAD_KW , i);
ecl_kw_type * nnchead_kw = ecl_file_view_iget_named_kw(lgr_view, NNCHEAD_KW, 0);
int lgr_nr = ecl_kw_iget_int(nnchead_kw, NNCHEAD_LGR_INDEX);
if (ecl_file_view_has_kw(lgr_view , NNC1_KW)) {
const ecl_kw_type * nnc1 = ecl_file_view_iget_named_kw(lgr_view, NNC1_KW, 0);
const ecl_kw_type * nnc2 = ecl_file_view_iget_named_kw(lgr_view, NNC2_KW, 0);
{
ecl_grid_type * grid = (lgr_nr > 0) ? ecl_grid_get_lgr_from_lgr_nr(main_grid, lgr_nr) : main_grid;
ecl_grid_init_nnc_cells(grid, grid, nnc1, nnc2);
}
}
if (ecl_file_view_has_kw(lgr_view , NNCL_KW)) {
const ecl_kw_type * nncl = ecl_file_view_iget_named_kw(lgr_view, NNCL_KW, 0);
const ecl_kw_type * nncg = ecl_file_view_iget_named_kw(lgr_view, NNCG_KW, 0);
{
ecl_grid_type * grid = (lgr_nr > 0) ? ecl_grid_get_lgr_from_lgr_nr(main_grid, lgr_nr) : main_grid;
ecl_grid_init_nnc_cells(main_grid, grid , nncg, nncl);
}
}
ecl_file_view_free( lgr_view );
}
}
/*
This function reads the non-neighbour connection data for
amalgamated LGRs (that is, non-neighbour connections between two
LGRs) and initializes the grid structure with the nnc data.
*/
static void ecl_grid_init_nnc_amalgamated(ecl_grid_type * main_grid, ecl_file_type * ecl_file) {
int num_nncheada_kw = ecl_file_get_num_named_kw( ecl_file , NNCHEADA_KW );
int i;
for (i = 0; i < num_nncheada_kw; i++) {
ecl_kw_type * nncheada_kw = ecl_file_iget_named_kw( ecl_file , NNCHEADA_KW , i);
int lgr_nr1 = ecl_kw_iget_int(nncheada_kw, NNCHEADA_ILOC1_INDEX);
int lgr_nr2 = ecl_kw_iget_int(nncheada_kw, NNCHEADA_ILOC2_INDEX);
ecl_grid_type * lgr_grid1 = ecl_grid_get_lgr_from_lgr_nr(main_grid, lgr_nr1);
ecl_grid_type * lgr_grid2 = ecl_grid_get_lgr_from_lgr_nr(main_grid, lgr_nr2);
ecl_kw_type * nna1_kw = ecl_file_iget_named_kw( ecl_file , NNA1_KW , i);
ecl_kw_type * nna2_kw = ecl_file_iget_named_kw( ecl_file , NNA2_KW , i);
ecl_grid_init_nnc_cells( lgr_grid1 ,lgr_grid2, nna1_kw, nna2_kw);
}
}
/**
Creating a grid based on a EGRID file is a three step process:
1. Load the file and extracte the keywords.
2. Call xx_alloc_GRDECL_kw__() to build grid based on keywords.
3. Call xx_alloc_GRDECL_data__() to build the grid based on keyword data.
The point is that external scope can create grid based on both a
list of keywords, and actual data - in addition to the normal
loading of a full file. Currently the 'shortcuts'
ecl_grid_alloc_grdecl_kw() and ecl_grid_alloc_grdecl_data() do not
support LGRs.
*/
static ecl_grid_type * ecl_grid_alloc_EGRID__( ecl_grid_type * main_grid , const ecl_file_type * ecl_file , int grid_nr, bool apply_mapaxes, const int * ext_actnum) {
ecl_kw_type * gridhead_kw = ecl_file_iget_named_kw( ecl_file , GRIDHEAD_KW , grid_nr);
ecl_kw_type * zcorn_kw = ecl_file_iget_named_kw( ecl_file , ZCORN_KW , grid_nr);
ecl_kw_type * coord_kw = ecl_file_iget_named_kw( ecl_file , COORD_KW , grid_nr);
ecl_kw_type * corsnum_kw = NULL;
ecl_kw_type * actnum_kw = NULL;
ecl_kw_type * gridunit_kw = NULL;
ecl_kw_type * mapaxes_kw = NULL;
int dualp_flag;
int eclipse_version;
if (grid_nr == 0) {
ecl_kw_type * filehead_kw = ecl_file_iget_named_kw( ecl_file , FILEHEAD_KW , grid_nr);
dualp_flag = ecl_kw_iget_int( filehead_kw , FILEHEAD_DUALP_INDEX );
eclipse_version = ecl_kw_iget_int( filehead_kw, FILEHEAD_YEAR_INDEX);
} else{
dualp_flag = main_grid->dualp_flag;
eclipse_version = main_grid->eclipse_version;
}
// If ACTNUM and ext_actnum are not present - that is is interpreted as all active.
const int * actnum_data;
std::vector<int> actnum_product;
if (ecl_file_get_num_named_kw(ecl_file , ACTNUM_KW) > grid_nr) {
actnum_kw = ecl_file_iget_named_kw( ecl_file , ACTNUM_KW , grid_nr);
actnum_data = ecl_kw_get_int_ptr(actnum_kw);
if (ext_actnum) {
int size = ecl_kw_get_size(actnum_kw);
actnum_product.resize(size);
for (int i = 0; i < size; i++)
actnum_product[i] = actnum_data[i] * ext_actnum[i];
actnum_data = actnum_product.data();
}
}
else
actnum_data = ext_actnum;
if (grid_nr == 0) {
/* MAPAXES and COARSENING only apply to the global grid. */
if (ecl_file_has_kw( ecl_file , MAPAXES_KW))
mapaxes_kw = ecl_file_iget_named_kw( ecl_file , MAPAXES_KW , 0);
if (ecl_file_has_kw( ecl_file , CORSNUM_KW))
corsnum_kw = ecl_file_iget_named_kw( ecl_file , CORSNUM_KW , 0);
if (ecl_file_has_kw(ecl_file, GRIDUNIT_KW))
gridunit_kw = ecl_file_iget_named_kw( ecl_file, GRIDUNIT_KW, 0);
}
{
ecl_grid_type * ecl_grid = ecl_grid_alloc_GRDECL_kw__( main_grid ,
dualp_flag ,
apply_mapaxes,
gridhead_kw ,
zcorn_kw ,
coord_kw ,
gridunit_kw,
mapaxes_kw ,
corsnum_kw,
actnum_data);
if (ECL_GRID_MAINGRID_LGR_NR != grid_nr) ecl_grid_set_lgr_name_EGRID(ecl_grid , ecl_file , grid_nr);
ecl_grid->eclipse_version = eclipse_version;
return ecl_grid;
}
}
static ecl_grid_type * ecl_grid_alloc_GRID_all_grids(const char * filename) {
util_abort("%s .GRID files - %s - not supported \n", __func__ , filename);
return NULL;
}
static ecl_grid_type * ecl_grid_alloc_EGRID_all_grids(const char * grid_file, bool apply_mapaxes, const int * ext_actnum) {
ecl_file_enum file_type;
file_type = ecl_util_get_file_type(grid_file , NULL , NULL);
if (file_type != ECL_EGRID_FILE)
util_abort("%s: %s wrong file type - expected .EGRID file - aborting \n",__func__ , grid_file);
{
ecl_file_type * ecl_file = ecl_file_open( grid_file , 0);
if (ecl_file) {
int num_grid = ecl_file_get_num_named_kw( ecl_file , GRIDHEAD_KW );
ecl_grid_type * main_grid = ecl_grid_alloc_EGRID__( NULL , ecl_file , 0 , apply_mapaxes, ext_actnum );
int grid_nr;
for ( grid_nr = 1; grid_nr < num_grid; grid_nr++) {
// The apply_mapaxes argument is ignored for LGR -
// it inherits from parent anyway.
ecl_grid_type * lgr_grid = ecl_grid_alloc_EGRID__( main_grid , ecl_file , grid_nr , false, NULL );
ecl_grid_add_lgr( main_grid , lgr_grid );
{
ecl_grid_type * host_grid;
ecl_kw_type * hostnum_kw = ecl_file_iget_named_kw( ecl_file , HOSTNUM_KW , grid_nr - 1);
if (lgr_grid->parent_name == NULL)
host_grid = main_grid;
else
host_grid = ecl_grid_get_lgr( main_grid , lgr_grid->parent_name );
ecl_grid_install_lgr_EGRID( host_grid , lgr_grid , ecl_kw_get_int_ptr( hostnum_kw) );
}
}
main_grid->name = util_alloc_string_copy( grid_file );
ecl_grid_init_nnc(main_grid, ecl_file);
ecl_grid_init_nnc_amalgamated(main_grid, ecl_file);
ecl_file_close( ecl_file );
return main_grid;
} else
return NULL;
}
}
ecl_grid_type * ecl_grid_alloc_EGRID(const char * grid_file, bool apply_mapaxes) {
return ecl_grid_alloc_EGRID_all_grids(grid_file, apply_mapaxes, NULL);
}
static ecl_grid_type * ecl_grid_alloc_GRID_data__(ecl_grid_type * global_grid , int num_coords , ert_ecl_unit_enum unit_system, int dualp_flag , bool apply_mapaxes, int nx, int ny , int nz , int grid_nr , int coords_size , int ** coords , float ** corners , const float * mapaxes) {
if (dualp_flag != FILEHEAD_SINGLE_POROSITY)
nz = nz / 2;
{
ecl_grid_type * grid = ecl_grid_alloc_empty( global_grid , unit_system, dualp_flag , nx , ny , nz , grid_nr, false);
if (grid) {
if (mapaxes != NULL)
ecl_grid_init_mapaxes( grid , apply_mapaxes , mapaxes);
{
int index;
for ( index=0; index < num_coords; index++)
ecl_grid_set_cell_GRID(grid , coords_size , coords[index] , corners[index]);
}
ecl_grid_init_coarse_cells( grid );
ecl_grid_update_index( grid );
ecl_grid_taint_cells( grid );
}
return grid;
}
}
/*
coords[num_coords][coords_size]
corners[num_coords][24]
*/
ecl_grid_type * ecl_grid_alloc_GRID_data(int num_coords , int nx , int ny , int nz , int coords_size , int ** coords , float ** corners , bool apply_mapaxes, const float * mapaxes) {
ert_ecl_unit_enum unit_system = ECL_METRIC_UNITS;
return ecl_grid_alloc_GRID_data__( NULL ,
num_coords ,
unit_system,
FILEHEAD_SINGLE_POROSITY , /* Does currently not support to determine dualp_flag from inspection. */
apply_mapaxes,
nx , ny , nz , 0 , coords_size , coords , corners , mapaxes);
}
static int ecl_grid_dual_porosity_GRID_check( ecl_file_type * ecl_file ) {
ecl_kw_type * dimens_kw = ecl_file_iget_named_kw( ecl_file , DIMENS_KW , 0);
int nx = ecl_kw_iget_int(dimens_kw , DIMENS_NX_INDEX);
int ny = ecl_kw_iget_int(dimens_kw , DIMENS_NY_INDEX);
int nz = ecl_kw_iget_int(dimens_kw , DIMENS_NZ_INDEX);
if ((nz % 2) == 1)
return FILEHEAD_SINGLE_POROSITY;
else {
int dualp_flag = FILEHEAD_DUAL_POROSITY;
int num_corners = ecl_file_get_num_named_kw( ecl_file , CORNERS_KW );
int matrix_index = 0;
int fracture_index;
ecl_kw_type * matrix_kw;
ecl_kw_type * fracture_kw;
if (num_corners > nx*ny*nz)
fracture_index = nx*ny*nz/2;
else
fracture_index = num_corners / 2;
while (true) {
matrix_kw = ecl_file_iget_named_kw( ecl_file, CORNERS_KW , matrix_index );
fracture_kw = ecl_file_iget_named_kw( ecl_file, CORNERS_KW , fracture_index );
if (!ecl_kw_equal(matrix_kw , fracture_kw)) {
dualp_flag = FILEHEAD_SINGLE_POROSITY;
break;
}
matrix_index++;
fracture_index++;
if (fracture_index == nx*ny*nz)
break;
}
return dualp_flag;
}
}
static ecl_grid_type * ecl_grid_alloc_GRID__(ecl_grid_type * global_grid , const ecl_file_type * ecl_file , int cell_offset , int grid_nr, int dualp_flag, bool apply_mapaxes) {
int nx,ny,nz;
const float * mapaxes_data = NULL;
ecl_grid_type * grid;
ert_ecl_unit_enum unit_system = ECL_METRIC_UNITS;
// 1: Fetching header data from the DIMENS keyword.
{
ecl_kw_type * dimens_kw = ecl_file_iget_named_kw( ecl_file , DIMENS_KW , grid_nr);
nx = ecl_kw_iget_int(dimens_kw , DIMENS_NX_INDEX);
ny = ecl_kw_iget_int(dimens_kw , DIMENS_NY_INDEX);
nz = ecl_kw_iget_int(dimens_kw , DIMENS_NZ_INDEX);
}
// 2: Fetching the mapaxes data from the MAPAXES keyword; the
// keyword is optional, and is only applicable to the global grid.
{
if ((grid_nr == 0) && (ecl_file_has_kw( ecl_file , MAPAXES_KW))) {
const ecl_kw_type * mapaxes_kw = ecl_file_iget_named_kw( ecl_file , MAPAXES_KW , 0);
mapaxes_data = ecl_grid_get_mapaxes_from_kw__(mapaxes_kw);
}
}
if ((grid_nr == 0) && (ecl_file_has_kw( ecl_file , GRIDUNIT_KW)))
unit_system = ecl_grid_check_unit_system( ecl_file_iget_named_kw(ecl_file, GRIDUNIT_KW, 0));
/*
The number of COORDS/CORNERS blocks depends on the GRIDFILE option
used in the ECLIPSE datafile when asking ECLIPSE to save a file in
GRID format. If the GRIDFILE option is set to 1; only active
cells are stored. In that case:
1. We have absolutely no real-world coordinate information about
the inactive cells.
2. The GRID file only contains the global information, and can
not contain LGR information.
If the GRIDFILE option is set to 2 ECLIPSE will save coordinate
information for all the cells in the grid, and also lgrs. To
determine how many coords keywords we should read we use the
following heuristics:
1. If global_grid != NULL : this is an lgr, and num_coords =
nx*ny*nz;
2. If global_grid == NULL : this is the global grid. We check
how many COORDS keywords there are in the file:
a) If there are >= nx*ny*nz keywords we set num_coords ==
nx*ny*nz.
b) If there are < nx*ny*nz keywords we set num_coords ==
#COORDS keywords in the file.
Possible LGR cells will follow *AFTER* the first nx*ny*nz cells;
the loop stops at nx*ny*nz. Additionally the LGR cells should be
discarded (by checking coords[5]) in the ecl_grid_set_cell_GRID()
function.
*/
{
int num_coords;
if (global_grid == NULL) {
/* This is the main grid - can be both nactive or nx*ny*nz coord elements. */
int num_coords_kw = ecl_file_get_num_named_kw( ecl_file , COORDS_KW);
if (num_coords_kw >= nx*ny*nz)
num_coords = nx*ny*nz;
else
num_coords = num_coords_kw;
} else
/* This is an lgr - always nx*ny*nz elements. */
num_coords = nx*ny*nz;
// 3: Fetching the main chunk of cell data from the COORDS and
// CORNERS keywords.
{
int coords_size = -1;
int index;
int ** coords = (int **)util_calloc( num_coords , sizeof * coords );
float ** corners = (float**)util_calloc( num_coords , sizeof * corners );
for (index = 0; index < num_coords; index++) {
const ecl_kw_type * coords_kw = ecl_file_iget_named_kw(ecl_file , COORDS_KW , index + cell_offset);
const ecl_kw_type * corners_kw = ecl_file_iget_named_kw(ecl_file , CORNERS_KW , index + cell_offset);
coords[index] = ecl_kw_get_int_ptr(coords_kw);
corners[index] = ecl_kw_get_float_ptr(corners_kw);
coords_size = ecl_kw_get_size( coords_kw );
}
// Create the grid:
grid = ecl_grid_alloc_GRID_data__( global_grid , num_coords , unit_system, dualp_flag , apply_mapaxes, nx , ny , nz , grid_nr , coords_size , coords , corners , mapaxes_data );
free( coords );
free( corners );
}
}
if (grid_nr > 0) ecl_grid_set_lgr_name_GRID(grid , ecl_file , grid_nr);
return grid;
}
ecl_grid_type * ecl_grid_alloc_GRID(const char * grid_file, bool apply_mapaxes) {
ecl_file_enum file_type;
file_type = ecl_util_get_file_type(grid_file , NULL , NULL);
if (file_type != ECL_GRID_FILE)
util_abort("%s: %s wrong file type - expected .GRID file - aborting \n",__func__ , grid_file);
{
int cell_offset = 0;
ecl_file_type * ecl_file = ecl_file_open( grid_file , 0);
int num_grid = ecl_file_get_num_named_kw( ecl_file , DIMENS_KW);
ecl_grid_type * main_grid;
int grid_nr;
int dualp_flag;
dualp_flag = ecl_grid_dual_porosity_GRID_check( ecl_file );
main_grid = ecl_grid_alloc_GRID__(NULL , ecl_file , cell_offset , 0,dualp_flag , apply_mapaxes);
cell_offset += ecl_grid_get_global_size( main_grid );
for (grid_nr = 1; grid_nr < num_grid; grid_nr++) {
ecl_grid_type * lgr_grid = ecl_grid_alloc_GRID__(main_grid , ecl_file , cell_offset , grid_nr , dualp_flag, false);
cell_offset += ecl_grid_get_global_size( lgr_grid );
ecl_grid_add_lgr( main_grid , lgr_grid );
{
ecl_grid_type * host_grid;
if (lgr_grid->parent_name == NULL)
host_grid = main_grid;
else
host_grid = ecl_grid_get_lgr( main_grid , lgr_grid->parent_name );
ecl_grid_install_lgr_GRID( host_grid , lgr_grid );
}
}
main_grid->name = util_alloc_string_copy( grid_file );
ecl_file_close( ecl_file );
return main_grid;
}
}
/**
This function will allocate a new regular grid with dimensions nx x
ny x nz. The cells in the grid are spanned by the the three unit
vectors ivec, jvec and kvec.
The actnum argument should be a pointer to an integer array of
length nx*ny*nz where actnum[i + j*nx + k*nx*ny] == 1 for active
cells and 0 for inactive cells. The actnum array can be NULL, in
which case all cells will be active.
*/
ecl_grid_type * ecl_grid_alloc_regular( int nx, int ny , int nz , const double * ivec, const double * jvec , const double * kvec , const int * actnum) {
ert_ecl_unit_enum unit_system = ECL_METRIC_UNITS;
ecl_grid_type * grid = ecl_grid_alloc_empty(NULL , unit_system, FILEHEAD_SINGLE_POROSITY , nx , ny , nz , 0, true);
if (grid) {
const double grid_offset[3] = {0,0,0};
int k,j,i;
for (k=0; k < nz; k++) {
for (j=0; j< ny; j++) {
for (i=0; i < nx; i++) {
int global_index = i + j*nx + k*nx*ny;
double offset[3] = {
grid_offset[0] + i*ivec[0] + j*jvec[0] + k*kvec[0],
grid_offset[1] + i*ivec[1] + j*jvec[1] + k*kvec[1],
grid_offset[2] + i*ivec[2] + j*jvec[2] + k*kvec[2]
};
ecl_cell_type * cell = ecl_grid_get_cell(grid , global_index );
ecl_cell_init_regular( cell , offset , i,j,k,global_index , ivec , jvec , kvec , actnum );
}
}
}
ecl_grid_update_index( grid );
}
return grid;
}
/**
This function will allocate a new rectangular grid with dimensions
nx x ny x nz. The cells in the grid are rectangular with dimensions
dx x dy x dz.
The actnum argument should be a pointer to an integer array of
length nx*ny*nz where actnum[i + j*nx + k*nx*ny] == 1 for active
cells and 0 for inactive cells. The actnum array can be NULL, in
which case all cells will be active.
*/
ecl_grid_type * ecl_grid_alloc_rectangular( int nx , int ny , int nz , double dx , double dy , double dz , const int * actnum) {
const double ivec[3] = {dx , 0 , 0};
const double jvec[3] = {0 , dy , 0};
const double kvec[3] = {0 , 0 , dz};
return ecl_grid_alloc_regular( nx , ny , nz , ivec , jvec , kvec , actnum);
}
/**
This function will allocate a new grid with logical dimensions nx x
ny x nz. The cells in the grid are spanned by the dxv, dyv and dzv
vectors which are of size nx, ny and nz and specify the thickness
of a cell in x, y, and in z direction.
The actnum argument is a pointer to an integer array of length
nx*ny*nz where actnum[i + j*nx + k*nx*ny] == 1 for active cells and
0 for inactive cells. The actnum array can be NULL, in which case
all cells will be active. */
ecl_grid_type * ecl_grid_alloc_dxv_dyv_dzv( int nx, int ny , int nz , const double * dxv , const double * dyv , const double * dzv , const int * actnum)
{
ert_ecl_unit_enum unit_system = ECL_METRIC_UNITS;
ecl_grid_type* grid = ecl_grid_alloc_empty(NULL,
unit_system,
FILEHEAD_SINGLE_POROSITY,
nx, ny, nz,
/*lgr_nr=*/0, /*init_valid=*/true);
if (grid) {
double ivec[3] = { 0, 0, 0 };
double jvec[3] = { 0, 0, 0 };
double kvec[3] = { 0, 0, 0 };
const double grid_offset[3] = {0,0,0};
double offset[3];
int i, j, k;
offset[2] = grid_offset[2];
for (k=0; k < nz; k++) {
kvec[2] = dzv[k];
offset[1] = grid_offset[1];
for (j=0; j < ny; j++) {
jvec[1] = dyv[j];
offset[0] = grid_offset[0];
for (i=0; i < nx; i++) {
int global_index = i + j*nx + k*nx*ny;
ecl_cell_type* cell = ecl_grid_get_cell(grid, global_index);
ivec[0] = dxv[i];
ecl_cell_init_regular(cell, offset,
i,j,k,global_index,
ivec,jvec,kvec,
actnum);
offset[0] += dxv[i];
}
offset[1] += dyv[j];
}
offset[2] += dzv[k];
}
ecl_grid_update_index(grid);
}
return grid;
}
ecl_grid_type * ecl_grid_alloc_dxv_dyv_dzv_depthz( int nx, int ny , int nz , const double * dxv , const double * dyv , const double * dzv , const double * depthz , const int * actnum)
{
ert_ecl_unit_enum unit_system = ECL_METRIC_UNITS;
ecl_grid_type* grid = ecl_grid_alloc_empty(NULL,
unit_system,
FILEHEAD_SINGLE_POROSITY,
nx, ny, nz,
/*lgr_nr=*/0, /*init_valid=*/true);
/* First layer - where the DEPTHZ keyword applies. */
if (grid) {
int i,j;
int k = 0;
double y0 = 0;
for (j=0; j < ny; j++) {
double x0 = 0;
for (i = 0; i < nx; i++) {
int global_index = i + j*nx + k*nx*ny;
ecl_cell_type* cell = ecl_grid_get_cell(grid, global_index);
double z0 = depthz[ i + j*(nx + 1)];
double z1 = depthz[ i + 1 + j*(nx + 1)];
double z2 = depthz[ i + (j + 1)*(nx + 1)];
double z3 = depthz[ i + 1 + (j + 1)*(nx + 1)];
point_set(&cell->corner_list[0] , x0 , y0 , z0);
point_set(&cell->corner_list[1] , x0 + dxv[i] , y0 , z1);
point_set(&cell->corner_list[2] , x0 , y0 + dyv[j] , z2);
point_set(&cell->corner_list[3] , x0 + dxv[i] , y0 + dyv[j] , z3);
{
int c;
for (c = 0; c < 4; c++) {
cell->corner_list[c + 4] = cell->corner_list[c];
point_shift(&cell->corner_list[c + 4] , 0 , 0 , dzv[0]);
}
}
x0 += dxv[i];
}
y0 += dyv[j];
}
}
/* Remaining layers */
if (grid) {
int i,j,k;
for (k=1; k < nz; k++) {
for (j=0; j <ny; j++) {
for (i=0; i < nx; i++) {
int g2 = i + j*nx + k*nx*ny;
int g1 = i + j*nx + (k - 1)*nx*ny;
ecl_cell_type* cell2 = ecl_grid_get_cell(grid, g2);
ecl_cell_type* cell1 = ecl_grid_get_cell(grid, g1);
int c;
for (c = 0; c < 4; c++) {
cell2->corner_list[c] = cell1->corner_list[c + 4];
cell2->corner_list[c + 4] = cell1->corner_list[c + 4];
point_shift( &cell2->corner_list[c + 4] , 0 , 0 , dzv[k]);
}
}
}
}
}
if (grid) {
int i,j,k;
for (k=0; k < nz; k++) {
for (j=0; j <ny; j++) {
for (i=0; i < nx; i++) {
int global_index = i + j*nx + k*nx*ny;
ecl_cell_type* cell = ecl_grid_get_cell(grid, global_index);
if (actnum)
cell->active = actnum[global_index];
else
cell->active = CELL_ACTIVE;
}
}
}
}
if (grid)
ecl_grid_update_index(grid);
return grid;
}
/**
This is a really broken function which is here only to support
creating rectangualar grids from OPM. The vectors dx,dy,dz and tops
are all of length nx*ny*nz. In principle all values of these four
arrays are independent - but taking that all out will create some
really weird looking grids. For physical correctness there should
be many constraints among the different values.
The x and y position of a cell is found by adding the increments
from dx and dy, whereas the vertical position is read dircetly out
of the tops array.
The ECLIPSE input format only requires size(dz) >= nx*ny and the
same for tops. The remaining layers will then be extrapolated; such
trickery must be performed before calling this function.
*/
ecl_grid_type * ecl_grid_alloc_dx_dy_dz_tops( int nx, int ny , int nz , const double * dx , const double * dy , const double * dz , const double * tops , const int * actnum) {
ert_ecl_unit_enum unit_system = ECL_METRIC_UNITS;
ecl_grid_type* grid = ecl_grid_alloc_empty(NULL,
unit_system,
FILEHEAD_SINGLE_POROSITY,
nx, ny, nz,
0, true);
if (grid) {
int i, j, k;
double * y0 = (double*)util_calloc( nx, sizeof * y0 );
for (k=0; k < nz; k++) {
for (i=0; i < nx; i++) {
y0[i] = 0;
}
for (j=0; j < ny; j++) {
double x0 = 0;
for (i=0; i < nx; i++) {
int g = i + j*nx + k*nx*ny;
ecl_cell_type* cell = ecl_grid_get_cell(grid, g);
double z0 = tops[ g ];
point_set(&cell->corner_list[0] , x0 , y0[i] , z0);
point_set(&cell->corner_list[1] , x0 + dx[g] , y0[i] , z0);
point_set(&cell->corner_list[2] , x0 , y0[i] + dy[g] , z0);
point_set(&cell->corner_list[3] , x0 + dx[g] , y0[i] + dy[g] , z0);
point_set(&cell->corner_list[4] , x0 , y0[i] , z0 + dz[g]);
point_set(&cell->corner_list[5] , x0 + dx[g] , y0[i] , z0 + dz[g]);
point_set(&cell->corner_list[6] , x0 , y0[i] + dy[g] , z0 + dz[g]);
point_set(&cell->corner_list[7] , x0 + dx[g] , y0[i] + dy[g] , z0 + dz[g]);
x0 += dx[g];
y0[i] += dy[g];
if (actnum != NULL)
cell->active = actnum[g];
else
cell->active = CELL_ACTIVE;
}
}
}
free( y0 );
ecl_grid_update_index(grid);
}
return grid;
}
/**
This function will allocate a ecl_grid instance. As input it takes
a filename, which can be both a GRID file and an EGRID file (both
formatted and unformatted).
When allocating based on an EGRID file the COORDS, ZCORN and ACTNUM
keywords are extracted, and the ecl_grid_alloc_GRDECL() function is
called with these keywords. This function can be called directly
with these keywords.
*/
ecl_grid_type * ecl_grid_alloc__(const char * grid_file , bool apply_mapaxes) {
ecl_file_enum file_type;
ecl_grid_type * ecl_grid = NULL;
file_type = ecl_util_get_file_type(grid_file , NULL , NULL);
if (file_type == ECL_GRID_FILE)
ecl_grid = ecl_grid_alloc_GRID(grid_file, apply_mapaxes);
else if (file_type == ECL_EGRID_FILE)
ecl_grid = ecl_grid_alloc_EGRID(grid_file, apply_mapaxes);
else
util_abort("%s must have .GRID or .EGRID file - %s not recognized \n", __func__ , grid_file);
return ecl_grid;
}
ecl_grid_type * ecl_grid_alloc(const char * grid_file ) {
bool apply_mapaxes = true;
return ecl_grid_alloc__( grid_file , apply_mapaxes );
}
// This function is used to override use of the keyword ACTNUM from the EGRID file.
// ext_actnum must have size equal to the number of cells in the main grid
// if ext_actnum = NULL, actnum is taken from file, otherwise ext_actnums
// determines which cells are active.
ecl_grid_type * ecl_grid_alloc_ext_actnum(const char * grid_file, const int * ext_actnum) {
ecl_file_enum file_type = ecl_util_get_file_type(grid_file , NULL , NULL);
if (file_type == ECL_EGRID_FILE)
return ecl_grid_alloc_EGRID_all_grids(grid_file, true, ext_actnum);
else if (file_type == ECL_GRID_FILE)
ecl_grid_alloc_GRID_all_grids(grid_file);
else
util_abort("%s must have .EGRID file - %s not recognized \n", __func__ , grid_file);
return NULL;
}
static void ecl_grid_file_nactive_dims( fortio_type * data_fortio , int * dims) {
if (data_fortio) {
if (ecl_kw_fseek_kw( INTEHEAD_KW , false , false , data_fortio )) {
ecl_kw_type * intehead_kw = ecl_kw_fread_alloc( data_fortio );
dims[3] = ecl_kw_iget_int( intehead_kw , INTEHEAD_NACTIVE_INDEX );
ecl_kw_free( intehead_kw );
}
}
}
static bool ecl_grid_file_EGRID_dims( fortio_type * grid_fortio , fortio_type * data_fortio , int * dims ) {
if (ecl_kw_fseek_kw( GRIDHEAD_KW , false , false , grid_fortio)) {
{
ecl_kw_type * gridhead_kw = ecl_kw_fread_alloc( grid_fortio );
dims[0] = ecl_kw_iget_int( gridhead_kw , GRIDHEAD_NX_INDEX );
dims[1] = ecl_kw_iget_int( gridhead_kw , GRIDHEAD_NY_INDEX );
dims[2] = ecl_kw_iget_int( gridhead_kw , GRIDHEAD_NZ_INDEX );
ecl_kw_free( gridhead_kw );
}
ecl_grid_file_nactive_dims( data_fortio , dims );
return true;
} else
return false;
}
static bool ecl_grid_file_GRID_dims( fortio_type * grid_fortio , fortio_type * data_fortio , int * dims ) {
if (ecl_kw_fseek_kw( DIMENS_KW , false , false , grid_fortio)) {
{
ecl_kw_type * dimens_kw = ecl_kw_fread_alloc( grid_fortio );
dims[0] = ecl_kw_iget_int( dimens_kw , DIMENS_NX_INDEX );
dims[1] = ecl_kw_iget_int( dimens_kw , DIMENS_NY_INDEX );
dims[2] = ecl_kw_iget_int( dimens_kw , DIMENS_NZ_INDEX );
ecl_kw_free( dimens_kw );
}
ecl_grid_file_nactive_dims( data_fortio , dims );
return true;
} else
return false;
}
/**
Will check the grid dimensions from the input grid file
@grid_filename; the input file must be a GRID/EGRID file. On exit
the dims array will be filled as:
dims[0] = nx;
dims[1] = ny;
dims[2] = nz;
Optionally you can in addition supply the name of a restart or INIT
file in the second file argument - if-and-only-if, that filename
points to an existing file the fourth element in the dims array
will be set as:
dims[3] = nactive;
The function as a whole will return true if the grid dimensions
(nx,ny,nz) are successfully set. If the dimensions are not set the
dims vector is not touched.
*/
bool ecl_grid_file_dims( const char * grid_filename , const char * init_restart_filename , int * dims) {
bool input_file_OK = false;
bool grid_fmt_file;
ecl_file_enum grid_file_type = ecl_util_get_file_type( grid_filename , &grid_fmt_file , NULL );
if ((grid_file_type == ECL_GRID_FILE) || (grid_file_type == ECL_EGRID_FILE)) {
fortio_type * grid_fortio = fortio_open_reader( grid_filename , grid_fmt_file , ECL_ENDIAN_FLIP );
if (grid_fortio) {
fortio_type * data_fortio = NULL;
bool data_fmt_file;
if (init_restart_filename) {
ecl_util_get_file_type( init_restart_filename , &data_fmt_file , NULL );
data_fortio = fortio_open_reader( init_restart_filename , data_fmt_file , ECL_ENDIAN_FLIP );
}
if (grid_file_type == ECL_GRID_FILE)
input_file_OK = ecl_grid_file_GRID_dims( grid_fortio , data_fortio , dims );
else
input_file_OK = ecl_grid_file_EGRID_dims( grid_fortio , data_fortio , dims );
if (data_fortio)
fortio_fclose( data_fortio );
fortio_fclose( grid_fortio );
}
}
return input_file_OK;
}
/**
Will load the grid corresponding to the input @input_case;
depending on the value of @input_case many different paths will be
tried:
1 case_input - an existing GRID/EGRID file: Just load the file -
with no further ado.
2 case_input - an existing ECLIPSE file which is not a grid file;
if it has definite formatted/unformatted status look only for
those GRID/EGRID with the same formatted/unformatted status.
3 case_input is only an ECLIPSE base, look for
formatted/unformatted files with the correct basename.
For cases 2 & 3 the function will look for files in the following order:
BASE.EGRID BASE.GRID BASE.FEGRID BASE.FGRID
and stop with the first success. Will return NULL if no GRID/EGRID
files can be found.
*/
char * ecl_grid_alloc_case_filename( const char * case_input ) {
ecl_file_enum file_type;
bool fmt_file;
file_type = ecl_util_get_file_type( case_input , &fmt_file , NULL);
if (file_type == ECL_GRID_FILE)
return util_alloc_string_copy( case_input ); /* Case 1 */
else if (file_type == ECL_EGRID_FILE)
return util_alloc_string_copy( case_input ); /* Case 1 */
else {
char * grid_file = NULL;
char * path;
char * basename;
util_alloc_file_components( case_input , &path , &basename , NULL);
if ((file_type == ECL_OTHER_FILE) || (file_type == ECL_DATA_FILE)) { /* Case 3 - only basename recognized */
char * EGRID = ecl_util_alloc_filename( path , basename , ECL_EGRID_FILE , false , -1);
char * GRID = ecl_util_alloc_filename( path , basename , ECL_GRID_FILE , false , -1);
char * FEGRID = ecl_util_alloc_filename( path , basename , ECL_EGRID_FILE , true , -1);
char * FGRID = ecl_util_alloc_filename( path , basename , ECL_GRID_FILE , true , -1);
if (util_file_exists( EGRID ))
grid_file = util_alloc_string_copy( EGRID );
else if (util_file_exists( GRID ))
grid_file = util_alloc_string_copy( GRID );
else if (util_file_exists( FEGRID ))
grid_file = util_alloc_string_copy( FEGRID );
else if (util_file_exists( FGRID ))
grid_file = util_alloc_string_copy( FGRID );
/*
else: could not find a GRID/EGRID.
*/
free( EGRID );
free( FEGRID );
free( GRID );
free( FGRID );
} else { /* Case 2 - we know the formatted / unformatted status. */
char * EGRID = ecl_util_alloc_filename( path , basename , ECL_EGRID_FILE , fmt_file , -1);
char * GRID = ecl_util_alloc_filename( path , basename , ECL_GRID_FILE , fmt_file , -1);
if (util_file_exists( EGRID ))
grid_file = util_alloc_string_copy( EGRID );
else if (util_file_exists( GRID ))
grid_file = util_alloc_string_copy( GRID );
free( EGRID );
free( GRID );
}
return grid_file;
}
}
ecl_grid_type * ecl_grid_load_case__( const char * case_input , bool apply_mapaxes) {
ecl_grid_type * ecl_grid = NULL;
char * grid_file = ecl_grid_alloc_case_filename( case_input );
if (grid_file != NULL) {
if (util_file_exists( grid_file ))
ecl_grid = ecl_grid_alloc__( grid_file , apply_mapaxes);
free( grid_file );
}
return ecl_grid;
}
ecl_grid_type * ecl_grid_load_case( const char * case_input ) {
bool apply_mapaxes = true;
return ecl_grid_load_case__( case_input , apply_mapaxes );
}
bool ecl_grid_exists( const char * case_input ) {
bool exists = false;
char * grid_file = ecl_grid_alloc_case_filename( case_input );
if (grid_file != NULL) {
exists = true;
free( grid_file );
}
return exists;
}
static bool ecl_grid_compare_coarse_cells(const ecl_grid_type * g1 , const ecl_grid_type * g2, bool verbose) {
if (vector_get_size( g1->coarse_cells ) == vector_get_size( g2->coarse_cells )) {
bool equal = true;
int c;
for (c = 0; c < vector_get_size( g1->coarse_cells ); c++) {
const ecl_coarse_cell_type * coarse_cell1 = (const ecl_coarse_cell_type*)vector_iget_const( g1->coarse_cells , c);
const ecl_coarse_cell_type * coarse_cell2 = (const ecl_coarse_cell_type*)vector_iget_const( g2->coarse_cells , c);
equal = ecl_coarse_cell_equal( coarse_cell1 , coarse_cell2 );
if (!equal)
if (verbose) fprintf(stderr,"Difference in coarse cell:%d \n",c );
}
return equal;
} else
return false;
}
static bool ecl_grid_compare_cells(const ecl_grid_type * g1 , const ecl_grid_type * g2, bool include_nnc , bool verbose) {
int g;
bool equal = true;
for (g = 0; g < g1->size; g++) {
bool this_equal = true;
ecl_cell_type *c1 = ecl_grid_get_cell( g1 , g );
ecl_cell_type *c2 = ecl_grid_get_cell( g2 , g );
ecl_cell_compare(c1 , c2 , include_nnc , &this_equal);
if (!this_equal) {
if (verbose) {
int i,j,k;
ecl_grid_get_ijk1( g1 , g , &i , &j , &k);
printf("Difference in cell: %d : %d,%d,%d nnc_equal:%d Volume:%g \n",g,i,j,k , nnc_info_equal( c1->nnc_info , c2->nnc_info) , ecl_cell_get_volume( c1 ));
printf("-----------------------------------------------------------------\n");
ecl_cell_dump_ascii( c1 , i , j , k , stdout , NULL);
printf("-----------------------------------------------------------------\n");
ecl_cell_dump_ascii( c2 , i , j , k , stdout , NULL );
printf("-----------------------------------------------------------------\n");
}
equal = false;
break;
}
}
return equal;
}
static bool ecl_grid_compare_index(const ecl_grid_type * g1 , const ecl_grid_type * g2, bool verbose) {
bool equal = true;
if (g1->total_active != g2->total_active) {
if (verbose)
fprintf(stderr,"Difference in total active:%d / %d\n",g1->total_active , g2->total_active);
equal = false;
}
if (equal) {
if (memcmp( g1->index_map , g2->index_map , g1->size * sizeof * g1->index_map ) != 0) {
equal = false;
if (verbose)
fprintf(stderr,"Difference in index map \n");
}
}
if (equal) {
if (memcmp( g1->inv_index_map , g2->inv_index_map , g1->total_active * sizeof * g1->inv_index_map ) != 0) {
equal = false;
if (verbose)
fprintf(stderr,"Difference in inverse index map \n");
}
}
if (equal && (g1->dualp_flag != FILEHEAD_SINGLE_POROSITY)) {
if (g1->total_active_fracture != g2->total_active_fracture) {
if (verbose)
fprintf(stderr,"Difference in toal_active_fracture %d / %d \n",g1->total_active_fracture , g2->total_active_fracture);
equal = false;
}
if (equal) {
if (memcmp( g1->fracture_index_map , g2->fracture_index_map , g1->size * sizeof * g1->fracture_index_map ) != 0) {
equal = false;
if (verbose)
fprintf(stderr,"Difference in fracture_index_map \n");
}
}
if (equal) {
if (memcmp( g1->inv_fracture_index_map , g2->inv_fracture_index_map , g1->total_active_fracture * sizeof * g1->inv_fracture_index_map ) != 0) {
equal = false;
if (verbose)
fprintf(stderr,"Difference in inv_fracture_index_map \n");
}
}
}
return equal;
}
static bool ecl_grid_compare_mapaxes(const ecl_grid_type * g1 , const ecl_grid_type * g2, bool verbose) {
bool equal = true;
if (g1->use_mapaxes == g2->use_mapaxes) {
if (g1->mapaxes) {
if (memcmp( g1->mapaxes , g2->mapaxes , 6 * sizeof * g1->mapaxes ) != 0)
equal = false;
}
} else
equal = false;
if (!equal && verbose)
fprintf(stderr,"Difference in mapaxes \n" );
return equal;
}
/**
Return true if grids g1 and g2 are equal, and false otherwise. To
return true all cells must be identical.
*/
static bool ecl_grid_compare__(const ecl_grid_type * g1 , const ecl_grid_type * g2, bool include_nnc , bool verbose) {
bool equal = true;
if (g1->size != g2->size)
equal = false;
// The name of the parent grid corresponds to a filename; they can be different.
if (equal && g1->parent_grid) {
if (!util_string_equal( g1->name , g2->name )) {
equal = false;
if (verbose)
fprintf(stderr,"Difference in name %s <-> %s \n" , g1->name , g2->name);
}
}
/*
When .GRID files are involved this is hardwired to FILEHEAD_SINGLE_POROSITY.
*/
if (g1->dualp_flag != g2->dualp_flag) {
equal = false;
if (verbose)
fprintf(stderr,"Dual porosity flags differ: %d / %d \n" , g1->dualp_flag , g2->dualp_flag);
}
if (equal)
equal = ecl_grid_compare_cells(g1 , g2 , include_nnc , verbose);
if (equal)
equal = ecl_grid_compare_index( g1 , g2 , true /*verbose*/);
if (equal)
equal = ecl_grid_compare_coarse_cells( g1 , g2 , verbose );
if (equal)
equal = ecl_grid_compare_mapaxes( g1 , g2 , verbose );
return equal;
}
bool ecl_grid_compare(const ecl_grid_type * g1 , const ecl_grid_type * g2 , bool include_lgr, bool include_nnc , bool verbose) {
bool equal = ecl_grid_compare__(g1 , g2 , include_nnc , verbose);
if (equal && include_lgr) {
if (vector_get_size( g1->LGR_list ) == vector_get_size( g2->LGR_list )) {
int grid_nr;
for (grid_nr = 0; grid_nr < vector_get_size( g1->LGR_list ); grid_nr++) {
const ecl_grid_type * lgr1 = (const ecl_grid_type*)vector_iget_const( g1->LGR_list , grid_nr);
const ecl_grid_type * lgr2 = (const ecl_grid_type*)vector_iget_const( g2->LGR_list , grid_nr);
printf("Comparing lgr grid:%d \n",grid_nr);
equal = ecl_grid_compare__(lgr1 , lgr2 , include_nnc , verbose);
if (!equal)
break;
}
} else
equal = false;
}
return equal;
}
/*****************************************************************/
typedef enum {NOT_ON_FACE, BELONGS_TO_CELL, BELONGS_TO_OTHER} face_status_enum;
/*
Returns whether the given point is contained within the minimal cube
encapsulating the cell that has all faces parallel to a coordinate plane.
*/
static bool ecl_grid_cube_contains(const ecl_cell_type * cell, const point_type * p) {
if (p->z < ecl_cell_min_z( cell ))
return false;
if (p->z > ecl_cell_max_z( cell ))
return false;
if (p->x < ecl_cell_min_x( cell ))
return false;
if (p->x > ecl_cell_max_x( cell ))
return false;
if (p->y < ecl_cell_min_y( cell ))
return false;
if (p->y > ecl_cell_max_y( cell ))
return false;
return true;
}
/*
Returns true if and only if p is on plane "plane" of cell when decomposed by "method".
*/
static bool ecl_grid_on_plane(const ecl_cell_type * cell, const int method,
const int plane, const point_type * p) {
const point_type * p0 = &cell->corner_list[ tetrahedron_permutations[method][plane][0] ];
const point_type * p1 = &cell->corner_list[ tetrahedron_permutations[method][plane][1] ];
const point_type * p2 = &cell->corner_list[ tetrahedron_permutations[method][plane][2] ];
return triangle_contains3d(p0, p1, p2, p);
}
/*
Returns true if and only if p is on one of the cells faces and
"belongs" to this cell. This is done such that every point is contained in at most
one point.
Known caveats when using this function:
- if a point is on the surface of a/many cells, but for all of these cells
the point is contained on two opposite sides of the cell. Imagine a cake
being cut as a cake should be cut. To which of the slices does the center
point of the cake belong? This is a somewhat obscure situation and it is
not possible to circumvent by only considering the grid cell by cell.
- if there is a fault and this cell is on the border of the grid.
- if a cells projection to the xy-plane is concave, this method might give
false positives.
Note: The correctness of this function relies *HEAVILY* on the permutation of the
tetrahedrons in the decompositions.
*/
static face_status_enum ecl_grid_on_cell_face(const ecl_cell_type * cell, const int method,
const point_type * p,
const bool max_i, const bool max_j, const bool max_k) {
int k_minus = 0, j_pluss = 1, i_minus = 2, i_pluss = 3, j_minus = 4, k_pluss = 5;
bool on[6];
for(int i = 0; i < 6; ++i) {
on[i] = (
ecl_grid_on_plane(cell, method, 2*i, p) ||
ecl_grid_on_plane(cell, method, 2*i+1, p)
);
}
// Not on any of the cell sides
if(!on[k_minus] && !on[k_pluss] && !on[j_pluss] && !on[j_minus] && !on[i_minus] && !on[i_pluss])
return NOT_ON_FACE;
// Handles side collapses, i.e. the point is contained on opposite sides.
// Cell passes on the responsibility if not on border of grid.
bool i_collapse = (on[i_minus] && on[i_pluss]);
bool j_collapse = (on[j_minus] && on[j_pluss]);
bool k_collapse = (on[k_minus] && on[k_pluss]);
for(int i = 0; i < 6; ++i)
on[i] &= (!i_collapse || max_i) && (!j_collapse || max_j) && (!k_collapse || max_k);
on[i_minus] &= !on[i_pluss];
on[j_minus] &= !on[j_pluss];
on[k_minus] &= !on[k_pluss];
// Removed from all sides
if(!on[k_minus] && !on[k_pluss] && !on[j_pluss] && !on[j_minus] && !on[i_minus] && !on[i_pluss])
return BELONGS_TO_OTHER;
// Not on any of the lower priority sides
if(!on[k_pluss] && !on[j_pluss] && !on[i_pluss])
return BELONGS_TO_CELL;
// Contained in cell due to border conditions
// NOTE: One should read X <= Y as X "implies" Y
if((on[i_pluss] <= max_i) && (on[j_pluss] <= max_j) && (on[k_pluss] <= max_k))
return BELONGS_TO_CELL;
return BELONGS_TO_OTHER;
}
/*
Returns true if and only if the tetrahedron defined by p0, p1, p2, p3
contains p.
The sole purpose of this functions is to make concave_cell_contains
more readable.
*/
static bool tetrahedron_by_points_contains(const point_type * p0,
const point_type * p1,
const point_type * p2,
const point_type * p3,
const point_type * p) {
tetrahedron_type pro_tet;
pro_tet.p0 = *p0;
pro_tet.p1 = *p1;
pro_tet.p2 = *p2;
pro_tet.p3 = *p3;
return tetrahedron_contains(pro_tet, *p);
}
/*
Returns true if and only if the cell "cell" decomposed by "method" contains the point "p".
This is done by decomposing the cell into 5 tetrahedrons according to the decomposition
method for the faces.
Assumes the cell to not be self-intersecting!
Note: This function relies *HEAVILY* on the permutation of tetrahedron_permutations.
*/
static bool concave_cell_contains( const ecl_cell_type * cell, int method, const point_type * p) {
const point_type * dia[2][2] = {
{
&cell->corner_list[tetrahedron_permutations[method][0][1]],
&cell->corner_list[tetrahedron_permutations[method][0][2]]
},
{
&cell->corner_list[tetrahedron_permutations[method][10][1]],
&cell->corner_list[tetrahedron_permutations[method][10][2]]
}
};
const point_type * extra[2][2] = {
{
&cell->corner_list[tetrahedron_permutations[method][0][0]],
&cell->corner_list[tetrahedron_permutations[method][1][0]]
},
{
&cell->corner_list[tetrahedron_permutations[method][10][0]],
&cell->corner_list[tetrahedron_permutations[method][11][0]]
}
};
// Test for containment in cell core
if (tetrahedron_by_points_contains(dia[0][0], dia[1][0], dia[0][1], dia[1][1], p))
return true;
// Test for containment in protrusions
for(int i = 0; i < 2; ++i) {
if(tetrahedron_by_points_contains(dia[i][0], dia[i][1], dia[(i+1)%2][0], extra[i][0], p))
return true;
if(tetrahedron_by_points_contains(dia[i][0], dia[(i+1)%2][1], dia[i][1], extra[i][1], p))
return true;
}
return false;
}
/*
Observe the following quirks with this functions:
- It is quite simple to create a cell where the center point is
actually *not* inside the cell - that might come as a surprise!
- Cells with nonzero twist are completely discarded from the search,
if the point (x,y,z) "should" have been found on the inside of a
twisted cell the algorithm will incorrectly return false; a
warning will be printed on stderr if a cell is discarded due to
twist.
- See the documentation of ecl_grid_on_cell_face for caveats regarding
containtment of points of cell faces.
*/
bool ecl_grid_cell_contains_xyz3( const ecl_grid_type * ecl_grid , int i, int j , int k, double x , double y , double z) {
point_type p;
ecl_cell_type * cell = ecl_grid_get_cell( ecl_grid , ecl_grid_get_global_index3( ecl_grid , i, j , k ));
point_set( &p , x , y , z);
int method = (i + j + k) % 2; // Chooses the approperiate decomposition method for the cell
if (GET_CELL_FLAG(cell , CELL_FLAG_TAINTED))
return false;
// Pruning
if (!ecl_grid_cube_contains(cell, &p))
return false;
// Checks if point is on one of the faces of the cell, and if so whether it
// "belongs" to this cell.
bool max_i = (i == ecl_grid->nx-1);
bool max_j = (j == ecl_grid->ny-1);
bool max_k = (k == ecl_grid->nz-1);
face_status_enum face_status = ecl_grid_on_cell_face(cell, method, &p, max_i, max_j, max_k);
if(face_status != NOT_ON_FACE)
return face_status == BELONGS_TO_CELL;
// Twisted cells
if (ecl_cell_get_twist(cell) > 0) {
fprintf(stderr, "** Warning: Point (%g,%g,%g) is in vicinity of twisted cell: (%d,%d,%d) - function:%s might be mistaken.\n", x,y,z,i,j,k, __func__);
return false;
}
// We now check whether the point is strictly inside the cell
return concave_cell_contains(cell, method, &p);
}
bool ecl_grid_cell_contains_xyz1( const ecl_grid_type * ecl_grid , int global_index, double x , double y , double z) {
int i,j,k;
ecl_grid_get_ijk1( ecl_grid , global_index , &i , &j , &k);
return ecl_grid_cell_contains_xyz3( ecl_grid , i,j,k,x ,y , z);
}
/**
This function returns the global index for the cell (in layer 'k')
which contains the point x,y. Observe that if you are looking for
(i,j) you must call the function ecl_grid_get_ijk1() on the return value.
*/
int ecl_grid_get_global_index_from_xy( const ecl_grid_type * ecl_grid , int k , bool lower_layer , double x , double y) {
int i,j;
for (j=0; j < ecl_grid->ny; j++)
for (i=0; i < ecl_grid->nx; i++) {
int global_index = ecl_grid_get_global_index3( ecl_grid , i , j , k );
if (ecl_cell_layer_contains_xy( ecl_grid_get_cell( ecl_grid , global_index ) , lower_layer , x , y))
return global_index;
}
return -1; /* Did not find x,y */
}
int ecl_grid_get_global_index_from_xy_top( const ecl_grid_type * ecl_grid , double x , double y) {
return ecl_grid_get_global_index_from_xy( ecl_grid , ecl_grid->nz - 1 , false , x , y );
}
int ecl_grid_get_global_index_from_xy_bottom( const ecl_grid_type * ecl_grid , double x , double y) {
return ecl_grid_get_global_index_from_xy( ecl_grid , 0 , true , x , y );
}
static void ecl_grid_clear_visited( ecl_grid_type * grid ) {
if (grid->visited == NULL)
grid->visited = (bool*)util_calloc( grid->size , sizeof * grid->visited );
{
int i;
for (i=0; i < grid->size; i++)
grid->visited[i] = false;
}
}
/*
Box coordinates are not inclusive, i.e. [i1,i2)
*/
static int ecl_grid_box_contains_xyz( const ecl_grid_type * grid , int i1, int i2 , int j1 , int j2 , int k1 , int k2 , const point_type * p) {
int i,j,k;
int global_index = -1;
for (k=k1; k < k2; k++)
for (j=j1; j < j2; j++)
for (i=i1; i < i2; i++) {
global_index = ecl_grid_get_global_index3( grid , i , j , k);
if (!grid->visited[ global_index ]) {
grid->visited[ global_index ] = true;
if (ecl_grid_cell_contains_xyz1( grid , global_index , p->x , p->y , p->z )) {
return global_index;
}
}
}
return -1; /* Returning -1; did not find xyz. */
}
/**
* Search for given xyz coordinate around global start_index in a box of size bx
*/
static int ecl_grid_get_global_index_from_xyz_around_box(ecl_grid_type * grid , double x , double y , double z , int start_index, int bx, point_type * p) {
/* Try neighbours */
int i,j,k;
int i1,i2,j1,j2,k1,k2;
int nx,ny,nz;
ecl_grid_get_dims( grid , &nx , &ny , &nz , NULL);
ecl_grid_get_ijk1( grid , start_index , &i , &j , &k);
i1 = util_int_max( 0 , i - bx );
j1 = util_int_max( 0 , j - bx );
k1 = util_int_max( 0 , k - bx );
i2 = util_int_min( nx , i + bx );
j2 = util_int_min( ny , j + bx );
k2 = util_int_min( nz , k + bx );
int global_index = ecl_grid_box_contains_xyz( grid , i1 , i2 , j1 , j2 , k1 , k2 , p);
return global_index;
}
/**
This function will find the global index of the cell containing the
world coordinates (x,y,z), if no cell can be found the function
will return -1.
The function is basically based on scanning through the cells in
natural (i fastest) order and querying whether the cell[i,j,k]
contains the (x,y,z) point; not very elegant :-(
The last argument - 'start_index' - can be used to speed things up
a bit if you have reasonable guess of where the the (x,y,z) is
located. The start_index value is used as this:
start_index == 0: I do not have a clue, start from the beginning
and scan through the grid linearly.
start_index != 0:
1. Check the cell 'start_index'.
2. Check the neighbours (i +/- 1, j +/- 1, k +/- 1 ).
3. Give up and do a linear search starting from start_index.
*/
int ecl_grid_get_global_index_from_xyz(ecl_grid_type * grid , double x , double y , double z , int start_index) {
int global_index;
point_type p;
point_set( &p , x , y , z);
ecl_grid_clear_visited( grid );
if (start_index >= 0) {
/* Try start index */
if (ecl_grid_cell_contains_xyz1( grid , start_index , x,y,z))
return start_index;
else {
/* Try boxes 2, 4, 8, ..., 64 */
for (int bx = 1; bx <= 6; bx++) {
global_index = ecl_grid_get_global_index_from_xyz_around_box(grid, x, y, z,
start_index,
1<<bx, &p);
if (global_index >= 0)
return global_index;
}
}
}
/*
OK - the attempted shortcuts did not pay off. Perform full linear search.
*/
global_index = -1;
for (int index = 0; index < grid->size; index++) {
if (ecl_grid_cell_contains_xyz1( grid , index , x , y , z))
return index;
}
return -1;
}
bool ecl_grid_get_ijk_from_xyz(ecl_grid_type * grid , double x , double y , double z , int start_index, int *i, int *j, int *k ) {
int g = ecl_grid_get_global_index_from_xyz(grid, x, y, z, start_index);
if (g < 0)
return false;
ecl_grid_get_ijk1( grid , g , i,j,k);
return true;
}
static bool ecl_grid_sublayer_contanins_xy__(const ecl_grid_type * grid , double x , double y , int k , int i1 , int i2 , int j1 , int j2, geo_polygon_type * polygon) {
int i,j;
geo_polygon_reset( polygon );
/* Bottom edge */
for (i=i1; i < i2; i++) {
double corner_pos[3];
ecl_grid_get_corner_xyz( grid , i , j1 , k , &corner_pos[0] , &corner_pos[1] , &corner_pos[2]);
geo_polygon_add_point( polygon , corner_pos[0] , corner_pos[1]);
}
/* Right edge */
for (j=j1; j < j2; j++) {
double corner_pos[3];
ecl_grid_get_corner_xyz( grid , i2 , j , k , &corner_pos[0] , &corner_pos[1] , &corner_pos[2]);
geo_polygon_add_point( polygon , corner_pos[0] , corner_pos[1]);
}
/* Top edge */
for (i=i2; i > i1; i--) {
double corner_pos[3];
ecl_grid_get_corner_xyz( grid , i , j2 , k , &corner_pos[0] , &corner_pos[1] , &corner_pos[2]);
geo_polygon_add_point( polygon , corner_pos[0] , corner_pos[1]);
}
/* Left edge */
for (j=j2; j > j1; j--) {
double corner_pos[3];
ecl_grid_get_corner_xyz( grid , i1 , j , k , &corner_pos[0] , &corner_pos[1] , &corner_pos[2]);
geo_polygon_add_point( polygon , corner_pos[0] , corner_pos[1]);
}
geo_polygon_close( polygon );
return geo_polygon_contains_point__( polygon , x , y , true );
}
bool ecl_grid_get_ij_from_xy( const ecl_grid_type * grid , double x , double y , int k , int* i, int* j) {
geo_polygon_type * polygon = geo_polygon_alloc( NULL );
int nx = ecl_grid_get_nx( grid );
int ny = ecl_grid_get_ny( grid );
bool inside = ecl_grid_sublayer_contanins_xy__(grid , x , y , k , 0 , nx , 0 , ny , polygon);
if (inside) {
int i1 = 0;
int i2 = nx;
int j1 = 0;
int j2 = ny;
while (true) {
if ((i2 - i1) > 1) {
int ic = (i1 + i2) / 2;
if (ecl_grid_sublayer_contanins_xy__(grid , x , y , k , i1 , ic , j1 , j2 , polygon))
i2 = ic;
else {
if (!ecl_grid_sublayer_contanins_xy__(grid , x , y , k , ic , i2 , j1 , j2 , polygon))
util_abort("%s: point nowhere to be found ... \n",__func__);
i1 = ic;
}
}
if ((j2 - j1) > 1) {
int jc = (j1 + j2) / 2;
if (ecl_grid_sublayer_contanins_xy__(grid , x , y , k , i1 , i2 , j1 , jc , polygon))
j2 = jc;
else {
if (!ecl_grid_sublayer_contanins_xy__(grid , x , y , k , i1 , i2 , jc , j2 , polygon))
util_abort("%s: point nowhere to be found ... \n",__func__);
j1 = jc;
}
}
if ((i2 - i1) == 1 && (j2 - j1) == 1) {
*i = i1;
*j = j1;
break;
}
}
}
geo_polygon_free( polygon );
return inside;
}
void ecl_grid_alloc_blocking_variables(ecl_grid_type * grid, int block_dim) {
int index;
grid->block_dim = block_dim;
if (block_dim == 2)
grid->block_size = grid->nx* grid->ny; // Not supported
else if (block_dim == 3)
grid->block_size = grid->size;
else
util_abort("%: valid values are two and three. Value:%d invaid \n",__func__ , block_dim);
grid->values = (double_vector_type**)util_calloc( grid->block_size , sizeof * grid->values );
for (index = 0; index < grid->block_size; index++)
grid->values[index] = double_vector_alloc( 0 , 0.0 );
}
void ecl_grid_init_blocking(ecl_grid_type * grid) {
int index;
for (index = 0; index < grid->block_size; index++)
double_vector_reset(grid->values[index]);
grid->last_block_index = 0;
}
bool ecl_grid_block_value_3d(ecl_grid_type * grid, double x , double y , double z , double value) {
if (grid->block_dim != 3)
util_abort("%s: Wrong blocking dimension \n",__func__);
{
int global_index = ecl_grid_get_global_index_from_xyz( grid , x , y , z , grid->last_block_index);
if (global_index >= 0) {
double_vector_append( grid->values[global_index] , value);
grid->last_block_index = global_index;
return true;
} else
return false;
}
}
double ecl_grid_block_eval3d(ecl_grid_type * grid , int i, int j , int k ,block_function_ftype * blockf ) {
int global_index = ecl_grid_get_global_index3(grid , i,j,k);
return blockf( grid->values[global_index]);
}
int ecl_grid_get_block_count3d(const ecl_grid_type * grid , int i , int j, int k) {
int global_index = ecl_grid_get_global_index3(grid , i,j,k);
return double_vector_size( grid->values[global_index]);
}
/* End of blocking functions */
/*****************************************************************/
void ecl_grid_free(ecl_grid_type * grid) {
ecl_grid_free_cells( grid );
free(grid->index_map);
free(grid->inv_index_map);
free(grid->fracture_index_map);
free(grid->inv_fracture_index_map);
free(grid->mapaxes);
if (grid->values != NULL) {
int i;
for (i=0; i < grid->block_size; i++)
double_vector_free( grid->values[i] );
free( grid->values );
}
if (ECL_GRID_MAINGRID_LGR_NR == grid->lgr_nr) { /* This is the main grid. */
vector_free( grid->LGR_list );
int_vector_free( grid->lgr_index_map);
hash_free( grid->LGR_hash );
}
if (grid->coord_kw != NULL)
ecl_kw_free( grid->coord_kw );
vector_free( grid->coarse_cells );
hash_free( grid->children );
free( grid->parent_name );
free( grid->visited );
free( grid->name );
free( grid );
}
void ecl_grid_free__( void * arg ) {
ecl_grid_type * ecl_grid = ecl_grid_safe_cast( arg );
ecl_grid_free( ecl_grid );
}
void ecl_grid_get_distance(const ecl_grid_type * grid , int global_index1, int global_index2 , double *dx , double *dy , double *dz) {
ecl_cell_type * cell1 = ecl_grid_get_cell( grid , global_index1);
ecl_cell_type * cell2 = ecl_grid_get_cell( grid , global_index2);
ecl_cell_assert_center( cell1 );
ecl_cell_assert_center( cell2 );
{
*dx = cell1->center.x - cell2->center.x;
*dy = cell1->center.y - cell2->center.y;
*dz = cell1->center.z - cell2->center.z;
}
}
/*****************************************************************/
/* Index based query functions */
/*****************************************************************/
/**
Only checks that i,j,k are in the required intervals:
0 <= i < nx
0 <= j < ny
0 <= k < nz
*/
bool ecl_grid_ijk_valid(const ecl_grid_type * grid , int i , int j , int k) {
bool OK = false;
if (i >= 0 && i < grid->nx)
if (j >= 0 && j < grid->ny)
if (k >= 0 && k < grid->nz)
OK = true;
return OK;
}
void ecl_grid_get_dims(const ecl_grid_type * grid , int *nx , int * ny , int * nz , int * active_size) {
if (nx != NULL) *nx = grid->nx;
if (ny != NULL) *ny = grid->ny;
if (nz != NULL) *nz = grid->nz;
if (active_size != NULL) *active_size = grid->total_active;
}
int ecl_grid_get_nz( const ecl_grid_type * grid ) {
return grid->nz;
}
int ecl_grid_get_nx( const ecl_grid_type * grid ) {
return grid->nx;
}
int ecl_grid_get_ny( const ecl_grid_type * grid ) {
return grid->ny;
}
int ecl_grid_get_nactive( const ecl_grid_type * grid ) {
return grid->total_active;
}
grid_dims_type ecl_grid_iget_dims( const ecl_grid_type * grid , int grid_nr) {
grid_dims_type dims;
const ecl_grid_type * lgr;
if (grid_nr == 0)
lgr = grid;
else
lgr = ecl_grid_iget_lgr(grid, grid_nr - 1);
dims.nx = lgr->nx;
dims.ny = lgr->ny;
dims.nz = lgr->nz;
dims.nactive = lgr->total_active;
return dims;
}
int ecl_grid_get_nactive_fracture( const ecl_grid_type * grid ) {
return grid->total_active_fracture;
}
int ecl_grid_get_parent_cell1( const ecl_grid_type * grid , int global_index ) {
const ecl_cell_type * cell = ecl_grid_get_cell( grid, global_index );
return cell->host_cell;
}
int ecl_grid_get_parent_cell3( const ecl_grid_type * grid , int i , int j , int k) {
int global_index = ecl_grid_get_global_index__(grid , i , j , k);
return ecl_grid_get_parent_cell1( grid , global_index );
}
/*****************************************************************/
/* Functions for converting between the different index types. */
/**
Converts: (i,j,k) -> global_index. i,j,k are zero offset.
*/
int ecl_grid_get_global_index3(const ecl_grid_type * ecl_grid , int i , int j , int k) {
if (ecl_grid_ijk_valid(ecl_grid , i , j , k))
return ecl_grid_get_global_index__(ecl_grid , i , j , k);
else {
util_abort("%s: i,j,k = (%d,%d,%d) is invalid:\n\n nx: [0,%d>\n ny: [0,%d>\n nz: [0,%d>\n",__func__ , i,j,k,ecl_grid->nx,ecl_grid->ny,ecl_grid->nz);
return -1; /* Compiler shut up. */
}
}
/**
Converts: active_index -> global_index
*/
int ecl_grid_get_global_index1A(const ecl_grid_type * ecl_grid , int active_index) {
return ecl_grid->inv_index_map[active_index];
}
int ecl_grid_get_global_index1F(const ecl_grid_type * ecl_grid , int active_fracture_index) {
return ecl_grid->inv_fracture_index_map[active_fracture_index];
}
/**
Converts: (i,j,k) -> active_index
(i,j,k ) are zero offset.
Will return -1 if the cell is not active.
*/
int ecl_grid_get_active_index3(const ecl_grid_type * ecl_grid , int i , int j , int k) {
int global_index = ecl_grid_get_global_index3(ecl_grid , i,j,k); /* In range: [0,nx*ny*nz) */
return ecl_grid_get_active_index1(ecl_grid , global_index);
}
/**
Converts: global_index -> active_index.
Will return -1 if the cell is not active.
*/
int ecl_grid_get_active_index1(const ecl_grid_type * ecl_grid , int global_index) {
return ecl_grid->index_map[global_index];
}
int ecl_grid_get_active_fracture_index3(const ecl_grid_type * ecl_grid , int i , int j , int k) {
int global_index = ecl_grid_get_global_index3(ecl_grid , i,j,k); /* In range: [0,nx*ny*nz) */
return ecl_grid_get_active_fracture_index1(ecl_grid , global_index);
}
/**
Converts: global_index -> active_index.
Will return -1 if the cell is not active.
*/
int ecl_grid_get_active_fracture_index1(const ecl_grid_type * ecl_grid , int global_index) {
if (ecl_grid->fracture_index_map == NULL)
return -1;
else
return ecl_grid->fracture_index_map[global_index];
}
/*
Converts global_index -> (i,j,k)
This function returns C-based zero offset indices. cell_
*/
void ecl_grid_get_ijk1(const ecl_grid_type * grid , int global_index, int *i, int *j , int *k) {
*k = global_index / (grid->nx * grid->ny); global_index -= (*k) * (grid->nx * grid->ny);
*j = global_index / grid->nx; global_index -= (*j) * grid->nx;
*i = global_index;
}
/*
Converts active_index -> (i,j,k)
*/
void ecl_grid_get_ijk1A(const ecl_grid_type *ecl_grid , int active_index , int *i, int * j, int * k) {
if (active_index >= 0 && active_index < ecl_grid->total_active) {
int global_index = ecl_grid_get_global_index1A( ecl_grid , active_index );
ecl_grid_get_ijk1(ecl_grid , global_index , i,j,k);
} else
util_abort("%s: error active_index:%d invalid - grid has only:%d active cells. \n",__func__ , active_index , ecl_grid->total_active);
}
/******************************************************************/
/*
Functions to get the 'true' (i.e. UTM or whatever) position (x,y,z).
The cell center is calculated as the plain average of the eight
corner positions, it is quite simple to construct cells where this
average position is on the outside of the cell - hence there is no
guarantee that the (x,y,z) position returned from this function
actually is on the inside of the cell.
*/
void ecl_grid_get_xyz1(const ecl_grid_type * grid , int global_index , double *xpos , double *ypos , double *zpos) {
ecl_cell_type * cell = ecl_grid_get_cell( grid , global_index);
ecl_cell_assert_center( cell );
{
*xpos = cell->center.x;
*ypos = cell->center.y;
*zpos = cell->center.z;
}
}
void ecl_grid_get_xyz3(const ecl_grid_type * grid , int i, int j , int k, double *xpos , double *ypos , double *zpos) {
const int global_index = ecl_grid_get_global_index__(grid , i , j , k );
ecl_grid_get_xyz1( grid , global_index , xpos , ypos , zpos);
}
/**
This function will return (by reference) the x,y,z values of corner
nr 'corner_nr' in cell 'global_index'. See the documentation of
tetraheder decomposition for the numbering of the corners.
*/
void ecl_grid_get_cell_corner_xyz1(const ecl_grid_type * grid , int global_index , int corner_nr , double * xpos , double * ypos , double * zpos ) {
if ((corner_nr >= 0) && (corner_nr <= 7)) {
const ecl_cell_type * cell = ecl_grid_get_cell( grid , global_index );
const point_type point = cell->corner_list[ corner_nr ];
*xpos = point.x;
*ypos = point.y;
*zpos = point.z;
}
}
void ecl_grid_export_cell_corners1(const ecl_grid_type * grid, int global_index, double *x, double *y, double *z) {
const ecl_cell_type * cell = ecl_grid_get_cell(grid, global_index);
for (int i=0; i<8; i++) {
const point_type point = cell->corner_list[i];
x[i] = point.x;
y[i] = point.y;
z[i] = point.z;
}
}
void ecl_grid_get_cell_corner_xyz3(const ecl_grid_type * grid , int i , int j , int k, int corner_nr , double * xpos , double * ypos , double * zpos ) {
const int global_index = ecl_grid_get_global_index__(grid , i , j , k );
ecl_grid_get_cell_corner_xyz1( grid , global_index , corner_nr , xpos , ypos , zpos);
}
void ecl_grid_get_corner_xyz(const ecl_grid_type * grid , int i , int j , int k, double * xpos , double * ypos , double * zpos ) {
if (i < 0 || i > grid->nx)
util_abort("%s: invalid i value:%d Valid range: [0,%d] \n",__func__ , i,grid->nx);
if (j < 0 || j > grid->ny)
util_abort("%s: invalid j value:%d Valid range: [0,%d] \n",__func__ , j,grid->ny);
if (k < 0 || k > grid->nz)
util_abort("%s: invalid k value:%d Valid range: [0,%d] \n",__func__ , k,grid->nz);
{
int corner_nr = 0;
if (i == grid->nx) {
i -= 1;
corner_nr += 1;
}
if (j == grid->ny) {
j -= 1;
corner_nr += 2;
}
if (k == grid->nz) {
k -= 1;
corner_nr += 4;
}
ecl_grid_get_cell_corner_xyz3( grid , i , j , k , corner_nr , xpos , ypos , zpos);
}
}
void ecl_grid_get_xyz1A(const ecl_grid_type * grid , int active_index , double *xpos , double *ypos , double *zpos) {
const int global_index = ecl_grid_get_global_index1A( grid , active_index );
ecl_grid_get_xyz1( grid , global_index , xpos , ypos , zpos );
}
double ecl_grid_get_cdepth1(const ecl_grid_type * grid , int global_index) {
ecl_cell_type * cell = ecl_grid_get_cell( grid , global_index);
ecl_cell_assert_center( cell );
return cell->center.z;
}
double ecl_grid_get_cdepth3(const ecl_grid_type * grid , int i, int j , int k) {
const int global_index = ecl_grid_get_global_index__(grid , i , j , k );
return ecl_grid_get_cdepth1( grid , global_index );
}
double ecl_grid_get_cdepth1A(const ecl_grid_type * grid , int active_index) {
const int global_index = ecl_grid_get_global_index1A(grid , active_index);
return ecl_grid_get_cdepth1( grid , global_index );
}
int ecl_grid_locate_depth( const ecl_grid_type * grid , double depth , int i , int j ) {
if (depth < ecl_grid_get_top2( grid , i , j))
return -1;
else if (depth >= ecl_grid_get_bottom2( grid , i , j ))
return -1 * grid->nz;
else {
int k=0;
double bottom = ecl_grid_get_top3( grid , i , j , k);
while (true) {
double top = bottom;
bottom = ecl_grid_get_bottom3( grid , i , j , k );
if ((depth >= top) && (depth < bottom))
return k;
k++;
if (k == grid->nz)
util_abort("%s: internal error when scanning for depth:%g \n",__func__ , depth);
}
}
}
/**
Returns the depth of the top surface of the cell.
*/
double ecl_grid_get_top1(const ecl_grid_type * grid , int global_index) {
const ecl_cell_type * cell = ecl_grid_get_cell( grid , global_index );
double depth = 0;
int ij;
for (ij = 0; ij < 4; ij++)
depth += cell->corner_list[ij].z;
return depth * 0.25;
}
double ecl_grid_get_top3(const ecl_grid_type * grid , int i, int j , int k) {
const int global_index = ecl_grid_get_global_index__(grid , i , j , k );
return ecl_grid_get_top1( grid , global_index );
}
double ecl_grid_get_top2(const ecl_grid_type * grid , int i, int j) {
const int global_index = ecl_grid_get_global_index__(grid , i , j , 0);
return ecl_grid_get_top1( grid , global_index );
}
double ecl_grid_get_bottom2(const ecl_grid_type * grid , int i, int j) {
const int global_index = ecl_grid_get_global_index__(grid , i , j , grid->nz - 1);
return ecl_grid_get_bottom1( grid , global_index );
}
double ecl_grid_get_top1A(const ecl_grid_type * grid , int active_index) {
const int global_index = ecl_grid_get_global_index1A(grid , active_index);
return ecl_grid_get_top1( grid , global_index );
}
/**
Returns the depth of the bottom surface of the cell.
*/
double ecl_grid_get_bottom1(const ecl_grid_type * grid , int global_index) {
const ecl_cell_type * cell = ecl_grid_get_cell( grid , global_index);
double depth = 0;
int ij;
for (ij = 0; ij < 4; ij++)
depth += cell->corner_list[ij + 4].z;
return depth * 0.25;
}
double ecl_grid_get_bottom3(const ecl_grid_type * grid , int i, int j , int k) {
const int global_index = ecl_grid_get_global_index__(grid , i , j , k );
return ecl_grid_get_bottom1( grid , global_index );
}
double ecl_grid_get_bottom1A(const ecl_grid_type * grid , int active_index) {
const int global_index = ecl_grid_get_global_index1A(grid , active_index);
return ecl_grid_get_bottom1( grid , global_index );
}
double ecl_grid_get_cell_dz1( const ecl_grid_type * grid , int global_index ) {
const ecl_cell_type * cell = ecl_grid_get_cell( grid , global_index);
double dz = 0;
int ij;
for (ij = 0; ij < 4; ij++)
dz += (cell->corner_list[ij + 4].z - cell->corner_list[ij].z);
return dz * 0.25;
}
double ecl_grid_get_cell_dz3( const ecl_grid_type * grid , int i , int j , int k) {
const int global_index = ecl_grid_get_global_index3(grid , i,j,k);
return ecl_grid_get_cell_dz1( grid , global_index );
}
double ecl_grid_get_cell_dz1A( const ecl_grid_type * grid , int active_index ) {
const int global_index = ecl_grid_get_global_index1A(grid , active_index);
return ecl_grid_get_cell_dz1( grid , global_index );
}
double ecl_grid_get_cell_thickness1( const ecl_grid_type * grid , int global_index ) {
return ecl_grid_get_cell_dz1( grid, global_index );
}
double ecl_grid_get_cell_thickness3( const ecl_grid_type * grid , int i , int j , int k) {
return ecl_grid_get_cell_dz3( grid, i,j,k);
}
double ecl_grid_get_cell_thickness1A( const ecl_grid_type * grid , int active_index ) {
return ecl_grid_get_cell_dz1A( grid, active_index);
}
double ecl_grid_get_cell_dx1( const ecl_grid_type * grid , int global_index ) {
const ecl_cell_type * cell = ecl_grid_get_cell( grid , global_index);
double dx = 0;
double dy = 0;
int c;
for (c = 1; c < 8; c += 2) {
dx += cell->corner_list[c].x - cell->corner_list[c - 1].x;
dy += cell->corner_list[c].y - cell->corner_list[c - 1].y;
}
dx *= 0.25;
dy *= 0.25;
return sqrt( dx * dx + dy * dy );
}
double ecl_grid_get_cell_dx3( const ecl_grid_type * grid , int i , int j , int k) {
const int global_index = ecl_grid_get_global_index3(grid , i,j,k);
return ecl_grid_get_cell_dx1( grid , global_index );
}
double ecl_grid_get_cell_dx1A( const ecl_grid_type * grid , int active_index) {
const int global_index = ecl_grid_get_global_index1A(grid , active_index);
return ecl_grid_get_cell_dx1( grid , global_index );
}
/*
The current algorithm for calculating the cell dimensions DX,DY and
DZ reproduces the Eclipse results from the INIT file, but we are in
general *not* guaranteed to satisfy the relationship:
DX * DY * DZ = V
*/
double ecl_grid_get_cell_dy1( const ecl_grid_type * grid , int global_index ) {
const ecl_cell_type * cell = ecl_grid_get_cell( grid , global_index);
double dx = 0;
double dy = 0;
for (int k = 0; k < 2; k++) {
for (int i = 0; i < 2; i++) {
int c1 = i + k*4;
int c2 = c1 + 2;
dx += cell->corner_list[c2].x - cell->corner_list[c1].x;
dy += cell->corner_list[c2].y - cell->corner_list[c1].y;
}
}
dx *= 0.25;
dy *= 0.25;
return sqrt( dx * dx + dy * dy );
}
double ecl_grid_get_cell_dy3( const ecl_grid_type * grid , int i , int j , int k) {
const int global_index = ecl_grid_get_global_index3(grid , i,j,k);
return ecl_grid_get_cell_dy1( grid , global_index );
}
double ecl_grid_get_cell_dy1A( const ecl_grid_type * grid , int active_index) {
const int global_index = ecl_grid_get_global_index1A(grid , active_index);
return ecl_grid_get_cell_dy1( grid , global_index );
}
const nnc_info_type * ecl_grid_get_cell_nnc_info1( const ecl_grid_type * grid , int global_index) {
const ecl_cell_type * cell = ecl_grid_get_cell( grid , global_index);
return cell->nnc_info;
}
const nnc_info_type * ecl_grid_get_cell_nnc_info3( const ecl_grid_type * grid , int i , int j , int k) {
const int global_index = ecl_grid_get_global_index3(grid , i,j,k);
return ecl_grid_get_cell_nnc_info1(grid, global_index);
}
/*****************************************************************/
/* Functions to query whether a cell is active or not. */
/*
Global index in [0,...,nx*ny*nz)
*/
bool ecl_grid_cell_active1(const ecl_grid_type * ecl_grid , int global_index) {
if (ecl_grid->index_map[global_index] >= 0)
return true;
else
return false;
}
bool ecl_grid_cell_active3(const ecl_grid_type * ecl_grid, int i , int j , int k) {
int global_index = ecl_grid_get_global_index3( ecl_grid , i , j , k);
return ecl_grid_cell_active1( ecl_grid , global_index );
}
/*****************************************************************/
bool ecl_grid_cell_invalid1(const ecl_grid_type * ecl_grid , int global_index) {
ecl_cell_type * cell = ecl_grid_get_cell( ecl_grid , global_index);
return GET_CELL_FLAG(cell , CELL_FLAG_TAINTED);
}
bool ecl_grid_cell_invalid3(const ecl_grid_type * ecl_grid , int i , int j , int k) {
int global_index = ecl_grid_get_global_index3( ecl_grid , i , j , k);
return ecl_grid_cell_invalid1( ecl_grid , global_index );
}
double ecl_grid_cell_invalid1A(const ecl_grid_type * grid , int active_index) {
const int global_index = ecl_grid_get_global_index1A(grid , active_index);
return ecl_grid_cell_invalid1( grid , global_index );
}
bool ecl_grid_cell_valid1(const ecl_grid_type * ecl_grid , int global_index) {
ecl_cell_type * cell = ecl_grid_get_cell( ecl_grid , global_index);
if (GET_CELL_FLAG(cell , CELL_FLAG_TAINTED))
return false;
else
return (GET_CELL_FLAG(cell , CELL_FLAG_VALID));
}
bool ecl_grid_cell_valid3(const ecl_grid_type * ecl_grid , int i , int j , int k) {
int global_index = ecl_grid_get_global_index3( ecl_grid , i , j , k);
return ecl_grid_cell_valid1( ecl_grid , global_index );
}
double ecl_grid_cell_valid1A(const ecl_grid_type * grid , int active_index) {
const int global_index = ecl_grid_get_global_index1A(grid , active_index);
return ecl_grid_cell_valid1( grid , global_index );
}
/*****************************************************************/
/* Functions for LGR query/lookup/... */
static void __assert_main_grid(const ecl_grid_type * ecl_grid) {
if (ecl_grid->lgr_nr != ECL_GRID_MAINGRID_LGR_NR)
util_abort("%s: tried to get LGR grid from another LGR_grid - only main grid can be used as first input \n",__func__);
}
/**
This functon will return a a ecl_grid instance corresponding to the
lgr with name lgr_name. The function will fail HARD if no lgr with
this name is installed under the present main grid; check first
with ecl_grid_has_lgr() if you are whimp.
Leading/trailing spaces on lgr_name are stripped prior to the hash lookup.
*/
ecl_grid_type * ecl_grid_get_lgr(const ecl_grid_type * main_grid, const char * __lgr_name) {
__assert_main_grid( main_grid );
{
char * lgr_name = util_alloc_strip_copy( __lgr_name );
ecl_grid_type * lgr_grid = (ecl_grid_type*)hash_get(main_grid->LGR_hash , lgr_name);
free(lgr_name);
return lgr_grid;
}
}
/**
Returns true/false if the main grid has a a lgr with name
__lgr_name. Leading/trailing spaces are stripped before checking.
*/
bool ecl_grid_has_lgr(const ecl_grid_type * main_grid, const char * __lgr_name) {
if(!__lgr_name)
return false;
__assert_main_grid( main_grid );
{
char * lgr_name = util_alloc_strip_copy( __lgr_name );
bool has_lgr = hash_has_key( main_grid->LGR_hash , lgr_name );
free(lgr_name);
return has_lgr;
}
}
bool ecl_grid_has_lgr_nr(const ecl_grid_type * main_grid, int lgr_nr) {
__assert_main_grid( main_grid );
{
if (int_vector_size( main_grid->lgr_index_map ) > lgr_nr)
return true;
else
return false;
}
}
int ecl_grid_get_num_coarse_groups( const ecl_grid_type * main_grid ) {
return vector_get_size( main_grid->coarse_cells );
}
bool ecl_grid_have_coarse_cells( const ecl_grid_type * main_grid ) {
if (vector_get_size( main_grid->coarse_cells ) == 0)
return false;
else
return true;
}
/**
Return the number of LGR's associated with this main grid
instance. The main grid is not counted.
*/
int ecl_grid_get_num_lgr(const ecl_grid_type * main_grid ) {
__assert_main_grid( main_grid );
return vector_get_size( main_grid->LGR_list );
}
/**
The lgr_index has zero offset
ecl_grid_iget_lgr( ecl_grid , 0); will return the first lgr
ecl_grid_iget_lgr( ecl_grid , 1); will return the second lgr
The method will fail HARD if lgr_index is out of bounds.
*/
ecl_grid_type * ecl_grid_iget_lgr(const ecl_grid_type * main_grid, int lgr_index) {
__assert_main_grid( main_grid );
return (ecl_grid_type*)vector_iget( main_grid->LGR_list , lgr_index);
}
/*
This function returns the lgr with the given lgr_nr. The lgr_nr is
the fourth element in the GRIDHEAD for EGRID files. The lgr nr is
equal to the grid nr if the grid's are consecutive numbered and
read from file in increasing lgr nr order.
This method can only be used for EGRID files. For GRID files the
lgr_nr is 0 for all grids.
*/
ecl_grid_type * ecl_grid_get_lgr_from_lgr_nr(const ecl_grid_type * main_grid, int lgr_nr) {
__assert_main_grid( main_grid );
{
int lgr_index = int_vector_iget( main_grid->lgr_index_map , lgr_nr );
return (ecl_grid_type*)vector_iget( main_grid->LGR_list , lgr_index);
}
}
/**
The following functions will return the LGR subgrid referenced by
the coordinates given. Observe the following:
1. The functions will happily return NULL if no LGR is associated
with the cell indicated - in fact that is (currently) the only
way to query whether a particular cell has a LGR.
2. If a certain cell is refined in several levels this function
will return a pointer to the first level of refinement. The
return value can can be used for repeated calls to descend
deeper into the refinement hierarchy.
*/
const ecl_grid_type * ecl_grid_get_cell_lgr1(const ecl_grid_type * grid , int global_index ) {
const ecl_cell_type * cell = ecl_grid_get_cell( grid , global_index);
return cell->lgr;
}
const ecl_grid_type * ecl_grid_get_cell_lgr3(const ecl_grid_type * grid , int i, int j , int k) {
const int global_index = ecl_grid_get_global_index__(grid , i , j , k );
return ecl_grid_get_cell_lgr1( grid , global_index );
}
const ecl_grid_type * ecl_grid_get_cell_lgr1A(const ecl_grid_type * grid , int active_index) {
const int global_index = ecl_grid_get_global_index1A( grid , active_index );
return ecl_grid_get_cell_lgr1( grid , global_index );
}
/**
Will return the global grid for a lgr. If the input grid is indeed
a global grid itself the function will return NULL.
*/
const ecl_grid_type * ecl_grid_get_global_grid( const ecl_grid_type * grid ) {
return grid->global_grid;
}
/*****************************************************************/
/**
Allocates a stringlist instance with the lookup names of the lgr names in this grid.
*/
stringlist_type * ecl_grid_alloc_lgr_name_list(const ecl_grid_type * ecl_grid) {
__assert_main_grid( ecl_grid );
{
return hash_alloc_stringlist( ecl_grid->LGR_hash );
}
}
const char * ecl_grid_iget_lgr_name( const ecl_grid_type * ecl_grid , int lgr_index) {
__assert_main_grid( ecl_grid );
if (lgr_index < (vector_get_size( ecl_grid->LGR_list ))) {
const ecl_grid_type * lgr = (const ecl_grid_type*)vector_iget( ecl_grid->LGR_list , lgr_index);
return lgr->name;
} else
return NULL;
}
const char * ecl_grid_get_lgr_name( const ecl_grid_type * ecl_grid , int lgr_nr) {
__assert_main_grid( ecl_grid );
if (lgr_nr == 0)
return ecl_grid->name;
{
int lgr_index = int_vector_iget( ecl_grid->lgr_index_map , lgr_nr );
return ecl_grid_iget_lgr_name( ecl_grid , lgr_index );
}
}
int ecl_grid_get_lgr_nr_from_name( const ecl_grid_type * grid , const char * name) {
__assert_main_grid( grid );
if (strcmp( name , grid->name) == 0)
return 0;
else {
const ecl_grid_type * lgr = ecl_grid_get_lgr( grid , name );
return lgr->lgr_nr;
}
}
/*****************************************************************/
/**
This function returns the lgr_nr field of the grid; for GRID files, this
is just the occurence number in the grid file. Starting with 0 at the main
grid, and then increasing consecutively through the lgr sections.
For EGRID files, this is the LGR number (fourth element in the
gridhead).
*/
int ecl_grid_get_lgr_nr( const ecl_grid_type * ecl_grid ) {
return ecl_grid->lgr_nr;
}
const char * ecl_grid_get_name( const ecl_grid_type * ecl_grid ) {
return ecl_grid->name;
}
int ecl_grid_get_global_size( const ecl_grid_type * ecl_grid ) {
return ecl_grid->nx * ecl_grid->ny * ecl_grid->nz;
}
int ecl_grid_get_active_size( const ecl_grid_type * ecl_grid ) {
return ecl_grid_get_nactive( ecl_grid );
}
bool ecl_grid_cell_regular1( const ecl_grid_type * ecl_grid, int global_index ) {
double x,y,z;
ecl_grid_get_xyz1( ecl_grid , global_index , &x,&y,&z);
return ecl_grid_cell_contains_xyz1( ecl_grid , global_index , x , y , z );
}
bool ecl_grid_cell_regular3( const ecl_grid_type * ecl_grid, int i,int j,int k) {
int global_index = ecl_grid_get_global_index3( ecl_grid , i,j,k);
return ecl_grid_cell_regular1( ecl_grid , global_index );
}
/*
The function ecl_grid_get_cell_twist() is an attempt to measure how
twisted or deformed a cell is. For a 'normal' cell the corners
[0..3] will z value <= the corners [4..7]. This function will count
the number of times the z value from the [4..7] is lower than the
corresponding z value from the [0..3] layer.
The purpose of the function is to detect twisted cells before
embarking on cell contains calculation. The current
ecl_cell_contains_xyz( ) implementation will fail badly for twisted
cells.
If the function return 4 you probably have an inverted z-axis!
*/
int ecl_grid_get_cell_twist1( const ecl_grid_type * ecl_grid, int global_index ) {
ecl_cell_type * cell = ecl_grid_get_cell( ecl_grid , global_index );
return ecl_cell_get_twist( cell );
}
int ecl_grid_get_cell_twist3(const ecl_grid_type * ecl_grid, int i, int j , int k) {
int global_index = ecl_grid_get_global_index3( ecl_grid , i , j , k);
return ecl_grid_get_cell_twist1( ecl_grid , global_index );
}
double ecl_grid_get_cell_volume1( const ecl_grid_type * ecl_grid, int global_index ) {
ecl_cell_type * cell = ecl_grid_get_cell( ecl_grid , global_index );
int i,j,k;
ecl_grid_get_ijk1( ecl_grid , global_index, &i , &j , &k);
return ecl_cell_get_volume( cell );
}
double ecl_grid_get_cell_volume1A( const ecl_grid_type * ecl_grid, int active_index ) {
int global_index = ecl_grid_get_global_index1A( ecl_grid , active_index );
return ecl_grid_get_cell_volume1( ecl_grid , global_index );
}
double ecl_grid_get_cell_volume3( const ecl_grid_type * ecl_grid, int i , int j , int k) {
int global_index = ecl_grid_get_global_index3( ecl_grid , i , j , k);
return ecl_grid_get_cell_volume1( ecl_grid , global_index );
}
void ecl_grid_summarize(const ecl_grid_type * ecl_grid) {
int active_cells , nx,ny,nz;
ecl_grid_get_dims(ecl_grid , &nx , &ny , &nz , &active_cells);
printf(" Name ..................: %s \n",ecl_grid->name);
printf(" Grid nr ...............: %d \n",ecl_grid->lgr_nr );
printf(" Active cells ..........: %d \n",active_cells);
printf(" Active fracture cells..: %d \n",ecl_grid_get_nactive_fracture( ecl_grid ));
printf(" nx ....................: %d \n",nx);
printf(" ny ....................: %d \n",ny);
printf(" nz ....................: %d \n",nz);
printf(" Volume ................: %d \n",nx*ny*nz);
printf(" Origo X................: %10.2f \n",ecl_grid->origo[0]);
printf(" Origo Y................: %10.2f \n",ecl_grid->origo[1]);
if (ECL_GRID_MAINGRID_LGR_NR == ecl_grid->lgr_nr) {
int grid_nr;
for (grid_nr=1; grid_nr < vector_get_size( ecl_grid->LGR_list ); grid_nr++) {
printf("\n");
ecl_grid_summarize( (const ecl_grid_type*)vector_iget_const( ecl_grid->LGR_list , grid_nr ));
}
}
ecl_grid_test_lgr_consistency( ecl_grid );
}
/*****************************************************************/
/**
This function is used to translate (with the help of the ecl_grid
functionality) i,j,k to an index which can be used to look up an
element in the ecl_kw instance. It is just a minor convenience
function.
* If the ecl_kw instance has nx*ny*nz (i,j,k) are translated to a
global index with ecl_grid_get_global_index3(). This is typically
the case when the ecl_kw instance represents a petrophysical
property which is e.g. loaded from a INIT file.
* If the ecl_kw instance has nactive elements the (i,j,k) indices
are converted to an active index with
ecl_grid_get_active_index3(). This is typically the case if the
ecl_kw instance is a solution vector which has been loaded from a
restart file. If you ask for an inactive cell the function will
return -1.
* If the ecl_kw instance has neither nx*ny*nz nor nactive elements
the function will fail HARD.
* The return value is double, irrespective of the type of the
underlying datatype of the ecl_kw instance - the function will
fail HARD if the underlying type can not be safely converted to
double, i.e. if it is not in the set [ecl_float_type ,
ecl_int_type , ecl_double_type].
* i,j,k: C-based zero offset grid coordinates.
*/
static int ecl_grid_get_property_index__(const ecl_grid_type * ecl_grid , const ecl_kw_type * ecl_kw , int i , int j , int k) {
int kw_size = ecl_kw_get_size( ecl_kw );
int lookup_index = -1;
if (kw_size == ecl_grid->nx * ecl_grid->ny * ecl_grid->nz)
lookup_index = ecl_grid_get_global_index3(ecl_grid , i , j , k);
else if (kw_size == ecl_grid->total_active)
/* Will be set to -1 if the cell is not active. */
lookup_index = ecl_grid_get_active_index3(ecl_grid , i , j , k);
else
util_abort("%s: incommensurable size ... \n",__func__);
return lookup_index;
}
static bool ecl_grid_get_property__(const ecl_grid_type * ecl_grid , const ecl_kw_type * ecl_kw , int i , int j , int k, void * value) {
ecl_data_type data_type = ecl_kw_get_data_type( ecl_kw );
if (ecl_type_is_numeric(data_type)) {
int lookup_index = ecl_grid_get_property_index__( ecl_grid , ecl_kw , i , j , k );
if (lookup_index >= 0) {
ecl_kw_iget( ecl_kw , lookup_index , value );
return true;
} else
return false;
} else {
util_abort("%s: sorry - can not lookup ECLIPSE type:%s with %s.\n",__func__ , ecl_type_alloc_name( data_type ) , __func__);
return false;
}
}
double ecl_grid_get_double_property(const ecl_grid_type * ecl_grid , const ecl_kw_type * ecl_kw , int i , int j , int k) {
ecl_data_type data_type = ecl_kw_get_data_type( ecl_kw );
if (ecl_type_is_double(data_type)) {
double value;
if (ecl_grid_get_property__( ecl_grid , ecl_kw , i , j , k , &value))
return value;
else
return -1; // (i,j,k) Points to an inactive cell.
} else {
util_abort("%s: Wrong type \n" , __func__);
return -1;
}
}
int ecl_grid_get_int_property(const ecl_grid_type * ecl_grid , const ecl_kw_type * ecl_kw , int i , int j , int k) {
ecl_data_type data_type = ecl_kw_get_data_type( ecl_kw );
if (ecl_type_is_int(data_type)) {
int value;
if (ecl_grid_get_property__( ecl_grid , ecl_kw , i , j , k , &value))
return value;
else
return -1; // (i,j,k) Points to an inactive cell.
} else {
util_abort("%s: Wrong type \n" , __func__);
return -1;
}
}
float ecl_grid_get_float_property(const ecl_grid_type * ecl_grid , const ecl_kw_type * ecl_kw , int i , int j , int k) {
ecl_data_type data_type = ecl_kw_get_data_type( ecl_kw );
if (ecl_type_is_float(data_type)) {
float value;
if (ecl_grid_get_property__( ecl_grid , ecl_kw , i , j , k , &value))
return value;
else
return -1; // (i,j,k) Points to an inactive cell.
} else {
util_abort("%s: Wrong type \n" , __func__);
return -1;
}
}
double ecl_grid_get_property(const ecl_grid_type * ecl_grid , const ecl_kw_type * ecl_kw , int i , int j , int k) {
ecl_data_type data_type = ecl_kw_get_data_type( ecl_kw );
if (ecl_type_is_numeric(data_type)) {
int lookup_index = ecl_grid_get_property_index__( ecl_grid , ecl_kw , i , j , k );
if (lookup_index >= 0)
return ecl_kw_iget_as_double( ecl_kw , lookup_index );
else
return -1; /* Tried to lookup an inactive cell. */
} else {
util_abort("%s: sorry - can not lookup ECLIPSE type:%s with %s.\n",__func__ , ecl_type_alloc_name( data_type ) , __func__);
return -1;
}
}
/**
Will fill the double_vector instance @column with values from
ecl_kw from the column given by (i,j). If @ecl_kw has size nactive
the inactive k values will not be set, i.e. you should make sure
that the default value of the @column instance has been properly
set beforehand.
The column vector will be filled with double values, the content of
ecl_kw will be converted to double in the case INTE,REAL and DOUB
types, otherwsie it is crash and burn.
*/
void ecl_grid_get_column_property(const ecl_grid_type * ecl_grid , const ecl_kw_type * ecl_kw , int i , int j, double_vector_type * column) {
ecl_data_type data_type = ecl_kw_get_data_type(ecl_kw);
if (ecl_type_is_numeric(data_type)) {
int kw_size = ecl_kw_get_size( ecl_kw );
bool use_global_index = false;
if (kw_size == ecl_grid->nx * ecl_grid->ny * ecl_grid->nz)
use_global_index = true;
else if (kw_size == ecl_grid->total_active)
use_global_index = false;
else
util_abort("%s: incommensurable sizes: nx*ny*nz = %d nactive=%d kw_size:%d \n",__func__ , ecl_grid->size , ecl_grid->total_active , ecl_kw_get_size( ecl_kw ));
double_vector_reset( column );
{
int k;
for (k=0; k < ecl_grid->nz; k++) {
if (use_global_index) {
int global_index = ecl_grid_get_global_index3( ecl_grid , i , j , k );
double_vector_iset( column , k , ecl_kw_iget_as_double( ecl_kw , global_index ));
} else {
int active_index = ecl_grid_get_active_index3( ecl_grid , i , j , k );
if (active_index >= 0)
double_vector_iset( column, k , ecl_kw_iget_as_double( ecl_kw , active_index ));
}
}
}
} else
util_abort("%s: sorry - can not lookup ECLIPSE type:%s with %s.\n",__func__ , ecl_type_alloc_name( data_type ) , __func__);
}
/*****************************************************************/
/**
This function will look up all the indices in the grid where the
region_kw has a certain value (region_value). The ecl_kw instance
must be loaded beforehand, typically with the functions
ecl_kw_grdecl_fseek_kw / ecl_kw_fscanf_alloc_grdecl_data.
The two boolean flags active_only and export_active_index determine
how active/inactive indieces should be handled:
active_only: Means that only cells which match the required
region_value AND are also active are stored. If active_only is
set to false, ALL cells matching region value are stored in
index_list.
export_active_index: if this value is true the the index of the
cell is in the space of active cells, otherwise it is in terms
of the global indexing.
Observe the following about the ecl_kw instance wth region data:
* It must be of type integer - otherwise we blow up hard. The
* size must be the total number of cells (should handle boxes and
so on ...)
Observe that there is no way to get ijk from this function, then
you must call ecl_grid_get_ijk() afterwards. the return value is
the number of cells found.
*/
int ecl_grid_get_region_cells(const ecl_grid_type * ecl_grid , const ecl_kw_type * region_kw , int region_value , bool active_only, bool export_active_index , int_vector_type * index_list) {
int cells_found = 0;
if (ecl_kw_get_size( region_kw ) == ecl_grid->size) {
if (ecl_type_is_int(ecl_kw_get_data_type( region_kw ))) {
const int * region_ptr = (const int*)ecl_kw_iget_ptr( region_kw , 0);
int_vector_reset( index_list );
{
int global_index;
for (global_index = 0; global_index < ecl_grid->size; global_index++) {
if (region_ptr[global_index] == region_value) {
if (!active_only || (ecl_grid->index_map[global_index] >= 0)) {
/* Okay - this index should be included */
if (export_active_index)
int_vector_iset(index_list , cells_found , ecl_grid->index_map[global_index]);
else
int_vector_iset(index_list , cells_found , global_index);
cells_found++;
}
}
}
}
} else
util_abort("%s: type mismatch - regions_kw must be of type integer \n",__func__);
} else
util_abort("%s: size mismatch grid has %d cells - region specifier:%d \n",__func__ , ecl_grid->size , ecl_kw_get_size( region_kw ));
return cells_found;
}
/*****************************************************************/
void ecl_grid_grdecl_fprintf_kw( const ecl_grid_type * ecl_grid , const ecl_kw_type * ecl_kw , const char * special_header , FILE * stream , double double_default) {
int src_size = ecl_kw_get_size( ecl_kw );
if (src_size == ecl_grid->size)
ecl_kw_fprintf_grdecl__( ecl_kw , special_header , stream );
else if (src_size == ecl_grid->total_active) {
void * default_ptr = NULL;
float float_default;
int int_default;
int bool_default;
ecl_type_enum ecl_type = ecl_type_get_type(ecl_kw_get_data_type( ecl_kw ));
if (ecl_type == ECL_FLOAT_TYPE) {
float_default = (float) double_default;
default_ptr = &float_default;
} else if (ecl_type == ECL_INT_TYPE) {
int_default = (int) double_default;
default_ptr = &int_default;
} else if (ecl_type == ECL_DOUBLE_TYPE) {
default_ptr = &double_default;
} else if (ecl_type == ECL_BOOL_TYPE) {
int tmp = (int) double_default;
if (tmp == 1)
bool_default = ECL_BOOL_TRUE_INT;
else if (tmp == 0)
bool_default = ECL_BOOL_FALSE_INT;
else
util_abort("%s: only 0 and 1 are allowed for bool interpolation\n",__func__);
default_ptr = &bool_default;
}
if (default_ptr == NULL)
util_abort("%s: invalid type \n",__func__);
{
ecl_kw_type * tmp_kw = ecl_kw_alloc_scatter_copy( ecl_kw , ecl_grid->size , ecl_grid->inv_index_map , default_ptr );
ecl_kw_fprintf_grdecl__( tmp_kw , special_header , stream );
ecl_kw_free( tmp_kw );
}
} else
util_abort("%s: size mismatch. ecl_kw must have either nx*ny*ny elements or nactive elements\n",__func__);
}
/*****************************************************************/
static bool ecl_grid_test_lgr_consistency2( const ecl_grid_type * parent , const ecl_grid_type * child) {
bool consistent = true;
int child_size = ecl_grid_get_global_size( child );
int child_index;
for (child_index = 0; child_index < child_size; child_index++) {
int parent_cell = ecl_grid_get_parent_cell1( child , child_index );
if (parent_cell >= 0) {
const ecl_grid_type * child_test = ecl_grid_get_cell_lgr1( parent , parent_cell );
if (child != child_test) {
fprintf(stderr , "Child parent mapping failure : ptr difference.\n");
consistent = false;
}
} else {
fprintf(stderr , "Child parent mapping failure : lgr has no parent cell.\n");
consistent = false;
}
}
return consistent;
}
bool ecl_grid_test_lgr_consistency( const ecl_grid_type * ecl_grid ) {
hash_iter_type * lgr_iter = hash_iter_alloc( ecl_grid->children );
bool consistent = true;
while (!hash_iter_is_complete( lgr_iter )) {
const ecl_grid_type * lgr = (const ecl_grid_type*)hash_iter_get_next_value( lgr_iter );
consistent &= ecl_grid_test_lgr_consistency2( ecl_grid , lgr );
consistent &= ecl_grid_test_lgr_consistency( lgr );
}
hash_iter_free( lgr_iter );
return consistent;
}
static void ecl_grid_dump__(const ecl_grid_type * grid , FILE * stream) {
util_fwrite_int( grid->lgr_nr , stream );
util_fwrite_string( grid->name , stream );
util_fwrite_int( grid->nx , stream );
util_fwrite_int( grid->nz , stream );
util_fwrite_int( grid->ny , stream );
util_fwrite_int( grid->size , stream );
util_fwrite_int( grid->total_active , stream );
util_fwrite_int_vector( grid->index_map , grid->size , stream , __func__ );
util_fwrite_int_vector( grid->inv_index_map , grid->total_active , stream , __func__ );
{
int i;
for (i=0; i < grid->size; i++) {
const ecl_cell_type * cell = ecl_grid_get_cell( grid , i );
ecl_cell_dump( cell , stream );
}
}
}
static void ecl_grid_dump_ascii__(ecl_grid_type * grid , bool active_only , FILE * stream) {
fprintf(stream , "Grid nr : %d\n",grid->lgr_nr);
fprintf(stream , "Grid name : %s\n",grid->name);
fprintf(stream , "nx : %6d\n",grid->nx);
fprintf(stream , "ny : %6d\n",grid->ny);
fprintf(stream , "nz : %6d\n",grid->nz);
fprintf(stream , "nactive : %6d\n",grid->total_active);
fprintf(stream , "nactive fracture : %6d\n",grid->total_active_fracture);
{
int l;
for (l=0; l < grid->size; l++) {
ecl_cell_type * cell = ecl_grid_get_cell( grid , l );
if (cell->active_index[MATRIX_INDEX] >= 0 || !active_only) {
int i,j,k;
ecl_grid_get_ijk1( grid , l , &i , &j , &k);
ecl_cell_dump_ascii( cell , i,j,k , stream , NULL);
}
}
}
}
/**
The dump function will dump a binary image of the internal grid
representation. The purpose of these dumps is to be able to test
the internal representation of the grid. No metadata is dumped, and
apart from byte-by-byte comparisons, the dump files are *not* meant
to be read.
*/
void ecl_grid_dump(const ecl_grid_type * grid , FILE * stream) {
ecl_grid_dump__(grid, stream );
{
int i;
for (i = 0; i < vector_get_size( grid->LGR_list ); i++)
ecl_grid_dump__( (const ecl_grid_type*)vector_iget_const( grid->LGR_list , i) , stream );
}
}
void ecl_grid_dump_ascii(ecl_grid_type * grid , bool active_only , FILE * stream) {
ecl_grid_dump_ascii__( grid , active_only , stream );
{
int i;
for (i = 0; i < vector_get_size( grid->LGR_list ); i++)
ecl_grid_dump_ascii__( (ecl_grid_type*)vector_iget( grid->LGR_list , i) , active_only , stream );
}
}
void ecl_grid_dump_ascii_cell1(ecl_grid_type * grid , int global_index , FILE * stream , const double * offset) {
ecl_cell_type * cell = ecl_grid_get_cell( grid , global_index );
int i,j,k;
ecl_grid_get_ijk1( grid , global_index , &i , &j , &k);
ecl_cell_dump_ascii(cell , i,j,k, stream , offset);
}
void ecl_grid_dump_ascii_cell3(ecl_grid_type * grid , int i , int j , int k , FILE * stream , const double * offset) {
int global_index = ecl_grid_get_global_index3(grid , i,j,k);
ecl_cell_type * cell = ecl_grid_get_cell( grid , global_index );
ecl_cell_dump_ascii(cell , i,j,k, stream , offset);
}
/*****************************************************************/
/*
'MAPUNITS' 1 'CHAR'
'METRES '
'GRIDUNIT' 2 'CHAR'
'METRES ' ' '
*/
/**
DIMENS
MAPUNITS
MAPAXES
GRIDUNIT
RADIAL
COORDS
CORNERS
COORDS
CORNERS
....
DIMENS
RADIAL
COORDS
CORNERS
COORDS
CORNERS
...
*/
bool ecl_grid_use_mapaxes( const ecl_grid_type * grid ) {
return grid->use_mapaxes;
}
void ecl_grid_init_mapaxes_data_double( const ecl_grid_type * grid , double * mapaxes) {
int i;
for (i = 0; i < 6; i++)
mapaxes[i] = grid->mapaxes[i];
}
static void ecl_grid_init_mapaxes_data_float( const ecl_grid_type * grid , float * mapaxes) {
int i;
for (i = 0; i < 6; i++)
mapaxes[i] = grid->mapaxes[i];
}
static const float * ecl_grid_get_mapaxes( const ecl_grid_type * grid ) {
return grid->mapaxes;
}
ecl_kw_type * ecl_grid_alloc_mapaxes_kw( const ecl_grid_type * grid ) {
return ecl_kw_alloc_new( MAPAXES_KW , 6 , ECL_FLOAT , grid->mapaxes);
}
static ecl_kw_type * ecl_grid_alloc_mapunits_kw( ert_ecl_unit_enum output_unit ) {
ecl_kw_type * mapunits_kw = ecl_kw_alloc( MAPUNITS_KW , 1 , ECL_CHAR);
if (output_unit == ECL_FIELD_UNITS)
ecl_kw_iset_string8( mapunits_kw , 0 , "FEET" );
if (output_unit == ECL_METRIC_UNITS)
ecl_kw_iset_string8( mapunits_kw , 0 , "METRES" );
if (output_unit == ECL_LAB_UNITS)
ecl_kw_iset_string8( mapunits_kw , 0 , "CM" );
return mapunits_kw;
}
static ecl_kw_type * ecl_grid_alloc_gridunits_kw( ert_ecl_unit_enum output_unit ) {
ecl_kw_type * gridunits_kw = ecl_kw_alloc( GRIDUNIT_KW , 2 , ECL_CHAR);
if (output_unit == ECL_FIELD_UNITS)
ecl_kw_iset_string8( gridunits_kw , 0 , "FEET" );
if (output_unit == ECL_METRIC_UNITS)
ecl_kw_iset_string8( gridunits_kw , 0 , "METRES" );
if (output_unit == ECL_LAB_UNITS)
ecl_kw_iset_string8( gridunits_kw , 0 , "CM" );
ecl_kw_iset_string8( gridunits_kw , 1 , "" );
return gridunits_kw;
}
static ert_ecl_unit_enum ecl_grid_check_unit_system(const ecl_kw_type * gridunit_kw) {
const char * length_unit = ecl_kw_iget_char_ptr(gridunit_kw, 0);
if (strncmp(length_unit, "FEET", 4) == 0)
return ECL_FIELD_UNITS;
if (strncmp(length_unit, "CM", 2) == 0)
return ECL_LAB_UNITS;
return ECL_METRIC_UNITS;
}
/*****************************************************************/
static float ecl_grid_output_scaling( const ecl_grid_type * grid , ert_ecl_unit_enum output_unit) {
if (grid->unit_system == output_unit)
return 1.0;
else {
double scale_factor = 1;
if (grid->unit_system == ECL_FIELD_UNITS)
scale_factor = 1.0 / METER_TO_FEET_SCALE_FACTOR;
if (grid->unit_system == ECL_LAB_UNITS)
scale_factor = 1.0 / METER_TO_CM_SCALE_FACTOR;
if (output_unit == ECL_FIELD_UNITS)
scale_factor *= METER_TO_FEET_SCALE_FACTOR;
if (output_unit == ECL_LAB_UNITS)
scale_factor *= METER_TO_CM_SCALE_FACTOR;
return scale_factor;
}
}
static void ecl_grid_fwrite_mapaxes( const ecl_grid_type * grid , fortio_type * fortio) {
ecl_kw_type * mapaxes_kw = ecl_grid_alloc_mapaxes_kw( grid );
ecl_kw_fwrite( mapaxes_kw , fortio );
ecl_kw_free( mapaxes_kw );
}
static void ecl_grid_fwrite_mapunits( fortio_type * fortio , ert_ecl_unit_enum output_unit) {
ecl_kw_type * mapunits_kw = ecl_grid_alloc_mapunits_kw( output_unit );
ecl_kw_fwrite( mapunits_kw , fortio );
ecl_kw_free( mapunits_kw );
}
static void ecl_grid_fwrite_gridunits( fortio_type * fortio, ert_ecl_unit_enum output_unit) {
ecl_kw_type * gridunits_kw = ecl_grid_alloc_gridunits_kw( output_unit );
ecl_kw_fwrite( gridunits_kw , fortio );
ecl_kw_free( gridunits_kw );
}
static void ecl_grid_fwrite_main_GRID_headers( const ecl_grid_type * ecl_grid , fortio_type * fortio , ert_ecl_unit_enum output_unit) {
ecl_grid_fwrite_mapunits( fortio , output_unit );
if (ecl_grid->use_mapaxes)
ecl_grid_fwrite_mapaxes( ecl_grid , fortio );
ecl_grid_fwrite_gridunits( fortio , output_unit );
}
static void ecl_grid_fwrite_GRID__( const ecl_grid_type * grid , int coords_size , fortio_type * fortio, ert_ecl_unit_enum output_unit) {
if (grid->parent_grid != NULL) {
ecl_kw_type * lgr_kw = ecl_kw_alloc(LGR_KW , 1 , ECL_CHAR);
ecl_kw_iset_string8( lgr_kw , 0 , grid->name );
ecl_kw_fwrite( lgr_kw , fortio );
ecl_kw_free( lgr_kw );
}
{
ecl_kw_type * dimens_kw = ecl_kw_alloc(DIMENS_KW , 3 , ECL_INT);
ecl_kw_iset_int( dimens_kw , 0 , grid->nx );
ecl_kw_iset_int( dimens_kw , 1 , grid->ny );
if (grid->dualp_flag == FILEHEAD_SINGLE_POROSITY)
ecl_kw_iset_int( dimens_kw , 2 , grid->nz );
else
ecl_kw_iset_int( dimens_kw , 2 , 2*grid->nz );
ecl_kw_fwrite( dimens_kw , fortio );
ecl_kw_free( dimens_kw );
}
if (grid->parent_grid == NULL)
ecl_grid_fwrite_main_GRID_headers( grid , fortio , output_unit);
{
ecl_kw_type * radial_kw = ecl_kw_alloc( RADIAL_KW , 1 , ECL_CHAR);
ecl_kw_iset_string8( radial_kw , 0 , "FALSE" );
ecl_kw_fwrite( radial_kw , fortio );
ecl_kw_free( radial_kw );
}
{
ecl_kw_type * coords_kw = ecl_kw_alloc( COORDS_KW , coords_size , ECL_INT);
ecl_kw_type * corners_kw = ecl_kw_alloc( CORNERS_KW , 24 , ECL_FLOAT);
int i,j,k;
for (k=0; k < grid->nz; k++) {
for (j=0; j < grid->ny; j++) {
for (i=0; i < grid->nx; i++) {
int global_index = ecl_grid_get_global_index__(grid , i , j , k );
const ecl_cell_type * cell = ecl_grid_get_cell( grid , global_index );
ecl_cell_fwrite_GRID( grid , cell , false , coords_size , i,j,k,global_index,coords_kw , corners_kw , fortio );
}
}
}
if (grid->dualp_flag != FILEHEAD_SINGLE_POROSITY) {
for (k=grid->nz; k < 2*grid->nz; k++) {
for (j=0; j < grid->ny; j++) {
for (i=0; i < grid->nx; i++) {
int global_index = ecl_grid_get_global_index__(grid , i , j , k - grid->nz );
const ecl_cell_type * cell = ecl_grid_get_cell( grid , global_index );
ecl_cell_fwrite_GRID( grid , cell , true , coords_size , i,j,k,global_index , coords_kw , corners_kw , fortio );
}
}
}
}
}
}
void ecl_grid_fwrite_GRID2( const ecl_grid_type * grid , const char * filename, ert_ecl_unit_enum output_unit) {
int coords_size = 5;
bool fmt_file = false;
fortio_type * fortio = fortio_open_writer( filename , fmt_file , ECL_ENDIAN_FLIP );
if (hash_get_size( grid->children ) > 0)
coords_size = 7;
if (grid->coarsening_active)
coords_size = 7;
ecl_grid_fwrite_GRID__( grid , coords_size , fortio , output_unit);
{
int grid_nr;
for (grid_nr = 0; grid_nr < vector_get_size( grid->LGR_list ); grid_nr++) {
const ecl_grid_type * igrid = (const ecl_grid_type*)vector_iget_const( grid->LGR_list , grid_nr );
ecl_grid_fwrite_GRID__( igrid , coords_size , fortio , output_unit );
}
}
fortio_fclose( fortio );
}
void ecl_grid_fwrite_GRID( const ecl_grid_type * grid , const char * filename) {
ecl_grid_fwrite_GRID2( grid , filename , ECL_METRIC_UNITS );
}
/*****************************************************************/
/*
FILEHEAD 100:INTE
MAPUNITS 1:CHAR
MAPAXES 6:REAL
GRIDUNIT 2:CHAR
GRIDHEAD 100:INTE
COORD 15990:REAL
ZCORN 286720:REAL
ACTNUM 35840:INTE
ENDGRID 0:INTE
*/
/**
This function does by construction not take a grid instance as
argument, so that it can be called from an external scope to create
a standard EGRID header without creating a grid instance first.
*/
static void ecl_grid_fwrite_main_EGRID_header( const ecl_grid_type * grid , fortio_type * fortio , ert_ecl_unit_enum output_unit) {
int EGRID_VERSION = 3;
int RELEASE_YEAR = 2007;
int COMPAT_VERSION = 0;
const float * mapaxes = ecl_grid_get_mapaxes( grid );
{
ecl_kw_type * filehead_kw = ecl_kw_alloc( FILEHEAD_KW , 100 , ECL_INT);
ecl_kw_scalar_set_int( filehead_kw , 0 );
ecl_kw_iset_int( filehead_kw , FILEHEAD_VERSION_INDEX , EGRID_VERSION );
ecl_kw_iset_int( filehead_kw , FILEHEAD_YEAR_INDEX , RELEASE_YEAR );
ecl_kw_iset_int( filehead_kw , FILEHEAD_COMPAT_INDEX , COMPAT_VERSION );
ecl_kw_iset_int( filehead_kw , FILEHEAD_TYPE_INDEX , FILEHEAD_GRIDTYPE_CORNERPOINT );
ecl_kw_iset_int( filehead_kw , FILEHEAD_DUALP_INDEX , grid->dualp_flag );
ecl_kw_iset_int( filehead_kw , FILEHEAD_ORGFORMAT_INDEX , FILEHEAD_ORGTYPE_CORNERPOINT );
ecl_kw_fwrite( filehead_kw , fortio );
ecl_kw_free( filehead_kw );
}
ecl_grid_fwrite_mapunits( fortio , output_unit );
if (mapaxes != NULL)
ecl_grid_fwrite_mapaxes( grid , fortio );
ecl_grid_fwrite_gridunits( fortio , output_unit);
}
static void ecl_grid_fwrite_gridhead_kw( int nx, int ny , int nz, int grid_nr, fortio_type * fortio ) {
ecl_kw_type * gridhead_kw = ecl_grid_alloc_gridhead_kw( nx , ny , nz , grid_nr);
ecl_kw_fwrite( gridhead_kw , fortio );
ecl_kw_free( gridhead_kw );
}
/*****************************************************************/
/*
Will scan the halfopen k-interval [k1,k2) to find a cell which has
valid geometry. If no cell is found the function will return -1.
*/
static int ecl_grid_get_valid_index( const ecl_grid_type * grid , int i , int j , int k1 , int k2) {
int global_index;
int k = k1;
int delta = (k1 < k2) ? 1 : -1 ;
while (true) {
ecl_cell_type * cell;
global_index = ecl_grid_get_global_index3( grid , i , j , k );
cell = ecl_grid_get_cell( grid , global_index );
if (GET_CELL_FLAG(cell , CELL_FLAG_VALID))
return global_index;
else {
k += delta;
if (k == k2)
return -1;
}
}
}
static int ecl_grid_get_bottom_valid_index( const ecl_grid_type * grid , int i , int j ) {
return ecl_grid_get_valid_index( grid , i , j , grid->nz - 1 , -1);
}
static int ecl_grid_get_top_valid_index( const ecl_grid_type * grid , int i , int j ) {
return ecl_grid_get_valid_index( grid , i , j , 0 , grid->nz);
}
static bool ecl_grid_init_coord_section__( const ecl_grid_type * grid , int i, int j , int i_corner, int j_corner , bool force_set , float * coord_float , double * coord_double ) {
const int top_index = ecl_grid_get_top_valid_index( grid , i , j );
const int bottom_index = ecl_grid_get_bottom_valid_index( grid , i , j );
if (top_index == -1)
util_exit("% : no cell with a valid geometry description found in (i,j) = %d,%d - then what? \n",__func__ , i,j);
{
point_type top_point;
point_type bottom_point;
const ecl_cell_type * bottom_cell = ecl_grid_get_cell( grid , bottom_index );
const ecl_cell_type * top_cell = ecl_grid_get_cell( grid , top_index);
/*
2---3
| |
0---1
*/
int corner_index = j_corner*2 + i_corner;
int coord_offset = 6 * ( (j + j_corner) * (grid->nx + 1) + (i + i_corner) );
{
point_copy_values( &top_point , &top_cell->corner_list[corner_index]);
point_copy_values( &bottom_point , &bottom_cell->corner_list[ corner_index + 4]);
if ((top_point.z == bottom_point.z) && (force_set == false)) {
return false;
} else {
if (grid->use_mapaxes) {
point_mapaxes_invtransform( &top_point , grid->origo , grid->unit_x , grid->unit_y );
point_mapaxes_invtransform( &bottom_point , grid->origo , grid->unit_x , grid->unit_y );
}
if (coord_float) {
coord_float[coord_offset] = top_point.x;
coord_float[coord_offset + 1] = top_point.y;
coord_float[coord_offset + 2] = top_point.z;
coord_float[coord_offset + 3] = bottom_point.x;
coord_float[coord_offset + 4] = bottom_point.y;
coord_float[coord_offset + 5] = bottom_point.z;
}
if (coord_double) {
coord_double[coord_offset] = top_point.x;
coord_double[coord_offset + 1] = top_point.y;
coord_double[coord_offset + 2] = top_point.z;
coord_double[coord_offset + 3] = bottom_point.x;
coord_double[coord_offset + 4] = bottom_point.y;
coord_double[coord_offset + 5] = bottom_point.z;
}
return true;
}
}
}
}
static void ecl_grid_init_coord_section( const ecl_grid_type * grid , int i, int j , float * coord_float , double * coord_double ) {
int i_corner = 0;
int j_corner = 0;
if (i == grid->nx) {
i -= 1;
i_corner = 1;
}
if (j == grid->ny) {
j -= 1;
j_corner = 1;
}
ecl_grid_init_coord_section__( grid , i,j, i_corner,j_corner, /*force_set=*/true, coord_float , coord_double);
}
void ecl_grid_init_coord_data( const ecl_grid_type * grid , float * coord ) {
/*
The coord vector contains the points defining the top and bottom
of the pillars. The vector contains (nx + 1) * (ny + 1) 6 element
chunks of data, where each chunk contains the coordinates (x,y,z)
of the top and the bottom of the pillar.
*/
int i,j;
for (j=0; j <= grid->ny; j++) {
for (i=0; i <= grid->nx; i++)
ecl_grid_init_coord_section( grid , i , j , coord , NULL);
}
}
void ecl_grid_init_coord_data_double( const ecl_grid_type * grid , double * coord ) {
/*
The coord vector contains the points defining the top and bottom
of the pillars. The vector contains (nx + 1) * (ny + 1) 6 element
chunks of data, where each chunk contains the coordinates (x,y,z)
f the top and the bottom of the pillar.
*/
int i,j;
for (j=0; j <= grid->ny; j++) {
for (i=0; i <= grid->nx; i++)
ecl_grid_init_coord_section( grid , i , j , NULL , coord);
}
}
float * ecl_grid_alloc_coord_data( const ecl_grid_type * grid ) {
float * coord = (float*)util_calloc( ecl_grid_get_coord_size(grid) , sizeof * coord );
ecl_grid_init_coord_data( grid , coord );
return coord;
}
void ecl_grid_assert_coord_kw( ecl_grid_type * grid ) {
if (grid->coord_kw == NULL) {
grid->coord_kw = ecl_kw_alloc( COORD_KW , ecl_grid_get_coord_size( grid ) , ECL_FLOAT);
ecl_grid_init_coord_data( grid , (float*)ecl_kw_get_void_ptr( grid->coord_kw ));
}
}
/*****************************************************************/
static void ecl_grid_init_zcorn_data__( const ecl_grid_type * grid , float * zcorn_float , double * zcorn_double ) {
int nx = grid->nx;
int ny = grid->ny;
int nz = grid->nz;
int i,j,k;
for (j=0; j < ny; j++) {
for (i=0; i < nx; i++) {
for (k=0; k < nz; k++) {
const int cell_index = ecl_grid_get_global_index3( grid , i,j,k);
const ecl_cell_type * cell = ecl_grid_get_cell( grid , cell_index );
int l;
for (l=0; l < 2; l++) {
point_type p0 = cell->corner_list[ 4*l];
point_type p1 = cell->corner_list[ 4*l + 1];
point_type p2 = cell->corner_list[ 4*l + 2];
point_type p3 = cell->corner_list[ 4*l + 3];
int z1 = k*8*nx*ny + j*4*nx + 2*i + l*4*nx*ny;
int z2 = k*8*nx*ny + j*4*nx + 2*i + 1 + l*4*nx*ny;
int z3 = k*8*nx*ny + j*4*nx + 2*nx + 2*i + l*4*nx*ny;
int z4 = k*8*nx*ny + j*4*nx + 2*nx + 2*i + 1 + l*4*nx*ny;
if (zcorn_float) {
zcorn_float[z1] = p0.z;
zcorn_float[z2] = p1.z;
zcorn_float[z3] = p2.z;
zcorn_float[z4] = p3.z;
}
if (zcorn_double) {
zcorn_double[z1] = p0.z;
zcorn_double[z2] = p1.z;
zcorn_double[z3] = p2.z;
zcorn_double[z4] = p3.z;
}
}
}
}
}
}
void ecl_grid_init_zcorn_data( const ecl_grid_type * grid , float * zcorn ) {
ecl_grid_init_zcorn_data__( grid , zcorn , NULL );
}
void ecl_grid_init_zcorn_data_double( const ecl_grid_type * grid , double * zcorn ) {
ecl_grid_init_zcorn_data__( grid , NULL , zcorn );
}
float * ecl_grid_alloc_zcorn_data( const ecl_grid_type * grid ) {
float * zcorn = (float*)util_calloc( 8 * grid->size , sizeof * zcorn );
ecl_grid_init_zcorn_data( grid , zcorn );
return zcorn;
}
ecl_kw_type * ecl_grid_alloc_zcorn_kw( const ecl_grid_type * grid ) {
ecl_kw_type * zcorn_kw = ecl_kw_alloc( ZCORN_KW , ecl_grid_get_zcorn_size(grid), ECL_FLOAT);
ecl_grid_init_zcorn_data(grid , (float*)ecl_kw_get_void_ptr(zcorn_kw));
return zcorn_kw;
}
ecl_kw_type * ecl_grid_alloc_coord_kw( const ecl_grid_type * grid) {
if(grid->coord_kw)
return ecl_kw_alloc_copy(grid->coord_kw);
ecl_kw_type * coord_kw = ecl_kw_alloc(
COORD_KW,
ECL_GRID_COORD_SIZE(grid->nx, grid->ny),
ECL_FLOAT
);
ecl_grid_init_coord_data(grid, ecl_kw_get_float_ptr(coord_kw));
return coord_kw;
}
int ecl_grid_get_coord_size( const ecl_grid_type * grid) {
return ECL_GRID_COORD_SIZE( grid->nx , grid->ny );
}
int ecl_grid_get_zcorn_size( const ecl_grid_type * grid ) {
return ECL_GRID_ZCORN_SIZE( grid->nx , grid->ny, grid->nz );
}
/*****************************************************************/
void ecl_grid_init_actnum_data( const ecl_grid_type * grid , int * actnum ) {
int i;
for (i=0; i < grid->size; i++) {
const ecl_cell_type * cell = ecl_grid_get_cell( grid , i );
if (cell->coarse_group == COARSE_GROUP_NONE)
actnum[i] = cell->active;
else {
/* In the case of coarse cells we must query the coarse cell for
the original, uncoarsened distribution of actnum values. */
ecl_coarse_cell_type * coarse_cell = ecl_grid_iget_coarse_group( grid , cell->coarse_group );
/* 1: Set all the elements in the coarse group to inactive. */
{
int group_size = ecl_coarse_cell_get_size( coarse_cell );
const int * index_ptr = ecl_coarse_cell_get_index_ptr( coarse_cell );
int j;
for (j=0; j < group_size; j++)
actnum[index_ptr[j]] = CELL_NOT_ACTIVE;
}
/* 2: Explicitly pick up the active cells from the coarse group
and mark them correctly. */
{
int num_active = ecl_coarse_cell_get_num_active( coarse_cell );
int j;
for (j=0; j < num_active; j++) {
int global_index = ecl_coarse_cell_iget_active_cell_index( coarse_cell , j );
int active_value = ecl_coarse_cell_iget_active_value( coarse_cell , j );
actnum[global_index] = active_value;
}
}
}
}
}
int * ecl_grid_alloc_actnum_data( const ecl_grid_type * grid ) {
int * actnum = (int*)util_calloc( grid->size , sizeof * actnum);
ecl_grid_init_actnum_data( grid , actnum );
return actnum;
}
ecl_kw_type * ecl_grid_alloc_actnum_kw( const ecl_grid_type * grid ) {
ecl_kw_type * actnum_kw = ecl_kw_alloc( ACTNUM_KW , grid->size , ECL_INT);
ecl_grid_init_actnum_data( grid , (int*)ecl_kw_get_void_ptr( actnum_kw ));
return actnum_kw;
}
void ecl_grid_compressed_kw_copy( const ecl_grid_type * grid , ecl_kw_type * target_kw , const ecl_kw_type * src_kw) {
if ((ecl_kw_get_size( target_kw ) == ecl_grid_get_nactive(grid)) && (ecl_kw_get_size( src_kw ) == ecl_grid_get_global_size(grid))) {
int active_index = 0;
int global_index;
for (global_index = 0; global_index < ecl_grid_get_global_size( grid ); global_index++) {
if (ecl_grid_cell_active1(grid, global_index)) {
ecl_kw_iset( target_kw , active_index , ecl_kw_iget_ptr(src_kw , global_index));
active_index++;
}
}
} else
util_abort("%s: size mismatch target:%d src:%d expected %d,%d \n",ecl_kw_get_size( target_kw ), ecl_kw_get_size( src_kw ) , ecl_grid_get_nactive(grid) , ecl_grid_get_global_size(grid));
}
void ecl_grid_global_kw_copy( const ecl_grid_type * grid , ecl_kw_type * target_kw , const ecl_kw_type * src_kw) {
if ((ecl_kw_get_size( src_kw ) == ecl_grid_get_nactive(grid)) && (ecl_kw_get_size( target_kw ) == ecl_grid_get_global_size(grid))) {
int active_index = 0;
int global_index;
for (global_index = 0; global_index < ecl_grid_get_global_size( grid ); global_index++) {
if (ecl_grid_cell_active1(grid, global_index)) {
ecl_kw_iset( target_kw , global_index , ecl_kw_iget_ptr(src_kw , active_index));
active_index++;
}
}
} else
util_abort("%s: size mismatch target:%d src:%d expected %d,%d \n",ecl_kw_get_size( target_kw ), ecl_kw_get_size( src_kw ) , ecl_grid_get_global_size(grid), ecl_grid_get_nactive(grid));
}
/*****************************************************************/
static void ecl_grid_init_hostnum_data( const ecl_grid_type * grid , int * hostnum ) {
int i;
for (i=0; i < grid->size; i++) {
const ecl_cell_type * cell = ecl_grid_get_cell(grid , i );
hostnum[i] = cell->host_cell;
}
}
int * ecl_grid_alloc_hostnum_data( const ecl_grid_type * grid ) {
int * hostnum = (int*)util_calloc( grid->size , sizeof * hostnum );
ecl_grid_init_hostnum_data( grid , hostnum );
return hostnum;
}
ecl_kw_type * ecl_grid_alloc_hostnum_kw( const ecl_grid_type * grid ) {
ecl_kw_type * hostnum_kw = ecl_kw_alloc( HOSTNUM_KW , grid->size , ECL_INT);
ecl_grid_init_hostnum_data( grid , (int*)ecl_kw_get_void_ptr( hostnum_kw ));
return hostnum_kw;
}
/*****************************************************************/
static void ecl_grid_init_corsnum_data( const ecl_grid_type * grid , int * corsnum ) {
int i;
for (i=0; i < grid->size; i++) {
const ecl_cell_type * cell = ecl_grid_get_cell(grid , i );
corsnum[i] = cell->coarse_group + 1;
}
}
int * ecl_grid_alloc_corsnum_data( const ecl_grid_type * grid ) {
int * corsnum = (int*)util_calloc( grid->size , sizeof * corsnum );
ecl_grid_init_corsnum_data( grid , corsnum );
return corsnum;
}
ecl_kw_type * ecl_grid_alloc_corsnum_kw( const ecl_grid_type * grid ) {
ecl_kw_type * corsnum_kw = ecl_kw_alloc( CORSNUM_KW , grid->size , ECL_INT);
ecl_grid_init_corsnum_data( grid , (int*)ecl_kw_get_void_ptr( corsnum_kw ));
return corsnum_kw;
}
/*****************************************************************/
ecl_kw_type * ecl_grid_alloc_gridhead_kw( int nx, int ny , int nz , int grid_nr) {
ecl_kw_type * gridhead_kw = ecl_kw_alloc( GRIDHEAD_KW , GRIDHEAD_SIZE , ECL_INT);
ecl_kw_scalar_set_int( gridhead_kw , 0 );
ecl_kw_iset_int( gridhead_kw , GRIDHEAD_TYPE_INDEX , GRIDHEAD_GRIDTYPE_CORNERPOINT );
ecl_kw_iset_int( gridhead_kw , GRIDHEAD_NX_INDEX , nx);
ecl_kw_iset_int( gridhead_kw , GRIDHEAD_NY_INDEX , ny);
ecl_kw_iset_int( gridhead_kw , GRIDHEAD_NZ_INDEX , nz);
ecl_kw_iset_int( gridhead_kw , GRIDHEAD_NUMRES_INDEX , 1);
ecl_kw_iset_int( gridhead_kw , GRIDHEAD_LGR_INDEX , grid_nr );
return gridhead_kw;
}
/*****************************************************************/
void ecl_grid_reset_actnum( ecl_grid_type * grid , const int * actnum ) {
const int global_size = ecl_grid_get_global_size( grid );
int g;
for (g=0; g < global_size; g++) {
ecl_cell_type * cell = ecl_grid_get_cell( grid , g );
if (actnum)
cell->active = actnum[g];
else
cell->active = 1;
}
ecl_grid_update_index( grid );
}
static void ecl_grid_fwrite_self_nnc( const ecl_grid_type * grid , fortio_type * fortio ) {
const int default_index = 1;
int_vector_type * g1 = int_vector_alloc(0 , default_index );
int_vector_type * g2 = int_vector_alloc(0 , default_index );
int g;
for (g=0; g < ecl_grid_get_global_size(grid); g++) {
ecl_cell_type * cell = ecl_grid_get_cell( grid , g );
const nnc_info_type * nnc_info = cell->nnc_info;
if (nnc_info) {
const nnc_vector_type * nnc_vector = nnc_info_get_self_vector(nnc_info);
int i;
for (i = 0; i < nnc_vector_get_size( nnc_vector ); i++) {
int nnc_index = nnc_vector_iget_nnc_index( nnc_vector , i );
int_vector_iset( g1 , nnc_index , 1 + g );
int_vector_iset( g2 , nnc_index , 1 + nnc_vector_iget_grid_index( nnc_vector , i ));
}
}
}
{
int num_nnc = int_vector_size( g1 );
ecl_kw_type * nnc1_kw = ecl_kw_alloc_new_shared( NNC1_KW , num_nnc , ECL_INT , int_vector_get_ptr( g1 ));
ecl_kw_type * nnc2_kw = ecl_kw_alloc_new_shared( NNC2_KW , num_nnc , ECL_INT , int_vector_get_ptr( g2 ));
ecl_kw_type * nnchead_kw = ecl_kw_alloc( NNCHEAD_KW , NNCHEAD_SIZE , ECL_INT);
ecl_kw_scalar_set_int( nnchead_kw , 0 );
ecl_kw_iset_int( nnchead_kw , NNCHEAD_NUMNNC_INDEX , num_nnc );
ecl_kw_iset_int( nnchead_kw , NNCHEAD_LGR_INDEX , grid->lgr_nr );
ecl_kw_fwrite( nnchead_kw , fortio);
ecl_kw_fwrite( nnc1_kw , fortio);
ecl_kw_fwrite( nnc2_kw , fortio);
ecl_kw_free( nnchead_kw );
ecl_kw_free( nnc2_kw );
ecl_kw_free( nnc1_kw );
}
int_vector_free( g1 );
int_vector_free( g2 );
}
static void ecl_grid_fwrite_EGRID__( ecl_grid_type * grid , fortio_type * fortio, ert_ecl_unit_enum output_unit) {
bool is_lgr = true;
if (grid->parent_grid == NULL)
is_lgr = false;
/* Writing header */
if (!is_lgr) {
ecl_grid_fwrite_main_EGRID_header( grid , fortio , output_unit );
} else {
{
ecl_kw_type * lgr_kw = ecl_kw_alloc(LGR_KW , 1 , ECL_CHAR);
ecl_kw_iset_string8( lgr_kw , 0 , grid->name );
ecl_kw_fwrite( lgr_kw , fortio );
ecl_kw_free( lgr_kw );
}
{
ecl_kw_type * lgr_parent_kw = ecl_kw_alloc(LGR_PARENT_KW , 1 , ECL_CHAR);
if (grid->parent_name != NULL)
ecl_kw_iset_string8( lgr_parent_kw , 0 , grid->parent_name );
else
ecl_kw_iset_string8( lgr_parent_kw , 0 , "");
ecl_kw_fwrite( lgr_parent_kw , fortio );
ecl_kw_free( lgr_parent_kw );
}
}
ecl_grid_fwrite_gridhead_kw( grid->nx , grid->ny , grid->nz , grid->lgr_nr , fortio);
/* Writing main grid data */
{
ecl_grid_assert_coord_kw( grid );
{
ecl_kw_type * coord_kw = ecl_kw_alloc_copy(grid->coord_kw);
ecl_kw_type * zcorn_kw = ecl_grid_alloc_zcorn_kw( grid );
if (output_unit != grid->unit_system) {
double scale_factor = ecl_grid_output_scaling( grid , output_unit );
ecl_kw_scale_float(coord_kw, scale_factor);
ecl_kw_scale_float(zcorn_kw, scale_factor);
}
ecl_kw_fwrite(coord_kw, fortio);
ecl_kw_fwrite(zcorn_kw, fortio);
ecl_kw_free( zcorn_kw );
ecl_kw_free(coord_kw);
}
{
ecl_kw_type * actnum_kw = ecl_grid_alloc_actnum_kw( grid );
ecl_kw_fwrite( actnum_kw , fortio );
ecl_kw_free( actnum_kw );
}
if (is_lgr) {
ecl_kw_type * hostnum_kw = ecl_grid_alloc_hostnum_kw( grid );
ecl_kw_fwrite( hostnum_kw , fortio );
ecl_kw_free( hostnum_kw );
}
if (grid->coarsening_active) {
ecl_kw_type * corsnum_kw = ecl_grid_alloc_corsnum_kw( grid );
ecl_kw_fwrite( corsnum_kw , fortio );
ecl_kw_free( corsnum_kw );
}
{
ecl_kw_type * endgrid_kw = ecl_kw_alloc( ENDGRID_KW , 0 , ECL_INT);
ecl_kw_fwrite( endgrid_kw , fortio );
ecl_kw_free( endgrid_kw );
}
}
if (is_lgr) {
ecl_kw_type * endlgr_kw = ecl_kw_alloc( ENDLGR_KW , 0 , ECL_INT);
ecl_kw_fwrite( endlgr_kw , fortio );
ecl_kw_free( endlgr_kw );
}
ecl_grid_fwrite_self_nnc( grid , fortio );
}
void ecl_grid_fwrite_EGRID2( ecl_grid_type * grid , const char * filename, ert_ecl_unit_enum output_unit) {
bool fmt_file = false;
fortio_type * fortio = fortio_open_writer( filename , fmt_file , ECL_ENDIAN_FLIP );
ecl_grid_fwrite_EGRID__( grid , fortio, output_unit);
{
int grid_nr;
for (grid_nr = 0; grid_nr < vector_get_size( grid->LGR_list ); grid_nr++) {
ecl_grid_type * igrid = (ecl_grid_type*)vector_iget( grid->LGR_list , grid_nr );
ecl_grid_fwrite_EGRID__( igrid , fortio, output_unit );
}
}
fortio_fclose( fortio );
}
/*
The construction with ecl_grid_fwrite_EGRID() and
ecl_grid_fwrite_EGRID2() is an attempt to create API stability. New
code should use the ecl_grid_fwrite_EGRID2() function.
*/
void ecl_grid_fwrite_EGRID( ecl_grid_type * grid , const char * filename, bool output_metric) {
ert_ecl_unit_enum output_unit = ECL_METRIC_UNITS;
if (!output_metric)
output_unit = ECL_FIELD_UNITS;
ecl_grid_fwrite_EGRID2( grid , filename , output_unit );
}
void ecl_grid_fwrite_depth( const ecl_grid_type * grid , fortio_type * init_file , ert_ecl_unit_enum output_unit) {
ecl_kw_type * depth_kw = ecl_kw_alloc("DEPTH" , ecl_grid_get_nactive(grid) , ECL_FLOAT);
{
float * depth_ptr = (float*)ecl_kw_get_ptr(depth_kw);
for (int i = 0; i < ecl_grid_get_nactive( grid ); i++)
depth_ptr[i] = ecl_grid_get_cdepth1A( grid , i );
}
ecl_kw_scale_float( depth_kw , ecl_grid_output_scaling( grid , output_unit ));
ecl_kw_fwrite( depth_kw , init_file );
ecl_kw_free( depth_kw );
}
void ecl_grid_fwrite_dims( const ecl_grid_type * grid , fortio_type * init_file, ert_ecl_unit_enum output_unit) {
ecl_kw_type * dx = ecl_kw_alloc("DX" , ecl_grid_get_nactive(grid) , ECL_FLOAT);
ecl_kw_type * dy = ecl_kw_alloc("DY" , ecl_grid_get_nactive(grid) , ECL_FLOAT);
ecl_kw_type * dz = ecl_kw_alloc("DZ" , ecl_grid_get_nactive(grid) , ECL_FLOAT);
{
{
float * dx_ptr = (float*)ecl_kw_get_ptr(dx);
float * dy_ptr = (float*)ecl_kw_get_ptr(dy);
float * dz_ptr = (float*)ecl_kw_get_ptr(dz);
for (int i = 0; i < ecl_grid_get_nactive( grid ); i++) {
dx_ptr[i] = ecl_grid_get_cell_dx1A( grid , i );
dy_ptr[i] = ecl_grid_get_cell_dy1A( grid , i );
dz_ptr[i] = ecl_grid_get_cell_dz1A( grid , i );
}
}
{
float scale_factor = ecl_grid_output_scaling( grid , output_unit );
ecl_kw_scale_float( dx , scale_factor );
ecl_kw_scale_float( dy , scale_factor );
ecl_kw_scale_float( dz , scale_factor );
}
}
ecl_kw_fwrite( dx , init_file );
ecl_kw_fwrite( dy , init_file );
ecl_kw_fwrite( dz , init_file );
ecl_kw_free( dx );
ecl_kw_free( dy );
ecl_kw_free( dz );
}
/**
Writes the current grid as grdecl keywords suitable to be read by
ECLIPSE. This function will only write the main grid and not
possible LGRs which are attached.
*/
void ecl_grid_fprintf_grdecl2(ecl_grid_type * grid , FILE * stream , ert_ecl_unit_enum output_unit) {
{
ecl_kw_type * mapunits_kw = ecl_grid_alloc_mapunits_kw( output_unit );
ecl_kw_fprintf_grdecl( mapunits_kw , stream );
ecl_kw_free( mapunits_kw );
fprintf(stream , "\n");
}
if (grid->use_mapaxes) {
ecl_kw_type * mapaxes_kw = ecl_grid_alloc_mapaxes_kw( grid );
ecl_kw_fprintf_grdecl( mapaxes_kw , stream );
ecl_kw_free( mapaxes_kw );
}
{
ecl_kw_type * gridunits_kw = ecl_grid_alloc_gridunits_kw( output_unit );
ecl_kw_fprintf_grdecl( gridunits_kw , stream );
ecl_kw_free( gridunits_kw );
fprintf(stream , "\n");
}
/*
The specgrid header is not properly internalized; the fourth and
fifth elements are just set to hardcoded values.
*/
{
int numres = 1;
char coord_type = 'F';
fprintf(stream , "%s\n" , SPECGRID_KW);
fprintf(stream , " %d %d %d %d %c / \n\n",grid->nx , grid->ny , grid->nz , numres , coord_type);
}
{
ecl_grid_assert_coord_kw( grid );
ecl_kw_fprintf_grdecl( grid->coord_kw , stream );
fprintf(stream , "\n");
}
{
ecl_kw_type * zcorn_kw = ecl_grid_alloc_zcorn_kw( grid );
ecl_kw_fprintf_grdecl( zcorn_kw , stream );
ecl_kw_free( zcorn_kw );
fprintf(stream , "\n");
}
{
ecl_kw_type * actnum_kw = ecl_grid_alloc_actnum_kw( grid );
ecl_kw_fprintf_grdecl( actnum_kw , stream );
ecl_kw_free( actnum_kw );
fprintf(stream , "\n");
}
}
void ecl_grid_fprintf_grdecl(ecl_grid_type * grid , FILE * stream ) {
ecl_grid_fprintf_grdecl2( grid , stream , ECL_METRIC_UNITS);
}
/*****************************************************************/
/**
The ri_points pointer should point to the base address of the
points data; this function will calculate the correct offset based on
global_index.
*/
void ecl_grid_cell_ri_export( const ecl_grid_type * ecl_grid , int global_index , double * ri_points) {
const ecl_cell_type * cell = ecl_grid_get_cell( ecl_grid , global_index );
int offset = global_index * 8 * 3;
ecl_cell_ri_export( cell , &ri_points[ offset ] );
}
void ecl_grid_ri_export( const ecl_grid_type * ecl_grid , double * ri_points) {
int global_index;
for (global_index = 0; global_index < ecl_grid->size; global_index++)
ecl_grid_cell_ri_export( ecl_grid , global_index , ri_points );
}
/*****************************************************************/
bool ecl_grid_dual_grid( const ecl_grid_type * ecl_grid ) {
if (ecl_grid->dualp_flag == FILEHEAD_SINGLE_POROSITY)
return false;
else
return true;
}
static int ecl_grid_get_num_nnc__( const ecl_grid_type * grid ) {
int g;
int num_nnc = 0;
for (g = 0; g < grid->size; g++) {
const nnc_info_type * nnc_info = ecl_grid_get_cell_nnc_info1( grid , g );
if (nnc_info)
num_nnc += nnc_info_get_total_size( nnc_info );
}
return num_nnc;
}
int ecl_grid_get_num_nnc( const ecl_grid_type * grid ) {
int num_nnc = ecl_grid_get_num_nnc__( grid );
{
int grid_nr;
for (grid_nr = 0; grid_nr < vector_get_size( grid->LGR_list ); grid_nr++) {
ecl_grid_type * igrid = (ecl_grid_type*)vector_iget( grid->LGR_list , grid_nr );
num_nnc += ecl_grid_get_num_nnc__( igrid );
}
}
return num_nnc;
}
static ecl_kw_type * ecl_grid_alloc_volume_kw_active( const ecl_grid_type * grid) {
ecl_kw_type * volume_kw = ecl_kw_alloc("VOLUME" , ecl_grid_get_active_size(grid) , ECL_DOUBLE);
{
double * volume_data = (double*)ecl_kw_get_ptr( volume_kw );
int active_index;
for (active_index = 0; active_index < ecl_grid_get_active_size(grid); active_index++) {
double cell_volume = ecl_grid_get_cell_volume1A(grid , active_index);
volume_data[ active_index] = cell_volume;
}
}
return volume_kw;
}
static ecl_kw_type * ecl_grid_alloc_volume_kw_global( const ecl_grid_type * grid) {
ecl_kw_type * volume_kw = ecl_kw_alloc("VOLUME" , ecl_grid_get_global_size(grid) , ECL_DOUBLE);
{
double * volume_data = (double*)ecl_kw_get_ptr( volume_kw );
int global_index;
for (global_index = 0; global_index < ecl_grid_get_global_size(grid); global_index++) {
double cell_volume = ecl_grid_get_cell_volume1(grid , global_index);
volume_data[ global_index] = cell_volume;
}
}
return volume_kw;
}
ecl_kw_type * ecl_grid_alloc_volume_kw( const ecl_grid_type * grid , bool active_size) {
if (active_size)
return ecl_grid_alloc_volume_kw_active( grid );
else
return ecl_grid_alloc_volume_kw_global( grid );
}
//