Source code for pygmm.slope_displacement.rigid_block

"""Newmark rigid sliding-block analysis of an acceleration time series.

Uses :mod:`numba` to compile the integration when it is installed
(``pip install pygmm[slope]``); otherwise a pure-Python loop is used, which
gives the same result more slowly.
"""

from __future__ import annotations

import numpy as np
import numpy.typing as npt
import scipy.constants
from scipy.integrate import cumulative_trapezoid

__author__ = "Albert Kottke"

try:
    from numba import njit
except ImportError:  # pragma: no cover - exercised only without numba

[docs] def njit(func): # type: ignore[no-redef] """Return the function unchanged, as numba is not installed.""" return func
@njit def _calc_block_velocity(time_step, accels, yield_coef): """Compute the velocity of a sliding block. The calculation is adapted from Slammer's source code [1]_. However, the displacement is computed outside of the numerical integration. .. [1] https://github.com/mjibson/slammer/blob/master/programs/slammer/analysis/Decoupled.java#L135 Parameters ---------- time_step : float Time step [s]. accels : array_like Acceleration time series [cm/s²]. yield_coef : float Yield acceleration of the slope [cm/s²]. Returns ------- :class:`numpy.ndarray` Velocity time series of the sliding mass [cm/s]. """ vels = np.zeros_like(accels) sliding = False for i in range(1, len(accels)): accel_avg = 0.5 * (accels[i] + accels[i - 1]) if sliding: vels[i] = vels[i - 1] + (accel_avg - yield_coef) * time_step else: vels[i] = 0 # Check if sliding if sliding: if vels[i] <= 0: sliding = False vels[i] = 0 else: if accels[i] > yield_coef: sliding = True return vels
[docs] def calc_rigid_disp( time_step: float, accels: npt.ArrayLike, yield_coef: float, invert: bool = False ) -> dict[str, npt.NDArray[np.floating]]: """Compute the displacement and velocity of a rigid sliding mass. Parameters ---------- time_step : float Time step [s]. accels : array_like Acceleration time series [g]. yield_coef : float Yield coefficient of the slope [g]. invert : bool, default False If the acceleration time series should be inverted. Returns ------- dict ``"disps"``: displacement time series of the sliding mass [cm], and ``"vels"``: velocity time series of the sliding mass [cm/s]. """ # Convert into cm/s/s scale = scipy.constants.g / scipy.constants.centi yield_accel = yield_coef * scale # Only invert the acceleration time series if invert: scale *= -1 # Don't update the array in place accels = np.asarray(accels, dtype=float) * scale vels = _calc_block_velocity(time_step, accels, yield_accel) disps = np.r_[0, cumulative_trapezoid(vels, dx=time_step)] return {"disps": disps, "vels": vels}