重构爆管定位算法,增加多进程支持与可视化功能

This commit is contained in:
2026-03-08 20:01:21 +08:00
parent a7e3b6aff9
commit 9a4a91c328
5 changed files with 311 additions and 67 deletions
+117 -9
View File
@@ -2,6 +2,7 @@
import copy
import math
import os
import sys
from datetime import datetime
from time import perf_counter
@@ -11,7 +12,11 @@ import numpy as np
import pandas as pd
from .leak_simulator import cal_signature_pipe_multi_pf
from .network_partitioner import cal_group_num, metis_grouping_pipe_weight
from .network_partitioner import (
cal_group_num,
metis_grouping_pipe_weight,
visualize_metis_partition,
)
from .similarity_calculator import (
adjust_ratio,
cal_similarity_all_multi_new_sq_improve_double_lzr,
@@ -30,7 +35,7 @@ def _ensure_signatures_for_centers(
timestep_list, # 你现有的时序列表
pressure_monitor,
flow_monitor, # 用来推断传感器列名
leak_mag, # 泄漏量,比如 400/3600
leak_mag,
n_workers=1,
):
"""
@@ -221,6 +226,41 @@ def _accumulate_stage(stage_timing, stage_name, started_at):
)
def _write_last_round_candidates_csv(
csv_path,
exit_condition,
iteration_count,
similarity_mode,
candidate_details,
fallback_similarity,
):
if not csv_path:
return None
timestamp_suffix = datetime.now().strftime("%Y%m%d_%H%M%S")
base_path, ext = os.path.splitext(csv_path)
ext = ext or ".csv"
output_path = f"{base_path}_{timestamp_suffix}{ext}"
if candidate_details is not None and len(candidate_details) > 0:
export_df = candidate_details.copy()
if export_df.index.name == "pipe_id":
export_df = export_df.reset_index()
else:
export_df = pd.DataFrame(
{
"pipe_id": [str(pipe_id) for pipe_id in fallback_similarity.index],
"final_similarity": [float(value) for value in fallback_similarity.values],
}
)
export_df["exit_condition"] = exit_condition
export_df["iterations"] = int(iteration_count)
export_df["similarity_mode"] = similarity_mode
parent_dir = os.path.dirname(output_path)
if parent_dir:
os.makedirs(parent_dir, exist_ok=True)
export_df.to_csv(output_path, index=False, encoding="utf-8-sig")
return output_path
def cal_DtoTop1(
G0, pipe_leak, located_pipe, pipe_start_node_all, pipe_end_node_all, pipe_length
):
@@ -327,12 +367,18 @@ def DN_search_multi_simple_add_flow_count_new(
Top_sensor_num,
if_gy,
pressure_threshold,
leak_mag=400 / 3600,
leak_mag,
n_workers=1,
stage_timing=None,
partition_on_full_graph=True,
visualize_partition=False,
visualize_pause_seconds=0.3,
final_candidates_csv_path=None,
):
if stage_timing is None:
stage_timing = {}
exit_condition = "unknown"
final_candidates_csv = None
iter_count = 0
all_node_iter = copy.deepcopy(all_node)
candidate_pipe_input = copy.deepcopy(candidate_pipe_input_initial) # 可能漏损管段
@@ -351,6 +397,8 @@ def DN_search_multi_simple_add_flow_count_new(
effective_sensor = list(dpressure.index)
simulation_times = 0 # 模拟次数
if len(dpressure) > 0:
break_flag = 0
last_round_candidate_details = None
cos_h = 0
dis_h = 0
@@ -363,6 +411,7 @@ def DN_search_multi_simple_add_flow_count_new(
final_area = []
final_center = []
group_num = cal_group_num(candidate_pipe_input, group_basic_num)
partition_nodes = all_node if partition_on_full_graph else all_node_iter
# group 分组,得出候选漏损中心
stage_start = perf_counter()
@@ -370,7 +419,7 @@ def DN_search_multi_simple_add_flow_count_new(
metis_grouping_pipe_weight(
G0,
wn,
all_node_iter,
partition_nodes,
candidate_pipe_input,
group_num,
node_x,
@@ -383,6 +432,23 @@ def DN_search_multi_simple_add_flow_count_new(
)
)
_accumulate_stage(stage_timing, "group_partitioning", stage_start)
if visualize_partition:
visualize_metis_partition(
G0,
candidate_center_list,
candidate_group_list,
node_x,
node_y,
pipe_start_node_all,
pipe_end_node_all,
title=(
f"METIS Partition Iteration {iter_count + 1} | "
f"candidate pipes={len(candidate_pipe_input)} "
f"groups={len(candidate_group_list)}"
),
block=False,
pause_seconds=visualize_pause_seconds,
)
simulation_times = simulation_times + len(candidate_center_list)
# pick_pressure_leak
# pressure_leak = pressure_leak_all.loc[candidate_center_list].loc[:, :]
@@ -455,7 +521,9 @@ def DN_search_multi_simple_add_flow_count_new(
n_workers=n_workers,
)
)
_accumulate_stage(stage_timing, "signature_for_extra_centers", stage_start)
_accumulate_stage(
stage_timing, "signature_for_extra_centers", stage_start
)
pressure_leak = pd.concat([pressure_leak, pressure_add], axis=0)
if (flow_leak is not None) and (flow_add is not None):
flow_leak = pd.concat([flow_leak, flow_add], axis=0)
@@ -468,7 +536,7 @@ def DN_search_multi_simple_add_flow_count_new(
candidate_center_list + add_center
)
stage_start = perf_counter()
similarity, cos_h, dis_h, dis_f_h, break_flag = (
similarity, cos_h, dis_h, dis_f_h, break_flag, similarity_details = (
cal_similarity_all_multi_new_sq_improve_double_lzr(
candidate_center_list_sup,
similarity_mode,
@@ -494,8 +562,10 @@ def DN_search_multi_simple_add_flow_count_new(
max_flow,
)
)
last_round_candidate_details = similarity_details
_accumulate_stage(stage_timing, "similarity_ranking", stage_start)
if break_flag == 1:
exit_condition = "similarity_break_flag"
break
new_similarity = update_similarity(
@@ -525,12 +595,15 @@ def DN_search_multi_simple_add_flow_count_new(
final_area = list(set(final_area))
final_center = list(set(final_center))
if if_end == 1:
exit_condition = "candidate_area_if_end"
break
elif len(candidate_pipe_input) == len(final_area):
exit_condition = "candidate_size_no_change"
break
else:
candidate_pipe_input = final_area
all_node_iter = all_node_new_1
if not partition_on_full_graph:
all_node_iter = all_node_new_1
iter_count += 1
sys.stdout.write(
"\r"
@@ -577,7 +650,14 @@ def DN_search_multi_simple_add_flow_count_new(
# 做法:让 _ensure_signatures_for_centers 额外返回 need_cnt,再 simulation_times += need_cnt
stage_start = perf_counter()
similarity_sp, cos_h, dis_h, dis_f_h, break_flag = (
(
similarity_sp,
cos_h,
dis_h,
dis_f_h,
break_flag,
similarity_details,
) = (
cal_similarity_all_multi_new_sq_improve_double_lzr(
final_area_pipe,
similarity_mode,
@@ -603,6 +683,7 @@ def DN_search_multi_simple_add_flow_count_new(
max_flow,
)
)
last_round_candidate_details = similarity_details
_accumulate_stage(stage_timing, "similarity_final", stage_start)
else:
@@ -628,7 +709,16 @@ def DN_search_multi_simple_add_flow_count_new(
)
t2 = datetime.now()
dt = (t2 - t1).seconds
final_candidates_csv = _write_last_round_candidates_csv(
csv_path=final_candidates_csv_path,
exit_condition=exit_condition,
iteration_count=iter_count + 1,
similarity_mode=similarity_mode,
candidate_details=last_round_candidate_details,
fallback_similarity=similarity_sp,
)
else:
exit_condition = "no_effective_sensor_after_threshold"
dpressure = (pressure_predict - pressure_monitor).mean()
dpressure = dpressure.abs()
@@ -639,9 +729,27 @@ def DN_search_multi_simple_add_flow_count_new(
similarity_sp = similarity_sp.sort_values(ascending=False)
t2 = datetime.now()
dt = (t2 - t1).seconds
final_candidates_csv = _write_last_round_candidates_csv(
csv_path=final_candidates_csv_path,
exit_condition=exit_condition,
iteration_count=0,
similarity_mode=similarity_mode,
candidate_details=None,
fallback_similarity=similarity_sp,
)
stage_timing["iterations"] = iter_count + 1 if len(dpressure) > 0 else 0
stage_timing["total_elapsed_seconds"] = float(dt)
return similarity_sp.index[0], dt, simulation_times, wn, similarity_sp
stage_timing["exit_condition"] = exit_condition
stage_timing["final_candidates_csv"] = final_candidates_csv
return (
similarity_sp.index[0],
dt,
simulation_times,
wn,
similarity_sp,
exit_condition,
final_candidates_csv,
)
class BurstLocator: