Template for CPU executables

forces_simple.forces_simf.run_forces(H, persis_info, sim_specs, libE_info)

Runs the forces MPI application.

By default assigns the number of MPI ranks to the number of cores available to this worker.

To assign a different number give e.g., num_procs=4 to exctr.submit.

forces_simf.py
 1import numpy as np
 2
 3# Optional status codes to display in libE_stats.txt for each gen or sim
 4from libensemble.message_numbers import TASK_FAILED, WORKER_DONE
 5
 6
 7def run_forces(H, persis_info, sim_specs, libE_info):
 8    """Runs the forces MPI application.
 9
10    By default assigns the number of MPI ranks to the number
11    of cores available to this worker.
12
13    To assign a different number give e.g., `num_procs=4` to
14    ``exctr.submit``.
15    """
16
17    calc_status = 0
18
19    # Parse out num particles, from generator function
20    particles = str(int(H["x"][0][0]))
21
22    # app arguments: num particles, timesteps, also using num particles as seed
23    args = particles + " " + str(10) + " " + particles
24
25    # Retrieve our MPI Executor
26    exctr = libE_info["executor"]
27
28    # Submit our forces app for execution.
29    task = exctr.submit(app_name="forces", app_args=args)
30
31    # Block until the task finishes
32    task.wait()
33
34    # Try loading final energy reading, set the sim's status
35    statfile = "forces.stat"
36    try:
37        data = np.loadtxt(statfile)
38        final_energy = data[-1]
39        calc_status = WORKER_DONE
40    except Exception:
41        final_energy = np.nan
42        calc_status = TASK_FAILED
43
44    # Define our output array, populate with energy reading
45    output = np.zeros(1, dtype=sim_specs["out"])
46    output["energy"] = final_energy
47
48    # Return final information to worker, for reporting to manager
49    return output, persis_info, calc_status
Example usage
 1#!/usr/bin/env python
 2import os
 3import sys
 4
 5import numpy as np
 6from forces_simf import run_forces  # Sim func from current dir
 7
 8from libensemble import Ensemble
 9from libensemble.alloc_funcs.start_only_persistent import only_persistent_gens as alloc_f
10from libensemble.executors import MPIExecutor
11from libensemble.gen_funcs.persistent_sampling import persistent_uniform as gen_f
12from libensemble.specs import AllocSpecs, ExitCriteria, GenSpecs, LibeSpecs, SimSpecs
13
14if __name__ == "__main__":
15    # Initialize MPI Executor
16    exctr = MPIExecutor()
17
18    # Register simulation executable with executor
19    sim_app = os.path.join(os.getcwd(), "../forces_app/forces.x")
20
21    if not os.path.isfile(sim_app):
22        sys.exit("forces.x not found - please build first in ../forces_app dir")
23
24    exctr.register_app(full_path=sim_app, app_name="forces")
25
26    # Parse number of workers, comms type, etc. from arguments
27    ensemble = Ensemble(parse_args=True, executor=exctr)
28    nsim_workers = ensemble.nworkers - 1  # One worker is for persistent generator
29
30    # Persistent gen does not need resources
31    ensemble.libE_specs = LibeSpecs(
32        num_resource_sets=nsim_workers,
33        sim_dirs_make=True,
34    )
35
36    ensemble.sim_specs = SimSpecs(
37        sim_f=run_forces,
38        inputs=["x"],
39        outputs=[("energy", float)],
40    )
41
42    ensemble.gen_specs = GenSpecs(
43        gen_f=gen_f,
44        inputs=[],  # No input when start persistent generator
45        persis_in=["sim_id"],  # Return sim_ids of evaluated points to generator
46        outputs=[("x", float, (1,))],
47        user={
48            "initial_batch_size": nsim_workers,
49            "lb": np.array([1000]),  # min particles
50            "ub": np.array([3000]),  # max particles
51        },
52    )
53
54    # Starts one persistent generator. Simulated values are returned in batch.
55    ensemble.alloc_specs = AllocSpecs(
56        alloc_f=alloc_f,
57        user={
58            "async_return": False,  # False causes batch returns
59        },
60    )
61
62    # Instruct libEnsemble to exit after this many simulations
63    ensemble.exit_criteria = ExitCriteria(sim_max=8)
64
65    # Seed random streams for each worker, particularly for gen_f
66    ensemble.add_random_streams()
67
68    # Run ensemble
69    ensemble.run()
70
71    if ensemble.is_manager:
72        # Note, this will change if changing sim_max, nworkers, lb, ub, etc.
73        print(f'Final energy checksum: {np.sum(ensemble.H["energy"])}')

Also see the Forces tutorial.