1use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub, SubAssign};
32
33use super::spherical_tensor::SphericalTensor;
34use super::symm_tensor::SymmTensor;
35use super::vector::Vector3;
36
37#[derive(Debug, Clone, Copy, PartialEq, Default)]
41pub struct Tensor {
42 pub xx: f64,
43 pub xy: f64,
44 pub xz: f64,
45 pub yx: f64,
46 pub yy: f64,
47 pub yz: f64,
48 pub zx: f64,
49 pub zy: f64,
50 pub zz: f64,
51}
52
53impl Tensor {
54 pub const ZERO: Self = Self {
56 xx: 0.0,
57 xy: 0.0,
58 xz: 0.0,
59 yx: 0.0,
60 yy: 0.0,
61 yz: 0.0,
62 zx: 0.0,
63 zy: 0.0,
64 zz: 0.0,
65 };
66 pub const IDENTITY: Self = Self {
68 xx: 1.0,
69 xy: 0.0,
70 xz: 0.0,
71 yx: 0.0,
72 yy: 1.0,
73 yz: 0.0,
74 zx: 0.0,
75 zy: 0.0,
76 zz: 1.0,
77 };
78
79 #[allow(clippy::too_many_arguments)]
81 #[inline]
82 pub fn new(
83 xx: f64,
84 xy: f64,
85 xz: f64,
86 yx: f64,
87 yy: f64,
88 yz: f64,
89 zx: f64,
90 zy: f64,
91 zz: f64,
92 ) -> Self {
93 Self {
94 xx,
95 xy,
96 xz,
97 yx,
98 yy,
99 yz,
100 zx,
101 zy,
102 zz,
103 }
104 }
105
106 #[inline]
108 pub fn from_rows(x: Vector3, y: Vector3, z: Vector3) -> Self {
109 Self {
110 xx: x.x,
111 xy: x.y,
112 xz: x.z,
113 yx: y.x,
114 yy: y.y,
115 yz: y.z,
116 zx: z.x,
117 zy: z.y,
118 zz: z.z,
119 }
120 }
121
122 #[inline]
124 pub fn from_cols(x: Vector3, y: Vector3, z: Vector3) -> Self {
125 Self {
126 xx: x.x,
127 xy: y.x,
128 xz: z.x,
129 yx: x.y,
130 yy: y.y,
131 yz: z.y,
132 zx: x.z,
133 zy: y.z,
134 zz: z.z,
135 }
136 }
137
138 #[inline]
140 pub fn row_x(self) -> Vector3 {
141 Vector3::new(self.xx, self.xy, self.xz)
142 }
143 #[inline]
145 pub fn row_y(self) -> Vector3 {
146 Vector3::new(self.yx, self.yy, self.yz)
147 }
148 #[inline]
150 pub fn row_z(self) -> Vector3 {
151 Vector3::new(self.zx, self.zy, self.zz)
152 }
153
154 #[inline]
156 pub fn col_x(self) -> Vector3 {
157 Vector3::new(self.xx, self.yx, self.zx)
158 }
159 #[inline]
161 pub fn col_y(self) -> Vector3 {
162 Vector3::new(self.xy, self.yy, self.zy)
163 }
164 #[inline]
166 pub fn col_z(self) -> Vector3 {
167 Vector3::new(self.xz, self.yz, self.zz)
168 }
169
170 #[inline]
172 pub fn diag(self) -> Vector3 {
173 Vector3::new(self.xx, self.yy, self.zz)
174 }
175
176 #[inline]
178 pub fn tr(self) -> f64 {
179 self.xx + self.yy + self.zz
180 }
181
182 #[inline]
184 pub fn diag_sqr(self) -> f64 {
185 self.xx * self.xx + self.yy * self.yy + self.zz * self.zz
186 }
187
188 #[inline]
190 pub fn transpose(self) -> Self {
191 Self {
192 xx: self.xx,
193 xy: self.yx,
194 xz: self.zx,
195 yx: self.xy,
196 yy: self.yy,
197 yz: self.zy,
198 zx: self.xz,
199 zy: self.yz,
200 zz: self.zz,
201 }
202 }
203
204 #[inline]
206 pub fn det(self) -> f64 {
207 self.xx * (self.yy * self.zz - self.yz * self.zy)
208 + self.xy * (self.yz * self.zx - self.yx * self.zz)
209 + self.xz * (self.yx * self.zy - self.yy * self.zx)
210 }
211
212 #[inline]
214 pub fn adjunct(self) -> Self {
215 Self {
216 xx: self.yy * self.zz - self.zy * self.yz,
217 xy: self.xz * self.zy - self.xy * self.zz,
218 xz: self.xy * self.yz - self.xz * self.yy,
219
220 yx: self.zx * self.yz - self.yx * self.zz,
221 yy: self.xx * self.zz - self.xz * self.zx,
222 yz: self.yx * self.xz - self.xx * self.yz,
223
224 zx: self.yx * self.zy - self.yy * self.zx,
225 zy: self.xy * self.zx - self.xx * self.zy,
226 zz: self.xx * self.yy - self.yx * self.xy,
227 }
228 }
229
230 #[inline]
232 pub fn cof(self) -> Self {
233 self.adjunct().transpose()
234 }
235
236 #[inline]
238 pub fn inv(self) -> Self {
239 let d = self.det();
240 debug_assert!(d.abs() > 0.0, "Tensor is singular");
241 self.adjunct() / d
242 }
243
244 pub fn safe_inv(self) -> Self {
246 use super::scalar::{ROOT_VSMALL, SMALL};
247 let diag_sqr = self.diag_sqr();
248 let threshold = SMALL * diag_sqr;
249 let mut work = self;
250 let small_xx = self.xx * self.xx < threshold;
251 let small_yy = self.yy * self.yy < threshold;
252 let small_zz = self.zz * self.zz < threshold;
253 if small_xx {
254 work.xx += 1.0;
255 }
256 if small_yy {
257 work.yy += 1.0;
258 }
259 if small_zz {
260 work.zz += 1.0;
261 }
262 let d = work.det();
263 if d.abs() < ROOT_VSMALL {
264 return Self::ZERO;
265 }
266 let mut result = work.adjunct() / d;
267 if small_xx {
268 result.xx -= 1.0;
269 }
270 if small_yy {
271 result.yy -= 1.0;
272 }
273 if small_zz {
274 result.zz -= 1.0;
275 }
276 result
277 }
278
279 #[inline]
281 pub fn mat_mul(self, t: Self) -> Self {
282 Self {
283 xx: self.xx * t.xx + self.xy * t.yx + self.xz * t.zx,
284 xy: self.xx * t.xy + self.xy * t.yy + self.xz * t.zy,
285 xz: self.xx * t.xz + self.xy * t.yz + self.xz * t.zz,
286
287 yx: self.yx * t.xx + self.yy * t.yx + self.yz * t.zx,
288 yy: self.yx * t.xy + self.yy * t.yy + self.yz * t.zy,
289 yz: self.yx * t.xz + self.yy * t.yz + self.yz * t.zz,
290
291 zx: self.zx * t.xx + self.zy * t.yx + self.zz * t.zx,
292 zy: self.zx * t.xy + self.zy * t.yy + self.zz * t.zy,
293 zz: self.zx * t.xz + self.zy * t.yz + self.zz * t.zz,
294 }
295 }
296
297 #[inline]
299 pub fn schur(self, t: Self) -> Self {
300 Self {
301 xx: self.xx * t.xx,
302 xy: self.xy * t.xy,
303 xz: self.xz * t.xz,
304 yx: self.yx * t.yx,
305 yy: self.yy * t.yy,
306 yz: self.yz * t.yz,
307 zx: self.zx * t.zx,
308 zy: self.zy * t.zy,
309 zz: self.zz * t.zz,
310 }
311 }
312
313 #[inline]
315 pub fn mat_vec(self, v: Vector3) -> Vector3 {
316 Vector3::new(
317 self.xx * v.x + self.xy * v.y + self.xz * v.z,
318 self.yx * v.x + self.yy * v.y + self.yz * v.z,
319 self.zx * v.x + self.zy * v.y + self.zz * v.z,
320 )
321 }
322
323 #[inline]
325 pub fn vec_mat(v: Vector3, t: Self) -> Vector3 {
326 Vector3::new(
327 v.x * t.xx + v.y * t.yx + v.z * t.zx,
328 v.x * t.xy + v.y * t.yy + v.z * t.zy,
329 v.x * t.xz + v.y * t.yz + v.z * t.zz,
330 )
331 }
332
333 #[inline]
335 pub fn double_inner(self, t: Self) -> f64 {
336 self.xx * t.xx
337 + self.xy * t.xy
338 + self.xz * t.xz
339 + self.yx * t.yx
340 + self.yy * t.yy
341 + self.yz * t.yz
342 + self.zx * t.zx
343 + self.zy * t.zy
344 + self.zz * t.zz
345 }
346
347 #[inline]
349 pub fn symm(self) -> SymmTensor {
350 SymmTensor {
351 xx: self.xx,
352 xy: 0.5 * (self.xy + self.yx),
353 xz: 0.5 * (self.xz + self.zx),
354 yy: self.yy,
355 yz: 0.5 * (self.yz + self.zy),
356 zz: self.zz,
357 }
358 }
359
360 #[inline]
362 pub fn two_symm(self) -> SymmTensor {
363 SymmTensor {
364 xx: 2.0 * self.xx,
365 xy: self.xy + self.yx,
366 xz: self.xz + self.zx,
367 yy: 2.0 * self.yy,
368 yz: self.yz + self.zy,
369 zz: 2.0 * self.zz,
370 }
371 }
372
373 #[inline]
375 pub fn skew(self) -> Self {
376 Self {
377 xx: 0.0,
378 xy: 0.5 * (self.xy - self.yx),
379 xz: 0.5 * (self.xz - self.zx),
380 yx: 0.5 * (self.yx - self.xy),
381 yy: 0.0,
382 yz: 0.5 * (self.yz - self.zy),
383 zx: 0.5 * (self.zx - self.xz),
384 zy: 0.5 * (self.zy - self.yz),
385 zz: 0.0,
386 }
387 }
388
389 #[inline]
391 pub fn dev(self) -> Self {
392 self - SphericalTensor::new(self.tr() / 3.0)
393 }
394
395 #[inline]
397 pub fn dev2(self) -> Self {
398 self - SphericalTensor::new(2.0 / 3.0 * self.tr())
399 }
400
401 #[inline]
403 pub fn dev_symm(self) -> SymmTensor {
404 let sph = self.tr() / 3.0;
405 SymmTensor {
406 xx: self.xx - sph,
407 xy: 0.5 * (self.xy + self.yx),
408 xz: 0.5 * (self.xz + self.zx),
409 yy: self.yy - sph,
410 yz: 0.5 * (self.yz + self.zy),
411 zz: self.zz - sph,
412 }
413 }
414
415 #[inline]
417 pub fn dev_two_symm(self) -> SymmTensor {
418 let sph = 2.0 / 3.0 * self.tr();
419 SymmTensor {
420 xx: 2.0 * self.xx - sph,
421 xy: self.xy + self.yx,
422 xz: self.xz + self.zx,
423 yy: 2.0 * self.yy - sph,
424 yz: self.yz + self.zy,
425 zz: 2.0 * self.zz - sph,
426 }
427 }
428
429 #[inline]
431 pub fn hodge_dual(self) -> Vector3 {
432 Vector3::new(self.yz, -self.xz, self.xy)
433 }
434
435 #[inline]
437 pub fn invariant_i(self) -> f64 {
438 self.tr()
439 }
440
441 #[inline]
443 pub fn invariant_ii(self) -> f64 {
444 self.xx * self.yy + self.yy * self.zz + self.xx * self.zz
445 - self.xy * self.yx
446 - self.yz * self.zy
447 - self.xz * self.zx
448 }
449
450 #[inline]
452 pub fn invariant_iii(self) -> f64 {
453 self.det()
454 }
455
456 pub fn is_identity(self, tol: f64) -> bool {
458 (self.xx - 1.0).abs() < tol
459 && (self.yy - 1.0).abs() < tol
460 && (self.zz - 1.0).abs() < tol
461 && self.xy.abs() < tol
462 && self.xz.abs() < tol
463 && self.yx.abs() < tol
464 && self.yz.abs() < tol
465 && self.zx.abs() < tol
466 && self.zy.abs() < tol
467 }
468
469 #[inline]
471 pub fn lerp(a: Self, b: Self, t: f64) -> Self {
472 let ot = 1.0 - t;
473 Self {
474 xx: ot * a.xx + t * b.xx,
475 xy: ot * a.xy + t * b.xy,
476 xz: ot * a.xz + t * b.xz,
477 yx: ot * a.yx + t * b.yx,
478 yy: ot * a.yy + t * b.yy,
479 yz: ot * a.yz + t * b.yz,
480 zx: ot * a.zx + t * b.zx,
481 zy: ot * a.zy + t * b.zy,
482 zz: ot * a.zz + t * b.zz,
483 }
484 }
485}
486
487impl From<SphericalTensor> for Tensor {
490 #[inline]
491 fn from(st: SphericalTensor) -> Self {
492 Self {
493 xx: st.ii,
494 xy: 0.0,
495 xz: 0.0,
496 yx: 0.0,
497 yy: st.ii,
498 yz: 0.0,
499 zx: 0.0,
500 zy: 0.0,
501 zz: st.ii,
502 }
503 }
504}
505
506impl From<SymmTensor> for Tensor {
507 #[inline]
508 fn from(st: SymmTensor) -> Self {
509 Self {
510 xx: st.xx,
511 xy: st.xy,
512 xz: st.xz,
513 yx: st.xy,
514 yy: st.yy,
515 yz: st.yz,
516 zx: st.xz,
517 zy: st.yz,
518 zz: st.zz,
519 }
520 }
521}
522
523pub fn hodge_dual_of_vec(v: Vector3) -> Tensor {
525 Tensor {
526 xx: 0.0,
527 xy: -v.z,
528 xz: v.y,
529 yx: v.z,
530 yy: 0.0,
531 yz: -v.x,
532 zx: -v.y,
533 zy: v.x,
534 zz: 0.0,
535 }
536}
537
538impl Neg for Tensor {
541 type Output = Self;
542 #[inline]
543 fn neg(self) -> Self {
544 Self {
545 xx: -self.xx,
546 xy: -self.xy,
547 xz: -self.xz,
548 yx: -self.yx,
549 yy: -self.yy,
550 yz: -self.yz,
551 zx: -self.zx,
552 zy: -self.zy,
553 zz: -self.zz,
554 }
555 }
556}
557
558impl Add for Tensor {
559 type Output = Self;
560 #[inline]
561 fn add(self, r: Self) -> Self {
562 Self {
563 xx: self.xx + r.xx,
564 xy: self.xy + r.xy,
565 xz: self.xz + r.xz,
566 yx: self.yx + r.yx,
567 yy: self.yy + r.yy,
568 yz: self.yz + r.yz,
569 zx: self.zx + r.zx,
570 zy: self.zy + r.zy,
571 zz: self.zz + r.zz,
572 }
573 }
574}
575
576impl Sub for Tensor {
577 type Output = Self;
578 #[inline]
579 fn sub(self, r: Self) -> Self {
580 Self {
581 xx: self.xx - r.xx,
582 xy: self.xy - r.xy,
583 xz: self.xz - r.xz,
584 yx: self.yx - r.yx,
585 yy: self.yy - r.yy,
586 yz: self.yz - r.yz,
587 zx: self.zx - r.zx,
588 zy: self.zy - r.zy,
589 zz: self.zz - r.zz,
590 }
591 }
592}
593
594impl Mul<f64> for Tensor {
595 type Output = Self;
596 #[inline]
597 fn mul(self, s: f64) -> Self {
598 Self {
599 xx: self.xx * s,
600 xy: self.xy * s,
601 xz: self.xz * s,
602 yx: self.yx * s,
603 yy: self.yy * s,
604 yz: self.yz * s,
605 zx: self.zx * s,
606 zy: self.zy * s,
607 zz: self.zz * s,
608 }
609 }
610}
611
612impl Mul<Tensor> for f64 {
613 type Output = Tensor;
614 #[inline]
615 fn mul(self, t: Tensor) -> Tensor {
616 t * self
617 }
618}
619
620impl Div<f64> for Tensor {
621 type Output = Self;
622 #[inline]
623 fn div(self, s: f64) -> Self {
624 Self {
625 xx: self.xx / s,
626 xy: self.xy / s,
627 xz: self.xz / s,
628 yx: self.yx / s,
629 yy: self.yy / s,
630 yz: self.yz / s,
631 zx: self.zx / s,
632 zy: self.zy / s,
633 zz: self.zz / s,
634 }
635 }
636}
637
638impl AddAssign for Tensor {
639 #[inline]
640 fn add_assign(&mut self, r: Self) {
641 *self = *self + r;
642 }
643}
644
645impl SubAssign for Tensor {
646 #[inline]
647 fn sub_assign(&mut self, r: Self) {
648 *self = *self - r;
649 }
650}
651
652impl MulAssign<f64> for Tensor {
653 #[inline]
654 fn mul_assign(&mut self, s: f64) {
655 *self = *self * s;
656 }
657}
658
659impl Add<SphericalTensor> for Tensor {
662 type Output = Self;
663 #[inline]
664 fn add(self, st: SphericalTensor) -> Self {
665 Self {
666 xx: self.xx + st.ii,
667 xy: self.xy,
668 xz: self.xz,
669 yx: self.yx,
670 yy: self.yy + st.ii,
671 yz: self.yz,
672 zx: self.zx,
673 zy: self.zy,
674 zz: self.zz + st.ii,
675 }
676 }
677}
678
679impl Add<Tensor> for SphericalTensor {
680 type Output = Tensor;
681 #[inline]
682 fn add(self, t: Tensor) -> Tensor {
683 t + self
684 }
685}
686
687impl Sub<SphericalTensor> for Tensor {
688 type Output = Self;
689 #[inline]
690 fn sub(self, st: SphericalTensor) -> Self {
691 Self {
692 xx: self.xx - st.ii,
693 xy: self.xy,
694 xz: self.xz,
695 yx: self.yx,
696 yy: self.yy - st.ii,
697 yz: self.yz,
698 zx: self.zx,
699 zy: self.zy,
700 zz: self.zz - st.ii,
701 }
702 }
703}
704
705impl Sub<Tensor> for SphericalTensor {
706 type Output = Tensor;
707 #[inline]
708 fn sub(self, t: Tensor) -> Tensor {
709 Tensor {
710 xx: self.ii - t.xx,
711 xy: -t.xy,
712 xz: -t.xz,
713 yx: -t.yx,
714 yy: self.ii - t.yy,
715 yz: -t.yz,
716 zx: -t.zx,
717 zy: -t.zy,
718 zz: self.ii - t.zz,
719 }
720 }
721}
722
723impl Add<SymmTensor> for Tensor {
726 type Output = Self;
727 #[inline]
728 fn add(self, st: SymmTensor) -> Self {
729 Self {
730 xx: self.xx + st.xx,
731 xy: self.xy + st.xy,
732 xz: self.xz + st.xz,
733 yx: self.yx + st.xy,
734 yy: self.yy + st.yy,
735 yz: self.yz + st.yz,
736 zx: self.zx + st.xz,
737 zy: self.zy + st.yz,
738 zz: self.zz + st.zz,
739 }
740 }
741}
742
743impl Add<Tensor> for SymmTensor {
744 type Output = Tensor;
745 #[inline]
746 fn add(self, t: Tensor) -> Tensor {
747 t + self
748 }
749}
750
751impl Sub<SymmTensor> for Tensor {
752 type Output = Self;
753 #[inline]
754 fn sub(self, st: SymmTensor) -> Self {
755 Self {
756 xx: self.xx - st.xx,
757 xy: self.xy - st.xy,
758 xz: self.xz - st.xz,
759 yx: self.yx - st.xy,
760 yy: self.yy - st.yy,
761 yz: self.yz - st.yz,
762 zx: self.zx - st.xz,
763 zy: self.zy - st.yz,
764 zz: self.zz - st.zz,
765 }
766 }
767}
768
769impl Sub<Tensor> for SymmTensor {
770 type Output = Tensor;
771 #[inline]
772 fn sub(self, t: Tensor) -> Tensor {
773 Tensor {
774 xx: self.xx - t.xx,
775 xy: self.xy - t.xy,
776 xz: self.xz - t.xz,
777 yx: self.xy - t.yx,
778 yy: self.yy - t.yy,
779 yz: self.yz - t.yz,
780 zx: self.xz - t.zx,
781 zy: self.yz - t.zy,
782 zz: self.zz - t.zz,
783 }
784 }
785}
786
787impl Mul<Tensor> for SphericalTensor {
789 type Output = Tensor;
790 #[inline]
791 fn mul(self, t: Tensor) -> Tensor {
792 t * self.ii
793 }
794}
795
796impl Mul<SphericalTensor> for Tensor {
797 type Output = Self;
798 #[inline]
799 fn mul(self, st: SphericalTensor) -> Self {
800 self * st.ii
801 }
802}
803
804impl Mul<Tensor> for SymmTensor {
806 type Output = Tensor;
807 #[inline]
808 fn mul(self, t: Tensor) -> Tensor {
809 Tensor {
810 xx: self.xx * t.xx + self.xy * t.yx + self.xz * t.zx,
811 xy: self.xx * t.xy + self.xy * t.yy + self.xz * t.zy,
812 xz: self.xx * t.xz + self.xy * t.yz + self.xz * t.zz,
813 yx: self.xy * t.xx + self.yy * t.yx + self.yz * t.zx,
814 yy: self.xy * t.xy + self.yy * t.yy + self.yz * t.zy,
815 yz: self.xy * t.xz + self.yy * t.yz + self.yz * t.zz,
816 zx: self.xz * t.xx + self.yz * t.yx + self.zz * t.zx,
817 zy: self.xz * t.xy + self.yz * t.yy + self.zz * t.zy,
818 zz: self.xz * t.xz + self.yz * t.yz + self.zz * t.zz,
819 }
820 }
821}
822
823impl Mul<SymmTensor> for Tensor {
824 type Output = Self;
825 #[inline]
826 fn mul(self, st: SymmTensor) -> Self {
827 Self {
828 xx: self.xx * st.xx + self.xy * st.xy + self.xz * st.xz,
829 xy: self.xx * st.xy + self.xy * st.yy + self.xz * st.yz,
830 xz: self.xx * st.xz + self.xy * st.yz + self.xz * st.zz,
831 yx: self.yx * st.xx + self.yy * st.xy + self.yz * st.xz,
832 yy: self.yx * st.xy + self.yy * st.yy + self.yz * st.yz,
833 yz: self.yx * st.xz + self.yy * st.yz + self.yz * st.zz,
834 zx: self.zx * st.xx + self.zy * st.xy + self.zz * st.xz,
835 zy: self.zx * st.xy + self.zy * st.yy + self.zz * st.yz,
836 zz: self.zx * st.xz + self.zy * st.yz + self.zz * st.zz,
837 }
838 }
839}
840
841impl Mul<SymmTensor> for SymmTensor {
843 type Output = Tensor;
844 #[inline]
845 fn mul(self, st: SymmTensor) -> Tensor {
846 Tensor {
847 xx: self.xx * st.xx + self.xy * st.xy + self.xz * st.xz,
848 xy: self.xx * st.xy + self.xy * st.yy + self.xz * st.yz,
849 xz: self.xx * st.xz + self.xy * st.yz + self.xz * st.zz,
850 yx: self.xy * st.xx + self.yy * st.xy + self.yz * st.xz,
851 yy: self.xy * st.xy + self.yy * st.yy + self.yz * st.yz,
852 yz: self.xy * st.xz + self.yy * st.yz + self.yz * st.zz,
853 zx: self.xz * st.xx + self.yz * st.xy + self.zz * st.xz,
854 zy: self.xz * st.xy + self.yz * st.yy + self.zz * st.yz,
855 zz: self.xz * st.xz + self.yz * st.yz + self.zz * st.zz,
856 }
857 }
858}
859
860impl Mul<Vector3> for Vector3 {
862 type Output = Tensor;
863 #[inline]
864 fn mul(self, v: Vector3) -> Tensor {
865 Tensor {
866 xx: self.x * v.x,
867 xy: self.x * v.y,
868 xz: self.x * v.z,
869 yx: self.y * v.x,
870 yy: self.y * v.y,
871 yz: self.y * v.z,
872 zx: self.z * v.x,
873 zy: self.z * v.y,
874 zz: self.z * v.z,
875 }
876 }
877}
878
879#[inline]
883pub fn tr(t: Tensor) -> f64 {
884 t.tr()
885}
886
887#[inline]
889pub fn det(t: Tensor) -> f64 {
890 t.det()
891}
892
893#[inline]
895pub fn inv(t: Tensor) -> Tensor {
896 t.inv()
897}
898
899#[inline]
901pub fn symm(t: Tensor) -> SymmTensor {
902 t.symm()
903}
904
905#[inline]
907pub fn two_symm(t: Tensor) -> SymmTensor {
908 t.two_symm()
909}
910
911#[inline]
913pub fn skew(t: Tensor) -> Tensor {
914 t.skew()
915}
916
917#[inline]
919pub fn dev(t: Tensor) -> Tensor {
920 t.dev()
921}
922
923#[inline]
925pub fn dev2(t: Tensor) -> Tensor {
926 t.dev2()
927}
928
929#[inline]
931pub fn dev_symm(t: Tensor) -> SymmTensor {
932 t.dev_symm()
933}
934
935#[inline]
937pub fn dev_two_symm(t: Tensor) -> SymmTensor {
938 t.dev_two_symm()
939}
940
941#[inline]
943pub fn lerp(a: Tensor, b: Tensor, t: f64) -> Tensor {
944 Tensor::lerp(a, b, t)
945}
946
947#[inline]
949pub fn outer(v: Vector3, w: Vector3) -> Tensor {
950 v * w
951}
952
953#[cfg(test)]
954mod tests {
955 use super::*;
956
957 #[test]
958 fn identity_properties() {
959 let i = Tensor::IDENTITY;
960 assert_eq!(i.tr(), 3.0);
961 assert_eq!(i.det(), 1.0);
962 let inv = i.inv();
963 assert!((inv.xx - 1.0).abs() < 1e-14);
964 }
965
966 #[test]
967 fn mat_mul_identity() {
968 let i = Tensor::IDENTITY;
969 let t = Tensor::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
970 assert_eq!(i.mat_mul(t), t);
971 assert_eq!(t.mat_mul(i), t);
972 }
973
974 #[test]
975 fn mat_vec_identity() {
976 let v = Vector3::new(1.0, 2.0, 3.0);
977 assert_eq!(Tensor::IDENTITY.mat_vec(v), v);
978 }
979
980 #[test]
981 fn transpose() {
982 let t = Tensor::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
983 let tt = t.transpose();
984 assert_eq!(tt.xy, t.yx);
985 assert_eq!(tt.yx, t.xy);
986 assert_eq!(tt.xz, t.zx);
987 }
988
989 #[test]
990 fn symm_and_skew_sum_to_original() {
991 let t = Tensor::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
992 let s: Tensor = Tensor::from(t.symm());
993 let sk = t.skew();
994 let sum = s + sk;
995 assert!((sum.xx - t.xx).abs() < 1e-14);
996 assert!((sum.xy - t.xy).abs() < 1e-14);
997 assert!((sum.yz - t.yz).abs() < 1e-14);
998 }
999
1000 #[test]
1001 fn outer_product() {
1002 let x = Vector3::X;
1003 let y = Vector3::Y;
1004 let xy = x * y;
1005 assert_eq!(xy.xx, 0.0);
1006 assert_eq!(xy.xy, 1.0);
1007 assert_eq!(xy.yx, 0.0);
1008 }
1009
1010 #[test]
1011 fn dev_traceless() {
1012 let t = Tensor::new(4.0, 1.0, 0.0, 1.0, 2.0, 0.0, 0.0, 0.0, 3.0);
1013 assert!((t.dev().tr()).abs() < 1e-14);
1014 }
1015
1016 #[test]
1017 fn inv_roundtrip_is_identity() {
1018 let t = Tensor::new(2.0, 1.0, 0.0, 1.0, 3.0, 1.0, 0.0, 1.0, 2.0);
1020 assert!(
1021 t.mat_mul(t.inv()).is_identity(1e-12),
1022 "T · T⁻¹ is not identity"
1023 );
1024 }
1025
1026 #[test]
1027 fn double_inner_is_symmetric() {
1028 let t1 = Tensor::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
1030 let t2 = Tensor::new(2.0, 0.0, 1.0, -1.0, 3.0, 0.0, 2.0, 1.0, 4.0);
1031 let diff = (t1.double_inner(t2) - t2.double_inner(t1)).abs();
1032 assert!(diff < 1e-12, "double_inner asymmetry = {diff:.3e}");
1033 }
1034
1035 #[test]
1036 fn dev2_is_two_thirds_trace_removed() {
1037 let t = Tensor::new(6.0, 1.0, 0.0, 1.0, 3.0, 0.0, 0.0, 0.0, 3.0);
1039 let tr = t.tr(); let d = t.dev2();
1041 assert!((d.xx - (t.xx - 2.0 / 3.0 * tr)).abs() < 1e-14);
1042 assert!((d.yy - (t.yy - 2.0 / 3.0 * tr)).abs() < 1e-14);
1043 assert!((d.zz - (t.zz - 2.0 / 3.0 * tr)).abs() < 1e-14);
1044 assert!((d.xy - t.xy).abs() < 1e-14);
1045 assert!(
1047 (d.tr() - (-tr)).abs() < 1e-14,
1048 "tr(dev2) = {:.3e}, expected {:.3e}",
1049 d.tr(),
1050 -tr
1051 );
1052 }
1053}