Initial commit
This commit is contained in:
185
A题/ZJ_v2/fig12_monte_carlo.py
Normal file
185
A题/ZJ_v2/fig12_monte_carlo.py
Normal file
@@ -0,0 +1,185 @@
|
||||
"""
|
||||
Fig 12: Monte Carlo Spaghetti Plot
|
||||
Shows uncertainty in TTE predictions with random perturbations
|
||||
"""
|
||||
|
||||
import os
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
from plot_style import set_oprice_style, save_figure
|
||||
|
||||
def simulate_stochastic_trajectory(base_power, duration_hours, n_points,
|
||||
sigma_P, theta_P, dt, seed_offset=0):
|
||||
"""
|
||||
Simulate one stochastic SOC trajectory with OU process for power
|
||||
|
||||
Args:
|
||||
base_power: Base power consumption (W)
|
||||
duration_hours: Simulation duration
|
||||
n_points: Number of time points
|
||||
sigma_P: OU process volatility
|
||||
theta_P: OU process mean reversion rate
|
||||
dt: Time step (hours)
|
||||
seed_offset: Random seed offset
|
||||
"""
|
||||
|
||||
np.random.seed(42 + seed_offset)
|
||||
|
||||
# Time axis
|
||||
t_h = np.linspace(0, duration_hours, n_points)
|
||||
|
||||
# Ornstein-Uhlenbeck process for power fluctuation
|
||||
dW = np.random.normal(0, np.sqrt(dt), n_points)
|
||||
P_perturbation = np.zeros(n_points)
|
||||
|
||||
for i in range(1, n_points):
|
||||
P_perturbation[i] = P_perturbation[i-1] * (1 - theta_P * dt) + sigma_P * dW[i]
|
||||
|
||||
P_total = base_power + P_perturbation
|
||||
P_total = np.clip(P_total, base_power * 0.5, base_power * 1.5)
|
||||
|
||||
# Compute SOC trajectory
|
||||
Q_full = 2.78 # Ah
|
||||
V_avg = 3.8 # V
|
||||
E_total = Q_full * V_avg # Wh
|
||||
|
||||
z = np.zeros(n_points)
|
||||
z[0] = 1.0
|
||||
|
||||
for i in range(1, n_points):
|
||||
# Energy consumed in this step
|
||||
dE = P_total[i] * dt
|
||||
dz = -dE / E_total
|
||||
z[i] = z[i-1] + dz
|
||||
|
||||
if z[i] < 0.05:
|
||||
z[i] = 0.05
|
||||
break
|
||||
|
||||
# Fill remaining with cutoff value
|
||||
z[z < 0.05] = 0.05
|
||||
|
||||
return t_h, z
|
||||
|
||||
def make_figure(config):
|
||||
"""Generate Fig 12: Monte Carlo Spaghetti Plot"""
|
||||
|
||||
set_oprice_style()
|
||||
|
||||
# Simulation parameters
|
||||
n_paths = 100
|
||||
duration_hours = 4.0
|
||||
n_points = 200
|
||||
dt = duration_hours / n_points
|
||||
|
||||
# Baseline power
|
||||
base_power = 15.0 # W
|
||||
|
||||
# OU process parameters for power uncertainty
|
||||
sigma_P = 2.0 # Volatility
|
||||
theta_P = 0.5 # Mean reversion rate
|
||||
|
||||
# Create figure
|
||||
fig, ax = plt.subplots(figsize=(12, 8))
|
||||
|
||||
# Store all paths for statistics
|
||||
all_z = []
|
||||
all_tte = []
|
||||
|
||||
# Simulate and plot paths
|
||||
for i in range(n_paths):
|
||||
t_h, z = simulate_stochastic_trajectory(base_power, duration_hours, n_points,
|
||||
sigma_P, theta_P, dt, seed_offset=i)
|
||||
|
||||
# Plot path
|
||||
if i < 50: # Only plot half to avoid overcrowding
|
||||
ax.plot(t_h, z * 100, color='gray', alpha=0.15, linewidth=0.8, zorder=1)
|
||||
|
||||
all_z.append(z)
|
||||
|
||||
# Find TTE (first time z <= 0.05)
|
||||
cutoff_idx = np.where(z <= 0.05)[0]
|
||||
if len(cutoff_idx) > 0:
|
||||
tte = t_h[cutoff_idx[0]]
|
||||
else:
|
||||
tte = duration_hours
|
||||
all_tte.append(tte)
|
||||
|
||||
# Compute statistics
|
||||
all_z = np.array(all_z)
|
||||
z_mean = np.mean(all_z, axis=0)
|
||||
z_std = np.std(all_z, axis=0)
|
||||
z_p5 = np.percentile(all_z, 5, axis=0)
|
||||
z_p95 = np.percentile(all_z, 95, axis=0)
|
||||
|
||||
t_h_ref = np.linspace(0, duration_hours, n_points)
|
||||
|
||||
# Plot confidence band
|
||||
ax.fill_between(t_h_ref, z_p5 * 100, z_p95 * 100,
|
||||
alpha=0.3, color='lightblue', label='90% Confidence Band', zorder=2)
|
||||
|
||||
# Plot mean
|
||||
ax.plot(t_h_ref, z_mean * 100, 'b-', linewidth=3, label='Mean Trajectory', zorder=3)
|
||||
|
||||
# Plot ±1 std
|
||||
ax.plot(t_h_ref, (z_mean + z_std) * 100, 'b--', linewidth=1.5, alpha=0.6,
|
||||
label='Mean ± 1σ', zorder=2)
|
||||
ax.plot(t_h_ref, (z_mean - z_std) * 100, 'b--', linewidth=1.5, alpha=0.6, zorder=2)
|
||||
|
||||
# Cutoff line
|
||||
ax.axhline(5, color='r', linestyle='--', linewidth=1.5, alpha=0.7, label='Cutoff (5%)')
|
||||
|
||||
# Labels and styling
|
||||
ax.set_xlabel('Time (hours)', fontsize=11)
|
||||
ax.set_ylabel('State of Charge (%)', fontsize=11)
|
||||
ax.set_title('Monte Carlo Uncertainty Quantification (N=100 paths)',
|
||||
fontsize=12, fontweight='bold')
|
||||
ax.set_xlim(0, duration_hours)
|
||||
ax.set_ylim(0, 105)
|
||||
ax.grid(True, alpha=0.3)
|
||||
ax.legend(loc='upper right', framealpha=0.9, fontsize=10)
|
||||
|
||||
# Add statistics box
|
||||
tte_mean = np.mean(all_tte)
|
||||
tte_std = np.std(all_tte)
|
||||
tte_p5 = np.percentile(all_tte, 5)
|
||||
tte_p95 = np.percentile(all_tte, 95)
|
||||
|
||||
stats_text = f'TTE Statistics (hours):\\n'
|
||||
stats_text += f'Mean: {tte_mean:.2f} ± {tte_std:.2f}\\n'
|
||||
stats_text += f'5th %ile: {tte_p5:.2f}\\n'
|
||||
stats_text += f'95th %ile: {tte_p95:.2f}\\n'
|
||||
stats_text += f'Range: [{tte_p5:.2f}, {tte_p95:.2f}]\\n'
|
||||
stats_text += f'Coefficient of Variation: {tte_std/tte_mean*100:.1f}%'
|
||||
|
||||
ax.text(0.02, 0.35, stats_text, transform=ax.transAxes,
|
||||
fontsize=9, verticalalignment='top',
|
||||
bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.8),
|
||||
family='monospace')
|
||||
|
||||
# Add annotation explaining OU process
|
||||
ax.text(0.98, 0.97, 'Uncertainty Model:\\nOrnstein-Uhlenbeck\\nprocess for power\\nfluctuations',
|
||||
transform=ax.transAxes, fontsize=9,
|
||||
verticalalignment='top', horizontalalignment='right',
|
||||
bbox=dict(boxstyle='round', facecolor='lightblue', alpha=0.7))
|
||||
|
||||
plt.tight_layout()
|
||||
|
||||
# Save
|
||||
figure_dir = config.get('global', {}).get('figure_dir', 'figures')
|
||||
os.makedirs(figure_dir, exist_ok=True)
|
||||
output_base = os.path.join(figure_dir, 'Fig12_Monte_Carlo')
|
||||
save_figure(fig, output_base, dpi=config.get('global', {}).get('dpi', 300))
|
||||
plt.close()
|
||||
|
||||
return {
|
||||
"output_files": [f"{output_base}.pdf", f"{output_base}.png"],
|
||||
"computed_metrics": {
|
||||
"tte_mean_h": float(tte_mean),
|
||||
"tte_std_h": float(tte_std),
|
||||
"tte_cv_pct": float(tte_std/tte_mean * 100),
|
||||
"n_paths": n_paths
|
||||
},
|
||||
"validation_flags": {},
|
||||
"pass": True
|
||||
}
|
||||
Reference in New Issue
Block a user