def add_electrical_pads_top(component: Component, **kwargs) -> Component: """connects component electrical ports with pad array at the top Args: component: pad: pad element spacing: pad array (x, y) spacing width: pad width height: pad height layer: pad layer """ c = Component(f"{component.name}_e") ports = component.get_ports_list(port_type="dc") c << component pads = c << pad_array(n=len(ports), port_list=["S"], **kwargs) pads.x = component.x pads.y = component.ymax + 100 ports_pads = list(pads.ports.values()) for p1, p2 in zip(ports_pads, ports): c.add(connect_electrical_shortest_path(p1, p2)) c.ports = component.ports for port in ports: c.ports.pop(port.name) return c
def pad( width: int = 100, height: int = 100, layer: Tuple[int, int] = LAYER.M3 ) -> Component: """rectangular pad with 4 ports (N, S, E, W) Args: width: pad width height: pad height layer: pad layer .. plot:: :include-source: import pp c = pp.c.pad(width=100, height=100, layer=pp.LAYER.M3) pp.plotgds(c) """ c = Component() _c = compass(size=(width, height), layer=layer).ref() c.add(_c) c.absorb(_c) c.ports = _c.ports return c
def add_labels( component: Component, port_type: str = "dc", get_label_function: Callable = get_input_label_electrical, layer_label: Layer = pp.LAYER.LABEL, gc: Optional[Component] = None, ) -> Component: """Add labels a particular type of ports Args: component: to add labels to port_type: type of port ('dc', 'optical', 'electrical') get_label_function: function to get label layer_label: layer_label Returns: original component with labels """ ports = component.get_ports_list(port_type=port_type) for i, port in enumerate(ports): label = get_label_function( port=port, gc=gc, gc_index=i, component_name=component.name, layer_label=layer_label, ) component.add(label) return component
def add_grating_couplers( component: Component, grating_coupler=grating_coupler_te, layer_label=pp.LAYER.LABEL, gc_port_name: str = "W0", get_input_labels_function=get_input_labels, ): """Return component with grating couplers and labels.""" cnew = Component(name=component.name + "_c") cnew.add_ref(component) grating_coupler = pp.call_if_func(grating_coupler) io_gratings = [] for port in component.ports.values(): gc_ref = grating_coupler.ref() gc_ref.connect(list(gc_ref.ports.values())[0], port) io_gratings.append(gc_ref) cnew.add(gc_ref) labels = get_input_labels_function( io_gratings, list(component.ports.values()), component_name=component.name, layer_label=layer_label, gc_port_name=gc_port_name, ) cnew.add(labels) return cnew
def add_trenches( c: Component, sstw: float = 2.0, trench_width: float = 0.5, trench_keep_out: float = 2.0, trenches: List[Dict[str, int]] = [ { "nb_segments": 2, "lane": 1, "x_start_offset": 0 }, { "nb_segments": 2, "lane": -1, "x_start_offset": 0 }, ], layer_trench: Tuple[int, int] = LAYER.DEEPTRENCH, ) -> Component: """ Add trenches to a waveguide-heater-like component """ heater_width = c.settings["heater_width"] heater_spacing = c.settings["heater_spacing"] width = c.settings["width"] length = c.settings["length"] a = heater_spacing + (width + heater_width) / 2 # Add trenches if trench_width and trench_width > 0: tko = trench_keep_out for trench in trenches: lane = trench["lane"] td = tko + a + (trench_width + heater_width) / 2 y = np.sign(lane) * (td + (abs(lane) - 1) * (trench_width + tko)) x_start_offset = trench["x_start_offset"] if "segments" not in trench: nb_segments = trench["nb_segments"] trench_length = (length - (nb_segments - 1) * sstw) / nb_segments segments = [trench_length] * nb_segments else: segments = trench["segments"] x = x_start_offset for i, trench_length in enumerate(segments): trench = hline(length=trench_length, width=trench_width, layer=layer_trench) _trench = trench.ref(port_id="W0", position=c.ports["W0"].position + (x, y)) c.add(_trench) c.absorb(_trench) x += trench_length + sstw return c
def tlm( width: float = 11.0, height: float = 11.0, layers: List[Tuple[int, int]] = [LAYER.M1, LAYER.M2, LAYER.M3], vias: List[Any] = [via2, via3], ) -> Component: """ Rectangular transition thru metal layers Args: name: component name width, height: rectangle parameters layers: layers on which to draw rectangles vias: vias to use to fill the rectangles Returns <Component> """ # assert len(layers) - 1 == len(vias), "tlm: There should be N layers for N-1 vias" a = width / 2 b = height / 2 rect_pts = [(-a, -b), (a, -b), (a, b), (-a, b)] c = Component() # Add metal rectangles for layer in layers: c.add_polygon(rect_pts, layer=layer) # Add vias for via in vias: via = via() if callable(via) else via w = via.info["width"] h = via.info["height"] g = via.info["clearance"] period = via.info["period"] nb_vias_x = (width - w - 2 * g) / period + 1 nb_vias_y = (height - h - 2 * g) / period + 1 nb_vias_x = int(floor(nb_vias_x)) nb_vias_y = int(floor(nb_vias_y)) cw = (width - (nb_vias_x - 1) * period - w) / 2 ch = (height - (nb_vias_y - 1) * period - h) / 2 x0 = -a + cw + w / 2 y0 = -b + ch + h / 2 for i in range(nb_vias_x): for j in range(nb_vias_y): c.add(via.ref(position=(x0 + i * period, y0 + j * period))) return c
def test_connect_bundle_u_indirect(dy=-200, angle=180): xs1 = [-100, -90, -80, -55, -35] + [200, 210, 240] axis = "X" if angle in [0, 180] else "Y" pitch = 10.0 N = len(xs1) xs2 = [50 + i * pitch for i in range(N)] a1 = angle a2 = a1 + 180 if axis == "X": ports1 = [ Port("top_{}".format(i), (0, xs1[i]), 0.5, a1) for i in range(N) ] ports2 = [ Port("bottom_{}".format(i), (dy, xs2[i]), 0.5, a2) for i in range(N) ] else: ports1 = [ Port("top_{}".format(i), (xs1[i], 0), 0.5, a1) for i in range(N) ] ports2 = [ Port("bottom_{}".format(i), (xs2[i], dy), 0.5, a2) for i in range(N) ] top_cell = Component("connect_bundle_u_indirect") routes = connect_bundle(ports1, ports2) lengths = [ 341.41592653589794, 341.41592653589794, 341.41592653589794, 326.41592653589794, 316.41592653589794, 291.41592653589794, 291.41592653589794, 311.41592653589794, ] for route, length in zip(routes, lengths): # print(route.parent.length) assert np.isclose(route.parent.length, length) top_cell.add(routes) return top_cell
def demo_connect_bundle(): """ combines all the connect_bundle tests """ y = 400.0 x = 500 y0 = 900 dy = 200.0 c = Component("connect_bundle") for j, s in enumerate([-1, 1]): for i, angle in enumerate([0, 90, 180, 270]): c2 = test_connect_bundle_u_indirect(dy=s * dy, angle=angle) c2ref = c2.ref(position=(i * x, j * y)) c.add(c2ref) c2 = test_connect_bundle_udirect(dy=s * dy, angle=angle) c2ref = c2.ref(position=(i * x, j * y + y0)) c.add(c2ref) for i, config in enumerate(["A", "B", "C", "D"]): c2 = test_connect_corner(config=config) c2ref = c2.ref(position=(i * x, 1700)) c.add(c2ref) c2 = test_facing_ports() c2ref = c2.ref(position=(800, 1820)) c.add(c2ref) return c
def top_level(): cmp = Component() _dummy_t = dummy() sides = ["north", "south", "east", "west"] positions = [(0, 0), (400, 0), (400, 400), (0, 400)] for pos, side in zip(positions, sides): dummy_ref = _dummy_t.ref(position=pos) cmp.add(dummy_ref) conns, ports = route_ports_to_side(dummy_ref, side) for e in conns: cmp.add(e) for i, p in enumerate(ports): cmp.add_port(name="{}{}".format(side[0], i), port=p) return cmp
def test_connect_bundle_udirect(dy=200, angle=270): xs1 = [-100, -90, -80, -55, -35, 24, 0] + [200, 210, 240] axis = "X" if angle in [0, 180] else "Y" pitch = 10.0 N = len(xs1) xs2 = [50 + i * pitch for i in range(N)] if axis == "X": ports1 = [ Port("top_{}".format(i), (0, xs1[i]), 0.5, angle) for i in range(N) ] ports2 = [ Port("bottom_{}".format(i), (dy, xs2[i]), 0.5, angle) for i in range(N) ] else: ports1 = [ Port("top_{}".format(i), (xs1[i], 0), 0.5, angle) for i in range(N) ] ports2 = [ Port("bottom_{}".format(i), (xs2[i], dy), 0.5, angle) for i in range(N) ] top_cell = Component(name="connect_bundle_udirect") routes = connect_bundle(ports1, ports2) lengths = [ 237.4359265358979, 281.4359265358979, 336.4359265358979, 376.4359265358979, 421.4359265358979, 451.4359265358979, 481.4359265358979, 271.4359265358979, ] for route, length in zip(routes, lengths): # print(route.parent.length) assert np.isclose(route.parent.length, length) top_cell.add(routes) return top_cell
def add_electrical_pads(component: Component, rotation=180, **kwargs): """add compnent with top electrical pads and routes Args: component: Component, pad_spacing: float = 150., pad: Callable = pad, fanout_length: Optional[int] = None, max_y0_optical: None = None, waveguide_separation: float = 4.0, bend_radius: float = 0.1, connected_port_list_ids: None = None, n_ports: int = 1, excluded_ports: List[Any] = [], pad_indices: None = None, route_filter: Callable = connect_elec_waypoints, port_name: str = "W", pad_rotation: int = -90, x_pad_offset: int = 0, port_labels: None = None, select_ports: Callable = select_electrical_ports, """ c = Component(f"{component.name}_pad") cr = rotate(component, rotation) elements, pads, _ = route_pad_array( component=cr, **kwargs, ) c << cr for e in elements: c.add(e) for e in pads: c.add(e) for pname, p in cr.ports.items(): if p.port_type == "optical": c.add_port(pname, port=p) return c.rotate(angle=-rotation)
def _arbitrary_straight_waveguide(length, windows): """ Args: length: length windows: [(y_start, y_stop, layer), ...] """ md5 = hashlib.md5() for e in windows: md5.update(str(e).encode()) component = Component() component.name = "ARB_SW_L{}_HASH{}".format(length, md5.hexdigest()) y_min, y_max, layer0 = windows[0] y_min, y_max = min(y_min, y_max), max(y_min, y_max) # Add one port on each side centered at y=0 for y_start, y_stop, layer in windows: w = abs(y_stop - y_start) y = (y_stop + y_start) / 2 _wg = hline(length=length, width=w, layer=layer).ref() _wg.movey(y) component.add(_wg) component.absorb(_wg) y_min = min(y_stop, y_start, y_min) y_max = max(y_stop, y_start, y_max) width = y_max - y_min component.add_port(name="W0", midpoint=[0, 0], width=width, orientation=180, layer=layer0) component.add_port(name="E0", midpoint=[length, 0], width=width, orientation=0, layer=layer0) return component
def import_phidl_component(component: Device, **kwargs) -> Component: """ returns a gdsfactory Component from a phidl Device or function """ D = call_if_func(component, **kwargs) D_copy = Component(name=D._internal_name) D_copy.info = copy.deepcopy(D.info) for ref in D.references: new_ref = ComponentReference( device=ref.parent, origin=ref.origin, rotation=ref.rotation, magnification=ref.magnification, x_reflection=ref.x_reflection, ) new_ref.owner = D_copy D_copy.add(new_ref) for alias_name, alias_ref in D.aliases.items(): if alias_ref == ref: D_copy.aliases[alias_name] = new_ref for p in D.ports.values(): D_copy.add_port( port=Port( name=p.name, midpoint=p.midpoint, width=p.width, orientation=p.orientation, parent=p.parent, ) ) for poly in D.polygons: D_copy.add_polygon(poly) for label in D.labels: D_copy.add_label( text=label.text, position=label.position, layer=(label.layer, label.texttype), ) return D_copy
def test_facing_ports(): dy = 200.0 xs1 = [-500, -300, -100, -90, -80, -55, -35, 200, 210, 240, 500, 650] pitch = 10.0 N = len(xs1) xs2 = [-20 + i * pitch for i in range(N // 2)] xs2 += [400 + i * pitch for i in range(N // 2)] a1 = 90 a2 = a1 + 180 ports1 = [Port("top_{}".format(i), (xs1[i], 0), 0.5, a1) for i in range(N)] ports2 = [ Port("bottom_{}".format(i), (xs2[i], dy), 0.5, a2) for i in range(N) ] top_cell = Component("test_facing_ports") routes = connect_bundle(ports1, ports2) lengths = [ 671.4159265358979, 481.41592653589794, 291.41592653589794, 291.41592653589794, 291.41592653589794, 276.41592653589794, 626.4159265358979, 401.41592653589794, 401.41592653589794, 381.41592653589794, 251.41592653589794, 391.41592653589794, ] for route, length in zip(routes, lengths): # print(route.parent.length) assert np.isclose(route["settings"]["length"], length) top_cell.add(route["references"]) return top_cell
def test_connect_bundle_u_indirect(dy=-200, angle=180): xs1 = [-100, -90, -80, -55, -35] + [200, 210, 240] axis = "X" if angle in [0, 180] else "Y" pitch = 10.0 N = len(xs1) xs2 = [50 + i * pitch for i in range(N)] a1 = angle a2 = a1 + 180 if axis == "X": ports1 = [ Port("top_{}".format(i), (0, xs1[i]), 0.5, a1) for i in range(N) ] ports2 = [ Port("bottom_{}".format(i), (dy, xs2[i]), 0.5, a2) for i in range(N) ] else: ports1 = [ Port("top_{}".format(i), (xs1[i], 0), 0.5, a1) for i in range(N) ] ports2 = [ Port("bottom_{}".format(i), (xs2[i], dy), 0.5, a2) for i in range(N) ] top_cell = Component("connect_bundle_u_indirect") elements = connect_bundle(ports1, ports2) for e in elements: top_cell.add(e) return top_cell
def add_electrical_pads_top( component: Component, component_top_to_pad_bottom_distance: float = 100.0, route_filter=connect_elec_waypoints, **kwargs, ) -> Component: """connects component electrical ports with pad array at the top Args: component: pad: pad element spacing: pad array (x, y) spacing width: pad width height: pad height layer: pad layer """ c = Component(f"{component.name}_e") ports = component.get_ports_list(port_type="dc") # for port in ports: # print(port.name) # print(len(ports)) c << component pads = c << pad_array(n=len(ports), port_list=["S"], **kwargs) pads.x = component.x pads.ymin = component.ymax + component_top_to_pad_bottom_distance ports_pads = list(pads.ports.values()) ports_pads.sort(key=lambda p: p.x) ports.sort(key=lambda p: p.x) for p1, p2 in zip(ports_pads, ports): c.add(connect_electrical_shortest_path(p1, p2)) c.ports = component.ports.copy() for port in ports: c.ports.pop(port.name) return c
def test_facing_ports(): dy = 200.0 xs1 = [-500, -300, -100, -90, -80, -55, -35, 200, 210, 240, 500, 650] pitch = 10.0 N = len(xs1) xs2 = [-20 + i * pitch for i in range(N // 2)] xs2 += [400 + i * pitch for i in range(N // 2)] a1 = 90 a2 = a1 + 180 ports1 = [Port("top_{}".format(i), (xs1[i], 0), 0.5, a1) for i in range(N)] ports2 = [ Port("bottom_{}".format(i), (xs2[i], dy), 0.5, a2) for i in range(N) ] top_cell = Component("test_facing_ports") elements = connect_bundle(ports1, ports2) # elements = link_ports_path_length_match(ports1, ports2) top_cell.add(elements) return top_cell
def test_connect_bundle(): xs_top = [-100, -90, -80, 0, 10, 20, 40, 50, 80, 90, 100, 105, 110, 115] pitch = 127.0 N = len(xs_top) xs_bottom = [(i - N / 2) * pitch for i in range(N)] top_ports = [Port(f"top_{i}", (xs_top[i], 0), 0.5, 270) for i in range(N)] bottom_ports = [ Port(f"bottom_{i}", (xs_bottom[i], -400), 0.5, 90) for i in range(N) ] top_cell = Component(name="connect_bundle") routes = connect_bundle(top_ports, bottom_ports) lengths = [ 1180.4159265358978, 1063.4159265358978, 946.4159265358978, 899.4159265358979, 782.4159265358979, 665.4159265358979, 558.4159265358979, 441.41592653589794, 438.41592653589794, 555.4159265358979, 672.4159265358979, 794.4159265358979, 916.415926535898, 1038.415926535898, ] for route, length in zip(routes, lengths): # print(route.parent.length) top_cell.add(route["references"]) assert np.isclose(route["settings"]["length"], length) return top_cell
def test_connect_bundle(): xs_top = [-100, -90, -80, 0, 10, 20, 40, 50, 80, 90, 100, 105, 110, 115] pitch = 127.0 N = len(xs_top) xs_bottom = [(i - N / 2) * pitch for i in range(N)] top_ports = [ Port("top_{}".format(i), (xs_top[i], 0), 0.5, 270) for i in range(N) ] bottom_ports = [ Port("bottom_{}".format(i), (xs_bottom[i], -400), 0.5, 90) for i in range(N) ] top_cell = Component(name="connect_bundle") elements = connect_bundle(top_ports, bottom_ports) for e in elements: top_cell.add(e) top_cell.name = "connect_bundle" return top_cell
def add_electrical_pads_shortest(component, pad=pad, pad_port_spacing=50, **kwargs): """add a pad to each closest electrical port Args: component: pad: pad element or function pad_port_spacing: between pad and port width: pad width height: pad height layer: pad layer """ c = Component(f"{component.name}_e") ports = component.get_ports_list(port_type="dc") c << component pad = pad(**kwargs) if callable(pad) else pad pad_port_spacing += pad.settings["width"] / 2 for port in ports: p = c << pad if port.orientation == 0: p.x = port.x + pad_port_spacing p.y = port.y c.add(connect_electrical_shortest_path(port, p.ports["W"])) elif port.orientation == 180: p.x = port.x - pad_port_spacing p.y = port.y c.add(connect_electrical_shortest_path(port, p.ports["E"])) elif port.orientation == 90: p.y = port.y + pad_port_spacing p.x = port.x c.add(connect_electrical_shortest_path(port, p.ports["S"])) elif port.orientation == 270: p.y = port.y - pad_port_spacing p.x = port.x c.add(connect_electrical_shortest_path(port, p.ports["N"])) c.ports = component.ports for port in ports: c.ports.pop(port.name) return c
def component_from_yaml( yaml_str: Union[str, pathlib.Path, IO[Any]], component_factory=None, route_factory=route_factory, link_factory=link_factory, label_instance_function=_add_instance_label, **kwargs, ) -> Component: """Returns a Component defined in YAML file or string. Args: yaml: YAML IO describing Component file or string (with newlines) (instances, placements, routes, ports, connections, names) component_factory: dict of {factory_name: factory_function} route_factory: for routes link_factory: for links label_instance_function: to label each instance kwargs: cache, pins ... to pass to all factories Returns: Component .. code:: valid properties: name: name of Component instances: name: component: settings (Optional) placements: x: Optional[float, str] str can be instanceName,portName y: Optional[float, str] rotation: Optional[float] mirror: Optional[bool, float] float is x mirror axis port: Optional[str] port anchor connections (Optional): between instances ports (Optional): defines ports to expose routes (Optional): defines bundles of routes routeName: factory: optical links: instance1,port1: instance2,port2 .. code:: instances: mmi_bot: component: mmi1x2 settings: width_mmi: 4.5 length_mmi: 10 mmi_top: component: mmi1x2 settings: width_mmi: 4.5 length_mmi: 5 placements: mmi_top: port: W0 x: 0 y: 0 mmi_bot: port: W0 x: mmi_top,E1 y: mmi_top,E1 dx: 30 dy: -30 routes: optical: factory: optical links: mmi_top,E0: mmi_bot,W0 """ yaml_str = (io.StringIO(yaml_str) if isinstance(yaml_str, str) and "\n" in yaml_str else yaml_str) component_factory = component_factory or component_factory_default conf = OmegaConf.load( yaml_str) # nicer loader than conf = yaml.safe_load(yaml_str) for key in conf.keys(): assert key in valid_keys, f"{key} not in {list(valid_keys)}" instances = {} routes = {} name = conf.get("name", "Unnamed") c = Component(name) placements_conf = conf.get("placements") routes_conf = conf.get("routes") ports_conf = conf.get("ports") connections_conf = conf.get("connections") instances_dict = conf["instances"] for instance_name in instances_dict: instance_conf = instances_dict[instance_name] component_type = instance_conf["component"] assert (component_type in component_factory ), f"{component_type} not in {list(component_factory.keys())}" component_settings = instance_conf["settings"] or {} component_settings.update(**kwargs) ci = component_factory[component_type](**component_settings) ref = c << ci instances[instance_name] = ref while placements_conf: place( placements_conf=placements_conf, instances=instances, encountered_insts=list(), ) if connections_conf: for port_src_string, port_dst_string in connections_conf.items(): instance_src_name, port_src_name = port_src_string.split(",") instance_dst_name, port_dst_name = port_dst_string.split(",") instance_src_name = instance_src_name.strip() instance_dst_name = instance_dst_name.strip() port_src_name = port_src_name.strip() port_dst_name = port_dst_name.strip() assert (instance_src_name in instances ), f"{instance_src_name} not in {list(instances.keys())}" assert (instance_dst_name in instances ), f"{instance_dst_name} not in {list(instances.keys())}" instance_src = instances[instance_src_name] instance_dst = instances[instance_dst_name] assert port_src_name in instance_src.ports, ( f"{port_src_name} not in {list(instance_src.ports.keys())} for" f" {instance_src_name} ") assert port_dst_name in instance_dst.ports, ( f"{port_dst_name} not in {list(instance_dst.ports.keys())} for" f" {instance_dst_name}") port_dst = instance_dst.ports[port_dst_name] instance_src.connect(port=port_src_name, destination=port_dst) for instance_name in instances_dict: label_instance_function(component=c, instance_name=instance_name, reference=instances[instance_name]) if routes_conf: for route_alias in routes_conf: route_names = [] ports1 = [] ports2 = [] routes_dict = routes_conf[route_alias] if not hasattr(routes_dict, "__items__"): print(f"Unvalid syntax for {routes_dict}\n", sample_mmis) raise ValueError(f"Unvalid syntax for {routes_dict}") for key in routes_dict.keys(): if key not in valid_route_keys: raise ValueError( f"`{route_alias}` has a key=`{key}` not in valid {valid_route_keys}" ) if "factory" not in routes_dict: raise ValueError( f"`{route_alias}` route needs `factory` : {list(route_factory.keys())}" ) route_type = routes_dict.pop("factory") assert isinstance(route_factory, dict), "route_factory needs to be a dict" assert ( route_type in route_factory ), f"factory `{route_type}` not in route_factory {list(route_factory.keys())}" route_filter = route_factory[route_type] route_settings = routes_dict.pop("settings", {}) link_function_name = routes_dict.pop("link_factory", "link_ports") assert ( link_function_name in link_factory ), f"function `{link_function_name}` not in link_factory {list(link_factory.keys())}" link_function = link_factory[link_function_name] link_settings = routes_dict.pop("link_settings", {}) if "links" not in routes_dict: raise ValueError( f"You need to define links for the `{route_alias}` route") links_dict = routes_dict["links"] for port_src_string, port_dst_string in links_dict.items(): # print(port_src_string) if ":" in port_src_string: src, src0, src1 = [ s.strip() for s in port_src_string.split(":") ] dst, dst0, dst1 = [ s.strip() for s in port_dst_string.split(":") ] instance_src_name, port_src_name = [ s.strip() for s in src.split(",") ] instance_dst_name, port_dst_name = [ s.strip() for s in dst.split(",") ] src0 = int(src0) src1 = int(src1) dst0 = int(dst0) dst1 = int(dst1) if src1 > src0: ports1names = [ f"{port_src_name}{i}" for i in range(src0, src1 + 1, 1) ] else: ports1names = [ f"{port_src_name}{i}" for i in range(src0, src1 - 1, -1) ] if dst1 > dst0: ports2names = [ f"{port_dst_name}{i}" for i in range(dst0, dst1 + 1, 1) ] else: ports2names = [ f"{port_dst_name}{i}" for i in range(dst0, dst1 - 1, -1) ] # print(ports1names) # print(ports2names) assert len(ports1names) == len(ports2names) route_names += [ f"{instance_src_name},{i}:{instance_dst_name},{j}" for i, j in zip(ports1names, ports2names) ] instance_src = instances[instance_src_name] instance_dst = instances[instance_dst_name] for port_src_name in ports1names: assert port_src_name in instance_src.ports, ( f"{port_src_name} not in {list(instance_src.ports.keys())}" f"for {instance_src_name} ") ports1.append(instance_src.ports[port_src_name]) for port_dst_name in ports2names: assert port_dst_name in instance_dst.ports, ( f"{port_dst_name} not in {list(instance_dst.ports.keys())}" f"for {instance_dst_name}") ports2.append(instance_dst.ports[port_dst_name]) # print(ports1) # print(ports2) print(route_names) else: instance_src_name, port_src_name = port_src_string.split( ",") instance_dst_name, port_dst_name = port_dst_string.split( ",") instance_src_name = instance_src_name.strip() instance_dst_name = instance_dst_name.strip() port_src_name = port_src_name.strip() port_dst_name = port_dst_name.strip() assert ( instance_src_name in instances ), f"{instance_src_name} not in {list(instances.keys())}" assert ( instance_dst_name in instances ), f"{instance_dst_name} not in {list(instances.keys())}" instance_src = instances[instance_src_name] instance_dst = instances[instance_dst_name] assert port_src_name in instance_src.ports, ( f"{port_src_name} not in {list(instance_src.ports.keys())} for" f" {instance_src_name} ") assert port_dst_name in instance_dst.ports, ( f"{port_dst_name} not in {list(instance_dst.ports.keys())} for" f" {instance_dst_name}") ports1.append(instance_src.ports[port_src_name]) ports2.append(instance_dst.ports[port_dst_name]) route_name = f"{port_src_string}:{port_dst_string}" route_names.append(route_name) if link_function_name in [ "link_electrical_waypoints", "link_optical_waypoints", ]: route = link_function( route_filter=route_filter, **route_settings, **link_settings, ) routes[route_name] = route else: route = link_function( ports1, ports2, route_filter=route_filter, **route_settings, **link_settings, ) for i, r in enumerate(route): routes[route_names[i]] = r c.add(route) if ports_conf: assert hasattr(ports_conf, "items"), f"{ports_conf} needs to be a dict" for port_name, instance_comma_port in ports_conf.items(): instance_name, instance_port_name = instance_comma_port.split(",") instance_name = instance_name.strip() instance_port_name = instance_port_name.strip() assert (instance_name in instances ), f"{instance_name} not in {list(instances.keys())}" instance = instances[instance_name] assert instance_port_name in instance.ports, ( f"{instance_port_name} not in {list(instance.ports.keys())} for" f" {instance_name} ") c.add_port(port_name, port=instance.ports[instance_port_name]) c.instances = instances c.routes = routes return c
def wg_heater_connector( heater_ports: List[Port], metal_width: float = 10.0, tlm_layers: List[Tuple[int, int]] = [ LAYER.VIA1, LAYER.M1, LAYER.VIA2, LAYER.M2, LAYER.VIA3, LAYER.M3, ], ) -> Component: """ Connects together a pair of wg heaters and connect to a M3 port """ cmp = Component() assert len(heater_ports) == 2 assert (heater_ports[0].orientation == heater_ports[1].orientation ), "both ports should be facing in the same direction" angle = heater_ports[0].orientation angle = angle % 360 assert angle in [0, 180], "angle should be 0 or 180, got {}".format(angle) dx = 0.0 dy = 0.0 angle_to_dps = {0: [(-dx, -dy), (-dx, dy)], 180: [(dx, -dy), (dx, dy)]} ports = heater_ports hw = heater_ports[0].width if angle in [0, 180]: ports.sort(key=lambda p: p.y) else: ports.sort(key=lambda p: p.x) _heater_to_metal = tlm(width=0.5, height=0.5, layers=tlm_layers, vias=[]) tlm_positions = [] for port, dp in zip(ports, angle_to_dps[angle]): # Extend heater p = port.midpoint # Add via/metal transitions tlm_pos = p + dp hm = _heater_to_metal.ref(position=tlm_pos) tlm_positions += [tlm_pos] cmp.add(hm) ss = 1 if angle == 0 else -1 # Connect both sides with top metal edge_metal_piece_width = 7.0 x = ss * edge_metal_piece_width / 2 top_metal_layer = tlm_layers[-1] cmp.add_polygon( line( tlm_positions[0] + (x, -hw / 2), tlm_positions[1] + (x, hw / 2), edge_metal_piece_width, ), layer=top_metal_layer, ) # Add metal port cmp.add_port( name="0", midpoint=0.5 * sum(tlm_positions) + (ss * edge_metal_piece_width / 2, 0), orientation=angle, width=metal_width, layer=top_metal_layer, port_type="dc", ) return cmp
p2x1 = port2.endpoints[1][0] p2y1 = port2.endpoints[1][1] if port1.orientation in [90, 270]: c.add_polygon(([(p1x1, p1y0), (p1x0, p1y1), (p2x1, p2y1), (p2x0, p2y0)]), layer=layer) else: c.add_polygon(([(p1x0, p1y1), (p1x1, p1y0), (p2x1, p2y1), (p2x0, p2y0)]), layer=layer) return c.ref() if __name__ == "__main__": import pp from pp.components.electrical.pad import pad_array c = Component("mzi_with_pads") mzi = pp.c.mzi2x2(with_elec_connections=True) pads = pad_array(n=3, port_list=["S"]) p = c << pads c << mzi p.move((-150, 200)) ports_pads = list(p.ports.values()) ports_mzi = mzi.get_ports_list(port_type="dc") for p1, p2 in zip(ports_pads, ports_mzi): c.add(connect_electrical_shortest_path(p1, p2)) pp.show(c)
def netlist_to_component( instances: Dict[str, Tuple[Component, str]], connections: List[Tuple[str, str, str, str]], ports_map: Dict[str, Tuple[str, str]] = None, position: Tuple[float, float] = (0.0, 0.0), ) -> Component: """ Netlist_to_component is deprecated! use pp.componet_from_yaml instead Returns a component from a netlist (instances, connections and ports map) Args: instances: list of (instance_id <str>, component <Component>, transform <tuple>) connections: list of (component_id1, port_name1, component_id2, port_name2) Has to be ordered such that any component refered to in the connections has already been placed (except for the first component) ports_map: {port_name: (instance_id, port_name)} Returns: component with netlist stored in component.netlist ``` [ { "name": "CP2x2", "rank": 0, "ports": ["in1", "in2", "out1", "out2"], "type": "CPBIDIR", "settings": {"R": 0.5}, }, { "name": "WG_CAVITY", "rank": 0, "ports": ["in", "out"], "type": "WG", "settings": {"length": 50, "loss_dB_m": 60000, "width": 0.5}, }, { "name": "ring_resonator", "rank": 1, "type": "COMPOUND", "settings": {}, "instances": [ "CP1, CP2x2, None, None, 0.0, 0.0", "WG1, WG_CAVITY, None, None, 0.0, 0.0", ], "connections": ["CP1, out1, WG1, in", "WG1, out, CP1, in1"], "ports": {"in1": "CP1, in2", "out1": "CP1, out2"}, }, ] ``` mirror, rotation, x, y """ if len(connections) == 0: raise ValueError("no connections defined") instance_id, port, _, _ = connections[0] assert instance_id in instances, f"{instance_id} not in {list(instances.keys())}" component, transform_name = instances[instance_id] # First component reference sref_start = gen_sref(component, transform_name, port, position) cmp_name_to_sref = {instance_id: sref_start} # Iterate over all connections: create and place instances for cmp1_name, port1, cmp2_name, port2 in connections: if cmp1_name not in cmp_name_to_sref: cmp1_name, port1, cmp2_name, port2 = cmp2_name, port2, cmp1_name, port1 if cmp2_name not in cmp_name_to_sref: component, transform_name = instances[cmp2_name] _ref = cmp_name_to_sref[cmp1_name] try: position = _ref.ports[port1].midpoint except Exception: print("{} has not port {}".format(cmp1_name, port1)) sref = gen_sref(component, transform_name, port2, position) cmp_name_to_sref[cmp2_name] = sref c = Component() c.add(list(cmp_name_to_sref.values())) if ports_map: for port_name, (cmp_id, internal_port_name) in ports_map.items(): component_ref = cmp_name_to_sref[cmp_id] component = component_ref.parent assert internal_port_name in component.ports, ( f"{internal_port_name} not in {component_ref.ports.keys()} for" f" {component}") port = component_ref.ports[internal_port_name] c.add_port(port_name, port=port) ports_map = {k: ", ".join(v) for k, v in ports_map.items()} # Set aliases for cmp_id, ref in cmp_name_to_sref.items(): c[cmp_id] = ref c.netlist = cmp_name_to_sref # add leaf cells to netlist netlist = [] for name, (component, _) in instances.items(): netlist.append( dict( name=name, rank=0, ports=list(component.ports.keys()), settings=component.settings, )) # add the compound cell to the netlist netlist.append( dict( name="component_name", rank=1, type="COMPOUND", settings={}, connections=[", ".join(i) for i in connections], ports=ports_map, )) c.netlist = netlist return c
def add_io_optical(c, grating_coupler=grating_coupler_te, gc_port_name="W0", component_name=None, **kwargs): """ returns component with optical IO (tapers, south routes and grating_couplers) Args: component: to connect optical_io_spacing: SPACING_GC grating_coupler: grating coupler instance, function or list of functions bend_factory: bend_circular straight_factory: waveguide fanout_length: None, # if None, automatic calculation of fanout length max_y0_optical: None with_align_ports: True, adds loopback structures waveguide_separation=4.0 bend_radius: BEND_RADIUS list_port_labels: None, adds TM labels to port indices in this list connected_port_list_ids: None # only for type 0 optical routing nb_optical_ports_lines: 1 force_manhattan: False excluded_ports: grating_indices: None routing_waveguide: None routing_method: connect_strip gc_port_name: W0 optical_routing_type: None: autoselection, 0: no extension gc_rotation=-90 layer_label=LAYER.LABEL input_port_indexes=[0] component_name: for the label """ if not c.ports: return c cc = Component( settings=c.get_settings(), test_protocol=c.test_protocol, data_analysis_protocol=c.data_analysis_protocol, ) cc.function_name = "add_io_optical" if isinstance(grating_coupler, list): gc = grating_coupler[0] else: gc = grating_coupler gc = pp.call_if_func(gc) if "polarization" in gc.settings: gc.polarization = gc.settings["polarization"] cc.name = "{}_{}".format(c.name, gc.polarization) port_width_gc = list(gc.ports.values())[0].width port_width_component = list(c.ports.values())[0].width if port_width_component != port_width_gc: c = add_tapers( c, taper(length=10, width1=port_width_gc, width2=port_width_component)) elements, io_gratings_lines, _ = _get_optical_io_elements( component=c, grating_coupler=grating_coupler, gc_port_name=gc_port_name, component_name=component_name, **kwargs) if len(elements) == 0: return c cc.add(elements) for io_gratings in io_gratings_lines: cc.add(io_gratings) cc.add(c.ref()) cc.move(origin=io_gratings_lines[0][0].ports[gc_port_name], destination=(0, 0)) return cc
def test_connect_corner(N=6, config="A"): d = 10.0 sep = 5.0 top_cell = Component(name="connect_corner") if config in ["A", "B"]: a = 100.0 ports_A_TR = [ Port("A_TR_{}".format(i), (d, a / 2 + i * sep), 0.5, 0) for i in range(N) ] ports_A_TL = [ Port("A_TL_{}".format(i), (-d, a / 2 + i * sep), 0.5, 180) for i in range(N) ] ports_A_BR = [ Port("A_BR_{}".format(i), (d, -a / 2 - i * sep), 0.5, 0) for i in range(N) ] ports_A_BL = [ Port("A_BL_{}".format(i), (-d, -a / 2 - i * sep), 0.5, 180) for i in range(N) ] ports_A = [ports_A_TR, ports_A_TL, ports_A_BR, ports_A_BL] ports_B_TR = [ Port("B_TR_{}".format(i), (a / 2 + i * sep, d), 0.5, 90) for i in range(N) ] ports_B_TL = [ Port("B_TL_{}".format(i), (-a / 2 - i * sep, d), 0.5, 90) for i in range(N) ] ports_B_BR = [ Port("B_BR_{}".format(i), (a / 2 + i * sep, -d), 0.5, 270) for i in range(N) ] ports_B_BL = [ Port("B_BL_{}".format(i), (-a / 2 - i * sep, -d), 0.5, 270) for i in range(N) ] ports_B = [ports_B_TR, ports_B_TL, ports_B_BR, ports_B_BL] elif config in ["C", "D"]: a = N * sep + 2 * d ports_A_TR = [ Port("A_TR_{}".format(i), (a, d + i * sep), 0.5, 0) for i in range(N) ] ports_A_TL = [ Port("A_TL_{}".format(i), (-a, d + i * sep), 0.5, 180) for i in range(N) ] ports_A_BR = [ Port("A_BR_{}".format(i), (a, -d - i * sep), 0.5, 0) for i in range(N) ] ports_A_BL = [ Port("A_BL_{}".format(i), (-a, -d - i * sep), 0.5, 180) for i in range(N) ] ports_A = [ports_A_TR, ports_A_TL, ports_A_BR, ports_A_BL] ports_B_TR = [ Port("B_TR_{}".format(i), (d + i * sep, a), 0.5, 90) for i in range(N) ] ports_B_TL = [ Port("B_TL_{}".format(i), (-d - i * sep, a), 0.5, 90) for i in range(N) ] ports_B_BR = [ Port("B_BR_{}".format(i), (d + i * sep, -a), 0.5, 270) for i in range(N) ] ports_B_BL = [ Port("B_BL_{}".format(i), (-d - i * sep, -a), 0.5, 270) for i in range(N) ] ports_B = [ports_B_TR, ports_B_TL, ports_B_BR, ports_B_BL] if config in ["A", "C"]: for ports1, ports2 in zip(ports_A, ports_B): routes = connect_bundle(ports1, ports2) for route in routes: top_cell.add(route["references"]) elif config in ["B", "D"]: for ports1, ports2 in zip(ports_A, ports_B): routes = connect_bundle(ports2, ports1) for route in routes: top_cell.add(route["references"]) return top_cell
def netlist_to_component(components, connections, ports_map, position=(0, 0)): """ Args: components: list of (component_id <str>, component <Component>, transform <tuple>) connections: list of (component_id1, port_name1, component_id2, port_name2) Has to be ordered such that any component refered to in the connections has already been placed (except for the first component) ports_map: {port_name: (component_id, port_name)} Returns: component the component has component.netlist [ { "name": "CP2x2", "rank": 0, "ports": ["in1", "in2", "out1", "out2"], "type": "CPBIDIR", "settings": {"R": 0.5}, }, { "name": "WG_CAVITY", "rank": 0, "ports": ["in", "out"], "type": "WG", "settings": {"length": 50, "loss_dB_m": 60000, "width": 0.5}, }, { "name": "ring_resonator", "rank": 1, "type": "COMPOUND", "settings": {}, "components": [ "CP1, CP2x2, None, None, 0.0, 0.0", "WG1, WG_CAVITY, None, None, 0.0, 0.0", ], "connections": ["CP1, out1, WG1, in", "WG1, out, CP1, in1"], "ports": {"in1": "CP1, in2", "out1": "CP1, out2"}, }, ] mirror, rotation, x, y """ if len(connections) == 0: raise ValueError("Error number of connections", len(connections), len(components)) component_id, cmp_port, _, _ = connections[0] component, transform_name = components[component_id] # First component reference sref_start = gen_sref(component, transform_name, cmp_port, position) cmp_name_to_sref = {component_id: sref_start} # Iterate over all connections: create and place components for cmp1_name, port1, cmp2_name, port2 in connections: if cmp1_name not in cmp_name_to_sref: cmp1_name, port1, cmp2_name, port2 = cmp2_name, port2, cmp1_name, port1 if cmp2_name not in cmp_name_to_sref: component, transform_name = components[cmp2_name] _ref = cmp_name_to_sref[cmp1_name] try: position = _ref.ports[port1].midpoint except: print("{} has not port {}".format(cmp1_name, port1)) sref = gen_sref(component, transform_name, port2, position) cmp_name_to_sref[cmp2_name] = sref c = Component() c.add(list(cmp_name_to_sref.values())) for port_name, (cmp_id, internal_port_name) in ports_map.items(): c.add_port(port_name, port=cmp_name_to_sref[cmp_id].ports[internal_port_name]) # Set aliases for cmp_id, ref in cmp_name_to_sref.items(): c[cmp_id] = ref c.info["components"] = cmp_name_to_sref # c.components = components # c.connections = connections # c.ports_map = ports_map # c.cells = { # cname: c for cname, (c, transf) in components.items() # } # "CP1": (cpl, "None"), # add leaf cells to netlist netlist = [] for name, (component, _) in components.items(): netlist.append( dict( name=name, rank=0, ports=list(component.ports.keys()), settings=component.settings, )) # add the compound cell to the netlist netlist.append( dict( name="component_name", rank=1, type="COMPOUND", settings={}, connections=[", ".join(i) for i in connections], ports={k: ", ".join(v) for k, v in ports_map.items()}, )) c.netlist = netlist return c
def component_from_yaml( yaml: Union[str, pathlib.Path, IO[Any]], component_factory=None, route_factory=route_factory, **kwargs, ) -> Component: """Returns a Component defined from YAML Args: yaml: YAML IO describing Component (instances, placements, routing, ports, connections) component_factory: dict of {factory_name: factory_function} route_factory: for routes kwargs: cache, pins ... to pass to all factories Returns: Component valid properties: name: name of Component instances: name component settings placements: x, y and rotations connections: between instances ports (Optional): defines ports to expose routes (Optional): defines bundles of routes ports (Optional): defines ports to expose """ yaml = io.StringIO(yaml) if isinstance(yaml, str) and "\n" in yaml else yaml component_factory = component_factory or component_factory_default conf = OmegaConf.load(yaml) for key in conf.keys(): assert key in valid_keys, f"{key} not in {list(valid_keys)}" instances = {} routes = {} name = conf.get("name") or "Unnamed" c = Component(name) placements_conf = conf.get("placements") routes_conf = conf.get("routes") ports_conf = conf.get("ports") connections_conf = conf.get("connections") for instance_name in conf.instances: instance_conf = conf.instances[instance_name] component_type = instance_conf["component"] assert ( component_type in component_factory ), f"{component_type} not in {list(component_factory.keys())}" component_settings = instance_conf["settings"] or {} component_settings.update(**kwargs) ci = component_factory[component_type](**component_settings) ref = c << ci instances[instance_name] = ref if placements_conf: placement_settings = placements_conf[instance_name] or {} for k, v in placement_settings.items(): if k not in valid_placements: raise ValueError( f"`{k}` not valid placement {valid_placements} for" f" {instance_name}" ) elif k == "rotation": ref.rotate(v, (ci.x, ci.y)) elif k == "mirror": ref.mirror((v[0], v[1]), (v[2], v[3])) else: setattr(ref, k, v) if connections_conf: for port_src_string, port_dst_string in connections_conf.items(): instance_src_name, port_src_name = port_src_string.split(",") instance_dst_name, port_dst_name = port_dst_string.split(",") instance_src_name = instance_src_name.strip() instance_dst_name = instance_dst_name.strip() port_src_name = port_src_name.strip() port_dst_name = port_dst_name.strip() assert ( instance_src_name in instances ), f"{instance_src_name} not in {list(instances.keys())}" assert ( instance_dst_name in instances ), f"{instance_dst_name} not in {list(instances.keys())}" instance_src = instances[instance_src_name] instance_dst = instances[instance_dst_name] assert port_src_name in instance_src.ports, ( f"{port_src_name} not in {list(instance_src.ports.keys())} for" f" {instance_src_name} " ) assert port_dst_name in instance_dst.ports, ( f"{port_dst_name} not in {list(instance_dst.ports.keys())} for" f" {instance_dst_name}" ) port_dst = instance_dst.ports[port_dst_name] instance_src.connect(port=port_src_name, destination=port_dst) if routes_conf: for route_type in routes_conf: route_names = [] ports1 = [] ports2 = [] routes_dict = routes_conf[route_type] assert ( route_type in route_factory ), f"route_type `{route_type}` not in route_factory {list(route_factory.keys())}" route_filter = route_factory[route_type] for port_src_string, port_dst_string in routes_dict.items(): instance_src_name, port_src_name = port_src_string.split(",") instance_dst_name, port_dst_name = port_dst_string.split(",") instance_src_name = instance_src_name.strip() instance_dst_name = instance_dst_name.strip() port_src_name = port_src_name.strip() port_dst_name = port_dst_name.strip() assert ( instance_src_name in instances ), f"{instance_src_name} not in {list(instances.keys())}" assert ( instance_dst_name in instances ), f"{instance_dst_name} not in {list(instances.keys())}" instance_in = instances[instance_src_name] instance_out = instances[instance_dst_name] assert port_src_name in instance_in.ports, ( f"{port_src_name} not in {list(instance_in.ports.keys())} for" f" {instance_src_name} " ) assert port_dst_name in instance_out.ports, ( f"{port_dst_name} not in {list(instance_out.ports.keys())} for" f" {instance_dst_name}" ) ports1.append(instance_in.ports[port_src_name]) ports2.append(instance_out.ports[port_dst_name]) route_names.append(f"{port_src_string}:{port_dst_string}") route = link_ports(ports1, ports2, route_filter=route_filter) for i, r in enumerate(route): routes[route_names[i]] = r c.add(route) if ports_conf: assert hasattr(ports_conf, "items"), f"{ports_conf} needs to be a dict" for port_name, instance_comma_port in ports_conf.items(): instance_name, instance_port_name = instance_comma_port.split(",") instance_name = instance_name.strip() instance_port_name = instance_port_name.strip() assert ( instance_name in instances ), f"{instance_name} not in {list(instances.keys())}" instance = instances[instance_name] assert instance_port_name in instance.ports, ( f"{instance_port_name} not in {list(instance.ports.keys())} for" f" {instance_name} " ) c.add_port(port_name, port=instance.ports[instance_port_name]) c.instances = instances c.routes = routes return c
def _bend_circular_heater( radius: float = 10, wg_width: float = 0.5, theta: int = -90, start_angle: int = 0, angle_resolution: float = 2.5, heater_to_wg_distance: float = 1.2, heater_width: float = 0.5, ) -> Component: """ Creates an arc of arclength ``theta`` starting at angle ``start_angle`` Args: radius width: of the waveguide theta: arc length start_angle: angle_resolution """ component = Component() wg_bend = bend_circular( radius=radius, width=wg_width, theta=theta, start_angle=start_angle, angle_resolution=angle_resolution, layer=LAYER.WG, ).ref((0, 0)) a = heater_to_wg_distance + wg_width / 2 + heater_width / 2 heater_outer = bend_circular( radius=radius + a, width=heater_width, theta=theta, start_angle=start_angle, angle_resolution=angle_resolution, layer=LAYER.HEATER, ).ref((0, -a)) heater_inner = bend_circular( radius=radius - a, width=heater_width, theta=theta, start_angle=start_angle, angle_resolution=angle_resolution, layer=LAYER.HEATER, ).ref((0, a)) component.add(wg_bend) component.add(heater_outer) component.add(heater_inner) component.absorb(wg_bend) component.absorb(heater_outer) component.absorb(heater_inner) i = 0 for device in [wg_bend, heater_outer, heater_inner]: for port in device.ports.values(): component.ports["{}".format(i)] = port i += 1 component.info["length"] = wg_bend.info["length"] component.radius = radius component.width = wg_width return component
def add_fiber_single( component: Component, grating_coupler: Callable = grating_coupler_te, layer_label: Tuple[int, int] = LAYER.LABEL, optical_io_spacing: int = 50, bend_factory: Callable = bend_circular, straight_factory: Callable = waveguide, taper_factory: Callable = taper, taper_length: float = 10.0, route_filter: Callable = connect_strip_way_points, min_input2output_spacing: int = 127, optical_routing_type: int = 2, with_align_ports: bool = True, component_name: Optional[str] = None, gc_port_name: str = "W0", **kwargs, ) -> Component: r"""returns component with grating ports and labels on each port can add align_ports reference structure Args: component: to connect grating_coupler: grating coupler instance, function or list of functions layer_label: LAYER.LABEL optical_io_spacing: SPACING_GC bend_factory: bend_circular straight_factory: waveguide fanout_length: None # if None, automatic calculation of fanout length max_y0_optical: None with_align_ports: True, adds loopback structures waveguide_separation: 4.0 bend_radius: BEND_RADIUS list_port_labels: None, adds TM labels to port indices in this list connected_port_list_ids: None # only for type 0 optical routing nb_optical_ports_lines: 1 force_manhattan: False excluded_ports: grating_indices: None routing_method: connect_strip gc_port_name: W0 optical_routing_type: None: autoselection, 0: no extension gc_rotation: -90 component_name: name of component taper_factory: taper .. code:: fiber ______ /| | | / | | | W0| | | | \ | | | | \|_|_|_ | xmin = 0 .. plot:: :include-source: import pp from pp.routing import add_fiber_array c = pp.c.crossing() cc = add_fiber_array(c) pp.plotgds(cc) """ component = component() if callable(component) else component gc = grating_coupler = (grating_coupler() if callable(grating_coupler) else grating_coupler) gc_port_to_edge = abs(gc.xmax - gc.ports[gc_port_name].midpoint[0]) port_width_gc = grating_coupler.ports[gc_port_name].width optical_ports = component.get_ports_list(port_type="optical") port_width_component = optical_ports[0].width if port_width_component != port_width_gc: component = add_tapers( component, taper_factory(length=taper_length, width1=port_width_gc, width2=port_width_component), ) component_name = component_name or component.name name = f"{component_name}_{grating_coupler.name}" elements, grating_couplers = route_fiber_single( component, component_name=component_name, optical_io_spacing=optical_io_spacing, bend_factory=bend_factory, straight_factory=straight_factory, route_filter=route_filter, grating_coupler=grating_coupler, layer_label=layer_label, optical_routing_type=optical_routing_type, min_input2output_spacing=min_input2output_spacing, gc_port_name=gc_port_name, **kwargs, ) c = Component(name=name) cr = c << component cr.rotate(90) for e in elements: c.add(e) for gc in grating_couplers: c.add(gc) for pname, p in component.ports.items(): if p.port_type != "optical": c.add_port(pname, port=p) if isinstance(grating_coupler, list): grating_couplers = [call_if_func(g) for g in grating_coupler] grating_coupler = grating_couplers[0] else: grating_coupler = call_if_func(grating_coupler) grating_couplers = [grating_coupler] if with_align_ports: length = c.ysize - 2 * gc_port_to_edge wg = c << straight_factory(length=length) wg.rotate(90) wg.xmax = (c.xmin - optical_io_spacing if abs(c.xmin) > abs(optical_io_spacing) else c.xmin - optical_io_spacing) wg.ymin = c.ymin + gc_port_to_edge gci = c << grating_coupler gco = c << grating_coupler gci.connect(gc_port_name, wg.ports["W0"]) gco.connect(gc_port_name, wg.ports["E0"]) gds_layer_label, gds_datatype_label = pd._parse_layer(layer_label) port = wg.ports["E0"] text = get_optical_text(port, grating_coupler, 0, component_name=f"loopback_{component.name}") label = pd.Label( text=text, position=port.midpoint, anchor="o", layer=gds_layer_label, texttype=gds_datatype_label, ) c.add(label) port = wg.ports["W0"] text = get_optical_text(port, grating_coupler, 1, component_name=f"loopback_{component.name}") label = pd.Label( text=text, position=port.midpoint, anchor="o", layer=gds_layer_label, texttype=gds_datatype_label, ) c.add(label) return c