ckunte.net

S-N curves

In order to set a suitable design criteria, I am looking to compare two classes of S-N curves for a fatigue design, viz., E and F2, and I cannot not find a handy plot to refer to, and it is frustrating when standards fail to include. So, I channel it to write some code to roll my own:

S-N curves

The basic S-N curve equation is as follows, which one may know is from Paris-Erdogan law (fracture mechanics):

\begin{aligned} N = k_1 \cdot S^{-m} \newline \end{aligned}

The standard does describe it in its logarithmic form, which is as follows:

\begin{aligned} \log N = \log k_1 - m \cdot \log S \newline \end{aligned}

and then it goes on to furnish its two sets of key components that form parts of the equation — highlighted below.1

From ISO 19902:2007

Plot code is as below.

#!/usr/bin/env python
# encoding: utf-8
"""
sncurves.py -- 2016 ckunte
May 7: Initial commit.
"""
import numpy as np
import matplotlib.pyplot as plt

a = [
12.18, 16.13, # TJ
14.61, 17.01, # B
13.23, 16.47, # C
11.78, 15.63, # D
11.62, 15.37, # E
11.40, 15.00, # F
11.23, 14.71, # F2
11.00, 14.33, # G
10.57, 13.62  # W1
]
m = [3.0, 3.5, 4.0, 5.0] # Slope
r = [1.8E6, 1.0E5, 4.68E5, 1.0E6] # Range limit for curves

fig, ax = plt.subplots()

def style():
    plt.rcParams['grid.linestyle'] = ':'
    plt.rcParams['grid.linewidth'] = 0.5
    plt.grid(True)


def tj_curve():
    # -- TJ (tubular joints) curve --
    # Slope 1
    n = np.arange(1, r[0], 1.0E5)
    s = (n / 10**a[0])**(-1 / m[0])
    ax.loglog(n, s, basex=10, color='black', linewidth=2.0, label="TJ curve")
    # Slope 2 
    n = np.arange(r[0], 1.0E12, 1.0E9)
    s = (n / 10**a[1])**(-1 / m[3])
    ax.loglog(n, s, basex=10, color='black', linewidth=2.0)
    pass

def b_curve():
    # -- B curve --
    # Slope 1
    n = np.arange(1, r[1], 1.0E4)
    s = (n / 10**a[2])**(-1 / m[2])
    ax.loglog(n, s, basex=10, color='magenta', linewidth=2.0, label="B curve")
    # Slope 2
    n = np.arange(r[1], 1.0E12, 1.0E9)
    s = (n / 10**a[3])**(-1 / m[3])
    ax.loglog(n, s, basex=10, color='magenta', linewidth=2.0)
    pass

def c_curve():
    # -- C curve --
    # Slope 1
    n = np.arange(1, r[2], 1.0E4)
    s = (n / 10**a[4])**(-1 / m[1])
    ax.loglog(n, s, basex=10, color='blue', linewidth=2.0, label="C curve")
    # Slope 2
    n = np.arange(r[2], 1.0E12, 1.0E9)
    s = (n / 10**a[5])**(-1 / m[3])
    ax.loglog(n, s, basex=10, color='blue', linewidth=2.0)
    pass

def d_curve():
    # -- D curve --
    # Slope 1
    n = np.arange(1, r[3], 1.0E4)
    s = (n / 10**a[6])**(-1 / m[0])
    ax.loglog(n, s, basex=10, color='orange', linewidth=2.0, label="D curve")
    # Slope 2
    n = np.arange(r[3], 1.0E12, 1.0E9)
    s = (n / 10**a[7])**(-1 / m[3])
    ax.loglog(n, s, basex=10, color='orange', linewidth=2.0)
    pass

def e_curve():
    # -- E curve --
    # Slope 1
    n = np.arange(1, r[3], 1.0E4)
    s = (n / 10**a[8])**(-1 / m[0])
    ax.loglog(n, s, basex=10, color='green', linewidth=2.0, label="E curve")
    # Slope 2
    n = np.arange(r[3], 1.0E12, 1.0E9)
    s = (n / 10**a[9])**(-1 / m[3])
    ax.loglog(n, s, basex=10, color='green', linewidth=2.0)
    pass

def f_curve():
    # -- F curve --
    # Slope 1
    n = np.arange(1, r[3], 1.0E4)
    s = (n / 10**a[10])**(-1 / m[0])
    ax.loglog(n, s, basex=10, color='olive', linewidth=2.0, label="F curve")
    # Slope 2
    n = np.arange(r[3], 1.0E12, 1.0E9)
    s = (n / 10**a[11])**(-1 / m[3])
    ax.loglog(n, s, basex=10, color='olive', linewidth=2.0)
    pass

def f2_curve():
    # -- F2 curve --
    # Slope 1
    n = np.arange(1, r[3], 1.0E4)
    s = (n / 10**a[12])**(-1 / m[0])
    ax.loglog(n, s, basex=10, color='brown', linewidth=2.0, label="F2 curve")
    # Slope 2
    n = np.arange(r[3], 1.0E12, 1.0E9)
    s = (n / 10**a[13])**(-1 / m[3])
    ax.loglog(n, s, basex=10, color='brown', linewidth=2.0)
    pass

def g_curve():
    # -- G curve --
    # Slope 1
    n = np.arange(1, r[3], 1.0E4)
    s = (n / 10**a[14])**(-1 / m[0])
    ax.loglog(n, s, basex=10, color='deeppink', linewidth=2.0, label="G curve")
    # Slope 2
    n = np.arange(r[3], 1.0E12, 1.0E9)
    s = (n / 10**a[15])**(-1 / m[3])
    ax.loglog(n, s, basex=10, color='deeppink', linewidth=2.0)
    pass

def w1_curve():
    # -- W1 curve --
    # Slope 1
    n = np.arange(1, r[3], 1.0E4)
    s = (n / 10**a[16])**(-1 / m[0])
    ax.loglog(n, s, basex=10, color='olivedrab', linewidth=2.0, label="W1 curve")
    # Slope 2
    n = np.arange(r[3], 1.0E12, 1.0E9)
    s = (n / 10**a[17])**(-1 / m[3])
    ax.loglog(n, s, basex=10, color='olivedrab', linewidth=2.0)
    pass

if __name__ == '__main__':
    # Plot all
    style()
    tj_curve()
    b_curve()
    c_curve()
    d_curve()
    e_curve()
    f_curve()
    f2_curve()
    g_curve()
    w1_curve()
    ax.legend(loc=0)
    ax.set_xlabel('Number of cycles, N')
    ax.set_ylabel('Hotspot stress $\sigma$, MPa')
    plt.savefig("sncurves.png")
    plt.show()

  1. Playing with logarithms is fraught with error, as they are not the same as plain algebra — Leonhard Euler’s gift to the world, which reminds me I should jog my memory from high-school. There is a very nice podcast recording (mp3) on Euler’s e that also discusses the history of logarithms, which was in ancient days allowed to transform “complicated” multiplication into simple addition, which makes perfect sense in a world that had no computers, no calculators, and no slide rules.