"""Lumped resonator made of a high-kinetic-inductance nanowire."""
from __future__ import annotations
import gdsfactory as gf
from gdsfactory.component import Component
from gdsfactory.typings import CrossSectionSpec, LayerSpec
from qpdk.cells._schematic import nanowire_resonator_schematic
from qpdk.cells.inductor import meander_inductor
from qpdk.cells.waveguides import straight
from qpdk.tech import (
LAYER,
MeanderCornerStyle,
get_etch_section,
validate_nanowire_layout,
)
from qpdk.utils import add_rect
__all__ = ["nanowire_resonator"]
[docs]
@gf.cell(
tags=["resonators", "inductors"], schematic_function=nanowire_resonator_schematic
)
def nanowire_resonator(
wire_width: float = 0.1,
wire_length: float = 40.0,
n_turns: int = 1,
wire_gap: float | None = None,
lead_length: float | None = None,
corner_style: MeanderCornerStyle = "sharp",
radius: float | None = None,
pad_width: float = 300.0,
pad_height: float = 150.0,
pad_ground_gap: float = 50.0,
coupling_gap: float = 4.0,
contact_overlap: float = 2.0,
feedline_length: float = 500.0,
cross_section: CrossSectionSpec = "cpw",
wire_layer: LayerSpec = LAYER.NbTiN,
) -> Component:
r"""Creates a lumped resonator from a high-kinetic-inductance nanowire.
A nanowire on ``wire_layer`` joins two capacitor pads. The upper pad sits
``coupling_gap`` below the centre conductor of a CPW feedline and couples to
it capacitively. The lower pad is tied to the ground plane along its bottom
edge, so the resonator is a parallel LC to ground, shunt-coupled to the
feedline:
.. svgbob::
o1 ────────────────────────────── o2 feedline
┌──────────────────────┐
│ pad (C) │
└──────────┬───────────┘
│ nanowire (L)
┌──────────┴───────────┐
│ ground pad │
─────┘ └───── ground
The wire is straight for ``n_turns=1``, or a :func:`meander_inductor` with
vertical runs of length ``wire_length`` otherwise, so ``n_turns`` must be odd
for the wire to end on the opposite pad. The meander sits ``lead_length``
away from both pads and joins each pad through one straight lead from its
end run, so the wire contacts each pad once and the U-turns stay clear of
the metal. ``info["n_squares"]`` is the drawn square count between the pad
edges: :func:`qpdk.tech.meander_n_squares_drawn` for the meander plus
``lead_length / wire_width`` for each lead. The ``contact_overlap`` stubs
under the pads are not counted.
Design references for the nanowire geometry and its kinetic inductance are
:cite:`joshiStrongKineticinductanceKerr2022,maleevaCircuitQuantumElectrodynamics2018`.
The matching SAX model is :func:`qpdk.models.nanowire_resonator`.
Args:
wire_width: Nanowire width in µm, between 0.05 and 1.
wire_length: Length of each nanowire run in µm. The gap between the
pads is ``wire_length + 2 * lead_length``.
n_turns: Number of nanowire runs (odd). 1 gives a straight wire.
wire_gap: Gap between adjacent runs in µm. Defaults to ``wire_width``.
lead_length: Length of the straight leads between the meander and each
pad in µm, which is also the clearance between the U-turns and the
pads. Defaults to ``wire_gap``.
corner_style: Corner style of a meandered wire, see
:func:`meander_inductor`.
radius: Centreline radius for ``"round"`` corners in µm.
pad_width: Pad size along the feedline in µm.
pad_height: Pad size across the feedline in µm.
pad_ground_gap: Gap between the pads and the ground plane at the sides
in µm.
coupling_gap: Gap between the feedline centre conductor and the upper pad
in µm.
contact_overlap: Length of the nanowire stubs that overlap the pads in µm.
feedline_length: Length of the feedline in µm.
cross_section: Cross-section of the feedline.
wire_layer: Layer of the nanowire.
Returns:
Component with feedline ports ``o1`` and ``o2``.
Raises:
ValueError: If the wire width is out of range, ``n_turns`` is even or
not positive, a length is not positive, or the wire does not fit
between the pads or the feedline is too short.
"""
if contact_overlap <= 0:
raise ValueError(f"contact_overlap must be positive, got {contact_overlap}")
if contact_overlap > pad_height:
raise ValueError(
f"contact_overlap={contact_overlap} must not exceed pad_height={pad_height}"
)
xs = gf.get_cross_section(cross_section)
metal_layer = xs.layer
etch_layer = get_etch_section(xs).layer
centre_width = xs.width
window_width = pad_width + 2 * pad_ground_gap
wire_xs = gf.cross_section.cross_section(width=wire_width, layer=wire_layer)
wire_gap = wire_width if wire_gap is None else wire_gap
lead_length = wire_gap if lead_length is None else lead_length
validate_nanowire_layout(
wire_width=wire_width,
n_turns=n_turns,
wire_gap=wire_gap,
wire_length=wire_length,
lead_length=lead_length,
pad_width=pad_width,
pad_height=pad_height,
pad_ground_gap=pad_ground_gap,
coupling_gap=coupling_gap,
feedline_length=feedline_length,
)
wire = meander_inductor(
n_turns=n_turns,
turn_length=wire_length,
cross_section=wire_xs,
wire_gap=wire_gap,
add_etch=False,
corner_style=corner_style,
radius=radius,
)
c = Component()
feed = c << straight(length=feedline_length, cross_section=cross_section)
feed.dcenter = (0, 0)
pad_top = -centre_width / 2 - coupling_gap
pad_bottom = pad_top - pad_height
pad_separation = wire_length + 2 * lead_length
ground_pad_top = pad_bottom - pad_separation
ground_pad_bottom = ground_pad_top - pad_height
add_rect(
c,
etch_layer,
x_center=0,
width=window_width,
y0=ground_pad_bottom,
y1=-centre_width / 2,
)
for y0 in (pad_bottom, ground_pad_bottom):
add_rect(c, metal_layer, x_center=0, width=pad_width, y0=y0, y1=y0 + pad_height)
wire_ref = c << wire
wire_ref.rotate(90)
y_mid = pad_bottom - pad_separation / 2
wire_ref.dcenter = (0, y_mid)
# Only the two end runs reach the pads, through leads that end
# contact_overlap inside the metal.
for port in wire_ref.ports:
x, y = port.dcenter
outward = 1 if y > y_mid else -1
y_end = y + outward * (lead_length + contact_overlap)
add_rect(
c,
wire_layer,
x_center=x,
width=wire_width,
y0=min(y, y_end),
y1=max(y, y_end),
)
c.add_ports(feed.ports)
c.info["n_squares"] = wire.info["n_squares_drawn"] + 2 * lead_length / wire_width
c.info["n_squares_convention"] = wire.info["n_squares_convention"]
c.info["wire_gap"] = wire_gap
c.info["lead_length"] = lead_length
return c