From d366970e1d408981ab358abc6a2dfd85750882a5 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Wed, 9 Sep 2026 06:44:15 +0900 Subject: [PATCH] =?UTF-8?q?feat(B08):=20=EA=B8=B0=EC=8A=AD=EB=A7=89?= =?UTF-8?q?=EC=9D=B4=20=EC=A0=84=EA=B0=9C=20=E2=80=94=20=ED=98=95=ED=83=9C?= =?UTF-8?q?=EA=B0=80=20=EB=8F=8C=EC=8C=93=EA=B8=B0=EB=A9=B4=20=EB=8F=8C?= =?UTF-8?q?=EC=8C=93=EA=B8=B0=20=EC=8B=9D=20=ED=95=9C=20=EB=B2=8C=EB=A1=9C?= =?UTF-8?q?=20(3=EB=8B=A8=EA=B3=84=20=EC=B2=AB=20=EC=A2=85=EB=A5=98)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 실무 정본이 근거임 — `구조물도/기슭막이/04.구조도(기슭막이).xls` 탭 제목이 「돌기슭막이(H=2.0m, **찰쌓기**, 기초유)」이고 그 안의 계산이 돌쌓기와 **같은 식**임. ⇒ 기슭막이는 새 식이 아니라 **「형태가 돌쌓기면 돌쌓기 식」**. 여기서 다시 짜면 같은 계산이 두 벌이 되어 돌쌓기와 갈림(CLAUDE.md 5장). 형태별 갈래 돌쌓기(찰) → `stone_masonry(..., wet=True)` 12줄 돌쌓기(메) → `stone_masonry(..., wet=False)` 11줄 콘크리트 · 돌망태 · 통나무/목재틀 · 바자 → **식 없음 + 왜 없는지 한 줄** 콘크리트 돌쌓기(13-4)가 아니라 콘크리트 구조물 — 벽 두께·저판이 정본에 없음 돌망태 품셈 13-8, 규격 축이 망태 치수라 돌쌓기 표를 못 씀 — 원단위 미확보 통나무틀 품셈 13-13, 밑수가 ㎥당 목공 품 — 각재·판재가 카탈로그에 없음 바자 바자얽기(5-15) 별도 일위대가로 잇기로 확정(2026-09-09 ⑧-4) 형태 미정 → 「형태를 골라야 물량이 섭니다」 ⚠ **뒷길이·돌 종류 칸이 기슭막이 등록부에 없음** — 돌쌓기 식은 그 둘로 계수가 갈리는데 없으면 `_back_length` 가 **조용히 45㎝ 로 돎**. 값이 나오므로 아무도 못 알아채는 자리라 **「돌쌓기 기본값으로 섰음」을 줄로 드러냄.** 칸 신설은 등록부 담당 창 몫. ⚠ 확정 대기가 남은 자리(막자갈 식 · 돌 중량 · 체적/석적 줄)는 **지금 값 그대로** 둠 — `stone_masonry` 를 부르므로 그쪽이 갈리면 기슭막이도 같이 따라감. 자체검증 — 여섯 형태를 다 돌려 봄(찰 12줄 · 메 11줄 · 나머지 0줄 + 사유). 회귀 586 통과 · 0 실패. Co-Authored-By: Claude Opus 5 (1M context) --- .../B08_Quantity_Engine_UnitQuantity.py | 12 +++ ..._Quantity_Engine_UnitQuantity_Revetment.py | 81 +++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 B08_Quantity/B08_Quantity_Engine_UnitQuantity_Revetment.py diff --git a/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py b/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py index d9e20814..90212d20 100644 --- a/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py +++ b/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py @@ -932,6 +932,10 @@ EXPANDERS = { "masonry_dry": lambda h, l, o, f=None, r="": stone_masonry(h, l, o, False, f, r), # 큰돌쌓기는 표준경사 표 대상이 아니라 성절토를 안 쓴다(교본 「1:0.3 이상」). "boulder_masonry": lambda h, l, o, f=None, r="": boulder_masonry(h, l, o), + # 기슭막이는 **형태가 돌쌓기면 돌쌓기 식**이다 — 실무 정본 탭 제목이 「돌기슭막이(…찰쌓기…)」 + # 이고 그 안의 계산이 같다. 갈래·사유는 `_UnitQuantity_Revetment` 가 든다(늦게 부른다 — + # 그쪽이 이 모듈의 `stone_masonry` 를 쓰므로 위에서 부르면 맞물린다). + "revetment": lambda h, l, o, f=None, r="": _revetment()(h, l, o, f, r), # ⚠⚠ **큰돌쌓기(`boulder_masonry`)를 여기에 두지 않는다** (2026-09-07 발견). # 큰돌쌓기는 품셈 **13-6** 이고 돌쌓기는 **13-4** 다 — **규격 축이 다르다.** # 돌쌓기는 **뒷길이**(35·45·55·60㎝), 큰돌쌓기는 **직경**(40~60·60~80·80~100㎝). @@ -941,6 +945,14 @@ EXPANDERS = { # 전개식·관측 원단위가 설 때까지 **미확보로 드러낸다.** } + +def _revetment(): + """기슭막이 전개 함수를 늦게 가져온다 — 서로 부르는 것을 풀기 위한 자리.""" + from B08_Quantity.B08_Quantity_Engine_UnitQuantity_Revetment import revetment + + return revetment + + #: 전개식을 일부러 안 두는 종류 — 왜 안 두는지 사람이 읽게 적는다. EXPANDER_WITHHELD: dict[str, str] = {} diff --git a/B08_Quantity/B08_Quantity_Engine_UnitQuantity_Revetment.py b/B08_Quantity/B08_Quantity_Engine_UnitQuantity_Revetment.py new file mode 100644 index 00000000..3c7bf94a --- /dev/null +++ b/B08_Quantity/B08_Quantity_Engine_UnitQuantity_Revetment.py @@ -0,0 +1,81 @@ +"""기슭막이 전개 — **형태로 갈라 돌쌓기 식을 그대로 쓴다**(3단계, 2026-09-09). + +실무 정본이 근거다 — `구조물도/기슭막이/04.구조도(기슭막이).xls` 의 탭 제목이 +**「돌기슭막이(H=2.0m, 찰쌓기, 기초유)」** 이고, 그 안의 계산이 돌쌓기와 **같은 식**이다. + + 정면적 → 비탈면적(×√(1+n²)) → 평균두께 → 입적 → 돌 · 막자갈 · 고임돌 → + 터파기(기초 + 비탈) → 되메우기 → 잔토 + +⇒ **기슭막이는 새 식이 아니라 「형태가 돌쌓기면 돌쌓기 식」**이다. 여기서 식을 다시 짜면 + 같은 계산이 두 벌이 되어 돌쌓기와 갈린다(CLAUDE.md 5장). + +⚠ **형태가 돌쌓기가 아닌 갈래는 식이 없다** — 콘크리트·돌망태·통나무/목재틀·바자. + 지어내지 않고 **왜 없는지**를 줄로 남긴다. + +⚠ **뒷길이·돌 종류 칸이 기슭막이 등록부에 아직 없다**(2026-09-09 실측). 돌쌓기 식은 그 둘로 + 계수가 갈리는데, 없으면 `_back_length` 가 **조용히 45㎝ 로 돈다**. 값이 나오므로 아무도 + 못 알아채는 자리라 **그 사실을 줄로 드러낸다** — 「값이 있기는 하니 안 보이는」 그것. +""" + +from __future__ import annotations + +from typing import Any + +from B08_Quantity.B08_Quantity_Engine_UnitQuantity import Component, stone_masonry + +#: 형태 → 돌쌓기 갈래(찰이면 True). 여기 없는 형태는 전개식이 없다. +STONE_FORMS: dict[str, bool] = { + "돌쌓기(찰)": True, + "돌쌓기(메)": False, +} + +#: 식이 없는 형태와 **왜 없는지**. 품셈 장이 다르거나 원단위가 원문에 없다. +WITHHELD_FORMS: dict[str, str] = { + "콘크리트": ( + "콘크리트 기슭막이는 돌쌓기(품셈 13-4)가 아니라 콘크리트 구조물이라 전개식이 다름 " + "— 벽 두께·저판이 정본에 없어 물량이 서지 않음" + ), + "돌망태": ("돌망태는 품셈 13-8 이고 규격 축이 망태 치수라 돌쌓기 표를 못 씀 — 원단위 미확보"), + "통나무·목재틀": ( + "목재틀은 품셈 13-13 이고 밑수가 ㎥당 목공 품이라 돌쌓기 표를 못 씀 " + "— 각재·판재 자재가 카탈로그에 없어 값이 모자람" + ), + "바자": "바자얽기(품셈 5-15)는 별도 일위대가를 만들어 잇기로 확정(2026-09-09 ⑧-4)", +} + +#: 돌쌓기 식이 계수를 가르는 데 쓰는 칸 — 기슭막이 등록부에 아직 없는 것을 알린다. +STONE_SPEC_KEYS: tuple[tuple[str, str], ...] = ( + ("back_len_cm", "뒷길이"), + ("stone_kind", "돌 종류"), +) + + +def revetment( + height_m: float, + length_m: float, + options: dict[str, Any], + face: str | None = None, + face_reason: str = "", +) -> tuple[list[Component], list[str]]: + """기슭막이 1구간 전개 — 형태가 돌쌓기면 그 식, 아니면 사유만.""" + form = str(options.get("form") or "").strip() + if form not in STONE_FORMS: + why = WITHHELD_FORMS.get(form) + if why: + return [], [f"기슭막이 형태 「{form}」 — {why}"] + return [], [ + f"기슭막이 형태가 정해지지 않았습니다(지금 「{form or '빈 값'}」) " + "— 형태를 골라야 물량이 섭니다" + ] + + components, notes = stone_masonry( + height_m, length_m, options, STONE_FORMS[form], face, face_reason + ) + # ⚠ 계수를 가르는 칸이 없으면 **기본값으로 조용히 돈다** — 그 사실을 드러낸다. + missing = [label for key, label in STONE_SPEC_KEYS if not options.get(key)] + if missing and components: + notes.append( + f"⚠ 기슭막이 제원에 {' · '.join(missing)} 칸이 없어 **돌쌓기 기본값으로 섰음** " + "— 그 값이 고임돌·야면석·채움콘크리트 계수를 가름" + ) + return components, notes