use crate::PhysicsError;
use crate::VelocityGradient;
use crate::kernels::fluids::kinematics::velocity_gradient_invariants_kernel;
use deep_causality_algebra::RealField;
use deep_causality_linear::{eigen_symmetric_3x3, trace_of_square_3x3};
use deep_causality_num::FromPrimitive;
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();
Ok(-half * trace_of_square_3x3(g))
}
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 = (-q_d * half + sqrt_disc).cbrt();
let u2 = (-q_d * half - sqrt_disc).cbrt();
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 symmetric_3x3_eigenvalues<R>(m: &[[R; 3]; 3]) -> Result<[R; 3], PhysicsError>
where
R: RealField + FromPrimitive,
{
Ok(eigen_symmetric_3x3(m)?)
}