use crate::{
ChemicalPotentialGradient, Concentration, Energy, Mobility, OrderParameter, PhysicsError,
VectorPotential,
};
use deep_causality_multivector::CausalMultiVector;
use deep_causality_num::{Complex, DivisionAlgebra};
use deep_causality_tensor::CausalTensor;
pub fn ginzburg_landau_free_energy_kernel(
psi: OrderParameter,
alpha: f64,
beta: f64,
gradient_psi: &CausalMultiVector<Complex<f64>>,
vector_potential: Option<&VectorPotential>,
) -> Result<Energy<f64>, PhysicsError> {
let val = psi.value();
let mag_sq = psi.magnitude_squared();
let potential_term = alpha * mag_sq + (beta / 2.0) * 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(0.0, 1.0) * 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, 0.0) * i_psi;
(*g - term_a).norm_sqr()
})
.sum::<f64>()
} else {
gradient_psi
.data()
.iter()
.map(|c| c.norm_sqr())
.sum::<f64>()
};
let total = potential_term + kinetic_norm_sq;
Energy::new(total)
}
pub fn cahn_hilliard_flux_kernel(
concentration: &Concentration,
mobility: Mobility<f64>,
chem_potential_grad: &ChemicalPotentialGradient,
) -> Result<CausalTensor<f64>, PhysicsError> {
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<f64> = CausalTensor::one(c_tensor.shape());
let one_minus_c: CausalTensor<f64> = ones - c_tensor.clone();
let c_factor = c_tensor.clone() * one_minus_c;
let mobility_field: CausalTensor<f64> = 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 flux_data: Vec<f64> = m_data
.iter()
.zip(g_data.iter())
.map(|(&m_val, &g_val): (&f64, &f64)| {
let m_clamped = m_val.max(0.0);
-m_clamped * g_val
})
.collect();
CausalTensor::new(flux_data, grad_mu.shape().to_vec()).map_err(PhysicsError::from)
}