---
# System prepended metadata

title: Uncertainty Analysis
tags: [Math360]

---

# Monte Carlo Uncertainty Analysis

Monte Carlo *Uncertainty Analysis* is a technique used to investigate how the inherent uncertainty associated with *measured input parameters* affects the output of a mathematical model.

```python
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm
import scipy.integrate as spi
import scipy.optimize as spo
```


## Overview of Uncertainty

### Sources of Uncertainty
Input parameters in models are often *measured quantities* (e.g., mass, time, distance). Even with a perfectly executed measurement, there is always an *uncertainty* associated with the result, often stemming from the limitations or inaccuracy of the tools used.

For example, a volumetric flask might have a volume measurement of $100 \pm 0.08$ mL, meaning the measurement is truly a range of values.

### The Role of Random Error
When many measurements are taken, the distribution of results often approaches a *normal curve* due to *random errors*. Random errors cause both over- and underestimates of the true value, which tend to cancel out on average. Monte Carlo leverages this natural distribution to model uncertainty.



## Monte Carlo Uncertainty Procedure
The general strategy is to incorporate the measured uncertainty $c$ into the model by simulating it as **random noise**:

1. **Define the Random Variable:** For a measured quantity $x$ with uncertainty $\pm c$, define a random variable $\hat{x} = x + \varepsilon$.
2. **Define the Error $\varepsilon$:** $\varepsilon$ is a *normally distributed* random variable with a *mean of $0$* and a *standard deviation of $c$* (the uncertainty level).
    * Choosing a **zero mean** assumes the system behaves, on average, like the measured value (i.e., no systematic errors).
    * Choosing the **standard deviation** equal to $c$ allows the variation to be correctly injected into the parameter's value.
3. **Simulate and Solve:** Solve the model multiple times using the randomly generated $\hat{x}$ value for the input parameter in each simulation.
4. **Visualize:** Plot the distribution of the final results to assess the output uncertainty.



## Example: Racecar Velocity
The velocity formula $v = d/t$ is calculated using uncertain measurements for distance $d$ and time $t$.

| Quantity | Value | Uncertainty (±c) |
| :--- | :---: | :---: |
| Distance ($d$) | $54 \text{ m}$ | $1 \text{ m}$ |
| Time ($t$) | $2 \text{ s}$ | $0.1 \text{ s}$ |

The calculated velocity without uncertainty is $v = 54/2 = 27 \text{ m/s}$. 

### Monte Carlo Simulation
We generate $N=10,000$ samples for $d$ and $t$ from Normal distributions centered at their measured values, with a standard deviation equal to the uncertainty.

```python=
# Apply monte carlo to see how uncertainty affects the velocity 
d = 54; t = 2
d_err = 1; t_err = 0.1 
N = 10000 

# Generate Monte Carlo samples using N(mean=value, scale=error)
d_mc = d + np.random.normal(loc=0.0, scale=d_err,size=N)
t_mc = t + np.random.normal(loc=0.0, scale=t_err,size=N)

v_mc = d_mc/t_mc

plt.hist(v_mc,bins = 20)
plt.axvline(x = v, color = 'r') #vertical line for exact value
plt.xlabel('velocity')
plt.ylabel('frequency')
plt.show()
```

![image](https://hackmd.io/_uploads/rJ0HgOCRel.png)

#### Analysis 
The resulting distribution of velocities is centered at $27 \text{ m/s}$. The spread of the distribution quantifies the uncertainty in the final velocity calculation: a measured velocity could range from approximately $23 \text{ m/s}$ to $33 \text{ m/s}$.



## Example: Energy Balance Model
We apply Monte Carlo uncertainty analysis to the *Global Energy Balance Model* to determine how uncertainties in measured physical constants affect the calculated temperature solution $T(t)$.


### Model and Uncertain Parameters

#### The Energy Balance Model
The deterministic differential equation for Earth's temperature ($T$) is:

$$
k \frac{dT}{dt} = \pi R^2 Q(1-\alpha(T)) - 4 \pi R^2 \sigma \varepsilon T^4
$$

| Variable | Definition |
| :---: | :--- |
| $k$ | Heat capacity of the Earth (J/K) |
| $Q$ | Solar energy reaching Earth's surface (s$^{-1}$ m$^{-2}$) |
| $R$ | Radius of the Earth (m) |
| $\sigma$ | Stefan-Boltzmann constant (W/(m$^2$K$^4$)) |
| $\alpha(T)$ | Albedo (unitless, dependent on T or constant) |
| $\varepsilon$ | Emissivity (unitless, constant $0.622$) |

#### Uncertain Constant Values
The measured constants are defined with a central value and an associated uncertainty ($\pm c$):

| Constant | Value | Uncertainty (±c) |
| :---: | :---: | :---: |
| $k$ | $1.0 \times 10^{23} \text{ J/K}$ | $\pm 0.22 \times 10^{9} \text{ J/K}$ |
| $R$ | $6.3781 \times 10^6 \text{ m}$ | $\pm 10 \text{ m}$ |
| $Q$ | $1365 \text{ W}/(\text{s}\cdot\text{m}^2)$ | $\pm 27.3 \text{ W}/(\text{s}\cdot\text{m}^2)$ |
| $\sigma$ | $5.6704 \times 10^{-8} \text{ W}/(\text{m}^2\text{K}^4)$ | $\pm 0.000021 \times 10^{-8} \text{ W}/(\text{m}^2\text{K}^4)$ |

#### Albedo Functions
The albedo can be modeled as constant 

$$
\alpha_1(T) = 0.3
$$

or temperature-dependent 

$$
\alpha_2(T)=
\begin{cases} 
0.7, & T \leq 247 \text{ K} \\ 
3.52296-0.011429T, & 247 < T < 282 \text{ K} \\ 
0.3, & T \geq 282 \text{ K} 
\end{cases}
$$


### Monte Carlo Implementation: Constant Albedo $\alpha_1$
For the first analysis, we use the simple constant albedo $\alpha_1(T)=0.3$ and inject *Normal random noise* (mean 0, standard deviation = uncertainty) into the four uncertain constants ($k, R, Q, \sigma$). We then solve the ODE for each set of noisy parameters to get a distribution of solutions.

```python=
# --- Deterministic Constants ---
Q_det = 1365 
R_det = 6.3781e6 
k_det = 1e23 
sigma_det = 5.670374419e-8 
eps = 0.622
alpha = 0.3

# --- Uncertainty Values ---
k_err = 0.22e9
R_err = 10
Q_err = 27.3
sigma_err = 0.000021e-8

# --- Monte Carlo Setup ---
N = 1000 
t = np.linspace(0,1e9,100)
T0 = 267
T_mc_results = []

# --- Monte Carlo Simulation ---
for _ in range(N):
    # 1. Define Noisy Parameters (Value + N(0, error))
    Q_mc = Q_det + np.random.normal(loc=0, scale=Q_err)
    R_mc = R_det + np.random.normal(loc=0, scale=R_err)
    k_mc = k_det + np.random.normal(loc=0, scale=k_err)
    sigma_mc = sigma_det + np.random.normal(loc=0, scale=sigma_err)
    
    # 2. Define ODE function with Noisy Parameters
    f = lambda T, t: (np.pi * R_mc**2 * Q_mc * (1 - alpha) - 
                      4 * np.pi * R_mc**2 * eps * sigma_mc * T**4) / k_mc
    
    # 3. Solve the ODE
    T_mc = spi.odeint(f, T0, t)
    T_mc_results.append(T_mc[:, 0])

# --- Deterministic Solution (for comparison) ---
f_det = lambda T,t: (np.pi * R_det**2 * Q_det * (1 - alpha) - 
                     4 * np.pi * R_det**2 * eps * sigma_det * T**4) / k_det
T_determin = spi.odeint(f_det, T0, t)

# --- Plotting Results ---
plt.figure(figsize=(10, 6))
for T_series in T_mc_results:
    plt.plot(t, T_series, color='gray', alpha=0.2)
plt.plot(t, T_determin, color='red', linewidth=2, label='Deterministic Solution')
plt.title('Energy Balance Model Solutions with Parameter Uncertainty ($\ \\alpha_1(T)=0.3$)')
plt.xlabel('Time (s)')
plt.ylabel('Temperature (K)')
plt.legend()
plt.grid(True)
plt.show()
```

![image](https://hackmd.io/_uploads/SJDW8uR0gx.png)


### Monte Carlo Implementation: Temperature-Dependent Albedo $\alpha_2$
In this more complex analysis, we use the *temperature-dependent albedo* $\alpha_2(T)$ and focus the uncertainty on the *initial temperature* $T_0$, simulating $T_0 = 267 \pm 1 \text{ K}$. This helps determine how a small initial uncertainty affects the long-term solution, especially around the critical ice/water boundaries defined by $\alpha_2(T)$.

```python=
# --- Deterministic Constants ---
Q = 1365 
R = 6.3781e6 
k = 1e23 
sigma = 5.670374419e-8 
eps = 0.622

# --- Albedo 2 Function ---
def alpha2(T):
    if T <= 247:
        return 0.7
    elif 247 < T < 282:
        return 3.52296 - 0.011429 * T
    else:
        return 0.3

# Vectorize the Albedo function for use in ODE
alpha_vec = np.vectorize(alpha2)

# --- Monte Carlo Setup ---
N = 1000 
t = np.linspace(0, 1e9, 100)
T0_det = 267
T0_err = 1 # Uncertainty of +/- 1 K
T_mc_results2 = []

# --- Monte Carlo Simulation ---
for _ in range(N):
    # 1. Define Noisy Initial Temperature
    T0_mc = T0_det + np.random.normal(loc=0, scale=T0_err)
    
    # 2. Define ODE function with alpha2
    f1 = lambda T, t: (np.pi * R**2 * Q * (1 - alpha_vec(T)) - 
                       4 * np.pi * R**2 * eps * sigma * T**4) / k
    
    # 3. Solve the ODE
    T_mc = spi.odeint(f1, T0_mc, t)
    T_mc_results2.append(T_mc[:, 0])

# --- Plotting Results ---
plt.figure(figsize=(10, 6))
for T_series in T_mc_results2:
    plt.plot(t, T_series, color='gray', alpha=0.2)
plt.title('Energy Balance Model Solutions with Initial Temperature Uncertainty ($\ \\alpha_2(T)$)')
plt.xlabel('Time (s)')
plt.ylabel('Temperature (K)')
plt.grid(True)
plt.show()
```

![image](https://hackmd.io/_uploads/HJnywu0Cge.png)


### Analysis
By comparing the results from the $\alpha_1(T)$ and $\alpha_2(T)$ models, we can see that models with a temperature-dependent albedo are generally more sensitive to initial conditions. A small initial error (like the $\pm 1 \text{ K}$) can lead to a wider range of final equilibrium temperatures because the system can be pushed past a tipping point (e.g., above or below the $282 \text{ K}$ or $247 \text{ K}$ thresholds in $\alpha_2(T)$). This highlights the necessity of using Monte Carlo to assess sensitivity in complex, nonlinear models.