well-log-toolkit 0.1.148__tar.gz → 0.1.149__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.148 → well_log_toolkit-0.1.149}/PKG-INFO +1 -1
- {well_log_toolkit-0.1.148 → well_log_toolkit-0.1.149}/pyproject.toml +1 -1
- {well_log_toolkit-0.1.148 → well_log_toolkit-0.1.149}/well_log_toolkit/property.py +84 -34
- {well_log_toolkit-0.1.148 → well_log_toolkit-0.1.149}/well_log_toolkit/statistics.py +77 -0
- {well_log_toolkit-0.1.148 → well_log_toolkit-0.1.149}/well_log_toolkit.egg-info/PKG-INFO +1 -1
- {well_log_toolkit-0.1.148 → well_log_toolkit-0.1.149}/README.md +0 -0
- {well_log_toolkit-0.1.148 → well_log_toolkit-0.1.149}/setup.cfg +0 -0
- {well_log_toolkit-0.1.148 → well_log_toolkit-0.1.149}/well_log_toolkit/__init__.py +0 -0
- {well_log_toolkit-0.1.148 → well_log_toolkit-0.1.149}/well_log_toolkit/exceptions.py +0 -0
- {well_log_toolkit-0.1.148 → well_log_toolkit-0.1.149}/well_log_toolkit/las_file.py +0 -0
- {well_log_toolkit-0.1.148 → well_log_toolkit-0.1.149}/well_log_toolkit/manager.py +0 -0
- {well_log_toolkit-0.1.148 → well_log_toolkit-0.1.149}/well_log_toolkit/operations.py +0 -0
- {well_log_toolkit-0.1.148 → well_log_toolkit-0.1.149}/well_log_toolkit/regression.py +0 -0
- {well_log_toolkit-0.1.148 → well_log_toolkit-0.1.149}/well_log_toolkit/utils.py +0 -0
- {well_log_toolkit-0.1.148 → well_log_toolkit-0.1.149}/well_log_toolkit/visualization.py +0 -0
- {well_log_toolkit-0.1.148 → well_log_toolkit-0.1.149}/well_log_toolkit/well.py +0 -0
- {well_log_toolkit-0.1.148 → well_log_toolkit-0.1.149}/well_log_toolkit.egg-info/SOURCES.txt +0 -0
- {well_log_toolkit-0.1.148 → well_log_toolkit-0.1.149}/well_log_toolkit.egg-info/dependency_links.txt +0 -0
- {well_log_toolkit-0.1.148 → well_log_toolkit-0.1.149}/well_log_toolkit.egg-info/requires.txt +0 -0
- {well_log_toolkit-0.1.148 → well_log_toolkit-0.1.149}/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.149"
|
|
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"
|
|
@@ -11,6 +11,7 @@ from scipy.interpolate import interp1d
|
|
|
11
11
|
from .exceptions import PropertyError, PropertyNotFoundError, PropertyTypeError, DepthAlignmentError
|
|
12
12
|
from .statistics import (
|
|
13
13
|
compute_intervals,
|
|
14
|
+
compute_zone_intervals,
|
|
14
15
|
mean as stat_mean, sum as stat_sum, std as stat_std, percentile as stat_percentile
|
|
15
16
|
)
|
|
16
17
|
from .utils import filter_names
|
|
@@ -1429,8 +1430,8 @@ class Property(PropertyOperationsMixin):
|
|
|
1429
1430
|
max_valid_depth = valid_depths.max()
|
|
1430
1431
|
|
|
1431
1432
|
# Find boundaries that fall within the valid data range
|
|
1432
|
-
# Filter to boundaries within valid range
|
|
1433
|
-
mask = (boundary_depths
|
|
1433
|
+
# Filter to boundaries within valid range (inclusive on both ends)
|
|
1434
|
+
mask = (boundary_depths >= min_valid_depth) & (boundary_depths <= max_valid_depth)
|
|
1434
1435
|
potential_boundaries = boundary_depths[mask]
|
|
1435
1436
|
|
|
1436
1437
|
if len(potential_boundaries) == 0:
|
|
@@ -1760,6 +1761,9 @@ class Property(PropertyOperationsMixin):
|
|
|
1760
1761
|
|
|
1761
1762
|
This allows overlapping intervals where the same depths can
|
|
1762
1763
|
contribute to multiple zones.
|
|
1764
|
+
|
|
1765
|
+
Uses zone-aware intervals that are truncated at zone boundaries to ensure
|
|
1766
|
+
thickness is correctly attributed to each zone.
|
|
1763
1767
|
"""
|
|
1764
1768
|
result = {}
|
|
1765
1769
|
|
|
@@ -1768,8 +1772,12 @@ class Property(PropertyOperationsMixin):
|
|
|
1768
1772
|
top = float(interval['top'])
|
|
1769
1773
|
base = float(interval['base'])
|
|
1770
1774
|
|
|
1771
|
-
#
|
|
1772
|
-
|
|
1775
|
+
# Compute zone-aware intervals truncated at zone boundaries
|
|
1776
|
+
zone_intervals = compute_zone_intervals(self.depth, top, base)
|
|
1777
|
+
|
|
1778
|
+
# Create mask based on zone intervals - includes any sample that
|
|
1779
|
+
# contributes to this zone (even boundary samples with partial intervals)
|
|
1780
|
+
interval_mask = zone_intervals > 0
|
|
1773
1781
|
|
|
1774
1782
|
# If there are secondary properties, group within this interval
|
|
1775
1783
|
if self.secondary_properties:
|
|
@@ -1779,7 +1787,8 @@ class Property(PropertyOperationsMixin):
|
|
|
1779
1787
|
weighted=weighted,
|
|
1780
1788
|
arithmetic=arithmetic,
|
|
1781
1789
|
gross_thickness=gross_thickness,
|
|
1782
|
-
precision=precision
|
|
1790
|
+
precision=precision,
|
|
1791
|
+
zone_intervals=zone_intervals
|
|
1783
1792
|
)
|
|
1784
1793
|
else:
|
|
1785
1794
|
# No secondary properties, compute stats directly for interval
|
|
@@ -1788,7 +1797,8 @@ class Property(PropertyOperationsMixin):
|
|
|
1788
1797
|
weighted=weighted,
|
|
1789
1798
|
arithmetic=arithmetic,
|
|
1790
1799
|
gross_thickness=gross_thickness,
|
|
1791
|
-
precision=precision
|
|
1800
|
+
precision=precision,
|
|
1801
|
+
zone_intervals=zone_intervals
|
|
1792
1802
|
)
|
|
1793
1803
|
|
|
1794
1804
|
return result
|
|
@@ -1905,6 +1915,9 @@ class Property(PropertyOperationsMixin):
|
|
|
1905
1915
|
This allows overlapping intervals where the same depths can
|
|
1906
1916
|
contribute to multiple zones. Zone-level metadata (depth_range, thickness)
|
|
1907
1917
|
is shown at the interval level, and fractions are relative to zone thickness.
|
|
1918
|
+
|
|
1919
|
+
Uses zone-aware intervals that are truncated at zone boundaries to ensure
|
|
1920
|
+
thickness is correctly attributed to each zone.
|
|
1908
1921
|
"""
|
|
1909
1922
|
result = {}
|
|
1910
1923
|
|
|
@@ -1913,13 +1926,16 @@ class Property(PropertyOperationsMixin):
|
|
|
1913
1926
|
top = float(interval['top'])
|
|
1914
1927
|
base = float(interval['base'])
|
|
1915
1928
|
|
|
1916
|
-
#
|
|
1917
|
-
|
|
1929
|
+
# Compute zone-aware intervals truncated at zone boundaries
|
|
1930
|
+
zone_intervals = compute_zone_intervals(self.depth, top, base)
|
|
1931
|
+
|
|
1932
|
+
# Create mask based on zone intervals - includes any sample that
|
|
1933
|
+
# contributes to this zone (even boundary samples with partial intervals)
|
|
1934
|
+
interval_mask = zone_intervals > 0
|
|
1918
1935
|
|
|
1919
|
-
# Calculate zone thickness
|
|
1920
|
-
full_intervals = compute_intervals(self.depth)
|
|
1936
|
+
# Calculate zone thickness using zone-aware intervals
|
|
1921
1937
|
valid_mask = ~np.isnan(self.values) & interval_mask
|
|
1922
|
-
zone_thickness = float(np.sum(
|
|
1938
|
+
zone_thickness = float(np.sum(zone_intervals[valid_mask]))
|
|
1923
1939
|
|
|
1924
1940
|
# Get actual depth range within the interval (where we have data)
|
|
1925
1941
|
if np.any(valid_mask):
|
|
@@ -1944,7 +1960,8 @@ class Property(PropertyOperationsMixin):
|
|
|
1944
1960
|
interval_mask,
|
|
1945
1961
|
gross_thickness=zone_thickness, # Use zone thickness for fractions
|
|
1946
1962
|
precision=precision,
|
|
1947
|
-
include_depth_range=False # Don't include depth_range per facies
|
|
1963
|
+
include_depth_range=False, # Don't include depth_range per facies
|
|
1964
|
+
zone_intervals=zone_intervals # Pass zone-aware intervals
|
|
1948
1965
|
)
|
|
1949
1966
|
else:
|
|
1950
1967
|
# No secondary properties, compute stats directly for interval
|
|
@@ -1952,7 +1969,8 @@ class Property(PropertyOperationsMixin):
|
|
|
1952
1969
|
interval_mask,
|
|
1953
1970
|
gross_thickness=zone_thickness, # Use zone thickness for fractions
|
|
1954
1971
|
precision=precision,
|
|
1955
|
-
include_depth_range=False # Don't include depth_range per facies
|
|
1972
|
+
include_depth_range=False, # Don't include depth_range per facies
|
|
1973
|
+
zone_intervals=zone_intervals # Pass zone-aware intervals
|
|
1956
1974
|
)
|
|
1957
1975
|
|
|
1958
1976
|
# Nest facies stats under 'facies' key for cleaner structure
|
|
@@ -1967,7 +1985,8 @@ class Property(PropertyOperationsMixin):
|
|
|
1967
1985
|
mask: np.ndarray,
|
|
1968
1986
|
gross_thickness: float,
|
|
1969
1987
|
precision: int = 6,
|
|
1970
|
-
include_depth_range: bool = True
|
|
1988
|
+
include_depth_range: bool = True,
|
|
1989
|
+
zone_intervals: Optional[np.ndarray] = None
|
|
1971
1990
|
) -> dict:
|
|
1972
1991
|
"""
|
|
1973
1992
|
Recursively group discrete statistics by secondary properties.
|
|
@@ -1984,6 +2003,9 @@ class Property(PropertyOperationsMixin):
|
|
|
1984
2003
|
Number of decimal places for rounding
|
|
1985
2004
|
include_depth_range : bool, default True
|
|
1986
2005
|
Whether to include depth_range in per-facies stats
|
|
2006
|
+
zone_intervals : np.ndarray, optional
|
|
2007
|
+
Pre-computed zone-aware intervals truncated at zone boundaries.
|
|
2008
|
+
If None, computes intervals using standard midpoint method.
|
|
1987
2009
|
|
|
1988
2010
|
Returns
|
|
1989
2011
|
-------
|
|
@@ -1992,7 +2014,7 @@ class Property(PropertyOperationsMixin):
|
|
|
1992
2014
|
"""
|
|
1993
2015
|
if filter_idx >= len(self.secondary_properties):
|
|
1994
2016
|
# Base case: compute discrete statistics for this group
|
|
1995
|
-
return self._compute_discrete_stats(mask, gross_thickness, precision, include_depth_range)
|
|
2017
|
+
return self._compute_discrete_stats(mask, gross_thickness, precision, include_depth_range, zone_intervals)
|
|
1996
2018
|
|
|
1997
2019
|
# Get unique values for current filter
|
|
1998
2020
|
current_filter = self.secondary_properties[filter_idx]
|
|
@@ -2002,12 +2024,16 @@ class Property(PropertyOperationsMixin):
|
|
|
2002
2024
|
|
|
2003
2025
|
if len(unique_vals) == 0:
|
|
2004
2026
|
# No valid values, return stats for current mask
|
|
2005
|
-
return self._compute_discrete_stats(mask, gross_thickness, precision, include_depth_range)
|
|
2027
|
+
return self._compute_discrete_stats(mask, gross_thickness, precision, include_depth_range, zone_intervals)
|
|
2006
2028
|
|
|
2007
2029
|
# Group by each unique value
|
|
2008
2030
|
depth_array = self.depth
|
|
2009
2031
|
values_array = self.values
|
|
2010
|
-
|
|
2032
|
+
# Use zone intervals if provided, otherwise compute on full array
|
|
2033
|
+
if zone_intervals is not None:
|
|
2034
|
+
full_intervals = zone_intervals
|
|
2035
|
+
else:
|
|
2036
|
+
full_intervals = compute_intervals(depth_array)
|
|
2011
2037
|
|
|
2012
2038
|
result = {}
|
|
2013
2039
|
for val in unique_vals:
|
|
@@ -2038,7 +2064,7 @@ class Property(PropertyOperationsMixin):
|
|
|
2038
2064
|
key = f"{current_filter.name}_{val:.2f}"
|
|
2039
2065
|
|
|
2040
2066
|
result[key] = self._recursive_discrete_group(
|
|
2041
|
-
filter_idx + 1, sub_mask, group_thickness, precision, include_depth_range
|
|
2067
|
+
filter_idx + 1, sub_mask, group_thickness, precision, include_depth_range, zone_intervals
|
|
2042
2068
|
)
|
|
2043
2069
|
|
|
2044
2070
|
return result
|
|
@@ -2048,7 +2074,8 @@ class Property(PropertyOperationsMixin):
|
|
|
2048
2074
|
mask: np.ndarray,
|
|
2049
2075
|
gross_thickness: float,
|
|
2050
2076
|
precision: int = 6,
|
|
2051
|
-
include_depth_range: bool = True
|
|
2077
|
+
include_depth_range: bool = True,
|
|
2078
|
+
zone_intervals: Optional[np.ndarray] = None
|
|
2052
2079
|
) -> dict:
|
|
2053
2080
|
"""
|
|
2054
2081
|
Compute categorical statistics for discrete property values.
|
|
@@ -2064,6 +2091,9 @@ class Property(PropertyOperationsMixin):
|
|
|
2064
2091
|
include_depth_range : bool, default True
|
|
2065
2092
|
Whether to include depth_range in per-facies stats.
|
|
2066
2093
|
Set to False when using filter_intervals (depth_range shown at zone level).
|
|
2094
|
+
zone_intervals : np.ndarray, optional
|
|
2095
|
+
Pre-computed zone-aware intervals truncated at zone boundaries.
|
|
2096
|
+
If None, computes intervals using standard midpoint method.
|
|
2067
2097
|
|
|
2068
2098
|
Returns
|
|
2069
2099
|
-------
|
|
@@ -2077,9 +2107,12 @@ class Property(PropertyOperationsMixin):
|
|
|
2077
2107
|
values = values_array[mask]
|
|
2078
2108
|
depths = depth_array[mask]
|
|
2079
2109
|
|
|
2080
|
-
#
|
|
2081
|
-
|
|
2082
|
-
|
|
2110
|
+
# Use zone intervals if provided, otherwise compute on full array
|
|
2111
|
+
if zone_intervals is not None:
|
|
2112
|
+
intervals = zone_intervals[mask]
|
|
2113
|
+
else:
|
|
2114
|
+
full_intervals = compute_intervals(depth_array)
|
|
2115
|
+
intervals = full_intervals[mask]
|
|
2083
2116
|
|
|
2084
2117
|
# Find unique discrete values
|
|
2085
2118
|
valid_mask_local = ~np.isnan(values)
|
|
@@ -2132,7 +2165,8 @@ class Property(PropertyOperationsMixin):
|
|
|
2132
2165
|
weighted: bool,
|
|
2133
2166
|
arithmetic: bool,
|
|
2134
2167
|
gross_thickness: float,
|
|
2135
|
-
precision: int = 6
|
|
2168
|
+
precision: int = 6,
|
|
2169
|
+
zone_intervals: Optional[np.ndarray] = None
|
|
2136
2170
|
) -> dict:
|
|
2137
2171
|
"""
|
|
2138
2172
|
Recursively group by secondary properties.
|
|
@@ -2151,6 +2185,9 @@ class Property(PropertyOperationsMixin):
|
|
|
2151
2185
|
Total gross thickness for fraction calculation
|
|
2152
2186
|
precision : int, default 6
|
|
2153
2187
|
Number of decimal places for rounding
|
|
2188
|
+
zone_intervals : np.ndarray, optional
|
|
2189
|
+
Pre-computed zone-aware intervals truncated at zone boundaries.
|
|
2190
|
+
If None, computes intervals using standard midpoint method.
|
|
2154
2191
|
|
|
2155
2192
|
Returns
|
|
2156
2193
|
-------
|
|
@@ -2159,7 +2196,7 @@ class Property(PropertyOperationsMixin):
|
|
|
2159
2196
|
"""
|
|
2160
2197
|
if filter_idx >= len(self.secondary_properties):
|
|
2161
2198
|
# Base case: compute statistics for this group
|
|
2162
|
-
return self._compute_stats(mask, weighted, arithmetic, gross_thickness, precision)
|
|
2199
|
+
return self._compute_stats(mask, weighted, arithmetic, gross_thickness, precision, zone_intervals)
|
|
2163
2200
|
|
|
2164
2201
|
# Get unique values for current filter
|
|
2165
2202
|
current_filter = self.secondary_properties[filter_idx]
|
|
@@ -2170,14 +2207,18 @@ class Property(PropertyOperationsMixin):
|
|
|
2170
2207
|
|
|
2171
2208
|
if len(unique_vals) == 0:
|
|
2172
2209
|
# No valid values, return stats for current mask
|
|
2173
|
-
return self._compute_stats(mask, weighted, arithmetic, gross_thickness, precision)
|
|
2210
|
+
return self._compute_stats(mask, weighted, arithmetic, gross_thickness, precision, zone_intervals)
|
|
2174
2211
|
|
|
2175
2212
|
# Calculate parent thickness BEFORE subdividing
|
|
2176
2213
|
# This becomes the gross_thickness for all child groups
|
|
2177
2214
|
# Cache property access to avoid overhead
|
|
2178
2215
|
depth_array = self.depth
|
|
2179
2216
|
values_array = self.values
|
|
2180
|
-
|
|
2217
|
+
# Use zone intervals if provided, otherwise compute on full array
|
|
2218
|
+
if zone_intervals is not None:
|
|
2219
|
+
parent_intervals = zone_intervals
|
|
2220
|
+
else:
|
|
2221
|
+
parent_intervals = compute_intervals(depth_array)
|
|
2181
2222
|
parent_valid = mask & ~np.isnan(values_array)
|
|
2182
2223
|
parent_thickness = float(np.sum(parent_intervals[parent_valid]))
|
|
2183
2224
|
|
|
@@ -2212,7 +2253,7 @@ class Property(PropertyOperationsMixin):
|
|
|
2212
2253
|
key = f"{current_filter.name}_{val:.2f}"
|
|
2213
2254
|
|
|
2214
2255
|
result[key] = self._recursive_group(
|
|
2215
|
-
filter_idx + 1, sub_mask, weighted, arithmetic, parent_thickness, precision
|
|
2256
|
+
filter_idx + 1, sub_mask, weighted, arithmetic, parent_thickness, precision, zone_intervals
|
|
2216
2257
|
)
|
|
2217
2258
|
|
|
2218
2259
|
return result
|
|
@@ -2223,7 +2264,8 @@ class Property(PropertyOperationsMixin):
|
|
|
2223
2264
|
weighted: bool = True,
|
|
2224
2265
|
arithmetic: bool = False,
|
|
2225
2266
|
gross_thickness: float = 0.0,
|
|
2226
|
-
precision: int = 6
|
|
2267
|
+
precision: int = 6,
|
|
2268
|
+
zone_intervals: Optional[np.ndarray] = None
|
|
2227
2269
|
) -> dict:
|
|
2228
2270
|
"""
|
|
2229
2271
|
Compute statistics for values selected by mask.
|
|
@@ -2244,6 +2286,9 @@ class Property(PropertyOperationsMixin):
|
|
|
2244
2286
|
Total gross thickness for fraction calculation
|
|
2245
2287
|
precision : int, default 6
|
|
2246
2288
|
Number of decimal places for rounding
|
|
2289
|
+
zone_intervals : np.ndarray, optional
|
|
2290
|
+
Pre-computed zone-aware intervals truncated at zone boundaries.
|
|
2291
|
+
If None, computes intervals using standard midpoint method.
|
|
2247
2292
|
|
|
2248
2293
|
Returns
|
|
2249
2294
|
-------
|
|
@@ -2263,12 +2308,17 @@ class Property(PropertyOperationsMixin):
|
|
|
2263
2308
|
values = values_array[mask]
|
|
2264
2309
|
valid = values[~np.isnan(values)]
|
|
2265
2310
|
|
|
2266
|
-
#
|
|
2267
|
-
#
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2311
|
+
# Use zone intervals if provided, otherwise compute on full array
|
|
2312
|
+
# Zone intervals are truncated at zone boundaries for accurate thickness
|
|
2313
|
+
if zone_intervals is not None:
|
|
2314
|
+
intervals = zone_intervals[mask]
|
|
2315
|
+
else:
|
|
2316
|
+
# Compute depth intervals on FULL depth array first, then mask
|
|
2317
|
+
# This is critical! Intervals must be computed on full grid so that
|
|
2318
|
+
# zone boundary samples get correct weights based on their neighbors
|
|
2319
|
+
# in the full sequence, not just within their zone.
|
|
2320
|
+
full_intervals = compute_intervals(depth_array)
|
|
2321
|
+
intervals = full_intervals[mask]
|
|
2272
2322
|
valid_mask_local = ~np.isnan(values)
|
|
2273
2323
|
valid_intervals = intervals[valid_mask_local]
|
|
2274
2324
|
|
|
@@ -61,6 +61,83 @@ def compute_intervals(depth: np.ndarray) -> np.ndarray:
|
|
|
61
61
|
return intervals
|
|
62
62
|
|
|
63
63
|
|
|
64
|
+
def compute_zone_intervals(
|
|
65
|
+
depth: np.ndarray,
|
|
66
|
+
top: float,
|
|
67
|
+
base: float
|
|
68
|
+
) -> np.ndarray:
|
|
69
|
+
"""
|
|
70
|
+
Compute depth intervals truncated to zone boundaries.
|
|
71
|
+
|
|
72
|
+
Uses the midpoint method but truncates intervals at zone boundaries
|
|
73
|
+
to ensure thickness is correctly attributed to each zone.
|
|
74
|
+
|
|
75
|
+
Parameters
|
|
76
|
+
----------
|
|
77
|
+
depth : np.ndarray
|
|
78
|
+
Depth values (must be sorted ascending)
|
|
79
|
+
top : float
|
|
80
|
+
Zone top depth (inclusive)
|
|
81
|
+
base : float
|
|
82
|
+
Zone base depth (exclusive)
|
|
83
|
+
|
|
84
|
+
Returns
|
|
85
|
+
-------
|
|
86
|
+
np.ndarray
|
|
87
|
+
Interval thickness for each depth point, truncated to zone boundaries.
|
|
88
|
+
Points outside the zone have zero interval.
|
|
89
|
+
|
|
90
|
+
Examples
|
|
91
|
+
--------
|
|
92
|
+
>>> depth = np.array([2708.0, 2708.3, 2708.4, 2708.6])
|
|
93
|
+
>>> # Zone from 2708.0 to 2708.4
|
|
94
|
+
>>> compute_zone_intervals(depth, 2708.0, 2708.4)
|
|
95
|
+
array([0.15, 0.2, 0.05, 0.0])
|
|
96
|
+
|
|
97
|
+
The intervals are truncated at zone boundary 2708.4:
|
|
98
|
+
- 2708.0: from 2708.0 to midpoint(2708.0, 2708.3)=2708.15 = 0.15m
|
|
99
|
+
- 2708.3: from 2708.15 to midpoint(2708.3, 2708.4)=2708.35 = 0.2m
|
|
100
|
+
- 2708.4: from 2708.35 to 2708.4 (zone boundary) = 0.05m (truncated)
|
|
101
|
+
- 2708.6: outside zone = 0.0m
|
|
102
|
+
"""
|
|
103
|
+
if len(depth) == 0:
|
|
104
|
+
return np.array([])
|
|
105
|
+
|
|
106
|
+
if len(depth) == 1:
|
|
107
|
+
# Single point - check if it's in the zone
|
|
108
|
+
if top <= depth[0] < base:
|
|
109
|
+
return np.array([base - top])
|
|
110
|
+
return np.array([0.0])
|
|
111
|
+
|
|
112
|
+
zone_intervals = np.zeros(len(depth))
|
|
113
|
+
|
|
114
|
+
for i in range(len(depth)):
|
|
115
|
+
d = depth[i]
|
|
116
|
+
|
|
117
|
+
# Calculate midpoint bounds for this sample
|
|
118
|
+
if i == 0:
|
|
119
|
+
lower_bound = d - (depth[1] - d) / 2.0 # Mirror first interval
|
|
120
|
+
else:
|
|
121
|
+
lower_bound = (depth[i - 1] + d) / 2.0
|
|
122
|
+
|
|
123
|
+
if i == len(depth) - 1:
|
|
124
|
+
upper_bound = d + (d - depth[-2]) / 2.0 # Mirror last interval
|
|
125
|
+
else:
|
|
126
|
+
upper_bound = (d + depth[i + 1]) / 2.0
|
|
127
|
+
|
|
128
|
+
# Truncate to zone boundaries
|
|
129
|
+
effective_lower = max(lower_bound, top)
|
|
130
|
+
effective_upper = min(upper_bound, base)
|
|
131
|
+
|
|
132
|
+
# Only count if there's overlap with the zone
|
|
133
|
+
if effective_upper > effective_lower:
|
|
134
|
+
zone_intervals[i] = effective_upper - effective_lower
|
|
135
|
+
else:
|
|
136
|
+
zone_intervals[i] = 0.0
|
|
137
|
+
|
|
138
|
+
return zone_intervals
|
|
139
|
+
|
|
140
|
+
|
|
64
141
|
def mean(
|
|
65
142
|
values: np.ndarray,
|
|
66
143
|
weights: Optional[np.ndarray] = None,
|
|
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.148 → well_log_toolkit-0.1.149}/well_log_toolkit.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{well_log_toolkit-0.1.148 → well_log_toolkit-0.1.149}/well_log_toolkit.egg-info/requires.txt
RENAMED
|
File without changes
|
{well_log_toolkit-0.1.148 → well_log_toolkit-0.1.149}/well_log_toolkit.egg-info/top_level.txt
RENAMED
|
File without changes
|