1use crate::error::IntegrateResult;
8use scirs2_core::ndarray::{Array2, Array3};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum TurbulenceModelType {
13 KEpsilon,
15 KOmega,
17 KOmegaSST,
19 ReynoldsStress,
21 SpalartAllmaras,
23}
24
25#[derive(Debug, Clone)]
27pub struct TurbulenceConstants {
28 pub c_mu: f64,
30 pub c_1: f64,
32 pub c_2: f64,
34 pub sigma_k: f64,
36 pub sigma_epsilon: f64,
38 pub sigma_omega: f64,
40}
41
42impl Default for TurbulenceConstants {
43 fn default() -> Self {
44 Self {
45 c_mu: 0.09,
46 c_1: 1.44,
47 c_2: 1.92,
48 sigma_k: 1.0,
49 sigma_epsilon: 1.3,
50 sigma_omega: 2.0,
51 }
52 }
53}
54
55pub trait TurbulenceModel {
57 fn model_type(&self) -> TurbulenceModelType;
59
60 fn constants(&self) -> &TurbulenceConstants;
62
63 fn compute_turbulent_viscosity(
65 &self,
66 k: &Array3<f64>,
67 epsilon_or_omega: &Array3<f64>,
68 ) -> IntegrateResult<Array3<f64>>;
69
70 fn compute_production_term(
72 &self,
73 velocity: &[Array3<f64>],
74 k: &Array3<f64>,
75 epsilon_or_omega: &Array3<f64>,
76 dx: f64,
77 dy: f64,
78 dz: f64,
79 ) -> IntegrateResult<Array3<f64>>;
80
81 fn apply_turbulence_boundary_conditions(
83 &self,
84 k: &mut Array3<f64>,
85 epsilon_or_omega: &mut Array3<f64>,
86 ) -> IntegrateResult<()>;
87}
88
89pub trait RANSModel: TurbulenceModel {
91 fn update_turbulence_quantities(
93 &self,
94 velocity: &[Array3<f64>],
95 k: &mut Array3<f64>,
96 epsilon_or_omega: &mut Array3<f64>,
97 dt: f64,
98 dx: f64,
99 dy: f64,
100 dz: f64,
101 ) -> IntegrateResult<()>;
102
103 fn compute_wall_functions(
105 &self,
106 velocity: &[Array3<f64>],
107 wall_distance: &Array3<f64>,
108 k: &mut Array3<f64>,
109 epsilon_or_omega: &mut Array3<f64>,
110 ) -> IntegrateResult<()>;
111}
112
113pub trait SGSModel {
115 fn compute_sgs_viscosity(
117 &self,
118 velocity: &[Array3<f64>],
119 filter_width: f64,
120 dx: f64,
121 dy: f64,
122 dz: f64,
123 ) -> IntegrateResult<Array3<f64>>;
124
125 fn compute_sgs_stress(
127 &self,
128 velocity: &[Array3<f64>],
129 filter_width: f64,
130 dx: f64,
131 dy: f64,
132 dz: f64,
133 ) -> IntegrateResult<Array3<[[f64; 3]; 3]>>;
134}
135
136pub struct TurbulenceUtils;
138
139impl TurbulenceUtils {
140 pub fn compute_strain_rate_magnitude(
142 velocity: &[Array3<f64>],
143 dx: f64,
144 dy: f64,
145 dz: f64,
146 ) -> IntegrateResult<Array3<f64>> {
147 let (nx, ny, nz) = velocity[0].dim();
148 let mut strain_mag = Array3::zeros((nx, ny, nz));
149
150 for i in 1..nx - 1 {
151 for j in 1..ny - 1 {
152 for k in 1..nz - 1 {
153 let dudx =
155 (velocity[0][[i + 1, j, k]] - velocity[0][[i - 1, j, k]]) / (2.0 * dx);
156 let dudy =
157 (velocity[0][[i, j + 1, k]] - velocity[0][[i, j - 1, k]]) / (2.0 * dy);
158 let dudz =
159 (velocity[0][[i, j, k + 1]] - velocity[0][[i, j, k - 1]]) / (2.0 * dz);
160
161 let dvdx =
162 (velocity[1][[i + 1, j, k]] - velocity[1][[i - 1, j, k]]) / (2.0 * dx);
163 let dvdy =
164 (velocity[1][[i, j + 1, k]] - velocity[1][[i, j - 1, k]]) / (2.0 * dy);
165 let dvdz =
166 (velocity[1][[i, j, k + 1]] - velocity[1][[i, j, k - 1]]) / (2.0 * dz);
167
168 let dwdx =
169 (velocity[2][[i + 1, j, k]] - velocity[2][[i - 1, j, k]]) / (2.0 * dx);
170 let dwdy =
171 (velocity[2][[i, j + 1, k]] - velocity[2][[i, j - 1, k]]) / (2.0 * dy);
172 let dwdz =
173 (velocity[2][[i, j, k + 1]] - velocity[2][[i, j, k - 1]]) / (2.0 * dz);
174
175 let s11 = dudx;
177 let s22 = dvdy;
178 let s33 = dwdz;
179 let s12 = 0.5 * (dudy + dvdx);
180 let s13 = 0.5 * (dudz + dwdx);
181 let s23 = 0.5 * (dvdz + dwdy);
182
183 let s_mag_squared = 2.0
185 * (s11 * s11
186 + s22 * s22
187 + s33 * s33
188 + 2.0 * (s12 * s12 + s13 * s13 + s23 * s23));
189 strain_mag[[i, j, k]] = s_mag_squared.sqrt();
190 }
191 }
192 }
193
194 Ok(strain_mag)
195 }
196
197 pub fn compute_vorticity_magnitude(
199 velocity: &[Array3<f64>],
200 dx: f64,
201 dy: f64,
202 dz: f64,
203 ) -> IntegrateResult<Array3<f64>> {
204 let (nx, ny, nz) = velocity[0].dim();
205 let mut vorticity_mag = Array3::zeros((nx, ny, nz));
206
207 for i in 1..nx - 1 {
208 for j in 1..ny - 1 {
209 for k in 1..nz - 1 {
210 let omega_x = (velocity[2][[i, j + 1, k]] - velocity[2][[i, j - 1, k]])
212 / (2.0 * dy)
213 - (velocity[1][[i, j, k + 1]] - velocity[1][[i, j, k - 1]]) / (2.0 * dz);
214
215 let omega_y = (velocity[0][[i, j, k + 1]] - velocity[0][[i, j, k - 1]])
216 / (2.0 * dz)
217 - (velocity[2][[i + 1, j, k]] - velocity[2][[i - 1, j, k]]) / (2.0 * dx);
218
219 let omega_z = (velocity[1][[i + 1, j, k]] - velocity[1][[i - 1, j, k]])
220 / (2.0 * dx)
221 - (velocity[0][[i, j + 1, k]] - velocity[0][[i, j - 1, k]]) / (2.0 * dy);
222
223 vorticity_mag[[i, j, k]] =
224 (omega_x * omega_x + omega_y * omega_y + omega_z * omega_z).sqrt();
225 }
226 }
227 }
228
229 Ok(vorticity_mag)
230 }
231
232 pub fn compute_wall_distance(nx: usize, ny: usize, nz: usize) -> Array3<f64> {
234 let mut wall_distance = Array3::zeros((nx, ny, nz));
235
236 for i in 0..nx {
237 for j in 0..ny {
238 for k in 0..nz {
239 let dist_to_wall = vec![
241 i as f64,
242 (nx - 1 - i) as f64,
243 j as f64,
244 (ny - 1 - j) as f64,
245 k as f64,
246 (nz - 1 - k) as f64,
247 ];
248
249 wall_distance[[i, j, k]] =
250 dist_to_wall.into_iter().fold(f64::INFINITY, f64::min);
251 }
252 }
253 }
254
255 wall_distance
256 }
257}
258
259#[cfg(test)]
260mod tests {
261 use super::*;
262
263 #[test]
264 fn test_turbulence_constants_default() {
265 let constants = TurbulenceConstants::default();
266 assert_eq!(constants.c_mu, 0.09);
267 assert_eq!(constants.c_1, 1.44);
268 assert_eq!(constants.c_2, 1.92);
269 }
270
271 #[test]
272 fn test_turbulence_model_type() {
273 let model_type = TurbulenceModelType::KEpsilon;
274 assert_eq!(model_type, TurbulenceModelType::KEpsilon);
275 assert_ne!(model_type, TurbulenceModelType::KOmega);
276 }
277
278 #[test]
279 fn test_strain_rate_computation() {
280 let velocity = vec![
281 Array3::ones((5, 5, 5)),
282 Array3::zeros((5, 5, 5)),
283 Array3::zeros((5, 5, 5)),
284 ];
285
286 let strain_mag = TurbulenceUtils::compute_strain_rate_magnitude(&velocity, 0.1, 0.1, 0.1);
287 assert!(strain_mag.is_ok());
288 }
289
290 #[test]
291 fn test_wall_distance_computation() {
292 let wall_distance = TurbulenceUtils::compute_wall_distance(10, 10, 10);
293 assert_eq!(wall_distance.dim(), (10, 10, 10));
294 assert_eq!(wall_distance[[0, 0, 0]], 0.0);
295 assert!(wall_distance[[5, 5, 5]] > 0.0);
296 }
297}