def _q_calc(fatigue_life, h, v0, sn, method='brentq'): """ Calculate Weibull scale parameter (q) that gives specified fatigue life using closed form expression in DNV-RP-C03 (2016) eq. F.12-1. Parameters ---------- fatigue_life: float Fatigue life [years]. h: float Weibull shape parameter (in 2-parameter distribution). v0: float, Cycle rate [1/s]. sn: dict or SNCurve Dictionary with S-N curve parameters, alternatively an SNCurve instance. If dict, expected attributes are: 'm1', 'm2', 'a1' (or 'loga1'), 'nswitch'. method: str, optional Which root finding function to use. 'brentq': scipy.optimize.brentq, 'brenth': scipy.optimize.brenth Returns ------- float Corresponding Weibull scale parameter. Notes ----- If thickness correction was taken into account when establishing fatigue life, this is implicitly included in the scale parameter calculated. To obtain the scale parameter excl. thickness correction: >>> q_ = q_calc(fatigue_life, h, v0, sn) >>> q = q_ / (t / t_ref)**k where `t` is the thickness, `t_ref` is the reference thickness, and `k` is the thickness exponent. Keep in mind that ``t = t_ref`` if ``t < t_ref``. See Also -------- q_calc_single_slope """ rootfuncs = { 'brentq': brentq, 'brenth': brenth, } if method not in rootfuncs: raise ValueError("method must be either of: %s" % ', '.join(["'%s'" % k for k in rootfuncs.keys()])) if type(sn) not in (dict, OrderedDict, defaultdict, SNCurve): raise ValueError("`sn` must be dict-like or SNCurve instance") if not isinstance(sn, SNCurve): sn = SNCurve("", **sn) # fatigue life in seconds td = fatigue_life * 3600. * 24 * 365 # calculate gamma parameters eps = np.finfo(float).eps # machine epsilon func = rootfuncs[method] q = func(lambda qq: minersum_weibull(qq, h, sn, v0, td) - 1, a=eps, b=1e10) return q
def test_minersum_weibull_scf(self): """ Test that SCF is correctly accounted for when fatigue damage is calculated from Weibull stress range distribution. """ sn = self.sn_studless scf = 1.15 life = 100. dyear_scf = (1 / life) * scf ** sn.m # only correct for linear (single slope) S-N curves life_scf = life / scf ** sn.m v0 = 0.1 # mean stress cycle frequency h = 1.0 q = _q_calc_single_slope(life, h, v0, sn) self.assertAlmostEqual(minersum_weibull(q, h, sn, v0, td=31536000, scf=scf), dyear_scf, places=6, msg="SCF not correctly accounting for by minersum_weibull()")
def test_minersum_weibull_singleslope(self): """ Test that correct fatigue Miner sum is calculated from Weibull stress range distribution. The test is performed as follows, for three different values of Weibull shape parameter: 1. For each shape parameter; calculate scale parameter (q) of the equivalent Weibull distribution (i.e. Weib. dist. that gives specified fatigue life) 2. Calculate fatigue damage (for one year) using specified shape parameter and calculated scale parameter. 3. Compare calculated fatigue damage to fatigue life (damage) specified initially. """ sn = self.sn_studless life = 100. dyear = 1 / life v0 = 0.1 # mean stress cycle frequency for h in (0.8, 1.0, 1.1): q = _q_calc_single_slope(life, h, v0, sn) self.assertAlmostEqual(minersum_weibull(q, h, sn, v0, td=31536000), dyear, places=6, msg=f"Wrong fatigue life from minersum_weibull() for linear S-N curve and shape={h}")