def _find_gap_voltage(volt_v, curr_a, **kw): """Find gap voltage. Args: volt_v (ndarray): voltage, in V curr_a (ndarray): current, in A Keyword Args: vgap_threshold (float): the current at the gap voltage Returns: float: gap voltage """ # Unpack keyword arguments vgap_threshold = kw.get('vgap_threshold', PARAMS['vgap_threshold']) # Method 1: current threshold if vgap_threshold is not None: idx = np.abs(curr_a - vgap_threshold).argmin() vgap = volt_v[idx] return vgap # Method 2: max derivative vstep = volt_v[1] - volt_v[0] mask = (1.5e-3 < volt_v) & (volt_v < 3.5e-3) der = slope(volt_v[mask], curr_a[mask]) der = gauss_conv(der, sigma=0.2e-3 / vstep) vgap = volt_v[mask][der.argmax()] return vgap
def _find_gap_voltage(volt_v, curr_a, **kw): """Find gap voltage. Args: volt_v (ndarray): voltage, in V curr_a (ndarray): current, in A Keyword Args: vrn (list): Voltage range over which to calculate the normal resistance, in units [V] debug (bool): plot for debugging purposes Returns: float: gap voltage """ # Unpack keyword arguments vrn = kw.get('vrn', PARAMS['vrn']) debug = kw.get('debug', PARAMS['debug']) # Normal resistance slope mask = (vrn[0] < volt_v) & (volt_v < vrn[1]) vtmp, itmp = volt_v[mask], curr_a[mask] prn = np.polyfit(vtmp, itmp, 1) # Find max derivative in I-V curve vstep = volt_v[1] - volt_v[0] mask = 2e-3 < volt_v der = slope(volt_v[mask], curr_a[mask]) der = gauss_conv(der, sigma=0.2e-3 / vstep) vgap = volt_v[mask][der.argmax()] # Fit line around max derivative mask = (vgap - 0.1e-4 < volt_v) & (volt_v < vgap + 0.1e-4) vtmp, itmp = volt_v[mask], curr_a[mask] pgap = np.polyfit(vtmp, itmp, 1) # Find "corner" in I-V curve vcorner = (pgap[1] - prn[1]) / (prn[0] - pgap[0]) icorner = pgap[0] * vcorner + pgap[1] # Find gap igap = icorner / 2 vgap = (igap - pgap[1]) / pgap[0] if debug: plt.figure() plt.plot(volt_v * 1e3, curr_a * 1e6) plt.plot(volt_v * 1e3, np.polyval(prn, volt_v) * 1e6, 'k--') plt.plot(volt_v * 1e3, np.polyval(pgap, volt_v) * 1e6, 'r--') plt.plot([vcorner * 1e3], [icorner * 1e6], 'r*') plt.plot([vgap * 1e3], [igap * 1e6], 'k*') plt.xlim([volt_v.min() * 1e3, volt_v.max() * 1e3]) plt.ylim([curr_a.min() * 1e6, curr_a.max() * 1e6]) plt.show() return vgap
def _sample_curve(voltage, current, max_npts, smear): """Sample curve. Sample more often when the curve is curvier. Args: voltage (ndarray): DC bias voltage current (ndarray): current (either DC tunneling or KK) max_npts (int): maximum number of sample points smear (float): smear current (only for sampling purposes) Returns: list: indices of sample points """ # Second derivative dd_current = np.abs(slope(voltage, slope(voltage, current))) # Cumulative sum of second derivative v_step = voltage[1] - voltage[0] cumsum = np.cumsum(gauss_conv(dd_current, sigma=smear / v_step)) # Build sampling array idx_list = [0] # Add indices based on curvy-ness cumsum_last = 0. voltage_last = voltage[0] for idx, v in enumerate(voltage): condition1 = abs(v) < 0.05 or abs(v - 1) < 0.1 or abs(v + 1) < 0.1 condition2 = v - voltage_last >= 1. condition3 = (cumsum[idx] - cumsum_last) * max_npts / cumsum[-1] > 1 condition4 = idx < 3 or idx > len(voltage) - 4 if condition1 or condition2 or condition3 or condition4: if idx != idx_list[-1]: idx_list.append(idx) voltage_last = v cumsum_last = cumsum[idx] # Add 10 to start/end for i in range(0, int(1 / VSTEP), int(1 / VSTEP / 10)): idx_list.append(i) for i in range( len(voltage) - int(1 / VSTEP) - 1, len(voltage), int(1 / VSTEP / 10)): idx_list.append(i) # Add 30 pts to middle ind_low = np.abs(voltage + 1.).argmin() ind_high = np.abs(voltage - 1.).argmin() npts = ind_high - ind_low for i in range(ind_low, ind_high, npts // 30): idx_list.append(i) idx_list = list(set(idx_list)) idx_list.sort() return idx_list
def __init__(self, **params): params = _default_params(params) if params['verbose']: print("Generating response function:") # Reflect about y-axis voltage = np.copy(VINIT) current = iv.perfect(voltage) if params['v_smear'] is None: voltage = np.r_[-voltage[::-1][:-1], voltage] current = np.r_[-current[::-1][:-1], current] # KK transform current_kk = iv.perfect_kk(voltage) # Place interpolations into hidden attributes self._f_idc = iv.perfect self._f_ikk = iv.perfect_kk self._f_didc = None self._f_dikk = None # Save DC I-V curve and KK transform as attributes self.voltage = voltage self.current = current self.voltage_kk = voltage self.current_kk = current_kk # Smear IV curve (optional) else: tmp = np.zeros_like(voltage) idx = np.abs(voltage - 1.).argmin() tmp[idx] = 1. v_step = voltage[1] - voltage[0] current = gauss_conv(tmp, sigma=params['v_smear']/v_step) + \ iv.perfect(voltage) if params['verbose']: print(" - Voltage smear: {:.4f}".format(params['v_smear'])) RespFn.__init__(self, voltage, current, **params)
def __init__(self, voltage, current, **params): params = _default_params(params) if params['verbose']: print("Generating response function:") assert voltage[0] == 0., "First voltage value must be zero" assert voltage[-1] > 5, "Voltage must extend to at least 5" # Reflect about y-axis voltage = np.r_[-voltage[::-1][:-1], voltage] current = np.r_[-current[::-1][:-1], current] # Smear DC I-V curve (optional) if params['v_smear'] is not None: v_step = voltage[1] - voltage[0] current = gauss_conv(current - voltage, sigma=params['v_smear'] / v_step) + voltage if params['verbose']: print(" - Voltage smear: {:.4f}".format(params['v_smear'])) # Calculate Kramers-Kronig (KK) transform current_kk = kk_trans(voltage, current, params['kk_n']) # Interpolate f_interp = _setup_interpolation(voltage, current, current_kk, **params) # Place interpolation objects into hidden attributes self._f_idc = f_interp[0] self._f_ikk = f_interp[1] self._f_didc = f_interp[2] self._f_dikk = f_interp[3] # Save DC I-V curve and KK transform as attributes self.voltage = voltage self.current = current self.voltage_kk = voltage self.current_kk = current_kk
params['t_cold'] = args.tcold # Plot data if args.verbose: print(' - Generating figure') fig, ax = plt.subplots(figsize=(args.width, args.height)) for filename in args.file: if args.verbose: print(" - Importing: " + filename) # Import and plot noise temperature data = if_response(filename, ifresp_delimiter=args.delimiter) freq, tn = data[0], data[1] # Filter if args.filter is not None: tn = gauss_conv(tn, args.filter) plt.plot(freq, tn, label=filename) # Figure properties if args.verbose: print(' - Customizing figure') plt.xlabel(r'Frequency (GHz)') plt.ylabel(r'Noise Temperature (K)') if args.xmax is not None: plt.xlim([0, args.xmax]) if args.ymax is not None: plt.ylim([0, args.ymax]) else: plt.ylim([0, 500]) plt.legend(loc=0) if args.title is not None:
def _load_if(ifdata, dc, **kwargs): """Import IF data. Args: ifdata: IF data. A Numpy array. The data should have two columns: the first for voltage, and the second for IF power. dc (qmix.exp.iv_data.DCIVData): DC I-V metadata. Keyword arguments: v_fmt (str): units for voltage ('V', 'mV', 'uV') ifdata_sigma (float): Standard deviation of Gaussian used for filtering, in units [V] ifdata_npts (float): evenly interpolate data to have this many data points rseries (float): series resistance of measurement system Returns: IF data (in matrix form) """ # Unpack keyword arguments v_multiplier = kwargs.get('v_multiplier', PARAMS['v_multiplier']) sigma = kwargs.get('ifdata_sigma', PARAMS['ifdata_sigma']) vmax = kwargs.get('vmax', PARAMS['vmax']) npts = kwargs.get('ifdata_npts', PARAMS['ifdata_npts']) rseries = kwargs.get('rseries', PARAMS['rseries']) v_fmt = kwargs.get('v_fmt', PARAMS['v_fmt']) # Import raw IF data ifdata = ifdata.copy() assert isinstance(ifdata, np.ndarray), 'IF data should be a Numpy array.' assert ifdata.ndim == 2, 'IF data should be 2-dimensional.' assert ifdata.shape[1] == 2, 'IF data should have 2 columns.' # Units for voltage ifdata[:, 0] *= _vfmt_dict[v_fmt] # Clean ifdata = remove_nans_matrix(ifdata) ifdata = ifdata[np.argsort(ifdata[:, 0])] ifdata = remove_doubles_matrix(ifdata) # Correct errors in experimental system ifdata[:, 0] *= v_multiplier # Correct for offset ifdata[:, 0] = ifdata[:, 0] - dc.offset[0] # Correct for series resistance if rseries is not None: v = ifdata[:, 0] rstatic = dc.vraw / dc.iraw rstatic[rstatic < 0] = 0. rstatic = np.interp(v, dc.vraw, rstatic) iraw = np.interp(v, dc.vraw, dc.iraw) rj = rstatic - rseries v0 = iraw * rj ifdata[:, 0] = v0 # Normalize voltage to gap voltage ifdata[:, 0] /= dc.vgap # Set to common voltage (so that data can be stacked) v, p = ifdata[:, 0], ifdata[:, 1] # assert v.max() > vmax / dc.vgap, \ # 'vmax ({0}) outside data range ({1})'.format(vmax / dc.vgap, v.max()) # assert v.min() < 0., 'V=0 not included in IF data' v_out = np.linspace(0, vmax / dc.vgap, npts) p_out = np.interp(v_out, v, p, left=0, right=0) ifdata = np.vstack((v_out, p_out)).T # Smooth IF data if sigma is not None: step = (ifdata[1, 0] - ifdata[0, 0]) * dc.vgap # Backwards compatibility if sigma > 0.5: sigma = sigma * step ifdata[:, 1] = gauss_conv(ifdata[:, 1], sigma / step) return ifdata
def _correct_offset(volt_v, curr_a, **kw): """Find and correct any I/V offset. The experimental data often has an offset in both V and I. This can be corrected by using the leakage current. This is found by looking at the derivative and correcting based on where the peak of the derivative is. Args: volt_v (ndarray): voltage, in V curr_a (ndarray): current, in A Keyword Args: voffset: voltage offset, in V ioffset: current offset, in A voffset_range (list): Voltage range over which to search for offset, in units [V]. voffset_sigma: std dev of Gaussian filter when searching for offset Returns: tuple: corrected voltage, corrected current, I/V offset """ # Unpack keyword arguments voffset_range = kw.get('voffset_range', PARAMS['voffset_range']) voffset_sigma = kw.get('voffset_sigma', PARAMS['voffset_sigma']) voffset = kw.get('voffset', PARAMS['voffset']) ioffset = kw.get('ioffset', PARAMS['ioffset']) if voffset is None: # Find voffset and ioffset # Search over a limited voltage range if isinstance(voffset_range, tuple): mask = (voffset_range[0] < volt_v) & (volt_v < voffset_range[1]) else: # if int or float mask = (-voffset_range < volt_v) & (volt_v < voffset_range) v = volt_v[mask] i = curr_a[mask] # Find derivative of I-V curve vstep = v[1] - v[0] sigma = voffset_sigma / vstep der = slope(v, i) der_smooth = gauss_conv(der, sigma=sigma) # Offset is at max derivative idx = der_smooth.argmax() voffset = v[idx] # ioffset = np.interp(voffset, v, i) ioffset = (np.interp(voffset - 0.1e-3, v, i) + np.interp(voffset + 0.1e-3, v, i)) / 2 if ioffset is None: # Find ioffset ioffset = np.interp(voffset, volt_v, curr_a) # Correct for the offset volt_v -= voffset curr_a -= ioffset return volt_v, curr_a, (voffset, ioffset)
def _load_if(ifdata, dc, **kwargs): """Import IF data. Args: ifdata: IF data. Either a CSV data file or a Numpy array. The data should have two columns: the first for voltage, and the second for IF power. If you are using a CSV file, the properties of the CSV file can be set through additional keyword arguments (see below). dc (qmix.exp.iv_data.DCIVData): DC I-V metadata. Keyword arguments: delimiter (str): delimiter used in data files v_fmt (str): units for voltage ('V', 'mV', 'uV') usecols (tuple): columns for voltage and current (e.g., ``(0,1)``) ifdata_sigma (float): Standard deviation of Gaussian used for filtering, in units [V] ifdata_npts (float): evenly interpolate data to have this many data points rseries (float): series resistance of measurement system skip_header: number of rows to skip at the beginning of the file Returns: IF data (in matrix form) """ # Unpack keyword arguments v_multiplier = kwargs.get('v_multiplier', PARAMS['v_multiplier']) skip_header = kwargs.get('skip_header', PARAMS['skip_header']) sigma = kwargs.get('ifdata_sigma', PARAMS['ifdata_sigma']) vmax = kwargs.get('vmax', PARAMS['vmax']) npts = kwargs.get('ifdata_npts', PARAMS['ifdata_npts']) delim = kwargs.get('delimiter', PARAMS['delimiter']) usecols = kwargs.get('usecols', PARAMS['usecols']) rseries = kwargs.get('rseries', PARAMS['rseries']) v_fmt = kwargs.get('v_fmt', PARAMS['v_fmt']) # Import raw IF data if isinstance(ifdata, str): # assume CSV data file ifdata = np.genfromtxt(ifdata, delimiter=delim, usecols=usecols, skip_header=skip_header) elif isinstance(ifdata, np.ndarray): # Numpy array ifdata = ifdata.copy() assert ifdata.ndim == 2, 'I-V data should be 2-dimensional.' assert ifdata.shape[1] == 2, 'I-V data should have 2 columns.' else: raise ValueError("Input data type not recognized.") # Units for voltage ifdata[:, 0] *= _vfmt_dict[v_fmt] # Clean ifdata = remove_nans_matrix(ifdata) ifdata = ifdata[np.argsort(ifdata[:, 0])] ifdata = remove_doubles_matrix(ifdata) # Correct errors in experimental system ifdata[:, 0] *= v_multiplier # Correct for offset ifdata[:, 0] = ifdata[:, 0] - dc.offset[0] # Correct for series resistance if rseries is not None: v = ifdata[:, 0] rstatic = dc.vraw / dc.iraw rstatic[rstatic < 0] = 0. rstatic = np.interp(v, dc.vraw, rstatic) iraw = np.interp(v, dc.vraw, dc.iraw) rj = rstatic - rseries v0 = iraw * rj ifdata[:, 0] = v0 # Normalize voltage to gap voltage ifdata[:, 0] /= dc.vgap # Set to common voltage (so that data can be stacked) v, p = ifdata[:, 0], ifdata[:, 1] assert v.max() > vmax / dc.vgap, \ 'vmax ({0}) outside data range ({1})'.format(vmax / dc.vgap, v.max()) assert v.min() < 0., 'V=0 not included in IF data' v_out = np.linspace(0, vmax / dc.vgap, npts) p_out = np.interp(v_out, v, p) ifdata = np.vstack((v_out, p_out)).T # Smooth IF data if sigma is not None: step = (ifdata[1, 0] - ifdata[0, 0]) * dc.vgap # Backwards compatibility if sigma > 0.5: sigma = sigma * step ifdata[:, 1] = gauss_conv(ifdata[:, 1], sigma / step) return ifdata