xtgeo 4.8.0__cp313-cp313-macosx_11_0_arm64.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.

Files changed (117) hide show
  1. cxtgeo.py +582 -0
  2. cxtgeoPYTHON_wrap.c +20938 -0
  3. xtgeo/__init__.py +246 -0
  4. xtgeo/_cxtgeo.cpython-313-darwin.so +0 -0
  5. xtgeo/_internal.cpython-313-darwin.so +0 -0
  6. xtgeo/common/__init__.py +19 -0
  7. xtgeo/common/_angles.py +29 -0
  8. xtgeo/common/_xyz_enum.py +50 -0
  9. xtgeo/common/calc.py +396 -0
  10. xtgeo/common/constants.py +30 -0
  11. xtgeo/common/exceptions.py +42 -0
  12. xtgeo/common/log.py +93 -0
  13. xtgeo/common/sys.py +166 -0
  14. xtgeo/common/types.py +18 -0
  15. xtgeo/common/version.py +21 -0
  16. xtgeo/common/xtgeo_dialog.py +604 -0
  17. xtgeo/cube/__init__.py +9 -0
  18. xtgeo/cube/_cube_export.py +214 -0
  19. xtgeo/cube/_cube_import.py +532 -0
  20. xtgeo/cube/_cube_roxapi.py +180 -0
  21. xtgeo/cube/_cube_utils.py +287 -0
  22. xtgeo/cube/_cube_window_attributes.py +340 -0
  23. xtgeo/cube/cube1.py +1023 -0
  24. xtgeo/grid3d/__init__.py +15 -0
  25. xtgeo/grid3d/_ecl_grid.py +774 -0
  26. xtgeo/grid3d/_ecl_inte_head.py +148 -0
  27. xtgeo/grid3d/_ecl_logi_head.py +71 -0
  28. xtgeo/grid3d/_ecl_output_file.py +81 -0
  29. xtgeo/grid3d/_egrid.py +1004 -0
  30. xtgeo/grid3d/_find_gridprop_in_eclrun.py +625 -0
  31. xtgeo/grid3d/_grdecl_format.py +266 -0
  32. xtgeo/grid3d/_grdecl_grid.py +388 -0
  33. xtgeo/grid3d/_grid3d.py +29 -0
  34. xtgeo/grid3d/_grid3d_fence.py +181 -0
  35. xtgeo/grid3d/_grid3d_utils.py +228 -0
  36. xtgeo/grid3d/_grid_boundary.py +76 -0
  37. xtgeo/grid3d/_grid_etc1.py +1566 -0
  38. xtgeo/grid3d/_grid_export.py +221 -0
  39. xtgeo/grid3d/_grid_hybrid.py +66 -0
  40. xtgeo/grid3d/_grid_import.py +79 -0
  41. xtgeo/grid3d/_grid_import_ecl.py +101 -0
  42. xtgeo/grid3d/_grid_import_roff.py +135 -0
  43. xtgeo/grid3d/_grid_import_xtgcpgeom.py +375 -0
  44. xtgeo/grid3d/_grid_refine.py +125 -0
  45. xtgeo/grid3d/_grid_roxapi.py +292 -0
  46. xtgeo/grid3d/_grid_wellzone.py +165 -0
  47. xtgeo/grid3d/_gridprop_export.py +178 -0
  48. xtgeo/grid3d/_gridprop_import_eclrun.py +164 -0
  49. xtgeo/grid3d/_gridprop_import_grdecl.py +130 -0
  50. xtgeo/grid3d/_gridprop_import_roff.py +52 -0
  51. xtgeo/grid3d/_gridprop_import_xtgcpprop.py +168 -0
  52. xtgeo/grid3d/_gridprop_lowlevel.py +171 -0
  53. xtgeo/grid3d/_gridprop_op1.py +174 -0
  54. xtgeo/grid3d/_gridprop_roxapi.py +239 -0
  55. xtgeo/grid3d/_gridprop_value_init.py +140 -0
  56. xtgeo/grid3d/_gridprops_import_eclrun.py +344 -0
  57. xtgeo/grid3d/_gridprops_import_roff.py +83 -0
  58. xtgeo/grid3d/_roff_grid.py +469 -0
  59. xtgeo/grid3d/_roff_parameter.py +303 -0
  60. xtgeo/grid3d/grid.py +2537 -0
  61. xtgeo/grid3d/grid_properties.py +699 -0
  62. xtgeo/grid3d/grid_property.py +1341 -0
  63. xtgeo/grid3d/types.py +15 -0
  64. xtgeo/io/__init__.py +1 -0
  65. xtgeo/io/_file.py +592 -0
  66. xtgeo/metadata/__init__.py +17 -0
  67. xtgeo/metadata/metadata.py +431 -0
  68. xtgeo/roxutils/__init__.py +7 -0
  69. xtgeo/roxutils/_roxar_loader.py +54 -0
  70. xtgeo/roxutils/_roxutils_etc.py +122 -0
  71. xtgeo/roxutils/roxutils.py +207 -0
  72. xtgeo/surface/__init__.py +18 -0
  73. xtgeo/surface/_regsurf_boundary.py +26 -0
  74. xtgeo/surface/_regsurf_cube.py +210 -0
  75. xtgeo/surface/_regsurf_cube_window.py +391 -0
  76. xtgeo/surface/_regsurf_cube_window_v2.py +297 -0
  77. xtgeo/surface/_regsurf_cube_window_v3.py +360 -0
  78. xtgeo/surface/_regsurf_export.py +388 -0
  79. xtgeo/surface/_regsurf_grid3d.py +271 -0
  80. xtgeo/surface/_regsurf_gridding.py +347 -0
  81. xtgeo/surface/_regsurf_ijxyz_parser.py +278 -0
  82. xtgeo/surface/_regsurf_import.py +347 -0
  83. xtgeo/surface/_regsurf_lowlevel.py +122 -0
  84. xtgeo/surface/_regsurf_oper.py +631 -0
  85. xtgeo/surface/_regsurf_roxapi.py +241 -0
  86. xtgeo/surface/_regsurf_utils.py +81 -0
  87. xtgeo/surface/_surfs_import.py +43 -0
  88. xtgeo/surface/_zmap_parser.py +138 -0
  89. xtgeo/surface/regular_surface.py +2967 -0
  90. xtgeo/surface/surfaces.py +276 -0
  91. xtgeo/well/__init__.py +24 -0
  92. xtgeo/well/_blockedwell_roxapi.py +221 -0
  93. xtgeo/well/_blockedwells_roxapi.py +68 -0
  94. xtgeo/well/_well_aux.py +30 -0
  95. xtgeo/well/_well_io.py +327 -0
  96. xtgeo/well/_well_oper.py +574 -0
  97. xtgeo/well/_well_roxapi.py +304 -0
  98. xtgeo/well/_wellmarkers.py +486 -0
  99. xtgeo/well/_wells_utils.py +158 -0
  100. xtgeo/well/blocked_well.py +216 -0
  101. xtgeo/well/blocked_wells.py +122 -0
  102. xtgeo/well/well1.py +1514 -0
  103. xtgeo/well/wells.py +211 -0
  104. xtgeo/xyz/__init__.py +6 -0
  105. xtgeo/xyz/_polygons_oper.py +272 -0
  106. xtgeo/xyz/_xyz.py +741 -0
  107. xtgeo/xyz/_xyz_data.py +646 -0
  108. xtgeo/xyz/_xyz_io.py +490 -0
  109. xtgeo/xyz/_xyz_lowlevel.py +42 -0
  110. xtgeo/xyz/_xyz_oper.py +613 -0
  111. xtgeo/xyz/_xyz_roxapi.py +766 -0
  112. xtgeo/xyz/points.py +681 -0
  113. xtgeo/xyz/polygons.py +811 -0
  114. xtgeo-4.8.0.dist-info/METADATA +145 -0
  115. xtgeo-4.8.0.dist-info/RECORD +117 -0
  116. xtgeo-4.8.0.dist-info/WHEEL +6 -0
  117. xtgeo-4.8.0.dist-info/licenses/LICENSE.md +165 -0
@@ -0,0 +1,375 @@
1
+ """Private module, Grid Import private functions for xtgeo based formats."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from struct import unpack
7
+ from typing import TYPE_CHECKING, Literal, Tuple, TypedDict, Union, overload
8
+
9
+ import h5py
10
+ import numpy as np
11
+ from typing_extensions import NotRequired, TypeAlias
12
+
13
+ import xtgeo.common.sys as xsys
14
+ from xtgeo.common import null_logger
15
+ from xtgeo.grid3d._gridprop_import_xtgcpprop import _read_filelike
16
+
17
+ logger = null_logger(__name__)
18
+
19
+ if TYPE_CHECKING:
20
+ from xtgeo.io._file import FileWrapper
21
+
22
+ LMIN: TypeAlias = Literal["min"]
23
+ LMAX: TypeAlias = Literal["max"]
24
+ IJKRANGE: TypeAlias = Tuple[
25
+ Union[int, LMIN],
26
+ Union[int, LMAX],
27
+ Union[int, LMIN],
28
+ Union[int, LMAX],
29
+ Union[int, LMIN],
30
+ Union[int, LMAX],
31
+ ]
32
+
33
+
34
+ class _RESULT(TypedDict):
35
+ coordsv: np.ndarray
36
+ zcornsv: np.ndarray
37
+ actnumsv: np.ndarray
38
+ subgrids: NotRequired[dict[str, range]]
39
+
40
+
41
+ class __REQUIRED_(TypedDict):
42
+ xshift: np.ndarray
43
+ xscale: np.ndarray
44
+ yshift: np.ndarray
45
+ yscale: np.ndarray
46
+ zshift: np.ndarray
47
+ zscale: np.ndarray
48
+ subgrids: NotRequired[dict[str, int]]
49
+
50
+
51
+ class _META(TypedDict):
52
+ _required_: __REQUIRED_
53
+
54
+
55
+ @overload
56
+ def convert_subgrids(sdict: dict[str, int]) -> dict[str, range]: ...
57
+
58
+
59
+ @overload
60
+ def convert_subgrids(sdict: None) -> None: ...
61
+
62
+
63
+ def convert_subgrids(sdict: dict[str, int] | None) -> dict[str, range] | None:
64
+ # flake8: noqa: F841
65
+ """Convert a simplified ordered dictionary to
66
+ subgrid required by Grid.
67
+
68
+ The simplified dictionary is on the form
69
+ {"name1": 3, "name2": 5}
70
+
71
+ Note that the input must be an dict!
72
+
73
+ """
74
+ if sdict is None:
75
+ return None
76
+
77
+ if not isinstance(sdict, dict):
78
+ raise ValueError("Input sdict is not an dict")
79
+
80
+ start = 1
81
+ return {name: range(start, start := start + nsub) for name, nsub in sdict.items()}
82
+
83
+
84
+ def handle_metadata(
85
+ result: _RESULT,
86
+ meta: _META,
87
+ ncol: int,
88
+ nrow: int,
89
+ nlay: int,
90
+ ) -> None:
91
+ # meta _optional_ *may* contain xshift, xscale etc which in case must be taken
92
+ # into account
93
+ coordsv = result["coordsv"]
94
+ zcornsv = result["zcornsv"]
95
+ actnumsv = result["actnumsv"]
96
+ nncol = ncol + 1
97
+ nnrow = nrow + 1
98
+ nnlay = nlay + 1
99
+ req = meta["_required_"]
100
+ shi = req["xshift"]
101
+ coordsv[0::3] = np.where(shi != 0, coordsv[0::3] + shi, coordsv[0::3])
102
+ sca = req["xscale"]
103
+ coordsv[0::3] = np.where(sca != 1, coordsv[0::3] * sca, coordsv[0::3])
104
+ shi = req["yshift"]
105
+ coordsv[1::3] = np.where(shi != 0, coordsv[1::3] + shi, coordsv[1::3])
106
+ sca = req["yscale"]
107
+ coordsv[1::3] = np.where(sca != 1, coordsv[1::3] * sca, coordsv[1::3])
108
+ shi = req["zshift"]
109
+ coordsv[2::3] = np.where(shi != 0, coordsv[2::3] + shi, coordsv[2::3])
110
+ sca = req["zscale"]
111
+ coordsv[2::3] = np.where(sca != 1, coordsv[2::3] * sca, coordsv[2::3])
112
+
113
+ result["coordsv"] = coordsv.reshape((nncol, nnrow, 6)).astype(np.float64)
114
+ result["zcornsv"] = zcornsv.reshape((nncol, nnrow, nnlay, 4)).astype(np.float32)
115
+ result["actnumsv"] = actnumsv.reshape((ncol, nrow, nlay)).astype(np.int32)
116
+
117
+ if "subgrids" in req:
118
+ result["subgrids"] = convert_subgrids(req["subgrids"])
119
+
120
+
121
+ def import_xtgcpgeom(
122
+ mfile: FileWrapper,
123
+ mmap: bool = False,
124
+ ) -> _RESULT:
125
+ """Using pure python for experimental grid geometry import."""
126
+ #
127
+ offset = 36
128
+ with _read_filelike(mfile.file, size=offset) as f:
129
+ # unpack header
130
+ swap, magic, nformat, ncol, nrow, nlay = unpack("= i i i q q q", f)
131
+
132
+ nncol = ncol + 1
133
+ nnrow = nrow + 1
134
+ nnlay = nlay + 1
135
+
136
+ if swap != 1 or magic != 1301:
137
+ raise ValueError(f"Error, swap magic are {swap} {magic}, expected is 1 1301")
138
+
139
+ # subformat processing, indicating number of bytes per datatype
140
+ # here, 844 is native XTGeo (float64, float32, int32)
141
+ if nformat not in (444, 844, 841, 881, 884):
142
+ raise ValueError(f"The subformat value {nformat} is not valid")
143
+
144
+ coordfmt, zcornfmt, actnumfmt = [int(nbyte) for nbyte in str(nformat)]
145
+
146
+ dtype_coordsv = f"float{coordfmt * 8}"
147
+ dtype_zcornsv = f"float{zcornfmt * 8}"
148
+ dtype_actnumv = f"int{actnumfmt * 8}"
149
+
150
+ ncoord = nncol * nnrow * 6
151
+ nzcorn = nncol * nnrow * nnlay * 4
152
+ nactnum = ncol * nrow * nlay
153
+
154
+ # read numpy arrays from file
155
+ coordsv = xsys.npfromfile(
156
+ mfile.file,
157
+ dtype=dtype_coordsv,
158
+ count=ncoord,
159
+ offset=offset,
160
+ mmap=mmap,
161
+ )
162
+
163
+ newoffset = offset + ncoord * coordfmt
164
+ zcornsv = xsys.npfromfile(
165
+ mfile.file,
166
+ dtype=dtype_zcornsv,
167
+ count=nzcorn,
168
+ offset=newoffset,
169
+ mmap=mmap,
170
+ )
171
+
172
+ newoffset += nzcorn * zcornfmt
173
+ actnumsv = xsys.npfromfile(
174
+ mfile.file,
175
+ dtype=dtype_actnumv,
176
+ count=nactnum,
177
+ offset=newoffset,
178
+ mmap=mmap,
179
+ )
180
+
181
+ newoffset += nactnum * actnumfmt
182
+ result = _RESULT(coordsv=coordsv, zcornsv=zcornsv, actnumsv=actnumsv)
183
+
184
+ # read metadata which will be at position offet + nfloat*narr +13
185
+ with _read_filelike(mfile.file, seek=newoffset + 13) as f:
186
+ meta = json.loads(f, object_pairs_hook=dict)
187
+
188
+ handle_metadata(result, meta, ncol, nrow, nlay)
189
+ return result
190
+
191
+
192
+ def import_hdf5_cpgeom(
193
+ mfile: FileWrapper,
194
+ ijkrange: IJKRANGE | None = None,
195
+ zerobased: bool = False,
196
+ ) -> _RESULT:
197
+ """Experimental grid geometry import using hdf5."""
198
+ #
199
+ with h5py.File(mfile.name, "r") as h5h:
200
+ grp = h5h["CornerPointGeometry"]
201
+
202
+ idcode = grp.attrs["format-idcode"]
203
+ provider = grp.attrs["provider"]
204
+ if idcode != 1301:
205
+ raise ValueError(f"Wrong id code: {idcode}")
206
+ logger.info("Provider is %s", provider)
207
+
208
+ jmeta = grp.attrs["metadata"]
209
+ meta = json.loads(jmeta, object_pairs_hook=dict)
210
+
211
+ req = meta["_required_"]
212
+ ncol = req["ncol"]
213
+ nrow = req["nrow"]
214
+ nlay = req["nlay"]
215
+
216
+ if ijkrange is not None:
217
+ incoord, inzcorn, inactnum, ncol, nrow, nlay = _partial_read(
218
+ h5h, req, ijkrange, zerobased
219
+ )
220
+ if (
221
+ "subgrids" in meta["_required_"]
222
+ and meta["_required_"]["subgrids"] is not None
223
+ ):
224
+ *_, k1, k2 = ijkrange
225
+ meta["_required_"]["subgrids"] = filter_subgrids_partial(
226
+ meta["_required_"]["subgrids"],
227
+ k1,
228
+ k2,
229
+ nlay,
230
+ zerobased,
231
+ )
232
+ else:
233
+ incoord = grp["coord"][:, :, :]
234
+ inzcorn = grp["zcorn"][:, :, :, :]
235
+ inactnum = grp["actnum"][:, :, :]
236
+
237
+ result = _RESULT(
238
+ coordsv=incoord.astype("float64"),
239
+ zcornsv=inzcorn.astype("float32"),
240
+ actnumsv=inactnum.astype("float32"),
241
+ )
242
+
243
+ handle_metadata(result, meta, ncol, nrow, nlay)
244
+ return result
245
+
246
+
247
+ def filter_subgrids_partial(
248
+ subgrids: dict[str, int],
249
+ k1: int | LMIN,
250
+ k2: int | LMAX,
251
+ nlay: int,
252
+ zerobased: bool,
253
+ ) -> dict[str, int]:
254
+ """
255
+ Filters and truncates the subgrids of the global grid so that they
256
+ refer to the filtered grid.
257
+
258
+ >>> filter_subgrids_partial(
259
+ ... {"subgrid1": 4, "subgrid2": 1, "subgrid3":9},
260
+ ... 4,
261
+ ... 5,
262
+ ... 12,
263
+ ... True
264
+ ... )
265
+ dict([('subgrid2', 1), ('subgrid3', 1)])
266
+
267
+ Args:
268
+ subgrids: The dict of subgrids.
269
+ k1: Start of subgrid layers (can be "min" to mean 0 or 1 dependent on zerobased)
270
+ k2: End of subgrid layers (cna be "max" to mean nlay or nlay -1
271
+ dependent on zerobased.
272
+ nlay: Original number of layers
273
+ zerobased: Whether indexing starts with 0 or 1.
274
+
275
+ Returns:
276
+ New Orderedict which is the subgrids dictionary with out of range
277
+ subgrids removed or truncated.
278
+
279
+ """
280
+ if k1 == "min":
281
+ k1 = 0 if zerobased else 1
282
+
283
+ if k2 == "max":
284
+ k2 = nlay - 1 if zerobased else nlay
285
+
286
+ # convert k1 and k2 to zero based
287
+ if not zerobased:
288
+ k1 -= 1
289
+ k2 -= 1
290
+
291
+ partial_subgrid: dict[str, int] = {}
292
+ start = 0
293
+ for key, value in subgrids.items():
294
+ end = value + start
295
+ partial_start = max(start, k1)
296
+ partial_end = min(end, k2 + 1)
297
+ if partial_end - partial_start > 0:
298
+ partial_subgrid[key] = partial_end - partial_start
299
+ start = end
300
+ return partial_subgrid
301
+
302
+
303
+ def _partial_read(
304
+ h5h: dict[str, np.ndarray],
305
+ req: dict[str, int],
306
+ ijkrange: tuple[
307
+ int | LMIN,
308
+ int | LMAX,
309
+ int | LMIN,
310
+ int | LMAX,
311
+ int | LMIN,
312
+ int | LMAX,
313
+ ],
314
+ zerobased: bool,
315
+ ) -> tuple[np.ndarray, np.ndarray, np.ndarray, int, int, int]:
316
+ """Read a partial IJ range."""
317
+ ncol = req["ncol"]
318
+ nrow = req["nrow"]
319
+ nlay = req["nlay"]
320
+
321
+ if len(ijkrange) != 6:
322
+ raise ValueError("The ijkrange list must have 6 elements")
323
+
324
+ i1, i2, j1, j2, k1, k2 = ijkrange
325
+
326
+ if i1 == "min":
327
+ i1 = 0 if zerobased else 1
328
+ if j1 == "min":
329
+ j1 = 0 if zerobased else 1
330
+ if k1 == "min":
331
+ k1 = 0 if zerobased else 1
332
+
333
+ if i2 == "max":
334
+ i2 = ncol - 1 if zerobased else ncol
335
+ if j2 == "max":
336
+ j2 = nrow - 1 if zerobased else nrow
337
+ if k2 == "max":
338
+ k2 = nlay - 1 if zerobased else nlay
339
+
340
+ if not zerobased:
341
+ i1 -= 1
342
+ i2 -= 1
343
+ j1 -= 1
344
+ j2 -= 1
345
+ k1 -= 1
346
+ k2 -= 1
347
+
348
+ ncol2 = i2 - i1 + 1
349
+ nrow2 = j2 - j1 + 1
350
+ nlay2 = k2 - k1 + 1
351
+
352
+ if (
353
+ ncol2 < 1
354
+ or ncol2 > ncol
355
+ or nrow2 < 1
356
+ or nrow2 > nrow
357
+ or nlay2 < 1
358
+ or nlay2 > nlay
359
+ ):
360
+ raise ValueError("The ijkrange spesification exceeds boundaries.")
361
+
362
+ nncol2 = ncol2 + 1
363
+ nnrow2 = nrow2 + 1
364
+ nnlay2 = nlay2 + 1
365
+
366
+ dset = h5h["CornerPointGeometry/coord"]
367
+ cv = dset[i1 : i1 + nncol2, j1 : j1 + nnrow2, :]
368
+
369
+ dset = h5h["CornerPointGeometry/zcorn"]
370
+ zv = dset[i1 : i1 + nncol2, j1 : j1 + nnrow2, k1 : k1 + nnlay2, :]
371
+
372
+ dset = h5h["CornerPointGeometry/actnum"]
373
+ av = dset[i1 : i1 + ncol2, j1 : j1 + nrow2, k1 : k1 + nlay2]
374
+
375
+ return cv, zv, av, ncol2, nrow2, nlay2
@@ -0,0 +1,125 @@
1
+ """Private module for refinement of a grid."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ import numpy as np
8
+
9
+ from xtgeo import _cxtgeo
10
+ from xtgeo.common import XTGeoDialog, null_logger
11
+
12
+ xtg = XTGeoDialog()
13
+ logger = null_logger(__name__)
14
+
15
+ if TYPE_CHECKING:
16
+ from xtgeo.grid3d import Grid, GridProperty
17
+
18
+
19
+ def refine_vertically(
20
+ self: Grid,
21
+ rfactor: int | dict[int, int],
22
+ zoneprop: GridProperty | None = None,
23
+ ) -> Grid:
24
+ """Refine vertically, proportionally.
25
+
26
+ See details in caller.
27
+ """
28
+ self._xtgformat1()
29
+
30
+ rfactord = {}
31
+
32
+ # case 1 rfactor as scalar value.
33
+ if isinstance(rfactor, int):
34
+ if self.subgrids:
35
+ subgrids = self.get_subgrids()
36
+ for i, _ in enumerate(self.subgrids.keys()):
37
+ rfactord[i + 1] = rfactor
38
+ else:
39
+ rfactord[0] = rfactor
40
+ subgrids = {}
41
+ subgrids[1] = self.nlay
42
+
43
+ # case 2 rfactor is a dict
44
+ else:
45
+ rfactord = dict(sorted(rfactor.items())) # redefined to ordered
46
+ # 2a: zoneprop is present
47
+ if zoneprop is not None:
48
+ oldsubgrids = None
49
+ if self.subgrids:
50
+ oldsubgrids = self.get_subgrids()
51
+
52
+ subgrids = self.subgrids_from_zoneprop(zoneprop)
53
+
54
+ if oldsubgrids and subgrids.values() != oldsubgrids.values():
55
+ xtg.warn("ISSUES!!!")
56
+
57
+ # 2b: zoneprop is not present
58
+ elif zoneprop is None and self.subgrids:
59
+ subgrids = self.get_subgrids()
60
+
61
+ elif zoneprop is None and not self.subgrids:
62
+ raise ValueError(
63
+ "You gave in a dict, but no zoneprops and "
64
+ "subgrids are not preesent in the grid"
65
+ )
66
+ else:
67
+ raise ValueError("Some major unexpected issue in routine...")
68
+
69
+ if len(subgrids) != len(rfactord):
70
+ raise RuntimeError("Subgrids and refinements: different definition!")
71
+
72
+ self.set_subgrids(subgrids)
73
+
74
+ # Now, based on dict, give a value per subgrid for key, val in rfactor
75
+ newsubgrids = {}
76
+ newnlay = 0
77
+ for (_x, rfi), (snam, sran) in zip(rfactord.items(), subgrids.items()):
78
+ newsubgrids[snam] = sran * rfi
79
+ newnlay += newsubgrids[snam]
80
+
81
+ logger.debug("New layers: %s", newnlay)
82
+
83
+ # refinefactors is an array with length nlay; has N refinements per single K layer
84
+ refinefactors = _cxtgeo.new_intarray(self.nlay)
85
+
86
+ totvector = []
87
+
88
+ for (_, rfi), (_, arr) in zip(rfactord.items(), self.subgrids.items()):
89
+ for _ in range(len(arr)):
90
+ totvector.append(rfi)
91
+
92
+ for inn, rfi in enumerate(totvector):
93
+ _cxtgeo.intarray_setitem(refinefactors, inn, rfi)
94
+
95
+ ref_zcornsv = np.zeros(self.ncol * self.nrow * (newnlay + 1) * 4, dtype=np.float64)
96
+ ref_actnumsv = np.zeros(self.ncol * self.nrow * newnlay, dtype=np.int32)
97
+
98
+ ier = _cxtgeo.grd3d_refine_vert(
99
+ self.ncol,
100
+ self.nrow,
101
+ self.nlay,
102
+ self._zcornsv,
103
+ self._actnumsv,
104
+ newnlay,
105
+ ref_zcornsv,
106
+ ref_actnumsv,
107
+ refinefactors,
108
+ )
109
+
110
+ if ier != 0:
111
+ raise RuntimeError(
112
+ f"An error occured in the C routine grd3d_refine_vert, code {ier}"
113
+ )
114
+
115
+ # update instance:
116
+ self._nlay = newnlay
117
+ self._zcornsv = ref_zcornsv
118
+ self._actnumsv = ref_actnumsv
119
+
120
+ if self.subgrids is None or len(self.subgrids) <= 1:
121
+ self.subgrids = None
122
+ else:
123
+ self.set_subgrids(newsubgrids)
124
+
125
+ return self