pysmo
Documentation: https://docs.pysmo.org
Source Code: https://github.com/pysmo/pysmo
Pysmo leverages modern Python (3.12+) to bring type safety and clean abstractions to seismology code.
Protocol-based design
Pysmo defines its core types —
Seismogram, Station,
Event — as Protocol classes (structural
subtyping). Any object that has the right attributes satisfies the protocol;
no inheritance required. This means pysmo functions work with your classes
out of the box, as long as they look like the right type.
Runtime validation with attrs and beartype
Pysmo's concrete "Mini" classes (e.g. MiniSeismogram) are built with
attrs and validated at runtime by
beartype. Fields carry validators and
converters — a datetime without tzinfo=UTC or a negative sampling
interval is rejected immediately, not silently propagated.
Separation of storage and processing
File formats bundle many fields together. Pysmo splits them into narrowly
scoped protocol types so that processing code stays simple, testable, and
reusable across projects. A SAC object exposes .seismogram, .station,
and .event views — each satisfying the corresponding protocol — without
coupling your analysis code to the SAC format.
Quick Start
from pysmo import Seismogram, MiniSeismogram
from pysmo.classes import SAC
from pysmo.functions import detrend, normalize, resample
# Read a SAC file — access seismogram data via protocol-typed views
sac = SAC.from_file("myfile.sac")
seis = sac.seismogram # satisfies the Seismogram protocol
# Process using built-in functions
detrend(seis)
normalize(seis)
resample(seis, seis.delta * 2)
# Write a function that works with ANY Seismogram implementation
def print_info(seismogram: Seismogram) -> None:
print(f"Start: {seismogram.begin_time}")
print(f"Samples: {len(seismogram)}, dt: {seismogram.delta}")
print_info(seis) # works with SacSeismogram
# ...or create a lightweight seismogram from scratch
mini = MiniSeismogram(data=seis.data, delta=seis.delta, begin_time=seis.begin_time)
print_info(mini) # works with MiniSeismogram too — same protocol
Key Concepts
| Concept | Python Feature | Pysmo Example |
|---|---|---|
| Structural subtyping | Protocol + @runtime_checkable |
Seismogram, Station, Event |
| Validated data classes | attrs with field validators |
MiniStation(name="STA", latitude=48.2, ...) |
| Runtime type checking | beartype + Annotated types |
PositiveTimedelta, UnitFloat |
| Generic functions | PEP 695 type parameters | def crop[T: Seismogram](...) -> T |
| Format adapters | Descriptors + __set_name__ |
SAC.seismogram, SAC.station |
Why Protocols?
Traditional seismology libraries often require you to inherit from a base class or convert data into a library-specific format. Pysmo takes a different approach: functions accept any object that satisfies a protocol. You can use pysmo functions with your own classes — no subclassing, no format conversion, no vendor lock-in.
# Your own class — no pysmo base class needed
@dataclass
class MySeismogram:
begin_time: datetime
delta: timedelta
data: np.ndarray
def __len__(self) -> int:
return len(self.data)
# pysmo functions just work
from pysmo.functions import detrend
detrend(MySeismogram(...)) # satisfies the Seismogram protocol