"""Contains the executor class."""
from typing import Optional
from bout_runners.executor.bout_paths import BoutPaths
from bout_runners.make.make import Make
from bout_runners.parameters.run_parameters import RunParameters
from bout_runners.submitter.local_submitter import LocalSubmitter
[docs]class Executor:
r"""
Executes the command for submitting a run.
Attributes
----------
__bout_paths : BoutPaths
Getter variable for project_path
__run_parameters : RunParameters
Object containing the run parameters
__make : Make
Object for making the project
__command : str
The terminal command for executing the run
submitter : AbstractSubmitter
Object containing the submitter
bout_paths : BoutPaths
Object containing the paths
Methods
-------
get_execute_command()
Return the execute command string
execute()
Execute a BOUT++ run
Examples
--------
The easiest way to use the Executor is to run a script from the root directory of
the project (i.e. where the `Makefile` and `data` directory are normally
situated. The script can simply call
>>> Executor().execute()
and `Executor` takes care of the rest.
A more elaborate example where all the dependency objects are built manually:
Import the dependencies
>>> from pathlib import Path
>>> from bout_runners.executor.bout_paths import BoutPaths
>>> from bout_runners.submitter.local_submitter import LocalSubmitter
Create the `bout_paths` object
>>> project_path = Path().joinpath('path', 'to', 'project')
>>> bout_inp_src_dir = Path().joinpath('path', 'to', 'source', 'BOUT.inp')
>>> bout_inp_dst_dir = Path().joinpath('path', 'to', 'destination', 'BOUT.inp')
>>> bout_paths = BoutPaths(project_path=project_path,
... bout_inp_src_dir=bout_inp_src_dir,
... bout_inp_dst_dir=bout_inp_dst_dir)
Create the executor object
>>> run_parameters = RunParameters({'global': {'nout': 0}})
>>> executor = Executor(
... bout_paths=bout_paths,
... submitter=LocalSubmitter(bout_paths.project_path),
... run_parameters=run_parameters)
Execute the run
>>> executor.execute()
"""
def __init__(
self,
bout_paths: Optional[BoutPaths] = None,
submitter: Optional[LocalSubmitter] = None,
run_parameters: Optional[RunParameters] = None,
) -> None:
"""
Set the input parameters.
Parameters
----------
bout_paths : BoutPaths or None
Object containing the paths
If None, default BoutPaths values will be used
submitter : AbstractSubmitter
Object containing the submitter
run_parameters : RunParameters or None
Object containing the run parameters
If None, default parameters will be used
"""
# Set member data
# NOTE: We are not setting the default as a keyword argument
# as this would mess up the paths
self.submitter = submitter if submitter is not None else LocalSubmitter()
self.__bout_paths = bout_paths if bout_paths is not None else BoutPaths()
self.__run_parameters = (
run_parameters if run_parameters is not None else RunParameters()
)
self.__make = Make(self.__bout_paths.project_path)
self.__command = self.get_execute_command()
@property
def bout_paths(self) -> BoutPaths:
"""
Set the properties of self.bout_paths.
Returns
-------
self.__bout_paths : BoutPaths
Object containing the paths
Notes
-----
The bout_paths is read only
"""
return self.__bout_paths
@property
def run_parameters(self):
"""
Set the properties of self.run_parameters.
Returns
-------
self.__run_parameters : RunParameters
Object containing the run parameters
Notes
-----
The run_parameters is read only
"""
return self.__run_parameters
[docs] def get_execute_command(self) -> str:
"""
Return the execute command string.
Returns
-------
command : str
The terminal command for executing the run
"""
mpi_cmd = "mpirun -np"
# NOTE: No spaces if parameters are None
command = (
f"{mpi_cmd} "
f"{self.submitter.processor_split.number_of_processors} "
f"./{self.__make.exec_name} "
f"-d {self.__bout_paths.bout_inp_dst_dir} "
f"{self.__run_parameters.run_parameters_str}"
)
return command
[docs] def execute(self) -> None:
"""Execute a BOUT++ run."""
# Make the project if not already made
self.__make.run_make()
# Submit the command
self.submitter.submit_command(self.__command)