Skip to main content

phyz_particle/
particle.rs

1//! Particle state representation for MPM.
2
3use phyz_math::{Mat3, Vec3};
4
5use crate::material::Material;
6
7/// A material point particle.
8#[derive(Debug, Clone)]
9pub struct Particle {
10    /// Position in world frame (m).
11    pub x: Vec3,
12    /// Velocity (m/s).
13    pub v: Vec3,
14    /// Mass (kg).
15    pub mass: f64,
16    /// Reference volume (m³).
17    pub volume: f64,
18    /// Deformation gradient F.
19    pub f: Mat3,
20    /// Affine velocity field C (for APIC).
21    pub c: Mat3,
22    /// Determinant of F (volume change ratio).
23    pub j: f64,
24    /// Material constitutive model.
25    pub material: Material,
26}
27
28impl Particle {
29    /// Create a new particle.
30    pub fn new(x: Vec3, v: Vec3, mass: f64, volume: f64, material: Material) -> Self {
31        Self {
32            x,
33            v,
34            mass,
35            volume,
36            f: Mat3::identity(),
37            c: Mat3::zeros(),
38            j: 1.0,
39            material,
40        }
41    }
42
43    /// Update deformation gradient from velocity gradient.
44    pub fn update_deformation(&mut self, grad_v: &Mat3, dt: f64) {
45        // F_new = (I + dt * ∇v) * F_old
46        self.f = (Mat3::identity() + grad_v * dt) * self.f;
47        self.j = self.f.determinant();
48
49        // Prevent degenerate deformation
50        if !self.j.is_finite() || self.j < 0.01 {
51            self.f = Mat3::identity();
52            self.j = 1.0;
53        }
54    }
55}
56
57#[cfg(test)]
58mod tests {
59    use super::*;
60    use crate::material::Material;
61
62    #[test]
63    fn test_particle_creation() {
64        let mat = Material::Elastic { e: 1e6, nu: 0.3 };
65        let p = Particle::new(
66            Vec3::new(0.0, 0.0, 0.0),
67            Vec3::new(1.0, 0.0, 0.0),
68            1.0,
69            0.1,
70            mat,
71        );
72
73        assert_eq!(p.mass, 1.0);
74        assert_eq!(p.j, 1.0);
75        assert_eq!(p.f, Mat3::identity());
76    }
77
78    #[test]
79    fn test_deformation_update() {
80        let mat = Material::Elastic { e: 1e6, nu: 0.3 };
81        let mut p = Particle::new(Vec3::zeros(), Vec3::zeros(), 1.0, 0.1, mat);
82
83        // Apply uniform compression
84        let grad_v = Mat3::identity() * -0.1; // 10% shrink per second
85        p.update_deformation(&grad_v, 0.1);
86
87        // After 0.1s, should be (1 - 0.01) = 0.99 scale factor
88        assert!((p.j - 0.99_f64.powi(3)).abs() < 1e-6);
89    }
90}