fmi_sim/sim/fmi3/
cs.rs

1use anyhow::Context;
2use fmi::{
3    fmi3::{CoSimulation, Fmi3Model, import::Fmi3Import, instance::InstanceCS},
4    traits::FmiInstance,
5};
6
7use crate::{
8    Error,
9    sim::{
10        InputState, RecorderState, SimState, SimStateTrait, SimStats,
11        interpolation::Linear,
12        params::SimParams,
13        traits::{InstRecordValues, SimHandleEvents},
14    },
15};
16
17impl<'a> SimStateTrait<'a, InstanceCS<'a>, Fmi3Import> for SimState<InstanceCS<'a>> {
18    fn new(
19        import: &'a Fmi3Import,
20        sim_params: SimParams,
21        input_state: InputState<InstanceCS<'a>>,
22        output_state: RecorderState<InstanceCS<'a>>,
23    ) -> Result<Self, Error> {
24        let inst = import.instantiate_cs(
25            "inst1",
26            true,
27            true,
28            sim_params.event_mode_used,
29            sim_params.early_return_allowed,
30            &[],
31        )?;
32        Ok(Self {
33            sim_params,
34            input_state,
35            recorder_state: output_state,
36            inst,
37            next_event_time: None,
38        })
39    }
40}
41
42impl<'a> SimState<InstanceCS<'a>> {
43    /// Main loop of the co-simulation
44    pub fn main_loop(&mut self) -> Result<SimStats, Error> {
45        let mut stats = SimStats::default();
46
47        if self.sim_params.event_mode_used {
48            self.inst.enter_step_mode().map_err(fmi::Error::from)?;
49        }
50
51        let mut time = self.sim_params.start_time;
52
53        loop {
54            self.inst.record_outputs(time, &mut self.recorder_state)?;
55
56            if time >= self.sim_params.stop_time {
57                break;
58            }
59
60            // calculate next time point
61            let next_regular_point = self.sim_params.start_time
62                + (stats.num_steps + 1) as f64 * self.sim_params.output_interval;
63            let next_input_event_time = self.input_state.next_input_event(time);
64            // use `next_input_event` if it is earlier than `next_regular_point`
65            let next_communication_point = next_input_event_time.min(next_regular_point);
66            let input_event = next_regular_point > next_input_event_time;
67
68            let step_size = next_communication_point - time;
69
70            let mut event_encountered = false;
71            let mut terminate_simulation = false;
72            let mut early_return = false;
73            let mut last_successful_time = 0.0;
74
75            if self.sim_params.event_mode_used {
76                self.input_state
77                    .apply_input::<Linear>(time, &mut self.inst, false, true, false)?;
78            } else {
79                self.input_state
80                    .apply_input::<Linear>(time, &mut self.inst, true, true, true)?;
81            }
82
83            self.inst
84                .do_step(
85                    time,
86                    step_size,
87                    true,
88                    &mut event_encountered,
89                    &mut terminate_simulation,
90                    &mut early_return,
91                    &mut last_successful_time,
92                )
93                .ok()
94                .context("do_step")?;
95
96            if early_return && !self.sim_params.early_return_allowed {
97                panic!("Early return is not allowed.");
98            }
99
100            if terminate_simulation {
101                break;
102            }
103
104            if early_return && last_successful_time < next_communication_point {
105                time = last_successful_time;
106            } else {
107                time = next_communication_point;
108            }
109
110            if time == next_regular_point {
111                stats.num_steps += 1;
112            }
113
114            if self.sim_params.event_mode_used && (input_event || event_encountered) {
115                log::trace!("Event encountered at t = {time}");
116                self.handle_events(time, input_event, &mut terminate_simulation)?;
117
118                self.inst
119                    .enter_step_mode()
120                    .ok()
121                    .context("enter_step_mode")?;
122            }
123        }
124
125        self.inst.terminate().ok().context("terminate")?;
126
127        stats.end_time = time;
128        Ok(stats)
129    }
130}