well-log-toolkit 0.1.143__tar.gz → 0.1.144__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.143 → well_log_toolkit-0.1.144}/PKG-INFO +69 -1
- {well_log_toolkit-0.1.143 → well_log_toolkit-0.1.144}/README.md +68 -0
- {well_log_toolkit-0.1.143 → well_log_toolkit-0.1.144}/pyproject.toml +1 -1
- {well_log_toolkit-0.1.143 → well_log_toolkit-0.1.144}/well_log_toolkit/property.py +52 -16
- {well_log_toolkit-0.1.143 → well_log_toolkit-0.1.144}/well_log_toolkit.egg-info/PKG-INFO +69 -1
- {well_log_toolkit-0.1.143 → well_log_toolkit-0.1.144}/setup.cfg +0 -0
- {well_log_toolkit-0.1.143 → well_log_toolkit-0.1.144}/well_log_toolkit/__init__.py +0 -0
- {well_log_toolkit-0.1.143 → well_log_toolkit-0.1.144}/well_log_toolkit/exceptions.py +0 -0
- {well_log_toolkit-0.1.143 → well_log_toolkit-0.1.144}/well_log_toolkit/las_file.py +0 -0
- {well_log_toolkit-0.1.143 → well_log_toolkit-0.1.144}/well_log_toolkit/manager.py +0 -0
- {well_log_toolkit-0.1.143 → well_log_toolkit-0.1.144}/well_log_toolkit/operations.py +0 -0
- {well_log_toolkit-0.1.143 → well_log_toolkit-0.1.144}/well_log_toolkit/regression.py +0 -0
- {well_log_toolkit-0.1.143 → well_log_toolkit-0.1.144}/well_log_toolkit/statistics.py +0 -0
- {well_log_toolkit-0.1.143 → well_log_toolkit-0.1.144}/well_log_toolkit/utils.py +0 -0
- {well_log_toolkit-0.1.143 → well_log_toolkit-0.1.144}/well_log_toolkit/visualization.py +0 -0
- {well_log_toolkit-0.1.143 → well_log_toolkit-0.1.144}/well_log_toolkit/well.py +0 -0
- {well_log_toolkit-0.1.143 → well_log_toolkit-0.1.144}/well_log_toolkit.egg-info/SOURCES.txt +0 -0
- {well_log_toolkit-0.1.143 → well_log_toolkit-0.1.144}/well_log_toolkit.egg-info/dependency_links.txt +0 -0
- {well_log_toolkit-0.1.143 → well_log_toolkit-0.1.144}/well_log_toolkit.egg-info/requires.txt +0 -0
- {well_log_toolkit-0.1.143 → well_log_toolkit-0.1.144}/well_log_toolkit.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: well-log-toolkit
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.144
|
|
4
4
|
Summary: Fast LAS file processing with lazy loading and filtering for well log analysis
|
|
5
5
|
Author-email: Kristian dF Kollsgård <kkollsg@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -288,6 +288,74 @@ stats = well.PHIE.filter('Zone').filter('Facies').sums_avg()
|
|
|
288
288
|
- `samples` - Number of valid measurements
|
|
289
289
|
- `range`, `depth_range` - Min/max values and depths
|
|
290
290
|
|
|
291
|
+
### Custom Interval Filtering
|
|
292
|
+
|
|
293
|
+
Define custom depth intervals without needing a discrete property in the well:
|
|
294
|
+
|
|
295
|
+
```python
|
|
296
|
+
# Define intervals with name, top, and base
|
|
297
|
+
intervals = [
|
|
298
|
+
{"name": "Zone_A", "top": 2500, "base": 2650},
|
|
299
|
+
{"name": "Zone_B", "top": 2650, "base": 2800}
|
|
300
|
+
]
|
|
301
|
+
|
|
302
|
+
# Use with sums_avg or discrete_summary
|
|
303
|
+
stats = well.PHIE.filter_intervals(intervals).sums_avg()
|
|
304
|
+
# → {'Zone_A': {'mean': 0.18, ...}, 'Zone_B': {'mean': 0.21, ...}}
|
|
305
|
+
|
|
306
|
+
facies_stats = well.Facies.filter_intervals(intervals).discrete_summary()
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
**Overlapping intervals** are supported - each interval is calculated independently:
|
|
310
|
+
|
|
311
|
+
```python
|
|
312
|
+
# These intervals overlap at 2600-2700m
|
|
313
|
+
intervals = [
|
|
314
|
+
{"name": "Full_Reservoir", "top": 2500, "base": 2800},
|
|
315
|
+
{"name": "Upper_Section", "top": 2500, "base": 2700}
|
|
316
|
+
]
|
|
317
|
+
# Depths 2500-2700 are counted in BOTH zones
|
|
318
|
+
stats = well.PHIE.filter_intervals(intervals).sums_avg()
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
**Save intervals for reuse:**
|
|
322
|
+
|
|
323
|
+
```python
|
|
324
|
+
# Save intervals to the well
|
|
325
|
+
well.PHIE.filter_intervals(intervals, save="Reservoir_Zones")
|
|
326
|
+
|
|
327
|
+
# Use saved intervals by name
|
|
328
|
+
stats = well.PHIE.filter_intervals("Reservoir_Zones").sums_avg()
|
|
329
|
+
|
|
330
|
+
# List saved intervals
|
|
331
|
+
print(well.saved_intervals) # ['Reservoir_Zones']
|
|
332
|
+
|
|
333
|
+
# Retrieve intervals
|
|
334
|
+
intervals = well.get_intervals("Reservoir_Zones")
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
**Save different intervals for multiple wells:**
|
|
338
|
+
|
|
339
|
+
```python
|
|
340
|
+
# Define well-specific intervals
|
|
341
|
+
manager.well_A.PHIE.filter_intervals({
|
|
342
|
+
"Well_A": [{"name": "Zone_A", "top": 2500, "base": 2700}],
|
|
343
|
+
"Well_B": [{"name": "Zone_A", "top": 2600, "base": 2800}]
|
|
344
|
+
}, save="My_Zones")
|
|
345
|
+
|
|
346
|
+
# Both wells now have "My_Zones" saved with their respective intervals
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
**Chain with other filters:**
|
|
350
|
+
|
|
351
|
+
```python
|
|
352
|
+
# Combine custom intervals with property filters
|
|
353
|
+
stats = well.PHIE.filter_intervals(intervals).filter("NetFlag").sums_avg()
|
|
354
|
+
# → {'Zone_A': {'Net': {...}, 'NonNet': {...}}, 'Zone_B': {...}}
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
> **💡 Key Difference:** Unlike `.filter('Well_Tops')` where each depth belongs to exactly one zone, `filter_intervals()` allows overlapping intervals where the same depths can contribute to multiple zones.
|
|
358
|
+
|
|
291
359
|
### Property Operations
|
|
292
360
|
|
|
293
361
|
Create computed properties using natural mathematical syntax:
|
|
@@ -250,6 +250,74 @@ stats = well.PHIE.filter('Zone').filter('Facies').sums_avg()
|
|
|
250
250
|
- `samples` - Number of valid measurements
|
|
251
251
|
- `range`, `depth_range` - Min/max values and depths
|
|
252
252
|
|
|
253
|
+
### Custom Interval Filtering
|
|
254
|
+
|
|
255
|
+
Define custom depth intervals without needing a discrete property in the well:
|
|
256
|
+
|
|
257
|
+
```python
|
|
258
|
+
# Define intervals with name, top, and base
|
|
259
|
+
intervals = [
|
|
260
|
+
{"name": "Zone_A", "top": 2500, "base": 2650},
|
|
261
|
+
{"name": "Zone_B", "top": 2650, "base": 2800}
|
|
262
|
+
]
|
|
263
|
+
|
|
264
|
+
# Use with sums_avg or discrete_summary
|
|
265
|
+
stats = well.PHIE.filter_intervals(intervals).sums_avg()
|
|
266
|
+
# → {'Zone_A': {'mean': 0.18, ...}, 'Zone_B': {'mean': 0.21, ...}}
|
|
267
|
+
|
|
268
|
+
facies_stats = well.Facies.filter_intervals(intervals).discrete_summary()
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
**Overlapping intervals** are supported - each interval is calculated independently:
|
|
272
|
+
|
|
273
|
+
```python
|
|
274
|
+
# These intervals overlap at 2600-2700m
|
|
275
|
+
intervals = [
|
|
276
|
+
{"name": "Full_Reservoir", "top": 2500, "base": 2800},
|
|
277
|
+
{"name": "Upper_Section", "top": 2500, "base": 2700}
|
|
278
|
+
]
|
|
279
|
+
# Depths 2500-2700 are counted in BOTH zones
|
|
280
|
+
stats = well.PHIE.filter_intervals(intervals).sums_avg()
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
**Save intervals for reuse:**
|
|
284
|
+
|
|
285
|
+
```python
|
|
286
|
+
# Save intervals to the well
|
|
287
|
+
well.PHIE.filter_intervals(intervals, save="Reservoir_Zones")
|
|
288
|
+
|
|
289
|
+
# Use saved intervals by name
|
|
290
|
+
stats = well.PHIE.filter_intervals("Reservoir_Zones").sums_avg()
|
|
291
|
+
|
|
292
|
+
# List saved intervals
|
|
293
|
+
print(well.saved_intervals) # ['Reservoir_Zones']
|
|
294
|
+
|
|
295
|
+
# Retrieve intervals
|
|
296
|
+
intervals = well.get_intervals("Reservoir_Zones")
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
**Save different intervals for multiple wells:**
|
|
300
|
+
|
|
301
|
+
```python
|
|
302
|
+
# Define well-specific intervals
|
|
303
|
+
manager.well_A.PHIE.filter_intervals({
|
|
304
|
+
"Well_A": [{"name": "Zone_A", "top": 2500, "base": 2700}],
|
|
305
|
+
"Well_B": [{"name": "Zone_A", "top": 2600, "base": 2800}]
|
|
306
|
+
}, save="My_Zones")
|
|
307
|
+
|
|
308
|
+
# Both wells now have "My_Zones" saved with their respective intervals
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
**Chain with other filters:**
|
|
312
|
+
|
|
313
|
+
```python
|
|
314
|
+
# Combine custom intervals with property filters
|
|
315
|
+
stats = well.PHIE.filter_intervals(intervals).filter("NetFlag").sums_avg()
|
|
316
|
+
# → {'Zone_A': {'Net': {...}, 'NonNet': {...}}, 'Zone_B': {...}}
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
> **💡 Key Difference:** Unlike `.filter('Well_Tops')` where each depth belongs to exactly one zone, `filter_intervals()` allows overlapping intervals where the same depths can contribute to multiple zones.
|
|
320
|
+
|
|
253
321
|
### Property Operations
|
|
254
322
|
|
|
255
323
|
Create computed properties using natural mathematical syntax:
|
|
@@ -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.144"
|
|
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"
|
|
@@ -1872,7 +1872,8 @@ class Property(PropertyOperationsMixin):
|
|
|
1872
1872
|
Compute discrete statistics for each custom interval independently.
|
|
1873
1873
|
|
|
1874
1874
|
This allows overlapping intervals where the same depths can
|
|
1875
|
-
contribute to multiple zones.
|
|
1875
|
+
contribute to multiple zones. Zone-level metadata (depth_range, thickness)
|
|
1876
|
+
is shown at the interval level, and fractions are relative to zone thickness.
|
|
1876
1877
|
"""
|
|
1877
1878
|
result = {}
|
|
1878
1879
|
|
|
@@ -1884,22 +1885,49 @@ class Property(PropertyOperationsMixin):
|
|
|
1884
1885
|
# Create mask for this interval (top <= depth < base)
|
|
1885
1886
|
interval_mask = (self.depth >= top) & (self.depth < base)
|
|
1886
1887
|
|
|
1888
|
+
# Calculate zone thickness for fraction calculation
|
|
1889
|
+
full_intervals = compute_intervals(self.depth)
|
|
1890
|
+
valid_mask = ~np.isnan(self.values) & interval_mask
|
|
1891
|
+
zone_thickness = float(np.sum(full_intervals[valid_mask]))
|
|
1892
|
+
|
|
1893
|
+
# Get actual depth range within the interval (where we have data)
|
|
1894
|
+
if np.any(valid_mask):
|
|
1895
|
+
zone_depths = self.depth[valid_mask]
|
|
1896
|
+
zone_depth_range = {
|
|
1897
|
+
'min': round(float(np.min(zone_depths)), precision),
|
|
1898
|
+
'max': round(float(np.max(zone_depths)), precision)
|
|
1899
|
+
}
|
|
1900
|
+
else:
|
|
1901
|
+
zone_depth_range = {'min': top, 'max': base}
|
|
1902
|
+
|
|
1903
|
+
# Build interval result with zone-level metadata
|
|
1904
|
+
interval_result = {
|
|
1905
|
+
'depth_range': zone_depth_range,
|
|
1906
|
+
'thickness': round(zone_thickness, precision)
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1887
1909
|
# If there are secondary properties, group within this interval
|
|
1888
1910
|
if self.secondary_properties:
|
|
1889
|
-
|
|
1911
|
+
facies_stats = self._recursive_discrete_group(
|
|
1890
1912
|
0,
|
|
1891
1913
|
interval_mask,
|
|
1892
|
-
gross_thickness=
|
|
1893
|
-
precision=precision
|
|
1914
|
+
gross_thickness=zone_thickness, # Use zone thickness for fractions
|
|
1915
|
+
precision=precision,
|
|
1916
|
+
include_depth_range=False # Don't include depth_range per facies
|
|
1894
1917
|
)
|
|
1895
1918
|
else:
|
|
1896
1919
|
# No secondary properties, compute stats directly for interval
|
|
1897
|
-
|
|
1920
|
+
facies_stats = self._compute_discrete_stats(
|
|
1898
1921
|
interval_mask,
|
|
1899
|
-
gross_thickness=
|
|
1900
|
-
precision=precision
|
|
1922
|
+
gross_thickness=zone_thickness, # Use zone thickness for fractions
|
|
1923
|
+
precision=precision,
|
|
1924
|
+
include_depth_range=False # Don't include depth_range per facies
|
|
1901
1925
|
)
|
|
1902
1926
|
|
|
1927
|
+
# Nest facies stats under 'facies' key for cleaner structure
|
|
1928
|
+
interval_result['facies'] = facies_stats
|
|
1929
|
+
result[interval_name] = interval_result
|
|
1930
|
+
|
|
1903
1931
|
return result
|
|
1904
1932
|
|
|
1905
1933
|
def _recursive_discrete_group(
|
|
@@ -1907,7 +1935,8 @@ class Property(PropertyOperationsMixin):
|
|
|
1907
1935
|
filter_idx: int,
|
|
1908
1936
|
mask: np.ndarray,
|
|
1909
1937
|
gross_thickness: float,
|
|
1910
|
-
precision: int = 6
|
|
1938
|
+
precision: int = 6,
|
|
1939
|
+
include_depth_range: bool = True
|
|
1911
1940
|
) -> dict:
|
|
1912
1941
|
"""
|
|
1913
1942
|
Recursively group discrete statistics by secondary properties.
|
|
@@ -1922,6 +1951,8 @@ class Property(PropertyOperationsMixin):
|
|
|
1922
1951
|
Total gross thickness for fraction calculation
|
|
1923
1952
|
precision : int, default 6
|
|
1924
1953
|
Number of decimal places for rounding
|
|
1954
|
+
include_depth_range : bool, default True
|
|
1955
|
+
Whether to include depth_range in per-facies stats
|
|
1925
1956
|
|
|
1926
1957
|
Returns
|
|
1927
1958
|
-------
|
|
@@ -1930,7 +1961,7 @@ class Property(PropertyOperationsMixin):
|
|
|
1930
1961
|
"""
|
|
1931
1962
|
if filter_idx >= len(self.secondary_properties):
|
|
1932
1963
|
# Base case: compute discrete statistics for this group
|
|
1933
|
-
return self._compute_discrete_stats(mask, gross_thickness, precision)
|
|
1964
|
+
return self._compute_discrete_stats(mask, gross_thickness, precision, include_depth_range)
|
|
1934
1965
|
|
|
1935
1966
|
# Get unique values for current filter
|
|
1936
1967
|
current_filter = self.secondary_properties[filter_idx]
|
|
@@ -1940,7 +1971,7 @@ class Property(PropertyOperationsMixin):
|
|
|
1940
1971
|
|
|
1941
1972
|
if len(unique_vals) == 0:
|
|
1942
1973
|
# No valid values, return stats for current mask
|
|
1943
|
-
return self._compute_discrete_stats(mask, gross_thickness, precision)
|
|
1974
|
+
return self._compute_discrete_stats(mask, gross_thickness, precision, include_depth_range)
|
|
1944
1975
|
|
|
1945
1976
|
# Group by each unique value
|
|
1946
1977
|
depth_array = self.depth
|
|
@@ -1976,7 +2007,7 @@ class Property(PropertyOperationsMixin):
|
|
|
1976
2007
|
key = f"{current_filter.name}_{val:.2f}"
|
|
1977
2008
|
|
|
1978
2009
|
result[key] = self._recursive_discrete_group(
|
|
1979
|
-
filter_idx + 1, sub_mask, group_thickness, precision
|
|
2010
|
+
filter_idx + 1, sub_mask, group_thickness, precision, include_depth_range
|
|
1980
2011
|
)
|
|
1981
2012
|
|
|
1982
2013
|
return result
|
|
@@ -1985,7 +2016,8 @@ class Property(PropertyOperationsMixin):
|
|
|
1985
2016
|
self,
|
|
1986
2017
|
mask: np.ndarray,
|
|
1987
2018
|
gross_thickness: float,
|
|
1988
|
-
precision: int = 6
|
|
2019
|
+
precision: int = 6,
|
|
2020
|
+
include_depth_range: bool = True
|
|
1989
2021
|
) -> dict:
|
|
1990
2022
|
"""
|
|
1991
2023
|
Compute categorical statistics for discrete property values.
|
|
@@ -1998,12 +2030,15 @@ class Property(PropertyOperationsMixin):
|
|
|
1998
2030
|
Total gross thickness for fraction calculation
|
|
1999
2031
|
precision : int, default 6
|
|
2000
2032
|
Number of decimal places for rounding
|
|
2033
|
+
include_depth_range : bool, default True
|
|
2034
|
+
Whether to include depth_range in per-facies stats.
|
|
2035
|
+
Set to False when using filter_intervals (depth_range shown at zone level).
|
|
2001
2036
|
|
|
2002
2037
|
Returns
|
|
2003
2038
|
-------
|
|
2004
2039
|
dict
|
|
2005
2040
|
Dictionary with stats for each discrete value:
|
|
2006
|
-
{value_label: {code, count, thickness, fraction, depth_range}}
|
|
2041
|
+
{value_label: {code, count, thickness, fraction, [depth_range]}}
|
|
2007
2042
|
"""
|
|
2008
2043
|
values_array = self.values
|
|
2009
2044
|
depth_array = self.depth
|
|
@@ -2049,12 +2084,13 @@ class Property(PropertyOperationsMixin):
|
|
|
2049
2084
|
'code': int_val,
|
|
2050
2085
|
'count': count,
|
|
2051
2086
|
'thickness': round(thickness, precision),
|
|
2052
|
-
'fraction': round(fraction, precision)
|
|
2053
|
-
|
|
2087
|
+
'fraction': round(fraction, precision)
|
|
2088
|
+
}
|
|
2089
|
+
if include_depth_range:
|
|
2090
|
+
stats['depth_range'] = {
|
|
2054
2091
|
'min': round(float(np.min(val_depths)), precision),
|
|
2055
2092
|
'max': round(float(np.max(val_depths)), precision)
|
|
2056
2093
|
}
|
|
2057
|
-
}
|
|
2058
2094
|
if label is not None:
|
|
2059
2095
|
stats['label'] = label
|
|
2060
2096
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: well-log-toolkit
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.144
|
|
4
4
|
Summary: Fast LAS file processing with lazy loading and filtering for well log analysis
|
|
5
5
|
Author-email: Kristian dF Kollsgård <kkollsg@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -288,6 +288,74 @@ stats = well.PHIE.filter('Zone').filter('Facies').sums_avg()
|
|
|
288
288
|
- `samples` - Number of valid measurements
|
|
289
289
|
- `range`, `depth_range` - Min/max values and depths
|
|
290
290
|
|
|
291
|
+
### Custom Interval Filtering
|
|
292
|
+
|
|
293
|
+
Define custom depth intervals without needing a discrete property in the well:
|
|
294
|
+
|
|
295
|
+
```python
|
|
296
|
+
# Define intervals with name, top, and base
|
|
297
|
+
intervals = [
|
|
298
|
+
{"name": "Zone_A", "top": 2500, "base": 2650},
|
|
299
|
+
{"name": "Zone_B", "top": 2650, "base": 2800}
|
|
300
|
+
]
|
|
301
|
+
|
|
302
|
+
# Use with sums_avg or discrete_summary
|
|
303
|
+
stats = well.PHIE.filter_intervals(intervals).sums_avg()
|
|
304
|
+
# → {'Zone_A': {'mean': 0.18, ...}, 'Zone_B': {'mean': 0.21, ...}}
|
|
305
|
+
|
|
306
|
+
facies_stats = well.Facies.filter_intervals(intervals).discrete_summary()
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
**Overlapping intervals** are supported - each interval is calculated independently:
|
|
310
|
+
|
|
311
|
+
```python
|
|
312
|
+
# These intervals overlap at 2600-2700m
|
|
313
|
+
intervals = [
|
|
314
|
+
{"name": "Full_Reservoir", "top": 2500, "base": 2800},
|
|
315
|
+
{"name": "Upper_Section", "top": 2500, "base": 2700}
|
|
316
|
+
]
|
|
317
|
+
# Depths 2500-2700 are counted in BOTH zones
|
|
318
|
+
stats = well.PHIE.filter_intervals(intervals).sums_avg()
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
**Save intervals for reuse:**
|
|
322
|
+
|
|
323
|
+
```python
|
|
324
|
+
# Save intervals to the well
|
|
325
|
+
well.PHIE.filter_intervals(intervals, save="Reservoir_Zones")
|
|
326
|
+
|
|
327
|
+
# Use saved intervals by name
|
|
328
|
+
stats = well.PHIE.filter_intervals("Reservoir_Zones").sums_avg()
|
|
329
|
+
|
|
330
|
+
# List saved intervals
|
|
331
|
+
print(well.saved_intervals) # ['Reservoir_Zones']
|
|
332
|
+
|
|
333
|
+
# Retrieve intervals
|
|
334
|
+
intervals = well.get_intervals("Reservoir_Zones")
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
**Save different intervals for multiple wells:**
|
|
338
|
+
|
|
339
|
+
```python
|
|
340
|
+
# Define well-specific intervals
|
|
341
|
+
manager.well_A.PHIE.filter_intervals({
|
|
342
|
+
"Well_A": [{"name": "Zone_A", "top": 2500, "base": 2700}],
|
|
343
|
+
"Well_B": [{"name": "Zone_A", "top": 2600, "base": 2800}]
|
|
344
|
+
}, save="My_Zones")
|
|
345
|
+
|
|
346
|
+
# Both wells now have "My_Zones" saved with their respective intervals
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
**Chain with other filters:**
|
|
350
|
+
|
|
351
|
+
```python
|
|
352
|
+
# Combine custom intervals with property filters
|
|
353
|
+
stats = well.PHIE.filter_intervals(intervals).filter("NetFlag").sums_avg()
|
|
354
|
+
# → {'Zone_A': {'Net': {...}, 'NonNet': {...}}, 'Zone_B': {...}}
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
> **💡 Key Difference:** Unlike `.filter('Well_Tops')` where each depth belongs to exactly one zone, `filter_intervals()` allows overlapping intervals where the same depths can contribute to multiple zones.
|
|
358
|
+
|
|
291
359
|
### Property Operations
|
|
292
360
|
|
|
293
361
|
Create computed properties using natural mathematical syntax:
|
|
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.143 → well_log_toolkit-0.1.144}/well_log_toolkit.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{well_log_toolkit-0.1.143 → well_log_toolkit-0.1.144}/well_log_toolkit.egg-info/requires.txt
RENAMED
|
File without changes
|
{well_log_toolkit-0.1.143 → well_log_toolkit-0.1.144}/well_log_toolkit.egg-info/top_level.txt
RENAMED
|
File without changes
|