Procedural modelling¶
Describe a model by its spaces and what connects them. The topology engine builds the structure, routes the systems, and finds every place a system crosses a wall or deck. The detail at each crossing is a plain Python function of yours.
The smallest case: two cells that share one wall, and a pipe from a pump in one cell to a tank in the other, so it has to cross that wall. The detail function cuts a circular hole in the wall and reinforces it with a thin-walled sleeve:
import ada
from ada.topo_model import SteelStru, create_pump, create_tank
from ada.topology import CellGrid, DesignRules, TopologyBuilder, run_design
# Two 5 x 5 x 3 m cells sharing one (stiffened) wall at x = 5
cells = [ada.PrimBox("Cell1", (0, 0, 0), (5, 5, 3)), ada.PrimBox("Cell2", (5, 0, 0), (10, 5, 3))]
builder = TopologyBuilder.from_prim_boxes(cells, blueprint=SteelStru(reinforce_internal_walls=True))
structure = builder.build()
# A pump in one cell, a tank in the other, and a pipe between them
pump = create_pump("Pump", origin=(2.5, 2.5, 0.0))
tank = create_tank("Tank", origin=(7.5, 2.5, 0.0))
pipe = ada.PipingSystem("Service", medium="water").connect(pump, "discharge").connect(tank, "inlet")
# The penetration detail: a circular cut through the wall, reinforced with a thin-walled sleeve
def penetration_detail(pen, name, clearance=0.02, wall_thickness=0.008, length=0.3):
r = pen.system.pipe_radius + clearance
p1, p2 = pen.point - pen.normal * length / 2, pen.point + pen.normal * length / 2
for plate in pen.face.associated_part.get_all_physical_objects(by_type=ada.Plate):
plate.add_boolean(ada.PrimCyl(f"{name}_cut", p1, p2, r))
sleeve = ada.PrimCyl(f"{name}_sleeve", p1, p2, r + wall_thickness, color="red")
sleeve.add_boolean(ada.PrimCyl(f"{name}_bore", p1, p2, r))
return ada.Part(name) / sleeve
# Route the pipe through the cells, find where it crosses a wall, and detail each crossing
grid = CellGrid.from_bounds((0, 0, 0), (10, 5, 3), spacing=0.5)
rules = DesignRules(model_penetration=penetration_detail)
result = run_design([pipe], cell_graph=builder.cell_graph, grid=grid, rules=rules)
piping = ada.Part("Piping") / result.route_geometry[pipe.name]
a = ada.Assembly("Penetration") / [structure, ada.Part("Equipment") / [pump, tank], piping, *result.penetration_parts]
The shared wall, the pump and tank, the pipe and its sleeve: drag to orbit, scroll to zoom, click an object to inspect it. The roof deck is left out so it doesn't hide the crossing.
TopologyBuilder.from_prim_boxesturns boxes into cells, and the faces they share into internal walls. TheSteelStrublueprint builds them as steel: columns, girders, decks and a stiffened plate for the shared wall.run_designroutes the pipe through the cell grid and finds where it crosses a wall. For each crossing it callsmodel_penetrationwith aPenetration: the system, the point, the face normal and the face, whoseassociated_partis the wall that was built for it.penetration_detailis the whole rule. It cuts the wall plates and returns the detail part. Swap in another function for another standard, or use the reference one,ada.topo_model.standard_design_rules(), which also handles cable trays and ducts.
Run it with python examples/penetration_detail.py, which opens the model in the viewer.
Topology engine covers blueprints, routing rules, equipment and whole
models built from a document.