Skip to main content

fmi_sim/sim/
me.rs

1//! Model-Exchange simulation generic across FMI versions.
2
3use fmi::traits::{FmiEventHandler, FmiInstance, FmiModelExchange};
4
5use crate::Error;
6
7use super::{
8    SimState, SimStats,
9    interpolation::Linear,
10    solver::Solver,
11    traits::{InstRecordValues, InstSetValues, SimHandleEvents, SimMe},
12};
13
14impl<Inst> SimMe<Inst> for SimState<Inst>
15where
16    Inst: FmiInstance + FmiModelExchange + InstSetValues + InstRecordValues + FmiEventHandler,
17{
18    fn main_loop<S>(&mut self, mut solver: S) -> Result<SimStats, Error>
19    where
20        S: Solver<Inst>,
21    {
22        let mut stats = SimStats::default();
23        self.inst.enter_continuous_time_mode().map_err(Into::into)?;
24
25        let mut time = self.sim_params.start_time;
26
27        loop {
28            self.inst.record_outputs(time, &mut self.recorder_state)?;
29
30            if time >= self.sim_params.stop_time {
31                break;
32            }
33
34            // calculate next time point
35            let next_regular_point = self.sim_params.start_time
36                + (stats.num_steps + 1) as f64 * self.sim_params.output_interval;
37            let next_input_event_time = self.input_state.next_input_event(time);
38
39            let input_event = next_regular_point >= next_input_event_time;
40            let time_event = next_regular_point >= self.next_event_time();
41
42            // Use the earliest of [next_input_event, next_event_time, and next_regular_point]
43            let next_communication_point = if input_event || time_event {
44                next_input_event_time.min(self.next_event_time())
45            } else {
46                next_regular_point
47            };
48
49            let (time_reached, state_event) =
50                solver.step(&mut self.inst, next_communication_point)?;
51            time = time_reached;
52
53            self.inst.set_time(time).map_err(Into::into)?;
54
55            self.input_state
56                .apply_input::<Linear>(time, &mut self.inst, false, true, false)?;
57
58            if time == next_regular_point {
59                stats.num_steps += 1;
60            }
61
62            let mut step_event = false;
63            let mut terminate = false;
64
65            self.inst
66                .completed_integrator_step(true, &mut step_event, &mut terminate)
67                .map_err(Into::into)?;
68
69            if terminate {
70                log::info!("Termination requested by FMU");
71                break;
72            }
73
74            if input_event || time_event || state_event || step_event {
75                log::trace!(
76                    "Event encountered at t = {time}. [Input: {input_event}, Time: {time_event}, State: {state_event}, Step: {step_event}]"
77                );
78                stats.num_events += 1;
79                let (reset_solver, terminate) = self.handle_events(time, input_event)?;
80
81                if terminate {
82                    break;
83                }
84
85                self.inst.enter_continuous_time_mode().map_err(Into::into)?;
86
87                if reset_solver {
88                    solver.reset(&mut self.inst, time)?;
89                }
90            }
91        }
92
93        self.inst.terminate().map_err(Into::into)?;
94
95        Ok(stats)
96    }
97}