Wöhler analyzing functions
Developed by Mustapha Kassem in scope of a master thesis at TU München
[1]:
import copy
import numpy as np
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
import pylife.materialdata.woehler as woehler
from pylife.materiallaws import WoehlerCurve
The Wölhler analysis module takes fatigue data, i. e. values of the form cycles load fracture that have been measured by a fatigue testing lab and analyze it to return the parameters of a Wöhler curve.
These are:
the slope
k_1the cycle number of the endurance limit
NDthe load level of the endurance limit
SDthe scatter line cycle (lifetime) direction
TNthe scatter in load direction
TS
We will see several methods to perform the analysis below.
Data import
Data is made up of two columns:
The first column is made up of the load values
The scond column is made up of the load-cycle values
[2]:
file_name = 'data/woehler/fatigue-data-plain.csv'
[3]:
df = pd.read_csv(file_name, sep='\t')
df.columns=['load', 'cycles']
px.scatter(df, x='cycles', y='load', log_x=True, log_y=True)
Data type cannot be displayed: application/vnd.plotly.v1+json
Guessing the fractures
In this case there is no information if the specimen was a runout or a fracture. We can guess it based on the value for the load_cycle_limit which defaults to 1e7.
[4]:
load_cycle_limit = None # or for example 1e7
df = woehler.determine_fractures(df, load_cycle_limit)
px.scatter(df, x='cycles', y='load', color='fracture', log_x=True, log_y=True)
Data type cannot be displayed: application/vnd.plotly.v1+json
Analysis
Genaral preparations
Before we do the actual analysis, we make some preparations like, guessing the initial finite_infinite_transition load level, distinguishing between runouts and fractures and between infinite_zone and finite_zone.
[5]:
fatigue_data = df.fatigue_data
fatigue_data.finite_infinite_transition
[5]:
np.float64(308.909475)
We can distinguish between the finite and infinite zones.
[6]:
infinite_zone = fatigue_data.infinite_zone
finite_zone = fatigue_data.finite_zone
go.Figure([
go.Scatter(x=finite_zone.cycles, y=finite_zone.load, mode='markers', name='finite'),
go.Scatter(x=infinite_zone.cycles, y=infinite_zone.load, mode='markers', name='infinite'),
go.Scatter(x=[df.cycles.min(), df.cycles.max()], y=[fatigue_data.finite_infinite_transition]*2, mode='lines', name='fatigue limit')
]).update_xaxes(type='log').update_yaxes(type='log').update_layout(xaxis_title='Cycles', yaxis_title='Load')
Data type cannot be displayed: application/vnd.plotly.v1+json
We can separate fractures from runouts.
[7]:
fractures = fatigue_data.fractures
runouts = fatigue_data.runouts
fig = go.Figure([
go.Scatter(x=fractures.cycles, y=fractures.load, mode='markers', name='fractures'),
go.Scatter(x=runouts.cycles, y=runouts.load, mode='markers', name='runouts'),
go.Scatter(x=[df.cycles.min(), df.cycles.max()], y=[fatigue_data.finite_infinite_transition]*2, mode='lines', name='fatigue limit')
]).update_xaxes(type='log').update_yaxes(type='log').update_layout(xaxis_title='Cycles', yaxis_title='Load')
fig
Data type cannot be displayed: application/vnd.plotly.v1+json
Elementary analysis
The Elementary analysis is the first step of the analysis. It determines the slope k_1 in the finite region and the scatter in cycle region TN using the pearl chain method.
The endurance limit in load direction SN is guessed from the tentative fatigue limit. The scatter in load direction TS is transformed from TN using the slope k_1.
[8]:
elementary_result = woehler.Elementary(fatigue_data).analyze()
elementary_result
[8]:
k_1 1.138923e+01
ND 1.002814e+06
SD 3.089095e+02
TN 1.076244e+01
TS 1.231981e+00
failure_probability 5.000000e-01
dtype: float64
[9]:
wc = elementary_result.woehler
cycles = np.logspace(np.log10(df.cycles.min()), np.log10(df.cycles.max()), 100)
elementary_fig = copy.deepcopy(fig)
elementary_fig.add_scatter(x=cycles, y=wc.basquin_load(cycles), mode='lines', name='Elementary 50%')
elementary_fig.add_scatter(x=cycles, y=wc.basquin_load(cycles, failure_probability=0.1),
mode='lines', name='Elementary 10%')
elementary_fig.add_scatter(x=cycles, y=wc.basquin_load(cycles, failure_probability=0.9),
mode='lines', name='Elementary 90%')
elementary_fig
Data type cannot be displayed: application/vnd.plotly.v1+json
Probit
[10]:
probit_result = woehler.Probit(fatigue_data).analyze()
probit_result
[10]:
k_1 1.138923e+01
ND 1.469346e+06
SD 2.987202e+02
TN 1.076244e+01
TS 1.110052e+00
failure_probability 5.000000e-01
dtype: float64
[11]:
wc = probit_result.woehler
cycles = np.logspace(np.log10(df.cycles.min()), np.log10(df.cycles.max()), 100)
probit_fig = copy.deepcopy(fig)
probit_fig.add_scatter(x=cycles, y=wc.basquin_load(cycles), mode='lines', name='Probit 50%')
probit_fig.add_scatter(x=cycles, y=wc.basquin_load(cycles, failure_probability=0.1), mode='lines', name='Probit 10%')
probit_fig.add_scatter(x=cycles, y=wc.basquin_load(cycles, failure_probability=0.9), mode='lines', name='Probit 90%')
probit_fig
Data type cannot be displayed: application/vnd.plotly.v1+json
Maximum Likelihood Infinite
The Maximum Likelihood Infinite method takes the parameters for the finite regime fitted by Elementary (k_1, TN) and fits the ones for the infinite regime (SD, ND, TS), hence the name.
[12]:
maxlike_inf_result = woehler.MaxLikeInf(fatigue_data).analyze()
maxlike_inf_result
[12]:
k_1 1.138923e+01
ND 1.678238e+06
SD 2.952540e+02
TN 1.076244e+01
TS 1.106812e+00
failure_probability 5.000000e-01
dtype: float64
[13]:
wc = maxlike_inf_result.woehler
cycles = np.logspace(np.log10(df.cycles.min()), np.log10(df.cycles.max()), 100)
maxlike_inf_fig = copy.deepcopy(fig)
maxlike_inf_fig.add_scatter(x=cycles, y=wc.basquin_load(cycles), mode='lines', name='MaxLikeInf 50%')
maxlike_inf_fig.add_scatter(x=cycles, y=wc.basquin_load(cycles, failure_probability=0.1), mode='lines', name='MaxLikeInf 10%')
maxlike_inf_fig.add_scatter(x=cycles, y=wc.basquin_load(cycles, failure_probability=0.9), mode='lines', name='MaxLikeInf 90%')
maxlike_inf_fig
Data type cannot be displayed: application/vnd.plotly.v1+json
Maximum Likelihood Full
The Maximum Likelihood Full method just takes the elementary result as starting values but fits all the parameters.
[14]:
maxlike_full_result = woehler.MaxLikeFull(fatigue_data).analyze()
maxlike_full_result
[14]:
k_1 8.626165e+00
ND 1.326971e+06
SD 2.952540e+02
TN 9.862007e+00
TS 1.106811e+00
failure_probability 5.000000e-01
dtype: float64
[15]:
wc = maxlike_full_result.woehler
cycles = np.logspace(np.log10(df.cycles.min()), np.log10(df.cycles.max()), 100)
maxlike_full_fig = copy.deepcopy(fig)
maxlike_full_fig.add_scatter(x=cycles, y=wc.basquin_load(cycles), mode='lines', name='MaxLikeFull 50%')
maxlike_full_fig.add_scatter(x=cycles, y=wc.basquin_load(cycles, failure_probability=0.1), mode='lines', name='MaxLikeFull 10%')
maxlike_full_fig.add_scatter(x=cycles, y=wc.basquin_load(cycles, failure_probability=0.9), mode='lines', name='MaxLikeFull 90%')
maxlike_full_fig
Data type cannot be displayed: application/vnd.plotly.v1+json
Maximum Likelihood Full with fixed parameters
Sometimes it is desirable to pin one or more parameters of the Wöhler curve to a predefined value based on assumptions of the Wöhler curve. You can archive that by handing the fixed parameters to the MaxLikeFull object using the fixed_parameters argument.
[16]:
fixed_parameters = {
'k_1': 7.,
'ND': 1e6
}
maxlike_fixed_result = woehler.MaxLikeFull(fatigue_data).analyze(fixed_parameters=fixed_parameters)
maxlike_fixed_result
[16]:
k_1 7.000000
ND 1000000.000000
SD 296.971759
TN 10.201477
TS 1.114568
failure_probability 0.500000
dtype: float64
[17]:
wc = maxlike_fixed_result.woehler
cycles = np.logspace(np.log10(df.cycles.min()), np.log10(df.cycles.max()), 100)
maxlike_fixed_fig = copy.deepcopy(fig)
maxlike_fixed_fig.add_scatter(x=cycles, y=wc.basquin_load(cycles), mode='lines', name='MaxLikefixed 50%')
maxlike_fixed_fig.add_scatter(x=cycles, y=wc.basquin_load(cycles, failure_probability=0.1), mode='lines', name='MaxLikeFixed 10%')
maxlike_fixed_fig.add_scatter(x=cycles, y=wc.basquin_load(cycles, failure_probability=0.9), mode='lines', name='MaxLikeFixed 90%')
maxlike_fixed_fig
Data type cannot be displayed: application/vnd.plotly.v1+json