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