das-mccc 0.2.0__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.
- das_mccc-0.2.0/LICENSE +21 -0
- das_mccc-0.2.0/PKG-INFO +107 -0
- das_mccc-0.2.0/README.md +79 -0
- das_mccc-0.2.0/pyproject.toml +45 -0
- das_mccc-0.2.0/setup.cfg +4 -0
- das_mccc-0.2.0/src/das_mccc.egg-info/PKG-INFO +107 -0
- das_mccc-0.2.0/src/das_mccc.egg-info/SOURCES.txt +19 -0
- das_mccc-0.2.0/src/das_mccc.egg-info/dependency_links.txt +1 -0
- das_mccc-0.2.0/src/das_mccc.egg-info/requires.txt +9 -0
- das_mccc-0.2.0/src/das_mccc.egg-info/top_level.txt +1 -0
- das_mccc-0.2.0/src/dasmccc/__init__.py +45 -0
- das_mccc-0.2.0/src/dasmccc/anchor.py +79 -0
- das_mccc-0.2.0/src/dasmccc/core.py +210 -0
- das_mccc-0.2.0/src/dasmccc/legacy.py +91 -0
- das_mccc-0.2.0/src/dasmccc/ops.py +61 -0
- das_mccc-0.2.0/src/dasmccc/pipeline.py +597 -0
- das_mccc-0.2.0/src/dasmccc/polarity.py +150 -0
- das_mccc-0.2.0/src/dasmccc/signal.py +147 -0
- das_mccc-0.2.0/tests/test_core.py +102 -0
- das_mccc-0.2.0/tests/test_legacy.py +64 -0
- das_mccc-0.2.0/tests/test_pipeline.py +268 -0
das_mccc-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-2026 Jaewoo Kim
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
das_mccc-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: das-mccc
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Iterative network MCCC refinement of DAS arrival curves with absolute anchoring and Ricker polarity QC
|
|
5
|
+
Author-email: Jaewoo Kim <jk103@rice.edu>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Repository, https://github.com/Jaewoo-Kim-Rice/das-mccc
|
|
8
|
+
Project-URL: Algorithm, https://github.com/Jaewoo-Kim-Rice/das-mccc/blob/main/docs/algorithm.md
|
|
9
|
+
Keywords: DAS,distributed acoustic sensing,microseismic,cross-correlation,phase picking
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: numpy
|
|
21
|
+
Requires-Dist: scipy
|
|
22
|
+
Provides-Extra: numba
|
|
23
|
+
Requires-Dist: numba; extra == "numba"
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pytest; extra == "dev"
|
|
26
|
+
Requires-Dist: ruff; extra == "dev"
|
|
27
|
+
Dynamic: license-file
|
|
28
|
+
|
|
29
|
+
# das-mccc
|
|
30
|
+
|
|
31
|
+
Refine a DAS arrival curve with an iterative network multi-channel cross-correlation
|
|
32
|
+
(MCCC), anchor its absolute level on the aligned stack, and return per-channel polarity
|
|
33
|
+
and quality measures. Arrays in, arrays out: no site, file or path concepts.
|
|
34
|
+
|
|
35
|
+
The core is the refiner of [das-focmec](https://github.com/Jaewoo-Kim-Rice/das-focmec)
|
|
36
|
+
(`das_focmec.processing.mccc_core.ultra_mccc_iterative`), extracted with its history so it
|
|
37
|
+
can be used by any picker. Started from a rough curve (a VLM trace, a bracket, a
|
|
38
|
+
theoretical moveout) it recovers the shape of the arrival to the precision of a human
|
|
39
|
+
curve: on 24 CAPE 2025 reads the shape MAD against human picks went from 2.2 to 1.95 ms
|
|
40
|
+
(P) and 4.5 to 4.15 ms (S), and a curve started on the wrong lobe went from 5.6 to 2.65 ms.
|
|
41
|
+
|
|
42
|
+
## Install
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
pip install -e . # numpy, scipy
|
|
46
|
+
pip install -e '.[numba]' # fast pairwise correlation (strongly recommended)
|
|
47
|
+
pip install -e '.[dev]' # pytest, ruff
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Without numba the pairwise correlation runs in pure numpy: identical results, one to two
|
|
51
|
+
orders of magnitude slower, and a warning is logged at import.
|
|
52
|
+
|
|
53
|
+
## Use
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
import numpy as np
|
|
57
|
+
from dasmccc import refine_curve, refine_phases, DIRECT, SECONDARY
|
|
58
|
+
|
|
59
|
+
# waveform: (n_channels, n_samples) float, filtered as you like
|
|
60
|
+
# curve : (n_channels,) arrival in samples, NaN where the phase is not picked
|
|
61
|
+
res = refine_curve(waveform, curve, DIRECT)
|
|
62
|
+
|
|
63
|
+
res.curve # refined arrival (samples), NaN where the input was NaN
|
|
64
|
+
res.curve_relative # same shape, at the initial curve's level (no anchor)
|
|
65
|
+
res.anchor_offset # samples added by the anchor rule (NaN if the rule refused)
|
|
66
|
+
res.polarity # -1 / 0 / +1 per channel
|
|
67
|
+
res.snr, res.coherence, res.kept
|
|
68
|
+
res.aligned, res.stack # the aligned window and its stack, for plots
|
|
69
|
+
|
|
70
|
+
# several phases of one gather, strongest first; refined phases are masked for the next and a
|
|
71
|
+
# narrow tapered pre-mask (+-40 samples, 10 taper) keeps them out of the correlation
|
|
72
|
+
out = refine_phases(waveform, {"S": s_curve, "P": p_curve, "SP": sp_curve})
|
|
73
|
+
# several curves of one tag: any keys, plus a key -> tag map
|
|
74
|
+
out = refine_phases(waveform, {"S": s_curve, "R1": r1, "R2": r2},
|
|
75
|
+
tags={"S": "S", "R1": "REFL", "R2": "REFL"})
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Settings live in `RefineConfig` (everything in samples and channels). `DIRECT` is the
|
|
79
|
+
das-focmec configuration for direct waves (window 200, corr_len 200, smoothness 50, four
|
|
80
|
+
passes, pre-mask 100); `SECONDARY` narrows it for conversions and reflections (window
|
|
81
|
+
120, corr_len 100, three passes, pre-mask 50). Both were calibrated at 1 kHz and 2 m channel
|
|
82
|
+
spacing; `docs/algorithm.md` gives the conversion to other rates and spacings, what each
|
|
83
|
+
knob does, and the anchoring and masking rules.
|
|
84
|
+
|
|
85
|
+
## For das-focmec
|
|
86
|
+
|
|
87
|
+
`dasmccc.legacy` exposes `ultra_mccc_iterative` and `diff_corr_ric` with the das-focmec
|
|
88
|
+
signatures and identical results, so `das_focmec.processing.workflows` only changes its
|
|
89
|
+
import line.
|
|
90
|
+
|
|
91
|
+
## What it does not do
|
|
92
|
+
|
|
93
|
+
* It does not re-pick. The initial curve decides which arrival and roughly which lobe is
|
|
94
|
+
refined; MCCC measures relative delays within `pair_slope` samples per channel of it.
|
|
95
|
+
* The anchor moves the whole curve by one offset measured on the stack (secondary phases
|
|
96
|
+
inherit their parent's). The first-lobe rule sits about 5 ms before the human pick for P
|
|
97
|
+
and within a few ms of it for S on the CAPE 2025 fibres, with a per-fibre constant;
|
|
98
|
+
calibrate it per site against a few human picks when onsets are needed.
|
|
99
|
+
* Sub-sample precision: the alignment is integer; tau is a float but the returned curve
|
|
100
|
+
inherits the integer initial alignment plus the smoothed tau.
|
|
101
|
+
|
|
102
|
+
## Development
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
PYTHONPATH=src pytest -q
|
|
106
|
+
ruff check src tests && ruff format --check src tests
|
|
107
|
+
```
|
das_mccc-0.2.0/README.md
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# das-mccc
|
|
2
|
+
|
|
3
|
+
Refine a DAS arrival curve with an iterative network multi-channel cross-correlation
|
|
4
|
+
(MCCC), anchor its absolute level on the aligned stack, and return per-channel polarity
|
|
5
|
+
and quality measures. Arrays in, arrays out: no site, file or path concepts.
|
|
6
|
+
|
|
7
|
+
The core is the refiner of [das-focmec](https://github.com/Jaewoo-Kim-Rice/das-focmec)
|
|
8
|
+
(`das_focmec.processing.mccc_core.ultra_mccc_iterative`), extracted with its history so it
|
|
9
|
+
can be used by any picker. Started from a rough curve (a VLM trace, a bracket, a
|
|
10
|
+
theoretical moveout) it recovers the shape of the arrival to the precision of a human
|
|
11
|
+
curve: on 24 CAPE 2025 reads the shape MAD against human picks went from 2.2 to 1.95 ms
|
|
12
|
+
(P) and 4.5 to 4.15 ms (S), and a curve started on the wrong lobe went from 5.6 to 2.65 ms.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
pip install -e . # numpy, scipy
|
|
18
|
+
pip install -e '.[numba]' # fast pairwise correlation (strongly recommended)
|
|
19
|
+
pip install -e '.[dev]' # pytest, ruff
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Without numba the pairwise correlation runs in pure numpy: identical results, one to two
|
|
23
|
+
orders of magnitude slower, and a warning is logged at import.
|
|
24
|
+
|
|
25
|
+
## Use
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
import numpy as np
|
|
29
|
+
from dasmccc import refine_curve, refine_phases, DIRECT, SECONDARY
|
|
30
|
+
|
|
31
|
+
# waveform: (n_channels, n_samples) float, filtered as you like
|
|
32
|
+
# curve : (n_channels,) arrival in samples, NaN where the phase is not picked
|
|
33
|
+
res = refine_curve(waveform, curve, DIRECT)
|
|
34
|
+
|
|
35
|
+
res.curve # refined arrival (samples), NaN where the input was NaN
|
|
36
|
+
res.curve_relative # same shape, at the initial curve's level (no anchor)
|
|
37
|
+
res.anchor_offset # samples added by the anchor rule (NaN if the rule refused)
|
|
38
|
+
res.polarity # -1 / 0 / +1 per channel
|
|
39
|
+
res.snr, res.coherence, res.kept
|
|
40
|
+
res.aligned, res.stack # the aligned window and its stack, for plots
|
|
41
|
+
|
|
42
|
+
# several phases of one gather, strongest first; refined phases are masked for the next and a
|
|
43
|
+
# narrow tapered pre-mask (+-40 samples, 10 taper) keeps them out of the correlation
|
|
44
|
+
out = refine_phases(waveform, {"S": s_curve, "P": p_curve, "SP": sp_curve})
|
|
45
|
+
# several curves of one tag: any keys, plus a key -> tag map
|
|
46
|
+
out = refine_phases(waveform, {"S": s_curve, "R1": r1, "R2": r2},
|
|
47
|
+
tags={"S": "S", "R1": "REFL", "R2": "REFL"})
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Settings live in `RefineConfig` (everything in samples and channels). `DIRECT` is the
|
|
51
|
+
das-focmec configuration for direct waves (window 200, corr_len 200, smoothness 50, four
|
|
52
|
+
passes, pre-mask 100); `SECONDARY` narrows it for conversions and reflections (window
|
|
53
|
+
120, corr_len 100, three passes, pre-mask 50). Both were calibrated at 1 kHz and 2 m channel
|
|
54
|
+
spacing; `docs/algorithm.md` gives the conversion to other rates and spacings, what each
|
|
55
|
+
knob does, and the anchoring and masking rules.
|
|
56
|
+
|
|
57
|
+
## For das-focmec
|
|
58
|
+
|
|
59
|
+
`dasmccc.legacy` exposes `ultra_mccc_iterative` and `diff_corr_ric` with the das-focmec
|
|
60
|
+
signatures and identical results, so `das_focmec.processing.workflows` only changes its
|
|
61
|
+
import line.
|
|
62
|
+
|
|
63
|
+
## What it does not do
|
|
64
|
+
|
|
65
|
+
* It does not re-pick. The initial curve decides which arrival and roughly which lobe is
|
|
66
|
+
refined; MCCC measures relative delays within `pair_slope` samples per channel of it.
|
|
67
|
+
* The anchor moves the whole curve by one offset measured on the stack (secondary phases
|
|
68
|
+
inherit their parent's). The first-lobe rule sits about 5 ms before the human pick for P
|
|
69
|
+
and within a few ms of it for S on the CAPE 2025 fibres, with a per-fibre constant;
|
|
70
|
+
calibrate it per site against a few human picks when onsets are needed.
|
|
71
|
+
* Sub-sample precision: the alignment is integer; tau is a float but the returned curve
|
|
72
|
+
inherits the integer initial alignment plus the smoothed tau.
|
|
73
|
+
|
|
74
|
+
## Development
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
PYTHONPATH=src pytest -q
|
|
78
|
+
ruff check src tests && ruff format --check src tests
|
|
79
|
+
```
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "das-mccc"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "Iterative network MCCC refinement of DAS arrival curves with absolute anchoring and Ricker polarity QC"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{name = "Jaewoo Kim", email = "jk103@rice.edu"}]
|
|
14
|
+
keywords = ["DAS", "distributed acoustic sensing", "microseismic", "cross-correlation", "phase picking"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 4 - Beta",
|
|
17
|
+
"Intended Audience :: Science/Research",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3.10",
|
|
20
|
+
"Programming Language :: Python :: 3.11",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Topic :: Scientific/Engineering :: Physics",
|
|
23
|
+
]
|
|
24
|
+
dependencies = ["numpy", "scipy"]
|
|
25
|
+
|
|
26
|
+
[project.urls]
|
|
27
|
+
Repository = "https://github.com/Jaewoo-Kim-Rice/das-mccc"
|
|
28
|
+
Algorithm = "https://github.com/Jaewoo-Kim-Rice/das-mccc/blob/main/docs/algorithm.md"
|
|
29
|
+
|
|
30
|
+
[project.optional-dependencies]
|
|
31
|
+
numba = ["numba"]
|
|
32
|
+
dev = ["pytest", "ruff"]
|
|
33
|
+
|
|
34
|
+
[tool.setuptools.packages.find]
|
|
35
|
+
where = ["src"]
|
|
36
|
+
|
|
37
|
+
[tool.ruff]
|
|
38
|
+
line-length = 100
|
|
39
|
+
target-version = "py310"
|
|
40
|
+
|
|
41
|
+
[tool.ruff.lint]
|
|
42
|
+
select = ["E", "F", "I", "B", "UP"]
|
|
43
|
+
|
|
44
|
+
[tool.pytest.ini_options]
|
|
45
|
+
testpaths = ["tests"]
|
das_mccc-0.2.0/setup.cfg
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: das-mccc
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Iterative network MCCC refinement of DAS arrival curves with absolute anchoring and Ricker polarity QC
|
|
5
|
+
Author-email: Jaewoo Kim <jk103@rice.edu>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Repository, https://github.com/Jaewoo-Kim-Rice/das-mccc
|
|
8
|
+
Project-URL: Algorithm, https://github.com/Jaewoo-Kim-Rice/das-mccc/blob/main/docs/algorithm.md
|
|
9
|
+
Keywords: DAS,distributed acoustic sensing,microseismic,cross-correlation,phase picking
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: numpy
|
|
21
|
+
Requires-Dist: scipy
|
|
22
|
+
Provides-Extra: numba
|
|
23
|
+
Requires-Dist: numba; extra == "numba"
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pytest; extra == "dev"
|
|
26
|
+
Requires-Dist: ruff; extra == "dev"
|
|
27
|
+
Dynamic: license-file
|
|
28
|
+
|
|
29
|
+
# das-mccc
|
|
30
|
+
|
|
31
|
+
Refine a DAS arrival curve with an iterative network multi-channel cross-correlation
|
|
32
|
+
(MCCC), anchor its absolute level on the aligned stack, and return per-channel polarity
|
|
33
|
+
and quality measures. Arrays in, arrays out: no site, file or path concepts.
|
|
34
|
+
|
|
35
|
+
The core is the refiner of [das-focmec](https://github.com/Jaewoo-Kim-Rice/das-focmec)
|
|
36
|
+
(`das_focmec.processing.mccc_core.ultra_mccc_iterative`), extracted with its history so it
|
|
37
|
+
can be used by any picker. Started from a rough curve (a VLM trace, a bracket, a
|
|
38
|
+
theoretical moveout) it recovers the shape of the arrival to the precision of a human
|
|
39
|
+
curve: on 24 CAPE 2025 reads the shape MAD against human picks went from 2.2 to 1.95 ms
|
|
40
|
+
(P) and 4.5 to 4.15 ms (S), and a curve started on the wrong lobe went from 5.6 to 2.65 ms.
|
|
41
|
+
|
|
42
|
+
## Install
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
pip install -e . # numpy, scipy
|
|
46
|
+
pip install -e '.[numba]' # fast pairwise correlation (strongly recommended)
|
|
47
|
+
pip install -e '.[dev]' # pytest, ruff
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Without numba the pairwise correlation runs in pure numpy: identical results, one to two
|
|
51
|
+
orders of magnitude slower, and a warning is logged at import.
|
|
52
|
+
|
|
53
|
+
## Use
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
import numpy as np
|
|
57
|
+
from dasmccc import refine_curve, refine_phases, DIRECT, SECONDARY
|
|
58
|
+
|
|
59
|
+
# waveform: (n_channels, n_samples) float, filtered as you like
|
|
60
|
+
# curve : (n_channels,) arrival in samples, NaN where the phase is not picked
|
|
61
|
+
res = refine_curve(waveform, curve, DIRECT)
|
|
62
|
+
|
|
63
|
+
res.curve # refined arrival (samples), NaN where the input was NaN
|
|
64
|
+
res.curve_relative # same shape, at the initial curve's level (no anchor)
|
|
65
|
+
res.anchor_offset # samples added by the anchor rule (NaN if the rule refused)
|
|
66
|
+
res.polarity # -1 / 0 / +1 per channel
|
|
67
|
+
res.snr, res.coherence, res.kept
|
|
68
|
+
res.aligned, res.stack # the aligned window and its stack, for plots
|
|
69
|
+
|
|
70
|
+
# several phases of one gather, strongest first; refined phases are masked for the next and a
|
|
71
|
+
# narrow tapered pre-mask (+-40 samples, 10 taper) keeps them out of the correlation
|
|
72
|
+
out = refine_phases(waveform, {"S": s_curve, "P": p_curve, "SP": sp_curve})
|
|
73
|
+
# several curves of one tag: any keys, plus a key -> tag map
|
|
74
|
+
out = refine_phases(waveform, {"S": s_curve, "R1": r1, "R2": r2},
|
|
75
|
+
tags={"S": "S", "R1": "REFL", "R2": "REFL"})
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Settings live in `RefineConfig` (everything in samples and channels). `DIRECT` is the
|
|
79
|
+
das-focmec configuration for direct waves (window 200, corr_len 200, smoothness 50, four
|
|
80
|
+
passes, pre-mask 100); `SECONDARY` narrows it for conversions and reflections (window
|
|
81
|
+
120, corr_len 100, three passes, pre-mask 50). Both were calibrated at 1 kHz and 2 m channel
|
|
82
|
+
spacing; `docs/algorithm.md` gives the conversion to other rates and spacings, what each
|
|
83
|
+
knob does, and the anchoring and masking rules.
|
|
84
|
+
|
|
85
|
+
## For das-focmec
|
|
86
|
+
|
|
87
|
+
`dasmccc.legacy` exposes `ultra_mccc_iterative` and `diff_corr_ric` with the das-focmec
|
|
88
|
+
signatures and identical results, so `das_focmec.processing.workflows` only changes its
|
|
89
|
+
import line.
|
|
90
|
+
|
|
91
|
+
## What it does not do
|
|
92
|
+
|
|
93
|
+
* It does not re-pick. The initial curve decides which arrival and roughly which lobe is
|
|
94
|
+
refined; MCCC measures relative delays within `pair_slope` samples per channel of it.
|
|
95
|
+
* The anchor moves the whole curve by one offset measured on the stack (secondary phases
|
|
96
|
+
inherit their parent's). The first-lobe rule sits about 5 ms before the human pick for P
|
|
97
|
+
and within a few ms of it for S on the CAPE 2025 fibres, with a per-fibre constant;
|
|
98
|
+
calibrate it per site against a few human picks when onsets are needed.
|
|
99
|
+
* Sub-sample precision: the alignment is integer; tau is a float but the returned curve
|
|
100
|
+
inherits the integer initial alignment plus the smoothed tau.
|
|
101
|
+
|
|
102
|
+
## Development
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
PYTHONPATH=src pytest -q
|
|
106
|
+
ruff check src tests && ruff format --check src tests
|
|
107
|
+
```
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/das_mccc.egg-info/PKG-INFO
|
|
5
|
+
src/das_mccc.egg-info/SOURCES.txt
|
|
6
|
+
src/das_mccc.egg-info/dependency_links.txt
|
|
7
|
+
src/das_mccc.egg-info/requires.txt
|
|
8
|
+
src/das_mccc.egg-info/top_level.txt
|
|
9
|
+
src/dasmccc/__init__.py
|
|
10
|
+
src/dasmccc/anchor.py
|
|
11
|
+
src/dasmccc/core.py
|
|
12
|
+
src/dasmccc/legacy.py
|
|
13
|
+
src/dasmccc/ops.py
|
|
14
|
+
src/dasmccc/pipeline.py
|
|
15
|
+
src/dasmccc/polarity.py
|
|
16
|
+
src/dasmccc/signal.py
|
|
17
|
+
tests/test_core.py
|
|
18
|
+
tests/test_legacy.py
|
|
19
|
+
tests/test_pipeline.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
dasmccc
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""dasmccc: iterative network MCCC refinement of DAS arrival curves.
|
|
2
|
+
|
|
3
|
+
from dasmccc import refine_curve, DIRECT
|
|
4
|
+
res = refine_curve(waveform, curve, DIRECT) # arrays in, RefineResult out
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
__version__ = "0.2.0"
|
|
8
|
+
|
|
9
|
+
from .anchor import first_lobe, stack_peak
|
|
10
|
+
from .core import iterate_align, mccc, pairwise_lags, partner_pairs, solve_tau
|
|
11
|
+
from .pipeline import (
|
|
12
|
+
DIRECT,
|
|
13
|
+
SECONDARY,
|
|
14
|
+
NothingToRefine,
|
|
15
|
+
RefineConfig,
|
|
16
|
+
RefineResult,
|
|
17
|
+
refine_curve,
|
|
18
|
+
refine_phases,
|
|
19
|
+
)
|
|
20
|
+
from .polarity import PolarityConfig, PolarityResult, RickerWindows, ricker_polarity, ricker_windows
|
|
21
|
+
from .signal import NUMBA_AVAILABLE
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"DIRECT",
|
|
25
|
+
"__version__",
|
|
26
|
+
"NUMBA_AVAILABLE",
|
|
27
|
+
"NothingToRefine",
|
|
28
|
+
"SECONDARY",
|
|
29
|
+
"PolarityConfig",
|
|
30
|
+
"PolarityResult",
|
|
31
|
+
"RefineConfig",
|
|
32
|
+
"RefineResult",
|
|
33
|
+
"RickerWindows",
|
|
34
|
+
"first_lobe",
|
|
35
|
+
"iterate_align",
|
|
36
|
+
"mccc",
|
|
37
|
+
"pairwise_lags",
|
|
38
|
+
"partner_pairs",
|
|
39
|
+
"refine_curve",
|
|
40
|
+
"refine_phases",
|
|
41
|
+
"ricker_polarity",
|
|
42
|
+
"ricker_windows",
|
|
43
|
+
"solve_tau",
|
|
44
|
+
"stack_peak",
|
|
45
|
+
]
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""Absolute anchoring of a relatively aligned gather.
|
|
2
|
+
|
|
3
|
+
Network MCCC fixes only relative delays; the level of the refined curve is whatever the
|
|
4
|
+
initial curve's level was (the lobe the initial picker traced). The rules here measure one
|
|
5
|
+
offset on the aligned stack so that the curve is moved to a reproducible feature of the
|
|
6
|
+
wavelet. All offsets are in samples relative to ``centre`` (the alignment sample).
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import logging
|
|
12
|
+
|
|
13
|
+
import numpy as np
|
|
14
|
+
|
|
15
|
+
log = logging.getLogger("dasmccc")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def stack_peak(stack: np.ndarray, centre: int) -> float:
|
|
19
|
+
"""Offset of the |stack| maximum. Reproducible on strong reads, but the peak lobe is
|
|
20
|
+
not the same lobe on every read (spread of tens of ms against human onsets)."""
|
|
21
|
+
return float(int(np.argmax(np.abs(stack))) - centre)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def first_lobe(
|
|
25
|
+
stack: np.ndarray,
|
|
26
|
+
centre: int,
|
|
27
|
+
min_frac: float = 0.4,
|
|
28
|
+
guard: int | None = 40,
|
|
29
|
+
contiguous: bool = True,
|
|
30
|
+
window: tuple[int, int] | None = (-30, 10),
|
|
31
|
+
) -> float:
|
|
32
|
+
"""Offset of the centre of the first lobe of the stack.
|
|
33
|
+
|
|
34
|
+
Candidates are the local maxima of |stack|. ``window`` = (lo, hi) restricts the search to
|
|
35
|
+
``centre + lo .. centre + hi`` (samples): the prior that the initial picker traced a lobe
|
|
36
|
+
of the arrival, so the onset lies at most one wavelet before it and hardly after it. The
|
|
37
|
+
reference amplitude is the |stack| peak inside the window. With ``contiguous`` the rule
|
|
38
|
+
walks back from that peak lobe by lobe while each lobe keeps at least ``min_frac`` of the
|
|
39
|
+
peak and returns the earliest lobe of that run (a precursor separated by a weaker lobe is
|
|
40
|
+
not the onset); without it the earliest candidate above ``min_frac`` anywhere before the
|
|
41
|
+
peak is taken (the original rule). The peak itself is returned when no earlier lobe
|
|
42
|
+
qualifies.
|
|
43
|
+
|
|
44
|
+
Tuned on 616 CAPE 2025 reads with human picks (das-phase-agent research record,
|
|
45
|
+
`docs/11_anchor_tuning.md`): window (-30, 10), contiguous, min_frac 0.4 on the
|
|
46
|
+
channel-normalised stack removed every refusal and halved the gross anchor errors
|
|
47
|
+
against the original rule (window None, contiguous False, min_frac 0.3, plain stack).
|
|
48
|
+
|
|
49
|
+
``guard`` bounds |offset|: a larger offset is judged unreliable, a warning is logged and
|
|
50
|
+
NaN is returned so the caller keeps the relative level.
|
|
51
|
+
"""
|
|
52
|
+
a = np.abs(np.asarray(stack, float))
|
|
53
|
+
n = len(a)
|
|
54
|
+
if window is None:
|
|
55
|
+
lo, hi = 0, n
|
|
56
|
+
else:
|
|
57
|
+
lo, hi = max(0, centre + int(window[0])), min(n, centre + int(window[1]) + 1)
|
|
58
|
+
if hi - lo < 3:
|
|
59
|
+
raise ValueError(f"anchor window {window} leaves no samples around centre {centre}")
|
|
60
|
+
pk = lo + int(np.argmax(a[lo:hi]))
|
|
61
|
+
ext = [i for i in range(max(1, lo), pk) if a[i] >= a[i - 1] and a[i] >= a[i + 1]]
|
|
62
|
+
first = pk
|
|
63
|
+
if contiguous:
|
|
64
|
+
for i in reversed(ext):
|
|
65
|
+
if a[i] >= min_frac * a[pk]:
|
|
66
|
+
first = i
|
|
67
|
+
else:
|
|
68
|
+
break
|
|
69
|
+
else:
|
|
70
|
+
ok = [i for i in ext if a[i] >= min_frac * a[pk]]
|
|
71
|
+
if ok:
|
|
72
|
+
first = ok[0]
|
|
73
|
+
offset = float(first - centre)
|
|
74
|
+
if guard is not None and abs(offset) > guard:
|
|
75
|
+
log.warning(
|
|
76
|
+
"first_lobe anchor %+.0f samples exceeds guard %d; anchor not applied", offset, guard
|
|
77
|
+
)
|
|
78
|
+
return float("nan")
|
|
79
|
+
return offset
|