qats.fatigue#

Sub-package for fatigue related calculations and operations.

qats.fatigue.corrections#

Fatigue related corrections.

Functions overview

goodman_haigh(cycles, uts)

Effective alternating stress using the Goodman-Haigh mean stress correction for fully reversed loading (R=-1).

API

goodman_haigh(cycles, uts)#

Effective alternating stress using the Goodman-Haigh mean stress correction for fully reversed loading (R=-1).

Parameters:
  • cycles (np.ndarray or list) – Array of cycle ranges and mean values, shape (n, 2). First column is cycle ranges, second column is cycle mean values.

  • uts (float) – Material ultimate tensile strength, in same unit as cycle ranges and mean values.

Returns:

corrected_ranges – Corrected stress ranges, shape: (n,) (1D array).

Return type:

np.ndarray

Notes

In materials science and fatigue, the Goodman relation is an equation used to quantify the interaction of mean and alternating stresses on the fatigue life of a material.

A Goodman diagram,[1][2] sometimes called a Haigh diagram or a Haigh-Soderberg diagram,[3] is a graph of (linear) mean stress vs. (linear) alternating stress, showing when the material fails at some given number of cycles.

A scatterplot of experimental data shown on such a plot can often be approximated by a parabola known as the Gerber line, which can in turn be (conservatively) approximated by a straight line called the Goodman line.

Correcting the stress ranges like this can only be applied with an SN-curve with a stress ratio (R) of -1.

References

  1. Herbert J. Sutherland and John F. Mandell. “Optimized Goodman diagram for the analysis of fiberglass composites used in wind turbine blades”

  2. David Roylance. “Fatigue”, Archived 2011-06-29 at the Wayback Machine.. 2001

  3. Tapany Udomphol. “Fatigue of metals”. 2007.

Examples

>>> from qats.fatigue.rainflow import count_cycles
>>> from qats.fatigue.corrections import goodman_haigh
>>> # assuming a series has been established, and that uts is defined
>>> cycles = count_cycles(series)
>>> corrected_ranges = goodman_haigh(cycles[:, :2], uts)

The array obtained is then a 1D array of same size as the first dimension of cycles:

>>> cycles.shape
(1350, 3)
>>> corrected_ranges.shape
(1350,)

qats.fatigue.rainflow#

Rainflow cycle counting algorithm according to ASTM E1049-85 (2011), section 5.4.4.

Functions overview

count_cycles(series[, endpoints])

Count number of occurrences of cycle range and mean combinations.

cycles(series[, endpoints])

Find full cycles and half-cycles range and mean value from series and count the number of occurrences using the Rainflow algorithm.

mesh(cycles[, nr, nm])

Mesh range-mean distribution.

rebin(cycles[, binby, n, w])

Rebin cycles in specified bins by range or mean value

reversals(series[, endpoints])

A generator function which iterates over the reversals in the iterable series.

API

reversals(series, endpoints=False)#

A generator function which iterates over the reversals in the iterable series.

Parameters:
  • series (array_like) – data series

  • endpoints (bool, optional) – If True, first and last points in series are included. Default is False. Note that in general, inclusion of end points is only relevant if the series passed is already an array of reversal points.

Returns:

yields floats

Return type:

generator

Notes

Reversals are the points at which the first derivative on the series changes sign. The generator never yields the first and the last points in the series, unless endpoints is set to True (in which case they are always included).

cycles(series, endpoints=False)#

Find full cycles and half-cycles range and mean value from series and count the number of occurrences using the Rainflow algorithm.

Parameters:
  • series (array_like) – data series

  • endpoints (bool, optional) – If True, first and last points in series are included as cycle start/end points. This is convenient if the series given is already an array of reversal points. Default is False.

Returns:

  • full (list) – full cycles (range and mean).

  • half (list) – half cycles (range and mean).

Notes

The cycles are extracted from the iterable series according to section 5.4.4 in ASTM E1049 (2011).

Examples

Extract start and end points for all full and half cycles.

>>> from qats.fatigue.rainflow import cycles
>>> series = [0, -2, 1, -3, 5, -1, 3, -4, 4, -2, 0]
>>> full, half = cycles(series)
>>> full
[(4, 1.0)]
>>> half
[(3, -0.5), (4, -1.0), (8, 1.0), (6, 1.0), (8, 0.0), (9, 0.5)]
count_cycles(series, endpoints=False)#

Count number of occurrences of cycle range and mean combinations.

Parameters:
  • series (array_like) – Data series.

  • endpoints (bool, optional) – If True, first and last points in series are included as cycle start/end points. This is convenient if the series given is already an array of reversal points, but should in general not be used otherwise. Default is False.

Returns:

cycles – Array of shape (n, 3) where n is number of cycle ranges. Each row consists of three values; the cycle range, mean and count. Counts are either 1.0 (for full cycles) or 0.5 (for half cycles). The array is sorted by increasing cycle range.

Return type:

np.ndarray

Notes

The cycles are extracted from the iterable series using the cycles() function.

Since half cycles are counted as 0.5, the returned counts are not necessarily whole numbers.

Examples

Extract raw cycle range, mean and count:

>>> from qats.fatigue.rainflow import count_cycles
>>> series = [0, -2, 1, -3, 5, -1, 3, -4, 4, -2, 0]
>>> count_cycles(series)
array([[ 3. , -0.5,  0.5],
       [ 4. , -1. ,  0.5],
       [ 4. ,  1. ,  1. ],
       [ 6. ,  1. ,  0.5],
       [ 8. ,  0. ,  0.5],
       [ 8. ,  1. ,  0.5],
       [ 9. ,  0.5,  0.5]])

The array may be unpacked into separate arrays of cycle range, mean and count as:

>>> r, m, c = count_cycles(series).T

The following will also work, but is slower than the example above:

>>> r, m, c = zip(*count_cycles(series))

See also

reversals, cycles

mesh(cycles, nr=100, nm=100)#

Mesh range-mean distribution.

Parameters:
  • cycles (array_like) – Array of shape (n, 3). Columns should be: cycle range, cycle mean, count. See description of output from count_cycles().

  • nr (int, optional) – Number of equidistant bins for cycle ranges.

  • nm (int, optional) – Number of equidistant bins for cycle means.

Returns:

  • rangemesh (np.ndarray) – Cycle ranges meshgrid, shape: (nm, nr).

  • meanmesh (np.ndarray) – Cycle mean value meshgrid, shape: (nm, nr).

  • countmesh (np.ndarray) – Cycle count 2D histogram, shape: (nm, nr).

Notes

New in version 4.7.0.

This function has been re-written for version 4.7.0. For versions <= 4.6.1, the mesh established was not correct.

Shape of the returned arrays is consistent with numpy.meshgrid(): (nm, nr), i.e. number of rows is nm and number of columns is nr. This means that the array is transposed compared the output from numpy.histogram2d(), which is a 2D histogram of shape (nr, nm).

The cycle count mesh is consistent with the cycles returned from rebin(), such that the sum of the cycle count mesh along each of its axes is equals the counts for cycles rebinned by ‘mean’ or ‘range’, respectively:

>>> cycles = count_cycles(series)
>>> _, _, cmesh = mesh(cycles, nr=200, nm=100)
>>> cycles_rebinned_range = rebin(cycles, binby='range', n=200)
>>> # sum of `cmesh` along cycle mean axis (constant cycle range) vs. counts from rebinned cycles
>>> (cmesh.sum(axis=0) == cycles_rebinned_range[:, 2]).all()
True
>>> cmesh.sum(axis=0).shape
(200,)
>>> cmesh.sum(axis=1).shape
(100,)

Examples

Rebin the cycle distribution onto a 10 x 15 mesh:

>>> from qats.fatigue.rainflow import count_cycles, mesh
>>> # (obtain some series from e.g. a simulation)
>>> count_cycles(series)
>>> rangemesh, meanmesh, countmesh = mesh(cycles, nr=15, nm=10)
>>> countmesh.shape  # (same shape for all three arrays)
(10, 15)

The mesh returned is suitable for plotting with matplotlib 3D plots, for instance:

>>> import matplotlib as mpl
>>> import matplotlib.pyplot as plt
>>> from mpl_toolkits.mplot3d import Axes3D
>>> fig = plt.figure()
>>> ax = fig.gca(projection='3d')
>>> ax.plot_surface(rangemesh, meanmesh, countmesh, cmap=mpl.cm.coolwarm)
>>> ax.set_xlabel('Cycle range')
>>> ax.set_ylabel('Cycle mean')
>>> ax.set_zlabel('Cycle count')
>>> plt.show()
rebin(cycles, binby='range', n=None, w=None)#

Rebin cycles in specified bins by range or mean value

Parameters:
  • cycles (array_like) – Array of shape (n, 3). Columns should be: cycle range, cycle mean, count. See description of output from rainflow.count_cycles().

  • binby (str, optional) – ‘range’ - Rebin by cycle range (default) ‘mean’ - Rebin by cycle mean

  • n (int, optional) – Number of equidistant bins for cycle ranges and cycle mean values.

  • w (float, optional) – Width of equidistant bins for cycle ranges and cycle mean values. Overrides n if specified.

Returns:

cycles – Array with shape (nbins, 3), where nbins is number of bins. Columns are: cycle range, mean, count.

Return type:

np.ndarray

Notes

Cycles are gathered into a specific bin if the primary measure (range or mean) is within that bin’s boundaries. The primary measure is represented by the bin mid point. The secondary measure (range or mean) is represented by its weighted average (weighted by number of occurrences) in the bin.

Note that rebinning may shift a significant amount of the cycles to bins which midpoint differs notably from the original cycle value (range or mean). We advice to rebin for plotting, but when calculating e.g. fatigue damage we advice to use the raw unbinned values.

New in version 4.7.0.

If a cycle is at the edge between two bins, it is placed in the ‘highest’ bin. E.g. If bin edges are [0, 1, 2, 3], a cycle with value 1 will be counted in the bin [1, 2) - see documentation for np.histogram for details. This behaviour is contrary to version <= 4.6.1, for which the value 1 was counted in bin (0, 1]. The new way is slightly more conservative, since cycle ranges (or means) that end up on an edge is shifted towards a higher value. However; in most cases, there will be no difference at all since very few cycles coincide with bin edges.

Examples

Rebinning by range with bin width 1.0. Number of bins is then determined from the max cycle range. The second column (which is here the secondary measure, since we are binning by range) is the weighted average of the mean value for the cycles that fall within each bin:

>>> from qats.fatigue.rainflow import count_cycles
>>> series = [0, -2, 1, -3, 1, -1, 3, -2, 2, -2, 0, 1, -2, 3, -1, 2, -3, 0, -1, 0]
>>> cycles = count_cycles(series)
>>> rebin(cycles, binby='range', w=2.0)
array([[ 1.  , -0.5 ,  0.5 ],
       [ 3.  , -0.25,  4.  ],
       [ 5.  ,  0.  ,  3.5 ]])

qats.fatigue.sn#

Classes and functions for fatigue calculations:
  • SNCurve (class)

  • Fatigue damage calculation (functions)

Classes and functions overview

SNCurve(name, m1, **kwargs)

S-N curve representing fatigue capacity versus cyclic stresses.

minersum(srange, count, sn[, td, scf, th, ...])

Fatigue damage (Palmgren-Miner sum) calculation based on stress cycle histogram and S-N curve.

minersum_weibull(q, h, sn, v0[, td, scf, th])

Fatigue damage (Palmgren-Miner sum) calculation based on (2-parameter) Weibull stress cycle distribution and S-N curve.

Class API

class SNCurve(name, m1, **kwargs)#

S-N curve representing fatigue capacity versus cyclic stresses.

Parameters:
  • name (str) – S-N curve name

  • m1 (float) – Negative inverse slope parameter (for bilinear curves: used for N < nswitch).

  • m2 (float, optional) – Negative inverse slope parameter for N > nswitch

  • a1 (float, optional) – Intercept parameter for N <= nswitch.

  • loga1 (float, optional) – Log10 of a1. Must be given if a1 is not specified.

  • nswitch (float, optional) – Number of cycles at transition from m1 to m2. Required if m2 is specified.

  • t_exp (float, optional) – Thickness correction exponent. If not specified, thickness may not be specified in later calculations.

  • t_ref (float, optional) – Reference thickness [mm]. If not specified, thickness may not be specified in later calculations.

Attributes:
  • a1 (float) – Intercept parameter for N <= nswitch.

  • a2 (float) – Intercept parameter for N > nswitch. Equals a1 for linear curves.

  • loga1 (float) – Common logarithm with base 10 of a1.

  • loga2 (float) – Common logarithm with base 10 of a2.

  • m1 (float) – Negative inverse slope parameter (for bilinear curves: used for N < nswitch).

  • m2 (float) – Negative inverse slope parameter for N > nswitch. Equals m1 for linear curves.

  • name (str) – S-N curve name.

  • nswitch (float) – Number of cycles at transition from m1 to m2. Applies only to bilinear curves.

  • sswitch (float) – Stress range at transition from m1 to m2. Applies only to bilinear curves.

  • t_exp (float) – Thickness correction exponent.

  • t_ref (float, optional) – Reference thickness [mm] for thickness correction.

Notes

For linear curves (single slope), the following input parameters are required: m1, a1 (or loga1). For bi-linear curves, the following additional parameters are required: m2, nswitch.

If S-N curve is overdefined (e.g. both loga1 and a1 are defined), the S-N curve is established based on the parameter order listed above (under “Parameters”).

Properties

a

Intercept parameter of linear (single slope) S-N curve (equal to a1).

bilinear

Returns True if S-N curve is bi-linear, otherwise False.

loga

Logarithm (base 10) of intercept parameter of linear (single slope) S-N curve (equal to loga1).

m

Slope parameter of linear (single slope) S-N curve (equal to m1).

Methods

fatigue_strength(n[, t])

Magnitude of stress range leading to a particular fatigue life (in terms of number of cycles.

n(s[, t])

Predicted number of cycles to failure for specified stress range(s) and thickness.

print_parameters()

thickness_correction(t)

Thickness correction for specified thickness.

property a#

Intercept parameter of linear (single slope) S-N curve (equal to a1). For bi-linear curves, use a1 and a2 instead.

property bilinear#

Returns True if S-N curve is bi-linear, otherwise False.

property loga#

Logarithm (base 10) of intercept parameter of linear (single slope) S-N curve (equal to loga1). For bi-linear curves, use loga1 and loga2 instead.

property m#

Slope parameter of linear (single slope) S-N curve (equal to m1). Not available for bi-linear curves.

fatigue_strength(n, t=None)#

Magnitude of stress range leading to a particular fatigue life (in terms of number of cycles.

Parameters:
  • n (float) – Number of cycles (fatigue life) [-].

  • t (float, optional) – Thickness [mm]. If specified, thickness reference and exponent must be defined for the S-N curve. If not specified, thickness correction is not taken into account.

Returns:

Fatigue strength, i.e. magnitude of stress range leading to specified fatigue life (no. of cycles).

Return type:

float

Raises:

ValueError – If thickness is specified, but thickness reference and exponent is not defined.:

n(s, t=None)#

Predicted number of cycles to failure for specified stress range(s) and thickness.

Parameters:
  • s (float or np.ndarray) – Stress range(s) [MPa].

  • t (float, optional) – Thickness [mm]. If specified, thickness reference and exponent must be defined for the S-N curve. If not specified, thickness correction is not taken into account.

Returns:

Predicted number of cycles to failure. Output type is same as input type (float or np.ndarray)

Return type:

float or np.ndarray

Raises:

ValueError – If thickness is specified, but thickness reference and exponent is not defined.:

thickness_correction(t)#

Thickness correction for specified thickness.

Parameters:

t (float) – Thickness [mm]

Returns:

Thickness correction factor.

Return type:

float

Raises:

ValueError – If thickness correction is not defined, i.e. t_exp and t_ref are not defined.

print_parameters()#

Functions API

minersum(srange, count, sn, td=1.0, scf=1.0, th=None, retbins=False, args=(), kwds=None)#

Fatigue damage (Palmgren-Miner sum) calculation based on stress cycle histogram and S-N curve.

Parameters:
  • srange (np.ndarray or list of floats) – List of stress ranges in histogram (Note: only one value per bin).

  • count (np.ndarray or list of floats) – Cycle count for each of the stress ranges. May be specified as number of cycles [-] or cycle rate [1/s]. If cycle rate is specified, specify duration td for scaling to number of cycles.

  • sn (dict or object or callable) – Dictionary with S-N curve parameters, or class instance, or callable (function). If dict: An SNCurve instance is initiated based on parameters defined in dict. Expected keys are ‘m1’ and ‘a1’ (or ‘loga1’) for linear S-N curve, and also ‘m2’ and ‘nswitch’ if bi-linear S-N curve. If object: Assumed to be class instance, and expected to have a callable method n which takes stress range array as input (e.g. an instance of the SNCurve class.). If callable: A function with takes stress range array as input, and returns an array of fatigue capacity (no. of cycles to failure), similar to SNCurve.n(). Additional positional and keyword arguments may be passed using parameter args and kwargs.

  • td (float, optional) – Duration [s]. Used to scale the histogram from cycle rate to number of cycles. Use 1 (the default) if histogram already represents number of cycles.

  • scf (float, optional) – Stress concentration factor to be applied on stress ranges. Default: 1.

  • th (float, optional) – Thickness [mm] for thickness correction. If specified, reference thickness and thickness exponent must be defined for the S-N curve given. NOTE: This parameter is only accepted if parameter sn is given as a dict or a SNCurve instance. In any other case, used the args or kwds parameter to pass additional parameters to the capacity function.

  • retbins (bool, optional) – If True, minersum per bin is also returned.

  • args (tuple, optional) – Tuple of arguments that are passed to the capacity function defined by parameter sn.

  • kwds (dict, optional) – Dictionary with keyword arguments that are passed to the capacity function defined by parameter sn.

Returns:

  • float – Fatigue damage (Palmgren-Miner sum).

  • np.ndarray, optional – Fatigue damage (Palmgren-Miner sum) for each stress range bin. Returned if retbin=True.

Raises:
  • ValueError: – If thickness is given but thickness correction not specified for S-N curve.

  • AssertionError – If parameter sn is not a dict, a callable, or a class instance with callable method n().

Notes

New in version 4.7.0.

Parameter sn may now be a callable (function) that calculates the fatigue capacity (number of cycles to failure) for a given stress range. It must accept array input, and return array output. To pass additional arguments to this function, use the args and kwargs keywords.

minersum_weibull(q, h, sn, v0, td=None, scf=1.0, th=None)#

Fatigue damage (Palmgren-Miner sum) calculation based on (2-parameter) Weibull stress cycle distribution and S-N curve. Ref. DNV-RP-C03 (2016) eq. F.12-1.

Parameters:
  • q (float) – Weibull scale parameter (in 2-parameter distribution).

  • h (float) – Weibull shape parameter (in 2-parameter distribution).

  • sn (dict or SNCurve) – Dictionary with S-N curve parameters, alternatively an SNCurve instance. If dict, expected attributes are: ‘m1’, ‘m2’, ‘a1’ (or ‘loga1’), ‘nswitch’.

  • v0 (float,) – Cycle rate [1/s].

  • td (float, optional) – Duration [s] (or design life, in seconds). Default is 31536000 (no. of seconds in a year, or 365 days).

  • scf (float, optional) – Stress concentration factor to be applied on stress ranges.

  • th (float, optional) – Thickness [mm] for thickness correction. If specified, reference thickness and thickness exponent must be defined for the S-N curve given.

Returns:

Fatigue damage (Palmgren-Miner sum).

Return type:

float

Raises:

ValueError: – If thickness is given but thickness correction not specified for S-N curve.