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,486 @@
|
|
|
1
|
+
"""Well marker data; private module"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
import pandas as pd
|
|
7
|
+
|
|
8
|
+
from xtgeo import _cxtgeo
|
|
9
|
+
from xtgeo.common.constants import (
|
|
10
|
+
UNDEF,
|
|
11
|
+
UNDEF_DISC,
|
|
12
|
+
UNDEF_INT,
|
|
13
|
+
UNDEF_INT_LIMIT,
|
|
14
|
+
UNDEF_LIMIT,
|
|
15
|
+
)
|
|
16
|
+
from xtgeo.common.log import null_logger
|
|
17
|
+
from xtgeo.xyz.points import Points
|
|
18
|
+
|
|
19
|
+
logger = null_logger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get_zonation_points(
|
|
23
|
+
self: Points, tops, incl_limit, top_prefix, zonelist, use_undef
|
|
24
|
+
):
|
|
25
|
+
"""
|
|
26
|
+
Getting zonation tops (private routine)
|
|
27
|
+
|
|
28
|
+
Args, see calling routine
|
|
29
|
+
"""
|
|
30
|
+
scopy = self.copy() # avoid altering original instance
|
|
31
|
+
|
|
32
|
+
scopy.geometrics()
|
|
33
|
+
dataframe = scopy.get_dataframe()
|
|
34
|
+
|
|
35
|
+
# as zlog is float64; need to convert to int array with high number as undef
|
|
36
|
+
if scopy.zonelogname is not None:
|
|
37
|
+
if use_undef:
|
|
38
|
+
dataframe.dropna(subset=[scopy.zonelogname], inplace=True)
|
|
39
|
+
zlog = dataframe[scopy.zonelogname].values
|
|
40
|
+
zlog[np.isnan(zlog)] = UNDEF_DISC
|
|
41
|
+
zlog = np.rint(zlog).astype(int)
|
|
42
|
+
else:
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
xvv = dataframe[scopy.xname].values
|
|
46
|
+
yvv = dataframe[scopy.yname].values
|
|
47
|
+
zvv = dataframe[scopy.zname].values
|
|
48
|
+
incl = dataframe["Q_INCL"].values
|
|
49
|
+
mdv = dataframe["Q_MDEPTH"].values
|
|
50
|
+
|
|
51
|
+
if scopy.mdlogname is not None:
|
|
52
|
+
mdv = dataframe[scopy.mdlogname].values
|
|
53
|
+
|
|
54
|
+
if zonelist is None:
|
|
55
|
+
# need to declare as list; otherwise Py3 will get dict.keys
|
|
56
|
+
zonelist = list(scopy.get_logrecord(scopy.zonelogname).keys())
|
|
57
|
+
|
|
58
|
+
logger.debug("Find values for %s", zonelist)
|
|
59
|
+
|
|
60
|
+
ztops, ztopnames, zisos, zisonames = _extract_ztops(
|
|
61
|
+
scopy,
|
|
62
|
+
zonelist,
|
|
63
|
+
xvv,
|
|
64
|
+
yvv,
|
|
65
|
+
zvv,
|
|
66
|
+
zlog,
|
|
67
|
+
mdv,
|
|
68
|
+
incl,
|
|
69
|
+
tops=tops,
|
|
70
|
+
incl_limit=incl_limit,
|
|
71
|
+
prefix=top_prefix,
|
|
72
|
+
use_undef=use_undef,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
zlist = ztops if tops else zisos
|
|
76
|
+
|
|
77
|
+
# collect temporary columns, made by geometrics() and _extract_ztops() methods
|
|
78
|
+
tmp_columns = [
|
|
79
|
+
col
|
|
80
|
+
for col in (ztopnames or []) + (zisonames or [])
|
|
81
|
+
if col.startswith("Q_") and col not in self.get_lognames()
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
dataframe = (
|
|
85
|
+
pd.DataFrame(zlist, columns=ztopnames)
|
|
86
|
+
if tops
|
|
87
|
+
else pd.DataFrame(zlist, columns=zisonames)
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
return dataframe.drop(columns=tmp_columns, errors="ignore")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _extract_ztops(
|
|
94
|
+
self,
|
|
95
|
+
zonelist,
|
|
96
|
+
xcv,
|
|
97
|
+
ycv,
|
|
98
|
+
zcv,
|
|
99
|
+
zlog,
|
|
100
|
+
mdv,
|
|
101
|
+
incl,
|
|
102
|
+
tops=True,
|
|
103
|
+
incl_limit=80,
|
|
104
|
+
prefix="Top",
|
|
105
|
+
use_undef=False,
|
|
106
|
+
):
|
|
107
|
+
"""Extract a list of tops for a zone.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
zonelist (list-like): The zonelog list numbers to apply; either
|
|
111
|
+
as a list, or a tuple; 2 entries forms a range [start, stop]
|
|
112
|
+
xcv (np): X Position numpy array
|
|
113
|
+
ycv (np): Y Position numpy array
|
|
114
|
+
zcv (np): Z Position numpy array
|
|
115
|
+
zlog (np): Zonelog array
|
|
116
|
+
mdv (np): MDepth log numpy array
|
|
117
|
+
incl (np): Inclination log numpy array
|
|
118
|
+
tops (bool): Compute tops or thickness (zone) points (default True)
|
|
119
|
+
incl_limit (float): Limitation of zone computation (angle, degrees)
|
|
120
|
+
use_undef (bool): If True, then transition from UNDEF is also
|
|
121
|
+
used.
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
# The wellpoints will be a list of tuples (one tuple per hit)
|
|
125
|
+
wpts = []
|
|
126
|
+
zlogname = self.zonelogname
|
|
127
|
+
|
|
128
|
+
if not tops and incl_limit is None:
|
|
129
|
+
incl_limit = 80
|
|
130
|
+
|
|
131
|
+
azi = -999.0 # tmp so far
|
|
132
|
+
|
|
133
|
+
if isinstance(zonelist, tuple):
|
|
134
|
+
if len(zonelist) != 2:
|
|
135
|
+
raise ValueError(
|
|
136
|
+
f"zonelist given as tuple must be of length 2, was {len(zonelist)}"
|
|
137
|
+
)
|
|
138
|
+
usezonerange = range(zonelist[0], zonelist[1] + 1)
|
|
139
|
+
elif isinstance(zonelist, list):
|
|
140
|
+
if len(zonelist) == 1:
|
|
141
|
+
raise ValueError(
|
|
142
|
+
f"zonelist given as list must contain two or more"
|
|
143
|
+
f" elements, had 1: {zonelist}"
|
|
144
|
+
)
|
|
145
|
+
usezonerange = zonelist
|
|
146
|
+
else:
|
|
147
|
+
raise TypeError(
|
|
148
|
+
f"zonelist must be either list (of two or more elements) or "
|
|
149
|
+
f"a tuple (with two elements representing start and stop), was"
|
|
150
|
+
f"{type(zonelist)}"
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
# check if increasing monotonic and with no jumps:
|
|
154
|
+
if not all(i + 1 == j for i, j in zip(usezonerange, usezonerange[1:])):
|
|
155
|
+
raise ValueError(
|
|
156
|
+
f"zonelist must be strictly increasing with increment of one,"
|
|
157
|
+
f" was {usezonerange}"
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
iundef = UNDEF_INT
|
|
161
|
+
iundeflimit = UNDEF_INT_LIMIT
|
|
162
|
+
pzone = iundef
|
|
163
|
+
|
|
164
|
+
if use_undef:
|
|
165
|
+
pzone = usezonerange[0] - 1
|
|
166
|
+
|
|
167
|
+
for ind, zone in np.ndenumerate(zlog):
|
|
168
|
+
ino = ind[0] # since ind is a tuple...
|
|
169
|
+
|
|
170
|
+
if pzone != zone and pzone < iundeflimit and zone < iundeflimit:
|
|
171
|
+
logger.debug("Found break in zonation")
|
|
172
|
+
if pzone < zone:
|
|
173
|
+
logger.debug(
|
|
174
|
+
"Found match, increasing zonation at %s < %s (MD %s)",
|
|
175
|
+
pzone,
|
|
176
|
+
zone,
|
|
177
|
+
mdv[ino],
|
|
178
|
+
)
|
|
179
|
+
for kzv in range(pzone + 1, zone + 1):
|
|
180
|
+
if kzv in usezonerange:
|
|
181
|
+
zname = self.get_logrecord_codename(zlogname, kzv)
|
|
182
|
+
zname = prefix + zname
|
|
183
|
+
ztop = (
|
|
184
|
+
xcv[ino],
|
|
185
|
+
ycv[ino],
|
|
186
|
+
zcv[ino],
|
|
187
|
+
mdv[ino],
|
|
188
|
+
incl[ino],
|
|
189
|
+
azi,
|
|
190
|
+
kzv,
|
|
191
|
+
zname,
|
|
192
|
+
self.xwellname,
|
|
193
|
+
)
|
|
194
|
+
wpts.append(ztop)
|
|
195
|
+
if pzone > zone and ino > 0:
|
|
196
|
+
logger.debug(
|
|
197
|
+
"Found match, decreasing zonation at %s > %s (MD %s)",
|
|
198
|
+
pzone,
|
|
199
|
+
zone,
|
|
200
|
+
mdv[ino - 1],
|
|
201
|
+
)
|
|
202
|
+
for kzv in range(pzone, zone, -1):
|
|
203
|
+
if kzv in usezonerange:
|
|
204
|
+
zname = self.get_logrecord_codename(zlogname, kzv)
|
|
205
|
+
zname = prefix + zname
|
|
206
|
+
ztop = (
|
|
207
|
+
xcv[ino - 1],
|
|
208
|
+
ycv[ino - 1],
|
|
209
|
+
zcv[ino - 1],
|
|
210
|
+
mdv[ino - 1],
|
|
211
|
+
incl[ino - 1],
|
|
212
|
+
azi,
|
|
213
|
+
kzv,
|
|
214
|
+
zname,
|
|
215
|
+
self.xwellname,
|
|
216
|
+
)
|
|
217
|
+
wpts.append(ztop)
|
|
218
|
+
pzone = zone
|
|
219
|
+
|
|
220
|
+
wpts_names = [
|
|
221
|
+
self.xname,
|
|
222
|
+
self.yname,
|
|
223
|
+
self.zname,
|
|
224
|
+
self.mdlogname,
|
|
225
|
+
"Q_INCL",
|
|
226
|
+
"Q_AZI",
|
|
227
|
+
"Zone",
|
|
228
|
+
"TopName",
|
|
229
|
+
"WellName",
|
|
230
|
+
]
|
|
231
|
+
|
|
232
|
+
if tops:
|
|
233
|
+
return wpts, wpts_names, None, None
|
|
234
|
+
|
|
235
|
+
# next get a MIDPOINT zthickness (DZ)
|
|
236
|
+
llen = len(wpts) - 1
|
|
237
|
+
|
|
238
|
+
zwpts_names = [
|
|
239
|
+
self.xname,
|
|
240
|
+
self.yname,
|
|
241
|
+
self.zname,
|
|
242
|
+
self.mdlogname + "_AVG",
|
|
243
|
+
"Q_MD1",
|
|
244
|
+
"Q_MD2",
|
|
245
|
+
"Q_INCL",
|
|
246
|
+
"Q_AZI",
|
|
247
|
+
"Zone",
|
|
248
|
+
"ZoneName",
|
|
249
|
+
"WellName",
|
|
250
|
+
]
|
|
251
|
+
|
|
252
|
+
zwpts = []
|
|
253
|
+
for ino in range(llen):
|
|
254
|
+
i1v = ino
|
|
255
|
+
i2v = ino + 1
|
|
256
|
+
xx1, yy1, zz1, md1, incl1, _azi1, zk1, zn1, wn1 = wpts[i1v]
|
|
257
|
+
xx2, yy2, zz2, md2, incl2, _azi2, zk2, zn2, _wn2 = wpts[i2v]
|
|
258
|
+
|
|
259
|
+
# mid point
|
|
260
|
+
xx_avg = (xx1 + xx2) / 2
|
|
261
|
+
yy_avg = (yy1 + yy2) / 2
|
|
262
|
+
md_avg = (md1 + md2) / 2
|
|
263
|
+
incl_avg = (incl1 + incl2) / 2
|
|
264
|
+
|
|
265
|
+
azi_avg = -999.0 # to be fixed later
|
|
266
|
+
|
|
267
|
+
zzp = round(abs(zz2 - zz1), 4)
|
|
268
|
+
|
|
269
|
+
useok = False
|
|
270
|
+
|
|
271
|
+
if incl_avg < incl_limit:
|
|
272
|
+
useok = True
|
|
273
|
+
|
|
274
|
+
if useok and zk2 != zk1:
|
|
275
|
+
usezk = zk1
|
|
276
|
+
usezn = zn1
|
|
277
|
+
if zk1 > zk2:
|
|
278
|
+
usezk = zk2
|
|
279
|
+
usezn = zn2
|
|
280
|
+
usezn = usezn[len(prefix) :]
|
|
281
|
+
|
|
282
|
+
zzone = (
|
|
283
|
+
xx_avg,
|
|
284
|
+
yy_avg,
|
|
285
|
+
zzp,
|
|
286
|
+
md_avg,
|
|
287
|
+
md1,
|
|
288
|
+
md2,
|
|
289
|
+
incl_avg,
|
|
290
|
+
azi_avg,
|
|
291
|
+
usezk,
|
|
292
|
+
usezn,
|
|
293
|
+
wn1,
|
|
294
|
+
)
|
|
295
|
+
zwpts.append(zzone)
|
|
296
|
+
|
|
297
|
+
return wpts, wpts_names, zwpts, zwpts_names
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def get_fraction_per_zone(
|
|
301
|
+
self,
|
|
302
|
+
dlogname,
|
|
303
|
+
dvalues,
|
|
304
|
+
zonelist=None,
|
|
305
|
+
incl_limit=80,
|
|
306
|
+
count_limit=3,
|
|
307
|
+
zonelogname=None,
|
|
308
|
+
):
|
|
309
|
+
"""Fraction of e.g. a facies in a zone segment.
|
|
310
|
+
|
|
311
|
+
X_UTME Y_UTMN Z_TVDSS Zonelog Facies M_INCL
|
|
312
|
+
464011.719 5931757.257 1663.1079 3.0 1.0 10.2
|
|
313
|
+
464011.751 5931757.271 1663.6084 3.0 1.0 10.3
|
|
314
|
+
464011.783 5931757.285 1664.1090 3.0 2.0 11.2
|
|
315
|
+
464011.815 5931757.299 1664.6097 3.0 2.0 11.4
|
|
316
|
+
464011.847 5931757.313 1665.1105 3.0 2.0 11.5
|
|
317
|
+
464011.879 5931757.326 1665.6114 3.0 2.0 12.0
|
|
318
|
+
464011.911 5931757.340 1666.1123 3.0 1.0 12.2
|
|
319
|
+
464011.943 5931757.354 1666.6134 3.0 1.0 13.4
|
|
320
|
+
|
|
321
|
+
Count fraction of one or more facies (dvalues list)
|
|
322
|
+
filtered on a zone, given that Inclination is below limit all over.
|
|
323
|
+
Since a zone can be repeated, it is important to split
|
|
324
|
+
into segments by POLY_ID. When fraction is determined, the
|
|
325
|
+
AVG X Y coord is applied.
|
|
326
|
+
|
|
327
|
+
If there are one or more occurences of undef for the dlogname
|
|
328
|
+
in that interval, no value shall be computed.
|
|
329
|
+
|
|
330
|
+
Args:
|
|
331
|
+
dlogname (str): Name of discrete log e.g. Facies
|
|
332
|
+
dvalues (list): List of codes to sum fraction upon
|
|
333
|
+
zonelist (list): List of zones to compute over
|
|
334
|
+
incl_limit (float): Skip if max inclination found > incl_limit
|
|
335
|
+
count_limit (int): Minimum number of samples required per segment
|
|
336
|
+
|
|
337
|
+
Returns:
|
|
338
|
+
A dataframe with relevant data...
|
|
339
|
+
|
|
340
|
+
"""
|
|
341
|
+
logger.debug("The zonelist is %s", zonelist)
|
|
342
|
+
logger.debug("The dlogname is %s", dlogname)
|
|
343
|
+
logger.debug("The dvalues are %s", dvalues)
|
|
344
|
+
|
|
345
|
+
if zonelogname is not None:
|
|
346
|
+
usezonelogname = zonelogname
|
|
347
|
+
self.zonelogname = zonelogname
|
|
348
|
+
else:
|
|
349
|
+
usezonelogname = self.zonelogname
|
|
350
|
+
|
|
351
|
+
if usezonelogname is None:
|
|
352
|
+
raise RuntimeError("Stop, zonelogname is None")
|
|
353
|
+
|
|
354
|
+
self.make_zone_qual_log("_QFLAG")
|
|
355
|
+
|
|
356
|
+
if zonelist is None:
|
|
357
|
+
# need to declare as list; otherwise Py3 will get dict.keys
|
|
358
|
+
zonelist = list(self.get_logrecord(self.zonelogname).keys())
|
|
359
|
+
|
|
360
|
+
useinclname = "Q_INCL"
|
|
361
|
+
if "M_INCL" in self.get_dataframe(copy=False):
|
|
362
|
+
useinclname = "M_INCL"
|
|
363
|
+
else:
|
|
364
|
+
self.geometrics()
|
|
365
|
+
|
|
366
|
+
result = {}
|
|
367
|
+
result[self.xname] = []
|
|
368
|
+
result[self.yname] = []
|
|
369
|
+
result["DFRAC"] = []
|
|
370
|
+
result["Q_INCL"] = []
|
|
371
|
+
result["ZONE"] = []
|
|
372
|
+
result["WELLNAME"] = []
|
|
373
|
+
result[dlogname] = []
|
|
374
|
+
|
|
375
|
+
svalues = str(dvalues).rstrip("]").lstrip("[").replace(", ", "+")
|
|
376
|
+
|
|
377
|
+
xtralogs = [dlogname, useinclname, "_QFLAG"]
|
|
378
|
+
for izon in zonelist:
|
|
379
|
+
logger.debug("The zone number is %s", izon)
|
|
380
|
+
logger.debug("The extralogs are %s", xtralogs)
|
|
381
|
+
|
|
382
|
+
dfr = self.get_zone_interval(izon, extralogs=xtralogs)
|
|
383
|
+
|
|
384
|
+
if dfr is None:
|
|
385
|
+
continue
|
|
386
|
+
|
|
387
|
+
dfrx = dfr.groupby("POLY_ID")
|
|
388
|
+
|
|
389
|
+
for _polyid, dframe in dfrx:
|
|
390
|
+
qinclmax = dframe["Q_INCL"].max()
|
|
391
|
+
qinclavg = dframe["Q_INCL"].mean()
|
|
392
|
+
qflag = dframe["_QFLAG"].mean()
|
|
393
|
+
dseries = dframe[dlogname]
|
|
394
|
+
if qflag < 0.5 or qflag > 2.5: # 1 or 2 is OK
|
|
395
|
+
logger.debug("Skipped due to zone %s", qflag)
|
|
396
|
+
continue
|
|
397
|
+
if qinclmax > incl_limit:
|
|
398
|
+
logger.debug("Skipped due to max inclination %s", qinclmax)
|
|
399
|
+
continue
|
|
400
|
+
if dseries.size < count_limit: # interval too short for fraction
|
|
401
|
+
logger.debug("Skipped due to too few values %s", dseries.size)
|
|
402
|
+
continue
|
|
403
|
+
if dseries.max() > UNDEF_INT_LIMIT:
|
|
404
|
+
logger.debug("Skipped due to too missing/undef value(s)")
|
|
405
|
+
continue
|
|
406
|
+
|
|
407
|
+
xavg = dframe[self.xname].mean()
|
|
408
|
+
yavg = dframe[self.yname].mean()
|
|
409
|
+
|
|
410
|
+
dfrac = 0.0
|
|
411
|
+
for dval in dvalues:
|
|
412
|
+
if any(dseries.isin([dval])):
|
|
413
|
+
dfrac += dseries.value_counts(normalize=True)[dval]
|
|
414
|
+
|
|
415
|
+
result[self.xname].append(xavg)
|
|
416
|
+
result[self.yname].append(yavg)
|
|
417
|
+
result["DFRAC"].append(dfrac)
|
|
418
|
+
result["Q_INCL"].append(qinclavg)
|
|
419
|
+
result["ZONE"].append(izon)
|
|
420
|
+
result["WELLNAME"].append(self.xwellname)
|
|
421
|
+
result[dlogname].append(svalues)
|
|
422
|
+
|
|
423
|
+
# make the dataframe and return it
|
|
424
|
+
if result[self.xname]:
|
|
425
|
+
return pd.DataFrame.from_dict(result)
|
|
426
|
+
|
|
427
|
+
self.delete_log("_QFLAG")
|
|
428
|
+
|
|
429
|
+
return None
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
def get_surface_picks(self, surf):
|
|
433
|
+
"""get Surface picks"""
|
|
434
|
+
|
|
435
|
+
dataframe = self.get_dataframe(copy=False)
|
|
436
|
+
xcor = dataframe[self.xname].values
|
|
437
|
+
ycor = dataframe[self.yname].values
|
|
438
|
+
zcor = dataframe[self.zname].values
|
|
439
|
+
|
|
440
|
+
if self.mdlogname:
|
|
441
|
+
mcor = dataframe[self.mdlogname].values
|
|
442
|
+
else:
|
|
443
|
+
mcor = np.zeros(xcor.size, dtype=np.float64) + UNDEF
|
|
444
|
+
|
|
445
|
+
nval, xres, yres, zres, mres, dres = _cxtgeo.well_surf_picks(
|
|
446
|
+
xcor,
|
|
447
|
+
ycor,
|
|
448
|
+
zcor,
|
|
449
|
+
mcor,
|
|
450
|
+
surf.ncol,
|
|
451
|
+
surf.nrow,
|
|
452
|
+
surf.xori,
|
|
453
|
+
surf.yori,
|
|
454
|
+
surf.xinc,
|
|
455
|
+
surf.yinc,
|
|
456
|
+
surf.yflip,
|
|
457
|
+
surf.rotation,
|
|
458
|
+
surf.npvalues1d,
|
|
459
|
+
xcor.size,
|
|
460
|
+
xcor.size,
|
|
461
|
+
xcor.size,
|
|
462
|
+
xcor.size,
|
|
463
|
+
xcor.size,
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
if nval > 0:
|
|
467
|
+
poi = Points()
|
|
468
|
+
|
|
469
|
+
mres[mres > UNDEF_LIMIT] = np.nan
|
|
470
|
+
|
|
471
|
+
res = {}
|
|
472
|
+
res[poi.xname] = xres[:nval]
|
|
473
|
+
res[poi.yname] = yres[:nval]
|
|
474
|
+
res[poi.zname] = zres[:nval]
|
|
475
|
+
if self.mdlogname:
|
|
476
|
+
res[self.mdlogname] = mres[:nval]
|
|
477
|
+
res["DIRECTION"] = dres[:nval]
|
|
478
|
+
res["WELLNAME"] = self.wellname
|
|
479
|
+
|
|
480
|
+
poi.set_dataframe(pd.DataFrame.from_dict(res))
|
|
481
|
+
|
|
482
|
+
return poi
|
|
483
|
+
|
|
484
|
+
return None
|
|
485
|
+
|
|
486
|
+
# return a xtgeo Poinst() object with points as dataframe, given that nval > 0
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"""Utilities for Wells class"""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
import pandas as pd
|
|
7
|
+
import shapely.geometry as sg
|
|
8
|
+
|
|
9
|
+
from xtgeo.common.xtgeo_dialog import XTGeoDialog, XTGShowProgress
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
logger.addHandler(logging.NullHandler())
|
|
13
|
+
|
|
14
|
+
xtg = XTGeoDialog()
|
|
15
|
+
|
|
16
|
+
# self == Wells instance (plural wells)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def wellintersections(
|
|
20
|
+
self,
|
|
21
|
+
wfilter=None,
|
|
22
|
+
showprogress=False,
|
|
23
|
+
):
|
|
24
|
+
"""Get intersections between wells, return as dataframe table.
|
|
25
|
+
|
|
26
|
+
This routine is using "shapely" functions!
|
|
27
|
+
|
|
28
|
+
Some actions are done in order to filter away the part of the trajectories
|
|
29
|
+
that are paralell.
|
|
30
|
+
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
xpoints = []
|
|
34
|
+
|
|
35
|
+
# make a dict if nocrossings
|
|
36
|
+
nox = {}
|
|
37
|
+
|
|
38
|
+
wlen = len(self.wells)
|
|
39
|
+
|
|
40
|
+
progress = XTGShowProgress(wlen, show=showprogress, leadtext="progress: ", skip=5)
|
|
41
|
+
|
|
42
|
+
for iwell, well in enumerate(self.wells):
|
|
43
|
+
progress.flush(iwell)
|
|
44
|
+
|
|
45
|
+
logger.debug("Work with %s", well.name)
|
|
46
|
+
try:
|
|
47
|
+
well.geometrics()
|
|
48
|
+
except ValueError:
|
|
49
|
+
logger.debug("Skip %s (cannot compute geometrics)", well.name)
|
|
50
|
+
continue
|
|
51
|
+
|
|
52
|
+
welldfr = well.get_dataframe()
|
|
53
|
+
|
|
54
|
+
xcor = welldfr[well.xname].values
|
|
55
|
+
ycor = welldfr[well.yname].values
|
|
56
|
+
mcor = welldfr[well.mdlogname].values
|
|
57
|
+
logger.debug("The mdlogname property is: %s", well.mdlogname)
|
|
58
|
+
|
|
59
|
+
if xcor.size < 2:
|
|
60
|
+
continue
|
|
61
|
+
|
|
62
|
+
thisline1 = sg.LineString(np.stack([xcor, ycor], axis=1))
|
|
63
|
+
thisline2 = sg.LineString(np.stack([xcor, ycor, mcor], axis=1))
|
|
64
|
+
|
|
65
|
+
nox[well.name] = []
|
|
66
|
+
# loop over other wells
|
|
67
|
+
for other in self.wells:
|
|
68
|
+
if other.name == well.name:
|
|
69
|
+
continue # same well
|
|
70
|
+
|
|
71
|
+
if not well.may_overlap(other):
|
|
72
|
+
nox[well.name].append(other.name)
|
|
73
|
+
continue # a quick check; no chance for overlap
|
|
74
|
+
|
|
75
|
+
logger.debug("Consider crossing with %s ...", other.name)
|
|
76
|
+
|
|
77
|
+
# try to be smart to skip entries that earlier have beenn tested
|
|
78
|
+
# for crossing. If other does not cross well, then well does not
|
|
79
|
+
# cross other...
|
|
80
|
+
if other.name in nox and well.name in nox[other.name]:
|
|
81
|
+
continue
|
|
82
|
+
|
|
83
|
+
# truncate away the paralell part on a copy
|
|
84
|
+
owell = other.copy()
|
|
85
|
+
|
|
86
|
+
# wfilter = None
|
|
87
|
+
if wfilter is not None and "parallel" in wfilter:
|
|
88
|
+
xtol = wfilter["parallel"].get("xtol")
|
|
89
|
+
ytol = wfilter["parallel"].get("ytol")
|
|
90
|
+
ztol = wfilter["parallel"].get("ztol")
|
|
91
|
+
itol = wfilter["parallel"].get("itol")
|
|
92
|
+
atol = wfilter["parallel"].get("atol")
|
|
93
|
+
owell.truncate_parallel_path(
|
|
94
|
+
well, xtol=xtol, ytol=ytol, ztol=ztol, itol=itol, atol=atol
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
other_dframe = owell.get_dataframe()
|
|
98
|
+
xcorc = other_dframe[well.xname].values
|
|
99
|
+
ycorc = other_dframe[well.yname].values
|
|
100
|
+
zcorc = other_dframe[well.zname].values
|
|
101
|
+
|
|
102
|
+
if xcorc.size < 2:
|
|
103
|
+
continue
|
|
104
|
+
|
|
105
|
+
otherline = sg.LineString(np.stack([xcorc, ycorc, zcorc], axis=1))
|
|
106
|
+
|
|
107
|
+
if not thisline1.crosses(otherline):
|
|
108
|
+
nox[well.name].append(other.name)
|
|
109
|
+
continue
|
|
110
|
+
|
|
111
|
+
ixx = thisline1.intersection(otherline)
|
|
112
|
+
|
|
113
|
+
if ixx.is_empty:
|
|
114
|
+
nox[well.name].append(other.name)
|
|
115
|
+
continue
|
|
116
|
+
|
|
117
|
+
# need this trick to get mdepth
|
|
118
|
+
other2 = sg.LineString(np.stack([xcorc, ycorc], axis=1))
|
|
119
|
+
ixx2 = thisline2.intersection(other2)
|
|
120
|
+
|
|
121
|
+
logger.debug("==> Intersects with %s", other.name)
|
|
122
|
+
|
|
123
|
+
if isinstance(ixx, sg.Point):
|
|
124
|
+
xcor, ycor, zcor = ixx.coords[0]
|
|
125
|
+
_x, _y, mcor = ixx2.coords[0]
|
|
126
|
+
xpoints.append([well.name, mcor, other.name, xcor, ycor, zcor])
|
|
127
|
+
|
|
128
|
+
elif isinstance(ixx, sg.MultiPoint):
|
|
129
|
+
pxx2 = list(ixx2)
|
|
130
|
+
for ino, pxx in enumerate(list(ixx)):
|
|
131
|
+
xcor, ycor, zcor = pxx.coords[0]
|
|
132
|
+
_x, _y, mcor = pxx2[ino].coords[0]
|
|
133
|
+
xpoints.append([well.name, mcor, other.name, xcor, ycor, zcor])
|
|
134
|
+
|
|
135
|
+
elif isinstance(ixx, sg.GeometryCollection):
|
|
136
|
+
gxx2 = list(ixx2)
|
|
137
|
+
for ino, gxx in enumerate(list(ixx)):
|
|
138
|
+
if isinstance(gxx, sg.Point):
|
|
139
|
+
xcor, ycor, zcor = gxx.coords[0]
|
|
140
|
+
_x, _y, mcor = gxx2[ino].coords[0]
|
|
141
|
+
xpoints.append([well.name, mcor, other.name, xcor, ycor, zcor])
|
|
142
|
+
|
|
143
|
+
dfr = pd.DataFrame(
|
|
144
|
+
xpoints,
|
|
145
|
+
columns=[
|
|
146
|
+
"WELL",
|
|
147
|
+
"MDEPTH",
|
|
148
|
+
"CWELL",
|
|
149
|
+
self._wells[0].xname,
|
|
150
|
+
self._wells[0].yname,
|
|
151
|
+
self._wells[0].zname,
|
|
152
|
+
],
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
progress.finished()
|
|
156
|
+
|
|
157
|
+
logger.debug("All intersections found!")
|
|
158
|
+
return dfr
|