Source code for specmod.smoothing.konno_ohmachi

"""Konno & Ohmachi (1998) smoothing.

A constant-width window in log-frequency: narrow at low frequency, wide at
high. That is what makes it the convention in engineering seismology — it
avoids over-smoothing the low-frequency band that constrains the long-period
level, while still suppressing scatter in the high-frequency tail.

The window is ``[sin(b log10(f/fc)) / (b log10(f/fc))]^4``, with ``b`` the
bandwidth. Smaller ``b`` smooths harder; 40 is the conventional value.

ObsPy already implements this and is already a hard dependency, so this is a
thin adapter rather than a reimplementation. It keeps the frequency axis
unchanged, unlike :class:`~specmod.smoothing.log_bins.LogBinner`, which is
often what you want before fitting on the original grid.

References
----------
Konno, K. & Ohmachi, T. (1998). Ground-motion characteristics estimated from
spectral ratio between horizontal and vertical components of microtremor.
*BSSA* 88(1), 228-241.
"""

from __future__ import annotations

from dataclasses import dataclass, replace

import numpy as np
from obspy.signal.konnoohmachismoothing import konno_ohmachi_smoothing

from ..core.spectrum import Spectrum
from .base import record_smoothing

__all__ = ["KonnoOhmachi"]


[docs] @dataclass(frozen=True) class KonnoOhmachi: """Konno-Ohmachi smoothing at fixed log-frequency bandwidth. Parameters ---------- bandwidth The ``b`` coefficient. Smaller values smooth more aggressively. count Number of times to apply the window. More than one is occasionally used for very noisy spectra. normalize Normalise each window to unit area. Without this the smoothed amplitudes are biased low where the window is truncated at the edges of the frequency axis. """ bandwidth: float = 40.0 count: int = 1 normalize: bool = True name: str = "konno_ohmachi" def __post_init__(self) -> None: if self.bandwidth <= 0: raise ValueError(f"bandwidth must be positive, got {self.bandwidth}") if self.count < 1: raise ValueError(f"count must be at least 1, got {self.count}") def smooth(self, spectrum: Spectrum) -> Spectrum: # ObsPy needs float32 or float64 and rejects anything else outright. smoothed = konno_ohmachi_smoothing( np.asarray(spectrum.amp, dtype=np.float64), np.asarray(spectrum.freq, dtype=np.float64), bandwidth=self.bandwidth, count=self.count, normalize=self.normalize, ) return replace( spectrum, amp=np.asarray(smoothed, dtype=np.float64), meta=record_smoothing( spectrum.meta, self.name, bandwidth=self.bandwidth, count=self.count, normalize=self.normalize, ), )