def bend_s(size: Float2 = (10.0, 2.0), nb_points: int = 99, with_cladding_box: bool = True, cross_section: CrossSectionFactory = strip, **kwargs) -> Component: """S bend with bezier curve stores min_bend_radius property in self.info['min_bend_radius'] min_bend_radius depends on height and length Args: size: in x and y direction nb_points: number of points with_cladding_box: square bounding box to avoid DRC errors cross_section: function kwargs: cross_section settings """ dx, dy = size x = cross_section(**kwargs) width = x.info["width"] layer = x.info["layer"] c = Component() bend = bezier( width=width, control_points=[(0, 0), (dx / 2, 0), (dx / 2, dy), (dx, dy)], npoints=nb_points, layer=layer, ) bend_ref = c << bend c.add_ports(bend_ref.ports) c.copy_child_info(bend) c.info.start_angle = bend.info.start_angle c.info.end_angle = bend.info.end_angle c.info.length = bend.info.length c.info.min_bend_radius = bend.info.min_bend_radius if with_cladding_box and x.info["layers_cladding"]: layers_cladding = x.info["layers_cladding"] cladding_offset = x.info["cladding_offset"] points = get_padding_points( component=c, default=0, bottom=cladding_offset, top=cladding_offset, ) for layer in layers_cladding or []: c.add_polygon(points, layer=layer) auto_rename_ports(c) return c
def fanout2x2(component: ComponentOrFactory, port_spacing: float = 20.0, bend_length: Optional[float] = None, npoints: int = 101, select_ports: Callable = select_ports_optical, cross_section: CrossSectionFactory = strip, with_cladding_box: bool = True, **kwargs) -> Component: """returns component with port_spacing. Args: component: to package port_spacing: for the returned component bend_length: length of the bend (defaults to port_spacing) npoints: for sbend select_ports: function to select optical_ports ports cross_section: with_cladding_box: square bounding box to avoid DRC errors Keyword Args: cross_section settings """ c = gf.Component() component = component() if callable(component) else component component.component = component ref = c << component ref.movey(-ref.y) if bend_length is None: bend_length = port_spacing dx = bend_length y = port_spacing / 2.0 p_w0 = ref.ports["o1"].midpoint p_w1 = ref.ports["o2"].midpoint p_e1 = ref.ports["o3"].midpoint p_e0 = ref.ports["o4"].midpoint y0 = p_e1[1] dy = y - y0 control_points = [(0, 0), (dx / 2, 0), (dx / 2, dy), (dx, dy)] x = cross_section(**kwargs) width = x.info["width"] layer = x.info["layer"] bend = bezier( control_points=control_points, npoints=npoints, start_angle=0, end_angle=0, width=width, layer=layer, ) if with_cladding_box and x.info["layers_cladding"]: layers_cladding = x.info["layers_cladding"] cladding_offset = x.info["cladding_offset"] bend.unlock() points = get_padding_points( component=bend, default=0, bottom=cladding_offset, top=cladding_offset, ) for layer in layers_cladding or []: bend.add_polygon(points, layer=layer) b_tr = bend.ref(port_id="o1", position=p_e1) b_br = bend.ref(port_id="o1", position=p_e0, v_mirror=True) b_tl = bend.ref(port_id="o1", position=p_w1, h_mirror=True) b_bl = bend.ref(port_id="o1", position=p_w0, rotation=180) c.add([b_tr, b_br, b_tl, b_bl]) c.add_port("o1", port=b_bl.ports["o2"]) c.add_port("o2", port=b_tl.ports["o2"]) c.add_port("o3", port=b_tr.ports["o2"]) c.add_port("o4", port=b_br.ports["o2"]) c.min_bend_radius = bend.info["min_bend_radius"] optical_ports = select_ports(ref.ports) for port_name in ref.ports.keys(): if port_name not in optical_ports: c.add_port(port_name, port=ref.ports[port_name]) c.copy_child_info(component) return c
def compensation_path(crossing45: ComponentOrFactory = crossing45, direction: str = "top") -> Component: r"""Returns Component Path with same path length as the crossing with input and output ports having same y coordinates Args: crossing45: component that we want to match in path length needs to have .info["components"] with bends and crossing direction: the direction in which the bend should go "top" / "bottom" .. code:: ---- ---- \ / \ / \ / X / \ / \ / \ ---- ---- Compensation path: .. code:: --+-- _/ \_ --/ \-- """ # Get total path length taken by the bends crossing45 = crossing45() if callable(crossing45) else crossing45 bezier_length = crossing45.info.bezier_length length = 2 * bezier_length # Find a bezier S-bend with half this length, but with a fixed length # governed by the crossing45 X-distance (west to east ports) and # the crossing x_distance target_bend_length = length / 2 def get_x_span(cmp): return cmp.ports["o3"].x - cmp.ports["o1"].x x_span_crossing45 = get_x_span(crossing45) x_span_crossing = get_x_span(crossing45.crossing) # x span allowed for the bend x0 = (x_span_crossing45 - x_span_crossing) / 2 def get_control_pts(x, y): return [(0, 0), (x0 / 2, 0), (x0 / 2, y), (x0, y)] def f(y): control_points = get_control_pts(x0, y) t = np.linspace(0, 1, 51) path_points = bezier_curve(t, control_points) return path_length(path_points) - target_bend_length # the path length of the s-bend between two ports p0 and p1 is : # - larger than the euclidian distance L2(p0, p1) # - smaller than the manhattan distance DL(p0, p1) # # This gives the bounds for the brentq root finding ya = target_bend_length - x0 yb = np.sqrt(target_bend_length**2 - x0**2) solution = so.root_scalar(f, bracket=[ya, yb], method="brentq") y_bend = solution.root y_bend = snap_to_grid(y_bend) if direction == "top": v_mirror = False else: v_mirror = True sbend = bezier(control_points=get_control_pts(x0, y_bend)) component = Component() crossing0 = crossing45.crossing.ref() component.add(crossing0) sbend_left = sbend.ref(position=crossing0.ports["o1"], port_id="o2", v_mirror=v_mirror) sbend_right = sbend.ref(position=crossing0.ports["o3"], port_id="o2", h_mirror=True, v_mirror=v_mirror) component.add(sbend_left) component.add(sbend_right) component.add_port("o1", port=sbend_left.ports["o1"]) component.add_port("o2", port=sbend_right.ports["o1"]) component.info["min_bend_radius"] = sbend.info["min_bend_radius"] component.info.sbend = sbend.info return component
def crossing45( crossing: ComponentFactory = crossing, port_spacing: float = 40.0, dx: Optional[float] = None, alpha: float = 0.08, npoints: int = 101, ) -> Component: r"""Returns 45deg crossing with bends. Args: crossing: crossing function port_spacing: target I/O port spacing dx: target length alpha: optimization parameter. diminish it for tight bends, increase it if raises assertion angle errors npoints: number of points. Implementation note: The 45 Degree crossing CANNOT be kept as an SRef since we only allow for multiples of 90Deg rotations in SRef .. code:: ---- ---- \ / X / \ --- ---- """ crossing = crossing() if callable(crossing) else crossing c = Component() _crossing = crossing.ref(rotation=45) c.add(_crossing) # Add bends p_e = _crossing.ports["o3"].midpoint p_w = _crossing.ports["o1"].midpoint p_n = _crossing.ports["o2"].midpoint p_s = _crossing.ports["o4"].midpoint # Flatten the crossing - not an SRef anymore c.absorb(_crossing) dx = dx or port_spacing dy = port_spacing / 2 start_angle = 45 end_angle = 0 cpts = find_min_curv_bezier_control_points( start_point=p_e, end_point=(dx, dy), start_angle=start_angle, end_angle=end_angle, npoints=npoints, alpha=alpha, ) bend = bezier( control_points=cpts, start_angle=start_angle, end_angle=end_angle, npoints=npoints, ) tol = 1e-2 assert abs(bend.info["start_angle"] - start_angle) < tol, bend.info["start_angle"] assert abs(bend.info["end_angle"] - end_angle) < tol, bend.info["end_angle"] b_tr = bend.ref(position=p_e, port_id="o1") b_tl = bend.ref(position=p_n, port_id="o1", h_mirror=True) b_bl = bend.ref(position=p_w, port_id="o1", rotation=180) b_br = bend.ref(position=p_s, port_id="o1", v_mirror=True) for cmp_ref in [b_tr, b_br, b_tl, b_bl]: # cmp_ref = _cmp.ref() c.add(cmp_ref) c.absorb(cmp_ref) c.info.bezier_length = bend.info.length c.info.crossing = crossing.info c.info.min_bend_radius = b_br.info.min_bend_radius c.bezier = bend c.crossing = crossing c.add_port("o1", port=b_br.ports["o2"]) c.add_port("o2", port=b_tr.ports["o2"]) c.add_port("o3", port=b_bl.ports["o2"]) c.add_port("o4", port=b_tl.ports["o2"]) c.snap_ports_to_grid() c.auto_rename_ports() return c
def fanout2x2( component: Component, port_spacing: float = 20.0, bend_length: Optional[float] = None, npoints: int = 101, select_ports: Callable = select_ports_optical, ) -> Component: """returns component with port_spacing. Args: component: to package port_spacing: for the returned component bend_length: length of the bend (defaults to port_spacing) npoints: for sbend select_ports: function to select optical_ports ports """ c = gf.Component() component = component() if callable(component) else component component.component = component ref = c << component ref.movey(-ref.y) if bend_length is None: bend_length = port_spacing dx = bend_length y = port_spacing / 2.0 p_w0 = ref.ports["o1"].midpoint p_w1 = ref.ports["o2"].midpoint p_e1 = ref.ports["o3"].midpoint p_e0 = ref.ports["o4"].midpoint y0 = p_e1[1] dy = y - y0 control_points = [(0, 0), (dx / 2, 0), (dx / 2, dy), (dx, dy)] bezier_bend_t = bezier(control_points=control_points, npoints=npoints, start_angle=0, end_angle=0) b_tr = bezier_bend_t.ref(port_id="o1", position=p_e1) b_br = bezier_bend_t.ref(port_id="o1", position=p_e0, v_mirror=True) b_tl = bezier_bend_t.ref(port_id="o1", position=p_w1, h_mirror=True) b_bl = bezier_bend_t.ref(port_id="o1", position=p_w0, rotation=180) c.add([b_tr, b_br, b_tl, b_bl]) c.add_port("o1", port=b_bl.ports["o2"]) c.add_port("o2", port=b_tl.ports["o2"]) c.add_port("o3", port=b_tr.ports["o2"]) c.add_port("o4", port=b_br.ports["o2"]) c.min_bend_radius = bezier_bend_t.info["min_bend_radius"] optical_ports = select_ports(ref.ports) for port_name in ref.ports.keys(): if port_name not in optical_ports: c.add_port(port_name, port=ref.ports[port_name]) c.copy_child_info(component) return c