Beispiel #1
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")
# 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)")
    lines = ax.plot(field * 1e3, np.transpose(ic[i]) * 1e6)
    for l, line in enumerate(lines):
        line.set_color(cmap(l / len(lines)))
Beispiel #3
0
n_ppls = np.arange(ppl_start, ppl_stop, ppl_step)
fidelity = np.empty(shape=(len(n_nodes), len(n_ppls)))

fig, ax = plt.subplots(constrained_layout=True)
ax.set_xlabel("x (um)")
ax.set_ylabel("J (uA/um)")
ax.plot(x / 1e-6, jx, color="k", label="original")

# choose a few reconstructions to plot (n_node, n_ppl)
should_plot = [(4, 40), (7, 60), (9, 30), (11, 40), (14, 40)]

for i, n_node in enumerate(n_nodes):
    for j, n_ppl in enumerate(n_ppls):
        b = np.linspace(-n_node * B_NODE, n_node * B_NODE, n_ppl * n_node * 2)
        ic = produce_fraunhofer_fast(b, B2BETA, jx, x)
        x_out, j_out = extract_current_distribution(b, ic, B2BETA, JJ_WIDTH,
                                                    100)
        fidelity[i, j] = np.sqrt(np.mean((j_out - j_true(x_out))**2))

        if (n_node, n_ppl) in should_plot:
            ax.plot(x_out / 1e-6, j_out, label=f"{n_node}, {n_ppl}")
ax.legend()

fig2, ax2 = plt.subplots(constrained_layout=True)
ax2.set_xlabel("B [mT]")
ax2.set_ylabel(r"$I_c$ [uA]")
ax2.plot(b / 1e-3, ic / 1e-6)

fig3, ax3 = plt.subplots(constrained_layout=True)
ax3.set_xlabel("points per lobe")
ax3.set_ylabel("# of nodes")
ax3.set_xticks(n_ppls)
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)')
Beispiel #5
0
fine_bs = np.linspace(-N_NODES * B_NODE, N_NODES * B_NODE, 10 * N_POINTS)
ax2.plot(
    fine_bs,
    IC0 * np.abs(np.sinc(fine_bs * B2BETA * JJ_WIDTH / 2 / np.pi)),
    lw=1,
    markersize=0,
    label="analytical",
    color=cmap(0),
)

bs = np.linspace(-N_NODES * B_NODE, N_NODES * B_NODE, N_POINTS)
TRIPS = 50
for i in range(TRIPS):
    # generate Fraunhofer
    ics = produce_fraunhofer_fast(bs, B2BETA, js, xs)
    ax2.plot(bs, ics, label=f"{2*i + 1}", color=cmap((2 * i + 1) / (2 * TRIPS)))

    # reconstruct current density
    xs, js = extract_current_distribution(bs, ics, B2BETA, JJ_WIDTH, len(xs) / 2)
    ax.plot(xs, js, label=f"{2*i + 2}", color=cmap((2 * i + 2) / (2 * TRIPS)))


# fig.legend()
# fig2.legend()
fig3, ax3 = plt.subplots(constrained_layout=True, figsize=[1, 5])
sm = mpl.cm.ScalarMappable(cmap=cmap)
sm.set_clim(0, TRIPS)
cbar = fig3.colorbar(sm, cax=ax3)
cbar.set_label("round trips")
plt.show()
Beispiel #6
0
         lw=1,
         marker=".",
         markersize=2,
         label="hilbert")
ax2.plot(b / 1e-3,
         theta_true_unwrapped / np.pi,
         lw=1,
         marker=".",
         markersize=2,
         label="true")
ax2.legend()

# compare current reconstruction using true vs. reconstructed phases
x_r, jx_r = extract_current_distribution(b,
                                         ic,
                                         b2beta,
                                         jj_width,
                                         100,
                                         theta=theta_r)
x_q, jx_q = extract_current_distribution(b,
                                         ic,
                                         b2beta,
                                         jj_width,
                                         100,
                                         theta=theta_q)
x_h, jx_h = extract_current_distribution(b,
                                         ic,
                                         b2beta,
                                         jj_width,
                                         100,
                                         theta=theta_h)
x_true, jx_true = extract_current_distribution(b,
Beispiel #7
0
        linewidth=0,
        marker='.',
        markersize=2,
        label='reconstructed')
# plot true phase modulo 2π (in units of π)
# small shift θ + ε forces points at 2π - ε into the 0 bin
ax.plot(b / 1e-3, (theta_true / np.pi + 1e-9) % 2,
        linewidth=0,
        marker='.',
        markersize=2,
        label='true')
ax.legend()
fig.show()

# compare current reconstruction using true vs. reconstructed phases
x2, jx2 = extract_current_distribution(b, ic, b2beta, jj_width, 100)
x2_true, jx2_true = extract_current_distribution(b,
                                                 ic,
                                                 b2beta,
                                                 jj_width,
                                                 100,
                                                 theta=theta_true)

fig, ax = plt.subplots(constrained_layout=True)
ax.set_xlabel('x [um]')
ax.set_ylabel('J [uA/um]')
ax.plot(x / 1e-6, jx, linewidth=2, label='original', color='black')
ax.plot(x2 / 1e-6, jx2, linewidth=2, label='reconstructed')
ax.plot(x2_true / 1e-6, jx2_true, linewidth=2, label='phase-corrected')
ax.legend()
fig.show()
Beispiel #8
0
    jx[mask] = js[1]
    mask = np.logical_and(mask, np.abs(x) < jj_width / 8)
    jx[mask] = js[2]

    gen_ic = produce_fraunhofer_fast(field_ / 1e3, b2beta, jx, x)

    fig, ax = plt.subplots(constrained_layout=True)
    ax.set_title(r"$V_{g,odd}$ = " + f"{gate_}")
    ax.set_xlabel("Magnetic field (mT)")
    ax.set_ylabel("Bias current (µA)")
    ax.plot(field_, gen_ic * 1e6, label="generated")
    ax.plot(field_, ic_, label="data")
    ax.set_ylim(0, 1.6)
    ax.legend()

    x_data, jx_data = extract_current_distribution(field_ / 1e3, ic_, b2beta,
                                                   jj_width, 200)
    x_gen, jx_gen = extract_current_distribution(field_ / 1e3, gen_ic, b2beta,
                                                 jj_width, 200)

    fig2, ax2 = plt.subplots(constrained_layout=True)
    ax2.set_title(r"$V_{g,odd}$ = " + f"{gate_}")
    ax2.set_xlabel("x (µm)")
    ax2.set_ylabel("Current density (µA/µm)")
    ax2.plot(x[175:-175] * 1e6,
             jx[175:-175],
             color="black",
             label="manually optimized")
    ax2.plot(x_gen * 1e6, jx_gen, label="from generated")
    ax2.plot(x_data * 1e6, jx_data / 1e6, label="from data")
    ax2.set_xlim(-3, 3)
    ax2.set_ylim(-0.15, 1)
Beispiel #9
0
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)

# Reconstruct supercurrent distribution and plot
x, jx = extract_current_distribution(field, ic, FIELD_TO_WAVENUM, JJ_WIDTH,
                                     len(field))
fig, ax = plt.subplots(constrained_layout=True)
ax.set_xlabel(r"$x$ (μm)")
ax.set_ylabel(r"$J(x)$ (μA/μm)")
ax.plot(x * 1e6, jx)
plt.show()
# fig.savefig("077_hi-res-current-density.pdf")