2025-12-25 18:06:12 +08:00
|
|
|
|
"""
|
|
|
|
|
|
高级可视化程序 - 多能互补系统储能容量优化
|
|
|
|
|
|
|
|
|
|
|
|
该程序提供更丰富的可视化功能,包括多种图表类型和交互式选项。
|
|
|
|
|
|
|
|
|
|
|
|
作者: iFlow CLI
|
|
|
|
|
|
创建日期: 2025-12-25
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
import matplotlib.pyplot as plt
|
|
|
|
|
|
import matplotlib.dates as mdates
|
|
|
|
|
|
import numpy as np
|
|
|
|
|
|
from datetime import datetime, timedelta
|
|
|
|
|
|
from storage_optimization import optimize_storage_capacity, SystemParameters
|
|
|
|
|
|
|
|
|
|
|
|
# 设置中文字体
|
|
|
|
|
|
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'DejaVu Sans']
|
|
|
|
|
|
plt.rcParams['axes.unicode_minus'] = False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def create_comprehensive_plot(solar_output, wind_output, thermal_output, load_demand, result, params):
|
|
|
|
|
|
"""
|
|
|
|
|
|
创建综合可视化图表
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
solar_output: 24小时光伏出力曲线 (MW)
|
|
|
|
|
|
wind_output: 24小时风电出力曲线 (MW)
|
|
|
|
|
|
thermal_output: 24小时火电出力曲线 (MW)
|
|
|
|
|
|
load_demand: 24小时负荷曲线 (MW)
|
|
|
|
|
|
result: 优化结果字典
|
|
|
|
|
|
params: 系统参数
|
|
|
|
|
|
"""
|
|
|
|
|
|
hours = np.arange(24)
|
|
|
|
|
|
|
|
|
|
|
|
# 创建大型图形
|
|
|
|
|
|
fig = plt.figure(figsize=(16, 12))
|
|
|
|
|
|
fig.suptitle('多能互补系统储能容量优化分析', fontsize=18, fontweight='bold')
|
|
|
|
|
|
|
|
|
|
|
|
# 创建网格布局
|
|
|
|
|
|
gs = fig.add_gridspec(3, 3, hspace=0.3, wspace=0.3)
|
|
|
|
|
|
|
|
|
|
|
|
# === 主要图表:发电和负荷 ===
|
|
|
|
|
|
ax1 = fig.add_subplot(gs[0, :])
|
|
|
|
|
|
|
|
|
|
|
|
# 绘制各发电类型
|
|
|
|
|
|
ax1.fill_between(hours, 0, thermal_output, alpha=0.7, color='blue', label='火电')
|
|
|
|
|
|
ax1.fill_between(hours, thermal_output,
|
|
|
|
|
|
[thermal_output[i] + wind_output[i] for i in range(24)],
|
|
|
|
|
|
alpha=0.7, color='green', label='风电')
|
|
|
|
|
|
ax1.fill_between(hours, [thermal_output[i] + wind_output[i] for i in range(24)],
|
|
|
|
|
|
[thermal_output[i] + wind_output[i] + solar_output[i] for i in range(24)],
|
|
|
|
|
|
alpha=0.7, color='orange', label='光伏')
|
|
|
|
|
|
|
|
|
|
|
|
# 绘制负荷曲线
|
|
|
|
|
|
ax1.plot(hours, load_demand, 'r-', linewidth=3, label='负荷需求')
|
|
|
|
|
|
|
|
|
|
|
|
ax1.set_xlabel('时间 (小时)')
|
|
|
|
|
|
ax1.set_ylabel('功率 (MW)')
|
|
|
|
|
|
ax1.set_title('24小时发电与负荷平衡')
|
|
|
|
|
|
ax1.legend(loc='upper right')
|
|
|
|
|
|
ax1.grid(True, alpha=0.3)
|
|
|
|
|
|
ax1.set_xlim(0, 23)
|
|
|
|
|
|
|
|
|
|
|
|
# === 储能充放电功率 ===
|
|
|
|
|
|
ax2 = fig.add_subplot(gs[1, 0])
|
|
|
|
|
|
charge_power = result['charge_profile']
|
|
|
|
|
|
discharge_power = [-x for x in result['discharge_profile']]
|
|
|
|
|
|
|
|
|
|
|
|
ax2.bar(hours, charge_power, color='green', alpha=0.7, label='充电')
|
|
|
|
|
|
ax2.bar(hours, discharge_power, color='red', alpha=0.7, label='放电')
|
|
|
|
|
|
ax2.set_xlabel('时间 (小时)')
|
|
|
|
|
|
ax2.set_ylabel('功率 (MW)')
|
|
|
|
|
|
ax2.set_title('储能充放电功率')
|
|
|
|
|
|
ax2.legend()
|
|
|
|
|
|
ax2.grid(True, alpha=0.3)
|
|
|
|
|
|
ax2.axhline(y=0, color='black', linestyle='-', linewidth=0.5)
|
|
|
|
|
|
|
|
|
|
|
|
# === 储能状态 ===
|
|
|
|
|
|
ax3 = fig.add_subplot(gs[1, 1])
|
|
|
|
|
|
storage_soc = result['storage_profile']
|
|
|
|
|
|
|
|
|
|
|
|
ax3.plot(hours, storage_soc, 'b-', linewidth=2, marker='o')
|
|
|
|
|
|
ax3.fill_between(hours, 0, storage_soc, alpha=0.3, color='blue')
|
|
|
|
|
|
ax3.set_xlabel('时间 (小时)')
|
|
|
|
|
|
ax3.set_ylabel('储能容量 (MWh)')
|
|
|
|
|
|
ax3.set_title(f'储能状态 (容量: {result["required_storage_capacity"]:.1f} MWh)')
|
|
|
|
|
|
ax3.grid(True, alpha=0.3)
|
|
|
|
|
|
ax3.set_ylim(bottom=0)
|
|
|
|
|
|
|
|
|
|
|
|
# === 弃风弃光 ===
|
|
|
|
|
|
ax4 = fig.add_subplot(gs[1, 2])
|
|
|
|
|
|
curtailed_wind = result['curtailed_wind']
|
|
|
|
|
|
curtailed_solar = result['curtailed_solar']
|
|
|
|
|
|
|
|
|
|
|
|
ax4.bar(hours, curtailed_wind, color='lightblue', alpha=0.7, label='弃风')
|
|
|
|
|
|
ax4.bar(hours, curtailed_solar, color='yellow', alpha=0.7, label='弃光')
|
|
|
|
|
|
ax4.set_xlabel('时间 (小时)')
|
|
|
|
|
|
ax4.set_ylabel('功率 (MW)')
|
|
|
|
|
|
ax4.set_title('弃风弃光功率')
|
|
|
|
|
|
ax4.legend()
|
|
|
|
|
|
ax4.grid(True, alpha=0.3)
|
|
|
|
|
|
|
|
|
|
|
|
# === 能量饼图 ===
|
|
|
|
|
|
ax5 = fig.add_subplot(gs[2, 0])
|
|
|
|
|
|
|
|
|
|
|
|
# 计算总能量
|
|
|
|
|
|
total_gen = sum(thermal_output) + sum(wind_output) + sum(solar_output)
|
|
|
|
|
|
total_load = sum(load_demand)
|
|
|
|
|
|
total_curtailed = sum(curtailed_wind) + sum(curtailed_solar)
|
|
|
|
|
|
total_grid = sum(result['grid_feed_in'])
|
|
|
|
|
|
|
2025-12-26 00:28:07 +08:00
|
|
|
|
# 处理上网电量为负的情况(购电)
|
|
|
|
|
|
if total_grid >= 0:
|
|
|
|
|
|
# 有上网电量
|
|
|
|
|
|
energy_data = [total_load, total_curtailed, total_grid]
|
|
|
|
|
|
energy_labels = [f'负荷\n({total_load:.1f} MWh)',
|
|
|
|
|
|
f'弃风弃光\n({total_curtailed:.1f} MWh)',
|
|
|
|
|
|
f'上网电量\n({total_grid:.1f} MWh)']
|
|
|
|
|
|
colors = ['red', 'orange', 'green']
|
|
|
|
|
|
ax5.pie(energy_data, labels=energy_labels, colors=colors, autopct='%1.1f%%', startangle=90)
|
|
|
|
|
|
ax5.set_title('能量分配')
|
|
|
|
|
|
else:
|
|
|
|
|
|
# 从电网购电
|
|
|
|
|
|
grid_purchase = -total_grid # 转为正值
|
|
|
|
|
|
energy_data = [total_load, total_curtailed, grid_purchase]
|
|
|
|
|
|
energy_labels = [f'负荷\n({total_load:.1f} MWh)',
|
|
|
|
|
|
f'弃风弃光\n({total_curtailed:.1f} MWh)',
|
|
|
|
|
|
f'购电量\n({grid_purchase:.1f} MWh)']
|
|
|
|
|
|
colors = ['red', 'orange', 'blue'] # 购电用蓝色
|
|
|
|
|
|
ax5.pie(energy_data, labels=energy_labels, colors=colors, autopct='%1.1f%%', startangle=90)
|
|
|
|
|
|
ax5.set_title('能量分配(含购电)')
|
2025-12-25 18:06:12 +08:00
|
|
|
|
|
|
|
|
|
|
# === 发电构成饼图 ===
|
|
|
|
|
|
ax6 = fig.add_subplot(gs[2, 1])
|
|
|
|
|
|
|
|
|
|
|
|
gen_data = [sum(thermal_output), sum(wind_output), sum(solar_output)]
|
|
|
|
|
|
gen_labels = [f'火电\n({gen_data[0]:.1f} MWh)',
|
|
|
|
|
|
f'风电\n({gen_data[1]:.1f} MWh)',
|
|
|
|
|
|
f'光伏\n({gen_data[2]:.1f} MWh)']
|
|
|
|
|
|
gen_colors = ['blue', 'green', 'orange']
|
|
|
|
|
|
|
|
|
|
|
|
ax6.pie(gen_data, labels=gen_labels, colors=gen_colors, autopct='%1.1f%%', startangle=90)
|
|
|
|
|
|
ax6.set_title('发电构成')
|
|
|
|
|
|
|
|
|
|
|
|
# === 关键指标文本 ===
|
|
|
|
|
|
ax7 = fig.add_subplot(gs[2, 2])
|
|
|
|
|
|
ax7.axis('off')
|
|
|
|
|
|
|
|
|
|
|
|
# 显示关键指标
|
|
|
|
|
|
metrics_text = f"""
|
|
|
|
|
|
关键指标
|
|
|
|
|
|
─────────────
|
|
|
|
|
|
所需储能容量: {result['required_storage_capacity']:.1f} MWh
|
|
|
|
|
|
储能效率: {params.storage_efficiency:.1%}
|
|
|
|
|
|
|
|
|
|
|
|
弃风率: {result['total_curtailment_wind_ratio']:.1%}
|
|
|
|
|
|
弃光率: {result['total_curtailment_solar_ratio']:.1%}
|
|
|
|
|
|
上网电量比例: {result['total_grid_feed_in_ratio']:.1%}
|
|
|
|
|
|
|
2025-12-26 00:28:07 +08:00
|
|
|
|
能量平衡: {'通过' if result['energy_balance_check'] else '未通过'}
|
2025-12-25 18:06:12 +08:00
|
|
|
|
|
|
|
|
|
|
最大储能状态: {max(storage_soc):.1f} MWh
|
|
|
|
|
|
最小储能状态: {min(storage_soc):.1f} MWh
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
ax7.text(0.1, 0.5, metrics_text, fontsize=11, verticalalignment='center',
|
2025-12-25 19:56:21 +08:00
|
|
|
|
fontfamily='SimHei', bbox=dict(boxstyle='round', facecolor='lightgray', alpha=0.8))
|
2025-12-25 18:06:12 +08:00
|
|
|
|
|
|
|
|
|
|
# 保存图片
|
|
|
|
|
|
plt.savefig('comprehensive_analysis.png', dpi=300, bbox_inches='tight')
|
|
|
|
|
|
plt.close()
|
|
|
|
|
|
|
|
|
|
|
|
print("综合分析图表已保存为 'comprehensive_analysis.png'")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def create_time_series_plot(solar_output, wind_output, thermal_output, load_demand, result):
|
|
|
|
|
|
"""
|
|
|
|
|
|
创建时间序列图表,模拟真实的时间轴
|
|
|
|
|
|
"""
|
|
|
|
|
|
# 创建时间轴
|
|
|
|
|
|
base_time = datetime(2025, 1, 1, 0, 0, 0)
|
|
|
|
|
|
times = [base_time + timedelta(hours=i) for i in range(24)]
|
|
|
|
|
|
|
|
|
|
|
|
fig, ax = plt.subplots(figsize=(14, 8))
|
|
|
|
|
|
|
|
|
|
|
|
# 绘制发电和负荷
|
|
|
|
|
|
ax.plot(times, load_demand, 'r-', linewidth=3, label='负荷需求')
|
|
|
|
|
|
ax.plot(times, thermal_output, 'b-', linewidth=2, label='火电出力')
|
|
|
|
|
|
ax.plot(times, wind_output, 'g-', linewidth=2, label='风电出力')
|
|
|
|
|
|
ax.plot(times, solar_output, 'orange', linewidth=2, label='光伏出力')
|
|
|
|
|
|
|
|
|
|
|
|
# 计算总发电量
|
|
|
|
|
|
total_generation = [thermal_output[i] + wind_output[i] + solar_output[i] for i in range(24)]
|
|
|
|
|
|
ax.plot(times, total_generation, 'k--', linewidth=1.5, alpha=0.7, label='总发电量')
|
|
|
|
|
|
|
|
|
|
|
|
# 设置时间轴格式
|
|
|
|
|
|
ax.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M'))
|
|
|
|
|
|
ax.xaxis.set_major_locator(mdates.HourLocator(interval=2))
|
|
|
|
|
|
|
|
|
|
|
|
ax.set_xlabel('时间')
|
|
|
|
|
|
ax.set_ylabel('功率 (MW)')
|
|
|
|
|
|
ax.set_title('多能互补系统24小时发电曲线 (时间序列)')
|
|
|
|
|
|
ax.legend(loc='upper right')
|
|
|
|
|
|
ax.grid(True, alpha=0.3)
|
|
|
|
|
|
|
|
|
|
|
|
# 旋转时间标签
|
|
|
|
|
|
plt.setp(ax.xaxis.get_majorticklabels(), rotation=45)
|
|
|
|
|
|
|
|
|
|
|
|
plt.tight_layout()
|
|
|
|
|
|
plt.savefig('time_series_curves.png', dpi=300, bbox_inches='tight')
|
|
|
|
|
|
plt.close()
|
|
|
|
|
|
|
|
|
|
|
|
print("时间序列图表已保存为 'time_series_curves.png'")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
|
|
"""主函数"""
|
|
|
|
|
|
# 示例数据
|
|
|
|
|
|
solar_output = [0.0] * 6 + [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0, 0.0] + [0.0] * 6
|
|
|
|
|
|
wind_output = [2.0, 3.0, 4.0, 3.0, 2.0, 1.0] * 4
|
|
|
|
|
|
thermal_output = [5.0] * 24
|
|
|
|
|
|
load_demand = [3.0, 4.0, 5.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0, 18.0, 20.0, 18.0,
|
|
|
|
|
|
16.0, 14.0, 12.0, 10.0, 8.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0, 2.0]
|
|
|
|
|
|
|
|
|
|
|
|
# 系统参数
|
|
|
|
|
|
params = SystemParameters(
|
|
|
|
|
|
max_curtailment_wind=0.1,
|
|
|
|
|
|
max_curtailment_solar=0.1,
|
|
|
|
|
|
max_grid_ratio=0.2,
|
|
|
|
|
|
storage_efficiency=0.9,
|
|
|
|
|
|
discharge_rate=1.0,
|
|
|
|
|
|
charge_rate=1.0
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# 计算最优储能容量
|
|
|
|
|
|
print("正在计算最优储能容量...")
|
|
|
|
|
|
result = optimize_storage_capacity(
|
|
|
|
|
|
solar_output, wind_output, thermal_output, load_demand, params
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
print("\n=== 优化结果 ===")
|
|
|
|
|
|
print(f"所需储能总容量: {result['required_storage_capacity']:.2f} MWh")
|
|
|
|
|
|
print(f"弃风率: {result['total_curtailment_wind_ratio']:.3f}")
|
|
|
|
|
|
print(f"弃光率: {result['total_curtailment_solar_ratio']:.3f}")
|
|
|
|
|
|
print(f"上网电量比例: {result['total_grid_feed_in_ratio']:.3f}")
|
|
|
|
|
|
print(f"能量平衡校验: {'通过' if result['energy_balance_check'] else '未通过'}")
|
|
|
|
|
|
|
|
|
|
|
|
# 创建各种图表
|
|
|
|
|
|
print("\n正在生成可视化图表...")
|
|
|
|
|
|
|
|
|
|
|
|
# 1. 基础曲线图(已在main.py中实现)
|
|
|
|
|
|
print("1. 基础系统运行曲线图")
|
|
|
|
|
|
|
|
|
|
|
|
# 2. 综合分析图
|
|
|
|
|
|
print("2. 综合分析图表")
|
|
|
|
|
|
create_comprehensive_plot(solar_output, wind_output, thermal_output, load_demand, result, params)
|
|
|
|
|
|
|
|
|
|
|
|
# 3. 时间序列图
|
|
|
|
|
|
print("3. 时间序列图表")
|
|
|
|
|
|
create_time_series_plot(solar_output, wind_output, thermal_output, load_demand, result)
|
|
|
|
|
|
|
|
|
|
|
|
print("\n=== 所有图表生成完成 ===")
|
|
|
|
|
|
print("生成的文件:")
|
|
|
|
|
|
print("- system_curves.png: 基础系统运行曲线")
|
|
|
|
|
|
print("- comprehensive_analysis.png: 综合分析图表")
|
|
|
|
|
|
print("- time_series_curves.png: 时间序列图表")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
|
main()
|