Runner

The BoutRunner class is the orchestrator of the run. Its responsibility is to execute the nodes of the the run graph, and to capture the metadata about the run. It is composed of several other classes which each performs one specific task in the orchestration. In its simplest form it can be called from a project directory (i.e. a directory with a make file and a directory which holds a BOUT.inp file) in the following way

from bout_runners.runner.bout_runner import BoutRunner
BoutRunner().run()

In the example above BoutRunner and its classes will be populated with default values. For a more fine grained control of the setup of the BOUT++ run, see Constructing the BoutRunSetup.

Restarting

If you do not care about run graphs, and you are only interested in restarting one run, you can simply execute the following script in your project directory

from bout_runners.runner.bout_runner import BoutRunner
runner = BoutRunner()
runner.run()  # This will execute the first run
runner.run(restart_all=True)  # This will restart the run

Note

The code above will not overwrite any files. The new dump files can be found in the directory <name_of_BOUT_inp_directory>_restart_<restart_number>.

Note

You can also make a graph of runs, where one of the nodes is a restart of the other. See chaining restarts for details.

Constructing the BoutRunner

Although it’s nice to run the scripts from the root of the project directory, it may in certain cases be too strict. The following example shows how to get a more fine-grained control of the runner class.

Constructing the BoutRunSetup

This section describes how a BoutRunSetup object can be build from scratch. The BoutRunSetup controls all aspects of a single BOUT++ run.

Note

There are two types of run which can be executed through the BoutRunner: The “bout run” and the “function run”. A “bout run” is a run where a BOUT++ project is involved. A “function run” is a run where a python function is involved. The “function runs” can be used for pre- and post-processor for a “bout run” as described in the run graph.

First we import the necessary dependencies

from pathlib import Path
from bout_runners.executor.bout_paths import BoutPaths
from bout_runners.executor.executor import BoutRunExecutor
from bout_runners.database.database_connector import DatabaseConnector
from bout_runners.parameters.default_parameters import DefaultParameters
from bout_runners.parameters.run_parameters import RunParameters
from bout_runners.parameters.final_parameters import FinalParameters
from bout_runners.submitter.local_submitter import LocalSubmitter
from bout_runners.runner.bout_run_setup import BoutRunSetup
from bout_runners.runner.run_graph import RunGraph
from bout_runners.runner.run_group import RunGroup
from bout_runners.runner.bout_runner import BoutRunner

Next, we create the bout_paths object. This object handles the path to the project. It is also responsible for copying the BOUT.inp from a source directory to a destination directory, and will throw an error if the source BOUT.inp is not found

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)

We can also override the parameters in the BOUT.inp located in the destination directory by using the parameters package. The parameters package contains the classes DefaultParameters, RunParameters and FinalParameters. The DefaultParameters obtains the default parameters by reading the BOUT.settings file. If none is present a settings_run with nout = 0 will be executed. The RunParameters accepts a dict which overrides the sections in BOUT.inp.

Note

Options without a section in BOUT.inp is should be listed under the 'global' key. See the code below, where 'nout' is listed under 'global'.

Finally, the FinalParameters synthesize the parameters from DefaultParameters and RunParameters, where RunParameters will have precedence. The FinalParameters will contain the parameters which will be used when executing the run.

default_parameters = DefaultParameters(bout_paths)
run_parameters = RunParameters({'global': {'nout': 0}})
final_parameters = FinalParameters(default_parameters,
                                   run_parameters)

Next, we create an BoutRunExecutor instance. This is responsible for submitting the command to the system which will carry out the run. The submitter parameter accepts any submitters which inherits from AbstractSubmitter which includes LocalSubmitter, PBSSubmitter and SlurmSubmitter.

submitter = LocalSubmitter(bout_paths.project_path)
executor = BoutRunExecutor(
    bout_paths=bout_paths,
    submitter=submitter,
    run_parameters=run_parameters)

In addition, we need to know what database to write to.

db_connector = DatabaseConnector('name_of_database',
                                  db_root_path=Path().joinpath('path', 'to', 'dir')

We are now ready to build the BoutRunSetup object

bout_run_setup = BoutRunSetup(executor, db_connector, final_parameters)

Note

The BoutRunSetup needs the final_parameters in order to write the metadata to the database.

Constructing the RunGroup and RunGraph

As we are just interested in a single bout run in this example, we will treat the RunGroup and the RunGraph as abstract concepts. You can read more about them and see more elaborate examples in the run graph.

The BoutRunner accepts a RunGraph. The following code will create the RunGraph and populate it with a RunGroup which contains the BoutRunSetup

run_graph = RunGraph()
RunGroup(run_graph, bout_run_setup, name='my_bout_run')  # This will add the run group to the run_graph

Starting the run

We are now ready to build the BoutRunner object.

runner = BoutRunner(run_graph)

Finally, we are ready to submit the run

runner.run()