use crate::PhysicsError;
use crate::kernels::fluids::kinematics::velocity_gradient_invariants_kernel;
use crate::kernels::fluids::quantities::VelocityGradient;
use deep_causality_num::{FromPrimitive, RealField};
pub fn q_criterion_kernel<R>(grad_u: &VelocityGradient<R>) -> Result<R, PhysicsError>
where
R: RealField + FromPrimitive,
{
let half = R::from_f64(0.5)
.ok_or_else(|| PhysicsError::NumericalInstability("R::from_f64(0.5) failed".into()))?;
let g = grad_u.value();
let tr_g_squared = g[0][0] * g[0][0]
+ g[0][1] * g[1][0]
+ g[0][2] * g[2][0]
+ g[1][0] * g[0][1]
+ g[1][1] * g[1][1]
+ g[1][2] * g[2][1]
+ g[2][0] * g[0][2]
+ g[2][1] * g[1][2]
+ g[2][2] * g[2][2];
Ok(-half * tr_g_squared)
}
pub fn delta_criterion_kernel<R>(grad_u: &VelocityGradient<R>) -> Result<R, PhysicsError>
where
R: RealField + FromPrimitive,
{
let third = R::from_f64(1.0 / 3.0)
.ok_or_else(|| PhysicsError::NumericalInstability("R::from_f64(1/3) failed".into()))?;
let half = R::from_f64(0.5)
.ok_or_else(|| PhysicsError::NumericalInstability("R::from_f64(0.5) failed".into()))?;
let two = R::from_f64(2.0)
.ok_or_else(|| PhysicsError::NumericalInstability("R::from_f64(2.0) failed".into()))?;
let twenty_seven = R::from_f64(27.0)
.ok_or_else(|| PhysicsError::NumericalInstability("R::from_f64(27.0) failed".into()))?;
let (p, q, r) = velocity_gradient_invariants_kernel(grad_u)?;
let p_d = q - p * p * third;
let q_d = two * p * p * p / twenty_seven - p * q * third + r;
let p3 = p_d * third;
let q2 = q_d * half;
Ok(p3 * p3 * p3 + q2 * q2)
}
pub fn lambda2_kernel<R>(grad_u: &VelocityGradient<R>) -> Result<R, PhysicsError>
where
R: RealField + FromPrimitive,
{
let half = R::from_f64(0.5)
.ok_or_else(|| PhysicsError::NumericalInstability("R::from_f64(0.5) failed".into()))?;
let g = grad_u.value();
let mut s = [[R::zero(); 3]; 3];
let mut o = [[R::zero(); 3]; 3];
for (i, (s_row, o_row)) in s.iter_mut().zip(o.iter_mut()).enumerate() {
for (j, (s_ij, o_ij)) in s_row.iter_mut().zip(o_row.iter_mut()).enumerate() {
*s_ij = half * (g[i][j] + g[j][i]);
*o_ij = half * (g[i][j] - g[j][i]);
}
}
let m = sym_3x3_add(&mat3_mul(&s, &s), &mat3_mul(&o, &o));
let eigs = symmetric_3x3_eigenvalues(&m)?;
Ok(eigs[1])
}
pub fn swirling_strength_kernel<R>(grad_u: &VelocityGradient<R>) -> Result<R, PhysicsError>
where
R: RealField + FromPrimitive,
{
let third = R::from_f64(1.0 / 3.0)
.ok_or_else(|| PhysicsError::NumericalInstability("R::from_f64(1/3) failed".into()))?;
let half = R::from_f64(0.5)
.ok_or_else(|| PhysicsError::NumericalInstability("R::from_f64(0.5) failed".into()))?;
let two = R::from_f64(2.0)
.ok_or_else(|| PhysicsError::NumericalInstability("R::from_f64(2.0) failed".into()))?;
let three = R::from_f64(3.0)
.ok_or_else(|| PhysicsError::NumericalInstability("R::from_f64(3.0) failed".into()))?;
let twenty_seven = R::from_f64(27.0)
.ok_or_else(|| PhysicsError::NumericalInstability("R::from_f64(27.0) failed".into()))?;
let (p, q, r) = velocity_gradient_invariants_kernel(grad_u)?;
let p_d = q - p * p * third;
let q_d = two * p * p * p / twenty_seven - p * q * third + r;
let disc = (q_d * half) * (q_d * half) + (p_d * third) * (p_d * third) * (p_d * third);
if disc <= R::zero() {
return Ok(R::zero());
}
let sqrt_disc = disc.sqrt();
let u1 = signed_cbrt(-q_d * half + sqrt_disc, third);
let u2 = signed_cbrt(-q_d * half - sqrt_disc, third);
let sqrt_3_over_2 = three.sqrt() * half;
let diff = u1 - u2;
let abs_diff = if diff < R::zero() { -diff } else { diff };
Ok(sqrt_3_over_2 * abs_diff)
}
fn mat3_mul<R: RealField>(a: &[[R; 3]; 3], b: &[[R; 3]; 3]) -> [[R; 3]; 3] {
let mut out = [[R::zero(); 3]; 3];
for (i, out_row) in out.iter_mut().enumerate() {
for (j, out_ij) in out_row.iter_mut().enumerate() {
*out_ij = a[i][0] * b[0][j] + a[i][1] * b[1][j] + a[i][2] * b[2][j];
}
}
out
}
fn sym_3x3_add<R: RealField>(a: &[[R; 3]; 3], b: &[[R; 3]; 3]) -> [[R; 3]; 3] {
let mut out = [[R::zero(); 3]; 3];
for (i, out_row) in out.iter_mut().enumerate() {
for (j, out_ij) in out_row.iter_mut().enumerate() {
*out_ij = a[i][j] + b[i][j];
}
}
out
}
fn signed_cbrt<R: RealField>(x: R, one_third: R) -> R {
if x >= R::zero() {
x.powf(one_third)
} else {
-((-x).powf(one_third))
}
}
fn symmetric_3x3_eigenvalues<R>(m: &[[R; 3]; 3]) -> Result<[R; 3], PhysicsError>
where
R: RealField + FromPrimitive,
{
let third = R::from_f64(1.0 / 3.0)
.ok_or_else(|| PhysicsError::NumericalInstability("R::from_f64(1/3) failed".into()))?;
let half = R::from_f64(0.5)
.ok_or_else(|| PhysicsError::NumericalInstability("R::from_f64(0.5) failed".into()))?;
let two = R::from_f64(2.0)
.ok_or_else(|| PhysicsError::NumericalInstability("R::from_f64(2.0) failed".into()))?;
let six = R::from_f64(6.0)
.ok_or_else(|| PhysicsError::NumericalInstability("R::from_f64(6.0) failed".into()))?;
let three = R::from_f64(3.0)
.ok_or_else(|| PhysicsError::NumericalInstability("R::from_f64(3.0) failed".into()))?;
let two_pi_over_3 = R::from_f64(2.0 * core::f64::consts::PI / 3.0)
.ok_or_else(|| PhysicsError::NumericalInstability("R::from_f64(2π/3) failed".into()))?;
let p1 = m[0][1] * m[0][1] + m[0][2] * m[0][2] + m[1][2] * m[1][2];
if p1 == R::zero() {
let mut e = [m[0][0], m[1][1], m[2][2]];
sort_desc_3(&mut e);
return Ok(e);
}
let q = (m[0][0] + m[1][1] + m[2][2]) * third; let d00 = m[0][0] - q;
let d11 = m[1][1] - q;
let d22 = m[2][2] - q;
let p2 = d00 * d00 + d11 * d11 + d22 * d22 + two * p1;
let p = (p2 / six).sqrt();
let inv_p = R::one() / p;
let b00 = d00 * inv_p;
let b11 = d11 * inv_p;
let b22 = d22 * inv_p;
let b01 = m[0][1] * inv_p;
let b02 = m[0][2] * inv_p;
let b12 = m[1][2] * inv_p;
let det_b = b00 * (b11 * b22 - b12 * b12) - b01 * (b01 * b22 - b12 * b02)
+ b02 * (b01 * b12 - b11 * b02);
let mut r_val = det_b * half;
if r_val < -R::one() {
r_val = -R::one();
}
if r_val > R::one() {
r_val = R::one();
}
let phi = r_val.acos() * third;
let eig1 = q + two * p * phi.cos();
let eig3 = q + two * p * (phi + two_pi_over_3).cos();
let eig2 = three * q - eig1 - eig3;
let mut e = [eig1, eig2, eig3];
sort_desc_3(&mut e);
Ok(e)
}
fn sort_desc_3<R: RealField>(a: &mut [R; 3]) {
if a[0] < a[1] {
a.swap(0, 1);
}
if a[1] < a[2] {
a.swap(1, 2);
}
if a[0] < a[1] {
a.swap(0, 1);
}
}