#![cfg_attr(feature = "ode_solver", doc = "```no_run")]
#![cfg_attr(not(feature = "ode_solver"), doc = "```ignore")]
#![doc = ::embed_doc_image::embed_image!("traffic_ode_solver", "doc/imgs/traffic_ode_solver.png")]
#![cfg_attr(docsrs, feature(doc_cfg))]
pub trait ExternalVelocity<T, X = T> {
fn eval(&self, t: T, x: X) -> X;
}
pub trait Interaction<T, X = T> {
fn eval<P>(&self, t: T, x: X, p: P) -> X
where
P: IntoIterator<Item = X>;
}
pub trait Mobility<T, X = T> {
fn eval(&self, t: T, x: X) -> X;
}
mod _zero;
pub use _zero::{OneMobility, ZeroInteraction, ZeroVelocity};
pub struct Velocity<V>(V);
impl<V> Velocity<V> {
#[allow(non_snake_case)]
pub fn new(V: V) -> Self {
Self(V)
}
}
impl<T, X, V> ExternalVelocity<T, X> for Velocity<V>
where
V: Fn(T, X) -> X,
{
#[inline]
fn eval(&self, t: T, x: X) -> X {
self.0(t, x)
}
}
impl<T, X, V> ExternalVelocity<T, X> for V
where
V: Fn(T, X) -> X,
{
#[inline]
fn eval(&self, t: T, x: X) -> X {
self(t, x)
}
}
impl<T, X, F> Mobility<T, X> for F
where
F: Fn(T, X) -> X,
{
#[inline]
fn eval(&self, t: T, x: X) -> X {
self(t, x)
}
}
pub struct SampledInteraction<Wprime>(Wprime);
impl<Wprime> SampledInteraction<Wprime> {
#[allow(non_snake_case)]
pub fn new(Wprime: Wprime) -> Self {
Self(Wprime)
}
}
impl<T, X, Wprime> Interaction<T, X> for SampledInteraction<Wprime>
where
T: Copy,
X: Copy + num_traits::real::Real,
Wprime: Fn(T, X) -> X,
{
fn eval<P>(&self, t: T, x: X, p: P) -> X
where
P: IntoIterator<Item = X>,
{
let mut sum = X::zero();
let mut len = 0;
for y in p.into_iter() {
len += 1;
let r = x - y;
if r != X::zero() {
sum = sum + self.0(t, r);
}
}
-X::from(sum).unwrap() / X::from(len - 1).unwrap()
}
}
pub struct IntegratedInteraction<W>(W);
impl<W> IntegratedInteraction<W> {
#[allow(non_snake_case)]
pub fn new(W: W) -> Self {
Self(W)
}
}
impl<T, X, W> Interaction<T, X> for IntegratedInteraction<W>
where
T: Copy,
X: Copy + num_traits::real::Real,
W: Fn(T, X) -> X,
{
fn eval<P>(&self, t: T, x: X, p: P) -> X
where
P: IntoIterator<Item = X>,
{
let mut p = p.into_iter().peekable();
let mut total = X::zero();
let mut left_dens = X::zero();
let mut len = 0;
while let Some(y) = p.next() {
len += 1;
let right_dens = if let Some(z) = p.peek() {
(*z - y).recip()
} else {
X::zero()
};
total = total + self.0(t, x - y) * (right_dens - left_dens);
left_dens = right_dens;
}
-total / X::from(len - 1).unwrap()
}
}
pub struct SingleSpeciesModel<T, X, Vel, Int, Mob> {
pub vel: Vel,
pub int: Int,
pub mob: Mob,
_t: std::marker::PhantomData<T>,
_x: std::marker::PhantomData<X>,
}
impl<T, X, Vel, Int, Mob> SingleSpeciesModel<T, X, Vel, Int, Mob>
where
Vel: ExternalVelocity<T, X>,
Int: Interaction<T, X>,
Mob: Mobility<T, X>,
{
pub fn new(vel: Vel, int: Int, mob: Mob) -> Self {
Self {
vel,
int,
mob,
_t: std::marker::PhantomData,
_x: std::marker::PhantomData,
}
}
}
#[cfg(feature = "ode_solver")]
pub use _ode_solver::*;
#[cfg(feature = "ode_solver")]
#[cfg_attr(docsrs, doc(cfg(feature = "ode_solver")))]
mod _ode_solver {
use super::*;
use ode_solvers::{SVector, System};
impl<X, Vel, Int, Mob, const N: usize> System<SVector<X, N>>
for SingleSpeciesModel<f64, X, Vel, Int, Mob>
where
Vel: ExternalVelocity<f64, X>,
Int: Interaction<f64, X>,
Mob: Mobility<f64, X>,
X: num_traits::real::Real + nalgebra::base::Scalar,
{
fn system(&self, t: f64, x: &SVector<X, N>, dx: &mut SVector<X, N>) {
let n = x.len();
let f = X::from(n - 1).unwrap().recip();
for i in 0..n {
let vel = self.vel.eval(t, x[i]);
let int = self.int.eval(t, x[i], x.iter().cloned());
let tot = vel + int;
let dens = if tot.is_sign_positive() {
if i < n - 1 {
f / (x[i + 1] - x[i])
} else {
X::zero()
}
} else {
if i > 0 {
f / (x[i] - x[i - 1])
} else {
X::zero()
}
};
let m = self.mob.eval(t, dens);
dx[i] = tot * m;
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn zero_velocity() {
assert_eq!(ZeroVelocity.eval(0.0, 0.0), 0.0);
}
#[test]
fn zero_interaction_potential() {
let p = [1., 2., 3.];
assert_eq!(ZeroInteraction.eval(0.0, 0.0, p), 0.0);
}
#[test]
fn velocity() {
let vel = Velocity(|t: f64, x: f64| t + x);
assert_eq!(vel.eval(2., 3.), 5.);
}
#[test]
fn sampled_interaction() {
let int = SampledInteraction(|_t: f64, x: f64| x);
let p = [0., 1., 2., 3.];
assert_eq!(int.eval(0., 0., p), (0. + 1. + 2. + 3.) / (4. - 1.));
}
#[test]
fn integrated_interaction() {
let int = IntegratedInteraction(|_t: f64, x: f64| x * x / 2.);
let p = [0., 1., 2., 3.];
assert_eq!(int.eval(0., 0., p), (3. * 3. / 2. - 0.) / 3.);
}
}