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,483 @@
1
+ """Operations along a well, private module."""
2
+
3
+ from copy import deepcopy
4
+
5
+ import numpy as np
6
+ import pandas as pd
7
+
8
+ from xtgeo import _cxtgeo
9
+ from xtgeo.common._xyz_enum import _AttrType
10
+ from xtgeo.common.constants import UNDEF_INT, UNDEF_INT_LIMIT
11
+ from xtgeo.common.log import null_logger
12
+ from xtgeo.xyz.points import Points
13
+
14
+ logger = null_logger(__name__)
15
+
16
+
17
+ def rescale(self, delta=0.15, tvdrange=None):
18
+ """Rescale by using a new MD increment.
19
+
20
+ The rescaling is technically done by interpolation in the Pandas dataframe
21
+ """
22
+ pdrows = pd.options.display.max_rows
23
+ pd.options.display.max_rows = 999
24
+
25
+ dfrcolumns0 = self.get_dataframe(copy=False).columns
26
+
27
+ if self.mdlogname is None:
28
+ self.geometrics()
29
+
30
+ dfrcolumns1 = self.get_dataframe(copy=False).columns
31
+ columnsadded = list(set(dfrcolumns1) - set(dfrcolumns0)) # new tmp columns, if any
32
+
33
+ dfr = self.get_dataframe().set_index(self.mdlogname)
34
+
35
+ logger.debug("Initial dataframe\n %s", dfr)
36
+
37
+ start = dfr.index[0]
38
+ stop = dfr.index[-1]
39
+ startt = start
40
+ stopt = stop
41
+
42
+ if tvdrange and isinstance(tvdrange, tuple) and len(tvdrange) == 2:
43
+ tvd1, tvd2 = tvdrange
44
+
45
+ try:
46
+ startt = dfr.index[dfr[self._wdata.zname] >= tvd1][0]
47
+ except IndexError:
48
+ startt = start
49
+
50
+ try:
51
+ stopt = dfr.index[dfr[self._wdata.zname] >= tvd2][0]
52
+ except IndexError:
53
+ stopt = stop
54
+
55
+ dfr1 = dfr[start:startt]
56
+ dfr2 = dfr[stopt:stop]
57
+
58
+ nentry = int(round((stopt - startt) / delta))
59
+
60
+ dfr = dfr.reindex(dfr.index.union(np.linspace(startt, stopt, num=nentry)))
61
+ dfr = dfr.interpolate("index", limit_area="inside").loc[
62
+ np.linspace(startt, stopt, num=nentry)
63
+ ]
64
+
65
+ dfr = pd.concat([dfr1, dfr, dfr2], sort=False)
66
+
67
+ dfr.drop_duplicates(inplace=True)
68
+ dfr[self.mdlogname] = dfr.index
69
+ dfr.reset_index(inplace=True, drop=True)
70
+
71
+ for lname in dfr.columns:
72
+ if lname in self.wlogtypes:
73
+ ltype = self.wlogtypes[lname]
74
+ if ltype == _AttrType.DISC.value:
75
+ dfr = dfr.round({lname: 0})
76
+
77
+ logger.debug("Updated dataframe:\n%s", dfr)
78
+
79
+ pd.options.display.max_rows = pdrows # reset
80
+
81
+ self.set_dataframe(dfr)
82
+ if columnsadded:
83
+ self.delete_log(columnsadded)
84
+
85
+
86
+ def make_zone_qual_log(self, zqname):
87
+ """Make a flag log based on stratigraphic relations."""
88
+ if zqname in self.get_dataframe(copy=False):
89
+ logger.warning("Quality log %s exists, will be overwritten", zqname)
90
+
91
+ if not self.zonelogname or self.zonelogname not in self.get_dataframe(copy=False):
92
+ raise ValueError("Cannot find a zonelog")
93
+
94
+ dff = self.get_filled_dataframe()
95
+ dff["ztmp"] = dff[self.zonelogname].copy()
96
+ dff["ztmp"] = (dff.ztmp != dff.ztmp.shift()).cumsum()
97
+
98
+ sgrp = dff.groupby("ztmp")
99
+
100
+ dff[zqname] = dff[self.zonelogname] * 0
101
+
102
+ idlist = []
103
+ seq = []
104
+ for idx, grp in sgrp:
105
+ izns = int(grp[self.zonelogname].mean())
106
+ seq.append(izns)
107
+ idlist.append(idx)
108
+
109
+ codes = {
110
+ 0: "UNDETERMINED",
111
+ 1: "INCREASE",
112
+ 2: "DECREASE",
113
+ 3: "U_TURN",
114
+ 4: "INV_U_TURN",
115
+ 9: "INCOMPLETE",
116
+ }
117
+
118
+ code = []
119
+ for ind, iseq in enumerate(seq):
120
+ if ind in (0, len(seq) - 1):
121
+ code.append(0)
122
+ else:
123
+ prev_ = seq[ind - 1]
124
+ next_ = seq[ind + 1]
125
+ if prev_ > UNDEF_INT_LIMIT or next_ > UNDEF_INT_LIMIT:
126
+ code.append(9)
127
+ elif next_ > iseq > prev_:
128
+ code.append(1)
129
+ elif next_ < iseq < prev_:
130
+ code.append(2)
131
+ elif next_ < iseq > prev_:
132
+ code.append(3)
133
+ elif next_ > iseq < prev_:
134
+ code.append(4)
135
+ dcode = dict(zip(idlist, code))
136
+
137
+ # now create the new log
138
+ self.create_log(zqname, logtype=_AttrType.DISC.value, logrecord=codes)
139
+ dataframe = self.get_dataframe()
140
+ for key, val in dcode.items():
141
+ dataframe.loc[dff["ztmp"] == key, zqname] = val
142
+
143
+ self.set_dataframe(dataframe)
144
+ # set the metadata
145
+ self.set_logtype(zqname, _AttrType.DISC.value)
146
+ self.set_logrecord(zqname, codes)
147
+ self._ensure_consistency()
148
+
149
+ del dff
150
+
151
+
152
+ def make_ijk_from_grid(self, grid, grid_id="", activeonly=True):
153
+ """Getting IJK from a grid and make as well logs."""
154
+ # establish a Points instance and make points dataframe from well trajectory X Y Z
155
+ wpoints = Points()
156
+ wpdf = self.get_dataframe().loc[:, [self.xname, self.yname, self.zname]]
157
+ wpdf.reset_index(inplace=True, drop=True)
158
+ wpoints.set_dataframe(wpdf)
159
+
160
+ # column names
161
+ cna = ("ICELL" + grid_id, "JCELL" + grid_id, "KCELL" + grid_id)
162
+
163
+ df = grid.get_ijk_from_points(
164
+ wpoints,
165
+ activeonly=activeonly,
166
+ zerobased=False,
167
+ dataframe=True,
168
+ includepoints=False,
169
+ columnnames=cna,
170
+ fmt="float",
171
+ undef=np.nan,
172
+ )
173
+
174
+ # The resulting df shall have same length as the well's dataframe,
175
+ # but the well index may not start from one. So first ignore index, then
176
+ # re-establish
177
+ wellindex = self.get_dataframe(copy=False).index
178
+
179
+ newdf = pd.concat([self.get_dataframe().reset_index(drop=True), df], axis=1)
180
+ newdf.index = wellindex
181
+
182
+ self.set_dataframe(newdf)
183
+
184
+
185
+ def get_gridproperties(self, gridprops, grid=("ICELL", "JCELL", "KCELL"), prop_id=""):
186
+ """Getting gridproperties as logs.
187
+
188
+ The routine will make grid_coordinates from grid with make_ijk_from_grid(), or reuse
189
+ existing vectors if grid is a tuple (much faster).
190
+ """
191
+ from xtgeo.grid3d.grid import Grid
192
+ from xtgeo.grid3d.grid_properties import GridProperties
193
+ from xtgeo.grid3d.grid_property import GridProperty
194
+
195
+ if not isinstance(gridprops, (GridProperty, GridProperties)):
196
+ raise ValueError('"gridprops" not a GridProperties or GridProperty instance')
197
+
198
+ if isinstance(gridprops, GridProperty):
199
+ gprops = GridProperties()
200
+ gprops.append_props([gridprops])
201
+ else:
202
+ gprops = gridprops
203
+
204
+ ijk_logs_created_tmp = False
205
+ if isinstance(grid, tuple):
206
+ icl, jcl, kcl = grid
207
+ elif isinstance(grid, Grid):
208
+ self.make_ijk_from_grid(grid, grid_id="_tmp")
209
+ icl, jcl, kcl = ("ICELL_tmp", "JCELL_tmp", "KCELL_tmp")
210
+ ijk_logs_created_tmp = True
211
+ else:
212
+ raise ValueError("The 'grid' is of wrong type, must be a tuple or a Grid")
213
+
214
+ # let grid values have base 1 when looking up cells for gridprops
215
+ iind = self.get_dataframe(copy=False)[icl].to_numpy(copy=True) - 1
216
+ jind = self.get_dataframe(copy=False)[jcl].to_numpy(copy=True) - 1
217
+ kind = self.get_dataframe(copy=False)[kcl].to_numpy(copy=True) - 1
218
+
219
+ xind = iind.copy()
220
+
221
+ iind[np.isnan(iind)] = 0
222
+ jind[np.isnan(jind)] = 0
223
+ kind[np.isnan(kind)] = 0
224
+
225
+ iind = iind.astype("int")
226
+ jind = jind.astype("int")
227
+ kind = kind.astype("int")
228
+ dfr = self.get_dataframe()
229
+
230
+ pnames = {}
231
+ for prop in gprops.props:
232
+ arr = prop.values[iind, jind, kind].astype("float")
233
+ arr = np.ma.filled(arr, fill_value=np.nan)
234
+ arr[np.isnan(xind)] = np.nan
235
+ pname = prop.name + prop_id
236
+ dfr[pname] = arr
237
+ pnames[pname] = (prop.isdiscrete, deepcopy(prop.codes))
238
+
239
+ self.set_dataframe(dfr)
240
+ for pname, isdiscrete_codes in pnames.items():
241
+ isdiscrete, codes = isdiscrete_codes
242
+ if isdiscrete:
243
+ self.set_logtype(pname, _AttrType.DISC.value)
244
+ self.set_logrecord(pname, codes)
245
+ else:
246
+ self.set_logtype(pname, _AttrType.CONT.value)
247
+ self.set_logrecord(pname, ("", ""))
248
+
249
+ if ijk_logs_created_tmp:
250
+ self.delete_logs(["ICELL_tmp", "JCELL_tmp", "KCELL_tmp"])
251
+
252
+
253
+ def report_zonation_holes(self, threshold=5):
254
+ """Reports if well has holes in zonation, less or equal to N samples."""
255
+
256
+ if self.zonelogname is None:
257
+ raise RuntimeError("No zonelog present for well")
258
+
259
+ wellreport = []
260
+
261
+ zlog = self._wdata.data[self.zonelogname].values.copy()
262
+
263
+ mdlog = None
264
+ if self.mdlogname:
265
+ mdlog = self._wdata.data[self.mdlogname].values
266
+
267
+ xvv = self._wdata.data[self.xname].values
268
+ yvv = self._wdata.data[self.yname].values
269
+ zvv = self._wdata.data[self.zname].values
270
+ zlog[np.isnan(zlog)] = UNDEF_INT
271
+
272
+ ncv = 0
273
+ first = True
274
+ hole = False
275
+ for ind, zone in np.ndenumerate(zlog):
276
+ ino = ind[0]
277
+ if zone > UNDEF_INT_LIMIT and first:
278
+ continue
279
+
280
+ if zone < UNDEF_INT_LIMIT and first:
281
+ first = False
282
+ continue
283
+
284
+ if zone > UNDEF_INT_LIMIT:
285
+ ncv += 1
286
+ hole = True
287
+
288
+ if zone > UNDEF_INT_LIMIT and ncv > threshold:
289
+ logger.debug("Restart first (bigger hole)")
290
+ hole = False
291
+ first = True
292
+ ncv = 0
293
+ continue
294
+
295
+ if hole and zone < UNDEF_INT_LIMIT and ncv <= threshold:
296
+ # here we have a hole that fits criteria
297
+ if mdlog is not None:
298
+ entry = (
299
+ ino,
300
+ xvv[ino],
301
+ yvv[ino],
302
+ zvv[ino],
303
+ int(zone),
304
+ self.xwellname,
305
+ mdlog[ino],
306
+ )
307
+ else:
308
+ entry = (ino, xvv[ino], yvv[ino], zvv[ino], int(zone), self.xwellname)
309
+
310
+ wellreport.append(entry)
311
+
312
+ # restart count
313
+ hole = False
314
+ ncv = 0
315
+
316
+ if hole and zone < UNDEF_INT_LIMIT and ncv > threshold:
317
+ hole = False
318
+ ncv = 0
319
+
320
+ if not wellreport: # ie length is 0
321
+ return None
322
+
323
+ if mdlog is not None:
324
+ clm = ["INDEX", self.xname, self.yname, self.zname, "Zone", "Well", "MD"]
325
+ else:
326
+ clm = ["INDEX", self.xname, self.yname, self.zname, "Zone", "Well"]
327
+
328
+ return pd.DataFrame(wellreport, columns=clm)
329
+
330
+
331
+ def mask_shoulderbeds(self, inputlogs, targetlogs, nsamples, strict):
332
+ """Mask targetlogs around discrete boundaries.
333
+
334
+ Returns True if inputlog(s) and targetlog(s) are present; otherwise False.
335
+ """
336
+ logger.debug("Mask shoulderbeds for some logs...")
337
+
338
+ useinputs, usetargets, use_numeric = _mask_shoulderbeds_checks(
339
+ self, inputlogs, targetlogs, nsamples, strict
340
+ )
341
+
342
+ if not useinputs or not usetargets:
343
+ logger.debug("Mask shoulderbeds for some logs... nothing done")
344
+ return False
345
+
346
+ for inlog in useinputs:
347
+ inseries = self._wdata.data[inlog]
348
+ if use_numeric:
349
+ bseries = _get_bseries(inseries, nsamples)
350
+ else:
351
+ mode, value = list(nsamples.items())[0]
352
+
353
+ depth = self._wdata.data[self.zname]
354
+ if mode == "md" and self.mdlogname is not None:
355
+ depth = self._wdata.data[self.mdlogname]
356
+ elif mode == "md" and self.mdlogname is None:
357
+ raise ValueError("There is no mdlogname attribute present.")
358
+
359
+ bseries = _get_bseries_by_distance(depth, inseries, value)
360
+
361
+ for target in usetargets:
362
+ self._wdata.data.loc[bseries, target] = np.nan
363
+
364
+ logger.debug("Mask shoulderbeds for some logs... done")
365
+ return True
366
+
367
+
368
+ def _mask_shoulderbeds_checks(self, inputlogs, targetlogs, nsamples, strict):
369
+ """Checks/validates input for mask targetlogs around discrete boundaries."""
370
+ # check that inputlogs exists and that they are discrete, and targetlogs
371
+ useinputs = []
372
+ for inlog in inputlogs:
373
+ if inlog not in self.wlogtypes and strict is True:
374
+ raise ValueError(f"Input log {inlog} is missing and strict=True")
375
+ if inlog in self.wlogtypes and self.wlogtypes[inlog] != _AttrType.DISC.value:
376
+ raise ValueError(f"Input log {inlog} is not of type DISC")
377
+ if inlog in self.wlogtypes:
378
+ useinputs.append(inlog)
379
+
380
+ usetargets = []
381
+ for target in targetlogs:
382
+ if target not in self.wlogtypes and strict is True:
383
+ raise ValueError(f"Target log {target} is missing and strict=True")
384
+ if target in self.wlogtypes:
385
+ usetargets.append(target)
386
+
387
+ use_numeric = True
388
+ if isinstance(nsamples, int):
389
+ maxlen = self.nrow // 2
390
+ if nsamples < 1 or nsamples > maxlen:
391
+ raise ValueError(f"Keyword nsamples must be an int > 1 and < {maxlen}")
392
+ elif isinstance(nsamples, dict):
393
+ if len(nsamples) == 1 and any(key in nsamples for key in ["md", "tvd"]):
394
+ use_numeric = False
395
+ else:
396
+ raise ValueError(f"Keyword nsamples is incorrect in some way: {nsamples}")
397
+ else:
398
+ raise ValueError("Keyword nsamples is not an int or a dictionary")
399
+
400
+ # return a list of input logs to be used (useinputs), a list of target logs to
401
+ # be used (usetargets) and a use_numeric bool (True if nsamples is an int)
402
+ return useinputs, usetargets, use_numeric
403
+
404
+
405
+ def _get_bseries(inseries, nsamples):
406
+ """Return a bool filter based on number of samples."""
407
+ if not isinstance(inseries, pd.Series):
408
+ raise RuntimeError("Bug, input must be a pandas Series() instance.")
409
+
410
+ if len(inseries) == 0:
411
+ return pd.Series([], dtype=bool)
412
+
413
+ # nsmaples < 1 or input series with <= 1 element will not be prosessed
414
+ if nsamples < 1 or len(inseries) <= 1:
415
+ return pd.Series(inseries, dtype=bool).replace(True, False)
416
+
417
+ def _growfilter(bseries, nleft):
418
+ if not nleft:
419
+ return bseries
420
+
421
+ return _growfilter(bseries | bseries.shift(-1) | bseries.shift(1), nleft - 1)
422
+
423
+ # make a tmp mask log (series) based input logs and use that for mask filterings
424
+ transitions = inseries.diff().abs() > 0
425
+ bseries = transitions | transitions.shift(-1)
426
+
427
+ return _growfilter(bseries, nsamples - 1)
428
+
429
+
430
+ def _get_bseries_by_distance(depth, inseries, distance):
431
+ """Return a bool filter defined by distance to log breaks."""
432
+ if not isinstance(inseries, pd.Series):
433
+ raise RuntimeError("BUG: input must be a pandas Series() instance.")
434
+
435
+ if len(inseries) == 0:
436
+ return pd.Series([], dtype=bool)
437
+
438
+ # Input series with <= 1 element will not be prosessed
439
+ if len(inseries) <= 1:
440
+ return pd.Series(inseries, dtype=bool).replace(True, False)
441
+
442
+ bseries = pd.Series(np.zeros(inseries.values.size), dtype="int32").values
443
+ try:
444
+ inseries = np.nan_to_num(inseries.values, nan=UNDEF_INT).astype("int32")
445
+ except TypeError:
446
+ # for older numpy version
447
+ inseries = inseries.values
448
+ inseries[np.isnan(inseries)] = UNDEF_INT
449
+ inseries = inseries.astype("int32")
450
+
451
+ res = _cxtgeo.well_mask_shoulder(
452
+ depth.values.astype("float64"), inseries, bseries, distance
453
+ )
454
+
455
+ if res != 0:
456
+ raise RuntimeError("BUG: return from _cxtgeo.well_mask_shoulder not zero")
457
+
458
+ return np.array(bseries, dtype=bool)
459
+
460
+
461
+ def create_surf_distance_log(self, surf, name):
462
+ """Create a log which is vertical distance from a RegularSurface."""
463
+ from xtgeo.surface.regular_surface import RegularSurface
464
+
465
+ logger.debug("Create a log which is distance to surface")
466
+
467
+ if not isinstance(surf, RegularSurface):
468
+ raise ValueError("Input surface is not a RegularSurface instance.")
469
+
470
+ # make a Points instance since points has the snap
471
+ zvalues = self.get_dataframe()[self.zname]
472
+ points = Points()
473
+ dframe = self.get_dataframe().iloc[:, 0:3]
474
+ points.set_dataframe(dframe)
475
+ points.snap_surface(surf)
476
+ snapped = points.get_dataframe(copy=False)[self.zname]
477
+ diff = snapped - zvalues
478
+
479
+ # create log (default is force overwrite if it exists)
480
+ self.create_log(name)
481
+ dframe = self.get_dataframe()
482
+ dframe[name] = diff
483
+ self.set_dataframe(dframe)