Initial commit

This commit is contained in:
ChuXun
2026-02-16 21:52:26 +08:00
commit 18ce59bec7
334 changed files with 35333 additions and 0 deletions

View File

@@ -0,0 +1,131 @@
"""
Fig 13: Survival/Reliability Curve
Shows probability of battery lasting beyond time t
"""
import os
import numpy as np
import matplotlib.pyplot as plt
from plot_style import set_oprice_style, save_figure
from validation import check_monotonic_decreasing
def make_figure(config):
"""Generate Fig 13: Survival Curve"""
set_oprice_style()
seed = config.get('global', {}).get('seed', 42)
np.random.seed(seed)
# Generate TTE distribution (log-normal)
n_samples = 300
tte_mean = 2.5 # hours
tte_std = 0.4
# Log-normal parameters
mu = np.log(tte_mean**2 / np.sqrt(tte_mean**2 + tte_std**2))
sigma = np.sqrt(np.log(1 + tte_std**2 / tte_mean**2))
tte_samples = np.random.lognormal(mu, sigma, n_samples)
tte_samples = np.clip(tte_samples, 1.0, 5.0)
# Sort for survival function
tte_sorted = np.sort(tte_samples)
# Compute survival function: S(t) = P(TTE > t)
t_values = np.linspace(0, 4.5, 200)
survival_prob = np.zeros(len(t_values))
for i, t in enumerate(t_values):
survival_prob[i] = np.sum(tte_sorted > t) / n_samples
# Find confidence levels
tte_50 = np.percentile(tte_sorted, 50) # Median
tte_95 = np.percentile(tte_sorted, 5) # 95% confidence (5th percentile)
# Create figure
fig, ax = plt.subplots(figsize=(12, 8))
# Plot survival curve
ax.plot(t_values, survival_prob * 100, 'b-', linewidth=3, label='Survival Function')
# Fill area under curve
ax.fill_between(t_values, 0, survival_prob * 100, alpha=0.2, color='lightblue')
# Mark key percentiles
ax.axhline(50, color='orange', linestyle='--', linewidth=1.5, alpha=0.7, label='50% Survival')
ax.axvline(tte_50, color='orange', linestyle=':', linewidth=1.5, alpha=0.7)
ax.plot(tte_50, 50, 'o', markersize=10, color='orange', markeredgecolor='white',
markeredgewidth=2, zorder=5)
ax.text(tte_50 + 0.1, 50, f'Median TTE = {tte_50:.2f}h', fontsize=10,
verticalalignment='center')
ax.axhline(95, color='green', linestyle='--', linewidth=1.5, alpha=0.7, label='95% Confidence')
ax.axvline(tte_95, color='green', linestyle=':', linewidth=1.5, alpha=0.7)
ax.plot(tte_95, 95, 's', markersize=10, color='green', markeredgecolor='white',
markeredgewidth=2, zorder=5)
ax.text(tte_95 + 0.1, 95, f'95% Confident TTE = {tte_95:.2f}h', fontsize=10,
verticalalignment='center')
# Annotate interpretation
ax.annotate('95% of devices will\nlast at least this long',
xy=(tte_95, 95), xytext=(tte_95 - 0.5, 75),
arrowprops=dict(arrowstyle='->', color='green', lw=2),
fontsize=10, color='darkgreen',
bbox=dict(boxstyle='round', facecolor='lightgreen', alpha=0.7))
# Labels and styling
ax.set_xlabel('Time (hours)', fontsize=11)
ax.set_ylabel('Survival Probability (%)', fontsize=11)
ax.set_title('Battery Reliability: Survival Function Analysis',
fontsize=12, fontweight='bold')
ax.set_xlim(0, 4.5)
ax.set_ylim(0, 105)
ax.grid(True, alpha=0.3)
ax.legend(loc='upper right', framealpha=0.9, fontsize=10)
# Add distribution statistics
stats_text = f'TTE Distribution:\\n'
stats_text += f'Mean: {tte_mean:.2f}h\\n'
stats_text += f'Std: {tte_std:.2f}h\\n'
stats_text += f'Median: {tte_50:.2f}h\\n'
stats_text += f'95% CI: {tte_95:.2f}h\\n'
stats_text += f'Sample Size: {n_samples}'
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 interpretation guide
guide_text = 'Interpretation:\\n' + \
'• S(t) = P(TTE > t)\\n' + \
'• Steep drop = high variability\\n' + \
'• Use 95% value for conservative estimates'
ax.text(0.98, 0.02, guide_text, transform=ax.transAxes,
fontsize=9, verticalalignment='bottom', 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, 'Fig13_Survival_Curve')
save_figure(fig, output_base, dpi=config.get('global', {}).get('dpi', 300))
plt.close()
# Validation
is_monotonic = check_monotonic_decreasing(survival_prob)
return {
"output_files": [f"{output_base}.pdf", f"{output_base}.png"],
"computed_metrics": {
"median_tte_h": float(tte_50),
"tte_95_confidence_h": float(tte_95),
"n_samples": n_samples
},
"validation_flags": {"survival_monotonic": is_monotonic},
"pass": is_monotonic
}