def _export_roff_discrete(self, pfile, name, append=False, last=True, binary=True): carray = _gridprop_lowlevel.update_carray(self, undef=-999) ptr_idum = _cxtgeo.new_intpointer() ptr_ddum = _cxtgeo.new_doublepointer() # codes: ptr_codes = _cxtgeo.new_intarray(256) ncodes = self.ncodes codenames = "" logger.info("Keys: %s", self.codes.keys()) for inum, ckey in enumerate(sorted(self.codes.keys())): if ckey is not None: codenames += str(self.codes[ckey]) codenames += "|" _cxtgeo.intarray_setitem(ptr_codes, inum, int(ckey)) else: logger.warning("For some odd reason, None is a key. Check!") mode = 0 if not binary: mode = 1 if not append: _cxtgeo.grd3d_export_roff_pstart(mode, self._ncol, self._nrow, self._nlay, pfile, XTGDEBUG) nsub = 0 isub_to_export = 0 _cxtgeo.grd3d_export_roff_prop( mode, self._ncol, self._nrow, self._nlay, nsub, isub_to_export, ptr_idum, name, "int", carray, ptr_ddum, ncodes, codenames, ptr_codes, pfile, XTGDEBUG, ) if last: _cxtgeo.grd3d_export_roff_end(mode, pfile, XTGDEBUG) _gridprop_lowlevel.delete_carray(self, carray)
def scan_dates(pfile, maxdates=1000, dataframe=False): """Quick scan dates in a simulation restart file. Cf. grid_properties.py description """ seq = _cxtgeo.new_intarray(maxdates) day = _cxtgeo.new_intarray(maxdates) mon = _cxtgeo.new_intarray(maxdates) yer = _cxtgeo.new_intarray(maxdates) local_fhandle = False fhandle = pfile if isinstance(pfile, str): pfile = xtgeo._XTGeoCFile(pfile) fhandle = pfile.fhandle local_fhandle = True nstat = _cxtgeo.grd3d_ecl_tsteps(fhandle, seq, day, mon, yer, maxdates, XTGDEBUG) if local_fhandle: pfile.close(cond=local_fhandle) sq = [] da = [] for i in range(nstat): sq.append(_cxtgeo.intarray_getitem(seq, i)) dday = _cxtgeo.intarray_getitem(day, i) dmon = _cxtgeo.intarray_getitem(mon, i) dyer = _cxtgeo.intarray_getitem(yer, i) date = "{0:4}{1:02}{2:02}".format(dyer, dmon, dday) da.append(int(date)) for item in [seq, day, mon, yer]: _cxtgeo.delete_intarray(item) zdates = list(zip(sq, da)) # list for PY3 if dataframe: cols = ["SEQNUM", "DATE"] df = pd.DataFrame.from_records(zdates, columns=cols) return df return zdates
def _export_segy_xtgeo(self, sfile): """Export SEGY via XTGeo internal C routine.""" values1d = self.values.reshape(-1) ilinesp = _cxtgeo.new_intarray(len(self._ilines)) xlinesp = _cxtgeo.new_intarray(len(self._xlines)) tracidp = _cxtgeo.new_intarray(self.ncol * self.nrow) ilns = self._ilines.astype(np.int32) xlns = self._xlines.astype(np.int32) trid = self._traceidcodes.flatten().astype(np.int32) _cxtgeo.swig_numpy_to_carr_i1d(ilns, ilinesp) _cxtgeo.swig_numpy_to_carr_i1d(xlns, xlinesp) _cxtgeo.swig_numpy_to_carr_i1d(trid, tracidp) status = _cxtgeo.cube_export_segy( sfile, self.ncol, self.nrow, self.nlay, values1d, self.xori, self.xinc, self.yori, self.yinc, self.zori, self.zinc, self.rotation, self.yflip, 1, ilinesp, xlinesp, tracidp, 0, XTGDEBUG, ) if status != 0: raise RuntimeError("Error when exporting to SEGY (xtgeo engine)") _cxtgeo.delete_intarray(ilinesp) _cxtgeo.delete_intarray(xlinesp)
def update_carray(self, undef=None, discrete=None, dtype=None, order="F"): """Copy (update) values from numpy to SWIG, 1D array, returns a pointer to SWIG C array. If discrete is defined as True or False, force the SWIG array to be of that kind. Note that dtype will "override" current datatype if set. The resulting carray will be in Fortran order, unless order is specified as 'C' """ dstatus = self._isdiscrete if discrete is not None: dstatus = bool(discrete) if undef is None: undef = xtgeo.UNDEF if dstatus: undef = xtgeo.UNDEF_INT logger.debug("Entering conversion from numpy to C array ...") values = self._values.copy() if not dtype: if dstatus: values = values.astype(np.int32) else: values = values.astype(np.float64) else: values = values.astype(dtype) values = ma.filled(values, undef) if order == "F": values = np.asfortranarray(values) values1d = np.ravel(values, order="K") if values1d.dtype == "float64" and dstatus: values1d = values1d.astype("int32") logger.debug("Casting has been done") if values1d.dtype == "float64": logger.debug("Convert to carray (double)") carray = _cxtgeo.new_doublearray(self.ntotal) _cxtgeo.swig_numpy_to_carr_1d(values1d, carray) elif values1d.dtype == "float32": logger.debug("Convert to carray (float)") carray = _cxtgeo.new_floatarray(self.ntotal) _cxtgeo.swig_numpy_to_carr_f1d(values1d, carray) elif values1d.dtype == "int32": logger.debug("Convert to carray (int32)") carray = _cxtgeo.new_intarray(self.ntotal) _cxtgeo.swig_numpy_to_carr_i1d(values1d, carray) else: raise RuntimeError( "Unsupported dtype, probable bug in {}".format(__name__)) return carray
def _scan_ecl_keywords(pfile, maxkeys=100000, dataframe=False): # In case pfile is not a file name but a swig pointer to a file handle, # the file must not be closed ultramax = int(1000000 / 9) # cf *swig_bnd_char_1m in cxtgeo.i if maxkeys > ultramax: raise ValueError("maxkeys value is too large, must be < {}".format(ultramax)) rectypes = _cxtgeo.new_intarray(maxkeys) reclens = _cxtgeo.new_longarray(maxkeys) recstarts = _cxtgeo.new_longarray(maxkeys) fhandle, pclose = _get_fhandle(pfile) nkeys, keywords = _cxtgeo.grd3d_scan_eclbinary( fhandle, rectypes, reclens, recstarts, maxkeys, XTGDEBUG ) _close_fhandle(fhandle, pclose) keywords = keywords.replace(" ", "") keywords = keywords.split("|") # record types translation (cf: grd3d_scan_eclbinary.c in cxtgeo) rct = { "1": "INTE", "2": "REAL", "3": "DOUB", "4": "CHAR", "5": "LOGI", "6": "MESS", "-1": "????", } rc = [] rl = [] rs = [] for i in range(nkeys): rc.append(rct[str(_cxtgeo.intarray_getitem(rectypes, i))]) rl.append(_cxtgeo.longarray_getitem(reclens, i)) rs.append(_cxtgeo.longarray_getitem(recstarts, i)) _cxtgeo.delete_intarray(rectypes) _cxtgeo.delete_longarray(reclens) _cxtgeo.delete_longarray(recstarts) result = list(zip(keywords, rc, rl, rs)) if dataframe: cols = ["KEYWORD", "TYPE", "NITEMS", "BYTESTART"] df = pd.DataFrame.from_records(result, columns=cols) return df return result
def copy(self): """Copy a grid instance (C pointers) and other props. Returns: A new instance (attached grid properties will also be unique) """ other = self.__class__() ntot = self.ncol * self.nrow * self.nlay ncoord = (self.ncol + 1) * (self.nrow + 1) * 2 * 3 nzcorn = self.ncol * self.nrow * (self.nlay + 1) * 4 new_p_coord_v = _cxtgeo.new_doublearray(ncoord) new_p_zcorn_v = _cxtgeo.new_doublearray(nzcorn) new_p_actnum_v = _cxtgeo.new_intarray(ntot) _cxtgeo.grd3d_copy( self.ncol, self.nrow, self.nlay, self._p_coord_v, self._p_zcorn_v, self._p_actnum_v, new_p_coord_v, new_p_zcorn_v, new_p_actnum_v, 0, XTGDEBUG, ) other._p_coord_v = new_p_coord_v other._p_zcorn_v = new_p_zcorn_v other._p_actnum_v = new_p_actnum_v other._ncol = self.ncol other._nrow = self.nrow other._nlay = self.nlay if isinstance(self.subgrids, dict): other.subgrids = deepcopy(self.subgrids) # copy attached properties if self._props: other._props = self._props.copy() logger.info("Other vs self props %s vs %s", other._props, self._props) if self._filesrc is not None and "(copy)" not in self._filesrc: other._filesrc = self._filesrc + " (copy)" elif self._filesrc is not None: other._filesrc = self._filesrc return other
def scan_dates(pfile, fformat="unrst", maxdates=1000, dataframe=False): """Quick scan dates in a simulation restart file. Cf. grid_properties.py description """ logger.info("Format supported as default is %s", fformat) seq = _cxtgeo.new_intarray(maxdates) day = _cxtgeo.new_intarray(maxdates) mon = _cxtgeo.new_intarray(maxdates) yer = _cxtgeo.new_intarray(maxdates) fhandle, pclose = _get_fhandle(pfile) nstat = _cxtgeo.grd3d_ecl_tsteps(fhandle, seq, day, mon, yer, maxdates, XTGDEBUG) _close_fhandle(fhandle, pclose) sq = [] da = [] for i in range(nstat): sq.append(_cxtgeo.intarray_getitem(seq, i)) dday = _cxtgeo.intarray_getitem(day, i) dmon = _cxtgeo.intarray_getitem(mon, i) dyer = _cxtgeo.intarray_getitem(yer, i) date = "{0:4}{1:02}{2:02}".format(dyer, dmon, dday) da.append(int(date)) for item in [seq, day, mon, yer]: _cxtgeo.delete_intarray(item) zdates = list(zip(sq, da)) # list for PY3 if dataframe: cols = ["SEQNUM", "DATE"] df = pd.DataFrame.from_records(zdates, columns=cols) return df return zdates
def _convert_np_carr_int(self, np_array): """Convert numpy 1D array to C array, assuming int type. The numpy is always a double (float64), so need to convert first """ carr = _cxtgeo.new_intarray(self.nrow) np_array = np_array.astype(np.int32) _cxtgeo.swig_numpy_to_carr_i1d(np_array, carr) return carr
def _rkwxvec(fhandle, kws, name, swap, strict=True): """Local function for returning swig pointers to C arrays. If strict is True, a ValueError will be raised if keyword is not found. If strict is False, None will be returned """ kwtypedict = { "int": 1, "float": 2, "double": 3, "char": 4, "bool": 5, "byte": 6 } dtype = 0 reclen = 0 bytepos = 1 for items in kws: if name in items[0]: dtype = kwtypedict.get(items[1]) reclen = items[2] bytepos = items[3] break if dtype == 0: if strict: raise ValueError("Cannot find property <{}> in file".format(name)) return None if reclen <= 1: raise SystemError("Stuff is rotten here...") xvec = None if dtype == 1: xvec = _cxtgeo.new_floatarray(reclen) _cxtgeo.grd3d_imp_roffbin_ivec(fhandle, swap, bytepos, xvec, reclen) elif dtype == 2: xvec = _cxtgeo.new_floatarray(reclen) _cxtgeo.grd3d_imp_roffbin_fvec(fhandle, swap, bytepos, xvec, reclen) elif dtype >= 4: xvec = _cxtgeo.new_intarray(reclen) # convert char/byte/bool to int _cxtgeo.grd3d_imp_roffbin_bvec(fhandle, swap, bytepos, xvec, reclen) else: raise ValueError("Unhandled dtype: {}".format(dtype)) return xvec
def create_box( self, dimension=(10, 12, 6), origin=(10.0, 20.0, 1000.0), oricenter=False, increment=(100, 150, 5), rotation=30.0, flip=1, ): """Create a shoebox grid from cubi'sh spec""" self._ncol, self._nrow, self._nlay = dimension ntot = self.ncol * self.nrow * self.nlay ncoord = (self.ncol + 1) * (self.nrow + 1) * 2 * 3 nzcorn = self.ncol * self.nrow * (self.nlay + 1) * 4 self._p_actnum_v = _cxtgeo.new_intarray(ntot) self._p_coord_v = _cxtgeo.new_doublearray(ncoord) self._p_zcorn_v = _cxtgeo.new_doublearray(nzcorn) option = 0 if oricenter: option = 1 _cxtgeo.grd3d_from_cube( self.ncol, self.nrow, self.nlay, self._p_coord_v, self._p_zcorn_v, self._p_actnum_v, origin[0], origin[1], origin[2], increment[0], increment[1], increment[2], rotation, flip, option, XTGDEBUG, ) self._actnum_indices = None self._filesrc = None self._props = None self._subgrids = None self._roxgrid = None self._roxindexer = None self._tmp = {}
def get_adjacent_cells(self, prop, val1, val2, activeonly=True): """Get adjacents cells""" if not isinstance(prop, GridProperty): raise ValueError("The argument prop is not a xtgeo.GridPropery") if prop.isdiscrete is False: raise ValueError("The argument prop is not a discrete property") result = GridProperty( ncol=self._ncol, nrow=self._nrow, nlay=self._nlay, values=np.zeros(self.ntotal, dtype=np.int32), name="ADJ_CELLS", discrete=True, ) p_prop1 = _gridprop_lowlevel.update_carray(prop) p_prop2 = _cxtgeo.new_intarray(self.ntotal) iflag1 = 1 if activeonly: iflag1 = 0 iflag2 = 1 _cxtgeo.grd3d_adj_cells( self._ncol, self._nrow, self._nlay, self._p_coord_v, self._p_zcorn_v, self._p_actnum_v, p_prop1, self.ntotal, val1, val2, p_prop2, self.ntotal, iflag1, iflag2, XTGDEBUG, ) _gridprop_lowlevel.update_values_from_carray(result, p_prop2, np.int32, delete=True) # return the property object return result
def reduce_to_one_layer(self): """Reduce the grid to one single layer. This can be useful for algorithms that need to test if a point is within the full grid. Example:: >>> from xtgeo.grid3d import Grid >>> gf = Grid('gullfaks2.roff') >>> gf.nlay 47 >>> gf.reduce_to_one_layer() >>> gf.nlay 1 """ # need new pointers in C (not for coord) ptr_new_num_act = _cxtgeo.new_intpointer() nnum = (1 + 1) * 4 ptr_new_zcorn_v = _cxtgeo.new_doublearray(self.ncol * self.nrow * nnum) ptr_new_actnum_v = _cxtgeo.new_intarray(self.ncol * self.nrow * 1) _cxtgeo.grd3d_reduce_onelayer( self.ncol, self.nrow, self.nlay, self._p_zcorn_v, ptr_new_zcorn_v, self._p_actnum_v, ptr_new_actnum_v, ptr_new_num_act, 0, XTGDEBUG, ) self._nlay = 1 self._p_zcorn_v = ptr_new_zcorn_v self._p_actnum_v = ptr_new_actnum_v self._props = None self._subgrids = None
def export_roff(self, gfile, option): """Export grid to ROFF format (binary)""" logger.debug("Export to ROFF...") nsubs = 0 if self.subgrids is None: logger.debug("Create a pointer for subgrd_v ...") subgrd_v = _cxtgeo.new_intpointer() else: nsubs = len(self.subgrids) subgrd_v = _cxtgeo.new_intarray(nsubs) for inum, (sname, sarray) in enumerate(self.subgrids.items()): logger.info("INUM SUBGRID: %s %s", inum, sname) _cxtgeo.intarray_setitem(subgrd_v, inum, len(sarray)) # get the geometrics list to find the xshift, etc gx = self.get_geometrics() _cxtgeo.grd3d_export_roff_grid( option, self._ncol, self._nrow, self._nlay, nsubs, 0, gx[3], gx[5], gx[7], self._p_coord_v, self._p_zcorn_v, self._p_actnum_v, subgrd_v, gfile, XTGDEBUG, ) # skip parameters for now (cf Perl code) # end tag _cxtgeo.grd3d_export_roff_end(option, gfile, XTGDEBUG)
def _import_roff_v1(self, pfile, name): """Import ROFF format, version 1""" # pylint: disable=too-many-locals # there is a todo here to get it more robust for various cases, # e.g. that a ROFF file may contain both a grid an numerous # props logger.info("Looking for %s in file %s", name, pfile) ptr_ncol = _cxtgeo.new_intpointer() ptr_nrow = _cxtgeo.new_intpointer() ptr_nlay = _cxtgeo.new_intpointer() ptr_ncodes = _cxtgeo.new_intpointer() ptr_type = _cxtgeo.new_intpointer() ptr_idum = _cxtgeo.new_intpointer() ptr_ddum = _cxtgeo.new_doublepointer() # read with mode 0, to scan for ncol, nrow, nlay and ndcodes, and if # property is found... ier, _codenames = _cxtgeo.grd3d_imp_prop_roffbin( pfile, 0, ptr_type, ptr_ncol, ptr_nrow, ptr_nlay, ptr_ncodes, name, ptr_idum, ptr_ddum, ptr_idum, 0, XTGDEBUG, ) if ier == -1: msg = "Cannot find property name {}".format(name) logger.warning(msg) raise SystemExit("Error from ROFF import") self._ncol = _cxtgeo.intpointer_value(ptr_ncol) self._nrow = _cxtgeo.intpointer_value(ptr_nrow) self._nlay = _cxtgeo.intpointer_value(ptr_nlay) self._ncodes = _cxtgeo.intpointer_value(ptr_ncodes) ptype = _cxtgeo.intpointer_value(ptr_type) ntot = self._ncol * self._nrow * self._nlay if self._ncodes <= 1: self._ncodes = 1 self._codes = {0: "undef"} logger.debug("Number of codes: %s", self._ncodes) # allocate if ptype == 1: # float, assign to double ptr_pval_v = _cxtgeo.new_doublearray(ntot) ptr_ival_v = _cxtgeo.new_intarray(1) self._isdiscrete = False self._dtype = "float64" elif ptype > 1: ptr_pval_v = _cxtgeo.new_doublearray(1) ptr_ival_v = _cxtgeo.new_intarray(ntot) self._isdiscrete = True self._dtype = "int32" # number of codes and names ptr_ccodes_v = _cxtgeo.new_intarray(self._ncodes) # NB! note the SWIG trick to return modified char values; use cstring.i # inn the config and %cstring_bounded_output(char *p_codenames_v, NN); # Then the argument for *p_codevalues_v in C is OMITTED here! ier, cnames = _cxtgeo.grd3d_imp_prop_roffbin( pfile, 1, ptr_type, ptr_ncol, ptr_nrow, ptr_nlay, ptr_ncodes, name, ptr_ival_v, ptr_pval_v, ptr_ccodes_v, 0, XTGDEBUG, ) if self._isdiscrete: _gridprop_lowlevel.update_values_from_carray(self, ptr_ival_v, np.int32, delete=True) else: _gridprop_lowlevel.update_values_from_carray(self, ptr_pval_v, np.float64, delete=True) # now make dictionary of codes if self._isdiscrete: cnames = cnames.replace(";", "") cname_list = cnames.split("|") cname_list.pop() # some rubbish as last entry ccodes = [] for ino in range(0, self._ncodes): ccodes.append(_cxtgeo.intarray_getitem(ptr_ccodes_v, ino)) self._codes = dict(zip(ccodes, cname_list)) self._name = name
def get_ijk_from_grid(self, grid, grid_id=""): """Getting IJK from a grid as well logs.""" wxarr = self.get_carray("X_UTME") wyarr = self.get_carray("Y_UTMN") wzarr = self.get_carray("Z_TVDSS") nlen = self.nrow wivec = _cxtgeo.new_intarray(nlen) wjvec = _cxtgeo.new_intarray(nlen) wkvec = _cxtgeo.new_intarray(nlen) onelayergrid = grid.copy() onelayergrid.reduce_to_one_layer() cstatus = _cxtgeo.grd3d_well_ijk( grid.ncol, grid.nrow, grid.nlay, grid._p_coord_v, grid._p_zcorn_v, grid._p_actnum_v, onelayergrid._p_zcorn_v, onelayergrid._p_actnum_v, self.nrow, wxarr, wyarr, wzarr, wivec, wjvec, wkvec, 0, XTG_DEBUG, ) if cstatus != 0: raise RuntimeError("Error from C routine, code is {}".format(cstatus)) indarray = _cxtgeo.swig_carr_to_numpy_i1d(nlen, wivec).astype("float") jndarray = _cxtgeo.swig_carr_to_numpy_i1d(nlen, wjvec).astype("float") kndarray = _cxtgeo.swig_carr_to_numpy_i1d(nlen, wkvec).astype("float") indarray[indarray == 0] = np.nan jndarray[jndarray == 0] = np.nan kndarray[kndarray == 0] = np.nan icellname = "ICELL" + grid_id jcellname = "JCELL" + grid_id kcellname = "KCELL" + grid_id self._df[icellname] = indarray self._df[jcellname] = jndarray self._df[kcellname] = kndarray for cellname in [icellname, jcellname, kcellname]: self._wlogtype[cellname] = "DISC" self._wlogrecord[icellname] = { ncel: str(ncel) for ncel in range(1, grid.ncol + 1) } self._wlogrecord[jcellname] = { ncel: str(ncel) for ncel in range(1, grid.nrow + 1) } self._wlogrecord[kcellname] = { ncel: str(ncel) for ncel in range(1, grid.nlay + 1) } _cxtgeo.delete_intarray(wivec) _cxtgeo.delete_intarray(wjvec) _cxtgeo.delete_intarray(wkvec) _cxtgeo.delete_doublearray(wxarr) _cxtgeo.delete_doublearray(wyarr) _cxtgeo.delete_doublearray(wzarr) del onelayergrid
def import_ecl_bgrdecl(self, gfile): """Import binary files with GRDECL layout""" fhandle, pclose = _get_fhandle(gfile) # scan file for properties; these have similar binary format as e.g. EGRID logger.info("Make kwlist by scanning") kwlist = utils.scan_keywords(fhandle, fformat="xecl", maxkeys=1000, dataframe=False, dates=False) bpos = {} needkwlist = ["SPECGRID", "COORD", "ZCORN", "ACTNUM"] optkwlist = ["MAPAXES"] for name in needkwlist + optkwlist: bpos[name] = -1 # initially for kwitem in kwlist: kwname, kwtype, kwlen, kwbyte = kwitem if kwname == "SPECGRID": # read grid geometry record: specgrid = eclbin_record(fhandle, "SPECGRID", kwlen, kwtype, kwbyte) ncol, nrow, nlay = specgrid[0:3].tolist() logger.info("%s %s %s", ncol, nrow, nlay) elif kwname in needkwlist: bpos[kwname] = kwbyte elif kwname == "MAPAXES": # not always present bpos[kwname] = kwbyte self._ncol = ncol self._nrow = nrow self._nlay = nlay logger.info("Grid dimensions in binary GRDECL file: %s %s %s", ncol, nrow, nlay) # allocate dimensions: ntot = self._ncol * self._nrow * self._nlay ncoord = (self._ncol + 1) * (self._nrow + 1) * 2 * 3 nzcorn = self._ncol * self._nrow * (self._nlay + 1) * 4 self._p_coord_v = _cxtgeo.new_doublearray(ncoord) self._p_zcorn_v = _cxtgeo.new_doublearray(nzcorn) self._p_actnum_v = _cxtgeo.new_intarray(ntot) nact = _cxtgeo.grd3d_imp_ecl_egrid( fhandle, self._ncol, self._nrow, self._nlay, bpos["MAPAXES"], bpos["COORD"], bpos["ZCORN"], bpos["ACTNUM"], self._p_coord_v, self._p_zcorn_v, self._p_actnum_v, XTGDEBUG, ) self._nactive = nact _close_fhandle(fhandle, pclose)
def import_ecl_egrid(self, gfile): """Import, private to this routine. """ fhandle, pclose = _get_fhandle(gfile) # scan file for property logger.info("Make kwlist by scanning") kwlist = utils.scan_keywords(fhandle, fformat="xecl", maxkeys=1000, dataframe=False, dates=False) bpos = {} for name in ("COORD", "ZCORN", "ACTNUM", "MAPAXES"): bpos[name] = -1 # initially for kwitem in kwlist: kwname, kwtype, kwlen, kwbyte = kwitem if kwname == "GRIDHEAD": # read GRIDHEAD record: gridhead = eclbin_record(fhandle, "GRIDHEAD", kwlen, kwtype, kwbyte) ncol, nrow, nlay = gridhead[1:4].tolist() logger.info("%s %s %s", ncol, nrow, nlay) elif kwname in ("COORD", "ZCORN", "ACTNUM"): bpos[kwname] = kwbyte elif kwname == "MAPAXES": # not always present bpos[kwname] = kwbyte self._ncol = ncol self._nrow = nrow self._nlay = nlay logger.info("Grid dimensions in EGRID file: %s %s %s", ncol, nrow, nlay) # allocate dimensions: ntot = self._ncol * self._nrow * self._nlay ncoord = (self._ncol + 1) * (self._nrow + 1) * 2 * 3 nzcorn = self._ncol * self._nrow * (self._nlay + 1) * 4 self._p_coord_v = _cxtgeo.new_doublearray(ncoord) self._p_zcorn_v = _cxtgeo.new_doublearray(nzcorn) self._p_actnum_v = _cxtgeo.new_intarray(ntot) nact = _cxtgeo.grd3d_imp_ecl_egrid( fhandle, self._ncol, self._nrow, self._nlay, bpos["MAPAXES"], bpos["COORD"], bpos["ZCORN"], bpos["ACTNUM"], self._p_coord_v, self._p_zcorn_v, self._p_actnum_v, XTGDEBUG, ) self._nactive = nact _close_fhandle(fhandle, pclose)
def make_hybridgrid(grid, **kwargs): """Make hybrid grid. It changes the grid geometry status of the object. Input: grid (object): A grid object TODO region (object): A region parameter (property object) etc... """ nhdiv = kwargs.get("nhdiv") toplevel = kwargs.get("toplevel") bottomlevel = kwargs.get("bottomlevel") region = kwargs.get("region", None) region_number = kwargs.get("region_number", None) logger.debug("nhdiv: %s", nhdiv) logger.debug("toplevel: %s", toplevel) logger.debug("bottomlevel: %s", bottomlevel) logger.debug("region: %s", region) logger.debug("region_number: %s", region_number) xtg_verbose_level = xtg.syslevel newnlay = grid.nlay * 2 + nhdiv hyb_num_act = _cxtgeo.new_intpointer() hyb_p_zcorn_v = _cxtgeo.new_doublearray(grid.ncol * grid.nrow * (newnlay + 1) * 4) hyb_p_actnum_v = _cxtgeo.new_intarray(grid.ncol * grid.nrow * newnlay) if region is None: _cxtgeo.grd3d_convert_hybrid( grid.ncol, grid.nrow, grid.nlay, grid._p_coord_v, grid._p_zcorn_v, grid._p_actnum_v, newnlay, hyb_p_zcorn_v, hyb_p_actnum_v, hyb_num_act, toplevel, bottomlevel, nhdiv, xtg_verbose_level, ) else: region.discrete_to_continuous() carray_reg = _gridprop_lowlevel.update_carray(region) _cxtgeo.grd3d_convert_hybrid2( grid.ncol, grid.nrow, grid.nlay, grid._p_coord_v, grid._p_zcorn_v, grid._p_actnum_v, newnlay, hyb_p_zcorn_v, hyb_p_actnum_v, hyb_num_act, toplevel, bottomlevel, nhdiv, carray_reg, region_number, xtg_verbose_level, ) _gridprop_lowlevel.delete_carray(region, carray_reg) grid._nlay = newnlay grid._p_zcorn_v = hyb_p_zcorn_v grid._p_actnum_v = hyb_p_actnum_v return grid
def import_ecl_grdecl(self, gfile): # make a temporary file fds, tmpfile = mkstemp(prefix="tmpxtgeo") os.close(fds) with open(gfile) as oldfile, open(tmpfile, "w") as newfile: for line in oldfile: if not (re.search(r"^--", line) or re.search(r"^\s+$", line)): newfile.write(line) newfile.close() oldfile.close() # find ncol nrow nz mylist = [] found = False with open(tmpfile) as xfile: for line in xfile: if found: logger.info(line) mylist = line.split() break if re.search(r"^SPECGRID", line): found = True if not found: logger.error("SPECGRID not found. Nothing imported!") return xfile.close() self._ncol, self._nrow, self._nlay = int(mylist[0]), int(mylist[1]), int(mylist[2]) logger.info("NX NY NZ in grdecl file: %s %s %s", self._ncol, self._nrow, self._nlay) ntot = self._ncol * self._nrow * self._nlay ncoord = (self._ncol + 1) * (self._nrow + 1) * 2 * 3 nzcorn = self._ncol * self._nrow * (self._nlay + 1) * 4 logger.info("Reading...") ptr_num_act = _cxtgeo.new_intpointer() self._p_coord_v = _cxtgeo.new_doublearray(ncoord) self._p_zcorn_v = _cxtgeo.new_doublearray(nzcorn) self._p_actnum_v = _cxtgeo.new_intarray(ntot) _cxtgeo.grd3d_import_grdecl( self._ncol, self._nrow, self._nlay, self._p_coord_v, self._p_zcorn_v, self._p_actnum_v, ptr_num_act, tmpfile, XTGDEBUG, ) # remove tmpfile os.remove(tmpfile) nact = _cxtgeo.intpointer_value(ptr_num_act) logger.info("Number of active cells: %s", nact) self._subgrids = None
def refine_vertically(self, rfactor, zoneprop=None): """Refine vertically, proportionally See details in caller. """ rfactord = OrderedDict() # case 1 rfactor as scalar value. if isinstance(rfactor, int): if self.subgrids: subgrids = self.get_subgrids() for i, _ in enumerate(self.subgrids.keys()): rfactord[i + 1] = rfactor else: rfactord[0] = rfactor subgrids = OrderedDict() subgrids[1] = self.nlay # case 2 rfactor is a dict else: rfactord = OrderedDict(sorted(rfactor.items())) # redefined to ordered # 2a: zoneprop is present if zoneprop is not None: oldsubgrids = None if self.subgrids: oldsubgrids = self.get_subgrids() subgrids = self.subgrids_from_zoneprop(zoneprop) if oldsubgrids: if subgrids.values() != oldsubgrids.values(): xtg.warn("ISSUES!!!") # 2b: zoneprop is not present elif zoneprop is None and self.subgrids: subgrids = self.get_subgrids() elif zoneprop is None and not self.subgrids: raise ValueError( "You gave in a dict, but no zoneprops and " "subgrids are not preesent in the grid" ) else: raise ValueError("Some major unexpected issue in routine...") if len(subgrids) != len(rfactord): raise RuntimeError("Subgrids and refinements: different definition!") self.set_subgrids(subgrids) # Now, based on dict, give a value per subgrid for key, val in rfactor newsubgrids = OrderedDict() newnlay = 0 for (_x, rfi), (snam, sran) in zip(rfactord.items(), subgrids.items()): newsubgrids[snam] = sran * rfi newnlay += newsubgrids[snam] logger.debug("New layers: %s", newnlay) # rfac is an array with length nlay; has N refinements per single K layer rfac = _cxtgeo.new_intarray(self.nlay) totvector = [] for (_tmp1, rfi), (_tmp2, arr) in zip(rfactord.items(), self.subgrids.items()): for _elem in range(len(arr)): totvector.append(rfi) for inn, rfi in enumerate(totvector): _cxtgeo.intarray_setitem(rfac, inn, rfi) ref_num_act = _cxtgeo.new_intpointer() ref_p_zcorn_v = _cxtgeo.new_doublearray(self.ncol * self.nrow * (newnlay + 1) * 4) ref_p_actnum_v = _cxtgeo.new_intarray(self.ncol * self.nrow * newnlay) ier = _cxtgeo.grd3d_refine_vert( self.ncol, self.nrow, self.nlay, self._p_coord_v, self._p_zcorn_v, self._p_actnum_v, newnlay, ref_p_zcorn_v, ref_p_actnum_v, ref_num_act, rfac, 0, XTGDEBUG, ) if ier != 0: raise RuntimeError( "An error occured in the C routine " "grd3d_refine_vert, code {}".format(ier) ) # update instance: self._nlay = newnlay self._p_zcorn_v = ref_p_zcorn_v self._p_actnum_v = ref_p_actnum_v if self.subgrids is None or len(self.subgrids) <= 1: self.subgrids = None else: self.set_subgrids(newsubgrids) return self
def crop(self, spec, props=None): # pylint: disable=too-many-locals """Do cropping of geometry (and properties). If props is 'all' then all properties assosiated (linked) to then grid are also cropped, and the instances are updated. Args: spec (tuple): A nested tuple on the form ((i1, i2), (j1, j2), (k1, k2)) where 1 represents start number, and 2 reperesent end. The range is inclusive for both ends, and the number start index is 1 based. props (list or str): None is default, while properties can be listed. If 'all', then all GridProperty objects which are linked to the Grid instance are updated. Returns: The instance is updated (cropped) """ (ic1, ic2), (jc1, jc2), (kc1, kc2) = spec if (ic1 < 1 or ic2 > self.ncol or jc1 < 1 or jc2 > self.nrow or kc1 < 1 or kc2 > self.nlay): raise ValueError("Boundary for tuples not matching grid" "NCOL, NROW, NLAY") oldnlay = self._nlay # compute size of new cropped grid nncol = ic2 - ic1 + 1 nnrow = jc2 - jc1 + 1 nnlay = kc2 - kc1 + 1 ntot = nncol * nnrow * nnlay ncoord = (nncol + 1) * (nnrow + 1) * 2 * 3 nzcorn = nncol * nnrow * (nnlay + 1) * 4 new_num_act = _cxtgeo.new_intpointer() new_p_coord_v = _cxtgeo.new_doublearray(ncoord) new_p_zcorn_v = _cxtgeo.new_doublearray(nzcorn) new_p_actnum_v = _cxtgeo.new_intarray(ntot) _cxtgeo.grd3d_crop_geometry( self.ncol, self.nrow, self.nlay, self._p_coord_v, self._p_zcorn_v, self._p_actnum_v, new_p_coord_v, new_p_zcorn_v, new_p_actnum_v, ic1, ic2, jc1, jc2, kc1, kc2, new_num_act, 0, XTGDEBUG, ) self._p_coord_v = new_p_coord_v self._p_zcorn_v = new_p_zcorn_v self._p_actnum_v = new_p_actnum_v self._ncol = nncol self._nrow = nnrow self._nlay = nnlay if isinstance(self.subgrids, dict): newsub = OrderedDict() # easier to work with numpies than lists newarr = np.array(range(1, oldnlay + 1)) newarr[newarr < kc1] = 0 newarr[newarr > kc2] = 0 newaxx = newarr.copy() - kc1 + 1 for sub, arr in self.subgrids.items(): arrx = np.array(arr) arrxmap = newaxx[arrx[0] - 1:arrx[-1]] arrxmap = arrxmap[arrxmap > 0] if arrxmap.size > 0: newsub[sub] = arrxmap.astype(np.int32).tolist() self.subgrids = newsub # crop properties if props is not None: if props == "all": props = self.props for prop in props: logger.info("Crop %s", prop.name) prop.crop(spec)
def import_ecl_bgrdecl(self, gfile): """Import binary files with GRDECL layout""" local_fhandle = False fhandle = gfile if isinstance(gfile, str): local_fhandle = True gfile = xtgeo._XTGeoCFile(gfile) fhandle = gfile.fhandle # scan file for properties; these have similar binary format as e.g. EGRID logger.info("Make kwlist by scanning") kwlist = utils.scan_keywords( fhandle, fformat="xecl", maxkeys=1000, dataframe=False, dates=False ) bpos = {} needkwlist = ["SPECGRID", "COORD", "ZCORN", "ACTNUM"] optkwlist = ["MAPAXES"] for name in needkwlist + optkwlist: bpos[name] = -1 # initially for kwitem in kwlist: kwname, kwtype, kwlen, kwbyte = kwitem if kwname == "SPECGRID": # read grid geometry record: specgrid = eclbin_record(fhandle, "SPECGRID", kwlen, kwtype, kwbyte) ncol, nrow, nlay = specgrid[0:3].tolist() logger.info("%s %s %s", ncol, nrow, nlay) elif kwname in needkwlist: bpos[kwname] = kwbyte elif kwname == "MAPAXES": # not always present bpos[kwname] = kwbyte self._ncol = ncol self._nrow = nrow self._nlay = nlay logger.info("Grid dimensions in binary GRDECL file: %s %s %s", ncol, nrow, nlay) # allocate dimensions: ntot = self._ncol * self._nrow * self._nlay ncoord = (self._ncol + 1) * (self._nrow + 1) * 2 * 3 nzcorn = self._ncol * self._nrow * (self._nlay + 1) * 4 self._p_coord_v = _cxtgeo.new_doublearray(ncoord) self._p_zcorn_v = _cxtgeo.new_doublearray(nzcorn) self._p_actnum_v = _cxtgeo.new_intarray(ntot) p_nact = _cxtgeo.new_longpointer() ier = _cxtgeo.grd3d_imp_ecl_egrid( fhandle, self._ncol, self._nrow, self._nlay, bpos["MAPAXES"], bpos["COORD"], bpos["ZCORN"], bpos["ACTNUM"], self._p_coord_v, self._p_zcorn_v, self._p_actnum_v, p_nact, 0, ) if ier == -1: raise RuntimeError("Error code -1 from _cxtgeo.grd3d_imp_ecl_egrid") self._nactive = _cxtgeo.longpointer_value(p_nact) if local_fhandle: gfile.close(cond=local_fhandle)
def import_ecl_egrid(self, gfile): """Import, private to this routine.""" eclfile = xtgeo._XTGeoCFile(gfile) # scan file for property logger.info("Make kwlist by scanning") kwlist = utils.scan_keywords( eclfile.fhandle, fformat="xecl", maxkeys=1000, dataframe=False, dates=False ) bpos = {} for name in ("COORD", "ZCORN", "ACTNUM", "MAPAXES"): bpos[name] = -1 # initially self._dualporo = False for kwitem in kwlist: kwname, kwtype, kwlen, kwbyte = kwitem if kwname == "FILEHEAD": # read FILEHEAD record: filehead = eclbin_record(eclfile.fhandle, "FILEHEAD", kwlen, kwtype, kwbyte) dualp = filehead[5].tolist() logger.info("Dual porosity flag is %s", dualp) if dualp == 1: self._dualporo = True self._dualperm = False elif dualp == 2: self._dualporo = True self._dualperm = True elif kwname == "GRIDHEAD": # read GRIDHEAD record: gridhead = eclbin_record(eclfile.fhandle, "GRIDHEAD", kwlen, kwtype, kwbyte) ncol, nrow, nlay = gridhead[1:4].tolist() logger.info("%s %s %s", ncol, nrow, nlay) elif kwname in ("COORD", "ZCORN", "ACTNUM"): bpos[kwname] = kwbyte elif kwname == "MAPAXES": # not always present bpos[kwname] = kwbyte self._ncol = ncol self._nrow = nrow self._nlay = nlay logger.info("Grid dimensions in EGRID file: %s %s %s", ncol, nrow, nlay) # allocate dimensions: ntot = self._ncol * self._nrow * self._nlay ncoord = (self._ncol + 1) * (self._nrow + 1) * 2 * 3 nzcorn = self._ncol * self._nrow * (self._nlay + 1) * 4 self._p_coord_v = _cxtgeo.new_doublearray(ncoord) self._p_zcorn_v = _cxtgeo.new_doublearray(nzcorn) self._p_actnum_v = _cxtgeo.new_intarray(ntot) p_nact = _cxtgeo.new_longpointer() option = 0 if self._dualporo: option = 1 ier = _cxtgeo.grd3d_imp_ecl_egrid( eclfile.fhandle, self._ncol, self._nrow, self._nlay, bpos["MAPAXES"], bpos["COORD"], bpos["ZCORN"], bpos["ACTNUM"], self._p_coord_v, self._p_zcorn_v, self._p_actnum_v, p_nact, option, ) if ier == -1: raise RuntimeError("Error code -1 from _cxtgeo.grd3d_imp_ecl_egrid") self._nactive = _cxtgeo.longpointer_value(p_nact) # in case of DUAL PORO/PERM ACTNUM will be 0..3; need to convert if self._dualporo: self._dualactnum = self.get_actnum(name="DUALACTNUM") acttmp = self._dualactnum.copy() acttmp.values[acttmp.values >= 1] = 1 self.set_actnum(acttmp) eclfile.close()
def _convert_to_xtgeo_grid(self, rox, roxgrid, corners): """Convert from RMS API to XTGeo API""" # pylint: disable=too-many-statements logger.info("Converting to XTGeo internals...") logger.info("Call the ROXAPI grid indexer") indexer = roxgrid.grid_indexer ncol, nrow, nlay = indexer.dimensions ntot = ncol * nrow * nlay # update other attributes self._ncol = ncol self._nrow = nrow self._nlay = nlay if corners is None: logger.info("Asked for dimensions_only: No geometry read!") return logger.info("Get active cells") mybuffer = np.ndarray(indexer.dimensions, dtype=np.int32) mybuffer.fill(0) logger.info("Get cell numbers") cellno = indexer.get_cell_numbers_in_range((0, 0, 0), indexer.dimensions) logger.info("Reorder...") ijk = indexer.get_indices(cellno) iind = ijk[:, 0] jind = ijk[:, 1] kind = ijk[:, 2] pvalues = np.ones(len(cellno)) pvalues[cellno] = 1 mybuffer[iind, jind, kind] = pvalues[cellno] actnum = mybuffer if rox.version_required("1.3"): logger.info("Handedness (new) %s", indexer.ijk_handedness) else: logger.info("Handedness (old) %s", indexer.handedness) corners = corners.ravel(order="K") actnum = actnum.ravel(order="K") logger.info("Convert to C pointers...") nnum = ncol * nrow * nlay * 24 ccorners = _cxtgeo.new_doublearray(nnum) ntot = ncol * nrow * nlay cactnum = _cxtgeo.new_intarray(ntot) ncoord = (ncol + 1) * (nrow + 1) * 2 * 3 nzcorn = ncol * nrow * (nlay + 1) * 4 self._p_coord_v = _cxtgeo.new_doublearray(ncoord) self._p_zcorn_v = _cxtgeo.new_doublearray(nzcorn) self._p_actnum_v = _cxtgeo.new_intarray(ntot) _cxtgeo.swig_numpy_to_carr_1d(corners, ccorners) _cxtgeo.swig_numpy_to_carr_i1d(actnum, cactnum) # next task is to convert geometry to cxtgeo internal format logger.info("Run XTGeo C code...") _cxtgeo.grd3d_conv_roxapi_grid( ncol, nrow, nlay, ntot, cactnum, ccorners, self._p_coord_v, self._p_zcorn_v, self._p_actnum_v, XTGDEBUG, ) logger.info("Run XTGeo C code... done") _cxtgeo.delete_doublearray(ccorners) _cxtgeo.delete_intarray(cactnum) logger.info("Converting to XTGeo internals... done") # subgrids if len(indexer.zonation) > 1: logger.debug("Zonation length (N subzones) is %s", len(indexer.zonation)) subz = OrderedDict() for inum, zrange in indexer.zonation.items(): logger.debug("inum: %s, zrange: %s", inum, zrange) zname = roxgrid.zone_names[inum] logger.debug("zname is: %s", zname) zra = [nn + 1 for ira in zrange for nn in ira] # nested lists subz[zname] = zra self.subgrids = subz