diff --git a/main.py b/main.py index 573325d..861198c 100644 --- a/main.py +++ b/main.py @@ -11,6 +11,8 @@ import matplotlib matplotlib.use('TkAgg') # 设置为TkAgg后端以支持图形窗口显示 import matplotlib.pyplot as plt import numpy as np +import pandas as pd +from datetime import datetime from storage_optimization import optimize_storage_capacity, SystemParameters from excel_reader import read_excel_data, create_excel_template, analyze_excel_data @@ -54,6 +56,7 @@ def plot_system_curves(solar_output, wind_output, thermal_output, load_demand, r sampled_storage = result['storage_profile'][::sample_rate] sampled_charge = result['charge_profile'][::sample_rate] sampled_discharge = result['discharge_profile'][::sample_rate] + sampled_grid_feed_in = result['grid_feed_in'][::sample_rate] else: title_suffix = " (24小时)" sampled_hours = hours @@ -64,9 +67,10 @@ def plot_system_curves(solar_output, wind_output, thermal_output, load_demand, r sampled_storage = result['storage_profile'] sampled_charge = result['charge_profile'] sampled_discharge = result['discharge_profile'] + sampled_grid_feed_in = result['grid_feed_in'] - # 创建图形 - fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(14, 12)) + # 创建图形(4个子图) + fig, (ax1, ax2, ax3, ax4) = plt.subplots(4, 1, figsize=(14, 16)) fig.suptitle('多能互补系统24小时运行曲线', fontsize=16, fontweight='bold') # === 第一个子图:发电和负荷曲线 === @@ -111,6 +115,31 @@ def plot_system_curves(solar_output, wind_output, thermal_output, load_demand, r ax3.set_xlim(0, max(sampled_hours)) ax3.set_ylim(bottom=0) + # === 第四个子图:购电量和上网电量曲线 === + # 直接使用原始数据:正值表示上网电量,负值表示购电量 + grid_power = sampled_grid_feed_in + + # 绘制电网交互电量(正值上网,负值购电) + colors = ['brown' if x >= 0 else 'purple' for x in grid_power] + ax4.bar(sampled_hours, grid_power, color=colors, alpha=0.7, label='电网交互电量') + + # 添加零线 + ax4.axhline(y=0, color='black', linestyle='-', linewidth=0.5) + + # 添加图例说明 + from matplotlib.patches import Patch + legend_elements = [ + Patch(facecolor='brown', alpha=0.7, label='上网电量 (+)'), + Patch(facecolor='purple', alpha=0.7, label='购电量 (-)') + ] + ax4.legend(handles=legend_elements, loc='upper right') + + ax4.set_xlabel('时间 (小时)') + ax4.set_ylabel('功率 (MW)') + ax4.set_title(f'电网交互电量{title_suffix} (正值:上网, 负值:购电)') + ax4.grid(True, alpha=0.3) + ax4.set_xlim(0, max(sampled_hours)) + # 调整布局 plt.tight_layout() @@ -145,6 +174,173 @@ def plot_system_curves(solar_output, wind_output, thermal_output, load_demand, r 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}") + + # 计算购电和上网电量统计 + total_grid_feed_in = sum(result['grid_feed_in']) + total_grid_purchase = sum(-x for x in result['grid_feed_in'] if x < 0) # 购电量 + total_grid_feed_out = sum(x for x in result['grid_feed_in'] if x > 0) # 上网电量 + + print(f"\n=== 电网交互统计 ===") + if total_grid_feed_in >= 0: + print(f"净上网电量: {total_grid_feed_in:.2f} MWh") + else: + print(f"净购电量: {-total_grid_feed_in:.2f} MWh") + print(f"总购电量: {total_grid_purchase:.2f} MWh") + print(f"总上网电量: {total_grid_feed_out:.2f} MWh") + + +def export_results_to_excel(solar_output, wind_output, thermal_output, load_demand, result, params, filename=None): + """ + 将优化结果导出到Excel文件 + + Args: + solar_output: 光伏出力曲线 (MW) + wind_output: 风电出力曲线 (MW) + thermal_output: 火电出力曲线 (MW) + load_demand: 负荷曲线 (MW) + result: 优化结果字典 + params: 系统参数 + filename: 输出文件名,如果为None则自动生成 + """ + if filename is None: + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + filename = f"storage_optimization_results_{timestamp}.xlsx" + + print(f"\n正在导出结果到Excel文件: {filename}") + + # 准备数据 + hours = list(range(1, len(solar_output) + 1)) + + # 分离购电和上网电量 + grid_purchase = [] + grid_feed_out = [] + + for power in result['grid_feed_in']: + if power < 0: + grid_purchase.append(-power) # 购电,转换为正值 + grid_feed_out.append(0) + else: + grid_purchase.append(0) + grid_feed_out.append(power) # 上网电量 + + # 创建主要数据DataFrame + data_df = pd.DataFrame({ + '小时': hours, + '光伏出力(MW)': solar_output, + '风电出力(MW)': wind_output, + '火电出力(MW)': thermal_output, + '总发电量(MW)': [solar_output[i] + wind_output[i] + thermal_output[i] for i in range(len(solar_output))], + '负荷需求(MW)': load_demand, + '储能充电(MW)': result['charge_profile'], + '储能放电(MW)': result['discharge_profile'], + '储能状态(MWh)': result['storage_profile'], + '弃风量(MW)': result['curtailed_wind'], + '弃光量(MW)': result['curtailed_solar'], + '购电量(MW)': grid_purchase, + '上网电量(MW)': grid_feed_out + }) + + # 创建统计信息DataFrame + total_grid_feed_in = sum(result['grid_feed_in']) + total_grid_purchase = sum(-x for x in result['grid_feed_in'] if x < 0) # 购电量 + total_grid_feed_out = sum(x for x in result['grid_feed_in'] if x > 0) # 上网电量 + + stats_df = pd.DataFrame({ + '指标': [ + '所需储能总容量', + '最大储能状态', + '最小储能状态', + '总充电量', + '总放电量', + '弃风率', + '弃光率', + '上网电量比例', + '能量平衡校验', + '净购电量/净上网电量', + '总购电量', + '总上网电量', + '容量限制是否达到' + ], + '数值': [ + f"{result['required_storage_capacity']:.2f} MWh", + f"{max(result['storage_profile']):.2f} MWh", + f"{min(result['storage_profile']):.2f} MWh", + f"{sum(result['charge_profile']):.2f} MWh", + f"{sum(result['discharge_profile']):.2f} MWh", + f"{result['total_curtailment_wind_ratio']:.3f}", + f"{result['total_curtailment_solar_ratio']:.3f}", + f"{result['total_grid_feed_in_ratio']:.3f}", + "通过" if result['energy_balance_check'] else "未通过", + f"{-total_grid_feed_in:.2f} MWh" if total_grid_feed_in < 0 else f"{total_grid_feed_in:.2f} MWh", + f"{total_grid_purchase:.2f} MWh", + f"{total_grid_feed_out:.2f} MWh", + "是" if result['capacity_limit_reached'] else "否" + ] + }) + + # 创建系统参数DataFrame + params_df = pd.DataFrame({ + '参数名称': [ + '最大弃风率', + '最大弃光率', + '最大上网电量比例', + '储能效率', + '放电倍率', + '充电倍率', + '最大储能容量' + ], + '参数值': [ + params.max_curtailment_wind, + params.max_curtailment_solar, + params.max_grid_ratio, + params.storage_efficiency, + params.discharge_rate, + params.charge_rate, + params.max_storage_capacity if params.max_storage_capacity is not None else "无限制" + ], + '单位': [ + "比例", + "比例", + "比例", + "效率", + "C-rate", + "C-rate", + "MWh" + ] + }) + + # 写入Excel文件 + with pd.ExcelWriter(filename, engine='openpyxl') as writer: + # 写入主要数据 + data_df.to_excel(writer, sheet_name='运行数据', index=False) + + # 写入统计信息 + stats_df.to_excel(writer, sheet_name='统计结果', index=False) + + # 写入系统参数 + params_df.to_excel(writer, sheet_name='系统参数', index=False) + + # 创建说明工作表 + description_df = pd.DataFrame({ + '项目': [ + '文件说明', + '生成时间', + '数据长度', + '数据内容', + '注意事项' + ], + '内容': [ + '多能互补系统储能容量优化结果', + datetime.now().strftime("%Y-%m-%d %H:%M:%S"), + f"{len(solar_output)} 小时", + '包含发电、负荷、储能、弃风弃光、购电上网等完整数据', + '负值表示购电,正值表示上网电量' + ] + }) + description_df.to_excel(writer, sheet_name='说明', index=False) + + print(f"结果已成功导出到: {filename}") + return filename def generate_yearly_data(): @@ -313,6 +509,12 @@ def main(): print("正在绘制系统运行曲线...") plot_system_curves(solar_output, wind_output, thermal_output, load_demand, result, show_window, display_only) + # 导出结果到Excel + try: + export_results_to_excel(solar_output, wind_output, thermal_output, load_demand, result, params) + except Exception as e: + print(f"导出Excel文件失败:{str(e)}") + if display_only: print("\n正在显示图形窗口...") elif show_window: