Skip to content

Commit 140baf4

Browse files
authored
Merge pull request #1672 from Libensemble/feature/container_support
Provide relative workdir
2 parents 4294a4d + 1de8c05 commit 140baf4

4 files changed

Lines changed: 39 additions & 7 deletions

File tree

libensemble/executors/executor.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@
3030
# To change logging level for just this module
3131
# logger.setLevel(logging.DEBUG)
3232

33+
# Placeholder for container support - replaced with simulation directory at runtime
34+
LIBE_SIM_DIR_PLACEHOLDER = "%LIBENSEMBLE_SIM_DIR%"
35+
3336
STATES = """
3437
UNKNOWN
3538
CREATED
@@ -431,6 +434,7 @@ def __init__(self) -> None:
431434
self.workerID = None
432435
self.comm = None
433436
self.last_task = 0
437+
self.base_dir = os.getcwd()
434438
Executor.executor = self
435439

436440
def __enter__(self):
@@ -522,6 +526,10 @@ def register_app(
522526
523527
precedent: str, Optional
524528
Any str that should directly precede the application full path.
529+
Supports the placeholder ``%LIBENSEMBLE_SIM_DIR%`` which is replaced
530+
at runtime with the simulation directory as a relative path from
531+
where the executor was created. This is useful for container exec
532+
commands.
525533
"""
526534

527535
if not app_name:
@@ -684,6 +692,16 @@ def _check_app_exists(self, app: Application) -> None:
684692
if not os.path.isfile(app.full_path):
685693
raise ExecutorException(f"Application does not exist {app.full_path}")
686694

695+
def _set_sim_dir_env(self, task: Task, run_cmd: list[str]) -> list[str]:
696+
"""Replace simulation directory placeholder in run command if present.
697+
698+
Supports container-based execution where the simulation directory needs to be
699+
passed to container exec commands (e.g., podman-hpc exec --workdir).
700+
"""
701+
sim_dir = os.path.relpath(task.workdir, self.base_dir)
702+
task._add_to_env("LIBENSEMBLE_SIM_DIR", sim_dir)
703+
return [arg.replace(LIBE_SIM_DIR_PLACEHOLDER, sim_dir) for arg in run_cmd]
704+
687705
def submit(
688706
self,
689707
calc_type: str | None = None,
@@ -757,6 +775,9 @@ def submit(
757775
if task.app_args is not None:
758776
runline.extend(task.app_args.split())
759777

778+
runline = self._set_sim_dir_env(task, runline)
779+
task.runline = " ".join(runline)
780+
760781
if dry_run:
761782
logger.info(f"Test (No submit) Runline: {' '.join(runline)}")
762783
else:

libensemble/executors/mpi_executor.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,6 @@ class MPIExecutor(Executor):
7171
7272
from libensemble.executors.mpi_executor import MPIExecutor
7373
exctr = MPIExecutor(custom_info=customizer)
74-
75-
7674
"""
7775

7876
def __init__(self, custom_info: dict = {}) -> None:
@@ -363,7 +361,8 @@ def submit(
363361
if task.app_args is not None:
364362
runline.extend(task.app_args.split())
365363

366-
task.runline = " ".join(runline) # Allow to be queried
364+
runline = self._set_sim_dir_env(task, runline)
365+
task.runline = " ".join(runline)
367366

368367
if env_script is not None:
369368
run_cmd = Executor._process_env_script(task, runline, env_script)

libensemble/tests/unit_tests/test_executor_gpus.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,9 @@ def run_check(exp_env, exp_cmd, **kwargs):
118118
args_for_sim = "sleep 0"
119119
exp_runline = exp_cmd + " simdir/my_simtask.x sleep 0"
120120
task = exctr.submit(calc_type="sim", app_args=args_for_sim, dry_run=True, **kwargs)
121-
assert task.env == exp_env, f"Task env does not match expected:\n Received: {task.env}\n Expected: {exp_env}"
121+
for key, value in exp_env.items():
122+
assert key in task.env, f"Expected env key '{key}' not found in task.env: {task.env}"
123+
assert task.env[key] == value, f"Env key '{key}' has value '{task.env[key]}', expected '{value}'"
122124
assert (
123125
task.runline == exp_runline
124126
), f"Run line does not match expected.\n Received: {task.runline}\n Expected: {exp_runline}"

libensemble/tools/test_support.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,16 @@ def check_gpu_setting(task, assert_setting=True, print_setting=False, resources=
242242
print(f"Worker {task.workerID}: {desc}GPU setting ({stype}): {gpu_setting} {addon}", flush=True)
243243

244244
if assert_setting:
245-
assert (
246-
gpu_setting == expected
247-
), f"Worker {task.workerID}: Found GPU setting: {gpu_setting}, Expected: {expected}"
245+
if isinstance(expected, dict):
246+
for key, value in expected.items():
247+
assert key in gpu_setting, (
248+
f"Worker {task.workerID}: Expected env key '{key}' not found in GPU setting: {gpu_setting}"
249+
)
250+
assert gpu_setting[key] == value, (
251+
f"Worker {task.workerID}: GPU setting key '{key}' has value '{gpu_setting[key]}', "
252+
f"expected '{value}'"
253+
)
254+
else:
255+
assert (
256+
gpu_setting == expected
257+
), f"Worker {task.workerID}: Found GPU setting: {gpu_setting}, Expected: {expected}"

0 commit comments

Comments
 (0)