July 31, 2014

S-N curves

In order to set a suitable design criteria, I was looking to compare two classes of S-N curves for a fatigue design, viz., E and F2, and I could not find a handy plot to refer to. The basic S-N curve equation is as follows, which one may know is from Paris-Erdogan law (fracture mechanics):

N = k1 * S^(-m)

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

log(N, 10) = log(k1, 10) - m * log(S, 10)

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

From ISO 19902:2007

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. Finally, here’s the plot I was looking to generate.

See python code below, which is used to generate the above graph.

#!/usr/bin/env python
# -*- coding: 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 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="E 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="E 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
    tj_curve()
    b_curve()
    c_curve()
    d_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.jpg")
    plt.grid()
    plt.show()

Update (Oct 11): 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.