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