Tutorial: Routing in Large Spaces with Obstacles#
This tutorial demonstrates routing multiple pipes through a large room environment
(use_case_labyrinth.stp) that contains many fixed and mobile obstacles. For
assemblies of this scale, using a MultiCadMap with
multi-scale grids is essential to keep memory and computation times manageable.
What you will learn:
Splitting a CAD model into fixed and mobile parts
Choosing voxel sizes for large assemblies
Using
MultiCadMap.from_cad_mapwithmulti_scale=TrueRouting multiple pipes with pooling
Source script: scripts/simple_test_cases/large_room_with_obstacles.py
Assembly Structure#
The test assembly contains a large room with several obstacles identified by name. Some obstacles are considered “mobile” (can be repositioned) and some are “fixed”. We partition the CAD primitives into two groups to apply weighting rules to each independently:
from volmdlr.model import VolumeModel
from routing.core.core import RuledVolume
from routing.core.design_rules import WeightingRule
weighting_rule = WeightingRule("linear", 0.0, None, min_value=1, max_value=10)
# Names of mobile obstacles in this assembly
mobile_names = ["EnvSimpleDessia20", "EnvSimpleDessia21", ...]
mobile_primitives = [p for p in volume_model.primitives if p.name in mobile_names]
fixed_primitives = [p for p in volume_model.primitives if p.name not in mobile_names]
mobile_volume = RuledVolume.from_volume_model(
VolumeModel(mobile_primitives), [weighting_rule], name="mobile"
)
fixed_volume = RuledVolume.from_volume_model(
VolumeModel(fixed_primitives), [weighting_rule], name="fixed"
)
Choosing Voxel Size for Large Assemblies#
The assembly dimensions are on the order of 15 × 10 × 10 metres with pipe radii of 50 mm. A coarse voxel size is appropriate:
voxel_size = 0.21 # 210 mm — about 2× the pipe diameter
For the optimizer, use a finer grid:
settings = Settings(voxel_size=0.07, ...) # 70 mm — 3× finer than routing grid
Rule of thumb for large assemblies:
Routing grid:
voxel_size = 1-2 * max_pipe_radiusOptimization grid:
voxel_size_opt = voxel_size / 3
Building the CadMap and MultiCadMap#
Build a regular CadMap first, then wrap it in a MultiCadMap with multi-scale
decomposition:
from routing.core.cadmap import CadMap, MultiCadMap
from routing.core.design_rules import PoolingMode, PoolingRule, PoolingSpecification, TurnPenalizer
pooling_rule = PoolingRule(
pooling_specs=[
PoolingSpecification(
types=["TypeA"],
pooling_mode=PoolingMode.strong_pooling(),
min_distance=0.0,
parallel=True,
)
],
min_distance=0.0,
distance_multiplier=500,
volume_indices=[0, 1],
)
cadmap = CadMap.from_project_inputs(
design_rules=[TurnPenalizer(cost=10.0), pooling_rule],
ruled_volumes=[fixed_volume, mobile_volume],
bounding_box=CadMap.build_bounding_volume(
[fixed_volume, mobile_volume], [], voxel_size
),
voxel_size=voxel_size,
exact_distances=True,
weights_operator="min_distance",
)
# Multi-scale: coarse grid (210 mm) + fine grid (70 mm)
multi_cadmap = MultiCadMap.from_cad_map(
cadmap,
multi_scale=True,
size_factor=3,
bounding_box=cadmap.bounding_box.bounding_box,
)
Routing and Optimization#
Pass the MultiCadMap to RoutePlanner exactly like a single CadMap:
from routing.core.finders import SmartFinder
from routing.core.optimizer import Costs, Settings
from routing.core.route_planner import RoutePlanner
finder = SmartFinder("AStar", "manhattan", "never")
route_planner = RoutePlanner(multi_cadmap, finder)
result = route_planner.generate(
specifications=specifications,
n_iterations=1,
steady_mode=False,
)
# Visualize generated paths
packs = result.collect_packs(only_optimized=False)
result.specific_packs_cad_view(packs)
# Optimize
costs = Costs(
length=1000, sideways=100, short_line=5000,
gravity=5000, bend=250, constraints=5000, interferences=1e8,
)
settings = Settings(
voxel_size=0.07,
max_shift=0.5,
stabilization_threshold=1.0,
n_iterations=5,
max_iter=1000,
)
result = route_planner.optimize(
costs=costs, settings=settings, picked_path="best", max_refinements=0
)
packs = result.collect_packs(only_optimized=True)
result.specific_packs_cad_view(packs)
Memory and Performance Tips#
For assemblies larger than about 10 × 10 × 10 m with 200 mm voxels:
Check memory first:
print(cadmap.nbytes / 1e9, "GB")Use
weights_operator="min_distance"— more accurate than the default"min"Use multi-scale (
MultiCadMap.from_cad_map(multi_scale=True)) to keep the routing grid coarse while preserving accuracy near the pipesReduce
exact_distances=TruetoFalseif voxelization is too slow (slightly less accurate weighting, but much faster)Increase
voxel_sizeif memory is still an issue — halving the voxel count in each dimension reduces memory by 8×
See User Guide: Multi-Scale and Multi-Grid Routing for a complete guide to multi-scale routing.
Next Steps#
User Guide: Multi-Scale and Multi-Grid Routing — multi-scale and multi-grid routing guide
User Guide: Pipe Pooling (Bundling) — bundling pipes in large assemblies
User Guide: Path Geometry Optimization — optimizer settings for large-scale paths