Files
Aislo/B08_Quantity/B08_Quantity_Engine_UnitQuantity_Box.py
T
eomsangdonandClaude Opus 5 e40f4c9fd3 feat(b05·b08): BOX암거 구체 터파기 — 평균 터파기고 칸 하나 · 토공 축으로만 · 기초잡석 사유(브레인 판정 ①③)
- 등록부 trench_depth_m(평균 터파기고 · 기본값 없음 · 비면 사유)
- 터파기 = (밑폭 버림 폭 + 여유 0.5×2 + 윗폭 1:0.5) ÷ 2 × 깊이 × 연장(울진 「암거구체 토공(2×2)」) · 되메우기 = 터파기 − 든 것(잡석·버림·구체) · 잔토처리 = 든 것 — destination earthwork(묶음에 안 듦 · 토공집계로만)
- 울진 원문은 잔토에 구체 전체 · 되메우기 한쪽만 — 옹벽 식(터파기 − 든 것)으로 맞추고 사유에 적음
- 기초잡석 사유 「근거 둘(예제·울진 집계표)에는 잡석 줄이 없음 — 사용자 확정 3차 ② 로 세움」

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016VBGFXB9AbJBwXP19z75Qq
2026-09-15 02:59:51 +09:00

183 lines
8.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""BOX암거 전개식 — **m당 × 연장** (2026-09-15 브레인 12장 D 판정 ①~⑤).
근거 둘 — 구조가 같다
① 산림과임업기술(임도) 5장 「나) 구조물도에 의한 수량산출(예)」 · 그림 5-2-70
Box(2.0×1.5×2련×6.03m) — 구체 = (외곽 − 유수구 + 헌치) × L · 기초 = 폭 × 0.1 × L ·
거푸집 = 외벽·유수구·받침판·귀면 · 동바리 = 유수구 단면 × L · 철근 「상세도에서」(값 없음)
② 실무 울진 기번3 `5. 구조도(기번3).xlsx` 「암거수량집계표(2×2)」 m당 — 레미콘 2.84 ·
버림 0.28 · 유로폼 11.132 · 합판6회 0.2 · 철근 SD40 H13 0.179 + H16 0.157t · 동바리 3.92 ·
비계 18.72
두께 제안값은 등록부 칸(예제 벽 0.3 · 판 0.25 · 헌치 0.2 · 버림 0.1 · KDS 300㎜ 병기).
⚠ 1련만 — 등록부에 련수 칸이 없음(예제 식은 련수로 늘어나나 그림·칸이 1련).
⚠ 철근은 **크기별 식이 없다** — 울진 2×2 관측값만 선다(판정 ②).
⚠ 비계 시종점 두 면은 **개소당 한 번** — 원문은 m당 칸에 넣어 연장을 곱하면 부풀어 오름.
"""
from __future__ import annotations
import math
from typing import Any
from B08_Quantity.B08_Quantity_Engine_UnitQuantity_Base import Component, _num, routed
#: 버림 폭 여유(m, 한쪽) — 예제 5.1 = 외곽 4.9 + 0.1×2 · 울진 2.8 = 외곽 2.6 + 0.1×2 (둘이 같음).
BLINDING_EXTRA_M = 0.1
#: 철근 관측값(kg/m) — 울진 기번3 「암거수량집계표(2×2)」 SD40 · 내공 2.0×2.0 · 벽·판 0.3.
REBAR_OBSERVED_2X2 = (("D13", 179.0), ("D16", 157.0))
REBAR_SIZE_ONLY = (
"철근 — 크기별 식 없음 · 관측값은 2×2 뿐(울진 기번3 구조도 SD40 H13 0.179 + H16 0.157t/m ·"
" 벽·판 0.3) — 이 크기는 철근이 안 섬(2026-09-15 브레인 판정 ②)"
)
SOURCES = (
"산식 = 산림과임업기술(임도) 5장 수량산출 예제(그림 5-2-70) · 실무 울진 기번3 구조도"
" 「암거수량집계표(2×2)」"
)
NOTE_FORMWORK = (
"거푸집 — 본체 유로폼(외벽 · 내벽 헌치 뺀 높이 · 상판 밑면 · 귀면 넷) · 버림 옆면 합판"
" (울진 2×2 모양 · 예제는 합판 3회)"
)
NOTE_SHORING_SCAFFOLD = (
"동바리(유수구 단면 − 헌치)·비계(외벽 양면 + 시종점 두 면)는 **실무 관측 모양**"
"(울진 2×2 「강재 3개월」) — 품셈 12-20 강관동바리 · 12-19 강관비계로 이음 ·"
" 시종점 두 면은 개소당 한 번(원문은 m당 칸)"
)
NOTE_WING = "날개벽 수량 근거 없음(교본·실무 모두 · 판정 ⑤) — 날개벽 줄 안 섬"
NOTE_RUBBLE = (
"기초잡석 — 근거 둘(예제·울진 집계표)에는 잡석 줄이 없음 — 사용자 확정 3차 ② 로 세움"
"(버림이 선 구조물엔 잡석 · 옹벽과 같은 규칙)"
)
#: 구체 터파기 — 울진 「암거구체 토공(2×2)」: 밑폭 = 버림 폭 + 여유 0.5×2 · 옆면 1:0.5.
TRENCH_CLEARANCE_M = 0.5
TRENCH_SIDE_SLOPE = 0.5
NOTE_TRENCH_MISSING = (
"평균 터파기고 칸이 비어 구체 터파기·되메우기가 안 섬 — 칸을 적으면 토공으로 섬"
)
NOTE_TRENCH = (
"구체 터파기 — 밑폭·옆면은 울진 「암거구체 토공(2×2)」(여유 0.5 · 1:0.5)"
" · 깊이 = 평균 터파기고 칸 · 되메우기 = 터파기 − 든 것(기초잡석·버림·구체)"
" — 원문은 잔토에 구체 전체 · 되메우기 한쪽만이라 옹벽 식(터파기 − 든 것)으로 맞춤"
" · 토공집계로만 감(이중계상 방지)"
)
def box_culvert(options: dict[str, Any]) -> tuple[list[Component], list[str]]:
"""BOX암거 한 개소 — (성분, 사유). 연장이 없으면 성분 없이 사유만."""
length = _num(options.get("length_m"))
if length <= 0:
return [], ["BOX암거 연장(계류 방향)이 없어 수량이 안 섬 — 연장 칸을 적을 것(기본값 없음)"]
width, height = _num(options.get("body_width_m")), _num(options.get("body_height_m"))
wall, slab = _num(options.get("wall_thickness_m")), _num(options.get("slab_thickness_m"))
haunch, blinding = _num(options.get("haunch_m")), _num(options.get("blinding_thickness_m"))
if min(width, height, wall, slab) <= 0:
return [], ["BOX암거 본체 폭·높이·두께 중 빈 칸이 있어 수량이 안 섬"]
outer_w, outer_h = width + 2 * wall, height + 2 * slab
chamfer = math.sqrt(2) * haunch
per_m: list[tuple[str, str, float, str, str]] = [
(
"콘크리트",
"㎥",
outer_w * outer_h - width * height + haunch**2 / 2 * 4,
f"{outer_w:g}×{outer_h:g} {width:g}×{height:g} + {haunch:g}²÷2×4",
"",
),
(
"버림콘크리트",
"㎥",
(outer_w + 2 * BLINDING_EXTRA_M) * blinding,
f"({outer_w:g} + {BLINDING_EXTRA_M:g}×2) × {blinding:g}",
"",
),
(
"유로폼",
"㎡",
2 * outer_h + 2 * (height - 2 * haunch) + (width - 2 * haunch) + 4 * chamfer,
f"외벽 {outer_h:g}×2 + 내벽 ({height:g} {haunch:g}×2)×2"
f" + 상판 밑 ({width:g} {haunch:g}×2) + 귀면 √({haunch:g}²+{haunch:g}²)×4",
"",
),
("합판거푸집", "㎡", 2 * blinding, f"버림 옆면 {blinding:g}×2", ""),
(
"동바리",
"㎥",
width * height - 2 * haunch**2,
f"{width:g}×{height:g} {haunch:g}²÷2×4",
"",
),
("비계", "㎡", 2 * outer_h, f"외벽 양면 {outer_h:g}×2", ""),
]
notes: list[str] = [SOURCES, NOTE_FORMWORK, NOTE_SHORING_SCAFFOLD, NOTE_WING, NOTE_RUBBLE]
if math.isclose(width, 2.0) and math.isclose(height, 2.0):
per_m += [
("이형철근", "kg", kg, "울진 기번3 구조도 2×2 관측(SD40)", size)
for size, kg in REBAR_OBSERVED_2X2
]
notes.append("철근은 관측값(울진 2×2 · 벽·판 0.3) — 두께를 바꿔도 따라 안 바뀜")
else:
notes.append(REBAR_SIZE_ONLY)
components = [
Component(
name,
unit,
amount * length,
destination,
f"{basis} = m당 {amount:.4g} × 연장 {length:g}m",
spec=spec,
)
for name, unit, amount, basis, spec in per_m
if (destination := routed(name, notes))
]
ends = 2 * outer_w * outer_h
for component in components:
if component.name == "비계":
component.amount += ends
component.basis += (
f" + 시종점 두 면 {outer_w:g}×{outer_h:g}×2 = {ends:.4g}(개소당 한 번)"
)
if _num(options.get("trench_depth_m")) <= 0:
notes.append(NOTE_TRENCH_MISSING)
return components, notes
def box_trench(
options: dict[str, Any], rubble_thickness_m: float
) -> tuple[list[Component], float] | None:
"""구체 터파기·되메우기·잔토(토공 축) — 평균 터파기고가 비면 `None`.
기초잡석 두께는 원단위표와 **같은 값**을 받음(`build_table` 의 두께) — 두 벌로 안 셈.
"""
depth, length = _num(options.get("trench_depth_m")), _num(options.get("length_m"))
outer_w = _num(options.get("body_width_m")) + 2 * _num(options.get("wall_thickness_m"))
outer_h = _num(options.get("body_height_m")) + 2 * _num(options.get("slab_thickness_m"))
blinding = _num(options.get("blinding_thickness_m"))
if depth <= 0 or length <= 0 or outer_w <= 0:
return None
base_w = outer_w + 2 * BLINDING_EXTRA_M
bottom = base_w + 2 * TRENCH_CLEARANCE_M
top = bottom + 2 * TRENCH_SIDE_SLOPE * depth
excavation = (bottom + top) / 2 * depth
body = min(max(depth - rubble_thickness_m - blinding, 0.0), outer_h)
filled = base_w * (rubble_thickness_m + blinding) + outer_w * body
notes: list[str] = []
rows = [
(
"터파기",
excavation,
f"(밑폭 {bottom:.2f}(버림 폭 {base_w:g} + 여유 {TRENCH_CLEARANCE_M:g}×2) + 윗폭"
f" {top:.2f}(1:{TRENCH_SIDE_SLOPE:g})) ÷ 2 × 깊이 {depth:g} · {NOTE_TRENCH}",
),
(
"되메우기",
excavation - filled,
f"터파기 든 것 {filled:.3f}(버림 폭 × (잡석 {rubble_thickness_m:g}"
f" + 버림 {blinding:g}) + 구체 폭 {outer_w:g} × {body:.2f})",
),
("잔토처리", filled, "터파기 되메우기"),
]
return [
Component(name, "㎥", amount * length, destination, f"{basis} × 연장 {length:g}m")
for name, amount, basis in rows
if (destination := routed(name, notes))
], depth