PlaceAndConnect

class picazzo3.routing.place_route.cell.PlaceAndConnect

Deprecated since version 2025.03: Use i3.Circuit instead. PlaceAndConnect will be removed in version 2026.03.

Parametric Cell for manual placement and Logical connection of components.

The user supplies a dictionary of the instances of child cells that need to be placed through the property child_cells. This dictionary maps the instance names to the PCell objects. The same PCell object can be used for multiple instances.

child_cells={ "ring1"  : my_ring1,
              "ring2"  : my_ring2,
              "spl"    : my_splitter,
              "com"    : my_splitter # the same cell is used both for splitting and combining
              }

The connectivity between the instances of the child cells is set by a list of tuples containing pairs of instance terms/port names. This list links is of the form instname:portname

links=[ ("spl:arm1",   "arm1:in1"),
        ("arm1:out1", "com:arm1"),
        ("spl:arm2",   "arm2:in1"),
        ("arm2:out1", "com:arm2")
        ]

All the unused terms of the instances are connected to outside terms. You can override the default external term names using the external_port_names property. There you can specify the individual names of the external terms. If no name is specified, the default pattern of ‘instname_termname’ will be used.

external_port_names={ "spl:in1"  : "input",
                      "com:out1" : "output"
                      }

The PCell will place the waveguides and connect them logically in the netlist, but it is up to the user to verify whether the physical location of the connected ports matches.

In the layout, the placement is specified manually using the child_transformations property, which defines a transformation for each instance. If no transformation is supplied for an instance, no transformation will be applied. It is also possible to supply a coordinate (Coord2) or tuple, which will be interpreted as a position for placement.

child_transformations={"arm1": (50, -50),
                       "arm2": (50,50),
                       "com": i3.HMirror(0.0)+i3.Translation((100,0))}

Child cells that are logically connected but where the ports are not physically connected (e.g. by wrong placement), will be connected with visual flylines.

You can subclass this PCell in order to implement your own additional functionality, or define subcircuits that define their own child cells and child transformations. Warning: do not refer to self.child_cells from within an overridden _default_child_transformations - rather refer to the child cell directly (like self.my_child_cell).

Parameters:
links: list and List with type restriction, allowed types: [<class ‘collections.abc.Sequence’>]

list of tuples connecting the instances. Format is [(‘inst1:term1’,’inst2:term2’), …]

external_port_names: str

Map of the free instance terms/ports to the names of external terms/ports.Format is a dict {‘inst:term’ : ‘new_term_name’}.If a term/port is not listed, the format instname_portname will be used

child_cells:

dict to create the instances of the child cells. Format is {‘inst_name1’: PCell}

name: String that contains only ISO/IEC 8859-1 (extended ASCII py3) or pure ASCII (py2) characters

The unique name of the pcell

Views

class Layout
Parameters:
flyline_width: float and number > 0

line width of the flylines

flyline_layer: ( __Layer__ ), *None allowed*

layer to draw flylines of physically unconnected links

child_transformations:

dictionary with the transformation of each child instance.

netlist_view: NetlistView

Netlist view in the same cell on which this Layout is based. Normally no need to manually override.

view_name: String that contains only alphanumeric characters from the ASCII set or contains _$. ASCII set is extended on PY3.

The name of the view

Examples

def __example_layout1(self):
    """Here we connect together 2 splitters and two rings to form a Ring-loaded
    Mach-Zehnder. We use the splitter twice (as splitter and combiner) but use
    two different rings. We calculate the transformations of the rings in such
    a way that they attach correctly to the splitter and combiner
    """
    import si_fab.all as pdk  # noqa: F401

    from ipkiss3 import all as i3  # noqa: F401
    from ipkiss.geometry.vector import vector_match_transform
    from picazzo3.filters.ring import RingRect180DropFilter, RingRectNotchFilter
    from picazzo3.routing.place_route import PlaceAndConnect
    from picazzo3.wg.splitters import WgY180Splitter

    # both rings have the same size
    ring1 = RingRectNotchFilter()
    ring1_layout = ring1.Layout()
    ring2 = RingRect180DropFilter()
    ring2_layout = ring2.Layout()
    splitter = WgY180Splitter()
    splitter_layout = splitter.Layout()

    pr = PlaceAndConnect(
        child_cells={
            "spl": splitter,
            "com": splitter,
            "arm1": ring1,
            "arm2": ring2,
        },
        links=[
            ("spl:arm1", "arm1:in"),
            ("arm1:out", "com:arm1"),
            ("spl:arm2", "arm2:in1"),
            ("arm2:out1", "com:arm2"),
        ],
    )

    # manually calculate the transformations needed to attach the ports together
    t_ring1 = vector_match_transform(ring1_layout.ports["in"], splitter_layout.ports["arm1"])
    t_ring2 = vector_match_transform(ring2_layout.ports["in1"], splitter_layout.ports["arm2"])
    t_com = (
        vector_match_transform(splitter_layout.ports["arm1"], ring1_layout.ports["out"], mirrored=True)
        + t_ring1
    )

    layout = pr.Layout(child_transformations={"arm1": t_ring1, "arm2": t_ring2, "com": t_com})
    layout.visualize(annotate=True)