use crate::{Displacement, Energy, Momentum, PhysicsError, Ratio, Speed, Stiffness, TwistAngle};
use deep_causality_num::Complex;
use deep_causality_tensor::{CausalTensor, Tensor};
use crate::constants::GRAPHENE_LATTICE_CONST;
use deep_causality_topology::SimplicialManifold;
use std::f64::consts::PI;
pub fn bistritzer_macdonald_kernel(
twist_angle: TwistAngle<f64>,
interlayer_coupling: Energy<f64>,
fermi_velocity: Speed<f64>,
k_point: Momentum,
shell_cutoff: usize,
) -> Result<CausalTensor<Complex<f64>>, PhysicsError> {
if shell_cutoff != 1 {
return Err(PhysicsError::CalculationError(
"Only shell_cutoff=1 is currently supported".into(),
));
}
let theta = twist_angle.value();
let w = interlayer_coupling.value();
let vf = fermi_velocity.value();
let hbar = crate::constants::REDUCED_PLANCK_CONSTANT;
let scale = hbar * vf;
let k_theta = (8.0 * PI / (3.0 * GRAPHENE_LATTICE_CONST)) * (theta / 2.0).sin();
let k_vec = k_point.inner().data();
let kx = k_vec.get(1).copied().unwrap_or(0.0);
let ky = k_vec.get(2).copied().unwrap_or(0.0);
let sqrt3 = 3.0f64.sqrt();
let q_vectors = [
(0.0, -k_theta),
(sqrt3 * 0.5 * k_theta, 0.5 * k_theta),
(-sqrt3 * 0.5 * k_theta, 0.5 * k_theta),
];
fn t_matrix(w: f64, phi: f64) -> [[Complex<f64>; 2]; 2] {
let z = Complex::new(0.0, phi).exp(); let z_conj = Complex::new(z.re, -z.im);
[
[Complex::new(w, 0.0), Complex::new(w, 0.0) * z_conj],
[Complex::new(w, 0.0) * z, Complex::new(w, 0.0)],
]
}
let t1 = t_matrix(w, 0.0);
let t2 = t_matrix(w, 2.0 * PI / 3.0);
let t3 = t_matrix(w, -2.0 * PI / 3.0);
let ts = [t1, t2, t3];
let dirac = |kx: f64, ky: f64| -> [[Complex<f64>; 2]; 2] {
let k_plus = Complex::new(kx, ky);
let k_minus = Complex::new(kx, -ky);
[
[Complex::new(0.0, 0.0), k_minus * scale],
[k_plus * scale, Complex::new(0.0, 0.0)],
]
};
let mut data = vec![Complex::new(0.0, 0.0); 64];
let set_block =
|d: &mut Vec<Complex<f64>>, row_blk: usize, col_blk: usize, mat: [[Complex<f64>; 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(
displacement_u: &Displacement,
youngs_modulus: Stiffness<f64>,
poisson_ratio: Ratio<f64>,
) -> Result<CausalTensor<f64>, PhysicsError> {
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::<f64>::trace(epsilon.clone(), 0, 1);
let trace_tensor = CausalTensor::ein_sum(&trace_op)?;
let tr_eps: f64 = trace_tensor.data()[0];
let shape = epsilon.shape();
let identity = CausalTensor::identity(shape)?;
let term1 = epsilon.clone() * (1.0 - nu);
let term2: CausalTensor<f64> = identity * (nu * tr_eps);
let sum = term1 + term2;
let prefactor = e / (1.0 - nu * nu);
let sigma = sum * prefactor;
Ok(sigma)
}
pub fn foppl_von_karman_strain_kernel(
u_manifold: &SimplicialManifold<f64, f64>,
w_manifold: &SimplicialManifold<f64, f64>,
youngs_modulus: Stiffness<f64>,
poisson_ratio: Ratio<f64>,
) -> Result<CausalTensor<f64>, PhysicsError> {
let dw = w_manifold.exterior_derivative(0);
let dw_sq = dw.clone() * dw.clone();
let strain_nonlinear = dw_sq * 0.5;
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 / (1.0 - nu * nu);
let sigma = epsilon * prefactor;
Ok(sigma)
}