ConnectManhattan

class ipkiss3.all.ConnectManhattan

A connector that uses i3.RouteManhattan to connect two ports and returns an instance of i3.RoundedWaveguide (optical ports) or i3.ElectricalWire (electrical ports).

This connector is supposed to be used together with i3.place_and_route.

Note

Electrical ports need to have an angle (not None) for the routing algorithm to work.

Parameters:
cover_layers: List with type restriction, allowed types: <class ‘ipkiss.primitives.layer.Layer’>, optional

Layers that can be used to generate additional coverage of the trace (e.g. manhattan corners). The ‘cover_bends’ property has to be set to True for this to take effect.

cover_bends: ( bool, bool_ or int ), optional

Adds rectangular blocks in the bends to avoid as much as possible non-manhattan angles. Needs to be configured together with the ‘cover_layers’ property.

fixed_bend90: ( PCell ), optional, *None allowed*

An optional override for the 90 degree bends used in the routing. The bend should have exactly two optical ports and the angle of the bend should be equal to 90 degrees. The trace template of the bend should match trace_template. Bends are assumed to be bi-directional in the routing. If the route requires non 90 degree angles, they will generated using bend_radius, angle_step and rounding_algorithm. Likewise when there isn’t enough space to place the fixed bend, it will be replaced by an autogenerated one.

min_spacing: float and Real, number and number >= 0, optional

minimal spacing between parallel sections of the route

end_straight: float and Real, number and number >= 0, optional

The length of the straight end section of the route

start_straight: float and Real, number and number >= 0, optional

The length of the straight start section of the route

min_straight: float and Real, number and number >= 0, optional

The minimum length of any straight sections in the route

angle_step: float and number > 0, optional

angle step for rounding

rounding_algorithm: optional

Rounding algorithm (ShapeRound, ShapeRoundAdiabaticSpline, …). Takes a shape as input and returns a new (rounded) shape.

bend_radius: float and number > 0, optional

bend radius for the auto-generated bends

control_points: list, optional

Control the routing by passing either a list of points i3.CP through which the route has to pass,or a list of i3.H / i3.V instances.

end_taper_length: ( float and Real, number and number >= 0 ), optional, *None allowed*

Length of the taper for the transition to be generated at the end of the connection if the trace templates mismatch. If it is not specified, a default transition from the database will be used.

start_taper_length: ( float and Real, number and number >= 0 ), optional, *None allowed*

Length of the taper for the transition to be generated at the start of the connection if the trace templates mismatch. If it is not specified, a default transition from the database will be used.

trace_template: ( PCell and _TraceTemplate ), optional, *None allowed*

Trace template to use for the waveguide between the two ports, when the ports have a different template, transitions will be added. When this property is left unspecified/None the trace_template of the start_port will be used

Examples

"""
This example illustrations how you can use the control_points property to guide
your waveguide around an obstacle. 'obstacle@N' means: north of the instance called 'obstacle'.
"""
import si_fab.all as pdk  # noqa: F401
from picazzo3.fibcoup.curved import FiberCouplerCurvedGrating
from ipkiss3 import all as i3
from pylab import plt

# Defining the pcells
gr = FiberCouplerCurvedGrating()
obstacle = pdk.BONDPAD_5050()

control_points = [
    i3.V(-20, relative_to="obstacle@W"),
    i3.H(20, relative_to="obstacle@N"),
    i3.V(20, relative_to="obstacle@E"),
]
circuit = i3.Circuit(
    insts={"obstacle": obstacle, "grb1": gr, "grb2": gr},
    specs=[
        i3.Place("grb1", position=(0, 0)),
        i3.Place("obstacle", position=(100, 0)),
        i3.Place("grb2", position=(200, 0), angle=180),
        i3.ConnectManhattan(
            "grb1:out",
            "grb2:out",
            control_points=control_points,
        ),
    ],
)
circuit_layout = circuit.Layout()

fig = plt.figure()
circuit_layout.visualize(figure=fig, show=False)

# plot annotations
ax = fig.gca()
ax.plot([25, 175], [45, 45], color="black", linestyle="--", linewidth=1.0)
ax.plot([25, 175], [25, 25], color="black", linestyle="--", linewidth=1.0)

ax.annotate(
    "", xy=(80, 25), xycoords="data", xytext=(80, 45), textcoords="data", arrowprops={"arrowstyle": "<->"}
)
ax.annotate("20", xy=(91, 30), xycoords="data", xytext=(-4, 5), textcoords="offset points")
ax.annotate(
    'i3.H(20, relative_to="obstacle@N")',
    xy=(70, 48),
    xycoords="data",
    xytext=(-4, 5),
    textcoords="offset points",
)
ax.set_ylim([-40, 70])
plt.show()
../../../../_images/ipkiss3-all-ConnectManhattan-1.png
"""
If more explicit control point over the routes is needed, you can use i3.CP for an explicit control point,
as shown below.
"""
import si_fab.all as pdk  # noqa: F401
from picazzo3.fibcoup.curved import FiberCouplerCurvedGrating
from ipkiss3 import all as i3
import matplotlib.pyplot as plt

# Defining the pcells
gr = FiberCouplerCurvedGrating()
obstacle = pdk.BONDPAD_5050()

control_points = [i3.CP((-40, 20), i3.NORTH), i3.CP((0, 30), i3.EAST), i3.CP((50, 20), i3.SOUTH)]
circuit = i3.Circuit(
    insts={"obstacle": obstacle, "grb1": gr, "grb2": gr},
    specs=[
        i3.Place("obstacle", position=(0, 0)),
        i3.Place("grb1", position=(-100, 0)),
        i3.Place("grb2", position=(+100, 0), angle=180),
        i3.ConnectManhattan(
            "grb1:out",
            "grb2:out",
            control_points=control_points,
        ),
    ],
)
circuit_layout = circuit.Layout()
circuit_layout.visualize(show=False)
plt.scatter([cp[0] for cp in control_points], [cp[1] for cp in control_points], c="C1", s=80, marker="x")
plt.arrow(-40, 20, 0, 2, width=1.0)
plt.arrow(0, 30, 2, 0, width=1.0)
plt.arrow(50, 20, 0, -2, width=1.0)
plt.show()
../../../../_images/ipkiss3-all-ConnectManhattan-2.png
import si_fab.all as pdk  # noqa: F401
import ipkiss3.all as i3
from picazzo3.traces.rib_wg.trace import RibWaveguideTemplate

# Instantiate waveguide
tt1 = RibWaveguideTemplate()
tt1.Layout(core_width=0.5)
wg = i3.Waveguide(trace_template=tt1)

# Define circuit with horizontal ports
circuit1 = i3.Circuit(
    insts={"wg1": wg, "wg2": wg},
    specs=[
        i3.Place("wg1", position=(0, 0), angle=0),
        i3.Place("wg2", position=(100, 100), angle=0),
        i3.ConnectManhattan("wg1:out", "wg2:in", bend_radius=10),
    ],
)

# Define circuit with non-manhattan angles
circuit2 = i3.Circuit(
    insts={"wg1": wg, "wg2": wg},
    specs=[
        i3.Place("wg1", position=(0, 0), angle=-20),
        i3.Place("wg2", position=(100, 100), angle=100),
        i3.ConnectManhattan("wg1:out", "wg2:in", bend_radius=10),
    ],
)

# Visualize circuits
circuit1_layout = circuit1.Layout()
circuit1_layout.visualize()
circuit2_layout = circuit2.Layout()
circuit2_layout.visualize()
../../../../_images/ipkiss3-all-ConnectManhattan-3_00.png
../../../../_images/ipkiss3-all-ConnectManhattan-3_01.png
import ipkiss3.all as i3

# Define waveguide
wg = i3.Waveguide()

# Define rounding algorithms
ra1 = i3.ShapeRound
ra2 = i3.SplineRoundingAlgorithm(adiabatic_angles=(15, 15))

# Create circuits
circuit1 = i3.Circuit(
    insts={"wg1": wg, "wg2": wg},
    specs=[
        i3.Place("wg1", (0, 0)),
        i3.Place("wg2", (-40, 40)),
        i3.ConnectManhattan("wg1:out", "wg2:in", bend_radius=7, rounding_algorithm=ra1),
    ],
)
circuit2 = i3.Circuit(
    insts={"wg1": wg, "wg2": wg},
    specs=[
        i3.Place("wg1", (0, 0)),
        i3.Place("wg2", (-40, 40)),
        i3.ConnectManhattan("wg1:out", "wg2:in", bend_radius=7, rounding_algorithm=ra2),
    ],
)

# Visualize circuits
circuit1_layout = circuit1.Layout()
circuit1_layout.visualize()
circuit2_layout = circuit2.Layout()
circuit2_layout.visualize()
../../../../_images/ipkiss3-all-ConnectManhattan-4_00.png
../../../../_images/ipkiss3-all-ConnectManhattan-4_01.png
import si_fab.all as pdk  # noqa: F401
import ipkiss3.all as i3
from picazzo3.traces.rib_wg.trace import RibWaveguideTemplate

# Instantiate waveguide
tt1 = RibWaveguideTemplate()
tt1.Layout(core_width=0.5)
wg = i3.Waveguide(trace_template=tt1)

# Circuit with a connection that will go through point (50, 50)
circuit = i3.Circuit(
    insts={"wg1": wg, "wg2": wg},
    specs=[
        i3.Place("wg1", (0, 0)),
        i3.Place("wg2", (100, 100)),
        i3.ConnectManhattan("wg1:out", "wg2:in", control_points=[(50, 50)], bend_radius=10),
    ],
)
circuit_layout = circuit.Layout()
circuit_layout.visualize()

# Another option for control_points is to describe the horizontal and vertical parts of the route.
# You can do this in the following way:
# [V(40)] describes that the route should be vertical at x = 40.
circuit = i3.Circuit(
    insts={"wg1": wg, "wg2": wg},
    specs=[
        i3.Place("wg1", (0, 0)),
        i3.Place("wg2", (100, 100)),
        i3.ConnectManhattan("wg1:out", "wg2:in", control_points=[i3.V(40)], bend_radius=10),
    ],
)
circuit_layout = circuit.Layout()
circuit_layout.visualize()

# [V(100), H(100)] determines that the route should be vertical at x = 100 and then horizontal at y = 100.
circuit = i3.Circuit(
    insts={"wg1": wg, "wg2": wg},
    specs=[
        i3.Place("wg1", (0, 0)),
        i3.Place("wg2", (50, 70)),
        i3.ConnectManhattan("wg1:out", "wg2:in", control_points=[i3.V(100), i3.H(100)], bend_radius=10),
    ],
)
circuit_layout = circuit.Layout()
circuit_layout.visualize()

# Note 1: These two types of control points don't mix: [V(20), (50, 50), H(10)] is not allowed.
# Note 2: When using more than one horizontal and/or vertical control points an H must always follow a V,
# and vice versa. You can't pass two of the same type right after each other. For instance:
# [V(20), V(30), H(10)] is not allowed, [V(20), H(20), V(30), H(10)] is allowed.
../../../../_images/ipkiss3-all-ConnectManhattan-5_00.png
../../../../_images/ipkiss3-all-ConnectManhattan-5_01.png
../../../../_images/ipkiss3-all-ConnectManhattan-5_02.png
import si_fab.all as pdk  # noqa: F401
import ipkiss3.all as i3
from picazzo3.traces.rib_wg.trace import RibWaveguideTemplate
from ipkiss3.all import START, END

# Instantiate waveguide
tt1 = RibWaveguideTemplate()
tt1.Layout(core_width=0.5)
wg = i3.Waveguide(trace_template=tt1)

control_points = [
    START + (30, 10),  # control point at (30, 10) relative to the start port
    (50, 50),  # control point at (50, 50) relative to (0, 0)
    END - (30, 10),
]  # control point at (-30, -10) relative to the end port
circuit = i3.Circuit(
    insts={"wg1": wg, "wg2": wg},
    specs=[
        i3.Place("wg1", (0, 0)),
        i3.Place("wg2", (100, 100)),
        i3.ConnectManhattan("wg1:out", "wg2:in", control_points=control_points, bend_radius=5),
    ],
)
circuit_layout = circuit.Layout()
circuit_layout.visualize()

# Similarly it is possible to define relative i3.H and i3.V control points
circuit = i3.Circuit(
    insts={"wg1": wg, "wg2": wg},
    specs=[
        i3.Place("wg1", (0, 0)),
        i3.Place("wg2", (100, 100)),
        i3.ConnectManhattan("wg1:out", "wg2:in", control_points=[i3.H(END - 40)], bend_radius=5),
    ],
)
circuit_layout = circuit.Layout()
circuit_layout.visualize()
../../../../_images/ipkiss3-all-ConnectManhattan-6_00.png
../../../../_images/ipkiss3-all-ConnectManhattan-6_01.png

When required ConnectManhattan will introduce transitions to create a valid connection.:

import si_fab.all as pdk  # noqa: F401
import ipkiss3.all as i3
from picazzo3.traces.rib_wg.trace import RibWaveguideTemplate
from picazzo3.traces.wire_wg.trace import WireWaveguideTemplate

# Instantiate trace templates to use for the ports
tt_start = RibWaveguideTemplate()
tt_start.Layout(core_width=0.5)

tt_end = WireWaveguideTemplate()
tt_end.Layout(core_width=0.5)

# The connecting waveguide will use the trace_template specified
# on the start_port and introduce a transition at the end
# to connect properly with the end_port.
circuit = i3.Circuit(
    insts={
        "wg1": i3.Waveguide(trace_template=tt_start),
        "wg2": i3.Waveguide(trace_template=tt_end),
    },
    specs=[
        i3.Place("wg1", (0, 0)),
        i3.Place("wg2", (40, 40)),
        i3.ConnectManhattan("wg1:out", "wg2:in", bend_radius=10),
    ],
)
circuit_layout = circuit.Layout()
circuit_layout.visualize()
../../../../_images/ipkiss3-all-ConnectManhattan-7.png

By default ConnectManhattan will use the trace_template from the start_port for the connecting waveguide, but this can be changed using the trace_template property:

import si_fab.all as pdk  # noqa: F401
import ipkiss3.all as i3
from picazzo3.traces.rib_wg.trace import RibWaveguideTemplate
from picazzo3.traces.wire_wg.trace import WireWaveguideTemplate

# Instantiate trace templates to use for the ports
tt_start = RibWaveguideTemplate()
tt_start.Layout(core_width=0.5)

tt_end = WireWaveguideTemplate()
tt_end.Layout(core_width=0.5)

# use the trace_template of the end_port
# this will add the transition at the start port rather
# than at the end port.
circuit = i3.Circuit(
    insts={
        "wg1": i3.Waveguide(trace_template=tt_start),
        "wg2": i3.Waveguide(trace_template=tt_end),
    },
    specs=[
        i3.Place("wg1", (0, 0)),
        i3.Place("wg2", (40, 40)),
        i3.ConnectManhattan("wg1:out", "wg2:in", bend_radius=10, trace_template=tt_end),
    ],
)
circuit_layout = circuit.Layout()
circuit_layout.visualize()
../../../../_images/ipkiss3-all-ConnectManhattan-8.png

If you want to have more control over the bends, you can provide the fixed_bend90 parameter.:

"""Example showcasing fixed bend usage"""
import si_fab.all as pdk  # noqa: F401
import ipkiss3.all as i3
from picazzo3.traces.rib_wg.trace import RibWaveguideTemplate

# Trace template for the ports
tt1 = RibWaveguideTemplate()
tt1.Layout(core_width=0.5)

# Bend to use for the 90 degree sections
bend = i3.RoundedWaveguide(trace_template=tt1)
bend.Layout(shape=[(-10.0, 0.0), (0.0, 0.0), (0, 10.0)], bend_radius=10.0)

wg = i3.Waveguide(trace_template=tt1).Layout()

circuit = i3.Circuit(
    insts={
        "wg1": wg,
        "wg2": wg,
        "wg3": wg,
        "wg4": wg,
    },
    specs=[
        i3.Place("wg1:in", position=(0, 0), angle=0.0),
        i3.Place("wg2:in", position=(100, 100), angle=180.0),
        i3.Place("wg3:in", position=(0, 50), angle=20),
        i3.Place("wg4:in", position=(50, 50 + 70), angle=90.0),
        i3.ConnectManhattan(
            [
                ("wg1:in", "wg2:in"),
                ("wg3:in", "wg4:in"),
            ],
            bend_radius=5.0,
            trace_template=tt1,
            fixed_bend90=bend,
        ),
    ],
)

lo = circuit.get_default_view(i3.LayoutView)
lo.visualize()
../../../../_images/ipkiss3-all-ConnectManhattan-9.png

Covers can be added to the bends on a specified layer.:

import si_fab.all as pdk  # noqa: F401
import ipkiss3.all as i3

wg = i3.Waveguide()
circuit = i3.Circuit(
    insts={"wg1": wg, "wg2": wg},
    specs=[
        i3.Place("wg1", (0, 0)),
        i3.Place("wg2", (15, 15), angle=90),
        i3.ConnectManhattan(
            "wg1:out", "wg2:in", bend_radius=7, cover_bends=True, cover_layers=[i3.TECH.PPLAYER.SI_CLADDING]
        ),
    ],
)
circuit_layout = circuit.Layout()
circuit_layout.visualize()
../../../../_images/ipkiss3-all-ConnectManhattan-10.png