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, solver_params: S::Params) -> 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 nx = self.inst.get_number_of_continuous_state_values();
26        let nz = self.inst.get_number_of_event_indicator_values();
27
28        let mut solver = <S as Solver<Inst>>::new(
29            self.sim_params.start_time,
30            self.sim_params.tolerance.unwrap_or_default(),
31            nx,
32            nz,
33            solver_params,
34        );
35
36        let mut time = self.sim_params.start_time;
37
38        loop {
39            self.inst.record_outputs(time, &mut self.recorder_state)?;
40
41            if time >= self.sim_params.stop_time {
42                break;
43            }
44
45            // calculate next time point
46            let next_regular_point = self.sim_params.start_time
47                + (stats.num_steps + 1) as f64 * self.sim_params.output_interval;
48            let next_input_event_time = self.input_state.next_input_event(time);
49
50            let input_event = next_regular_point >= next_input_event_time;
51            let time_event = next_regular_point >= self.next_event_time.unwrap_or(f64::INFINITY);
52
53            // Use the earliest of [next_input_event, next_event_time, and next_regular_point]
54            let next_communication_point = if input_event || time_event {
55                next_input_event_time.min(self.next_event_time.unwrap_or(f64::INFINITY))
56            } else {
57                next_regular_point
58            };
59
60            let (time_reached, state_event) =
61                solver.step(&mut self.inst, next_communication_point)?;
62            time = time_reached;
63
64            self.inst.set_time(time).map_err(Into::into)?;
65
66            self.input_state
67                .apply_input::<Linear>(time, &mut self.inst, false, true, false)?;
68
69            if time == next_regular_point {
70                stats.num_steps += 1;
71            }
72
73            let mut step_event = false;
74            let mut terminate = false;
75
76            self.inst
77                .completed_integrator_step(true, &mut step_event, &mut terminate)
78                .map_err(Into::into)?;
79
80            if terminate {
81                log::info!("Termination requested by FMU");
82                break;
83            }
84
85            if input_event || time_event || state_event || step_event {
86                log::trace!(
87                    "Event encountered at t = {time}. [INPUT/TIME/STATE/STEP] = [{input_event}/{time_event}/{state_event}/{step_event}]"
88                );
89                stats.num_events += 1;
90                let mut terminate = false;
91                let reset_solver = self.handle_events(time, input_event, &mut terminate)?;
92
93                if terminate {
94                    break;
95                }
96
97                self.inst.enter_continuous_time_mode().map_err(Into::into)?;
98
99                if reset_solver {
100                    solver.reset(&mut self.inst, time)?;
101                }
102            }
103        }
104
105        self.inst.terminate().map_err(Into::into)?;
106
107        Ok(stats)
108    }
109}