Source code for gdsfactory.components.snspd
from __future__ import annotations
import numpy as np
from gdsfactory.cell import cell
from gdsfactory.component import Component
from gdsfactory.components.compass import compass
from gdsfactory.components.optimal_hairpin import optimal_hairpin
from gdsfactory.typings import Float2, LayerSpec
[docs]
@cell
def snspd(
wire_width: float = 0.2,
wire_pitch: float = 0.6,
size: Float2 = (10, 8),
num_squares: int | None = None,
turn_ratio: float = 4,
terminals_same_side: bool = False,
layer: LayerSpec = (1, 0),
) -> Component:
"""Creates an optimally-rounded SNSPD.
Args:
width: Width of the wire.
pitch: Distance between two adjacent wires. Must be greater than `width`.
size: None or array-like[2] of int or float
(width, height) of the rectangle formed by the outer boundary of the
SNSPD. Must be none if `num_squares` is specified.
num_squares: int or None
Total number of squares inside the SNSPD length. Must be none if
`size` is specified.
turn_ratio: int or float
Specifies how much of the SNSPD width is dedicated to the 180 degree
turn. A `turn_ratio` of 10 will result in 20% of the width being
comprised of the turn.
terminals_same_side : bool
If True, both ports will be located on the same side of the SNSPD.
layer: layer spec to put polygon geometry on.
"""
# Convenience tests to auto-shape the size based
# on the number of squares
if num_squares is not None and (
(size is None) or ((size[0] is None) and (size[1]) is None)
):
xy = np.sqrt(num_squares * wire_pitch * wire_width)
size = [xy, xy]
num_squares = None
if [size[0], size[1], num_squares].count(None) != 1:
raise ValueError(
"snspd() requires that exactly ONE value of "
"the arguments ``num_squares`` and ``size`` be None "
"to prevent overconstraining, for example:\n"
">>> snspd(size = (3, None), num_squares = 2000)"
)
if size[0] is None:
ysize = size[1]
xsize = num_squares * wire_pitch * wire_width / ysize
elif size[1] is None:
xsize = size[0]
ysize = num_squares * wire_pitch * wire_width / xsize
else:
xsize = size[0]
ysize = size[1]
num_meanders = int(np.ceil(ysize / wire_pitch))
D = Component()
hairpin = optimal_hairpin(
width=wire_width,
pitch=wire_pitch,
turn_ratio=turn_ratio,
length=xsize / 2,
num_pts=20,
layer=layer,
)
if not terminals_same_side and (num_meanders % 2) == 0:
num_meanders += 1
elif terminals_same_side and (num_meanders % 2) == 1:
num_meanders += 1
start_nw = D.add_ref(compass(size=[xsize / 2, wire_width], layer=layer))
hp_prev = D.add_ref(hairpin)
hp_prev.connect("e1", start_nw.ports["e3"])
alternate = True
for _n in range(2, num_meanders):
hp = D.add_ref(hairpin)
if alternate:
hp.connect("e2", hp_prev.ports["e2"])
else:
hp.connect("e1", hp_prev.ports["e1"])
last_port = hp.ports["e1"]
hp_prev = hp
alternate = not alternate
finish_se = D.add_ref(compass(size=[xsize / 2, wire_width], layer=layer))
finish_se.connect("e3", last_port)
D.add_port(port=start_nw.ports["e1"], name="e1")
D.add_port(port=finish_se.ports["e1"], name="e2")
D.info["num_squares"] = num_meanders * (xsize / wire_width)
D.info["area"] = xsize * ysize
D.info["size"] = (xsize, ysize)
return D
if __name__ == "__main__":
c = snspd()
c.show(show_ports=True)