Files
Aislo/B09_Estimation/B09_Estimation_TopsoilRemoval.py
T
eomsangdonandClaude Opus 5 ac66a25cc7 feat(b09): ㉰ 표토제거 답외구간 9-15-2 일위대가 — 계수표를 불도저 식 + T 로 · 영월 호표 어긋남(E 자리에 e) 기록
· 계수표(T·L·E·q0·e·f·V1·V2·t) 칸을 기호로 읽어 불도저 식(8-2-1 · dozer_hourly_output) 그대로 Q1 47.92 ㎥/hr · Q = Q1 ÷ T 0.2 = 239.60 ㎡/hr
· 기종은 q0 3.2 · V1 40 · V2 46(1단)로 8-2-1 표에서 되짚음 → 무한궤도 19ton([주]① 과 맞음) · D 줄 1/Q hr/㎡
· 원문 대 실무 어긋남 기록 — 영월 호표 Q=576.95 는 E 자리에 e(0.96)를 넣은 값으로 정확히 역산됨 · 다른 실무엔 이 호표 없음 → 원문이 또렷해 원문 E=0.4 로 셈(공종 사유 한 줄)

새 제목 B·D 618원/㎡ · 바뀐 제목 0 · 프로젝트 내역 표토제거 9,954.32㎡ 가 금액으로 섬(+6,151,768) · 잴 시험 둘 빨강→초록 · 끄기 셋 빨강 · 전체 시험 1703 + 364 통과

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

113 lines
4.6 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.
"""B09 원가계산 — **표토제거 답외구간** 9-15-2 (2026-09-14 브레인 ㉰ 첫째).
B08 준비공 「표토제거」 줄이 부르는 코드인데 표가 계수표(T·L·E·q0·e·f·V1·V2·t)라 일위대가가 안 섰음.
q = q0 × e · ㎝ = L/V1 + L/V2 + t · Q1 = 60 × q × f × E / ㎝ (㎥/hr) · Q = Q1 / T (㎡/hr)
[주]① 무한궤도 불도저(19ton) · ③ 건설품셈 8-2-1 불도저 참조 → 불도저 식(`dozer_hourly_output`) 그대로 + T 로 나눔
기종은 표의 q0·V1·V2(1단)로 8-2-1 표에서 되짚음(`resolve_dozer`) — [주]① 19ton 과 맞는지 시험이 봄
⚠ 실무 영월 호표 Q=576.95 는 E 자리에 e(0.96)를 넣은 값 — 원문이 또렷해 원문 E 로 셈(까닭은 `KnownGaps`).
"""
from __future__ import annotations
import re
from decimal import Decimal
from typing import Any
CODE = "FP-09-15-02"
TABLE = "F0282"
_RE_SYMBOL = re.compile(r"^([A-Za-z]\d?)\s*\(")
_RE_GEAR = re.compile(r"(\d+)\s*단")
def _factors(node: dict[str, Any]):
"""(불도저 계수, T) — 칸이 모자라거나 기종이 안 좁혀지면 까닭 글."""
from B09_Estimation.B09_Estimation_MachineProductivity import parse_measure
from B09_Estimation.B09_Estimation_MachineProductivity_Dozer import DozerFactors, resolve_dozer
table = next((t for t in node.get("tables") or [] if t.get("pum_table_id") == TABLE), {})
values: dict[str, Decimal] = {}
gear = 1
for row in table.get("raw_row") or []:
found = _RE_SYMBOL.match(str(row[0]).strip()) if row else None
value = parse_measure(str(row[1])) if found and len(row) > 1 else None
if value is not None:
values[found.group(1)] = value
shift = _RE_GEAR.search(str(row[1]))
gear = int(shift.group(1)) if shift else gear
missing = [k for k in ("T", "L", "E", "q0", "e", "f", "V1", "V2") if k not in values]
if missing:
return f"9-15-2 표 칸 없음: {', '.join(missing)}"
machine = resolve_dozer(values["q0"], values["V1"], values["V2"], gear)
if machine is None:
return f"삽날 {values['q0']}㎥ · {values['V1']}/{values['V2']}m/분({gear}단) 으로 불도저가 안 좁혀짐"
factors = DozerFactors(
work_item_code=CODE,
blade_capacity_m3=values["q0"],
distance_factor=values["e"],
volume_factor=values["f"],
efficiency=values["E"],
haul_distance_m=values["L"],
forward_speed_m_min=values["V1"],
reverse_speed_m_min=values["V2"],
machine_code=machine[0],
machine_name=machine[1],
)
return factors, values["T"]
def topsoil_output(node: dict[str, Any] | None = None) -> tuple[Decimal, Decimal]:
"""(Q1 ㎥/hr, Q ㎡/hr) — 둘 다 소수 2자리로 확정한 뒤 씀(명세 7장)."""
from B09_Estimation.B09_Estimation_MachineProductivity import fix2
from B09_Estimation.B09_Estimation_MachineProductivity_Dozer import dozer_hourly_output
from B09_Estimation.B09_Estimation_ResourceAxis import load_work_item_master
if node is None:
node = next(n for n in load_work_item_master()["work_items"] if n["work_item_code"] == CODE)
found = _factors(node)
if isinstance(found, str):
raise ValueError(found)
factors, thickness = found
q1 = dozer_hourly_output(factors)
return q1, fix2(q1 / thickness)
def attach_topsoil_removal(build: Any, nodes_by_code: dict[str, dict[str, Any]]) -> None:
"""`B-FP-09-15-02` — 불도저 1/Q hr/㎡ 한 줄(D). 기계 층이 없거나 표가 달라지면 까닭만."""
from B09_Estimation.B09_Estimation_PriceBook import PriceKind, PriceTitle
node = nodes_by_code.get(CODE)
title_code = f"B-{CODE}"
if node is None or title_code in build.book.titles:
return
found = _factors(node)
if isinstance(found, str):
build.component_gaps[CODE] = found
return
factors, thickness = found
hourly = f"X-{factors.machine_code}"
if hourly not in build.book.titles:
build.component_gaps[CODE] = f"{factors.machine_name} 시간당 사용료가 안 섬"
return
q1, q = topsoil_output(node)
build.book.add_title(
PriceTitle(
code=title_code,
kind=PriceKind.UNIT_PRICE,
name=str(node.get("name") or CODE),
spec="표토제거",
unit="㎡",
)
)
build.book.add_output_detail(
title_code,
hourly,
Decimal(1) / q,
f"{factors.formula_text} → Q = Q1 {q1} ÷ T {thickness}m = {q} ㎡/hr"
" (산림품셈 9-15-2 [주]②③ · 건설 8-2-1)",
output=q,
)
if CODE in build.skipped:
build.skipped.remove(CODE)