Here we will develop some classes for interpolating general multi-linear fatigue curves using SciPy's interp1d. interp1d creates a class instance which can then be called with lookup values:
x = [ 1,2,3,...] # X-axis values y = [ 20,15,10,...] # Y-axis values f = interp1d(x,y) y1 = f(x1) # Query at a new value, x1
Semi-Log
Using a linear interpolation function on a semi-log graph requires that the log of the values is used on the log axis. With fatigue curve arrays of \(N\) and \(S\), interpolating for stress can be done by:
\begin{equation*}
f=\text{interp1d}(\log_{10}N, S)
\end{equation*}
\begin{equation*}
S_{new}=f(\log_{10}N_{new})
\end{equation*}
And interpolating for cycles:
\begin{equation*}
f=\text{interp1d}(S, \log_{10}N)
\end{equation*}
\begin{equation*}
N_{new}=10^{f(S_{new})}
\end{equation*}
Log-Log
Log-log calculations are the same as Semi-log but with the exception that both axes use the logarithm of the values.
To obtain stress:
\begin{equation*}
f=\text{interp1d}(\log_{10}N, \log_{10}S)
\end{equation*}
\begin{equation*}
S_{new}=10^{f(\log_{10}N_{new})}
\end{equation*}
To obtain cycles:
\begin{equation*}
f=\text{interp1d}(\log_{10}S, \log_{10}N)
\end{equation*}
\begin{equation*}
N_{new}=10^{f(\log_{10}S_{new})}
\end{equation*}
Implementation
In Python we can implement the above as classes. First a general class common to both log-log and semi-log:
import numpy as np import matplotlib.pyplot as plt from scipy.interpolate import interp1d class FatigueCurve: ''' Base class for a fatigue curve. Parameters ---------- points : list A list containing (N,S) data point pairs (2+) ''' def __init__(self,points): self.N, self.S = list(zip(*points)) self.N = np.asarray(self.N) self.S = np.asarray(self.S) self.logN = np.log10(self.N) self.logS = np.log10(self.S) def _plot(self): ''' Plot setup method ''' fig, ax = plt.subplots() ax.set_xlabel('Cycles') ax.set_ylabel('Stress') ax.grid(True, which='both', axis='both', color='lightgrey') return fig, ax
Next we create classes for semi-log and log-log:
class SemiLogCurve(FatigueCurve): def __init__(self,points): super().__init__(points) self.getS_f = interp1d(self.logN,self.S) self.getN_f = interp1d(self.S,self.logN) def _semilogx(self,ax,N,S): ''' Plot an interpolated value on a semilog fatigue curve. ''' ax.semilogx(self.N,self.S,markersize=6,marker='o') ax.semilogx(N,S,markersize=6,marker='o') ax.annotate('({:,.2}, {:,.2})'.format(N,S), (N,S)) return ax def getS(self,N,plot=False): ''' Interpolate stress from cycles. ''' S = self.getS_f(np.log10(N)) if plot: fig,ax = self._plot() ax = self._semilogx(ax,float(N),float(S)) return int(S) def getN(self,S,plot=False): ''' Interpolate cycles from stress. ''' N = np.power(10,self.getN_f(S)) if plot: fig,ax = self._plot() ax = self._semilogx(ax,float(N),float(S)) return int(N) def plot(self): fig, ax = self._plot() ax.semilogx(self.N,self.S) __call__ = getS class LogLogCurve(FatigueCurve): def __init__(self,points): super().__init__(points) self.getS_f = interp1d(self.logN,self.logS) self.getN_f = interp1d(self.logS,self.logN) def _loglog(self,ax,N,S): ''' Plot an interpolated value on a log-log fatigue curve. ''' ax.loglog(self.N,self.S,marker='o',markersize=6) ax.loglog(N,S,marker='o',markersize=6) ax.annotate('({:,.2}, {:,.2})'.format(N,S), (N,S)) return ax def getS(self,N,plot=False): ''' Interpolate stress from cycles ''' S = np.power(10,self.getS_f(np.log10(N))) if plot: fig,ax = self._plot() ax = self._loglog(ax,float(N),float(S)) return int(S) def getN(self,S,plot=False): ''' Interpolate cycles from stress ''' N = np.power(10,self.getN_f(np.log10(S))) if plot: fig,ax = self._plot() ax = self._loglog(ax,float(N),float(S)) return int(N) def plot(self): fig, ax = self._plot() ax.loglog(self.N,self.S) __call__ = getS
Usage
>>> curve = LogLogCurve([(1e3,50e3),(1e6,20e3),(1e7,18e3)]) >>> curve.getS(70e3,plot=True) 28459
>>> curve.getN(34e3,plot=True) 18309