Source code for structdyn.sdf.response_spectrum

import numpy as np
import pandas as pd
from structdyn.sdf.sdf import SDF


[docs] class ResponseSpectrum: """ Generates a linear elastic response spectrum for a given ground motion. This class calculates the displacement, pseudo-velocity, and pseudo-acceleration response spectra for a range of periods and a specified damping ratio. """ def __init__(self, periods, damping_ratio, ground_motion, method="interpolation"): """ Initializes the ResponseSpectrum generator. Parameters ---------- periods : array-like An array of periods (in seconds) for which to compute the response spectrum. damping_ratio : float The damping ratio (ji) for the SDF systems. Must be between 0 and 1. ground_motion : GroundMotion A GroundMotion object representing the input ground motion. method : str, optional The numerical integration method to use for the time history analysis, by default "interpolation". """ self.periods = np.asarray(periods, dtype=float) self.ji = damping_ratio self.gm = ground_motion self.method = method if not (0 <= damping_ratio < 1): raise ValueError("Damping ratio must be between 0 and 1") if np.any(self.periods < 0): raise ValueError("Periods must be positive")
[docs] def compute(self): """ Computes the response spectrum. This method iterates through the specified periods, creates an SDF system for each, and calculates the peak displacement response to the ground motion. It then computes the pseudo-velocity and pseudo-acceleration spectra. Returns ------- pandas.DataFrame A DataFrame containing the response spectrum, with columns for period (T), spectral displacement (Sd), pseudo-spectral velocity (pSv), and pseudo-spectral acceleration in g (pSa (g)). """ periods = self.periods.copy() include_zero = np.any(periods == 0) # Remove zero from numerical loop periods_nonzero = periods[periods > 0] Sd_list = [] for T in periods_nonzero: # Natural frequency w_n = 2 * np.pi / T m = 1.0 # Use unit mass k = m * w_n**2 sdf = SDF(m, k, ji=self.ji) results = sdf.find_response_ground_motion(self.gm, method=self.method) u = results["displacement"] Sd = np.max(np.abs(u)) Sd_list.append(Sd) Sd = np.array(Sd_list) # Compute pseudo spectral values w_n_array = 2 * np.pi / periods_nonzero pSv = w_n_array * Sd pSa = (w_n_array**2) * Sd / 9.81 # convert to g df = pd.DataFrame({"T": periods_nonzero, "Sd": Sd, "pSv": pSv, "pSa (g)": pSa}) # Handle T = 0 case explicitly if include_zero: PGA = np.max(np.abs(self.gm.acc_g)) # already in g if acc_g is in g zero_row = pd.DataFrame( {"T": [0.0], "Sd": [0.0], "pSv": [0.0], "pSa (g)": [PGA]} ) df = pd.concat([zero_row, df], ignore_index=True) return df.sort_values("T").reset_index(drop=True)