# 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)")
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")
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)')
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)')
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)')