Skip to content

Commit

Permalink
Fix formatting issues in documentation
Browse files Browse the repository at this point in the history
Signed-off-by: Benjamin Maier <[email protected]>
  • Loading branch information
maierbn committed Nov 22, 2023
1 parent c28e3ba commit b597235
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 67 deletions.
4 changes: 2 additions & 2 deletions demos/fkm_nonlinear/fkm_nonlinear.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"id": "0d2b68d8",
"metadata": {},
"source": [
"# FKM nonlinear assessment for a single point\n",
"This jupyter notebook is the main example how to apply the FKM nonlinear, i.e., the local strain concept using pylife.\n",
"# FKM Nonlinear demo\n",
"This jupyter notebook is the main example how to apply the FKM nonlinear, i.e., the local strain concept using pylife. The fatigue assessment is performed for a single point. Note that assessment for a mesh is also possible with pylife, by providing the respective meshes.\n",
"If you have voilà installed (``pip install voila``), you can also open the notebook by clicking on the voila button in the top bar. This will hide the code blocks and make the plots better visible.\n",
"\n",
"The algorithm follows the document `\"RICHTLINIE NICHTLINEAR / Rechnerischer Festigkeitsnachweis unter expliziter Erfassung nichtlinearen Werkstoffverformungsverhaltens / Für Bauteile aus Stahl, Stahlguss und Aluminiumknetlegierungen / 1.Auflage, 2019\"`. If you want to learn more about how the algorithm works, have a look at the notebook [fkm_nonlinear_full](fkm_nonlinear_full.ipynb)."
Expand Down
6 changes: 3 additions & 3 deletions demos/fkm_nonlinear/fkm_nonlinear_full.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"id": "39ff11e6",
"metadata": {},
"source": [
"# FKM Nonlinear example\n",
"# FKM Nonlinear - full algorithm step-through\n",
"The algorithm follows the document \"RICHTLINIE NICHTLINEAR / Rechnerischer Festigkeitsnachweis unter expliziter Erfassung nichtlinearen Werkstoffverformungsverhaltens / Für Bauteile aus Stahl, Stahlguss und Aluminiumknetlegierungen / 1.Auflage, 2019\"\n",
"\n",
"The used values are according to \"Akademisches Beispiel\", chapter 2.7.1.\n",
Expand Down Expand Up @@ -719,7 +719,7 @@
"id": "e6ac3526",
"metadata": {},
"source": [
"# Assessment with $P_{RAJ}$"
"## Assessment with $P_{RAJ}$"
]
},
{
Expand Down Expand Up @@ -830,7 +830,7 @@
"id": "aaa5d042",
"metadata": {},
"source": [
"# Debug crack opening strain of hystereses"
"## Debug crack opening strain of hystereses"
]
},
{
Expand Down
2 changes: 1 addition & 1 deletion docs/materiallaws/notch_approximation_laws.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
The notch approximation law classes
======================================

The following classes are available: ̀ `ExtendedNeuber``, ``SeegerBeste``, and ``Binned``.
The following classes are available: ``ExtendedNeuber``, ``SeegerBeste``, and ``Binned``.

The ``ExtendedNeuber`` and ``SeegerBeste`` classes implement the respective notch approximations.

Expand Down
64 changes: 38 additions & 26 deletions src/pylife/materiallaws/notch_approximation_law.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ def load(self, stress, *, rtol=1e-4, tol=1e-4):
from the elastic-plastic stress as from the notch approximation.
This backward step is needed for the pfp FKM nonlinear surface layer & roughness.
This method is the inverse operation of "stress", i.e., L = load(stress(L)) and S = stress(load(stress)).
This method is the inverse operation of "stress", i.e., ``L = load(stress(L))`` and ``S = stress(load(stress))``.
Parameters
----------
Expand Down Expand Up @@ -256,7 +256,7 @@ def load_secondary_branch(self, delta_stress, *, rtol=1e-4, tol=1e-4):
from the elastic-plastic stress as from the notch approximation.
This backward step is needed for the pfp FKM nonlinear surface layer & roughness.
This method is the inverse operation of "stress", i.e., L = load(stress(L)) and S = stress(load(stress)).
This method is the inverse operation of "stress", i.e., ``L = load(stress(L))`` and ``S = stress(load(stress))``.
Parameters
----------
Expand Down Expand Up @@ -295,7 +295,7 @@ def _e_star(self, load):
"""Compute the plastic corrected strain term e^{\ast} from the Neuber approximation
(eq. 2.5-43 in FKM nonlinear)
e_star = L/K_p / E + (L/K_p / K')^(1/n')
``e_star = L/K_p / E + (L/K_p / K')^(1/n')``
"""

corrected_load = load / self._K_p
Expand All @@ -304,10 +304,12 @@ def _e_star(self, load):
def _d_e_star(self, load):
"""Compute the first derivative of self._e_star(load)
e_star = L/K_p / E + (L/K_p / K')^(1/n')
.. code::
e_star = L/K_p / E + (L/K_p / K')^(1/n')
de_star(L)/dL = d/dL[ L/K_p / E + (L/K_p / K')^(1/n') ]
= 1/(K_p * E) + tangential_compliance(L/K_p) / K_p
de_star(L)/dL = d/dL[ L/K_p / E + (L/K_p / K')^(1/n') ]
= 1/(K_p * E) + tangential_compliance(L/K_p) / K_p
"""
return 1/(self.K_p * self.E) \
+ self._ramberg_osgood_relation.tangential_compliance(load/self.K_p) / self.K_p
Expand All @@ -316,7 +318,7 @@ def _neuber_strain(self, stress, load):
"""Compute the additional strain term from the Neuber approximation
(2nd summand in eq. 2.5-45 in FKM nonlinear)
(L/sigma * K_p * e_star)
``(L/sigma * K_p * e_star)``
"""

e_star = self._e_star(load)
Expand All @@ -334,15 +336,15 @@ def _stress_implicit(self, stress, load):
"""Compute the implicit function of the stress, f(sigma),
defined in eq.2.5-45 of FKM nonlinear
f(sigma) = sigma/E + (sigma/K')^(1/n') - (L/sigma * K_p * e_star)
``f(sigma) = sigma/E + (sigma/K')^(1/n') - (L/sigma * K_p * e_star)``
"""

return self._ramberg_osgood_relation.strain(stress) - self._neuber_strain(stress, load)

def _d_stress_implicit(self, stress, load):
"""Compute the first derivative of self._stress_implicit
df/dsigma
``df/dsigma``
"""

e_star = self._e_star(load)
Expand All @@ -361,12 +363,14 @@ def _delta_e_star(self, delta_load):
def _d_delta_e_star(self, delta_load):
"""Compute the first derivative of self._delta_e_star(load)
delta_e_star = ΔL/K_p / E + 2*(ΔL/K_p / (2*K'))^(1/n')
= ΔL/K_p / E + 2*(ΔL/(2*K_p) / K')^(1/n')
.. code::
delta_e_star = ΔL/K_p / E + 2*(ΔL/K_p / (2*K'))^(1/n')
= ΔL/K_p / E + 2*(ΔL/(2*K_p) / K')^(1/n')
d_delta_e_star(ΔL)/dΔL = d/dΔL[ ΔL/K_p / E + 2*(ΔL/(2*K_p) / K')^(1/n') ]
= 1/(K_p * E) + 2*tangential_compliance(ΔL/(2*K_p)) / (2*K_p)
= 1/(K_p * E) + tangential_compliance(ΔL/(2*K_p)) / K_p
d_delta_e_star(ΔL)/dΔL = d/dΔL[ ΔL/K_p / E + 2*(ΔL/(2*K_p) / K')^(1/n') ]
= 1/(K_p * E) + 2*tangential_compliance(ΔL/(2*K_p)) / (2*K_p)
= 1/(K_p * E) + tangential_compliance(ΔL/(2*K_p)) / K_p
"""
return 1/(self.K_p * self.E) \
+ self._ramberg_osgood_relation.tangential_compliance(delta_load/(2*self.K_p)) / self.K_p
Expand All @@ -393,9 +397,12 @@ def _stress_secondary_implicit(self, delta_stress, delta_load):
def _d_stress_secondary_implicit(self, delta_stress, delta_load):
"""Compute the first derivative of self._stress_secondary_implicit
Note, the derivative of `self._ramberg_osgood_relation.delta_strain` is:
d/dΔsigma delta_strain(Δsigma) = d/dΔsigma 2*strain(Δsigma/2)
= 2*d/dΔsigma strain(Δsigma/2) = 2 * 1/2 * tangential_compliance(Δsigma/2)
= self._ramberg_osgood_relation.tangential_compliance(delta_stress/2)
.. code::
d/dΔsigma delta_strain(Δsigma) = d/dΔsigma 2*strain(Δsigma/2)
= 2*d/dΔsigma strain(Δsigma/2) = 2 * 1/2 * tangential_compliance(Δsigma/2)
= self._ramberg_osgood_relation.tangential_compliance(delta_stress/2)
"""

delta_e_star = self._delta_e_star(delta_load)
Expand All @@ -411,18 +418,20 @@ def _load_implicit(self, load, stress):
This is needed to apply the notch approximation law "backwards", i.e.,
to get from stress back to load. This is required for the FKM nonlinear roughness & surface layer.
f(L) = sigma/E + (sigma/K')^(1/n') - (L/sigma * K_p * e_star(L))
``f(L) = sigma/E + (sigma/K')^(1/n') - (L/sigma * K_p * e_star(L))``
"""

return self._stress_implicit(stress, load)

def _d_load_implicit(self, load, stress):
"""Compute the first derivative of self._load_implicit
f(L) = sigma/E + (sigma/K')^(1/n') - (L/sigma * K_p * e_star(L))
.. code::
f(L) = sigma/E + (sigma/K')^(1/n') - (L/sigma * K_p * e_star(L))
df/dL = d/dL [ -(L/sigma * K_p * e_star(L))]
= -1/sigma * K_p * e_star(L) - L/sigma * K_p * de_star/dL
df/dL = d/dL [ -(L/sigma * K_p * e_star(L))]
= -1/sigma * K_p * e_star(L) - L/sigma * K_p * de_star/dL
"""

Expand All @@ -436,7 +445,7 @@ def _load_secondary_implicit(self, delta_load, delta_stress):
This is needed to apply the notch approximation law "backwards", i.e.,
to get from stress back to load. This is required for the FKM nonlinear roughness & surface layer.
f(ΔL) = Δsigma/E + 2*(Δsigma/(2*K'))^(1/n') - (ΔL/Δsigma * K_p * Δe_star(ΔL))
``f(ΔL) = Δsigma/E + 2*(Δsigma/(2*K'))^(1/n') - (ΔL/Δsigma * K_p * Δe_star(ΔL))``
"""

Expand All @@ -445,10 +454,12 @@ def _load_secondary_implicit(self, delta_load, delta_stress):
def _d_load_secondary_implicit(self, delta_load, delta_stress):
"""Compute the first derivative of self._load_secondary_implicit
f(ΔL) = Δsigma/E + 2*(Δsigma/(2*K'))^(1/n') - (ΔL/Δsigma * K_p * Δe_star(ΔL))
.. code::
f(ΔL) = Δsigma/E + 2*(Δsigma/(2*K'))^(1/n') - (ΔL/Δsigma * K_p * Δe_star(ΔL))
df/dΔL = d/dΔL [ -(ΔL/Δsigma * K_p * Δe_star(ΔL))]
= -1/Δsigma * K_p * Δe_star(ΔL) - ΔL/Δsigma * K_p * dΔe_star/dΔL
df/dΔL = d/dΔL [ -(ΔL/Δsigma * K_p * Δe_star(ΔL))]
= -1/Δsigma * K_p * Δe_star(ΔL) - ΔL/Δsigma * K_p * dΔe_star/dΔL
"""

Expand All @@ -465,6 +476,7 @@ class Binned:
root finding algorithm for every new load.
There are two variants of the data structure.
* First, for a single assessment point, the lookup-table contains one load,
strain and stress value in every bin.
* Second, for vectorized assessment of multiple nodes at once, the lookup-table
Expand Down Expand Up @@ -897,4 +909,4 @@ def _create_bins_multiple_assessment_points(self):
self._lut_secondary_branch.delta_strain \
= self._notch_approximation_law.strain_secondary_branch(
self._lut_secondary_branch.delta_stress, self._lut_secondary_branch.delta_load)


45 changes: 25 additions & 20 deletions src/pylife/materiallaws/notch_approximation_law_seegerbeste.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ def load(self, stress, *, rtol=1e-4, tol=1e-4):
from the elastic-plastic stress as from the notch approximation.
This backward step is needed for the pfp FKM nonlinear surface layer & roughness.
This method is the inverse operation of "stress", i.e., L = load(stress(L)) and S = stress(load(stress)).
This method is the inverse operation of "stress", i.e., ``L = load(stress(L))`` and ``S = stress(load(stress))``.
Note that this method is only implemented for the scalar case, as the FKM nonlinear surface layer & roughness
also only handles the scalar case with one assessment point at once, not with entire meshes.
Expand Down Expand Up @@ -241,7 +241,7 @@ def load_secondary_branch(self, delta_stress, *, rtol=1e-4, tol=1e-4):
from the elastic-plastic stress as from the notch approximation.
This backward step is needed for the pfp FKM nonlinear surface layer & roughness.
This method is the inverse operation of "stress", i.e., L = load(stress(L)) and S = stress(load(stress)).
This method is the inverse operation of "stress", i.e., ``L = load(stress(L))`` and ``S = stress(load(stress))``.
Note that this method is only implemented for the scalar case, as the FKM nonlinear surface layer & roughness
also only handles the scalar case with one assessment point at once, not with entire meshes.
Expand Down Expand Up @@ -277,7 +277,7 @@ def _e_star(self, load):
"""Compute the plastic corrected strain term e^{\ast} from the Neuber approximation
(eq. 2.5-43 in FKM nonlinear)
e_star = L/K_p / E + (L/K_p / K')^(1/n')
``e_star = L/K_p / E + (L/K_p / K')^(1/n')``
"""

corrected_load = load / self._K_p
Expand All @@ -287,7 +287,7 @@ def _neuber_strain(self, stress, load):
"""Compute the additional strain term from the Neuber approximation
(2nd summand in eq. 2.5-45 in FKM nonlinear)
(L/sigma * K_p * e_star)
``(L/sigma * K_p * e_star)``
"""

e_star = self._e_star(load)
Expand All @@ -305,7 +305,7 @@ def _u_term(self, stress, load):
'''
Compute the "u-term" from equation 2.8.40
(pi/2*(L/Sigma-1/k_p-1))
``(pi/2*(L/Sigma-1/k_p-1))``
'''
if not isinstance(load, float):
Expand All @@ -320,11 +320,14 @@ def _middle_term(self, stress, load):
(2/u^2)*ln(1/cos(u))+(Sigma/L)^2-(Sigma/L)
Note, this is only possible for
1/cos(u) > 0
<=> 0 <= u < pi/2
<=> 0 <= L/Sigma - 1/k_p - 1 < 1
<=> 1 <= L/Sigma - 1/k_p < 2
<=> 1/(L/Sigma - 2) < k_p <= 1/(L/Sigma - 1)
.. code::
1/cos(u) > 0
<=> 0 <= u < pi/2
<=> 0 <= L/Sigma - 1/k_p - 1 < 1
<=> 1 <= L/Sigma - 1/k_p < 2
<=> 1/(L/Sigma - 2) < k_p <= 1/(L/Sigma - 1)
'''
# convert stress value to float
Expand Down Expand Up @@ -372,7 +375,7 @@ def _u_term_secondary(self, delta_stress, delta_load):
'''
Compute the "u-term" from equation 2.8.45 for the secondary branch
(pi/2*(delta_L/delta_Sigma-1/k_p-1))
``(pi/2*(delta_L/delta_Sigma-1/k_p-1))``
'''
if not isinstance(delta_load, float):
Expand All @@ -384,14 +387,16 @@ def _middle_term_secondary(self, delta_stress, delta_load):
'''
Compute the middle term of euqation 2.8.42 for the secondary branch
(2/u^2)*ln(1/cos(u))+(Sigma/L)^2-(Sigma/L)
``(2/u^2)*ln(1/cos(u))+(Sigma/L)^2-(Sigma/L)``
Note, this is only possible for
1/cos(u) > 0
<=> 0 <= u < pi/2
<=> 0 <= delta_L/delta_Sigma - 1/k_p - 1 < 1
<=> 1 <= delta_L/delta_Sigma - 1/k_p < 2
<=> 1/(delta_L/delta_Sigma - 2) < k_p <= 1/(delta_L/delta_Sigma - 1)
.. code::
1/cos(u) > 0
<=> 0 <= u < pi/2
<=> 0 <= delta_L/delta_Sigma - 1/k_p - 1 < 1
<=> 1 <= delta_L/delta_Sigma - 1/k_p < 2
<=> 1/(delta_L/delta_Sigma - 2) < k_p <= 1/(delta_L/delta_Sigma - 1)
'''
if not isinstance(delta_stress, float):
Expand All @@ -407,8 +412,8 @@ def _stress_secondary_implicit(self, delta_stress, delta_load):
"""Compute the implicit function of the stress, f(sigma), defined in eq.2.8-43 of FKM nonlinear.
There are in principal two different approaches:
* find root of f(sigma)-epsilon
* find root of f(sigma)/epsilon - 1
* find root of ``f(sigma)-epsilon``
* find root of ``f(sigma)/epsilon - 1``
The second approach is numerically more stable and is used here.
The code for the first approach would be:
Expand Down Expand Up @@ -497,4 +502,4 @@ def _stress_secondary_fix_not_converged_values(self, delta_stress, delta_load, x
if result[1].converged:
delta_stress[0][index_diverged] = result[0]
return delta_stress


30 changes: 15 additions & 15 deletions src/pylife/strength/fkm_nonlinear/assessment_nonlinear_standard.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,15 @@ def perform_fkm_nonlinear_assessment(assessment_parameters, load_sequence, calcu
The FKM nonlinear guideline defines three possible methods to consider the statistical distribution of the load:
1. a normal distribution with given standard deviation, $s_L$
2. a logarithmic-normal distribution with given standard deviation $LSD_s$
3. an unknown distribution, use the constant factor :math:`\gamma_L=1.1` for $P_L = 2.5\%$
or :math:`\gamma_L=1` for $P_L = 50\%$ or
1. a normal distribution with given standard deviation, :math:`s_L`
2. a logarithmic-normal distribution with given standard deviation :math:`LSD_s`
3. an unknown distribution, use the constant factor :math:`\gamma_L=1.1` for :math:`P_L = 2.5\%`
or :math:`\gamma_L=1` for :math:`P_L = 50\%` or
If the ``assessment_parameters`̀ contain a value for ``s_L``, the first approach is used (normal distribution).
If the ``assessment_parameters`̀` contain a value for ``s_L``, the first approach is used (normal distribution).
Else, if the ``assessment_parameters`̀ contain a value for ``LSD_s``, the second approach is used (log-normal distribution).
Else, if only ``P_L`̀ is given a scaling with the according factor is used. The statistical assessment can be skipped
by settings P_A = 0.5 and P_L = 50.
Else, if only ``P_L``̀ is given a scaling with the according factor is used. The statistical assessment can be skipped
by settings ``P_A = 0.5`` and ``P_L = 50``.
Parameters
----------
Expand Down Expand Up @@ -262,15 +262,15 @@ def _scale_load_sequence_according_to_probability(assessment_parameters, load_se
The FKM nonlinear guideline defines three possible methods to consider the statistical distribution of the load:
1. a normal distribution with given standard deviation, $s_L$
2. a logarithmic-normal distribution with given standard deviation $LSD_s$
3. an unknown distribution, use the constant factor :math:`\gamma_L=1.1` for $P_L = 2.5\%$
or :math:`\gamma_L=1` for $P_L = 50\%$ or
1. a normal distribution with given standard deviation, :math:`s_L`
2. a logarithmic-normal distribution with given standard deviation :math:`LSD_s`
3. an unknown distribution, use the constant factor :math:`\gamma_L=1.1` for :math:`P_L = 2.5\%`
or :math:`\gamma_L=1` for :math:`P_L = 50\%` or
If the ``assessment_parameters`̀ contain a value for ``s_L``, the first approach is used (normal distribution).
Else, if the ``assessment_parameters`̀ contain a value for ``LSD_s``, the second approach is used (log-normal distribution).
If the ``assessment_parameters`̀` contain a value for ``s_L``, the first approach is used (normal distribution).
Else, if the ``assessment_parameters``̀ contain a value for ``LSD_s``, the second approach is used (log-normal distribution).
Else, if only ``P_L`̀ is given a scaling with the according factor is used. The statistical assessment can be skipped
by settings P_A = 0.5 and P_L = 50.
by settings ``P_A = 0.5`` and ``P_L = 50``.
Parameters
----------
Expand Down Expand Up @@ -653,4 +653,4 @@ def _compute_lifetimes_P_RAM(assessment_parameters, result, scaled_load_sequence
result = _compute_lifetimes_for_failure_probabilities_RAM(assessment_parameters, result, damage_calculator)

result = _store_additional_objects_in_result_RAM(result, recorder, damage_calculator, component_woehler_curve_P_RAM, detector, detector_1st)
return result
return result

0 comments on commit b597235

Please sign in to comment.