Exemplo n.º 1
0
def test_extract_switching():
    """Test extracting the switching current offset for 1D, 2D and 3D data.

    """
    current = np.linspace(-1, 1, 101)
    voltage = np.concatenate(
        (np.linspace(-1, -0.1, 40), np.zeros(31), np.linspace(0.1, 1, 30)))

    assert extract_switching_current(current, voltage, 0.04) == current[70]
    assert extract_switching_current(current, voltage, 0.04,
                                     "negative") == current[40]
    assert (extract_switching_current(current,
                                      voltage + 0.1,
                                      0.04,
                                      offset_correction=10) == current[70])

    c2d = np.empty((2, 101))
    c2d[0] = current
    c2d[1] = -current
    v2d = np.empty((2, 101))
    v2d[0] = voltage
    v2d[1] = -voltage + 1

    np.testing.assert_equal(
        extract_switching_current(c2d, v2d, 0.04, offset_correction=10),
        np.array([current[70], -current[40]]),
    )

    c3d = np.empty((2, 2, 101))
    c3d[..., :] = current
    v3d = np.empty((2, 2, 101))
    v3d[0, 0] = voltage
    v3d[0, 1] = -voltage[::-1]
    v3d[1, 0] = -voltage[::-1] + 0.1
    v3d[1, 1] = voltage - 0.1

    np.testing.assert_equal(
        extract_switching_current(c3d, v3d, 0.04, offset_correction=10),
        np.array([[current[70], -current[40]], [-current[40], current[70]]]),
    )
    # NOTE: Also, in this case we have to manually correct some Labber shenanigans by
    # flipping some data.
    gate_3 = np.flip(np.unique(f.get_data(CH_GATE_3)))
    gate_2_4 = np.flip(np.unique(f.get_data(CH_GATE_2_4)))
    field = np.unique(f.get_data(CH_MAGNET)) * CURR_TO_FIELD

    # Bias current from the custom Labber driver VICurveTracer isn't available via
    # LabberData methods.
    bias = np.unique(f._file["/Traces/VITracer - VI curve"][:, 1, :])

    resist = f.get_data(CH_RESIST)

# extract_switching_current chokes on 1D arrays. Construct the ndarray of bias sweeps
# for each (gate, field) to match the shape of the resistance ndarray
ic = extract_switching_current(
    np.tile(bias, resist.shape[:-1] + (1,)), resist, threshold=2.96e-3,
)

# NOTE: Here, every other fraunhofer appears flipped horizontally (i.e.  field B -> -B)
# 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,))
Exemplo n.º 3
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.º 4
0
def plot_inplane_vs_IcRn(
    inplane_field: np.ndarray,
    bias: np.ndarray,
    voltage_drop: np.ndarray,
    ic_voltage_threshold: float,
    high_bias_threshold: float,
    ic_extraction_method: Optional[str] = 'iv_analysis',
    switching_bias_threshold: Optional[float] = 2e-5,
    savgol_windowl: Optional[int] = None,
    savgol_polyorder: Optional[int] = None,
    icrn_limits: Optional[np.ndarray] = None,
    in_field_limits: Optional[np.ndarray] = None,
    fig_size: Optional[np.ndarray] = None,
    debug: bool = False,
) -> None:
    """Plot Ic*Rn as a function of in-plane magnetic field.

    Parameters
    ----------
    inplane_field : np.ndarray
        2D array of the applied in-plane magnetic field.
    bias : np.ndarray
        2D array of the bias current.
    voltage_drop : np.ndarray
        2D array of the measured voltage drop.
    ic_voltage_threshold : float
        Voltage threshold in V above which the junction is not considered to carry a
        supercurrent anymore. Used in the determination of the critical current.
    high_bias_threshold : float
        Positive bias value above which the data can be used to extract the
        normal resistance.
    ic_extraction_method: str, Optional
        Choose method to extract critical current. Default is using "iv_analysis" function but sometimes
        "extract_switching_current" works better.
    switching_bias_threshold: float, Optional
        Value of threshold for extract_switching_current function    
    savgol_windowl : int, optional
        Window length of savgol_filter.
    savgol_polyorder: int, optional
        Polyorder of savgol_filter.
    icrn_limits : np.ndarray, optional
        icrn axis plot limits.
    in_field_limits : np.ndarray, optional
        In-plane field 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()

    # Use savgol_filter if params are available
    voltage_drop = savgol_filter(
        voltage_drop, savgol_windowl, savgol_polyorder
    ) if savgol_windowl and savgol_polyorder else voltage_drop

    x = analyse_vi_curve(bias, voltage_drop, ic_voltage_threshold,
                         high_bias_threshold)
    if ic_extraction_method == 'extract_switching_current':
        ic = extract_switching_current(bias, voltage_drop,
                                       switching_bias_threshold)
    elif ic_extraction_method == 'iv_analysis':
        ic = x[2]

    m_ax.grid()
    m_ax.plot(inplane_field[:, 0] * 1000,
              x[0] * ic * 1e6,
              color='green',
              linewidth=5,
              marker='x',
              markersize=10)
    m_ax.set_xlabel('In-plane Field (mT)', labelpad=20)
    m_ax.set_ylabel(r'I$_{c}$R$_{n}$(Ω)', labelpad=20, color='green')
    if in_field_limits:
        m_ax.set_xlim(in_field_limits)
    if icrn_limits:
        m_ax.set_ylim(icrn_limits)
Exemplo n.º 5
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.º 6
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)')
Exemplo n.º 7
0
resist = []
ic = []
with LabberData(str(DATA_FILE_PATH)) as f:
    channels = f.channel_names

    field = np.unique(f.get_data(CH_MAGNET))[:-10] * CURR_TO_FIELD

    gate = np.unique(f.get_data(CH_GATE))
    for g in gate:
        bias = f.get_data(CH_BIAS, filters={CH_GATE: g})[:-10]
        resist.append(
            np.abs(
                np.real(VOLT_TO_RESIST *
                        f.get_data(CH_RESIST, filters={CH_GATE: g})[:-10])))
        ic.append(extract_switching_current(bias, resist[-1], 5, "positive"))
resist = np.array(resist)
ic = np.array(ic)
# bias sweeps should be the same for all gate values
bias_min, bias_max = np.min(bias), np.max(bias)

jss = [[0.05, 0.26, 0.1], [], [0.5, 0.26, 0.18], [], [0.9, 0.26, 0.4]]
gate_V_selection = [-1, 0, 1]
for gate_, resist_, ic_, js in zip(gate, resist, ic, jss):
    if gate_ not in gate_V_selection:
        continue

    field_ = field - find_fraunhofer_center(field, ic_)
    field_, ic_ = symmetrize_fraunhofer(field_, ic_)

    # zero-padded uniform current distributions under variable gate widths
Exemplo n.º 8
0
    extent=[
        (field.min() - dfield) * 1e3,
        (field.max() + dfield) * 1e3,
        # recall dV is diffed along bias and shrinks by 1 element
        (bias.min() + dbias) * 1e6,
        (bias.max() - dbias) * 1e6,
    ],
    aspect=3 / 2,
)
cb = fig.colorbar(im, label=r"$\Delta V / \Delta I$ (kΩ)")
plt.show()
# fig.savefig('077_wide-fraunhofer-dVdI_corrected.pdf')

# extract_switching_current needs arrays of the same shape: use tile
ic = extract_switching_current(np.tile(bias, volt.shape[:-1] + (1, )),
                               volt,
                               threshold=2.32e-3)

# Plot critical current
fig, ax = plt.subplots(constrained_layout=True, figsize=(9, 5))
ax.set_xlabel(r"$B_\perp$ (mT)")
ax.set_ylabel(r"$I_c$ (μA)")
ax.plot(field * 1e3, ic * 1e6)
plt.show()
# fig.savefig('077_wide-fraunhofer-Ic_corrected.pdf')

# Center and symmetrize fraunhofer (optional; this fraunhofer is already highly
# symmetric and centered).
# field = field - find_fraunhofer_center(field, ic)
# field, ic = symmetrize_fraunhofer(field, ic)
Exemplo n.º 9
0
OUTDIR = Path("plots")
OUTDIR.mkdir(exist_ok=True)
print(f"Output will be saved to {str(OUTDIR)}/")

# from vector9 fridge
FILENAME = "JS602-SE1_4xFlQpcSq-v1_N_WFSBHE01-071"
PATH = get_data_dir() / f"2021/10/Data_1030/{FILENAME}.hdf5"
with LabberData(PATH) as f:
    bfield = f.get_data("Vector Magnet - Field X")
    ibias, dc_volts = f.get_data("VITracer - VI curve", get_x=True)
    dc_volts /= 100  # amplifier gain 100x
    iflux = f.get_data("circleFL 6 - Source current")

# plot extracted switching current for a few flux current values
ic = extract_switching_current(ibias, dc_volts, threshold=3.5e-5)
fig, ax = plot(
    np.unique(bfield) / 1e-6,
    ic[::2].T / 1e-6,
    label=[f"{int(i / 1e-6)} μA" for i in np.unique(iflux[::2])],
    xlabel="Vector Magnet Field (μT)",
    ylabel="Current Bias (μA)",
)
fig.savefig(OUTDIR / f"{FILENAME}_Ic.png")

# broadcast scalar data over vectorial data to have same shape
iflux, bfield, ibias, dc_volts = np.broadcast_arrays(iflux[..., np.newaxis],
                                                     bfield[..., np.newaxis],
                                                     ibias, dc_volts)

# plot the first CPR