Skip to main content

fmi_sim/sim/fmi3/
cs.rs

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