Skip to main content

fmi_schema/fmi2/
scalar_variable.rs

1use std::{fmt::Display, str::FromStr};
2
3/// Enumeration that defines the causality of the variable.
4#[derive(Clone, Default, PartialEq, Debug)]
5pub enum Causality {
6    Parameter,
7    CalculatedParameter,
8    Input,
9    Output,
10    #[default]
11    Local,
12    Independent,
13}
14
15impl FromStr for Causality {
16    type Err = String;
17
18    fn from_str(s: &str) -> Result<Self, Self::Err> {
19        match s {
20            "parameter" => Ok(Causality::Parameter),
21            "calculatedParameter" => Ok(Causality::CalculatedParameter),
22            "input" => Ok(Causality::Input),
23            "output" => Ok(Causality::Output),
24            "local" => Ok(Causality::Local),
25            "independent" => Ok(Causality::Independent),
26            _ => Err(format!("Invalid Causality: {}", s)),
27        }
28    }
29}
30
31impl Display for Causality {
32    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33        let s = match self {
34            Causality::Parameter => "parameter",
35            Causality::CalculatedParameter => "calculatedParameter",
36            Causality::Input => "input",
37            Causality::Output => "output",
38            Causality::Local => "local",
39            Causality::Independent => "independent",
40        };
41        write!(f, "{}", s)
42    }
43}
44
45/// Enumeration that defines the time dependency of the variable, in other words it defines the time instants when a variable can change its value.
46///
47/// The default is [`Variability::Continuous`].
48#[derive(Clone, Copy, Default, PartialEq, Debug)]
49pub enum Variability {
50    /// The value of the variable never changes.
51    Constant,
52    /// The value of the variable is fixed after initialization, in other words after `exit_initialization_mode()` was called the variable value does not change anymore.
53    Fixed,
54    /// The value of the variable is constant between external events (ModelExchange) and between Communication Points (CoSimulation) due to changing variables with causality = "parameter" or "input" and variability = "tunable".
55    Tunable,
56    /// * ModelExchange: The value of the variable is constant between external and internal events (= time, state, step events defined implicitly in the FMU).
57    /// * CoSimulation: By convention, the variable is from a "real" sampled data system and its value is only changed at Communication Points (also inside the slave).
58    Discrete,
59    /// Only a variable of type = "Real" can be "continuous".
60    /// * ModelExchange: No restrictions on value changes.
61    /// * CoSimulation: By convention, the variable is from a differential
62    #[default]
63    Continuous,
64}
65
66impl FromStr for Variability {
67    type Err = String;
68
69    fn from_str(s: &str) -> Result<Self, Self::Err> {
70        match s {
71            "constant" => Ok(Variability::Constant),
72            "fixed" => Ok(Variability::Fixed),
73            "tunable" => Ok(Variability::Tunable),
74            "discrete" => Ok(Variability::Discrete),
75            "continuous" => Ok(Variability::Continuous),
76            _ => Err(format!("Invalid Variability: {}", s)),
77        }
78    }
79}
80
81impl Display for Variability {
82    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83        let s = match self {
84            Variability::Constant => "constant",
85            Variability::Fixed => "fixed",
86            Variability::Tunable => "tunable",
87            Variability::Discrete => "discrete",
88            Variability::Continuous => "continuous",
89        };
90        write!(f, "{}", s)
91    }
92}
93
94#[derive(Clone, Default, PartialEq, Debug)]
95pub enum Initial {
96    #[default]
97    Exact,
98    Approx,
99    Calculated,
100}
101
102impl FromStr for Initial {
103    type Err = String;
104
105    fn from_str(s: &str) -> Result<Self, Self::Err> {
106        match s {
107            "exact" => Ok(Initial::Exact),
108            "approx" => Ok(Initial::Approx),
109            "calculated" => Ok(Initial::Calculated),
110            _ => Err(format!("Invalid Initial: {}", s)),
111        }
112    }
113}
114
115impl Display for Initial {
116    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
117        let s = match self {
118            Initial::Exact => "exact",
119            Initial::Approx => "approx",
120            Initial::Calculated => "calculated",
121        };
122        write!(f, "{}", s)
123    }
124}
125
126#[derive(Clone, Default, PartialEq, Debug, hard_xml::XmlRead, hard_xml::XmlWrite)]
127#[xml(tag = "Real")]
128pub struct Real {
129    /// If present, name of type defined with TypeDefinitions / SimpleType providing defaults.
130    #[xml(attr = "declaredType")]
131    pub declared_type: Option<String>,
132
133    /// Value before initialization, if initial=exact or approx.
134    /// max >= start >= min required
135    #[xml(attr = "start")]
136    pub start: Option<f64>,
137
138    /// If present, this variable is the derivative of variable with ScalarVariable index
139    /// "derivative".
140    #[xml(attr = "derivative")]
141    pub derivative: Option<u32>,
142
143    /// Only for ModelExchange and if variable is a continuous-time state:
144    /// If true, state can be reinitialized at an event by the FMU
145    /// If false, state will never be reinitialized at an event by the FMU
146    #[xml(attr = "reinit")]
147    pub reinit: Option<bool>,
148}
149
150#[derive(Clone, Default, PartialEq, Debug, hard_xml::XmlRead, hard_xml::XmlWrite)]
151#[xml(tag = "Integer")]
152pub struct Integer {
153    /// If present, name of type defined with TypeDefinitions / SimpleType providing defaults.
154    #[xml(attr = "declaredType")]
155    pub declared_type: Option<String>,
156
157    /// Value before initialization, if initial=exact or approx.
158    /// max >= start >= min required
159    #[xml(attr = "start")]
160    pub start: Option<i32>,
161}
162
163#[derive(Clone, Default, PartialEq, Debug, hard_xml::XmlRead, hard_xml::XmlWrite)]
164#[xml(tag = "Boolean")]
165pub struct Boolean {
166    /// If present, name of type defined with TypeDefinitions / SimpleType providing defaults.
167    #[xml(attr = "declaredType")]
168    pub declared_type: Option<String>,
169
170    /// Value before initialization, if initial=exact or approx.
171    #[xml(attr = "start")]
172    pub start: Option<bool>,
173}
174
175#[derive(Clone, PartialEq, Debug, hard_xml::XmlRead, hard_xml::XmlWrite)]
176pub enum ScalarVariableElement {
177    #[xml(tag = "Real")]
178    Real(Real),
179    #[xml(tag = "Integer")]
180    Integer(Integer),
181    #[xml(tag = "Boolean")]
182    Boolean(Boolean),
183    #[xml(tag = "String")]
184    String,
185    #[xml(tag = "Enumeration")]
186    Enumeration,
187}
188
189impl Default for ScalarVariableElement {
190    fn default() -> Self {
191        Self::Real(Real::default())
192    }
193}
194
195#[cfg(feature = "arrow")]
196impl ScalarVariableElement {
197    pub fn data_type(&self) -> arrow::datatypes::DataType {
198        match self {
199            ScalarVariableElement::Real(_) => arrow::datatypes::DataType::Float64,
200            ScalarVariableElement::Integer(_) => arrow::datatypes::DataType::Int32,
201            ScalarVariableElement::Boolean(_) => arrow::datatypes::DataType::Boolean,
202            ScalarVariableElement::String => arrow::datatypes::DataType::Utf8,
203            ScalarVariableElement::Enumeration => arrow::datatypes::DataType::Int32,
204        }
205    }
206}
207
208#[derive(Default, Debug, hard_xml::XmlRead, hard_xml::XmlWrite)]
209#[xml(tag = "ScalarVariable", strict(unknown_attribute, unknown_element))]
210pub struct ScalarVariable {
211    /// The full, unique name of the variable.
212    #[xml(attr = "name")]
213    pub name: String,
214
215    /// A handle of the variable to efficiently identify the variable value in the model interface.
216    #[xml(attr = "valueReference")]
217    pub value_reference: u32,
218
219    /// An optional description string describing the meaning of the variable.
220    #[xml(attr = "description")]
221    pub description: Option<String>,
222
223    /// Enumeration that defines the causality of the variable.
224    #[xml(attr = "causality", default)]
225    pub causality: Causality,
226
227    /// Enumeration that defines the time dependency of the variable, in other words it defines the
228    /// time instants when a variable can change its value.
229    #[xml(attr = "variability")]
230    pub variability: Option<Variability>,
231
232    /// Enumeration that defines how the variable is initialized. It is not allowed to provide a
233    /// value for initial if `causality`=`Input` or `Independent`.
234    #[xml(attr = "initial")]
235    pub initial: Option<Initial>,
236
237    #[xml(
238        child = "Real",
239        child = "Integer",
240        child = "Boolean",
241        child = "String",
242        child = "Enumeration"
243    )]
244    pub elem: ScalarVariableElement,
245}
246
247impl ScalarVariable {
248    pub fn is_continuous_input(&self) -> bool {
249        matches!(
250            (&self.elem, &self.causality),
251            (ScalarVariableElement::Real { .. }, Causality::Input)
252        )
253    }
254}
255
256#[cfg(test)]
257mod tests {
258    use hard_xml::XmlRead;
259
260    use super::*;
261
262    #[test]
263    fn test_scalar_variable() {
264        let s = r#"
265        <ScalarVariable
266            name="inertia1.J"
267            valueReference="1073741824"
268            description="Moment of load inertia"
269            causality="parameter"
270            variability="fixed">
271            <Real declaredType="Modelica.SIunits.Inertia" start="1"/>
272        </ScalarVariable>
273        "#;
274        let sv = ScalarVariable::from_str(s).unwrap();
275        assert_eq!(sv.name, "inertia1.J");
276        assert_eq!(sv.value_reference, 1073741824);
277        assert_eq!(sv.description, Some("Moment of load inertia".into()));
278        assert_eq!(sv.causality, Causality::Parameter);
279        assert_eq!(sv.variability, Some(Variability::Fixed));
280        assert_eq!(
281            sv.elem,
282            ScalarVariableElement::Real(Real {
283                declared_type: Some("Modelica.SIunits.Inertia".to_string()),
284                start: Some(1.0),
285                derivative: None,
286                reinit: None
287            })
288        );
289    }
290}