Source code for ubcpdk.components

"""Cells imported from the PDK."""

from functools import cache, partial

import gdsfactory as gf
from gdsfactory import Component
from gdsfactory.typings import (
    ComponentReference,
    ComponentSpec,
    CrossSectionSpec,
    Optional,
)

from ubcpdk import tech
from ubcpdk.config import CONFIG, PATH
from ubcpdk.import_gds import import_gds
from ubcpdk.tech import (
    LAYER,
    add_pins_bbox_siepic,
)

um = 1e-6


@gf.cell
def bend_euler(cross_section="strip", **kwargs) -> Component:
    return gf.components.bend_euler(cross_section=cross_section, **kwargs)


bend_euler180 = partial(bend_euler, angle=180)
bend = bend_euler


[docs] def straight( length: float = 10.0, npoints: int = 2, cross_section: CrossSectionSpec = "strip", **kwargs, ) -> Component: """Returns a Straight waveguide. Args: length: straight length (um). npoints: number of points. cross_section: specification (CrossSection, string or dict). kwargs: additional cross_section arguments. .. code:: o1 -------------- o2 length """ return gf.c.straight( length=length, npoints=npoints, cross_section=cross_section, **kwargs )
@gf.cell def wire_corner(cross_section="metal_routing", **kwargs) -> Component: return gf.c.wire_corner(cross_section=cross_section, **kwargs)
[docs] @gf.cell def straight_heater_metal(length: float = 320.0, cross_section="strip") -> gf.Component: c = gf.c.straight_heater_metal(length=length, cross_section=cross_section) return c
info1550te = dict(polarization="te", wavelength=1.55) info1310te = dict(polarization="te", wavelength=1.31) info1550tm = dict(polarization="tm", wavelength=1.55) info1310tm = dict(polarization="tm", wavelength=1.31) thermal_phase_shifter_names = [ "thermal_phase_shifter_multimode_500um", "thermal_phase_shifter_te_1310_500um", "thermal_phase_shifter_te_1310_500um_lowloss", "thermal_phase_shifter_te_1550_500um_lowloss", ] prefix_te1550 = prefix_tm1550 = prefix_te1310 = prefix_tm1130 = "o2" def clean_name(name: str) -> str: return name.replace("_", ".")
[docs] def thermal_phase_shifter0() -> gf.Component: """Return thermal_phase_shifters fixed cell.""" return import_gds( PATH.gds / "thermal_phase_shifters.gds", cellname=thermal_phase_shifter_names[0] )
[docs] def thermal_phase_shifter1() -> gf.Component: """Return thermal_phase_shifters fixed cell.""" return import_gds( PATH.gds / "thermal_phase_shifters.gds", cellname=thermal_phase_shifter_names[1] )
[docs] def thermal_phase_shifter2() -> gf.Component: """Return thermal_phase_shifters fixed cell.""" return import_gds( PATH.gds / "thermal_phase_shifters.gds", cellname=thermal_phase_shifter_names[2] )
[docs] def thermal_phase_shifter3() -> gf.Component: """Return thermal_phase_shifters fixed cell.""" return import_gds( PATH.gds / "thermal_phase_shifters.gds", cellname=thermal_phase_shifter_names[3] )
[docs] def ebeam_BondPad() -> gf.Component: """Return ebeam_BondPad fixed cell.""" return import_gds(PATH.gds / "ebeam_BondPad.gds")
[docs] def ebeam_adiabatic_te1550() -> gf.Component: """Return ebeam_adiabatic_te1550 fixed cell.""" return import_gds(PATH.gds / "ebeam_adiabatic_te1550.gds")
[docs] def ebeam_adiabatic_tm1550() -> gf.Component: """Return ebeam_adiabatic_tm1550 fixed cell.""" return import_gds(PATH.gds / "ebeam_adiabatic_tm1550.gds")
[docs] def ebeam_bdc_te1550() -> gf.Component: """Return ebeam_bdc_te1550 fixed cell.""" return import_gds(PATH.gds / "ebeam_bdc_te1550.gds")
[docs] def ebeam_bdc_tm1550() -> gf.Component: """Return ebeam_bdc_tm1550 fixed cell.""" return import_gds(PATH.gds / "ebeam_bdc_tm1550.gds")
[docs] def ebeam_crossing4() -> gf.Component: """Return ebeam_crossing4 fixed cell.""" return import_gds(PATH.gds / "ebeam_crossing4.gds")
[docs] @gf.cell def straight_one_pin(length=1, cross_section=tech.strip_bbox) -> gf.Component: c = gf.Component() add_pins_left = partial(tech.add_pins_siepic, prefix="o1", pin_length=0.1) s = c << gf.components.straight(length=length, cross_section=cross_section) c.add_ports(s.ports) add_pins_left(c) c.absorb(s) return c
[docs] @gf.cell def ebeam_crossing4_2ports() -> gf.Component: """Return ebeam_crossing4 fixed cell.""" c = gf.Component() x = c << ebeam_crossing4() s1 = c << straight_one_pin() s2 = c << straight_one_pin() s1.connect("o1", x.ports["o2"]) s2.connect("o1", x.ports["o4"]) c.add_port(name="o1", port=x.ports["o1"]) c.add_port(name="o4", port=x.ports["o3"]) c.flatten() return c
[docs] @gf.cell def ebeam_splitter_adiabatic_swg_te1550() -> gf.Component: """Return ebeam_splitter_adiabatic_swg_te1550 fixed cell.""" return import_gds(PATH.gds / "ebeam_splitter_adiabatic_swg_te1550.gds")
[docs] @gf.cell def ebeam_splitter_swg_assist_te1310() -> gf.Component: """Return ebeam_splitter_swg_assist_te1310 fixed cell.""" return import_gds(PATH.gds / "ebeam_splitter_swg_assist_te1310.gds")
[docs] @gf.cell def ebeam_splitter_swg_assist_te1550() -> gf.Component: """Return ebeam_splitter_swg_assist_te1550 fixed cell.""" return import_gds(PATH.gds / "ebeam_splitter_swg_assist_te1550.gds")
[docs] @gf.cell def ebeam_swg_edgecoupler() -> gf.Component: """Return ebeam_swg_edgecoupler fixed cell.""" return import_gds(PATH.gds / "ebeam_swg_edgecoupler.gds")
[docs] @gf.cell def ebeam_terminator_te1310() -> gf.Component: """Return ebeam_terminator_te1310 fixed cell.""" return import_gds(PATH.gds / "ebeam_terminator_te1310.gds")
[docs] @gf.cell def ebeam_terminator_te1550() -> gf.Component: """Return ebeam_terminator_te1550 fixed cell.""" return import_gds(PATH.gds / "ebeam_terminator_te1550.gds")
[docs] @gf.cell def ebeam_terminator_tm1550() -> gf.Component: """Return ebeam_terminator_tm1550 fixed cell.""" return import_gds(PATH.gds / "ebeam_terminator_tm1550.gds")
[docs] def ebeam_y_1550() -> gf.Component: """Return ebeam_y_1550 fixed cell.""" return import_gds(PATH.gds / "ebeam_y_1550.gds")
[docs] @gf.cell def ebeam_y_adiabatic() -> gf.Component: """Return ebeam_y_adiabatic fixed cell.""" return import_gds(PATH.gds / "ebeam_y_adiabatic.gds")
[docs] @gf.cell def ebeam_y_adiabatic_1310() -> gf.Component: """Return ebeam_y_adiabatic_1310 fixed cell.""" return import_gds(PATH.gds / "ebeam_y_adiabatic_1310.gds")
[docs] @gf.cell def metal_via() -> gf.Component: """Return metal_via fixed cell.""" return import_gds(PATH.gds / "metal_via.gds")
[docs] @gf.cell def photonic_wirebond_surfacetaper_1310() -> gf.Component: """Return photonic_wirebond_surfacetaper_1310 fixed cell.""" return import_gds(PATH.gds / "photonic_wirebond_surfacetaper_1310.gds")
[docs] @gf.cell def photonic_wirebond_surfacetaper_1550() -> gf.Component: """Return photonic_wirebond_surfacetaper_1550 fixed cell.""" return import_gds(PATH.gds / "photonic_wirebond_surfacetaper_1550.gds")
[docs] @gf.cell def gc_te1310() -> gf.Component: """Return ebeam_gc_te1310 fixed cell.""" c = gf.Component() gc = import_gds(PATH.gds / "ebeam_gc_te1310.gds") gc_ref = c << gc gc_ref.dmirror() c.add_ports(gc_ref.ports) c.copy_child_info(gc) name = prefix_te1310 c.add_port( name=name, port_type="vertical_te", center=(25, 0), layer=(1, 0), width=9, orientation=0, ) c.info.update(info1310te) c.flatten() return c
[docs] @gf.cell def gc_te1310_8deg() -> gf.Component: """Return ebeam_gc_te1310_8deg fixed cell.""" c = gf.Component() gc = import_gds(PATH.gds / "ebeam_gc_te1310_8deg.gds") gc_ref = c << gc gc_ref.dmirror() c.add_ports(gc_ref.ports) c.copy_child_info(gc) name = prefix_te1310 c.add_port( name=name, port_type="vertical_te", center=(25, 0), layer=(1, 0), width=9, orientation=0, ) c.info.update(info1310te) c.flatten() return c
[docs] @gf.cell def gc_te1310_broadband() -> gf.Component: """Return ebeam_gc_te1310_broadband fixed cell.""" c = gf.Component() gc = import_gds(PATH.gds / "ebeam_gc_te1310_broadband.gds") gc_ref = c << gc gc_ref.dmirror() c.add_ports(gc_ref.ports) c.copy_child_info(gc) name = prefix_te1310 c.add_port( name=name, port_type="vertical_te", center=(25, 0), layer=(1, 0), width=9, orientation=0, ) c.info.update(info1310te) c.flatten() return c
[docs] @gf.cell def gc_te1550() -> gf.Component: """Return ebeam_gc_te1550 fixed cell.""" c = gf.Component() gc = import_gds(PATH.gds / "ebeam_gc_te1550.gds") gc_ref = c << gc gc_ref.dmirror() c.add_ports(gc_ref.ports) c.copy_child_info(gc) name = prefix_te1550 c.add_port( name=name, port_type="vertical_te", center=(25, 0), layer=(1, 0), width=9, orientation=0, ) c.info.update(info1550te) c.flatten() return c
[docs] @gf.cell def gc_te1550_90nmSlab() -> gf.Component: """Return ebeam_gc_te1550_90nmSlab fixed cell.""" c = gf.Component() gc = import_gds(PATH.gds / "ebeam_gc_te1550_90nmSlab.gds") gc_ref = c << gc gc_ref.dmirror() c.add_ports(gc_ref.ports) c.copy_child_info(gc) name = prefix_te1550 c.add_port( name=name, port_type="vertical_te", center=(25, 0), layer=(1, 0), width=9, orientation=0, ) c.info.update(info1550te) c.flatten() return c
[docs] @gf.cell def gc_te1550_broadband() -> gf.Component: """Return ebeam_gc_te1550_broadband fixed cell.""" c = gf.Component() gc = import_gds(PATH.gds / "ebeam_gc_te1550_broadband.gds") gc_ref = c << gc gc_ref.dmirror() c.add_ports(gc_ref.ports) c.copy_child_info(gc) name = prefix_te1550 c.add_port( name=name, port_type="vertical_te", center=(25, 0), layer=(1, 0), width=9, orientation=0, ) c.info.update(info1550te) c.flatten() return c
[docs] @gf.cell def gc_tm1550() -> gf.Component: """Return ebeam_gc_tm1550 fixed cell.""" c = gf.Component() gc = import_gds(PATH.gds / "ebeam_gc_tm1550.gds") gc_ref = c << gc gc_ref.dmirror() c.add_ports(gc_ref.ports) c.copy_child_info(gc) name = prefix_tm1550 c.add_port( name=name, port_type="vertical_tm", center=(25, 0), layer=(1, 0), width=9, orientation=0, ) c.info.update(info1550tm) c.flatten() return c
mzi = partial( gf.components.mzi, splitter=ebeam_y_1550, bend=bend_euler, straight="straight", cross_section="strip", ) _mzi_heater = partial( gf.components.mzi_phase_shifter, bend="bend_euler", straight="straight", splitter="ebeam_y_1550", straight_x_top="straight_heater_metal", )
[docs] @gf.cell def mzi_heater(delta_length=10.0, length_x=320) -> gf.Component: """Returns MZI with heater. Args: delta_length: extra length for mzi arms. """ return _mzi_heater(delta_length=delta_length, length_x=length_x)
[docs] @gf.cell def via_stack_heater_mtop(size=(10, 10)) -> gf.Component: return gf.components.via_stack( size=size, layers=(LAYER.M1_HEATER, LAYER.M2_ROUTER), vias=(None, None), )
def get_input_label_text( gc: ComponentReference, component_name: Optional[str] = None, username: str = CONFIG.username, ) -> str: """Return label for port and a grating coupler. Args: gc: grating coupler reference. component_name: optional component name. username: for the label. """ polarization = gc.info.get("polarization") wavelength = gc.info.get("wavelength") assert polarization.upper() in [ "TE", "TM", ], f"Not valid polarization {polarization.upper()!r} in [TE, TM]" assert ( isinstance(wavelength, int | float) and 1.0 < wavelength < 2.0 ), f"{wavelength} is Not valid 1000 < wavelength < 2000" name = component_name name = clean_name(name) # return f"opt_{polarization.upper()}_{int(wavelength * 1000.0)}_device_{username}-{name}-{gc_index}-{port.name}" return f"opt_in_{polarization.upper()}_{int(wavelength * 1000.0)}_device_{username}-{name}"
[docs] def add_fiber_array( component: ComponentSpec = straight, component_name: Optional[str] = None, gc_port_name: str = "o1", with_loopback: bool = False, fanout_length: float = 0.0, grating_coupler: ComponentSpec = gc_te1550, cross_section: CrossSectionSpec = "strip", straight: ComponentSpec = "straight", taper: ComponentSpec | None = None, **kwargs, ) -> Component: """Returns component with grating couplers and labels on each port. Routes all component ports south. Can add align_ports loopback reference structure on the edges. Args: component: to connect. component_name: for the label. gc_port_name: grating coupler input port name 'o1'. with_loopback: True, adds loopback structures. fanout_length: None # if None, automatic calculation of fanout length. grating_coupler: grating coupler instance, function or list of functions. cross_section: spec. straight: straight component. taper: taper component. kwargs: cross_section settings. """ c = gf.Component() ref = c << gf.routing.add_fiber_array( straight=straight, bend=bend, component=component, component_name=component_name, grating_coupler=grating_coupler, gc_port_name=gc_port_name, with_loopback=with_loopback, fanout_length=fanout_length, cross_section=cross_section, taper=taper, **kwargs, ) ref.drotate(-90) c.add_ports(ref.ports) c.copy_child_info(component) component_name = component_name or component.name grating_coupler = gf.get_component(grating_coupler) label = get_input_label_text(gc=grating_coupler, component_name=component_name) c.add_label(position=c.ports["o1"].dcenter, text=label, layer=LAYER.TEXT) return c
L = 1.55 / 4 / 2 / 2.44
[docs] @gf.cell def dbg( w0: float = 0.5, dw: float = 0.1, n: int = 100, l1: float = L, l2: float = L, ) -> gf.Component: """Includes two ports. Args: w0: width. dw: delta width. n: number of elements. l1: length teeth1. l2: length teeth2. """ c = gf.Component() s = gf.components.straight(length=l1, cross_section="strip") g = c << gf.components.dbr( w1=w0 - dw / 2, w2=w0 + dw / 2, n=n, l1=l1, l2=l2, cross_section="strip", ) s1 = c << s s2 = c << s s1.connect("o2", g.ports["o1"]) s2.connect("o2", g.ports["o2"]) c.add_port("o1", port=s1.ports["o1"]) c.add_port("o2", port=s2.ports["o1"]) c = add_pins_bbox_siepic(c) return c
[docs] @gf.cell def terminator_short(width2=0.1) -> gf.Component: c = gf.Component() s = gf.components.taper(cross_section="strip", width2=width2) s1 = c << s c.add_port("o1", port=s1.ports["o1"]) c = add_pins_bbox_siepic(c) c.flatten() return c
[docs] @gf.cell def dbr( w0: float = 0.5, dw: float = 0.1, n: int = 100, l1: float = L, l2: float = L, cross_section: CrossSectionSpec = "strip", **kwargs, ) -> gf.Component: """Returns distributed bragg reflector. Args: w0: width. dw: delta width. n: number of elements. l1: length teeth1. l2: length teeth2. cross_section: spec. kwargs: cross_section settings. """ c = gf.Component() xs = gf.get_cross_section(cross_section, **kwargs) # add_pins_left = partial(add_pins_siepic, prefix="o1") s = c << gf.components.straight(length=l1, cross_section=xs) _dbr = gf.components.dbr( w1=w0 - dw / 2, w2=w0 + dw / 2, n=n, l1=l1, l2=l2, cross_section=xs, ) dbr = c << _dbr s.connect("o2", dbr.ports["o1"]) c.add_port("o1", port=s.ports["o1"]) return add_pins_bbox_siepic(c)
[docs] @gf.cell def coupler(**kwargs) -> gf.Component: c = gf.components.coupler(**kwargs).dup() c.flatten() return c
[docs] @gf.cell def mmi1x2(**kwargs) -> gf.Component: return gf.components.mmi1x2(**kwargs)
[docs] @gf.cell def dbr_cavity(dbr=dbr, coupler="coupler", **kwargs) -> gf.Component: dbr = dbr(**kwargs) return gf.components.cavity(component=dbr, coupler=coupler)
[docs] @gf.cell def dbr_cavity_te(component="dbr_cavity", **kwargs) -> gf.Component: component = gf.get_component(component, **kwargs) return add_fiber_array(component=component)
[docs] @gf.cell def spiral( length: float = 100, spacing: float = 3.0, n_loops: int = 6, ) -> gf.Component: """Returns spiral component. Args: length: length. spacing: spacing. n_loops: number of loops. """ return gf.c.spiral( length=length, spacing=spacing, n_loops=n_loops, bend=bend_euler, straight=straight, )
coupler90 = partial(gf.components.coupler90, bend=bend_euler, straight=straight) coupler_straight = partial( gf.components.coupler_straight, gap=0.2, cross_section="strip" )
[docs] @gf.cell def coupler_ring( gap: float = 0.2, radius: float = 10.0, length_x: float = 4.0, length_extension: float = 3, bend=bend, cross_section="strip", **kwargs, ) -> Component: c = gf.components.coupler_ring( gap=gap, radius=radius, length_x=length_x, length_extension=length_extension, bend=bend, cross_section=cross_section, **kwargs, ).dup() c = add_pins_bbox_siepic(c) c.flatten() return c
[docs] @gf.cell def ring_single( gap: float = 0.2, radius: float = 10.0, length_x: float = 4.0, length_y: float = 0.6, ) -> Component: return gf.components.ring_single( gap=gap, radius=radius, length_x=length_x, length_y=length_y, cross_section="strip", bend=bend, coupler_ring=coupler_ring, )
[docs] @gf.cell def ring_double( gap: float = 0.2, radius: float = 10.0, length_x: float = 4.0, length_y: float = 0.6, ) -> Component: return gf.components.ring_double( gap=gap, radius=radius, length_x=length_x, length_y=length_y, cross_section="strip", bend=bend, coupler_ring=coupler_ring, )
ring_double_heater = partial( gf.components.ring_double_heater, via_stack="via_stack_heater_mtop", straight=straight, length_y=0.2, cross_section_heater="heater_metal", cross_section_waveguide_heater="strip_heater_metal", cross_section="strip", coupler_ring=coupler_ring, ) ring_single_heater = partial( gf.components.ring_single_heater, via_stack="via_stack_heater_mtop", straight=straight, cross_section_heater="heater_metal", cross_section_waveguide_heater="strip_heater_metal", cross_section="strip", coupler_ring=coupler_ring, ) ebeam_dc_te1550 = partial( gf.components.coupler, ) taper = partial(gf.components.taper) ring_with_crossing = partial( gf.components.ring_single_dut, component=ebeam_crossing4_2ports, port_name="o4", bend=bend, cross_section="strip", ) pad = partial( gf.components.pad, size=(75, 75), layer=LAYER.M2_ROUTER, bbox_layers=(LAYER.PAD_OPEN,), bbox_offsets=(-1.8,), ) def add_label_electrical(component: Component, text: str, port_name: str = "e2"): """Adds labels for electrical port. Returns same component so it needs to be used as a decorator. """ if port_name not in component.ports: port_names = [port.name for port in component.ports] raise ValueError(f"No port {port_name!r} in {port_names}") component.add_label( text=text, position=component.ports[port_name].dcenter, layer=LAYER.TEXT ) return component pad_array = partial(gf.components.pad_array, pad=pad, spacing=(125, 125)) add_pads_rf = partial( gf.routing.add_electrical_pads_top, component="ring_single_heater", pad_array="pad_array", ) add_pads_top = partial( gf.routing.add_pads_top, component=straight_heater_metal, ) add_pads_bot = partial( gf.routing.add_pads_bot, component=straight_heater_metal, )
[docs] @cache def add_fiber_array_pads_rf( component: ComponentSpec = "ring_single_heater", username: str = CONFIG.username, orientation: float = 0, pad_yspacing: float = 50, component_name: Optional[str] = None, **kwargs, ) -> Component: """Returns fiber array with label and electrical pads. Args: component: to add fiber array and pads. username: for the label. orientation: for adding pads. pad_yspacing: for adding pads. component_name: for the label. kwargs: for add_fiber_array. """ c0 = gf.get_component(component) component_name = component_name or c0.name component_name = clean_name(component_name) text = f"elec_{username}-{component_name}_G" c1 = add_pads_rf(component=c0, orientation=orientation, spacing=(0, pad_yspacing)) add_label_electrical(component=c1, text=text) # ports_names = [port.name for port in c0.ports if port.orientation == orientation] # c1 = add_pads_top(component=c0, port_names=ports_names) return add_fiber_array(component=c1, component_name=component_name, **kwargs)
[docs] @cache def add_pads( component: ComponentSpec = "ring_single_heater", username: str = CONFIG.username, **kwargs, ) -> Component: """Returns fiber array with label and electrical pads. Args: component: to add fiber array and pads. username: for the label. kwargs: for add_fiber_array. """ c0 = gf.get_component(component) text = f"elec_{username}-{clean_name(c0.name)}_G" c0._locked = False c0 = add_label_electrical(c0, text=text) return add_pads_rf(component=c0, **kwargs)
if __name__ == "__main__": # c = straight_heater_metal() # c = thermal_phase_shifter0() # c = straight_one_pin() # c = ebeam_adiabatic_te1550() # c = ebeam_bdc_te1550() # c = gc_tm1550() # c = spiral() # c = add_pads_top() # c.pprint_ports() # c.pprint_ports() # c = straight() # c = terminator_short() # c = add_fiber_array_pads_rf(c) # c = ring_double(length_y=10) # c = ring_with_crossing() # c = straight_heater_metal() # c = add_fiber_array(straight_heater_metal) # c.pprint_ports() # c = coupler_ring() # c = dbr_cavity_te() # c = dbr_cavity() # c = ring_single(radius=12) # c = bend_euler() # c = mzi() # c = spiral() # c = pad_array() # c = bend_euler() # c = mzi_heater() # c = ring_with_crossing() # c = ring_single() # c = ring_double() # c = ring_double(radius=12, length_x=2, length_y=2) # c = straight() c = add_fiber_array_pads_rf(component_name="ring_single_heater") c.show()