Source code for dphox.route

from typing import Union, Optional

import numpy as np

from .parametric import circular_bend, link, spiral, turn
from .path import Curve
from .pattern import Pattern
from .port import Port
from .utils import DEFAULT_RESOLUTION


def _turn_connect_angle_solve(start: Port, end: Port, start_r: float, end_r: float):
    """Solve the turn connect problem with the least possible bending (try 16 options) using nazca strategy."""
    # Curve up or down at start or end ports for a total of four circle orientations
    circle_orientations = np.array([(1, 1), (1, -1), (-1, 1), (-1, -1)])

    # Get the list of possible angles; we eventually want to find the shortest possible path between the ports.
    possible_angles = []
    possible_lengths = []

    # edge case for circular bend connection
    # if start_r == end_r and np.linalg.norm(start.xy - end.xy) == 2 * start_r * np.cos(start.a - end.a):
    #     return (-90, -90), 0

    for co in circle_orientations:
        # define the center of the circles
        start_c = start.xy - co[0] * start.normal(start_r)
        end_c = end.xy - co[1] * end.normal(end_r)
        end_a = np.radians(end.a)
        start_a = np.radians(start.a - 180)
        diff_c = end_c - start_c
        s = np.linalg.norm(diff_c)
        rr = co @ np.array((start_r, end_r))
        # angle through circle centers (for aligning the circles along axis)
        circle_a = np.arctan2(*diff_c[::-1])
        if np.abs(rr) <= s:
            # angle due to the difference in radii when circles are horizontally aligned)
            corr_a = np.pi / 2 if rr == s == 0 else np.arcsin(rr / s)
            angles = (circle_a - corr_a - np.array((start_a, end_a))) * np.array((-1, 1))
            angles = angles % (2 * np.pi) - (1 + np.array((-1, 1)) * co) * np.pi
            possible_angles.append(-angles)
            possible_lengths.append(np.abs(s * np.cos(corr_a)))
    if not possible_angles:
        raise ValueError(
            "No solution found for the turn connector. Make sure the radii of the turns are sufficiently"
            f"small to connect {start} to {end}. One possible solution: reduce the turn radius.")
    idx = np.argmin(np.sum(np.abs(np.array(possible_angles)), axis=1))
    return np.degrees(possible_angles[idx]), possible_lengths[idx]


[docs]def turn_connect(start: Port, end: Port, radius: float, radius_end: Optional[float] = None, euler: float = 0, resolution: int = DEFAULT_RESOLUTION, include_width: bool = True) -> Union[Pattern, Curve]: """Turn connect. Args: start: Start port end: End port radius: Start turn effective radius radius_end: End turn effective radius (use :code:`radius` if :code:`None`) euler: Euler path contribution (see :code:`turn` method). resolution: Number of evaluations for the turns. include_width: Whether to include the width (cubic taper) of the turn connect Returns: A path connecting the start and end port using a turn-straight-turn approach """ start_r = radius end_r = start_r if radius_end is None else radius_end angles, length = _turn_connect_angle_solve(start.copy.flip(), end.copy.flip(), start_r, end_r) curve = link(turn(start_r, angles[0], euler, resolution) if angles[0] % 360 != 0 else None, length, turn(end_r, angles[1], euler, resolution) if angles[1] % 360 != 0 else None).to(start) return curve.path(start.w) if include_width else curve
[docs]def manhattan_route(start: Port, lengths: np.ndarray, include_width: bool = True): """Manhattan route (intended for metal routing). Starting with horizontal and switching back and forth with vertical, make alternating turns. A positive length indicates moving in a +x or +y direction, whereas a negative length indicates moving in a -x or -y direction. Args: start: Start port for the route. lengths: List of dx and dy segments (alternating left and right turns). include_width: Include width returns a path instead of a curve. Returns: The manhattan route path. """ lengths = lengths if isinstance(lengths, np.ndarray) else np.array(lengths) xs = np.hstack((0, np.tile(np.cumsum(lengths[::2]), 2).reshape((2, -1)).T.flatten()))[:lengths.size + 1] ys = np.hstack(((0, 0), np.tile(np.cumsum(lengths[1::2]), 2).reshape((2, -1)).T.flatten()))[:lengths.size + 1] points = np.vstack((xs, ys)).T path = Curve(points) if include_width: path = Pattern(path.shapely.buffer(start.w, join_style=2, cap_style=2)) return path.to(start)
[docs]def spiral_delay(n_turns: int, min_radius: float, separation: float, resolution: int = 1000, turn_resolution: int = DEFAULT_RESOLUTION, include_straightening_bend: bool = True): """Spiral delay (large waveguide delay in minimal area). Args: n_turns: Number of turns in the spiral min_radius: Minimum radius of the spiral (affects the inner part of the design) separation: Separation of waveguides in the spiral. resolution: Number of evaluations for the spiral. turn_resolution: Number of evaluations for the turns. include_straightening_bend: Include input and output bends to straighten spiral delay along a line Returns: The spiral delay waveguide. """ spiral_ = spiral(n_turns, min_radius, theta_offset=2 * np.pi, separation_scale=separation, resolution=resolution) bend = circular_bend(min_radius, 180, resolution=turn_resolution) curve = link(spiral_.copy.reverse(), bend.copy.reflect(), bend, spiral_) if include_straightening_bend: start, end = curve.port['a0'], curve.port['b0'] start_section = turn_connect(start, Port(start.x - 3 * min_radius, start.y, 0), min_radius, resolution=turn_resolution, include_width=False) end_section = turn_connect(end, Port(end.x + 3 * min_radius, end.y, 180), min_radius, resolution=turn_resolution, include_width=False) curve = link(start_section.reverse(), curve.reflect(), end_section) curve.halign(0) return curve
[docs]def loopify(curve: Curve, radius: float, euler: float = 0, resolution: int = DEFAULT_RESOLUTION): """Automatically create a loop by connecting the ends of a curve Note: This only works in some cases, as there are no self-intersection checks. Args: curve: Curve to loopify. radius: Radius of the turns. euler: Euler parameter for the turn. resolution: Number of evaluations for the curves in the loop. Returns: The loopified curve. """ return link(curve, turn_connect(curve.port['b0'], curve.port['a0'], radius, euler=euler, resolution=resolution))