Skip to main content

conspire/math/tensor/rank_3/
mod.rs

1#[cfg(test)]
2mod test;
3use crate::math::{Current, Reference};
4use crate::units::Dimensionless;
5
6use crate::math::assert::FiniteDifference;
7
8use std::{
9    array::from_fn,
10    fmt::{self, Debug, Display, Formatter},
11    iter::Sum,
12    marker::PhantomData,
13    ops::{Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Sub, SubAssign},
14};
15
16use super::{
17    Tensor, TensorArray,
18    rank_0::TensorRank0,
19    rank_2::{
20        TensorRank2, get_identity_1010_parts_1, get_identity_1010_parts_2,
21        get_identity_1010_parts_3, get_levi_civita_parts,
22    },
23};
24
25/// Returns the rank-3 Levi-Civita symbol.
26pub fn levi_civita<I, J, K>() -> TensorRank3<3, I, J, K> {
27    TensorRank3::from([
28        [[0.0, 0.0, 0.0], [0.0, 0.0, 1.0], [0.0, -1.0, 0.0]],
29        [[0.0, 0.0, -1.0], [0.0, 0.0, 0.0], [1.0, 0.0, 0.0]],
30        [[0.0, 1.0, 0.0], [-1.0, 0.0, 0.0], [0.0, 0.0, 0.0]],
31    ])
32}
33
34/// A *d*-dimensional tensor of rank 3.
35///
36/// `D` is the dimension, `I`, `J`, `K` are the configurations.
37#[repr(transparent)]
38pub struct TensorRank3<const D: usize, I, J, K, U = Dimensionless>(
39    [TensorRank2<D, J, K, U>; D],
40    pub(super) PhantomData<I>,
41);
42
43impl<const D: usize, I, J, K, U> Clone for TensorRank3<D, I, J, K, U> {
44    fn clone(&self) -> Self {
45        Self(self.0.clone(), PhantomData)
46    }
47}
48
49impl<const D: usize, I, J, K, U> Debug for TensorRank3<D, I, J, K, U> {
50    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
51        self.0.fmt(f)
52    }
53}
54
55impl<const D: usize, I, J, K, U> PartialEq for TensorRank3<D, I, J, K, U> {
56    fn eq(&self, other: &Self) -> bool {
57        self.0 == other.0
58    }
59}
60
61impl<const D: usize, I, J, K, U> TensorRank3<D, I, J, K, U> {
62    fn canonical(&self) -> &TensorRank3<D, Reference, Reference, Reference, Dimensionless> {
63        unsafe {
64            &*(self as *const Self
65                as *const TensorRank3<D, Reference, Reference, Reference, Dimensionless>)
66        }
67    }
68    fn canonical_mut(
69        &mut self,
70    ) -> &mut TensorRank3<D, Reference, Reference, Reference, Dimensionless> {
71        unsafe {
72            &mut *(self as *mut Self
73                as *mut TensorRank3<D, Reference, Reference, Reference, Dimensionless>)
74        }
75    }
76    fn into_canonical(self) -> TensorRank3<D, Reference, Reference, Reference, Dimensionless> {
77        unsafe {
78            (&self as *const Self)
79                .cast::<TensorRank3<D, Reference, Reference, Reference, Dimensionless>>()
80                .read()
81        }
82    }
83}
84
85pub(super) fn relabel<const D: usize, I, J, K, U>(
86    tensor: TensorRank3<D, Reference, Reference, Reference, Dimensionless>,
87) -> TensorRank3<D, I, J, K, U> {
88    unsafe {
89        (&tensor as *const TensorRank3<D, Reference, Reference, Reference, Dimensionless>)
90            .cast::<TensorRank3<D, I, J, K, U>>()
91            .read()
92    }
93}
94
95impl<const D: usize> TensorRank3<D, Reference, Reference, Reference, Dimensionless> {
96    fn as_array_core(&self) -> [[[TensorRank0; D]; D]; D] {
97        let mut array = [[[0.0; D]; D]; D];
98        array
99            .iter_mut()
100            .zip(self.iter())
101            .for_each(|(entry_rank_2, tensor_rank_2)| *entry_rank_2 = tensor_rank_2.as_array());
102        array
103    }
104    fn zero_core() -> Self {
105        Self(from_fn(|_| TensorRank2::zero()), PhantomData)
106    }
107    fn add_assign_core(&mut self, tensor: Self) {
108        self.iter_mut()
109            .zip(tensor)
110            .for_each(|(self_i, tensor_i)| *self_i += tensor_i);
111    }
112    fn add_assign_ref_core(&mut self, tensor: &Self) {
113        self.iter_mut()
114            .zip(tensor.iter())
115            .for_each(|(self_i, tensor_i)| *self_i += tensor_i);
116    }
117    fn sub_assign_core(&mut self, tensor: Self) {
118        self.iter_mut()
119            .zip(tensor)
120            .for_each(|(self_i, tensor_i)| *self_i -= tensor_i);
121    }
122    fn sub_assign_ref_core(&mut self, tensor: &Self) {
123        self.iter_mut()
124            .zip(tensor.iter())
125            .for_each(|(self_i, tensor_i)| *self_i -= tensor_i);
126    }
127}
128
129impl<const D: usize, I, J, K, U> Default for TensorRank3<D, I, J, K, U> {
130    fn default() -> Self {
131        Self::zero()
132    }
133}
134
135impl<const D: usize, I, J, K, U> From<[[[TensorRank0; D]; D]; D]> for TensorRank3<D, I, J, K, U> {
136    fn from(array: [[[TensorRank0; D]; D]; D]) -> Self {
137        array.into_iter().map(|entry| entry.into()).collect()
138    }
139}
140
141/// The 3D Levi-Civita permutation tensor, configurations (1, 1, 1).
142pub const LEVI_CIVITA: TensorRank3<3, Current, Current, Current, Dimensionless> =
143    TensorRank3(get_levi_civita_parts(), PhantomData);
144
145pub(crate) const fn get_identity_1010_parts<I, J, K, U>() -> [TensorRank3<3, I, J, K, U>; 3] {
146    [
147        TensorRank3(get_identity_1010_parts_1(), PhantomData),
148        TensorRank3(get_identity_1010_parts_2(), PhantomData),
149        TensorRank3(get_identity_1010_parts_3(), PhantomData),
150    ]
151}
152
153impl<const D: usize, I, J, K, U> Display for TensorRank3<D, I, J, K, U> {
154    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
155        write!(f, "[")?;
156        self.iter()
157            .enumerate()
158            .try_for_each(|(i, entry)| write!(f, "{entry},\n\x1B[u\x1B[{}B\x1B[1D", i + 1))?;
159        write!(f, "\x1B[u\x1B[1A\x1B[{}C]", 16 * D + 1)
160    }
161}
162
163impl<const D: usize, I, J, K, U> FiniteDifference for TensorRank3<D, I, J, K, U> {
164    fn error_fd(&self, comparator: &Self, epsilon: TensorRank0) -> Option<(bool, usize)> {
165        let error_count = self
166            .iter()
167            .zip(comparator.iter())
168            .map(|(self_i, comparator_i)| {
169                self_i
170                    .iter()
171                    .zip(comparator_i.iter())
172                    .map(|(self_ij, comparator_ij)| {
173                        self_ij
174                            .iter()
175                            .zip(comparator_ij.iter())
176                            .filter(|&(&self_ijk, &comparator_ijk)| {
177                                self_ijk.differs(comparator_ijk, epsilon)
178                            })
179                            .count()
180                    })
181                    .sum::<usize>()
182            })
183            .sum();
184        if error_count > 0 {
185            Some((true, error_count))
186        } else {
187            None
188        }
189    }
190}
191
192impl<const D: usize, I, J, K, U> Tensor for TensorRank3<D, I, J, K, U> {
193    type Item = TensorRank2<D, J, K, U>;
194    type Unit = U;
195    fn iter(&self) -> impl Iterator<Item = &Self::Item> {
196        self.0.iter()
197    }
198    fn iter_mut(&mut self) -> impl Iterator<Item = &mut Self::Item> {
199        self.0.iter_mut()
200    }
201    fn len(&self) -> usize {
202        D
203    }
204    fn size(&self) -> usize {
205        D * D * D
206    }
207}
208
209impl<const D: usize, I, J, K, U> IntoIterator for TensorRank3<D, I, J, K, U> {
210    type Item = TensorRank2<D, J, K, U>;
211    type IntoIter = std::array::IntoIter<Self::Item, D>;
212    fn into_iter(self) -> Self::IntoIter {
213        self.0.into_iter()
214    }
215}
216
217impl<const D: usize, I, J, K, U> TensorArray for TensorRank3<D, I, J, K, U> {
218    type Array = [[[TensorRank0; D]; D]; D];
219    type Item = TensorRank2<D, J, K, U>;
220    fn as_array(&self) -> Self::Array {
221        self.canonical().as_array_core()
222    }
223    fn identity() -> Self {
224        panic!()
225    }
226    fn zero() -> Self {
227        relabel(TensorRank3::<
228            D,
229            Reference,
230            Reference,
231            Reference,
232            Dimensionless,
233        >::zero_core())
234    }
235}
236
237impl<const D: usize, I, J, K, U> FromIterator<TensorRank2<D, J, K, U>>
238    for TensorRank3<D, I, J, K, U>
239{
240    fn from_iter<Ii: IntoIterator<Item = TensorRank2<D, J, K, U>>>(into_iterator: Ii) -> Self {
241        let mut tensor_rank_3 = Self::zero();
242        tensor_rank_3
243            .iter_mut()
244            .zip(into_iterator)
245            .for_each(|(tensor_rank_3_i, value_i)| *tensor_rank_3_i = value_i);
246        tensor_rank_3
247    }
248}
249
250impl<const D: usize, I, J, K, U> Index<usize> for TensorRank3<D, I, J, K, U> {
251    type Output = TensorRank2<D, J, K, U>;
252    fn index(&self, index: usize) -> &Self::Output {
253        &self.0[index]
254    }
255}
256
257impl<const D: usize, I, J, K, U> IndexMut<usize> for TensorRank3<D, I, J, K, U> {
258    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
259        &mut self.0[index]
260    }
261}
262
263impl<const D: usize, I, J, K, U> Sum for TensorRank3<D, I, J, K, U> {
264    fn sum<Ii>(iter: Ii) -> Self
265    where
266        Ii: Iterator<Item = Self>,
267    {
268        iter.reduce(|mut acc, item| {
269            acc += item;
270            acc
271        })
272        .unwrap_or_else(Self::default)
273    }
274}
275
276impl<'a, const D: usize, I, J, K, U> Sum<&'a Self> for TensorRank3<D, I, J, K, U> {
277    fn sum<Ii>(iter: Ii) -> Self
278    where
279        Ii: Iterator<Item = &'a Self>,
280    {
281        iter.fold(Self::default(), |mut acc, item| {
282            acc += item;
283            acc
284        })
285    }
286}
287
288impl<const D: usize, I, J, K, U> Div<TensorRank0> for TensorRank3<D, I, J, K, U> {
289    type Output = Self;
290    fn div(mut self, tensor_rank_0: TensorRank0) -> Self::Output {
291        self /= &tensor_rank_0;
292        self
293    }
294}
295
296impl<const D: usize, I, J, K, U> Div<TensorRank0> for &TensorRank3<D, I, J, K, U> {
297    type Output = TensorRank3<D, I, J, K, U>;
298    fn div(self, tensor_rank_0: TensorRank0) -> Self::Output {
299        self.iter().map(|self_i| self_i / tensor_rank_0).collect()
300    }
301}
302
303impl<const D: usize, I, J, K, U> Div<&TensorRank0> for TensorRank3<D, I, J, K, U> {
304    type Output = Self;
305    fn div(mut self, tensor_rank_0: &TensorRank0) -> Self::Output {
306        self /= tensor_rank_0;
307        self
308    }
309}
310
311impl<const D: usize, I, J, K, U> DivAssign<TensorRank0> for TensorRank3<D, I, J, K, U> {
312    fn div_assign(&mut self, tensor_rank_0: TensorRank0) {
313        self.iter_mut().for_each(|self_i| *self_i /= &tensor_rank_0);
314    }
315}
316
317impl<const D: usize, I, J, K, U> DivAssign<&TensorRank0> for TensorRank3<D, I, J, K, U> {
318    fn div_assign(&mut self, tensor_rank_0: &TensorRank0) {
319        self.iter_mut().for_each(|self_i| *self_i /= tensor_rank_0);
320    }
321}
322
323impl<const D: usize, I, J, K, U> Mul<TensorRank0> for TensorRank3<D, I, J, K, U> {
324    type Output = Self;
325    fn mul(mut self, tensor_rank_0: TensorRank0) -> Self::Output {
326        self *= &tensor_rank_0;
327        self
328    }
329}
330
331impl<const D: usize, I, J, K, U> Mul<&TensorRank0> for TensorRank3<D, I, J, K, U> {
332    type Output = Self;
333    fn mul(mut self, tensor_rank_0: &TensorRank0) -> Self::Output {
334        self *= tensor_rank_0;
335        self
336    }
337}
338
339impl<const D: usize, I, J, K, U> Mul<TensorRank0> for &TensorRank3<D, I, J, K, U> {
340    type Output = TensorRank3<D, I, J, K, U>;
341    fn mul(self, tensor_rank_0: TensorRank0) -> Self::Output {
342        self.iter().map(|self_i| self_i * tensor_rank_0).collect()
343    }
344}
345
346impl<const D: usize, I, J, K, U> Mul<&TensorRank0> for &TensorRank3<D, I, J, K, U> {
347    type Output = TensorRank3<D, I, J, K, U>;
348    fn mul(self, tensor_rank_0: &TensorRank0) -> Self::Output {
349        self.iter().map(|self_i| self_i * tensor_rank_0).collect()
350    }
351}
352
353impl<const D: usize, I, J, K, U> MulAssign<TensorRank0> for TensorRank3<D, I, J, K, U> {
354    fn mul_assign(&mut self, tensor_rank_0: TensorRank0) {
355        self.iter_mut().for_each(|self_i| *self_i *= &tensor_rank_0);
356    }
357}
358
359impl<const D: usize, I, J, K, U> MulAssign<&TensorRank0> for TensorRank3<D, I, J, K, U> {
360    fn mul_assign(&mut self, tensor_rank_0: &TensorRank0) {
361        self.iter_mut().for_each(|self_i| *self_i *= tensor_rank_0);
362    }
363}
364
365impl<const D: usize, I, J, K, U> Add for TensorRank3<D, I, J, K, U> {
366    type Output = Self;
367    fn add(mut self, tensor_rank_3: Self) -> Self::Output {
368        self += tensor_rank_3;
369        self
370    }
371}
372
373impl<const D: usize, I, J, K, U> Add<&Self> for TensorRank3<D, I, J, K, U> {
374    type Output = Self;
375    fn add(mut self, tensor_rank_3: &Self) -> Self::Output {
376        self += tensor_rank_3;
377        self
378    }
379}
380
381impl<const D: usize, I, J, K, U> Add<TensorRank3<D, I, J, K, U>> for &TensorRank3<D, I, J, K, U> {
382    type Output = TensorRank3<D, I, J, K, U>;
383    fn add(self, mut tensor_rank_3: TensorRank3<D, I, J, K, U>) -> Self::Output {
384        tensor_rank_3 += self;
385        tensor_rank_3
386    }
387}
388
389impl<const D: usize, I, J, K, U> AddAssign for TensorRank3<D, I, J, K, U> {
390    fn add_assign(&mut self, tensor_rank_3: Self) {
391        self.canonical_mut()
392            .add_assign_core(tensor_rank_3.into_canonical());
393    }
394}
395
396impl<const D: usize, I, J, K, U> AddAssign<&Self> for TensorRank3<D, I, J, K, U> {
397    fn add_assign(&mut self, tensor_rank_3: &Self) {
398        self.canonical_mut()
399            .add_assign_ref_core(tensor_rank_3.canonical());
400    }
401}
402
403impl<const D: usize, I, J, K, U> Sub for TensorRank3<D, I, J, K, U> {
404    type Output = Self;
405    fn sub(mut self, tensor_rank_3: Self) -> Self::Output {
406        self -= tensor_rank_3;
407        self
408    }
409}
410
411impl<const D: usize, I, J, K, U> Sub<&Self> for TensorRank3<D, I, J, K, U> {
412    type Output = Self;
413    fn sub(mut self, tensor_rank_3: &Self) -> Self::Output {
414        self -= tensor_rank_3;
415        self
416    }
417}
418
419impl<const D: usize, I, J, K, U> Sub for &TensorRank3<D, I, J, K, U> {
420    type Output = TensorRank3<D, I, J, K, U>;
421    fn sub(self, tensor_rank_3: Self) -> Self::Output {
422        tensor_rank_3
423            .iter()
424            .zip(self.iter())
425            .map(|(tensor_rank_3_i, self_i)| self_i - tensor_rank_3_i)
426            .collect()
427    }
428}
429
430impl<const D: usize, I, J, K, U> SubAssign for TensorRank3<D, I, J, K, U> {
431    fn sub_assign(&mut self, tensor_rank_3: Self) {
432        self.canonical_mut()
433            .sub_assign_core(tensor_rank_3.into_canonical());
434    }
435}
436
437impl<const D: usize, I, J, K, U> SubAssign<&Self> for TensorRank3<D, I, J, K, U> {
438    fn sub_assign(&mut self, tensor_rank_3: &Self) {
439        self.canonical_mut()
440            .sub_assign_ref_core(tensor_rank_3.canonical());
441    }
442}