SurveyEnsemble
Survey ensemble modules facilitate the generation of ensembles of mission simulations.
Prototype
The SurveyEnsemble
prototype does not provide any parallelization capability, but executes simulations in series. It is intended only to provide the basic interface specification for generating survey ensembles, and can be useful for debugging purposes where you wish the output from each simulation to be displayed as it is executed.
The method for generating an ensemble is run_ensemble
, with inputs:
sim
- AMissionSim
objectnb_runs
- The number of simulations to executerun_one
- A method to execute for each simulation
There are two keywords: genNewPlanets
and rewindPlanets
, which are both True by default and are passed to the run_one
method. All other keywords are passed directly to run_one
.
In the prototype implementation, run_one
cannot be overwritten and is always defined as follows:
def run_one(self, SS, genNewPlanets=True, rewindPlanets=True):
SS.run_sim()
res = SS.DRM[:]
SS.reset_sim(genNewPlanets=genNewPlanets, rewindPlanets=rewindPlanets)
return res
IPyParallel
This implementation uses the ipyparallel package for paralellization. This requires you to run a cluster, typically activated at the command line by ipcluster start -n 10
, where 10 is the number of workers to create (Note: If a worker from a cluster that was not shut down properly persists when the new cluster is created, this can cause run_ipcluster_ensemble.py to hang. It is recommended to terminate all your active python sessions). Then, from an ipython command prompt, create the MissionSim
object as usual with a script including the IPClusterEnsemble
module:
import EXOSIMS, EXOSIMS.MissionSim, os.path
scriptfile = os.path.join(EXOSIMS.__path__[0],'Scripts','sampleScript_parallel_ensemble.json')
sim = EXOSIMS.MissionSim.MissionSim(scriptfile)
Warning
It is important that you first run a version of your script with the prototype SurveyEnsemble
to ensure that there are no errors and that all cached products are built on disk before execution on the cluster. The MissionSim constructor takes a nopar
keyword input. Building the sim
object with nopar=True
will ignore any non-prototype SurveyEnsemble
in your script file and build with the prototype.
The sim
object will have a SurveyEnsemble
attribute with a run_ensemble method, as described above. This method takes an argument of the run_one
function, which must be defined in the main intepreter scope.
Note
Because of the requirement for run_one
to be in the main scope, you cannot import a run_one
method from a file, or use an object method from an instantiated object. Your easiest options are either to copy and paste your run_one
method directly into the interpreter (preferably using the ipython %paste magic command), or to place the run_one
method by itself in a file on disk, and then use the run
command from the intepreter to load it into the main scope. In either case, run_one
should be defined in the interpreter session as <function __main__.run_one>
.
IPClusterEnsemble`
imports the following modules to all workers in the cluster:
EXOSIMS, EXOSIMS.util.get_module
os, os.path
time
random
cPickle
traceback
and executes SS = EXOSIMS.util.get_module.get_module(specs['modules']['SurveySimulation'], 'SurveySimulation')(**specs)
on each worker, generating a SurveySimulation
object called SS
on each worker using the parameters of your original input script. If your particular run_one
requires additional inputs or common pre-simulation commands to be executed, then you must modify the constructor (preferably by implementing a new SurveyEnsemble
implementation that inherits IPClusterEnsemble
.
A simple run_one
implementation is provided below:
def run_one(genNewPlanets=True, rewindPlanets=True):
SS.run_sim()
res = SS.DRM[:]
SS.reset_sim(genNewPlanets=genNewPlanets, rewindPlanets=rewindPlanets)
return res
Warning
This version of run_one
returns the full DRM
list, meaning that all outputs will need to be collected in the main scope after the ensemble execution, potentially adding considerable overheads. A better approach for large ensembles is to write each individual set of results to disk and return only a scalar value (or some other small output) to the main scope.
Once defined, the run_one
method is executed in parallel by running:
res = sim.run_ensemble(N, run_one=run_one, **kwargs)
where kwargs
are any kewyord arguments, or a dictionary of arguments that are passed to run_one
.
run_ipcluster_ensemble
To simplify parallel ensemble execution via IPClusterEnsemble
, EXOSIMS
provides a run script called run_ipcluster_ensemble.py
(located in the run
directory - see: Directory Layout). This script is intended to be called from the command line, and is executed as:
>python run_ipcluster_ensemble scriptname nruns
where scriptname
is the full path to the JSON script to use, and nruns
is the number of simulations to execute. For full usage information, execute:
>python run_ipcluster_ensemble --help
This script saves the results of each individual simulation to disk as a pickle file, containing a dictionary with two keys:
DRM
: The fullDRM
list of dictionaries encoding the mission simulationsystems
: A dictionary of planet parameters generated by thedump_systems
method of theSimulatedUniverse
object.
In addition, the script saves the output specification (generated by sim.genOutSpec()
) to the same directory as the rest of the results, and saves the traceback of any error generated on any worker during ensemble execution to a log.err
file in the output directory.
read_ipcluster_ensemble
To read in and parse the pickle files generated by run_ipcluster_ensemble
we use EXOSIMS.util.read_ipcluster_ensemble
which provides a gen_summary()
method. This generates lists of detection and characterization parameters for all missions in an ensemble.