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
4from pathlib import Path
5
6import numpy as np
7from forces_simf import run_forces # Sim func from current dir
8
9from libensemble import Ensemble
10from libensemble.executors import MPIExecutor
11from libensemble.gen_funcs.persistent_sampling import persistent_uniform as gen_f
12from libensemble.specs import 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 = Path.cwd() / "../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 initial_batch_size=nsim_workers,
48 async_return=False,
49 user={
50 "lb": np.array([1000]), # min particles
51 "ub": np.array([3000]), # max particles
52 },
53 )
54
55 # Starts one persistent generator. Simulated values are returned in batch.
56
57 # Instruct libEnsemble to exit after this many simulations
58 ensemble.exit_criteria = ExitCriteria(sim_max=8)
59
60 # Run ensemble
61 ensemble.run()
62
63 if ensemble.is_manager:
64 # Note, this will change if changing sim_max, nworkers, lb, ub, etc.
65 print(f'Final energy checksum: {np.sum(ensemble.H["energy"])}')
Also see the Forces tutorial.