From 9db4b4de336d806fd3154e724cc5a82afecb2621 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 26 Oct 2026 05:04:56 +0900 Subject: [PATCH] =?UTF-8?q?feat(E05):=20=EB=B3=B4=EA=B3=A0=EC=84=9C=20?= =?UTF-8?q?=E3=80=8C=ED=99=95=EB=A5=A0=ED=95=B4=EC=84=9D=E3=80=8D=20?= =?UTF-8?q?=EC=8B=9C=ED=8A=B8(=EC=9E=88=EC=9D=84=20=EB=95=8C=EB=A7=8C)=20?= =?UTF-8?q?=E2=80=94=20=EC=B8=A1=EC=A0=90=20=C2=B7=20=EB=B9=84=ED=83=88=20?= =?UTF-8?q?=C2=B7=20=EC=A1=B0=EA=B1=B4=EB=A7=88=EB=8B=A4=20=EA=B2=B0?= =?UTF-8?q?=EC=A0=95=EB=A1=A0=20Fs=20=C2=B7=20=ED=8F=89=EA=B7=A0=20=C2=B7?= =?UTF-8?q?=20=CF=83=20=C2=B7=20Pf=20=C2=B7=20=CE=B2=20=C2=B7=20P(F --- E05_StabReport/E05_StabReport_Build.py | 13 +- E05_StabReport/E05_StabReport_Print.py | 1 + E05_StabReport/E05_StabReport_Prob.py | 145 ++++++ .../master_template/report/stab_report.json | 446 ++++++++++++++++++ .../report/stab_report_slots.md | 14 +- .../tester/test_534_3_stab_report_form.py | 9 +- resources/tester/test_537_1_e03_prob_route.py | 95 ++++ 7 files changed, 714 insertions(+), 9 deletions(-) create mode 100644 E05_StabReport/E05_StabReport_Prob.py diff --git a/E05_StabReport/E05_StabReport_Build.py b/E05_StabReport/E05_StabReport_Build.py index 0105cd5e1..bf0407150 100644 --- a/E05_StabReport/E05_StabReport_Build.py +++ b/E05_StabReport/E05_StabReport_Build.py @@ -3,14 +3,14 @@ 입력 = E02 지반 조건(`ground.json`) · E03 2D 파생 저장본(`stab2d_summary` 가장 최근 · 측점별 `stab2d_station`) · 대표 단면(E03 자동 + `representative.json` 켬/끔) · E04 3D 저장본 요약 (있으면) · E07 광역 저장본(있으면) · E03 보강 검토 저장본(있으면 · 없으면 s8 뺌) · -E06 암반 해석 저장본(있으면 · 없으면 s11 뺌) · +E06 암반 해석 저장본(있으면 · 없으면 s11 뺌) · E03 확률 해석 저장본(있으면 · 없으면 s12 뺌) · E01 복사본(측점 · 횡단 · 노선). 양식 = `resources/master_template/report/stab_report.json` 칸 자리 = 같은 폴더 `stab_report_slots.md`. 몫: 사용자(표지 s1 · 개요 s2) = 처음 만들 때만 채움 · 프로그램(목차 s9 · 입력조건 s3 · 측점표 s4 · 대표단면 s5 · 3D요약 s6 · 광역요약 s7 · 보강검토 s8 · -암반검토 s11 · 부록_절편 s10) = [보고서 갱신] 때 양식에서 통째로 다시 씀. 대표 단면 · 부록 = `_Section` · +암반검토 s11 · 확률해석 s12 · 부록_절편 s10) = [보고서 갱신] 때 양식에서 통째로 다시 씀. 대표 단면 · 부록 = `_Section` · 목차 쪽 번호 · 머리말/꼬리말 · 긴 표 쪽 나눔 = `_Print` · 칸 공용 = `_Cells`. 판정 글 칸 = 엔진 판정 값 + N.G 서식 10 · O.K 서식 11 을 직접 붙임 (인쇄 HTML · xlsx 는 조건부 서식을 안 먹음). @@ -38,6 +38,7 @@ from E04_Stab3D import E04_Stab3D_Run as run3d from E05_StabReport import E05_StabReport_Area as area from E05_StabReport import E05_StabReport_Figure as figure from E05_StabReport import E05_StabReport_Print as printer +from E05_StabReport import E05_StabReport_Prob as prob from E05_StabReport import E05_StabReport_Reinforce as reinforce from E05_StabReport import E05_StabReport_Rock as rock from E05_StabReport import E05_StabReport_Section as section @@ -62,7 +63,7 @@ FORM_PATH = ROOT / "resources" / "master_template" / "report" / "stab_report.jso STAGE = "E05_StabReport" REPORT, META = "report.json", "report_meta.json" USER_SHEETS = ("s1", "s2") -PROGRAM_SHEETS = ("s9", "s3", "s4", "s5", "s6", "s7", "s8", "s11", "s10") +PROGRAM_SHEETS = ("s9", "s3", "s4", "s5", "s6", "s7", "s8", "s11", "s12", "s10") REPRESENTATIVE = Path("E03_StabSection") / "representative.json" BAND_KEY = "fig/3d_band.png" @@ -170,6 +171,7 @@ def analysis_fingerprint(project_root: Path) -> str: "area": area.stamp(project_root), "reinforce": reinforce.stamp(project_root), "rock": rock.stamp(project_root), + "prob": prob.stamp(project_root), "form": hashlib.sha1(FORM_PATH.read_bytes()).hexdigest(), } text = json.dumps(body, ensure_ascii=False, sort_keys=True, default=str) @@ -221,6 +223,7 @@ def gather(project_root: Path) -> dict[str, Any]: "area": area.read(project_root), "reinforce": reinforce.read(project_root), "rock": rock.read(project_root), + "prob": prob.read(project_root), "fingerprint": analysis_fingerprint(project_root), } @@ -453,6 +456,10 @@ def build_program(book: dict[str, Any], inputs: dict[str, Any]) -> list[tuple[st rock.fill_rock(by["s11"], inputs["rock"], _labels(inputs["stations"])) else: # 암반 해석이 없으면 시트를 뺌 book["시트"] = [s for s in book["시트"] if s["id"] != "s11"] + if inputs.get("prob"): + prob.fill_prob(by["s12"], inputs["prob"], _labels(inputs["stations"])) + else: # 확률 해석이 없으면 시트를 뺌 + book["시트"] = [s for s in book["시트"] if s["id"] != "s12"] printer.fill_toc(by["s9"], book, None) # 쪽 번호 = 저장 전 `printer.number_toc` return jobs diff --git a/E05_StabReport/E05_StabReport_Print.py b/E05_StabReport/E05_StabReport_Print.py index ff06e3dbf..d2dfe7603 100644 --- a/E05_StabReport/E05_StabReport_Print.py +++ b/E05_StabReport/E05_StabReport_Print.py @@ -32,6 +32,7 @@ TITLES = { "s7": "광역 자연사면 위험 요약", "s8": "보강 검토 — 비탈 완화 · 소단 · 벽 외적 안정", "s11": "암반 비탈면 검토 — 평사투영 · 평면 · 쐐기 안전율", + "s12": "확률 해석 · 민감도 — 파괴확률 · 신뢰도 지수", "s10": "대표 단면 절편 계산표", } APPENDIX = {"s10"} diff --git a/E05_StabReport/E05_StabReport_Prob.py b/E05_StabReport/E05_StabReport_Prob.py new file mode 100644 index 000000000..4388dce08 --- /dev/null +++ b/E05_StabReport/E05_StabReport_Prob.py @@ -0,0 +1,145 @@ +"""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 diff --git a/resources/master_template/report/stab_report.json b/resources/master_template/report/stab_report.json index 1b9d49ef2..1136f7c7d 100644 --- a/resources/master_template/report/stab_report.json +++ b/resources/master_template/report/stab_report.json @@ -4133,6 +4133,452 @@ } } }, + { + "id": "s12", + "이름": "확률해석", + "칸": { + "A1": { + "서식": 1, + "값": "확률 해석 · 민감도 (대표 단면)" + }, + "B1": { + "서식": 1 + }, + "C1": { + "서식": 1 + }, + "D1": { + "서식": 1 + }, + "E1": { + "서식": 1 + }, + "F1": { + "서식": 1 + }, + "G1": { + "서식": 1 + }, + "H1": { + "서식": 1 + }, + "I1": { + "서식": 1 + }, + "J1": { + "서식": 1 + }, + "K1": { + "서식": 1 + }, + "A2": { + "서식": 2, + "값": "물성 = 확률 변수(문헌 추정 변동계수) · Pf = P(Fs < 1) · β 정규 = (μ − 1)/σ · β 대수정규 = ln(μ/√(1+V²))/√ln(1+V²) — Slide2 · SLOPE/W Probabilistic 과 같은 정의" + }, + "B2": { + "서식": 2 + }, + "C2": { + "서식": 2 + }, + "D2": { + "서식": 2 + }, + "E2": { + "서식": 2 + }, + "F2": { + "서식": 2 + }, + "G2": { + "서식": 2 + }, + "H2": { + "서식": 2 + }, + "I2": { + "서식": 2 + }, + "J2": { + "서식": 2 + }, + "K2": { + "서식": 2 + }, + "A3": { + "서식": 12 + }, + "B3": { + "서식": 12 + }, + "C3": { + "서식": 12 + }, + "D3": { + "서식": 12 + }, + "E3": { + "서식": 12 + }, + "F3": { + "서식": 12 + }, + "G3": { + "서식": 12 + }, + "H3": { + "서식": 12 + }, + "I3": { + "서식": 12 + }, + "J3": { + "서식": 12 + }, + "K3": { + "서식": 12 + }, + "A4": { + "서식": 3, + "값": "1. 측점별 결과 — 안전율 분포 · 파괴확률 · 신뢰도 지수" + }, + "B4": { + "서식": 3 + }, + "C4": { + "서식": 3 + }, + "D4": { + "서식": 3 + }, + "E4": { + "서식": 3 + }, + "F4": { + "서식": 3 + }, + "G4": { + "서식": 3 + }, + "H4": { + "서식": 3 + }, + "I4": { + "서식": 3 + }, + "J4": { + "서식": 3 + }, + "K4": { + "서식": 3 + }, + "A5": { + "서식": 6, + "값": "측점" + }, + "B5": { + "서식": 6, + "값": "비탈 · 조건" + }, + "C5": { + "서식": 6, + "값": "방법 · 임계면" + }, + "D5": { + "서식": 6, + "값": "표본" + }, + "E5": { + "서식": 6, + "값": "결정론 Fs" + }, + "F5": { + "서식": 6, + "값": "Fs 평균" + }, + "G5": { + "서식": 6, + "값": "표준편차" + }, + "H5": { + "서식": 6, + "값": "Pf %" + }, + "I5": { + "서식": 6, + "값": "β 정규" + }, + "J5": { + "서식": 6, + "값": "β 대수정규" + }, + "K5": { + "서식": 6, + "값": "P(F<기준) %" + }, + "A6": { + "서식": 7 + }, + "B6": { + "서식": 7 + }, + "C6": { + "서식": 5 + }, + "D6": { + "서식": 9 + }, + "E6": { + "서식": 8 + }, + "F6": { + "서식": 8 + }, + "G6": { + "서식": 8 + }, + "H6": { + "서식": 8 + }, + "I6": { + "서식": 8 + }, + "J6": { + "서식": 8 + }, + "K6": { + "서식": 8 + }, + "A8": { + "서식": 3, + "값": "2. 확률 변수 · 민감도 — 토네이도 순(나머지 평균 · 임계면 고정 · 범위 끝 Fs)" + }, + "B8": { + "서식": 3 + }, + "C8": { + "서식": 3 + }, + "D8": { + "서식": 3 + }, + "E8": { + "서식": 3 + }, + "F8": { + "서식": 3 + }, + "G8": { + "서식": 3 + }, + "H8": { + "서식": 3 + }, + "I8": { + "서식": 3 + }, + "J8": { + "서식": 3 + }, + "K8": { + "서식": 3 + }, + "A9": { + "서식": 6, + "값": "측점 · 비탈 · 조건" + }, + "B9": { + "서식": 6, + "값": "변수" + }, + "C9": { + "서식": 6, + "값": "분포" + }, + "D9": { + "서식": 6, + "값": "평균" + }, + "E9": { + "서식": 6, + "값": "표준편차" + }, + "F9": { + "서식": 6, + "값": "절단 ±σ" + }, + "G9": { + "서식": 6, + "값": "아래 끝" + }, + "H9": { + "서식": 6, + "값": "Fs(아래)" + }, + "I9": { + "서식": 6, + "값": "위 끝" + }, + "J9": { + "서식": 6, + "값": "Fs(위)" + }, + "K9": { + "서식": 6, + "값": "순위" + }, + "A10": { + "서식": 7 + }, + "B10": { + "서식": 5 + }, + "C10": { + "서식": 7 + }, + "D10": { + "서식": 8 + }, + "E10": { + "서식": 8 + }, + "F10": { + "서식": 16 + }, + "G10": { + "서식": 8 + }, + "H10": { + "서식": 8 + }, + "I10": { + "서식": 8 + }, + "J10": { + "서식": 8 + }, + "K10": { + "서식": 9 + }, + "A12": { + "서식": 5, + "값": "확률 해석 없음 — E03 [확률 · 민감도] 뒤 [보고서 갱신]" + }, + "B12": { + "서식": 5 + }, + "C12": { + "서식": 5 + }, + "D12": { + "서식": 5 + }, + "E12": { + "서식": 5 + }, + "F12": { + "서식": 5 + }, + "G12": { + "서식": 5 + }, + "H12": { + "서식": 5 + }, + "I12": { + "서식": 5 + }, + "J12": { + "서식": 5 + }, + "K12": { + "서식": 5 + } + }, + "병합": [ + "A1:K1", + "A2:K2", + "A3:K3", + "A4:K4", + "A8:K8", + "A12:K12" + ], + "열": { + "A": { + "폭": 13 + }, + "B": { + "폭": 16 + }, + "C": { + "폭": 18 + }, + "D": { + "폭": 7 + }, + "E": { + "폭": 8 + }, + "F": { + "폭": 8 + }, + "G": { + "폭": 8 + }, + "H": { + "폭": 8 + }, + "I": { + "폭": 8 + }, + "J": { + "폭": 8 + }, + "K": { + "폭": 8 + } + }, + "행": { + "1": { + "높이": 36 + }, + "2": { + "높이": 30 + }, + "3": { + "높이": 22 + }, + "4": { + "높이": 22 + }, + "5": { + "높이": 30 + }, + "8": { + "높이": 22 + }, + "9": { + "높이": 30 + } + }, + "보기": { + "눈금선": false + }, + "인쇄": { + "용지": "A4", + "방향": "landscape", + "맞춤": { + "폭": 1, + "높이": 0 + }, + "여백": { + "위": 15, + "아래": 15, + "왼": 10, + "오른": 10 + } + } + }, { "id": "s10", "이름": "부록_절편", diff --git a/resources/master_template/report/stab_report_slots.md b/resources/master_template/report/stab_report_slots.md index b566cf62d..47d2cf4d9 100644 --- a/resources/master_template/report/stab_report_slots.md +++ b/resources/master_template/report/stab_report_slots.md @@ -4,8 +4,8 @@ ## 공통 -- 몫: 사용자 몫(표지 · 개요) = 처음 만들 때만 채움 · 다시 만들 때(보고서 갱신) 안 건드림. 프로그램 몫(목차 · 입력조건 · 측점표 · 대표단면 · 3D요약 · 광역요약 · 보강검토 · 암반검토 · 부록_절편) = 갱신 때 통째 다시 씀 · 작업본에 없는 프로그램 몫 시트는 양식 차례 자리에 끼움 · 새 서식은 작업본 서식 표 끝에 붙여 번호를 바꿈. -- 시트 차례: 표지 · 목차 · 개요 · 입력조건 · 측점표 · 대표단면 · 3D요약 · 광역요약 · (보강검토) · (암반검토) · 부록_절편. +- 몫: 사용자 몫(표지 · 개요) = 처음 만들 때만 채움 · 다시 만들 때(보고서 갱신) 안 건드림. 프로그램 몫(목차 · 입력조건 · 측점표 · 대표단면 · 3D요약 · 광역요약 · 보강검토 · 암반검토 · 확률해석 · 부록_절편) = 갱신 때 통째 다시 씀 · 작업본에 없는 프로그램 몫 시트는 양식 차례 자리에 끼움 · 새 서식은 작업본 서식 표 끝에 붙여 번호를 바꿈. +- 시트 차례: 표지 · 목차 · 개요 · 입력조건 · 측점표 · 대표단면 · 3D요약 · 광역요약 · (보강검토) · (암반검토) · (확률해석) · 부록_절편. - PDF: 표지 = 쪽 번호 · 머리말 없음 · 본문 = 머리말(왼 사업명 = 표지 `C8` · 오른 보고서 이름) · 꼬리말 「- n / N -」(목차 = 1 쪽). - 사용자 확정 전 표기 한 규칙 — 기본값 그대로 = 「(기본값 · 사용자 확정 전)」 · 바꿈 = 「(사용자 입력)」 · 물성 = 「문헌 추정치 · 사용자 확정 전」. - 서식 번호(`서식` 표): 1 title · 2 sub · 3 section · 4 label · 5 text · 6 head · 7 body(가운데 글) · 8 fs(0.00) · 9 int(0) · 10 ng(N.G 빨강 채움) · 11 ok(O.K 초록) · 12 notice · 13 num1(#,##0.0) · 14 num0(#,##0) · 15 para(위 맞춤 긴 글) · 16 prop(0.0) · 17 chain(#,##0.00) · 18 head8 · 19 num8(0.00) · 20 num8(0.0) · 21 int8 · 22 목차 내용 · 23 목차 쪽 · 24 목차 장 · 25 sum8(0.00 · 회색) · 26 표지 제목. @@ -126,6 +126,16 @@ | 11 · 12 행 꼴(제목 3 · 머리 6) 다음 절리군 하나 = 한 줄(13 행 서식) | A 이름(7) · B 경사방향 · C 경사 · D φj · E cj(16) · F 지속성(13) · G · H 구간 시작 · 끝(7) · 모르면 「-」 · 없으면 「절리군 없음」 | | 절리 줄 다음 한 줄 띄고 15 · 16 행 꼴 · 그 아래 측점 비탈 × 위험 모드 × 조건 = 한 줄(17 행 서식) | A 측점(7) · B 비탈 글(쪽 · 경사 / 방향 · 높이)(5) · C 파괴 형태(평면 · 쐐기 · 전도 + 절리)(7) · D 조건(7) · E Fs · F 기준(8) · G 판정(7 → 10 · 11 · 전도 「검토」) · H 비고(블록 안 생김 · 미끄러짐 면 · 지속성)(5) · 위험 모드 없는 비탈 = 「없음」 한 줄 | +## s12 확률해석 (프로그램 몫 · E03 확률 해석 저장본 · 없으면 시트를 뺌 · 가로 쪽) + +| 칸 | 내용 | +| --- | --- | +| `A3` (A:K 병합 · 서식 12) | 「변동계수 · 분포 · 표본 = 문헌 기본값 후보(근거 E03 탭)」 + 사용자 확정 전 표기(모든 기록이 `prob_defaults.json` 그대로면 기본값 · 아니면 사용자 입력) | +| 4 · 5 행 꼴(제목 3 · 머리 6) 다음 (측점 · 비탈 · 조건)마다 한 줄(6 행 서식) | A 측점(7) · B 비탈 · 조건(7) · C 방법 · 임계면 고정 / 매회 탐색(5) · D 유효 표본(9) · E 결정론 Fs · F 평균 · G 표준편차 · H Pf % · I β 정규 · J β 대수정규 · K P(F < 기준) %(8) | +| 한 줄 띄고 8 · 9 행 꼴 · 그 아래 기록마다 변수 = 한 줄(10 행 서식 · 토네이도 순) | A 측점 · 비탈 · 조건(7) · B 변수(5) · C 분포(7) · D 평균 · E 표준편차(8) · F 절단 ±σ(16 · 없으면 「-」) · G 아래 끝 · H 그때 Fs · I 위 끝 · J 그때 Fs(8) · K 순위(9) | + +- 저장본 = (측점 · 비탈 · 조건)마다 가장 최근 하나 · 지금 2D 결과 측점 지문에 붙은 것만. + ## s10 부록_절편 (프로그램 몫 · 대표 단면마다 한 쪽) 양식 1~5 행 = 덩이 꼴(1 제목 · 2 단위 · 3 머리 · 4 절편 줄 서식 · 5 합계 서식). 대표 단면마다 지배 조건(Fs / 기준 가장 낮음) 임계면의 절편표. diff --git a/resources/tester/test_534_3_stab_report_form.py b/resources/tester/test_534_3_stab_report_form.py index 4a4bb2c51..cc625b2bc 100644 --- a/resources/tester/test_534_3_stab_report_form.py +++ b/resources/tester/test_534_3_stab_report_form.py @@ -38,7 +38,7 @@ PRINT_KEYS = {"용지", "방향", "배율", "맞춤", "여백", "머리행", " A1 = re.compile(r"^([A-Z]{1,3})(\d+)$") NAMES = [ "표지", "목차", "개요", "입력조건", "측점표", "대표단면", "3D요약", "광역요약", "보강검토", "암반검토", - "부록_절편", + "확률해석", "부록_절편", ] # fmt: skip needs_node = pytest.mark.skipif(shutil.which("node") is None, reason="node 가 없음") @@ -151,7 +151,7 @@ def test_양식_계약(): book = _form() _contract(book) assert [s["이름"] for s in book["시트"]] == NAMES - ids = ["s1", "s9", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s11", "s10"] + ids = ["s1", "s9", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s11", "s12", "s10"] assert [s["id"] for s in book["시트"]] == ids @@ -175,6 +175,7 @@ def test_양식_인쇄_쪽나눔_머리행_그림(): "광역요약": 0, "보강검토": 0, "암반검토": 0, + "확률해석": 0, } # N.G 빨강 조건부 서식 — 측점표 판정 열 · 서식 표에 N.G 칸 서식(빌더가 인쇄 · xlsx 용으로 직접 씀) assert by["측점표"]["조건부서식"][0]["범위"][0]["c0"] == 9 @@ -239,9 +240,9 @@ def test_견본_pdf_쪽수_머리행(png): sections = [i for i, t in enumerate(texts) if "대표 단면" in t and "안전율 Fs" in t] assert len(sections) == 2 # 단면마다 한 쪽 # 표지 1 + 목차 1 + 개요 1 + 입력조건 1 + 측점표 n + 대표단면 2 + 3D 1 + 광역 1 + 보강 1 + 암반 1 - # + 부록 1 + # + 확률 1 + 부록 1 # (양식 빈 판) - assert len(pages) == 1 + 1 + 1 + 1 + len(stab) + 2 + 1 + 1 + 1 + 1 + 1 + assert len(pages) == 1 + 1 + 1 + 1 + len(stab) + 2 + 1 + 1 + 1 + 1 + 1 + 1 assert all(float(pages[i].mediabox.width) > float(pages[i].mediabox.height) for i in stab) html = spreadsheet.print_htmls([{"book": book, "figures": {k: png for k in figs}}])[0] assert html.count("= 1.47 and 0 not in got["ticks"] + + if __name__ == "__main__": sys.exit(pytest.main([__file__, "-q"]))