Source code for qiskit_metal.qlibrary.qubits.SNAIL

# -*- coding: utf-8 -*-

# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2021.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
"""SNAIL (Superconducting Nonlinear Asymmetric Inductive eLement).

.. code-block:
:
    |                                                              |
    |                                                              |
    |--seg a--|JJ|--seg ab--|JJ|--seg ab--|JJ|--seg b--|           |
    |          (three large junctions, upper arm)       |          |
  plate1                                              seg c--seg d--plate2
    |                                                   |          |
    |--------seg a lower--------|jj|----seg b lower-----|          |
    |              (single small junction, lower arm)              |
    |                                                              |
"""

from qiskit_metal import draw, Dict
from qiskit_metal.qlibrary.core.base import QComponent


[docs] class SNAIL(QComponent): """A SNAIL (Superconducting Nonlinear Asymmetric Inductive eLement). Inherits the "QComponent" class. A SNAIL is a superconducting loop interrupted by ``n`` large Josephson junctions on one arm (here ``n = 3``) and a single smaller junction (Josephson energy ``alpha * E_J``) on the opposite arm. The asymmetry between the two arms gives the element a tunable third-order (three-wave mixing) nonlinearity that a symmetric two-junction SQUID does not have, which makes it the building block of SNAIL parametric amplifiers (SPAs) and Kerr-free three-wave mixers. Physics ------- With ``phi`` the superconducting phase across the small junction and ``phi_ext = 2*pi*Phi_ext/Phi_0`` the reduced external flux, the SNAIL potential is (Frattini 2017):: U(phi)/E_J = - alpha*cos(phi) - n*cos((phi_ext - phi)/n) Taylor-expanding about its minimum ``phi_min`` gives ``U_eff/E_J = c2*p^2 + c3*p^3 + c4*p^4 + ...`` (``p = phi - phi_min``), where the coefficients ``c2, c3, c4`` are set purely by ``(n, alpha, Phi_ext)``. ``c2`` is the (flux-tunable) linear inductance, ``c3`` the three-wave-mixing strength and ``c4`` the Kerr. The useful operating point is the **Kerr-free** flux where ``c4 -> 0`` while ``c3`` stays large: pure three-wave mixing with no self-Kerr / Stark shift, which is what preserves gain and quantum efficiency at strong pump drive. ``n = 3`` is the smallest array that simultaneously (a) admits an ``alpha`` below the single-well bound while still giving useful inductive tunability, and (b) opens a region in ``(alpha, Phi_ext)`` where ``c4`` crosses zero with ``c3`` still large. Limiting cases: ``n = 1`` is a SQUID (pure cosine, ``c3 = 0``, no three-wave mixing); ``n >> 1`` approaches the linear RF-SQUID / fluxonium regime. Single-well design constraint: keep ``alpha < 1/n`` (i.e. ``alpha < 1/3`` for ``n = 3``). Above this the potential develops multiple inequivalent minima and becomes hysteretic (flux-qubit-like), which is unusable for clean three-wave mixing. The fabricated devices below sit just under the bound at ``alpha ~ 0.29``. (More precisely the non-hysteretic region is a 2-D area in the ``(alpha, Phi_ext)`` plane; ``alpha < 1/n`` is the canonical rule of thumb, not an all-flux guarantee.) References ---------- * Frattini et al., "3-wave mixing Josephson dipole element", Appl. Phys. Lett. 110, 222603 (2017), arXiv:1702.00869 -- introduces the SNAIL; source of ``n = 3``, ``alpha = 0.29``, ``Phi_ext = 0.41 Phi_0``, ``I0 = 7.1 / 2.0 uA``. * Frattini et al., "Optimizing the Nonlinearity and Dissipation of a SNAIL Parametric Amplifier for Dynamic Range", Phys. Rev. Applied 10, 054020 (2018), arXiv:1806.06093 -- arraying (``M = 1, 10, 20``) to raise saturation power; also uses ``alpha = 0.09``. * Sivak et al., "Kerr-free three-wave mixing in superconducting quantum circuits", Phys. Rev. Applied 11, 054060 (2019), arXiv:1902.10575 -- Kerr-free operation, ``M = 20`` array, resonator tunable 6.2-7.2 GHz. * Miano et al., "Frequency-tunable Kerr-free three-wave mixing with a gradiometric SNAIL", Appl. Phys. Lett. 120, 184002 (2022), arXiv:2112.09785 -- restates the ``alpha < 1/3`` bound; two-loop gradiometric variant (not drawn here). * Josephson inductance relation: ``L_J = Phi_0/(2*pi*I0) = hbar/(2*e*I0)`` (``Phi_0 = h/2e``); ``I0 = 1 uA -> L_J = 329 pH``. Typical device parameters (measured values are from Frattini 2017 and recur in Frattini 2018 / Sivak 2019; inductances are computed from the critical currents). Useful when choosing junction inductances for a renderer/analysis: * ``n = 3`` large junctions, each ``I0 ~ 7.1 uA`` -> ``L_J ~ 46 pH`` (the ``Lj`` default). * one small junction, ``I0 ~ 2.0 uA`` -> ``L_J ~ 165 pH`` (the ``Lj_small`` default), i.e. ``L_J,small = L_J,large / alpha`` with ``alpha = I0_small / I0_large ~ 0.29``. * Kerr-free point near ``alpha ~ 0.29``, ``Phi_ext ~ 0.41 * Phi_0``; reported flux bias points cluster in ``Phi_ext/Phi_0 ~ 0.33-0.45``. * the three nominally-identical large junctions are made from two Dolan bridges (Al-on-Si); SNAILs are typically arrayed in series (``M = 10-20``) to raise saturation power -- this class draws one. * NOTE: the source papers specify the element *electrically* (critical currents, ``alpha``, flux) and do not tabulate on-chip junction overlap areas or loop dimensions, so the *layout* dimensions below are schematic (SQUID_LOOP-scale), not lifted from a published mask. Only the electrical parameters are grounded. Junction representation in Quantum Metal ---------------------------------------- A Josephson junction has two distinct representations in this library, and this component deliberately uses only the first: 1. **Schematic / analysis** -- a 2-point ``LineString`` in the ``junction`` qgeometry table with a ``width``. The Ansys/HFSS and pyEPR renderers turn each such row into a *lumped* Josephson inductance (``hfss_inductance``). This is what ``TransmonPocket`` does and what makes the element EM-/EPR-analyzable. It is a meshing placeholder, **not** the physical junction. 2. **Physical / fabrication** -- the real sub-micron junction geometry (overlapping fingers/pads on a separate e-beam layer), drawn by the dedicated p-cells ``jj_dolan`` (Dolan, Appl. Phys. Lett. 31, 337, 1977) and ``jj_manhattan`` (Pop et al., Nature 508, 369, 2014). Those carry the real ``um``-scale dimensions for the GDS mask and, per their own docstrings, **must not be rendered for EM simulation**. ``SNAIL`` emits the four junctions as the schematic LineStrings (three large sharing ``Lj``, one small with ``Lj_small``) -- the analysis path. For a fabrication-faithful mask, overlay a ``jj_dolan`` / ``jj_manhattan`` p-cell at each junction location on the e-beam layer and exclude the schematic junctions from the GDS export. Keeping the two representations in separate components is intentional: the physical junction has features that break EM meshing, while the lumped line is exactly what the simulator wants. ``alpha`` is ultimately set by the *physical* junction overlap areas / critical currents at fabrication; here the asymmetry is expressed for analysis through ``Lj`` vs ``Lj_small`` and marked geometrically by the narrower default ``segment_lower_width`` on the small-junction (lower) arm. Geometry -------- Follows the same rectangular-loop convention as ``SQUID_LOOP``, built from rectangles: a left plate (``plate1``), the upper arm (``seg a`` / ``seg ab`` / ``seg b`` separated by three ``JJ_gap`` breaks where the three large junctions sit), the lower arm (``seg a lower`` / ``seg b lower`` separated by a single ``JJ_gap_small`` break where the small junction sits), a vertical connector (``seg c``), a horizontal lead (``seg d``) and a right plate (``plate2``). The lower arm is automatically padded so that both arms close onto the same vertical connector, keeping the loop rectangular for any junction spacing. .. image:: SNAIL.png .. meta:: :description: SNAIL (Superconducting Nonlinear Asymmetric Inductive eLement) Default Options: * plate1_width: '5.5um' -- width of plate1 (left) * plate1_height: '40um' -- height of plate1 (left) * plate1_pos_x: '0' -- origin of plate1 (left) in x * plate1_pos_y: '0' -- origin of plate1 (left) in y * squid_gap: '10um' -- vertical space between the upper and lower arms * segment_a_length: '10um' -- length of seg a (first upper-arm segment) * segment_a_width: '1um' -- width of the upper-arm segments * JJ_gap: '0.5um' -- break for each of the three large junctions (upper arm) * segment_ab_length: '5um' -- length of the islands between the large junctions * segment_b_length: '5um' -- length of seg b (last upper-arm segment) * segment_b_width: '1um' -- width of seg b * JJ_gap_small: '0.3um' -- break for the single small junction (lower arm) * segment_lower_width: '0.6um' -- width of the lower (small-junction) arm * segment_c_width: '1um' -- width of the vertical connector seg c * segment_d_length: '10um' -- length of seg d * segment_d_width: '2um' -- width of seg d * plate2_width: '6um' -- width of plate 2 (right) * plate2_height: '30um' -- height of plate 2 (right) * Lj: '0.046nH' -- Josephson inductance of each large junction (~46 pH, from I0 ~ 7.1 uA in Frattini et al. 2017); written to the ``junction`` qgeometry table for HFSS/pyEPR * Lj_small: '0.165nH' -- Josephson inductance of the small junction (~165 pH, from I0 ~ 2.0 uA; equals Lj / alpha with alpha ~ 0.29) * inductor_width: '1um' -- width of the rendered junction strips (for HFSS/other EM software meshing; not the physical junction size) * pin_width: '5um' -- width of the connection pins on plate1/plate2 Pins: ``a`` (left, on plate1) and ``b`` (right, on plate2) so the SNAIL can be wired into a larger design with a route. The four junctions are emitted to the ``junction`` qgeometry table (three large with ``Lj``, one small with ``Lj_small``) so the element is HFSS-eigenmode / pyEPR analyzable; keep ``Cj = Rj = 0`` (the renderer default) for pyEPR. """ # Default drawing options default_options = Dict( plate1_width="5.5um", plate1_height="40um", plate1_pos_x="0", plate1_pos_y="0", squid_gap="10um", segment_a_length="10um", segment_a_width="1um", JJ_gap="0.5um", segment_ab_length="5um", segment_b_length="5um", segment_b_width="1um", JJ_gap_small="0.3um", segment_lower_width="0.6um", segment_c_width="1um", segment_d_length="10um", segment_d_width="2um", plate2_width="6um", plate2_height="30um", Lj="0.046nH", Lj_small="0.165nH", inductor_width="1um", pin_width="5um", ) """Default drawing options""" # Name prefix of component, if user doesn't provide name component_metadata = Dict( short_name="component", _qgeometry_table_poly="True", _qgeometry_table_junction="True", ) """Component metadata"""
[docs] def make(self): """Convert self.options into QGeometry.""" p = self.parse_options() # Parse the string options into numbers half_p1 = 0.5 * p.plate1_width # Total horizontal span of the upper arm: three large junctions # (``JJ_gap`` each) separating four conducting segments # (seg a, two intermediate islands, seg b). upper_arm_length = ( p.segment_a_length + 2.0 * p.segment_ab_length + p.segment_b_length + 3.0 * p.JJ_gap ) # draw the left plate as a rectangle plate1 = draw.rectangle( p.plate1_width, p.plate1_height, p.plate1_pos_x, p.plate1_pos_y ) # ----- Upper arm: three large junctions in series ----- # y-centre of the upper arm upper_y = 0.5 * (p.squid_gap + p.segment_a_width) segment_a = draw.rectangle( p.segment_a_length, p.segment_a_width, half_p1 + 0.5 * p.segment_a_length, upper_y, ) # first intermediate island, after seg a + first large junction ab1_start = half_p1 + p.segment_a_length + p.JJ_gap segment_ab1 = draw.rectangle( p.segment_ab_length, p.segment_a_width, ab1_start + 0.5 * p.segment_ab_length, upper_y, ) # second intermediate island, after the second large junction ab2_start = ab1_start + p.segment_ab_length + p.JJ_gap segment_ab2 = draw.rectangle( p.segment_ab_length, p.segment_a_width, ab2_start + 0.5 * p.segment_ab_length, upper_y, ) # seg b, after the third large junction b_start = ab2_start + p.segment_ab_length + p.JJ_gap segment_b = draw.rectangle( p.segment_b_length, p.segment_b_width, b_start + 0.5 * p.segment_b_length, 0.5 * (p.squid_gap + p.segment_b_width), ) # ----- Lower arm: single small junction ----- # The lower arm spans the same horizontal distance as the (longer) # upper arm so both arms close onto the shared vertical connector. # The single small junction is centred on the arm: this keeps both # lower segments at equal, positive length for any ``JJ_gap_small`` # up to the full arm span, avoiding the silent negative-width # rectangle (and broken loop) that an "extend seg b to fill" # scheme produces when the small-junction gap is large. lower_y = -0.5 * (p.squid_gap + p.segment_lower_width) lower_seg_length = max(0.0, 0.5 * (upper_arm_length - p.JJ_gap_small)) segment_a_lower = draw.rectangle( lower_seg_length, p.segment_lower_width, half_p1 + 0.5 * lower_seg_length, lower_y, ) segment_b_lower = draw.rectangle( lower_seg_length, p.segment_lower_width, half_p1 + upper_arm_length - 0.5 * lower_seg_length, lower_y, ) # ----- Vertical connector, horizontal lead and right plate ----- segment_c = draw.rectangle( p.segment_c_width, p.squid_gap + p.segment_a_width + p.segment_b_width, half_p1 + upper_arm_length + 0.5 * p.segment_c_width, p.plate1_pos_y, ) segment_d = draw.rectangle( p.segment_d_length, p.segment_d_width, half_p1 + upper_arm_length + p.segment_c_width + 0.5 * p.segment_d_length, p.plate1_pos_y, ) plate2 = draw.rectangle( p.plate2_width, p.plate2_height, half_p1 + upper_arm_length + p.segment_c_width + p.segment_d_length + 0.5 * p.plate2_width, p.plate1_pos_y, ) design1 = draw.union( plate1, segment_a, segment_ab1, segment_ab2, segment_b, segment_a_lower, segment_b_lower, segment_c, segment_d, plate2, ) # ----- Josephson junctions as 2-point lines (junction table) ----- # Each junction is a LineString spanning its gap, rendered by HFSS / # pyEPR as a *lumped* Josephson inductance (the schematic / analysis # representation, as in TransmonPocket). This is not the physical # junction: for a fabrication mask, overlay a jj_dolan / jj_manhattan # p-cell at each location on the e-beam layer. See the class docstring # ("Junction representation in Quantum Metal"). # Three large junctions on the upper arm, one small on the lower arm. jj_large_1 = draw.LineString( [(half_p1 + p.segment_a_length, upper_y), (ab1_start, upper_y)] ) jj_large_2 = draw.LineString( [(ab1_start + p.segment_ab_length, upper_y), (ab2_start, upper_y)] ) jj_large_3 = draw.LineString( [(ab2_start + p.segment_ab_length, upper_y), (b_start, upper_y)] ) jj_small = draw.LineString( [ (half_p1 + lower_seg_length, lower_y), (half_p1 + upper_arm_length - lower_seg_length, lower_y), ] ) # ----- Connection pins on the outer edges of the two plates ----- # Point-order sets the outward normal: pin ``a`` points -x off # plate1, pin ``b`` points +x off plate2. x_left = p.plate1_pos_x - half_p1 x_right = ( half_p1 + upper_arm_length + p.segment_c_width + p.segment_d_length + p.plate2_width ) pin_a_line = draw.LineString( [ (x_left, p.plate1_pos_y - 0.5 * p.pin_width), (x_left, p.plate1_pos_y + 0.5 * p.pin_width), ] ) pin_b_line = draw.LineString( [ (x_right, p.plate1_pos_y + 0.5 * p.pin_width), (x_right, p.plate1_pos_y - 0.5 * p.pin_width), ] ) # rotate and translate every object together so they stay aligned objects = [ design1, jj_large_1, jj_large_2, jj_large_3, jj_small, pin_a_line, pin_b_line, ] objects = draw.rotate(objects, p.orientation, origin=(0, 0)) objects = draw.translate(objects, p.pos_x, p.pos_y) ( design1, jj_large_1, jj_large_2, jj_large_3, jj_small, pin_a_line, pin_b_line, ) = objects self.add_qgeometry("poly", {"design": design1}, layer=p.layer, subtract=False) # three large junctions share Lj; the small junction uses Lj_small. # ``hfss_inductance`` is the column the Ansys renderer reads per row. self.add_qgeometry( "junction", dict( jj_large_1=jj_large_1, jj_large_2=jj_large_2, jj_large_3=jj_large_3, ), width=p.inductor_width, hfss_inductance=p.Lj, layer=p.layer, ) self.add_qgeometry( "junction", dict(jj_small=jj_small), width=p.inductor_width, hfss_inductance=p.Lj_small, layer=p.layer, ) self.add_pin("a", list(pin_a_line.coords), width=p.pin_width) self.add_pin("b", list(pin_b_line.coords), width=p.pin_width)