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 :class:`~routing.core.cadmap.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_map`` with ``multi_scale=True`` - Routing multiple pipes with pooling **Source script**: ``scripts/simple_test_cases/large_room_with_obstacles.py`` .. contents:: On this page :local: :depth: 1 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: .. code-block:: python 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: .. code-block:: python voxel_size = 0.21 # 210 mm — about 2× the pipe diameter For the optimizer, use a finer grid: .. code-block:: python 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_radius`` - Optimization 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: .. code-block:: python 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``: .. code-block:: python 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 pipes - **Reduce** ``exact_distances=True`` to ``False`` if voxelization is too slow (slightly less accurate weighting, but much faster) - **Increase** ``voxel_size`` if memory is still an issue — halving the voxel count in each dimension reduces memory by 8× See :doc:`../user_guide/multi_scale` for a complete guide to multi-scale routing. Next Steps ---------- - :doc:`../user_guide/multi_scale` — multi-scale and multi-grid routing guide - :doc:`../user_guide/pooling` — bundling pipes in large assemblies - :doc:`../user_guide/optimization` — optimizer settings for large-scale paths