libEnsemble is a complete Python toolkit for steering dynamic ensembles of calculations. Workflows are highly portable and detect/integrate heterogeneous resources with little effort. For instance, libEnsemble can automatically detect, assign, and reassign allocated processors and GPUs to ensemble members.

Users select or supply generator and simulator functions to express their ensembles; the generator typically steers the ensemble based on prior simulator results. Such functions can also launch and monitor external executables at any scale.


Begin by loading the python module:

$ module load cray-python

libEnsemble is available on PyPI, conda-forge, the xSDK, and E4S. Most users install libEnsemble via pip:

$ pip install libensemble

Installing libEnsemble in a virtual environment is highly recommended. See the Python on OCLF Systems page for more information.


For a very simple example of using libEsemble see the Simple Sine Tutorial on libEnsemble’s documentation.

For an example that runs a small ensemble with an application that offloads work to a GPU, see this GPU App Tutorial.

Additional information on compiling/running the above sample GPU app is available here.

See this video for an example workflow on Spock. The channel will soon publish a Frontier-specific guide.

Example Code

import numpy as np
from tutorial_gen import gen_random_sample
from tutorial_sim import sim_find_sine

from libensemble.libE import libE
from import add_unique_random_streams

libE_specs = {"nworkers": 4, "comms": "local"}

gen_specs = {
    "gen_f": gen_random_sample,  # Our generator function
    "out": [("x", float, (1,))],  # gen_f output (name, type, size).
    "user": {
        "lower": np.array([-3]),  # random sampling lower bound
        "upper": np.array([3]),  # random sampling upper bound
        "gen_batch_size": 5,  # number of values gen_f will generate per call

sim_specs = {
    "sim_f": sim_find_sine,  # Our simulator function
    "in": ["x"],  # Input field names. 'x' from gen_f output
    "out": [("y", float)],  # sim_f output. 'y' = sine('x')

persis_info = add_unique_random_streams({}, 5)  # Initialize manager/workers random streams

exit_criteria = {"sim_max": 80}  # Stop libEnsemble after 80 simulations

H, persis_info, flag = libE(sim_specs, gen_specs, exit_criteria, persis_info, libE_specs=libE_specs)

Job Submission

Upon initialization, libEnsemble will detect available nodes and GPUs from the Slurm environment, and allocate those resources towards application-launches.

Start an interactive session:

$ salloc --nodes=2 -A <project_id> --time=00:10:00

Within the session (multiprocessing comms, all processes on first node):

$ python --comms local --nworkers 8