# Copyright (c) 2019-2023 - for information on the respective copyright owner
# see the NOTICE file and/or the repository
# https://github.com/boschresearch/pylife
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
__author__ = "Johannes Mueller"
__maintainer__ = __author__
import cython
import numpy as np
from .general import AbstractDetector
[docs]
class FKMDetector(AbstractDetector):
"""Rainflow detector as described in FKM non linear.
The algorithm has been published by Clormann & Seeger 1985 and has
been cited heavily since.
.. jupyter-execute::
from pylife.stress.timesignal import TimeSignalGenerator
import pylife.stress.rainflow as RF
ts = TimeSignalGenerator(10, {
'number': 50,
'amplitude_median': 1.0, 'amplitude_std_dev': 0.5,
'frequency_median': 4, 'frequency_std_dev': 3,
'offset_median': 0, 'offset_std_dev': 0.4}, None, None).query(10000)
rfc = RF.FKMDetector(recorder=RF.LoopValueRecorder())
rfc.process(ts)
rfc.recorder.collective
Alternatively you can ask the recorder for a histogram matrix:
.. jupyter-execute::
rfc.recorder.histogram(bins=16)
Note
----
This detector **does not** report the loop index.
"""
[docs]
def __init__(self, recorder):
"""Instantiate a FKMDetector.
Parameters
----------
recorder : subclass of :class:`.AbstractRecorder`
The recorder that the detector will report to.
"""
super().__init__(recorder)
self._ir = 1
self._residuals = []
self._max_turn = 0.0
[docs]
@cython.locals(
turns=cython.double[:],
iz=cython.int, ir=cython.int,
last0=cython.double, last1=cython.double,
loop_assumed=cython.int,
max_turn=cython.double)
def process(self, samples, flush=False):
"""Process a sample chunk.
Parameters
----------
samples : array_like, shape (N, )
The samples to be processed
Returns
-------
self : FKMDetector
The ``self`` object so that processing can be chained
"""
ir = self._ir
max_turn = self._max_turn
turns_index, turns = self._new_turns(samples, flush)
from_vals = []
to_vals = []
for current in turns:
loop_assumed = True
while loop_assumed:
iz = len(self._residuals)
if iz < ir:
break
loop_assumed = False
if iz > ir:
last0 = self._residuals[-1]
last1 = self._residuals[-2]
if np.abs(current-last0) >= np.abs(last0-last1):
from_vals.append(last1)
to_vals.append(last0)
self._residuals.pop()
self._residuals.pop()
if np.abs(last0) < max_turn and np.abs(last1) < max_turn:
loop_assumed = True
continue
if np.abs(current) > max_turn:
ir += 1
max_turn = max(np.abs(current), max_turn)
self._residuals.append(current)
self._ir = ir
self._max_turn = max_turn
self._recorder.record_values(from_vals, to_vals)
return self