Source code for gdsfactory.components.straight_heater_doped_rib
from __future__ import annotations
from functools import partial
import gdsfactory as gf
from gdsfactory.component import Component
from gdsfactory.components.taper_cross_section import taper_cross_section
from gdsfactory.components.via_stack import via_stack_m1_m3 as via_stack_metal_function
from gdsfactory.components.via_stack import via_stack_slab_npp_m3
from gdsfactory.cross_section import rib_heater_doped, strip_rib_tip
from gdsfactory.snap import snap_to_grid
from gdsfactory.typings import ComponentSpec, CrossSectionSpec
[docs]
@gf.cell
def straight_heater_doped_rib(
length: float = 320.0,
nsections: int = 3,
cross_section: CrossSectionSpec = strip_rib_tip,
cross_section_heater: CrossSectionSpec = rib_heater_doped,
via_stack: ComponentSpec | None = via_stack_slab_npp_m3,
via_stack_metal: ComponentSpec | None = via_stack_metal_function,
via_stack_metal_size: tuple[float, float] = (10.0, 10.0),
via_stack_size: tuple[float, float] = (10.0, 10.0),
taper: ComponentSpec | None = taper_cross_section,
heater_width: float = 2.0,
heater_gap: float = 0.8,
via_stack_gap: float = 0.0,
width: float = 0.5,
xoffset_tip1: float = 0.2,
xoffset_tip2: float = 0.4,
) -> Component:
r"""Returns a doped thermal phase shifter.
dimensions from https://doi.org/10.1364/OE.27.010456
Args:
length: of the waveguide in um.
nsections: between via_stacks.
cross_section: for the input/output ports.
cross_section_heater: for the heater.
via_stack: optional function to connect the heater strip.
via_stack_metal: function to connect the metal area.
via_stack_metal_size: x, y size in um.
via_stack_size: x, y size in um.
taper: optional taper spec.
heater_width: in um.
heater_gap: in um.
via_stack_gap: from edge of via_stack to waveguide.
width: waveguide width on the ridge.
xoffset_tip1: distance in um from input taper to via_stack.
xoffset_tip2: distance in um from output taper to via_stack.
.. code::
length
|<--------------------------------------------->|
| length_section |
| <---------------------------> |
| length_via_stack |
| <-------> taper|
| __________ _________ |
| | | | | |
| | via_stack|__________________| | |
| | size | heater width | | |
| /|__________|__________________|________|\ |
| / | heater_gap | \ |
|/ |______________________________________| \ |
\ |_______________width__________________| /
\ | | /
\|_____________heater_gap______________ |/
| | | |
| |____heater_width____| |
| | | |
|________| |________|
taper cross_section_heater
|<------width------>|
____________________ heater_gap slab_gap
top_via_stack | |<---------->| bot_via_stack <-->
___ ______________________| |___________________________|___
| | | undoped Si | | |
| |layer_heater| intrinsic region |layer_heater | |
|___|____________|__________________________________________|______________|___|
<------------>
heater_width
<------------------------------------------------------------------------------>
slab_width
"""
c = Component()
cross_section_heater = partial(
cross_section_heater,
heater_width=heater_width,
heater_gap=heater_gap,
width=width,
)
if taper:
taper = (
taper(cross_section1=cross_section, cross_section2=cross_section_heater)
if callable(taper)
else taper
)
length -= taper.get_ports_xsize() * 2
wg = c << gf.c.straight(
cross_section=cross_section_heater,
length=snap_to_grid(length),
)
if taper:
taper1 = c << taper
taper1.connect("o2", wg.ports["o1"])
c.add_port("o1", port=taper1.ports["o1"])
taper2 = c << taper
taper2.mirror()
taper2.connect("o2", wg.ports["o2"])
c.add_port("o2", port=taper2.ports["o1"])
else:
c.add_port("o2", port=wg.ports["o2"])
c.add_port("o1", port=wg.ports["o1"])
if via_stack_metal:
via_stack_section = via_stack_metal(size=via_stack_metal_size)
via_stacks = []
length_via_stack = snap_to_grid(via_stack_size[1])
length_section = snap_to_grid((length - length_via_stack) / nsections)
x0 = via_stack_size[0] / 2 - xoffset_tip1
for i in range(nsections + 1):
xi = x0 + length_section * i
if via_stack_metal and via_stack:
via_stack_center = c.add_ref(via_stack_section)
via_stack_center.x = xi
via_stack_ref = c << via_stack_section
via_stack_ref.x = xi
via_stack_ref.y = (
+via_stack_metal_size[1] if i % 2 == 0 else -via_stack_metal_size[1]
)
via_stacks.append(via_stack_ref)
if via_stack:
via_stack_top = c << via_stack(size=via_stack_size)
via_stack_top.x = xi
via_stack_top.ymin = +(heater_gap + width / 2 + via_stack_gap)
via_stack_bot = c << via_stack(size=via_stack_size)
via_stack_bot.x = xi
via_stack_bot.ymax = -(heater_gap + width / 2 + via_stack_gap)
if via_stack:
via_stack.xmax = x0 + length_section * nsections
via_stack_top.movex(xoffset_tip2)
if via_stack:
via_stack.xmax = x0 + length_section * nsections
via_stack_bot.movex(xoffset_tip2)
if via_stack_metal and via_stack:
via_stack_length = length + via_stack_metal_size[0]
via_stack_top = c << via_stack_metal(
size=(via_stack_length, via_stack_metal_size[0]),
)
via_stack_bot = c << via_stack_metal(
size=(via_stack_length, via_stack_metal_size[0]),
)
via_stack_bot.xmin = via_stacks[0].xmin
via_stack_top.xmin = via_stacks[0].xmin
via_stack_top.ymin = via_stacks[0].ymax
via_stack_bot.ymax = via_stacks[1].ymin
c.add_ports(via_stack_top.ports, prefix="top_")
c.add_ports(via_stack_bot.ports, prefix="bot_")
return c
def test_straight_heater_doped_rib_ports() -> None:
c = straight_heater_doped_rib(length=100.0)
assert c.get_ports_xsize(port_type="optical") == 100.0, c.get_ports_xsize(
port_type="optical"
)
if __name__ == "__main__":
c = straight_heater_doped_rib()
c.show(show_ports=True)