文件

108 行
5.3 KiB
Python

# -*- coding: ascii -*-
"""Shroud feasibility: per-channel fan-bore coverage + plenum-depth resistance model.
All geometry read back from the live assembly (_top.json), nothing assumed."""
import json, math
rows = json.load(open('_top.json', encoding='utf-8'))
B = {r['name']: r['box'] for r in rows if r['box']}
blades = sorted([v for k, v in B.items() if 'BLADE' in k.upper()], key=lambda b: b[1])
fanW = [v for k, v in B.items() if 'OD6025' in k.upper() and v[0] < 50][0]
fanE = [v for k, v in B.items() if 'OD6025' in k.upper() and v[0] > 50][0]
tray = [v for k, v in B.items() if 'TRAY' in k.upper()][0]
top = [v for k, v in B.items() if 'TOPPANEL' in k.upper()][0]
ep = [v for k, v in B.items() if 'ENDPLATE' in k.upper()][0]
base = [v for k, v in B.items() if 'BASE-PCB' in k.upper()][0]
BZ0, BZ1 = blades[0][2], blades[0][5] # blade z span
SX0, SX1 = blades[0][0], blades[0][3] # blade x span (stack length)
IY0, IY1 = ep[1], ep[4] # interior y (end-plate span)
IZ0, IZ1 = tray[5], top[2] # interior z: tray top .. top panel inner
CY, CZ, R = 51.345, 37.85, 30.5 # fan bore centre + radius (Orion OD6025 measured)
print('== measured geometry ==')
print(' blade stack x %.2f~%.2f z %.2f~%.2f (h %.2f)' % (SX0, SX1, BZ0, BZ1, BZ1 - BZ0))
print(' interior y %.3f~%.3f z %.2f~%.2f' % (IY0, IY1, IZ0, IZ1))
print(' west fan x %.2f~%.2f exit plane x=%.2f' % (fanW[0], fanW[3], fanW[3]))
print(' east fan x %.2f~%.2f inlet plane x=%.2f' % (fanE[0], fanE[3], fanE[0]))
print(' PLENUM west x %.2f~%.2f = %.2f mm' % (fanW[3], SX0, SX0 - fanW[3]))
print(' PLENUM east x %.2f~%.2f = %.2f mm' % (SX1, fanE[0], fanE[0] - SX1))
print(' base PCB x %.2f~%.2f y %.2f~%.2f z %.2f~%.2f' % (base[0], base[3], base[1], base[4], base[2], base[5]))
# ---- channels: 4 inter-blade + 2 edge -------------------------------------
ch = []
ch.append(('edge-S', IY0, blades[0][1]))
for i in range(4):
ch.append(('C%d (blade%d-%d)' % (i + 1, i + 1, i + 2), blades[i][4], blades[i + 1][1]))
ch.append(('edge-N', blades[4][4], IY1))
def circ_area(y0, y1, z0, z1, n=2000):
"""area of [y0,y1]x[z0,z1] inside the bore circle, by strip integration"""
a = 0.0
dy = (y1 - y0) / n
for i in range(n):
y = y0 + (i + 0.5) * dy
d = R * R - (y - CY) ** 2
if d <= 0:
continue
h = math.sqrt(d)
lo, hi = max(z0, CZ - h), min(z1, CZ + h)
if hi > lo:
a += (hi - lo) * dy
return a
print('\n== per-channel frontal coverage by the fan bore (D61) ==')
print(' %-18s %8s %8s %10s %10s %7s' % ('channel', 'y0', 'y1', 'front mm2', 'in-bore', 'cover'))
tot_f = tot_c = 0.0
for nm, y0, y1 in ch:
z0, z1 = (BZ0, BZ1) if 'edge' not in nm else (IZ0, IZ1)
f = (y1 - y0) * (z1 - z0)
c = circ_area(y0, y1, z0, z1)
tot_f += f
tot_c += c
print(' %-18s %8.3f %8.3f %10.0f %10.0f %6.1f%%' % (nm, y0, y1, f, c, 100 * c / f))
print(' %-18s %8s %8s %10.0f %10.0f %6.1f%%' % ('TOTAL', '', '', tot_f, tot_c, 100 * tot_c / tot_f))
# ---- bypass: air can go around the stack instead of through it ------------
print('\n== bypass paths around the blade stack (unsealed today) ==')
byp = [('south strip', (blades[0][1] - IY0) * (IZ1 - IZ0)),
('north strip', (IY1 - blades[4][4]) * (IZ1 - IZ0)),
('over the top', (blades[4][4] - blades[0][1]) * (IZ1 - BZ1)),
('under (slots)', (blades[4][4] - blades[0][1]) * (BZ0 - IZ0))]
bt = sum(v for _, v in byp)
chan_area = sum((y1 - y0) * (BZ1 - BZ0) for nm, y0, y1 in ch if 'edge' not in nm)
for nm, v in byp:
print(' %-16s %8.0f mm2' % (nm, v))
print(' bypass total %8.0f mm2 vs 4 real channels %.0f mm2 => %.0f%% of the'
' flow area is short-circuit' % (bt, chan_area, 100 * bt / (bt + chan_area)))
# ---- how deep must the plenum be? -----------------------------------------
# Compare dP of the lateral feed run inside the plenum against dP of one channel
# at equal volume flow. dP ~ (L/Dh) * V^2 ; V ~ 1/A.
Lc = SX1 - SX0
wc = ch[3][2] - ch[3][1] # a representative inter-blade gap
Ac = wc * (BZ1 - BZ0)
Dhc = 2 * wc * (BZ1 - BZ0) / (wc + (BZ1 - BZ0))
lat = abs(ch[5][1] - CY) # lateral run from bore centre to the far channel
print('\n== how deep does the distribution plenum have to be? ==')
print(' channel: %.2f wide x %.2f tall x %.0f long, Dh %.1f, A %.0f mm2'
% (wc, BZ1 - BZ0, Lc, Dhc, Ac))
print(' lateral run inside the plenum: %.1f mm (bore centre -> far channel)\n' % lat)
print(' %8s %10s %8s %12s' % ('depth', 'A_plenum', 'Dh', 'dP_lat/dP_ch'))
for d in (4.01, 6, 8, 10, 12, 15, 20, 25):
Ap = d * (BZ1 - BZ0)
Dhp = 2 * d * (BZ1 - BZ0) / (d + (BZ1 - BZ0))
ratio = ((lat / Dhp) / (Lc / Dhc)) * (Ac / Ap) ** 2
flag = ' <= today' if abs(d - 4.01) < 0.01 else (' OK' if ratio < 0.5 else '')
print(' %8.2f %10.0f %8.1f %12.2f%s' % (d, Ap, Dhp, ratio, flag))
print('\n (ratio >> 1 means the far channels are starved no matter what vanes you add;')
print(' ratio < ~0.5 means the plenum equalises pressure and every channel sees the same dP)')
# geometric diffuser check
need = ((IY1 - IY0) - 2 * R) / 2.0
print('\n== a real (vaneless) diffuser would need ==')
for ang in (7, 10, 15):
print(' half-angle %2d deg -> %.0f mm of axial length (have %.2f)'
% (ang, need / math.tan(math.radians(ang)), SX0 - fanW[3]))