Ejemplo n.º 1
0
class EclSumKeyWordVector(BaseCClass):
    TYPE_NAME = "ecl_sum_vector"
    _alloc = EclPrototype("void* ecl_sum_vector_alloc(ecl_sum)", bind=False)
    _free = EclPrototype("void ecl_sum_vector_free(ecl_sum_vector)")
    _add = EclPrototype("bool ecl_sum_vector_add_key(ecl_sum_vector,  char*)")
    _add_multiple = EclPrototype(
        "void ecl_sum_vector_add_keys(ecl_sum_vector,  char*)")
    _get_size = EclPrototype("int ecl_sum_vector_get_size(ecl_sum_vector)")

    def __init__(self, ecl_sum):
        c_pointer = self._alloc(ecl_sum)
        super(EclSumKeyWordVector, self).__init__(c_pointer)

    def __len__(self):
        return self._get_size()

    def free(self):
        self._free()

    def add_keyword(self, keyword):
        success = self._add(keyword)
        if not success:
            raise KeyError("Failed to add keyword to vector")

    def add_keywords(self, keyword_pattern):
        self._add_multiple(keyword_pattern)

    def __repr__(self):
        return self._create_repr('len=%d' % len(self))
Ejemplo n.º 2
0
class EclUtil(object):
    _get_num_cpu = EclPrototype("int ecl_util_get_num_cpu( char* )",
                                bind=False)
    _get_file_type = EclPrototype(
        "ecl_file_enum ecl_util_get_file_type( char* , bool* , int*)",
        bind=False)
    _get_start_date = EclPrototype("time_t ecl_util_get_start_date( char* )",
                                   bind=False)
    _get_report_step = EclPrototype("int ecl_util_filename_report_nr( char* )",
                                    bind=False)

    @staticmethod
    def get_num_cpu(datafile):
        """
        Parse ECLIPSE datafile and determine how many CPUs are needed.

        Will look for the "PARALLELL" keyword, and then read off the
        number of CPUs required. Will return one if no PARALLELL keyword
        is found.
        """
        return EclUtil._get_num_cpu(datafile)

    @staticmethod
    def get_file_type(filename):
        """
        Will inspect an ECLIPSE filename and return an integer type flag.
        """
        file_type, fmt, step = EclUtil.inspectExtension(filename)
        return file_type

    @staticmethod
    def get_start_date(datafile):
        return EclUtil._get_start_date(datafile).datetime()

    @staticmethod
    def inspectExtension(filename):
        """Will inspect an ECLIPSE filename and return a tuple consisting of
        file type (EclFileEnum), a bool for formatted or not, and an
        integer for the step number.

        """
        fmt_file = ctypes.c_bool()
        report_step = ctypes.c_int(-1)
        file_type = EclUtil._get_file_type(filename, ctypes.byref(fmt_file),
                                           ctypes.byref(report_step))
        if report_step.value == -1:
            step = None
        else:
            step = report_step.value

        return (file_type, fmt_file.value, step)

    @staticmethod
    def reportStep(filename):
        report_step = EclUtil._get_report_step(filename)
        if report_step < 0:
            raise ValueError("Could not infer report step from: %s" % filename)

        return report_step
Ejemplo n.º 3
0
class FaultBlockCollection(BaseCClass):
    TYPE_NAME = "fault_block_collection"
    _alloc         = EclPrototype("void* fault_block_collection_alloc(ecl_grid)", bind=False)
    _free          = EclPrototype("void  fault_block_collection_free(fault_block_collection)")
    _num_layers    = EclPrototype("int   fault_block_collection_num_layers(fault_block_collection)")
    _scan_keyword  = EclPrototype("bool  fault_block_collection_scan_kw(fault_block_collection, ecl_kw)")
    _get_layer     = EclPrototype("fault_block_layer_ref  fault_block_collection_get_layer(fault_block_collection, int)")

    def __init__(self, grid):
        c_ptr = self._alloc(grid)
        if c_ptr:
            super(FaultBlockCollection, self).__init__(c_ptr)
        else:
            raise ValueError("Invalid input - failed to create FaultBlockCollection")

        # The underlying C implementation uses lazy evaluation and
        # needs to hold on to the grid reference. We therefor take
        # references to it here, to protect against premature garbage
        # collection.
        self.grid_ref = grid


    def __len__(self):
        return self._num_layers()


    def __repr__(self):
        return self._create_repr('len=%s' % len(self))


    def __getitem__(self, index):
        """
        @rtype: FaultBlockLayer
        """
        if isinstance(index, int):
            if 0 <= index < len(self):
                return self._get_layer(index).setParent(self)
            else:
                raise IndexError("Index:%d out of range [0,%d)" % (index, len(self)))
        else:
            raise TypeError("Index should be integer type")


    def get_layer(self, k):
        """
        @rtype: FaultBlockLayer
        """
        return self[k]

    def free(self):
        self._free()


    def scan_keyword(self, fault_block_kw):
        ok = self._scan_keyword(fault_block_kw)
        if not ok:
            raise ValueError("The fault block keyword had wrong type/size")
Ejemplo n.º 4
0
class EclSumTStep(BaseCClass):
    TYPE_NAME = "ecl_sum_tstep"
    _alloc = EclPrototype(
        "void* ecl_sum_tstep_alloc_new(int, int, float, void*)", bind=False)
    _free = EclPrototype("void ecl_sum_tstep_free(ecl_sum_tstep)")
    _get_sim_days = EclPrototype(
        "double ecl_sum_tstep_get_sim_days(ecl_sum_tstep)")
    _get_sim_time = EclPrototype(
        "time_t ecl_sum_tstep_get_sim_time(ecl_sum_tstep)")
    _get_report = EclPrototype("int ecl_sum_tstep_get_report(ecl_sum_tstep)")
    _get_ministep = EclPrototype(
        "int ecl_sum_tstep_get_ministep(ecl_sum_tstep)")
    _set_from_key = EclPrototype(
        "void ecl_sum_tstep_set_from_key(ecl_sum_tstep, char*, float)")
    _get_from_key = EclPrototype(
        "double ecl_sum_tstep_get_from_key(ecl_sum_tstep, char*)")
    _has_key = EclPrototype("bool ecl_sum_tstep_has_key(ecl_sum_tstep)")

    def __init__(self, report_step, mini_step, sim_days, smspec):
        sim_seconds = sim_days * 24 * 60 * 60
        c_pointer = self._alloc(report_step, mini_step, sim_seconds, smspec)
        super(EclSumTStep, self).__init__(c_pointer)

    def get_sim_days(self):
        """ @rtype: double """
        return self._get_sim_days()

    def get_report(self):
        """ @rtype: int """
        return self._get_report()

    def get_mini_step(self):
        """ @rtype: int """
        return self._get_ministep()

    def get_sim_time(self):
        """ @rtype: CTime """
        return self._get_sim_time()

    def __getitem__(self, key):
        """ @rtype: double """
        if not key in self:
            raise KeyError("Key '%s' is not available." % key)

        return self._get_from_key(key)

    def __setitem__(self, key, value):
        if not key in self:
            raise KeyError("Key '%s' is not available." % key)

        self._set_from_key(key, value)

    def __contains__(self, key):
        return self._has_key(key)

    def free(self):
        self._free(self)
Ejemplo n.º 5
0
class EclRestartHead(BaseCClass):
    TYPE_NAME = "ecl_rsthead"
    _alloc = EclPrototype("void*  ecl_rsthead_alloc(ecl_file_view , int )",
                          bind=False)
    _alloc_from_kw = EclPrototype(
        "void*  ecl_rsthead_alloc_from_kw(int , ecl_kw , ecl_kw , ecl_kw )",
        bind=False)
    _free = EclPrototype("void   ecl_rsthead_free(ecl_rsthead)")
    _get_report_step = EclPrototype(
        "int    ecl_rsthead_get_report_step(ecl_rsthead)")
    _get_sim_time = EclPrototype(
        "time_t ecl_rsthead_get_sim_time(ecl_rsthead)")
    _get_sim_days = EclPrototype(
        "double ecl_rsthead_get_sim_days(ecl_rsthead)")
    _get_nxconz = EclPrototype("int   ecl_rsthead_get_nxconz(ecl_rsthead)")
    _get_ncwmax = EclPrototype("int   ecl_rsthead_get_ncwmax(ecl_rsthead)")

    def __init__(self, kw_arg=None, rst_view=None):
        if kw_arg is None and rst_view is None:
            raise ValueError(
                'Cannot construct EclRestartHead without one of kw_arg and rst_view, both were None!'
            )

        if not kw_arg is None:
            report_step, intehead_kw, doubhead_kw, logihead_kw = kw_arg
            c_ptr = self._alloc_from_kw(report_step, intehead_kw, doubhead_kw,
                                        logihead_kw)
        else:
            c_ptr = self._alloc(rst_view, -1)

        super(EclRestartHead, self).__init__(c_ptr)

    def free(self):
        self._free()

    def get_report_step(self):
        return self._get_report_step()

    def get_sim_date(self):
        ct = CTime(self._get_sim_time())
        return ct.datetime()

    def get_sim_days(self):
        return self._get_sim_days()

    def well_details(self):
        return {"NXCONZ": self._get_nxconz(), "NCWMAX": self._get_ncwmax()}
Ejemplo n.º 6
0
class EclRFTCell(RFTCell):
    TYPE_NAME = "ecl_rft_cell"
    _alloc_RFT = EclPrototype(
        "void* ecl_rft_cell_alloc_RFT( int, int , int , double , double , double , double)",
        bind=False)
    _get_swat = EclPrototype("double ecl_rft_cell_get_swat( ecl_rft_cell )")
    _get_soil = EclPrototype("double ecl_rft_cell_get_soil( ecl_rft_cell )")
    _get_sgas = EclPrototype("double ecl_rft_cell_get_sgas( ecl_rft_cell )")

    def __init__(self, i, j, k, depth, pressure, swat, sgas):
        c_ptr = self._alloc_RFT(i, j, k, depth, pressure, swat, sgas)
        super(EclRFTCell, self).__init__(c_ptr)

    @property
    def swat(self):
        return self._get_swat()

    @property
    def sgas(self):
        return self._get_sgas()

    @property
    def soil(self):
        return 1 - (self._get_sgas() + self._get_swat())
Ejemplo n.º 7
0
class RFTCell(BaseCClass):
    """The RFTCell is a base class for the cells which are part of an RFT/PLT.
    
    The RFTCell class contains the elements which are common to both
    RFT and PLT. The list of common elements include the corrdinates
    (i,j,k) the pressure and the depth of the cell. Actual user access
    should be based on the derived classes EclRFTCell and EclPLTCell.

    Observe that from june 2013 the properties i,j and k which return
    offset 1 coordinate values are deprecated, and you should rather
    use the methods get_i(), get_j() and get_k() which return offset 0
    coordinate values.
    """
    TYPE_NAME = "rft_cell"
    _free = EclPrototype("void ecl_rft_cell_free( rft_cell )")
    _get_pressure = EclPrototype(
        "double ecl_rft_cell_get_pressure( rft_cell )")
    _get_depth = EclPrototype("double ecl_rft_cell_get_depth( rft_cell )")
    _get_i = EclPrototype("int ecl_rft_cell_get_i( rft_cell )")
    _get_j = EclPrototype("int ecl_rft_cell_get_j( rft_cell )")
    _get_k = EclPrototype("int ecl_rft_cell_get_k( rft_cell )")

    def free(self):
        self._free()

    def get_i(self):
        return self._get_i()

    def get_j(self):
        return self._get_j()

    def get_k(self):
        return self._get_k()

    def get_ijk(self):
        return (self.get_i(), self.get_j(), self.get_k())

    @property
    def pressure(self):
        return self._get_pressure()

    @property
    def depth(self):
        return self._get_depth()
Ejemplo n.º 8
0
class EclFileView(BaseCClass):
    TYPE_NAME = "ecl_file_view"
    _iget_kw = EclPrototype(
        "ecl_kw_ref    ecl_file_view_iget_kw( ecl_file_view , int)")
    _iget_named_kw = EclPrototype(
        "ecl_kw_ref    ecl_file_view_iget_named_kw( ecl_file_view , char* , int)"
    )
    _get_size = EclPrototype(
        "int           ecl_file_view_get_size( ecl_file_view )")
    _get_num_named_kw = EclPrototype(
        "int           ecl_file_view_get_num_named_kw( ecl_file_view , char* )"
    )
    _get_unique_size = EclPrototype(
        "int           ecl_file_view_get_num_distinct_kw( ecl_file_view )")
    _create_block_view = EclPrototype(
        "ecl_file_view_ref ecl_file_view_add_blockview( ecl_file_view , char*, int )"
    )
    _create_block_view2 = EclPrototype(
        "ecl_file_view_ref ecl_file_view_add_blockview2( ecl_file_view , char*, char*, int )"
    )
    _restart_view = EclPrototype(
        "ecl_file_view_ref ecl_file_view_add_restart_view( ecl_file_view , int, int, time_t, double )"
    )

    def __init__(self):
        raise NotImplementedError("Can not instantiate directly")

    def __iget(self, index):
        return self._iget_kw(index).setParent(parent=self)

    def __repr__(self):
        return 'EclFileView(size=%d) %s' % (len(self), self._ad_str())

    def iget_named_kw(self, kw_name, index):
        if not kw_name in self:
            raise KeyError("No such keyword: %s" % kw_name)

        if index >= self.numKeywords(kw_name):
            raise IndexError("Too large index: %d" % index)

        return self._iget_named_kw(kw_name, index).setParent(parent=self)

    def __getitem__(self, index):
        """
        Implements [] operator; index can be integer or key.

        Will look up EclKW instances from the current EclFile
        instance. The @index argument can either be an integer, in
        which case the method will return EclKW number @index, or
        alternatively a keyword string, in which case the method will
        return a list of EclKW instances with that keyword:

           restart_file = ecl_file.EclFile("ECLIPSE.UNRST")
           kw9 = restart_file[9]
           swat_list = restart_file["SWAT"]

        The keyword based lookup can be combined with an extra [] to
        get EclKW instance nr:

           swat9 = restart_file["SWAT"][9]

        Will return the 10'th SWAT keyword from the restart file. The
        following example will iterate over all the SWAT keywords in a
        restart file:

           restart_file = ecl_file.EclFile("ECLIPSE.UNRST")
           for swat in restart_file["SWAT"]:
               ....
        """

        if isinstance(index, int):
            ls = len(self)
            idx = index
            if idx < 0:
                idx += ls
            if 0 <= idx < ls:
                return self.__iget(idx)
            else:
                raise IndexError('Index must be in [0, %d), was: %d.' %
                                 (ls, index))

        if isinstance(index, slice):
            indices = index.indices(len(self))
            kw_list = []
            for i in range(*indices):
                kw_list.append(self[i])
            return kw_list
        else:
            if isinstance(index, bytes):
                index = index.decode('ascii')
            if isinstance(index, string_types):
                if index in self:
                    kw_index = index
                    kw_list = []
                    for index in range(self.numKeywords(kw_index)):
                        kw_list.append(self.iget_named_kw(kw_index, index))
                    return kw_list
                else:
                    raise KeyError("Unrecognized keyword:\'%s\'" % index)
            else:
                raise TypeError("Index must be integer or string (keyword)")

    def __len__(self):
        return self._get_size()

    def __contains__(self, kw):
        if self.numKeywords(kw) > 0:
            return True
        else:
            return False

    def num_keywords(self, kw):
        return self._get_num_named_kw(kw)

    def unique_size(self):
        return self._get_unique_size()

    def block_view2(self, start_kw, stop_kw, start_index):
        idx = start_index
        if start_kw:
            if not start_kw in self:
                raise KeyError("The keyword:%s is not in file" % start_kw)

            ls = self.numKeywords(start_kw)
            if idx < 0:
                idx += ls
            if not (0 <= idx < ls):
                raise IndexError('Index must be in [0, %d), was: %d.' %
                                 (ls, start_index))

        if stop_kw:
            if not stop_kw in self:
                raise KeyError("The keyword:%s is not in file" % stop_kw)

        view = self._create_block_view2(start_kw, stop_kw, idx)
        view.setParent(parent=self)
        return view

    def block_view(self, kw, kw_index):
        num = self.numKeywords(kw)

        if num == 0:
            raise KeyError("Unknown keyword: %s" % kw)

        idx = kw_index
        if idx < 0:
            idx += num

        if not (0 <= idx < num):
            raise IndexError('Index must be in [0, %d), was: %d.' %
                             (num, kw_index))

        view = self._create_block_view(kw, kw_index)
        view.setParent(parent=self)
        return view

    def restart_view(self,
                     seqnum_index=None,
                     report_step=None,
                     sim_time=None,
                     sim_days=None):
        if report_step is None:
            report_step = -1

        if sim_time is None:
            sim_time = -1

        if sim_days is None:
            sim_days = -1

        if seqnum_index is None:
            seqnum_index = -1

        view = self._restart_view(seqnum_index, report_step, CTime(sim_time),
                                  sim_days)
        if view is None:
            raise ValueError("No such restart block could be identiefied")

        view.setParent(parent=self)
        return view
Ejemplo n.º 9
0
class EclRegion(BaseCClass):
    TYPE_NAME = "ecl_region"
    _alloc = EclPrototype("void* ecl_region_alloc( ecl_grid , bool )",
                          bind=False)
    _alloc_copy = EclPrototype(
        "ecl_region_obj ecl_region_alloc_copy( ecl_region )")

    _set_kw_int = EclPrototype(
        "void ecl_region_set_kw_int( ecl_region , ecl_kw , int, bool) ")
    _set_kw_float = EclPrototype(
        "void ecl_region_set_kw_float( ecl_region , ecl_kw , float, bool ) ")
    _set_kw_double = EclPrototype(
        "void ecl_region_set_kw_double( ecl_region , ecl_kw , double , bool) ")
    _shift_kw_int = EclPrototype(
        "void ecl_region_shift_kw_int( ecl_region , ecl_kw , int, bool) ")
    _shift_kw_float = EclPrototype(
        "void ecl_region_shift_kw_float( ecl_region , ecl_kw , float, bool ) ")
    _shift_kw_double = EclPrototype(
        "void ecl_region_shift_kw_double( ecl_region , ecl_kw , double , bool) "
    )
    _scale_kw_int = EclPrototype(
        "void ecl_region_scale_kw_int( ecl_region , ecl_kw , int, bool) ")
    _scale_kw_float = EclPrototype(
        "void ecl_region_scale_kw_float( ecl_region , ecl_kw , float, bool ) ")
    _scale_kw_double = EclPrototype(
        "void ecl_region_scale_kw_double( ecl_region , ecl_kw , double , bool) "
    )

    _free = EclPrototype("void ecl_region_free( ecl_region )")
    _reset = EclPrototype("void ecl_region_reset( ecl_region )")
    _select_all = EclPrototype("void ecl_region_select_all( ecl_region )")
    _deselect_all = EclPrototype("void ecl_region_deselect_all( ecl_region )")
    _select_equal = EclPrototype(
        "void ecl_region_select_equal( ecl_region , ecl_kw , int )")
    _deselect_equal = EclPrototype(
        "void ecl_region_deselect_equal( ecl_region , ecl_kw , int)")
    _select_less = EclPrototype(
        "void ecl_region_select_smaller( ecl_region , ecl_kw , float )")
    _deselect_less = EclPrototype(
        "void ecl_region_deselect_smaller( ecl_region , ecl_kw , float )")
    _select_more = EclPrototype(
        "void ecl_region_select_larger( ecl_region , ecl_kw , float )")
    _deselect_more = EclPrototype(
        "void ecl_region_deselect_larger( ecl_region , ecl_kw , float )")
    _select_in_interval = EclPrototype(
        "void ecl_region_select_in_interval( ecl_region, ecl_kw , float , float )"
    )
    _deselect_in_interval = EclPrototype(
        "void ecl_region_deselect_in_interval( ecl_region, ecl_kw, float , float )"
    )
    _invert_selection = EclPrototype(
        "void ecl_region_invert_selection( ecl_region )")

    _select_box = EclPrototype(
        "void ecl_region_select_from_ijkbox(ecl_region , int , int , int , int , int , int)"
    )
    _deselect_box = EclPrototype(
        "void ecl_region_deselect_from_ijkbox(ecl_region , int , int , int , int , int , int)"
    )
    _imul_kw = EclPrototype(
        "void  ecl_region_kw_imul( ecl_region , ecl_kw , ecl_kw , bool)")
    _idiv_kw = EclPrototype(
        "void  ecl_region_kw_idiv( ecl_region , ecl_kw , ecl_kw , bool)")
    _iadd_kw = EclPrototype(
        "void  ecl_region_kw_iadd( ecl_region , ecl_kw , ecl_kw , bool)")
    _isub_kw = EclPrototype(
        "void  ecl_region_kw_isub( ecl_region , ecl_kw , ecl_kw , bool)")
    _copy_kw = EclPrototype(
        "void  ecl_region_kw_copy( ecl_region , ecl_kw , ecl_kw , bool)")
    _intersect = EclPrototype(
        "void ecl_region_intersection( ecl_region , ecl_region )")
    _combine = EclPrototype("void ecl_region_union( ecl_region , ecl_region )")
    _subtract = EclPrototype(
        "void ecl_region_subtract( ecl_region , ecl_region )")
    _xor = EclPrototype("void ecl_region_xor( ecl_region , ecl_region )")
    _get_kw_index_list = EclPrototype(
        "int_vector_ref ecl_region_get_kw_index_list( ecl_region , ecl_kw , bool )"
    )
    _get_active_list = EclPrototype(
        "int_vector_ref ecl_region_get_active_list( ecl_region )")
    _get_global_list = EclPrototype(
        "int_vector_ref ecl_region_get_global_list( ecl_region )")
    _get_active_global = EclPrototype(
        "int_vector_ref ecl_region_get_global_active_list( ecl_region )")
    _select_cmp_less = EclPrototype(
        "void ecl_region_cmp_select_less( ecl_region , ecl_kw , ecl_kw)")
    _select_cmp_more = EclPrototype(
        "void ecl_region_cmp_select_more( ecl_region , ecl_kw , ecl_kw)")
    _deselect_cmp_less = EclPrototype(
        "void ecl_region_cmp_deselect_less( ecl_region , ecl_kw , ecl_kw)")
    _deselect_cmp_more = EclPrototype(
        "void ecl_region_cmp_deselect_more( ecl_region , ecl_kw , ecl_kw)")
    _select_islice = EclPrototype(
        "void ecl_region_select_i1i2( ecl_region , int , int )")
    _deselect_islice = EclPrototype(
        "void ecl_region_deselect_i1i2( ecl_region , int , int )")
    _select_jslice = EclPrototype(
        "void ecl_region_select_j1j2( ecl_region , int , int )")
    _deselect_jslice = EclPrototype(
        "void ecl_region_deselect_j1j2( ecl_region , int , int )")
    _select_kslice = EclPrototype(
        "void ecl_region_select_k1k2( ecl_region , int , int )")
    _deselect_kslice = EclPrototype(
        "void ecl_region_deselect_k1k2( ecl_region , int , int )")
    _select_deep_cells = EclPrototype(
        "void ecl_region_select_deep_cells( ecl_region , double )")
    _deselect_deep_cells = EclPrototype(
        "void ecl_region_select_deep_cells( ecl_region , double )")
    _select_shallow_cells = EclPrototype(
        "void ecl_region_select_shallow_cells( ecl_region , double )")
    _deselect_shallow_cells = EclPrototype(
        "void ecl_region_select_shallow_cells( ecl_region , double )")
    _select_small = EclPrototype(
        "void ecl_region_select_small_cells( ecl_region , double )")
    _deselect_small = EclPrototype(
        "void ecl_region_deselect_small_cells( ecl_region , double )")
    _select_large = EclPrototype(
        "void ecl_region_select_large_cells( ecl_region , double )")
    _deselect_large = EclPrototype(
        "void ecl_region_deselect_large_cells( ecl_region , double )")
    _select_thin = EclPrototype(
        "void ecl_region_select_thin_cells( ecl_region , double )")
    _deselect_thin = EclPrototype(
        "void ecl_region_deselect_thin_cells( ecl_region , double )")
    _select_thick = EclPrototype(
        "void ecl_region_select_thick_cells( ecl_region , double )")
    _deselect_thick = EclPrototype(
        "void ecl_region_deselect_thick_cells( ecl_region , double )")
    _select_active = EclPrototype(
        "void ecl_region_select_active_cells( ecl_region )")
    _select_inactive = EclPrototype(
        "void ecl_region_select_inactive_cells( ecl_region )")
    _deselect_active = EclPrototype(
        "void ecl_region_deselect_active_cells( ecl_region )")
    _deselect_inactive = EclPrototype(
        "void ecl_region_deselect_inactive_cells( ecl_region )")
    _select_above_plane = EclPrototype(
        "void ecl_region_select_above_plane( ecl_region  , double* , double* )"
    )
    _select_below_plane = EclPrototype(
        "void ecl_region_select_below_plane( ecl_region  , double* , double* )"
    )
    _deselect_above_plane = EclPrototype(
        "void ecl_region_deselect_above_plane( ecl_region, double* , double* )"
    )
    _deselect_below_plane = EclPrototype(
        "void ecl_region_deselect_below_plane( ecl_region, double* , double* )"
    )
    _select_inside_polygon = EclPrototype(
        "void ecl_region_select_inside_polygon( ecl_region , geo_polygon)")
    _select_outside_polygon = EclPrototype(
        "void ecl_region_select_outside_polygon( ecl_region , geo_polygon)")
    _deselect_inside_polygon = EclPrototype(
        "void ecl_region_deselect_inside_polygon( ecl_region , geo_polygon)")
    _deselect_outside_polygon = EclPrototype(
        "void ecl_region_deselect_outside_polygon( ecl_region , geo_polygon)")
    _set_name = EclPrototype("void ecl_region_set_name( ecl_region , char*)")
    _get_name = EclPrototype("char* ecl_region_get_name( ecl_region )")
    _contains_ijk = EclPrototype(
        "void ecl_region_contains_ijk( ecl_region , int , int , int)")
    _contains_global = EclPrototype(
        "void ecl_region_contains_global( ecl_region, int )")
    _contains_active = EclPrototype(
        "void ecl_region_contains_active( ecl_region , int )")
    _equal = EclPrototype("bool ecl_region_equal( ecl_region , ecl_region )")
    _select_true = EclPrototype(
        "void ecl_region_select_true( ecl_region , ecl_kw)")
    _select_false = EclPrototype(
        "void ecl_region_select_false( ecl_region , ecl_kw)")
    _deselect_true = EclPrototype(
        "void ecl_region_deselect_true( ecl_region , ecl_kw)")
    _deselect_false = EclPrototype(
        "void ecl_region_deselect_false( ecl_region , ecl_kw)")
    _select_from_layer = EclPrototype(
        "void ecl_region_select_from_layer( ecl_region , layer , int , int)")
    _deselect_from_layer = EclPrototype(
        "void ecl_region_deselect_from_layer( ecl_region , layer , int , int)")

    def __init__(self, grid, preselect):
        """
        Create a new region selector for cells in @grid.

        Will create a new region selector to select and deselect the
        cells in the grid given by @grid. The input argument @grid
        should be a EclGrid instance. You can start with either all
        cells, or no cells, selected, depending on the value of
        @preselect.
        """

        self.grid = grid
        self.active_index = False
        c_ptr = self._alloc(grid, preselect)
        super(EclRegion, self).__init__(c_ptr)

    def free(self):
        self._free()

    def __eq__(self, other):
        return self._equal(other)

    def __hash__(self):
        return hash(hash(self.grid) + hash(self.active_index))

    def __deep_copy__(self, memo):
        """
        Creates a deep copy of the current region.
        """
        return self._alloc_copy()

    def __zero__(self):
        global_list = self.getGlobalList()
        if len(global_list) > 0:
            return True
        else:
            return False

    def __iand__(self, other):
        """
        Will perform set intersection operation inplace.

        Will update the current region selection so that the elements
        selected in self are also selected in @other. Bound to the
        inplace & operator, i.e.

           reg1 &= reg2

        will eventually call this method.
        """
        if isinstance(other, EclRegion):
            self._intersect(other)
        else:
            raise TypeError(
                "Ecl region can only intersect with other EclRegion instances")

        return self

    def __isub__(self, other):
        """
        Inplace "subtract" one selection from another.

        Bound to reg -= reg2
        """
        if isinstance(other, EclRegion):
            self._subtract(other)
        else:
            raise TypeError(
                "Ecl region can only subtract with other EclRegion instances")

        return self

    def __ior__(self, other):
        """
        Will perform set operation union in place.

        The current region selection will be updated to contain all
        the elements which are selected either in the current region,
        or in @other; bound to to inplace | operator, so you can write e.g.

            reg1 |= reg2

        to update reg1 with the selections from reg2.
        """
        if isinstance(other, EclRegion):
            self._combine(other)
        else:
            raise TypeError(
                "Ecl region can only be combined with other EclRegion instances"
            )

        return self

    def __iadd__(self, other):
        """
        Combines to regions - see __ior__().
        """
        return self.__ior__(other)

    def __or__(self, other):
        """
        Creates a new region which is the union of @self and other.

        The method will create a new region which selection status is
        given by the logical or of regions @self and @other; the two
        initial regions will not be modified. Bound to the unary |
        operator:

            new_reg = reg1 | reg2

        """
        new_region = self.copy()
        new_region.__ior__(other)
        return new_region

    def __and__(self, other):
        """
        Creates a new region which is the intersection of @self and other.

        The method will create a new region which selection status is
        given by the logical and of regions @self and @other; the two
        initial regions will not be modified. Bound to the unary &
        operator:

            new_reg = reg1 & reg2
        """
        new_region = self.copy()
        new_region.__iand__(other)
        return new_region

    def __add__(self, other):
        """
        Unary add operator for two regions - implemented by __or__().
        """
        return self.__or__(other)

    def __sub__(self, other):
        """
        Unary del operator for two regions.
        """
        new_region = self.copy()
        new_region.__isub__(other)
        return new_region

    def union_with(self, other):
        """
        Will update self with the union of @self and @other.

        See doscumentation of __ior__().
        """
        return self.__ior__(other)

    def intersect_with(self, other):
        """
        Will update self with the intersection of @self and @other.

        See doscumentation of __iand__().
        """
        return self.__iand__(other)

    def copy(self):
        return self.__deep_copy__({})

    def reset(self):
        """
        Clear selections according to constructor argument @preselect.

        Will clear all selections, depending on the value of the
        constructor argument @preselect. If @preselect is true
        everything will be selected after calling reset(), otherwise
        no cells will be selected after calling reset().
        """
        self._reset()

    ##################################################################

    @select_method
    def select_more(self, ecl_kw, limit, intersect=False):
        """
        Select all cells where keyword @ecl_kw is above @limit.

        This method is used to select all the cells where an arbitrary
        field, contained in @ecl_kw, is above a limiting value
        @limit. The EclKW instance must have either nactive or
        nx*ny*nz elements; if this is not satisfied method will fail
        hard. The datatype of @ecl_kw must be numeric,
        i.e. ECL_INT_TYPE, ECL_DOUBLE_TYPE or ECL_FLOAT_TYPE. In the
        example below we select all the cells with water saturation
        above 0.85:

           restart_file = ecl.EclFile( "ECLIPSE.X0067" )
           swat_kw = restart_file["SWAT"][0]
           grid = ecl.EclGrid( "ECLIPSE.EGRID" )
           region = ecl.EclRegion( grid , False )

           region.select_more( swat_kw , 0.85 )

        """
        self._select_more(ecl_kw, limit)

    def deselect_more(self, ecl_kw, limit):
        """
        Deselects cells with value above limit.

        See select_more() for further documentation.
        """
        self._deselect_more(ecl_kw, limit)

    @select_method
    def select_less(self, ecl_kw, limit, intersect=False):
        """
        Select all cells where keyword @ecl_kw is below @limit.

        See select_more() for further documentation.
        """
        self._select_less(ecl_kw, limit)

    def deselect_less(self, ecl_kw, limit):
        """
        Deselect all cells where keyword @ecl_kw is below @limit.

        See select_more() for further documentation.
        """
        self._deselect_less(ecl_kw, limit)

    @select_method
    def select_equal(self, ecl_kw, value, intersect=False):
        """
        Select all cells where @ecl_kw is equal to @value.

        The EclKW instance @ecl_kw must be of size nactive or
        nx*ny*nz, and it must be of integer type; testing for equality
        is not supported for floating point numbers. In the example
        below we select all the cells in PVT regions 2 and 4:

           init_file = ecl.EclFile( "ECLIPSE.INIT" )
           pvtnum_kw = init_file.iget_named_kw( "PVTNUM" , 0 )
           grid = ecl.EclGrid( "ECLIPSE.GRID" )
           region = ecl.EclRegion( grid , False )

           region.select_equal( pvtnum_kw , 2 )
           region.select_equal( pvtnum_kw , 4 )

        """
        if not ecl_kw.data_type.is_int():
            raise ValueError(
                "The select_equal method must have an integer valued keyword - got:%s"
                % ecl_kw.typeName())
        self._select_equal(ecl_kw, value)

    def deselect_equal(self, ecl_kw, value):
        """
        Select all cells where @ecl_kw is equal to @value.

        See select_equal() for further documentation.
        """
        if not ecl_kw.data_type.is_int():
            raise ValueError(
                "The select_equal method must have an integer valued keyword - got:%s"
                % ecl_kw.typeName())
        self._deselect_equal(ecl_kw, value)

    @select_method
    def select_in_range(self, ecl_kw, lower_limit, upper_limit, select=False):
        """
        Select all cells where @ecl_kw is in the half-open interval [ , ).

        Will select all the cells where EclKW instance @ecl_kw has
        value in the half-open interval [@lower_limit ,
        @upper_limit). The input argument @ecl_kw must have size
        nactive or nx*ny*nz, and it must be of type ECL_FLOAT_TYPE.

        The following example will select all cells with porosity in
        the range [0.15,0.20):

           init_file = ecl.EclFile( "ECLIPSE.INIT" )
           poro_kw = init_file.iget_named_kw( "PORO" , 0 )
           grid = ecl.EclGrid( "ECLIPSE.GRID" )
           region = ecl.EclRegion( grid , False )

           region.select_in_range( poro_kw , 0.15, 0.20 )

        """
        self._select_in_interval(ecl_kw, lower_limit, upper_limit)

    def deselect_in_range(self, ecl_kw, lower_limit, upper_limit):
        """
        Deselect all cells where @ecl_kw is in the half-open interval [ , ).

        See select_in_range() for further documentation.
        """
        self._deselect_in_interval(ecl_kw, lower_limit, upper_limit)

    @select_method
    def select_cmp_less(self, kw1, kw2, intersect=False):
        """
        Will select all cells where kw2 < kw1.

        Will compare the ECLIPSE keywords @kw1 and @kw2, and select
        all the cells where the numerical value of @kw1 is less than
        the numerical value of @kw2. The ECLIPSE keywords @kw1 and
        @kw2 must both be of the same size, nactive or nx*ny*nz. In
        addition they must both be of type type ECL_FLOAT_TYPE. In the
        example below we select all the cells where the pressure has
        dropped:

           restart_file = ecl.EclFile("ECLIPSE.UNRST")
           pressure1 = restart_file.iget_named_kw( "PRESSURE" , 0)
           pressure2 = restart_file.iget_named_kw( "PRESSURE" , 100)

           region.select_cmp_less( pressure2 , pressure1)

        """
        self._select_cmp_less(kw1, kw2)

    def deselect_cmp_less(self, kw1, kw2):
        """
        Will deselect all cells where kw2 < kw1.

        See select_cmp_less() for further documentation.
        """
        self._deselect_cmp_less(kw1, kw2)

    @select_method
    def select_cmp_more(self, kw1, kw2, intersect=False):
        """
        Will select all cells where kw2 > kw1.

        See select_cmp_less() for further documentation.
        """
        self._select_cmp_more(kw1, kw2)

    def deselect_cmp_more(self, kw1, kw2):
        """
        Will deselect all cells where kw2 > kw1.

        See select_cmp_less() for further documentation.
        """
        self._deselect_cmp_more(kw1, kw2)

    @select_method
    def select_active(self, intersect=False):
        """
        Will select all the active grid cells.
        """
        self._select_active()

    def deselect_active(self):
        """
        Will deselect all the active grid cells.
        """
        self._deselect_active()

    @select_method
    def select_inactive(self, intersect=False):
        """
        Will select all the inactive grid cells.
        """
        self._select_inactive()

    def deselect_inactive(self):
        """
        Will deselect all the inactive grid cells.
        """
        self._deselect_inactive()

    def select_all(self):
        """
        Will select all the cells.
        """
        self._select_all()

    def deselect_all(self):
        """
        Will deselect all the cells.
        """
        self._deselect_all()

    def clear(self):
        """
        Will deselect all cells.
        """
        self.deselect_all()

    @select_method
    def select_deep(self, depth, intersect=False):
        """
        Will select all cells below @depth.
        """
        self._select_deep_cells(depth)

    def deselect_deep(self, depth):
        """
        Will deselect all cells below @depth.
        """
        self._deselect_deep_cells(depth)

    @select_method
    def select_shallow(self, depth, intersect=False):
        """
        Will select all cells above @depth.
        """
        self._select_shallow_cells(depth)

    def deselect_shallow(self, depth):
        """
        Will deselect all cells above @depth.
        """
        self._deselect_shallow_cells(depth)

    @select_method
    def select_small(self, size_limit, intersect=False):
        """
        Will select all cells smaller than @size_limit.
        """
        self._select_small(size_limit)

    def deselect_small(self, size_limit):
        """
        Will deselect all cells smaller than @size_limit.
        """
        self._deselect_small(size_limit)

    @select_method
    def select_large(self, size_limit, intersect=False):
        """
        Will select all cells larger than @size_limit.
        """
        self._select_large(size_limit)

    def deselect_large(self, size_limit):
        """
        Will deselect all cells larger than @size_limit.
        """
        self._deselect_large(size_limit)

    @select_method
    def select_thin(self, size_limit, intersect=False):
        """
        Will select all cells thinner than @size_limit.
        """
        self._select_thin(size_limit)

    def deselect_thin(self, size_limit):
        """
        Will deselect all cells thinner than @size_limit.
        """
        self._deselect_thin(size_limit)

    @select_method
    def select_thick(self, size_limit, intersect=False):
        """
        Will select all cells thicker than @size_limit.
        """
        self._select_thick(size_limit)

    def deselect_thick(self, size_limit):
        """
        Will deselect all cells thicker than @size_limit.
        """
        self._deselect_thick(size_limit)

    @select_method
    def select_box(self, ijk1, ijk2, intersect=False):
        """
        Will select all cells in box.

        Will select all the the cells in the box given by @ijk1 and
        @ijk2. The two arguments @ijk1 and @ijk2 are tuples (1,j,k)
        representing two arbitrary - diagonally opposed corners - of a
        box. All the elements in @ijk1 and @ijk2 are inclusive, i.e.

           select_box( (10,12,8) , (8 , 16,4) )

        will select the box defined by [8,10] x [12,16] x [4,8].
        """
        self._select_box(ijk1[0], ijk2[0], ijk1[1], ijk2[1], ijk1[2], ijk2[2])

    def deselect_box(self, ijk1, ijk2):
        """
        Will deselect all elements in box.

        See select_box() for further documentation.
        """
        self._deselect_box(ijk1[0], ijk2[0], ijk1[1], ijk2[1], ijk1[2],
                           ijk2[2])

    @select_method
    def select_islice(self, i1, i2, intersect=False):
        """
        Will select all cells with i in [@i1, @i2]. @i1 and @i2 are zero offset.
        """
        self._select_islice(i1, i2)

    def deselect_islice(self, i1, i2):
        """
        Will deselect all cells with i in [@i1, @i2]. @i1 and @i2 are zero offset.
        """
        self._deselect_islice(i1, i2)

    @select_method
    def select_jslice(self, j1, j2, intersect=False):
        """
        Will select all cells with j in [@j1, @j2]. @i1 and @i2 are zero offset.
        """
        self._select_jslice(j1, j2)

    def deselect_jslice(self, j1, j2):
        """
        Will deselect all cells with j in [@j1, @j2]. @i1 and @i2 are zero offset.
        """
        self._deselect_jslice(j1, j2)

    @select_method
    def select_kslice(self, k1, k2, intersect=False):
        """
        Will select all cells with k in [@k1, @k2]. @i1 and @i2 are zero offset.
        """
        self._select_kslice(k1, k2)

    def deselect_kslice(self, k1, k2):
        """
        Will deselect all cells with k in [@k1, @k2]. @i1 and @i2 are zero offset.
        """
        self._deselect_kslice(k1, k2)

    def invert(self):
        """
        Will invert the current selection.
        """
        self._invert_selection()

    def __init_plane_select(self, n, p):
        n_vec = ctypes.cast((ctypes.c_double * 3)(),
                            ctypes.POINTER(ctypes.c_double))
        p_vec = ctypes.cast((ctypes.c_double * 3)(),
                            ctypes.POINTER(ctypes.c_double))
        for i in range(3):
            n_vec[i] = n[i]
            p_vec[i] = p[i]
        return (n_vec, p_vec)

    @select_method
    def select_above_plane(self, n, p, intersect=False):
        """
        Will select all the cells 'above' the plane defined by n & p.

        @n is the surface normal vector of the plane in question and
        @p is a point on the plane surface. The point @p should be
        given in (utm_x , utm_y , tvd) coordinates. The term 'above'
        means that the cell center has a positive distance to the
        plain; correspondingly 'below' means that the cell center has
        a negative disatnce to the plane.
        """
        (n_vec, p_vec) = self.__init_plane_select(n, p)
        self._select_above_plane(n_vec, p_vec)

    @select_method
    def select_below_plane(self, n, p, interscet=False):
        """
        Will select all the cells 'below' the plane defined by n & p.

        See method 'select_above_plane' for further documentation.
        """
        (n_vec, p_vec) = self.__init_plane_select(n, p)
        self._select_below_plane(n_vec, p_vec)

    def deselect_above_plane(self, n, p):
        """
        Will deselect all the cells 'above' the plane defined by n & p.

        See method 'select_above_plane' for further documentation.
        """
        (n_vec, p_vec) = self.__init_plane_select(n, p)
        self._deselect_above_plane(n_vec, p_vec)

    def deselect_below_plane(self, n, p):
        """
        Will deselect all the cells 'below' the plane defined by n & p.

        See method 'select_above_plane' for further documentation.
        """
        (n_vec, p_vec) = self.__init_plane_select(n, p)
        self._deselect_below_plane(n_vec, p_vec)

    @select_method
    def select_inside_polygon(self, points, intersect=False):
        """
        Will select all points inside polygon.

        Will select all points inside polygon specified by input
        variable @points. Points should be a list of two-element
        tuples (x,y). So to select all the points within the rectangle
        bounded by the lower left rectangle (0,0) and upper right
        (100,100) the @points list should be:

           points = [(0,0) , (0,100) , (100,100) ,  (100,0)]

        The elements in the points list should be (utm_x, utm_y)
        values. These values will be compared with the centerpoints of
        the cells in the grid. The selection is based the top k=0
        layer, and then extending this selection to all k values; this
        implies that the selection polygon will effectively be
        translated if the pillars are not vertical.
        """
        self._select_inside_polygon(CPolyline(init_points=points))

    @select_method
    def select_outside_polygon(self, points, intersect=False):
        """
        Will select all points outside polygon.

        See select_inside_polygon for more docuemntation.
        """
        self._select_outside_polygon(CPolyline(init_points=points))

    def deselect_inside_polygon(self, points):
        """
        Will select all points outside polygon.

        See select_inside_polygon for more docuemntation.
        """
        self._deselect_inside_polygon(CPolyline(init_points=points))

    def deselect_outside_polygon(self, points):
        """
        Will select all points outside polygon.

        See select_inside_polygon for more docuemntation.
        """
        self._deselect_outside_polygon(CPolyline(init_points=points))

    @select_method
    def selectTrue(self, ecl_kw, intersect=False):
        """
        Assume that input ecl_kw is a boolean mask.
        """
        self._select_true(ecl_kw)

    @select_method
    def selectFalse(self, ecl_kw, intersect=False):
        """
        Assume that input ecl_kw is a boolean mask.
        """
        self._select_false(ecl_kw)

    @select_method
    def selectFromLayer(self, layer, k, value, intersect=False):
        """Will select all the cells in in @layer with value @value - at
        vertical coordinate @k.

        The input @layer should be of type Layer - from the
        ecl.ecl.faults.layer module. The k value must in the range
        [0,grid.nz) and the dimensions of the layer must correspond
        exactly to nx,ny of the grid.
        """
        grid = self.grid
        if k < 0 or k >= grid.getNZ():
            raise ValueError("Invalid k value:%d - must be in range [0,%d)" %
                             (k, grid.getNZ()))

        if grid.getNX() != layer.getNX():
            raise ValueError("NX dimension mismatch. Grid:%d  layer:%d" %
                             (grid.getNX(), layer.getNX()))

        if grid.getNY() != layer.getNY():
            raise ValueError("NY dimension mismatch. Grid:%d  layer:%d" %
                             (grid.getNY(), layer.getNY()))

        self._select_from_layer(layer, k, value)

    #################################################################

    def scalar_apply_kw(self,
                        target_kw,
                        scalar,
                        func_dict,
                        force_active=False):
        """
        Helper function to apply a function with one scalar arg on target_kw.
        """
        data_type = target_kw.data_type
        if func_dict.has_key(data_type):
            func = func_dict[data_type]
            func(target_kw, scalar, force_active)
        else:
            raise Exception(
                "scalar_apply_kw() only supported for INT/FLOAT/DOUBLE")

    def iadd_kw(self, target_kw, delta_kw, force_active=False):
        """
        The functions iadd_kw(), copy_kw(), set_kw(), scale_kw() and
        shift_kw() are not meant to be used as methods of the
        EclRegion class (altough that is of course perfectly OK) -
        rather a EclRegion instance is passed as an argument to an
        EclKW method, and then that method "flips things around" and
        calls one of these methods with the EclKW instance as
        argument. This applies to all the EclKW methods which take an
        optional "mask" argument.
        """
        if isinstance(delta_kw, EclKW):
            if target_kw.assert_binary(delta_kw):
                self._iadd_kw(target_kw, delta_kw, force_active)
            else:
                raise TypeError("Type mismatch")
        else:
            self.shift_kw(target_kw, delta_kw, force_active=force_active)

    def shift_kw(self, ecl_kw, shift, force_active=False):
        """
        See usage documentation on iadd_kw().
        """
        self.scalar_apply_kw(
            ecl_kw, shift, {
                EclDataType.ECL_INT: self._shift_kw_int,
                EclDataType.ECL_FLOAT: self._shift_kw_float,
                EclDataType.ECL_DOUBLE: self._shift_kw_double
            }, force_active)

    def isub_kw(self, target_kw, delta_kw, force_active=False):
        if isinstance(delta_kw, EclKW):
            if target_kw.assert_binary(delta_kw):
                self._isub_kw(target_kw, delta_kw, force_active)
            else:
                raise TypeError("Type mismatch")
        else:
            self.shift_kw(target_kw, -delta_kw, force_active=force_active)

    def scale_kw(self, ecl_kw, scale, force_active=False):
        """
        See usage documentation on iadd_kw().
        """
        self.scalar_apply_kw(
            ecl_kw, scale, {
                EclDataType.ECL_INT: self._scale_kw_int,
                EclDataType.ECL_FLOAT: self._scale_kw_float,
                EclDataType.ECL_DOUBLE: self._scale_kw_double
            }, force_active)

    def imul_kw(self, target_kw, other, force_active=False):
        if isinstance(other, EclKW):
            if target_kw.assert_binary(other):
                self._imul_kw(target_kw, other)
            else:
                raise TypeError("Type mismatch")
        else:
            self.scale_kw(target_kw, other, force_active)

    def idiv_kw(self, target_kw, other, force_active=False):
        if isinstance(other, EclKW):
            if target_kw.assert_binary(other):
                self._idiv_kw(target_kw, other)
            else:
                raise TypeError("Type mismatch")
        else:
            self.scale_kw(target_kw, 1 / other, force_active)

    def copy_kw(self, target_kw, src_kw, force_active=False):
        """
        See usage documentation on iadd_kw().
        """
        if target_kw.assert_binary(src_kw):
            self._copy_kw(target_kw, src_kw, force_active)
        else:
            raise TypeError("Type mismatch")

    def set_kw(self, ecl_kw, value, force_active=False):
        """
        See usage documentation on iadd_kw().
        """
        self.scalar_apply_kw(
            ecl_kw, value, {
                EclDataType.ECL_INT: self._set_kw_int,
                EclDataType.ECL_FLOAT: self._set_kw_float,
                EclDataType.ECL_DOUBLE: self._set_kw_double
            }, force_active)

    #################################################################

    def ecl_region_instance(self):
        """
        Helper function (attribute) to support run-time typechecking.
        """
        return True

    def getActiveList(self):
        """
        IntVector instance with active indices in the region.
        """
        active_list = self._get_active_list()
        active_list.setParent(self)
        return active_list

    def getGlobalList(self):
        """
        IntVector instance with global indices in the region.
        """
        global_list = self._get_global_list()
        global_list.setParent(self)
        return global_list

    def getIJKList(self):
        """
        WIll return a Python list of (ij,k) tuples for the region.
        """
        global_list = self.getGlobalList()
        ijk_list = []
        for g in global_list:
            ijk_list.append(self.grid.get_ijk(global_index=g))

        return ijk_list

    def contains_ijk(self, i, j, k):
        """
        Will check if the cell given by i,j,k is part of the region.
        """
        return self._contains_ijk(i, j, k)

    def contains_global(self, global_index):
        """
        Will check if the cell given by @global_index is part of the region.
        """
        return self._contains_global(global_index)

    def contains_active(self, active_index):
        """
        Will check if the cell given by @active_index is part of the region.
        """
        return self._contains_active(active_index)

    def kw_index_list(self, ecl_kw, force_active):
        c_ptr = self._get_kw_index_list(ecl_kw, force_active)
        index_list = IntVector.createCReference(c_ptr, self)
        return index_list

    def getName(self):
        return self._get_name()

    def setName(self, name):
        self._set_name(name)
Ejemplo n.º 10
0
class EclRFT(BaseCClass):
    """The EclRFT class contains the information for *one* RFT.

    The ECLIPSE RFT file can contain three different types of RFT like
    objects which are lumped together; the EclRFTClass is a container
    for such objects. The three different object types which can be
    found in an RFT file are:

       RFT: This is old-fashioned RFT which contains measurements of
            saturations for each of the completed cells.

       PLT: This contains production and flow rates for each phase in
            each cell.

       SEGMENT: Not implemented.

    In addition to the measurements specific for RFT and PLT each cell
    has coordinates, pressure and depth.
    """
    TYPE_NAME = "ecl_rft"
    _alloc            = EclPrototype("void* ecl_rft_node_alloc_new( char* , char* , time_t , double)" , bind = False)
    _free             = EclPrototype("void  ecl_rft_node_free( ecl_rft )")
    _get_type         = EclPrototype("int    ecl_rft_node_get_type( ecl_rft )")
    _get_well         = EclPrototype("char*  ecl_rft_node_get_well_name( ecl_rft )")
    _get_date         = EclPrototype("time_t ecl_rft_node_get_date( ecl_rft )")
    _get_size         = EclPrototype("int ecl_rft_node_get_size( ecl_rft )")
    _iget_cell        = EclPrototype("void* ecl_rft_node_iget_cell( ecl_rft )")
    _iget_cell_sorted = EclPrototype("void* ecl_rft_node_iget_cell_sorted( ecl_rft )")
    _sort_cells       = EclPrototype("void* ecl_rft_node_inplace_sort_cells( ecl_rft )")
    _iget_depth       = EclPrototype("double ecl_rft_node_iget_depth( ecl_rft )")
    _iget_pressure    = EclPrototype("double ecl_rft_node_iget_pressure(ecl_rft)")
    _iget_ijk         = EclPrototype("void ecl_rft_node_iget_ijk( ecl_rft , int , int*, int*, int*)")
    _iget_swat        = EclPrototype("double ecl_rft_node_iget_swat(ecl_rft)")
    _iget_sgas        = EclPrototype("double ecl_rft_node_iget_sgas(ecl_rft)")
    _iget_orat        = EclPrototype("double ecl_rft_node_iget_orat(ecl_rft)")
    _iget_wrat        = EclPrototype("double ecl_rft_node_iget_wrat(ecl_rft)")
    _iget_grat        = EclPrototype("double ecl_rft_node_iget_grat(ecl_rft)")
    _lookup_ijk       = EclPrototype("void* ecl_rft_node_lookup_ijk( ecl_rft , int , int , int)")
    _is_RFT           = EclPrototype("bool   ecl_rft_node_is_RFT( ecl_rft )")
    _is_PLT           = EclPrototype("bool   ecl_rft_node_is_PLT( ecl_rft )")
    _is_SEGMENT       = EclPrototype("bool   ecl_rft_node_is_SEGMENT( ecl_rft )")
    _is_MSW           = EclPrototype("bool   ecl_rft_node_is_MSW( ecl_rft )")


    def __init__(self , name , type_string , date , days):
        c_ptr = self._alloc( name , type_string , CTime( date ) , days )
        super(EclRFT , self).__init__( c_ptr )


    def free(self):
        self._free( )

    def __repr__(self):
        rs = []
        rs.append('completed_cells = %d' % len(self))
        rs.append('date = %s' % self.getDate())
        if self.is_RFT():
            rs.append('RFT')
        if self.is_PLT():
            rs.append('PLT')
        if self.is_SEGMENT():
            rs.append('SEGMENT')
        if self.is_MSW():
            rs.append('MSW')
        rstr = ', '.join(rs)
        return self._create_repr(rstr)

    def __len__(self):
        """
        The number of completed cells in this RFT.
        """
        return self._get_size( )

    def is_RFT(self):
        """
        Is instance an RFT; in that case all the cells will be EclRFTCell instances.
        """
        return self._is_RFT( )

    def is_PLT(self):
        """
        Is instance a PLT; in that case all the cells will be EclPLTCell instances.
        """
        return self._is_PLT( )

    def is_SEGMENT(self):
        """
        Is this a SEGMENT - not implemented.
        """
        return self._is_SEGMENT( )

    def is_MSW(self):
        """
        Is this well a MSW well. Observe that the test ONLY applies to PLTs.
        """
        return self._is_MSW( )


    def get_well_name(self):
        """
        The name of the well we are considering.
        """
        return self._get_well( )

    def get_date(self):
        """
        The date when this RFT/PLT/... was recorded.
        """
        ct = CTime(self._get_date( ))
        return ct.date()

    def __cell_ref( self , cell_ptr ):
        if self.is_RFT():
            return EclRFTCell.createCReference( cell_ptr , self )
        elif self.is_PLT():
            return EclPLTCell.createCReference( cell_ptr , self )
        else:
            raise NotImplementedError("Only RFT and PLT cells are implemented")


    def assert_cell_index( self , index ):
        if isinstance( index , int):
            length = self.__len__()
            if index < 0 or index >= length:
                raise IndexError
        else:
            raise TypeError("Index should be integer type")


    def __getitem__(self , index):
        """Implements the [] operator to return the cells.

        To get the object related to cell nr 5:

           cell = rft[4]

        The return value from the __getitem__() method is either an
        EclRFTCell instance or a EclPLTCell instance, depending on the
        type of this particular RFT object.
        """
        self.assert_cell_index( index )
        cell_ptr = self._iget_cell( index )
        return self.__cell_ref( cell_ptr )


    def iget( self , index ):
        return self[index]


    def iget_sorted( self , index ):
        """
        Will return the cell nr @index in the list of sorted cells.

        See method sort() for further documentation.
        """
        self.assert_cell_index( index )
        cell_ptr = self._iget_cell_sorted( index )
        return self.__cell_ref( cell_ptr )


    def sort(self):
        """
        Will sort cells in RFT; currently only applies to MSW wells.

        By default the cells in the RFT come in the order they are
        specified in the ECLIPSE input file; that is not necessarily
        in a suitable order. In the case of MSW wells it is possible
        to sort the connections after distance along the wellpath. To
        access the cells in sort order you have two options:

           1. Sort the cells using the sort() method, and then
              subsequently access them sequentially:

                rft.sort()
                for cell in rft:
                    print cell

           2. Let the rft object stay unsorted, but access the cells
              using the iget_sorted() method:

                 for i in range(len(rft)):
                     cell = rft.iget_sorted( i )

        Currently only MSW/PLTs are sorted, based on the CONLENST
        keyword; for other wells the sort() method does nothing.
        """
        self._sort_cells( )


    # ijk are zero offset
    def ijkget( self , ijk ):
        """
        Will look up the cell based on (i,j,k).

        If the cell (i,j,k) is not part of this RFT/PLT None will be
        returned. The (i,j,k) input values should be zero offset,
        i.e. you must subtract 1 from the (i,j,k) values given in the ECLIPSE input.
        """
        cell_ptr = self._lookup_ijk( ijk[0] , ijk[1] , ijk[2])
        if cell_ptr:
            return self.__cell_ref( cell_ptr )
        else:
            return None
Ejemplo n.º 11
0
Archivo: layer.py Proyecto: pgdr/libecl
class Layer(BaseCClass):
    TYPE_NAME = "layer"
    _alloc = EclPrototype("void* layer_alloc(int,  int)", bind=False)
    _copy = EclPrototype("void  layer_memcpy(layer, layer)")
    _free = EclPrototype("void  layer_free(layer)")
    _get_nx = EclPrototype("int   layer_get_nx(layer)")
    _get_ny = EclPrototype("int   layer_get_ny(layer)")
    _set_cell = EclPrototype(
        "void  layer_iset_cell_value(layer, int, int, int)")
    _get_cell = EclPrototype("int   layer_iget_cell_value(layer, int, int)")
    _get_bottom_barrier = EclPrototype(
        "bool  layer_iget_bottom_barrier(layer, int, int)")
    _get_left_barrier = EclPrototype(
        "bool  layer_iget_left_barrier(layer, int, int)")
    _cell_contact = EclPrototype(
        "bool  layer_cell_contact(layer, int, int, int, int)")
    _add_barrier = EclPrototype("void  layer_add_barrier(layer, int, int)")
    _add_ijbarrier = EclPrototype(
        "void  layer_add_ijbarrier(layer, int, int, int, int)")
    _add_interp_barrier = EclPrototype(
        "void  layer_add_interp_barrier(layer, int, int)")
    _clear_cells = EclPrototype("void  layer_clear_cells(layer)")
    _assign = EclPrototype("void  layer_assign(layer, int)")
    _cell_sum = EclPrototype("int   layer_get_cell_sum(layer)")
    _update_connected = EclPrototype(
        "void  layer_update_connected_cells(layer,int,int,int,int)")
    _cells_equal = EclPrototype(
        "void  layer_cells_equal(layer, int,int_vector,int_vector)")
    _count_equal = EclPrototype("int   layer_count_equal(layer, int)")
    _active_cell = EclPrototype("bool  layer_iget_active(layer, int,int)")
    _update_active = EclPrototype(
        "bool  layer_update_active(layer, ecl_grid, int)")

    def __init__(self, nx, ny):
        c_ptr = self._alloc(nx, ny)
        if c_ptr:
            super(Layer, self).__init__(c_ptr)
        else:
            raise ValueError("Invalid input - no Layer object created")

    @classmethod
    def copy(cls, src):
        layer = Layer(src.getNX(), src.getNY())
        layer._copy(layer, src)
        return layer

    def _assert_ij(self, i, j):
        if i < 0 or i >= self.getNX():
            raise ValueError("Invalid layer i:%d" % i)

        if j < 0 or j >= self.getNY():
            raise ValueError("Invalid layer j:%d" % j)

    def __unpack_index(self, index):
        try:
            (i, j) = index
        except TypeError:
            raise ValueError("Index:%s is invalid - must have two integers" %
                             str(index))

        self._assert_ij(i, j)

        return (i, j)

    def __setitem__(self, index, value):
        (i, j) = self.__unpack_index(index)
        self._set_cell(i, j, value)

    def active_cell(self, i, j):
        self._assert_ij(i, j)
        return self._active_cell(i, j)

    def update_active(self, grid, k):
        if grid.getNX() != self.getNX():
            raise ValueError("NX dimension mismatch. Grid:%d  layer:%d" %
                             (grid.getNX(), self.getNX()))

        if grid.getNY() != self.getNY():
            raise ValueError("NY dimension mismatch. Grid:%d  layer:%d" %
                             (grid.getNY(), self.getNY()))

        if k >= grid.getNZ():
            raise ValueError("K value invalid: Grid range [0,%d)" %
                             grid.getNZ())

        self._update_active(grid, k)

    def __getitem__(self, index):
        (i, j) = self.__unpack_index(index)
        return self._get_cell(i, j)

    def bottom_barrier(self, i, j):
        self._assert_ij(i, j)
        return self._get_bottom_barrier(i, j)

    def left_barrier(self, i, j):
        self._assert_ij(i, j)
        return self._get_left_barrier(i, j)

    def get_nx(self):
        return self._get_nx()

    @property
    def nx(self):
        return self._get_nx()

    def get_ny(self):
        return self._get_ny()

    @property
    def ny(self):
        return self._get_ny()

    def free(self):
        self._free()

    def cell_contact(self, p1, p2):
        i1, j1 = p1
        i2, j2 = p2

        if not 0 <= i1 < self.getNX():
            raise IndexError("Invalid i1:%d" % i1)

        if not 0 <= i2 < self.getNX():
            raise IndexError("Invalid i2:%d" % i2)

        if not 0 <= j1 < self.getNY():
            raise IndexError("Invalid i1:%d" % j1)

        if not 0 <= j2 < self.getNY():
            raise IndexError("Invalid i2:%d" % j2)

        return self._cell_contact(i1, j1, i2, j2)

    def add_interp_barrier(self, c1, c2):
        self._add_interp_barrier(c1, c2)

    def add_polyline_barrier(self, polyline, grid, k):
        if len(polyline) > 1:
            for i in range(len(polyline) - 1):
                x1, y1 = polyline[i]
                x2, y2 = polyline[i + 1]

                c1 = grid.findCellCornerXY(x1, y1, k)
                c2 = grid.findCellCornerXY(x2, y2, k)

                self.addInterpBarrier(c1, c2)

    def add_fault_barrier(self, fault, K, link_segments=True):
        fault_layer = fault[K]
        num_lines = len(fault_layer)
        for index, fault_line in enumerate(fault_layer):
            for segment in fault_line:
                c1, c2 = segment.getCorners()
                self._add_barrier(c1, c2)

            if index < num_lines - 1:
                next_line = fault_layer[index + 1]
                next_segment = next_line[0]
                next_c1, next_c2 = next_segment.getCorners()

                if link_segments:
                    self.addInterpBarrier(c2, next_c1)

    def add_ij_barrier(self, ij_list):
        if len(ij_list) < 2:
            raise ValueError("Must have at least two (i,j) points")

        nx = self.getNX()
        ny = self.getNY()
        p1 = ij_list[0]
        i1, j1 = p1
        for p2 in ij_list[1:]:
            i2, j2 = p2
            if i1 == i2 or j1 == j2:
                if not 0 <= i2 <= nx:
                    raise ValueError(
                        "i value:%d invalid. Valid range: [0,%d] " % (i1, i2))

                if not 0 <= j2 <= ny:
                    raise ValueError(
                        "i value:%d invalid. Valid range: [0,%d] " % (j1, j2))

                self._add_ijbarrier(i1, j1, i2, j2)
                p1 = p2
                i1, j1 = p1
            else:
                raise ValueError("Must have i1 == i2 or j1 == j2")

    def cell_sum(self):
        return self._cell_sum()

    def clear_cells(self):
        """
        Will reset all cell and edge values to zero. Barriers will be left
        unchanged.
        """
        self._clear_cells()

    def assign(self, value):
        """
        Will set the cell value to @value in all cells. Barriers will not be changed
        """
        self._assign(value)

    def update_connected(self, ij, new_value, org_value=None):
        """
        Will update cell value of all cells in contact with cell ij to the
        value @new_value. If org_value is not supplied, the current
        value in cell ij is used.
        """
        if org_value is None:
            org_value = self[ij]

        if self[ij] == org_value:
            self._update_connected(ij[0], ij[1], org_value, new_value)
        else:
            raise ValueError("Cell %s is not equal to %d \n" % (ij, org_value))

    def cells_equal(self, value):
        """
        Will return a list [(i1,j1),(i2,j2), ...(in,jn)] of all cells with value @value.
        """
        i_list = IntVector()
        j_list = IntVector()
        self._cells_equal(value, i_list, j_list)
        ij_list = []
        for (i, j) in zip(i_list, j_list):
            ij_list.append((i, j))
        return ij_list

    def count_equal(self, value):
        return self._count_equal(value)
Ejemplo n.º 12
0
class EclPLTCell(RFTCell):
    TYPE_NAME = "ecl_plt_cell"
    _alloc_PLT = EclPrototype(
        "void* ecl_rft_cell_alloc_PLT( int, int , int , double , double , double, double , double, double , double , double , double , double , double )",
        bind=False)
    _get_orat = EclPrototype("double ecl_rft_cell_get_orat( ecl_plt_cell )")
    _get_grat = EclPrototype("double ecl_rft_cell_get_grat( ecl_plt_cell )")
    _get_wrat = EclPrototype("double ecl_rft_cell_get_wrat( ecl_plt_cell )")

    _get_flowrate = EclPrototype(
        "double ecl_rft_cell_get_flowrate( ecl_plt_cell )")
    _get_oil_flowrate = EclPrototype(
        "double ecl_rft_cell_get_oil_flowrate( ecl_plt_cell )")
    _get_gas_flowrate = EclPrototype(
        "double ecl_rft_cell_get_gas_flowrate( ecl_plt_cell )")
    _get_water_flowrate = EclPrototype(
        "double ecl_rft_cell_get_water_flowrate( ecl_plt_cell )")

    _get_conn_start = EclPrototype(
        "double ecl_rft_cell_get_connection_start( ecl_plt_cell )")
    _get_conn_end = EclPrototype(
        "double ecl_rft_cell_get_connection_end( ecl_plt_cell )")

    def __init__(self, i, j, k, depth, pressure, orat, grat, wrat, conn_start,
                 conn_end, flowrate, oil_flowrate, gas_flowrate,
                 water_flowrate):
        c_ptr = self._alloc_PLT(i, j, k, depth, pressure, orat, grat, wrat,
                                conn_start, conn_end, flowrate, oil_flowrate,
                                gas_flowrate, water_flowrate)
        super(EclPLTCell, self).__init__(c_ptr)

    @property
    def orat(self):
        return self._get_orat()

    @property
    def grat(self):
        return self._get_grat()

    @property
    def wrat(self):
        return self._get_wrat()

    @property
    def conn_start(self):
        """Will return the length from wellhead(?) to connection.

        For MSW wells this property will return the distance from a
        fixed point (wellhead) to the current connection. This value
        will be used to sort the completed cells along the well
        path. In the case of non MSW wells this will just return a
        fixed default value.
        """
        return self._get_conn_start()

    @property
    def conn_end(self):
        """Will return the length from wellhead(?) to connection end.

        For MSW wells this property will return the distance from a
        fixed point (wellhead) to the current connection end. This value
        will be used to sort the completed cells along the well
        path. In the case of non MSW wells this will just return a
        fixed default value.
        """
        return self._get_conn_end()

    @property
    def flowrate(self):
        return self._get_flowrate()

    @property
    def oil_flowrate(self):
        return self._get_oil_flowrate()

    @property
    def gas_flowrate(self):
        return self._get_gas_flowrate()

    @property
    def water_flowrate(self):
        return self._get_water_flowrate()
Ejemplo n.º 13
0
#  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.

from ecl.ecl import EclPrototype

__arglist = 'double, double, double, '
__arglist += 'ecl_grid, ecl_file, '
__arglist += 'ecl_kw, ecl_kw, ecl_kw, ecl_kw, ecl_kw, ecl_kw'
_phase_deltag = EclPrototype("double ecl_grav_phase_deltag(%s)" % __arglist)


def phase_deltag(xyz, grid, init, sat1, rho1, porv1, sat2, rho2, porv2):
    return _phase_deltag(xyz[0], xyz[1], xyz[2], grid.c_ptr, init.c_ptr,
                         sat1.c_ptr, rho1.c_ptr, porv1.c_ptr, sat2.c_ptr,
                         rho2.c_ptr, porv2.c_ptr)


def deltag(xyz, grid, init_file, restart_file1, restart_file2):
    """
    1. All restart files should have water, i.e. the SWAT keyword.
    2. All phases present in the restart file should also be present as densities,
       in addition the model must contain one additional phase - which should have a density.
    3. The restart files can never contain oil saturation.
    """
Ejemplo n.º 14
0
class EclKW(BaseCClass):
    """
    The EclKW class contains the information from one ECLIPSE keyword.

    The ecl_kw type is the lowest level type in the libecl C library,
    and all the other datatypes like e.g. ecl_grid and ecl_sum are
    based on collections of ecl_kw instances, and interpreting the
    content of the ecl_kw keywords.

    Many of the special __xxx___() functions have been implemented, so
    that the EclKW class supports both numerical operations and also
    [] based lookup. Many of the methods accept an optional @mask
    argument; this should be a EclRegion instance which can be used to
    limit the operation to a part of the EclKW.
    """

    int_kw_set = set([
        "PVTNUM", "FIPNUM", "EQLNUM", "FLUXNUM", "MULTNUM", "ACTNUM",
        "SPECGRID", "REGIONS"
    ])

    TYPE_NAME = "ecl_kw"
    _alloc_new = EclPrototype(
        "void* ecl_kw_alloc_python(char*, int, ecl_data_type)", bind=False)
    _fread_alloc = EclPrototype("ecl_kw_obj ecl_kw_fread_alloc(fortio)",
                                bind=False)
    _load_grdecl = EclPrototype(
        "ecl_kw_obj ecl_kw_fscanf_alloc_grdecl_dynamic_python(FILE, char*, bool, ecl_data_type)",
        bind=False)
    _fseek_grdecl = EclPrototype(
        "bool     ecl_kw_grdecl_fseek_kw(char*, bool, FILE)", bind=False)

    _sub_copy = EclPrototype(
        "ecl_kw_obj ecl_kw_alloc_sub_copy(ecl_kw, char*, int, int)")
    _copyc = EclPrototype("ecl_kw_obj ecl_kw_alloc_copy(ecl_kw)")
    _slice_copyc = EclPrototype(
        "ecl_kw_obj ecl_kw_alloc_slice_copy(ecl_kw, int, int, int)")
    _fprintf_grdecl = EclPrototype(
        "void     ecl_kw_fprintf_grdecl(ecl_kw, FILE)")
    _fprintf_data = EclPrototype(
        "void     ecl_kw_fprintf_data(ecl_kw, char*, FILE)")

    _get_size = EclPrototype("int      ecl_kw_get_size(ecl_kw)")
    _get_fortio_size = EclPrototype("size_t   ecl_kw_fortio_size(ecl_kw)")
    _get_type = EclPrototype("ecl_type_enum ecl_kw_get_type(ecl_kw)")
    _iget_char_ptr = EclPrototype("char*    ecl_kw_iget_char_ptr(ecl_kw, int)")
    _iset_char_ptr = EclPrototype(
        "void     ecl_kw_iset_char_ptr(ecl_kw, int, char*)")
    _iget_string_ptr = EclPrototype(
        "char*    ecl_kw_iget_string_ptr(ecl_kw, int)")
    _iset_string_ptr = EclPrototype(
        "void     ecl_kw_iset_string_ptr(ecl_kw, int, char*)")
    _iget_bool = EclPrototype("bool     ecl_kw_iget_bool(ecl_kw, int)")
    _iset_bool = EclPrototype("bool     ecl_kw_iset_bool(ecl_kw, int, bool)")
    _iget_int = EclPrototype("int      ecl_kw_iget_int(ecl_kw, int)")
    _iget_double = EclPrototype("double   ecl_kw_iget_double(ecl_kw, int)")
    _iget_float = EclPrototype("float    ecl_kw_iget_float(ecl_kw, int)")
    _float_ptr = EclPrototype("float*   ecl_kw_get_float_ptr(ecl_kw)")
    _int_ptr = EclPrototype("int*     ecl_kw_get_int_ptr(ecl_kw)")
    _double_ptr = EclPrototype("double*  ecl_kw_get_double_ptr(ecl_kw)")
    _free = EclPrototype("void     ecl_kw_free(ecl_kw)")
    _fwrite = EclPrototype("void     ecl_kw_fwrite(ecl_kw, fortio)")
    _get_header = EclPrototype("char*    ecl_kw_get_header (ecl_kw)")
    _set_header = EclPrototype(
        "void     ecl_kw_set_header_name (ecl_kw, char*)")
    _get_data_type = EclPrototype(
        "ecl_data_type_obj ecl_kw_get_data_type_python(ecl_kw)")

    _int_sum = EclPrototype("int      ecl_kw_element_sum_int(ecl_kw)")
    _float_sum = EclPrototype("double   ecl_kw_element_sum_float(ecl_kw)")
    _iadd = EclPrototype("void     ecl_kw_inplace_add(ecl_kw, ecl_kw)")
    _imul = EclPrototype("void     ecl_kw_inplace_mul(ecl_kw, ecl_kw)")
    _idiv = EclPrototype("void     ecl_kw_inplace_div(ecl_kw, ecl_kw)")
    _isub = EclPrototype("void     ecl_kw_inplace_sub(ecl_kw, ecl_kw)")
    _iabs = EclPrototype("void     ecl_kw_inplace_abs(ecl_kw)")
    _equal = EclPrototype("bool     ecl_kw_equal(ecl_kw, ecl_kw)")
    _equal_numeric = EclPrototype(
        "bool     ecl_kw_numeric_equal(ecl_kw, ecl_kw, double, double)")

    _assert_binary = EclPrototype(
        "bool     ecl_kw_size_and_numeric_type_equal(ecl_kw, ecl_kw)")
    _scale_int = EclPrototype("void     ecl_kw_scale_int(ecl_kw, int)")
    _scale_float = EclPrototype(
        "void     ecl_kw_scale_float_or_double(ecl_kw, double)")
    _shift_int = EclPrototype("void     ecl_kw_shift_int(ecl_kw, int)")
    _shift_float = EclPrototype(
        "void     ecl_kw_shift_float_or_double(ecl_kw, double)")
    _copy_data = EclPrototype("void     ecl_kw_memcpy_data(ecl_kw, ecl_kw)")
    _set_int = EclPrototype("void     ecl_kw_scalar_set_int(ecl_kw, int)")
    _set_float = EclPrototype(
        "void     ecl_kw_scalar_set_float_or_double(ecl_kw, double)")

    _max_min_int = EclPrototype(
        "void     ecl_kw_max_min_int(ecl_kw, int*, int*)")
    _max_min_float = EclPrototype(
        "void     ecl_kw_max_min_float(ecl_kw, float*, float*)")
    _max_min_double = EclPrototype(
        "void     ecl_kw_max_min_double(ecl_kw, double*, double*)")
    _fix_uninitialized = EclPrototype(
        "void     ecl_kw_fix_uninitialized(ecl_kw,int, int, int, int*)")
    _first_different = EclPrototype(
        "int      ecl_kw_first_different(ecl_kw, ecl_kw, int, double, double)")
    _resize = EclPrototype("void     ecl_kw_resize(ecl_kw, int)")

    @classmethod
    def createCReference(cls, c_ptr, parent=None):
        ecl_kw = super(EclKW, cls).createCReference(c_ptr, parent=parent)
        if ecl_kw is None:
            raise ValueError("Failed to create EclKW instance")

        ecl_kw.__private_init()
        return ecl_kw

    @classmethod
    def createPythonObject(cls, c_ptr):
        ecl_kw = super(EclKW, cls).createPythonObject(c_ptr)
        if ecl_kw is None:
            raise ValueError("Failed to create EclKW instance")

        ecl_kw.__private_init()
        return ecl_kw

    @classmethod
    def add_int_kw(cls, kw):
        """Will add keyword @kw to the standard set of integer keywords."""
        cls.int_kw_set.add(kw)

    @classmethod
    def del_int_kw(cls, kw):
        """Will remove keyword @kw from the standard set of integer keywords."""
        cls.int_kw_set.discard(kw)

    @classmethod
    def int_keywords(cls):
        """Will return the current set of integer keywords."""
        return cls.int_kw_set

    def slice_copy(self, slice_range):
        (start, stop, step) = slice_range.indices(len(self))
        if stop > start:
            return self._slice_copyc(start, stop, step)
        else:
            return None

    def copy(self):
        """
        Will create a deep copy of the current kw instance.
        """
        return self._copyc()

    @classmethod
    def read_grdecl(cls, fileH, kw, strict=True, ecl_type=None):
        """
        Function to load an EclKW instance from a grdecl formatted filehandle.

        This constructor can be used to load an EclKW instance from a
        grdecl formatted file; the input files for petrophysical
        properties are typically given as grdecl files.

        The @file argument should be a Python filehandle to an open
        file. The @kw argument should be the keyword header you are
        searching for, e.g. "PORO" or "PVTNUM"[1], the method will
        then search forward through the file to look for this @kw. If
        the keyword can not be found the method will return None. The
        searching will start from the current position in the file; so
        if you want to reposition the file pointer you should use the
        seek() method of the file object first.

        Observe that there is a strict 8 character limit on @kw -
        altough you could in principle use an arbitrary external
        program to create grdecl files with more than 8 character
        length headers, this implementation will refuse to even try
        loading them. In that case you will have to rename the
        keywords in your file - sorry. A TypeError exception
        will be raised if @kw has more than 8 characters.

        The implementation in ert can read integer and float type
        keywords from grdecl files; however the grdecl files have no
        datatype header, and it is impossible to determine the type
        reliably by inspection. Hence the type must be known when
        reading the file. The algorithm for specifying type, in order
        of presedence, is as follows:

        1. The optional argument @ecl_type can be used to specify
           the type:

           special_int_kw = EclKW.read_grdecl(fileH, 'INTKW', ecl_type=ECL_INT)

           If ecl_type is different from ECL_INT or
           ECL_FLOAT a TypeError exception will be raised.

           If ecl_type == None (the default), the method will continue
           to point 2. or 3. to determine the correct type.


        2. If the keyword is included in the set built in set
           'int_kw_set' the type will be ECL_INT_TYPE.

           pvtnum_kw = EclKW.read_grdecl(fileH, 'PVTNUM')

           Observe that (currently) no case conversions take place
           when checking the 'int_kw_set'. The current built in set is
           accesible through the int_kw property.


        3. Otherwise the default is float, i.e. ECL_FLOAT.

           EclKw reads grdecl with EclDataType
           poro_kw = EclKW.read_grdecl(fileH, 'PORO')


        Observe that since the grdecl files are quite weakly
        structured it is difficult to verify the integrity of the
        files, malformed input might therefor pass unnoticed before
        things blow up at a later stage.

        [1]: It is possible, but not recommended, to pass in None for
        @kw, in which case the method will load the first keyword
        it finds in the file.
        """

        cfile = CFILE(fileH)
        if kw:
            if len(kw) > 8:
                raise TypeError(
                    "Sorry keyword:%s is too long, must be eight characters or less."
                    % kw)

        if ecl_type is None:
            if cls.int_kw_set.__contains__(kw):
                ecl_type = EclDataType.ECL_INT
            else:
                ecl_type = EclDataType.ECL_FLOAT

        ecl_type = warn_and_cast_data_type(ecl_type)

        if not isinstance(ecl_type, EclDataType):
            raise TypeError("Expected EclDataType, was: %s" % type(ecl_type))

        if not ecl_type in [EclDataType.ECL_FLOAT, EclDataType.ECL_INT]:
            raise ValueError("The type:%s is invalid when loading keyword:%s" %
                             (ecl_type.type_name, kw))

        return cls._load_grdecl(cfile, kw, strict, ecl_type)

    @classmethod
    def fseek_grdecl(cls, fileH, kw, rewind=False):
        """
        Will search through the open file and look for string @kw.

        If the search succeeds the function will return and the file
        pointer will be positioned at the start of the kw, if the
        search fails the function will return false and the file
        pointer will be repositioned at the position it had prior to
        the call.

        Only @kw instances which are found at the beginning of a line
        (with optional leading space characters) are considered,
        i.e. searching for the string PERMX in the cases below will
        fail:

           -- PERMX
           EQUIL   PERMX /


        The function will start searching from the current position in
        the file and forwards, if the optional argument @rewind is
        true the function rewind to the beginning of the file and
        search from there after the initial search.
        """
        cfile = CFILE(fileH)
        return cls._fseek_grdecl(kw, rewind, cfile)

    @classmethod
    def fread(cls, fortio):
        """
        Will read a new EclKW instance from the open FortIO file.
        """
        return cls._fread_alloc(fortio)

    def free(self):
        self._free()

    def __repr__(self):
        si = len(self)
        nm = self.getName()
        mm = 'type=%s' % str(self.getEclType())
        if self.isNumeric():
            mi, ma = self.getMinMax()
            mm = 'min=%.2f, max=%.2f' % (mi, ma)
        ad = self._ad_str()
        fmt = 'EclKW(size=%d, name="%s", %s) %s'
        return fmt % (si, nm, mm, ad)

    def __init__(self, name, size, data_type):
        """Creates a brand new EclKW instance.

        This method will create a grand spanking new EclKW
        instance. The instance will get name @name @size elements and
        datatype @data_type. Using this method you could create a SOIL
        keyword with:

           soil_kw = EclKW("SOIL", 10000, ECL_FLOAT_TYPE)

        """
        if len(name) > 8:
            raise ValueError(
                "Sorry - maximum eight characters in keyword name")

        data_type = warn_and_cast_data_type(data_type)

        if not isinstance(data_type, EclDataType):
            raise TypeError("Expected an EclDataType, received: %s" %
                            type(data_type))

        c_ptr = self._alloc_new(name, size, data_type)
        super(EclKW, self).__init__(c_ptr)
        self.__private_init()

    def __private_init(self):
        self.data_ptr = None

        if self.data_type.is_int():
            self.data_ptr = self._int_ptr()
            self.dtype = numpy.int32
            self.str_fmt = "%8d"
        elif self.data_type.is_float():
            self.data_ptr = self._float_ptr()
            self.dtype = numpy.float32
            self.str_fmt = "%13.4f"
        elif self.data_type.is_double():
            self.data_ptr = self._double_ptr()
            self.dtype = numpy.float64
            self.str_fmt = "%13.4f"
        else:
            # Iteration not supported for CHAR / BOOL
            self.data_ptr = None
            self.dtype = None
            if self.data_type.is_char():
                self.str_fmt = "%8s"
            elif self.data_type.is_bool():
                self.str_fmt = "%d"
            elif self.data_type.is_mess():
                self.str_fmt = "%s"  #"Message type"
            elif self.data_type.is_string():
                self.str_fmt = "%" + str(self.data_type.element_size) + "s"
            else:
                raise ValueError("Unknown EclDataType (%s)!" %
                                 self.data_type.type_name)

    def sub_copy(self, offset, count, new_header=None):
        """
        Will create a new block copy of the src keyword.

        If @new_header == None the copy will get the same 'name' as
        the src, otherwise the keyword will get the @new_header as
        header.

        The copy will start at @block of the src keyword and copy
        @count elements; a negative value of @count is interpreted as
        'the rest of the elements'

           new1 = src.sub_copy(0, 10, new_header="NEW1")
           new2 = src.sub_copy(10, -1, new_header="NEW2")

        If the count or index arguments are in some way invalid the
        method will raise IndexError.
        """
        if offset < 0 or offset >= len(self):
            raise IndexError("Offset:%d invalid - valid range:[0,%d)" %
                             (offset, len(self)))

        if offset + count > len(self):
            raise IndexError("Invalid value of (offset + count):%d" %
                             (offset + count))

        return self._sub_copy(new_header, offset, count)

    def is_numeric(self):
        """
        Will check if the keyword contains numeric data, i.e int, float or double.
        """
        return self.data_type.is_numeric()

    def ecl_kw_instance(self):
        return True

    def __len__(self):
        """
        Returns the number of elements. Implements len()
        """
        return self._get_size()

    def __deep_copy__(self, memo):
        """
        Python special routine used to perform deep copy.
        """
        ecl_kw = self.copy()
        return ecl_kw

    def __getitem__(self, index):
        """
        Function to support index based lookup: y = kw[index]
        """
        if isinstance(index, int):
            length = len(self)
            if index < 0:
                # We allow one level of negative indexing
                index += len(self)

            if index < 0 or index >= length:
                raise IndexError
            else:
                if self.data_ptr:
                    return self.data_ptr[index]
                else:
                    if self.data_type.is_bool():
                        return self._iget_bool(index)
                    elif self.data_type.is_char():
                        return self._iget_char_ptr(index)
                    elif self.data_type.is_string():
                        return self._iget_string_ptr(index)
                    else:
                        raise TypeError("Internal implementation error ...")
        elif isinstance(index, slice):
            return self.slice_copy(index)
        else:
            raise TypeError("Index should be integer type")

    def __setitem__(self, index, value):
        """
        Function to support index based assignment: kw[index] = value
        """
        if isinstance(index, int):
            length = len(self)
            if index < 0:
                # Will only wrap backwards once
                index = len(self) + index

            if index < 0 or index >= length:
                raise IndexError
            else:
                if self.data_ptr:
                    self.data_ptr[index] = value
                else:
                    if self.data_type.is_bool():
                        self._iset_bool(index, value)
                    elif self.data_type.is_char():
                        return self._iset_char_ptr(index, value)
                    elif self.data_type.is_string():
                        return self._iset_string_ptr(index, value)
                    else:
                        raise SystemError("Internal implementation error ...")
        elif isinstance(index, slice):
            (start, stop, step) = index.indices(len(self))
            index = start
            while index < stop:
                self[index] = value
                index += step
        else:
            raise TypeError("Index should be integer type")

    #################################################################

    def __IMUL__(self, factor, mul=True):
        if self.isNumeric():
            if hasattr(factor, "ecl_kw_instance"):
                if self.assert_binary(factor):
                    if mul:
                        self._imul(factor)
                    else:
                        self._idiv(factor)
                else:
                    raise TypeError("Type mismatch")
            else:
                if not mul:
                    factor = 1.0 / factor

                if self.data_type.is_int():
                    if isinstance(factor, int):
                        self._scale_int(factor)
                    else:
                        raise TypeError("Type mismatch")
                else:
                    if isinstance(factor, int) or isinstance(factor, float):
                        self._scale_float(factor)
                    else:
                        raise TypeError(
                            "Only muliplication with scalar supported")
        else:
            raise TypeError("Not numeric type")

        return self

    def __IADD__(self, delta, add=True):
        if self.isNumeric():
            if type(self) == type(delta):
                if self.assert_binary(delta):
                    if add:
                        self._iadd(delta)
                    else:
                        self._isub(delta)
                else:
                    raise TypeError("Type / size mismatch")
            else:
                if add:
                    sign = 1
                else:
                    sign = -1

                if self.data_type.is_int():
                    if isinstance(delta, int):
                        self._shift_int(delta * sign)
                    else:
                        raise TypeError("Type mismatch")
                else:
                    if isinstance(delta, int) or isinstance(delta, float):
                        self._shift_float(
                            delta * sign
                        )  # Will call the _float() or _double() function in the C layer.
                    else:
                        raise TypeError("Type mismatch")
        else:
            raise TypeError("Type / size mismatch")

        return self

    def __iadd__(self, delta):
        return self.__IADD__(delta, True)

    def __isub__(self, delta):
        return self.__IADD__(delta, False)

    def __imul__(self, delta):
        return self.__IMUL__(delta, True)

    def __idiv__(self, delta):
        return self.__IMUL__(delta, False)

    #################################################################

    def __abs__(self):
        if self.isNumeric():
            copy = self.copy()
            copy._iabs()
            return copy
        else:
            raise TypeError(
                "The __abs__() function is only implemented for numeric types")

    def __add__(self, delta):
        copy = self.copy()
        copy += delta
        return copy

    def __radd__(self, delta):
        return self.__add__(delta)

    def __sub__(self, delta):
        copy = self.copy()
        copy -= delta
        return copy

    def __rsub__(self, delta):
        return self.__sub__(delta) * -1

    def __mul__(self, factor):
        copy = self.copy()
        copy *= factor
        return copy

    def __rmul__(self, factor):
        return self.__mul__(factor)

    def __div__(self, factor):
        copy = self.copy()
        copy /= factor
        return copy

    # No __rdiv__()

    def sum(self):
        """
        Will calculate the sum of all the elements in the keyword.

        String: Raise ValueError exception.
        Bool:   The number of true values
        """
        if self.data_type.is_int():
            return self._int_sum()
        elif self.data_type.is_float():
            return self._float_sum()
        elif self.data_type.is_double():
            return self._float_sum()
        elif self.data_type.is_bool():
            sum = 0
            for elm in self:
                if elm:
                    sum += 1
            return sum
        else:
            raise ValueError(
                'The keyword "%s" is of string type - sum is not implemented' %
                self.getName())

    def assert_binary(self, other):
        """
        Utility function to assert that keywords @self and @other can
        be combined.
        """
        return self._assert_binary(other)

    #################################################################

    def assign(self, value, mask=None, force_active=False):
        """
        Assign a value to current kw instance.

        This method is used to assign value(s) to the current EclKW
        instance. The @value parameter can either be a scalar, or
        another EclKW instance. To set all elements of a keyword to
        1.0:

            kw.assign(1.0)

        The numerical type of @value must be compatible with the
        current keyword. The optional @mask argument should be an
        EclRegion instance which can be used to limit the assignment
        to only parts of the EclKW. In the example below we select all
        the elements with PORO below 0.10, and then assign EQLNUM
        value 88 to those cells:

            grid = ecl.EclGrid("ECLIPSE.EGRID")
            reg  = ecl.EclRegion(grid, false)
            init = ecl.EclFile("ECLIPSE.INIT")

            poro = init["PORO"][0]
            eqlnum = init["EQLNUM"][0]
            reg.select_below(poro, 0.10)

            eqlnum.assign(88, mask = reg)

        The EclRegion instance has two equivalent sets of selected
        indices; one consisting of active indices and one consisting
        of global indices. By default the assign() method will select
        the global indices if the keyword has nx*ny*nz elements and
        the active indices if the kw has nactive elements. By setting
        the optional argument @force_active to true, you can force the
        method to only modify the active indices, even though the
        keyword has nx*ny*nz elements; if the keyword has nactive
        elements the @force_active flag is not considered.
        """
        if self.isNumeric():
            if type(value) == type(self):
                if mask:
                    mask.copy_kw(self, value, force_active)
                else:
                    if self.assert_binary(value):
                        self._copy_data(value)
                    else:
                        raise TypeError("Type / size mismatch")
            else:
                if mask:
                    mask.set_kw(self, value, force_active)
                else:
                    if self.data_type.is_int():
                        if isinstance(value, int):
                            self._set_int(value)
                        else:
                            raise TypeError("Type mismatch")
                    else:
                        if isinstance(value, int) or isinstance(value, float):
                            self._set_float(value)
                        else:
                            raise TypeError(
                                "Only muliplication with scalar supported")

    def add(self, other, mask=None, force_active=False):
        """
        See method assign() for documentation of optional arguments
        @mask and @force_active.
        """
        if mask:
            mask.iadd_kw(self, other, force_active)
        else:
            return self.__iadd__(other)

    def sub(self, other, mask=None, force_active=False):
        """
        See method assign() for documentation of optional arguments
        @mask and @force_active.
        """
        if mask:
            mask.isub_kw(self, other, force_active)
        else:
            return self.__isub__(other)

    def mul(self, other, mask=None, force_active=False):
        """
        See method assign() for documentation of optional arguments
        @mask and @force_active.
        """
        if mask:
            mask.imul_kw(self, other, force_active)
        else:
            return self.__imul__(other)

    def div(self, other, mask=None, force_active=False):
        """
        See method assign() for documentation of optional arguments
        @mask and @force_active.
        """
        if mask:
            mask.idiv_kw(self, other, force_active)
        else:
            return self.__idiv__(other)

    def apply(self, func, arg=None, mask=None, force_active=False):
        """
        Will apply the function @func on the keyword - inplace.

        The function @func should take a scalar value from the ecl_kw
        vector as input, and return a scalar value of the same type;
        optionally you can supply a second argument with the @arg
        attribute:

          def cutoff(x, limit):
              if x > limit:
                 return x
              else:
                 return 0


          kw.apply(math.sin)
          kw.apply(cutoff, arg=0.10)


        See method assign() for documentation of optional arguments
        @mask and @force_active.
        """
        if mask:
            active_list = mask.kw_index_list(self, force_active)
            if arg:
                for index in active_list:
                    self.data_ptr[index] = func(self.data_ptr[index], arg)
            else:
                for index in active_list:
                    self.data_ptr[index] = func(self.data_ptr[index])
        else:
            if arg:
                for i in range(len(self)):
                    self.data_ptr[i] = func(self.data_ptr[i], arg)
            else:
                for i in range(len(self)):
                    self.data_ptr[i] = func(self.data_ptr[i])

    def equal(self, other):
        """
        Will check if the two keywords are (exactly) equal.

        The check is based on the content of the keywords, and not
        pointer comparison.
        """
        if isinstance(other, EclKW):
            return self._equal(other)
        else:
            raise TypeError("Can only compare with another EclKW")

    def __eq__(self, other):
        return self.equal(other)

    def __hash__(self):
        return hash(self._get_header())

    def equal_numeric(self,
                      other,
                      epsilon=1e-6,
                      abs_epsilon=None,
                      rel_epsilon=None):
        """Will check if two numerical keywords are ~nearly equal.


        If the keywords are of type integer, the comparison is
        absolute.

        If you pass in xxx_epsilon <= 0 the xxx_epsilon will be
        ignored in the test.

        """
        if isinstance(other, EclKW):
            if abs_epsilon is None:
                abs_epsilon = epsilon

            if rel_epsilon is None:
                rel_epsilon = epsilon

            return self._equal_numeric(other, abs_epsilon, rel_epsilon)
        else:
            raise TypeError("Can only compare with another EclKW")

    #################################################################

    def deep_copy(self):
        ecl_kw = self.__deep_copy__({})
        return ecl_kw

    def fort_io_size(self):
        """
        The number of bytes this keyword would occupy in a BINARY file.
        """
        return self._get_fortio_size()

    def set_name(self, name):
        if len(name) > 8:
            raise ValueError(
                "Sorry: the name property must be max 8 characters long :-(")
        self._set_header(name)

    @property
    def name(self):
        n = self._get_header()
        return str(n) if n else ''

    def get_name(self):
        return self.name

    def resize(self, new_size):
        """
        Will set the new size of the kw to @new_size.
        """
        if new_size >= 0:
            self._resize(new_size)

        # Iteration is based on a pointer to the underlying storage,
        # that will generally by reset by the resize() call; i.e. we
        # need to call the __private_init() method again.
        self.__private_init()

    def get_min_max(self):
        """
        Will return a touple (min,max) for numerical types.

        Will raise TypeError exception if the keyword is not of
        numerical type.
        """
        if self.data_type.is_float():
            min_ = ctypes.c_float()
            max_ = ctypes.c_float()
            self._max_min_float(ctypes.byref(max_), ctypes.byref(min_))
        elif self.data_type.is_double():
            min_ = ctypes.c_double()
            max_ = ctypes.c_double()
            self._max_min_double(ctypes.byref(max_), ctypes.byref(min_))
        elif self.data_type.is_int():
            min_ = ctypes.c_int()
            max_ = ctypes.c_int()
            self._max_min_int(ctypes.byref(max_), ctypes.byref(min_))
        else:
            raise TypeError(
                "min_max property not defined for keywords of type: %s" %
                self.type)
        return (min_.value, max_.value)

    def get_max(self):
        mm = self.getMinMax()
        return mm[1]

    def get_min(self):
        mm = self.getMinMax()
        return mm[0]

    @property
    def type(self):
        return self.getEclType()

    @property
    def data_type(self):
        return self._get_data_type()

    @property
    def type_name(self):
        return self.data_type.type_name

    def type_name(self):
        return self.data_type.type_name

    def get_ecl_type(self):
        warnings.warn(
            "EclTypeEnum is deprecated. " +
            "You should instead provide an EclDataType", DeprecationWarning)

        return self._get_type()

    @property
    def header(self):
        return (self.getName(), len(self), self.typeName())

    @property
    def array(self):
        a = self.data_ptr
        if not a == None:
            a.size = len(self)
            a.__parent__ = self  # Inhibit GC
        return a

    def str_data(self, width, index1, index2, fmt):
        """
        Helper function for str() method.
        """
        data = []
        s = ""
        for index in range(index1, index2):
            data.append(self[index])
        for index in range(len(data)):
            s += fmt % data[index]
            if index % width == (width - 1):
                s += "\n"
        return s

    def str(self, width=5, max_lines=10, fmt=None):
        """
        Return string representation of kw for pretty printing.

        The function will return a string consisting of a header, and
        then a chunk of data. The data will be formatted in @width
        columns, and a maximum of @max_lines lines. If @max_lines is
        not sufficient the first elements in the kewyord are
        represented, a .... continuation line and then the last part
        of the keyword. If @max_lines is None all of the vector will
        be printed, irrespective of how long it is.

        If a value is given for @fmt that is used as format string for
        each element, otherwise a type-specific default format is
        used. If given the @fmt string should contain spacing between
        the elements. The implementation of the builtin method
        __str__() is based on this method.
        """
        s = "%-8s %8d %-4s\n" % (self.getName(), len(self), self.typeName())
        lines = len(self) // width
        if not fmt:
            fmt = self.str_fmt + " "

        if max_lines is None or lines <= max_lines:
            s += self.str_data(width, 0, len(self), fmt)
        else:
            s1 = width * max_lines // 2
            s += self.str_data(width, 0, s1, fmt)
            s += "   ....   \n"
            s += self.str_data(width, len(self) - s1, len(self), fmt)

        return s

    def __str__(self):
        """
        Return string representation - see method str().
        """
        return self.str(width=5, max_lines=10)

    def numpy_view(self):
        """Will return a numpy view to the underlying data.

        The data in this numpy array is *shared* with the EclKW
        instance, meaning that updates in one will be reflected in the
        other.
        """

        if self.dtype is numpy.float64:
            ct = ctypes.c_double
        elif self.dtype is numpy.float32:
            ct = ctypes.c_float
        elif self.dtype is numpy.int32:
            ct = ctypes.c_int
        else:
            raise ValueError(
                "Invalid type - numpy array only valid for int/float/double")

        ap = ctypes.cast(self.data_ptr, ctypes.POINTER(ct * len(self)))
        return numpy.frombuffer(ap.contents, dtype=self.dtype)

    def numpy_copy(self):
        """Will return a numpy array which contains a copy of the EclKW data.

        The numpy array has a separate copy of the data, so that
        changes to either the numpy array or the EclKW will *not* be
        reflected in the other datastructure. This is in contrast to
        the EclKW.numpyView() method where the underlying data is
        shared.
        """
        view = self.numpyView()
        return numpy.copy(view)

    def fwrite(self, fortio):
        self._fwrite(fortio)

    def write_grdecl(self, file):
        """
        Will write keyword in GRDECL format.

        This method will write the current keyword in GRDECL format,
        the @file argument must be a Python file handle to an already
        opened file. In the example below we load the porosity from an
        existing GRDECL file, set all poro values below 0.05 to 0.00
        and write back an updated GRDECL file.

            poro = ecl.EclKW.read_grdecl(open("poro1.grdecl", "r"), "PORO")
            grid = ecl.EclGrid("ECLIPSE.EGRID")
            reg  = ecl.EclRegion(grid, False)

            reg.select_below(poro, 0.05)
            poro.assign(0.0, mask=reg)

            fileH = open("poro2.grdecl", "w")
            poro.write_grdecl(fileH)
            fileH.close()

        """
        cfile = CFILE(file)
        self._fprintf_grdecl(cfile)

    def fprintf_data(self, file, fmt=None):
        """
        Will print the keyword data formatted to file.

        The @file argument should be a python file handle to a file
        opened for writing. The @fmt argument is used as fprintf()
        format specifier, observe that the format specifier should
        include a separation character between the elements. If no
        @fmt argument is supplied the default str_fmt specifier is
        used for every element, separated by a newline.

        In the case of boolean data the function will print o and 1
        for False and True respectively. For string data the function
        will print the data as 8 characters long string with blank
        padding on the right.
        """
        if fmt is None:
            fmt = self.str_fmt + "\n"
        cfile = CFILE(file)
        self._fprintf_data(fmt, cfile)

    def fix_uninitialized(self, grid):
        """
        Special case function for region code.
        """
        dims = grid.getDims()
        actnum = grid.exportACTNUM()
        self._fix_uninitialized(dims[0], dims[1], dims[2], actnum.getDataPtr())

    def get_data_ptr(self):
        if self.data_type.is_int():
            return self._int_ptr()
        elif self.data_type.is_float():
            return self._float_ptr()
        elif self.data_type.is_double():
            return self._double_ptr()
        else:
            raise ValueError("Only numeric types can export data pointer")

    def first_different(self,
                        other,
                        offset=0,
                        epsilon=0,
                        abs_epsilon=None,
                        rel_epsilon=None):
        if len(self) != len(other):
            raise ValueError("Keywords must have equal size")

        if offset >= len(self):
            raise IndexError("Offset:%d invalid - size:%d" %
                             (offset, len(self)))

        if self.getEclType() != other.getEclType():
            raise TypeError("The two keywords have different type")

        if abs_epsilon is None:
            abs_epsilon = epsilon

        if rel_epsilon is None:
            rel_epsilon = epsilon

        return self._first_different(other, offset, abs_epsilon, rel_epsilon)
Ejemplo n.º 15
0
#
#  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.

from ecl.ecl import EclPrototype
_phase_deltag = EclPrototype(
    "double ecl_grav_phase_deltag( double, double ,double , ecl_grid , ecl_file , ecl_kw , ecl_kw , ecl_kw , ecl_kw , ecl_kw , ecl_kw"
)


def phase_deltag(xyz, grid, init, sat1, rho1, porv1, sat2, rho2, porv2):

    return _phase_deltag(xyz[0], xyz[1], xyz[2], grid.c_ptr, init.c_ptr,
                         sat1.c_ptr, rho1.c_ptr, porv1.c_ptr, sat2.c_ptr,
                         rho2.c_ptr, porv2.c_ptr)


# 1. All restart files should have water, i.e. the SWAT keyword.
# 2. All phases present in the restart file should also be present as densities,
#    in addition the model must contain one additional phase - which should have a density.
# 3. The restart files can never contain oil saturation.
Ejemplo n.º 16
0
class EclDataType(BaseCClass):

    TYPE_NAME = "ecl_data_type"

    _alloc = EclPrototype("void* ecl_type_alloc_python(ecl_type_enum, size_t)",
                          bind=False)
    _alloc_from_type = EclPrototype(
        "void* ecl_type_alloc_from_type_python(ecl_type_enum)", bind=False)
    _alloc_from_name = EclPrototype(
        "void* ecl_type_alloc_from_name_python(char*)", bind=False)
    _free = EclPrototype("void ecl_type_free_python(ecl_data_type)")
    _get_type = EclPrototype(
        "ecl_type_enum ecl_type_get_type_python(ecl_data_type)")
    _get_element_size = EclPrototype(
        "size_t ecl_type_get_sizeof_ctype_fortio_python(ecl_data_type)")
    _is_int = EclPrototype("bool ecl_type_is_int_python(ecl_data_type)")
    _is_char = EclPrototype("bool ecl_type_is_char_python(ecl_data_type)")
    _is_float = EclPrototype("bool ecl_type_is_float_python(ecl_data_type)")
    _is_double = EclPrototype("bool ecl_type_is_double_python(ecl_data_type)")
    _is_mess = EclPrototype("bool ecl_type_is_mess_python(ecl_data_type)")
    _is_bool = EclPrototype("bool ecl_type_is_bool_python(ecl_data_type)")
    _is_string = EclPrototype("bool ecl_type_is_string_python(ecl_data_type)")
    _get_name = EclPrototype("char* ecl_type_alloc_name_python(ecl_data_type)")
    _is_numeric = EclPrototype(
        "bool ecl_type_is_numeric_python(ecl_data_type)")
    _is_equal = EclPrototype(
        "bool ecl_type_is_equal_python(ecl_data_type, ecl_data_type)")

    def __init__(self, type_enum=None, element_size=None, type_name=None):
        self._assert_valid_arguments(type_enum, element_size, type_name)

        if type_name:
            c_ptr = self._alloc_from_name(type_name)
        elif element_size is None:
            c_ptr = self._alloc_from_type(type_enum)
        else:
            c_ptr = self._alloc(type_enum, element_size)

        super(EclDataType, self).__init__(c_ptr)

    def _assert_valid_arguments(self, type_enum, element_size, type_name):
        if type_name is not None:
            if type_enum is not None or element_size is not None:
                err_msg = ("Type name given (%s). Expected both " +
                           "type_enum and element_size to be None")
                raise ValueError(err_msg % type_name)

        elif type_enum is None:
            raise ValueError("Both type_enum and type_name is None!")

        elif type_enum == EclTypeEnum.ECL_STRING_TYPE:
            if element_size is None:
                raise ValueError("When creating an ECL_STRING one must " +
                                 "provide an element size!")

            if not (0 <= element_size <= 999):
                raise ValueError("Expected element_size to be in the range " +
                                 "[0, 999], was: %d" % element_size)

    @property
    def type(self):
        return self._get_type()

    @property
    def element_size(self):
        return self._get_element_size()

    @property
    def type_name(self):
        return self._get_name()

    def free(self):
        self._free()

    def is_int(self):
        return self._is_int()

    def is_char(self):
        return self._is_char()

    def is_float(self):
        return self._is_float()

    def is_double(self):
        return self._is_double()

    def is_mess(self):
        return self._is_mess()

    def is_bool(self):
        return self._is_bool()

    def is_string(self):
        return self._is_string()

    def is_numeric(self):
        return self._is_numeric()

    def is_equal(self, other):
        return self._is_equal(other)

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return self.is_equal(other)
        return False

    def __ne__(self, other):
        return not self.__eq__(other)

    def __hash__(self):
        return hash((self.type, self.element_size))

    @classmethod
    def create_from_type_name(cls, name):
        return EclDataType(type_name=name)

    # Enables one to fetch a type as EclDataType.ECL_XXXX
    class classproperty(object):
        def __init__(self, fget):
            self.fget = fget

        def __get__(self, owner_self, owner_cls):
            return self.fget(owner_cls)

    @classproperty
    def ECL_INT(cls):
        return EclDataType(EclTypeEnum.ECL_INT_TYPE)

    @classproperty
    def ECL_FLOAT(cls):
        return EclDataType(EclTypeEnum.ECL_FLOAT_TYPE)

    @classproperty
    def ECL_DOUBLE(cls):
        return EclDataType(EclTypeEnum.ECL_DOUBLE_TYPE)

    @classproperty
    def ECL_BOOL(cls):
        return EclDataType(EclTypeEnum.ECL_BOOL_TYPE)

    @classproperty
    def ECL_MESS(cls):
        return EclDataType(EclTypeEnum.ECL_MESS_TYPE)

    @classproperty
    def ECL_CHAR(cls):
        return EclDataType(EclTypeEnum.ECL_CHAR_TYPE)

    @classmethod
    def ECL_STRING(cls, elem_size):
        return EclDataType(EclTypeEnum.ECL_STRING_TYPE, elem_size)
Ejemplo n.º 17
0
class EclGridGenerator:

    _alloc_rectangular = EclPrototype("ecl_grid_obj ecl_grid_alloc_rectangular( int , int , int , double , double , double , int*)" , bind = False)

    @classmethod
    def createRectangular(cls, dims , dV , actnum = None):
        """
        Will create a new rectangular grid. @dims = (nx,ny,nz)  @dVg = (dx,dy,dz)

        With the default value @actnum == None all cells will be active,
        """
        if actnum is None:
            ecl_grid = cls._alloc_rectangular( dims[0] , dims[1] , dims[2] , dV[0] , dV[1] , dV[2] , None )
        else:
            if not isinstance(actnum , IntVector):
                tmp = IntVector(initial_size = len(actnum))
                for (index , value) in enumerate(actnum):
                    tmp[index] = value
                actnum = tmp

            if not len(actnum) == dims[0] * dims[1] * dims[2]:
                raise ValueError("ACTNUM size mismatch: len(ACTNUM):%d  Expected:%d" % (len(actnum) , dims[0] * dims[1] * dims[2]))
            ecl_grid = cls._alloc_rectangular( dims[0] , dims[1] , dims[2] , dV[0] , dV[1] , dV[2] , actnum.getDataPtr() )

        # If we have not succeeded in creatin the grid we *assume* the
        # error is due to a failed malloc.
        if ecl_grid is None:
            raise MemoryError("Failed to allocated regualar grid")
            
        return ecl_grid

    @classmethod
    def createSingleCellGrid(cls, corners):
        """
        Provided with the corners of the grid in a similar manner as the eight
        corners are output for a single cell, this method will create a grid
        consisting of a single cell with the specified corners as its corners.
        """

        zcorn = [corners[i][2] for i in range(8)]

        flatten = lambda l : [elem for sublist in l for elem in sublist]
        coord = [(corners[i], corners[i+4]) for i in range(4)]
        coord = flatten(flatten(coord))

        def constructFloatKW(name, values):
            kw = EclKW(name, len(values), EclDataType.ECL_FLOAT)
            for i in range(len(values)):
                kw[i] = values[i]
            return kw

        grid = EclGrid.create((1,1,1), constructFloatKW("ZCORN", zcorn), constructFloatKW("COORD", coord), None)

        if not corners == [grid.getCellCorner(i, 0) for i in range(8)]:
            raise AssertionError("Failed to generate single cell grid. " +
                    "Did not end up the expected corners.")

        return grid

    @classmethod
    def createGrid(cls, dims, dV, offset=1,
            escape_origo_shift=(1,1,0),
            irregular_offset=False, irregular=False, concave=False,
            faults=False, scale=1, translation=(0,0,0), rotate=False,
            misalign=False):
        """
        Will create a new grid where each cell is a parallelogram (skewed by z-value).
        The number of cells are given by @dims = (nx, ny, nz) and the dimention
        of each cell by @dV = (dx, dy, dz).

        All cells are guaranteed to not be self-intersecting. Hence, no twisted
        cells and somewhat meaningfull cells.

        @offset gives how much the layers should fluctuate or "wave" as you
        move along the X-axis.

        @irregular_offset decides whether the offset should be constant or
        increase by dz/2 every now and then.

        @irregular if true some of the layers will be inclining and others
        declining at the start.

        @concave decides whether the cells are to be convex or not. In
        particular, if set to False, all cells of the grid will be concave.

        @escape_origo_shift is used to prevent any cell of having corners in (0,0,z)
        as there is a heuristic in ecl_grid.c that marks such cells as tainted.

        @faults decides if there are to be faults in the grid.

        @scale A positive number that scales the "lower" endpoint of all
        coord's. In particular, @scale != 1 creates trapeziod cells in both the XZ
        and YZ-plane.

        @translation the lower part of the grid is translated ("slided") by the specified
        additive factor.

        @rotate the lower part of the grid is rotated 90 degrees around its
        center.

        @misalign will toggle COORD's slightly in various directions to break
        alignment

        Note that cells in the lowermost layer can have multiple corners
        at the same point.

        For testing it should give good coverage of the various scenarios this
        method can produce, by leting @dims be (10,10,10), @dV=(2,2,2), @offset=1,
        and try all 4 different configurations of @concave and
        @irregular_offset.
        """

        nx, ny, nz = dims
        dx, dy, dz = dV

        # Validate arguments
        if min(dims + dV) <= 0:
            raise ValueError("Expected positive grid and cell dimentions")

        if offset < 0:
            raise ValueError("Expected non-negative offset")

        if irregular and offset + (dz/2. if irregular_offset else 0) > dz:
            raise AssertionError("Arguments can result in self-" +
                    "intersecting cells. Increase dz, deactivate eiter " +
                    "irregular or irregular_offset, or decrease offset to avoid " +
                    "any problems")

        verbose = lambda l : [elem for elem in l for i in range(2)][1:-1:]
        flatten = lambda l : [elem for sublist in l for elem in sublist]

        # Compute zcorn
        z = escape_origo_shift[2]
        zcorn = [z]*(4*nx*ny)
        for k in range(nz-1):
            z = z+dz
            local_offset = offset + (dz/2. if irregular_offset and k%2 == 0 else 0)

            layer = []
            for i in range(ny+1):
                shift = ((i if concave else 0) + (k/2 if irregular else 0)) % 2
                path = [z if i%2 == shift else z+local_offset for i in range(nx+1)]
                layer.append(verbose(path))

            zcorn = zcorn + (2*flatten(verbose(layer)))

        z = z+dz
        zcorn = zcorn + ([z]*(4*nx*ny))

        if faults:
            # Ensure that drop does not align with grid structure
            drop = (offset+dz)/2. if abs(offset-dz/2.) > 0.2 else offset + 0.4
            zcorn = cls.__createFaults(nx, ny, nz, zcorn, drop)

        cls.assertZcorn(nx, ny, nz, zcorn)

        # Compute coord
        coord = []
        for j, i in itertools.product(range(ny+1), range(nx+1)):
            x, y = i*dx+escape_origo_shift[0], j*dy+escape_origo_shift[1]
            coord = coord + [x, y, escape_origo_shift[2], x, y, z]

        # Apply transformations
        lower_center = (
                nx*dx/2. + escape_origo_shift[0],
                ny*dy/2. + escape_origo_shift[1]
                )

        if misalign:
            coord = cls.__misalignCoord(coord, dims, dV)

        coord = cls.__scaleCoord(coord, scale, lower_center)

        if rotate:
            coord = cls.__rotateCoord(coord, lower_center)

        coord = cls.__translateCoord(coord, translation)

        cls.assertCoord(nx, ny, nz, coord)

        # Construct grid
        def constructFloatKW(name, values):
            kw = EclKW(name, len(values), EclDataType.ECL_FLOAT)
            for i in range(len(values)):
                kw[i] = values[i]
            return kw

        return EclGrid.create(dims, constructFloatKW("ZCORN", zcorn), constructFloatKW("COORD", coord), None)

    @classmethod
    def __createFaults(cls, nx, ny, nz, zcorn, drop):
        """
        Will create several faults consisting of all cells such that either its
        i or j index is 1 modulo 3.
        """

        plane_size = 4*nx*ny
        for x, y, z in itertools.product(range(nx), range(ny), range(nz)):
            if x%3 != 1 and y%3 != 1:
                continue

            corner = [0]*8
            corner[0] = 2*z*plane_size + 4*y*nx + 2*x
            corner[1] = corner[0] + 1
            corner[2] = corner[0] + 2*nx
            corner[3] = corner[2] + 1

            for i in range(4, 8):
                corner[i] = corner[i-4] + plane_size

            for c in corner:
                zcorn[c] = zcorn[c] + drop

        return zcorn

    @classmethod
    def assertZcorn(cls, nx, ny, nz, zcorn):
        """

        Raises an AssertionError if the zcorn is not as expected. In
        patricular, it is verified that:

            - zcorn has the approperiate length (8*nx*ny*nz) and
            - that no cell is twisted.

        """

        if len(zcorn) != 8*nx*ny*nz:
            raise AssertionError(
                    "Expected len(zcorn) to be %d, was %d" %
                    (8*nx*ny*nz, len(zcorn))
                    )

        plane_size = 4*nx*ny
        for p in range(8*nx*ny*nz - plane_size):
            if zcorn[p] > zcorn[p + plane_size]:
                raise AssertionError(
                    "Twisted cell was created. " +
                    "Decrease offset or increase dz to avoid this!"
                    )

    @classmethod
    def __scaleCoord(cls, coord, scale, lower_center):
        coord = numpy.array([
            map(float, coord[i:i+6:])
            for i in range(0, len(coord), 6)
            ])
        origo = numpy.array(3*[0.] + list(lower_center) + [0])
        scale = numpy.array(3*[1.] + 2*[scale] + [1])

        coord = scale * (coord-origo) + origo
        return coord.flatten().tolist()

    @classmethod
    def __misalignCoord(cls, coord, dims, dV):
        nx, ny, nz = dims

        coord = numpy.array([
            map(float, coord[i:i+6:])
            for i in range(0, len(coord), 6)
            ])

        adjustment = numpy.array([
            (0, 0, 0, i*dV[0]/2., j*dV[1]/2., 0) for i, j in itertools.product([-1, 0, 1], repeat=2)
            ])

        for i, c in enumerate(coord):
            # Leave the outermost coords alone
            if i < nx+1 or i >= len(coord)-(nx+1):
                continue
            if i%(nx+1) in [0, nx]:
                continue

            c += adjustment[i%len(adjustment)]

        return coord.flatten().tolist()

    @classmethod
    def __rotateCoord(cls, coord, lower_center):
        coord = numpy.array([
            map(float, coord[i:i+6:])
            for i in range(0, len(coord), 6)
            ])

        origo = numpy.array(3*[0.] + list(lower_center) + [0])
        coord -= origo

        for c in coord:
            c[3], c[4] = -c[4], c[3]

        coord += origo
        return coord.flatten().tolist()

    @classmethod
    def __translateCoord(cls, coord, translation):
        coord = numpy.array([
            map(float, coord[i:i+6:])
            for i in range(0, len(coord), 6)
            ])
        translation = numpy.array(3*[0.] + list(translation))

        coord = coord + translation
        return coord.flatten().tolist()

    @classmethod
    def assertCoord(cls, nx, ny, nz, coord):
        """

        Raises an AssertionError if the coord is not as expected. In
        particular, it is verfied that:

            - coord has the approperiate length (6*(nx+1)*(ny+1)) and
            - that all values are positive.

        """

        if len(coord) != 6*(nx+1)*(ny+1):
            raise AssertionError(
                    "Expected len(coord) to be %d, was %d" %
                    (6*(nx+1)*(ny+1), len(coord))
                    )

        if min(coord) < 0:
            raise AssertionError("Negative COORD values was generated. " +
                    "This is likely due to a tranformation. " +
                    "Increasing the escape_origio_shift will most likely " +
                    "fix the problem")
Ejemplo n.º 18
0
class FaultBlockLayer(BaseCClass):
    TYPE_NAME = "fault_block_layer"
    _alloc                = EclPrototype("void*           fault_block_layer_alloc(ecl_grid ,  int)", bind = False)
    _free                 = EclPrototype("void            fault_block_layer_free(fault_block_layer)")
    _size                 = EclPrototype("int             fault_block_layer_get_size(fault_block_layer)")
    _iget_block           = EclPrototype("fault_block_ref fault_block_layer_iget_block(fault_block_layer, int)")
    _add_block            = EclPrototype("fault_block_ref fault_block_layer_add_block(fault_block_layer, int)")
    _get_block            = EclPrototype("fault_block_ref fault_block_layer_get_block(fault_block_layer, int)")
    _del_block            = EclPrototype("void            fault_block_layer_del_block(fault_block_layer, int)")
    _has_block            = EclPrototype("bool            fault_block_layer_has_block(fault_block_layer, int)")
    _scan_keyword         = EclPrototype("bool            fault_block_layer_scan_kw(fault_block_layer, ecl_kw)")
    _load_keyword         = EclPrototype("bool            fault_block_layer_load_kw(fault_block_layer, ecl_kw)")
    _getK                 = EclPrototype("int             fault_block_layer_get_k(fault_block_layer)")
    _get_next_id          = EclPrototype("int             fault_block_layer_get_next_id(fault_block_layer)")
    _scan_layer           = EclPrototype("void            fault_block_layer_scan_layer( fault_block_layer , layer)")
    _insert_block_content = EclPrototype("void            fault_block_layer_insert_block_content( fault_block_layer , fault_block)")
    _export_kw            = EclPrototype("bool            fault_block_layer_export( fault_block_layer , ecl_kw )")
    _get_layer            = EclPrototype("layer_ref       fault_block_layer_get_layer( fault_block_layer )")


    def __init__(self , grid , k):
        c_ptr = self._alloc( grid , k)
        if c_ptr:
            super(FaultBlockLayer, self).__init__(c_ptr)
        else:
            raise ValueError("Invalid input - failed to create FaultBlockLayer")

        # The underlying C implementation uses lazy evaluation and
        # needs to hold on to the grid reference. We therefor take
        # references to it here, to protect against premature garbage
        # collection.
        self.grid_ref = grid


    def __len__(self):
        return self._size()

    def __getitem__(self , index):
        """
        @rtype: FaultBlock
        """
        if isinstance(index, int):
            if index < 0:
                index += len(self)

            if 0 <= index < len(self):
                return self._iget_block( index ).setParent(self)
            else:
                raise IndexError("Index:%d out of range: [0,%d)" % (index , len(self)))
        elif isinstance(index,tuple):
            i,j = index
            if 0 <= i < self.grid_ref.getNX() and 0 <= j < self.grid_ref.getNY():
                geo_layer = self.getGeoLayer()
                block_id = geo_layer[i,j]
                if block_id == 0:
                    raise ValueError("No fault block defined for location (%d,%d)" % (i,j))
                else:
                    return self.getBlock( block_id )
            else:
                raise IndexError("Invalid i,j : (%d,%d)" % (i,j))
        else:
            raise TypeError("Index should be integer type")

    def __contains__(self , block_id):
        return self._has_block( block_id)


    def scanKeyword(self , fault_block_kw):
        """
        Will reorder the block ids, and ensure single connectedness. Assign block_id to zero blocks.
        """
        ok = self._scan_keyword( fault_block_kw )
        if not ok:
            raise ValueError("The fault block keyword had wrong type/size: type:%s  size:%d  grid_size:%d" % (fault_block_kw.type_name , len(fault_block_kw) , self.grid_ref.getGlobalSize()))


    def loadKeyword(self , fault_block_kw):
        """
        Will load directly from keyword - without reorder; ignoring zero.
        """
        ok = self._load_keyword( fault_block_kw )
        if not ok:
            raise ValueError("The fault block keyword had wrong type/size:  type:%s  size:%d  grid_size:%d" % (fault_block_kw.typeName() , len(fault_block_kw) , self.grid_ref.getGlobalSize()))


    def getBlock(self , block_id):
        """
        @rtype: FaultBlock
        """
        if block_id in self:
            return self._get_block(block_id).setParent(self)
        else:
            raise KeyError("No blocks with ID:%d in this layer" % block_id)


    def deleteBlock(self , block_id):
        if block_id in self:
            self._del_block( block_id)
        else:
            raise KeyError("No blocks with ID:%d in this layer" % block_id)

    def addBlock(self , block_id = None):
        if block_id is None:
            block_id = self.getNextID()

        if block_id in self:
            raise KeyError("Layer already contains block with ID:%s" % block_id)
        else:
            return self._add_block(block_id).setParent(self)

    def getNextID(self):
        return self._get_next_id( )


    def getK(self):
        return self._getK( )


    def free(self):
        self._free( )


    def scanLayer( self , layer):
        self._scan_layer( layer )


    def insertBlockContent(self , block):
        self._insert_block_content( block )

    def exportKeyword(self , kw):
        if len(kw) != self.grid_ref.getGlobalSize():
            raise ValueError("The size of the target keyword must be equal to the size of the grid. Got:%d Expected:%d" % (len(kw) , self.grid_ref.getGlobalSize()))

        if not kw.data_type.is_int():
            raise TypeError("The target kewyord must be of integer type")

        self._export_kw( kw )


    def addFaultBarrier(self , fault , link_segments = False):
        layer = self.getGeoLayer( )
        layer.addFaultBarrier( fault , self.getK() , link_segments )


    def addFaultLink(self , fault1 , fault2 ):
        if not fault1.intersectsFault( fault2 , self.getK()):
            layer = self.getGeoLayer()
            layer.addIJBarrier( fault1.extendToFault( fault2 , self.getK() ) )


    def joinFaults(self , fault1 , fault2):
        if not fault1.intersectsFault( fault2 , self.getK()):
            layer = self.getGeoLayer()
            try:
                layer.addIJBarrier( Fault.joinFaults( fault1 , fault2 , self.getK()) )
            except ValueError:
                print('Failed to join faults %s and %s' % (fault1.getName() , fault2.getName()))
                raise ValueError("")


    def addPolylineBarrier(self , polyline):
        layer = self.getGeoLayer()
        p0 = polyline[0]
        c0 = self.grid_ref.findCellCornerXY( p0[0] ,  p0[1] , self.getK() )
        i,j = self.grid_ref.findCellXY( p0[0] ,  p0[1] , self.getK() )
        print('%g,%g -> %d,%d   %d' % (p0[0] , p0[1] , i,j,c0))
        for index in range(1,len(polyline)):
            p1 = polyline[index]
            c1 = self.grid_ref.findCellCornerXY( p1[0] ,  p1[1] , self.getK() )
            i,j = self.grid_ref.findCellXY( p1[0] ,  p1[1] , self.getK() )
            layer.addInterpBarrier( c0 , c1 )
            print('%g,%g -> %d,%d   %d' % (p1[0] , p1[1] , i,j,c1))
            print('Adding barrier %d -> %d' % (c0 , c1))
            c0 = c1


    def getGeoLayer(self):
        """Returns the underlying geometric layer."""
        return self._get_layer( )


    def cellContact(self , p1 , p2):
        layer = self.getGeoLayer()
        return layer.cellContact(p1,p2)
Ejemplo n.º 19
0
class EclSubsidence(BaseCClass):
    """
    Holding ECLIPSE results for calculating subsidence changes.

    The EclSubsidence class is a collection class holding the results from
    ECLIPSE forward modelling of subsidence surveys. Observe that the
    class is focused on the ECLIPSE side of things, and does not have
    any notion of observed values or measurement locations; that
    should be handled by the scope using the EclSubsidence class.

    Typical use of the EclSubsidence class involves the following steps:

      1. Create the EclSubsidence instance.
      2. Add surveys with the add_survey_XXXX() methods.
      3. Evalute the subsidence response with the eval() method.
    """
    TYPE_NAME = "ecl_subsidence"
    _alloc = EclPrototype("void* ecl_subsidence_alloc( ecl_grid , ecl_file )",
                          bind=False)
    _free = EclPrototype("void ecl_subsidence_free( ecl_subsidence )")
    _add_survey_PRESSURE = EclPrototype(
        "void*  ecl_subsidence_add_survey_PRESSURE( ecl_subsidence , char* , ecl_file_view )"
    )
    _eval = EclPrototype(
        "double ecl_subsidence_eval( ecl_subsidence , char* , char* , ecl_region , double , double , double, double, double)"
    )
    _eval_geertsma = EclPrototype(
        "double ecl_subsidence_eval_geertsma( ecl_subsidence , char* , char* , ecl_region , double , double , double, double, double, double)"
    )
    _has_survey = EclPrototype(
        "bool  ecl_subsidence_has_survey( ecl_subsidence , char*)")

    def __init__(self, grid, init_file):
        """
        Creates a new EclSubsidence instance.

        The input arguments @grid and @init_file should be instances
        of EclGrid and EclFile respectively.
        """
        self.init_file = init_file  # Inhibit premature garbage collection of init_file
        c_ptr = self._alloc(grid, init_file)
        super(EclSubsidence, self).__init__(c_ptr)

    def __contains__(self, survey_name):
        return self._has_survey(survey_name)

    def add_survey_PRESSURE(self, survey_name, restart_file):
        """
        Add new survey based on PRESSURE keyword.

        Add a new survey; in this context a survey is the state of
        reservoir, i.e. an ECLIPSE restart file. The @survey_name
        input argument will be used when refering to this survey at a
        later stage. The @restart_file input argument should be an
        EclFile instance with data from one report step. A typical way
        to load the @restart_file argument is:

           import datetime
           import ecl.ecl.ecl as ecl
           ...
           ...
           date = datetime.datetime( year , month , day )
           restart_file1 = ecl.EclFile.restart_block( "ECLIPSE.UNRST" , dtime = date)
           restart_file2 = ecl.EclFile.restart_block( "ECLIPSE.UNRST" , report_step = 67 )

        The pore volume is calculated from the initial pore volume and
        the PRESSURE keyword from the restart file.
        """
        self._add_survey_PRESSURE(survey_name, restart_file)

    def eval_geertsma(self,
                      base_survey,
                      monitor_survey,
                      pos,
                      youngs_modulus,
                      poisson_ratio,
                      seabed,
                      region=None):
        if not base_survey in self:
            raise KeyError("No such survey: %s" % base_survey)

        if monitor_survey is not None:
            if not monitor_survey in self:
                raise KeyError("No such survey: %s" % monitor_survey)

        return self._eval_geertsma(base_survey, monitor_survey, region, pos[0],
                                   pos[1], pos[2], youngs_modulus,
                                   poisson_ratio, seabed)

    def eval(self,
             base_survey,
             monitor_survey,
             pos,
             compressibility,
             poisson_ratio,
             region=None):
        """
        Calculates the subsidence change between two surveys.

        This is the method everything is leading up to; will calculate
        the change in subsidence, in centimeters,
        between the two surveys named @base_survey and
        @monitor_survey.

        The monitor survey can be 'None' - the resulting answer has
        nothing whatsovever to do with subsidence, but can be
        interesting to determine the numerical size of the quantities
        which are subtracted in a 4D study.

        The @pos argument should be a tuple of three elements with the
        (utm_x , utm_y , depth) position where we want to evaluate the
        change in subsidence.

        If supplied the optional argument @region should be an
        EclRegion() instance; this region will be used to limit the
        part of the reserviour included in the subsidence calculations.

        The argument @compressibility is the total reservoir compressibility.
        """
        if not base_survey in self:
            raise KeyError("No such survey: %s" % base_survey)

        if not monitor_survey in self:
            raise KeyError("No such survey: %s" % monitor_survey)

        return self._eval(base_survey, monitor_survey, region, pos[0], pos[1],
                          pos[2], compressibility, poisson_ratio)

    def free(self):
        self._free()
Ejemplo n.º 20
0
        Will export all well related variables for wells 'OP1' and
        'OP2' and all total field vectors.
        """

        if keys is None:
            var_list = self.keys()
        else:
            var_list = StringList()
            for key in keys:
                var_list |= self.keys(pattern=key)
        self._export_csv(filename, var_list, date_format, sep)


import ecl.ecl.ecl_sum_keyword_vector
EclSum._dump_csv_line = EclPrototype(
    "void  ecl_sum_fwrite_interp_csv_line(ecl_sum, time_t, ecl_sum_vector, FILE)",
    bind=False)
EclSum._get_interp_vector = EclPrototype(
    "void  ecl_sum_get_interp_vector(ecl_sum, time_t, ecl_sum_vector, double_vector)",
    bind=False)

monkey_the_camel(EclSum, 'varType', EclSum.var_type, classmethod)
monkey_the_camel(EclSum, 'addVariable', EclSum.add_variable)
monkey_the_camel(EclSum, 'addTStep', EclSum.add_t_step)
monkey_the_camel(EclSum, 'assertKeyValid', EclSum.assert_key_valid)
monkey_the_camel(EclSum, 'scaleVector', EclSum.scale_vector)
monkey_the_camel(EclSum, 'shiftVector', EclSum.shift_vector)
monkey_the_camel(EclSum, 'timeRange', EclSum.time_range)
monkey_the_camel(EclSum, 'blockedProduction', EclSum.blocked_production)
monkey_the_camel(EclSum, 'getDataStartTime', EclSum.get_data_start_time)
monkey_the_camel(EclSum, 'getStartTime', EclSum.get_start_time)
Ejemplo n.º 21
0
class EclSum(BaseCClass):
    TYPE_NAME = "ecl_sum"
    _fread_alloc_case = EclPrototype(
        "void*     ecl_sum_fread_alloc_case__(char*, char*, bool)", bind=False)
    _fread_alloc = EclPrototype(
        "void*     ecl_sum_fread_alloc(char*, stringlist, char*, bool)",
        bind=False)
    _create_restart_writer = EclPrototype(
        "ecl_sum_obj  ecl_sum_alloc_restart_writer(char*, char*, bool, bool, char*, time_t, bool, int, int, int)",
        bind=False)
    _iiget = EclPrototype("double   ecl_sum_iget(ecl_sum, int, int)")
    _free = EclPrototype("void     ecl_sum_free(ecl_sum)")
    _data_length = EclPrototype("int      ecl_sum_get_data_length(ecl_sum)")
    _scale_vector = EclPrototype(
        "void     ecl_sum_scale_vector(ecl_sum, int, double)")
    _shift_vector = EclPrototype(
        "void     ecl_sum_shift_vector(ecl_sum, int, double)")
    _iget_sim_days = EclPrototype(
        "double   ecl_sum_iget_sim_days(ecl_sum, int) ")
    _iget_report_step = EclPrototype(
        "int      ecl_sum_iget_report_step(ecl_sum, int) ")
    _iget_mini_step = EclPrototype(
        "int      ecl_sum_iget_mini_step(ecl_sum, int) ")
    _iget_sim_time = EclPrototype(
        "time_t   ecl_sum_iget_sim_time(ecl_sum, int) ")
    _get_report_end = EclPrototype(
        "int      ecl_sum_iget_report_end(ecl_sum, int)")
    _get_general_var = EclPrototype(
        "double   ecl_sum_get_general_var(ecl_sum, int, char*)")
    _get_general_var_index = EclPrototype(
        "int      ecl_sum_get_general_var_params_index(ecl_sum, char*)")
    _get_general_var_from_sim_days = EclPrototype(
        "double   ecl_sum_get_general_var_from_sim_days(ecl_sum, double, char*)"
    )
    _get_general_var_from_sim_time = EclPrototype(
        "double   ecl_sum_get_general_var_from_sim_time(ecl_sum, time_t, char*)"
    )
    _solve_days = EclPrototype(
        "double_vector_obj  ecl_sum_alloc_days_solution(ecl_sum, char*, double, bool)"
    )
    _solve_dates = EclPrototype(
        "time_t_vector_obj  ecl_sum_alloc_time_solution(ecl_sum, char*, double, bool)"
    )
    _get_first_gt = EclPrototype(
        "int      ecl_sum_get_first_gt(ecl_sum, int, double)")
    _get_first_lt = EclPrototype(
        "int      ecl_sum_get_first_lt(ecl_sum, int, double)")
    _get_start_date = EclPrototype("time_t   ecl_sum_get_start_time(ecl_sum)")
    _get_end_date = EclPrototype("time_t   ecl_sum_get_end_time(ecl_sum)")
    _get_last_report_step = EclPrototype(
        "int      ecl_sum_get_last_report_step(ecl_sum)")
    _get_first_report_step = EclPrototype(
        "int      ecl_sum_get_first_report_step(ecl_sum)")
    _select_matching_keys = EclPrototype(
        "void     ecl_sum_select_matching_general_var_list(ecl_sum, char*, stringlist)"
    )
    _has_key = EclPrototype("bool     ecl_sum_has_general_var(ecl_sum, char*)")
    _check_sim_time = EclPrototype(
        "bool     ecl_sum_check_sim_time(ecl_sum, time_t)")
    _check_sim_days = EclPrototype(
        "bool     ecl_sum_check_sim_days(ecl_sum, double)")
    _sim_length = EclPrototype("double   ecl_sum_get_sim_length(ecl_sum)")
    _get_first_day = EclPrototype("double   ecl_sum_get_first_day(ecl_sum)")
    _get_data_start = EclPrototype("time_t   ecl_sum_get_data_start(ecl_sum)")
    _get_unit = EclPrototype("char*    ecl_sum_get_unit(ecl_sum, char*)")
    _get_simcase = EclPrototype("char*    ecl_sum_get_case(ecl_sum)")
    _get_base = EclPrototype("char*    ecl_sum_get_base(ecl_sum)")
    _get_path = EclPrototype("char*    ecl_sum_get_path(ecl_sum)")
    _get_abs_path = EclPrototype("char*    ecl_sum_get_abs_path(ecl_sum)")
    _get_report_step_from_time = EclPrototype(
        "int      ecl_sum_get_report_step_from_time(ecl_sum, time_t)")
    _get_report_step_from_days = EclPrototype(
        "int      ecl_sum_get_report_step_from_days(ecl_sum, double)")
    _get_report_time = EclPrototype(
        "time_t   ecl_sum_get_report_time(ecl_sum, int)")
    _fwrite_sum = EclPrototype("void     ecl_sum_fwrite(ecl_sum)")
    _set_case = EclPrototype("void     ecl_sum_set_case(ecl_sum, char*)")
    _alloc_time_vector = EclPrototype(
        "time_t_vector_obj ecl_sum_alloc_time_vector(ecl_sum, bool)")
    _alloc_data_vector = EclPrototype(
        "double_vector_obj ecl_sum_alloc_data_vector(ecl_sum, int, bool)")
    _get_var_node = EclPrototype(
        "smspec_node_ref ecl_sum_get_general_var_node(ecl_sum, char*)")
    _create_well_list = EclPrototype(
        "stringlist_obj ecl_sum_alloc_well_list(ecl_sum, char*)")
    _create_group_list = EclPrototype(
        "stringlist_obj ecl_sum_alloc_group_list(ecl_sum, char*)")
    _add_variable = EclPrototype(
        "smspec_node_ref   ecl_sum_add_var(ecl_sum, char*, char*, int, char*, double)"
    )
    _add_tstep = EclPrototype(
        "ecl_sum_tstep_ref ecl_sum_add_tstep(ecl_sum, int, double)")
    _export_csv = EclPrototype(
        "void ecl_sum_export_csv(ecl_sum, char*, stringlist, char*, char*)")
    _identify_var_type = EclPrototype(
        "ecl_sum_var_type ecl_sum_identify_var_type(char*)", bind=False)

    def __init__(self, load_case, join_string=":", include_restart=True):
        """
        Loads a new EclSum instance with summary data.

        Loads a new summary results from the ECLIPSE case given by
        argument @load_case; @load_case should be the basename of the ECLIPSE
        simulation you want to load. @load_case can contain a leading path
        component, and also an extension - the latter will be ignored.

        The @join_string is the string used when combining elements
        from the WGNAMES, KEYWORDS and NUMS vectors into a composit
        key; with @join_string == ":" the water cut in well OP_1 will
        be available as "WWCT:OP_1".

        If the @include_restart parameter is set to true the summary
        loader will, in the case of a restarted ECLIPSE simulation,
        try to load summary results also from the restarted case.
        """
        if not load_case:
            raise ValueError(
                'load_case must be the basename of the simulation')
        c_pointer = self._fread_alloc_case(load_case, join_string,
                                           include_restart)
        if c_pointer is None:
            raise IOError(
                "Failed to create summary instance from argument:%s" %
                load_case)

        super(EclSum, self).__init__(c_pointer)
        self.__private_init()
        self._load_case = load_case

    @classmethod
    def load(cls,
             smspec_file,
             unsmry_file,
             key_join_string=":",
             include_restart=True):
        if not os.path.isfile(smspec_file):
            raise IOError("No such file: %s" % smspec_file)

        if not os.path.isfile(unsmry_file):
            raise IOError("No such file: %s" % unsmry_file)

        data_files = StringList()
        data_files.append(unsmry_file)
        c_ptr = cls._fread_alloc(smspec_file, data_files, key_join_string,
                                 include_restart)
        if c_ptr is None:
            raise IOError("Failed to create summary instance")

        ecl_sum = cls.createPythonObject(c_ptr)
        ecl_sum._load_case = smspec_file
        return ecl_sum

    @classmethod
    def createCReference(cls, c_pointer, parent=None):
        result = super(EclSum, cls).createCReference(c_pointer, parent)
        if not result is None:
            result.__private_init()
        return result

    @classmethod
    def createPythonObject(cls, c_pointer):
        result = super(EclSum, cls).createPythonObject(c_pointer)
        result.__private_init()
        return result

    @classmethod
    def var_type(cls, keyword):
        return cls._identify_var_type(keyword)

    @staticmethod
    def writer(case,
               start_time,
               nx,
               ny,
               nz,
               fmt_output=False,
               unified=True,
               time_in_days=True,
               key_join_string=":"):
Ejemplo n.º 22
0
class FaultBlock(BaseCClass):
    TYPE_NAME = "fault_block"

    _get_xc = EclPrototype("double         fault_block_get_xc(fault_block)")
    _get_yc = EclPrototype("double         fault_block_get_yc(fault_block)")
    _get_block_id = EclPrototype(
        "int            fault_block_get_id(fault_block)")
    _get_size = EclPrototype(
        "int            fault_block_get_size(fault_block)")
    _export_cell = EclPrototype(
        "void           fault_block_export_cell(fault_block , int , int* , int* , int* , double* , double* , double*)"
    )
    _assign_to_region = EclPrototype(
        "void           fault_block_assign_to_region(fault_block , int)")
    _get_region_list = EclPrototype(
        "int_vector_ref fault_block_get_region_list(fault_block)")
    _add_cell = EclPrototype(
        "void           fault_block_add_cell(fault_block,  int , int)")
    _get_global_index_list = EclPrototype(
        "int_vector_ref fault_block_get_global_index_list(fault_block)")
    _trace_edge = EclPrototype(
        "void           fault_block_trace_edge( fault_block, double_vector , double_vector , int_vector)"
    )
    _get_neighbours = EclPrototype(
        "void           fault_block_list_neighbours( fault_block , bool , geo_polygon_collection , int_vector)"
    )
    _free = EclPrototype("void           fault_block_free__(fault_block)")

    def __init__(self, *args, **kwargs):
        raise NotImplementedError("Class can not be instantiated directly!")

    def __getitem__(self, index):
        if isinstance(index, int):
            if index < 0:
                index += len(self)

            if 0 <= index < len(self):
                x = ctypes.c_double()
                y = ctypes.c_double()
                z = ctypes.c_double()

                i = ctypes.c_int()
                j = ctypes.c_int()
                k = ctypes.c_int()

                self._export_cell(index, ctypes.byref(i), ctypes.byref(j),
                                  ctypes.byref(k), ctypes.byref(x),
                                  ctypes.byref(y), ctypes.byref(z))
                return FaultBlockCell(i.value, j.value, k.value, x.value,
                                      y.value, z.value)
            else:
                raise IndexError("Index:%d out of range: [0,%d)" %
                                 (index, len(self)))
        else:
            raise TypeError("Index:%s wrong type - integer expected")

    def __str__(self):
        return "Block ID: %d" % self.getBlockID()

    def __len__(self):
        return self._get_size()

    def free(self):
        self._free()

    def getCentroid(self):
        xc = self._get_xc()
        yc = self._get_yc()
        return (xc, yc)

    def countInside(self, polygon):
        """
        Will count the number of points in block which are inside polygon.
        """
        inside = 0
        for p in self:
            if GeometryTools.pointInPolygon((p.x, p.y), polygon):
                inside += 1

        return inside

    def getBlockID(self):
        return self._get_block_id()

    def assignToRegion(self, region_id):
        self._assign_to_region(region_id)

    def getRegionList(self):
        regionList = self._get_region_list()
        return regionList.copy()

    def addCell(self, i, j):
        self._add_cell(i, j)

    def getGlobalIndexList(self):
        return self._get_global_index_list()

    def getEdgePolygon(self):
        x_list = DoubleVector()
        y_list = DoubleVector()
        cell_list = IntVector()

        self._trace_edge(x_list, y_list, cell_list)
        p = Polyline()
        for (x, y) in zip(x_list, y_list):
            p.addPoint(x, y)
        return p

    def containsPolyline(self, polyline):
        """
        Will return true if at least one point from the polyline is inside the block.
        """
        edge_polyline = self.getEdgePolygon()
        for p in polyline:
            if GeometryTools.pointInPolygon(p, edge_polyline):
                return True
        else:
            edge_polyline.assertClosed()
            return GeometryTools.polylinesIntersect(edge_polyline, polyline)

    def getNeighbours(self, polylines=None, connected_only=True):
        """
        Will return a list of FaultBlock instances which are in direct
        contact with this block.
        """
        neighbour_id_list = IntVector()
        if polylines is None:
            polylines = CPolylineCollection()

        self._get_neighbours(connected_only, polylines, neighbour_id_list)

        parent_layer = self.getParentLayer()
        neighbour_list = []
        for id in neighbour_id_list:
            neighbour_list.append(parent_layer.getBlock(id))
        return neighbour_list

    def getParentLayer(self):
        return self.parent()
Ejemplo n.º 23
0
class EclGrav(BaseCClass):
    """
    Holding ECLIPSE results for calculating gravity changes.

    The EclGrav class is a collection class holding the results from
    ECLIPSE forward modelling of gravity surveys. Observe that the
    class is focused on the ECLIPSE side of things, and does not have
    any notion of observed values or measurement locations; that
    should be handled by the scope using the EclGrav class.

    Typical use of the EclGrav class involves the following steps:

      1. Create the EclGrav instance.
      2. Add surveys with the add_survey_XXXX() methods.
      3. Evalute the gravitational response with the eval() method.
    """
    TYPE_NAME = "ecl_grav"
    _grav_alloc = EclPrototype("void* ecl_grav_alloc(ecl_grid, ecl_file)",
                               bind=False)
    _free = EclPrototype("void ecl_grav_free(ecl_grav)")
    _add_survey_RPORV = EclPrototype(
        "void*  ecl_grav_add_survey_RPORV(ecl_grav, char*, ecl_file_view)")
    _add_survey_PORMOD = EclPrototype(
        "void*  ecl_grav_add_survey_PORMOD(ecl_grav, char*, ecl_file_view)")
    _add_survey_FIP = EclPrototype(
        "void*  ecl_grav_add_survey_FIP(ecl_grav, char*, ecl_file_view)")
    _add_survey_RFIP = EclPrototype(
        "void*  ecl_grav_add_survey_RFIP(ecl_grav, char*, ecl_file_view)")
    _new_std_density = EclPrototype(
        "void ecl_grav_new_std_density(ecl_grav, int, double)")
    _add_std_density = EclPrototype(
        "void ecl_grav_add_std_density(ecl_grav, int, int, double)")
    _eval = EclPrototype(
        "double ecl_grav_eval(ecl_grav, char*, char*, ecl_region, double, double, double, int)"
    )

    def __init__(self, grid, init_file):
        """
        Creates a new EclGrav instance.

        The input arguments @grid and @init_file should be instances
        of EclGrid and EclFile respectively.
        """
        self.init_file = init_file  # Inhibit premature garbage collection of init_file

        c_ptr = self._grav_alloc(grid, init_file)
        super(EclGrav, self).__init__(c_ptr)

        self.dispatch = {
            "FIP": self.add_survey_FIP,
            "RFIP": self.add_survey_RFIP,
            "PORMOD": self.add_survey_PORMOD,
            "RPORV": self.add_survey_RPORV
        }

    def add_survey_RPORV(self, survey_name, restart_view):
        """
        Add new survey based on RPORV keyword.

        Add a new survey; in this context a survey is the state of
        reservoir, i.e. an ECLIPSE restart file. The @survey_name
        input argument will be used when refering to this survey at a
        later stage. The @restart_view input argument should be an
        EclFile instance with data from one report step. A typical way
        to load the @restart_view argument is:

           import datetime
           from ecl.ecl import EclRestartFile
           ...
           ...
           date = datetime.datetime(year, month, day)
           rst_file = EclRestartFile("ECLIPSE.UNRST")
           restart_view1 = rst_file.restartView(sim_time=date)
           restart_view2 = rst_file.restartView(report_step=67)

        The pore volume of each cell will be calculated based on the
        RPORV keyword from the restart files. The methods
        add_survey_PORMOD() and add_survey_FIP() are alternatives
        which are based on other keywords.
        """
        self._add_survey_RPORV(survey_name, restart_view)

    def add_survey_PORMOD(self, survey_name, restart_view):
        """
        Add new survey based on PORMOD keyword.

        The pore volum is calculated from the initial pore volume and
        the PORV_MOD keyword from the restart file; see
        add_survey_RPORV() for further details.
        """
        self._add_survey_PORMOD(survey_name, restart_view)

    def add_survey_FIP(self, survey_name, restart_view):
        """
        Add new survey based on FIP keywords.

        This method adds a survey as add_survey_RPORV() and
        add_survey_PORMOD; but the mass content in each cell is
        calculated based on the FIPxxx keyword along with the mass
        density at standard conditions of the respective phases.

        The mass density at standard conditions must be specified with
        the new_std_density() (and possibly also add_std_density())
        method before calling the add_survey_FIP() method.
        """
        self._add_survey_FIP(survey_name, restart_view)

    def add_survey_RFIP(self, survey_name, restart_view):
        """
        Add new survey based on RFIP keywords.

        This method adds a survey as add_survey_RPORV() and
        add_survey_PORMOD; but the mass content in each cell is
        calculated based on the RFIPxxx keyword along with the
        per-cell mass density of the respective phases.
        """
        self._add_survey_RFIP(survey_name, restart_view)

    def add_survey(self, name, restart_view, method):
        method = self.dispatch[method]
        return method(name, restart_view)

    def eval(self,
             base_survey,
             monitor_survey,
             pos,
             region=None,
             phase_mask=EclPhaseEnum.ECL_OIL_PHASE +
             EclPhaseEnum.ECL_GAS_PHASE + EclPhaseEnum.ECL_WATER_PHASE):
        """
        Calculates the gravity change between two surveys.

        This is the method everything is leading up to; will calculate
        the change in gravitational strength, in units of micro Gal,
        between the two surveys named @base_survey and
        @monitor_survey.

        The monitor survey can be 'None' - the resulting answer has
        nothing whatsovever to do with gravitation, but can be
        interesting to determine the numerical size of the quantities
        which are subtracted in a 4D study.

        The @pos argument should be a tuple of three elements with the
        (utm_x, utm_y, depth) position where we want to evaluate the
        change in gravitational strength.

        If supplied the optional argument @region should be an
        EclRegion() instance; this region will be used to limit the
        part of the reserviour included in the gravity calculations.

        The optional argument @phase_mask is an integer flag to
        indicate which phases you are interested in. It should be a
        sum of the relevant integer constants 'ECL_OIL_PHASE',
        'ECL_GAS_PHASE' and 'ECL_WATER_PHASE'.
        """
        return self._eval(base_survey, monitor_survey, region, pos[0], pos[1],
                          pos[2], phase_mask)

    def new_std_density(self, phase_enum, default_density):
        """
        Adds a new phase with a corresponding density.

        @phase_enum is one of the integer constants ECL_OIL_PHASE,
        ECL_GAS_PHASE or ECL_WATER_PHASE, all available in the
        ecl_util and also ecl modules.

        @default_density is the density, at standard conditions, for
        this particular phase. By default @default_density will be
        used for all the cells in the model; by using the
        add_std_density() method you can specify different densities
        for different PVT regions.

        The new_std_density() and add_std_density() methods must be
        used before you use the add_survey_FIP() method to add a
        survey based on the FIP keyword.
        """
        self._new_std_density(phase_enum, default_density)

    def add_std_density(self, phase_enum, pvtnum, density):
        """
        Add standard conditions density for PVT region @pvtnum.

        The new_std_density() method will add a standard conditions
        density which applies to all cells in the model. Using the
        add_std_density() method it is possible to add standard
        conditions densities on a per PVT region basis. You can add
        densities for as many PVT regions as you like, and then fall
        back to the default density for the others.

        The new_std_density() method must be called before calling the
        add_std_density() method.

        The new_std_density() and add_std_density() methods must be
        used before you use the add_survey_FIP() method to add a
        survey based on the FIP keyword.
        """
        self._add_std_density(phase_enum, pvtnum, density)

    def free(self):
        self._free()
Ejemplo n.º 24
0
class EclIndexedReadTest(ExtendedTestCase):
    _freadIndexedData = EclPrototype(
        "void ecl_kw_fread_indexed_data_python(fortio, int, ecl_data_type, int, int_vector, char*)",
        bind=False)  # fortio, offset, type, count, index_map, buffer
    _eclFileIndexedRead = EclPrototype(
        "void ecl_file_indexed_read(ecl_file, char*, int, int_vector, char*)",
        bind=False)  # ecl_file, kw, index, index_map, buffer

    def test_ecl_kw_indexed_read(self):
        with TestAreaContext("ecl_kw_indexed_read") as area:
            fortio = FortIO("index_test", mode=FortIO.WRITE_MODE)

            element_count = 100000
            ecl_kw = EclKW("TEST", element_count, EclDataType.ECL_INT)

            for index in range(element_count):
                ecl_kw[index] = index

            ecl_kw.fwrite(fortio)

            fortio.close()

            fortio = FortIO("index_test", mode=FortIO.READ_MODE)

            new_ecl_kw = EclKW.fread(fortio)

            for index in range(element_count):
                self.assertEqual(new_ecl_kw[index], index)

            index_map = IntVector()
            index_map.append(2)
            index_map.append(3)
            index_map.append(5)
            index_map.append(7)
            index_map.append(11)
            index_map.append(13)
            index_map.append(313)
            index_map.append(1867)
            index_map.append(5227)
            index_map.append(7159)
            index_map.append(12689)
            index_map.append(18719)
            index_map.append(32321)
            index_map.append(37879)
            index_map.append(54167)
            index_map.append(77213)
            index_map.append(88843)
            index_map.append(99991)

            char_buffer = ctypes.create_string_buffer(
                len(index_map) * ctypes.sizeof(ctypes.c_int))

            self._freadIndexedData(fortio, 24, EclDataType.ECL_INT,
                                   element_count, index_map, char_buffer)

            int_buffer = ctypes.cast(char_buffer, ctypes.POINTER(ctypes.c_int))

            for index, index_map_value in enumerate(index_map):
                self.assertEqual(index_map_value, int_buffer[index])

    def test_ecl_file_indexed_read(self):
        with TestAreaContext("ecl_file_indexed_read") as area:
            fortio = FortIO("ecl_file_index_test", mode=FortIO.WRITE_MODE)

            element_count = 100000
            ecl_kw_1 = EclKW("TEST1", element_count, EclDataType.ECL_INT)
            ecl_kw_2 = EclKW("TEST2", element_count, EclDataType.ECL_INT)

            for index in range(element_count):
                ecl_kw_1[index] = index
                ecl_kw_2[index] = index + 3

            ecl_kw_1.fwrite(fortio)
            ecl_kw_2.fwrite(fortio)

            fortio.close()

            ecl_file = EclFile("ecl_file_index_test")

            index_map = IntVector()
            index_map.append(2)
            index_map.append(3)
            index_map.append(5)
            index_map.append(7)
            index_map.append(11)
            index_map.append(13)
            index_map.append(313)
            index_map.append(1867)
            index_map.append(5227)
            index_map.append(7159)
            index_map.append(12689)
            index_map.append(18719)
            index_map.append(32321)
            index_map.append(37879)
            index_map.append(54167)
            index_map.append(77213)
            index_map.append(88843)
            index_map.append(99991)

            char_buffer_1 = ctypes.create_string_buffer(
                len(index_map) * ctypes.sizeof(ctypes.c_int))
            char_buffer_2 = ctypes.create_string_buffer(
                len(index_map) * ctypes.sizeof(ctypes.c_int))

            self._eclFileIndexedRead(ecl_file, "TEST2", 0, index_map,
                                     char_buffer_2)
            self._eclFileIndexedRead(ecl_file, "TEST1", 0, index_map,
                                     char_buffer_1)

            int_buffer_1 = ctypes.cast(char_buffer_1,
                                       ctypes.POINTER(ctypes.c_int))
            int_buffer_2 = ctypes.cast(char_buffer_2,
                                       ctypes.POINTER(ctypes.c_int))

            for index, index_map_value in enumerate(index_map):
                self.assertEqual(index_map_value, int_buffer_1[index])
                self.assertEqual(index_map_value, int_buffer_2[index] - 3)
Ejemplo n.º 25
0
class EclGridGenerator:

    _alloc_rectangular = EclPrototype(
        "ecl_grid_obj ecl_grid_alloc_rectangular(int, int, int, double, double, double, int*)",
        bind=False)

    @classmethod
    def createRectangular(cls, dims, dV, actnum=None):
        """
        Will create a new rectangular grid. @dims = (nx,ny,nz)  @dVg = (dx,dy,dz)

        With the default value @actnum == None all cells will be active,
        """
        if actnum is None:
            ecl_grid = cls._alloc_rectangular(dims[0], dims[1], dims[2], dV[0],
                                              dV[1], dV[2], None)
        else:
            if not isinstance(actnum, IntVector):
                tmp = IntVector(initial_size=len(actnum))
                for (index, value) in enumerate(actnum):
                    tmp[index] = value
                actnum = tmp

            if not len(actnum) == dims[0] * dims[1] * dims[2]:
                raise ValueError(
                    "ACTNUM size mismatch: len(ACTNUM):%d  Expected:%d" %
                    (len(actnum), dims[0] * dims[1] * dims[2]))

            ecl_grid = cls._alloc_rectangular(dims[0], dims[1], dims[2],
                                              dV[0], dV[1], dV[2],
                                              actnum.getDataPtr())

        # If we have not succeeded in creatin the grid we *assume* the
        # error is due to a failed malloc.
        if ecl_grid is None:
            raise MemoryError("Failed to allocated regualar grid")

        return ecl_grid

    @classmethod
    def create_single_cell_grid(cls, corners):
        """
        Provided with the corners of the grid in a similar manner as the eight
        corners are output for a single cell, this method will create a grid
        consisting of a single cell with the specified corners as its corners.
        """

        zcorn = [corners[i][2] for i in range(8)]

        coord = [(corners[i], corners[i + 4]) for i in range(4)]
        coord = flatten(flatten(coord))

        def construct_floatKW(name, values):
            kw = EclKW(name, len(values), EclDataType.ECL_FLOAT)
            for i in range(len(values)):
                kw[i] = values[i]
            return kw

        grid = EclGrid.create((1, 1, 1), construct_floatKW("ZCORN", zcorn),
                              construct_floatKW("COORD", coord), None)

        if not corners == [grid.getCellCorner(i, 0) for i in range(8)]:
            raise AssertionError("Failed to generate single cell grid. " +
                                 "Did not end up the expected corners.")

        return grid

    @classmethod
    def create_zcorn(cls,
                     dims,
                     dV,
                     offset=1,
                     escape_origo_shift=(1, 1, 0),
                     irregular_offset=False,
                     irregular=False,
                     concave=False,
                     faults=False):

        cls.__assert_zcorn_parameters(dims, dV, offset, escape_origo_shift,
                                      irregular_offset, irregular, concave,
                                      faults)

        nx, ny, nz = dims
        dx, dy, dz = dV

        # Compute zcorn
        z = escape_origo_shift[2]
        zcorn = [z] * (4 * nx * ny)
        for k in range(nz - 1):
            z = z + dz
            local_offset = offset + (dz / 2.
                                     if irregular_offset and k % 2 == 0 else 0)

            layer = []
            for i in range(ny + 1):
                shift = ((i if concave else 0) +
                         (k / 2 if irregular else 0)) % 2
                path = [
                    z if i % 2 == shift else z + local_offset
                    for i in range(nx + 1)
                ]
                layer.append(duplicate_inner(path))

            zcorn = zcorn + (2 * flatten(duplicate_inner(layer)))

        z = z + dz
        zcorn = zcorn + ([z] * (4 * nx * ny))

        if faults:
            # Ensure that drop does not align with grid structure
            drop = (offset + dz) / 2. if abs(offset -
                                             dz / 2.) > 0.2 else offset + 0.4
            zcorn = cls.__create_faults(nx, ny, nz, zcorn, drop)

        if z != escape_origo_shift[2] + nz * dz:
            raise ValueError("%f != %f" % (z, escape_origo_shift[2] + nz * dz))

        cls.assert_zcorn(nx, ny, nz, zcorn)
        return construct_floatKW("ZCORN", zcorn)

    @classmethod
    def create_coord(cls,
                     dims,
                     dV,
                     escape_origo_shift=(1, 1, 0),
                     scale=1,
                     translation=(0, 0, 0),
                     rotate=False,
                     misalign=False):

        nx, ny, nz = dims
        dx, dy, dz = dV

        # Compute coord
        z = escape_origo_shift[2] + nz * dz
        coord = []
        for j, i in itertools.product(range(ny + 1), range(nx + 1)):
            x, y = i * dx + escape_origo_shift[0], j * dy + escape_origo_shift[
                1]
            coord = coord + [x, y, escape_origo_shift[2], x, y, z]

        # Apply transformations
        lower_center = (nx * dx / 2. + escape_origo_shift[0],
                        ny * dy / 2. + escape_origo_shift[1])

        if misalign:
            coord = cls.__misalign_coord(coord, dims, dV)

        coord = cls.__scale_coord(coord, scale, lower_center)

        if rotate:
            coord = cls.__rotate_coord(coord, lower_center)

        coord = cls.__translate_lower_coord(coord, translation)

        cls.assert_coord(nx, ny, nz, coord)
        return construct_floatKW("COORD", coord)

    @classmethod
    def __assert_zcorn_parameters(cls, dims, dV, offset, escape_origo_shift,
                                  irregular_offset, irregular, concave,
                                  faults):

        nx, ny, nz = dims
        dx, dy, dz = dV

        # Validate arguments
        if min(dims + dV) <= 0:
            raise ValueError("Expected positive grid and cell dimentions")

        if offset < 0:
            raise ValueError("Expected non-negative offset")

        if irregular and offset + (dz / 2. if irregular_offset else 0) > dz:
            raise AssertionError(
                "Arguments can result in self-" +
                "intersecting cells. Increase dz, deactivate eiter " +
                "irregular or irregular_offset, or decrease offset to avoid " +
                "any problems")

    @classmethod
    def create_grid(cls,
                    dims,
                    dV,
                    offset=1,
                    escape_origo_shift=(1, 1, 0),
                    irregular_offset=False,
                    irregular=False,
                    concave=False,
                    faults=False,
                    scale=1,
                    translation=(0, 0, 0),
                    rotate=False,
                    misalign=False):
        """
        Will create a new grid where each cell is a parallelogram (skewed by z-value).
        The number of cells are given by @dims = (nx, ny, nz) and the dimention
        of each cell by @dV = (dx, dy, dz).

        All cells are guaranteed to not be self-intersecting. Hence, no twisted
        cells and somewhat meaningfull cells.

        @offset gives how much the layers should fluctuate or "wave" as you
        move along the X-axis.

        @irregular_offset decides whether the offset should be constant or
        increase by dz/2 every now and then.

        @irregular if true some of the layers will be inclining and others
        declining at the start.

        @concave decides whether the cells are to be convex or not. In
        particular, if set to False, all cells of the grid will be concave.

        @escape_origo_shift is used to prevent any cell of having corners in (0,0,z)
        as there is a heuristic in ecl_grid.c that marks such cells as tainted.

        @faults decides if there are to be faults in the grid.

        @scale A positive number that scales the "lower" endpoint of all
        coord's. In particular, @scale != 1 creates trapeziod cells in both the XZ
        and YZ-plane.

        @translation the lower part of the grid is translated ("slided") by the specified
        additive factor.

        @rotate the lower part of the grid is rotated 90 degrees around its
        center.

        @misalign will toggle COORD's slightly in various directions to break
        alignment

        Note that cells in the lowermost layer can have multiple corners
        at the same point.

        For testing it should give good coverage of the various scenarios this
        method can produce, by leting @dims be (10,10,10), @dV=(2,2,2), @offset=1,
        and try all 4 different configurations of @concave and
        @irregular_offset.
        """

        zcorn = cls.create_zcorn(dims, dV, offset, escape_origo_shift,
                                 irregular_offset, irregular, concave, faults)

        coord = cls.create_coord(dims, dV, escape_origo_shift, scale,
                                 translation, rotate, misalign)

        return EclGrid.create(dims, zcorn, coord, None)

    @classmethod
    def __create_faults(cls, nx, ny, nz, zcorn, drop):
        """
        Will create several faults consisting of all cells such that either its
        i or j index is 1 modulo 3.
        """

        plane_size = 4 * nx * ny
        for x, y, z in itertools.product(range(nx), range(ny), range(nz)):
            if x % 3 != 1 and y % 3 != 1:
                continue

            corner = [0] * 8
            corner[0] = 2 * z * plane_size + 4 * y * nx + 2 * x
            corner[1] = corner[0] + 1
            corner[2] = corner[0] + 2 * nx
            corner[3] = corner[2] + 1

            for i in range(4, 8):
                corner[i] = corner[i - 4] + plane_size

            for c in corner:
                zcorn[c] = zcorn[c] + drop

        return zcorn

    @classmethod
    def assert_zcorn(cls, nx, ny, nz, zcorn, twisted_check=True):
        """

        Raises an AssertionError if the zcorn is not as expected. In
        patricular, it is verified that:

            - zcorn has the approperiate length (8*nx*ny*nz) and
            - that no cell is twisted.

        """

        if len(zcorn) != 8 * nx * ny * nz:
            raise AssertionError("Expected len(zcorn) to be %d, was %d" %
                                 (8 * nx * ny * nz, len(zcorn)))

        plane_size = 4 * nx * ny
        for p in range(8 * nx * ny * nz - plane_size):
            if zcorn[p] > zcorn[p + plane_size] and twisted_check:
                raise AssertionError(
                    "Twisted cell was created. " +
                    "Decrease offset or increase dz to avoid this!")

    @classmethod
    def __scale_coord(cls, coord, scale, lower_center):
        coord = numpy.array(
            [map(float, coord[i:i + 6:]) for i in range(0, len(coord), 6)])
        origo = numpy.array(3 * [0.] + list(lower_center) + [0])
        scale = numpy.array(3 * [1.] + 2 * [scale] + [1])

        coord = scale * (coord - origo) + origo
        return coord.flatten().tolist()

    @classmethod
    def __misalign_coord(cls, coord, dims, dV):
        nx, ny, nz = dims

        coord = numpy.array(
            [map(float, coord[i:i + 6:]) for i in range(0, len(coord), 6)])

        tf = lambda i, j: 1. / 2 if abs(i) + abs(j) <= 1 else 0.25
        adjustment = numpy.array([
            (0, 0, 0, i * tf(i, j) * dV[0], j * tf(i, j) * dV[1], 0)
            for i, j in itertools.product([-1, 0, 1], repeat=2)
        ])

        for i, c in enumerate(coord):
            # Leave the outermost coords alone
            if i < nx + 1 or i >= len(coord) - (nx + 1):
                continue
            if i % (nx + 1) in [0, nx]:
                continue

            c += adjustment[i % len(adjustment)]

        return coord.flatten().tolist()

    @classmethod
    def __rotate_coord(cls, coord, lower_center):
        coord = numpy.array(
            [map(float, coord[i:i + 6:]) for i in range(0, len(coord), 6)])

        origo = numpy.array(3 * [0.] + list(lower_center) + [0])
        coord -= origo

        for c in coord:
            c[3], c[4] = -c[4], c[3]

        coord += origo
        return coord.flatten().tolist()

    @classmethod
    def __translate_lower_coord(cls, coord, translation):
        coord = numpy.array(
            [map(float, coord[i:i + 6:]) for i in range(0, len(coord), 6)])
        translation = numpy.array(3 * [0.] + list(translation))

        coord = coord + translation
        return coord.flatten().tolist()

    @classmethod
    def assert_coord(cls, nx, ny, nz, coord, negative_values=False):
        """

        Raises an AssertionError if the coord is not as expected. In
        particular, it is verified that:

            - coord has the approperiate length (6*(nx+1)*(ny+1)) and
            - that all values are positive unless negative_values are
              explicitly allowed.

        """

        if len(coord) != 6 * (nx + 1) * (ny + 1):
            raise AssertionError("Expected len(coord) to be %d, was %d" %
                                 (6 * (nx + 1) * (ny + 1), len(coord)))

        if not negative_values and min(coord) < 0:
            raise AssertionError(
                "Negative COORD values was generated. " +
                "This is likely due to a tranformation. " +
                "Increasing the escape_origio_shift will most likely " +
                "fix the problem")

    @classmethod
    def assert_actnum(cls, nx, ny, nz, actnum):
        """

        Raises an AssertionError if the actnum is not as expected. In
        particular, it is verified that:

            - actnum has the approperiate length nx*ny*nz and
            - that all values are either 0 or 1.

        """

        if actnum is None:
            return

        if len(actnum) != nx * ny * nz:
            raise AssertionError(
                "Expected the length of ACTNUM to be %d, was %s." %
                (nx * ny * nz, len(actnum)))

        if set(actnum) - set([0, 1]):
            raise AssertionError(
                "Expected ACTNUM to consist of 0's and 1's, was %s." %
                ", ".join(map(str, set(actnum))))

    @classmethod
    def extract_coord(cls, dims, coord, ijk_bounds):
        nx, ny, nz = dims
        (lx, ux), (ly, uy), (lz, uz) = ijk_bounds
        new_nx, new_ny, new_nz = ux - lx + 1, uy - ly + 1, uz - lz + 1

        cls.assert_coord(nx, ny, nz, coord, negative_values=True)

        # Format COORD
        coord = divide(divide(coord, 6), nx + 1)

        # Extract new COORD
        new_coord = [
            coord_slice[lx:ux + 2:] for coord_slice in coord[ly:uy + 2]
        ]

        # Flatten and verify
        new_coord = flatten(flatten(new_coord))
        cls.assert_coord(new_nx,
                         new_ny,
                         new_nz,
                         new_coord,
                         negative_values=True)

        return construct_floatKW("COORD", new_coord)

    @classmethod
    def extract_zcorn(cls, dims, zcorn, ijk_bounds):
        nx, ny, nz = dims
        (lx, ux), (ly, uy), (lz, uz) = ijk_bounds
        new_nx, new_ny, new_nz = ux - lx + 1, uy - ly + 1, uz - lz + 1

        cls.assert_zcorn(nx, ny, nz, zcorn, twisted_check=False)

        # Format ZCORN
        zcorn = divide(divide(zcorn, 2 * nx), 2 * ny)

        # Extract new ZCORN
        new_zcorn = [
            y_slice[2 * lx:2 * ux + 2:]
            for z_slice in zcorn[2 * lz:2 * uz + 2:]
            for y_slice in z_slice[2 * ly:2 * uy + 2:]
        ]

        # Flatten and verify
        new_zcorn = flatten(new_zcorn)
        cls.assert_zcorn(new_nx, new_ny, new_nz, new_zcorn)

        return construct_floatKW("ZCORN", new_zcorn)

    @classmethod
    def extract_actnum(cls, dims, actnum, ijk_bounds):
        if actnum is None:
            return None

        nx, ny, nz = dims
        (lx, ux), (ly, uy), (lz, uz) = ijk_bounds
        new_nx, new_ny, new_nz = ux - lx + 1, uy - ly + 1, uz - lz + 1

        cls.assert_actnum(nx, ny, nz, actnum)

        actnum = divide(divide(actnum, nx), ny)

        new_actnum = [
            y_slice[lx:ux + 1:] for z_slice in actnum[lz:uz + 1:]
            for y_slice in z_slice[ly:uy + 1:]
        ]

        new_actnum = flatten(new_actnum)
        cls.assert_actnum(new_nx, new_ny, new_nz, new_actnum)

        actnumkw = EclKW("ACTNUM", len(new_actnum), EclDataType.ECL_INT)
        for i, value in enumerate(new_actnum):
            actnumkw[i] = value

        return actnumkw

    @classmethod
    def __translate_coord(cls, coord, translation):
        coord = numpy.array(
            [map(float, coord[i:i + 6:]) for i in range(0, len(coord), 6)])
        translation = numpy.array(list(translation) + list(translation))

        coord = coord + translation
        return construct_floatKW("COORD", coord.flatten().tolist())

    @classmethod
    def extract_subgrid(cls,
                        grid,
                        ijk_bounds,
                        decomposition_change=False,
                        translation=None):
        """
        Extracts a subgrid from the given grid according to the specified
        bounds.

        @ijk_bounds: The bounds describing the subgrid. Should be a tuple of
        length 3, where each element gives the bound for the i, j, k
        coordinates of the subgrid to be described, respectively. Each bound
        should either be an interval of the form (a, b) where 0 <= a <= b < nx
        or a single integer a which is equivialent to the bound (a, a).

        NOTE: The given bounds are including endpoints.

        @decomposition_change: Depending on the given ijk_bounds, libecl might
        decompose the cells of the subgrid differently when extracted from
        grid. This is somewhat unexpected behaviour and if this event occur we
        give an exception together with an description for how to avoid this,
        unless decompostion_change is set to True.

        @translation: Gives the possibility of translating the subgrid. Should
        be given as a tuple (dx, dy, dz), where each coordinate of the grid
        will be moved by di in direction i.

        """

        gdims = grid.getDims()[:-1:]
        nx, ny, nz = gdims
        ijk_bounds = cls.assert_ijk_bounds(gdims, ijk_bounds)

        coord = grid.export_coord()
        cls.assert_coord(nx, ny, nz, coord, negative_values=True)

        zcorn = grid.export_zcorn()
        cls.assert_zcorn(nx, ny, nz, zcorn)

        actnum = grid.export_actnum()
        cls.assert_actnum(nx, ny, nz, actnum)

        mapaxes = grid.export_mapaxes()

        sub_data = cls.extract_subgrid_data(
            gdims,
            coord,
            zcorn,
            ijk_bounds=ijk_bounds,
            actnum=actnum,
            mapaxes=mapaxes,
            decomposition_change=decomposition_change,
            translation=translation)

        sdim = tuple([b - a + 1 for a, b in ijk_bounds])
        sub_coord, sub_zcorn, sub_actnum = sub_data

        return EclGrid.create(sdim,
                              sub_zcorn,
                              sub_coord,
                              sub_actnum,
                              mapaxes=mapaxes)

    @classmethod
    def extract_subgrid_data(cls,
                             dims,
                             coord,
                             zcorn,
                             ijk_bounds,
                             actnum=None,
                             mapaxes=None,
                             decomposition_change=False,
                             translation=None):
        """

        Extracts subgrid data from COORD, ZCORN and potentially ACTNUM. It
        returns similar formatted data for the subgrid described by the bounds.

        @dims: The dimentions (nx, ny, nz) of the grid

        @coord: The COORD data of the grid.

        @zcorn: The ZCORN data of the grid.

        @ijk_bounds: The bounds describing the subgrid. Should be a tuple of
        length 3, where each element gives the bound for the i, j, k
        coordinates of the subgrid to be described, respectively. Each bound
        should either be an interval of the form (a, b) where 0 <= a <= b < nx
        or a single integer a which is equivialent to the bound (a, a).

        NOTE: The given bounds are including endpoints.

        @actnum: The ACTNUM data of the grid.

        @mapaxes The MAPAXES data of the grid.

        @decomposition_change: Depending on the given ijk_bounds, libecl might
        decompose the cells of the subgrid differently when extracted from
        grid. This is somewhat unexpected behaviour and if this event occur we
        give an exception together with an description for how to avoid this,
        unless decompostion_change is set to True.

        @translation: Gives the possibility of translating the subgrid. Should
        be given as a tuple (dx, dy, dz), where each coordinate of the grid
        will be moved by di in direction i.

        """
        coord, zcorn = list(coord), list(zcorn)
        actnum = None if actnum is None else list(actnum)

        ijk_bounds = cls.assert_ijk_bounds(dims, ijk_bounds)
        cls.assert_decomposition_change(ijk_bounds, decomposition_change)

        nx, ny, nz = dims
        (lx, ux), (ly, uy), (lz, uz) = ijk_bounds
        new_nx, new_ny, new_nz = ux - lx + 1, uy - ly + 1, uz - lz + 1

        new_coord = cls.extract_coord(dims, coord, ijk_bounds)
        new_zcorn = cls.extract_zcorn(dims, zcorn, ijk_bounds)
        new_actnum = cls.extract_actnum(dims, actnum, ijk_bounds)

        if translation is not None:
            mtranslation = pre_mapaxes_translation(translation, mapaxes)
            new_coord = cls.__translate_coord(new_coord, mtranslation)

            for i in range(len(new_zcorn)):
                new_zcorn[i] += translation[2]

        return new_coord, new_zcorn, new_actnum

    @classmethod
    def assert_ijk_bounds(cls, dims, ijk_bounds):
        ijk_bounds = list(ijk_bounds)

        for i in range(len(ijk_bounds)):
            if isinstance(ijk_bounds[i], int):
                ijk_bounds[i] = [ijk_bounds[i]]
            if len(ijk_bounds[i]) == 1:
                ijk_bounds[i] += ijk_bounds[i]

        if len(ijk_bounds) != 3:
            raise ValueError(
                "Expected ijk_bounds to contain three intervals, " +
                "contained only %d" % len(ijk_bounds))

        for n, bound in zip(dims, ijk_bounds):
            if len(bound) != 2:
                raise ValueError(
                    "Expected bound to consist of two elements, was %s",
                    str(bound))

            if not (isinstance(bound[0], int) and isinstance(bound[1], int)):
                raise TypeError(
                    "Expected bound to consist of two integers, ",
                    "was %s (%s)" % (str(bound), str((map(type, bound)))))

            if not (0 <= bound[0] <= bound[1] < n):
                raise ValueError(
                    "Expected bounds to have the following format: " +
                    "0 <= lower bound <= upper_bound < ni, " +
                    "was %d <=? %d <=? %d <? %d." % (0, bound[0], bound[1], n))

        return ijk_bounds

    @classmethod
    def assert_decomposition_change(cls, ijk_bounds, decomposition_change):
        if sum(zip(*ijk_bounds)[0]) % 2 == 1 and not decomposition_change:
            raise ValueError(
                "The subgrid defined by %s " % str(ijk_bounds) +
                "will cause an unintended decomposition change. " +
                "Either change one of the lower bounds by 1 " +
                "or activate decomposition_change.")
Ejemplo n.º 26
0
class EclGrid(BaseCClass):
    """
    Class for loading and internalizing ECLIPSE GRID/EGRID files.
    """

    TYPE_NAME = "ecl_grid"
    _fread_alloc                  = EclPrototype("void* ecl_grid_load_case__( char* , bool )" , bind = False)
    _grdecl_create                = EclPrototype("ecl_grid_obj ecl_grid_alloc_GRDECL_kw( int , int , int , ecl_kw , ecl_kw , ecl_kw , ecl_kw)" , bind = False)
    _alloc_rectangular            = EclPrototype("ecl_grid_obj ecl_grid_alloc_rectangular( int , int , int , double , double , double , int*)" , bind = False)
    _exists                       = EclPrototype("bool ecl_grid_exists( char* )" , bind = False)

    _get_lgr                      = EclPrototype("ecl_grid_ref ecl_grid_get_lgr( ecl_grid , char* )")
    _get_cell_lgr                 = EclPrototype("ecl_grid_ref ecl_grid_get_cell_lgr1( ecl_grid , int )")
    _num_coarse_groups            = EclPrototype("int  ecl_grid_get_num_coarse_groups( ecl_grid )")
    _in_coarse_group1             = EclPrototype("bool ecl_grid_cell_in_coarse_group1( ecl_grid , int)")
    _free                         = EclPrototype("void ecl_grid_free( ecl_grid )")
    _get_nx                       = EclPrototype("int ecl_grid_get_nx( ecl_grid )")
    _get_ny                       = EclPrototype("int ecl_grid_get_ny( ecl_grid )")
    _get_nz                       = EclPrototype("int ecl_grid_get_nz( ecl_grid )")
    _get_global_size              = EclPrototype("int ecl_grid_get_global_size( ecl_grid )")
    _get_active                   = EclPrototype("int ecl_grid_get_active_size( ecl_grid )")
    _get_active_fracture          = EclPrototype("int ecl_grid_get_nactive_fracture( ecl_grid )")
    _get_name                     = EclPrototype("char* ecl_grid_get_name( ecl_grid )")
    _ijk_valid                    = EclPrototype("bool ecl_grid_ijk_valid(ecl_grid , int , int , int)")
    _get_active_index3            = EclPrototype("int ecl_grid_get_active_index3( ecl_grid , int , int , int)")
    _get_global_index3            = EclPrototype("int ecl_grid_get_global_index3( ecl_grid , int , int , int)")
    _get_active_index1            = EclPrototype("int ecl_grid_get_active_index1( ecl_grid , int )")
    _get_active_fracture_index1   = EclPrototype("int ecl_grid_get_active_fracture_index1( ecl_grid , int )")
    _get_global_index1A           = EclPrototype("int ecl_grid_get_global_index1A( ecl_grid , int )")
    _get_global_index1F           = EclPrototype("int ecl_grid_get_global_index1F( ecl_grid , int )")
    _get_ijk1                     = EclPrototype("void ecl_grid_get_ijk1( ecl_grid , int , int* , int* , int*)")
    _get_ijk1A                    = EclPrototype("void ecl_grid_get_ijk1A( ecl_grid , int , int* , int* , int*)")
    _get_xyz3                     = EclPrototype("void ecl_grid_get_xyz3( ecl_grid , int , int , int , double* , double* , double*)")
    _get_xyz1                     = EclPrototype("void ecl_grid_get_xyz1( ecl_grid , int , double* , double* , double*)")
    _get_cell_corner_xyz1         = EclPrototype("void ecl_grid_get_cell_corner_xyz1( ecl_grid , int , int , double* , double* , double*)")
    _get_corner_xyz               = EclPrototype("void ecl_grid_get_corner_xyz( ecl_grid , int , int , int, double* , double* , double*)")
    _get_xyz1A                    = EclPrototype("void ecl_grid_get_xyz1A( ecl_grid , int , double* , double* , double*)")
    _get_ij_xy                    = EclPrototype("bool ecl_grid_get_ij_from_xy( ecl_grid , double , double , int , int* , int*)")
    _get_ijk_xyz                  = EclPrototype("int  ecl_grid_get_global_index_from_xyz( ecl_grid , double , double , double , int)")
    _cell_contains                = EclPrototype("bool ecl_grid_cell_contains_xyz1( ecl_grid , int , double , double , double )")
    _cell_regular                 = EclPrototype("bool ecl_grid_cell_regular1( ecl_grid , int)")
    _num_lgr                      = EclPrototype("int  ecl_grid_get_num_lgr( ecl_grid )")
    _has_lgr                      = EclPrototype("bool ecl_grid_has_lgr( ecl_grid , char* )")
    _grid_value                   = EclPrototype("double ecl_grid_get_property( ecl_grid , ecl_kw , int , int , int)")
    _get_cell_volume              = EclPrototype("double ecl_grid_get_cell_volume1( ecl_grid , int )")
    _get_cell_thickness           = EclPrototype("double ecl_grid_get_cell_thickness1( ecl_grid , int )")
    _get_cell_dx                  = EclPrototype("double ecl_grid_get_cell_dx1( ecl_grid , int )")
    _get_cell_dy                  = EclPrototype("double ecl_grid_get_cell_dy1( ecl_grid , int )")
    _get_depth                    = EclPrototype("double ecl_grid_get_cdepth1( ecl_grid , int )")
    _fwrite_grdecl                = EclPrototype("void   ecl_grid_grdecl_fprintf_kw( ecl_grid , ecl_kw , char* , FILE , double)")
    _load_column                  = EclPrototype("void   ecl_grid_get_column_property( ecl_grid , ecl_kw , int , int , double_vector)")
    _get_top                      = EclPrototype("double ecl_grid_get_top2( ecl_grid , int , int )")
    _get_top1A                    = EclPrototype("double ecl_grid_get_top1A(ecl_grid , int )")
    _get_bottom                   = EclPrototype("double ecl_grid_get_bottom2( ecl_grid , int , int )")
    _locate_depth                 = EclPrototype("int    ecl_grid_locate_depth( ecl_grid , double , int , int )")
    _invalid_cell                 = EclPrototype("bool   ecl_grid_cell_invalid1( ecl_grid , int)")
    _valid_cell                   = EclPrototype("bool   ecl_grid_cell_valid1( ecl_grid , int)")
    _get_distance                 = EclPrototype("void   ecl_grid_get_distance( ecl_grid , int , int , double* , double* , double*)")
    _fprintf_grdecl2              = EclPrototype("void   ecl_grid_fprintf_grdecl2( ecl_grid , FILE , ecl_unit_enum) ")
    _fwrite_GRID2                 = EclPrototype("void   ecl_grid_fwrite_GRID2( ecl_grid , char* , ecl_unit_enum)")
    _fwrite_EGRID2                = EclPrototype("void   ecl_grid_fwrite_EGRID2( ecl_grid , char*, ecl_unit_enum)")
    _equal                        = EclPrototype("bool   ecl_grid_compare(ecl_grid , ecl_grid , bool, bool)")
    _dual_grid                    = EclPrototype("bool   ecl_grid_dual_grid( ecl_grid )")
    _init_actnum                  = EclPrototype("void   ecl_grid_init_actnum_data( ecl_grid , int* )")
    _compressed_kw_copy           = EclPrototype("void   ecl_grid_compressed_kw_copy( ecl_grid , ecl_kw , ecl_kw)")
    _global_kw_copy               = EclPrototype("void   ecl_grid_global_kw_copy( ecl_grid , ecl_kw , ecl_kw)")
    _create_volume_keyword        = EclPrototype("ecl_kw_obj ecl_grid_alloc_volume_kw( ecl_grid , bool)")
    _use_mapaxes                  = EclPrototype("bool ecl_grid_use_mapaxes(ecl_grid)")
    _export_coord                 = EclPrototype("ecl_kw_obj ecl_grid_alloc_coord_kw( ecl_grid )")
    _export_zcorn                 = EclPrototype("ecl_kw_obj ecl_grid_alloc_zcorn_kw( ecl_grid )")
    _export_actnum                = EclPrototype("ecl_kw_obj ecl_grid_alloc_actnum_kw( ecl_grid )")
    _export_mapaxes               = EclPrototype("ecl_kw_obj ecl_grid_alloc_mapaxes_kw( ecl_grid )")



    @classmethod
    def loadFromGrdecl(cls , filename):
        """Will create a new EclGrid instance from grdecl file.

        This function will scan the input file @filename and look for
        the keywords required to build a grid. The following keywords
        are required:

              SPECGRID   ZCORN   COORD

        In addition the function will look for and use the ACTNUM and
        MAPAXES keywords if they are found; if ACTNUM is not found all
        cells are assumed to be active.

        Slightly more exotic grid concepts like dual porosity, NNC
        mapping, LGR and coarsened cells will be completely ignored;
        if you need such concepts you must have an EGRID file and use
        the default EclGrid() constructor - that is also considerably
        faster.
        """

        if os.path.isfile(filename):
            with open(filename) as f:
                specgrid = EclKW.read_grdecl(f, "SPECGRID", ecl_type=EclDataType.ECL_INT, strict=False)
                zcorn = EclKW.read_grdecl(f, "ZCORN")
                coord = EclKW.read_grdecl(f, "COORD")
                try:
                    actnum = EclKW.read_grdecl(f, "ACTNUM", ecl_type=EclDataType.ECL_INT)
                except ValueError:
                    actnum = None

                try:
                    mapaxes = EclKW.read_grdecl(f, "MAPAXES")
                except ValueError:
                    mapaxes = None

            return EclGrid.create( specgrid , zcorn , coord , actnum , mapaxes )
        else:
            raise IOError("No such file:%s" % filename)

    @classmethod
    def loadFromFile(cls , filename):
        """
        Will inspect the @filename argument and create a new EclGrid instance.
        """
        if FortIO.isFortranFile( filename ):
            return EclGrid( filename )
        else:
            return EclGrid.loadFromGrdecl( filename )


    @classmethod
    def create(cls , specgrid , zcorn , coord , actnum , mapaxes = None ):

        """
        Create a new grid instance from existing keywords.

        This is a class method which can be used to create an EclGrid
        instance based on the EclKW instances @specgrid, @zcorn,
        @coord and @actnum. An ECLIPSE EGRID file contains the
        SPECGRID, ZCORN, COORD and ACTNUM keywords, so a somewhat
        involved way to create a EclGrid instance could be:

          file = ecl.EclFile( "ECLIPSE.EGRID" )
          specgrid_kw = file.iget_named_kw( "SPECGRID" , 0)
          zcorn_kw = file.iget_named_kw( "ZCORN" , 0)
          coord_kw = file.iget_named_kw( "COORD" , 0)
          actnum_kw = file.iget_named_kw( "ACTNUM" , 0 )

          grid = EclGrid.create( specgrid_kw , zcorn_kw , coord_kw , actnum_kw)

        If you are so inclined ...
        """
        return cls._grdecl_create( specgrid[0] , specgrid[1] , specgrid[2] , zcorn , coord , actnum , mapaxes )

    @classmethod
    def createRectangular(cls, dims , dV , actnum = None):
        """
        Will create a new rectangular grid. @dims = (nx,ny,nz)  @dVg = (dx,dy,dz)

        With the default value @actnum == None all cells will be active,
        """

        warnings.warn("EclGrid.createRectangular is deprecated. " +
                "Please used the similar method in EclGridGenerator!",
                DeprecationWarning)

        if actnum is None:
            ecl_grid = cls._alloc_rectangular( dims[0] , dims[1] , dims[2] , dV[0] , dV[1] , dV[2] , None )
        else:
            if not isinstance(actnum , IntVector):
                tmp = IntVector(initial_size = len(actnum))
                for (index , value) in enumerate(actnum):
                    tmp[index] = value
                actnum = tmp

            if not len(actnum) == dims[0] * dims[1] * dims[2]:
                raise ValueError("ACTNUM size mismatch: len(ACTNUM):%d  Expected:%d" % (len(actnum) , dims[0] * dims[1] * dims[2]))
            ecl_grid = cls._alloc_rectangular( dims[0] , dims[1] , dims[2] , dV[0] , dV[1] , dV[2] , actnum.getDataPtr() )

        # If we have not succeeded in creatin the grid we *assume* the
        # error is due to a failed malloc.
        if ecl_grid is None:
            raise MemoryError("Failed to allocated regualar grid")
            
        return ecl_grid

    def __init__(self , filename , apply_mapaxes = True):
        """
        Will create a grid structure from an EGRID or GRID file.
        """
        c_ptr = self._fread_alloc( filename , apply_mapaxes)
        if c_ptr:
            super(EclGrid, self).__init__(c_ptr)
        else:
            raise IOError("Loading grid from:%s failed" % filename)


    def free(self):
        self._free( )

    def _nicename(self):
        """name is often full path to grid, if so, output basename, else name"""
        name = self.getName()
        if os.path.isfile(name):
            name = os.path.basename(name)
        return name

    def __repr__(self):
        """Returns, e.g.:
           EclGrid("NORNE_ATW2013.EGRID", 46x112x22, global_size = 113344, active_size = 44431) at 0x28c4a70
        """
        name = self._nicename()
        if name:
            name = '"%s", ' % name
        g_size = self.getGlobalSize()
        a_size = self.getNumActive()
        xyz_s  = '%dx%dx%d' % (self.getNX(),self.getNY(),self.getNZ())
        return self._create_repr('%s%s, global_size = %d, active_size = %d' % (name, xyz_s, g_size, a_size))

    def __len__(self):
        """
        len(grid) wil return the total number of cells.
        """
        return self._get_global_size( )

    def equal(self , other , include_lgr = True , include_nnc = False , verbose = False):
        """
        Compare the current grid with the other grid.
        """
        if not isinstance(other , EclGrid):
            raise TypeError("The other argument must be an EclGrid instance")
        return self._equal( other , include_lgr , include_nnc , verbose)


    def dualGrid(self):
        """Is this grid dual porosity model?"""
        return self._dual_grid( )

    def getDims(self):
        """A tuple of four elements: (nx , ny , nz , nactive)."""
        return ( self.getNX(  ) ,
                 self.getNY(  ) ,
                 self.getNZ(  ) ,
                 self.getNumActive(  ) )


    def getNX(self):
        """ The number of elements in the x direction"""
        return self._get_nx(  )

    def getNY(self):
        """ The number of elements in the y direction"""
        return self._get_ny( )

    def getNZ(self):
        """ The number of elements in the z direction"""
        return self._get_nz(  )

    def getGlobalSize(self):
        """Returns the total number of cells in this grid"""
        return self._get_global_size( )

    def getNumActive(self):
        """The number of active cells in the grid."""
        return self._get_active( )


    def getNumActiveFracture(self):
        """The number of active cells in the grid."""
        return self._get_active_fracture( )


    def getBoundingBox2D(self , layer = 0 , lower_left = None , upper_right = None):
        if 0 <= layer <= self.getNZ():
            x = ctypes.c_double()
            y = ctypes.c_double()
            z = ctypes.c_double()

            if lower_left is None:
                i1 = 0
                j1 = 0
            else:
                i1,j1 = lower_left
                if not 0 < i1 < self.getNX():
                    raise ValueError("lower_left i coordinate invalid")

                if not 0 < j1 < self.getNY():
                    raise ValueError("lower_left j coordinate invalid")


            if upper_right is None:
                i2 = self.getNX()
                j2 = self.getNY()
            else:
                i2,j2 = upper_right

                if not 1 < i2 <= self.getNX():
                    raise ValueError("upper_right i coordinate invalid")

                if not 1 < j2 <= self.getNY():
                    raise ValueError("upper_right j coordinate invalid")

            if not i1 < i2:
                raise ValueError("Must have lower_left < upper_right")

            if not j1 < j2:
                raise ValueError("Must have lower_left < upper_right")



            self._get_corner_xyz( i1 , j1 , layer , ctypes.byref(x) , ctypes.byref(y) , ctypes.byref(z) )
            p0 = (x.value , y.value )

            self._get_corner_xyz( i2 , j1 , layer , ctypes.byref(x) , ctypes.byref(y) , ctypes.byref(z) )
            p1 = (x.value , y.value  )

            self._get_corner_xyz(  i2 , j2 , layer , ctypes.byref(x) , ctypes.byref(y) , ctypes.byref(z) )
            p2 = (x.value , y.value  )

            self._get_corner_xyz( i1 , j2 , layer , ctypes.byref(x) , ctypes.byref(y) , ctypes.byref(z) )
            p3 = (x.value , y.value  )

            return (p0,p1,p2,p3)
        else:
            raise ValueError("Invalid layer value:%d  Valid range: [0,%d]" % (layer , self.getNZ()))


    def getName(self):
        """
        Name of the current grid, returns a string.

        For the main grid this is the filename given to the
        constructor when loading the grid; for an LGR this is the name
        of the LGR. If the grid instance has been created with the
        create() classmethod this can be None.
        """
        n = self._get_name()
        return str(n) if n else ''

    def global_index( self , active_index = None, ijk = None):
        """
        Will convert either active_index or (i,j,k) to global index.
        """
        return self.__global_index( active_index = active_index , ijk = ijk )

    def __global_index( self , active_index = None , global_index = None , ijk = None):
        """
        Will convert @active_index or @ijk to global_index.

        This method will convert @active_index or @ijk to a global
        index. Exactly one of the arguments @active_index,
        @global_index or @ijk must be supplied.

        The method is used extensively internally in the EclGrid
        class; most methods which take coordinate input pass through
        this method to normalize the coordinate representation.
        """

        set_count = 0
        if not active_index is None:
            set_count += 1

        if not global_index is None:
            set_count += 1

        if ijk:
            set_count += 1

        if not set_count == 1:
            raise ValueError("Exactly one of the kewyord arguments active_index, global_index or ijk must be set")

        if not active_index is None:
            global_index = self._get_global_index1A(  active_index )
        elif ijk:
            nx = self.getNX()
            ny = self.getNY()
            nz = self.getNZ()

            i,j,k = ijk

            if not 0 <= i < nx:
                raise IndexError("Invalid value i:%d  Range: [%d,%d)" % (i , 0 , nx))

            if not 0 <= j < ny:
                raise IndexError("Invalid value j:%d  Range: [%d,%d)" % (j , 0 , ny))

            if not 0 <= k < nz:
                raise IndexError("Invalid value k:%d  Range: [%d,%d)" % (k , 0 , nz))

            global_index = self._get_global_index3( i,j,k)
        else:
            if not 0 <= global_index < self.getGlobalSize():
                raise IndexError("Invalid value global_index:%d  Range: [%d,%d)" % (global_index , 0 , self.getGlobalSize()))
        return global_index


    def get_active_index( self , ijk = None , global_index = None):
        """
        Lookup active index based on ijk or global index.

        Will determine the active_index of a cell, based on either
        @ijk = (i,j,k) or @global_index. If the cell specified by the
        input arguments is not active the function will return -1.
        """
        gi = self.__global_index( global_index = global_index , ijk = ijk)
        return self._get_active_index1( gi)


    def get_active_fracture_index( self , ijk = None , global_index = None):
        """
        For dual porosity - get the active fracture index.
        """
        gi = self.__global_index( global_index = global_index , ijk = ijk)
        return self._get_active_fracture_index1( gi )


    def get_global_index1F( self , active_fracture_index):
        """
        Will return the global index corresponding to active fracture index.
        """
        return self._get_global_index1F( active_fracture_index )


    def cell_invalid( self , ijk = None , global_index = None , active_index = None):
        """
        Tries to check if a cell is invalid.

        Cells which are used to represent numerical aquifers are
        typically located in UTM position (0,0); these cells have
        completely whacked up shape and size, and should **NOT** be
        used in calculations involving real world coordinates. To
        protect against this a heuristic is used identify such cells
        and mark them as invalid. There might be other sources than
        numerical aquifers to this problem.
        """
        gi = self.__global_index( global_index = global_index , ijk = ijk , active_index = active_index)
        return self._invalid_cell( gi )


    def validCellGeometry(self, ijk = None , global_index = None , active_index = None):
        """Checks if the cell has valid geometry.

        There are at least two reasons why a cell might have invalid
        gemetry:

          1. In the case of GRID files it is not necessary to supply
             the geometry for all the cells; in that case this
             function will return false for cells which do not have
             valid coordinates.

          2. Cells which are used to represent numerical aquifers are
             typically located in UTM position (0,0); these cells have
             completely whacked up shape and size; these cells are
             identified by a heuristic - which might fail

        If the validCellGeometry( ) returns false for a particular
        cell functions which calculate cell volumes, real world
        coordinates and so on - should not be used.
        """
        gi = self.__global_index( global_index = global_index , ijk = ijk , active_index = active_index)
        return self._valid_cell( gi )



    def active( self , ijk = None , global_index = None):
        """
        Is the cell active?

        See documentation og get_xyz() for explanation of parameters
        @ijk and @global_index.
        """
        gi = self.__global_index( global_index = global_index , ijk = ijk)
        active_index = self._get_active_index1( gi)
        if active_index >= 0:
            return True
        else:
            return False


    def get_global_index( self , ijk = None , active_index = None):
        """
        Lookup global index based on ijk or active index.
        """
        gi = self.__global_index( active_index = active_index , ijk = ijk)
        return gi


    def get_ijk( self, active_index = None , global_index = None):
        """
        Lookup (i,j,k) for a cell, based on either active index or global index.

        The return value is a tuple with three elements (i,j,k).
        """
        i = ctypes.c_int()
        j = ctypes.c_int()
        k = ctypes.c_int()

        gi = self.__global_index( active_index = active_index , global_index = global_index)
        self._get_ijk1( gi , ctypes.byref(i) , ctypes.byref(j) , ctypes.byref(k))

        return (i.value , j.value , k.value)


    def get_xyz( self, active_index = None , global_index = None , ijk = None):
        """
        Find true position of cell center.

        Will return world position of the center of a cell in the
        grid. The return value is a tuple of three elements:
        (utm_x , utm_y , depth).

        The cells of a grid can be specified in three different ways:

           (i,j,k)      : As a tuple of i,j,k values.

           global_index : A number in the range [0,nx*ny*nz). The
                          global index is related to (i,j,k) as:

                            global_index = i + j*nx + k*nx*ny

           active_index : A number in the range [0,nactive).

        For many of the EclGrid methods a cell can be specified using
        any of these three methods. Observe that one and only method is
        allowed:

        OK:
            pos1 = grid.get_xyz( active_index = 100 )
            pos2 = grid.get_xyz( ijk = (10,20,7 ))

        Crash and burn:
            pos3 = grid.get_xyz( ijk = (10,20,7 ) , global_index = 10)
            pos4 = grid.get_xyz()

        All the indices in the EclGrid() class are zero offset, this
        is in contrast to ECLIPSE which has an offset 1 interface.
        """
        gi = self.__global_index( ijk = ijk , active_index = active_index , global_index = global_index)

        x = ctypes.c_double()
        y = ctypes.c_double()
        z = ctypes.c_double()
        self._get_xyz1( gi , ctypes.byref(x) , ctypes.byref(y) , ctypes.byref(z))
        return (x.value , y.value , z.value)


    def getNodePos(self , i , j , k):
        """Will return the (x,y,z) for the node given by (i,j,k).

        Observe that this method does not consider cells, but the
        nodes in the grid. This means that the valid input range for
        i,j and k are are upper end inclusive. To get the four
        bounding points of the lower layer of the grid:

           p0 = grid.getNodePos(0 , 0 , 0)
           p1 = grid.getNodePos(grid.getNX() , 0 , 0)
           p2 = grid.getNodePos(0 , grid.getNY() , 0)
           p3 = grid.getNodePos(grid.getNX() , grid.getNY() , 0)

        """
        if not 0 <= i <= self.getNX():
            raise IndexError("Invalid I value:%d - valid range: [0,%d]" % (i , self.getNX()))

        if not 0 <= j <= self.getNY():
            raise IndexError("Invalid J value:%d - valid range: [0,%d]" % (j , self.getNY()))

        if not 0 <= k <= self.getNZ():
            raise IndexError("Invalid K value:%d - valid range: [0,%d]" % (k , self.getNZ()))

        x = ctypes.c_double()
        y = ctypes.c_double()
        z = ctypes.c_double()
        self._get_corner_xyz( i,j,k , ctypes.byref(x) , ctypes.byref(y) , ctypes.byref(z))
        return (x.value , y.value , z.value)


    def getCellCorner(self , corner_nr , active_index = None , global_index = None , ijk = None):
        """
        Will look up xyz of corner nr @corner_nr


        lower layer:   upper layer

         2---3           6---7
         |   |           |   |
         0---1           4---5

        """
        gi = self.__global_index( ijk = ijk , active_index = active_index , global_index = global_index)
        x = ctypes.c_double()
        y = ctypes.c_double()
        z = ctypes.c_double()
        self._get_cell_corner_xyz1( gi , corner_nr , ctypes.byref(x) , ctypes.byref(y) , ctypes.byref(z))
        return (x.value , y.value , z.value)

    def getNodeXYZ(self , i,j,k):
        """
        This function returns the position of Vertex (i,j,k).

        The coordinates are in the inclusive interval [0,nx] x [0,ny] x [0,nz].
        """
        nx = self.getNX()
        ny = self.getNY()
        nz = self.getNZ()

        corner = 0

        if i == nx:
            i -= 1
            corner += 1

        if j == ny:
            j -= 1
            corner += 2

        if k == nz:
            k -= 1
            corner += 4

        if self._ijk_valid( i , j , k):
            return self.getCellCorner( corner , global_index = i + j*nx + k*nx*ny )
        else:
            raise IndexError("Invalid coordinates: (%d,%d,%d) " % (i,j,k))



    def getLayerXYZ(self , xy_corner , layer):
        nx = self.getNX()

        (j , i) = divmod(xy_corner , nx + 1)
        k = layer
        return self.getNodeXYZ(i,j,k)



    def distance( self , global_index1 , global_index2):
        dx = ctypes.c_double()
        dy = ctypes.c_double()
        dz = ctypes.c_double()
        self._get_distance( global_index1 , global_index2 , ctypes.byref(dx) , ctypes.byref(dy) , ctypes.byref(dz))
        return (dx.value , dy.value , dz.value)


    def depth( self , active_index = None , global_index = None , ijk = None):
        """
        Depth of the center of a cell.

        Returns the depth of the center of the cell given by
        @active_index, @global_index or @ijk. See method get_xyz() for
        documentation of @active_index, @global_index and @ijk.
        """
        gi = self.__global_index( ijk = ijk , active_index = active_index , global_index = global_index)
        return self._get_depth(  gi )

    def top( self , i , j ):
        """
        Top of the reservoir; in the column (@i , @j).
        Returns average depth of the four top corners.
        """
        return self._get_top( i , j )

    def top_active( self, i, j ):
        """
        Top of the active part of the reservoir; in the column (@i , @j).
        Raises ValueError if (i,j) column is inactive.
        """
        for k in range(self.getNZ()):
            a_idx = self.get_active_index(ijk=(i,j,k))
            if a_idx >= 0:
                return self._get_top1A(a_idx)
        raise ValueError('No active cell in column (%d,%d)' % (i,j))

    def bottom( self , i , j ):
        """
        Bottom of the reservoir; in the column (@i , @j).
        """
        return self._get_bottom(  i , j )

    def locate_depth( self , depth , i , j ):
        """
        Will locate the k value of cell containing specified depth.

        Will scan through the grid column specified by the input
        arguments @i and @j and search for a cell containing the depth
        given by input argument @depth. The return value is the k
        value of cell containing @depth.

        If @depth is above the top of the reservoir the function will
        return -1, and if @depth is below the bottom of the reservoir
        the function will return -nz.
        """
        return self._locate_depth(  depth , i , j)


    def find_cell( self , x , y , z , start_ijk = None):
        """
        Lookup cell containg true position (x,y,z).

        Will locate the cell in the grid which contains the true
        position (@x,@y,@z), the return value is as a triplet
        (i,j,k). The underlying C implementation is not veeery
        efficient, and can potentially take quite long time. If you
        provide a good intial guess with the parameter @start_ijk (a
        tuple (i,j,k)) things can speed up quite substantially.

        If the location (@x,@y,@z) can not be found in the grid, the
        method will return None.
        """
        start_index = 0
        if start_ijk:
            start_index = self.__global_index( ijk = start_ijk )

        global_index = self._get_ijk_xyz( x , y , z , start_index)
        if global_index >= 0:
            i = ctypes.c_int()
            j = ctypes.c_int()
            k = ctypes.c_int()
            self._get_ijk1( global_index,
                            ctypes.byref(i), ctypes.byref(j), ctypes.byref(k) )
            return (i.value, j.value, k.value)
        return None

    def cell_contains( self , x , y , z , active_index = None , global_index = None , ijk = None):
        """
        Will check if the cell contains point given by world
        coordinates (x,y,z).

        See method get_xyz() for documentation of @active_index,
        @global_index and @ijk.
        """
        gi = self.__global_index( ijk = ijk , active_index = active_index , global_index = global_index)
        return self._cell_contains( gi , x,y,z)


    def findCellXY(self , x, y , k):
        """Will find the i,j of cell with utm coordinates x,y.

        The @k input is the layer you are interested in, the allowed
        values for k are [0,nz]. If the coordinates (x,y) are found to
        be outside the grid a ValueError exception is raised.
        """
        if 0 <= k <= self.getNZ():
            i = ctypes.c_int()
            j = ctypes.c_int()
            ok = self._get_ij_xy( x,y,k , ctypes.byref(i) , ctypes.byref(j))
            if ok:
                return (i.value , j.value)
            else:
                raise ValueError("Could not find the point:(%g,%g) in layer:%d" % (x,y,k))
        else:
            raise IndexError("Invalid layer value:%d" % k)


    @staticmethod
    def d_cmp(a,b):
        return cmp(a[0] , b[0])


    def findCellCornerXY(self , x, y , k):
        """Will find the corner nr of corner closest to utm coordinates x,y.

        The @k input is the layer you are interested in, the allowed
        values for k are [0,nz]. If the coordinates (x,y) are found to
        be outside the grid a ValueError exception is raised.
        """
        i,j = self.findCellXY(x,y,k)
        if k == self.getNZ():
            k -= 1
            corner_shift = 4
        else:
            corner_shift = 0

        nx = self.getNX()
        x0,y0,z0 = self.getCellCorner( corner_shift , ijk = (i,j,k))
        d0 = math.sqrt( (x0 - x)*(x0 - x) + (y0 - y)*(y0 - y))
        c0 = i + j*(nx + 1)

        x1,y1,z1 = self.getCellCorner( 1 + corner_shift , ijk = (i,j,k))
        d1 = math.sqrt( (x1 - x)*(x1 - x) + (y1 - y)*(y1 - y))
        c1 = i + 1 + j*(nx + 1)

        x2,y2,z2 = self.getCellCorner( 2 + corner_shift , ijk = (i,j,k))
        d2 = math.sqrt( (x2 - x)*(x2 - x) + (y2 - y)*(y2 - y))
        c2 = i + (j + 1)*(nx + 1)

        x3,y3,z3 = self.getCellCorner( 3 + corner_shift , ijk = (i,j,k))
        d3 = math.sqrt( (x3 - x)*(x3 - x) + (y3 - y)*(y3 - y))
        c3 = i + 1 + (j + 1)*(nx + 1)

        l = [(d0 , c0) , (d1,c1) , (d2 , c2) , (d3,c3)]
        l.sort( EclGrid.d_cmp )
        return l[0][1]



    def cell_regular(self, active_index = None , global_index = None , ijk = None):
        """
        The ECLIPSE grid models often contain various degenerate cells,
        which are twisted, have overlapping corners or what not. This
        function gives a moderate sanity check on a cell, essentially
        what the function does is to check if the cell contains it's
        own centerpoint - which is actually not as trivial as it
        sounds.
        """
        gi = self.__global_index( ijk = ijk , active_index = active_index , global_index = global_index)
        return self._cell_regular(  gi )


    def cell_volume( self, active_index = None , global_index = None , ijk = None):
        """
        Calculate the volume of a cell.

        Will calculate the total volume of the cell. See method
        get_xyz() for documentation of @active_index, @global_index
        and @ijk.
        """
        gi = self.__global_index( ijk = ijk , active_index = active_index , global_index = global_index)
        return self._get_cell_volume( gi)


    def cell_dz( self , active_index = None , global_index = None , ijk = None):
        """
        The thickness of a cell.

        Will calculate the (average) thickness of the cell. See method
        get_xyz() for documentation of @active_index, @global_index
        and @ijk.
        """
        gi = self.__global_index( ijk = ijk , active_index = active_index , global_index = global_index )
        return self._get_cell_thickness(  gi )


    def getCellDims(self , active_index = None , global_index = None , ijk = None):
        """Will return a tuple (dx,dy,dz) for cell dimension.

        The dx and dy values are best effor estimates of the cell size
        along the i and j directions respectively. The three values
        are guaranteed to satisfy:

              dx * dy * dz = dV

        See method get_xyz() for documentation of @active_index,
        @global_index and @ijk.

        """
        gi = self.__global_index( ijk = ijk , active_index = active_index , global_index = global_index )
        dx = self._get_cell_dx( gi )
        dy = self._get_cell_dy( gi )
        dz = self._get_cell_thickness(  gi )
        return (dx,dy,dz)



    def getNumLGR(self):

        """
        How many LGRs are attached to this main grid?

        How many LGRs are attached to this main grid; the grid
        instance doing the query must itself be a main grid.
        """
        return self._num_lgr(  )



    def has_lgr( self , lgr_name ):
        """
        Query if the grid has an LGR with name @lgr_name.
        """
        if self._has_lgr( lgr_name ):
            return True
        else:
            return False


    def get_lgr( self , lgr_name ):
        """
        Get EclGrid instance with LGR content.

        Return an EclGrid instance based on the LGR named
        @lgr_name. The LGR grid instance is in most questions like an
        ordinary grid instance; the only difference is that it can not
        be used for further queries about LGRs.

        If the grid does not contain an LGR with this name the method
        will return None.
        """
        if self._has_lgr( lgr_name ):
            lgr = self._get_lgr( name )
            lgr.setParent( self )
            return lgr
        else:
            raise KeyError("No such LGR:%s" % lgr_name)


    def get_cell_lgr( self, active_index = None , global_index = None , ijk = None):
        """
        Get EclGrid instance located in cell.

        Will query the current grid instance if the cell given by
        @active_index, @global_index or @ijk has been refined with an
        LGR. Will return None if the cell in question has not been
        refined, the return value can be used for further queries.

        See get_xyz() for documentation of the input parameters.
        """
        gi  = self.__global_index( ijk = ijk , active_index = active_index , global_index = global_index)
        lgr = self._get_cell_lgr( gi )
        if lgr:
            lgr.setParent( self )
            return lgr
        else:
            raise IndexError("No LGR defined for this cell")


    def grid_value( self , kw , i , j , k):
        """
        Will evalute @kw in location (@i,@j,@k).

        The ECLIPSE properties and solution vectors are stored in
        restart and init files as 1D vectors of length nx*nx*nz or
        nactive. The grid_value() method is a minor convenience
        function to convert the (@i,@j,@k) input values to an
        appropriate 1D index.

        Depending on the length of kw the input arguments are
        converted either to an active index or to a global index. If
        the length of kw does not fit with either the global size of
        the grid or the active size of the grid things will fail hard.
        """
        return self._grid_value( kw , i , j , k)


    def load_column( self , kw , i , j , column):
        """
        Load the values of @kw from the column specified by (@i,@j).

        The method will scan through all k values of the input field
        @kw for fixed values of i and j. The size of @kw must be
        either nactive or nx*ny*nz.

        The input argument @column should be a DoubleVector instance,
        observe that if size of @kw == nactive k values corresponding
        to inactive cells will not be modified in the @column
        instance; in that case it is important that @column is
        initialized with a suitable default value.
        """
        self._load_column(  kw , i , j , column)


    def createKW( self , array , kw_name , pack):
        """
        Creates an EclKW instance based on existing 3D numpy object.

        The method create3D() does the inverse operation; creating a
        3D numpy object from an EclKW instance. If the argument @pack
        is true the resulting keyword will have length 'nactive',
        otherwise the element will have length nx*ny*nz.
        """
        if array.ndim == 3:
            dims = array.shape
            if dims[0] == self.getNX() and dims[1] == self.getNY() and dims[2] == self.getNZ():
                dtype = array.dtype
                if dtype == numpy.int32:
                    type = EclDataType.ECL_INT
                elif dtype == numpy.float32:
                    type = EclDataType.ECL_FLOAT
                elif dtype == numpy.float64:
                    type = EclDataType.ECL_DOUBLE
                else:
                    sys.exit("Do not know how to create ecl_kw from type:%s" % dtype)

                if pack:
                    size = self.getNumActive()
                else:
                    size = self.getGlobalSize()

                if len(kw_name) > 8:
                    # Silently truncate to length 8 - ECLIPSE has it's challenges.
                    kw_name = kw_name[0:8]

                kw = EclKW( kw_name , size , type )
                active_index = 0
                global_index = 0
                for k in range( self.getNZ() ):
                    for j in range( self.getNY() ):
                        for i in range( self.getNX() ):
                            if pack:
                                if self.active( global_index = global_index ):
                                    kw[active_index] = array[i,j,k]
                                    active_index += 1
                            else:
                                if dtype == numpy.int32:
                                    kw[global_index] = int( array[i,j,k] )
                                else:
                                    kw[global_index] = array[i,j,k]

                            global_index += 1
                return kw
        raise ValueError("Wrong size / dimension on array")


    def coarse_groups(self):
        """
        Will return the number of coarse groups in this grid.
        """
        return self._num_coarse_groups(  )


    def in_coarse_group(self , global_index = None , ijk = None , active_index = None):
        """
        Will return True or False if the cell is part of coarse group.
        """
        global_index = self.__global_index( active_index = active_index , ijk = ijk , global_index = global_index)
        return self._in_coarse_group1( global_index )


    def create3D( self , ecl_kw , default = 0):
        """
        Creates a 3D numpy array object with the data from  @ecl_kw.

        Observe that 3D numpy object is a copy of the data in the
        EclKW instance, i.e. modification to the numpy object will not
        be reflected in the ECLIPSE keyword.

        The methods createKW() does the inverse operation; creating an
        EclKW instance from a 3D numpy object.

        Alternative: Creating the numpy array object is not very
        efficient; if you only need a limited number of elements from
        the ecl_kw instance it might be wiser to use the grid_value()
        method:

           value = grid.grid_value( ecl_kw , i , j , k )

        """
        if len(ecl_kw) == self.getNumActive() or len(ecl_kw) == self.getGlobalSize():
            array = numpy.ones( [ self.getGlobalSize() ] , dtype = ecl_kw.dtype) * default
            kwa = ecl_kw.array
            if len(ecl_kw) == self.getGlobalSize():
                for i in range(kwa.size):
                    array[i] = kwa[i]
            else:
                data_index = 0
                for global_index in range(self.getGlobalSize()):
                    if self.active( global_index = global_index ):
                        array[global_index] = kwa[data_index]
                        data_index += 1

            array = array.reshape( [self.getNX() , self.getNY() , self.getNZ()] , order = 'F')
            return array
        else:
            err_msg_fmt = 'Keyword "%s" has invalid size %d; must be either nactive=%d or nx*ny*nz=%d'
            err_msg = err_msg_fmt % (ecl_kw, len(ecl_kw), self.getNumActive(),
                                     self.getGlobalSize())
            raise ValueError(err_msg)

    def save_grdecl(self , pyfile, output_unit = EclUnitTypeEnum.ECL_METRIC_UNITS):
        """
        Will write the the grid content as grdecl formatted keywords.

        Will only write the main grid.
        """
        cfile = CFILE( pyfile )
        self._fprintf_grdecl2( cfile , output_unit)

    def save_EGRID( self , filename , output_unit = EclUnitTypeEnum.ECL_METRIC_UNITS):
        """
        Will save the current grid as a EGRID file.
        """
        self._fwrite_EGRID2( filename, output_unit )

    def save_GRID( self , filename , output_unit = EclUnitTypeEnum.ECL_METRIC_UNITS):
        """
        Will save the current grid as a EGRID file.
        """
        self._fwrite_GRID2(  filename, output_unit )


    def write_grdecl( self , ecl_kw , pyfile , special_header = None , default_value = 0):
        """
        Writes an EclKW instance as an ECLIPSE grdecl formatted file.

        The input argument @ecl_kw must be an EclKW instance of size
        nactive or nx*ny*nz. If the size is nactive the inactive cells
        will be filled with @default_value; hence the function will
        always write nx*ny*nz elements.

        The data in the @ecl_kw argument can be of type integer,
        float, double or bool. In the case of bool the default value
        must be specified as 1 (True) or 0 (False).

        The input argument @pyfile should be a valid python filehandle
        opened for writing; i.e.

           pyfile = open("PORO.GRDECL" , "w")
           grid.write_grdecl( poro_kw  , pyfile , default_value = 0.0)
           grid.write_grdecl( permx_kw , pyfile , default_value = 0.0)
           pyfile.close()

        """

        if len(ecl_kw) == self.getNumActive() or len(ecl_kw) == self.getGlobalSize():
            cfile = CFILE( pyfile )
            self._fwrite_grdecl( ecl_kw , special_header , cfile , default_value )
        else:
            raise ValueError("Keyword: %s has invalid size(%d), must be either nactive:%d  or nx*ny*nz:%d" % (ecl_kw.getName() , len(ecl_kw) , self.getNumActive() , self.getGlobalSize()))


    def exportACTNUM(self):
        actnum = IntVector( initial_size = self.getGlobalSize() )
        self._init_actnum( actnum.getDataPtr() )
        return actnum


    def compressedKWCopy(self, kw):
        if len(kw) == self.getNumActive():
            return kw.copy( )
        elif len(kw) == self.getGlobalSize():
            kw_copy = EclKW( kw.getName() , self.getNumActive() , kw.data_type)
            self._compressed_kw_copy( kw_copy , kw)
            return kw_copy
        else:
            raise ValueError("The input keyword must have nx*n*nz or nactive elements. Size:%d invalid" % len(kw))

    def globalKWCopy(self, kw , default_value):
        if len(kw) == self.getGlobalSize( ):
            return kw.copy( )
        elif len(kw) == self.getNumActive():
            kw_copy = EclKW( kw.getName() , self.getGlobalSize() , kw.data_type)
            kw_copy.assign( default_value )
            self._global_kw_copy( kw_copy , kw)
            return kw_copy
        else:
            raise ValueError("The input keyword must have nx*n*nz or nactive elements. Size:%d invalid" % len(kw))


    def exportACTNUMKw(self):
        actnum = EclKW("ACTNUM" , self.getGlobalSize() , EclDataType.ECL_INT)
        self._init_actnum( actnum.getDataPtr() )
        return actnum


    def createVolumeKeyword(self , active_size = True):
        """Will create a EclKW initialized with cell volumes.

        The purpose of this method is to create a EclKW instance which
        is initialized with all the cell volumes, this can then be
        used to perform volume summation; i.e. to calculate the total
        oil volume:

           soil = 1 - sgas - swat
           cell_volume = grid.createVolumeKeyword()
           tmp = cell_volume * soil
           oip = tmp.sum( )

        The oil in place calculation shown above could easily be
        implemented by iterating over the soil kw, however using the
        volume keyword has two advantages:

          1. The calculation of cell volumes is quite time consuming,
             by storing the results in a kw they can be reused.

          2. By using the compact form 'oip = cell_volume * soil' the
             inner loop iteration will go in C - which is faster.

        By default the kw will only have values for the active cells,
        but by setting the optional variable @active_size to False you
        will get volume values for all cells in the grid.
        """

        return self._create_volume_keyword( active_size )

    def export_coord(self):
        return self._export_coord()

    def export_zcorn(self):
        return self._export_zcorn()

    def export_actnum(self):
        return self._export_actnum()

    def export_mapaxes(self):
        if not self._use_mapaxes():
            return None

        return self._export_mapaxes()
Ejemplo n.º 27
0
class EclFile(BaseCClass):
    TYPE_NAME = "ecl_file"
    _open = EclPrototype("void*       ecl_file_open( char* , int )",
                         bind=False)
    _get_file_type = EclPrototype(
        "ecl_file_enum ecl_util_get_file_type( char* , bool* , int*)",
        bind=False)
    _writable = EclPrototype("bool        ecl_file_writable( ecl_file )")
    _save_kw = EclPrototype(
        "void        ecl_file_save_kw( ecl_file , ecl_kw )")
    _close = EclPrototype("void        ecl_file_close( ecl_file )")
    _iget_restart_time = EclPrototype(
        "time_t      ecl_file_iget_restart_sim_date( ecl_file , int )")
    _iget_restart_days = EclPrototype(
        "double      ecl_file_iget_restart_sim_days( ecl_file , int )")
    _get_restart_index = EclPrototype(
        "int         ecl_file_get_restart_index( ecl_file , time_t)")
    _get_src_file = EclPrototype(
        "char*       ecl_file_get_src_file( ecl_file )")
    _replace_kw = EclPrototype(
        "void        ecl_file_replace_kw( ecl_file , ecl_kw , ecl_kw , bool)")
    _fwrite = EclPrototype(
        "void        ecl_file_fwrite_fortio( ecl_file , fortio , int)")
    _has_report_step = EclPrototype(
        "bool        ecl_file_has_report_step( ecl_file , int)")
    _has_sim_time = EclPrototype(
        "bool        ecl_file_has_sim_time( ecl_file , time_t )")
    _get_global_view = EclPrototype(
        "ecl_file_view_ref ecl_file_get_global_view( ecl_file )")

    @staticmethod
    def get_filetype(filename):
        fmt_file = ctypes.c_bool()
        report_step = ctypes.c_int()

        file_type = EclFile._get_file_type(filename, ctypes.byref(fmt_file),
                                           ctypes.byref(report_step))
        if file_type in [
                EclFileEnum.ECL_RESTART_FILE, EclFileEnum.ECL_SUMMARY_FILE
        ]:
            report_step = report_step.value
        else:
            report_step = None

        if file_type in [
                EclFileEnum.ECL_OTHER_FILE, EclFileEnum.ECL_DATA_FILE
        ]:
            fmt_file = None
        else:
            fmt_file = fmt_file.value

        return (file_type, report_step, fmt_file)

    @classmethod
    def restart_block(cls, filename, dtime=None, report_step=None):
        raise NotImplementedError(
            "The restart_block implementation has been removed - open file normally and use EclFileView."
        )

    @classmethod
    def contains_report_step(cls, filename, report_step):
        """
        Will check if the @filename contains @report_step.

        This classmethod works by opening the file @filename and
        searching through linearly to see if an ecl_kw with value
        corresponding to @report_step can be found. Since this is a
        classmethod it is invoked like this:

           import ecl.ecl.ecl as ecl
           ....
           if ecl.EclFile.contains_report_step("ECLIPSE.UNRST" , 20):
              print "OK - file contains report step 20"
           else:
              print "File does not contain report step 20"

        If you have already loaded the file into an EclFile instance
        you should use the has_report_step() method instead.
        """
        obj = EclFile(filename)
        return obj.has_report_step(report_step)

    @classmethod
    def contains_sim_time(cls, filename, dtime):
        """
        Will check if the @filename contains simulation at @dtime.

        This classmethod works by opening the file @filename and
        searching through linearly to see if a result block at the
        time corresponding to @dtime can be found. Since this is a
        classmethod it is invoked like this:

           import ecl.ecl.ecl as ecl
           ....
           if ecl.EclFile.contains_sim_time("ECLIPSE.UNRST" , datetime.datetime( 2007 , 10 , 10) ):
              print "OK - file contains 10th of October 2007"
           else:
              print "File does not contain 10th of October 2007"

        If you have already loaded the file into an EclFile instance
        you should use the has_sim_time() method instead.
        """
        obj = EclFile(filename)
        return obj.has_sim_time(dtime)

    @property
    def report_list(self):
        report_steps = []
        try:
            seqnum_list = self["SEQNUM"]
            for s in seqnum_list:
                report_steps.append(s[0])
        except KeyError:
            # OK - we did not have seqnum; that might be because this
            # a non-unified restart file; or because this is not a
            # restart file at all.
            fname = self.getFilename()
            matchObj = re.search("\.[XF](\d{4})$", fname)
            if matchObj:
                report_steps.append(int(matchObj.group(1)))
            else:
                raise TypeError(
                    'Tried get list of report steps from file "%s" - which is not a restart file'
                    % fname)

        return report_steps

    @classmethod
    def file_report_list(cls, filename):
        """
        Will identify the available report_steps from @filename.
        """

        file = EclFile(filename)
        return file.report_list

    def __repr__(self):
        fn = self.getFilename()
        wr = ', read/write' if self._writable() else ''
        return self._create_repr('"%s"%s' % (fn, wr))

    def __init__(self, filename, flags=0):
        """
        Loads the complete file @filename.

        Will create a new EclFile instance with the content of file
        @filename. The file @filename must be in 'restart format' -
        otherwise it will be crash and burn.

        The optional argument flags can be an or'ed combination of the
        flags:

           ecl.ECL_FILE_WRITABLE : It is possible to update the
              content of the keywords in the file.

           ecl.ECL_FILE_CLOSE_STREAM : The underlying FILE * is closed
              when not used; to save number of open file descriptors
              in cases where a high number of EclFile instances are
              open concurrently.

        When the file has been loaded the EclFile instance can be used
        to query for and get reference to the EclKW instances
        constituting the file, like e.g. SWAT from a restart file or
        FIPNUM from an INIT file.
        """
        c_ptr = self._open(filename, flags)
        if c_ptr is None:
            raise IOError('Failed to open file "%s"' % filename)
        else:
            super(EclFile, self).__init__(c_ptr)
            self.global_view = self._get_global_view()
            self.global_view.setParent(self)

    def save_kw(self, kw):
        """
        Will write the @kw back to file.

        This function should typically be used in situations like this:

          1. Create an EclFile instance around an ECLIPSE output file.
          2. Extract a keyword of interest and modify it.
          3. Call this method to save the modifications to disk.

        There are several restrictions to the use of this function:

          1. The EclFile instance must have been created with the
             optional read_only flag set to False.

          2. You can only modify the content of the keyword; if you
             try to modify the header in any way (i.e. size, datatype
             or name) the function will fail.

          3. The keyword you are trying to save must be exactly the
             keyword you got from this EclFile instance, otherwise the
             function will fail.
        """
        if self._writable():
            self._save_kw(kw)
        else:
            raise IOError('save_kw: the file "%s" has been opened read only.' %
                          self.getFilename())

    def __len__(self):
        return len(self.global_view)

    def close(self):
        if self:
            self._close()
            self._invalidateCPointer()

    def free(self):
        self.close()

    def block_view(self, kw, kw_index):
        if not kw in self:
            raise KeyError('No such keyword "%s".' % kw)
        ls = self.global_view.numKeywords(kw)
        idx = kw_index
        if idx < 0:
            idx += ls
        if 0 <= idx < ls:
            return self.global_view.blockView(kw, idx)
        raise IndexError('Index out of range, must be in [0, %d), was %d.' %
                         (ls, kw_index))

    def block_view2(self, start_kw, stop_kw, start_index):
        return self.global_view.blockView2(start_kw, stop_kw, start_index)

    def restart_view(self,
                     seqnum_index=None,
                     report_step=None,
                     sim_time=None,
                     sim_days=None):
        return self.global_view.restartView(seqnum_index, report_step,
                                            sim_time, sim_days)

    def select_block(self, kw, kw_index):
        raise NotImplementedError(
            "The select_block implementation has been removed - use EclFileView"
        )

    def select_global(self):
        raise NotImplementedError(
            "The select_global implementation has been removed - use EclFileView"
        )

    def select_restart_section(self,
                               index=None,
                               report_step=None,
                               sim_time=None):
        raise NotImplementedError(
            "The select_restart_section implementation has been removed - use EclFileView"
        )
        """
        Will select a restart section as the active section.

        You must specify a report step with the @report_step argument,
        a true time with the @sim_time argument or a plain index to
        select restart block. If none of arguments are given exception
        TypeError will be raised. If present the @sim_time argument
        should be a datetime instance.

        If the restart section you ask for can not be found the method
        will raise a ValueError exeception. To protect against this
        you can query first with the has_report_step(),
        has_sim_time() or num_report_steps() methods.

        This method should be used when you have already loaded the
        complete file; if you only want to load a section from the
        file you can use the classmethod restart_block().

        The method will return 'self' which can be used to aid
        readability.
        """

    def select_last_restart(self):
        raise NotImplementedError(
            "The select_restart_section implementation has been removed - use EclFileView"
        )
        """
        Will select the last SEQNUM block in restart file.

        Works by searching for the last SEQNUM keyword; the SEQNUM
        Keywords are only present in unified restart files. If this
        is a non-unified restart file (or not a restart file at all),
        the method will do nothing and return False.
        """

    def __getitem__(self, index):
        """
        Implements [] operator; index can be integer or key.

        Will look up EclKW instances from the current EclFile
        instance. The @index argument can either be an integer, in
        which case the method will return EclKW number @index, or
        alternatively a keyword string, in which case the method will
        return a list of EclKW instances with that keyword:

           restart_file = ecl_file.EclFile("ECLIPSE.UNRST")
           kw9 = restart_file[9]
           swat_list = restart_file["SWAT"]

        The keyword based lookup can be combined with an extra [] to
        get EclKW instance nr:

           swat9 = restart_file["SWAT"][9]

        Will return the 10'th SWAT keyword from the restart file. The
        following example will iterate over all the SWAT keywords in a
        restart file:

           restart_file = ecl_file.EclFile("ECLIPSE.UNRST")
           for swat in restart_file["SWAT"]:
               ....
        """
        if isinstance(index, int):
            ls = len(self)
            idx = index
            if idx < 0:
                idx += ls
            if 0 <= idx < ls:
                return self.global_view[idx]
            else:
                raise IndexError('Index must be in [0, %d), was: %d.' %
                                 (ls, index))
        return self.global_view[index]

    def iget_kw(self, index, copy=False):
        """
        Will return EclKW instance nr @index.

        In the files loaded with the EclFile implementation the
        ECLIPSE keywords come sequentially in a long series, an INIT
        file might have the following keywords:

          INTEHEAD
          LOGIHEAD
          DOUBHEAD
          PORV
          DX
          DY
          DZ
          PERMX
          PERMY
          PERMZ
          MULTX
          MULTY
          .....

        The iget_kw() method will give you a EclKW reference to
        keyword nr @index. This functionality is also available
        through the index operator []:

           file = EclFile( "ECLIPSE.INIT" )
           permx = file.iget_kw( 7 )
           permz = file[ 9 ]

        Observe that the returned EclKW instance is only a reference
        to the data owned by the EclFile instance.

        The method iget_named_kw() which lets you specify the name of
        the keyword you are interested in is in general more useful
        than this method.
        """
        kw = self[index]
        if copy:
            return EclKW.copy(kw)
        else:
            return kw

    def iget_named_kw(self, kw_name, index, copy=False):
        return self.global_view.iget_named_kw(kw_name, index)

    def restart_get_kw(self, kw_name, dtime, copy=False):
        """Will return EclKW @kw_name from restart file at time @dtime.

        This function assumes that the current EclFile instance
        represents a restart file. It will then look for keyword
        @kw_name exactly at the time @dtime; @dtime is a datetime
        instance:

            file = EclFile( "ECLIPSE.UNRST" )
            swat2010 = file.restart_get_kw( "SWAT" , datetime.datetime( 2000 , 1 , 1 ))

        By default the returned kw instance is a reference to the
        ecl_kw still contained in the EclFile instance; i.e. the kw
        will become a dangling reference if the EclFile instance goes
        out of scope. If the optional argument @copy is True the
        returned kw will be a true copy.

        If the file does not have the keyword at the specified time
        the function will raise IndexError(); if the file does not
        have the keyword at all - KeyError will be raised.
        """
        index = self._get_restart_index(CTime(dtime))
        if index >= 0:
            if self.num_named_kw(kw_name) > index:
                kw = self.iget_named_kw(kw_name, index)
                if copy:
                    return EclKW.copy(kw)
                else:
                    return kw
            else:
                if self.has_kw(kw_name):
                    raise IndexError('Does not have keyword "%s" at time:%s.' %
                                     (kw_name, dtime))
                else:
                    raise KeyError('Keyword "%s" not recognized.' % kw_name)
        else:
            raise IndexError('Does not have keyword "%s" at time:%s.' %
                             (kw_name, dtime))

    def replace_kw(self, old_kw, new_kw):
        """
        Will replace @old_kw with @new_kw in current EclFile instance.

        This method can be used to replace one of the EclKW instances
        in the current EclFile. The @old_kw reference must be to the
        actual EclKW instance in the current EclFile instance (the
        final comparison is based on C pointer equality!), i.e. it
        must be a reference (not a copy) from one of the ??get_kw??
        methods of the EclFile class. In the example below we replace
        the SWAT keyword from a restart file:

           swat = file.iget_named_kw( "SWAT" , 0 )
           new_swat = swat * 0.25
           file.replace_kw( swat , new_swat )


        The C-level ecl_file_type structure takes full ownership of
        all installed ecl_kw instances; mixing the garbage collector
        into it means that this is quite low level - and potentially
        dangerous!
        """

        # We ensure that this scope owns the new_kw instance; the
        # new_kw will be handed over to the ecl_file instance, and we
        # can not give away something we do not alreeady own.
        if not new_kw.data_owner:
            new_kw = EclKW.copy(new_kw)

        # The ecl_file instance will take responsability for freeing
        # this ecl_kw instance.
        new_kw.data_owner = False
        self._replace_kw(old_kw, new_kw, False)

    @property
    def size(self):
        """
        The number of keywords in the current EclFile object.
        """
        return len(self)

    @property
    def unique_size(self):
        """
        The number of unique keyword (names) in the current EclFile object.
        """
        return self.global_view.uniqueSize()

    def keys(self):
        """
        Will return a list of unique kw names - like keys() on a dict.
        """
        header_dict = {}
        for index in range(len(self)):
            kw = self[index]
            header_dict[kw.getName()] = True
        return header_dict.keys()

    @property
    def headers(self):
        """
        Will return a list of the headers of all the keywords.
        """
        header_list = []
        for index in range(self.size):
            kw = self[index]
            header_list.append(kw.header)
        return header_list

    @property
    def report_steps(self):
        """
        Will return a list of all report steps.

        The method works by iterating through the whole restart file
        looking for 'SEQNUM' keywords; if the current EclFile instance
        is not a restart file it will not contain any 'SEQNUM'
        keywords and the method will simply return an empty list.
        """
        steps = []
        seqnum_list = self["SEQNUM"]
        for kw in self["SEQNUM"]:
            steps.append(kw[0])
        return steps

    @property
    def report_dates(self):
        """
        Will return a list of the dates for all report steps.

        The method works by iterating through the whole restart file
        looking for 'SEQNUM/INTEHEAD' keywords; the method can
        probably be tricked by other file types also containing an
        INTEHEAD keyword.
        """
        if self.has_kw('SEQNUM'):
            dates = []
            for index in range(self.num_named_kw('SEQNUM')):
                dates.append(self.iget_restart_sim_time(index))
            return dates
        elif 'INTEHEAD' in self:
            # This is a uber-hack; should export the ecl_rsthead
            # object as ctypes structure.
            intehead = self["INTEHEAD"][0]
            year = intehead[66]
            month = intehead[65]
            day = intehead[64]
            date = datetime.datetime(year, month, day)
            return [date]
        return None

    @property
    def dates(self):
        """
        Will return a list of the dates for all report steps.
        """
        return self.report_dates

    def num_named_kw(self, kw):
        """
        The number of keywords with name == @kw in the current EclFile object.
        """
        return self.global_view.numKeywords(kw)

    def has_kw(self, kw, num=0):
        """
        Check if current EclFile instance has a keyword @kw.

        If the optional argument @num is given it will check if the
        EclFile has at least @num occurences of @kw.
        """

        return self.num_named_kw(kw) > num

    def __contains__(self, kw):
        """
        Check if the current file contains keyword @kw.
        """
        return self.has_kw(kw)

    def has_report_step(self, report_step):
        """
        Checks if the current EclFile has report step @report_step.

        If the EclFile in question is not a restart file, you will
        just get False. If you want to check if the file contains the
        actual report_step before loading the file, you should use the
        classmethod contains_report_step() instead.
        """
        return self._has_report_step(report_step)

    def num_report_steps(self):
        """
        Returns the total number of report steps in the restart file.

        Works by counting the number of 'SEQNUM' instances, and will
        happily return 0 for a non-restart file. Observe that the
        report_steps present in a unified restart file are in general
        not consecutive, i.e. the last report step will typically be
        much higher than the return value from this function.
        """
        return len(self["SEQNUM"])

    def has_sim_time(self, dtime):
        """
        Checks if the current EclFile has data for time @dtime.

        The implementation goes through all the INTEHEAD headers in
        the EclFile, i.e. it can be fooled (and probably crash and
        burn) if the EclFile instance in question is has INTEHEAD
        keyword(s), but is still not a restart file. The @dtime
        argument should be a normal python datetime instance.
        """
        return self._has_sim_time(CTime(dtime))

    def iget_restart_sim_time(self, index):
        """
        Will locate restart block nr @index and return the true time
        as a datetime instance.
        """
        ct = CTime(self._iget_restart_time(index))
        return ct.datetime()

    def iget_restart_sim_days(self, index):
        """
        Will locate restart block nr @index and return the number of days
        (in METRIC at least ...) since the simulation started.

        """
        return self._iget_restart_days(index)

    def get_filename(self):
        """
        Name of the file currently loaded.
        """
        fn = self._get_src_file()
        return str(fn) if fn else ''

    def fwrite(self, fortio):
        """
        Will write current EclFile instance to fortio stream.

        ECLIPSE is written in Fortran; and a "special" handle for
        Fortran IO must be used when reading and writing these files.
        This method will write the current EclFile instance to a
        FortIO stream already opened for writing:

           import ecl.ecl.ecl as ecl
           ...
           fortio = ecl.FortIO( "FILE.XX" )
           file.fwrite( fortio )
           fortio.close()

        """
        self._fwrite(fortio, 0)
Ejemplo n.º 28
0
class EclRFTFile(BaseCClass):
    TYPE_NAME = "ecl_rft_file"
    _load          = EclPrototype("void* ecl_rft_file_alloc_case( char* )", bind = False)
    _iget          = EclPrototype("ecl_rft_ref ecl_rft_file_iget_node( ecl_rft_file , int )")
    _get_rft       = EclPrototype("ecl_rft_ref ecl_rft_file_get_well_time_rft( ecl_rft_file , char* , time_t)")
    _has_rft       = EclPrototype("bool ecl_rft_file_case_has_rft( char* )", bind = False)
    _free          = EclPrototype("void ecl_rft_file_free( ecl_rft_file )")
    _get_size      = EclPrototype("int ecl_rft_file_get_size__( ecl_rft_file , char* , time_t)")
    _get_num_wells = EclPrototype("int  ecl_rft_file_get_num_wells( ecl_rft_file )")


    """
    The EclRFTFile class is used to load an ECLIPSE RFT file.

    The EclRFTFile serves as a container which can load and hold the
    content of an ECLIPSE RFT file. The RFT files will in general
    contain data for several wells and several times in one large
    container. The EclRFTClass class contains methods get the the RFT
    results for a specific time and/or well.

    The EclRFTFile class can in general contain a mix of RFT and PLT
    measurements. The class does not really differentiate between
    these.
    """

    def __init__(self , case):
        c_ptr = self._load( case )
        super(EclRFTFile , self).__init__(c_ptr)


    def __len__(self):
        return self._get_size(  None , CTime(-1))


    def __getitem__(self, index):
        if isinstance(index, int):
            if 0 <= index < len(self):
                rft = self._iget(index)
                rft.setParent( self )
                return rft
            else:
                raise IndexError("Index '%d' must be in range: [0, %d]" % (index, len(self) - 1))
        else:
            raise TypeError("Index must be integer type")


    def size(self, well=None, date=None):
        """
        The number of elements in EclRFTFile container.

        By default the size() method will return the total number of
        RFTs/PLTs in the container, but by specifying the optional
        arguments date and/or well the function will only count the
        number of well measurements matching that time or well
        name. The well argument can contain wildcards.

           rftFile = ecl.EclRFTFile( "ECLIPSE.RFT" )
           print "Total number of RFTs : %d" % rftFile.size( )
           print "RFTs matching OP*    : %d" % rftFile.size( well = "OP*" )
           print "RFTs at 01/01/2010   : %d" % rftFile.size( date = datetime.date( 2010 , 1 , 1 ))

        """
        if date:
            cdate = CTime( date )
        else:
            cdate = CTime( -1 )

        return self._get_size( well , cdate)


    def get_num_wells(self):
        """
        Returns the total number of distinct wells in the RFT file.
        """
        return self._get_num_wells( )


    def get_headers(self):
        """
        Returns a list of two tuples (well_name , date) for the whole file.
        """
        header_list = []
        for i in (range(self._get_size( None , CTime(-1)))):
            rft = self.iget( i )
            header_list.append( (rft.getWellName() , rft.getDate()) )
        return header_list


    def iget(self , index):
        """
        Will lookup RFT @index - equivalent to [@index].
        """
        return self[index]


    def get(self , well_name , date ):
        """
        Will look up the RFT object corresponding to @well and @date.

        Raise Exception if not found.
        """
        if self.size( well = well_name , date = date) == 0:
            raise KeyError("No RFT for well:%s at %s" % (well_name , date))

        rft = self._get_rft( well_name , CTime( date ))
        rft.setParent( self )
        return rft

    def free(self):
        self._free( )

    def __repr__(self):
        w = len(self)
        return self._create_repr('wells = %d' % w)
Ejemplo n.º 29
0
class EclSMSPECNode(BaseCClass):
    """
    Small class with some meta information about a summary variable.

    The summary variables have different attributes, like if they
    represent a total quantity, a rate or a historical quantity. These
    quantities, in addition to the underlying values like WGNAMES,
    KEYWORD and NUMS taken from the the SMSPEC file are stored in this
    structure.
    """
    TYPE_NAME = "smspec_node"
    _node_is_total = EclPrototype("bool smspec_node_is_total( smspec_node )")
    _node_is_historical = EclPrototype(
        "bool smspec_node_is_historical( smspec_node )")
    _node_is_rate = EclPrototype("bool smspec_node_is_rate( smspec_node )")
    _node_unit = EclPrototype("char* smspec_node_get_unit( smspec_node )")
    _node_wgname = EclPrototype("char* smspec_node_get_wgname( smspec_node )")
    _node_keyword = EclPrototype(
        "char* smspec_node_get_keyword( smspec_node )")
    _node_num = EclPrototype("int   smspec_node_get_num( smspec_node )")
    _node_need_num = EclPrototype("bool  smspec_node_need_nums( smspec_node )")
    _gen_key1 = EclPrototype("char* smspec_node_get_gen_key1( smspec_node )")
    _gen_key2 = EclPrototype("char* smspec_node_get_gen_key2( smspec_node )")
    _var_type = EclPrototype(
        "ecl_sum_var_type smspec_node_get_var_type( smspec_node )")
    _cmp = EclPrototype("int smspec_node_cmp( smspec_node , smspec_node)")

    def __init__(self):
        super(EclSMSPECNode, self).__init__(0)  # null pointer
        raise NotImplementedError("Class can not be instantiated directly!")

    def cmp(self, other):
        if isinstance(other, EclSMSPECNode):
            return self._cmp(other)
        else:
            raise TypeError("Other argument must be of type EclSMSPECNode")

    def __lt__(self, other):
        return self.cmp(other) < 0

    def __gt__(self, other):
        return self.cmp(other) > 0

    def __eq__(self, other):
        return self.cmp(other) == 0

    def __hash__(self, other):
        return hash(self._gen_key1())

    @property
    def unit(self):
        """
        Returns the unit of this node as a string.
        """
        return self._node_unit()

    @property
    def wgname(self):
        """
        Returns the WGNAME property for this node.

        Many variables do not have the WGNAME property, i.e. the field
        related variables like FOPT and the block properties like
        BPR:10,10,10. For these variables the function will return
        None, and not the ECLIPSE dummy value: ":+:+:+:+".
        """
        return self._node_wgname()

    @property
    def keyword(self):
        """
        Returns the KEYWORD property for this node.

        The KEYWORD property is the main classification property in
        the ECLIPSE SMSPEC file. The properties of a variable can be
        read from the KEWYORD value; see table 3.4 in the ECLIPSE file
        format reference manual.
        """
        return self._node_keyword()

    @property
    def num(self):
        return self.getNum()

    def get_key1(self):
        """
        Returns the primary composite key, i.e. like 'WOPR:OPX' for this
        node.
        """
        return self._gen_key1()

    def get_key2(self):
        """Returns the secondary composite key for this node.

        Most variables have only one composite key, but in particular
        nodes which involve (i,j,k) coordinates will contain two
        forms:

            getKey1()  =>  "BPR:10,11,6"
            getKey2()  =>  "BPR:52423"

        Where the '52423' in getKey2() corresponds to i + j*nx +
        k*nx*ny.
        """
        return self._gen_key2()

    def var_type(self):
        return self._var_type()

    def get_num(self):
        """
        Returns the NUMS value for this keyword; or None.

        Many of the summary keywords have an integer stored in the
        vector NUMS as an attribute, i.e. the block properties have
        the global index of the cell in the nums vector. If the
        variable in question makes use of the NUMS value this property
        will return the value, otherwise it will return None:

           sum.smspec_node("FOPT").num     => None
           sum.smspec_node("BPR:1000").num => 1000

        """
        if self._node_need_num():
            return self._node_num()
        else:
            return None

    def is_rate(self):
        """
        Will check if the variable in question is a rate variable.

        The conecpt of rate variabel is important (internally) when
        interpolation values to arbitrary times.
        """
        return self._node_is_rate()

    def is_total(self):
        """
        Will check if the node corresponds to a total quantity.

        The question of whether a variable corresponds to a 'total'
        quantity or not can be interesting for e.g. interpolation
        purposes. The actual question whether a quantity is total or
        not is based on a hardcoded list in smspec_node_set_flags() in
        smspec_node.c; this list again is based on the tables 2.7 -
        2.11 in the ECLIPSE fileformat documentation.
        """
        return self._node_is_total()

    def is_historical(self):
        """
        Checks if the key corresponds to a historical variable.

        The check is only based on the last character; all variables
        ending with 'H' are considered historical.
        """
        return self._node_is_historical()
Ejemplo n.º 30
0
class FortIO(BaseCClass):
    TYPE_NAME = "fortio"

    READ_MODE = 1
    WRITE_MODE = 2
    READ_AND_WRITE_MODE = 3
    APPEND_MODE = 4

    _open_reader    = EclPrototype("void* fortio_open_reader(char*, bool, bool)", bind=False)
    _open_writer    = EclPrototype("void* fortio_open_writer(char*, bool, bool)", bind=False)
    _open_readwrite = EclPrototype("void* fortio_open_readwrite(char*, bool, bool)", bind=False)
    _open_append    = EclPrototype("void* fortio_open_append(char*, bool, bool)", bind=False)
    _guess_fortran  = EclPrototype("bool fortio_looks_like_fortran_file(char*, bool)", bind=False)

    _write_record   = EclPrototype("void fortio_fwrite_record(fortio, char*, int)")
    _get_position   = EclPrototype("long fortio_ftell(fortio)")
    _seek           = EclPrototype("void fortio_fseek(fortio, long, int)")
    _close          = EclPrototype("bool fortio_fclose(fortio)")
    _truncate       = EclPrototype("bool fortio_ftruncate(fortio, long)")
    _filename       = EclPrototype("char* fortio_filename_ref(fortio)")


    def __init__(self, file_name, mode=READ_MODE, fmt_file=False, endian_flip_header=True):
        """Will open a new FortIO handle to @file_name - default for reading.

        The newly created FortIO handle will open the underlying FILE*
        for reading, but if you pass the flag mode=FortIO.WRITE_MODE
        the file will be opened for writing.

        Observe that the flag @endian_flip_header will only affect the
        interpretation of the block size markers in the file, endian
        flipping of the actual data blocks must be handled at a higher
        level.

        When you are finished working with the FortIO instance you can
        manually close it with the close() method, alternatively that
        will happen automagically when it goes out of scope.

        Small example script opening a restart file, and then writing
        all the pressure keywords to another file:

           import sys
           from ecl.ecl import FortIO, EclFile

           rst_file = EclFile(sys.argv[1])
           fortio = FortIO("PRESSURE", mode=FortIO.WRITE_MODE)

           for kw in rst_file:
               if kw.name() == "PRESSURE":
                  kw.write(fortio)

           fortio.close()

        See the documentation of openFortIO() for an alternative
        method based on a context manager and the with statement.

        """
        read_modes = (FortIO.READ_MODE, FortIO.APPEND_MODE, FortIO.READ_AND_WRITE_MODE)
        if mode in read_modes and not os.path.exists(file_name):
            raise IOError('No such file "%s".' % file_name)
        if mode == FortIO.READ_MODE:
            c_pointer = self._open_reader(file_name, fmt_file, endian_flip_header)
        elif mode == FortIO.WRITE_MODE:
            c_pointer = self._open_writer(file_name, fmt_file, endian_flip_header)
        elif mode == FortIO.READ_AND_WRITE_MODE:
            c_pointer = self._open_readwrite(file_name, fmt_file, endian_flip_header)
        elif mode == FortIO.APPEND_MODE:
            c_pointer = self._open_append(file_name, fmt_file, endian_flip_header)
        else:
            raise UserWarning("Unknown mode: %d" % mode)

        self.__mode = mode
        if not c_pointer:
            raise IOError('Failed to open FortIO file "%s".' % file_name)
        super(FortIO, self).__init__(c_pointer)



    def close(self):
        if self:
            self._close()
            self._invalidateCPointer()


    def get_position(self):
        """ @rtype: long """
        return self._get_position()


    def truncate(self, size=None):
        """Will truncate the file to new size.

        If the method is called without a size argument the stream
        will be truncated to the current position.
        """
        if size is None:
            size = self.getPosition()

        if not self._truncate(size):
            raise IOError("Truncate of fortran filehandle:%s failed" % self.filename())


    def filename(self):
        return self._filename()


    def seek(self, position, whence=0):
        # SEEK_SET = 0
        # SEEK_CUR = 1
        # SEEK_END = 2
        self._seek(position, whence)


    @classmethod
    def is_fortran_file(cls, filename, endian_flip=True):

        """@rtype: bool
        @type filename: str


        Will use heuristics to try to guess if @filename is a binary
        file written in fortran style. ASCII files will return false,
        even if they are structured as ECLIPSE keywords.
        """
        return cls._guess_fortran(filename, endian_flip)


    def free(self):
        self.close()