def cli(): parser = argparse.ArgumentParser() parser.add_argument('--data', action='store_true', help='Plot the recorded data. (Default)') parser.add_argument('--testfunc', action='store_true', help='Plot the simulated test functions.') parser.add_argument('--tu', type=str, help='Specify the time units (e.g., \'s\' or \'ms\').') parser.add_argument('--au', type=str, help='Specify the amplitude units (e.g., \'m\' or \'mm\').') parser.add_argument('--pclip', type=float, help='''Specify the percentage (0-1) of the peak amplitude to display. This parameter is used for pcolormesh plots only. Default is set to 1.''') parser.add_argument('--title', type=str, help='''Specify a title for the wiggle plot. Default title is \'Data\' if \'--data\' is passed and 'Test Function' if \'--testfunc\' is passed.''') parser.add_argument('--format', '-f', type=str, default='pdf', choices=['png', 'pdf', 'ps', 'eps', 'svg'], help='''Specify the image format of the saved file. Accepted formats are png, pdf, ps, eps, and svg. Default format is set to pdf.''') parser.add_argument('--map', action='store_true', help='''Plot a map of the receiver and source/sampling point locations. The current source/sampling point will be highlighted. The boundary of the scatterer will also be shown if available.''') parser.add_argument('--mode', type=str, choices=['light', 'dark'], required=False, help='''Specify whether to view plots in light mode for daytime viewing or dark mode for nighttime viewing. Mode must be either \'light\' or \'dark\'.''') args = parser.parse_args() #============================================================================== # if a plotParams.pkl file already exists, load relevant parameters if Path('plotParams.pkl').exists(): plotParams = pickle.load(open('plotParams.pkl', 'rb')) # update parameters for wiggle plots based on passed arguments if args.mode is not None: plotParams['view_mode'] = args.mode if args.tu is not None: plotParams['tu'] = args.tu if args.au is not None: plotParams['au'] = args.au if args.pclip is not None: if args.pclip >= 0 and args.pclip <= 1: plotParams['pclip'] = args.pclip else: print(textwrap.dedent( ''' Warning: Invalid value passed to argument \'--pclip\'. Value must be between 0 and 1. ''')) if args.title is not None: if args.data: plotParams['data_title'] = args.title elif args.testfunc: plotParams['tf_title'] = args.title else: # create a plotParams dictionary file with default values plotParams = default_params() # update parameters for wiggle plots based on passed arguments if args.mode is not None: plotParams['view_mode'] = args.mode if args.tu is not None: plotParams['tu'] = args.tu if args.au is not None: plotParams['au'] = args.au if args.title is not None: if args.data: plotParams['data_title'] = args.title elif args.testfunc: plotParams['tf_title'] = args.title pickle.dump(plotParams, open('plotParams.pkl', 'wb'), pickle.HIGHEST_PROTOCOL) #============================================================================== # Load the relevant data to plot datadir = np.load('datadir.npz') receiverPoints = np.load(str(datadir['receivers'])) time = np.load(str(datadir['recordingTimes'])) # Apply any user-specified windows rinterval, tinterval, tstep, dt, sinterval = get_user_windows() receiverPoints = receiverPoints[rinterval, :] time = time[tinterval] # Load the scatterer boundary, if it exists if 'scatterer' in datadir: scatterer = np.load(str(datadir['scatterer'])) else: scatterer = None if all(v is True for v in [args.data, args.testfunc]): # User specified both data and testfuncs for plotting # Send error message and exit. sys.exit(textwrap.dedent( ''' Error: Cannot plot both recorded data and simulated test functions. Use vzwiggles --data to plot the recorded data or vzwiggles --testfuncs to plot the simulated test functions. ''')) elif all(v is not True for v in [args.data, args.testfunc]): # User did not specify which wiggles to plot. # Plot recorded data by default. # load the 3D data array into variable 'X' # X[receiver, time, source] wiggleType = 'data' X = load_data(domain='time', verbose=True) if 'sources' in datadir: sourcePoints = np.load(str(datadir['sources'])) sourcePoints = sourcePoints[sinterval, :] else: sourcePoints = None elif args.data: # load the 3D data array into variable 'X' # X[receiver, time, source] wiggleType = 'data' X = load_data(domain='time', verbose=True) if 'sources' in datadir: sourcePoints = np.load(str(datadir['sources'])) sourcePoints = sourcePoints[sinterval, :] else: sourcePoints = None elif args.testfunc: wiggleType = 'testfunc' # Update time to convolution times T = time[-1] - time[0] time = np.linspace(-T, T, 2 * len(time) - 1) if 'testFuncs' not in datadir and not Path('VZTestFuncs.npz').exists(): X, sourcePoints = load_test_funcs(domain='time', medium='constant', verbose=True, return_sampling_points=True) if 'testFuncs' in datadir and not Path('VZTestFuncs.npz').exists(): X, sourcePoints = load_test_funcs(domain='time', medium='variable', verbose=True, return_sampling_points=True) # Pad time axis with zeros to length of convolution 2Nt-1 npad = ((0, 0), (X.shape[1] - 1, 0), (0, 0)) X = np.pad(X, pad_width=npad, mode='constant', constant_values=0) elif not 'testFuncs' in datadir and Path('VZTestFuncs.npz').exists(): X, sourcePoints = load_test_funcs(domain='time', medium='constant', verbose=True, return_sampling_points=True) elif 'testFuncs' in datadir and Path('VZTestFuncs.npz').exists(): userResponded = False print(textwrap.dedent( ''' Two files are available containing simulated test functions. Enter '1' to view the user-provided test functions. (Default) Enter '2' to view the test functions computed by Vezda. Enter 'q/quit' to exit. ''')) while userResponded == False: answer = input('Action: ') if answer == '' or answer == '1': X, sourcePoints = load_test_funcs(domain='time', medium='variable', verbose=True, return_sampling_points=True) # Pad time axis with zeros to length of convolution 2Nt-1 npad = ((0, 0), (X.shape[1] - 1, 0), (0, 0)) X = np.pad(X, pad_width=npad, mode='constant', constant_values=0) userResponded = True break elif answer == '2': X, sourcePoints = load_test_funcs(domain='time', medium='constant', verbose=True, return_sampling_points=True) userResponded = True break elif answer == 'q' or answer == 'quit': sys.exit('Exiting program.') else: print('Invalid response. Please enter \'1\', \'2\', or \'q/quit\'.') #============================================================================== # increment source/recording interval and receiver interval to be consistent # with one-based indexing (i.e., count from one instead of zero) sinterval += 1 rinterval += 1 Ns = X.shape[2] remove_keymap_conflicts({'left', 'right', 'up', 'down', 'save'}) if args.map: fig, ax1, ax2 = setFigure(num_axes=2, mode=plotParams['view_mode'], ax2_dim=receiverPoints.shape[1]) ax1.volume = X ax1.index = Ns // 2 title = wave_title(ax1.index, sinterval, sourcePoints, wiggleType, plotParams) plotWiggles(ax1, X[:, :, ax1.index], time, rinterval, receiverPoints, title, wiggleType, plotParams) ax2.index = ax1.index plotMap(ax2, ax2.index, receiverPoints, sourcePoints, scatterer, wiggleType, plotParams) plt.tight_layout() fig.canvas.mpl_connect('key_press_event', lambda event: process_key_waves(event, time, rinterval, sinterval, receiverPoints, sourcePoints, Ns, scatterer, args.map, wiggleType, plotParams)) else: fig, ax = setFigure(num_axes=1, mode=plotParams['view_mode']) ax.volume = X ax.index = Ns // 2 title = wave_title(ax.index, sinterval, sourcePoints, wiggleType, plotParams) plotWiggles(ax, X[:, :, ax.index], time, rinterval, receiverPoints, title, wiggleType, plotParams) plt.tight_layout() fig.canvas.mpl_connect('key_press_event', lambda event: process_key_waves(event, time, rinterval, sinterval, receiverPoints, sourcePoints, Ns, scatterer, args.map, wiggleType, plotParams)) plt.show()
import sys import numpy as np from vezda.data_utils import get_user_windows, fft_and_window from vezda.math_utils import nextPow2 from vezda.sampling_utils import sampleSpace from vezda.plot_utils import setFigure from vezda.LinearOperators import asConvolutionalOperator import matplotlib.pyplot as plt import matplotlib.animation as animation import textwrap sys.path.append(os.getcwd()) import pulseFun rinterval, tinterval, tstep, dt, sinterval = get_user_windows() datadir = np.load('datadir.npz') recordingTimes = np.load(str(datadir['recordingTimes'])) recordingTimes = recordingTimes[tinterval] Nt = len(recordingTimes) T = recordingTimes[-1] - recordingTimes[0] convolutionTimes = np.linspace(-T, T, 2 * Nt - 1) N = nextPow2(2 * Nt) freqs = np.fft.rfftfreq(N, tstep * dt) Nf = len(freqs) if 'sources' in datadir: sourcePoints = np.load(str(datadir['sources']))
def cli(): parser = argparse.ArgumentParser() parser.add_argument('--nfo', action='store_true', help='''Plot the singular-value decomposition of the near-field operator (NFO).''') parser.add_argument('--lso', action='store_true', help='''Plot the singular-value decomposition of the Lippmann-Schwinger operator (LSO).''') parser.add_argument('--format', '-f', type=str, default='pdf', choices=['png', 'pdf', 'ps', 'eps', 'svg'], help='''Specify the image format of the saved file. Accepted formats are png, pdf, ps, eps, and svg. Default format is set to pdf.''') parser.add_argument('--mode', type=str, choices=['light', 'dark'], required=False, help='''Specify whether to view plots in light mode for daytime viewing or dark mode for nighttime viewing. Mode must be either \'light\' or \'dark\'.''') args = parser.parse_args() # See if an SVD already exists. If so, attempt to load it... if args.nfo and not args.lso: operatorName = 'near-field operator' filename = 'NFO_SVD.npz' elif not args.nfo and args.lso: operatorName = 'Lippmann-Schwinger operator' filename = 'LSO_SVD.npz' elif args.nfo and args.lso: sys.exit(textwrap.dedent( ''' UsageError: Please specify only one of the arguments \'--nfo\' or \'--lso\'. ''')) else: sys.exit(textwrap.dedent( ''' For which operator would you like to plot a singular-value decomposition? Enter: vzsvd --nfo for the near-field operator or vzsvd --lso for the Lippmann-Schwinger operator. ''')) try: U, s, Vh = load_svd(filename) except IOError: sys.exit(textwrap.dedent( ''' A singular-value decomposition of the {s} does not exist. '''.format(s=operatorName))) #============================================================================== # Read in data files #============================================================================== datadir = np.load('datadir.npz') receiverPoints = np.load(str(datadir['receivers'])) recordingTimes = np.load(str(datadir['recordingTimes'])) # Apply user-specified windows rinterval, tinterval, tstep, dt, sinterval = get_user_windows() receiverPoints = receiverPoints[rinterval, :] recordingTimes = recordingTimes[tinterval] # Load appropriate source points and source window if args.nfo: # Near-field operator if 'sources' in datadir: sourcePoints = np.load(str(datadir['sources'])) sourcePoints = sourcePoints[sinterval, :] else: sourcePoints = None else: # if args.lso (Lippmann-Schwinger operator) # in the case of the Lippmann-Schwinger operator, 'sourcePoints' # correspond to sampling points, which should always exist. if 'testFuncs' in datadir: sourcePoints = np.load(str(datadir['samplingPoints'])) elif Path('VZTestFuncs.npz').exists(): TFDict = np.load('VZTestFuncs.npz') sourcePoints = TFDict['samplingPoints'] else: sys.exit(textwrap.dedent( ''' Error: A sampling grid must exist and test functions computed before a singular-value decomposition of the Lippmann-Schwinger operator can be computed or plotted. ''')) # update sinterval for test functions sinterval = np.arange(0, sourcePoints.shape[0], 1) # increment receiver/source intervals to be consistent with # one-based indexing (i.e., count from one instead of zero) rinterval += 1 sinterval += 1 #============================================================================== # Determine whether to plot SVD in time domain or frequency domain #============================================================================== if np.issubdtype(U.dtype, np.complexfloating): domain = 'freq' else: domain = 'time' # Load plot parameters if Path('plotParams.pkl').exists(): plotParams = pickle.load(open('plotParams.pkl', 'rb')) else: plotParams = default_params() Nr = receiverPoints.shape[0] Nt = len(recordingTimes) k = len(s) if domain == 'freq': # plot singular vectors in frequency domain N = nextPow2(2 * Nt) freqs = np.fft.rfftfreq(N, tstep * dt) if plotParams['fmax'] is None: plotParams['fmax'] = np.max(freqs) # Apply the frequency window fmin = plotParams['fmin'] fmax = plotParams['fmax'] df = 1.0 / (N * tstep * dt) startIndex = int(round(fmin / df)) stopIndex = int(round(fmax / df)) finterval = np.arange(startIndex, stopIndex, 1) freqs = freqs[finterval] M = len(freqs) Ns = int(Vh.shape[1] / M) U = U.toarray().reshape((Nr, M, k)) V = Vh.getH().toarray().reshape((Ns, M, k)) else: # domain == 'time' M = 2 * Nt - 1 Ns = int(Vh.shape[1] / M) U = U.reshape((Nr, M, k)) V = Vh.T.reshape((Ns, M, k)) T = recordingTimes[-1] - recordingTimes[0] times = np.linspace(-T, T, M) if args.mode is not None: plotParams['view_mode'] = args.mode pickle.dump(plotParams, open('plotParams.pkl', 'wb'), pickle.HIGHEST_PROTOCOL) remove_keymap_conflicts({'left', 'right', 'up', 'down', 'save'}) if domain == 'freq': # plot the left singular vectors fig_lvec, ax_lvec_r, ax_lvec_i = setFigure(num_axes=2, mode=plotParams['view_mode']) ax_lvec_r.volume = U.real ax_lvec_i.volume = U.imag ax_lvec_r.index = 0 ax_lvec_i.index = 0 fig_lvec.suptitle('Left-Singular Vector', color=ax_lvec_r.titlecolor, fontsize=16) fig_lvec.subplots_adjust(bottom=0.27, top=0.86) leftTitle_r = vector_title('left', ax_lvec_r.index + 1, 'real') leftTitle_i = vector_title('left', ax_lvec_i.index + 1, 'imag') for ax, title in zip([ax_lvec_r, ax_lvec_i], [leftTitle_r, leftTitle_i]): left_im = plotFreqVectors(ax, ax.volume[:, :, ax.index], freqs, rinterval, receiverPoints, title, 'left', plotParams) lp0 = ax_lvec_r.get_position().get_points().flatten() lp1 = ax_lvec_i.get_position().get_points().flatten() left_cax = fig_lvec.add_axes([lp0[0], 0.12, lp1[2]-lp0[0], 0.03]) lcbar = fig_lvec.colorbar(left_im, left_cax, orientation='horizontal') lcbar.outline.set_edgecolor(ax_lvec_r.cbaredgecolor) lcbar.ax.tick_params(axis='x', colors=ax_lvec_r.labelcolor) lcbar.ax.yaxis.set_major_formatter(FormatStrFormatter('%.1f')) lcbar.set_label('Amplitude', labelpad=5, rotation=0, fontsize=12, color=ax_lvec_r.labelcolor) fig_lvec.canvas.mpl_connect('key_press_event', lambda event: process_key_vectors(event, freqs, rinterval, sinterval, receiverPoints, sourcePoints, plotParams, 'cmplx_left')) # plot the right singular vectors fig_rvec, ax_rvec_r, ax_rvec_i = setFigure(num_axes=2, mode=plotParams['view_mode']) ax_rvec_r.volume = V.real ax_rvec_i.volume = V.imag ax_rvec_r.index = 0 ax_rvec_i.index = 0 fig_rvec.suptitle('Right-Singular Vector', color=ax_rvec_r.titlecolor, fontsize=16) fig_rvec.subplots_adjust(bottom=0.27, top=0.86) rightTitle_r = vector_title('right', ax_rvec_r.index + 1, 'real') rightTitle_i = vector_title('right', ax_rvec_i.index + 1, 'imag') for ax, title in zip([ax_rvec_r, ax_rvec_i], [rightTitle_r, rightTitle_i]): right_im = plotFreqVectors(ax, ax.volume[:, :, ax.index], freqs, sinterval, sourcePoints, title, 'right', plotParams) rp0 = ax_rvec_r.get_position().get_points().flatten() rp1 = ax_rvec_i.get_position().get_points().flatten() right_cax = fig_rvec.add_axes([rp0[0], 0.12, rp1[2]-rp0[0], 0.03]) rcbar = fig_rvec.colorbar(right_im, right_cax, orientation='horizontal') rcbar.outline.set_edgecolor(ax_rvec_r.cbaredgecolor) rcbar.ax.tick_params(axis='x', colors=ax_rvec_r.labelcolor) rcbar.ax.yaxis.set_major_formatter(FormatStrFormatter('%.1f')) rcbar.set_label('Amplitude', labelpad=5, rotation=0, fontsize=12, color=ax_lvec_r.labelcolor) fig_rvec.canvas.mpl_connect('key_press_event', lambda event: process_key_vectors(event, freqs, rinterval, sinterval, receiverPoints, sourcePoints, plotParams, 'cmplx_right')) else: # domain == 'time' fig_vec, ax_lvec, ax_rvec = setFigure(num_axes=2, mode=plotParams['view_mode']) ax_lvec.volume = U ax_lvec.index = 0 leftTitle = vector_title('left', ax_lvec.index + 1) plotWiggles(ax_lvec, ax_lvec.volume[:, :, ax_lvec.index], times, rinterval, receiverPoints, leftTitle, 'left', plotParams) ax_rvec.volume = V ax_rvec.index = 0 rightTitle = vector_title('right', ax_rvec.index + 1) plotWiggles(ax_rvec, ax_rvec.volume[:, :, ax_rvec.index], times, sinterval, sourcePoints, rightTitle, 'right', plotParams) fig_vec.tight_layout() fig_vec.canvas.mpl_connect('key_press_event', lambda event: process_key_vectors(event, times, rinterval, sinterval, receiverPoints, sourcePoints, plotParams)) #============================================================================== # plot the singular values # figure and axis for singular values fig_vals, ax_vals = setFigure(num_axes=1, mode=plotParams['view_mode']) n = np.arange(1, k + 1, 1) kappa = s[0] / s[-1] # condition number = max(s) / min(s) ax_vals.plot(n, s, '.', clip_on=False, markersize=9, label=r'Condition Number: %0.1e' %(kappa), color=ax_vals.pointcolor) ax_vals.set_xlabel('n', color=ax_vals.labelcolor) ax_vals.set_ylabel('$\sigma_n$', color=ax_vals.labelcolor) legend = ax_vals.legend(title='Singular Values', loc='upper center', bbox_to_anchor=(0.5, 1.25), markerscale=0, handlelength=0, handletextpad=0, fancybox=True, shadow=True, fontsize='large') legend.get_title().set_fontsize('large') ax_vals.set_xlim([1, k]) ax_vals.set_ylim(bottom=0) ax_vals.locator_params(axis='y', nticks=6) ax_vals.ticklabel_format(style='sci', axis='y', scilimits=(0,0)) fig_vals.tight_layout() fig_vals.savefig('singularValues.' + args.format, format=args.format, bbox_inches='tight', facecolor=fig_vals.get_facecolor()) plt.show()
def cli(): parser = argparse.ArgumentParser() parser.add_argument('--data', action='store_true', help='Plot the frequency spectrum of the recorded data. (Default)') parser.add_argument('--testfunc', action='store_true', help='Plot the frequency spectrum of the simulated test functions.') parser.add_argument('--power', action='store_true', help='''Plot the mean power spectrum of the input signals. Default is to plot the mean amplitude spectrum of the Fourier transform.''') parser.add_argument('--fmin', type=float, help='Specify the minimum frequency of the amplitude/power spectrum plot. Default is set to 0.') parser.add_argument('--fmax', type=float, help='''Specify the maximum frequency of the amplitude/power spectrum plot. Default is set to the maximum frequency bin based on the length of the time signal.''') parser.add_argument('--fu', type=str, help='Specify the frequency units (e.g., Hz)') parser.add_argument('--format', '-f', type=str, default='pdf', choices=['png', 'pdf', 'ps', 'eps', 'svg'], help='''specify the image format of the saved file. Accepted formats are png, pdf, ps, eps, and svg. Default format is set to pdf.''') parser.add_argument('--mode', type=str, choices=['light', 'dark'], required=False, help='''Specify whether to view plots in light mode for daytime viewing or dark mode for nighttime viewing. Mode must be either \'light\' or \'dark\'.''') args = parser.parse_args() #============================================================================== # Get time window parameters tinterval, tstep, dt = get_user_windows()[1:4] datadir = np.load('datadir.npz') recordingTimes = np.load(str(datadir['recordingTimes'])) recordingTimes = recordingTimes[tinterval] # Used for getting time and frequency units if Path('plotParams.pkl').exists(): plotParams = pickle.load(open('plotParams.pkl', 'rb')) else: plotParams = default_params() if all(v is True for v in [args.data, args.testfunc]): sys.exit(textwrap.dedent( ''' Error: Cannot plot frequency spectrum of both recorded data and simulated test functions. Use vzspectra --data to plot the frequency spectrum of the recorded data or vzspectra --testfuncs to plot the frequency spectrum of the simulated test functions. ''')) elif (args.data and not args.testfunc) or all(v is not True for v in [args.data, args.testfunc]): # default is to plot spectra of data if user does not specify either args.data or args.testfunc X = load_data(domain='time', verbose=True) elif not args.data and args.testfunc: if 'testFuncs' not in datadir and not Path('VZTestFuncs.npz').exists(): X = load_test_funcs(domain='time', medium='constant', verbose=True) elif 'testFuncs' in datadir and not Path('VZTestFuncs.npz').exists(): X = load_test_funcs(domain='time', medium='variable', verbose=True) elif not 'testFuncs' in datadir and Path('VZTestFuncs.npz').exists(): X = load_test_funcs(domain='time', medium='constant', verbose=True) elif 'testFuncs' in datadir and Path('VZTestFuncs.npz').exists(): userResponded = False print(textwrap.dedent( ''' Two files are available containing simulated test functions. Enter '1' to view the user-provided test functions. (Default) Enter '2' to view the test functions computed by Vezda. Enter 'q/quit' to exit. ''')) while userResponded == False: answer = input('Action: ') if answer == '' or answer == '1': X = load_test_funcs(domain='time', medium='variable', verbose=True) userResponded = True break elif answer == '2': X = load_test_funcs(domain='time', medium='constant', verbose=True) userResponded = True break elif answer == 'q' or answer == 'quit': sys.exit('Exiting program.') else: print('Invalid response. Please enter \'1\', \'2\', or \'q/quit\'.') #============================================================================== # compute spectra freqs, amplitudes = compute_spectrum(X, tstep * dt, args.power) if args.power: plotLabel = 'power' plotParams['freq_title'] = 'Mean Power Spectrum' plotParams['freq_ylabel'] = 'Power' else: plotLabel = 'amplitude' plotParams['freq_title'] = 'Mean Amplitude Spectrum' plotParams['freq_ylabel'] = 'Amplitude' if args.data or all(v is not True for v in [args.data, args.testfunc]): plotParams['freq_title'] += ' [' + plotParams['data_title'] + ']' elif args.testfunc: plotParams['freq_title'] += ' [' + plotParams['tf_title'] + 's]' if args.fmin is not None: if args.fmin >= 0: if args.fmax is not None: if args.fmax > args.fmin: plotParams['fmin'] = args.fmin plotParams['fmax'] = args.fmax else: sys.exit(textwrap.dedent( ''' RelationError: The maximum frequency of the %s spectrum plot must be greater than the mininum frequency. ''' %(plotLabel))) else: fmax = plotParams['fmax'] if fmax > args.fmin: plotParams['fmin'] = args.fmin else: sys.exit(textwrap.dedent( ''' RelationError: The specified minimum frequency of the %s spectrum plot must be less than the maximum frequency. ''' %(plotLabel))) else: sys.exit(textwrap.dedent( ''' ValueError: The specified minimum frequency of the %s spectrum plot must be nonnegative. ''' %(plotLabel))) #=============================================================================== if args.fmax is not None: if args.fmin is not None: if args.fmin >= 0: if args.fmax > args.fmin: plotParams['fmin'] = args.fmin plotParams['fmax'] = args.fmax else: sys.exit(textwrap.dedent( ''' RelationError: The maximum frequency of the %s spectrum plot must be greater than the mininum frequency. ''' %(plotLabel))) else: sys.exit(textwrap.dedent( ''' ValueError: The specified minimum frequency of the %s spectrum plot must be nonnegative. ''' %(plotLabel))) else: fmin = plotParams['fmin'] if args.fmax > fmin: plotParams['fmax'] = args.fmax else: sys.exit(textwrap.dedent( ''' RelationError: The specified maximum frequency of the %s spectrum plot must be greater than the minimum frequency. ''' %(plotLabel))) elif plotParams['fmax'] is None: plotParams['fmax'] = np.max(freqs) #=================================================================================== if args.fu is not None: plotParams['fu'] = args.fu if args.mode is not None: plotParams['view_mode'] = args.mode pickle.dump(plotParams, open('plotParams.pkl', 'wb'), pickle.HIGHEST_PROTOCOL) fig, ax = setFigure(num_axes=1, mode=plotParams['view_mode']) ax.plot(freqs, amplitudes, color=ax.linecolor, linewidth=ax.linewidth) ax.set_title(plotParams['freq_title'], color=ax.titlecolor) # get frequency units from plotParams fu = plotParams['fu'] fmin = plotParams['fmin'] fmax = plotParams['fmax'] if fu != '': ax.set_xlabel('Frequency (%s)' %(fu), color=ax.labelcolor) else: ax.set_xlabel('Frequency', color=ax.labelcolor) ax.set_ylabel(plotParams['freq_ylabel'], color=ax.labelcolor) ax.set_xlim([fmin, fmax]) ax.set_ylim(bottom=0) ax.fill_between(freqs, 0, amplitudes, where=(amplitudes > 0), color='m', alpha=ax.alpha) ax.locator_params(axis='y', nticks=6) ax.ticklabel_format(style='sci', axis='y', scilimits=(0,0)) plt.tight_layout() fig.savefig(plotLabel + 'Spectrum.' + args.format, format=args.format, bbox_inches='tight', facecolor=fig.get_facecolor()) plt.show()
def cli(): parser = argparse.ArgumentParser() parser.add_argument('--data', action='store_true', help='Plot the recorded data. (Default)') parser.add_argument('--impulse', action='store_true', help='Plot the simulated impulse responses.') parser.add_argument('--medium', type=str, default='constant', choices=['constant', 'variable'], help='''Specify whether the background medium is constant or variable (inhomogeneous). If argument is set to 'constant', the velocity defined in the required 'pulsesFun.py' file is used. Default is set to 'constant'.''') parser.add_argument('--tu', type=str, help='Specify the time units (e.g., \'s\' or \'ms\').') parser.add_argument('--au', type=str, help='Specify the amplitude units (e.g., \'m\' or \'mm\').') parser.add_argument('--colormap', type=str, default=None, choices=['grays', 'seismic', 'native'], help='specify a colormap for wiggle plots. Default is \'grays\'.') parser.add_argument('--pclip', type=float, help='''Specify the percentage (0-1) of the peak amplitude to display. This parameter is used for pcolormesh plots only. Default is set to 1.''') parser.add_argument('--title', type=str, help='''Specify a title for the wiggle plot. Default title is \'Data\' if \'--data\' is passed and 'Impulse Response' if \'--impulse\' is passed.''') parser.add_argument('--format', '-f', type=str, default='pdf', choices=['png', 'pdf', 'ps', 'eps', 'svg'], help='''Specify the image format of the saved file. Accepted formats are png, pdf, ps, eps, and svg. Default format is set to pdf.''') parser.add_argument('--map', action='store_true', help='''Plot a map of the receiver and source/search point locations. The current source/search point will be highlighted. The boundary of the scatterer will also be shown if available.''') parser.add_argument('--mode', type=str, choices=['light', 'dark'], required=False, help='''Specify whether to view plots in light mode for daytime viewing or dark mode for nighttime viewing. Mode must be either \'light\' or \'dark\'.''') args = parser.parse_args() #============================================================================== # if a plotParams.pkl file already exists, load relevant parameters if Path('plotParams.pkl').exists(): plotParams = pickle.load(open('plotParams.pkl', 'rb')) # update parameters for wiggle plots based on passed arguments if args.mode is not None: plotParams['view_mode'] = args.mode if args.tu is not None: plotParams['tu'] = args.tu if args.au is not None: plotParams['au'] = args.au if args.colormap is not None: plotParams['wiggle_colormap'] = args.colormap if args.pclip is not None: if args.pclip >= 0 and args.pclip <= 1: plotParams['pclip'] = args.pclip else: print(textwrap.dedent( ''' Warning: Invalid value passed to argument \'--pclip\'. Value must be between 0 and 1. ''')) if args.title is not None: if args.data: plotParams['data_title'] = args.title elif args.impulse: plotParams['tf_title'] = args.title else: # create a plotParams dictionary file with default values plotParams = default_params() # update parameters for wiggle plots based on passed arguments if args.mode is not None: plotParams['view_mode'] = args.mode if args.tu is not None: plotParams['tu'] = args.tu if args.au is not None: plotParams['au'] = args.au if args.colormap is not None: plotParams['wiggle_colormap'] = args.colormap if args.title is not None: if args.data: plotParams['data_title'] = args.title elif args.impulse: plotParams['tf_title'] = args.title pickle.dump(plotParams, open('plotParams.pkl', 'wb'), pickle.HIGHEST_PROTOCOL) #============================================================================== # Load the relevant data to plot datadir = np.load('datadir.npz') receiverPoints = np.load(str(datadir['receivers'])) time = np.load(str(datadir['recordingTimes'])) # Apply any user-specified windows receiverNumbers, tinterval, tstep, dt, sourceNumbers = get_user_windows() receiverPoints = receiverPoints[receiverNumbers, :] time = time[tinterval] if 'sources' in datadir: sourcePoints = np.load(str(datadir['sources'])) sourcePoints = sourcePoints[sourceNumbers, :] # Check for source-receiver reciprocity reciprocalNumbers = get_unique_indices(sourcePoints, receiverPoints) reciprocalNumbers = np.asarray(reciprocalNumbers, dtype=np.int) if len(reciprocalNumbers) > 0: newReceivers = sourcePoints[reciprocalNumbers, :] reciprocity = True else: reciprocity = False else: reciprocity = False sourcePoints = None # Load the scatterer boundary, if it exists if 'scatterer' in datadir: scatterer = np.load(str(datadir['scatterer'])) else: scatterer = None if all(v is True for v in [args.data, args.impulse]): # User specified both data and impulse response for plotting # Send error message and exit. sys.exit(textwrap.dedent( ''' Error: Cannot plot both recorded data and simulated impulse responses. Use vzwiggles --data to plot the recorded data or vzwiggles --impulse to plot the simulated impulse responses. ''')) elif all(v is not True for v in [args.data, args.impulse]) or args.data: # If user did not specify which wiggles to plot, plot recorded data by default. # load the 3D data array into variable 'X' # X[receiver, time, source] wiggleType = 'data' X = load_data(domain='time', verbose=True) if reciprocity: Nr = len(receiverNumbers) Ns = len(sourceNumbers) M = len(reciprocalNumbers) XR = X[-M:, :, -Nr:] X = X[:Nr, :, :Ns] reciprocalNumbers += 1 ER = Experiment(XR, time, reciprocalNumbers, newReceivers, receiverNumbers, receiverPoints, wiggleType) else: ER = None elif args.impulse: wiggleType = 'impulse' # Update time to convolution times T = time[-1] - time[0] time = np.linspace(-T, T, 2 * len(time) - 1) X, sourcePoints = load_impulse_responses(domain='time', medium=args.medium, verbose=True, return_search_points=True) # Update sourceNumbers to match search points sourceNumbers = np.arange(sourcePoints.shape[0]) if reciprocity: Nr = len(receiverNumbers) M = len(reciprocalNumbers) XR = X[-M:, :, :] X = X[:Nr, :, :] reciprocalNumbers += 1 ER = Experiment(XR, time, reciprocalNumbers, newReceivers, sourceNumbers, sourcePoints, wiggleType) else: ER = None #============================================================================== # increment source/receiver numbers to be consistent with # one-based indexing (i.e., count from one instead of zero) sourceNumbers += 1 receiverNumbers += 1 E = Experiment(X, time, receiverNumbers, receiverPoints, sourceNumbers, sourcePoints, wiggleType) p = Plotter(E, ER) p.plot(scatterer, plotParams, args.map) plt.show()
def cli(): parser = argparse.ArgumentParser() parser.add_argument('--data', action='store_true', help='Plot the frequency spectra of the recorded data. (Default)') parser.add_argument('--impulse', action='store_true', help='Plot the frequency spectra of the simulated impulse responses.') parser.add_argument('--scaling', '-s', type=str, default='amp', choices=['amp', 'pow', 'psd'], help='''Specify the scaling of the spectrum. Choose from amplitude ('amp'), power ('pow'), or power spectral density ('psd') using Welch's method. Default is set to 'amp'.''') parser.add_argument('--nseg', type=int, help='''Specify the approximate number of segments into which the time signal will be partitioned. Only used if scaling is set to 'psd'. Increasing the number of segments increases computational cost and the accuracy of the PSD estimate, but decreases frequency resolution. Default is set to 20.''') parser.add_argument('--fmin', type=float, help='Specify the minimum frequency of the amplitude/power spectrum plot. Default is set to 0.') parser.add_argument('--fmax', type=float, help='''Specify the maximum frequency of the amplitude/power spectrum plot. Default is set to the maximum frequency bin based on the length of the time signal.''') parser.add_argument('--au', type=str, help='Specify the amplitude units (e.g., Pa)') parser.add_argument('--fu', type=str, help='Specify the frequency units (e.g., Hz)') parser.add_argument('--format', '-f', type=str, default='pdf', choices=['png', 'pdf', 'ps', 'eps', 'svg'], help='''specify the image format of the saved file. Accepted formats are png, pdf, ps, eps, and svg. Default format is set to pdf.''') parser.add_argument('--mode', type=str, choices=['light', 'dark'], required=False, help='''Specify whether to view plots in light mode for daytime viewing or dark mode for nighttime viewing. Mode must be either \'light\' or \'dark\'.''') args = parser.parse_args() #============================================================================== # Get time window parameters tinterval, tstep, dt = get_user_windows()[1:4] datadir = np.load('datadir.npz') recordingTimes = np.load(str(datadir['recordingTimes'])) recordingTimes = recordingTimes[tinterval] # Used for getting time and frequency units if Path('plotParams.pkl').exists(): plotParams = pickle.load(open('plotParams.pkl', 'rb')) else: plotParams = default_params() if all(v is True for v in [args.data, args.impulse]): X = load_data(domain='time', verbose=True) Xlabel = plotParams['data_title'] Xcolor = 'm' if 'testFuncs' not in datadir and not Path('VZImpulseResponses.npz').exists(): X2 = load_impulse_responses(domain='time', medium='constant', verbose=True) X2label = plotParams['impulse_title'] X2color = 'c' elif 'testFuncs' in datadir and not Path('VZImpulseResponses.npz').exists(): X2 = load_impulse_responses(domain='time', medium='variable', verbose=True) X2label = plotParams['impulse_title'] X2color = 'c' elif not 'testFuncs' in datadir and Path('VZImpulseResponses.npz').exists(): X2 = load_impulse_responses(domain='time', medium='constant', verbose=True) X2label = plotParams['impulse_title'] X2color = 'c' elif 'testFuncs' in datadir and Path('VZImpulseResponses.npz').exists(): userResponded = False print(textwrap.dedent( ''' Two files are available containing simulated impulse responses. Enter '1' to view the user-provided impulse responses. (Default) Enter '2' to view the impulse responses computed by Vezda. Enter 'q/quit' to exit. ''')) while userResponded == False: answer = input('Action: ') if answer == '' or answer == '1': X2 = load_impulse_responses(domain='time', medium='variable', verbose=True) X2label = plotParams['impulse_title'] X2color = 'c' userResponded = True break elif answer == '2': X2 = load_impulse_responses(domain='time', medium='constant', verbose=True) X2label = plotParams['impulse_title'] X2color = 'c' userResponded = True break elif answer == 'q' or answer == 'quit': sys.exit('Exiting program.') else: print('Invalid response. Please enter \'1\', \'2\', or \'q/quit\'.') elif (args.data and not args.impulse) or all(v is not True for v in [args.data, args.impulse]): # default is to plot spectra of data if user does not specify either args.data or args.impulse X = load_data(domain='time', verbose=True) Xlabel = plotParams['data_title'] Xcolor = 'm' X2 = None elif not args.data and args.impulse: if 'testFuncs' not in datadir and not Path('VZImpulseResponses.npz').exists(): X = load_impulse_responses(domain='time', medium='constant', verbose=True) Xlabel = plotParams['impulse_title'] Xcolor = 'c' elif 'testFuncs' in datadir and not Path('VZImpulseResponses.npz').exists(): X = load_impulse_responses(domain='time', medium='variable', verbose=True) Xlabel = plotParams['impulse_title'] Xcolor = 'c' elif not 'testFuncs' in datadir and Path('VZImpulseResponses.npz').exists(): X = load_impulse_responses(domain='time', medium='constant', verbose=True) Xlabel = plotParams['impulse_title'] Xcolor = 'c' elif 'testFuncs' in datadir and Path('VZImpulseResponses.npz').exists(): userResponded = False print(textwrap.dedent( ''' Two files are available containing simulated impulse responses. Enter '1' to view the user-provided impulse responses. (Default) Enter '2' to view the impulse responses computed by Vezda. Enter 'q/quit' to exit. ''')) while userResponded == False: answer = input('Action: ') if answer == '' or answer == '1': X = load_impulse_responses(domain='time', medium='variable', verbose=True) Xlabel = plotParams['impulse_title'] Xcolor = 'c' userResponded = True break elif answer == '2': X = load_impulse_responses(domain='time', medium='constant', verbose=True) Xlabel = plotParams['impulse_title'] Xcolor = 'c' userResponded = True break elif answer == 'q' or answer == 'quit': sys.exit('Exiting program.') else: print('Invalid response. Please enter \'1\', \'2\', or \'q/quit\'.') X2 = None #============================================================================== # compute spectra if args.nseg is not None: if args.nseg >= 1: nseg = args.nseg else: sys.exit(textwrap.dedent( ''' Error: Optional argument '--nseg' must be greater than or equal to one. ''')) else: # if args.nseg is None nseg = 1 freqs, A = compute_spectra(X, tstep * dt, scaling=args.scaling, nseg=nseg) if X2 is not None: Nt = X.shape[1] X2 = X2[:, -Nt:, :] freqs2, A2 = compute_spectra(X2, tstep * dt, scaling=args.scaling, nseg=nseg) else: freqs2 = None A2 = None if args.au is not None: plotParams['au'] = args.au if args.fu is not None: plotParams['fu'] = args.fu au = plotParams['au'] fu = plotParams['fu'] if args.scaling == 'amp': plotLabel = 'amplitude' plotParams['freq_title'] = 'Mean Amplitude Spectrum' if au != '': plotParams['freq_ylabel'] = 'Amplitude (%s)' %(au) else: plotParams['freq_ylabel'] = 'Amplitude' elif args.scaling == 'pow': plotLabel = 'power' plotParams['freq_title'] = 'Mean Power Spectrum' if au != '': plotParams['freq_ylabel'] = 'Power (%s)' %(au + '$^2$') else: plotParams['freq_ylabel'] = 'Power' elif args.scaling == 'psd': plotLabel = 'psd' plotParams['freq_title'] = 'Mean Power Spectral Density' if au != '' and fu != '': plotParams['freq_ylabel'] = 'Power/Frequency (%s)' %(au + '$^2/$' + fu) else: plotParams['freq_ylabel'] = 'Power/Frequency' if args.fmin is not None: if args.fmin >= 0: if args.fmax is not None: if args.fmax > args.fmin: plotParams['fmin'] = args.fmin plotParams['fmax'] = args.fmax else: sys.exit(textwrap.dedent( ''' RelationError: The maximum frequency of the %s spectrum plot must be greater than the mininum frequency. ''' %(plotLabel))) else: fmax = plotParams['fmax'] if fmax > args.fmin: plotParams['fmin'] = args.fmin else: sys.exit(textwrap.dedent( ''' RelationError: The specified minimum frequency of the %s spectrum plot must be less than the maximum frequency. ''' %(plotLabel))) else: sys.exit(textwrap.dedent( ''' ValueError: The specified minimum frequency of the %s spectrum plot must be nonnegative. ''' %(plotLabel))) #=============================================================================== if args.fmax is not None: if args.fmin is not None: if args.fmin >= 0: if args.fmax > args.fmin: plotParams['fmin'] = args.fmin plotParams['fmax'] = args.fmax else: sys.exit(textwrap.dedent( ''' RelationError: The maximum frequency of the %s spectrum plot must be greater than the mininum frequency. ''' %(plotLabel))) else: sys.exit(textwrap.dedent( ''' ValueError: The specified minimum frequency of the %s spectrum plot must be nonnegative. ''' %(plotLabel))) else: fmin = plotParams['fmin'] if args.fmax > fmin: plotParams['fmax'] = args.fmax else: sys.exit(textwrap.dedent( ''' RelationError: The specified maximum frequency of the %s spectrum plot must be greater than the minimum frequency. ''' %(plotLabel))) elif plotParams['fmax'] is None: plotParams['fmax'] = np.max(freqs) #=================================================================================== if args.mode is not None: plotParams['view_mode'] = args.mode pickle.dump(plotParams, open('plotParams.pkl', 'wb'), pickle.HIGHEST_PROTOCOL) fig, ax = setFigure(num_axes=1, mode=plotParams['view_mode']) if args.scaling == 'psd': plotscale = 'log' else: plotscale = 'linear' gradient_fill(freqs, A, fill_color=Xcolor, ax=ax, scale=plotscale, zorder=2) handles, labels = [], [] handles.append(Line2D([0], [0], color=Xcolor, lw=4)) labels.append(Xlabel) if all(v is not None for v in [freqs2, A2]): gradient_fill(freqs2, A2, fill_color=X2color, ax=ax, scale=plotscale, zorder=1) handles.append(Line2D([0], [0], color=X2color, lw=4)) labels.append(X2label) ax.legend(handles, labels, fancybox=True, framealpha=1, shadow=True, loc='upper right') ax.set_title(plotParams['freq_title'], color=ax.titlecolor) fmin = plotParams['fmin'] fmax = plotParams['fmax'] if fu != '': ax.set_xlabel('Frequency (%s)' %(fu), color=ax.labelcolor) else: ax.set_xlabel('Frequency', color=ax.labelcolor) ax.set_ylabel(plotParams['freq_ylabel'], color=ax.labelcolor) ax.set_xlim([fmin, fmax]) if args.scaling != 'psd': ax.set_ylim(bottom=0) ax.ticklabel_format(style='sci', axis='y', scilimits=(0,0)) plt.tight_layout() fig.savefig(plotLabel + 'Spectrum.' + args.format, format=args.format, bbox_inches='tight', facecolor=fig.get_facecolor()) plt.show()