Fatigue damage equivalent PSDs

This and other notebooks are available here: https://github.com/twmacro/pyyeti/tree/master/docs/tutorials.

First, do some imports:

import numpy as np
import matplotlib.pyplot as plt
from pyyeti import psd, fdepsd
import scipy.signal as signal

Some settings specifically for the jupyter notebook.

%matplotlib inline
plt.rcParams['figure.figsize'] = [6.4, 4.8]
plt.rcParams['figure.dpi'] = 150.

Generate a signal with flat content from 20 to 50 Hz

TF = 60  # make a 60 second signal
spec = np.array([[20, 1], [50, 1]])
sig, sr, t = psd.psd2time(spec, ppc=10, fstart=20,
                          fstop=50, df=1/TF,
                          winends=dict(portion=10),
                          gettime=True)
plt.plot(t, sig)
plt.title(r'Input Signal - Specification Level = '
           '1.0 $g^{2}$/Hz')
plt.xlabel('Time (sec)')
h = plt.ylabel('Acceleration (g)')
../_images/temp_fatigue_6_0.png

Compute fatigue damage equivalent PSDs using two different methods

One will use absolute-acceleration and the other will use pseudo-velocity. The plots will compare the G1 and G8 outputs. The output of the fdepsd function is a SimpleNamespace with several items; the main output is in the .psd member which has G1, G2, G4, G8 and G12 as columns in an array.

freq = np.arange(20., 50.1)
Q = 10
fde_acce = fdepsd.fdepsd(sig, sr, freq, Q)
fde_pvelo = fdepsd.fdepsd(sig, sr, freq, Q, resp='pvelo')
plt.plot(freq, fde_acce.psd['G1'], label='G1, Accel-based')
plt.plot(freq, fde_pvelo.psd['G1'], label='G1, PVelo-based')
plt.plot(freq, fde_acce.psd['G8'], label='G8, Accel-based')
plt.plot(freq, fde_pvelo.psd['G8'], label='G8, PVelo-based')
plt.title('G1 and G8 PSD Comparison')
plt.xlabel('Freq (Hz)')
plt.ylabel(r'PSD ($g^{2}$/Hz)')
h = plt.legend(loc='best')
../_images/temp_fatigue_9_0.png

Compute fatigue damage equivalent PSDs to compare with standard PSDs

Form envelope over Q’s of 10, 25, 50.

psdenv = 0
freq = np.arange(20., 50.1)
for q in (10, 25, 50):
    fde = fdepsd.fdepsd(sig, sr, freq, q)
    psdenv = np.fmax(psdenv, fde.psd)

Compute standard Welch periodogram and use psd.psdmod for comparison

f, p = signal.welch(sig, sr, nperseg=sr)
f2, p2 = psd.psdmod(sig, sr, nperseg=sr, timeslice=4, tsoverlap=0.5)

Plot fatigue damage equivalents and the standard PSDs

spec = np.array(spec).T
plt.plot(*spec, 'k--', lw=2.5, label='Spec')
plt.plot(f, p, label='Welch PSD')
plt.plot(f2, p2, label='PSDmod')
psdenv.rename(
    columns={i: i + ' Env'
             for i in psdenv.columns}).plot.line(ax=plt.gca())
plt.xlim(20, 50)
plt.title('PSD Comparison')
plt.xlabel('Freq (Hz)')
plt.ylabel(r'PSD ($g^{2}$/Hz)')
h = plt.legend(loc='upper left',
               bbox_to_anchor=(1.02, 1.),
               borderaxespad=0.)
../_images/temp_fatigue_15_0.png

Compare cycle count results for 30 Hz to theoretical

This will be for Q = 50 since that was the last one run above.

Frq = freq[np.searchsorted(freq, 30)]

# First, plot counts vs. amplitude**2 from the data:
plt.semilogy(fde.binamps.loc[Frq]**2,
             fde.count.loc[Frq],
             label='Data')

# Compute theoretical curve: (use flight time here (TF),
# not test time (T0))

Amax2 = 2 * fde.var.loc[Frq] * np.log(Frq * TF)
plt.plot([0, Amax2], [Frq * TF, 1], label='Theory')

# Next, plot amp**2 vs total count for each PSD:
y1 = fde.count.loc[Frq, 0]
peakamp = fde.peakamp.loc[Frq]
for j, lbl in enumerate(fde.peakamp.columns):
    plt.plot([0, peakamp.iloc[j]**2], [y1, 1], label=lbl)
plt.title('Bin Count Check for Q=50, Freq=30 Hz')
plt.xlabel(r'$Amp^2$')
plt.ylabel('Count')
plt.grid(True)
h = plt.legend(loc='best')
../_images/temp_fatigue_17_0.png