bd_cruft/examples/immersion-cooling.py

360 lines
11 KiB
Python
Executable File

#!/usr/bin/env python3
"""
This script creates an assembly for liquid immersion and allows for various exports.
Usage: python3 sys-assembly --display --stl output.stl
"""
import argparse
from bd_cruft import (
atx_motherboard,
immersion_mb_plate,
tslot_extrusion,
atx_psu,
immersion_pci_gpu,
immersion_tank,
immersion_chassis,
immersion_lid,
export_3d,
)
from build123d import *
from ocp_vscode import *
import logging
import copy
TANK_THICK = 9 # XXX from immersion tank, don't set statically
TSLOT_HI_LEN = 380 + TANK_THICK # Length of 4x corners from immersion_chassis
TSLOT_WIDE_LEN = 450 # Length of 5x left/right horizontal from immersion_chassis
TSLOT_DEEP_LEN = 260 # Length of 4x front/back horizontal
TSLOT_SIZE = 20 # Size of extrusion (e.g. 20mm x 20mm)
BOT_OFFSET = 24 # How far first horizontal extrusion is from bottom
WEIR_OFFSET = 60 # XXX from immersion_tank, don't set statically
MB_PLATE_THICKNESS = 5.6 # XXX FIX don't set
TANK_OFFSET_X = TSLOT_SIZE
# The zero points on the motherboard are the first hole, not the edge of the board.
MB_RISER = 6.5 # Size of motherboard risers/standoffs
GPU_OFFSET = 12 + 2 # Distance from motherboard to GPU
ATX_PSU_LENGTH = 200 # XXX from atx_psu
ATX_PSU_WIDTH = 85 # Width of PSU XXX from atx_psu
ATX_PSU_HEIGHT = 150 # XXX from atx_psu
export_formats = {
"mf": export_3d.export_mf,
"stl": export_3d.export_stl,
"step": export_3d.sys_export_step,
"brep": export_3d.sys_export_brep,
}
def compound_assembly():
# Immersion Chassis
chassis_config = immersion_chassis.ImmersionChassisConfig()
chassis = immersion_chassis.compound_chassis(**chassis_config.model_dump())
compound_chassis = Compound.make_compound(chassis).locate(
Location(
Vector(
0,
0,
0,
),
(0, 0, 0),
)
)
compound_chassis.label = "Immersion Chassis"
compound_chassis.color = Color(0.5, 0.5, 0.5, 1.0)
# Acrylic immersion tank
tank_config = immersion_tank.TankConfig()
tank = immersion_tank.create_tank(**tank_config.model_dump())
compound_tank = Compound.make_compound(tank).locate(
Location(
Vector(
TANK_OFFSET_X,
TSLOT_SIZE,
0,
),
(0, 0, 0),
)
)
compound_tank.label = "Immersion Tank"
compound_tank.color = Color(0.25, 0.64, 0.76, 0.35) # Acrylic
# Acrylic immersion motherboard mount plate
plate_config = immersion_mb_plate.ImmersionMBPlateConfig()
plate = immersion_mb_plate.create_plate(**plate_config.model_dump())
plate.label = "Immersion Motherboard Plate"
compound_plate = Compound.make_compound([(plate.part)]).locate(
Location(
Vector(
TANK_OFFSET_X + TANK_THICK,
TSLOT_SIZE + tank_config.tank_length - TANK_THICK - WEIR_OFFSET,
TANK_THICK,
),
(90, 0, 0),
)
)
compound_plate.label = "MB Mount Plate"
compound_plate.color = Color(0.25, 0.64, 0.76, 0.25) # Acrylic
# Acrylic Immersion Lid
lid_config = immersion_lid.ImmersionLidConfig()
lid = immersion_lid.create_plate(**lid_config.model_dump())
lid.label = "Immersion Lid"
compound_lid = Compound.make_compound([(lid.part)]).locate(
Location(
Vector(
0,
0,
TSLOT_HI_LEN,
),
(0, 0, 0),
)
)
compound_lid.label = "Immersion Lid"
compound_lid.color = Color(0.25, 0.64, 0.76, 0.25) # Acrylic
# ATX Motherboard
mb = import_step(file_name="examples/motherboard.stp")
mb.label = "Motherboard"
compound_mb = Compound.make_compound([(mb)]).locate(
Location(
Vector(
TSLOT_SIZE + 16 + TANK_OFFSET_X + TANK_THICK,
TSLOT_SIZE
+ tank_config.tank_length
- TANK_THICK
- WEIR_OFFSET
- MB_PLATE_THICKNESS
- MB_RISER,
243.84 + 99,
),
(0, 180, 180),
)
)
compound_mb.label = "Motherboard"
compound_mb.color = Color("ForestGreen", 1.0)
# # Koolance ERM-3K3U
# koolance = import_step(file_name="examples/koolance.step")
# koolance.label = "Koolance"
# compound_koolance = Compound.make_compound([(koolance)]).locate(
# Location(
# Vector(
# 900,
# 388,
# 240,
# ),
# (90, 90, 0),
# )
# )
# compound_koolance.label = "Koolance ERM-3K3U"
# compound_koolance.color = Color("Black", 1.0)
# Power Supplies
def create_psu(label, location):
psu_config = atx_psu.AtxpsuConfig()
psu = atx_psu.create_atx_psu(**psu_config.model_dump())
psu.label = label
compound = Compound.make_compound([(psu.part)]).locate(location)
return psu, compound
# ATX PSU1
psu1_config = atx_psu.AtxpsuConfig()
psu1, compound_psu1 = create_psu(
"PSU1",
Location(
Vector(
# PSU on back of chassis
# (psu1_config.atx_psu_width * 2) - TSLOT_SIZE,
# TSLOT_DEEP_LEN + (TSLOT_SIZE * 2),
# TSLOT_HI_LEN - psu1_config.atx_psu_height - (TSLOT_SIZE * 2) - (TSLOT_SIZE / 2),
# ),
# (0, 0, 90),
# PSU on top of chassis
(psu1_config.atx_psu_width * 2) - TSLOT_SIZE,
TSLOT_DEEP_LEN + (TSLOT_SIZE * 2),
TSLOT_HI_LEN,
),
(90, 0, 90),
),
)
compound_psu1.label = "PSU 1"
compound_psu1.color = Color("Black", 1.0)
# ATX PSU2
psu2_config = atx_psu.AtxpsuConfig()
psu2, compound_psu2 = create_psu(
"PSU2",
Location(
Vector(
# PSU on back of chassis
# TSLOT_WIDE_LEN - (TSLOT_SIZE / 2), # wut
# TSLOT_DEEP_LEN + (TSLOT_SIZE * 2),
# TSLOT_HI_LEN - psu1_config.atx_psu_height - (TSLOT_SIZE * 2) - (TSLOT_SIZE / 2), # XXX
# ),
# (0, 0, 90),
# PSU on top of chassis
TSLOT_WIDE_LEN - (TSLOT_SIZE / 2), # XXX
TSLOT_DEEP_LEN + (TSLOT_SIZE * 2),
TSLOT_HI_LEN,
),
(90, 0, 90),
),
)
compound_psu2.label = "PSU 2"
compound_psu2.color = Color("SlateGray", 1.0)
# PCIe GPUs for Immersion
def create_gpu(label, location):
gpu_config = immersion_pci_gpu.ImmersionpcigpuConfig()
gpu = immersion_pci_gpu.create_immersion_pci_gpu(**gpu_config.model_dump())
gpu.label = label
compound = Compound.make_compound([(gpu.part)]).locate(location)
compound.label = label
compound.color = Color("GreenYellow", 1.0)
return gpu, compound
# List of GPUs to create
gpus_to_create = [
{"label": "GPU 1", "offset": 20},
{"label": "GPU 2", "offset": 40},
{"label": "GPU 3", "offset": 60},
{"label": "GPU 4", "offset": 80},
{"label": "GPU 5", "offset": 100},
{"label": "GPU 6", "offset": 120},
{"label": "GPU 7", "offset": 140},
]
all_gpus = []
gpu_compounds = []
for gpu_info in gpus_to_create:
gpu, compound_gpu = create_gpu(
gpu_info["label"],
Location(
Vector(
TSLOT_SIZE + TANK_OFFSET_X + (gpu_info["offset"]),
TSLOT_SIZE
+ tank_config.tank_length
- TANK_THICK
- WEIR_OFFSET
- MB_PLATE_THICKNESS
- MB_RISER
- GPU_OFFSET,
84,
),
(90, 0, 0),
),
)
all_gpus.append(gpu)
gpu_compounds.append(compound_gpu)
all_assembly = Compound(
label="Immersion AI Node",
children=[
compound_chassis,
compound_plate,
compound_lid,
compound_psu1,
compound_psu2,
gpu_compounds[0],
gpu_compounds[1],
gpu_compounds[2],
gpu_compounds[3],
gpu_compounds[4],
gpu_compounds[5],
gpu_compounds[6],
compound_tank,
compound_mb,
# compound_koolance,
],
)
# print(all_assembly.show_topology())
return all_assembly
def display_object(obj: BuildPart):
"""
Display the given object in the IDE with specified color and transparency.
:param obj: The object to be displayed.
:type obj: Any
:param color: The color of the object.
:type color: Tuple[int, int, int]
:param alpha: The transparency of the object.
:type alpha: float
"""
show_object(
obj,
black_edges=False,
)
def parse_args(**kwargs):
"""
Parse the command line arguments.
:param kwargs: Additional arguments to pass to ArgumentParser.parse_args()
:return: The parsed command line arguments.
:rtype: Any
"""
parser = argparse.ArgumentParser(
description="Create and manipulate a atx motherboard."
)
parser.add_argument(
"--display", action="store_false", help="Display the generated plate in IDE"
)
parser.add_argument(
"--mf",
dest="mf_filename",
type=str,
metavar="FILENAME",
help="Export the plate as an 3MF file",
)
parser.add_argument(
"--stl",
dest="stl_filename",
type=str,
metavar="FILENAME",
help="Export the plate as an STL file",
)
parser.add_argument(
"--step",
dest="step_filename",
type=str,
metavar="FILENAME",
help="Export the plate as an STEP file",
)
parser.add_argument(
"--brep",
dest="brep_filename",
type=str,
metavar="FILENAME",
help="Export the plate as an BREP file",
)
args = parser.parse_args(**kwargs)
return args
def main():
args = parse_args()
sys_assembly = compound_assembly()
if any(filename in args for filename in export_formats):
args.display = False
if args.display:
display_object(sys_assembly)
for file_extension, export_function in export_formats.items():
filename = getattr(args, f"{file_extension}_filename")
if filename is not None:
export_function(sys_assembly, filename)
if __name__ == "__main__":
main()