调整api结构;恢复丢失部分api,详见scripts文件夹;新增关阀分析算法,实现api

This commit is contained in:
2026-01-29 11:39:50 +08:00
parent 7c9667822f
commit 9d7a9fb2fd
17 changed files with 6866 additions and 45 deletions

View File

@@ -3,6 +3,7 @@ from app.algorithms.sensors import (
pressure_sensor_placement_sensitivity,
pressure_sensor_placement_kmeans,
)
from app.algorithms.valve_isolation import valve_isolation_analysis
from app.algorithms.simulations import (
convert_to_local_unit,
burst_analysis,
@@ -25,4 +26,5 @@ __all__ = [
"contaminant_simulation",
"age_analysis",
"pressure_regulation",
"valve_isolation_analysis",
]

View File

@@ -14,14 +14,20 @@ class PipelineHealthAnalyzer:
使用前需确保安装依赖joblib, pandas, numpy, scikit-survival, matplotlib。
"""
def __init__(self, model_path: str = "model/my_survival_forest_model_quxi.joblib"):
def __init__(self, model_path: str = None):
"""
初始化分析器,加载预训练的随机生存森林模型。
:param model_path: 模型文件的路径(默认为相对路径 'model/my_survival_forest_model_quxi.joblib')。
:param model_path: 模型文件的路径(默认为相对路径 './model/my_survival_forest_model_quxi.joblib')。
:raises FileNotFoundError: 如果模型文件不存在。
:raises Exception: 如果模型加载失败。
"""
if model_path is None:
model_path = os.path.join(
os.path.dirname(__file__),
"model",
"my_survival_forest_model_quxi.joblib",
)
# 确保 model 目录存在
model_dir = os.path.dirname(model_path)
if model_dir and not os.path.exists(model_dir):

View File

@@ -0,0 +1,86 @@
from collections import defaultdict, deque
from typing import Any
from app.services.tjnetwork import (
get_link_properties,
get_link_type,
get_network_link_nodes,
is_link,
is_node,
)
VALVE_LINK_TYPE = "valve"
def _parse_link_entry(link_entry: str) -> tuple[str, str, str, str]:
parts = link_entry.split(":", 3)
if len(parts) != 4:
raise ValueError(f"Invalid link entry format: {link_entry}")
return parts[0], parts[1], parts[2], parts[3]
def valve_isolation_analysis(network: str, accident_element: str) -> dict[str, Any]:
"""
关阀搜索/分析:基于拓扑结构确定事故隔离所需关阀。
:param network: 模型名称
:param accident_element: 事故点(节点或管道/泵/阀门ID
:return: dict包含受影响节点、必须关闭阀门、可选阀门等信息
"""
if is_node(network, accident_element):
start_nodes = {accident_element}
accident_type = "node"
elif is_link(network, accident_element):
accident_type = get_link_type(network, accident_element)
link_props = get_link_properties(network, accident_element)
node1 = link_props.get("node1")
node2 = link_props.get("node2")
if not node1 or not node2:
raise ValueError("Accident link missing node endpoints")
start_nodes = {node1, node2}
else:
raise ValueError("Accident element not found")
adjacency: dict[str, set[str]] = defaultdict(set)
valve_links: dict[str, tuple[str, str]] = {}
for link_entry in get_network_link_nodes(network):
link_id, link_type, node1, node2 = _parse_link_entry(link_entry)
link_type_name = str(link_type).lower()
if link_type_name == VALVE_LINK_TYPE:
valve_links[link_id] = (node1, node2)
continue
adjacency[node1].add(node2)
adjacency[node2].add(node1)
affected_nodes: set[str] = set()
queue = deque(start_nodes)
while queue:
node = queue.popleft()
if node in affected_nodes:
continue
affected_nodes.add(node)
for neighbor in adjacency.get(node, []):
if neighbor not in affected_nodes:
queue.append(neighbor)
must_close_valves: list[str] = []
optional_valves: list[str] = []
for valve_id, (node1, node2) in valve_links.items():
in_node1 = node1 in affected_nodes
in_node2 = node2 in affected_nodes
if in_node1 and in_node2:
optional_valves.append(valve_id)
elif in_node1 or in_node2:
must_close_valves.append(valve_id)
must_close_valves.sort()
optional_valves.sort()
return {
"accident_element": accident_element,
"accident_type": accident_type,
"affected_nodes": sorted(affected_nodes),
"must_close_valves": must_close_valves,
"optional_valves": optional_valves,
"isolatable": len(must_close_valves) > 0,
}