Source code for FLife.freq_domain.narrowband

import numpy as np
from scipy.special import gamma
from scipy.integrate import quad

[docs] class Narrowband(object): """Class for fatigue life estimation using frequency domain method by Miles [1] / Bendat and Piersol [2]. References ---------- [1] John W. Miles. On structural fatigue under random loading. Journal of the Aeronautical Sciences, 21(11):753{762, 1954. [2] Julius S. Bendat and Allen G. Piersol. Measurement and Analysis of Random Data. Wiley, 1966. [3] Aleš Zorman and Janko Slavič and Miha Boltežar. Vibration fatigue by spectral methods—A review with open-source support, Mechanical Systems and Signal Processing, 2023, https://doi.org/10.1016/j.ymssp.2023.110149 Example ------- Import modules, define time- and frequency-domain data >>> import FLife >>> import pyExSi as es >>> import numpy as np >>> from matplotlib import pyplot as plt >>> # time-domain data >>> N = 2 ** 16 # number of data points of time signal >>> fs = 2048 # sampling frequency [Hz] >>> t = np.arange(0, N) / fs # time vector >>> # frequency-domain data >>> M = N // 2 + 1 # number of data points of frequency vector >>> freq = np.arange(0, M, 1) * fs / N # frequency vector >>> PSD = es.get_psd(freq, 20, 60, variance = 5) # one-sided flat-shaped PSD Get Gaussian stationary signal, instantiate SpectralData object and plot PSD >>> rg = np.random.default_rng(123) # random generator seed >>> x = es.random_gaussian(N, PSD, fs, rg) # Gaussian stationary signal >>> sd = FLife.SpectralData(input=x, dt=1/fs) # SpectralData instance >>> plt.plot(sd.psd[:,0], sd.psd[:,1]) >>> plt.xlabel('Frequency [Hz]') >>> plt.ylabel('PSD') Define S-N curve parameters and get fatigue-life estimatate >>> C = 1.8e+22 # S-N curve intercept [MPa**k] >>> k = 7.3 # S-N curve inverse slope [/] >>> nb = FLife.Narrowband(sd) >>> print(f'Fatigue life: {nb.get_life(C,k):.3e} s.') Define stress vector and depict stress peak PDF >>> s = np.arange(0,np.max(x),.01) >>> plt.plot(s,nb.get_PDF(s)) >>> plt.xlabel('Stress [MPa]') >>> plt.ylabel('PDF') """
[docs] def __init__(self, spectral_data): """Get needed values from reference object. :param spectral_data: Instance of class SpectralData """ self.spectral_data = spectral_data
[docs] def get_PDF(self,s): """Returns cycle PDF(Probability Density Function) as a function of stress s. :param s: numpy.ndarray Stress vector. :return: function pdf(s) """ m0 = self.spectral_data.moments[0] def pdf(s): px = (s / m0) * np.exp( - s**2 / (2 * m0)) return px return pdf(s)
[docs] def damage_intesity_NB(self, m0, nu, C, k): """Calculates narrowband damage intensity with parameters m0, nu, C, k, as defined in [2]. :param m0: [int,float] Zeroth spectral moment [MPa**2]. :param nu: [int,float] Frequency of positive slope zero crossing [Hz]. :param C: [int,float] Fatigue strength coefficient [MPa**k]. :param k: [int,float] Fatigue strength exponent [/]. :return: Estimated damage intensity. :rtype: float """ d = nu * np.sqrt(2 * m0)**k * gamma(1.0 + k/2.0) / C return d
[docs] def get_life(self, C, k, integrate_pdf=False): """Calculate fatigue life with parameters C, k, as defined in [2]. :param C: [int,float] S-N curve intercept [MPa**k]. :param k: [int,float] S-N curve inverse slope [/]. :param integrate_pdf: boolean If true the the fatigue life is estimated by integrating the PDF, Default is false which means that the theoretical equation is used :return: Estimated fatigue life in seconds. :rtype: float """ if integrate_pdf: d = self.spectral_data.nu / C * \ quad(lambda s: s**k*self.get_PDF(s), a=0, b=np.inf)[0] else: m0 = self.spectral_data.moments[0] nu = self.spectral_data.nu d = self.damage_intesity_NB(m0=m0, nu=nu, C=C, k=k) T = 1.0/d return T