Exemple #1
0
def api(inputfile,
        n=1,
        mpi=False,
        task=False,
        benchmark=False,
        geometry_only=False,
        geometry_fixed=False,
        write_processed=False,
        opt_taguchi=False):
    """If installed as a module this is the entry point."""

    # Print gprMax logo, version, and licencing/copyright information
    logo(__version__ + ' (Bowmore)')

    class ImportArguments:
        pass

    args = ImportArguments()

    args.inputfile = inputfile
    args.n = n
    args.mpi = mpi
    args.task = task
    args.benchmark = benchmark
    args.geometry_only = geometry_only
    args.geometry_fixed = geometry_fixed
    args.write_processed = write_processed
    args.opt_taguchi = opt_taguchi

    run_main(args)
Exemple #2
0
def main():
    """This is the main function for gprMax."""

    # Print gprMax logo, version, and licencing/copyright information
    logo(__version__ + ' (Bowmore)')

    # Parse command line arguments
    parser = argparse.ArgumentParser(
        prog='gprMax', formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser.add_argument('inputfile',
                        help='path to, and name of inputfile or file object')
    parser.add_argument(
        '-n',
        default=1,
        type=int,
        help='number of times to run the input file, e.g. to create a B-scan')
    parser.add_argument('-mpi',
                        action='store_true',
                        default=False,
                        help='flag to switch on MPI task farm')
    parser.add_argument(
        '-task',
        type=int,
        help=
        'task identifier for job array on Open Grid Scheduler/Grid Engine (http://gridscheduler.sourceforge.net/index.html)'
    )
    parser.add_argument('-benchmark',
                        action='store_true',
                        default=False,
                        help='flag to switch on benchmarking mode')
    parser.add_argument(
        '--geometry-only',
        action='store_true',
        default=False,
        help='flag to only build model and produce geometry file(s)')
    parser.add_argument(
        '--geometry-fixed',
        action='store_true',
        default=False,
        help=
        'flag to not reprocess model geometry, e.g. for B-scans where the geometry is fixed'
    )
    parser.add_argument(
        '--write-processed',
        action='store_true',
        default=False,
        help=
        'flag to write an input file after any Python code and include commands in the original input file have been processed'
    )
    parser.add_argument(
        '--opt-taguchi',
        action='store_true',
        default=False,
        help='flag to optimise parameters using the Taguchi optimisation method'
    )
    args = parser.parse_args()

    run_main(args)
Exemple #3
0
def main():
    """This is the main function for gprMax."""
    
    # Print gprMax logo, version, and licencing/copyright information
    logo(gprMax.__version__ + ' (Bowmore)')

    # Parse command line arguments
    parser = argparse.ArgumentParser(prog='gprMax', description='Electromagnetic modelling software based on the Finite-Difference Time-Domain (FDTD) method')
    parser.add_argument('inputfile', help='path to and name of inputfile')
    parser.add_argument('-n', default=1, type=int, help='number of times to run the input file')
    parser.add_argument('-mpi', action='store_true', default=False, help='switch on MPI task farm')
    parser.add_argument('-benchmark', action='store_true', default=False, help='switch on benchmarking mode')
    parser.add_argument('--geometry-only', action='store_true', default=False, help='only build model and produce geometry file(s)')
    parser.add_argument('--write-python', action='store_true', default=False, help='write an input file after any Python code blocks in the original input file have been processed')
    parser.add_argument('--opt-taguchi', action='store_true', default=False, help='optimise parameters using the Taguchi optimisation method')
    args = parser.parse_args()
    numbermodelruns = args.n
    inputdirectory = os.path.dirname(os.path.abspath(args.inputfile)) + os.sep
    inputfile = inputdirectory + os.path.basename(args.inputfile)
    
    # Create a separate namespace that users can access in any Python code blocks in the input file
    usernamespace = {'c': c, 'e0': e0, 'm0': m0, 'z0': z0, 'number_model_runs': numbermodelruns, 'inputdirectory': inputdirectory}

    # Process for Taguchi optimisation
    if args.opt_taguchi:
        if args.benchmark:
            raise GeneralError('Taguchi optimisation should not be used with benchmarking mode')
        from gprMax.optimisation_taguchi import run_opt_sim
        run_opt_sim(args, numbermodelruns, inputfile, usernamespace)

    # Process for benchmarking simulation
    elif args.benchmark:
        run_benchmark_sim(args, inputfile, usernamespace)

    # Process for standard simulation
    else:
        # Mixed mode MPI/OpenMP - MPI task farm for models with each model parallelised with OpenMP
        if args.mpi:
            if args.benchmark:
                raise GeneralError('MPI should not be used with benchmarking mode')
            if numbermodelruns == 1:
                raise GeneralError('MPI is not beneficial when there is only one model to run')
            run_mpi_sim(args, numbermodelruns, inputfile, usernamespace)
        # Standard behaviour - models run serially with each model parallelised with OpenMP
        else:
            run_std_sim(args, numbermodelruns, inputfile, usernamespace)

        print('\nSimulation completed.\n{}\n'.format(68*'*'))
Exemple #4
0
def run_main(args):
    """
    Top-level function that controls what mode of simulation (standard/optimsation/benchmark etc...) is run.

    Args:
        args (dict): Namespace with input arguments from command line or api.
    """

    # Print gprMax logo, version, and licencing/copyright information
    logo(__version__ + ' (' + codename + ')')

    with open_path_file(args.inputfile) as inputfile:

        # Get information about host machine
        hostinfo = get_host_info()
        hyperthreading = ', {} cores with Hyper-Threading'.format(
            hostinfo['logicalcores']) if hostinfo['hyperthreading'] else ''
        print('\nHost: {} | {} | {} x {} ({} cores{}) | {} RAM | {}'.format(
            hostinfo['hostname'], hostinfo['machineID'], hostinfo['sockets'],
            hostinfo['cpuID'], hostinfo['physicalcores'], hyperthreading,
            human_size(hostinfo['ram'],
                       a_kilobyte_is_1024_bytes=True), hostinfo['osversion']))

        # Get information/setup any Nvidia GPU(s)
        if args.gpu is not None:
            # Flatten a list of lists
            if any(isinstance(element, list) for element in args.gpu):
                args.gpu = [val for sublist in args.gpu for val in sublist]
            gpus, allgpustext = detect_check_gpus(args.gpu)
            print('GPU(s) detected: {}'.format(' | '.join(allgpustext)))

            # If in MPI mode or benchmarking provide list of GPU objects, otherwise
            # provide single GPU object
            if args.mpi or args.mpi_no_spawn or args.benchmark:
                args.gpu = gpus
            else:
                args.gpu = gpus[0]

        # Create a separate namespace that users can access in any Python code blocks in the input file
        usernamespace = {
            'c': c,
            'e0': e0,
            'm0': m0,
            'z0': z0,
            'number_model_runs': args.n,
            'inputfile': os.path.abspath(inputfile.name)
        }

        #######################################
        # Process for benchmarking simulation #
        #######################################
        if args.benchmark:
            if args.mpi or args.opt_taguchi or args.task or args.n > 1:
                raise GeneralError(
                    'Benchmarking mode cannot be combined with MPI, job array, or Taguchi optimisation modes, or multiple model runs.'
                )
            run_benchmark_sim(args, inputfile, usernamespace)

        ####################################################
        # Process for simulation with Taguchi optimisation #
        ####################################################
        elif args.opt_taguchi:
            if args.mpi_worker:  # Special case for MPI spawned workers - they do not need to enter the Taguchi optimisation mode
                run_mpi_sim(args, inputfile, usernamespace)
            else:
                from gprMax.optimisation_taguchi import run_opt_sim
                run_opt_sim(args, inputfile, usernamespace)

        ################################################
        # Process for standard simulation (CPU or GPU) #
        ################################################
        else:
            # Mixed mode MPI with OpenMP or CUDA - MPI task farm for models with each model parallelised with OpenMP (CPU) or CUDA (GPU)
            if args.mpi:
                if args.n == 1:
                    raise GeneralError(
                        'MPI is not beneficial when there is only one model to run'
                    )
                if args.task:
                    raise GeneralError(
                        'MPI cannot be combined with job array mode')
                run_mpi_sim(args, inputfile, usernamespace)

            # Alternate MPI configuration that does not use MPI spawn mechanism
            elif args.mpi_no_spawn:
                if args.n == 1:
                    raise GeneralError(
                        'MPI is not beneficial when there is only one model to run'
                    )
                if args.task:
                    raise GeneralError(
                        'MPI cannot be combined with job array mode')
                run_mpi_no_spawn_sim(args, inputfile, usernamespace)

            # Standard behaviour - models run serially with each model parallelised with OpenMP (CPU) or CUDA (GPU)
            else:
                if args.task and args.restart:
                    raise GeneralError(
                        'Job array and restart modes cannot be used together')
                run_std_sim(args, inputfile, usernamespace)
Exemple #5
0
def main():
    """This is the main function for gprMax."""
    
    # Print gprMax logo, version, and licencing/copyright information
    logo(__version__ + versionname)

    # Parse command line arguments
    parser = argparse.ArgumentParser(prog='gprMax', description='Electromagnetic modelling software based on the Finite-Difference Time-Domain (FDTD) method')
    parser.add_argument('inputfile', help='path to and name of inputfile')
    parser.add_argument('-n', default=1, type=int, help='number of times to run the input file')
    parser.add_argument('-mpi', action='store_true', default=False, help='switch on MPI')
    parser.add_argument('--geometry-only', action='store_true', default=False, help='only build model and produce geometry file(s)')
    parser.add_argument('--write-python', action='store_true', default=False, help='write an input file after any Python code blocks in the original input file have been processed')
    parser.add_argument('--opt-taguchi', action='store_true', default=False, help='optimise parameters using the Taguchi optimisation method')
    args = parser.parse_args()
    numbermodelruns = args.n
    inputdirectory = os.path.dirname(os.path.abspath(args.inputfile)) + os.sep
    inputfile = inputdirectory + os.path.basename(args.inputfile)
    inputfileparts = os.path.splitext(inputfile)
    
    # Create a separate namespace that users can access in any Python code blocks in the input file
    usernamespace = {'c': c, 'e0': e0, 'm0': m0, 'z0': z0, 'number_model_runs': numbermodelruns, 'inputdirectory': inputdirectory}
    
    if args.opt_taguchi and numbermodelruns > 1:
        raise CmdInputError('When a Taguchi optimisation is being carried out the number of model runs argument is not required')

    ########################################
    #   Process for Taguchi optimisation   #
    ########################################
    if args.opt_taguchi:
        from user_libs.optimisations.taguchi import taguchi_code_blocks, select_OA, calculate_ranges_experiments, calculate_optimal_levels, plot_optimisation_history

        # Default maximum number of iterations of optimisation to perform (used if the stopping criterion is not achieved)
        maxiterations = 20
        
        # Process Taguchi code blocks in the input file; pass in ordered dictionary to hold parameters to optimise
        tmp = usernamespace.copy()
        tmp.update({'optparams': OrderedDict()})
        taguchinamespace = taguchi_code_blocks(inputfile, tmp)
        
        # Extract dictionaries and variables containing initialisation parameters
        optparams = taguchinamespace['optparams']
        fitness = taguchinamespace['fitness']
        if 'maxiterations' in taguchinamespace:
            maxiterations = taguchinamespace['maxiterations']

        # Store initial parameter ranges
        optparamsinit = list(optparams.items())

        # Dictionary to hold history of optmised values of parameters
        optparamshist = OrderedDict((key, list()) for key in optparams)
        
        # Import specified fitness function
        fitness_metric = getattr(importlib.import_module('user_libs.optimisations.taguchi_fitness'), fitness['name'])

        # Select OA
        OA, N, k, s = select_OA(optparams)
        
        # Initialise arrays and lists to store parameters required throughout optimisation
        # Lower, central, and upper values for each parameter
        levels = np.zeros((s, k), dtype=floattype)
        # Optimal lower, central, or upper value for each parameter
        levelsopt = np.zeros(k, dtype=floattype)
        # Difference used to set values for levels
        levelsdiff = np.zeros(k, dtype=floattype)
        # History of fitness values from each confirmation experiment
        fitnessvalueshist = []

        i = 0
        while i < maxiterations:
            # Set number of model runs to number of experiments
            numbermodelruns = N
            usernamespace['number_model_runs'] = numbermodelruns
            
            # Fitness values for each experiment
            fitnessvalues = []
    
            # Set parameter ranges and define experiments
            optparams, levels, levelsdiff = calculate_ranges_experiments(optparams, optparamsinit, levels, levelsopt, levelsdiff, OA, N, k, s, i)
    
            # Mixed mode MPI/OpenMP - task farm for model runs with MPI; each model parallelised with OpenMP
            if args.mpi:        
                from mpi4py import MPI

                # Define MPI message tags
                tags = Enum('tags', {'READY': 0, 'DONE': 1, 'EXIT': 2, 'START': 3})

                # Initializations and preliminaries
                comm = MPI.COMM_WORLD   # get MPI communicator object
                size = comm.size        # total number of processes
                rank = comm.rank        # rank of this process
                status = MPI.Status()   # get MPI status object
                name = MPI.Get_processor_name()     # get name of processor/host

                if rank == 0:
                    # Master process
                    modelrun = 1
                    numworkers = size - 1
                    closedworkers = 0
                    print('Master: PID {} on {} using {} workers.'.format(os.getpid(), name, numworkers))
                    while closedworkers < numworkers:
                        data = comm.recv(source=MPI.ANY_SOURCE, tag=MPI.ANY_TAG, status=status)
                        source = status.Get_source()
                        tag = status.Get_tag()
                        if tag == tags.READY.value:
                            # Worker is ready, so send it a task
                            if modelrun < numbermodelruns + 1:
                                comm.send(modelrun, dest=source, tag=tags.START.value)
                                print('Master: sending model {} to worker {}.'.format(modelrun, source))
                                modelrun += 1
                            else:
                                comm.send(None, dest=source, tag=tags.EXIT.value)
                        elif tag == tags.DONE.value:
                            print('Worker {}: completed.'.format(source))
                        elif tag == tags.EXIT.value:
                            print('Worker {}: exited.'.format(source))
                            closedworkers += 1
                else:
                    # Worker process
                    print('Worker {}: PID {} on {} requesting {} OpenMP threads.'.format(rank, os.getpid(), name, os.environ.get('OMP_NUM_THREADS')))
                    while True:
                        comm.send(None, dest=0, tag=tags.READY.value)
                        # Receive a model number to run from the master
                        modelrun = comm.recv(source=0, tag=MPI.ANY_TAG, status=status)
                        tag = status.Get_tag()
                        
                        if tag == tags.START.value:
                            # Run a model
                            # Add specific value for each parameter to optimise for each experiment to user accessible namespace
                            optnamespace = usernamespace.copy()
                            tmp = {}
                            tmp.update((key, value[modelrun - 1]) for key, value in optparams.items())
                            optnamespace.update({'optparams': tmp})
                            run_model(args, modelrun, numbermodelruns, inputfile, usernamespace)
                            comm.send(None, dest=0, tag=tags.DONE.value)
                        elif tag == tags.EXIT.value:
                            break

                    comm.send(None, dest=0, tag=tags.EXIT.value)

            # Standard behaviour - models run serially; each model parallelised with OpenMP
            else:
                tsimstart = perf_counter()
                for modelrun in range(1, numbermodelruns + 1):
                    # Add specific value for each parameter to optimise, for each experiment to user accessible namespace
                    optnamespace = usernamespace.copy()
                    tmp = {}
                    tmp.update((key, value[modelrun - 1]) for key, value in optparams.items())
                    optnamespace.update({'optparams': tmp})
                    run_model(args, modelrun, numbermodelruns, inputfile, optnamespace)
                tsimend = perf_counter()
                print('\nTotal simulation time [HH:MM:SS]: {}'.format(datetime.timedelta(seconds=int(tsimend - tsimstart))))

            # Calculate fitness value for each experiment
            for exp in range(1, numbermodelruns + 1):
                outputfile = inputfileparts[0] + str(exp) + '.out'
                fitnessvalues.append(fitness_metric(outputfile, fitness['args']))
                os.remove(outputfile)

            print('\nTaguchi optimisation, iteration {}: completed initial {} experiments completed with fitness values {}.'.format(i + 1, numbermodelruns, fitnessvalues))
            
            # Calculate optimal levels from fitness values by building a response table; update dictionary of parameters with optimal values
            optparams, levelsopt = calculate_optimal_levels(optparams, levels, levelsopt, fitnessvalues, OA, N, k)

            # Run a confirmation experiment with optimal values
            numbermodelruns = 1
            usernamespace['number_model_runs'] = numbermodelruns
            tsimstart = perf_counter()
            for modelrun in range(1, numbermodelruns + 1):
                # Add specific value for each parameter to optimise, for each experiment to user accessible namespace
                optnamespace = usernamespace.copy()
                tmp = {}
                for key, value in optparams.items():
                    tmp[key] = value[modelrun - 1]
                    optparamshist[key].append(value[modelrun - 1])
                optnamespace.update({'optparams': tmp})
                run_model(args, modelrun, numbermodelruns, inputfile, optnamespace)
            tsimend = perf_counter()
            print('\nTotal simulation time [HH:MM:SS]: {}'.format(datetime.timedelta(seconds=int(tsimend - tsimstart))))

            # Calculate fitness value for confirmation experiment
            outputfile = inputfileparts[0] + '.out'
            fitnessvalueshist.append(fitness_metric(outputfile, fitness['args']))

            # Rename confirmation experiment output file so that it is retained for each iteraction
            os.rename(outputfile, os.path.splitext(outputfile)[0] + '_final' + str(i + 1) + '.out')
            
            print('\nTaguchi optimisation, iteration {} completed. History of optimal parameter values {} and of fitness values {}'.format(i + 1, dict(optparamshist), fitnessvalueshist, 68*'*'))
            
            i += 1

            # Stop optimisation if stopping criterion has been reached
            if fitnessvalueshist[i - 1] > fitness['stop']:
                break

            # Stop optimisation if successive fitness values are within 1%
            if i > 2:
                fitnessvaluesclose = (np.abs(fitnessvalueshist[i - 2] - fitnessvalueshist[i - 1]) / fitnessvalueshist[i - 1]) * 100
                if fitnessvaluesclose < 1:
                    break

        # Save optimisation parameters history and fitness values history to file
        opthistfile = inputfileparts[0] + '_hist'
        np.savez(opthistfile, dict(optparamshist), fitnessvalueshist)

        print('\n{}\nTaguchi optimisation completed after {} iteration(s).\nHistory of optimal parameter values {} and of fitness values {}\n{}\n'.format(68*'*', i, dict(optparamshist), fitnessvalueshist, 68*'*'))

        # Plot the history of fitness values and each optimised parameter values for the optimisation
        plot_optimisation_history(fitnessvalueshist, optparamshist, optparamsinit)


    #######################################
    #   Process for standard simulation   #
    #######################################
    else:
        if args.mpi and numbermodelruns == 1:
            raise CmdInputError('MPI is not beneficial when there is only one model to run')

        # Mixed mode MPI/OpenMP - task farm for model runs with MPI; each model parallelised with OpenMP
        if args.mpi:
            from mpi4py import MPI

            # Define MPI message tags
            tags = Enum('tags', {'READY': 0, 'DONE': 1, 'EXIT': 2, 'START': 3})

            # Initializations and preliminaries
            comm = MPI.COMM_WORLD   # get MPI communicator object
            size = comm.size        # total number of processes
            rank = comm.rank        # rank of this process
            status = MPI.Status()   # get MPI status object
            name = MPI.Get_processor_name()     # get name of processor/host

            if rank == 0:
                # Master process
                modelrun = 1
                numworkers = size - 1
                closedworkers = 0
                print('Master: PID {} on {} using {} workers.'.format(os.getpid(), name, numworkers))
                while closedworkers < numworkers:
                    data = comm.recv(source=MPI.ANY_SOURCE, tag=MPI.ANY_TAG, status=status)
                    source = status.Get_source()
                    tag = status.Get_tag()
                    if tag == tags.READY.value:
                        # Worker is ready, so send it a task
                        if modelrun < numbermodelruns + 1:
                            comm.send(modelrun, dest=source, tag=tags.START.value)
                            print('Master: sending model {} to worker {}.'.format(modelrun, source))
                            modelrun += 1
                        else:
                            comm.send(None, dest=source, tag=tags.EXIT.value)
                    elif tag == tags.DONE.value:
                        print('Worker {}: completed.'.format(source))
                    elif tag == tags.EXIT.value:
                        print('Worker {}: exited.'.format(source))
                        closedworkers += 1
            else:
                # Worker process
                print('Worker {}: PID {} on {} requesting {} OpenMP threads.'.format(rank, os.getpid(), name, os.environ.get('OMP_NUM_THREADS')))
                while True:
                    comm.send(None, dest=0, tag=tags.READY.value)
                    # Receive a model number to run from the master
                    modelrun = comm.recv(source=0, tag=MPI.ANY_TAG, status=status)
                    tag = status.Get_tag()
                    
                    if tag == tags.START.value:
                        # Run a model
                        run_model(args, modelrun, numbermodelruns, inputfile, usernamespace)
                        comm.send(None, dest=0, tag=tags.DONE.value)
                    elif tag == tags.EXIT.value:
                        break

                comm.send(None, dest=0, tag=tags.EXIT.value)

        # Standard behaviour - models run serially; each model parallelised with OpenMP
        else:
            tsimstart = perf_counter()
            for modelrun in range(1, numbermodelruns + 1):
                run_model(args, modelrun, numbermodelruns, inputfile, usernamespace)
            tsimend = perf_counter()
            print('\nTotal simulation time [HH:MM:SS]: {}'.format(datetime.timedelta(seconds=int(tsimend - tsimstart))))

        print('\nSimulation completed.\n{}\n'.format(68*'*'))
Exemple #6
0
def main():
    """This is the main function for gprMax."""

    # Print gprMax logo, version, and licencing/copyright information
    logo(gprMax.__version__ + ' (Bowmore)')

    # Parse command line arguments
    parser = argparse.ArgumentParser(
        prog='gprMax',
        description=
        'Electromagnetic modelling software based on the Finite-Difference Time-Domain (FDTD) method'
    )
    parser.add_argument('inputfile', help='path to and name of inputfile')
    parser.add_argument('-n',
                        default=1,
                        type=int,
                        help='number of times to run the input file')
    parser.add_argument('-mpi',
                        action='store_true',
                        default=False,
                        help='switch on MPI')
    parser.add_argument('--geometry-only',
                        action='store_true',
                        default=False,
                        help='only build model and produce geometry file(s)')
    parser.add_argument(
        '--write-python',
        action='store_true',
        default=False,
        help=
        'write an input file after any Python code blocks in the original input file have been processed'
    )
    parser.add_argument(
        '--opt-taguchi',
        action='store_true',
        default=False,
        help='optimise parameters using the Taguchi optimisation method')
    args = parser.parse_args()
    numbermodelruns = args.n
    inputdirectory = os.path.dirname(os.path.abspath(args.inputfile)) + os.sep
    inputfile = inputdirectory + os.path.basename(args.inputfile)

    # Create a separate namespace that users can access in any Python code blocks in the input file
    usernamespace = {
        'c': c,
        'e0': e0,
        'm0': m0,
        'z0': z0,
        'number_model_runs': numbermodelruns,
        'inputdirectory': inputdirectory
    }

    # Process for Taguchi optimisation
    if args.opt_taguchi:
        from gprMax.optimisation_taguchi import run_opt_sim
        run_opt_sim(args, numbermodelruns, inputfile, usernamespace)

    # Process for standard simulation
    else:
        if args.mpi:  # Mixed mode MPI/OpenMP - MPI task farm for models with each model parallelised with OpenMP
            if numbermodelruns == 1:
                raise GeneralError(
                    'MPI is not beneficial when there is only one model to run'
                )
            run_mpi_sim(args, numbermodelruns, inputfile, usernamespace)
        else:  # Standard behaviour - models run serially with each model parallelised with OpenMP
            run_std_sim(args, numbermodelruns, inputfile, usernamespace)

        print('\nSimulation completed.\n{}\n'.format(68 * '*'))