feat(B05): 배수 추천 구조물·관경 자동 설정 + 물넘이·세월교 개략 단면
지시 2 — 배수유역도가 유효직경 다음에 추천을 보여 주고, 초기 전처리 계산이 그 값으로 관 지점을 설정한다: - recommend_structure(): 유효직경 임계로 구조물·관경을 고른다. D≤1500 배관(규격 스냅 800·1000·1200·1500), 1500<D≤2000 BOX암거 후보, D>2000 세월교 검토. 판정 근거는 유량뿐 — 계곡 횡단경사·하천 차수는 지형 계산이 필요해 뺐고 화면이 "현장 확인"으로 안내한다(2026-08-17 사용자 확정) - WatershedBasin에 recommended_facility·recommended_diameter_mm·required_area_m2 추가, 응답 payload와 DetailBasin 타입까지 배선 - _apply_recommendations(): 자동 배치 관의 **빈칸에만** 추천을 채운다. 사용자가 놓은 관(source=user)·이미 바꾼 시설·이미 있는 옵션 키는 건드리지 않는다. carry_facility_attributes 뒤에 얹어 승계값이 덮이지 않게 했다 - 유역 목록 표기: `Ø1234mm → 추천 Ø1500 배관` / `→ BOX암거 후보` - 추천 관경 목록은 레지스트리 pipe_diameter_mm 선택지와 같아야 한다 — 폼에서 못 고르는 값을 추천하지 않도록 테스트로 묶었다 지시 1 — 물넘이포장·세월교는 지정할 옵션이 거의 없으니 계산값이라도 보여 준다: - 레지스트리에 ford_width_m(월류 폭) 신설. 기본값 없음 — 지식DB에 폭 근거가 없어 프로그램이 지어내지 않는다 - fordSection(): 설계유량과 폭으로 필요 수심을 되짚는다. 광폭 근사로 출발해 실제 동수반경(R=A/P)으로 수렴시킨다 — 근사값을 그대로 쓰면 통수능이 늘 설계유량에 못 미쳐 판정이 무의미해진다. n=0.017·S=10%는 실무 수리계산서 역산값 (울진1 물넘이·관 시트가 둘 다 S=0.10에서 검산 일치) - 폼에 월류 폭 한 칸 + 읽기전용 요약(설계유량·필요 수심·필요 단면·유속). 설계유량은 유역 payload에서 폼까지 배선(onPipesChanged → Page → setFacility) ⚠ 실무 시트의 수심 0.27m는 Qd 역산값이 아니라 가정 수심이다(B=20·h=0.27의 통수능 41.2㎥/s ≫ 그 시트 Qd 2.54㎥/s). 실무는 단면을 가정해 통수능을 검증하고, 프로그램은 필요 최소 수심을 낸다 — 화면 문구도 "필요 수심"으로 적었다. 검증: pytest tmp/tests 124 passed·7 skipped(신규 17건), npm run typecheck 무오류, npm run build 성공. 물넘이 산식은 프론트 전용이라 수치 손검증으로 대신했다 — Qd=2.54·B=20에서 필요 수심 0.0503m·유속 2.526m/s, 통수능이 2.5400㎥/s로 Qd와 일치. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -26,6 +26,7 @@ from B04_PreProcess.B04_PreProcess_Engine_Watershed_Export import drainage_dir
|
||||
from common_util.common_util_drainage_context import DrainageContext, load_drainage_context
|
||||
from common_util.common_util_drainage_detail import DrainageDetail, build_detail
|
||||
from common_util.common_util_drainage_pipes import (
|
||||
PIPE_FACILITY_PIPE,
|
||||
PIPE_SOURCE_USER,
|
||||
PipePoint,
|
||||
carry_facility_attributes,
|
||||
@@ -64,7 +65,10 @@ def _build(
|
||||
rebuilt = [PipePoint(chainage_m=pipe.chainage_m, source=pipe.reason) for pipe in detail.pipes]
|
||||
# 계산기는 chainage만 다뤄 시설 종류·구간·옵션이 사라진다 — 원본에서 되붙인다
|
||||
# (2026-08-17 계곡 통과 시설: 배관/BOX암거/물넘이/세월교).
|
||||
return detail, carry_facility_attributes(rebuilt, points or [])
|
||||
carried = carry_facility_attributes(rebuilt, points or [])
|
||||
# 되붙인 뒤에 추천을 얹는다 — 순서가 뒤바뀌면 승계된 사용자 값이 추천에 덮인다.
|
||||
_apply_recommendations(detail, carried)
|
||||
return detail, carried
|
||||
|
||||
|
||||
def _retag(pipes: list[StructureCandidate], points: list[PipePoint]) -> None:
|
||||
@@ -78,6 +82,39 @@ def _retag(pipes: list[StructureCandidate], points: list[PipePoint]) -> None:
|
||||
pipe.reason = source or PIPE_SOURCE_USER
|
||||
|
||||
|
||||
def _apply_recommendations(detail: DrainageDetail, points: list[PipePoint]) -> None:
|
||||
"""유역별 추천(시설 종류·관경)을 관 지점 옵션의 **빈칸에만** 채운다.
|
||||
|
||||
"초기 전처리 계산에서 해당값으로 설정"(2026-08-17 사용자 지시) — 자동 배치된 관이
|
||||
유역 유량에 맞는 규격을 처음부터 갖고 있어야 폼을 열지 않아도 하류가 값을 받는다.
|
||||
|
||||
덮어쓰지 않는 두 경우: ① 사용자가 직접 놓거나 옮긴 관(`source == "user"`) ②
|
||||
그 옵션 키가 이미 있는 관. 설계자가 고른 값을 재계산이 되돌리면 안 되기 때문이다.
|
||||
"""
|
||||
by_chainage = {round(basin.chainage_m, 2): basin for basin in detail.basins}
|
||||
for point in points:
|
||||
if point.source == PIPE_SOURCE_USER:
|
||||
continue
|
||||
basin = by_chainage.get(round(point.chainage_m, 2))
|
||||
if basin is None:
|
||||
continue
|
||||
options = dict(point.options or {})
|
||||
# 시설 종류는 아직 손대지 않은 기본 배관일 때만 추천으로 바꾼다.
|
||||
if (
|
||||
point.facility == PIPE_FACILITY_PIPE
|
||||
and basin.recommended_facility != PIPE_FACILITY_PIPE
|
||||
):
|
||||
point.facility = basin.recommended_facility
|
||||
if (
|
||||
point.facility == PIPE_FACILITY_PIPE
|
||||
and basin.recommended_diameter_mm is not None
|
||||
and "pipe_diameter_mm" not in options
|
||||
):
|
||||
options["pipe_diameter_mm"] = basin.recommended_diameter_mm
|
||||
if options:
|
||||
point.options = options
|
||||
|
||||
|
||||
def _payload(
|
||||
project_id: UUID,
|
||||
context: DrainageContext,
|
||||
@@ -136,6 +173,11 @@ def _payload(
|
||||
"design_flow_m3s": basin.design_flow_m3s,
|
||||
# 유효직경이 관 최대 규격 초과 — 세월교·물넘이·교량 검토 대상(임도설치규정 제12조).
|
||||
"bridge_required": basin.bridge_required,
|
||||
# 필요 통수단면적(㎡) — 물넘이·세월교 개략 단면의 출발값.
|
||||
"required_area_m2": basin.required_area_m2,
|
||||
# 유량 근거 추천(2026-08-17 사용자 확정) — 배관이면 규격 스냅 관경이 붙는다.
|
||||
"recommended_facility": basin.recommended_facility,
|
||||
"recommended_diameter_mm": basin.recommended_diameter_mm,
|
||||
}
|
||||
for basin in detail.basins
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user