1use arrow::array::RecordBatch;
2use std::path::Path;
3
4use fmi::traits::{FmiEventHandler, FmiImport, FmiInstance};
5
6pub use io::{InputState, RecorderState};
7
8use crate::{Error, options};
9
10use self::{
11 interpolation::Linear,
12 params::SimParams,
13 traits::{FmiSim, InstRecordValues, InstSetValues, SimDefaultInitialize, SimHandleEvents},
14};
15
16#[cfg(feature = "fmi2")]
17pub mod fmi2;
18#[cfg(feature = "fmi3")]
19pub mod fmi3;
20mod interpolation;
21mod io;
22mod me;
23pub mod params;
24pub mod solver;
25pub mod traits;
26pub mod util;
27
28pub struct SimState<Inst>
29where
30 Inst: FmiInstance,
31{
32 sim_params: SimParams,
33 input_state: InputState<Inst>,
34 recorder_state: RecorderState<Inst>,
35 inst: Inst,
36 next_event_time: Option<f64>,
37}
38
39pub trait SimStateTrait<'a, Inst: FmiInstance, Import: FmiImport> {
40 fn new(
41 import: &'a Import,
42 sim_params: SimParams,
43 input_state: InputState<Inst>,
44 output_state: RecorderState<Inst>,
45 ) -> Result<Self, Error>
46 where
47 Self: Sized;
48}
49
50impl<Inst> SimHandleEvents for SimState<Inst>
51where
52 Inst: FmiEventHandler + InstSetValues + InstRecordValues,
53{
54 fn handle_events(
55 &mut self,
56 time: f64,
57 input_event: bool,
58 terminate_simulation: &mut bool,
59 ) -> Result<bool, Error> {
60 self.inst.record_outputs(time, &mut self.recorder_state)?;
61 self.inst.enter_event_mode().map_err(Into::into)?;
62 if input_event {
63 self.input_state
64 .apply_input::<Linear>(time, &mut self.inst, true, true, true)?;
65 }
66 let mut reset_solver = false;
67 let mut discrete_states_need_update = true;
68 let mut nominals_of_continuous_states_changed = false;
69 let mut values_of_continuous_states_changed = false;
70 while discrete_states_need_update {
71 self.inst
72 .update_discrete_states(
73 &mut discrete_states_need_update,
74 terminate_simulation,
75 &mut nominals_of_continuous_states_changed,
76 &mut values_of_continuous_states_changed,
77 &mut self.next_event_time,
78 )
79 .map_err(Into::into)?;
80 if *terminate_simulation {
81 break;
82 }
83 reset_solver |=
84 nominals_of_continuous_states_changed || values_of_continuous_states_changed;
85 }
86 Ok(reset_solver)
87 }
88}
89
90#[derive(Default, Debug)]
91pub struct SimStats {
92 pub end_time: f64,
94 pub num_steps: usize,
96 pub num_events: usize,
98}
99
100pub fn simulate_with<Imp: FmiSim>(
102 input_data: Option<RecordBatch>,
103 interface: &options::Interface,
104 import: Imp,
105) -> Result<(RecordBatch, SimStats), Error> {
106 match interface {
107 #[cfg(feature = "me")]
108 options::Interface::ModelExchange(options) => import.simulate_me(options, input_data),
109 #[cfg(feature = "cs")]
110 options::Interface::CoSimulation(options) => import.simulate_cs(options, input_data),
111 #[cfg(feature = "se")]
112 options::Interface::ScheduledExecution(options) => unimplemented!(),
113 #[cfg(any(not(feature = "me"), not(feature = "cs")))]
114 _ => Err(fmi::Error::UnsupportedInterface(format!("{}", interface)).into()),
115 }
116}
117
118macro_rules! impl_sim_default_initialize {
119 ($inst:ty) => {
120 impl SimDefaultInitialize for SimState<$inst> {
121 fn default_initialize(&mut self) -> Result<(), Error> {
122 self.inst
123 .enter_initialization_mode(
124 self.sim_params.tolerance,
125 self.sim_params.start_time,
126 Some(self.sim_params.stop_time),
127 )
128 .map_err(fmi::Error::from)?;
129
130 self.inst
131 .exit_initialization_mode()
132 .map_err(fmi::Error::from)?;
133
134 if self.sim_params.event_mode_used {
135 let mut discrete_states_need_update = true;
137 let mut nominals_of_continuous_states_changed = false;
138 let mut values_of_continuous_states_changed = false;
139 while discrete_states_need_update {
140 let mut terminate_simulation = false;
141
142 self.inst
143 .update_discrete_states(
144 &mut discrete_states_need_update,
145 &mut terminate_simulation,
146 &mut nominals_of_continuous_states_changed,
147 &mut values_of_continuous_states_changed,
148 &mut self.next_event_time,
149 )
150 .map_err(fmi::Error::from)?;
151
152 if terminate_simulation {
153 self.inst.terminate().map_err(fmi::Error::from)?;
154 log::error!("update_discrete_states() requested termination.");
155 break;
156 }
157 }
158 }
159 Ok(())
160 }
161 }
162 };
163}
164
165#[cfg(feature = "me")]
166impl_sim_default_initialize!(fmi::fmi2::instance::InstanceME<'_>);
167#[cfg(feature = "cs")]
168impl SimDefaultInitialize for SimState<fmi::fmi2::instance::InstanceCS<'_>> {
169 fn default_initialize(&mut self) -> Result<(), Error> {
170 self.inst
171 .enter_initialization_mode(
172 self.sim_params.tolerance,
173 self.sim_params.start_time,
174 Some(self.sim_params.stop_time),
175 )
176 .map_err(fmi::Error::from)?;
177 self.inst
178 .exit_initialization_mode()
179 .map_err(fmi::Error::from)?;
180
181 Ok(())
182 }
183}
184
185#[cfg(feature = "me")]
186impl_sim_default_initialize!(fmi::fmi3::instance::InstanceME<'_>);
187#[cfg(feature = "cs")]
188impl_sim_default_initialize!(fmi::fmi3::instance::InstanceCS<'_>);
189
190macro_rules! impl_sim_initialize {
191 ($inst:ty) => {
192 impl traits::SimInitialize<$inst> for SimState<$inst> {
193 fn initialize<P: AsRef<Path>>(
194 &mut self,
195 start_values: io::StartValues<<$inst as FmiInstance>::ValueRef>,
196 initial_fmu_state_file: Option<P>,
197 ) -> Result<(), Error> {
198 if let Some(_initial_state_file) = &initial_fmu_state_file {
199 unimplemented!("initial_fmu_state_file");
200 }
202
203 traits::SimApplyStartValues::apply_start_values(self, &start_values)?;
205
206 self.input_state.apply_input::<interpolation::Linear>(
207 self.sim_params.start_time,
208 &mut self.inst,
209 true,
210 true,
211 false,
212 )?;
213
214 if initial_fmu_state_file.is_none() {
216 self.default_initialize()?;
217 }
218
219 Ok(())
220 }
221 }
222 };
223}
224
225#[cfg(feature = "me")]
226impl_sim_initialize!(fmi::fmi2::instance::InstanceME<'_>);
227#[cfg(feature = "me")]
228impl_sim_initialize!(fmi::fmi3::instance::InstanceME<'_>);
229#[cfg(feature = "cs")]
230impl_sim_initialize!(fmi::fmi2::instance::InstanceCS<'_>);
231#[cfg(feature = "cs")]
232impl_sim_initialize!(fmi::fmi3::instance::InstanceCS<'_>);