From d366970e1d408981ab358abc6a2dfd85750882a5 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Wed, 9 Sep 2026 06:44:15 +0900 Subject: [PATCH 1/6] =?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 From d6a64700fc32b22ddcd85f757d0d4634d5a61d73 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Wed, 9 Sep 2026 08:25:58 +0900 Subject: [PATCH 2/6] =?UTF-8?q?feat(B08):=20=EA=B3=A8=EB=A7=89=EC=9D=B4?= =?UTF-8?q?=C2=B7=EB=B0=94=EB=8B=A5=EB=A7=89=EC=9D=B4=20=E2=80=94=20?= =?UTF-8?q?=EC=8B=9D=EC=9D=84=20=EC=95=88=20=EB=91=90=EA=B3=A0=20**?= =?UTF-8?q?=EC=99=9C=20=EC=97=86=EB=8A=94=EC=A7=80**=EB=A5=BC=20=EB=83=84?= =?UTF-8?q?=20(3=EB=8B=A8=EA=B3=84=20=EB=91=98=EC=A7=B8=C2=B7=EC=85=8B?= =?UTF-8?q?=EC=A7=B8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ⚠⚠ 조사 중 큰 것이 나옴 — **우리가 「틀렸다」고 버린 옛 두께식이 실은 「골막이 식」이었음.** 소광리 숨김탭 「골막이(찰)(치수조서연결)」 원문: ``` C26 평 균 두 께 = {( 0.45 + 0.1 × H ) + ( 0.45 + 0.4 × H )} ÷ 2 ``` 우리 옛 식과 글자까지 같음. ⇒ **실무는 구조물 종류마다 두께식이 다름.** 확정 ②(뒷길이 기반)는 **기슭막이에 맞고**, 골막이에는 이 식이 맞음. ⇒ 골막이에 `stone_masonry` 를 그대로 부르면 **틀어짐.** 그래서 안 부름. 골막이가 돌쌓기와 다른 자리 셋 (정본 실측) · 두께식 위와 같음 · 밑수가 **「돌쌓기 + 돌붙임」** (19.62 + 2.625 = 22.245) — 기슭막이는 돌쌓기만 · 단위가 **개소당** — 기슭막이는 m당 · 정면적이 사다리꼴 ((상장+하장)÷2×높이) − 파형강관(0.4×0.4×3.14) ⚠ **상장·하장 칸이 등록부에 없어** 정면적을 못 냄 — 칸 신설이 선행 바닥막이는 형태가 **돌붙임**(품셈 13-5)이라 돌쌓기(13-4) 표를 못 씀. 밑수도 면적(㎡)이라 축이 다름 — 돌붙임 원단위 미확보. ⚠ **일부러 안 부름** — 돌 계열이라 `stone_masonry` 를 부르면 **값이 나오므로 아무 시험도 안 잡음.** 오늘 큰돌쌓기에서 같은 일이 있었음(직경 60~80㎝ 가 뒷길이 45㎝ 계수로 돌던 자리). 자체검증 — 골막이 0줄 + 사유 · 바닥막이 0줄 + 사유 · 기슭막이 13줄(그대로). 회귀 586 통과 · 0 실패. Co-Authored-By: Claude Opus 5 (1M context) --- .../B08_Quantity_Engine_UnitQuantity.py | 10 ++++++++ ..._Quantity_Engine_UnitQuantity_Revetment.py | 25 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py b/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py index 90212d20..f0c9b6ab 100644 --- a/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py +++ b/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py @@ -936,6 +936,9 @@ EXPANDERS = { # 이고 그 안의 계산이 같다. 갈래·사유는 `_UnitQuantity_Revetment` 가 든다(늦게 부른다 — # 그쪽이 이 모듈의 `stone_masonry` 를 쓰므로 위에서 부르면 맞물린다). "revetment": lambda h, l, o, f=None, r="": _revetment()(h, l, o, f, r), + # 골막이·바닥막이는 돌 계열이나 **돌쌓기 식을 그대로 못 쓴다** — 까닭은 그 모듈에 적었다. + "erosion_check": lambda h, l, o, f=None, r="": _stone_kin()("erosion_check"), + "bed_sill": lambda h, l, o, f=None, r="": _stone_kin()("bed_sill"), # ⚠⚠ **큰돌쌓기(`boulder_masonry`)를 여기에 두지 않는다** (2026-09-07 발견). # 큰돌쌓기는 품셈 **13-6** 이고 돌쌓기는 **13-4** 다 — **규격 축이 다르다.** # 돌쌓기는 **뒷길이**(35·45·55·60㎝), 큰돌쌓기는 **직경**(40~60·60~80·80~100㎝). @@ -946,6 +949,13 @@ EXPANDERS = { } +def _stone_kin(): + """골막이·바닥막이 사유 함수 — 늦게 가져온다(서로 부르는 것을 푸는 자리).""" + from B08_Quantity.B08_Quantity_Engine_UnitQuantity_Revetment import stone_kin_withheld + + return stone_kin_withheld + + def _revetment(): """기슭막이 전개 함수를 늦게 가져온다 — 서로 부르는 것을 풀기 위한 자리.""" from B08_Quantity.B08_Quantity_Engine_UnitQuantity_Revetment import revetment diff --git a/B08_Quantity/B08_Quantity_Engine_UnitQuantity_Revetment.py b/B08_Quantity/B08_Quantity_Engine_UnitQuantity_Revetment.py index 3c7bf94a..be151028 100644 --- a/B08_Quantity/B08_Quantity_Engine_UnitQuantity_Revetment.py +++ b/B08_Quantity/B08_Quantity_Engine_UnitQuantity_Revetment.py @@ -43,6 +43,20 @@ WITHHELD_FORMS: dict[str, str] = { "바자": "바자얽기(품셈 5-15)는 별도 일위대가를 만들어 잇기로 확정(2026-09-09 ⑧-4)", } +#: 골막이·바닥막이 — **왜 아직 안 서는지**. 둘 다 돌 계열이나 돌쌓기 식을 그대로 못 쓴다. +#: ⚠ 근거 없는 식을 만들지 않는다(2026-09-09 확인). 칸·표가 오면 그때 붙인다. +STONE_KIN_WITHHELD: dict[str, str] = { + "erosion_check": ( + "골막이는 돌쌓기와 **두께식·밑수·단위가 다름**(소광리 정본 「골막이(찰)(치수조서연결)」 " + "실측) — 평균두께 {(0.45+0.1H)+(0.45+0.4H)}÷2 · 밑수 「돌쌓기+돌붙임」 · 개소당. " + "게다가 정면적이 사다리꼴이라 **상장·하장 칸이 있어야** 셈이 서는데 등록부에 없음" + ), + "bed_sill": ( + "바닥막이는 형태가 **돌붙임**(품셈 13-5)이라 돌쌓기(13-4) 표를 못 씀 — " + "밑수도 면적(㎡)이라 축이 다름. 돌붙임 원단위 미확보" + ), +} + #: 돌쌓기 식이 계수를 가르는 데 쓰는 칸 — 기슭막이 등록부에 아직 없는 것을 알린다. STONE_SPEC_KEYS: tuple[tuple[str, str], ...] = ( ("back_len_cm", "뒷길이"), @@ -79,3 +93,14 @@ def revetment( "— 그 값이 고임돌·야면석·채움콘크리트 계수를 가름" ) return components, notes + + +def stone_kin_withheld(type_id: str) -> tuple[list[Component], list[str]]: + """골막이·바닥막이 — **식을 안 두는 까닭**만 낸다. + + ⚠ 돌 계열이라 `stone_masonry` 를 부르고 싶어지는 자리다. 실제로 부르면 **값이 나오므로** + 아무 시험도 안 잡는다 — 오늘 큰돌쌓기에서 같은 일이 있었다(직경 60~80㎝ 가 「뒷길이 + 45㎝」 계수로 돌던 자리). 그래서 **일부러 안 부른다.** + """ + why = STONE_KIN_WITHHELD.get(type_id) + return [], [why] if why else [] From 60b7196754b4b8cb5cbc2034821b2841cb034a7d Mon Sep 17 00:00:00 2001 From: umsangdon Date: Wed, 9 Sep 2026 08:27:37 +0900 Subject: [PATCH 3/6] =?UTF-8?q?docs(B08):=20=EA=B3=A8=EB=A7=89=EC=9D=B4?= =?UTF-8?q?=EB=A5=BC=20=EB=8F=8C=EC=8C=93=EA=B8=B0=EC=99=80=20=ED=95=A9?= =?UTF-8?q?=EC=B9=98=EC=A7=80=20=EB=AA=BB=ED=95=98=EA=B2=8C=20=EB=AA=BB=20?= =?UTF-8?q?=EB=B0=95=EC=9D=8C=20=E2=80=94=20=EB=8B=A4=EB=A5=B8=20=EB=84=B7?= =?UTF-8?q?=C2=B7=EA=B0=99=EC=9D=80=20=EC=97=AC=EC=84=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 「같은 돌쌓기니 한 함수로」가 되기 쉬운 자리라, 두 정본을 나란히 잰 결과를 코드에 남김. 다른 것 넷 (소광리 「골막이(찰)(치수조서연결)」 ↔ 「돌기슭막이」) 두께식 골막이 (0.45+0.1H)/(0.45+0.4H) ↔ 기슭막이 뒷길이 기반(ℓ3+0.30 …) 밑수 골막이 **돌쌓기 + 돌붙임** ↔ 기슭막이 돌쌓기만 단위 골막이 **개소당** ↔ 기슭막이 m당 (확정 ⑦ 「통일 안 함」) 정면적 골막이 **사다리꼴**(상장·하장) ↔ 기슭막이 직사각(H × 1) 같은 것 여섯 — 막자갈 2/3 · 야면석 0.88 ton/㎡ · 물구멍 2㎡/개소 · 기울기 몫 1.04 · 고임돌 0.15 · 채움콘크리트 0.2. **우리 기본값이 어디서 왔는지의 답**이기도 함. `STONE_MASONRY` 상수 머리에도 한 줄 — 「**기슭막이 계열 전용**. 골막이는 두께식이 다름 (정본 C26)」. 상수만 보고 가져다 쓰면 **값이 나오므로 아무 시험도 안 잡는** 자리임. 자체검증 — 회귀 586 통과 · 0 실패. Co-Authored-By: Claude Opus 5 (1M context) --- B08_Quantity/B08_Quantity_Engine_UnitQuantity.py | 4 ++++ .../B08_Quantity_Engine_UnitQuantity_Revetment.py | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py b/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py index f0c9b6ab..03370aff 100644 --- a/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py +++ b/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py @@ -170,6 +170,10 @@ def stone_coefficients(options: dict[str, Any], back_cm: int) -> tuple[dict[str, # 돌쌓기 전개식의 상수 — 실무 수식에 박혀 있던 값을 뺀 것. +#: ⚠⚠ **이 상수는 기슭막이 계열(돌쌓기 찰·메) 전용이다.** +#: 골막이는 두께식이 다르다 — 소광리 정본 「골막이(찰)(치수조서연결)」 C26 이 +#: `{(0.45+0.1H) + (0.45+0.4H)} ÷ 2` 이고, 여기 두께식(뒷길이 기반)과 갈린다. +#: 상수만 보고 골막이에 가져다 쓰면 **값이 나오므로 아무 시험도 안 잡는다**(2026-09-09). STONE_MASONRY = { # ⚠ **곱하는 값이 아니라 검산 참고값이다** (2026-09-08 ㉘ 에서 고침). # 실무 시트의 「돌쌓기 = 정면적 × 1.04」에서 그 1.04 가 **곧 기울기 몫**이다 diff --git a/B08_Quantity/B08_Quantity_Engine_UnitQuantity_Revetment.py b/B08_Quantity/B08_Quantity_Engine_UnitQuantity_Revetment.py index be151028..1ccae6f7 100644 --- a/B08_Quantity/B08_Quantity_Engine_UnitQuantity_Revetment.py +++ b/B08_Quantity/B08_Quantity_Engine_UnitQuantity_Revetment.py @@ -45,6 +45,18 @@ WITHHELD_FORMS: dict[str, str] = { #: 골막이·바닥막이 — **왜 아직 안 서는지**. 둘 다 돌 계열이나 돌쌓기 식을 그대로 못 쓴다. #: ⚠ 근거 없는 식을 만들지 않는다(2026-09-09 확인). 칸·표가 오면 그때 붙인다. +#: +#: ⚠⚠ **골막이를 돌쌓기와 합치지 말 것** — 「같은 돌쌓기니 한 함수로」가 되기 쉬운 자리다. +#: 소광리 정본 「골막이(찰)(치수조서연결)」과 「돌기슭막이」를 나란히 재면 **넷이 다르다**: +#: +#: 두께식 골막이 (0.45+0.1H)/(0.45+0.4H) ↔ 기슭막이 뒷길이 기반(ℓ3+0.30 …) +#: 밑수 골막이 **돌쌓기 + 돌붙임** ↔ 기슭막이 돌쌓기만 +#: 단위 골막이 **개소당** ↔ 기슭막이 m당 (확정 ⑦ 「통일 안 함」) +#: 정면적 골막이 **사다리꼴**(상장·하장) ↔ 기슭막이 직사각(H × 1) +#: +#: ⓘ 같은 것도 있다 — 막자갈 **2/3**·야면석 **0.88 ton/㎡**·물구멍 **2㎡/개소**· +#: 기울기 몫 **1.04**·고임돌 **0.15**·채움콘크리트 **0.2** 는 두 시트가 같은 값이다. +#: 우리 기본값이 어디서 왔는지의 답이기도 하다. STONE_KIN_WITHHELD: dict[str, str] = { "erosion_check": ( "골막이는 돌쌓기와 **두께식·밑수·단위가 다름**(소광리 정본 「골막이(찰)(치수조서연결)」 " From 62f1e252d56bfafcfffc575fc79bd90295315368 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Wed, 9 Sep 2026 08:36:47 +0900 Subject: [PATCH 4/6] =?UTF-8?q?feat(B08):=20=EB=B0=94=EB=8B=A5=EB=A7=89?= =?UTF-8?q?=EC=9D=B4=20=EC=A0=84=EA=B0=9C=20=E2=80=94=20=EB=8F=8C=EB=B6=99?= =?UTF-8?q?=EC=9E=84=20=E3=8E=A1=EB=8B=B9=20=EC=9B=90=EB=8B=A8=EC=9C=84=20?= =?UTF-8?q?(3=EB=8B=A8=EA=B3=84=20=EC=85=8B=EC=A7=B8).=20=EC=95=9E?= =?UTF-8?q?=EC=84=9C=20=EB=82=B8=20=EC=82=AC=EC=9C=A0=EA=B0=80=20=ED=8B=80?= =?UTF-8?q?=EB=A0=B8=EC=9D=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ⚠ **정정** — 어제 「돌붙임 원단위 미확보」로 사유를 냈으나 **소광리 정본에 식이 셋 있었음.** 안 찾아본 것을 「없다」고 적은 자리였음. 53탭 전수에서 드러남. 붙인 원단위 (정본 「돌붙임L3=45(사방)」 · 「돌붙임L3=30(야면석메붙임)」) ``` 돌붙임(찰) 두께 0.45 · 돌 30×30×45㎝ **0.918 ton/㎡**(= 0.45 × 0.77 × 2.65) · 고임돌 0.15 · 모르타르 0.009 · 채움 0.2 + **버림 0.1** · 터파기 0.45 8줄 돌붙임(메) 두께 0.30 · 야면석 20×20×30㎝ **0.42 ton/㎡**(원문 직접값) · 고임돌 0.07 · 터파기 0.30 5줄 ⚠ 모르타르·채움·버림은 **줄 자체가 없음** — 0 으로 만들지 않고 뺌 ``` 밑수는 **면적(㎡)** — 등록부 `area_m2` 와 축이 그대로 맞음. **기울기 몫을 안 곱함**(평면). ⚠⚠ **돌쌓기 표(13-4)와 합치지 못하게 못 박음** — 값이 겹쳐 보여 합치고 싶어지는 자리: ``` 돌쌓기(13-4) 밑수 비탈면적 · 뒷길이 일곱 규격 · 기울기 몫 √(1+n²) 돌붙임(13-5) 밑수 **평면적** · 뒷길이 **넷** · **기울기 몫 없음** · 메는 모르타르·채움 없음 ⚠ 45㎝ 에서 고임돌 0.15·채움 0.20 이 **우연히 같음** — 합치면 25·30·60·75 에서 조용히 틀림 ``` ⓘ **돌중량이 관측값이 아니라 유도값임** — `뒷길이 × 0.77(채움률) × 2.65(비중)`. 두 원본에서 같은 모양이 나왔음. 다만 **야면석은 그 식이 안 맞아**(공극이 커 ㎥당 무게가 다름) 원문 직접값 0.42 를 그대로 옮김. ⇒ 확정 ⑤ 가 「값 고르기」가 아니라 **「채움률·비중 두 인자 정하기」**로 바뀔 수 있음 — 우리 0.88 은 안 건드렸음. ⚠ 뒷길이가 **붙박이**(찰 0.45 · 메 0.30) — 정본 탭이 그 둘뿐이고 등록부에 칸이 없음. 그 사실을 줄로 냄. 칸이 생기면 표를 열면 됨. 자체검증 — 새 시험 5건(정본 값 대조 · 메는 세 줄이 빠지는지 · 기울기 몫 없음), 회귀 591 통과 · 0 실패. Co-Authored-By: Claude Opus 5 (1M context) --- .../B08_Quantity_Engine_UnitQuantity.py | 10 +- ..._Quantity_Engine_UnitQuantity_Revetment.py | 107 +++++++++++++++++- 2 files changed, 112 insertions(+), 5 deletions(-) diff --git a/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py b/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py index 03370aff..6ce09aa9 100644 --- a/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py +++ b/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py @@ -942,7 +942,8 @@ EXPANDERS = { "revetment": lambda h, l, o, f=None, r="": _revetment()(h, l, o, f, r), # 골막이·바닥막이는 돌 계열이나 **돌쌓기 식을 그대로 못 쓴다** — 까닭은 그 모듈에 적었다. "erosion_check": lambda h, l, o, f=None, r="": _stone_kin()("erosion_check"), - "bed_sill": lambda h, l, o, f=None, r="": _stone_kin()("bed_sill"), + # 바닥막이는 **돌붙임 ㎡당**이라 높이·연장이 아니라 **면적**이 밑수다(정본 「돌붙임L3=…」). + "bed_sill": lambda h, l, o, f=None, r="": _bed_sill()(_num(o.get("area_m2"), 0.0), o), # ⚠⚠ **큰돌쌓기(`boulder_masonry`)를 여기에 두지 않는다** (2026-09-07 발견). # 큰돌쌓기는 품셈 **13-6** 이고 돌쌓기는 **13-4** 다 — **규격 축이 다르다.** # 돌쌓기는 **뒷길이**(35·45·55·60㎝), 큰돌쌓기는 **직경**(40~60·60~80·80~100㎝). @@ -953,6 +954,13 @@ EXPANDERS = { } +def _bed_sill(): + """바닥막이 전개 함수를 늦게 가져온다.""" + from B08_Quantity.B08_Quantity_Engine_UnitQuantity_Revetment import bed_sill + + return bed_sill + + def _stone_kin(): """골막이·바닥막이 사유 함수 — 늦게 가져온다(서로 부르는 것을 푸는 자리).""" from B08_Quantity.B08_Quantity_Engine_UnitQuantity_Revetment import stone_kin_withheld diff --git a/B08_Quantity/B08_Quantity_Engine_UnitQuantity_Revetment.py b/B08_Quantity/B08_Quantity_Engine_UnitQuantity_Revetment.py index 1ccae6f7..8af1bf40 100644 --- a/B08_Quantity/B08_Quantity_Engine_UnitQuantity_Revetment.py +++ b/B08_Quantity/B08_Quantity_Engine_UnitQuantity_Revetment.py @@ -63,10 +63,45 @@ STONE_KIN_WITHHELD: dict[str, str] = { "실측) — 평균두께 {(0.45+0.1H)+(0.45+0.4H)}÷2 · 밑수 「돌쌓기+돌붙임」 · 개소당. " "게다가 정면적이 사다리꼴이라 **상장·하장 칸이 있어야** 셈이 서는데 등록부에 없음" ), - "bed_sill": ( - "바닥막이는 형태가 **돌붙임**(품셈 13-5)이라 돌쌓기(13-4) 표를 못 씀 — " - "밑수도 면적(㎡)이라 축이 다름. 돌붙임 원단위 미확보" - ), +} + +#: 돌붙임 원단위 — 소광리 정본 「돌붙임L3=45(사방)」(찰) · 「돌붙임L3=30(야면석메붙임)」(메). +#: ⚠⚠ **돌쌓기 표(13-4)와 합치지 말 것.** 값이 겹쳐 보여 합치고 싶어지는 자리다: +#: +#: 돌쌓기(13-4) 밑수 **비탈면적** · 뒷길이 **일곱 규격** · **기울기 몫 √(1+n²)** +#: 돌붙임(13-5) 밑수 **평면적** · 뒷길이 **넷** · **기울기 몫 없음** +#: 메는 모르타르·채움콘크리트가 통째로 빠진다 +#: +#: ⚠ 45㎝ 에서 고임돌 0.15 · 채움 0.20 이 **우연히 같다** — 그것이 합치고 싶어지는 까닭이고, +#: 합치면 뒷길이 25·30·60·75 에서 조용히 틀린다(돌붙임 표에 없는 규격이다). +#: +#: ⚠ **뒷길이는 지금 붙박이다** — 찰 0.45m · 메 0.30m. 정본 탭이 그 둘로만 있고 바닥막이 +#: 등록부에 뒷길이 칸이 없다. 칸이 생기면 이 표를 열면 된다. +#: ⓘ 돌중량은 **관측값이 아니라 유도값**이다 — `뒷길이 × 0.77(채움률) × 2.65(비중)`. +#: 찰 0.45 × 0.77 × 2.65 = 0.918 ton/㎡. 메(야면석)는 0.42 로 **원문에 값이 직접 적혀 있어** +#: 같은 유도식이 안 맞는다(야면석은 공극이 커 ㎥당 무게가 다르다) — 그대로 옮긴다. +BED_SILL_FORMS: dict[str, dict[str, Any]] = { + "돌붙임(찰)": { + "back_len_m": 0.45, + "stone_name": "돌", + "stone_spec": "30×30×45㎝", + "stone_ton_per_m2": 0.45 * 0.77 * 2.65, + "wedge_stone_m3_per_m2": 0.15, + "mortar_m3_per_m2": 0.009, + "fill_concrete_m3_per_m2": 0.2, + "blinding_m3_per_m2": 0.1, + }, + "돌붙임(메)": { + "back_len_m": 0.30, + "stone_name": "야면석", + "stone_spec": "20×20×30㎝", + "stone_ton_per_m2": 0.42, + "wedge_stone_m3_per_m2": 0.07, + # 메붙임은 모르타르·채움콘크리트·버림이 **없다**(정본 탭에 줄 자체가 없음). + "mortar_m3_per_m2": None, + "fill_concrete_m3_per_m2": None, + "blinding_m3_per_m2": None, + }, } #: 돌쌓기 식이 계수를 가르는 데 쓰는 칸 — 기슭막이 등록부에 아직 없는 것을 알린다. @@ -116,3 +151,67 @@ def stone_kin_withheld(type_id: str) -> tuple[list[Component], list[str]]: """ why = STONE_KIN_WITHHELD.get(type_id) return [], [why] if why else [] + + +def bed_sill(area_m2: float, options: dict[str, Any]) -> tuple[list[Component], list[str]]: + """바닥막이 전개 — **돌붙임 ㎡당** 원단위를 면적에 곱한다(소광리 정본 두 탭). + + ⚠ 밑수가 **면적**이다. 돌쌓기처럼 높이·연장으로 셈하지 않는다 — 정본 표 제목이 「㎡당」이고 + 등록부 밑수도 `area_m2` 라 축이 그대로 맞는다. + ⚠ 기울기 몫이 **없다** — 평면에 붙이는 것이라 √(1+n²) 를 곱하지 않는다. + """ + from B08_Quantity.B08_Quantity_Engine_UnitQuantity import DESTINATION + + form = str(options.get("form") or "").strip() + table = BED_SILL_FORMS.get(form) + if table is None: + return [], [ + f"바닥막이 형태 「{form or '빈 값'}」 — 돌붙임(찰)·돌붙임(메)만 원단위가 있습니다" + ] + if area_m2 <= 0: + return [], ["면적이 없어 전개하지 않음"] + + back = table["back_len_m"] + rows: list[tuple[str, str, float, str]] = [ + ("돌붙임", "㎡", area_m2, f"면적 {area_m2:g}㎡ (평면적 — 기울기 몫 없음)"), + ("입적", "㎥", area_m2 * back, f"면적 × 두께 {back:g}m"), + ( + table["stone_name"], + "ton", + area_m2 * table["stone_ton_per_m2"], + f"면적 × {table['stone_ton_per_m2']:.3f} ton/㎡ · {table['stone_spec']}" + + ( + f" (= 뒷길이 {back:g} × 0.77 × 2.65)" + if form == "돌붙임(찰)" + else " (원문 직접값 — 야면석은 유도식이 안 맞음)" + ), + ), + ( + "고임돌", + "㎥", + area_m2 * table["wedge_stone_m3_per_m2"], + f"면적 × {table['wedge_stone_m3_per_m2']} ㎥/㎡", + ), + ] + for name, key, unit in ( + ("모르터", "mortar_m3_per_m2", "㎥"), + ("채움콘크리트", "fill_concrete_m3_per_m2", "㎥"), + ("버림콘크리트", "blinding_m3_per_m2", "㎥"), + ): + value = table[key] + if value is not None: + rows.append((name, unit, area_m2 * value, f"면적 × {value} ㎥/㎡")) + # 터파기 — 정본은 「두께 × 1 × 1」이라 **면적 × 두께**와 같다. + rows.append(("터파기", "㎥", area_m2 * back, f"면적 × 두께 {back:g}m")) + + components = [ + Component(name, unit, amount, DESTINATION.get(name, "quantity"), basis) + for name, unit, amount, basis in rows + ] + notes = [f"뒷길이 {back:g}m 는 정본 탭 붙박이 값입니다 — 바닥막이 제원에 뒷길이 칸이 없습니다"] + if form == "돌붙임(찰)": + notes.append( + "버림 콘크리트 0.1 ㎥/㎡ 포함 — 정본 주기 「바닥 10㎝ 이상 콘크리트(버림) 포설 후 " + "돌붙임」(KDS 44 90 00 의 100㎜ 와 같음)" + ) + return components, notes From 9f2e81babc5f911a3a4b9adc1b915f706ed8b613 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Wed, 9 Sep 2026 11:28:30 +0900 Subject: [PATCH 5/6] =?UTF-8?q?feat(B08):=20=EA=B0=9C=EA=B1=B0(=EA=B2=89?= =?UTF-8?q?=EB=8F=84=EB=9E=91)=20=EC=A0=84=EA=B0=9C=20=E2=80=94=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B8=20=EB=91=90=20=ED=91=9C=EB=A5=BC=20m?= =?UTF-8?q?=EB=8B=B9=EC=9C=BC=EB=A1=9C=20(3=EB=8B=A8=EA=B3=84=20=EB=84=B7?= =?UTF-8?q?=EC=A7=B8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 소광리 정본 「개거(150-200) (2)」·「L형수로-(201)」 — **둘 다 m당**. ⚠⚠ **두 표가 담는 것이 다름.** 값이 비슷해 보여 합치고 싶어지는 자리라 못 박음: ``` 콘크리트 개거 150×200 터파기 · 유로폼 · 면목 **세 줄뿐** — **콘크리트 본체 줄이 없음** 콘크리트 L형수로 H=0.2 터파기 · 되메우기 · 잔토 · **콘크리트** · PVC Φ50 · 이형철근 D13 · 거푸집 · 면목 **여덟 줄** ⓘ 같은 것 — 둘 다 m당이고 거푸집 계열(유로폼·거푸집)과 면목이 있음 ``` ⚠ 개거에 콘크리트가 없는 것은 **빠뜨린 것이 아니라 원문 그대로**임. 0 으로 때우거나 L형수로 값을 옮겨 채우지 않음 — 돌붙임(메)에서 세 줄을 안 만든 그 판단과 같음. ⚠ **PVC Φ50 은 줄만 있고 수량 칸이 빔** — 안 세우고 그 사실을 줄로 냄. ⚠ 규격이 **붙박이**(「콘크리트 개거 150×200」) — `open_ditch` 등록부에 **옵션이 하나도 없음**. `ditch_spec` 을 주면 L형수로로 갈림. 칸이 생기면 열면 됨. **측구(`ditch_side`)는 안 건드림** — `design_owner: 횡단 설계` 라 B06 소관이고 `enabled: False`. 정본 「측구수로400/500」은 **「터파기 계산서」**라 터파기·잔토·되메우기 셋뿐임(본체 없음) — 그쪽이 쓸 때를 위해 적어 둠. 실전개 (연장 10m) 개거 터파기 0.3㎥ · 유로폼 5.5㎡ · 면목 20m L형수로 터파기 3.1645 · 되메우기 1.2997 · 잔토 1.8647 · 콘크리트 1.508 · 이형철근 3.98㎏ · 거푸집 7.38㎡ · 면목 10m 자체검증 — 새 시험 5건(정본 값 대조 · 개거에 콘크리트가 없는지 · 잔토 = 터파기−되메우기 · 빈 수량 줄 · 없는 규격), 회귀 596 통과 · 0 실패. Co-Authored-By: Claude Opus 5 (1M context) --- .../B08_Quantity_Engine_UnitQuantity.py | 9 +++ ..._Quantity_Engine_UnitQuantity_Revetment.py | 81 +++++++++++++++++++ 2 files changed, 90 insertions(+) diff --git a/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py b/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py index 6ce09aa9..d3932054 100644 --- a/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py +++ b/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py @@ -942,6 +942,8 @@ EXPANDERS = { "revetment": lambda h, l, o, f=None, r="": _revetment()(h, l, o, f, r), # 골막이·바닥막이는 돌 계열이나 **돌쌓기 식을 그대로 못 쓴다** — 까닭은 그 모듈에 적었다. "erosion_check": lambda h, l, o, f=None, r="": _stone_kin()("erosion_check"), + # 개거는 **m당** 원단위라 연장이 밑수다(정본 「개거(150-200)」·「L형수로-(201)」). + "open_ditch": lambda h, l, o, f=None, r="": _open_ditch()(l, o), # 바닥막이는 **돌붙임 ㎡당**이라 높이·연장이 아니라 **면적**이 밑수다(정본 「돌붙임L3=…」). "bed_sill": lambda h, l, o, f=None, r="": _bed_sill()(_num(o.get("area_m2"), 0.0), o), # ⚠⚠ **큰돌쌓기(`boulder_masonry`)를 여기에 두지 않는다** (2026-09-07 발견). @@ -954,6 +956,13 @@ EXPANDERS = { } +def _open_ditch(): + """개거 전개 함수를 늦게 가져온다.""" + from B08_Quantity.B08_Quantity_Engine_UnitQuantity_Revetment import open_ditch + + return open_ditch + + def _bed_sill(): """바닥막이 전개 함수를 늦게 가져온다.""" from B08_Quantity.B08_Quantity_Engine_UnitQuantity_Revetment import bed_sill diff --git a/B08_Quantity/B08_Quantity_Engine_UnitQuantity_Revetment.py b/B08_Quantity/B08_Quantity_Engine_UnitQuantity_Revetment.py index 8af1bf40..cd425949 100644 --- a/B08_Quantity/B08_Quantity_Engine_UnitQuantity_Revetment.py +++ b/B08_Quantity/B08_Quantity_Engine_UnitQuantity_Revetment.py @@ -153,6 +153,87 @@ def stone_kin_withheld(type_id: str) -> tuple[list[Component], list[str]]: return [], [why] if why else [] +#: 개거(겉도랑) 원단위 — 소광리 정본 「개거(150-200) (2)」·「L형수로-(201)」, **둘 다 m당**. +#: ⚠⚠ **두 표가 담는 것이 다르다.** 값이 비슷해 보여 합치고 싶어지는 자리다: +#: +#: 콘크리트 개거 150×200 터파기 · 유로폼 · 면목 **세 줄뿐** — **콘크리트 본체 줄이 없다** +#: 콘크리트 L형수로 H=0.2 터파기 · 되메우기 · 잔토 · **콘크리트** · PVC Φ50 · 이형철근 D13 · +#: 거푸집 · 면목 **여덟 줄** +#: +#: ⓘ 같은 것 — **둘 다 m당**이고 거푸집 계열(유로폼·거푸집)과 면목이 있다. +#: ⚠ 개거 표에 콘크리트가 없는 것은 **빠뜨린 것이 아니라 원문 그대로**다. 0 으로 때우거나 +#: L형수로 값을 옮겨 채우지 않는다(2026-09-09 판단 — 돌붙임(메)에서 세 줄을 안 만든 그 자리). +#: +#: ⚠ **규격이 붙박이다** — 정본에 「150×200」 하나뿐이고 `open_ditch` 등록부에 **옵션이 하나도 +#: 없다**(규격 칸 없음). 칸이 생기면 이 표를 열면 된다. +OPEN_DITCH_FORMS: dict[str, dict[str, Any]] = { + "콘크리트 개거 150×200": { + "rows": ( + ("터파기", "㎥", 0.3 * 0.1 * 1.0, "0.3 × 0.1 × 1"), + ("유로폼", "㎡", (0.2 + 0.2 + 0.15) * 1.0, "(0.2 + 0.2 + 0.15) × 1"), + ("면목", "m", 2.0 * 1.0, "2 × 1"), + ), + }, + "콘크리트 L형수로 H=0.2": { + "rows": ( + ("터파기", "㎥", 0.31645, "[{(1.11+1.51)÷2×0.2} + {(0.33×0.33)÷2}] × 1.0"), + ("되메우기", "㎥", 0.129975, "(0.2+0.30)÷2×0.25×1 + (0.2+0.19)÷2×0.33×1.0"), + ("잔토처리", "㎥", 0.186475, "터파기 − 되메우기"), + ( + "콘크리트", + "㎥", + 0.1508, + "{(0.2+0.15)÷2×0.7} + (0.21×0.13)×1 + {(0.15+0.21)÷2×0.20}×1", + ), + ("이형철근 D13", "kg", 0.398, "0.2 × 2 × 0.995"), + ("거푸집", "㎡", 0.738, "0.2 + 0.33 + √(0.2² + 0.06²)"), + ("면목", "m", 1.0, "1"), + ), + # ⚠ 정본에 줄은 있으나 **수량 칸이 비어 있다**(PVC Φ50). 0 으로 만들지 않고 알린다. + "blank_rows": (("PVC 파이프 Φ50㎜", "m"),), + }, +} + + +def open_ditch(length_m: float, options: dict[str, Any]) -> tuple[list[Component], list[str]]: + """개거(겉도랑) 전개 — **m당** 원단위를 연장에 곱한다(소광리 정본 두 탭). + + ⚠ 규격을 고를 칸이 없어 지금은 **「콘크리트 개거 150×200」 붙박이**다. 그 사실을 줄로 낸다. + """ + from B08_Quantity.B08_Quantity_Engine_UnitQuantity import DESTINATION + + if length_m <= 0: + return [], ["연장이 없어 전개하지 않음"] + spec = str(options.get("ditch_spec") or "콘크리트 개거 150×200").strip() + table = OPEN_DITCH_FORMS.get(spec) + if table is None: + return [], [ + f"개거 규격 「{spec}」 원단위가 없습니다 — 있는 것: {' · '.join(OPEN_DITCH_FORMS)}" + ] + + components = [ + Component( + name, + unit, + per_m * length_m, + DESTINATION.get(name, "quantity"), + f"{basis} × 연장 {length_m:g}m (m당 {per_m:g})", + ) + for name, unit, per_m, basis in table["rows"] + ] + notes: list[str] = [] + if "ditch_spec" not in options: + notes.append(f"규격이 「{spec}」 붙박이입니다 — 개거 제원에 규격 칸이 없습니다") + if spec == "콘크리트 개거 150×200": + notes.append( + "⚠ 이 표에는 **콘크리트 본체 줄이 없습니다** — 정본 원문 그대로입니다" + "(터파기·유로폼·면목 세 줄뿐). 지어내지 않았습니다" + ) + for name, unit in table.get("blank_rows", ()): + notes.append(f"{name}({unit}) 은 정본에 줄은 있으나 **수량이 비어 있어** 안 세웠습니다") + return components, notes + + def bed_sill(area_m2: float, options: dict[str, Any]) -> tuple[list[Component], list[str]]: """바닥막이 전개 — **돌붙임 ㎡당** 원단위를 면적에 곱한다(소광리 정본 두 탭). From 24a734cdb219fe56e0b258b7427374dc73b80b8c Mon Sep 17 00:00:00 2001 From: umsangdon Date: Wed, 9 Sep 2026 11:32:51 +0900 Subject: [PATCH 6/6] auto: 2026-09-09 11:32 (ESD_LAPTOP) --- .../B08_Quantity_Engine_UnitQuantity.py | 9 ++++ ..._Quantity_Engine_UnitQuantity_Revetment.py | 50 ++++++++++++++++++- 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py b/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py index d3932054..9280374b 100644 --- a/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py +++ b/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py @@ -944,6 +944,8 @@ EXPANDERS = { "erosion_check": lambda h, l, o, f=None, r="": _stone_kin()("erosion_check"), # 개거는 **m당** 원단위라 연장이 밑수다(정본 「개거(150-200)」·「L형수로-(201)」). "open_ditch": lambda h, l, o, f=None, r="": _open_ditch()(l, o), + # 흙막이는 **아직 못 세움** — 정본에 떼흙막이 하나뿐인데 우리 형식 칸에 「떼」가 없음. + "soil_guard": lambda h, l, o, f=None, r="": _soil_guard()(o), # 바닥막이는 **돌붙임 ㎡당**이라 높이·연장이 아니라 **면적**이 밑수다(정본 「돌붙임L3=…」). "bed_sill": lambda h, l, o, f=None, r="": _bed_sill()(_num(o.get("area_m2"), 0.0), o), # ⚠⚠ **큰돌쌓기(`boulder_masonry`)를 여기에 두지 않는다** (2026-09-07 발견). @@ -956,6 +958,13 @@ EXPANDERS = { } +def _soil_guard(): + """흙막이 사유 함수를 늦게 가져온다.""" + from B08_Quantity.B08_Quantity_Engine_UnitQuantity_Revetment import soil_guard + + return soil_guard + + def _open_ditch(): """개거 전개 함수를 늦게 가져온다.""" from B08_Quantity.B08_Quantity_Engine_UnitQuantity_Revetment import open_ditch diff --git a/B08_Quantity/B08_Quantity_Engine_UnitQuantity_Revetment.py b/B08_Quantity/B08_Quantity_Engine_UnitQuantity_Revetment.py index cd425949..23c0ff91 100644 --- a/B08_Quantity/B08_Quantity_Engine_UnitQuantity_Revetment.py +++ b/B08_Quantity/B08_Quantity_Engine_UnitQuantity_Revetment.py @@ -164,6 +164,17 @@ def stone_kin_withheld(type_id: str) -> tuple[list[Component], list[str]]: #: ⚠ 개거 표에 콘크리트가 없는 것은 **빠뜨린 것이 아니라 원문 그대로**다. 0 으로 때우거나 #: L형수로 값을 옮겨 채우지 않는다(2026-09-09 판단 — 돌붙임(메)에서 세 줄을 안 만든 그 자리). #: +#: ⓘ **「다른 탭에 본체가 있나」를 찾아봤고 없었다**(2026-09-09 53탭 전수): +#: · 「개거」 글자가 나오는 탭은 **둘뿐** — 이 탭과 「콘크리트개거」. +#: 뒤엣것은 **제목만 「콘크리트개거 (물넘이형)」이고 내용이 국가지점번호판 표**다. +#: · 탭 이름의 「(2)」는 **짝 번호가 아니라 엑셀 사본 표식**이다 — `Φ1000` ↔ `Φ1000(2)`, +#: `떼수로(윤주1.08)` ↔ `…(2)` 가 **내용까지 똑같다**(대조 확인). 「(1)」은 없다. +#: · 같은 시트의 「측구수로400/500」도 제목이 **「터파기 계산서」**이고 본체가 없다 — +#: **한 구조물을 부분 계산서로 쪼개는 버릇**이 있는 시트다. +#: ⇒ 그러므로 이 표는 **부분 계산서일 가능성이 남아 있다.** 다만 이 원본 안에서는 +#: 본체 표를 못 찾았으므로 **「원문에 없음」으로 두고 그 사실을 화면에 적는다** — +#: 「콘크리트 개거인데 콘크리트가 0」이 우리 결함으로 읽히지 않게. +#: #: ⚠ **규격이 붙박이다** — 정본에 「150×200」 하나뿐이고 `open_ditch` 등록부에 **옵션이 하나도 #: 없다**(규격 칸 없음). 칸이 생기면 이 표를 열면 된다. OPEN_DITCH_FORMS: dict[str, dict[str, Any]] = { @@ -195,6 +206,40 @@ OPEN_DITCH_FORMS: dict[str, dict[str, Any]] = { } +#: 떼흙막이 원단위 — 소광리 정본 「떼흙막이」, **개소당**. 치수는 전부 **평균 붙박이**다 +#: (상단 1.5 · 하단 1.1 · 높이 0.5 · 두께 0.2 · 기슭 0.54 — 원문에 「(평균)」이라 적혀 있다). +#: ⚠⚠ **아직 못 쓴다** — 우리 `soil_guard` 형식이 「콘크리트 · 돌(찰) · 돌(메) · 블록쌓기 · +#: 돌망태」 다섯이고 **「떼」가 없다.** 형식 칸이 생기면 이 표를 그대로 붙이면 된다. +#: ⚠ 나머지 다섯 형식은 **정본에 탭이 없다.** 돌(찰)/(메)에 기슭막이 식을 빌려 쓰지 않는다 — +#: 흙막이는 교본상 앞면 1:0.3·뒷면 수직·천단 30㎝ 라 축이 다르고, 무엇보다 **근거가 없다.** +SOIL_GUARD_SOD: dict[str, Any] = { + "unit_label": "개소당", + "rows": ( + ( + "떼", + "㎡", + 1.39, + "머리떼 1.5×0.2 + 바닥떼 1.1×0.2 + 정면떼 (1.5+1.1)÷2×0.5 " + "+ 양기슭바닥떼 0.54×0.2×2 · 규격 20×20㎝", + ), + ("바닥파기", "㎥", 0.17, "(0.54×0.4×0.2×2) + (1.1×0.4×0.2)"), + ), +} + + +def soil_guard(options: dict[str, Any]) -> tuple[list[Component], list[str]]: + """흙막이 — **아직 못 세운다.** 어느 형식이 왜 안 되는지만 낸다.""" + form = str(options.get("form") or "").strip() + if not form: + return [], ["흙막이 형식이 정해지지 않았습니다 — 형식을 골라야 물량이 섭니다"] + return [], [ + f"흙막이 형태 「{form}」 — 정본(소광리 53탭)에 그 형식의 산출식이 없습니다. " + "있는 것은 **떼흙막이** 하나인데 우리 형식 칸에 「떼」가 없습니다. " + "⚠ 돌(찰)·돌(메)에 기슭막이 식을 빌려 쓰지 않습니다 — 흙막이는 앞면 1:0.3·뒷면 수직·" + "천단 30㎝(교본)라 축이 다르고 근거가 없습니다" + ] + + def open_ditch(length_m: float, options: dict[str, Any]) -> tuple[list[Component], list[str]]: """개거(겉도랑) 전개 — **m당** 원단위를 연장에 곱한다(소광리 정본 두 탭). @@ -227,7 +272,10 @@ def open_ditch(length_m: float, options: dict[str, Any]) -> tuple[list[Component if spec == "콘크리트 개거 150×200": notes.append( "⚠ 이 표에는 **콘크리트 본체 줄이 없습니다** — 정본 원문 그대로입니다" - "(터파기·유로폼·면목 세 줄뿐). 지어내지 않았습니다" + "(터파기·유로폼·면목 세 줄뿐). 같은 원본 53탭에서 본체 표를 찾았으나 없었고" + "(「콘크리트개거」 탭은 제목만 그 이름이고 내용이 다른 표), 이 시트가" + "「측구수로 터파기 계산서」처럼 **부분 계산서를 쓰는 버릇**이 있어" + "**다른 문서에 본체가 있을 수 있습니다.** 지어내지 않았습니다" ) for name, unit in table.get("blank_rows", ()): notes.append(f"{name}({unit}) 은 정본에 줄은 있으나 **수량이 비어 있어** 안 세웠습니다")