Theoretical Background

FDS and PSD in fdscore

In fdscore, a Fatigue Damage Spectrum (FDS) is represented by FDSResult(f, damage), where f is the oscillator natural-frequency grid and damage contains the predicted accumulated damage for each SDOF oscillator. A Power Spectral Density (PSD) is represented by PSDResult(f, psd) as a one-sided acceleration PSD defined over frequency.

The main workflows in the package use an SDOF oscillator bank defined by SDOFParams together with an S-N model defined by SNParams. Damping is consistently written internally as

\[\zeta = \frac{1}{2Q}\]

which is the relationship used throughout the implementation when converting the quality factor q to damping ratio.

Time-Domain FDS

compute_fds_time(...) reconstructs each oscillator response in the frequency domain using an FFT transfer matrix and then applies rainflow counting and Miner damage accumulation. The implementation in fdscore.rainflow_damage works on reversal points and sums full-cycle and half-cycle contributions in the form

\[D = \sum_j \phi_j \frac{S_j^k}{C}\]

where \phi_j is the cycle fraction (0.5 or 1.0 in the code), S_j is the effective cycle load, k is the S-N slope exponent, and C = N_{ref} S_{ref}^k.

The compute_fds_time(...) documentation also makes explicit that, for fixed k, the absolute damage level scales globally as

\[\frac{p_{scale}^k}{N_{ref} S_{ref}^k}\]

which changes the magnitude of damage(f) without changing its relative shape.

Spectral FDS via Dirlik

compute_fds_spectral_psd(...) builds the spectral response of each oscillator from a base-acceleration PSD. Its implementation explicitly uses

\[P_{resp}(f; f_0) = p_{scale}^2 \, |H(f; f_0)|^2 \, P_{base}(f)\]

and

\[damage(f_0) = \frac{duration_s}{life(f_0)}\]

where life(f_0) is obtained from the internal Dirlik implementation. The companion route compute_fds_spectral_time(...) first estimates the PSD with Welch (compute_psd_welch(...)) and then applies the same spectral fatigue evaluation.

The implementation explicitly notes that Dirlik is a spectral fatigue approximation, not the same algorithm as counting rainflow cycles directly on a finite time-history realization. Agreement between the two routes is therefore approximate rather than exact.

Henderson-Piersol Closed-Form Inversion

The module fdscore.inverse_closed_form documents the closed-form derivation used to invert an FDS into an equivalent acceleration PSD. The function compute_damage_to_dp_factor(...) presents the damage expression

\[D = \left(\frac{\nu_0 T}{C}\right)\left(\sqrt{2}\,\sigma_S\right)^b \Gamma\left(1 + \frac{b}{2}\right)\]

and the approximate relationship between the stress standard deviation and the input acceleration PSD

\[\sigma_S \approx p_{scale}\sqrt{\frac{G_{aa}(f_n)}{16 \pi f_n \zeta}}\]

under the assumptions of narrowband Gaussian response, stationarity, SDOF behavior, and light damping.

Those assumptions are central to the interpretation of the inversion:

  • the excitation is treated as stationary Gaussian;

  • the response is modeled as linear and SDOF-dominated;

  • light damping is assumed, typically zeta < 0.1;

  • the response peaks follow the Rayleigh law associated with narrowband Gaussian response;

  • the input PSD is treated as locally flat across the oscillator half-power bandwidth.

In practical terms, the closed-form route returns the PSD of an equivalent stationary Gaussian environment that would reproduce the same damage under those assumptions. This is especially important when the input FDS was originally computed from time-domain rainflow on a real signal rather than from a purely spectral Gaussian model.

From there, the code defines the Damage Potential (DP) as

\[DP(f_n) = f_n T \left[\frac{G_{aa}(f_n)}{f_n \zeta}\right]^{b/2}\]

and implements its algebraic inverse as

\[G_{aa}(f_n) = f_n \zeta \left[\frac{DP(f_n)}{f_n T}\right]^{2/b}\]

invert_fds_closed_form(...) first converts damage to DP(f) using the proportionality factor K such that Damage = K * DP, and then applies the inverse above. The implementation restricts this inversion to metric="pv", using pseudo-velocity as a numerically convenient proxy for relative velocity at resonance.

Iterative Inversion Engines

The package also implements two iterative engines that synthesize a PSD to match a target FDS:

  • invert_fds_iterative_spectral(...) uses compute_fds_spectral_psd(...) as its internal predictor and updates the PSD multiplicatively from an influence matrix derived from the SDOF transfer function in the spectral domain.

  • invert_fds_iterative_time(...) synthesizes Gaussian time histories from a candidate PSD with synthesize_time_from_psd(...), evaluates the resulting FDS with compute_fds_time(...), and corrects the PSD from the mismatch between predicted and target damage.

In both cases, the output remains a one-sided acceleration PSD defined on the user-provided f_psd_hz grid, while the returned metadata records the convergence history and the parameters effectively used by each engine.

For a practical comparison of when the closed-form and iterative engines are methodologically aligned with the chosen FDS path, see workflows/inversion.md.