"""Wire form fan guard: concentric wire rings held by four ribs that end in mounting eyes, Qualtek's 08 series, for 30 to 120 mm fans. The guard lies on the XY plane, centred on the origin: the eyes at the corners of the fan's square mounting pattern sit on the XY plane, and the rings are raised to the guard's thickness (+Z). Dimensions from Qualtek's drawings, listed in the README.""" import math from steve import Model fan = "120mm" # Fan size finish = "nickel-chrome" # Finish # Each entry: (Qualtek part, mounting hole centres (square), eye hole diameter, outside ring diameter (over the wire), inside diameter of # the centre ring, number of rings, ring wire diameter, rib wire diameter, thickness (overall)), mm. # Qualtek Electronics Corp., FAN-S Division, drawings "WIRE FORM FAN GUARD", unit mm, from qualtekusa.com # (www.qualtekusa.com/Catalog/Fan_Accessories/PDFDrawings/.pdf; 08346 and 08174 under www.qualtekusa.com/images/Fan%20Accessories/PDFDrawings/): # 08346 (30mm) rev. A: 4-24, Ø3.2 +0.3/0, Ø21.8, Ø9, ring and rib Ø1.6, 4.8 REF. # 08149 (40mm) rev. B: 32, Ø4.0 ±0.2, Ø29.53, Ø14.2 ±0.5, ring and rib Ø1.6, 4.8 REF. # 08148 (50mm) rev. B: 4-42, 4-Ø4.2 ±0.2, Ø41.1, Ø14.2, ring and rib Ø1.6, 4.8. # 08147 (60mm) rev. C: Ø50 (the hole circle's square, 50 apart), Ø4.57 +0.3/-0.1, Ø53, Ø14.2 ±0.5, ring and rib Ø1.6, 5.2 REF. # 08172 (80mm) rev. A: 71.37, Ø4.57 +0.3/-0.1, Ø76.2, Ø14.2 ±0.5, ring and rib Ø1.6, 4.95 REF. # 08174 (92mm) rev. C: 82.42, 4.57 ±0.2, Ø89.8 ±1.2, Ø14.2 ±0.5, ring and rib Ø1.6, 5.0 REF. # 08170 (120mm) rev. B: Ø104.78 ±0.5, Ø4.57 +0.3/-0.1, Ø115.6, Ø14.2 ±0.5, ring and rib Ø1.8, 5.66 REF. # The number of rings is counted on each drawing's front view (not dimensioned); the drawings, to scale, space them evenly (README). GUARDS = { "30mm": ("08346", 24.0, 3.2, 21.8, 9.0, 2, 1.6, 1.6, 4.8), "40mm": ("08149", 32.0, 4.0, 29.53, 14.2, 2, 1.6, 1.6, 4.8), "50mm": ("08148", 42.0, 4.2, 41.1, 14.2, 3, 1.6, 1.6, 4.8), "60mm": ("08147", 50.0, 4.57, 53.0, 14.2, 4, 1.6, 1.6, 5.2), "80mm": ("08172", 71.37, 4.57, 76.2, 14.2, 5, 1.6, 1.6, 4.95), "92mm": ("08174", 82.42, 4.57, 89.8, 14.2, 6, 1.6, 1.6, 5.0), "120mm": ("08170", 104.78, 4.57, 115.6, 14.2, 8, 1.8, 1.8, 5.66), } # Finish: the drawings' note 2 (bright nickel chrome plating); the catalogue page (Qualtek "Wire Form Fan Guards - 08 Series", p. 67) # adds black (-02) and stainless steel (-SS). Colours are for looks only. FINISHES = {"nickel-chrome": "#d9dcdf", "black": "#222325", "stainless": "#c4c7ca"} if fan not in GUARDS: raise ValueError(f"no fan size {fan!r}: {', '.join(GUARDS)}") if finish not in FINISHES: raise ValueError(f"no finish {finish!r}: {', '.join(FINISHES)}") _, pitch, hole, od, id_, rings, d_ring, d_rib, thickness = GUARDS[fan] z_eye = 1.06 * d_rib / 2 # the eyes and the ribs' outer ends on the XY plane (their weld spheres touch it) z_ring = thickness - d_ring / 2 # the rings' top at the guard's thickness r_out = (od - d_ring) / 2 # ring centrelines: the outside ring and the centre ring r_in = (id_ + d_ring) / 2 r_eye = (hole + d_rib) / 2 # the eye's wire loop around the mounting hole s_eye = pitch / math.sqrt(2) # eye centre from the guard's centre, on the diagonals # Proportion, for looks: each rib is straight along its diagonal, in the rings' plane from the centre ring to the outside ring, then down # at 45 degrees to the eyes' plane and on to its eye. The drawings' ribs curve round the centre ring (4.57 REF apart at the centre on # 08170) and their jog isn't dimensioned. # The ribs run just under the rings, overlapping them by 30 % of the wires' radii so that each weld is a solid joint. z_rib = z_ring - 0.7 * (d_ring + d_rib) / 2 s_jog = r_out + (z_rib - z_eye) model = Model() for k in range(rings): r = r_in + k * (r_out - r_in) / (rings - 1) model.torus(f"ring_{k + 1}", r, d_ring / 2, at=(0, 0, z_ring), op="new" if k == 0 else "join", body="guard", name=f"Ring {k + 1}") for k in range(4): a = math.radians(45 + 90 * k) ux, uy = math.cos(a), math.sin(a) model.torus(f"eye_{k + 1}", r_eye, d_rib / 2, at=(s_eye * ux, s_eye * uy, z_eye), op="join", body="guard", name=f"Eye {k + 1}") # Straight parts from point to point, with a weld sphere at each joint (the eye, both bends and the inner end). The spheres are 6 % # larger than the wire so that the straight parts end inside them: equal radii meeting make the welds fail. points = [(s_eye - r_eye, z_eye), (s_jog, z_eye), (r_out, z_rib), (r_in, z_rib)] for j, ((s0, z0), (s1, z1)) in enumerate(zip(points, points[1:])): length = math.hypot(s1 - s0, z1 - z0) model.cylinder(f"rib_{k + 1}_{j + 1}", d_rib / 2, length, at=(s0 * ux, s0 * uy, z0), direction=((s1 - s0) * ux / length, (s1 - s0) * uy / length, (z1 - z0) / length), op="join", body="guard", name=f"Rib {k + 1}") for j, (s0, z0) in enumerate(points): model.sphere(f"rib_{k + 1}_weld_{j + 1}", 1.06 * d_rib / 2, at=(s0 * ux, s0 * uy, z0), op="join", body="guard", name=f"Rib {k + 1} joint") # Material: C1008 bright basic wire (note 1), modelled as steel-c10 (EN C10, the nearest plain low-carbon steel in the list), plated. model.material("steel-c10", bodies=["guard"], color=FINISHES[finish]) result = model.result()