use crate::{Displacement, Energy, Momentum, PhysicsError, Ratio, Speed, Stiffness, TwistAngle};
use core::fmt::Debug;
use deep_causality_algebra::RealField;
use deep_causality_num::FromPrimitive;
use deep_causality_num_complex::Complex;
use deep_causality_par::MaybeParallel;
use deep_causality_tensor::{CausalTensor, Tensor};
use crate::constants::{graphene_lattice_const, real_from_f64, reduced_planck_constant};
use deep_causality_topology::SimplicialManifold;
use std::f64::consts::PI;
pub fn bistritzer_macdonald_kernel<R>(
twist_angle: TwistAngle<R>,
interlayer_coupling: Energy<R>,
fermi_velocity: Speed<R>,
k_point: Momentum<R>,
shell_cutoff: usize,
) -> Result<CausalTensor<Complex<R>>, PhysicsError>
where
R: RealField + FromPrimitive,
{
if shell_cutoff != 1 {
return Err(PhysicsError::CalculationError(
"Only shell_cutoff=1 is currently supported".into(),
));
}
let zero = R::zero();
let two = real_from_f64::<R>(2.0);
let three = real_from_f64::<R>(3.0);
let eight = real_from_f64::<R>(8.0);
let half = real_from_f64::<R>(0.5);
let pi = real_from_f64::<R>(PI);
let theta = twist_angle.value();
let w = interlayer_coupling.value();
let vf = fermi_velocity.value();
let hbar = reduced_planck_constant::<R>();
let scale = hbar * vf;
let a = graphene_lattice_const::<R>();
let k_theta = (eight * pi / (three * a)) * (theta / two).sin();
let k_vec = k_point.inner().data();
let kx = k_vec.get(1).copied().unwrap_or(zero);
let ky = k_vec.get(2).copied().unwrap_or(zero);
let sqrt3 = real_from_f64::<R>(3.0_f64.sqrt());
let q_vectors = [
(zero, -k_theta),
(sqrt3 * half * k_theta, half * k_theta),
(-sqrt3 * half * k_theta, half * k_theta),
];
fn t_matrix<R: RealField>(w: R, phi: R) -> [[Complex<R>; 2]; 2] {
let z = Complex::new(R::zero(), phi).exp(); let z_conj = Complex::new(z.re, -z.im);
[
[
Complex::new(w, R::zero()),
Complex::new(w, R::zero()) * z_conj,
],
[Complex::new(w, R::zero()) * z, Complex::new(w, R::zero())],
]
}
let t1 = t_matrix(w, zero);
let t2 = t_matrix(w, two * pi / three);
let t3 = t_matrix(w, -(two * pi / three));
let ts = [t1, t2, t3];
let dirac = |kx: R, ky: R| -> [[Complex<R>; 2]; 2] {
let k_plus = Complex::new(kx, ky);
let k_minus = Complex::new(kx, -ky);
[
[Complex::new(zero, zero), k_minus * scale],
[k_plus * scale, Complex::new(zero, zero)],
]
};
let mut data = vec![Complex::new(zero, zero); 64];
let set_block =
|d: &mut Vec<Complex<R>>, row_blk: usize, col_blk: usize, mat: [[Complex<R>; 2]; 2]| {
for (r, row) in mat.iter().enumerate() {
for (c, &val) in row.iter().enumerate() {
let gr = row_blk * 2 + r;
let gc = col_blk * 2 + c;
d[gr * 8 + gc] = val;
}
}
};
set_block(&mut data, 0, 0, dirac(kx, ky));
for j in 0..3 {
let blk_idx = j + 1;
let (qx, qy) = q_vectors[j];
let kx2 = kx + qx;
let ky2 = ky + qy;
set_block(&mut data, blk_idx, blk_idx, dirac(kx2, ky2));
let t = ts[j];
set_block(&mut data, blk_idx, 0, t);
let t_dag = [
[
Complex::new(t[0][0].re, -t[0][0].im),
Complex::new(t[1][0].re, -t[1][0].im),
],
[
Complex::new(t[0][1].re, -t[0][1].im),
Complex::new(t[1][1].re, -t[1][1].im),
],
];
set_block(&mut data, 0, blk_idx, t_dag);
}
CausalTensor::new(data, vec![8, 8]).map_err(PhysicsError::from)
}
pub fn foppl_von_karman_strain_simple_kernel<R>(
displacement_u: &Displacement<R>,
youngs_modulus: Stiffness<R>,
poisson_ratio: Ratio<R>,
) -> Result<CausalTensor<R>, PhysicsError>
where
R: RealField + FromPrimitive + Default,
{
let one = R::one();
let epsilon = displacement_u.inner();
let e = youngs_modulus.value();
let nu = poisson_ratio.value();
if epsilon.num_dim() != 2 {
return Err(PhysicsError::DimensionMismatch(
"Strain tensor must be Rank 2".into(),
));
}
let trace_op = deep_causality_tensor::EinSumOp::<R>::trace(epsilon.clone(), 0, 1);
let trace_tensor = CausalTensor::ein_sum(&trace_op)?;
let tr_eps: R = trace_tensor.data()[0];
let shape = epsilon.shape();
let identity = CausalTensor::identity(shape)?;
let term1 = epsilon.clone() * (one - nu);
let term2: CausalTensor<R> = identity * (nu * tr_eps);
let sum = term1 + term2;
let prefactor = e / (one - nu * nu);
let sigma = sum * prefactor;
Ok(sigma)
}
pub fn foppl_von_karman_strain_kernel<R>(
u_manifold: &SimplicialManifold<R, R>,
w_manifold: &SimplicialManifold<R, R>,
youngs_modulus: Stiffness<R>,
poisson_ratio: Ratio<R>,
) -> Result<CausalTensor<R>, PhysicsError>
where
R: RealField + FromPrimitive + MaybeParallel + Default + Debug,
{
let one = R::one();
let half = real_from_f64::<R>(0.5);
let dw = w_manifold.exterior_derivative(0);
let dw_sq = dw.clone() * dw.clone();
let strain_nonlinear = dw_sq * half;
let du = u_manifold.exterior_derivative(0);
let strain_linear = du;
if strain_linear.shape() != strain_nonlinear.shape() {
return Err(PhysicsError::DimensionMismatch(
"Strain term shape mismatch between linear and non-linear components".into(),
));
}
let epsilon = strain_linear + strain_nonlinear;
let e = youngs_modulus.value();
let nu = poisson_ratio.value();
let prefactor = e / (one - nu * nu);
let sigma = epsilon * prefactor;
Ok(sigma)
}