def test_filter(resp_wfm, frequency_filters=None, noise_threshold=None, excit_wfm=None, show_plots=True, plot_title=None, verbose=False): """ Filters the provided response with the provided filters. Parameters ---------- resp_wfm : array-like, 1D Raw response waveform in the time domain frequency_filters : (Optional) FrequencyFilter object or list of Frequency filters to apply to signal noise_threshold : (Optional) float Noise threshold to apply to signal excit_wfm : (Optional) 1D array-like Excitation waveform in the time domain. This waveform is necessary for plotting loops. If the length of resp_wfm matches excit_wfm, a single plot will be returned with the raw and filtered signals plotted against the excit_wfm. Else, resp_wfm and the filtered (filt_data) signal will be broken into chunks matching the length of excit_wfm and a figure with multiple plots (one for each chunk) with the raw and filtered signal chunks plotted against excit_wfm will be returned for fig_loops show_plots : (Optional) Boolean Whether or not to plot FFTs before and after filtering plot_title : str / unicode (Optional) Title for the raw vs filtered plots if requested. For example - 'Row 15' verbose : (Optional) Boolean Prints extra debugging information if True. Default False Returns ------- filt_data : 1D numpy float array Filtered signal in the time domain fig_fft : matplotlib.pyplot.figure object handle to the plotted figure if requested, else None fig_loops : matplotlib.pyplot.figure object handle to figure with the filtered signal and raw signal plotted against the excitation waveform """ if not isinstance(resp_wfm, (np.ndarray, list)): raise TypeError('resp_wfm should be array-like') resp_wfm = np.array(resp_wfm) show_loops = False if excit_wfm is not None and show_plots: if len(resp_wfm) % len(excit_wfm) == 0: show_loops = True else: raise ValueError( 'Length of resp_wfm should be divisibe by length of excit_wfm') if show_loops: if plot_title is None: plot_title = 'FFT Filtering' else: assert isinstance(plot_title, (str, unicode)) if frequency_filters is None and noise_threshold is None: raise ValueError( 'Need to specify at least some noise thresholding / frequency filter' ) if noise_threshold is not None: if noise_threshold >= 1 or noise_threshold <= 0: raise ValueError('Noise threshold must be within (0 1)') samp_rate = 1 composite_filter = 1 if frequency_filters is not None: if not isinstance(frequency_filters, Iterable): frequency_filters = [frequency_filters] if not are_compatible_filters(frequency_filters): raise ValueError( 'frequency filters must be a single or list of FrequencyFilter objects' ) composite_filter = build_composite_freq_filter(frequency_filters) samp_rate = frequency_filters[0].samp_rate resp_wfm = np.array(resp_wfm) num_pts = resp_wfm.size fft_pix_data = np.fft.fftshift(np.fft.fft(resp_wfm)) if noise_threshold is not None: noise_floor = get_noise_floor(fft_pix_data, noise_threshold)[0] if verbose: print('The noise_floor is', noise_floor) fig_fft = None if show_plots: w_vec = np.linspace(-0.5 * samp_rate, 0.5 * samp_rate, num_pts) * 1E-3 fig_fft, [ax_raw, ax_filt] = plt.subplots(figsize=(12, 8), nrows=2) axes_fft = [ax_raw, ax_filt] set_tick_font_size(axes_fft, 14) r_ind = num_pts if isinstance(composite_filter, np.ndarray): r_ind = np.max(np.where(composite_filter > 0)[0]) x_lims = slice(len(w_vec) // 2, r_ind) amp = np.abs(fft_pix_data) ax_raw.semilogy(w_vec[x_lims], amp[x_lims], label='Raw') if frequency_filters is not None: ax_raw.semilogy(w_vec[x_lims], (composite_filter[x_lims] + np.min(amp)) * (np.max(amp) - np.min(amp)), linewidth=3, color='orange', label='Composite Filter') if noise_threshold is not None: ax_raw.axhline( noise_floor, # ax_raw.semilogy(w_vec, np.ones(r_ind - l_ind) * noise_floor, linewidth=2, color='r', label='Noise Threshold') ax_raw.legend(loc='best', fontsize=14) ax_raw.set_title('Raw Signal', fontsize=16) ax_raw.set_ylabel('Magnitude (a. u.)', fontsize=14) fft_pix_data *= composite_filter if noise_threshold is not None: fft_pix_data[ np.abs(fft_pix_data) < noise_floor] = 1E-16 # DON'T use 0 here. ipython kernel dies if show_plots: ax_filt.semilogy(w_vec[x_lims], np.abs(fft_pix_data)[x_lims]) ax_filt.set_title('Filtered Signal', fontsize=16) ax_filt.set_xlabel('Frequency(kHz)', fontsize=14) ax_filt.set_ylabel('Magnitude (a. u.)', fontsize=14) if noise_threshold is not None: ax_filt.set_ylim( bottom=noise_floor ) # prevents the noise threshold from messing up plots fig_fft.tight_layout() filt_data = np.real(np.fft.ifft(np.fft.ifftshift(fft_pix_data))) if verbose: print('The shape of the filtered data is {}'.format(filt_data.shape)) print('The shape of the excitation waveform is {}'.format( excit_wfm.shape)) fig_loops = None if show_loops: if len(resp_wfm) == len(excit_wfm): # single plot: fig_loops, axis = plt.subplots(figsize=(5.5, 5)) axis.plot(excit_wfm, resp_wfm, 'r', label='Raw') axis.plot(excit_wfm, filt_data, 'k', label='Filtered') axis.legend(fontsize=14) set_tick_font_size(axis, 14) axis.set_xlabel('Excitation', fontsize=16) axis.set_ylabel('Signal', fontsize=16) axis.set_title(plot_title, fontsize=16) fig_loops.tight_layout() else: # N loops: raw_pixels = np.reshape(resp_wfm, (-1, len(excit_wfm))) filt_pixels = np.reshape(filt_data, (-1, len(excit_wfm))) print(raw_pixels.shape, filt_pixels.shape) fig_loops, axes_loops = plot_curves( excit_wfm, [raw_pixels, filt_pixels], line_colors=['r', 'k'], dataset_names=['Raw', 'Filtered'], x_label='Excitation', y_label='Signal', subtitle_prefix='Col ', num_plots=16, title=plot_title) return filt_data, fig_fft, fig_loops
def plot_bayesian_results(bias_sine, i_meas, i_corrected, bias_triang, resistance, r_variance, i_recon=None, pix_pos=[0, 0], broken_resistance=True, r_max=None, res_scatter=False, **kwargs): """ Plots the basic Bayesian Inference results for a specific pixel Parameters ---------- bias_sine : 1D float numpy array Original bias vector used for experiment i_meas : 1D float numpy array Current measured from experiment i_corrected : 1D float numpy array current with capacitance and R extra compensated i_recon : 1D float numpy array Reconstructed current bias_triang : 1D float numpy array Interpolated bias resistance : 1D float numpy array Inferred resistance r_variance : 1D float numpy array Variance of the resistance pix_pos : list of two numbers Pixel row and column positions or values broken_resistance : bool, Optional Whether or not to break the resistance plots into sections so as to avoid plotting areas with high variance r_max : float, Optional Maximum value of resistance to plot res_scatter : bool, Optional Use scatter instead of line plots for resistance Returns ------- fig : matplotlib.pyplot figure handle Handle to figure """ font_size_1 = 14 font_size_2 = 16 half_x_ind = int(0.5 * bias_triang.size) ex_amp = np.max(bias_triang) colors = [['red', 'orange'], ['blue', 'cyan']] syms = [['-', '--', '--'], ['-', ':', ':']] names = ['Forward', 'Reverse'] cos_omega_t = np.roll(bias_sine, int(-0.25 * bias_sine.size)) orig_half_pt = int(0.5 * bias_sine.size) i_correct_rolled = np.roll(i_corrected, int(-0.25 * bias_sine.size)) st_dev = np.sqrt(r_variance) tests = [st_dev < 10, resistance > 0] if r_max is not None: tests.append(resistance < r_max) good_pts = np.ones(resistance.shape, dtype=bool) for item in tests: good_pts = np.logical_and(good_pts, item) good_pts = np.where(good_pts)[0] good_forw = good_pts[np.where(good_pts < half_x_ind)[0]] good_rev = good_pts[np.where(good_pts >= half_x_ind)[0]] pos_limits = resistance + st_dev neg_limits = resistance - st_dev fig, axes = plt.subplots(ncols=3, figsize=(15, 5)) # fig.subplots_adjust(wspace=3.5) axes[0].set_ylabel('Resistance (G$\Omega$)', fontsize=font_size_2) pts_to_plot = [good_forw, good_rev] for type_ind, axis, pts_list, cols_set, sym_set, set_name in zip(range(len(names)), axes[:2], pts_to_plot, colors, syms, names): axis.set_title('$R(V)$ ' + set_name + ' at Row = ' + str(pix_pos[1]) + ' Col =' + str(pix_pos[0]), fontsize=font_size_2) single_plot = not broken_resistance if broken_resistance: diff = np.diff(pts_list) jump_inds = np.argwhere(diff > 4) + 1 if jump_inds.size < 1: single_plot = True if not single_plot: jump_inds = np.append(np.append(0, jump_inds), pts_list[-1]) for ind in range(1, jump_inds.size): cur_range = pts_list[jump_inds[ind - 1]:jump_inds[ind]] if res_scatter: axis.scatter(bias_triang[cur_range], resistance[cur_range], color=cols_set[0], s=30) else: axis.plot(bias_triang[cur_range], resistance[cur_range], cols_set[0], linestyle=sym_set[0], linewidth=3) axis.fill_between(bias_triang[cur_range], pos_limits[cur_range], neg_limits[cur_range], alpha=0.25, color=cols_set[1]) if ind == 1: axis.legend(['R(V)', 'R(V)+-$\sigma$'], loc='upper center', fontsize=font_size_1) else: if res_scatter: axis.scatter(bias_triang[pts_list], resistance[pts_list], color=cols_set[0], s=30) else: axis.plot(bias_triang[pts_list], resistance[pts_list], cols_set[0], linestyle=sym_set[0], linewidth=3, label='R(V)') axis.fill_between(bias_triang[pts_list], pos_limits[pts_list], neg_limits[pts_list], alpha=0.25, color=cols_set[1], label='R(V)+-$\sigma$') axis.legend(loc='upper center', fontsize=font_size_1) axis.set_xlabel('Voltage (V)', fontsize=font_size_2) axis.set_xlim((-ex_amp, ex_amp)) # ################### CURRENT PLOT ########################## axes[2].plot(bias_sine, i_meas, 'green', linewidth=3, label='I$_{meas}$') if i_recon is not None: axes[2].plot(bias_sine, i_recon, 'c--', linewidth=3, label='I$_{recon}$') axes[2].plot(cos_omega_t[orig_half_pt:], i_correct_rolled[orig_half_pt:], 'blue', linewidth=3, label='I$_{Bayes} Forw$') axes[2].plot(cos_omega_t[:orig_half_pt], i_correct_rolled[:orig_half_pt], 'red', linewidth=3, label='I$_{Bayes} Rev$') # axes[2].legend(loc='upper right', bbox_to_anchor=(-.1, 0.30), fontsize=font_size_1) axes[2].legend(loc='best', fontsize=font_size_1) axes[2].set_xlabel('Voltage(V)', fontsize=font_size_2) axes[2].set_title('$I(V)$ at row ' + str(pix_pos[0]) + ', col ' + str(pix_pos[1]), fontsize=font_size_2) axes[2].set_ylabel('Current (nA)', fontsize=font_size_2) set_tick_font_size(axes, font_size_1) fig.tight_layout() return fig
def plot_bayesian_results(bias_sine, i_meas, i_corrected, bias_triang, resistance, r_variance, i_recon=None, pix_pos=[0, 0], broken_resistance=True, r_max=None, res_scatter=False, **kwargs): """ Plots the basic Bayesian Inference results for a specific pixel Parameters ---------- bias_sine : 1D float numpy array Original bias vector used for experiment i_meas : 1D float numpy array Current measured from experiment i_corrected : 1D float numpy array current with capacitance and R extra compensated i_recon : 1D float numpy array Reconstructed current bias_triang : 1D float numpy array Interpolated bias resistance : 1D float numpy array Inferred resistance r_variance : 1D float numpy array Variance of the resistance pix_pos : list of two numbers Pixel row and column positions or values broken_resistance : bool, Optional Whether or not to break the resistance plots into sections so as to avoid plotting areas with high variance r_max : float, Optional Maximum value of resistance to plot res_scatter : bool, Optional Use scatter instead of line plots for resistance Returns ------- fig : matplotlib.pyplot figure handle Handle to figure """ font_size_1 = 14 font_size_2 = 16 half_x_ind = int(0.5 * bias_triang.size) ex_amp = np.max(bias_triang) colors = [['red', 'orange'], ['blue', 'cyan']] syms = [['-', '--', '--'], ['-', ':', ':']] names = ['Forward', 'Reverse'] cos_omega_t = np.roll(bias_sine, int(-0.25 * bias_sine.size)) orig_half_pt = int(0.5 * bias_sine.size) i_correct_rolled = np.roll(i_corrected, int(-0.25 * bias_sine.size)) st_dev = np.sqrt(r_variance) tests = [st_dev < 10, resistance > 0] if r_max is not None: tests.append(resistance < r_max) good_pts = np.ones(resistance.shape, dtype=bool) for item in tests: good_pts = np.logical_and(good_pts, item) good_pts = np.where(good_pts)[0] good_forw = good_pts[np.where(good_pts < half_x_ind)[0]] good_rev = good_pts[np.where(good_pts >= half_x_ind)[0]] pos_limits = resistance + st_dev neg_limits = resistance - st_dev fig, axes = plt.subplots(ncols=3, figsize=(15, 5)) # fig.subplots_adjust(wspace=3.5) axes[0].set_ylabel('Resistance (G$\Omega$)', fontsize=font_size_2) pts_to_plot = [good_forw, good_rev] for type_ind, axis, pts_list, cols_set, sym_set, set_name in zip( range(len(names)), axes[:2], pts_to_plot, colors, syms, names): axis.set_title('$R(V)$ ' + set_name + ' at Row = ' + str(pix_pos[1]) + ' Col =' + str(pix_pos[0]), fontsize=font_size_2) single_plot = not broken_resistance if broken_resistance: diff = np.diff(pts_list) jump_inds = np.argwhere(diff > 4) + 1 if jump_inds.size < 1: single_plot = True if not single_plot: jump_inds = np.append(np.append(0, jump_inds), pts_list[-1]) for ind in range(1, jump_inds.size): cur_range = pts_list[jump_inds[ind - 1]:jump_inds[ind]] if res_scatter: axis.scatter(bias_triang[cur_range], resistance[cur_range], color=cols_set[0], s=30) else: axis.plot(bias_triang[cur_range], resistance[cur_range], cols_set[0], linestyle=sym_set[0], linewidth=3) axis.fill_between(bias_triang[cur_range], pos_limits[cur_range], neg_limits[cur_range], alpha=0.25, color=cols_set[1]) if ind == 1: axis.legend(['R(V)', 'R(V)+-$\sigma$'], loc='upper center', fontsize=font_size_1) else: if res_scatter: axis.scatter(bias_triang[pts_list], resistance[pts_list], color=cols_set[0], s=30) else: axis.plot(bias_triang[pts_list], resistance[pts_list], cols_set[0], linestyle=sym_set[0], linewidth=3, label='R(V)') axis.fill_between(bias_triang[pts_list], pos_limits[pts_list], neg_limits[pts_list], alpha=0.25, color=cols_set[1], label='R(V)+-$\sigma$') axis.legend(loc='upper center', fontsize=font_size_1) axis.set_xlabel('Voltage (V)', fontsize=font_size_2) axis.set_xlim((-ex_amp, ex_amp)) # ################### CURRENT PLOT ########################## axes[2].plot(bias_sine, i_meas, 'green', linewidth=3, label='I$_{meas}$') if i_recon is not None: axes[2].plot(bias_sine, i_recon, 'c--', linewidth=3, label='I$_{recon}$') axes[2].plot(cos_omega_t[orig_half_pt:], i_correct_rolled[orig_half_pt:], 'blue', linewidth=3, label='I$_{Bayes} Forw$') axes[2].plot(cos_omega_t[:orig_half_pt], i_correct_rolled[:orig_half_pt], 'red', linewidth=3, label='I$_{Bayes} Rev$') # axes[2].legend(loc='upper right', bbox_to_anchor=(-.1, 0.30), fontsize=font_size_1) axes[2].legend(loc='best', fontsize=font_size_1) axes[2].set_xlabel('Voltage(V)', fontsize=font_size_2) axes[2].set_title('$I(V)$ at row ' + str(pix_pos[0]) + ', col ' + str(pix_pos[1]), fontsize=font_size_2) axes[2].set_ylabel('Current (nA)', fontsize=font_size_2) set_tick_font_size(axes, font_size_1) fig.tight_layout() return fig
def test_filter(resp_wfm, frequency_filters=None, noise_threshold=None, excit_wfm=None, show_plots=True, plot_title=None, verbose=False): """ Filters the provided response with the provided filters. Parameters ---------- resp_wfm : array-like, 1D Raw response waveform in the time domain frequency_filters : (Optional) FrequencyFilter object or list of Frequency filters to apply to signal noise_threshold : (Optional) float Noise threshold to apply to signal excit_wfm : (Optional) 1D array-like Excitation waveform in the time domain. This waveform is necessary for plotting loops. If the length of resp_wfm matches excit_wfm, a single plot will be returned with the raw and filtered signals plotted against the excit_wfm. Else, resp_wfm and the filtered (filt_data) signal will be broken into chunks matching the length of excit_wfm and a figure with multiple plots (one for each chunk) with the raw and filtered signal chunks plotted against excit_wfm will be returned for fig_loops show_plots : (Optional) Boolean Whether or not to plot FFTs before and after filtering plot_title : str / unicode (Optional) Title for the raw vs filtered plots if requested. For example - 'Row 15' verbose : (Optional) Boolean Prints extra debugging information if True. Default False Returns ------- filt_data : 1D numpy float array Filtered signal in the time domain fig_fft : matplotlib.pyplot.figure object handle to the plotted figure if requested, else None fig_loops : matplotlib.pyplot.figure object handle to figure with the filtered signal and raw signal plotted against the excitation waveform """ if not isinstance(resp_wfm, (np.ndarray, list)): raise TypeError('resp_wfm should be array-like') resp_wfm = np.array(resp_wfm) show_loops = False if excit_wfm is not None and show_plots: if len(resp_wfm) % len(excit_wfm) == 0: show_loops = True else: raise ValueError('Length of resp_wfm should be divisibe by length of excit_wfm') if show_loops: if plot_title is None: plot_title = 'FFT Filtering' else: assert isinstance(plot_title, (str, unicode)) if frequency_filters is None and noise_threshold is None: raise ValueError('Need to specify at least some noise thresholding / frequency filter') if noise_threshold is not None: if noise_threshold >= 1 or noise_threshold <= 0: raise ValueError('Noise threshold must be within (0 1)') samp_rate = 1 composite_filter = 1 if frequency_filters is not None: if not isinstance(frequency_filters, Iterable): frequency_filters = [frequency_filters] if not are_compatible_filters(frequency_filters): raise ValueError('frequency filters must be a single or list of FrequencyFilter objects') composite_filter = build_composite_freq_filter(frequency_filters) samp_rate = frequency_filters[0].samp_rate resp_wfm = np.array(resp_wfm) num_pts = resp_wfm.size fft_pix_data = np.fft.fftshift(np.fft.fft(resp_wfm)) if noise_threshold is not None: noise_floor = get_noise_floor(fft_pix_data, noise_threshold)[0] if verbose: print('The noise_floor is', noise_floor) fig_fft = None if show_plots: w_vec = np.linspace(-0.5 * samp_rate, 0.5 * samp_rate, num_pts) * 1E-3 fig_fft, [ax_raw, ax_filt] = plt.subplots(figsize=(12, 8), nrows=2) axes_fft = [ax_raw, ax_filt] set_tick_font_size(axes_fft, 14) r_ind = num_pts if isinstance(composite_filter, np.ndarray): r_ind = np.max(np.where(composite_filter > 0)[0]) x_lims = slice(len(w_vec) // 2, r_ind) amp = np.abs(fft_pix_data) ax_raw.semilogy(w_vec[x_lims], amp[x_lims], label='Raw') if frequency_filters is not None: ax_raw.semilogy(w_vec[x_lims], (composite_filter[x_lims] + np.min(amp)) * (np.max(amp) - np.min(amp)), linewidth=3, color='orange', label='Composite Filter') if noise_threshold is not None: ax_raw.axhline(noise_floor, # ax_raw.semilogy(w_vec, np.ones(r_ind - l_ind) * noise_floor, linewidth=2, color='r', label='Noise Threshold') ax_raw.legend(loc='best', fontsize=14) ax_raw.set_title('Raw Signal', fontsize=16) ax_raw.set_ylabel('Magnitude (a. u.)', fontsize=14) fft_pix_data *= composite_filter if noise_threshold is not None: fft_pix_data[np.abs(fft_pix_data) < noise_floor] = 1E-16 # DON'T use 0 here. ipython kernel dies if show_plots: ax_filt.semilogy(w_vec[x_lims], np.abs(fft_pix_data)[x_lims]) ax_filt.set_title('Filtered Signal', fontsize=16) ax_filt.set_xlabel('Frequency(kHz)', fontsize=14) ax_filt.set_ylabel('Magnitude (a. u.)', fontsize=14) if noise_threshold is not None: ax_filt.set_ylim(bottom=noise_floor) # prevents the noise threshold from messing up plots fig_fft.tight_layout() filt_data = np.real(np.fft.ifft(np.fft.ifftshift(fft_pix_data))) if verbose: print('The shape of the filtered data is {}'.format(filt_data.shape)) print('The shape of the excitation waveform is {}'.format(excit_wfm.shape)) fig_loops = None if show_loops: if len(resp_wfm) == len(excit_wfm): # single plot: fig_loops, axis = plt.subplots(figsize=(5.5, 5)) axis.plot(excit_wfm, resp_wfm, 'r', label='Raw') axis.plot(excit_wfm, filt_data, 'k', label='Filtered') axis.legend(fontsize=14) set_tick_font_size(axis, 14) axis.set_xlabel('Excitation', fontsize=16) axis.set_ylabel('Signal', fontsize=16) axis.set_title(plot_title, fontsize=16) fig_loops.tight_layout() else: # N loops: raw_pixels = np.reshape(resp_wfm, (-1, len(excit_wfm))) filt_pixels = np.reshape(filt_data, (-1, len(excit_wfm))) print(raw_pixels.shape, filt_pixels.shape) fig_loops, axes_loops = plot_curves(excit_wfm, [raw_pixels, filt_pixels], line_colors=['r', 'k'], dataset_names=['Raw', 'Filtered'], x_label='Excitation', y_label='Signal', subtitle_prefix='Col ', num_plots=16, title=plot_title) return filt_data, fig_fft, fig_loops