Skip to content

aimbat.io

I/O for AIMBAT data sources.

Data source modules plug in by decorating their functions with the decorator factories from this package (station_creator, event_creator, seismogram_creator, source_records_reader, seismogram_data_reader, seismogram_data_writer). Not every source needs to implement everything. A source that only provides waveform data would register a reader and writer but skip the creator functions, and one whose station, event and seismogram all come out of a single read adds a source_records_reader so ingestion reads it once. A source is identified by an opaque sourcename string whose meaning is defined by the registered callbacks; for file-based sources it is a filesystem path, but non-file sources are free to use any identifier.

SAC (aimbat.io.sac), JSON (aimbat.io.json), and miniSEED (aimbat.io.mseed) data sources are loaded automatically and their capabilities registered on import of this package.

Modules:

Name Description
json

JSON data source support for AIMBAT.

mseed

miniSEED waveform I/O for AIMBAT.

sac

SAC file I/O for AIMBAT.

Classes:

Name Description
DataType

Valid AIMBAT data types.

SeismogramReadContext

What a registered seismogram-data reader is handed for one read.

SourceRecords

The records built from a single read of one data source.

SourceRecordsRequest

Which records one read of a data source is asked to produce.

SourceUnavailableError

A probe could not determine whether a data source is present.

Functions:

Name Description
clear_seismogram_data_cache

Drop every entry from the in-memory waveform read cache.

create_event

Create an AimbatEvent from a data source.

create_seismogram

Create an AimbatSeismogram from a data source.

create_station

Create an AimbatStation from a data source.

event_creator

Decorator that registers a function as an event creator for datatype.

file_source_present

Return whether a filesystem-backed data source is present.

read_seismogram_data

Read seismogram waveform data from a data source.

read_source_records

Build the requested records from a data source, reading it once where possible.

register_event_creator

Register a function that creates an AimbatEvent from a data source.

register_seismogram_creator

Register a function that creates an AimbatSeismogram from a data source.

register_seismogram_data_reader

Register a function that reads seismogram waveform data from a data source.

register_seismogram_data_writer

Register a function that writes seismogram waveform data to a data source.

register_source_probe

Register a function that checks whether a data source is still present.

register_source_records_reader

Register a function that builds several records from one read of a data source.

register_station_creator

Register a function that creates an AimbatStation from a data source.

seismogram_creator

Decorator that registers a function as a seismogram creator for datatype.

seismogram_data_reader

Decorator that registers a function as a seismogram data reader for datatype.

seismogram_data_writer

Decorator that registers a function as a seismogram data writer for datatype.

source_present

Check whether a data source is still present and provides its seismogram.

source_probe

Decorator that registers a function as a source-presence probe for datatype.

source_records_reader

Decorator that registers a function as a source-records reader for datatype.

stage_seismogram_data

Stage a waveform write to flush when session commits.

station_creator

Decorator that registers a function as a station creator for datatype.

supports_event_creation

Return whether datatype has a registered event creator.

supports_seismogram_creation

Return whether datatype has a registered seismogram creator.

supports_seismogram_data_reading

Return whether datatype has a registered seismogram data reader.

supports_seismogram_data_writing

Return whether datatype has a registered seismogram data writer.

supports_source_probe

Return whether datatype has a registered source-presence probe.

supports_source_records_reading

Return whether datatype has a registered source-records reader.

supports_station_creation

Return whether datatype has a registered station creator.

write_seismogram_data

Write seismogram waveform data to a data source.

Attributes:

Name Type Description
DATATYPE_SUFFIXES dict[DataType, list[str]]

.json maps to both JSON_EVENT and JSON_STATION, so a suffix alone

DATATYPE_SUFFIXES module-attribute

DATATYPE_SUFFIXES: dict[DataType, list[str]] = {
    DataType.SAC: [".sac", ".bhz", ".bhn", ".bhe"],
    DataType.JSON_EVENT: [".json"],
    DataType.JSON_STATION: [".json"],
    DataType.MSEED: [".mseed", ".miniseed", ".ms"],
}

.json maps to both JSON_EVENT and JSON_STATION, so a suffix alone never resolves to one DataType - callers must pass an explicit DataType for JSON sources rather than inferring it from this mapping.

DataType

Bases: StrEnum

Valid AIMBAT data types.

Attributes:

Name Type Description
JSON_EVENT

JSON file containing a single seismic event record.

JSON_STATION

JSON file containing a single seismic station record.

MSEED

miniSEED waveform file. Waveform data only - no station or event data.

SAC

SAC (Seismic Analysis Code) waveform file. Provides station, event, and seismogram data.

Source code in src/aimbat/io/_data.py
class DataType(StrEnum):
    """Valid AIMBAT data types."""

    SAC = auto()
    """SAC (Seismic Analysis Code) waveform file. Provides station, event, and seismogram data."""

    JSON_EVENT = auto()
    """JSON file containing a single seismic event record."""

    JSON_STATION = auto()
    """JSON file containing a single seismic station record."""

    MSEED = auto()
    """miniSEED waveform file. Waveform data only - no station or event data."""

JSON_EVENT class-attribute instance-attribute

JSON_EVENT = auto()

JSON file containing a single seismic event record.

JSON_STATION class-attribute instance-attribute

JSON_STATION = auto()

JSON file containing a single seismic station record.

MSEED class-attribute instance-attribute

MSEED = auto()

miniSEED waveform file. Waveform data only - no station or event data.

SAC class-attribute instance-attribute

SAC = auto()

SAC (Seismic Analysis Code) waveform file. Provides station, event, and seismogram data.

SeismogramReadContext dataclass

What a registered seismogram-data reader is handed for one read.

Parameters:

Name Type Description Default
sourcename str
required
datatype DataType
required
session Session | None
None
Source code in src/aimbat/io/_base.py
@dataclass(frozen=True, slots=True)
class SeismogramReadContext:
    """What a registered seismogram-data reader is handed for one read."""

    sourcename: str
    datatype: DataType
    session: Session | None = None

SourceRecords dataclass

The records built from a single read of one data source.

A field is None when it was not asked for, or when the source has nothing to build it from.

Parameters:

Name Type Description Default
station AimbatStation | None
None
event AimbatEvent | None
None
seismogram AimbatSeismogram | None
None
Source code in src/aimbat/io/_base.py
@dataclass(frozen=True, slots=True)
class SourceRecords:
    """The records built from a single read of one data source.

    A field is `None` when it was not asked for, or when the source has
    nothing to build it from.
    """

    station: AimbatStation | None = None
    event: AimbatEvent | None = None
    seismogram: AimbatSeismogram | None = None

SourceRecordsRequest dataclass

Which records one read of a data source is asked to produce.

Parameters:

Name Type Description Default
sourcename str
required
datatype DataType
required
station bool
required
event bool
required
seismogram bool
required
Source code in src/aimbat/io/_base.py
@dataclass(frozen=True, slots=True)
class SourceRecordsRequest:
    """Which records one read of a data source is asked to produce."""

    sourcename: str
    datatype: DataType
    station: bool
    event: bool
    seismogram: bool

SourceUnavailableError

Bases: Exception

A probe could not determine whether a data source is present.

Distinct from a clean "absent": raised when the answer is unknowable right now (an unreadable file, an unreachable mount). aimbat data prune treats this as a hard stop, never as a licence to delete.

Source code in src/aimbat/io/_base.py
class SourceUnavailableError(Exception):
    """A probe could not determine whether a data source is present.

    Distinct from a clean "absent": raised when the answer is unknowable
    right now (an unreadable file, an unreachable mount). `aimbat data prune`
    treats this as a hard stop, never as a licence to delete.
    """

clear_seismogram_data_cache

clear_seismogram_data_cache() -> None

Drop every entry from the in-memory waveform read cache.

Staged (uncommitted) writes in _pending are left intact.

Source code in src/aimbat/io/_base.py
def clear_seismogram_data_cache() -> None:
    """Drop every entry from the in-memory waveform read cache.

    Staged (uncommitted) writes in `_pending` are left intact.
    """
    _cache.clear()

create_event

create_event(
    datasource: str | PathLike[str], datatype: DataType
) -> AimbatEvent

Create an AimbatEvent from a data source.

Parameters:

Name Type Description Default
datasource str | PathLike[str]

Logical source identifier for the data source.

required
datatype DataType

Data type of the source.

required

Returns:

Type Description
AimbatEvent

A new AimbatEvent instance.

Raises:

Type Description
NotImplementedError

If datatype has no registered event creator.

Source code in src/aimbat/io/_base.py
def create_event(datasource: str | PathLike[str], datatype: DataType) -> AimbatEvent:
    """Create an `AimbatEvent` from a data source.

    Args:
        datasource: Logical source identifier for the data source.
        datatype: Data type of the source.

    Returns:
        A new `AimbatEvent` instance.

    Raises:
        NotImplementedError: If `datatype` has no registered event creator.
    """
    logger.debug(f"Creating AimbatEvent from {datasource}.")
    creator = _event_creators.get(datatype)
    if creator is None:
        raise NotImplementedError(f"{datatype} does not support event creation.")
    return creator(datasource)

create_seismogram

create_seismogram(
    datasource: str | PathLike[str], datatype: DataType
) -> AimbatSeismogram

Create an AimbatSeismogram from a data source.

Parameters:

Name Type Description Default
datasource str | PathLike[str]

Logical source identifier for the data source.

required
datatype DataType

Data type of the source.

required

Returns:

Type Description
AimbatSeismogram

A new AimbatSeismogram instance.

Raises:

Type Description
NotImplementedError

If datatype has no registered seismogram creator.

Source code in src/aimbat/io/_base.py
def create_seismogram(
    datasource: str | PathLike[str], datatype: DataType
) -> AimbatSeismogram:
    """Create an `AimbatSeismogram` from a data source.

    Args:
        datasource: Logical source identifier for the data source.
        datatype: Data type of the source.

    Returns:
        A new `AimbatSeismogram` instance.

    Raises:
        NotImplementedError: If `datatype` has no registered seismogram creator.
    """
    logger.debug(f"Creating AimbatSeismogram from {datasource}.")
    creator = _seismogram_creators.get(datatype)
    if creator is None:
        raise NotImplementedError(f"{datatype} does not support seismogram creation.")
    return creator(datasource)

create_station

create_station(
    datasource: str | PathLike[str], datatype: DataType
) -> AimbatStation

Create an AimbatStation from a data source.

Parameters:

Name Type Description Default
datasource str | PathLike[str]

Logical source identifier for the data source.

required
datatype DataType

Data type of the source.

required

Returns:

Type Description
AimbatStation

A new AimbatStation instance.

Raises:

Type Description
NotImplementedError

If datatype has no registered station creator.

Source code in src/aimbat/io/_base.py
def create_station(
    datasource: str | PathLike[str], datatype: DataType
) -> AimbatStation:
    """Create an `AimbatStation` from a data source.

    Args:
        datasource: Logical source identifier for the data source.
        datatype: Data type of the source.

    Returns:
        A new `AimbatStation` instance.

    Raises:
        NotImplementedError: If `datatype` has no registered station creator.
    """
    logger.debug(f"Creating AimbatStation from {datasource}.")
    creator = _station_creators.get(datatype)
    if creator is None:
        raise NotImplementedError(f"{datatype} does not support station creation.")
    return creator(datasource)

event_creator

event_creator(
    datatype: DataType,
) -> Callable[
    [Callable[[str | PathLike[str]], AimbatEvent]],
    Callable[[str | PathLike[str]], AimbatEvent],
]

Decorator that registers a function as an event creator for datatype.

Parameters:

Name Type Description Default
datatype DataType

The data type the decorated function creates events from.

required
Example
@event_creator(DataType.SAC)
def create_event_from_sacfile(sacfile: str | PathLike[str]) -> AimbatEvent: ...
Source code in src/aimbat/io/_base.py
def event_creator(
    datatype: DataType,
) -> Callable[
    [Callable[[str | PathLike[str]], AimbatEvent]],
    Callable[[str | PathLike[str]], AimbatEvent],
]:
    """Decorator that registers a function as an event creator for `datatype`.

    Args:
        datatype: The data type the decorated function creates events from.

    Example:
        ```python
        @event_creator(DataType.SAC)
        def create_event_from_sacfile(sacfile: str | PathLike[str]) -> AimbatEvent: ...
        ```
    """

    def decorator(
        fn: Callable[[str | PathLike[str]], AimbatEvent],
    ) -> Callable[[str | PathLike[str]], AimbatEvent]:
        register_event_creator(datatype, fn)
        return fn

    return decorator

file_source_present

file_source_present(
    sourcename: str | PathLike[str],
) -> bool

Return whether a filesystem-backed data source is present.

Shared implementation for the file-based probes (SAC, miniSEED, JSON). A reachable regular file is present; a reachable directory that simply does not contain the named entry is a clean False. Anything that leaves the answer unknowable right now - a missing or unreadable parent directory (a moved or unmounted tree), a permission error, an unreachable mount - raises SourceUnavailableError rather than being mistaken for a deletion.

Parameters:

Name Type Description Default
sourcename str | PathLike[str]

Path to the file backing the data source.

required

Raises:

Type Description
SourceUnavailableError

If presence cannot be determined right now.

Source code in src/aimbat/io/_base.py
def file_source_present(sourcename: str | PathLike[str]) -> bool:
    """Return whether a filesystem-backed data source is present.

    Shared implementation for the file-based probes (SAC, miniSEED, JSON). A
    reachable regular file is present; a reachable directory that simply does
    not contain the named entry is a clean `False`. Anything that leaves the
    answer unknowable right now - a missing or unreadable parent directory (a
    moved or unmounted tree), a permission error, an unreachable mount -
    raises `SourceUnavailableError` rather than being mistaken for a deletion.

    Args:
        sourcename: Path to the file backing the data source.

    Raises:
        SourceUnavailableError: If presence cannot be determined right now.
    """
    path = Path(sourcename)
    try:
        mode = os.stat(path).st_mode
    except FileNotFoundError as exc:
        # The file is gone. Only trust that as a real deletion if the parent
        # directory is still reachable; an unmounted volume whose mountpoint
        # lingers would otherwise look like a clean absence.
        if os.path.isdir(path.parent):
            return False
        raise SourceUnavailableError(
            f"Neither {os.fspath(path)!r} nor its parent directory could be "
            + "reached; this looks like a moved or unmounted data tree, not a "
            + "deletion."
        ) from exc
    except OSError as exc:
        raise SourceUnavailableError(
            f"Could not determine whether {os.fspath(path)!r} is present: {exc}"
        ) from exc
    return stat_module.S_ISREG(mode)

read_seismogram_data

read_seismogram_data(
    datasource: str | PathLike[str],
    datatype: DataType,
    session: Session | None = None,
) -> NDArray[floating]

Read seismogram waveform data from a data source.

Results are cached in memory by (datasource, datatype) key, alongside the source's modification time and size; a cached array is re-read when either has changed since, so a source written behind AIMBAT's back does not keep serving stale data. The returned array is read-only.

If session is given and has a staged write for this key (from stage_seismogram_data), the staged value is returned instead of reading the data source, so a session sees its own uncommitted waveform write.

Parameters:

Name Type Description Default
datasource str | PathLike[str]

Logical source identifier for the data source.

required
datatype DataType

Data type of the source.

required
session Session | None

Session whose staged writes should be consulted first.

None

Returns:

Type Description
NDArray[floating]

Read-only seismogram waveform data as a NumPy array.

Raises:

Type Description
NotImplementedError

If datatype has no registered data reader.

Source code in src/aimbat/io/_base.py
def read_seismogram_data(
    datasource: str | PathLike[str],
    datatype: DataType,
    session: Session | None = None,
) -> npt.NDArray[np.floating]:
    """Read seismogram waveform data from a data source.

    Results are cached in memory by `(datasource, datatype)` key, alongside
    the source's modification time and size; a cached array is re-read when
    either has changed since, so a source written behind AIMBAT's back does
    not keep serving stale data. The returned array is read-only.

    If `session` is given and has a staged write for this key (from
    `stage_seismogram_data`), the staged value is returned instead of reading
    the data source, so a session sees its own uncommitted waveform write.

    Args:
        datasource: Logical source identifier for the data source.
        datatype: Data type of the source.
        session: Session whose staged writes should be consulted first.

    Returns:
        Read-only seismogram waveform data as a NumPy array.

    Raises:
        NotImplementedError: If `datatype` has no registered data reader.
    """
    logger.debug(f"Reading seismogram data from {datasource}.")
    key = (str(datasource), datatype)
    if session is not None:
        # Checked before the reader-registration check below: a session's own
        # staged write should be readable back regardless of whether the
        # datatype also supports reading through a registered reader.
        staged = _pending.get(session)
        if staged is not None and key in staged:
            logger.debug(f"Retrieved staged seismogram data for {datasource}.")
            return staged[key]
    reader = _seismogram_data_readers.get(datatype)
    if reader is None:
        raise NotImplementedError(
            f"{datatype} does not support reading seismogram data."
        )
    stamp = _source_stamp(key[0])
    entry = _cache.get(key)
    if entry is not None and entry.stamp == stamp:
        logger.debug(f"Retrieved seismogram data from cache for {datasource}.")
        _cache.move_to_end(key)
        return entry.data

    context = SeismogramReadContext(
        sourcename=key[0], datatype=datatype, session=session
    )
    arr = reader(context)
    arr.flags.writeable = False
    _cache[key] = _CacheEntry(stamp, arr)
    _cache.move_to_end(key)
    if len(_cache) > _CACHE_MAX_ENTRIES:
        _cache.popitem(last=False)
    return arr

read_source_records

read_source_records(
    datasource: str | PathLike[str],
    datatype: DataType,
    *,
    station: bool = True,
    event: bool = True,
    seismogram: bool = True,
) -> SourceRecords

Build the requested records from a data source, reading it once where possible.

Data types with a registered source-records reader (SAC) read the source a single time and build everything asked for from it. The rest fall back to their individual creators, one read each.

Parameters:

Name Type Description Default
datasource str | PathLike[str]

Logical source identifier for the data source.

required
datatype DataType

Data type of the source.

required
station bool

Whether an AimbatStation is wanted.

True
event bool

Whether an AimbatEvent is wanted.

True
seismogram bool

Whether an AimbatSeismogram is wanted.

True

Returns:

Type Description
SourceRecords

The requested records; the fields that were not asked for are None.

Raises:

Type Description
NotImplementedError

If a record is asked for that datatype has no registered creator for.

Source code in src/aimbat/io/_base.py
def read_source_records(
    datasource: str | PathLike[str],
    datatype: DataType,
    *,
    station: bool = True,
    event: bool = True,
    seismogram: bool = True,
) -> SourceRecords:
    """Build the requested records from a data source, reading it once where possible.

    Data types with a registered source-records reader (SAC) read the source
    a single time and build everything asked for from it. The rest fall back
    to their individual creators, one read each.

    Args:
        datasource: Logical source identifier for the data source.
        datatype: Data type of the source.
        station: Whether an `AimbatStation` is wanted.
        event: Whether an `AimbatEvent` is wanted.
        seismogram: Whether an `AimbatSeismogram` is wanted.

    Returns:
        The requested records; the fields that were not asked for are `None`.

    Raises:
        NotImplementedError: If a record is asked for that `datatype` has no
            registered creator for.
    """
    if not (station or event or seismogram):
        return SourceRecords()

    reader = _source_records_readers.get(datatype)
    if reader is not None:
        return reader(
            SourceRecordsRequest(
                sourcename=str(datasource),
                datatype=datatype,
                station=station,
                event=event,
                seismogram=seismogram,
            )
        )

    return SourceRecords(
        station=create_station(datasource, datatype) if station else None,
        event=create_event(datasource, datatype) if event else None,
        seismogram=create_seismogram(datasource, datatype) if seismogram else None,
    )

register_event_creator

register_event_creator(
    datatype: DataType,
    fn: Callable[[str | PathLike[str]], AimbatEvent],
) -> None

Register a function that creates an AimbatEvent from a data source.

Parameters:

Name Type Description Default
datatype DataType

The data type this creator handles.

required
fn Callable[[str | PathLike[str]], AimbatEvent]

Callable that accepts a logical source identifier and returns an AimbatEvent instance.

required
Source code in src/aimbat/io/_base.py
def register_event_creator(
    datatype: DataType,
    fn: Callable[[str | PathLike[str]], AimbatEvent],
) -> None:
    """Register a function that creates an `AimbatEvent` from a data source.

    Args:
        datatype: The data type this creator handles.
        fn: Callable that accepts a logical source identifier and returns an
            `AimbatEvent` instance.
    """
    if datatype in _event_creators:
        logger.warning(f"Overwriting existing event creator for {datatype}.")
    logger.debug(f"Registering event creator for {datatype}.")
    _event_creators[datatype] = fn

register_seismogram_creator

register_seismogram_creator(
    datatype: DataType,
    fn: Callable[[str | PathLike[str]], AimbatSeismogram],
) -> None

Register a function that creates an AimbatSeismogram from a data source.

Parameters:

Name Type Description Default
datatype DataType

The data type this creator handles.

required
fn Callable[[str | PathLike[str]], AimbatSeismogram]

Callable that accepts a logical source identifier and returns an AimbatSeismogram instance.

required
Source code in src/aimbat/io/_base.py
def register_seismogram_creator(
    datatype: DataType,
    fn: Callable[[str | PathLike[str]], AimbatSeismogram],
) -> None:
    """Register a function that creates an `AimbatSeismogram` from a data source.

    Args:
        datatype: The data type this creator handles.
        fn: Callable that accepts a logical source identifier and returns an
            `AimbatSeismogram` instance.
    """
    if datatype in _seismogram_creators:
        logger.warning(f"Overwriting existing seismogram creator for {datatype}.")
    logger.debug(f"Registering seismogram creator for {datatype}.")
    _seismogram_creators[datatype] = fn

register_seismogram_data_reader

register_seismogram_data_reader(
    datatype: DataType, fn: SeismogramDataReader
) -> None

Register a function that reads seismogram waveform data from a data source.

Parameters:

Name Type Description Default
datatype DataType

The data type this reader handles.

required
fn SeismogramDataReader

Callable that accepts a SeismogramReadContext and returns the waveform data as a NumPy array.

required
Source code in src/aimbat/io/_base.py
def register_seismogram_data_reader(
    datatype: DataType,
    fn: SeismogramDataReader,
) -> None:
    """Register a function that reads seismogram waveform data from a data source.

    Args:
        datatype: The data type this reader handles.
        fn: Callable that accepts a `SeismogramReadContext` and returns the
            waveform data as a NumPy array.
    """
    if datatype in _seismogram_data_readers:
        logger.warning(f"Overwriting existing seismogram data reader for {datatype}.")
    logger.debug(f"Registering seismogram data reader for {datatype}.")
    _seismogram_data_readers[datatype] = fn

register_seismogram_data_writer

register_seismogram_data_writer(
    datatype: DataType,
    fn: Callable[
        [str | PathLike[str], NDArray[floating]], None
    ],
) -> None

Register a function that writes seismogram waveform data to a data source.

Parameters:

Name Type Description Default
datatype DataType

The data type this writer handles.

required
fn Callable[[str | PathLike[str], NDArray[floating]], None]

Callable that accepts a logical source identifier and a NumPy array, and writes the data to the source.

required
Source code in src/aimbat/io/_base.py
def register_seismogram_data_writer(
    datatype: DataType,
    fn: Callable[[str | PathLike[str], npt.NDArray[np.floating]], None],
) -> None:
    """Register a function that writes seismogram waveform data to a data source.

    Args:
        datatype: The data type this writer handles.
        fn: Callable that accepts a logical source identifier and a NumPy array,
            and writes the data to the source.
    """
    if datatype in _seismogram_data_writers:
        logger.warning(f"Overwriting existing seismogram data writer for {datatype}.")
    logger.debug(f"Registering seismogram data writer for {datatype}.")
    _seismogram_data_writers[datatype] = fn

register_source_probe

register_source_probe(
    datatype: DataType, fn: SourcePresenceProbe
) -> None

Register a function that checks whether a data source is still present.

Parameters:

Name Type Description Default
datatype DataType

The data type this probe handles.

required
fn SourcePresenceProbe

Callable that accepts a SeismogramReadContext and returns whether the source still provides the seismogram, raising SourceUnavailableError when it cannot tell.

required
Source code in src/aimbat/io/_base.py
def register_source_probe(
    datatype: DataType,
    fn: SourcePresenceProbe,
) -> None:
    """Register a function that checks whether a data source is still present.

    Args:
        datatype: The data type this probe handles.
        fn: Callable that accepts a `SeismogramReadContext` and returns whether
            the source still provides the seismogram, raising
            `SourceUnavailableError` when it cannot tell.
    """
    if datatype in _source_probes:
        logger.warning(f"Overwriting existing source presence probe for {datatype}.")
    logger.debug(f"Registering source presence probe for {datatype}.")
    _source_probes[datatype] = fn

register_source_records_reader

register_source_records_reader(
    datatype: DataType, fn: SourceRecordsReader
) -> None

Register a function that builds several records from one read of a data source.

Optional: a data type with no reader of its own is served by calling its individual creators instead (see read_source_records). Registering one is worth it when a single read yields more than one record, as it does for SAC.

Parameters:

Name Type Description Default
datatype DataType

The data type this reader handles.

required
fn SourceRecordsReader

Callable that accepts a SourceRecordsRequest and returns the requested records as a SourceRecords.

required
Source code in src/aimbat/io/_base.py
def register_source_records_reader(
    datatype: DataType,
    fn: SourceRecordsReader,
) -> None:
    """Register a function that builds several records from one read of a data source.

    Optional: a data type with no reader of its own is served by calling its
    individual creators instead (see `read_source_records`). Registering one
    is worth it when a single read yields more than one record, as it does
    for SAC.

    Args:
        datatype: The data type this reader handles.
        fn: Callable that accepts a `SourceRecordsRequest` and returns the
            requested records as a `SourceRecords`.
    """
    if datatype in _source_records_readers:
        logger.warning(f"Overwriting existing source records reader for {datatype}.")
    logger.debug(f"Registering source records reader for {datatype}.")
    _source_records_readers[datatype] = fn

register_station_creator

register_station_creator(
    datatype: DataType,
    fn: Callable[[str | PathLike[str]], AimbatStation],
) -> None

Register a function that creates an AimbatStation from a data source.

Parameters:

Name Type Description Default
datatype DataType

The data type this creator handles.

required
fn Callable[[str | PathLike[str]], AimbatStation]

Callable that accepts a logical source identifier and returns an AimbatStation instance.

required
Source code in src/aimbat/io/_base.py
def register_station_creator(
    datatype: DataType,
    fn: Callable[[str | PathLike[str]], AimbatStation],
) -> None:
    """Register a function that creates an `AimbatStation` from a data source.

    Args:
        datatype: The data type this creator handles.
        fn: Callable that accepts a logical source identifier and returns an
            `AimbatStation` instance.
    """
    if datatype in _station_creators:
        logger.warning(f"Overwriting existing station creator for {datatype}.")
    logger.debug(f"Registering station creator for {datatype}.")
    _station_creators[datatype] = fn

seismogram_creator

seismogram_creator(
    datatype: DataType,
) -> Callable[
    [Callable[[str | PathLike[str]], AimbatSeismogram]],
    Callable[[str | PathLike[str]], AimbatSeismogram],
]

Decorator that registers a function as a seismogram creator for datatype.

Parameters:

Name Type Description Default
datatype DataType

The data type the decorated function creates seismograms from.

required
Example
@seismogram_creator(DataType.SAC)
def create_seismogram_from_sacfile(
    sacfile: str | PathLike[str],
) -> AimbatSeismogram: ...
Source code in src/aimbat/io/_base.py
def seismogram_creator(
    datatype: DataType,
) -> Callable[
    [Callable[[str | PathLike[str]], AimbatSeismogram]],
    Callable[[str | PathLike[str]], AimbatSeismogram],
]:
    """Decorator that registers a function as a seismogram creator for `datatype`.

    Args:
        datatype: The data type the decorated function creates seismograms from.

    Example:
        ```python
        @seismogram_creator(DataType.SAC)
        def create_seismogram_from_sacfile(
            sacfile: str | PathLike[str],
        ) -> AimbatSeismogram: ...
        ```
    """

    def decorator(
        fn: Callable[[str | PathLike[str]], AimbatSeismogram],
    ) -> Callable[[str | PathLike[str]], AimbatSeismogram]:
        register_seismogram_creator(datatype, fn)
        return fn

    return decorator

seismogram_data_reader

seismogram_data_reader(
    datatype: DataType,
) -> Callable[[SeismogramDataReader], SeismogramDataReader]

Decorator that registers a function as a seismogram data reader for datatype.

Parameters:

Name Type Description Default
datatype DataType

The data type the decorated function reads waveform data from.

required
Example
@seismogram_data_reader(DataType.SAC)
def read_seismogram_data_from_sacfile(
    context: SeismogramReadContext,
) -> npt.NDArray[np.floating]: ...
Source code in src/aimbat/io/_base.py
def seismogram_data_reader(
    datatype: DataType,
) -> Callable[[SeismogramDataReader], SeismogramDataReader]:
    """Decorator that registers a function as a seismogram data reader for `datatype`.

    Args:
        datatype: The data type the decorated function reads waveform data from.

    Example:
        ```python
        @seismogram_data_reader(DataType.SAC)
        def read_seismogram_data_from_sacfile(
            context: SeismogramReadContext,
        ) -> npt.NDArray[np.floating]: ...
        ```
    """

    def decorator(fn: SeismogramDataReader) -> SeismogramDataReader:
        register_seismogram_data_reader(datatype, fn)
        return fn

    return decorator

seismogram_data_writer

seismogram_data_writer(
    datatype: DataType,
) -> Callable[
    [
        Callable[
            [str | PathLike[str], NDArray[floating]], None
        ]
    ],
    Callable[
        [str | PathLike[str], NDArray[floating]], None
    ],
]

Decorator that registers a function as a seismogram data writer for datatype.

Parameters:

Name Type Description Default
datatype DataType

The data type the decorated function writes waveform data to.

required
Example
@seismogram_data_writer(DataType.SAC)
def write_seismogram_data_to_sacfile(
    sacfile: str | PathLike[str], data: npt.NDArray[np.floating]
) -> None: ...
Source code in src/aimbat/io/_base.py
def seismogram_data_writer(
    datatype: DataType,
) -> Callable[
    [Callable[[str | PathLike[str], npt.NDArray[np.floating]], None]],
    Callable[[str | PathLike[str], npt.NDArray[np.floating]], None],
]:
    """Decorator that registers a function as a seismogram data writer for `datatype`.

    Args:
        datatype: The data type the decorated function writes waveform data to.

    Example:
        ```python
        @seismogram_data_writer(DataType.SAC)
        def write_seismogram_data_to_sacfile(
            sacfile: str | PathLike[str], data: npt.NDArray[np.floating]
        ) -> None: ...
        ```
    """

    def decorator(
        fn: Callable[[str | PathLike[str], npt.NDArray[np.floating]], None],
    ) -> Callable[[str | PathLike[str], npt.NDArray[np.floating]], None]:
        register_seismogram_data_writer(datatype, fn)
        return fn

    return decorator

source_present

source_present(
    sourcename: str,
    datatype: DataType,
    session: Session | None = None,
) -> bool

Check whether a data source is still present and provides its seismogram.

Parameters:

Name Type Description Default
sourcename str

Logical source identifier for the data source.

required
datatype DataType

Data type of the source.

required
session Session | None

Session made available to the probe (needed by sources whose identity is resolved through the database).

None

Returns:

Type Description
bool

True if the source is reachable and provides the seismogram,

bool

False if it is reachable and definitely does not.

Raises:

Type Description
NotImplementedError

If datatype has no registered probe.

SourceUnavailableError

If the probe cannot determine presence right now (e.g. an unreadable file or an unreachable mount).

Source code in src/aimbat/io/_base.py
def source_present(
    sourcename: str, datatype: DataType, session: Session | None = None
) -> bool:
    """Check whether a data source is still present and provides its seismogram.

    Args:
        sourcename: Logical source identifier for the data source.
        datatype: Data type of the source.
        session: Session made available to the probe (needed by sources whose
            identity is resolved through the database).

    Returns:
        `True` if the source is reachable and provides the seismogram,
        `False` if it is reachable and definitely does not.

    Raises:
        NotImplementedError: If `datatype` has no registered probe.
        SourceUnavailableError: If the probe cannot determine presence right
            now (e.g. an unreadable file or an unreachable mount).
    """
    probe = _source_probes.get(datatype)
    if probe is None:
        raise NotImplementedError(f"{datatype} does not support source probing.")
    context = SeismogramReadContext(
        sourcename=str(sourcename), datatype=datatype, session=session
    )
    return probe(context)

source_probe

source_probe(
    datatype: DataType,
) -> Callable[[SourcePresenceProbe], SourcePresenceProbe]

Decorator that registers a function as a source-presence probe for datatype.

Parameters:

Name Type Description Default
datatype DataType

The data type the decorated function probes.

required
Example
@source_probe(DataType.SAC)
def sac_source_present(context: SeismogramReadContext) -> bool: ...
Source code in src/aimbat/io/_base.py
def source_probe(
    datatype: DataType,
) -> Callable[[SourcePresenceProbe], SourcePresenceProbe]:
    """Decorator that registers a function as a source-presence probe for `datatype`.

    Args:
        datatype: The data type the decorated function probes.

    Example:
        ```python
        @source_probe(DataType.SAC)
        def sac_source_present(context: SeismogramReadContext) -> bool: ...
        ```
    """

    def decorator(fn: SourcePresenceProbe) -> SourcePresenceProbe:
        register_source_probe(datatype, fn)
        return fn

    return decorator

source_records_reader

source_records_reader(
    datatype: DataType,
) -> Callable[[SourceRecordsReader], SourceRecordsReader]

Decorator that registers a function as a source-records reader for datatype.

Parameters:

Name Type Description Default
datatype DataType

The data type the decorated function reads records from.

required
Example
@source_records_reader(DataType.SAC)
def read_records_from_sacfile(
    request: SourceRecordsRequest,
) -> SourceRecords: ...
Source code in src/aimbat/io/_base.py
def source_records_reader(
    datatype: DataType,
) -> Callable[[SourceRecordsReader], SourceRecordsReader]:
    """Decorator that registers a function as a source-records reader for `datatype`.

    Args:
        datatype: The data type the decorated function reads records from.

    Example:
        ```python
        @source_records_reader(DataType.SAC)
        def read_records_from_sacfile(
            request: SourceRecordsRequest,
        ) -> SourceRecords: ...
        ```
    """

    def decorator(fn: SourceRecordsReader) -> SourceRecordsReader:
        register_source_records_reader(datatype, fn)
        return fn

    return decorator

stage_seismogram_data

stage_seismogram_data(
    session: Session | None,
    datasource: str | PathLike[str],
    datatype: DataType,
    data: NDArray[floating],
) -> None

Stage a waveform write to flush when session commits.

The write is validated immediately (a datatype with no registered writer raises straight away), but the data source is not touched until session commits. The staged value is discarded if session rolls back or is abandoned, and is visible to reads made through the same session before the commit.

A copy of data is taken at call time, so later mutation of the caller's array does not change what is persisted. The (datasource, datatype) key is also captured now: renaming the data source on the model before the commit would flush to the old path. Staged pages are held in memory until the commit with no eviction (unlike the read cache), so staging waveform writes for a very large event holds every new array at once.

Staging is keyed by (datasource, datatype), not by seismogram: staging a second write for the same data source in one session before committing replaces the first, and only the last survives the flush.

If session is None (a detached instance, attached to no session) there is no transaction to bind to, so the write falls back to an eager write_seismogram_data call.

Parameters:

Name Type Description Default
session Session | None

The session that owns the seismogram, or None.

required
datasource str | PathLike[str]

Logical source identifier for the data source.

required
datatype DataType

Data type of the source.

required
data NDArray[floating]

Seismogram waveform data to write.

required

Raises:

Type Description
NotImplementedError

If datatype has no registered data writer.

Source code in src/aimbat/io/_base.py
def stage_seismogram_data(
    session: Session | None,
    datasource: str | PathLike[str],
    datatype: DataType,
    data: npt.NDArray[np.floating],
) -> None:
    """Stage a waveform write to flush when `session` commits.

    The write is validated immediately (a datatype with no registered writer
    raises straight away), but the data source is not touched until `session`
    commits. The staged value is discarded if `session` rolls back or is
    abandoned, and is visible to reads made through the same session before
    the commit.

    A copy of `data` is taken at call time, so later mutation of the caller's
    array does not change what is persisted. The `(datasource, datatype)` key
    is also captured now: renaming the data source on the model before the
    commit would flush to the old path. Staged pages are held in memory until
    the commit with no eviction (unlike the read cache), so staging waveform
    writes for a very large event holds every new array at once.

    Staging is keyed by `(datasource, datatype)`, not by seismogram: staging
    a second write for the same data source in one session before committing
    replaces the first, and only the last survives the flush.

    If `session` is `None` (a detached instance, attached to no session)
    there is no transaction to bind to, so the write falls back to an eager
    `write_seismogram_data` call.

    Args:
        session: The session that owns the seismogram, or `None`.
        datasource: Logical source identifier for the data source.
        datatype: Data type of the source.
        data: Seismogram waveform data to write.

    Raises:
        NotImplementedError: If `datatype` has no registered data writer.
    """
    if datatype not in _seismogram_data_writers:
        raise NotImplementedError(
            f"{datatype} does not support writing seismogram data."
        )
    if session is None:
        msg = f"Waveform write for detached seismogram {datasource}: "
        logger.warning(msg + "no session to bind to, writing eagerly.")
        write_seismogram_data(datasource, datatype, data)
        return
    logger.debug(f"Staging seismogram data write to {datasource}.")
    staged = np.array(data, copy=True)
    staged.flags.writeable = False
    session_pending = _pending.setdefault(session, {})
    key = (str(datasource), datatype)
    if key in session_pending:
        logger.warning(
            f"Replacing an earlier staged waveform write for {datasource}; "
            + "only the latest is flushed on commit."
        )
    session_pending[key] = staged

station_creator

station_creator(
    datatype: DataType,
) -> Callable[
    [Callable[[str | PathLike[str]], AimbatStation]],
    Callable[[str | PathLike[str]], AimbatStation],
]

Decorator that registers a function as a station creator for datatype.

Parameters:

Name Type Description Default
datatype DataType

The data type the decorated function creates stations from.

required
Example
@station_creator(DataType.SAC)
def create_station_from_sacfile(
    sacfile: str | PathLike[str],
) -> AimbatStation: ...
Source code in src/aimbat/io/_base.py
def station_creator(
    datatype: DataType,
) -> Callable[
    [Callable[[str | PathLike[str]], AimbatStation]],
    Callable[[str | PathLike[str]], AimbatStation],
]:
    """Decorator that registers a function as a station creator for `datatype`.

    Args:
        datatype: The data type the decorated function creates stations from.

    Example:
        ```python
        @station_creator(DataType.SAC)
        def create_station_from_sacfile(
            sacfile: str | PathLike[str],
        ) -> AimbatStation: ...
        ```
    """

    def decorator(
        fn: Callable[[str | PathLike[str]], AimbatStation],
    ) -> Callable[[str | PathLike[str]], AimbatStation]:
        register_station_creator(datatype, fn)
        return fn

    return decorator

supports_event_creation

supports_event_creation(datatype: DataType) -> bool

Return whether datatype has a registered event creator.

Source code in src/aimbat/io/_base.py
def supports_event_creation(datatype: DataType) -> bool:
    """Return whether `datatype` has a registered event creator."""
    return datatype in _event_creators

supports_seismogram_creation

supports_seismogram_creation(datatype: DataType) -> bool

Return whether datatype has a registered seismogram creator.

Source code in src/aimbat/io/_base.py
def supports_seismogram_creation(datatype: DataType) -> bool:
    """Return whether `datatype` has a registered seismogram creator."""
    return datatype in _seismogram_creators

supports_seismogram_data_reading

supports_seismogram_data_reading(
    datatype: DataType,
) -> bool

Return whether datatype has a registered seismogram data reader.

Source code in src/aimbat/io/_base.py
def supports_seismogram_data_reading(datatype: DataType) -> bool:
    """Return whether `datatype` has a registered seismogram data reader."""
    return datatype in _seismogram_data_readers

supports_seismogram_data_writing

supports_seismogram_data_writing(
    datatype: DataType,
) -> bool

Return whether datatype has a registered seismogram data writer.

Source code in src/aimbat/io/_base.py
def supports_seismogram_data_writing(datatype: DataType) -> bool:
    """Return whether `datatype` has a registered seismogram data writer."""
    return datatype in _seismogram_data_writers

supports_source_probe

supports_source_probe(datatype: DataType) -> bool

Return whether datatype has a registered source-presence probe.

Source code in src/aimbat/io/_base.py
def supports_source_probe(datatype: DataType) -> bool:
    """Return whether `datatype` has a registered source-presence probe."""
    return datatype in _source_probes

supports_source_records_reading

supports_source_records_reading(datatype: DataType) -> bool

Return whether datatype has a registered source-records reader.

Source code in src/aimbat/io/_base.py
def supports_source_records_reading(datatype: DataType) -> bool:
    """Return whether `datatype` has a registered source-records reader."""
    return datatype in _source_records_readers

supports_station_creation

supports_station_creation(datatype: DataType) -> bool

Return whether datatype has a registered station creator.

Source code in src/aimbat/io/_base.py
def supports_station_creation(datatype: DataType) -> bool:
    """Return whether `datatype` has a registered station creator."""
    return datatype in _station_creators

write_seismogram_data

write_seismogram_data(
    datasource: str | PathLike[str],
    datatype: DataType,
    data: NDArray[floating],
) -> None

Write seismogram waveform data to a data source.

Writes eagerly and invalidates the cache entry for (datasource, datatype). AimbatSeismogram.data assignment no longer calls this directly - it stages the write via stage_seismogram_data and the flush listener in aimbat.io._flush calls this on the owning session's commit.

Parameters:

Name Type Description Default
datasource str | PathLike[str]

Logical source identifier for the data source.

required
datatype DataType

Data type of the source.

required
data NDArray[floating]

Seismogram waveform data to write.

required

Raises:

Type Description
NotImplementedError

If datatype has no registered data writer.

Source code in src/aimbat/io/_base.py
def write_seismogram_data(
    datasource: str | PathLike[str],
    datatype: DataType,
    data: npt.NDArray[np.floating],
) -> None:
    """Write seismogram waveform data to a data source.

    Writes eagerly and invalidates the cache entry for `(datasource,
    datatype)`. `AimbatSeismogram.data` assignment no longer calls this
    directly - it stages the write via `stage_seismogram_data` and the flush
    listener in `aimbat.io._flush` calls this on the owning session's commit.

    Args:
        datasource: Logical source identifier for the data source.
        datatype: Data type of the source.
        data: Seismogram waveform data to write.

    Raises:
        NotImplementedError: If `datatype` has no registered data writer.
    """
    logger.debug(f"Writing seismogram data to {datasource}.")
    writer = _seismogram_data_writers.get(datatype)
    if writer is None:
        raise NotImplementedError(
            f"{datatype} does not support writing seismogram data."
        )
    writer(datasource, data)
    _cache.pop((str(datasource), datatype), None)

json

JSON data source support for AIMBAT.

Provides station and event creation from JSON files:

  • JSON_STATION (DataType.JSON_STATION): a JSON file containing a single station record. Field names match AimbatStation:

    {
        "name": "ANMO",
        "network": "IU",
        "location": "00",
        "channel": "BHZ",
        "latitude": 34.9459,
        "longitude": -106.4572,
        "elevation": 1820.0
    }
    
  • JSON_EVENT (DataType.JSON_EVENT): a JSON file containing a single event record. Field names match AimbatEvent:

    {
        "time": "2020-01-01T00:00:00Z",
        "latitude": 35.0,
        "longitude": -120.0,
        "depth": 10.0
    }
    

Functions:

Name Description
create_event_from_json

Create an AimbatEvent from a JSON file.

create_station_from_json

Create an AimbatStation from a JSON file.

json_source_present

Return whether the JSON file backing this record still exists.

create_event_from_json

create_event_from_json(
    path: str | PathLike[str],
) -> AimbatEvent

Create an AimbatEvent from a JSON file.

Parameters:

Name Type Description Default
path str | PathLike[str]

Path to the JSON file.

required

Returns:

Type Description
AimbatEvent

A new AimbatEvent instance.

Source code in src/aimbat/io/json.py
@event_creator(DataType.JSON_EVENT)
def create_event_from_json(path: str | PathLike[str]) -> AimbatEvent:
    """Create an `AimbatEvent` from a JSON file.

    Args:
        path: Path to the JSON file.

    Returns:
        A new `AimbatEvent` instance.
    """
    from aimbat.models import AimbatEvent, AimbatEventParameters

    logger.debug(f"Reading event data from {path}.")

    event = AimbatEvent.model_validate(_load_json(path))
    event.parameters = AimbatEventParameters()
    return event

create_station_from_json

create_station_from_json(
    path: str | PathLike[str],
) -> AimbatStation

Create an AimbatStation from a JSON file.

Parameters:

Name Type Description Default
path str | PathLike[str]

Path to the JSON file.

required

Returns:

Type Description
AimbatStation

A new AimbatStation instance.

Source code in src/aimbat/io/json.py
@station_creator(DataType.JSON_STATION)
def create_station_from_json(path: str | PathLike[str]) -> AimbatStation:
    """Create an `AimbatStation` from a JSON file.

    Args:
        path: Path to the JSON file.

    Returns:
        A new `AimbatStation` instance.
    """
    from aimbat.models import AimbatStation

    logger.debug(f"Reading station data from {path}.")

    return AimbatStation.model_validate(_load_json(path))

json_source_present

json_source_present(context: SeismogramReadContext) -> bool

Return whether the JSON file backing this record still exists.

Parameters:

Name Type Description Default
context SeismogramReadContext

Read context whose sourcename names the JSON file.

required

Raises:

Type Description
SourceUnavailableError

If presence cannot be determined right now (an unreadable file, a missing parent directory, an unreachable mount).

Source code in src/aimbat/io/json.py
@source_probe(DataType.JSON_EVENT)
@source_probe(DataType.JSON_STATION)
def json_source_present(context: SeismogramReadContext) -> bool:
    """Return whether the JSON file backing this record still exists.

    Args:
        context: Read context whose `sourcename` names the JSON file.

    Raises:
        SourceUnavailableError: If presence cannot be determined right now
            (an unreadable file, a missing parent directory, an unreachable
            mount).
    """
    return file_source_present(context.sourcename)

mseed

miniSEED waveform I/O for AIMBAT.

Registers a reader and writer only - no station, event, or seismogram creator. pysmo.classes.MSeed carries no station coordinates and no event data, only channel identity, timing and samples, so there is nothing for a creator to build a station or event from, and no pick concept to supply a t0 from either.

Scope boundary: this reader round-trips files AIMBAT itself wrote (e.g. via aimbat.core.add_seismograms_to_project), it is not a general-purpose miniSEED ingestion path. MSeed.from_file requires exactly one contiguous segment and raises ValueError on a data gap or more than one channel. Real-world archival or downloaded miniSEED is routinely multiplexed (several channels in one file) and/or gappy (a channel split across segments); reading that needs MSeed.all_from_file plus a policy for picking, splitting, or merging segments, which this module does not attempt. Such a file needs pre-processing (trimmed to one channel, one contiguous segment) before it fits AIMBAT's one-file-one-seismogram model.

This module registers its capabilities with the I/O dispatch layer on import, so importing it is sufficient to enable miniSEED support.

Functions:

Name Description
mseed_source_present

Return whether the miniSEED file backing this seismogram still exists.

read_seismogram_data_from_mseedfile

Read seismogram waveform data from a miniSEED file.

write_seismogram_data_to_mseedfile

Write seismogram waveform data to a miniSEED file.

mseed_source_present

mseed_source_present(
    context: SeismogramReadContext,
) -> bool

Return whether the miniSEED file backing this seismogram still exists.

A miniSEED file written by add_seismograms_to_project is created once and then treated exactly like any other external file, so a missing one is an ordinary prunable orphan.

Parameters:

Name Type Description Default
context SeismogramReadContext

Read context whose sourcename names the miniSEED file.

required

Raises:

Type Description
SourceUnavailableError

If presence cannot be determined right now (an unreadable file, a missing parent directory, an unreachable mount).

Source code in src/aimbat/io/mseed.py
@source_probe(DataType.MSEED)
def mseed_source_present(context: SeismogramReadContext) -> bool:
    """Return whether the miniSEED file backing this seismogram still exists.

    A miniSEED file written by `add_seismograms_to_project` is created once and
    then treated exactly like any other external file, so a missing one is an
    ordinary prunable orphan.

    Args:
        context: Read context whose `sourcename` names the miniSEED file.

    Raises:
        SourceUnavailableError: If presence cannot be determined right now
            (an unreadable file, a missing parent directory, an unreachable
            mount).
    """
    return file_source_present(context.sourcename)

read_seismogram_data_from_mseedfile

read_seismogram_data_from_mseedfile(
    context: SeismogramReadContext,
) -> NDArray[floating]

Read seismogram waveform data from a miniSEED file.

Parameters:

Name Type Description Default
context SeismogramReadContext

Read context whose sourcename names the miniSEED file.

required

Returns:

Type Description
NDArray[floating]

Seismogram amplitude data.

Raises:

Type Description
ValueError

If the file holds zero, or more than one, contiguous segment (a data gap, or more than one channel) - see the module docstring's scope boundary.

Source code in src/aimbat/io/mseed.py
@seismogram_data_reader(DataType.MSEED)
def read_seismogram_data_from_mseedfile(
    context: SeismogramReadContext,
) -> npt.NDArray[np.floating]:
    """Read seismogram waveform data from a miniSEED file.

    Args:
        context: Read context whose `sourcename` names the miniSEED file.

    Returns:
        Seismogram amplitude data.

    Raises:
        ValueError: If the file holds zero, or more than one, contiguous
            segment (a data gap, or more than one channel) - see the
            module docstring's scope boundary.
    """

    logger.debug(f"Reading seismogram data from {context.sourcename}.")

    return MSeed.from_file(context.sourcename).data

write_seismogram_data_to_mseedfile

write_seismogram_data_to_mseedfile(
    mseedfile: str | PathLike[str], data: NDArray[floating]
) -> None

Write seismogram waveform data to a miniSEED file.

Parameters:

Name Type Description Default
mseedfile str | PathLike[str]

Name of the miniSEED file.

required
data NDArray[floating]

Seismogram amplitude data to write.

required
Source code in src/aimbat/io/mseed.py
@seismogram_data_writer(DataType.MSEED)
def write_seismogram_data_to_mseedfile(
    mseedfile: str | PathLike[str], data: npt.NDArray[np.floating]
) -> None:
    """Write seismogram waveform data to a miniSEED file.

    Args:
        mseedfile: Name of the miniSEED file.
        data: Seismogram amplitude data to write.
    """

    logger.debug(f"Writing seismogram data to {mseedfile}.")

    mseed = MSeed.from_file(mseedfile)
    mseed.data = data
    mseed.write(mseedfile)

sac

SAC file I/O for AIMBAT.

Reads and writes seismogram data from SAC files via pysmo, and creates AimbatStation, AimbatEvent, and AimbatSeismogram model instances from SAC file metadata.

This module registers its capabilities with the I/O dispatch layer on import, so importing it is sufficient to enable SAC support.

Functions:

Name Description
create_event_from_sacfile

Create an AimbatEvent instance from a SAC file.

create_seismogram_from_sacfile

Create an AimbatSeismogram instance from a SAC file, using the configured pick header.

create_seismogram_from_sacfile_and_pick_header

Create an AimbatSeismogram instance from a SAC file.

create_station_from_sacfile

Create an AimbatStation instance from a SAC file.

read_records_from_sacfile

Build every record a request asks for from one read of a SAC file.

read_seismogram_data_from_sacfile

Read seismogram waveform data from a SAC file.

sac_source_present

Return whether the SAC file backing this seismogram still exists.

write_seismogram_data_to_sacfile

Write seismogram waveform data to a SAC file.

create_event_from_sacfile

create_event_from_sacfile(
    sacfile: str | PathLike[str],
) -> AimbatEvent

Create an AimbatEvent instance from a SAC file.

Parameters:

Name Type Description Default
sacfile str | PathLike[str]

Name of the SAC file.

required

Returns:

Type Description
AimbatEvent

A new AimbatEvent instance.

Source code in src/aimbat/io/sac.py
@event_creator(DataType.SAC)
def create_event_from_sacfile(sacfile: str | PathLike[str]) -> AimbatEvent:
    """Create an `AimbatEvent` instance from a SAC file.

    Args:
        sacfile: Name of the SAC file.

    Returns:
        A new `AimbatEvent` instance.
    """

    logger.debug(f"Reading event data from {sacfile}.")

    return _event_from_sac(SAC.from_file(sacfile))

create_seismogram_from_sacfile

create_seismogram_from_sacfile(
    sacfile: str | PathLike[str],
) -> AimbatSeismogram

Create an AimbatSeismogram instance from a SAC file, using the configured pick header.

Parameters:

Name Type Description Default
sacfile str | PathLike[str]

Name of the SAC file.

required

Returns:

Type Description
AimbatSeismogram

A new AimbatSeismogram instance.

Source code in src/aimbat/io/sac.py
@seismogram_creator(DataType.SAC)
def create_seismogram_from_sacfile(sacfile: str | PathLike[str]) -> AimbatSeismogram:
    """Create an `AimbatSeismogram` instance from a SAC file, using the configured pick header.

    Args:
        sacfile: Name of the SAC file.

    Returns:
        A new `AimbatSeismogram` instance.
    """
    return create_seismogram_from_sacfile_and_pick_header(
        sacfile, settings.sac_pick_header
    )

create_seismogram_from_sacfile_and_pick_header

create_seismogram_from_sacfile_and_pick_header(
    sacfile: str | PathLike[str], sac_pick_header: str
) -> AimbatSeismogram

Create an AimbatSeismogram instance from a SAC file.

Parameters:

Name Type Description Default
sacfile str | PathLike[str]

Name of the SAC file.

required
sac_pick_header str

SAC header to use as t0 in AIMBAT.

required

Returns:

Type Description
AimbatSeismogram

A new AimbatSeismogram instance, with t0 set from sac_pick_header.

Raises:

Type Description
ValueError

If sac_pick_header is not a SAC pick header, or if the file has no pick stored in it.

Source code in src/aimbat/io/sac.py
def create_seismogram_from_sacfile_and_pick_header(
    sacfile: str | PathLike[str], sac_pick_header: str
) -> AimbatSeismogram:
    """Create an `AimbatSeismogram` instance from a SAC file.

    Args:
        sacfile: Name of the SAC file.
        sac_pick_header: SAC header to use as t0 in AIMBAT.

    Returns:
        A new `AimbatSeismogram` instance, with `t0` set from `sac_pick_header`.

    Raises:
        ValueError: If `sac_pick_header` is not a SAC pick header, or if the
            file has no pick stored in it.
    """

    logger.debug(f"Reading seismogram metadata from {sacfile}.")

    return _seismogram_from_sac(SAC.from_file(sacfile), sac_pick_header)

create_station_from_sacfile

create_station_from_sacfile(
    sacfile: str | PathLike[str],
) -> AimbatStation

Create an AimbatStation instance from a SAC file.

Parameters:

Name Type Description Default
sacfile str | PathLike[str]

Name of the SAC file.

required

Returns:

Type Description
AimbatStation

A new AimbatStation instance.

Source code in src/aimbat/io/sac.py
@station_creator(DataType.SAC)
def create_station_from_sacfile(sacfile: str | PathLike[str]) -> AimbatStation:
    """Create an `AimbatStation` instance from a SAC file.

    Args:
        sacfile: Name of the SAC file.

    Returns:
        A new `AimbatStation` instance.
    """

    logger.debug(f"Reading station data from {sacfile}.")

    return _station_from_sac(SAC.from_file(sacfile))

read_records_from_sacfile

read_records_from_sacfile(
    request: SourceRecordsRequest,
) -> SourceRecords

Build every record a request asks for from one read of a SAC file.

Parameters:

Name Type Description Default
request SourceRecordsRequest

The records wanted, and the SAC file to build them from.

required

Returns:

Type Description
SourceRecords

The requested records.

Raises:

Type Description
ValueError

If a seismogram is wanted and the configured sac_pick_header is not a SAC pick header, or the file has no pick stored in it.

Source code in src/aimbat/io/sac.py
@source_records_reader(DataType.SAC)
def read_records_from_sacfile(request: SourceRecordsRequest) -> SourceRecords:
    """Build every record a request asks for from one read of a SAC file.

    Args:
        request: The records wanted, and the SAC file to build them from.

    Returns:
        The requested records.

    Raises:
        ValueError: If a seismogram is wanted and the configured
            `sac_pick_header` is not a SAC pick header, or the file has no
            pick stored in it.
    """

    logger.debug(f"Reading records from {request.sourcename}.")

    sac = SAC.from_file(request.sourcename)
    return SourceRecords(
        station=_station_from_sac(sac) if request.station else None,
        event=_event_from_sac(sac) if request.event else None,
        seismogram=(
            _seismogram_from_sac(sac, settings.sac_pick_header)
            if request.seismogram
            else None
        ),
    )

read_seismogram_data_from_sacfile

read_seismogram_data_from_sacfile(
    context: SeismogramReadContext,
) -> NDArray[floating]

Read seismogram waveform data from a SAC file.

Parameters:

Name Type Description Default
context SeismogramReadContext

Read context whose sourcename names the SAC file.

required

Returns:

Type Description
NDArray[floating]

Seismogram amplitude data.

Source code in src/aimbat/io/sac.py
@seismogram_data_reader(DataType.SAC)
def read_seismogram_data_from_sacfile(
    context: SeismogramReadContext,
) -> npt.NDArray[np.floating]:
    """Read seismogram waveform data from a SAC file.

    Args:
        context: Read context whose `sourcename` names the SAC file.

    Returns:
        Seismogram amplitude data.
    """

    logger.debug(f"Reading seismogram data from {context.sourcename}.")

    return SAC.from_file(context.sourcename).seismogram.data

sac_source_present

sac_source_present(context: SeismogramReadContext) -> bool

Return whether the SAC file backing this seismogram still exists.

Parameters:

Name Type Description Default
context SeismogramReadContext

Read context whose sourcename names the SAC file.

required

Raises:

Type Description
SourceUnavailableError

If presence cannot be determined right now (an unreadable file, a missing parent directory, an unreachable mount).

Source code in src/aimbat/io/sac.py
@source_probe(DataType.SAC)
def sac_source_present(context: SeismogramReadContext) -> bool:
    """Return whether the SAC file backing this seismogram still exists.

    Args:
        context: Read context whose `sourcename` names the SAC file.

    Raises:
        SourceUnavailableError: If presence cannot be determined right now
            (an unreadable file, a missing parent directory, an unreachable
            mount).
    """
    return file_source_present(context.sourcename)

write_seismogram_data_to_sacfile

write_seismogram_data_to_sacfile(
    sacfile: str | PathLike[str], data: NDArray[floating]
) -> None

Write seismogram waveform data to a SAC file.

Parameters:

Name Type Description Default
sacfile str | PathLike[str]

Name of the SAC file.

required
data NDArray[floating]

Seismogram amplitude data to write.

required
Source code in src/aimbat/io/sac.py
@seismogram_data_writer(DataType.SAC)
def write_seismogram_data_to_sacfile(
    sacfile: str | PathLike[str], data: npt.NDArray[np.floating]
) -> None:
    """Write seismogram waveform data to a SAC file.

    Args:
        sacfile: Name of the SAC file.
        data: Seismogram amplitude data to write.
    """

    logger.debug(f"Writing seismogram data to {sacfile}.")

    sac = SAC.from_file(sacfile)
    sac.seismogram.data = data
    sac.write(sacfile)