def crossing_arm( width: float = 0.5, r1: float = 3.0, r2: float = 1.1, w: float = 1.2, L: float = 3.4, ) -> Component: """arm of a crossing""" c = Component() _ellipse = ellipse(radii=(r1, r2), layer=LAYER.SLAB150).ref() c.add(_ellipse) c.absorb(_ellipse) a = np.round(L + w / 2, 3) h = width / 2 taper_pts = [ (-a, h), (-w / 2, w / 2), (w / 2, w / 2), (a, h), (a, -h), (w / 2, -w / 2), (-w / 2, -w / 2), (-a, -h), ] c.add_polygon(taper_pts, layer=LAYER.WG) c.add_port( name="o1", midpoint=(-a, 0), orientation=180, width=width, layer=LAYER.WG ) c.add_port(name="o2", midpoint=(a, 0), orientation=0, width=width, layer=LAYER.WG) return c
def coupler90bend( radius: float = 10.0, gap: float = 0.2, bend: ComponentFactory = bend_euler, cross_section_inner: CrossSectionFactory = strip, cross_section_outer: CrossSectionFactory = strip, ) -> Component: r"""Returns 2 coupled bends. Args: radius: um gap: um bend: for bend cross_section_inner: cross_section_outer: .. code:: r 3 4 | | | | / / | / / 2____/ / 1_____/ """ c = Component() xi = cross_section_inner() xo = cross_section_outer() width = xo.info["width"] / 2 + xi.info["width"] / 2 spacing = gap + width bend90_inner = bend(radius=radius, cross_section=cross_section_inner) bend90_outer = bend(radius=radius + spacing, cross_section=cross_section_outer) bend_inner_ref = c << bend90_inner bend_outer_ref = c << bend90_outer pbw = bend_inner_ref.ports["o1"] bend_inner_ref.movey(pbw.midpoint[1] + spacing) # This component is a leaf cell => using absorb c.absorb(bend_outer_ref) c.absorb(bend_inner_ref) c.add_port("o1", port=bend_outer_ref.ports["o1"]) c.add_port("o2", port=bend_inner_ref.ports["o1"]) c.add_port("o3", port=bend_inner_ref.ports["o2"]) c.add_port("o4", port=bend_outer_ref.ports["o2"]) return c
def bend_circular(angle: int = 90, npoints: int = 720, with_cladding_box: bool = True, cross_section: CrossSectionOrFactory = strip, **kwargs) -> Component: """Returns a radial arc. Args: angle: angle of arc (degrees) npoints: number of points with_cladding_box: square in layers_cladding to remove DRC cross_section: kwargs: cross_section settings .. code:: o2 | / / / o1_____/ """ x = cross_section(**kwargs) if callable(cross_section) else cross_section radius = x.info["radius"] p = arc(radius=radius, angle=angle, npoints=npoints) c = Component() path = extrude(p, x) ref = c << path c.add_ports(ref.ports) c.info.length = snap_to_grid(p.length()) c.info.dy = float(abs(p.points[0][0] - p.points[-1][0])) c.info.radius = float(radius) if with_cladding_box and x.info["layers_cladding"]: layers_cladding = x.info["layers_cladding"] cladding_offset = x.info["cladding_offset"] top = cladding_offset if angle == 180 else 0 points = get_padding_points( component=c, default=0, bottom=cladding_offset, right=cladding_offset, top=top, ) for layer in layers_cladding or []: c.add_polygon(points, layer=layer) c.absorb(ref) return c
def coupler90(gap: float = 0.2, radius: float = 10.0, bend: ComponentFactory = bend_euler, cross_section: CrossSectionFactory = strip, **kwargs) -> Component: r"""straight coupled to a bend. Args: gap: um radius: um straight: for straight bend: for bend cross_section: kwargs: cross_section settings .. code:: 3 | / / 2_/ 1____4 """ c = Component() x = cross_section(radius=radius, **kwargs) bend90 = (bend(cross_section=cross_section, radius=radius, **kwargs) if callable(bend) else bend) bend_ref = c << bend90 straight_component = (straight( cross_section=cross_section, length=bend90.ports["o2"].midpoint[0] - bend90.ports["o1"].midpoint[0], **kwargs) if callable(straight) else straight) wg_ref = c << straight_component width = x.info["width"] pbw = bend_ref.ports["o1"] bend_ref.movey(pbw.midpoint[1] + gap + width) c.absorb(wg_ref) c.absorb(bend_ref) c.add_port("o1", port=wg_ref.ports["o1"]) c.add_port("o4", port=wg_ref.ports["o2"]) c.add_port("o2", port=bend_ref.ports["o1"]) c.add_port("o3", port=bend_ref.ports["o2"]) return c
def crossing_from_taper(taper=lambda: taper(width2=2.5, length=3.0)): """ Crossing based on a taper. The default is a dummy taper """ taper = taper() if callable(taper) else taper c = Component() for i, a in enumerate([0, 90, 180, 270]): _taper = taper.ref(position=(0, 0), port_id="o2", rotation=a) c.add(_taper) c.add_port(name=i, port=_taper.ports["o1"]) c.absorb(_taper) c.auto_rename_ports() return c
def crossing(arm: ComponentFactory = crossing_arm) -> Component: """Waveguide crossing""" cx = Component() arm = arm() if callable(arm) else arm arm_h = arm.ref() arm_v = arm.ref(rotation=90) port_id = 0 for c in [arm_h, arm_v]: cx.add(c) cx.absorb(c) for p in c.ports.values(): cx.add_port(name=port_id, port=p) port_id += 1 cx.auto_rename_ports() return cx
def loop_mirror(component: ComponentFactory = mmi1x2, bend90: ComponentFactory = bend_euler) -> Component: """Returns Sagnac loop_mirror.""" c = Component() component = gf.call_if_func(component) bend90 = gf.call_if_func(bend90) cref = c.add_ref(component) routes = route_manhattan( cref.ports["o3"], cref.ports["o2"], straight=gf.components.straight, bend=bend90, ) c.add(routes.references) c.add_port(name="o1", port=cref.ports["o1"]) c.absorb(cref) return c
def cdsem_straight_density( width: float = 0.372, trench_width: float = 0.304, x: float = LINE_LENGTH, y: float = 50.0, margin: float = 2.0, label: str = "", straight: ComponentFactory = straight, layer: Tuple[int, int] = LAYER.WG, layers_cladding: Optional[Tuple[Layer, ...]] = None, cross_section: CrossSectionFactory = strip, pixel_size: float = 1.0, ) -> Component: """Horizontal grating etch lines Args: width: width trench_width: trench_width """ c = Component() period = width + trench_width n_o_lines = int((y - 2 * margin) / period) length = x - 2 * margin cross_section = partial(cross_section, width=width, layer=layer) tooth = straight(length=length, cross_section=cross_section) for i in range(n_o_lines): tooth_ref = c.add_ref(tooth) tooth_ref.movey((-n_o_lines / 2 + 0.5 + i) * period) c.absorb(tooth_ref) marker_label = manhattan_text( text=f"{label}", size=pixel_size, layer=layer, ) _marker_label = c.add_ref(marker_label) _marker_label.move((length + 3, 10.0)) c.absorb(_marker_label) return c
def add_frame( component: Component = rectangle, width: float = 10.0, spacing: float = 10.0, layer: Layer = (1, 0), ) -> Component: """Returns component with a frame around it. Args: component: Component to frame width: of the frame spacing: of component to frame """ c = Component() component = component() if callable(component) else component cref = c.add_ref(component) cref.move(-c.size_info.center) y = ( max([component.size_info.height, component.size_info.width]) / 2 + spacing + width / 2 ) x = y w = width rh = rectangle(size=(2 * y + w, w), layer=layer, centered=True) rtop = c.add_ref(rh) rbot = c.add_ref(rh) rtop.movey(+y) rbot.movey(-y) rv = rectangle(size=(w, 2 * y), layer=layer, centered=True) rl = c.add_ref(rv) rr = c.add_ref(rv) rl.movex(-x) rr.movex(+x) c.absorb(cref) return c
def straight(length: float = 10.0, npoints: int = 2, with_cladding_box: bool = True, cross_section: CrossSectionOrFactory = strip, **kwargs) -> Component: """Returns a Straight waveguide. Args: length: straight length npoints: number of points with_cladding_box: box in layers_cladding to avoid DRC sharp edges cross_section: **kwargs: cross_section settings """ p = gf.path.straight(length=length, npoints=npoints) x = cross_section(**kwargs) if callable(cross_section) else cross_section c = Component() path = gf.path.extrude(p, x) ref = c << path c.add_ports(ref.ports) c.info.length = gf.snap.snap_to_grid(length) c.info.width = float(x.info["width"]) if length > 0 and 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) c.absorb(ref) return c
def rectangle_with_slits( size: Tuple[float, float] = (100.0, 200.0), layer: Layer = (1, 0), layer_slit: Optional[Layer] = (2, 0), centered: bool = False, port_type: Optional[str] = None, slit_size: Tuple[float, float] = (1.0, 1.0), slit_spacing: Float2 = (20, 20), slit_enclosure: float = 10, ) -> Component: """Returns a rectangle with slits. Metal slits reduce stress. Args: size: (tuple) Width and height of rectangle. layer: Specific layer to put polygon geometry on. layer_slit: does a boolan NOT when None. centered: True sets center to (0, 0), False sets south-west to (0, 0) port_type: for the rectangle slit_size: x, y slit size slit_spacing: pitch_x, pitch_y for slits slit_enclosure: from slit to rectangle edge .. code:: slit_enclosure _____________________________________ |<---> | | | | ______________________ | | | | | | | | slit_size[1] | |______________________| | | | | | | slit_spacing | | | | size[1] | | ______________________ | | | | | | | | | | | | | |______________________| | | <---------------------> | | slit_size[0] | |___________________________________| size[0] """ c = Component() r = rectangle(size=size, layer=layer, port_type=port_type, centered=centered) c.add_ports(r.ports) slit = rectangle(size=slit_size, port_type=None, layer=layer_slit or layer) columns = np.floor((size[0] - 2 * slit_enclosure) / slit_spacing[0]) rows = np.floor((size[1] - 2 * slit_enclosure) / slit_spacing[1]) slits = array(slit, columns=columns, rows=rows, spacing=slit_spacing).ref() slits.xmin = slit_enclosure slits.ymin = slit_enclosure if layer_slit: c << r c.add(slits) else: r_with_slits = c << gf.geometry.boolean( r, slits, operation="not", layer=layer) c.absorb(r_with_slits) return c
def coupler_asymmetric( bend: ComponentFactory = bend_s, straight: ComponentFactory = straight_function, gap: float = 0.234, dy: float = 5.0, dx: float = 10.0, cross_section: CrossSectionFactory = strip, **kwargs ) -> Component: """bend coupled to straight waveguide Args: bend: straight: straight library gap: um dy: port to port vertical spacing dx: bend length in x direction cross_section: **kwargs: cross_section settings .. code:: dx |-----| _____ o2 / | _____/ | gap o1____________ | dy o3 """ x = cross_section(**kwargs) width = x.info["width"] bend_component = ( bend(size=(dx, dy - gap - width), cross_section=cross_section, **kwargs) if callable(bend) else bend ) wg = ( straight(cross_section=cross_section, **kwargs) if callable(straight) else straight ) w = bend_component.ports["o1"].width y = (w + gap) / 2 c = Component() wg = wg.ref(position=(0, y), port_id="o1") bottom_bend = bend_component.ref(position=(0, -y), port_id="o1", v_mirror=True) c.add(wg) c.add(bottom_bend) # Using absorb here to have a flat cell and avoid # to have deeper hierarchy than needed c.absorb(wg) c.absorb(bottom_bend) port_width = 2 * w + gap c.add_port(name="o1", midpoint=[0, 0], width=port_width, orientation=180) c.add_port(port=bottom_bend.ports["o2"], name="o3") c.add_port(port=wg.ports["o2"], name="o2") return c
def resistance_meander( num_squares: int = 1000, width: float = 1.0, layer: Tuple[int, int] = LAYER.M3, pad: ComponentFactory = pad80, ) -> Component: """meander to test resistance from phidl.geometry FIXME, add pad_pitch Args: pad_size: Size of the two matched impedance pads (microns) num_squares: Number of squares comprising the resonator wire width: The width of the squares (microns) layer: pad: function for pad """ pad = pad() pad_size = pad.info.size x = pad_size[0] z = pad_size[1] # Checking validity of input if x <= 0 or z <= 0: raise ValueError("Pad must have positive, real dimensions") elif width > z: raise ValueError("Width of cell cannot be greater than height of pad") elif num_squares <= 0: raise ValueError("Number of squares must be a positive real number") elif width <= 0: raise ValueError("Width of cell must be a positive real number") # Performing preliminary calculations num_rows = int(np.floor(z / (2 * width))) if num_rows % 2 == 0: num_rows -= 1 num_columns = num_rows - 1 squares_in_row = (num_squares - num_columns - 2) / num_rows # Compensating for weird edge cases if squares_in_row < 1: num_rows = round(num_rows / 2) - 2 squares_in_row = 1 if width * 2 > z: num_rows = 1 squares_in_row = num_squares - 2 length_row = squares_in_row * width # Creating row/column corner combination structure T = Component() Row = pc.rectangle(size=(length_row, width), layer=layer, port_type=None) Col = pc.rectangle(size=(width, width), layer=layer, port_type=None) T.add_ref(Row) col = T.add_ref(Col) col.move([length_row - width, -width]) # Creating entire straight net N = Component("net") n = 1 for i in range(num_rows): if i != num_rows - 1: d = N.add_ref(T) else: d = N.add_ref(Row) if n % 2 == 0: d.reflect(p1=(d.x, d.ymax), p2=(d.x, d.ymin)) d.movey(-(n - 1) * T.ysize) n += 1 d = N.add_ref(Col).movex(-width) d = N.add_ref(Col).move([length_row, -(n - 2) * T.ysize]) # Creating pads P = Component() pad1 = P.add_ref(pad) pad1.movex(-x - width) pad2 = P.add_ref(pad) pad2.movex(length_row + width) pad1.xmax = 0 pad2.xmin = z net = P.add_ref(N) net.y = pad1.y P.absorb(net) P.absorb(pad1) P.absorb(pad2) return P
def coupler_ring( gap: float = 0.2, radius: float = 5.0, length_x: float = 4.0, coupler90: ComponentFactory = coupler90function, bend: Optional[ComponentFactory] = None, coupler_straight: ComponentFactory = coupler_straight_function, cross_section: CrossSectionFactory = strip, **kwargs) -> Component: r"""Coupler for ring. Args: gap: spacing between parallel coupled straight waveguides. radius: of the bends. length_x: length of the parallel coupled straight waveguides. coupler90: straight coupled to a 90deg bend. straight: library for straight waveguides. bend: library for bend coupler_straight: two parallel coupled straight waveguides. cross_section: **kwargs: cross_section settings .. code:: 2 3 | | \ / \ / ---=========--- 1 length_x 4 """ bend = bend or bend_euler c = Component() assert_on_2nm_grid(gap) # define subcells coupler90_component = (coupler90(gap=gap, radius=radius, bend=bend, cross_section=cross_section, **kwargs) if callable(coupler90) else coupler90) coupler_straight_component = (coupler_straight( gap=gap, length=length_x, cross_section=cross_section, ** kwargs) if callable(coupler_straight) else coupler_straight) # add references to subcells cbl = c << coupler90_component cbr = c << coupler90_component cs = c << coupler_straight_component # connect references y = coupler90_component.y cs.connect(port="o4", destination=cbr.ports["o1"]) cbl.reflect(p1=(0, y), p2=(1, y)) cbl.connect(port="o2", destination=cs.ports["o2"]) c.absorb(cbl) c.absorb(cbr) c.absorb(cs) c.add_port("o1", port=cbl.ports["o3"]) c.add_port("o2", port=cbl.ports["o4"]) c.add_port("o3", port=cbr.ports["o3"]) c.add_port("o4", port=cbr.ports["o4"]) c.auto_rename_ports() return c
def coupler_symmetric( bend: ComponentFactory = bend_s, gap: float = 0.234, dy: float = 5.0, dx: float = 10.0, cross_section: CrossSectionFactory = strip, **kwargs, ) -> Component: r"""Two coupled straights with bends. Args: bend: bend or library gap: dy: port to port vertical spacing dx: bend length in x direction cross_section: **kwargs: cross_section settings .. code:: dx |-----| ___ E1 / | _____/ | gap _____ | dy \ | \___ | E0 """ x = cross_section(**kwargs) width = x.info["width"] bend_component = ( bend( size=(dx, (dy - gap - width) / 2), cross_section=cross_section, **kwargs, ) if callable(bend) else bend ) w = bend_component.ports["o1"].width y = (w + gap) / 2 c = Component() top_bend = bend_component.ref(position=(0, y), port_id="o1") bottom_bend = bend_component.ref(position=(0, -y), port_id="o1", v_mirror=True) c.add(top_bend) c.add(bottom_bend) c.absorb(top_bend) c.absorb(bottom_bend) c.add_port("o1", port=bottom_bend.ports["o1"]) c.add_port("o2", port=top_bend.ports["o1"]) c.add_port("o3", port=top_bend.ports["o2"]) c.add_port("o4", port=bottom_bend.ports["o2"]) c.info.length = bend_component.info.length c.info.min_bend_radius = bend_component.info.min_bend_radius return c
def mmi1x2(width: float = 0.5, width_taper: float = 1.0, length_taper: float = 10.0, length_mmi: float = 5.5, width_mmi: float = 2.5, gap_mmi: float = 0.25, taper: ComponentFactory = taper_function, with_cladding_box: bool = True, cross_section: CrossSectionFactory = strip, **kwargs) -> Component: r"""Mmi 1x2. Args: width: input and output straight width width_taper: interface between input straights and mmi region length_taper: into the mmi region length_mmi: in x direction width_mmi: in y direction gap_mmi: gap between tapered wg taper: taper function with_cladding_box: to avoid DRC acute angle errors in cladding cross_section: **kwargs: cross_section settings .. code:: length_mmi <------> ________ | | | \__ | __ E1 __/ /_ _ _ _ W0 __ | _ _ _ _| gap_mmi \ \__ | __ E0 | / |________| <-> length_taper """ gf.snap.assert_on_2nm_grid(gap_mmi) x = cross_section(**kwargs) cladding_offset = x.info["cladding_offset"] layers_cladding = x.info["layers_cladding"] layer = x.info["layer"] c = Component() w_mmi = width_mmi w_taper = width_taper taper = taper(length=length_taper, width1=width, width2=w_taper, cross_section=cross_section, **kwargs) a = gap_mmi / 2 + width_taper / 2 mmi = c << gf.components.rectangle( size=(length_mmi, w_mmi), layer=layer, centered=True, ) ports = [ gf.Port("o1", orientation=180, midpoint=(-length_mmi / 2, 0), width=w_taper), gf.Port("o2", orientation=0, midpoint=(+length_mmi / 2, +a), width=w_taper), gf.Port("o3", orientation=0, midpoint=(+length_mmi / 2, -a), width=w_taper), ] for port in ports: taper_ref = c << taper taper_ref.connect(port="o2", destination=port) c.add_port(name=port.name, port=taper_ref.ports["o1"]) c.absorb(taper_ref) c.absorb(mmi) layers_cladding = layers_cladding or [] if layers_cladding and with_cladding_box: add_padding( c, default=cladding_offset, right=0, left=0, top=cladding_offset, bottom=cladding_offset, layers=layers_cladding, ) return c
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 crossing_etched( width: float = 0.5, r1: float = 3.0, r2: float = 1.1, w: float = 1.2, L: float = 3.4, layer_wg: Layer = LAYER.WG, layer_slab: Layer = LAYER.SLAB150, ): """ Waveguide crossing: - The full crossing has to be on WG layer (to start with a 220nm slab) - Then we etch the ellipses down to 150nm slabs and we keep linear taper at 220nm. What we write is what we etch on this step """ # Draw the ellipses c = Component() _ellipse1 = c << ellipse(radii=(r1, r2), layer=layer_wg) _ellipse2 = c << ellipse(radii=(r2, r1), layer=layer_wg) c.absorb(_ellipse1) c.absorb(_ellipse2) a = L + w / 2 h = width / 2 taper_cross_pts = [ (-a, h), (-w / 2, w / 2), (-h, a), (h, a), (w / 2, w / 2), (a, h), (a, -h), (w / 2, -w / 2), (h, -a), (-h, -a), (-w / 2, -w / 2), (-a, -h), ] c.add_polygon(taper_cross_pts, layer=layer_wg) # tapers_poly = c.add_polygon(taper_cross_pts, layer=layer_wg) # b = a - 0.1 # To make sure we get 4 distinct polygons when doing bool ops # tmp_polygon = [(-b, b), (b, b), (b, -b), (-b, -b)] # polys_etch = gdspy.fast_boolean([tmp_polygon], tapers_poly, "not", layer=layer_slab) # c.add(polys_etch) positions = [(a, 0), (0, a), (-a, 0), (0, -a)] angles = [0, 90, 180, 270] i = 0 for p, angle in zip(positions, angles): c.add_port( name=str(i), midpoint=p, orientation=angle, width=width, layer=layer_wg, ) i += 1 c.auto_rename_ports() return c
def coupler(gap: float = 0.236, length: float = 20.0, coupler_symmetric: ComponentFactory = coupler_symmetric_function, coupler_straight: ComponentFactory = coupler_straight_function, dy: float = 5.0, dx: float = 10.0, cross_section: CrossSectionFactory = strip, **kwargs) -> Component: r"""Symmetric coupler. Args: gap: between straights length: of coupling region coupler_symmetric coupler_straight dy: port to port vertical spacing dx: length of bend in x direction cross_section: factory kwargs: cross_section settings .. code:: dx dx |------| |------| o2 ________ ______o3 \ / | \ length / | ======================= gap | dy / \ | ________/ \_______ | o1 o4 coupler_straight coupler_symmetric """ length = snap_to_grid(length) assert_on_2nm_grid(gap) c = Component() sbend = coupler_symmetric(gap=gap, dy=dy, dx=dx, cross_section=cross_section, **kwargs) sr = c << sbend sl = c << sbend cs = c << coupler_straight( length=length, gap=gap, cross_section=cross_section, **kwargs) sl.connect("o2", destination=cs.ports["o1"]) sr.connect("o1", destination=cs.ports["o4"]) c.add_port("o1", port=sl.ports["o3"]) c.add_port("o2", port=sl.ports["o4"]) c.add_port("o3", port=sr.ports["o3"]) c.add_port("o4", port=sr.ports["o4"]) c.absorb(sl) c.absorb(sr) c.absorb(cs) c.info.length = sbend.info.length c.info.min_bend_radius = sbend.info.min_bend_radius c.auto_rename_ports() return c
def bend_euler(angle: int = 90, p: float = 0.5, with_arc_floorplan: bool = True, npoints: int = 720, direction: str = "ccw", with_cladding_box: bool = True, cross_section: CrossSectionOrFactory = strip, **kwargs) -> Component: """Returns an euler bend that adiabatically transitions from straight to curved. By default, `radius` corresponds to the minimum radius of curvature of the bend. However, if `with_arc_floorplan` is True, `radius` corresponds to the effective radius of curvature (making the curve a drop-in replacement for an arc). If p < 1.0, will create a "partial euler" curve as described in Vogelbacher et. al. https://dx.doi.org/10.1364/oe.27.031394 default p = 0.5 based on this paper https://www.osapublishing.org/oe/fulltext.cfm?uri=oe-25-8-9150&id=362937 Args: angle: total angle of the curve p: Proportion of the curve that is an Euler curve with_arc_floorplan: If False: `radius` is the minimum radius of curvature If True: The curve scales such that the endpoints match a bend_circular with parameters `radius` and `angle` npoints: Number of points used per 360 degrees direction: cw (clock-wise) or ccw (counter clock-wise) with_cladding_box: to avoid DRC acute angle errors in cladding cross_section: kwargs: cross_section settings .. code:: o2 | / / / o1_____/ """ x = cross_section(**kwargs) if callable(cross_section) else cross_section radius = x.info["radius"] c = Component() p = euler(radius=radius, angle=angle, p=p, use_eff=with_arc_floorplan, npoints=npoints) ref = c << extrude(p, x) c.add_ports(ref.ports) c.info.length = snap_to_grid(p.length()) c.info.dy = abs(float(p.points[0][0] - p.points[-1][0])) c.info.radius_min = float(snap_to_grid(p.info["Rmin"])) c.info.radius = float(radius) if with_cladding_box and x.info["layers_cladding"]: layers_cladding = x.info["layers_cladding"] cladding_offset = x.info["cladding_offset"] top = cladding_offset if angle == 180 else 0 points = get_padding_points( component=c, default=0, bottom=cladding_offset, right=cladding_offset, top=top, ) for layer in layers_cladding or []: c.add_polygon(points, layer=layer) if direction == "cw": ref.mirror(p1=[0, 0], p2=[1, 0]) c.absorb(ref) return c
def resistance_meander( pad_size: Tuple[float] = (50.0, 50.0), num_squares: int = 1000, width: float = 1.0, res_layer: Tuple[int, int] = LAYER.M3, pad_layer: Tuple[int, int] = LAYER.M3, gnd_layer: Tuple[int, int] = LAYER.M3, ) -> Component: """meander to test resistance from phidl.geometry Args: pad_size: Size of the two matched impedance pads (microns) num_squares: Number of squares comprising the resonator wire width: The width of the squares (microns) res_layer: pad_layer: gnd_layer: """ x = pad_size[0] z = pad_size[1] # Checking validity of input if x <= 0 or z <= 0: raise ValueError("Pad must have positive, real dimensions") elif width > z: raise ValueError("Width of cell cannot be greater than height of pad") elif num_squares <= 0: raise ValueError("Number of squares must be a positive real number") elif width <= 0: raise ValueError("Width of cell must be a positive real number") # Performing preliminary calculations num_rows = int(np.floor(z / (2 * width))) if num_rows % 2 == 0: num_rows -= 1 num_columns = num_rows - 1 squares_in_row = (num_squares - num_columns - 2) / num_rows # Compensating for weird edge cases if squares_in_row < 1: num_rows = round(num_rows / 2) - 2 squares_in_row = 1 if width * 2 > z: num_rows = 1 squares_in_row = num_squares - 2 length_row = squares_in_row * width # Creating row/column corner combination structure T = Component() Row = pc.rectangle(size=(length_row, width), layer=res_layer) Col = pc.rectangle(size=(width, width), layer=res_layer) T.add_ref(Row) col = T.add_ref(Col) col.move([length_row - width, -width]) # Creating entire straight net N = Component("net") n = 1 for i in range(num_rows): if i != num_rows - 1: d = N.add_ref(T) else: d = N.add_ref(Row) if n % 2 == 0: d.reflect(p1=(d.x, d.ymax), p2=(d.x, d.ymin)) d.movey(-(n - 1) * T.ysize) n += 1 d = N.add_ref(Col).movex(-width) d = N.add_ref(Col).move([length_row, -(n - 2) * T.ysize]) # Creating pads P = Component() pad1 = pc.rectangle(size=(x, z), layer=pad_layer) pad2 = pc.rectangle(size=(x + 5, z), layer=pad_layer) gnd1 = offset(pad1, distance=-5, layer=gnd_layer) gnd2 = offset(pad2, distance=-5, layer=gnd_layer) pad1_ref = P.add_ref(pad1) pad1_ref.movex(-x - width) pad2_ref = P.add_ref(pad1) pad2_ref.movex(length_row + width) gnd1_ref = P.add_ref(gnd1) gnd1_ref.center = pad1_ref.center gnd2_ref = P.add_ref(gnd2) net = P.add_ref(N) net.y = pad1_ref.y gnd2_ref.center = pad2_ref.center gnd2_ref.movex(2.5) P.absorb(net) P.absorb(gnd1_ref) P.absorb(gnd2_ref) P.absorb(pad1_ref) P.absorb(pad2_ref) return P