def _add_pin_square( component: Component, port: Port, pin_length: float = 0.1, layer: Tuple[int, int] = LAYER.PORT, label_layer: Optional[Tuple[int, int]] = LAYER.PORT, port_margin: float = 0.0, ) -> None: """Add half out pin to a component. Args: component: port: Port pin_length: length of the pin marker for the port layer: for the pin marker label_layer: for the label port_margin: margin to port edge .. code:: _______________ | | | | | | ||| | ||| | | | | __ | |_______________| __ """ p = port a = p.orientation ca = np.cos(a * np.pi / 180) sa = np.sin(a * np.pi / 180) rot_mat = np.array([[ca, -sa], [sa, ca]]) d = p.width / 2 + port_margin dbot = np.array([pin_length / 2, -d]) dtop = np.array([pin_length / 2, d]) dbotin = np.array([-pin_length / 2, -d]) dtopin = np.array([-pin_length / 2, +d]) p0 = p.position + _rotate(dbot, rot_mat) p1 = p.position + _rotate(dtop, rot_mat) ptopin = p.position + _rotate(dtopin, rot_mat) pbotin = p.position + _rotate(dbotin, rot_mat) polygon = [p0, p1, ptopin, pbotin] component.add_polygon(polygon, layer=layer) if label_layer: component.add_label( text=str(p.name), position=p.midpoint, layer=label_layer, )
def add_pin_square_inside( component: Component, port: Port, pin_length: float = 0.1, layer: Tuple[int, int] = LAYER.PORT, layer_label: Optional[Tuple[int, int]] = LAYER.TEXT, ) -> None: """Add square pin towards the inside of the port Args: component: port: Port pin_length: length of the pin marker for the port layer: for the pin marker layer_label: for the label .. code:: _______________ | | | | | | || | || | | | | __ | |_______________| """ p = port a = p.orientation ca = np.cos(a * np.pi / 180) sa = np.sin(a * np.pi / 180) rot_mat = np.array([[ca, -sa], [sa, ca]]) d = p.width / 2 dbot = np.array([0, -d]) dtop = np.array([0, d]) dbotin = np.array([-pin_length, -d]) dtopin = np.array([-pin_length, +d]) p0 = p.position + _rotate(dbot, rot_mat) p1 = p.position + _rotate(dtop, rot_mat) ptopin = p.position + _rotate(dtopin, rot_mat) pbotin = p.position + _rotate(dbotin, rot_mat) polygon = [p0, p1, ptopin, pbotin] component.add_polygon(polygon, layer=layer) if layer_label: component.add_label( text=str(p.name), position=p.midpoint, layer=layer_label, )
def copy(D: Component, prefix: str = "", suffix: str = "_copy", cache: bool = True) -> Component: """returns a deep copy of a Component. based on phidl.geometry with CellArray support """ D_copy = Component(name=f"{prefix}{D.name}{suffix}") D_copy.info = python_copy.deepcopy(D.info) for ref in D.references: if isinstance(ref, DeviceReference): new_ref = ComponentReference( ref.parent, origin=ref.origin, rotation=ref.rotation, magnification=ref.magnification, x_reflection=ref.x_reflection, ) new_ref.owner = D_copy elif isinstance(ref, gdspy.CellArray): new_ref = CellArray( device=ref.parent, columns=ref.columns, rows=ref.rows, spacing=ref.spacing, origin=ref.origin, rotation=ref.rotation, magnification=ref.magnification, x_reflection=ref.x_reflection, ) 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 port in D.ports.values(): D_copy.add_port(port=port) 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), ) if cache: D_copy = avoid_duplicated_cells(D_copy) return D_copy
def add_settings_label( component: Component, layer_label: Layer = (66, 0), settings: Optional[Strs] = None ) -> Component: """Add a settings label to a component. Args: component: layer_label: settings: tuple or list of settings. if None, adds all changed settings """ d = ( {setting: component.get_setting(setting) for setting in settings} if settings else component.info.changed ) component.add_label(text=OmegaConf.to_yaml(d), layer=layer_label) return component
def add_instance_label( component: Component, reference: ComponentReference, instance_name: Optional[str] = None, layer: Tuple[int, int] = LAYER.LABEL_INSTANCE, ) -> None: """Adds label to a reference in a component.""" instance_name = ( instance_name or f"{reference.parent.name},{int(reference.x)},{int(reference.y)}") x = gf.snap.snap_to_grid(reference.x) y = gf.snap.snap_to_grid(reference.y) component.add_label( text=instance_name, position=(x, y), layer=layer, )
def add_settings_label( component: Component, reference: ComponentReference, layer_label: Tuple[int, int] = LAYER.LABEL_SETTINGS, ) -> None: """Add settings in label Args: componnent reference layer_label: """ settings_dict = OmegaConf.to_container(reference.settings.full) settings_string = f"settings={json.dumps(settings_dict)}" print(settings_string) if len(settings_string) > 1024: raise ValueError(f"label > 1024 characters: {settings_string}") component.add_label(position=reference.center, text=settings_string, layer=layer_label)
def add_pin_triangle( component: Component, port: Port, layer: Tuple[int, int] = LAYER.PORT, label_layer: Optional[Tuple[int, int]] = LAYER.TEXT, ) -> None: """Add triangle pin with a right angle, pointing out of the port Args: component: port: Port layer: for the pin marker label_layer: for the label """ p = port a = p.orientation ca = np.cos(a * np.pi / 180) sa = np.sin(a * np.pi / 180) rot_mat = np.array([[ca, -sa], [sa, ca]]) d = p.width / 2 dbot = np.array([0, -d]) dtop = np.array([0, d]) dtip = np.array([d, 0]) p0 = p.position + _rotate(dbot, rot_mat) p1 = p.position + _rotate(dtop, rot_mat) ptip = p.position + _rotate(dtip, rot_mat) polygon = [p0, p1, ptip] component.add_polygon(polygon, layer=layer) if label_layer: component.add_label( text=str(p.name), position=ptip, layer=label_layer, )
def add_pin_triangle( component: Component, port: Port, layer: Tuple[int, int] = LAYER.PORT, layer_label: Optional[Tuple[int, int]] = LAYER.TEXT, ) -> None: """Add triangle pin with a right angle, pointing out of the port Args: component: port: Port layer: for the pin marker layer_label: for the label """ polygon, ptip = get_pin_triangle_polygon_tip(port=port) component.add_polygon(polygon, layer=layer) if layer_label: component.add_label( text=str(port.name), position=ptip, layer=layer_label, )
def import_gds( gdspath: Union[str, Path], cellname: Optional[str] = None, flatten: bool = False, snap_to_grid_nm: Optional[int] = None, decorator: Optional[Callable] = None, **kwargs, ) -> Component: """Returns a Componenent from a GDS file. Adapted from phidl/geometry.py Args: gdspath: path of GDS file cellname: cell of the name to import (None) imports top cell flatten: if True returns flattened (no hierarchy) snap_to_grid_nm: snap to different nm grid (does not snap if False) **kwargs """ gdspath = Path(gdspath) if not gdspath.exists(): raise FileNotFoundError(f"No file {gdspath} found") gdsii_lib = gdspy.GdsLibrary() gdsii_lib.read_gds(str(gdspath)) top_level_cells = gdsii_lib.top_level() cellnames = [c.name for c in top_level_cells] if cellname is not None: if cellname not in gdsii_lib.cells: raise ValueError( f"cell {cellname} is not in file {gdspath} with cells {cellnames}" ) topcell = gdsii_lib.cells[cellname] elif cellname is None and len(top_level_cells) == 1: topcell = top_level_cells[0] elif cellname is None and len(top_level_cells) > 1: raise ValueError( f"import_gds() There are multiple top-level cells in {gdspath}, " f"you must specify `cellname` to select of one of them among {cellnames}" ) if flatten: component = Component() polygons = topcell.get_polygons(by_spec=True) for layer_in_gds, polys in polygons.items(): component.add_polygon(polys, layer=layer_in_gds) else: D_list = [] c2dmap = {} for cell in gdsii_lib.cells.values(): D = Component(name=cell.name) D.polygons = cell.polygons D.references = cell.references D.name = cell.name for label in cell.labels: rotation = label.rotation if rotation is None: rotation = 0 label_ref = D.add_label( text=label.text, position=np.asfarray(label.position), magnification=label.magnification, rotation=rotation * 180 / np.pi, layer=(label.layer, label.texttype), ) label_ref.anchor = label.anchor c2dmap.update({cell: D}) D_list += [D] for D in D_list: # First convert each reference so it points to the right Device converted_references = [] for e in D.references: ref_device = c2dmap[e.ref_cell] if isinstance(e, gdspy.CellReference): dr = DeviceReference( device=ref_device, origin=e.origin, rotation=e.rotation, magnification=e.magnification, x_reflection=e.x_reflection, ) dr.owner = D converted_references.append(dr) elif isinstance(e, gdspy.CellArray): dr = CellArray( device=ref_device, columns=e.columns, rows=e.rows, spacing=e.spacing, origin=e.origin, rotation=e.rotation, magnification=e.magnification, x_reflection=e.x_reflection, ) dr.owner = D converted_references.append(dr) D.references = converted_references # Next convert each Polygon # temp_polygons = list(D.polygons) # D.polygons = [] # for p in temp_polygons: # D.add_polygon(p) # Next convert each Polygon temp_polygons = list(D.polygons) D.polygons = [] for p in temp_polygons: if snap_to_grid_nm: points_on_grid = snap_to_grid(p.polygons[0], nm=snap_to_grid_nm) p = gdspy.Polygon( points_on_grid, layer=p.layers[0], datatype=p.datatypes[0] ) D.add_polygon(p) component = c2dmap[topcell] cast(Component, component) for key, value in kwargs.items(): setattr(component, key, value) if decorator: decorator(component) component._autoname = False return component
def add_grating_couplers_with_loopback_fiber_single( component: Component, grating_coupler: ComponentFactory = grating_coupler_te, layer_label: Tuple[int, int] = (200, 0), gc_port_name: str = "o1", get_input_labels_function: Callable[..., List[Label]] = get_input_labels, get_input_label_text_loopback_function: Callable = get_input_label_text_loopback, select_ports: Callable = select_ports_optical, with_loopback: bool = True, cross_section: CrossSectionFactory = strip, component_name: Optional[str] = None, fiber_spacing: float = 50.0, loopback_xspacing: float = 5.0, straight: ComponentFactory = straight_function, rotation: int = 90, ) -> Component: """ Returns component with all ports terminated with grating couplers Args: component: grating_coupler: layer_label: gc_port_name: get_input_label_text_loopback_function: with_loopback: adds a reference loopback rotation: 90 for North South devices, 0 for East-West """ c = Component() c.component = component c.add_ref(component) grating_coupler = ( grating_coupler() if callable(grating_coupler) else grating_coupler ) component_name = component_name or component.info_child.name io_gratings = [] optical_ports = select_ports(component.ports) optical_ports = list(optical_ports.values()) for port in optical_ports: gc_ref = grating_coupler.ref() gc_port = gc_ref.ports[gc_port_name] gc_ref.connect(gc_port, port) io_gratings.append(gc_ref) c.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, ) c.add(labels) p2 = optical_ports[0] p1 = optical_ports[-1] if with_loopback: if rotation in [0, 180]: length = abs(p2.x - p1.x) wg = c << straight(length=length, cross_section=cross_section) wg.rotate(rotation) wg.xmin = p2.x wg.ymin = c.ymax + grating_coupler.ysize / 2 + loopback_xspacing else: length = abs(p2.y - p1.y) wg = c << straight(length=length, cross_section=cross_section) wg.rotate(rotation) wg.ymin = p1.y wg.xmin = c.xmax + grating_coupler.ysize / 2 + loopback_xspacing gci = c << grating_coupler gco = c << grating_coupler gci.connect(gc_port_name, wg.ports["o1"]) gco.connect(gc_port_name, wg.ports["o2"]) port = wg.ports["o2"] text = get_input_label_text_loopback_function( port=port, gc=grating_coupler, gc_index=0, component_name=component_name ) c.add_label( text=text, position=port.midpoint, anchor="o", layer=layer_label, ) port = wg.ports["o1"] text = get_input_label_text_loopback_function( port=port, gc=grating_coupler, gc_index=1, component_name=component_name ) c.add_label( text=text, position=port.midpoint, anchor="o", layer=layer_label, ) c.copy_child_info(component) return c
def import_gds( gdspath: Union[str, Path], cellname: Optional[str] = None, flatten: bool = False, snap_to_grid_nm: Optional[int] = None, name: Optional[str] = None, decorator: Optional[Callable] = None, gdsdir: Optional[Union[str, Path]] = None, **kwargs, ) -> Component: """Returns a Componenent from a GDS file. Adapted from phidl/geometry.py if any cell names are found on the component CACHE we append a $ with a number to the name Args: gdspath: path of GDS file. cellname: cell of the name to import (None) imports top cell. flatten: if True returns flattened (no hierarchy) snap_to_grid_nm: snap to different nm grid (does not snap if False) name: Optional name. Over-rides the default imported name. decorator: function to apply over the imported gds. gdsdir: optional GDS directory. kwargs: settings for the imported component (polarization, wavelength ...). """ gdspath = Path(gdsdir) / Path(gdspath) if gdsdir else Path(gdspath) if not gdspath.exists(): raise FileNotFoundError(f"No file {gdspath!r} found") metadata_filepath = gdspath.with_suffix(".yml") gdsii_lib = gdspy.GdsLibrary() gdsii_lib.read_gds(str(gdspath)) top_level_cells = gdsii_lib.top_level() cellnames = [c.name for c in top_level_cells] if cellname is not None: if cellname not in gdsii_lib.cells: raise ValueError( f"cell {cellname} is not in file {gdspath} with cells {cellnames}" ) topcell = gdsii_lib.cells[cellname] elif cellname is None and len(top_level_cells) == 1: topcell = top_level_cells[0] elif cellname is None and len(top_level_cells) > 1: raise ValueError( f"import_gds() There are multiple top-level cells in {gdspath!r}, " f"you must specify `cellname` to select of one of them among {cellnames}" ) if name: if name in CACHE or name in CACHE_IMPORTED_CELLS: raise ValueError( f"name = {name!r} already on cache. " "Please, choose a different name or set name = None. ") else: topcell.name = name if flatten: component = Component(name=name or cellname or cellnames[0]) polygons = topcell.get_polygons(by_spec=True) for layer_in_gds, polys in polygons.items(): component.add_polygon(polys, layer=layer_in_gds) component = avoid_duplicated_cells(component) else: D_list = [] cell_to_device = {} for c in gdsii_lib.cells.values(): D = Component(name=c.name) D.polygons = c.polygons D.references = c.references D.name = c.name for label in c.labels: rotation = label.rotation if rotation is None: rotation = 0 label_ref = D.add_label( text=label.text, position=np.asfarray(label.position), magnification=label.magnification, rotation=rotation * 180 / np.pi, layer=(label.layer, label.texttype), ) label_ref.anchor = label.anchor D = avoid_duplicated_cells(D) D.unlock() cell_to_device.update({c: D}) D_list += [D] for D in D_list: # First convert each reference so it points to the right Device converted_references = [] for e in D.references: ref_device = cell_to_device[e.ref_cell] if isinstance(e, gdspy.CellReference): dr = DeviceReference( device=ref_device, origin=e.origin, rotation=e.rotation, magnification=e.magnification, x_reflection=e.x_reflection, ) dr.owner = D converted_references.append(dr) elif isinstance(e, gdspy.CellArray): dr = CellArray( device=ref_device, columns=e.columns, rows=e.rows, spacing=e.spacing, origin=e.origin, rotation=e.rotation, magnification=e.magnification, x_reflection=e.x_reflection, ) dr.owner = D converted_references.append(dr) D.references = converted_references # Next convert each Polygon # temp_polygons = list(D.polygons) # D.polygons = [] # for p in temp_polygons: # D.add_polygon(p) # Next convert each Polygon temp_polygons = list(D.polygons) D.polygons = [] for p in temp_polygons: if snap_to_grid_nm: points_on_grid = snap_to_grid(p.polygons[0], nm=snap_to_grid_nm) p = gdspy.Polygon(points_on_grid, layer=p.layers[0], datatype=p.datatypes[0]) D.add_polygon(p) component = cell_to_device[topcell] cast(Component, component) name = name or component.name component.name = name if metadata_filepath.exists(): logger.info(f"Read YAML metadata from {metadata_filepath}") metadata = OmegaConf.load(metadata_filepath) for port_name, port in metadata.ports.items(): if port_name not in component.ports: component.add_port( name=port_name, midpoint=port.midpoint, width=port.width, orientation=port.orientation, layer=port.layer, port_type=port.port_type, ) component.info = metadata.info component.info.update(**kwargs) component.name = name component.info.name = name if decorator: component_new = decorator(component) component = component_new or component if flatten: component.flatten() component.lock() return component
def add_fiber_array( component: Component, grating_coupler: Component = grating_coupler_te, straight: ComponentFactory = straight, bend: ComponentFactory = bend_euler, gc_port_name: str = "o1", gc_port_labels: Optional[Tuple[str, ...]] = None, component_name: Optional[str] = None, select_ports: Callable = select_ports_optical, cross_section: CrossSectionFactory = strip, get_input_labels_function: Optional[Callable] = get_input_labels, layer_label: Optional[Tuple[int, int]] = (66, 0), **kwargs, ) -> Component: """Returns component with optical IO (tapers, south routes and grating_couplers). Args: component: to connect grating_coupler: grating coupler instance, function or list of functions bend: bend_circular gc_port_name: grating coupler input port name 'W0' component_name: for the label taper: taper function name or dict get_input_labels_function: function to get input labels for grating couplers get_input_label_text_loopback_function: function to get input label test get_input_label_text_function straight: straight fanout_length: None # if None, automatic calculation of fanout length max_y0_optical: None with_loopback: True, adds loopback structures straight_separation: 4.0 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_straight: None routing_method: get_route optical_routing_type: None: auto, 0: no extension, 1: standard, 2: check gc_rotation: -90 layer_label: LAYER.LABEL input_port_indexes: [0] .. plot:: :include-source: import gdsfactory as gf gf.config.set_plot_options(show_subports=False) c = gf.components.crossing() cc = gf.routing.add_fiber_array( component=c, optical_routing_type=2, grating_coupler=gf.components.grating_coupler_elliptical_te, with_loopback=False ) cc.plot() """ get_input_labels_function = None if gc_port_labels else get_input_labels_function component = gf.call_if_func(component) grating_coupler = (grating_coupler() if callable(grating_coupler) else grating_coupler) if not component.ports: return component if isinstance(grating_coupler, list): gc = grating_coupler[0] else: gc = grating_coupler gc = gf.call_if_func(gc) if gc_port_name not in gc.ports: raise ValueError( f"gc_port_name={gc_port_name} not in {gc.ports.keys()}") component_name = component_name or component.get_parent_name() component_new = Component() component_new.component = component optical_ports = select_ports(component.ports) optical_ports_names = list(optical_ports.keys()) if not optical_ports: return component elements, io_gratings_lines, ports = route_fiber_array( component=component, grating_coupler=grating_coupler, bend=bend, straight=straight, gc_port_name=gc_port_name, component_name=component_name, cross_section=cross_section, select_ports=select_ports, get_input_labels_function=get_input_labels_function, layer_label=layer_label, **kwargs, ) if len(elements) == 0: return component for e in elements: component_new.add(e) for io_gratings in io_gratings_lines: component_new.add(io_gratings) component_new.add_ref(component) for pname, p in component.ports.items(): if p.name not in optical_ports_names: component_new.add_port(pname, port=p) ports = sort_ports_x(ports) if gc_port_labels: for gc_port_label, port in zip(gc_port_labels, ports): component_new.add_label(text=gc_port_label, layer=layer_label, position=port.midpoint) for i, io_row in enumerate(io_gratings_lines): for j, io in enumerate(io_row): ports = io.get_ports_list(prefix="vertical") if ports: port = ports[0] component_new.add_port(f"{port.name}_{i}{j}", port=port) component_new.copy_child_info(component) return component_new
def add_fiber_single( component: ComponentOrFactory, grating_coupler: ComponentFactory = grating_coupler_te, layer_label: Tuple[int, int] = TECH.layer_label, fiber_spacing: float = TECH.fiber_spacing, bend: ComponentFactory = bend_circular, straight: ComponentFactory = straight, route_filter: Callable = get_route_from_waypoints, min_input_to_output_spacing: float = 200.0, optical_routing_type: int = 2, with_loopback: bool = True, component_name: Optional[str] = None, gc_port_name: str = "o1", get_input_label_text_loopback_function: Callable = get_input_label_text_loopback, get_input_label_text_function: Callable = get_input_label_text, select_ports: Callable = select_ports_optical, cross_section: CrossSectionFactory = strip, **kwargs, ) -> Component: r"""Returns component with grating ports and labels on each port. Can add loopback reference structure next to it. Args: component: to connect grating_coupler: grating coupler instance, function or list of functions layer_label: for test and measurement label fiber_spacing: between outputs bend: bend_circular straight: straight route_filter: max_y0_optical: None with_loopback: True, adds loopback structures straight_separation: 4.0 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: get_route gc_port_name: W0 get_input_labels_function: function to get input labels for grating couplers optical_routing_type: None: autoselection, 0: no extension gc_rotation: -90 component_name: name of component cross_section: **kwargs: cross_section settings .. code:: fiber ______ /| | | / | | | W0| | | | \ | | | | \|_|_|_ | xmin = 0 .. plot:: :include-source: import gdsfactory as gf c = gf.components.crossing() cc = gf.routing.add_fiber_single( component=c, optical_routing_type=0, grating_coupler=gf.components.grating_coupler_elliptical_te, ) cc.plot() """ component = component() if callable(component) else component optical_ports = select_ports(component.ports) optical_ports = list(optical_ports.values()) optical_port_names = [p.name for p in optical_ports] if not optical_ports: raise ValueError(f"No ports for {component.name}") component = component() if callable(component) else component component_name = component_name or component.get_parent_name() gc = grating_coupler = (grating_coupler() if callable(grating_coupler) else grating_coupler) if gc_port_name not in gc.ports: raise ValueError(f"{gc_port_name} not in {list(gc.ports.keys())}") gc_port_to_edge = abs(gc.xmax - gc.ports[gc_port_name].midpoint[0]) c = Component() c.component = component cr = c << component cr.rotate(90) for port in cr.ports.values(): if port.name not in optical_port_names: c.add_port(name=port.name, port=port) if (len(optical_ports) == 2 and abs(optical_ports[0].x - optical_ports[1].x) > min_input_to_output_spacing): grating_coupler = call_if_func(grating_coupler) grating_couplers = [] for port in cr.ports.values(): if port.name in optical_port_names: gc_ref = grating_coupler.ref() gc_ref.connect(gc_port_name, port) grating_couplers.append(gc_ref) elements = get_input_labels( io_gratings=grating_couplers, ordered_ports=list(cr.ports.values()), component_name=component_name, layer_label=layer_label, gc_port_name=gc_port_name, get_input_label_text_function=get_input_label_text_function, ) else: elements, grating_couplers = route_fiber_single( component, fiber_spacing=fiber_spacing, bend=bend, straight=straight, route_filter=route_filter, grating_coupler=grating_coupler, layer_label=layer_label, optical_routing_type=optical_routing_type, min_input_to_output_spacing=min_input_to_output_spacing, gc_port_name=gc_port_name, component_name=component_name, cross_section=cross_section, select_ports=select_ports, **kwargs, ) for e in elements: c.add(e) for gc in grating_couplers: c.add(gc) for i, io_row in enumerate(grating_couplers): if isinstance(io_row, list): for j, io in enumerate(io_row): ports = io.get_ports_list(prefix="vertical") if ports: port = ports[0] c.add_port(f"{port.name}_{i}{j}", port=port) else: ports = io_row.get_ports_list(prefix="vertical") if ports: port = ports[0] c.add_port(f"{port.name}_{i}", port=port) 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_loopback: length = c.ysize - 2 * gc_port_to_edge wg = c << straight( length=length, cross_section=cross_section, **kwargs) wg.rotate(90) wg.xmax = (c.xmin - fiber_spacing if abs(c.xmin) > abs(fiber_spacing) else c.xmin - fiber_spacing) wg.ymin = c.ymin + gc_port_to_edge gci = c << grating_coupler gco = c << grating_coupler gci.connect(gc_port_name, wg.ports["o1"]) gco.connect(gc_port_name, wg.ports["o2"]) port = wg.ports["o2"] text = get_input_label_text_loopback_function( port=port, gc=grating_coupler, gc_index=0, component_name=component_name) c.add_label( text=text, position=port.midpoint, anchor="o", layer=layer_label, ) port = wg.ports["o1"] text = get_input_label_text_loopback_function( port=port, gc=grating_coupler, gc_index=1, component_name=component_name) c.add_label( text=text, position=port.midpoint, anchor="o", layer=layer_label, ) c.copy_child_info(component) return c
def add_pin_square_double( component: Component, port: Port, pin_length: float = 0.1, layer: Tuple[int, int] = LAYER.PORT, layer_label: Optional[Tuple[int, int]] = LAYER.TEXT, ) -> None: """Add two square pins: one inside with label, one outside Args: component: port: Port pin_length: length of the pin marker for the port layer: for the pin marker layer_label: for the label .. code:: _______________ | | | | | | ||| | ||| | | | | __ | |_______________| __ """ p = port a = p.orientation ca = np.cos(a * np.pi / 180) sa = np.sin(a * np.pi / 180) rot_mat = np.array([[ca, -sa], [sa, ca]]) # outer square d = p.width / 2 dbot = np.array([0, -d]) dtop = np.array([0, d]) dbotin = np.array([pin_length / 2, -d]) dtopin = np.array([pin_length / 2, +d]) p0 = p.position + _rotate(dbot, rot_mat) p1 = p.position + _rotate(dtop, rot_mat) ptopin = p.position + _rotate(dtopin, rot_mat) pbotin = p.position + _rotate(dbotin, rot_mat) polygon = [p0, p1, ptopin, pbotin] component.add_polygon(polygon, layer=layer) # inner square d = p.width / 2 dbot = np.array([0, -d]) dtop = np.array([0, d]) dbotin = np.array([-pin_length / 2, -d]) dtopin = np.array([-pin_length / 2, +d]) p0 = p.position + _rotate(dbot, rot_mat) p1 = p.position + _rotate(dtop, rot_mat) ptopin = p.position + _rotate(dtopin, rot_mat) pbotin = p.position + _rotate(dbotin, rot_mat) polygon = [p0, p1, ptopin, pbotin] component.add_polygon(polygon, layer=layer) x = (p0[0] + ptopin[0]) / 2 y = (ptopin[1] + pbotin[1]) / 2 if layer_label: component.add_label( text=str(p.name), position=(x, y), layer=layer_label, )