ASTM counting methods

[1]:
# Import auxiliary libraries for demonstration

import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np

plt.rcParams[ "figure.figsize" ] = [ 5, 4 ]

plt.rcParams[ "figure.dpi" ] = 80
plt.rcParams[ "font.family" ] = "Times New Roman"
plt.rcParams[ "font.size" ] = '14'

ASTM level crossing counting

Function astmLevelCrossingCounting implements the level crossing counting method in ASTM E1049-85 (2017): sec 5.1.1.

By the definition:

One count is recorded each time the positive sloped portion of the load exceeds a preset level above the reference load, and each time the negative sloped portion of the load exceeds a preset level below the reference load. Reference load crossings are counted on the positive sloped portion of the loading history.

Function help

[2]:
from ffpack.lcc import astmLevelCrossingCounting
help( astmLevelCrossingCounting )
Help on function astmLevelCrossingCounting in module ffpack.lcc.astmCounting:

astmLevelCrossingCounting(data, refLevel=0.0, levels=None, aggregate=True)
    ASTM level crossing counting in E1049-85: sec 5.1.1.

    Parameters
    ----------
    data: 1d array
        Load sequence data for counting.
    refLevel: scalar, optional
        Reference level.
    levels: 1d array
        Self-defined levels for counting.
    aggragate: bool, optional
        If aggregate is set to False, the original sequence for internal counting,
        e.g., [ crossPoint1, corssPoint2, ... ], will be returned.

    Returns
    -------
    rst: 2d array
        Sorted counting results.

    Raises
    ------
    ValueError
        If the data length is less than 2 or the data dimension is not 1.

    Examples
    --------
    >>> from ffpack.lcc import astmLevelCrossingCounting
    >>> data = [ -0.8, 1.3, 0.7, 3.4, 0.7, 2.5, -1.4, -0.5, -2.3,
    >>>          -2.2, -2.6, -2.4, -3.3, 1.5, 0.6, 3.4, -0.5 ]
    >>> rst = astmLevelCrossingCounting( data )

Example with default values

[3]:
astmLccSequenceData = [ -0.8, 1.3, 0.7, 3.4, 0.7, 2.5, -1.4, -0.5, -2.3,
                        -2.2, -2.6, -2.4, -3.3, 1.5, 0.6, 3.4, -0.5 ]

astmLccCountingResults = astmLevelCrossingCounting( astmLccSequenceData )
[4]:
print( astmLccCountingResults )
[[-3.0, 1.0], [-2.0, 1.0], [-1.0, 2.0], [0.0, 2.0], [1.0, 5.0], [2.0, 3.0], [3.0, 2.0]]
[5]:
fig, ( ax1, ax2 ) = plt.subplots( 1, 2, figsize=( 10, 4 ) )

ax1.plot( astmLccSequenceData, "o-" )

ax1.tick_params( axis='x', direction="in", length=5 )
ax1.tick_params( axis='y', direction="in", length=5 )
ax1.set_ylabel( "Load units" )
ax1.set_xlabel( "Data points" )
ax1.set_title( "Sequence data" )

ax2.barh( np.array( astmLccCountingResults )[ :, 0 ],
          np.array( astmLccCountingResults )[ :, 1 ] )

ax2.tick_params(axis='x', direction="in", length=5)
ax2.tick_params(axis='y', direction="in", length=5)
ax2.set_ylabel( "Load level units" )
ax2.set_xlabel( "Counts" )
ax2.set_title( "ASTM level crossing counting" )

plt.tight_layout()
plt.show()
../_images/moduleCookbook_lccASTMCountingMethods_9_0.png

ASTM peak counting

Function astmPeakCounting implements the peak counting method in ASTM E1049-85 (2017): sec 5.2.1.

By the definition:

Peaks above the reference load level are counted, and valleys below the reference load level are counted.

Function help

[6]:
from ffpack.lcc import astmPeakCounting
help( astmPeakCounting )
Help on function astmPeakCounting in module ffpack.lcc.astmCounting:

astmPeakCounting(data, refLevel=None, aggregate=True)
    ASTM peak counting in E1049-85: sec 5.2.1.

    Parameters
    ----------
    data: 1d array
        Load sequence data for counting.
    refLevel: scalar, optional
        Reference level.
    aggragate: bool, optional
        If aggregate is set to False, the original sequence for internal counting,
        e.g., [ peak1, peak2, ... ], will be returned.

    Returns
    -------
    rst: 2d array
        Sorted counting results.

    Raises
    ------
    ValueError
        If the data length is less than 2 or the data dimension is not 1.

    Examples
    --------
    >>> from ffpack.lcc import astmPeakCounting
    >>> data = [ 0.0, 1.5, 0.5, 3.5, 0.5, 2.5, -1.5, -0.5, -2.5,
    >>>          -2.0, -2.7, -2.5, -3.5, 1.5, 0.5, 3.5, -0.5 ]
    >>> rst = astmPeakCounting( data )

Example with default values

[7]:
astmPcSequenceData = [ 0.0, 1.5, 0.5, 3.5, 0.5, 2.5, -1.5, -0.5, -2.5,
                       -2.0, -2.7, -2.5, -3.5, 1.5, 0.5, 3.5, -0.5 ]

astmPcCountingResults = astmPeakCounting( astmPcSequenceData )
[8]:
print( astmPcCountingResults )
[[-3.5, 1.0], [-2.7, 1.0], [-2.5, 1.0], [-1.5, 1.0], [1.5, 2.0], [2.5, 1.0], [3.5, 2.0]]
[9]:
fig, ( ax1, ax2 ) = plt.subplots( 1, 2, figsize=( 10, 4 ) )

ax1.plot( astmPcSequenceData, "o-" )

ax1.tick_params( axis='x', direction="in", length=5 )
ax1.tick_params( axis='y', direction="in", length=5 )
ax1.set_ylabel( "Load units" )
ax1.set_xlabel( "Data points" )
ax1.set_title( "Sequence data" )

ax2.barh( np.array( astmPcCountingResults )[ :, 0 ],
          np.array( astmPcCountingResults )[ :, 1 ] )

ax2.tick_params( axis='x', direction="in", length=5 )
ax2.tick_params( axis='y', direction="in", length=5 )
ax2.set_ylabel( "Peak units" )
ax2.set_xlabel( "Counts" )
ax2.set_title( "ASTM peak counting" )

plt.tight_layout()
plt.show()
../_images/moduleCookbook_lccASTMCountingMethods_17_0.png

ASTM simple range counting

Function astmSimpleRangeCounting implements the simple range counting method in ASTM E1049-85 (2017): sec 5.3.1.

By the definition:

A range is defined as the difference between two successive reversals, the range being positive when a valley is followed by a peak and negative when a peak is followed by a valley. If only positive or only negative ranges are counted, then each is counted as one cycle. If both positive and negative ranges are counted, then each is counted as one-half cycle.

Function help

[10]:
from ffpack.lcc import astmSimpleRangeCounting
help( astmSimpleRangeCounting )
Help on function astmSimpleRangeCounting in module ffpack.lcc.astmCounting:

astmSimpleRangeCounting(data, aggregate=True)
    ASTM simple range counting in E1049-85: sec 5.3.1.

    Parameters
    ----------
    data: 1d array
        Load sequence data for counting.
    aggragate: bool, optional
        If aggregate is set to False, the original sequence for internal counting,
        e.g., [ [ rangeStart1, rangeEnd1, count1 ],
        [ rangeStart2, rangeEnd2, count2 ], ... ], will be returned.

    Returns
    -------
    rst: 2d array
        Sorted counting results.

    Raises
    ------
    ValueError
        If the data length is less than 2 or the data dimension is not 1.

    Examples
    --------
    >>> from ffpack.lcc import astmSimpleRangeCounting
    >>> data = [ -2.0, 1.0, -3.0, 5.0, -1.0, 3.0, -4.0, 4.0, -2.0 ]
    >>> rst = astmSimpleRangeCounting( data )

Example with default values

[11]:
astmSrcSequenceData = [ -2.0, 1.0, -3.0, 5.0, -1.0, 3.0, -4.0, 4.0, -2.0 ]

astmSrcCountingResults = astmSimpleRangeCounting( astmSrcSequenceData )
[12]:
print( astmSrcCountingResults )
[[3.0, 0.5], [4.0, 1.0], [6.0, 1.0], [7.0, 0.5], [8.0, 1.0]]
[13]:
fig, ( ax1, ax2 ) = plt.subplots( 1, 2, figsize=( 10, 4 ) )

ax1.plot( astmSrcSequenceData, "o-" )

ax1.tick_params( axis='x', direction="in", length=5 )
ax1.tick_params( axis='y', direction="in", length=5 )
ax1.set_ylabel( "Load units" )
ax1.set_xlabel( "Data points" )
ax1.set_title( "Sequence data" )

ax2.barh( np.array( astmSrcCountingResults )[ :, 0 ],
          np.array( astmSrcCountingResults )[ :, 1 ] )

ax2.tick_params( axis='x', direction="in", length=5 )
ax2.tick_params( axis='y', direction="in", length=5 )
ax2.set_ylabel( "Load range units" )
ax2.set_xlabel( "Counts" )
ax2.set_title( "ASTM simple range counting" )

plt.tight_layout()
plt.show()
../_images/moduleCookbook_lccASTMCountingMethods_25_0.png

ASTM range pair counting

Function astmRangePairCounting implements the range pair counting method in ASTM E1049-85 (2017): sec 5.4.3.

By the definition:

The range-paired method counts a range as a cycle if it can be paired with a subsequent loading in the opposite direction.

Function help

[14]:
from ffpack.lcc import astmRangePairCounting
help( astmRangePairCounting )
Help on function astmRangePairCounting in module ffpack.lcc.astmCounting:

astmRangePairCounting(data, aggregate=True)
    ASTM range pair counting in E1049-85: sec 5.4.3.

    Parameters
    ----------
    data: 1d array
        Load sequence data for counting.
    aggragate: bool, optional
        If aggregate is set to False, the original sequence for internal counting,
        e.g., [ [ rangeStart1, rangeEnd1, count1 ],
        [ rangeStart2, rangeEnd2, count2 ], ... ], will be returned.

    Returns
    -------
    rst: 2d array
        Sorted counting results.

    Raises
    ------
    ValueError
        If the data length is less than 2 or the data dimension is not 1.

    Examples
    --------
    >>> from ffpack.lcc import astmRangePairCounting
    >>> data = [ -2.0, 1.0, -3.0, 5.0, -1.0, 3.0, -4.0, 4.0, -2.0 ]
    >>> rst = astmRangePairCounting( data )

Example with default values

[15]:
astmRpcSequenceData = [ -2.0, 1.0, -3.0, 5.0, -1.0, 3.0, -4.0, 4.0, -2.0 ]

astmRpcCountingResults = astmRangePairCounting( astmRpcSequenceData )
[16]:
print( astmRpcCountingResults )
[[3.0, 1.0], [4.0, 1.0], [6.0, 1.0], [8.0, 1.0]]
[17]:
fig, ( ax1, ax2 ) = plt.subplots( 1, 2, figsize=( 10, 4 ) )

ax1.plot( astmRpcSequenceData, "o-" )

ax1.tick_params( axis='x', direction="in", length=5 )
ax1.tick_params( axis='y', direction="in", length=5 )
ax1.set_ylabel( "Load units" )
ax1.set_xlabel( "Data points" )
ax1.set_title( "Sequence data" )

ax2.barh( np.array( astmRpcCountingResults )[ :, 0 ],
          np.array( astmRpcCountingResults )[ :, 1 ] )

ax2.tick_params( axis='x', direction="in", length=5 )
ax2.tick_params( axis='y', direction="in", length=5 )
ax2.set_ylabel( "Load range units" )
ax2.set_xlabel( "Counts" )
ax2.set_title( "ASTM range pair counting" )

plt.tight_layout()
plt.show()
../_images/moduleCookbook_lccASTMCountingMethods_33_0.png

ASTM rainflow counting

Function astmRainflowCounting implements the rainflow counting method in ASTM E1049-85 (2017): sec 5.4.4.

Notes

The original rainflow counting method in ASTM does not provide any preprocessing method for the sequence data or postprocessing method for counting results. To get meaningful results, we provide the preprocessing sequenceDigitization function to digitize the input sequence data with a specific resolution, such as 0.5, or the postprocessing cycleCountingAggregation function to aggregate the cycle counting results. See the following examples for details.

Function help

[18]:
from ffpack.lcc import astmRainflowCounting
help( astmRainflowCounting )
Help on function astmRainflowCounting in module ffpack.lcc.astmCounting:

astmRainflowCounting(data, aggregate=True)
    ASTM rainflow counting in E1049-85: sec 5.4.4.

    Parameters
    ----------
    data: 1d array
        Load sequence data for counting.
    aggragate: bool, optional
        If aggregate is set to False, the original sequence for internal counting,
        e.g., [ [ rangeStart1, rangeEnd1, count1 ],
        [ rangeStart2, rangeEnd2, count2 ], ... ], will be returned.

    Returns
    -------
    rst: 2d array
        Sorted counting results.

    Raises
    ------
    ValueError
        If the data length is less than 2 or the data dimension is not 1.

    Examples
    --------
    >>> from ffpack.lcc import astmRainflowCounting
    >>> data = [ -2.0, 1.0, -3.0, 5.0, -1.0, 3.0, -4.0, 4.0, -2.0 ]
    >>> rst = astmRainflowCounting( data )

Example with default values

[19]:
astmRfcSequenceData = [ -2.0, 1.0, -3.0, 5.0, -1.0, 3.0, -4.0, 4.0, -2.0 ]

astmRfcCountingResults = astmRainflowCounting( astmRfcSequenceData )
[20]:
print( astmRfcCountingResults )
[[3.0, 0.5], [4.0, 1.5], [6.0, 0.5], [8.0, 1.0], [9.0, 0.5]]
[21]:
fig, ( ax1, ax2 ) = plt.subplots( 1, 2, figsize=( 10, 4 ) )

ax1.plot( astmRfcSequenceData, "o-" )

ax1.tick_params( axis='x', direction="in", length=5 )
ax1.tick_params( axis='y', direction="in", length=5 )
ax1.set_ylabel( "Load units" )
ax1.set_xlabel( "Data points" )
ax1.set_title( "Sequence data" )

ax2.barh( np.array( astmRfcCountingResults )[ :, 0 ],
          np.array( astmRfcCountingResults )[ :, 1 ] )

ax2.tick_params( axis='x', direction="in", length=5 )
ax2.tick_params( axis='y', direction="in", length=5 )
ax2.set_ylabel( "Load range units" )
ax2.set_xlabel( "Counts" )
ax2.set_title( "ASTM rainflow counting" )

plt.tight_layout()
plt.show()
../_images/moduleCookbook_lccASTMCountingMethods_41_0.png

Example with sequence digitization

This example demonstrates the usage of sequenceDigitization function before the rainflow counting method. The rainflow counting method counts the number of the different load ranges. Therefore, undigitized sequence data can generate noisy output results. With the sequenceDigitization function, the output results can be aggregated.

[22]:
from ffpack.utils import sequenceDigitization
[23]:
astmRfcSequenceData = [ -2.4, 1.3, -3.3, 4.6, -1.4, 3.2, -4.4, 4.2, -2.1 ]
digitizedAstmRfcSequenceData = sequenceDigitization( astmRfcSequenceData,
                                                     resolution=1.0 )

originalAstmRfcCountingResults = astmRainflowCounting( astmRfcSequenceData )
digitizedAstmRfcCountingResults = astmRainflowCounting( digitizedAstmRfcSequenceData )
[24]:
print( "Original sequence data: " )
print( astmRfcSequenceData )
print()
print( "Digitized sequence data with resolution of 1.0: " )
print( digitizedAstmRfcSequenceData )
print()

with np.printoptions( precision=3, suppress=True ):
    print( "Original rainflow counting results: " )
    print( np.array( originalAstmRfcCountingResults ) )
    print()
    print( "Digitized rainflow counting results: " )
    print( np.array( digitizedAstmRfcCountingResults ) )
Original sequence data:
[-2.4, 1.3, -3.3, 4.6, -1.4, 3.2, -4.4, 4.2, -2.1]

Digitized sequence data with resolution of 1.0:
[-2.0, 1.0, -3.0, 5.0, -1.0, 3.0, -4.0, 4.0, -2.0]

Original rainflow counting results:
[[3.7 0.5]
 [4.6 1.5]
 [6.3 0.5]
 [7.9 0.5]
 [8.6 0.5]
 [9.  0.5]]

Digitized rainflow counting results:
[[3.  0.5]
 [4.  1.5]
 [6.  0.5]
 [8.  1. ]
 [9.  0.5]]
[25]:
fig, ( ax1, ax2 ) = plt.subplots( 2, 2, figsize=( 10, 8 ) )

ax1[0].plot( astmRfcSequenceData, "o-" )

ax1[0].tick_params( axis='x', direction="in", length=5 )
ax1[0].tick_params( axis='y', direction="in", length=5 )
ax1[0].set_ylabel( "Load units" )
ax1[0].set_xlabel( "Data points" )
ax1[0].set_title( "Original sequence data" )
ax1[0].set_yticks( np.arange(-5, 6.5, 1) )
ax1[0].grid( axis='y', color="0.7" )

ax1[1].barh( np.array( originalAstmRfcCountingResults )[ :, 0 ],
             np.array( originalAstmRfcCountingResults )[ :, 1 ] )

ax1[1].tick_params( axis='x', direction="in", length=5 )
ax1[1].tick_params( axis='y', direction="in", length=5 )
ax1[1].set_ylabel( "Load range units" )
ax1[1].set_xlabel( "Counts" )
ax1[1].set_title( "Original ASTM rainflow counting" )

ax2[0].plot( digitizedAstmRfcSequenceData, "o-" )

ax2[0].tick_params( axis='x', direction="in", length=5 )
ax2[0].tick_params( axis='y', direction="in", length=5 )
ax2[0].set_ylabel( "Load units" )
ax2[0].set_xlabel( "Data points" )
ax2[0].set_title( "Digitized sequence data" )
ax2[0].set_yticks( np.arange(-5, 6.5, 1) )
ax2[0].grid( axis='y', color="0.7" )

ax2[1].barh( np.array( digitizedAstmRfcCountingResults )[ :, 0 ],
             np.array( digitizedAstmRfcCountingResults )[ :, 1 ] )

ax2[1].tick_params( axis='x', direction="in", length=5 )
ax2[1].tick_params( axis='y', direction="in", length=5 )
ax2[1].set_ylabel( "Load range units" )
ax2[1].set_xlabel( "Counts" )
ax2[1].set_title( "Digitized ASTM rainflow counting" )

plt.tight_layout()
plt.show()
../_images/moduleCookbook_lccASTMCountingMethods_46_0.png

Example with cycle counting aggregation

This example demonstrates the usage of cycleCountingAggregation function after the rainflow counting method. The rainflow counting method counts the number of the different load ranges. Therefore, undigitized sequence data can generate noisy output results. With the cycleCountingAggregation function, the output results can be aggregated.

It should be noted that there exists discrepancy of the final counting results with the preprocessing sequenceDigitization method and the postprocessing cycleCountingAggregation method.

[26]:
from ffpack.utils import cycleCountingAggregation
[27]:
astmRfcSequenceData = [ -2.4, 1.3, -3.3, 4.6, -1.4, 3.2, -4.4, 4.2, -2.1 ]

originalAstmRfcCountingResults = astmRainflowCounting( astmRfcSequenceData )
aggregatedAstmRfcCountingResults = cycleCountingAggregation(
    originalAstmRfcCountingResults )
[28]:
print( "Original sequence data: " )
print( astmRfcSequenceData )
print()

with np.printoptions( precision=3, suppress=True ):
    print( "Original rainflow counting results: " )
    print( np.array( originalAstmRfcCountingResults ) )
    print()
    print( "Digitized rainflow counting results: " )
    print( np.array( aggregatedAstmRfcCountingResults ) )
Original sequence data:
[-2.4, 1.3, -3.3, 4.6, -1.4, 3.2, -4.4, 4.2, -2.1]

Original rainflow counting results:
[[3.7 0.5]
 [4.6 1.5]
 [6.3 0.5]
 [7.9 0.5]
 [8.6 0.5]
 [9.  0.5]]

Digitized rainflow counting results:
[[4.  0.5]
 [5.  1.5]
 [6.  0.5]
 [8.  0.5]
 [9.  1. ]]
[29]:
fig, ( ax1, ax2 ) = plt.subplots( 1, 2, figsize=( 10, 4 ) )

ax1.barh( np.array( originalAstmRfcCountingResults )[ :, 0 ],
          np.array( originalAstmRfcCountingResults )[ :, 1 ] )

ax1.tick_params( axis='x', direction="in", length=5 )
ax1.tick_params( axis='y', direction="in", length=5 )
ax1.set_ylabel( "Load range units" )
ax1.set_xlabel( "Counts" )
ax1.set_title( "Original ASTM rainflow counting" )

ax2.barh( np.array( aggregatedAstmRfcCountingResults )[ :, 0 ],
          np.array( aggregatedAstmRfcCountingResults )[ :, 1 ] )

ax2.tick_params( axis='x', direction="in", length=5 )
ax2.tick_params( axis='y', direction="in", length=5 )
ax2.set_ylabel( "Load range units" )
ax2.set_xlabel( "Counts" )
ax2.set_title( "Aggregated ASTM rainflow counting" )

plt.tight_layout()
plt.show()
../_images/moduleCookbook_lccASTMCountingMethods_51_0.png

ASTM rainflow counting for repeating histories

Function astmRainflowRepeatHistoryCounting implements the rainflow counting for repeating histories method in ASTM E1049-85 (2017): sec 5.4.5.

Notes

The original rainflow counting for repeating histories method in ASTM does not provide any preprocessing method for the sequence data or postprocessing method for counting results. To get meaningful results, we provide the preprocessing sequenceDigitization function to digitize the input sequence data with a specific resolution, such as 0.5, or the postprocessing cycleCountingAggregation function to aggregate the cycle counting results. See the previous “ASTM rainflow counting” section for details.

Function help

[30]:
from ffpack.lcc import astmRainflowRepeatHistoryCounting
help( astmRainflowRepeatHistoryCounting )
Help on function astmRainflowRepeatHistoryCounting in module ffpack.lcc.astmCounting:

astmRainflowRepeatHistoryCounting(data, aggregate=True)
    ASTM simplified rainflow counting for repeating histories in E1049-85: sec 5.4.5.

    Parameters
    ----------
    data: 1d array
        Load sequence data for counting.
    aggragate: bool, optional
        If aggregate is set to False, the original sequence for internal counting,
        e.g., [ [ rangeStart1, rangeEnd1, count1 ],
        [ rangeStart2, rangeEnd2, count2 ], ... ], will be returned.

    Returns
    -------
    rst: 2d array
        Sorted counting results.

    Raises
    ------
    ValueError
        If the data length is less than 2 or the data dimension is not 1.
        If the data is not repeatable: first data point is different from the
        last data point.

    Examples
    --------
    >>> from ffpack.lcc import astmRainflowRepeatHistoryCounting
    >>> data = [ -2.0, 1.0, -3.0, 5.0, -1.0, 3.0, -4.0, 4.0, -2.0 ]
    >>> rst = astmRainflowRepeatHistoryCounting( data )

Example with default values

[31]:
astmRfcrhSequenceData = [ -2.0, 1.0, -3.0, 5.0, -1.0, 3.0, -4.0, 4.0, -2.0 ]

astmmRfcrhCountingResults = astmRainflowRepeatHistoryCounting( astmRfcrhSequenceData )
[32]:
print( astmmRfcrhCountingResults )
[[3.0, 1.0], [4.0, 1.0], [7.0, 1.0], [9.0, 1.0]]
[33]:
fig, ( ax1, ax2 ) = plt.subplots( 1, 2, figsize=( 10, 4 ) )

ax1.plot( astmRfcrhSequenceData, "o-" )

ax1.tick_params( axis='x', direction="in", length=5 )
ax1.tick_params( axis='y', direction="in", length=5 )
ax1.set_ylabel( "Load units" )
ax1.set_xlabel( "Data points" )
ax1.set_title( "Sequence data" )

ax2.barh( np.array( astmmRfcrhCountingResults )[ :, 0 ],
          np.array( astmmRfcrhCountingResults )[ :, 1 ] )

ax2.tick_params( axis='x', direction="in", length=5 )
ax2.tick_params( axis='y', direction="in", length=5 )
ax2.set_ylabel( "Load range units" )
ax2.set_xlabel( "Counts" )
ax2.set_title( "ASTM rainflow counting for repeating histories" )

plt.tight_layout()
plt.show()
../_images/moduleCookbook_lccASTMCountingMethods_59_0.png