moldyn_core/particle.rs
1//! This module contains the [Particle] struct.
2
3use crate::Vec3;
4use serde::{Deserialize, Serialize};
5
6/// A struct representing a particle record in the simulation.
7#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, Copy, Default)]
8pub struct Particle {
9 /// Position of the particle in 3D space.
10 #[serde(default)]
11 position: Vec3,
12
13 /// Velocity of the particle in 3D space.
14 #[serde(default)]
15 velocity: Vec3,
16
17 /// Force effective on the particle in 3D space.
18 #[serde(default, skip_deserializing)]
19 force: Vec3,
20
21 /// Force which was effective on the particle in the previous time step.
22 #[serde(default, skip_deserializing)]
23 old_force: Vec3,
24
25 /// Mass of the particle.
26 #[serde(default)]
27 mass: f64,
28}
29
30impl Particle {
31 /// [Particle] constructor from position, initial velocity and mass.
32 pub fn from_data(position: Vec3, velocity: Vec3, mass: f64) -> Self {
33 Self {
34 position,
35 velocity,
36 mass,
37 ..Default::default()
38 }
39 }
40
41 /// Returns the current force of the particle.
42 pub fn get_force(&self) -> Vec3 {
43 self.force
44 }
45
46 /// Propagates the current force to the old force. This has to be called
47 /// every time step before invoking [Particle::apply_force] to apply new
48 /// forces.
49 pub fn delay_force(&mut self) {
50 self.old_force = self.force;
51 self.force = Vec3::zero();
52 }
53
54 /// Applies the given force to the particle (addition). It assumes that the
55 /// force was reset with [Particle::delay_force] in a timestep.
56 pub fn apply_force(&mut self, force: Vec3) {
57 self.force += force;
58 }
59
60 /// Returns the current position of the particle.
61 pub fn get_position(&self) -> Vec3 {
62 self.position
63 }
64
65 /// Calculate the updated position of the particle given a delta time step.
66 /// This functionality is constant across different simulation algorithms,
67 /// so it is implemented here.
68 pub fn update_position(&mut self, delta_time: f64) {
69 self.position +=
70 self.velocity * delta_time + self.force * (delta_time.powi(2) / (2.0 * self.mass));
71 }
72
73 /// Returns the current velocity of the particle.
74 pub fn get_velocity(&self) -> Vec3 {
75 self.velocity
76 }
77
78 /// Calculate the updated velocity of the particle given a delta time step.
79 /// This functionality is constant across different simulation algorithms,
80 /// so it is implemented here.
81 pub fn update_velocity(&mut self, delta_time: f64) {
82 self.velocity += (self.force + self.old_force) * (delta_time / (2.0 * self.mass));
83 }
84
85 /// Returns the constant mass of the particle.
86 pub fn get_mass(&self) -> f64 {
87 self.mass
88 }
89
90 /// Calculate the vector difference between two particles' positions. Note
91 /// that the order of the particles affects the sign.
92 ///
93 /// - `direction(a, b) == -direction(b, a)`.
94 pub fn position_difference(particle1: &Particle, particle2: &Particle) -> Vec3 {
95 particle1.position - particle2.position
96 }
97
98 /// Calculate the normalized vector difference between two particles' positions.
99 /// Note that the order of the particles affects the sign.
100 ///
101 /// - If result is `Some`: `direction(a, b) == -direction(b, a)`.
102 /// - If result is `None`: `direction(a, b) == direction(b, a) == None`.
103 pub fn direction(particle1: &Particle, particle2: &Particle) -> Option<Vec3> {
104 Particle::position_difference(particle1, particle2).normal()
105 }
106
107 /// Calculate the distance between two particles' positions. This function is
108 /// symmetric:
109 ///
110 /// - `distance(a, b) == distance(b, a)`.
111 pub fn distance(particle1: &Particle, particle2: &Particle) -> f64 {
112 Particle::position_difference(particle1, particle2).length()
113 }
114
115 /// Calculate the product of the masses of two particles.
116 pub fn mass_product(particle1: &Particle, particle2: &Particle) -> f64 {
117 particle1.mass * particle2.mass
118 }
119}
120
121/// These methods are used for creating particles in tests.
122#[cfg(test)]
123impl Particle {
124 /// Creates a particle at the given position with zero velocity.
125 pub fn at(x: f64, y: f64, z: f64) -> Self {
126 Self {
127 position: Vec3::new(x, y, z),
128 ..Default::default()
129 }
130 }
131
132 /// Builder method for testing. Manually sets the velocity of the particle
133 /// to the given value.
134 pub fn with_velocity(mut self, x: f64, y: f64, z: f64) -> Self {
135 self.velocity = Vec3::new(x, y, z);
136 self
137 }
138
139 /// Builder method for testing. Manually sets the force of the particle
140 /// to the given value.
141 pub fn with_force(mut self, x: f64, y: f64, z: f64) -> Self {
142 self.force = Vec3::new(x, y, z);
143 self
144 }
145
146 /// Builder method for testing. Manually sets the mass of the particle
147 /// to the given value.
148 pub fn with_mass(mut self, mass: f64) -> Self {
149 self.mass = mass;
150 self
151 }
152}
153
154// TODO tests for [update_velocity], [update_position], and [delay_force]