1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
use derive_builder::Builder;
use log::LevelFilter;
use serde::Serialize;

use necsim_core::lineage::Lineage;
use necsim_core_bond::NonNegativeF64;

use crate::args::{cli::CommandArgs, utils::ser::BufferingSerializeResult};

mod dispatch;
mod parse;
mod pause;

use dispatch::dispatch;

#[allow(dead_code)]
enum SimulationOutcome {
    Done {
        time: NonNegativeF64,
        steps: u64,
    },
    Paused {
        time: NonNegativeF64,
        steps: u64,
        lineages: Vec<Lineage>,
    },
}

#[allow(clippy::module_name_repetitions)]
pub fn simulate_with_logger(simulate_args: CommandArgs) -> anyhow::Result<()> {
    log::set_max_level(LevelFilter::Info);

    let ron_args = simulate_args.into_config_string();
    parse::fields::parse_and_normalise(&ron_args)?;
    let mut normalised_args = BufferingSimulateArgs::builder();

    let partitioning = parse::partitioning::parse_and_normalise(&ron_args, &mut normalised_args)?;

    #[cfg(feature = "mpi-partitioning")]
    if let crate::args::config::partitioning::Partitioning::Mpi(partitioning) = &partitioning {
        // Only log to stdout/stderr in the MPI root partition
        if !partitioning.peek_is_root() {
            log::set_max_level(log::LevelFilter::Off);
        }
    }

    let pause = parse::pause::parse_and_normalise(&ron_args, &mut normalised_args, &partitioning)?;
    let sample = parse::sample::parse_and_normalise(&ron_args, &mut normalised_args, &pause)?;

    let speciation_probability_per_generation =
        parse::speciation::parse_and_normalise(&ron_args, &mut normalised_args)?;

    let scenario = parse::scenario::parse_and_normalise(&ron_args, &mut normalised_args)?;
    let algorithm =
        parse::algorithm::parse_and_normalise(&ron_args, &mut normalised_args, &partitioning)?;

    let event_log = parse::event_log::parse_and_normalise(
        &ron_args,
        &mut normalised_args,
        &partitioning,
        &sample,
        &pause,
    )?;

    let reporters = parse::reporters::parse_and_normalise(&ron_args, &mut normalised_args)?;

    // Ensure the dynamic reporter plugin libraries are not deallocated prematurely
    reporters.with_lifetime(|reporters| {
        let result = dispatch(
            partitioning,
            event_log,
            reporters,
            speciation_probability_per_generation,
            sample,
            scenario,
            algorithm,
            pause.as_ref().map(|pause| pause.before),
            &ron_args,
            &mut normalised_args,
        )?;

        match &result {
            SimulationOutcome::Done { time, steps } => info!(
                "The simulation finished at time {} after {} steps.\n",
                time.get(),
                steps
            ),
            SimulationOutcome::Paused { time, steps, .. } => info!(
                "The simulation paused at time {} after {} steps.\n",
                time.get(),
                steps
            ),
        }

        if let (Some(pause), SimulationOutcome::Paused { lineages, .. }) = (pause, result) {
            pause::write_resume_config(normalised_args, pause, lineages)?;
        }

        Ok(())
    })
}

#[derive(Serialize, Builder)]
#[builder(setter(into))]
#[serde(rename = "Simulate")]
struct BufferingSimulateArgs {
    speciation: BufferingSerializeResult,
    sample: BufferingSerializeResult,
    pause: BufferingSerializeResult,
    rng: BufferingSerializeResult,
    scenario: BufferingSerializeResult,
    algorithm: BufferingSerializeResult,
    partitioning: BufferingSerializeResult,
    log: BufferingSerializeResult,
    reporters: BufferingSerializeResult,
}

impl BufferingSimulateArgs {
    #[must_use]
    pub fn builder() -> BufferingSimulateArgsBuilder {
        BufferingSimulateArgsBuilder::default()
    }
}