use crate::error::IntegrateResult;
use scirs2_core::ndarray::{Array2, Array3};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TurbulenceModelType {
KEpsilon,
KOmega,
KOmegaSST,
ReynoldsStress,
SpalartAllmaras,
}
#[derive(Debug, Clone)]
pub struct TurbulenceConstants {
pub c_mu: f64,
pub c_1: f64,
pub c_2: f64,
pub sigma_k: f64,
pub sigma_epsilon: f64,
pub sigma_omega: f64,
}
impl Default for TurbulenceConstants {
fn default() -> Self {
Self {
c_mu: 0.09,
c_1: 1.44,
c_2: 1.92,
sigma_k: 1.0,
sigma_epsilon: 1.3,
sigma_omega: 2.0,
}
}
}
pub trait TurbulenceModel {
fn model_type(&self) -> TurbulenceModelType;
fn constants(&self) -> &TurbulenceConstants;
fn compute_turbulent_viscosity(
&self,
k: &Array3<f64>,
epsilon_or_omega: &Array3<f64>,
) -> IntegrateResult<Array3<f64>>;
fn compute_production_term(
&self,
velocity: &[Array3<f64>],
k: &Array3<f64>,
epsilon_or_omega: &Array3<f64>,
dx: f64,
dy: f64,
dz: f64,
) -> IntegrateResult<Array3<f64>>;
fn apply_turbulence_boundary_conditions(
&self,
k: &mut Array3<f64>,
epsilon_or_omega: &mut Array3<f64>,
) -> IntegrateResult<()>;
}
pub trait RANSModel: TurbulenceModel {
fn update_turbulence_quantities(
&self,
velocity: &[Array3<f64>],
k: &mut Array3<f64>,
epsilon_or_omega: &mut Array3<f64>,
dt: f64,
dx: f64,
dy: f64,
dz: f64,
) -> IntegrateResult<()>;
fn compute_wall_functions(
&self,
velocity: &[Array3<f64>],
wall_distance: &Array3<f64>,
k: &mut Array3<f64>,
epsilon_or_omega: &mut Array3<f64>,
) -> IntegrateResult<()>;
}
pub trait SGSModel {
fn compute_sgs_viscosity(
&self,
velocity: &[Array3<f64>],
filter_width: f64,
dx: f64,
dy: f64,
dz: f64,
) -> IntegrateResult<Array3<f64>>;
fn compute_sgs_stress(
&self,
velocity: &[Array3<f64>],
filter_width: f64,
dx: f64,
dy: f64,
dz: f64,
) -> IntegrateResult<Array3<[[f64; 3]; 3]>>;
}
pub struct TurbulenceUtils;
impl TurbulenceUtils {
pub fn compute_strain_rate_magnitude(
velocity: &[Array3<f64>],
dx: f64,
dy: f64,
dz: f64,
) -> IntegrateResult<Array3<f64>> {
let (nx, ny, nz) = velocity[0].dim();
let mut strain_mag = Array3::zeros((nx, ny, nz));
for i in 1..nx - 1 {
for j in 1..ny - 1 {
for k in 1..nz - 1 {
let dudx =
(velocity[0][[i + 1, j, k]] - velocity[0][[i - 1, j, k]]) / (2.0 * dx);
let dudy =
(velocity[0][[i, j + 1, k]] - velocity[0][[i, j - 1, k]]) / (2.0 * dy);
let dudz =
(velocity[0][[i, j, k + 1]] - velocity[0][[i, j, k - 1]]) / (2.0 * dz);
let dvdx =
(velocity[1][[i + 1, j, k]] - velocity[1][[i - 1, j, k]]) / (2.0 * dx);
let dvdy =
(velocity[1][[i, j + 1, k]] - velocity[1][[i, j - 1, k]]) / (2.0 * dy);
let dvdz =
(velocity[1][[i, j, k + 1]] - velocity[1][[i, j, k - 1]]) / (2.0 * dz);
let dwdx =
(velocity[2][[i + 1, j, k]] - velocity[2][[i - 1, j, k]]) / (2.0 * dx);
let dwdy =
(velocity[2][[i, j + 1, k]] - velocity[2][[i, j - 1, k]]) / (2.0 * dy);
let dwdz =
(velocity[2][[i, j, k + 1]] - velocity[2][[i, j, k - 1]]) / (2.0 * dz);
let s11 = dudx;
let s22 = dvdy;
let s33 = dwdz;
let s12 = 0.5 * (dudy + dvdx);
let s13 = 0.5 * (dudz + dwdx);
let s23 = 0.5 * (dvdz + dwdy);
let s_mag_squared = 2.0
* (s11 * s11
+ s22 * s22
+ s33 * s33
+ 2.0 * (s12 * s12 + s13 * s13 + s23 * s23));
strain_mag[[i, j, k]] = s_mag_squared.sqrt();
}
}
}
Ok(strain_mag)
}
pub fn compute_vorticity_magnitude(
velocity: &[Array3<f64>],
dx: f64,
dy: f64,
dz: f64,
) -> IntegrateResult<Array3<f64>> {
let (nx, ny, nz) = velocity[0].dim();
let mut vorticity_mag = Array3::zeros((nx, ny, nz));
for i in 1..nx - 1 {
for j in 1..ny - 1 {
for k in 1..nz - 1 {
let omega_x = (velocity[2][[i, j + 1, k]] - velocity[2][[i, j - 1, k]])
/ (2.0 * dy)
- (velocity[1][[i, j, k + 1]] - velocity[1][[i, j, k - 1]]) / (2.0 * dz);
let omega_y = (velocity[0][[i, j, k + 1]] - velocity[0][[i, j, k - 1]])
/ (2.0 * dz)
- (velocity[2][[i + 1, j, k]] - velocity[2][[i - 1, j, k]]) / (2.0 * dx);
let omega_z = (velocity[1][[i + 1, j, k]] - velocity[1][[i - 1, j, k]])
/ (2.0 * dx)
- (velocity[0][[i, j + 1, k]] - velocity[0][[i, j - 1, k]]) / (2.0 * dy);
vorticity_mag[[i, j, k]] =
(omega_x * omega_x + omega_y * omega_y + omega_z * omega_z).sqrt();
}
}
}
Ok(vorticity_mag)
}
pub fn compute_wall_distance(nx: usize, ny: usize, nz: usize) -> Array3<f64> {
let mut wall_distance = Array3::zeros((nx, ny, nz));
for i in 0..nx {
for j in 0..ny {
for k in 0..nz {
let dist_to_wall = vec![
i as f64,
(nx - 1 - i) as f64,
j as f64,
(ny - 1 - j) as f64,
k as f64,
(nz - 1 - k) as f64,
];
wall_distance[[i, j, k]] =
dist_to_wall.into_iter().fold(f64::INFINITY, f64::min);
}
}
}
wall_distance
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_turbulence_constants_default() {
let constants = TurbulenceConstants::default();
assert_eq!(constants.c_mu, 0.09);
assert_eq!(constants.c_1, 1.44);
assert_eq!(constants.c_2, 1.92);
}
#[test]
fn test_turbulence_model_type() {
let model_type = TurbulenceModelType::KEpsilon;
assert_eq!(model_type, TurbulenceModelType::KEpsilon);
assert_ne!(model_type, TurbulenceModelType::KOmega);
}
#[test]
fn test_strain_rate_computation() {
let velocity = vec![
Array3::ones((5, 5, 5)),
Array3::zeros((5, 5, 5)),
Array3::zeros((5, 5, 5)),
];
let strain_mag = TurbulenceUtils::compute_strain_rate_magnitude(&velocity, 0.1, 0.1, 0.1);
assert!(strain_mag.is_ok());
}
#[test]
fn test_wall_distance_computation() {
let wall_distance = TurbulenceUtils::compute_wall_distance(10, 10, 10);
assert_eq!(wall_distance.dim(), (10, 10, 10));
assert_eq!(wall_distance[[0, 0, 0]], 0.0);
assert!(wall_distance[[5, 5, 5]] > 0.0);
}
}