Source code for pygmm.kishida_2017

"""Kishida (2017, :cite:`kishida17`) conditional spectrum."""

import numpy as np

from .baker_jayaram_2008 import calc_correls
from .types import ArrayLike


[docs] def calc_cond_mean_spectrum_vector( periods: ArrayLike, ln_psas: ArrayLike, ln_stds: ArrayLike, ln_psas_cond: ArrayLike ) -> (np.ndarray, np.ndarray): """Kishida (2017, :cite:`kishida17`) conditional spectrum. Conditional mean spectrum vector (CMSV) by Kishida (2017, :cite:`kishida17`) is specifying the target spectral acceleration at multiple periods, rather than the single conditioning period by Cornell and Baker (2008). If this approach is used for a single period, then the resulting spectrum is the same as computed by Cornell and Baker (2008) -- implemented by :func:`~pygmm.baker_jayaram_2008.calc_cond_mean_spectrum`. Parameters ---------- periods : array_like Spectral periods of the response spectrum [sec]. This array must be increasing. ln_psas : array_like Natural logarithm of the spectral acceleration. Same length as `periods`. ln_stds : array_like Logarithmic standard deviation of the spectral acceleration. Same length as `periods`. ln_psas_cond : :class:`np.ma.masked_array` The vector of conditioning spectral accelerations. This is a masked array with the same length as `periods`. Masked values are not used for defining the CMSV. Returns ------- ln_psas_cmsv : :class:`np.ndarray` Natural logarithm of the conditional mean spectral accelerations. ln_stds_cmsv : :class:`np.ndarray` Logarithmic standard deviation of the conditional mean spectral acceleration. Raises ------ ValueError If `periods` are monotonically increasing. """ periods = np.asarray(periods) ln_psas = np.asarray(ln_psas) ln_stds = np.asarray(ln_stds) if not np.all(np.diff(periods) > 0): raise ValueError("`periods` must be increasing") mask = ln_psas_cond.mask # Group the periods into those not being conditioned upon, and those used # for the conditioning. See Equations (14), (15), and (16) periods_grouped = np.r_[periods[mask], periods[~mask]] # Standard deviation matrix. Named V^{1/2} in Kishida, Equation (8) mat_ln_std = np.diag(np.r_[ln_stds[mask], ln_stds[~mask]]) # Correlation matrix, Equation (9) mat_correls = np.vstack( [calc_correls(periods_grouped, period_cond) for period_cond in periods_grouped] ).T # Covariance matrix, Equation (7) mat_covar = mat_ln_std @ mat_correls @ mat_ln_std # Extract the submatrices, Equation (6) n = np.ma.count_masked(ln_psas_cond) mat_covar_11 = mat_covar[0:n, 0:n] mat_covar_22 = mat_covar[n:, n:] mat_covar_12 = mat_covar[0:n, n:] mat_covar_21 = mat_covar[n:, 0:n] mat_covar_22_inv = np.linalg.inv(mat_covar_22) # Conditional mean value, Equation (3) mat_total_resids = (ln_psas_cond[~mask] - ln_psas[~mask]).T ln_psas_cmsv = np.r_[ ln_psas[mask] + np.ravel(mat_covar_12 @ mat_covar_22_inv @ mat_total_resids), ln_psas_cond[~mask].data, ] # Compute standard deviation, Equation (4) ln_stds_cmsv = np.r_[ np.sqrt( np.diag(mat_covar_11 - (mat_covar_12 @ mat_covar_22_inv @ mat_covar_21)) ), np.zeros(ln_psas_cond.count()), ] # Sort to the original period indices indices = np.argsort(periods_grouped) ln_psas_cmsv = ln_psas_cmsv[indices] ln_stds_cmsv = ln_stds_cmsv[indices] return ln_psas_cmsv, ln_stds_cmsv