pysmo.tools.noise
Generate synthetic noise matching the naturally observed amplitude spectrum.
Examples:
Given the spectral amplitude in observed seismic noise on Earth is not flat (i.e. not consisting of white noise), it makes sense to calculate more realistic noise for synthetic-data experiments and Monte Carlo studies.
In this example, random noise seismograms are generated from three different noise models. These are Peterson's NHNM (red), NLNM (blue), and an interpolated model that lies between the two (green).
Example source code
# fmt: off
"""Generate synthetic noise from Peterson's noise models and check it.
A random seismogram is generated from each of Peterson's New Low Noise Model
(NLNM), New High Noise Model (NHNM), and an interpolated model half way
between them. The power spectral density of every generated seismogram is
plotted on top of the model it was drawn from, showing that `generate_noise`
reproduces the target spectrum.
"""
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from pysmo.tools.noise import generate_noise, peterson
from pysmo.tools.signal import psd
# name, Peterson noise level, plot colour, model label, model line style
NOISE_LEVELS = [
("low", 0.0, "b", "NLNM", "dashed"),
("mid", 0.5, "g", "interpolated model", "dashdot"),
("high", 1.0, "r", "NHNM", "dotted"),
]
def main(outfile: str = "peterson.png") -> None:
# A long series gives the PSD estimate enough frequency resolution to
# track the model curves; npts must be a multiple of 4 for the Welch
# segment and FFT lengths below.
npts = 200_000
delta = pd.Timedelta(seconds=0.1)
nperseg, nfft = npts // 4, npts // 2
times = np.linspace(0.0, npts * delta.total_seconds(), npts)
fig, axes = plt.subplot_mosaic(
[
["low", "mid", "high"],
["psd", "psd", "psd"],
["psd", "psd", "psd"],
["psd", "psd", "psd"],
],
figsize=(13, 9),
layout="tight",
)
psd_ax = axes["psd"]
for name, level, color, model_label, model_style in NOISE_LEVELS:
model = peterson(noise_level=level)
seismogram = generate_noise(npts=npts, model=model, delta=delta)
freqs, power = psd(seismogram, nperseg=nperseg, nfft=nfft)
wave_ax = axes[name]
wave_ax.plot(times, seismogram.data, color, linewidth=0.2)
wave_ax.set_xlim(times[0], times[-1])
wave_ax.set_xlabel("Time [s]")
wave_ax.locator_params(axis="x", nbins=4)
# Skip the zero-frequency bin before converting to period.
psd_ax.plot(
1 / freqs[1:],
10 * np.log10(power[1:]),
color,
linewidth=0.5,
label=f"generated {name} noise",
)
psd_ax.plot(
model.T.total_seconds(),
model.psd,
color=plt.rcParams["text.color"], # legible in light and dark themes
linewidth=1,
linestyle=model_style,
label=model_label,
)
axes["low"].set_ylabel("Ground acceleration")
periods = peterson(0.0).T.total_seconds()
psd_ax.set_xscale("log")
psd_ax.set_xlim(periods[0], periods[-1])
psd_ax.set_xlabel("Period [s]")
psd_ax.set_ylabel("Power spectral density [dB]")
psd_ax.legend()
fig.savefig(outfile, transparent=True)
plt.show()
if __name__ == "__main__":
main()
# fmt: on
Classes:
| Name | Description |
|---|---|
NoiseModel |
A seismic noise model: power spectral density against period. |
Functions:
| Name | Description |
|---|---|
generate_noise |
Generate a random seismogram from a noise model. |
peterson |
Interpolate a noise model between Peterson's[^1] NLNM and NHNM. |
NoiseModel
dataclass
A seismic noise model: power spectral density against period.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
psd
|
NDArray[floating]
|
Power spectral density of ground acceleration [dB]. |
(lambda: array([]))()
|
T
|
TimedeltaIndex
|
Period. |
(lambda: TimedeltaIndex([]))()
|
Examples:
A NoiseModel freezes its own copy of psd, so the array passed in
remains writeable and independent of the stored copy:
>>> import numpy as np
>>> import pandas as pd
>>> from pysmo.tools.noise import NoiseModel
>>> psd = np.array([-150.0, -140.0, -130.0])
>>> T = pd.to_timedelta([1.0, 10.0, 100.0], unit="s")
>>> model = NoiseModel(psd=psd, T=T)
>>> model.psd
array([-150., -140., -130.])
>>> psd[0] = -999.0 # does not affect the NoiseModel's own copy
>>> model.psd[0]
np.float64(-150.0)
>>>
Methods:
| Name | Description |
|---|---|
__post_init__ |
Validate |
Source code in src/pysmo/tools/noise.py
__post_init__
Validate psd/T have matching lengths, then freeze a copy of psd.
Source code in src/pysmo/tools/noise.py
generate_noise
generate_noise(
model: NoiseModel,
npts: int,
delta: Timedelta = delta,
begin_time: Timestamp = begin_time,
return_velocity: bool = False,
) -> MiniSeismogram
Generate a random seismogram from a noise model.
The amplitude spectrum is prescribed by the noise model and random phases
are drawn uniformly from [-π, π]. The combined spectrum is transformed
back to the time domain via an inverse FFT. Internally the computation is
performed on the next power-of-two length greater than or equal to npts
to ensure an efficient FFT; the central npts samples are then extracted
from the result to avoid edge artefacts near the start and end of the
generated buffer.
Each call is an independent random draw; the function takes no seed.
Reproducible output requires replacing numpy.random.default_rng in the
caller.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
NoiseModel
|
Noise model used to compute seismic noise. |
required |
npts
|
int
|
Number of samples in the output seismogram. |
required |
delta
|
Timedelta
|
Sampling interval of the generated noise. |
delta
|
begin_time
|
Timestamp
|
Begin time of the output seismogram. |
begin_time
|
return_velocity
|
bool
|
If |
False
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Returns:
| Type | Description |
|---|---|
MiniSeismogram
|
Seismogram containing the generated noise. Data represent ground |
MiniSeismogram
|
acceleration (arbitrary units matching the noise model's PSD) unless |
MiniSeismogram
|
|
Examples:
>>> import pandas as pd
>>> from pysmo import MiniSeismogram
>>> from pysmo.tools.noise import peterson, generate_noise
>>> model = peterson(0.0)
>>> noise = generate_noise(model=model, npts=64, delta=pd.Timedelta(seconds=1.0))
>>> isinstance(noise, MiniSeismogram)
True
>>> len(noise.data)
64
>>>
Source code in src/pysmo/tools/noise.py
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 | |
peterson
peterson(noise_level: float) -> NoiseModel
Interpolate a noise model between Peterson's1 NLNM and NHNM.
The New Low Noise Model (NLNM) and New High Noise Model (NHNM) bound the range of seismic background noise observed on Earth.
-
Peterson, Jon R. Observations and Modeling of Seismic Background Noise. Report, 93–322, 1993, https://doi.org/10.3133/ofr93322. USGS Publications Warehouse. ↩
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
noise_level
|
float
|
Determines the noise level of the generated noise model. A noise level of 0 returns the NLNM, 1 returns the NHNM, and anything > 0 and < 1 returns an interpolated model that lies between the NLNM and NHNM. |
required |
Returns:
| Type | Description |
|---|---|
NoiseModel
|
Noise model. |
Examples:
>>> from pysmo.tools.noise import peterson, NLNM, NHNM
>>> peterson(0.0) == NLNM
True
>>> peterson(1.0) == NHNM
True
>>> model = peterson(0.5)
>>> model.psd[0] # midpoint of NLNM and NHNM at the shortest period
np.float64(-129.75)
>>>

