Exemple #1
0
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()
Exemple #2
0
def cli():
    parser = argparse.ArgumentParser()
    parser.add_argument(
        '--nfe',
        action='store_true',
        help='''Plot the image obtained by solving the near-field equation.''')
    parser.add_argument(
        '--lse',
        action='store_true',
        help=
        '''Plot the image obtained by solving the Lippmann-Schwinger equation.'''
    )
    parser.add_argument(
        '--spacetime',
        action='store_true',
        help='''Plot the support of the source function in space-time.''')
    parser.add_argument(
        '--movie',
        action='store_true',
        help='''Save the space-time figure as a rotating frame (2D space)
                        or as a time-lapse structure (3D space).''')
    parser.add_argument('--isolevel',
                        type=float,
                        default=None,
                        help='''Specify the contour level of the isosurface for
                        three-dimensional visualizations. Level must be between 0 and 1.'''
                        )
    parser.add_argument(
        '--format',
        '-f',
        type=str,
        default=None,
        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('--xlabel',
                        type=str,
                        default=None,
                        help='''specify a label for the x-axis.''')
    parser.add_argument('--ylabel',
                        type=str,
                        default=None,
                        help='''specify a label for the y-axis.''')
    parser.add_argument('--zlabel',
                        type=str,
                        default=None,
                        help='''specify a label for the z-axis.''')
    parser.add_argument(
        '--xu',
        type=str,
        default=None,
        help='Specify the units for the x-axis (e.g., \'m\' or \'km\').')
    parser.add_argument(
        '--yu',
        type=str,
        default=None,
        help='Specify the units for the y-axis (e.g., \'m\' or \'km\').')
    parser.add_argument(
        '--zu',
        type=str,
        default=None,
        help='Specify the units for the z-axis (e.g., \'m\' or \'km\').')
    parser.add_argument(
        '--colormap',
        type=str,
        default=None,
        choices=['viridis', 'plasma', 'inferno', 'magma', 'cividis'],
        help=
        'specify a perceptually uniform sequential colormap. Default is \'magma\''
    )
    parser.add_argument(
        '--colorbar',
        type=str,
        default=None,
        choices=['n', 'no', 'false', 'y', 'yes', 'true'],
        help=
        'specify \'y/yes/true\' to plot colorbar. Default is \'n/no/false\'')
    parser.add_argument(
        '--invert_xaxis',
        type=str,
        default=None,
        choices=['n', 'no', 'false', 'y', 'yes', 'true'],
        help=
        'specify \'y/yes/true\' to invert x-axis. Default is \'n/no/false\'')
    parser.add_argument(
        '--invert_yaxis',
        type=str,
        default=None,
        choices=['n', 'no', 'false', 'y', 'yes', 'true'],
        help=
        'specify \'y/yes/true\' to invert y-axis. Default is \'n/no/false\'')
    parser.add_argument(
        '--invert_zaxis',
        type=str,
        default=None,
        choices=['n', 'no', 'false', 'y', 'yes', 'true'],
        help=
        'specify \'y/yes/true\' to invert z-axis. Default is \'n/no/false\'')
    parser.add_argument(
        '--show_scatterer',
        type=str,
        default=None,
        choices=['n', 'no', 'false', 'y', 'yes', 'true'],
        help=
        'specify \'y/yes/true\' to show scatterer boundary. Default is \'n/no/false\''
    )
    parser.add_argument(
        '--show_sources',
        type=str,
        default=None,
        choices=['n', 'no', 'false', 'y', 'yes', 'true'],
        help='specify \'n/no/false\' to hide sources. Default is \'y/yes/true\''
    )
    parser.add_argument(
        '--show_receivers',
        type=str,
        default=None,
        choices=['n', 'no', 'false', 'y', 'yes', 'true'],
        help=
        'specify \'n/no/false\' to hide receivers. Default is \'y/yes/true\'')
    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'))

        # for both wiggle plots and image plots
        if args.format is not None:
            plotParams['pltformat'] = args.format

        if args.mode is not None:
            plotParams['view_mode'] = args.mode

        # for image/map plots
        if args.isolevel is not None:
            plotParams['isolevel'] = args.isolevel

        if args.xlabel is not None:
            plotParams['xlabel'] = args.xlabel

        if args.ylabel is not None:
            plotParams['ylabel'] = args.ylabel

        if args.zlabel is not None:
            plotParams['zlabel'] = args.zlabel
        #==============================================================================
        # handle units here
        if args.xu is not None:
            plotParams['xu'] = args.xu

        if args.yu is not None:
            plotParams['yu'] = args.yu

        if args.zu is not None:
            plotParams['zu'] = args.zu
        #==============================================================================
        if args.colormap is not None:
            plotParams['colormap'] = args.colormap
        #==============================================================================
        if args.colorbar is not None:
            if args.colorbar == 'n' or args.colorbar == 'no' or args.colorbar == 'false':
                plotParams['colorbar'] = False

            elif args.colorbar == 'y' or args.colorbar == 'yes' or args.colorbar == 'true':
                plotParams['colorbar'] = True
        #==============================================================================
        if args.invert_xaxis is not None:
            if args.invert_xaxis == 'n' or args.invert_xaxis == 'no' or args.invert_xaxis == 'false':
                plotParams['invert_xaxis'] = False

            elif args.invert_xaxis == 'y' or args.invert_xaxis == 'yes' or args.invert_xaxis == 'true':
                plotParams['invert_xaxis'] = True
        #==============================================================================
        if args.invert_yaxis is not None:
            if args.invert_yaxis == 'n' or args.invert_yaxis == 'no' or args.invert_yaxis == 'false':
                plotParams['invert_yaxis'] = False

            elif args.invert_yaxis == 'y' or args.invert_yaxis == 'yes' or args.invert_yaxis == 'true':
                plotParams['invert_yaxis'] = True
        #==============================================================================
        if args.invert_zaxis is not None:
            if args.invert_zaxis == 'n' or args.invert_zaxis == 'no' or args.invert_zaxis == 'false':
                plotParams['invert_zaxis'] = False

            elif args.invert_zaxis == 'y' or args.invert_zaxis == 'yes' or args.invert_zaxis == 'true':
                plotParams['invert_zaxis'] = True
        #==============================================================================
        if args.show_scatterer is not None:
            if args.show_scatterer == 'n' or args.show_scatterer == 'no' or args.show_scatterer == 'false':
                plotParams['show_scatterer'] = False

            elif args.show_scatterer == 'y' or args.show_scatterer == 'yes' or args.show_scatterer == 'true':
                plotParams['show_scatterer'] = True
        #==============================================================================
        if args.show_sources is not None:
            if args.show_sources == 'n' or args.show_sources == 'no' or args.show_sources == 'false':
                plotParams['show_sources'] = False

            elif args.show_sources == 'y' or args.show_sources == 'yes' or args.show_sources == 'true':
                plotParams['show_sources'] = True
        #==============================================================================
        if args.show_receivers is not None:
            if args.show_receivers == 'n' or args.show_receivers == 'no' or args.show_receivers == 'false':
                plotParams['show_receivers'] = False

            elif args.show_receivers == 'y' or args.show_receivers == 'yes' or args.show_receivers == 'true':
                plotParams['show_receivers'] = True

    #==============================================================================
    else:  # create a plotParams dictionary file with default values
        plotParams = default_params()

        # updated parameters based on passed arguments
        #for both image and wiggle plots
        if args.format is not None:
            plotParams['pltformat'] = args.format

        # for image/map plots
        if args.isolevel is not None:
            plotParams['isolevel'] = args.isolevel

        if args.colormap is not None:
            plotParams['colormap'] = args.colormap

        if args.colorbar is not None:
            if args.colorbar == 'n' or args.colorbar == 'no' or args.colorbar == 'false':
                plotParams['colorbar'] = False

            elif args.colorbar == 'y' or args.colorbar == 'yes' or args.colorbar == 'true':
                plotParams['colorbar'] = True

        if args.xlabel is not None:
            plotParams['xlabel'] = args.xlabel

        if args.ylabel is not None:
            plotParams['ylabel'] = args.ylabel

        if args.zlabel is not None:
            plotParams['zlabel'] = args.zlabel

        #==============================================================================
        # update units
        if args.xu is not None:
            plotParams['xu'] = args.xu

        if args.yu is not None:
            plotParams['yu'] = args.yu

        if args.zu is not None:
            plotParams['zu'] = args.zu
        #==============================================================================
        if args.invert_xaxis is not None:
            if args.invert_xaxis == 'n' or args.invert_xaxis == 'no' or args.invert_xaxis == 'false':
                plotParams['invert_xaxis'] = False

            elif args.invert_xaxis == 'y' or args.invert_xaxis == 'yes' or args.invert_xaxis == 'true':
                plotParams['invert_xaxis'] = True
        #==============================================================================
        if args.invert_yaxis is not None:
            if args.invert_yaxis == 'n' or args.invert_yaxis == 'no' or args.invert_yaxis == 'false':
                plotParams['invert_yaxis'] = False

            elif args.invert_yaxis == 'y' or args.invert_yaxis == 'yes' or args.invert_yaxis == 'true':
                plotParams['invert_yaxis'] = True
        #==============================================================================
        if args.invert_zaxis is not None:
            if args.invert_zaxis == 'n' or args.invert_zaxis == 'no' or args.invert_zaxis == 'false':
                plotParams['invert_zaxis'] = False

            elif args.invert_zaxis == 'y' or args.invert_zaxis == 'yes' or args.invert_zaxis == 'true':
                plotParams['invert_zaxis'] = True
        #==============================================================================
        if args.show_scatterer is not None:
            if args.show_scatterer == 'n' or args.show_scatterer == 'no' or args.show_scatterer == 'false':
                plotParams['show_scatterer'] = False

            elif args.show_scatterer == 'y' or args.show_scatterer == 'yes' or args.show_scatterer == 'true':
                plotParams['show_scatterer'] = True
        #==============================================================================
        if args.show_sources is not None:
            if args.show_sources == 'n' or args.show_sources == 'no' or args.show_sources == 'false':
                plotParams['show_sources'] = False

            elif args.show_sources == 'y' or args.show_sources == 'yes' or args.show_sources == 'true':
                plotParams['show_sources'] = True
        #==============================================================================
        if args.show_receivers is not None:
            if args.show_receivers == 'n' or args.show_receivers == 'no' or args.show_receivers == 'false':
                plotParams['show_receivers'] = False

            elif args.show_receivers == 'y' or args.show_receivers == 'yes' or args.show_receivers == 'true':
                plotParams['show_receivers'] = True

    pickle.dump(plotParams, open('plotParams.pkl', 'wb'),
                pickle.HIGHEST_PROTOCOL)

    #==============================================================================
    # load the shape of the scatterer and source/receiver locations
    datadir = np.load('datadir.npz')
    receiverPoints = np.load(str(datadir['receivers']))

    if 'sources' in datadir:
        sourcePoints = np.load(str(datadir['sources']))
    else:
        sourcePoints = None

    if 'scatterer' in datadir and plotParams['show_scatterer']:
        scatterer = np.load(str(datadir['scatterer']))
    else:
        scatterer = None
        if plotParams['show_scatterer']:
            print(
                textwrap.dedent('''
                  Warning: Attempted to load the file containing the scatterer coordinates,
                  but no such file exists. If a file exists containing the scatterer
                  points, run:
                      
                      'vzdata --path=<path/to/data/>'
                      
                  and specify the file containing the scatterer points when prompted. Otherwise,
                  specify 'no' when asked if a file containing the scatterer points exists.
                  '''))

    if Path('window.npz').exists():
        windowDict = np.load('window.npz')

        # Apply the receiver window
        rstart = windowDict['rstart']
        rstop = windowDict['rstop']
        rstep = windowDict['rstep']

        rinterval = np.arange(rstart, rstop, rstep)
        receiverPoints = receiverPoints[rinterval, :]

        if sourcePoints is not None:
            # Apply the source window
            sstart = windowDict['sstart']
            sstop = windowDict['sstop']
            sstep = windowDict['sstep']

            sinterval = np.arange(sstart, sstop, sstep)
            sourcePoints = sourcePoints[sinterval, :]

    #==============================================================================
    if Path('imageNFE.npz').exists() and not Path('imageLSE.npz').exists():
        if args.lse:
            sys.exit(
                textwrap.dedent('''
                    PlotError: User requested to plot an image obtained by solving
                    the Lippmann-Schwinger equation (LSE), but no such image exists.
                    '''))

        # plot the image obtained by solving the near-field equaiton (NFE)
        Dict = np.load('imageNFE.npz')
        X = Dict['X']
        Y = Dict['Y']
        if 'Z' in Dict:
            Z = Dict['Z']
        else:
            Z = None
        if 'tau' in Dict:
            tau = Dict['tau']
            Ntau = len(tau)
        else:
            tau = None
        alpha = Dict['alpha']
        flag = 'NFE'
        fig1, ax1, *otherImages = plotImage(Dict, plotParams, flag,
                                            args.spacetime, args.movie)
        if otherImages:
            if len(otherImages) == 2:
                fig2, ax2 = otherImages
            elif len(otherImages) == 4:
                fig2, ax2, fig3, ax3 = otherImages

    elif not Path('imageNFE.npz').exists() and Path('imageLSE.npz').exists():
        if args.nfe:
            sys.exit(
                textwrap.dedent('''
                    PlotError: User requested to plot an image obtained by solving
                    the near-field equation (NFE), but no such image exists.
                    '''))

        # plot the image obtained by solving the Lippmann-Schwinger equation (LSE)
        Dict = np.load('imageLSE.npz')
        flag = 'LSE'
        fig1, ax1 = plotImage(Dict, plotParams, flag)

    elif Path('imageNFE.npz').exists() and Path('imageLSE.npz').exists():
        if args.nfe and not args.lse:
            # plot the image obtained by solving the near-field equaiton (NFE)
            Dict = np.load('imageNFE.npz')
            X = Dict['X']
            Y = Dict['Y']
            if 'Z' in Dict:
                Z = Dict['Z']
            else:
                Z = None
            if 'tau' in Dict:
                tau = Dict['tau']
                Ntau = len(tau)
            else:
                tau = None
            alpha = Dict['alpha']
            flag = 'NFE'
            fig1, ax1, *otherImages = plotImage(Dict, plotParams, flag,
                                                args.spacetime, args.movie)
            if otherImages:
                if len(otherImages) == 2:
                    fig2, ax2 = otherImages
                elif len(otherImages) == 4:
                    fig2, ax2, fig3, ax3 = otherImages

        elif not args.nfe and args.lse:
            # plot the image obtained by solving the Lippmann-Schwinger equation (LSE)
            Dict = np.load('imageLSE.npz')
            flag = 'LSE'
            fig1, ax1 = plotImage(Dict, plotParams, flag)

        elif args.nfe and args.lse:
            sys.exit(
                textwrap.dedent('''
                    PlotError: Please specify only one of the arguments \'--nfe\' or \'--lse\' to
                    view the corresponding image.'''))

        else:
            sys.exit(
                textwrap.dedent('''
                    Images obtained by solving both NFE and LSE are available. Enter:
                        
                        vzimage --nfe
                        
                    to view the image obtained by solving NFE or
                    
                        vzimage --lse
                        
                    to view the image obtained by solving LSE.
                    '''))

    else:
        flag = ''
        if args.nfe:
            print(
                'Warning: An image obtained by solving the near-field equation (NFE) does not exist.'
            )

        elif args.lse:
            print(
                'Warning: An image obtained by solving the Lippmann-Schwinger equation (LSE) does not exist.'
            )

        elif args.nfe and args.lse:
            print(
                textwrap.dedent('''
                    Warning: An image has not yet been obtained by solving either the
                    near-field equation (NFE) or the Lippmann-Schwinger equation (LSE).
                    '''))

    try:
        ax1
    except NameError:
        fig1, ax1 = setFigure(num_axes=1,
                              mode=plotParams['view_mode'],
                              ax1_dim=receiverPoints.shape[1])

    plotMap(ax1, None, receiverPoints, sourcePoints, scatterer, 'data',
            plotParams)

    #==============================================================================

    pltformat = plotParams['pltformat']
    fig1.savefig('image' + flag + '.' + pltformat,
                 format=pltformat,
                 bbox_inches='tight',
                 facecolor=fig1.get_facecolor(),
                 transparent=True)

    if flag == 'NFE' and args.spacetime:
        remove_keymap_conflicts({'left', 'right', 'up', 'down', 'save'})
        if Z is None:
            try:
                fig3.savefig('spacetime' + flag + '.' + pltformat,
                             format=pltformat,
                             bbox_inches='tight',
                             facecolor=fig3.get_facecolor(),
                             transparent=True)
                fig2.canvas.mpl_connect(
                    'key_press_event', lambda event: process_key_images(
                        event, plotParams, alpha, X, Y, Z, Ntau, tau))
            except NameError:
                print(
                    '\nSpace-time reconstruction is not available for a single sampling point in time.\n'
                )
        else:
            try:
                fig2.savefig('spacetime' + flag + '.' + pltformat,
                             format=pltformat,
                             bbox_inches='tight',
                             facecolor=fig2.get_facecolor(),
                             transparent=True)
                fig2.canvas.mpl_connect(
                    'key_press_event', lambda event: process_key_images(
                        event, plotParams, alpha, X, Y, Z, Ntau, tau))
            except NameError:
                print(
                    '\nSpace-time reconstruction is not available for a single sampling point in time.\n'
                )

    plt.show()
Exemple #3
0
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']))
    recordingTimes = np.load(str(datadir['recordingTimes']))
    dt = recordingTimes[1] - recordingTimes[0]

    if 'scatterer' in datadir:
        scatterer = np.load(str(datadir['scatterer']))
    else:
        scatterer = None

    if Path('window.npz').exists():
        windowDict = np.load('window.npz')

        # Apply the receiver window
        rstart = windowDict['rstart']
        rstop = windowDict['rstop']
        rstep = windowDict['rstep']

        # Apply the time window
        tstart = windowDict['tstart']
        tstop = windowDict['tstop']
        tstep = windowDict['tstep']

        # Convert time window parameters to corresponding array indices
        Tstart = int(round(tstart / dt))
        Tstop = int(round(tstop / dt))

    else:
        rstart = 0
        rstop = receiverPoints.shape[0]
        rstep = 1

        tstart = recordingTimes[0]
        tstop = recordingTimes[-1]

        Tstart = 0
        Tstop = len(recordingTimes)
        tstep = 1

    rinterval = np.arange(rstart, rstop, rstep)
    receiverPoints = receiverPoints[rinterval, :]

    tinterval = np.arange(Tstart, Tstop, tstep)

    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'
        if Path('noisyData.npz').exists():
            userResponded = False
            print(
                textwrap.dedent('''
                  Detected that band-limited noise has been added to the data array.
                  Would you like to plot the noisy data? ([y]/n)
                    
                  Enter 'q/quit' exit the program.
                  '''))
            while userResponded == False:
                answer = input('Action: ')
                if answer == '' or answer == 'y' or answer == 'yes':
                    print('Proceeding with plot of noisy data...')
                    # read in the noisy data array
                    X = np.load('noisyData.npz')['noisyData']
                    userResponded = True
                elif answer == 'n' or answer == 'no':
                    print('Proceeding with plot of noise-free data...')
                    # read in the recorded data array
                    X = np.load(str(datadir['recordedData']))
                    userResponded = True
                elif answer == 'q' or answer == 'quit':
                    sys.exit('Exiting program.\n')
                else:
                    print(
                        'Invalid response. Please enter \'y/yes\', \'n\no\', or \'q/quit\'.'
                    )

        else:
            # read in the recorded data array
            X = np.load(str(datadir['recordedData']))

        time = recordingTimes
        if 'sources' in datadir:
            sourcePoints = np.load(str(datadir['sources']))
        else:
            sourcePoints = None
        X = X[rinterval, :, :]

    elif args.data:
        # load the 3D data array into variable 'X'
        # X[receiver, time, source]
        wiggleType = 'data'
        if Path('noisyData.npz').exists():
            userResponded = False
            print(
                textwrap.dedent('''
                  Detected that band-limited noise has been added to the data array.
                  Would you like to plot the noisy data? ([y]/n)
                    
                  Enter 'q/quit' exit the program.
                  '''))
            while userResponded == False:
                answer = input('Action: ')
                if answer == '' or answer == 'y' or answer == 'yes':
                    print('Proceeding with plot of noisy data...')
                    # read in the noisy data array
                    X = np.load('noisyData.npz')['noisyData']
                    userResponded = True
                elif answer == 'n' or answer == 'no':
                    print('Proceeding with plot of noise-free data...')
                    # read in the recorded data array
                    X = np.load(str(datadir['recordedData']))
                    userResponded = True
                elif answer == 'q' or answer == 'quit':
                    sys.exit('Exiting program.\n')
                else:
                    print(
                        'Invalid response. Please enter \'y/yes\', \'n\no\', or \'q/quit\'.'
                    )

        else:
            # read in the recorded data array
            X = np.load(str(datadir['recordedData']))

        time = recordingTimes
        if 'sources' in datadir:
            sourcePoints = np.load(str(datadir['sources']))
        else:
            sourcePoints = None
        X = X[rinterval, :, :]

    elif args.testfunc:
        wiggleType = 'testfunc'
        if 'testFuncs' in datadir and not Path('VZTestFuncs.npz').exists():
            X = np.load(str(datadir['testFuncs']))
            time = np.load(str(datadir['convolutionTimes']))
            sourcePoints = np.load(str(datadir['samplingPoints']))
            X = X[rinterval, :, :]

        elif not 'testFuncs' in datadir and Path('VZTestFuncs.npz').exists():
            print(
                '\nDetected that free-space test functions have already been computed...'
            )
            print(
                'Checking consistency with current space-time sampling grid...'
            )
            TFDict = np.load('VZTestFuncs.npz')

            samplingGrid = np.load('samplingGrid.npz')
            x = samplingGrid['x']
            y = samplingGrid['y']
            if 'z' in samplingGrid:
                z = samplingGrid['z']
            else:
                z = None
            tau = samplingGrid['tau']

            pulse = lambda t: pulseFun.pulse(t)
            velocity = pulseFun.velocity
            peakFreq = pulseFun.peakFreq
            peakTime = pulseFun.peakTime

            # set up the convolution times based on the length of the recording time interval
            time = recordingTimes[tinterval]
            T = time[-1] - time[0]
            time = np.linspace(-T, T, 2 * len(time) - 1)
            if samplingIsCurrent(TFDict, receiverPoints, time, velocity, tau,
                                 x, y, z, peakFreq, peakTime):
                print('Moving forward to plot test functions...')
                X = TFDict['TFarray']
                sourcePoints = TFDict['samplingPoints']

            else:
                if tau[0] != 0:
                    tu = plotParams['tu']
                    if tu != '':
                        print(
                            'Recomputing test functions for focusing time %0.2f %s...'
                            % (tau[0], tu))
                    else:
                        print(
                            'Recomputing test functions for focusing time %0.2f...'
                            % (tau[0]))
                    X, sourcePoints = sampleSpace(receiverPoints,
                                                  time - tau[0], velocity, x,
                                                  y, z, pulse)
                else:
                    print('Recomputing test functions...')
                    X, sourcePoints = sampleSpace(receiverPoints, time,
                                                  velocity, x, y, z, pulse)

                if z is None:
                    np.savez('VZTestFuncs.npz',
                             TFarray=X,
                             time=time,
                             receivers=receiverPoints,
                             peakFreq=peakFreq,
                             peakTime=peakTime,
                             velocity=velocity,
                             x=x,
                             y=y,
                             tau=tau,
                             samplingPoints=sourcePoints)
                else:
                    np.savez('VZTestFuncs.npz',
                             TFarray=X,
                             time=time,
                             receivers=receiverPoints,
                             peakFreq=peakFreq,
                             peakTime=peakTime,
                             velocity=velocity,
                             x=x,
                             y=y,
                             z=z,
                             tau=tau,
                             samplingPoints=sourcePoints)

        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 = np.load(str(datadir['testFuncs']))
                    time = np.load(str(datadir['convolutionTimes']))
                    sourcePoints = np.load(str(datadir['samplingPoints']))
                    X = X[rinterval, :, :]
                    userResponded = True
                    break

                elif answer == '2':
                    print(
                        '\nDetected that free-space test functions have already been computed...'
                    )
                    print(
                        'Checking consistency with current spatial sampling grid...'
                    )
                    TFDict = np.load('VZTestFuncs.npz')

                    samplingGrid = np.load('samplingGrid.npz')
                    x = samplingGrid['x']
                    y = samplingGrid['y']
                    if 'z' in samplingGrid:
                        z = samplingGrid['z']
                    else:
                        z = None
                    tau = samplingGrid['tau']

                    pulse = lambda t: pulseFun.pulse(t)
                    velocity = pulseFun.velocity
                    peakFreq = pulseFun.peakFreq
                    peakTime = pulseFun.peakTime

                    # set up the convolution times based on the length of the recording time interval
                    time = recordingTimes[tinterval]
                    T = time[-1] - time[0]
                    time = np.linspace(-T, T, 2 * len(time) - 1)
                    if samplingIsCurrent(TFDict, receiverPoints, time,
                                         velocity, tau, x, y, z, peakFreq,
                                         peakTime):
                        print('Moving forward to plot test functions...')
                        X = TFDict['TFarray']
                        sourcePoints = TFDict['samplingPoints']

                    else:
                        if tau[0] != 0:
                            tu = plotParams['tu']
                            if tu != '':
                                print(
                                    'Recomputing test functions for focusing time %0.2f %s...'
                                    % (tau[0], tu))
                            else:
                                print(
                                    'Recomputing test functions for focusing time %0.2f...'
                                    % (tau[0]))
                            X, sourcePoints = sampleSpace(
                                receiverPoints, time - tau[0], velocity, x, y,
                                z, pulse)
                        else:
                            print('Recomputing test functions...')
                            X, sourcePoints = sampleSpace(
                                receiverPoints, time, velocity, x, y, z, pulse)

                        if z is None:
                            np.savez('VZTestFuncs.npz',
                                     TFarray=X,
                                     time=time,
                                     receivers=receiverPoints,
                                     peakFreq=peakFreq,
                                     peakTime=peakTime,
                                     velocity=velocity,
                                     x=x,
                                     y=y,
                                     tau=tau,
                                     samplingPoints=sourcePoints)
                        else:
                            np.savez('VZTestFuncs.npz',
                                     TFarray=X,
                                     time=time,
                                     receivers=receiverPoints,
                                     peakFreq=peakFreq,
                                     peakTime=peakTime,
                                     velocity=velocity,
                                     x=x,
                                     y=y,
                                     z=z,
                                     tau=tau,
                                     samplingPoints=sourcePoints)

                    userResponded = True

                elif answer == 'q' or answer == 'quit':
                    sys.exit('Exiting program.')

                else:
                    print(
                        'Invalid response. Please enter \'1\', \'2\', or \'q/quit\'.'
                    )

        else:
            print(
                '\nComputing free-space test functions for the current space-time sampling grid...'
            )
            # set up the convolution times based on the length of the recording time interval
            time = recordingTimes[tinterval]
            T = time[-1] - time[0]
            time = np.linspace(-T, T, 2 * len(time) - 1)

            samplingGrid = np.load('samplingGrid.npz')
            x = samplingGrid['x']
            y = samplingGrid['y']
            if 'z' in samplingGrid:
                z = samplingGrid['z']
            else:
                z = None
            tau = samplingGrid['tau']

            pulse = lambda t: pulseFun.pulse(t)
            velocity = pulseFun.velocity
            peakFreq = pulseFun.peakFreq
            peakTime = pulseFun.peakTime

            if tau[0] != 0:
                tu = plotParams['tu']
                if tu != '':
                    print(
                        'Computing test functions for focusing time %0.2f %s...'
                        % (tau[0], tu))
                else:
                    print(
                        'Computing test functions for focusing time %0.2f...' %
                        (tau[0]))
                X, sourcePoints = sampleSpace(receiverPoints, time - tau[0],
                                              velocity, x, y, z, pulse)
            else:
                X, sourcePoints = sampleSpace(receiverPoints, time, velocity,
                                              x, y, z, pulse)

            if z is None:
                np.savez('VZTestFuncs.npz',
                         TFarray=X,
                         time=time,
                         receivers=receiverPoints,
                         peakFreq=peakFreq,
                         peakTime=peakTime,
                         velocity=velocity,
                         x=x,
                         y=y,
                         tau=tau,
                         samplingPoints=sourcePoints)
            else:
                np.savez('VZTestFuncs.npz',
                         TFarray=X,
                         time=time,
                         receivers=receiverPoints,
                         peakFreq=peakFreq,
                         peakTime=peakTime,
                         velocity=velocity,
                         x=x,
                         y=y,
                         z=z,
                         tau=tau,
                         samplingPoints=sourcePoints)

    #==============================================================================
    if Path('window.npz').exists() and wiggleType == 'data':
        t0 = tstart
        tf = tstop

        # Apply the source window
        sstart = windowDict['sstart']
        sstop = windowDict['sstop']
        sstep = windowDict['sstep']

    else:
        t0 = time[0]
        tf = time[-1]

        sstart = 0
        sstop = X.shape[2]
        sstep = 1

    sinterval = np.arange(sstart, sstop, sstep)

    X = X[:, :, sinterval]
    if sourcePoints is not None:
        sourcePoints = sourcePoints[sinterval, :]

    # 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
    rstart += 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, t0, tf, rstart, 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, t0, tf, rstart, 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, t0, tf, rstart, rinterval,
                    receiverPoints, title, wiggleType, plotParams)
        plt.tight_layout()
        fig.canvas.mpl_connect(
            'key_press_event', lambda event: process_key_waves(
                event, time, t0, tf, rstart, rinterval, sinterval,
                receiverPoints, sourcePoints, Ns, scatterer, args.map,
                wiggleType, plotParams))

    plt.show()
Exemple #4
0
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()
Exemple #5
0
def cli():
    parser = argparse.ArgumentParser()
    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()

    #==============================================================================

    def process_key_picard(event, tstart, tstop, rinterval, receiverPoints,
                           scatterer, args, recordingTimes):
        if args.map:
            fig = event.canvas.figure
            ax1 = fig.axes[0]
            ax2 = fig.axes[1]
            if event.key == 'left' or event.key == 'down':
                previous_slice(ax1, tstart, tstop, rinterval, args,
                               recordingTimes, receiverPoints)
                previous_source(ax2, tstart, tstop, rinterval, args,
                                recordingTimes, receiverPoints)
            elif event.key == 'right' or event.key == 'up':
                next_slice(ax1, tstart, tstop, rinterval, args, recordingTimes,
                           receiverPoints)
                next_source(ax1, tstart, tstop, rinterval, args,
                            recordingTimes, receiverPoints)
        else:
            fig = event.canvas.figure
            ax = fig.axes[0]
            if event.key == 'left' or event.key == 'down':
                previous_slice(ax, tstart, tstop, rinterval, args,
                               recordingTimes, receiverPoints)
            elif event.key == 'right' or event.key == 'up':
                next_slice(ax, tstart, tstop, rinterval, args, recordingTimes,
                           receiverPoints)
        fig.canvas.draw()

    #==============================================================================
    try:
        s = np.load('singularValues.npy')
        U = np.load('leftVectors.npy')
    except FileNotFoundError:
        s = None
        U = None

    if any(v is None for v in [s, U]):
        sys.exit(
            textwrap.dedent('''
                A singular-value decomposition needs to be computed before any
                spectral analysis can be performed. Enter:
                    
                    vzsvd --help
                    
                from the command line for more information on how to compute
                a singular-value decomposition.
                '''))

    try:
        TFDict = np.load('VZTestFuncs.npz')
    except FileNotFoundError:
        TFDict = None

    if TFDict is None:
        sys.exit(textwrap.dedent('''
                '''))

    k = s.size
    s = np.reshape(s, (k, 1))
    # Compute coefficients for Picard plot
    TFarray = TFDict['TFarray']
    TF = TFarray[:, :, 0, 0]
    Nr, Nt = TF.shape
    b = np.reshape(TF, (Nt * Nr, 1))
    c = np.abs(U.T @ b)
    d = np.divide(c, s)

    #==============================================================================
    datadir = np.load('datadir.npz')
    receiverPoints = np.load(str(datadir['receivers']))
    sourcePoints = np.load(str(datadir['sources']))
    if 'scatterer' in datadir:
        scatterer = np.load(str(datadir['scatterer']))
    else:
        scatterer = None

    if Path('window.npz').exists():
        windowDict = np.load('window.npz')

        # Set the receiver window for receiverPoints
        rstart = windowDict['rstart']
        rstop = windowDict['rstop']
        rstep = windowDict['rstep']

        # Set the source window for sourcePoints
        sstart = windowDict['sstart']
        sstop = windowDict['sstop']
        sstep = windowDict['sstep']

    else:

        rstart = 0
        rstop = Nr
        rstep = 1

        sstart = 0
        sstop = sourcePoints.shape[0]
        sstep = 1

    # pltrstart is used to plot the correct receivers for
    # the simulated test function computed by Vezda
    pltrstart = rstart
    pltsstart = sstart

    rinterval = np.arange(rstart, rstop, rstep)
    receiverPoints = receiverPoints[rinterval, :]

    sinterval = np.arange(sstart, sstop, sstep)
    sourcePoints = sourcePoints[sinterval, :]

    #==============================================================================
    remove_keymap_conflicts({'left', 'right', 'up', 'down', 'save'})

    if Path('plotParams.pkl').exists():
        plotParams = pickle.load(open('plotParams.pkl', 'rb'))
    else:
        plotParams = default_params()

    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'])

    #        ax1.index = k // 2
    #        PicardPlot(ax1, s, c, d)

    #        if receiverPoints.shape[1] == 2:
    #            ax2 = fig.add_subplot(122)
    #        elif receiverPoints.shape[1] == 3:
    #            ax2 = fig.add_subplot(122, projection='3d')

    #        ax2.index = ax1.index
    #        map_plot(ax2, ax2.index, args, rinterval, receiverPoints, sourcePoints, scatterer)
    #        plt.tight_layout()
    #        #fig.canvas.mpl_connect('key_press_event', lambda event: process_key(event, tstart, tstop, rinterval,
    #        #                                                                    receiverPoints, sourcePoints, scatterer,
    #        #                                                                    args, recordingTimes))
    ax.index = k // 2
    PicardPlot(ax, s, c, d)
    plt.tight_layout()
    fig.savefig('Picard.' + args.format,
                format=args.format,
                bbox_inches='tight')
    plt.show()
Exemple #6
0
def cli():
    parser = argparse.ArgumentParser()
    parser.add_argument(
        '--nfo',
        action='store_true',
        help='''Compute or plot the singular-value decomposition of the
                        near-field operator (NFO).''')
    parser.add_argument(
        '--lso',
        action='store_true',
        help='''Compute or plot the singular-value decomposition of the
                        Lippmann-Schwinger operator (LSO).''')
    parser.add_argument(
        '--numVals',
        '-k',
        type=int,
        help='''Specify the number of singular values/vectors to compute.
                        Must a positive integer between 1 and the order of the square
                        input matrix.''')
    parser.add_argument(
        '--domain',
        '-d',
        type=str,
        choices=['time', 'freq'],
        help='''Specify whether to compute the singular-value decomposition in
                        the time domain or frequency domain. Default is set to frequency domain
                        for faster, more accurate performance.''')
    parser.add_argument(
        '--plot',
        '-p',
        action='store_true',
        help='''Plot the computed singular values and vectors.''')
    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()

    if args.nfo and not args.lso:
        operatorType = 'near-field operator'
        inputType = 'data'
        try:
            SVD = np.load('NFO_SVD.npz')
            s = SVD['s']
            Uh = SVD['Uh']
            V = SVD['V']
            domain = SVD['domain']

        except FileNotFoundError:
            s, Uh, V, domain = None, None, None, 'freq'

    elif not args.nfo and args.lso:
        operatorType = 'Lippmann-Schwinger operator'
        inputType = 'test functions'
        try:
            SVD = np.load('LSO_SVD.npz')
            s = SVD['s']
            Uh = SVD['Uh']
            V = SVD['V']
            domain = SVD['domain']

        except FileNotFoundError:
            s, Uh, V, domain = None, None, None, 'freq'

    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 compute or plot a singular-value decomposition?
                Enter:
                    
                    vzsvd --nfo
                
                for the near-field operator or
                
                    vzsvd --lso
                    
                for the Lippmann-Schwinger operator.
                '''))

    #==============================================================================
    # if an SVD already exists...
    if any(v is not None for v in
           [s, Uh, V]) and args.numVals is not None and args.plot is True:
        if args.numVals >= 1 and args.numVals == len(s):
            userResponded = False
            print(
                textwrap.dedent('''
                 A singular-value decomposition of the {s} for {n} values/vectors already exists. 
                 What would you like to do?
                 
                 Enter '1' to specify a new number of values/vectors to compute. (Default)
                 Enter '2' to recompute a singular-value decomposition for {n} values/vectors.
                 Enter 'q/quit' to exit.
                 '''.format(s=operatorType, n=args.numVals)))
            while userResponded == False:
                answer = input('Action: ')
                if answer == '' or answer == '1':
                    k = int(
                        input(
                            'Please specify the number of singular values/vectors to compute: '
                        ))
                    if isValid(k):
                        print('Proceeding with numVals = %s...' % (k))
                        userResponded = True
                        computeSVD = True
                        break
                    else:
                        break
                elif answer == '2':
                    k = args.numVals
                    print(
                        'Recomputing SVD of the %s for %s singular values/vectors...'
                        % (operatorType, k))
                    userResponded = True
                    computeSVD = True
                elif answer == 'q' or answer == 'quit':
                    sys.exit('Exiting program.\n')
                else:
                    print(
                        'Invalid response. Please enter \'1\', \'2\', or \'q/quit\'.'
                    )

        elif args.numVals >= 1 and args.numVals != len(s):
            k = args.numVals
            computeSVD = True

        elif args.numVals < 1:
            userResponded = False
            print(
                textwrap.dedent('''
                 ValueError: Argument '-k/--numVals' must be a positive integer 
                 between 1 and the order of the square input matrix. The parameter will
                 be set to the default value of 6.
                 What would you like to do?
                 
                 Enter '1' to specify a value of the parameter. (Default)
                 Enter '2' to proceed with the default value.
                 Enter 'q/quit' exit the program.
                 '''))
            while userResponded == False:
                answer = input('Action: ')
                if answer == '' or answer == '1':
                    k = int(
                        input(
                            'Please specify the number of singular values/vectors to compute: '
                        ))
                    if isValid(k):
                        print('Proceeding with numVals = %s...' % (k))
                        userResponded = True
                        computeSVD = True
                        break
                    else:
                        break
                elif answer == '2':
                    k = 6
                    print('Proceeding with the default value numVals = %s...' %
                          (k))
                    computeSVD = True
                    userResponded = True
                    break
                elif answer == 'q' or answer == 'quit':
                    sys.exit('Exiting program.\n')
                else:
                    print(
                        'Invalid response. Please enter \'1\', \'2\', or \'q/quit\'.'
                    )

    elif all(v is not None for v in
             [s, Uh, V]) and args.numVals is None and args.plot is True:
        computeSVD = False

    elif all(v is not None for v in
             [s, Uh, V]) and args.numVals is not None and args.plot is False:
        if args.numVals >= 1 and args.numVals == len(s):
            userResponded = False
            print(
                textwrap.dedent('''
                 A singular-value decomposition of the {s} for {n} values/vectors already exists. 
                 What would you like to do?
                 
                 Enter '1' to specify a new number of values/vectors to compute. (Default)
                 Enter '2' to recompute a singular-value decomposition for {n} values/vectors.
                 Enter 'q/quit' to exit.
                 '''.format(s=operatorType, n=args.numVals)))
            while userResponded == False:
                answer = input('Action: ')
                if answer == '' or answer == '1':
                    k = int(
                        input(
                            'Please specify the number of singular values/vectors to compute: '
                        ))
                    if isValid(k):
                        print('Proceeding with numVals = %s...' % (k))
                        userResponded = True
                        computeSVD = True
                        break
                    else:
                        break
                elif answer == '2':
                    k = args.numVals
                    print(
                        'Recomputing SVD of the %s for %s singular values/vectors...'
                        % (operatorType, k))
                    userResponded = True
                    computeSVD = True
                elif answer == 'q' or answer == 'quit':
                    sys.exit('Exiting program.\n')
                else:
                    print(
                        'Invalid response. Please enter \'1\', \'2\', or \'q/quit\'.'
                    )

        elif args.numVals >= 1 and args.numVals != len(s):
            k = args.numVals
            computeSVD = True

        elif args.numVals < 1:
            userResponded = False
            print(
                textwrap.dedent('''
                 ValueError: Argument '-k/--numVals' must be a positive integer 
                 between 1 and the order of the square input matrix. The parameter will
                 be set to the default value of 6.
                 What would you like to do?
                 
                 Enter '1' to specify a value of the parameter. (Default)
                 Enter '2' to proceed with the default value.
                 Enter 'q/quit' exit the program.
                 '''))
            while userResponded == False:
                answer = input('Action: ')
                if answer == '' or answer == '1':
                    k = int(
                        input(
                            'Please specify the number of singular values/vectors to compute: '
                        ))
                    if isValid(k):
                        print('Proceeding with numVals = %s...' % (k))
                        userResponded = True
                        computeSVD = True
                        break
                    else:
                        break
                elif answer == '2':
                    k = 6
                    print('Proceeding with the default value numVals = %s...' %
                          (k))
                    computeSVD = True
                    userResponded = True
                    break
                elif answer == 'q' or answer == 'quit':
                    sys.exit('Exiting program.\n')
                else:
                    print(
                        'Invalid response. Please enter \'1\', \'2\', or \'q/quit\'.'
                    )

    elif all(v is not None for v in
             [s, Uh, V]) and args.numVals is None and args.plot is False:
        sys.exit(
            textwrap.dedent('''
                No action specified. A singular-value decomposition of the %s
                for %s values/vectors already exists. Please specify at least one of '-k/--numVals'
                or '-p/--plot' arguments with 'vzsvd' command.
                ''' % (operatorType, len(s))))
    #==============================================================================
    # if an SVD does not already exist...
    elif any(v is None for v in
             [s, Uh, V]) and args.numVals is not None and args.plot is True:
        if args.numVals >= 1:
            computeSVD = True
            k = args.numVals

        elif args.numVals < 1:
            userResponded = False
            print(
                textwrap.dedent('''
                 ValueError: Argument '-k/--numVals' must be a positive integer 
                 between 1 and the order of the square input matrix. The parameter will
                 be set to the default value of 6.
                 What would you like to do?
                 
                 Enter '1' to specify a value of the parameter. (Default)
                 Enter '2' to proceed with the default value.
                 Enter 'q/quit' exit the program.
                 '''))
            while userResponded == False:
                answer = input('Action: ')
                if answer == '' or answer == '1':
                    k = int(
                        input(
                            'Please specify the number of singular values/vectors to compute: '
                        ))
                    if isValid(k):
                        print('Proceeding with numVals = %s...' % (k))
                        userResponded = True
                        computeSVD = True
                        break
                    else:
                        break
                elif answer == '2':
                    k = 6
                    print('Proceeding with the default value numVals = %s...' %
                          (k))
                    computeSVD = True
                    userResponded = True
                    break
                elif answer == 'q' or answer == 'quit':
                    sys.exit('Exiting program.\n')
                else:
                    print(
                        'Invalid response. Please enter \'1\', \'2\', or \'q/quit\'.'
                    )

    elif any(v is None for v in
             [s, Uh, V]) and args.numVals is None and args.plot is True:
        userResponded = False
        print(
            textwrap.dedent('''
             PlotError: A singular-value decomposition of the {s} does not exist. A plot will be
             generated after a singular-value decomposition has been computed.
             
             Enter '1' to specify a number of singular values/vectors to compute. (Default)
             Enter 'q/quit' to exit.
             '''.format(s=operatorType)))
        while userResponded == False:
            answer = input('Action: ')
            if answer == '' or answer == '1':
                k = int(
                    input(
                        'Please specify the number of singular values/vectors to compute: '
                    ))
                if isValid(k):
                    print('Proceeding with numVals = %s...' % (k))
                    userResponded = True
                    computeSVD = True
                    break
                else:
                    break
            elif answer == 'q' or answer == 'quit':
                sys.exit('Exiting program.\n')
            else:
                print('Invalid response. Please enter \'1\', or \'q/quit\'.')

    elif any(v is None for v in
             [s, Uh, V]) and args.numVals is not None and args.plot is False:
        if args.numVals >= 1:
            k = args.numVals
            computeSVD = True

        elif args.numVals < 1:
            userResponded = False
            print(
                textwrap.dedent('''
                 ValueError: Argument '-k/--numVals' must be a positive integer 
                 between 1 and the order of the square input matrix. The parameter will
                 be set to the default value of 6.
                 What would you like to do?
                 
                 Enter '1' to specify a value of the parameter. (Default)
                 Enter '2' to proceed with the default value.
                 Enter 'q/quit' exit the program.
                 '''))
            while userResponded == False:
                answer = input('Action: ')
                if answer == '' or answer == '1':
                    k = int(
                        input(
                            'Please specify the number of singular values/vectors to compute: '
                        ))
                    if isValid(k):
                        print('Proceeding with numVals = %s...' % (k))
                        userResponded = True
                        computeSVD = True
                        break
                    else:
                        break
                elif answer == '2':
                    k = 6
                    print('Proceeding with the default value numVals = %s...' %
                          (k))
                    computeSVD = True
                    userResponded = True
                    break
                elif answer == 'q' or answer == 'quit':
                    sys.exit('Exiting program.\n')
                else:
                    print(
                        'Invalid response. Please enter \'1\', \'2\', or \'q/quit\'.'
                    )

    elif any(v is None for v in
             [s, Uh, V]) and args.numVals is None and args.plot is False:
        sys.exit(
            textwrap.dedent('''
                Nothing to be done. A singular-value decomposition of the {s} does not exist.
                Please specify at least one of '-k/--numVals' or '-p/--plot'
                arguments with 'vzsvd' command.
                '''.format(s=operatorType)))
    #==============================================================================
    # Read in data files
    datadir = np.load('datadir.npz')
    receiverPoints = np.load(str(datadir['receivers']))
    recordingTimes = np.load(str(datadir['recordingTimes']))
    dt = recordingTimes[1] - recordingTimes[0]

    if Path('window.npz').exists():
        windowDict = np.load('window.npz')

        # Apply the receiver window
        rstart = windowDict['rstart']
        rstop = windowDict['rstop']
        rstep = windowDict['rstep']

        # Apply the time window
        tstart = windowDict['tstart']
        tstop = windowDict['tstop']
        tstep = windowDict['tstep']

        # Convert time window parameters to corresponding array indices
        Tstart = int(round(tstart / dt))
        Tstop = int(round(tstop / dt))

    else:
        rstart = 0
        rstop = receiverPoints.shape[0]
        rstep = 1

        tstart = recordingTimes[0]
        tstop = recordingTimes[-1]

        Tstart = 0
        Tstop = len(recordingTimes)
        tstep = 1

    # Apply the receiver window
    rinterval = np.arange(rstart, rstop, rstep)
    receiverPoints = receiverPoints[rinterval, :]

    # Apply the time window
    tinterval = np.arange(Tstart, Tstop, tstep)
    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 computeSVD:
        # get time units for printing time windows or time shifts
        tu = plotParams['tu']

        if args.nfo:

            if Path('noisyData.npz').exists():
                userResponded = False
                print(
                    textwrap.dedent('''
                      Detected that band-limited noise has been added to the data array.
                      Would you like to compute an SVD of the noisy data? ([y]/n)
                      
                      Enter 'q/quit' exit the program.
                      '''))
                while userResponded == False:
                    answer = input('Action: ')
                    if answer == '' or answer == 'y' or answer == 'yes':
                        print(
                            'Proceeding with singular-value decomposition using noisy data...'
                        )
                        # read in the noisy data array
                        X = np.load('noisyData.npz')['noisyData']
                        userResponded = True
                    elif answer == 'n' or answer == 'no':
                        print(
                            'Proceeding with singular-value decomposition using noise-free data...'
                        )
                        # read in the recorded data array
                        X = np.load(str(datadir['recordedData']))
                        userResponded = True
                    elif answer == 'q' or answer == 'quit':
                        sys.exit('Exiting program.\n')
                    else:
                        print(
                            'Invalid response. Please enter \'y/yes\', \'n\no\', or \'q/quit\'.'
                        )

            else:
                # read in the recorded data array
                X = np.load(str(datadir['recordedData']))

            if Path('window.npz').exists():
                print('Detected user-specified window:\n')

                # For display/printing purposes, count receivers with one-based
                # indexing. This amounts to incrementing the rstart parameter by 1
                print('window @ receivers : start =', rstart + 1)
                print('window @ receivers : stop =', rstop)
                print('window @ receivers : step =', rstep, '\n')

                if tu != '':
                    print('window @ time : start = %0.2f %s' % (tstart, tu))
                    print('window @ time : stop = %0.2f %s' % (tstop, tu))
                else:
                    print('window @ time : start =', tstart)
                    print('window @ time : stop =', tstop)
                print('window @ time : step =', tstep, '\n')

                # Apply the source window
                slabel = windowDict['slabel']
                sstart = windowDict['sstart']
                sstop = windowDict['sstop']
                sstep = windowDict['sstep']
                sinterval = np.arange(sstart, sstop, sstep)

                # For display/printing purposes, count recordings/sources with one-based
                # indexing. This amounts to incrementing the sstart parameter by 1
                print('window @ %s : start = %s' % (slabel, sstart + 1))
                print('window @ %s : stop = %s' % (slabel, sstop))
                print('window @ %s : step = %s\n' % (slabel, sstep))

                print('Applying window to data volume...')
                X = X[rinterval, :, :]
                X = X[:, tinterval, :]
                X = X[:, :, sinterval]
                Nr, Nt, Ns = X.shape

                # Apply tapered cosine (Tukey) window to time signals.
                # This ensures the fast fourier transform (FFT) used in
                # the definition of the matrix-vector product below is
                # acting on a function that is continuous at its edges.

                peakFreq = pulseFun.peakFreq
                # Np : Number of samples in the dominant period T = 1 / peakFreq
                Np = int(round(1 / (tstep * dt * peakFreq)))
                # alpha is set to taper over 6 of the dominant period of the
                # pulse function (3 periods from each end of the signal)
                alpha = 6 * Np / Nt
                print('Tapering time signals with Tukey window: %d' %
                      (int(round(alpha * 100))) + '%')
                TukeyWindow = tukey(Nt, alpha)
                X *= TukeyWindow[None, :, None]

            else:
                Nr, Nt, Ns = X.shape

        elif args.lso:

            if Path('samplingGrid.npz').exists():
                samplingGrid = np.load('samplingGrid.npz')
                x = samplingGrid['x']
                y = samplingGrid['y']
                tau = samplingGrid['tau']
                if 'z' in samplingGrid:
                    z = samplingGrid['z']
                else:
                    z = None

            else:
                sys.exit(
                    textwrap.dedent('''
                        A sampling grid needs to be set up before computing a
                        singular-value decomposition of the %s.
                        Enter:
                            
                            vzgrid --help
                            
                        from the command-line for more information on how to set up a
                        sampling grid.
                        ''' % (operatorType)))

            pulse = lambda t: pulseFun.pulse(t)
            velocity = pulseFun.velocity
            peakFreq = pulseFun.peakFreq
            peakTime = pulseFun.peakTime

            if Path('VZTestFuncs.npz').exists():
                print(
                    '\nDetected that free-space test functions have already been computed...'
                )
                print(
                    'Checking consistency with current space-time sampling grid...'
                )
                TFDict = np.load('VZTestFuncs.npz')

                if samplingIsCurrent(TFDict, receiverPoints, recordingTimes,
                                     velocity, tau, x, y, z, peakFreq,
                                     peakTime):
                    X = TFDict['TFarray']
                    sourcePoints = TFDict['samplingPoints']
                    print('Moving forward to SVD...')

                else:
                    print('Recomputing test functions...')
                    # set up the convolution times based on length of recording time interval
                    T = recordingTimes[-1] - recordingTimes[0]
                    convolutionTimes = np.linspace(-T, T,
                                                   2 * len(recordingTimes) - 1)

                    if tau[0] != 0:
                        if tu != '':
                            print(
                                'Recomputing test functions for focusing time %0.2f %s...'
                                % (tau[0], tu))
                        else:
                            print(
                                'Recomputing test functions for focusing time %0.2f...'
                                % (tau[0]))
                        X, sourcePoints = sampleSpace(
                            receiverPoints, convolutionTimes - tau[0],
                            velocity, x, y, z, pulse)
                    else:
                        X, sourcePoints = sampleSpace(receiverPoints,
                                                      convolutionTimes,
                                                      velocity, x, y, z, pulse)

                    if z is None:
                        np.savez('VZTestFuncs.npz',
                                 TFarray=X,
                                 time=recordingTimes,
                                 receivers=receiverPoints,
                                 peakFreq=peakFreq,
                                 peakTime=peakTime,
                                 velocity=velocity,
                                 x=x,
                                 y=y,
                                 tau=tau,
                                 samplingPoints=sourcePoints)
                    else:
                        np.savez('VZTestFuncs.npz',
                                 TFarray=X,
                                 time=recordingTimes,
                                 receivers=receiverPoints,
                                 peakFreq=peakFreq,
                                 peakTime=peakTime,
                                 velocity=velocity,
                                 x=x,
                                 y=y,
                                 z=z,
                                 tau=tau,
                                 samplingPoints=sourcePoints)

            else:
                print(
                    '\nComputing free-space test functions for the current space-time sampling grid...'
                )
                if tau[0] != 0:
                    if tu != '':
                        print(
                            'Computing test functions for focusing time %0.2f %s...'
                            % (tau[0], tu))
                    else:
                        print(
                            'Computing test functions for focusing time %0.2f...'
                            % (tau[0]))
                    X, sourcePoints = sampleSpace(receiverPoints,
                                                  recordingTimes - tau[0],
                                                  velocity, x, y, z, pulse)
                else:
                    X, sourcePoints = sampleSpace(receiverPoints,
                                                  recordingTimes, velocity, x,
                                                  y, z, pulse)

                if z is None:
                    np.savez('VZTestFuncs.npz',
                             TFarray=X,
                             time=recordingTimes,
                             receivers=receiverPoints,
                             peakFreq=peakFreq,
                             peakTime=peakTime,
                             velocity=velocity,
                             x=x,
                             y=y,
                             tau=tau,
                             samplingPoints=sourcePoints)
                else:
                    np.savez('VZTestFuncs.npz',
                             TFarray=X,
                             time=recordingTimes,
                             receivers=receiverPoints,
                             peakFreq=peakFreq,
                             peakTime=peakTime,
                             velocity=velocity,
                             x=x,
                             y=y,
                             z=z,
                             tau=tau,
                             samplingPoints=sourcePoints)

            Nr, Nt, Ns = X.shape

        #==============================================================================
        if args.domain is not None:
            domain = args.domain

        if domain == 'freq':
            # Transform convolutional operator into frequency domain and bandpass for efficient SVD
            print('Transforming %s to the frequency domain...' % (inputType))
            N = nextPow2(2 * Nt)
            X = np.fft.rfft(X, n=N, axis=1)

            if plotParams['fmax'] is None:
                freqs = np.fft.rfftfreq(N, tstep * dt)
                plotParams['fmax'] = np.max(freqs)

            # Apply the frequency window
            fmin = plotParams['fmin']
            fmax = plotParams['fmax']
            fu = plotParams['fu']  # frequency units (e.g., Hz)

            if fu != '':
                print('Applying bandpass filter: [%0.2f %s, %0.2f %s]' %
                      (fmin, fu, fmax, fu))
            else:
                print('Applying bandpass filter: [%0.2f, %0.2f]' %
                      (fmin, fmax))

            df = 1.0 / (N * tstep * dt)
            startIndex = int(round(fmin / df))
            stopIndex = int(round(fmax / df))

            finterval = np.arange(startIndex, stopIndex, 1)
            X = X[:, finterval, :]

        #==============================================================================
        # Compute the k largest singular values (which='LM') of the operator A
        # Singular values are elements of the vector 's'
        # Left singular vectors are columns of 'U'
        # Right singular vectors are columns of 'V'

        A = asConvolutionalOperator(X)

        if k == 1:
            print('Computing SVD of the %s for 1 singular value/vector...' %
                  (operatorType))
        else:
            print('Computing SVD of the %s for %s singular values/vectors...' %
                  (operatorType, k))
        startTime = time.time()
        U, s, Vh = svds(A, k, which='LM')
        endTime = time.time()
        print('Elapsed time:', humanReadable(endTime - startTime), '\n')

        # sort the singular values and corresponding vectors in descending order
        # (i.e., largest to smallest)
        index = s.argsort()[::-1]
        s = s[index]
        Uh = U[:, index].conj().T
        V = Vh[index, :].conj().T

        # Write binary output with numpy
        if args.nfo:
            np.savez('NFO_SVD.npz', s=s, Uh=Uh, V=V, domain=domain)
        elif args.lso:
            np.savez('LSO_SVD.npz', s=s, Uh=Uh, V=V, domain=domain)

    #==============================================================================
    if args.plot and all(v is not None for v in [s, Uh, V]):

        Nr = receiverPoints.shape[0]
        Nt = len(recordingTimes)

        try:
            k
        except NameError:
            k = len(s)

        if args.domain is not None and domain != args.domain:
            if domain == 'freq':
                s1 = 'time'
                s2 = 'frequency'
            else:
                s1 = 'frequency'
                s2 = 'time'
            sys.exit(
                textwrap.dedent('''
                    Error: Attempted to plot the singular-value decomposition in the %s
                    domain, but the decomposition was computed in the %s domain.
                    ''' % (s1, s2)))

        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]
            fmax = freqs[-1]

            M = len(freqs)
            Ns = int(V.shape[0] / M)
            U = np.reshape(Uh.conj().T, (Nr, M, k))
            V = np.reshape(V, (Ns, M, k))

        else:  # domain == 'time'
            M = 2 * Nt - 1
            Ns = int(V.shape[0] / M)
            U = np.reshape(Uh.T, (Nr, M, k))
            V = np.reshape(V, (Ns, M, k))
            T = recordingTimes[-1] - recordingTimes[0]
            times = np.linspace(-T, T, M)

        if args.nfo:  # Near-field operator
            try:
                sinterval
            except NameError:
                if Path('window.npz').exists():
                    sstart = windowDict['sstart']
                    sstop = windowDict['sstop']
                    sstep = windowDict['sstep']
                else:
                    sstart = 0
                    sstop = Ns
                    sstep = 1

                sinterval = np.arange(sstart, sstop, sstep)

            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.
            try:
                sourcePoints
            except NameError:
                if 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.
                            '''))

            sstart = 0
            sstop = sourcePoints.shape[0]
            sstep = 1
            sinterval = np.arange(sstart, sstop, sstep)

        # 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
        rstart += 1
        sstart += 1

        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,
                                          fmin, fmax, rstart, 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, fmin, fmax, rstart, sstart, 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, fmin, fmax, sstart,
                                           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, fmin, fmax, rstart, sstart, 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,
                        -T, T, rstart, 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,
                        -T, T, sstart, sinterval, sourcePoints, rightTitle,
                        'right', plotParams)
            fig_vec.tight_layout()
            fig_vec.canvas.mpl_connect(
                'key_press_event', lambda event: process_key_vectors(
                    event, times, -T, T, rstart, sstart, 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()