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_pad_array( component: Component, pad_spacing: float = 150.0, 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] = None, 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, ) -> Tuple[ List[Union[ComponentReference, Label]], List[List[ComponentReference]], float64 ]: """ Returns component I/O elements for adding pads to south Basic 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 Args: component: The component to connect. pad_spacing: the wanted spacing between the pads fanout_length: Wanted distance between the gratings and the southest component port. If set to None, automatically calculated. max_y0_optical: Maximum y coordinate at which the intermediate optical ports can be set. Usually fine to leave at None. waveguide_separation: min separation between the waveguides used to route pads to the component I/O. bend_radius: bend radius list_port_labels: list of the port indices (e.g [0,3]) which require a T&M label. connected_port_list_ids: only for type 0 optical routing. Can specify which ports goes to which pads assuming the gratings are ordered from left to right. e.g ['N0', 'W1','W0','E0','E1', 'N1' ] or [4,1,7,3] n_ports: number of lines with I/O pads. One line by default. WARNING: Only works properly if: - n_ports divides the total number of ports - the components have an equal number of inputs and outputs pad_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 pads slots, and after that an additional two gratings. Returns: elements, pads, y0_optical """ excluded_ports = excluded_ports or [] if port_labels is None: ports = list(select_ports(component.ports).values()) else: ports = [component.ports[lbl] for lbl in port_labels] ports = [p for p in ports if p.name not in excluded_ports] N = len(ports) if N == 0: return [], [], 0 elements = [] pad = pad() if callable(pad) else pad pads = [pad] * N route_filter_params = { "bend_radius": bend_radius, "wg_width": ports[0].width, } def routing_method(p1, p2, **kwargs): way_points = get_waypoints_connect_strip(p1, p2, **kwargs) return route_filter(way_points, **route_filter_params) """ # `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 = pad_spacing offset = (N - 1) * io_sep / 2.0 # Get the center along x axis x_c = round(sum([p.x for p in ports]) / N, 1) y_min = component.ymin # min([p.y for p in ports]) # Sort the list of optical ports: direction_ports = direction_ports_from_list_ports(ports) sep = waveguide_separation K = len(ports) K = K + 1 if K % 2 else K # Set routing type if not specified pxs = [p.x for p in ports] is_big_component = ( (K > 2) or (max(pxs) - min(pxs) > io_sep - delta_gr_min) or (component.xsize > io_sep) ) """ 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 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 pad since we rotate it y0_optical = y_min - fanout_length - pad.ports[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 // n_ports pad_size_info = pad.size_info y_gap = (K / (n_ports) + 1) * sep y_sep = pad_size_info.height + y_gap + bend_radius offset = (nb_ports_per_line - 1) * io_sep / 2 - x_pad_offset io_pad_lines = [] # [[gr11, gr12, gr13...], [gr21, gr22, gr23...] ...] if pad_indices is None: pad_indices = list(range(nb_ports_per_line)) else: assert len(pad_indices) == nb_ports_per_line for j in range(n_ports): pads = [ pad.ref( position=(x_c - offset + i * io_sep, y0_optical - j * y_sep), rotation=pad_rotation, port_id=port_name, ) for i, pad in zip(pad_indices, pads) ] io_pad_lines += [pads[:]] if connected_port_list_ids: ordered_ports = [component.ports[i] for i in connected_port_list_ids] for pads in io_pad_lines: for i in range(N): p0 = pads[i].ports[port_name] p1 = ordered_ports[i] elements += [routing_method(p0, p1, bend_radius=bend_radius)] return elements, io_pad_lines, y0_optical
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 route_all_ports_to_south( component, bend_radius=BEND_RADIUS, optical_routing_type=1, excluded_ports=[], waveguide_separation=4.0, io_gratings_lines=None, route_filter=connect_strip_way_points, gc_port_name="E0", ): """ Args: component: The component to be connected bend_radius optical_routing_type: routing heuristic ``1`` or ``2`` (see below) excluded_ports=[]: list of port names to NOT route waveguide_separation io_gratings_lines: list of ports to which the ports produced by this function will be connected. Supplying this information helps avoiding waveguide collisions routing_method: routing method to connect the waveguides gc_port_name: grating port name Returns: list of elements, list of ports Standard optical routing - type ``1`` or variant ``2`` ``1`` uses the component size info to estimate the box size ``2`` only looks at the optical port positions to estimate the size Works well if the component looks rougly like a rectangular box with north ports on the north of the box south ports on the south of the box east ports on the east of the box west ports on the west of the box """ optical_ports = component.get_optical_ports() optical_ports = [p for p in optical_ports if p.name not in excluded_ports] csi = component.size_info elements = [] # Handle empty list gracefully if not optical_ports: return [], [] conn_params = {"bend_radius": bend_radius} route_filter_params = { "bend_radius": bend_radius, "wg_width": optical_ports[0].width, } def routing_method(p1, p2, **kwargs): way_points = get_waypoints_connect_strip(p1, p2, **kwargs) return route_filter(way_points, **route_filter_params) # 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 sep = waveguide_separation # Get lists of optical ports by orientation direction_ports = direction_ports_from_list_ports(optical_ports) 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 def get_index_port_closest_to_x(x, list_ports): return np.array([abs(x - p.ports[gc_port_name].x) for p in list_ports]).argmin() def gen_port_from_port(x, y, p): new_p = pd.Port(name=p.name, midpoint=(x, y), orientation=90.0, width=p.width) return new_p R = bend_radius west_ports.reverse() y0 = min([p.y for p in ordered_ports]) - R - 0.5 ports_to_route = [] i = 0 optical_xs_tmp = [p.x for p in ordered_ports] x_optical_min = min(optical_xs_tmp) x_optical_max = max(optical_xs_tmp) """ ``x`` is the x-coord of the waypoint where the current component port is connected. x starts as close as possible to the component. For each new port, the distance is increased by the separation. The starting x depends on the heuristic chosen : ``1`` or ``2`` """ # Set starting ``x`` on the west side if optical_routing_type == 1: ## `` use component size to know how far to route x = csi.west - R - 1 elif optical_routing_type == 2: ## `` use optical port to know how far to route x = x_optical_min - R - 1 else: raise ValueError("Invalid optical routing type") # First route the ports facing west for p in west_ports: """ In case we have to connect these ports to a line of grating, Ensure that the port is aligned with the grating port or has enough space for manhattan routing (at least two bend radius) """ if io_gratings_lines: i_grating = get_index_port_closest_to_x(x, io_gratings_lines[-1]) x_gr = io_gratings_lines[-1][i_grating].ports[gc_port_name].x if abs(x - x_gr) < delta_gr_min: if x > x_gr: x = x_gr elif x < x_gr: x = x_gr - delta_gr_min tmp_port = gen_port_from_port(x, y0, p) ports_to_route.append(tmp_port) elements += [routing_method(p, tmp_port, **conn_params)] x -= sep i += 1 start_straight = 0.5 # First-half of north ports # This ensures that north ports are routed above the top west one north_start.reverse() # We need them from left to right if len(north_start) > 0: y_max = max([p.y for p in west_ports + north_start]) for p in north_start: tmp_port = gen_port_from_port(x, y0, p) elements += [ routing_method(p, tmp_port, start_straight=start_straight + y_max - p.y, **conn_params) ] ports_to_route.append(tmp_port) x -= sep start_straight += sep # Set starting ``x`` on the east side if optical_routing_type == 1: ## `` use component size to know how far to route x = csi.east + R + 1 elif optical_routing_type == 2: ## `` use optical port to know how far to route x = x_optical_max + R + 1 else: raise ValueError("Invalid optical routing type") i = 0 # Route the east ports start_straight = 0.5 for p in east_ports: """ In case we have to connect these ports to a line of grating, Ensure that the port is aligned with the grating port or has enough space for manhattan routing (at least two bend radius) """ if io_gratings_lines: i_grating = get_index_port_closest_to_x(x, io_gratings_lines[-1]) x_gr = io_gratings_lines[-1][i_grating].ports[gc_port_name].x if abs(x - x_gr) < delta_gr_min: if x < x_gr: x = x_gr elif x > x_gr: x = x_gr + delta_gr_min tmp_port = gen_port_from_port(x, y0, p) elements += [ routing_method(p, tmp_port, start_straight=start_straight, **conn_params) ] ports_to_route.append(tmp_port) x += sep i += 1 # Route the remaining north ports start_straight = 0.5 if len(north_finish) > 0: y_max = max([p.y for p in east_ports + north_finish]) for p in north_finish: tmp_port = gen_port_from_port(x, y0, p) ports_to_route.append(tmp_port) elements += [ routing_method(p, tmp_port, start_straight=start_straight + y_max - p.y, **conn_params) ] x += sep start_straight += sep # Add south ports ports = [flip(p) for p in ports_to_route] + south_ports return elements, ports