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
124
125
126
127
128
129
130
131
132
133
134
135
// Copyright (c) 2020-2022  David Sorokin <david.sorokin@gmail.com>, based in Yoshkar-Ola, Russia
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use crate::simulation;
use crate::simulation::Run;
use crate::simulation::Point;
use crate::simulation::event::*;
use crate::simulation::parameter::*;

/// Some useful utilities.
pub mod utils;

/// Return the current nested level within `Parameter<usize>` computation,
/// which is started from zero for the root computation and then increased by one
/// for each next nested computation.
#[inline]
pub fn branch_level() -> BranchLevel {
    BranchLevel {}
}

/// Branch a new nested computation and return its result within the current
/// `Event` by leaving the current computation intact.
///
/// A new derivative branch with `branch_level` increased by 1 is created at
/// the current modeling time. Then the result of the specified computation for
/// derivative branch is returned to the current computation.
///
/// The state of the current computation including its event queue and mutable
/// references `RefComp` remain intact. In some sense we copy the state of the model
/// to derivative branch and then proceed with the derived simulation.
/// Only this copying operation is relatively cheap.
#[inline]
pub fn branch_event<M>(comp: M) -> BranchEvent<M>
    where M: Event
{
    BranchEvent { comp }
}

/// Branch a new nested computation and return its result at the desired
/// time of the future within the current `Event` by leaving
/// the current computation intact.
///
/// A new derivative branch with `branch_level` increased by 1 is created at
/// the current modeling time. Then the result of the specified computation for
/// derivative branch is returned to the current computation.
///
/// The state of the current computation including its event queue and mutable
/// references `RefComp` remain intact. In some sense we copy the state of the model
/// to derivative branch and then proceed with the derived simulation.
/// Only this copying operation is relatively cheap.
#[inline]
pub fn future_event<M>(event_time: f64, comp: M, including_current: bool) -> FutureEvent<M>
    where M: Event
{
    FutureEvent { event_time, comp, including_current }
}

/// Return the branch level.
#[must_use = "computations are lazy and do nothing unless to be run"]
#[derive(Clone)]
pub struct BranchLevel {}

impl Parameter for BranchLevel {

    type Item = usize;

    #[doc(hidden)]
    #[inline]
    fn call_parameter(self, r: &Run) -> simulation::Result<usize> {
        Result::Ok(r.branch.level)
    }
}

/// Allows branching the `Event` computation.
#[must_use = "computations are lazy and do nothing unless to be run"]
#[derive(Clone)]
pub struct BranchEvent<M> {

    /// The computation.
    comp: M
}

impl<M: Event> Event for BranchEvent<M> {

    type Item = M::Item;

    #[doc(hidden)]
    #[inline]
    fn call_event(self, p: &Point) -> simulation::Result<Self::Item> {
        let BranchEvent { comp } = self;
        let r = p.run.new_child(p);
        let p = Point {
            run: &r,
            time: p.time,
            priority: p.priority,
            iteration: p.iteration,
            phase: p.phase
        };
        comp.call_event(&p)
    }
}

/// Allows running the `Event` computation in future.
#[must_use = "computations are lazy and do nothing unless to be run"]
#[derive(Clone)]
pub struct FutureEvent<M> {

    /// The event time.
    event_time: f64,

    /// The computation.
    comp: M,

    /// Whether to include the current events?
    including_current: bool
}

impl<M: Event> Event for FutureEvent<M> {

    type Item = M::Item;

    #[doc(hidden)]
    #[inline]
    fn call_event(self, p: &Point) -> simulation::Result<Self::Item> {
        let FutureEvent { event_time, comp, including_current } = self;
        assert!(event_time >= p.time, "The event time cannot be less than the current modeling time");
        let r = p.run.new_child(p);
        let p = r.point_at(event_time, p.priority);
        r.event_queue.run_events(including_current, &p)?;
        comp.call_event(&p)
    }
}