r"""Flux-driven Josephson parametric amplifier (JPA) components.
A flux-driven JPA is a :math:`\lambda/4` CPW resonator used in reflection:
its open end is capacitively coupled to a single feed line and its far end is
shorted to ground through a DC SQUID. The SQUID inductance
:math:`L_\text{s}(\Phi)` tunes the resonance. A separate CPW line, shorted to
ground next to the SQUID loop, carries a pump at twice the signal frequency;
the pump modulates :math:`\Phi` and gives three-wave mixing gain.
References:
- :cite:`yamamotoFluxdrivenJosephsonParametric2008`
- :cite:`linJosephsonParametricPhaselocked2014`
- :cite:`zhongSqueezingFluxdrivenJosephson2013`
- :cite:`pogorzalekHystereticFluxResponse2017`
"""
from __future__ import annotations
from functools import partial
import gdsfactory as gf
from gdsfactory.component import Component
from gdsfactory.typings import ComponentSpec, CrossSectionSpec
from kfactory import kdb
from kfactory.conf import CheckInstances
from qpdk.cells._schematic import squid_termination_schematic
from qpdk.cells.capacitor import interdigital_capacitor
from qpdk.cells.junction import josephson_junction, squid_junction
from qpdk.cells.resonator import resonator
from qpdk.cells.waveguides import bend_circular, straight, straight_shorted
from qpdk.tech import LAYER, get_etch_section
__all__ = ["cpw_jpa", "squid_termination"]
[docs]
@gf.cell(
check_instances=CheckInstances.IGNORE,
tags=["junctions", "terminations"],
schematic_function=squid_termination_schematic,
)
def squid_termination(
length: float = 10.0,
cross_section: CrossSectionSpec = "cpw",
junction_spec: ComponentSpec = partial(
squid_junction,
junction_spec=partial(
josephson_junction,
junction_overlap_displacement=1.8,
wide_straight_length=4.0,
narrow_straight_length=0.5,
taper_length=4,
),
),
) -> Component:
r"""CPW lead shorted to the ground plane through a DC SQUID.
The lead ends in an etched pocket that separates the centre conductor from
the ground plane. The SQUID bridges the pocket, so the centre conductor
reaches ground only through the SQUID, as in the flux-driven JPA of
:cite:`yamamotoFluxdrivenJosephsonParametric2008`. The pocket length
follows from the SQUID: the pocket edges run through the centres of the
``JJ_PATCH`` contact patches, so the lead-side patches land half on the
lead and the ground-side patches half on the ground plane.
.. svgbob::
──────────┬──────┐
│ │
o1 ────────│ X │ ground
│ SQUID│
──────────┴──────┘
CPW lead pocket
Args:
length: Length of the CPW lead in µm.
cross_section: Cross-section of the CPW lead. The pocket spans the
full CPW width, centre conductor plus both gaps.
junction_spec: Component specification for the SQUID.
Returns:
Component: The SQUID termination with CPW port ``o1`` and a
``squid`` placement port at the SQUID centre.
Raises:
ValueError: If the SQUID has no ``JJ_PATCH`` contacts on one side,
its lead-side contacts overhang the centre conductor, or a contact
lies wholly over the pocket.
"""
c = Component()
xs = gf.get_cross_section(cross_section)
etch_section = get_etch_section(xs)
pocket_width = xs.width + 2 * etch_section.width
lead_ref = c << straight(length=length, cross_section=cross_section)
junction = gf.get_component(junction_spec)
squid_ref = c << junction
squid_ref.dcplx_trans *= kdb.DCplxTrans(1, 45, False, 0, 0)
squid_ref.dcenter = (0.0, 0.0)
# The lead has no JJ_PATCH shapes, so these are the SQUID contacts.
patches = kdb.Region(c.begin_shapes_rec(gf.get_layer(LAYER.JJ_PATCH)))
boxes = [patch.bbox().to_dtype(c.kcl.dbu) for patch in patches.merged()]
lead_side = [box.center().x for box in boxes if box.center().x < 0]
ground_side = [box.center().x for box in boxes if box.center().x > 0]
if not lead_side or not ground_side:
raise ValueError(
"junction_spec must have JJ_PATCH contacts on both sides of its "
"centre after the 45° rotation."
)
if any(
box.center().x < 0 and max(abs(box.top), abs(box.bottom)) > xs.width / 2
for box in boxes
):
raise ValueError(
"The lead-side JJ_PATCH contacts of junction_spec extend beyond the "
f"centre conductor width ({xs.width} µm), so some would float."
)
lead_contact_x = sum(lead_side) / len(lead_side)
junction_gap = gf.snap.snap_to_grid(
sum(ground_side) / len(ground_side) - lead_contact_x
)
shift = length - lead_contact_x
squid_ref.dmove((shift, 0.0))
if any(
box.left + shift >= length
if box.center().x < 0
else box.right + shift <= length + junction_gap
for box in boxes
):
raise ValueError(
"A JJ_PATCH contact of junction_spec lies wholly over the etched "
"pocket, so it would float. Its contacts must be symmetric enough "
"that every patch reaches the lead or the ground plane."
)
pocket = gf.c.rectangle(size=(junction_gap, pocket_width), layer=etch_section.layer)
pocket_ref = c << pocket
pocket_ref.dmove((length, -pocket_width / 2))
# Port widths must be an even number of database units.
port_width = 2 * round(squid_ref.size_info.height / (2 * c.kcl.dbu)) * c.kcl.dbu
c.add_port("o1", port=lead_ref.ports["o1"])
c.add_port(
name="squid",
center=squid_ref.dcenter,
width=port_width,
orientation=0,
layer=LAYER.JJ_AREA,
port_type="placement",
)
c.info["length"] = length
c.info["pocket_width"] = pocket_width
c.info["junction_gap"] = junction_gap
c.info["loop_area"] = junction.settings.get("loop_area")
return c
[docs]
@gf.cell(check_instances=CheckInstances.IGNORE, tags=["resonators", "junctions"])
def cpw_jpa(
length: float = 4680.0,
meanders: int = 6,
bend_spec: ComponentSpec = bend_circular,
cross_section: CrossSectionSpec = "cpw",
coupling_capacitor_spec: ComponentSpec = partial(
interdigital_capacitor, fingers=8, finger_length=50.0
),
termination_spec: ComponentSpec = squid_termination,
pump_length: float = 200.0,
pump_gap: float = 2.0,
pump_offset: float = 20.0,
) -> Component:
r"""Flux-driven :math:`\lambda/4` CPW Josephson parametric amplifier.
The signal port ``o1`` couples through ``coupling_capacitor_spec`` into a
meandered :math:`\lambda/4` resonator. The resonator ends in
``termination_spec``, which shorts it to ground through a DC SQUID. The
pump line ``o2`` is a shorted CPW that runs perpendicular to the
resonator, past the ground side of the SQUID, as in the WMI devices of
:cite:`pogorzalekHystereticFluxResponse2017`. Only a ``pump_gap`` wide
strip of ground plane separates the pump slot from the ground-side SQUID
contacts, and the short sits ``pump_offset`` beyond the SQUID. The pump
current and its return through that strip couple to the SQUID loop
through a mutual inductance :math:`M`
:cite:`yamamotoFluxdrivenJosephsonParametric2008,linJosephsonParametricPhaselocked2014`.
The pump-to-loop distance is not reported in these sources, so ``pump_gap``
and ``pump_offset`` are design variables. The SAX models do not include
:math:`M`; it has to be extracted by EM simulation. The static flux bias
comes from an off-chip coil.
With the SQUID inductance :math:`L_\text{s}` the resonance is
:cite:`yamamotoFluxdrivenJosephsonParametric2008`:
.. math::
\omega_\text{r}(\Phi) \approx \frac{\omega_{\lambda/4}}{1 + L_\text{s}(\Phi) / L_\text{r}},
\qquad
L_\text{s}(\Phi) = \frac{\Phi_0}{2 \pi I_\text{c,tot} |\cos(\pi \Phi / \Phi_0)|},
where :math:`L_\text{r}` is the total inductance of the :math:`\lambda/4`
line. The default coupling capacitor is about :math:`28\,\text{fF}`,
close to the :math:`30\,\text{fF}` of the WMI devices with
:math:`Q_\text{ext} \approx 300` :cite:`pogorzalekHystereticFluxResponse2017`.
With the default :math:`I_\text{c,tot} = 4.8\,\text{µA}` of
:func:`~qpdk.models.junction.squid_termination`, the SAX model of the
default layout resonates at :math:`6.07\,\text{GHz}` at zero flux and
:math:`5.93\,\text{GHz}` at :math:`0.3\,\Phi_0`, with
:math:`Q_\text{ext} \approx 290`.
.. svgbob::
┌──────────────┐
│ meander │
o1 ──║║──────┘ λ/4 line │
Cc │
┌──────────────┘ ┬ pump short
└──────── X ║ │
║ │
║ │
o2
Args:
length: Realized :math:`\lambda/4` line length in µm, from the
coupling capacitor to the SQUID pocket. Includes the
termination's CPW lead.
meanders: Number of meander sections.
bend_spec: Specification for the meander bends.
cross_section: Cross-section of the resonator, the pump line, the
coupling capacitor and the termination lead.
coupling_capacitor_spec: Two-port coupling capacitor between ``o1``
and the open end of the resonator.
termination_spec: One-port SQUID termination, see
:func:`squid_termination`. It must provide a ``squid`` port and
the ``length``, ``pocket_width`` and ``loop_area`` info entries.
pump_length: Length of the pump CPW line in µm.
pump_gap: Width in µm of the ground-plane strip between the
ground-side SQUID contacts and the pump line slot.
pump_offset: Distance in µm from the SQUID centre to the pump short,
along the pump line, so the pump runs past the whole SQUID.
Returns:
Component: The JPA with signal port ``o1`` and pump port ``o2``.
Raises:
ValueError: If ``length`` does not exceed the termination lead length.
"""
c = Component()
xs = gf.get_cross_section(cross_section)
termination = gf.get_component(termination_spec, cross_section=cross_section)
resonator_length = length - termination.info["length"]
if resonator_length <= 0:
raise ValueError(
f"length ({length}) must exceed the termination lead length "
f"({termination.info['length']})."
)
capacitor_ref = c << gf.get_component(
coupling_capacitor_spec, cross_section=cross_section
)
resonator_ref = c << resonator(
length=resonator_length,
meanders=meanders,
bend_spec=bend_spec,
cross_section=cross_section,
open_start=False,
open_end=False,
)
termination_ref = c << termination
resonator_ref.connect("o1", capacitor_ref.ports["o2"])
termination_ref.connect("o1", resonator_ref.ports["o2"])
# The pump runs up past the ground side of the SQUID, perpendicular to
# the resonator, from below the termination; the meander lies above.
squid_port = termination_ref.ports["squid"]
pump_axis_x = (
termination_ref.dxmax + pump_gap + get_etch_section(xs).width + xs.width / 2
)
pump_ref = c << straight_shorted(length=pump_length, cross_section=cross_section)
pump_ref.dcplx_trans = kdb.DCplxTrans(
1, 90, False, pump_axis_x, squid_port.dy + pump_offset
) * kdb.DCplxTrans(-pump_length, 0)
c.add_port("o1", port=capacitor_ref.ports["o1"])
c.add_port("o2", port=pump_ref.ports["o1"])
capacitor_settings = capacitor_ref.cell.settings
c.info["length"] = length
c.info["cross_section"] = xs.name
c.info["resonator_type"] = "quarter_wave"
c.info["coupling_fingers"] = capacitor_settings.get("fingers")
c.info["coupling_finger_length"] = capacitor_settings.get("finger_length")
c.info["coupling_finger_gap"] = capacitor_settings.get("finger_gap")
c.info["coupling_thickness"] = capacitor_settings.get("thickness")
c.info["squid_loop_area"] = termination.info["loop_area"]
c.info["pump_gap"] = pump_gap
c.info["pump_offset"] = pump_offset
return c