|
30 | 30 | # To change logging level for just this module |
31 | 31 | # logger.setLevel(logging.DEBUG) |
32 | 32 |
|
| 33 | +# Placeholder for container support - replaced with simulation directory at runtime |
| 34 | +LIBE_SIM_DIR_PLACEHOLDER = "%LIBENSEMBLE_SIM_DIR%" |
| 35 | + |
33 | 36 | STATES = """ |
34 | 37 | UNKNOWN |
35 | 38 | CREATED |
@@ -431,6 +434,7 @@ def __init__(self) -> None: |
431 | 434 | self.workerID = None |
432 | 435 | self.comm = None |
433 | 436 | self.last_task = 0 |
| 437 | + self.base_dir = os.getcwd() |
434 | 438 | Executor.executor = self |
435 | 439 |
|
436 | 440 | def __enter__(self): |
@@ -522,6 +526,10 @@ def register_app( |
522 | 526 |
|
523 | 527 | precedent: str, Optional |
524 | 528 | 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. |
525 | 533 | """ |
526 | 534 |
|
527 | 535 | if not app_name: |
@@ -673,10 +681,26 @@ def set_worker_info(self, comm=None, workerid=None) -> None: |
673 | 681 | self.workerID = workerid |
674 | 682 | self.comm = comm |
675 | 683 |
|
676 | | - def _check_app_exists(self, full_path: str) -> None: |
| 684 | + def _check_app_exists(self, app: Application) -> None: |
677 | 685 | """Allows submit function to check if app exists and error if not""" |
678 | | - if not os.path.isfile(full_path): |
679 | | - raise ExecutorException(f"Application does not exist {full_path}") |
| 686 | + if app.precedent: |
| 687 | + # Could be a container call in precedent. In that case, |
| 688 | + # the executable is not available on the host system and |
| 689 | + # we just forward what the user provided. |
| 690 | + return |
| 691 | + |
| 692 | + if not os.path.isfile(app.full_path): |
| 693 | + raise ExecutorException(f"Application does not exist {app.full_path}") |
| 694 | + |
| 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] |
680 | 704 |
|
681 | 705 | def submit( |
682 | 706 | self, |
@@ -745,12 +769,15 @@ def submit( |
745 | 769 | task = Task(app, app_args, default_workdir, stdout, stderr, self.workerID, dry_run) |
746 | 770 |
|
747 | 771 | if not dry_run: |
748 | | - self._check_app_exists(task.app.full_path) |
| 772 | + self._check_app_exists(task.app) |
749 | 773 |
|
750 | 774 | runline = task.app.app_cmd.split() |
751 | 775 | if task.app_args is not None: |
752 | 776 | runline.extend(task.app_args.split()) |
753 | 777 |
|
| 778 | + runline = self._set_sim_dir_env(task, runline) |
| 779 | + task.runline = " ".join(runline) |
| 780 | + |
754 | 781 | if dry_run: |
755 | 782 | logger.info(f"Test (No submit) Runline: {' '.join(runline)}") |
756 | 783 | else: |
|
0 commit comments