xtgeo 4.10.1__cp312-cp312-win_amd64.whl → 4.12.0__cp312-cp312-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of xtgeo might be problematic. Click here for more details.
- xtgeo/_cxtgeo.cp312-win_amd64.pyd +0 -0
- xtgeo/_internal.cp312-win_amd64.pyd +0 -0
- xtgeo/common/version.py +16 -3
- xtgeo/cube/_cube_window_attributes.py +13 -4
- xtgeo/grid3d/_grdecl_format.py +43 -0
- xtgeo/grid3d/_grdecl_grid.py +24 -12
- xtgeo/grid3d/_grid_etc1.py +25 -9
- xtgeo/grid3d/_grid_export.py +4 -3
- xtgeo/grid3d/_gridprop_export.py +34 -12
- xtgeo/grid3d/_gridprop_op1.py +74 -8
- xtgeo/grid3d/_gridprop_roxapi.py +81 -25
- xtgeo/grid3d/grid.py +60 -18
- xtgeo/grid3d/grid_property.py +5 -27
- xtgeo/metadata/metadata.py +20 -13
- {xtgeo-4.10.1.dist-info → xtgeo-4.12.0.dist-info}/METADATA +3 -4
- {xtgeo-4.10.1.dist-info → xtgeo-4.12.0.dist-info}/RECORD +18 -18
- {xtgeo-4.10.1.dist-info → xtgeo-4.12.0.dist-info}/WHEEL +1 -1
- {xtgeo-4.10.1.dist-info → xtgeo-4.12.0.dist-info}/licenses/LICENSE.md +0 -0
|
Binary file
|
|
Binary file
|
xtgeo/common/version.py
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
# file generated by setuptools-scm
|
|
2
2
|
# don't change, don't track in version control
|
|
3
3
|
|
|
4
|
-
__all__ = [
|
|
4
|
+
__all__ = [
|
|
5
|
+
"__version__",
|
|
6
|
+
"__version_tuple__",
|
|
7
|
+
"version",
|
|
8
|
+
"version_tuple",
|
|
9
|
+
"__commit_id__",
|
|
10
|
+
"commit_id",
|
|
11
|
+
]
|
|
5
12
|
|
|
6
13
|
TYPE_CHECKING = False
|
|
7
14
|
if TYPE_CHECKING:
|
|
@@ -9,13 +16,19 @@ if TYPE_CHECKING:
|
|
|
9
16
|
from typing import Union
|
|
10
17
|
|
|
11
18
|
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
19
|
+
COMMIT_ID = Union[str, None]
|
|
12
20
|
else:
|
|
13
21
|
VERSION_TUPLE = object
|
|
22
|
+
COMMIT_ID = object
|
|
14
23
|
|
|
15
24
|
version: str
|
|
16
25
|
__version__: str
|
|
17
26
|
__version_tuple__: VERSION_TUPLE
|
|
18
27
|
version_tuple: VERSION_TUPLE
|
|
28
|
+
commit_id: COMMIT_ID
|
|
29
|
+
__commit_id__: COMMIT_ID
|
|
19
30
|
|
|
20
|
-
__version__ = version = '4.
|
|
21
|
-
__version_tuple__ = version_tuple = (4,
|
|
31
|
+
__version__ = version = '4.12.0'
|
|
32
|
+
__version_tuple__ = version_tuple = (4, 12, 0)
|
|
33
|
+
|
|
34
|
+
__commit_id__ = commit_id = 'g4f136d221'
|
|
@@ -63,6 +63,7 @@ class CubeAttrs:
|
|
|
63
63
|
_upper: RegularSurface | None = None # upper surf, resampled to cube map resolution
|
|
64
64
|
_lower: RegularSurface | None = None # lower surf, resampled to cube map resolution
|
|
65
65
|
_min_thickness_mask: RegularSurface | None = None # mask for min. thickness trunc.
|
|
66
|
+
_mask_map_by_traceidcode: RegularSurface | None = None # mask for traceidcode 2
|
|
66
67
|
|
|
67
68
|
_result_attr_maps: dict = field(default_factory=dict) # holds the resulting maps
|
|
68
69
|
|
|
@@ -94,6 +95,7 @@ class CubeAttrs:
|
|
|
94
95
|
else self.lower_surface
|
|
95
96
|
)
|
|
96
97
|
|
|
98
|
+
# the template surface is the topology that defines the resulting attribute maps
|
|
97
99
|
self._template_surface = (
|
|
98
100
|
upper
|
|
99
101
|
if isinstance(self.upper_surface, (float, int))
|
|
@@ -184,7 +186,7 @@ class CubeAttrs:
|
|
|
184
186
|
from xtgeo import Cube # avoid circular import by having this here
|
|
185
187
|
|
|
186
188
|
cubev = self.cube.values.copy() # copy, so we don't change the input instance
|
|
187
|
-
cubev[self.cube.traceidcodes == 2] =
|
|
189
|
+
cubev[self.cube.traceidcodes == 2] = 0.0 # set traceidcode 2 to zero
|
|
188
190
|
|
|
189
191
|
# Create a boolean mask based on the threshold
|
|
190
192
|
mask = self._depth_array < self._outside_depth
|
|
@@ -311,13 +313,20 @@ class CubeAttrs:
|
|
|
311
313
|
attr_map = self._upper.copy()
|
|
312
314
|
attr_map.values = np.ma.masked_invalid(values)
|
|
313
315
|
|
|
316
|
+
# apply mask for the cube's dead traces (traceidcode 2)
|
|
317
|
+
attr_map.values = np.ma.masked_where(
|
|
318
|
+
self.cube.traceidcodes == 2, attr_map.values
|
|
319
|
+
)
|
|
320
|
+
|
|
314
321
|
# now resample to the original input map
|
|
315
322
|
attr_map_resampled = self._template_surface.copy()
|
|
316
323
|
attr_map_resampled.resample(attr_map)
|
|
317
324
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
325
|
+
# Use template_surface consistently for masking (it's already set correctly)
|
|
326
|
+
if hasattr(self._template_surface.values, "mask"):
|
|
327
|
+
attr_map_resampled.values = np.ma.masked_where(
|
|
328
|
+
self._template_surface.values.mask, attr_map_resampled.values
|
|
329
|
+
)
|
|
321
330
|
|
|
322
331
|
self._result_attr_maps[attr_name] = attr_map_resampled
|
|
323
332
|
|
xtgeo/grid3d/_grdecl_format.py
CHANGED
|
@@ -4,6 +4,8 @@ import warnings
|
|
|
4
4
|
from contextlib import contextmanager
|
|
5
5
|
from typing import TYPE_CHECKING
|
|
6
6
|
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
7
9
|
from xtgeo.common import null_logger
|
|
8
10
|
|
|
9
11
|
if TYPE_CHECKING:
|
|
@@ -15,6 +17,47 @@ if TYPE_CHECKING:
|
|
|
15
17
|
logger = null_logger(__name__)
|
|
16
18
|
|
|
17
19
|
|
|
20
|
+
def run_length_encoding(arr: np.ndarray) -> tuple[np.ndarray, np.ndarray]:
|
|
21
|
+
"""
|
|
22
|
+
Perform run-length encoding on a 1D NumPy array.
|
|
23
|
+
|
|
24
|
+
Run-length encoding is a data compression technique that represents
|
|
25
|
+
consecutive repeated values as a single value and its count.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
arr (np.ndarray): A 1D NumPy array to be encoded.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
tuple[np.ndarray, np.ndarray]: A tuple containing two 1D NumPy arrays:
|
|
32
|
+
- counts (np.ndarray): An array of counts representing the number
|
|
33
|
+
of consecutive occurrences of each unique value.
|
|
34
|
+
- values (np.ndarray): An array of the unique values corresponding to
|
|
35
|
+
the counts.
|
|
36
|
+
|
|
37
|
+
Examples:
|
|
38
|
+
>>> import numpy as np
|
|
39
|
+
>>> arr = np.array([1, 1, 2, 2, 2, 3, 3, 1, 1, 1])
|
|
40
|
+
>>> counts, values = run_length_encoding(arr)
|
|
41
|
+
>>> print(counts)
|
|
42
|
+
[2 3 2 3]
|
|
43
|
+
>>> print(values)
|
|
44
|
+
[1 2 3 1]
|
|
45
|
+
|
|
46
|
+
>>> arr = np.array([5, 5, 5, 5, 5])
|
|
47
|
+
>>> counts, values = run_length_encoding(arr)
|
|
48
|
+
>>> print(counts)
|
|
49
|
+
[5]
|
|
50
|
+
>>> print(values)
|
|
51
|
+
[5]
|
|
52
|
+
|
|
53
|
+
"""
|
|
54
|
+
change_indices = np.where(~np.isclose(arr[:-1], arr[1:]))[0] + 1
|
|
55
|
+
counts = np.diff(np.concatenate(([0], change_indices, [len(arr)])))
|
|
56
|
+
values = arr[np.concatenate(([0], change_indices))]
|
|
57
|
+
|
|
58
|
+
return counts, values
|
|
59
|
+
|
|
60
|
+
|
|
18
61
|
def split_line(line: str) -> Generator[str, None, None]:
|
|
19
62
|
"""
|
|
20
63
|
split a keyword line inside a grdecl file. This splits the values of a
|
xtgeo/grid3d/_grdecl_grid.py
CHANGED
|
@@ -41,7 +41,7 @@ from ._ecl_grid import (
|
|
|
41
41
|
MapAxes,
|
|
42
42
|
Units,
|
|
43
43
|
)
|
|
44
|
-
from ._grdecl_format import IGNORE_ALL, open_grdecl
|
|
44
|
+
from ._grdecl_format import IGNORE_ALL, open_grdecl, run_length_encoding
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
@dataclass
|
|
@@ -311,20 +311,22 @@ class GrdeclGrid(EclGrid):
|
|
|
311
311
|
results[kw.lower()] = factory(values)
|
|
312
312
|
return cls(**results)
|
|
313
313
|
|
|
314
|
-
def to_file(self, filename, fileformat="grdecl"):
|
|
314
|
+
def to_file(self, filename, fileformat="grdecl", rle: bool = False):
|
|
315
315
|
"""
|
|
316
316
|
write the grdeclgrid to a file.
|
|
317
317
|
:param filename: path to file to write.
|
|
318
318
|
:param fileformat: Either "grdecl" or "bgrdecl" to
|
|
319
319
|
indicate binary or ascii format.
|
|
320
|
+
:param rle: Boolean flag indicating whether to use Run-Length Encoding (RLE)
|
|
321
|
+
compression when writing the file (only for grdecl).
|
|
320
322
|
"""
|
|
321
323
|
if fileformat == "grdecl":
|
|
322
|
-
return self._to_grdecl_file(filename)
|
|
324
|
+
return self._to_grdecl_file(filename, rle)
|
|
323
325
|
if fileformat == "bgrdecl":
|
|
324
326
|
return self._to_bgrdecl_file(filename)
|
|
325
327
|
raise ValueError(b"Unknown grdecl file format {fileformat}")
|
|
326
328
|
|
|
327
|
-
def _to_grdecl_file(self, filename):
|
|
329
|
+
def _to_grdecl_file(self, filename, rle):
|
|
328
330
|
with open(filename, "w") as filestream:
|
|
329
331
|
keywords = [
|
|
330
332
|
("SPECGRID", self.specgrid.to_grdecl()),
|
|
@@ -340,14 +342,24 @@ class GrdeclGrid(EclGrid):
|
|
|
340
342
|
if values is None:
|
|
341
343
|
continue
|
|
342
344
|
filestream.write(f"{kw}\n")
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
345
|
+
if rle and (kw == "ACTNUM"):
|
|
346
|
+
counts, unique_values = run_length_encoding(values)
|
|
347
|
+
for i, (count, unique_value) in enumerate(
|
|
348
|
+
zip(counts, unique_values)
|
|
349
|
+
):
|
|
350
|
+
filestream.write(
|
|
351
|
+
f" {count}*{unique_value}"
|
|
352
|
+
if count > 1
|
|
353
|
+
else f" {unique_value}"
|
|
354
|
+
)
|
|
355
|
+
if i % 6 == 5:
|
|
356
|
+
filestream.write("\n")
|
|
357
|
+
else:
|
|
358
|
+
for i, value in enumerate(values):
|
|
359
|
+
filestream.write(f" {value}")
|
|
360
|
+
|
|
361
|
+
if i % 6 == 5: # 6 should ensure < 128 character width total
|
|
362
|
+
filestream.write("\n")
|
|
351
363
|
|
|
352
364
|
filestream.write("\n /\n")
|
|
353
365
|
|
xtgeo/grid3d/_grid_etc1.py
CHANGED
|
@@ -309,23 +309,34 @@ def get_heights_above_ffl(
|
|
|
309
309
|
grid: Grid,
|
|
310
310
|
ffl: GridProperty,
|
|
311
311
|
option: Literal[
|
|
312
|
-
"cell_center_above_ffl",
|
|
312
|
+
"cell_center_above_ffl",
|
|
313
|
+
"cell_corners_above_ffl",
|
|
314
|
+
"truncated_cell_corners_above_ffl",
|
|
313
315
|
] = "cell_center_above_ffl",
|
|
314
316
|
) -> tuple[GridProperty, GridProperty, GridProperty]:
|
|
315
317
|
"""Compute delta heights for cell top, bottom and midpoints above a given level."""
|
|
316
318
|
|
|
317
|
-
valid_options = (
|
|
319
|
+
valid_options = (
|
|
320
|
+
"cell_center_above_ffl",
|
|
321
|
+
"cell_corners_above_ffl",
|
|
322
|
+
"truncated_cell_corners_above_ffl",
|
|
323
|
+
)
|
|
318
324
|
if option not in valid_options:
|
|
319
325
|
raise ValueError(
|
|
320
326
|
f"The option key <{option}> is invalid, must be one of {valid_options}"
|
|
321
327
|
)
|
|
328
|
+
if option == "cell_center_above_ffl":
|
|
329
|
+
option_flag = _internal.grid3d.HeightAboveFFLOption.CellCenter
|
|
330
|
+
elif option == "cell_corners_above_ffl":
|
|
331
|
+
option_flag = _internal.grid3d.HeightAboveFFLOption.CellCorners
|
|
332
|
+
else:
|
|
333
|
+
option_flag = _internal.grid3d.HeightAboveFFLOption.TruncatedCellCorners
|
|
322
334
|
|
|
323
335
|
grid._set_xtgformat2()
|
|
324
336
|
|
|
325
337
|
grid_cpp = grid._get_grid_cpp()
|
|
326
338
|
htop_arr, hbot_arr, hmid_arr = grid_cpp.get_height_above_ffl(
|
|
327
|
-
ffl.values.ravel(),
|
|
328
|
-
1 if option == "cell_center_above_ffl" else 2,
|
|
339
|
+
ffl.values.ravel(), option_flag
|
|
329
340
|
)
|
|
330
341
|
|
|
331
342
|
htop = GridProperty(
|
|
@@ -1053,13 +1064,18 @@ def inactivate_inside(
|
|
|
1053
1064
|
raise RuntimeError("Problems with one or more polygons. Not closed?")
|
|
1054
1065
|
|
|
1055
1066
|
|
|
1056
|
-
def collapse_inactive_cells(self: Grid) -> None:
|
|
1067
|
+
def collapse_inactive_cells(self: Grid, internal: bool = True) -> None:
|
|
1057
1068
|
"""Collapse inactive cells."""
|
|
1058
|
-
|
|
1069
|
+
logger.debug("Collapsing inactive cells...")
|
|
1070
|
+
self._set_xtgformat2()
|
|
1059
1071
|
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1072
|
+
grid_cpp = _internal.grid3d.Grid(self)
|
|
1073
|
+
new_zcornsv = grid_cpp.collapse_inactive_cells(collapse_internal=internal)
|
|
1074
|
+
|
|
1075
|
+
if new_zcornsv is not None:
|
|
1076
|
+
self._zcornsv = new_zcornsv
|
|
1077
|
+
|
|
1078
|
+
logger.debug("Collapsing inactive cells done")
|
|
1063
1079
|
|
|
1064
1080
|
|
|
1065
1081
|
def copy(self: Grid) -> Grid:
|
xtgeo/grid3d/_grid_export.py
CHANGED
|
@@ -49,10 +49,11 @@ def export_roff(
|
|
|
49
49
|
)
|
|
50
50
|
|
|
51
51
|
|
|
52
|
-
def export_grdecl(grid: Grid, gfile: FileLike, mode: int) -> None:
|
|
53
|
-
"""Export grid to Eclipse GRDECL format (ascii, mode=1) or binary (mode=0).
|
|
52
|
+
def export_grdecl(grid: Grid, gfile: FileLike, mode: int, rle: bool = False) -> None:
|
|
53
|
+
"""Export grid to Eclipse GRDECL format (ascii, mode=1) or binary (mode=0).
|
|
54
|
+
Optionally for ASCII GRDECL, run-length encoding RLE can be applied"""
|
|
54
55
|
fileformat = "grdecl" if mode == 1 else "bgrdecl"
|
|
55
|
-
GrdeclGrid.from_xtgeo_grid(grid).to_file(gfile, fileformat=fileformat)
|
|
56
|
+
GrdeclGrid.from_xtgeo_grid(grid).to_file(gfile, fileformat=fileformat, rle=rle)
|
|
56
57
|
|
|
57
58
|
|
|
58
59
|
def export_egrid(grid: Grid, gfile: FileLike) -> None:
|
xtgeo/grid3d/_gridprop_export.py
CHANGED
|
@@ -17,6 +17,7 @@ from xtgeo.common.exceptions import InvalidFileFormatError
|
|
|
17
17
|
from xtgeo.common.log import null_logger
|
|
18
18
|
from xtgeo.io._file import FileFormat, FileWrapper
|
|
19
19
|
|
|
20
|
+
from ._grdecl_format import run_length_encoding
|
|
20
21
|
from ._roff_parameter import RoffParameter
|
|
21
22
|
|
|
22
23
|
if TYPE_CHECKING:
|
|
@@ -37,6 +38,7 @@ def to_file(
|
|
|
37
38
|
append: bool = False,
|
|
38
39
|
dtype: type[np.float32] | type[np.float64] | type[np.int32] | None = None,
|
|
39
40
|
fmt: str | None = None,
|
|
41
|
+
rle: bool = False,
|
|
40
42
|
) -> None:
|
|
41
43
|
"""Export the grid property to file."""
|
|
42
44
|
logger.debug("Export property to file %s as %s", pfile, fformat)
|
|
@@ -62,6 +64,7 @@ def to_file(
|
|
|
62
64
|
append=append,
|
|
63
65
|
binary=binary,
|
|
64
66
|
fmt=fmt,
|
|
67
|
+
rle=rle,
|
|
65
68
|
)
|
|
66
69
|
elif fformat == "xtgcpprop":
|
|
67
70
|
_export_xtgcpprop(gridprop, xtg_file.name)
|
|
@@ -104,6 +107,7 @@ def _export_grdecl(
|
|
|
104
107
|
append: bool = False,
|
|
105
108
|
binary: bool = False,
|
|
106
109
|
fmt: str | None = None,
|
|
110
|
+
rle: bool = False,
|
|
107
111
|
) -> None:
|
|
108
112
|
"""Export ascii or binary GRDECL"""
|
|
109
113
|
vals: npt.NDArray = gridprop.values.ravel(order="F")
|
|
@@ -132,18 +136,36 @@ def _export_grdecl(
|
|
|
132
136
|
else:
|
|
133
137
|
# Always the case when not binary
|
|
134
138
|
assert isinstance(fout, io.TextIOWrapper)
|
|
135
|
-
fout.write(name)
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
139
|
+
fout.write(f"{name}\n")
|
|
140
|
+
if rle:
|
|
141
|
+
counts, unique_values = run_length_encoding(vals)
|
|
142
|
+
for i, (count, unique_value) in enumerate(zip(counts, unique_values)):
|
|
143
|
+
fout.write(" ")
|
|
144
|
+
if fmt:
|
|
145
|
+
formatted_value = fmt % unique_value
|
|
146
|
+
elif gridprop.isdiscrete:
|
|
147
|
+
formatted_value = str(unique_value)
|
|
148
|
+
else:
|
|
149
|
+
formatted_value = f"{unique_value:3e}"
|
|
150
|
+
if count > 1:
|
|
151
|
+
# Try to preserve the alignment
|
|
152
|
+
text_width = len(formatted_value)
|
|
153
|
+
new_text = f"{count}*{formatted_value.lstrip()}"
|
|
154
|
+
formatted_value = " " * (text_width - len(new_text)) + new_text
|
|
155
|
+
fout.write(formatted_value)
|
|
156
|
+
if i % 6 == 5:
|
|
157
|
+
fout.write("\n")
|
|
158
|
+
else:
|
|
159
|
+
for i, v in enumerate(vals):
|
|
160
|
+
fout.write(" ")
|
|
161
|
+
if fmt:
|
|
162
|
+
fout.write(fmt % v)
|
|
163
|
+
elif gridprop.isdiscrete:
|
|
164
|
+
fout.write(str(v))
|
|
165
|
+
else:
|
|
166
|
+
fout.write(f"{v:3e}")
|
|
167
|
+
if i % 6 == 5:
|
|
168
|
+
fout.write("\n")
|
|
147
169
|
fout.write(" /\n")
|
|
148
170
|
|
|
149
171
|
|
xtgeo/grid3d/_gridprop_op1.py
CHANGED
|
@@ -25,6 +25,77 @@ _ValueList = List[List[_CoordOrValue]]
|
|
|
25
25
|
XYValueLists = Tuple[_XYCoordList, _ValueList]
|
|
26
26
|
|
|
27
27
|
|
|
28
|
+
def _validate_cast_range(values: np.ndarray, target_dtype: np.dtype) -> None:
|
|
29
|
+
"""Validate that values fit in target dtype range."""
|
|
30
|
+
if np.issubdtype(target_dtype, np.integer):
|
|
31
|
+
info = np.iinfo(target_dtype)
|
|
32
|
+
min_val, max_val = info.min, info.max
|
|
33
|
+
|
|
34
|
+
# Check actual value ranges
|
|
35
|
+
actual_min = np.nanmin(values)
|
|
36
|
+
actual_max = np.nanmax(values)
|
|
37
|
+
|
|
38
|
+
if actual_min < min_val or actual_max > max_val:
|
|
39
|
+
raise ValueError(
|
|
40
|
+
f"Values [{actual_min}, {actual_max}] exceed {target_dtype} "
|
|
41
|
+
f"range [{min_val}, {max_val}]"
|
|
42
|
+
)
|
|
43
|
+
elif np.issubdtype(target_dtype, np.floating):
|
|
44
|
+
info = np.finfo(target_dtype)
|
|
45
|
+
# Check for overflow/underflow
|
|
46
|
+
if np.any(np.abs(values[np.isfinite(values)]) > info.max):
|
|
47
|
+
raise ValueError(f"Values too large for {target_dtype}")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def discrete_to_continuous(self: GridProperty) -> None:
|
|
51
|
+
"""Convert from discrete to continuous values."""
|
|
52
|
+
if not self.isdiscrete:
|
|
53
|
+
logger.debug("No need to convert, already continuous")
|
|
54
|
+
return
|
|
55
|
+
|
|
56
|
+
logger.debug("Converting to continuous ...")
|
|
57
|
+
val = self._values.copy()
|
|
58
|
+
val = val.astype(np.float64)
|
|
59
|
+
self._values = val
|
|
60
|
+
self._isdiscrete = False
|
|
61
|
+
self._codes = {}
|
|
62
|
+
self.roxar_dtype = np.float32
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def continuous_to_discrete(self) -> None:
|
|
66
|
+
"""Convert from continuous to discrete values."""
|
|
67
|
+
if self.isdiscrete:
|
|
68
|
+
logger.debug("No need to convert, already discrete")
|
|
69
|
+
return
|
|
70
|
+
|
|
71
|
+
logger.debug("Converting to discrete ...")
|
|
72
|
+
val = self._values.copy()
|
|
73
|
+
|
|
74
|
+
# Validate finite values only
|
|
75
|
+
finite_vals = (
|
|
76
|
+
val[np.isfinite(val)]
|
|
77
|
+
if not isinstance(val, np.ma.MaskedArray)
|
|
78
|
+
else val.compressed()
|
|
79
|
+
)
|
|
80
|
+
if len(finite_vals) > 0:
|
|
81
|
+
_validate_cast_range(finite_vals, np.int32)
|
|
82
|
+
|
|
83
|
+
# Round and cast, suppressing the NaN warning
|
|
84
|
+
val = np.round(val)
|
|
85
|
+
with np.errstate(invalid="ignore"):
|
|
86
|
+
val = val.astype(np.int32)
|
|
87
|
+
|
|
88
|
+
self._values = val
|
|
89
|
+
self._isdiscrete = True
|
|
90
|
+
|
|
91
|
+
self._codes = {
|
|
92
|
+
v: str(v)
|
|
93
|
+
for v in np.ma.unique(val)
|
|
94
|
+
if np.isfinite(v) and not isinstance(v, np.ma.core.MaskedConstant)
|
|
95
|
+
}
|
|
96
|
+
self.roxar_dtype = np.uint16
|
|
97
|
+
|
|
98
|
+
|
|
28
99
|
def get_xy_value_lists(
|
|
29
100
|
self: GridProperty,
|
|
30
101
|
grid: Optional[Grid | GridProperty] = None,
|
|
@@ -134,12 +205,6 @@ def operation_polygons(
|
|
|
134
205
|
|
|
135
206
|
gl.update_values_from_carray(proxy, cvals, np.float64, delete=True)
|
|
136
207
|
|
|
137
|
-
proxyv = proxy.values.astype(np.int8)
|
|
138
|
-
|
|
139
|
-
proxytarget = 1
|
|
140
|
-
if not inside:
|
|
141
|
-
proxytarget = 0
|
|
142
|
-
|
|
143
208
|
if opname == "add":
|
|
144
209
|
tmp = self.values.copy() + value
|
|
145
210
|
elif opname == "sub":
|
|
@@ -170,8 +235,9 @@ def operation_polygons(
|
|
|
170
235
|
# convert tmp back to correct dtype
|
|
171
236
|
tmp = tmp.astype(dtype)
|
|
172
237
|
|
|
173
|
-
|
|
174
|
-
|
|
238
|
+
mask = proxy.values == 1 if inside else proxy.values == 0
|
|
239
|
+
|
|
240
|
+
self.values[mask] = tmp[mask]
|
|
175
241
|
|
|
176
242
|
|
|
177
243
|
def refine(
|
xtgeo/grid3d/_gridprop_roxapi.py
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
6
6
|
from typing import TYPE_CHECKING, Any, Literal
|
|
7
|
+
from warnings import warn
|
|
7
8
|
|
|
8
9
|
import numpy as np
|
|
9
10
|
from numpy import ma
|
|
@@ -135,6 +136,51 @@ def export_prop_roxapi(
|
|
|
135
136
|
rox.safe_close()
|
|
136
137
|
|
|
137
138
|
|
|
139
|
+
def _validate_dtype_in_roxar(
|
|
140
|
+
pval: np.ndarray, original_dtype: Any, dtype: Any, allow_unsafe_casting: bool = True
|
|
141
|
+
) -> None:
|
|
142
|
+
"""Local routine that check the range in xtgeo's parameter vs Roxar dtype"""
|
|
143
|
+
message = ""
|
|
144
|
+
|
|
145
|
+
if dtype != original_dtype:
|
|
146
|
+
message += (
|
|
147
|
+
f"Existing RMS icon has data type {original_dtype} while "
|
|
148
|
+
f"GridProperty has RMS data type {dtype}. This may cause issues in data "
|
|
149
|
+
"content while saving to RMS."
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
try:
|
|
153
|
+
if np.issubdtype(dtype, np.integer):
|
|
154
|
+
# Integer type
|
|
155
|
+
min_val, max_val = np.iinfo(dtype).min, np.iinfo(dtype).max
|
|
156
|
+
elif np.issubdtype(dtype, np.floating):
|
|
157
|
+
# Float type
|
|
158
|
+
min_val, max_val = np.finfo(dtype).min, np.finfo(dtype).max
|
|
159
|
+
else:
|
|
160
|
+
# Unknown type
|
|
161
|
+
raise RuntimeError("Probable bug, values array not integer or float")
|
|
162
|
+
|
|
163
|
+
if pval.min() < min_val or pval.max() > max_val:
|
|
164
|
+
message += (
|
|
165
|
+
f"Values outside {dtype} range [{min_val}, {max_val}] found. "
|
|
166
|
+
f"Range in xtgeo values are [{pval.min()}, {pval.max()}]"
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
except (ValueError, OverflowError):
|
|
170
|
+
# Handle edge cases gracefully
|
|
171
|
+
message += f"Could not validate range for dtype {dtype}. "
|
|
172
|
+
|
|
173
|
+
if message and allow_unsafe_casting:
|
|
174
|
+
message += (
|
|
175
|
+
"\nUnsafe casting is allowed; this means that original values may "
|
|
176
|
+
"be truncated which may give unpredictable results and/or mess "
|
|
177
|
+
"up code names and values settings."
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
if message:
|
|
181
|
+
warn(message, UserWarning)
|
|
182
|
+
|
|
183
|
+
|
|
138
184
|
def _store_in_roxar(
|
|
139
185
|
self: GridProperty,
|
|
140
186
|
pname: str,
|
|
@@ -147,50 +193,60 @@ def _store_in_roxar(
|
|
|
147
193
|
|
|
148
194
|
logger.info("Store in RMS...")
|
|
149
195
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
196
|
+
roxtype: Any = roxar # needed for mypy
|
|
197
|
+
roxar_property_type = (
|
|
198
|
+
roxtype.GridPropertyType.discrete
|
|
199
|
+
if self.isdiscrete
|
|
200
|
+
else roxtype.GridPropertyType.continuous
|
|
201
|
+
)
|
|
155
202
|
|
|
156
|
-
|
|
157
|
-
jind = ijk[:, 1]
|
|
158
|
-
kind = ijk[:, 2]
|
|
203
|
+
val3d = self.values.copy()
|
|
159
204
|
|
|
160
205
|
dtype = self._roxar_dtype
|
|
161
206
|
logger.info("DTYPE is %s for %s", dtype, pname)
|
|
162
207
|
|
|
163
|
-
# casting will secure correct types
|
|
164
208
|
if dtype not in VALID_ROXAR_DTYPES:
|
|
165
209
|
raise TypeError(
|
|
166
210
|
f"Roxar dtype is not valid: {dtype} must be in {VALID_ROXAR_DTYPES}"
|
|
167
211
|
)
|
|
168
212
|
|
|
169
|
-
pvalues = roxgrid.get_grid(realisation=realisation).generate_values(data_type=dtype)
|
|
170
|
-
|
|
171
|
-
roxtype: Any = roxar # needed for mypy
|
|
172
|
-
roxar_property_type = (
|
|
173
|
-
roxtype.GridPropertyType.discrete
|
|
174
|
-
if self.isdiscrete
|
|
175
|
-
else roxtype.GridPropertyType.continuous
|
|
176
|
-
)
|
|
177
|
-
|
|
178
|
-
pvalues[cellno] = val3d[iind, jind, kind]
|
|
179
|
-
|
|
180
213
|
properties = roxgrid.properties
|
|
181
|
-
|
|
214
|
+
original_dtype = dtype
|
|
182
215
|
if pname not in properties:
|
|
183
216
|
rprop = properties.create(
|
|
184
217
|
pname, property_type=roxar_property_type, data_type=dtype
|
|
185
218
|
)
|
|
186
219
|
else:
|
|
187
220
|
rprop = properties[pname]
|
|
188
|
-
|
|
221
|
+
original_dtype = rprop.data_type
|
|
222
|
+
|
|
223
|
+
_validate_dtype_in_roxar(val3d, original_dtype, dtype, casting == "unsafe")
|
|
189
224
|
|
|
190
|
-
|
|
225
|
+
dtype = original_dtype
|
|
226
|
+
val3d = val3d.astype(dtype, casting=casting)
|
|
191
227
|
|
|
192
|
-
|
|
193
|
-
|
|
228
|
+
cellno = indexer.get_cell_numbers_in_range((0, 0, 0), indexer.dimensions)
|
|
229
|
+
|
|
230
|
+
ijk = indexer.get_indices(cellno)
|
|
231
|
+
|
|
232
|
+
iind = ijk[:, 0]
|
|
233
|
+
jind = ijk[:, 1]
|
|
234
|
+
kind = ijk[:, 2]
|
|
235
|
+
|
|
236
|
+
pvalues = roxgrid.get_grid(realisation=realisation).generate_values(data_type=dtype)
|
|
237
|
+
|
|
238
|
+
pvalues[cellno] = val3d[iind, jind, kind]
|
|
239
|
+
rprop.set_values(pvalues, realisation=realisation)
|
|
240
|
+
|
|
241
|
+
if self.isdiscrete and "float" not in str(original_dtype):
|
|
242
|
+
try:
|
|
243
|
+
rprop.code_names = _rox_compatible_codes(self.codes)
|
|
244
|
+
except ValueError as verr:
|
|
245
|
+
message = (
|
|
246
|
+
f"Trying to set codes in Roxar: {self.codes} for type "
|
|
247
|
+
f"{self._roxar_dtype}. {str(verr)} Consider editing the codes!"
|
|
248
|
+
)
|
|
249
|
+
raise ValueError(message)
|
|
194
250
|
|
|
195
251
|
|
|
196
252
|
def _fix_codes(
|
xtgeo/grid3d/grid.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import json
|
|
6
|
-
from typing import TYPE_CHECKING, Any, Literal
|
|
6
|
+
from typing import TYPE_CHECKING, Any, Literal
|
|
7
7
|
|
|
8
8
|
import numpy as np
|
|
9
9
|
import numpy.ma as ma
|
|
@@ -994,13 +994,16 @@ class Grid(_Grid3D):
|
|
|
994
994
|
# Create/import/export
|
|
995
995
|
# ==================================================================================
|
|
996
996
|
|
|
997
|
-
def to_file(
|
|
997
|
+
def to_file(
|
|
998
|
+
self, gfile: FileLike, fformat: str = "roff", rle: bool = False
|
|
999
|
+
) -> None:
|
|
998
1000
|
"""Export grid geometry to file, various vendor formats.
|
|
999
1001
|
|
|
1000
1002
|
Args:
|
|
1001
1003
|
gfile (str): Name of output file
|
|
1002
1004
|
fformat (str): File format; roff/roff_binary/roff_ascii/
|
|
1003
1005
|
grdecl/bgrdecl/egrid.
|
|
1006
|
+
rle (bool): Use run-length encoding (only for grdecl file)
|
|
1004
1007
|
|
|
1005
1008
|
Raises:
|
|
1006
1009
|
OSError: Directory does not exist
|
|
@@ -1018,7 +1021,7 @@ class Grid(_Grid3D):
|
|
|
1018
1021
|
elif fformat in FileFormat.ROFF_ASCII.value:
|
|
1019
1022
|
_grid_export.export_roff(self, _gfile.name, "ascii")
|
|
1020
1023
|
elif fformat in FileFormat.GRDECL.value:
|
|
1021
|
-
_grid_export.export_grdecl(self, _gfile.name, 1)
|
|
1024
|
+
_grid_export.export_grdecl(self, _gfile.name, 1, rle)
|
|
1022
1025
|
elif fformat in FileFormat.BGRDECL.value:
|
|
1023
1026
|
_grid_export.export_grdecl(self, _gfile.name, 0)
|
|
1024
1027
|
elif fformat in FileFormat.EGRID.value:
|
|
@@ -1577,9 +1580,30 @@ class Grid(_Grid3D):
|
|
|
1577
1580
|
|
|
1578
1581
|
return self.get_subgrids()
|
|
1579
1582
|
|
|
1580
|
-
def get_zoneprop_from_subgrids(self) ->
|
|
1581
|
-
"""
|
|
1582
|
-
|
|
1583
|
+
def get_zoneprop_from_subgrids(self) -> xtgeo.GridProperty | None:
|
|
1584
|
+
"""
|
|
1585
|
+
Create a discrete GridProperty encoding subgrid indices as zone values.
|
|
1586
|
+
|
|
1587
|
+
Returns:
|
|
1588
|
+
A GridProperty where each layer belonging to a subgrid is assigned a unique
|
|
1589
|
+
integer zone index (starting from 1). The property codes map these indices
|
|
1590
|
+
to subgrid names. Returns None if no subgrids are defined.
|
|
1591
|
+
"""
|
|
1592
|
+
if not self.subgrids:
|
|
1593
|
+
return None
|
|
1594
|
+
|
|
1595
|
+
zoneprop = xtgeo.GridProperty(
|
|
1596
|
+
ncol=self.ncol,
|
|
1597
|
+
nrow=self.nrow,
|
|
1598
|
+
nlay=self.nlay,
|
|
1599
|
+
name="Zone",
|
|
1600
|
+
discrete=True,
|
|
1601
|
+
values=0,
|
|
1602
|
+
)
|
|
1603
|
+
for i, (name, layers) in enumerate(self.subgrids.items()):
|
|
1604
|
+
zoneprop.codes[i + 1] = name
|
|
1605
|
+
zoneprop.values[:, :, min(layers) - 1 : max(layers)] = i + 1
|
|
1606
|
+
return zoneprop
|
|
1583
1607
|
|
|
1584
1608
|
def get_boundary_polygons(
|
|
1585
1609
|
self: Grid,
|
|
@@ -1966,12 +1990,14 @@ class Grid(_Grid3D):
|
|
|
1966
1990
|
ffl: Free fluid level e.g. FWL (or level whatever is required; a level from
|
|
1967
1991
|
which cells above will be shown as delta heights (positive), while
|
|
1968
1992
|
cells below will have 0.0 values.
|
|
1969
|
-
option: How to compute values, as either "cell_center_above_ffl"
|
|
1970
|
-
"cell_corners_above_ffl"
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1993
|
+
option: How to compute values, as either "cell_center_above_ffl",
|
|
1994
|
+
"cell_corners_above_ffl" or "truncated_cell_corners_above_ffl".
|
|
1995
|
+
The first one looks at cell Z centerlines, and compute the top,
|
|
1996
|
+
the bottom and the midpoint. The second will look at cell corners,
|
|
1997
|
+
using the uppermost corner for top, and the lowermost corner for bottom.
|
|
1998
|
+
The third will truncate all cell corners above ffl and compute cell Z
|
|
1999
|
+
centerlines. In all cases, values are modified if cell is intersected
|
|
2000
|
+
with the provided ffl.
|
|
1975
2001
|
|
|
1976
2002
|
Returns:
|
|
1977
2003
|
(htop, hbot, hmid) delta heights, as xtgeo GridProperty objects
|
|
@@ -2295,7 +2321,7 @@ class Grid(_Grid3D):
|
|
|
2295
2321
|
* maxangle_topbase_proj (degrees) max angle projected (bird view)
|
|
2296
2322
|
* minangle_sides (degress) minimum angle, all side surfaces
|
|
2297
2323
|
* maxangle_sides (degress) maximum angle, all side surfaces
|
|
2298
|
-
* collapsed (int) Integer, 1 of one or more corners are
|
|
2324
|
+
* collapsed (int) Integer, 1 of one or more corners are collapsed in Z
|
|
2299
2325
|
* faulted (int) Integer, 1 if cell is faulted (one or more neighbours offset)
|
|
2300
2326
|
* negative_thickness (int) Integer, 1 if cell has negative thickness
|
|
2301
2327
|
* concave_proj (int) 1 if cell is concave seen from projected bird view
|
|
@@ -2332,7 +2358,7 @@ class Grid(_Grid3D):
|
|
|
2332
2358
|
inside: bool = True,
|
|
2333
2359
|
force_close: bool = False,
|
|
2334
2360
|
) -> None:
|
|
2335
|
-
"""
|
|
2361
|
+
"""Inactivate grid inside a polygon.
|
|
2336
2362
|
|
|
2337
2363
|
The Polygons instance may consist of several polygons. If a polygon
|
|
2338
2364
|
is open, then the flag force_close will close any that are not open
|
|
@@ -2359,14 +2385,30 @@ class Grid(_Grid3D):
|
|
|
2359
2385
|
layer_range: tuple[int, int] | None = None,
|
|
2360
2386
|
force_close: bool = False,
|
|
2361
2387
|
) -> None:
|
|
2362
|
-
"""
|
|
2388
|
+
"""Inactivate grid outside a polygon."""
|
|
2363
2389
|
self.inactivate_inside(
|
|
2364
2390
|
poly, layer_range=layer_range, inside=False, force_close=force_close
|
|
2365
2391
|
)
|
|
2366
2392
|
|
|
2367
|
-
def collapse_inactive_cells(self) -> None:
|
|
2368
|
-
"""Collapse inactive layers
|
|
2369
|
-
|
|
2393
|
+
def collapse_inactive_cells(self, internal: bool = True) -> None:
|
|
2394
|
+
"""Collapse inactive layers per I J column (~vertically).
|
|
2395
|
+
|
|
2396
|
+
Seen by I,J column, the inactive cells are collapsed to the first active cell in
|
|
2397
|
+
the column. First transversed from top, then from bottom. If no active cells in
|
|
2398
|
+
the column, the (invisible) Z coordinates are averaged to one common location.
|
|
2399
|
+
|
|
2400
|
+
If `internal` is True (default), then also internal inactive cells (i.e.
|
|
2401
|
+
inactive "hole" surrounded by active cells) are collapsed. In this case the Z
|
|
2402
|
+
coordinates of the adjacent active cells are moved to fill the gap.
|
|
2403
|
+
|
|
2404
|
+
The current grid instance will be updated.
|
|
2405
|
+
|
|
2406
|
+
Args:
|
|
2407
|
+
internal: If True (default), then the internal collapse is also done.
|
|
2408
|
+
|
|
2409
|
+
.. versionchanged:: 4.11 Added ``internal option``, algorithm is improved
|
|
2410
|
+
"""
|
|
2411
|
+
_grid_etc1.collapse_inactive_cells(self, internal=internal)
|
|
2370
2412
|
|
|
2371
2413
|
def crop(
|
|
2372
2414
|
self,
|
xtgeo/grid3d/grid_property.py
CHANGED
|
@@ -773,6 +773,7 @@ class GridProperty(_Grid3D):
|
|
|
773
773
|
append: bool = False,
|
|
774
774
|
dtype: type[np.float32] | type[np.float64] | type[np.int32] | None = None,
|
|
775
775
|
fmt: str | None = None,
|
|
776
|
+
rle: bool = False,
|
|
776
777
|
) -> None:
|
|
777
778
|
"""
|
|
778
779
|
Export the grid property to file.
|
|
@@ -791,6 +792,7 @@ class GridProperty(_Grid3D):
|
|
|
791
792
|
Eclipse formats.
|
|
792
793
|
fmt: Format for ascii grdecl format. Default is None. If specified,
|
|
793
794
|
the user is responsible for a valid format specifier, e.g. "%8.4f".
|
|
795
|
+
rle: Use run length encoding, only for grdecl format.
|
|
794
796
|
|
|
795
797
|
Example::
|
|
796
798
|
|
|
@@ -815,6 +817,7 @@ class GridProperty(_Grid3D):
|
|
|
815
817
|
append=append,
|
|
816
818
|
dtype=dtype,
|
|
817
819
|
fmt=fmt,
|
|
820
|
+
rle=rle,
|
|
818
821
|
)
|
|
819
822
|
|
|
820
823
|
@classmethod
|
|
@@ -1222,36 +1225,11 @@ class GridProperty(_Grid3D):
|
|
|
1222
1225
|
|
|
1223
1226
|
def discrete_to_continuous(self) -> None:
|
|
1224
1227
|
"""Convert from discrete to continuous values."""
|
|
1225
|
-
|
|
1226
|
-
logger.debug("No need to convert, already continuous")
|
|
1227
|
-
return
|
|
1228
|
-
|
|
1229
|
-
logger.debug("Converting to continuous ...")
|
|
1230
|
-
val = self._values.copy()
|
|
1231
|
-
val = val.astype("float64")
|
|
1232
|
-
self._values = val
|
|
1233
|
-
self._isdiscrete = False
|
|
1234
|
-
self._codes = {}
|
|
1235
|
-
self.roxar_dtype = np.float32
|
|
1228
|
+
_gridprop_op1.discrete_to_continuous(self)
|
|
1236
1229
|
|
|
1237
1230
|
def continuous_to_discrete(self) -> None:
|
|
1238
1231
|
"""Convert from continuous to discrete values."""
|
|
1239
|
-
|
|
1240
|
-
logger.debug("No need to convert, already discrete")
|
|
1241
|
-
return
|
|
1242
|
-
|
|
1243
|
-
logger.debug("Converting to discrete ...")
|
|
1244
|
-
val = self._values.copy()
|
|
1245
|
-
val = val.astype(np.int32)
|
|
1246
|
-
self._values = val
|
|
1247
|
-
self._isdiscrete = True
|
|
1248
|
-
|
|
1249
|
-
# make the code list
|
|
1250
|
-
uniq = np.unique(val).tolist()
|
|
1251
|
-
codes = dict(zip(uniq, uniq))
|
|
1252
|
-
codes = {k: str(v) for k, v in codes.items()} # val as strings
|
|
1253
|
-
self._codes = codes
|
|
1254
|
-
self.roxar_dtype = np.uint16
|
|
1232
|
+
_gridprop_op1.continuous_to_discrete(self)
|
|
1255
1233
|
|
|
1256
1234
|
# ==================================================================================
|
|
1257
1235
|
# Operations restricted to inside/outside polygons
|
xtgeo/metadata/metadata.py
CHANGED
|
@@ -10,10 +10,17 @@ The metadata works through the various datatypes in XTGeo. For example::
|
|
|
10
10
|
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from typing import TYPE_CHECKING, Any
|
|
16
|
+
|
|
13
17
|
import xtgeo
|
|
14
18
|
from xtgeo.common.constants import UNDEF
|
|
15
19
|
from xtgeo.common.log import null_logger
|
|
16
20
|
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from datetime import datetime
|
|
23
|
+
|
|
17
24
|
logger = null_logger(__name__)
|
|
18
25
|
|
|
19
26
|
|
|
@@ -50,10 +57,10 @@ class _OptionalMetaData:
|
|
|
50
57
|
self._name = "A Longer Descriptive Name e.g. from SMDA"
|
|
51
58
|
self._shortname = "TheShortName"
|
|
52
59
|
self._datatype = None
|
|
53
|
-
self._md5sum = None
|
|
60
|
+
self._md5sum: str | None = None
|
|
54
61
|
self._description = "Some description"
|
|
55
62
|
self._crs = None
|
|
56
|
-
self._datetime = None
|
|
63
|
+
self._datetime: datetime | None = None
|
|
57
64
|
self._deltadatetime = None
|
|
58
65
|
self._visuals = {"colortable": "rainbow", "lower": None, "upper": None}
|
|
59
66
|
self._domain = "depth"
|
|
@@ -68,29 +75,29 @@ class _OptionalMetaData:
|
|
|
68
75
|
self._source = "unknown"
|
|
69
76
|
|
|
70
77
|
@property
|
|
71
|
-
def name(self):
|
|
78
|
+
def name(self) -> str:
|
|
72
79
|
return self._name
|
|
73
80
|
|
|
74
81
|
@name.setter
|
|
75
|
-
def name(self, newname):
|
|
82
|
+
def name(self, newname: str) -> None:
|
|
76
83
|
# TODO: validation
|
|
77
84
|
self._name = newname
|
|
78
85
|
|
|
79
86
|
@property
|
|
80
|
-
def datetime(self):
|
|
87
|
+
def datetime(self) -> datetime | None:
|
|
81
88
|
return self._datetime
|
|
82
89
|
|
|
83
90
|
@datetime.setter
|
|
84
|
-
def datetime(self, newdate):
|
|
91
|
+
def datetime(self, newdate: datetime) -> None:
|
|
85
92
|
# TODO: validation
|
|
86
93
|
self._datetime = newdate
|
|
87
94
|
|
|
88
95
|
@property
|
|
89
|
-
def shortname(self):
|
|
96
|
+
def shortname(self) -> str:
|
|
90
97
|
return self._shortname
|
|
91
98
|
|
|
92
99
|
@shortname.setter
|
|
93
|
-
def shortname(self, newname):
|
|
100
|
+
def shortname(self, newname: str) -> None:
|
|
94
101
|
if not isinstance(newname, str):
|
|
95
102
|
raise ValueError("The shortname must be a string.")
|
|
96
103
|
if len(newname) >= 32:
|
|
@@ -99,11 +106,11 @@ class _OptionalMetaData:
|
|
|
99
106
|
self._shortname = newname
|
|
100
107
|
|
|
101
108
|
@property
|
|
102
|
-
def description(self):
|
|
109
|
+
def description(self) -> str:
|
|
103
110
|
return self._description
|
|
104
111
|
|
|
105
112
|
@description.setter
|
|
106
|
-
def description(self, newstr):
|
|
113
|
+
def description(self, newstr: str) -> None:
|
|
107
114
|
if not isinstance(newstr, str):
|
|
108
115
|
raise ValueError("The description must be a string.")
|
|
109
116
|
if len(newstr) >= 64:
|
|
@@ -115,7 +122,7 @@ class _OptionalMetaData:
|
|
|
115
122
|
self._description = newstr
|
|
116
123
|
|
|
117
124
|
@property
|
|
118
|
-
def md5sum(self):
|
|
125
|
+
def md5sum(self) -> str | None:
|
|
119
126
|
"""Set or get the md5 checksum of file content.
|
|
120
127
|
|
|
121
128
|
See generate_hash() method in e.g. RegularSurface.
|
|
@@ -123,11 +130,11 @@ class _OptionalMetaData:
|
|
|
123
130
|
return self._md5sum
|
|
124
131
|
|
|
125
132
|
@md5sum.setter
|
|
126
|
-
def md5sum(self, newhash):
|
|
133
|
+
def md5sum(self, newhash: str) -> None:
|
|
127
134
|
# TODO: validation
|
|
128
135
|
self._md5sum = newhash
|
|
129
136
|
|
|
130
|
-
def get_meta(self):
|
|
137
|
+
def get_meta(self) -> dict[str, Any]:
|
|
131
138
|
"""Return metadata as an dict."""
|
|
132
139
|
meta = {}
|
|
133
140
|
for key in self.__slots__:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: xtgeo
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.12.0
|
|
4
4
|
Summary: XTGeo is a Python library for 3D grids, surfaces, wells, etc
|
|
5
5
|
Keywords: grids,surfaces,wells,cubes
|
|
6
6
|
Author-Email: Equinor <fg_fmu-atlas@equinor.com>
|
|
@@ -14,7 +14,6 @@ Classifier: Operating System :: Microsoft :: Windows
|
|
|
14
14
|
Classifier: Operating System :: MacOS
|
|
15
15
|
Classifier: Natural Language :: English
|
|
16
16
|
Classifier: Programming Language :: Python
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
18
17
|
Classifier: Programming Language :: Python :: 3.10
|
|
19
18
|
Classifier: Programming Language :: Python :: 3.11
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.12
|
|
@@ -27,7 +26,7 @@ Project-URL: Homepage, https://github.com/equinor/xtgeo
|
|
|
27
26
|
Project-URL: Repository, https://github.com/equinor/xtgeo
|
|
28
27
|
Project-URL: Issues, https://github.com/equinor/xtgeo/issues
|
|
29
28
|
Project-URL: Documentation, https://xtgeo.readthedocs.io
|
|
30
|
-
Requires-Python: >=3.
|
|
29
|
+
Requires-Python: >=3.10
|
|
31
30
|
Requires-Dist: deprecation
|
|
32
31
|
Requires-Dist: h5py>=3
|
|
33
32
|
Requires-Dist: hdf5plugin>=2.3
|
|
@@ -92,7 +91,7 @@ Detailed documentation for [XTGeo at Read _the_ Docs](https://xtgeo.readthedocs.
|
|
|
92
91
|
|
|
93
92
|
## Feature summary
|
|
94
93
|
|
|
95
|
-
- Python 3.
|
|
94
|
+
- Python 3.10+ support
|
|
96
95
|
- Focus on high speed, using numpy and pandas with C backend
|
|
97
96
|
- Regular surfaces, i.e. 2D maps with regular sampling and rotation
|
|
98
97
|
- 3D grids (corner-point), supporting several formats such as
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
cxtgeo.py,sha256=TdHva0vqjD5B_9jA6kxvev9i2kvZ4Oftc1bQoLdFNXQ,33972
|
|
2
2
|
cxtgeoPYTHON_wrap.c,sha256=E76gd7QM28i3EYkuQEA1V_-3cxzXxsi_b151ZcEszxk,655525
|
|
3
3
|
xtgeo/__init__.py,sha256=edNCOXuPS5qjutDGX5-2Pz0T2Lpx1GoeG3IoeXUdBw8,5834
|
|
4
|
-
xtgeo/_cxtgeo.cp312-win_amd64.pyd,sha256=
|
|
5
|
-
xtgeo/_internal.cp312-win_amd64.pyd,sha256=
|
|
4
|
+
xtgeo/_cxtgeo.cp312-win_amd64.pyd,sha256=VpIuaVWOpWE7QIvE6Zqa58i2iDv7fJ4dYmz3OwavT7I,720896
|
|
5
|
+
xtgeo/_internal.cp312-win_amd64.pyd,sha256=SMvHNjTgIYsZgRWo9-DlE1JCuLZVUlVzDP7blcCxjUk,985600
|
|
6
6
|
xtgeo/common/__init__.py,sha256=6R_OizhLnAoJUmUoQtGqgNvGcn1HWc4udc6YvRRlxWQ,480
|
|
7
7
|
xtgeo/common/_angles.py,sha256=fAX5FzgqIjjIDe9_C7gy6zPjdaSQuzMb8Z2XdKSx-6Y,851
|
|
8
8
|
xtgeo/common/_xyz_enum.py,sha256=gb1PVOWMTRb4YGDq9Q0YVVkKqMyI_xBg1vUBvOLXlfs,1095
|
|
@@ -12,14 +12,14 @@ xtgeo/common/exceptions.py,sha256=OZyU9C8cS4NhCnjQyREfuoVy9mdvGCiccd4nC9yn_N4,12
|
|
|
12
12
|
xtgeo/common/log.py,sha256=8-wTrZd87UNr_ZEb_lZvR1khUeKmIO_UECMpV_Q_btM,2835
|
|
13
13
|
xtgeo/common/sys.py,sha256=lvvmS4D7qaAVVg5UueNSt44WeEgpZcmyINEGKLHXctM,5144
|
|
14
14
|
xtgeo/common/types.py,sha256=LanIGZh0HU2XXfJelZA-FC__J6fLB1DzuN7jj7hNSgU,362
|
|
15
|
-
xtgeo/common/version.py,sha256=
|
|
15
|
+
xtgeo/common/version.py,sha256=TGPzC91qCIpsmAjdh_1loT2Gs_vjWi5JhetfyITnpsU,748
|
|
16
16
|
xtgeo/common/xtgeo_dialog.py,sha256=XX445byFvdRjT2Eg9Mgd3ChRQ9d1Mj5MEdsBRYBSo8A,17733
|
|
17
17
|
xtgeo/cube/__init__.py,sha256=5_F9Cefu4081jIwDqsyJGKTlrVLe1UfwI8wQXVRYPDM,169
|
|
18
18
|
xtgeo/cube/_cube_export.py,sha256=YcU_0c_kFyh8T_ytY8XRR9Bmt8KK3J1taMLzo_w_m4c,6659
|
|
19
19
|
xtgeo/cube/_cube_import.py,sha256=F6uWEXWxX0a4Px6HVa0pRCPNcfr36bMe_559CSf4h9c,17314
|
|
20
20
|
xtgeo/cube/_cube_roxapi.py,sha256=UvU4yr1b1ZTyYxBIj7_ei2aub_VqAdP9S_6znpzDJ14,5101
|
|
21
21
|
xtgeo/cube/_cube_utils.py,sha256=Mx79j29QLBJQ8wlwAfVgb-xN0ud7ilRIh6jOx7HMs2o,7727
|
|
22
|
-
xtgeo/cube/_cube_window_attributes.py,sha256=
|
|
22
|
+
xtgeo/cube/_cube_window_attributes.py,sha256=NAgm2WCjpiFx5eLUM5G0HqUSf0SKaVb6hVOXMD7x3zs,12956
|
|
23
23
|
xtgeo/cube/cube1.py,sha256=QKqKIGH814zehx-avRuett996cx8lI-2bYyjuqaCmkw,35864
|
|
24
24
|
xtgeo/grid3d/__init__.py,sha256=yctcaBS-PW_s3hCd7xq-XZZi514R_fgcpIrMnETZ87w,347
|
|
25
25
|
xtgeo/grid3d/_ecl_grid.py,sha256=Oqe0_4ErUZkM-le4EnM6hMqXGMpMZyrr9-W9Xa7ZN1A,26679
|
|
@@ -28,14 +28,14 @@ xtgeo/grid3d/_ecl_logi_head.py,sha256=pioEgigpnLjyDWq07Msp82xS15GCxrBZfSpEd2klEu
|
|
|
28
28
|
xtgeo/grid3d/_ecl_output_file.py,sha256=8IPXV-WMkh37VH78XibmMuVK9zBkmimqwc-uqy_1VCQ,2095
|
|
29
29
|
xtgeo/grid3d/_egrid.py,sha256=S-X1B8vmO_Zmj8dJZuEdVUDDu3noXhTGgg6udB2hHVg,34839
|
|
30
30
|
xtgeo/grid3d/_find_gridprop_in_eclrun.py,sha256=JsCBFvUsTBcCD6tFdn4ON8zOLGFT6PyhyDr8SL9fyJs,22059
|
|
31
|
-
xtgeo/grid3d/_grdecl_format.py,sha256=
|
|
32
|
-
xtgeo/grid3d/_grdecl_grid.py,sha256=
|
|
31
|
+
xtgeo/grid3d/_grdecl_format.py,sha256=1AwSTrLNx4XCthHXNDH4cmNOGC2QWoWfhBM1jMxN91A,10447
|
|
32
|
+
xtgeo/grid3d/_grdecl_grid.py,sha256=f7hzGg3M9Rd7DVnqR_8D0WGBUSWlDFfnIBmz_80kVTk,14002
|
|
33
33
|
xtgeo/grid3d/_grid3d.py,sha256=rTqfhwK4dNX1A5M-xUC-9hWwfxrkX6_TVgwc79KCXCI,769
|
|
34
34
|
xtgeo/grid3d/_grid3d_fence.py,sha256=yDmKwlErNCl13vV7opJjKlzlwCzwk2gSJnzIb4jGpHg,9501
|
|
35
35
|
xtgeo/grid3d/_grid3d_utils.py,sha256=VBbdzah_D8yeaMU4vMQRYTFRKq95QaGqPk4oXYBWdHw,7477
|
|
36
36
|
xtgeo/grid3d/_grid_boundary.py,sha256=xNumtMeOfjmwS5vzv1Nl_yZpGiDEQXk896pXzugdUVs,2384
|
|
37
|
-
xtgeo/grid3d/_grid_etc1.py,sha256=
|
|
38
|
-
xtgeo/grid3d/_grid_export.py,sha256=
|
|
37
|
+
xtgeo/grid3d/_grid_etc1.py,sha256=V8XeVU4-1ZRDOmyDQS3FrU_SY6bZkssvMuV_boR6Uzw,45156
|
|
38
|
+
xtgeo/grid3d/_grid_export.py,sha256=G36va-Kz93W-P2z8Qr-0Dq-o7Abfsd3OC8ESCAFRh2M,7492
|
|
39
39
|
xtgeo/grid3d/_grid_hybrid.py,sha256=4xtZlZSzn0BY8Kl8TY6Su3QtCH16ax-Tf49ItvHaRPk,1666
|
|
40
40
|
xtgeo/grid3d/_grid_import.py,sha256=Y11JDjf5tZkDth69aTtbkMfMjRbVvSnQbV3lt-jCuA8,2672
|
|
41
41
|
xtgeo/grid3d/_grid_import_ecl.py,sha256=WQw0Vr7ftFdM-_Jm1KjhGJ_Ko2PPO0eLgi6lCE98VYo,3554
|
|
@@ -44,27 +44,27 @@ xtgeo/grid3d/_grid_import_xtgcpgeom.py,sha256=ZVqkf1QAvv8iDFZxV-2AhGimUjAdFcMwFm
|
|
|
44
44
|
xtgeo/grid3d/_grid_refine.py,sha256=W5mDAJuKuCovuWRseaXfrS4Eqku0NbgqwnsuAsQOOlQ,8799
|
|
45
45
|
xtgeo/grid3d/_grid_roxapi.py,sha256=ixanN3VNGQM3QeVxXLHj_hKZJSiG3cdZwHc2NdXg_ms,9199
|
|
46
46
|
xtgeo/grid3d/_grid_wellzone.py,sha256=5OwMsmksC0Wza5XXfUiDHCzSLWqmV3JbSORDUFPXZho,5951
|
|
47
|
-
xtgeo/grid3d/_gridprop_export.py,sha256=
|
|
47
|
+
xtgeo/grid3d/_gridprop_export.py,sha256=wKYm6UhRCrDwf74V_HuWvdlYKgET6iIm8i1fGpPgbGY,6735
|
|
48
48
|
xtgeo/grid3d/_gridprop_import_eclrun.py,sha256=E8fgp5VFbJknoitP-t2JJv5LcIUuLutIDJuVXBS_ugE,5260
|
|
49
49
|
xtgeo/grid3d/_gridprop_import_grdecl.py,sha256=q-RZ8wvvyxzrw8ep1yr0L997WX13d__togXMYHypGqM,4263
|
|
50
50
|
xtgeo/grid3d/_gridprop_import_roff.py,sha256=vAHfXTjopeFLCVtseQdqlgkLNFfT9aW9I0nL6xiYorI,1575
|
|
51
51
|
xtgeo/grid3d/_gridprop_import_xtgcpprop.py,sha256=ToajcbJL4cHo-gV1ab2rPQ0U5DxNnUfetvO1tetafNc,5027
|
|
52
52
|
xtgeo/grid3d/_gridprop_lowlevel.py,sha256=1PBIn2ZMiF5Gj9u2n6xu2PFDiJ6NdrgYaL89lkq3_yo,5257
|
|
53
|
-
xtgeo/grid3d/_gridprop_op1.py,sha256=
|
|
54
|
-
xtgeo/grid3d/_gridprop_roxapi.py,sha256=
|
|
53
|
+
xtgeo/grid3d/_gridprop_op1.py,sha256=5es5e-b14OsEnk1xt-C2iuyv0DEYYkC_0Nq7Coe6Tw8,9228
|
|
54
|
+
xtgeo/grid3d/_gridprop_roxapi.py,sha256=4Aa4Vqzmml6RjePuRqyl68WeCxuICYv7p1u6KgL_R94,9262
|
|
55
55
|
xtgeo/grid3d/_gridprop_value_init.py,sha256=xMAFhTKiZvq3QLcsYuuFRBBWzEQR9gxw7rMuj5ko5P4,4454
|
|
56
56
|
xtgeo/grid3d/_gridprops_import_eclrun.py,sha256=M7hvBazZPjYsKOYBL1SHCcYqztgonARuQtXBQ_hyMdI,11782
|
|
57
57
|
xtgeo/grid3d/_gridprops_import_roff.py,sha256=fCCMwa8OgLXk9zMvFpr4nfsKLkmBFKKLSfLwUHenyTA,2550
|
|
58
58
|
xtgeo/grid3d/_roff_grid.py,sha256=kppx044-JmfLvaIt5NPABnIoLwJ1BGQc2gFeog7Cl0g,17742
|
|
59
59
|
xtgeo/grid3d/_roff_parameter.py,sha256=9gVlFMyT2QbwUvm_FCJG0hTvJPrK2Y86kbeljs2eCbc,11376
|
|
60
|
-
xtgeo/grid3d/grid.py,sha256=
|
|
60
|
+
xtgeo/grid3d/grid.py,sha256=d628JbJ5VqM-jiCyyITCNflPbSo-9ESE_v0nGGNJKh0,109596
|
|
61
61
|
xtgeo/grid3d/grid_properties.py,sha256=OlIyo1PXrTRFHGn8avXdBXNpFM5DJuLjVRB9koayKGQ,24993
|
|
62
|
-
xtgeo/grid3d/grid_property.py,sha256=
|
|
62
|
+
xtgeo/grid3d/grid_property.py,sha256=Wm9H6Nzqs8fSD35eg3NqTb-i6HGThsRmwbynZLg1nMo,45980
|
|
63
63
|
xtgeo/grid3d/types.py,sha256=eaBHJL91zXV0ipaY30K_ZpEdUfjj681CoAbKfvNZ_5Q,290
|
|
64
64
|
xtgeo/io/__init__.py,sha256=k5OQUG7ePoTleAh8ECiwowCg1imL6dx9O7gPUArcQQw,23
|
|
65
65
|
xtgeo/io/_file.py,sha256=-bacKEMpbPMF1al3gqoV5pu0J57g26mRdSasxNk20Gg,21085
|
|
66
66
|
xtgeo/metadata/__init__.py,sha256=VV81ga8OzzrowieSQZ5bZxo3Ug70Dch89iwZF1mOshA,337
|
|
67
|
-
xtgeo/metadata/metadata.py,sha256=
|
|
67
|
+
xtgeo/metadata/metadata.py,sha256=0EVNcXXDh3mimYNZYW3FIYe3bXIzeEKRyqZOzBhu31E,12874
|
|
68
68
|
xtgeo/roxutils/__init__.py,sha256=_f2qb32IFeZ_8YhcTDFlNgsWyH0HPDJQ7V0qUbjmUSs,99
|
|
69
69
|
xtgeo/roxutils/_roxar_loader.py,sha256=mKg3SsHJDzaGEYQwuY2yPcTxOnCybKmq3IeLmteCJPg,1958
|
|
70
70
|
xtgeo/roxutils/_roxutils_etc.py,sha256=Lma5Hskrld2PDLicSTkASse2t3eqxvtnure9uDCyjxo,3852
|
|
@@ -111,7 +111,7 @@ xtgeo/xyz/_xyz_oper.py,sha256=_uCR-PYEsPQkBKh82oDvUsz7Y2vqsjqoiEdfmd8HIEU,20058
|
|
|
111
111
|
xtgeo/xyz/_xyz_roxapi.py,sha256=NWgnPShA8amuHgSTqvy5_y21Qyx2s_6IWP1qPyHTPb8,26585
|
|
112
112
|
xtgeo/xyz/points.py,sha256=UhcdR7nEw3Lxa496UMOWOm5IkpQLC_IRU0FcFLpEV-o,23253
|
|
113
113
|
xtgeo/xyz/polygons.py,sha256=JjvA5BiW0HzaVmuZjnKB55n0OJXZfjOBQHfSNcZBDiA,28791
|
|
114
|
-
xtgeo-4.
|
|
115
|
-
xtgeo-4.
|
|
116
|
-
xtgeo-4.
|
|
117
|
-
xtgeo-4.
|
|
114
|
+
xtgeo-4.12.0.dist-info/METADATA,sha256=au9aO7YlUYU5YAt5jB5uAwdsSjIqhAuyWSmIMUt7TYI,5741
|
|
115
|
+
xtgeo-4.12.0.dist-info/WHEEL,sha256=chqeLhPBtPdrOoreR34YMcofSk3yWDQhkrsDJ2n48LU,106
|
|
116
|
+
xtgeo-4.12.0.dist-info/licenses/LICENSE.md,sha256=fTqV5eBpeAZO0_jit8j4Ref9ikBSlHJ8xwj5TLg7gFk,7817
|
|
117
|
+
xtgeo-4.12.0.dist-info/RECORD,,
|
|
File without changes
|