Skip to content

aimbat.models

ORM classes mapping to AIMBAT database tables.

Each class in this module corresponds to a table in the SQLite project database and is built on SQLModel, which combines SQLAlchemy (for database access) with Pydantic (for validation).

The main classes and their relationships are:

  • AimbatEvent — a seismic event.
  • AimbatStation — a seismic recording station.
  • AimbatSeismogram — links an AimbatEvent to an AimbatStation and holds the timing metadata (begin_time, delta, t0). Waveform data is accessed via the associated AimbatDataSource.
  • AimbatDataSource — records where the waveform data for a seismogram is stored, along with its type (e.g. SAC).
  • AimbatEventParameters — processing parameters shared across all seismograms of an event (window bounds, bandpass filter settings, MCCC settings, etc.).
  • AimbatSeismogramParameters — per-seismogram processing parameters (flip, select, working pick t1).
  • AimbatSnapshot — captures a point-in-time copy of event and seismogram parameters via AimbatEventParametersSnapshot and AimbatSeismogramParametersSnapshot, enabling rollback and comparison.
  • AimbatEventQuality / AimbatSeismogramQuality — live quality metrics updated during processing; AimbatSeismogramQuality stores the ICCS cross-correlation coefficient iccs_cc and MCCC per-seismogram metrics; AimbatEventQuality stores the MCCC global RMSE.
  • AimbatEventQualitySnapshot / AimbatSeismogramQualitySnapshot — point-in-time copies of quality metrics captured alongside parameter snapshots.

Type Aliases:

Name Description
AimbatTypes

Union of all AIMBAT models that exist in the database.

Classes:

Name Description
AimbatDataSource

Class to store data source information.

AimbatEvent

Seismic event with origin time, location, and depth.

AimbatEventParameters

Processing parameters common to all seismograms of a particular event.

AimbatEventParametersBase

Base class defining event-level processing parameters for AIMBAT.

AimbatEventParametersSnapshot

Snapshot of processing parameters for a particular event.

AimbatEventQuality

Live quality metrics for a seismic event.

AimbatEventQualityBase

Base class defining event-level quality metrics.

AimbatEventQualitySnapshot

Snapshot of quality metrics for a seismic event.

AimbatEventRead

Read model for AimbatEvent including computed counts.

AimbatNote

Free-text Markdown note attached to an event, station, seismogram, or snapshot.

AimbatSeismogram

Class to store seismogram metadata.

AimbatSeismogramParameters

Processing parameters for a single seismogram.

AimbatSeismogramParametersBase

Base class defining seismogram-level processing parameters for AIMBAT.

AimbatSeismogramParametersSnapshot

Snapshot of processing parameters for a single seismogram.

AimbatSeismogramQuality

Live quality metrics for a single seismogram.

AimbatSeismogramQualityBase

Base class defining seismogram-level quality metrics.

AimbatSeismogramQualitySnapshot

Snapshot of quality metrics for a single seismogram.

AimbatSeismogramRead

Read model for AimbatSeismogram including parameters.

AimbatSnapshot

Container for a point-in-time snapshot of event and seismogram parameters.

AimbatSnapshotRead

Read model for AimbatSnapshot with a seismogram count.

AimbatStation

Recording station with network, location, and channel metadata.

AimbatStationRead

Read model for AimbatStation including parameters.

RichColSpec

Display metadata for a model field rendered in a Rich table.

SeismogramQualityStats

Aggregated seismogram quality statistics for an event or station.

TuiColSpec

Display metadata for a model field rendered in the TUI.

AimbatDataSource

Bases: SQLModel

Class to store data source information.

Parameters:

Name Type Description Default
id UUID

Unique data source ID.

UUID('5c4dbee0-1b05-472f-a8c3-4405147515f5')
sourcename str

Path or name of the data source.

required
datatype DataType

Data type of the data source.

<DataType.SAC: 'sac'>
seismogram_id UUID

Foreign key referencing the parent seismogram.

None

Attributes:

Name Type Description
seismogram AimbatSeismogram

The seismogram this data source belongs to.

Source code in src/aimbat/models/_models.py
class AimbatDataSource(SQLModel, table=True):
    """Class to store data source information."""

    model_config = SQLModelConfig(
        alias_generator=to_camel,
        populate_by_name=True,
    )

    id: uuid.UUID = Field(
        default_factory=uuid.uuid4,
        primary_key=True,
        title="ID",
        description="Unique data source ID.",
        schema_extra={"rich": RichColSpec(style="yellow", highlight=False)},
    )
    sourcename: str = Field(
        title="Source name",
        description="Path or name of the data source.",
    )
    datatype: DataType = Field(
        default=DataType.SAC,
        title="Data type",
        description="Data type of the data source.",
    )
    seismogram_id: uuid.UUID = Field(
        default=None,
        foreign_key="aimbatseismogram.id",
        ondelete="CASCADE",
        title="Seismogram ID",
        description="Foreign key referencing the parent seismogram.",
        schema_extra={"rich": RichColSpec(style="magenta", highlight=False)},
    )
    seismogram: "AimbatSeismogram" = Relationship(back_populates="datasource")
    "The seismogram this data source belongs to."

seismogram class-attribute instance-attribute

seismogram: AimbatSeismogram = Relationship(
    back_populates="datasource"
)

The seismogram this data source belongs to.

AimbatEvent

Bases: SQLModel

Seismic event with origin time, location, and depth.

Parameters:

Name Type Description Default
id UUID

Unique event ID.

UUID('cf40ac72-f000-41f1-968b-a7b847f252c2')
time PydanticTimestamp

Event origin time (UTC).

required
latitude float

Event latitude in degrees.

required
longitude float

Event longitude in degrees.

required
depth float | None

Event depth in metres.

None
last_modified PydanticTimestamp | None

Timestamp of the last parameter modification.

None

Attributes:

Name Type Description
parameters AimbatEventParameters

Event parameters.

quality AimbatEventQuality | None

Live quality metrics for this event.

seismograms list[AimbatSeismogram]

List of seismograms of this event.

snapshots list[AimbatSnapshot]

List of snapshots.

Source code in src/aimbat/models/_models.py
class AimbatEvent(SQLModel, table=True):
    """Seismic event with origin time, location, and depth."""

    model_config = SQLModelConfig(
        alias_generator=to_camel,
        populate_by_name=True,
    )

    id: uuid.UUID = Field(
        default_factory=uuid.uuid4,
        primary_key=True,
        title="ID",
        description="Unique event ID.",
        schema_extra={"rich": RichColSpec(style="yellow", highlight=False)},
    )
    time: PydanticTimestamp = Field(
        unique=True,
        sa_type=SAPandasTimestamp,
        allow_mutation=False,
        title="Time",
        description="Event origin time (UTC).",
    )
    latitude: float = Field(title="Latitude", description="Event latitude in degrees.")
    longitude: float = Field(
        title="Longitude", description="Event longitude in degrees."
    )
    depth: float | None = Field(
        default=None, title="Depth", description="Event depth in metres."
    )
    last_modified: PydanticTimestamp | None = Field(
        default=None,
        sa_type=SAPandasTimestamp,
        title="Last modified",
        description="Timestamp of the last parameter modification.",
    )
    seismograms: list[AimbatSeismogram] = Relationship(
        back_populates="event", cascade_delete=True
    )
    "List of seismograms of this event."

    parameters: AimbatEventParameters = Relationship(
        back_populates="event", cascade_delete=True
    )
    "Event parameters."

    quality: AimbatEventQuality | None = Relationship(
        back_populates="event", cascade_delete=True
    )
    "Live quality metrics for this event."

    snapshots: list[AimbatSnapshot] = Relationship(
        back_populates="event", cascade_delete=True
    )
    "List of snapshots."

    if TYPE_CHECKING:
        # Column properties defined below, but add same default values for type checking purposes
        seismogram_count: int = 0
        station_count: int = 0
        snapshot_count: int = 0

parameters class-attribute instance-attribute

parameters: AimbatEventParameters = Relationship(
    back_populates="event", cascade_delete=True
)

Event parameters.

quality class-attribute instance-attribute

quality: AimbatEventQuality | None = Relationship(
    back_populates="event", cascade_delete=True
)

Live quality metrics for this event.

seismograms class-attribute instance-attribute

seismograms: list[AimbatSeismogram] = Relationship(
    back_populates="event", cascade_delete=True
)

List of seismograms of this event.

snapshots class-attribute instance-attribute

snapshots: list[AimbatSnapshot] = Relationship(
    back_populates="event", cascade_delete=True
)

List of snapshots.

AimbatEventParameters

Bases: AimbatEventParametersBase

Processing parameters common to all seismograms of a particular event.

Parameters:

Name Type Description Default
completed bool

Mark an event as completed.

False
ramp_width float

Width of taper ramp up and down as a fraction of the window length.

0.1
window_pre PydanticNegativeTimedelta

Pre-pick window length in seconds.

Timedelta('-1 days +23:59:45')
window_post PydanticPositiveTimedelta

Post-pick window length in seconds.

Timedelta('0 days 00:00:15')
bandpass_apply bool

Whether to apply bandpass filter to seismograms.

False
bandpass_fmin float

Minimum frequency for bandpass filter in Hz (ignored if bandpass_apply is False).

0.05
bandpass_fmax float

Maximum frequency for bandpass filter in Hz (ignored if bandpass_apply is False).

2.0
min_cc float

Minimum cross-correlation used when automatically de-selecting seismograms.

0.5
mccc_damp float

Damping factor for MCCC algorithm.

0.1
mccc_min_cc float

Minimum correlation coefficient required to include a pair in the MCCC inversion.

0.5
id UUID

Unique ID.

UUID('518e532c-dc2d-4449-9a44-1030f72b3d3e')
event_id UUID

Foreign key referencing the parent event.

None

Methods:

Name Description
check_freq_range

Validate that bandpass_fmax is strictly greater than bandpass_fmin.

validate_iccs_context

Attempt ICCS construction with these parameters if requested.

Attributes:

Name Type Description
event AimbatEvent

The event these parameters belong to.

snapshots list[AimbatEventParametersSnapshot]

Parameter snapshots for this event.

Source code in src/aimbat/models/_models.py
class AimbatEventParameters(AimbatEventParametersBase, table=True):
    """Processing parameters common to all seismograms of a particular event."""

    model_config = SQLModelConfig(
        alias_generator=to_camel,
        populate_by_name=True,
    )

    id: uuid.UUID = Field(
        default_factory=uuid.uuid4,
        primary_key=True,
        title="ID",
        description="Unique ID.",
        schema_extra={
            "rich": RichColSpec(style="yellow", no_wrap=True, highlight=False)
        },
    )
    event_id: uuid.UUID = Field(
        default=None,
        foreign_key="aimbatevent.id",
        ondelete="CASCADE",
        title="Event ID",
        description="Foreign key referencing the parent event.",
        schema_extra={
            "rich": RichColSpec(style="magenta", no_wrap=True, highlight=False)
        },
    )
    event: "AimbatEvent" = Relationship(back_populates="parameters")
    "The event these parameters belong to."
    snapshots: list["AimbatEventParametersSnapshot"] = Relationship(
        back_populates="parameters", cascade_delete=True
    )
    "Parameter snapshots for this event."

event class-attribute instance-attribute

event: AimbatEvent = Relationship(
    back_populates="parameters"
)

The event these parameters belong to.

snapshots class-attribute instance-attribute

snapshots: list[AimbatEventParametersSnapshot] = (
    Relationship(
        back_populates="parameters", cascade_delete=True
    )
)

Parameter snapshots for this event.

check_freq_range

check_freq_range() -> Self

Validate that bandpass_fmax is strictly greater than bandpass_fmin.

Source code in src/aimbat/models/_parameters.py
@model_validator(mode="after")
def check_freq_range(self) -> Self:
    """Validate that `bandpass_fmax` is strictly greater than `bandpass_fmin`."""
    if self.bandpass_fmax <= self.bandpass_fmin:
        raise ValueError("bandpass_fmax must be > bandpass_fmin")
    return self

validate_iccs_context

validate_iccs_context(info: ValidationInfo) -> Self

Attempt ICCS construction with these parameters if requested.

Requires validate_iccs=True and an event instance in the validation context.

Source code in src/aimbat/models/_parameters.py
@model_validator(mode="after")
def validate_iccs_context(self, info: ValidationInfo) -> Self:
    """Attempt ICCS construction with these parameters if requested.

    Requires `validate_iccs=True` and an `event` instance in the validation
    context.
    """
    context = info.context or {}
    if context.get("validate_iccs"):
        event = context.get("event")
        if event:
            from aimbat.core._iccs import validate_iccs_construction

            try:
                validate_iccs_construction(event, parameters=self)
            except Exception as exc:
                raise ValueError(f"ICCS validation failed: {exc}") from exc
    return self

AimbatEventParametersBase

Bases: SQLModel

Base class defining event-level processing parameters for AIMBAT.

This class serves as a base that is inherited by the actual classes that create the database tables. The attributes correspond exactly to the AIMBAT event parameters.

Parameters:

Name Type Description Default
completed bool

Mark an event as completed.

False
ramp_width float

Width of taper ramp up and down as a fraction of the window length.

0.1
window_pre PydanticNegativeTimedelta

Pre-pick window length in seconds.

Timedelta('-1 days +23:59:45')
window_post PydanticPositiveTimedelta

Post-pick window length in seconds.

Timedelta('0 days 00:00:15')
bandpass_apply bool

Whether to apply bandpass filter to seismograms.

False
bandpass_fmin float

Minimum frequency for bandpass filter in Hz (ignored if bandpass_apply is False).

0.05
bandpass_fmax float

Maximum frequency for bandpass filter in Hz (ignored if bandpass_apply is False).

2.0
min_cc float

Minimum cross-correlation used when automatically de-selecting seismograms.

0.5
mccc_damp float

Damping factor for MCCC algorithm.

0.1
mccc_min_cc float

Minimum correlation coefficient required to include a pair in the MCCC inversion.

0.5

Methods:

Name Description
check_freq_range

Validate that bandpass_fmax is strictly greater than bandpass_fmin.

validate_iccs_context

Attempt ICCS construction with these parameters if requested.

Source code in src/aimbat/models/_parameters.py
class AimbatEventParametersBase(SQLModel):
    """Base class defining event-level processing parameters for AIMBAT.

    This class serves as a base that is inherited by the actual classes that
    create the database tables. The attributes correspond exactly to the AIMBAT
    event parameters.
    """

    completed: bool = Field(
        default=False,
        title="Completed",
        description="Mark an event as completed.",
    )

    ramp_width: float = Field(
        default_factory=lambda: settings.ramp_width,
        title="Ramp width",
        description="Width of taper ramp up and down as a fraction of the window length.",
    )

    window_pre: PydanticNegativeTimedelta = Field(
        sa_type=SAPandasTimedelta,
        default_factory=lambda: settings.window_pre,
        title="Window pre",
        description="Pre-pick window length in seconds.",
    )

    window_post: PydanticPositiveTimedelta = Field(
        sa_type=SAPandasTimedelta,
        default_factory=lambda: settings.window_post,
        title="Window post",
        description="Post-pick window length in seconds.",
    )

    bandpass_apply: bool = Field(
        default_factory=lambda: settings.bandpass_apply,
        title="Bandpass apply",
        description="Whether to apply bandpass filter to seismograms.",
    )

    bandpass_fmin: float = Field(
        default_factory=lambda: settings.bandpass_fmin,
        ge=0,
        title="Bandpass f min",
        description="Minimum frequency for bandpass filter in Hz (ignored if `bandpass_apply` is False).",
    )

    bandpass_fmax: float = Field(
        default_factory=lambda: settings.bandpass_fmax,
        gt=0,
        title="Bandpass f max",
        description="Maximum frequency for bandpass filter in Hz (ignored if `bandpass_apply` is False).",
    )

    min_cc: float = Field(
        ge=0.0,
        le=1.0,
        default_factory=lambda: settings.min_cc,
        title="Min CC",
        description="Minimum cross-correlation used when automatically de-selecting seismograms.",
    )

    mccc_damp: float = Field(
        default_factory=lambda: settings.mccc_damp,
        ge=0,
        title="MCCC damp",
        description="Damping factor for MCCC algorithm.",
    )

    mccc_min_cc: float = Field(
        default_factory=lambda: settings.mccc_min_cc,
        ge=0,
        le=1,
        title="MCCC min CC",
        description="Minimum correlation coefficient required to include a pair in the MCCC inversion.",
    )

    @model_validator(mode="after")
    def check_freq_range(self) -> Self:
        """Validate that `bandpass_fmax` is strictly greater than `bandpass_fmin`."""
        if self.bandpass_fmax <= self.bandpass_fmin:
            raise ValueError("bandpass_fmax must be > bandpass_fmin")
        return self

    @model_validator(mode="after")
    def validate_iccs_context(self, info: ValidationInfo) -> Self:
        """Attempt ICCS construction with these parameters if requested.

        Requires `validate_iccs=True` and an `event` instance in the validation
        context.
        """
        context = info.context or {}
        if context.get("validate_iccs"):
            event = context.get("event")
            if event:
                from aimbat.core._iccs import validate_iccs_construction

                try:
                    validate_iccs_construction(event, parameters=self)
                except Exception as exc:
                    raise ValueError(f"ICCS validation failed: {exc}") from exc
        return self

check_freq_range

check_freq_range() -> Self

Validate that bandpass_fmax is strictly greater than bandpass_fmin.

Source code in src/aimbat/models/_parameters.py
@model_validator(mode="after")
def check_freq_range(self) -> Self:
    """Validate that `bandpass_fmax` is strictly greater than `bandpass_fmin`."""
    if self.bandpass_fmax <= self.bandpass_fmin:
        raise ValueError("bandpass_fmax must be > bandpass_fmin")
    return self

validate_iccs_context

validate_iccs_context(info: ValidationInfo) -> Self

Attempt ICCS construction with these parameters if requested.

Requires validate_iccs=True and an event instance in the validation context.

Source code in src/aimbat/models/_parameters.py
@model_validator(mode="after")
def validate_iccs_context(self, info: ValidationInfo) -> Self:
    """Attempt ICCS construction with these parameters if requested.

    Requires `validate_iccs=True` and an `event` instance in the validation
    context.
    """
    context = info.context or {}
    if context.get("validate_iccs"):
        event = context.get("event")
        if event:
            from aimbat.core._iccs import validate_iccs_construction

            try:
                validate_iccs_construction(event, parameters=self)
            except Exception as exc:
                raise ValueError(f"ICCS validation failed: {exc}") from exc
    return self

AimbatEventParametersSnapshot

Bases: AimbatEventParametersBase

Snapshot of processing parameters for a particular event.

Parameters:

Name Type Description Default
completed bool

Mark an event as completed.

False
ramp_width float

Width of taper ramp up and down as a fraction of the window length.

0.1
window_pre PydanticNegativeTimedelta

Pre-pick window length in seconds.

Timedelta('-1 days +23:59:45')
window_post PydanticPositiveTimedelta

Post-pick window length in seconds.

Timedelta('0 days 00:00:15')
bandpass_apply bool

Whether to apply bandpass filter to seismograms.

False
bandpass_fmin float

Minimum frequency for bandpass filter in Hz (ignored if bandpass_apply is False).

0.05
bandpass_fmax float

Maximum frequency for bandpass filter in Hz (ignored if bandpass_apply is False).

2.0
min_cc float

Minimum cross-correlation used when automatically de-selecting seismograms.

0.5
mccc_damp float

Damping factor for MCCC algorithm.

0.1
mccc_min_cc float

Minimum correlation coefficient required to include a pair in the MCCC inversion.

0.5
id UUID

Unique ID.

UUID('cb44d60e-3654-4480-a951-7c868833e10a')
snapshot_id UUID

Foreign key referencing the parent snapshot.

None
parameters_id UUID

Foreign key referencing the source event parameters.

None

Methods:

Name Description
check_freq_range

Validate that bandpass_fmax is strictly greater than bandpass_fmin.

validate_iccs_context

Attempt ICCS construction with these parameters if requested.

Attributes:

Name Type Description
parameters AimbatEventParameters

The event parameters this snapshot was taken from.

snapshot AimbatSnapshot

The snapshot this record belongs to.

Source code in src/aimbat/models/_models.py
class AimbatEventParametersSnapshot(AimbatEventParametersBase, table=True):
    """Snapshot of processing parameters for a particular event."""

    model_config = SQLModelConfig(
        alias_generator=to_camel,
        populate_by_name=True,
    )

    id: uuid.UUID = Field(
        default_factory=uuid.uuid4, primary_key=True, description="Unique ID."
    )
    snapshot_id: uuid.UUID = Field(
        default=None,
        foreign_key="aimbatsnapshot.id",
        ondelete="CASCADE",
        title="Snapshot ID",
        description="Foreign key referencing the parent snapshot.",
    )
    parameters_id: uuid.UUID = Field(
        default=None,
        foreign_key="aimbateventparameters.id",
        ondelete="CASCADE",
        title="Event parameters ID",
        description="Foreign key referencing the source event parameters.",
    )
    snapshot: "AimbatSnapshot" = Relationship(
        back_populates="event_parameters_snapshot"
    )
    "The snapshot this record belongs to."
    parameters: AimbatEventParameters = Relationship(back_populates="snapshots")
    "The event parameters this snapshot was taken from."

parameters class-attribute instance-attribute

parameters: AimbatEventParameters = Relationship(
    back_populates="snapshots"
)

The event parameters this snapshot was taken from.

snapshot class-attribute instance-attribute

snapshot: AimbatSnapshot = Relationship(
    back_populates="event_parameters_snapshot"
)

The snapshot this record belongs to.

check_freq_range

check_freq_range() -> Self

Validate that bandpass_fmax is strictly greater than bandpass_fmin.

Source code in src/aimbat/models/_parameters.py
@model_validator(mode="after")
def check_freq_range(self) -> Self:
    """Validate that `bandpass_fmax` is strictly greater than `bandpass_fmin`."""
    if self.bandpass_fmax <= self.bandpass_fmin:
        raise ValueError("bandpass_fmax must be > bandpass_fmin")
    return self

validate_iccs_context

validate_iccs_context(info: ValidationInfo) -> Self

Attempt ICCS construction with these parameters if requested.

Requires validate_iccs=True and an event instance in the validation context.

Source code in src/aimbat/models/_parameters.py
@model_validator(mode="after")
def validate_iccs_context(self, info: ValidationInfo) -> Self:
    """Attempt ICCS construction with these parameters if requested.

    Requires `validate_iccs=True` and an `event` instance in the validation
    context.
    """
    context = info.context or {}
    if context.get("validate_iccs"):
        event = context.get("event")
        if event:
            from aimbat.core._iccs import validate_iccs_construction

            try:
                validate_iccs_construction(event, parameters=self)
            except Exception as exc:
                raise ValueError(f"ICCS validation failed: {exc}") from exc
    return self

AimbatEventQuality

Bases: AimbatEventQualityBase

Live quality metrics for a seismic event.

One row per event. Updated in place whenever MCCC runs. Fields are None until MCCC has been executed.

Parameters:

Name Type Description Default
mccc_rmse PydanticPositiveTimedelta | None

Root-mean-square error of the MCCC inversion fit across the whole array.

None
id UUID

Unique ID.

UUID('ee121e56-06e3-4e95-ab1f-a6bf61ee6434')
event_id UUID

Foreign key referencing the parent event.

None

Attributes:

Name Type Description
event AimbatEvent

The event these quality metrics belong to.

snapshots list[AimbatEventQualitySnapshot]

Quality snapshots taken from this live record.

Source code in src/aimbat/models/_models.py
class AimbatEventQuality(AimbatEventQualityBase, table=True):
    """Live quality metrics for a seismic event.

    One row per event. Updated in place whenever MCCC runs.
    Fields are `None` until MCCC has been executed.
    """

    model_config = SQLModelConfig(
        alias_generator=to_camel,
        populate_by_name=True,
    )

    id: uuid.UUID = Field(
        default_factory=uuid.uuid4, primary_key=True, description="Unique ID."
    )
    event_id: uuid.UUID = Field(
        default=None,
        foreign_key="aimbatevent.id",
        ondelete="CASCADE",
        title="Event ID",
        description="Foreign key referencing the parent event.",
    )
    event: "AimbatEvent" = Relationship(back_populates="quality")
    "The event these quality metrics belong to."
    snapshots: list["AimbatEventQualitySnapshot"] = Relationship(
        back_populates="quality", cascade_delete=True
    )
    "Quality snapshots taken from this live record."

event class-attribute instance-attribute

event: AimbatEvent = Relationship(back_populates='quality')

The event these quality metrics belong to.

snapshots class-attribute instance-attribute

snapshots: list[AimbatEventQualitySnapshot] = Relationship(
    back_populates="quality", cascade_delete=True
)

Quality snapshots taken from this live record.

AimbatEventQualityBase

Bases: SQLModel

Base class defining event-level quality metrics.

Fields are None when the corresponding algorithm has not been run yet.

Parameters:

Name Type Description Default
mccc_rmse PydanticPositiveTimedelta | None

Root-mean-square error of the MCCC inversion fit across the whole array.

None
Source code in src/aimbat/models/_quality.py
class AimbatEventQualityBase(SQLModel):
    """Base class defining event-level quality metrics.

    Fields are `None` when the corresponding algorithm has not been run yet.
    """

    mccc_rmse: PydanticPositiveTimedelta | None = Field(
        default=None,
        sa_type=SAPandasTimedelta,
        title="MCCC RMSE",
        description="Root-mean-square error of the MCCC inversion fit across the whole array.",
    )

AimbatEventQualitySnapshot

Bases: AimbatEventQualityBase

Snapshot of quality metrics for a seismic event.

Parameters:

Name Type Description Default
mccc_rmse PydanticPositiveTimedelta | None

Root-mean-square error of the MCCC inversion fit across the whole array.

None
id UUID

Unique ID.

UUID('9a312876-6b9a-42ef-bd79-d21f6764e681')
event_quality_id UUID

Foreign key referencing the source event quality.

None
snapshot_id UUID

Foreign key referencing the parent snapshot.

None

Attributes:

Name Type Description
quality AimbatEventQuality

The event quality this snapshot was taken from.

snapshot AimbatSnapshot

The snapshot this record belongs to.

Source code in src/aimbat/models/_models.py
class AimbatEventQualitySnapshot(AimbatEventQualityBase, table=True):
    """Snapshot of quality metrics for a seismic event."""

    model_config = SQLModelConfig(
        alias_generator=to_camel,
        populate_by_name=True,
    )

    id: uuid.UUID = Field(
        default_factory=uuid.uuid4, primary_key=True, description="Unique ID."
    )
    event_quality_id: uuid.UUID = Field(
        default=None,
        foreign_key="aimbateventquality.id",
        ondelete="CASCADE",
        title="Event quality ID",
        description="Foreign key referencing the source event quality.",
    )
    quality: AimbatEventQuality = Relationship(back_populates="snapshots")
    "The event quality this snapshot was taken from."
    snapshot_id: uuid.UUID = Field(
        default=None,
        foreign_key="aimbatsnapshot.id",
        ondelete="CASCADE",
        title="Snapshot ID",
        description="Foreign key referencing the parent snapshot.",
    )
    snapshot: "AimbatSnapshot" = Relationship(back_populates="event_quality_snapshot")
    "The snapshot this record belongs to."

quality class-attribute instance-attribute

quality: AimbatEventQuality = Relationship(
    back_populates="snapshots"
)

The event quality this snapshot was taken from.

snapshot class-attribute instance-attribute

snapshot: AimbatSnapshot = Relationship(
    back_populates="event_quality_snapshot"
)

The snapshot this record belongs to.

AimbatEventRead

Bases: BaseModel

Read model for AimbatEvent including computed counts.

Parameters:

Name Type Description Default
id UUID

Unique identifier for the event

required
short_id str | None

Shortened unique identifier

None
completed bool

Indicates if the event's parameters are marked as completed

required
time PydanticTimestamp

Origin time of the event

required
latitude float

Latitude of the event

required
longitude float

Longitude of the event

required
depth float | None

Depth of the event

required
seismogram_count int

Number of seismograms associated with this event

required
station_count int

Number of stations associated with this event

required
snapshot_count int

Number of snapshots associated with this event

required
last_modified PydanticTimestamp | None

Timestamp of the last modification of this event's parameters

None

Methods:

Name Description
from_event

Create an AimbatEventRead from an AimbatEvent ORM instance.

Source code in src/aimbat/models/_readers.py
class AimbatEventRead(BaseModel):
    """Read model for AimbatEvent including computed counts."""

    model_config = ConfigDict(
        frozen=True,
        alias_generator=to_camel,
        populate_by_name=True,
    )

    id: UUID = Field(
        title="ID",
        description="Unique identifier for the event",
        json_schema_extra={
            "rich": RichColSpec(style="yellow"),  # type: ignore[dict-item]
        },
    )
    short_id: str | None = Field(
        default=None,
        title="Short ID",
        description="Shortened unique identifier",
        json_schema_extra={
            "tui": TuiColSpec(display_title="ID"),  # type: ignore[dict-item]
            "rich": RichColSpec(display_title="ID", style="yellow", highlight=False),  # type: ignore[dict-item]
        },
    )
    completed: bool = Field(
        title="Completed",
        description="Indicates if the event's parameters are marked as completed",
        json_schema_extra={"tui": TuiColSpec(text_align="center")},  # type: ignore[dict-item]
    )
    time: PydanticTimestamp = Field(
        title="Event time",
        description="Origin time of the event",
    )
    latitude: float = Field(
        title="Latitude",
        description="Latitude of the event",
    )
    longitude: float = Field(
        title="Longitude",
        description="Longitude of the event",
    )
    depth: float | None = Field(
        title="Depth",
        description="Depth of the event",
        json_schema_extra={
            "tui": TuiColSpec(display_title="Depth km", formatter=fmt_depth_km),  # type: ignore[dict-item]
            "rich": RichColSpec(
                display_title=r"Depth \[km]", justify="right", formatter=fmt_depth_km
            ),  # type: ignore[dict-item]
        },
    )
    seismogram_count: int = Field(
        title="Seismograms",
        description="Number of seismograms associated with this event",
    )
    station_count: int = Field(
        title="Stations",
        description="Number of stations associated with this event",
    )
    snapshot_count: int = Field(
        title="Snapshots",
        description="Number of snapshots associated with this event",
    )
    last_modified: PydanticTimestamp | None = Field(
        default=None,
        title="Last modified",
        description="Timestamp of the last modification of this event's parameters",
    )

    @classmethod
    def from_event(cls, event: "AimbatEvent", session: "Session | None" = None) -> Self:
        """Create an AimbatEventRead from an AimbatEvent ORM instance."""
        data = event.model_dump()

        if session is not None:
            from aimbat.utils import uuid_shortener

            data["short_id"] = uuid_shortener(session, event)

        data.update(
            {
                "completed": event.parameters.completed if event.parameters else False,
                "seismogram_count": event.seismogram_count or 0,
                "station_count": event.station_count or 0,
                "snapshot_count": event.snapshot_count or 0,
            }
        )
        return cls(**data)

from_event classmethod

from_event(
    event: "AimbatEvent", session: "Session | None" = None
) -> Self

Create an AimbatEventRead from an AimbatEvent ORM instance.

Source code in src/aimbat/models/_readers.py
@classmethod
def from_event(cls, event: "AimbatEvent", session: "Session | None" = None) -> Self:
    """Create an AimbatEventRead from an AimbatEvent ORM instance."""
    data = event.model_dump()

    if session is not None:
        from aimbat.utils import uuid_shortener

        data["short_id"] = uuid_shortener(session, event)

    data.update(
        {
            "completed": event.parameters.completed if event.parameters else False,
            "seismogram_count": event.seismogram_count or 0,
            "station_count": event.station_count or 0,
            "snapshot_count": event.snapshot_count or 0,
        }
    )
    return cls(**data)

AimbatNote

Bases: SQLModel

Free-text Markdown note attached to an event, station, seismogram, or snapshot.

At most one of the four FK fields is set per row. Deletion of the parent record cascades to delete the note via the DB-level foreign key constraint.

Parameters:

Name Type Description Default
id UUID

Unique note ID.

UUID('f696058c-dd29-4968-b94a-eebd4e7bac13')
content str

Note content in Markdown format.

''
event_id UUID | None

Foreign key referencing the parent event.

None
station_id UUID | None

Foreign key referencing the parent station.

None
seismogram_id UUID | None

Foreign key referencing the parent seismogram.

None
snapshot_id UUID | None

Foreign key referencing the parent snapshot.

None
Source code in src/aimbat/models/_models.py
class AimbatNote(SQLModel, table=True):
    """Free-text Markdown note attached to an event, station, seismogram, or snapshot.

    At most one of the four FK fields is set per row. Deletion of the parent
    record cascades to delete the note via the DB-level foreign key constraint.
    """

    model_config = SQLModelConfig(
        alias_generator=to_camel,
        populate_by_name=True,
    )

    __table_args__ = (
        CheckConstraint(
            "(CASE WHEN event_id IS NOT NULL THEN 1 ELSE 0 END"
            " + CASE WHEN station_id IS NOT NULL THEN 1 ELSE 0 END"
            " + CASE WHEN seismogram_id IS NOT NULL THEN 1 ELSE 0 END"
            " + CASE WHEN snapshot_id IS NOT NULL THEN 1 ELSE 0 END) <= 1",
            name="aimbat_note_at_most_one_parent",
        ),
    )

    id: uuid.UUID = Field(
        default_factory=uuid.uuid4,
        primary_key=True,
        description="Unique note ID.",
    )
    content: str = Field(
        default="",
        description="Note content in Markdown format.",
    )
    event_id: uuid.UUID | None = Field(
        default=None,
        foreign_key="aimbatevent.id",
        ondelete="CASCADE",
        description="Foreign key referencing the parent event.",
    )
    station_id: uuid.UUID | None = Field(
        default=None,
        foreign_key="aimbatstation.id",
        ondelete="CASCADE",
        description="Foreign key referencing the parent station.",
    )
    seismogram_id: uuid.UUID | None = Field(
        default=None,
        foreign_key="aimbatseismogram.id",
        ondelete="CASCADE",
        description="Foreign key referencing the parent seismogram.",
    )
    snapshot_id: uuid.UUID | None = Field(
        default=None,
        foreign_key="aimbatsnapshot.id",
        ondelete="CASCADE",
        description="Foreign key referencing the parent snapshot.",
    )

    @model_validator(mode="after")
    def _at_most_one_parent(self) -> "AimbatNote":
        set_count = sum(
            fk is not None
            for fk in (
                self.event_id,
                self.station_id,
                self.seismogram_id,
                self.snapshot_id,
            )
        )
        if set_count > 1:
            raise ValueError(
                "At most one of event_id, station_id, seismogram_id, snapshot_id"
                " may be set on AimbatNote."
            )
        return self

AimbatSeismogram

Bases: SQLModel

Class to store seismogram metadata.

Parameters:

Name Type Description Default
id UUID

Unique seismogram ID.

UUID('30504b86-88bf-4565-92f9-fa3f6e0191bf')
begin_time PydanticTimestamp

Start time of the seismogram.

required
delta PydanticPositiveTimedelta

Sampling interval.

required
t0 PydanticTimestamp

Initial phase arrival pick.

required
station_id UUID

Foreign key referencing the recording station.

None
event_id UUID

Foreign key referencing the parent event.

None
extra dict[Hashable, Any]

Dictionary to store any additional metadata for the seismogram.

<class 'dict'>

Methods:

Name Description
end_time

End time of the seismogram, derived from begin_time, delta, and data length.

Attributes:

Name Type Description
data NDArray[float64]

Seismogram waveform data array.

datasource AimbatDataSource

Data source for the seismogram waveform.

event AimbatEvent

The event this seismogram belongs to.

flip bool

Whether the seismogram should be flipped.

parameters AimbatSeismogramParameters

Processing parameters for this seismogram.

quality AimbatSeismogramQuality | None

Live quality metrics for this seismogram.

select bool

Whether this seismogram should be used for processing.

station AimbatStation

The station that recorded this seismogram.

t1 Timestamp | None

Working phase arrival pick.

Source code in src/aimbat/models/_models.py
class AimbatSeismogram(SQLModel, table=True):
    """Class to store seismogram metadata."""

    model_config = SQLModelConfig(
        alias_generator=to_camel,
        populate_by_name=True,
    )

    id: uuid.UUID = Field(
        default_factory=uuid.uuid4,
        primary_key=True,
        title="ID",
        description="Unique seismogram ID.",
        schema_extra={"rich": RichColSpec(style="yellow", highlight=False)},
    )
    begin_time: PydanticTimestamp = Field(
        sa_type=SAPandasTimestamp,
        title="Begin time",
        description="Start time of the seismogram.",
    )
    delta: PydanticPositiveTimedelta = Field(
        sa_type=SAPandasTimedelta,
        title="Sampling interval",
        description="Sampling interval.",
    )
    t0: PydanticTimestamp = Field(
        sa_type=SAPandasTimestamp,
        title="Initial pick",
        description="Initial phase arrival pick.",
    )
    datasource: AimbatDataSource = Relationship(
        back_populates="seismogram", cascade_delete=True
    )
    "Data source for the seismogram waveform."
    station_id: uuid.UUID = Field(
        default=None,
        foreign_key="aimbatstation.id",
        ondelete="CASCADE",
        title="Station ID",
        description="Foreign key referencing the recording station.",
    )
    station: "AimbatStation" = Relationship(back_populates="seismograms")
    "The station that recorded this seismogram."
    event_id: uuid.UUID = Field(
        default=None,
        foreign_key="aimbatevent.id",
        ondelete="CASCADE",
        title="Event ID",
        description="Foreign key referencing the parent event.",
    )
    extra: dict[Hashable, Any] = Field(
        default_factory=dict,
        sa_column=Column(MutableDict.as_mutable(PickleType)),
        title="Extra metadata",
        description="Dictionary to store any additional metadata for the seismogram.",
    )
    event: "AimbatEvent" = Relationship(back_populates="seismograms")
    "The event this seismogram belongs to."
    parameters: "AimbatSeismogramParameters" = Relationship(
        back_populates="seismogram",
        cascade_delete=True,
    )
    "Processing parameters for this seismogram."
    quality: AimbatSeismogramQuality | None = Relationship(
        back_populates="seismogram",
        cascade_delete=True,
    )
    "Live quality metrics for this seismogram."

    if TYPE_CHECKING:
        # Add same default values for type checking purposes
        # as in AimbatSeismogramParametersBase
        flip: bool = False
        select: bool = True
        t1: Timestamp | None = None
        data: npt.NDArray[np.float64] = np.array([])

        @property
        def end_time(self) -> Timestamp: ...

    else:

        @computed_field
        def end_time(self) -> PydanticTimestamp:
            """End time of the seismogram, derived from begin_time, delta, and data length."""
            if len(self.data) == 0:
                return self.begin_time
            return self.begin_time + self.delta * (len(self.data) - 1)

        @property
        def flip(self) -> bool:
            """Whether the seismogram should be flipped."""
            return self.parameters.flip

        @flip.setter
        def flip(self, value: bool) -> None:
            self.parameters.flip = value

        @property
        def select(self) -> bool:
            """Whether this seismogram should be used for processing."""
            return self.parameters.select

        @select.setter
        def select(self, value: bool) -> None:
            self.parameters.select = value

        @property
        def t1(self) -> Timestamp | None:
            """Working phase arrival pick."""
            return self.parameters.t1

        @t1.setter
        def t1(self, value: Timestamp | None) -> None:
            self.parameters.t1 = value

        @property
        def data(self) -> npt.NDArray[np.float64]:
            """Seismogram waveform data array."""
            if self.datasource is None:
                raise ValueError("Expected a valid datasource name, got None.")
            return read_seismogram_data(
                self.datasource.sourcename, self.datasource.datatype
            )

        @data.setter
        def data(self, value: npt.NDArray[np.float64]) -> None:
            if self.datasource is None:
                raise ValueError("Expected a valid datasource name, got None.")
            write_seismogram_data(
                self.datasource.sourcename, self.datasource.datatype, value
            )

data property writable

Seismogram waveform data array.

datasource class-attribute instance-attribute

datasource: AimbatDataSource = Relationship(
    back_populates="seismogram", cascade_delete=True
)

Data source for the seismogram waveform.

event class-attribute instance-attribute

event: AimbatEvent = Relationship(
    back_populates="seismograms"
)

The event this seismogram belongs to.

flip property writable

flip: bool

Whether the seismogram should be flipped.

parameters class-attribute instance-attribute

parameters: AimbatSeismogramParameters = Relationship(
    back_populates="seismogram", cascade_delete=True
)

Processing parameters for this seismogram.

quality class-attribute instance-attribute

quality: AimbatSeismogramQuality | None = Relationship(
    back_populates="seismogram", cascade_delete=True
)

Live quality metrics for this seismogram.

select property writable

select: bool

Whether this seismogram should be used for processing.

station class-attribute instance-attribute

station: AimbatStation = Relationship(
    back_populates="seismograms"
)

The station that recorded this seismogram.

t1 property writable

t1: Timestamp | None

Working phase arrival pick.

end_time

end_time() -> PydanticTimestamp

End time of the seismogram, derived from begin_time, delta, and data length.

Source code in src/aimbat/models/_models.py
@computed_field
def end_time(self) -> PydanticTimestamp:
    """End time of the seismogram, derived from begin_time, delta, and data length."""
    if len(self.data) == 0:
        return self.begin_time
    return self.begin_time + self.delta * (len(self.data) - 1)

AimbatSeismogramParameters

Bases: AimbatSeismogramParametersBase

Processing parameters for a single seismogram.

Parameters:

Name Type Description Default
flip bool

Whether or not the seismogram should be flipped.

False
select bool

Whether or not this seismogram should be used for processing.

True
t1 PydanticTimestamp | None

Working pick. This pick serves as working as well as output pick. It is changed by: 1. Picking the phase arrival in the stack, 2. Running ICCS, 3. Running MCCC.

None
id UUID

Unique ID.

UUID('2ee96b79-bc62-4897-be98-2742a6fbcc72')
seismogram_id UUID

Foreign key referencing the parent seismogram.

None

Attributes:

Name Type Description
seismogram AimbatSeismogram

The seismogram these parameters belong to.

snapshots list[AimbatSeismogramParametersSnapshot]

Parameter snapshots for this seismogram.

Source code in src/aimbat/models/_models.py
class AimbatSeismogramParameters(AimbatSeismogramParametersBase, table=True):
    """Processing parameters for a single seismogram."""

    model_config = SQLModelConfig(
        alias_generator=to_camel,
        populate_by_name=True,
    )

    id: uuid.UUID = Field(
        default_factory=uuid.uuid4,
        primary_key=True,
        title="ID",
        description="Unique ID.",
    )
    seismogram_id: uuid.UUID = Field(
        default=None,
        foreign_key="aimbatseismogram.id",
        ondelete="CASCADE",
        title="Seismogram ID",
        description="Foreign key referencing the parent seismogram.",
    )

    seismogram: "AimbatSeismogram" = Relationship(back_populates="parameters")
    "The seismogram these parameters belong to."

    snapshots: list["AimbatSeismogramParametersSnapshot"] = Relationship(
        back_populates="parameters", cascade_delete=True
    )
    "Parameter snapshots for this seismogram."

seismogram class-attribute instance-attribute

seismogram: AimbatSeismogram = Relationship(
    back_populates="parameters"
)

The seismogram these parameters belong to.

snapshots class-attribute instance-attribute

snapshots: list[AimbatSeismogramParametersSnapshot] = (
    Relationship(
        back_populates="parameters", cascade_delete=True
    )
)

Parameter snapshots for this seismogram.

AimbatSeismogramParametersBase

Bases: SQLModel

Base class defining seismogram-level processing parameters for AIMBAT.

Parameters:

Name Type Description Default
flip bool

Whether or not the seismogram should be flipped.

False
select bool

Whether or not this seismogram should be used for processing.

True
t1 PydanticTimestamp | None

Working pick. This pick serves as working as well as output pick. It is changed by: 1. Picking the phase arrival in the stack, 2. Running ICCS, 3. Running MCCC.

None
Source code in src/aimbat/models/_parameters.py
class AimbatSeismogramParametersBase(SQLModel):
    """Base class defining seismogram-level processing parameters for AIMBAT."""

    flip: bool = Field(
        default=False,
        title="Flip",
        description="Whether or not the seismogram should be flipped.",
    )

    select: bool = Field(
        default=True,
        title="Select",
        description="Whether or not this seismogram should be used for processing.",
    )

    t1: PydanticTimestamp | None = Field(
        default=None,
        sa_type=SAPandasTimestamp,
        title="T1",
        description=(
            "Working pick. This pick serves as working as well as output pick."
            " It is changed by: 1. Picking the phase arrival in the stack,"
            " 2. Running ICCS, 3. Running MCCC."
        ),
    )

AimbatSeismogramParametersSnapshot

Bases: AimbatSeismogramParametersBase

Snapshot of processing parameters for a single seismogram.

Parameters:

Name Type Description Default
flip bool

Whether or not the seismogram should be flipped.

False
select bool

Whether or not this seismogram should be used for processing.

True
t1 PydanticTimestamp | None

Working pick. This pick serves as working as well as output pick. It is changed by: 1. Picking the phase arrival in the stack, 2. Running ICCS, 3. Running MCCC.

None
id UUID

Unique ID.

UUID('1e449615-9e14-4bd4-ac70-038b808ce4af')
seismogram_parameters_id UUID

Foreign key referencing the source seismogram parameters.

required
snapshot_id UUID

Foreign key referencing the parent snapshot.

None

Attributes:

Name Type Description
parameters AimbatSeismogramParameters

The seismogram parameters this snapshot was taken from.

snapshot AimbatSnapshot

The snapshot this record belongs to.

Source code in src/aimbat/models/_models.py
class AimbatSeismogramParametersSnapshot(AimbatSeismogramParametersBase, table=True):
    """Snapshot of processing parameters for a single seismogram."""

    model_config = SQLModelConfig(
        alias_generator=to_camel,
        populate_by_name=True,
    )

    id: uuid.UUID = Field(
        default_factory=uuid.uuid4, primary_key=True, description="Unique ID."
    )
    seismogram_parameters_id: uuid.UUID = Field(
        foreign_key="aimbatseismogramparameters.id",
        ondelete="CASCADE",
        title="Seismogram parameters ID",
        description="Foreign key referencing the source seismogram parameters.",
    )
    parameters: AimbatSeismogramParameters = Relationship(back_populates="snapshots")
    "The seismogram parameters this snapshot was taken from."
    snapshot_id: uuid.UUID = Field(
        default=None,
        foreign_key="aimbatsnapshot.id",
        ondelete="CASCADE",
        title="Snapshot ID",
        description="Foreign key referencing the parent snapshot.",
    )
    snapshot: "AimbatSnapshot" = Relationship(
        back_populates="seismogram_parameters_snapshots"
    )
    "The snapshot this record belongs to."

parameters class-attribute instance-attribute

parameters: AimbatSeismogramParameters = Relationship(
    back_populates="snapshots"
)

The seismogram parameters this snapshot was taken from.

snapshot class-attribute instance-attribute

snapshot: AimbatSnapshot = Relationship(
    back_populates="seismogram_parameters_snapshots"
)

The snapshot this record belongs to.

AimbatSeismogramQuality

Bases: AimbatSeismogramQualityBase

Live quality metrics for a single seismogram.

One row per seismogram. Updated in place whenever ICCS or MCCC runs. Fields are None until the corresponding algorithm has been executed.

Parameters:

Name Type Description Default
iccs_cc float | None

Pearson cross-correlation coefficient of this seismogram with the ICCS stack.

None
mccc_cc_mean float | None

Mean cross-correlation coefficient from the MCCC run (waveform quality).

None
mccc_cc_std float | None

Standard deviation of cross-correlation coefficients from the MCCC run (waveform consistency).

None
mccc_error PydanticPositiveTimedelta | None

Timing precision (standard error from covariance matrix) from the MCCC run.

None
id UUID

Unique ID.

UUID('7c92d5c7-4154-4629-b874-544d671fd69d')
seismogram_id UUID

Foreign key referencing the parent seismogram.

None

Attributes:

Name Type Description
seismogram AimbatSeismogram

The seismogram these quality metrics belong to.

snapshots list[AimbatSeismogramQualitySnapshot]

Quality snapshots taken from this live record.

Source code in src/aimbat/models/_models.py
class AimbatSeismogramQuality(AimbatSeismogramQualityBase, table=True):
    """Live quality metrics for a single seismogram.

    One row per seismogram. Updated in place whenever ICCS or MCCC runs.
    Fields are `None` until the corresponding algorithm has been executed.
    """

    model_config = SQLModelConfig(
        alias_generator=to_camel,
        populate_by_name=True,
    )

    id: uuid.UUID = Field(
        default_factory=uuid.uuid4, primary_key=True, description="Unique ID."
    )
    seismogram_id: uuid.UUID = Field(
        default=None,
        foreign_key="aimbatseismogram.id",
        ondelete="CASCADE",
        title="Seismogram ID",
        description="Foreign key referencing the parent seismogram.",
    )
    seismogram: "AimbatSeismogram" = Relationship(back_populates="quality")
    "The seismogram these quality metrics belong to."
    snapshots: list["AimbatSeismogramQualitySnapshot"] = Relationship(
        back_populates="quality", cascade_delete=True
    )
    "Quality snapshots taken from this live record."

seismogram class-attribute instance-attribute

seismogram: AimbatSeismogram = Relationship(
    back_populates="quality"
)

The seismogram these quality metrics belong to.

snapshots class-attribute instance-attribute

snapshots: list[AimbatSeismogramQualitySnapshot] = (
    Relationship(
        back_populates="quality", cascade_delete=True
    )
)

Quality snapshots taken from this live record.

AimbatSeismogramQualityBase

Bases: SQLModel

Base class defining seismogram-level quality metrics.

Fields are None when the corresponding algorithm has not been run for this seismogram.

Parameters:

Name Type Description Default
iccs_cc float | None

Pearson cross-correlation coefficient of this seismogram with the ICCS stack.

None
mccc_cc_mean float | None

Mean cross-correlation coefficient from the MCCC run (waveform quality).

None
mccc_cc_std float | None

Standard deviation of cross-correlation coefficients from the MCCC run (waveform consistency).

None
mccc_error PydanticPositiveTimedelta | None

Timing precision (standard error from covariance matrix) from the MCCC run.

None
Source code in src/aimbat/models/_quality.py
class AimbatSeismogramQualityBase(SQLModel):
    """Base class defining seismogram-level quality metrics.

    Fields are `None` when the corresponding algorithm has not been run for
    this seismogram.
    """

    iccs_cc: float | None = Field(
        default=None,
        ge=-1.0,
        le=1.0,
        title="ICCS CC",
        description="Pearson cross-correlation coefficient of this seismogram with the ICCS stack.",
    )

    mccc_cc_mean: float | None = Field(
        default=None,
        ge=0.0,
        le=1.0,
        title="MCCC CC mean",
        description="Mean cross-correlation coefficient from the MCCC run (waveform quality).",
    )

    mccc_cc_std: float | None = Field(
        default=None,
        ge=0.0,
        title="MCCC CC std",
        description="Standard deviation of cross-correlation coefficients from the MCCC run (waveform consistency).",
    )

    mccc_error: PydanticPositiveTimedelta | None = Field(
        default=None,
        sa_type=SAPandasTimedelta,
        title="MCCC error",
        description="Timing precision (standard error from covariance matrix) from the MCCC run.",
    )

AimbatSeismogramQualitySnapshot

Bases: AimbatSeismogramQualityBase

Snapshot of quality metrics for a single seismogram.

Parameters:

Name Type Description Default
iccs_cc float | None

Pearson cross-correlation coefficient of this seismogram with the ICCS stack.

None
mccc_cc_mean float | None

Mean cross-correlation coefficient from the MCCC run (waveform quality).

None
mccc_cc_std float | None

Standard deviation of cross-correlation coefficients from the MCCC run (waveform consistency).

None
mccc_error PydanticPositiveTimedelta | None

Timing precision (standard error from covariance matrix) from the MCCC run.

None
id UUID

Unique ID.

UUID('159c30fb-85de-4c17-adf9-9bfd37846823')
seismogram_quality_id UUID

Foreign key referencing the source seismogram quality.

None
snapshot_id UUID

Foreign key referencing the parent snapshot.

None

Attributes:

Name Type Description
quality AimbatSeismogramQuality

The seismogram quality this snapshot was taken from.

snapshot AimbatSnapshot

The snapshot this record belongs to.

Source code in src/aimbat/models/_models.py
class AimbatSeismogramQualitySnapshot(AimbatSeismogramQualityBase, table=True):
    """Snapshot of quality metrics for a single seismogram."""

    model_config = SQLModelConfig(
        alias_generator=to_camel,
        populate_by_name=True,
    )

    id: uuid.UUID = Field(
        default_factory=uuid.uuid4, primary_key=True, description="Unique ID."
    )
    seismogram_quality_id: uuid.UUID = Field(
        default=None,
        foreign_key="aimbatseismogramquality.id",
        ondelete="CASCADE",
        title="Seismogram quality ID",
        description="Foreign key referencing the source seismogram quality.",
    )
    quality: AimbatSeismogramQuality = Relationship(back_populates="snapshots")
    "The seismogram quality this snapshot was taken from."
    snapshot_id: uuid.UUID = Field(
        default=None,
        foreign_key="aimbatsnapshot.id",
        ondelete="CASCADE",
        title="Snapshot ID",
        description="Foreign key referencing the parent snapshot.",
    )
    snapshot: "AimbatSnapshot" = Relationship(
        back_populates="seismogram_quality_snapshots"
    )
    "The snapshot this record belongs to."

quality class-attribute instance-attribute

quality: AimbatSeismogramQuality = Relationship(
    back_populates="snapshots"
)

The seismogram quality this snapshot was taken from.

snapshot class-attribute instance-attribute

snapshot: AimbatSnapshot = Relationship(
    back_populates="seismogram_quality_snapshots"
)

The snapshot this record belongs to.

AimbatSeismogramRead

Bases: BaseModel

Read model for AimbatSeismogram including parameters.

Parameters:

Name Type Description Default
id UUID

Unique identifier for the seismogram

required
short_id str | None

Shortened unique identifier

None
name str

Name of the seismogram.

required
channel str

Seismogram channel.

required
select bool

Whether the seismogram is selected for processing.

required
flip bool

Whether the seismogram is flipped for processing.

required
delta_t PydanticTimedelta | None

Arrival time residual (observed - predicted) in seconds.

required
mccc_error PydanticTimedelta | None

Uncertainty in the MCCC arrival time residual (observed - predicted) in seconds.

required
iccs_cc float | None

Cross-correlation coefficient with ICCS stack.

required
mccc_cc_mean float | None

Mean cross-correlation coefficient of MCCC cluster.

required
mccc_cc_std float | None

Standard deviation of cross-correlation coefficients in MCCC cluster.

required
event_id UUID

ID of the associated event.

required
short_event_id str | None

Shortened unique identifier for the associated event.

required
Source code in src/aimbat/models/_readers.py
class AimbatSeismogramRead(BaseModel):
    """Read model for AimbatSeismogram including parameters."""

    model_config = ConfigDict(
        frozen=True,
        alias_generator=to_camel,
        populate_by_name=True,
    )

    id: UUID = Field(
        title="ID",
        description="Unique identifier for the seismogram",
        json_schema_extra={
            "rich": RichColSpec(style="yellow", no_wrap=True, highlight=False),  # type: ignore[dict-item]
        },
    )
    short_id: str | None = Field(
        default=None,
        title="Short ID",
        description="Shortened unique identifier",
        json_schema_extra={
            "tui": TuiColSpec(display_title="ID"),  # type: ignore[dict-item]
            "rich": RichColSpec(
                display_title="ID", style="yellow", no_wrap=True, highlight=False
            ),  # type: ignore[dict-item]
        },
    )
    name: str = Field(title="Name", description="Name of the seismogram.")

    channel: str = Field(
        title="Channel",
        description="Seismogram channel.",
    )

    select: bool = Field(
        title="Select",
        description="Whether the seismogram is selected for processing.",
        json_schema_extra={"tui": TuiColSpec(text_align="center")},  # type: ignore[dict-item]
    )

    flip: bool = Field(
        title="Flip",
        description="Whether the seismogram is flipped for processing.",
        json_schema_extra={
            "tui": TuiColSpec(text_align="center", formatter=fmt_flip),  # type: ignore[dict-item]
            "rich": RichColSpec(formatter=fmt_flip),  # type: ignore[dict-item]
        },
    )

    delta_t: PydanticTimedelta | None = Field(
        title="Δt (s)",
        description="Arrival time residual (observed - predicted) in seconds.",
    )

    mccc_error: PydanticTimedelta | None = Field(
        title="MCCC err Δt (s)",
        description="Uncertainty in the MCCC arrival time residual (observed - predicted) in seconds.",
    )

    iccs_cc: float | None = Field(
        title="Stack CC",
        description="Cross-correlation coefficient with ICCS stack.",
    )

    mccc_cc_mean: float | None = Field(
        title="MCCC CC",
        description="Mean cross-correlation coefficient of MCCC cluster.",
    )

    mccc_cc_std: float | None = Field(
        title="MCCC CC std",
        description="Standard deviation of cross-correlation coefficients in MCCC cluster.",
    )

    event_id: UUID = Field(
        title="Event ID",
        description="ID of the associated event.",
        json_schema_extra={
            "rich": RichColSpec(style="magenta", no_wrap=True, highlight=False),  # type: ignore[dict-item]
        },
    )

    short_event_id: str | None = Field(
        title="Short Event ID",
        description="Shortened unique identifier for the associated event.",
        json_schema_extra={
            "rich": RichColSpec(
                display_title="Event ID", style="magenta", no_wrap=True, highlight=False
            ),  # type: ignore[dict-item]
        },
    )

    @classmethod
    def from_seismogram(
        cls, seismogram: AimbatSeismogram, session: Session | None = None
    ) -> Self:
        name = (f"{seismogram.station.network}." or "") + seismogram.station.name

        short_id = None
        short_event_id = None
        if session is not None:
            from aimbat.utils import uuid_shortener

            short_id = uuid_shortener(session, seismogram)
            short_event_id = uuid_shortener(session, seismogram.event)

        delta_t = (
            seismogram.parameters.t1 - seismogram.t0
            if seismogram.parameters.t1
            else None
        )
        return cls(
            id=seismogram.id,
            short_id=short_id,
            name=name,
            channel=seismogram.station.channel,
            select=seismogram.parameters.select,
            flip=seismogram.parameters.flip,
            delta_t=delta_t,
            mccc_error=getattr(seismogram.quality, "mccc_error", None),
            iccs_cc=getattr(seismogram.quality, "iccs_cc", None),
            mccc_cc_mean=getattr(seismogram.quality, "mccc_cc_mean", None),
            mccc_cc_std=getattr(seismogram.quality, "mccc_cc_std", None),
            event_id=seismogram.event_id,
            short_event_id=short_event_id,
        )

AimbatSnapshot

Bases: SQLModel

Container for a point-in-time snapshot of event and seismogram parameters.

The AimbatSnapshot class does not actually save any parameter data. It is used to keep track of the AimbatEventParametersSnapshot and AimbatSeismogramParametersSnapshot instances.

Parameters:

Name Type Description Default
id UUID

Unique ID.

UUID('2cf15c70-5a39-47ae-ad2f-a12cf2cb8475')
time PydanticTimestamp

Timestamp when the snapshot was created.

Timestamp('2026-03-20 16:54:52.583201+0000', tz='UTC')
comment str | None

Optional comment for the snapshot.

None
parameters_hash str | None

SHA-256 hash of event and seismogram parameters at creation time.

None
event_id UUID

Foreign key referencing the parent event.

None

Attributes:

Name Type Description
event AimbatEvent

The event this snapshot belongs to.

event_parameters_snapshot AimbatEventParametersSnapshot

Event parameter snapshot associated with this snapshot.

event_quality_snapshot AimbatEventQualitySnapshot | None

Event quality metric snapshot associated with this snapshot.

seismogram_parameters_snapshots list[AimbatSeismogramParametersSnapshot]

Seismogram parameter snapshots associated with this snapshot.

seismogram_quality_snapshots list[AimbatSeismogramQualitySnapshot]

Seismogram quality metric snapshots associated with this snapshot.

Source code in src/aimbat/models/_models.py
class AimbatSnapshot(SQLModel, table=True):
    """Container for a point-in-time snapshot of event and seismogram parameters.

    The AimbatSnapshot class does not actually save any parameter data.
    It is used to keep track of the AimbatEventParametersSnapshot and
    AimbatSeismogramParametersSnapshot instances.
    """

    model_config = SQLModelConfig(
        alias_generator=to_camel,
        populate_by_name=True,
    )

    id: uuid.UUID = Field(
        default_factory=uuid.uuid4, primary_key=True, description="Unique ID."
    )
    time: PydanticTimestamp = Field(
        default_factory=lambda: Timestamp.now(tz=timezone.utc),
        unique=True,
        allow_mutation=False,
        sa_type=SAPandasTimestamp,
        title="Snapshot time",
        description="Timestamp when the snapshot was created.",
    )
    comment: str | None = Field(
        default=None, description="Optional comment for the snapshot."
    )
    parameters_hash: str | None = Field(
        default=None,
        title="Hash",
        description="SHA-256 hash of event and seismogram parameters at creation time.",
    )
    event_parameters_snapshot: AimbatEventParametersSnapshot = Relationship(
        back_populates="snapshot", cascade_delete=True
    )
    "Event parameter snapshot associated with this snapshot."
    seismogram_parameters_snapshots: list[AimbatSeismogramParametersSnapshot] = (
        Relationship(back_populates="snapshot", cascade_delete=True)
    )
    "Seismogram parameter snapshots associated with this snapshot."
    event_quality_snapshot: AimbatEventQualitySnapshot | None = Relationship(
        back_populates="snapshot", cascade_delete=True
    )
    "Event quality metric snapshot associated with this snapshot."
    seismogram_quality_snapshots: list[AimbatSeismogramQualitySnapshot] = Relationship(
        back_populates="snapshot", cascade_delete=True
    )
    "Seismogram quality metric snapshots associated with this snapshot."
    event_id: uuid.UUID = Field(
        default=None,
        foreign_key="aimbatevent.id",
        ondelete="CASCADE",
        title="Event ID",
        description="Foreign key referencing the parent event.",
    )
    event: "AimbatEvent" = Relationship(back_populates="snapshots")
    "The event this snapshot belongs to."

    if TYPE_CHECKING:
        # defined as column properties below, but add same default values for type checking purposes
        seismogram_count: int = 0
        selected_seismogram_count: int = 0
        flipped_seismogram_count: int = 0

event class-attribute instance-attribute

event: AimbatEvent = Relationship(
    back_populates="snapshots"
)

The event this snapshot belongs to.

event_parameters_snapshot class-attribute instance-attribute

event_parameters_snapshot: AimbatEventParametersSnapshot = (
    Relationship(
        back_populates="snapshot", cascade_delete=True
    )
)

Event parameter snapshot associated with this snapshot.

event_quality_snapshot class-attribute instance-attribute

event_quality_snapshot: (
    AimbatEventQualitySnapshot | None
) = Relationship(
    back_populates="snapshot", cascade_delete=True
)

Event quality metric snapshot associated with this snapshot.

seismogram_parameters_snapshots class-attribute instance-attribute

seismogram_parameters_snapshots: list[
    AimbatSeismogramParametersSnapshot
] = Relationship(
    back_populates="snapshot", cascade_delete=True
)

Seismogram parameter snapshots associated with this snapshot.

seismogram_quality_snapshots class-attribute instance-attribute

seismogram_quality_snapshots: list[
    AimbatSeismogramQualitySnapshot
] = Relationship(
    back_populates="snapshot", cascade_delete=True
)

Seismogram quality metric snapshots associated with this snapshot.

AimbatSnapshotRead

Bases: BaseModel

Read model for AimbatSnapshot with a seismogram count.

Parameters:

Name Type Description Default
id UUID

Unique identifier for the snapshot

required
short_id str | None

Shortened unique identifier

None
time PydanticTimestamp

Timestamp of the snapshot

required
comment str | None

Optional comment for the snapshot

required
seismogram_count int

Total number of seismograms in the snapshot

required
selected_seismogram_count int

Number of selected seismograms in the snapshot

required
flipped_seismogram_count int

Number of flipped seismograms in the snapshot

required
cc_mean float | None

Mean cross-correlation coefficient for this snapshot

None
cc_sem float | None

Standard error of the mean of cross-correlation coefficients for this snapshot

None
mccc bool | None

Whether MCCC parameters are included in this snapshot

None
event_id UUID

ID of the associated event

required
short_event_id str | None

Shortened unique identifier for the associated event

required

Methods:

Name Description
from_snapshot

Create an AimbatSnapshotRead from an AimbatSnapshot ORM instance.

Source code in src/aimbat/models/_readers.py
class AimbatSnapshotRead(BaseModel):
    """Read model for AimbatSnapshot with a seismogram count."""

    model_config = ConfigDict(
        frozen=True,
        alias_generator=to_camel,
        populate_by_name=True,
    )

    id: UUID = Field(
        title="ID",
        description="Unique identifier for the snapshot",
        json_schema_extra={
            "rich": RichColSpec(style="yellow", no_wrap=True, highlight=False),  # type: ignore[dict-item]
        },
    )
    short_id: str | None = Field(
        default=None,
        title="Short ID",
        description="Shortened unique identifier",
        json_schema_extra={
            "tui": TuiColSpec(display_title="ID"),  # type: ignore[dict-item]
            "rich": RichColSpec(
                display_title="ID", style="yellow", no_wrap=True, highlight=False
            ),  # type: ignore[dict-item]
        },
    )
    time: PydanticTimestamp = Field(
        title="Time", description="Timestamp of the snapshot"
    )
    comment: str | None = Field(
        title="Comment", description="Optional comment for the snapshot"
    )
    seismogram_count: int = Field(
        title="Seismograms",
        description="Total number of seismograms in the snapshot",
    )
    selected_seismogram_count: int = Field(
        title="Selected",
        description="Number of selected seismograms in the snapshot",
    )
    flipped_seismogram_count: int = Field(
        title="Flipped",
        description="Number of flipped seismograms in the snapshot",
    )

    cc_mean: float | None = Field(
        default=None,
        title="Stack CC (mean)",
        description="Mean cross-correlation coefficient for this snapshot",
    )

    cc_sem: float | None = Field(
        default=None,
        title="Stack CC (SEM)",
        description="Standard error of the mean of cross-correlation coefficients for this snapshot",
    )

    mccc: bool | None = Field(
        default=None,
        title="MCCC",
        description="Whether MCCC parameters are included in this snapshot",
    )

    event_id: UUID = Field(
        title="Event ID",
        description="ID of the associated event",
        json_schema_extra={
            "rich": RichColSpec(style="magenta", no_wrap=True, highlight=False),  # type: ignore[dict-item]
        },
    )
    short_event_id: str | None = Field(
        title="Short Event ID",
        description="Shortened unique identifier for the associated event",
        json_schema_extra={
            "rich": RichColSpec(
                display_title="Event ID", style="magenta", no_wrap=True, highlight=False
            ),  # type: ignore[dict-item]
        },
    )

    @classmethod
    def from_snapshot(
        cls, snapshot: AimbatSnapshot, session: Session | None = None
    ) -> Self:
        """Create an AimbatSnapshotRead from an AimbatSnapshot ORM instance."""

        short_id = None
        short_event_id = None
        if session is not None:
            from aimbat.utils import uuid_shortener

            short_id = uuid_shortener(session, snapshot)
            short_event_id = uuid_shortener(session, snapshot.event)

        iccs_ccs = [
            q.iccs_cc
            for q in snapshot.seismogram_quality_snapshots
            if q.iccs_cc is not None
        ]
        cc_mean, cc_sem = mean_and_sem(iccs_ccs)
        mccc = bool(snapshot.event_quality_snapshot)

        return cls(
            id=snapshot.id,
            short_id=short_id,
            time=snapshot.time,
            comment=snapshot.comment,
            seismogram_count=snapshot.seismogram_count,
            selected_seismogram_count=snapshot.selected_seismogram_count,
            flipped_seismogram_count=snapshot.flipped_seismogram_count,
            cc_mean=cc_mean,
            cc_sem=cc_sem,
            mccc=mccc,
            event_id=snapshot.event_id,
            short_event_id=short_event_id,
        )

from_snapshot classmethod

from_snapshot(
    snapshot: AimbatSnapshot, session: Session | None = None
) -> Self

Create an AimbatSnapshotRead from an AimbatSnapshot ORM instance.

Source code in src/aimbat/models/_readers.py
@classmethod
def from_snapshot(
    cls, snapshot: AimbatSnapshot, session: Session | None = None
) -> Self:
    """Create an AimbatSnapshotRead from an AimbatSnapshot ORM instance."""

    short_id = None
    short_event_id = None
    if session is not None:
        from aimbat.utils import uuid_shortener

        short_id = uuid_shortener(session, snapshot)
        short_event_id = uuid_shortener(session, snapshot.event)

    iccs_ccs = [
        q.iccs_cc
        for q in snapshot.seismogram_quality_snapshots
        if q.iccs_cc is not None
    ]
    cc_mean, cc_sem = mean_and_sem(iccs_ccs)
    mccc = bool(snapshot.event_quality_snapshot)

    return cls(
        id=snapshot.id,
        short_id=short_id,
        time=snapshot.time,
        comment=snapshot.comment,
        seismogram_count=snapshot.seismogram_count,
        selected_seismogram_count=snapshot.selected_seismogram_count,
        flipped_seismogram_count=snapshot.flipped_seismogram_count,
        cc_mean=cc_mean,
        cc_sem=cc_sem,
        mccc=mccc,
        event_id=snapshot.event_id,
        short_event_id=short_event_id,
    )

AimbatStation

Bases: SQLModel

Recording station with network, location, and channel metadata.

Parameters:

Name Type Description Default
id UUID

Unique station ID.

UUID('b36c4b94-888e-46ff-95ec-c56b33542158')
name str

Station name (SEED station code).

required
network str

Network code.

required
location str

Location code.

required
channel str

Channel code (e.g. BHZ).

required
latitude float

Station latitude in degrees.

required
longitude float

Station longitude in degrees.

required
elevation float | None

Station elevation in metres.

None

Attributes:

Name Type Description
seismograms list[AimbatSeismogram]

Seismograms recorded at this station.

Source code in src/aimbat/models/_models.py
class AimbatStation(SQLModel, table=True):
    """Recording station with network, location, and channel metadata."""

    model_config = SQLModelConfig(
        alias_generator=to_camel,
        populate_by_name=True,
    )

    id: uuid.UUID = Field(
        default_factory=uuid.uuid4,
        primary_key=True,
        title="ID",
        description="Unique station ID.",
        schema_extra={"rich": RichColSpec(style="yellow", highlight=False)},
    )
    name: str = Field(
        allow_mutation=False,
        title="Name",
        description="Station name (SEED station code).",
    )
    network: str = Field(
        allow_mutation=False,
        title="Network",
        description="Network code.",
    )
    location: str = Field(
        allow_mutation=False,
        title="Location",
        description="Location code.",
    )
    channel: str = Field(
        allow_mutation=False,
        title="Channel",
        description="Channel code (e.g. BHZ).",
    )
    latitude: float = Field(
        title="Latitude", description="Station latitude in degrees."
    )
    longitude: float = Field(
        title="Longitude", description="Station longitude in degrees."
    )
    elevation: float | None = Field(
        default=None, title="Elevation", description="Station elevation in metres."
    )
    seismograms: list[AimbatSeismogram] = Relationship(
        back_populates="station", cascade_delete=True
    )
    "Seismograms recorded at this station."

    if TYPE_CHECKING:
        # Column properties defined below, but add same default values for type checking purposes
        seismogram_count: int = 0
        event_count: int = 0

seismograms class-attribute instance-attribute

seismograms: list[AimbatSeismogram] = Relationship(
    back_populates="station", cascade_delete=True
)

Seismograms recorded at this station.

AimbatStationRead

Bases: BaseModel

Read model for AimbatStation including parameters.

Parameters:

Name Type Description Default
id UUID

Unique identifier for the station

required
short_id str | None

Shortened unique identifier

None
network str

Station network code

required
name str

Station name

required
location str | None

Station location code

required
channel str

Station channel code

required
latitude float

Station latitude

required
longitude float

Station longitude

required
elevation float | None

Station elevation

None
cc_mean float | None

Mean cross-correlation coefficient at this station

None
cc_sem float | None

Standard error of the mean of cross-correlation coefficients at this station

None
seismogram_count int

Number of seismograms associated with this station

required
event_count int

Number of unique events recorded at this station

required
Source code in src/aimbat/models/_readers.py
class AimbatStationRead(BaseModel):
    """Read model for AimbatStation including parameters."""

    model_config = ConfigDict(
        frozen=True, alias_generator=to_camel, populate_by_name=True
    )

    id: UUID = Field(
        title="ID",
        description="Unique identifier for the station",
        json_schema_extra={
            "rich": RichColSpec(style="yellow", no_wrap=True, highlight=False),  # type: ignore[dict-item]
        },
    )
    short_id: str | None = Field(
        default=None,
        title="Short ID",
        description="Shortened unique identifier",
        json_schema_extra={
            "tui": TuiColSpec(display_title="ID"),  # type: ignore[dict-item]
            "rich": RichColSpec(
                display_title="ID", style="yellow", no_wrap=True, highlight=False
            ),  # type: ignore[dict-item]
        },
    )
    network: str = Field(title="Network", description="Station network code")
    name: str = Field(title="Name", description="Station name")
    location: str | None = Field(title="Location", description="Station location code")
    channel: str = Field(title="Channel", description="Station channel code")
    latitude: float = Field(title="Latitude", description="Station latitude")
    longitude: float = Field(title="Longitude", description="Station longitude")
    elevation: float | None = Field(
        default=None,
        title="Elevation",
        description="Station elevation",
        json_schema_extra={"tui": TuiColSpec(formatter=lambda x: str(int(x)))},  # type: ignore[dict-item]
    )

    cc_mean: float | None = Field(
        default=None,
        title="Stack CC (mean)",
        description="Mean cross-correlation coefficient at this station",
    )

    cc_sem: float | None = Field(
        default=None,
        title="Stack CC (SEM)",
        description="Standard error of the mean of cross-correlation coefficients at this station",
    )

    seismogram_count: int = Field(
        title="Seismogram count",
        description="Number of seismograms associated with this station",
    )

    event_count: int = Field(
        title="Event count",
        description="Number of unique events recorded at this station",
    )

    @classmethod
    def from_station(
        cls,
        station: AimbatStation,
        session: Session | None = None,
    ) -> Self:
        data = station.model_dump()

        if session is not None:
            from aimbat.utils import uuid_shortener

            data["short_id"] = uuid_shortener(session, station)
            iccs_ccs = tuple(
                seis.quality.iccs_cc
                for seis in station.seismograms
                if seis.quality is not None and seis.quality.iccs_cc is not None
            )
            data["cc_mean"], data["cc_sem"] = mean_and_sem(iccs_ccs)

        data.update(
            {
                "seismogram_count": station.seismogram_count or 0,
                "event_count": station.event_count or 0,
            }
        )
        return cls(**data)

RichColSpec

Bases: BaseModel

Display metadata for a model field rendered in a Rich table.

Attach to a field via json_schema_extra={"rich": RichColSpec(...)}. Only attributes that differ from the field's defaults need to be set.

Attributes:

Name Type Description

Parameters:

Name Type Description Default
display_title str | None
None
justify Literal['left', 'center', 'right'] | None
None
style str | None
None
no_wrap bool | None
None
highlight bool | None
True
formatter Callable[list, str] | None
None
Source code in src/aimbat/models/_format.py
class RichColSpec(BaseModel):
    """Display metadata for a model field rendered in a Rich table.

    Attach to a field via `json_schema_extra={"rich": RichColSpec(...)}`.
    Only attributes that differ from the field's defaults need to be set.

    Attributes:
        display_title: Override for the column header shown in the Rich table.
            If `None`, the field's `title` is used instead.
        justify: Horizontal alignment for cell values in this column.
            Maps to `rich.table.Column.justify`. If `None`, no explicit
            alignment is applied.
        style: Style string for the column (e.g. "bold magenta").
        no_wrap: If `True`, cell values in this column will not wrap.
        highlight: If `True`, enables Rich's automatic syntax highlighting for
            values in this column. If `False`, disables it. If `None`, no
            explicit setting is applied.
        formatter: Custom formatter for cell values. Called with the raw field
            value (guaranteed non-`None`) and must return a display string. If
            `None`, a generic fallback is used instead.
    """

    model_config = ConfigDict(frozen=True, arbitrary_types_allowed=True)

    display_title: str | None = None
    justify: Literal["left", "center", "right"] | None = None
    style: str | None = None
    no_wrap: bool | None = None
    highlight: bool | None = True
    formatter: Callable[[Any], str] | None = None

SeismogramQualityStats

Bases: BaseModel

Aggregated seismogram quality statistics for an event or station.

Built from live quality records. All mean fields are None when no seismograms in the group have quality data. SEM fields are None when fewer than two values are available. mccc_rmse is only populated by from_event and from_snapshot; it is always None for from_station. event_id is populated by from_event and from_snapshot; it is always None for from_station.

Parameters:

Name Type Description Default
event_id UUID | None
None
snapshot_id UUID | None
None
station_id UUID | None
None
count int
required
cc_mean float | None
None
cc_mean_sem float | None
None
mccc_cc_mean float | None
None
mccc_cc_mean_sem float | None
None
mccc_cc_std float | None
None
mccc_cc_std_sem float | None
None
mccc_error PydanticTimedelta | None
None
mccc_error_sem PydanticTimedelta | None
None
mccc_rmse PydanticTimedelta | None
None

Methods:

Name Description
from_event

Build quality stats from live quality records for an event.

from_snapshot

Build quality stats from the frozen quality records in a snapshot.

from_station

Build quality stats from live quality records for a station.

Source code in src/aimbat/models/_readers.py
class SeismogramQualityStats(BaseModel):
    """Aggregated seismogram quality statistics for an event or station.

    Built from live quality records. All mean fields are `None` when no
    seismograms in the group have quality data. SEM fields are `None` when
    fewer than two values are available. `mccc_rmse` is only populated by
    `from_event` and `from_snapshot`; it is always `None` for `from_station`.
    `event_id` is populated by `from_event` and `from_snapshot`; it is always
    `None` for `from_station`.
    """

    model_config = ConfigDict(frozen=True)

    event_id: UUID | None = Field(
        default=None,
        title="Event ID",
        json_schema_extra={
            "rich": RichColSpec(style="magenta", no_wrap=True, highlight=False),  # type: ignore[dict-item]
        },
    )
    snapshot_id: UUID | None = Field(
        default=None,
        title="Snapshot ID",
        json_schema_extra={
            "rich": RichColSpec(style="magenta", no_wrap=True, highlight=False),  # type: ignore[dict-item]
        },
    )
    station_id: UUID | None = Field(
        default=None,
        title="Station ID",
        json_schema_extra={
            "rich": RichColSpec(style="magenta", no_wrap=True, highlight=False),  # type: ignore[dict-item]
        },
    )
    count: int = Field(title="Count")
    cc_mean: float | None = Field(default=None, title="ICCS CC mean")
    cc_mean_sem: float | None = Field(default=None, title="ICCS CC mean SEM")
    mccc_cc_mean: float | None = Field(default=None, title="MCCC CC mean")
    mccc_cc_mean_sem: float | None = Field(default=None, title="MCCC CC mean SEM")
    mccc_cc_std: float | None = Field(default=None, title="MCCC CC std")
    mccc_cc_std_sem: float | None = Field(default=None, title="MCCC CC std SEM")
    mccc_error: PydanticTimedelta | None = Field(default=None, title="MCCC error")
    mccc_error_sem: PydanticTimedelta | None = Field(
        default=None, title="MCCC error SEM"
    )
    mccc_rmse: PydanticTimedelta | None = Field(default=None, title="MCCC RMSE")

    @classmethod
    def from_event(cls, event: AimbatEvent) -> Self:
        """Build quality stats from live quality records for an event.

        Aggregates `iccs_cc` and MCCC metrics across all seismograms that
        have live quality records. `mccc_rmse` is taken from the event-level
        quality record.

        Warning:
            This method may trigger lazy-loading of `event.seismograms` and
            each `seis.quality` relationship. For performance, query `event`
            with `selectinload` for `seismograms` and their nested `quality`
            relationships before calling.

        Args:
            event: The event whose seismograms' live quality to aggregate.

        Returns:
            Aggregated quality statistics.
        """
        logger.debug(f"Building quality stats for event {event.id}.")
        qualities = [
            seis.quality for seis in event.seismograms if seis.quality is not None
        ]

        cc_mean, cc_mean_sem = mean_and_sem(
            [q.iccs_cc for q in qualities if q.iccs_cc is not None]
        )
        mccc_cc_mean, mccc_cc_mean_sem = mean_and_sem(
            [q.mccc_cc_mean for q in qualities if q.mccc_cc_mean is not None]
        )
        mccc_cc_std, mccc_cc_std_sem = mean_and_sem(
            [q.mccc_cc_std for q in qualities if q.mccc_cc_std is not None]
        )
        mccc_error, mccc_error_sem = mean_and_sem_timedelta(
            [q.mccc_error for q in qualities if q.mccc_error is not None]
        )
        mccc_rmse = event.quality.mccc_rmse if event.quality is not None else None

        return cls(
            event_id=event.id,
            count=len(event.seismograms),
            cc_mean=cc_mean,
            cc_mean_sem=cc_mean_sem,
            mccc_cc_mean=mccc_cc_mean,
            mccc_cc_mean_sem=mccc_cc_mean_sem,
            mccc_cc_std=mccc_cc_std,
            mccc_cc_std_sem=mccc_cc_std_sem,
            mccc_error=mccc_error,
            mccc_error_sem=mccc_error_sem,
            mccc_rmse=mccc_rmse,
        )

    @classmethod
    def from_station(cls, station: AimbatStation) -> Self:
        """Build quality stats from live quality records for a station.

        Aggregates `iccs_cc` and MCCC metrics across all seismograms at the
        station that have live quality records.

        Warning:
            This method may trigger lazy-loading of `station.seismograms` and
            each `seis.quality` relationship. For performance, query `station`
            with `selectinload` for `seismograms` and their nested `quality`
            relationships before calling.

        Args:
            station: The station whose seismograms' live quality to aggregate.

        Returns:
            Aggregated quality statistics. `mccc_rmse` is always `None`.
        """
        logger.debug(f"Building quality stats for station {station.id}.")
        qualities = [
            seis.quality for seis in station.seismograms if seis.quality is not None
        ]

        cc_mean, cc_mean_sem = mean_and_sem(
            [q.iccs_cc for q in qualities if q.iccs_cc is not None]
        )
        mccc_cc_mean, mccc_cc_mean_sem = mean_and_sem(
            [q.mccc_cc_mean for q in qualities if q.mccc_cc_mean is not None]
        )
        mccc_cc_std, mccc_cc_std_sem = mean_and_sem(
            [q.mccc_cc_std for q in qualities if q.mccc_cc_std is not None]
        )
        mccc_error, mccc_error_sem = mean_and_sem_timedelta(
            [q.mccc_error for q in qualities if q.mccc_error is not None]
        )

        return cls(
            station_id=station.id,
            count=len(station.seismograms),
            cc_mean=cc_mean,
            cc_mean_sem=cc_mean_sem,
            mccc_cc_mean=mccc_cc_mean,
            mccc_cc_mean_sem=mccc_cc_mean_sem,
            mccc_cc_std=mccc_cc_std,
            mccc_cc_std_sem=mccc_cc_std_sem,
            mccc_error=mccc_error,
            mccc_error_sem=mccc_error_sem,
            mccc_rmse=None,
        )

    @classmethod
    def from_snapshot(cls, snapshot: AimbatSnapshot) -> Self:
        """Build quality stats from the frozen quality records in a snapshot.

        Aggregates from `AimbatSeismogramQualitySnapshot` records rather than
        live quality, so the result reflects the state at snapshot time.

        Args:
            snapshot: The snapshot to aggregate quality from.

        Returns:
            Aggregated quality statistics.
        """
        logger.debug(f"Building quality stats for snapshot {snapshot.id}.")
        records = snapshot.seismogram_quality_snapshots

        cc_mean, cc_mean_sem = mean_and_sem(
            [r.iccs_cc for r in records if r.iccs_cc is not None]
        )
        mccc_cc_mean, mccc_cc_mean_sem = mean_and_sem(
            [r.mccc_cc_mean for r in records if r.mccc_cc_mean is not None]
        )
        mccc_cc_std, mccc_cc_std_sem = mean_and_sem(
            [r.mccc_cc_std for r in records if r.mccc_cc_std is not None]
        )
        mccc_error, mccc_error_sem = mean_and_sem_timedelta(
            [r.mccc_error for r in records if r.mccc_error is not None]
        )
        eq = snapshot.event_quality_snapshot
        mccc_rmse = eq.mccc_rmse if eq is not None else None

        return cls(
            event_id=snapshot.event_id,
            snapshot_id=snapshot.id,
            count=snapshot.seismogram_count,
            cc_mean=cc_mean,
            cc_mean_sem=cc_mean_sem,
            mccc_cc_mean=mccc_cc_mean,
            mccc_cc_mean_sem=mccc_cc_mean_sem,
            mccc_cc_std=mccc_cc_std,
            mccc_cc_std_sem=mccc_cc_std_sem,
            mccc_error=mccc_error,
            mccc_error_sem=mccc_error_sem,
            mccc_rmse=mccc_rmse,
        )

from_event classmethod

from_event(event: AimbatEvent) -> Self

Build quality stats from live quality records for an event.

Aggregates iccs_cc and MCCC metrics across all seismograms that have live quality records. mccc_rmse is taken from the event-level quality record.

Warning

This method may trigger lazy-loading of event.seismograms and each seis.quality relationship. For performance, query event with selectinload for seismograms and their nested quality relationships before calling.

Parameters:

Name Type Description Default
event AimbatEvent

The event whose seismograms' live quality to aggregate.

required

Returns:

Type Description
Self

Aggregated quality statistics.

Source code in src/aimbat/models/_readers.py
@classmethod
def from_event(cls, event: AimbatEvent) -> Self:
    """Build quality stats from live quality records for an event.

    Aggregates `iccs_cc` and MCCC metrics across all seismograms that
    have live quality records. `mccc_rmse` is taken from the event-level
    quality record.

    Warning:
        This method may trigger lazy-loading of `event.seismograms` and
        each `seis.quality` relationship. For performance, query `event`
        with `selectinload` for `seismograms` and their nested `quality`
        relationships before calling.

    Args:
        event: The event whose seismograms' live quality to aggregate.

    Returns:
        Aggregated quality statistics.
    """
    logger.debug(f"Building quality stats for event {event.id}.")
    qualities = [
        seis.quality for seis in event.seismograms if seis.quality is not None
    ]

    cc_mean, cc_mean_sem = mean_and_sem(
        [q.iccs_cc for q in qualities if q.iccs_cc is not None]
    )
    mccc_cc_mean, mccc_cc_mean_sem = mean_and_sem(
        [q.mccc_cc_mean for q in qualities if q.mccc_cc_mean is not None]
    )
    mccc_cc_std, mccc_cc_std_sem = mean_and_sem(
        [q.mccc_cc_std for q in qualities if q.mccc_cc_std is not None]
    )
    mccc_error, mccc_error_sem = mean_and_sem_timedelta(
        [q.mccc_error for q in qualities if q.mccc_error is not None]
    )
    mccc_rmse = event.quality.mccc_rmse if event.quality is not None else None

    return cls(
        event_id=event.id,
        count=len(event.seismograms),
        cc_mean=cc_mean,
        cc_mean_sem=cc_mean_sem,
        mccc_cc_mean=mccc_cc_mean,
        mccc_cc_mean_sem=mccc_cc_mean_sem,
        mccc_cc_std=mccc_cc_std,
        mccc_cc_std_sem=mccc_cc_std_sem,
        mccc_error=mccc_error,
        mccc_error_sem=mccc_error_sem,
        mccc_rmse=mccc_rmse,
    )

from_snapshot classmethod

from_snapshot(snapshot: AimbatSnapshot) -> Self

Build quality stats from the frozen quality records in a snapshot.

Aggregates from AimbatSeismogramQualitySnapshot records rather than live quality, so the result reflects the state at snapshot time.

Parameters:

Name Type Description Default
snapshot AimbatSnapshot

The snapshot to aggregate quality from.

required

Returns:

Type Description
Self

Aggregated quality statistics.

Source code in src/aimbat/models/_readers.py
@classmethod
def from_snapshot(cls, snapshot: AimbatSnapshot) -> Self:
    """Build quality stats from the frozen quality records in a snapshot.

    Aggregates from `AimbatSeismogramQualitySnapshot` records rather than
    live quality, so the result reflects the state at snapshot time.

    Args:
        snapshot: The snapshot to aggregate quality from.

    Returns:
        Aggregated quality statistics.
    """
    logger.debug(f"Building quality stats for snapshot {snapshot.id}.")
    records = snapshot.seismogram_quality_snapshots

    cc_mean, cc_mean_sem = mean_and_sem(
        [r.iccs_cc for r in records if r.iccs_cc is not None]
    )
    mccc_cc_mean, mccc_cc_mean_sem = mean_and_sem(
        [r.mccc_cc_mean for r in records if r.mccc_cc_mean is not None]
    )
    mccc_cc_std, mccc_cc_std_sem = mean_and_sem(
        [r.mccc_cc_std for r in records if r.mccc_cc_std is not None]
    )
    mccc_error, mccc_error_sem = mean_and_sem_timedelta(
        [r.mccc_error for r in records if r.mccc_error is not None]
    )
    eq = snapshot.event_quality_snapshot
    mccc_rmse = eq.mccc_rmse if eq is not None else None

    return cls(
        event_id=snapshot.event_id,
        snapshot_id=snapshot.id,
        count=snapshot.seismogram_count,
        cc_mean=cc_mean,
        cc_mean_sem=cc_mean_sem,
        mccc_cc_mean=mccc_cc_mean,
        mccc_cc_mean_sem=mccc_cc_mean_sem,
        mccc_cc_std=mccc_cc_std,
        mccc_cc_std_sem=mccc_cc_std_sem,
        mccc_error=mccc_error,
        mccc_error_sem=mccc_error_sem,
        mccc_rmse=mccc_rmse,
    )

from_station classmethod

from_station(station: AimbatStation) -> Self

Build quality stats from live quality records for a station.

Aggregates iccs_cc and MCCC metrics across all seismograms at the station that have live quality records.

Warning

This method may trigger lazy-loading of station.seismograms and each seis.quality relationship. For performance, query station with selectinload for seismograms and their nested quality relationships before calling.

Parameters:

Name Type Description Default
station AimbatStation

The station whose seismograms' live quality to aggregate.

required

Returns:

Type Description
Self

Aggregated quality statistics. mccc_rmse is always None.

Source code in src/aimbat/models/_readers.py
@classmethod
def from_station(cls, station: AimbatStation) -> Self:
    """Build quality stats from live quality records for a station.

    Aggregates `iccs_cc` and MCCC metrics across all seismograms at the
    station that have live quality records.

    Warning:
        This method may trigger lazy-loading of `station.seismograms` and
        each `seis.quality` relationship. For performance, query `station`
        with `selectinload` for `seismograms` and their nested `quality`
        relationships before calling.

    Args:
        station: The station whose seismograms' live quality to aggregate.

    Returns:
        Aggregated quality statistics. `mccc_rmse` is always `None`.
    """
    logger.debug(f"Building quality stats for station {station.id}.")
    qualities = [
        seis.quality for seis in station.seismograms if seis.quality is not None
    ]

    cc_mean, cc_mean_sem = mean_and_sem(
        [q.iccs_cc for q in qualities if q.iccs_cc is not None]
    )
    mccc_cc_mean, mccc_cc_mean_sem = mean_and_sem(
        [q.mccc_cc_mean for q in qualities if q.mccc_cc_mean is not None]
    )
    mccc_cc_std, mccc_cc_std_sem = mean_and_sem(
        [q.mccc_cc_std for q in qualities if q.mccc_cc_std is not None]
    )
    mccc_error, mccc_error_sem = mean_and_sem_timedelta(
        [q.mccc_error for q in qualities if q.mccc_error is not None]
    )

    return cls(
        station_id=station.id,
        count=len(station.seismograms),
        cc_mean=cc_mean,
        cc_mean_sem=cc_mean_sem,
        mccc_cc_mean=mccc_cc_mean,
        mccc_cc_mean_sem=mccc_cc_mean_sem,
        mccc_cc_std=mccc_cc_std,
        mccc_cc_std_sem=mccc_cc_std_sem,
        mccc_error=mccc_error,
        mccc_error_sem=mccc_error_sem,
        mccc_rmse=None,
    )

TuiColSpec dataclass

Display metadata for a model field rendered in the TUI.

Attach to a field via json_schema_extra={"tui": TuiColSpec(...)}. Only attributes that differ from the field's defaults need to be set.

Attributes:

Name Type Description

Parameters:

Name Type Description Default
display_title str | None
None
text_align Literal['left', 'center', 'right'] | None
None
formatter Formatter[Any] | None
None
Source code in src/aimbat/models/_format.py
@dataclass(frozen=True)
class TuiColSpec:
    """Display metadata for a model field rendered in the TUI.

    Attach to a field via `json_schema_extra={"tui": TuiColSpec(...)}`.
    Only attributes that differ from the field's defaults need to be set.

    Attributes:
        display_title: Override for the column header shown in the TUI table.
            If `None`, the field's `title` is used instead.
        text_align: Horizontal alignment for cell values in this column.
            Maps to `rich.text.Text.justify`. If `None`, no explicit alignment
            is applied and the DataTable renders with its default (left).
        formatter: Custom formatter for cell values. Called with the raw field
            value (guaranteed non-`None`) and must return a display string. If
            `None`, the generic `tui_fmt` fallback is used instead.
    """

    display_title: str | None = None
    text_align: Literal["left", "center", "right"] | None = None
    formatter: Formatter[Any] | None = None