Load Calibration for Non Linear Limit State Functions (LSFs)#

This tutorial demonstrates how Pystra can calibrate safety and combination factors for Non-Linear limit state functions.

Import Libraries#

[1]:
import pystra as ra
import numpy as np

Define the limit state function for calibration#

The LSF to be supplied in LoadCombination can be any valid Pystra LSF with one additional argument: a scalar design parameter for a random variable (generally, the resistance). Note that: 1. The design parameter must be specified as a Pystra constant with any arbitrary default value, such as 1.0. 2. Only a single design parameter for the resistance random variable can be calibrated at a time.

Keeping with the LoadCombination example, the design parameter for resistance, denoted \(z\), is added to the LSF. The LSF contains additional random variables wR and wS to account for the uncertainties within the resistance and the load effects, respectively. The non-linearity of the LSF is evident as the degree of random variables in each term of the LSF is two.

[2]:
def lsf(z, wR, wS, R, Q1, Q2):
    gX = z * wR * R - wS * (Q1 + Q2)
    return gX

Define the Load and Resistance distributions#

Next follow the creation of load combination object, as explained for the LoadCombination class.

[3]:
wR = ra.Lognormal("wR", 1.0, 0.05)
wS = ra.Lognormal("wS", 1.0, 0.10)
R = ra.Normal("R", 60, 6)  # [units]
Q1_max = ra.Normal("Q1", 30, 3)  # [units]
Q2_max = ra.Normal("Q2", 20, 2)  # [units]
Q1_pit = ra.Normal("Q1", 15, 3)  # [units]
Q2_pit = ra.Normal("Q2", 10, 2)  # [units]

z = ra.Constant("z", 1)

Set up Load Combinations#

Specify nominal values & combinations#

[4]:
rvs_all = ['wR', 'wS', 'R', 'Q1', 'Q2']
dict_nom = dict(zip(rvs_all, np.array([1.0, 1.0, R.ppf(0.05),
                                             Q1_max.ppf(0.95),
                                             Q2_max.ppf(0.95)])))

Q_dict = {'Q1': {'max': Q1_max, 'pit': Q1_pit},
      'Q2': {'max': Q2_max, 'pit': Q2_pit}}
[5]:
loadcombinations = {'Q1_max':['Q1'], 'Q2_max':['Q2']}

Instantiate LoadCombination object#

[6]:
lc = ra.LoadCombination(lsf, dict_dist_comb=Q_dict, list_dist_other=[wS],
                         list_dist_resist=[R, wR], list_const = [z],
                         dict_comb_cases=loadcombinations)

Note that since wR is associated with the resistance, it is specified in list_dist_resist; while wS is specified in list_dist_other as it is associated with the load effects but it’s not a load combination variable.

Load calibration#

[7]:
betaT = 3.7
calib1 = ra.Calibration(lc, target_beta=betaT, dict_nom_vals=
                                dict_nom, calib_var='z',
                                est_method="matrix", calib_method="optimize")
calib1.run()

calib1.print_detailed_output()


======================================================
X* =
             R    wR   wS     Q1     Q2     z
Q1_max  44.40  0.95  1.2  33.81  11.69  1.30
Q2_max  44.76  0.95  1.2  19.16  21.85  1.16

phi =
            R    wR
Q1_max  0.89  0.95
Q2_max  0.89  0.95

gamma =
          wS    Q1    Q2
Q1_max  1.2  0.97  0.94
Q2_max  1.2  0.97  0.94

psi =
          wS    Q1    Q2
Q1_max  1.0  1.00  0.54
Q2_max  1.0  0.57  1.00
======================================================

Design Check#

[8]:
design_z1 = calib1.get_design_param_factor()
design_beta1 = calib1.calc_beta_design_param(np.max(design_z1))
print(f"Design reliabilities = {design_beta1.round(2)}")
print(f"Design Check = {design_beta1.round(2)>=betaT}")
Design reliabilities = [3.7  4.28]
Design Check = [ True  True]