Source code for parasweep.sweep

# -*- coding: utf-8 -*-
"""Main sweep functionality."""
import datetime
import os

from parasweep.namers import SequentialNamer
from parasweep.dispatchers import SubprocessDispatcher
from parasweep.templates import PythonFormatTemplate


[docs]def run_sweep(command, configs, templates, sweep, namer=SequentialNamer(), dispatcher=SubprocessDispatcher(), template_engine=PythonFormatTemplate(), sweep_id=None, excluded_sim_ids=[], serial=False, wait=True, cleanup=False, verbose=True, overwrite=True, save_mapping=True): r""" Run parameter sweeps. Parameters ---------- command : str The command to run. Must include ``{sim_id}`` indicating where the simulation ID is to be inserted. configs : list List of paths indicating where the configuration files should be saved after substitution of the parameters into the templates. Each must include ``{sim_id}`` indicating where the simulation ID is to be inserted. Must be in the same order as ``templates``. templates : list List of paths of templates to substitute parameters into. Must be in the same order as ``configs``. sweep : sweepers.Sweep instance A :class:`parasweep.sweepers.Sweep` object that specifies the type of sweep. By default, it is a Cartesian sweep. namer : namers.Namer instance, optional A :class:`parasweep.namers.Namer` object that specifies how to assign simulation IDs. By default, assigns simulation IDs sequentially. dispatcher : dispatchers.Dispatcher instance, optional A :class:`parasweep.dispatchers.Dispatcher` object that specifies how to run the jobs. By default, uses Python's ``subprocess`` module. template_engine : templates.TemplateEngine instance, optional A :class:`parasweep.templates.TemplateEngine` object that specifies the template engine to use. By default, uses Python format strings. sweep_id : str, optional A name for the sweep. By default, the name is generated automatically from the date and time. excluded_sim_ids : list, optional A list of simulation IDs to exclude from the sweep. serial : bool, optional Whether to run simulations serially, i.e., to wait for each simulation to complete before executing the next one. Enabling this turns off parallelism. False by default. wait : bool, optional Whether to wait for all simulations to complete before returning. True by default. cleanup : bool, optional Whether to delete configuration files after all the simulations are done. This will cause the command to wait on all processes before returning (as with the ``wait`` argument). False by default. verbose : bool, optional Whether to print some information about each simulation as it is launched. True by default. overwrite : bool, optional Whether to overwrite existing files when creating configuration files. If False, a ``FileExistsError`` will be raised when a configuration filename coincides with an existing one in the same directory. True by default. save_mapping : bool, optional Whether to save a mapping between the parameters to the simulation IDs. The type of mapping will depend on the type of sweep (set with the ``sweep`` argument). The filename will be of the form ``sim_ids_{sweep_id}``, with an extension depending on the mapping type. True by default. Examples -------- Many examples are provided in :doc:`examples`. """ if isinstance(configs, str) or isinstance(templates, str): raise TypeError('`configs` and `templates` must be a list.') if sweep_id is None: current_time = datetime.datetime.now() sweep_id = current_time.strftime('%Y-%m-%dT%H_%M_%S') template_engine.load(paths=templates) namer.start(length=len(sweep)) sim_ids = [] config_filenames = [] dispatcher.initialize_session() for param_set in sweep.elements(): sim_id = namer.generate_id(param_set, sweep_id) sim_ids.append(sim_id) if sim_id not in excluded_sim_ids: rendered = template_engine.render(param_set) for config_rendered, config_path in zip(rendered, configs): config_filename = config_path.format(sim_id=sim_id) config_filenames.append(config_filename) if not overwrite: if os.path.isfile(config_filename): raise FileExistsError(f'{config_filename} exists, set ' '`overwrite` to True to overwrite.') with open(config_filename, 'wb') as config_file: config_file.write(config_rendered.encode('utf-8', 'replace')) if verbose: print(f'Running simulation {sim_id} with parameters:') print('\n'.join(f'{key}: {param}' for key, param in param_set.items())) dispatcher.dispatch(command.format(sim_id=sim_id), serial) if wait: dispatcher.wait_all() if cleanup: dispatcher.wait_all() for config_filename in config_filenames: os.remove(config_filename) return sweep.mapping(sim_ids, sweep_id, save_mapping)