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:
@@ -60,6 +60,8 @@ export interface PipeFacilityItem {
|
||||
end_m?: number;
|
||||
/** 자동 배치 출처(stream/spacing/user) — 배관 유형(계곡부형/보완형) 제안 근거. */
|
||||
source?: PipeSource;
|
||||
/** 담당 유역의 설계유량(㎥/s) — 물넘이·세월교 개략 단면 계산의 입력. */
|
||||
design_flow_m3s?: number | null;
|
||||
/** 부속 옵션(유형·집수정·기슭막이·돌붙임·날개벽·세월교 관 등). */
|
||||
options?: Record<string, string | number>;
|
||||
}
|
||||
@@ -246,8 +248,11 @@ export function createStructuresSection(callbacks: StructuresCallbacks): Structu
|
||||
}
|
||||
|
||||
/** 종류에 맞춰 부속 옵션 서브폼을 켠다(계곡 통과 시설·독립 기슭막이, 값은 인자로). */
|
||||
function syncFacilityForm(options: Record<string, string | number> = {}): void {
|
||||
facilityOptions.setFacility(facilityFormKind(currentType()), options);
|
||||
function syncFacilityForm(
|
||||
options: Record<string, string | number> = {},
|
||||
designFlow: number | null = null,
|
||||
): void {
|
||||
facilityOptions.setFacility(facilityFormKind(currentType()), options, designFlow);
|
||||
}
|
||||
|
||||
/** 타입의 옵션 스키마대로 입력 칸을 다시 그린다 — 상세(detail) 옵션은 B06/B07
|
||||
@@ -385,7 +390,7 @@ export function createStructuresSection(callbacks: StructuresCallbacks): Structu
|
||||
startFields.write(null, step);
|
||||
endFields.write(null, step);
|
||||
memoField.value = "";
|
||||
syncFacilityForm(pipe.options ?? {});
|
||||
syncFacilityForm(pipe.options ?? {}, pipe.design_flow_m3s ?? null);
|
||||
[anchorFields, startFields, endFields].forEach((fields) => fields.clearInvalid());
|
||||
syncPlacementFields();
|
||||
syncButtons();
|
||||
|
||||
Reference in New Issue
Block a user