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