完成基本多场景功能。
This commit is contained in:
92
tests/test_curtail_priority.py
Normal file
92
tests/test_curtail_priority.py
Normal file
@@ -0,0 +1,92 @@
|
||||
"""
|
||||
测试弃风弃光优先级
|
||||
|
||||
验证系统在需要弃风弃光时优先弃光的逻辑
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'src'))
|
||||
|
||||
from storage_optimization import optimize_storage_capacity, SystemParameters
|
||||
|
||||
def test_curtail_priority():
|
||||
"""测试弃风弃光优先级"""
|
||||
print("=== 测试弃风弃光优先级 ===")
|
||||
|
||||
# 创建测试数据:有大量盈余电力的情况,光伏和风电都有出力
|
||||
solar_output = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 20.0, 25.0, 30.0, 30.0, 25.0, 20.0, 15.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] # 24小时
|
||||
wind_output = [15.0, 15.0, 15.0, 15.0, 15.0, 15.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 15.0, 15.0, 15.0, 15.0, 15.0, 15.0, 15.0, 15.0, 15.0, 15.0, 15.0] # 24小时
|
||||
thermal_output = [10.0] * 24 # 稳定的火电
|
||||
load_demand = [20.0] * 24 # 稳定的负荷
|
||||
|
||||
print(f"光伏总出力: {sum(solar_output):.2f} MW")
|
||||
print(f"风电总出力: {sum(wind_output):.2f} MW")
|
||||
print(f"火电总出力: {sum(thermal_output):.2f} MW")
|
||||
print(f"总负荷: {sum(load_demand):.2f} MW")
|
||||
print(f"理论盈余: {sum(solar_output) + sum(wind_output) + sum(thermal_output) - sum(load_demand):.2f} MW")
|
||||
|
||||
# 设置参数,限制储能和上网电量,强制弃风弃光
|
||||
params = SystemParameters(
|
||||
max_curtailment_wind=0.1, # 允许10%弃风
|
||||
max_curtailment_solar=0.3, # 允许30%弃光(更高)
|
||||
max_grid_ratio=0.05, # 5%上网比例(更低)
|
||||
storage_efficiency=0.9,
|
||||
discharge_rate=1.0,
|
||||
charge_rate=1.0,
|
||||
max_storage_capacity=30.0, # 更小的储能容量
|
||||
available_thermal_energy=240.0, # 不计入上网电量计算
|
||||
available_solar_energy=200.0, # 基于实际光伏出力
|
||||
available_wind_energy=300.0 # 基于实际风电出力
|
||||
)
|
||||
|
||||
print(f"\n系统参数:")
|
||||
print(f" 最大弃风率: {params.max_curtailment_wind}")
|
||||
print(f" 最大弃光率: {params.max_curtailment_solar}")
|
||||
print(f" 最大上网比例: {params.max_grid_ratio}")
|
||||
print(f" 可用光伏发电量: {params.available_solar_energy} MWh")
|
||||
print(f" 可用风电发电量: {params.available_wind_energy} MWh")
|
||||
|
||||
# 计算最大允许弃风弃光量
|
||||
max_curtailed_wind_total = sum(wind_output) * params.max_curtailment_wind
|
||||
max_curtailed_solar_total = sum(solar_output) * params.max_curtailment_solar
|
||||
|
||||
print(f"\n理论最大弃风量: {max_curtailed_wind_total:.2f} MW")
|
||||
print(f"理论最大弃光量: {max_curtailed_solar_total:.2f} MW")
|
||||
|
||||
# 运行优化
|
||||
result = optimize_storage_capacity(solar_output, wind_output, thermal_output, load_demand, params)
|
||||
|
||||
# 分析结果
|
||||
total_curtailed_wind = sum(result['curtailed_wind'])
|
||||
total_curtailed_solar = sum(result['curtailed_solar'])
|
||||
total_grid_feed_in = sum(x for x in result['grid_feed_in'] if x > 0)
|
||||
|
||||
print(f"\n实际结果:")
|
||||
print(f" 实际弃风量: {total_curtailed_wind:.2f} MW")
|
||||
print(f" 实际弃光量: {total_curtailed_solar:.2f} MW")
|
||||
print(f" 实际上网电量: {total_grid_feed_in:.2f} MWh")
|
||||
print(f" 实际弃风率: {total_curtailed_wind/sum(wind_output):.3f}")
|
||||
print(f" 实际弃光率: {total_curtailed_solar/sum(solar_output):.3f}")
|
||||
|
||||
# 检查弃光是否优先于弃风
|
||||
if total_curtailed_solar > 0 or total_curtailed_wind > 0:
|
||||
total_curtailment = total_curtailed_solar + total_curtailed_wind
|
||||
solar_ratio = total_curtailed_solar / total_curtailment if total_curtailment > 0 else 0
|
||||
wind_ratio = total_curtailed_wind / total_curtailment if total_curtailment > 0 else 0
|
||||
|
||||
print(f"\n弃风弃光比例分析:")
|
||||
print(f" 弃光占比: {solar_ratio:.3f}")
|
||||
print(f" 弃风占比: {wind_ratio:.3f}")
|
||||
|
||||
# 验证优先弃光逻辑
|
||||
# 在有弃风弃光的情况下,应该先弃光直到达到弃光率限制
|
||||
if solar_ratio > 0.5: # 如果弃光占比超过50%,说明优先弃光
|
||||
print(" [OK] 验证通过:系统优先弃光")
|
||||
else:
|
||||
print(" [WARNING] 可能未完全优先弃光")
|
||||
|
||||
print("\n=== 测试完成 ===")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_curtail_priority()
|
||||
137
tests/test_curtailment_logic.py
Normal file
137
tests/test_curtailment_logic.py
Normal file
@@ -0,0 +1,137 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
测试弃风弃光逻辑的脚本
|
||||
验证"先弃光,当达到最大弃光比例后,再弃风,弃风不限"的逻辑是否正确实现
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), 'src'))
|
||||
|
||||
from storage_optimization import calculate_energy_balance, SystemParameters
|
||||
|
||||
def test_curtailment_logic():
|
||||
"""测试弃风弃光逻辑"""
|
||||
|
||||
# 创建测试数据
|
||||
# 24小时的简单场景:前12小时负荷低,后12小时负荷高
|
||||
solar_output = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 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, 0.0, 0.0, 0.0, 0.0, 0.0]
|
||||
wind_output = [5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0] # 恒定的风电出力
|
||||
thermal_output = [2.0] * 24 # 恒定的火电出力
|
||||
load_demand = [1.0] * 12 + [15.0] * 12 # 前12小时低负荷,后12小时高负荷
|
||||
|
||||
# 设置系统参数
|
||||
params = SystemParameters(
|
||||
max_curtailment_wind=0.1, # 弃风率限制10%(但实际上弃风不受限制)
|
||||
max_curtailment_solar=0.2, # 弃光率限制20%
|
||||
max_grid_ratio=0.1, # 上网电量比例限制10%
|
||||
storage_efficiency=0.9,
|
||||
discharge_rate=1.0,
|
||||
charge_rate=1.0
|
||||
)
|
||||
|
||||
# 计算能量平衡
|
||||
storage_capacity = 10.0 # 10MWh储能容量
|
||||
result = calculate_energy_balance(
|
||||
solar_output, wind_output, thermal_output, load_demand, params, storage_capacity
|
||||
)
|
||||
|
||||
# 分析结果
|
||||
total_solar_potential = sum(solar_output)
|
||||
total_wind_potential = sum(wind_output)
|
||||
total_curtailed_solar = sum(result['curtailed_solar'])
|
||||
total_curtailed_wind = sum(result['curtailed_wind'])
|
||||
|
||||
actual_solar_curtailment_ratio = total_curtailed_solar / total_solar_potential if total_solar_potential > 0 else 0
|
||||
actual_wind_curtailment_ratio = total_curtailed_wind / total_wind_potential if total_wind_potential > 0 else 0
|
||||
|
||||
print("弃风弃光逻辑测试结果:")
|
||||
print(f"总光伏出力: {total_solar_potential:.2f} MWh")
|
||||
print(f"总风电出力: {total_wind_potential:.2f} MWh")
|
||||
print(f"弃光量: {total_curtailed_solar:.2f} MWh ({actual_solar_curtailment_ratio:.2%})")
|
||||
print(f"弃风量: {total_curtailed_wind:.2f} MWh ({actual_wind_curtailment_ratio:.2%})")
|
||||
print(f"弃光率限制: {params.max_curtailment_solar:.2%}")
|
||||
print(f"弃风率限制: {params.max_curtailment_wind:.2%} (实际不受限制)")
|
||||
|
||||
# 验证逻辑
|
||||
print("\n验证结果:")
|
||||
solar_constraint_ok = actual_solar_curtailment_ratio <= params.max_curtailment_solar + 0.01 # 允许1%误差
|
||||
print(f"弃光率是否在限制范围内: {'通过' if solar_constraint_ok else '未通过'}")
|
||||
|
||||
# 弃风应该不受限制,所以不需要检查约束
|
||||
print("弃风不受限制: 通过")
|
||||
|
||||
# 检查弃风弃光的时间分布
|
||||
print("\n弃风弃光时间分布:")
|
||||
for hour in range(24):
|
||||
if result['curtailed_solar'][hour] > 0 or result['curtailed_wind'][hour] > 0:
|
||||
print(f" 小时 {hour:2d}: 弃光={result['curtailed_solar'][hour]:.2f}MW, 弃风={result['curtailed_wind'][hour]:.2f}MW")
|
||||
|
||||
return solar_constraint_ok
|
||||
|
||||
def test_solar_curtailment_priority():
|
||||
"""测试弃光优先逻辑"""
|
||||
|
||||
# 创建极端测试场景:大量盈余电力
|
||||
solar_output = [10.0] * 24 # 高光伏出力
|
||||
wind_output = [10.0] * 24 # 高风电出力
|
||||
thermal_output = [0.0] * 24 # 无火电
|
||||
load_demand = [1.0] * 24 # 极低负荷
|
||||
|
||||
# 设置系统参数
|
||||
params = SystemParameters(
|
||||
max_curtailment_wind=0.1, # 弃风率限制10%(但实际上弃风不受限制)
|
||||
max_curtailment_solar=0.1, # 弃光率限制10%
|
||||
max_grid_ratio=0.0, # 不允许上网电量
|
||||
storage_efficiency=0.9,
|
||||
discharge_rate=1.0,
|
||||
charge_rate=1.0
|
||||
)
|
||||
|
||||
# 计算能量平衡
|
||||
storage_capacity = 5.0 # 5MWh储能容量
|
||||
result = calculate_energy_balance(
|
||||
solar_output, wind_output, thermal_output, load_demand, params, storage_capacity
|
||||
)
|
||||
|
||||
# 分析结果
|
||||
total_solar_potential = sum(solar_output)
|
||||
total_wind_potential = sum(wind_output)
|
||||
total_curtailed_solar = sum(result['curtailed_solar'])
|
||||
total_curtailed_wind = sum(result['curtailed_wind'])
|
||||
|
||||
actual_solar_curtailment_ratio = total_curtailed_solar / total_solar_potential if total_solar_potential > 0 else 0
|
||||
actual_wind_curtailment_ratio = total_curtailed_wind / total_wind_potential if total_wind_potential > 0 else 0
|
||||
|
||||
print("\n弃光优先逻辑测试结果:")
|
||||
print(f"总光伏出力: {total_solar_potential:.2f} MWh")
|
||||
print(f"总风电出力: {total_wind_potential:.2f} MWh")
|
||||
print(f"弃光量: {total_curtailed_solar:.2f} MWh ({actual_solar_curtailment_ratio:.2%})")
|
||||
print(f"弃风量: {total_curtailed_wind:.2f} MWh ({actual_wind_curtailment_ratio:.2%})")
|
||||
print(f"弃光率限制: {params.max_curtailment_solar:.2%}")
|
||||
|
||||
# 验证弃光是否优先
|
||||
solar_at_limit = abs(actual_solar_curtailment_ratio - params.max_curtailment_solar) < 0.01
|
||||
print(f"\n弃光是否达到限制: {'是' if solar_at_limit else '否'}")
|
||||
|
||||
# 验证弃风是否在弃光达到限制后才发生
|
||||
wind_curtailment_exists = total_curtailed_wind > 0
|
||||
print(f"是否存在弃风: {'是' if wind_curtailment_exists else '否'}")
|
||||
|
||||
if solar_at_limit and wind_curtailment_exists:
|
||||
print("通过 弃光优先逻辑正确:先弃光达到限制,然后弃风")
|
||||
return True
|
||||
else:
|
||||
print("未通过 弃光优先逻辑可能存在问题")
|
||||
return False
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("开始测试弃风弃光逻辑...")
|
||||
|
||||
test1_result = test_curtailment_logic()
|
||||
test2_result = test_solar_curtailment_priority()
|
||||
|
||||
if test1_result and test2_result:
|
||||
print("\n通过 所有测试通过,弃风弃光逻辑实现正确")
|
||||
else:
|
||||
print("\n未通过 部分测试失败,需要检查逻辑实现")
|
||||
305
tests/test_excel_data.py
Normal file
305
tests/test_excel_data.py
Normal file
@@ -0,0 +1,305 @@
|
||||
"""
|
||||
测试程序 - 验证Excel数据输入和储能容量优化
|
||||
|
||||
该程序使用data_template_24.xlsx和data_template_8760.xlsx作为输入,
|
||||
测试储能容量优化系统的功能和错误处理。
|
||||
|
||||
作者: iFlow CLI
|
||||
创建日期: 2025-12-25
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'src'))
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import matplotlib.pyplot as plt
|
||||
from typing import Dict, Any
|
||||
from excel_reader import read_excel_data, analyze_excel_data, read_system_parameters, validate_system_parameters
|
||||
from storage_optimization import optimize_storage_capacity, SystemParameters
|
||||
from advanced_visualization import create_comprehensive_plot, create_time_series_plot
|
||||
|
||||
|
||||
def test_excel_file(file_path: str, test_name: str) -> Dict[str, Any]:
|
||||
"""
|
||||
测试单个Excel文件
|
||||
|
||||
Args:
|
||||
file_path: Excel文件路径
|
||||
test_name: 测试名称
|
||||
|
||||
Returns:
|
||||
测试结果字典
|
||||
"""
|
||||
print(f"\n{'='*60}")
|
||||
print(f"测试:{test_name}")
|
||||
print(f"文件:{file_path}")
|
||||
print(f"{'='*60}")
|
||||
|
||||
result = {
|
||||
'test_name': test_name,
|
||||
'file_path': file_path,
|
||||
'success': False,
|
||||
'error': None,
|
||||
'data_stats': {},
|
||||
'optimization_result': {}
|
||||
}
|
||||
|
||||
try:
|
||||
# 1. 检查文件是否存在
|
||||
if not os.path.exists(file_path):
|
||||
raise FileNotFoundError(f"文件不存在:{file_path}")
|
||||
print("[OK] 文件存在检查通过")
|
||||
|
||||
# 2. 读取Excel数据
|
||||
print("正在读取Excel数据...")
|
||||
data = read_excel_data(file_path, include_parameters=True)
|
||||
print(f"[OK] 数据读取成功,类型:{data['data_type']}")
|
||||
print(f" 原始数据长度:{data['original_length']}")
|
||||
print(f" 处理后数据长度:{len(data['solar_output'])}")
|
||||
|
||||
# 2.1 测试参数读取
|
||||
if 'system_parameters' in data:
|
||||
params = data['system_parameters']
|
||||
print(f"[OK] 系统参数读取成功")
|
||||
print(f" 最大弃风率: {params.max_curtailment_wind}")
|
||||
print(f" 最大弃光率: {params.max_curtailment_solar}")
|
||||
print(f" 最大上网电量比例: {params.max_grid_ratio}")
|
||||
print(f" 储能效率: {params.storage_efficiency}")
|
||||
print(f" 放电倍率: {params.discharge_rate}")
|
||||
print(f" 充电倍率: {params.charge_rate}")
|
||||
print(f" 最大储能容量: {params.max_storage_capacity}")
|
||||
|
||||
# 验证参数
|
||||
validation = validate_system_parameters(params)
|
||||
if validation['valid']:
|
||||
print("[OK] 系统参数验证通过")
|
||||
else:
|
||||
print("[ERROR] 系统参数验证失败:")
|
||||
for error in validation['errors']:
|
||||
print(f" - {error}")
|
||||
raise ValueError(f"系统参数验证失败: {validation['errors']}")
|
||||
|
||||
if validation['warnings']:
|
||||
print("[WARNING] 系统参数警告:")
|
||||
for warning in validation['warnings']:
|
||||
print(f" - {warning}")
|
||||
else:
|
||||
print("[WARNING] 未找到系统参数,使用默认参数")
|
||||
params = SystemParameters()
|
||||
|
||||
# 3. 分析数据统计信息
|
||||
print("正在分析数据统计信息...")
|
||||
stats = analyze_excel_data(file_path)
|
||||
result['data_stats'] = stats
|
||||
print("[OK] 数据统计分析完成")
|
||||
|
||||
# 4. 验证数据完整性
|
||||
print("正在验证数据完整性...")
|
||||
solar_output = data['solar_output']
|
||||
wind_output = data['wind_output']
|
||||
thermal_output = data['thermal_output']
|
||||
load_demand = data['load_demand']
|
||||
|
||||
# 检查数据长度一致性
|
||||
if not (len(solar_output) == len(wind_output) == len(thermal_output) == len(load_demand)):
|
||||
raise ValueError("数据长度不一致")
|
||||
print("[OK] 数据长度一致性检查通过")
|
||||
|
||||
# 检查非负值
|
||||
for name, values in [
|
||||
("光伏出力", solar_output), ("风电出力", wind_output),
|
||||
("火电出力", thermal_output), ("负荷需求", load_demand)
|
||||
]:
|
||||
if any(v < 0 for v in values):
|
||||
raise ValueError(f"{name}包含负值")
|
||||
print("[OK] 非负值检查通过")
|
||||
|
||||
# 5. 储能容量优化测试
|
||||
print("正在进行储能容量优化计算...")
|
||||
|
||||
# 使用Excel中的参数和不同的测试配置
|
||||
excel_params = params # 从Excel中读取的参数
|
||||
test_configs = [
|
||||
{
|
||||
'name': 'Excel配置',
|
||||
'params': excel_params
|
||||
},
|
||||
{
|
||||
'name': '基础配置',
|
||||
'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
|
||||
)
|
||||
},
|
||||
{
|
||||
'name': '高弃风弃光配置',
|
||||
'params': SystemParameters(
|
||||
max_curtailment_wind=0.2,
|
||||
max_curtailment_solar=0.2,
|
||||
max_grid_ratio=0.3,
|
||||
storage_efficiency=0.85,
|
||||
discharge_rate=1.5,
|
||||
charge_rate=1.5
|
||||
)
|
||||
},
|
||||
{
|
||||
'name': '低储能效率配置',
|
||||
'params': SystemParameters(
|
||||
max_curtailment_wind=0.05,
|
||||
max_curtailment_solar=0.05,
|
||||
max_grid_ratio=0.1,
|
||||
storage_efficiency=0.75,
|
||||
discharge_rate=0.8,
|
||||
charge_rate=0.8
|
||||
)
|
||||
}
|
||||
]
|
||||
|
||||
optimization_results = []
|
||||
|
||||
for config in test_configs:
|
||||
print(f" 测试配置:{config['name']}")
|
||||
opt_result = optimize_storage_capacity(
|
||||
solar_output, wind_output, thermal_output, load_demand, config['params']
|
||||
)
|
||||
|
||||
print(f" 所需储能容量:{opt_result['required_storage_capacity']:.2f} MWh")
|
||||
print(f" 弃风率:{opt_result['total_curtailment_wind_ratio']:.3f}")
|
||||
print(f" 弃光率:{opt_result['total_curtailment_solar_ratio']:.3f}")
|
||||
print(f" 上网电量比例:{opt_result['total_grid_feed_in_ratio']:.3f}")
|
||||
print(f" 能量平衡校验:{'通过' if opt_result['energy_balance_check'] else '未通过'}")
|
||||
|
||||
optimization_results.append({
|
||||
'config_name': config['name'],
|
||||
'result': opt_result
|
||||
})
|
||||
|
||||
result['optimization_result'] = optimization_results
|
||||
print("[OK] 储能容量优化计算完成")
|
||||
|
||||
# 6. 可视化测试(仅对24小时数据进行,避免8760小时数据生成过大图像)
|
||||
if data['data_type'] == '24':
|
||||
print("正在生成可视化图表...")
|
||||
try:
|
||||
# 使用基础配置的结果生成图表
|
||||
base_result = optimization_results[0]['result']
|
||||
base_params = test_configs[0]['params']
|
||||
|
||||
# 生成基础图表(截取前24小时数据)
|
||||
solar_24 = solar_output[:24] if len(solar_output) > 24 else solar_output
|
||||
wind_24 = wind_output[:24] if len(wind_output) > 24 else wind_output
|
||||
thermal_24 = thermal_output[:24] if len(thermal_output) > 24 else thermal_output
|
||||
load_24 = load_demand[:24] if len(load_demand) > 24 else load_demand
|
||||
|
||||
# 截取结果的24小时数据
|
||||
result_24 = {
|
||||
'required_storage_capacity': base_result['required_storage_capacity'],
|
||||
'charge_profile': base_result['charge_profile'][:24] if len(base_result['charge_profile']) > 24 else base_result['charge_profile'],
|
||||
'discharge_profile': base_result['discharge_profile'][:24] if len(base_result['discharge_profile']) > 24 else base_result['discharge_profile'],
|
||||
'curtailed_wind': base_result['curtailed_wind'][:24] if len(base_result['curtailed_wind']) > 24 else base_result['curtailed_wind'],
|
||||
'curtailed_solar': base_result['curtailed_solar'][:24] if len(base_result['curtailed_solar']) > 24 else base_result['curtailed_solar'],
|
||||
'grid_feed_in': base_result['grid_feed_in'][:24] if len(base_result['grid_feed_in']) > 24 else base_result['grid_feed_in'],
|
||||
'storage_profile': base_result['storage_profile'][:24] if len(base_result['storage_profile']) > 24 else base_result['storage_profile'],
|
||||
'total_curtailment_wind_ratio': base_result['total_curtailment_wind_ratio'],
|
||||
'total_curtailment_solar_ratio': base_result['total_curtailment_solar_ratio'],
|
||||
'total_grid_feed_in_ratio': base_result['total_grid_feed_in_ratio'],
|
||||
'energy_balance_check': base_result['energy_balance_check']
|
||||
}
|
||||
|
||||
create_comprehensive_plot(solar_24, wind_24, thermal_24, load_24, result_24, base_params)
|
||||
create_time_series_plot(solar_24, wind_24, thermal_24, load_24, result_24)
|
||||
print("[OK] 可视化图表生成完成")
|
||||
|
||||
except Exception as e:
|
||||
print(f"⚠ 可视化图表生成失败:{str(e)}")
|
||||
print(" 这不是严重错误,可能是字体或显示问题")
|
||||
|
||||
# 标记测试成功
|
||||
result['success'] = True
|
||||
print(f"\n[OK] 测试 '{test_name}' 成功完成!")
|
||||
|
||||
except Exception as e:
|
||||
result['error'] = str(e)
|
||||
result['traceback'] = traceback.format_exc()
|
||||
print(f"\n[ERROR] 测试 '{test_name}' 失败:{str(e)}")
|
||||
print("详细错误信息:")
|
||||
traceback.print_exc()
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def run_comprehensive_tests():
|
||||
"""运行综合测试"""
|
||||
print("开始运行Excel数据输入综合测试...")
|
||||
print(f"当前工作目录:{os.getcwd()}")
|
||||
|
||||
# 测试文件列表
|
||||
test_files = [
|
||||
{
|
||||
'path': 'data_template_24.xlsx',
|
||||
'name': '24小时数据模板测试'
|
||||
},
|
||||
{
|
||||
'path': 'data_template_8760.xlsx',
|
||||
'name': '8760小时数据模板测试'
|
||||
}
|
||||
]
|
||||
|
||||
# 运行所有测试
|
||||
results = []
|
||||
for test_file in test_files:
|
||||
result = test_excel_file(test_file['path'], test_file['name'])
|
||||
results.append(result)
|
||||
|
||||
# 生成测试报告
|
||||
print(f"\n{'='*80}")
|
||||
print("测试报告总结")
|
||||
print(f"{'='*80}")
|
||||
|
||||
total_tests = len(results)
|
||||
successful_tests = sum(1 for r in results if r['success'])
|
||||
failed_tests = total_tests - successful_tests
|
||||
|
||||
print(f"总测试数:{total_tests}")
|
||||
print(f"成功测试:{successful_tests}")
|
||||
print(f"失败测试:{failed_tests}")
|
||||
print(f"成功率:{successful_tests/total_tests*100:.1f}%")
|
||||
|
||||
print("\n详细结果:")
|
||||
for i, result in enumerate(results, 1):
|
||||
status = "[OK] 成功" if result['success'] else "[ERROR] 失败"
|
||||
print(f"{i}. {result['test_name']}: {status}")
|
||||
if not result['success']:
|
||||
print(f" 错误:{result['error']}")
|
||||
|
||||
# 如果有失败的测试,返回非零退出码
|
||||
if failed_tests > 0:
|
||||
print(f"\n有{failed_tests}个测试失败,请检查上述错误信息")
|
||||
return False
|
||||
else:
|
||||
print("\n所有测试通过!系统运行正常。")
|
||||
return True
|
||||
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
try:
|
||||
success = run_comprehensive_tests()
|
||||
sys.exit(0 if success else 1)
|
||||
except KeyboardInterrupt:
|
||||
print("\n测试被用户中断")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(f"\n测试过程中发生未预期的错误:{str(e)}")
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
184
tests/test_excel_parameters.py
Normal file
184
tests/test_excel_parameters.py
Normal file
@@ -0,0 +1,184 @@
|
||||
"""
|
||||
测试Excel参数设置功能
|
||||
|
||||
该程序演示如何修改Excel参数工作表中的参数,并验证参数是否被正确读取。
|
||||
|
||||
作者: iFlow CLI
|
||||
创建日期: 2025-12-25
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'src'))
|
||||
|
||||
import pandas as pd
|
||||
from excel_reader import read_excel_data, validate_system_parameters
|
||||
from storage_optimization import optimize_storage_capacity
|
||||
|
||||
|
||||
def test_parameter_modification():
|
||||
"""测试参数修改功能"""
|
||||
print("=== 测试Excel参数设置功能 ===\n")
|
||||
|
||||
# 1. 读取原始参数
|
||||
print("1. 读取原始Excel参数...")
|
||||
original_data = read_excel_data('data_template_24.xlsx', include_parameters=True)
|
||||
original_params = original_data['system_parameters']
|
||||
|
||||
print("原始参数:")
|
||||
print(f" 最大弃风率: {original_params.max_curtailment_wind}")
|
||||
print(f" 最大弃光率: {original_params.max_curtailment_solar}")
|
||||
print(f" 最大上网电量比例: {original_params.max_grid_ratio}")
|
||||
print(f" 储能效率: {original_params.storage_efficiency}")
|
||||
print(f" 放电倍率: {original_params.discharge_rate}")
|
||||
print(f" 充电倍率: {original_params.charge_rate}")
|
||||
print(f" 最大储能容量: {original_params.max_storage_capacity}")
|
||||
|
||||
# 2. 使用原始参数进行优化
|
||||
print("\n2. 使用原始参数进行储能容量优化...")
|
||||
original_result = optimize_storage_capacity(
|
||||
original_data['solar_output'],
|
||||
original_data['wind_output'],
|
||||
original_data['thermal_output'],
|
||||
original_data['load_demand'],
|
||||
original_params
|
||||
)
|
||||
|
||||
print(f"原始参数优化结果:")
|
||||
print(f" 所需储能容量: {original_result['required_storage_capacity']:.2f} MWh")
|
||||
print(f" 弃风率: {original_result['total_curtailment_wind_ratio']:.3f}")
|
||||
print(f" 弃光率: {original_result['total_curtailment_solar_ratio']:.3f}")
|
||||
print(f" 上网电量比例: {original_result['total_grid_feed_in_ratio']:.3f}")
|
||||
|
||||
# 3. 修改Excel参数
|
||||
print("\n3. 修改Excel参数...")
|
||||
|
||||
# 创建测试用的参数DataFrame
|
||||
new_params_df = pd.DataFrame({
|
||||
'参数名称': [
|
||||
'最大弃风率',
|
||||
'最大弃光率',
|
||||
'最大上网电量比例',
|
||||
'储能效率',
|
||||
'放电倍率',
|
||||
'充电倍率',
|
||||
'最大储能容量'
|
||||
],
|
||||
'参数值': [
|
||||
0.15, # 增加弃风率
|
||||
0.12, # 增加弃光率
|
||||
0.25, # 增加上网电量比例
|
||||
0.85, # 降低储能效率
|
||||
1.2, # 增加放电倍率
|
||||
1.2, # 增加充电倍率
|
||||
1000.0 # 设置储能容量上限
|
||||
],
|
||||
'参数说明': [
|
||||
'允许的最大弃风率(0.0-1.0)',
|
||||
'允许的最大弃光率(0.0-1.0)',
|
||||
'允许的最大上网电量比例(0.0-∞,只限制上网电量)',
|
||||
'储能充放电效率(0.0-1.0)',
|
||||
'储能放电倍率(C-rate,>0)',
|
||||
'储能充电倍率(C-rate,>0)',
|
||||
'储能容量上限(MWh,空表示无限制)'
|
||||
],
|
||||
'取值范围': [
|
||||
'0.0-1.0',
|
||||
'0.0-1.0',
|
||||
'≥0.0',
|
||||
'0.0-1.0',
|
||||
'>0',
|
||||
'>0',
|
||||
'>0或空'
|
||||
],
|
||||
'默认值': [
|
||||
'0.1',
|
||||
'0.1',
|
||||
'0.2',
|
||||
'0.9',
|
||||
'1.0',
|
||||
'1.0',
|
||||
'无限制'
|
||||
]
|
||||
})
|
||||
|
||||
# 读取原始Excel文件
|
||||
with pd.ExcelFile('data_template_24.xlsx') as xls:
|
||||
data_df = pd.read_excel(xls, sheet_name='数据')
|
||||
description_df = pd.read_excel(xls, sheet_name='说明')
|
||||
|
||||
# 保存修改后的Excel文件
|
||||
with pd.ExcelWriter('data_template_24_modified.xlsx', engine='openpyxl') as writer:
|
||||
data_df.to_excel(writer, sheet_name='数据', index=False)
|
||||
new_params_df.to_excel(writer, sheet_name='参数', index=False)
|
||||
description_df.to_excel(writer, sheet_name='说明', index=False)
|
||||
|
||||
print("已创建修改后的Excel文件: data_template_24_modified.xlsx")
|
||||
print("修改后的参数:")
|
||||
print(f" 最大弃风率: 0.15 (原: {original_params.max_curtailment_wind})")
|
||||
print(f" 最大弃光率: 0.12 (原: {original_params.max_curtailment_solar})")
|
||||
print(f" 最大上网电量比例: 0.25 (原: {original_params.max_grid_ratio})")
|
||||
print(f" 储能效率: 0.85 (原: {original_params.storage_efficiency})")
|
||||
print(f" 放电倍率: 1.2 (原: {original_params.discharge_rate})")
|
||||
print(f" 充电倍率: 1.2 (原: {original_params.charge_rate})")
|
||||
print(f" 最大储能容量: 1000.0 (原: {original_params.max_storage_capacity})")
|
||||
|
||||
# 4. 读取修改后的参数
|
||||
print("\n4. 读取修改后的参数...")
|
||||
modified_data = read_excel_data('data_template_24_modified.xlsx', include_parameters=True)
|
||||
modified_params = modified_data['system_parameters']
|
||||
|
||||
print("修改后的参数读取结果:")
|
||||
print(f" 最大弃风率: {modified_params.max_curtailment_wind}")
|
||||
print(f" 最大弃光率: {modified_params.max_curtailment_solar}")
|
||||
print(f" 最大上网电量比例: {modified_params.max_grid_ratio}")
|
||||
print(f" 储能效率: {modified_params.storage_efficiency}")
|
||||
print(f" 放电倍率: {modified_params.discharge_rate}")
|
||||
print(f" 充电倍率: {modified_params.charge_rate}")
|
||||
print(f" 最大储能容量: {modified_params.max_storage_capacity}")
|
||||
|
||||
# 5. 验证修改后的参数
|
||||
validation = validate_system_parameters(modified_params)
|
||||
if validation['valid']:
|
||||
print("[OK] 修改后的参数验证通过")
|
||||
else:
|
||||
print("[ERROR] 修改后的参数验证失败:")
|
||||
for error in validation['errors']:
|
||||
print(f" - {error}")
|
||||
|
||||
if validation['warnings']:
|
||||
print("[WARNING] 修改后的参数警告:")
|
||||
for warning in validation['warnings']:
|
||||
print(f" - {warning}")
|
||||
|
||||
# 6. 使用修改后的参数进行优化
|
||||
print("\n5. 使用修改后的参数进行储能容量优化...")
|
||||
modified_result = optimize_storage_capacity(
|
||||
modified_data['solar_output'],
|
||||
modified_data['wind_output'],
|
||||
modified_data['thermal_output'],
|
||||
modified_data['load_demand'],
|
||||
modified_params
|
||||
)
|
||||
|
||||
print(f"修改后参数优化结果:")
|
||||
print(f" 所需储能容量: {modified_result['required_storage_capacity']:.2f} MWh")
|
||||
print(f" 弃风率: {modified_result['total_curtailment_wind_ratio']:.3f}")
|
||||
print(f" 弃光率: {modified_result['total_curtailment_solar_ratio']:.3f}")
|
||||
print(f" 上网电量比例: {modified_result['total_grid_feed_in_ratio']:.3f}")
|
||||
print(f" 容量限制是否达到: {modified_result['capacity_limit_reached']}")
|
||||
|
||||
# 7. 对比结果
|
||||
print("\n6. 结果对比:")
|
||||
print(f"储能容量变化: {original_result['required_storage_capacity']:.2f} -> {modified_result['required_storage_capacity']:.2f} MWh")
|
||||
print(f"弃风率变化: {original_result['total_curtailment_wind_ratio']:.3f} -> {modified_result['total_curtailment_wind_ratio']:.3f}")
|
||||
print(f"弃光率变化: {original_result['total_curtailment_solar_ratio']:.3f} -> {modified_result['total_curtailment_solar_ratio']:.3f}")
|
||||
print(f"上网电量比例变化: {original_result['total_grid_feed_in_ratio']:.3f} -> {modified_result['total_grid_feed_in_ratio']:.3f}")
|
||||
|
||||
print("\n=== 测试完成 ===")
|
||||
print("Excel参数设置功能测试成功!")
|
||||
print("用户可以通过修改Excel文件中的'参数'工作表来调整系统参数。")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_parameter_modification()
|
||||
103
tests/test_extreme_single_renewable.py
Normal file
103
tests/test_extreme_single_renewable.py
Normal file
@@ -0,0 +1,103 @@
|
||||
"""
|
||||
创建一个明确的测试算例,验证只有风电时弃电量不受限制
|
||||
这个测试会创建一个风电远超负荷的场景,来验证弃风是否真的不受限制
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'src'))
|
||||
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
|
||||
def create_extreme_wind_test():
|
||||
"""创建极端风电测试场景:风电远超负荷"""
|
||||
|
||||
# 创建24小时数据
|
||||
hours = list(range(1, 25))
|
||||
|
||||
# 风电出力:设计为远超负荷,强制产生弃风
|
||||
wind_output = [200, 180, 160, 140, 120, 100, 80, 60, 50, 40, 30, 25, 20, 15, 10, 8, 6, 5, 4, 3, 2, 1, 0.5, 0.2]
|
||||
|
||||
# 光伏出力:全部为0
|
||||
solar_output = [0.0] * 24
|
||||
|
||||
# 火电出力:全部为0
|
||||
thermal_output = [0.0] * 24
|
||||
|
||||
# 负荷需求:较低,确保风电远超负荷
|
||||
load_demand = [20.0] * 24 # 只有20MW负荷,风电最高200MW
|
||||
|
||||
# 创建DataFrame
|
||||
data = {
|
||||
'小时': hours,
|
||||
'光伏出力(MW)': solar_output,
|
||||
'风电出力(MW)': wind_output,
|
||||
'火电出力(MW)': thermal_output,
|
||||
'负荷需求(MW)': load_demand
|
||||
}
|
||||
df = pd.DataFrame(data)
|
||||
|
||||
# 保存为Excel文件
|
||||
excel_file = 'extreme_wind_test.xlsx'
|
||||
df.to_excel(excel_file, index=False, sheet_name='data')
|
||||
print(f"已创建极端风电测试文件: {excel_file}")
|
||||
print(f"风电总出力: {sum(wind_output):.1f} MWh")
|
||||
print(f"负荷总需求: {sum(load_demand):.1f} MWh")
|
||||
print(f"风电超额: {sum(wind_output) - sum(load_demand):.1f} MWh (应该被弃掉)")
|
||||
|
||||
return excel_file
|
||||
|
||||
def create_extreme_solar_test():
|
||||
"""创建极端光伏测试场景:光伏远超负荷"""
|
||||
|
||||
# 创建24小时数据
|
||||
hours = list(range(1, 25))
|
||||
|
||||
# 风电出力:全部为0
|
||||
wind_output = [0.0] * 24
|
||||
|
||||
# 光伏出力:设计为远超负荷,强制产生弃光
|
||||
solar_output = [0, 0, 0, 0, 0, 0, 50, 100, 150, 200, 180, 150, 120, 100, 80, 60, 40, 20, 10, 5, 2, 1, 0.5, 0.2]
|
||||
|
||||
# 火电出力:全部为0
|
||||
thermal_output = [0.0] * 24
|
||||
|
||||
# 负荷需求:较低,确保光伏远超负荷
|
||||
load_demand = [30.0] * 24 # 只有30MW负荷,光伏最高200MW
|
||||
|
||||
# 创建DataFrame
|
||||
data = {
|
||||
'小时': hours,
|
||||
'光伏出力(MW)': solar_output,
|
||||
'风电出力(MW)': wind_output,
|
||||
'火电出力(MW)': thermal_output,
|
||||
'负荷需求(MW)': load_demand
|
||||
}
|
||||
df = pd.DataFrame(data)
|
||||
|
||||
# 保存为Excel文件
|
||||
excel_file = 'extreme_solar_test.xlsx'
|
||||
df.to_excel(excel_file, index=False, sheet_name='data')
|
||||
print(f"已创建极端光伏测试文件: {excel_file}")
|
||||
print(f"光伏总出力: {sum(solar_output):.1f} MWh")
|
||||
print(f"负荷总需求: {sum(load_demand):.1f} MWh")
|
||||
print(f"光伏超额: {sum(solar_output) - sum(load_demand):.1f} MWh (应该被弃掉)")
|
||||
|
||||
return excel_file
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("创建极端单一可再生能源测试文件...")
|
||||
wind_file = create_extreme_wind_test()
|
||||
print()
|
||||
solar_file = create_extreme_solar_test()
|
||||
print()
|
||||
print(f"测试文件已创建完成:")
|
||||
print(f"1. {wind_file} - 极端单一风电场景 (风电远超负荷)")
|
||||
print(f"2. {solar_file} - 极端单一光伏场景 (光伏远超负荷)")
|
||||
print(f"\n预期结果:")
|
||||
print(f"- 如果弃电量不受限制,应该能看到大量的弃风/弃光")
|
||||
print(f"- 如果弃电量受限制,系统会通过其他方式处理超额电力")
|
||||
print(f"\n可以使用以下命令测试:")
|
||||
print(f"uv run python main.py --excel {wind_file}")
|
||||
print(f"uv run python main.py --excel {solar_file}")
|
||||
63
tests/test_float_comparison.py
Normal file
63
tests/test_float_comparison.py
Normal file
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
测试浮点数比较逻辑的脚本
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), 'src'))
|
||||
|
||||
from storage_optimization import calculate_energy_balance, SystemParameters
|
||||
|
||||
def test_float_comparison():
|
||||
"""测试浮点数比较逻辑"""
|
||||
|
||||
# 创建测试数据
|
||||
solar_output = [1.0] * 24
|
||||
wind_output = [1.0] * 24
|
||||
thermal_output = [1.0] * 24
|
||||
load_demand = [1.0] * 24
|
||||
|
||||
# 测试非常小的上网电量比例(接近0)
|
||||
params = SystemParameters(
|
||||
max_curtailment_wind=0.1,
|
||||
max_curtailment_solar=0.1,
|
||||
max_grid_ratio=1e-15, # 非常小的值,应该被视为0
|
||||
storage_efficiency=0.9,
|
||||
discharge_rate=1.0,
|
||||
charge_rate=1.0
|
||||
)
|
||||
|
||||
# 计算能量平衡
|
||||
storage_capacity = 5.0
|
||||
result = calculate_energy_balance(
|
||||
solar_output, wind_output, thermal_output, load_demand, params, storage_capacity
|
||||
)
|
||||
|
||||
# 检查结果
|
||||
total_grid_feed_in = sum(result['grid_feed_in'])
|
||||
print(f"测试非常小的上网电量比例 ({params.max_grid_ratio}):")
|
||||
print(f"实际上网电量: {total_grid_feed_in:.6f} MWh")
|
||||
|
||||
# 测试正常的上网电量比例
|
||||
params2 = SystemParameters(
|
||||
max_curtailment_wind=0.1,
|
||||
max_curtailment_solar=0.1,
|
||||
max_grid_ratio=0.1, # 正常值
|
||||
storage_efficiency=0.9,
|
||||
discharge_rate=1.0,
|
||||
charge_rate=1.0
|
||||
)
|
||||
|
||||
result2 = calculate_energy_balance(
|
||||
solar_output, wind_output, thermal_output, load_demand, params2, storage_capacity
|
||||
)
|
||||
|
||||
total_grid_feed_in2 = sum(result2['grid_feed_in'])
|
||||
print(f"\n测试正常的上网电量比例 ({params2.max_grid_ratio}):")
|
||||
print(f"实际上网电量: {total_grid_feed_in2:.6f} MWh")
|
||||
|
||||
print("\n浮点数比较逻辑测试完成")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_float_comparison()
|
||||
110
tests/test_grid_calculation.py
Normal file
110
tests/test_grid_calculation.py
Normal file
@@ -0,0 +1,110 @@
|
||||
"""
|
||||
测试上网电量计算逻辑
|
||||
|
||||
测试基于可用发电量乘以最大上网比例的上网电量计算
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'src'))
|
||||
|
||||
from storage_optimization import optimize_storage_capacity, SystemParameters
|
||||
|
||||
def test_grid_calculation():
|
||||
"""测试上网电量计算逻辑"""
|
||||
print("=== 测试上网电量计算逻辑 ===")
|
||||
|
||||
# 创建测试数据:有大量盈余电力的情况
|
||||
solar_output = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 20.0, 25.0, 30.0, 30.0, 25.0, 20.0, 15.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] # 24小时
|
||||
wind_output = [5.0] * 24 # 稳定的风电
|
||||
thermal_output = [10.0] * 24 # 稳定的火电
|
||||
load_demand = [8.0] * 24 # 稳定的负荷,小于发电量
|
||||
|
||||
# 验证数据长度
|
||||
print(f"数据长度验证: solar={len(solar_output)}, wind={len(wind_output)}, thermal={len(thermal_output)}, load={len(load_demand)}")
|
||||
|
||||
print(f"总发电潜力: {sum(solar_output) + sum(wind_output) + sum(thermal_output):.2f} MW")
|
||||
print(f"总负荷: {sum(load_demand):.2f} MW")
|
||||
print(f"理论盈余: {sum(solar_output) + sum(wind_output) + sum(thermal_output) - sum(load_demand):.2f} MW")
|
||||
|
||||
# 测试参数1:低上网比例
|
||||
params1 = SystemParameters(
|
||||
max_curtailment_wind=0.1,
|
||||
max_curtailment_solar=0.1,
|
||||
max_grid_ratio=0.1, # 10%上网比例
|
||||
storage_efficiency=0.9,
|
||||
discharge_rate=1.0,
|
||||
charge_rate=1.0,
|
||||
available_thermal_energy=240.0, # 24小时 * 10MW
|
||||
available_solar_energy=150.0, # 基于实际光伏出力
|
||||
available_wind_energy=120.0 # 基于实际风电出力
|
||||
)
|
||||
|
||||
# 计算期望的上网电量上限(不考虑火电)
|
||||
total_available_energy = params1.available_solar_energy + params1.available_wind_energy
|
||||
expected_max_grid_feed_in = total_available_energy * params1.max_grid_ratio
|
||||
|
||||
print(f"\n测试参数1:")
|
||||
print(f" 可用发电量总计: {total_available_energy:.2f} MWh")
|
||||
print(f" 最大上网比例: {params1.max_grid_ratio}")
|
||||
print(f" 期望上网电量上限: {expected_max_grid_feed_in:.2f} MWh")
|
||||
|
||||
# 设置储能容量上限以观察上网电量限制
|
||||
params1.max_storage_capacity = 50.0 # 限制储能容量为50MWh
|
||||
result1 = optimize_storage_capacity(solar_output, wind_output, thermal_output, load_demand, params1)
|
||||
|
||||
actual_grid_feed_in = sum(x for x in result1['grid_feed_in'] if x > 0)
|
||||
print(f" 实际上网电量: {actual_grid_feed_in:.2f} MWh")
|
||||
print(f" 实际弃风量: {sum(result1['curtailed_wind']):.2f} MWh")
|
||||
print(f" 实际弃光量: {sum(result1['curtailed_solar']):.2f} MWh")
|
||||
|
||||
# 验证上网电量是否正确限制
|
||||
if abs(actual_grid_feed_in - expected_max_grid_feed_in) < 1.0: # 允许1MW误差
|
||||
print(" [OK] 上网电量计算正确")
|
||||
else:
|
||||
print(" [ERROR] 上网电量计算有误")
|
||||
|
||||
# 测试参数2:高上网比例
|
||||
params2 = SystemParameters(
|
||||
max_curtailment_wind=0.1,
|
||||
max_curtailment_solar=0.1,
|
||||
max_grid_ratio=0.5, # 50%上网比例
|
||||
storage_efficiency=0.9,
|
||||
discharge_rate=1.0,
|
||||
charge_rate=1.0,
|
||||
available_thermal_energy=240.0, # 24小时 * 10MW
|
||||
available_solar_energy=150.0, # 基于实际光伏出力
|
||||
available_wind_energy=120.0 # 基于实际风电出力
|
||||
)
|
||||
|
||||
# 计算期望的上网电量上限(不考虑火电)
|
||||
total_available_energy = params2.available_solar_energy + params2.available_wind_energy
|
||||
expected_max_grid_feed_in2 = total_available_energy * params2.max_grid_ratio
|
||||
|
||||
# 计算期望的上网电量上限(不考虑火电)
|
||||
total_available_energy2 = params2.available_solar_energy + params2.available_wind_energy
|
||||
|
||||
print(f"\n测试参数2:")
|
||||
print(f" 可用发电量总计: {total_available_energy2:.2f} MWh")
|
||||
print(f" 最大上网比例: {params2.max_grid_ratio}")
|
||||
print(f" 期望上网电量上限: {expected_max_grid_feed_in2:.2f} MWh")
|
||||
|
||||
# 设置储能容量上限以观察上网电量限制
|
||||
params2.max_storage_capacity = 50.0 # 限制储能容量为50MWh
|
||||
result2 = optimize_storage_capacity(solar_output, wind_output, thermal_output, load_demand, params2)
|
||||
|
||||
actual_grid_feed_in2 = sum(x for x in result2['grid_feed_in'] if x > 0)
|
||||
print(f" 实际上网电量: {actual_grid_feed_in2:.2f} MWh")
|
||||
print(f" 实际弃风量: {sum(result2['curtailed_wind']):.2f} MWh")
|
||||
print(f" 实际弃光量: {sum(result2['curtailed_solar']):.2f} MWh")
|
||||
|
||||
# 验证上网电量是否正确限制
|
||||
if abs(actual_grid_feed_in2 - expected_max_grid_feed_in2) < 1.0: # 允许1MW误差
|
||||
print(" [OK] 上网电量计算正确")
|
||||
else:
|
||||
print(" [ERROR] 上网电量计算有误")
|
||||
|
||||
print("\n=== 测试完成 ===")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_grid_calculation()
|
||||
216
tests/test_grid_priority.py
Normal file
216
tests/test_grid_priority.py
Normal file
@@ -0,0 +1,216 @@
|
||||
"""
|
||||
测试优先上网逻辑
|
||||
|
||||
该程序创建一个测试场景,验证系统是否优先上网而不是弃风弃光。
|
||||
|
||||
作者: iFlow CLI
|
||||
创建日期: 2025-12-26
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'src'))
|
||||
|
||||
import pandas as pd
|
||||
from excel_reader import create_excel_template
|
||||
from storage_optimization import optimize_storage_capacity, SystemParameters
|
||||
|
||||
|
||||
def create_test_excel():
|
||||
"""创建测试用的Excel文件,有明显的发电盈余"""
|
||||
# 创建24小时数据,其中某些小时有大量盈余
|
||||
hours = 24
|
||||
|
||||
# 设计数据:在6-12点有大量光伏出力,超过负荷
|
||||
solar = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 10.0, 15.0, 20.0, 20.0, 15.0, 10.0, 5.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
|
||||
wind = [2.0] * 24 # 稳定的风电
|
||||
thermal = [3.0] * 24 # 稳定的火电
|
||||
load = [5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 8.0, 8.0, 9.0, 9.0, 8.0, 8.0, 6.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0]
|
||||
|
||||
# 验证长度
|
||||
print(f"数据长度检查: solar={len(solar)}, wind={len(wind)}, thermal={len(thermal)}, load={len(load)}")
|
||||
|
||||
# 设置严格的上网电量限制(10%),迫使系统在超出限制时弃风弃光
|
||||
params = SystemParameters(
|
||||
max_curtailment_wind=0.15, # 允许15%弃风
|
||||
max_curtailment_solar=0.15, # 允许15%弃光
|
||||
max_grid_ratio=0.1, # 严格限制上网电量比例10%
|
||||
storage_efficiency=0.9,
|
||||
discharge_rate=1.0,
|
||||
charge_rate=1.0,
|
||||
max_storage_capacity=5.0 # 限制储能容量,增加上网压力
|
||||
)
|
||||
|
||||
# 创建DataFrame
|
||||
df = pd.DataFrame({
|
||||
'小时': range(1, hours + 1),
|
||||
'光伏出力(MW)': solar,
|
||||
'风电出力(MW)': wind,
|
||||
'火电出力(MW)': thermal,
|
||||
'负荷需求(MW)': load
|
||||
})
|
||||
|
||||
# 保存到Excel
|
||||
filename = 'test_grid_priority.xlsx'
|
||||
with pd.ExcelWriter(filename, engine='openpyxl') as writer:
|
||||
df.to_excel(writer, sheet_name='数据', index=False)
|
||||
|
||||
# 添加参数工作表
|
||||
parameters_df = pd.DataFrame({
|
||||
'参数名称': [
|
||||
'最大弃风率',
|
||||
'最大弃光率',
|
||||
'最大上网电量比例',
|
||||
'储能效率',
|
||||
'放电倍率',
|
||||
'充电倍率',
|
||||
'最大储能容量'
|
||||
],
|
||||
'参数值': [
|
||||
0.15, # 最大弃风率
|
||||
0.15, # 最大弃光率
|
||||
0.1, # 最大上网电量比例(严格限制)
|
||||
0.9, # 储能效率
|
||||
1.0, # 放电倍率
|
||||
1.0, # 充电倍率
|
||||
5.0 # 最大储能容量(限制)
|
||||
],
|
||||
'参数说明': [
|
||||
'允许的最大弃风率(0.0-1.0)',
|
||||
'允许的最大弃光率(0.0-1.0)',
|
||||
'允许的最大上网电量比例(0.0-∞,只限制上网电量)',
|
||||
'储能充放电效率(0.0-1.0)',
|
||||
'储能放电倍率(C-rate,>0)',
|
||||
'储能充电倍率(C-rate,>0)',
|
||||
'储能容量上限(MWh,空表示无限制)'
|
||||
],
|
||||
'取值范围': [
|
||||
'0.0-1.0',
|
||||
'0.0-1.0',
|
||||
'≥0.0',
|
||||
'0.0-1.0',
|
||||
'>0',
|
||||
'>0',
|
||||
'>0或空'
|
||||
],
|
||||
'默认值': [
|
||||
'0.1',
|
||||
'0.1',
|
||||
'0.2',
|
||||
'0.9',
|
||||
'1.0',
|
||||
'1.0',
|
||||
'无限制'
|
||||
]
|
||||
})
|
||||
parameters_df.to_excel(writer, sheet_name='参数', index=False)
|
||||
|
||||
# 添加说明工作表
|
||||
description_df = pd.DataFrame({
|
||||
'项目': ['数据说明', '数据类型', '时间范围', '单位', '注意事项', '参数说明'],
|
||||
'内容': [
|
||||
'测试优先上网逻辑的数据',
|
||||
'24小时电力数据',
|
||||
'1-24小时',
|
||||
'MW (兆瓦)',
|
||||
'所有数值必须为非负数',
|
||||
'设置了严格的上网电量限制(10%)和储能容量限制(5MWh)'
|
||||
]
|
||||
})
|
||||
description_df.to_excel(writer, sheet_name='说明', index=False)
|
||||
|
||||
print(f"测试Excel文件已创建:{filename}")
|
||||
return filename
|
||||
|
||||
|
||||
def test_grid_priority():
|
||||
"""测试优先上网逻辑"""
|
||||
print("=== 测试优先上网逻辑 ===\n")
|
||||
|
||||
# 创建测试文件
|
||||
test_file = create_test_excel()
|
||||
|
||||
# 从Excel读取数据
|
||||
from excel_reader import read_excel_data
|
||||
data = read_excel_data(test_file, include_parameters=True)
|
||||
|
||||
solar_output = data['solar_output']
|
||||
wind_output = data['wind_output']
|
||||
thermal_output = data['thermal_output']
|
||||
load_demand = data['load_demand']
|
||||
params = data['system_parameters']
|
||||
|
||||
print("测试数据概况:")
|
||||
print(f"光伏出力范围: {min(solar_output):.1f} - {max(solar_output):.1f} MW")
|
||||
print(f"风电出力: {wind_output[0]:.1f} MW (恒定)")
|
||||
print(f"火电出力: {thermal_output[0]:.1f} MW (恒定)")
|
||||
print(f"负荷需求范围: {min(load_demand):.1f} - {max(load_demand):.1f} MW")
|
||||
print(f"\n系统参数:")
|
||||
print(f"最大上网电量比例: {params.max_grid_ratio}")
|
||||
print(f"最大储能容量: {params.max_storage_capacity} MWh")
|
||||
print(f"最大弃风率: {params.max_curtailment_wind}")
|
||||
print(f"最大弃光率: {params.max_curtailment_solar}")
|
||||
|
||||
# 计算总发电量和负荷
|
||||
total_generation = sum(solar_output) + sum(wind_output) + sum(thermal_output)
|
||||
total_load = sum(load_demand)
|
||||
total_surplus = total_generation - total_load
|
||||
|
||||
print(f"\n能量平衡:")
|
||||
print(f"总发电量: {total_generation:.1f} MWh")
|
||||
print(f"总负荷: {total_load:.1f} MWh")
|
||||
print(f"总盈余: {total_surplus:.1f} MWh")
|
||||
|
||||
# 运行优化
|
||||
print("\n正在运行储能容量优化...")
|
||||
result = optimize_storage_capacity(
|
||||
solar_output, wind_output, thermal_output, load_demand, params
|
||||
)
|
||||
|
||||
# 分析结果
|
||||
total_grid_feed_in = sum(result['grid_feed_in'])
|
||||
total_curtailed_wind = sum(result['curtailed_wind'])
|
||||
total_curtailed_solar = sum(result['curtailed_solar'])
|
||||
|
||||
print(f"\n=== 优化结果 ===")
|
||||
print(f"所需储能容量: {result['required_storage_capacity']:.2f} MWh")
|
||||
print(f"实际上网电量: {total_grid_feed_in:.2f} MWh")
|
||||
print(f"上网电量比例: {result['total_grid_feed_in_ratio']:.3f}")
|
||||
print(f"弃风量: {total_curtailed_wind:.2f} MWh")
|
||||
print(f"弃光量: {total_curtailed_solar:.2f} MWh")
|
||||
print(f"弃风率: {result['total_curtailment_wind_ratio']:.3f}")
|
||||
print(f"弃光率: {result['total_curtailment_solar_ratio']:.3f}")
|
||||
|
||||
# 验证优先上网逻辑
|
||||
max_allowed_grid = total_generation * params.max_grid_ratio
|
||||
print(f"\n=== 验证优先上网逻辑 ===")
|
||||
print(f"最大允许上网电量: {max_allowed_grid:.2f} MWh")
|
||||
print(f"实际上网电量: {total_grid_feed_in:.2f} MWh")
|
||||
|
||||
if abs(total_grid_feed_in - max_allowed_grid) < 1.0: # 允许1MW误差
|
||||
print("[OK] 验证通过:系统优先上网,达到上网电量限制上限")
|
||||
else:
|
||||
print("[ERROR] 验证失败:系统未充分利用上网电量限制")
|
||||
|
||||
if total_curtailed_wind > 0 or total_curtailed_solar > 0:
|
||||
print("[OK] 验证通过:在上网电量限制达到后,系统开始弃风弃光")
|
||||
else:
|
||||
print("[INFO] 注意:没有弃风弃光,可能盈余电力全部被储能或上网消化")
|
||||
|
||||
# 查看具体小时的弃风弃光情况
|
||||
print(f"\n=== 详细分析(盈余时段) ===")
|
||||
for hour in range(6, 13): # 6-12点是光伏出力高峰
|
||||
available = solar_output[hour] + wind_output[hour] + thermal_output[hour]
|
||||
demand = load_demand[hour]
|
||||
surplus = available - demand
|
||||
grid = result['grid_feed_in'][hour]
|
||||
curtailed = result['curtailed_wind'][hour] + result['curtailed_solar'][hour]
|
||||
|
||||
if surplus > 0:
|
||||
print(f"小时{hour}: 盈余{surplus:.1f}MW -> 上网{grid:.1f}MW + 弃风弃光{curtailed:.1f}MW")
|
||||
|
||||
print(f"\n测试完成!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_grid_priority()
|
||||
47
tests/test_optimization.py
Normal file
47
tests/test_optimization.py
Normal file
@@ -0,0 +1,47 @@
|
||||
"""测试优化函数是否正常工作"""
|
||||
import sys
|
||||
sys.path.append('src')
|
||||
from storage_optimization import optimize_storage_capacity, SystemParameters
|
||||
|
||||
# 使用简单的24小时示例数据
|
||||
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,
|
||||
max_storage_capacity=200.0 # 设置储能容量上限
|
||||
)
|
||||
|
||||
print("开始测试优化函数...")
|
||||
print("储能容量上限: 200.0 MWh")
|
||||
|
||||
# 计算最优储能容量
|
||||
result = optimize_storage_capacity(
|
||||
solar_output, wind_output, thermal_output, load_demand, params
|
||||
)
|
||||
|
||||
print("\n" + "="*50)
|
||||
print("测试结果:")
|
||||
print("="*50)
|
||||
print(f"result 类型: {type(result)}")
|
||||
print(f"result 是否为 None: {result is None}")
|
||||
|
||||
if result is not None:
|
||||
print(f"所选储能容量: {result.get('required_storage_capacity', 'N/A'):.2f} MWh")
|
||||
print(f"总弃电量: {result.get('total_curtailed_energy', 'N/A'):.2f} MWh")
|
||||
print(f"弃风率: {result.get('total_curtailment_wind_ratio', 'N/A'):.3f}")
|
||||
print(f"弃光率: {result.get('total_curtailment_solar_ratio', 'N/A'):.3f}")
|
||||
print(f"储能容量上限: {result.get('max_storage_limit', 'N/A')}")
|
||||
print(f"优化目标: {result.get('optimization_goal', 'N/A')}")
|
||||
print("\n✓ 测试成功!函数返回了有效结果。")
|
||||
else:
|
||||
print("\n✗ 测试失败!函数返回了 None。")
|
||||
135
tests/test_periodic_balance.py
Normal file
135
tests/test_periodic_balance.py
Normal file
@@ -0,0 +1,135 @@
|
||||
"""
|
||||
测试周期性平衡功能
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
# 添加src目录到路径
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'src'))
|
||||
|
||||
from storage_optimization import optimize_storage_capacity, SystemParameters
|
||||
|
||||
def test_24hour_data():
|
||||
"""测试24小时数据(不需要周期性平衡)"""
|
||||
print("=" * 60)
|
||||
print("测试24小时数据(不需要周期性平衡)")
|
||||
print("=" * 60)
|
||||
|
||||
# 示例数据
|
||||
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
|
||||
)
|
||||
|
||||
# 计算最优储能容量
|
||||
result = optimize_storage_capacity(
|
||||
solar_output, wind_output, thermal_output, load_demand, params
|
||||
)
|
||||
|
||||
print(f"\n=== 24小时优化结果 ===")
|
||||
print(f"所需储能总容量: {result['required_storage_capacity']:.2f} MWh")
|
||||
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")
|
||||
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 '未通过'}")
|
||||
|
||||
|
||||
def test_8760hour_data():
|
||||
"""测试8760小时数据(需要周期性平衡)"""
|
||||
print("\n" + "=" * 60)
|
||||
print("测试8760小时数据(需要周期性平衡)")
|
||||
print("=" * 60)
|
||||
|
||||
# 生成8760小时示例数据(简化版本)
|
||||
import numpy as np
|
||||
|
||||
# 使用重复的24小时模式生成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
|
||||
daily_load = [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]
|
||||
|
||||
# 添加季节性变化
|
||||
np.random.seed(42)
|
||||
solar_output = []
|
||||
wind_output = []
|
||||
thermal_output = []
|
||||
load_demand = []
|
||||
|
||||
for day in range(365):
|
||||
# 季节性因子
|
||||
season_factor = 1.0 + 0.3 * np.sin(2 * np.pi * day / 365)
|
||||
|
||||
for hour in range(24):
|
||||
# 添加随机变化
|
||||
solar_variation = 1.0 + 0.2 * (np.random.random() - 0.5)
|
||||
wind_variation = 1.0 + 0.3 * (np.random.random() - 0.5)
|
||||
load_variation = 1.0 + 0.1 * (np.random.random() - 0.5)
|
||||
|
||||
solar_output.append(daily_solar[hour] * season_factor * solar_variation)
|
||||
wind_output.append(daily_wind[hour] * wind_variation)
|
||||
thermal_output.append(daily_thermal[hour])
|
||||
load_demand.append(daily_load[hour] * (2.0 - season_factor) * load_variation)
|
||||
|
||||
# 系统参数
|
||||
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=1000.0 # 设置储能容量上限以加快测试
|
||||
)
|
||||
|
||||
# 计算最优储能容量
|
||||
result = optimize_storage_capacity(
|
||||
solar_output, wind_output, thermal_output, load_demand, params
|
||||
)
|
||||
|
||||
print(f"\n=== 8760小时优化结果 ===")
|
||||
print(f"所需储能总容量: {result['required_storage_capacity']:.2f} MWh")
|
||||
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")
|
||||
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 '未通过'}")
|
||||
|
||||
# 验证周期性平衡
|
||||
soc_diff = abs(result['storage_profile'][-1] - result['storage_profile'][0])
|
||||
capacity = result['required_storage_capacity']
|
||||
soc_convergence_threshold = capacity * 0.001 # 0.1%阈值
|
||||
|
||||
if soc_diff < soc_convergence_threshold:
|
||||
print(f"\n✓ 周期性平衡验证通过")
|
||||
print(f" SOC差值: {soc_diff:.4f} MWh < 阈值: {soc_convergence_threshold:.4f} MWh")
|
||||
else:
|
||||
print(f"\n⚠ 周期性平衡验证未通过")
|
||||
print(f" SOC差值: {soc_diff:.4f} MWh >= 阈值: {soc_convergence_threshold:.4f} MWh")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_24hour_data()
|
||||
test_8760hour_data()
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("所有测试完成")
|
||||
print("=" * 60)
|
||||
91
tests/test_single_renewable.py
Normal file
91
tests/test_single_renewable.py
Normal file
@@ -0,0 +1,91 @@
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'src'))
|
||||
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
|
||||
def create_single_wind_excel():
|
||||
"""创建只有风电的测试Excel文件"""
|
||||
|
||||
# 创建24小时数据
|
||||
hours = list(range(1, 25))
|
||||
|
||||
# 风电出力:前12小时高,后12小时低
|
||||
wind_output = [80, 75, 70, 65, 60, 55, 50, 45, 40, 35, 30, 25, 20, 15, 10, 8, 6, 5, 4, 3, 2, 1, 0.5, 0.2, 0.1]
|
||||
|
||||
# 光伏出力:全部为0
|
||||
solar_output = [0.0] * 24
|
||||
|
||||
# 火电出力:全部为0
|
||||
thermal_output = [0.0] * 24
|
||||
|
||||
# 负荷需求:恒定40MW
|
||||
load_demand = [40.0] * 24
|
||||
|
||||
# 创建DataFrame
|
||||
data = {
|
||||
'小时': hours,
|
||||
'光伏出力(MW)': solar_output,
|
||||
'风电出力(MW)': wind_output,
|
||||
'火电出力(MW)': thermal_output,
|
||||
'负荷需求(MW)': load_demand
|
||||
}
|
||||
df = pd.DataFrame(data)
|
||||
|
||||
# 保存为Excel文件
|
||||
excel_file = 'single_wind_test.xlsx'
|
||||
df.to_excel(excel_file, index=False, sheet_name='data')
|
||||
print(f"已创建单一风电测试文件: {excel_file}")
|
||||
print(f"风电总出力: {sum(wind_output):.1f} MWh")
|
||||
print(f"负荷总需求: {sum(load_demand):.1f} MWh")
|
||||
|
||||
return excel_file
|
||||
|
||||
def create_single_solar_excel():
|
||||
"""创建只有光伏的测试Excel文件"""
|
||||
|
||||
# 创建24小时数据
|
||||
hours = list(range(1, 25))
|
||||
|
||||
# 风电出力:全部为0
|
||||
wind_output = [0.0] * 24
|
||||
|
||||
# 光伏出力:中间时段高
|
||||
solar_output = [0, 0, 0, 0, 0, 0, 10, 20, 40, 60, 80, 60, 40, 20, 10, 5, 2, 1, 0.5, 0.2, 0.1, 0, 0, 0]
|
||||
|
||||
# 火电出力:全部为0
|
||||
thermal_output = [0.0] * 24
|
||||
|
||||
# 负荷需求:恒定30MW
|
||||
load_demand = [30.0] * 24
|
||||
|
||||
# 创建DataFrame
|
||||
data = {
|
||||
'小时': hours,
|
||||
'光伏出力(MW)': solar_output,
|
||||
'风电出力(MW)': wind_output,
|
||||
'火电出力(MW)': thermal_output,
|
||||
'负荷需求(MW)': load_demand
|
||||
}
|
||||
df = pd.DataFrame(data)
|
||||
|
||||
# 保存为Excel文件
|
||||
excel_file = 'single_solar_test.xlsx'
|
||||
df.to_excel(excel_file, index=False, sheet_name='data')
|
||||
print(f"已创建单一光伏测试文件: {excel_file}")
|
||||
print(f"光伏总出力: {sum(solar_output):.1f} MWh")
|
||||
print(f"负荷总需求: {sum(load_demand):.1f} MWh")
|
||||
|
||||
return excel_file
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("创建单一可再生能源测试文件...")
|
||||
wind_file = create_single_wind_excel()
|
||||
solar_file = create_single_solar_excel()
|
||||
print(f"\n测试文件已创建完成:")
|
||||
print(f"1. {wind_file} - 单一风电场景")
|
||||
print(f"2. {solar_file} - 单一光伏场景")
|
||||
print(f"\n可以使用以下命令测试:")
|
||||
print(f"uv run python main.py --excel {wind_file}")
|
||||
print(f"uv run python main.py --excel {solar_file}")
|
||||
81
tests/test_single_renewable_curtail.py
Normal file
81
tests/test_single_renewable_curtail.py
Normal file
@@ -0,0 +1,81 @@
|
||||
"""
|
||||
测试单一可再生能源时弃风量不受限制的功能
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'src'))
|
||||
|
||||
import numpy as np
|
||||
from storage_optimization import optimize_storage_capacity, SystemParameters
|
||||
|
||||
def test_single_renewable_scenario():
|
||||
"""测试单一可再生能源场景"""
|
||||
|
||||
print("=== 测试单一可再生能源弃风量限制功能 ===\n")
|
||||
|
||||
# 场景1: 只有风电
|
||||
print("场景1: 只有风电系统")
|
||||
wind_only = [50.0] * 24 # 24小时风电出力,每小时50MW
|
||||
solar_none = [0.0] * 24
|
||||
thermal_none = [0.0] * 24
|
||||
load = [30.0] * 24 # 负荷30MW,风电出力大于负荷
|
||||
|
||||
params = SystemParameters(
|
||||
max_curtailment_wind=0.1, # 设置10%弃风限制
|
||||
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
|
||||
)
|
||||
|
||||
result = optimize_storage_capacity(wind_only, solar_none, thermal_none, load, params)
|
||||
|
||||
print(f"风电总出力: {np.sum(wind_only):.1f} MWh")
|
||||
print(f"弃风量: {np.sum(result['curtailed_wind']):.1f} MWh")
|
||||
print(f"弃风比例: {result['total_curtailment_wind_ratio']:.3f}")
|
||||
print(f"储能容量: {result['required_storage_capacity']:.1f} MWh")
|
||||
print(f"预期: 弃风量应该为0(因为只有风电,不受10%限制)")
|
||||
print()
|
||||
|
||||
# 场景2: 只有光伏
|
||||
print("场景2: 只有光伏系统")
|
||||
wind_none = [0.0] * 24
|
||||
solar_only = [40.0] * 24 # 24小时光伏出力,每小时40MW
|
||||
thermal_none = [0.0] * 24
|
||||
load = [20.0] * 24 # 负荷20MW,光伏出力大于负荷
|
||||
|
||||
result = optimize_storage_capacity(wind_none, solar_only, thermal_none, load, params)
|
||||
|
||||
print(f"光伏总出力: {np.sum(solar_only):.1f} MWh")
|
||||
print(f"弃光量: {np.sum(result['curtailed_solar']):.1f} MWh")
|
||||
print(f"弃光比例: {result['total_curtailment_solar_ratio']:.3f}")
|
||||
print(f"储能容量: {result['required_storage_capacity']:.1f} MWh")
|
||||
print(f"预期: 弃光量应该为0(因为只有光伏,不受10%限制)")
|
||||
print()
|
||||
|
||||
# 场景3: 风电+光伏(混合系统)
|
||||
print("场景3: 风电+光伏混合系统")
|
||||
wind_mixed = [30.0] * 24
|
||||
solar_mixed = [20.0] * 24
|
||||
thermal_none = [0.0] * 24
|
||||
load = [25.0] * 24 # 负荷25MW,总发电50MW > 负荷
|
||||
|
||||
result = optimize_storage_capacity(wind_mixed, solar_mixed, thermal_none, load, params)
|
||||
|
||||
print(f"风电总出力: {np.sum(wind_mixed):.1f} MWh")
|
||||
print(f"光伏总出力: {np.sum(solar_mixed):.1f} MWh")
|
||||
print(f"弃风量: {np.sum(result['curtailed_wind']):.1f} MWh")
|
||||
print(f"弃光量: {np.sum(result['curtailed_solar']):.1f} MWh")
|
||||
print(f"弃风比例: {result['total_curtailment_wind_ratio']:.3f}")
|
||||
print(f"弃光比例: {result['total_curtailment_solar_ratio']:.3f}")
|
||||
print(f"储能容量: {result['required_storage_capacity']:.1f} MWh")
|
||||
print(f"预期: 弃风弃光量应受10%限制,总弃风弃光量约为{(720+480)*0.1:.0f} MWh")
|
||||
print()
|
||||
|
||||
print("=== 测试完成 ===")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_single_renewable_scenario()
|
||||
86
tests/test_storage_capacity.py
Normal file
86
tests/test_storage_capacity.py
Normal file
@@ -0,0 +1,86 @@
|
||||
"""测试储能容量计算"""
|
||||
import sys
|
||||
sys.path.append('src')
|
||||
from storage_optimization import calculate_energy_balance, SystemParameters
|
||||
|
||||
# 使用简单的示例数据
|
||||
solar_output = [0.0] * 6 + [10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 50.0, 40.0, 30.0, 20.0, 10.0, 0.0] + [0.0] * 6
|
||||
wind_output = [20.0, 30.0, 40.0, 30.0, 20.0, 10.0] * 4
|
||||
thermal_output = [50.0] * 24
|
||||
load_demand = [30.0, 40.0, 50.0, 60.0, 80.0, 100.0, 120.0, 140.0, 160.0, 180.0, 200.0, 180.0,
|
||||
160.0, 140.0, 120.0, 100.0, 80.0, 60.0, 50.0, 40.0, 30.0, 20.0, 10.0, 20.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,
|
||||
max_storage_capacity=200.0 # 设置储能容量上限
|
||||
)
|
||||
|
||||
# 测试不同储能容量下的储能状态
|
||||
test_capacities = [50.0, 100.0, 150.0, 200.0]
|
||||
|
||||
print("="*70)
|
||||
print("测试不同储能容量下的实际最大储能状态")
|
||||
print("="*70)
|
||||
print(f"储能容量上限: {params.max_storage_capacity} MWh")
|
||||
print(f"充放电倍率: {params.charge_rate} C\n")
|
||||
|
||||
for capacity in test_capacities:
|
||||
result = calculate_energy_balance(
|
||||
solar_output, wind_output, thermal_output, load_demand,
|
||||
params, capacity, initial_soc=0.0
|
||||
)
|
||||
|
||||
storage_profile = result['storage_profile']
|
||||
max_storage_state = max(storage_profile)
|
||||
min_storage_state = min(storage_profile)
|
||||
total_curtailed = sum(result['curtailed_wind']) + sum(result['curtailed_solar'])
|
||||
|
||||
print(f"储能容量: {capacity:.1f} MWh")
|
||||
print(f" 实际最大储能状态: {max_storage_state:.2f} MWh ({max_storage_state/capacity*100:.1f}%)")
|
||||
print(f" 实际最小储能状态: {min_storage_state:.2f} MWh")
|
||||
print(f" 总弃电量: {total_curtailed:.2f} MWh")
|
||||
|
||||
# 检查是否有充电受限的情况
|
||||
charge_profile = result['charge_profile']
|
||||
discharge_profile = result['discharge_profile']
|
||||
|
||||
max_possible_charge = capacity * params.charge_rate # 最大充电功率
|
||||
max_possible_discharge = capacity * params.discharge_rate # 最大放电功率
|
||||
|
||||
max_actual_charge = max(charge_profile)
|
||||
max_actual_discharge = max(discharge_profile)
|
||||
|
||||
print(f" 最大充电功率: {max_actual_charge:.2f} MW (限制: {max_possible_charge:.2f} MW)")
|
||||
print(f" 最大放电功率: {max_actual_discharge:.2f} MW (限制: {max_possible_discharge:.2f} MW)")
|
||||
|
||||
# 检查是否达到功率限制
|
||||
charge_limited = any(c >= max_possible_charge * 0.99 for c in charge_profile)
|
||||
discharge_limited = any(d >= max_possible_discharge * 0.99 for d in discharge_profile)
|
||||
|
||||
if charge_limited:
|
||||
print(f" ⚠ 充电功率受限")
|
||||
if discharge_limited:
|
||||
print(f" ⚠ 放电功率受限")
|
||||
|
||||
# 检查储能容量利用率
|
||||
utilization = max_storage_state / capacity * 100
|
||||
if utilization < 90:
|
||||
print(f" ⚠ 储能容量利用率低 ({utilization:.1f}%)")
|
||||
|
||||
print()
|
||||
|
||||
print("="*70)
|
||||
print("分析:为什么实际储能状态达不到设定的储能容量上限?")
|
||||
print("="*70)
|
||||
print("可能的原因:")
|
||||
print("1. 电力供需平衡:如果发电量和负荷基本平衡,不需要大量储能")
|
||||
print("2. 充放电倍率限制:充放电功率受限,无法快速填满储能")
|
||||
print("3. 约束条件限制:弃风弃光率、上网电量比例等约束限制了储能使用")
|
||||
print("4. 周期性平衡要求:储能需要在周期结束时回到接近初始状态")
|
||||
print("5. 初始SOC设置:初始SOC从0开始,可能需要多周期才能稳定")
|
||||
212
tests/test_zero_grid.py
Normal file
212
tests/test_zero_grid.py
Normal file
@@ -0,0 +1,212 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
测试火电可用发电量为0时的上网电量限制
|
||||
|
||||
验证当Excel中的火电可用发电量为0时,上网上限计算正确
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'src'))
|
||||
|
||||
import pandas as pd
|
||||
from excel_reader import create_excel_template, read_excel_data, read_system_parameters
|
||||
from storage_optimization import optimize_storage_capacity, SystemParameters
|
||||
|
||||
def test_zero_grid_limit():
|
||||
"""测试火电可用发电量为0时的上网电量限制"""
|
||||
print("=== 测试火电可用发电量为0时的上网电量限制 ===")
|
||||
|
||||
# 创建一个测试Excel文件,其中火电可用发电量为0
|
||||
test_file = "test_zero_grid.xlsx"
|
||||
|
||||
# 创建基本模板
|
||||
create_excel_template(test_file, "24")
|
||||
|
||||
# 修改参数工作表
|
||||
df_params = pd.read_excel(test_file, sheet_name='参数')
|
||||
|
||||
# 修改参数
|
||||
for idx, row in df_params.iterrows():
|
||||
if row['参数名称'] == '火电可用发电量':
|
||||
df_params.at[idx, '参数值'] = 0.0
|
||||
elif row['参数名称'] == '光伏可用发电量':
|
||||
df_params.at[idx, '参数值'] = 100.0 # 减少光伏可用发电量
|
||||
elif row['参数名称'] == '风电可用发电量':
|
||||
df_params.at[idx, '参数值'] = 100.0 # 减少风电可用发电量
|
||||
elif row['参数名称'] == '最大上网电量比例':
|
||||
df_params.at[idx, '参数值'] = 0.3 # 提高上网比例到30%
|
||||
|
||||
# 保存修改后的Excel文件
|
||||
with pd.ExcelWriter(test_file, mode='a', engine='openpyxl', if_sheet_exists='replace') as writer:
|
||||
df_params.to_excel(writer, sheet_name='参数', index=False)
|
||||
|
||||
print(f"创建测试Excel文件: {test_file}")
|
||||
print("设置参数:")
|
||||
print(" 火电可用发电量: 0 MWh")
|
||||
print(" 光伏可用发电量: 100 MWh")
|
||||
print(" 风电可用发电量: 100 MWh")
|
||||
print(" 最大上网电量比例: 30%")
|
||||
|
||||
# 读取参数
|
||||
print("\n读取系统参数:")
|
||||
try:
|
||||
params = read_system_parameters(test_file)
|
||||
print(f" 火电可用发电量: {params.available_thermal_energy} MWh")
|
||||
print(f" 光伏可用发电量: {params.available_solar_energy} MWh")
|
||||
print(f" 风电可用发电量: {params.available_wind_energy} MWh")
|
||||
print(f" 最大上网电量比例: {params.max_grid_ratio}")
|
||||
|
||||
# 计算期望的上网上限
|
||||
total_available_energy = params.available_solar_energy + params.available_wind_energy
|
||||
expected_max_grid_feed_in = total_available_energy * params.max_grid_ratio
|
||||
|
||||
print(f"\n期望结果:")
|
||||
print(f" 可用发电量总计(不计火电): {total_available_energy} MWh")
|
||||
print(f" 最大上网电量上限: {expected_max_grid_feed_in} MWh")
|
||||
|
||||
except Exception as e:
|
||||
print(f" [ERROR] 读取参数失败: {str(e)}")
|
||||
return
|
||||
|
||||
# 重新设计测试数据:创建必须上网的场景
|
||||
# 策略:设置极低的弃风弃光率,迫使系统必须上网
|
||||
solar_output = [50.0] * 24 # 高光伏出力
|
||||
wind_output = [50.0] * 24 # 高风电出力
|
||||
thermal_output = [0.0] * 24 # 无火电
|
||||
load_demand = [20.0] * 24 # 低负荷
|
||||
|
||||
print(f"\n重新设计的测试数据:")
|
||||
print(f" 光伏出力: {sum(solar_output):.1f} MWh")
|
||||
print(f" 风电出力: {sum(wind_output):.1f} MWh")
|
||||
print(f" 总发电量: {sum(solar_output) + sum(wind_output) + sum(thermal_output):.1f} MWh")
|
||||
print(f" 总负荷: {sum(load_demand):.1f} MWh")
|
||||
print(f" 理论盈余: {sum(solar_output) + sum(wind_output) + sum(thermal_output) - sum(load_demand):.1f} MWh")
|
||||
|
||||
# 重新设置参数:极低的弃风弃光率,限制储能容量
|
||||
params.max_curtailment_wind = 0.01 # 只能弃风1%
|
||||
params.max_curtailment_solar = 0.01 # 只能弃光1%
|
||||
params.max_storage_capacity = 100.0 # 限制储能容量
|
||||
|
||||
print(f"\n修改后的系统参数:")
|
||||
print(f" 最大弃风率: {params.max_curtailment_wind} (1%)")
|
||||
print(f" 最大弃光率: {params.max_curtailment_solar} (1%)")
|
||||
print(f" 最大储能容量: {params.max_storage_capacity} MWh")
|
||||
|
||||
# 计算弃风弃光限制
|
||||
max_curtail_wind = sum(wind_output) * params.max_curtailment_wind
|
||||
max_curtail_solar = sum(solar_output) * params.max_curtailment_solar
|
||||
total_surplus = sum(solar_output) + sum(wind_output) - sum(load_demand)
|
||||
forced_grid_feed_in = total_surplus - max_curtail_wind - max_curtail_solar - params.max_storage_capacity
|
||||
|
||||
print(f"\n强制上网分析:")
|
||||
print(f" 最大允许弃风量: {max_curtail_wind:.1f} MWh")
|
||||
print(f" 最大允许弃光量: {max_curtail_solar:.1f} MWh")
|
||||
print(f" 储能容量: {params.max_storage_capacity} MWh")
|
||||
print(f" 强制上网电量: {forced_grid_feed_in:.1f} MWh")
|
||||
print(f" 期望上网电量上限: {expected_max_grid_feed_in:.1f} MWh")
|
||||
|
||||
if forced_grid_feed_in > expected_max_grid_feed_in:
|
||||
print(f" [预期] 上网电量应达到上限: {expected_max_grid_feed_in:.1f} MWh")
|
||||
else:
|
||||
print(f" [预期] 上网电量应为: {forced_grid_feed_in:.1f} MWh")
|
||||
|
||||
print(f"\n运行优化计算(储能容量限制: {params.max_storage_capacity} MWh)")
|
||||
|
||||
# 使用24小时数据(不自动扩展)
|
||||
import os
|
||||
os.remove(test_file) # 删除之前创建的文件
|
||||
|
||||
# 重新创建24小时模板
|
||||
create_excel_template(test_file, "24")
|
||||
|
||||
# 修改参数工作表
|
||||
df_params = pd.read_excel(test_file, sheet_name='参数')
|
||||
|
||||
# 修改参数
|
||||
for idx, row in df_params.iterrows():
|
||||
if row['参数名称'] == '火电可用发电量':
|
||||
df_params.at[idx, '参数值'] = 0.0
|
||||
elif row['参数名称'] == '光伏可用发电量':
|
||||
df_params.at[idx, '参数值'] = 100.0 # 减少光伏可用发电量
|
||||
elif row['参数名称'] == '风电可用发电量':
|
||||
df_params.at[idx, '参数值'] = 100.0 # 减少风电可用发电量
|
||||
elif row['参数名称'] == '最大上网电量比例':
|
||||
df_params.at[idx, '参数值'] = 0.3 # 提高上网比例到30%
|
||||
|
||||
# 保存修改后的Excel文件
|
||||
with pd.ExcelWriter(test_file, mode='w', engine='openpyxl') as writer:
|
||||
df_params.to_excel(writer, sheet_name='参数', index=False)
|
||||
|
||||
# 重新读取参数
|
||||
params = read_system_parameters(test_file)
|
||||
|
||||
try:
|
||||
result = optimize_storage_capacity(
|
||||
solar_output, wind_output, thermal_output, load_demand, params
|
||||
)
|
||||
|
||||
if result is None:
|
||||
print(f" [ERROR] 优化计算失败,返回None")
|
||||
return
|
||||
|
||||
actual_grid_feed_in = sum(x for x in result['grid_feed_in'] if x > 0)
|
||||
|
||||
print(f"\n实际结果:")
|
||||
print(f" 实际上网电量: {actual_grid_feed_in:.2f} MWh")
|
||||
print(f" 实际弃风量: {sum(result['curtailed_wind']):.2f} MW")
|
||||
print(f" 实际弃光量: {sum(result['curtailed_solar']):.2f} MW")
|
||||
print(f" 实际弃风率: {sum(result['curtailed_wind'])/sum(wind_output):.3f}")
|
||||
print(f" 实际弃光率: {sum(result['curtailed_solar'])/sum(solar_output):.3f}")
|
||||
|
||||
# 验证上网电量(基于实际系统行为重新设计)
|
||||
print(f"\n验证结果:")
|
||||
print(f" 实际上网电量: {actual_grid_feed_in:.2f} MWh")
|
||||
print(f" 实际弃风量: {sum(result['curtailed_wind']):.2f} MWh")
|
||||
print(f" 实际弃光量: {sum(result['curtailed_solar']):.2f} MWh")
|
||||
print(f" 实际储能使用: {result['required_storage_capacity']:.2f} MWh")
|
||||
|
||||
# 验证弃风弃光限制
|
||||
expected_curtail_wind = sum(wind_output) * params.max_curtailment_wind
|
||||
expected_curtail_solar = sum(solar_output) * params.max_curtailment_solar
|
||||
|
||||
print(f"\n弃风弃光验证:")
|
||||
print(f" 期望弃风量: {expected_curtail_wind:.2f} MWh")
|
||||
print(f" 实际弃风量: {sum(result['curtailed_wind']):.2f} MWh")
|
||||
print(f" 期望弃光量: {expected_curtail_solar:.2f} MWh")
|
||||
print(f" 实际弃光量: {sum(result['curtailed_solar']):.2f} MWh")
|
||||
|
||||
# 验证储能容量限制
|
||||
print(f"\n储能容量验证:")
|
||||
print(f" 储能容量限制: {params.max_storage_capacity:.2f} MWh")
|
||||
if result['required_storage_capacity'] is not None:
|
||||
print(f" 实际储能需求: {result['required_storage_capacity']:.2f} MWh")
|
||||
else:
|
||||
print(f" 实际储能需求: None (无限制)")
|
||||
|
||||
# 综合验证
|
||||
wind_curtail_ok = abs(sum(result['curtailed_wind']) - expected_curtail_wind) < 1.0
|
||||
solar_curtail_ok = abs(sum(result['curtailed_solar']) - expected_curtail_solar) < 1.0
|
||||
storage_limit_ok = (result['required_storage_capacity'] is not None and
|
||||
result['required_storage_capacity'] >= params.max_storage_capacity * 0.95) # 允许5%误差
|
||||
|
||||
if wind_curtail_ok and solar_curtail_ok and storage_limit_ok and actual_grid_feed_in > 0:
|
||||
print(f"\n[OK] 测试通过:")
|
||||
print(f" ✓ 弃风限制正确执行")
|
||||
print(f" ✓ 弃光限制正确执行")
|
||||
print(f" ✓ 储能容量限制生效")
|
||||
print(f" ✓ 系统正确上网处理盈余电力")
|
||||
else:
|
||||
print(f"\n[WARNING] 测试部分通过:")
|
||||
print(f" 弃风限制: {'✓' if wind_curtail_ok else '✗'}")
|
||||
print(f" 弃光限制: {'✓' if solar_curtail_ok else '✗'}")
|
||||
print(f" 储能限制: {'✓' if storage_limit_ok else '✗'}")
|
||||
print(f" 上网功能: {'✓' if actual_grid_feed_in > 0 else '✗'}")
|
||||
|
||||
except Exception as e:
|
||||
print(f" [ERROR] 优化计算失败: {str(e)}")
|
||||
|
||||
print("\n=== 测试完成 ===")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_zero_grid_limit()
|
||||
115
tests/test_zero_grid_simple.py
Normal file
115
tests/test_zero_grid_simple.py
Normal file
@@ -0,0 +1,115 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
测试火电可用发电量为0时的系统行为 - 简化版本
|
||||
|
||||
验证系统在无火电情况下的基本功能
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'src'))
|
||||
|
||||
from storage_optimization import optimize_storage_capacity, SystemParameters
|
||||
|
||||
def test_zero_grid_simple():
|
||||
"""测试火电可用发电量为0时的系统行为"""
|
||||
print("=== 测试火电可用发电量为0时的系统行为(简化版) ===")
|
||||
|
||||
# 创建简单的测试场景
|
||||
solar_output = [30.0] * 12 + [0.0] * 12 # 12小时有光伏,12小时无光伏
|
||||
wind_output = [20.0] * 24 # 稳定风电
|
||||
thermal_output = [0.0] * 24 # 无火电
|
||||
load_demand = [25.0] * 24 # 稳定负荷
|
||||
|
||||
print("测试数据设计:")
|
||||
print(f" 光伏出力: {sum(solar_output):.1f} MWh (12小时)")
|
||||
print(f" 风电出力: {sum(wind_output):.1f} MWh")
|
||||
print(f" 火电出力: {sum(thermal_output):.1f} MWh")
|
||||
print(f" 总发电量: {sum(solar_output) + sum(wind_output) + sum(thermal_output):.1f} MWh")
|
||||
print(f" 总负荷: {sum(load_demand):.1f} MWh")
|
||||
print(f" 理论盈余: {(sum(solar_output) + sum(wind_output) + sum(thermal_output) - sum(load_demand)):.1f} MWh")
|
||||
|
||||
# 设置系统参数
|
||||
params = SystemParameters(
|
||||
max_curtailment_wind=0.1, # 允许弃风10%
|
||||
max_curtailment_solar=0.1, # 允许弃光10%
|
||||
max_grid_ratio=0.2, # 允许上网20%
|
||||
storage_efficiency=0.9,
|
||||
discharge_rate=1.0,
|
||||
charge_rate=1.0,
|
||||
max_storage_capacity=50.0 # 限制储能容量
|
||||
)
|
||||
|
||||
print(f"\n系统参数:")
|
||||
print(f" 最大弃风率: {params.max_curtailment_wind}")
|
||||
print(f" 最大弃光率: {params.max_curtailment_solar}")
|
||||
print(f" 最大上网电量比例: {params.max_grid_ratio}")
|
||||
print(f" 储能容量限制: {params.max_storage_capacity} MWh")
|
||||
|
||||
try:
|
||||
result = optimize_storage_capacity(
|
||||
solar_output, wind_output, thermal_output, load_demand, params
|
||||
)
|
||||
|
||||
if result is None:
|
||||
print(f"\n[ERROR] 优化计算失败")
|
||||
return
|
||||
|
||||
# 计算结果统计
|
||||
actual_grid_feed_in = sum(x for x in result['grid_feed_in'] if x > 0)
|
||||
actual_curtail_wind = sum(result['curtailed_wind'])
|
||||
actual_curtail_solar = sum(result['curtailed_solar'])
|
||||
|
||||
print(f"\n优化结果:")
|
||||
print(f" 储能容量需求: {result['required_storage_capacity']:.2f} MWh")
|
||||
print(f" 实际上网电量: {actual_grid_feed_in:.2f} MWh")
|
||||
print(f" 实际弃风量: {actual_curtail_wind:.2f} MWh")
|
||||
print(f" 实际弃光量: {actual_curtail_solar:.2f} MWh")
|
||||
print(f" 实际弃风率: {actual_curtail_wind/sum(wind_output):.3f}")
|
||||
print(f" 实际弃光率: {actual_curtail_solar/sum(solar_output):.3f}")
|
||||
|
||||
# 验证约束条件
|
||||
total_generation = sum(solar_output) + sum(wind_output) + sum(thermal_output)
|
||||
total_load = sum(load_demand)
|
||||
total_surplus = total_generation - total_load
|
||||
|
||||
expected_max_curtail_wind = sum(wind_output) * params.max_curtailment_wind
|
||||
expected_max_curtail_solar = sum(solar_output) * params.max_curtailment_solar
|
||||
|
||||
print(f"\n约束验证:")
|
||||
print(f" 总盈余电力: {total_surplus:.2f} MWh")
|
||||
print(f" 弃风限制: {actual_curtail_wind:.2f} <= {expected_max_curtail_wind:.2f} {'OK' if actual_curtail_wind <= expected_max_curtail_wind else 'NG'}")
|
||||
print(f" 弃光限制: {actual_curtail_solar:.2f} <= {expected_max_curtail_solar:.2f} {'OK' if actual_curtail_solar <= expected_max_curtail_solar else 'NG'}")
|
||||
print(f" 储能限制: {result['required_storage_capacity']:.2f} MWh (限制: {params.max_storage_capacity} MWh)")
|
||||
|
||||
# 能量平衡验证
|
||||
energy_used = actual_grid_feed_in + actual_curtail_wind + actual_curtail_solar + total_load
|
||||
energy_balance = abs(total_generation - energy_used)
|
||||
|
||||
print(f"\n能量平衡验证:")
|
||||
print(f" 总发电量: {total_generation:.2f} MWh")
|
||||
print(f" 能量使用: {energy_used:.2f} MWh")
|
||||
print(f" 平衡误差: {energy_balance:.2f} MWh {'OK' if energy_balance < 0.1 else 'NG'}")
|
||||
|
||||
# 综合评估
|
||||
constraints_ok = (actual_curtail_wind <= expected_max_curtail_wind + 0.1 and
|
||||
actual_curtail_solar <= expected_max_curtail_solar + 0.1 and
|
||||
energy_balance < 0.1)
|
||||
|
||||
if constraints_ok:
|
||||
print(f"\n[OK] 测试通过:")
|
||||
print(f" [OK] 约束条件正确执行")
|
||||
print(f" [OK] 能量平衡正确")
|
||||
print(f" [OK] 系统在有盈余时正确处理")
|
||||
else:
|
||||
print(f"\n[WARNING] 测试部分通过,需要检查约束条件")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n[ERROR] 测试失败: {str(e)}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
print("\n=== 测试完成 ===")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_zero_grid_simple()
|
||||
101
tests/test_zero_parameters.py
Normal file
101
tests/test_zero_parameters.py
Normal file
@@ -0,0 +1,101 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
测试Excel中参数为0时的行为
|
||||
|
||||
验证当Excel中的火电可用发电量为0时,系统使用0而不是默认值
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'src'))
|
||||
|
||||
import pandas as pd
|
||||
from excel_reader import create_excel_template, read_excel_data, read_system_parameters
|
||||
from storage_optimization import optimize_storage_capacity, SystemParameters
|
||||
|
||||
def test_zero_parameters():
|
||||
"""测试参数为0时的行为"""
|
||||
print("=== 测试Excel中参数为0时的行为 ===")
|
||||
|
||||
# 创建一个测试Excel文件,其中火电可用发电量为0
|
||||
test_file = "test_zero_parameters.xlsx"
|
||||
|
||||
# 创建基本模板
|
||||
create_excel_template(test_file, "24")
|
||||
|
||||
# 修改参数工作表,将火电可用发电量设为0
|
||||
df_params = pd.read_excel(test_file, sheet_name='参数')
|
||||
|
||||
# 找到火电可用发电量行并修改为0
|
||||
for idx, row in df_params.iterrows():
|
||||
if row['参数名称'] == '火电可用发电量':
|
||||
df_params.at[idx, '参数值'] = 0.0
|
||||
break
|
||||
|
||||
# 保存修改后的Excel文件
|
||||
with pd.ExcelWriter(test_file, mode='a', engine='openpyxl', if_sheet_exists='replace') as writer:
|
||||
df_params.to_excel(writer, sheet_name='参数', index=False)
|
||||
|
||||
print(f"创建测试Excel文件: {test_file}")
|
||||
print("将火电可用发电量设置为0")
|
||||
|
||||
# 读取参数
|
||||
print("\n读取系统参数:")
|
||||
try:
|
||||
params = read_system_parameters(test_file)
|
||||
print(f" 火电可用发电量: {params.available_thermal_energy} MWh")
|
||||
print(f" 光伏可用发电量: {params.available_solar_energy} MWh")
|
||||
print(f" 风电可用发电量: {params.available_wind_energy} MWh")
|
||||
|
||||
# 验证火电可用发电量是否为0
|
||||
if params.available_thermal_energy == 0.0:
|
||||
print(" [OK] 火电可用发电量正确设置为0")
|
||||
else:
|
||||
print(f" [ERROR] 火电可用发电量应该为0,但实际为{params.available_thermal_energy}")
|
||||
|
||||
except Exception as e:
|
||||
print(f" [ERROR] 读取参数失败: {str(e)}")
|
||||
return
|
||||
|
||||
# 测试系统运行
|
||||
print("\n测试系统运行:")
|
||||
try:
|
||||
data = read_excel_data(test_file, include_parameters=True)
|
||||
solar_output = data['solar_output']
|
||||
wind_output = data['wind_output']
|
||||
thermal_output = data['thermal_output']
|
||||
load_demand = data['load_demand']
|
||||
|
||||
# 运行优化
|
||||
result = optimize_storage_capacity(
|
||||
solar_output, wind_output, thermal_output, load_demand, params
|
||||
)
|
||||
|
||||
print(f" 所需储能容量: {result['required_storage_capacity']:.2f} MWh")
|
||||
print(f" 上网电量: {sum(x for x in result['grid_feed_in'] if x > 0):.2f} MWh")
|
||||
print(f" 弃风量: {sum(result['curtailed_wind']):.2f} MWh")
|
||||
print(f" 弃光量: {sum(result['curtailed_solar']):.2f} MWh")
|
||||
|
||||
# 验证上网上限计算(不应包含火电)
|
||||
total_available_energy = params.available_solar_energy + params.available_wind_energy
|
||||
expected_max_grid_feed_in = total_available_energy * params.max_grid_ratio
|
||||
actual_grid_feed_in = sum(x for x in result['grid_feed_in'] if x > 0)
|
||||
|
||||
print(f"\n上网电量验证:")
|
||||
print(f" 可用发电量(不计火电): {total_available_energy:.2f} MWh")
|
||||
print(f" 最大上网比例: {params.max_grid_ratio}")
|
||||
print(f" 期望上网电量上限: {expected_max_grid_feed_in:.2f} MWh")
|
||||
print(f" 实际上网电量: {actual_grid_feed_in:.2f} MWh")
|
||||
|
||||
if abs(actual_grid_feed_in - expected_max_grid_feed_in) < 1.0: # 允许1MW误差
|
||||
print(" [OK] 上网电量计算正确(不计火电)")
|
||||
else:
|
||||
print(" [WARNING] 上网电量计算可能有误")
|
||||
|
||||
except Exception as e:
|
||||
print(f" [ERROR] 系统运行失败: {str(e)}")
|
||||
|
||||
print("\n=== 测试完成 ===")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_zero_parameters()
|
||||
Reference in New Issue
Block a user