923e4a8220
Co-authored-by: multica-agent <github@multica.ai>
510 lines
17 KiB
Python
510 lines
17 KiB
Python
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from app.services.fl_analysis_rules import (
|
|
grade_mitigation_snapshot_payload,
|
|
grade_normal_snapshot_payload,
|
|
grade_snapshot_payload,
|
|
grade_tongtiao_snapshot_payload,
|
|
)
|
|
|
|
|
|
def _build_circuit_geometry(
|
|
*,
|
|
shield_left_m: float,
|
|
shield_right_m: float,
|
|
shield_height_m: float,
|
|
insulator_length_mm: float,
|
|
circuit_i_upper_m: float,
|
|
circuit_i_middle_m: float,
|
|
circuit_i_lower_m: float,
|
|
circuit_i_upper_h_m: float,
|
|
circuit_i_middle_h_m: float,
|
|
circuit_i_lower_h_m: float,
|
|
circuit_ii_upper_m: float | None = None,
|
|
circuit_ii_middle_m: float | None = None,
|
|
circuit_ii_lower_m: float | None = None,
|
|
circuit_ii_upper_h_m: float | None = None,
|
|
circuit_ii_middle_h_m: float | None = None,
|
|
circuit_ii_lower_h_m: float | None = None,
|
|
) -> dict[str, object]:
|
|
return {
|
|
"I": {
|
|
"phase_spacing_m": {
|
|
"upper": circuit_i_upper_m,
|
|
"middle": circuit_i_middle_m,
|
|
"lower": circuit_i_lower_m,
|
|
},
|
|
"phase_height_m": {
|
|
"upper": circuit_i_upper_h_m,
|
|
"middle": circuit_i_middle_h_m,
|
|
"lower": circuit_i_lower_h_m,
|
|
},
|
|
},
|
|
"II": {
|
|
"phase_spacing_m": {
|
|
"upper": circuit_ii_upper_m,
|
|
"middle": circuit_ii_middle_m,
|
|
"lower": circuit_ii_lower_m,
|
|
},
|
|
"phase_height_m": {
|
|
"upper": circuit_ii_upper_h_m,
|
|
"middle": circuit_ii_middle_h_m,
|
|
"lower": circuit_ii_lower_h_m,
|
|
},
|
|
},
|
|
"lightning_wire": {
|
|
"left_mid_distance_m": shield_left_m,
|
|
"right_mid_distance_m": shield_right_m,
|
|
"height_m": shield_height_m,
|
|
},
|
|
"insulator_length_mm": insulator_length_mm,
|
|
}
|
|
|
|
|
|
def test_grade_snapshot_payload_marks_high_risk_and_actions() -> None:
|
|
payload = {
|
|
"base_tower_json": {
|
|
"tower_no": "T001",
|
|
"tower_type": "耐张",
|
|
"ground_resistance_ohm": 35.0,
|
|
"lightning_density": 6.5,
|
|
"span_large_m": 620.0,
|
|
"line_voltage_kv": 110,
|
|
"slope_1": 18.0,
|
|
"slope_2": 6.0,
|
|
"circuit_geometry_json": {
|
|
"I": {
|
|
"phase_height_m": {"upper": 28.0, "middle": 24.0, "lower": 20.0},
|
|
},
|
|
"lightning_wire": {
|
|
"left_mid_distance_m": 9.0,
|
|
"right_mid_distance_m": 9.0,
|
|
"height_m": 32.0,
|
|
},
|
|
},
|
|
},
|
|
"profile_json": {
|
|
"structure_kind": "耐张",
|
|
"stroke_mode": "反击",
|
|
"arrester_a": "否",
|
|
"arrester_b": "否",
|
|
"arrester_c": "否",
|
|
"insulator_length_m": 1200.0,
|
|
},
|
|
}
|
|
|
|
result = grade_snapshot_payload(payload)
|
|
|
|
assert result["risk_level"] == "high"
|
|
assert result["score"] >= 80
|
|
assert "接地电阻偏高" in result["cause_analysis"]
|
|
assert "避雷器" in result["mitigation_recommendation"]
|
|
assert any(item["code"] == "insulator_length" for item in result["reason_details"])
|
|
|
|
|
|
def test_grade_snapshot_payload_marks_low_risk_when_inputs_are_good() -> None:
|
|
payload = {
|
|
"base_tower_json": {
|
|
"tower_no": "T002",
|
|
"tower_type": "直线",
|
|
"ground_resistance_ohm": 4.0,
|
|
"lightning_density": 1.5,
|
|
"span_large_m": 180.0,
|
|
"line_voltage_kv": 110,
|
|
},
|
|
"profile_json": {
|
|
"structure_kind": "直线",
|
|
"stroke_mode": "绕击",
|
|
"arrester_a": "是",
|
|
"arrester_b": "是",
|
|
"arrester_c": "是",
|
|
"insulator_length_m": 1800.0,
|
|
},
|
|
}
|
|
|
|
result = grade_snapshot_payload(payload)
|
|
|
|
assert result["risk_level"] == "low"
|
|
assert result["score"] < 40
|
|
assert result["cause_analysis"]
|
|
assert result["mitigation_recommendation"]
|
|
|
|
|
|
def test_grade_mitigation_snapshot_payload_builds_actions_and_reduces_expected_risk() -> None:
|
|
payload = {
|
|
"base_tower_json": {
|
|
"tower_no": "T003",
|
|
"tower_type": "耐张",
|
|
"ground_resistance_ohm": 28.0,
|
|
"lightning_density": 5.0,
|
|
"span_large_m": 420.0,
|
|
"line_voltage_kv": 220,
|
|
"slope_1": 12.0,
|
|
"slope_2": 8.0,
|
|
"circuit_geometry_json": {
|
|
"I": {
|
|
"phase_height_m": {"upper": 36.0, "middle": 32.0, "lower": 28.0},
|
|
},
|
|
"lightning_wire": {
|
|
"left_mid_distance_m": 8.5,
|
|
"right_mid_distance_m": 8.5,
|
|
"height_m": 40.0,
|
|
},
|
|
},
|
|
},
|
|
"profile_json": {
|
|
"structure_kind": "耐张",
|
|
"stroke_mode": "反击",
|
|
"arrester_a": "否",
|
|
"arrester_b": "否",
|
|
"arrester_c": "否",
|
|
"insulator_length_m": 2000.0,
|
|
},
|
|
}
|
|
|
|
result = grade_mitigation_snapshot_payload(payload, non_construction=False)
|
|
|
|
assert result["recommendation_result"] in {"需要安装避雷器", "不需要安装避雷器"}
|
|
assert result["mitigation_actions"]
|
|
assert result["expected_score"] < result["current_score"]
|
|
assert any(action["code"] == "grounding_upgrade" for action in result["mitigation_actions"])
|
|
|
|
|
|
def test_grade_mitigation_snapshot_payload_includes_non_construction_action() -> None:
|
|
payload = {
|
|
"base_tower_json": {
|
|
"tower_no": "T004",
|
|
"tower_model": "s220guxing",
|
|
"tower_type": "直线",
|
|
"ground_resistance_ohm": 16.0,
|
|
"lightning_density": 3.5,
|
|
"span_large_m": 260.0,
|
|
"line_voltage_kv": 220,
|
|
"circuit_geometry_json": {
|
|
"I": {
|
|
"phase_height_m": {"upper": 30.0, "middle": 26.0, "lower": 22.0},
|
|
},
|
|
"lightning_wire": {
|
|
"left_mid_distance_m": 10.0,
|
|
"right_mid_distance_m": 10.0,
|
|
"height_m": 33.0,
|
|
},
|
|
},
|
|
},
|
|
"profile_json": {
|
|
"structure_kind": "直线",
|
|
"stroke_mode": "绕击",
|
|
"arrester_a": "是",
|
|
"arrester_b": "是",
|
|
"arrester_c": "是",
|
|
"insulator_length_m": 2500.0,
|
|
},
|
|
}
|
|
|
|
result = grade_mitigation_snapshot_payload(payload, non_construction=True)
|
|
|
|
assert result["non_construction"] is True
|
|
assert any(action["code"] == "shielding_geometry" for action in result["mitigation_actions"])
|
|
|
|
|
|
def test_grade_mitigation_snapshot_payload_prefers_source_risk_result() -> None:
|
|
payload = {
|
|
"base_tower_json": {
|
|
"tower_no": "T005",
|
|
"tower_type": "直线",
|
|
"ground_resistance_ohm": 4.0,
|
|
"lightning_density": 1.5,
|
|
"span_large_m": 180.0,
|
|
"line_voltage_kv": 110,
|
|
},
|
|
"profile_json": {
|
|
"structure_kind": "直线",
|
|
"stroke_mode": "绕击",
|
|
"arrester_a": "是",
|
|
"arrester_b": "是",
|
|
"arrester_c": "是",
|
|
"insulator_length_m": 1800.0,
|
|
},
|
|
"source_result_json": {
|
|
"risk_level": "high",
|
|
"score": 92,
|
|
"cause_analysis": "沿用前驱风险结果",
|
|
"reason_details": [{"code": "source_reason", "label": "前驱原因"}],
|
|
"inputs": {"ground_resistance_ohm": 35.0},
|
|
},
|
|
}
|
|
|
|
result = grade_mitigation_snapshot_payload(payload, non_construction=False)
|
|
|
|
assert result["current_risk_level"] == "high"
|
|
assert result["current_score"] == 92
|
|
assert result["cause_analysis"] == "沿用前驱风险结果"
|
|
assert result["reason_details"][0]["code"] == "source_reason"
|
|
|
|
|
|
def test_grade_snapshot_payload_uses_source_formula_and_converts_mm_to_m() -> None:
|
|
geometry = _build_circuit_geometry(
|
|
shield_left_m=12.4,
|
|
shield_right_m=15.9,
|
|
shield_height_m=73.5,
|
|
insulator_length_mm=5945.0,
|
|
circuit_i_upper_m=14.5,
|
|
circuit_i_middle_m=10.5,
|
|
circuit_i_lower_m=12.0,
|
|
circuit_i_upper_h_m=53.0,
|
|
circuit_i_middle_h_m=65.5,
|
|
circuit_i_lower_h_m=42.0,
|
|
circuit_ii_upper_m=14.5,
|
|
circuit_ii_middle_m=10.5,
|
|
circuit_ii_lower_m=12.0,
|
|
circuit_ii_upper_h_m=53.0,
|
|
circuit_ii_middle_h_m=65.5,
|
|
circuit_ii_lower_h_m=42.0,
|
|
)
|
|
payload = {
|
|
"base_tower_json": {
|
|
"tower_no": "P1",
|
|
"tower_type": "耐张",
|
|
"tower_model": "500-MC31S-DJC2-42",
|
|
"line_name": "交流500kV雁船线(双回段)",
|
|
"line_voltage_kv": 500,
|
|
"ground_resistance_ohm": 20.0,
|
|
"lightning_density": 5.42528304182587,
|
|
"slope_1": -5.70339674677262,
|
|
"slope_2": -1.42923539492821,
|
|
"circuit_geometry_json": geometry,
|
|
"lightning_result_json": {},
|
|
},
|
|
"profile_json": {
|
|
"structure_kind": "耐张",
|
|
"arrester_a": "否",
|
|
"arrester_b": "否",
|
|
"arrester_c": "否",
|
|
"shield_wire_height_m": 73.5,
|
|
"insulator_length_m": 5945.0,
|
|
"current_a": 25.109,
|
|
"current_b": 3.62,
|
|
},
|
|
}
|
|
|
|
result = grade_snapshot_payload(payload)
|
|
|
|
assert result["risk_level"] == "high"
|
|
assert result["risk_grade"] == 3
|
|
assert result["inputs"]["insulator_length_m"] == pytest.approx(5.945, abs=1e-6)
|
|
assert result["counterstrike_withstand_ka"] == pytest.approx(75.8070, rel=1e-4)
|
|
assert result["counterstrike_trip_rate"] == pytest.approx(0.529876, rel=1e-4)
|
|
assert result["shielding_withstand_ka"] == pytest.approx(31.2860, rel=1e-4)
|
|
assert result["shielding_trip_rate"] == pytest.approx(0.000003, abs=1e-6)
|
|
assert "接地电阻" in result["cause_analysis"]
|
|
assert "规程法等级 3" in result["summary_text"]
|
|
|
|
|
|
def test_grade_snapshot_payload_marks_low_risk_for_good_inputs() -> None:
|
|
geometry = _build_circuit_geometry(
|
|
shield_left_m=8.0,
|
|
shield_right_m=8.0,
|
|
shield_height_m=40.0,
|
|
insulator_length_mm=3500.0,
|
|
circuit_i_upper_m=10.0,
|
|
circuit_i_middle_m=3.0,
|
|
circuit_i_lower_m=8.0,
|
|
circuit_i_upper_h_m=28.0,
|
|
circuit_i_middle_h_m=31.0,
|
|
circuit_i_lower_h_m=24.0,
|
|
)
|
|
payload = {
|
|
"base_tower_json": {
|
|
"tower_no": "L1",
|
|
"tower_type": "直线",
|
|
"tower_model": "220-TEST-ZX",
|
|
"line_name": "交流220kV示例线",
|
|
"line_voltage_kv": 220,
|
|
"ground_resistance_ohm": 5.0,
|
|
"lightning_density": 1.2,
|
|
"slope_1": 0.0,
|
|
"slope_2": 0.0,
|
|
"circuit_geometry_json": geometry,
|
|
"lightning_result_json": {},
|
|
},
|
|
"profile_json": {
|
|
"structure_kind": "直线",
|
|
"arrester_a": "是",
|
|
"arrester_b": "是",
|
|
"arrester_c": "是",
|
|
"shield_wire_height_m": 40.0,
|
|
"insulator_length_m": 3500.0,
|
|
"current_a": 31.0,
|
|
"current_b": 2.6,
|
|
},
|
|
}
|
|
|
|
result = grade_snapshot_payload(payload)
|
|
|
|
assert result["risk_level"] == "low"
|
|
assert result["risk_grade"] == 1
|
|
assert result["score"] < 30
|
|
assert result["counterstrike_trip_rate"] == pytest.approx(0.109404, rel=1e-4)
|
|
assert result["shielding_trip_rate"] == pytest.approx(0.006737, rel=1e-4)
|
|
assert "高风险" not in result["summary_text"]
|
|
|
|
|
|
def test_grade_snapshot_payload_falls_back_to_legacy_result_when_geometry_is_missing() -> None:
|
|
payload = {
|
|
"base_tower_json": {
|
|
"tower_no": "F1",
|
|
"lightning_result_json": {
|
|
"counterstroke_withstand_ka": 52.3,
|
|
"counterstroke_trip_rate": 0.12,
|
|
"shielding_withstand_ka": 18.6,
|
|
"shielding_trip_rate": 0.03,
|
|
"risk_level": 2,
|
|
},
|
|
},
|
|
"profile_json": {},
|
|
}
|
|
|
|
result = grade_snapshot_payload(payload)
|
|
|
|
assert result["used_legacy_fallback"] is True
|
|
assert result["risk_level"] == "medium"
|
|
assert result["counterstrike_withstand_ka"] == pytest.approx(52.3)
|
|
assert result["shielding_trip_rate"] == pytest.approx(0.03)
|
|
assert "历史结果" in result["cause_analysis"]
|
|
|
|
|
|
def test_grade_normal_snapshot_payload_builds_waveform_scan_and_selected_case() -> None:
|
|
geometry = _build_circuit_geometry(
|
|
shield_left_m=9.0,
|
|
shield_right_m=9.0,
|
|
shield_height_m=41.0,
|
|
insulator_length_mm=4200.0,
|
|
circuit_i_upper_m=9.0,
|
|
circuit_i_middle_m=4.5,
|
|
circuit_i_lower_m=8.5,
|
|
circuit_i_upper_h_m=29.0,
|
|
circuit_i_middle_h_m=31.0,
|
|
circuit_i_lower_h_m=25.0,
|
|
)
|
|
payload = {
|
|
"base_tower_json": {
|
|
"tower_no": "N1",
|
|
"tower_type": "直线",
|
|
"tower_model": "220-TEST-ZX",
|
|
"line_name": "交流220kV示例线",
|
|
"line_voltage_kv": 220,
|
|
"ground_resistance_ohm": 12.0,
|
|
"lightning_density": 3.2,
|
|
"altitude_m": 1680.0,
|
|
"slope_1": 3.0,
|
|
"slope_2": 1.5,
|
|
"circuit_geometry_json": geometry,
|
|
"lightning_result_json": {},
|
|
},
|
|
"profile_json": {
|
|
"structure_kind": "直线",
|
|
"arrester_a": "是",
|
|
"arrester_b": "否",
|
|
"arrester_c": "是",
|
|
"shield_wire_height_m": 41.0,
|
|
"insulator_length_m": 4200.0,
|
|
"current_type": "Heidler",
|
|
},
|
|
}
|
|
|
|
result = grade_normal_snapshot_payload(
|
|
payload,
|
|
execution_options={
|
|
"current_waveform": "double_slope",
|
|
"flashover_method": "intersection",
|
|
"altitude_correction": "formula1",
|
|
"induced_voltage_formula": "formula2",
|
|
"head_time_min_us": 2.4,
|
|
"head_time_max_us": 2.6,
|
|
"head_time_step_us": 0.2,
|
|
"tail_time_min_us": 45.0,
|
|
"tail_time_max_us": 50.0,
|
|
"tail_time_step_us": 5.0,
|
|
},
|
|
)
|
|
|
|
assert result["job_type"] == "normal"
|
|
assert result["workflow"]["scan_point_count"] == 4
|
|
assert len(result["scan_points"]) == 4
|
|
assert result["selected_case"]["head_time_us"] is not None
|
|
assert result["selected_case"]["tail_time_us"] is not None
|
|
assert any(item["code"] == "current_head_time" for item in result["reason_details"])
|
|
assert any(item["code"] == "current_tail_time" for item in result["reason_details"])
|
|
assert "普通计算" in result["summary_text"]
|
|
|
|
|
|
def test_grade_tongtiao_snapshot_payload_builds_phase_and_multi_phase_results() -> None:
|
|
geometry = _build_circuit_geometry(
|
|
shield_left_m=11.0,
|
|
shield_right_m=11.0,
|
|
shield_height_m=72.0,
|
|
insulator_length_mm=5600.0,
|
|
circuit_i_upper_m=14.0,
|
|
circuit_i_middle_m=11.0,
|
|
circuit_i_lower_m=9.0,
|
|
circuit_i_upper_h_m=54.0,
|
|
circuit_i_middle_h_m=50.0,
|
|
circuit_i_lower_h_m=46.0,
|
|
circuit_ii_upper_m=14.0,
|
|
circuit_ii_middle_m=11.0,
|
|
circuit_ii_lower_m=9.0,
|
|
circuit_ii_upper_h_m=53.0,
|
|
circuit_ii_middle_h_m=49.0,
|
|
circuit_ii_lower_h_m=45.0,
|
|
)
|
|
payload = {
|
|
"base_tower_json": {
|
|
"tower_no": "TT1",
|
|
"tower_type": "耐张",
|
|
"tower_model": "500-GUXING",
|
|
"line_name": "交流500kV双回示例线",
|
|
"line_voltage_kv": 500,
|
|
"ground_resistance_ohm": 18.0,
|
|
"lightning_density": 4.6,
|
|
"altitude_m": 1220.0,
|
|
"slope_1": 8.0,
|
|
"slope_2": 3.0,
|
|
"circuit_geometry_json": geometry,
|
|
"lightning_result_json": {},
|
|
},
|
|
"profile_json": {
|
|
"structure_kind": "耐张",
|
|
"phase_sequence_1": "ABC",
|
|
"phase_sequence_2": "CAB",
|
|
"arrester_a": "否",
|
|
"arrester_b": "否",
|
|
"arrester_c": "是",
|
|
"shield_wire_height_m": 72.0,
|
|
"insulator_length_m": 5600.0,
|
|
"current_type": "Heidler",
|
|
},
|
|
}
|
|
|
|
result = grade_tongtiao_snapshot_payload(
|
|
payload,
|
|
execution_options={
|
|
"current_waveform": "heidler",
|
|
"flashover_method": "leader_development",
|
|
"head_time_min_us": 2.6,
|
|
"head_time_max_us": 2.6,
|
|
"head_time_step_us": 0.1,
|
|
"tail_time_min_us": 50.0,
|
|
"tail_time_max_us": 50.0,
|
|
"tail_time_step_us": 1.0,
|
|
},
|
|
)
|
|
|
|
assert result["job_type"] == "tongtiao"
|
|
assert result["phase_results"]
|
|
assert result["multi_phase_results"]
|
|
assert result["dominant_phase_set"] in {"单相", "双相", "三相", "四相", "五相", "六相"}
|
|
assert result["flashover_phase"]
|
|
assert result["workflow"]["current_waveform"] == "heidler"
|