Files
Aislo/E05_StabReport/E05_StabReport_Area.py
T

131 lines
5.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""E05 보고서 s7 광역요약 — E07 광역 자연사면 위험 저장본 → 시트 (PLAN 536-2).
칸 자리 = `resources/master_template/report/stab_report_slots.md` s7.
E07 저장본이 없으면 「광역 해석 전」.
"""
from __future__ import annotations
from pathlib import Path
from typing import Any
from E02_StabGround.E02_StabGround_Model import load_defaults
from E05_StabReport.E05_StabReport_Cells import UNCONFIRMED
from E07_StabArea.E07_StabArea_Engine import CLASS_KEYS, LEVEL_KEYS
from E07_StabArea.E07_StabArea_Router import RESULT, AreaRequest, load
LEVEL_NAMES = {"low": "낮음", "mid": "보통", "high": "높음"}
def scenario_text(s: dict[str, Any]) -> str:
"""시나리오 글 — 화면 시나리오 이름과 같은 말."""
if s.get("scenario") == "saturated":
return "최악 — 완전 포화(m = 1 · 강우 극한)"
if s.get("scenario") == "sinmap":
return (
f"표준 — SINMAP 습윤지수 W = min(R·a / (T·sinθ), 1) · "
f"T/R {s['tr_lo']:g}~{s['tr_hi']:g} m"
)
return f"옛 조건({s.get('scenario')}) — E07 에서 [다시 해석]"
def stamp(project_root: Path) -> list[int] | None:
"""해석 지문용 — 저장본 크기 · 고친 때."""
path = Path(project_root) / RESULT
return [path.stat().st_size, path.stat().st_mtime_ns] if path.is_file() else None
def read(project_root: Path) -> dict[str, Any] | None:
"""저장본에서 보고서에 쓰는 것만(격자 배열 뺌)."""
doc = load(project_root)
if not doc:
return None
return {k: doc[k] for k in ("settings", "computed_at", "stations", "summary")}
def _set(sheet: dict[str, Any], a1: str, value: Any, style: int | None = None) -> None:
cell = dict(sheet["칸"].get(a1) or {})
cell["값"] = round(value, 6) if isinstance(value, float) else value
if style is not None:
cell["서식"] = style
sheet["칸"][a1] = cell
UNCONFIRMED_KEYS = ( # (설정 칸, 글) — 기본값 그대로면 사용자 확정 전
("c_root", "뿌리 점착 {c_root:g} kPa"),
("soil_depth_m", "토층 {soil_depth_m:g} m"),
("tr_lo", "T/R {tr_lo:g}~{tr_hi:g} m"),
("level_lo_pct", "측점 문턱 {level_lo_pct:g} % · {level_hi_pct:g} %"),
)
PAIRS = {"tr_lo": "tr_hi", "level_lo_pct": "level_hi_pct"}
def _unconfirmed(s: dict[str, Any]) -> str:
"""기본값 그대로인 설정 · 물성(E02 붕적토 문헌 추정치) — 사용자 확정 전 한 줄."""
fields = AreaRequest.model_fields
same = [
text.format(**s)
for key, text in UNCONFIRMED_KEYS
if all(
k in s and float(s[k]) == float(fields[k].default) for k in (key, PAIRS.get(key, key))
)
]
base = {m["id"]: m for m in load_defaults()["materials"]}.get(
s.get("material_id") or "colluvium"
)
if base and all(
float(s.get(k, -1)) == float(base[k]) for k in ("gamma_t", "gamma_sat", "c", "phi")
):
same.insert(0, f"토층 물성 = {base['name']} 문헌 추정치")
return f"{' · '.join(same)} — {UNCONFIRMED}" if same else "없음(모두 사용자 입력)"
def fill_area(sheet: dict[str, Any], area: dict[str, Any] | None, labels: dict[float, str]) -> None:
"""s7 — 조건 넷 · 등급별 면적 여섯 · 측점 등급별 수 · 보통 · 높음 측점(22 행부터 한 줄씩)."""
if not area:
_set(sheet, "B5", "광역 해석 전 — E07 광역에서 [해석] 뒤 [보고서 갱신]")
return
if "station_levels" not in area["summary"]:
_set(sheet, "B5", "옛 판 저장본 — E07 광역에서 [다시 해석] 뒤 [보고서 갱신]")
return
s, summary = area["settings"], area["summary"]
_set(sheet, "B5", f"중심선 ± {s['band_m']:g} m · 격자 {s['cell_m']:g} m")
_set(
sheet,
"B6",
f"γt {s['gamma_t']:g} · γsat {s['gamma_sat']:g} kN/m³ · c {s['c']:g}~{s['c_hi']:g} kPa · "
f"φ {s['phi']:g}~{s['phi_hi']:g}° · 뿌리 점착 {s['c_root']:g} kPa · "
f"토층 {s['soil_depth_m']:g} m",
)
_set(sheet, "B7", scenario_text(s))
_set(sheet, "B8", str(area["computed_at"]).replace("T", " "))
_set(sheet, "B9", _unconfirmed(s))
for k in range(1, 7):
_set(sheet, f"C{11 + k}", float(summary["areas_m2"][CLASS_KEYS[k]]))
lo, hi = s["level_lo_pct"], s["level_hi_pct"]
bounds = {"low": f"< {lo:g} %", "mid": f"{lo:g} ~ {hi:g} %", "high": f"≥ {hi:g} %"}
for col, text in zip("EFG", ("측점 등급", "SI<1 셀 비율", "측점 수")):
_set(sheet, f"{col}11", text, 6)
for i, key in enumerate(LEVEL_KEYS):
_set(sheet, f"E{12 + i}", LEVEL_NAMES[key], 7)
_set(sheet, f"F{12 + i}", bounds[key], 7)
_set(sheet, f"G{12 + i}", int(summary["station_levels"][key]), 14)
_set(sheet, "A20", f"3. 위험 측점 — 보통 · 높음 (영향 구역 안 SI < 1 셀 비율 {lo:g} % 이상)")
_set(sheet, "E21", "측점 등급 (SI<1 비율)")
rows = [r for r in area["stations"] if r.get("level") in ("mid", "high")]
template = {col: dict(sheet["칸"].get(f"{col}22") or {}) for col in "ABCDEFG"}
if not rows:
_set(sheet, "A22", "위험 측점 없음")
for i, row in enumerate(rows):
r = 22 + i
for col, cell in template.items():
sheet["칸"][f"{col}{r}"] = dict(cell)
ch = float(row["chainage_m"])
_set(sheet, f"A{r}", row.get("label") or labels.get(round(ch, 3)) or f"{ch:.2f}")
_set(sheet, f"B{r}", ch)
_set(sheet, f"C{r}", float(row["fs_min"]))
_set(sheet, f"D{r}", float(row["si_min"]))
_set(sheet, f"E{r}", f"{LEVEL_NAMES[row['level']]} ({row['risk_ratio'] * 100:.1f} %)")
_set(sheet, f"F{r}", float(row["risk_area_m2"]))
_set(sheet, f"G{r}", float(row["slope_max_deg"]))