perf(계측): 격자 확장·하강 방향장 단계 마크 추가

PLAN 0-10. 1차 영역이 2.6초로 떨어진 뒤 남은 병목이 `expand_by_red_boundary` 102초라
그 안을 가르기 위한 계측. 로직 불변.

- 확장 회차별 시간·격자·셀 수·방향장 재사용 여부를 한 줄씩
- 격자 해석 1회: 하강 방향장(재사용/새로) / 지형 조립 / 도로 굽기 / 세류 새김 / 도달 판정
- 하강 방향장: 등고선 굽기 / 밴드 표고(EDT 1회) / 밴드별 하강거리(EDT N회) /
  위치에너지 조립 / 흐름 경로

실측 로그상 방향장이 회차마다 새로 만들어지고 밴드가 100단이라, 격자 전체 EDT 가
회차당 100회씩 도는 구조로 보임 — 그 가설을 수치로 가르려는 것.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-07 04:58:45 +09:00
co-authored by Claude Opus 5
parent 9ded6fe6c6
commit 5c22c367a9
2 changed files with 41 additions and 1 deletions
@@ -23,6 +23,7 @@
from __future__ import annotations
import logging
import time
import math
from dataclasses import dataclass
from typing import Any
@@ -127,8 +128,12 @@ def build_contour_descent(
elevation_floor_m: float | None = None,
) -> ContourDescent:
"""등고선만으로 셀별 흐름 방향을 세운다. 보간면을 만들지 않는다."""
from B03_FileInput.B03_FileInput_Service_Chain import _log_steps
marks = [("시작", time.perf_counter())]
rows, cols = spec.n_rows, spec.n_cols
burned, levels = rasterize_contours(spec, contour_features, elevation_floor_m)
marks.append(("등고선 굽기(rasterize_contours)", time.perf_counter()))
empty = ContourDescent(
spec=spec,
band_elevation=np.full((rows, cols), np.nan, dtype=np.float32),
@@ -146,6 +151,7 @@ def build_contour_descent(
# ② 셀마다 가장 가까운 등고 라인의 표고 = 그 셀의 밴드.
_, (near_row, near_col) = distance_transform_edt(~on_contour, return_indices=True)
band_elevation = burned[near_row, near_col].astype(np.float32)
marks.append(("밴드 표고(EDT 1회)", time.perf_counter()))
inside = domain if domain is not None else np.ones((rows, cols), dtype=bool)
band_elevation = np.where(inside, band_elevation, np.nan)
@@ -169,6 +175,7 @@ def build_contour_descent(
# 최하단 밴드는 더 내려갈 등고 라인이 없다. 무효로 빼지 않고 **정지 셀**로 남긴다 —
# 그래야 그 자리가 도로·세류선이면 적색으로 잡히고, 아니면 물이 고이는 지점으로 보인다.
marks.append((f"밴드별 하강거리(EDT {len(levels) - 1}회)", time.perf_counter()))
lowest = inside & (band_elevation == levels[-1]) & (band_rank < 0)
if lowest.any():
distance[lowest] = 0.0
@@ -185,9 +192,12 @@ def build_contour_descent(
distance[valid & ~np.isfinite(distance)] = 0.0
potential = np.where(valid, band_rank.astype(np.float64) * span + distance, np.inf)
marks.append(("위치에너지 조립", time.perf_counter()))
receiver, step_length, azimuth = _route_by_potential(
spec, potential, valid, target_row, target_col
)
marks.append(("흐름 경로(_route_by_potential)", time.perf_counter()))
_log_steps(f"하강 방향장({rows}×{cols})", marks)
logger.info(
"배수유역: 등고선 하강 방향 %d셀 (밴드 %d단), 최하단 정지 %d셀",
int(valid.sum()),