def print_info(self): """Print information about the embedding circuit to the terminal.""" print(self) str1 = " f={0}, p={1}\t\t\tvph = {2:.4f} x {1}\t\t{3}" str2 = " f={0}, p={1}\t\t\t{2:.1f} GHz x {1}\t\t{3}" str3 = "\tThev. voltage:\t\t{:.4f} * Vgap" str6 = "\t \t\t{:.4f} * Vph" str4 = "\tThev. impedance:\t{:.2f} * Rn" str7 = "\tAvail. power: \t{:.2E} W" str8 = "\t \t{:.3f} dBm" if self.fgap is None or self.vgap is None or self.rn is None: for f in range(1, self.num_f + 1): for p in range(1, self.num_p + 1): vph = self.vph[f] vt = self.vt[f, p] zt = self.zt[f, p] cprint(str1.format(f, p, vph, self.comment[f][p]), 'GREEN') print(str3.format(float(vt.real))) print(str4.format(zt)) else: for f in range(1, self.num_f + 1): for p in range(1, self.num_p + 1): fq = self.vph[f] * self.fgap / 1e9 vt = self.vt[f, p] zt = self.zt[f, p] with np.errstate(divide='ignore'): power_w = self.available_power(f, p, units='W') power_dbm = self.available_power(f, p, units='dBm') cprint(str2.format(f, p, fq, self.comment[f][p]), 'GREEN') print(str3.format(float(vt.real))) print(str6.format(float(vt.real / (self.vph[f] * p)))) print(str4.format(zt)) print(str7.format(power_w)) print(str8.format(power_dbm)) print("")
def dciv_curve(ivdata, **kwargs): """Import and analyze DC I-V data (a.k.a., the unpumped I-V curve). Args: ivdata: DC I-V data. A Numpy array. The data should have two columns: the first for voltage, and the second for current. Keyword Args: v_fmt (str): Units for voltage ('uV', 'mV', or 'V'). i_fmt (str): Units for current ('uA', 'mA', or 'A'). vmax (float): Maximum voltage to import in units [V]. npts (int): Number of points to have in I-V interpolation. debug (bool): Plot each step of the I-V processing procedure. voffset (float): Voltage offset, in units [V]. ioffset (float): Current offset, in units [A]. voffset_range (list): Voltage range over which to search for offset, in units [V]. voffset_sigma (float): Standard deviation of Gaussian filter when searching for offset. rseries (float): Series resistance in experimental measurement system, in units [ohms]. i_multiplier (float): Multiply the imported current by this value. v_multiplier (float): Multiply the imported voltage by this value. filter_data (bool): Filter data vgap_guess (float): Guess of gap voltage. Used to temporarily normalize while filtering. Given in units [V]. igap_guess (float): Guess of gap current. Used to temporarily normalize while filtering. Given in units [A]. filter_theta (float): Angle by which to the rotate data while filtering. Given in radians. filter_nwind (int): Window size for Savitsky-Golay filter. filter_npoly (int): Order of Savitsky-Golay filter. vrn (list): Voltage range over which to calculate the normal resistance, in units [V] rn_vmin (float): Lower voltage range to determine the normal resistance, in units [V] (DEPRECATED) rn_vmax (float): Upper voltage range to determine the normal resistance, in units [V] (DEPRECATED) vrsg (float): The voltage at which to calculate the subgap resistance. vleak (float): The voltage at which to calculate the subgap leakage current. Returns: tuple: normalized voltage, normalized current, DC I-V metadata """ # Unpack keyword arguments # Use default values from qmix.exp.parameters if they aren't provided v_multiplier = kwargs.get('v_multiplier', PARAMS['v_multiplier']) i_multiplier = kwargs.get('i_multiplier', PARAMS['i_multiplier']) filter_data = kwargs.get('filter_data', PARAMS['filter_data']) rseries = kwargs.get('rseries', PARAMS['rseries']) debug = kwargs.get('debug', PARAMS['debug']) vmax = kwargs.get('vmax', PARAMS['vmax']) npts = kwargs.get('npts', PARAMS['npts']) # Import and do some basic cleaning (remove nans, sort by voltage, etc.) # voltage in V, current in A volt_v, curr_a = _load_iv(ivdata, **kwargs) if debug: # pragma: no cover plt.figure() plt.plot(volt_v * 1e3, curr_a * 1e6) plt.title('Initial import (raw data)') plt.xlabel("Voltage (mV)") plt.ylabel("Current (uA)") plt.grid() plt.show() # Correct for DC gain errors in experimental system (if required) volt_v *= v_multiplier curr_a *= i_multiplier # Correct offsets in I-V data volt_v, curr_a, offset = _correct_offset(volt_v, curr_a, **kwargs) if debug: # pragma: no cover plt.figure() plt.plot(volt_v * 1e3, curr_a * 1e6) plt.title('After correcting for the I/V offset') plt.xlabel("Voltage (mV)") plt.ylabel("Current (uA)") plt.grid() plt.show() # Filter I-V data volt_v, curr_a = _filter_iv_data(volt_v, curr_a, **kwargs) if debug and filter_data: # pragma: no cover plt.figure() plt.plot(volt_v * 1e3, curr_a * 1e6) plt.title('After filtering I-V curve') plt.xlabel("Voltage (mV)") plt.ylabel("Current (uA)") plt.grid() plt.show() # Save uncorrected data vraw, iraw = volt_v.copy(), curr_a.copy() # Correct for series resistances in DC biasing system volt_v, curr_a = _correct_series_resistance(volt_v, curr_a, **kwargs) if debug: # pragma: no cover plt.figure() plt.plot(volt_v * 1e3, curr_a * 1e6) plt.title('After correcting for the series resistance') plt.xlabel("Voltage (mV)") plt.ylabel("Current (uA)") plt.grid() plt.show() # Analyze properties of DC I-V curve vgap = _find_gap_voltage(volt_v, curr_a, **kwargs) rn, vint = _find_normal_resistance(volt_v, curr_a, **kwargs) rsg = _find_subgap_resistance(volt_v, curr_a, **kwargs) fgap = sc.e * vgap / sc.h igap = vgap / rn # Warnings if rn < 1: cprint('\nWarning: Normal resistance is very low...', 'RED') cprint(' Are you sure you have the right units?\n', 'RED') if rn > 50: cprint('\nWarning: Normal resistance is very high...', 'RED') cprint(' Are you sure you have the right units?\n', 'RED') # Normalize I-V curve voltage = volt_v / vgap current = curr_a / igap # Re-sample I-V curve v_temp = np.linspace(-vmax, vmax, npts) / vgap current = np.interp(v_temp, voltage, current) voltage = v_temp # Save DC I-V curve metadata dc = DCIVData(vraw=vraw, iraw=iraw, vnorm=voltage, inorm=current, vgap=vgap, igap=igap, fgap=fgap, rn=rn, rsg=rsg, offset=offset, vint=vint, rseries=rseries) return voltage, current, dc
def _find_if_noise(if_data, dc, **kw): """Determine IF noise from shot noise slope. Uses Woody's method (Woody 1985). Args: if_data: IF data, 2-column numpy array: voltage x power dc: DC I-V metadata Keyword Args: vshot (list): Voltage range over which to fit shot noise slope, in units [V]. Can be a list of lists to define multiple ranges. Returns: tuple: IF noise, correction factor, linear fit """ # Unpack keyword arguments vshot = kw.get('vshot', PARAMS['vshot']) # This is relatively tricky to automate # It still makes mistakes occasionally, make sure to check/plot your data # TODO: Sort out this function # Unpack x = if_data[:, 0] y = if_data[:, 1] # DEBUG # import matplotlib.pyplot as plt # plt.plot(x, y) # plt.show() if vshot is None: # Find linear region where spikes due to Josephson effect are not present # Begin by filtering and calculating the first/second derivative y_filt = savgol_filter(y, 21, 3) first_der = slope_span_n(x, y_filt, 11) first_der = savgol_filter(first_der, 51, 3) second_der = slope_span_n(x, first_der, 11) second_der = savgol_filter(np.abs(second_der), 51, 3) # First criteria: x>1.7 and second derivative must be small condition1 = np.max(np.abs(second_der)) * 1e-2 mask = (x > 1.7) & (np.abs(second_der) < condition1) # Second criteria: first derivative must be similar to bulk value med_der = np.median(first_der[mask]) mask_tmp = (0. < first_der) & (first_der < med_der * 2) mask = mask & mask_tmp # Third criteria: must be at least two values clumped together mask_tmp = np.zeros_like(mask, dtype=bool) mask_tmp[:-1] = mask[:-1] & mask[1:] mask = mask & mask_tmp else: # Make vshot a list of lists assert isinstance(vshot, tuple) or isinstance(vshot, list) if not isinstance(vshot[0], tuple) and not isinstance(vshot[0], list): vshot = (vshot,) # Build mask mask = np.zeros_like(x, dtype=bool) for vrange in vshot: mask_tmp = (vrange[0] < x * dc.vgap) & (x * dc.vgap < vrange[1]) mask = mask | mask_tmp if np.sum(mask) < 5: # pragma: no cover cprint('\t\tShot noise fit failed.', 'RED') cprint('\t\tSelecting all voltages above 2*Vgap.', 'RED') mask = x > 2. # Combine criteria x_red, y_red = x[mask], y[mask] # Find slope of shot noise slope, intercept, _, _, _ = stats.linregress(x_red, y_red) i_slope = slope * x + intercept # # Normal resistance in this region # volt_v = x_red * dc.vgap # curr_a = np.interp(x_red, dc.vnorm, dc.inorm) * dc.igap # rn_slope = (curr_a[-1] - curr_a[0]) / (volt_v[-1] - volt_v[0]) # vint = curr_a[0] - rn_slope * volt_v[0] # if vint < 0: # vint = 0 # # Plot for debugging # import matplotlib.pyplot as plt # plt.figure() # plt.plot(x, y, 'k') # plt.plot(x_red, y_red, 'r') # plt.plot(x, i_slope, 'g--') # plt.ylim([0, i_slope.max() * 1.05]) # plt.show() # Correct shot noise slope to 5.8/mV # gamma = (dc.rn - 50.) / (dc.rn + 50.) # trans = (1 - gamma**2) corr = 5.8 / slope * dc.vgap * 1e3 # * trans i_slope *= corr # IF noise contribution if_noise = np.interp(dc.vint / dc.vgap, x, i_slope) # if_noise = np.interp(vint, x, i_slope) # Is it a reasonable IF noise estimate? good_if_noise_fit = 0 < if_noise < 50 return if_noise, corr, i_slope, good_if_noise_fit