Compare commits

...

3 Commits

Author SHA1 Message Date
dmy
7ce065f2df 按每1MW去优化程序。 2025-12-27 17:30:40 +08:00
dmy
a80830e750 输出存储容量取整。 2025-12-27 16:53:36 +08:00
dmy
4dbe3d5083 修复了逻辑,优先弃光,当达到最大弃光比例后,再弃风。 2025-12-27 16:30:50 +08:00
13 changed files with 790 additions and 3912 deletions

View File

@@ -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()

View File

@@ -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.0010.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状态确保系统在完整周期结束后储能状态能够恢复到初始值符合实际运行场景的要求。

136
docs/PROJECT_STRUCTURE.md Normal file
View File

@@ -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/` 中的文档与代码同步

View File

@@ -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文件路径> # 使用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 <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\
- 储能系统设计
- 能源政策分析
- 学术研究
- **经济性评估**:项目投资决策和成本效益分析
- **储能配置优化**:基于经济指标的最优储能容量确定
- **能源交易分析**:购电成本和上网收益分析
## 注意事项

86
docs/README_exe.md Normal file
View File

@@ -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打包
- 包含所有必需依赖库
- 支持中文显示和输入

View File

@@ -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()

View File

@@ -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示例运行完成!")

View File

@@ -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()

View File

@@ -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()

View File

@@ -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()

View File

@@ -9,6 +9,7 @@
"""
import numpy as np
import math
from typing import List, Dict, Tuple, Optional
from dataclasses import dataclass
@@ -145,28 +146,11 @@ def calculate_energy_balance(
# 设置初始储能状态
storage_soc[0] = initial_soc
# 计算总发电潜力
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
# 新逻辑:弃光受最大弃光比例限制,弃风不受限制
total_solar_potential = sum(solar_output)
max_curtailed_solar_total = total_solar_potential * params.max_curtailment_solar
max_curtailed_wind_total = float('inf') # 弃风不受限制
# 初始化累计弃风弃光量
accumulated_curtailed_wind = 0.0
@@ -224,55 +208,24 @@ def calculate_energy_balance(
# 剩余电力考虑弃风弃光
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]
# 计算当前还能弃光的量
remaining_solar_curtailment_quota = max_curtailed_solar_total - accumulated_curtailed_solar
# 优先弃光,但不超过最大弃光比例
if solar[hour] > 0 and remaining_solar_curtailment_quota > 0:
# 可以弃光的量取:光伏出力、剩余盈余电力、剩余弃光配额的最小值
curtailed_solar[hour] = min(solar[hour], remaining_surplus, remaining_solar_curtailment_quota)
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]
# 确保电力平衡:如果仍有剩余电力,强制弃掉(安全机制)
if remaining_surplus > 0:
@@ -408,6 +361,7 @@ def check_constraints(
solar_output: 光伏出力曲线 (MW) - 支持24小时或8760小时
wind_output: 风电出力曲线 (MW) - 支持24小时或8760小时
thermal_output: 火电出力曲线 (MW) - 支持24小时或8760小时
load_demand: 负荷曲线 (MW) - 支持24小时或8760小时
balance_result: 电能平衡计算结果
params: 系统参数配置
@@ -456,7 +410,6 @@ def optimize_storage_capacity(
Args:
solar_output: 光伏出力曲线 (MW) - 支持24小时或8760小时
wind_output: 风电出力曲线 (MW) - 支持24小时或8760小时
thermal_output: 火电出力曲线 (MW) - 支持24小时或8760小时
load_demand: 负荷曲线 (MW) - 支持24小时或8760小时
params: 系统参数配置
max_iterations: 最大迭代次数
@@ -526,19 +479,22 @@ def optimize_storage_capacity(
grid_constraint_satisfied = constraint_results['total_grid_feed_in_ratio'] <= params.max_grid_ratio
else:
grid_constraint_satisfied = True
# 新逻辑:弃光受最大弃光比例限制,弃风不受限制
solar_constraint_satisfied = constraint_results['total_curtailment_solar_ratio'] <= params.max_curtailment_solar
# 弃风不受限制,始终满足
wind_constraint_satisfied = True
# 考虑浮点数误差判断上网电量比例是否为0
grid_quota_zero = abs(params.max_grid_ratio) < 1e-10
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)
grid_quota_zero = params.max_grid_ratio == 0
if single_renewable or grid_quota_zero:
constraints_satisfied = grid_constraint_satisfied
if grid_quota_zero:
constraints_satisfied = grid_constraint_satisfied and solar_constraint_satisfied
else:
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
solar_constraint_satisfied and # 弃光受限制
wind_constraint_satisfied and # 弃风不受限制
grid_constraint_satisfied # 上网电量受限制
)
# 检查储能日平衡(周期结束时储能状态应接近初始值)
@@ -593,81 +549,112 @@ 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"\n最终选择储能容量: {best_capacity:.2f} MWh (容量上限: {upper_bound:.2f} MWh)")
print(f"精确搜索完成,共检查 {end_capacity - start_capacity + 1} 个容量值")
# 将储能容量向上取整为整数
rounded_capacity = math.ceil(best_capacity)
# 确保不超过容量上限
if params.max_storage_capacity is not None:
rounded_capacity = min(rounded_capacity, int(params.max_storage_capacity))
# 确保不为负数
rounded_capacity = max(0, rounded_capacity)
print(f"\n优化得到的储能容量: {best_capacity:.2f} MWh")
print(f"调整后储能容量(向上取整): {rounded_capacity:.0f} MWh (容量上限: {upper_bound:.2f} MWh)")
print(f"最终弃电量: {best_curtailed:.2f} MWh")
# 如果调整后的容量不同,重新计算以确保结果准确
if abs(rounded_capacity - best_capacity) > 0.1:
print(f"重新计算调整后的储能容量结果...")
if is_yearly_data:
adjusted_result = find_periodic_steady_state(
solar_output, wind_output, thermal_output, load_demand,
params, rounded_capacity
)
else:
adjusted_result = calculate_energy_balance(
solar_output, wind_output, thermal_output, load_demand, params, rounded_capacity
)
# 检查约束条件
adjusted_constraint_results = check_constraints(solar_output, wind_output, thermal_output, adjusted_result, params)
# 更新结果
best_result = {**adjusted_result, **adjusted_constraint_results}
best_capacity = rounded_capacity
best_curtailed = sum(adjusted_result['curtailed_wind']) + sum(adjusted_result['curtailed_solar'])
print(f"调整后的弃电量: {best_curtailed:.2f} MWh")
else:
best_capacity = rounded_capacity
# 添加能量平衡校验
total_generation = sum(thermal_output) + sum(wind_output) + sum(solar_output)
total_consumption = sum(load_demand)
@@ -766,4 +753,4 @@ def main():
if __name__ == "__main__":
main()
main()

View File

@@ -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()

View File

@@ -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()