Source code for pylife.strength.helpers

# Copyright (c) 2019-2021 - 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.

"""Small helper functions for fatigue analysis

"""

__author__ = "Cedric Philip Wagner"
__maintainer__ = "Johannes Mueller"

import numpy as np


[docs]def solidity_haibach(collective, k): """Compute solidity according to Haibach Refer to: Haibach - Betriebsfestigkeit - 3. Auflage (2005) - S.271 Parameters ---------- collective : np.ndarray numpy array of shape (:, 2) where ":" depends on the number of classes defined for the rainflow counting 1. column: class values in ascending order 2. column: accumulated number of cycles first entry is the total number of cycles then in a descending manner till the number of cycles of the highest stress class k : float slope of the S/N curve Returns ------- V : np.ndarray (1,) Völligkeitswert (solidity) """ S = collective[:, 0] # the accumulated number of cycles N_acc = collective[:, 1] # the number of cycles for each class hi = np.zeros_like(N_acc) # get the number of cycles for each class for i in range(len(hi)): if i == (len(hi) - 1): # the last entry is the accumulation of only the last class # so it is already the number of cycles of the highest class hi[i] = N_acc[i] else: hi[i] = N_acc[i] - N_acc[i + 1] # the selection of S is required so that the highest class # with actual counts (hi > 0) is taken as reference for all stress values xi = S / S[hi > 0].max() V = np.sum((hi * (xi**k)) / hi.sum()) return V
[docs]def solidity_fkm(collective, k): """Compute solidity according to the FKM guideline (2012) Refer to: FKM-Richtlinie - 6. Auflage (2012) - S.58 - Gl. (2.4.55) + Gl. (2.4.55) Parameters ---------- collective : np.ndarray numpy array of shape (:, 2) where ":" depends on the number of classes defined for the rainflow counting 1. column: class values in ascending order 2. column: accumulated number of cycles first entry is the total number of cycles then in a descending manner till the number of cycles of the highest stress class k : float slope of the S/N curve Returns ------- V : np.ndarray Völligkeitswert (solidity) """ V_haibach = solidity_haibach(collective, k) V = V_haibach**(1./k) return V
[docs]class StressRelations: """Namespace for simple relations of stress / amplitude / R-ratio Refer to: Haibach (2006), p. 21 """
[docs] @staticmethod def get_max_stress_from_amplitude(amplitude, R): return 2 * amplitude / (1 - R)
[docs] @staticmethod def get_mean_stress_from_amplitude(amplitude, R): return amplitude * (1 + R) / (1 - R)
[docs]def irregularity_factor(rainflow_matrix, residuals=np.empty(0), decision_bin=None): """ Calculate the irregularity factor of a turning point sequence based on a rainflow matrix and its residuals. Two sided irregularity factor: ..math:: I = N_{mean crossings} / N_{turning points} Parameters ---------- rainflow_matrix: np.ndarray[int, int] 2D-rainflow matrix (must be square shaped) residuals: np.ndarray[int], Optional 1D array of residuals to consider for accurate calculation. Consecutive duplicates are removed beforehand. Residuals must be provided as bin numbers. Hint: Transformation from physical to binned values possible via np.digitize. decision_bin: int, Optional Bin number that equals the mean (two-sided). If not provided the decision_bin is inferred by the matrix entries as the mean value based on the turning points and will be broadcasted to int-type. Todo ---- Future version may provide the one-sided irregularity factor as a second option. Formula would be: One sided irregularity factor: .. math:: I = N_{zero bin upwards crossing} / N_{peaks} N_{zero bin upwards crossings} equals positive_mean_bin_crossing if `decision_bin` is set to the bin of physical 0. Inferring exact amount of peaks from rainflow-matrix and residuals is left to be done. """ # Ensure input types assert isinstance(rainflow_matrix, np.ndarray) assert isinstance(residuals, np.ndarray) if rainflow_matrix.shape[0] != rainflow_matrix.shape[1]: raise ValueError("Rainflow matrix must be square shaped in order to calculate the irregularity factor.") # Remove duplicates from residuals diffs = np.diff(residuals) if np.any(diffs == 0.0): # Remove the duplicates duplicates = np.concatenate([diffs == 0, [False]]) residuals = residuals[~duplicates] # Infer decision bin as mean if necessary if decision_bin is None: row_sum = 0 col_sum = 0 total_counts = 0 for i in range(rainflow_matrix.shape[0]): row = rainflow_matrix[i, :].sum() col = rainflow_matrix[:, i].sum() total_counts += row + col row_sum += i * row col_sum += i * col total_counts += residuals.shape[0] res_sum = residuals.sum() decision_bin = int((row_sum + col_sum + res_sum) / total_counts) else: decision_bin = int(decision_bin) # Calculate two sided irregularity factor positive_mean_bin_crossing = rainflow_matrix[0:decision_bin, decision_bin:-1].sum() negative_mean_bin_crossing = rainflow_matrix[decision_bin:-1, 0:decision_bin].sum() total_mean_crossing = 2 * (positive_mean_bin_crossing + negative_mean_bin_crossing) amount_of_turning_points = 2 * rainflow_matrix.sum() amount_of_turning_points += residuals.shape[0] for i in range(residuals.shape[0] - 1): if (residuals[i] - decision_bin) * (residuals[i+1] - decision_bin) < 0: total_mean_crossing += 1 return total_mean_crossing / amount_of_turning_points