well-log-toolkit 0.1.142__tar.gz → 0.1.143__tar.gz
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.
- {well_log_toolkit-0.1.142 → well_log_toolkit-0.1.143}/PKG-INFO +1 -1
- {well_log_toolkit-0.1.142 → well_log_toolkit-0.1.143}/pyproject.toml +1 -1
- {well_log_toolkit-0.1.142 → well_log_toolkit-0.1.143}/well_log_toolkit/property.py +316 -0
- {well_log_toolkit-0.1.142 → well_log_toolkit-0.1.143}/well_log_toolkit/well.py +54 -1
- {well_log_toolkit-0.1.142 → well_log_toolkit-0.1.143}/well_log_toolkit.egg-info/PKG-INFO +1 -1
- {well_log_toolkit-0.1.142 → well_log_toolkit-0.1.143}/README.md +0 -0
- {well_log_toolkit-0.1.142 → well_log_toolkit-0.1.143}/setup.cfg +0 -0
- {well_log_toolkit-0.1.142 → well_log_toolkit-0.1.143}/well_log_toolkit/__init__.py +0 -0
- {well_log_toolkit-0.1.142 → well_log_toolkit-0.1.143}/well_log_toolkit/exceptions.py +0 -0
- {well_log_toolkit-0.1.142 → well_log_toolkit-0.1.143}/well_log_toolkit/las_file.py +0 -0
- {well_log_toolkit-0.1.142 → well_log_toolkit-0.1.143}/well_log_toolkit/manager.py +0 -0
- {well_log_toolkit-0.1.142 → well_log_toolkit-0.1.143}/well_log_toolkit/operations.py +0 -0
- {well_log_toolkit-0.1.142 → well_log_toolkit-0.1.143}/well_log_toolkit/regression.py +0 -0
- {well_log_toolkit-0.1.142 → well_log_toolkit-0.1.143}/well_log_toolkit/statistics.py +0 -0
- {well_log_toolkit-0.1.142 → well_log_toolkit-0.1.143}/well_log_toolkit/utils.py +0 -0
- {well_log_toolkit-0.1.142 → well_log_toolkit-0.1.143}/well_log_toolkit/visualization.py +0 -0
- {well_log_toolkit-0.1.142 → well_log_toolkit-0.1.143}/well_log_toolkit.egg-info/SOURCES.txt +0 -0
- {well_log_toolkit-0.1.142 → well_log_toolkit-0.1.143}/well_log_toolkit.egg-info/dependency_links.txt +0 -0
- {well_log_toolkit-0.1.142 → well_log_toolkit-0.1.143}/well_log_toolkit.egg-info/requires.txt +0 -0
- {well_log_toolkit-0.1.142 → well_log_toolkit-0.1.143}/well_log_toolkit.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "well-log-toolkit"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.143"
|
|
8
8
|
description = "Fast LAS file processing with lazy loading and filtering for well log analysis"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -1167,6 +1167,220 @@ class Property(PropertyOperationsMixin):
|
|
|
1167
1167
|
|
|
1168
1168
|
return new_prop
|
|
1169
1169
|
|
|
1170
|
+
def filter_intervals(
|
|
1171
|
+
self,
|
|
1172
|
+
intervals: Union[list[dict], dict[str, list[dict]], str],
|
|
1173
|
+
name: str = "Custom_Intervals",
|
|
1174
|
+
insert_boundaries: Optional[bool] = None,
|
|
1175
|
+
save: Optional[str] = None
|
|
1176
|
+
) -> 'Property':
|
|
1177
|
+
"""
|
|
1178
|
+
Filter by custom depth intervals defined as top/base pairs.
|
|
1179
|
+
|
|
1180
|
+
Each interval is processed independently, allowing overlapping intervals
|
|
1181
|
+
where the same depths can be counted in multiple zones.
|
|
1182
|
+
|
|
1183
|
+
Parameters
|
|
1184
|
+
----------
|
|
1185
|
+
intervals : list[dict] | dict[str, list[dict]] | str
|
|
1186
|
+
Interval definitions. Can be:
|
|
1187
|
+
- list[dict]: Direct list of intervals for the current well
|
|
1188
|
+
- dict[str, list[dict]]: Well-specific intervals keyed by well name.
|
|
1189
|
+
Current well must be included or raises error.
|
|
1190
|
+
- str: Name of a previously saved filter to use
|
|
1191
|
+
name : str, default "Custom_Intervals"
|
|
1192
|
+
Name for the filter property (used in output labels)
|
|
1193
|
+
insert_boundaries : bool, optional
|
|
1194
|
+
If True, insert synthetic samples at interval boundaries.
|
|
1195
|
+
Default is True for continuous properties, False for sampled properties.
|
|
1196
|
+
save : str, optional
|
|
1197
|
+
If provided, save the intervals to the well(s) under this name.
|
|
1198
|
+
Overwrites any existing filter with the same name.
|
|
1199
|
+
|
|
1200
|
+
Returns
|
|
1201
|
+
-------
|
|
1202
|
+
Property
|
|
1203
|
+
New property instance with custom intervals as filter dimension
|
|
1204
|
+
|
|
1205
|
+
Examples
|
|
1206
|
+
--------
|
|
1207
|
+
>>> # Filter by custom zones
|
|
1208
|
+
>>> intervals = [
|
|
1209
|
+
... {"name": "Zone_A", "top": 2500, "base": 2600},
|
|
1210
|
+
... {"name": "Zone_B", "top": 2600, "base": 2750},
|
|
1211
|
+
... ]
|
|
1212
|
+
>>> filtered = well.PHIE.filter_intervals(intervals)
|
|
1213
|
+
>>> filtered.sums_avg()
|
|
1214
|
+
|
|
1215
|
+
>>> # Save intervals for reuse
|
|
1216
|
+
>>> well.PHIE.filter_intervals(intervals, save="Reservoir_Zones")
|
|
1217
|
+
>>> # Later, use saved filter by name
|
|
1218
|
+
>>> well.PHIE.filter_intervals("Reservoir_Zones").sums_avg()
|
|
1219
|
+
|
|
1220
|
+
>>> # Save different intervals for multiple wells
|
|
1221
|
+
>>> manager.well_A.PHIE.filter_intervals({
|
|
1222
|
+
... "well_A": intervals_a,
|
|
1223
|
+
... "well_B": intervals_b
|
|
1224
|
+
... }, save="My_Zones")
|
|
1225
|
+
>>> # Now both wells have "My_Zones" saved
|
|
1226
|
+
|
|
1227
|
+
>>> # Overlapping intervals - each calculated independently
|
|
1228
|
+
>>> intervals = [
|
|
1229
|
+
... {"name": "Full_Reservoir", "top": 2500, "base": 2800},
|
|
1230
|
+
... {"name": "Upper_Only", "top": 2500, "base": 2650}
|
|
1231
|
+
... ]
|
|
1232
|
+
>>> # Depths 2500-2650 will be counted in BOTH zones
|
|
1233
|
+
|
|
1234
|
+
Notes
|
|
1235
|
+
-----
|
|
1236
|
+
Intervals can overlap or have gaps. Depths outside all intervals
|
|
1237
|
+
are excluded from statistics. Overlapping intervals are calculated
|
|
1238
|
+
independently - the same depths can contribute to multiple zones.
|
|
1239
|
+
"""
|
|
1240
|
+
# Handle string input (saved filter name)
|
|
1241
|
+
if isinstance(intervals, str):
|
|
1242
|
+
filter_name = intervals
|
|
1243
|
+
if self.parent_well is None:
|
|
1244
|
+
raise PropertyNotFoundError(
|
|
1245
|
+
f"Cannot use saved filter '{filter_name}': no parent well reference."
|
|
1246
|
+
)
|
|
1247
|
+
if filter_name not in self.parent_well._saved_filter_intervals:
|
|
1248
|
+
available = list(self.parent_well._saved_filter_intervals.keys())
|
|
1249
|
+
raise PropertyNotFoundError(
|
|
1250
|
+
f"Saved filter '{filter_name}' not found in well '{self.parent_well.name}'. "
|
|
1251
|
+
f"Available filters: {available if available else 'none'}"
|
|
1252
|
+
)
|
|
1253
|
+
intervals = self.parent_well._saved_filter_intervals[filter_name]
|
|
1254
|
+
# Use filter name as the output name if not overridden
|
|
1255
|
+
if name == "Custom_Intervals":
|
|
1256
|
+
name = filter_name
|
|
1257
|
+
|
|
1258
|
+
# Handle dict input (well-specific intervals)
|
|
1259
|
+
elif isinstance(intervals, dict):
|
|
1260
|
+
if self.parent_well is None:
|
|
1261
|
+
raise PropertyNotFoundError(
|
|
1262
|
+
"Cannot use well-specific intervals: no parent well reference."
|
|
1263
|
+
)
|
|
1264
|
+
|
|
1265
|
+
well_name = self.parent_well.name
|
|
1266
|
+
sanitized_name = self.parent_well.sanitized_name
|
|
1267
|
+
|
|
1268
|
+
# Check if current well is in the dict (by name or sanitized name)
|
|
1269
|
+
current_well_intervals = [] # Default to empty if not found
|
|
1270
|
+
if well_name in intervals:
|
|
1271
|
+
current_well_intervals = intervals[well_name]
|
|
1272
|
+
elif sanitized_name in intervals:
|
|
1273
|
+
current_well_intervals = intervals[sanitized_name]
|
|
1274
|
+
|
|
1275
|
+
# Save to all specified wells if save parameter provided
|
|
1276
|
+
if save and self.parent_well.parent_manager:
|
|
1277
|
+
manager = self.parent_well.parent_manager
|
|
1278
|
+
for key, well_intervals in intervals.items():
|
|
1279
|
+
# Find well by name or sanitized name
|
|
1280
|
+
target_well = None
|
|
1281
|
+
for w in manager._wells.values():
|
|
1282
|
+
if w.name == key or w.sanitized_name == key:
|
|
1283
|
+
target_well = w
|
|
1284
|
+
break
|
|
1285
|
+
if target_well:
|
|
1286
|
+
self._validate_intervals(well_intervals)
|
|
1287
|
+
target_well._saved_filter_intervals[save] = well_intervals
|
|
1288
|
+
|
|
1289
|
+
intervals = current_well_intervals
|
|
1290
|
+
|
|
1291
|
+
# Validate and save if we have intervals
|
|
1292
|
+
if intervals:
|
|
1293
|
+
# Validate interval structure
|
|
1294
|
+
self._validate_intervals(intervals)
|
|
1295
|
+
|
|
1296
|
+
# Save to current well if save parameter provided
|
|
1297
|
+
if save and self.parent_well:
|
|
1298
|
+
self.parent_well._saved_filter_intervals[save] = intervals
|
|
1299
|
+
|
|
1300
|
+
# Determine if we should insert boundaries
|
|
1301
|
+
if insert_boundaries is None:
|
|
1302
|
+
insert_boundaries = self.type != 'sampled'
|
|
1303
|
+
|
|
1304
|
+
# Collect all boundary depths from intervals for boundary insertion
|
|
1305
|
+
if insert_boundaries and intervals:
|
|
1306
|
+
boundary_depths = []
|
|
1307
|
+
for interval in intervals:
|
|
1308
|
+
boundary_depths.append(float(interval['top']))
|
|
1309
|
+
boundary_depths.append(float(interval['base']))
|
|
1310
|
+
boundary_depths = np.unique(boundary_depths)
|
|
1311
|
+
|
|
1312
|
+
# Create a temporary discrete property just for boundary insertion
|
|
1313
|
+
# Values don't matter here, only the depths
|
|
1314
|
+
temp_discrete = Property(
|
|
1315
|
+
name=name,
|
|
1316
|
+
depth=boundary_depths,
|
|
1317
|
+
values=np.arange(len(boundary_depths), dtype=float),
|
|
1318
|
+
parent_well=self.parent_well,
|
|
1319
|
+
prop_type='discrete'
|
|
1320
|
+
)
|
|
1321
|
+
new_depth, new_values, new_secondaries = self._insert_boundary_samples(temp_discrete)
|
|
1322
|
+
else:
|
|
1323
|
+
new_depth = self.depth.copy()
|
|
1324
|
+
new_values = self.values.copy()
|
|
1325
|
+
new_secondaries = [sp for sp in self.secondary_properties]
|
|
1326
|
+
|
|
1327
|
+
# Create new Property instance
|
|
1328
|
+
new_prop = Property(
|
|
1329
|
+
name=self.name,
|
|
1330
|
+
depth=new_depth,
|
|
1331
|
+
values=new_values,
|
|
1332
|
+
parent_well=self.parent_well,
|
|
1333
|
+
unit=self.unit,
|
|
1334
|
+
prop_type=self.type,
|
|
1335
|
+
description=self.description,
|
|
1336
|
+
null_value=-999.25,
|
|
1337
|
+
labels=self.labels,
|
|
1338
|
+
colors=self.colors,
|
|
1339
|
+
styles=self.styles,
|
|
1340
|
+
thicknesses=self.thicknesses,
|
|
1341
|
+
source_las=self.source_las,
|
|
1342
|
+
source_name=self.source_name,
|
|
1343
|
+
original_name=self.original_name
|
|
1344
|
+
)
|
|
1345
|
+
new_prop.secondary_properties = new_secondaries
|
|
1346
|
+
|
|
1347
|
+
# Store custom intervals for independent processing in sums_avg/discrete_summary
|
|
1348
|
+
new_prop._custom_intervals = intervals
|
|
1349
|
+
new_prop._custom_intervals_name = name
|
|
1350
|
+
|
|
1351
|
+
# Track filtering metadata
|
|
1352
|
+
new_prop._is_filtered = True
|
|
1353
|
+
new_prop._original_sample_count = len(self.depth)
|
|
1354
|
+
new_prop._boundary_samples_inserted = len(new_depth) - len(self.depth)
|
|
1355
|
+
|
|
1356
|
+
return new_prop
|
|
1357
|
+
|
|
1358
|
+
def _validate_intervals(self, intervals: list[dict]) -> None:
|
|
1359
|
+
"""
|
|
1360
|
+
Validate interval structure.
|
|
1361
|
+
|
|
1362
|
+
Parameters
|
|
1363
|
+
----------
|
|
1364
|
+
intervals : list[dict]
|
|
1365
|
+
List of interval definitions to validate
|
|
1366
|
+
|
|
1367
|
+
Raises
|
|
1368
|
+
------
|
|
1369
|
+
ValueError
|
|
1370
|
+
If any interval is invalid
|
|
1371
|
+
"""
|
|
1372
|
+
for i, interval in enumerate(intervals):
|
|
1373
|
+
if not isinstance(interval, dict):
|
|
1374
|
+
raise ValueError(f"Interval {i} must be a dict, got {type(interval)}")
|
|
1375
|
+
for key in ('name', 'top', 'base'):
|
|
1376
|
+
if key not in interval:
|
|
1377
|
+
raise ValueError(f"Interval {i} missing required key '{key}'")
|
|
1378
|
+
if interval['top'] >= interval['base']:
|
|
1379
|
+
raise ValueError(
|
|
1380
|
+
f"Interval '{interval['name']}': top ({interval['top']}) must be "
|
|
1381
|
+
f"less than base ({interval['base']})"
|
|
1382
|
+
)
|
|
1383
|
+
|
|
1170
1384
|
def _insert_boundary_samples(
|
|
1171
1385
|
self,
|
|
1172
1386
|
discrete_prop: 'Property'
|
|
@@ -1500,6 +1714,16 @@ class Property(PropertyOperationsMixin):
|
|
|
1500
1714
|
valid_mask = ~np.isnan(self.values)
|
|
1501
1715
|
gross_thickness = float(np.sum(full_intervals[valid_mask]))
|
|
1502
1716
|
|
|
1717
|
+
# Check for custom intervals (from filter_intervals)
|
|
1718
|
+
# These are processed independently, allowing overlaps
|
|
1719
|
+
if hasattr(self, '_custom_intervals') and self._custom_intervals:
|
|
1720
|
+
return self._compute_stats_by_intervals(
|
|
1721
|
+
weighted=weighted,
|
|
1722
|
+
arithmetic=arithmetic,
|
|
1723
|
+
gross_thickness=gross_thickness,
|
|
1724
|
+
precision=precision
|
|
1725
|
+
)
|
|
1726
|
+
|
|
1503
1727
|
if not self.secondary_properties:
|
|
1504
1728
|
# No filters, simple statistics
|
|
1505
1729
|
return self._compute_stats(
|
|
@@ -1520,6 +1744,51 @@ class Property(PropertyOperationsMixin):
|
|
|
1520
1744
|
precision=precision
|
|
1521
1745
|
)
|
|
1522
1746
|
|
|
1747
|
+
def _compute_stats_by_intervals(
|
|
1748
|
+
self,
|
|
1749
|
+
weighted: bool,
|
|
1750
|
+
arithmetic: bool,
|
|
1751
|
+
gross_thickness: float,
|
|
1752
|
+
precision: int
|
|
1753
|
+
) -> dict:
|
|
1754
|
+
"""
|
|
1755
|
+
Compute statistics for each custom interval independently.
|
|
1756
|
+
|
|
1757
|
+
This allows overlapping intervals where the same depths can
|
|
1758
|
+
contribute to multiple zones.
|
|
1759
|
+
"""
|
|
1760
|
+
result = {}
|
|
1761
|
+
|
|
1762
|
+
for interval in self._custom_intervals:
|
|
1763
|
+
interval_name = interval['name']
|
|
1764
|
+
top = float(interval['top'])
|
|
1765
|
+
base = float(interval['base'])
|
|
1766
|
+
|
|
1767
|
+
# Create mask for this interval (top <= depth < base)
|
|
1768
|
+
interval_mask = (self.depth >= top) & (self.depth < base)
|
|
1769
|
+
|
|
1770
|
+
# If there are secondary properties, group within this interval
|
|
1771
|
+
if self.secondary_properties:
|
|
1772
|
+
result[interval_name] = self._recursive_group(
|
|
1773
|
+
0,
|
|
1774
|
+
interval_mask,
|
|
1775
|
+
weighted=weighted,
|
|
1776
|
+
arithmetic=arithmetic,
|
|
1777
|
+
gross_thickness=gross_thickness,
|
|
1778
|
+
precision=precision
|
|
1779
|
+
)
|
|
1780
|
+
else:
|
|
1781
|
+
# No secondary properties, compute stats directly for interval
|
|
1782
|
+
result[interval_name] = self._compute_stats(
|
|
1783
|
+
interval_mask,
|
|
1784
|
+
weighted=weighted,
|
|
1785
|
+
arithmetic=arithmetic,
|
|
1786
|
+
gross_thickness=gross_thickness,
|
|
1787
|
+
precision=precision
|
|
1788
|
+
)
|
|
1789
|
+
|
|
1790
|
+
return result
|
|
1791
|
+
|
|
1523
1792
|
def discrete_summary(self, precision: int = 6) -> dict:
|
|
1524
1793
|
"""
|
|
1525
1794
|
Compute summary statistics for discrete/categorical properties.
|
|
@@ -1570,6 +1839,14 @@ class Property(PropertyOperationsMixin):
|
|
|
1570
1839
|
valid_mask = ~np.isnan(self.values)
|
|
1571
1840
|
gross_thickness = float(np.sum(full_intervals[valid_mask]))
|
|
1572
1841
|
|
|
1842
|
+
# Check for custom intervals (from filter_intervals)
|
|
1843
|
+
# These are processed independently, allowing overlaps
|
|
1844
|
+
if hasattr(self, '_custom_intervals') and self._custom_intervals:
|
|
1845
|
+
return self._compute_discrete_stats_by_intervals(
|
|
1846
|
+
gross_thickness=gross_thickness,
|
|
1847
|
+
precision=precision
|
|
1848
|
+
)
|
|
1849
|
+
|
|
1573
1850
|
if not self.secondary_properties:
|
|
1574
1851
|
# No filters, compute stats for all discrete values
|
|
1575
1852
|
return self._compute_discrete_stats(
|
|
@@ -1586,6 +1863,45 @@ class Property(PropertyOperationsMixin):
|
|
|
1586
1863
|
precision=precision
|
|
1587
1864
|
)
|
|
1588
1865
|
|
|
1866
|
+
def _compute_discrete_stats_by_intervals(
|
|
1867
|
+
self,
|
|
1868
|
+
gross_thickness: float,
|
|
1869
|
+
precision: int
|
|
1870
|
+
) -> dict:
|
|
1871
|
+
"""
|
|
1872
|
+
Compute discrete statistics for each custom interval independently.
|
|
1873
|
+
|
|
1874
|
+
This allows overlapping intervals where the same depths can
|
|
1875
|
+
contribute to multiple zones.
|
|
1876
|
+
"""
|
|
1877
|
+
result = {}
|
|
1878
|
+
|
|
1879
|
+
for interval in self._custom_intervals:
|
|
1880
|
+
interval_name = interval['name']
|
|
1881
|
+
top = float(interval['top'])
|
|
1882
|
+
base = float(interval['base'])
|
|
1883
|
+
|
|
1884
|
+
# Create mask for this interval (top <= depth < base)
|
|
1885
|
+
interval_mask = (self.depth >= top) & (self.depth < base)
|
|
1886
|
+
|
|
1887
|
+
# If there are secondary properties, group within this interval
|
|
1888
|
+
if self.secondary_properties:
|
|
1889
|
+
result[interval_name] = self._recursive_discrete_group(
|
|
1890
|
+
0,
|
|
1891
|
+
interval_mask,
|
|
1892
|
+
gross_thickness=gross_thickness,
|
|
1893
|
+
precision=precision
|
|
1894
|
+
)
|
|
1895
|
+
else:
|
|
1896
|
+
# No secondary properties, compute stats directly for interval
|
|
1897
|
+
result[interval_name] = self._compute_discrete_stats(
|
|
1898
|
+
interval_mask,
|
|
1899
|
+
gross_thickness=gross_thickness,
|
|
1900
|
+
precision=precision
|
|
1901
|
+
)
|
|
1902
|
+
|
|
1903
|
+
return result
|
|
1904
|
+
|
|
1589
1905
|
def _recursive_discrete_group(
|
|
1590
1906
|
self,
|
|
1591
1907
|
filter_idx: int,
|
|
@@ -220,6 +220,8 @@ class Well:
|
|
|
220
220
|
self._deleted_sources: list[str] = [] # List of source names to delete
|
|
221
221
|
# Track sources marked for rename (to rename files on save)
|
|
222
222
|
self._renamed_sources: dict[str, str] = {} # {old_name: new_name}
|
|
223
|
+
# Saved filter intervals for use with filter_intervals()
|
|
224
|
+
self._saved_filter_intervals: dict[str, list[dict]] = {} # {filter_name: [intervals]}
|
|
223
225
|
|
|
224
226
|
def __setattr__(self, name: str, value):
|
|
225
227
|
"""
|
|
@@ -1226,7 +1228,58 @@ class Well:
|
|
|
1226
1228
|
f"Property '{name}' not found in well '{self.name}'. "
|
|
1227
1229
|
f"Available properties: {available or 'none'}"
|
|
1228
1230
|
)
|
|
1229
|
-
|
|
1231
|
+
|
|
1232
|
+
def get_intervals(self, name: str) -> list[dict]:
|
|
1233
|
+
"""
|
|
1234
|
+
Get saved filter intervals by name.
|
|
1235
|
+
|
|
1236
|
+
Parameters
|
|
1237
|
+
----------
|
|
1238
|
+
name : str
|
|
1239
|
+
Name of the saved filter intervals
|
|
1240
|
+
|
|
1241
|
+
Returns
|
|
1242
|
+
-------
|
|
1243
|
+
list[dict]
|
|
1244
|
+
List of interval definitions, each with keys 'name', 'top', 'base'
|
|
1245
|
+
|
|
1246
|
+
Raises
|
|
1247
|
+
------
|
|
1248
|
+
KeyError
|
|
1249
|
+
If no intervals with this name exist
|
|
1250
|
+
|
|
1251
|
+
Examples
|
|
1252
|
+
--------
|
|
1253
|
+
>>> # Save intervals
|
|
1254
|
+
>>> well.PHIE.filter_intervals([
|
|
1255
|
+
... {"name": "Zone_A", "top": 2500, "base": 2650}
|
|
1256
|
+
... ], save="My_Zones")
|
|
1257
|
+
>>>
|
|
1258
|
+
>>> # Retrieve them later
|
|
1259
|
+
>>> intervals = well.get_intervals("My_Zones")
|
|
1260
|
+
>>> print(intervals)
|
|
1261
|
+
[{'name': 'Zone_A', 'top': 2500, 'base': 2650}]
|
|
1262
|
+
"""
|
|
1263
|
+
if name not in self._saved_filter_intervals:
|
|
1264
|
+
available = list(self._saved_filter_intervals.keys())
|
|
1265
|
+
raise KeyError(
|
|
1266
|
+
f"Filter intervals '{name}' not found in well '{self.name}'. "
|
|
1267
|
+
f"Available: {available if available else 'none'}"
|
|
1268
|
+
)
|
|
1269
|
+
return self._saved_filter_intervals[name]
|
|
1270
|
+
|
|
1271
|
+
@property
|
|
1272
|
+
def saved_intervals(self) -> list[str]:
|
|
1273
|
+
"""
|
|
1274
|
+
List of saved filter interval names.
|
|
1275
|
+
|
|
1276
|
+
Returns
|
|
1277
|
+
-------
|
|
1278
|
+
list[str]
|
|
1279
|
+
Names of all saved filter intervals
|
|
1280
|
+
"""
|
|
1281
|
+
return list(self._saved_filter_intervals.keys())
|
|
1282
|
+
|
|
1230
1283
|
@staticmethod
|
|
1231
1284
|
def _is_regular_grid(depth: np.ndarray, tolerance: float = 1e-6) -> tuple[bool, Optional[float]]:
|
|
1232
1285
|
"""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{well_log_toolkit-0.1.142 → well_log_toolkit-0.1.143}/well_log_toolkit.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{well_log_toolkit-0.1.142 → well_log_toolkit-0.1.143}/well_log_toolkit.egg-info/requires.txt
RENAMED
|
File without changes
|
{well_log_toolkit-0.1.142 → well_log_toolkit-0.1.143}/well_log_toolkit.egg-info/top_level.txt
RENAMED
|
File without changes
|