This commit is contained in:
2026-02-03 01:09:09 +08:00
parent 17386eb724
commit 5cc8c5bd9a
9 changed files with 1187 additions and 1013 deletions

View File

@@ -1,160 +1,160 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Richards S-Curve Fit for 2010-2025 Data with K=4298 Richards S-Curve Fit for 2010-2025 Data with K=4298
Fits Richards model to 2010-2025 launch data with carrying capacity Fits Richards model to 2010-2025 launch data with carrying capacity
constrained to K=4298 (close to physical limit of 3650). constrained to K=4298 (close to physical limit of 3650).
""" """
import pandas as pd import pandas as pd
import numpy as np import numpy as np
from scipy.optimize import curve_fit from scipy.optimize import curve_fit
import matplotlib import matplotlib
matplotlib.use('Agg') matplotlib.use('Agg')
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import warnings import warnings
warnings.filterwarnings('ignore') warnings.filterwarnings('ignore')
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS', 'SimHei', 'DejaVu Sans'] plt.rcParams['font.sans-serif'] = ['Arial Unicode MS', 'SimHei', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False plt.rcParams['axes.unicode_minus'] = False
def richards(t, K, r, t0, v): def richards(t, K, r, t0, v):
"""Richards curve (generalized logistic model)""" """Richards curve (generalized logistic model)"""
exp_term = np.exp(-r * (t - t0)) exp_term = np.exp(-r * (t - t0))
exp_term = np.clip(exp_term, 1e-10, 1e10) exp_term = np.clip(exp_term, 1e-10, 1e10)
return K / np.power(1 + exp_term, 1/v) return K / np.power(1 + exp_term, 1/v)
def richards_fixed_K(t, r, t0, v): def richards_fixed_K(t, r, t0, v):
"""Richards curve with fixed K=4298""" """Richards curve with fixed K=4298"""
K = 4298 K = 4298
exp_term = np.exp(-r * (t - t0)) exp_term = np.exp(-r * (t - t0))
exp_term = np.clip(exp_term, 1e-10, 1e10) exp_term = np.clip(exp_term, 1e-10, 1e10)
return K / np.power(1 + exp_term, 1/v) return K / np.power(1 + exp_term, 1/v)
def load_data(filepath="rocket_launch_counts.csv"): def load_data(filepath="rocket_launch_counts.csv"):
"""Load rocket launch data""" """Load rocket launch data"""
df = pd.read_csv(filepath) df = pd.read_csv(filepath)
df = df.rename(columns={"YDate": "year", "Total": "launches"}) df = df.rename(columns={"YDate": "year", "Total": "launches"})
df["year"] = pd.to_numeric(df["year"], errors="coerce") df["year"] = pd.to_numeric(df["year"], errors="coerce")
df["launches"] = pd.to_numeric(df["launches"], errors="coerce") df["launches"] = pd.to_numeric(df["launches"], errors="coerce")
df = df.dropna(subset=["year", "launches"]) df = df.dropna(subset=["year", "launches"])
df = df[(df["year"] >= 1957) & (df["year"] <= 2025)] df = df[(df["year"] >= 1957) & (df["year"] <= 2025)]
df = df.astype({"year": int, "launches": int}) df = df.astype({"year": int, "launches": int})
df = df.sort_values("year").reset_index(drop=True) df = df.sort_values("year").reset_index(drop=True)
return df return df
def main(): def main():
print("=" * 60) print("=" * 60)
print("Richards S-Curve Fit (2010-2025 Data, K=4298)") print("Richards S-Curve Fit (2010-2025 Data, K=4298)")
print("=" * 60) print("=" * 60)
# Load data # Load data
df = load_data() df = load_data()
# Filter 2010-2025 # Filter 2010-2025
start_year = 2010 start_year = 2010
end_year = 2025 end_year = 2025
data = df[(df["year"] >= start_year) & (df["year"] <= end_year)].copy() data = df[(df["year"] >= start_year) & (df["year"] <= end_year)].copy()
years = data["year"].values years = data["year"].values
launches = data["launches"].values launches = data["launches"].values
print(f"Data range: {start_year} - {end_year}") print(f"Data range: {start_year} - {end_year}")
print(f"Data points: {len(data)}") print(f"Data points: {len(data)}")
print(f"\nHistorical data:") print(f"\nHistorical data:")
for y, l in zip(years, launches): for y, l in zip(years, launches):
print(f" {y}: {l} launches") print(f" {y}: {l} launches")
# Fit with fixed K=4298 # Fit with fixed K=4298
K_fixed = 4298 K_fixed = 4298
t = (years - start_year).astype(float) t = (years - start_year).astype(float)
p0 = [0.2, 20.0, 2.0] # r, t0, v p0 = [0.2, 20.0, 2.0] # r, t0, v
bounds = ([0.01, 5, 0.5], [1.0, 100, 10.0]) bounds = ([0.01, 5, 0.5], [1.0, 100, 10.0])
try: try:
popt, pcov = curve_fit(richards_fixed_K, t, launches, p0=p0, bounds=bounds, maxfev=100000) popt, pcov = curve_fit(richards_fixed_K, t, launches, p0=p0, bounds=bounds, maxfev=100000)
r, t0, v = popt r, t0, v = popt
# Calculate R² # Calculate R²
y_pred = richards_fixed_K(t, *popt) y_pred = richards_fixed_K(t, *popt)
ss_res = np.sum((launches - y_pred) ** 2) ss_res = np.sum((launches - y_pred) ** 2)
ss_tot = np.sum((launches - np.mean(launches)) ** 2) ss_tot = np.sum((launches - np.mean(launches)) ** 2)
r_squared = 1 - (ss_res / ss_tot) r_squared = 1 - (ss_res / ss_tot)
print(f"\nFitted Parameters (K fixed at {K_fixed}):") print(f"\nFitted Parameters (K fixed at {K_fixed}):")
print(f" K (carrying capacity) = {K_fixed} launches/year (FIXED)") print(f" K (carrying capacity) = {K_fixed} launches/year (FIXED)")
print(f" r (growth rate) = {r:.4f}") print(f" r (growth rate) = {r:.4f}")
print(f" t0 (inflection point) = {start_year + t0:.1f}") print(f" t0 (inflection point) = {start_year + t0:.1f}")
print(f" v (shape parameter) = {v:.3f}") print(f" v (shape parameter) = {v:.3f}")
print(f" R² = {r_squared:.4f}") print(f" R² = {r_squared:.4f}")
except Exception as e: except Exception as e:
print(f"Fitting error: {e}") print(f"Fitting error: {e}")
return return
# Physical limit # Physical limit
physical_max = 3650 physical_max = 3650
print(f"\nPhysical limit: {physical_max} (10 sites × 365 days)") print(f"\nPhysical limit: {physical_max} (10 sites × 365 days)")
print(f"K / Physical limit = {K_fixed/physical_max:.2f}x") print(f"K / Physical limit = {K_fixed/physical_max:.2f}x")
# ========== Create Visualization ========== # ========== Create Visualization ==========
# 缩小图片尺寸(比例不变),使字体相对更大 # 缩小图片尺寸(比例不变),使字体相对更大
fig, ax = plt.subplots(figsize=(8, 4.67)) fig, ax = plt.subplots(figsize=(8, 4.67))
# 中低饱和度配色 # 中低饱和度配色
color_data = '#5D6D7E' # 灰蓝色 - 数据点 color_data = '#5D6D7E' # 灰蓝色 - 数据点
color_curve = '#52796F' # 暗绿色 - S曲线 color_curve = '#52796F' # 暗绿色 - S曲线
color_target = '#7B9EA8' # 灰蓝绿色 - 2050标记 color_target = '#7B9EA8' # 灰蓝绿色 - 2050标记
# Historical data points # Historical data points
ax.scatter(years, launches, color=color_data, s=80, alpha=0.85, ax.scatter(years, launches, color=color_data, s=80, alpha=0.85,
label='Historical Data (2010-2025)', zorder=4, edgecolor='white', linewidth=1) label='Historical Data (2010-2025)', zorder=4, edgecolor='white', linewidth=1)
# Generate smooth S-curve # Generate smooth S-curve
years_smooth = np.linspace(start_year, 2055, 500) years_smooth = np.linspace(start_year, 2055, 500)
t_smooth = years_smooth - start_year t_smooth = years_smooth - start_year
pred_smooth = richards(t_smooth, K_fixed, r, t0, v) pred_smooth = richards(t_smooth, K_fixed, r, t0, v)
# S-curve prediction # S-curve prediction
ax.plot(years_smooth, pred_smooth, color=color_curve, lw=2.5, ax.plot(years_smooth, pred_smooth, color=color_curve, lw=2.5,
label=f'Richards Model (K={K_fixed}, R²={r_squared:.3f})', zorder=2) label=f'Richards Model (K={K_fixed}, R²={r_squared:.3f})', zorder=2)
# K=4298 saturation line # K=4298 saturation line
ax.axhline(K_fixed, color=color_curve, ls=':', lw=1.5, alpha=0.6, ax.axhline(K_fixed, color=color_curve, ls=':', lw=1.5, alpha=0.6,
label=f'K = {K_fixed}') label=f'K = {K_fixed}')
# Mark 2050 line only # Mark 2050 line only
ax.axvline(2050, color=color_target, ls=':', lw=1.5, alpha=0.7) ax.axvline(2050, color=color_target, ls=':', lw=1.5, alpha=0.7)
ax.text(2050.5, K_fixed*0.83, '2050\n(Target)', fontsize=9, color=color_target) ax.text(2050.5, K_fixed*0.83, '2050\n(Target)', fontsize=9, color=color_target)
# Only show 2050 prediction point # Only show 2050 prediction point
t_2050 = 2050 - start_year t_2050 = 2050 - start_year
pred_2050 = richards(t_2050, K_fixed, r, t0, v) pred_2050 = richards(t_2050, K_fixed, r, t0, v)
ax.scatter([2050], [pred_2050], color=color_target, s=60, marker='D', zorder=4) ax.scatter([2050], [pred_2050], color=color_target, s=60, marker='D', zorder=4)
ax.annotate(f'{pred_2050:.0f}', xy=(2050, pred_2050), ax.annotate(f'{pred_2050:.0f}', xy=(2050, pred_2050),
xytext=(2050.5, pred_2050+180), xytext=(2050.5, pred_2050+180),
fontsize=9, color=color_target, fontweight='bold') fontsize=9, color=color_target, fontweight='bold')
# Formatting # Formatting
ax.set_xlabel('Year', fontsize=11) ax.set_xlabel('Year', fontsize=11)
ax.set_ylabel('Annual Launches', fontsize=11) ax.set_ylabel('Annual Launches', fontsize=11)
ax.legend(loc='upper left', fontsize=9) ax.legend(loc='upper left', fontsize=9)
ax.grid(True, alpha=0.25) ax.grid(True, alpha=0.25)
ax.set_xlim(2010, 2055) ax.set_xlim(2010, 2055)
ax.set_ylim(0, K_fixed * 1.15) ax.set_ylim(0, K_fixed * 1.15)
plt.tight_layout() plt.tight_layout()
plt.savefig('richards_curve_2010_K4298.png', dpi=150, bbox_inches='tight') plt.savefig('richards_curve_2010_K4298.png', dpi=150, bbox_inches='tight')
plt.close() plt.close()
print("\nPlot saved: richards_curve_2010_K4298.png") print("\nPlot saved: richards_curve_2010_K4298.png")
print("=" * 60) print("=" * 60)
if __name__ == "__main__": if __name__ == "__main__":
main() main()

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

View File

@@ -0,0 +1,89 @@
"""
生成论文用的完成时间分布图 - 改进版
"""
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from matplotlib import rcParams
import pandas as pd
# 设置字体
rcParams['font.sans-serif'] = ['Arial', 'DejaVu Sans', 'Helvetica']
rcParams['axes.unicode_minus'] = False
rcParams['font.size'] = 11
# 读取模拟结果数据
df = pd.read_csv('/Volumes/Files/code/mm/20260130_b/p2/simulation_results.csv')
# 中低饱和度配色方案 - 柔和学术风格
colors = ['#7BAE7F', '#6A9ECF', '#9B8EC2'] # 柔和的绿、蓝、紫
# 方案名称
scenario_labels = {
'Scenario_A': 'Cost Priority',
'Scenario_B': 'Time Priority',
'Scenario_C': 'Balanced'
}
# 创建图表 - 缩小尺寸以放大字体效果
fig, axes = plt.subplots(1, 3, figsize=(10, 3.2))
for idx, (scenario_key, label) in enumerate(scenario_labels.items()):
ax = axes[idx]
# 获取该方案的数据
data = df[df['scenario'] == scenario_key]['completion_years']
# 计算统计量
mean_val = np.mean(data)
p5 = np.percentile(data, 5)
p95 = np.percentile(data, 95)
# 绘制柱状图 - 实心柱状图,增粗柱子
ax.hist(data, bins=15, color=colors[idx], alpha=0.9,
edgecolor='white', linewidth=0.8, rwidth=0.9)
# 设置子图标题 - 简洁的 (a) (b) (c) 标签
ax.set_title(f'({chr(97+idx)}) {label}', fontsize=12, fontweight='normal', pad=8)
# 设置坐标轴标签
ax.set_xlabel('Completion Years', fontsize=11)
if idx == 0:
ax.set_ylabel('Frequency', fontsize=11)
# 在图内右上角添加纯文本统计信息(无边框)
text_str = f'Mean: {mean_val:.1f}\n5%: {p5:.1f}\n95%: {p95:.1f}'
ax.text(0.97, 0.97, text_str, transform=ax.transAxes, fontsize=9,
verticalalignment='top', horizontalalignment='right',
bbox=dict(boxstyle='round,pad=0.3', facecolor='white',
edgecolor='none', alpha=0.85))
# 网格线 - 非常淡
ax.grid(True, alpha=0.15, linestyle='-', linewidth=0.5)
# 设置刻度字体大小
ax.tick_params(axis='both', labelsize=10)
# 中间图 (b) 的 x 轴使用整数刻度
if idx == 1:
from matplotlib.ticker import MaxNLocator
ax.xaxis.set_major_locator(MaxNLocator(integer=True))
# 简化边框
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
# 调整布局
plt.tight_layout()
# 保存图片
plt.savefig('/Volumes/Files/code/mm/20260130_b/p2/completion_time_distribution_paper.png',
dpi=200, bbox_inches='tight', facecolor='white')
plt.savefig('/Volumes/Files/code/mm/20260130_b/p2/completion_time_distribution_paper.pdf',
dpi=200, bbox_inches='tight', facecolor='white')
print("图表已保存:")
print(" - completion_time_distribution_paper.png")
print(" - completion_time_distribution_paper.pdf")

View File

@@ -0,0 +1,85 @@
"""
生成论文用的能量分布图 - 改进版
"""
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from matplotlib import rcParams
from matplotlib.ticker import MaxNLocator
import pandas as pd
# 设置字体
rcParams['font.sans-serif'] = ['Arial', 'DejaVu Sans', 'Helvetica']
rcParams['axes.unicode_minus'] = False
rcParams['font.size'] = 11
# 读取模拟结果数据
df = pd.read_csv('/Volumes/Files/code/mm/20260130_b/p2/simulation_results.csv')
# 中低饱和度配色方案 - 不同于之前的绿蓝紫,使用暖色调
colors = ['#D4936A', '#6AACAC', '#C48BB8'] # 柔和的橙、青、玫瑰
# 方案名称
scenario_labels = {
'Scenario_A': 'Cost Priority',
'Scenario_B': 'Time Priority',
'Scenario_C': 'Balanced'
}
# 创建图表 - 缩小尺寸以放大字体效果
fig, axes = plt.subplots(1, 3, figsize=(10, 3.2))
for idx, (scenario_key, label) in enumerate(scenario_labels.items()):
ax = axes[idx]
# 获取该方案的数据
data = df[df['scenario'] == scenario_key]['total_energy_pj']
# 计算统计量
mean_val = np.mean(data)
p5 = np.percentile(data, 5)
p95 = np.percentile(data, 95)
# 绘制柱状图 - 实心柱状图,增粗柱子
ax.hist(data, bins=15, color=colors[idx], alpha=0.9,
edgecolor='white', linewidth=0.8, rwidth=0.9)
# 设置子图标题 - 简洁的 (a) (b) (c) 标签
ax.set_title(f'({chr(97+idx)}) {label}', fontsize=12, fontweight='normal', pad=8)
# 设置坐标轴标签
ax.set_xlabel('Total Energy (PJ)', fontsize=11)
if idx == 0:
ax.set_ylabel('Frequency', fontsize=11)
# 在图内右上角添加纯文本统计信息(无边框)
text_str = f'Mean: {mean_val:.0f}\n5%: {p5:.0f}\n95%: {p95:.0f}'
ax.text(0.97, 0.97, text_str, transform=ax.transAxes, fontsize=9,
verticalalignment='top', horizontalalignment='right',
bbox=dict(boxstyle='round,pad=0.3', facecolor='white',
edgecolor='none', alpha=0.85))
# 网格线 - 非常淡
ax.grid(True, alpha=0.15, linestyle='-', linewidth=0.5)
# 设置刻度字体大小
ax.tick_params(axis='both', labelsize=10)
# 简化边框
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
# 调整布局
plt.tight_layout()
# 保存图片
plt.savefig('/Volumes/Files/code/mm/20260130_b/p2/energy_distribution_paper.png',
dpi=200, bbox_inches='tight', facecolor='white')
plt.savefig('/Volumes/Files/code/mm/20260130_b/p2/energy_distribution_paper.pdf',
dpi=200, bbox_inches='tight', facecolor='white')
print("图表已保存:")
print(" - energy_distribution_paper.png")
print(" - energy_distribution_paper.pdf")