fix: 修复打包后exe程序在无控制台模式下运行时的uvicorn日志配置错误
通过检测sys.frozen判断运行环境,只在打包后的exe程序中禁用日志配置, 避免AttributeError: 'NoneType' object has no attribute 'isatty'错误。 普通Python运行环境保留完整日志功能,方便调试。
This commit is contained in:
142
README.md
142
README.md
@@ -1,116 +1,78 @@
|
|||||||
# 海上风电场集电系统设计工具
|
# 海上风电场集电系统设计优化工具 (Wind Farm Collector System Optimizer)
|
||||||
|
|
||||||
一个用于设计和优化海上风电场集电系统的Python工具,支持多种布局算法和电缆优化方案。
|
一个用于设计和优化海上风电场集电系统拓扑的综合工具。支持多种先进算法,能够根据风机坐标、功率以及海缆规格,自动生成投资成本最低、损耗最小的设计方案。
|
||||||
|
|
||||||
## 功能特性
|
## 🌟 主要功能
|
||||||
|
|
||||||
- 🌊 多种风机布局生成(随机分布、规则网格)
|
- 🖥️ **交互式 Web 界面**:基于 NiceGUI 开发,支持文件上传、实时日志、方案对比和可视化。
|
||||||
- 🔌 多种集电系统设计算法:
|
- 🌊 **多种布局生成**:内置模拟数据生成器,支持规则网格和随机分布布局。
|
||||||
- 最小生成树(MST)算法
|
- 🔌 **先进设计算法**:
|
||||||
- K-means聚类算法
|
- **MST (Minimum Spanning Tree)**:无容量约束基准方案。
|
||||||
- 容量扫描算法(Capacitated Sweep)
|
- **Capacitated Sweep (Base)**:基础扇区扫描分组。
|
||||||
- 旋转优化算法(Rotational Sweep)
|
- **Rotational Sweep**:全局最优起始角度旋转扫描优化。
|
||||||
- 📊 多方案对比分析和可视化
|
- **Esau-Williams**:经典启发式算法,在距离与容量间寻找最优平衡。
|
||||||
- 📋 自动导出DXF图纸和Excel报告
|
- 📊 **智能方案对比**:自动运行三大场景(标准方案、含可选电缆方案、限制最大截面方案)并对比结果。
|
||||||
- 🔧 智能电缆规格选择和成本优化
|
- 📋 **数据校验与保障**:严格校验输入数据的有序性及电缆配置规则。
|
||||||
|
- 📁 **多格式导出**:
|
||||||
|
- 自动生成 CAD 图纸 (`.dxf`),按电缆规格分层并着色。
|
||||||
|
- 导出详细的 Excel 对比报告及单方案电缆清册。
|
||||||
|
- 支持一键打包导出所有方案压缩包 (`.zip`)。
|
||||||
|
|
||||||
## 安装依赖
|
## 🛠️ 安装依赖
|
||||||
|
|
||||||
|
本项目使用 `uv` 或 `pip` 管理环境。推荐安装依赖:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install numpy pandas matplotlib scikit-learn scipy networkx
|
pip install numpy pandas matplotlib scikit-learn scipy networkx ezdxf nicegui openpyxl
|
||||||
```
|
```
|
||||||
|
|
||||||
## 使用方法
|
## 🚀 使用方法
|
||||||
|
|
||||||
### 基本用法
|
### 1. 启动图形化界面 (推荐)
|
||||||
|
|
||||||
|
运行以下命令启动 Web 界面,程序将自动在浏览器中打开:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python main.py
|
python gui.py
|
||||||
```
|
```
|
||||||
|
*注:程序默认监听 8080 端口,若被占用将自动尝试后续可用端口。*
|
||||||
|
|
||||||
### 指定数据文件
|
### 2. 命令行模式
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python main.py --excel wind_farm_coordinates.xlsx
|
python main.py --excel your_data.xlsx
|
||||||
```
|
```
|
||||||
|
|
||||||
### 覆盖默认簇数
|
## 📝 输入数据规范 (Excel)
|
||||||
|
|
||||||
```bash
|
为了确保计算结果的准确性,输入 Excel 文件应包含以下两个 Sheet:
|
||||||
python main.py --clusters 20
|
|
||||||
```
|
|
||||||
|
|
||||||
## 算法说明
|
### 1. Coordinates (坐标)
|
||||||
|
| Type | ID | X | Y | Power | PlatformHeight |
|
||||||
|
|------|----|---|---|-------|----------------|
|
||||||
|
| Substation | Sub1 | 4000 | -800 | 0 | 0 |
|
||||||
|
| Turbine | 1 | 0 | 0 | 8.0 | 25 |
|
||||||
|
| ... | ... | ... | ... | ... | ... |
|
||||||
|
|
||||||
### 1. MST Method(最小生成树)
|
### 2. Cables (电缆)
|
||||||
- 使用最小生成树连接所有风机到海上变电站
|
**必须遵守以下规则:**
|
||||||
- 简单高效,适合初步设计
|
- **单调递增性**:电缆必须按截面从小到大排列,且对应的额定载流量也必须严格递增。
|
||||||
|
- **可选电缆规则**:
|
||||||
|
- `Optional` 列标记为 'Y' 的电缆最多只能有一条。
|
||||||
|
- 若存在可选电缆,它必须是列表中截面最大的一条。
|
||||||
|
|
||||||
### 2. K-means Clustering
|
## 📈 场景说明 (Scenarios)
|
||||||
- 将风机分组到多个回路中
|
|
||||||
- 平衡每回路的功率分配
|
|
||||||
|
|
||||||
### 3. Capacitated Sweep(容量扫描)
|
1. **Scenario 1 (Standard)**:仅使用非可选(标准)电缆进行优化。
|
||||||
- 考虑电缆容量约束的智能分组
|
2. **Scenario 2 (With Optional)**:包含标记为 'Y' 的大型电缆,适用于尝试增加单回路容量的场景。
|
||||||
- 支持多种电缆规格自动选择
|
3. **Scenario 3 (No Max)**:排除最大截面电缆,测试在电缆供应受限时的最优拓扑。
|
||||||
|
|
||||||
### 4. Rotational Sweep(旋转优化)
|
## 📂 输出文件说明
|
||||||
- 在容量扫描基础上进行旋转优化
|
|
||||||
- 进一步降低总成本和损耗
|
|
||||||
|
|
||||||
## 输出文件
|
- **Excel 报告**:`[文件名]_result.xlsx` 包含所有方案的总览及详细连接清单。
|
||||||
|
- **CAD 图纸**:`design_[方案名].dxf` 包含分层分色的拓扑图。
|
||||||
|
- **全部方案**:`[文件名]_result.zip` 包含所有图纸及 Excel 报告。
|
||||||
|
|
||||||
1. **可视化图片**:`wind_farm_design_comparison.png`
|
## ⚖️ 许可证
|
||||||
- 不同算法的设计方案对比图
|
|
||||||
|
|
||||||
2. **CAD图纸**:`wind_farm_design.dxf`
|
本项目仅供工程学习、研究和初步设计评估使用。详细计算应以专业设计院规范为准。
|
||||||
- 可导入CAD软件的详细设计图纸
|
|
||||||
|
|
||||||
3. **数据报告**:`wind_farm_design.xlsx`
|
|
||||||
- 包含所有方案的详细技术参数和成本分析
|
|
||||||
|
|
||||||
## 关键参数说明
|
|
||||||
|
|
||||||
可以在 `main.py` 中调整以下核心常量以适配不同项目:
|
|
||||||
|
|
||||||
```python
|
|
||||||
VOLTAGE_LEVEL = 66000 # 集电系统电压 (V)
|
|
||||||
POWER_FACTOR = 0.95 # 功率因数
|
|
||||||
cost_multiplier = 5.0 # 海缆相对于陆缆的成本倍数
|
|
||||||
```
|
|
||||||
|
|
||||||
## 电缆规格配置
|
|
||||||
|
|
||||||
项目支持多种电缆规格,可在 `generate_template.py` 中配置:
|
|
||||||
|
|
||||||
| 截面积(mm²) | 容量(MW) | 电阻(Ω/km) | 成本(元/m) |
|
|
||||||
|-------------|----------|------------|------------|
|
|
||||||
| 35 | 150 | 0.524 | 80 |
|
|
||||||
| 70 | 215 | 0.268 | 120 |
|
|
||||||
| 95 | 260 | 0.193 | 150 |
|
|
||||||
| 120 | 295 | 0.153 | 180 |
|
|
||||||
| 150 | 330 | 0.124 | 220 |
|
|
||||||
| 185 | 370 | 0.0991 | 270 |
|
|
||||||
| 240 | 425 | 0.0754 | 350 |
|
|
||||||
| 300 | 500 | 0.0601 | 450 |
|
|
||||||
| 400 | 580 | 0.0470 | 600 |
|
|
||||||
|
|
||||||
## 输出示例
|
|
||||||
|
|
||||||
```text
|
|
||||||
===== 开始比较电缆方案 =====
|
|
||||||
|
|
||||||
--- All Cables (Base) ---
|
|
||||||
[Base] Cost: ¥12,456,789.12 | Loss: 234.56 kW
|
|
||||||
[Rotational] Cost: ¥12,234,567.89 | Loss: 223.45 kW
|
|
||||||
|
|
||||||
--- High Current (Base) ---
|
|
||||||
[Base] Cost: ¥11,987,654.32 | Loss: 245.67 kW
|
|
||||||
[Rotational] Cost: ¥11,876,543.21 | Loss: 234.56 kW
|
|
||||||
|
|
||||||
推荐方案: High Current (Rotational) (默认)
|
|
||||||
```
|
|
||||||
|
|
||||||
## 许可证
|
|
||||||
|
|
||||||
本项目仅供学习和研究使用。
|
|
||||||
@@ -50,12 +50,20 @@ def create_template():
|
|||||||
]
|
]
|
||||||
df_cables = pd.DataFrame(cable_data)
|
df_cables = pd.DataFrame(cable_data)
|
||||||
|
|
||||||
|
# Create System Parameters data
|
||||||
|
param_data = [
|
||||||
|
{'Parameter': 'Voltage (kV) / 电压 (kV)', 'Value': 66},
|
||||||
|
{'Parameter': 'Power Factor / 功率因数', 'Value': 0.95}
|
||||||
|
]
|
||||||
|
df_params = pd.DataFrame(param_data)
|
||||||
|
|
||||||
# Save to Excel
|
# Save to Excel
|
||||||
output_file = 'coordinates.xlsx'
|
output_file = 'windfarm_template.xlsx'
|
||||||
with pd.ExcelWriter(output_file) as writer:
|
with pd.ExcelWriter(output_file) as writer:
|
||||||
df.to_excel(writer, sheet_name='Coordinates', index=False)
|
df.to_excel(writer, sheet_name='Coordinates', index=False)
|
||||||
df_cables.to_excel(writer, sheet_name='Cables', index=False)
|
df_cables.to_excel(writer, sheet_name='Cables', index=False)
|
||||||
print(f"Created sample file: {output_file} with sheets 'Coordinates' and 'Cables'")
|
df_params.to_excel(writer, sheet_name='Parameters', index=False)
|
||||||
|
print(f"Created sample file: {output_file} with sheets 'Coordinates', 'Cables', and 'Parameters'")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
create_template()
|
create_template()
|
||||||
96
gui.py
96
gui.py
@@ -38,6 +38,8 @@ state = {
|
|||||||
"results": [],
|
"results": [],
|
||||||
"substation": None,
|
"substation": None,
|
||||||
"turbines": None,
|
"turbines": None,
|
||||||
|
"cable_specs": None,
|
||||||
|
"system_params": None,
|
||||||
"temp_dir": os.path.join(tempfile.gettempdir(), "windfarm_gui_uploads"),
|
"temp_dir": os.path.join(tempfile.gettempdir(), "windfarm_gui_uploads"),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,8 +72,74 @@ def index():
|
|||||||
"upload_widget": None,
|
"upload_widget": None,
|
||||||
"run_btn": None,
|
"run_btn": None,
|
||||||
"current_file_label": None,
|
"current_file_label": None,
|
||||||
|
"info_container": None, # 新增信息展示容器
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def update_info_panel():
|
||||||
|
if refs["info_container"]:
|
||||||
|
refs["info_container"].clear()
|
||||||
|
with refs["info_container"]:
|
||||||
|
# System Params - Always show
|
||||||
|
with ui.row().classes("w-full items-center gap-4 mb-2"):
|
||||||
|
ui.icon('settings', color='primary').classes('text-2xl')
|
||||||
|
ui.label("系统参数").classes("text-lg font-bold")
|
||||||
|
|
||||||
|
params_text = []
|
||||||
|
|
||||||
|
# 获取电压
|
||||||
|
v = 66000 # Default
|
||||||
|
is_default_v = True
|
||||||
|
if state.get("system_params") and 'voltage' in state['system_params']:
|
||||||
|
v = state['system_params']['voltage']
|
||||||
|
is_default_v = False
|
||||||
|
|
||||||
|
v_str = f"电压: {v/1000:.1f} kV" if v >= 1000 else f"电压: {v} V"
|
||||||
|
if is_default_v:
|
||||||
|
v_str += " (默认)"
|
||||||
|
params_text.append(v_str)
|
||||||
|
|
||||||
|
# 获取功率因数
|
||||||
|
pf = 0.95 # Default
|
||||||
|
is_default_pf = True
|
||||||
|
if state.get("system_params") and 'power_factor' in state['system_params']:
|
||||||
|
pf = state['system_params']['power_factor']
|
||||||
|
is_default_pf = False
|
||||||
|
|
||||||
|
pf_str = f"功率因数: {pf}"
|
||||||
|
if is_default_pf:
|
||||||
|
pf_str += " (默认)"
|
||||||
|
params_text.append(pf_str)
|
||||||
|
|
||||||
|
for p in params_text:
|
||||||
|
ui.chip(p, icon='bolt').props('outline color=primary')
|
||||||
|
|
||||||
|
ui.separator().classes('my-2')
|
||||||
|
|
||||||
|
# Cables
|
||||||
|
if state.get("cable_specs"):
|
||||||
|
with ui.row().classes("w-full items-center gap-2 mb-2"):
|
||||||
|
ui.icon('cable', color='secondary').classes('text-2xl')
|
||||||
|
ui.label("电缆规格参数").classes("text-lg font-bold")
|
||||||
|
|
||||||
|
columns = [
|
||||||
|
{'name': 'section', 'label': '截面 (mm²)', 'field': 'section', 'align': 'center'},
|
||||||
|
{'name': 'capacity', 'label': '载流量 (A)', 'field': 'capacity', 'align': 'center'},
|
||||||
|
{'name': 'resistance', 'label': '电阻 (Ω/km)', 'field': 'resistance', 'align': 'center'},
|
||||||
|
{'name': 'cost', 'label': '参考单价 (元/m)', 'field': 'cost', 'align': 'center'},
|
||||||
|
]
|
||||||
|
rows = []
|
||||||
|
for spec in state["cable_specs"]:
|
||||||
|
# spec is (section, capacity, resistance, cost, is_optional)
|
||||||
|
rows.append({
|
||||||
|
'section': spec[0],
|
||||||
|
'capacity': spec[1],
|
||||||
|
'resistance': spec[2],
|
||||||
|
'cost': spec[3]
|
||||||
|
})
|
||||||
|
ui.table(columns=columns, rows=rows).classes('w-full').props('dense flat bordered')
|
||||||
|
else:
|
||||||
|
ui.label("未检测到电缆数据,将使用默认参数。").classes("text-gray-500 italic")
|
||||||
|
|
||||||
async def handle_upload(e: events.UploadEventArguments):
|
async def handle_upload(e: events.UploadEventArguments):
|
||||||
try:
|
try:
|
||||||
filename = None
|
filename = None
|
||||||
@@ -125,7 +193,16 @@ def index():
|
|||||||
refs["current_file_label"].text = f"当前文件: {filename}"
|
refs["current_file_label"].text = f"当前文件: {filename}"
|
||||||
|
|
||||||
# 加载数据
|
# 加载数据
|
||||||
state["turbines"], state["substation"], _ = load_data_from_excel(path)
|
try:
|
||||||
|
# 尝试解包 4 个返回值 (新版 main.py)
|
||||||
|
state["turbines"], state["substation"], state["cable_specs"], state["system_params"] = load_data_from_excel(path)
|
||||||
|
except ValueError:
|
||||||
|
# 兼容旧版 (如果是 3 个返回值)
|
||||||
|
state["turbines"], state["substation"], state["cable_specs"] = load_data_from_excel(path)
|
||||||
|
state["system_params"] = {}
|
||||||
|
|
||||||
|
update_info_panel()
|
||||||
|
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
ui.notify(f"上传处理失败: {ex}", type="negative")
|
ui.notify(f"上传处理失败: {ex}", type="negative")
|
||||||
|
|
||||||
@@ -546,6 +623,11 @@ def index():
|
|||||||
)
|
)
|
||||||
|
|
||||||
with ui.column().classes("w-3/4 gap-4"):
|
with ui.column().classes("w-3/4 gap-4"):
|
||||||
|
# 新增:信息展示卡片
|
||||||
|
with ui.card().classes("w-full p-4 shadow-md").style("max-height: 400px; overflow-y: auto;"):
|
||||||
|
refs["info_container"] = ui.column().classes("w-full")
|
||||||
|
ui.label("请上传 Excel 文件以查看系统参数和电缆规格...").classes("text-gray-500 italic")
|
||||||
|
|
||||||
with ui.card().classes("w-full p-4 shadow-md"):
|
with ui.card().classes("w-full p-4 shadow-md"):
|
||||||
ui.label("方案对比结果 (点击行查看拓扑详情)").classes(
|
ui.label("方案对比结果 (点击行查看拓扑详情)").classes(
|
||||||
"text-xl font-semibold mb-2"
|
"text-xl font-semibold mb-2"
|
||||||
@@ -610,4 +692,14 @@ def find_available_port(start_port=8080, max_attempts=10):
|
|||||||
|
|
||||||
# 自动寻找可用端口,避免端口冲突
|
# 自动寻找可用端口,避免端口冲突
|
||||||
target_port = find_available_port(8080)
|
target_port = find_available_port(8080)
|
||||||
ui.run(title="海上风电场集电线路优化", port=target_port)
|
|
||||||
|
# 检测是否为打包后的exe程序
|
||||||
|
import sys
|
||||||
|
if getattr(sys, 'frozen', False):
|
||||||
|
# 打包环境下禁用日志配置,避免在无控制台模式下出现错误
|
||||||
|
import logging
|
||||||
|
logging.disable(logging.CRITICAL)
|
||||||
|
ui.run(title="海上风电场集电线路优化", port=target_port, log_level=None)
|
||||||
|
else:
|
||||||
|
# 普通使用环境保留日志功能
|
||||||
|
ui.run(title="海上风电场集电线路优化", port=target_port)
|
||||||
|
|||||||
75
main.py
75
main.py
@@ -199,7 +199,39 @@ def load_data_from_excel(file_path):
|
|||||||
if cable_specs:
|
if cable_specs:
|
||||||
print(f"成功加载: {len(cable_specs)} 种电缆规格")
|
print(f"成功加载: {len(cable_specs)} 种电缆规格")
|
||||||
|
|
||||||
return turbines, substation, cable_specs
|
# 读取参数数据 (如果存在)
|
||||||
|
system_params = {}
|
||||||
|
param_sheet_name = None
|
||||||
|
if 'Parameters' in xl.sheet_names:
|
||||||
|
param_sheet_name = 'Parameters'
|
||||||
|
elif '参数' in xl.sheet_names:
|
||||||
|
param_sheet_name = '参数'
|
||||||
|
|
||||||
|
if param_sheet_name:
|
||||||
|
try:
|
||||||
|
params_df = pd.read_excel(xl, param_sheet_name)
|
||||||
|
# 假设格式为两列:Parameter (参数名), Value (值)
|
||||||
|
if len(params_df.columns) >= 2:
|
||||||
|
for _, row in params_df.iterrows():
|
||||||
|
key = str(row[0]).strip().lower()
|
||||||
|
try:
|
||||||
|
val = float(row[1])
|
||||||
|
if 'voltage' in key or '电压' in key:
|
||||||
|
# 检测是否为kV单位
|
||||||
|
if 'kv' in key:
|
||||||
|
system_params['voltage'] = val * 1000
|
||||||
|
else:
|
||||||
|
system_params['voltage'] = val
|
||||||
|
elif 'factor' in key or '功率因数' in key:
|
||||||
|
system_params['power_factor'] = val
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
if system_params:
|
||||||
|
print(f"成功加载系统参数: {system_params}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"读取参数Sheet失败: {e}")
|
||||||
|
|
||||||
|
return turbines, substation, cable_specs, system_params
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"读取Excel文件失败: {str(e)}")
|
print(f"读取Excel文件失败: {str(e)}")
|
||||||
@@ -315,7 +347,7 @@ def design_with_kmeans(turbines, substation, n_clusters=3):
|
|||||||
return cluster_connections + substation_connections, turbines
|
return cluster_connections + substation_connections, turbines
|
||||||
|
|
||||||
# 3.5 带容量约束的扇区扫描算法 (Capacitated 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, voltage=VOLTAGE_LEVEL, power_factor=POWER_FACTOR):
|
||||||
"""
|
"""
|
||||||
使用带容量约束的扇区扫描算法设计集电线路 (基础版:单次扫描)
|
使用带容量约束的扇区扫描算法设计集电线路 (基础版:单次扫描)
|
||||||
原理:
|
原理:
|
||||||
@@ -325,10 +357,9 @@ def design_with_capacitated_sweep(turbines, substation, cable_specs=None):
|
|||||||
4. 满载后开启新回路。
|
4. 满载后开启新回路。
|
||||||
"""
|
"""
|
||||||
# 1. 获取电缆最大容量
|
# 1. 获取电缆最大容量
|
||||||
max_mw = get_max_cable_capacity_mw(cable_specs)
|
max_mw = get_max_cable_capacity_mw(cable_specs, voltage=voltage, power_factor=power_factor)
|
||||||
|
|
||||||
substation_coord = substation[0]
|
substation_coord = substation[0]
|
||||||
|
|
||||||
# 2. 计算角度 (使用 arctan2 返回 -pi 到 pi)
|
# 2. 计算角度 (使用 arctan2 返回 -pi 到 pi)
|
||||||
work_df = turbines.copy()
|
work_df = turbines.copy()
|
||||||
dx = work_df['x'] - substation_coord[0]
|
dx = work_df['x'] - substation_coord[0]
|
||||||
@@ -417,7 +448,7 @@ def design_with_capacitated_sweep(turbines, substation, cable_specs=None):
|
|||||||
return cluster_connections + substation_connections, turbines
|
return cluster_connections + substation_connections, turbines
|
||||||
|
|
||||||
# 3.6 旋转扫描算法 (Rotational Sweep) - 优化版
|
# 3.6 旋转扫描算法 (Rotational Sweep) - 优化版
|
||||||
def design_with_rotational_sweep(turbines, substation, cable_specs=None):
|
def design_with_rotational_sweep(turbines, substation, cable_specs=None, voltage=VOLTAGE_LEVEL, power_factor=POWER_FACTOR):
|
||||||
"""
|
"""
|
||||||
使用带容量约束的扇区扫描算法设计集电线路 (优化版:旋转扫描)
|
使用带容量约束的扇区扫描算法设计集电线路 (优化版:旋转扫描)
|
||||||
原理:
|
原理:
|
||||||
@@ -427,7 +458,7 @@ def design_with_rotational_sweep(turbines, substation, cable_specs=None):
|
|||||||
4. 对每种分组方案计算MST成本,选出总成本最低的方案。
|
4. 对每种分组方案计算MST成本,选出总成本最低的方案。
|
||||||
"""
|
"""
|
||||||
# 1. 获取电缆最大容量
|
# 1. 获取电缆最大容量
|
||||||
max_mw = get_max_cable_capacity_mw(cable_specs)
|
max_mw = get_max_cable_capacity_mw(cable_specs, voltage=voltage, power_factor=power_factor)
|
||||||
|
|
||||||
substation_coord = substation[0]
|
substation_coord = substation[0]
|
||||||
|
|
||||||
@@ -541,7 +572,7 @@ def design_with_rotational_sweep(turbines, substation, cable_specs=None):
|
|||||||
|
|
||||||
return final_connections, turbines
|
return final_connections, turbines
|
||||||
|
|
||||||
def get_max_cable_capacity_mw(cable_specs=None):
|
def get_max_cable_capacity_mw(cable_specs=None, voltage=VOLTAGE_LEVEL, power_factor=POWER_FACTOR):
|
||||||
"""
|
"""
|
||||||
计算给定电缆规格中能够承载的最大功率 (单位: MW)。
|
计算给定电缆规格中能够承载的最大功率 (单位: MW)。
|
||||||
|
|
||||||
@@ -549,6 +580,8 @@ def get_max_cable_capacity_mw(cable_specs=None):
|
|||||||
|
|
||||||
参数:
|
参数:
|
||||||
cable_specs (list, optional): 电缆规格列表。每个元素应包含 (截面积, 额定电流, 单价, 损耗系数)。
|
cable_specs (list, optional): 电缆规格列表。每个元素应包含 (截面积, 额定电流, 单价, 损耗系数)。
|
||||||
|
voltage (float): 系统电压 (V), 默认 66000
|
||||||
|
power_factor (float): 功率因数, 默认 0.95
|
||||||
|
|
||||||
返回:
|
返回:
|
||||||
float: 最大功率承载能力 (MW)。
|
float: 最大功率承载能力 (MW)。
|
||||||
@@ -576,13 +609,13 @@ def get_max_cable_capacity_mw(cable_specs=None):
|
|||||||
# 计算最大功率:P = √3 * U * I * cosφ
|
# 计算最大功率:P = √3 * U * I * cosφ
|
||||||
# 这里假设降额系数为 1 (不降额)
|
# 这里假设降额系数为 1 (不降额)
|
||||||
max_current = max_current_capacity * 1
|
max_current = max_current_capacity * 1
|
||||||
max_power_w = np.sqrt(3) * VOLTAGE_LEVEL * max_current * POWER_FACTOR
|
max_power_w = np.sqrt(3) * voltage * max_current * power_factor
|
||||||
|
|
||||||
# 将单位从 W 转换为 MW
|
# 将单位从 W 转换为 MW
|
||||||
return max_power_w / 1e6
|
return max_power_w / 1e6
|
||||||
|
|
||||||
# 5. 计算集电线路方案成本
|
# 5. 计算集电线路方案成本
|
||||||
def evaluate_design(turbines, connections, substation, cable_specs=None, is_offshore=False, method_name="Unknown Method"):
|
def evaluate_design(turbines, connections, substation, cable_specs=None, is_offshore=False, method_name="Unknown Method", voltage=VOLTAGE_LEVEL, power_factor=POWER_FACTOR):
|
||||||
"""评估设计方案的总成本和损耗"""
|
"""评估设计方案的总成本和损耗"""
|
||||||
total_cost = 0
|
total_cost = 0
|
||||||
total_loss = 0
|
total_loss = 0
|
||||||
@@ -685,7 +718,7 @@ def evaluate_design(turbines, connections, substation, cable_specs=None, is_offs
|
|||||||
cable_specs_to_use = cable_specs
|
cable_specs_to_use = cable_specs
|
||||||
|
|
||||||
# 估算电流
|
# 估算电流
|
||||||
current = (power * 1e6) / (np.sqrt(3) * VOLTAGE_LEVEL * POWER_FACTOR)
|
current = (power * 1e6) / (np.sqrt(3) * voltage * power_factor)
|
||||||
|
|
||||||
# 选择满足载流量的最小电缆
|
# 选择满足载流量的最小电缆
|
||||||
selected_spec = None
|
selected_spec = None
|
||||||
@@ -1055,10 +1088,12 @@ def compare_design_methods(excel_path=None, n_clusters_override=None, interactiv
|
|||||||
:param plot_results: 是否生成和保存对比图表
|
:param plot_results: 是否生成和保存对比图表
|
||||||
"""
|
"""
|
||||||
cable_specs = None
|
cable_specs = None
|
||||||
|
system_params = {}
|
||||||
|
|
||||||
if excel_path:
|
if excel_path:
|
||||||
print(f"正在从 {excel_path} 读取坐标数据...")
|
print(f"正在从 {excel_path} 读取坐标数据...")
|
||||||
try:
|
try:
|
||||||
turbines, substation, cable_specs = load_data_from_excel(excel_path)
|
turbines, substation, cable_specs, system_params = load_data_from_excel(excel_path)
|
||||||
scenario_title = "Offshore Wind Farm (Imported Data)"
|
scenario_title = "Offshore Wind Farm (Imported Data)"
|
||||||
except Exception:
|
except Exception:
|
||||||
print("回退到自动生成数据模式...")
|
print("回退到自动生成数据模式...")
|
||||||
@@ -1070,6 +1105,10 @@ def compare_design_methods(excel_path=None, n_clusters_override=None, interactiv
|
|||||||
|
|
||||||
is_offshore = True
|
is_offshore = True
|
||||||
|
|
||||||
|
voltage = system_params.get('voltage', VOLTAGE_LEVEL)
|
||||||
|
power_factor = system_params.get('power_factor', POWER_FACTOR)
|
||||||
|
print(f"使用的系统参数: 电压={voltage} V, 功率因数={power_factor}")
|
||||||
|
|
||||||
# 准备三种电缆方案
|
# 准备三种电缆方案
|
||||||
# 原始 specs 是 5 元素元组: (section, capacity, resistance, cost, is_optional)
|
# 原始 specs 是 5 元素元组: (section, capacity, resistance, cost, is_optional)
|
||||||
# 下游函数期望 4 元素元组: (section, capacity, resistance, cost)
|
# 下游函数期望 4 元素元组: (section, capacity, resistance, cost)
|
||||||
@@ -1114,7 +1153,7 @@ def compare_design_methods(excel_path=None, n_clusters_override=None, interactiv
|
|||||||
|
|
||||||
# 1. MST 方法作为基准 (使用 Scenario 1)
|
# 1. MST 方法作为基准 (使用 Scenario 1)
|
||||||
mst_connections = design_with_mst(turbines, substation)
|
mst_connections = design_with_mst(turbines, substation)
|
||||||
mst_evaluation = evaluate_design(turbines, mst_connections, substation, cable_specs=specs_1, is_offshore=is_offshore, method_name="MST Method")
|
mst_evaluation = evaluate_design(turbines, mst_connections, substation, cable_specs=specs_1, is_offshore=is_offshore, method_name="MST Method", voltage=voltage, power_factor=power_factor)
|
||||||
|
|
||||||
# 准备画布 2x2
|
# 准备画布 2x2
|
||||||
fig = None
|
fig = None
|
||||||
@@ -1152,7 +1191,7 @@ def compare_design_methods(excel_path=None, n_clusters_override=None, interactiv
|
|||||||
|
|
||||||
# 计算参数
|
# 计算参数
|
||||||
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, voltage=voltage, power_factor=power_factor)
|
||||||
|
|
||||||
# 确定簇数 (针对 Base 算法)
|
# 确定簇数 (针对 Base 算法)
|
||||||
if n_clusters_override is not None:
|
if n_clusters_override is not None:
|
||||||
@@ -1172,11 +1211,11 @@ def compare_design_methods(excel_path=None, n_clusters_override=None, interactiv
|
|||||||
# --- Run 1: Base Algorithm (Capacitated Sweep) ---
|
# --- Run 1: Base Algorithm (Capacitated Sweep) ---
|
||||||
base_name = f"{name} (Base)"
|
base_name = f"{name} (Base)"
|
||||||
conns_base, turbines_base = design_with_capacitated_sweep(
|
conns_base, turbines_base = design_with_capacitated_sweep(
|
||||||
turbines.copy(), substation, cable_specs=current_specs
|
turbines.copy(), substation, cable_specs=current_specs, voltage=voltage, power_factor=power_factor
|
||||||
)
|
)
|
||||||
eval_base = evaluate_design(
|
eval_base = evaluate_design(
|
||||||
turbines, conns_base, substation, cable_specs=current_specs,
|
turbines, conns_base, substation, cable_specs=current_specs,
|
||||||
is_offshore=is_offshore, method_name=base_name
|
is_offshore=is_offshore, method_name=base_name, voltage=voltage, power_factor=power_factor
|
||||||
)
|
)
|
||||||
|
|
||||||
comparison_results.append({
|
comparison_results.append({
|
||||||
@@ -1192,11 +1231,11 @@ def compare_design_methods(excel_path=None, n_clusters_override=None, interactiv
|
|||||||
# --- Run 2: Rotational Algorithm (Optimization) ---
|
# --- Run 2: Rotational Algorithm (Optimization) ---
|
||||||
rot_name = f"{name} (Rotational)"
|
rot_name = f"{name} (Rotational)"
|
||||||
conns_rot, turbines_rot = design_with_rotational_sweep(
|
conns_rot, turbines_rot = design_with_rotational_sweep(
|
||||||
turbines.copy(), substation, cable_specs=current_specs
|
turbines.copy(), substation, cable_specs=current_specs, voltage=voltage, power_factor=power_factor
|
||||||
)
|
)
|
||||||
eval_rot = evaluate_design(
|
eval_rot = evaluate_design(
|
||||||
turbines, conns_rot, substation, cable_specs=current_specs,
|
turbines, conns_rot, substation, cable_specs=current_specs,
|
||||||
is_offshore=is_offshore, method_name=rot_name
|
is_offshore=is_offshore, method_name=rot_name, voltage=voltage, power_factor=power_factor
|
||||||
)
|
)
|
||||||
|
|
||||||
comparison_results.append({
|
comparison_results.append({
|
||||||
@@ -1216,7 +1255,7 @@ def compare_design_methods(excel_path=None, n_clusters_override=None, interactiv
|
|||||||
)
|
)
|
||||||
eval_ew = evaluate_design(
|
eval_ew = evaluate_design(
|
||||||
turbines, conns_ew, substation, cable_specs=current_specs,
|
turbines, conns_ew, substation, cable_specs=current_specs,
|
||||||
is_offshore=is_offshore, method_name=ew_name
|
is_offshore=is_offshore, method_name=ew_name, voltage=voltage, power_factor=power_factor
|
||||||
)
|
)
|
||||||
|
|
||||||
comparison_results.append({
|
comparison_results.append({
|
||||||
|
|||||||
Reference in New Issue
Block a user