use scirs2_core::ndarray::Array3;
#[derive(Debug, Clone)]
pub struct CompressibleState {
pub density: Array3<f64>,
pub momentum: Vec<Array3<f64>>,
pub energy: Array3<f64>,
pub pressure: Array3<f64>,
pub temperature: Array3<f64>,
pub mach: Array3<f64>,
}
impl CompressibleState {
pub fn new(nx: usize, ny: usize, nz: usize) -> Self {
let density = Array3::ones((nx, ny, nz));
let momentum = vec![
Array3::zeros((nx, ny, nz)), Array3::zeros((nx, ny, nz)), Array3::zeros((nx, ny, nz)), ];
let energy = Array3::from_elem((nx, ny, nz), 2.5); let pressure = Array3::ones((nx, ny, nz));
let temperature = Array3::from_elem((nx, ny, nz), 300.0);
let mach = Array3::zeros((nx, ny, nz));
CompressibleState {
density,
momentum,
energy,
pressure,
temperature,
mach,
}
}
pub fn dimensions(&self) -> (usize, usize, usize) {
let (nx, ny, nz) = self.density.dim();
(nx, ny, nz)
}
pub fn velocity_components(&self) -> Vec<Array3<f64>> {
self.momentum
.iter()
.map(|mom| mom / &self.density)
.collect()
}
pub fn kinetic_energy_density(&self) -> Array3<f64> {
let velocities = self.velocity_components();
let u = &velocities[0];
let v = &velocities[1];
let w = &velocities[2];
0.5 * &self.density * (u * u + v * v + w * w)
}
pub fn internal_energy_density(&self) -> Array3<f64> {
&self.energy - &self.kinetic_energy_density()
}
pub fn sound_speed(&self, gamma: f64) -> Array3<f64> {
(gamma * &self.pressure / &self.density).mapv(|x| x.sqrt())
}
pub fn update_mach_number(&mut self, gamma: f64) {
let velocities = self.velocity_components();
let u = &velocities[0];
let v = &velocities[1];
let w = &velocities[2];
let velocity_magnitude = (u * u + v * v + w * w).mapv(|x| x.sqrt());
let sound_speed = self.sound_speed(gamma);
self.mach = velocity_magnitude / sound_speed;
}
pub fn is_physically_valid(&self) -> bool {
if self.density.iter().any(|&rho| rho <= 0.0) {
return false;
}
if self.pressure.iter().any(|&p| p <= 0.0) {
return false;
}
if self.temperature.iter().any(|&t| t <= 0.0) {
return false;
}
if self.density.iter().any(|&x| !x.is_finite())
|| self.pressure.iter().any(|&x| !x.is_finite())
|| self.temperature.iter().any(|&x| !x.is_finite())
|| self.energy.iter().any(|&x| !x.is_finite())
{
return false;
}
for momentum_component in &self.momentum {
if momentum_component.iter().any(|&x| !x.is_finite()) {
return false;
}
}
true
}
pub fn set_uniform_conditions(
&mut self,
rho0: f64,
u0: f64,
v0: f64,
w0: f64,
p0: f64,
t0: f64,
) {
self.density.fill(rho0);
self.momentum[0].fill(rho0 * u0);
self.momentum[1].fill(rho0 * v0);
self.momentum[2].fill(rho0 * w0);
self.pressure.fill(p0);
self.temperature.fill(t0);
let kinetic = 0.5 * rho0 * (u0 * u0 + v0 * v0 + w0 * w0);
let gamma = 1.4; let internal = p0 / (gamma - 1.0);
self.energy.fill(internal + kinetic);
let c = (gamma * p0 / rho0).sqrt();
let vel_mag = (u0 * u0 + v0 * v0 + w0 * w0).sqrt();
self.mach.fill(vel_mag / c);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_state_creation() {
let state = CompressibleState::new(10, 10, 10);
assert_eq!(state.dimensions(), (10, 10, 10));
assert_eq!(state.momentum.len(), 3);
assert!(state.density.iter().all(|&x| x == 1.0));
assert!(state.momentum[0].iter().all(|&x| x == 0.0));
assert!(state.pressure.iter().all(|&x| x == 1.0));
assert!(state.temperature.iter().all(|&x| x == 300.0));
}
#[test]
fn test_velocity_components() {
let mut state = CompressibleState::new(2, 2, 2);
state.set_uniform_conditions(1.0, 10.0, 5.0, 2.0, 1.0, 300.0);
let velocities = state.velocity_components();
assert!(velocities[0].iter().all(|&x| (x - 10.0).abs() < 1e-10));
assert!(velocities[1].iter().all(|&x| (x - 5.0).abs() < 1e-10));
assert!(velocities[2].iter().all(|&x| (x - 2.0).abs() < 1e-10));
}
#[test]
fn test_physical_validity() {
let mut state = CompressibleState::new(5, 5, 5);
assert!(state.is_physically_valid());
state.density[[0, 0, 0]] = -1.0;
assert!(!state.is_physically_valid());
}
#[test]
fn test_sound_speed() {
let mut state = CompressibleState::new(2, 2, 2);
state.set_uniform_conditions(1.0, 0.0, 0.0, 0.0, 1.0, 300.0);
let gamma = 1.4;
let c = state.sound_speed(gamma);
let expected_c = (gamma * 1.0 / 1.0).sqrt();
assert!(c.iter().all(|&x| (x - expected_c).abs() < 1e-10));
}
#[test]
fn test_mach_number_update() {
let mut state = CompressibleState::new(2, 2, 2);
state.set_uniform_conditions(1.0, 10.0, 0.0, 0.0, 1.0, 300.0);
let gamma = 1.4;
state.update_mach_number(gamma);
let expected_c = (gamma * 1.0 / 1.0).sqrt();
let expected_mach = 10.0 / expected_c;
assert!(state
.mach
.iter()
.all(|&x| (x - expected_mach).abs() < 1e-10));
}
}