Skip to content

processSEG 0.4.0: the API settled before the viewer, on parseSEG 0.11.0 - #6

Merged
xrfez merged 7 commits into
mainfrom
phase13
Nov 13, 2026
Merged

xrfez merged 7 commits into
mainfrom
phase13

Conversation

@xrfez

@xrfez xrfez commented Nov 13, 2026

Copy link
Copy Markdown
Owner

The open items of the 0.3.0 review, decided by the owner. Breaking, without deprecated forms: nothing depends on processSEG yet. On parseSEG 0.11.0 (xrfez/parseSEG#5); the lock names its release commit.

Breaking: one argument order

verb([pool,] [plan,] src[, dst], meta, operands..., results..., ws, options = ...), written down in docs/api.md → Conventions. A compiled plan comes first, the workspace is the last required argument, and plan constructors take dtMs before the length.

  • applyFilter (FFT and IIR), agc, normalize, applyGainCurve, panelFactor: plan.verb(data, meta, ws), and plan.verb(x, ws) for one trace. agc takes its gains before the workspace.
  • pickInto and coherenceSnrInto: the output array before the workspace.
  • extractInto: the output block right after the input.
  • runPart and panelFactorOf: the part index after the meta.
  • initCorrelationPlan(T, pilot, dtMs, recordSamples) and initSpectrumPlan(T, dtMs, windowLen).

Changed

  • Very low low-cuts: an FFT filter whose impulse is longer than the trace cuts its zero pads to the trace. A 0.02-0.04 Hz low-cut on a 2000 × 2001 panel goes from 568 to 63 ms, and the output stays within 0.39% of the uncut one in the worst measured case (inside the documented zoom bounds). Reflected pads are unchanged.
  • StackAccumulator.merge compares its time axis as every block form does: the interval by sameInterval, the start time within 1e-9 of a sample.
  • Relation walks follow parseSEG 0.11.0's whole-station rule (identical results).

New

  • A guide section with a tested example: processed pre-stack traces written like the input, with the keys held in memory (parseSEG's new writeTrace(w, f, i, samples, keys)).

Rejected, with numbers

stack.normalize stays scalar. It costs 2% of a 2D brute stack and runs once per stack.

Local: nim test, testRelease, asan, docs, and nimpretty over every file.

🤖 Generated with Claude Code

xrfez and others added 7 commits November 13, 2026 17:13
… branch

The parseSEGBranch task switched deps/parseSEG to the long-merged
station-join branch after an Atlas command; with the real repository
mounted there, Atlas is not run at all. parseSEGTest stays, with the
other suites' command in its doc.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rm does

merge compared the interval and the start time exactly, where every
block form compares intervals with sameInterval (a relative 1e-9): two
accumulators made from a header's interval (2000 us / 1000) and a
computed one (1000 / 500 Hz) refused to merge. Now the interval by
sameInterval and the start time within 1e-9 of a sample. Test: tstack
merge_equals_one (0.1 + 0.2 against 0.3 merges; 1e-6 apart does not).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The rule, now in docs/api.md "Conventions":
  verb([pool,] [plan,] src[, dst], meta, operands..., results..., ws, options = ...)
- a compiled plan comes first, so `plan.verb(...)` reads as the plan
  applied; a call without a plan starts with its data;
- the rows, then a separate output block, then the meta;
- then what the operation needs besides the data, then the arrays it fills
  per row;
- the workspace is the last required argument, defaulted options after it;
- plan constructors: initXPlan(T, what it does..., dtMs, samples, options).

Survey of the public forms (the rest already followed it: apply,
correlateInto, traceAmplitudeInto, fitShot, windowSnrInto, applyNmo,
applyStatics, the moveout and statics column forms, the pool forms):
- applyFilter (FFT and IIR), agc, normalize, applyGainCurve, panelFactor:
  data, meta, plan, ws -> plan, data, meta, ws (single-trace forms: plan, x,
  ws); agc's gains before the workspace;
- pickInto: ..., ws, dst -> ..., dst, ws; coherenceSnrInto likewise;
- extractInto: plan, data, meta, dst -> plan, data, dst, meta;
- runPart, panelFactorOf: plan, part, data... -> plan, data..., meta, part;
- initCorrelationPlan(T, pilot, recordSamples, dtMs) and
  initSpectrumPlan(T, windowLen, dtMs) take dtMs before the length.

Breaking, without deprecated forms (nothing depends on processSEG yet).
Every call site in src, tests, bench, the guides and the doc comments
moved with it; the tests of every touched module, tdocs (the guides'
code), tapireview, tumbrella and tvectorized pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The FFT length followed the impulse length L: samples + 2L of zeros, and
bins no wider than half the narrowest ramp. A 0.02-0.04 Hz low-cut at 2 ms
has L = 22031 samples, so a 2001-sample trace took nfft 50000: 568 ms per
2000 x 2001 panel on one thread. Zero pads are now min(L, samples) each
side, and the ramp no longer sets the bins once they are cut (the trace
cannot resolve such a ramp): nfft 6144, 63 ms. requiredMargin stays L.
Reflected pads (high-cuts, notches, trMean or trNone) keep L.

Accuracy first (review xE P4 asked for it): capped against uncapped, the
whole trace (a zoom window of a trace shorter than L is the whole trace),
on 2001 and 501 samples at 2 ms and 1001 at 4 ms; low-cuts of 0.02-0.04,
0.1-0.2 and 0.5-1 Hz, a 0.3-0.6-60-80 band-pass, a Butterworth 0.05 Hz
low-cut; white noise, events, a steady 10 Hz sinusoid, sinusoids inside
the ramps, an offset with a slope. Largest difference 0.39 % of the
output's peak (in-ramp sinusoids, 501 samples), mostly under 0.06 %:
inside the documented zoom bounds (0.5 % broadband, 2.5 % worst case).
Reflected pads cut the same way moved a 60-80 Hz high-cut with a 50 Hz
notch by 0.4-0.5 % on noise and a trMean low-cut by 3-214 %, so they are
left alone (outputs bit-identical to before).

Cost, danger v3, min of 3, ms per 2000-trace panel, before -> after (zero
pads): 2001 samples: 0.02-0.04 Hz 568 -> 63, 0.1-0.2 Hz 97 -> 50, Butter
0.05 Hz 397 -> 48; 501 samples: 504 -> 10.5, 85 -> 10.5, 16.2 -> 10.5
(0.5-1 Hz), 25.2 -> 10.5 (band-pass), 373 -> 10.4. Filters with L shorter
than the trace are unchanged.

Test: tfilterapply an_impulse_longer_than_the_trace.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s in memory

A guide section (velocity-and-stack, "Processed pre-stack traces, written
like the input") and its example, run by tdocs: a shot read, band-passed
by a pipeline, and written with openSeismicWriter(like = f) and
parseSEG 0.11.0's writeTrace(w, f, i, x, row, keys), with coordinates
that exist only in memory; the copy reads back with them, its offsets
and midpoints, and the processed samples. docs/integration.md (D14)
points at it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- processSEG.nimble requires parseSEG >= 0.11.0; atlas.lock (by hand)
  names its release commit 773408d and the new requirement.
- survey/relwalk: parseSEG now ends a relation run with its last whole
  station in every lookup, so the walk no longer needs its channel count
  check for a partial multi-component run; the fallback to `relations`
  stays for runs that overlap or are out of order. Same digests as the
  previous build for every fold, patch, comparison and completeness result
  on Morgantown and the synthetic geometries.
- The two tests that corrupt an index on purpose take parseSEG's internal
  `mindex` (SeismicFile.index is read-only now).
- tmorgantown: the survey's grid unit reads as feet (US survey feet,
  measurement system 2).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Also corrects the lock's copy of processSEG.nimble, which said 0.3.0
through 0.3.1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@xrfez
xrfez merged commit 521f282 into main Nov 13, 2026
3 checks passed
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