use crate::{
FloatExt, SimulationError, XResult,
simulation::prelude::{FirstPassageTime, Moment, OccupationTime, TAMSD},
};
pub trait ContinuousProcess<T: FloatExt = f64>: Send + Sync {
fn simulate(&self, duration: T, time_step: T) -> XResult<(Vec<T>, Vec<T>)>;
fn displacement(&self, duration: T, time_step: T) -> XResult<T> {
let (_, x) = self.simulate(duration, time_step)?;
match (x.first(), x.last()) {
(Some(first), Some(last)) => Ok(*last - *first),
_ => Err(SimulationError::Unknown.into()),
}
}
fn start(&self) -> T;
fn end(&self, duration: T, time_step: T) -> XResult<T> {
let delta_x = self.displacement(duration, time_step)?;
Ok(self.start() + delta_x)
}
fn mean(&self, duration: T, particles: usize, time_step: T) -> XResult<T>
where
Self: ContinuousTrajectoryTrait<T>,
{
let traj = self.duration(duration)?;
traj.mean(particles, time_step)
}
fn msd(&self, duration: T, particles: usize, time_step: T) -> XResult<T>
where
Self: ContinuousTrajectoryTrait<T>,
{
let traj = self.duration(duration)?;
traj.msd(particles, time_step)
}
fn raw_moment(&self, duration: T, order: i32, particles: usize, time_step: T) -> XResult<T>
where
Self: ContinuousTrajectoryTrait<T>,
{
let traj = self.duration(duration)?;
traj.raw_moment(order, particles, time_step)
}
fn central_moment(&self, duration: T, order: i32, particles: usize, time_step: T) -> XResult<T>
where
Self: ContinuousTrajectoryTrait<T>,
{
let traj = self.duration(duration)?;
traj.central_moment(order, particles, time_step)
}
fn frac_raw_moment(&self, duration: T, order: T, particles: usize, time_step: T) -> XResult<T>
where
Self: ContinuousTrajectoryTrait<T>,
{
let traj = self.duration(duration)?;
traj.frac_raw_moment(order, particles, time_step)
}
fn frac_central_moment(
&self,
duration: T,
order: T,
particles: usize,
time_step: T,
) -> XResult<T>
where
Self: ContinuousTrajectoryTrait<T>,
{
let traj = self.duration(duration)?;
traj.frac_central_moment(order, particles, time_step)
}
fn fpt(&self, domain: (T, T), max_duration: T, time_step: T) -> XResult<Option<T>>
where
Self: Sized,
{
let fpt = FirstPassageTime::new(self, domain)?;
fpt.simulate(max_duration, time_step)
}
fn occupation_time(&self, domain: (T, T), duration: T, time_step: T) -> XResult<T>
where
Self: Sized,
{
let ot = OccupationTime::new(self, domain, duration)?;
ot.simulate(time_step)
}
fn tamsd(&self, duration: T, delta: T, time_step: T, quad_order: usize) -> XResult<T>
where
Self: Sized,
{
let tamsd = TAMSD::new(self, duration, delta)?;
tamsd.simulate(time_step, quad_order)
}
fn eatamsd(
&self,
duration: T,
delta: T,
particles: usize,
time_step: T,
quad_order: usize,
) -> XResult<T>
where
Self: Sized,
{
let tamsd = TAMSD::new(self, duration, delta)?;
tamsd.mean(particles, time_step, quad_order)
}
}
#[derive(Debug, Clone)]
pub struct ContinuousTrajectory<SP, T: FloatExt = f64>
where
SP: ContinuousProcess<T> + Clone,
{
pub(crate) sp: SP,
pub(crate) duration: T,
}
pub trait ContinuousTrajectoryTrait<T: FloatExt>: ContinuousProcess<T> + Clone {
fn duration(&self, duration_arg: T) -> XResult<ContinuousTrajectory<Self, T>> {
let traj = ContinuousTrajectory::new(self.clone(), duration_arg)?;
Ok(traj)
}
}
impl<T: FloatExt, SP: ContinuousProcess<T> + Clone> ContinuousTrajectoryTrait<T> for SP {}
impl<T: FloatExt, SP: ContinuousProcess<T> + Clone> ContinuousTrajectory<SP, T> {
pub fn new(sp: SP, duration: T) -> XResult<Self> {
if duration <= T::zero() {
return Err(SimulationError::InvalidParameters(format!(
"The `duration` must be positive, got {duration:?}"
))
.into());
}
Ok(Self { sp, duration })
}
pub fn get_process(&self) -> &SP {
&self.sp
}
pub fn get_duration(&self) -> T {
self.duration
}
pub fn simulate(&self, time_step: T) -> XResult<(Vec<T>, Vec<T>)> {
self.sp.simulate(self.duration, time_step)
}
}