feat:[FL-111][地闪密度计算功能优化]实现电压等级自动推荐半径

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
chengkai3
2026-06-13 23:08:42 +08:00
parent a77500a762
commit 46e3b203d4
2 changed files with 44 additions and 8 deletions
+1 -1
View File
@@ -321,7 +321,7 @@ class LightningDensityPreparationRequest(BaseModel):
line_id: str = Field(min_length=1, max_length=64)
region_id: str | None = Field(default=None, max_length=64)
is_synthetic: bool | None = None
radius_km: float = Field(default=3.0, gt=0.05, le=50.0)
radius_km: float | None = Field(default=None, gt=0.05, le=50.0)
years: float | None = Field(default=None, gt=0)
+43 -7
View File
@@ -69,6 +69,36 @@ DEGREE_TO_KM = 111.32
TERRAIN_ALGORITHM_VERSION = "horn_3x3.v1"
def recommend_radius_km(voltage_kv: int | None) -> float:
"""
根据线路电压等级推荐地闪密度计算的缓冲区半径。
参考规则(来自参考工程FormDiShanMiDu.cs):
- 35/66/110kV → 2.0 km
- 220/330kV → 3.0 km
- 500/750/800/1000kV → 5.0 km
- ±500/±800直流 → 5.0 km
- 默认(未知或无电压等级) → 2.0 km
Args:
voltage_kv: 线路电压等级(kV
Returns:
推荐的缓冲区半径(km)
"""
if voltage_kv is None:
return 2.0
if voltage_kv in (35, 66, 110):
return 2.0
elif voltage_kv in (220, 330):
return 3.0
elif voltage_kv in (500, 750, 800, 1000):
return 5.0
else:
return 2.0
@dataclass
class ParsedSeries:
currents_ka: list[float]
@@ -1435,6 +1465,12 @@ def prepare_line_lightning_density(
if not line:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="线路不存在")
# 当radius_km未指定时,根据电压等级自动推荐半径
if payload.radius_km is None:
actual_radius_km = recommend_radius_km(line.voltage_kv)
else:
actual_radius_km = payload.radius_km
towers = db.execute(
select(LineTower)
.where(LineTower.line_id == line.id)
@@ -1447,9 +1483,9 @@ def prepare_line_lightning_density(
if not geo_towers:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="当前线路缺少杆塔经纬度,无法计算地闪密度")
lat_delta = payload.radius_km / DEGREE_TO_KM
lat_delta = actual_radius_km / DEGREE_TO_KM
lon_deltas = [
payload.radius_km / _safe_km_per_lon(float(tower.latitude))
actual_radius_km / _safe_km_per_lon(float(tower.latitude))
for tower in geo_towers
if tower.latitude is not None
]
@@ -1481,7 +1517,7 @@ def prepare_line_lightning_density(
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="未找到可用于地闪密度计算的雷击分布数据")
now = utcnow()
area_km2 = math.pi * (payload.radius_km ** 2)
area_km2 = math.pi * (actual_radius_km ** 2)
updated_tower_count = 0
missing_geo_count = 0
density_values: list[float] = []
@@ -1505,7 +1541,7 @@ def prepare_line_lightning_density(
float(row.latitude),
float(row.longitude),
)
if distance_km > payload.radius_km:
if distance_km > actual_radius_km:
continue
strike_count += 1
if row.event_time is not None:
@@ -1521,7 +1557,7 @@ def prepare_line_lightning_density(
raw_extra["lightning_density"] = {
"line_id": line.id,
"line_code": line.code,
"radius_km": payload.radius_km,
"radius_km": actual_radius_km,
"data_years": round(tower_years, 6),
"strike_count": strike_count,
"region_id": _normalize_str(payload.region_id),
@@ -1545,7 +1581,7 @@ def prepare_line_lightning_density(
"prepared_by_user_id": actor_user_id,
"region_id": _normalize_str(payload.region_id),
"is_synthetic": payload.is_synthetic,
"radius_km": payload.radius_km,
"radius_km": actual_radius_km,
"data_years": round(source_years, 6),
"updated_tower_count": updated_tower_count,
"missing_geo_count": missing_geo_count,
@@ -1565,7 +1601,7 @@ def prepare_line_lightning_density(
line=serialize_line(line, tower_count=len(towers), preparation_json=preparation_json),
updated_tower_count=updated_tower_count,
missing_geo_count=missing_geo_count,
radius_km=payload.radius_km,
radius_km=actual_radius_km,
data_years=round(source_years, 6),
avg_density=round(sum(density_values) / len(density_values), 6) if density_values else None,
min_density=round(min(density_values), 6) if density_values else None,