- E05_StabReport_Prob(새 파일): E03 확률 저장본(가장 최근) → s12 · 기본값 후보 그대로면 「사용자 확정 전」 · 아니면 「사용자 입력」 - Build: 입력 · 해석 지문 · 프로그램 몫 s12 · 없으면 시트 뺌 · Print 목차 제목 - 양식 stab_report.json s12(가로 · A~K) · stab_report_slots.md s12 · 양식 시험 시트 차례 · 빈 판 쪽 수 +1 - 시험 test_537_1_e03_prob_route: E05 시트 값 = 저장본 · 없으면 뺌 · 화면 계산 TS 요청 = 서버 ProbRequest Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
146 lines
5.9 KiB
Python
146 lines
5.9 KiB
Python
"""E05 보고서 s12 확률해석 — E03 확률 해석 저장본(대표 단면 · 비탈 · 조건마다 가장 최근) → 시트(PLAN 537-1).
|
|
|
|
칸 자리 = `resources/master_template/report/stab_report_slots.md` s12.
|
|
저장본이 없으면 시트를 뺌(있을 때만) — `build_program` 이 책에서 지움.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import copy
|
|
import hashlib
|
|
import json
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
from E03_StabSection.E03_StabSection_Router_Prob import load_defaults, records
|
|
from E05_StabReport.E05_StabReport_Cells import _set, mark
|
|
|
|
CONDITION_NAMES = {"dry": "건기", "wet": "우기", "seismic": "지진"}
|
|
SIDE_NAMES = {"left": "좌", "right": "우"}
|
|
ROLE_NAMES = {"cut": "깎기", "fill": "쌓기"}
|
|
METHOD_NAMES = {
|
|
"ordinary": "Fellenius",
|
|
"bishop": "Bishop",
|
|
"janbu": "Janbu 보정",
|
|
"spencer": "Spencer",
|
|
"morgenstern_price": "M-P",
|
|
}
|
|
MODE_NAMES = {"fixed": "임계면 고정", "search": "매회 탐색"}
|
|
DIST_NAMES = {"normal": "정규", "lognormal": "대수정규"}
|
|
COLS = "ABCDEFGHIJK"
|
|
|
|
|
|
def read(project_root: Path) -> list[dict[str, Any]]:
|
|
return records(Path(project_root))
|
|
|
|
|
|
def stamp(project_root: Path) -> str:
|
|
"""해석 지문용 — 저장본 목록 내용(표본 F 뺌)."""
|
|
body = [
|
|
{**r, "result": {k: v for k, v in r["result"].items() if k != "F"}}
|
|
for r in read(project_root)
|
|
]
|
|
text = json.dumps(body, ensure_ascii=False, sort_keys=True, default=str)
|
|
return hashlib.sha1(text.encode("utf-8")).hexdigest()
|
|
|
|
|
|
def _is_default(rec: dict[str, Any], defaults: dict[str, Any]) -> bool:
|
|
"""요청 변수 · 표본 설정이 기본값 후보 그대로인지(「사용자 확정 전」 표기)."""
|
|
req = rec["request"]
|
|
props, own = defaults["properties"], defaults.get("materials") or {}
|
|
for v in req["variables"]:
|
|
base = {
|
|
**props[v["prop"]],
|
|
**((own.get(v.get("material") or "") or {}).get(v["prop"]) or {}),
|
|
}
|
|
spread = v.get("cov") if v.get("cov") is not None else v.get("std")
|
|
want = base.get("cov") if "cov" in base else base.get("std")
|
|
if (
|
|
v["dist"] != base["dist"]
|
|
or spread != want
|
|
or v.get("trunc_sigma") != base["trunc_sigma"]
|
|
):
|
|
return False
|
|
run = defaults["run"]
|
|
same = all(req.get(k) == run[k] for k in ("n", "sampling", "seed", "mode", "method"))
|
|
return same and req.get("rho_c_phi", 0.0) == defaults["rho_c_phi"]
|
|
|
|
|
|
def fill_prob(sheet: dict[str, Any], recs: list[dict[str, Any]], labels: dict) -> None:
|
|
"""s12 — 1. 측점 · 비탈 · 조건마다 한 줄(6 행 서식) · 2. 변수마다 한 줄(10 행 서식 · 토네이도 순)."""
|
|
tpl = {
|
|
r: {c: copy.deepcopy(sheet["칸"].get(f"{c}{r}") or {}) for c in COLS} for r in (6, 8, 9, 10)
|
|
}
|
|
for a1 in [k for k in sheet["칸"] if int("".join(ch for ch in k if ch.isdigit())) >= 6]:
|
|
del sheet["칸"][a1]
|
|
sheet["병합"] = [
|
|
m for m in sheet["병합"] if int("".join(ch for ch in m.split(":")[0] if ch.isdigit())) < 6
|
|
]
|
|
sheet["행"] = {k: v for k, v in sheet["행"].items() if int(k) < 6}
|
|
|
|
def put_row(r: int, styles: dict[str, dict]) -> None:
|
|
for c, cell in styles.items():
|
|
sheet["칸"][f"{c}{r}"] = copy.deepcopy(cell)
|
|
|
|
def name(rec: dict[str, Any]) -> str:
|
|
ch = float(rec["chainage_m"])
|
|
return rec.get("label") or labels.get(round(ch, 3)) or f"{ch:.2f}"
|
|
|
|
def where(rec: dict[str, Any]) -> str:
|
|
return (
|
|
f"{SIDE_NAMES[rec['side']]} {ROLE_NAMES.get(rec['role'], rec['role'])} · "
|
|
f"{CONDITION_NAMES.get(rec['condition'], rec['condition'])}"
|
|
)
|
|
|
|
defaults = load_defaults()
|
|
same = all(_is_default(rec, defaults) for rec in recs)
|
|
_set(sheet, "A3", mark("변동계수 · 분포 · 표본 = 문헌 기본값 후보(근거 E03 탭)", same))
|
|
pct = lambda v: None if v is None else 100.0 * float(v) # noqa: E731
|
|
r = 6
|
|
for rec in recs:
|
|
res, st = rec["result"], rec["result"]["stats"]
|
|
put_row(r, tpl[6])
|
|
_set(sheet, f"A{r}", name(rec))
|
|
_set(sheet, f"B{r}", where(rec))
|
|
_set(
|
|
sheet,
|
|
f"C{r}",
|
|
f"{METHOD_NAMES.get(res['method'], res['method'])} · {MODE_NAMES[res['mode']]}",
|
|
)
|
|
_set(sheet, f"D{r}", int(st["n_valid"]))
|
|
_set(sheet, f"E{r}", rec.get("fs_deterministic"))
|
|
for c, k in zip("FG", ("mean", "std")):
|
|
_set(sheet, f"{c}{r}", st.get(k))
|
|
_set(sheet, f"H{r}", pct(st.get("pf")))
|
|
_set(sheet, f"I{r}", st.get("beta_normal"))
|
|
_set(sheet, f"J{r}", st.get("beta_lognormal"))
|
|
_set(sheet, f"K{r}", pct(st.get("p_below_required")))
|
|
r += 1
|
|
r += 1
|
|
put_row(r, tpl[8])
|
|
sheet["병합"].append(f"A{r}:K{r}")
|
|
sheet["행"][str(r)] = {"높이": 22}
|
|
sheet["행"][str(r + 1)] = {"높이": 30}
|
|
put_row(r + 1, tpl[9])
|
|
r += 2
|
|
for rec in recs:
|
|
res = rec["result"]
|
|
curves = {c["name"]: c for c in res["sensitivity"]["curves"]}
|
|
rank = {t["name"]: i + 1 for i, t in enumerate(res["sensitivity"]["tornado"])}
|
|
for v in sorted(res["variables"], key=lambda v: rank.get(v["name"], 99)):
|
|
c = curves.get(v["name"]) or {}
|
|
xs = c.get("x") or [None]
|
|
put_row(r, tpl[10])
|
|
_set(sheet, f"A{r}", f"{name(rec)} · {where(rec)}")
|
|
_set(sheet, f"B{r}", v["name"])
|
|
_set(sheet, f"C{r}", DIST_NAMES.get(v["dist"], v["dist"]))
|
|
_set(sheet, f"D{r}", float(v["mean"]))
|
|
_set(sheet, f"E{r}", float(v["std"]))
|
|
_set(sheet, f"F{r}", v["lo"] / v["std"] if v.get("lo") is not None else "-")
|
|
_set(sheet, f"G{r}", xs[0])
|
|
_set(sheet, f"H{r}", c.get("F_lo"))
|
|
_set(sheet, f"I{r}", xs[-1])
|
|
_set(sheet, f"J{r}", c.get("F_hi"))
|
|
_set(sheet, f"K{r}", rank.get(v["name"]))
|
|
r += 1
|