优化风电场设计方案对比算法:添加旋转算法和双模式对比
- 新增design_with_rotational_sweep函数:实现旋转优化算法 - 修改compare_design_methods函数: * 将MST结果纳入对比列表 * 每个电缆场景运行Base和Rotational两种算法 * 添加成本和损耗对比显示 * 优化可视化展示和文件输出 - 改进算法选择逻辑:增强簇数计算的智能化 - 更新输出格式:区分不同算法结果并优化显示
This commit is contained in:
256
main.py
256
main.py
@@ -274,10 +274,10 @@ def design_with_kmeans(turbines, substation, n_clusters=3):
|
|||||||
|
|
||||||
return cluster_connections + substation_connections, turbines
|
return cluster_connections + substation_connections, turbines
|
||||||
|
|
||||||
# 3.5 带容量约束的扇区扫描算法 (Capacitated Angular Sweep)
|
# 3.5 带容量约束的扇区扫描算法 (Capacitated Sweep) - 基础版
|
||||||
def design_with_capacitated_sweep(turbines, substation, cable_specs=None):
|
def design_with_capacitated_sweep(turbines, substation, cable_specs=None):
|
||||||
"""
|
"""
|
||||||
使用带容量约束的扇区扫描算法设计集电线路
|
使用带容量约束的扇区扫描算法设计集电线路 (基础版:单次扫描)
|
||||||
原理:
|
原理:
|
||||||
1. 计算所有风机相对于升压站的角度。
|
1. 计算所有风机相对于升压站的角度。
|
||||||
2. 找到角度间隔最大的位置作为起始“切割线”,以避免切断密集的风机群。
|
2. 找到角度间隔最大的位置作为起始“切割线”,以避免切断密集的风机群。
|
||||||
@@ -286,7 +286,7 @@ def design_with_capacitated_sweep(turbines, substation, cable_specs=None):
|
|||||||
"""
|
"""
|
||||||
# 1. 获取电缆最大容量
|
# 1. 获取电缆最大容量
|
||||||
max_mw = get_max_cable_capacity_mw(cable_specs)
|
max_mw = get_max_cable_capacity_mw(cable_specs)
|
||||||
print(f"DEBUG: 扇区扫描算法启动 - 单回路容量限制: {max_mw:.2f} MW")
|
# print(f"DEBUG: 扇区扫描算法启动 - 单回路容量限制: {max_mw:.2f} MW")
|
||||||
|
|
||||||
substation_coord = substation[0]
|
substation_coord = substation[0]
|
||||||
|
|
||||||
@@ -311,18 +311,12 @@ def design_with_capacitated_sweep(turbines, substation, cable_specs=None):
|
|||||||
wrap_diff = (2 * np.pi) - (angles[-1] - angles[0])
|
wrap_diff = (2 * np.pi) - (angles[-1] - angles[0])
|
||||||
diffs = np.append(diffs, wrap_diff)
|
diffs = np.append(diffs, wrap_diff)
|
||||||
|
|
||||||
# 找到最大间隙的索引 (该索引对应元素的 *后面* 是间隙)
|
# 找到最大间隙的索引
|
||||||
max_gap_idx = np.argmax(diffs)
|
max_gap_idx = np.argmax(diffs)
|
||||||
|
|
||||||
# 如果最大间隙不是在队尾,则需要旋转数组,使最大间隙成为新的起点(即队尾)
|
# 旋转数组,使最大间隙成为新的起点
|
||||||
# 实际上我们希望扫描的起点是最大间隙的“右侧”
|
|
||||||
# 例如: [0, 10, 100, 110]. Gaps: 10, 90, 10, 250. Max gap is wrap (250). Start at 0 is fine.
|
|
||||||
# 例如: [0, 10, 200, 210]. Gaps: 10, 190, 10, 150. Max gap is 190 (idx 1).
|
|
||||||
# 我们应该从 idx 2 (200) 开始扫描。
|
|
||||||
|
|
||||||
start_idx = (max_gap_idx + 1) % n
|
start_idx = (max_gap_idx + 1) % n
|
||||||
|
|
||||||
# 重新排序风机列表
|
|
||||||
if start_idx != 0:
|
if start_idx != 0:
|
||||||
work_df = pd.concat([work_df.iloc[start_idx:], work_df.iloc[:start_idx]]).reset_index(drop=True)
|
work_df = pd.concat([work_df.iloc[start_idx:], work_df.iloc[:start_idx]]).reset_index(drop=True)
|
||||||
|
|
||||||
@@ -335,10 +329,7 @@ def design_with_capacitated_sweep(turbines, substation, cable_specs=None):
|
|||||||
for i, row in work_df.iterrows():
|
for i, row in work_df.iterrows():
|
||||||
p = row['power']
|
p = row['power']
|
||||||
|
|
||||||
# 检查是否超载
|
|
||||||
# 注意:如果是空簇,必须加入至少一个(即使单个风机超载也得加,但在风电中单机不会超载)
|
|
||||||
if len(current_indices) > 0 and (current_power + p > max_mw):
|
if len(current_indices) > 0 and (current_power + p > max_mw):
|
||||||
# 当前簇已满,结束当前簇,开启新簇
|
|
||||||
work_df.loc[current_indices, 'cluster'] = cluster_id
|
work_df.loc[current_indices, 'cluster'] = cluster_id
|
||||||
cluster_id += 1
|
cluster_id += 1
|
||||||
current_power = 0
|
current_power = 0
|
||||||
@@ -347,22 +338,15 @@ def design_with_capacitated_sweep(turbines, substation, cable_specs=None):
|
|||||||
current_indices.append(i)
|
current_indices.append(i)
|
||||||
current_power += p
|
current_power += p
|
||||||
|
|
||||||
# 处理最后一个簇
|
|
||||||
if current_indices:
|
if current_indices:
|
||||||
work_df.loc[current_indices, 'cluster'] = cluster_id
|
work_df.loc[current_indices, 'cluster'] = cluster_id
|
||||||
cluster_id += 1 # 计数用
|
cluster_id += 1
|
||||||
|
|
||||||
print(f"DEBUG: 生成了 {cluster_id} 个回路 (簇)")
|
|
||||||
|
|
||||||
# 将 cluster 标记映射回原始 turbines DataFrame (通过 original_id 或 索引匹配)
|
|
||||||
# 这里我们简单地重建 turbines,因为 work_df 包含了所有信息且顺序变了
|
|
||||||
# 为了保持外部一致性,我们把 cluster 列 map 回去
|
|
||||||
|
|
||||||
# 建立 id -> cluster 的映射
|
# 建立 id -> cluster 的映射
|
||||||
id_to_cluster = dict(zip(work_df['id'], work_df['cluster']))
|
id_to_cluster = dict(zip(work_df['id'], work_df['cluster']))
|
||||||
turbines['cluster'] = turbines['id'].map(id_to_cluster)
|
turbines['cluster'] = turbines['id'].map(id_to_cluster)
|
||||||
|
|
||||||
# 5. 对每个簇内部进行MST连接 (复用现有逻辑)
|
# 5. 对每个簇内部进行MST连接
|
||||||
cluster_connections = []
|
cluster_connections = []
|
||||||
substation_connections = []
|
substation_connections = []
|
||||||
n_clusters = cluster_id
|
n_clusters = cluster_id
|
||||||
@@ -372,7 +356,6 @@ def design_with_capacitated_sweep(turbines, substation, cable_specs=None):
|
|||||||
if len(cluster_turbines) == 0:
|
if len(cluster_turbines) == 0:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# --- 簇内 MST ---
|
|
||||||
cluster_indices = cluster_turbines.index.tolist()
|
cluster_indices = cluster_turbines.index.tolist()
|
||||||
coords = cluster_turbines[['x', 'y']].values
|
coords = cluster_turbines[['x', 'y']].values
|
||||||
|
|
||||||
@@ -387,18 +370,141 @@ def design_with_capacitated_sweep(turbines, substation, cable_specs=None):
|
|||||||
target = f'turbine_{cluster_indices[j]}'
|
target = f'turbine_{cluster_indices[j]}'
|
||||||
cluster_connections.append((source, target, mst[i, j]))
|
cluster_connections.append((source, target, mst[i, j]))
|
||||||
|
|
||||||
# --- 连接到升压站 ---
|
# 连接到升压站
|
||||||
# 找到簇内离升压站最近的风机
|
|
||||||
dists = np.sqrt((cluster_turbines['x'] - substation_coord[0])**2 +
|
dists = np.sqrt((cluster_turbines['x'] - substation_coord[0])**2 +
|
||||||
(cluster_turbines['y'] - substation_coord[1])**2)
|
(cluster_turbines['y'] - substation_coord[1])**2)
|
||||||
closest_id = dists.idxmin() # 返回的是原始DataFrame的index
|
closest_id = dists.idxmin()
|
||||||
|
|
||||||
# 添加升压站连接
|
|
||||||
min_dist = dists.min()
|
min_dist = dists.min()
|
||||||
substation_connections.append((f'turbine_{closest_id}', 'substation', min_dist))
|
substation_connections.append((f'turbine_{closest_id}', 'substation', min_dist))
|
||||||
|
|
||||||
return cluster_connections + substation_connections, turbines
|
return cluster_connections + substation_connections, turbines
|
||||||
|
|
||||||
|
# 3.6 旋转扫描算法 (Rotational Sweep) - 优化版
|
||||||
|
def design_with_rotational_sweep(turbines, substation, cable_specs=None):
|
||||||
|
"""
|
||||||
|
使用带容量约束的扇区扫描算法设计集电线路 (优化版:旋转扫描)
|
||||||
|
原理:
|
||||||
|
1. 计算所有风机相对于升压站的角度并排序。
|
||||||
|
2. 遍历所有可能的起始角度(即尝试以每一台风机作为扫描的起点)。
|
||||||
|
3. 对每种起始角度,贪婪地将风机加入回路直到满载。
|
||||||
|
4. 对每种分组方案计算MST成本,选出总成本最低的方案。
|
||||||
|
"""
|
||||||
|
# 1. 获取电缆最大容量
|
||||||
|
max_mw = get_max_cable_capacity_mw(cable_specs)
|
||||||
|
# print(f"DEBUG: 扇区扫描算法启动 - 单回路容量限制: {max_mw:.2f} MW")
|
||||||
|
|
||||||
|
substation_coord = substation[0]
|
||||||
|
|
||||||
|
# 2. 计算角度 (使用 arctan2 返回 -pi 到 pi)
|
||||||
|
work_df = turbines.copy()
|
||||||
|
dx = work_df['x'] - substation_coord[0]
|
||||||
|
dy = work_df['y'] - substation_coord[1]
|
||||||
|
work_df['angle'] = np.arctan2(dy, dx)
|
||||||
|
|
||||||
|
# 按角度排序
|
||||||
|
work_df = work_df.sort_values('angle').reset_index(drop=True)
|
||||||
|
|
||||||
|
n_turbines = len(work_df)
|
||||||
|
best_cost = float('inf')
|
||||||
|
best_connections = []
|
||||||
|
best_turbines_state = None
|
||||||
|
best_start_idx = -1
|
||||||
|
|
||||||
|
# 遍历所有可能的起始点
|
||||||
|
for start_idx in range(n_turbines):
|
||||||
|
|
||||||
|
# 构建当前旋转顺序的风机列表
|
||||||
|
if start_idx == 0:
|
||||||
|
current_df = work_df.copy()
|
||||||
|
else:
|
||||||
|
current_df = pd.concat([work_df.iloc[start_idx:], work_df.iloc[:start_idx]]).reset_index(drop=True)
|
||||||
|
|
||||||
|
# --- 贪婪分组 ---
|
||||||
|
current_df['cluster'] = -1
|
||||||
|
cluster_id = 0
|
||||||
|
current_power = 0
|
||||||
|
current_indices_in_df = []
|
||||||
|
|
||||||
|
powers = current_df['power'].values
|
||||||
|
|
||||||
|
for i in range(n_turbines):
|
||||||
|
p = powers[i]
|
||||||
|
|
||||||
|
if len(current_indices_in_df) > 0 and (current_power + p > max_mw):
|
||||||
|
current_df.loc[current_indices_in_df, 'cluster'] = cluster_id
|
||||||
|
cluster_id += 1
|
||||||
|
current_power = 0
|
||||||
|
current_indices_in_df = []
|
||||||
|
|
||||||
|
current_indices_in_df.append(i)
|
||||||
|
current_power += p
|
||||||
|
|
||||||
|
if current_indices_in_df:
|
||||||
|
current_df.loc[current_indices_in_df, 'cluster'] = cluster_id
|
||||||
|
cluster_id += 1
|
||||||
|
|
||||||
|
# --- 计算该分组方案的成本 ---
|
||||||
|
current_total_length = 0
|
||||||
|
|
||||||
|
n_clusters = cluster_id
|
||||||
|
for cid in range(n_clusters):
|
||||||
|
cluster_rows = current_df[current_df['cluster'] == cid]
|
||||||
|
if len(cluster_rows) == 0: continue
|
||||||
|
|
||||||
|
# 1. 簇内 MST 长度
|
||||||
|
coords = cluster_rows[['x', 'y']].values
|
||||||
|
if len(cluster_rows) > 1:
|
||||||
|
dm = distance_matrix(coords, coords)
|
||||||
|
mst = minimum_spanning_tree(dm).toarray()
|
||||||
|
mst_len = mst.sum()
|
||||||
|
current_total_length += mst_len
|
||||||
|
|
||||||
|
# 2. 连接升压站长度
|
||||||
|
dists = np.sqrt((cluster_rows['x'] - substation_coord[0])**2 +
|
||||||
|
(cluster_rows['y'] - substation_coord[1])**2)
|
||||||
|
min_dist = dists.min()
|
||||||
|
current_total_length += min_dist
|
||||||
|
|
||||||
|
# --- 比较并保存最佳结果 ---
|
||||||
|
if current_total_length < best_cost:
|
||||||
|
best_cost = current_total_length
|
||||||
|
best_start_idx = start_idx
|
||||||
|
best_id_to_cluster = dict(zip(current_df['id'], current_df['cluster']))
|
||||||
|
|
||||||
|
# --- 根据最佳方案重新生成详细连接 ---
|
||||||
|
turbines['cluster'] = turbines['id'].map(best_id_to_cluster)
|
||||||
|
|
||||||
|
final_connections = []
|
||||||
|
|
||||||
|
unique_clusters = turbines['cluster'].unique()
|
||||||
|
unique_clusters = [c for c in unique_clusters if not pd.isna(c) and c >= 0]
|
||||||
|
|
||||||
|
for cid in unique_clusters:
|
||||||
|
cluster_turbines = turbines[turbines['cluster'] == cid]
|
||||||
|
if len(cluster_turbines) == 0: continue
|
||||||
|
|
||||||
|
cluster_indices = cluster_turbines.index.tolist()
|
||||||
|
coords = cluster_turbines[['x', 'y']].values
|
||||||
|
|
||||||
|
if len(cluster_indices) > 1:
|
||||||
|
dist_matrix_local = distance_matrix(coords, coords)
|
||||||
|
mst = minimum_spanning_tree(dist_matrix_local).toarray()
|
||||||
|
|
||||||
|
for i in range(len(cluster_indices)):
|
||||||
|
for j in range(len(cluster_indices)):
|
||||||
|
if mst[i, j] > 0:
|
||||||
|
source = f'turbine_{cluster_indices[i]}'
|
||||||
|
target = f'turbine_{cluster_indices[j]}'
|
||||||
|
final_connections.append((source, target, mst[i, j]))
|
||||||
|
|
||||||
|
dists = np.sqrt((cluster_turbines['x'] - substation_coord[0])**2 +
|
||||||
|
(cluster_turbines['y'] - substation_coord[1])**2)
|
||||||
|
closest_idx_in_df = dists.idxmin()
|
||||||
|
min_dist = dists.min()
|
||||||
|
final_connections.append((f'turbine_{closest_idx_in_df}', 'substation', min_dist))
|
||||||
|
|
||||||
|
return final_connections, turbines
|
||||||
|
|
||||||
def get_max_cable_capacity_mw(cable_specs=None):
|
def get_max_cable_capacity_mw(cable_specs=None):
|
||||||
"""
|
"""
|
||||||
计算给定电缆规格中能够承载的最大功率 (单位: MW)。
|
计算给定电缆规格中能够承载的最大功率 (单位: MW)。
|
||||||
@@ -962,13 +1068,23 @@ def compare_design_methods(excel_path=None, n_clusters_override=None):
|
|||||||
f"MST Method (Standard Cables)\nTotal Cost: ¥{mst_evaluation['total_cost']/10000:.2f}万",
|
f"MST Method (Standard Cables)\nTotal Cost: ¥{mst_evaluation['total_cost']/10000:.2f}万",
|
||||||
ax=axes[0])
|
ax=axes[0])
|
||||||
|
|
||||||
print(f"\n===== 开始比较电缆方案 (基于 Capacitated Sweep) =====")
|
print(f"\n===== 开始比较电缆方案 =====")
|
||||||
|
|
||||||
best_cost = float('inf')
|
best_cost = float('inf')
|
||||||
best_result = None
|
best_result = None
|
||||||
|
|
||||||
comparison_results = []
|
comparison_results = []
|
||||||
|
|
||||||
|
# 将 MST 结果也加入对比列表,方便查看
|
||||||
|
comparison_results.append({
|
||||||
|
'name': 'MST Method',
|
||||||
|
'cost': mst_evaluation['total_cost'],
|
||||||
|
'loss': mst_evaluation['total_loss'],
|
||||||
|
'eval': mst_evaluation,
|
||||||
|
'turbines': turbines.copy(), # MST 不改变 turbines,但为了统一格式
|
||||||
|
'specs': specs_1
|
||||||
|
})
|
||||||
|
|
||||||
for i, (name, current_specs) in enumerate(scenarios):
|
for i, (name, current_specs) in enumerate(scenarios):
|
||||||
print(f"\n--- {name} ---")
|
print(f"\n--- {name} ---")
|
||||||
if not current_specs:
|
if not current_specs:
|
||||||
@@ -978,71 +1094,81 @@ def compare_design_methods(excel_path=None, n_clusters_override=None):
|
|||||||
# 计算参数
|
# 计算参数
|
||||||
total_power = turbines['power'].sum()
|
total_power = turbines['power'].sum()
|
||||||
max_cable_mw = get_max_cable_capacity_mw(cable_specs=current_specs)
|
max_cable_mw = get_max_cable_capacity_mw(cable_specs=current_specs)
|
||||||
n_cable_types = len(current_specs)
|
|
||||||
|
|
||||||
# 确定簇数
|
# 确定簇数 (针对 Base 算法)
|
||||||
if n_clusters_override is not None:
|
if n_clusters_override is not None:
|
||||||
n_clusters = n_clusters_override
|
n_clusters = n_clusters_override
|
||||||
min_needed = int(np.ceil(total_power / max_cable_mw))
|
min_needed = int(np.ceil(total_power / max_cable_mw))
|
||||||
if n_clusters < min_needed:
|
if n_clusters < min_needed:
|
||||||
print(f" Warning: 指定簇数 {n_clusters} 小于理论最小需求 {min_needed}。设计可能会失败或严重过载。")
|
print(f" Warning: 指定簇数 {n_clusters} 小于理论最小需求 {min_needed}。")
|
||||||
else:
|
else:
|
||||||
# 自动计算:取 (理论最小需求) 和 (基于电缆型号分级估算) 的较大值
|
|
||||||
min_needed = int(np.ceil(total_power / max_cable_mw))
|
min_needed = int(np.ceil(total_power / max_cable_mw))
|
||||||
# 这里的逻辑是:如果电缆级差很密,可能不需要那么多回路;如果电缆很大,回路可以少。
|
n_cable_types = len(current_specs)
|
||||||
# 原有的逻辑是 len(turbines)/n_cable_types,这只是一个经验值。
|
|
||||||
# 我们主要保证满足容量:
|
|
||||||
n_clusters = min_needed
|
|
||||||
# 稍微增加一点裕度,避免因为离散分布导致最后一点功率塞不进
|
|
||||||
# 或者保持原有的启发式逻辑,取最大值
|
|
||||||
heuristic = int(np.ceil(len(turbines) / n_cable_types))
|
heuristic = int(np.ceil(len(turbines) / n_cable_types))
|
||||||
n_clusters = max(min_needed, heuristic)
|
n_clusters = max(min_needed, heuristic)
|
||||||
if n_clusters > len(turbines): n_clusters = len(turbines)
|
if n_clusters > len(turbines): n_clusters = len(turbines)
|
||||||
|
|
||||||
print(f" 最大电缆容量: {max_cable_mw:.2f} MW")
|
print(f" 最大电缆容量: {max_cable_mw:.2f} MW")
|
||||||
print(f" 设计回路数: {n_clusters}")
|
|
||||||
|
|
||||||
# 运行设计
|
# --- Run 1: Base Algorithm (Capacitated Sweep) ---
|
||||||
conns, clustered_turbines = design_with_capacitated_sweep(
|
base_name = f"{name} (Base)"
|
||||||
|
conns_base, turbines_base = design_with_capacitated_sweep(
|
||||||
turbines.copy(), substation, cable_specs=current_specs
|
turbines.copy(), substation, cable_specs=current_specs
|
||||||
)
|
)
|
||||||
|
eval_base = evaluate_design(
|
||||||
# 评估
|
turbines, conns_base, substation, cable_specs=current_specs,
|
||||||
evaluation = evaluate_design(
|
is_offshore=is_offshore, method_name=base_name
|
||||||
turbines, conns, substation, cable_specs=current_specs,
|
|
||||||
is_offshore=is_offshore, method_name=name
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# 记录结果
|
|
||||||
comparison_results.append({
|
comparison_results.append({
|
||||||
'name': name,
|
'name': base_name,
|
||||||
'cost': evaluation['total_cost'],
|
'cost': eval_base['total_cost'],
|
||||||
'loss': evaluation['total_loss'],
|
'loss': eval_base['total_loss'],
|
||||||
'eval': evaluation,
|
'eval': eval_base,
|
||||||
'turbines': clustered_turbines,
|
'turbines': turbines_base,
|
||||||
'specs': current_specs
|
'specs': current_specs
|
||||||
})
|
})
|
||||||
|
print(f" [Base] Cost: ¥{eval_base['total_cost']:,.2f} | Loss: {eval_base['total_loss']:.2f} kW")
|
||||||
|
|
||||||
# 输出简报
|
# --- Run 2: Rotational Algorithm (Optimization) ---
|
||||||
print(f" 总成本: ¥{evaluation['total_cost']:,.2f}")
|
rot_name = f"{name} (Rotational)"
|
||||||
print(f" 总损耗: {evaluation['total_loss']:.2f} kW")
|
conns_rot, turbines_rot = design_with_rotational_sweep(
|
||||||
|
turbines.copy(), substation, cable_specs=current_specs
|
||||||
|
)
|
||||||
|
eval_rot = evaluate_design(
|
||||||
|
turbines, conns_rot, substation, cable_specs=current_specs,
|
||||||
|
is_offshore=is_offshore, method_name=rot_name
|
||||||
|
)
|
||||||
|
|
||||||
|
comparison_results.append({
|
||||||
|
'name': rot_name,
|
||||||
|
'cost': eval_rot['total_cost'],
|
||||||
|
'loss': eval_rot['total_loss'],
|
||||||
|
'eval': eval_rot,
|
||||||
|
'turbines': turbines_rot,
|
||||||
|
'specs': current_specs
|
||||||
|
})
|
||||||
|
print(f" [Rotational] Cost: ¥{eval_rot['total_cost']:,.2f} | Loss: {eval_rot['total_loss']:.2f} kW")
|
||||||
|
|
||||||
# 记录最佳
|
# 记录最佳
|
||||||
if evaluation['total_cost'] < best_cost:
|
if eval_rot['total_cost'] < best_cost:
|
||||||
best_cost = evaluation['total_cost']
|
best_cost = eval_rot['total_cost']
|
||||||
best_result = comparison_results[-1]
|
# best_result 不再需要单独维护,最后遍历 comparison_results 即可
|
||||||
|
|
||||||
# 可视化 (axes 1, 2, 3)
|
if eval_base['total_cost'] < best_cost:
|
||||||
|
best_cost = eval_base['total_cost']
|
||||||
|
|
||||||
|
# 可视化 (只画 Base 版本)
|
||||||
ax_idx = i + 1
|
ax_idx = i + 1
|
||||||
if ax_idx < 4:
|
if ax_idx < 4:
|
||||||
n_actual_clusters = clustered_turbines['cluster'].nunique()
|
n_circuits = turbines_base['cluster'].nunique()
|
||||||
title = f"{name} ({n_actual_clusters} circuits)\nCost: ¥{evaluation['total_cost']/10000:.2f}万 | Loss: {evaluation['total_loss']:.2f} kW"
|
title = f"{base_name} ({n_circuits} circuits)\nCost: ¥{eval_base['total_cost']/10000:.2f}万"
|
||||||
visualize_design(clustered_turbines, substation, evaluation['details'], title, ax=axes[ax_idx])
|
visualize_design(turbines_base, substation, eval_base['details'], title, ax=axes[ax_idx])
|
||||||
|
|
||||||
plt.tight_layout()
|
plt.tight_layout()
|
||||||
output_filename = 'wind_farm_design_comparison.png'
|
output_filename = 'wind_farm_design_comparison.png'
|
||||||
plt.savefig(output_filename, dpi=300)
|
plt.savefig(output_filename, dpi=300)
|
||||||
print(f"\n比较图已保存至: {output_filename}")
|
print(f"\n比较图(Base版)已保存至: {output_filename}")
|
||||||
|
|
||||||
# 准备文件路径
|
# 准备文件路径
|
||||||
if excel_path:
|
if excel_path:
|
||||||
|
|||||||
Reference in New Issue
Block a user