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