- 환경친화적인 도로건설 지침 부록1 확인: 뿌리분 체적 V = 0.3927D³ × 1,300㎏/㎥ — 고르개로 넣음(기본은 분배비 15/85) - 뿌리분 직경 D 는 지침 표기 「뿌리분의 직경(DBH, m)」이 모호해 흉고직경과 따로 받음 - 지상부 식을 지침 원문대로 π 3.14 로 셈 — 실정보고 예 130.40㎥ 와 바로 맞음 - 분리발주 칸 밑·사유에 「임목폐기물은 분리발주 대상 아님(환경부 회신)」 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BW6Jdsh18WPtYUR6THZJqn
80 lines
3.5 KiB
Python
80 lines
3.5 KiB
Python
"""임목폐기물 처리 — 톤으로 서는가 · 경비라 내역에 안 서는가 (2026-09-14 판정).
|
||
|
||
⚠ 산식 — 한국건설기술연구원(2012) 3.2: WA = 0.5 × π × (B/2)² × h × 1.3 × W1 × N · WR = WA × 15/85.
|
||
⚠ 실정보고 예(p26) 「V1 = 0.5×π×(0.16/2)²×10.0×1.3×134/1000×7,450 = 130.40㎥」 로
|
||
지상부 부피를 대 봄.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
import pytest
|
||
|
||
ROOT = Path(__file__).resolve().parents[2]
|
||
sys.path.insert(0, str(ROOT))
|
||
|
||
from B08_Quantity.B08_Quantity_Engine_Preparation_TreeWaste import ( # noqa: E402
|
||
tree_waste_rows,
|
||
tree_waste_tons,
|
||
)
|
||
|
||
SLOPE = {"tree_removal_fill": 4000.0, "tree_removal_cut": 3450.0} # 7,450㎡
|
||
FIELD = {"stems_per_1000m2": 134, "dbh_cm": 16, "height_m": 10.0, "unit_weight_kg_m3": 1000}
|
||
|
||
|
||
def test_지상부는_실정보고_부피와_같다() -> None:
|
||
"""W1 = 1,000㎏/㎥ 로 두면 지상부 톤 = 지상부 부피(㎥) — 실정보고 130.40㎥."""
|
||
above, root = tree_waste_tons(7450.0, FIELD)
|
||
# 지침 부록1 식·실정보고 모두 π 를 3.14 로 적음 — 그대로 셈해 130.40 과 같다.
|
||
assert above == pytest.approx(130.40, abs=0.01)
|
||
assert root == pytest.approx(above * 15 / 85)
|
||
|
||
|
||
def test_칸이_비면_톤이_안_서고_무엇이_빈지_적는다() -> None:
|
||
row = tree_waste_rows(SLOPE, {**FIELD, "height_m": None})[0]
|
||
assert row["amount"] is None and "평균 수고" in row["reason"]
|
||
assert row["reference_amount"] == 7450.0
|
||
|
||
|
||
def test_단가가_없으면_금액이_안_서고_있으면_미확정() -> None:
|
||
no_price = tree_waste_rows(SLOPE, FIELD)[0]
|
||
assert no_price["unit"] == "톤" and no_price["amount"] > 0
|
||
assert no_price["amount_krw"] is None and no_price["unconfirmed"] == 0
|
||
priced = tree_waste_rows(SLOPE, FIELD, 60000)[0]
|
||
assert priced["unconfirmed"] == 1
|
||
assert priced["amount_krw"] == int(priced["amount"] * 60000)
|
||
|
||
|
||
def test_톤_경계를_알리고_분리발주_대상_아님을_적는다() -> None:
|
||
big = tree_waste_rows(SLOPE, FIELD)[0] # 약 153톤
|
||
assert "분리발주 대상 아님" in big["reason"] and "환경부" in big["reason"]
|
||
small = tree_waste_rows({"tree_removal_fill": 500.0}, FIELD)[0] # 약 10톤
|
||
assert 5 <= small["amount"] < 100 and "5톤 이상" in small["reason"]
|
||
|
||
|
||
def test_경비라_내역에_안_서고_제외_사유가_간다() -> None:
|
||
from B08_Quantity.B08_Quantity_Engine_Handoff import build_handoff
|
||
from B08_Quantity.B08_Quantity_Engine_Preparation import build_table
|
||
|
||
table = build_table(SLOPE, tree_waste=FIELD, tree_waste_unit_price_krw_per_ton=60000)
|
||
handed = {r["name"]: r for r in build_handoff(preparation_table=table)["work_items"]}
|
||
row = handed["임목폐기물 처리"]
|
||
assert row["in_bill"] is False and row["blocked_kind"] is None
|
||
assert "경비" in row["in_bill_reason"]
|
||
|
||
|
||
def test_뿌리분_체적법은_D_세제곱_곱하기_1300() -> None:
|
||
"""지침 부록1 — V = 0.3927 D³ · 뿌리분 중량 = V × 1,300㎏/㎥ (본수만큼)."""
|
||
field = {**FIELD, "root_ball_diameter_m": 0.5}
|
||
above, root = tree_waste_tons(7450.0, field, "root_ball")
|
||
stems = 134 / 1000 * 7450
|
||
assert root == pytest.approx(0.3927 * 0.5**3 * 1300 * stems / 1000)
|
||
assert above == pytest.approx(tree_waste_tons(7450.0, FIELD)[0])
|
||
|
||
|
||
def test_체적법인데_뿌리분_직경이_비면_안_선다() -> None:
|
||
row = tree_waste_rows(SLOPE, FIELD, root_method="root_ball")[0]
|
||
assert row["amount"] is None and "뿌리분 직경" in row["reason"]
|