以最小弃电量为目标配置储能。

This commit is contained in:
dmy
2025-12-27 12:51:42 +08:00
parent 164b9da026
commit 25fc0b33aa
2 changed files with 140 additions and 51 deletions

View File

@@ -642,6 +642,14 @@ def main():
solar_output, wind_output, thermal_output, load_demand, params
)
# 显示优化结果摘要
print("\n=== 优化结果摘要 ===")
print(f"优化目标: 最小化弃电量")
print(f"所选储能容量: {result['required_storage_capacity']:.2f} MWh")
if result.get('total_curtailed_energy') is not None:
print(f"总弃电量: {result['total_curtailed_energy']:.2f} MWh")
print(f"储能容量上限: {result.get('max_storage_limit', '无限制')}")
# 绘制曲线
print("正在绘制系统运行曲线...")
plot_system_curves(solar_output, wind_output, thermal_output, load_demand, result, params.storage_efficiency, show_window, display_only, output_dir)

View File

@@ -447,10 +447,11 @@ def optimize_storage_capacity(
load_demand: List[float],
params: SystemParameters,
max_iterations: int = 100,
tolerance: float = 0.01
tolerance: float = 0.01,
search_step: float = 0.01
) -> Dict:
"""
优化储能容量,使用迭代方法寻找满足所有约束的最小储能容量
优化储能容量,在不超过设定储能容量上限的前提下,选择使弃电量最小储能容量
Args:
solar_output: 光伏出力曲线 (MW) - 支持24小时或8760小时
@@ -460,6 +461,7 @@ def optimize_storage_capacity(
params: 系统参数配置
max_iterations: 最大迭代次数
tolerance: 收敛容差
search_step: 搜索步长(相对于最大容量的比例)
Returns:
包含优化结果的字典
@@ -476,6 +478,7 @@ def optimize_storage_capacity(
upper_bound = min(theoretical_max, params.max_storage_capacity)
else:
upper_bound = theoretical_max
print("警告:未设置储能容量上限,将使用理论最大值")
# 判断数据类型24小时或8760小时
data_length = len(solar_output)
@@ -484,17 +487,26 @@ def optimize_storage_capacity(
if is_yearly_data:
print(f"处理8760小时全年数据启用周期性平衡优化...")
# 二分搜索寻找最小储能容量
# 在容量范围内搜索,找到弃电量最小储能容量
# 使用二分搜索 + 局部搜索相结合的方法
# 首先使用二分搜索找到一个满足约束的基准容量
print("第一阶段:二分搜索寻找满足约束的基准容量...")
best_capacity = upper_bound
best_result = None
solution_found = False # 标记是否找到可行解
best_curtailed = float('inf')
solution_found = False
# 二分搜索寻找最小可行容量
lower_bound_search = lower_bound
upper_bound_search = upper_bound
feasible_capacity = None
for iteration in range(max_iterations):
mid_capacity = (lower_bound + upper_bound) / 2
mid_capacity = (lower_bound_search + upper_bound_search) / 2
# 计算当前容量下的平衡
# 对于8760小时数据使用周期性平衡函数
# 对于24小时数据使用普通平衡函数初始SOC=0
if is_yearly_data:
balance_result = find_periodic_steady_state(
solar_output, wind_output, thermal_output, load_demand,
@@ -509,30 +521,20 @@ def optimize_storage_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
@@ -546,50 +548,125 @@ def optimize_storage_capacity(
if constraints_satisfied and daily_balance:
# 满足条件,尝试减小容量
best_capacity = mid_capacity
best_result = {**balance_result, **constraint_results}
solution_found = True
upper_bound = mid_capacity
feasible_capacity = mid_capacity
upper_bound_search = mid_capacity
else:
# 不满足条件,增大容量
lower_bound = mid_capacity
lower_bound_search = mid_capacity
# 检查收敛
if upper_bound - lower_bound < tolerance:
if upper_bound_search - lower_bound_search < tolerance:
break
# 处理储能容量上限限制的情况
if not solution_found and params.max_storage_capacity is not None:
print(f"警告:在储能容量上限 {params.max_storage_capacity:.2f} MWh 内无法找到满足所有约束的解")
print("使用最大允许容量进行计算,但某些约束条件可能无法满足")
# 如果找到了可行解,使用它作为基准;否则使用上限
if feasible_capacity is not None:
print(f"找到基准可行容量: {feasible_capacity:.2f} MWh")
else:
print(f"未找到可行解,使用上限容量: {upper_bound:.2f} MWh")
feasible_capacity = upper_bound
# 使用最大允许容量计算结果
# 第二阶段:在 [feasible_capacity, upper_bound] 范围内搜索弃电量最小的容量
print("\n第二阶段:在可行容量范围内搜索弃电量最小的储能容量...")
# 使用梯度下降方法搜索最优容量
# 定义搜索区间
search_lower = feasible_capacity
search_upper = upper_bound
# 计算当前最小弃电量
best_capacity = feasible_capacity
# 计算基准容量的弃电量
if is_yearly_data:
balance_result = find_periodic_steady_state(
base_result = find_periodic_steady_state(
solar_output, wind_output, thermal_output, load_demand,
params, params.max_storage_capacity
params, best_capacity
)
else:
balance_result = calculate_energy_balance(
solar_output, wind_output, thermal_output, load_demand, params, params.max_storage_capacity
base_result = calculate_energy_balance(
solar_output, wind_output, thermal_output, load_demand, params, best_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:
# 如果没有找到可行解(且没有容量上限限制),使用最大容量
base_curtailed = sum(base_result['curtailed_wind']) + sum(base_result['curtailed_solar'])
best_curtailed = base_curtailed
best_result = {**base_result, **check_constraints(solar_output, wind_output, thermal_output, base_result, params)}
print(f"基准容量 {best_capacity:.2f} MWh 的弃电量: {best_curtailed:.2f} MWh")
# 使用黄金分割搜索法寻找最优容量
# 黄金分割点
golden_ratio = (3 - 5**0.5) / 2 # 约0.382
# 初始化两个测试点
a = search_lower
b = search_upper
c = b - golden_ratio * (b - a)
d = a + golden_ratio * (b - a)
max_refinement_iterations = 50
for iter_num in range(max_refinement_iterations):
# 计算点 c 的弃电量
if is_yearly_data:
balance_result = find_periodic_steady_state(
result_c = find_periodic_steady_state(
solar_output, wind_output, thermal_output, load_demand,
params, upper_bound
params, c
)
else:
balance_result = calculate_energy_balance(
solar_output, wind_output, thermal_output, load_demand, params, upper_bound
result_c = calculate_energy_balance(
solar_output, wind_output, thermal_output, load_demand, params, c
)
constraint_results = check_constraints(solar_output, wind_output, thermal_output, balance_result, params)
best_result = {**balance_result, **constraint_results}
best_capacity = upper_bound
curtailed_c = sum(result_c['curtailed_wind']) + sum(result_c['curtailed_solar'])
# 计算点 d 的弃电量
if is_yearly_data:
result_d = find_periodic_steady_state(
solar_output, wind_output, thermal_output, load_demand,
params, d
)
else:
result_d = calculate_energy_balance(
solar_output, wind_output, thermal_output, load_demand, params, d
)
curtailed_d = sum(result_d['curtailed_wind']) + sum(result_d['curtailed_solar'])
# 比较并更新最优解
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 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")
print(f"\n最终选择储能容量: {best_capacity:.2f} MWh (容量上限: {upper_bound:.2f} MWh)")
print(f"最终弃电量: {best_curtailed:.2f} MWh")
# 添加能量平衡校验
total_generation = sum(thermal_output) + sum(wind_output) + sum(solar_output)
@@ -630,8 +707,8 @@ def optimize_storage_capacity(
print(f" 最终SOC: {best_result['storage_profile'][-1]:.4f} MWh")
print(f" SOC差值: {soc_initial_final_diff:.4f} MWh")
# 返回最终结果
return {
# 最终结果
result = {
'required_storage_capacity': best_capacity,
'storage_profile': best_result['storage_profile'],
'charge_profile': best_result['charge_profile'],
@@ -644,10 +721,14 @@ def optimize_storage_capacity(
'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
'total_curtailed_energy': best_curtailed, # 总弃电量
'min_curtailed_capacity': best_capacity, # 弃电量最小时的储能容量
'max_storage_limit': params.max_storage_capacity,
'optimization_goal': 'minimize_curtailment' # 优化目标:最小化弃电量
}
return result
def main():
"""主函数,提供示例使用"""