Lab 09: Monte Carlo Simulation#
Examples (by instructor)#
In this lab, you’ll practice Monte Carlo simulation from Chapter 14: sampling from input distributions, propagating uncertainty through a model, and summarizing the output distribution to estimate failure rates and reliability.
Example 1: Estimating π with Monte Carlo#
Throw \(N\) random darts at a unit square. The fraction landing inside the quarter-circle estimates \(\pi/4\).
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
Example 2: Process Reliability — Propagating Input Uncertainty#
A reactor product must have purity ≥ 97.5%. Residence time \(\tau \sim \mathcal{N}(120, 12^2)\) s and temperature \(T \sim \mathcal{N}(400, 8^2)\) K vary each batch. The purity model is:
Run 50,000 simulated batches and estimate the failure rate.
Example 3: Convergence — How Many Samples Are Enough?#
The MC estimate improves as \(N\) grows. Error shrinks as \(\sim 1/\sqrt{N}\). Track the running failure rate estimate as batches accumulate.
Warm-Up: Syntax Practice#
Short exercises to get comfortable with the MC building blocks before the main problems.
Exercise 1 — Sample from a distribution.
Using np.random.default_rng(seed=10), draw 1,000 samples from \(\mathcal{N}(\mu=500,\; \sigma=20)\). Print the sample mean and std. Then print the fraction of samples exceeding 530.
import numpy as np
rng = np.random.default_rng(seed=10)
samples = rng.normal(500, 20, 1000)
print(f"Sample mean : {np.mean(samples):.2f}")
print(f"Sample std : {np.std(samples, ddof=1):.2f}")
print(f"Fraction > 530 : {np.mean(samples > 530):.4f}")
Sample mean : 498.62
Sample std : 19.90
Fraction > 530 : 0.0550
Exercise 2 — Pass a sample through a model.
Feed concentration \(C_0 \sim \mathcal{N}(2.0,\; 0.1^2)\) mol/L. After a first-order reaction with fixed \(k=0.5\) min⁻¹ and \(t=3\) min, the outlet concentration is:
Using seed=5 and \(N=5{,}000\) samples, compute the mean and std of \(C\). Print what fraction of outlet concentrations fall below 0.25 mol/L.
import numpy as np
k, t = 0.5, 3.0
rng = np.random.default_rng(___)
C0 = rng.normal(___, ___, ___)
C_out = ___
print(f"Mean C_out : {np.mean(C_out):.4f} mol/L")
print(f"Std C_out : {np.std(C_out, ddof=1):.4f} mol/L")
print(f"Fraction below 0.25 mol/L : {np.mean(C_out < 0.25):.4f}")
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[3], line 5
1 import numpy as np
3 k, t = 0.5, 3.0
----> 5 rng = np.random.default_rng(___)
6 C0 = rng.normal(___, ___, ___)
8 C_out = ___
File numpy/random/_generator.pyx:4957, in numpy.random._generator.default_rng()
File _pcg64.pyx:123, in numpy.random._pcg64.PCG64.__init__()
File bit_generator.pyx:535, in numpy.random.bit_generator.BitGenerator.__init__()
File bit_generator.pyx:307, in numpy.random.bit_generator.SeedSequence.__init__()
TypeError: SeedSequence expects int or sequence of ints for entropy not
Exercise 3 — Two uncertain inputs.
Both \(k \sim \mathcal{N}(0.5,\; 0.05^2)\) and \(t \sim \mathcal{N}(3.0,\; 0.2^2)\) are uncertain (with fixed \(C_0 = 2.0\) mol/L). Using seed=8 and \(N=10{,}000\), sample both, compute \(C = C_0\,e^{-k\,t}\), and print mean, std, and fraction below 0.25 mol/L. Compare your answer to Exercise 2 — does uncertainty in \(k\) and \(t\) increase or decrease the spread?
import numpy as np
C0 = 2.0
rng = np.random.default_rng(___)
k_s = rng.___(___, ___, ___)
t_s = rng.___(___, ___, ___)
C_out2 = ___
print(f"Mean C_out : {np.mean(C_out2):.4f} mol/L")
print(f"Std C_out : {np.std(C_out2, ddof=1):.4f} mol/L")
print(f"Fraction below 0.25 mol/L : {np.mean(C_out2 < 0.25):.4f}")
Exercise 4 — Summarize output distribution.
Using the C_out2 array from Exercise 3, print the 5th and 95th percentiles and plot a histogram with a vertical line at the 5th percentile.
import numpy as np
import matplotlib.pyplot as plt
# Compute percentiles
p5 = np.___(C_out2, ___)
p95 = np.___(C_out2, ___)
print(f"5th percentile : {p5:.4f} mol/L")
print(f"95th percentile : {p95:.4f} mol/L")
fig, ax = plt.subplots(figsize=(7, 4))
ax.hist(C_out2, bins=50, color='steelblue', edgecolor='white', alpha=0.8, density=True)
ax.axvline(p5, color='tomato', linestyle='--', linewidth=2, label=f'5th pct = {p5:.3f}')
ax.set_xlabel('Outlet concentration (mol/L)')
ax.set_ylabel('Density')
ax.set_title('MC Output Distribution')
ax.legend()
plt.tight_layout()
plt.show()
Practice Problems (by students)#
Problem 1: Heat Exchanger — Outlet Temperature Reliability#
A shell-and-tube heat exchanger heats a process stream. The outlet temperature is modeled as:
For simplicity, use the linearized form:
where \(\eta\) is the heat exchanger effectiveness. The following inputs are uncertain each day:
Variable |
Symbol |
Distribution |
|---|---|---|
Inlet temperature (°C) |
\(T_\text{in}\) |
\(\mathcal{N}(25,\; 3^2)\) |
Hot side temperature (°C) |
\(T_\text{hot}\) |
\(\mathcal{N}(120,\; 5^2)\) |
Effectiveness |
\(\eta\) |
\(\mathcal{N}(0.75,\; 0.04^2)\) |
The process requires \(T_\text{out} \geq 80°C\).
(a) Using seed=15 and \(N = 50{,}000\) samples, simulate \(T_\text{out}\) for all three uncertain inputs. Print mean, std, 5th percentile, and the failure rate (% of days below 80°C).
import numpy as np
import matplotlib.pyplot as plt
# --- Model ---
# (a) All three inputs uncertain
(b) Plot the output histogram with vertical lines at the spec limit (80°C) and the mean.
# (b) Histogram with spec line and shaded failing region
(c) Which input contributes most to variability? Re-run the simulation three times, each time holding two inputs fixed at their means and only sampling the third. Print the std of \(T_\text{out}\) for each case and identify the most influential input.
# (c) One-at-a-time sensitivity
Problem 2: Batch Reactor Yield — Operating Condition Optimization#
A batch reactor produces a polymer. The yield (%) is modeled as:
with \(A = 2\times10^6\) min⁻¹, \(E_a/R = 6{,}000\) K, and the following uncertain operating conditions:
Variable |
Symbol |
Baseline |
Std dev |
|---|---|---|---|
Residence time (min) |
\(\tau\) |
30 |
3 |
Temperature (K) |
\(T\) |
370 |
10 |
The minimum acceptable yield is 85%.
(a) Using seed=99 and \(N=50{,}000\), simulate the yield distribution at the baseline conditions. Print mean, std, and failure rate.
import numpy as np
import matplotlib.pyplot as plt
# --- Model ---
# (a) Baseline: tau~N(30,3), T~N(370,10)
(b) The process engineer proposes raising the mean temperature to 380 K (same std = 10 K). Re-run the simulation with this change. By how many percentage points does the failure rate drop?
# (b) Higher mean T: T~N(380,10)
(c) Alternatively, the engineer could tighten temperature control to \(\sigma_T = 5\) K (keeping mean at 370 K). Re-run this scenario. Compare the failure rate to part (b) — which strategy is more effective?
# (c) Tighter sigma_T: T~N(370,5)