From e732dd35b0e1dc167b29dbeebee1e0c28b6b96ab Mon Sep 17 00:00:00 2001 From: madsmpedersen <m@madsp.dk> Date: Mon, 9 Nov 2015 09:47:53 +0100 Subject: [PATCH] reorganized the fatigue module --- README.md | 1 + wetb/fatigue_tools/fatigue.py | 219 ++---------------- .../rainflowcounting/__init__.py | 0 .../fatigue_tools/rainflowcounting/compile.py | 7 + .../{ => rainflowcounting}/pair_range.py | 0 .../{ => rainflowcounting}/pair_range.pyd | Bin 39936 -> 39936 bytes .../{ => rainflowcounting}/peak_trough.py | 0 .../{ => rainflowcounting}/peak_trough.pyd | Bin 37888 -> 37888 bytes .../rainflowcounting/rainflowcount.py | 126 ++++++++++ .../rainflowcount_astm.py | 0 .../rainflowcount_astm.pyd | Bin 45056 -> 45056 bytes .../rainflowcounting/rfc_hist.py | 49 ++++ 12 files changed, 208 insertions(+), 194 deletions(-) create mode 100644 wetb/fatigue_tools/rainflowcounting/__init__.py create mode 100644 wetb/fatigue_tools/rainflowcounting/compile.py rename wetb/fatigue_tools/{ => rainflowcounting}/pair_range.py (100%) rename wetb/fatigue_tools/{ => rainflowcounting}/pair_range.pyd (71%) rename wetb/fatigue_tools/{ => rainflowcounting}/peak_trough.py (100%) rename wetb/fatigue_tools/{ => rainflowcounting}/peak_trough.pyd (99%) create mode 100644 wetb/fatigue_tools/rainflowcounting/rainflowcount.py rename wetb/fatigue_tools/{ => rainflowcounting}/rainflowcount_astm.py (100%) rename wetb/fatigue_tools/{ => rainflowcounting}/rainflowcount_astm.pyd (99%) create mode 100644 wetb/fatigue_tools/rainflowcounting/rfc_hist.py diff --git a/README.md b/README.md index 1900083..2365c88 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ General Time Series Data Format, a binary hdf5 data format for storing time seri ### [fatigue_tools](wetb/fatigue_tools) - [fatigue](wetb/fatigue_tools/fatigue.py): Rainflow counting, cycle matrix and equvivalent loads +- [bearing_damage](wetb/fatigue_tools/bearing_damage.py): Calculate a comparable measure of bearing damage ### [wind](wetb/wind) - [shear](wetb/wind/shear.py): Calculate and fit wind shear diff --git a/wetb/fatigue_tools/fatigue.py b/wetb/fatigue_tools/fatigue.py index 0c7425f..a411a16 100644 --- a/wetb/fatigue_tools/fatigue.py +++ b/wetb/fatigue_tools/fatigue.py @@ -2,199 +2,23 @@ Created on 04/03/2013 @author: mmpe -'rainflow_windap' and 'rainflow_astm' are two different methods to for rainflow counting + 'eq_load' calculate equivalent loads using one of the two rain flow counting methods 'cycle_matrix' calculates a matrix of cycles (binned on amplitude and mean value) 'eq_load_and_cycles' is used to calculate eq_loads of multiple time series (e.g. life time equivalent load) +The methods uses the rainflow counting routines (See documentation in top of methods): +- 'rainflow_windap': (Described in "Recommended Practices for Wind Turbine Testing - 3. Fatigue Loads", + 2. edition 1990, Appendix A) +or +- 'rainflow_astm' (based on the c-implementation by Adam Nieslony found at the MATLAB Central File Exchange + http://www.mathworks.com/matlabcentral/fileexchange/3026) ''' - -#try: -# """ -# The cython_import function compiles modules using cython. -# It is found at: https://github.com/madsmpedersen/MMPE/blob/master/cython_compile/cython_compile.py -# """ -# from mmpe.cython_compile.cython_compile import cython_import -#except ImportError: -# cython_import = __import__ import numpy as np +from wetb.fatigue_tools.rainflowcounting import rainflowcount - -def rfc_hist(sig_rf, nrbins=46): - """ - Histogram of rainflow counted cycles - ==================================== - - hist, bin_edges, bin_avg = rfc_hist(sig, nrbins=46) - - Divide the rainflow counted cycles of a signal into equally spaced bins. - - Created on Wed Feb 16 16:53:18 2011 - @author: David Verelst - Modified 10.10.2011 by Mads M Pedersen to elimintate __copy__ and __eq__ - - Parameters - ---------- - sig_rf : array-like - As output by rfc_astm or rainflow - - nrbins : int, optional - Divide the rainflow counted amplitudes in a number of equally spaced - bins. - - Returns - ------- - hist : array-like - Counted rainflow cycles per bin, has nrbins elements - - bin_edges : array-like - Edges of the bins, has nrbins+1 elements. - - bin_avg : array-like - Average rainflow cycle amplitude per bin, has nrbins elements. - """ - - rf_half = sig_rf - - # the Matlab approach is to divide into 46 bins - bin_edges = np.linspace(0, 1, num=nrbins + 1) * rf_half.max() - hist = np.histogram(rf_half, bins=bin_edges)[0] - # calculate the average per bin - hist_sum = np.histogram(rf_half, weights=rf_half, bins=bin_edges)[0] - # replace zeros with one, to avoid 0/0 - hist_ = hist.copy() - hist_[(hist == 0).nonzero()] = 1.0 - # since the sum is also 0, the avg remains zero for those whos hist is zero - bin_avg = hist_sum / hist_ - - return hist, bin_edges, bin_avg - - -def check_signal(signal): - # check input data validity - if not type(signal).__name__ == 'ndarray': - raise TypeError('signal must be ndarray, not: ' + type(signal).__name__) - - elif len(signal.shape) not in (1, 2): - raise TypeError('signal must be 1D or 2D, not: ' + str(len(signal.shape))) - - if len(signal.shape) == 2: - if signal.shape[1] > 1: - raise TypeError('signal must have one column only, not: ' + str(signal.shape[1])) - if np.min(signal) == np.max(signal): - raise TypeError("Signal contains no variation") - - -def rainflow_windap(signal, levels=255., thresshold=(255 / 50)): - """Windap equivalent rainflow counting - - - Calculate the amplitude and mean values of half cycles in signal - - This algorithms used by this routine is implemented directly as described in - "Recommended Practices for Wind Turbine Testing - 3. Fatigue Loads", 2. edition 1990, Appendix A - - Parameters - ---------- - Signal : array-like - The raw signal - - levels : int, optional - The signal is discretize into this number of levels. - 255 is equivalent to the implementation in Windap - - thresshold : int, optional - Cycles smaller than this thresshold are ignored - 255/50 is equivalent to the implementation in Windap - - Returns - ------- - ampl : array-like - Peak to peak amplitudes of the half cycles - - mean : array-like - Mean values of the half cycles - - - Example - ------- - >>> signal = np.array([-2.0, 0.0, 1.0, 0.0, -3.0, 0.0, 5.0, 0.0, -1.0, 0.0, 3.0, 0.0, -4.0, 0.0, 4.0, 0.0, -2.0]) - >>> ampl, mean = rainflow_windap(signal) - """ - check_signal(signal) - #type <double> is required by <find_extreme> and <rainflow> - signal = signal.astype(np.double) - - offset = np.nanmin(signal) - signal -= offset - if np.nanmax(signal) > 0: - gain = np.nanmax(signal) / levels - signal = signal / gain - signal = np.round(signal).astype(np.int) - - from wetb.fatigue_tools.peak_trough import peak_trough - - - #Convert to list of local minima/maxima where difference > thresshold - sig_ext = peak_trough(signal, thresshold) - - from wetb.fatigue_tools.pair_range import pair_range_amplitude_mean - - #rainflow count - ampl_mean = pair_range_amplitude_mean(sig_ext) - - ampl_mean = np.array(ampl_mean) - ampl_mean = np.round(ampl_mean / thresshold) * gain * thresshold - ampl_mean[:, 1] += offset - return ampl_mean.T - - - -def rainflow_astm(signal): - """Matlab equivalent rainflow counting - - Calculate the amplitude and mean values of half cycles in signal - - This implemementation is based on the c-implementation by Adam Nieslony found at - the MATLAB Central File Exchange http://www.mathworks.com/matlabcentral/fileexchange/3026 - - Parameters - ---------- - Signal : array-like - The raw signal - - Returns - ------- - ampl : array-like - peak to peak amplitudes of the half cycles (note that the matlab implementation - uses peak amplitude instead of peak to peak) - - mean : array-like - Mean values of the half cycles - - - Examples - -------- - >>> signal = np.array([-2.0, 0.0, 1.0, 0.0, -3.0, 0.0, 5.0, 0.0, -1.0, 0.0, 3.0, 0.0, -4.0, 0.0, 4.0, 0.0, -2.0]) - >>> ampl, mean = rainflow_astm(signal) - """ - check_signal(signal) - - # type <double> is reuqired by <find_extreme> and <rainflow> - signal = signal.astype(np.double) - - # Import find extremes and rainflow. - # If possible the module is compiled using cython otherwise the python implementation is used - #cython_import('fatigue_tools.rainflowcount_astm') - from wetb.fatigue_tools.rainflowcount_astm import find_extremes, rainflowcount - - # Remove points which is not local minimum/maximum - sig_ext = find_extremes(signal) - - # rainflow count - ampl_mean = np.array(rainflowcount(sig_ext)) - - return np.array(ampl_mean).T +rainflow_windap = rainflowcount.rainflow_windap +rainflow_astm = rainflowcount.rainflow_astm def eq_load(signals, no_bins=46, m=[3, 4, 6, 8, 10, 12], neq=1, rainflow_func=rainflow_windap): @@ -239,14 +63,6 @@ def eq_load(signals, no_bins=46, m=[3, 4, 6, 8, 10, 12], neq=1, rainflow_func=ra except TypeError: return [[np.nan] * len(np.atleast_1d(m))] * len(np.atleast_1d(neq)) -# ampl, _ = rainflow_func(signals) -# if ampl is None: -# return [] -# hist_data, x, bin_avg = rfc_hist(ampl, no_bins) -# -# m = np.atleast_1d(m) -# -# return np.array([np.power(np.sum(0.5 * hist_data * np.power(bin_avg, m[i])) / neq, 1. / m[i]) for i in range(len(m))]) def eq_load_and_cycles(signals, no_bins=46, m=[3, 4, 6, 8, 10, 12], neq=[10 ** 6, 10 ** 7, 10 ** 8], rainflow_func=rainflow_windap): @@ -354,3 +170,18 @@ def cycle_matrix(signals, ampl_bins=10, mean_bins=10, rainflow_func=rainflow_win return cycles, ampl_bin_mean, ampl_edges, mean_bin_mean, mean_edges +if __name__ == "__main__": + signal1 = np.array([-2.0, 0.0, 1.0, 0.0, -3.0, 0.0, 5.0, 0.0, -1.0, 0.0, 3.0, 0.0, -4.0, 0.0, 4.0, 0.0, -2.0]) + signal2 = signal1 * 1.1 + + # equivalent load for default wöhler slopes + print (eq_load(signal1, no_bins=50, neq=17, rainflow_func=rainflow_windap)) + print (eq_load(signal1, no_bins=50, neq=17, rainflow_func=rainflow_astm)) + + # Cycle matrix with 4 amplitude bins and 4 mean value bins + print (cycle_matrix(signal1, 4, 4, rainflow_func=rainflow_windap)) + print (cycle_matrix(signal1, 4, 4, rainflow_func=rainflow_astm)) + + # Cycle matrix where signal1 and signal2 contributes with 50% each + print (cycle_matrix([(.5, signal1), (.5, signal2)], 4, 8, rainflow_func=rainflow_astm)) + diff --git a/wetb/fatigue_tools/rainflowcounting/__init__.py b/wetb/fatigue_tools/rainflowcounting/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/wetb/fatigue_tools/rainflowcounting/compile.py b/wetb/fatigue_tools/rainflowcounting/compile.py new file mode 100644 index 0000000..2666310 --- /dev/null +++ b/wetb/fatigue_tools/rainflowcounting/compile.py @@ -0,0 +1,7 @@ +import sys +sys.path.append("../../../../MMPE/") +from mmpe.cython_compile.cython_compile import cython_import + +cython_import('pair_range') +cython_import('peak_trough') +cython_import('rainflowcount_astm') diff --git a/wetb/fatigue_tools/pair_range.py b/wetb/fatigue_tools/rainflowcounting/pair_range.py similarity index 100% rename from wetb/fatigue_tools/pair_range.py rename to wetb/fatigue_tools/rainflowcounting/pair_range.py diff --git a/wetb/fatigue_tools/pair_range.pyd b/wetb/fatigue_tools/rainflowcounting/pair_range.pyd similarity index 71% rename from wetb/fatigue_tools/pair_range.pyd rename to wetb/fatigue_tools/rainflowcounting/pair_range.pyd index e3e28cb4e3bb7ab0fc866c3ab7d1ec242458abdb..30f8baed7a441394607bf0e5759a496a08e83a6d 100644 GIT binary patch delta 3984 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 delta 3962 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 Oat56vPTzoR2KgUhb0EI} diff --git a/wetb/fatigue_tools/peak_trough.py b/wetb/fatigue_tools/rainflowcounting/peak_trough.py similarity index 100% rename from wetb/fatigue_tools/peak_trough.py rename to wetb/fatigue_tools/rainflowcounting/peak_trough.py diff --git a/wetb/fatigue_tools/peak_trough.pyd b/wetb/fatigue_tools/rainflowcounting/peak_trough.pyd similarity index 99% rename from wetb/fatigue_tools/peak_trough.pyd rename to wetb/fatigue_tools/rainflowcounting/peak_trough.pyd index db4087cd89878143c3de4f75a60b5026f5dceb1f..250f85f0e8de0b2c2fe387e0175e92e9c8415cf7 100644 GIT binary patch delta 27 gcmZoz!PKyVX~G8<^Js^#jbA?GfEk-#<VvUk0Hak4bN~PV delta 27 gcmZoz!PKyVX~GAVW#4T=Hh%e#17>V~kt?AF0KLEsegFUf diff --git a/wetb/fatigue_tools/rainflowcounting/rainflowcount.py b/wetb/fatigue_tools/rainflowcounting/rainflowcount.py new file mode 100644 index 0000000..f05a1a0 --- /dev/null +++ b/wetb/fatigue_tools/rainflowcounting/rainflowcount.py @@ -0,0 +1,126 @@ +import numpy as np +def check_signal(signal): + # check input data validity + if not type(signal).__name__ == 'ndarray': + raise TypeError('signal must be ndarray, not: ' + type(signal).__name__) + + elif len(signal.shape) not in (1, 2): + raise TypeError('signal must be 1D or 2D, not: ' + str(len(signal.shape))) + + if len(signal.shape) == 2: + if signal.shape[1] > 1: + raise TypeError('signal must have one column only, not: ' + str(signal.shape[1])) + if np.min(signal) == np.max(signal): + raise TypeError("Signal contains no variation") + + +def rainflow_windap(signal, levels=255., thresshold=(255 / 50)): + """Windap equivalent rainflow counting + + + Calculate the amplitude and mean values of half cycles in signal + + This algorithms used by this routine is implemented directly as described in + "Recommended Practices for Wind Turbine Testing - 3. Fatigue Loads", 2. edition 1990, Appendix A + + Parameters + ---------- + Signal : array-like + The raw signal + + levels : int, optional + The signal is discretize into this number of levels. + 255 is equivalent to the implementation in Windap + + thresshold : int, optional + Cycles smaller than this thresshold are ignored + 255/50 is equivalent to the implementation in Windap + + Returns + ------- + ampl : array-like + Peak to peak amplitudes of the half cycles + + mean : array-like + Mean values of the half cycles + + + Example + ------- + >>> signal = np.array([-2.0, 0.0, 1.0, 0.0, -3.0, 0.0, 5.0, 0.0, -1.0, 0.0, 3.0, 0.0, -4.0, 0.0, 4.0, 0.0, -2.0]) + >>> ampl, mean = rainflow_windap(signal) + """ + check_signal(signal) + #type <double> is required by <find_extreme> and <rainflow> + signal = signal.astype(np.double) + + offset = np.nanmin(signal) + signal -= offset + if np.nanmax(signal) > 0: + gain = np.nanmax(signal) / levels + signal = signal / gain + signal = np.round(signal).astype(np.int) + + from wetb.fatigue_tools.rainflowcounting.peak_trough import peak_trough + + + #Convert to list of local minima/maxima where difference > thresshold + sig_ext = peak_trough(signal, thresshold) + + from wetb.fatigue_tools.rainflowcounting.pair_range import pair_range_amplitude_mean + + #rainflow count + ampl_mean = pair_range_amplitude_mean(sig_ext) + + ampl_mean = np.array(ampl_mean) + ampl_mean = np.round(ampl_mean / thresshold) * gain * thresshold + ampl_mean[:, 1] += offset + return ampl_mean.T + + + +def rainflow_astm(signal): + """Matlab equivalent rainflow counting + + Calculate the amplitude and mean values of half cycles in signal + + This implemementation is based on the c-implementation by Adam Nieslony found at + the MATLAB Central File Exchange http://www.mathworks.com/matlabcentral/fileexchange/3026 + + Parameters + ---------- + Signal : array-like + The raw signal + + Returns + ------- + ampl : array-like + peak to peak amplitudes of the half cycles (note that the matlab implementation + uses peak amplitude instead of peak to peak) + + mean : array-like + Mean values of the half cycles + + + Examples + -------- + >>> signal = np.array([-2.0, 0.0, 1.0, 0.0, -3.0, 0.0, 5.0, 0.0, -1.0, 0.0, 3.0, 0.0, -4.0, 0.0, 4.0, 0.0, -2.0]) + >>> ampl, mean = rainflow_astm(signal) + """ + check_signal(signal) + + # type <double> is reuqired by <find_extreme> and <rainflow> + signal = signal.astype(np.double) + + # Import find extremes and rainflow. + # If possible the module is compiled using cython otherwise the python implementation is used + #cython_import('fatigue_tools.rainflowcount_astm') + from wetb.fatigue_tools.rainflowcounting.rainflowcount_astm import find_extremes, rainflowcount + + # Remove points which is not local minimum/maximum + sig_ext = find_extremes(signal) + + # rainflow count + ampl_mean = np.array(rainflowcount(sig_ext)) + + return np.array(ampl_mean).T \ No newline at end of file diff --git a/wetb/fatigue_tools/rainflowcount_astm.py b/wetb/fatigue_tools/rainflowcounting/rainflowcount_astm.py similarity index 100% rename from wetb/fatigue_tools/rainflowcount_astm.py rename to wetb/fatigue_tools/rainflowcounting/rainflowcount_astm.py diff --git a/wetb/fatigue_tools/rainflowcount_astm.pyd b/wetb/fatigue_tools/rainflowcounting/rainflowcount_astm.pyd similarity index 99% rename from wetb/fatigue_tools/rainflowcount_astm.pyd rename to wetb/fatigue_tools/rainflowcounting/rainflowcount_astm.pyd index 6ef6cd427dfb019332137805ebe4db80d90dcbc8..e985824ec83011c3b893870352cdef19cba7a582 100644 GIT binary patch delta 27 gcmZp8z|`=7X~G8<t7wO?jb9!#gBhD|v}kAp0ItOh4FCWD delta 27 gcmZp8z|`=7X~G8<hPRO+8^1hg1~WF_XwlFH0J#1P>i_@% diff --git a/wetb/fatigue_tools/rainflowcounting/rfc_hist.py b/wetb/fatigue_tools/rainflowcounting/rfc_hist.py new file mode 100644 index 0000000..d4c7fb0 --- /dev/null +++ b/wetb/fatigue_tools/rainflowcounting/rfc_hist.py @@ -0,0 +1,49 @@ +import numpy as np +def rfc_hist(sig_rf, nrbins=46): + """ + Histogram of rainflow counted cycles + ==================================== + + hist, bin_edges, bin_avg = rfc_hist(sig, nrbins=46) + + Divide the rainflow counted cycles of a signal into equally spaced bins. + + Created on Wed Feb 16 16:53:18 2011 + @author: David Verelst + Modified 10.10.2011 by Mads M Pedersen to elimintate __copy__ and __eq__ + + Parameters + ---------- + sig_rf : array-like + As output by rfc_astm or rainflow + + nrbins : int, optional + Divide the rainflow counted amplitudes in a number of equally spaced + bins. + + Returns + ------- + hist : array-like + Counted rainflow cycles per bin, has nrbins elements + + bin_edges : array-like + Edges of the bins, has nrbins+1 elements. + + bin_avg : array-like + Average rainflow cycle amplitude per bin, has nrbins elements. + """ + + rf_half = sig_rf + + # the Matlab approach is to divide into 46 bins + bin_edges = np.linspace(0, 1, num=nrbins + 1) * rf_half.max() + hist = np.histogram(rf_half, bins=bin_edges)[0] + # calculate the average per bin + hist_sum = np.histogram(rf_half, weights=rf_half, bins=bin_edges)[0] + # replace zeros with one, to avoid 0/0 + hist_ = hist.copy() + hist_[(hist == 0).nonzero()] = 1.0 + # since the sum is also 0, the avg remains zero for those whos hist is zero + bin_avg = hist_sum / hist_ + + return hist, bin_edges, bin_avg \ No newline at end of file -- GitLab