Example #1
0
    def __check_submitted(self, metadata_updater: MetadataUpdater,
                          submitted_to_check: DataFrame) -> None:
        """
        Check the status of all runs which has status `submitted`.

        Parameters
        ----------
        metadata_updater : MetadataUpdater
            Object which updates the database
        submitted_to_check : DataFrame
            DataFrame containing the `id` and `name` of the runs with status `submitted`

        Raises
        ------
        RuntimeError
            In case log_reader.started() is True and log_reader.start_time is None
        """
        for name, run_id in submitted_to_check.itertuples(index=False):
            metadata_updater.run_id = run_id

            log_path = self.__project_path.joinpath(name, "BOUT.log.0")

            if log_path.is_file():
                log_reader = LogReader(log_path)
                if log_reader.started():
                    start_time = log_reader.start_time
                    # Assert to prevent "Incompatible types in assignment" with Optional
                    if start_time is None:
                        msg = ("log_reader.start_time is None although "
                               "log_reader.started is True")
                        logging.critical(msg)
                        raise RuntimeError(msg)
                    metadata_updater.update_start_time(start_time)
                    latest_status = self.__check_if_stopped(
                        log_reader, metadata_updater)

                else:
                    # No started time is found in the log
                    latest_status = self.check_if_running_or_errored(
                        log_reader)
            else:
                # No log file exists
                # NOTE: This means that the execution is either in a
                #       queue or has failed the submission.
                #       For now, we still consider this as submitted
                #       This can maybe be decided by checking either the
                #       pid or the status from the submitter
                latest_status = "submitted"

            metadata_updater.update_latest_status(latest_status)
Example #2
0
    def __check_if_stopped(self, log_reader: LogReader,
                           metadata_updater: MetadataUpdater) -> str:
        """
        Check if a run has stopped.

        Parameters
        ----------
        log_reader : LogReader
            The object which reads log files
        metadata_updater : MetadataUpdater
            Object which updates the database

        Returns
        -------
        latest_status : str
            The latest status

        Raises
        ------
        RuntimeError
            In case log_reader.ended() is True and log_reader.end_time is None
        """
        if log_reader.ended():
            end_time = log_reader.end_time
            # Assert to prevent "Incompatible types in assignment" with Optional
            if end_time is None:
                raise RuntimeError("log_reader.end_time is None although "
                                   "log_reader.ended() is True")
            metadata_updater.update_stop_time(end_time)
            latest_status = "complete"
        else:
            latest_status = self.check_if_running_or_errored(log_reader)
        return latest_status
def test_pid(yield_logs: Dict[str, Path]) -> None:
    """
    Test self.pid.

    Parameters
    ----------
    yield_logs : dict of Path
        A dictionary containing the log paths used for testing
    """
    log_paths = yield_logs
    success_log_reader = LogReader(log_paths["success_log"])
    assert success_log_reader.pid == 1191

    failed_log_reader = LogReader(log_paths["fail_log"])
    assert failed_log_reader.pid == 1190

    unfinished_no_pid_log_reader = LogReader(
        log_paths["unfinished_no_pid_log"])
    assert unfinished_no_pid_log_reader.pid is None
def test_end_time(yield_logs: Dict[str, Path]) -> None:
    """
    Test self.end_time.

    Parameters
    ----------
    yield_logs : dict of Path
        A dictionary containing the log paths used for testing
    """
    log_paths = yield_logs
    end_time = datetime(2020, 5, 1, 17, 7, 14)
    success_log_reader = LogReader(log_paths["success_log"])
    assert success_log_reader.end_time == end_time

    failed_log_reader = LogReader(log_paths["fail_log"])
    assert failed_log_reader.end_time is None

    unfinished_no_pid_log_reader = LogReader(
        log_paths["unfinished_no_pid_log"])
    assert unfinished_no_pid_log_reader.end_time is None
def test_get_simulation_steps(yield_logs: Dict[str, Path]) -> None:
    """
    Test self.get_simulation_steps.

    Parameters
    ----------
    yield_logs : dict of Path
        A dictionary containing the log paths used for testing
    """
    log_paths = yield_logs
    success_log_reader = LogReader(log_paths["success_log"])
    assert success_log_reader.get_simulation_steps().shape == (101, 8)

    failed_log_reader = LogReader(log_paths["fail_log"])
    assert failed_log_reader.get_simulation_steps().shape == (1, 8)

    unfinished_no_pid_log_reader = LogReader(
        log_paths["unfinished_no_pid_log"])
    assert unfinished_no_pid_log_reader.get_simulation_steps().empty
Example #6
0
    def __check_running(self, metadata_updater: MetadataUpdater,
                        running_to_check: DataFrame) -> None:
        """
        Check the status of all runs which has status `running`.

        Parameters
        ----------
        metadata_updater : MetadataUpdater
            Object which updates the database
        running_to_check : DataFrame
            DataFrame containing the `id` and `name` of the runs with status `running`
        """
        for name, run_id in running_to_check.itertuples(index=False):
            metadata_updater.run_id = run_id
            log_path = self.__project_path.joinpath(name, "BOUT.log.0")
            log_reader = LogReader(log_path)
            latest_status = self.check_if_running_or_errored(log_reader)
            metadata_updater.update_latest_status(latest_status)
def large_graph_tester(
    submitter_type: Type[AbstractSubmitter],
    make_project: Path,
    yield_number_of_rows_for_all_tables: Callable[[DatabaseReader], Dict[str, int]],
    file_state_restorer: FileStateRestorer,
) -> None:
    """
    Test that the graph with 10 nodes work as expected.

    The node setup can be found in node_functions.py

    Parameters
    ----------
    submitter_type : type
        Used to assert that the correct submitter is used
    make_project : Path
        The path to the conduction example
    yield_number_of_rows_for_all_tables : function
        Function which returns the number of rows for all tables in a schema
    file_state_restorer : FileStateRestorer
        Object for restoring files to original state
    """
    name = f"test_large_graph_{submitter_type.__name__}"

    node_adder = LargeGraphNodeAdder(
        name, make_project, submitter_type, file_state_restorer
    )
    # RunGroup belonging to node 2
    node_adder.add_and_assert_node_group_2()
    # RunGroup belonging to node 3 and 4
    node_adder.add_and_assert_node_group_3_and_4()
    # RunGroup belonging to node 6
    node_8 = node_adder.add_and_assert_node_group_6()
    # RunGroup belonging to node 9
    node_adder.add_and_assert_node_node_9(node_8)
    # Run the project
    runner = BoutRunner(node_adder.run_graph)
    runner.run()
    runner.wait_until_completed()
    # Check that all the nodes have changed status
    with pytest.raises(RuntimeError):
        runner.run()
        runner.wait_until_completed()
    # Check that all files are present
    # Check that the pre and post files are present
    for node in (0, 1, 5, 7, 8, 10):
        assert (
            node_adder.paths["pre_and_post_directory"].joinpath(f"{node}.txt").is_file()
        )
    # Check that all the dump files are present
    for restart_str in ("", "_restart_0", "_restart_1", "_restart_2"):
        assert (
            node_adder.paths["project_path"]
            .joinpath(f"{name}{restart_str}", "BOUT.dmp.0.nc")
            .is_file()
            or node_adder.paths["project_path"]
            .joinpath(f"{name}{restart_str}", "BOUT.dmp.0.h5")
            .is_file()
        )
    # NOTE: We will only have 4 runs as node 4 is a duplicate of node 2 and will
    #       therefore be skipped
    number_of_runs = 4
    assert_tables_have_expected_len(
        DatabaseReader(node_adder.run_groups["run_group_2"].db_connector),
        yield_number_of_rows_for_all_tables,
        expected_run_number=number_of_runs,
        restarted=True,
    )
    simulation_steps = LogReader(
        node_adder.paths["project_path"].joinpath(f"{name}_restart_2", "BOUT.log.0")
    ).get_simulation_steps()
    # NOTE: nout=0 set in the function tests.utils.run.make_run_group
    assert np.isclose(simulation_steps.loc[simulation_steps.index[-1], "Sim_time"], 0.0)
def bout_runner_from_path_tester(
    submitter_type: Type[AbstractSubmitter],
    project_path: Path,
    yield_number_of_rows_for_all_tables: Callable[[DatabaseReader], Dict[str, int]],
    file_state_restorer: FileStateRestorer,
) -> None:
    """
    Test that the minimal BoutRunners setup works.

    This test will test that:
    1. We can execute a run from the (mocked) current work directory
    2. The correct submitter has been used
    3. The metadata is properly stored
    4. We cannot execute the run again...
    5. ...unless we set force=True
    6. Check the restart functionality twice

    Parameters
    ----------
    submitter_type : type
        Submitter type to check for
    project_path : Path
        The path to the conduction example
    yield_number_of_rows_for_all_tables : function
        Function which returns the number of rows for all tables in a schema
    file_state_restorer : FileStateRestorer
        Object for restoring files to original state
    """
    # NOTE: This triggers too-many-statements (51/50)
    # pylint: disable=too-many-statements
    logging.info("Start: First run")
    # Make project to save time
    _ = project_path
    file_state_restorer.add(project_path.joinpath("conduction.db"))
    file_state_restorer.add(project_path.joinpath("settings_run"))
    with change_directory(project_path):
        runner = BoutRunner()
        bout_run_setup = runner.run_graph["bout_run_0"]["bout_run_setup"]
        file_state_restorer.add(
            bout_run_setup.bout_paths.bout_inp_dst_dir, force_mark_removal=True
        )
    runner.run()
    runner.wait_until_completed()

    assert isinstance(bout_run_setup.executor.submitter, submitter_type)

    bout_paths = bout_run_setup.bout_paths
    db_connector = bout_run_setup.db_connector
    # Assert that the run went well
    db_reader = assert_first_run(bout_paths, db_connector)
    # Assert that the number of runs is 1
    assert_tables_have_expected_len(
        db_reader, yield_number_of_rows_for_all_tables, expected_run_number=1
    )
    logging.info("Done: First run")

    logging.info("Start: Check RuntimeError")
    # Check that all the nodes have changed status
    with pytest.raises(RuntimeError):
        runner.run()
        runner.wait_until_completed()
    logging.info("Done: Check RuntimeError")
    logging.info("Start: Assert that run will not be run again")
    # Check that the run will not be executed again
    runner.reset()
    runner.run()
    runner.wait_until_completed()
    # Assert that the number of runs is 1
    assert_tables_have_expected_len(
        db_reader, yield_number_of_rows_for_all_tables, expected_run_number=1
    )
    logging.info("Done: Assert that run will not be run again")
    logging.info("Start: Run with force=True")
    # Check that force overrides the behaviour
    runner.run(force=True)
    runner.wait_until_completed()
    assert_tables_have_expected_len(
        db_reader, yield_number_of_rows_for_all_tables, expected_run_number=2
    )
    logging.info("Done: Run with force=True")

    logging.info("Start: Run with restart_all=True the first time")
    dump_dir_parent = bout_paths.bout_inp_dst_dir.parent
    dump_dir_name = bout_paths.bout_inp_dst_dir.name
    file_state_restorer.add(dump_dir_parent.joinpath(f"{dump_dir_name}_restart_0"))

    # Check that the restart functionality works
    runner.run(restart_all=True)
    runner.wait_until_completed()
    assert_tables_have_expected_len(
        db_reader,
        yield_number_of_rows_for_all_tables,
        expected_run_number=3,
        restarted=True,
    )
    # NOTE: The test in tests.unit.bout_runners.runners.test_bout_runner is testing
    #       restart_from_bout_inp_dst=True, whether this is testing restart_all=True
    assert_dump_files_exist(dump_dir_parent.joinpath(f"{dump_dir_name}_restart_0"))
    logging.info("Done: Run with restart_all=True the first time")
    logging.info("Start: Run with restart_all=True the second time")
    # ...twice
    file_state_restorer.add(dump_dir_parent.joinpath(f"{dump_dir_name}_restart_1"))
    runner.run(restart_all=True)
    runner.wait_until_completed()
    assert_tables_have_expected_len(
        db_reader,
        yield_number_of_rows_for_all_tables,
        expected_run_number=4,
        restarted=True,
    )
    # NOTE: The test in tests.unit.bout_runners.runners.test_bout_runner is testing
    #       restart_from_bout_inp_dst=True, whether this is testing restart_all=True
    assert_dump_files_exist(dump_dir_parent.joinpath(f"{dump_dir_name}_restart_1"))
    simulation_steps = LogReader(
        dump_dir_parent.joinpath(f"{dump_dir_name}_restart_1", "BOUT.log.0")
    ).get_simulation_steps()
    assert np.isclose(
        simulation_steps.loc[simulation_steps.index[-1], "Sim_time"], 30.0
    )
    logging.info("Done: Run with restart_all=True the second time")