User Guide: Accessing Routing Results#

After generate() or optimize(), the RoutingResults object contains all routing solutions and visualization helpers.

RoutingResults Object#

result = route_planner.generate(specifications=specs, n_iterations=1)
# or
result = route_planner.optimize(costs=costs, settings=settings, picked_path="best")

Key attributes and methods:

  • result.solutions — list of Solution objects, one per specification, in planning order: a tee after the conduit it taps, your order otherwise

  • result.routed_solutions — the solutions a route joins

  • result.solution(name) — the solution of that name

  • result.summary() — one line per conduit: the conduit it taps, whether it is routed, its number of alternatives, its generated cost, its optimized cost, whether it is well optimized

  • result.cadmap — the CadMap used for routing

A solution is named after its specification, else after its pipe type; a name several specifications share takes an index suffix in planning order (hydraulic_1, hydraulic_2).

print(result.summary())
conduit         taps             routed paths     cost  optimized  well
O7                                  yes     1    224.3     7628.3  True
tee_O7_thin     O7                  yes     1      3.0     1114.4  True

Visualizing Generated Paths#

The paths produced by generate() follow the voxel grid. Display them with:

# Interactive parallel-coordinates plot (compare all solutions)
result.plot_data_generated().plot()

This opens an interactive view where you can select which pipes to highlight and inspect their cost metrics.

Visualizing Optimized Paths#

After optimize(), display the refined geometry:

result.plot_data_optimized().plot()

The parallel plot shows per-pipe metrics including: cost, weighted_length, n_bends, deviation, sideways_cost.

3D CAD View with Packs#

collect_packs() groups pipes that share a common sub-path into packs (bundles).

# Collect packs from generated paths
packs = result.collect_packs(only_optimized=False)

# Collect packs from optimized paths (only valid after optimize())
packs = result.collect_packs(only_optimized=True)

# Open interactive 3D viewer showing pipes and packs
result.specific_packs_display_3d(packs)

specific_packs_display_3d opens a BabylonJS 3D viewer in your browser showing:

  • All routed pipe segments

  • Highlighted pack bundles

  • The CAD voxelization

Bifurcation Points#

Bifurcation points are the locations where a pipe bundle splits into separate branches. They are useful for placing junction boxes, tees, or connectors.

bifurcation_pts = result.cadmap.get_bifurcation_points(
    packs=packs,
    tolerance=0.04,    # grouping tolerance in metres
    with_ports=False,  # exclude port positions from the output
)

print("Bifurcation points:", bifurcation_pts)
# [array([x1, y1, z1]), array([x2, y2, z2]), ...]

tolerance controls when two nearby bifurcation candidates are merged into one. Omitted, it is twice the coarsest voxel size of the map.

Alternatives, Refinements and the Pick#

Each Solution keeps every route found for its conduit, and stands by one of them:

  • solution.alternatives — one Alternative per route found (n_iterations above 1 searches several, except with Theta* or through forced zones). Each carries its grid_path, the ports it joins (input_port, output_port) and its refinements: one Refinement per optimization pass.

  • solution.picked — the alternative the solution stands by: the one you retained with solution.pick(index) (picked_index), else the cheapest generated path. solution.pick_nearest(point) retains the alternative whose end port stands nearest a point: for a tee, the site where it taps.

  • solution.best_path / solution.best_path_index — the GridPath of that alternative and its rank.

  • solution.best_pipe — the geometry the packs, the 3D views and the tees use: the cheapest refinement the optimizer’s checks accepted, else the generated path of the alternative the solution stands by. A refinement the checks rejected (its name ends with _failed) is kept for diagnosis, never laid.

  • solution.input_port / solution.output_port — the ports of the alternative the solution stands by; for a tee, the output port is the tap port, on the axis of the conduit it taps (solution.terminals.tapped).

  • solution.is_routed, solution.is_optimized, solution.is_well_optimized — routed, optimized, and optimized with a refinement the checks accepted.

  • solution.polyline_points — the best pipe’s points as an (N, 3) array, ready for export or for your own geometric checks.

for solution in result.routed_solutions:
    print(solution.name, len(solution.alternatives), solution.best_pipe.cost)
    for index, alternative in enumerate(solution.alternatives):
        refined = [refinement.cost for refinement in alternative.refinements]
        print("   ", index, alternative.grid_path.cost, refined)

result.solution("O7").pick(1)           # stand by the second route of O7 from now on
result = route_planner.optimize(costs=costs, settings=settings)   # optimizes that route

Note

collect_packs(only_optimized=True) and the 3D views read best_pipe: the alternative the solution stands by, refined when the optimizer succeeded. A solution not routed has best_pipe and best_path set to None; routed_solutions leaves it out.

Complete Inspection Example#

from routing.core.finders import SmartFinder
from routing.core.optimizer import Costs, Settings
from routing.core.route_planner import RoutePlanner

route_planner = RoutePlanner(cadmap, SmartFinder("AStar", "manhattan", "never"))

# Generate
result = route_planner.generate(specifications=specs, n_iterations=1)

# Inspect generated paths
result.plot_data_generated().plot()
packs_gen = result.collect_packs(only_optimized=False)
result.specific_packs_display_3d(packs_gen)

# Optimize
costs = Costs(length=1000, sideways=100, short_line=5000,
              gravity=5000, bend=250, constraints=5000, interferences=1e8)
settings = Settings(voxel_size=0.01, max_shift=0.1, n_iterations=3, max_iter=1000)
result = route_planner.optimize(costs=costs, settings=settings, picked_path="best")

# Inspect optimized paths
print(result.summary())
result.plot_data_optimized().plot()
packs_opt = result.collect_packs(only_optimized=True)
result.specific_packs_display_3d(packs_opt)

# Find bifurcation points
bifurcation_pts = result.cadmap.get_bifurcation_points(
    packs_opt, tolerance=0.04, with_ports=False
)
result.cadmap.show_with_points(bifurcation_pts)

Next Steps#