def _import_eclbinary_checks2(kwlist, name, etype, date): """More checks, and returns what's needed for actual import""" datefound = True kwfound = False datefoundhere = False usedate = "0" restart = False kwname = "unset" kwlen = 0 kwtype = "unset" kwbyte = 0 if etype == 5: usedate = str(date) restart = True kwxlist = list(kwlist.itertuples(index=False, name=None)) for kwitem in kwxlist: kwname, kwtype, kwlen, kwbyte, kwdate = kwitem logger.debug("Keyword %s - date: %s usedate: %s", kwname, kwdate, usedate) if name == kwname: kwfound = True if name == kwname and usedate == str(kwdate): logger.info("Keyword %s ok at date %s", name, usedate) kwname, kwtype, kwlen, kwbyte, kwdate = kwitem datefoundhere = True break if restart: if datefound and not kwfound: msg = "Date <{}> is found, but not keyword <{}>".format(date, name) xtg.warn(msg) raise xtgeo.KeywordNotFoundError(msg) if not datefoundhere and kwfound: msg = "The keyword <{}> exists but not for " "date <{}>".format( name, date) xtg.warn(msg) raise xtgeo.KeywordFoundNoDateError(msg) else: if not kwfound: msg = "The keyword <{}> is not found".format(name) xtg.warn(msg) raise xtgeo.KeywordNotFoundError(msg) return kwname, kwlen, kwtype, kwbyte
def read_grdecl_3d_property(filename, keyword, dimensions, dtype=float): """ Read a 3d grid property from a grdecl file, see open_grdecl for description of format. Args: filename (pathlib.Path or str): File in grdecl format. keyword (str): The keyword of the property in the file dimensions ((int,int,int)): Triple of the size of grid. dtype (function): The datatype to be read, ie., float. Raises: xtgeo.KeywordNotFoundError: If keyword is not found in the file. Returns: numpy array with given dimensions and data type read from the grdecl file. """ result = None with open_grdecl(filename, keywords=[], simple_keywords=(keyword, )) as kw_generator: try: _, result = next(kw_generator) except StopIteration as si: raise xtgeo.KeywordNotFoundError( f"Cannot import {keyword}, not present in file {filename}?" ) from si # The values are stored in F order in the grdecl file f_order_values = np.array([dtype(v) for v in result]) return np.ascontiguousarray(f_order_values.reshape(dimensions, order="F"))
def import_bgrdecl_prop(self, pfile, name="unknown", grid=None): """Import property from binary files with GRDECL layout""" local_fhandle = False fhandle = pfile if isinstance(pfile, str): local_fhandle = True pfile = xtgeo._XTGeoCFile(pfile) fhandle = pfile.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 = {} bpos[name] = -1 for kwitem in kwlist: kwname, kwtype, kwlen, kwbyte = kwitem logger.info("KWITEM: %s", kwitem) if name == kwname: bpos[name] = kwbyte break if bpos[name] == -1: raise xtgeo.KeywordNotFoundError( "Cannot find property name {} in file {}".format(name, pfile)) self._ncol = grid.ncol self._nrow = grid.nrow self._nlay = grid.nlay values = _eclbin.eclbin_record(fhandle, kwname, kwlen, kwtype, kwbyte) if kwtype == "INTE": self._isdiscrete = True # make the code list uniq = np.unique(values).tolist() codes = dict(zip(uniq, uniq)) codes = {key: str(val) for key, val in codes.items()} # val: strings self.codes = codes else: self._isdiscrete = False values = values.astype(np.float64) # cast REAL (float32) to float64 self.codes = {} # property arrays from binary GRDECL will be for all cells, but they # are in Fortran order, so need to convert... actnum = grid.get_actnum().values allvalues = values.reshape(self.dimensions, order="F") allvalues = np.asanyarray(allvalues, order="C") allvalues = ma.masked_where(actnum < 1, allvalues) self.values = allvalues self._name = name if local_fhandle and not pfile.close(cond=local_fhandle): raise RuntimeError("Error in file handling; cannot close file")
def import_grdecl_prop(self, pfile, name="unknown", grid=None): """Read a GRDECL ASCII property record""" if grid is None: raise ValueError("A grid instance is required as argument") self._ncol = grid.ncol self._nrow = grid.nrow self._nlay = grid.nlay self._name = name self._filesrc = pfile actnumv = grid.get_actnum().values # This requires that the Python part clean up comments # etc, and make a tmp file. # make a temporary file fds, tmpfile = mkstemp() # make a temporary with open(pfile) 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() # now read the property nlen = self._ncol * self._nrow * self._nlay ier, values = _cxtgeo.grd3d_import_grdecl_prop(tmpfile, self._ncol, self._nrow, self._nlay, name, nlen, 0, XTGDEBUG) # remove tmpfile os.close(fds) os.remove(tmpfile) if ier != 0: raise xtgeo.KeywordNotFoundError( "Cannot import {}, not present in file {}?".format(name, pfile)) self.values = values.reshape(self.dimensions) self.values = ma.masked_equal(self.values, actnumv == 0) return 0
def from_file(self, pfile, fformat=None, name="unknown", grid=None, date=None, _roffapiv=1): # _roffapiv for devel. """Import grid property from file, and makes an instance of this.""" # pylint: disable=too-many-branches, too-many-statements self._filesrc = pfile # it may be that pfile already is an open file; hence a filehandle # instead. Check for this, and skip tests if so pfile_is_not_fhandle = True _fhandle, pclose = _get_fhandle(pfile) if not pclose: pfile_is_not_fhandle = False if pfile_is_not_fhandle: if os.path.isfile(pfile): logger.debug("File %s exists OK", pfile) else: raise IOError("No such file: {}".format(pfile)) # work on file extension _froot, fext = os.path.splitext(pfile) if fformat is None or fformat == "guess": if not fext: raise ValueError("File extension missing. STOP") fformat = fext.lower().replace(".", "") logger.debug("File name to be used is %s", pfile) logger.debug("File format is %s", fformat) ier = 0 if fformat == "roff": logger.info("Importing ROFF...") ier = import_roff(self, pfile, name, grid=grid, _roffapiv=_roffapiv) elif fformat.lower() == "init": ier = import_eclbinary(self, pfile, name=name, etype=1, date=None, grid=grid) elif fformat.lower() == "unrst": if date is None: raise ValueError("Restart file, but no date is given") if isinstance(date, str): if "-" in date: date = int(date.replace("-", "")) elif date == "first": date = 0 elif date == "last": date = 9 else: date = int(date) if not isinstance(date, int): raise RuntimeError("Date is not int format") ier = import_eclbinary(self, pfile, name=name, etype=5, date=date, grid=grid) elif fformat.lower() == "grdecl": ier = import_grdecl_prop(self, pfile, name=name, grid=grid) elif fformat.lower() == "bgrdecl": ier = import_bgrdecl_prop(self, pfile, name=name, grid=grid) else: logger.warning("Invalid file format") raise SystemExit("Invalid file format") # if grid, then append this gridprop to the current grid object if ier == 0: if grid: grid.append_prop(self) elif ier == 22: raise xtgeo.DateNotFoundError( "Date {} not found when importing {}".format(date, name)) elif ier == 23: raise xtgeo.KeywordNotFoundError( "Keyword {} not found for date {} when importing".format( name, date)) elif ier == 24: raise xtgeo.KeywordFoundNoDateError( "Keyword {} found but not for date " "{} when importing".format(name, date)) elif ier == 25: raise xtgeo.KeywordNotFoundError( "Keyword {} not found when importing".format(name)) else: raise RuntimeError("Something went wrong, code {}".format(ier)) return self
def _import_eclbinary(self, pfile, name=None, etype=1, date=None, grid=None): """Import, private to this routine. Raises: DateNotFoundError: If restart do not contain requested date. KeywordFoundNoDateError: If keyword is found but not at given date. KeywordNotFoundError: If Keyword is not found. RuntimeError: Mismatch in grid vs property, etc. """ # This function requires simplification! # pylint: disable=too-many-locals # pylint: disable=too-many-branches # pylint: disable=too-many-statements fhandle, pclose = _get_fhandle(pfile) nentry = 0 datefound = True if etype == 5: datefound = False logger.info("Look for date %s", date) # scan for date and find SEQNUM entry number dtlist = utils.scan_dates(fhandle) if date == 0: date = dtlist[0][1] elif date == 9: date = dtlist[-1][1] logger.info("Redefined date is %s", date) for ientry, dtentry in enumerate(dtlist): if str(dtentry[1]) == str(date): datefound = True nentry = ientry break if not datefound: msg = "In {}: Date {} not found, nentry={}".format( pfile, date, nentry) xtg.warn(msg) raise xtgeo.DateNotFoundError(msg) # scan file for property logger.info("Make kwlist") kwlist = utils.scan_keywords(fhandle, fformat="xecl", maxkeys=100000, dataframe=False, dates=True) # first INTEHEAD is needed to verify grid dimensions: for kwitem in kwlist: if kwitem[0] == "INTEHEAD": kwname, kwtype, kwlen, kwbyte, kwdate = kwitem break # read INTEHEAD record: intehead = eclbin_record(fhandle, kwname, kwlen, kwtype, kwbyte) ncol, nrow, nlay = intehead[8:11].tolist() self._ncol = ncol self._nrow = nrow self._nlay = nlay logger.info("Grid dimensions in INIT or RESTART file: %s %s %s", ncol, nrow, nlay) logger.info("Grid dimensions from GRID file: %s %s %s", grid.ncol, grid.nrow, grid.nlay) if grid.ncol != ncol or grid.nrow != nrow or grid.nlay != nlay: msg = "In {}: Errors in dimensions prop: {} {} {} vs grid: {} {} {} ".format( pfile, ncol, nrow, nlay, grid.ncol, grid.ncol, grid.nlay) raise RuntimeError(msg) # Restarts (etype == 5): # there are cases where keywords do not exist for all dates, e.g .'RV'. # The trick is to check for dates also... kwfound = False datefoundhere = False usedate = "0" restart = False if etype == 5: usedate = str(date) restart = True for kwitem in kwlist: kwname, kwtype, kwlen, kwbyte, kwdate = kwitem logger.debug("Keyword %s - date: %s usedate: %s", kwname, kwdate, usedate) if name == kwname: kwfound = True if name == kwname and usedate == str(kwdate): logger.info("Keyword %s ok at date %s", name, usedate) kwname, kwtype, kwlen, kwbyte, kwdate = kwitem datefoundhere = True break if restart: if datefound and not kwfound: msg = "For {}: Date <{}> is found, but not keyword <{}>".format( pfile, date, name) xtg.warn(msg) raise xtgeo.KeywordNotFoundError(msg) if not datefoundhere and kwfound: msg = "For {}: The keyword <{}> exists but not for " "date <{}>".format( pfile, name, date) xtg.warn(msg) raise xtgeo.KeywordFoundNoDateError(msg) else: if not kwfound: msg = "For {}: The keyword <{}> is not found".format(pfile, name) xtg.warn(msg) raise xtgeo.KeywordNotFoundError(msg) # read record: values = eclbin_record(fhandle, kwname, kwlen, kwtype, kwbyte) if kwtype == "INTE": self._isdiscrete = True use_undef = xtgeo.UNDEF_INT # make the code list uniq = np.unique(values).tolist() codes = dict(zip(uniq, uniq)) codes = {key: str(val) for key, val in codes.items()} # val: strings self.codes = codes else: self._isdiscrete = False values = values.astype(np.float64) # cast REAL (float32) to float64 use_undef = xtgeo.UNDEF self.codes = {} # arrays from Eclipse INIT or UNRST are usually for inactive values only. # Use the ACTNUM index array for vectorized numpy remapping actnum = grid.get_actnum().values allvalues = np.zeros((ncol * nrow * nlay), dtype=values.dtype) + use_undef msg = "\n" msg = msg + "grid.actnum_indices.shape[0] = {}\n".format( grid.actnum_indices.shape[0]) msg = msg + "values.shape[0] = {}\n".format(values.shape[0]) msg = msg + "ncol nrow nlay {} {} {}, nrow*nrow*nlay = {}\n".format( ncol, nrow, nlay, ncol * nrow * nlay) logger.info(msg) if grid.actnum_indices.shape[0] == values.shape[0]: allvalues[grid.get_actnum_indices(order="F")] = values elif values.shape[0] == ncol * nrow * nlay: # often case for PORV array allvalues = values.copy() else: msg = ("BUG somehow... Is the file corrupt? If not contact " "the library developer(s)!\n" + msg) raise SystemExit(msg) allvalues = allvalues.reshape((ncol, nrow, nlay), order="F") allvalues = np.asanyarray(allvalues, order="C") allvalues = ma.masked_where(actnum < 1, allvalues) _close_fhandle(fhandle, pclose) self._values = allvalues if etype == 1: self._name = name else: self._name = name + "_" + str(date) self._date = date return 0