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,435 @@
|
|
|
1
|
+
"""The metadata module, currently experimental.
|
|
2
|
+
|
|
3
|
+
The metadata works through the various datatypes in XTGeo. For example::
|
|
4
|
+
|
|
5
|
+
>>> import xtgeo
|
|
6
|
+
>>> surf = xtgeo.surface_from_file(surface_dir + "/topreek_rota.gri")
|
|
7
|
+
>>> surf.metadata.required
|
|
8
|
+
dict([('ncol', 554),...
|
|
9
|
+
>>> surf.metadata.optional.mean = surf.values.mean()
|
|
10
|
+
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from typing import TYPE_CHECKING, Any
|
|
16
|
+
|
|
17
|
+
import xtgeo
|
|
18
|
+
from xtgeo.common.constants import UNDEF
|
|
19
|
+
from xtgeo.common.log import null_logger
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from datetime import datetime
|
|
23
|
+
|
|
24
|
+
from xtgeo.cube.cube1 import Cube
|
|
25
|
+
from xtgeo.grid3d.grid import Grid, GridProperty
|
|
26
|
+
from xtgeo.surface.regular_surface import RegularSurface
|
|
27
|
+
from xtgeo.well.well1 import Well
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
logger = null_logger(__name__)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class _OptionalMetaData:
|
|
34
|
+
"""Optional metadata are not required, but keys are limited.
|
|
35
|
+
|
|
36
|
+
A limited sets of possible keys are available, and they can modified. This
|
|
37
|
+
class can also have validation methods.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
__slots__ = (
|
|
41
|
+
"_name",
|
|
42
|
+
"_shortname",
|
|
43
|
+
"_datatype",
|
|
44
|
+
"_md5sum",
|
|
45
|
+
"_description",
|
|
46
|
+
"_crs",
|
|
47
|
+
"_datetime",
|
|
48
|
+
"_deltadatetime",
|
|
49
|
+
"_visuals",
|
|
50
|
+
"_domain",
|
|
51
|
+
"_user",
|
|
52
|
+
"_field",
|
|
53
|
+
"_source",
|
|
54
|
+
"_modelid",
|
|
55
|
+
"_ensembleid",
|
|
56
|
+
"_units",
|
|
57
|
+
"_mean",
|
|
58
|
+
"_stddev",
|
|
59
|
+
"_percentiles",
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
def __init__(self) -> None:
|
|
63
|
+
self._name = "A Longer Descriptive Name e.g. from SMDA"
|
|
64
|
+
self._shortname = "TheShortName"
|
|
65
|
+
self._datatype: str | None = None
|
|
66
|
+
self._md5sum: str | None = None
|
|
67
|
+
self._description = "Some description"
|
|
68
|
+
self._crs = None
|
|
69
|
+
self._datetime: datetime | str | None = None
|
|
70
|
+
self._deltadatetime = None
|
|
71
|
+
self._visuals = {"colortable": "rainbow", "lower": None, "upper": None}
|
|
72
|
+
self._domain = "depth"
|
|
73
|
+
self._units = "metric"
|
|
74
|
+
self._mean = None
|
|
75
|
+
self._stddev = None
|
|
76
|
+
self._percentiles = None
|
|
77
|
+
self._user = "anonymous"
|
|
78
|
+
self._field = "nofield"
|
|
79
|
+
self._ensembleid = None
|
|
80
|
+
self._modelid = None
|
|
81
|
+
self._source = "unknown"
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def name(self) -> str:
|
|
85
|
+
return self._name
|
|
86
|
+
|
|
87
|
+
@name.setter
|
|
88
|
+
def name(self, newname: str) -> None:
|
|
89
|
+
# TODO: validation
|
|
90
|
+
self._name = newname
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def datetime(self) -> datetime | str | None:
|
|
94
|
+
return self._datetime
|
|
95
|
+
|
|
96
|
+
@datetime.setter
|
|
97
|
+
def datetime(self, newdate: datetime | str) -> None:
|
|
98
|
+
# TODO: validation
|
|
99
|
+
self._datetime = newdate
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def shortname(self) -> str:
|
|
103
|
+
return self._shortname
|
|
104
|
+
|
|
105
|
+
@shortname.setter
|
|
106
|
+
def shortname(self, newname: str) -> None:
|
|
107
|
+
if not isinstance(newname, str):
|
|
108
|
+
raise ValueError("The shortname must be a string.")
|
|
109
|
+
if len(newname) >= 32:
|
|
110
|
+
raise ValueError("The shortname length must less or equal 32 letters.")
|
|
111
|
+
|
|
112
|
+
self._shortname = newname
|
|
113
|
+
|
|
114
|
+
@property
|
|
115
|
+
def description(self) -> str:
|
|
116
|
+
return self._description
|
|
117
|
+
|
|
118
|
+
@description.setter
|
|
119
|
+
def description(self, newstr: str) -> None:
|
|
120
|
+
if not isinstance(newstr, str):
|
|
121
|
+
raise ValueError("The description must be a string.")
|
|
122
|
+
if len(newstr) >= 64:
|
|
123
|
+
raise ValueError("The description length must less or equal 64 letters.")
|
|
124
|
+
invalids = r"/$<>[]:\&%"
|
|
125
|
+
if set(invalids).intersection(newstr):
|
|
126
|
+
raise ValueError("The description contains invalid characters such as /.")
|
|
127
|
+
|
|
128
|
+
self._description = newstr
|
|
129
|
+
|
|
130
|
+
@property
|
|
131
|
+
def md5sum(self) -> str | None:
|
|
132
|
+
"""Set or get the md5 checksum of file content.
|
|
133
|
+
|
|
134
|
+
See generate_hash() method in e.g. RegularSurface.
|
|
135
|
+
"""
|
|
136
|
+
return self._md5sum
|
|
137
|
+
|
|
138
|
+
@md5sum.setter
|
|
139
|
+
def md5sum(self, newhash: str) -> None:
|
|
140
|
+
# TODO: validation
|
|
141
|
+
self._md5sum = newhash
|
|
142
|
+
|
|
143
|
+
def get_meta(self) -> dict[str, Any]:
|
|
144
|
+
"""Return metadata as an dict."""
|
|
145
|
+
meta = {}
|
|
146
|
+
for key in self.__slots__:
|
|
147
|
+
newkey = key[1:]
|
|
148
|
+
meta[newkey] = getattr(self, key)
|
|
149
|
+
|
|
150
|
+
return meta
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class MetaData:
|
|
154
|
+
"""Generic metadata class, not intended to be used directly."""
|
|
155
|
+
|
|
156
|
+
def __init__(self) -> None:
|
|
157
|
+
"""Generic metadata class __init__, not be used directly."""
|
|
158
|
+
self._required: dict[str, Any] = {}
|
|
159
|
+
self._optional = _OptionalMetaData()
|
|
160
|
+
self._freeform = {}
|
|
161
|
+
|
|
162
|
+
self._freeform = {"smda": "whatever"}
|
|
163
|
+
|
|
164
|
+
def get_metadata(self) -> dict[str, Any]:
|
|
165
|
+
"""Get all metadata that are present."""
|
|
166
|
+
allmeta = {}
|
|
167
|
+
allmeta["_required_"] = self._required
|
|
168
|
+
allmeta["_optional_"] = self._optional.get_meta()
|
|
169
|
+
allmeta["_freeform_"] = self._freeform
|
|
170
|
+
return allmeta
|
|
171
|
+
|
|
172
|
+
@property
|
|
173
|
+
def optional(self) -> dict[str, Any]:
|
|
174
|
+
"""Return or set optional metadata.
|
|
175
|
+
|
|
176
|
+
When setting optional names, it can be done in several ways...
|
|
177
|
+
|
|
178
|
+
surf.metadata.optional.name = "New name"
|
|
179
|
+
"""
|
|
180
|
+
# return a copy of the instance; the reason for this is to avoid manipulation
|
|
181
|
+
# without validation
|
|
182
|
+
return self._optional.get_meta()
|
|
183
|
+
|
|
184
|
+
@optional.setter
|
|
185
|
+
def optional(self, indict: dict[str, Any]) -> None:
|
|
186
|
+
# setting the optional key, including validation
|
|
187
|
+
if not isinstance(indict, dict):
|
|
188
|
+
raise ValueError(f"Input must be a dictionary, not a {type(indict)}")
|
|
189
|
+
|
|
190
|
+
for key, value in indict.items():
|
|
191
|
+
setattr(self._optional, "_" + key, value)
|
|
192
|
+
|
|
193
|
+
@property
|
|
194
|
+
def opt(self) -> _OptionalMetaData:
|
|
195
|
+
"""Return the metadata optional instance.
|
|
196
|
+
|
|
197
|
+
This makes access to the _OptionalMetaData instance.
|
|
198
|
+
|
|
199
|
+
Example::
|
|
200
|
+
>>> import xtgeo
|
|
201
|
+
>>> surf = xtgeo.surface_from_file(surface_dir + "/topreek_rota.gri")
|
|
202
|
+
>>> surf.metadata.opt.shortname = "TopValysar"
|
|
203
|
+
|
|
204
|
+
"""
|
|
205
|
+
return self._optional
|
|
206
|
+
|
|
207
|
+
@property
|
|
208
|
+
def freeform(self) -> dict[str, Any]:
|
|
209
|
+
"""Get or set the current freeform metadata dictionary."""
|
|
210
|
+
return self._freeform
|
|
211
|
+
|
|
212
|
+
@freeform.setter
|
|
213
|
+
def freeform(self, adict: dict[str, Any]) -> None:
|
|
214
|
+
"""Freeform is a whatever you want set, without any validation."""
|
|
215
|
+
self._freeform = adict.copy()
|
|
216
|
+
|
|
217
|
+
def generate_fmu_name(self) -> str:
|
|
218
|
+
"""Generate FMU name on form xxxx--yyyy--date but no suffix."""
|
|
219
|
+
fname = ""
|
|
220
|
+
first = "prefix"
|
|
221
|
+
fname += first
|
|
222
|
+
fname += "--"
|
|
223
|
+
fname += self._optional._shortname.lower()
|
|
224
|
+
if self._optional._datetime:
|
|
225
|
+
fname += "--"
|
|
226
|
+
fname += str(self._optional._datetime)
|
|
227
|
+
return fname
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
class MetaDataRegularSurface(MetaData):
|
|
231
|
+
"""Metadata for RegularSurface() objects."""
|
|
232
|
+
|
|
233
|
+
REQUIRED: dict[str, Any] = {
|
|
234
|
+
"ncol": 1,
|
|
235
|
+
"nrow": 1,
|
|
236
|
+
"xori": 0.0,
|
|
237
|
+
"yori": 0.0,
|
|
238
|
+
"xinc": 1.0,
|
|
239
|
+
"yinc": 1.0,
|
|
240
|
+
"yflip": 1,
|
|
241
|
+
"rotation": 0.0,
|
|
242
|
+
"undef": UNDEF,
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
def __init__(self) -> None:
|
|
246
|
+
"""Docstring."""
|
|
247
|
+
super().__init__()
|
|
248
|
+
self._required = self.REQUIRED
|
|
249
|
+
self._optional._datatype = "Regular Surface"
|
|
250
|
+
|
|
251
|
+
@property
|
|
252
|
+
def required(self) -> dict[str, Any]:
|
|
253
|
+
"""Get of set required metadata."""
|
|
254
|
+
return self._required
|
|
255
|
+
|
|
256
|
+
@required.setter
|
|
257
|
+
def required(self, obj: RegularSurface) -> None:
|
|
258
|
+
if not isinstance(obj, xtgeo.RegularSurface): # type: ignore[attr-defined]
|
|
259
|
+
raise ValueError("Input object is not a RegularSurface()")
|
|
260
|
+
|
|
261
|
+
self._required["ncol"] = obj.ncol
|
|
262
|
+
self._required["nrow"] = obj.nrow
|
|
263
|
+
self._required["xori"] = obj.xori
|
|
264
|
+
self._required["yori"] = obj.yori
|
|
265
|
+
self._required["xinc"] = obj.xinc
|
|
266
|
+
self._required["yinc"] = obj.yinc
|
|
267
|
+
self._required["yflip"] = obj.yflip
|
|
268
|
+
self._required["rotation"] = obj.rotation
|
|
269
|
+
self._required["undef"] = obj.undef
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
class MetaDataRegularCube(MetaData):
|
|
273
|
+
"""Metadata for Cube() objects."""
|
|
274
|
+
|
|
275
|
+
# allowed optional keys; these are set to avoid discussions
|
|
276
|
+
REQUIRED: dict[str, Any] = {
|
|
277
|
+
"ncol": 1,
|
|
278
|
+
"nrow": 1,
|
|
279
|
+
"nlay": 1,
|
|
280
|
+
"xori": 0.0,
|
|
281
|
+
"yori": 0.0,
|
|
282
|
+
"zori": 0.0,
|
|
283
|
+
"xinc": 1.0,
|
|
284
|
+
"yinc": 1.0,
|
|
285
|
+
"zinc": 1.0,
|
|
286
|
+
"yflip": 1,
|
|
287
|
+
"zflip": 1,
|
|
288
|
+
"rotation": 0.0,
|
|
289
|
+
"undef": UNDEF,
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
def __init__(self) -> None:
|
|
293
|
+
"""Docstring."""
|
|
294
|
+
super().__init__()
|
|
295
|
+
self._required = self.REQUIRED
|
|
296
|
+
self._optional._datatype = "Regular Cube"
|
|
297
|
+
|
|
298
|
+
@property
|
|
299
|
+
def required(self) -> dict[str, Any]:
|
|
300
|
+
"""Get of set required metadata."""
|
|
301
|
+
return self._required
|
|
302
|
+
|
|
303
|
+
@required.setter
|
|
304
|
+
def required(self, obj: Cube) -> None:
|
|
305
|
+
if not isinstance(obj, xtgeo.Cube): # type: ignore[attr-defined]
|
|
306
|
+
raise ValueError("Input object is not a regular Cube()")
|
|
307
|
+
|
|
308
|
+
self._required["ncol"] = obj.ncol
|
|
309
|
+
self._required["nrow"] = obj.nrow
|
|
310
|
+
self._required["nlay"] = obj.nlay
|
|
311
|
+
self._required["xori"] = obj.xori
|
|
312
|
+
self._required["yori"] = obj.yori
|
|
313
|
+
self._required["zori"] = obj.zori
|
|
314
|
+
self._required["xinc"] = obj.xinc
|
|
315
|
+
self._required["yinc"] = obj.yinc
|
|
316
|
+
self._required["zinc"] = obj.zinc
|
|
317
|
+
self._required["yflip"] = obj.yflip
|
|
318
|
+
self._required["zflip"] = 1
|
|
319
|
+
self._required["rotation"] = obj.rotation
|
|
320
|
+
self._required["undef"] = obj.undef
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
class MetaDataCPGeometry(MetaData):
|
|
324
|
+
"""Metadata for Grid() objects of type simplified CornerPoint Geometry."""
|
|
325
|
+
|
|
326
|
+
REQUIRED: dict[str, Any] = {
|
|
327
|
+
"ncol": 1,
|
|
328
|
+
"nrow": 1,
|
|
329
|
+
"nlay": 1,
|
|
330
|
+
"xshift": 0.0,
|
|
331
|
+
"yshift": 0.0,
|
|
332
|
+
"zshift": 0.0,
|
|
333
|
+
"xscale": 1.0,
|
|
334
|
+
"yscale": 1.0,
|
|
335
|
+
"zscale": 1.0,
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
def __init__(self) -> None:
|
|
339
|
+
"""Docstring."""
|
|
340
|
+
super().__init__()
|
|
341
|
+
self._required = self.REQUIRED
|
|
342
|
+
self._optional._datatype = "CornerPoint GridGeometry"
|
|
343
|
+
|
|
344
|
+
@property
|
|
345
|
+
def required(self) -> dict[str, Any]:
|
|
346
|
+
"""Get of set required metadata."""
|
|
347
|
+
return self._required
|
|
348
|
+
|
|
349
|
+
@required.setter
|
|
350
|
+
def required(self, obj: Grid) -> None:
|
|
351
|
+
if not isinstance(obj, xtgeo.Grid): # type: ignore[attr-defined]
|
|
352
|
+
raise ValueError("Input object is not a Grid()")
|
|
353
|
+
|
|
354
|
+
self._required["ncol"] = obj.ncol
|
|
355
|
+
self._required["nrow"] = obj.nrow
|
|
356
|
+
self._required["nlay"] = obj.nlay
|
|
357
|
+
self._required["xshift"] = 0.0 # hardcoded so far
|
|
358
|
+
self._required["yshift"] = 0.0
|
|
359
|
+
self._required["zshift"] = 0.0
|
|
360
|
+
self._required["xscale"] = 1.0
|
|
361
|
+
self._required["yscale"] = 1.0
|
|
362
|
+
self._required["zscale"] = 1.0
|
|
363
|
+
self._required["subgrids"] = obj.get_subgrids()
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
class MetaDataCPProperty(MetaData):
|
|
367
|
+
"""Metadata for GridProperty() objects belonging to CPGeometry."""
|
|
368
|
+
|
|
369
|
+
REQUIRED: dict[str, Any] = {
|
|
370
|
+
"ncol": 1,
|
|
371
|
+
"nrow": 1,
|
|
372
|
+
"nlay": 1,
|
|
373
|
+
"codes": None,
|
|
374
|
+
"discrete": False,
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
def __init__(self) -> None:
|
|
378
|
+
"""Docstring."""
|
|
379
|
+
super().__init__()
|
|
380
|
+
self._required = self.REQUIRED
|
|
381
|
+
self._optional._datatype = "CornerPoint GridProperty"
|
|
382
|
+
|
|
383
|
+
@property
|
|
384
|
+
def required(self) -> dict[str, Any]:
|
|
385
|
+
"""Get of set required metadata."""
|
|
386
|
+
return self._required
|
|
387
|
+
|
|
388
|
+
@required.setter
|
|
389
|
+
def required(self, obj: GridProperty) -> None:
|
|
390
|
+
if not isinstance(obj, xtgeo.GridProperty): # type: ignore[attr-defined]
|
|
391
|
+
raise ValueError("Input object is not a GridProperty()")
|
|
392
|
+
|
|
393
|
+
self._required["ncol"] = obj.ncol
|
|
394
|
+
self._required["nrow"] = obj.nrow
|
|
395
|
+
self._required["nlay"] = obj.nlay
|
|
396
|
+
self._required["codes"] = obj.codes
|
|
397
|
+
self._required["discrete"] = obj.isdiscrete
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
class MetaDataWell(MetaData):
|
|
401
|
+
"""Metadata for single Well() objects."""
|
|
402
|
+
|
|
403
|
+
REQUIRED: dict[str, Any] = {
|
|
404
|
+
"rkb": 0.0,
|
|
405
|
+
"xpos": 0.0,
|
|
406
|
+
"ypos": 0.0,
|
|
407
|
+
"name": "noname",
|
|
408
|
+
"wlogs": {},
|
|
409
|
+
"mdlogname": None,
|
|
410
|
+
"zonelogname": None,
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
def __init__(self) -> None:
|
|
414
|
+
"""Initialisation for Well metadata."""
|
|
415
|
+
super().__init__()
|
|
416
|
+
self._required = self.REQUIRED
|
|
417
|
+
self._optional._datatype = "Well"
|
|
418
|
+
|
|
419
|
+
@property
|
|
420
|
+
def required(self) -> dict[str, Any]:
|
|
421
|
+
"""Get of set required metadata."""
|
|
422
|
+
return self._required
|
|
423
|
+
|
|
424
|
+
@required.setter
|
|
425
|
+
def required(self, obj: Well) -> None:
|
|
426
|
+
if not isinstance(obj, xtgeo.Well): # type: ignore[attr-defined]
|
|
427
|
+
raise ValueError("Input object is not a Well() instance!")
|
|
428
|
+
|
|
429
|
+
self._required["rkb"] = obj.rkb
|
|
430
|
+
self._required["xpos"] = obj.xpos
|
|
431
|
+
self._required["ypos"] = obj.ypos
|
|
432
|
+
self._required["name"] = obj.wname
|
|
433
|
+
self._required["wlogs"] = obj.get_wlogs()
|
|
434
|
+
self._required["mdlogname"] = obj.mdlogname
|
|
435
|
+
self._required["zonelogname"] = obj.zonelogname
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Loading rmsapi or roxar module based on availability"""
|
|
2
|
+
|
|
3
|
+
import importlib
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Optional, TypeVar
|
|
5
|
+
|
|
6
|
+
# Roxar*Type are placeholders for the actual types which are not known at this point,
|
|
7
|
+
# and these may be replaced in modules by e.g:
|
|
8
|
+
# ---------
|
|
9
|
+
# from xtgeo.roxutils._roxar_loader import roxar
|
|
10
|
+
# if TYPE_CHECKING:
|
|
11
|
+
# from xtgeo.grid3d.grid_property import GridProperty
|
|
12
|
+
# if roxar is not None:
|
|
13
|
+
# from roxar.grids import Grid3D as RoxarGridType
|
|
14
|
+
# ---------
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def load_module(module_name: str) -> Optional[Any]:
|
|
18
|
+
try:
|
|
19
|
+
return importlib.import_module(module_name)
|
|
20
|
+
except ImportError:
|
|
21
|
+
return None
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
rmsapi = load_module("rmsapi")
|
|
25
|
+
roxar = rmsapi if rmsapi else load_module("roxar")
|
|
26
|
+
|
|
27
|
+
if rmsapi:
|
|
28
|
+
roxar_well_picks = load_module("rmsapi.well_picks")
|
|
29
|
+
roxar_jobs = load_module("rmsapi.jobs")
|
|
30
|
+
roxar_grids = load_module("rmsapi.grids")
|
|
31
|
+
else:
|
|
32
|
+
roxar_well_picks = load_module("roxar.well_picks")
|
|
33
|
+
roxar_jobs = load_module("roxar.jobs")
|
|
34
|
+
roxar_grids = load_module("roxar.grids")
|
|
35
|
+
|
|
36
|
+
# Use TypeVar correctly
|
|
37
|
+
RoxarType = TypeVar("RoxarType")
|
|
38
|
+
RoxarGridType = TypeVar("RoxarGridType")
|
|
39
|
+
RoxarGrid3DType = TypeVar("RoxarGrid3DType")
|
|
40
|
+
RoxarWellpicksType = TypeVar("RoxarWellpicksType")
|
|
41
|
+
RoxarJobsType = TypeVar("RoxarJobsType")
|
|
42
|
+
|
|
43
|
+
if TYPE_CHECKING:
|
|
44
|
+
if roxar is not None:
|
|
45
|
+
import roxar.grids as RoxarGridType # noqa # type: ignore
|
|
46
|
+
from roxar.grids import Grid3D as RoxarGrid3DType # noqa # type: ignore
|
|
47
|
+
from roxar.jobs import Jobs as RoxarJobsType # noqa # type: ignore
|
|
48
|
+
from roxar.well_picks import WellPicks as RoxarWellpicksType # noqa # type: ignore
|
|
49
|
+
|
|
50
|
+
# Explicitly type the roxar* as Optional to indicate it may be None
|
|
51
|
+
roxar: Optional[Any] = roxar
|
|
52
|
+
roxar_grids: Optional[Any] = roxar_grids
|
|
53
|
+
roxar_well_picks: Optional[Any] = roxar_well_picks
|
|
54
|
+
roxar_jobs: Optional[Any] = roxar_jobs
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""Private module for etc functions"""
|
|
2
|
+
|
|
3
|
+
from xtgeo.common.log import null_logger
|
|
4
|
+
|
|
5
|
+
from ._roxar_loader import roxar
|
|
6
|
+
|
|
7
|
+
logger = null_logger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def create_whatever_category(
|
|
11
|
+
self, category, stype="horizons", domain="depth", htype="surface"
|
|
12
|
+
):
|
|
13
|
+
"""Create one or more a Horizons/Zones... category entries.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
category (str or list): Name(s) of category to make, either as
|
|
17
|
+
a simple string or a list of strings.
|
|
18
|
+
stype (str): 'Super type' in RMS (horizons or zones).
|
|
19
|
+
Default is horizons
|
|
20
|
+
domain (str): 'depth' (default) or 'time'
|
|
21
|
+
htype (str): Horizon type: surface/lines/points
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
project = self.project
|
|
25
|
+
categories = []
|
|
26
|
+
|
|
27
|
+
if isinstance(category, str):
|
|
28
|
+
categories.append(category)
|
|
29
|
+
else:
|
|
30
|
+
categories.extend(category)
|
|
31
|
+
|
|
32
|
+
for catg in categories:
|
|
33
|
+
geom = roxar.GeometryType.surface
|
|
34
|
+
if htype.lower() == "lines":
|
|
35
|
+
geom = roxar.GeometryType.lines
|
|
36
|
+
elif htype.lower() == "points":
|
|
37
|
+
geom = roxar.GeometryType.points
|
|
38
|
+
|
|
39
|
+
dom = roxar.VerticalDomain.depth
|
|
40
|
+
if domain.lower() == "time":
|
|
41
|
+
dom = roxar.GeometryType.lines
|
|
42
|
+
|
|
43
|
+
if stype.lower() == "horizons":
|
|
44
|
+
if catg not in project.horizons.representations:
|
|
45
|
+
try:
|
|
46
|
+
project.horizons.representations.create(catg, geom, dom)
|
|
47
|
+
except Exception as exmsg:
|
|
48
|
+
print(f"Error: {exmsg}")
|
|
49
|
+
else:
|
|
50
|
+
print(f"Category <{catg}> already exists")
|
|
51
|
+
|
|
52
|
+
elif stype.lower() == "zones":
|
|
53
|
+
if catg not in project.zones.representations:
|
|
54
|
+
try:
|
|
55
|
+
project.zones.representations.create(catg, geom, dom)
|
|
56
|
+
except Exception as exmsg:
|
|
57
|
+
print(f"Error: {exmsg}")
|
|
58
|
+
else:
|
|
59
|
+
print(f"Category <{catg}> already exists")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def delete_whatever_category(self, category, stype="horizons"):
|
|
63
|
+
"""Delete one or more horizons or zones categories.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
category (str or list): Name(s) of category to make, either
|
|
67
|
+
as a simple string or a list of strings.
|
|
68
|
+
stype (str): 'Super type', in RMS ('horizons' or 'zones').
|
|
69
|
+
Default is 'horizons'
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
project = self.project
|
|
73
|
+
categories = []
|
|
74
|
+
|
|
75
|
+
if isinstance(category, str):
|
|
76
|
+
categories.append(category)
|
|
77
|
+
else:
|
|
78
|
+
categories.extend(category)
|
|
79
|
+
|
|
80
|
+
for catg in categories:
|
|
81
|
+
stype_lower = stype.lower()
|
|
82
|
+
if stype_lower == "horizons" or stype_lower == "zones":
|
|
83
|
+
try:
|
|
84
|
+
del project.horizons.representations[catg]
|
|
85
|
+
except KeyError as kerr:
|
|
86
|
+
if kerr == catg:
|
|
87
|
+
print(f"Cannot delete {kerr}, does not exist")
|
|
88
|
+
else:
|
|
89
|
+
raise ValueError("Wrong stype applied")
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def clear_whatever_category(self, category, stype="horizons"):
|
|
93
|
+
"""Clear (or make empty) the content of one or more horizon/zones... categories.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
category (str or list): Name(s) of category to empty, either as
|
|
97
|
+
a simple string or a list of strings.
|
|
98
|
+
stype (str): 'Super type' in RMS (horizons or zones).
|
|
99
|
+
Default is horizons
|
|
100
|
+
|
|
101
|
+
.. versionadded:: 2.1
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
project = self.project
|
|
105
|
+
|
|
106
|
+
categories = []
|
|
107
|
+
if isinstance(category, str):
|
|
108
|
+
categories.append(category)
|
|
109
|
+
else:
|
|
110
|
+
categories.extend(category)
|
|
111
|
+
|
|
112
|
+
xtype = project.horizons
|
|
113
|
+
if stype.lower() == "zones":
|
|
114
|
+
xtype = project.zones
|
|
115
|
+
|
|
116
|
+
for catg in categories:
|
|
117
|
+
for xitem in xtype:
|
|
118
|
+
try:
|
|
119
|
+
item = xtype[xitem.name][catg]
|
|
120
|
+
item.set_empty()
|
|
121
|
+
except KeyError as kmsg:
|
|
122
|
+
print(kmsg)
|