use crate::vector3::Vector3;
#[derive(Debug, Clone)]
pub struct FluidField {
pub velocity: Vec<Vec<Vector3<f64>>>,
pub pressure: Vec<Vec<f64>>,
pub nx: usize,
pub ny: usize,
pub dx: f64,
pub dy: f64,
}
impl FluidField {
pub fn new(nx: usize, ny: usize, dx: f64, dy: f64) -> Self {
Self {
velocity: vec![vec![Vector3::new(0.0, 0.0, 0.0); ny]; nx],
pressure: vec![vec![0.0; ny]; nx],
nx,
ny,
dx,
dy,
}
}
pub fn set_velocity(&mut self, i: usize, j: usize, v: Vector3<f64>) {
if i < self.nx && j < self.ny {
self.velocity[i][j] = v;
}
}
}
#[derive(Debug, Clone)]
pub struct NavierStokes {
pub viscosity: f64,
pub density: f64,
pub dt: f64,
}
impl NavierStokes {
pub fn mercury(dt: f64) -> Self {
Self {
viscosity: 1.15e-7,
density: 13534.0,
dt,
}
}
pub fn galinstan(dt: f64) -> Self {
Self {
viscosity: 3.4e-7,
density: 6440.0,
dt,
}
}
#[allow(clippy::needless_range_loop)]
pub fn step(&self, field: &mut FluidField, external_force: Vector3<f64>) {
let mut new_velocity = field.velocity.clone();
for i in 1..field.nx - 1 {
for j in 1..field.ny - 1 {
let v = field.velocity[i][j];
let advection = self.calculate_advection(field, i, j);
let diffusion = self.calculate_diffusion(field, i, j);
let pressure_grad = Vector3::new(0.0, 0.0, 0.0);
let dv_dt = advection * (-1.0)
+ diffusion * self.viscosity
+ pressure_grad
+ external_force;
new_velocity[i][j] = v + dv_dt * self.dt;
}
}
field.velocity = new_velocity;
}
#[allow(dead_code)]
fn calculate_advection(&self, field: &FluidField, i: usize, j: usize) -> Vector3<f64> {
let v = field.velocity[i][j];
let dv_dx = if v.x > 0.0 {
(v - field.velocity[i - 1][j]) * (1.0 / field.dx)
} else {
(field.velocity[i + 1][j] - v) * (1.0 / field.dx)
};
let dv_dy = if v.y > 0.0 {
(v - field.velocity[i][j - 1]) * (1.0 / field.dy)
} else {
(field.velocity[i][j + 1] - v) * (1.0 / field.dy)
};
Vector3::new(
v.x * dv_dx.x + v.y * dv_dy.x,
v.x * dv_dx.y + v.y * dv_dy.y,
0.0,
)
}
fn calculate_diffusion(&self, field: &FluidField, i: usize, j: usize) -> Vector3<f64> {
let v = field.velocity[i][j];
let v_xp = field.velocity[i + 1][j];
let v_xm = field.velocity[i - 1][j];
let v_yp = field.velocity[i][j + 1];
let v_ym = field.velocity[i][j - 1];
let d2v_dx2 = (v_xp + v_xm - v * 2.0) * (1.0 / field.dx.powi(2));
let d2v_dy2 = (v_yp + v_ym - v * 2.0) * (1.0 / field.dy.powi(2));
d2v_dx2 + d2v_dy2
}
pub fn reynolds_number(&self, velocity: f64, length: f64) -> f64 {
velocity * length / self.viscosity
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fluid_field_creation() {
let field = FluidField::new(10, 10, 1.0e-3, 1.0e-3);
assert_eq!(field.nx, 10);
assert_eq!(field.ny, 10);
}
#[test]
fn test_navier_stokes_mercury() {
let ns = NavierStokes::mercury(1.0e-6);
assert!((ns.density - 13534.0).abs() < 1.0);
}
#[test]
fn test_reynolds_number() {
let ns = NavierStokes::galinstan(1.0e-6);
let re = ns.reynolds_number(0.1, 1.0e-3);
assert!(re > 0.0);
assert!(re < 2000.0);
}
#[test]
fn test_diffusion_calculation() {
let ns = NavierStokes::mercury(1.0e-6);
let mut field = FluidField::new(5, 5, 1.0e-3, 1.0e-3);
field.set_velocity(2, 2, Vector3::new(1.0, 0.0, 0.0));
let diffusion = ns.calculate_diffusion(&field, 2, 2);
assert!(diffusion.x < 0.0);
}
}