Source code for bout_runners.runner.bout_runner

"""Contains the BOUT runner class."""


import logging
from typing import Optional

from bout_runners.database.database_connector import DatabaseConnector
from bout_runners.database.database_creator import DatabaseCreator
from bout_runners.executor.executor import Executor
from bout_runners.metadata.metadata_recorder import MetadataRecorder
from bout_runners.parameters.final_parameters import FinalParameters


[docs]class BoutRunner: r""" Class for executing a run and store its metadata. Attributes ---------- self.__executor : Executor Getter variable for executor self.__db_connector : DatabaseConnector Getter variable for db_connector self.__final_parameters : FinalParameters Getter variable for final_parameters self.__db_creator : DatabaseCreator Object used to create the database self.__metadata_recorder : MetadataRecorder Object used to record the metadata about a run self.executor : Executor Object used to execute the run self.db_creator : DatabaseCreator Object used to create the database self.final_parameters : FinalParameters Object containing the parameters to use Methods ------- create_schema() Create the schema run(force) Execute the run Examples -------- The easiest way to use BoutRunner 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 >>> BoutRunner().run() and `BoutRunner` takes care of the rest. A more elaborate example where all the dependency objects are built manually: Import dependencies >>> from pathlib import Path >>> from bout_runners.executor.bout_paths import BoutPaths >>> from bout_runners.executor.executor import Executor >>> 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 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 input objects >>> default_parameters = DefaultParameters(bout_paths) >>> run_parameters = RunParameters({'global': {'nout': 0}}) >>> final_parameters = FinalParameters(default_parameters, ... run_parameters) >>> executor = Executor( ... bout_paths=bout_paths, ... submitter=LocalSubmitter(bout_paths.project_path), ... run_parameters=run_parameters) >>> db_connection = DatabaseConnector('name_of_database', db_root_path=Path()) Run the project >>> runner = BoutRunner(executor, ... db_connection, ... final_parameters) >>> runner.run() """ def __init__( self, executor: Optional[Executor] = None, db_connector: Optional[DatabaseConnector] = None, final_parameters: Optional[FinalParameters] = None, ) -> None: """ Set the member data. Parameters ---------- executor : Executor or None Object executing the run If None, default parameters will be used db_connector : DatabaseConnector or None The connection to the database If None: Default database connector will be used final_parameters : FinalParameters or None The object containing the parameters which are going to be used in the run 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.__executor = executor if executor is not None else Executor() self.__final_parameters = ( final_parameters if final_parameters is not None else FinalParameters() ) self.__db_connector = ( db_connector if db_connector is not None else DatabaseConnector() ) self.__db_creator = DatabaseCreator(self.db_connector) self.__metadata_recorder = MetadataRecorder( self.__db_connector, self.executor.bout_paths, self.final_parameters ) @property def executor(self) -> Executor: """ Get the properties of self.executor. Returns ------- self.__executor : Executor The executor object """ return self.__executor @property def final_parameters(self) -> FinalParameters: """ Get the properties of self.final_parameters. Returns ------- self.__final_parameters : FinalParameters The object containing the parameters used in the run """ return self.__final_parameters @property def db_connector(self) -> DatabaseConnector: """ Get the properties of self.db_connector. Returns ------- self.__db_connector : DatabaseConnector The object holding the database connection """ return self.__db_connector
[docs] def create_schema(self) -> None: """Create the schema.""" final_parameters_dict = self.final_parameters.get_final_parameters() final_parameters_as_sql_types = self.final_parameters.cast_to_sql_type( final_parameters_dict ) self.__db_creator.create_all_schema_tables(final_parameters_as_sql_types)
[docs] def run(self, force: bool = False) -> None: """ Perform the run and capture data. Parameters ---------- force : bool Execute the run even if has been performed with the same parameters """ if not self.__metadata_recorder.db_reader.check_tables_created(): logging.info( "Creating schema as no tables were found in " "%s", self.__metadata_recorder.db_reader.db_connector.db_path, ) self.create_schema() run_id = self.__metadata_recorder.capture_new_data_from_run( self.__executor.submitter.processor_split, force ) if run_id is None: logging.info("Executing the run") self.executor.execute() else: logging.warning( "Run with the same configuration has been executed before, " "see run with run_id %d", run_id, ) if force: logging.info("Executing the run as force==True") self.executor.execute()