use crate::constants::real_from_f64;
use crate::{
ChemicalPotentialGradient, Concentration, Energy, Mobility, OrderParameter, PhysicsError,
VectorPotential,
};
use deep_causality_algebra::{DivisionAlgebra, RealField};
use deep_causality_multivector::CausalMultiVector;
use deep_causality_num::FromPrimitive;
use deep_causality_num_complex::Complex;
use deep_causality_tensor::CausalTensor;
use std::iter::Sum;
pub fn ginzburg_landau_free_energy_kernel<R>(
psi: OrderParameter<R>,
alpha: R,
beta: R,
gradient_psi: &CausalMultiVector<Complex<R>>,
vector_potential: Option<&VectorPotential<R>>,
) -> Result<Energy<R>, PhysicsError>
where
R: RealField + FromPrimitive + Sum,
{
let two = real_from_f64::<R>(2.0);
let val = psi.value();
let mag_sq = psi.magnitude_squared();
let potential_term = alpha * mag_sq + (beta / two) * mag_sq * mag_sq;
let kinetic_norm_sq = if let Some(a_wrapper) = vector_potential {
let a = a_wrapper.inner();
if a.metric() != gradient_psi.metric() {
return Err(PhysicsError::DimensionMismatch(
"Metric mismatch between gradient and vector potential".into(),
));
}
let i_psi = Complex::new(R::zero(), R::one()) * val;
let a_data = a.data();
let grad_data = gradient_psi.data();
if a_data.len() != grad_data.len() {
return Err(PhysicsError::DimensionMismatch(
"Vector size mismatch".into(),
));
}
gradient_psi
.data()
.iter()
.zip(a.data().iter())
.map(|(g, a_val)| {
let term_a = Complex::new(*a_val, R::zero()) * i_psi;
(*g - term_a).norm_sqr()
})
.sum::<R>()
} else {
gradient_psi.data().iter().map(|c| c.norm_sqr()).sum::<R>()
};
let total = potential_term + kinetic_norm_sq;
Energy::new(total)
}
pub fn cahn_hilliard_flux_kernel<R>(
concentration: &Concentration<R>,
mobility: Mobility<R>,
chem_potential_grad: &ChemicalPotentialGradient<R>,
) -> Result<CausalTensor<R>, PhysicsError>
where
R: RealField,
{
let grad_mu = chem_potential_grad.inner();
let c_tensor = concentration.inner();
let m0 = mobility.value();
if c_tensor.shape() != grad_mu.shape() {
return Err(PhysicsError::DimensionMismatch(format!(
"Concentration shape {:?} != Gradient shape {:?}",
c_tensor.shape(),
grad_mu.shape()
)));
}
let ones: CausalTensor<R> = CausalTensor::one(c_tensor.shape());
let one_minus_c: CausalTensor<R> = ones - c_tensor.clone();
let c_factor = c_tensor.clone() * one_minus_c;
let mobility_field: CausalTensor<R> = c_factor * m0;
let m_data = mobility_field.as_slice();
let g_data = grad_mu.as_slice();
if m_data.len() != g_data.len() {
return Err(PhysicsError::DimensionMismatch(
"Mobility field size does not match gradient field size".into(),
));
}
let zero = R::zero();
let flux_data: Vec<R> = m_data
.iter()
.zip(g_data.iter())
.map(|(&m_val, &g_val): (&R, &R)| {
let m_clamped = if m_val < zero { zero } else { m_val };
-m_clamped * g_val
})
.collect();
CausalTensor::new(flux_data, grad_mu.shape().to_vec()).map_err(PhysicsError::from)
}