def geometrics(self): """Compute some well geometrical arrays MD, INCL, AZI, as logs. These are kind of quasi measurements hence the logs will named with a Q in front as Q_MDEPTH, Q_INCL, and Q_AZI. These logs will be added to the dataframe. If the mdlogname attribute does not exist in advance, it will be set to 'Q_MDEPTH'. Returns: False if geometrics cannot be computed """ if self._df.size < 3: logger.warning( "Cannot compute geometrics for %s. Too few " "trajectory points", self.name, ) return False # extract numpies from XYZ trajetory logs ptr_xv = self.get_carray("X_UTME") ptr_yv = self.get_carray("Y_UTMN") ptr_zv = self.get_carray("Z_TVDSS") # get number of rows in pandas nlen = self.nrow ptr_md = _cxtgeo.new_doublearray(nlen) ptr_incl = _cxtgeo.new_doublearray(nlen) ptr_az = _cxtgeo.new_doublearray(nlen) ier = _cxtgeo.well_geometrics( nlen, ptr_xv, ptr_yv, ptr_zv, ptr_md, ptr_incl, ptr_az, 0, XTGDEBUG ) if ier != 0: sys.exit(-9) dnumpy = self._convert_carr_double_np(ptr_md) self._df["Q_MDEPTH"] = pd.Series(dnumpy, index=self._df.index) dnumpy = self._convert_carr_double_np(ptr_incl) self._df["Q_INCL"] = pd.Series(dnumpy, index=self._df.index) dnumpy = self._convert_carr_double_np(ptr_az) self._df["Q_AZI"] = pd.Series(dnumpy, index=self._df.index) if not self._mdlogname: self._mdlogname = "Q_MDEPTH" # delete tmp pointers _cxtgeo.delete_doublearray(ptr_xv) _cxtgeo.delete_doublearray(ptr_yv) _cxtgeo.delete_doublearray(ptr_zv) _cxtgeo.delete_doublearray(ptr_md) _cxtgeo.delete_doublearray(ptr_incl) _cxtgeo.delete_doublearray(ptr_az) return True
def get_dxdy(self, names=("dX", "dY"), asmasked=False): """Get dX, dY as properties""" ntot = self._ncol * self._nrow * self._nlay dx = GridProperty( ncol=self._ncol, nrow=self._nrow, nlay=self._nlay, values=np.zeros(ntot, dtype=np.float64), name=names[0], discrete=False, ) dy = GridProperty( ncol=self._ncol, nrow=self._nrow, nlay=self._nlay, values=np.zeros(ntot, dtype=np.float64), name=names[1], discrete=False, ) ptr_dx_v = _cxtgeo.new_doublearray(self.ntotal) ptr_dy_v = _cxtgeo.new_doublearray(self.ntotal) option1 = 0 option2 = 0 if asmasked: option1 = 1 _cxtgeo.grd3d_calc_dxdy( self._ncol, self._nrow, self._nlay, self._p_coord_v, self._p_zcorn_v, self._p_actnum_v, ptr_dx_v, ptr_dy_v, option1, option2, XTGDEBUG, ) _gridprop_lowlevel.update_values_from_carray(dx, ptr_dx_v, np.float64, delete=True) _gridprop_lowlevel.update_values_from_carray(dy, ptr_dy_v, np.float64, delete=True) # return the property objects return dx, dy
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 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 _convert_np_carr_double(self, np_array): """Convert numpy 1D array to C array, assuming double type.""" carr = _cxtgeo.new_doublearray(self.nrow) _cxtgeo.swig_numpy_to_carr_1d(np_array, carr) return carr
def get_carr_double(self): """Return the SWIG Carray object""" carr = _cxtgeo.new_doublearray(self.ncol * self.nrow) _cxtgeo.swig_numpy_to_carr_1d(self.get_values1d(), carr) return carr
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 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 get_dz(self, name="dZ", flip=True, asmasked=True): """Get dZ as property""" ntot = (self._ncol, self._nrow, self._nlay) dzv = GridProperty( ncol=self._ncol, nrow=self._nrow, nlay=self._nlay, values=np.zeros(ntot, dtype=np.float64), name=name, discrete=False, ) ptr_dz_v = _cxtgeo.new_doublearray(self.ntotal) nflip = 1 if not flip: nflip = -1 option = 0 if asmasked: option = 1 _cxtgeo.grd3d_calc_dz( self._ncol, self._nrow, self._nlay, self._p_zcorn_v, self._p_actnum_v, ptr_dz_v, nflip, option, XTGDEBUG, ) _gridprop_lowlevel.update_values_from_carray(dzv, ptr_dz_v, np.float64, delete=True) # return the property object logger.info("DZ mean value: %s", dzv.values.mean()) return dzv
def import_irap_binary2(self, mfile, values=True): """Import Irap binary format, variant 2.""" logger.debug("Enter function (v2)...") pmx = _cxtgeo.new_intpointer() pmy = _cxtgeo.new_intpointer() pll = _cxtgeo.new_longpointer() pxori = _cxtgeo.new_doublepointer() pyori = _cxtgeo.new_doublepointer() pxinc = _cxtgeo.new_doublepointer() pyinc = _cxtgeo.new_doublepointer() prot = _cxtgeo.new_doublepointer() pmap = _cxtgeo.new_doublearray(1) logger.debug("Enter function (v2)... scanning start") _cxtgeo.surf_import_irap_bin2(mfile, 0, pmx, pmy, pll, pxori, pyori, pxinc, pyinc, prot, pmap, 1, 0) logger.debug("Enter function (v2)... scanning done")
def get_xyz_cell_corners(self, ijk=(1, 1, 1), activeonly=True, zerobased=False): """Get X Y Z cell corners for one cell.""" i, j, k = ijk shift = 0 if zerobased: shift = 1 if activeonly: actnum = self.get_actnum() iact = actnum.values3d[i - 1 + shift, j - 1 + shift, k - 1 + shift] if iact == 0: return None pcorners = _cxtgeo.new_doublearray(24) _cxtgeo.grd3d_corners( i + shift, j + shift, k + shift, self.ncol, self.nrow, self.nlay, self._p_coord_v, self._p_zcorn_v, pcorners, XTGDEBUG, ) cornerlist = [] for i in range(24): cornerlist.append(_cxtgeo.doublearray_getitem(pcorners, i)) clist = tuple(cornerlist) return clist
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 get_xyz_corners(self, names=("X_UTME", "Y_UTMN", "Z_TVDSS")): """Get X Y Z cell corners for all cells (as 24 GridProperty objects)""" ntot = (self._ncol, self._nrow, self._nlay) grid_props = [] for i in range(0, 8): xname = names[0] + str(i) yname = names[1] + str(i) zname = names[2] + str(i) x = GridProperty( ncol=self._ncol, nrow=self._nrow, nlay=self._nlay, values=np.zeros(ntot, dtype=np.float64), name=xname, discrete=False, ) y = GridProperty( ncol=self._ncol, nrow=self._nrow, nlay=self._nlay, values=np.zeros(ntot, dtype=np.float64), name=yname, discrete=False, ) z = GridProperty( ncol=self._ncol, nrow=self._nrow, nlay=self._nlay, values=np.zeros(ntot, dtype=np.float64), name=zname, discrete=False, ) grid_props.append(x) grid_props.append(y) grid_props.append(z) ptr_coord = [] for i in range(24): some = _cxtgeo.new_doublearray(self.ntotal) ptr_coord.append(some) for i, va in enumerate(ptr_coord): logger.debug("SWIG object %s %s", i, va) option = 0 # note, fool the argument list to unpack ptr_coord with * ... _cxtgeo.grd3d_get_all_corners(self._ncol, self._nrow, self._nlay, self._p_coord_v, self._p_zcorn_v, self._p_actnum_v, *(ptr_coord + [option] + [XTGDEBUG])) for i in range(0, 24, 3): _gridprop_lowlevel.update_values_from_carray(grid_props[i], ptr_coord[i], np.float64, delete=True) _gridprop_lowlevel.update_values_from_carray(grid_props[i + 1], ptr_coord[i + 1], np.float64, delete=True) _gridprop_lowlevel.update_values_from_carray(grid_props[i + 2], ptr_coord[i + 2], np.float64, delete=True) # return the 24 objects (x1, y1, z1, ... x8, y8, z8) return tuple(grid_props)
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 report_zone_mismatch( # pylint: disable=too-many-statements self, well=None, zonelogname="ZONELOG", zoneprop=None, onelayergrid=None, zonelogrange=(0, 9999), zonelogshift=0, depthrange=None, option=0, perflogname=None, ): """Reports well to zone mismatch; this works together with a Well object.""" this = inspect.currentframe().f_code.co_name # first do some trimming of the well dataframe if not well or not isinstance(well, Well): msg = "No well object in <{}> or invalid object; " "returns no result".format( this) xtg.warn(msg) return None if not well.zonelogname: msg = ("Asked for zone log <{}> but no such in <{}> for well {}; " "return None".format(zonelogname, this, well.wellname)) xtg.warn(msg) # warnings.warn(UserWarning(msg)) return None # qperf = True if perflogname == "None" or perflogname is None: # qperf = False pass else: if perflogname not in well.lognames: msg = ("Asked for perf log <{}> but no such in <{}> for well {}; " "return None".format(perflogname, this, well.wellname)) xtg.warn(msg) # warnings.warn(UserWarning(msg)) return None logger.info("Process well object for %s...", well.wellname) df = well.dataframe.copy() if depthrange: logger.info("Filter depth...") df = df[df.Z_TVDSS > depthrange[0]] df = df[df.Z_TVDSS < depthrange[1]] df = df.copy() logger.info("Adding zoneshift %s", zonelogshift) if zonelogshift != 0: df[zonelogname] += zonelogshift logger.info("Filter ZONELOG...") df = df[df[zonelogname] > zonelogrange[0]] df = df[df[zonelogname] < zonelogrange[1]] df = df.copy() if perflogname: logger.info("Filter PERF...") df[perflogname].fillna(-999, inplace=True) df = df[df[perflogname] > 0] df = df.copy() df.reset_index(drop=True, inplace=True) well.dataframe = df # get the relevant well log C arrays... ptr_xc = well.get_carray("X_UTME") ptr_yc = well.get_carray("Y_UTMN") ptr_zc = well.get_carray("Z_TVDSS") ptr_zo = well.get_carray(zonelogname) nval = well.nrow ptr_results = _cxtgeo.new_doublearray(10) ptr_zprop = _gridprop_lowlevel.update_carray(zoneprop) cstatus = _cxtgeo.grd3d_rpt_zlog_vs_zon( self._ncol, self._nrow, self._nlay, self._p_coord_v, self._p_zcorn_v, self._p_actnum_v, ptr_zprop, nval, ptr_xc, ptr_yc, ptr_zc, ptr_zo, zonelogrange[0], zonelogrange[1], onelayergrid._p_zcorn_v, onelayergrid._p_actnum_v, ptr_results, option, XTGDEBUG, ) _gridprop_lowlevel.delete_carray(zoneprop, ptr_zprop) if cstatus == 0: logger.debug("OK well") elif cstatus == 2: msg = "Well {} have no zonation?".format(well.wellname) warnings.warn(msg, UserWarning) else: msg = "Something is rotten with {}".format(well.wellname) raise SystemExit(msg) # extract the report perc = _cxtgeo.doublearray_getitem(ptr_results, 0) tpoi = _cxtgeo.doublearray_getitem(ptr_results, 1) mpoi = _cxtgeo.doublearray_getitem(ptr_results, 2) # returns percent match, then total numbers of well counts for zone, # then match count. perc = mpoi/tpoi return (perc, int(tpoi), int(mpoi))
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 _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
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_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_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 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 get_xyz(self, names=("X_UTME", "Y_UTMN", "Z_TVDSS"), asmasked=True): """Get X Y Z as properties... May be issues with asmasked vs activeonly here""" ntot = self.ntotal x = GridProperty( ncol=self._ncol, nrow=self._nrow, nlay=self._nlay, values=np.zeros(ntot, dtype=np.float64), name=names[0], discrete=False, ) y = GridProperty( ncol=self._ncol, nrow=self._nrow, nlay=self._nlay, values=np.zeros(ntot, dtype=np.float64), name=names[1], discrete=False, ) z = GridProperty( ncol=self._ncol, nrow=self._nrow, nlay=self._nlay, values=np.zeros(ntot, dtype=np.float64), name=names[2], discrete=False, ) ptr_x_v = _cxtgeo.new_doublearray(self.ntotal) ptr_y_v = _cxtgeo.new_doublearray(self.ntotal) ptr_z_v = _cxtgeo.new_doublearray(self.ntotal) option = 0 if asmasked: option = 1 _cxtgeo.grd3d_calc_xyz( self._ncol, self._nrow, self._nlay, self._p_coord_v, self._p_zcorn_v, self._p_actnum_v, ptr_x_v, ptr_y_v, ptr_z_v, option, XTGDEBUG, ) _gridprop_lowlevel.update_values_from_carray(x, ptr_x_v, np.float64, delete=True) _gridprop_lowlevel.update_values_from_carray(y, ptr_y_v, np.float64, delete=True) _gridprop_lowlevel.update_values_from_carray(z, ptr_z_v, np.float64, delete=True) # Note: C arrays are deleted in the update_values_from_carray() return x, y, z
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_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