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.
Files changed (122) hide show
  1. cxtgeo.py +558 -0
  2. cxtgeoPYTHON_wrap.c +19537 -0
  3. xtgeo/__init__.py +248 -0
  4. xtgeo/_cxtgeo.cp313-win_amd64.pyd +0 -0
  5. xtgeo/_internal.cp313-win_amd64.pyd +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 +34 -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 +273 -0
  23. xtgeo/cube/cube1.py +1023 -0
  24. xtgeo/grid3d/__init__.py +15 -0
  25. xtgeo/grid3d/_ecl_grid.py +778 -0
  26. xtgeo/grid3d/_ecl_inte_head.py +152 -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 +309 -0
  32. xtgeo/grid3d/_grdecl_grid.py +400 -0
  33. xtgeo/grid3d/_grid3d.py +29 -0
  34. xtgeo/grid3d/_grid3d_fence.py +284 -0
  35. xtgeo/grid3d/_grid3d_utils.py +228 -0
  36. xtgeo/grid3d/_grid_boundary.py +76 -0
  37. xtgeo/grid3d/_grid_etc1.py +1683 -0
  38. xtgeo/grid3d/_grid_export.py +222 -0
  39. xtgeo/grid3d/_grid_hybrid.py +50 -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 +258 -0
  45. xtgeo/grid3d/_grid_roxapi.py +292 -0
  46. xtgeo/grid3d/_grid_translate_coords.py +154 -0
  47. xtgeo/grid3d/_grid_wellzone.py +165 -0
  48. xtgeo/grid3d/_gridprop_export.py +202 -0
  49. xtgeo/grid3d/_gridprop_import_eclrun.py +164 -0
  50. xtgeo/grid3d/_gridprop_import_grdecl.py +132 -0
  51. xtgeo/grid3d/_gridprop_import_roff.py +52 -0
  52. xtgeo/grid3d/_gridprop_import_xtgcpprop.py +168 -0
  53. xtgeo/grid3d/_gridprop_lowlevel.py +171 -0
  54. xtgeo/grid3d/_gridprop_op1.py +272 -0
  55. xtgeo/grid3d/_gridprop_roxapi.py +301 -0
  56. xtgeo/grid3d/_gridprop_value_init.py +140 -0
  57. xtgeo/grid3d/_gridprops_import_eclrun.py +344 -0
  58. xtgeo/grid3d/_gridprops_import_roff.py +83 -0
  59. xtgeo/grid3d/_roff_grid.py +470 -0
  60. xtgeo/grid3d/_roff_parameter.py +303 -0
  61. xtgeo/grid3d/grid.py +3010 -0
  62. xtgeo/grid3d/grid_properties.py +699 -0
  63. xtgeo/grid3d/grid_property.py +1313 -0
  64. xtgeo/grid3d/types.py +15 -0
  65. xtgeo/interfaces/rms/__init__.py +18 -0
  66. xtgeo/interfaces/rms/_regular_surface.py +460 -0
  67. xtgeo/interfaces/rms/_rms_base.py +100 -0
  68. xtgeo/interfaces/rms/_rmsapi_package.py +69 -0
  69. xtgeo/interfaces/rms/rmsapi_utils.py +438 -0
  70. xtgeo/io/__init__.py +1 -0
  71. xtgeo/io/_file.py +603 -0
  72. xtgeo/metadata/__init__.py +17 -0
  73. xtgeo/metadata/metadata.py +435 -0
  74. xtgeo/roxutils/__init__.py +7 -0
  75. xtgeo/roxutils/_roxar_loader.py +54 -0
  76. xtgeo/roxutils/_roxutils_etc.py +122 -0
  77. xtgeo/roxutils/roxutils.py +207 -0
  78. xtgeo/surface/__init__.py +20 -0
  79. xtgeo/surface/_regsurf_boundary.py +26 -0
  80. xtgeo/surface/_regsurf_cube.py +210 -0
  81. xtgeo/surface/_regsurf_cube_window.py +391 -0
  82. xtgeo/surface/_regsurf_cube_window_v2.py +297 -0
  83. xtgeo/surface/_regsurf_cube_window_v3.py +360 -0
  84. xtgeo/surface/_regsurf_export.py +388 -0
  85. xtgeo/surface/_regsurf_grid3d.py +275 -0
  86. xtgeo/surface/_regsurf_gridding.py +347 -0
  87. xtgeo/surface/_regsurf_ijxyz_parser.py +278 -0
  88. xtgeo/surface/_regsurf_import.py +347 -0
  89. xtgeo/surface/_regsurf_lowlevel.py +122 -0
  90. xtgeo/surface/_regsurf_oper.py +538 -0
  91. xtgeo/surface/_regsurf_utils.py +81 -0
  92. xtgeo/surface/_surfs_import.py +43 -0
  93. xtgeo/surface/_zmap_parser.py +138 -0
  94. xtgeo/surface/regular_surface.py +3043 -0
  95. xtgeo/surface/surfaces.py +276 -0
  96. xtgeo/well/__init__.py +24 -0
  97. xtgeo/well/_blockedwell_roxapi.py +241 -0
  98. xtgeo/well/_blockedwells_roxapi.py +68 -0
  99. xtgeo/well/_well_aux.py +30 -0
  100. xtgeo/well/_well_io.py +327 -0
  101. xtgeo/well/_well_oper.py +483 -0
  102. xtgeo/well/_well_roxapi.py +304 -0
  103. xtgeo/well/_wellmarkers.py +486 -0
  104. xtgeo/well/_wells_utils.py +158 -0
  105. xtgeo/well/blocked_well.py +220 -0
  106. xtgeo/well/blocked_wells.py +134 -0
  107. xtgeo/well/well1.py +1516 -0
  108. xtgeo/well/wells.py +211 -0
  109. xtgeo/xyz/__init__.py +6 -0
  110. xtgeo/xyz/_polygons_oper.py +272 -0
  111. xtgeo/xyz/_xyz.py +758 -0
  112. xtgeo/xyz/_xyz_data.py +646 -0
  113. xtgeo/xyz/_xyz_io.py +737 -0
  114. xtgeo/xyz/_xyz_lowlevel.py +42 -0
  115. xtgeo/xyz/_xyz_oper.py +613 -0
  116. xtgeo/xyz/_xyz_roxapi.py +766 -0
  117. xtgeo/xyz/points.py +698 -0
  118. xtgeo/xyz/polygons.py +827 -0
  119. xtgeo-4.14.1.dist-info/METADATA +146 -0
  120. xtgeo-4.14.1.dist-info/RECORD +122 -0
  121. xtgeo-4.14.1.dist-info/WHEEL +5 -0
  122. xtgeo-4.14.1.dist-info/licenses/LICENSE.md +165 -0
@@ -0,0 +1,538 @@
1
+ """Various operations"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import numbers
6
+ from typing import Any
7
+
8
+ import numpy as np
9
+ import numpy.ma as ma
10
+
11
+ from xtgeo import _cxtgeo
12
+ from xtgeo._cxtgeo import XTGeoCLibError
13
+ from xtgeo.common.constants import UNDEF, UNDEF_LIMIT
14
+ from xtgeo.common.log import null_logger
15
+ from xtgeo.common.xtgeo_dialog import XTGeoDialog
16
+ from xtgeo.xyz.polygons import Polygons
17
+
18
+ xtg = XTGeoDialog()
19
+
20
+ logger = null_logger(__name__)
21
+
22
+ VALID_OPER = (
23
+ "add",
24
+ "iadd",
25
+ "sub",
26
+ "isub",
27
+ "mul",
28
+ "imul",
29
+ "div",
30
+ "idiv",
31
+ "lt",
32
+ "gt",
33
+ "le",
34
+ "eq",
35
+ "ne",
36
+ )
37
+
38
+ VALID_OPER_POLYS = (
39
+ "add",
40
+ "sub",
41
+ "mul",
42
+ "div",
43
+ "set",
44
+ "eli",
45
+ )
46
+
47
+
48
+ def operations_two(self, other, oper="add"):
49
+ """General operations between two maps"""
50
+
51
+ other = _check_other(self, other)
52
+
53
+ okstatus = self.compare_topology(other)
54
+
55
+ useother = other
56
+ if not okstatus:
57
+ # to avoid that the "other" instance is changed
58
+ useother = self.copy()
59
+ useother.resample(other)
60
+
61
+ if oper not in VALID_OPER:
62
+ raise ValueError(f"Operation key oper has invalid value: {oper}")
63
+
64
+ retvalue = None
65
+
66
+ if oper == "add":
67
+ self.values = self.values + useother.values
68
+ elif oper == "iadd":
69
+ self.values += useother.values
70
+ elif oper == "sub":
71
+ self.values = self.values - useother.values
72
+ elif oper == "isub":
73
+ self._values -= useother._values
74
+ elif oper == "mul":
75
+ self.values = self.values * useother.values
76
+ elif oper == "imul":
77
+ self._values *= useother._values
78
+ elif oper == "div":
79
+ self.values = self.values / useother.values
80
+ elif oper == "idiv":
81
+ self._values /= useother._values
82
+
83
+ # comparisons:
84
+ elif oper == "lt":
85
+ retvalue = self.values < other.values
86
+ elif oper == "gt":
87
+ retvalue = self.values > other.values
88
+ elif oper == "le":
89
+ retvalue = self.values <= other.values
90
+ elif oper == "ge":
91
+ retvalue = self.values >= other.values
92
+ elif oper == "eq":
93
+ retvalue = self.values == other.values
94
+ elif oper == "ne":
95
+ retvalue = self.values != other.values
96
+
97
+ if useother is not other:
98
+ del useother
99
+
100
+ self._filesrc = "Calculated"
101
+
102
+ # return None or a boolean array
103
+ return retvalue
104
+
105
+
106
+ def _check_other(self, other):
107
+ """Will convert an other scalar to a valid numpy array"""
108
+
109
+ if isinstance(other, numbers.Number):
110
+ vals = other
111
+ other = self.copy()
112
+ other.values *= 0
113
+ other.values += vals
114
+ other._filesrc = None
115
+
116
+ return other
117
+
118
+
119
+ def resample(self, other, mask=True, sampling="bilinear"):
120
+ """Resample from other surface object to this surf."""
121
+
122
+ logger.info("Resampling...")
123
+
124
+ # a special case occur of the maps have same topology, but
125
+ # different masks
126
+ if self.compare_topology(other, strict=False):
127
+ self.values = other.values.copy()
128
+ return
129
+
130
+ svalues = np.ma.filled(self.values, fill_value=UNDEF)
131
+ ovalues = np.ma.filled(other.values, fill_value=UNDEF)
132
+
133
+ _cxtgeo.surf_resample(
134
+ other._ncol,
135
+ other._nrow,
136
+ other._xori,
137
+ other._xinc,
138
+ other._yori,
139
+ other._yinc,
140
+ other._yflip,
141
+ other._rotation,
142
+ ovalues,
143
+ self._ncol,
144
+ self._nrow,
145
+ self._xori,
146
+ self._xinc,
147
+ self._yori,
148
+ self._yinc,
149
+ self._yflip,
150
+ self._rotation,
151
+ svalues,
152
+ 0 if not mask else 1,
153
+ 2 if sampling == "nearest" else 0,
154
+ )
155
+
156
+ self.values = np.ma.masked_greater(svalues, UNDEF_LIMIT)
157
+
158
+ self.set_values1d(svalues)
159
+ self._filesrc = "Resampled"
160
+
161
+
162
+ def distance_from_point(self, point=(0, 0), azimuth=0.0):
163
+ """Find distance bwteen point and surface."""
164
+ xpv, ypv = point
165
+
166
+ svalues = self.get_values1d()
167
+
168
+ # call C routine
169
+ ier = _cxtgeo.surf_get_dist_values(
170
+ self._xori,
171
+ self._xinc,
172
+ self._yori,
173
+ self._yinc,
174
+ self._ncol,
175
+ self._nrow,
176
+ self._rotation,
177
+ xpv,
178
+ ypv,
179
+ azimuth,
180
+ svalues,
181
+ 0,
182
+ )
183
+
184
+ if ier != 0:
185
+ logger.error("Something went wrong...")
186
+ raise RuntimeError(f"Something went wrong in {__name__}")
187
+
188
+ self.set_values1d(svalues)
189
+
190
+
191
+ def get_value_from_xy(self, point=(0.0, 0.0), sampling="bilinear"):
192
+ """Find surface value for point X Y."""
193
+
194
+ xcoord, ycoord = point
195
+
196
+ option = 0 if sampling == "bilinear" else 2
197
+
198
+ zcoord = _cxtgeo.surf_get_z_from_xy(
199
+ float(xcoord),
200
+ float(ycoord),
201
+ self.ncol,
202
+ self.nrow,
203
+ self.xori,
204
+ self.yori,
205
+ self.xinc,
206
+ self.yinc,
207
+ self.yflip,
208
+ self.rotation,
209
+ self.get_values1d(),
210
+ option,
211
+ )
212
+ if zcoord > UNDEF_LIMIT:
213
+ return None
214
+
215
+ return zcoord
216
+
217
+
218
+ def get_xy_value_from_ij(self, iloc, jloc, zvalues=None):
219
+ """Find X Y value from I J index"""
220
+
221
+ if zvalues is None:
222
+ zvalues = self.get_values1d()
223
+
224
+ try:
225
+ ier, xval, yval, value = _cxtgeo.surf_xyz_from_ij(
226
+ iloc,
227
+ jloc,
228
+ self.xori,
229
+ self.xinc,
230
+ self.yori,
231
+ self.yinc,
232
+ self.ncol,
233
+ self.nrow,
234
+ self._yflip,
235
+ self.rotation,
236
+ zvalues,
237
+ 0,
238
+ )
239
+ except XTGeoCLibError:
240
+ raise ValueError(f"Index i {iloc} and/or j {jloc} out of bounds")
241
+
242
+ if value > UNDEF_LIMIT:
243
+ value = None
244
+
245
+ return xval, yval, value
246
+
247
+
248
+ def get_ij_values(self, zero_based=False, order="C", asmasked=False):
249
+ """Get I J values as numpy 2D arrays.
250
+
251
+ Args:
252
+ zero_based (bool): If True, first index is 0. False (1) is default.
253
+ order (str): 'C' or 'F' order (row vs column major)
254
+
255
+ """
256
+
257
+ ixn, jyn = np.indices((self._ncol, self._nrow))
258
+
259
+ if order == "F":
260
+ ixn = np.asfortranarray(ixn)
261
+ jyn = np.asfortranarray(jyn)
262
+
263
+ if not zero_based:
264
+ ixn += 1
265
+ jyn += 1
266
+
267
+ if asmasked:
268
+ ixn = ixn[~self.values.mask]
269
+ jyn = jyn[~self.values.mask]
270
+
271
+ return ixn, jyn
272
+
273
+
274
+ def get_ij_values1d(self, zero_based=False, activeonly=True, order="C"):
275
+ """Get I J values as numpy 1D arrays.
276
+
277
+ Args:
278
+ zero_based (bool): If True, first index is 0. False (1) is default.
279
+ activeonly (bool): If True, only for active nodes
280
+ order (str): 'C' or 'F' order (row vs column major)
281
+
282
+ """
283
+
284
+ ixn, jyn = self.get_ij_values(zero_based=zero_based, order=order)
285
+
286
+ ixn = ixn.ravel(order=order)
287
+ jyn = jyn.ravel(order=order)
288
+
289
+ if activeonly:
290
+ tmask = ma.getmaskarray(self.get_values1d(order=order, asmasked=True))
291
+ ixn = ma.array(ixn, mask=tmask)
292
+ ixn = ixn[~ixn.mask]
293
+ jyn = ma.array(jyn, mask=tmask)
294
+ jyn = jyn[~jyn.mask]
295
+
296
+ return ixn, jyn
297
+
298
+
299
+ def get_xy_values(self, order="C", asmasked=False):
300
+ """Get X Y coordinate values as numpy 2D arrays."""
301
+ nno = self.ncol * self.nrow
302
+
303
+ ier, xvals, yvals = _cxtgeo.surf_xy_as_values(
304
+ self.xori,
305
+ self.xinc,
306
+ self.yori,
307
+ self.yinc * self.yflip,
308
+ self.ncol,
309
+ self.nrow,
310
+ self.rotation,
311
+ nno,
312
+ nno,
313
+ 0,
314
+ )
315
+ if ier != 0:
316
+ raise XTGeoCLibError(f"Error in surf_xy_as_values, error code: {ier}")
317
+
318
+ # reshape
319
+ xvals = xvals.reshape((self.ncol, self.nrow))
320
+ yvals = yvals.reshape((self.ncol, self.nrow))
321
+
322
+ if order == "F":
323
+ xvals = np.array(xvals, order="F")
324
+ yvals = np.array(yvals, order="F")
325
+
326
+ if asmasked:
327
+ tmpv = ma.filled(self.values, fill_value=np.nan)
328
+ tmpv = np.array(tmpv, order=order)
329
+ tmpv = ma.masked_invalid(tmpv)
330
+ mymask = ma.getmaskarray(tmpv)
331
+ xvals = ma.array(xvals, mask=mymask, order=order)
332
+ yvals = ma.array(yvals, mask=mymask, order=order)
333
+
334
+ return xvals, yvals
335
+
336
+
337
+ def get_xy_values1d(self, order="C", activeonly=True):
338
+ """Get X Y coordinate values as numpy 1D arrays."""
339
+
340
+ asmasked = False
341
+ if activeonly:
342
+ asmasked = True
343
+
344
+ xvals, yvals = self.get_xy_values(order=order, asmasked=asmasked)
345
+
346
+ xvals = xvals.ravel(order=order)
347
+ yvals = yvals.ravel(order=order)
348
+
349
+ if activeonly:
350
+ xvals = xvals[~xvals.mask]
351
+ yvals = yvals[~yvals.mask]
352
+
353
+ return xvals, yvals
354
+
355
+
356
+ def get_fence(self, xyfence, sampling="bilinear"):
357
+ """Get surface values along fence."""
358
+
359
+ cxarr = xyfence[:, 0]
360
+ cyarr = xyfence[:, 1]
361
+ czarr = xyfence[:, 2].copy()
362
+
363
+ sampleoptions = {"bilinear": 0, "nearest": 2}
364
+
365
+ # czarr will be updated "inplace":
366
+ istat = _cxtgeo.surf_get_zv_from_xyv(
367
+ cxarr,
368
+ cyarr,
369
+ czarr,
370
+ self.ncol,
371
+ self.nrow,
372
+ self.xori,
373
+ self.yori,
374
+ self.xinc,
375
+ self.yinc,
376
+ self.yflip,
377
+ self.rotation,
378
+ self.get_values1d(),
379
+ sampleoptions.get(sampling, 0),
380
+ )
381
+
382
+ if istat != 0:
383
+ logger.warning("Seem to be rotten")
384
+
385
+ xyfence[:, 2] = czarr
386
+ xyfence = ma.masked_greater(xyfence, UNDEF_LIMIT)
387
+ return ma.mask_rows(xyfence)
388
+
389
+
390
+ def get_randomline(
391
+ self, fencespec, hincrement=None, atleast=5, nextend=2, sampling="bilinear"
392
+ ):
393
+ """Get surface values along fence."""
394
+
395
+ if hincrement is None and isinstance(fencespec, Polygons):
396
+ logger.info("Estimate hincrement from instance...")
397
+ fencespec = _get_randomline_fence(self, fencespec, hincrement, atleast, nextend)
398
+ logger.info("Estimate hincrement from instance... DONE")
399
+
400
+ if fencespec is None or fencespec is False:
401
+ return None
402
+
403
+ sampleoptions = {"bilinear": 0, "nearest": 2}
404
+
405
+ xcoords = fencespec[:, 0]
406
+ ycoords = fencespec[:, 1]
407
+ zcoords = fencespec[:, 2].copy()
408
+ hcoords = fencespec[:, 3]
409
+
410
+ # zcoords will be updated "inplace":
411
+ istat = _cxtgeo.surf_get_zv_from_xyv(
412
+ xcoords,
413
+ ycoords,
414
+ zcoords,
415
+ self.ncol,
416
+ self.nrow,
417
+ self.xori,
418
+ self.yori,
419
+ self.xinc,
420
+ self.yinc,
421
+ self.yflip,
422
+ self.rotation,
423
+ self.get_values1d(),
424
+ sampleoptions.get(sampling, 0),
425
+ )
426
+
427
+ if istat != 0:
428
+ logger.warning("Seem to be rotten")
429
+
430
+ zcoords[zcoords > UNDEF_LIMIT] = np.nan
431
+ return np.vstack([hcoords, zcoords]).T
432
+
433
+
434
+ def _get_randomline_fence(self, fencespec, hincrement, atleast, nextend):
435
+ """Compute a resampled fence from a Polygons instance"""
436
+
437
+ if hincrement is None:
438
+ avgdxdy = 0.5 * (self.xinc + self.yinc)
439
+ distance = 0.5 * avgdxdy
440
+ else:
441
+ distance = hincrement
442
+
443
+ logger.info("Getting fence from a Polygons instance...")
444
+ fspec = fencespec.get_fence(
445
+ distance=distance, atleast=atleast, nextend=nextend, asnumpy=True
446
+ )
447
+ logger.info("Getting fence from a Polygons instance... DONE")
448
+ return fspec
449
+
450
+
451
+ def _proxy_map_polygons(surf, poly, inside=True):
452
+ """Return a proxy map where on one to do operations, as 0 and 1."""
453
+ inside_value = 1 if inside else 0
454
+ outside_value = 0 if inside else 1
455
+
456
+ proxy = surf.copy()
457
+
458
+ # allow a single Polygons instance or a list of Polygons instances
459
+ if isinstance(poly, Polygons):
460
+ usepolys = [poly]
461
+ elif isinstance(poly, list) and all(isinstance(pol, Polygons) for pol in poly):
462
+ usepolys = poly
463
+ else:
464
+ raise ValueError("The poly values is not a Polygons or a list of Polygons")
465
+
466
+ proxy.values = outside_value
467
+ xvals, yvals = proxy.get_xy_values(asmasked=False)
468
+ points = np.array([xvals.ravel(), yvals.ravel()]).T
469
+
470
+ import matplotlib.path as mplpath
471
+
472
+ for pol in usepolys:
473
+ idgroups = pol.get_dataframe(copy=False).groupby(pol.pname)
474
+ for _, grp in idgroups:
475
+ singlepoly = np.array([grp[pol.xname].values, grp[pol.yname].values]).T
476
+ poly_path = mplpath.Path(singlepoly)
477
+ is_inside = poly_path.contains_points(points)
478
+ is_inside = is_inside.reshape(proxy.ncol, proxy.nrow)
479
+ proxy.values = np.where(is_inside, inside_value, proxy.values)
480
+
481
+ return proxy
482
+
483
+
484
+ def operation_polygons(self, poly, value: float | Any, opname="add", inside=True):
485
+ """Operations restricted to polygons, using matplotlib (much faster).
486
+
487
+ The 'value' can be a number or another regular surface (with same design)
488
+
489
+ """
490
+
491
+ proxy = _proxy_map_polygons(self, poly, inside=inside)
492
+ result = self.copy()
493
+
494
+ if isinstance(value, type(result)):
495
+ if not result.compare_topology(value):
496
+ raise ValueError("Input is RegularSurface, but not same map topology")
497
+ value = value.values.copy()
498
+ else:
499
+ # turn scalar value into numpy array
500
+ value = result.values.copy() * 0 + value
501
+
502
+ if opname == "add":
503
+ result.values = np.ma.where(
504
+ proxy.values == 1, result.values + value, result.values
505
+ )
506
+ elif opname == "sub":
507
+ result.values = np.ma.where(
508
+ proxy.values == 1, result.values - value, result.values
509
+ )
510
+ elif opname == "mul":
511
+ result.values = np.ma.where(
512
+ proxy.values == 1, result.values * value, result.values
513
+ )
514
+ elif opname == "div":
515
+ # Dividing a map of zero is always a hazzle; try to obtain 0.0
516
+ # as result in these cases
517
+ if 0.0 in value:
518
+ xtg.warn(
519
+ "Dividing a surface with value = 0.0 or surface with zero "
520
+ "elements; may get unexpected results, will try to "
521
+ "achieve zero values as result!"
522
+ )
523
+
524
+ result.values = np.ma.where(value == 0.0, 0.0, result.values)
525
+ proxy.values = np.ma.where(value == 0.0, 0, proxy.values)
526
+ result.values = np.ma.where(
527
+ proxy.values == 1, result.values / value, result.values
528
+ )
529
+
530
+ elif opname == "set":
531
+ result.values = np.ma.where(proxy.values == 1, value, result.values)
532
+ elif opname == "eli":
533
+ result.values = np.ma.where(proxy.values == 1, UNDEF, result.values)
534
+ result.values = ma.masked_greater(result.values, UNDEF_LIMIT)
535
+ else:
536
+ raise KeyError(f"The opname={opname} is not one of {VALID_OPER_POLYS}")
537
+
538
+ self.values = result.values
@@ -0,0 +1,81 @@
1
+ """RegularSurface utilities"""
2
+
3
+ import numpy as np
4
+
5
+ from xtgeo.common.calc import _swap_axes
6
+ from xtgeo.common.constants import UNDEF
7
+ from xtgeo.common.log import null_logger
8
+
9
+ logger = null_logger(__name__)
10
+
11
+
12
+ def swapaxes(self):
13
+ """Swap the axes columns vs rows, keep origin. Will change yflip."""
14
+ self._rotation, self._yflip, swapped_values = _swap_axes(
15
+ self._rotation,
16
+ self._yflip,
17
+ values=self.values.filled(UNDEF),
18
+ )
19
+ self._ncol, self._nrow = self._nrow, self._ncol
20
+ self._xinc, self._yinc = self._yinc, self._xinc
21
+ self.values = swapped_values["values"]
22
+
23
+
24
+ def make_lefthanded(self):
25
+ """Will rearrange in case of right-handed system, ie. yflip = -1.
26
+
27
+ In contrasts to swapaxes(), this will change the origin.
28
+ """
29
+ if self.yflip == -1 or self.yinc < 0:
30
+ self._xori, self._yori, _ = self.get_xy_value_from_ij(1, self.nrow)
31
+ self._yflip = 1
32
+ self._yinc = abs(self.yinc)
33
+
34
+ # the values shall be reversed along the last axis
35
+ self._values = self._values[:, ::-1]
36
+ self._xlines = self._xlines[::-1]
37
+
38
+
39
+ def make_righthanded(self):
40
+ """Will rearrange in case of left-handed system, ie. yflip = 1.
41
+
42
+ In contrasts to swapaxes(), this will change the origin.
43
+ """
44
+ if self.yflip == 1 or self.yinc > 0:
45
+ self._xori, self._yori, _ = self.get_xy_value_from_ij(1, self.nrow)
46
+ self._yflip = -1
47
+ self._yinc = -1 * abs(self.yinc)
48
+
49
+ # the values shall be reversed along the last axis
50
+ self._values = self._values[:, ::-1]
51
+ self._xlines = self._xlines[::-1]
52
+
53
+
54
+ def autocrop(self):
55
+ """Crop surface by looking at undefined areas, update instance"""
56
+
57
+ minvalue = self.values.min()
58
+
59
+ if np.isnan(minvalue):
60
+ return
61
+
62
+ arrx, arry = np.ma.where(self.values >= minvalue)
63
+
64
+ imin = int(arrx.min())
65
+ imax = int(arrx.max())
66
+
67
+ jmin = int(arry.min())
68
+ jmax = int(arry.max())
69
+
70
+ xori, yori, _dummy = self.get_xy_value_from_ij(imin + 1, jmin + 1)
71
+
72
+ ncol = imax - imin + 1
73
+ nrow = jmax - jmin + 1
74
+
75
+ self._values = self.values[imin : imax + 1, jmin : jmax + 1]
76
+ self._ilines = self.ilines[imin : imax + 1]
77
+ self._xlines = self.xlines[jmin : jmax + 1]
78
+ self._ncol = ncol
79
+ self._nrow = nrow
80
+ self._xori = xori
81
+ self._yori = yori
@@ -0,0 +1,43 @@
1
+ """Import multiple surfaces"""
2
+
3
+ from xtgeo.common.log import null_logger
4
+
5
+ from .regular_surface import surface_from_grid3d
6
+
7
+ logger = null_logger(__name__)
8
+
9
+
10
+ def from_grid3d(grid, subgrids, rfactor):
11
+ """Get surfaces, subtype and order from 3D grid, including subgrids"""
12
+
13
+ logger.info("Extracting surface from 3D grid...")
14
+
15
+ # determine layers
16
+ layers = []
17
+ names = []
18
+ if subgrids and grid.subgrids is not None:
19
+ last = ""
20
+ for sgrd, srange in grid.subgrids.items():
21
+ layers.append(str(srange[0]) + "_top")
22
+ names.append(sgrd + "_top")
23
+ last = str(srange[-1])
24
+ lastname = sgrd
25
+ # base of last layer
26
+ layers.append(last + "_base")
27
+ names.append(lastname + "_base")
28
+ else:
29
+ layers.append("top")
30
+ names.append("top")
31
+ layers.append("base")
32
+ names.append("base")
33
+
34
+ # next extract these layers
35
+ layerstack = []
36
+ for inum, lay in enumerate(layers):
37
+ layer = surface_from_grid3d(grid, template=None, where=lay, rfactor=rfactor)
38
+ layer.name = names[inum]
39
+ layerstack.append(layer)
40
+
41
+ logger.info("Extracting surface from 3D grid... DONE")
42
+
43
+ return layerstack, "tops", "stratigraphic"