删除env.local;新增漏损区域识别功能

This commit is contained in:
2026-02-27 17:37:39 +08:00
parent df76e40b0a
commit 5566172e26
6 changed files with 628 additions and 23 deletions
+2
View File
@@ -15,6 +15,7 @@ from app.services.simulation_ops import (
scheduling_simulation,
daily_scheduling_simulation,
)
from app.services.leakage_identifier import run_leakage_identification
__all__ = [
"network_update",
@@ -31,4 +32,5 @@ __all__ = [
"scheduling_simulation",
"daily_scheduling_simulation",
"analyze_valve_isolation",
"run_leakage_identification",
]
+104
View File
@@ -0,0 +1,104 @@
import os
from typing import Any
import pandas as pd
from app.algorithms.leakage_identifier import LeakageIdentifier
from app.services.tjnetwork import (
PARTITION_TYPE_KWAY,
calculate_district_metering_area_for_nodes,
dump_inp,
get_all_scada_info,
get_network_node_coords,
)
def run_leakage_identification(
network: str,
observed_pressure_data: str | pd.DataFrame | dict[str, list[Any]] | list[dict[str, Any]],
start_time: float = 0,
duration: float = 24,
timestep: float = 5,
q_sum: float = 0.2,
q_sum_unit: str = "m3/s",
output_dir: str = "Results",
pop_size: int = 50,
max_gen: int = 100,
output_flow_unit: str = "m3/s",
) -> dict[str, Any]:
os.makedirs(output_dir, exist_ok=True)
inp_path = os.path.join(output_dir, f"{network}.leakage.inp")
dump_inp(network, inp_path, "2")
sensor_nodes = _get_pressure_sensor_nodes(network)
area_map = _build_area_map_by_spectral_partition(network, sensor_nodes)
q_sum_m3s = LeakageIdentifier._flow_to_m3s(q_sum, q_sum_unit)
identifier = LeakageIdentifier(
inp_path=inp_path,
sensor_nodes=sensor_nodes,
area_map=area_map,
start_time=start_time,
duration=duration,
timestep=timestep,
q_sum=q_sum_m3s,
)
result_df = identifier.run_identification(
observed_pressure_data=observed_pressure_data,
output_dir=output_dir,
pop_size=pop_size,
max_gen=max_gen,
output_flow_unit=output_flow_unit,
save_result=False,
)
return {
"result_path": result_df.attrs.get("result_path"),
"sensor_nodes": sensor_nodes,
"area_count": len(set(area_map.values())),
"rows": result_df.to_dict(orient="records"),
}
def _get_pressure_sensor_nodes(network: str) -> list[str]:
scada_info = get_all_scada_info(network)
sensor_nodes: list[str] = []
for item in scada_info:
if item.get("type") != "pressure":
continue
node_id = item.get("associated_element_id")
if isinstance(node_id, str) and node_id:
sensor_nodes.append(node_id)
sensor_nodes = list(dict.fromkeys(sensor_nodes))
if not sensor_nodes:
raise ValueError("未找到压力传感器对应节点(scada_info.type=pressure)。")
return sensor_nodes
def _build_area_map_by_spectral_partition(
network: str, sensor_nodes: list[str]
) -> dict[str, str]:
node_coords = get_network_node_coords(network)
all_nodes = list(node_coords.keys())
if not all_nodes:
raise ValueError("管网中未获取到可分区节点。")
part_count = min(len(sensor_nodes), len(all_nodes))
if part_count <= 0:
raise ValueError("无可用压力传感器,无法生成虚拟分区。")
groups = calculate_district_metering_area_for_nodes(
network,
all_nodes,
part_count=part_count,
part_type=PARTITION_TYPE_KWAY,
)
if not groups:
raise ValueError("虚拟分区计算失败,未返回分区结果。")
area_map: dict[str, str] = {}
for idx, group_nodes in enumerate(groups, start=1):
area_id = str(idx)
for node_id in group_nodes:
area_map[node_id] = area_id
if not area_map:
raise ValueError("虚拟分区结果为空,无法生成节点区域映射。")
return area_map