use numra_core::Scalar;
pub trait DdeSystem<S: Scalar> {
fn dim(&self) -> usize;
fn delays(&self) -> Vec<S>;
fn delays_at(&self, _t: S, _y: &[S]) -> Vec<S> {
self.delays()
}
fn n_delays(&self) -> usize {
self.delays().len()
}
fn rhs(&self, t: S, y: &[S], y_delayed: &[&[S]], dydt: &mut [S]);
fn has_state_dependent_delays(&self) -> bool {
false
}
}
#[derive(Clone, Debug)]
pub struct DdeOptions<S: Scalar> {
pub rtol: S,
pub atol: S,
pub h0: Option<S>,
pub h_max: S,
pub h_min: S,
pub max_steps: usize,
pub t_eval: Option<Vec<S>>,
pub dense_output: bool,
pub track_discontinuities: bool,
pub discontinuity_order: usize,
}
impl<S: Scalar> Default for DdeOptions<S> {
fn default() -> Self {
Self {
rtol: S::from_f64(1e-6),
atol: S::from_f64(1e-9),
h0: None,
h_max: S::INFINITY,
h_min: S::from_f64(1e-14),
max_steps: 100_000,
t_eval: None,
dense_output: true, track_discontinuities: false,
discontinuity_order: 0,
}
}
}
impl<S: Scalar> DdeOptions<S> {
pub fn rtol(mut self, rtol: S) -> Self {
self.rtol = rtol;
self
}
pub fn atol(mut self, atol: S) -> Self {
self.atol = atol;
self
}
pub fn h0(mut self, h0: S) -> Self {
self.h0 = Some(h0);
self
}
pub fn h_max(mut self, h_max: S) -> Self {
self.h_max = h_max;
self
}
pub fn max_steps(mut self, max_steps: usize) -> Self {
self.max_steps = max_steps;
self
}
pub fn track_discontinuities(mut self, track: bool) -> Self {
self.track_discontinuities = track;
self
}
pub fn discontinuity_order(mut self, order: usize) -> Self {
self.discontinuity_order = order;
self
}
}
#[derive(Clone, Debug, Default)]
pub struct DdeStats {
pub n_eval: usize,
pub n_accept: usize,
pub n_reject: usize,
pub n_discontinuities: usize,
}
#[derive(Clone, Debug)]
pub struct DdeResult<S: Scalar> {
pub t: Vec<S>,
pub y: Vec<S>,
pub dim: usize,
pub stats: DdeStats,
pub success: bool,
pub message: String,
}
impl<S: Scalar> DdeResult<S> {
pub fn new(t: Vec<S>, y: Vec<S>, dim: usize, stats: DdeStats) -> Self {
Self {
t,
y,
dim,
stats,
success: true,
message: String::new(),
}
}
pub fn failed(message: String, stats: DdeStats) -> Self {
Self {
t: Vec::new(),
y: Vec::new(),
dim: 0,
stats,
success: false,
message,
}
}
pub fn len(&self) -> usize {
self.t.len()
}
pub fn is_empty(&self) -> bool {
self.t.is_empty()
}
pub fn t_final(&self) -> Option<S> {
self.t.last().copied()
}
pub fn y_final(&self) -> Option<Vec<S>> {
if self.t.is_empty() {
None
} else {
let start = (self.t.len() - 1) * self.dim;
Some(self.y[start..start + self.dim].to_vec())
}
}
pub fn y_at(&self, i: usize) -> &[S] {
let start = i * self.dim;
&self.y[start..start + self.dim]
}
pub fn iter(&self) -> impl Iterator<Item = (S, &[S])> {
self.t
.iter()
.enumerate()
.map(move |(i, &t)| (t, self.y_at(i)))
}
}
pub trait DdeSolver<S: Scalar> {
fn solve<Sys, H>(
system: &Sys,
t0: S,
tf: S,
history: &H,
options: &DdeOptions<S>,
) -> Result<DdeResult<S>, String>
where
Sys: DdeSystem<S>,
H: Fn(S) -> Vec<S>;
}
#[cfg(test)]
mod tests {
use super::*;
struct TestDde;
impl DdeSystem<f64> for TestDde {
fn dim(&self) -> usize {
1
}
fn delays(&self) -> Vec<f64> {
vec![1.0]
}
fn rhs(&self, _t: f64, y: &[f64], y_delayed: &[&[f64]], dydt: &mut [f64]) {
dydt[0] = -y[0] + y_delayed[0][0];
}
}
#[test]
fn test_dde_system_trait() {
let sys = TestDde;
assert_eq!(sys.dim(), 1);
assert_eq!(sys.n_delays(), 1);
assert_eq!(sys.delays(), vec![1.0]);
let mut dydt = [0.0];
let y_delayed = [&[0.5][..]];
sys.rhs(0.0, &[1.0], &y_delayed, &mut dydt);
assert!((dydt[0] - (-0.5)).abs() < 1e-10);
}
#[test]
fn test_dde_options() {
let opts: DdeOptions<f64> = DdeOptions::default().rtol(1e-8).atol(1e-10);
assert!((opts.rtol - 1e-8).abs() < 1e-15);
assert!((opts.atol - 1e-10).abs() < 1e-15);
}
}