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)))
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)')
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()
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,
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()
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)
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")