360 lines
11 KiB
Python
Executable File
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()
|