Files
Aislo/E05_StabReport/E05_StabReport_Prob.py
T
eomsangdonandClaude Opus 5.5 9db4b4de33 feat(E05): 보고서 「확률해석」 시트(있을 때만) — 측점 · 비탈 · 조건마다 결정론 Fs · 평균 · σ · Pf · β · P(F<기준) · 변수 토네이도 순 · 양식 s12 · 자리표 (537-1 · 요구 351)
- 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>
2026-10-26 05:04:56 +09:00

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