xtgeo 4.14.1__cp313-cp313-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.
- cxtgeo.py +558 -0
- cxtgeoPYTHON_wrap.c +19537 -0
- xtgeo/__init__.py +248 -0
- xtgeo/_cxtgeo.cp313-win_amd64.pyd +0 -0
- xtgeo/_internal.cp313-win_amd64.pyd +0 -0
- xtgeo/common/__init__.py +19 -0
- xtgeo/common/_angles.py +29 -0
- xtgeo/common/_xyz_enum.py +50 -0
- xtgeo/common/calc.py +396 -0
- xtgeo/common/constants.py +30 -0
- xtgeo/common/exceptions.py +42 -0
- xtgeo/common/log.py +93 -0
- xtgeo/common/sys.py +166 -0
- xtgeo/common/types.py +18 -0
- xtgeo/common/version.py +34 -0
- xtgeo/common/xtgeo_dialog.py +604 -0
- xtgeo/cube/__init__.py +9 -0
- xtgeo/cube/_cube_export.py +214 -0
- xtgeo/cube/_cube_import.py +532 -0
- xtgeo/cube/_cube_roxapi.py +180 -0
- xtgeo/cube/_cube_utils.py +287 -0
- xtgeo/cube/_cube_window_attributes.py +273 -0
- xtgeo/cube/cube1.py +1023 -0
- xtgeo/grid3d/__init__.py +15 -0
- xtgeo/grid3d/_ecl_grid.py +778 -0
- xtgeo/grid3d/_ecl_inte_head.py +152 -0
- xtgeo/grid3d/_ecl_logi_head.py +71 -0
- xtgeo/grid3d/_ecl_output_file.py +81 -0
- xtgeo/grid3d/_egrid.py +1004 -0
- xtgeo/grid3d/_find_gridprop_in_eclrun.py +625 -0
- xtgeo/grid3d/_grdecl_format.py +309 -0
- xtgeo/grid3d/_grdecl_grid.py +400 -0
- xtgeo/grid3d/_grid3d.py +29 -0
- xtgeo/grid3d/_grid3d_fence.py +284 -0
- xtgeo/grid3d/_grid3d_utils.py +228 -0
- xtgeo/grid3d/_grid_boundary.py +76 -0
- xtgeo/grid3d/_grid_etc1.py +1683 -0
- xtgeo/grid3d/_grid_export.py +222 -0
- xtgeo/grid3d/_grid_hybrid.py +50 -0
- xtgeo/grid3d/_grid_import.py +79 -0
- xtgeo/grid3d/_grid_import_ecl.py +101 -0
- xtgeo/grid3d/_grid_import_roff.py +135 -0
- xtgeo/grid3d/_grid_import_xtgcpgeom.py +375 -0
- xtgeo/grid3d/_grid_refine.py +258 -0
- xtgeo/grid3d/_grid_roxapi.py +292 -0
- xtgeo/grid3d/_grid_translate_coords.py +154 -0
- xtgeo/grid3d/_grid_wellzone.py +165 -0
- xtgeo/grid3d/_gridprop_export.py +202 -0
- xtgeo/grid3d/_gridprop_import_eclrun.py +164 -0
- xtgeo/grid3d/_gridprop_import_grdecl.py +132 -0
- xtgeo/grid3d/_gridprop_import_roff.py +52 -0
- xtgeo/grid3d/_gridprop_import_xtgcpprop.py +168 -0
- xtgeo/grid3d/_gridprop_lowlevel.py +171 -0
- xtgeo/grid3d/_gridprop_op1.py +272 -0
- xtgeo/grid3d/_gridprop_roxapi.py +301 -0
- xtgeo/grid3d/_gridprop_value_init.py +140 -0
- xtgeo/grid3d/_gridprops_import_eclrun.py +344 -0
- xtgeo/grid3d/_gridprops_import_roff.py +83 -0
- xtgeo/grid3d/_roff_grid.py +470 -0
- xtgeo/grid3d/_roff_parameter.py +303 -0
- xtgeo/grid3d/grid.py +3010 -0
- xtgeo/grid3d/grid_properties.py +699 -0
- xtgeo/grid3d/grid_property.py +1313 -0
- xtgeo/grid3d/types.py +15 -0
- xtgeo/interfaces/rms/__init__.py +18 -0
- xtgeo/interfaces/rms/_regular_surface.py +460 -0
- xtgeo/interfaces/rms/_rms_base.py +100 -0
- xtgeo/interfaces/rms/_rmsapi_package.py +69 -0
- xtgeo/interfaces/rms/rmsapi_utils.py +438 -0
- xtgeo/io/__init__.py +1 -0
- xtgeo/io/_file.py +603 -0
- xtgeo/metadata/__init__.py +17 -0
- xtgeo/metadata/metadata.py +435 -0
- xtgeo/roxutils/__init__.py +7 -0
- xtgeo/roxutils/_roxar_loader.py +54 -0
- xtgeo/roxutils/_roxutils_etc.py +122 -0
- xtgeo/roxutils/roxutils.py +207 -0
- xtgeo/surface/__init__.py +20 -0
- xtgeo/surface/_regsurf_boundary.py +26 -0
- xtgeo/surface/_regsurf_cube.py +210 -0
- xtgeo/surface/_regsurf_cube_window.py +391 -0
- xtgeo/surface/_regsurf_cube_window_v2.py +297 -0
- xtgeo/surface/_regsurf_cube_window_v3.py +360 -0
- xtgeo/surface/_regsurf_export.py +388 -0
- xtgeo/surface/_regsurf_grid3d.py +275 -0
- xtgeo/surface/_regsurf_gridding.py +347 -0
- xtgeo/surface/_regsurf_ijxyz_parser.py +278 -0
- xtgeo/surface/_regsurf_import.py +347 -0
- xtgeo/surface/_regsurf_lowlevel.py +122 -0
- xtgeo/surface/_regsurf_oper.py +538 -0
- xtgeo/surface/_regsurf_utils.py +81 -0
- xtgeo/surface/_surfs_import.py +43 -0
- xtgeo/surface/_zmap_parser.py +138 -0
- xtgeo/surface/regular_surface.py +3043 -0
- xtgeo/surface/surfaces.py +276 -0
- xtgeo/well/__init__.py +24 -0
- xtgeo/well/_blockedwell_roxapi.py +241 -0
- xtgeo/well/_blockedwells_roxapi.py +68 -0
- xtgeo/well/_well_aux.py +30 -0
- xtgeo/well/_well_io.py +327 -0
- xtgeo/well/_well_oper.py +483 -0
- xtgeo/well/_well_roxapi.py +304 -0
- xtgeo/well/_wellmarkers.py +486 -0
- xtgeo/well/_wells_utils.py +158 -0
- xtgeo/well/blocked_well.py +220 -0
- xtgeo/well/blocked_wells.py +134 -0
- xtgeo/well/well1.py +1516 -0
- xtgeo/well/wells.py +211 -0
- xtgeo/xyz/__init__.py +6 -0
- xtgeo/xyz/_polygons_oper.py +272 -0
- xtgeo/xyz/_xyz.py +758 -0
- xtgeo/xyz/_xyz_data.py +646 -0
- xtgeo/xyz/_xyz_io.py +737 -0
- xtgeo/xyz/_xyz_lowlevel.py +42 -0
- xtgeo/xyz/_xyz_oper.py +613 -0
- xtgeo/xyz/_xyz_roxapi.py +766 -0
- xtgeo/xyz/points.py +698 -0
- xtgeo/xyz/polygons.py +827 -0
- xtgeo-4.14.1.dist-info/METADATA +146 -0
- xtgeo-4.14.1.dist-info/RECORD +122 -0
- xtgeo-4.14.1.dist-info/WHEEL +5 -0
- xtgeo-4.14.1.dist-info/licenses/LICENSE.md +165 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import io
|
|
4
|
+
import json
|
|
5
|
+
import struct
|
|
6
|
+
from contextlib import ExitStack
|
|
7
|
+
from copy import deepcopy
|
|
8
|
+
from typing import IO, TYPE_CHECKING, Any, Literal
|
|
9
|
+
|
|
10
|
+
import h5py
|
|
11
|
+
import hdf5plugin
|
|
12
|
+
import roffio
|
|
13
|
+
|
|
14
|
+
from xtgeo.common import null_logger
|
|
15
|
+
|
|
16
|
+
from ._egrid import EGrid
|
|
17
|
+
from ._grdecl_grid import GrdeclGrid
|
|
18
|
+
from ._roff_grid import RoffGrid
|
|
19
|
+
|
|
20
|
+
logger = null_logger(__name__)
|
|
21
|
+
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
import numpy as np
|
|
24
|
+
|
|
25
|
+
from xtgeo.common.types import FileLike
|
|
26
|
+
from xtgeo.io._file import FileWrapper
|
|
27
|
+
from xtgeo.metadata.metata import MetaData
|
|
28
|
+
|
|
29
|
+
from .grid import Grid
|
|
30
|
+
|
|
31
|
+
# valid byte settings for xtgeo/hdf5 based export e.g. 441 means
|
|
32
|
+
# 4byte float for coord, 4byte float for zcorn, and 1 byte Int for actnums.
|
|
33
|
+
# Native XTGeo is 844
|
|
34
|
+
VALIDXTGFMT = (221, 421, 441, 444, 841, 844, 881, 884)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def export_roff(
|
|
38
|
+
grid: Grid, gfile: FileLike, roff_format: Literal["ascii", "binary"] = "binary"
|
|
39
|
+
) -> None:
|
|
40
|
+
"""Export grid to ROFF format."""
|
|
41
|
+
if roff_format == "binary":
|
|
42
|
+
RoffGrid.from_xtgeo_grid(grid).to_file(gfile, roff_format=roffio.Format.BINARY)
|
|
43
|
+
elif roff_format == "ascii":
|
|
44
|
+
RoffGrid.from_xtgeo_grid(grid).to_file(gfile, roff_format=roffio.Format.ASCII)
|
|
45
|
+
else:
|
|
46
|
+
raise ValueError(
|
|
47
|
+
"Incorrect format specifier in export_roff,"
|
|
48
|
+
f" expected 'binary' or 'ascii, got {roff_format}"
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
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"""
|
|
55
|
+
fileformat = "grdecl" if mode == 1 else "bgrdecl"
|
|
56
|
+
GrdeclGrid.from_xtgeo_grid(grid).to_file(gfile, fileformat=fileformat, rle=rle)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def export_egrid(grid: Grid, gfile: FileLike) -> None:
|
|
60
|
+
"""Export grid to Eclipse EGRID format, binary."""
|
|
61
|
+
EGrid.from_xtgeo_grid(grid).to_file(gfile, fileformat="egrid")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def export_fegrid(grid: Grid, gfile: FileLike) -> None:
|
|
65
|
+
"""Export grid to Eclipse FEGRID format, ascii."""
|
|
66
|
+
EGrid.from_xtgeo_grid(grid).to_file(gfile, fileformat="fegrid")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def export_xtgcpgeom(grid: Grid, gfile: FileWrapper, subformat: int = 844) -> None:
|
|
70
|
+
"""Export grid to binary XTGeo xtgcpgeom format, in prep. and experimental."""
|
|
71
|
+
logger.info("Export to native binary xtgeo...")
|
|
72
|
+
|
|
73
|
+
grid._set_xtgformat2()
|
|
74
|
+
logger.info("Export to native binary xtgeo...(2)")
|
|
75
|
+
|
|
76
|
+
grid.metadata.required = grid
|
|
77
|
+
meta = grid.metadata.get_metadata()
|
|
78
|
+
|
|
79
|
+
# subformat processing, indicating number of bytes per datatype
|
|
80
|
+
# here, 844 is native XTGeo (float64, float32, int32)
|
|
81
|
+
dtypecoo, dtypezco, dtypeact, meta = _define_dtypes(grid, subformat, meta)
|
|
82
|
+
|
|
83
|
+
coordsv, zcornsv, actnumsv = _transform_vectors(
|
|
84
|
+
grid, meta, dtypecoo, dtypezco, dtypeact
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
prevalues = (1, 1301, int(subformat), grid.ncol, grid.nrow, grid.nlay)
|
|
88
|
+
mystruct = struct.Struct("= i i i q q q")
|
|
89
|
+
hdr = mystruct.pack(*prevalues)
|
|
90
|
+
|
|
91
|
+
# Convert StringIO to BytesIO as this is a binary format
|
|
92
|
+
with ExitStack() as stack:
|
|
93
|
+
if isinstance(gfile.file, io.StringIO):
|
|
94
|
+
data = gfile.file.getvalue().encode("utf-8")
|
|
95
|
+
fout: IO[Any] = io.BytesIO(data)
|
|
96
|
+
elif isinstance(gfile.file, io.BytesIO):
|
|
97
|
+
fout = gfile.file
|
|
98
|
+
else:
|
|
99
|
+
fout = stack.enter_context(open(gfile.file, "wb"))
|
|
100
|
+
fout.write(hdr)
|
|
101
|
+
coordsv.tofile(fout)
|
|
102
|
+
zcornsv.tofile(fout)
|
|
103
|
+
actnumsv.tofile(fout)
|
|
104
|
+
fout.write("\nXTGMETA.v01\n".encode())
|
|
105
|
+
fout.write(json.dumps(meta).encode())
|
|
106
|
+
|
|
107
|
+
logger.info("Export to native binary xtgeo... done!")
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def export_hdf5_cpgeom(
|
|
111
|
+
grid: Grid,
|
|
112
|
+
gfile: FileWrapper,
|
|
113
|
+
compression: Literal["blosc"] | None = None,
|
|
114
|
+
chunks: bool = False,
|
|
115
|
+
subformat: int = 844,
|
|
116
|
+
) -> None:
|
|
117
|
+
"""Export grid to h5/hdf5, in prep. and experimental."""
|
|
118
|
+
grid._set_xtgformat2()
|
|
119
|
+
|
|
120
|
+
logger.debug("Export to hdf5 xtgeo layout...")
|
|
121
|
+
|
|
122
|
+
grid.metadata.required = grid
|
|
123
|
+
meta = grid.metadata.get_metadata()
|
|
124
|
+
|
|
125
|
+
if compression == "blosc":
|
|
126
|
+
compression = hdf5plugin.Blosc(
|
|
127
|
+
cname="blosclz", clevel=9, shuffle=hdf5plugin.Blosc.SHUFFLE
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
dtypecoo, dtypezco, dtypeact, meta = _define_dtypes(grid, subformat, meta)
|
|
131
|
+
coordsv, zcornsv, actnumsv = _transform_vectors(
|
|
132
|
+
grid, meta, dtypecoo, dtypezco, dtypeact
|
|
133
|
+
)
|
|
134
|
+
jmeta = json.dumps(meta).encode()
|
|
135
|
+
|
|
136
|
+
with h5py.File(gfile.name, "w") as fh5:
|
|
137
|
+
grp = fh5.create_group("CornerPointGeometry")
|
|
138
|
+
grp.create_dataset(
|
|
139
|
+
"coord",
|
|
140
|
+
data=coordsv,
|
|
141
|
+
compression=compression,
|
|
142
|
+
chunks=chunks or None,
|
|
143
|
+
)
|
|
144
|
+
grp.create_dataset(
|
|
145
|
+
"zcorn",
|
|
146
|
+
data=zcornsv,
|
|
147
|
+
compression=compression,
|
|
148
|
+
chunks=chunks or None,
|
|
149
|
+
)
|
|
150
|
+
grp.create_dataset(
|
|
151
|
+
"actnum",
|
|
152
|
+
data=actnumsv,
|
|
153
|
+
compression=compression,
|
|
154
|
+
chunks=chunks or None,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
grp.attrs["metadata"] = jmeta
|
|
158
|
+
grp.attrs["provider"] = "xtgeo"
|
|
159
|
+
grp.attrs["format-idcode"] = 1301
|
|
160
|
+
|
|
161
|
+
logger.debug("Export to hdf5 xtgeo layout... done!")
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _define_dtypes(grid: Grid, fmt: int, meta: MetaData) -> tuple[str, str, str, str]:
|
|
165
|
+
# subformat processing, indicating number of bytes per datatype
|
|
166
|
+
# here, 844 is native XTGeo (float64, float32, int32)
|
|
167
|
+
logger.debug("Define dtypes...")
|
|
168
|
+
|
|
169
|
+
newmeta = deepcopy(meta)
|
|
170
|
+
|
|
171
|
+
if int(fmt) not in VALIDXTGFMT:
|
|
172
|
+
raise ValueError(f"The subformat value is not valid, must be in: {VALIDXTGFMT}")
|
|
173
|
+
|
|
174
|
+
if fmt <= 444:
|
|
175
|
+
# the float() is for JSON serialization which cannot handle float32
|
|
176
|
+
newmeta["_required_"]["xshift"] = float(grid._coordsv[:, :, 0::3].mean())
|
|
177
|
+
newmeta["_required_"]["yshift"] = float(grid._coordsv[:, :, 1::3].mean())
|
|
178
|
+
newmeta["_required_"]["zshift"] = float(grid._zcornsv.mean())
|
|
179
|
+
|
|
180
|
+
nbytecoord, nbytezcorn, nbyteactnum = [int(nbyte) for nbyte in str(fmt)]
|
|
181
|
+
|
|
182
|
+
dtype_coord = "float" + str(nbytecoord * 8)
|
|
183
|
+
dtype_zcorn = "float" + str(nbytezcorn * 8)
|
|
184
|
+
dtype_actnum = "int" + str(nbyteactnum * 8)
|
|
185
|
+
|
|
186
|
+
logger.debug("Define dtypes... done!")
|
|
187
|
+
return dtype_coord, dtype_zcorn, dtype_actnum, newmeta
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _transform_vectors(
|
|
191
|
+
grid: Grid, meta: MetaData, dtypecoo: str, dtypezco: str, dtypeact: str
|
|
192
|
+
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
193
|
+
logger.debug("Transform vectors...")
|
|
194
|
+
|
|
195
|
+
xshift = meta["_required_"]["xshift"]
|
|
196
|
+
yshift = meta["_required_"]["yshift"]
|
|
197
|
+
zshift = meta["_required_"]["zshift"]
|
|
198
|
+
|
|
199
|
+
if xshift != 0.0 or yshift != 0.0 or zshift != 0.0:
|
|
200
|
+
coordsv = grid._coordsv.copy()
|
|
201
|
+
zcornsv = grid._zcornsv.copy()
|
|
202
|
+
actnumsv = grid._actnumsv.copy()
|
|
203
|
+
coordsv[:, :, 0::3] -= xshift
|
|
204
|
+
coordsv[:, :, 1::3] -= yshift
|
|
205
|
+
coordsv[:, :, 2::3] -= zshift
|
|
206
|
+
zcornsv -= zshift
|
|
207
|
+
else:
|
|
208
|
+
coordsv = grid._coordsv
|
|
209
|
+
zcornsv = grid._zcornsv
|
|
210
|
+
actnumsv = grid._actnumsv
|
|
211
|
+
|
|
212
|
+
if dtypecoo != "float64":
|
|
213
|
+
coordsv = coordsv.astype(dtypecoo)
|
|
214
|
+
|
|
215
|
+
if dtypecoo != "float32":
|
|
216
|
+
zcornsv = zcornsv.astype(dtypezco)
|
|
217
|
+
|
|
218
|
+
if dtypecoo != "int32":
|
|
219
|
+
actnumsv = actnumsv.astype(dtypeact)
|
|
220
|
+
|
|
221
|
+
logger.debug("Transform vectors... done!")
|
|
222
|
+
return coordsv, zcornsv, actnumsv
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
import xtgeo._internal as _internal # type: ignore
|
|
8
|
+
from xtgeo.common import null_logger
|
|
9
|
+
|
|
10
|
+
logger = null_logger(__name__)
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from xtgeo.grid3d import Grid, GridProperty
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def make_hybridgrid(
|
|
17
|
+
self: Grid,
|
|
18
|
+
nhdiv: int = 10,
|
|
19
|
+
toplevel: float = 1000.0,
|
|
20
|
+
bottomlevel: float = 1100.0,
|
|
21
|
+
region: GridProperty | None = None,
|
|
22
|
+
region_number: int | None = None,
|
|
23
|
+
) -> None:
|
|
24
|
+
"""Make hybrid grid."""
|
|
25
|
+
self._set_xtgformat2()
|
|
26
|
+
|
|
27
|
+
newnlay = self.nlay * 2 + nhdiv
|
|
28
|
+
|
|
29
|
+
grid3d_cpp = _internal.grid3d.Grid(self)
|
|
30
|
+
region_array = (
|
|
31
|
+
region.values.astype(np.int32)
|
|
32
|
+
if region
|
|
33
|
+
else np.empty((0, 0, 0), dtype=np.int32)
|
|
34
|
+
)
|
|
35
|
+
hyb_zcornsv, hyb_actnumsv = grid3d_cpp.convert_to_hybrid_grid(
|
|
36
|
+
toplevel,
|
|
37
|
+
bottomlevel,
|
|
38
|
+
nhdiv,
|
|
39
|
+
region_array,
|
|
40
|
+
int(region_number) if region_number is not None else -1,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# when a hybridgrid is made, the current subrid settings lose relevance, hence
|
|
44
|
+
# it is forced set to None
|
|
45
|
+
self.subgrids = None
|
|
46
|
+
|
|
47
|
+
# update the grid in place
|
|
48
|
+
self._nlay = newnlay
|
|
49
|
+
self._zcornsv = hyb_zcornsv
|
|
50
|
+
self._actnumsv = hyb_actnumsv.astype(np.int32)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""Grid import functions for various formats."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from xtgeo.common.exceptions import InvalidFileFormatError
|
|
9
|
+
from xtgeo.common.log import null_logger
|
|
10
|
+
from xtgeo.io._file import FileFormat, FileWrapper
|
|
11
|
+
|
|
12
|
+
from . import _grid_import_ecl, _grid_import_roff, _grid_import_xtgcpgeom
|
|
13
|
+
|
|
14
|
+
logger = null_logger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def from_file(
|
|
18
|
+
gfile: FileWrapper,
|
|
19
|
+
fformat: FileFormat,
|
|
20
|
+
**kwargs: Any,
|
|
21
|
+
) -> dict[str, Any]:
|
|
22
|
+
"""Import grid geometry from file, and makes an instance of this class.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
dictionary of keyword arguments to be used in Grid constructor.
|
|
26
|
+
"""
|
|
27
|
+
if not isinstance(gfile, FileWrapper):
|
|
28
|
+
raise RuntimeError("Error gfile must be a FileWrapper instance")
|
|
29
|
+
|
|
30
|
+
result: dict[str, Any] = {
|
|
31
|
+
"filesrc": gfile.name,
|
|
32
|
+
}
|
|
33
|
+
gfile.check_file(raiseerror=IOError, raisetext=f"Cannot access file {gfile.name}")
|
|
34
|
+
|
|
35
|
+
if fformat in (FileFormat.ROFF_BINARY, FileFormat.ROFF_ASCII):
|
|
36
|
+
result.update(_grid_import_roff.import_roff(gfile, **kwargs))
|
|
37
|
+
elif fformat in (FileFormat.EGRID, FileFormat.FEGRID):
|
|
38
|
+
result.update(
|
|
39
|
+
_grid_import_ecl.import_ecl_egrid(
|
|
40
|
+
gfile,
|
|
41
|
+
fileformat=fformat,
|
|
42
|
+
**kwargs,
|
|
43
|
+
)
|
|
44
|
+
)
|
|
45
|
+
elif fformat == FileFormat.GRDECL:
|
|
46
|
+
result.update(_grid_import_ecl.import_ecl_grdecl(gfile, **kwargs))
|
|
47
|
+
elif fformat == FileFormat.BGRDECL:
|
|
48
|
+
result.update(_grid_import_ecl.import_ecl_bgrdecl(gfile, **kwargs))
|
|
49
|
+
elif fformat == FileFormat.XTG:
|
|
50
|
+
result.update(_grid_import_xtgcpgeom.import_xtgcpgeom(gfile, **kwargs))
|
|
51
|
+
elif fformat == FileFormat.HDF:
|
|
52
|
+
result.update(_grid_import_xtgcpgeom.import_hdf5_cpgeom(gfile, **kwargs))
|
|
53
|
+
else:
|
|
54
|
+
extensions = FileFormat.extensions_string(
|
|
55
|
+
[
|
|
56
|
+
FileFormat.ROFF_BINARY,
|
|
57
|
+
FileFormat.ROFF_ASCII,
|
|
58
|
+
FileFormat.EGRID,
|
|
59
|
+
FileFormat.FEGRID,
|
|
60
|
+
FileFormat.GRDECL,
|
|
61
|
+
FileFormat.BGRDECL,
|
|
62
|
+
FileFormat.XTG,
|
|
63
|
+
FileFormat.HDF,
|
|
64
|
+
]
|
|
65
|
+
)
|
|
66
|
+
raise InvalidFileFormatError(
|
|
67
|
+
f"File format {fformat} is invalid for type Grid. "
|
|
68
|
+
f"Supported formats are {extensions}."
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
if gfile.memstream:
|
|
72
|
+
result["name"] = "unknown"
|
|
73
|
+
else:
|
|
74
|
+
# Mypy does not know that if gfile.memstream -> False
|
|
75
|
+
# then .file must be Path.
|
|
76
|
+
assert isinstance(gfile.file, Path)
|
|
77
|
+
result["name"] = gfile.file.stem
|
|
78
|
+
|
|
79
|
+
return result
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""Grid import functions for Eclipse, new approach (i.e. version 2)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
|
+
|
|
7
|
+
from xtgeo.common import null_logger
|
|
8
|
+
from xtgeo.grid3d._egrid import EGrid, RockModel
|
|
9
|
+
from xtgeo.grid3d._grdecl_grid import GrdeclGrid, GridRelative
|
|
10
|
+
from xtgeo.grid3d.grid_properties import GridProperties, gridproperties_from_file
|
|
11
|
+
from xtgeo.io._file import FileFormat, FileWrapper
|
|
12
|
+
|
|
13
|
+
logger = null_logger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from xtgeo.grid3d.grid import Grid
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def import_ecl_egrid(
|
|
21
|
+
gfile: FileWrapper,
|
|
22
|
+
relative_to: GridRelative = GridRelative.MAP,
|
|
23
|
+
fileformat: FileFormat = FileFormat.EGRID,
|
|
24
|
+
) -> dict[str, Any]:
|
|
25
|
+
egrid = EGrid.from_file(gfile.file, fileformat=fileformat)
|
|
26
|
+
result = grid_from_ecl_grid(egrid, relative_to=relative_to)
|
|
27
|
+
|
|
28
|
+
if egrid.egrid_head.file_head.rock_model == RockModel.DUAL_POROSITY:
|
|
29
|
+
result["dualporo"] = True
|
|
30
|
+
result["dualperm"] = False
|
|
31
|
+
elif egrid.egrid_head.file_head.rock_model == RockModel.DUAL_PERMEABILITY:
|
|
32
|
+
result["dualporo"] = True
|
|
33
|
+
result["dualperm"] = True
|
|
34
|
+
|
|
35
|
+
return result
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def import_ecl_run(
|
|
39
|
+
groot: str,
|
|
40
|
+
ecl_grid: Grid,
|
|
41
|
+
initprops: list[str] | None = None,
|
|
42
|
+
restartprops: list[str] | None = None,
|
|
43
|
+
restartdates: list[str] | None = None,
|
|
44
|
+
) -> None:
|
|
45
|
+
"""Import Eclipse run suite: EGrid and properties from INIT and UNRST.
|
|
46
|
+
For the INIT and UNRST files, property dates shall be selected."""
|
|
47
|
+
ecl_init = FileWrapper(f"{groot}.INIT")
|
|
48
|
+
ecl_rsta = FileWrapper(f"{groot}.UNRST")
|
|
49
|
+
grdprops = GridProperties()
|
|
50
|
+
|
|
51
|
+
# import the init properties unless list is empty
|
|
52
|
+
if initprops:
|
|
53
|
+
initprops_ = gridproperties_from_file(
|
|
54
|
+
ecl_init.name, names=initprops, fformat="init", dates=None, grid=ecl_grid
|
|
55
|
+
)
|
|
56
|
+
if initprops_.props:
|
|
57
|
+
grdprops.append_props(initprops_.props)
|
|
58
|
+
|
|
59
|
+
# import the restart properties for dates unless lists are empty
|
|
60
|
+
if restartprops and restartdates:
|
|
61
|
+
restartprops_ = gridproperties_from_file(
|
|
62
|
+
ecl_rsta.name,
|
|
63
|
+
names=restartprops,
|
|
64
|
+
fformat="unrst",
|
|
65
|
+
dates=restartdates,
|
|
66
|
+
grid=ecl_grid,
|
|
67
|
+
)
|
|
68
|
+
if restartprops_.props:
|
|
69
|
+
grdprops.append_props(restartprops_.props)
|
|
70
|
+
ecl_grid.gridprops = grdprops
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def import_ecl_grdecl(
|
|
74
|
+
gfile: FileWrapper, relative_to: GridRelative = GridRelative.MAP
|
|
75
|
+
) -> dict[str, Any]:
|
|
76
|
+
"""Import grdecl format."""
|
|
77
|
+
grdecl_grid = GrdeclGrid.from_file(gfile.file, fileformat=FileFormat.GRDECL)
|
|
78
|
+
return grid_from_ecl_grid(grdecl_grid, relative_to=relative_to)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def import_ecl_bgrdecl(
|
|
82
|
+
gfile: FileWrapper, relative_to: GridRelative = GridRelative.MAP
|
|
83
|
+
) -> dict[str, Any]:
|
|
84
|
+
"""Import binary files with GRDECL layout."""
|
|
85
|
+
grdecl_grid = GrdeclGrid.from_file(gfile.file, fileformat=FileFormat.BGRDECL)
|
|
86
|
+
return grid_from_ecl_grid(grdecl_grid, relative_to=relative_to)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def grid_from_ecl_grid(
|
|
90
|
+
ecl_grid: EGrid, relative_to: GridRelative = GridRelative.MAP
|
|
91
|
+
) -> dict[str, Any]:
|
|
92
|
+
result = {}
|
|
93
|
+
result["coordsv"] = ecl_grid.xtgeo_coord(relative_to=relative_to)
|
|
94
|
+
result["zcornsv"] = ecl_grid.xtgeo_zcorn(relative_to=relative_to)
|
|
95
|
+
result["actnumsv"] = ecl_grid.xtgeo_actnum()
|
|
96
|
+
if relative_to == GridRelative.MAP and ecl_grid.map_axis_units is not None:
|
|
97
|
+
result["units"] = ecl_grid.map_axis_units
|
|
98
|
+
else:
|
|
99
|
+
result["units"] = ecl_grid.grid_units
|
|
100
|
+
|
|
101
|
+
return result
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"""Private module, Grid Import private functions for ROFF format."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import pathlib
|
|
6
|
+
import tempfile
|
|
7
|
+
import warnings
|
|
8
|
+
from contextlib import contextmanager
|
|
9
|
+
from io import BufferedReader, BytesIO
|
|
10
|
+
from typing import TYPE_CHECKING, Generator
|
|
11
|
+
|
|
12
|
+
from xtgeo.common import null_logger
|
|
13
|
+
|
|
14
|
+
from ._roff_grid import RoffGrid
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from numpy.typing import NDArray
|
|
18
|
+
|
|
19
|
+
from xtgeo.common.types import FileLike
|
|
20
|
+
from xtgeo.io._file import FileWrapper
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
logger = null_logger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def match_xtgeo_214_header(header: bytes) -> bool:
|
|
27
|
+
"""
|
|
28
|
+
Check whether the start of a binary file matches
|
|
29
|
+
the problematic xtgeo version 2.14 contents.
|
|
30
|
+
|
|
31
|
+
Params:
|
|
32
|
+
header: the start of a file
|
|
33
|
+
|
|
34
|
+
"""
|
|
35
|
+
first_part_match = header.startswith(
|
|
36
|
+
b"roff-bin\0"
|
|
37
|
+
b"#ROFF file#\0"
|
|
38
|
+
b"#Creator: CXTGeo subsystem of XTGeo by JCR#\0"
|
|
39
|
+
b"tag\0filedata\0"
|
|
40
|
+
b"int\0byteswaptest\0"
|
|
41
|
+
)
|
|
42
|
+
problem_area_match = header[99:].startswith(
|
|
43
|
+
b"char\0filetype\0grid\0char\0creationDate\0UNKNOWNendtag"
|
|
44
|
+
)
|
|
45
|
+
return first_part_match and problem_area_match
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def replace_xtgeo_214_header(header: bytes) -> bytes:
|
|
49
|
+
"""
|
|
50
|
+
Given that match_xtgeo_214_header(header), inserts
|
|
51
|
+
a \0 in the correct place.
|
|
52
|
+
"""
|
|
53
|
+
return header[:143] + b"\0" + header[143:]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@contextmanager
|
|
57
|
+
def handle_deprecated_xtgeo_roff_file(
|
|
58
|
+
filelike: FileLike,
|
|
59
|
+
) -> Generator[
|
|
60
|
+
FileLike | tempfile._TemporaryFileWrapper[bytes],
|
|
61
|
+
None,
|
|
62
|
+
None,
|
|
63
|
+
]:
|
|
64
|
+
"""
|
|
65
|
+
A contextmanager that inplace fixes grid roff files
|
|
66
|
+
that were written by XTGeo prior to version 2.15. This
|
|
67
|
+
backwards compatibility should eventually be deprecated
|
|
68
|
+
|
|
69
|
+
Example::
|
|
70
|
+
|
|
71
|
+
with handle_deprecated_xtgeo_roff_file("grid.roff") as converted_grid:
|
|
72
|
+
roff_grid = RoffGrid.from_file(converted_file)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
Before version 2.15, grids with _xtgformat=1 would be written with missing
|
|
76
|
+
'\\0' after the creationDate. However, it would also silently read that
|
|
77
|
+
file without any issues in the final product. roffio is less leanient when
|
|
78
|
+
it comes to the format it will accept and so does not recover. Luckily, the
|
|
79
|
+
creationDate was set to 'UNKNOWN' so we can be fairly certain we replace
|
|
80
|
+
correctly.
|
|
81
|
+
|
|
82
|
+
"""
|
|
83
|
+
inhandle: FileLike | BufferedReader = filelike
|
|
84
|
+
close = False
|
|
85
|
+
if isinstance(filelike, (str, pathlib.Path)):
|
|
86
|
+
inhandle = open(filelike, "rb") # noqa: SIM115
|
|
87
|
+
close = True
|
|
88
|
+
|
|
89
|
+
# MYPY is uanble to figur out that isinstance check
|
|
90
|
+
# above means we have an IO-based class now.
|
|
91
|
+
assert isinstance(inhandle, (BufferedReader, BytesIO))
|
|
92
|
+
goback = inhandle.tell()
|
|
93
|
+
header = inhandle.read(200)
|
|
94
|
+
|
|
95
|
+
if match_xtgeo_214_header(header):
|
|
96
|
+
name = "buffer"
|
|
97
|
+
if hasattr(filelike, "name"):
|
|
98
|
+
name = filelike.name
|
|
99
|
+
warnings.warn(
|
|
100
|
+
f"The roff file {name} contains nonstandard but harmless roff"
|
|
101
|
+
" format detail written by XTGeo version <=2.14. Reading of such files"
|
|
102
|
+
" is deprecated, consider re-exporting the file with XTGeo version >=2.15.3"
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
new_header = replace_xtgeo_214_header(header)
|
|
106
|
+
|
|
107
|
+
with tempfile.NamedTemporaryFile() as outhandle:
|
|
108
|
+
outhandle.write(new_header)
|
|
109
|
+
inhandle.seek(200)
|
|
110
|
+
outhandle.write(inhandle.read())
|
|
111
|
+
outhandle.seek(0)
|
|
112
|
+
yield outhandle
|
|
113
|
+
if close:
|
|
114
|
+
inhandle.close()
|
|
115
|
+
else:
|
|
116
|
+
inhandle.seek(goback)
|
|
117
|
+
|
|
118
|
+
else:
|
|
119
|
+
if close:
|
|
120
|
+
inhandle.close()
|
|
121
|
+
else:
|
|
122
|
+
inhandle.seek(goback)
|
|
123
|
+
|
|
124
|
+
yield filelike
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def import_roff(gfile: FileWrapper) -> dict[str, NDArray | dict[str, range] | None]:
|
|
128
|
+
with handle_deprecated_xtgeo_roff_file(gfile._file) as converted_file:
|
|
129
|
+
roff_grid = RoffGrid.from_file(converted_file)
|
|
130
|
+
return {
|
|
131
|
+
"actnumsv": roff_grid.xtgeo_actnum(),
|
|
132
|
+
"coordsv": roff_grid.xtgeo_coord(),
|
|
133
|
+
"zcornsv": roff_grid.xtgeo_zcorn(),
|
|
134
|
+
"subgrids": roff_grid.xtgeo_subgrids(),
|
|
135
|
+
}
|