chore(rebar): Pier_T_rebar 작업 폴더 초기 구성 및 외부 의존 파일 통합
- Code\rebar_pipeline, review_pipeline, pier-program을 정션에서 실제 폴더로 전환 - Code\rhino_mcp 및 CLAUDE_rhino배경.md 추가, .mcp.json은 PYTHONPATH로 새 위치 사용 - pier-program 내부 .git 제거(이력은 backup\pier-program_20261028.bundle에 보관) - worktree 가이드 반영(CLAUDE.local.md, HANDOFF.md) - D:\Rhino-MCP 경로 참조를 새 위치로 치환 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
964a841de1
3978 files changed
+58013356
No files matched your search
@@ -0,0 +1,410 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""운곡교 P1 철근재료표 재작성 (모델 기준) + 도면 재료표(3.dxf)와 비교 + 비고.
|
||||
|
||||
사용: python -I make_model_bom.py <pylib경로> <출력xlsx>
|
||||
- pylib경로 : rhino3dm, openpyxl, ezdxf 가 설치된 폴더 (pip install --target)
|
||||
- 입력(고정): 3.dxf(도면 재료표), 운곡교p1.3dm(모델)
|
||||
"""
|
||||
import sys, re, collections, statistics as st
|
||||
sys.path.insert(0, sys.argv[1])
|
||||
import ezdxf, rhino3dm
|
||||
from openpyxl import Workbook
|
||||
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
|
||||
from openpyxl.utils import get_column_letter
|
||||
|
||||
BASE = r"D:\★Pier 자료\심수석님 검토 파일\철근 모델랑 운곡교P1_바이브코딩"
|
||||
DXF3 = BASE + r"\3.dxf"
|
||||
M3DM = BASE + r"\운곡교p1.3dm"
|
||||
OUT = sys.argv[2]
|
||||
|
||||
UNIT = {"32": 6.230, "29": 5.040, "25": 3.980, "22": 3.040, "19": 2.250, "16": 1.560} # kg/m (도면 표)
|
||||
def surcharge(d): # 도면: D22 이상 6%, 미만 3%
|
||||
return 1.06 if int(d) >= 22 else 1.03
|
||||
|
||||
# ---------------------------------------------------------------- 도면 재료표 파싱
|
||||
doc = ezdxf.readfile(DXF3)
|
||||
texts = []
|
||||
for e in doc.modelspace():
|
||||
if e.dxftype() == "TEXT":
|
||||
texts.append((e.dxf.insert.x, e.dxf.insert.y, e.dxf.text))
|
||||
elif e.dxftype() == "MTEXT":
|
||||
texts.append((e.dxf.insert.x, e.dxf.insert.y, e.plain_text()))
|
||||
COLS = [("num", 1900, 2500), ("shape", 2500, 3500), ("dia", 3500, 4400), ("n", 4400, 5300),
|
||||
("A", 5300, 6700), ("B", 6700, 8200), ("C", 8200, 9700), ("D", 9700, 11200),
|
||||
("E", 11200, 12700), ("R", 12700, 14200), ("X1", 14200, 15600), ("X2", 15600, 17100),
|
||||
("J", 17100, 18800), ("L", 18800, 20500), ("tot", 20500, 21400)]
|
||||
OFF = 23750
|
||||
rowsraw = collections.defaultdict(dict)
|
||||
for x, y, t in texts:
|
||||
if y > 30300 or y < 20000:
|
||||
pass
|
||||
tab = "SD500" if x >= 25000 else "SD400"
|
||||
xx = x - OFF if tab == "SD500" else x
|
||||
for name, lo, hi in COLS:
|
||||
if lo <= xx < hi:
|
||||
rowsraw[(tab, round(y / 10) * 10)].setdefault(name, []).append(t.strip())
|
||||
break
|
||||
drw = {} # mark -> dict
|
||||
order_draw = {"SD400": [], "SD500": []}
|
||||
last = {}
|
||||
for (tab, y), cells in sorted(rowsraw.items(), key=lambda kv: (kv[0][0], -kv[0][1])):
|
||||
num = (cells.get("num") or [""])[0]
|
||||
if not re.match(r"^[A-Z]{1,2}\d+(-\d+)?$", num) or y < 20000 or y > 30000:
|
||||
continue
|
||||
r = {"tab": tab, "mark": num}
|
||||
for k in ("shape", "dia", "n", "A", "B", "C", "D", "E", "R", "X1", "X2", "J", "L", "tot"):
|
||||
r[k] = (cells.get(k) or [None])[0]
|
||||
for k in ("shape", "dia"):
|
||||
if r[k] == '"':
|
||||
r[k] = last[(tab, k)]
|
||||
else:
|
||||
last[(tab, k)] = r[k]
|
||||
drw[num] = r
|
||||
order_draw[tab].append(num)
|
||||
|
||||
def f(s):
|
||||
try:
|
||||
return float(s)
|
||||
except Exception:
|
||||
return None
|
||||
for m, r in drw.items():
|
||||
r["n_i"] = int(r["n"]) if r["n"] and r["n"].isdigit() else None
|
||||
r["L_f"] = f(r["L"]) if r["L"] else None
|
||||
r["tot_f"] = f(r["tot"]) if r["tot"] else None
|
||||
r["dia_n"] = re.sub(r"\D", "", r["dia"] or "")
|
||||
dims = []
|
||||
for k in ("A", "B", "C", "D", "E", "R", "X1", "X2", "J"):
|
||||
if r[k]:
|
||||
dims.append("%s=%s" % (k, r[k]))
|
||||
r["dims"] = " ".join(dims)
|
||||
|
||||
# ---------------------------------------------------------------- 모델
|
||||
m3 = rhino3dm.File3dm.Read(M3DM)
|
||||
lay = {l.Index: l.FullPath for l in m3.Layers}
|
||||
|
||||
def curve_len(c, n=3000):
|
||||
d = c.Domain
|
||||
p0 = c.PointAt(d.T0)
|
||||
s = 0.0
|
||||
for i in range(1, n + 1):
|
||||
p = c.PointAt(d.T0 + (d.T1 - d.T0) * i / n)
|
||||
s += ((p.X - p0.X) ** 2 + (p.Y - p0.Y) ** 2 + (p.Z - p0.Z) ** 2) ** 0.5
|
||||
p0 = p
|
||||
return s
|
||||
|
||||
bars = collections.defaultdict(dict) # mark -> occ -> dict
|
||||
for o in m3.Objects:
|
||||
L = lay.get(o.Attributes.LayerIndex, "")
|
||||
if not L.startswith("P1::철근"):
|
||||
continue
|
||||
g = o.Geometry
|
||||
nm = o.Attributes.Name or ""
|
||||
if isinstance(g, rhino3dm.Brep):
|
||||
us = dict(o.Attributes.GetUserStrings())
|
||||
mk = us.get("mark")
|
||||
occ = us.get("occurrence")
|
||||
b = bars[mk].setdefault(occ, {})
|
||||
b.update(us=us, name=nm, layer=L)
|
||||
elif L.endswith("중심선"):
|
||||
p = nm.split("-")
|
||||
if len(p) >= 4 and p[1] == "RC":
|
||||
mk = p[2]
|
||||
occ = "-".join(p[3:])
|
||||
bars[mk].setdefault(occ, {})["cl"] = curve_len(g)
|
||||
|
||||
# ---------------------------------------------------------------- 마크별 모델 집계
|
||||
def num(s):
|
||||
try:
|
||||
return float(s)
|
||||
except Exception:
|
||||
return 0.0
|
||||
mod = {}
|
||||
for mk, d in bars.items():
|
||||
if mk is None:
|
||||
continue
|
||||
items = [b for b in d.values() if "us" in b]
|
||||
Ls = [num(b["us"].get("design_length")) + num(b["us"].get("coupler_length")) for b in items]
|
||||
cls = [b.get("cl") for b in items if b.get("cl") is not None]
|
||||
pieces = len(items)
|
||||
# P3·P4 는 반원 2조각 = 1링 (재료표 개수 = 링 수)
|
||||
half = mk in ("P3", "P4")
|
||||
n_tab = pieces // 2 if half else pieces
|
||||
mod[mk] = dict(pieces=pieces, n=n_tab, tot=sum(Ls) / 1000.0, Lmean=(sum(Ls) / n_tab / 1000.0) if n_tab else 0,
|
||||
Lmin=min(Ls) / 1000.0, Lmax=max(Ls) / 1000.0, cl=sum(cls) / 1000.0 if cls else None,
|
||||
dia=items[0]["us"].get("dia"), shape=items[0]["us"].get("shape"), items=items, half=half)
|
||||
|
||||
# C9~C13 : 도면 폭(1.407) 기준으로 환산한 길이 -> 폭 영향 / (최대+최소)/2 근사 영향 분리
|
||||
def width_effect(mk):
|
||||
ex = 0.0
|
||||
for b in mod[mk]["items"]:
|
||||
w = num(b["us"].get("w_model"))
|
||||
if w:
|
||||
ex += 2 * (1407.0 - w)
|
||||
return ex / 1000.0
|
||||
|
||||
# ---------------------------------------------------------------- 비고 사전
|
||||
NOTE = {}
|
||||
KDS = {}
|
||||
def n_(mk, s):
|
||||
NOTE[mk] = s
|
||||
for mk in ("C1", "C4"):
|
||||
n_(mk, "L 도면 14.490 ↔ 모델 14.489 (0.001 m, 각진 치수 반올림). [D-8] 도면 내부: 재료표 다리각 98°/96° ↔ 정면도 96.4°.")
|
||||
for mk in ("C2", "C5"):
|
||||
n_(mk, "[D-7] 도면 내부: 재료표 L 12.081 ↔ 치수 합 A+B+C 12.082 (모델 12.082, 1 mm). [D-8] 다리각 98°/96° ↔ 정면도 96.4°.")
|
||||
for mk in ("C3", "C6"):
|
||||
n_(mk, "개수·길이 일치. [D-8] 도면 내부: 재료표 다리각 98°/96° ↔ 정면도 96.4°.")
|
||||
for mk in ("C7", "C8"):
|
||||
n_(mk, "개수·길이 일치. [D-9] 도면 내부: 재료표 B·D·R·X·J 빈칸 → 모델은 정면도 R 12,100(하면 R 12,000 동심)·평면부 EL 40.511 로 작성. [D-10] 다리 650 은 정면도 겹침으로 확인 불가.")
|
||||
n_("C9", "[D-4] 스터럽 폭 도면 재료표 1.407 ↔ 단면 작도 1.160/1.740/1.160 (모델은 작도값). [D-6] 재료표 L = (최대+최소)/2 근사 → 모델은 철근마다 개별 변길이. [M-5] 길이 산식 +54.3 규칙은 변하는 치수에만 적용.")
|
||||
n_("C10", "[D-4] 스터럽 폭 1.407 ↔ 작도 1.160 (모델은 작도값). 변길이 b=2.998 고정이라 [D-6] 근사 영향 없음. [M-5] +54.3 규칙.")
|
||||
n_("C11", "[D-4] 스터럽 폭 1.407 ↔ 작도 1.160/1.740/1.160. [D-6] (최대+최소)/2 근사 vs 모델 개별 변길이. [M-5] +54.3 규칙.")
|
||||
n_("C12", "[D-5] 수평폐합근 폭 1.407 ↔ 작도 1.106/1.686/1.106 (모델은 작도값). [D-6] 길이 (최대+최소)/2 근사 vs 모델 개별. [M-5] +54.3 규칙.")
|
||||
n_("C13", "[D-5] 수평폐합근 폭 1.407 ↔ 작도 1.106/1.686/1.106. [D-6] (최대+최소)/2 근사 vs 모델 개별. [M-5] +54.3 규칙.")
|
||||
n_("C14", "개수·길이 일치. [M-1] 모델 최하단 점 2개(x=±1913, ±2913)가 도면보다 350 mm 높음(근거 기록 없음). [M-2] 훅 방향 도면 표기 없음 — 모델은 아래로 가정, 도면 점 위치로는 하부 점은 위·상부 점은 아래일 가능성(추정, 간접 확인).")
|
||||
n_("C15", "개수·길이 일치.")
|
||||
n_("C16", "개수·길이 일치. 훅 방향·정면도 좌측 끝은 도면 직접 표기 없음 (재료표 L 5.611 = 5.261 + 0.350 한쪽 훅으로 간접 확인).")
|
||||
for mk in ("S1", "S2"):
|
||||
n_(mk, "개수·길이 일치. [M-6] 높이 기준: 도면(D2 전열 상세)은 대좌 상면 -50 ↔ 모델은 코핑 상면 -50 (139~164 mm 낮음). [M-7] 층 순서: 도면은 S2 점이 S1 선 위 ↔ 모델은 S1 위·S2 50 mm 아래(모델 src '추론').")
|
||||
n_("UA1", "[M-13] 모델에 없음(0본). 도면은 기초 조립용 '배치간격 예시도'(1.0×1.2 예시)라 수량 73본 근거 미확인. 도면 총길이 377.848 m 가 모델에는 빠짐.")
|
||||
n_("P1", "개수·길이 일치(모델 17.247). [D-20] 주철근 상단: 정면도 EL 43.011 ↔ 재료표 EL 43.061 (모델은 재료표). [D-21] 반지름 치수·선 875 ↔ 점 870.5 (모델은 점).")
|
||||
n_("P2", "개수·길이 일치. [D-20] 주철근 상단: 정면도 EL 40.411 ↔ 재료표 EL 43.061 (코핑 정착 2.65 m 차이, 모델은 재료표). [D-21] 반지름 830 ↔ 점 825.5 (모델은 점).")
|
||||
n_("P3", "모델은 반원 2조각(72개)=1링, 재료표 개수 36 = 링 수로 환산. 링 길이 일치. [M-12] 단마다 90° 엇갈린 이음 각도는 도면에 기준 없음(모델 가정, 간접).")
|
||||
n_("P4", "모델은 반원 2조각(18개)=1링(커플러 포함 L 5.636), 재료표 개수 9 = 링 수로 환산. [D-23] 라벨 H19 @100 ↔ 치수 8@120=960 (모델 @120 — 구조계산서 확인 사항, E-01). [M-12] 커플러 각도 도면에 없음.")
|
||||
n_("P5", "개수·길이 일치(커플러 65 포함 L 5.636). [D-23] 라벨 @100 ↔ 치수 22@120=2,640 (모델 @120, 구조계산서 확인). [D-22] 링 점 902 ↔ R 897. [M-12] 커플러 각도 도면에 없음.")
|
||||
n_("P6", "개수·길이 일치. 정면도 선 반길이 875 ↔ 모델 직선 894 (한쪽 19 mm, 경미).")
|
||||
n_("B1", "개수·길이 일치. [M-8] 모델 교차 철근 간격 54~56 mm ↔ 도면 두 뷰 모두 27~28 mm(점 값을 서로 다른 뷰에서 가져옴: B1 2073 ↔ B5 2127). [D-15] 같은 철근을 점/선으로 그린 높이가 정면도·측면도에서 27~28 mm 다름.")
|
||||
n_("B2", "개수·길이 일치. [M-9] 다리 끝 +2072 (도면 선 기준 +2100). [D-15] 점/선 높이 27~28 mm 차.")
|
||||
n_("B3", "개수·길이 일치. [D-17] 도면 중앙 치수 33@250+450(36본) ↔ 점·재료표 38본 (모델 38).")
|
||||
n_("B4", "개수·길이 일치. [M-8] 하면 교차 철근 간격 영향(B2·B4).")
|
||||
n_("B5", "개수·길이 일치. [M-9] 모델 중심 73 아래(순피복 58.5 mm, 도면 표기 상면 100, 선행 E-06). [M-8] 교차 철근 간격.")
|
||||
n_("B6", "개수·길이 일치. [M-9] 다리 끝 +2128 (도면 선 기준 +2100, 선행 E-06 피복 57.5). [M-8] 교차 철근 간격.")
|
||||
n_("B7", "개수·길이 일치. [M-8] 교차 철근 간격.")
|
||||
n_("B8", "개수·길이 일치.")
|
||||
n_("B9", "[M-01 선행] 도면 재료표 개수 빈칸. 라벨 H16 @500 + 격자 34×29(@250) 체크무늬 986/2 = 493본을 총길이 1,062.415 ÷ L 2.155 로 역산(간접 확인) → 모델 493본, 총길이 일치. 기초 내 위치 확정은 도면에 없음.")
|
||||
n_("B10", "개수·길이 일치. [D-18] 치수 9@200(10단) ↔ 점·재료표 9단 (모델 9단), 표 9.198×7.644 ↔ 점 ±4593/±3818.")
|
||||
n_("B11", "개수·길이 일치. B11·B12 4단 높이(z 550)와 위치 일치.")
|
||||
n_("B12", "[D-16] 도면 내부: 재료표 34본 ↔ 작도 28곳 (선행 E-05, 6본 과다 또는 작도 누락, 46.8 m). 모델은 작도 28본을 따름 → 6본 차이.")
|
||||
KDS["C1"] = KDS["C2"] = KDS["C3"] = KDS["C4"] = KDS["C5"] = KDS["C6"] = "[M-4] 내면반지름 여유 거의 없음: 모델 내면지름 101.5 mm ↔ 3.5db 100.1/101.5 mm (H29, 표 4.5-1). 도면에 내면반지름 표기 없음."
|
||||
KDS["C7"] = KDS["C8"] = "[M-3] 굽힘 4곳이 각진 연결(내면반지름 미반영) → 내면지름 3.5db(H29 100.1 mm) 미적용 (KDS 24 14 21 표 4.5-1)."
|
||||
KDS["P3"] = "[M-11] 갈고리 연장 모델 86.7 mm < 5db 95 mm (KDS 4.5.4), 내면지름 66.5 mm < 공칭 기준 66.85 mm (0.35 mm 부족)."
|
||||
KDS["B1"] = KDS["B2"] = "[M-10] S25 굽힘: 모델 내면지름 87.6 mm < 공칭 기준 88.9 mm (1.3 mm 부족, 3.5db)."
|
||||
KDS["B3"] = KDS["B7"] = KDS["B10"] = "[M-10] S19 굽힘: 모델 내면지름 66.6 mm < 공칭 기준 66.9 mm (0.3 mm 부족, 3.5db)."
|
||||
|
||||
# ---------------------------------------------------------------- 엑셀
|
||||
wb = Workbook()
|
||||
ws = wb.active
|
||||
ws.title = "철근재료표_모델기준"
|
||||
thin = Side(style="thin", color="999999")
|
||||
BR = Border(left=thin, right=thin, top=thin, bottom=thin)
|
||||
HF = PatternFill("solid", fgColor="D9D9D9")
|
||||
GREEN = PatternFill("solid", fgColor="E2F0D9")
|
||||
ORANGE = PatternFill("solid", fgColor="FCE4D6")
|
||||
YEL = PatternFill("solid", fgColor="FFF2CC")
|
||||
GREY = PatternFill("solid", fgColor="EDEDED")
|
||||
heads = ["번호", "형식", "직경", "도면 치수(재료표 표기)", "도면 개수", "도면 L(m)", "도면 총길이(m)",
|
||||
"모델 개수", "모델 L 평균(m)", "모델 L 범위(m)", "모델 총길이(m)\n(재료표 방식: 각진 치수+커플러)", "모델 중심선 실총길이(m)\n(굽힘 반영)",
|
||||
"개수 차이\n(모델−도면)", "총길이 차이(m)\n(모델−도면)", "판정", "단위중량\n(kg/m)", "도면 총중량(t)", "모델 총중량(t)",
|
||||
"비고 (도면↔모델 차이 · 도면 내부 불일치)", "비고 3 (KDS 24 14 21 기준 상이)"]
|
||||
ws.append(["운곡교 P1 (완도방향) 철근재료표 — 모델(운곡교p1.3dm) 기준 재작성 + 도면 재료표(3.dxf) 대비 비고"])
|
||||
ws.append(["작성 2026-10-16 · 검토 미확정 · 모델 L = 철근 모델의 재료표식 길이(각진 치수 합 + 커플러), 중심선 실길이는 굽힘 반영값이라 짧음 · "
|
||||
"[M-n]=도면↔모델, [D-n]=도면 내부, KDS=기준↔모델 (검토기록_최종.md 번호)"])
|
||||
ws.append([])
|
||||
ws.append(heads)
|
||||
HR = 4
|
||||
for c in range(1, len(heads) + 1):
|
||||
cell = ws.cell(row=HR, column=c)
|
||||
cell.font = Font(bold=True)
|
||||
cell.fill = HF
|
||||
cell.border = BR
|
||||
cell.alignment = Alignment(horizontal="center", vertical="center", wrap_text=True)
|
||||
|
||||
def status(dn, mn, dt, mt, dL=None, mL=None):
|
||||
if dn is None:
|
||||
return "도면 개수 빈칸"
|
||||
if mn != dn:
|
||||
return "차이"
|
||||
if dL is not None and mL is not None and abs(mL - dL) <= 0.0015: # 반올림 수준(≤1.5 mm/본)
|
||||
return "일치" if abs(mt - dt) <= 0.0005 else "일치(경미)"
|
||||
if abs(mt - dt) > 0.01:
|
||||
return "차이"
|
||||
return "일치(경미)" if abs(mt - dt) > 0.0005 else "일치"
|
||||
|
||||
summary_rows = [] # 소계 계산용
|
||||
bw_rows = [] # C9~C13 차이 분해
|
||||
all_rows = [] # 요약용 (마크, 판정, 개수차, 총길이차)
|
||||
sections = [("SD400", "SD400 (코핑 C·S, 기둥 P3~P6, 기초 B9, UA1)"), ("SD500", "SD500 (기둥 P1·P2, 기초 B1~B8, B10~B12)")]
|
||||
tot_all = {}
|
||||
for tab, title in sections:
|
||||
r0 = ws.max_row + 1
|
||||
ws.append([title])
|
||||
ws.cell(row=r0, column=1).font = Font(bold=True, size=12)
|
||||
# 직경별 묶음 (도면 순서 유지, 직경 큰 순)
|
||||
marks = order_draw[tab]
|
||||
groups = collections.OrderedDict()
|
||||
for mk in marks:
|
||||
groups.setdefault(drw[mk]["dia_n"], []).append(mk)
|
||||
grp_tot = []
|
||||
for dia, mks in groups.items():
|
||||
sd = drw[mks[0]]["dia"]
|
||||
gd = gm = gdw = gmw = 0.0
|
||||
gcl = 0.0
|
||||
for mk in mks:
|
||||
d = drw[mk]
|
||||
mm = mod.get(mk)
|
||||
mn = mm["n"] if mm else 0
|
||||
mt = mm["tot"] if mm else 0.0
|
||||
mcl = mm["cl"] if mm and mm["cl"] is not None else None
|
||||
dn = d["n_i"]
|
||||
dt = d["tot_f"] or 0.0
|
||||
Lr = ("%.3f ~ %.3f" % (mm["Lmin"], mm["Lmax"])) if mm and abs(mm["Lmax"] - mm["Lmin"]) > 1e-6 else ""
|
||||
stt = status(dn, mn, dt, mt, d["L_f"], mm["Lmean"] if mm else None)
|
||||
note = NOTE.get(mk, "")
|
||||
if mk in ("C9", "C10", "C11", "C12", "C13") and mm:
|
||||
we = width_effect(mk)
|
||||
diff = mt - dt
|
||||
note += " ▶ 총길이 차이 %+.3f m = 폭 영향 %+.3f m + 근사·기타 %+.3f m." % (diff, -we, diff + we)
|
||||
note += " 중심선 실길이는 겹침 이음(미작도, 길이만 반영)이 빠져 재료표 길이보다 짧음."
|
||||
bw_rows.append((mk, dt, mt, diff, -we, diff + we))
|
||||
if stt in ("차이", "도면 개수 빈칸") and mm is None:
|
||||
pass
|
||||
unit = UNIT[dia]
|
||||
row = [mk, d["shape"], d["dia"], d["dims"], dn if dn is not None else "(빈칸)", d["L_f"], dt,
|
||||
mn, round(mm["Lmean"], 3) if mm else 0, Lr, round(mt, 3), round(mcl, 3) if mcl is not None else None,
|
||||
(mn - dn) if dn is not None else None, round(mt - dt, 3), stt, unit,
|
||||
round(dt * unit / 1000, 3), round(mt * unit / 1000, 3), note, KDS.get(mk, "")]
|
||||
ws.append(row)
|
||||
all_rows.append((mk, stt, (mn - dn) if dn is not None else None, mt - dt))
|
||||
rr = ws.max_row
|
||||
fill = {"일치": GREEN, "일치(경미)": GREEN, "차이": ORANGE, "도면 개수 빈칸": YEL}[stt]
|
||||
for c in range(1, len(heads) + 1):
|
||||
cell = ws.cell(row=rr, column=c)
|
||||
cell.border = BR
|
||||
cell.alignment = Alignment(vertical="top", wrap_text=(c in (4, 19, 20)))
|
||||
if c in (15,):
|
||||
cell.fill = fill
|
||||
ws.cell(row=rr, column=20).font = Font(color="FF00FF")
|
||||
for c in (7, 11, 12, 14):
|
||||
ws.cell(row=rr, column=c).number_format = "0.000"
|
||||
for c in (17, 18):
|
||||
ws.cell(row=rr, column=c).number_format = "0.000"
|
||||
gd += dt; gm += mt; gcl += (mcl or 0.0)
|
||||
gdw = gd * UNIT[dia] / 1000
|
||||
gmw = gm * UNIT[dia] / 1000
|
||||
sc = surcharge(dia)
|
||||
grp_tot.append((sd, gd, gm, gcl, gdw, gmw, sc))
|
||||
ws.append(["소 계", "", sd, "", "", "", round(gd, 3), "", "", "", round(gm, 3), round(gcl, 3), "", round(gm - gd, 3), "", UNIT[dia],
|
||||
round(gdw, 3), round(gmw, 3), "할증 %d%% 적용 시: 도면 %.3f t / 모델 %.3f t" % (round((sc - 1) * 100), gdw * sc, gmw * sc), ""])
|
||||
rr = ws.max_row
|
||||
for c in range(1, len(heads) + 1):
|
||||
ws.cell(row=rr, column=c).fill = GREY
|
||||
ws.cell(row=rr, column=c).font = Font(bold=True)
|
||||
ws.cell(row=rr, column=c).border = BR
|
||||
for c in (7, 11, 12, 14, 17, 18):
|
||||
ws.cell(row=rr, column=c).number_format = "0.000"
|
||||
sd_ = sum(g[1] for g in grp_tot); sm_ = sum(g[2] for g in grp_tot); scl = sum(g[3] for g in grp_tot)
|
||||
dw = sum(g[4] for g in grp_tot); mw = sum(g[5] for g in grp_tot)
|
||||
dws = sum(g[4] * g[6] for g in grp_tot); mws = sum(g[5] * g[6] for g in grp_tot)
|
||||
ws.append(["총 계 (%s)" % tab, "", "", "", "", "", round(sd_, 3), "", "", "", round(sm_, 3), round(scl, 3), "", round(sm_ - sd_, 3), "", "",
|
||||
round(dw, 3), round(mw, 3), "할증 적용 총중량: 도면 %.3f t / 모델 %.3f t (모델 − 도면 %+.3f t)" % (dws, mws, mws - dws), ""])
|
||||
rr = ws.max_row
|
||||
for c in range(1, len(heads) + 1):
|
||||
ws.cell(row=rr, column=c).fill = HF
|
||||
ws.cell(row=rr, column=c).font = Font(bold=True)
|
||||
ws.cell(row=rr, column=c).border = BR
|
||||
for c in (7, 11, 12, 14, 17, 18):
|
||||
ws.cell(row=rr, column=c).number_format = "0.000"
|
||||
tot_all[tab] = (sd_, sm_, dw, mw, dws, mws)
|
||||
ws.append([])
|
||||
|
||||
widths = [8, 7, 7, 34, 8, 9, 11, 8, 10, 14, 17, 17, 9, 12, 12, 9, 11, 11, 70, 48]
|
||||
for i, w in enumerate(widths, 1):
|
||||
ws.column_dimensions[get_column_letter(i)].width = w
|
||||
ws.row_dimensions[HR].height = 48
|
||||
ws.freeze_panes = "B5"
|
||||
|
||||
# ---------------------------------------------------------------- 시트 0: 요약
|
||||
w0 = wb.create_sheet("요약", 0)
|
||||
w0.append(["도면 재료표 ↔ 모델 재료표 차이 요약 (운곡교 P1, 2026-10-16 작성, 검토 미확정)"])
|
||||
w0.append([])
|
||||
w0.append(["구분", "마크", "도면 총길이(m)", "모델 총길이(m)", "차이(m)", "내용"])
|
||||
dd = {m: drw[m]["tot_f"] or 0.0 for m in drw}
|
||||
mm_ = {m: mod[m]["tot"] if m in mod else 0.0 for m in drw}
|
||||
w0.append(["개수·길이 불일치", "UA1", dd["UA1"], mm_["UA1"], round(mm_["UA1"] - dd["UA1"], 3), "모델에 없음 (M-13). 도면은 배치 예시도, 73본 근거 미확인"])
|
||||
w0.append(["개수·길이 불일치", "B12", dd["B12"], mm_["B12"], round(mm_["B12"] - dd["B12"], 3), "도면 재료표 34본 ↔ 작도·모델 28본 (D-16, 6본)"])
|
||||
sw = sum(r[4] for r in bw_rows); so = sum(r[5] for r in bw_rows)
|
||||
w0.append(["길이 불일치", "C9~C13", round(sum(r[1] for r in bw_rows), 3), round(sum(r[2] for r in bw_rows), 3), round(sum(r[3] for r in bw_rows), 3),
|
||||
"스터럽·폐합근: 폭 1.407↔작도값 영향 %+.3f m + (최대+최소)/2 근사 등 %+.3f m (D-4/D-5/D-6)" % (sw, so)])
|
||||
for r in bw_rows:
|
||||
w0.append([" (세부)", r[0], round(r[1], 3), round(r[2], 3), round(r[3], 3), "폭 영향 %+.3f / 근사·기타 %+.3f" % (r[4], r[5])])
|
||||
w0.append(["개수 표기 차이(합계 일치)", "P3·P4", "", "", 0, "모델은 반원 2조각 = 1링 (P3 72개, P4 18개), 재료표 개수는 링 수"])
|
||||
w0.append(["개수 빈칸 보완", "B9", dd["B9"], mm_["B9"], round(mm_["B9"] - dd["B9"], 3), "도면 개수 빈칸 → 총길이 역산 493본(체크무늬)이 모델과 일치 (간접 확인)"])
|
||||
w0.append([])
|
||||
w0.append(["재료표 합계", "", "도면", "모델", "차이", ""])
|
||||
for tab, v in tot_all.items():
|
||||
w0.append([tab + " 총길이(m)", "", round(v[0], 3), round(v[1], 3), round(v[1] - v[0], 3), ""])
|
||||
w0.append([tab + " 총중량(t)", "", round(v[2], 3), round(v[3], 3), round(v[3] - v[2], 3), "할증 적용: 도면 %.3f / 모델 %.3f" % (v[4], v[5])])
|
||||
w0.append([])
|
||||
w0.append(["그 외 마크는 개수·길이가 일치(반올림 ≤1.5 mm/본 포함)이며 위치·형상·굽힘 차이는 '철근재료표_모델기준' 시트 비고와 비고 3(KDS)에 적었음."])
|
||||
for i, w in enumerate([24, 10, 16, 16, 12, 100], 1):
|
||||
w0.column_dimensions[get_column_letter(i)].width = w
|
||||
for c in range(1, 7):
|
||||
w0.cell(row=3, column=c).font = Font(bold=True); w0.cell(row=3, column=c).fill = HF
|
||||
w0.cell(row=1, column=1).font = Font(bold=True, size=12)
|
||||
|
||||
# ---------------------------------------------------------------- 시트 2: 비고 코드 설명
|
||||
w2 = wb.create_sheet("비고코드설명")
|
||||
w2.append(["코드", "구분", "내용"])
|
||||
codes = [
|
||||
("M-1", "도면↔모델", "C14 최하단 점 2개 모델이 350 mm 높음(근거 없음)"),
|
||||
("M-2", "미확인", "C14 훅 방향 도면 표기 없음 (모델은 아래 가정)"),
|
||||
("M-3", "기준↔모델", "C7·C8 굽힘 4곳 각진 연결 (내면반지름 미반영)"),
|
||||
("M-4", "기준↔모델", "C1~C6 내면반지름 여유 거의 없음"),
|
||||
("M-5", "도면↔모델", "C9~C13 길이 산식 +54.3 규칙이 변하는 치수에만 적용"),
|
||||
("M-6", "도면↔모델", "S1·S2 높이 기준 (도면 대좌 상면 -50 ↔ 모델 코핑 상면 -50)"),
|
||||
("M-7", "도면↔모델", "S1·S2 층 순서 (도면 S2 위 ↔ 모델 S1 위)"),
|
||||
("M-8", "도면↔모델", "기초 교차 철근 간격 (도면 27~28 mm ↔ 모델 54~56 mm)"),
|
||||
("M-9", "도면↔모델", "기초 B5 중심 73 아래(피복 58.5), B2·B6 다리 끝 높이"),
|
||||
("M-10", "기준↔모델", "기초 S25·S19 굽힘 내면지름이 공칭 기준에 1.3/0.3 mm 부족"),
|
||||
("M-11", "기준↔모델", "P3 갈고리 연장 86.7 < 5db 95, 내면지름 66.5 < 66.85"),
|
||||
("M-12", "미확인", "P3 이음·P4/P5 커플러 각도 도면에 없음 (모델 가정)"),
|
||||
("M-13", "도면↔모델", "UA1(73본, H22) 모델에 없음"),
|
||||
("D-4/D-5", "도면 내부", "스터럽 폭 재료표 1.407 ↔ 단면 작도 1.160/1.740, 폐합근 1.106/1.686"),
|
||||
("D-6", "도면 내부", "재료표 L 이 (최대+최소)/2 근사 → 총길이 과대"),
|
||||
("D-7", "도면 내부", "C2·C5 L 12.081 ↔ 치수 합 12.082"),
|
||||
("D-8", "도면 내부", "다리 각도 재료표 98°/96° ↔ 정면도 96.4°"),
|
||||
("D-9", "도면 내부", "C7·C8 재료표 B·D·R·X·J 빈칸"),
|
||||
("D-15~D-18", "도면 내부", "기초 점/선 높이 27~28 mm 차, B12 34↔28, B3 치수↔본수, B10 단 수"),
|
||||
("D-20~D-23", "도면 내부", "P1/P2 상단 EL, 기둥 반지름, 링 R, P4·P5 @100↔@120"),
|
||||
("E-01", "선행보고서", "기둥 띠철근 간격 → 구조계산서 확인 사항"),
|
||||
]
|
||||
for c in codes:
|
||||
w2.append(list(c))
|
||||
for i, w in enumerate([12, 14, 90], 1):
|
||||
w2.column_dimensions[get_column_letter(i)].width = w
|
||||
for c in range(1, 4):
|
||||
w2.cell(row=1, column=c).font = Font(bold=True); w2.cell(row=1, column=c).fill = HF
|
||||
|
||||
# ---------------------------------------------------------------- 시트 3: 모델 개별 철근
|
||||
w3 = wb.create_sheet("모델_개별철근")
|
||||
w3.append(["마크", "직경", "형식", "발생(occurrence)", "모델 L(각진+커플러, mm)", "커플러(mm)", "중심선 실길이(mm)", "호스트", "근거(src)"])
|
||||
for mk in sorted(mod, key=lambda s: (re.sub(r"\d", "", s), int(re.sub(r"\D", "", s) or 0))):
|
||||
for occ, b in sorted(bars[mk].items()):
|
||||
if "us" not in b:
|
||||
continue
|
||||
us = b["us"]
|
||||
w3.append([mk, us.get("dia"), us.get("shape"), occ, round(num(us.get("design_length")) + num(us.get("coupler_length")), 3),
|
||||
num(us.get("coupler_length")), round(b["cl"], 1) if b.get("cl") is not None else None, us.get("host"), us.get("src")])
|
||||
for c in range(1, 10):
|
||||
w3.cell(row=1, column=c).font = Font(bold=True); w3.cell(row=1, column=c).fill = HF
|
||||
for i, w in enumerate([8, 7, 7, 18, 20, 11, 18, 10, 70], 1):
|
||||
w3.column_dimensions[get_column_letter(i)].width = w
|
||||
w3.freeze_panes = "A2"
|
||||
|
||||
wb.save(OUT)
|
||||
print("saved", OUT)
|
||||
for tab, v in tot_all.items():
|
||||
print(tab, "도면 총길이 %.3f / 모델 %.3f | 도면 t %.3f(할증 %.3f) / 모델 t %.3f(할증 %.3f)" % (v[0], v[1], v[2], v[4], v[3], v[5]))
|
||||
print("drawing marks:", len(drw), "model marks:", len(mod))
|
||||
miss = [m for m in drw if m not in mod]
|
||||
extra = [m for m in mod if m not in drw]
|
||||
print("도면에만:", miss, "모델에만:", extra)
|
||||
Reference in new issue
Block a user