var_resources¶
Simulation functions that use the MPIExecutor with dynamic resource assignment.
six_hump_camel and helloworld python scripts are used as example
applications, but these could be any MPI application.
Each simulation function uses the resources assigned to this worker to set CPU count and, in some functions, specify GPU usage.
GPUs are not used for the six_hump_camel function, but these tests check the assignment is correct. For an example that runs an actual GPU application, see the forces_gpu tutorial under libensemble/tests/scaling_tests/forces/forces_gpu.
See CUDA_variable_resources for an example where the sim function interrogates available resources and sets explicitly.
- var_resources.gpu_variable_resources(H, persis_info, sim_specs, libE_info)¶
Launches an app and automatically assigns GPU resources.
The six_hump_camel app does not run on the GPU, but this test demonstrates how to automatically assign the GPUs given to this worker via the MPIExecutor.
The method used to assign GPUs will be determined by the MPI runner or user-provided configuration (e.g., by setting the
platformorplatform_specsoptions or the LIBE_PLATFORM environment variable).
- var_resources.gpu_variable_resources_from_gen(H, persis_info, sim_specs, libE_info)¶
Launches an app and assigns CPU and GPU resources as defined by the gen.
Otherwise similar to gpu_variable_resources.
- var_resources.gpu_variable_resources_subenv(H, persis_info, sim_specs, libE_info)¶
Launches a chain of apps via bash scripts in different sub-processes.
Different MPI runners are specified for each submit. To run without dry_run these MPI runners need to be present. Dry_run is used by default.
Otherwise, this test is similar to
gpu_variable_resources.
- var_resources.multi_points_with_variable_resources(H, _, sim_specs, libE_info)¶
Evaluates either helloworld or six hump camel for a collection of points given in
H["x"]via the MPI executor, supporting variable sized simulations/resources, as determined by the generator. The term rset refers to a resource set (the minimal set of resources that can be assigned to each worker). It can be anything from a partition of a node to multiple nodes.Note that this is also an example that is capable of handling multiple points (sim ids) in each call.
- var_resources.CUDA_variable_resources(H, _, sim_specs, libE_info)¶
Launches an app setting GPU resources
The standard test apps do not run on GPU, but demonstrates accessing resource information to set
CUDA_VISIBLE_DEVICES, and typical run configuration.For an equivalent function that auto-assigns GPUs using platform detection, see GPU_variable_resources.
var_resources.py
1"""
2Simulation functions that use the MPIExecutor with dynamic resource assignment.
3``six_hump_camel`` and ``helloworld`` python scripts are used as example
4applications, but these could be any MPI application.
5
6Each simulation function uses the resources assigned to this worker to set CPU
7count and, in some functions, specify GPU usage.
8
9GPUs are not used for the six_hump_camel function, but these tests check the
10assignment is correct. For an example that runs an actual GPU application, see
11the forces_gpu tutorial under libensemble/tests/scaling_tests/forces/forces_gpu.
12
13See CUDA_variable_resources for an example where the sim function
14interrogates available resources and sets explicitly.
15
16"""
17
18__all__ = [
19 "gpu_variable_resources",
20 "gpu_variable_resources_from_gen",
21 "gpu_variable_resources_subenv",
22 "multi_points_with_variable_resources",
23 "CUDA_variable_resources",
24]
25
26import os
27
28import numpy as np
29
30from libensemble.message_numbers import TASK_FAILED, UNSET_TAG, WORKER_DONE
31from libensemble.resources.resources import Resources
32from libensemble.sim_funcs.six_hump_camel import six_hump_camel_func
33from libensemble.tools.test_support import check_gpu_setting, check_mpi_runner
34
35
36def gpu_variable_resources(H, persis_info, sim_specs, libE_info):
37 """Launches an app and automatically assigns GPU resources.
38
39 The six_hump_camel app does not run on the GPU, but this test demonstrates
40 how to automatically assign the GPUs given to this worker via the MPIExecutor.
41
42 The method used to assign GPUs will be determined by the MPI runner or
43 user-provided configuration (e.g., by setting the ``platform`` or
44 ``platform_specs`` options or the LIBE_PLATFORM environment variable).
45
46 """
47 x = H["x"][0]
48 H_o = np.zeros(1, dtype=sim_specs["out"])
49 dry_run = sim_specs["user"].get("dry_run", False) # logs run lines instead of running
50 inpt = " ".join(map(str, x)) # Application input
51
52 exctr = libE_info["executor"]
53
54 # Launch application via system MPI runner, using assigned resources.
55 task = exctr.submit(
56 app_name="six_hump_camel",
57 app_args=inpt,
58 auto_assign_gpus=True,
59 match_procs_to_gpus=True,
60 stdout="out.txt",
61 stderr="err.txt",
62 dry_run=dry_run,
63 )
64
65 if not dry_run:
66 task.wait() # Wait for run to complete
67
68 # Access app output
69 with open("out.txt") as f:
70 H_o["f"] = float(f.readline().strip()) # Read just first line
71
72 # Asserts GPU set correctly (for known MPI runners)
73 check_gpu_setting(task, print_setting=True)
74
75 calc_status = WORKER_DONE if task.state == "FINISHED" else "FAILED"
76 return H_o, persis_info, calc_status
77
78
79def gpu_variable_resources_from_gen(H, persis_info, sim_specs, libE_info):
80 """
81 Launches an app and assigns CPU and GPU resources as defined by the gen.
82
83 Otherwise similar to gpu_variable_resources.
84 """
85 x = H["x"][0]
86 H_o = np.zeros(1, dtype=sim_specs["out"])
87 dry_run = sim_specs["user"].get("dry_run", False) # logs run lines instead of running
88 inpt = " ".join(map(str, x)) # Application input
89
90 exctr = libE_info["executor"] # Get Executor
91
92 # Launch application via system MPI runner, using assigned resources.
93 task = exctr.submit(
94 app_name="six_hump_camel",
95 app_args=inpt,
96 stdout="out.txt",
97 stderr="err.txt",
98 dry_run=dry_run,
99 )
100
101 if not dry_run:
102 task.wait() # Wait for run to complete
103
104 # Access app output
105 with open("out.txt") as f:
106 H_o["f"] = float(f.readline().strip()) # Read just first line
107
108 # Asserts GPU set correctly (for known MPI runners)
109 check_gpu_setting(task, print_setting=True)
110
111 calc_status = WORKER_DONE if task.state == "FINISHED" else "FAILED"
112 return H_o, persis_info, calc_status
113
114
115def _launch_with_env_and_mpi(exctr, inpt, dry_run, env_script_path, mpi_runner):
116 """Used to launch each application in a chain"""
117
118 task = exctr.submit(
119 app_name="six_hump_camel",
120 app_args=inpt,
121 auto_assign_gpus=True,
122 match_procs_to_gpus=True,
123 dry_run=dry_run,
124 env_script=env_script_path,
125 mpi_runner_type=mpi_runner,
126 )
127
128 if isinstance(mpi_runner, dict):
129 mpi_runner = mpi_runner["runner_name"]
130
131 check_mpi_runner(task, mpi_runner, print_setting=True)
132 check_gpu_setting(task, print_setting=True)
133
134
135def gpu_variable_resources_subenv(H, persis_info, sim_specs, libE_info):
136 """Launches a chain of apps via bash scripts in different sub-processes.
137
138 Different MPI runners are specified for each submit. To run without dry_run
139 these MPI runners need to be present. Dry_run is used by default.
140
141 Otherwise, this test is similar to ``gpu_variable_resources``.
142
143 """
144 x = H["x"][0]
145 H_o = np.zeros(1, dtype=sim_specs["out"])
146 dry_run = sim_specs["user"].get("dry_run", False) # logs run lines instead of running
147 env_script_path = sim_specs["user"]["env_script"] # Script to run in subprocess
148 inpt = " ".join(map(str, x)) # Application input
149
150 exctr = libE_info["executor"] # Get Executor
151
152 # Launch application via given MPI runner, using assigned resources.
153 _launch_with_env_and_mpi(exctr, inpt, dry_run, env_script_path, "openmpi")
154 _launch_with_env_and_mpi(exctr, inpt, dry_run, env_script_path, "srun")
155
156 mpi_runner_type = {"mpi_runner": "openmpi", "runner_name": "special_mpi"}
157 _launch_with_env_and_mpi(exctr, inpt, dry_run, env_script_path, mpi_runner_type)
158
159 # Now run in current environment.
160 task = exctr.submit(
161 app_name="six_hump_camel",
162 app_args=inpt,
163 auto_assign_gpus=True,
164 match_procs_to_gpus=True,
165 dry_run=dry_run,
166 )
167 check_mpi_runner(task, "mpich", print_setting=True)
168 check_gpu_setting(task, print_setting=True)
169
170 if not dry_run:
171 task.wait() # Wait for run to complete
172
173 # Access app output
174 with open("out.txt") as f:
175 H_o["f"] = float(f.readline().strip()) # Read just first line
176
177 # Asserts GPU set correctly (for known MPI runners)
178 check_gpu_setting(task, print_setting=True)
179
180 calc_status = WORKER_DONE if task.state == "FINISHED" else "FAILED"
181 return H_o, persis_info, calc_status
182
183
184def multi_points_with_variable_resources(H, _, sim_specs, libE_info):
185 """
186 Evaluates either helloworld or six hump camel for a collection of points
187 given in ``H["x"]`` via the MPI executor, supporting variable sized
188 simulations/resources, as determined by the generator. The term `rset`
189 refers to a resource set (the minimal set of resources that can be assigned
190 to each worker). It can be anything from a partition of a node to multiple
191 nodes.
192
193 Note that this is also an example that is capable of handling multiple
194 points (sim ids) in each call.
195
196 .. seealso::
197 `test_uniform_sampling_with_variable_resources.py <https://github.com/Libensemble/libensemble/blob/develop/libensemble/tests/functionality_tests/test_uniform_sampling_with_variable_resources.py>`_ # noqa
198 """
199
200 batch = len(H["x"])
201 H_o = np.zeros(batch, dtype=sim_specs["out"])
202 app = sim_specs["user"].get("app", "helloworld")
203 dry_run = sim_specs["user"].get("dry_run", False) # dry_run only prints run lines in ensemble.log
204 set_cores_by_rsets = True # If True use rset count to set num procs, else use all available to this worker.
205 core_multiplier = 1 # Only used with set_cores_by_rsets as a multiplier.
206
207 exctr = libE_info["executor"] # Get Executor
208 task_states = []
209 for i, x in enumerate(H["x"]):
210 nprocs = None # Will be as if argument is not present
211 if set_cores_by_rsets:
212 resources = Resources.resources.worker_resources
213 nprocs = resources.num_rsets * core_multiplier
214
215 inpt = None # Will be as if argument is not present
216 if app == "six_hump_camel":
217 inpt = " ".join(map(str, H["x"][i]))
218
219 task = exctr.submit(
220 app_name=app,
221 app_args=inpt,
222 num_procs=nprocs,
223 stdout="out.txt",
224 stderr="err.txt",
225 dry_run=dry_run,
226 )
227
228 if not dry_run:
229 task.wait() # Wait for run to complete
230
231 # while(not task.finished):
232 # time.sleep(0.1)
233 # task.poll()
234
235 task_states.append(task.state)
236
237 if app == "six_hump_camel":
238 # H_o["f"][i] = float(task.read_stdout()) # Reads whole file
239 with open("out.txt") as f:
240 H_o["f"][i] = float(f.readline().strip()) # Read just first line
241 else:
242 # To return something in test
243 H_o["f"][i] = six_hump_camel_func(x)
244
245 calc_status = UNSET_TAG # Returns to worker
246 if all(t == "FINISHED" for t in task_states):
247 calc_status = WORKER_DONE
248 elif any(t == "FAILED" for t in task_states):
249 calc_status = TASK_FAILED
250
251 return H_o, calc_status
252
253
254def CUDA_variable_resources(H, _, sim_specs, libE_info):
255 """Launches an app setting GPU resources
256
257 The standard test apps do not run on GPU, but demonstrates accessing resource
258 information to set ``CUDA_VISIBLE_DEVICES``, and typical run configuration.
259
260 For an equivalent function that auto-assigns GPUs using platform detection, see
261 GPU_variable_resources.
262 """
263 x = H["x"][0]
264 H_o = np.zeros(1, dtype=sim_specs["out"])
265 dry_run = sim_specs["user"].get("dry_run", False) # dry_run only prints run lines in ensemble.log
266
267 # Interrogate resources available to this worker
268 resources = Resources.resources.worker_resources
269 slots = resources.slots
270
271 assert resources.matching_slots, f"Error: Cannot set CUDA_VISIBLE_DEVICES when unmatching slots on nodes {slots}"
272
273 num_nodes = resources.local_node_count
274
275 # Set to slots
276 resources.set_env_to_slots("CUDA_VISIBLE_DEVICES")
277 cores_per_node = resources.slot_count
278
279 # Set to detected GPUs
280 # gpus_per_slot = resources.gpus_per_rset_per_node
281 # resources.set_env_to_slots("CUDA_VISIBLE_DEVICES", multiplier=gpus_per_slot)
282 # cores_per_node = resources.slot_count * gpus_per_slot # One CPU per GPU
283
284 print(
285 f"Worker {libE_info['workerID']}: CUDA_VISIBLE_DEVICES={os.environ['CUDA_VISIBLE_DEVICES']}"
286 f"\tnodes {num_nodes} ppn {cores_per_node} slots {slots}"
287 )
288
289 # Create application input file
290 inpt = " ".join(map(str, x))
291 exctr = libE_info["executor"] # Get Executor
292
293 # Launch application via system MPI runner, using assigned resources.
294 task = exctr.submit(
295 app_name="six_hump_camel",
296 app_args=inpt,
297 num_nodes=num_nodes,
298 procs_per_node=cores_per_node,
299 stdout="out.txt",
300 stderr="err.txt",
301 dry_run=dry_run,
302 # extra_args='--gpus-per-task=1'
303 )
304
305 if not dry_run:
306 task.wait() # Wait for run to complete
307
308 # Access app output
309 with open("out.txt") as f:
310 H_o["f"] = float(f.readline().strip()) # Read just first line
311
312 calc_status = WORKER_DONE if task.state == "FINISHED" else "FAILED"
313 return H_o, calc_status