Skip to content

Commit

Permalink
Merge pull request #96 from boschresearch/80-slope-estimator
Browse files Browse the repository at this point in the history
Fix evaluation of slope k and scatter TN by evaluating only fractures…
  • Loading branch information
johannes-mueller authored Jul 4, 2024
2 parents 3548a99 + fa98022 commit b1bc710
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 29 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ In this file noteworthy changes of new releases of pyLife are documented since

## pylife-2.1.1

### Breaking changes

* Change fracture load levels used for slope `k_1` and scatter `T_N` estimation. Now only fractures in the `finite_zone` are used for estimation and not all fractures (in `finite_zone` and `infinite_zone`). Change based on findings in DIN50100:2022-12.


### New features

* Add option to manually set `fatigue_limit` for Woehler curve estimation. (#73)
Expand Down
7 changes: 4 additions & 3 deletions src/pylife/materialdata/woehler/elementary.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ def analyze(self, **kwargs):
"""
if len(self._fd.load.unique()) < 2:
raise ValueError("Need at least two load levels to do a Wöhler analysis.")
self._finite_fractures = self._fd.finite_zone.loc[self._fd.finite_zone.fracture == True]
wc = self._common_analysis()
wc = self._specific_analysis(wc, **kwargs)
self.__calc_bic(wc)
Expand Down Expand Up @@ -117,8 +118,8 @@ def __calc_bic(self, wc):
self._bic = (-2 * log_likelihood) + (param_num * np.log(self._fd.num_tests))

def _fit_slope(self):
slope, lg_intercept, _, _, _ = stats.linregress(np.log10(self._fd.fractures.load),
np.log10(self._fd.fractures.cycles))
slope, lg_intercept, _, _, _ = stats.linregress(np.log10(self._finite_fractures.load),
np.log10(self._finite_fractures.cycles))

return slope, lg_intercept

Expand All @@ -129,7 +130,7 @@ def _transition_cycles(self, fatigue_limit):
return 10**(self._lg_intercept + self._slope * (np.log10(fatigue_limit)))

def _pearl_chain_method(self):
self._pearl_chain_estimator = PearlChainProbability(self._fd.fractures, self._slope)
self._pearl_chain_estimator = PearlChainProbability(self._finite_fractures, self._slope)

TN = functions.std_to_scattering_range(1./self._pearl_chain_estimator.slope)
TS = TN**(1./-self._slope)
Expand Down
108 changes: 82 additions & 26 deletions tests/materialdata/woehler/test_analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,9 +178,7 @@ def test_woehler_endur_zones(data, fatigue_limit_expected):
])
def test_woehler_endur_zones_conservative(data, fatigue_limit_expected):
fd = woehler.determine_fractures(data, 1e7).fatigue_data
print(fd.fatigue_limit)
fd = fd.conservative_fatigue_limit()
print(fd.fatigue_limit)
assert fd.fatigue_limit == fatigue_limit_expected


Expand All @@ -193,10 +191,10 @@ def test_woehler_endure_zones_no_runouts():
def test_woehler_elementary():
expected = pd.Series({
'SD': 362.5,
'k_1': 7.0,
'ND': 3e5,
'TN': 5.3,
'TS': 1.27,
'k_1': 9.88,
'ND': 417772,
'TN': 6.78,
'TS': 1.21,
'failure_probability': 0.5
}).sort_index()

Expand All @@ -208,10 +206,10 @@ def test_woehler_elementary():
def test_woehler_elementary_initialize_with_determined_fractures():
expected = pd.Series({
'SD': 362.5,
'k_1': 7.0,
'ND': 3e5,
'TN': 5.3,
'TS': 1.27,
'k_1': 9.88,
'ND': 417772,
'TN': 6.78,
'TS': 1.21,
'failure_probability': 0.5
}).sort_index()

Expand All @@ -223,10 +221,10 @@ def test_woehler_elementary_initialize_with_determined_fractures():
def test_woehler_elementary_initialize_with_pandas_dataframe():
expected = pd.Series({
'SD': 362.5,
'k_1': 7.0,
'ND': 3e5,
'TN': 5.3,
'TS': 1.27,
'k_1': 9.88,
'ND': 417772,
'TN': 6.78,
'TS': 1.21,
'failure_probability': 0.5
}).sort_index()

Expand Down Expand Up @@ -255,13 +253,41 @@ def test_woehler_elementary_only_one_load_level():
woehler.Elementary(fd).analyze().sort_index()


def test_woehler_elementary_set_fatigue_limit_low():
expected = pd.Series({
'SD': 350.0,
'k_1': 9.88,
'ND': 590904,
'TN': 6.78,
'TS': 1.21,
'failure_probability': 0.5
}).sort_index()
fd = woehler.determine_fractures(data, 1e7).sort_index().fatigue_data.set_fatigue_limit(fatigue_limit=350.)
wc = woehler.Elementary(fd).analyze().sort_index()
pd.testing.assert_series_equal(wc, expected, rtol=1e-1)


def test_woehler_elementary_set_fatigue_limit_high():
expected = pd.Series({
'SD': 376.,
'k_1': 7.07,
'ND': 191572,
'TN': 4.18,
'TS': 1.22,
'failure_probability': 0.5
}).sort_index()
fd = woehler.determine_fractures(data, 1e7).sort_index().fatigue_data.set_fatigue_limit(fatigue_limit=376.)
wc = woehler.Elementary(fd).analyze().sort_index()
pd.testing.assert_series_equal(wc, expected, rtol=1e-1)


def test_woehler_probit():
expected = pd.Series({
'SD': 335,
'TS': 1.19,
'k_1': 6.94,
'ND': 463000.,
'TN': 5.26,
'SD': 339.3,
'TS': 1.2,
'k_1': 9.88,
'ND': 802898.,
'TN': 6.78,
'failure_probability': 0.5
}).sort_index()

Expand All @@ -270,6 +296,21 @@ def test_woehler_probit():
pd.testing.assert_series_equal(wc, expected, rtol=1e-1)


def test_woehler_probit_set_fatigue_limit():
expected = pd.Series({
'SD': 340.4,
'TS': 1.21,
'k_1': 7.1,
'ND': 386721.,
'TN': 4.18,
'failure_probability': 0.5
}).sort_index()

fd = woehler.determine_fractures(data, 1e7).sort_index().fatigue_data.set_fatigue_limit(fatigue_limit=376.)
wc = woehler.Probit(fd).analyze().sort_index()
pd.testing.assert_series_equal(wc, expected, rtol=1e-1)


def test_woehler_probit_one_runout_load_level():
fd = woehler.determine_fractures(data_one_runout_load_level, 1e7).fatigue_data
expected = woehler.Elementary(fd).analyze()
Expand All @@ -283,9 +324,9 @@ def test_woehler_probit_data01():
expected = pd.Series({
'SD': 490,
'TS': 1.1,
'k_1': 8.0,
'ND': 530e3,
'TN': 3.0,
'k_1': 5.2,
'ND': 298638.,
'TN': 1.93,
'failure_probability': 0.5
}).sort_index()

Expand Down Expand Up @@ -315,10 +356,10 @@ def test_woehler_probit_no_runouts():
def test_woehler_max_likelihood_inf_limit():
expected = pd.Series({
'SD': 335,
'TS': 1.19,
'k_1': 6.94,
'ND': 463000.,
'TN': 5.26,
'TS': 1.20,
'k_1': 9.88,
'ND': 897649.,
'TN': 6.78,
'failure_probability': 0.5
}).sort_index()

Expand All @@ -327,6 +368,21 @@ def test_woehler_max_likelihood_inf_limit():
pd.testing.assert_series_equal(wc, expected, rtol=1e-1)


def test_woehler_max_likelihood_inf_limit_set_fatigue_limit():
expected = pd.Series({
'SD': 333.6,
'TS': 1.22,
'k_1': 7.1,
'ND': 446054.,
'TN': 4.18,
'failure_probability': 0.5
}).sort_index()

fd = woehler.determine_fractures(data, 1e7).fatigue_data.set_fatigue_limit(fatigue_limit=376.)
wc = woehler.MaxLikeInf(fd).analyze().sort_index()
pd.testing.assert_series_equal(wc, expected, rtol=1e-1)


def test_bic_without_analysis():
fd = woehler.determine_fractures(data, 1e7).fatigue_data
we = woehler.MaxLikeFull(fd)
Expand Down

0 comments on commit b1bc710

Please sign in to comment.