API¶
dasio.dasdata¶
Universal DAS data container used across all readers and the desample pipeline.
- dasio.dasdata.normalize_unit(s)[source]¶
Map a free-form unit string onto
VALID_UNITS, else ‘unknown’.Case-insensitive substring match, so a vendor’s “Strain rate (microstrain/sec)” comes back as “microstrain/s”.
- Return type:
str
- class dasio.dasdata.DASmeta[source]¶
One row of per-file catalog metadata (what DASdb holds).
Dict-shaped so vendor scanners can emit it directly and pandas turns a list of them straight into a DataFrame. For ASN / Proc each file yields exactly one DASmeta; OptaSense yields one per contiguous RawDataTime chunk (same path, different first_sample and nt).
begin_time / end_time are last-sample-inclusive, matching the legacy DAS_db contract.
- class dasio.dasdata.DASdata(data: numpy.ndarray, fs: float, dt: float, nt: int, nx: int, dx: float, begin_time: datetime.datetime, end_time: datetime.datetime, gauge_length_m: float | None = None, format: str = 'unknown', origin: str = 'unknown', raw_meta: dict | None = None, t0_sec: float = 0.0, units: str = 'unknown', physical_factor: float = 1.0, index_raw: numpy.ndarray | None = None, dasinfo: dasio.dasinfo.DASinfo | None = None, channel_type: str = 'raw')[source]¶
- Parameters:
data (ndarray)
fs (float)
dt (float)
nt (int)
nx (int)
dx (float)
begin_time (datetime)
end_time (datetime)
gauge_length_m (float | None)
format (str)
origin (str)
raw_meta (dict | None)
t0_sec (float)
units (str)
physical_factor (float)
index_raw (ndarray | None)
dasinfo (DASinfo | None)
channel_type (str)
- property info: dict¶
Snapshot of scalar metadata as a plain dict (no
data/raw_meta).Useful for logging, JSON serialization, and stuffing into downstream metadata records — anything that needs the identifying fields of a DASdata without dragging the array.
- times(reftime=None, type='seconds')[source]¶
The time of every sample.
type='seconds': floats in the record’s own frame (t0_secat sample 0), or sincereftimewhen one is given – asobspy.Trace.times, nothing on the record changes.type='datetime': absolutedatetime64[ns], naive UTC, since numpy carries no zone and every DASdata time is UTC.- Parameters:
reftime (datetime | None)
type (str)
- Return type:
ndarray
- property reftime: datetime¶
what
times(),plotlabels and a floatt_rangecount from. Assign a datetime to move it, an event’s origin time usually; it only rewritest0_sec.- Type:
The instant at t = 0 of the seconds frame
- channels(type=None)[source]¶
Channel number of every row.
- Parameters:
type (str | None) – ‘raw’ (the interrogator’s numbering) or ‘taptest’ (the catalog’s, after
select_taptest). Default ischannel_type, which is also whatselect’sch_rangeandch_indexmatch.- Raises:
ValueError – ‘taptest’ asked for before a catalog was attached.
- Return type:
ndarray
- property ch0: int¶
Optical channel of row 0, read off
index_raw.
- property dch: int¶
Mean raw channel step; exact while the rows are a ramp.
Kept because a texture and an
imshowextent can only be placed on a uniform grid, so they need one spacing rather than a list.
- property plot¶
d.plot()(≡d.plot.imshow()),d.plot.imshow(...),d.plot.wiggle(...).Implementation lives in
dasio.plot; lazy-imported so a bareimport dasdatadoesn’t pull in matplotlib.- Type:
Plot accessor
- select(ch_range=None, ch_index=None, t_range=None)[source]¶
Cut a time window and / or pick channels, in one call. :param ch_range:
(min_ch, max_ch), max exclusive, in the channel numberschannels()reports. Ends outside the array are clipped.- Parameters:
ch_index – explicit channel numbers, in the order wanted, or a length-
nxboolean mask (which selects by row position). A number the array does not hold raises.t_range –
(begin, end), bothdatetime, or both seconds in this DASdata’s own frame wheret0_secis the value at sample 0.
- Returns:
A fresh DASdata.
dt,fsanddxare unchanged — this selects, it does not decimate — anddatais C-contiguous.- Raises:
ValueError – if the two channel arguments together select nothing.
- Return type:
The kept channels are the intersection:
ch_rangefilters,ch_indexrequests, and either may beNonefor no constraint. A channel named bych_indexbut excluded bych_rangeis dropped rather than raising — that is what the pair is for.
- truncate(ch_range=None, t_range=None)[source]¶
A contiguous window —
select(ch_range=..., t_range=...).- Return type:
- select_taptest(dasinfo)[source]¶
Keep the surveyed channels a
DASinfolists, and report their index.- Parameters:
dasinfo – the catalog. Pass
dasinfo.active()to drop bad-quality channels too;located()on that subset is a no-op.- Returns:
A fresh DASdata with
dasinfoattached andchannel_typeset to ‘taptest’.- Return type:
Intersected, not demanded: a catalog covers the whole deployment and this window holds a part of it.
- skip_ch(step)[source]¶
Keep every
step-th channel (uniform decimation), updatingdx.A coarse, faster channel view —
d.skip_ch(5)keeps 1 channel in 5. Because the stride is uniform,dxscales bystepso the channel axis stays physically correct (unlike an arbitraryselect, which leavesdxmeaningless). No spatial anti-alias filter is applied, so this is for display/preview, not analysis.step <= 1returns an unchanged copy.datais C-contiguous.- Parameters:
step (int)
- Return type:
- skip_t(step)[source]¶
Keep every
step-th time sample (uniform decimation), updatingdt/fs.A coarse, faster time view —
d.skip_t(5)keeps 1 sample in 5.dtscales bystepandfsdivides by it, so the time axis stays correct;begin_time/t0_secare unchanged (sample 0 is kept) andend_timesnaps to the last kept sample. No temporal anti-alias filter is applied, so this is for display/preview, not analysis (use a proper resample/decimate for that).step <= 1returns an unchanged copy.datais C-contiguous.- Parameters:
step (int)
- Return type:
- to_physical()[source]¶
Return a copy in microstrain (or microstrain/s), whatever the vendor.
Applies
physical_factorand the strain -> microstrain 1e6 in one float32 pass, resets the factor to 1.0 and updatesunits. Counts, radians and strain all land on the same unit, so downstream code and colorbars never have to ask which vendor a window came from.A metadata-only copy when the data is already microstrain — sharing
datarather than duplicating it — so calling this unconditionally is both safe and idempotent.- Raises:
ValueError – On count / radian / radian/s with no factor attached (
physical_factor == 1.0), rather than silently relabeling raw counts as microstrain. Read withwith_factor=True(the default).- Return type:
- bandpass(fmin, fmax, order=14, zerophase=True, copy=True, nthreads=None)[source]¶
Butterworth bandpass along the time axis. See
processing.bandpass.- Parameters:
fmin (float)
fmax (float)
order (int)
zerophase (bool)
copy (bool)
nthreads (int | None)
- Return type:
- detrend(copy=True)[source]¶
Per-channel linear detrend along time. See
processing.detrend.- Parameters:
copy (bool)
- Return type:
- taper(alpha=0.4, copy=True)[source]¶
Tukey edge taper along time. See
processing.taper.- Parameters:
alpha (float)
copy (bool)
- Return type:
- differentiate(copy=True, method='central')[source]¶
Time-axis derivative. See
processing.differentiate.- Parameters:
copy (bool)
method (str)
- Return type:
- integrate(copy=True)[source]¶
Time-axis cumulative integral. See
processing.integrate.- Parameters:
copy (bool)
- Return type:
- subtract_common_mode(ch_min=0, ch_max=None, copy=True)[source]¶
Common-mode noise rejection. See
processing.subtract_common_mode.- Parameters:
ch_min (int)
ch_max (int | None)
copy (bool)
- Return type:
- unwrap_int32(factor=1, copy=True)[source]¶
OptaSense int32 phase-wrap correction. See
processing.unwrap.- Parameters:
factor (int)
copy (bool)
- Return type:
- median_filter_1d(kernel_size, axis='t', copy=True)[source]¶
Running median along time or channels. See
processing.median_filter.- Parameters:
kernel_size (int)
axis (str)
copy (bool)
- Return type:
- downsample(factor, anti_alias=True, order=8, zerophase=True, copy=True, nthreads=None)[source]¶
Integer-factor time downsample: anti-alias low-pass + stride.
Analysis-grade decimation — unlike
skip_t(a bare stride that aliases), this low-passes below the new Nyquist first. Seeprocessing.downsample.- Parameters:
factor (int)
anti_alias (bool)
order (int)
zerophase (bool)
copy (bool)
nthreads (int | None)
- Return type:
dasio.dasfile¶
DASFile: per-file I/O facade.
Wraps one .h5 / .hdf5 path. format (on-disk format) and
origin (original capture vendor) are detected lazily — only on
attribute access — so construction is free. This matters for
directory-scan hot paths (DASdb.scan_metadata) that build
thousands of DASFile`s in a loop: eager detection would cost one
extra h5py open per file on top of the `.metadata() read itself.
Thin glue over the vendor-specific readers + the OptaSense count-to-
strain factor. This is the canonical entry point for working with a
single DAS file. The free-function dispatchers in
dasio/__init__.py (read_das_data, read_das_metadata,
factor_raw2strain) are thin wrappers around it — kept for
one-shot call sites.
- class dasio.dasfile.DASFile(path, format=None, origin=None)[source]¶
One DAS HDF5 file with lazy format + origin detection.
formatis the on-disk format ('ASN','OptaSense','Proc','Basic', …) and decides which reader dispatches.originis the vendor that originally captured the data — equal toformatfor raw files, recovered from/Acquisition_originattrs for Proc files. It picks the raw->strain factor, andDASdatacarries the same pair so a window keeps both facts after the file is closed.Both attributes are detected on first access and cached. Passing them up-front skips the detection open entirely —
.metadata()needs onlyformat,.factor()needs onlyorigin.- Parameters:
path (Union[str, Path]) – Filesystem path to the .h5 / .hdf5 file.
format (Optional[str]) – Pre-known format. If omitted, detected via
detect_formaton first access.origin (Optional[str]) – Pre-known origin vendor. If omitted, detected via
detect_originon first access.
- read(*, with_factor=True, **kwargs)[source]¶
Load the payload as a
DASdata.Keyword arguments pass through to the vendor reader (
min_ch,max_ch,first_sample,n_samples).with_factor=True(default) attachesDASFile.factor()asDASdata.physical_factor, which is what makes.to_physical()work without the caller having to re-read the file. It costs one extra open — 0.6 ms against a ~550 ms payload read — so the default is on anddesampleopts out explicitly, being the one path that wants raw counts on disk.It is attached only for the instrument units in
_NEEDS_FACTOR; strain payloads need no vendor factor, only the 1e6 thatto_physical()applies.- Parameters:
with_factor (bool)
- Return type:
dasio.dasdb¶
DASdb: catalog of raw DAS files + RawWindow read-plan helpers.
Model:
- DASmeta one contiguous readable piece of raw data (one ASN / Proc
file, or one RawDataTime chunk within an OptaSense file). Defined in
dasdata.pyas a TypedDict so scanners can emit a list of dicts and pandas consumes them directly.- DASdb DataFrame of DASmeta rows for one vendor. Time-contiguous
runs are recomputed on the fly by
segments; nothing is persisted as a segment_id column.- RawWindow selected read plan that
desample.desample_window consumes — files + per-row slice offsets + pad flags.
Vendor-specific logic lives only in list_das_files and the per-
file metadata readers. The catalog and all queries (select_window,
read, plus the desample-side next_unprocessed selector that
consumes DASdb) work off DataFrame rows regardless of vendor.
Time convention: begin_time and end_time are last-sample-inclusive
timestamps, matching legacy DAS-utilities (DAS_db.py). Continuity
between two rows is therefore next.begin_time - prev.end_time ≈ 1/fs,
not ≈ 0.
File metadata (fs, nt, channel count, etc.) is populated at scan time from each file’s HDF5 metadata — filename parsing is never used as a correctness fallback; a file that can’t be read is skipped with a stderr warning.
- dasio.dasdb.list_das_files(raw_dir, format)[source]¶
Enumerate candidate DAS files under
raw_dirforformat.Pure filesystem work — no HDF5 opens. Paired with
DASFile.metadata()to drive incremental catalog refresh without reopening files already in the DASdb.- Parameters:
raw_dir (Path)
format (str)
- Return type:
List[Path]
- dasio.dasdb.detect_dir_format(raw_dir)[source]¶
On-disk format of the first DAS file found under
raw_dir.Depth 1 (flat Proc / OptaSense), 2 (dated Proc, plain ASN) and 3 (ASN with a channel subdir:
<YYYYMMDD>/<ch>/<HHMMSS>.hdf5).globis lazy andnextstops at the first hit, so the deep patterns cost nothing on the shallow layouts.- Parameters:
raw_dir (str | Path)
- Return type:
str
- dasio.dasdb.scan_metadata(raw_dir, format, workers=1, progress=False)[source]¶
Scan
raw_dirfor allformatfiles and return a DASmeta DataFrame.Enumerates files via
list_das_files, reads each through theDASFile.metadata()facade, concatenates OptaSense RawDataTime splits, and sorts by begin_time. Files that fail metadata read are skipped (the vendor metadata readers emit a stderr warning).workers > 1parallelizes the per-file reads;progress=Trueadds a tqdm bar.raw_diris resolved to an absolute path before enumeration so every row’sfilefield is absolute regardless of the caller’s cwd. Without this, a catalog written from a relative--fromwould only resolve correctly when re-read from the same working dir.Raises ValueError when the first readable file is not
format, rather than opening thousands of them to produce an empty catalog.- Parameters:
raw_dir (Path)
format (str)
workers (int)
progress (bool)
- Return type:
DataFrame
- class dasio.dasdb.DASdb(df, format)[source]¶
Catalog of DAS file metadata rows, partitioned on the fly.
- Parameters:
df (pd.DataFrame) – DataFrame with the
_DF_COLUMNSschema (one row per DASmeta). Missing columns fall back to NaN / 0 where a default is meaningful.format (str) –
ASN,OptaSense, orProc(matches the scanner used to builddf).
- property n_segments: int¶
Number of time-contiguous segments — recomputed on access.
- segments(gap_factor=1.5)[source]¶
Yield (begin_time-sorted) DataFrames of time-contiguous rows.
One segment = one continuous run of rows with no acquisition gap. Break criteria between consecutive rows (prev → cur):
timing break — |cur.begin_time - prev.end_time - 1/fs| >
gap_factor x 1/fs (default tolerates roughly one sample of jitter; raise gap_factor to fuse near-contiguous segments). * fs or nx change — acquisition parameters changed. * same file path — OptaSense intra-file RawDataTime breaks always start a new segment regardless of gap size.
Recomputed on every call; DASdb never persists a segment_id.
Implementation note: the break-detection rules are evaluated vectorized over the whole catalog with
Series.shift(1)— on Santorini’s 1.13 M-row production catalog the 24 h slice (~8.6 k rows) goes from ~1 s of GIL-held Python in the pre-vectorize row-by-row loop to ~5 ms. The slow loop was amplifying every /api/state poll’s_read_gaps_24hcall, saturating one CPU and freezing the dashboard whenever a user clicked the gaps tab. See test/test_dasdb_segments_perf.py for the regression guard.- Parameters:
gap_factor (float)
- Return type:
Iterator[DataFrame]
- classmethod from_dir(raw_dir, format=None, workers=1, progress=True)[source]¶
Scan
raw_dirand build a catalog for the givenformat.- Parameters:
raw_dir (Path)
format (str | None)
workers (int)
progress (bool)
- Return type:
- classmethod from_csv(file, format=None)[source]¶
Load a legacy DAS_db CSV (whitespace-separated).
Translates legacy column names (begTime, endTime, nChannels, dCh, GaugeLen, firstSample) into the internal snake_case schema. Drops any
segment_idorformatcolumns the CSV might carry — neither is part of the in-memory schema. Ifformatis given and the CSV has aformatcolumn, rows are filtered to that format before the column is dropped.- Parameters:
file (str | Path)
format (str | None)
- Return type:
- to_csv(file)[source]¶
Write a DAS_db CSV (whitespace-separated, one row per DASmeta).
Emits legacy column names (begTime, endTime, nChannels, dCh, GaugeLen, firstSample) so external DAS-utilities tooling can still read the output. Missing numeric fields get the legacy -1 sentinel (rather than empty, which would collapse under whitespace-separation). Sorted by begin_time; atomic write.
- Parameters:
file (str | Path)
- Return type:
None
- classmethod from_parquet(file, format=None)[source]¶
Load a DASdb Parquet file.
format is recovered from the b’format’ file-level metadata key written by to_parquet; the kwarg is a fallback for foreign parquet files without that key, and a conflict between the two raises.
- Parameters:
file (str | Path)
format (str | None)
- Return type:
- to_parquet(file)[source]¶
Write a DASdb to a Parquet file (snake_case schema, snappy).
format rides along in file-level metadata under b’format’. Sorted by begin_time; atomic write.
- Parameters:
file (str | Path)
- Return type:
None
- classmethod from_file(file, format=None)[source]¶
Load a catalog by path; format chosen from extension.
- Parameters:
file (str | Path)
format (str | None)
- Return type:
- to_file(file)[source]¶
Persist the catalog; format chosen from extension.
- Parameters:
file (str | Path)
- Return type:
None
- append(df_new)[source]¶
Concatenate
df_newrows into the catalog.De-duplicates on (
file,first_sample) — OptaSense emits multiple rows per file (one per RawDataTime chunk), sofilealone is not unique. Sort order is preserved bybegin_time.- Parameters:
df_new (DataFrame)
- Return type:
None
- update_from_dir(raw_dir, workers=1, progress=False)[source]¶
Incrementally add any new files from
raw_dir.Lists the candidate files on disk and only opens the ones whose path isn’t already in
self.df['file']— turns a full O(n_total) rescan into O(n_new) HDF5 opens. Safe to call on every cron tick; continuity is recomputed on the fly bysegments, so gaps that open or close in the newly discovered tail are reflected immediately.workers > 1parallelizes the per-file metadata reads;progress=Truewraps the iteration with a tqdm bar.Returns the number of rows added (0 if nothing was new).
- Parameters:
raw_dir (Path)
workers (int)
progress (bool)
- Return type:
int
- classmethod load_or_build(file, raw_dir, format, overwrite=False, workers=1, progress=False)[source]¶
Load an existing catalog (+ update) or scan raw_dir fresh.
file=None — fresh scan, no persistence.
file exists + !overwrite → load (csv or parquet by extension) + update_from_dir.
file missing, or overwrite → fresh from_dir.
Never writes; caller persists via db.to_csv / db.to_parquet / db.to_file.
- Parameters:
file (Path | None)
raw_dir (Path)
format (str)
overwrite (bool)
workers (int)
progress (bool)
- Return type:
- select_window(begin, end, pad=True)[source]¶
One RawWindow per segment intersecting [begin, end).
Rows whose
[begin_time, end_time]intersects[begin, end)are the target set. Pad files are added from the SAME segment only — neighbours across a gap are never pulled in, so the returned list makes each segment’s own pad-before/pad-after state explicit instead of silently dropping one.Most callers (the
next_unprocessed→desample_windowpath) ask for a window that falls entirely in one segment and just take[0]; multi-segment callers iterate.Raises
RuntimeErrorwhen no segment intersects.- Parameters:
begin (datetime)
end (datetime)
pad (bool)
- Return type:
List[RawWindow]
- read(begin_time, end_time, min_ch=0, max_ch=None, fill_gap=True, with_factor=True)[source]¶
Read a concatenated DASdata covering [begin, end).
Vendor-agnostic time-range query. Walks every segment that overlaps the window; for each intersecting row the exact
first_sampleoffset andn_samplescount are computed from the row’s begin_time / fs / nt, soDASFile.readreturns only the samples that fall inside the window — no post-read trim.If the window straddles acquisition gaps:
fill_gap=True(default) — each inter-segment gap is
filled with zeros so the returned data has a uniformly- spaced time axis running from the first kept sample onward. *
fill_gap=False— gaps are dropped and the output is shorter than(end - begin) x fs.end_timereports the last sample’s true timestamp, so callers can detect the shortening.with_factor=True(default) attaches the raw->physical conversion asphysical_factor, so.to_physical()reaches microstrain without a re-read. It is not applied here: OptaSense counts must be `unwrap`ed on the concatenated array first, and scaling before that would put the 2**32 rails at unrecognizable values.Raises
RuntimeErrorif no files intersect the window.- Parameters:
begin_time (datetime)
end_time (datetime)
min_ch (int)
max_ch (int | None)
fill_gap (bool)
with_factor (bool)
- Return type:
- dasio.dasdb.main(argv=None)[source]¶
python -m dasio.dasdb— build or update a DASdb catalog.Format dispatch is by file extension: .parquet/.parq writes parquet, .csv/.txt/no-suffix writes legacy whitespace CSV. Default behaviour is incremental: if the target file already exists we load it and only open the HDF5 files that are new on disk. Pass –overwrite to force a full rescan.
- One-shot format conversion of an existing catalog is a one-liner:
- python -c “from dasio.dasdb import DASdb;
DASdb.from_file(‘old.csv’, format=’ASN’).to_file(‘new.parquet’)”
dasio.dasinfo¶
Channel-metadata catalog for one snapshot of an instrument configuration.
DASinfo wraps a pandas DataFrame indexed by index_raw (the row position
in the reader’s raw (nx_raw, nt) array). Each row has:
fiber 'n' / 's' / 'c' / future 'e' / 'w'. 'u' marks an unknown
fiber (NaN rows in the CSV are mapped to 'u'). Single-
fiber rigs default to 'n' when the input CSV lacks a
fiber column.
taptest 1 if a taptest survey produced a geolocation, else 0.
Defaults to 1 when the CSV lacks the column.
quality 1 if the located channel is currently good for downstream
processing, 0 if known noisy / muted. Only meaningful
when taptest == 1; forced to 0 when taptest == 0.
Defaults to 1 when the CSV lacks the column.
index_taptest Sequential integer per fiber within taptest==1 rows.
NA when taptest == 0. Preserved verbatim if the CSV
already supplies the column; otherwise computed.
lat, lon WGS84 from the taptest survey. NaN when taptest == 0.
Subset operations (by_fiber, located, active) preserve index_raw and
index_taptest — they shrink the row count without renumbering.
For cross-instrument-config durability, the join key on located channels is
the composite (fiber, index_taptest) — frozen at survey time. index_raw
shifts when fiber is added or removed on the instrument side.
- class dasio.dasinfo.DASinfo(df)[source]¶
Immutable channel catalog:
DASinfo(df)orDASinfo.from_csv(path).Any channel table is normalized into the canonical schema on construction.
select_taptestneedsindex_rawandtaptest;plot,coord_lookupandprojection_originneedlat(orlatitude) andlon(orlongitude). Columns defaulted when absent:fiber defaults to ‘n’. taptest defaults to 1. quality defaults to 1. Forced to 0 wherever taptest==0. index_raw defaults to row position (0..N-1). index_taptest computed per-fiber starting at 0 for
taptest==1 rows when missing.
Rename aliases (source column dropped after rename):
index -> index_raw ichan_before_taptest -> index_raw ichan_after_taptest -> index_taptest taptest_channel_index -> index_taptest status -> taptest (string ‘good’ / ‘bad’
converted to 1 / 0)
- spiky -> quality (polarity-flipped:
spiky=0 -> quality=1; spiky=1 -> 0)
latitude -> lat longitude -> lon elevation -> ele azimuth -> azi dipping angle -> dip
Production LF / EQ deployment CSVs (Iceland + Santorini) load directly through these aliases — no CSV mutation needed before consumers migrate to
DASinfo.from_csv.- Parameters:
df (DataFrame)
- by_fiber(letter)[source]¶
Rows where
fiber == letter. Empty result is allowed.- Parameters:
letter (str)
- Return type:
- active()[source]¶
Rows where
taptest == 1 AND quality == 1. The set fed to the picker / locator in production.- Return type:
- property index_raw: ndarray¶
index_rawvalues for the current rows, as a numpy int64 array.Use to slice the reader’s
(nx_raw, nt)output array down to this DASinfo’s subset:>>> d_raw = reader(path) # shape (nx_raw, nt) >>> d = d_raw[dasinfo.active().index_raw, :] >>> # row k of d corresponds to dasinfo.active().df.index[k]
- coord_lookup()[source]¶
lat/loncolumns indexed byindex_raw. Suitable for joining picks (which carryindex_raw) to coordinates.- Return type:
DataFrame
- property projection_origin: tuple[float, float]¶
Mean (lat, lon) over located channels. Raises if none are located.
- plot(*, every=500, index='taptest', ax=None, **kwargs)[source]¶
Scatter-plot every Nth located channel on a (lon, lat) map.
- Parameters:
500. (every stride. Default)
'taptest' (index) – fiber; selected channels are located by construction. ‘raw’ strides
index_rawand skips rows without a geolocation (taptest == 0).None. (ax matplotlib Axes to draw on; created if)
marker (**kwargs forwarded to ax.scatter; defaults to red) – with black edge.
every (int)
index (str)
Annotation labels are
<fiber><index_taptest>for taptest mode and the bare<index_raw>integer for raw mode. Returns (fig, ax).
dasio.processing¶
High-level DAS processing — DASdata in, DASdata out.
Thin wrappers around the numeric kernels in signal.py that preserve
the DASdata envelope (fs, dt, nx, nt, dx, begin_time, end_time,
gauge_length_m, format, raw_meta) so pipelines read naturally:
d = read_das_data(path, ‘Proc’) d = bandpass(d, 0.01, 0.4) d = integrate(d)
Each function returns a new DASdata via dataclasses.replace. Pass
copy=False when you know the input’s data can be thrown away — the
kernels themselves return fresh arrays either way, but copy=True
(the default) guards against later aliasing surprises when the
caller mutates the input in place.
- dasio.processing.bandpass(d, fmin, fmax, order=14, zerophase=True, copy=True, nthreads=None)[source]¶
Butterworth bandpass along the time axis.
nthreadssets the channel-parallel OpenMP fan-out in the C++ kernel.None(default) resolves todefault_nthreads()— the kernel’s own default is 1 thread, which leaves most of the machine idle on a filter that is embarrassingly parallel over channels. Pass an explicit integer to throttle on a shared host, or when calling this from inside worker processes that already partition the cores (asdesampledoes).
- dasio.processing.detrend(d, copy=True)[source]¶
Subtract a per-channel linear trend along the time axis.
Standard pre-processing step before bandpass — removes any DC offset and slow drift that would otherwise leak into the filter transition band. Mirrors legacy DASutils.detrend_2D.
- dasio.processing.taper(d, alpha=0.4, copy=True)[source]¶
Tukey edge taper along the time axis.
alphais the fraction of the window covered by the cosine transition at each end (0 = no taper, 1 = full Hann). Default 0.4 matches legacy DASutils.readFile_HDF. Apply betweendetrendandbandpassto suppress filter ringing at the segment boundaries.
- dasio.processing.differentiate(d, copy=True, method='central')[source]¶
Differentiate along the time axis (d/dt).
method="central"(default) uses the second-order central difference (signal.gradient_time, bit-identical tonp.gradient)."backward"uses the first-order backward difference with the first sample forced to zero (signal.diff_time, matching DASutils.preprocess_diff).
- dasio.processing.integrate(d, copy=True)[source]¶
Integrate along the time axis (cumulative sum × dt).
First time sample is forced to zero so the trace is pinned to the window’s start — otherwise a DC offset in
d.datawould run off linearly and dominate the result.
- dasio.processing.subtract_common_mode(d, ch_min=0, ch_max=None, copy=True)[source]¶
Per-time-sample common-mode noise subtraction across channels.
For each time sample we take the median across channels [ch_min, ch_max) and subtract it from every channel of
d. The slice bounds let callers estimate from a quiet stretch of fiber rather than the whole array — biases the median toward background rather than active signal. Default bounds use all channels (matches legacy DASutils.preprocess_medfilt).
- dasio.processing.unwrap_int32(d, factor=1, copy=True)[source]¶
Unwrap int32 rollover along the time axis.
Needed for raw OptaSense phase counts; a no-op for data that’s already strain.
factorscales the 2**32 wrap increment (default 1, matching standard OptaSense int32).Thin wrapper over
signal.unwrap_int32, which works in place;copy=True(default) protects the caller’s array,copy=Falseunwrapsd.datawhere it lies.
- dasio.processing.downsample(d, factor, anti_alias=True, order=8, zerophase=True, copy=True, nthreads=None)[source]¶
Integer-factor downsample along time: anti-alias low-pass, then stride.
The analysis-grade counterpart to
DASdata.skip_t, which is a bare stride and therefore aliases. Withanti_alias=True(default) a zero-phase Butterworth low-pass atfs / (2.5 * factor)is applied first, reusing the C++ filter — a band-pass with the high-pass corner disabled (freqmin=0) is a pure low-pass. That cutoff is 0.8x the new Nyquist (fs / factor / 2), the same 2.5x-oversampling guard band asdesample.desample_window, so the order-orderButterworth has room for its transition roll-off before aliased energy can fold back.fs,dt,ntandend_timeare then updated by reusingskip_t;begin_timeandt0_secare unchanged (sample 0 is kept).Set
anti_alias=Falseto skip the filter when the data is already band-limited below the new Nyquist (e.g. you just band-passed) — then this is exactlyskip_tin the same call.factor <= 1is a no-op. The C++ filter needs float32/float64 data, likebandpass.nthreadsis forwarded to the anti-alias filter; seebandpass.
- dasio.processing.median_filter_1d(d, kernel_size, axis='t', copy=True)[source]¶
Running median along time (
axis='t') or channels (axis='x').Suppresses spikes narrower than the kernel while leaving genuine edges intact — the thing a band-pass cannot do, since a band-pass smears an impulse across its whole passband instead of removing it.
kernel_sizemust be odd. Seesignal.median_filter_1d.
dasio.plot¶
Minimal plotting helpers for DASdata.
Two free functions (imshow, wiggle) and a bound _PlotAccessor
exposed via DASdata.plot. Matplotlib is imported lazily inside each
function body so a bare import dasio (or import dasdata) stays
free of the matplotlib startup cost — the load only fires when an
actual plot is requested.
plot_xcorr images a cross-correlation gather (the cc array of a
dasio.noise.CCGather); CCGather.plot() is the convenience wrapper
around it. plot_spectrogram images the (freq, times, S) result of
psd.spectrogram / psd.spectrogram_cwt, and plot_fk / plot_fc
the f-k and dispersion panels from dasio.fk.
- dasio.plot.imshow(d, ax=None, ch_range=None, t_range=None, skip_ch=1, skip_t=1, style='seismic', usedatetime=False, perc=99.5, vmin=None, vmax=None, cmap='seismic', cbar=True, figsize=None, **kwargs)[source]¶
Color-mapped image of a
DASdata(regular grid).- Parameters:
d (DASdata)
ax (matplotlib Axes, optional) – Plot target; one is created if not given.
ch_range (tuples, optional) – Forwarded to
DASdata.truncatefor the displayed window. For an arbitrary channel subset, pre-select withd.select(ch_index=...)and plot the result.t_range (tuples, optional) – Forwarded to
DASdata.truncatefor the displayed window. For an arbitrary channel subset, pre-select withd.select(ch_index=...)and plot the result.skip_ch (int, default 1) – Plot only every
skip_ch-th channel /skip_t-th time sample — a coarser, faster raster for big arrays (e.g.skip_t=5on a wide window). ReusesDASdata.skip_ch/.skip_t. The percentile and the image both run on the decimated array, so this genuinely speeds the plot up; the channel/time axes still span the full range.1(default) draws every sample.skip_t (int, default 1) – Plot only every
skip_ch-th channel /skip_t-th time sample — a coarser, faster raster for big arrays (e.g.skip_t=5on a wide window). ReusesDASdata.skip_ch/.skip_t. The percentile and the image both run on the decimated array, so this genuinely speeds the plot up; the channel/time axes still span the full range.1(default) draws every sample.style ({'seismic', 'normal'}) –
'seismic'(default, matches legacy DASutils) puts channels on the x-axis and time on the y-axis with time growing downward — the traditional seismic-record orientation.'normal'puts time on the x-axis and channels on the y-axis — easier to read as a stack of time-series.usedatetime (bool) – Show the time axis as
datetime64ticks (True) instead of seconds-from-t0_sec(False, default).perc (float) – Percentile (of
|data|) used to set symmetricvmin/vmaxwhen the latter aren’t provided. Default 99.5 trims out the loudest 0.5 % so background noise stays visible.vmin (floats, optional) – Manual color-scale bounds; bypass
percwhen both are given.vmax (floats, optional) – Manual color-scale bounds; bypass
percwhen both are given.cmap (str or Colormap) – Default
'seismic'(red-white-blue), matches legacy DASutils.cbar (bool)
figsize ((w, h), optional) – Figure size when
axis created here. Defaults to (8, 4) forstyle='seismic'and (4, 8) forstyle='normal'. Ignored whenaxis supplied.**kwargs (forwarded to
ax.imshow)
- Return type:
(ax, im) so callers can attach annotations / set titles / etc.
- dasio.plot.wiggle(d, ax=None, ch_range=None, t_range=None, n_max_ch=100, max_nt=5000, style='seismic', scale=1.0, color='k', lw=0.6, usedatetime=False, normalize=True, figsize=None, **kwargs)[source]¶
Per-channel wiggle traces of a
DASdata.Each retained channel becomes a line offset by its (original) channel index, optionally amplitude-normalized so all traces share the same visual range.
style='seismic'(default) places channels on the x-axis and time on the y-axis growing downward — the traditional seismic record orientation.style='normal'places time on the x-axis and channel on the y-axis (descending), which reads more like a stack of time-series.n_max_chcaps the number of traces drawn — whensub.nx > n_max_chthe channels are decimated with strideceil(sub.nx / n_max_ch). Defaults to 100; passNoneto draw every channel. This stops a casuald.plot.wiggle()on a full-array DASdata from rendering thousands of overlapping lines. Usech_rangefirst if you want a specific window.max_ntcaps the number of time samples drawn per trace — whensub.nt > max_ntthe time axis is strided byceil(sub.nt / max_nt). Wiggle render cost scales withn_traces * n_samples, and at screen resolution a few thousand points per trace is already pixel-lossless, so this is the main speed lever on wide time windows. Defaults to 5000; passNoneto draw every sample.- Parameters:
ch_range (Tuple[int, int] | None)
t_range (Tuple | None)
n_max_ch (int | None)
max_nt (int | None)
style (str)
scale (float)
color (str)
lw (float)
usedatetime (bool)
normalize (bool)
figsize (Tuple[float, float] | None)
- dasio.plot.plot_xcorr(cc, lag_s=None, fs=None, offsets=None, ax=None, perc=99.0, vmin=None, vmax=None, cmap='seismic', cbar=True, figsize=None, **kwargs)[source]¶
Image a cross-correlation gather — pair (x) vs lag time (y, downward).
Plots the
(npair, nlag)array returned bydasio.noise.xcorr_stack(the first tuple element) orxcorr_dataset(result['cc']): each column is one channel pair’s stacked correlation, zero lag at the centre row. Lag time runs down the vertical axis (seismic-gather convention) and pairs along the horizontal axis, so a common-shot gather shows the virtual-source moveout fanning out symmetrically in +/- lag.- Parameters:
cc (
(npair, nlag)array.)lag_s (lag axis in seconds (e.g.
xcorr_dataset’sresult['lag_s']).) – If None it is built fromfs(or falls back to sample indices), with zero lag at the centre. Drawn on the vertical axis, increasing downward.fs (sample rate, used to label the lag axis in seconds when
lag_sis None.)offsets (optional per-pair x values (e.g. inter-channel offset in km for a) – common-offset gather); defaults to the pair index.
perc (as in
imshow—percsets) – symmetric colour limits from theperc-th percentile of|cc|unlessvmin/vmaxare given.vmin (as in
imshow—percsets) – symmetric colour limits from theperc-th percentile of|cc|unlessvmin/vmaxare given.vmax (as in
imshow—percsets) – symmetric colour limits from theperc-th percentile of|cc|unlessvmin/vmaxare given.cmap (as in
imshow—percsets) – symmetric colour limits from theperc-th percentile of|cc|unlessvmin/vmaxare given.cbar (as in
imshow—percsets) – symmetric colour limits from theperc-th percentile of|cc|unlessvmin/vmaxare given.figsize (as in
imshow—percsets) – symmetric colour limits from theperc-th percentile of|cc|unlessvmin/vmaxare given.**kwargs (forwarded to
ax.imshow.)``(ax (Returns) –
im)``.
- dasio.plot.plot_spectrogram(S, freq=None, times=None, ich=0, t0=None, usedatetime=None, ax=None, log_color=True, period_axis=None, perc=99.5, vmin=None, vmax=None, cmap='turbo', cbar=True, units='', figsize=None, **kwargs)[source]¶
Image a time-resolved PSD from
psd.spectrogramorpsd.spectrogram_cwt.Data first, like the other plotters here — either the whole result or the array plus its axes:
plot_spectrogram(spectrogram_cwt(d.data, d.fs, 1/200, 0.5)) plot_spectrogram(S, freq, times, t0=d.start_time)
- Parameters:
S (
(nchan, nfreq, ntime)or(nfreq, ntime)array, or the whole) –(freq, times, S)tuple those functions return (accepted as-is whenfreq/timesare not given).freq (the frequency (Hz) and time (s) axes; taken from the result) – tuple, or bin/sample indices when absent.
times (the frequency (Hz) and time (s) axes; taken from the result) – tuple, or bin/sample indices when absent.
ich (channel to draw when
Sis 3-D.)t0 (optional absolute start time —
np.datetime64, datetime, ISO) – string, or unix seconds. Turns the x-axis into calendar time.usedatetime (calendar-time x-axis, as in
imshow. Default None means) – “whenevert0is given”. True withoutt0raises —timesholds seconds from the record start, so the epoch must come from somewhere. False keeps the seconds axis even whent0is passed.log_color (color by
log10(S)(default) rather than linear PSD.)period_axis (log-frequency axis labeled in seconds of period. Default) – None auto-enables it for a log-spaced grid (
spectrogram_cwt) and leaves a linear grid (spectrogram) on a linear frequency axis.perc (color limits; unless given, the
perc-th and) –(100-perc)-th percentiles of the finite color values.vmin (color limits; unless given, the
perc-th and) –(100-perc)-th percentiles of the finite color values.vmax (color limits; unless given, the
perc-th and) –(100-perc)-th percentiles of the finite color values.cmap (as in
imshow.)cbar (as in
imshow.)figsize (as in
imshow.)units (physical unit of the data (e.g.
'Pa') for the colorbar label,) – shown as units^2/Hz.**kwargs (forwarded to
ax.pcolormesh(rasterizeddefaults to True —) – thousands of quads otherwise swamp vector backends).— (NaN cells — e.g. the cone of influence that spectrogram_cwt masks)
``(ax (are left blank. Returns) –
im)``.
- dasio.plot.plot_fk(F, f=None, k=None, ax=None, db=True, perc=100.0, vmin=None, vmax=None, scaling=None, cmap='turbo', cbar=True, velocities=None, figsize=None, **kwargs)[source]¶
Image the f-k plane from
dasio.fk.fk. :param F: f-k amplitude fromfk, or the whole(F, f, k)result. :param f: frequency axis [Hz]; taken fromFwhen it is the whole result. :param k: wavenumber axis [rad/m]; likewise. :param ax: axes to draw on; a new figure is made when None. :param db: scale to dB re the panel maximum. :param perc: percentile for the upper colour limit. :param vmin: lower colour limit; defaults to 60 dB belowvmax. :param vmax: upper colour limit. :param scaling: what the panel is, matching howfkproduced it — the sametoken in both places. None (default) is a raw complex amplitude: dB is taken relative to the panel maximum, because the absolute level of a raw transform has no meaning (it moves with the sample count), and the consequence is that two panels cannot be compared with each other. ‘density’ is what
fk(..., scaling='density')returns: a power, so 10*log10 against one unit, absolute and comparable between panels.- Parameters:
cmap (str) – colormap name.
cbar (bool) – draw the colour bar.
velocities –
c = 2*pi*f/kguide lines [m/s] — positive values draw both directions, signed values draw one.figsize (Tuple[float, float] | None) – figure size when
axis None.**kwargs – forwarded to
pcolormesh.db (bool)
perc (float)
vmin (float | None)
vmax (float | None)
scaling (str | None)
- Returns:
the axes drawn on. im: the
QuadMesh, for a colour bar or limit tweak.- Return type:
ax
- Raises:
ValueError – if
Fis not 2-D or the axes do not match it.
Data first, like the other plotters here — either the whole result or the array plus its axes:
plot_fk(*fk(d.data, d.dx, d.dt)) plot_fk(F, f, k, velocities=[1500, 3000])
Frequency runs along x and wavenumber along y, so the apparent-velocity fans open sideways and a dispersive arrival reads left to right.
dbscales relative to the panel maximum, since an f-k amplitude inherits the gather’s units and only its contrast means anything.
- dasio.plot.plot_fc(A, f=None, c=None, ax=None, db=False, perc=100.0, vmin=None, vmax=None, scaling=None, cmap='turbo', cbar=True, picks=None, figsize=None, **kwargs)[source]¶
Image the dispersion panel from
dasio.fk.fk2fc. :param A: dispersion panel fromfk2fc, or the whole(A, f, c)result. :param f: frequency axis [Hz]; taken fromAwhen it is the whole result. :param c: velocity axis [m/s]; likewise. :param ax: axes to draw on; a new figure is made when None. :param db: scale to dB re the panel maximum. :param perc: percentile for the upper colour limit. :param vmin: lower colour limit. :param vmax: upper colour limit. :param scaling: what the panel is, as inplot_fk; pass ‘density’ for onebuilt from
fk(..., scaling='density').- Parameters:
cmap (str) – colormap name.
cbar (bool) – draw the colour bar.
picks –
(f, c)sequences to overlay, e.g. a picked dispersion curve.figsize (Tuple[float, float] | None) – figure size when
axis None.**kwargs – forwarded to
pcolormesh.db (bool)
perc (float)
vmin (float | None)
vmax (float | None)
scaling (str | None)
- Returns:
the axes drawn on. im: the
QuadMesh, for a colour bar or limit tweak.- Return type:
ax
- Raises:
ValueError – if
Ais not 2-D or the axes do not match it.plot_fc(*fk2fc(F, f, k, 200, 5000)) –
Frequency on x, apparent velocity on y — the orientation dispersion curves are picked in. Linear colour by default: a dispersion image is read by where its ridge is, and dB flattens the contrast that makes it stand out.
- dasio.plot.plot_timeline(db, ax=None, by_month=None)[source]¶
Gantt-style coverage timeline for a
DASdb.Green where data exists, red for the acquisition gaps between.
With
by_month=Trueeach calendar month becomes its own row against a shared day-of-month axis, which keeps a multi-month archive legible — a year on one continuous axis compresses every gap into a hairline.None(default) picks that layout automatically when the catalog spans more than one month.Bar geometry is computed in matplotlib date numbers rather than handed over as Timestamp/Timedelta objects. Mixing the two lets the unit machinery interpret a width in the same frame as a position, and a single bad conversion is enough to push the axes limits far enough that rendering a
bbox_inches='tight'figure asks for a canvas of millions of pixels.
dasio.noise¶
Ambient-noise cross-correlation for DAS.
preprocess runs the standard CC preparation chain on a freshly loaded
(continuous, unprocessed) DASdata — differentiate, detrend, band-pass,
decimate, common-mode removal, temporal normalization — reusing the core dasio
ops. temporal_normalization and spectral_whitening are the individual
amplitude-flattening steps; xcorr is the FFT cross-correlation.
The channel-pair gathers (common_shot_pairs, common_offset_pairs) build the
(ch1, ch2) index arrays that say which channels to correlate.
xcorr_stack cross-correlates those pairs over the 60-s-style segments of
one DASdata and stacks; xcorr_dataset walks a whole DASdb,
preprocessing and stacking every segment. Both return a CCGather — the
(npair, lag) stacked correlation plus its lag axis, with a .plot() method.
PyTorch is an optional dependency (pip install 'dasio[noise]'); it is
imported lazily inside xcorr / spectral_whitening, so preprocess,
temporal_normalization and the gather builders (all CPU) work without it.
- dasio.noise.preprocess(d, *, differentiate=True, detrend=True, diff_method='central', freqmin=None, freqmax=None, taper_alpha=None, order=14, zerophase=True, decimate=1, subtract_common_mode=True, norm_window_sec=None)[source]¶
Standard ambient-noise CC preparation for a raw-loaded
DASdata.Runs the canonical chain on continuous data straight off the reader and returns a fresh
DASdata: differentiate -> detrend -> (taper ->) band-pass -> decimate -> common-mode removal -> temporal normalization. Each step reuses an existing dasio op; compose them yourself for a non-standard recipe.- differentiatestrain -> strain rate (the usual CC quantity). Turn off if
the data is already a rate.
- diff_method“central” (default, second-order central difference, bit-
identical to
np.gradient) or “backward” (first-order, first sample zeroed). Only used whendifferentiateis True.
freqmin, freqmax : band-pass corners in Hz; skipped unless both are given. taper_alpha : Tukey edge-taper fraction applied just before the band-pass
(None skips it). Matches the legacy
filter(data) = tukey * data -> butterrecipe; e.g. 0.05 tapers 2.5 % at each end before filtering.- order, zerophaseButterworth band-pass order and zero-phase flag (order 4
zerophase=Truereproduces abutter(4)+filtfiltpass).
- decimateinteger time-decimation factor, applied as a plain stride AFTER
the band-pass — the band-pass is the anti-alias filter, so choose
freqmax < fs / (2 * decimate). 1 (default) means no decimation.- norm_window_sectemporal normalization. None skips it; 0 = one-bit;
>0 = running-absolute-mean window in seconds.
detrend and common-mode removal are always applied — they are part of the standard recipe. CPU only (no PyTorch needed; that’s just
xcorr).
- dasio.noise.temporal_normalization(d, window_sec=None)[source]¶
Per-channel temporal amplitude normalization.
window_secNone or 0 -> one-bit (the sign of each sample). Otherwise running-absolute-mean: divide each sample by a moving average of|amplitude|overwindow_secseconds (a common choice is ~half the longest period of interest). Flattens the amplitude envelope so transients don’t dominate the cross-correlation. Returns a freshDASdata.
- dasio.noise.spectral_whitening(spec, smooth_bins=1, eps=0.001, band=None, df=None)[source]¶
Flatten a spectrum’s amplitude while keeping its phase (PyTorch).
smooth_bins == 1is phase-only whitening: divide by|spec|in every bin.smooth_bins > 1is running-absolute-mean (RAM) whitening: divide by a moving average of|spec|over that many frequency bins, which keeps the broad spectral shape that phase-only erases. The denominator is floored atepsx the row’s mean magnitude so empty (out-of-band) bins stay ~0 instead of blowing up.- bandoptional
(f1, f2)Hz pass-band. After whitening, bins below f1and abovef2are smoothly suppressed with acos**2edge taper (1 at the band edge, 0 at DC / Nyquist), reproducing the legacy spectral_whitening band-limit so whitening doesn’t re-flatten out-of-band noise. Needsdf(the rFFT bin spacing,fs / nfft).
specis a torch rFFT tensor (last axis = frequency); returns a tensor.- bandoptional
- dasio.noise.xcorr(a, b, *, whiten=False, fs=None, max_lag=None, eps=0.001, whiten_band=None, device=None)[source]¶
FFT cross-correlation along the time axis (PyTorch).
Correlates
aandbper channel.ais(nch, npts);bis either the same shape (channel-wise) or a single(npts,)trace, broadcast against every channel ofa(a virtual-source gather). Linear (zero-padded, not circular) correlation; the lag axis is centered, so columnmax_lag— ornpts-1whenmax_lagis None — is zero lag.- whitenspectral whitening before correlating.
False-> none;True -> phase-only; a float -> running-absolute-mean whitening with that smoothing bandwidth in Hz (requires
fs). Seespectral_whitening. Assumes inputs are already band-limited (bandpass first).- fssampling rate, needed when
whitenis a Hz bandwidth orwhiten_band is set.
- whiten_bandoptional
(f1, f2)Hz pass-band for the whitening (needs fs); applies the legacycos**2band-limit so whitening doesn’t re-flatten out-of-band noise. Seespectral_whitening.- max_lagkeep only +/- this many lag samples (returns
2*max_lag+1); None returns the full
2*npts-1.
eps : whitening floor, as a fraction of each row’s mean magnitude. device : torch device for the FFTs (e.g.
'cuda'); None uses the inputs’.Returns a numpy array
(nch, 2*npts-1)or(nch, 2*max_lag+1).- whitenspectral whitening before correlating.
- dasio.noise.haversine_km(lat1, lon1, lat2, lon2)[source]¶
Great-circle distance in km between two lon/lat points (vectorized).
A lightweight stand-in for
obspy.geodetics— spherical-earth haversine, accurate to a few tenths of a percent, which is plenty for picking common-offset channel pairs. All four arguments broadcast, so you can pass one point against an array of channels (haversine_km(lat[i], lon[i], lat, lon)) to get that channel’s distance to every other.
- dasio.noise.common_shot_pairs(source, channels)[source]¶
Pair one source channel against every channel — a common-shot gather.
sourceis the fixed source channel index.channelsis the list of receiver indices, or an intnmeaningrange(n). Returns(ch1, ch2)integer arrays withch1filled withsource, suitable forxcorr_stack/xcorr_dataset.
- dasio.noise.common_offset_pairs(lat, lon, offset_km, tol_km=None)[source]¶
Channel pairs separated by ~`offset_km` — a common-offset gather.
For each channel
i, find the forward channelj > iwhose great-circle distance toiis closest tooffset_km(viahaversine_km), and keep the pair when that distance is withintol_km. Forward-only search keeps a consistent pair orientation and avoids duplicate (i, j)/(j, i).lat/lonare per-channel coordinate arrays (degrees).tol_kmNone keeps every channel’s nearest-to-offset partner — including channels near the far end that have no true match; pass a tolerance (e.g. half the channel spacing) to drop those. Returns(ch1, ch2).
- class dasio.noise.CCGather(cc, lag_s, fs, nseg)[source]¶
A stacked cross-correlation gather — what
xcorr_stack/xcorr_datasetreturn.ccis the(npair, nlag)mean cross-correlation overnsegstacked segments, zero lag at the centre column;lag_sis the matching lag axis in seconds andfsthe (post-preprocess) sample rate.plot()images it viadasio.plot.plot_xcorr— the same DASdata-style “the object knows how to draw itself” convenience, so callers don’t juggleccandlag_sseparately.- Parameters:
cc (ndarray)
lag_s (ndarray)
fs (float)
nseg (int)
- dasio.noise.xcorr_stack(d, ch1, ch2, *, seg_sec, max_lag_sec, whiten=False, whiten_band=None, eps=0.001, chunk=1250, device=None)[source]¶
Cross-correlate channel pairs over
seg_secsegments and stack.Splits
dinto back-to-back windows ofseg_secseconds (the trailing remainder is dropped), cross-correlates each pair’s matching segments withxcorr, and averages over the segments — the segment stack.ch1/ch2are equal-length channel-index arrays (e.g. fromcommon_shot_pairs); they index the channel axis ofd.data.- seg_sec, max_lag_secsegment length and kept lag half-width, in seconds;
converted to samples with
d.fs, so this is decimation-agnostic.- whiten, whiten_bandforwarded to
xcorr(whitening mode and optional (f1, f2)Hz band-limit).
chunk : process this many pairs per
xcorrcall to bound memory.Returns a
CCGather(mean cross-correlation over itsnsegsegments), orNonewhendis shorter than one segment.xcorr_datasetweights these bynsegto accumulate across windows.- Parameters:
d (DASdata)
seg_sec (float)
max_lag_sec (float)
eps (float)
chunk (int)
- dasio.noise.xcorr_dataset(db, ch1, ch2, *, max_lag_sec, seg_sec=60.0, begin=None, end=None, window_sec=3600.0, min_ch=0, max_ch=None, prep=None, whiten=False, whiten_band=None, eps=0.001, chunk=1250, device=None)[source]¶
Cross-correlate channel pairs across a whole
DASdband stack.Walks the catalog’s time-contiguous
segments, reads each segment in continuouswindow_secchunks (DASdb.read), runspreprocesson each chunk, thenxcorr_stack, accumulating the per-segment sums. The final result is the mean cross-correlation over every segment of every chunk — the dataset-level stack.db : a
DASdbcatalog of the raw files to correlate. ch1, ch2 : channel-index pair arrays (e.g. fromcommon_shot_pairs/common_offset_pairs); they index the read range[min_ch, max_ch)(default = all channels, so indices are absolute). Channel selection is the caller’s job — restrict the read withmin_ch/max_chand only reference good channels inch1/ch2(bad channels read but unpaired).min_ch, max_ch : contiguous channel range to read each window. begin, end : tz-aware UTC datetimes bounding the run (default = full
catalog span). Group by day by calling once per day’s [begin, end).
- window_seccontinuous read-chunk length; a multiple of
seg_secwastes no samples at chunk boundaries. Iterating per segment means a segment never straddles an acquisition gap.
- prepkwargs forwarded to
preprocess(e.g. ``dict(freqmin=0.1, freqmax=20, decimate=5, norm_window_sec=0)``);
{}runs thepreprocessdefaults.- whiten, whiten_band, eps, chunk, deviceforwarded down to
xcorrvia xcorr_stack.
Returns a
CCGatherwhoseccis the mean cross-correlation over thensegsegments of every window. Raises if no data falls in the requested range. Assumes a uniform sample rate across the catalog.- Parameters:
max_lag_sec (float)
seg_sec (float)
window_sec (float)
min_ch (int)
max_ch (int | None)
prep (dict | None)
eps (float)
chunk (int)
- window_seccontinuous read-chunk length; a multiple of
dasio.picking¶
PhaseNet-DAS P/S phase picking. phasenet (AI4EPS) and torch are optional and
imported lazily: pip install 'dasio[pick]'.
- class dasio.picking.Picks(df: object, fs: float, model: str, begin_time: datetime.datetime | None, nx: int, nt: int, t0_sec: float = 0.0, scores: numpy.ndarray | None = None)[source]¶
- Parameters:
df (object)
fs (float)
model (str)
begin_time (datetime | None)
nx (int)
nt (int)
t0_sec (float)
scores (ndarray | None)
dasio.synthetic¶
A synthetic record, so a snippet or a notebook has something to run on.
Built in the order the physics happens: a wavefield along the fibre,
differentiated into strain, then what the interrogator adds on top. Strain,
not strain rate: that is what the OptaSense archive stores, and
differentiate() is one call away.
- dasio.synthetic.boussinesq_strain(x, y, z, load, shear=80000000.0, nu=0.3)[source]¶
Strain along x at (x, y, z) below a vertical point
loadon the surface of a half-space: d/dx of Boussinesq’s horizontal displacement. Checked against the numerical derivative to 1e-6.
- dasio.synthetic.scatter_coda(x, src, t0, fs, nt, vp, vs, fc, rng, a=40.0, kappa=0.3, cell=25.0, v_slow=500.0, q=150.0)[source]¶
Coda by single (Born) scattering off a random near-surface medium.
The medium is a velocity perturbation on a grid with a von Karman autocorrelation: length
a, Hurst exponentkappa. Its power spectrum,(1 + k^2 a^2)^-(kappa+1), is what sets how strongly each wavelength scatters, and the power-law tail is why the crust scatters at every scale where a Gaussian medium would go quiet. Every cell scatters the incident wave once – no multiple scattering, which is what Born means.Every arrival is the same wavelet at a different delay, so the delays go into a spike train and one convolution serves every channel: cells x nx x nt wavelet evaluations become cells x nx additions and one FFT.
- dasio.synthetic.demo_record(nx=300, nt=6000, fs=100.0, dx=10.0, seed=0)[source]¶
A roadside fibre: a local earthquake with its coda, three vehicles, and the noise a real interrogator writes underneath.
The defaults are a compromise the signals force. P and S only separate if the source is far enough away (2.4 km: four wavelet periods apart), the hyperbolas only curve if the array is long enough to see it, and a car at 25 m/s needs a minute to cross any of that – so 3 km of fibre and 60 s.
Amplitudes are physical: peak S strain for the quake, axle weights on a road bed for the cars, an rms for the noise, all in microstrain.
- Return type: