"""Independent geometry / fit / load checks on the rack design.""" import itertools import math import rack_params as P fails, warns = [], [] def check(cond, msg): print((" PASS " if cond else " FAIL ") + msg) if not cond: fails.append(msg) def warn(cond, msg): if not cond: print(" WARN " + msg) warns.append(msg) def ov(a0, a1, b0, b1): return max(0.0, min(a1, b1) - max(a0, b0)) print(f"\nMATERIAL: {P.MATERIAL} T = {P.T}\"") print("\n[1] Overall envelope & clear heights") check(abs(P.H - (3 * P.T + 2 * P.CLEAR)) < 1e-9, f"overall height = {P.H:.3f}\" (target 10\")") check(P.H <= 10.0, f"meets the 10\" height target: {P.H:.3f}\"") check(abs((P.Z_MID_0 - P.Z_BOT_1) - P.CLEAR) < 1e-9, f"equipment bay clear height = {P.CLEAR:.4f}\"") check(abs((P.Z_TOP_0 - P.Z_MID_1) - P.CLEAR) < 1e-9, f"cable bay clear height = {P.CLEAR:.4f}\"") check(abs(P.W - 33.0) < 1e-9, "overall width = 33.000\"") check(abs(P.D - 10.0) < 1e-9, "overall depth = 10.000\"") check(abs(P.INNER_LEN - (P.W - 2 * P.T)) < 1e-9, f"inner panel length = {P.INNER_LEN:.3f}\" (cut 31-9/16\" to drop in easily)") check(abs(P.SIDE_HT - (2 * P.CLEAR + P.T)) < 1e-9, f"side panel height = {P.SIDE_HT:.3f}\" (cut 8-9/16\")") eq = P.EQUIP_BAY["y"][1] - P.EQUIP_BAY["y"][0] check(9.0 <= eq <= 10.0, f"equipment bay clear depth = {eq:.3f}\"") check(abs(P.CLEAR_W - P.INNER_LEN) < 1e-9, f"clear width, both bays = {P.CLEAR_W:.3f}\"") print("\n[2] No panel-to-panel interpenetration") uniq = [p for p in P.PANELS] bad = 0 for a, b in itertools.combinations(uniq, 2): ax0, ay0, az0, ax1, ay1, az1 = a["box"] bx0, by0, bz0, bx1, by1, bz1 = b["box"] v = ov(ax0, ax1, bx0, bx1) * ov(ay0, ay1, by0, by1) * ov(az0, az1, bz0, bz1) if v > 1e-9: bad += 1 print(f" {a['key']} vs {b['key']}: {v:.4f} in^3") check(bad == 0, f"all {len(uniq) * (len(uniq) - 1) // 2} panel pairs are disjoint") print("\n[3] MIDDLE SHELF — one 2\" grommet per front-bay device") mid = P.PANELS_BY_KEY["middle"] r = P.GROMMET_DIA / 2 check(len(P.GROMMET_X) == len(P.DEVICES), f"{len(P.GROMMET_X)} holes, one per front-bay device") for x in P.GROMMET_X: m = min(x - r - mid["box"][0], mid["box"][3] - (x + r)) check(m >= 0.5, f" X={x}: side margin {m:.2f}\"") check(P.Y_REAR_WALL_0 - (P.GROMMET_Y + r) >= 0.5, f" clears the rear wall by {P.Y_REAR_WALL_0 - (P.GROMMET_Y + r):.2f}\"") check(P.GROMMET_Y - r >= 0.5, f" clears the front edge by {P.GROMMET_Y - r:.2f}\"") check(min(b - a for a, b in zip(P.GROMMET_X, P.GROMMET_X[1:])) - P.GROMMET_DIA >= 1.5, f" min web between holes = " f"{min(b - a for a, b in zip(P.GROMMET_X, P.GROMMET_X[1:])) - P.GROMMET_DIA:.2f}\"") print("\n[4] BOTTOM PANEL — 12 x 1-1/2\" vents (2 rows x 6 columns)") bot = P.PANELS_BY_KEY["bottom"] vr = P.VENT_DIA / 2 check(len(bot["holes"]) == 12, f"{len(bot['holes'])} holes") for x in P.VENT_X: for y in P.VENT_Y: m = min(x - vr, P.W - (x + vr), y - vr, P.D - (y + vr)) check(m >= 0.5, f" ({x}, {y}): min edge distance {m:.2f}\"") row_web = (max(P.VENT_Y) - vr) - (min(P.VENT_Y) + vr) check(row_web >= 0.75, f" web between the two rows = {row_web:.2f}\"") check(min(P.VENT_Y) - vr >= 1.5, f" solid strip along the open front edge = {min(P.VENT_Y) - vr:.2f}\"") check(P.Y_REAR_WALL_0 - (max(P.VENT_Y) + vr) >= 1.0, f" solid strip under the rear wall = {P.Y_REAR_WALL_0 - (max(P.VENT_Y) + vr):.2f}\"") net = P.W - len(P.VENT_X) * P.VENT_DIA check(net / P.W >= 0.70, f" net material across the worst section = {net:.1f}\" of {P.W:.0f}\" " f"({net / P.W * 100:.0f}%)") # how many vent columns land under a device footprint? under = 0 for x in P.VENT_X: for name, cx, w, d, h in P.DEVICES: if cx - w / 2 <= x <= cx + w / 2: under += 1 break check(under >= 5, f" {under} of {len(P.VENT_X)} vent columns land under a device") for name, cx, w, d, h in P.DEVICES: rows_ok = all(P.DEVICE_Y0 <= y <= P.DEVICE_Y0 + d for y in P.VENT_Y) check(rows_ok, f" both vent rows fall inside the {d}\" depth of {name}") print("\n[5] REAR WALL — 6 x 2\" pass-throughs, equally spaced") rw = P.PANELS_BY_KEY["rear_wall"] rr = P.REAR_HOLE_DIA / 2 check(len(P.REAR_HOLE_X) == 6, "6 holes") pitches = [round(b - a, 4) for a, b in zip(P.REAR_HOLE_X, P.REAR_HOLE_X[1:])] check(len(set(pitches)) == 1, f" equal pitch = {pitches[0]:.3f}\" throughout") check(abs((P.REAR_HOLE_X[0] - P.X_IN_0) - (P.X_IN_1 - P.REAR_HOLE_X[-1])) < 1e-6, f" symmetric end margins = {P.REAR_HOLE_X[0] - P.X_IN_0:.3f}\" each") for x in P.REAR_HOLE_X: m = min(x - rr - P.X_IN_0, P.X_IN_1 - (x + rr)) check(m >= 0.5, f" X={x:.3f}: end margin {m:.2f}\"") check(P.REAR_HOLE_Z - rr - P.Z_BOT_1 >= 0.75, f" edge distance below hole = {P.REAR_HOLE_Z - rr - P.Z_BOT_1:.2f}\"") check(P.Z_MID_0 - (P.REAR_HOLE_Z + rr) >= 0.75, f" edge distance above hole = {P.Z_MID_0 - (P.REAR_HOLE_Z + rr):.2f}\"") web = pitches[0] - P.REAR_HOLE_DIA check(web >= 1.5, f" web between holes = {web:.2f}\"") net_rw = P.INNER_LEN - 6 * P.REAR_HOLE_DIA check(net_rw / P.INNER_LEN >= 0.60, f" shear web remaining = {net_rw:.1f}\" of {P.INNER_LEN:.1f}\" " f"({net_rw / P.INNER_LEN * 100:.0f}%)") # how well do the equally spaced holes line up with the device columns? behind = 0 for hx in P.REAR_HOLE_X: for name, cx, w, _d, _h in P.DEVICES: if cx - w / 2 <= hx <= cx + w / 2: behind += 1 break check(behind >= 4, f" {behind} of 6 pass-throughs sit directly behind a device") print("\n[6] Hole saws required") sizes = sorted({P.GROMMET_DIA, P.VENT_DIA, P.REAR_HOLE_DIA}) print(f" distinct hole sizes: {sizes}") check(len(sizes) <= 2, f"{len(sizes)} hole saws needed") print("\n[7] Equipment fits the front bay (switch moved to the cable bay)") prev = P.X_IN_0 for name, cx, w, d, h in P.DEVICES: x0, x1 = cx - w / 2, cx + w / 2 check(x0 >= P.X_IN_0 and x1 <= P.X_IN_1, f"{name}: X {x0:.2f}-{x1:.2f} inside the bay") check(h <= P.CLEAR - 0.25, f"{name}: {h}\" tall clears the shelf " f"({P.CLEAR - h:.2f}\" headroom)") check(P.DEVICE_Y0 + d <= P.Y_REAR_WALL_0, f"{name}: {d}\" deep fits") check(x0 - prev >= 0.9, f"{name}: {x0 - prev:.2f}\" airflow gap on its left") prev = x1 check(P.X_IN_1 - prev >= 0.9, f"right-hand margin = {P.X_IN_1 - prev:.2f}\"") for name, cx, w, d, h, y0 in P.CABLE_DEVICES: check(P.X_IN_0 <= cx - w / 2 and cx + w / 2 <= P.X_IN_1, f"{name}: fits the cable bay width") check(h <= P.CLEAR - 0.25, f"{name}: {h}\" tall clears the cable bay") check(y0 + d <= P.D, f"{name}: {d}\" deep fits the cable bay") print("\n[8] Clamp zones") for x in P.CLAMP_X: check(P.X_IN_0 <= x <= P.X_IN_1, f"clamp @ X={x} is backed by the doubler") spans = [P.CLAMP_X[0]] + [b - a for a, b in zip(P.CLAMP_X, P.CLAMP_X[1:])] + [P.W - P.CLAMP_X[-1]] check(max(spans) <= 9.0, f"max unsupported span = {max(spans):.2f}\"") print("\n[9] Weight and clamp loading") vol = 0.0 for p in P.PANELS: u, v = p["size"] a = u * v - sum(math.pi * (d / 2) ** 2 for _, _, d in p["holes"]) vol += a * P.T * (2 if p["qty"] == 2 else 1 if p["qty"] else 0) rack = vol / P.T / 144.0 * P.PLY_LB_PER_SQFT gear, cable = 4 * 1.5 + 1.5, 8.0 + 1.0 total = rack + gear + cable print(f" rack {rack:.1f} lb + gear {gear:.1f} lb + cabling {cable:.1f} lb") check(total < 80, f"total hanging load = {total:.1f} lb") check(total / 4 < 150, f"load per clamp = {total / 4:.1f} lb") print("\n[10] Sheet nesting on one 96\" x 48\" sheet") SW, SH = P.SHEET placed = [] for key, x, y, w, h, rip in P.NEST: p = P.PANELS_BY_KEY[key] su, sv = sorted(p["size"], reverse=True), sorted((w, h), reverse=True) check(all(abs(a - b) < 1e-6 for a, b in zip(su, sv)), f"{key}: nested footprint {w:.3f} x {h:.3f} matches the cut size") check(x >= 0 and y >= 0 and x + w <= SW + 1e-6 and y + h <= SH + 1e-6, f"{key}: fits inside the sheet") for k2, x2, y2, w2, h2 in placed: a = ov(x, x + w, x2, x2 + w2) * ov(y, y + h, y2, y2 + h2) check(a < 1e-6, f"{key} vs {k2}: nesting overlap {a:.3f} in^2") placed.append((key, x, y, w, h)) for key, x, y, w, h, rip in P.NEST: r = [r for r in P.RIPS if r[2] == rip][0] check(y >= r[0] - 1e-6 and y + h <= r[0] + r[1] + 1e-6, f"{key}: stays within rip {rip}") used = sum(w * h for _, _, _, w, h, _ in P.NEST) check(used <= SW * SH, f"sheet utilisation = {used / (SW * SH) * 100:.0f}% " f"(offcut {SH - sum(r[1] for r in P.RIPS):.2f}\" x 96\" plus ends)") check(len(P.NEST) == sum(p["qty"] for p in P.PANELS), "every piece in the cut list is nested") print("\n" + "=" * 62) print(f"{len(fails)} failures, {len(warns)} warnings") for f in fails: print(" FAIL: " + f) print("=" * 62)