[docs]defroute_south(component:Component,optical_routing_type:int=1,excluded_ports:tuple[str,...]|None=None,straight_separation:float=4.0,io_gratings_lines:list[list[ComponentReference]]|None=None,gc_port_name:str="o1",bend:ComponentSpec=bend_euler,straight:ComponentSpec=straight_function,taper:ComponentSpec|None=taper_function,select_ports:Callable=select_ports_optical,port_names:Strs|None=None,cross_section:CrossSectionSpec=strip,min_length:float=10e-3,auto_widen:bool=False,auto_widen_minimum_length:float=100,taper_length:float=10,width_wide:float=2,**kwargs,)->Routes:"""Returns Routes to route a component ports to the south. Args: component: component to route. optical_routing_type: routing heuristic `1` or `2` \ `1` uses the component size info to estimate the box size.\ `2` only looks at the optical port positions to estimate the size. excluded_ports: list of port names to NOT route. straight_separation: in um. io_gratings_lines: list of ports to which the ports produced by this function will be connected. \ Supplying this information helps avoiding straight collisions. gc_port_name: grating coupler port name. bend: spec. straight: spec. taper: spec. select_ports: function to select_ports. port_names: optional port names. Overrides select_ports. cross_section: cross_section spec. kwargs: cross_section settings. Works well if the component looks roughly 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. .. plot:: :include-source: import gdsfactory as gf c = gf.components.ring_double() c = gf.Component() ref = c << gf.components.ring_double() r = gf.routing.route_south(ref) for e in r.references: c.add(e) c.plot() """xs=gf.get_cross_section(cross_section)excluded_ports=excluded_portsor[]assertoptical_routing_typein{1,2,},f"optical_routing_type = {optical_routing_type}, not supported "ifport_names:optical_ports=[component[port_name]forport_nameinport_names]else:optical_ports=list(select_ports(component.ports).values())optical_ports=[pforpinoptical_portsifp.namenotinexcluded_ports]csi=component.size_inforeferences=[]lengths=[]bend90=bend(cross_section=cross_section,**kwargs)ifcallable(bend)elsebendbend90=gf.get_component(bend90)dy=abs(bend90.info["dy"])# Handle empty list gracefullyifnotoptical_ports:return[],[]conn_params=dict(bend=bend,straight=straight,taper=taper,cross_section=cross_section,min_straight_length=min_length,auto_widen=auto_widen,auto_widen_minimum_length=auto_widen_minimum_length,taper_length=taper_length,width_wide=width_wide,**kwargs,)# Used to avoid crossing between straights in special cases# This could happen when abs(x_port - x_grating) <= 2 * dydelta_gr_min=2*dy+1sep=straight_separation# Get lists of optical ports by orientationdirection_ports=direction_ports_from_list_ports(optical_ports)north_ports=direction_ports["N"]north_start=north_ports[: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 leftnorth_start.reverse()# Sort right to leftordered_ports=north_start+west_ports+south_ports+east_ports+north_finishdefget_index_port_closest_to_x(x,list_ports):returnnp.array([abs(x-p.ports[gc_port_name].x)forpinlist_ports]).argmin()defgen_port_from_port(x,y,p,cross_section):returnPort(name=p.name,center=(x,y),orientation=90.0,width=p.width,cross_section=cross_section,)west_ports.reverse()y0=min(p.yforpinordered_ports)-dy-0.5ports_to_route=[]i=0optical_xs_tmp=[p.xforpinordered_ports]x_optical_min=min(optical_xs_tmp)x_optical_max=max(optical_xs_tmp)# Set starting ``x`` on the west side# ``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``ifoptical_routing_type==1:# use component size to know how far to routex=csi.west-dy-1elifoptical_routing_type==2:# use optical port to know how far to routex=x_optical_min-dy-1else:raiseValueError(f"Invalid optical routing type {optical_routing_type!r} not in [1, 2]")# First route the ports facing west# In case we have to connect these ports to a line of gratings,# Ensure that the port is aligned with the grating port or# has enough space for manhattan routing (at least two bend radius)forpinwest_ports:ifio_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].xifabs(x-x_gr)<delta_gr_min:ifx>x_gr:x=x_grelifx<x_gr:x=x_gr-delta_gr_mintmp_port=gen_port_from_port(x,y0,p,cross_section=xs)ports_to_route.append(tmp_port)route=get_route(input_port=p,output_port=tmp_port,**conn_params)references.extend(route.references)lengths.append(route.length)x-=sepi+=1start_straight_length=0.5# First-half of north ports# This ensures that north ports are routed above the top west onenorth_start.reverse()# We need them from left to rightiflen(north_start)>0:y_max=max(p.yforpinwest_ports+north_start)forpinnorth_start:tmp_port=gen_port_from_port(x,y0,p,cross_section=xs)route=get_route(input_port=p,output_port=tmp_port,start_straight_length=start_straight_length+y_max-p.y,**conn_params,)references.extend(route.references)lengths.append(route.length)ports_to_route.append(tmp_port)x-=sepstart_straight_length+=sep# Set starting ``x`` on the east sideifoptical_routing_type==1:# use component size to know how far to routex=csi.east+dy+1elifoptical_routing_type==2:# use optical port to know how far to routex=x_optical_max+dy+1else:raiseValueError(f"Invalid optical routing type. Got {optical_routing_type}, only (1, 2 supported) ")i=0# Route the east ports# In case we have to connect these ports to a line of gratings,# Ensure that the port is aligned with the grating port or# has enough space for manhattan routing (at least two bend radius)start_straight_length=0.5forpineast_ports:ifio_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].xifabs(x-x_gr)<delta_gr_min:ifx<x_gr:x=x_grelifx>x_gr:x=x_gr+delta_gr_mintmp_port=gen_port_from_port(x,y0,p,cross_section=xs)route=get_route(p,tmp_port,start_straight_length=start_straight_length,**conn_params)references.extend(route.references)lengths.append(route.length)ports_to_route.append(tmp_port)x+=sepi+=1# Route the remaining north portsstart_straight_length=0.5iflen(north_finish)>0:y_max=max(p.yforpineast_ports+north_finish)forpinnorth_finish:tmp_port=gen_port_from_port(x,y0,p,cross_section=xs)ports_to_route.append(tmp_port)route=get_route(input_port=p,output_port=tmp_port,start_straight_length=start_straight_length+y_max-p.y,**conn_params,)references.extend(route.references)lengths.append(route.length)x+=sepstart_straight_length+=sep# Add south portsports=[flip(p)forpinports_to_route]+south_portsreturnRoutes(references=references,ports=ports,lengths=lengths)
if__name__=="__main__":# c = gf.components.mmi2x2()# c = gf.components.ring_single()c=gf.components.ring_double()layer=(2,0)c=gf.Component()ref=c<<gf.components.ring_double(layer=layer)r=route_south(ref,bend=gf.components.bend_euler,layer=layer)foreinr.references:c.add(e)print(r.lengths)c.show(show_ports=True)