Sensitivity Analysis#
Pystra can compute the sensitivity of the FORM reliability index \(\beta\) with respect to the distribution parameters (mean and standard deviation) of each random variable, and with respect to the correlation coefficients.
Two methods are available, selected via the numerical argument of SensitivityAnalysis.run():
Method |
Call |
Cost |
Correlation sens. |
|---|---|---|---|
Finite difference (FD) |
|
\(2n+1\) FORM runs |
No |
Closed-form (CF) |
|
1 FORM run |
Yes |
The closed-form approach follows Bourinet (2017), differentiating the Nataf transformation chain analytically. The key equation (Eq. 17) decomposes the sensitivity into two terms:
where \(\boldsymbol{\alpha}\) is the FORM direction cosine vector, \(\mathbf{L}_0\) is the Cholesky factor of the modified (Nataf) correlation matrix, and \(\mathbf{z}\) is the correlated standard-normal design point.
The two terms have distinct physical meanings:
First term — captures how the marginal CDF transformation shifts at the design point when a distribution parameter changes. For example, increasing the mean of a variable moves where that variable’s CDF maps into standard-normal space, pulling the design point closer to or further from the origin. This term is present even for independent variables.
Second term — captures how the Nataf correlation structure changes. The modified correlation matrix \(\mathbf{R}_0\) depends (through double integration) on the marginal distributions, so perturbing a marginal parameter alters \(\mathbf{L}_0\) and hence the isoprobabilistic transformation. For uncorrelated normal variables \(\mathbf{R}_0 = \mathbf{I}\) regardless of the marginals, so this term vanishes identically. For correlated non-normal variables it can be the dominant contribution — and it is also the term that makes the finite-difference method numerically unstable, as demonstrated in the examples below.
[1]:
import numpy as np
import pandas as pd
import pystra as pr
CalRel Example (Bourinet 2017, §4.1, Example 1)#
This example, taken from the CalRel software manual (Liu et al., 1989), has three correlated random variables and a nonlinear limit state function:
Variable |
Distribution |
Mean \(\mu\) |
Std. dev. \(\sigma\) |
|---|---|---|---|
\(X_1\) |
Lognormal |
500 |
100 |
\(X_2\) |
Lognormal |
2000 |
400 |
\(X_3\) |
Uniform |
5 |
0.5 |
The correlation matrix is:
The limit state function is:
FORM gives \(\beta = 1.7728\) and \(p_f^{\text{FORM}} = 3.81 \times 10^{-2}\).
[11]:
def lsf_calrel(X1, X2, X3):
return 1 - X2 / (1000 * X3) - (X1 / (200 * X3)) ** 2
ls1 = pr.LimitState(lsf_calrel)
model1 = pr.StochasticModel()
model1.addVariable(pr.Lognormal("X1", 500, 100))
model1.addVariable(pr.Lognormal("X2", 2000, 400))
model1.addVariable(pr.Uniform("X3", 5, 0.5))
R1 = np.array([
[1.0, 0.3, 0.2],
[0.3, 1.0, 0.2],
[0.2, 0.2, 1.0],
])
model1.setCorrelation(pr.CorrelationMatrix(R1))
[12]:
opts1 = pr.AnalysisOptions()
opts1.setPrintOutput(False)
form1 = pr.Form(
stochastic_model=model1,
limit_state=ls1,
analysis_options=opts1,
)
form1.run()
form1.showDetailedOutput()
==========================================================
FORM
==========================================================
Pf 3.8133894361e-02
BetaHL 1.7727641950
Model Evaluations 154
----------------------------------------------------------
Variable U_star X_star alpha
X1 1.281626 631.952172 +0.723158
X2 0.481529 2320.035440 +0.271741
X3 -1.126170 4.525984 -0.634980
==========================================================
Closed-form sensitivities#
[13]:
sa1 = pr.SensitivityAnalysis(
stochastic_model=model1,
limit_state=ls1,
analysis_options=opts1,
)
cf1 = sa1.run(numerical=False)
[14]:
# Reference values from Bourinet (2017), Table 2
ref1 = {
"X1": {"mean": -0.0059, "std": -0.0079},
"X2": {"mean": -0.0009, "std": -0.0006},
"X3": {"mean": 1.2602, "std": -1.1942},
}
rows = []
for var in ["X1", "X2", "X3"]:
for p in ["mean", "std"]:
sym = "\u03bc" if p == "mean" else "\u03c3"
rows.append({
"Parameter": f"\u2202\u03b2/\u2202{sym}_{var}",
"Reference": ref1[var][p],
"CF": cf1["marginal"][var][p],
})
pd.DataFrame(rows).set_index("Parameter").style.format("{:+.4f}")
[14]:
| Reference | CF | |
|---|---|---|
| Parameter | ||
| ∂β/∂μ_X1 | -0.0059 | -0.0059 |
| ∂β/∂σ_X1 | -0.0079 | -0.0079 |
| ∂β/∂μ_X2 | -0.0009 | -0.0009 |
| ∂β/∂σ_X2 | -0.0006 | -0.0006 |
| ∂β/∂μ_X3 | +1.2602 | +1.2603 |
| ∂β/∂σ_X3 | -1.1942 | -1.1948 |
[15]:
# Reference values from Bourinet (2017), Table 3
ref_corr = {
(1, 0): -0.5151,
(2, 0): 0.8916,
(2, 1): 0.4688,
}
names1 = ["X1", "X2", "X3"]
rows = []
for (i, j), r in ref_corr.items():
rows.append({
"Parameter": f"\u2202\u03b2/\u2202\u03c1({names1[i]},{names1[j]})",
"Reference": r,
"CF": cf1["correlation"][i, j],
})
pd.DataFrame(rows).set_index("Parameter").style.format("{:+.4f}")
[15]:
| Reference | CF | |
|---|---|---|
| Parameter | ||
| ∂β/∂ρ(X2,X1) | -0.5151 | -0.5150 |
| ∂β/∂ρ(X3,X1) | +0.8916 | +0.8914 |
| ∂β/∂ρ(X3,X2) | +0.4688 | +0.4687 |
This example includes a Uniform distribution (\(X_3\)), for which dF_dtheta is evaluated numerically via the base-class finite-difference fallback. The closed-form method handles this seamlessly — no analytical CDF derivative is needed for the Uniform distribution.
Summary#
Finite difference |
Closed-form |
|
|---|---|---|
FORM runs |
\(2n + 1\) |
1 |
Marginal sens. |
Yes |
Yes |
Correlation sens. |
No |
Yes |
Accuracy |
Depends on step size \(\delta\) |
Exact (up to quadrature) |
Stability |
Can be noisy for correlated non-normal variables |
Stable |
Distribution support |
Any |
Any (numerical |
Recommendation: Use the closed-form method (numerical=False) when accuracy and correlation sensitivities are needed. The FD method remains useful as an independent verification tool.