# when compared to Labber's Log Viewer.  However, Labber's Log Viewer shows a field
# offset that systematically changes sign on every other fraunhofer. This suggests that
# the Log Viewer incorrectly flips every other fraunhofer.  To recover the data as
# viewed in Log Viewer, uncomment this line.  The fraunhofers are centered and
# symmetrized before current reconstruction, so it shouldn't matter.
# ic[:, 1::2, :] = np.flip(ic[:, 1::2, :], axis=-1)

# 183 is the largest number of points returned by symmetrize_fraunhofer
# extract_current_distribution then returns max 183*2 = 366 points
POINTS = 366
x = np.empty(shape=ic.shape[:-1] + (POINTS,))
jx = np.empty(shape=ic.shape[:-1] + (POINTS,))
for i, g3 in enumerate(gate_3):
    for j, g24 in enumerate(gate_2_4):
        ic_ = ic[i, j]
        field_ = field - find_fraunhofer_center(field, ic_)
        field_, ic_ = symmetrize_fraunhofer(field_, ic_)
        x_, jx_ = extract_current_distribution(
            field_, ic_, FIELD_TO_WAVENUM, JJ_WIDTH, len(field_)
        )
        x[i, j] = np.pad(x_, (POINTS - len(x_)) // 2, mode="edge")
        jx[i, j] = np.pad(jx_, (POINTS - len(jx_)) // 2, mode="edge")

# There are 11x10 fraunhofers, 1 for each (Vg3, Vg2=Vg4) combination.
# Make 21 plots by fixing Vg3 and sweeping over Vg2=Vg4, and vice versa.
cmap = plt.get_cmap("inferno")
for i, g3 in enumerate(gate_3):
    fig, ax = plt.subplots(constrained_layout=True)
    ax.set_title(r"$V_\mathrm{g1,g5} = 0$, $V_\mathrm{g3} = $" + f"{g3} V")
    ax.set_xlabel(r"$B_\perp$ (mT)")
    ax.set_ylabel(r"$I_c$ (μA)")
Exemplo n.º 2
0
def reconstruct_current(name, obj):
    scan = name.split("/")[-1].split("::")[-1]
    if scan not in scans.keys():
        return None
    print(scan, name)
    params = scans[scan]

    field = obj["x magnet field"]
    ibias = obj["dc current bias"]
    dvdi = np.abs(obj["lock-in (impedance)"])

    # extract fraunhofer from data
    ic = extract_switching_current(ibias, dvdi, threshold=params["threshold"])
    fig, ax = plot2d(field, ibias, dvdi)
    plot(
        np.unique(field),
        ic,
        ax=ax,
        title="Ic extraction",
        color="k",
        lw="0",
        marker=".",
        markersize=5,
    )
    ax.axis("off")
    stamp(ax, scan)
    fig.savefig(outdir / f"{scan}_ic-extraction.png")

    field = np.unique(field)

    # center fraunhofer
    argmax_ic = find_fraunhofer_center(field, ic)
    ic_n = extract_switching_current(ibias,
                                     dvdi,
                                     threshold=params["threshold"],
                                     side="negative")
    argmax_ic_n = find_fraunhofer_center(field, np.abs(ic_n))
    field -= (argmax_ic + argmax_ic_n) / 2
    # field = recenter_fraunhofer(np.unique(field), ic) # if just centering at argmax(Ic+)
    fig, ax = plot(
        field / 1e-3,
        np.array([ic, ic_n]).T / 1e-6,
        xlabel="field (mT)",
        ylabel="critical current (μA)",
        title="fraunhofer centered",
    )
    ax.axvline(0, color="k")
    fig.savefig(outdir / f"{scan}_fraunhofer-centered.png")

    # reconstruct current distribution
    x, jx = extract_current_distribution(
        field,
        ic,
        f2k=2 * np.pi * params["jj_length"] / phys_c["mag. flux quantum"][0],
        jj_width=params["jj_width"],
        jj_points=400,
    )
    fig, ax = subplots()
    ax.fill_between(x / 1e-6, jx)
    ax.set_xlabel("position (μA)")
    ax.set_ylabel("critical current density (μA/μm)")
    ax.set_title(
        f"$\ell_\mathrm{{eff}}$ = {round(params['jj_length'] / 1e-6, 1)}μm")
    fig.savefig(outdir / f"{scan}_current-distribution.png")
Exemplo n.º 3
0
def plot_extracted_critical_current(
    out_field: np.ndarray,
    bias: np.ndarray,
    voltage_drop: np.ndarray,
    points_mask: int = 10,
    peak_height: float = 0.8,
    current_field_conversion: Optional[float] = None,
    symmetrize_fraun: Optional[bool] = False,
    savgol_windowl: Optional[int] = None,
    savgol_polyorder: Optional[int] = None,
    bias_limits: Optional[np.ndarray] = None,
    out_field_limits: Optional[np.ndarray] = None,
    fig_size: Optional[np.ndarray] = None,
    debug: bool = False,
) -> None:
    """Plot the extracted critical current (Ic) as a function of out-of-plane magnetic field from Fraunhofer pattern.

    Parameters
    ----------
    out_field : np.ndarray
        2D array of the applied out of plane field.
    bias : np.ndarray
        2D array of the bias current.
     voltage_drop : np.ndarray
        2D array of the voltage drop across junction.
    points_mask : int
        Number of points to ignore on the sides of the VI curve when calculating derivative to find peaks 
        because sometimes there's abnormal peaks towards the sides.
    peak_heights : float
        Fraction of max height used for height in find_peaks.
    current_field_conversion : float, optional
        Convert current being applied by Keithley to out-of-plane field in units of mA:mT. 
    symmetrize_fraun : bool, optional
        Do you want to symmetrize the Fraunhofer or not. Symmetrizing is best when the Fraunhofer
        field range is uneven
    savgol_windowl : int, optional
        Window length of savgol_filter (has to be an odd number).
    savgol_polyorder: int, optional
        Polyorder of savgol_filter.
    bias_limits : np.ndarray, optional
        Bias axis plot limits.
    out_field_limits : np.ndarray, optional
        Out-of-plane field axis plot limits.
    fig_size : np.ndarray, optional
        Figure size of plot.
    debug : bool, optional
        Should debug information be provided, by default False.

    """
    f = plt.figure(
        constrained_layout=True,
        figsize=fig_size if fig_size else mpl.rcParams['figure.figsize'])
    m_ax = f.gca()

    #Convert from current to field if conversion factor is available
    out_field = out_field / current_field_conversion if current_field_conversion else out_field

    #Extract critical current and use savgol_filter if params are available
    ic = extract_critical_current(bias,
                                  voltage_drop,
                                  points_mask=points_mask,
                                  peak_height=peak_height)
    ic = savgol_filter(
        ic, savgol_windowl,
        savgol_polyorder) if savgol_windowl and savgol_polyorder else ic

    #Find max of fraunhofer(which should be in the center) and center field around 0
    centered_field = out_field[:, 0] - find_fraunhofer_center(
        out_field[:, 0], ic)

    #Symmetrize the field and ic
    if symmetrize_fraun:
        centered_field, ic = symmetrize_fraunhofer(centered_field, ic)

    pm = m_ax.plot(
        centered_field * 1e3,  # field: 1e3 factor converts from T to mT
        ic * 1e6,  #ic: 1e6 factor converts from A to µA
        color='royalblue',
        linewidth=5)

    if out_field_limits:
        m_ax.set_xlim(out_field_limits)
    if bias_limits:
        m_ax.set_ylim(bias_limits)

    m_ax.grid()
    m_ax.set_xlabel('Out-of-plane field (mT)', labelpad=20)
    m_ax.set_ylabel('I$_{c}$ (µA)')
Exemplo n.º 4
0
def plot_current_distribution(
    out_field: np.ndarray,
    bias: np.ndarray,
    voltage_drop: np.ndarray,
    threshold: float,
    jj_length: float,
    jj_width: float,
    out_field_range: Optional[float] = None,
    current_field_conversion: Optional[float] = None,
    correct_v_offset: Optional[bool] = True,
    symmetrize_fraun: Optional[bool] = False,
    center_fraun: Optional[bool] = True,
    savgol_windowl: Optional[int] = None,
    savgol_polyorder: Optional[int] = None,
    x_limits: Optional[np.ndarray] = None,
    jx_limits: Optional[np.ndarray] = None,
    fig_size: Optional[np.ndarray] = None,
    debug: bool = False,
) -> None:
    """Plot to calculate the current distribution from the extracted switching current of the Fraunhofer pattern.

    Parameters
    ----------
    out_field : np.ndarray
        2D array of the applied out of plane field.
    bias : np.ndarray
        2D array of the bias current.
    voltage_drop : np.ndarray
        2D array of the differential resistance.
    threshold : float
        Since there's a shift in the DMM the superconducting region isn't exactly around zero. This value is not constant and needs to be adjusted.
        This threshold sets the voltage range around zero used to determine the swicthing current. Usually the threshold is of the order of 1e-4.
    jj_length : float
        Length of the junction in meters.
    jj_width : float
        Width of the junction in meters.
    out_field_range : float
        Range of out-of-plane field to extract critical current from.
    current_field_conversion : float, optional
        Convert current being applied by Keithley to out-of-plane field in units of mA:mT
    correct_v_offset : bool, optional
        Correct voltage offset when extracting switching current or not
    symmetrize_fraun : bool, optional
        Do you want to symmetrize the Fraunhofer or not. Symmetrizing is best when the Fraunhofer
        field range is uneven
    center_fraun : bool, optional
        Center the fraunofer pattern around 0mT.  
    savgol_windowl : int, optional
        Window length of savgol_filter (has to be an odd number).
    savgol_polyorder: int, optional
        Polyorder of savgol_filter.
    x_limits: np.ndarray, optional
        x axis plot limits.
    jx_limits: np.ndarray, optional
        jx axis plot limits.
    fig_size : np.ndarray, optional
        Figure size of plot.
    debug : bool, optional
        Should debug information be provided, by default False.

    """

    PHI0 = cs.h / (2 * cs.e)  # magnetic flux quantum
    FIELD_TO_WAVENUM = 2 * np.pi * jj_length / PHI0  # B-field to beta wavenumber
    PERIOD = 2 * np.pi / (FIELD_TO_WAVENUM * jj_width)

    f = plt.figure(
        constrained_layout=True,
        figsize=fig_size if fig_size else mpl.rcParams['figure.figsize'])
    m_ax = f.gca()

    #Convert from current to field if conversion factor is available
    out_field = out_field / current_field_conversion if current_field_conversion else out_field

    if out_field_range:
        mask = (out_field[:, 0] < out_field_range) & (-out_field_range <
                                                      out_field[:, 0])
        out_field = out_field[mask]
        bias = bias[mask]
        voltage_drop = voltage_drop[mask]
    #Extract switching current and use savgol_filter if params are available
    ic = extract_switching_current(
        bias,
        voltage_drop,
        threshold=threshold,
        correct_v_offset=True if correct_v_offset else None)
    # ic = ic-min(ic)
    ic = savgol_filter(
        ic, savgol_windowl,
        savgol_polyorder) if savgol_windowl and savgol_polyorder else ic

    #Find max of fraunhofer(which should be in the center) and center field around 0
    field = out_field[:, 0] - find_fraunhofer_center(
        out_field[:, 0], ic) if center_fraun else out_field

    #Symmetrize the field and ic
    if symmetrize_fraun: field, ic = symmetrize_fraunhofer(field, ic)

    #Extract current distributions with symmertizied field and ic
    x, jx = extract_current_distribution(field, ic, FIELD_TO_WAVENUM, jj_width,
                                         len(out_field))

    pm = m_ax.plot(
        x * 1e6,  #x: 1e6 factor converts from m into µm
        jx.real,  #  Jx: is in units of µA/µm
        linewidth=7)

    m_ax.fill_between(x * 1e6, jx.real, facecolor='lightblue')

    if x_limits:
        m_ax.set_xlims(x_limits)
    if jx_limits:
        m_ax.set_ylims(jx_limits)

    m_ax.set_xlabel(r'x (µm)')
    m_ax.set_ylabel(r'J$_{x}$ (µA/µm)')
Exemplo n.º 5
0
def plot_extracted_switching_current(
    out_field: np.ndarray,
    bias: np.ndarray,
    voltage_drop: np.ndarray,
    threshold: float,
    out_field_range: Optional[float] = None,
    current_field_conversion: Optional[float] = None,
    correct_v_offset: Optional[bool] = True,
    symmetrize_fraun: Optional[bool] = False,
    center_fraun: Optional[bool] = True,
    savgol_windowl: Optional[int] = None,
    savgol_polyorder: Optional[int] = None,
    bias_limits: Optional[np.ndarray] = None,
    out_field_limits: Optional[np.ndarray] = None,
    fig_size: Optional[np.ndarray] = None,
    debug: bool = False,
) -> None:
    """Plot the extracted switching current (Ic) as a function of out-of-plane magnetic field from Fraunhofer pattern.

    Parameters
    ----------
    out_field : np.ndarray
        2D array of the applied out of plane field.
    bias : np.ndarray
        2D array of the bias current.
    voltage_drop : np.ndarray
        2D array of the voltage drop across junction.
    threshold : float
        Since there's a shift in the DMM the superconducting region isn't exactly around zero. This value is not constant and needs to be adjusted.
        This threshold sets the voltage range around zero used to determine the swicthing current. Usually the threshold is of the order of 1e-4.
    out_field_range : float
        Range of out-of-plane field to extract critical current from.
    current_field_conversion : float, optional
        Convert current being applied by Keithley to out-of-plane field in units of mA:mT. 
    correct_v_offset : bool, optional
        Correct voltage offset when extracting switching current or not
    symmetrize_fraun : bool, optional
        Do you want to symmetrize the Fraunhofer or not. Symmetrizing is best when the Fraunhofer
        field range is uneven    
    center_fraun : bool, optional
        Center the fraunofer pattern around 0mT.  
    savgol_windowl : int, optional
        Window length of savgol_filter (has to be an odd number).
    savgol_polyorder: int, optional
        Polyorder of savgol_filter.
    bias_limits : np.ndarray, optional
        Bias axis plot limits.
    out_field_limits : np.ndarray, optional
        Out-of-plane field axis plot limits.
    fig_size : np.ndarray, optional
        Figure size of plot.
    debug : bool, optional
        Should debug information be provided, by default False.

    """
    f = plt.figure(
        constrained_layout=True,
        figsize=fig_size if fig_size else mpl.rcParams['figure.figsize'])
    m_ax = f.gca()

    #Convert from current to field if conversion factor is available
    out_field = out_field / current_field_conversion if current_field_conversion else out_field

    if out_field_range:
        mask = (out_field[:, 0] < out_field_range) & (-out_field_range <
                                                      out_field[:, 0])
        out_field = out_field[mask]
        bias = bias[mask]
        voltage_drop = voltage_drop[mask]

    #Extract switching current and use savgol_filter if params are available
    ic = extract_switching_current(
        bias,
        voltage_drop,
        threshold=threshold,
        correct_v_offset=True if correct_v_offset else None)
    # ic = ic-min(ic)
    ic = savgol_filter(
        ic, savgol_windowl,
        savgol_polyorder) if savgol_windowl and savgol_polyorder else ic

    #Find max of fraunhofer(which should be in the center) and center field around 0
    field = out_field[:, 0] - find_fraunhofer_center(
        out_field[:, 0], ic) if center_fraun else out_field

    #Symmetrize the field and ic
    if symmetrize_fraun: field, ic = symmetrize_fraunhofer(field, ic)

    pm = m_ax.plot(
        field * 1e3,  #field: 1e3 factor to convert from T to mT 
        ic * 1e6,  #ic: 1e6 factor to convert from A to µA
        color='royalblue',
        linewidth=3)

    if out_field_limits:
        m_ax.set_xlim(out_field_limits)
    if bias_limits:
        m_ax.set_ylim(bias_limits)

    m_ax.grid()
    m_ax.set_xlabel('Out-of-plane field (mT)', labelpad=20)
    m_ax.set_ylabel('I$_{c}$ (µA)')