Skip to content

Add STALTA characteristic-function atom - #86

Open
amaggi wants to merge 2 commits into
xdas-dev:devfrom
amaggi:feat/stalta
Open

Add STALTA characteristic-function atom#86
amaggi wants to merge 2 commits into
xdas-dev:devfrom
amaggi:feat/stalta

Conversation

@amaggi

@amaggi amaggi commented Sep 3, 2026

Copy link
Copy Markdown

Add an STALTA characteristic-function atom

Xdas has a threshold detector (Trigger) but nothing to feed it apart from a
SeisBench model. This adds the classic amplitude detector — the ratio of a
short-term to a long-term average of the squared signal — as an atom, so
detection no longer needs a network of weights:

picks = xd.trigger(xd.stalta(da, sta=0.5, lta=10.0), thresh=8.0)

or, as a pipeline that streams:

pipeline = xd.stalta(..., sta=0.5, lta=10.0) >> xd.trigger(..., thresh=8.0)
pipeline.process(source, out="picks.csv")

What it does

  • Windows in physical units. sta and lta are given in the units of
    dim, and the sample counts are derived in initialize from the measured
    sampling rate, the way operations.Filter does — so a parameter survives a
    change of rate.
  • Two placements. mode="causal" (default) averages the samples up to and
    including the one it produces, as a real-time detector must; mode="centered"
    averages symmetrically about it, which sharpens the onset but reaches into the
    future and so is offline only. The choice moves pick times, so it is a
    property of the detector rather than a tuning knob.
  • window and pad_mode for the taper applied to both means and for how
    the two ends of the record are extended. Chunk seams are never padded: they
    use the real neighbouring samples.
  • Chunk-invariant. State carries the halo the next window needs, so a
    chunked run returns exactly what a single call on the whole record returns —
    tested over 2, 5, 17 and 200 chunks in both modes, and for a record shorter
    than the halo. In "centered" mode the samples whose window is not yet
    complete are held back and flush() releases them; in "causal" mode nothing
    is held back and flush() has nothing to return.

Notes for review

  • Nothing is added to Trigger. It already turns a characteristic function
    into picks, statefully, and does it well; this only supplies another kind of
    input to it.
  • recursive_sta_lta is deliberately left out. The recursive form is a
    different (and genuinely streaming-native) estimator, and belongs in its own
    PR rather than as a method= flag bolted on here.
  • STALTA does not survive save_state/load_state. Its buffer is a
    DataArray, and Atom.set_state converts a DataArray state to a bare
    ndarray (xdas/atoms/core.py, the # TODO: shouldn't need __array__ line),
    so the coordinates are lost. This is the same limitation DownSample,
    Rechunk and Polyphase already have — the two commented-out round-trip
    tests in tests/test_atoms.py are about exactly this — so I have not worked
    around it here. Happy to take it on as a follow-up if you want the state
    machinery finished.

Checks

  • ruff check and ruff format clean.
  • xdas/atoms/detect.py at 100% coverage bar one pre-existing line
    (Trigger.merge's empty-table branch, covered by test_atoms_ml.py).
  • Docs build with no new warnings; the new user-guide section executes.
  • Full suite green, with two caveats that are not from this branch and
    reproduce on a clean dev on macOS arm64:
    • tests/test_signal.py::TestSTFT::test_parrallel and
      tests/virtual/test_tiles.py::TestStoredForm::test_non_utf8_root_survives_the_header
      fail on dev as they do here.
    • The interpreter segfaults when tests/test_picking.py runs in the same
      process as anything that has imported torch (tests/test_atoms_ml.py, but
      also dascore/seisbench pulled in elsewhere) — torch's OpenMP against
      numba's @njit(parallel=True) in xdas/picking.py. Neither
      KMP_DUPLICATE_LIB_OK=TRUE nor NUMBA_NUM_THREADS=1 helps. Running
      tests/test_picking.py in its own process is the workaround; worth a
      separate issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant