/*
   Copyright (c) 2011  equinor 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 <unordered_map>
#include <string>

#include <ert/util/util.h>
#include <ert/util/double_vector.hpp>
#include <ert/util/int_vector.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. */
  std::unordered_map<std::string, ecl_grid_type*> children;
  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.*/
  std::unordered_map<std::string, ecl_grid_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 = new ecl_grid_type();
  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);
  } else {
    grid->LGR_list      = NULL;
    grid->lgr_index_map = NULL;
  }
  grid->name            = NULL;
  grid->parent_name     = NULL;
  grid->parent_grid     = NULL;
  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);

    // Coarse cell group numbering is not necessarily in consecutive order
    if(coarse_cell == NULL) {
      continue;
    }

    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: Reset the coarse cell active_indices.
          In the involved path ecl_coarse_cell_update_index() only updates
          the coarse cells' active_index if it is -1. */
    for (int coarse_index = 0; coarse_index < vector_get_size(ecl_grid->coarse_cells); coarse_index++) {
      ecl_coarse_cell_type * coarse_cell = (ecl_coarse_cell_type*)vector_iget_const(ecl_grid->coarse_cells, coarse_index);

      // Coarse cell group numbering is not necessarily in consecutive order
      if(coarse_cell != NULL) {
        ecl_coarse_cell_reset_active_index(coarse_cell);
      }
    }

    /* 2: 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);
        }
      }
    }


    /*
      3: 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 );

      // Coarse cell group numbering is not necessarily in consecutive order
      if (coarse_cell == NULL) {
        continue;
      }

      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__);
  if ( lgr_grid->lgr_nr >= int_vector_size(main_grid->lgr_index_map) )
    int_vector_resize( main_grid->lgr_index_map, lgr_grid->lgr_nr+1 , 0);
  int_vector_iset(main_grid->lgr_index_map, lgr_grid->lgr_nr, vector_get_size(main_grid->LGR_list)-1);
  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) {
  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 ext_actnum is present that is used as ACTNUM, and the file is not checked
    for an ACTNUM keyword at all.
  */
  const int * actnum_data = NULL;
  if (ext_actnum)
    actnum_data = ext_actnum;
  else {
    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 (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 );

      // If the INIT file type is INTERSECT, we read NNC data from the INIT file
      // instead of the EGRID file.
      ecl_file_type* ecl_file_for_nnc_import = ecl_file;

      ecl_file_type* ecl_init_file = NULL;
      {
        char* path = NULL;    
        char* basename = NULL;
        util_alloc_file_components(grid_file, &path, &basename, NULL);
        char* init_file_name = ecl_util_alloc_filename(path, basename, ECL_INIT_FILE, false, -1);
        if (util_file_exists(init_file_name)) {
          ecl_file_type* ecl_init_file = ecl_file_open(init_file_name, 0);
          if (ecl_init_file) {
            ecl_version_enum version = ecl_file_get_ecl_version(ecl_init_file);
            if (version == INTERSECT) {
              ecl_file_for_nnc_import = ecl_init_file;
            }
          }
        }
        free(path);
        free(basename);
        free(init_file_name);
      }

      ecl_grid_init_nnc(main_grid, ecl_file_for_nnc_import);
      ecl_grid_init_nnc_amalgamated(main_grid, ecl_file_for_nnc_import);
      
      ecl_file_close(ecl_file);
      
      if (ecl_init_file) ecl_file_close(ecl_init_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);
  }
  if (grid->coord_kw != NULL)
    ecl_kw_free( grid->coord_kw );

  vector_free( grid->coarse_cells );
  free( grid->parent_name );
  free( grid->visited );
  free( grid->name );
  delete 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 quite well, relative error
  on the order 1e-4 for DX and DY and 1e-3 for DZ.

  Observe that the DX, DY and DZ values are not tied to the cell volume; i.e. 
  the relationship:

       DX * DY * DZ = V

  does generally not hold.
*/

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 = main_grid->LGR_hash.at(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             = main_grid->LGR_hash.count(lgr_name ) > 0;
    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 );
  {
    stringlist_type * s = stringlist_alloc_new();
    for (const auto& lgr_pair : ecl_grid->LGR_hash)
      stringlist_append_copy(s, lgr_pair.first.c_str());
    return s;
  }
}

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 ) {
  bool consistent = true;
  for (const auto& lgr_pair : ecl_grid->children) {
    const ecl_grid_type * lgr = lgr_pair.second;
    consistent &= ecl_grid_test_lgr_consistency2( ecl_grid , lgr );
    consistent &= ecl_grid_test_lgr_consistency( lgr );
  }
  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 );
          }
        }
      }
    }
    ecl_kw_free(coords_kw);
    ecl_kw_free(corners_kw);
  }
}


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 (grid->children.size() > 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 );

  // util_exit("% : no cell with a valid geometry description found in (i,j) = %d,%d - then what? \n",__func__ , i,j);
  // https://github.com/OPM/ResInsight/issues/7944
  if (top_index == -1) return false;

  {
    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;
  {
    bool is_fmt;

    if (ecl_util_get_file_type( filename , &is_fmt, NULL ) != ECL_OTHER_FILE)
      fmt_file = is_fmt;
  }
  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->mapaxes != NULL) {
    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 );
}

//This function is meant to be used w/ pandas datafram and numpy
//Note: global_index must be allocated w/ ecl_grid->total_active or ecl_grid->global_size int32 data points
//      index_data must be allocated w/ (4 * ecl_grid->total_active or ecl_grid->global_size) int32 data points.
void ecl_grid_export_index(const ecl_grid_type * grid, int * global_index, int * index_data , bool active_only) {
  int pos_indx = 0;
  int pos_data = 0;
  for (int k = 0; k < grid->nz; k++)
    for (int j = 0; j < grid->ny; j++)
      for (int i = 0; i < grid->nx; i++) {
        int g = ecl_grid_get_global_index__(grid, i, j, k);
        if (!active_only || grid->cells[g].active_index[0] >= 0) {
          global_index[pos_indx++] = g;
          index_data[pos_data++] = i;
          index_data[pos_data++] = j;
          index_data[pos_data++] = k;
          index_data[pos_data++] = grid->cells[g].active_index[0];
        }          
      }
}

//This function is meant to be used w/ pandas datafram and numpy
//Note: index_size must equal allocated size of output
void ecl_grid_export_data_as_int( int index_size, const int * data_index, const ecl_kw_type * kw, int * output) {
  int * input = ecl_kw_get_int_ptr(kw);
  for (int i=0; i < index_size; i++) {
    int di = data_index[i];
    if (di >= 0)
      output[i] = input[di];
  }
}

//This function is meant to be used w/ pandas datafram and numpy
//Note: index_size must equal allocated size of output
void ecl_grid_export_data_as_double( int index_size, const int * data_index, const ecl_kw_type * kw, double * output) {
  for (int i=0; i < index_size; i++) {
    int di = data_index[i];
    if (di >= 0)
      output[i] = ecl_kw_iget_as_double(kw, di);
  }
}

//This function is meant to be used w/ pandas datafram and numpy
void ecl_grid_export_volume( const ecl_grid_type * grid, int index_size, const int * global_index, double * output ) {
  for (int i = 0; i < index_size; i++) {
    int g = global_index[i];
    output[i] = ecl_grid_get_cell_volume1(grid, g);
  }
}

//This function is meant to be used w/ pandas datafram and numpy
void ecl_grid_export_position( const ecl_grid_type * grid, int index_size, const int * global_index, double * output) {
  for (int i = 0; i < index_size; i++) {
    int g = global_index[i];
    int j = 3 * i;
    ecl_grid_get_xyz1(grid, g, &output[j], &output[j+1], &output[j+2]);
  }
}

//This function is meant to be used w/ pandas dataframe and numpy
void export_corners( const ecl_grid_type * grid, int index_size, const int * global_index, double * output) {
  double x[8], y[8], z[8];
  int pos = 0;
  for (int i = 0; i < index_size; i++) {
    int g = global_index[i];
    ecl_grid_export_cell_corners1(grid, g, x, y, z);
    for (int j = 0; j < 8; j++) {
      output[pos++] = x[j];
      output[pos++] = y[j];
      output[pos++] = z[j];
    }
  }
}


//
