Files
Aislo/resources/tester/test_b08_tree_waste.py
T
eomsangdonandClaude Opus 5 35ba45f572 feat(b08): 임목폐기물 뿌리 산정법 고르개 · 지침 π 3.14 · 분리발주 대상 아님 안내
- 환경친화적인 도로건설 지침 부록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
2026-09-14 01:09:23 +09:00

80 lines
3.5 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.
"""임목폐기물 처리 — 톤으로 서는가 · 경비라 내역에 안 서는가 (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"]