diff --git a/p3/water_supply_analysis.py b/p3/water_supply_analysis.py index 4994f1e..40759a5 100644 --- a/p3/water_supply_analysis.py +++ b/p3/water_supply_analysis.py @@ -484,6 +484,133 @@ def plot_scenario_comparison( return fig +def plot_scenario_comparison_combined( + results: List[Dict], + water_demand: Dict, + alpha: float = None, + save_dir: str = '/Volumes/Files/code/mm/20260130_b/p3' +): + """绘制方案对比图(Energy和Time合并显示)- 马卡龙色系简约版""" + + # 缩小图片尺寸,相对增大字体 + fig, axes = plt.subplots(1, 2, figsize=(10, 4.5)) + + scenarios = [r['scenario_name_cn'] for r in results] + scenario_labels = ['S1\nElevator', 'S2\nRocket', 'S3\nCombined\n(All)', 'S4\nCombined\n(Low-Lat)'] + + # 马卡龙色系 - 中偏低饱和度柔和色调 + macaron_colors = ['#A8D8B9', '#F7C5A8', '#B5C7E3', '#D4B8E0'] # 薄荷绿、杏橙、雾蓝、藤紫 + macaron_colors_dark = ['#7BC08F', '#E8A07A', '#8AA5C9', '#B896C7'] # 深色版本用于边框 + + x = np.arange(len(scenarios)) + width = 0.38 + + # 获取alpha值 + if alpha is None: + alpha = water_demand.get('comfort_factor', 1) + + # ========== 图1: 首批运输 (Energy + Time) ========== + ax1 = axes[0] + ax1_twin = ax1.twinx() + + initial_energy = [r['initial_energy_gj'] / 1000 for r in results] # 转换为TJ + initial_days = [r['initial_days'] for r in results] + + # 左轴: Energy (TJ) - 实心柱状图 + bars_energy = ax1.bar(x - width/2, initial_energy, width, color=macaron_colors, + edgecolor=macaron_colors_dark, linewidth=1.2, label='Energy (TJ)') + ax1.set_ylabel('Energy (TJ)', fontsize=11, color='#444444', fontweight='medium') + ax1.tick_params(axis='y', labelcolor='#444444', labelsize=9) + ax1.set_ylim(0, max(initial_energy) * 1.25) + + # 右轴: Time (Days) - 斜线填充柱状图 + bars_time = [] + for i, (xi, day) in enumerate(zip(x + width/2, initial_days)): + bar = ax1_twin.bar(xi, day, width, color='white', + edgecolor=macaron_colors_dark[i], linewidth=1.5, + hatch='///', label='Time (Days)' if i == 0 else '') + bars_time.append(bar[0]) + ax1_twin.set_ylabel('Days', fontsize=11, color='#666666', fontweight='medium') + ax1_twin.tick_params(axis='y', labelcolor='#666666', labelsize=9) + ax1_twin.set_ylim(0, max(initial_days) * 1.25) + + # 添加数值标注 - 简洁风格 + for bar, val in zip(bars_energy, initial_energy): + ax1.text(bar.get_x() + bar.get_width()/2, bar.get_height() + max(initial_energy)*0.02, + f'{val:.1f}', ha='center', fontsize=8, color='#333333') + + for bar, val in zip(bars_time, initial_days): + ax1_twin.text(bar.get_x() + bar.get_width()/2, bar.get_height() + max(initial_days)*0.02, + f'{val:.1f}', ha='center', fontsize=8, color='#666666') + + ax1.set_xticks(x) + ax1.set_xticklabels(scenario_labels, fontsize=9) + ax1.spines['top'].set_visible(False) + ax1_twin.spines['top'].set_visible(False) + ax1.grid(True, alpha=0.2, axis='y', linestyle='--') + + # 简化图例 + from matplotlib.patches import Patch + legend_elements = [Patch(facecolor='#B5C7E3', edgecolor='#8AA5C9', label='Energy'), + Patch(facecolor='white', edgecolor='#8AA5C9', hatch='///', label='Time')] + ax1.legend(handles=legend_elements, loc='upper right', fontsize=8, framealpha=0.9) + + # ========== 图2: 每月补充 (Energy + Time) ========== + ax2 = axes[1] + ax2_twin = ax2.twinx() + + monthly_energy = [r['monthly_energy_gj'] / 1000 for r in results] # 转换为TJ + monthly_days = [r['monthly_days'] for r in results] + + # 左轴: Energy (TJ) - 实心柱状图 + bars_energy2 = ax2.bar(x - width/2, monthly_energy, width, color=macaron_colors, + edgecolor=macaron_colors_dark, linewidth=1.2, label='Energy (TJ)') + ax2.set_ylabel('Energy (TJ)', fontsize=11, color='#444444', fontweight='medium') + ax2.tick_params(axis='y', labelcolor='#444444', labelsize=9) + ax2.set_ylim(0, max(monthly_energy) * 1.25) + + # 右轴: Time (Days) - 斜线填充柱状图 + bars_time2 = [] + for i, (xi, day) in enumerate(zip(x + width/2, monthly_days)): + bar = ax2_twin.bar(xi, day, width, color='white', + edgecolor=macaron_colors_dark[i], linewidth=1.5, + hatch='///', label='Time (Days)' if i == 0 else '') + bars_time2.append(bar[0]) + ax2_twin.set_ylabel('Days', fontsize=11, color='#666666', fontweight='medium') + ax2_twin.tick_params(axis='y', labelcolor='#666666', labelsize=9) + ax2_twin.set_ylim(0, max(monthly_days) * 1.25) + + # 添加数值标注 - 简洁风格 + for bar, val in zip(bars_energy2, monthly_energy): + ax2.text(bar.get_x() + bar.get_width()/2, bar.get_height() + max(monthly_energy)*0.02, + f'{val:.1f}', ha='center', fontsize=8, color='#333333') + + for bar, val in zip(bars_time2, monthly_days): + ax2_twin.text(bar.get_x() + bar.get_width()/2, bar.get_height() + max(monthly_days)*0.02, + f'{val:.1f}', ha='center', fontsize=8, color='#666666') + + ax2.set_xticks(x) + ax2.set_xticklabels(scenario_labels, fontsize=9) + ax2.spines['top'].set_visible(False) + ax2_twin.spines['top'].set_visible(False) + ax2.grid(True, alpha=0.2, axis='y', linestyle='--') + + # 简化图例 + ax2.legend(handles=legend_elements, loc='upper right', fontsize=8, framealpha=0.9) + + # 设置背景色为浅灰白色 + fig.patch.set_facecolor('#FAFAFA') + for ax in [ax1, ax2]: + ax.set_facecolor('#FAFAFA') + + plt.tight_layout() + filename = f'{save_dir}/water_transport_combined_alpha{int(alpha)}.png' + plt.savefig(filename, dpi=150, bbox_inches='tight', facecolor='#FAFAFA') + print(f"合并方案对比图已保存至: {filename}") + + return fig + + def plot_water_demand_breakdown( water_demand: Dict, save_dir: str = '/Volumes/Files/code/mm/20260130_b/p3' @@ -1063,6 +1190,10 @@ def run_water_analysis(): plot_scenario_comparison(all_results[1], all_demands[1]) plot_annual_comparison(all_results[1]) + # 为每个alpha值生成合并图 + for alpha in comfort_factors: + plot_scenario_comparison_combined(all_results[alpha], all_demands[alpha], alpha) + # 多场景对比图 plot_multi_scenario_comparison(all_results, all_demands) diff --git a/p3/water_transport_combined_alpha1.png b/p3/water_transport_combined_alpha1.png new file mode 100644 index 0000000..d95aff9 Binary files /dev/null and b/p3/water_transport_combined_alpha1.png differ diff --git a/p3/water_transport_combined_alpha250.png b/p3/water_transport_combined_alpha250.png new file mode 100644 index 0000000..1b23e8c Binary files /dev/null and b/p3/water_transport_combined_alpha250.png differ diff --git a/p3/water_transport_combined_alpha50.png b/p3/water_transport_combined_alpha50.png new file mode 100644 index 0000000..2eb9b5b Binary files /dev/null and b/p3/water_transport_combined_alpha50.png differ