Fatigue curves are typically in semi-log or log-log form. And in fatigue analysis it is often needed to interpolate both ways in order to determine stress or cycles. In both cases below we will interpolate over a straight line drawn on the graph using two points as its definition: \( (N_1,S_1) \), \( (N_2,S_2) \).

Semi-Log

In semi-log plots the stress is linear and the cycles are logarithmic. The equation of a line in this coordinate space is therefore:

\begin{equation}
S = m \log_{10}N + b
\end{equation}

The values of \(m\) and \(b\) can be found by 2 known points on the fatigue curve:

\begin{align}
&m = \frac{\Delta S}{\Delta \log_{10}N} = \frac{S_2-S_1}{\log_{10}(N_2/N_1)}
&b = S_1 – m\log_{10}N_1
\end{align}

To get stress from cycles along this line, apply the equation of the line with your values of \(m\) and \(b\):
\begin{equation}
S(N) = m\log_{10}N + b
\end{equation}

To interpolate cycles from stress along this line, rearrange the equation for N:

\begin{equation}
N(S) = 10^{\frac{S-b}{m}}
\end{equation}

Log-log

Here both scales are logarithmic. The equation for a line in this coordinate system is therefore:

\begin{equation}
\log_{10}S = m \log_{10}N + \log_{10}b
\end{equation}

m and b are found in the same manner:
\begin{align}
&m = \frac{\Delta\log_{10}S}{\Delta\log_{10}N} = \frac{\log_{10}(S_2/S_1)}{\log_{10}(N_2/N_1)}
&b = 10^{\log_{10}S_1 – m \log_{10}N_1}
\end{align}

To interpolate stress from cycles:
\begin{equation}
S(N) = 10^{m\log_{10}N + \log_{10}b}
\end{equation}

To interpolate cycles from stress:
\begin{equation}
N(S) = 10^{ \frac{ \log_{10}S – \log_{10}b}{m} } = 10^{ \frac{\log_{10}(S/b)}{m} }
\end{equation}

Python Implementation

In Python, we can make a class to represent a fatigue curve and give it methods for interpolation and plotting. Moreover, we can define a __call__ method to make the instance callable. For example:


#!/usr/bin/env python
"""
Provides classes for semi-log and log-log fatigue curves having methods for
interpolation and plotting.
"""

import numpy as np
import matplotlib.pyplot as plt


__author__    = 'Rob Siegwart'
__copyright__ = 'Copyright 2018 Rob Siegwart'


class FatigueCurve:
    ''' Base class for fatigue curves. Input:
            A list containing two (N,S) data point pairs
    '''
    def __init__(self,points):
        self.N, self.S = list(zip(points[0],points[1]))
        self.N1, self.N2 = self.N
        self.S1, self.S2 = 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
    
    def plot(self):
        fig, ax = self._plot()
        ax.semilogx(self.N,self.S)
    


class SemiLogCurve(FatigueCurve):
    def __init__(self,points):
        super().__init__(points)
        self.m = (self.S2 - self.S1)/np.log10(self.N2/self.N1)
        self.b = self.S1 - self.m*np.log10(self.N1)
    
    def getS(self,N,plot=False):
        ''' Interpolate stress from cycles. '''
        S = np.round(self.m*np.log10(N) + self.b,2)
        if plot:
            fig,ax = self._plot()
            ax.semilogx(self.N,self.S,N,S,'*')
        return S

    def getN(self,S,plot=False):
        ''' Interpolate cycles from stress. '''
        N = int(10**((S-self.b)/self.m))
        if plot:
            fig,ax = self._plot()
            ax.semilogx(self.N,self.S,N,S,'*')
        return N
    
    __call__ = getS


class LogLogCurve(FatigueCurve):
    def __init__(self,points):
        super().__init__(points)
        self.m = np.log10(self.S2/self.S1)/np.log10(self.N2/self.N1)
        self.b = 10**(np.log10(self.S1) - self.m*np.log10(self.N1))
    
    def plot(self):
        fig,ax = self._plot()
        ax.loglog(self.N,self.S)
    
    def getS(self,N,plot=False):
        ''' Interpolate stress from cycles '''
        S = np.round(10**(self.m*np.log10(N)+np.log10(self.b)),2)
        if plot:
            fig,ax = self._plot()
            ax.loglog(self.N,self.S,N,S,'*')
        return S
    
    def getN(self,S,plot=False):
        ''' Interpolate cycles from stress '''
        N = int(10**(np.log10(S/self.b)/self.m))
        if plot:
            fig,ax = self._plot()
            ax.loglog(self.N,self.S,N,S,'*')
        return N
    
    __call__ = getS

Usage:


>>> mat1 = SemiLogCurve([(1000,60),(1e6,20)])   # create a new curve
>>> mat1(3e5)                                   # default call is interpolating to obtain stress from cycles
26.97
>>> mat1.getN(28.5,plot=True)                   # call to get cycles from stress input with plot set to True to generate a graph
230409