#![forbid(unsafe_code)]
pub struct LatticeGas {
pub cells: Vec<i8>,
pub velocities: Vec<u8>,
pub width: usize,
}
impl LatticeGas {
pub fn new(width: usize, height: usize) -> Self {
let size = width * height;
LatticeGas {
cells: vec![0; size],
velocities: vec![0; size],
width,
}
}
pub fn height(&self) -> usize {
self.cells.len() / self.width
}
pub fn stream(&mut self) {
let w = self.width;
let h = self.height();
let size = w * h;
let mut new_cells = vec![0i8; size];
let mut new_vel = vec![0u8; size];
for i in 0..size {
if self.cells[i] == 0 { continue; }
let v = self.velocities[i];
let (nx, ny) = match v {
0 => (i % w, i / w), 1 => ((i % w) + 1, i / w), 2 => (i % w, (i / w) + 1), 3 => { let x = i % w; (if x > 0 { x - 1 } else { 0 }, i / w) } 4 => (i % w, { let y = i / w; if y > 0 { y - 1 } else { 0 } }), _ => (i % w, i / w),
};
let ni = if nx < w && ny < h { ny * w + nx } else { i };
if new_cells[ni] == 0 {
new_cells[ni] = self.cells[i];
new_vel[ni] = v;
} else {
new_cells[i] = self.cells[i];
new_vel[i] = v;
}
}
self.cells = new_cells;
self.velocities = new_vel;
}
pub fn collide(&mut self) {
let size = self.cells.len();
let w = self.width;
for i in 0..size {
if self.cells[i] == 0 { continue; }
let v = self.velocities[i];
let neighbor = match v {
1 => { if i % w + 1 < w { Some(i + 1) } else { None } } 3 => { if i % w > 0 { Some(i - 1) } else { None } } _ => None,
};
if let Some(ni) = neighbor {
if self.cells[ni] != 0 {
if (v == 1 && self.velocities[ni] == 3) || (v == 3 && self.velocities[ni] == 1) {
self.velocities[i] = 2; self.velocities[ni] = 4; }
}
}
}
}
pub fn step(&mut self) {
self.stream();
self.collide();
}
pub fn density(&self) -> Vec<f64> {
self.cells.iter().map(|&c| if c != 0 { 1.0 } else { 0.0 }).collect()
}
pub fn momentum(&self) -> (f64, f64) {
let mut mx = 0.0f64;
let mut my = 0.0f64;
for i in 0..self.cells.len() {
if self.cells[i] != 0 {
match self.velocities[i] {
1 => mx += 1.0,
3 => mx -= 1.0,
2 => my += 1.0,
4 => my -= 1.0,
_ => {}
}
}
}
(mx, my)
}
pub fn temperature(&self) -> f64 {
let n = self.cells.len();
if n == 0 { return 0.0; }
let total_ke: f64 = self.cells.iter().zip(self.velocities.iter())
.map(|(&c, &v)| {
if c == 0 { 0.0 }
else if v == 0 { 0.0 }
else { 0.5 }
})
.sum();
let count = self.cells.iter().filter(|&&c| c != 0).count();
if count == 0 { 0.0 } else { total_ke / count as f64 }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new() {
let lg = LatticeGas::new(5, 5);
assert_eq!(lg.cells.len(), 25);
assert_eq!(lg.velocities.len(), 25);
assert_eq!(lg.width, 5);
}
#[test]
fn test_struct_size() {
let lg = LatticeGas::new(3, 3);
assert_eq!(lg.cells.len(), 9);
}
#[test]
fn test_stream_rest() {
let mut lg = LatticeGas::new(3, 3);
lg.cells[4] = 1;
lg.velocities[4] = 0; lg.stream();
assert_eq!(lg.cells[4], 1);
}
#[test]
fn test_stream_right() {
let mut lg = LatticeGas::new(3, 3);
lg.cells[3] = 1;
lg.velocities[3] = 1; lg.stream();
assert_eq!(lg.cells[4], 1);
assert_eq!(lg.cells[3], 0);
}
#[test]
fn test_stream_left() {
let mut lg = LatticeGas::new(3, 3);
lg.cells[5] = 1;
lg.velocities[5] = 3; lg.stream();
assert_eq!(lg.cells[4], 1);
assert_eq!(lg.cells[5], 0);
}
#[test]
fn test_stream_up() {
let mut lg = LatticeGas::new(3, 3);
lg.cells[3] = 1; lg.velocities[3] = 2; lg.stream();
assert_eq!(lg.cells[6], 1); }
#[test]
fn test_stream_down() {
let mut lg = LatticeGas::new(3, 3);
lg.cells[6] = 1; lg.velocities[6] = 4; lg.stream();
assert_eq!(lg.cells[3], 1); }
#[test]
fn test_density() {
let mut lg = LatticeGas::new(2, 2);
lg.cells[0] = 1;
lg.cells[3] = -1;
let d = lg.density();
assert_eq!(d[0], 1.0);
assert_eq!(d[1], 0.0);
assert_eq!(d[3], 1.0);
}
#[test]
fn test_momentum_rest() {
let mut lg = LatticeGas::new(3, 3);
lg.cells[4] = 1;
lg.velocities[4] = 0;
let (mx, my) = lg.momentum();
assert_eq!(mx, 0.0);
assert_eq!(my, 0.0);
}
#[test]
fn test_momentum_moving() {
let mut lg = LatticeGas::new(3, 3);
lg.cells[4] = 1;
lg.velocities[4] = 1; let (mx, my) = lg.momentum();
assert_eq!(mx, 1.0);
assert_eq!(my, 0.0);
}
#[test]
fn test_momentum_balanced() {
let mut lg = LatticeGas::new(5, 1);
lg.cells[1] = 1;
lg.velocities[1] = 1; lg.cells[3] = 1;
lg.velocities[3] = 3; let (mx, _) = lg.momentum();
assert_eq!(mx, 0.0);
}
#[test]
fn test_temperature() {
let mut lg = LatticeGas::new(3, 3);
lg.cells[0] = 1;
lg.velocities[0] = 0; lg.cells[1] = 1;
lg.velocities[1] = 1; let t = lg.temperature();
assert!((t - 0.25).abs() < 0.001); }
#[test]
fn test_step() {
let mut lg = LatticeGas::new(3, 3);
lg.cells[4] = 1;
lg.velocities[4] = 1; lg.step();
assert_eq!(lg.cells[5], 1);
}
#[test]
fn test_collide_head_on() {
let mut lg = LatticeGas::new(5, 1);
lg.cells[1] = 1;
lg.velocities[1] = 1; lg.cells[2] = 1;
lg.velocities[2] = 3; lg.collide();
assert_eq!(lg.velocities[1], 2); assert_eq!(lg.velocities[2], 4); }
}