use super::*;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Tolerance {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub abs: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub rel: Option<f64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct TimeSpan {
pub start: f64,
pub end: f64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum AssertionReference {
FromFile(FromFileReference),
Expression(Box<Expr>),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct FromFileReference {
#[serde(rename = "type")]
pub ref_type: String,
pub path: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub format: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ModelTestAssertion {
pub variable: String,
pub time: f64,
pub expected: f64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tolerance: Option<Tolerance>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub coords: Option<HashMap<String, f64>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reduce: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reference: Option<AssertionReference>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ModelTest {
pub id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub initial_conditions: Option<HashMap<String, InlineValue>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub parameter_overrides: Option<HashMap<String, InlineValue>>,
pub time_span: TimeSpan,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tolerance: Option<Tolerance>,
pub assertions: Vec<ModelTestAssertion>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub expression_template_imports: Vec<serde_json::Value>,
}
impl ModelTest {
pub fn scalar_initial_conditions(&self) -> HashMap<String, f64> {
scalar_entries(self.initial_conditions.as_ref())
}
pub fn scalar_parameter_overrides(&self) -> HashMap<String, f64> {
scalar_entries(self.parameter_overrides.as_ref())
}
}
fn scalar_entries(m: Option<&HashMap<String, InlineValue>>) -> HashMap<String, f64> {
m.map(|m| {
m.iter()
.filter_map(|(k, v)| v.as_scalar().map(|x| (k.clone(), x)))
.collect()
})
.unwrap_or_default()
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SweepRange {
pub start: f64,
pub stop: f64,
pub count: i64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub scale: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SweepDimension {
pub parameter: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub values: Option<Vec<f64>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub range: Option<SweepRange>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ParameterSweep {
#[serde(rename = "type")]
pub sweep_type: String,
pub dimensions: Vec<SweepDimension>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct PlotAxis {
pub variable: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum PlotY {
Axis(PlotAxis),
Axes(Vec<PlotAxis>),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct PlotValue {
pub variable: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub at_time: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reduce: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct PlotSeries {
pub name: String,
pub variable: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Plot {
pub id: String,
#[serde(rename = "type")]
pub plot_type: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
pub x: PlotAxis,
pub y: PlotY,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub value: Option<PlotValue>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub series: Option<Vec<PlotSeries>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub at_time: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pinned_coords: Option<HashMap<String, f64>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub contours: Option<Vec<f64>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ModelAnalysis {
pub id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub initial_state: Option<serde_json::Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub parameters: Option<HashMap<String, f64>>,
pub time_span: TimeSpan,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub parameter_sweep: Option<ParameterSweep>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub plots: Option<Vec<Plot>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub expression_template_imports: Vec<serde_json::Value>,
}