- 등록부 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
183 lines
8.7 KiB
Python
183 lines
8.7 KiB
Python
"""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
|