pub trait Fiber: Clone + Send + Sync + 'static {
type Element: Clone + Send + Sync + Default;
const FIBER_DIM: usize;
fn zero() -> Self::Element;
fn transport_by(rotation: &[f64], d: usize, element: &Self::Element) -> Self::Element;
}
pub trait FiberOps: Fiber {
fn accumulate_diff(target: &mut Self::Element, a: &Self::Element, b: &Self::Element, scale: f64);
fn scale_element(target: &mut Self::Element, scale: f64);
}
pub trait Section<F: Fiber> {
fn n_vertices(&self) -> usize;
fn at(&self, v: usize) -> &F::Element;
fn at_mut(&mut self, v: usize) -> &mut F::Element;
}
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
#[cfg(feature = "alloc")]
#[derive(Clone, Debug)]
pub struct VecSection<F: Fiber> {
data: Vec<F::Element>,
_marker: core::marker::PhantomData<F>,
}
#[cfg(feature = "alloc")]
impl<F: Fiber> VecSection<F> {
pub fn zeros(n: usize) -> Self {
Self {
data: (0..n).map(|_| F::zero()).collect(),
_marker: core::marker::PhantomData,
}
}
pub fn from_vec(data: Vec<F::Element>) -> Self {
Self { data, _marker: core::marker::PhantomData }
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
}
#[cfg(feature = "alloc")]
impl<F: Fiber> Section<F> for VecSection<F> {
fn n_vertices(&self) -> usize { self.data.len() }
fn at(&self, v: usize) -> &F::Element { &self.data[v] }
fn at_mut(&mut self, v: usize) -> &mut F::Element { &mut self.data[v] }
}
#[derive(Clone, Debug)]
pub struct U1Spin2;
impl Fiber for U1Spin2 {
type Element = [f64; 2];
const FIBER_DIM: usize = 2;
fn zero() -> [f64; 2] { [0.0, 0.0] }
fn transport_by(rotation: &[f64], d: usize, element: &[f64; 2]) -> [f64; 2] {
debug_assert_eq!(d, 2, "U1Spin2 requires d=2");
debug_assert!(rotation.len() >= 4);
let cos_t = rotation[0];
let sin_t = rotation[2]; let cos_2t = cos_t * cos_t - sin_t * sin_t;
let sin_2t = 2.0 * sin_t * cos_t;
let q1 = element[0];
let q2 = element[1];
[cos_2t * q1 - sin_2t * q2, sin_2t * q1 + cos_2t * q2]
}
}
impl FiberOps for U1Spin2 {
fn accumulate_diff(target: &mut [f64; 2], a: &[f64; 2], b: &[f64; 2], scale: f64) {
target[0] += scale * (a[0] - b[0]);
target[1] += scale * (a[1] - b[1]);
}
fn scale_element(target: &mut [f64; 2], scale: f64) {
target[0] *= scale;
target[1] *= scale;
}
}
#[derive(Clone, Debug)]
pub struct TangentFiber<const D: usize>;
impl<const D: usize> Fiber for TangentFiber<D>
where
[f64; D]: Default,
{
type Element = [f64; D];
const FIBER_DIM: usize = D;
fn zero() -> [f64; D] { [0.0; D] }
fn transport_by(rotation: &[f64], d: usize, element: &[f64; D]) -> [f64; D] {
debug_assert_eq!(d, D);
debug_assert!(rotation.len() >= D * D);
let mut result = [0.0; D];
for i in 0..D {
for j in 0..D {
result[i] += rotation[i * D + j] * element[j];
}
}
result
}
}
impl<const D: usize> FiberOps for TangentFiber<D>
where
[f64; D]: Default,
{
fn accumulate_diff(target: &mut [f64; D], a: &[f64; D], b: &[f64; D], scale: f64) {
for (t, (ai, bi)) in target.iter_mut().zip(a.iter().zip(b.iter())) {
*t += scale * (ai - bi);
}
}
fn scale_element(target: &mut [f64; D], scale: f64) {
for t in target.iter_mut() {
*t *= scale;
}
}
}
#[derive(Clone, Debug)]
pub struct NematicFiber3D;
impl Fiber for NematicFiber3D {
type Element = [f64; 5];
const FIBER_DIM: usize = 5;
fn zero() -> [f64; 5] { [0.0; 5] }
fn transport_by(rotation: &[f64], d: usize, element: &[f64; 5]) -> [f64; 5] {
debug_assert_eq!(d, 3, "NematicFiber3D requires d=3");
debug_assert!(rotation.len() >= 9);
let (q11, q12, q13, q22, q23) = (element[0], element[1], element[2], element[3], element[4]);
let q33 = -q11 - q22;
let q = [[q11, q12, q13], [q12, q22, q23], [q13, q23, q33]];
let r = |i: usize, j: usize| rotation[i * 3 + j];
let mut qp = [[0.0_f64; 3]; 3];
for (i, qp_row) in qp.iter_mut().enumerate() {
for (j, qp_ij) in qp_row.iter_mut().enumerate() {
let mut sum = 0.0;
for (a, q_row) in q.iter().enumerate() {
for (b, &q_ab) in q_row.iter().enumerate() {
sum += r(i, a) * q_ab * r(j, b);
}
}
*qp_ij = sum;
}
}
[qp[0][0], qp[0][1], qp[0][2], qp[1][1], qp[1][2]]
}
}
impl FiberOps for NematicFiber3D {
fn accumulate_diff(target: &mut [f64; 5], a: &[f64; 5], b: &[f64; 5], scale: f64) {
for (t, (ai, bi)) in target.iter_mut().zip(a.iter().zip(b.iter())) {
*t += scale * (ai - bi);
}
}
fn scale_element(target: &mut [f64; 5], scale: f64) {
for t in target.iter_mut() {
*t *= scale;
}
}
}