Source code for qpdk.models.junction

"""Josephson junction Models."""

import warnings
from functools import partial

import jax
import jax.numpy as jnp
import sax
from sax.models.rf import admittance

from qpdk.models.constants import DEFAULT_FREQUENCY, Φ_0


def _warn_if_overbiased(ib: float, ic: float) -> None:
    """Host callback to warn if bias current exceeds critical current."""
    if jnp.any(jnp.abs(ib) >= ic):
        warnings.warn(
            "DC bias |I_b| >= I_c detected. Linearized RCSJ model is invalid in the voltage state. "
            "Please check your 'ib' and 'ic' ('ic_tot', 'flux', 'asymmetry') inputs.",
            RuntimeWarning,
            stacklevel=0,
        )


[docs] @partial(jax.jit, inline=True) def josephson_junction( *, f: sax.FloatArrayLike = DEFAULT_FREQUENCY, ic: sax.Float = 1e-6, capacitance: sax.Float = 5e-15, resistance: sax.Float = 10e3, ib: sax.Float = 0.0, ) -> sax.SDict: r"""Josephson junction (RCSJ) small-signal Sax model. Linearized RCSJ model consisting of a bias-dependent Josephson inductance in parallel with capacitance and resistance. Valid in the superconducting (zero-voltage) state and for small AC signals. Default capacitance taken from :cite:`shcherbakovaFabricationMeasurementsHybrid2015`. See :cite:`McCumber1968` for details. Args: f: Array of frequency points in Hz ic: Critical current :math:`I_c` in Amperes capacitance: Junction capacitance :math:`C` in Farads resistance: Shunt resistance :math:`R` in Ohms ib: DC bias current :math:`I_b` in Amperes (:math:`\|I_b\| < I_c`) Returns: sax.SDict: S-parameters dictionary """ jax.debug.callback(_warn_if_overbiased, ib, ic) ω = 2 * jnp.pi * jnp.asarray(f) # Bias-dependent phase factor # jnp.clip ensures we don't get NaNs during compilation tracing or slightly overbiased states cos_Φ_0 = jnp.sqrt(jnp.clip(1.0 - (ib / ic) ** 2, a_min=1e-10)) # Josephson inductance Lⱼ = Φ_0 / (2 * jnp.pi * ic * cos_Φ_0) # Admittances (parallel RCSJ) Y_R = 1 / resistance Y_C = 1j * ω * capacitance Y_L = 1 / (1j * ω * Lⱼ) # Total admittance Y_JJ = Y_R + Y_C + Y_L return admittance(f=f, y=Y_JJ)
[docs] @partial(jax.jit, inline=True) def squid_junction( *, f: sax.FloatArrayLike = DEFAULT_FREQUENCY, ic_tot: sax.Float = 2e-6, asymmetry: sax.Float = 0.0, capacitance: sax.Float = 10e-15, resistance: sax.Float = 5e3, ib: sax.Float = 0.0, flux: sax.Float = 0.0, ) -> sax.SDict: r"""DC SQUID small-signal Sax model in the zero-screening limit. Treats the DC SQUID as a single effective RCSJ whose critical current is tunable by an external magnetic flux. Assumes negligible loop geometric inductance. See :cite:`kochChargeinsensitiveQubitDesign2007a` and :cite:`tinkhamIntroductionSuperconductivity2015` for details on asymmetric SQUIDs and effective Josephson inductance. Args: f: Array of frequency points in Hz ic_tot: Total critical current sum :math:`I_{c1} + I_{c2}` in Amperes asymmetry: Junction asymmetry :math:`(I_{c1} - I_{c2}) / I_{c,tot}` capacitance: Total SQUID capacitance :math:`C_1 + C_2` in Farads resistance: Total SQUID shunt resistance :math:`R_1 || R_2` in Ohms ib: DC bias current :math:`I_b` in Amperes flux: External magnetic flux :math:`\Phi_{ext}` in Webers Returns: sax.SDict: S-parameters dictionary """ ω = 2 * jnp.pi * jnp.asarray(f) # Normalized flux phi_reduced = jnp.pi * flux / Φ_0 # Flux-tunable critical current for an asymmetric SQUID ic_squid = ic_tot * jnp.sqrt( jnp.cos(phi_reduced) ** 2 + (asymmetry**2) * jnp.sin(phi_reduced) ** 2 ) jax.debug.callback(_warn_if_overbiased, ib, ic_squid) # Bias-dependent phase factor # Guard against ic_squid == 0 (e.g. symmetric SQUID at half-flux quantum) ratio_sq = jnp.where( ic_squid > 0, (ib / jnp.where(ic_squid > 0, ic_squid, 1.0)) ** 2, 0.0 ) cos_Φ_0_eff = jnp.sqrt(jnp.clip(1.0 - ratio_sq, a_min=1e-10)) # Effective Josephson inductance (will go to inf when ic_squid == 0) Lⱼ_eff = Φ_0 / (2 * jnp.pi * jnp.where(ic_squid > 0, ic_squid, 1e-20) * cos_Φ_0_eff) # Admittances (parallel RCSJ) Y_R = 1 / resistance Y_C = 1j * ω * capacitance # Guard against division by zero in Y_L if L_j_eff somehow goes to zero or Y_L -> 0 if L_j_eff is inf Y_L = jnp.where(ic_squid > 0, 1 / (1j * ω * Lⱼ_eff), 0j) # Total SQUID admittance Y_SQUID = Y_R + Y_C + Y_L return admittance(f=f, y=Y_SQUID)
if __name__ == "__main__": import matplotlib.pyplot as plt sax.set_port_naming_strategy("optical") f = jnp.linspace(1e9, 10e9, 500) s = josephson_junction(f=f, ic=1e-6, capacitance=50e-15, resistance=10e3, ib=0.5e-6) plt.figure() plt.plot(f / 1e9, jnp.abs(s[("o1", "o1")]), label="|S11|") plt.plot(f / 1e9, jnp.abs(s[("o1", "o2")]), label="|S12|") plt.plot(f / 1e9, jnp.abs(s[("o2", "o2")]), label="|S22|") plt.xlabel("Frequency [GHz]") plt.ylabel("Magnitude") plt.legend() plt.title("Josephson Junction S-parameters") plt.show()