Skip to content

Commit

Permalink
CLN: refaktor well class
Browse files Browse the repository at this point in the history
    Includes:
    - Adding ENUM types instead of hardcoding "DISC", "CONT"
    - Adding ENUM for xname, yname, ...
    - Reorganising modules, making public modules less code
    - A core class is _xyz_data.py in xyz, which can be re-used
      by Points and Polygons
    - Add type annotations, but not complete
    - Some linting and code improvements
  • Loading branch information
jcrivenaes committed Oct 25, 2023
1 parent ef1212a commit 9bb3ccf
Show file tree
Hide file tree
Showing 31 changed files with 1,769 additions and 947 deletions.
2 changes: 1 addition & 1 deletion src/xtgeo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ def _xprint(msg):
#
_xprint("Import matplotlib etc...DONE")

from xtgeo.common import XTGeoDialog
from xtgeo.common.constants import UNDEF, UNDEF_INT, UNDEF_INT_LIMIT, UNDEF_LIMIT
from xtgeo.common.exceptions import (
BlockedWellsNotFoundError,
Expand All @@ -89,7 +90,6 @@ def _xprint(msg):
WellNotFoundError,
)
from xtgeo.common.sys import _XTGeoFile
from xtgeo.common.xtgeo_dialog import XTGeoDialog
from xtgeo.cxtgeo._cxtgeo import XTGeoCLibError

_xprint("Import common... done")
Expand Down
15 changes: 15 additions & 0 deletions src/xtgeo/common/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,23 @@
"""The XTGeo common package"""


from xtgeo.common._xyz_enum import _AttrName, _AttrType, _XYZType
from xtgeo.common.exceptions import WellNotFoundError
from xtgeo.common.sys import _XTGeoFile, inherit_docstring

# flake8: noqa
from xtgeo.common.xtgeo_dialog import XTGDescription, XTGeoDialog, XTGShowProgress
from xtgeo.xyz._xyz_data import _XYZData

__all__ = [
"_XYZData",
"_AttrName",
"_AttrType",
"_XYZType",
"WellNotFoundError",
"_XTGeoFile",
"inherit_docstring",
"XTGDescription",
"XTGeoDialog",
"XTGShowProgress",
]
43 changes: 43 additions & 0 deletions src/xtgeo/common/_xyz_enum.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from enum import Enum, unique


# to be able to list all values in an easy manner e.g. _AttrName.list()
class ExtendedEnum(Enum):
@classmethod
def list(cls):
return list(map(lambda c: c.value, cls))


# default names of special column names
@unique
class _AttrName(ExtendedEnum):
XNAME = "X_UTME"
YNAME = "Y_UTMN"
ZNAME = "Z_TVDSS"
M_MD_NAME = "M_MDEPTH"
Q_MD_NAME = "Q_MDEPTH"
M_AZI_NAME = "M_AZI"
Q_AZI_NAME = "Q_AZI"
M_INCL_NAME = "M_INCL"
Q_INCL_NAME = "Q_INCL"
I_INDEX = "I_INDEX"
J_INDEX = "J_INDEX"
K_INDEX = "K_INDEX"
R_HLEN_NAME = "R_HLEN"


@unique
class _AttrType(ExtendedEnum):
"""Enumerate type of attribute/log"""

CONT = "CONT"
DISC = "DISC"


@unique
class _XYZType(ExtendedEnum):
"""Enumerate type of context"""

POINTS = 1
POLYGONS = 2 # ie. same here as PolyLines
WELL = 3
4 changes: 4 additions & 0 deletions src/xtgeo/common/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,7 @@

MAXKEYWORDS = cx.MAXKEYWORDS # maximum keywords for ECL and ROFF scanning
MAXDATES = cx.MAXDATES # maximum keywords for ECL scanning

# for XYZ data, restricted to float32 and int32
UNDEF_CONT = UNDEF
UNDEF_DISC = UNDEF_INT
60 changes: 60 additions & 0 deletions src/xtgeo/common/sys.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

from xtgeo.cxtgeo import _cxtgeo

from ._xyz_enum import _AttrType
from .xtgeo_dialog import XTGeoDialog

xtg = XTGeoDialog()
Expand Down Expand Up @@ -687,3 +688,62 @@ def decorator_set_docstring(func):
return func

return decorator_set_docstring


# ----------------------------------------------------------------------------------
# Special methods for nerds, to be removed when not appplied any more
# ----------------------------------------------------------------------------------


def _convert_np_carr_int(length, 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(length)

np_array = np_array.astype(np.int32)

_cxtgeo.swig_numpy_to_carr_i1d(np_array, carr)

return carr


def _convert_np_carr_double(length, np_array):
"""Convert numpy 1D array to C array, assuming double type."""
carr = _cxtgeo.new_doublearray(length)

_cxtgeo.swig_numpy_to_carr_1d(np_array, carr)

return carr


def _convert_carr_double_np(length, carray, nlen=None):
"""Convert a C array to numpy, assuming double type."""
if nlen is None:
nlen = length

nparray = _cxtgeo.swig_carr_to_numpy_1d(nlen, carray)

return nparray


def _get_carray(dataframe, attributes, attrname: str):
"""Returns the C array pointer (via SWIG) for a given attr.
Type conversion is double if float64, int32 if DISC attr.
Returns None if log does not exist.
"""
np_array = None
if attrname in dataframe:
np_array = dataframe[attrname].values
else:
return None

nlen = len(dataframe.index)
if attributes[attrname] == _AttrType.DISC.value:
carr = _convert_np_carr_int(nlen, np_array)
else:
carr = _convert_np_carr_double(nlen, np_array)

return carr
3 changes: 2 additions & 1 deletion src/xtgeo/grid3d/_grid_etc1.py
Original file line number Diff line number Diff line change
Expand Up @@ -839,7 +839,8 @@ def _get_geometrics_v2(self, allcells=False, cellcenter=True, return_dict=False)
y1 = ycor.values[self.ncol - 1, midcol, midlay]
glist.append(degrees(atan2(y1 - y0, x1 - x0)))

dx, dy = self.get_dxdy(asmasked=False)
dx = self.get_dx(asmasked=False)
dy = self.get_dy(asmasked=False)
dz = self.get_dz(asmasked=False)
glist.append(dx.values.mean())
glist.append(dy.values.mean())
Expand Down
8 changes: 5 additions & 3 deletions src/xtgeo/grid3d/_grid_wellzone.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,19 @@ def report_zone_mismatch(

# get the IJK along the well as logs; use a copy of the well instance
wll = well.copy()
wll._df[zonelogname] += zonelogshift
wll.dataframe[zonelogname] += zonelogshift

if depthrange:
d1, d2 = depthrange
wll._df = wll._df[(d1 < wll._df.Z_TVDSS) & (wll._df.Z_TVDSS < d2)]
wll.dataframe = wll.dataframe[
(d1 < wll.dataframe.Z_TVDSS) & (wll.dataframe.Z_TVDSS < d2)
]

wll.get_gridproperties(zoneprop, self)
zmodel = zoneprop.name + "_model"

# from here, work with the dataframe only
df = wll._df
df = wll.dataframe

# zonelogrange
z1, z2 = zonelogrange
Expand Down
63 changes: 36 additions & 27 deletions src/xtgeo/well/_blockedwell_roxapi.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,32 @@
# -*- coding: utf-8 -*-
"""Well input and output, private module for ROXAPI"""


from collections import OrderedDict
"""Blocked Well input and output, private module for ROXAPI"""

import numpy as np
import numpy.ma as npma
import pandas as pd

from xtgeo.common import XTGeoDialog
from xtgeo.common._xyz_enum import _AttrName, _AttrType
from xtgeo.common.exceptions import WellNotFoundError
from xtgeo.roxutils import RoxUtils

try:
import roxar
import roxar # type: ignore
except ImportError:
pass

xtg = XTGeoDialog()
logger = xtg.functionlogger(__name__)


# Import / export via ROX api
# Import / export via ROX/RMS api


def import_bwell_roxapi(
self, project, gname, bwname, wname, lognames=None, ijk=True, realisation=0
):
"""Private function for loading project and ROXAPI blockwell import"""

logger.info("Opening RMS project ...")
logger.debug("Opening RMS project ...")
rox = RoxUtils(project, readonly=True)

_roxapi_import_bwell(self, rox, gname, bwname, wname, lognames, ijk, realisation)
Expand All @@ -42,7 +39,7 @@ def export_bwell_roxapi(
):
"""Private function for blockwell export (store in RMS) from XTGeo to RoxarAPI"""

logger.info("Opening RMS project ...")
logger.debug("Opening RMS project ...")
rox = RoxUtils(project, readonly=False)

_roxapi_export_bwell(self, rox, gname, bwname, wname, lognames, ijk, realisation)
Expand All @@ -60,13 +57,13 @@ def _roxapi_import_bwell(

if gname in rox.project.grid_models:
gmodel = rox.project.grid_models[gname]
logger.info("RMS grid model <%s> OK", gname)
logger.debug("RMS grid model <%s> OK", gname)
else:
raise ValueError(f"No such grid name present: {gname}")

if bwname in gmodel.blocked_wells_set:
bwset = gmodel.blocked_wells_set[bwname]
logger.info("Blocked well set <%s> OK", bwname)
logger.debug("Blocked well set <%s> OK", bwname)
else:
raise ValueError(f"No such blocked well set: {bwname}")

Expand All @@ -85,17 +82,18 @@ def _roxapi_import_bwell(
cind = bw_cellindices[dind]
xyz = np.transpose(gmodel.get_grid(realisation=realisation).get_cell_centers(cind))

logs = OrderedDict()
logs["X_UTME"] = xyz[0].astype(np.float64)
logs["Y_UTMN"] = xyz[1].astype(np.float64)
logs["Z_TVDSS"] = xyz[2].astype(np.float64)
logs = {}
logs[_AttrName.XNAME.value] = xyz[0].astype(np.float64)
logs[_AttrName.YNAME.value] = xyz[1].astype(np.float64)
logs[_AttrName.ZNAME.value] = xyz[2].astype(np.float64)

if ijk:
ijk = np.transpose(
gmodel.get_grid(realisation=realisation).grid_indexer.get_indices(cind)
)
logs["I_INDEX"] = ijk[0].astype(np.float64)
logs["J_INDEX"] = ijk[1].astype(np.float64)
logs["K_INDEX"] = ijk[2].astype(np.float64)
logs[_AttrName.I_INDEX.value] = ijk[0].astype(np.float64)
logs[_AttrName.J_INDEX.value] = ijk[1].astype(np.float64)
logs[_AttrName.K_INDEX.value] = ijk[2].astype(np.float64)

usenames = []
if lognames and lognames == "all":
Expand All @@ -112,11 +110,11 @@ def _roxapi_import_bwell(
tmplog = npma.filled(tmplog, fill_value=np.nan)
tmplog[tmplog == -999] = np.nan
if "discrete" in str(bwprop.type):
self._wlogtypes[lname] = "DISC"
self._wlogrecords[lname] = bwprop.code_names
self.set_logtype(lname, _AttrType.DISC.value)
self.set_logrecord(lname, bwprop.code_names)
else:
self._wlogtypes[lname] = "CONT"
self._wlogrecords[lname] = None
self.set_logtype(lname, _AttrType.CONT.value)
self.set_logrecord(lname, None)

logs[lname] = tmplog

Expand All @@ -133,7 +131,10 @@ def _roxapi_import_bwell(
self._xpos, self._ypos = rox.project.wells[wname].wellhead
else:
self._rkb = None
self._xpos, self._ypos = self._df["X_UTME"][0], self._df["Y_UTMN"][0]
self._xpos, self._ypos = (
self._df[_AttrName.XNAME][0],
self._df[_AttrName.YNAME][0],
)


def _roxapi_export_bwell(
Expand All @@ -143,13 +144,13 @@ def _roxapi_export_bwell(

if gname in rox.project.grid_models:
gmodel = rox.project.grid_models[gname]
logger.info("RMS grid model <%s> OK", gname)
logger.debug("RMS grid model <%s> OK", gname)
else:
raise ValueError(f"No such grid name present: {gname}")

if bwname in gmodel.blocked_wells_set:
bwset = gmodel.blocked_wells_set[bwname]
logger.info("Blocked well set <%s> OK", bwname)
logger.debug("Blocked well set <%s> OK", bwname)
bwprops = bwset.properties
else:
raise ValueError(f"No such blocked well set: {bwname}")
Expand All @@ -165,14 +166,22 @@ def _roxapi_export_bwell(
dind = bwset.get_data_indices([self._wname], realisation=realisation)

for lname in self.lognames:
if not ijk and "_INDEX" in lname:
if (
not ijk
and any(
_AttrName.I_INDEX.value,
_AttrName.J_INDEX.value,
_AttrName.K_INDEX.value,
)
in lname
):
continue

if lognames != "all" and lname not in lognames:
continue

if lname not in bwnames:
if self._wlogtypes[lname] == "CONT":
if self._wlogtypes[lname] == _AttrType.CONT.value:
print("Create CONT", lname, "for", wname)
bwlog = bwset.properties.create(
lname, roxar.GridPropertyType.continuous, np.float32
Expand Down
12 changes: 6 additions & 6 deletions src/xtgeo/well/_blockedwells_roxapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def import_bwells_roxapi(
): # pragma: no cover
"""Private function for loading project and ROXAPI blockwell import"""

logger.info("Opening RMS project ...")
logger.debug("Opening RMS project ...")
rox = RoxUtils(project, readonly=True)

_roxapi_import_bwells(self, rox, gname, bwname, lognames, ijk, realisation)
Expand All @@ -33,24 +33,24 @@ def _roxapi_import_bwells(

if gname in rox.project.grid_models:
gmodel = rox.project.grid_models[gname]
logger.info("RMS grid model <%s> OK", gname)
logger.debug("RMS grid model <%s> OK", gname)
else:
raise ValueError(f"No such grid name present: {gname}")

if bwname in gmodel.blocked_wells_set:
bwset = gmodel.blocked_wells_set[bwname]
logger.info("Blocked well set <%s> OK", bwname)
logger.debug("Blocked well set <%s> OK", bwname)
else:
raise ValueError(f"No such blocked well set: {bwname}")

wnames = bwset.get_well_names()

logger.info("Lognames are %s", lnames)
logger.debug("Lognames are %s", lnames)

bwlist = []
logger.info("Loading wells ...")
logger.debug("Loading wells ...")
for wname in wnames:
logger.info("Loading well %s", wname)
logger.debug("Loading well %s", wname)
bwtmp = BlockedWell()
bwtmp.from_roxar(
rox.project,
Expand Down
Loading

0 comments on commit 9bb3ccf

Please sign in to comment.