Design Rules Reference#

This page is the complete parameter reference for every design rule class in routing.core.design_rules.

Where to Attach Rules#

Design rules can be attached in two places:

  1. Per-volume rules — attach to a RuledVolume via its design_rules list. The rule applies relative to that CAD surface’s distance field.

    from routing.core.core import RuledVolume
    from routing.core.design_rules import WeightingRule
    
    volume = RuledVolume.from_volume_model(
        volume_model, design_rules=[WeightingRule(...)], name="v1"
    )
    
  2. Global rules — pass to CadMap.from_project_inputs(design_rules=[...]) . These rules apply everywhere in the routing space.

    cadmap = CadMap.from_project_inputs(
        design_rules=[TurnPenalizer(cost=3.0), GravityRule(min_value=0.02)],
        ruled_volumes=[...],
        ...
    )
    

The table below shows where each rule can be used:

Rule

Per-volume

Global

Notes

WeightingRule

Requires a distance field; attach to a volume

TurnPenalizer

Applies at every direction change

DistanceAttributeToggler

Tags voxels; usually paired with ClampingConstraint

GravityRule

Automatically instantiated if PipeType.min_slope and/or PipeType.max_slope are set in PipeType ; Can be instantiated at CadMap level for being applied to all Specification

StraightLengthRule

Automatically instantiated for a specific Specification if PipeType.length_before_turn_min != 0

ClampingConstraint

Requires DistanceAttributeToggler("clampable", ...) on the same volume. Can be set for each PipeType with a UserProperty which name is set to “clampable”.

ShapedWeightingRule

Zone defined by an arbitrary 3D solid ; instantiated as a global_rule at CadMap level.

PoolingRule

Governs pipe bundling; see User Guide: Pipe Pooling (Bundling)

WeightingRule#

Assigns a cost multiplier to voxels based on their distance from a CAD surface.

from routing.core.design_rules import WeightingRule

rule = WeightingRule(
    function="linear",
    min_length=0.0,
    max_length=0.2,
    min_value=1,
    max_value=10,
)

Parameter

Type

Description

function

str

Shape of the cost curve. One of: "linear", "sqrt", "constant".

min_length

float

Distance (m) at which the rule starts applying. Typically 0.0 (at the surface).

max_length

float

Distance (m) at which the rule stops applying. Beyond this, the rule has no effect.

min_value

float

Cost multiplier at min_length. 1.0 is neutral; lower = cheaper.

max_value

float

Cost multiplier at max_length.

Cost function shapes:

  • "linear": cost = min_value + (max_value - min_value) × (d / max_length)

  • "sqrt": cost increases with the square root of distance (gentler than linear)

  • "constant": cost = min_value everywhere in the range

Attach to: RuledVolume.design_rules

TurnPenalizer#

Adds a fixed cost every time the path changes direction.

from routing.core.design_rules import TurnPenalizer

rule = TurnPenalizer(cost=3.0)

Parameter

Type

Description

cost

float

Cost penalty per turn, in units of a single straight-voxel step. Default: 50.0.

The penalty is zero when the direction is unchanged and cost when the direction changes (any angle). Increase this to produce longer but straighter paths.

Attach to: CadMap.from_project_inputs(design_rules=[...]).

CostRule (base class)#

CostRule is the base class for building custom per-step cost rules (like TurnPenalizer). Subclass it and override:

compute_cost(self, node, neighbor, vector) float

Called for every candidate edge during pathfinding. Return the additional cost of moving from node to neighbor. Return 0.0 for no penalty.

  • node / neighbor: SmartGridNode

  • vector: direction tuple (dx, dy, dz) of the candidate move

CostRule.compute_heuristic always returns 0.0 by design — cost rules add traversal cost rather than heuristic penalties.

Example — reproducing TurnPenalizer’s logic:

from routing.core.design_rules import CostRule

class MyTurnPenalizer(CostRule):

    def __init__(self, cost: float = 50.0):
        super().__init__()
        self.cost = cost

    def compute_cost(self, node, neighbor, vector):
        if node.direction == vector:
            return 0.0   # no direction change, no penalty
        return self.cost  # any turn costs self.cost

Pass the rule as a global rule to CadMap.from_project_inputs(design_rules=[...]).

DistanceAttributeToggler#

Tags voxels within a distance band with a boolean attribute. Other constraint rules (e.g. ClampingConstraint) read these attributes to decide where they apply.

from routing.core.design_rules import DistanceAttributeToggler

rule = DistanceAttributeToggler(
    attribute="clampable",
    min_distance=0.0,
    max_distance=0.08,
)

Parameter

Type

Description

attribute

str

Name of the boolean attribute to set. Standard names: "clampable" (for ClampingConstraint), "user_defined" (for custom constraint rules).

min_distance

float

Near edge of the distance band (m).

max_distance

float

Far edge of the distance band (m). Voxels beyond this are not tagged.

Attach to: RuledVolume.design_rules (same volume as ClampingConstraint).

GravityRule#

Enforces a minimum drainage slope for gravity-drained pipes (drain lines, coolant returns). Paths that slope insufficiently receive a cost penalty.

from routing.core.design_rules import GravityRule

rule = GravityRule(
    min_value=0.02,
    max_value=0.5,
)

Parameter

Type

Description

min_value

float

Minimum required slope in m/m. Example: 0.02 = 2% grade.

max_value

float

Maximum acceptable slope in m/m. Set to None for no upper limit.

distance_multiplier

float

Scales the penalty for slope violations. Default: 500.0.

Also set PipeType.min_slope to the same value so the router knows which pipe types require slope enforcement.

Attach to: CadMap.from_project_inputs(design_rules=[...]) for a global requirement.

Only Specify in PipeType: min_slope and/or max_slope for a constraint specific to a PipeType.

StraightLengthRule#

Penalizes turns that occur before a minimum straight run. Use this when your pipe or fitting catalogue requires a certain run-in distance before each bend.

from routing.core.design_rules import StraightLengthRule

rule = StraightLengthRule(
    min_value=0.030,
    distance_multiplier=2.0,
)

Parameter

Type

Description

min_value

float

Minimum straight segment (m) required before each turn.

distance_multiplier

float

Scales the penalty cost for violations. Default: 500.0.

Also set PipeType.length_before_turn_min to the same value.

Attach to: CadMap.from_project_inputs(design_rules=[...]).

Only Specify in PipeType: length_before_turn_min != 0 for a constraint specific to a PipeType.

ClampingConstraint#

Specifies how pipe clamps are placed along the route. Works with a DistanceAttributeToggler("clampable", ...) on the same RuledVolume.

from routing.core.design_rules import ClampingConstraint

rule = ClampingConstraint(
    clamp_length=0.04,
    clamp_step=0.25,
)

Parameter

Type

Description

clamp_length

float

Distance from the surface (m) at which a clamp sits. Should match the physical clamp bracket depth.

clamp_step

float

Minimum spacing between successive clamps along the pipe (m).

distance_multiplier

float

Scales the clamping cost contribution. Default: 19.0.

Attach to: RuledVolume.design_rules, on the same volume as the DistanceAttributeToggler("clampable", ...).

ShapedWeightingRule#

Applies weight modifications based on whether a voxel is inside an arbitrary 3D shape (box, cone, sphere, imported solid). Four zone types are available.

from routing.core.design_rules import ShapedWeightingRule

# Attractive
rule = ShapedWeightingRule.attractive_zone(shape=my_solid, strength=0.1)

# Repulsive
rule = ShapedWeightingRule.repulsive_zone(shape=my_solid, strength=10.0)

# Forbidden
rule = ShapedWeightingRule.forbidden_zone(shape=my_solid, cut_ports=False)

# Forced
rule = ShapedWeightingRule.forced_zone(shape=my_solid)

Constructor parameters (direct):

Parameter

Type

Description

shape

volmdlr Solid or VolumeModel

The 3D shape defining the zone extent. Must support triangulation().

coefficient

float

Weight multiplier inside the zone. Meaning depends on zone_type.

zone_type

str

One of: "attractive", "repulsive", "forbidden", "forced".

cut_ports

bool

For forbidden zones only: if True, silently truncate port extensions that enter the zone rather than raising an error. Default: False.

name

str

Optional identifier.

Zone behaviour summary:

zone_type

coefficient meaning

Effect inside the shape

"attractive"

0 < coeff < 1 (lower = stronger)

Weight × coeff — path prefers to enter

"repulsive"

coeff > 1 (higher = stronger)

Weight × coeff — path avoids if possible

"forbidden"

ignored

Weight = 0 — voxels are non-traversable

"forced"

ignored

Weight × 1e-16 inside; × 100 outside — path must enter

Attach to: CadMap.from_project_inputs(design_rules=[...]).

See Tutorial: Shaped Routing Zones for a full tutorial.

PoolingRule#

Controls pipe bundling (grouping pipes of compatible types together). See the dedicated User Guide: Pipe Pooling (Bundling) guide for full details.

from routing.core.design_rules import (
    PoolingMode, PoolingSpecification, PoolingRule
)

pooling_specs = [
    PoolingSpecification(
        types=["TypeA", "TypeB"],
        pooling_mode=PoolingMode.strong_pooling(),
        min_distance=0.0,
        parallel=False,
    )
]

rule = PoolingRule(
    pooling_specs=pooling_specs,
    min_distance=0.0,
    distance_multiplier=500,
    volume_indices=[0, 1],
)

Parameter

Type

Description

pooling_specs

list of PoolingSpecification

Specifies which pipe type pairs are pooled or segregated, and how strongly.

min_distance

float

Default minimum separation (m) between pipes with no explicit spec.

distance_multiplier

float

Scales the pooling cost contribution. Default: 500.0.

volume_indices

list of int

Indices of ruled_volumes that the pooling rule considers. [] = all.

Attach to: CadMap.from_project_inputs(design_rules=[...]).

ConstraintRule (base class)#

ConstraintRule is the base class for building custom constraint rules. Subclass it to encode any voxel-level constraint not provided by the built-in rules.

Methods to override:

compute_heuristic(self, node, neighbor, vector)

Called at every step during A* to steer the pathfinder in real time. Return 0.0 if the move from node to neighbor is allowed; return self.distance_multiplier (or another penalty) to discourage it.

  • node / neighbor: SmartGridNode

  • vector: direction tuple (dx, dy, dz) of the candidate move

Without this method, pathfinding ignores your constraint; it will find paths that violate it and only detect the violation after the fact.

fast_check(self, voxels, voxel_size, bool_matrix)

Called on a complete candidate path during post-routing optimization. voxels is a list of (x, y, z) index tuples; bool_matrix is a numpy bool array marking where the rule’s attribute is active. Return 0.0 if satisfied, or a penalty cost otherwise.

A full custom constraint typically implements both.

from routing.core.design_rules import ConstraintRule

class UserDefinedConstraint(ConstraintRule):
    """Allow routing only through voxels tagged 'user_defined'."""

    def __init__(self, name: str = ""):
        super().__init__(attribute="user_defined", name=name)

    def compute_heuristic(self, node, neighbor, vector):
        """Real-time check: penalize moves toward voxels not tagged 'user_defined'."""
        if not neighbor.get_custom_attr(self.attribute):
            return self.distance_multiplier
        return 0.0

    def fast_check(self, voxels, voxel_size, bool_matrix):
        """Post-routing check: penalize any path segment missing the attribute."""
        if any(bool_matrix[v] for v in voxels):
            return self.distance_multiplier
        return 0.0

Note

ConstraintRule.__init__ requires attribute: str as a positional argument. Every subclass must call super().__init__(attribute="your_attribute_name"). The attribute name must match the DistanceAttributeToggler used on the same RuledVolume.

Use a DistanceAttributeToggler on the relevant RuledVolume to set the attribute that your custom rule reads.

See Tutorial: Engineering Design Rules for a working example.