From 7ce065f2df1151ad8621948a895a07bbfccb103a Mon Sep 17 00:00:00 2001 From: dmy Date: Sat, 27 Dec 2025 17:30:40 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8C=89=E6=AF=8F1MW=E5=8E=BB=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E7=A8=8B=E5=BA=8F=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- advanced_visualization.py | 270 ------------ docs/PERIODIC_BALANCE_UPDATE.md | 225 ++++++++++ docs/PROJECT_STRUCTURE.md | 136 ++++++ README.md => docs/README.md | 238 ++++++++++- docs/README_exe.md | 86 ++++ economic_optimization.py | 709 -------------------------------- example_usage.py | 529 ------------------------ solar_optimization.py | 362 ---------------- solar_optimization_examples.py | 537 ------------------------ solar_scenarios_demo.py | 414 ------------------- src/storage_optimization.py | 111 +++-- storage_optimization.py | 571 ------------------------- test_storage_optimization.py | 362 ---------------- 13 files changed, 719 insertions(+), 3831 deletions(-) delete mode 100644 advanced_visualization.py create mode 100644 docs/PERIODIC_BALANCE_UPDATE.md create mode 100644 docs/PROJECT_STRUCTURE.md rename README.md => docs/README.md (60%) create mode 100644 docs/README_exe.md delete mode 100644 economic_optimization.py delete mode 100644 example_usage.py delete mode 100644 solar_optimization.py delete mode 100644 solar_optimization_examples.py delete mode 100644 solar_scenarios_demo.py delete mode 100644 storage_optimization.py delete mode 100644 test_storage_optimization.py diff --git a/advanced_visualization.py b/advanced_visualization.py deleted file mode 100644 index 3d2cb15..0000000 --- a/advanced_visualization.py +++ /dev/null @@ -1,270 +0,0 @@ -""" -高级可视化程序 - 多能互补系统储能容量优化 - -该程序提供更丰富的可视化功能,包括多种图表类型和交互式选项。 - -作者: 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']) - - # 处理上网电量为负的情况(购电) - 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('能量分配(含购电)') - - # === 发电构成饼图 === - 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%} - - 能量平衡: {'通过' if result['energy_balance_check'] else '未通过'} - - 最大储能状态: {max(storage_soc):.1f} MWh - 最小储能状态: {min(storage_soc):.1f} MWh - """ - - ax7.text(0.1, 0.5, metrics_text, fontsize=11, verticalalignment='center', - fontfamily='SimHei', bbox=dict(boxstyle='round', facecolor='lightgray', alpha=0.8)) - - # 保存图片 - 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() diff --git a/docs/PERIODIC_BALANCE_UPDATE.md b/docs/PERIODIC_BALANCE_UPDATE.md new file mode 100644 index 0000000..a4aea4f --- /dev/null +++ b/docs/PERIODIC_BALANCE_UPDATE.md @@ -0,0 +1,225 @@ +# 周期性平衡功能更新说明 + +## 问题描述 + +在计算8760小时全年数据时,原有的代码将储能初始状态(SOC)固定为0,导致经过一个完整的8760小时循环后,储能无法恢复到初始状态。这意味着系统在一个周期结束后,储能状态发生了变化,不符合实际运行场景中的周期性稳定要求。 + +## 解决方案 + +实现了迭代收敛算法,通过以下步骤找到满足周期性平衡的储能初始状态: + +1. **步骤一**:从初始SOC=0开始,运行一次全年仿真,记录最后一小时的SOC值 +2. **步骤二**:将这个SOC值作为新的"初始SOC",再次运行仿真 +3. **步骤三**:重复上述过程,直到首尾SOC的差值小于设定的阈值(默认为储能容量的0.1%) + +## 代码修改 + +### 1. `calculate_energy_balance` 函数 + +**修改内容**:添加了 `initial_soc` 参数,允许指定初始储能状态。 + +```python +def calculate_energy_balance( + solar_output: List[float], + wind_output: List[float], + thermal_output: List[float], + load_demand: List[float], + params: SystemParameters, + storage_capacity: float, + initial_soc: float = 0.0 # 新增参数 +) -> Dict[str, List[float]]: +``` + +**关键变化**: +- 将初始储能状态从固定的0改为可配置的 `initial_soc` 参数 +- 在计算开始时设置 `storage_soc[0] = initial_soc` + +### 2. `find_periodic_steady_state` 函数(新增) + +**功能**:通过迭代收敛找到满足周期性平衡的储能初始状态。 + +```python +def find_periodic_steady_state( + solar_output: List[float], + wind_output: List[float], + thermal_output: List[float], + load_demand: List[float], + params: SystemParameters, + storage_capacity: float, + soc_convergence_threshold: float = 0.001, # SOC收敛阈值(0.1%) + max_iterations: int = 100 +) -> Dict[str, List[float]]: +``` + +**算法逻辑**: +``` +initial_soc = 0.0 +while iteration < max_iterations: + 运行仿真,得到 balance_result + storage_final = balance_result['storage_profile'][-1] + soc_diff = abs(storage_final - initial_soc) + + if soc_diff < soc_convergence_threshold: + 收敛成功,退出循环 + + initial_soc = storage_final + iteration += 1 +``` + +**输出信息**: +``` +正在寻找周期性平衡状态(SOC收敛阈值: 0.5000 MWh)... + 迭代 1: 初始SOC=0.0000 MWh, 最终SOC=21.7157 MWh, 差值=21.7157 MWh + 迭代 2: 初始SOC=21.7157 MWh, 最终SOC=21.7157 MWh, 差值=0.0000 MWh +✓ 周期性平衡收敛成功(迭代2次,SOC差值=0.0000 MWh) +``` + +### 3. `optimize_storage_capacity` 函数 + +**修改内容**:根据数据长度自动选择是否使用周期性平衡。 + +```python +# 判断数据类型(24小时或8760小时) +data_length = len(solar_output) +is_yearly_data = data_length == 8760 + +if is_yearly_data: + print(f"处理8760小时全年数据,启用周期性平衡优化...") + +# 二分搜索中 +if is_yearly_data: + balance_result = find_periodic_steady_state(...) +else: + balance_result = calculate_energy_balance(...) +``` + +**关键变化**: +- 自动识别数据类型(24小时或8760小时) +- 对于8760小时数据,使用周期性平衡函数 +- 对于24小时数据,保持原有逻辑(不需要周期性平衡) +- 在最终输出中显示周期性平衡信息 + +## 使用示例 + +### 测试脚本 + +项目提供了测试脚本 `tests/test_periodic_balance.py`,可以验证周期性平衡功能: + +```bash +python tests\test_periodic_balance.py +``` + +### 正常使用 + +无需修改现有使用方式,周期性平衡功能会自动启用: + +```python +from src.storage_optimization import optimize_storage_capacity, SystemParameters + +# 8760小时数据 +solar_output = [...] # 长度为8760 +wind_output = [...] # 长度为8760 +thermal_output = [...] # 长度为8760 +load_demand = [...] # 长度为8760 + +params = SystemParameters(...) + +# 自动启用周期性平衡 +result = optimize_storage_capacity( + solar_output, wind_output, thermal_output, load_demand, params +) + +# 输出信息会显示周期性平衡状态 +print(f"初始SOC: {result['storage_profile'][0]:.4f} MWh") +print(f"最终SOC: {result['storage_profile'][-1]:.4f} MWh") +print(f"SOC差值: {abs(result['storage_profile'][-1] - result['storage_profile'][0]):.4f} MWh") +``` + +## 测试结果 + +### 24小时数据测试 + +``` +============================================================ +测试24小时数据(不需要周期性平衡) +============================================================ + +=== 24小时优化结果 === +所需储能总容量: 217.00 MWh +初始SOC: 0.0000 MWh +最终SOC: 21.6000 MWh +SOC差值: 21.6000 MWh +实际弃风率: 0.000 +实际弃光率: 0.000 +实际上网电量比例: -0.145 +能量平衡校验: 通过 +``` + +**说明**:24小时数据不需要周期性平衡,SOC差值可以不为0。 + +### 8760小时数据测试 + +``` +============================================================ +测试8760小时数据(需要周期性平衡) +============================================================ +处理8760小时全年数据,启用周期性平衡优化... +正在寻找周期性平衡状态(SOC收敛阈值: 0.5000 MWh)... + 迭代 1: 初始SOC=0.0000 MWh, 最终SOC=21.7157 MWh, 差值=21.7157 MWh + 迭代 2: 初始SOC=21.7157 MWh, 最终SOC=21.7157 MWh, 差值=0.0000 MWh +✓ 周期性平衡收敛成功(迭代2次,SOC差值=0.0000 MWh) + +=== 8760小时优化结果 === +所需储能总容量: 28.31 MWh +初始SOC: 21.7157 MWh +最终SOC: 21.7157 MWh +SOC差值: 0.0000 MWh +实际弃风率: 0.100 +实际弃光率: 0.072 +实际上网电量比例: -0.141 +能量平衡校验: 通过 + +✓ 周期性平衡验证通过 + SOC差值: 0.0000 MWh < 阈值: 0.0283 MWh +``` + +**说明**:8760小时数据自动启用周期性平衡,SOC差值为0,满足周期性平衡要求。 + +## 关键特性 + +1. **自动识别**:根据数据长度自动选择是否启用周期性平衡 +2. **快速收敛**:通常只需要2-3次迭代即可收敛 +3. **可配置阈值**:默认SOC收敛阈值为容量的0.1%,可以根据需要调整 +4. **向后兼容**:不影响现有24小时数据的处理逻辑 +5. **详细输出**:提供迭代过程的详细信息,便于调试和验证 + +## 参数说明 + +### `soc_convergence_threshold` + +SOC收敛阈值(相对于储能容量的比例),默认值为0.001(0.1%)。 + +- **类型**:float +- **默认值**:0.001 +- **取值范围**:> 0 +- **说明**:当初始SOC和最终SOC的差值小于 `storage_capacity * soc_convergence_threshold` 时,认为已达到周期性平衡 + +### `max_iterations` + +最大迭代次数,默认值为100。 + +- **类型**:int +- **默认值**:100 +- **取值范围**:> 0 +- **说明**:防止在极端情况下无限循环 + +## 注意事项 + +1. **收敛性**:在大多数情况下,算法会在2-5次迭代内收敛 +2. **性能影响**:8760小时数据的计算时间会增加,但影响有限(每次迭代约增加0.1-0.5秒) +3. **内存使用**:与原算法相同,没有额外内存开销 +4. **兼容性**:完全向后兼容,不影响24小时数据的处理 + +## 总结 + +本次更新成功解决了8760小时全年数据计算时储能周期性不平衡的问题,通过迭代收敛算法自动找到满足周期性平衡的初始SOC状态,确保系统在完整周期结束后储能状态能够恢复到初始值,符合实际运行场景的要求。 diff --git a/docs/PROJECT_STRUCTURE.md b/docs/PROJECT_STRUCTURE.md new file mode 100644 index 0000000..f74880b --- /dev/null +++ b/docs/PROJECT_STRUCTURE.md @@ -0,0 +1,136 @@ +# 项目结构说明 + +## 目录结构 + +``` +D:\code\storage\ +├── src/ # 核心源代码 +│ ├── storage_optimization.py # 储能容量优化核心模块 +│ ├── excel_reader.py # Excel数据读取模块 +│ ├── economic_optimization.py # 经济优化模块 +│ ├── solar_optimization.py # 太阳能优化模块 +│ └── advanced_visualization.py # 高级可视化模块 +│ +├── tests/ # 测试文件 +│ ├── test_*.py # 所有Python测试文件 +│ ├── test_*.xlsx # 测试Excel文件 +│ ├── extreme_*.xlsx # 极端场景测试文件 +│ └── test_zero_grid_simple.py # 简化版测试文件 +│ +├── config/ # 配置文件 +│ ├── pyproject.toml # 项目配置 +│ ├── requirements.txt # 依赖配置 +│ ├── uv.lock # uv锁定文件 +│ └── main.spec # PyInstaller配置 +│ +├── docs/ # 文档文件 +│ ├── README.md # 项目主文档 +│ └── README_exe.md # 可执行文件说明 +│ +├── scripts/ # 示例和演示脚本 +│ ├── example_usage.py # 使用示例 +│ ├── solar_optimization_examples.py # 太阳能优化示例 +│ └── solar_scenarios_demo.py # 太阳能场景演示 +│ +├── images/ # 图片文件 +│ ├── *.png # 所有生成的图表和可视化图片 +│ +├── templates/ # Excel模板文件 +│ ├── data_template_*.xlsx # 数据模板 +│ ├── economic_data_template_*.xlsx # 经济优化模板 +│ └── data_template_*-*.xlsx # 临时和修改的模板 +│ +├── results/ # 优化结果文件 +│ └── storage_optimization_results_*.xlsx # 储能优化结果 +│ +├── reports/ # 报告文件 +│ └── economic_optimization_report_*.xlsx # 经济优化报告 +│ +├── build/ # 构建输出 +├── dist/ # 分发文件 +├── __pycache__/ # Python缓存 +├── .mypy_cache/ # MyPy缓存 +├── .vscode/ # VS Code配置 +├── .gitignore # Git忽略文件 +├── .python-version # Python版本 +└── main.py # 主程序入口 +``` + +## 文件分类说明 + +### 📁 **src/** - 核心源代码 +- **storage_optimization.py**: 储能容量优化的核心算法 +- **excel_reader.py**: Excel文件读取和数据验证 +- **economic_optimization.py**: 经济指标优化模块 +- **solar_optimization.py**: 太阳能系统优化 +- **advanced_visualization.py**: 高级数据可视化 + +### 📁 **tests/** - 测试文件 +- 所有 `test_*.py` 文件:单元测试和集成测试 +- 所有 `test_*.xlsx` 文件:测试用Excel数据 +- `extreme_*.xlsx`:极端场景测试数据 + +### 📁 **config/** - 配置文件 +- **pyproject.toml**: 项目元数据和依赖管理 +- **requirements.txt**: Python依赖包列表 +- **uv.lock**: 依赖版本锁定文件 +- **main.spec**: PyInstaller打包配置 + +### 📁 **docs/** - 文档 +- **README.md**: 项目主文档 +- **README_exe.md**: 可执行文件使用说明 + +### 📁 **scripts/** - 示例和演示 +- **example_usage.py**: 基本使用示例 +- **solar_optimization_examples.py**: 太阳能优化示例 +- **solar_scenarios_demo.py**: 太阳能场景演示 + +### 📁 **images/** - 图片文件 +- 所有 `.png` 文件:系统生成的图表和可视化结果 + +### 📁 **templates/** - Excel模板 +- **data_template_*.xlsx**: 基础数据模板 +- **economic_data_template_*.xlsx**: 经济优化模板 +- 临时和修改的模板文件 + +### 📁 **results/** - 优化结果 +- **storage_optimization_results_*.xlsx**: 储能优化计算结果 + +### 📁 **reports/** - 报告文件 +- **economic_optimization_report_*.xlsx**: 经济优化分析报告 + +## 使用说明 + +### 运行主程序 +```bash +uv run python main.py --excel templates/data_template_8760.xlsx +``` + +### 运行测试 +```bash +uv run python tests/test_excel_data.py +``` + +### 运行示例 +```bash +uv run python scripts/example_usage.py +``` + +### 创建Excel模板 +```bash +uv run python src/excel_reader.py --economic +``` + +## 注意事项 + +1. **导入路径**: 由于文件移动,可能需要调整Python导入路径 +2. **相对路径**: 某些脚本中的文件路径可能需要更新 +3. **配置文件**: pyproject.toml等配置文件位置已改变 +4. **可执行文件**: 如果使用了打包的exe,需要检查路径引用 + +## 维护建议 + +- 定期清理 `results/` 和 `reports/` 中的旧文件 +- 保持 `templates/` 中的模板文件更新 +- 及时更新 `config/` 中的依赖配置 +- 维护 `docs/` 中的文档与代码同步 \ No newline at end of file diff --git a/README.md b/docs/README.md similarity index 60% rename from README.md rename to docs/README.md index c30181b..88ae997 100644 --- a/README.md +++ b/docs/README.md @@ -4,19 +4,24 @@ 本项目是一个Python算法程序,专注于计算多能互补系统中所需的最优储能容量。程序能够确保系统在24小时或8760小时(全年)时间尺度内电能平衡,同时满足用户定义的弃风弃光率和上网电量比例约束。 +**新增功能**:经济优化模块,在光伏、风电、负荷确定的前提下,进行储能配置优化,目标函数是光伏建设费用、风电建设费用、储能建设费用、购电费用最小。 + ## 核心目标 - 计算24小时或8760小时多能互补系统中的最优储能容量 - 确保系统电能平衡,满足所有约束条件 - 提供详细的储能运行曲线和统计信息 - 支持可视化分析 +- **经济优化**:最小化总建设成本和购电费用,计算最优储能配置 ## 主要技术栈 - **编程语言**: Python -- **核心库**: NumPy, SciPy, matplotlib -- **算法类型**: 迭代优化算法(二分搜索) +- **核心库**: NumPy, SciPy, matplotlib, pandas, openpyxl +- **算法类型**: 迭代优化算法(二分搜索)、网格搜索算法 - **可视化**: matplotlib绘图库 +- **数据处理**: Excel文件读写和数据处理 +- **经济分析**: LCOE、NPV计算和经济优化 ## 核心功能 @@ -26,8 +31,14 @@ - 上网电量比例约束 - 储能充放电约束 3. **优化算法**: 计算满足所有约束的最小储能容量 -4. **结果输出**: 提供详细的储能运行曲线和统计信息 -5. **可视化功能**: 生成系统运行曲线图表 +4. **经济优化**: + - 最小化总建设成本(光伏、风电、储能) + - 最小化购电费用 + - 计算LCOE(平准化电力成本) + - 计算NPV(净现值) +5. **结果输出**: 提供详细的储能运行曲线和统计信息 +6. **可视化功能**: 生成系统运行曲线图表 +7. **Excel集成**: 支持从Excel文件读取数据和参数 ## 安装依赖 @@ -49,16 +60,27 @@ python main.py ### 3. 创建Excel模板 ```bash -python main.py --create-template 8760 # 创建8760小时模板 -python main.py --create-template 24 # 创建24小时模板 +python excel_reader.py --create-template 8760 # 创建8760小时模板 +python excel_reader.py --create-template 24 # 创建24小时模板 ``` -### 5. 高级可视化 +### 4. 创建经济优化Excel模板 +```bash +python excel_reader.py --economic # 创建包含经济参数的模板 +``` + +### 5. 经济优化分析 +```bash +python economic_optimization.py --demo # 运行演示 +python economic_optimization.py --excel # 使用Excel数据进行经济优化 +``` + +### 6. 高级可视化 ```bash python advanced_visualization.py ``` -### 6. 运行测试 +### 7. 运行测试 ```bash python test_storage_optimization.py ``` @@ -116,10 +138,11 @@ python main.py --excel my_data.xlsx python main.py --excel data_template_8760.xlsx ``` -#### Excel文件示例 +#### Excel文件结构 -**8760小时数据示例:** +完整的Excel文件包含4个工作表: +**1. 数据工作表** | 小时 | 光伏出力(MW) | 风电出力(MW) | 火电出力(MW) | 负荷需求(MW) | |------|-------------|-------------|-------------|-------------| | 1 | 0.0 | 2.1 | 5.0 | 3.2 | @@ -127,14 +150,38 @@ python main.py --excel data_template_8760.xlsx | ... | ... | ... | ... | ... | | 8760 | 0.0 | 2.0 | 5.0 | 3.5 | -**24小时数据示例:** +**2. 参数工作表(系统运行参数)** +| 参数名称 | 参数值 | 参数说明 | 取值范围 | 默认值 | +|---------|--------|---------|---------|--------| +| 最大弃风率 | 0.1 | 允许的最大弃风率(0.0-1.0) | 0.0-1.0 | 0.1 | +| 最大弃光率 | 0.1 | 允许的最大弃光率(0.0-1.0) | 0.0-1.0 | 0.1 | +| 最大上网电量比例 | 0.2 | 允许的最大上网电量比例 | ≥0.0 | 0.2 | +| 储能效率 | 0.9 | 储能充放电效率(0.0-1.0) | 0.0-1.0 | 0.9 | +| 放电倍率 | 1.0 | 储能放电倍率(C-rate,>0) | >0 | 1.0 | +| 充电倍率 | 1.0 | 储能充电倍率(C-rate,>0) | >0 | 1.0 | -| 小时 | 光伏出力(MW) | 风电出力(MW) | 火电出力(MW) | 负荷需求(MW) | -|------|-------------|-------------|-------------|-------------| -| 1 | 0.0 | 2.0 | 5.0 | 3.0 | -| 2 | 0.0 | 3.0 | 5.0 | 4.0 | -| ... | ... | ... | ... | ... | -| 24 | 0.0 | 2.0 | 5.0 | 2.0 | +**3. 经济参数工作表(经济优化参数)** +| 参数名称 | 参数值 | 参数说明 | 取值范围 | 默认值 | +|---------|--------|---------|---------|--------| +| 光伏建设成本 | 3000000 | 光伏发电系统建设成本 (元/MW) | >0 | 3,000,000 | +| 风电建设成本 | 2500000 | 风力发电系统建设成本 (元/MW) | >0 | 2,500,000 | +| 储能建设成本 | 800000 | 储能系统建设成本 (元/MWh) | >0 | 800,000 | +| 购电价格 | 600 | 从电网购电价格 (元/MWh) | >0 | 600 | +| 上网电价 | 400 | 向电网售电价格 (元/MWh) | ≥0 | 400 | +| 光伏运维成本 | 50000 | 光伏系统年度运维成本 (元/MW/年) | ≥0 | 50,000 | +| 风电运维成本 | 45000 | 风电系统年度运维成本 (元/MW/年) | ≥0 | 45,000 | +| 储能运维成本 | 3000 | 储能系统年度运维成本 (元/MW/年) | ≥0 | 3,000 | +| 项目寿命 | 25 | 项目运营寿命 (年) | >0 | 25 | +| 折现率 | 0.08 | 项目折现率 (用于NPV计算) | 0-1 | 0.08 | +| 储能容量搜索范围-最小值 | 0 | 储能容量优化搜索范围下限 (MWh) | ≥0 | 0 | +| 储能容量搜索范围-最大值 | 1000 | 储能容量优化搜索范围上限 (MWh) | >0 | 1000 | +| 充放电倍率搜索范围-最小值 | 0.1 | 充放电倍率优化搜索范围下限 | >0 | 0.1 | +| 充放电倍率搜索范围-最大值 | 2.0 | 充放电倍率优化搜索范围上限 | >0 | 2.0 | +| 最大迭代次数 | 100 | 优化算法最大迭代次数 | >0 | 100 | +| 收敛容差 | 0.01 | 优化算法收敛容差 | >0 | 0.01 | + +**4. 说明工作表** +包含使用说明和注意事项。 #### 数据处理逻辑 @@ -261,6 +308,130 @@ print(f"实际弃光率: {result['total_curtailment_solar_ratio']:.3f}") print(f"实际上网电量比例: {result['total_grid_feed_in_ratio']:.3f}") ``` +## 经济优化模块 + +### 功能概述 + +经济优化模块在光伏、风电、负荷确定的前提下,进行储能配置优化,目标函数是光伏建设费用、风电建设费用、储能建设费用、购电费用最小。 + +### 主要功能 + +1. **经济参数配置** + - 建设成本:光伏、风电、储能(元/MW 或 元/MWh) + - 运行成本:购电价格、上网电价、运维成本 + - 财务参数:项目寿命、折现率 + +2. **优化算法** + - 网格搜索优化算法 + - 精细搜索(在最优解附近进行小范围搜索) + - 支持储能容量和充放电倍率的联合优化 + +3. **经济指标计算** + - **LCOE(平准化电力成本)**:考虑建设、运维、电费成本 + - **NPV(净现值)**:基于折现率的现金流分析 + - **总成本分析**:建设成本、运维成本、电费成本 + +4. **系统性能评估** + - 新能源消纳比例 + - 弃风弃光量统计 + - 购电量和上网电量分析 + +### 使用方法 + +#### 1. 演示模式 +```bash +python economic_optimization.py --demo +``` + +#### 2. Excel数据模式 +```bash +python economic_optimization.py --excel +``` + +#### 3. 编程接口 +```python +from economic_optimization import EconomicParameters, optimize_storage_economic + +# 经济参数 +econ_params = EconomicParameters( + solar_capex=3000000, # 光伏建设成本 (元/MW) + wind_capex=2500000, # 风电建设成本 (元/MW) + storage_capex=800000, # 储能建设成本 (元/MWh) + electricity_price=600, # 购电价格 (元/MWh) + feed_in_price=400, # 上网电价 (元/MWh) + solar_om=50000, # 光伏运维成本 (元/MW/年) + wind_om=45000, # 风电运维成本 (元/MW/年) + storage_om=3000, # 储能运维成本 (元/MW/年) + project_lifetime=25, # 项目寿命 (年) + discount_rate=0.08 # 折现率 +) + +# 系统参数 +system_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 +) + +# 运行优化 +result = optimize_storage_economic( + solar_output, wind_output, thermal_output, load_demand, + econ_params, system_params, + storage_capacity_range=(0, 1000), + rate_range=(0.1, 2.0), + max_iterations=100, + tolerance=0.01 +) + +# 输出结果 +print(f"最优储能容量: {result.storage_capacity:.2f} MWh") +print(f"最优充电倍率: {result.charge_rate:.2f}") +print(f"最优放电倍率: {result.discharge_rate:.2f}") +print(f"总建设成本: {result.total_capex:.2f} 元") +print(f"LCOE: {result.total_lcoe:.2f} 元/MWh") +print(f"NPV: {result.total_npv:.2f} 元") +print(f"新能源消纳比例: {result.renewable_ratio:.2f}%") +``` + +### 输出结果 + +经济优化模块返回 `OptimizationResult` 对象,包含: + +```python +{ + 'storage_capacity': float, # 最优储能容量 (MWh) + 'charge_rate': float, # 最优充电倍率 (C-rate) + 'discharge_rate': float, # 最优放电倍率 (C-rate) + 'total_capex': float, # 总建设成本 (元) + 'total_om_cost': float, # 总运维成本 (元) + 'total_electricity_cost': float # 总电费成本 (元) + 'total_lcoe': float, # 平准化电力成本 (元/MWh) + 'total_npv': float, # 净现值 (元) + 'total_curtailment': float, # 总弃风弃光量 (MWh) + 'grid_purchase': float, # 总购电量 (MWh) + 'grid_feed_in': float, # 总上网电量 (MWh) + 'renewable_ratio': float # 新能源消纳比例 (%) +} +``` + +### 报告生成 + +程序自动生成Excel报告,包含: +- 优化结果汇总 +- 经济参数配置 +- 详细的成本分析 + +### 可视化图表 + +生成经济分析图表: +- NPV vs 储能容量 +- LCOE vs 储能容量 +- 新能源消纳比例 vs 储能容量 +- 成本构成饼图 + ## 系统参数说明 - **max_curtailment_wind**: 最大允许弃风率 (0.0-1.0) @@ -416,15 +587,36 @@ print(f"实际上网电量比例: {result['total_grid_feed_in_ratio']:.3f}") ``` D:\code\storage\ -├── storage_optimization.py # 主程序文件 -├── test_storage_optimization.py # 测试文件 +├── storage_optimization.py # 主程序文件(储能容量优化) +├── economic_optimization.py # 经济优化模块 +├── excel_reader.py # Excel数据读取和模板生成 ├── main.py # 基础可视化程序 ├── advanced_visualization.py # 高级可视化程序 ├── example_usage.py # 使用示例 ├── requirements.txt # 依赖包列表 -└── README.md # 本文档 +├── pyproject.toml # 项目配置文件 +├── main.spec # PyInstaller配置文件 +└── README.md # 本文档 ``` +### 主要模块说明 + +- **storage_optimization.py**: 储能容量优化核心算法 +- **economic_optimization.py**: 经济优化模块,包含LCOE、NPV计算 +- **excel_reader.py**: Excel文件读取、验证和模板生成 +- **main.py**: 基础可视化和系统分析 +- **advanced_visualization.py**: 高级图表和综合分析 + +### 生成的文件 + +运行程序后会生成以下文件: +- `system_curves.png`: 系统运行曲线图 +- `time_series_curves.png`: 时间序列曲线图 +- `comprehensive_analysis.png`: 综合分析图 +- `economic_analysis.png`: 经济分析图 +- `economic_optimization_report_*.xlsx`: 经济优化报告 +- `storage_optimization_results_*.xlsx`: 储能优化结果 + ## 扩展性 程序设计具有良好的扩展性: @@ -432,6 +624,9 @@ D:\code\storage\ - 支持不同时间分辨率调整 - 预留了储能成本和寿命模型接口 - 可集成更复杂的优化算法 +- **经济模块扩展**:支持更多经济指标和优化目标 +- **Excel集成**:易于添加新的参数类型和配置选项 +- **可视化扩展**:模块化的图表生成系统 ## 使用场景 @@ -441,6 +636,9 @@ D:\code\storage\ - 储能系统设计 - 能源政策分析 - 学术研究 +- **经济性评估**:项目投资决策和成本效益分析 +- **储能配置优化**:基于经济指标的最优储能容量确定 +- **能源交易分析**:购电成本和上网收益分析 ## 注意事项 diff --git a/docs/README_exe.md b/docs/README_exe.md new file mode 100644 index 0000000..9a82732 --- /dev/null +++ b/docs/README_exe.md @@ -0,0 +1,86 @@ +# 多能互补系统储能容量优化程序 - 独立可执行版本 + +## 文件说明 + +- **文件名**: `storage_optimization.exe` +- **大小**: 约 52.7 MB +- **位置**: `dist/storage_optimization.exe` +- **类型**: 独立可执行程序(无需安装Python环境) + +## 使用方法 + +### 基本用法 + +```bash +# 使用24小时示例数据 +storage_optimization.exe + +# 从Excel文件读取数据 +storage_optimization.exe --excel data.xlsx + +# 显示图形窗口 +storage_optimization.exe --excel data.xlsx --show + +# 只显示图形窗口,不保存文件 +storage_optimization.exe --excel data.xlsx --display-only + +# 创建Excel模板 +storage_optimization.exe --create-template 8760 +storage_optimization.exe --create-template 24 +``` + +### Excel文件格式要求 + +Excel文件应包含以下列: +- 小时 +- 光伏出力(MW) +- 风电出力(MW) +- 火电出力(MW) +- 负荷需求(MW) + +可选的系统参数工作表(工作表名为"参数"): +- 参数名称:最大弃风率、最大弃光率、最大上网电量比例等 +- 参数值:对应的数值 +- 参数说明:参数说明 + +### 输出结果 + +程序运行后会生成: +1. **控制台输出**:系统运行统计、弃风弃光统计、电网交互统计、新能源统计 +2. **Excel结果文件**:`storage_optimization_results_YYYYMMDD_HHMMSS.xlsx` + - 运行数据:小时级运行数据 + - 统计结果:关键性能指标 + - 系统参数:输入参数汇总 + - 说明:文件使用说明 +3. **图形文件**:`system_curves.png`(系统运行曲线图) + +## 功能特点 + +- ✅ 完全独立运行,无需安装Python环境 +- ✅ 支持Excel数据输入和模板生成 +- ✅ 自动储能容量优化 +- ✅ 弃风弃光智能处理(已修复bug) +- ✅ 新能源利用率统计 +- ✅ 图形化结果展示 +- ✅ 详细Excel报告导出 + +## 系统要求 + +- Windows操作系统 +- 至少100MB可用磁盘空间 +- 支持Excel文件(.xlsx格式) + +## 故障排除 + +如果程序无法运行: +1. 检查Windows版本兼容性 +2. 确保有足够的磁盘空间 +3. 检查Excel文件格式是否正确 +4. 确保没有杀毒软件阻止程序运行 + +## 技术信息 + +- 基于Python 3.14.0 +- 使用PyInstaller打包 +- 包含所有必需依赖库 +- 支持中文显示和输入 \ No newline at end of file diff --git a/economic_optimization.py b/economic_optimization.py deleted file mode 100644 index 629cedf..0000000 --- a/economic_optimization.py +++ /dev/null @@ -1,709 +0,0 @@ -""" -经济指标优化模块 - -该模块在光伏、风电、负荷确定的前提下,进行储能配置优化。 -目标函数是光伏建设费用、风电建设费用、储能建设费用、购电费用最小。 - -作者: iFlow CLI -创建日期: 2025-12-26 -""" - -import numpy as np -import pandas as pd -from typing import List, Dict, Tuple -from dataclasses import dataclass -from storage_optimization import SystemParameters, calculate_energy_balance, check_constraints -import matplotlib.pyplot as plt - - -@dataclass -class EconomicParameters: - """经济参数配置""" - # 建设成本参数 (元/MW 或 元/MWh) - solar_capex: float = 3000000 # 光伏建设成本 (元/MW) - wind_capex: float = 2500000 # 风电建设成本 (元/MW) - storage_capex: float = 800000 # 储能建设成本 (元/MWh) - - # 运行成本参数 - electricity_price: float = 600 # 购电价格 (元/MWh) - feed_in_price: float = 400 # 上网电价 (元/MWh) - - # 维护成本参数 - solar_om: float = 50000 # 光伏运维成本 (元/MW/年) - wind_om: float = 45000 # 风电运维成本 (元/MW/年) - storage_om: float = 3000 # 储能运维成本 (元/MW/年) - - # 财务参数 - project_lifetime: int = 25 # 项目寿命 (年) - discount_rate: float = 0.08 # 折现率 - - -@dataclass -class OptimizationResult: - """优化结果""" - # 储能配置参数 - storage_capacity: float # 储能容量 (MWh) - charge_rate: float # 充电倍率 (C-rate) - discharge_rate: float # 放电倍率 (C-rate) - - # 经济指标 - total_capex: float # 总建设成本 (元) - total_om_cost: float # 总运维成本 (元) - total_electricity_cost: float # 总电费成本 (元) - total_lcoe: float # 平准化电力成本 (元/MWh) - total_npv: float # 净现值 (元) - - # 系统性能指标 - total_curtailment: float # 总弃风弃光量 (MWh) - grid_purchase: float # 总购电量 (MWh) - grid_feed_in: float # 总上网电量 (MWh) - renewable_ratio: float # 新能源消纳比例 - - -def calculate_lcoe( - capex: float, - om_cost: float, - electricity_cost: float, - annual_generation: float, - project_lifetime: int, - discount_rate: float -) -> float: - """ - 计算基准化电力成本 (LCOE) - - Args: - capex: 建设成本 - om_cost: 年运维成本 - electricity_cost: 年电费成本 - annual_generation: 年发电量 - project_lifetime: 项目寿命 - discount_rate: 折现率 - - Returns: - LCOE值 (元/MWh) - """ - if annual_generation <= 0: - return float('inf') - - # 计算现值因子 - pv_factor = sum(1 / (1 + discount_rate) ** t for t in range(1, project_lifetime + 1)) - - # LCOE = (建设成本现值 + 运维成本现值 + 电费成本现值) / 年发电量现值 - total_cost = capex + om_cost * pv_factor + electricity_cost * pv_factor - generation_pv = annual_generation * pv_factor - - return total_cost / generation_pv - - -def calculate_npv( - costs: List[float], - discount_rate: float -) -> float: - """ - 计算净现值 (NPV) - - Args: - costs: 各年度成本流 - discount_rate: 折现率 - - Returns: - NPV值 - """ - npv = 0 - for t, cost in enumerate(costs): - npv += cost / (1 + discount_rate) ** t - - return npv - - -def evaluate_objective( - solar_output: List[float], - wind_output: List[float], - thermal_output: List[float], - load_demand: List[float], - storage_capacity: float, - charge_rate: float, - discharge_rate: float, - econ_params: EconomicParameters, - system_params: SystemParameters -) -> Dict: - """ - 评估目标函数值 - - Args: - solar_output: 光伏出力曲线 (MW) - wind_output: 风电出力曲线 (MW) - thermal_output: 火电出力曲线 (MW) - load_demand: 负荷需求曲线 (MW) - storage_capacity: 储能容量 (MWh) - charge_rate: 充电倍率 - discharge_rate: 放电倍率 - econ_params: 经济参数 - system_params: 系统参数 - - Returns: - 包含各项成本和性能指标的字典 - """ - # 计算系统运行结果 - result = calculate_energy_balance( - solar_output, wind_output, thermal_output, load_demand, - system_params, storage_capacity - ) - - # 计算弃风弃光量 - total_curtailment = sum(result['curtailed_wind']) + sum(result['curtailed_solar']) - - # 计算购电和上网电量 - grid_purchase = sum(-x for x in result['grid_feed_in'] if x < 0) - grid_feed_in = sum(x for x in result['grid_feed_in'] if x > 0) - - # 计算建设成本 - solar_capex_cost = sum(solar_output) * econ_params.solar_capex / len(solar_output) * 8760 # 转换为年容量 - wind_capex_cost = sum(wind_output) * econ_params.wind_capex / len(wind_output) * 8760 - storage_capex_cost = storage_capacity * econ_params.storage_capex - - total_capex = solar_capex_cost + wind_capex_cost + storage_capex_cost - - # 计算年运维成本 - solar_om_cost = sum(solar_output) * econ_params.solar_om / len(solar_output) * 8760 - wind_om_cost = sum(wind_output) * econ_params.wind_om / len(wind_output) * 8760 - storage_om_cost = storage_capacity * econ_params.storage_om - - total_om_cost = solar_om_cost + wind_om_cost + storage_om_cost - - # 计算年电费成本 - annual_electricity_cost = grid_purchase * econ_params.electricity_price - - # 计算年发电量 - total_generation = sum(solar_output) + sum(wind_output) + sum(thermal_output) - renewable_generation = sum(solar_output) + sum(wind_output) - total_curtailment - - # 计算LCOE - lcoe = calculate_lcoe( - total_capex, total_om_cost, annual_electricity_cost, - renewable_generation, econ_params.project_lifetime, econ_params.discount_rate - ) - - # 计算NPV - annual_costs = [total_om_cost + annual_electricity_cost] * econ_params.project_lifetime - npv = calculate_npv([total_capex] + annual_costs, econ_params.discount_rate) - - # 计算新能源消纳比例 - renewable_ratio = (renewable_generation / sum(load_demand) * 100) if sum(load_demand) > 0 else 0 - - return { - 'total_capex': total_capex, - 'total_om_cost': total_om_cost * econ_params.project_lifetime, - 'total_electricity_cost': annual_electricity_cost * econ_params.project_lifetime, - 'total_lcoe': lcoe, - 'total_npv': npv, - 'total_curtailment': total_curtailment, - 'grid_purchase': grid_purchase, - 'grid_feed_in': grid_feed_in, - 'renewable_ratio': renewable_ratio, - 'storage_capacity': storage_capacity, - 'charge_rate': charge_rate, - 'discharge_rate': discharge_rate - } - - -def optimize_storage_economic( - solar_output: List[float], - wind_output: List[float], - thermal_output: List[float], - load_demand: List[float], - econ_params: EconomicParameters, - system_params: SystemParameters, - storage_capacity_range: Tuple[float, float] = (0, 1000), - rate_range: Tuple[float, float] = (0.1, 2.0), - max_iterations: int = 100, - tolerance: float = 0.01 -) -> OptimizationResult: - """ - 经济指标优化主函数 - - Args: - solar_output: 光伏出力曲线 (MW) - wind_output: 风电出力曲线 (MW) - thermal_output: 火电出力曲线 (MW) - load_demand: 负荷需求曲线 (MW) - econ_params: 经济参数 - system_params: 系统参数 - storage_capacity_range: 储能容量搜索范围 (MWh) - rate_range: 充放电倍率搜索范围 - max_iterations: 最大迭代次数 - tolerance: 收敛容差 - - Returns: - 优化结果 - """ - print("开始经济指标优化...") - - best_result = None - best_npv = float('inf') - - # 简化的网格搜索优化 - for iteration in range(max_iterations): - # 在搜索范围内随机采样 - storage_capacity = np.random.uniform(storage_capacity_range[0], storage_capacity_range[1]) - charge_rate = np.random.uniform(rate_range[0], rate_range[1]) - discharge_rate = np.random.uniform(rate_range[0], rate_range[1]) - - # 评估当前配置 - current_result = evaluate_objective( - solar_output, wind_output, thermal_output, load_demand, - storage_capacity, charge_rate, discharge_rate, - econ_params, system_params - ) - - # 更新最优解 - if current_result['total_npv'] < best_npv: - best_npv = current_result['total_npv'] - best_result = current_result - - # 输出进度 - if (iteration + 1) % 10 == 0: - print(f"迭代 {iteration + 1}/{max_iterations}, 当前最优NPV: {best_npv:.2f}元") - - # 在最优解附近进行精细搜索 - if best_result is not None: - print("在最优解附近进行精细搜索...") - best_result = fine_tune_optimization( - solar_output, wind_output, thermal_output, load_demand, - best_result, econ_params, system_params, - storage_capacity_range, rate_range - ) - - return best_result - - -def fine_tune_optimization( - solar_output: List[float], - wind_output: List[float], - thermal_output: List[float], - load_demand: List[float], - initial_result: Dict, - econ_params: EconomicParameters, - system_params: SystemParameters, - storage_capacity_range: Tuple[float, float], - rate_range: Tuple[float, float] -) -> OptimizationResult: - """ - 在最优解附近进行精细搜索 - - Args: - solar_output: 光伏出力曲线 (MW) - wind_output: 风电出力曲线 (MW) - thermal_output: 火电出力曲线 (MW) - load_demand: 负荷需求曲线 (MW) - initial_result: 初始优化结果 - econ_params: 经济参数 - system_params: 系统参数 - storage_capacity_range: 储能容量搜索范围 - rate_range: 充放电倍率搜索范围 - - Returns: - 精细优化结果 - """ - best_result = initial_result - best_npv = initial_result['total_npv'] - - # 在最优解附近进行小范围搜索 - search_range = 0.1 # 搜索范围为最优值的±10% - - for storage_capacity in np.linspace( - max(initial_result['storage_capacity'] * (1 - search_range), 0), - min(initial_result['storage_capacity'] * (1 + search_range), storage_capacity_range[1]), - 20 - ): - for charge_rate in np.linspace( - max(initial_result['charge_rate'] * (1 - search_range), rate_range[0]), - min(initial_result['charge_rate'] * (1 + search_range), rate_range[1]), - 10 - ): - for discharge_rate in np.linspace( - max(initial_result['discharge_rate'] * (1 - search_range), rate_range[0]), - min(initial_result['discharge_rate'] * (1 + search_range), rate_range[1]), - 10 - ): - current_result = evaluate_objective( - solar_output, wind_output, thermal_output, load_demand, - storage_capacity, charge_rate, discharge_rate, - econ_params, system_params - ) - - if current_result['total_npv'] < best_npv: - best_npv = current_result['total_npv'] - best_result = current_result - - # 转换为OptimizationResult格式 - return OptimizationResult( - storage_capacity=best_result['storage_capacity'], - charge_rate=best_result['charge_rate'], - discharge_rate=best_result['discharge_rate'], - total_capex=best_result['total_capex'], - total_om_cost=best_result['total_om_cost'], - total_electricity_cost=best_result['total_electricity_cost'], - total_lcoe=best_result['total_lcoe'], - total_npv=best_result['total_npv'], - total_curtailment=best_result['total_curtailment'], - grid_purchase=best_result['grid_purchase'], - grid_feed_in=best_result['grid_feed_in'], - renewable_ratio=best_result['renewable_ratio'] - ) - - -def create_economic_report( - result: OptimizationResult, - econ_params: EconomicParameters, - filename: str = None -) -> str: - """ - 创建经济优化报告 - - Args: - result: 优化结果 - econ_params: 经济参数 - filename: 输出文件名 - - Returns: - 报告文件路径 - """ - if filename is None: - from datetime import datetime - timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") - filename = f"economic_optimization_report_{timestamp}.xlsx" - - print(f"\n正在生成经济优化报告: {filename}") - - # 创建报告数据 - report_data = { - '指标': [ - '储能容量 (MWh)', - '充电倍率 (C-rate)', - '放电倍率 (光伏建设成本 (元/MW)', - '光伏建设成本 (元)', - '风电建设成本 (元)', - '储能建设成本 (元)', - '总建设成本 (元)', - '年运维成本 (元)', - '总运维成本 (元)', - '年电费成本 (元)', - '总电费成本 (元)', - 'LCOE (元/MWh)', - 'NPV (元)', - '总弃风弃光量 (MWh)', - '总购电量 (MWh)', - '总上网电量 (MWh)', - '新能源消纳比例 (%)' - ], - '数值': [ - f"{result.storage_capacity:.2f}", - f"{result.charge_rate:.2f}", - f"{result.discharge_rate:.2f}", - f"{result.total_capex * 0.3:.2f}", # 光伏建设成本 - f"{result.total_capex * 0.6:.2f}", # 风电建设成本 - f"{result.total_capex * 0.1:.2f}", # 储能建设成本 - f"{result.total_capex:.2f}", - f"{result.total_om_cost / econ_params.project_lifetime:.2f}", - f"{result.total_om_cost:.2f}", - f"{result.total_electricity_cost / econ_params.project_lifetime:.2f}", - f"{result.total_electricity_cost:.2f}", - f"{result.total_lcoe:.2f}", - f"{result.total_npv:.2f}", - f"{result.total_curtailment:.2f}", - f"{result.grid_purchase:.2f}", - f"{result.grid_feed_in:.2f}", - f"{result.renewable_ratio:.2f}" - ] - } - - # 创建参数数据 - params_data = { - '参数': [ - '光伏建设成本 (元/MW)', - '风电建设成本 (元/MW)', - '储能建设成本 (元/MWh)', - '购电价格 (元/MWh)', - '上网电价 (元/MWh)', - '光伏运维成本 (元/MW/年)', - '风电运维成本 (元/MW/年)', - '储能运维成本 (元/MW/年)', - '项目寿命 (年)', - '折现率' ], - '数值': [ - econ_params.solar_capex, - econ_params.wind_capex, - econ_params.storage_capex, - econ_params.electricity_price, - econ_params.feed_in_price, - econ_params.solar_om, - econ_params.wind_om, - econ_params.storage_om, - econ_params.project_lifetime, - f"{econ_params.discount_rate:.2f}" ] - } - - # 写入Excel文件 - with pd.ExcelWriter(filename, engine='openpyxl') as writer: - # 写入优化结果 - pd.DataFrame(report_data).to_excel(writer, sheet_name='优化结果', index=False) - - # 写入经济参数 - pd.DataFrame(params_data).to_excel(writer, sheet_name='经济参数', index=False) - - # 创建说明 - description_data = { - '项目': [ - '报告说明', - '生成时间', - '优化目标', - '优化方法', - '数据来源', - '注意事项' - ], - '内容': [ - '多能互补系统储能经济优化报告', - pd.Timestamp.now().strftime("%Y-%m-%d %H:%M:%S"), - '最小化总建设成本和购电费用', - '网格搜索算法 + 精细搜索', - '基于给定的风光出力和负荷数据', - '成本估算仅供参考,实际成本可能有所不同' - ] - } - pd.DataFrame(description_data).to_excel(writer, sheet_name='说明', index=False) - - print(f"经济优化报告已生成: {filename}") - return filename - - -def plot_economic_analysis( - results: List[OptimizationResult], - filename: str = None -): - """ - 绘制经济分析图表 - - Args: - results: 优化结果列表 - filename: 图片保存文件名 - """ - if filename is None: - filename = 'economic_analysis.png' - - # 提取数据 - capacities = [r.storage_capacity for r in results] - npvs = [r.total_npv for r in results] - lcoes = [r.total_lcoe for r in results] - renewable_ratios = [r.renewable_ratio for r in results] - - # 创建图表 - fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(15, 12)) - fig.suptitle('储能配置经济分析', fontsize=16, fontweight='bold') - - # NPV vs 储能容量 - ax1.scatter(capacities, npvs, alpha=0.6, c='blue') - ax1.set_xlabel('储能容量 (MWh)') - ax1.set_ylabel('NPV (元)') - ax1.set_title('NPV vs 储能容量') - ax1.grid(True, alpha=0.3) - - # LCOE vs 储能容量 - ax2.scatter(capacities, lcoes, alpha=0.6, c='green') - ax2.set_xlabel('储能容量 (MWh)') - ax2.set_ylabel('LCOE (元/MWh)') - ax2.set_title('LCOE vs 储能容量') - ax2.grid(True, alpha=0.3) - - # 新能源消纳比例 vs 储能容量 - ax3.scatter(capacities, renewable_ratios, alpha=0.6, c='orange') - ax3.set_xlabel('储能容量 (MWh)') - ax3.set_ylabel('新能源消纳比例 (%)') - ax3.set_title('新能源消纳比例 vs 储能容量') - ax3.grid(True, alpha=0.3) - - # 成本构成饼图(使用最优结果) - if results: - best_result = min(results, key=lambda x: x.total_npv) - costs = [ - best_result.total_capex * 0.3, # 光伏 - best_result.total_capex * 0.6, # 风电 - best_result.total_capex * 0.1, # 储能 - best_result.total_om_cost, # 运维 - best_result.total_electricity_cost # 电费 - ] - labels = ['光伏建设', '风电建设', '储能建设', '运维成本', '电费成本'] - colors = ['yellow', 'lightblue', 'lightgreen', 'orange', 'red'] - - ax4.pie(costs, labels=labels, colors=colors, autopct='%1.1f%%') - ax4.set_title('成本构成分析') - - plt.tight_layout() - plt.savefig(filename, dpi=300, bbox_inches='tight') - print(f"经济分析图表已保存: {filename}") - - -def main(): - """主函数 - 演示经济优化功能""" - import sys - - # 检查命令行参数 - if len(sys.argv) < 2: - print("用法: python economic_optimization.py --excel <文件路径>") - print(" python economic_optimization.py --demo # 运行演示") - return - - command = sys.argv[1] - - if command == '--demo': - print("运行经济优化演示...") - - # 生成演示数据 - hours = 8760 - 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 - solar_output = solar_output * 365 - - wind_output = [2.0, 3.0, 4.0, 3.0, 2.0, 1.0] * 4 - wind_output = wind_output * 365 - - thermal_output = [5.0] * 24 - thermal_output = thermal_output * 365 - - 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] * 365 - - # 经济参数 - econ_params = EconomicParameters( - solar_capex=3000000, # 300万/MW - wind_capex=2500000, # 250万/MW - storage_capex=800000, # 80万/MWh - electricity_price=600, # 600元/MWh - feed_in_price=400, # 400元/MWh - solar_om=50000, # 5万/MW/年 - wind_om=45000, # 4.5万/MW/年 - storage_om=3000, # 3000/MW/年 - project_lifetime=25, # 25年 - discount_rate=0.08 # 8% - ) - - # 系统参数 - system_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, - max_storage_capacity=None, - rated_thermal_capacity=0, - rated_solar_capacity=0, - rated_wind_capacity=0, - available_thermal_energy=0, - available_solar_energy=0, - available_wind_energy=0 - ) - - # 运行优化 - result = optimize_storage_economic( - solar_output, wind_output, thermal_output, load_demand, - econ_params, system_params, - storage_capacity_range=(0, 500), - rate_range=(0.1, 1.5), - max_iterations=50 - ) - - # 输出结果 - print("\n=== 经济优化结果 ===") - print(f"最优储能容量: {result.storage_capacity:.2f} MWh") - print(f"最优充电倍率: {result.charge_rate:.2f}") - print(f"最优放电倍率: {result.discharge_rate:.2f}") - print(f"总建设成本: {result.total_capex:.2f} 元") - print(f"总运维成本: {result.total_om_cost:.2f} 元") - print(f"总电费成本: {result.total_electricity_cost:.2f} 元") - print(f"LCOE: {result.total_lcoe:.2f} 元/MWh") - print(f"NPV: {result.total_npv:.2f} 元") - print(f"新能源消纳比例: {result.renewable_ratio:.2f}%") - - # 生成报告 - create_economic_report(result, econ_params) - - # 生成图表 - plot_economic_analysis([result]) - - elif command == '--excel': - if len(sys.argv) < 3: - print("错误:请指定Excel文件路径") - return - - excel_file = sys.argv[2] - print(f"从Excel文件读取数据: {excel_file}") - - try: - # 从Excel文件读取数据 - from excel_reader import read_excel_data - - data = read_excel_data(excel_file, include_parameters=True) - - solar_output = data['solar_output'] - wind_output = data['wind_output'] - thermal_output = data['thermal_output'] - load_demand = data['load_demand'] - - # 获取系统参数 - system_params = data.get('system_parameters', SystemParameters()) - - # 获取经济参数 - econ_params = data.get('economic_parameters', EconomicParameters()) - - # 获取优化设置 - opt_settings = data.get('optimization_settings', { - 'storage_capacity_range': (0, 1000), - 'rate_range': (0.1, 2.0), - 'max_iterations': 100, - 'tolerance': 0.01 - }) - - print(f"成功读取数据,类型:{data['data_type']}") - print(f"光伏出力总量: {sum(solar_output):.2f} MW") - print(f"风电出力总量: {sum(wind_output):.2f} MW") - print(f"负荷需求总量: {sum(load_demand):.2f} MW") - - # 运行优化 - result = optimize_storage_economic( - solar_output, wind_output, thermal_output, load_demand, - econ_params, system_params, - storage_capacity_range=opt_settings['storage_capacity_range'], - rate_range=opt_settings['rate_range'], - max_iterations=opt_settings['max_iterations'], - tolerance=opt_settings['tolerance'] - ) - - # 输出结果 - print("\n=== 经济优化结果 ===") - print(f"最优储能容量: {result.storage_capacity:.2f} MWh") - print(f"最优充电倍率: {result.charge_rate:.2f}") - print(f"最优放电倍率: {result.discharge_rate:.2f}") - print(f"总建设成本: {result.total_capex:.2f} 元") - print(f"总运维成本: {result.total_om_cost:.2f} 元") - print(f"总电费成本: {result.total_electricity_cost:.2f} 元") - print(f"LCOE: {result.total_lcoe:.2f} 元/MWh") - print(f"NPV: {result.total_npv:.2f} 元") - print(f"总弃风弃光量: {result.total_curtailment:.2f} MWh") - print(f"总购电量: {result.grid_purchase:.2f} MWh") - print(f"总上网电量: {result.grid_feed_in:.2f} MWh") - print(f"新能源消纳比例: {result.renewable_ratio:.2f}%") - - # 生成报告 - create_economic_report(result, econ_params) - - # 生成图表 - plot_economic_analysis([result]) - - except Exception as e: - print(f"处理Excel文件时出错:{str(e)}") - import traceback - traceback.print_exc() - - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/example_usage.py b/example_usage.py deleted file mode 100644 index c102e5d..0000000 --- a/example_usage.py +++ /dev/null @@ -1,529 +0,0 @@ -""" -多能互补系统储能容量优化计算程序使用示例 - -该文件展示了如何使用储能优化程序处理不同的实际场景。 - -作者: iFlow CLI -创建日期: 2025-12-25 -""" - -import numpy as np -import matplotlib.pyplot as plt -from storage_optimization import optimize_storage_capacity, SystemParameters - -# 配置matplotlib支持中文显示 -plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'DejaVu Sans'] -plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题 - - -def example_1_basic_scenario(): - """示例1: 基础场景""" - print("=== 示例1: 基础场景 ===") - - # 基础数据 - 夏日典型日 - solar_output = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5, 2.0, 4.0, 6.0, 8.0, 9.0, - 8.0, 6.0, 4.0, 2.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] - - wind_output = [4.0, 4.5, 5.0, 5.5, 5.0, 4.5, 4.0, 3.5, 3.0, 2.5, 2.0, 1.5, - 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 5.0, 4.5, 4.0] - - thermal_output = [8.0] * 24 # 火电基荷 - - load_demand = [6.0, 5.5, 5.0, 5.0, 5.5, 7.0, 9.0, 12.0, 15.0, 18.0, 20.0, 19.0, - 18.0, 17.0, 16.0, 15.0, 14.0, 13.0, 12.0, 10.0, 8.0, 7.0, 6.0, 6.0] - - # 系统参数 - params = SystemParameters( - max_curtailment_wind=0.1, # 最大弃风率10% - max_curtailment_solar=0.05, # 最大弃光率5% - max_grid_ratio=0.15, # 最大上网电量比例15% - storage_efficiency=0.9, # 储能效率90% - discharge_rate=1.0, # 1C放电 - charge_rate=1.0 # 1C充电 - ) - - # 计算最优储能容量 - result = optimize_storage_capacity(solar_output, wind_output, thermal_output, load_demand, params) - - # 打印结果 - print(f"所需储能容量: {result['required_storage_capacity']:.2f} MWh") - print(f"实际弃风率: {result['total_curtailment_wind_ratio']:.3f} (约束: {params.max_curtailment_wind})") - print(f"实际弃光率: {result['total_curtailment_solar_ratio']:.3f} (约束: {params.max_curtailment_solar})") - print(f"实际上网电量比例: {result['total_grid_feed_in_ratio']:.3f} (约束: {params.max_grid_ratio})") - print(f"能量平衡校验: {'通过' if result['energy_balance_check'] else '未通过'}") - - return { - 'result': result, - 'solar_output': solar_output, - 'wind_output': wind_output, - 'thermal_output': thermal_output, - 'load_demand': load_demand - } - - -def example_2_high_renewable_scenario(): - """示例2: 高可再生能源渗透场景""" - print("\n=== 示例2: 高可再生能源渗透场景 ===") - - # 高可再生能源数据 - solar_output = [0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 3.0, 6.0, 10.0, 14.0, 18.0, 20.0, - 18.0, 14.0, 10.0, 6.0, 3.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] - - wind_output = [8.0, 9.0, 10.0, 11.0, 10.0, 9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, - 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 10.0, 9.0, 8.0] - - thermal_output = [4.0] * 24 # 较低的火电基荷 - - load_demand = [8.0, 7.5, 7.0, 7.0, 7.5, 9.0, 11.0, 14.0, 17.0, 20.0, 22.0, 21.0, - 20.0, 19.0, 18.0, 17.0, 16.0, 15.0, 14.0, 12.0, 10.0, 9.0, 8.0, 8.0] - - # 系统参数 - 较高的弃风弃光容忍度 - params = SystemParameters( - max_curtailment_wind=0.2, # 最大弃风率20% - max_curtailment_solar=0.15, # 最大弃光率15% - max_grid_ratio=0.25, # 最大上网电量比例25% - storage_efficiency=0.85, # 较低的储能效率 - discharge_rate=1.0, - charge_rate=1.0 - ) - - result = optimize_storage_capacity(solar_output, wind_output, thermal_output, load_demand, params) - - 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 '未通过'}") - - return { - 'result': result, - 'solar_output': solar_output, - 'wind_output': wind_output, - 'thermal_output': thermal_output, - 'load_demand': load_demand - } - - -def example_3_winter_scenario(): - """示例3: 冬季场景""" - print("\n=== 示例3: 冬季场景 ===") - - # 冬季数据 - 光照弱,风电强,负荷高 - solar_output = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.2, 0.8, 1.5, 2.0, 2.5, 2.8, - 2.5, 2.0, 1.5, 0.8, 0.2, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] - - wind_output = [12.0, 13.0, 14.0, 15.0, 14.0, 13.0, 12.0, 11.0, 10.0, 9.0, 8.0, 7.0, - 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 14.0, 13.0, 12.0] - - thermal_output = [12.0] * 24 # 高火电基荷 - - load_demand = [12.0, 11.5, 11.0, 11.0, 11.5, 13.0, 15.0, 18.0, 21.0, 24.0, 26.0, 25.0, - 24.0, 23.0, 22.0, 21.0, 20.0, 19.0, 18.0, 16.0, 14.0, 13.0, 12.0, 12.0] - - # 系统参数 - 严格的弃风弃光控制 - params = SystemParameters( - max_curtailment_wind=0.05, # 严格的弃风控制 - max_curtailment_solar=0.02, # 严格的弃光控制 - max_grid_ratio=0.1, # 低上网电量比例 - storage_efficiency=0.92, # 高储能效率 - discharge_rate=1.0, - charge_rate=1.0 - ) - - result = optimize_storage_capacity(solar_output, wind_output, thermal_output, load_demand, params) - - 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 '未通过'}") - - return { - 'result': result, - 'solar_output': solar_output, - 'wind_output': wind_output, - 'thermal_output': thermal_output, - 'load_demand': load_demand - } - - -def plot_results(result, title, solar_output, wind_output, thermal_output, load_demand): - """绘制结果图表""" - hours = list(range(24)) - - fig, ((ax1, ax2), (ax3, ax4), (ax5, ax6)) = plt.subplots(3, 2, figsize=(16, 12)) - fig.suptitle(title, fontsize=16) - - # 发电与负荷对比 - ax1.plot(hours, load_demand, 'r-', linewidth=2, label='负荷需求') - ax1.plot(hours, thermal_output, 'b-', linewidth=2, label='火电出力') - ax1.plot(hours, wind_output, 'g-', linewidth=2, label='风电出力') - ax1.plot(hours, solar_output, 'orange', linewidth=2, label='光伏出力') - - # 计算总发电量 - total_generation = [thermal_output[i] + wind_output[i] + solar_output[i] for i in range(24)] - ax1.plot(hours, total_generation, 'k--', linewidth=1.5, alpha=0.7, label='总发电量') - - ax1.set_title('发电与负荷曲线') - ax1.set_xlabel('时间 (小时)') - ax1.set_ylabel('功率 (MW)') - ax1.legend() - ax1.grid(True) - - # 储能状态 - ax2.plot(hours, result['storage_profile'], 'b-', linewidth=2) - ax2.set_title('储能状态 (MWh)') - ax2.set_xlabel('时间 (小时)') - ax2.set_ylabel('储能容量 (MWh)') - ax2.grid(True) - - # 充放电功率 - ax3.plot(hours, result['charge_profile'], 'g-', label='充电', linewidth=2) - ax3.plot(hours, [-p for p in result['discharge_profile']], 'r-', label='放电', linewidth=2) - ax3.set_title('储能充放电功率 (MW)') - ax3.set_xlabel('时间 (小时)') - ax3.set_ylabel('功率 (MW)') - ax3.legend() - ax3.grid(True) - - # 弃风弃光 - ax4.plot(hours, result['curtailed_wind'], 'c-', label='弃风', linewidth=2) - ax4.plot(hours, result['curtailed_solar'], 'm-', label='弃光', linewidth=2) - ax4.set_title('弃风弃光量 (MW)') - ax4.set_xlabel('时间 (小时)') - ax4.set_ylabel('功率 (MW)') - ax4.legend() - ax4.grid(True) - - # 上网电量/购电量 - ax5.plot(hours, result['grid_feed_in'], 'orange', linewidth=2) - ax5.axhline(y=0, color='black', linestyle='-', linewidth=0.5, alpha=0.5) - ax5.fill_between(hours, 0, result['grid_feed_in'], - where=[x >= 0 for x in result['grid_feed_in']], - alpha=0.3, color='green', label='上网') - ax5.fill_between(hours, 0, result['grid_feed_in'], - where=[x < 0 for x in result['grid_feed_in']], - alpha=0.3, color='red', label='购电') - - # 动态设置标题 - total_grid = sum(result['grid_feed_in']) - if total_grid < 0: - ax5.set_title(f'购电量 (总计: {abs(total_grid):.1f} MWh)') - else: - ax5.set_title(f'上网电量 (总计: {total_grid:.1f} MWh)') - - ax5.set_xlabel('时间 (小时)') - ax5.set_ylabel('功率 (MW)') - ax5.legend() - ax5.grid(True) - - # 能量平衡分析 - total_gen = sum(thermal_output) + sum(wind_output) + sum(solar_output) - total_load = sum(load_demand) - total_curtailed = sum(result['curtailed_wind']) + sum(result['curtailed_solar']) - total_grid = sum(result['grid_feed_in']) - total_charge = sum(result['charge_profile']) - total_discharge = sum(result['discharge_profile']) - - # 创建能量平衡柱状图 - categories = ['总发电量', '总负荷', '弃风弃光', '上网电量', '储能充电', '储能放电'] - values = [total_gen, total_load, total_curtailed, total_grid, total_charge, total_discharge] - colors = ['blue', 'red', 'orange', 'green', 'cyan', 'magenta'] - - bars = ax6.bar(categories, values, color=colors, alpha=0.7) - ax6.set_title('能量平衡分析') - ax6.set_ylabel('能量 (MWh)') - ax6.grid(True, axis='y', alpha=0.3) - - # 在柱状图上添加数值标签 - for bar, value in zip(bars, values): - height = bar.get_height() - ax6.text(bar.get_x() + bar.get_width()/2., height, - f'{value:.1f}', ha='center', va='bottom', fontsize=9) - - plt.tight_layout() - plt.show() - - -def example_5_high_load_grid_purchase_scenario(): - """示例5: 高负荷购电场景""" - print("\n=== 示例5: 高负荷购电场景 ===") - - # 高负荷场景数据 - 有充电和放电时段 - solar_output = [0.0, 0.0, 0.0, 0.0, 0.0, 2.0, 4.0, 6.0, 8.0, 10.0, 9.0, 8.0, - 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0] - - wind_output = [5.0, 5.5, 6.0, 6.5, 6.0, 5.5, 5.0, 4.5, 4.0, 3.5, 3.0, 2.5, - 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0, 5.5, 5.0, 5.0] - - thermal_output = [8.0] * 24 # 火电基荷 - - # 负荷曲线:夜间低负荷(充电时段),白天高负荷(放电和购电时段) - load_demand = [10.0, 9.0, 8.0, 7.0, 8.0, 12.0, 18.0, 25.0, 35.0, 42.0, 45.0, 43.0, - 40.0, 38.0, 35.0, 30.0, 25.0, 20.0, 15.0, 12.0, 11.0, 10.0, 10.0, 10.0] - - # 系统参数 - max_grid_ratio只限制上网电量比例,不限制购电 - params = SystemParameters( - max_curtailment_wind=0.05, # 严格的弃风控制 - max_curtailment_solar=0.02, # 严格的弃光控制 - max_grid_ratio=0.3, # 上网电量比例限制为30%,但不限制购电 - storage_efficiency=0.9, # 储能效率90% - discharge_rate=2.0, # 2C放电,满足高峰需求 - charge_rate=1.0, # 1C充电 - max_storage_capacity=8.0 # 限制储能容量为8MWh,确保储能被充分利用 - ) - - result = optimize_storage_capacity(solar_output, wind_output, thermal_output, load_demand, params, tolerance=0.1) - - print(f"所需储能容量: {result['required_storage_capacity']:.2f} MWh") - print(f"储能容量上限: {result['max_storage_limit']:.2f} MWh") - print(f"是否达到容量上限: {'是' if result['capacity_limit_reached'] else '否'}") - print(f"实际弃风率: {result['total_curtailment_wind_ratio']:.3f} (约束: {params.max_curtailment_wind})") - print(f"实际弃光率: {result['total_curtailment_solar_ratio']:.3f} (约束: {params.max_curtailment_solar})") - print(f"实际上网电量比例: {result['total_grid_feed_in_ratio']:.3f} (负值表示购电,正值表示上网)") - print(f"能量平衡校验: {'通过' if result['energy_balance_check'] else '未通过'}") - - # 调试信息 - total_gen = sum(solar_output) + sum(wind_output) + sum(thermal_output) - total_load = sum(load_demand) - total_charge = sum(result['charge_profile']) - total_discharge = sum(result['discharge_profile']) - - print(f"\n=== 调试信息 ===") - print(f"总发电量: {total_gen:.2f} MWh") - print(f"总负荷: {total_load:.2f} MWh") - print(f"负荷-发电差: {total_load - total_gen:.2f} MWh") - print(f"总充电量: {total_charge:.2f} MWh") - print(f"总放电量: {total_discharge:.2f} MWh") - print(f"储能净变化: {total_discharge - total_charge:.2f} MWh") - - # 计算购电量统计 - total_grid_feed = sum(result['grid_feed_in']) - if total_grid_feed < 0: - print(f"总购电量: {abs(total_grid_feed):.2f} MWh") - - # 显示前几个小时的详细情况 - print(f"\n前6小时详细情况:") - print(f"小时 | 发电 | 负荷 | 储能充电 | 储能放电 | 购电") - print("-" * 55) - for i in range(6): - gen = solar_output[i] + wind_output[i] + thermal_output[i] - charge = result['charge_profile'][i] - discharge = result['discharge_profile'][i] - grid = result['grid_feed_in'][i] - print(f"{i:2d} | {gen:4.1f} | {load_demand[i]:4.1f} | {charge:7.2f} | {discharge:7.2f} | {grid:5.2f}") - - # 计算最大缺电功率 - max_deficit = 0 - for hour in range(24): - total_gen = solar_output[hour] + wind_output[hour] + thermal_output[hour] - deficit = max(0, load_demand[hour] - total_gen - result['discharge_profile'][hour]) - max_deficit = max(max_deficit, deficit) - - if max_deficit > 0: - print(f"\n最大缺电功率: {max_deficit:.2f} MW") - - return { - 'result': result, - 'solar_output': solar_output, - 'wind_output': wind_output, - 'thermal_output': thermal_output, - 'load_demand': load_demand - } - - -def example_6_grid_ratio_limited_scenario(): - """示例6: 上网电量比例限制场景""" - print("\n=== 示例6: 上网电量比例限制场景 ===") - - # 高可再生能源场景 - 有大量盈余电力 - solar_output = [0.0, 0.0, 0.0, 0.0, 0.0, 2.0, 5.0, 8.0, 12.0, 16.0, 20.0, 18.0, - 15.0, 12.0, 8.0, 5.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] - - wind_output = [8.0, 9.0, 10.0, 11.0, 10.0, 9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, - 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 10.0, 9.0, 8.0] - - thermal_output = [6.0] * 24 # 中等火电出力 - - # 低负荷场景 - 有大量盈余电力 - load_demand = [8.0, 7.0, 6.0, 6.0, 7.0, 10.0, 12.0, 14.0, 16.0, 18.0, 20.0, 18.0, - 16.0, 14.0, 12.0, 10.0, 9.0, 8.0, 7.0, 6.0, 6.0, 7.0, 8.0, 8.0] - - # 系统参数 - 限制上网电量比例 - params = SystemParameters( - max_curtailment_wind=0.15, # 允许一定弃风 - max_curtailment_solar=0.1, # 允许一定弃光 - max_grid_ratio=0.15, # 限制上网电量比例为15% - storage_efficiency=0.9, # 储能效率90% - discharge_rate=1.0, # 1C放电 - charge_rate=1.0, # 1C充电 - max_storage_capacity=100.0 # 足够大的储能容量 - ) - - result = optimize_storage_capacity(solar_output, wind_output, thermal_output, load_demand, params) - - print(f"所需储能容量: {result['required_storage_capacity']:.2f} MWh") - print(f"上网电量比例限制: {params.max_grid_ratio:.1%}") - print(f"实际上网电量比例: {result['total_grid_feed_in_ratio']:.3f}") - print(f"实际弃风率: {result['total_curtailment_wind_ratio']:.3f} (约束: {params.max_curtailment_wind})") - print(f"实际弃光率: {result['total_curtailment_solar_ratio']:.3f} (约束: {params.max_curtailment_solar})") - print(f"能量平衡校验: {'通过' if result['energy_balance_check'] else '未通过'}") - - # 检查是否达到上网电量比例限制 - if result['total_grid_feed_in_ratio'] >= params.max_grid_ratio - 0.01: - print("注意:已达到上网电量比例限制") - - return { - 'result': result, - 'solar_output': solar_output, - 'wind_output': wind_output, - 'thermal_output': thermal_output, - 'load_demand': load_demand - } - - -def example_4_capacity_limited_scenario(): - """示例4: 储能容量限制场景""" - print("\n=== 示例4: 储能容量限制场景 ===") - - # 使用基础场景的数据 - solar_output = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5, 2.0, 4.0, 6.0, 8.0, 9.0, - 8.0, 6.0, 4.0, 2.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] - - wind_output = [4.0, 4.5, 5.0, 5.5, 5.0, 4.5, 4.0, 3.5, 3.0, 2.5, 2.0, 1.5, - 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 5.0, 4.5, 4.0] - - thermal_output = [8.0] * 24 - - load_demand = [6.0, 5.5, 5.0, 5.0, 5.5, 7.0, 9.0, 12.0, 15.0, 18.0, 20.0, 19.0, - 18.0, 17.0, 16.0, 15.0, 14.0, 13.0, 12.0, 10.0, 8.0, 7.0, 6.0, 6.0] - - # 系统参数 - 设置储能容量上限为10 MWh - params = SystemParameters( - max_curtailment_wind=0.1, - max_curtailment_solar=0.05, - max_grid_ratio=0.15, - storage_efficiency=0.9, - discharge_rate=1.0, - charge_rate=1.0, - max_storage_capacity=10.0 # 限制储能容量上限为10 MWh - ) - - result = optimize_storage_capacity(solar_output, wind_output, thermal_output, load_demand, params) - - print(f"所需储能容量: {result['required_storage_capacity']:.2f} MWh") - print(f"储能容量上限: {result['max_storage_limit']:.2f} MWh") - print(f"是否达到容量上限: {'是' if result['capacity_limit_reached'] else '否'}") - print(f"实际弃风率: {result['total_curtailment_wind_ratio']:.3f} (约束: {params.max_curtailment_wind})") - print(f"实际弃光率: {result['total_curtailment_solar_ratio']:.3f} (约束: {params.max_curtailment_solar})") - print(f"实际上网电量比例: {result['total_grid_feed_in_ratio']:.3f} (约束: {params.max_grid_ratio})") - print(f"能量平衡校验: {'通过' if result['energy_balance_check'] else '未通过'}") - - return { - 'result': result, - 'solar_output': solar_output, - 'wind_output': wind_output, - 'thermal_output': thermal_output, - 'load_demand': load_demand - } - - -def compare_scenarios(): - """比较不同场景的结果""" - print("\n=== 场景比较 ===") - - # 运行六个场景 - data1 = example_1_basic_scenario() - data2 = example_2_high_renewable_scenario() - data3 = example_3_winter_scenario() - data4 = example_4_capacity_limited_scenario() - data5 = example_5_high_load_grid_purchase_scenario() - data6 = example_6_grid_ratio_limited_scenario() - - # 比较结果 - scenarios = ['基础场景', '高可再生能源场景', '冬季场景', '容量限制场景', '高负荷购电场景', '上网电量比例限制场景'] - storage_capacities = [ - data1['result']['required_storage_capacity'], - data2['result']['required_storage_capacity'], - data3['result']['required_storage_capacity'], - data4['result']['required_storage_capacity'], - data5['result']['required_storage_capacity'], - data6['result']['required_storage_capacity'] - ] - curtailment_wind = [ - data1['result']['total_curtailment_wind_ratio'], - data2['result']['total_curtailment_wind_ratio'], - data3['result']['total_curtailment_wind_ratio'], - data4['result']['total_curtailment_wind_ratio'], - data5['result']['total_curtailment_wind_ratio'], - data6['result']['total_curtailment_wind_ratio'] - ] - curtailment_solar = [ - data1['result']['total_curtailment_solar_ratio'], - data2['result']['total_curtailment_solar_ratio'], - data3['result']['total_curtailment_solar_ratio'], - data4['result']['total_curtailment_solar_ratio'], - data5['result']['total_curtailment_solar_ratio'], - data6['result']['total_curtailment_solar_ratio'] - ] - grid_feed_in = [ - data1['result']['total_grid_feed_in_ratio'], - data2['result']['total_grid_feed_in_ratio'], - data3['result']['total_grid_feed_in_ratio'], - data4['result']['total_grid_feed_in_ratio'], - data5['result']['total_grid_feed_in_ratio'], - data6['result']['total_grid_feed_in_ratio'] - ] - capacity_limit = [ - '无', - '无', - '无', - f"{data4['result']['max_storage_limit']:.1f}MWh", - f"{data5['result']['max_storage_limit']:.1f}MWh", - f"{data6['result']['max_storage_limit']:.1f}MWh" - ] - - print("\n场景比较结果:") - print(f"{'场景':<15} {'储能容量(MWh)':<12} {'容量限制':<10} {'弃风率':<8} {'弃光率':<8} {'上网比例':<8}") - print("-" * 80) - for i, scenario in enumerate(scenarios): - grid_text = f"{grid_feed_in[i]:.3f}" if grid_feed_in[i] >= 0 else f"{abs(grid_feed_in[i]):.3f}↓" - limit_reached = "*" if (data4['result']['capacity_limit_reached'] and i == 3) or (data5['result']['capacity_limit_reached'] and i == 4) or (data6['result']['max_storage_limit'] and i == 5) else "" - print(f"{scenario:<15} {storage_capacities[i]:<12.2f} {capacity_limit[i]:<10} {curtailment_wind[i]:<8.3f} " - f"{curtailment_solar[i]:<8.3f} {grid_text:<8} {limit_reached}") - - return data1, data2, data3, data4, data5, data6 - - -if __name__ == "__main__": - print("多能互补系统储能容量优化计算程序示例") - print("=" * 50) - - # 运行示例 - data1, data2, data3, data4, data5, data6 = compare_scenarios() - - # 绘制图表(如果matplotlib可用) - try: - plot_results(data1['result'], "基础场景储能运行情况", - data1['solar_output'], data1['wind_output'], - data1['thermal_output'], data1['load_demand']) - plot_results(data2['result'], "高可再生能源场景储能运行情况", - data2['solar_output'], data2['wind_output'], - data2['thermal_output'], data2['load_demand']) - plot_results(data3['result'], "冬季场景储能运行情况", - data3['solar_output'], data3['wind_output'], - data3['thermal_output'], data3['load_demand']) - plot_results(data4['result'], "容量限制场景储能运行情况", - data4['solar_output'], data4['wind_output'], - data4['thermal_output'], data4['load_demand']) - plot_results(data5['result'], "高负荷购电场景储能运行情况", - data5['solar_output'], data5['wind_output'], - data5['thermal_output'], data5['load_demand']) - plot_results(data6['result'], "上网电量比例限制场景储能运行情况", - data6['solar_output'], data6['wind_output'], - data6['thermal_output'], data6['load_demand']) - except ImportError: - print("\n注意: matplotlib未安装,无法绘制图表") - print("要安装matplotlib,请运行: pip install matplotlib") - - print("\n示例运行完成!") diff --git a/solar_optimization.py b/solar_optimization.py deleted file mode 100644 index 88855af..0000000 --- a/solar_optimization.py +++ /dev/null @@ -1,362 +0,0 @@ -""" -光伏出力优化模块 - -该模块通过调整光伏出力曲线的系数,在给定的系统参数条件下 -最小化与电网交换的电量,提高系统的自平衡能力。 - -作者: iFlow CLI -创建日期: 2025-12-26 -""" - -import numpy as np -from typing import List, Dict, Tuple, Optional -from dataclasses import dataclass -from storage_optimization import SystemParameters, optimize_storage_capacity, calculate_energy_balance - - -@dataclass -class SolarOptimizationResult: - """光伏优化结果类""" - optimal_solar_coefficient: float # 最优光伏系数 - original_solar_output: List[float] # 原始光伏出力曲线 - optimized_solar_output: List[float] # 优化后光伏出力曲线 - min_grid_exchange: float # 最小电网交换电量 - grid_purchase: float # 购电量 - grid_feed_in: float # 上网电量 - storage_result: Dict # 储能优化结果 - optimization_history: List[Dict] # 优化历史记录 - - -def calculate_grid_exchange_metric( - solar_output: List[float], - wind_output: List[float], - thermal_output: List[float], - load_demand: List[float], - params: SystemParameters -) -> Dict[str, float]: - """ - 计算电网交换电量指标 - - Args: - solar_output: 光伏出力曲线 (MW) - wind_output: 风电出力曲线 (MW) - thermal_output: 火电出力曲线 (MW) - load_demand: 负荷曲线 (MW) - params: 系统参数配置 - - Returns: - 包含电网交换指标的字典 - """ - # 计算最优储能容量 - storage_result = optimize_storage_capacity( - solar_output, wind_output, thermal_output, load_demand, params - ) - - # 计算电网交换电量 - grid_feed_in = storage_result['grid_feed_in'] - - # 分离购电和上网电量 - total_purchase = sum(-x for x in grid_feed_in if x < 0) # 购电量(正值) - total_feed_in = sum(x for x in grid_feed_in if x > 0) # 上网电量(正值) - - # 计算总交换电量(购电 + 上网) - total_exchange = total_purchase + total_feed_in - - return { - 'total_exchange': total_exchange, - 'grid_purchase': total_purchase, - 'grid_feed_in': total_feed_in, - 'storage_capacity': storage_result['required_storage_capacity'], - 'storage_result': storage_result - } - - -def optimize_solar_output( - original_solar_output: List[float], - wind_output: List[float], - thermal_output: List[float], - load_demand: List[float], - params: SystemParameters, - coefficient_range: Tuple[float, float] = (0.1, 2.0), - tolerance: float = 0.01, - max_iterations: int = 50 -) -> SolarOptimizationResult: - """ - 优化光伏出力系数以最小化电网交换电量 - - Args: - original_solar_output: 原始光伏出力曲线 (MW) - wind_output: 风电出力曲线 (MW) - thermal_output: 火电出力曲线 (MW) - load_demand: 负荷曲线 (MW) - params: 系统参数配置 - coefficient_range: 光伏系数搜索范围 (最小值, 最大值) - tolerance: 收敛容差 - max_iterations: 最大迭代次数 - - Returns: - 光伏优化结果 - """ - print("开始光伏出力优化...") - - # 初始化优化历史 - optimization_history = [] - - # 使用黄金分割法进行一维优化 - phi = (1 + np.sqrt(5)) / 2 # 黄金比例 - resphi = 2 - phi - - a, b = coefficient_range - c = b - resphi * (b - a) - d = a + resphi * (b - a) - - # 计算初始点的目标函数值 - fc = calculate_grid_exchange_metric( - [x * c for x in original_solar_output], - wind_output, thermal_output, load_demand, params - ) - - fd = calculate_grid_exchange_metric( - [x * d for x in original_solar_output], - wind_output, thermal_output, load_demand, params - ) - - # 记录初始点 - optimization_history.append({ - 'coefficient': c, - 'total_exchange': fc['total_exchange'], - 'grid_purchase': fc['grid_purchase'], - 'grid_feed_in': fc['grid_feed_in'], - 'storage_capacity': fc['storage_capacity'] - }) - - optimization_history.append({ - 'coefficient': d, - 'total_exchange': fd['total_exchange'], - 'grid_purchase': fd['grid_purchase'], - 'grid_feed_in': fd['grid_feed_in'], - 'storage_capacity': fd['storage_capacity'] - }) - - # 黄金分割搜索 - for iteration in range(max_iterations): - if abs(fc['total_exchange'] - fd['total_exchange']) < tolerance: - break - - if fc['total_exchange'] < fd['total_exchange']: - b = d - d = c - fd = fc - c = b - resphi * (b - a) - - fc = calculate_grid_exchange_metric( - [x * c for x in original_solar_output], - wind_output, thermal_output, load_demand, params - ) - - optimization_history.append({ - 'coefficient': c, - 'total_exchange': fc['total_exchange'], - 'grid_purchase': fc['grid_purchase'], - 'grid_feed_in': fc['grid_feed_in'], - 'storage_capacity': fc['storage_capacity'] - }) - else: - a = c - c = d - fc = fd - d = a + resphi * (b - a) - - fd = calculate_grid_exchange_metric( - [x * d for x in original_solar_output], - wind_output, thermal_output, load_demand, params - ) - - optimization_history.append({ - 'coefficient': d, - 'total_exchange': fd['total_exchange'], - 'grid_purchase': fd['grid_purchase'], - 'grid_feed_in': fd['grid_feed_in'], - 'storage_capacity': fd['storage_capacity'] - }) - - # 确定最优系数 - if fc['total_exchange'] < fd['total_exchange']: - optimal_coefficient = c - best_result = fc - else: - optimal_coefficient = d - best_result = fd - - # 生成优化后的光伏出力曲线 - optimized_solar_output = [x * optimal_coefficient for x in original_solar_output] - - # 重新计算完整的最优储能配置 - final_storage_result = optimize_storage_capacity( - optimized_solar_output, wind_output, thermal_output, load_demand, params - ) - - print(f"优化完成!最优光伏系数: {optimal_coefficient:.3f}") - print(f"最小电网交换电量: {best_result['total_exchange']:.2f} MWh") - - return SolarOptimizationResult( - optimal_solar_coefficient=optimal_coefficient, - original_solar_output=original_solar_output, - optimized_solar_output=optimized_solar_output, - min_grid_exchange=best_result['total_exchange'], - grid_purchase=best_result['grid_purchase'], - grid_feed_in=best_result['grid_feed_in'], - storage_result=final_storage_result, - optimization_history=optimization_history - ) - - - - - -def export_optimization_results(result: SolarOptimizationResult, filename: str = None): - """ - 导出光伏优化结果到Excel文件 - - Args: - result: 光伏优化结果 - filename: 输出文件名,如果为None则自动生成 - """ - import pandas as pd - from datetime import datetime - - if filename is None: - timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") - filename = f"solar_optimization_results_{timestamp}.xlsx" - - print(f"正在导出光伏优化结果到Excel文件: {filename}") - - hours = list(range(1, len(result.original_solar_output) + 1)) - - # 创建主要数据DataFrame - data_df = pd.DataFrame({ - '小时': hours, - '原始光伏出力(MW)': result.original_solar_output, - '优化后光伏出力(MW)': result.optimized_solar_output, - '出力变化(MW)': [result.optimized_solar_output[i] - result.original_solar_output[i] - for i in range(len(result.original_solar_output))], - '变化比例(%)': [(result.optimized_solar_output[i] / result.original_solar_output[i] - 1) * 100 - if result.original_solar_output[i] > 0 else 0 - for i in range(len(result.original_solar_output))] - }) - - # 创建优化结果摘要DataFrame - summary_df = pd.DataFrame({ - '指标': [ - '最优光伏系数', - '最小电网交换电量', - '购电量', - '上网电量', - '所需储能容量', - '优化后弃风率', - '优化后弃光率', - '优化后上网电量比例' - ], - '数值': [ - f"{result.optimal_solar_coefficient:.3f}", - f"{result.min_grid_exchange:.2f} MWh", - f"{result.grid_purchase:.2f} MWh", - f"{result.grid_feed_in:.2f} MWh", - f"{result.storage_result['required_storage_capacity']:.2f} MWh", - f"{result.storage_result['total_curtailment_wind_ratio']:.3f}", - f"{result.storage_result['total_curtailment_solar_ratio']:.3f}", - f"{result.storage_result['total_grid_feed_in_ratio']:.3f}" - ] - }) - - # 创建优化历史DataFrame - history_df = pd.DataFrame(result.optimization_history) - history_df.columns = ['光伏系数', '电网交换电量(MWh)', '购电量(MWh)', '上网电量(MWh)', '储能容量(MWh)'] - - # 写入Excel文件 - with pd.ExcelWriter(filename, engine='openpyxl') as writer: - # 写入主要数据 - data_df.to_excel(writer, sheet_name='出力曲线对比', index=False) - - # 写入优化结果摘要 - summary_df.to_excel(writer, sheet_name='优化结果摘要', index=False) - - # 写入优化历史 - history_df.to_excel(writer, sheet_name='优化历史', index=False) - - # 创建说明工作表 - description_df = pd.DataFrame({ - '项目': [ - '文件说明', - '生成时间', - '优化目标', - '优化方法', - '数据长度', - '注意事项' - ], - '内容': [ - '光伏出力优化结果 - 通过调整光伏系数最小化电网交换电量', - datetime.now().strftime("%Y-%m-%d %H:%M:%S"), - '最小化与电网交换的总电量(购电 + 上网)', - '黄金分割一维优化算法', - f"{len(result.original_solar_output)} 小时", - '优化结果在给定的系统参数约束下得出' - ] - }) - description_df.to_excel(writer, sheet_name='说明', index=False) - - print(f"光伏优化结果已成功导出到: {filename}") - return filename - - -def main(): - """主函数,提供示例使用""" - # 示例数据 - original_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, - rated_thermal_capacity=100.0, - rated_solar_capacity=100.0, - rated_wind_capacity=100.0, - available_thermal_energy=2400.0, - available_solar_energy=600.0, - available_wind_energy=1200.0 - ) - - # 执行光伏优化 - result = optimize_solar_output( - original_solar_output, wind_output, thermal_output, load_demand, params - ) - - # 打印结果 - print("\n=== 光伏出力优化结果 ===") - print(f"最优光伏系数: {result.optimal_solar_coefficient:.3f}") - print(f"最小电网交换电量: {result.min_grid_exchange:.2f} MWh") - print(f"其中购电量: {result.grid_purchase:.2f} MWh") - print(f"其中上网电量: {result.grid_feed_in:.2f} MWh") - print(f"所需储能容量: {result.storage_result['required_storage_capacity']:.2f} MWh") - print(f"优化后弃风率: {result.storage_result['total_curtailment_wind_ratio']:.3f}") - print(f"优化后弃光率: {result.storage_result['total_curtailment_solar_ratio']:.3f}") - print(f"优化后上网电量比例: {result.storage_result['total_grid_feed_in_ratio']:.3f}") - - - - # 导出结果 - export_optimization_results(result) - - return result - - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/solar_optimization_examples.py b/solar_optimization_examples.py deleted file mode 100644 index 3c9c905..0000000 --- a/solar_optimization_examples.py +++ /dev/null @@ -1,537 +0,0 @@ -""" -光伏优化模块场景示例 - -该文件展示了光伏优化模块在不同场景下的应用,包括: -1. 典型日场景 - 基础优化示例 -2. 高负荷场景 - 夏季高峰用电场景 -3. 低负荷场景 - 春秋季低负荷场景 -4. 风光互补场景 - 风电和光伏协同优化 -5. 储能受限场景 - 储能容量受限情况下的优化 - -作者: iFlow CLI -创建日期: 2025-12-26 -""" - -import numpy as np -import matplotlib.pyplot as plt -from typing import List, Dict -from solar_optimization import optimize_solar_output, plot_optimization_results, export_optimization_results -from storage_optimization import SystemParameters - - -def scenario_1_typical_day(): - """ - 场景1:典型日场景 - - 标准24小时负荷曲线 - - 适中风光出力 - - 常规系统参数 - """ - print("=" * 60) - print("场景1:典型日场景 - 基础优化示例") - print("=" * 60) - - # 典型日光伏出力(中午高峰) - solar_output = [0.0] * 6 + [0.5, 1.0, 2.0, 3.5, 5.0, 6.0, 5.5, 4.0, 2.5, 1.0, 0.5, 0.0] + [0.0] * 6 - - # 典型日风电出力(夜间和早晨较高) - wind_output = [4.0, 5.0, 4.5, 3.5, 2.5, 2.0, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 2.0, 3.0, 4.0, 5.0, 4.5, 4.0, 3.5, 3.0, 2.5, 2.0, 1.5, 1.0] - - # 火电基础出力 - thermal_output = [8.0] * 24 - - # 典型日负荷曲线(早晚高峰) - load_demand = [2.0, 2.5, 3.0, 4.0, 6.0, 9.0, 12.0, 15.0, 18.0, 20.0, 19.0, 18.0, - 17.0, 16.0, 18.0, 19.0, 20.0, 18.0, 15.0, 12.0, 8.0, 5.0, 3.0, 2.0] - - # 标准系统参数 - params = SystemParameters( - max_curtailment_wind=0.1, - max_curtailment_solar=0.1, - max_grid_ratio=0.15, - storage_efficiency=0.9, - discharge_rate=1.0, - charge_rate=1.0, - rated_thermal_capacity=100.0, - rated_solar_capacity=50.0, - rated_wind_capacity=50.0, - available_thermal_energy=2000.0, - available_solar_energy=400.0, - available_wind_energy=600.0 - ) - - # 执行优化 - result = optimize_solar_output( - solar_output, wind_output, thermal_output, load_demand, params - ) - - # 输出结果 - print_scenario_result("典型日场景", result) - - # 绘制结果 - plot_optimization_results(result, show_window=False) - - # 导出结果 - filename = export_optimization_results(result, "scenario_1_typical_day.xlsx") - - return result - - -def scenario_2_high_load(): - """ - 场景2:高负荷场景 - - 夏季高温,空调负荷高 - - 白天负荷特别高 - - 光伏出力与负荷匹配度较低 - """ - print("=" * 60) - print("场景2:高负荷场景 - 夏季高峰用电") - print("=" * 60) - - # 夏季光伏出力(较强) - solar_output = [0.0] * 5 + [0.8, 1.5, 3.0, 4.5, 6.0, 7.5, 8.0, 7.0, 5.0, 3.0, 1.5, 0.5, 0.0, 0.0] + [0.0] * 5 - - # 夏季风电出力(相对较低) - wind_output = [2.0, 2.5, 3.0, 2.5, 2.0, 1.5, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.5, 2.0, 2.5, 3.0, 2.5, 2.0, 1.8, 1.6, 1.4, 1.2, 1.0, 0.8] - - # 火电高峰出力 - thermal_output = [12.0] * 24 - - # 夏季高负荷曲线(空调导致白天负荷极高) - load_demand = [3.0, 3.5, 4.0, 5.0, 8.0, 12.0, 18.0, 25.0, 30.0, 32.0, 31.0, 30.0, - 29.0, 28.0, 30.0, 31.0, 32.0, 28.0, 22.0, 18.0, 12.0, 8.0, 5.0, 3.0] - - # 高负荷场景参数(更宽松的弃风弃光限制) - params = SystemParameters( - max_curtailment_wind=0.15, - max_curtailment_solar=0.15, - max_grid_ratio=0.25, - storage_efficiency=0.85, - discharge_rate=1.2, - charge_rate=1.2, - rated_thermal_capacity=150.0, - rated_solar_capacity=80.0, - rated_wind_capacity=40.0, - available_thermal_energy=3000.0, - available_solar_energy=600.0, - available_wind_energy=400.0 - ) - - # 执行优化 - result = optimize_solar_output( - solar_output, wind_output, thermal_output, load_demand, params - ) - - # 输出结果 - print_scenario_result("高负荷场景", result) - - # 绘制结果 - plot_optimization_results(result, show_window=False) - - # 导出结果 - filename = export_optimization_results(result, "scenario_2_high_load.xlsx") - - return result - - -def scenario_3_low_load(): - """ - 场景3:低负荷场景 - - 春秋季,负荷较低 - - 光伏出力相对较高 - - 容易出现电力盈余 - """ - print("=" * 60) - print("场景3:低负荷场景 - 春秋季低负荷") - print("=" * 60) - - # 春秋季光伏出力(适中) - solar_output = [0.0] * 6 + [1.0, 2.0, 3.5, 5.0, 6.5, 7.0, 6.5, 5.0, 3.5, 2.0, 1.0, 0.0] + [0.0] * 6 - - # 春秋季风电出力(较好) - wind_output = [5.0, 6.0, 5.5, 4.5, 3.5, 3.0, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 3.0, 4.0, 5.0, 6.0, 5.5, 5.0, 4.5, 4.0, 3.5, 3.0, 2.5, 2.0] - - # 火电基础出力(较低) - thermal_output = [5.0] * 24 - - # 春秋季低负荷曲线 - load_demand = [2.0, 2.2, 2.5, 3.0, 4.0, 6.0, 8.0, 10.0, 12.0, 13.0, 12.5, 12.0, - 11.5, 11.0, 12.0, 12.5, 13.0, 11.0, 9.0, 7.0, 5.0, 3.5, 2.5, 2.0] - - # 低负荷场景参数(更严格的弃风弃光限制) - params = SystemParameters( - max_curtailment_wind=0.05, - max_curtailment_solar=0.05, - max_grid_ratio=0.1, - storage_efficiency=0.92, - discharge_rate=0.8, - charge_rate=0.8, - rated_thermal_capacity=80.0, - rated_solar_capacity=60.0, - rated_wind_capacity=60.0, - available_thermal_energy=1500.0, - available_solar_energy=500.0, - available_wind_energy=700.0 - ) - - # 执行优化 - result = optimize_solar_output( - solar_output, wind_output, thermal_output, load_demand, params - ) - - # 输出结果 - print_scenario_result("低负荷场景", result) - - # 绘制结果 - plot_optimization_results(result, show_window=False) - - # 导出结果 - filename = export_optimization_results(result, "scenario_3_low_load.xlsx") - - return result - - -def scenario_4_wind_solar_complement(): - """ - 场景4:风光互补场景 - - 风电和光伏出力时间互补性强 - - 夜间风电高,白天光伏高 - - 系统整体平衡性较好 - """ - print("=" * 60) - print("场景4:风光互补场景 - 风电和光伏协同优化") - print("=" * 60) - - # 光伏出力(标准日间模式) - solar_output = [0.0] * 6 + [0.5, 1.5, 3.0, 4.5, 6.0, 7.0, 6.0, 4.5, 3.0, 1.5, 0.5, 0.0] + [0.0] * 6 - - # 风电出力(与光伏互补,夜间和早晚较高) - wind_output = [8.0, 9.0, 8.5, 7.0, 5.0, 3.0, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 3.0, 5.0, 7.0, 8.0, 8.5, 8.0, 7.5, 7.0, 6.5, 6.0, 5.5, 5.0] - - # 火电出力(作为补充) - thermal_output = [6.0] * 24 - - # 负荷曲线(相对平稳) - load_demand = [4.0, 4.5, 5.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0, 17.0, 16.5, 16.0, - 15.5, 15.0, 16.0, 16.5, 17.0, 15.0, 13.0, 11.0, 9.0, 7.0, 5.0, 4.0] - - # 风光互补场景参数 - params = SystemParameters( - max_curtailment_wind=0.08, - max_curtailment_solar=0.08, - max_grid_ratio=0.12, - storage_efficiency=0.9, - discharge_rate=1.0, - charge_rate=1.0, - rated_thermal_capacity=100.0, - rated_solar_capacity=70.0, - rated_wind_capacity=70.0, - available_thermal_energy=1800.0, - available_solar_energy=450.0, - available_wind_energy=800.0 - ) - - # 执行优化 - result = optimize_solar_output( - solar_output, wind_output, thermal_output, load_demand, params - ) - - # 输出结果 - print_scenario_result("风光互补场景", result) - - # 绘制结果 - plot_optimization_results(result, show_window=False) - - # 导出结果 - filename = export_optimization_results(result, "scenario_4_wind_solar_complement.xlsx") - - return result - - -def scenario_5_storage_limited(): - """ - 场景5:储能受限场景 - - 储能容量受限 - - 需要更精确的光伏出力调节 - - 对电网交换更敏感 - """ - print("=" * 60) - print("场景5:储能受限场景 - 储能容量受限情况下的优化") - print("=" * 60) - - # 标准光伏出力 - solar_output = [0.0] * 6 + [1.0, 2.0, 3.0, 4.5, 6.0, 7.0, 6.0, 4.5, 3.0, 2.0, 1.0, 0.0] + [0.0] * 6 - - # 标准风电出力 - wind_output = [3.0, 4.0, 3.5, 3.0, 2.5, 2.0, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 2.0, 3.0, 3.5, 4.0, 3.5, 3.0, 2.8, 2.6, 2.4, 2.2, 2.0, 1.8] - - # 火电出力 - thermal_output = [7.0] * 24 - - # 标准负荷曲线 - load_demand = [3.0, 3.5, 4.0, 5.0, 7.0, 10.0, 13.0, 16.0, 18.0, 19.0, 18.5, 18.0, - 17.5, 17.0, 18.0, 18.5, 19.0, 17.0, 14.0, 11.0, 8.0, 6.0, 4.0, 3.0] - - # 储能受限场景参数(储能容量限制为50MWh) - params = SystemParameters( - max_curtailment_wind=0.12, - max_curtailment_solar=0.12, - max_grid_ratio=0.2, - storage_efficiency=0.88, - discharge_rate=1.5, - charge_rate=1.5, - max_storage_capacity=50.0, # 储能容量受限 - rated_thermal_capacity=100.0, - rated_solar_capacity=60.0, - rated_wind_capacity=50.0, - available_thermal_energy=2000.0, - available_solar_energy=480.0, - available_wind_energy=550.0 - ) - - # 执行优化 - result = optimize_solar_output( - solar_output, wind_output, thermal_output, load_demand, params - ) - - # 输出结果 - print_scenario_result("储能受限场景", result) - - # 绘制结果 - plot_optimization_results(result, show_window=False) - - # 导出结果 - filename = export_optimization_results(result, "scenario_5_storage_limited.xlsx") - - return result - - -def print_scenario_result(scenario_name: str, result): - """ - 打印场景优化结果 - - Args: - scenario_name: 场景名称 - result: 优化结果 - """ - print(f"\n=== {scenario_name}优化结果 ===") - print(f"最优光伏系数: {result.optimal_solar_coefficient:.3f}") - print(f"最小电网交换电量: {result.min_grid_exchange:.2f} MWh") - print(f" - 购电量: {result.grid_purchase:.2f} MWh") - print(f" - 上网电量: {result.grid_feed_in:.2f} MWh") - print(f"所需储能容量: {result.storage_result['required_storage_capacity']:.2f} MWh") - print(f"优化后弃风率: {result.storage_result['total_curtailment_wind_ratio']:.3f}") - print(f"优化后弃光率: {result.storage_result['total_curtailment_solar_ratio']:.3f}") - print(f"优化后上网电量比例: {result.storage_result['total_grid_feed_in_ratio']:.3f}") - - # 分析优化效果 - if result.optimal_solar_coefficient > 1.0: - print(f"分析:建议将光伏出力提高 {(result.optimal_solar_coefficient - 1.0) * 100:.1f}% 以减少电网依赖") - elif result.optimal_solar_coefficient < 1.0: - print(f"分析:建议将光伏出力降低 {(1.0 - result.optimal_solar_coefficient) * 100:.1f}% 以避免电力过剩") - else: - print("分析:当前光伏出力已经是最优配置") - - -def compare_scenarios(results: List[Dict]): - """ - 对比不同场景的优化结果 - - Args: - results: 各场景优化结果列表 - """ - print("\n" + "=" * 80) - print("场景对比分析") - print("=" * 80) - - scenario_names = [ - "典型日场景", - "高负荷场景", - "低负荷场景", - "风光互补场景", - "储能受限场景" - ] - - # 创建对比表格 - print(f"{'场景名称':<12} {'最优系数':<8} {'电网交换(MWh)':<12} {'购电量(MWh)':<10} {'上网电量(MWh)':<12} {'储能容量(MWh)':<12}") - print("-" * 80) - - for i, (name, result) in enumerate(zip(scenario_names, results)): - print(f"{name:<12} {result.optimal_solar_coefficient:<8.3f} " - f"{result.min_grid_exchange:<12.2f} {result.grid_purchase:<10.2f} " - f"{result.grid_feed_in:<12.2f} {result.storage_result['required_storage_capacity']:<12.2f}") - - # 分析趋势 - print("\n=== 趋势分析 ===") - - # 找出最优和最差场景 - min_exchange_result = min(results, key=lambda x: x.min_grid_exchange) - max_exchange_result = max(results, key=lambda x: x.min_grid_exchange) - - min_exchange_idx = results.index(min_exchange_result) - max_exchange_idx = results.index(max_exchange_result) - - print(f"电网交换最小场景:{scenario_names[min_exchange_idx]} ({min_exchange_result.min_grid_exchange:.2f} MWh)") - print(f"电网交换最大场景:{scenario_names[max_exchange_idx]} ({max_exchange_result.min_grid_exchange:.2f} MWh)") - - # 分析光伏系数趋势 - avg_coefficient = sum(r.optimal_solar_coefficient for r in results) / len(results) - print(f"平均最优光伏系数:{avg_coefficient:.3f}") - - high_coefficient_scenarios = [name for name, result in zip(scenario_names, results) - if result.optimal_solar_coefficient > avg_coefficient] - low_coefficient_scenarios = [name for name, result in zip(scenario_names, results) - if result.optimal_solar_coefficient < avg_coefficient] - - if high_coefficient_scenarios: - print(f"需要提高光伏出力的场景:{', '.join(high_coefficient_scenarios)}") - if low_coefficient_scenarios: - print(f"需要降低光伏出力的场景:{', '.join(low_coefficient_scenarios)}") - - -def plot_scenario_comparison(results: List[Dict]): - """ - 绘制场景对比图表 - - Args: - results: 各场景优化结果列表 - """ - scenario_names = [ - "典型日", - "高负荷", - "低负荷", - "风光互补", - "储能受限" - ] - - # 设置中文字体 - plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'DejaVu Sans'] - plt.rcParams['axes.unicode_minus'] = False - - # 创建图形 - fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(16, 12)) - fig.suptitle('光伏优化场景对比分析', fontsize=16, fontweight='bold') - - # 1. 最优光伏系数对比 - coefficients = [r.optimal_solar_coefficient for r in results] - bars1 = ax1.bar(scenario_names, coefficients, color='skyblue', alpha=0.7) - ax1.set_ylabel('最优光伏系数') - ax1.set_title('各场景最优光伏系数对比') - ax1.grid(True, alpha=0.3, axis='y') - ax1.axhline(y=1.0, color='red', linestyle='--', alpha=0.7, label='原始系数') - - # 添加数值标签 - for bar, coeff in zip(bars1, coefficients): - height = bar.get_height() - ax1.text(bar.get_x() + bar.get_width()/2., height + 0.01, - f'{coeff:.3f}', ha='center', va='bottom', fontweight='bold') - - # 2. 电网交换电量对比 - exchanges = [r.min_grid_exchange for r in results] - purchases = [r.grid_purchase for r in results] - feed_ins = [r.grid_feed_in for r in results] - - x = np.arange(len(scenario_names)) - width = 0.25 - - bars2 = ax2.bar(x - width, purchases, width, label='购电量', color='purple', alpha=0.7) - bars3 = ax2.bar(x, feed_ins, width, label='上网电量', color='brown', alpha=0.7) - bars4 = ax2.bar(x + width, exchanges, width, label='总交换电量', color='orange', alpha=0.7) - - ax2.set_ylabel('电量 (MWh)') - ax2.set_title('电网交换电量对比') - ax2.set_xticks(x) - ax2.set_xticklabels(scenario_names) - ax2.legend() - ax2.grid(True, alpha=0.3, axis='y') - - # 3. 储能容量需求对比 - storage_capacities = [r.storage_result['required_storage_capacity'] for r in results] - bars5 = ax3.bar(scenario_names, storage_capacities, color='green', alpha=0.7) - ax3.set_ylabel('储能容量 (MWh)') - ax3.set_title('各场景储能容量需求对比') - ax3.grid(True, alpha=0.3, axis='y') - - # 添加数值标签 - for bar, capacity in zip(bars5, storage_capacities): - height = bar.get_height() - ax3.text(bar.get_x() + bar.get_width()/2., height + height*0.01, - f'{capacity:.1f}', ha='center', va='bottom', fontweight='bold') - - # 4. 弃风弃光率对比 - curtailment_winds = [r.storage_result['total_curtailment_wind_ratio'] for r in results] - curtailment_solars = [r.storage_result['total_curtailment_solar_ratio'] for r in results] - - bars6 = ax4.bar(x - width/2, curtailment_winds, width, label='弃风率', color='blue', alpha=0.7) - bars7 = ax4.bar(x + width/2, curtailment_solars, width, label='弃光率', color='orange', alpha=0.7) - - ax4.set_ylabel('弃风弃光率') - ax4.set_title('各场景弃风弃光率对比') - ax4.set_xticks(x) - ax4.set_xticklabels(scenario_names) - ax4.legend() - ax4.grid(True, alpha=0.3, axis='y') - - # 调整布局 - plt.tight_layout() - - # 保存图片 - plt.savefig('solar_optimization_scenario_comparison.png', dpi=300, bbox_inches='tight') - plt.close() - - print("场景对比图表已保存为 'solar_optimization_scenario_comparison.png'") - - -def main(): - """主函数,运行所有场景示例""" - print("光伏优化模块场景示例") - print("运行5个不同场景的优化分析...") - - # 运行所有场景 - results = [] - - try: - # 场景1:典型日场景 - result1 = scenario_1_typical_day() - results.append(result1) - - # 场景2:高负荷场景 - result2 = scenario_2_high_load() - results.append(result2) - - # 场景3:低负荷场景 - result3 = scenario_3_low_load() - results.append(result3) - - # 场景4:风光互补场景 - result4 = scenario_4_wind_solar_complement() - results.append(result4) - - # 场景5:储能受限场景 - result5 = scenario_5_storage_limited() - results.append(result5) - - # 对比分析 - compare_scenarios(results) - - # 绘制对比图表 - plot_scenario_comparison(results) - - print("\n" + "=" * 80) - print("所有场景示例运行完成!") - print("=" * 80) - print("生成的文件:") - print("- scenario_1_typical_day.xlsx") - print("- scenario_2_high_load.xlsx") - print("- scenario_3_low_load.xlsx") - print("- scenario_4_wind_solar_complement.xlsx") - print("- scenario_5_storage_limited.xlsx") - print("- solar_optimization_scenario_comparison.png") - - except Exception as e: - print(f"运行场景示例时出错:{str(e)}") - import traceback - traceback.print_exc() - - -if __name__ == "__main__": - main() diff --git a/solar_scenarios_demo.py b/solar_scenarios_demo.py deleted file mode 100644 index daac38f..0000000 --- a/solar_scenarios_demo.py +++ /dev/null @@ -1,414 +0,0 @@ -""" -光伏优化模块场景演示 - -该文件展示了光伏优化模块在不同场景下的应用,包括: -1. 典型日场景 - 基础优化示例 -2. 高负荷场景 - 夏季高峰用电场景 -3. 低负荷场景 - 春秋季低负荷场景 -4. 风光互补场景 - 风电和光伏协同优化 -5. 储能受限场景 - 储能容量受限情况下的优化 - -作者: iFlow CLI -创建日期: 2025-12-26 -""" - -from solar_optimization import optimize_solar_output, export_optimization_results -import matplotlib.pyplot as plt -from storage_optimization import SystemParameters - - -def scenario_1_typical_day(): - """场景1:典型日场景""" - print("=" * 60) - print("场景1:典型日场景 - 基础优化示例") - print("=" * 60) - - # 典型日数据(24小时) - solar_output = [0.0] * 6 + [0.5, 1.0, 2.0, 3.5, 5.0, 6.0, 5.5, 4.0, 2.5, 1.0, 0.5, 0.0] + [0.0] * 6 - wind_output = [4.0, 5.0, 4.5, 3.5, 2.5, 2.0, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 2.0, 3.0, 4.0, 5.0, 4.5, 4.0, 3.5, 3.0, 2.5, 2.0, 1.5, 1.0] - thermal_output = [8.0] * 24 - load_demand = [2.0, 2.5, 3.0, 4.0, 6.0, 9.0, 12.0, 15.0, 18.0, 20.0, 19.0, 18.0, - 17.0, 16.0, 18.0, 19.0, 20.0, 18.0, 15.0, 12.0, 8.0, 5.0, 3.0, 2.0] - - # 系统参数 - params = SystemParameters( - max_curtailment_wind=0.1, - max_curtailment_solar=0.1, - max_grid_ratio=0.15, - storage_efficiency=0.9, - discharge_rate=1.0, - charge_rate=1.0, - rated_thermal_capacity=100.0, - rated_solar_capacity=50.0, - rated_wind_capacity=50.0, - available_thermal_energy=2000.0, - available_solar_energy=400.0, - available_wind_energy=600.0 - ) - - # 执行优化 - result = optimize_solar_output(solar_output, wind_output, thermal_output, load_demand, params) - - # 输出结果 - print_scenario_result("典型日场景", result) - - # 绘制光伏对比图 - plot_solar_comparison(result, "典型日场景") - - # 导出结果 - export_optimization_results(result, "scenario_1_typical_day.xlsx") - - return result - - -def scenario_2_high_load(): - """场景2:高负荷场景""" - print("=" * 60) - print("场景2:高负荷场景 - 夏季高峰用电") - print("=" * 60) - - # 夏季高负荷数据 - solar_output = [0.0] * 5 + [0.8, 1.5, 3.0, 4.5, 6.0, 7.5, 8.0, 7.0, 5.0, 3.0, 1.5, 0.5, 0.0, 0.0] + [0.0] * 5 - wind_output = [2.0, 2.5, 3.0, 2.5, 2.0, 1.5, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.5, 2.0, 2.5, 3.0, 2.5, 2.0, 1.8, 1.6, 1.4, 1.2, 1.0, 0.8] - thermal_output = [12.0] * 24 - load_demand = [3.0, 3.5, 4.0, 5.0, 8.0, 12.0, 18.0, 25.0, 30.0, 32.0, 31.0, 30.0, - 29.0, 28.0, 30.0, 31.0, 32.0, 28.0, 22.0, 18.0, 12.0, 8.0, 5.0, 3.0] - - # 高负荷场景参数 - params = SystemParameters( - max_curtailment_wind=0.15, - max_curtailment_solar=0.15, - max_grid_ratio=0.25, - storage_efficiency=0.85, - discharge_rate=1.2, - charge_rate=1.2, - rated_thermal_capacity=150.0, - rated_solar_capacity=80.0, - rated_wind_capacity=40.0, - available_thermal_energy=3000.0, - available_solar_energy=600.0, - available_wind_energy=400.0 - ) - - # 执行优化 - result = optimize_solar_output(solar_output, wind_output, thermal_output, load_demand, params) - - # 输出结果 - print_scenario_result("高负荷场景", result) - - # 绘制光伏对比图 - plot_solar_comparison(result, "高负荷场景") - - # 导出结果 - export_optimization_results(result, "scenario_2_high_load.xlsx") - - return result - - -def scenario_3_low_load(): - """场景3:低负荷场景""" - print("=" * 60) - print("场景3:低负荷场景 - 春秋季低负荷") - print("=" * 60) - - # 春秋季低负荷数据 - solar_output = [0.0] * 6 + [1.0, 2.0, 3.5, 5.0, 6.5, 7.0, 6.5, 5.0, 3.5, 2.0, 1.0, 0.0] + [0.0] * 6 - wind_output = [5.0, 6.0, 5.5, 4.5, 3.5, 3.0, 2.5, 2.5, 2.5, 2.5, 2.5, 2.5, 3.0, 4.0, 5.0, 6.0, 5.5, 5.0, 4.5, 4.0, 3.5, 3.0, 2.5, 2.0] - thermal_output = [5.0] * 24 - load_demand = [2.0, 2.2, 2.5, 3.0, 4.0, 6.0, 8.0, 10.0, 12.0, 13.0, 12.5, 12.0, - 11.5, 11.0, 12.0, 12.5, 13.0, 11.0, 9.0, 7.0, 5.0, 3.5, 2.5, 2.0] - - # 低负荷场景参数 - params = SystemParameters( - max_curtailment_wind=0.05, - max_curtailment_solar=0.05, - max_grid_ratio=0.1, - storage_efficiency=0.92, - discharge_rate=0.8, - charge_rate=0.8, - rated_thermal_capacity=80.0, - rated_solar_capacity=60.0, - rated_wind_capacity=60.0, - available_thermal_energy=1500.0, - available_solar_energy=500.0, - available_wind_energy=700.0 - ) - - # 执行优化 - result = optimize_solar_output(solar_output, wind_output, thermal_output, load_demand, params) - - # 输出结果 - print_scenario_result("低负荷场景", result) - - # 绘制光伏对比图 - plot_solar_comparison(result, "低负荷场景") - - # 导出结果 - export_optimization_results(result, "scenario_3_low_load.xlsx") - - return result - - -def scenario_4_wind_solar_complement(): - """场景4:风光互补场景""" - print("=" * 60) - print("场景4:风光互补场景 - 风电和光伏协同优化") - print("=" * 60) - - # 风光互补数据 - solar_output = [0.0] * 6 + [0.5, 1.5, 3.0, 4.5, 6.0, 7.0, 6.0, 4.5, 3.0, 1.5, 0.5, 0.0] + [0.0] * 6 - wind_output = [8.0, 9.0, 8.5, 7.0, 5.0, 3.0, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 3.0, 5.0, 7.0, 8.0, 8.5, 8.0, 7.5, 7.0, 6.5, 6.0, 5.5, 5.0] - thermal_output = [6.0] * 24 - load_demand = [4.0, 4.5, 5.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0, 17.0, 16.5, 16.0, - 15.5, 15.0, 16.0, 16.5, 17.0, 15.0, 13.0, 11.0, 9.0, 7.0, 5.0, 4.0] - - # 风光互补场景参数 - params = SystemParameters( - max_curtailment_wind=0.08, - max_curtailment_solar=0.08, - max_grid_ratio=0.12, - storage_efficiency=0.9, - discharge_rate=1.0, - charge_rate=1.0, - rated_thermal_capacity=100.0, - rated_solar_capacity=70.0, - rated_wind_capacity=70.0, - available_thermal_energy=1800.0, - available_solar_energy=450.0, - available_wind_energy=800.0 - ) - - # 执行优化 - result = optimize_solar_output(solar_output, wind_output, thermal_output, load_demand, params) - - # 输出结果 - print_scenario_result("风光互补场景", result) - - # 绘制光伏对比图 - plot_solar_comparison(result, "风光互补场景") - - # 导出结果 - export_optimization_results(result, "scenario_4_wind_solar_complement.xlsx") - - return result - - -def scenario_5_storage_limited(): - """场景5:储能受限场景""" - print("=" * 60) - print("场景5:储能受限场景 - 储能容量受限情况下的优化") - print("=" * 60) - - # 储能受限数据 - solar_output = [0.0] * 6 + [1.0, 2.0, 3.0, 4.5, 6.0, 7.0, 6.0, 4.5, 3.0, 2.0, 1.0, 0.0] + [0.0] * 6 - wind_output = [3.0, 4.0, 3.5, 3.0, 2.5, 2.0, 1.5, 1.5, 1.5, 1.5, 1.5, 1.5, 2.0, 3.0, 3.5, 4.0, 3.5, 3.0, 2.8, 2.6, 2.4, 2.2, 2.0, 1.8] - thermal_output = [7.0] * 24 - load_demand = [3.0, 3.5, 4.0, 5.0, 7.0, 10.0, 13.0, 16.0, 18.0, 19.0, 18.5, 18.0, - 17.5, 17.0, 18.0, 18.5, 19.0, 17.0, 14.0, 11.0, 8.0, 6.0, 4.0, 3.0] - - # 储能受限场景参数 - params = SystemParameters( - max_curtailment_wind=0.12, - max_curtailment_solar=0.12, - max_grid_ratio=0.2, - storage_efficiency=0.88, - discharge_rate=1.5, - charge_rate=1.5, - max_storage_capacity=50.0, # 储能容量受限 - rated_thermal_capacity=100.0, - rated_solar_capacity=60.0, - rated_wind_capacity=50.0, - available_thermal_energy=2000.0, - available_solar_energy=480.0, - available_wind_energy=550.0 - ) - - # 执行优化 - result = optimize_solar_output(solar_output, wind_output, thermal_output, load_demand, params) - - # 输出结果 - print_scenario_result("储能受限场景", result) - - # 绘制光伏对比图 - plot_solar_comparison(result, "储能受限场景") - - # 导出结果 - export_optimization_results(result, "scenario_5_storage_limited.xlsx") - - return result - - -def print_scenario_result(scenario_name: str, result): - """打印场景优化结果""" - print(f"\n=== {scenario_name}优化结果 ===") - print(f"最优光伏系数: {result.optimal_solar_coefficient:.3f}") - print(f"最小电网交换电量: {result.min_grid_exchange:.2f} MWh") - print(f" - 购电量: {result.grid_purchase:.2f} MWh") - print(f" - 上网电量: {result.grid_feed_in:.2f} MWh") - print(f"所需储能容量: {result.storage_result['required_storage_capacity']:.2f} MWh") - print(f"优化后弃风率: {result.storage_result['total_curtailment_wind_ratio']:.3f}") - print(f"优化后弃光率: {result.storage_result['total_curtailment_solar_ratio']:.3f}") - print(f"优化后上网电量比例: {result.storage_result['total_grid_feed_in_ratio']:.3f}") - - # 分析优化效果 - if result.optimal_solar_coefficient > 1.0: - print(f"分析:建议将光伏出力提高 {(result.optimal_solar_coefficient - 1.0) * 100:.1f}% 以减少电网依赖") - elif result.optimal_solar_coefficient < 1.0: - print(f"分析:建议将光伏出力降低 {(1.0 - result.optimal_solar_coefficient) * 100:.1f}% 以避免电力过剩") - else: - print("分析:当前光伏出力已经是最优配置") - - -def plot_solar_comparison(result, scenario_name, show_window=True): - """ - 绘制光伏出力对比图 - - Args: - result: 光伏优化结果 - scenario_name: 场景名称 - show_window: 是否显示图形窗口 - """ - # 设置中文字体 - plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'DejaVu Sans'] - plt.rcParams['axes.unicode_minus'] = False - - hours = list(range(len(result.original_solar_output))) - - # 创建图形 - fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8)) - fig.suptitle(f'{scenario_name} - 光伏优化结果 (系数: {result.optimal_solar_coefficient:.3f})', - fontsize=14, fontweight='bold') - - # === 第一个子图:光伏出力对比 === - ax1.plot(hours, result.original_solar_output, 'b-', linewidth=2, - label='原始光伏出力', alpha=0.7) - ax1.plot(hours, result.optimized_solar_output, 'r-', linewidth=2, - label=f'优化后光伏出力') - - ax1.set_xlabel('时间 (小时)') - ax1.set_ylabel('功率 (MW)') - ax1.set_title('光伏出力曲线对比') - ax1.legend(loc='upper right') - ax1.grid(True, alpha=0.3) - ax1.set_xlim(0, max(hours)) - - # === 第二个子图:电网交换电量组成 === - categories = ['购电量', '上网电量'] - values = [result.grid_purchase, result.grid_feed_in] - colors = ['purple', 'brown'] - - bars = ax2.bar(categories, values, color=colors, alpha=0.7) - ax2.set_ylabel('电量 (MWh)') - ax2.set_title(f'电网交换电量组成 (总计: {result.min_grid_exchange:.2f} MWh)') - ax2.grid(True, alpha=0.3, axis='y') - - # 在柱状图上添加数值标签 - for bar, value in zip(bars, values): - height = bar.get_height() - ax2.text(bar.get_x() + bar.get_width()/2., height + height*0.01, - f'{value:.2f}', ha='center', va='bottom', fontweight='bold') - - # 调整布局 - plt.tight_layout() - - # 根据参数决定是否显示图形窗口 - if show_window: - try: - plt.show() - except Exception as e: - print(f"无法显示图形窗口:{str(e)}") - else: - plt.close() - - -def compare_scenarios(results): - """对比不同场景的优化结果""" - print("\n" + "=" * 80) - print("场景对比分析") - print("=" * 80) - - scenario_names = [ - "典型日场景", - "高负荷场景", - "低负荷场景", - "风光互补场景", - "储能受限场景" - ] - - # 创建对比表格 - print(f"{'场景名称':<12} {'最优系数':<8} {'电网交换(MWh)':<12} {'购电量(MWh)':<10} {'上网电量(MWh)':<12} {'储能容量(MWh)':<12}") - print("-" * 80) - - for i, (name, result) in enumerate(zip(scenario_names, results)): - print(f"{name:<12} {result.optimal_solar_coefficient:<8.3f} " - f"{result.min_grid_exchange:<12.2f} {result.grid_purchase:<10.2f} " - f"{result.grid_feed_in:<12.2f} {result.storage_result['required_storage_capacity']:<12.2f}") - - # 分析趋势 - print("\n=== 趋势分析 ===") - - # 找出最优和最差场景 - min_exchange_result = min(results, key=lambda x: x.min_grid_exchange) - max_exchange_result = max(results, key=lambda x: x.min_grid_exchange) - - min_exchange_idx = results.index(min_exchange_result) - max_exchange_idx = results.index(max_exchange_result) - - print(f"电网交换最小场景:{scenario_names[min_exchange_idx]} ({min_exchange_result.min_grid_exchange:.2f} MWh)") - print(f"电网交换最大场景:{scenario_names[max_exchange_idx]} ({max_exchange_result.min_grid_exchange:.2f} MWh)") - - # 分析光伏系数趋势 - avg_coefficient = sum(r.optimal_solar_coefficient for r in results) / len(results) - print(f"平均最优光伏系数:{avg_coefficient:.3f}") - - -def main(): - """主函数,运行所有场景示例""" - print("光伏优化模块场景演示") - print("运行5个不同场景的优化分析...") - - # 运行所有场景 - results = [] - - try: - # 场景1:典型日场景 - result1 = scenario_1_typical_day() - results.append(result1) - - # 场景2:高负荷场景 - result2 = scenario_2_high_load() - results.append(result2) - - # 场景3:低负荷场景 - result3 = scenario_3_low_load() - results.append(result3) - - # 场景4:风光互补场景 - result4 = scenario_4_wind_solar_complement() - results.append(result4) - - # 场景5:储能受限场景 - result5 = scenario_5_storage_limited() - results.append(result5) - - # 对比分析 - compare_scenarios(results) - - print("\n" + "=" * 80) - print("所有场景演示完成!") - print("=" * 80) - print("生成的文件:") - print("- scenario_1_typical_day.xlsx") - print("- scenario_2_high_load.xlsx") - print("- scenario_3_low_load.xlsx") - print("- scenario_4_wind_solar_complement.xlsx") - print("- scenario_5_storage_limited.xlsx") - - except Exception as e: - print(f"运行场景演示时出错:{str(e)}") - import traceback - traceback.print_exc() - - -if __name__ == "__main__": - main() diff --git a/src/storage_optimization.py b/src/storage_optimization.py index 515bc05..47037ea 100644 --- a/src/storage_optimization.py +++ b/src/storage_optimization.py @@ -9,6 +9,7 @@ """ import numpy as np +import math from typing import List, Dict, Tuple, Optional from dataclasses import dataclass @@ -548,80 +549,76 @@ def optimize_storage_capacity( print(f"基准容量 {best_capacity:.2f} MWh 的弃电量: {best_curtailed:.2f} MWh") - # 使用黄金分割搜索法寻找最优容量 - # 黄金分割点 - golden_ratio = (3 - 5**0.5) / 2 # 约0.382 + # 对每1MW的容量进行计算,寻找最优容量 + print(f"\n开始对每1MW容量进行精确搜索...") - # 初始化两个测试点 - a = search_lower - b = search_upper + # 计算搜索范围,确保为整数 + start_capacity = int(math.ceil(search_lower)) + end_capacity = int(math.floor(search_upper)) - c = b - golden_ratio * (b - a) - d = a + golden_ratio * (b - a) + print(f"搜索范围: {start_capacity} MWh 到 {end_capacity} MWh") - max_refinement_iterations = 50 - - for iter_num in range(max_refinement_iterations): - # 计算点 c 的弃电量 + # 遍历每个可能的容量值(步长为1MW) + for capacity in range(start_capacity, end_capacity + 1): + # 计算当前容量的弃电量 if is_yearly_data: - result_c = find_periodic_steady_state( + result = find_periodic_steady_state( solar_output, wind_output, thermal_output, load_demand, - params, c + params, capacity ) else: - result_c = calculate_energy_balance( - solar_output, wind_output, thermal_output, load_demand, params, c + result = calculate_energy_balance( + solar_output, wind_output, thermal_output, load_demand, params, capacity ) - curtailed_c = sum(result_c['curtailed_wind']) + sum(result_c['curtailed_solar']) + curtailed = sum(result['curtailed_wind']) + sum(result['curtailed_solar']) - # 计算点 d 的弃电量 - if is_yearly_data: - result_d = find_periodic_steady_state( - solar_output, wind_output, thermal_output, load_demand, - params, d - ) + # 检查约束条件 + constraint_results = check_constraints(solar_output, wind_output, thermal_output, result, params) + + # 检查是否满足约束条件 + total_grid_feed_in = sum(result['grid_feed_in']) + if total_grid_feed_in > 0: + grid_constraint_satisfied = constraint_results['total_grid_feed_in_ratio'] <= params.max_grid_ratio else: - result_d = calculate_energy_balance( - solar_output, wind_output, thermal_output, load_demand, params, d + grid_constraint_satisfied = True + + # 新逻辑:弃光受最大弃光比例限制,弃风不受限制 + solar_constraint_satisfied = constraint_results['total_curtailment_solar_ratio'] <= params.max_curtailment_solar + wind_constraint_satisfied = True # 弃风不受限制 + + grid_quota_zero = abs(params.max_grid_ratio) < 1e-10 + + if grid_quota_zero: + constraints_satisfied = grid_constraint_satisfied and solar_constraint_satisfied + else: + constraints_satisfied = ( + solar_constraint_satisfied and # 弃光受限制 + wind_constraint_satisfied and # 弃风不受限制 + grid_constraint_satisfied # 上网电量受限制 ) - curtailed_d = sum(result_d['curtailed_wind']) + sum(result_d['curtailed_solar']) + # 检查储能日平衡(周期结束时储能状态应接近初始值) + storage_initial = result['storage_profile'][0] + storage_final = result['storage_profile'][-1] + daily_balance = abs(storage_final - storage_initial) < tolerance - # 比较并更新最优解 - if curtailed_c < best_curtailed: - best_curtailed = curtailed_c - best_capacity = c - best_result = {**result_c, **check_constraints(solar_output, wind_output, thermal_output, result_c, params)} - print(f" 发现更优容量: {best_capacity:.2f} MWh, 弃电量: {best_curtailed:.2f} MWh") + # 只有满足约束条件的容量才考虑 + if constraints_satisfied and daily_balance: + # 更新最优解 + if curtailed < best_curtailed: + best_curtailed = curtailed + best_capacity = capacity + best_result = {**result, **constraint_results} + print(f" 发现更优容量: {best_capacity} MWh, 弃电量: {best_curtailed:.2f} MWh") - if curtailed_d < best_curtailed: - best_curtailed = curtailed_d - best_capacity = d - best_result = {**result_d, **check_constraints(solar_output, wind_output, thermal_output, result_d, params)} - print(f" 发现更优容量: {best_capacity:.2f} MWh, 弃电量: {best_curtailed:.2f} MWh") - - # 缩小搜索区间 - if curtailed_c < curtailed_d: - b = d - d = c - c = b - golden_ratio * (b - a) - else: - a = c - c = d - d = a + golden_ratio * (b - a) - - # 检查收敛 - if b - a < tolerance: - print(f"搜索收敛,区间宽度: {b - a:.4f} MWh") - break - - # 限制最大迭代次数 - if iter_num % 10 == 0 and iter_num > 0: - print(f" 已完成 {iter_num} 次迭代,当前搜索区间: [{a:.2f}, {b:.2f}] MWh") + # 每处理10个容量值输出一次进度 + if (capacity - start_capacity + 1) % 10 == 0: + print(f" 已检查 {capacity - start_capacity + 1}/{end_capacity - start_capacity + 1} 个容量值") + + print(f"精确搜索完成,共检查 {end_capacity - start_capacity + 1} 个容量值") # 将储能容量向上取整为整数 - import math rounded_capacity = math.ceil(best_capacity) # 确保不超过容量上限 if params.max_storage_capacity is not None: diff --git a/storage_optimization.py b/storage_optimization.py deleted file mode 100644 index cf45e56..0000000 --- a/storage_optimization.py +++ /dev/null @@ -1,571 +0,0 @@ -""" -多能互补系统储能容量优化计算程序 - -该程序计算多能互补系统中所需的储能容量,确保系统在24小时内电能平衡, -同时满足用户定义的弃风弃光率和上网电量比例约束。 - -作者: iFlow CLI -创建日期: 2025-12-25 -""" - -import numpy as np -from typing import List, Dict, Tuple, Optional -from dataclasses import dataclass - - -@dataclass -class SystemParameters: - """系统参数配置类""" - max_curtailment_wind: float = 0.1 # 最大允许弃风率 (0.0-1.0) - max_curtailment_solar: float = 0.1 # 最大允许弃光率 (0.0-1.0) - max_grid_ratio: float = 0.2 # 最大允许上网电量比例 (0.0-∞,只限制上网电量,不限制购电) - storage_efficiency: float = 0.9 # 储能充放电效率 (0.0-1.0) - discharge_rate: float = 1.0 # 储能放电倍率 (C-rate) - charge_rate: float = 1.0 # 储能充电倍率 (C-rate) - max_storage_capacity: Optional[float] = None # 储能容量上限 (MWh),None表示无限制 - # 新增额定装机容量参数 - rated_thermal_capacity: float = 100.0 # 额定火电装机容量 (MW) - rated_solar_capacity: float = 100.0 # 额定光伏装机容量 (MW) - rated_wind_capacity: float = 100.0 # 额定风电装机容量 (MW) - # 新增可用发电量参数 - available_thermal_energy: float = 2400.0 # 火电可用发电量 (MWh) - available_solar_energy: float = 600.0 # 光伏可用发电量 (MWh) - available_wind_energy: float = 1200.0 # 风电可用发电量 (MWh) - - -def validate_inputs( - solar_output: List[float], - wind_output: List[float], - thermal_output: List[float], - load_demand: List[float], - params: SystemParameters -) -> None: - """ - 验证输入数据的有效性 - - Args: - solar_output: 24小时光伏出力曲线 (MW) - wind_output: 24小时风电出力曲线 (MW) - thermal_output: 24小时火电出力曲线 (MW) - load_demand: 24小时负荷曲线 (MW) - params: 系统参数配置 - - Raises: - ValueError: 当输入数据无效时抛出异常 - """ - # 检查数据长度(支持24小时或8760小时) - data_length = len(solar_output) - valid_lengths = [24, 8760] - - if data_length not in valid_lengths: - raise ValueError(f"输入数据长度必须为24小时或8760小时,当前长度为{data_length}") - - if len(wind_output) != data_length or len(thermal_output) != data_length or len(load_demand) != data_length: - raise ValueError("所有输入数据长度必须一致") - - # 检查数据类型和范围 - for name, data in [ - ("光伏出力", solar_output), ("风电出力", wind_output), - ("火电出力", thermal_output), ("负荷需求", load_demand) - ]: - if not all(isinstance(x, (int, float)) for x in data): - raise ValueError(f"{name}必须包含数值数据") - if any(x < 0 for x in data): - raise ValueError(f"{name}不能包含负值") - - # 检查参数范围 - if not (0.0 <= params.max_curtailment_wind <= 1.0): - raise ValueError("弃风率必须在0.0-1.0之间") - if not (0.0 <= params.max_curtailment_solar <= 1.0): - raise ValueError("弃光率必须在0.0-1.0之间") - # max_grid_ratio只限制上网电量比例,必须为非负值 - if not (0.0 <= params.max_grid_ratio): - raise ValueError("上网电量比例限制必须为非负值") - if not (0.0 < params.storage_efficiency <= 1.0): - raise ValueError("储能效率必须在0.0-1.0之间") - if params.discharge_rate <= 0 or params.charge_rate <= 0: - raise ValueError("充放电倍率必须大于0") - if params.max_storage_capacity is not None and params.max_storage_capacity <= 0: - raise ValueError("储能容量上限必须大于0") - # 验证新增的额定装机容量参数 - if params.rated_thermal_capacity < 0: - raise ValueError("额定火电装机容量必须为非负值") - if params.rated_solar_capacity <= 0: - raise ValueError("额定光伏装机容量必须大于0") - if params.rated_wind_capacity <= 0: - raise ValueError("额定风电装机容量必须大于0") - # 验证新增的可用发电量参数 - if params.available_thermal_energy < 0: - raise ValueError("火电可用发电量必须为非负值") - if params.available_solar_energy < 0: - raise ValueError("光伏可用发电量必须为非负值") - if params.available_wind_energy < 0: - raise ValueError("风电可用发电量必须为非负值") - - -def calculate_energy_balance( - solar_output: List[float], - wind_output: List[float], - thermal_output: List[float], - load_demand: List[float], - params: SystemParameters, - storage_capacity: float -) -> Dict[str, List[float]]: - """ - 计算给定储能容量下的系统电能平衡 - - Args: - solar_output: 光伏出力曲线 (MW) - 支持24小时或8760小时 - wind_output: 风电出力曲线 (MW) - 支持24小时或8760小时 - thermal_output: 火电出力曲线 (MW) - 支持24小时或8760小时 - load_demand: 负荷曲线 (MW) - 支持24小时或8760小时 - params: 系统参数配置 - storage_capacity: 储能容量 (MWh) - - Returns: - 包含各种功率曲线的字典 - """ - # 转换为numpy数组便于计算 - solar = np.array(solar_output) - wind = np.array(wind_output) - thermal = np.array(thermal_output) - load = np.array(load_demand) - - # 初始化输出数组 - hours = len(solar_output) - storage_soc = np.zeros(hours) # 储能状态 (MWh) - charge_power = np.zeros(hours) # 充电功率 (MW) - discharge_power = np.zeros(hours) # 放电功率 (MW) - curtailed_wind = np.zeros(hours) # 弃风量 (MW) - curtailed_solar = np.zeros(hours) # 弃光量 (MW) - grid_feed_in = np.zeros(hours) # 上网电量 (MW) - - # 计算总发电潜力 - total_potential_wind = np.sum(wind) - total_potential_solar = np.sum(solar) - -# 判断是否只有一种可再生能源 - has_wind = total_potential_wind > 0 - has_solar = total_potential_solar > 0 - single_renewable = (has_wind and not has_solar) or (has_solar and not has_wind) - - # 计算允许的最大弃风弃光量 - if single_renewable: - # 只有一种可再生能源时,弃电量不受限制 - max_curtailed_wind_total = float('inf') - max_curtailed_solar_total = float('inf') - elif params.max_grid_ratio == 0: - # 上网电量限制为0时,所有超额电力都必须被弃掉,不受弃风弃光限制 - max_curtailed_wind_total = float('inf') - max_curtailed_solar_total = float('inf') - else: - # 有多种可再生能源且上网电量限制不为0时,应用弃风弃光限制 - max_curtailed_wind_total = total_potential_wind * params.max_curtailment_wind - max_curtailed_solar_total = total_potential_solar * params.max_curtailment_solar - - # 初始化累计弃风弃光量 - accumulated_curtailed_wind = 0.0 - accumulated_curtailed_solar = 0.0 - - # 计算总可用发电量上限(不考虑火电) - total_available_energy = params.available_solar_energy + params.available_wind_energy - max_total_grid_feed_in = total_available_energy * params.max_grid_ratio - - # 初始化累计上网电量 - cumulative_grid_feed_in = 0.0 - - # 逐小时计算 - for hour in range(hours): - # 确保储能状态不为负 - storage_soc[hour] = max(0, storage_soc[hour]) - - # 可用发电量(未考虑弃风弃光) - available_generation = thermal[hour] + wind[hour] + solar[hour] - - # 需求电量(负荷) - demand = load[hour] - - # 计算功率平衡 - power_surplus = available_generation - demand - - if power_surplus > 0: - # 有盈余电力,优先储能 - max_charge = min( - storage_capacity - storage_soc[hour], # 储能空间限制 - storage_capacity * params.charge_rate, # 充电功率限制 - power_surplus # 可用盈余电力 - ) - - # 实际充电功率 - actual_charge = min(max_charge, power_surplus) - charge_power[hour] = actual_charge - - # 更新储能状态(考虑充电效率) - if hour < hours - 1: - storage_soc[hour + 1] = storage_soc[hour] + actual_charge * params.storage_efficiency - - # 剩余电力优先上网,超出上网电量比例限制时才弃风弃光 - remaining_surplus = power_surplus - actual_charge - - # 计算当前允许的最大上网电量 - # 基于总可用发电量和已累计上网电量 - remaining_grid_quota = max_total_grid_feed_in - cumulative_grid_feed_in - - # 优先上网,但不超过剩余配额 - grid_feed_allowed = min(remaining_surplus, max(0, remaining_grid_quota)) - grid_feed_in[hour] = grid_feed_allowed - cumulative_grid_feed_in += grid_feed_allowed - - # 剩余电力考虑弃风弃光 - remaining_surplus -= grid_feed_allowed - - # 计算弃风弃光(优先弃光,然后弃风) - if remaining_surplus > 0: - # 在单一可再生能源场景下,弃风弃光不受限制 - if single_renewable: - # 优先弃光 - if solar[hour] > 0: - curtailed_solar[hour] = min(solar[hour], remaining_surplus) - remaining_surplus -= curtailed_solar[hour] - accumulated_curtailed_solar += curtailed_solar[hour] - - # 如果还有剩余,弃风 - if remaining_surplus > 0 and wind[hour] > 0: - curtailed_wind[hour] = min(wind[hour], remaining_surplus) - remaining_surplus -= curtailed_wind[hour] - accumulated_curtailed_wind += curtailed_wind[hour] - else: - # 混合可再生能源场景,弃风弃光受限制 - # 计算当前可弃光量 - if max_curtailed_solar_total == float('inf'): - # 无限制弃光 - available_solar_curtail = solar[hour] - else: - # 受限制弃光 - available_solar_curtail = min( - solar[hour], - max_curtailed_solar_total - accumulated_curtailed_solar - ) - - if available_solar_curtail > 0: - curtailed_solar[hour] = min(available_solar_curtail, remaining_surplus) - remaining_surplus -= curtailed_solar[hour] - accumulated_curtailed_solar += curtailed_solar[hour] - - # 如果还有剩余,弃风 - if remaining_surplus > 0: - if max_curtailed_wind_total == float('inf'): - # 无限制弃风 - available_wind_curtail = wind[hour] - else: - # 受限制弃风 - available_wind_curtail = min( - wind[hour], - max_curtailed_wind_total - accumulated_curtailed_wind - ) - - if available_wind_curtail > 0: - curtailed_wind[hour] = min(available_wind_curtail, remaining_surplus) - remaining_surplus -= curtailed_wind[hour] - accumulated_curtailed_wind += curtailed_wind[hour] - - # 确保电力平衡:如果仍有剩余电力,强制弃掉(安全机制) - if remaining_surplus > 0: - # 记录警告但不影响计算 - # 在实际系统中,这种情况不应该发生,但作为安全保护 - pass - - else: - # 电力不足,优先放电 - power_deficit = -power_surplus - grid_feed_in[hour] = 0 # 初始化购电为0 - - max_discharge = min( - storage_soc[hour], # 储能状态限制 - storage_capacity * params.discharge_rate, # 放电功率限制 - power_deficit # 缺电功率 - ) - - # 实际放电功率 - actual_discharge = min(max_discharge, power_deficit) - discharge_power[hour] = actual_discharge - - # 更新储能状态(考虑放电效率) - if hour < hours - 1: - storage_soc[hour + 1] = storage_soc[hour] - actual_discharge / params.storage_efficiency - - # 计算剩余缺电,需要从电网购电 - remaining_deficit = power_deficit - actual_discharge - - # 如果还有缺电,从电网购电 - if remaining_deficit > 0: - # 购电功率为负值,表示从电网输入 - grid_feed_in[hour] = -remaining_deficit - - return { - 'storage_profile': storage_soc.tolist(), - 'charge_profile': charge_power.tolist(), - 'discharge_profile': discharge_power.tolist(), - 'curtailed_wind': curtailed_wind.tolist(), - 'curtailed_solar': curtailed_solar.tolist(), - 'grid_feed_in': grid_feed_in.tolist() - } - - -def check_constraints( - solar_output: List[float], - wind_output: List[float], - thermal_output: List[float], - balance_result: Dict[str, List[float]], - params: SystemParameters -) -> Dict[str, float]: - """ - 检查约束条件是否满足 - - Args: - solar_output: 光伏出力曲线 (MW) - 支持24小时或8760小时 - wind_output: 风电出力曲线 (MW) - 支持24小时或8760小时 - thermal_output: 火电出力曲线 (MW) - 支持24小时或8760小时 - balance_result: 电能平衡计算结果 - params: 系统参数配置 - - Returns: - 包含各约束实际比例的字典 - """ - # 计算总量 - total_wind_potential = sum(wind_output) - total_solar_potential = sum(solar_output) - total_thermal = sum(thermal_output) - - total_curtailed_wind = sum(balance_result['curtailed_wind']) - total_curtailed_solar = sum(balance_result['curtailed_solar']) - total_grid_feed_in = sum(balance_result['grid_feed_in']) - - # 实际发电量(考虑弃风弃光) - actual_wind_generation = total_wind_potential - total_curtailed_wind - actual_solar_generation = total_solar_potential - total_curtailed_solar - total_generation = total_thermal + actual_wind_generation + actual_solar_generation - - # 计算比例 - actual_curtailment_wind_ratio = total_curtailed_wind / total_wind_potential if total_wind_potential > 0 else 0 - actual_curtailment_solar_ratio = total_curtailed_solar / total_solar_potential if total_solar_potential > 0 else 0 - actual_grid_feed_in_ratio = total_grid_feed_in / total_generation if total_generation > 0 else 0 - - return { - 'total_curtailment_wind_ratio': actual_curtailment_wind_ratio, - 'total_curtailment_solar_ratio': actual_curtailment_solar_ratio, - 'total_grid_feed_in_ratio': actual_grid_feed_in_ratio - } - - -def optimize_storage_capacity( - solar_output: List[float], - wind_output: List[float], - thermal_output: List[float], - load_demand: List[float], - params: SystemParameters, - max_iterations: int = 100, - tolerance: float = 0.01 -) -> Dict: - """ - 优化储能容量,使用迭代方法寻找满足所有约束的最小储能容量 - - Args: - solar_output: 光伏出力曲线 (MW) - 支持24小时或8760小时 - wind_output: 风电出力曲线 (MW) - 支持24小时或8760小时 - thermal_output: 火电出力曲线 (MW) - 支持24小时或8760小时 - load_demand: 负荷曲线 (MW) - 支持24小时或8760小时 - params: 系统参数配置 - max_iterations: 最大迭代次数 - tolerance: 收敛容差 - - Returns: - 包含优化结果的字典 - """ - # 验证输入 - validate_inputs(solar_output, wind_output, thermal_output, load_demand, params) - - # 初始化搜索范围 - lower_bound = 0.0 - theoretical_max = max(sum(solar_output) + sum(wind_output) + sum(thermal_output), sum(load_demand)) - - # 应用储能容量上限限制 - if params.max_storage_capacity is not None: - upper_bound = min(theoretical_max, params.max_storage_capacity) - else: - upper_bound = theoretical_max - - # 二分搜索寻找最小储能容量 - best_capacity = upper_bound - best_result = None - solution_found = False # 标记是否找到可行解 - - for iteration in range(max_iterations): - mid_capacity = (lower_bound + upper_bound) / 2 - - # 计算当前容量下的平衡 - balance_result = calculate_energy_balance( - solar_output, wind_output, thermal_output, load_demand, params, mid_capacity - ) - - # 检查约束条件 - constraint_results = check_constraints(solar_output, wind_output, thermal_output, balance_result, params) - - # 检查是否满足所有约束 - # max_grid_ratio只限制上网电量比例,不约束购电 - # 只有当grid_feed_in为正时(上网)才需要检查约束 - total_grid_feed_in = sum(balance_result['grid_feed_in']) - if total_grid_feed_in > 0: - # 有上网电量,检查是否超过限制 - grid_constraint_satisfied = constraint_results['total_grid_feed_in_ratio'] <= params.max_grid_ratio - else: - # 没有上网电量或为负值(购电),总是满足约束 - grid_constraint_satisfied = True - - # 判断是否只有一种可再生能源 - has_wind = sum(wind_output) > 0 - has_solar = sum(solar_output) > 0 - single_renewable = (has_wind and not has_solar) or (has_solar and not has_wind) - - # 特殊情况:当上网电量限制为0时,所有超额电力都必须被弃掉 - # 此时应该允许无限制弃风弃光 - grid_quota_zero = params.max_grid_ratio == 0 - - if single_renewable or grid_quota_zero: - # 只有一种可再生能源时,或上网电量限制为0时,跳过弃风弃光约束检查 - constraints_satisfied = grid_constraint_satisfied - else: - # 有多种可再生能源且上网电量限制不为0时,检查所有约束 - constraints_satisfied = ( - constraint_results['total_curtailment_wind_ratio'] <= params.max_curtailment_wind and - constraint_results['total_curtailment_solar_ratio'] <= params.max_curtailment_solar and - grid_constraint_satisfied - ) - - # 检查储能日平衡(周期结束时储能状态应接近初始值) - storage_initial = balance_result['storage_profile'][0] - storage_final = balance_result['storage_profile'][-1] - daily_balance = abs(storage_final - storage_initial) < tolerance - - if constraints_satisfied and daily_balance: - # 满足条件,尝试减小容量 - best_capacity = mid_capacity - best_result = {**balance_result, **constraint_results} - solution_found = True - upper_bound = mid_capacity - else: - # 不满足条件,增大容量 - lower_bound = mid_capacity - - # 检查收敛 - if upper_bound - lower_bound < tolerance: - break - - # 处理储能容量上限限制的情况 - if not solution_found and params.max_storage_capacity is not None: - print(f"警告:在储能容量上限 {params.max_storage_capacity:.2f} MWh 内无法找到满足所有约束的解") - print("使用最大允许容量进行计算,但某些约束条件可能无法满足") - - # 使用最大允许容量计算结果 - balance_result = calculate_energy_balance( - solar_output, wind_output, thermal_output, load_demand, params, params.max_storage_capacity - ) - constraint_results = check_constraints(solar_output, wind_output, thermal_output, balance_result, params) - best_result = {**balance_result, **constraint_results} - best_capacity = params.max_storage_capacity - elif best_result is None: - # 如果没有找到可行解(且没有容量上限限制),使用最大容量 - balance_result = calculate_energy_balance( - solar_output, wind_output, thermal_output, load_demand, params, upper_bound - ) - constraint_results = check_constraints(solar_output, wind_output, thermal_output, balance_result, params) - best_result = {**balance_result, **constraint_results} - best_capacity = upper_bound - - # 添加能量平衡校验 - total_generation = sum(thermal_output) + sum(wind_output) + sum(solar_output) - total_consumption = sum(load_demand) - total_curtailed = sum(best_result['curtailed_wind']) + sum(best_result['curtailed_solar']) - total_grid = sum(best_result['grid_feed_in']) - total_charge = sum(best_result['charge_profile']) - total_discharge = sum(best_result['discharge_profile']) - storage_net_change = best_result['storage_profile'][-1] - best_result['storage_profile'][0] - - # 能量平衡校验:发电量 + 放电量/效率 = 负荷 + 充电量*效率 + 弃风弃光 + 上网电量 - # 考虑储能充放电效率的能量平衡 - energy_from_storage = total_discharge / params.storage_efficiency # 储能提供的有效能量 - energy_to_storage = total_charge * params.storage_efficiency # 储能消耗的电网能量 - - # 能量平衡校验:应该接近0,但允许一定误差 - # 当total_grid为负时(购电),应该加到左侧(供给侧) - # 当total_grid为正时(上网),应该加到右侧(需求侧) - if total_grid < 0: # 购电情况 - energy_balance_error = abs( - total_generation + energy_from_storage + abs(total_grid) - total_consumption - energy_to_storage - total_curtailed - ) - else: # 上网情况 - energy_balance_error = abs( - total_generation + energy_from_storage - total_consumption - energy_to_storage - total_curtailed - total_grid - ) - # 使用更大的容差,考虑储能效率损失和数值误差 - # 允许误差为总发电量的15%或10MW,取较大者 - # 储能效率损失可能达到总能量的10%以上 - tolerance = max(10.0, total_generation * 0.15) - energy_balance_check = energy_balance_error < tolerance - - # 返回最终结果 - return { - 'required_storage_capacity': best_capacity, - 'storage_profile': best_result['storage_profile'], - 'charge_profile': best_result['charge_profile'], - 'discharge_profile': best_result['discharge_profile'], - 'curtailed_wind': best_result['curtailed_wind'], - 'curtailed_solar': best_result['curtailed_solar'], - 'grid_feed_in': best_result['grid_feed_in'], - 'total_curtailment_wind_ratio': best_result['total_curtailment_wind_ratio'], - 'total_curtailment_solar_ratio': best_result['total_curtailment_solar_ratio'], - 'total_grid_feed_in_ratio': best_result['total_grid_feed_in_ratio'], - 'energy_balance_check': energy_balance_check, - 'capacity_limit_reached': params.max_storage_capacity is not None and best_capacity >= params.max_storage_capacity, - 'theoretical_optimal_capacity': best_capacity if solution_found else None, - 'max_storage_limit': params.max_storage_capacity - } - - -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] * 2 - 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 - ) - - # 计算最优储能容量 - result = optimize_storage_capacity( - solar_output, wind_output, thermal_output, load_demand, params - ) - - # 打印结果 - print("多能互补系统储能容量优化结果:") - 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 '未通过'}") - - return result - - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/test_storage_optimization.py b/test_storage_optimization.py deleted file mode 100644 index db1a044..0000000 --- a/test_storage_optimization.py +++ /dev/null @@ -1,362 +0,0 @@ -# 多能互补系统储能容量优化计算程序测试用例 - -# 该文件包含单元测试和验证测试,确保程序在各种场景下的正确性。 - -# 作者: iFlow CLI -# 创建日期: 2025-12-25 - -import unittest -import numpy as np -from storage_optimization import ( - optimize_storage_capacity, - validate_inputs, - calculate_energy_balance, - check_constraints, - SystemParameters -) - - -class TestStorageOptimization(unittest.TestCase): - """储能优化程序测试类""" - - def setUp(self): - """测试前的准备工作""" - # 基础测试数据 - self.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 - self.wind_output = [2.0, 3.0, 4.0, 3.0, 2.0, 1.0] * 4 - self.thermal_output = [5.0] * 24 - self.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] - self.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 - ) - - def test_validate_inputs_valid_data(self): - """测试有效输入数据的验证""" - # 应该不抛出异常 - validate_inputs(self.solar_output, self.wind_output, self.thermal_output, - self.load_demand, self.params) - - def test_validate_inputs_invalid_length(self): - """测试无效长度的输入数据""" - with self.assertRaises(ValueError): - validate_inputs([1.0] * 23, self.wind_output, self.thermal_output, - self.load_demand, self.params) - - def test_validate_inputs_negative_values(self): - """测试包含负值的输入数据""" - with self.assertRaises(ValueError): - validate_inputs([-1.0] + self.solar_output[1:], self.wind_output, - self.thermal_output, self.load_demand, self.params) - - def test_validate_inputs_invalid_parameters(self): - """测试无效的参数设置""" - invalid_params = SystemParameters(max_curtailment_wind=1.5) # 超出范围 - with self.assertRaises(ValueError): - validate_inputs(self.solar_output, self.wind_output, self.thermal_output, - self.load_demand, invalid_params) - - def test_calculate_energy_balance_basic(self): - """测试基本电能平衡计算""" - result = calculate_energy_balance( - self.solar_output, self.wind_output, self.thermal_output, - self.load_demand, self.params, 10.0 - ) - - # 检查返回结果包含所有必要的键 - expected_keys = ['storage_profile', 'charge_profile', 'discharge_profile', - 'curtailed_wind', 'curtailed_solar', 'grid_feed_in'] - for key in expected_keys: - self.assertIn(key, result) - self.assertEqual(len(result[key]), 24) - - # 检查储能状态不为负 - self.assertTrue(all(soc >= 0 for soc in result['storage_profile'])) - - def test_check_constraints(self): - """测试约束条件检查""" - # 先计算平衡结果 - balance_result = calculate_energy_balance( - self.solar_output, self.wind_output, self.thermal_output, - self.load_demand, self.params, 10.0 - ) - - # 检查约束 - constraint_results = check_constraints( - self.solar_output, self.wind_output, self.thermal_output, balance_result, self.params - ) - - # 检查返回结果 - expected_keys = ['total_curtailment_wind_ratio', 'total_curtailment_solar_ratio', - 'total_grid_feed_in_ratio'] - for key in expected_keys: - self.assertIn(key, constraint_results) - self.assertGreaterEqual(constraint_results[key], 0) - self.assertLessEqual(constraint_results[key], 1.0) - - def test_optimize_storage_capacity_basic(self): - """测试基本储能容量优化""" - result = optimize_storage_capacity( - self.solar_output, self.wind_output, self.thermal_output, - self.load_demand, self.params - ) - - # 检查返回结果结构 - expected_keys = [ - 'required_storage_capacity', 'storage_profile', 'charge_profile', - 'discharge_profile', 'curtailed_wind', 'curtailed_solar', - 'grid_feed_in', 'total_curtailment_wind_ratio', - 'total_curtailment_solar_ratio', 'total_grid_feed_in_ratio', - 'energy_balance_check' - ] - for key in expected_keys: - self.assertIn(key, result) - - # 检查数值合理性 - self.assertGreaterEqual(result['required_storage_capacity'], 0) - self.assertTrue(result['energy_balance_check']) - - def test_zero_curtailment_scenario(self): - """测试零弃风弃光场景""" - zero_curtail_params = SystemParameters( - max_curtailment_wind=0.0, - max_curtailment_solar=0.0, - max_grid_ratio=0.2, - storage_efficiency=0.9 - ) - - result = optimize_storage_capacity( - self.solar_output, self.wind_output, self.thermal_output, - self.load_demand, zero_curtail_params - ) - - # 检查弃风弃光率是否为0 - self.assertEqual(result['total_curtailment_wind_ratio'], 0.0) - self.assertEqual(result['total_curtailment_solar_ratio'], 0.0) - - def test_high_grid_ratio_scenario(self): - """测试高上网电量比例场景""" - high_grid_params = SystemParameters( - max_curtailment_wind=0.1, - max_curtailment_solar=0.1, - max_grid_ratio=0.5, # 高上网电量比例 - storage_efficiency=0.9 - ) - - result = optimize_storage_capacity( - self.solar_output, self.wind_output, self.thermal_output, - self.load_demand, high_grid_params - ) - - # 检查上网电量比例是否在约束范围内 - self.assertLessEqual(result['total_grid_feed_in_ratio'], 0.5) - - def test_energy_balance_verification(self): - """测试能量平衡验证""" - result = optimize_storage_capacity( - self.solar_output, self.wind_output, self.thermal_output, - self.load_demand, self.params - ) - - # 手动验证能量平衡(使用新的计算方法) - total_generation = sum(self.thermal_output) + sum(self.wind_output) + sum(self.solar_output) - total_consumption = sum(self.load_demand) - total_curtailed = sum(result['curtailed_wind']) + sum(result['curtailed_solar']) - total_grid = sum(result['grid_feed_in']) - total_charge = sum(result['charge_profile']) - total_discharge = sum(result['discharge_profile']) - - # 新的能量平衡计算:考虑储能效率 - energy_from_storage = total_discharge / self.params.storage_efficiency - energy_to_storage = total_charge * self.params.storage_efficiency - energy_balance = total_generation + energy_from_storage - total_consumption - energy_to_storage - total_curtailed - total_grid - - # 能量平衡误差应该在合理范围内(考虑储能效率损失) - tolerance = max(10.0, total_generation * 0.15) - self.assertLessEqual(abs(energy_balance), tolerance) - - def test_extreme_high_load_scenario(self): - """测试极高负荷场景""" - high_load = [50.0] * 24 # 极高负荷 - - result = optimize_storage_capacity( - self.solar_output, self.wind_output, self.thermal_output, - high_load, self.params - ) - - # 应该返回一个结果,即使系统可能不平衡 - self.assertIsNotNone(result) - self.assertGreater(result['required_storage_capacity'], 0) - - def test_extreme_low_load_scenario(self): - """测试极低负荷场景""" - low_load = [0.1] * 24 # 极低负荷 - - result = optimize_storage_capacity( - self.solar_output, self.wind_output, self.thermal_output, - low_load, self.params - ) - - # 应该返回一个结果,可能有大量弃风弃光 - self.assertIsNotNone(result) - self.assertGreaterEqual(result['total_curtailment_wind_ratio'], 0) - self.assertGreaterEqual(result['total_curtailment_solar_ratio'], 0) - - -class TestKnownScenarios(unittest.TestCase): - """已知场景测试类""" - - def test_perfect_balance_scenario(self): - """测试完美平衡场景""" - # 设计一个完美平衡的场景 - solar = [2.0] * 6 + [4.0] * 6 + [2.0] * 6 + [0.0] * 6 # 48 MW - wind = [3.0] * 12 + [1.0] * 12 # 48 MW - thermal = [6.0] * 24 # 144 MW (增加了1 MW每小时) - load = [10.0] * 24 # 恒定负荷 240 MW - # 总发电量: 48 + 48 + 144 = 240 MW,与负荷平衡 - - params = SystemParameters( - max_curtailment_wind=0.1, - max_curtailment_solar=0.1, - max_grid_ratio=0.2, - storage_efficiency=0.9 - ) - - result = optimize_storage_capacity(solar, wind, thermal, load, params) - - # 验证结果 - self.assertTrue(result['energy_balance_check']) - self.assertLessEqual(result['total_curtailment_wind_ratio'], params.max_curtailment_wind) - self.assertLessEqual(result['total_curtailment_solar_ratio'], params.max_curtailment_solar) - self.assertLessEqual(result['total_grid_feed_in_ratio'], params.max_grid_ratio) - - def test_no_renewable_scenario(self): - """测试无可再生能源场景""" - solar = [0.0] * 24 - wind = [0.0] * 24 - thermal = [10.0] * 24 - load = [8.0] * 24 - - params = SystemParameters( - max_curtailment_wind=0.1, - max_curtailment_solar=0.1, - max_grid_ratio=0.2, - storage_efficiency=0.9 - ) - - result = optimize_storage_capacity(solar, wind, thermal, load, params) - - # 验证结果 - self.assertTrue(result['energy_balance_check']) - self.assertEqual(result['total_curtailment_wind_ratio'], 0.0) - self.assertEqual(result['total_curtailment_solar_ratio'], 0.0) - self.assertGreaterEqual(result['total_grid_feed_in_ratio'], 0) - - -def run_performance_test(): - """运行性能测试""" - print("\n=== 性能测试 ===") - - # 生成随机测试数据 - np.random.seed(42) - solar = np.random.exponential(3, 24).tolist() - wind = np.random.exponential(2, 24).tolist() - thermal = np.random.uniform(3, 8, 24).tolist() - load = np.random.uniform(5, 15, 24).tolist() - - params = SystemParameters() - - import time - start_time = time.time() - - result = optimize_storage_capacity(solar, wind, thermal, load, params) - - end_time = time.time() - execution_time = end_time - start_time - - print(f"执行时间: {execution_time:.4f} 秒") - print(f"所需储能容量: {result['required_storage_capacity']:.2f} MWh") - print(f"能量平衡校验: {'通过' if result['energy_balance_check'] else '未通过'}") - - -class TestYearlyData(unittest.TestCase): - """8760小时数据测试类""" - - def setUp(self): - """测试前的准备工作""" - # 生成简化的8760小时测试数据(每小时的重复模式) - daily_pattern = [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] - self.yearly_load = daily_pattern * 365 # 24 * 365 = 8760 - - # 简化的发电数据 - daily_solar = [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 - daily_wind = [2.0, 3.0, 4.0, 3.0, 2.0, 1.0] * 4 - daily_thermal = [5.0] * 24 - - self.yearly_solar = daily_solar * 365 - self.yearly_wind = daily_wind * 365 - self.yearly_thermal = daily_thermal * 365 - - self.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 - ) - - def test_yearly_data_validation(self): - """测试8760小时数据验证""" - # 验证数据长度 - self.assertEqual(len(self.yearly_solar), 8760) - self.assertEqual(len(self.yearly_wind), 8760) - self.assertEqual(len(self.yearly_thermal), 8760) - self.assertEqual(len(self.yearly_load), 8760) - - # 验证不会抛出异常 - validate_inputs(self.yearly_solar, self.yearly_wind, self.yearly_thermal, - self.yearly_load, self.params) - - def test_yearly_basic_optimization(self): - """测试8760小时基本优化""" - # 使用较小的迭代次数以加快测试 - result = optimize_storage_capacity( - self.yearly_solar, self.yearly_wind, self.yearly_thermal, - self.yearly_load, self.params, max_iterations=50 - ) - - # 检查返回结果结构 - expected_keys = [ - 'required_storage_capacity', 'storage_profile', 'charge_profile', - 'discharge_profile', 'curtailed_wind', 'curtailed_solar', - 'grid_feed_in', 'total_curtailment_wind_ratio', - 'total_curtailment_solar_ratio', 'total_grid_feed_in_ratio', - 'energy_balance_check' - ] - for key in expected_keys: - self.assertIn(key, result) - - # 检查数据长度 - self.assertEqual(len(result['storage_profile']), 8760) - self.assertEqual(len(result['charge_profile']), 8760) - self.assertEqual(len(result['discharge_profile']), 8760) - - # 检查数值合理性 - self.assertGreaterEqual(result['required_storage_capacity'], 0) - - -if __name__ == "__main__": - print("运行多能互补系统储能容量优化程序测试...") - - # 运行单元测试 - unittest.main(argv=[''], exit=False, verbosity=2) - - # 运行性能测试 - run_performance_test() \ No newline at end of file