pub mod chain;
pub mod config;
pub mod methods;
pub mod scenario;
pub use config::{IntegratorKind, SolverConfiguration, StepControl, TimeConfiguration};
pub use scenario::{Domain, DomainId, Scenario};
use crate::context::error::OxiflowError;
#[non_exhaustive]
#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SimulationResult {
pub states: Vec<crate::context::value::ContextValue>,
pub times: Vec<f64>,
pub n_steps: usize,
pub metadata: std::collections::HashMap<String, f64>,
}
impl SimulationResult {
pub fn len(&self) -> usize {
self.states.len()
}
pub fn is_empty(&self) -> bool {
self.states.is_empty()
}
pub fn t_final(&self) -> Option<f64> {
self.times.last().copied()
}
}
pub trait Solver: Send + Sync {
fn solve(
&self,
scenario: &Scenario,
config: &SolverConfiguration,
) -> Result<SimulationResult, OxiflowError>;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::context::value::ContextValue;
use nalgebra::DVector;
#[test]
fn simulation_result_len() {
let r = SimulationResult {
states: vec![ContextValue::ScalarField(DVector::from_element(5, 0.0))],
times: vec![1.0],
n_steps: 10,
metadata: std::collections::HashMap::new(),
};
assert_eq!(r.len(), 1);
assert!(!r.is_empty());
}
#[test]
fn simulation_result_t_final() {
let r = SimulationResult {
states: vec![
ContextValue::ScalarField(DVector::from_element(5, 0.0)),
ContextValue::ScalarField(DVector::from_element(5, 1.0)),
],
times: vec![0.0, 1.0],
n_steps: 2,
metadata: std::collections::HashMap::new(),
};
assert_eq!(r.t_final(), Some(1.0));
}
#[test]
fn empty_result() {
let r = SimulationResult {
states: vec![],
times: vec![],
n_steps: 0,
metadata: std::collections::HashMap::new(),
};
assert!(r.is_empty());
assert_eq!(r.t_final(), None);
}
#[test]
fn solver_is_object_safe() {
fn assert_object_safe<T: Solver + ?Sized>() {}
assert_object_safe::<dyn Solver>();
}
}