def wg_deep_rib(width=0.5, layer=pp.layer("slab90"), layer_cladding=pp.layer("slab90clad"), **kwargs): width = pp.bias.width(width) return waveguide(width=width, layer=layer, layer_cladding=layer_cladding, **kwargs)
def waveguide_trenches( length=10.0, width=0.5, layer=pp.layer("wgcore"), trench_width=3.0, trench_offset=0.2, trench_layer=pp.layer("slab90"), ): width = pp.bias.width(width) w = width / 2 ww = w + trench_width wt = ww + trench_offset windows = [(-ww, ww, layer), (-wt, -w, trench_layer), (w, wt, trench_layer)] return _arbitrary_straight_waveguide(length=length, windows=windows)
def align_wafer( width=10, spacing=10, cross_length=80, layer=pp.LAYER.WG, with_tile_excl=True, square_corner="bottom_left", ): """ returns cross inside a frame to align wafer .. plot:: :include-source: import pp c = pp.c.fidutial() pp.plotgds(c) """ c = pp.Component() cross = pp.c.cross(length=cross_length, width=width, layer=layer) c.add_ref(cross) b = cross_length / 2 + spacing + width / 2 w = width c.add rh = rectangle_centered(w=2 * b + w, h=w, layer=layer) rtop = c.add_ref(rh) rbot = c.add_ref(rh) rtop.movey(+b) rbot.movey(-b) rv = rectangle_centered(w=w, h=2 * b, layer=layer) rl = c.add_ref(rv) rr = c.add_ref(rv) rl.movex(-b) rr.movex(+b) wsq = (cross_length + 2 * spacing) / 4 square_mark = c << rectangle_centered(w=wsq, h=wsq, layer=layer) a = width / 2 + wsq / 2 + spacing corner_to_position = { "bottom_left": (-a, -a), "bottom_right": (a, -a), "top_right": (a, a), "top_left": (-a, a), } square_mark.move(corner_to_position[square_corner]) if with_tile_excl: rc_tile_excl = rectangle_centered(w=2 * (b + spacing), h=2 * (b + spacing), layer=pp.layer("no_tile_si")) c.add_ref(rc_tile_excl) return c
def add_frame(component, width=10, spacing=10, layer=pp.LAYER.WG): """ returns component with a frame around it """ c = pp.Component() cref = c.add_ref(component) cref.move(-c.size_info.center) b = component.size_info.height / 2 + spacing + width / 2 w = width rh = rectangle_centered(w=2 * b + w, h=w, layer=layer) rtop = c.add_ref(rh) rbot = c.add_ref(rh) rtop.movey(+b) rbot.movey(-b) rv = rectangle_centered(w=w, h=2 * b, layer=layer) rl = c.add_ref(rv) rr = c.add_ref(rv) rl.movex(-b) rr.movex(+b) c.absorb(cref) rc = rectangle_centered(w=2 * (b + spacing), h=2 * (b + spacing), layer=pp.layer("no_tile_si")) c.add_ref(rc) return c
def taper_from_csv(csv_path, wg_layer=1, clad_offset=3, clad_layer=pp.layer("wgclad")): taper_data = pp.load_csv(csv_path) # taper_data = pd.read_csv(csv_path) # print(taper_data) xs = taper_data["x"] * 1e6 ys = taper_data["width"] * 1e6 / 2.0 ys_trench = ys + clad_offset c = pp.Component() c.add_polygon(list(zip(xs, ys)) + list(zip(xs, -ys))[::-1], layer=wg_layer) c.add_polygon(list(zip(xs, ys_trench)) + list(zip(xs, -ys_trench))[::-1], layer=clad_layer) c.add_port( name="W0", midpoint=(xs[0], 0), width=2 * ys[0], orientation=180, port_type="optical", ) c.add_port( name="E0", midpoint=(xs[-1], 0), width=2 * ys[-1], orientation=0, port_type="optical", ) return c
def waveguide_slot(length=10.0, width=0.5, gap=0.2, layer=pp.layer("wgcore")): width = pp.bias.width(width) gap = pp.bias.gap(gap) a = width / 2 d = a + gap / 2 windows = [(-a - d, a - d, layer), (-a + d, a + d, layer)] return _arbitrary_straight_waveguide(length=length, windows=windows)
def waveguide_slab(length=10.0, width=0.5, cladding=2.0, slab_layer=pp.layer("slab90")): width = pp.bias.width(width) ymin = width / 2 ymax = ymin + cladding windows = [(-ymin, ymin, pp.layer["wgcore"]), (-ymax, ymax, slab_layer)] return _arbitrary_straight_waveguide(length=length, windows=windows)
def waveguide( length=10, width=0.5, layer=pp.layer("wgcore"), layer_cladding=pp.layer("wgclad"), cladding_offset=3, ): """ straight waveguide Args: length: in X direction width: in Y direction .. plot:: :include-source: import pp c = pp.c.waveguide(length=10, width=0.5) pp.plotgds(c) """ c = pp.Component() w = width / 2 wc = w + cladding_offset c.add_polygon([(0, -w), (length, -w), (length, w), (0, w)], layer=layer) if layer_cladding is not None: c.add_polygon([(0, -wc), (length, -wc), (length, wc), (0, wc)], layer=layer_cladding) c.add_port(name="W0", midpoint=[0, 0], width=width, orientation=180, layer=layer) c.add_port(name="E0", midpoint=[length, 0], width=width, orientation=0, layer=layer) c.width = width c.length = length return c
def coupler_straight( length=10, width=0.5, gap=0.27, layer=pp.LAYER.WG, layer_cladding=pp.layer("wgclad"), cladding_offset=3, ): """ straight coupled waveguides. Two multimode ports .. plot:: :include-source: import pp c = pp.c.coupler_straight() pp.plotgds(c) """ c = Component() # Top path c.add_polygon([(0, 0), (length, 0), (length, width), (0, width)], layer=layer) y = width + gap # Bottom path c.add_polygon([(0, y), (length, y), (length, width + y), (0, width + y)], layer=layer) # One multimode port on each side port_w = width * 2 + gap c.add_port(name="W0", midpoint=[0, port_w / 2], width=port_w, orientation=180) c.add_port(name="E0", midpoint=[length, port_w / 2], width=port_w, orientation=0) c.width = width c.length = length # cladding ymax = 2 * width + gap + cladding_offset c.add_polygon( [(0, -cladding_offset), (length, -cladding_offset), (length, ymax), (0, ymax)], layer=layer_cladding, ) return c
def test_label_move(): """ test that when we move a device its label also moves """ c = pp.Component("ellipse_with_label") c << pp.c.ellipse() c.label(text="demo", position=(10, 0), layer=pp.layer("TEXT")) c.movex(10) print(c.references) print(c.labels) # assert c.references[0].origin[0] == 10 # assert c.labels[0].position[0] == 20 return c
def add_padding(component, padding=50, layers=[pp.layer("padding")]): """ returns component width a padding layer on each side""" c = pp.Component(name=component.name + "_p") cr = c.add_ref(component) points = [ [cr.xmin - padding, cr.ymin - padding], [cr.xmax + padding, cr.ymin - padding], [cr.xmax + padding, cr.ymax + padding], [cr.xmin - padding, cr.ymax + padding], ] for layer in layers: c.add_polygon(points, layer=layer) c.ports = cr.ports c.settings = component.settings return c
def add_padding_to_grid(component, grid_size=127, padding=10, bottom_padding=5, layers=[pp.layer("padding")]): """ returns component width a padding layer on each side matches a minimum size grating couplers are at ymin """ c = component c = pp.Component( name=c.name + "_p", settings=c.get_settings(), test_protocol=c.test_protocol, data_analysis_protocol=c.data_analysis_protocol, ) cr = c.add_ref(component) if c.size_info.height < grid_size: y_padding = grid_size - c.size_info.height else: n_grids = np.ceil(c.size_info.height / grid_size) y_padding = n_grids * grid_size - c.size_info.height if c.size_info.width < grid_size: x_padding = grid_size - c.size_info.width else: n_grids = np.ceil(c.size_info.width / grid_size) x_padding = n_grids * grid_size - c.size_info.width x_padding -= padding y_padding -= padding points = [ [cr.xmin - x_padding / 2, cr.ymin - bottom_padding], [cr.xmax + x_padding / 2, cr.ymin - bottom_padding], [cr.xmax + x_padding / 2, cr.ymax + y_padding - bottom_padding], [cr.xmin - x_padding / 2, cr.ymax + y_padding - bottom_padding], ] for layer in layers: c.add_polygon(points, layer=layer) return c
def sims_pad(width=500, height=500, pad=100, layer=1): c = pp.Component() w = width h = height points = [ [-w / 2.0, -h / 2.0], [-w / 2.0, h / 2], [w / 2, h / 2], [w / 2, -h / 2.0], ] c.add_polygon(points, layer=layer) w = width + 2 * pad h = height + 2 * pad points = [ [-w / 2.0, -h / 2.0], [-w / 2.0, h / 2], [w / 2, h / 2], [w / 2, -h / 2.0], ] c.add_polygon(points, layer=pp.layer("padding")) return c
def text(t="U"): return pp.c.text(text=t, layer=pp.layer("wgcore"), size=5)
def cdsem_uturn( width=0.5, radius=10.0, symbol_bot="S", symbol_top="U", wg_length=LINE_LENGTH, waveguide_factory=pp.c.waveguide, bend90_factory=bend_circular, layer=pp.layer("wgcore"), layer_cladding=pp.layer("wgclad"), cladding_offset=3, ): """ Args: width: of the line cladding_offset: radius: bend radius wg_length """ c = pp.Component() r = radius bend90 = bend90_factory(width=width, radius=r, layer=layer, cladding_layer=layer_cladding) if wg_length is None: wg_length = 2 * r wg = waveguide_factory( width=width, length=wg_length, layer=layer, layer_cladding=layer_cladding, cladding_offset=cladding_offset, ) # bend90.ports() rename_ports_by_orientation(bend90) # Add the U-turn on waveguide layer b1 = c.add_ref(bend90) b2 = c.add_ref(bend90) b2.connect("N0", b1.ports["W0"]) wg1 = c.add_ref(wg) wg1.connect("W0", b1.ports["N0"]) wg2 = c.add_ref(wg) wg2.connect("W0", b2.ports["W0"]) # Add symbols sym1 = c.add_ref(CENTER_SHAPES_MAP[symbol_bot]()) sym1.rotate(-90) sym1.movey(r) sym2 = c.add_ref(CENTER_SHAPES_MAP[symbol_top]()) sym2.rotate(-90) sym2.movey(2 * r) c.absorb(sym1) c.absorb(sym2) c.rotate(angle=90) # print(c._bb_valid) # print(c.size_info) c.move(c.size_info.cc, (0, 0)) return c
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 ppe(layer=pp.layer("wgcore"), layer_cladding=pp.layer("wgclad"), cladding_offset=3): """ pattern placement error """ D = pp.Component() # Define global variables xmax = 500 ymax = 500 xmin = 0 ymin = 0 xm = (xmax - xmin) / 2.0 ym = (ymax - ymin) / 2.0 o = cladding_offset # Cover the entire macro D.add_polygon( [ (0, -o), (xmax + o, -o), (xmax + o, ymax + o), (0, ymax + o), ], layer=layer_cladding, ) # Place the pattern rec Cross = cross(x0=xm, y0=ym, width=100, lw=10, layer=layer) Cross.rotate(45, center=[xm, ym]) D.add_ref(Cross) # calculate offset due to the cross xoff = math.sqrt(100 * 50) yoff = math.sqrt(100 * 50) # Top left 1 x0 = 10 y0 = ym pitch = 20 LS1 = linespace( x0=x0, y0=y0, width=240 - xoff, height=10, pitch=pitch, ymax=ym + yoff, layer=layer, ) y0 = y0linespace(y0=y0, height=10, pitch=pitch, ymax=ym + yoff) D.add_ref(LS1) # Top left 2 x0 = 10 y0 = y0 LS1 = linespace(x0=x0, y0=y0, width=240, height=10, pitch=20, ymax=500, layer=layer) D.add_ref(LS1) # Top right 1 x0 = xm + xoff y0 = ym pitch = 30 LS2 = linespace( x0=x0, y0=y0, width=240 - xoff + 10, height=10, pitch=pitch, ymax=ym + yoff, layer=layer, ) y0 = y0linespace(y0=y0, height=10, pitch=pitch, ymax=ym + yoff) D.add_ref(LS2) # Top right 2 x0 = xm + 10 LS2 = linespace(x0=x0, y0=y0, width=240, height=10, pitch=30, ymax=500, layer=layer) D.add_ref(LS2) # Lower left 1 x0 = 10 y0 = 0 pitch = 30 LS3 = linespace(x0=x0, y0=y0, width=240, height=10, pitch=30, ymax=xm - yoff, layer=layer) D.add_ref(LS3) # Lower left 2 x0 = 10 y0 += pitch LS3 = linespace(x0=x0, y0=y0, width=240 - xoff, height=10, pitch=30, ymax=240, layer=layer) D.add_ref(LS3) # Lower right 1 x0 = xm + 10 y0 = 0 pitch = 20 LS4 = linespace(x0=x0, y0=y0, width=240, height=10, pitch=20, ymax=xm - yoff, layer=layer) D.add_ref(LS4) # Lower right 2 x0 = xm + xoff y0 += pitch LS4 = linespace(x0=x0, y0=y0, width=240 - xoff + 10, height=10, pitch=20, ymax=240, layer=layer) D.add_ref(LS4) # Add NOOPC cover on the pattern rec xt = xoff - 10 yt = yoff - 10 D.add_polygon( [ (xm - xt, ym - yt), (xm - xt, ym + yt), (xm + xt, ym + yt), (xm + xt, ym - yt), ], layer=layer_cladding, ) return D
def bend_circular( radius=10.0, width=0.5, theta=-90, start_angle=0, angle_resolution=2.5, layer=LAYER.WG, cladding_layer=pp.layer("wgclad"), cladding_offset=3, ): """ Creates an arc of arclength ``theta`` starting at angle ``start_angle`` Args: radius width: of the waveguide theta: arc length start_angle: angle_resolution layer .. plot:: :include-source: import pp c = pp.c.bend_circular( radius=10, width=0.5, theta=-90, start_angle=0, ) pp.plotgds(c) """ component = pp.Component() # Core inner_radius = radius - width / 2 outer_radius = radius + width / 2 angle1 = (start_angle) * pi / 180 angle2 = (start_angle + theta) * pi / 180 t = np.linspace(angle1, angle2, int(abs(theta) / angle_resolution)) inner_points_x = (inner_radius * cos(t)).tolist() inner_points_y = (inner_radius * sin(t)).tolist() outer_points_x = (outer_radius * cos(t)).tolist() outer_points_y = (outer_radius * sin(t)).tolist() xpts = inner_points_x + outer_points_x[::-1] ypts = inner_points_y + outer_points_y[::-1] component.add_polygon(points=(xpts, ypts), layer=layer) # Cladding w = width + 2 * cladding_offset inner_radius = radius - w / 2 outer_radius = radius + w / 2 angle1 = (start_angle) * pi / 180 angle2 = (start_angle + theta) * pi / 180 t = np.linspace(angle1, angle2, int(abs(theta) / angle_resolution)) inner_points_x = (inner_radius * cos(t)).tolist() inner_points_y = (inner_radius * sin(t)).tolist() outer_points_x = (outer_radius * cos(t)).tolist() outer_points_y = (outer_radius * sin(t)).tolist() xpts = inner_points_x + outer_points_x[::-1] ypts = inner_points_y + outer_points_y[::-1] if cladding_layer is not None: component.add_polygon(points=(xpts, ypts), layer=cladding_layer) component.add_port( name="W0", midpoint=(radius * cos(angle1), radius * sin(angle1)), width=width, orientation=start_angle - 90 + 180 * (theta < 0), layer=layer, ) component.add_port( name="N0", midpoint=(radius * cos(angle2), radius * sin(angle2)), width=width, orientation=start_angle + theta + 90 - 180 * (theta < 0), layer=layer, ) component.info["length"] = (abs(theta) * pi / 180) * radius component.radius = radius component.width = width component.move((0, radius)) pp.ports.port_naming.rename_ports_by_orientation(component) return component
def bend_circular_shallow_rib(layer=pp.layer("slab150"), cladding_layer=pp.layer("slab150clad"), **kwargs): return bend_circular(layer=layer, cladding_layer=cladding_layer, **kwargs)
def bend_circular_deep_rib( layer=pp.layer("slab90"), cladding_layer=pp.layer("slab90clad"), **kwargs): c = bend_circular(layer=layer, cladding_layer=cladding_layer, **kwargs) pp.ports.port_naming.rename_ports_by_orientation(c) return c