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 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 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 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}