Tutorial: Importing Ports from Excel ====================================== For assemblies with many connection points it is more convenient to define ports in a spreadsheet than to hard-code them in Python. :class:`~routing.core.core.Port` includes a class method that reads an Excel file and returns a list of ``Port`` objects. .. contents:: On this page :local: :depth: 1 Excel Format ------------ The Excel file must have the following columns (in order) in the first worksheet. The first row is treated as a header and is automatically skipped: .. list-table:: :header-rows: 1 :widths: 10 20 70 * - Column - Header name - Description * - A - ``name`` - Port identifier string (e.g. ``"port_inlet_1"``) * - B - ``coordinates_x`` - X coordinate in metres * - C - ``coordinates_y`` - Y coordinate in metres * - D - ``coordinates_z`` - Z coordinate in metres * - E - ``direction_x`` - X component of the pipe exit direction * - F - ``direction_y`` - Y component * - G - ``direction_z`` - Z component * - H - ``length`` - Minimum straight length at the port before the first turn (metres) Direction vectors do not need to be unit vectors — they are normalized automatically. **Example spreadsheet:** .. code-block:: text name | coord_x | coord_y | coord_z | dir_x | dir_y | dir_z | length inlet_port | 0.05 | -0.4 | 0.1 | 0 | 1 | 0 | 0.1 outlet_port | 0.05 | 0.8 | 0.1 | 0 | -1 | 0 | 0.1 branch_port_A | 0.37 | -0.4 | 0.3 | 0 | 1 | 0 | 0.08 branch_port_B | 0.45 | 0.8 | 0.18 | 0 | -1 | 0 | 0.04 Reading Ports from a File -------------------------- .. code-block:: python from routing.core.core import Port with open("ports.xlsx", "rb") as f: ports = Port.from_xlsx_stream(f) # ports is a list of Port objects for port in ports: print(port.name, port.coordinates, port.direction) The method returns a flat ``list[Port]``. If your spreadsheet defines port *pairs* (start + end for each pipe), you will need to pair them manually. Pairing Ports into Specifications ---------------------------------- A common pattern is to store ports in consecutive pairs (start, end) and pair them in a loop: .. code-block:: python from routing.core.core import Port, PipeType, Specification from piping_3d import piping with open("ports.xlsx", "rb") as f: ports = Port.from_xlsx_stream(f) # Pair consecutive ports as (start, end) port_pairs = [(ports[i], ports[i + 1]) for i in range(0, len(ports), 2)] # One pipe type for all pipe_type = PipeType( section=piping.Section(radius_equivalent=0.015), name="HydraulicLine", radius_of_curvature_ratio_min=1.5, color=(0, 100, 200), type_="Hydraulic", ) specifications = [ Specification(start, end, pipe_type, name=f"route_{i}") for i, (start, end) in enumerate(port_pairs) ] Alternatively, use naming conventions to match starts and ends: .. code-block:: python # Ports named "inlet_" pair with "outlet_" port_dict = {p.name: p for p in ports} specifications = [ Specification( port_dict[f"inlet_{i}"], port_dict[f"outlet_{i}"], pipe_type, name=f"route_{i}", ) for i in range(n_pipes) ] Next Steps ---------- - :doc:`../user_guide/core_concepts` — complete Port and Specification reference - :doc:`basic_routing` — full routing tutorial