Coverage for /builds/ase/ase/ase/calculators/exciting/runner.py: 60.00%
35 statements
« prev ^ index » next coverage.py v7.5.3, created at 2025-08-02 00:12 +0000
« prev ^ index » next coverage.py v7.5.3, created at 2025-08-02 00:12 +0000
1# fmt: off
3"""Binary runner and results class."""
4import os
5import subprocess
6import time
7from pathlib import Path
8from typing import List, Optional, Union
11class SubprocessRunResults:
12 """Results returned from subprocess.run()."""
14 def __init__(
15 self, stdout, stderr, return_code: int,
16 process_time: Optional[float] = None):
17 self.stdout = stdout
18 self.stderr = stderr
19 self.return_code = return_code
20 self.success = return_code == 0
21 self.process_time = process_time
24class SimpleBinaryRunner:
25 """Class to execute a subprocess."""
26 path_type = Union[str, Path]
28 def __init__(self,
29 binary,
30 run_argv: List[str],
31 omp_num_threads: int,
32 directory: path_type = './',
33 args=None) -> None:
34 """Initialise class.
36 :param binary: Binary name prepended by full path, or just binary name
37 (if present in $PATH).
38 :param run_argv: Run commands sequentially as a list of str.
39 For example:
40 * For serial: ['./'] or ['']
41 * For MPI: ['mpirun', '-np', '2']
42 :param omp_num_threads: Number of OMP threads.
43 :param args: Optional arguments for the binary.
44 """
45 if args is None:
46 args = []
47 self.binary = binary
48 self.directory = directory
50 self.run_argv = run_argv
52 self.omp_num_threads = omp_num_threads
53 self.args = args
55 if directory is not None and not Path(directory).is_dir():
56 raise OSError(f"Run directory does not exist: {directory}")
58 if omp_num_threads <= 0:
59 raise ValueError("Number of OMP threads must be > 0")
61 def compose_execution_list(self) -> list:
62 """Generate a complete list of strings to pass to subprocess.run().
64 This is done to execute the calculation.
66 For example, given:
67 ['mpirun', '-np, '2'] + ['binary.exe'] + ['>', 'std.out']
69 return ['mpirun', '-np, '2', 'binary.exe', '>', 'std.out']
70 """
71 return self.run_argv + [self.binary] + self.args
73 def run(self) -> SubprocessRunResults:
74 """Run a binary."""
75 execution_list = self.compose_execution_list()
76 my_env = {**os.environ}
78 time_start: float = time.time()
79 result = subprocess.run(execution_list,
80 env=my_env,
81 capture_output=True,
82 cwd=self.directory, check=False)
83 total_time = time.time() - time_start
84 return SubprocessRunResults(
85 result.stdout, result.stderr, result.returncode, total_time)