Sensitivity Analysis#
In this tutorial we demonstrate how to perform sensitivity analysis as part of the AutoEmulate
workflow. The tutorial covers:
Setting up an example simulation: here we use our “FlowProblem” simulator. This is a cardiovascular modelling example, simulating a blood vessel divided into 10 compartments. This allows for the study of the pressure and flow rate at various points in the tube. See “The Flow Problem” below for more details.
Running the simulation for 100 sets of parameters sampled from the parameter space.
Using Autoemulate to find the best emulator for this simulation
Performing sensitivity analysis.
The Flow Problem
In the field of cardiovascular modeling, capturing the dynamics of blood flow and the associated pressures and volumes within the vascular system is crucial for understanding heart function and disease. This simulator simulates a vessel divided to 10 compartments.
Parameters#
The simulation parameters include :
R (Resistance): Represents the resistance to blood flow in blood vessels, akin to the hydraulic resistance caused by vessel diameter and blood viscosity (Analogous to electrical resistor).
L (Inductance): Represents the inertial effects of blood flow, capturing how blood resists changes in its velocity (Analogous to electrical inductor).
C (Capacitance): Represents the compliance or elasticity of blood vessels, primarily large arteries, which store and release blood volume with changes in pressure (Analogous to a capacitor).
Boundary conditions#
Neumann boundary condition : Specifies the derivative of the variable at the boundary.
Dirichlet Boundary condition : Specifies the value of the variable directly at the boundary.
The setup#
The input flow rate in each compartment is \(Q_i(t)\) for the \(i^{th}\) compartment and the output flow rate is \(Q_{i+1}(t)\).
\(Q_0(t) = \begin{cases} A \cdot \sin^2\left(\frac{\pi}{t_d} t\right), & \text{if } 0 \leq t < T, \\ 0, & \text{otherwise}. \end{cases}\)
Where:
\(Q_0(t)\) is the input pulse function (flow rate) at time t
A is the amplitude of the pulse
\(t_d\) is the pulse duration.
Solve#
Pressure in Each Compartment (\(P_i\)): This determines how the pressure in each compartment evolves over time, based on the inflow (\(Q_i(t)\)) and the outflow (\(Q_{i+1}(t)\)). where \(i\) is the number of compartment.

\(\frac{dP_i}{dt} = \frac{1}{C_n} \left( Q_i(t) - Q_{i+1}(t) \right)\) where, \(C_n = \frac{C}{n_\text{comp}}\)
Flow rate equation (\(Q_i\)): This governs how the flow in each compartment changes over time, depending on the pressures in the neighboring compartments and the resistance and inertance properties of each compartment.
\(\frac{dQ_i}{dt} = \frac{1}{L_n} \left( P_i - P_{10} - R_n Q_i(t) \right)\), where \(L_n = \frac{L}{n_\text{comp}}, \quad R_n = \frac{R}{n_\text{comp}}\)
from autoemulate.core.compare import AutoEmulate
from autoemulate.core.sensitivity_analysis import SensitivityAnalysis
from autoemulate.simulations.flow_problem import FlowProblem
import warnings
warnings.filterwarnings("ignore")
figsize = (9, 5)
Set up the simulation parameters and ranges:
parameters_range = {
"T": (0.5, 2.0), # Cardiac cycle period (s)
"td": (0.1, 0.5), # Pulse duration (s)
"amp": (100.0, 1000.0), # Amplitude (e.g., pressure or flow rate)
"dt": (0.0001, 0.01), # Time step (s)
"C": (20.0, 60.0), # Compliance (unit varies based on context)
"R": (0.01, 0.1), # Resistance (unit varies based on context)
"L": (0.001, 0.005), # Inductance (unit varies based on context)
"R_o": (0.01, 0.05), # Outflow resistance (unit varies based on context)
"p_o": (5.0, 15.0) # Initial pressure (unit varies based on context)
}
output_names = ["pressure"]
simulator = FlowProblem(
parameters_range=parameters_range,
output_names=output_names,
log_level="error"
)
Run the simulation for 100 sets of parameters sampled from the parameter space:
x = simulator.sample_inputs(100)
y = simulator.forward_batch(x)
print(x.shape, y.shape)
torch.Size([100, 9]) torch.Size([100, 1])
Use AutoEmulate to find the best emulator for this simulation:
ae = AutoEmulate(x, y, models=["MLP", "GP"], log_level="error") # remove models argument to use all models
best = ae.best_result()
print(best.model_name)
GaussianProcess
Sensitivity Analysis#
Define the problem by creating a dictionary which contains the names and the boundaries of the parameters
Evaluate the contribution of each parameter via the Sobol and Morris methods.
problem = {
'num_vars': simulator.in_dim,
'names': simulator.param_names,
'bounds': simulator.param_bounds,
'output_names': simulator.output_names,
}
sa = SensitivityAnalysis(best.model, problem=problem)
Sobol metrics:
\(S_1\): First-order sensitivity index.
\(S_2\): Second-order sensitivity index.
\(S_t\): Total sensitivity index.
Sobol interpretation:
\(S_1\) values sum to ≤ 1.0 (exact fraction of variance explained)
\(S_t - S_1\) = interaction effects involving that parameter
Large \(S_t - S_1\) gap indicates strong interactions
sobol_df = sa.run("sobol")
sobol_df
output | parameter | index | value | confidence | |
---|---|---|---|---|---|
0 | pressure | T | S1 | 0.000219 | 0.000026 |
1 | pressure | td | S1 | 0.026737 | 0.004087 |
2 | pressure | amp | S1 | 0.932001 | 0.065842 |
3 | pressure | dt | S1 | 0.000039 | 0.000005 |
4 | pressure | C | S1 | 0.011443 | 0.001409 |
5 | pressure | R | S1 | 0.042863 | 0.004401 |
6 | pressure | L | S1 | 0.001839 | 0.000245 |
7 | pressure | R_o | S1 | 0.000009 | 0.000001 |
8 | pressure | p_o | S1 | 0.000027 | 0.000003 |
0 | pressure | T | ST | 0.000257 | 0.001272 |
1 | pressure | td | ST | 0.019788 | 0.013475 |
2 | pressure | amp | ST | 0.920282 | 0.067542 |
3 | pressure | dt | ST | 0.000035 | 0.000581 |
4 | pressure | C | ST | 0.009463 | 0.010082 |
5 | pressure | R | ST | 0.035305 | 0.019144 |
6 | pressure | L | ST | 0.001074 | 0.003844 |
7 | pressure | R_o | ST | 0.000013 | 0.000273 |
8 | pressure | p_o | ST | -0.000263 | 0.000440 |
0 | pressure | (T, td) | S2 | -0.000149 | 0.001793 |
1 | pressure | (T, amp) | S2 | -0.000122 | 0.002438 |
2 | pressure | (T, dt) | S2 | -0.000154 | 0.001767 |
3 | pressure | (T, C) | S2 | -0.000177 | 0.001749 |
4 | pressure | (T, R) | S2 | -0.000052 | 0.001803 |
5 | pressure | (T, L) | S2 | -0.000171 | 0.001766 |
6 | pressure | (T, R_o) | S2 | -0.000154 | 0.001767 |
7 | pressure | (T, p_o) | S2 | -0.000154 | 0.001767 |
8 | pressure | (td, amp) | S2 | 0.003803 | 0.024147 |
9 | pressure | (td, dt) | S2 | 0.000314 | 0.020327 |
10 | pressure | (td, C) | S2 | 0.001483 | 0.019977 |
11 | pressure | (td, R) | S2 | 0.000407 | 0.020004 |
12 | pressure | (td, L) | S2 | 0.000549 | 0.020458 |
13 | pressure | (td, R_o) | S2 | 0.000289 | 0.020343 |
14 | pressure | (td, p_o) | S2 | 0.000333 | 0.020314 |
15 | pressure | (amp, dt) | S2 | -0.001597 | 0.072261 |
16 | pressure | (amp, C) | S2 | -0.000690 | 0.074542 |
17 | pressure | (amp, R) | S2 | 0.004835 | 0.078065 |
18 | pressure | (amp, L) | S2 | -0.000931 | 0.071613 |
19 | pressure | (amp, R_o) | S2 | -0.001571 | 0.072169 |
20 | pressure | (amp, p_o) | S2 | -0.001324 | 0.072091 |
21 | pressure | (dt, C) | S2 | -0.000066 | 0.000861 |
22 | pressure | (dt, R) | S2 | -0.000041 | 0.000876 |
23 | pressure | (dt, L) | S2 | -0.000071 | 0.000896 |
24 | pressure | (dt, R_o) | S2 | -0.000068 | 0.000891 |
25 | pressure | (dt, p_o) | S2 | -0.000070 | 0.000890 |
26 | pressure | (C, R) | S2 | -0.000391 | 0.014164 |
27 | pressure | (C, L) | S2 | -0.001165 | 0.014616 |
28 | pressure | (C, R_o) | S2 | -0.001171 | 0.014697 |
29 | pressure | (C, p_o) | S2 | -0.001176 | 0.014693 |
30 | pressure | (R, L) | S2 | -0.000452 | 0.029515 |
31 | pressure | (R, R_o) | S2 | -0.000426 | 0.029544 |
32 | pressure | (R, p_o) | S2 | -0.000429 | 0.029533 |
33 | pressure | (L, R_o) | S2 | 0.000397 | 0.005670 |
34 | pressure | (L, p_o) | S2 | 0.000398 | 0.005673 |
35 | pressure | (R_o, p_o) | S2 | 0.000032 | 0.000370 |
sa.plot_sobol(sobol_df, index="ST", figsize=figsize)

Morris Interpretation:
High \(\mu^*\), Low \(\sigma\): Important parameter with linear/monotonic effects
High \(\mu^*\), High \(\sigma\): Important parameter with non-linear effects or interactions
Low \(\mu^*\), High \(\sigma\): Parameter involved in interactions but not individually important
Low \(\mu^*\), Low \(\sigma\): Unimportant parameter
morris_df = sa.run("morris")
morris_df
output | parameter | mu | mu_star | sigma | mu_star_conf | |
---|---|---|---|---|---|---|
0 | pressure | T | -11.086156 | 11.086156 | 4.535842 | 0.271645 |
1 | pressure | td | 90.665741 | 92.981903 | 83.706955 | 4.435920 |
2 | pressure | amp | 693.918091 | 693.918091 | 93.965645 | 5.443970 |
3 | pressure | dt | -4.862327 | 4.870245 | 2.174578 | 0.124750 |
4 | pressure | C | -73.903687 | 73.903687 | 38.804546 | 2.478522 |
5 | pressure | R | -139.173538 | 139.173538 | 73.651794 | 4.480520 |
6 | pressure | L | 21.870932 | 22.049536 | 16.503216 | 0.936327 |
7 | pressure | R_o | -0.930185 | 1.841697 | 1.938184 | 0.065188 |
8 | pressure | p_o | 3.279172 | 3.280887 | 1.908300 | 0.117685 |
sa.plot_morris(morris_df, figsize=figsize)
