def pad( width: int = 100, height: int = 100, layer: Tuple[int, int] = LAYER.M3 ) -> Component: """rectangular pad with 4 ports (N, S, E, W) Args: width: pad width height: pad height layer: pad layer .. plot:: :include-source: import pp c = pp.c.pad(width=100, height=100, layer=pp.LAYER.M3) pp.plotgds(c) """ c = Component() _c = compass(size=(width, height), layer=layer).ref() c.add(_c) c.absorb(_c) c.ports = _c.ports return c
def add_trenches( c: Component, sstw: float = 2.0, trench_width: float = 0.5, trench_keep_out: float = 2.0, trenches: List[Dict[str, int]] = [ { "nb_segments": 2, "lane": 1, "x_start_offset": 0 }, { "nb_segments": 2, "lane": -1, "x_start_offset": 0 }, ], layer_trench: Tuple[int, int] = LAYER.DEEPTRENCH, ) -> Component: """ Add trenches to a waveguide-heater-like component """ heater_width = c.settings["heater_width"] heater_spacing = c.settings["heater_spacing"] width = c.settings["width"] length = c.settings["length"] a = heater_spacing + (width + heater_width) / 2 # Add trenches if trench_width and trench_width > 0: tko = trench_keep_out for trench in trenches: lane = trench["lane"] td = tko + a + (trench_width + heater_width) / 2 y = np.sign(lane) * (td + (abs(lane) - 1) * (trench_width + tko)) x_start_offset = trench["x_start_offset"] if "segments" not in trench: nb_segments = trench["nb_segments"] trench_length = (length - (nb_segments - 1) * sstw) / nb_segments segments = [trench_length] * nb_segments else: segments = trench["segments"] x = x_start_offset for i, trench_length in enumerate(segments): trench = hline(length=trench_length, width=trench_width, layer=layer_trench) _trench = trench.ref(port_id="W0", position=c.ports["W0"].position + (x, y)) c.add(_trench) c.absorb(_trench) x += trench_length + sstw return c
def wg_heater_connected(waveguide_heater: Callable = waveguide_heater, wg_heater_connector: Callable = wg_heater_connector, tlm_layers: List[Tuple[int, int]] = [ LAYER.VIA1, LAYER.M1, LAYER.VIA2, LAYER.M2, LAYER.VIA3, LAYER.M3, ], **kwargs) -> Component: """ .. plot:: :include-source: import pp c = pp.c.wg_heater_connected() pp.plotgds(c) """ wg_heater = waveguide_heater(**kwargs) # print(wg_heater.ports.keys()) conn1 = wg_heater_connector( heater_ports=[wg_heater.ports["HBE0"], wg_heater.ports["HTE0"]], tlm_layers=tlm_layers, ) conn2 = wg_heater_connector( heater_ports=[wg_heater.ports["HBW0"], wg_heater.ports["HTW0"]], tlm_layers=tlm_layers, ) cmp = Component() for c in [wg_heater, conn1, conn2]: _c = cmp.add_ref(c) cmp.absorb(_c) for port_name, p in wg_heater.ports.items(): cmp.add_port(name=port_name, port=p) cmp.add_port(name=1, port=conn1.ports["0"]) cmp.add_port(name=2, port=conn2.ports["0"]) cmp.ports[1].orientation = 90 cmp.ports[2].orientation = 90 return cmp
def coupler90( bend_radius: float = 10.0, width: float = 0.5, gap: float = 0.2, waveguide_factory: Callable = waveguide, bend90_factory: Callable = bend_circular, ) -> Component: """ Waveguide coupled to a bend with gap Args: bend_radius: um width: waveguide width (um) gap: um .. plot:: :include-source: import pp c = pp.c.coupler90() pp.plotgds(c) """ # pp.drc.assert_on_1nm_grid((width + gap) / 2) y = pp.drc.snap_to_1nm_grid((width + gap) / 2) c = Component() wg = c << waveguide_factory( length=bend_radius, width=width, ) bend = c << bend90_factory(radius=bend_radius, width=width) pbw = bend.ports["W0"] bend.movey(pbw.midpoint[1] + gap + width) # This component is a leaf cell => using absorb c.absorb(wg) c.absorb(bend) port_width = 2 * width + gap c.add_port(port=wg.ports["E0"], name="E0") c.add_port(port=bend.ports["N0"], name="N0") c.add_port(name="W0", midpoint=[0, y], width=port_width, orientation=180) return c
def _arbitrary_straight_waveguide(length, windows): """ Args: length: length windows: [(y_start, y_stop, layer), ...] """ md5 = hashlib.md5() for e in windows: md5.update(str(e).encode()) component = Component() component.name = "ARB_SW_L{}_HASH{}".format(length, md5.hexdigest()) y_min, y_max, layer0 = windows[0] y_min, y_max = min(y_min, y_max), max(y_min, y_max) # Add one port on each side centered at y=0 for y_start, y_stop, layer in windows: w = abs(y_stop - y_start) y = (y_stop + y_start) / 2 _wg = hline(length=length, width=w, layer=layer).ref() _wg.movey(y) component.add(_wg) component.absorb(_wg) y_min = min(y_stop, y_start, y_min) y_max = max(y_stop, y_start, y_max) width = y_max - y_min component.add_port(name="W0", midpoint=[0, 0], width=width, orientation=180, layer=layer0) component.add_port(name="E0", midpoint=[length, 0], width=width, orientation=0, layer=layer0) return component
def waveguide_pin( length: float = 10.0, width: float = 0.5, width_i: float = 0.0, width_p: float = 1.0, width_n: float = 1.0, width_pp: float = 1.0, width_np: float = 1.0, width_ppp: float = 1.0, width_npp: float = 1.0, layer_p: Tuple[int, int] = LAYER.P, layer_n: Tuple[int, int] = LAYER.N, layer_pp: Tuple[int, int] = LAYER.Pp, layer_np: Tuple[int, int] = LAYER.Np, layer_ppp: Tuple[int, int] = LAYER.Ppp, layer_npp: Tuple[int, int] = LAYER.Npp, waveguide_factory: Callable = waveguide, ) -> Component: """PN doped waveguide .. code:: |<------width------>| ____________________ | | | | ___________________| | | |__________________________| | | | P++ P+ P | I | N N+ N++ | __________________________________________________________________| | |width_i| width_n | width_np | width_npp | 0 oi on onp onpp .. plot:: :include-source: import pp c = pp.c.waveguide_pin(length=10) pp.plotgds(c) """ c = Component() w = c << waveguide_factory(length=length, width=width) c.absorb(w) oi = width_i / 2 on = oi + width_n onp = oi + width_n + width_np onpp = oi + width_n + width_np + width_npp # N doping c.add_polygon([(0, oi), (length, oi), (length, onpp), (0, onpp)], layer=layer_n) if layer_np: c.add_polygon([(0, on), (length, on), (length, onpp), (0, onpp)], layer=layer_np) if layer_npp: c.add_polygon([(0, onp), (length, onp), (length, onpp), (0, onpp)], layer=layer_npp) oi = -width_i / 2 op = oi - width_p opp = oi - width_p - width_pp oppp = oi - width_p - width_pp - width_ppp # P doping c.add_polygon([(0, oi), (length, oi), (length, oppp), (0, oppp)], layer=layer_p) if layer_pp: c.add_polygon([(0, op), (length, op), (length, oppp), (0, oppp)], layer=layer_pp) if layer_ppp: c.add_polygon([(0, opp), (length, opp), (length, oppp), (0, oppp)], layer=layer_ppp) return c
def _bend_circular_heater( radius: float = 10, wg_width: float = 0.5, theta: int = -90, start_angle: int = 0, angle_resolution: float = 2.5, heater_to_wg_distance: float = 1.2, heater_width: float = 0.5, ) -> Component: """ Creates an arc of arclength ``theta`` starting at angle ``start_angle`` Args: radius width: of the waveguide theta: arc length start_angle: angle_resolution """ component = Component() wg_bend = bend_circular( radius=radius, width=wg_width, theta=theta, start_angle=start_angle, angle_resolution=angle_resolution, layer=LAYER.WG, ).ref((0, 0)) a = heater_to_wg_distance + wg_width / 2 + heater_width / 2 heater_outer = bend_circular( radius=radius + a, width=heater_width, theta=theta, start_angle=start_angle, angle_resolution=angle_resolution, layer=LAYER.HEATER, ).ref((0, -a)) heater_inner = bend_circular( radius=radius - a, width=heater_width, theta=theta, start_angle=start_angle, angle_resolution=angle_resolution, layer=LAYER.HEATER, ).ref((0, a)) component.add(wg_bend) component.add(heater_outer) component.add(heater_inner) component.absorb(wg_bend) component.absorb(heater_outer) component.absorb(heater_inner) i = 0 for device in [wg_bend, heater_outer, heater_inner]: for port in device.ports.values(): component.ports["{}".format(i)] = port i += 1 component.info["length"] = wg_bend.info["length"] component.radius = radius component.width = wg_width return component
def waveguide_heater( length: float = 10.0, width: float = 0.5, heater_width: float = 0.5, heater_spacing: float = 1.2, metal_connection: bool = True, sstw: float = 2.0, trench_width: float = 0.5, trench_keep_out: float = 2.0, trenches: List[Dict[str, int]] = [ { "nb_segments": 2, "lane": 1, "x_start_offset": 0 }, { "nb_segments": 2, "lane": -1, "x_start_offset": 0 }, ], layers_heater: List[Tuple[int, int]] = [LAYER.HEATER], waveguide_factory: Callable = waveguide, layer_trench: Tuple[int, int] = LAYER.DEEPTRENCH, ) -> Component: """ waveguide with heater .. code:: TTTTTTTTTTTTT TTTTTTTTTTTTT <-- trench HHHHHHHHHHHHHHHHHHHHHHHHHHHHHH <-- heater ------------------------------ <-- waveguide HHHHHHHHHHHHHHHHHHHHHHHHHHHHHH <-- heater TTTTTTTTTTTTT TTTTTTTTTTTTT <-- trench .. plot:: :include-source: import pp c = pp.c.waveguide_heater() pp.plotgds(c) """ c = Component() _heater = heater(length=length, width=heater_width, layers_heater=layers_heater) y_heater = heater_spacing + (width + heater_width) / 2 heater_top = c << _heater heater_bot = c << _heater heater_top.movey(+y_heater) heater_bot.movey(-y_heater) wg = c << waveguide_factory(length=length, width=width) for i in [heater_top, heater_bot, wg]: c.absorb(i) # Add wg ports for p in wg.ports.values(): c.add_port(name=p.name, port=p) # Add heater ports for p in heater_top.ports.values(): c.add_port(name="HT" + p.name, port=p) for p in heater_bot.ports.values(): c.add_port(name="HB" + p.name, port=p) c.settings["width"] = width c.settings["heater_width"] = heater_width c.settings["heater_spacing"] = heater_spacing c.settings["length"] = length add_trenches(c, sstw, trench_width, trench_keep_out, trenches, layer_trench=layer_trench) return c
def coupler( wg_width: float = 0.5, gap: float = 0.236, length: float = 20.007, coupler_symmetric_factory: Callable = coupler_symmetric, coupler_straight_factory: Callable = coupler_straight, layer: Tuple[int, int] = LAYER.WG, layers_cladding: List[Tuple[int, int]] = [LAYER.WGCLAD], cladding_offset: float = conf.tech.cladding_offset, dy: float = 5.0, ) -> Component: r"""symmetric coupler Args: gap length coupler_symmetric_factory coupler_straight_factory layer: layers_cladding: list of cladding layers cladding_offset: offset from waveguide to cladding edge dy: port to port vertical spacing .. code:: W1 __ __ E1 \ / | \ length / | ======================= gap | dy / \ | _/ \_ | W0 E0 | coupler_straight_factory coupler_symmetric_factory .. plot:: :include-source: import pp c = pp.c.coupler(gap=0.2, length=10) pp.plotgds(c) """ assert_on_1nm_grid(length) assert_on_1nm_grid(gap) c = Component() sbend = coupler_symmetric_factory( gap=gap, wg_width=wg_width, layer=layer, layers_cladding=layers_cladding, cladding_offset=cladding_offset, dy=dy, ) sr = c << sbend sl = c << sbend cs = c << coupler_straight_factory( length=length, gap=gap, width=wg_width, layer=layer, layers_cladding=layers_cladding, cladding_offset=cladding_offset, ) sl.connect("W0", destination=cs.ports["W0"]) sr.connect("W0", destination=cs.ports["E0"]) c.add_port("W1", port=sl.ports["E0"]) c.add_port("W0", port=sl.ports["E1"]) c.add_port("E0", port=sr.ports["E0"]) c.add_port("E1", port=sr.ports["E1"]) c.absorb(sl) c.absorb(sr) c.absorb(cs) return c
def coupler( wg_width: float = 0.5, gap: float = 0.236, length: float = 20.007, coupler_symmetric_factory: Callable = coupler_symmetric, coupler_straight: Callable = coupler_straight, layer: Tuple[int, int] = LAYER.WG, layers_cladding: List[Tuple[int, int]] = [LAYER.WGCLAD], cladding_offset: int = 3, ) -> Component: r""" symmetric coupler Args: gap length coupler_symmetric_factory coupler_straight .. code:: W1 __ __ E1 \ / \ length / ======================= gap / \ _/ \_ W0 E0 coupler_straight coupler_symmetric_factory .. plot:: :include-source: import pp c = pp.c.coupler(gap=0.2, length=10) pp.plotgds(c) """ assert_on_1nm_grid(length) assert_on_1nm_grid(gap) c = Component() sbend = coupler_symmetric_factory( gap=gap, wg_width=wg_width, layer=layer, layers_cladding=layers_cladding, cladding_offset=cladding_offset, ) sr = c << sbend sl = c << sbend cs = c << coupler_straight( length=length, gap=gap, width=wg_width, layer=layer, layers_cladding=layers_cladding, cladding_offset=cladding_offset, ) sl.connect("W0", destination=cs.ports["W0"]) sr.connect("W0", destination=cs.ports["E0"]) c.add_port("W1", port=sl.ports["E0"]) c.add_port("W0", port=sl.ports["E1"]) c.add_port("E0", port=sr.ports["E0"]) c.add_port("E1", port=sr.ports["E1"]) c.absorb(sl) c.absorb(sr) c.absorb(cs) return c