def connect_strip_way_points(way_points=[], bend_factory=bend_circular, straight_factory=waveguide, taper_factory=taper_factory, bend_radius=10.0, wg_width=0.5, layer=LAYER.WG, **kwargs): """ Returns a deep-etched route formed by the given way_points with bends instead of corners and optionally tapers in straight sections. taper_factory: can be either a taper component or a factory """ bend90 = bend_factory(radius=bend_radius, width=wg_width) if taper_factory: if callable(taper_factory): taper = taper_factory( length=TAPER_LENGTH, width1=wg_width, width2=WG_EXPANDED_WIDTH, layer=layer, ) else: # In this case the taper is a fixed cell taper = taper_factory else: taper = None connector = round_corners(way_points, bend90, straight_factory, taper) return connector
def connect_strip_way_points(way_points: List[Tuple[float, float]], bend_factory: Callable = bend_circular, straight_factory: Callable = waveguide, taper_factory: Callable = taper_factory, bend_radius: float = 10.0, wg_width: float = 0.5, layer=LAYER.WG, **kwargs): """Returns a deep-etched route formed by the given way_points with bends instead of corners and optionally tapers in straight sections. taper_factory: can be either a taper Component or a factory """ way_points = np.array(way_points) bend90 = bend_factory(radius=bend_radius, width=wg_width) taper = (taper_factory( length=TAPER_LENGTH, width1=wg_width, width2=WG_EXPANDED_WIDTH, layer=layer, ) if callable(taper_factory) else taper_factory) connector = round_corners(way_points, bend90, straight_factory, taper) return connector
def gen_loopback( start_port, end_port, gc, grating_separation=127.0, gc_rotation=-90, gc_port_name="W0", bend_radius_align_ports=10.0, bend_factory=bend_circular, waveguide_factory=waveguide, y_bot_align_route=None, ): """ Add a loopback w.r.t a start port and and end port Input grating generated on the left of start_port Output grating generated on the right of end_port """ if hasattr(start_port, "y"): y0 = start_port.y else: y0 = start_port[1] if hasattr(start_port, "x"): x0 = start_port.x - grating_separation else: x0 = start_port[0] - grating_separation if hasattr(end_port, "x"): x1 = end_port.x + grating_separation else: x1 = end_port[0] + grating_separation gca1, gca2 = [ gc.ref(position=(x, y0), rotation=gc_rotation, port_id=gc_port_name) for x in [x0, x1] ] gsi = gc.size_info p0 = gca1.ports[gc_port_name].position p1 = gca2.ports[gc_port_name].position a = bend_radius_align_ports + 0.5 b = max(2 * a, grating_separation / 2) if y_bot_align_route == None: y_bot_align_route = -gsi.width - 5.0 route = [ p0, p0 + (0, a), p0 + (b, a), p0 + (b, y_bot_align_route), p1 + (-b, y_bot_align_route), p1 + (-b, a), p1 + (0, a), p1, ] bend90 = bend_factory(radius=bend_radius_align_ports) loop_back = round_corners(route, bend90, waveguide_factory) elements = [gca1, gca2, loop_back] return elements
def connect_strip( way_points=[], bend_factory=bend_circular, straight_factory=waveguide, bend_radius=10.0, wg_width=0.5, **kwargs, ): """ Returns a deep-etched route formed by the given way_points with bends instead of corners and optionally tapers in straight sections. """ bend90 = bend_factory(radius=bend_radius, width=wg_width) connector = round_corners(way_points, bend90, straight_factory) return connector
def connect_loop_back(port0, port1, a, b, R, y_bot_align_route): p0 = port0.position p1 = port1.position route = [ p0, p0 + (0, a), p0 + (b, a), p0 + (b, y_bot_align_route), p1 + (-b, y_bot_align_route), p1 + (-b, a), p1 + (0, a), p1, ] bend90 = pp.c.bend_circular(radius=R) loop_back = round_corners(route, bend90, pp.c.waveguide) return loop_back
def connect_loop_back(port0: Port, port1: Port, a, b, R, y_bot_align_route) -> ComponentReference: p0 = port0.position p1 = port1.position route = [ p0, p0 + (0, a), p0 + (b, a), p0 + (b, y_bot_align_route), p1 + (-b, y_bot_align_route), p1 + (-b, a), p1 + (0, a), p1, ] bend90 = pp.c.bend_circular(radius=R) return round_corners(route, bend90, pp.c.waveguide)["references"]
def connect_elec_waypoints(way_points, bend_factory=corner, straight_factory=wire, taper_factory=taper_factory, wg_width=10.0, layer=LAYER.M3, **kwargs): """returns a route with electrical traces""" bend90 = bend_factory(width=wg_width, layer=layer) def _straight_factory(length=10.0, width=wg_width): return straight_factory(length=length, width=width, layer=layer) connector = round_corners(way_points, bend90, _straight_factory, taper=None) return connector
def connect_elec_waypoints(way_points=[], bend_factory=bend_circular, straight_factory=waveguide, taper_factory=taper_factory, bend_radius=10.0, wg_width=0.5, layer=LAYER.WG, **kwargs): bend90 = bend_factory(width=wg_width, radius=bend_radius, layer=layer) def _straight_factory(length=10.0, width=wg_width): return straight_factory(length=length, width=width, layer=layer) if "bend_radius" in kwargs: bend_radius = kwargs.pop("bend_radius") else: bend_radius = 10 connector = round_corners(way_points, bend90, _straight_factory, taper=None) return connector
def connect_bundle_waypoints(start_ports, end_ports, way_points, straight_factory=waveguide, taper_factory=taper_factory, bend_factory=bend_circular, bend_radius=10.0, auto_sort=True, **kwargs): """ start_ports: a list of ports end_ports: a list of ports way_points: a list of points defining a route with way_points[0] = start_ports[0] way_points[-1] = end_ports[0] """ if len(end_ports) != len(start_ports): raise ValueError( "Number of start ports should match number of end ports.\ Got {} {}".format(len(start_ports), len(end_ports))) for p in start_ports: p.angle = int(p.angle) % 360 for p in end_ports: p.angle = int(p.angle) % 360 start_angle = start_ports[0].orientation end_angle = end_ports[0].orientation """ Sort the ports such that the bundle connect the correct corresponding ports. """ angles_to_sorttypes = { (0, 180): ("Y", "Y"), (0, 90): ("Y", "X"), (0, 0): ("Y", "-Y"), (0, 270): ("Y", "-X"), (90, 0): ("X", "Y"), (90, 90): ("X", "-X"), (90, 180): ("X", "-Y"), (90, 270): ("X", "X"), (180, 90): ("Y", "-X"), (180, 0): ("Y", "Y"), (180, 270): ("Y", "X"), (180, 180): ("Y", "-Y"), (270, 90): ("X", "X"), (270, 270): ("X", "-X"), (270, 0): ("X", "-Y"), (270, 180): ("X", "Y"), } dict_sorts = { "X": lambda p: p.x, "Y": lambda p: p.y, "-X": lambda p: -p.x, "-Y": lambda p: -p.y, } key = (start_angle, end_angle) sp_st, ep_st = angles_to_sorttypes[key] start_port_sort = dict_sorts[sp_st] end_port_sort = dict_sorts[ep_st] if auto_sort: start_ports.sort(key=start_port_sort) end_ports.sort(key=end_port_sort) routes = _generate_manhattan_bundle_waypoints(start_ports, end_ports, way_points, **kwargs) bends90 = [ bend_factory(radius=bend_radius, width=p.width) for p in start_ports ] if taper_factory != None: if type(taper_factory) == type(lambda a: a): taper = taper_factory( length=TAPER_LENGTH, width1=start_ports[0].width, width2=WG_EXPANDED_WIDTH, layer=start_ports[0].layer, ) else: # In this case the taper is a fixed cell taper = taper_factory else: taper = None connections = [ round_corners(pts, bend90, straight_factory, taper=taper) for pts, bend90 in zip(routes, bends90) ] return connections
def add_gratings_and_loop_back( component, grating_coupler=grating_coupler_te, excluded_ports=[], grating_separation=127.0, bend_radius_align_ports=10.0, gc_port_name=None, gc_rotation=-90, waveguide_separation=5.0, bend_factory=bend_circular, waveguide_factory=waveguide, layer_label=pp.layer("TEXT"), # input_port_indexes=[0], name=None, component_name=None, ): """ returns a component with grating_couplers and loopback """ direction = "S" component_name = component.name name = name or component_name or f"{component_name}_c" c = pp.Component(name=name) c.add_ref(component) gc = pp.call_if_func(grating_coupler) # Find grating port name if not specified if gc_port_name is None: gc_port_name = list(gc.ports.values())[0].name # List the optical ports to connect optical_ports = component.get_optical_ports() optical_ports = [p for p in optical_ports if p.name not in excluded_ports] optical_ports = direction_ports_from_list_ports(optical_ports)[direction] # Check if the ports are equally spaced grating_separation_extracted = check_ports_have_equal_spacing( optical_ports) if grating_separation_extracted != grating_separation: raise ValueError("Grating separation must be {}. Got {}".format( grating_separation, grating_separation_extracted)) # Add grating couplers couplers = [] for port in optical_ports: coupler_ref = c.add_ref(gc) coupler_ref.connect(list(coupler_ref.ports.values())[0].name, port) couplers += [coupler_ref] # add labels for i, optical_port in enumerate(optical_ports): label = get_input_label( optical_port, couplers[i], i, component_name=component_name, layer_label=layer_label, ) c.add(label) # Add loopback y0 = couplers[0].ports[gc_port_name].y xs = [p.x for p in optical_ports] x0 = min(xs) - grating_separation x1 = max(xs) + grating_separation gca1, gca2 = [ gc.ref(position=(x, y0), rotation=gc_rotation, port_id=gc_port_name) for x in [x0, x1] ] gsi = gc.size_info p0 = gca1.ports[gc_port_name].position p1 = gca2.ports[gc_port_name].position a = bend_radius_align_ports + 0.5 b = max(2 * a, grating_separation / 2) y_bot_align_route = -gsi.width - waveguide_separation route = [ p0, p0 + (0, a), p0 + (b, a), p0 + (b, y_bot_align_route), p1 + (-b, y_bot_align_route), p1 + (-b, a), p1 + (0, a), p1, ] bend90 = bend_factory(radius=bend_radius_align_ports) loop_back = round_corners(route, bend90, waveguide_factory) elements = [gca1, gca2, loop_back] c.add(elements) return c
def route_fiber_array( component: Component, optical_io_spacing: float = SPACING_GC, grating_coupler: Callable = grating_coupler_te, bend_factory: Callable = bend_circular, straight_factory: Callable = waveguide, fanout_length: Optional[int] = None, max_y0_optical: None = None, with_align_ports: bool = True, waveguide_separation: float = 4.0, optical_routing_type: Optional[int] = None, bend_radius: float = BEND_RADIUS, connected_port_list_ids: None = None, nb_optical_ports_lines: int = 1, force_manhattan: bool = False, excluded_ports: List[Any] = None, grating_indices: None = None, route_filter: Callable = connect_strip_way_points, gc_port_name: str = "W0", gc_rotation: int = -90, layer_label: Tuple[int, int] = LAYER.LABEL, component_name: Optional[str] = None, x_grating_offset: int = 0, optical_port_labels: None = None, route_factory: Callable = route_south, get_input_labels_function: Callable = get_input_labels, select_ports: Callable = select_optical_ports, ) -> Tuple[ List[Union[ComponentReference, Label]], List[List[ComponentReference]], float64 ]: """ Returns component I/O elements for adding grating couplers with a fiber array input Many components are fine with the default. Args: component: The component to connect. optical_io_spacing: the wanted spacing between the optical I/O grating_coupler: grating coupler instance, function or list of functions bend_factory: bend_circular straight_factory: waveguide fanout_length: Wanted distance between the gratings and the southest component port. If None, automatically calculated. max_y0_optical: Maximum y coordinate at which the intermediate optical ports can be set. Usually fine to leave at None. with_align_ports: If True, add compact loopback alignment ports waveguide_separation: min separation between the waveguides used to route grating couplers to the component I/O. optical_routing_type: There are three options for optical routing * ``0`` is very basic but can be more compact. Can also be used in combination with ``connected_port_list_ids`` or to route some components which otherwise fail with type ``1``. * ``1`` is the standard routing. * ``2`` uses the optical ports as a guideline for the component's physical size (instead of using the actual component size). Useful where the component is large due to metal tracks * ``None: leads to an automatic decision based on size and number of I/O of the component. bend_radius: bend radius connected_port_list_ids: only for type 0 optical routing. Can specify which ports goes to which grating assuming the gratings are ordered from left to right. e.g ['N0', 'W1','W0','E0','E1', 'N1' ] or [4,1,7,3] nb_optical_ports_lines: number of lines with I/O grating couplers. One line by default. WARNING: Only works properly if: - nb_optical_ports_lines divides the total number of ports - the components have an equal number of inputs and outputs force_manhattan: in some instances, the port linker defaults to an S-bend due to lack of space to do manhattan. Force manhattan offsets all the ports to replace the s-bend by a straight link. This fails if multiple ports have the same issue excluded_ports: ports excluded from routing grating_indices: allows to fine skip some grating slots e.g [0,1,4,5] will put two gratings separated by the pitch. Then there will be two empty grating slots, and after that an additional two gratings. route_filter: waveguide and bend factories gc_port_name: grating_coupler port name, where to route waveguides gc_rotation: grating_coupler rotation (deg) layer_label: for TM labels component_name: name of component x_grating_offset: x offset optical_port_labels: port labels that need connection route_factory: factories for route get_input_labels_function: functions to add labels select_ports: function to select ports Returns: elements, io_grating_lines, y0_optical """ component_name = component_name or component.name excluded_ports = excluded_ports or [] if optical_port_labels is None: # for pn, p in component.ports.items(): # print(p.name, p.port_type, p.layer) # optical_ports = component.get_ports_list(port_type='optical') optical_ports = list(select_ports(component.ports).values()) # print(optical_ports) else: optical_ports = [component.ports[lbl] for lbl in optical_port_labels] optical_ports = [p for p in optical_ports if p.name not in excluded_ports] N = len(optical_ports) if N == 0: return [], [], 0 elements = [] # grating_coupler can either be a component/function # or a list of components/functions if isinstance(grating_coupler, list): grating_couplers = [pp.call_if_func(g) for g in grating_coupler] grating_coupler = grating_couplers[0] else: grating_coupler = pp.call_if_func(grating_coupler) grating_couplers = [grating_coupler] * N assert ( gc_port_name in grating_coupler.ports ), f"{gc_port_name} not in {list(grating_coupler.ports.keys())}" # Now: # - grating_coupler is a single grating coupler # - grating_couplers is a list of grating couplers # Define the route filter to apply to connection methods route_filter_params = { "bend_radius": bend_radius, "wg_width": grating_coupler.ports[gc_port_name].width, } def routing_method(p1, p2, **kwargs): way_points = get_waypoints_connect_strip(p1, p2, **kwargs) return route_filter(way_points, **route_filter_params) R = bend_radius # `delta_gr_min` Used to avoid crossing between waveguides in special cases # This could happen when abs(x_port - x_grating) <= 2 * bend_radius delta_gr_min = 2 * bend_radius + 1 io_sep = optical_io_spacing offset = (N - 1) * io_sep / 2.0 # Get the center along x axis x_c = round(sum([p.x for p in optical_ports]) / N, 1) y_min = component.ymin # min([p.y for p in optical_ports]) # Sort the list of optical ports: direction_ports = direction_ports_from_list_ports(optical_ports) sep = waveguide_separation K = len(optical_ports) K = K + 1 if K % 2 else K # Set routing type if not specified pxs = [p.x for p in optical_ports] is_big_component = ( (K > 2) or (max(pxs) - min(pxs) > io_sep - delta_gr_min) or (component.xsize > io_sep) ) if optical_routing_type is None: if not is_big_component: optical_routing_type = 0 else: optical_routing_type = 1 """ Look at a bunch of conditions to choose the default length if the default fanout distance is not set """ def has_p(side): return len(direction_ports[side]) > 0 list_ew_ports_on_sides = [has_p(side) for side in ["E", "W"]] list_ns_ports_on_sides = [has_p(side) for side in ["N", "S"]] has_ew_ports = any(list_ew_ports_on_sides) has_ns_ports = any(list_ns_ports_on_sides) is_one_sided_horizontal = False for side1, side2 in [("E", "W"), ("W", "E")]: if len(direction_ports[side1]) >= 2: if all([len(direction_ports[side]) == 0 for side in ["N", "S", side2]]): is_one_sided_horizontal = True # Compute fanout length if not specified if fanout_length is None: fanout_length = bend_radius + 1.0 # We need 3 bends in that case to connect the most bottom port to the # grating couplers if has_ew_ports and is_big_component: fanout_length = max(fanout_length, 3 * bend_radius + 1.0) if has_ns_ports or is_one_sided_horizontal: fanout_length = max(fanout_length, 2 * bend_radius + 1.0) if has_ew_ports and not is_big_component: fanout_length = max(fanout_length, bend_radius + 1.0) fanout_length += 5 # use x for grating coupler since we rotate it y0_optical = y_min - fanout_length - grating_coupler.ports[gc_port_name].x y0_optical += -K / 2 * sep y0_optical = round(y0_optical, 1) if max_y0_optical is not None: y0_optical = round(min(max_y0_optical, y0_optical), 1) """ - First connect half of the north ports going from middle of list down to first elements - then connect west ports (top to bottom) - then connect south ports (left to right) - then east ports (bottom to top) - then second half of the north ports (right to left) """ north_ports = direction_ports["N"] north_start = north_ports[0 : len(north_ports) // 2] north_finish = north_ports[len(north_ports) // 2 :] west_ports = direction_ports["W"] west_ports.reverse() east_ports = direction_ports["E"] south_ports = direction_ports["S"] north_finish.reverse() # Sort right to left north_start.reverse() # Sort right to left ordered_ports = north_start + west_ports + south_ports + east_ports + north_finish nb_ports_per_line = N // nb_optical_ports_lines grating_coupler_si = grating_coupler.size_info y_gr_gap = (K / (nb_optical_ports_lines) + 1) * sep gr_coupler_y_sep = grating_coupler_si.height + y_gr_gap + bend_radius offset = (nb_ports_per_line - 1) * io_sep / 2 - x_grating_offset io_gratings_lines = [] # [[gr11, gr12, gr13...], [gr21, gr22, gr23...] ...] if grating_indices is None: grating_indices = list(range(nb_ports_per_line)) else: assert len(grating_indices) == nb_ports_per_line for j in range(nb_optical_ports_lines): io_gratings = [ gc.ref( position=(x_c - offset + i * io_sep, y0_optical - j * gr_coupler_y_sep), rotation=gc_rotation, port_id=gc_port_name, ) for i, gc in zip(grating_indices, grating_couplers) ] io_gratings_lines += [io_gratings[:]] # optical routing - type ``0`` if optical_routing_type == 0: """ Basic optical routing, typically fine for small components No heuristic to avoid collisions between connectors. If specified ports to connect in a specific order (i.e if connected_port_list_ids is not None and not empty) then grab these ports """ if connected_port_list_ids: ordered_ports = [component.ports[i] for i in connected_port_list_ids] for io_gratings in io_gratings_lines: for i in range(N): p0 = io_gratings[i].ports[gc_port_name] p1 = ordered_ports[i] elements.extend( routing_method(p0, p1, bend_radius=bend_radius)["references"] ) # optical routing - type ``1 or 2`` elif optical_routing_type in [1, 2]: elems, to_route = route_factory( component=component, bend_radius=bend_radius, optical_routing_type=optical_routing_type, excluded_ports=excluded_ports, waveguide_separation=waveguide_separation, io_gratings_lines=io_gratings_lines, gc_port_name=gc_port_name, route_filter=route_filter, ) elements.extend(elems) if force_manhattan: """ 1) find the min x_distance between each grating port and each component port. 2) If abs(min distance) < 2* bend radius then offset io_gratings by -min_distance """ min_dist = 2 * R + 10.0 min_dist_threshold = 2 * R + 1.0 for io_gratings in io_gratings_lines: for gr in io_gratings: for p in to_route: dist = gr[0].x - p.x if abs(dist) < abs(min_dist): min_dist = dist if abs(min_dist) < min_dist_threshold: for gr in io_gratings: gr.translate((-min_dist, 0)) # If the array of gratings is too close, adjust its location gc_ports_tmp = [] for io_gratings in io_gratings_lines: gc_ports_tmp += [gc.ports[gc_port_name] for gc in io_gratings] min_y = get_min_spacing(to_route, gc_ports_tmp, sep=sep, radius=R) delta_y = abs(to_route[0].y - gc_ports_tmp[0].y) if min_y > delta_y: for io_gratings in io_gratings_lines: for gr in io_gratings: gr.translate(0, delta_y - min_y) # If we add align ports, we need enough space for the bends end_straight_offset = waveguide_separation + 5 if with_align_ports else 0.01 if len(io_gratings_lines) == 1: io_gratings = io_gratings_lines[0] gc_ports = [gc.ports[gc_port_name] for gc in io_gratings] routes = link_optical_ports( to_route, gc_ports, separation=sep, end_straight_offset=end_straight_offset, route_filter=route_filter, **route_filter_params, ) elements.extend([route["references"] for route in routes]) else: for io_gratings in io_gratings_lines: gc_ports = [gc.ports[gc_port_name] for gc in io_gratings] nb_gc_ports = len(io_gratings) nb_ports_to_route = len(to_route) n0 = nb_ports_to_route / 2 dn = nb_gc_ports / 2 routes = link_optical_ports( to_route[n0 - dn : n0 + dn], gc_ports, separation=sep, end_straight_offset=end_straight_offset, route_filter=route_filter, **route_filter_params, ) elements.extend([route["references"] for route in routes]) del to_route[n0 - dn : n0 + dn] if with_align_ports: gca1, gca2 = [ grating_coupler.ref( position=( x_c - offset + ii * io_sep, io_gratings_lines[-1][0].ports[gc_port_name].y, ), rotation=gc_rotation, port_id=gc_port_name, ) for ii in [grating_indices[0] - 1, grating_indices[-1] + 1] ] gsi = grating_coupler.size_info p0 = gca1.ports[gc_port_name].position p1 = gca2.ports[gc_port_name].position bend_radius_align_ports = R a = bend_radius_align_ports + 5.0 # 0.5 b = max(2 * a, io_sep / 2) y_bot_align_route = -gsi.width - waveguide_separation route = [ p0, p0 + (0, a), p0 + (b, a), p0 + (b, y_bot_align_route), p1 + (-b, y_bot_align_route), p1 + (-b, a), p1 + (0, a), p1, ] elements.extend([gca1, gca2]) bend90 = bend_factory(radius=bend_radius) route = round_corners(route, bend90, straight_factory) elements.extend(route["references"]) elements.extend( get_input_labels_function( io_gratings, ordered_ports, component_name, layer_label, gc_port_name ) ) return elements, io_gratings_lines, y0_optical
def delay_snake( wg_width: float = 0.5, total_length: float = 160000.0, L0: float = 2350.0, n: int = 5, taper: Callable = taper_function, bend_factory: Callable = bend_circular, bend_radius: float = 10.0, straight_factory: Callable = waveguide_function, ) -> Component: """ Snake input facing west Snake output facing east Args: wg_width total_length: L0: initial offset n: number of loops taper: taper factory bend_factory bend_radius straight_factory .. code:: | L0 | L2 | ->-------------| | pi * radius |-------------------| | |-------------------> | DL | .. plot:: :include-source: import pp c = pp.c.delay_snake(L0=5, total_length=1600, n=2) pp.plotgds(c) """ epsilon = 0.1 R = bend_radius bend90 = bend_factory(radius=R, width=wg_width) DL = (total_length + L0 - n * (pi * R + epsilon)) / (2 * n + 1) L2 = DL - L0 assert ( L2 > 0 ), "Snake is too short: either reduce L0, increase the total length,\ or decrease n" y = 0 path = [(0, y), (L2, y)] for i in range(n): y -= 2 * R + epsilon path += [(L2, y), (-L0, y)] y -= 2 * R + epsilon path += [(-L0, y), (L2, y)] path = [(round(_x, 3), round(_y, 3)) for _x, _y in path] component = pp.Component() if taper: _taper = taper(width1=wg_width, width2=WG_EXPANDED_WIDTH, length=TAPER_LENGTH) snake = round_corners(path, bend90, straight_factory, taper=_taper) component.add(snake) component.ports = snake.ports pp.port.auto_rename_ports(component) return component
def gen_loopback( start_port: Port, end_port: Port, gc: Callable, grating_separation: float = 127.0, gc_rotation: int = -90, gc_port_name: str = "W0", bend_radius_align_ports: float = 10.0, bend_factory: Callable = bend_circular, waveguide_factory: Callable = waveguide, y_bot_align_route=None, ) -> List[ComponentReference]: """ Add a loopback (grating coupler align reference) to a start port and and end port Input grating generated on the left of start_port Output grating generated on the right of end_port .. code:: __________________________________________ | separation | | | | | | | GC start_port end_port GC """ gc = gc() if callable(gc) else gc if hasattr(start_port, "y"): y0 = start_port.y else: y0 = start_port[1] if hasattr(start_port, "x"): x0 = start_port.x - grating_separation else: x0 = start_port[0] - grating_separation if hasattr(end_port, "x"): x1 = end_port.x + grating_separation else: x1 = end_port[0] + grating_separation gca1, gca2 = [ gc.ref(position=(x, y0), rotation=gc_rotation, port_id=gc_port_name) for x in [x0, x1] ] gsi = gc.size_info p0 = gca1.ports[gc_port_name].position p1 = gca2.ports[gc_port_name].position a = bend_radius_align_ports + 0.5 b = max(2 * a, grating_separation / 2) y_bot_align_route = (y_bot_align_route if y_bot_align_route is not None else -gsi.width - 5.0) route = [ p0, p0 + (0, a), p0 + (b, a), p0 + (b, y_bot_align_route), p1 + (-b, y_bot_align_route), p1 + (-b, a), p1 + (0, a), p1, ] bend90 = bend_factory(radius=bend_radius_align_ports) route = round_corners(route, bend90, waveguide_factory) elements = [gca1, gca2] elements.extend(route["references"]) return elements
def delay_snake( total_length=160000, L0=2350.0, n=5, taper=taper, bend_factory=bend_circular, bend_radius=10.0, straight_factory=waveguide, wg_width=0.5, ): """ Snake input facing west Snake output facing east Args: total_length: L0: n: taper: bend_factory bend_radius straight_factory wg_width .. code:: | L0 | L2 | ->-------------| | pi * radius |-------------------| | |-------------------> | L1 | .. plot:: :include-source: import pp c = pp.c.delay_snake(L0=5, total_length=1600, n=2) pp.plotgds(c) """ epsilon = 0.1 R = bend_radius bend90 = bend_factory(radius=R, width=wg_width) L1 = (total_length + L0 - n * (pi * R + epsilon)) / (2 * n + 1) L2 = L1 - L0 assert ( L2 > 0), "Snake is too short: either reduce L0, increase the total length,\ or decrease n" y = 0 path = [(0, y), (L2, y)] for i in range(n): y -= 2 * R + epsilon path += [(L2, y), (-L0, y)] y -= 2 * R + epsilon path += [(-L0, y), (L2, y)] path = [(round(_x, 3), round(_y, 3)) for _x, _y in path] component = pp.Component() if taper != None: if callable(taper): _taper = taper(width1=wg_width, width2=WG_EXPANDED_WIDTH, length=TAPER_LENGTH) else: _taper = taper else: _taper = None snake = round_corners(path, bend90, straight_factory, taper=_taper) component.add(snake) component.ports = snake.ports pp.ports.port_naming.auto_rename_ports(component) return component