def staircase( bend90: ComponentOrFactory = bend_euler, length_v: float = 5.0, length_h: float = 5.0, rows: int = 4, straight: ComponentFactory = straight, ) -> Component: bend90 = gf.call_if_func(bend90) wgh = straight(length=length_h, width=bend90.ports["o1"].width) wgv = straight(length=length_v, width=bend90.ports["o1"].width) # Define a map between symbols and (component, input port, output port) symbol_to_component = { "A": (bend90, "o1", "o2"), "B": (bend90, "o2", "o1"), "-": (wgh, "o1", "o2"), "|": (wgv, "o1", "o2"), } # Generate the sequence of staircases s = "-A|B" * rows + "-" c = component_sequence(sequence=s, symbol_to_component=symbol_to_component, start_orientation=0) c.info.n_bends = 2 * rows return c
def test_cutback_phase(straight_length: float = 100.0, bend_radius: float = 12.0, n: int = 2) -> Component: """Modulator sections connected by bends""" # Define sub components bend180 = gf.components.bend_circular180(radius=bend_radius) pm_wg = gf.c.straight_pin(length=straight_length, taper=None) wg_short = straight(length=1.0) wg_short2 = straight(length=2.0) wg_heater = gf.c.straight_pin(length=10.0, taper=None) taper = taper_strip_to_ridge() # Define a map between symbols and (component, input port, output port) symbol_to_component = { "I": (taper, "o1", "o2"), "O": (taper, "o2", "o1"), "S": (wg_short, "o1", "o2"), "P": (pm_wg, "o1", "o2"), "A": (bend180, "o1", "o2"), "B": (bend180, "o2", "o1"), "H": (wg_heater, "o1", "o2"), "-": (wg_short2, "o1", "o2"), } # Generate a sequence # This is simply a chain of characters. Each of them represents a component # with a given input and and a given output repeated_sequence = "SIPOSASIPOSB" heater_seq = "-H-H-H-H-" sequence = repeated_sequence * n + "SIPO" + heater_seq component = component_sequence(sequence=sequence, symbol_to_component=symbol_to_component) return component
def cutback_bend180( bend180: ComponentOrFactory = bend_euler180, straight_length: float = 5.0, rows: int = 6, columns: int = 6, spacing: int = 3, straight: ComponentFactory = straight, ) -> Component: """ .. code:: _ _| |_ this is a row _ this is a column """ bend180 = gf.call_if_func(bend180) straightx = straight(length=straight_length, width=bend180.ports["o1"].width) wg_vertical = straight( length=2 * bend180.size_info.width + straight_length + spacing, width=bend180.ports["o1"].width, ) # Define a map between symbols and (component, input port, output port) symbol_to_component = { "D": (bend180, "o1", "o2"), "C": (bend180, "o2", "o1"), "-": (straightx, "o1", "o2"), "|": (wg_vertical, "o1", "o2"), } # Generate the sequence of staircases s = "" for i in range(columns): if i % 2 == 0: # even row s += "D-C-" * rows + "|" else: s += "C-D-" * rows + "|" s = s[:-1] # Create the component from the sequence c = component_sequence(sequence=s, symbol_to_component=symbol_to_component, start_orientation=0) c.info.n_bends = rows * columns * 2 + columns * 2 - 2 return c
def cutback_bend90( bend90: ComponentOrFactory = bend_euler, straight_length: float = 5.0, rows: int = 6, columns: int = 6, spacing: int = 5, straight: ComponentFactory = straight, ) -> Component: """ .. code:: _ |_| | """ bend90 = gf.call_if_func(bend90) straightx = straight(length=straight_length, width=bend90.ports["o1"].width) straight_length = 2 * _get_bend_size(bend90) + spacing + straight_length straighty = straight( length=straight_length, width=bend90.ports["o1"].width, ) # Define a map between symbols and (component, input port, output port) symbol_to_component = { "A": (bend90, "o1", "o2"), "B": (bend90, "o2", "o1"), "-": (straightx, "o1", "o2"), "|": (straighty, "o1", "o2"), } # Generate the sequence of staircases s = "" for i in range(columns): if i % 2 == 0: # even row s += "A-A-B-B-" * rows + "|" else: s += "B-B-A-A-" * rows + "|" s = s[:-1] # Create the component from the sequence c = component_sequence(sequence=s, symbol_to_component=symbol_to_component, start_orientation=0) c.info.n_bends = rows * columns * 4 return c
def cutback_component( component: ComponentFactory = taper_0p5_to_3_l36, cols: int = 4, rows: int = 5, radius: float = 5.0, port1: str = "o1", port2: str = "o2", bend180: ComponentFactory = bend_euler180, straight: ComponentFactory = straight, ) -> Component: """Returns a daisy chain of components for measuring their loss. Args: component: for cutback cols rows radius: for bend port1: name of first optical port port2: name of second optical port bend180: ubend straight: waveguide function to connect both sides """ component = component() if callable(component) else component bendu = bend180(radius=radius) straight_component = straight() # Define a map between symbols and (component, input port, output port) symbol_to_component = { "A": (component, port1, port2), "B": (component, port2, port1), "D": (bendu, "o1", "o2"), "C": (bendu, "o2", "o1"), "-": (straight_component, "o1", "o2"), "_": (straight_component, "o2", "o1"), } # Generate the sequence of staircases s = "" for i in range(rows): s += "AB" * cols s += "D" if i % 2 == 0 else "C" s = s[:-1] s += "-_" for i in range(rows): s += "AB" * cols s += "D" if (i + rows) % 2 == 0 else "C" s = s[:-1] # Create the component from the sequence c = component_sequence(sequence=s, symbol_to_component=symbol_to_component) n = len(s) - 2 c.copy_child_info(component) c.info["components"] = n c.info["parent_name"] = f"loopback_{component.get_parent_name()}_{n}" return c
def cutback_component_mirror( component: ComponentFactory = component_flipped, cols: int = 4, rows: int = 5, radius: int = 10, port1: str = "o2", port2: str = "o1", bend180: ComponentFactory = bend_euler180, straight: ComponentFactory = straight_long, ) -> Component: """Returns a daisy chain of components for measuring their loss. Flips component. Useful when 'o2' is the port that you want to route to Args: component: for cutback cols rows radius: for bend port1: name of first optical port port2: name of second optical port bend180: ubend straight: waveguide function to connect both sides """ component = component() if callable(component) else component bendu = bend180(radius=radius) straight_component = straight() # Define a map between symbols and (component, input port, output port) symbol_to_component = { "A": (component, port1, port2), "B": (component, port2, port1), "D": (bendu, "o1", "o2"), "C": (bendu, "o2", "o1"), "-": (straight_component, "o1", "o2"), "_": (straight_component, "o2", "o1"), } s = "" for i in range(rows): s += "AB" * cols s += "C" if i % 2 == 0 else "D" s = s[:-1] s += "-_" for i in range(rows): s += "AB" * cols s += "D" if (i + rows + 1) % 2 == 0 else "C" s = s[:-1] c = component_sequence(sequence=s, symbol_to_component=symbol_to_component) n = len(s) - 2 c.copy_child_info(component) c.info["components"] = n c.info["parent_name"] = f"loopback_{component.get_parent_name()}_{n}" return c
def cdsem_uturn( width: float = 0.5, radius: float = 10.0, wg_length: float = LINE_LENGTH, straight: ComponentFactory = straight, bend90: ComponentFactory = bend_circular, layer: Tuple[int, int] = LAYER.WG, layers_cladding: List[Tuple[int, int]] = None, cross_section: CrossSectionFactory = strip, pixel_size: float = 1.0, ) -> Component: """ Args: width: of the line cladding_offset: radius: bend radius wg_length """ c = Component() r = radius cross_section = partial(cross_section, width=width, layer=layer) if wg_length is None: wg_length = 2 * r bend90 = bend90(cross_section=cross_section, radius=r) wg = straight( cross_section=cross_section, length=wg_length, ) # Add the U-turn on straight layer b1 = c.add_ref(bend90) b2 = c.add_ref(bend90) b2.connect("o2", b1.ports["o1"]) wg1 = c.add_ref(wg) wg1.connect("o1", b1.ports["o2"]) wg2 = c.add_ref(wg) wg2.connect("o1", b2.ports["o1"]) label = c << manhattan_text( text=str(int(width * 1e3)), size=pixel_size, layer=layer) label.ymax = b2.ymin - 5 label.x = 0 b1.rotate(90) b2.rotate(90) wg1.rotate(90) wg2.rotate(90) label.rotate(90) return c
def coupler90(gap: float = 0.2, radius: float = 10.0, bend: ComponentFactory = bend_euler, cross_section: CrossSectionFactory = strip, **kwargs) -> Component: r"""straight coupled to a bend. Args: gap: um radius: um straight: for straight bend: for bend cross_section: kwargs: cross_section settings .. code:: 3 | / / 2_/ 1____4 """ c = Component() x = cross_section(radius=radius, **kwargs) bend90 = (bend(cross_section=cross_section, radius=radius, **kwargs) if callable(bend) else bend) bend_ref = c << bend90 straight_component = (straight( cross_section=cross_section, length=bend90.ports["o2"].midpoint[0] - bend90.ports["o1"].midpoint[0], **kwargs) if callable(straight) else straight) wg_ref = c << straight_component width = x.info["width"] pbw = bend_ref.ports["o1"] bend_ref.movey(pbw.midpoint[1] + gap + width) c.absorb(wg_ref) c.absorb(bend_ref) c.add_port("o1", port=wg_ref.ports["o1"]) c.add_port("o4", port=wg_ref.ports["o2"]) c.add_port("o2", port=bend_ref.ports["o1"]) c.add_port("o3", port=bend_ref.ports["o2"]) return c
def test_add_keepout() -> None: from gdsfactory.components.straight import straight c = straight() polygons = len(c.get_polygons()) target_layers = [LAYER.WG] keepout_layers = [LAYER.NO_TILE_SI] print(len(c.get_polygons())) assert len(c.get_polygons()) == polygons c = add_keepout(component=c, target_layers=target_layers, keepout_layers=keepout_layers) # print(len(c.get_polygons())) assert len(c.get_polygons()) == polygons + 1
def cdsem_straight_all( straight: ComponentFactory = straight, cross_section: CrossSectionFactory = strip, layer: Tuple[int, int] = LAYER.WG, layers_cladding: List[Tuple[int, int]] = None, widths: Tuple[float, ...] = (0.4, 0.45, 0.5, 0.6, 0.8, 1.0), pixel_size: float = 1.0, length: float = LINE_LENGTH, spacing: float = 4.5, ) -> Component: c = Component() for width in widths: cross_section = partial(cross_section, width=width, layer=layer) c.add_ref(straight(length=length, cross_section=cross_section)) c.distribute(direction="y", spacing=spacing) return c
def bend_straight_bend(straight_length: float = 10.0, angle: int = 90, p: float = 0.5, with_arc_floorplan: bool = True, npoints: int = 720, direction: str = "ccw", with_cladding_box: bool = True, cross_section: CrossSectionOrFactory = strip, **kwargs) -> Component: """Sbend made of 2 euler bends and straight section in between. Args: straight_length: angle: total angle of the curve p: Proportion of the curve that is an Euler curve with_arc_floorplan: If False: `radius` is the minimum radius of curvature If True: The curve scales such that the endpoints match a bend_circular with parameters `radius` and `angle` npoints: Number of points used per 360 degrees direction: cw (clock-wise) or ccw (counter clock-wise) with_cladding_box: to avoid DRC acute angle errors in cladding cross_section: kwargs: cross_section settings """ c = Component() b = bend_euler(angle=angle, p=p, with_arc_floorplan=with_arc_floorplan, npoints=npoints, direction=direction, with_cladding_box=with_cladding_box, cross_section=cross_section, **kwargs) b1 = c.add_ref(b) b2 = c.add_ref(b) s = c << straight( length=straight_length, cross_section=cross_section, **kwargs) s.connect("o1", b1.ports["o2"]) b2.mirror() b2.connect("o1", s.ports["o2"]) c.add_port("o1", port=b1.ports["o1"]) c.add_port("o2", port=b2.ports["o2"]) return c
def cutback_bend( bend90: ComponentOrFactory = bend_euler, straight_length: float = 5.0, rows: int = 6, columns: int = 5, ): """Deprecated! use cutback_bend90 instead! .. code:: this is a column _ _| _| _ this is a row """ bend90 = gf.call_if_func(bend90) straightx = straight(length=straight_length, width=bend90.ports["o1"].width) # Define a map between symbols and (component, input port, output port) symbol_to_component = { "A": (bend90, "o1", "o2"), "B": (bend90, "o2", "o1"), "S": (straightx, "o1", "o2"), } # Generate the sequence of staircases s = "" for i in range(columns): s += "ASBS" * rows s += "ASAS" if i % 2 == 0 else "BSBS" s = s[:-4] c = component_sequence(sequence=s, symbol_to_component=symbol_to_component, start_orientation=90) c.info.n_bends = rows * columns * 2 + columns * 2 - 2 return c
def cdsem_straight_density( width: float = 0.372, trench_width: float = 0.304, x: float = LINE_LENGTH, y: float = 50.0, margin: float = 2.0, label: str = "", straight: ComponentFactory = straight, layer: Tuple[int, int] = LAYER.WG, layers_cladding: Optional[Tuple[Layer, ...]] = None, cross_section: CrossSectionFactory = strip, pixel_size: float = 1.0, ) -> Component: """Horizontal grating etch lines Args: width: width trench_width: trench_width """ c = Component() period = width + trench_width n_o_lines = int((y - 2 * margin) / period) length = x - 2 * margin cross_section = partial(cross_section, width=width, layer=layer) tooth = straight(length=length, cross_section=cross_section) for i in range(n_o_lines): tooth_ref = c.add_ref(tooth) tooth_ref.movey((-n_o_lines / 2 + 0.5 + i) * period) c.absorb(tooth_ref) marker_label = manhattan_text( text=f"{label}", size=pixel_size, layer=layer, ) _marker_label = c.add_ref(marker_label) _marker_label.move((length + 3, 10.0)) c.absorb(_marker_label) return c
def get_routes_straight( ports: Union[List[Port], Dict[str, Port]], straight: ComponentOrFactory = straight, **kwargs, ) -> Routes: """Returns routes made by 180 degree straights. Args: ports: List or dict of ports straight: function for straight **kwargs: waveguide settings """ ports = list(ports.values()) if isinstance(ports, dict) else ports straight = straight(**kwargs) references = [straight.ref() for port in ports] references = [ ref.connect("o1", port) for port, ref in zip(ports, references) ] ports = [ref.ports["o2"] for i, ref in enumerate(references)] lengths = [straight.info.length] * len(ports) return Routes(references=references, ports=ports, lengths=lengths)
def test_cutback_pn() -> Component: # Define subcomponents bend_radius = 10.0 bend180 = bend_circular(radius=bend_radius, angle=180) wg = straight(length=5.0) wg_heater = straight_pn(length=50.0) # Define a map between symbols and (component, input port, output port) symbol_to_component = { "A": (bend180, "o1", "o2"), "B": (bend180, "o2", "o1"), "H": (wg_heater, "o1", "o2"), "-": (wg, "o1", "o2"), } # Generate a sequence # This is simply a chain of characters. Each of them represents a component # with a given input and a given output sequence = "AB-H-H-H-H-BA" component = component_sequence(sequence=sequence, symbol_to_component=symbol_to_component) return component
def array_with_via( component: ComponentOrFactory = pad, columns: int = 3, spacing: float = 150.0, via_spacing: float = 10.0, straight_length: float = 60.0, cross_section: Optional[CrossSectionFactory] = metal2, contact: ComponentFactory = contact_factory, contact_dy: float = 0, port_orientation: int = 180, port_offset: Optional[Float2] = None, **kwargs, ) -> Component: """Returns an array of components in X axis with fanout waveguides facing west Args: component: to replicate in the array columns: number of components spacing: for the array via_spacing: for fanout straight_length: lenght of the straight at the end waveguide: waveguide definition cross_section: contact: contact_dy: contact offset port_orientation: 180: facing west port_offset: Optional port movement kwargs: cross_section settings """ c = Component() component = component() if callable(component) else component contact = contact() for col in range(columns): ref = component.ref() ref.x = col * spacing c.add(ref) if port_orientation == 180: xlength = col * spacing + straight_length elif port_orientation == 0: xlength = columns * spacing - (col * spacing) + straight_length elif port_orientation == 270: xlength = col * via_spacing + straight_length elif port_orientation == 90: xlength = columns * via_spacing - (col * via_spacing) + straight_length else: raise ValueError( f"Invalid port_orientation = {port_orientation}", "180: west, 0: east, 90: north, 270: south", ) contact_ref = c << contact contact_ref.x = col * spacing contact_ref.y = col * via_spacing + contact_dy if cross_section: port_name = f"e{col}" straightx_ref = c << straight( length=xlength, cross_section=cross_section, **kwargs) straightx_ref.connect( "e2", contact_ref.get_ports_list(orientation=port_orientation)[0]) c.add_port(port_name, port=straightx_ref.ports["e1"]) if port_offset: c.ports[port_name].move(np.array(port_offset) * col) return c
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 round_corners( points: Coordinates, straight: ComponentFactory = straight, bend: ComponentFactory = bend_euler, bend_s_factory: Optional[ComponentFactory] = bend_s, taper: Optional[ComponentFactory] = None, straight_fall_back_no_taper: Optional[ComponentFactory] = None, mirror_straight: bool = False, straight_ports: Optional[List[str]] = None, cross_section: CrossSectionFactory = strip, on_route_error: Callable = get_route_error, with_point_markers: bool = False, **kwargs, ) -> Route: """Returns Route: - references list with rounded straight route from a list of manhattan points. - ports: Tuple of ports - length: route length (float) Args: points: manhattan route defined by waypoints bend90: the bend to use for 90Deg turns straight: the straight library to use to generate straight portions taper: taper for straight portions. If None, no tapering straight_fall_back_no_taper: in case there is no space for two tapers mirror_straight: mirror_straight waveguide straight_ports: port names for straights. If None finds them automatically. cross_section: on_route_error: function to run when route fails with_point_markers: add route points markers (easy for debugging) **kwargs: cross_section settings """ x = cross_section(**kwargs) auto_widen = x.info.get("auto_widen", False) auto_widen_minimum_length = x.info.get("auto_widen_minimum_length", 200.0) taper_length = x.info.get("taper_length", 10.0) width = x.info.get("width", 2.0) width_wide = x.info.get("width_wide", None) references = [] bend90 = bend(cross_section=cross_section, ** kwargs) if callable(bend) else bend # bsx = bsy = _get_bend_size(bend90) taper = taper or taper_factory( cross_section=cross_section, width1=width, width2=width_wide, length=taper_length, ) taper = taper(cross_section=cross_section, ** kwargs) if callable(taper) else taper # If there is a taper, make sure its length is known if taper and isinstance(taper, Component): if "length" not in taper.info: _taper_ports = list(taper.ports.values()) taper.info["length"] = _taper_ports[-1].x - _taper_ports[0].x straight_fall_back_no_taper = straight_fall_back_no_taper or straight # Remove any flat angle, otherwise the algorithm won't work points = remove_flat_angles(points) points = np.array(points) straight_sections = [] # (p0, angle, length) p0_straight = points[0] p1 = points[1] total_length = 0 # Keep track of the total path length if not hasattr(bend90.info, "length"): raise ValueError( f"bend {bend90} needs to have bend.info.length defined") bend_length = bend90.info.length dp = p1 - p0_straight bend_orientation = None if _is_vertical(p0_straight, p1): if dp[1] > 0: bend_orientation = 90 elif dp[1] < 0: bend_orientation = 270 elif _is_horizontal(p0_straight, p1): if dp[0] > 0: bend_orientation = 0 elif dp[0] < 0: bend_orientation = 180 if bend_orientation is None: return on_route_error(points=points, cross_section=x) layer = x.info["layer"] try: pname_west, pname_north = [ p.name for p in _get_bend_ports(bend=bend90, layer=layer) ] except ValueError as exc: raise ValueError( f"Did not find 2 ports on layer {layer}. Got {list(bend90.ports.values())}" ) from exc n_o_bends = points.shape[0] - 2 total_length += n_o_bends * bend_length previous_port_point = points[0] bend_points = [previous_port_point] # Add bend sections and record straight-section information for i in range(1, points.shape[0] - 1): bend_origin, rotation, x_reflection = _get_bend_reference_parameters( points[i - 1], points[i], points[i + 1], bend90, x.info["layer"]) bend_ref = gen_sref(bend90, rotation, x_reflection, pname_west, bend_origin) references.append(bend_ref) dx_points = points[i][0] - points[i - 1][0] dy_points = points[i][1] - points[i - 1][1] if abs(dx_points) < TOLERANCE: matching_ports = [ port for port in bend_ref.ports.values() if np.isclose(port.x, points[i][0]) ] if abs(dy_points) < TOLERANCE: matching_ports = [ port for port in bend_ref.ports.values() if np.isclose(port.y, points[i][1]) ] if matching_ports: next_port = matching_ports[0] other_port_name = set(bend_ref.ports.keys()) - {next_port.name} other_port = bend_ref.ports[list(other_port_name)[0]] bend_points.append(next_port.midpoint) bend_points.append(other_port.midpoint) previous_port_point = other_port.midpoint straight_sections += [( p0_straight, bend_orientation, get_straight_distance(p0_straight, bend_origin), )] p0_straight = bend_ref.ports[pname_north].midpoint bend_orientation = bend_ref.ports[pname_north].orientation bend_points.append(points[-1]) straight_sections += [(p0_straight, bend_orientation, get_straight_distance(p0_straight, points[-1]))] # with_point_markers=True # print() # for i, point in enumerate(points): # print(i, point) # print() # for i, point in enumerate(bend_points): # print(i, point) # ensure bend connectivity for i, point in enumerate(points[:-1]): sx = np.sign(points[i + 1][0] - point[0]) sy = np.sign(points[i + 1][1] - point[1]) bsx = np.sign(bend_points[2 * i + 1][0] - bend_points[2 * i][0]) bsy = np.sign(bend_points[2 * i + 1][1] - bend_points[2 * i][1]) if bsx * sx == -1 or bsy * sy == -1: # print("error", bsx * sx, bsy * sy) # references += [gf.c.rectangle(size=(2, 2)).ref(position=point)] return on_route_error(points=points, cross_section=x, references=references) # for i, point in enumerate(bend_points[:-1]): # bsx = bend_points[i + 1][0] - point[0] # bsy = bend_points[i + 1][1] - point[1] # if abs(bsx) > 0.001 and abs(bsy) > 0.001: # print(i, point, bsx, bsy) # # return on_route_error(points=points, cross_section=x, references=references) wg_refs = [] for straight_origin, angle, length in straight_sections: with_taper = False # wg_width = list(bend90.ports.values())[0].width length = snap_to_grid(length) total_length += length if auto_widen and length > auto_widen_minimum_length and width_wide: # Taper starts where straight would have started with_taper = True length = length - 2 * taper_length taper_origin = straight_origin pname_west, pname_east = [ p.name for p in _get_straight_ports(taper, layer=x.info["layer"]) ] taper_ref = taper.ref(position=taper_origin, port_id=pname_west, rotation=angle) references.append(taper_ref) wg_refs += [taper_ref] # Update start straight position straight_origin = taper_ref.ports[pname_east].midpoint # Straight waveguide kwargs_wide = kwargs.copy() kwargs_wide.update(width=width_wide) cross_section_wide = gf.partial(cross_section, **kwargs_wide) wg = straight(length=length, cross_section=cross_section_wide) else: wg = straight_fall_back_no_taper(length=length, cross_section=cross_section, **kwargs) if straight_ports is None: straight_ports = [ p.name for p in _get_straight_ports(wg, layer=x.info["layer"]) ] pname_west, pname_east = straight_ports wg_ref = wg.ref() wg_ref.move(wg.ports[pname_west], (0, 0)) if mirror_straight: wg_ref.reflect_v(list(wg_ref.ports.values())[0].name) wg_ref.rotate(angle) wg_ref.move(straight_origin) if length > 0: references.append(wg_ref) wg_refs += [wg_ref] port_index_out = 1 if with_taper: # Second taper: # Origin at end of straight waveguide, starting from east side of taper taper_origin = wg_ref.ports[pname_east] pname_west, pname_east = [ p.name for p in _get_straight_ports(taper, layer=x.info["layer"]) ] taper_ref = taper.ref(position=taper_origin, port_id=pname_east, rotation=angle + 180) references.append(taper_ref) wg_refs += [taper_ref] port_index_out = 0 # route = Component() # route.add(references) # netlist = route.get_netlist() # if len(netlist["connections"]) != len(references) - 1: # return on_route_error(points=points, cross_section=x, references=references) if with_point_markers: route = get_route_error(points, cross_section=x) references += route.references port_input = list(wg_refs[0].ports.values())[0] port_output = list(wg_refs[-1].ports.values())[port_index_out] length = snap_to_grid(float(total_length)) return Route(references=references, ports=(port_input, port_output), length=length)
def array_with_fanout( component: ComponentOrFactory = pad, columns: int = 3, pitch: float = 150.0, waveguide_pitch: float = 10.0, start_straight_length: float = 5.0, end_straight_length: float = 40.0, radius: float = 5.0, component_port_name: str = "e4", bend: ComponentFactory = bend_euler, bend_port_name1: Optional[str] = None, bend_port_name2: Optional[str] = None, cross_section: CrossSectionFactory = strip, **kwargs, ) -> Component: """Returns an array of components in X axis with fanout waveguides facing west Args: component: to replicate. columns: number of components. pitch: for waveguides. waveguide_pitch: for output waveguides. start_straight_length: length of the start of the straight end_straight_length: lenght of the straight at the end radius: bend radius component_port_name: bend: bend_port_name1: bend_port_name2: cross_section: cross_section definition kwargs: cross_section settings """ c = Component() component = component() if callable(component) else component bend = bend(radius=radius, cross_section=cross_section, **kwargs) bend_ports = bend.get_ports_list() bend_ports = sort_ports_x(bend_ports) bend_ports.reverse() bend_port_name1 = bend_port_name1 or bend_ports[0].name bend_port_name2 = bend_port_name2 or bend_ports[1].name for col in range(columns): ref = component.ref() ref.x = col * pitch c.add(ref) ylength = col * waveguide_pitch + start_straight_length xlength = col * pitch + end_straight_length straight_ref = c << straight( length=ylength, cross_section=cross_section, **kwargs) port_s1, port_s2 = straight_ref.get_ports_list() straight_ref.connect(port_s2.name, ref.ports[component_port_name]) bend_ref = c.add_ref(bend) bend_ref.connect(bend_port_name1, straight_ref.ports[port_s1.name]) straightx_ref = c << straight( length=xlength, cross_section=cross_section, **kwargs) straightx_ref.connect(port_s2.name, bend_ref.ports[bend_port_name2]) c.add_port(f"W_{col}", port=straightx_ref.ports[port_s1.name]) auto_rename_ports(c) return c
from gdsfactory.components.straight import straight c = straight() polygons = len(c.get_polygons()) target_layers = [LAYER.WG] keepout_layers = [LAYER.NO_TILE_SI] print(len(c.get_polygons())) assert len(c.get_polygons()) == polygons c = add_keepout(component=c, target_layers=target_layers, keepout_layers=keepout_layers) # print(len(c.get_polygons())) assert len(c.get_polygons()) == polygons + 1 if __name__ == "__main__": # test_add_keepout() # import gdsfactory as gf # from gdsfactory.components.crossing_waveguide import crossing_etched # from gdsfactory.components.crossing_waveguide import crossing45 from gdsfactory.components.straight import straight c = straight() target_layers = [LAYER.WG] keepout_layers = [LAYER.SLAB150] c = add_keepout(c, target_layers, keepout_layers) c.show()