routing.core.design_rules#

Design rules module.

class routing.core.design_rules.BoundedAttributeRule(attribute: str, min_value: float | None = None, max_value: float | None = None, distance_multiplier: float = 500.0, name: str = '')[source]#

Bases: ConstraintRule

A class representing a constraint rule with bounded attribute values.

This class inherits from ConstraintRule, and enforces constraints based on whether an attribute value falls within a specified range (min_value, max_value).

Parameters:
  • attribute – The name of the attribute to check.

  • min_value – The optional minimum value for the attribute. Defaults to None.

  • max_value – The optional maximum value for the attribute. Defaults to None.

  • distance_multiplier – The multiplier for computing constraint cost based on distance. Defaults to 500.0.

  • name – The name to identify this rule. Defaults to ‘’.

property bounds: tuple[float, float]#

The bounds as the compiled pricers read them: (min_value, max_value), NaN for an absent one.

get_function() → Callable[source]#

Get the constraint function as a callable: whether a value lies within the bounds (min_value <= x < max_value).

An absent bound does not constrain; without any, every value is within. The test is the compiled search’s own (within_bounds()).

move_pricer(attribute_map: AttributeMap) → MovePricer | None[source]#

Build the bounded-attribute pricer of the compiled search: the rule’s heuristic, read by attribute name.

to_voxelized_rule(voxel_size: float) → BoundedAttributeRule[source]#

Convert rule to voxelized version with values in voxel units.

Creates a new BoundedAttributeRule with min_value and max_value converted from world coordinates to voxel units by dividing by voxel_size and rounding up. This allows the rule to work directly with voxel-based attribute computations.

Parameters:

voxel_size – Size of a voxel in length units. Used to convert attribute value thresholds from world coordinates to voxel units.

Returns:

New BoundedAttributeRule instance with voxelized value parameters.

class routing.core.design_rules.BoundedCustomLengthRule(attribute: str, min_value: float | None = None, max_value: float | None = None, distance_multiplier: float = 500.0, name: str = '')[source]#

Bases: BoundedAttributeRule

A class representing a constraint rule with bounded custom length attribute values.

This class inherits from BoundedAttributeRule, and enforces constraints based on whether a custom length attribute value falls within a specified range (min_value, max_value).

Parameters:
  • attribute – The name of the custom attribute to check.

  • min_value – The optional minimum value for the attribute. Defaults to None.

  • max_value – The optional maximum value for the attribute. Defaults to None.

  • distance_multiplier – The multiplier for computing constraint cost based on distance. Defaults to 500.0.

  • name – The name to identify this rule. Defaults to ‘’.

move_pricer(attribute_map: AttributeMap) → MovePricer | None[source]#

Build the bounded-length pricer of the compiled search: the rule’s heuristic.

class routing.core.design_rules.BranchClampDistanceRule(branch_clamp_min_distance: float, clamps_min_distance: float, clamps_max_distance: float, cost: float = 50.0, name: str = '')[source]#

Bases: CostRule

Design rule: clamp spacing at shared / clampable boundaries (inside and outside).

Applies when entering or exiting is_shared zones and on clampable transitions.

compute_cost(node: SmartGridNode, neighbor: SmartGridNode, vector: tuple[int, int, int] = None) → float[source]#

Compute cost for is_shared / clampable boundary transitions (shared-zone enter/exit).

Uses _clampable_transition_cost() when crossing is_shared, and enforces clamps_min_distance when leaving a clampable segment.

fast_check(voxels: list[tuple[int, int, int]], voxel_size: float, bool_matrix: ndarray) → float[source]#

Fast check for BranchClampDistanceRule (stub).

to_voxelized_rule(voxel_size: float) → BranchClampDistanceRule[source]#

Give the rule a grid of voxel_size prices: without lengths to convert, the rule itself.

Its parameters and the state update_with_solutions() wrote reach the grid as they are. A rule whose parameters are lengths overrides this with a copy in voxel units.

Parameters:

voxel_size – Size of a voxel in length units

class routing.core.design_rules.BranchPortDistanceRule(port_branch_min_distance: float = 0.0, branches_min_distance: float = 0.0, max_pipes_per_branch: int = 0, cost: float = 50.0, neutral_fiber_condition: bool = False, enforcement: RuleEnforcement | None = None, name: str = '')[source]#

Bases: CostRule

Design rule: distances between branch junctions and between branches and ports.

Applies on is_shared boundary transitions (enter/exit) and optional neutral-fiber adjacency on the bundle graph. The bundle graph and forbidden voxels are refreshed from the CadMap via update_with_solutions().

compute_cost(node: SmartGridNode, neighbor: SmartGridNode, vector: tuple[int, int, int] = None) → float[source]#

Compute cost for is_shared boundary transitions and optional neutral-fiber adjacency.

Delegates exiting/entering shared transitions to _exiting_shared_cost() / _entering_shared_cost(); in neutral-fiber mode, penalizes moves without a bundle-graph edge.

consolidation_fusion_distance() → float[source]#

Fanout-fusion distance at skeleton consolidation: the rule’s branches_min_distance.

evaluate_on_bundle(measures: BundlePathMeasures) → ndarray[source]#

Length deficits along the harness path; positive entry = segment shorter than its minimum.

branches_min_distance bounds inter-junction branch lengths, port_branch_min_distance the port lead-ins. A zero minimum disables its block (the knob is off), as on the grid.

fast_check(voxels: list[tuple[int, int, int]], voxel_size: float, bool_matrix: ndarray) → float[source]#

Fast check for BranchPortDistanceRule (stub).

plot_data(reference_path: str = '#', axis_voxels: dict[tuple[int, int, int], tuple[int, int, int]] | None = None, **kwargs)#

Specific display tag for PlotData. Add a picture argument.

set_port_nodes(source: tuple[float, float, float], target: tuple[float, float, float]) → None[source]#

Set port node positions for rule computation.

Base implementation does nothing. Subclasses can override to use port positions in their computations.

Parameters:
  • source – Source port coordinates

  • target – Target port coordinates

to_voxelized_rule(voxel_size: float) → BranchPortDistanceRule[source]#

Give the rule a grid of voxel_size prices: without lengths to convert, the rule itself.

Its parameters and the state update_with_solutions() wrote reach the grid as they are. A rule whose parameters are lengths overrides this with a copy in voxel units.

Parameters:

voxel_size – Size of a voxel in length units

update_with_solutions(is_shared_voxels: set[tuple[int, int, int, int]], bifurcation_nodes: list[tuple[int, int, int, int]], port_nodes: list[tuple[int, int, int, int]] | None = None, axis_voxels: dict[tuple[int, int, int, int], tuple[int, int, int, int]] | None = None, bundle_graph: Graph = None) → None[source]#

Update the bundle graph with solution wires and calculate minimum distances.

Uses only axis_voxels (no packs): mapping from CadMap / packs for axis-aligned representatives. Port graph nodes and bifurcation points are passed in by the caller. Always clears forbidden_voxels and the bundle graph (when not provided) before updating.

Parameters:
  • is_shared_voxels – Shared voxels as 4-tuples (x, y, z, grid_id).

  • bifurcation_nodes – List of graph nodes (4-tuples (x,y,z,grid_id)) for bifurcations (excluding ports).

  • port_nodes – List of graph nodes (4-tuples) for port endpoints on the bundle graph.

  • axis_voxels – Pre-computed mapping (x,y,z,grid_id) -> representative 4-tuple.

  • bundle_graph – Pre-built networkx Graph from CadMap (required).

class routing.core.design_rules.BundlePathMeasures(inter_junction_lengths: ndarray, lead_in_lengths: ndarray)[source]#

Bases: object

Along-path lengths read off a bundle, the input to the bundle-level rule hooks.

Pure data so design rules consume bundle geometry without importing the bundling module (kept a leaf to forbid an import cycle). Filled by Bundle.path_measures.

Parameters:
  • inter_junction_lengths – Length of each harness branch between two junctions.

  • lead_in_lengths – Length of each port lead-in (port to its first junction).

inter_junction_lengths: ndarray#
lead_in_lengths: ndarray#
class routing.core.design_rules.ClampingConstraint(clamp_length: float, clamp_step: float, distance_multiplier: float = 19.0, name: str = '')[source]#

Bases: ConstraintRule

A class representing a constraint rule for clamping requirements.

This class inherits from ConstraintRule, and enforces constraints related to clamping, ensuring pipes maintain appropriate spacing for clamp installation. The rule ensures that pipes can be clamped at regular intervals by penalizing paths that exceed the maximum distance (clamp_step) between clampable locations.

The constraint works by: - Setting a “clampable” attribute on voxels that can accommodate clamps - Tracking the length of pipe segments without clamping - Penalizing paths where the unclamped length exceeds clamp_step

Parameters:
  • clamp_length – The minimum length of pipe segment that requires clamping. This is the length requirement for a clamp to be effective.

  • clamp_step – The maximum distance (in length units) allowed between two clampable locations. Paths exceeding this distance are penalized.

  • distance_multiplier – The multiplier for computing constraint cost based on distance. Higher values create stronger penalties. Defaults to 19.0.

  • name – The name to identify this rule. Defaults to ‘’.

fast_check(voxels: list[tuple[int, int, int]], voxel_size: float, bool_matrix: ndarray) → float[source]#

Fast constraint check for a list of voxels without full pathfinding.

Performs a quick validation of a path segment to check if it violates clamping constraints. The method:

  1. Converts clamp_step and clamp_length to voxel units

  2. If path is shorter than clamp_step, returns 0.0 (no violation)

  3. Extracts clampable attribute values for all voxels in the path

  4. Splits the path into consecutive segments of clampable/non-clampable voxels

  5. Tracks unclampable segments and checks if they exceed clamp_step

  6. Accumulates total violation length (unclampable segments > clamp_step)

Returns the total violation length in world coordinates, which represents the cumulative distance where clamping constraints are violated. A value of 0.0 means the path satisfies all clamping constraints.

This method is used for early validation of paths before committing to full pathfinding, improving performance by rejecting invalid paths quickly.

Parameters:
  • voxels – List of voxel indices (i, j, k) representing a path segment to check for clamping violations.

  • voxel_size – Size of a voxel in length units. Used to convert clamp_step and clamp_length to voxel units.

  • bool_matrix – Boolean matrix indicating which voxels have the “clampable” attribute set to True. Shape should match the voxel grid.

Returns:

Total violation length in world coordinates. Returns 0.0 if the path satisfies all clamping constraints, otherwise returns the cumulative distance where constraints are violated (in length units).

move_pricer(attribute_map: AttributeMap) → MovePricer | None[source]#

Build the clamping pricer of the compiled search: the rule’s heuristic.

to_voxelized_rule(voxel_size: float) → ClampingConstraint[source]#

Convert rule to voxelized version with lengths in voxel units.

Creates a new ClampingConstraint with clamp_length and clamp_step converted from world coordinates to voxel units by dividing by voxel_size and rounding up. This allows the rule to work directly with voxel-based length computations.

The conversion ensures that clamping constraints are properly scaled for the voxel grid resolution, maintaining the same physical constraints but expressed in voxel units.

Parameters:

voxel_size – Size of a voxel in length units. Used to convert clamp_length and clamp_step from world coordinates to voxel indices.

Returns:

New ClampingConstraint instance with voxelized length parameters.

class routing.core.design_rules.ConstraintRule(attribute: str, distance_multiplier: float = 500.0, name: str = '')[source]#

Bases: DesignRule

A class representing a design rule that enforces constraints based on boolean attributes.

This class inherits from DesignRule, and prevents routing through voxels with a specific boolean attribute set to True. This is used to enforce hard constraints like “no routing near sensitive components” or “must stay in designated zones”.

Parameters:
  • attribute – The name of the boolean attribute that triggers the constraint. Voxels with this attribute set to True will be blocked or heavily penalized.

  • distance_multiplier – The multiplier for computing constraint cost based on distance. Higher values create stronger constraints. Defaults to 500.0.

  • name – The name to identify this constraint rule. Defaults to ‘’.

fast_check(voxels: list[tuple[int, int, int]], voxel_size: float, bool_matrix: ndarray) → float[source]#

Fast constraint check for a list of voxels.

Base implementation returns 0.0 (no constraint violation). Subclasses can override to provide fast constraint checking without full pathfinding. This is used for early validation of paths.

Parameters:
  • voxels – List of voxel indices to check.

  • voxel_size – Size of a voxel in length units.

  • bool_matrix – Boolean matrix indicating which voxels have the constraint attribute set to True.

Returns:

Constraint violation cost (0.0 means no violation).

class routing.core.design_rules.CostRule(cost: float = 50.0, name: str = '')[source]#

Bases: DesignRule

A base class for design rules that apply a cost penalty.

This class inherits from DesignRule, and provides a constant cost penalty that can be applied during pathfinding. Subclasses like TurnPenalizer extend this to apply costs based on specific conditions.

Parameters:
  • cost – The cost penalty value to apply. Higher values create stronger penalties during pathfinding. Defaults to 50.0.

  • name – The name to identify this cost rule. Defaults to ‘’.

class routing.core.design_rules.DesignRule(name: str = '')[source]#

Bases: DessiaObject

A base class for all design rules used in routing.

This class inherits from DessiaObject, and defines how pipes interact with volumes and space during routing. They can provide weights (preferences), costs (penalties), constraints, and heuristics for pathfinding algorithms.

enforcement mirrors evaluate_on_bundle(): neutral on the base class, declared by the rules relevant at the bundle level to control their optimization form.

Parameters:

name – The name to identify this design rule. Defaults to ‘’.

compute_cost(node: SmartGridNode, neighbor: SmartGridNode, vector: tuple[int, int, int] | None = None) → float[source]#

Compute cost from DesignRule between node and neighbor.

A rule the search prices natively answers through its compiled pricer (move_pricer()), the single definition of its pricing; a rule without one prices nothing. A rule of your own overrides this.

Parameters:
  • node – Current grid node

  • neighbor – Neighbor grid node

  • vector – Optional direction vector

Returns:

Cost value (default: 0.0)

compute_heuristic(node: SmartGridNode, neighbor: SmartGridNode, vector: tuple[int, int, int] | None = None) → float[source]#

Compute heuristic from DesignRule between node and neighbor to get to target.

A rule the search prices natively answers through its compiled pricer (move_pricer()), the single definition of its pricing; a rule without one weighs nothing. A rule of your own overrides this.

Parameters:
  • node – Current grid node

  • neighbor – Neighbor grid node

  • vector – Optional direction vector

Returns:

Heuristic value (default: 0.0)

compute_weights(distances: ndarray) → ndarray[source]#

Compute weights from distance matrix.

Base implementation returns zeros. Subclasses should override to provide actual weight computation based on distances to volumes.

Parameters:

distances – Distance matrix or array from which to compute weights

Returns:

Weight array of same shape as distances (default: zeros)

consolidation_fusion_distance() → float[source]#

Distance under which close branch endpoints fuse into one junction during skeleton consolidation.

Base implementation returns 0.0 (no fusion requirement). Rules constraining branch spacing override it, so the bundle ⇄ skeleton round-trip reads its consolidation recipe off the attached rules without depending on any concrete rule class.

enforcement: RuleEnforcement | None = None#
evaluate_on_bundle(measures: BundlePathMeasures) → ndarray[source]#

Constraint violations vector for the bundle optimizer.

Base implementation returns an empty array (no violation). Subclasses relevant at the bundle level override to return one float per atomic constraint (e.g. per BundleLine or per junction). Values <= 0 mean satisfied; positive values are violation magnitudes — the convention matches pymoo’s G(x) <= 0 feasibility test.

Shape must remain stable for a fixed bundle topology so that the concatenated G(x) vector keeps a consistent dimension across evaluations.

Parameters:

measures – Along-path lengths of the current bundle geometry.

Returns:

Float array of constraint violations (default: empty).

property forbids_zones: bool#

Check if current rule is forbidding some zones.

move_pricer(attribute_map: AttributeMap) → MovePricer | None[source]#

Build the compiled pricer of the rule, the single definition of its cost and heuristic on a move; None without one.

The rules the search prices natively override this; attribute_map resolves the toggled attributes such a pricer reads. A rule redefining its pricing methods is priced through them instead (prices_moves, PythonPricer, chosen by the grid).

property prices_moves: bool#

Whether the rule prices moves through methods of its own: it redefines compute_cost() or compute_heuristic(), whose base versions answer through the compiled pricer.

set_port_nodes(source: tuple[float, float, float], target: tuple[float, float, float]) → None[source]#

Set port node positions for rule computation.

Base implementation does nothing. Subclasses can override to use port positions in their computations.

Parameters:
  • source – Source port coordinates

  • target – Target port coordinates

to_voxelized_rule(voxel_size: float) → DesignRule[source]#

Give the rule a grid of voxel_size prices: without lengths to convert, the rule itself.

Its parameters and the state update_with_solutions() wrote reach the grid as they are. A rule whose parameters are lengths overrides this with a copy in voxel units.

Parameters:

voxel_size – Size of a voxel in length units

property tracks_solutions: bool#

Whether the rule reads the routed solutions: it defines its own update_with_solutions().

The map builds the shared zones, the pack axes and the bundle graph before a search only when some rule tracks the solutions.

update_with_solutions(is_shared_voxels: set[tuple[int, int, int, int]], bifurcation_nodes: list[tuple[float, float, float]], port_nodes: list[tuple[float, float, float]] | None = None, axis_voxels: dict[tuple[int, int, int, int], tuple[int, int, int, int]] | None = None, bundle_graph: Graph = None) → None[source]#
property weighs: bool#

Whether the rule writes weights: it defines its own compute_weights(), the base one writing zeros.

class routing.core.design_rules.DistanceAttributeToggler(attribute: str, min_distance: float = 0.0, max_distance: float = 1e+16, name: str = '')[source]#

Bases: DesignRule

A class representing a design rule that toggles a boolean attribute based on distance.

This class inherits from DesignRule, and sets a boolean attribute on voxels within a distance range. This is used by ConstraintRule to define regions where constraints apply.

Parameters:
  • attribute – The name of the boolean attribute to set on voxels.

  • min_distance – The minimum distance (in length units) where attribute is True. Defaults to 0.0.

  • max_distance – The maximum distance (in length units) where attribute is True. Defaults to _MAX_VALUE.

  • name – The name to identify this rule. Defaults to ‘’.

get_all_nodes_value(distances: ndarray) → ndarray[source]#

Get boolean array indicating which voxels have the attribute set to True.

Returns a boolean array where True indicates voxels with distances in the range [min_distance, max_distance). These voxels will have the attribute set to True, which can be used by ConstraintRule to enforce constraints.

Parameters:

distances – Distance array to check against min_distance and max_distance.

Returns:

Boolean array of same shape as distances, True where distances are in the range [min_distance, max_distance).

to_voxelized_rule(voxel_size: float) → DistanceAttributeToggler[source]#

Convert rule to voxelized version with distances in voxel units.

Creates a new DistanceAttributeToggler with min_distance and max_distance converted from world coordinates to voxel units by dividing by voxel_size and rounding up. This allows the rule to work directly with voxel-based distance computations.

Parameters:

voxel_size – Size of a voxel in length units. Used to convert distance thresholds from world coordinates to voxel indices.

Returns:

New DistanceAttributeToggler instance with voxelized distance parameters.

class routing.core.design_rules.ForbiddenZone(shape: Solid | VolumeModel, cut_ports: bool = False, name: str = '')[source]#

Bases: WeightedZone

A zone no route enters: every voxel the shape touches is closed.

Parameters:
  • shape – The 3D shape defining the zone.

  • cut_ports – Whether a port extension entering the zone is cut at its edge instead of raising. Defaults to False.

  • name – The name to identify this rule. Defaults to ‘’.

cut_port_extension(extension_coords: ndarray, port_name: str, voxel_size: float) → int | None[source]#

Return where the port extension enters the zone, judged cell by cell at voxel_size; None if it never does.

The port keeps the last cell the zone does not claim, traversable by construction. Raises when the port itself is in the zone, or when the extension enters it without cut_ports.

property forbids_zones: bool#

A forbidden zone forbids its zone.

class routing.core.design_rules.ForcedZone(shape: Solid | VolumeModel, name: str = '')[source]#

Bases: Zone

A mandatory passage: every route the planner generates passes through it.

The CadMap lays it on its grids as a GridForcedZone, whose doors the finder targets.

Parameters:
  • shape – The 3D shape defining the zone.

  • name – The name to identify this rule. Defaults to ‘’.

class routing.core.design_rules.GravityRule(min_value: float | None = None, max_value: float | None = None, distance_multiplier: float = 500.0, name: str = '')[source]#

Bases: BoundedAttributeRule

A class representing a constraint rule for gravity-based routing.

This class inherits from BoundedAttributeRule, and enforces slope constraints for gravity systems, ensuring pipes maintain appropriate downward slopes between source and target ports.

Parameters:
  • min_value – The optional minimum slope value (negative for gravity systems); None checks no minimum slope. Defaults to None.

  • max_value – The optional maximum slope value. Defaults to None.

  • distance_multiplier – The multiplier for computing constraint cost based on distance. Defaults to 500.0.

  • name – The name to identify this rule. Defaults to ‘’.

move_pricer(attribute_map: AttributeMap) → MovePricer | None[source]#

Build the gravity pricer of the compiled search: the rule’s heuristic.

set_port_nodes(source: tuple[float, float, float], target: tuple[float, float, float]) → None[source]#

Set source and target port positions for gravity constraint.

Sets the source and target coordinates and validates feasibility. The source should be above the target for gravity systems.

Parameters:
  • source – Source port coordinates as (x, y, z) array.

  • target – Target port coordinates as (x, y, z) array.

Returns:

None

Raises:

ValueError – If gravity constraint is not feasible (target above source).

source: tuple[float, float, float] | None#
target: tuple[float, float, float] | None#
to_voxelized_rule(voxel_size: float) → GravityRule[source]#

Convert rule to voxelized version with values in voxel units.

Creates a new BoundedAttributeRule with min_value and max_value converted from world coordinates to voxel units by dividing by voxel_size and rounding up. This allows the rule to work directly with voxel-based attribute computations.

Parameters:

voxel_size – Size of a voxel in length units. Used to convert attribute value thresholds from world coordinates to voxel units.

Returns:

New BoundedAttributeRule instance with voxelized value parameters.

class routing.core.design_rules.PoolingMode(name: str, description: str, default_coeff: float, valid_range: tuple[float, float])[source]#

Bases: DessiaObject

A class defining different modes of pooling with configurable coefficients.

This class inherits from DessiaObject, and defines modes with a description, default coefficient, and valid range for coefficient values. Coefficients determine how pipes can be grouped: - coeff == 0: no pooling allowed - coeff < 1: pooling allowed - coeff == 1: pipe can pass without modification - coeff > 1: permissive segregation

Parameters:
  • name – The name identifying this pooling mode.

  • description – The description of the pooling mode.

  • default_coeff – The default coefficient value for this mode.

  • valid_range – The valid range for coefficient values as (min, max) tuple.

property coeff: float#

Getter for coeff.

classmethod custom(name: str, description: str, default_coeff: float, valid_range: tuple[float, float]) → PoolingMode[source]#

Create a custom pooling mode instance with specified parameters.

Allows creation of custom pooling modes with user-defined name, description, default coefficient, and valid range. Useful for domain-specific pooling requirements.

Parameters:
  • name – Name identifier for the custom mode.

  • description – Human-readable description of the mode’s behavior.

  • default_coeff – Default coefficient value for this mode.

  • valid_range – Valid range for coefficient values as (min, max) tuple.

Returns:

PoolingMode instance with custom settings.

classmethod expert_mode() → PoolingMode[source]#

Create an expert mode instance.

Expert mode allows any coefficient value from 0 to infinity, giving full control to the user. The default is 0 (strict segregation), but users can set any value within the valid range.

Returns:

PoolingMode instance with expert mode settings (unrestricted range).

classmethod intermediate_pooling() → PoolingMode[source]#

Create an intermediate pooling mode instance.

Intermediate pooling mode has coeff=0.75, indicating weak preference for grouping pipes. This allows some pooling but is closer to neutral behavior than strict pooling.

Returns:

PoolingMode instance with intermediate pooling settings.

is_intermediate() → bool[source]#

Check if this mode represents intermediate pooling.

Returns True if 0.5 < coeff < 1, indicating moderate preference for grouping pipes. This is between strict pooling and neutral.

Returns:

True if 0.5 < coeff < 1 (intermediate pooling), False otherwise.

is_permissive() → bool[source]#

Check if this mode represents permissive segregation.

Returns True if coeff > 1, indicating that pipes can pass through each other’s zones but should not be actively grouped. This is a softer constraint than strict segregation.

Returns:

True if coeff > 1 (permissive segregation), False otherwise.

is_pooling() → bool[source]#

Check if this mode allows pooling.

Returns True if 0 < coeff < 1, indicating that pipes can be grouped together. Lower coefficient values indicate stronger preference for pooling.

Returns:

True if pooling is allowed (0 < coeff < 1), False otherwise.

is_segregating() → bool[source]#

Check if this mode represents segregation (no pooling or permissive segregation).

Returns True if coeff == 0.0 (strict segregation) or coeff > 1 (permissive segregation). In both cases, pipes should not be grouped together.

Returns:

True if mode is segregating (coeff == 0 or coeff > 1), False otherwise.

is_strict_pooling() → bool[source]#

Check if this mode represents strict pooling.

Returns True if coeff <= 0.5, indicating strong preference for grouping pipes together. This is the strongest pooling mode.

Returns:

True if coeff <= 0.5 (strict pooling), False otherwise.

is_strong_segregation() → bool[source]#

Check if this mode represents strong segregation.

Strong segregation means coeff == 0, indicating that no pooling is allowed between pipes. This is the strictest constraint.

Returns:

True if coeff == 0 (strict segregation), False otherwise.

classmethod moderate_pooling() → PoolingMode[source]#

Create a moderate pooling mode instance.

Moderate pooling mode has coeff=0.5, indicating moderate preference for grouping pipes. This is a balanced approach between strict pooling and neutral behavior.

Returns:

PoolingMode instance with moderate pooling settings.

classmethod permissive_segregation() → PoolingMode[source]#

Create a permissive segregation mode instance.

Permissive segregation mode has coeff=10, indicating that pipes can pass through each other’s zones but should not be actively grouped. This is a softer constraint than strict segregation, allowing more flexibility while still discouraging pooling.

Returns:

PoolingMode instance with permissive segregation settings.

reset() → None[source]#

Reset coefficient to its default value.

Restores the coefficient to the default_coeff value specified when the PoolingMode was created. Useful for reverting custom modifications.

Returns:

None

classmethod segregation() → PoolingMode[source]#

Create a segregation mode instance.

Segregation mode has coeff=0, meaning no pooling is allowed between pipes. This is the strictest constraint, requiring pipes to maintain minimum distances and never be grouped together.

Returns:

PoolingMode instance with strict segregation settings.

classmethod strong_pooling() → PoolingMode[source]#

Create a strong pooling mode instance.

Strong pooling mode has a very low coefficient (1e-14), indicating strong preference for grouping pipes together. This mode encourages maximum pooling while still allowing some flexibility.

Returns:

PoolingMode instance with strong pooling settings.

classmethod weak_pooling() → PoolingMode[source]#

Create a weak pooling mode instance.

Weak pooling mode has coeff=0.99, indicating very weak preference for grouping pipes. This is almost neutral, allowing minimal pooling but preferring separate paths.

Returns:

PoolingMode instance with weak pooling settings.

class routing.core.design_rules.PoolingRule(pooling_specs: list[PoolingSpecification] | None = None, min_distance: float = 0.0, cost: float = 500.0, allow_loops: bool = True, name: str = '')[source]#

Bases: CostRule

A class representing a rule that defines constraints for pooling types in the routing process.

This class inherits from CostRule: the specified pooling types, coefficients and distances shape the field the packs paint, and the rule prices the moves on the pooling zone they leave (POOLED_ATTRIBUTE).

Parameters:
  • pooling_specs – The optional list of PoolingSpecification objects defining pooling rules for specific pipe types. Defaults to None.

  • min_distance – The minimum distance used for the pooling rule in case no Pooling mode is set between two pipe types, in which case the pipes segregate by default. Defaults to 0.0.

  • cost – What a move closing a loop costs, while loops are priced (allow_loops=False). Defaults to 500.0.

  • allow_loops – Whether a route may rejoin a harness it has left. True, the default, leaves such moves free; False charges them cost, the loop tax (move_pricer()).

  • name – The name of the rule. Defaults to ‘’.

CORRIDOR_OVERHANG_IN_VOXELS = 1#

World voxels each pack’s corridor runs past the pack, at each of its two ends.

DEFAULT_POOLING_COEFF = 0.0001#
DEFAULT_STRONG_SEGREGATION_COEFF = 0#
POOLED_ATTRIBUTE = 'is_shared'#

Node attribute the packs’ pooling zone is published under (VoxelRegion.toggled_attributes()).

property all_types: list[str]#

Get all pipe types covered by pooling specifications.

Aggregates all pipe type identifiers from all PoolingSpecification objects in pooling_specs. May contain duplicates if a type appears in multiple specifications.

Returns:

List of all pipe type identifiers (strings) in all specifications.

build_pooling_matrices(types: list[str]) → None[source]#

Build pooling coefficient and distance matrices for all pipe type combinations.

Creates two matrices (coefficients and distances) that define how pipes of different types interact: - coefficients: Pooling coefficients for each type pair (affects routing costs) - distances: Minimum distances required between each type pair

The method: 1. Collects all types from specifications and input list 2. Creates square matrices for all type combinations 3. Fills matrices from PoolingSpecification objects 4. Sets default values (1.0 for coefficients, min_distance for distances) 5. Stores results in self.coefficients and self.distances dictionaries

This must be called before using pooling features in routing.

Parameters:

types – List of pipe type identifiers to include in matrices. Types from pooling_specs are automatically added. “None” type is added if not present.

Returns:

None

coefficients: dict[str, dict[str, float]]#
distances: dict[str, dict[str, float]]#
get_min_distance(types: tuple[str, str]) → float[source]#

Get the minimum distance required between two pipe types.

Returns the minimum distance from the PoolingSpecification if one exists for the given types, otherwise returns the default min_distance from the PoolingRule.

Parameters:

types – Tuple of two pipe type identifiers.

Returns:

Minimum distance value in length units.

get_pooling_coefficient(types: tuple[str, str]) → float[source]#

Get pooling coefficient value for given pipe types.

Returns the coefficient from the PoolingSpecification if one exists for the given types. If no specification matches: - Same types (type1, type1): Returns DEFAULT_POOLING_COEFF (0.0001) - Different types: Returns DEFAULT_STRONG_SEGREGATION_COEFF (0)

Parameters:

types – Tuple of two pipe type identifiers.

Returns:

Pooling coefficient value (0 = strict segregation, < 1 = pooling allowed).

is_parallel(types: tuple[str, str]) → bool[source]#

Check if pooling for given types should use parallel grouping.

Returns True if the types can be pooled AND the PoolingSpecification has parallel=True. Parallel grouping uses agglomerative clustering with support surface constraints, while non-parallel uses standard proximity-based grouping.

Parameters:

types – Tuple of two pipe type identifiers to check.

Returns:

True if parallel grouping should be used, False otherwise.

is_pooling(types: tuple[str, str]) → bool[source]#

Verify if given PipeTypes types corresponds to a pooling rule.

Parameters:

types – PipeType types for pooling rule.

Returns:

bool.

is_segregation(types: tuple[str, str]) → bool[source]#

Check if the given pipe types must be segregated.

Returns True if any PoolingSpecification enforces segregation for these types. If a specification allows pooling, returns False. If no specification matches, returns the inverse of is_pooling().

Parameters:

types – Tuple of two pipe type identifiers to check.

Returns:

True if segregation is required for these types, False otherwise.

is_strong_segregation(types: tuple[str, str]) → bool[source]#

Check if the given pipe types have strong segregation enforced.

Returns True if any PoolingSpecification enforces strong segregation (coeff == 0) for these types. If a specification allows pooling for these types, returns False. If no specification matches and types are different, returns True (default segregation).

Parameters:

types – Tuple of two pipe type identifiers to check.

Returns:

True if strong segregation is enforced for these types, False otherwise.

labels#

The labels the corridors carry (CorridorLabels), read off the packs at each stamping (CadMap.update_weights_with_history); None until a first stamping.

move_pricer(attribute_map: AttributeMap) → MovePricer | None[source]#

Build the pooling pricer of the compiled search once a stamping has labelled the corridors: the loop tax; None while loops are allowed.

tray_specification(type_: str) → PoolingSpecification | None[source]#

Give the parallel specification type_ routes under, None when it pools generically.

type_in_parallel(type_: str) → bool[source]#

Whether type_ is pooled as trays: it has a parallel specification (tray_specification()).

update_history_zones(types: tuple[str, str], pooling_range: ndarray, pooling_zone: ndarray, forbidden_zone: ndarray) → None[source]#

Update history zones for tracking pipe placement.

Placeholder method for updating history zones that track where pipes have been placed. This information is used to influence future routing decisions based on existing pipe locations.

Currently not implemented (pass). Future implementation would update pooling_zone and forbidden_zone arrays based on pipe placement history.

Parameters:
  • types – Tuple of two pipe type identifiers.

  • pooling_range – Boolean array indicating voxels in pooling influence zone.

  • pooling_zone – Array to update with pooling zone information.

  • forbidden_zone – Array to update with forbidden zone information.

Returns:

None

update_weights(global_weights: ndarray, types: tuple[str, str], pooling_range: ndarray, no_room_voxels: list[tuple[int, int, int]]) → ndarray[source]#

Update weights with pooling rules’ coefficient.

Applies pooling coefficients to voxels in the pooling range and sets weights to 0.0 for voxels that are not in the room (non-traversable). The pooling coefficient modifies routing costs to encourage or discourage grouping pipes based on the pooling mode.

Parameters:
  • global_weights – 3D weight array to update (same shape as room_voxels.matrix).

  • types – Tuple of two pipe type identifiers to get pooling coefficient for.

  • pooling_range – Boolean array indicating voxels in the pooling influence zone.

  • no_room_voxels – List of voxel indices (i, j, k) that are not traversable.

Returns:

Updated weight array with pooling coefficients applied.

class routing.core.design_rules.PoolingSpecification(types: list[str], pooling_mode: PoolingMode, min_distance: float, parallel: bool = False, max_layers: int | None = 2, aspect: float | None = None, name: str = '')[source]#

Bases: DessiaObject

A class defining pooling rules for specific pipe types.

This class inherits from DessiaObject, and defines pooling rules that determine how pipes of specific types can be grouped together during routing.

Parameters:
  • types – The list of pipe types this pooling rule applies to.

  • pooling_mode – The mode determining how pipes can be grouped.

  • min_distance – The minimum distance for pooling mode to be applied.

  • parallel – Whether the types are pooled as trays, in planes of fibres stacked over the support they rest on (ParallelPack), instead of generic packs. Defaults to False.

  • max_layers – Planes a tray may stack, None for no cap; unread by generic packs. Defaults to 2.

  • aspect – Width over height of a tray’s section above which its first plane stops widening, None to leave it free; unread by generic packs. Defaults to None.

  • name – The name to identify this pooling specification. Defaults to ‘’.

contains_types(types: tuple[str, str]) → bool[source]#

Check if given types are covered by this pooling specification.

Returns True if both pipe types in the tuple are present in this specification’s types list. For same-type pairs (type1, type1), requires the type to appear twice in the types list.

Parameters:

types – Tuple of two pipe type identifiers to check.

Returns:

True if both types are covered by this specification, False otherwise.

is_pooled_with(type_: str) → bool[source]#

Check if a pipe type can be pooled with types in this specification.

Returns True if the pooling mode allows pooling AND the given type is in this specification’s types list. This indicates that pipes of the given type can be grouped with pipes of types in this specification.

Parameters:

type – Pipe type identifier to check.

Returns:

True if the type can be pooled with types in this specification, False otherwise.

is_pooling(types: tuple[str, str]) → bool[source]#

Check if this specification allows pooling for the given types.

Returns True if the types are covered by this specification AND the pooling mode allows pooling (0 < coeff < 1).

Parameters:

types – Tuple of two pipe type identifiers to check.

Returns:

True if pooling is allowed for these types, False otherwise.

is_segregated_with(type_: str) → bool[source]#

Check if a pipe type must be segregated from types in this specification.

Returns True if the pooling mode enforces segregation AND the given type is in this specification’s types list. This indicates that pipes of the given type must maintain separation from pipes of types in this specification.

Parameters:

type – Pipe type identifier to check.

Returns:

True if the type must be segregated from types in this specification, False otherwise.

is_segregating(types: tuple[str, str]) → bool[source]#

Check if this specification enforces segregation for the given types.

Returns True if the types are covered by this specification AND the pooling mode is segregating (coeff == 0 or coeff > 1). This includes both strict segregation and permissive segregation.

Parameters:

types – Tuple of two pipe type identifiers to check.

Returns:

True if segregation is enforced for these types, False otherwise.

is_strong_segregation(types: tuple[str, str]) → bool[source]#

Check if this specification enforces strong segregation for the given types.

Returns True if the types are covered by this specification AND the pooling mode is strong segregation (coeff == 0). This means no pooling/mutualization is allowed between these pipe types.

Parameters:

types – Tuple of two pipe type identifiers to check.

Returns:

True if strong segregation is enforced for these types, False otherwise.

class routing.core.design_rules.RuleEnforcement(*, name: str = '', hard: bool = True, weight: float = 1.0, power: int = 1, tolerance: float = 0.0, stages: list[str] = <factory>)[source]#

Bases: Model

How a bundle-active design rule enters the harness optimization problem.

hard=True poses the rule as a hard constraint offset by tolerance; hard=False poses it as a soft cost shaped by weight and power. stages lists the optimization stages whose problems the rule joins — design rules default to the shaping stage only, being antagonistic to consolidation. Carried by the rule itself (DesignRule.enforcement) so a rule is declared once with both its projections: routing-grid parameters and optimization form.

hard: bool#
power: int#
stages: list[str]#
tolerance: float#
weight: float#
class routing.core.design_rules.ShapedWeightingRule(shape: Solid | VolumeModel, coefficient: float = 0.5, zone_type: str = 'attractive', cut_ports: bool = False, name: str = '')[source]#

Bases: object

Deprecated: builds the Zone the legacy zone_type names (attractive and repulsive: a WeightedZone).

ZONE_TYPES = ('attractive', 'repulsive', 'forbidden', 'forced')#
classmethod attractive_zone(shape: Solid | VolumeModel, strength: float = 0.1, name: str = '') → WeightedZone[source]#

Build WeightedZone(shape, strength); deprecated.

classmethod forbidden_zone(shape: Solid | VolumeModel, cut_ports: bool = False, name: str = '') → ForbiddenZone[source]#

Build ForbiddenZone(shape, cut_ports); deprecated.

classmethod forced_zone(shape: Solid | VolumeModel, name: str = '') → ForcedZone[source]#

Build ForcedZone(shape); deprecated.

classmethod repulsive_zone(shape: Solid | VolumeModel, strength: float = 10.0, name: str = '') → WeightedZone[source]#

Build WeightedZone(shape, strength); deprecated.

class routing.core.design_rules.StraightLengthRule(min_value: float = -1e+16, distance_multiplier: float = 500.0, name: str = '')[source]#

Bases: BoundedAttributeRule

A class representing a constraint rule for minimum straight length before turns.

This class inherits from BoundedAttributeRule, and enforces a minimum straight length constraint, ensuring pipes maintain a straight segment before making turns.

Parameters:
  • min_value – The minimum straight length value before a turn is allowed. Defaults to _MIN_VALUE.

  • distance_multiplier – The multiplier for computing constraint cost based on distance. Defaults to 500.0.

  • name – The name to identify this rule. Defaults to ‘’.

move_pricer(attribute_map: AttributeMap) → MovePricer | None[source]#

Build the straight-length pricer of the compiled search: the rule’s heuristic.

to_voxelized_rule(voxel_size: float) → StraightLengthRule[source]#

Convert rule to voxelized version with length in voxel units.

Creates a new StraightLengthRule with min_value converted from world coordinates to voxel units by dividing by voxel_size and rounding up. This allows the rule to work directly with voxel-based length computations.

Parameters:

voxel_size – Size of a voxel in length units. Used to convert minimum straight length from world coordinates to voxel units.

Returns:

New StraightLengthRule instance with voxelized min_value.

class routing.core.design_rules.TurnPenalizer(cost: float = 50.0, name: str = '')[source]#

Bases: CostRule

A class representing a design rule that penalizes turns during routing.

This class inherits from CostRule, and applies a cost penalty when the path changes direction, encouraging straighter paths and reducing the number of bends.

Parameters:
  • cost – The cost penalty to apply when the path changes direction. Higher values create stronger preference for straight paths. Defaults to 50.0.

  • name – The name to identify this turn penalizer. Defaults to ‘’.

move_pricer(attribute_map: AttributeMap) → MovePricer | None[source]#

Build the turn pricer of the compiled search: the rule’s cost.

class routing.core.design_rules.WeightedZone(shape: Solid | VolumeModel, coefficient: float = 0.5, name: str = '')[source]#

Bases: Zone

A zone multiplying the routing weights of its voxels by coefficient: below 1 attracts, above 1 repels.

Parameters:
  • shape – The 3D shape defining the zone.

  • coefficient – The weight multiplier inside the zone. Defaults to 0.5.

  • name – The name to identify this rule. Defaults to ‘’.

apply_weights(weights: ndarray, room: RoutingVoxelization) → ndarray[source]#

Multiply in place the weights of the room voxels the zone covers, and return weights.

class routing.core.design_rules.WeightingRule(function: str = 'linear', min_length: float = 0.0, max_length: float | None = None, min_value: float = 0.0, max_value: float | None = None, name: str = '')[source]#

Bases: DesignRule

A class representing a design rule that applies weights based on distance to volumes.

This class inherits from DesignRule, and defines how routing costs vary with distance to a volume. Supports linear, square root, and constant functions. Weights are applied during pathfinding to prefer or avoid certain distances.

Parameters:
  • function – The weight function type. Options: ‘linear’, ‘lin’, ‘affine’ for linear interpolation; ‘sqrt’, ‘square root’, ‘square_root’ for square root interpolation; ‘constant’, ‘const’ for constant weight. Defaults to ‘linear’.

  • min_length – The minimum distance (in length units) where rule applies. Defaults to 0.0.

  • max_length – The maximum distance (in length units) where rule applies. If None, applies to all distances >= min_length. Defaults to None.

  • min_value – The minimum weight value applied at min_length distance. Defaults to 0.0.

  • max_value – The maximum weight value applied at max_length distance. If None, computed automatically from function. Defaults to None.

  • name – The name to identify this weighting rule. Defaults to ‘’.

build(distances: ndarray) → None[source]#

Calibrate coeff on this distance field; call before get_function().

A max_length left None resolves to the field’s maximum distance — locally: the rule definition (min/max_length, min/max_value) is never mutated, so a shared rule recalibrates on every field it weights instead of freezing on the first one.

Parameters:

distances – Distance array the rule is about to weight.

Returns:

None

coeff: float | None#
compute_weights(distances: ndarray) → ndarray[source]#

Compute weights from distance array using the weighting function.

Applies the weighting function to distances within the valid range [min_length, max_length). Distances outside this range receive zero weight.

The method: 1. Initializes weights array with zeros 2. Identifies voxels in the valid distance range 3. Builds the weighting function (calibrates coeff on this field) 4. Applies the function to distances in range

Parameters:

distances – Distance array from which to compute weights.

Returns:

Weight array of same shape as distances, with non-zero values only for distances in the valid range [min_length, max_length).

get_boolean_range(distances: ndarray) → ndarray[source]#

Get boolean mask for distances within the valid range.

Returns a boolean array where True indicates distances that are within the valid range [min_length, max_length). Voxels outside this range will not have weights applied by this rule.

Parameters:

distances – Distance array to check.

Returns:

Boolean array of same shape as distances, True where distances are in the valid range [min_length, max_length).

get_function() → Callable[source]#

Get the weighting function as a callable.

Returns a lambda function that computes weight from distance based on the function type: - Constant: Returns min_value for all distances - Linear: coeff * (x - min_length) + min_value - Square root: coeff * sqrt(x - min_length) + min_value

The function must be called after build() to ensure coeff is properly calibrated.

Returns:

Callable function that takes distance and returns weight.

to_voxelized_rule(voxel_size: float) → WeightingRule[source]#

Convert rule to voxelized version with distances in voxel units.

Creates a new WeightingRule with min_length and max_length converted from world coordinates to voxel units by dividing by voxel_size and rounding up. This allows the rule to work directly with voxel-based distance computations.

Parameters:

voxel_size – Size of a voxel in length units. Used to convert distance thresholds from world coordinates to voxel indices.

Returns:

New WeightingRule instance with voxelized distance parameters.

class routing.core.design_rules.Zone(shape: Solid | VolumeModel, name: str = '')[source]#

Bases: DesignRule

A design rule over the voxels a 3D shape covers: those whose centre lies in the shape.

shape is any volmdlr Solid, Shell or VolumeModel with a triangulation. The zone is its filled voxelization at the grid step, kept to the voxels the shape contains (_kept()), laid on each room grid by covered_voxels().

Parameters:
  • shape – The 3D shape defining the zone.

  • name – The name to identify this rule. Defaults to ‘’.

covered_voxels(room: RoutingVoxelization) → ndarray[source]#

Return the indices of the occupied room voxels the zone covers, cached per room grid.

zone_voxels(voxel_size: float) → RoutingVoxelization[source]#

Return the voxels of the zone at voxel_size, cached per step.