def bend_s(width=0.5, height=2, length=10, layer=pp.LAYER.WG, nb_points=99): """ S bend Based on bezier curve Args: width height: in y direction length: in x direction layer: gds number nb_points: number of points .. plot:: :include-source: import pp c = pp.c.bend_s(width=0.5, height=20) pp.plotgds(c) """ l, h = length, height c = bezier( width=width, control_points=[(0, 0), (l / 2, 0), (l / 2, h), (l, h)], t=np.linspace(0, 1, nb_points), layer=layer, ) c.add_port(name="W0", port=c.ports.pop("0")) c.add_port(name="E0", port=c.ports.pop("1")) # c.ports["W0"] = c.ports.pop("0") # c.ports["E0"] = c.ports.pop("1") return c
def bend_s_biased(width=0.5, height=2, length=10, layer=pp.LAYER.WG, nb_points=99): l, h = length, height return bezier( width=pp.bias.width(width), control_points=[(0, 0), (l / 2, 0), (l / 2, h), (l, h)], t=np.linspace(0, 1, nb_points), layer=layer, )
def bend_s( width: float = 0.5, height: float = 2.0, length: float = 10.0, layer: Tuple[int, int] = pp.LAYER.WG, nb_points: int = 99, layers_cladding: List[Tuple[int, int]] = [pp.LAYER.WGCLAD], cladding_offset: float = 3.0, ) -> Component: """ S bend Based on bezier curve Args: width height: in y direction length: in x direction layer: gds number nb_points: number of points .. plot:: :include-source: import pp c = pp.c.bend_s(width=0.5, height=20) pp.plotgds(c) """ l, h = length, height c = bezier( width=width, control_points=[(0, 0), (l / 2, 0), (l / 2, h), (l, h)], t=np.linspace(0, 1, nb_points), layer=layer, ) c.add_port(name="W0", port=c.ports.pop("0")) c.add_port(name="E0", port=c.ports.pop("1")) y = cladding_offset points = [ [c.xmin, c.ymin - y], [c.xmax, c.ymin - y], [c.xmax, c.ymax + y], [c.xmin, c.ymax + y], ] for layer in layers_cladding: c.add_polygon(points, layer=layer) # c.ports["W0"] = c.ports.pop("0") # c.ports["E0"] = c.ports.pop("1") return c
def package_optical2x2( component: Component, port_spacing: float = 20.0, bend_length: Optional[float] = None, ): """returns component with port_spacing""" comp = component() if callable(component) else component if bend_length is None: bend_length = port_spacing dx = bend_length dy = port_spacing / 2.0 p_w0 = comp.ports["W0"].midpoint p_e0 = comp.ports["E0"].midpoint p_w1 = comp.ports["W1"].midpoint p_e1 = comp.ports["E1"].midpoint c = pp.Component(f"{comp.name}_{int(port_spacing)}") c << comp t = np.linspace(0, 1, 101) y0 = p_e1[1] control_points = [(0, 0), (dx / 2, 0), (dx / 2, dy - y0), (dx, dy - y0)] bezier_bend_t = bezier(control_points=control_points, t=t, start_angle=0, end_angle=0) b_tr = bezier_bend_t.ref(port_id="0", position=p_e1) b_br = bezier_bend_t.ref(port_id="0", position=p_e0, v_mirror=True) b_tl = bezier_bend_t.ref(port_id="0", position=p_w1, h_mirror=True) b_bl = bezier_bend_t.ref(port_id="0", position=p_w0, rotation=180) c.add([b_tr, b_br, b_tl, b_bl]) c.add_port("W0", port=b_bl.ports["1"]) c.add_port("W1", port=b_tl.ports["1"]) c.add_port("E0", port=b_br.ports["1"]) c.add_port("E1", port=b_tr.ports["1"]) c.info["min_bend_radius"] = bezier_bend_t.info["min_bend_radius"] for pname, p in c.ports.items(): if p.port_type != "optical": c.add_port(pname, port=p) return c
def package_optical2x2(component, port_spacing=20.0, bend_length=None): """ returns component with port_spacing """ component = pp.call_if_func(component) if bend_length is None: bend_length = port_spacing dx = bend_length dy = port_spacing / 2 p_w0 = component.ports["W0"].midpoint p_e0 = component.ports["E0"].midpoint p_w1 = component.ports["W1"].midpoint p_e1 = component.ports["E1"].midpoint c = pp.Component() c.add_ref(component) t = np.linspace(0, 1, 101) y0 = p_e1[1] control_points = [(0, 0), (dx / 2, 0), (dx / 2, dy - y0), (dx, dy - y0)] bezier_bend_t = bezier(control_points=control_points, t=t, start_angle=0, end_angle=0) b_tr = bezier_bend_t.ref(port_id="0", position=p_e1) b_br = bezier_bend_t.ref(port_id="0", position=p_e0, v_mirror=True) b_tl = bezier_bend_t.ref(port_id="0", position=p_w1, h_mirror=True) b_bl = bezier_bend_t.ref(port_id="0", position=p_w0, rotation=180) c.add([b_tr, b_br, b_tl, b_bl]) c.add_port("W0", port=b_bl.ports["1"]) c.add_port("W1", port=b_tl.ports["1"]) c.add_port("E0", port=b_br.ports["1"]) c.add_port("E1", port=b_tr.ports["1"]) c.info["min_bend_radius"] = bezier_bend_t.info["min_bend_radius"] return c
def compensation_path(crossing45=crossing45, direction="top"): r""" Path with same path length as crossing45 but with input and output ports having same y coordinates Args: name: component name crossing45: the crossing45 component that we want to match in path length This component needs to have .info["components"] with bends and crossing direction: the direction in which the bend should go "top" / "bottom" Returns: <pp.Component> a compensation path crossing45: .. code:: ---- ---- \ / \ / \ / X / \ / \ / \ ---- ---- Compensation path: .. code:: --+-- _/ \_ --/ \-- """ # Get total path length taken by the bends crossing45 = pp.call_if_func(crossing45) X45_cmps = crossing45.info["components"] length = 2 * X45_cmps["bezier_bend"].info["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["E0"].x - cmp.ports["W0"].x x_span_crossing45 = get_x_span(crossing45) x_span_crossing = get_x_span(X45_cmps["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 = rnd(y_bend) if direction == "top": v_mirror = False else: v_mirror = True sbend = bezier(control_points=get_control_pts(x0, y_bend)) component = pp.Component() crossing0 = X45_cmps["crossing"].ref() component.add(crossing0) sbend_left = sbend.ref(position=crossing0.ports["W0"], port_id="1", v_mirror=v_mirror) sbend_right = sbend.ref(position=crossing0.ports["E0"], port_id="1", h_mirror=True, v_mirror=v_mirror) component.add(sbend_left) component.add(sbend_right) # Add ports component.add_port("W0", port=sbend_left.ports["0"]) component.add_port("E0", port=sbend_right.ports["0"]) component.info["min_bend_radius"] = sbend.info["min_bend_radius"] component.info["components"] = {"sbend": sbend} return component
def crossing45(crossing=crossing, port_spacing=40.0, dx=None, alpha=0.08): r""" 45Deg crossing with bends Args: crossing: 90D crossing port_spacing: target I/O port spacing dx: target length alpha: optimization parameter. Try with 0.1 to start with. - If the structure has too tight bends, diminish it. - If raise assertion angle errors, increase it 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 = pp.Component() _crossing = crossing.ref(rotation=45) c.add(_crossing) # Add bends p_e = _crossing.ports["E0"].midpoint p_w = _crossing.ports["W0"].midpoint p_n = _crossing.ports["N0"].midpoint p_s = _crossing.ports["S0"].midpoint # Flatten the crossing - not an SRef anymore c.absorb(_crossing) if dx is None: dx = port_spacing dy = port_spacing / 2 t = np.linspace(0, 1, 101) 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, t=t, alpha=alpha, ) _bez_bend = bezier( control_points=cpts, t=t, start_angle=start_angle, end_angle=end_angle, ) tol = 1e-2 assert abs(_bez_bend.info["start_angle"] - start_angle) < tol, _bez_bend.info["start_angle"] assert abs(_bez_bend.info["end_angle"] - end_angle) < tol, _bez_bend.info["end_angle"] # print(abs(_bez_bend.info["start_angle"] - start_angle)) # print(abs(_bez_bend.info["end_angle"] - end_angle)) b_tr = _bez_bend.ref(position=p_e, port_id="0") b_tl = _bez_bend.ref(position=p_n, port_id="0", h_mirror=True) b_bl = _bez_bend.ref(position=p_w, port_id="0", rotation=180) b_br = _bez_bend.ref(position=p_s, port_id="0", 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["components"] = {"bezier_bend": _bez_bend, "crossing": crossing} c.info["min_bend_radius"] = b_br.info["min_bend_radius"] # print (b_tr.info["min_bend_radius"]) c.add_port("E0", port=b_br.ports["1"]) c.add_port("E1", port=b_tr.ports["1"]) c.add_port("W0", port=b_bl.ports["1"]) c.add_port("W1", port=b_tl.ports["1"]) c.snap_ports_to_grid() return c