- 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>
49 lines
3.3 KiB
Python
49 lines
3.3 KiB
Python
"""Ulsan P2 form test (2026-10-15), no Rhino: engine gate reports of data/산외교울산P2 (G1~G5; G3 with one planned FAIL) PASS with negatives detected, the
|
|
foundation rotation (37 deg, piles 6 x 5 on the foundation's axes) and the P2-specific values are in params / expected, and the reference
|
|
piers are unchanged (검증/open19_snapshot_after.json)."""
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
APP = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(APP))
|
|
sys.path.insert(0, str(APP / "tools"))
|
|
from open19_snapshot import snapshot # noqa: E402
|
|
|
|
ok = True
|
|
|
|
|
|
def check(name, cond, detail=""):
|
|
global ok
|
|
ok &= bool(cond)
|
|
print("PASS" if cond else "FAIL", name, str(detail)[:300], flush=True)
|
|
|
|
|
|
ref = json.loads((APP / "검증" / "open19_snapshot_after.json").read_text(encoding="utf-8"))
|
|
now = snapshot()
|
|
for k in ref:
|
|
check(f"{k}: table records / proposals unchanged", ref[k]["records"] == now[k]["records"] and ref[k]["proposals"] == now[k]["proposals"])
|
|
D = APP / "data" / "산외교울산P2"
|
|
P = json.loads((D / "params.json").read_text(encoding="utf-8"))
|
|
X = json.loads((D / "expected.json").read_text(encoding="utf-8"))
|
|
check("scope G1~G5, builders / checkers", P["scope"] == ["G1", "G2", "G3", "G4", "G5"] and X["checkers"] == {"G1": "g1_tapered.py", "G2": "g2_ring.py", "G3": "g3_mats.py", "G4": "g4_polylines.py"}
|
|
and P["builders"]["G3"] == "footing_rebar_mats.py")
|
|
F, G = P["footing"], X["G1"]
|
|
check("footing 7800 x 7500 x 2500 at EL 38.916, rotated 37 deg (params = expected)",
|
|
(F["size_x"], F["size_y"], F["thickness"], F["z_bottom"], F["rot_deg"]) == (7800, 7500, 2500, 38916, 37.0)
|
|
and (G["footing"]["dx"], G["footing"]["dy"], G["footing"]["z0"], G["footing"]["rot_deg"]) == (7800, 7500, 38916, 37.0))
|
|
check("piles 6 x 5 @1300/1550, D508 L7000, head +100", (P["piles"]["count_x"], P["piles"]["count_y"], P["piles"]["pitch_x"], P["piles"]["pitch_y"],
|
|
P["piles"]["diameter"], P["piles"]["length"], P["piles"]["head_embedment"]) == (6, 5, 1300.0, 1550.0, 508.0, 7000.0, 100.0)
|
|
and (G["piles"]["nx"], G["piles"]["ny"], G["piles"]["edge_x"], G["piles"]["edge_y"]) == (6, 5, 650, 650))
|
|
check("column 2500 x 27300; coping EL 72.046 / 71.916", P["column"]["height"] == 27300 and P["coping"]["z_top_left"] == 72046.0 and P["coping"]["z_top_right"] == 71916.0)
|
|
check("4 bearings, seat levels from the coping steps", len(P["bearing_seats"]["bearings"]) == 4
|
|
and all(abs(b["el_x"] - b["h1"] - b["h2"] - b["seat_z"]) <= 1 for b in P["bearing_seats"]["bearings"]))
|
|
check("rebar counts = schedule: column P1 68 / footing B7 320 / coping C7 51",
|
|
X["G2"]["schedule_n"]["P1"] == 68 and X["G3"]["schedule"]["B7"][1] == 320 and X["G4"]["schedule"]["C7"][1] == 51)
|
|
for g, n, tot in (("G1", 118, 118), ("G2", 35, 35), ("G3", 63, 64), ("G4", 129, 129), ("G5", 75, 75)): # G3: 1 planned FAIL (B8 length, OPEN #32)
|
|
r = json.loads((D / "runs" / "current" / f"{g}_gate.json").read_text(encoding="utf-8"))
|
|
check(f"{g} gate PASS {n}/{tot}, negatives detected", r["verdict"] == "PASS" and r["summary"]["passed"] == n and r["summary"]["total"] == tot
|
|
and not r["unplanned_fails"] and all(v["detected"] for v in r["negative"].values()), r["summary"])
|
|
print("RESULT", "PASS" if ok else "FAIL")
|
|
sys.exit(0 if ok else 1)
|