fmi-sim 0.5.0

A pure Rust FMI simulator
Documentation
use arrow::{
    array::StringArray,
    datatypes::{Field, Fields, Schema},
};
use fmi::{
    fmi2::{
        import::Fmi2Import,
        schema::{Causality, Variability},
    },
    traits::FmiImport,
};

use crate::sim::{io::StartValues, traits::ImportSchemaBuilder};

impl ImportSchemaBuilder for Fmi2Import
where
    Self::ValueRef: From<u32>,
{
    fn inputs_schema(&self) -> Schema {
        let input_fields = self
            .model_description()
            .model_variables
            .variables
            .iter()
            .filter(|v| v.causality == Causality::Input)
            .map(|v| Field::new(&v.name, v.elem.data_type(), false))
            .collect::<Fields>();

        Schema::new(input_fields)
    }

    fn outputs_schema(&self) -> Schema {
        let time = Field::new("time", arrow::datatypes::DataType::Float64, false);
        let output_fields = self
            .model_description()
            .model_variables
            .variables
            .iter()
            .filter(|v| v.causality == Causality::Output)
            .map(|v| Field::new(&v.name, v.elem.data_type(), false))
            .chain(std::iter::once(time))
            .collect::<Fields>();

        Schema::new(output_fields)
    }

    fn continuous_inputs(&self) -> impl Iterator<Item = (Field, Self::ValueRef)> + '_ {
        self.model_description()
            .model_variables
            .variables
            .iter()
            .filter(|v| v.causality == Causality::Input && v.variability == Variability::Continuous)
            .map(|v| {
                (
                    Field::new(&v.name, v.elem.data_type(), false),
                    v.value_reference,
                )
            })
    }

    fn discrete_inputs(&self) -> impl Iterator<Item = (Field, Self::ValueRef)> + '_ {
        self.model_description()
            .model_variables
            .variables
            .iter()
            .filter(|v| {
                v.causality == Causality::Input
                    && (v.variability == Variability::Discrete
                        || v.variability == Variability::Tunable)
            })
            .map(|v| {
                (
                    Field::new(&v.name, v.elem.data_type(), false),
                    v.value_reference,
                )
            })
    }

    fn outputs(&self) -> impl Iterator<Item = (Field, Self::ValueRef)> + '_ {
        self.model_description()
            .model_variables
            .variables
            .iter()
            .filter(|v| v.causality == Causality::Output)
            .map(|v| {
                (
                    Field::new(&v.name, v.elem.data_type(), false),
                    v.value_reference,
                )
            })
    }

    fn parse_start_values(
        &self,
        start_values: &[String],
    ) -> anyhow::Result<crate::sim::io::StartValues<Self::ValueRef>> {
        let mut variables = vec![];

        for start_value in start_values {
            let (name, value) = start_value
                .split_once('=')
                .ok_or_else(|| anyhow::anyhow!("Invalid start value: {}", start_value))?;

            let var = self
                .model_description()
                .model_variables
                .variables
                .iter()
                .find(|v| v.name == name)
                .ok_or_else(|| {
                    anyhow::anyhow!(
                        "Invalid variable name: {name}. Valid variables are: {valid:?}",
                        valid = self
                            .model_description()
                            .model_variables
                            .variables
                            .iter()
                            .map(|v| &v.name)
                            .collect::<Vec<_>>()
                    )
                })?;

            let dt = var.elem.data_type();
            let ary = StringArray::from(vec![value.to_string()]);
            let ary = arrow::compute::cast(&ary, &dt)
                .map_err(|e| anyhow::anyhow!("Error casting type: {e}"))?;

            variables.push((var.value_reference, ary));
        }

        Ok(StartValues {
            structural_parameters: vec![],
            variables,
        })
    }
}