use crate::math::tensor::rank_2::vector_list::VectorList;
use crate::math::tensor::{RandType, TensorRandFiller};
use crate::space::continuous::sampling::{
VectorSamplingError, VectorSamplingMethod, sample_vectors,
};
use rayon::prelude::*;
use crate::engines::soa::phys_obj::{AttrsCore, AttrsError, AttrsMeta, PhysObj};
pub use crate::models::particles::attrs::{
ALIVE_TRUE, ATTR_A, ATTR_ALIVE, ATTR_M, ATTR_M_INV, ATTR_R, ATTR_RIGID, ATTR_V, RIGID_FALSE,
};
use crate::models::particles::state::{ParticleStateError, gather_inverse_mass};
#[derive(Debug, Clone, PartialEq)]
pub enum MassiveParticlesError {
Attrs(AttrsError),
InvalidDimension {
dim: usize,
},
InvalidParticleCount {
n: usize,
},
InvalidTau {
tau: f64,
},
InvalidMassInv {
index: usize,
value: f64,
},
InvalidMassInvShape {
expected_dim: usize,
got_dim: usize,
},
InconsistentParticleCount {
expected: usize,
got: usize,
},
Distribution {
msg: String,
},
}
impl From<AttrsError> for MassiveParticlesError {
fn from(value: AttrsError) -> Self {
Self::Attrs(value)
}
}
impl From<VectorSamplingError> for MassiveParticlesError {
fn from(value: VectorSamplingError) -> Self {
Self::Distribution {
msg: format!("{value:?}"),
}
}
}
impl From<ParticleStateError> for MassiveParticlesError {
fn from(value: ParticleStateError) -> Self {
match value {
ParticleStateError::Attrs(err) => Self::Attrs(err),
ParticleStateError::InvalidAttrShape {
expected_dim,
got_dim,
..
} => Self::InvalidMassInvShape {
expected_dim,
got_dim,
},
ParticleStateError::InconsistentParticleCount { expected, got, .. } => {
Self::InconsistentParticleCount { expected, got }
}
}
}
}
pub fn create_template(dim: usize, num_particles: usize) -> Result<PhysObj, MassiveParticlesError> {
if dim == 0 {
return Err(MassiveParticlesError::InvalidDimension { dim });
}
if num_particles == 0 {
return Err(MassiveParticlesError::InvalidParticleCount { n: num_particles });
}
let mut core = AttrsCore::empty();
core.allocate::<f64>(ATTR_R, dim, num_particles)?;
core.allocate::<f64>(ATTR_V, dim, num_particles)?;
core.allocate::<f64>(ATTR_A, dim, num_particles)?;
let mut m = VectorList::<f64>::empty(1, num_particles);
m.fill(1.0);
core.insert(ATTR_M, m)?;
let mut m_inv = VectorList::<f64>::empty(1, num_particles);
m_inv.fill(1.0);
core.insert(ATTR_M_INV, m_inv)?;
let mut alive = VectorList::<u8>::empty(1, num_particles);
alive.fill(ALIVE_TRUE);
core.insert(ATTR_ALIVE, alive)?;
let mut rigid = VectorList::<u8>::empty(1, num_particles);
rigid.fill(RIGID_FALSE);
core.insert(ATTR_RIGID, rigid)?;
let meta = AttrsMeta::new(0, "particles", "canonical massive-particle state");
Ok(PhysObj::new(meta, core))
}
pub fn randomize_r(
phys_obj: &mut PhysObj,
method: VectorSamplingMethod<'_>,
) -> Result<(), MassiveParticlesError> {
let r = phys_obj.core.get_mut::<f64>(ATTR_R)?;
sample_vectors(r, method)?;
Ok(())
}
#[derive(Debug, Clone, PartialEq)]
pub enum VelocitySamplingMethod<'a> {
Uniform { low: f64, high: f64 },
MaxwellBoltzmann { tau: f64 },
GaussianPerAxis {
mean: &'a [f64],
std: &'a [f64],
},
}
pub fn randomize_v(
phys_obj: &mut PhysObj,
method: VelocitySamplingMethod<'_>,
) -> Result<(), MassiveParticlesError> {
match method {
VelocitySamplingMethod::Uniform { low, high } => {
let v = phys_obj.core.get_mut::<f64>(ATTR_V)?;
sample_vectors(v, VectorSamplingMethod::Uniform { low, high })?;
Ok(())
}
VelocitySamplingMethod::GaussianPerAxis { mean, std } => {
let v = phys_obj.core.get_mut::<f64>(ATTR_V)?;
sample_vectors(v, VectorSamplingMethod::GaussianPerAxis { mean, std })?;
Ok(())
}
VelocitySamplingMethod::MaxwellBoltzmann { tau } => {
if !tau.is_finite() || tau < 0.0 {
return Err(MassiveParticlesError::InvalidTau { tau });
}
let (dim, n) = {
let v = phys_obj.core.get::<f64>(ATTR_V)?;
(v.dim(), v.num_vectors())
};
let m_inv_values = gather_inverse_mass(phys_obj, n)?;
for (i, &m_inv_i) in m_inv_values.iter().enumerate() {
if !m_inv_i.is_finite() || m_inv_i < 0.0 {
return Err(MassiveParticlesError::InvalidMassInv {
index: i,
value: m_inv_i,
});
}
}
let v = phys_obj.core.get_mut::<f64>(ATTR_V)?;
if tau == 0.0 {
v.as_tensor_mut().data.par_iter_mut().for_each(|x| *x = 0.0);
return Ok(());
}
let mut filler = TensorRandFiller::new(
RandType::Normal {
mean: 0.0,
std: 1.0,
},
None,
);
filler.refresh(v.as_tensor_mut());
v.as_tensor_mut()
.data
.par_chunks_mut(dim)
.zip(m_inv_values.par_iter())
.for_each(|(row, &m_inv_i)| {
let sigma = (tau * m_inv_i).sqrt();
if sigma == 0.0 {
for x in row.iter_mut() {
*x = 0.0;
}
return;
}
for x in row.iter_mut().take(dim) {
*x *= sigma;
}
});
Ok(())
}
}
}