💥 Mechanics
The Mechanics
and
SubDomainMechanics
traits
specify the physical representation of cellular agents inside the simulation domain.
The traits act on 3 distinct types for position, velocity and forces acting on the cell-agents.
In many examples, these types are identical.
The subdomain trait is responsible for keeping cell-agents inside the specified simulation
domain.
Difference to Interaction concept
To describe physical interactions between cells when in proximity of each other, we also provide the
Interaction concept.
In contrast, the Mechanics concepts is only concerned with the physical representation and physical
motion of one cell on its own.
Although these two concepts seem to be similar in nature, it can be benefitial to separate them not
only conceptually but also for practical reasons.
For example we can seamlessly change between the
Brownian3D
and
Langevin3D
struct without having to
alter the currently used Interaction type (if present).
Examples
A wide variety of cellular repesentations can be realized by this trait.
cellular_raza
provides some of them in its
cellular_raza_building_blocks
crate.
Point-like Particles
To illustrate how the Mechanics
concept works, we
take alook at the most simple representation as point-like particles.
In this case, cells are described by a postion and velocity vector of $n$ (typically $n=2,3$)
dimensions which we will call VectorN
for simplicity.
$$
\vec{x} = \begin{bmatrix}
x_1\\
x_2\\
\vdots\\
x_n
\end{bmatrix}
\hspace{1cm}
\vec{v} = \begin{bmatrix}
v_1\\
v_2\\
\vdots\\
v_n
\end{bmatrix}
$$
A third type is also of importance which describes the force acting on the cell.
In our case we can assume the same form as before
$$
\vec{F} = \begin{bmatrix}
F_1\\
F_2\\
\vdots\\
F_n
\end{bmatrix}.
$$
The solver of the chosen backend must be told how to increment position and
velocity of the cell.
This is done by the calculate_increment
function.
fn calculate_increment(&self, force: For) -> Result<(Pos, Vel), CalcError> {
// Do some calculations
...
// Return increments
Ok((...,...))
}
Even for simple point-like particles, we have a variety of options. If we assume simple newtonian dynamics without any additional stochastic effects, we can write down the equations of motion $$\begin{align} \dot{\vec{x}} &= \vec{v}\\ \dot{\vec{v}} &= \frac{1}{m}\vec{F}. \end{align}$$ Note that we assume that our cell has a certain mass, which may also be set to $m=1$ and thus neglected in some implementations.
struct MyCell {
pos: VectorN,
vel: VectorN,
mass: f64,
}
The Mechanics
trait interoperates very
closesly with the Position
and
Velocity
traits which provide setters and
getters
If we implement the properties explained by the preceding equations, we obtain
impl Position<VectorN> for MyCell {
fn pos(&self) -> VectorN {
self.pos.clone()
}
fn set_pos(&mut self, pos: &VectorN) {
self.pos = pos.clone();
}
}
impl Velocity<VectorN> for MyCell {
fn velocity(&self) -> VectorN {
self.vel.clone()
}
fn set_velocity(&mut self, velocity: &VectorN) {
self.vel = velocity.clone();
}
}
// Here is the magic
impl Mechanics<VectorN, VectorN, VectorN> for MyCell {
fn calculate_increment(
&self,
force: VectorN
) -> Result<(VectorN, VectorN), CalcError> {
let dx = self.vel();
let dv = 1.0/self.mass * force;
Ok((dx, dv))
}
}
If the user decides to leave out the Interaction concept, we will assume a force of zero as
specified by the num::Zero
trait when
numerically solving the equations.