Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
2a97d9f
Introduce draft of NotchApproxBinner
johannes-mueller Dec 11, 2024
7ccf6ca
Adjust Rainflow HCM to NotchApproxBinner
johannes-mueller Dec 11, 2024
026cf35
Apply NotchApproxBinner to Seeger Beste
johannes-mueller Dec 12, 2024
ad4131b
Adjust other modules to NotchApproxBinner
johannes-mueller Dec 12, 2024
eabdbe0
Adjust HCM rainflow counting unit tests to NotchApproxBinner
johannes-mueller Dec 12, 2024
a5e22bb
Add docstrings to NotchApproxBinner
johannes-mueller Dec 13, 2024
4c648a0
Drop Binned class for notch approximation and adjust tests accordingly
johannes-mueller Dec 13, 2024
7eb2d95
Drop assertions that checked for the LUTs of former Binned
johannes-mueller Dec 13, 2024
2c8ee98
Add tests for secondary branch notch approximation
johannes-mueller Dec 13, 2024
737102f
Drop `load` argument of notch approximation strain methods
johannes-mueller Dec 13, 2024
dd6c18f
Let FKMNonLinearDetector initialize NotchApproxBinner
johannes-mueller Dec 13, 2024
bd04e54
Use automatic binning of FKMNonLinearDetector
johannes-mueller Dec 13, 2024
fc08bc0
Adjust jupyter notebook
johannes-mueller Dec 16, 2024
8983ff3
Don't regenerate the test signal while collecting benchmark tests
johannes-mueller Dec 16, 2024
8bd8763
Add docstrings for .primary() and .secondary()
johannes-mueller Dec 16, 2024
bd97d96
Add abstract base class methods to NotchApproximationLawBase
johannes-mueller Dec 17, 2024
fff5b23
Drop obsolete file
johannes-mueller Dec 17, 2024
a7105fd
Simplifications
johannes-mueller Jan 13, 2025
02dadf3
Handle an edge case in recording epslilon LF on open hysteresis
johannes-mueller Jan 16, 2025
0e7c15e
Cleanups in tests
johannes-mueller Jan 29, 2025
1867584
Improvements from review and some refactorings
johannes-mueller Feb 5, 2025
435217f
Drop maximum_absolute_load parameter for FKM NK rainflow counter
johannes-mueller Feb 6, 2025
b8e7006
Handle case when first mesh load point is zero
johannes-mueller Feb 6, 2025
b136190
Add a couple of docstrings to abstract methods
johannes-mueller Feb 6, 2025
a3d7452
Restore notch approximation law object in result output
johannes-mueller Feb 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 6 additions & 10 deletions src/pylife/stress/rainflow/fkm_nonlinear.py
Original file line number Diff line number Diff line change
Expand Up @@ -318,21 +318,15 @@ def _update_residuals(self, record_vals, turning_point, load_turning_points_rep)

def _collect_record(self, load_turning_points, num_turning_points, record):
def primary(_prev, load):
sigma = self._notch_approximation_law.stress(load)
epsilon = self._notch_approximation_law.strain(sigma, load)
return np.array([load, sigma, epsilon])
return self._notch_approximation_law.primary(load)

def secondary(prev, load):
prev_load = prev[LOAD]

delta_L = load - prev_load
delta_sigma = self._notch_approximation_law.stress_secondary_branch(delta_L)
delta_epsilon = self._notch_approximation_law.strain_secondary_branch(delta_sigma, delta_L)
delta = self._notch_approximation_law.secondary(delta_L)

sigma = prev[STRESS] + delta_sigma
epsilon = prev[STRAIN] + delta_epsilon

return np.array([load, sigma, epsilon])
return prev[STRESS:STRAIN+1].T + delta

def determine_prev_record(prev_idx):
if prev_idx < 0:
Expand Down Expand Up @@ -372,7 +366,9 @@ def determine_prev_record(prev_idx):
return record_vals.set_index("turning_point", drop=True, append=True)

def _process_deformation(self, deformation_func, result_buf, load, prev_record):
result_buf[:3] = deformation_func(prev_record, load)
result_buf[0] = load
res = deformation_func(prev_record, load).T
result_buf[1:3] = res

old_load = self._last_record[LOAD, 0]

Expand Down
137 changes: 69 additions & 68 deletions tests/stress/rainflow/test_fkm_nonlinear.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@

from pylife.stress.rainflow.fkm_nonlinear import FKMNonlinearDetector
import pylife.stress.rainflow.recorders as RFR
import pylife.materiallaws.notch_approximation_law
import pylife.materiallaws.notch_approximation_law_seegerbeste
import pylife.materiallaws.notch_approximation_law as NAL
from pylife.materiallaws.notch_approximation_law_seegerbeste import SeegerBeste


@pytest.fixture(autouse=True)
Expand Down Expand Up @@ -54,12 +54,13 @@ def setUp(self):
K_p = 3.5 # [-] (de: Traglastformzahl) K_p = F_plastic / F_yield (3.1.1)

# initialize notch approximation law
extended_neuber = pylife.materiallaws.notch_approximation_law.ExtendedNeuber(E, K, n, K_p)
extended_neuber = NAL.ExtendedNeuber(E, K, n, K_p)

# wrap the notch approximation law by a binning class, which precomputes the values
maximum_absolute_load = max(abs(signal))
extended_neuber_binned = pylife.materiallaws.notch_approximation_law.Binned(
extended_neuber, maximum_absolute_load, 100)
extended_neuber_binned = NAL.NotchApproxBinner(extended_neuber).initialize(
maximum_absolute_load
)

# first run
detector = FKMNonlinearDetector(recorder=self._recorder, notch_approximation_law=extended_neuber_binned)
Expand Down Expand Up @@ -117,12 +118,13 @@ def setUp(self):
K_p = 3.5 # [-] (de: Traglastformzahl) K_p = F_plastic / F_yield (3.1.1)

# initialize notch approximation law
extended_neuber = pylife.materiallaws.notch_approximation_law.ExtendedNeuber(E, K, n, K_p)
extended_neuber = NAL.ExtendedNeuber(E, K, n, K_p)

# wrap the notch approximation law by a binning class, which precomputes the values
maximum_absolute_load = max(abs(signal))
extended_neuber_binned = pylife.materiallaws.notch_approximation_law.Binned(
extended_neuber, maximum_absolute_load, 100)
extended_neuber_binned = NAL.NotchApproxBinner(extended_neuber).initialize(
maximum_absolute_load
)

# first run
detector = FKMNonlinearDetector(recorder=self._recorder, notch_approximation_law=extended_neuber_binned)
Expand Down Expand Up @@ -194,12 +196,13 @@ def setUp(self):
K_p = 3.5 # [-] (de: Traglastformzahl) K_p = F_plastic / F_yield (3.1.1)

# initialize notch approximation law
extended_neuber = pylife.materiallaws.notch_approximation_law.ExtendedNeuber(E, K, n, K_p)
extended_neuber = NAL.ExtendedNeuber(E, K, n, K_p)

# wrap the notch approximation law by a binning class, which precomputes the values
maximum_absolute_load = max(abs(signal))
extended_neuber_binned = pylife.materiallaws.notch_approximation_law.Binned(
extended_neuber, maximum_absolute_load, 100)
extended_neuber_binned = NAL.NotchApproxBinner(extended_neuber).initialize(
maximum_absolute_load
)

# first run
detector = FKMNonlinearDetector(recorder=self._recorder, notch_approximation_law=extended_neuber_binned)
Expand Down Expand Up @@ -269,12 +272,13 @@ def setUp(self):
K_p = 3.5 # [-] (de: Traglastformzahl) K_p = F_plastic / F_yield (3.1.1)

# initialize notch approximation law
extended_neuber = pylife.materiallaws.notch_approximation_law.ExtendedNeuber(E, K, n, K_p)
extended_neuber = NAL.ExtendedNeuber(E, K, n, K_p)

# wrap the notch approximation law by a binning class, which precomputes the values
maximum_absolute_load = max(abs(self.signal))
extended_neuber_binned = pylife.materiallaws.notch_approximation_law.Binned(
extended_neuber, maximum_absolute_load, 100)
extended_neuber_binned = NAL.NotchApproxBinner(extended_neuber).initialize(
maximum_absolute_load
)

# first run
detector = FKMNonlinearDetector(recorder=self._recorder, notch_approximation_law=extended_neuber_binned)
Expand Down Expand Up @@ -338,13 +342,14 @@ def setUp(self):
np.testing.assert_allclose(max(abs(signal)), 1013)

# initialize notch approximation law
extended_neuber = pylife.materiallaws.notch_approximation_law.ExtendedNeuber(E, K, n, K_p)
extended_neuber = NAL.ExtendedNeuber(E, K, n, K_p)

# wrap the notch approximation law by a binning class, which precomputes the values
maximum_absolute_load = max(abs(signal))

extended_neuber_binned = pylife.materiallaws.notch_approximation_law.Binned(
extended_neuber, maximum_absolute_load, 100)
extended_neuber_binned = NAL.NotchApproxBinner(extended_neuber).initialize(
maximum_absolute_load
)

# first run
detector = FKMNonlinearDetector(recorder=self._recorder, notch_approximation_law=extended_neuber_binned)
Expand Down Expand Up @@ -416,12 +421,12 @@ def test_edge_case_value_in_sample_tail_simple_signal(vals, expected_loads_min,
n = 0.187 # [-]
K_p = 3.5 # [-] (de: Traglastformzahl) K_p = F_plastic / F_yield (3.1.1)

extended_neuber = pylife.materiallaws.notch_approximation_law.ExtendedNeuber(E, K, n, K_p)
extended_neuber = NAL.ExtendedNeuber(E, K, n, K_p)

maximum_absolute_load = max(abs(signal))

extended_neuber_binned = pylife.materiallaws.notch_approximation_law.Binned(
extended_neuber, maximum_absolute_load, 100
extended_neuber_binned = NAL.NotchApproxBinner(extended_neuber).initialize(
maximum_absolute_load
)

detector = FKMNonlinearDetector(
Expand Down Expand Up @@ -466,12 +471,12 @@ def test_edge_case_value_in_sample_tail(vals, expected_loads_min, expected_loads
n = 0.187 # [-]
K_p = 3.5 # [-] (de: Traglastformzahl) K_p = F_plastic / F_yield (3.1.1)

extended_neuber = pylife.materiallaws.notch_approximation_law.ExtendedNeuber(E, K, n, K_p)
extended_neuber = NAL.ExtendedNeuber(E, K, n, K_p)

maximum_absolute_load = max(abs(signal))

extended_neuber_binned = pylife.materiallaws.notch_approximation_law.Binned(
extended_neuber, maximum_absolute_load, 100
extended_neuber_binned = NAL.NotchApproxBinner(extended_neuber).initialize(
maximum_absolute_load
)

detector = FKMNonlinearDetector(
Expand Down Expand Up @@ -512,12 +517,14 @@ def test_flush_edge_case_load():
n = 0.07 # [-]
K_p = 3.5 # [-] (de: Traglastformzahl) K_p = F_plastic / F_yield (3.1.1)

extended_neuber = pylife.materiallaws.notch_approximation_law.ExtendedNeuber(E, K, n, K_p)
extended_neuber = NAL.ExtendedNeuber(E, K, n, K_p)

maximum_absolute_load = max(abs(pd.concat([signal_1, signal_2])))
maximum_absolute_load = pd.concat([signal_1, signal_2]).abs().groupby("node_id").max()

extended_neuber_binned = pylife.materiallaws.notch_approximation_law.Binned(
extended_neuber, maximum_absolute_load, 100
print(maximum_absolute_load)

extended_neuber_binned = NAL.NotchApproxBinner(extended_neuber).initialize(
maximum_absolute_load
)

detector = FKMNonlinearDetector(
Expand Down Expand Up @@ -570,11 +577,7 @@ def test_flush_edge_case_load():


def test_flush_edge_case_load_simple_signal():

signal_1 = np.array([0.0, 143.0, -287.0, 143.0, -359.0, 287.0, 0.0, 287.0, -287.0])

mi_2 = pd.MultiIndex.from_product([range(9, 17), range(3)], names=["load_step", "node_id"])

signal_2 = np.array([143.0, -287.0, 143.0, -359.0, 287.0, 0.0, 287.0, -287.0])

E = 206e3 # [MPa] Young's modulus
Expand All @@ -583,12 +586,12 @@ def test_flush_edge_case_load_simple_signal():
n = 0.07 # [-]
K_p = 3.5 # [-] (de: Traglastformzahl) K_p = F_plastic / F_yield (3.1.1)

extended_neuber = pylife.materiallaws.notch_approximation_law.ExtendedNeuber(E, K, n, K_p)
extended_neuber = NAL.ExtendedNeuber(E, K, n, K_p)

maximum_absolute_load = max(abs(np.concatenate([signal_1, signal_2])))

extended_neuber_binned = pylife.materiallaws.notch_approximation_law.Binned(
extended_neuber, maximum_absolute_load, 100
extended_neuber_binned = NAL.NotchApproxBinner(extended_neuber).initialize(
maximum_absolute_load
)

detector = FKMNonlinearDetector(
Expand All @@ -613,9 +616,7 @@ def test_flush_edge_case_load_simple_signal():


def test_flush_edge_case_S_simple_signal():

signal_1 = np.array([0.0, 143.0, -287.0, 143.0, -359.0, 287.0, 0.0, 287.0, -287.0])

signal_2 = np.array([143.0, -287.0, 143.0, -359.0, 287.0, 0.0, 287.0, -287.0])

E = 206e3 # [MPa] Young's modulus
Expand All @@ -624,12 +625,12 @@ def test_flush_edge_case_S_simple_signal():
n = 0.07 # [-]
K_p = 3.5 # [-] (de: Traglastformzahl) K_p = F_plastic / F_yield (3.1.1)

extended_neuber = pylife.materiallaws.notch_approximation_law.ExtendedNeuber(E, K, n, K_p)
extended_neuber = NAL.ExtendedNeuber(E, K, n, K_p)

maximum_absolute_load = max(abs(np.concatenate([signal_1, signal_2])))

extended_neuber_binned = pylife.materiallaws.notch_approximation_law.Binned(
extended_neuber, maximum_absolute_load, 100
extended_neuber_binned = NAL.NotchApproxBinner(extended_neuber).initialize(
maximum_absolute_load
)

detector = FKMNonlinearDetector(
Expand Down Expand Up @@ -672,12 +673,15 @@ def test_flush_edge_case_S():
n = 0.07 # [-]
K_p = 3.5 # [-] (de: Traglastformzahl) K_p = F_plastic / F_yield (3.1.1)

extended_neuber = pylife.materiallaws.notch_approximation_law.ExtendedNeuber(E, K, n, K_p)
extended_neuber = NAL.ExtendedNeuber(E, K, n, K_p)

maximum_absolute_load = pd.concat([signal_1, signal_2]).abs().groupby("node_id").max()

maximum_absolute_load = max(abs(pd.concat([signal_1, signal_2])))
print(maximum_absolute_load)
print(max(abs(pd.concat([signal_1, signal_2]))))

extended_neuber_binned = pylife.materiallaws.notch_approximation_law.Binned(
extended_neuber, maximum_absolute_load, 100
extended_neuber_binned = NAL.NotchApproxBinner(extended_neuber).initialize(
maximum_absolute_load
)

detector = FKMNonlinearDetector(
Expand Down Expand Up @@ -714,11 +718,8 @@ def test_flush_edge_case_S():
S_min = detector.recorder.S_min
S_max = detector.recorder.S_max

pd.testing.assert_series_equal(S_min, expected_S_min, check_index=False)
pd.testing.assert_series_equal(S_max, expected_S_max, check_index=False)

pd.testing.assert_series_equal(S_min, expected_S_min)
pd.testing.assert_series_equal(S_max, expected_S_max)
pd.testing.assert_series_equal(S_min, expected_S_min, rtol=1e-1)
pd.testing.assert_series_equal(S_max, expected_S_max, rtol=1e-1)


@pytest.mark.parametrize('vals, num', [
Expand Down Expand Up @@ -768,15 +769,15 @@ def test_edge_case_value_in_sample_tail_compare_simple(vals, num):
n = 0.187 # [-]
K_p = 3.5 # [-] (de: Traglastformzahl) K_p = F_plastic / F_yield (3.1.1)

extended_neuber = pylife.materiallaws.notch_approximation_law.ExtendedNeuber(E, K, n, K_p)
extended_neuber = NAL.ExtendedNeuber(E, K, n, K_p)

maximum_absolute_load_simple = max(abs(vals))
maximum_absolute_load_multiple = signal.abs().groupby('node_id').max()


print("single")
extended_neuber_binned_simple = pylife.materiallaws.notch_approximation_law.Binned(
extended_neuber, maximum_absolute_load_simple, 100
extended_neuber_binned_simple = NAL.NotchApproxBinner(extended_neuber).initialize(
maximum_absolute_load_simple
)
detector_simple = FKMNonlinearDetector(
recorder=RFR.FKMNonlinearRecorder(),
Expand All @@ -785,8 +786,8 @@ def test_edge_case_value_in_sample_tail_compare_simple(vals, num):
detector_simple.process(vals).process(vals)

print("multiple")
extended_neuber_binned_multiple = pylife.materiallaws.notch_approximation_law.Binned(
extended_neuber, maximum_absolute_load_multiple, 100
extended_neuber_binned_multiple = NAL.NotchApproxBinner(extended_neuber).initialize(
maximum_absolute_load_multiple
)
detector_multiindex = FKMNonlinearDetector(
recorder=RFR.FKMNonlinearRecorder(),
Expand Down Expand Up @@ -852,16 +853,16 @@ def test_hcm_first_second(vals, num):
n = 0.187 # [-]
K_p = 3.5 # [-] (de: Traglastformzahl) K_p = F_plastic / F_yield (3.1.1)

extended_neuber = pylife.materiallaws.notch_approximation_law.ExtendedNeuber(E, K, n, K_p)
extended_neuber = NAL.ExtendedNeuber(E, K, n, K_p)

maximum_absolute_load_simple = max(abs(vals))
maximum_absolute_load_multiple = signal.abs().groupby('node_id').max()

extended_neuber_binned_simple = pylife.materiallaws.notch_approximation_law.Binned(
extended_neuber, maximum_absolute_load_simple, 100
extended_neuber_binned_simple = NAL.NotchApproxBinner(extended_neuber).initialize(
maximum_absolute_load_simple
)
extended_neuber_binned_multiple = pylife.materiallaws.notch_approximation_law.Binned(
extended_neuber, maximum_absolute_load_multiple, 100
extended_neuber_binned_multiple = NAL.NotchApproxBinner(extended_neuber).initialize(
maximum_absolute_load_multiple
)
detector_simple = FKMNonlinearDetector(
recorder=RFR.FKMNonlinearRecorder(),
Expand Down Expand Up @@ -897,10 +898,8 @@ def detector_seeger_beste():
n = 0.187 # [-]
K_p = 3.5 # [-] (de: Traglastformzahl) K_p = F_plastic / F_yield (3.1.1)

seeger_beste = pylife.materiallaws.notch_approximation_law_seegerbeste.SeegerBeste(E, K, n, K_p)
seeger_beste_binned = pylife.materiallaws.notch_approximation_law.Binned(
seeger_beste, 800, 100
)
seeger_beste = SeegerBeste(E, K, n, K_p)
seeger_beste_binned = NAL.NotchApproxBinner(seeger_beste).initialize(800)

return FKMNonlinearDetector(
recorder=RFR.FKMNonlinearRecorder(), notch_approximation_law=seeger_beste_binned
Expand Down Expand Up @@ -1097,12 +1096,13 @@ def test_history_guideline_at_once():
K_p = 3.5 # [-] (de: Traglastformzahl) K_p = F_plastic / F_yield (3.1.1)

# initialize notch approximation law
extended_neuber = pylife.materiallaws.notch_approximation_law.ExtendedNeuber(E, K, n, K_p)
extended_neuber = NAL.ExtendedNeuber(E, K, n, K_p)

# wrap the notch approximation law by a binning class, which precomputes the values
maximum_absolute_load = max(abs(signal))
extended_neuber_binned = pylife.materiallaws.notch_approximation_law.Binned(
extended_neuber, maximum_absolute_load, 100)
extended_neuber_binned = NAL.NotchApproxBinner(extended_neuber).initialize(
maximum_absolute_load
)

# first run
detector = FKMNonlinearDetector(recorder=recorder, notch_approximation_law=extended_neuber_binned)
Expand Down Expand Up @@ -1144,12 +1144,13 @@ def test_history_guideline_at_split(split_point):
K_p = 3.5 # [-] (de: Traglastformzahl) K_p = F_plastic / F_yield (3.1.1)

# initialize notch approximation law
extended_neuber = pylife.materiallaws.notch_approximation_law.ExtendedNeuber(E, K, n, K_p)
extended_neuber = NAL.ExtendedNeuber(E, K, n, K_p)

# wrap the notch approximation law by a binning class, which precomputes the values
maximum_absolute_load = max(abs(signal))
extended_neuber_binned = pylife.materiallaws.notch_approximation_law.Binned(
extended_neuber, maximum_absolute_load, 100)
extended_neuber_binned = NAL.NotchApproxBinner(extended_neuber).initialize(
maximum_absolute_load
)

# first run
detector = FKMNonlinearDetector(recorder=recorder, notch_approximation_law=extended_neuber_binned)
Expand Down