From 35aec17737803e0943b1c4ea84b9ed3fe8369f2a Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Fri, 10 May 2024 16:38:01 +1000 Subject: [PATCH 01/44] Impl vec_outer_update --- russell_lab/src/matvec/mod.rs | 2 + russell_lab/src/matvec/vec_outer_update.rs | 151 +++++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 russell_lab/src/matvec/vec_outer_update.rs diff --git a/russell_lab/src/matvec/mod.rs b/russell_lab/src/matvec/mod.rs index abfdce3b..296322c7 100644 --- a/russell_lab/src/matvec/mod.rs +++ b/russell_lab/src/matvec/mod.rs @@ -10,6 +10,7 @@ mod mat_vec_mul_update; mod solve_lin_sys; mod vec_mat_mul; mod vec_outer; +mod vec_outer_update; pub use crate::matvec::complex_mat_vec_mul::*; pub use crate::matvec::complex_solve_lin_sys::*; pub use crate::matvec::complex_vec_mat_mul::*; @@ -20,3 +21,4 @@ pub use crate::matvec::mat_vec_mul_update::*; pub use crate::matvec::solve_lin_sys::*; pub use crate::matvec::vec_mat_mul::*; pub use crate::matvec::vec_outer::*; +pub use crate::matvec::vec_outer_update::*; diff --git a/russell_lab/src/matvec/vec_outer_update.rs b/russell_lab/src/matvec/vec_outer_update.rs new file mode 100644 index 00000000..9e63a264 --- /dev/null +++ b/russell_lab/src/matvec/vec_outer_update.rs @@ -0,0 +1,151 @@ +use crate::matrix::Matrix; +use crate::vector::Vector; +use crate::{to_i32, StrError, CBLAS_COL_MAJOR}; + +extern "C" { + // Performs the rank 1 operation (tensor product) + // + fn cblas_dger( + layout: i32, + m: i32, + n: i32, + alpha: f64, + x: *const f64, + incx: i32, + y: *const f64, + incy: i32, + a: *mut f64, + lda: i32, + ); +} + +/// (dger) Performs the outer (tensor) product between two vectors resulting in a matrix (with update) +/// +/// ```text +/// a += α ⋅ u outer v +/// (m,n) (m) (n) +/// ``` +/// +/// See also: +/// +/// # Note +/// +/// The rows of matrix a must equal the length of vector u and +/// the columns of matrix a must equal the length of vector v +/// +/// # Examples +/// +/// ``` +/// use russell_lab::{vec_outer_update, Matrix, Vector, StrError}; +/// +/// fn main() -> Result<(), StrError> { +/// let u = Vector::from(&[1.0, 2.0, 3.0]); +/// let v = Vector::from(&[5.0, -2.0, 0.0, 1.0]); +/// let mut a = Matrix::from(&[ +/// [1000.0, 0.0, 10.0, 100.0], +/// [2000.0, 0.0, 20.0, 200.0], +/// [3000.0, 0.0, 30.0, 300.0], +/// ]); +/// vec_outer_update(&mut a, 1.0, &u, &v)?; +/// let correct = "┌ ┐\n\ +/// │ 1005 -2 10 101 │\n\ +/// │ 2010 -4 20 202 │\n\ +/// │ 3015 -6 30 303 │\n\ +/// └ ┘"; +/// assert_eq!(format!("{}", a), correct); +/// Ok(()) +/// } +/// ``` +pub fn vec_outer_update(a: &mut Matrix, alpha: f64, u: &Vector, v: &Vector) -> Result<(), StrError> { + let m = u.dim(); + let n = v.dim(); + if a.nrow() != m || a.ncol() != n { + return Err("matrix and vectors are incompatible"); + } + let m_i32: i32 = to_i32(m); + let n_i32: i32 = to_i32(n); + let lda = m_i32; + unsafe { + cblas_dger( + CBLAS_COL_MAJOR, + m_i32, + n_i32, + alpha, + u.as_data().as_ptr(), + 1, + v.as_data().as_ptr(), + 1, + a.as_mut_data().as_mut_ptr(), + lda, + ) + } + Ok(()) +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#[cfg(test)] +mod tests { + use super::{vec_outer_update, Matrix, Vector}; + use crate::mat_approx_eq; + + #[test] + fn mat_vec_mul_fail_on_wrong_dims() { + let u = Vector::new(2); + let v = Vector::new(3); + let mut a_1x3 = Matrix::new(1, 3); + let mut a_2x1 = Matrix::new(2, 1); + assert_eq!( + vec_outer_update(&mut a_1x3, 1.0, &u, &v), + Err("matrix and vectors are incompatible") + ); + assert_eq!( + vec_outer_update(&mut a_2x1, 1.0, &u, &v), + Err("matrix and vectors are incompatible") + ); + } + + #[test] + fn vec_outer_update_works() { + let u = Vector::from(&[1.0, 2.0, 3.0]); + let v = Vector::from(&[5.0, -2.0, 0.0, 1.0]); + let (m, n) = (u.dim(), v.dim()); + let mut a = Matrix::new(m, n); + vec_outer_update(&mut a, 3.0, &u, &v).unwrap(); + #[rustfmt::skip] + let correct = &[ + [15.0, -6.0, 0.0, 3.0], + [30.0, -12.0, 0.0, 6.0], + [45.0, -18.0, 0.0, 9.0], + ]; + mat_approx_eq(&a, correct, 1e-15); + } + + #[test] + fn vec_outer_update_works_1() { + let u = Vector::from(&[1.0, 2.0, 3.0, 4.0]); + let v = Vector::from(&[1.0, 1.0, -2.0]); + let (m, n) = (u.dim(), v.dim()); + let mut a = Matrix::new(m, n); + vec_outer_update(&mut a, 2.0, &u, &v).unwrap(); + #[rustfmt::skip] + let correct = &[ + [2.0, 2.0, -4.0], + [4.0, 4.0, -8.0], + [6.0, 6.0, -12.0], + [8.0, 8.0, -16.0], + ]; + mat_approx_eq(&a, correct, 1e-15); + + // call it again to make sure that summation is happening + #[rustfmt::skip] + let correct2 = &[ + [ 4.0, 4.0, -8.0], + [ 8.0, 8.0, -16.0], + [12.0, 12.0, -24.0], + [16.0, 16.0, -32.0], + ]; + vec_outer_update(&mut a, 2.0, &u, &v).unwrap(); + mat_approx_eq(&a, correct2, 1e-15); + } +} From ec438d0b77473b1414610da2342b135c26cda63c Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Fri, 10 May 2024 17:40:02 +1000 Subject: [PATCH 02/44] Fix doc comment --- russell_tensor/src/as_matrix_3x3.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/russell_tensor/src/as_matrix_3x3.rs b/russell_tensor/src/as_matrix_3x3.rs index b260756b..b4e1f59d 100644 --- a/russell_tensor/src/as_matrix_3x3.rs +++ b/russell_tensor/src/as_matrix_3x3.rs @@ -1,6 +1,6 @@ use russell_lab::Matrix; -/// Defines a trait to handle 2D arrays +/// Defines a trait to handle 3x3 matrices /// /// # Examples /// @@ -97,9 +97,8 @@ impl AsMatrix3x3 for Matrix { #[cfg(test)] mod tests { - use russell_lab::Matrix; - use super::AsMatrix3x3; + use russell_lab::Matrix; fn flatten(mat: &dyn AsMatrix3x3) -> Vec { let mut res = vec![0.0; 9]; From fe6374dae38eca038340696b4dbdf12993ae406e Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Fri, 10 May 2024 17:40:37 +1000 Subject: [PATCH 03/44] Impl AsMatrix9x9 trait --- russell_tensor/src/as_matrix_9x9.rs | 176 ++++++++++++++++++++++++++++ russell_tensor/src/lib.rs | 2 + russell_tensor/src/tensor4.rs | 35 +++--- 3 files changed, 197 insertions(+), 16 deletions(-) create mode 100644 russell_tensor/src/as_matrix_9x9.rs diff --git a/russell_tensor/src/as_matrix_9x9.rs b/russell_tensor/src/as_matrix_9x9.rs new file mode 100644 index 00000000..a3cc0376 --- /dev/null +++ b/russell_tensor/src/as_matrix_9x9.rs @@ -0,0 +1,176 @@ +use russell_lab::Matrix; + +/// Defines a trait to handle 9x9 matrices +/// +/// # Examples +/// +/// ``` +/// use russell_lab::Matrix; +/// use russell_tensor::{AsMatrix9x9, MN_TO_IJKL}; +/// +/// fn diagonal(mat: &dyn AsMatrix9x9) -> Vec { +/// let mut res = vec![0.0; 9]; +/// for i in 0..9 { +/// res[i] = mat.at(i, i); +/// } +/// res +/// } +/// +/// // heap-allocated matrix (vector of vectors) +/// // ┌ ┐ +/// // │ 1111 1122 1133 1112 1123 1113 1121 1132 1131 │ +/// // │ 2211 2222 2233 2212 2223 2213 2221 2232 2231 │ +/// // │ 3311 3322 3333 3312 3323 3313 3321 3332 3331 │ +/// // │ 1211 1222 1233 1212 1223 1213 1221 1232 1231 │ +/// // │ 2311 2322 2333 2312 2323 2313 2321 2332 2331 │ +/// // │ 1311 1322 1333 1312 1323 1313 1321 1332 1331 │ +/// // │ 2111 2122 2133 2112 2123 2113 2121 2132 2131 │ +/// // │ 3211 3222 3233 3212 3223 3213 3221 3232 3231 │ +/// // │ 3111 3122 3133 3112 3123 3113 3121 3132 3131 │ +/// // └ ┘ +/// let mut mat = vec![vec![0.0; 9]; 9]; +/// for m in 0..9 { +/// for n in 0..9 { +/// let (i, j, k, l) = MN_TO_IJKL[m][n]; +/// mat[m][n] = (1000 * (i + 1) + 100 * (j + 1) + 10 * (k + 1) + (l + 1)) as f64; +/// } +/// } +/// assert_eq!( +/// diagonal(&mat), +/// &[1111.0, 2222.0, 3333.0, 1212.0, 2323.0, 1313.0, 2121.0, 3232.0, 3131.0] +/// ); +/// ``` +pub trait AsMatrix9x9 { + /// Returns the value at (i,j) indices + /// + /// # Panics + /// + /// This function panics if the indices are out of range. + fn at(&self, i: usize, j: usize) -> f64; +} + +/// Defines a heap-allocated 9x9 matrix (vector of vectors) +/// +/// # Panics +/// +/// * The array must be 9x9; otherwise a panic will occur. +/// * The methods may panic if the array is empty. +impl AsMatrix9x9 for Vec> { + fn at(&self, i: usize, j: usize) -> f64 { + self[i][j] + } +} + +/// Defines a heap-allocated 9x9 matrix (slice of slices) +/// +/// # Panics +/// +/// * The array must be 9x9; otherwise a panic will occur. +/// * The methods may panic if the array is empty. +impl AsMatrix9x9 for &[&[f64]] { + fn at(&self, i: usize, j: usize) -> f64 { + self[i][j] + } +} + +/// Defines a stack-allocated (fixed-size) 9x9 matrix +/// +/// # Panics +/// +/// * The array must be 9x9; otherwise a panic will occur. +/// * The methods may panic if the array is empty. +impl AsMatrix9x9 for [[f64; 9]; 9] { + fn at(&self, i: usize, j: usize) -> f64 { + self[i][j] + } +} + +/// Defines a 9x9 matrix from russell_lab::Matrix +/// +/// # Panics +/// +/// * The matrix must be 9x9; otherwise a panic will occur. +/// * The methods may panic if the array is empty. +impl AsMatrix9x9 for Matrix { + fn at(&self, i: usize, j: usize) -> f64 { + self.get(i, j) + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#[cfg(test)] +mod tests { + use super::AsMatrix9x9; + use crate::MN_TO_IJKL; + use russell_lab::Matrix; + + fn diagonal(mat: &dyn AsMatrix9x9) -> Vec { + let mut res = vec![0.0; 9]; + for i in 0..9 { + res[i] = mat.at(i, i); + } + res + } + + #[test] + fn as_matrix_9x9_works() { + // heap-allocated matrix (vector of vectors) + // ┌ ┐ + // │ 1111 1122 1133 1112 1123 1113 1121 1132 1131 │ + // │ 2211 2222 2233 2212 2223 2213 2221 2232 2231 │ + // │ 3311 3322 3333 3312 3323 3313 3321 3332 3331 │ + // │ 1211 1222 1233 1212 1223 1213 1221 1232 1231 │ + // │ 2311 2322 2333 2312 2323 2313 2321 2332 2331 │ + // │ 1311 1322 1333 1312 1323 1313 1321 1332 1331 │ + // │ 2111 2122 2133 2112 2123 2113 2121 2132 2131 │ + // │ 3211 3222 3233 3212 3223 3213 3221 3232 3231 │ + // │ 3111 3122 3133 3112 3123 3113 3121 3132 3131 │ + // └ ┘ + let mut mat = vec![vec![0.0; 9]; 9]; + for m in 0..9 { + for n in 0..9 { + let (i, j, k, l) = MN_TO_IJKL[m][n]; + mat[m][n] = (1000 * (i + 1) + 100 * (j + 1) + 10 * (k + 1) + (l + 1)) as f64; + } + } + assert_eq!( + diagonal(&mat), + &[1111.0, 2222.0, 3333.0, 1212.0, 2323.0, 1313.0, 2121.0, 3232.0, 3131.0] + ); + + // heap-allocated 2D array (aka slice of slices) + let ___ = 0.0; + let mat: &[&[f64]] = &[ + &[1.0, ___, ___, ___, ___, ___, ___, ___, ___], + &[___, 2.0, ___, ___, ___, ___, ___, ___, ___], + &[___, ___, 3.0, ___, ___, ___, ___, ___, ___], + &[___, ___, ___, 4.0, ___, ___, ___, ___, ___], + &[___, ___, ___, ___, 5.0, ___, ___, ___, ___], + &[___, ___, ___, ___, ___, 6.0, ___, ___, ___], + &[___, ___, ___, ___, ___, ___, 7.0, ___, ___], + &[___, ___, ___, ___, ___, ___, ___, 8.0, ___], + &[___, ___, ___, ___, ___, ___, ___, ___, 9.0], + ]; + assert_eq!(diagonal(&mat), &[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]); + + // stack-allocated (fixed-size) 2D array + let mat = [ + [1.0, ___, ___, ___, ___, ___, ___, ___, ___], + [___, 2.0, ___, ___, ___, ___, ___, ___, ___], + [___, ___, 3.0, ___, ___, ___, ___, ___, ___], + [___, ___, ___, 4.0, ___, ___, ___, ___, ___], + [___, ___, ___, ___, 5.0, ___, ___, ___, ___], + [___, ___, ___, ___, ___, 6.0, ___, ___, ___], + [___, ___, ___, ___, ___, ___, 7.0, ___, ___], + [___, ___, ___, ___, ___, ___, ___, 8.0, ___], + [___, ___, ___, ___, ___, ___, ___, ___, 9.0], + ]; + assert_eq!(diagonal(&mat), &[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]); + + // russell_lab::Matrix + let diag = [10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0]; + let mat = Matrix::diagonal(&diag); + assert_eq!(diagonal(&mat), &diag); + } +} diff --git a/russell_tensor/src/lib.rs b/russell_tensor/src/lib.rs index a983196c..e252da3f 100644 --- a/russell_tensor/src/lib.rs +++ b/russell_tensor/src/lib.rs @@ -12,6 +12,7 @@ pub type StrError = &'static str; mod as_matrix_3x3; +mod as_matrix_9x9; mod constants; mod derivatives_t2; mod derivatives_t4; @@ -24,6 +25,7 @@ mod spectral2; mod tensor2; mod tensor4; pub use crate::as_matrix_3x3::*; +pub use crate::as_matrix_9x9::*; pub use crate::constants::*; pub use crate::derivatives_t2::*; pub use crate::derivatives_t4::*; diff --git a/russell_tensor/src/tensor4.rs b/russell_tensor/src/tensor4.rs index 582c4c7f..35498794 100644 --- a/russell_tensor/src/tensor4.rs +++ b/russell_tensor/src/tensor4.rs @@ -1,5 +1,5 @@ use super::{IJKL_TO_MN, IJKL_TO_MN_SYM, MN_TO_IJKL, SQRT_2}; -use crate::{Mandel, StrError, ONE_BY_3, TWO_BY_3}; +use crate::{AsMatrix9x9, Mandel, StrError, ONE_BY_3, TWO_BY_3}; use russell_lab::{mat_copy, Matrix}; use serde::{Deserialize, Serialize}; @@ -353,7 +353,7 @@ impl Tensor4 { /// Ok(()) /// } /// ``` - pub fn from_matrix(inp: &[[f64; 9]; 9], mandel: Mandel) -> Result { + pub fn from_matrix(inp: &dyn AsMatrix9x9, mandel: Mandel) -> Result { let dim = mandel.dim(); let mut mat = Matrix::new(dim, dim); if dim == 4 || dim == 6 { @@ -368,21 +368,24 @@ impl Tensor4 { let (u, v) = IJKL_TO_MN[j][i][l][k]; // check minor-symmetry if i > j || k > l { - if inp[m][n] != inp[p][q] || inp[m][n] != inp[r][s] || inp[m][n] != inp[u][v] { + if inp.at(m, n) != inp.at(p, q) + || inp.at(m, n) != inp.at(r, s) + || inp.at(m, n) != inp.at(u, v) + { return Err("minor-symmetric Tensor4 does not pass symmetry check"); } } else { if m > max || n > max { - if inp[m][n] != 0.0 { + if inp.at(m, n) != 0.0 { return Err("cannot define 2D Tensor4 due to non-zero values"); } continue; } else if m < 3 && n < 3 { - mat.set(m, n, inp[m][n]); + mat.set(m, n, inp.at(m, n)); } else if m > 2 && n > 2 { - mat.set(m, n, 2.0 * inp[m][n]); + mat.set(m, n, 2.0 * inp.at(m, n)); } else { - mat.set(m, n, SQRT_2 * inp[m][n]); + mat.set(m, n, SQRT_2 * inp.at(m, n)); } } } @@ -398,49 +401,49 @@ impl Tensor4 { // ** i == j ** // 1 if i == j && k == l { - mat.set(m, n, inp[m][n]); + mat.set(m, n, inp.at(m, n)); // 2 } else if i == j && k < l { let (p, q) = IJKL_TO_MN[i][j][l][k]; - mat.set(m, n, (inp[m][n] + inp[p][q]) / SQRT_2); + mat.set(m, n, (inp.at(m, n) + inp.at(p, q)) / SQRT_2); // 3 } else if i == j && k > l { let (p, q) = IJKL_TO_MN[i][j][l][k]; - mat.set(m, n, (inp[p][q] - inp[m][n]) / SQRT_2); + mat.set(m, n, (inp.at(p, q) - inp.at(m, n)) / SQRT_2); // ** i < j ** // 4 } else if i < j && k == l { let (r, s) = IJKL_TO_MN[j][i][k][l]; - mat.set(m, n, (inp[m][n] + inp[r][s]) / SQRT_2); + mat.set(m, n, (inp.at(m, n) + inp.at(r, s)) / SQRT_2); // 5 } else if i < j && k < l { let (p, q) = IJKL_TO_MN[i][j][l][k]; let (r, s) = IJKL_TO_MN[j][i][k][l]; let (u, v) = IJKL_TO_MN[j][i][l][k]; - mat.set(m, n, (inp[m][n] + inp[p][q] + inp[r][s] + inp[u][v]) / 2.0); + mat.set(m, n, (inp.at(m, n) + inp.at(p, q) + inp.at(r, s) + inp.at(u, v)) / 2.0); // 6 } else if i < j && k > l { let (p, q) = IJKL_TO_MN[i][j][l][k]; let (r, s) = IJKL_TO_MN[j][i][k][l]; let (u, v) = IJKL_TO_MN[j][i][l][k]; - mat.set(m, n, (inp[p][q] - inp[m][n] + inp[u][v] - inp[r][s]) / 2.0); + mat.set(m, n, (inp.at(p, q) - inp.at(m, n) + inp.at(u, v) - inp.at(r, s)) / 2.0); // ** i > j ** // 7 } else if i > j && k == l { let (r, s) = IJKL_TO_MN[j][i][k][l]; - mat.set(m, n, (inp[r][s] - inp[m][n]) / SQRT_2); + mat.set(m, n, (inp.at(r, s) - inp.at(m, n)) / SQRT_2); // 8 } else if i > j && k < l { let (p, q) = IJKL_TO_MN[i][j][l][k]; let (r, s) = IJKL_TO_MN[j][i][k][l]; let (u, v) = IJKL_TO_MN[j][i][l][k]; - mat.set(m, n, (inp[r][s] + inp[u][v] - inp[m][n] - inp[p][q]) / 2.0); + mat.set(m, n, (inp.at(r, s) + inp.at(u, v) - inp.at(m, n) - inp.at(p, q)) / 2.0); // 9 } else if i > j && k > l { let (p, q) = IJKL_TO_MN[i][j][l][k]; let (r, s) = IJKL_TO_MN[j][i][k][l]; let (u, v) = IJKL_TO_MN[j][i][l][k]; - mat.set(m, n, (inp[u][v] - inp[r][s] - inp[p][q] + inp[m][n]) / 2.0); + mat.set(m, n, (inp.at(u, v) - inp.at(r, s) - inp.at(p, q) + inp.at(m, n)) / 2.0); } } } From d0ff14484a46b270144ec03f4789b1b214ccd51f Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Fri, 10 May 2024 19:19:33 +1000 Subject: [PATCH 04/44] Impl t2_dyad_t2_update --- russell_tensor/src/operations.rs | 99 +++++++++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 1 deletion(-) diff --git a/russell_tensor/src/operations.rs b/russell_tensor/src/operations.rs index 6bffd017..b1d36775 100644 --- a/russell_tensor/src/operations.rs +++ b/russell_tensor/src/operations.rs @@ -1,6 +1,8 @@ use super::{Tensor2, Tensor4}; use crate::{StrError, SQRT_2}; -use russell_lab::{mat_mat_mul, mat_vec_mul, mat_vec_mul_update, vec_inner, vec_mat_mul, vec_outer, Vector}; +use russell_lab::{ + mat_mat_mul, mat_vec_mul, mat_vec_mul_update, vec_inner, vec_mat_mul, vec_outer, vec_outer_update, Vector, +}; /// Performs the double-dot (ddot) operation between two Tensor2 (inner product) /// @@ -362,6 +364,68 @@ pub fn t2_dyad_t2(dd: &mut Tensor4, alpha: f64, a: &Tensor2, b: &Tensor2) -> Res vec_outer(&mut dd.mat, alpha, &a.vec, &b.vec) } +/// Performs the dyadic product between two Tensor2 resulting in a Tensor4 (with update) +/// +/// ```text +/// D += α a ⊗ b +/// ``` +/// +/// ```text +/// With orthonormal Cartesian components: +/// +/// Dᵢⱼₖₗ += α aᵢⱼ bₖₗ +/// ``` +/// +/// Note: this function does NOT work with mixed symmetry types. +/// +/// # Examples +/// +/// ``` +/// use russell_lab::Matrix; +/// use russell_tensor::{t2_dyad_t2_update, Mandel, StrError, Tensor2, Tensor4}; +/// +/// fn main() -> Result<(), StrError> { +/// #[rustfmt::skip] +/// let a = Tensor2::from_matrix(&[ +/// [ 1.0, 10.0, 0.0], +/// [ 2.0, 1.0, 0.0], +/// [ 0.0, 0.0, 2.0], +/// ], Mandel::General)?; +/// +/// #[rustfmt::skip] +/// let b = Tensor2::from_matrix(&[ +/// [1.0, 4.0, 6.0], +/// [7.0, 2.0, 5.0], +/// [9.0, 8.0, 3.0], +/// ], Mandel::General)?; +/// +/// let mat = Matrix::filled(9, 9, 0.5); +/// let mut dd = Tensor4::from_matrix(&mat, Mandel::General)?; +/// t2_dyad_t2_update(&mut dd, 1.0, &a, &b)?; +/// +/// println!("{:.1}", dd.to_matrix()); +/// assert_eq!( +/// format!("{:.1}", dd.to_matrix()), +/// "┌ ┐\n\ +/// │ 1.5 2.5 3.5 4.5 5.5 6.5 7.5 8.5 9.5 │\n\ +/// │ 1.5 2.5 3.5 4.5 5.5 6.5 7.5 8.5 9.5 │\n\ +/// │ 2.5 4.5 6.5 8.5 10.5 12.5 14.5 16.5 18.5 │\n\ +/// │ 10.5 20.5 30.5 40.5 50.5 60.5 70.5 80.5 90.5 │\n\ +/// │ 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 │\n\ +/// │ 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 │\n\ +/// │ 2.5 4.5 6.5 8.5 10.5 12.5 14.5 16.5 18.5 │\n\ +/// │ 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 │\n\ +/// │ 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 │\n\ +/// └ ┘" +/// ); +/// Ok(()) +/// } +/// ``` +#[inline] +pub fn t2_dyad_t2_update(dd: &mut Tensor4, alpha: f64, a: &Tensor2, b: &Tensor2) -> Result<(), StrError> { + vec_outer_update(&mut dd.mat, alpha, &a.vec, &b.vec) +} + /// Performs the overbar dyadic product between two Tensor2 resulting in a (general) Tensor4 /// /// ```text @@ -1976,6 +2040,39 @@ mod tests { ); } + #[test] + fn t2_dyad_t2_update_works() { + // general dyad general + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 2.0, 3.0], + [4.0, 5.0, 6.0], + [7.0, 8.0, 9.0], + ], Mandel::General).unwrap(); + #[rustfmt::skip] + let b = Tensor2::from_matrix(&[ + [0.5, 0.5, 0.5], + [0.5, 0.5, 0.5], + [0.5, 0.5, 0.5], + ], Mandel::General).unwrap(); + let mat = Matrix::filled(9, 9, 0.1); + let mut dd = Tensor4::from_matrix(&mat, Mandel::General).unwrap(); + t2_dyad_t2_update(&mut dd, 2.0, &a, &b).unwrap(); + let mat = dd.to_matrix(); + let correct = "┌ ┐\n\ + │ 1.1 1.1 1.1 1.1 1.1 1.1 1.1 1.1 1.1 │\n\ + │ 5.1 5.1 5.1 5.1 5.1 5.1 5.1 5.1 5.1 │\n\ + │ 9.1 9.1 9.1 9.1 9.1 9.1 9.1 9.1 9.1 │\n\ + │ 2.1 2.1 2.1 2.1 2.1 2.1 2.1 2.1 2.1 │\n\ + │ 6.1 6.1 6.1 6.1 6.1 6.1 6.1 6.1 6.1 │\n\ + │ 3.1 3.1 3.1 3.1 3.1 3.1 3.1 3.1 3.1 │\n\ + │ 4.1 4.1 4.1 4.1 4.1 4.1 4.1 4.1 4.1 │\n\ + │ 8.1 8.1 8.1 8.1 8.1 8.1 8.1 8.1 8.1 │\n\ + │ 7.1 7.1 7.1 7.1 7.1 7.1 7.1 7.1 7.1 │\n\ + └ ┘"; + assert_eq!(format!("{:.1}", mat), correct); + } + fn check_dyad(s: f64, a_ten: &Tensor2, b_ten: &Tensor2, dd_ten: &Tensor4, tol: f64) { let a = a_ten.to_matrix(); let b = b_ten.to_matrix(); From 4cec57fde5811d3b400c535b76b6d0e6869e1c2b Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Fri, 10 May 2024 19:45:09 +1000 Subject: [PATCH 05/44] Impl operators for plasticity --- russell_tensor/src/operations.rs | 97 ++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/russell_tensor/src/operations.rs b/russell_tensor/src/operations.rs index b1d36775..11d0bc40 100644 --- a/russell_tensor/src/operations.rs +++ b/russell_tensor/src/operations.rs @@ -1578,6 +1578,49 @@ pub fn t4_ddot_t4(ee: &mut Tensor4, alpha: f64, cc: &Tensor4, dd: &Tensor4) -> R mat_mat_mul(&mut ee.mat, alpha, &cc.mat, &dd.mat, 0.0) } +/// Computes Tensor2 double-dot Tensor4 double-dot Tensor2 +/// +/// Computes: +/// +/// ```text +/// s = a : D : b +/// ``` +/// +/// Note: this function does NOT work with mixed symmetry types. +/// +/// For example, the Lagrange multiplier in Plasticity needs this operation. +#[inline] +pub fn t2_ddot_t4_ddot_t2(a: &Tensor2, dd: &Tensor4, b: &Tensor2, workspace: &mut Tensor2) -> Result { + if workspace.mandel() != a.mandel() { + return Err("tensors must have the same Mandel representation"); + } + t4_ddot_t2(workspace, 1.0, dd, b)?; // D : b + Ok(t2_ddot_t2(a, workspace)) // a : D : b +} + +/// Computes Tensor4 double-dot Tensor2 dyadic Tensor2 double-dot Tensor4 +/// +/// Computes: +/// +/// ```text +/// D += α (D : a) ⊗ (b : D) +/// ``` +/// +/// Note: this function does NOT work with mixed symmetry types. +#[inline] +pub fn t4_ddot_t2_dyad_t2_ddot_t4( + dd: &mut Tensor4, + alpha: f64, + a: &Tensor2, + b: &Tensor2, + workspace1: &mut Tensor2, + workspace2: &mut Tensor2, +) -> Result<(), StrError> { + t4_ddot_t2(workspace1, 1.0, dd, a)?; // D : a + t2_ddot_t4(workspace2, 1.0, b, dd)?; // b : D + t2_dyad_t2_update(dd, alpha, workspace1, workspace2) // D += α (D : a) ⊗ (b : D) +} + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #[cfg(test)] @@ -2761,4 +2804,58 @@ mod tests { └ ┘" ); } + + #[test] + fn t2_ddot_t4_ddot_t2_works() { + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 2.0, 3.0], + [4.0, 5.0, 6.0], + [7.0, 8.0, 9.0], + ], Mandel::General).unwrap(); + #[rustfmt::skip] + let b = Tensor2::from_matrix(&[ + [9.0, 8.0, 7.0], + [6.0, 5.0, 4.0], + [3.0, 2.0, 1.0], + ], Mandel::General).unwrap(); + let mat = Matrix::filled(9, 9, -1.0); + let dd = Tensor4::from_matrix(&mat, Mandel::General).unwrap(); + let mut aux = Tensor2::new(Mandel::General); + let s = t2_ddot_t4_ddot_t2(&a, &dd, &b, &mut aux).unwrap(); + approx_eq(s, -2025.0, 1e-15); + } + + #[test] + fn t4_ddot_t2_dyad_t2_ddot_t4_works() { + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 2.0, 3.0], + [4.0, 5.0, 6.0], + [7.0, 8.0, 9.0], + ], Mandel::General).unwrap(); + #[rustfmt::skip] + let b = Tensor2::from_matrix(&[ + [9.0, 8.0, 7.0], + [6.0, 5.0, 4.0], + [3.0, 2.0, 1.0], + ], Mandel::General).unwrap(); + let mat = Matrix::filled(9, 9, -1.0); + let mut dd = Tensor4::from_matrix(&mat, Mandel::General).unwrap(); + let mut aux1 = Tensor2::new(Mandel::General); + let mut aux2 = Tensor2::new(Mandel::General); + t4_ddot_t2_dyad_t2_ddot_t4(&mut dd, 2.0, &a, &b, &mut aux1, &mut aux2).unwrap(); + let correct = [ + [4049., 4049., 4049., 4049., 4049., 4049., 4049., 4049., 4049.], + [4049., 4049., 4049., 4049., 4049., 4049., 4049., 4049., 4049.], + [4049., 4049., 4049., 4049., 4049., 4049., 4049., 4049., 4049.], + [4049., 4049., 4049., 4049., 4049., 4049., 4049., 4049., 4049.], + [4049., 4049., 4049., 4049., 4049., 4049., 4049., 4049., 4049.], + [4049., 4049., 4049., 4049., 4049., 4049., 4049., 4049., 4049.], + [4049., 4049., 4049., 4049., 4049., 4049., 4049., 4049., 4049.], + [4049., 4049., 4049., 4049., 4049., 4049., 4049., 4049., 4049.], + [4049., 4049., 4049., 4049., 4049., 4049., 4049., 4049., 4049.], + ]; + mat_approx_eq(&dd.to_matrix(), &correct, 1e-12); + } } From 60b19982c1e75be186e813fa88e4b08ea17ec144 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Sat, 11 May 2024 12:34:24 +1000 Subject: [PATCH 06/44] [tensor] Improve doc comments --- .vscode/settings.json | 5 + russell_tensor/src/operations.rs | 327 ++++++++++++++++++++++--------- 2 files changed, 243 insertions(+), 89 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index c16583ba..af2deddf 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -13,6 +13,7 @@ "brusselator", "bweuler", "Cephes", + "Cᵢⱼₛₜ", "cmath", "condest", "copysign", @@ -30,16 +31,20 @@ "dggev", "dgssvx", "Dᵢⱼₖₗ", + "Dᵢⱼₛₜ", + "Dₒₚₖₗ", "dopri", "Dormand", "Dorozhinskii", "dpotrf", "dscal", + "Dₛₜₖₗ", "dsyev", "dsyrk", "dtype", "dydx", "dznrm2", + "Eᵢⱼₖₗ", "erfinv", "FACCON", "Fehlberg", diff --git a/russell_tensor/src/operations.rs b/russell_tensor/src/operations.rs index 11d0bc40..05ac6e1c 100644 --- a/russell_tensor/src/operations.rs +++ b/russell_tensor/src/operations.rs @@ -6,11 +6,25 @@ use russell_lab::{ /// Performs the double-dot (ddot) operation between two Tensor2 (inner product) /// +/// Computes: +/// /// ```text /// s = a : b /// ``` /// -/// Note: this function works with mixed symmetry types. +/// With orthonormal Cartesian components: +/// +/// ```text +/// s = Σ Σ aᵢⱼ bᵢⱼ +/// i j +/// ``` +/// +/// Or, in Mandel basis: +/// +/// ```text +/// s = Σ aₘ bₘ +/// m +/// ``` /// /// # Examples /// @@ -31,27 +45,37 @@ use russell_lab::{ /// [0.0, 4.0, 1.0], /// ], Mandel::General)?; /// -/// let res = t2_ddot_t2(&a, &b); +/// let res = t2_ddot_t2(&a.to_general(), &b)?; /// /// approx_eq(res, 8.0, 1e-15); /// Ok(()) /// } /// ``` #[inline] -pub fn t2_ddot_t2(a: &Tensor2, b: &Tensor2) -> f64 { - vec_inner(&a.vec, &b.vec) +pub fn t2_ddot_t2(a: &Tensor2, b: &Tensor2) -> Result { + if a.vec.dim() != b.vec.dim() { + return Err("tensors are incompatible"); + } + Ok(vec_inner(&a.vec, &b.vec)) } /// Performs the single dot operation between two Tensor2 (matrix multiplication) /// +/// Computes: +/// /// ```text -/// C = A · B +/// c = a · b /// ``` /// -/// # Note +/// With orthonormal Cartesian components: +/// +/// ```text +/// cᵢⱼ = Σ aᵢₖ bₖⱼ +/// k +/// ``` /// -/// Even if `A` and `B` are symmetric, the result `C` may not be symmetric. -/// Thus, `C` must be a General tensor. +/// **Important:** Even if `a` and `b` are symmetric, the result `c` +/// may not be symmetric. Therefore, `c` must be a General tensor. /// /// # Examples /// @@ -85,17 +109,17 @@ pub fn t2_ddot_t2(a: &Tensor2, b: &Tensor2) -> f64 { /// } /// ``` #[rustfmt::skip] -pub fn t2_dot_t2(cc: &mut Tensor2, aa: &Tensor2, bb: &Tensor2) -> Result<(), StrError> { - let dim = aa.vec.dim(); - if cc.vec.dim() != 9 { - return Err("C tensor must be General"); +pub fn t2_dot_t2(c: &mut Tensor2, a: &Tensor2, b: &Tensor2) -> Result<(), StrError> { + let dim = a.vec.dim(); + if c.vec.dim() != 9 { + return Err("'c' tensor must be General"); } - if bb.vec.dim() != dim { - return Err("A and B tensors must be compatible"); + if b.vec.dim() != dim { + return Err("'a' and 'b' tensors must be compatible"); } - let a = &aa.vec; - let b = &bb.vec; - let c = &mut cc.vec; + let a = &a.vec; + let b = &b.vec; + let c = &mut c.vec; let tsq2 = 2.0 * SQRT_2; if dim == 4 { c[0] = a[0] * b[0] + (a[3] * b[3]) / 2.0; @@ -133,10 +157,19 @@ pub fn t2_dot_t2(cc: &mut Tensor2, aa: &Tensor2, bb: &Tensor2) -> Result<(), Str /// Performs the single dot operation between a Tensor2 and a vector /// +/// Computes: +/// /// ```text /// v = α a · u /// ``` /// +/// With orthonormal Cartesian components: +/// +/// ```text +/// vᵢ = α Σ aᵢⱼ uⱼ +/// j +/// ``` +/// /// # Examples /// /// ``` @@ -184,10 +217,19 @@ pub fn t2_dot_vec(v: &mut Vector, alpha: f64, a: &Tensor2, u: &Vector) -> Result /// Performs the single dot operation between a vector and a Tensor2 /// +/// Computes: +/// /// ```text /// v = α u · a /// ``` /// +/// With orthonormal Cartesian components: +/// +/// ```text +/// vⱼ = α Σ uᵢ aᵢⱼ +/// i +/// ``` +/// /// # Examples /// /// ``` @@ -235,17 +277,21 @@ pub fn vec_dot_t2(v: &mut Vector, alpha: f64, u: &Vector, a: &Tensor2) -> Result /// Performs the dyadic product between two vectors resulting in a second-order tensor /// +/// Computes: +/// /// ```text /// T = α u ⊗ v /// ``` /// -/// # Notes +/// With orthonormal Cartesian components: /// -/// * Note that, in general, the dyadic product between two vectors -/// may result in a **non-symmetric** second-order tensor. Therefore, -/// if the input tensor `T` is symmetric, an error may occur. Thus, -/// make sure that the you expect `u ⊗ v` to be symmetric when passing -/// a symmetric tensor `T`. +/// ```text +/// Tᵢⱼ = α uᵢ vⱼ +/// ``` +/// +/// **Important:** The dyadic product between two vectors may result in a **non-symmetric** +/// second-order tensor. Therefore, if the input tensor `T` is symmetric, an error may occur. +/// Thus, make sure that the you expect `u ⊗ v` to be symmetric when passing a symmetric tensor `T`. /// /// # Examples /// @@ -307,19 +353,23 @@ pub fn vec_dyad_vec(tt: &mut Tensor2, alpha: f64, u: &Vector, v: &Vector) -> Res Ok(()) } -/// Performs the dyadic product between two Tensor2 resulting in a Tensor4 +/// Performs the dyadic product between two Tensor2 resulting a Tensor4 /// /// ```text /// D = α a ⊗ b /// ``` /// -/// ```text /// With orthonormal Cartesian components: /// +/// ```text /// Dᵢⱼₖₗ = α aᵢⱼ bₖₗ /// ``` /// -/// Note: this function does NOT work with mixed symmetry types. +/// Or, in Mandel basis: +/// +/// ```text +/// Dₘₙ = α aₘ bₙ +/// ``` /// /// # Examples /// @@ -366,17 +416,23 @@ pub fn t2_dyad_t2(dd: &mut Tensor4, alpha: f64, a: &Tensor2, b: &Tensor2) -> Res /// Performs the dyadic product between two Tensor2 resulting in a Tensor4 (with update) /// +/// Computes: +/// /// ```text /// D += α a ⊗ b /// ``` /// -/// ```text /// With orthonormal Cartesian components: /// +/// ```text /// Dᵢⱼₖₗ += α aᵢⱼ bₖₗ /// ``` /// -/// Note: this function does NOT work with mixed symmetry types. +/// Or, in Mandel basis: +/// +/// ```text +/// Dₘₙ += α aₘ bₙ +/// ``` /// /// # Examples /// @@ -428,18 +484,20 @@ pub fn t2_dyad_t2_update(dd: &mut Tensor4, alpha: f64, a: &Tensor2, b: &Tensor2) /// Performs the overbar dyadic product between two Tensor2 resulting in a (general) Tensor4 /// +/// Computes: +/// /// ```text /// _ /// D = s A ⊗ B /// ``` -/// -/// ```text +/// /// With orthonormal Cartesian components: /// +/// ```text /// Dᵢⱼₖₗ = s Aᵢₖ Bⱼₗ /// ``` /// -/// **Important:** The result is **not** necessarily minor-symmetric; therefore `dd` must be General. +/// **Important:** The result is **not** necessarily minor-symmetric; therefore `D` must be General. #[rustfmt::skip] pub fn t2_odyad_t2(dd: &mut Tensor4, s: f64, aa: &Tensor2, bb: &Tensor2) -> Result<(), StrError> { if dd.mat.dims().1 != 9 { @@ -728,18 +786,20 @@ pub fn t2_odyad_t2(dd: &mut Tensor4, s: f64, aa: &Tensor2, bb: &Tensor2) -> Resu /// Performs the underbar dyadic product between two Tensor2 resulting in a (general) Tensor4 /// +/// Computes: +/// /// ```text /// D = s A ⊗ B /// ‾ /// ``` -/// -/// ```text +/// /// With orthonormal Cartesian components: /// +/// ```text /// Dᵢⱼₖₗ = s Aᵢₗ Bⱼₖ /// ``` /// -/// **Important:** The result is **not** necessarily minor-symmetric; therefore `dd` must be General. +/// **Important:** The result is **not** necessarily minor-symmetric; therefore `D` must be General. #[rustfmt::skip] pub fn t2_udyad_t2(dd: &mut Tensor4, s: f64, aa: &Tensor2, bb: &Tensor2) -> Result<(), StrError> { if dd.mat.dims().1 != 9 { @@ -1028,17 +1088,21 @@ pub fn t2_udyad_t2(dd: &mut Tensor4, s: f64, aa: &Tensor2, bb: &Tensor2) -> Resu /// Performs the self-sum-dyadic (ssd) operation with a Tensor2 yielding a minor-symmetric Tensor4 /// +/// Computes: +/// /// ```text /// _ /// D = s (A ⊗ A + A ⊗ A) /// ‾ /// ``` -/// -/// ```text +/// /// With orthonormal Cartesian components: /// +/// ```text /// Dᵢⱼₖₗ = s (Aᵢₖ Aⱼₗ + Aᵢₗ Aⱼₖ) /// ``` +/// +/// **Important:** Even if `A` is Symmetric 2D, the result may not be expressed by a Symmetric 2D Tensor4. /// /// # Output /// @@ -1047,10 +1111,6 @@ pub fn t2_udyad_t2(dd: &mut Tensor4, s: f64, aa: &Tensor2, bb: &Tensor2) -> Resu /// # Input /// /// * `aa` -- Second-order tensor, symmetric or not. -/// -/// # Note -/// -/// Even if `A` is Symmetric 2D, the result may not be expressed by a Symmetric 2D Tensor4. #[rustfmt::skip] pub fn t2_ssd(dd: &mut Tensor4, s: f64, aa: &Tensor2) -> Result<(), StrError> { if dd.mat.dims().1 != 6 { @@ -1190,18 +1250,22 @@ pub fn t2_ssd(dd: &mut Tensor4, s: f64, aa: &Tensor2) -> Result<(), StrError> { /// Performs the quad-sum-dyadic (qsd) operation with two Tensor2 yielding a minor-symmetric Tensor4 /// +/// Computes: +/// /// ```text /// _ _ /// D = s (A ⊗ B + A ⊗ B + B ⊗ A + B ⊗ A) /// ‾ ‾ /// ``` -/// -/// ```text +/// /// With orthonormal Cartesian components: /// +/// ```text /// Dᵢⱼₖₗ = s (Aᵢₖ Bⱼₗ + Aᵢₗ Bⱼₖ + Bᵢₖ Aⱼₗ + Bᵢₗ Aⱼₖ) /// ``` /// +/// **Important:** Even if `A` and `B` are Symmetric 2D, the result may not be expressed by a Symmetric 2D Tensor4. +/// /// # Output /// /// * `dd` -- The result is minor-symmetric; therefore `dd` must be Symmetric (but not 2D). @@ -1210,10 +1274,6 @@ pub fn t2_ssd(dd: &mut Tensor4, s: f64, aa: &Tensor2) -> Result<(), StrError> { /// /// * `aa` -- Second-order tensor, symmetric or not (with the same Mandel type as `bb`) /// * `bb` -- Second-order tensor, symmetric or not (with the same Mandel type as `aa`) -/// -/// # Note -/// -/// Even if `A` and `B` are Symmetric 2D, the result may not be expressed by a Symmetric 2D Tensor4. #[rustfmt::skip] pub fn t2_qsd_t2(dd: &mut Tensor4, s: f64, aa: &Tensor2, bb: &Tensor2) -> Result<(), StrError> { if dd.mat.dims().1 != 6 { @@ -1361,7 +1421,19 @@ pub fn t2_qsd_t2(dd: &mut Tensor4, s: f64, aa: &Tensor2, bb: &Tensor2) -> Result /// b = α D : a /// ``` /// -/// Note: this function does NOT work with mixed symmetry types. +/// With orthonormal Cartesian components: +/// +/// ```text +/// bᵢⱼ = α Σ Σ Dᵢⱼₖₗ aₖₗ +/// k l +/// ``` +/// +/// Or, in Mandel basis: +/// +/// ```text +/// bₘ = α Σ Dₘₙ aₙ +/// n +/// ``` /// /// # Examples /// @@ -1408,11 +1480,25 @@ pub fn t4_ddot_t2(b: &mut Tensor2, alpha: f64, dd: &Tensor4, a: &Tensor2) -> Res /// Performs the double-dot (ddot) operation between a Tensor4 and a Tensor2 with update /// +/// Computes: +/// /// ```text /// b = α D : a + β b /// ``` /// -/// Note: this function does NOT work with mixed symmetry types. +/// With orthonormal Cartesian components: +/// +/// ```text +/// bᵢⱼ = α Σ Σ Dᵢⱼₖₗ aₖₗ + β bᵢⱼ +/// k l +/// ``` +/// +/// Or, in Mandel basis: +/// +/// ```text +/// bₘ = α Σ Dₘₙ aₙ + β bₘ +/// n +/// ``` /// /// # Examples /// @@ -1463,11 +1549,18 @@ pub fn t4_ddot_t2_update(b: &mut Tensor2, alpha: f64, dd: &Tensor4, a: &Tensor2, /// Performs the double-dot (ddot) operation between a Tensor2 and a Tensor4 /// +/// Computes: +/// /// ```text /// b = α a : D /// ``` /// -/// Note: this function does NOT work with mixed symmetry types. +/// With orthonormal Cartesian components: +/// +/// ```text +/// bₖₗ = α Σ Σ aᵢⱼ Dᵢⱼₖₗ +/// i j +/// ``` /// /// # Examples /// @@ -1514,11 +1607,25 @@ pub fn t2_ddot_t4(b: &mut Tensor2, alpha: f64, a: &Tensor2, dd: &Tensor4) -> Res /// Performs the double-dot (ddot) operation between two Tensor4 /// +/// Computes: +/// /// ```text /// E = α C : D /// ``` /// -/// Note: this function does NOT work with mixed symmetry types. +/// With orthonormal Cartesian components: +/// +/// ```text +/// Eᵢⱼₖₗ = α Σ Σ Cᵢⱼₛₜ : Dₛₜₖₗ +/// s t +/// ``` +/// +/// Or, in Mandel basis: +/// +/// ```text +/// Eₘₙ = α Σ Cₘₐ Dₐₙ +/// m +/// ``` /// /// # Examples /// @@ -1586,16 +1693,33 @@ pub fn t4_ddot_t4(ee: &mut Tensor4, alpha: f64, cc: &Tensor4, dd: &Tensor4) -> R /// s = a : D : b /// ``` /// -/// Note: this function does NOT work with mixed symmetry types. +/// With orthonormal Cartesian components: /// -/// For example, the Lagrange multiplier in Plasticity needs this operation. -#[inline] -pub fn t2_ddot_t4_ddot_t2(a: &Tensor2, dd: &Tensor4, b: &Tensor2, workspace: &mut Tensor2) -> Result { - if workspace.mandel() != a.mandel() { - return Err("tensors must have the same Mandel representation"); +/// ```text +/// s = Σ Σ Σ Σ aᵢⱼ Dᵢⱼₖₗ bₖₗ +/// i j k l +/// ``` +/// +/// Or, in Mandel basis: +/// +/// ```text +/// s = Σ Σ aₘ Dₘₙ bₙ +/// m n +/// ``` +/// +/// Note: the Lagrange multiplier in Plasticity needs this operation. +pub fn t2_ddot_t4_ddot_t2(a: &Tensor2, dd: &Tensor4, b: &Tensor2) -> Result { + let dim = a.vec.dim(); + if b.vec.dim() != dim || dd.mat.dims().0 != dim { + return Err("tensors are incompatible"); + } + let mut s = 0.0; + for m in 0..dim { + for n in 0..dim { + s += a.vec[m] * dd.mat.get(m, n) * b.vec[n]; + } } - t4_ddot_t2(workspace, 1.0, dd, b)?; // D : b - Ok(t2_ddot_t2(a, workspace)) // a : D : b + Ok(s) } /// Computes Tensor4 double-dot Tensor2 dyadic Tensor2 double-dot Tensor4 @@ -1603,22 +1727,48 @@ pub fn t2_ddot_t4_ddot_t2(a: &Tensor2, dd: &Tensor4, b: &Tensor2, workspace: &mu /// Computes: /// /// ```text -/// D += α (D : a) ⊗ (b : D) +/// E = α (D : a) ⊗ (b : D) /// ``` /// -/// Note: this function does NOT work with mixed symmetry types. +/// With orthonormal Cartesian components: +/// +/// ```text +/// Eᵢⱼₖₗ = α Σ Σ Σ Σ (Dᵢⱼₛₜ aₛₜ) (bₒₚ Dₒₚₖₗ) +/// s t o p +/// ``` +/// +/// Or, in Mandel basis: +/// +/// ```text +/// Eₘₙ = α Σ Σ (Dₘₐ aₐ) (bₑ Dₑₙ) +/// a e +/// ``` +/// +/// Note: the elastoplastic modulus in Plasticity needs this operation. #[inline] pub fn t4_ddot_t2_dyad_t2_ddot_t4( - dd: &mut Tensor4, + ee: &mut Tensor4, alpha: f64, a: &Tensor2, b: &Tensor2, - workspace1: &mut Tensor2, - workspace2: &mut Tensor2, + dd: &Tensor4, ) -> Result<(), StrError> { - t4_ddot_t2(workspace1, 1.0, dd, a)?; // D : a - t2_ddot_t4(workspace2, 1.0, b, dd)?; // b : D - t2_dyad_t2_update(dd, alpha, workspace1, workspace2) // D += α (D : a) ⊗ (b : D) + let dim = a.vec.dim(); + if b.vec.dim() != dim || dd.mat.dims().0 != dim { + return Err("tensors are incompatible"); + } + ee.mat.fill(0.0); + for m in 0..dim { + for n in 0..dim { + for p in 0..dim { + for q in 0..dim { + ee.mat + .add(m, n, alpha * dd.mat.get(m, p) * a.vec[p] * b.vec[q] * dd.mat.get(q, n)); + } + } + } + } + Ok(()) } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1644,7 +1794,7 @@ mod tests { [6.0, 5.0, 4.0], [3.0, 2.0, 1.0], ], Mandel::General).unwrap(); - let s = t2_ddot_t2(&a, &b); + let s = t2_ddot_t2(&a, &b).unwrap(); assert_eq!(s, 165.0); // sym-3D : sym-3D @@ -1660,7 +1810,7 @@ mod tests { [5.0, 2.0, 4.0], [6.0, 4.0, 1.0], ], Mandel::Symmetric).unwrap(); - let s = t2_ddot_t2(&a, &b); + let s = t2_ddot_t2(&a, &b).unwrap(); approx_eq(s, 162.0, 1e-13); // sym-3D : general @@ -1676,7 +1826,7 @@ mod tests { [6.0, 5.0, 4.0], [3.0, 2.0, 1.0], ], Mandel::General).unwrap(); - let s = t2_ddot_t2(&a, &b); + let s = t2_ddot_t2(&a.to_general(), &b).unwrap(); // TODO approx_eq(s, 168.0, 1e-13); // sym-2D : sym-2D @@ -1692,7 +1842,7 @@ mod tests { [5.0, 2.0, 0.0], [0.0, 0.0, 1.0], ], Mandel::Symmetric2D).unwrap(); - let s = t2_ddot_t2(&a, &b); + let s = t2_ddot_t2(&a, &b).unwrap(); approx_eq(s, 50.0, 1e-13); // sym-2D : sym-3D @@ -1708,7 +1858,7 @@ mod tests { [5.0, 2.0, 4.0], [6.0, 4.0, 1.0], ], Mandel::Symmetric).unwrap(); - let s = t2_ddot_t2(&a, &b); + let s = t2_ddot_t2(&a.to_general(), &b.to_general()).unwrap(); approx_eq(s, 50.0, 1e-13); } @@ -1717,11 +1867,11 @@ mod tests { let a = Tensor2::new(Mandel::Symmetric); let b = Tensor2::new(Mandel::General); let mut c = Tensor2::new(Mandel::Symmetric); - assert_eq!(t2_dot_t2(&mut c, &a, &b).err(), Some("C tensor must be General")); + assert_eq!(t2_dot_t2(&mut c, &a, &b).err(), Some("'c' tensor must be General")); let mut c = Tensor2::new(Mandel::General); assert_eq!( t2_dot_t2(&mut c, &a, &b).err(), - Some("A and B tensors must be compatible") + Some("'a' and 'b' tensors must be compatible") ); } @@ -2821,8 +2971,7 @@ mod tests { ], Mandel::General).unwrap(); let mat = Matrix::filled(9, 9, -1.0); let dd = Tensor4::from_matrix(&mat, Mandel::General).unwrap(); - let mut aux = Tensor2::new(Mandel::General); - let s = t2_ddot_t4_ddot_t2(&a, &dd, &b, &mut aux).unwrap(); + let s = t2_ddot_t4_ddot_t2(&a, &dd, &b).unwrap(); approx_eq(s, -2025.0, 1e-15); } @@ -2841,21 +2990,21 @@ mod tests { [3.0, 2.0, 1.0], ], Mandel::General).unwrap(); let mat = Matrix::filled(9, 9, -1.0); - let mut dd = Tensor4::from_matrix(&mat, Mandel::General).unwrap(); - let mut aux1 = Tensor2::new(Mandel::General); - let mut aux2 = Tensor2::new(Mandel::General); - t4_ddot_t2_dyad_t2_ddot_t4(&mut dd, 2.0, &a, &b, &mut aux1, &mut aux2).unwrap(); + let dd = Tensor4::from_matrix(&mat, Mandel::General).unwrap(); + let mut ee = Tensor4::new(Mandel::General); + t4_ddot_t2_dyad_t2_ddot_t4(&mut ee, 2.0, &a, &b, &dd).unwrap(); + println!("{}", ee.to_matrix()); let correct = [ - [4049., 4049., 4049., 4049., 4049., 4049., 4049., 4049., 4049.], - [4049., 4049., 4049., 4049., 4049., 4049., 4049., 4049., 4049.], - [4049., 4049., 4049., 4049., 4049., 4049., 4049., 4049., 4049.], - [4049., 4049., 4049., 4049., 4049., 4049., 4049., 4049., 4049.], - [4049., 4049., 4049., 4049., 4049., 4049., 4049., 4049., 4049.], - [4049., 4049., 4049., 4049., 4049., 4049., 4049., 4049., 4049.], - [4049., 4049., 4049., 4049., 4049., 4049., 4049., 4049., 4049.], - [4049., 4049., 4049., 4049., 4049., 4049., 4049., 4049., 4049.], - [4049., 4049., 4049., 4049., 4049., 4049., 4049., 4049., 4049.], + [4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050.], + [4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050.], + [4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050.], + [4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050.], + [4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050.], + [4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050.], + [4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050.], + [4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050.], + [4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050.], ]; - mat_approx_eq(&dd.to_matrix(), &correct, 1e-12); + mat_approx_eq(&ee.to_matrix(), &correct, 1e-11); } } From 319962fc87d6754e4aa7bd08bd75c3bc0698b124 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Sat, 11 May 2024 12:39:06 +1000 Subject: [PATCH 07/44] [wip] Use assert in tensor operations --- russell_tensor/src/operations.rs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/russell_tensor/src/operations.rs b/russell_tensor/src/operations.rs index 05ac6e1c..3535136b 100644 --- a/russell_tensor/src/operations.rs +++ b/russell_tensor/src/operations.rs @@ -26,6 +26,10 @@ use russell_lab::{ /// m /// ``` /// +/// # Panics +/// +/// The [crate::Mandel] representation must be the same for all tensors; otherwise this function will panic. +/// /// # Examples /// /// ``` @@ -45,18 +49,16 @@ use russell_lab::{ /// [0.0, 4.0, 1.0], /// ], Mandel::General)?; /// -/// let res = t2_ddot_t2(&a.to_general(), &b)?; +/// let res = t2_ddot_t2(&a.to_general(), &b); /// /// approx_eq(res, 8.0, 1e-15); /// Ok(()) /// } /// ``` #[inline] -pub fn t2_ddot_t2(a: &Tensor2, b: &Tensor2) -> Result { - if a.vec.dim() != b.vec.dim() { - return Err("tensors are incompatible"); - } - Ok(vec_inner(&a.vec, &b.vec)) +pub fn t2_ddot_t2(a: &Tensor2, b: &Tensor2) -> f64 { + assert_eq!(a.vec.dim(), b.vec.dim()); + vec_inner(&a.vec, &b.vec) } /// Performs the single dot operation between two Tensor2 (matrix multiplication) @@ -1794,7 +1796,7 @@ mod tests { [6.0, 5.0, 4.0], [3.0, 2.0, 1.0], ], Mandel::General).unwrap(); - let s = t2_ddot_t2(&a, &b).unwrap(); + let s = t2_ddot_t2(&a, &b); assert_eq!(s, 165.0); // sym-3D : sym-3D @@ -1810,7 +1812,7 @@ mod tests { [5.0, 2.0, 4.0], [6.0, 4.0, 1.0], ], Mandel::Symmetric).unwrap(); - let s = t2_ddot_t2(&a, &b).unwrap(); + let s = t2_ddot_t2(&a, &b); approx_eq(s, 162.0, 1e-13); // sym-3D : general @@ -1826,7 +1828,7 @@ mod tests { [6.0, 5.0, 4.0], [3.0, 2.0, 1.0], ], Mandel::General).unwrap(); - let s = t2_ddot_t2(&a.to_general(), &b).unwrap(); // TODO + let s = t2_ddot_t2(&a.to_general(), &b); approx_eq(s, 168.0, 1e-13); // sym-2D : sym-2D @@ -1842,7 +1844,7 @@ mod tests { [5.0, 2.0, 0.0], [0.0, 0.0, 1.0], ], Mandel::Symmetric2D).unwrap(); - let s = t2_ddot_t2(&a, &b).unwrap(); + let s = t2_ddot_t2(&a, &b); approx_eq(s, 50.0, 1e-13); // sym-2D : sym-3D @@ -1858,7 +1860,7 @@ mod tests { [5.0, 2.0, 4.0], [6.0, 4.0, 1.0], ], Mandel::Symmetric).unwrap(); - let s = t2_ddot_t2(&a.to_general(), &b.to_general()).unwrap(); + let s = t2_ddot_t2(&a.to_general(), &b.to_general()); approx_eq(s, 50.0, 1e-13); } From 1c12fa14e55d799198dd7c6e29b2d683b65dd047 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Sat, 11 May 2024 12:44:00 +1000 Subject: [PATCH 08/44] Revert back to returning Result in tensor operations --- russell_tensor/src/operations.rs | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/russell_tensor/src/operations.rs b/russell_tensor/src/operations.rs index 3535136b..cae9a77a 100644 --- a/russell_tensor/src/operations.rs +++ b/russell_tensor/src/operations.rs @@ -26,10 +26,6 @@ use russell_lab::{ /// m /// ``` /// -/// # Panics -/// -/// The [crate::Mandel] representation must be the same for all tensors; otherwise this function will panic. -/// /// # Examples /// /// ``` @@ -49,16 +45,18 @@ use russell_lab::{ /// [0.0, 4.0, 1.0], /// ], Mandel::General)?; /// -/// let res = t2_ddot_t2(&a.to_general(), &b); +/// let res = t2_ddot_t2(&a.to_general(), &b)?; /// /// approx_eq(res, 8.0, 1e-15); /// Ok(()) /// } /// ``` #[inline] -pub fn t2_ddot_t2(a: &Tensor2, b: &Tensor2) -> f64 { - assert_eq!(a.vec.dim(), b.vec.dim()); - vec_inner(&a.vec, &b.vec) +pub fn t2_ddot_t2(a: &Tensor2, b: &Tensor2) -> Result { + if a.vec.dim() != b.vec.dim() { + return Err("tensors are incompatible"); + } + Ok(vec_inner(&a.vec, &b.vec)) } /// Performs the single dot operation between two Tensor2 (matrix multiplication) @@ -1796,7 +1794,7 @@ mod tests { [6.0, 5.0, 4.0], [3.0, 2.0, 1.0], ], Mandel::General).unwrap(); - let s = t2_ddot_t2(&a, &b); + let s = t2_ddot_t2(&a, &b).unwrap(); assert_eq!(s, 165.0); // sym-3D : sym-3D @@ -1812,7 +1810,7 @@ mod tests { [5.0, 2.0, 4.0], [6.0, 4.0, 1.0], ], Mandel::Symmetric).unwrap(); - let s = t2_ddot_t2(&a, &b); + let s = t2_ddot_t2(&a, &b).unwrap(); approx_eq(s, 162.0, 1e-13); // sym-3D : general @@ -1828,8 +1826,7 @@ mod tests { [6.0, 5.0, 4.0], [3.0, 2.0, 1.0], ], Mandel::General).unwrap(); - let s = t2_ddot_t2(&a.to_general(), &b); - approx_eq(s, 168.0, 1e-13); + assert_eq!(t2_ddot_t2(&a, &b).err(), Some("tensors are incompatible")); // sym-2D : sym-2D #[rustfmt::skip] @@ -1844,7 +1841,7 @@ mod tests { [5.0, 2.0, 0.0], [0.0, 0.0, 1.0], ], Mandel::Symmetric2D).unwrap(); - let s = t2_ddot_t2(&a, &b); + let s = t2_ddot_t2(&a, &b).unwrap(); approx_eq(s, 50.0, 1e-13); // sym-2D : sym-3D @@ -1860,7 +1857,7 @@ mod tests { [5.0, 2.0, 4.0], [6.0, 4.0, 1.0], ], Mandel::Symmetric).unwrap(); - let s = t2_ddot_t2(&a.to_general(), &b.to_general()); + let s = t2_ddot_t2(&a.to_general(), &b.to_general()).unwrap(); approx_eq(s, 50.0, 1e-13); } From c13d841de3d945ade9e13e351f1f2eec6312f2d4 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Sat, 11 May 2024 12:46:18 +1000 Subject: [PATCH 09/44] Improve tests --- russell_tensor/src/operations.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/russell_tensor/src/operations.rs b/russell_tensor/src/operations.rs index cae9a77a..9a9d0516 100644 --- a/russell_tensor/src/operations.rs +++ b/russell_tensor/src/operations.rs @@ -2972,6 +2972,13 @@ mod tests { let dd = Tensor4::from_matrix(&mat, Mandel::General).unwrap(); let s = t2_ddot_t4_ddot_t2(&a, &dd, &b).unwrap(); approx_eq(s, -2025.0, 1e-15); + + let b = Tensor2::from_matrix( + &[[-1.0, -2.0, 0.0], [-2.0, 2.0, 0.0], [0.0, 0.0, -3.0]], + Mandel::Symmetric2D, + ) + .unwrap(); + assert_eq!(t2_ddot_t4_ddot_t2(&a, &dd, &b).err(), Some("tensors are incompatible")); } #[test] @@ -2992,7 +2999,6 @@ mod tests { let dd = Tensor4::from_matrix(&mat, Mandel::General).unwrap(); let mut ee = Tensor4::new(Mandel::General); t4_ddot_t2_dyad_t2_ddot_t4(&mut ee, 2.0, &a, &b, &dd).unwrap(); - println!("{}", ee.to_matrix()); let correct = [ [4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050.], [4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050.], @@ -3005,5 +3011,15 @@ mod tests { [4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050.], ]; mat_approx_eq(&ee.to_matrix(), &correct, 1e-11); + + let b = Tensor2::from_matrix( + &[[-1.0, -2.0, 0.0], [-2.0, 2.0, 0.0], [0.0, 0.0, -3.0]], + Mandel::Symmetric2D, + ) + .unwrap(); + assert_eq!( + t4_ddot_t2_dyad_t2_ddot_t4(&mut ee, 2.0, &a, &b, &dd).err(), + Some("tensors are incompatible") + ); } } From ad3b428a079d57a3da75a203fb97686790859bb9 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Sat, 11 May 2024 19:21:58 +1000 Subject: [PATCH 10/44] Impl tensor4 update --- russell_tensor/src/tensor4.rs | 104 +++++++++++++++++++++++++++++++--- 1 file changed, 95 insertions(+), 9 deletions(-) diff --git a/russell_tensor/src/tensor4.rs b/russell_tensor/src/tensor4.rs index 35498794..1ccb57ff 100644 --- a/russell_tensor/src/tensor4.rs +++ b/russell_tensor/src/tensor4.rs @@ -1,6 +1,6 @@ use super::{IJKL_TO_MN, IJKL_TO_MN_SYM, MN_TO_IJKL, SQRT_2}; use crate::{AsMatrix9x9, Mandel, StrError, ONE_BY_3, TWO_BY_3}; -use russell_lab::{mat_copy, Matrix}; +use russell_lab::{mat_copy, mat_update, Matrix}; use serde::{Deserialize, Serialize}; /// Implements a fourth order-tensor, minor-symmetric or not @@ -231,13 +231,13 @@ impl Tensor4 { || inp[i][j][k][l] != inp[i][j][l][k] || inp[i][j][k][l] != inp[j][i][l][k] { - return Err("minor-symmetric Tensor4 does not pass symmetry check"); + return Err("the input data does not correspond to a symmetric tensor"); } } else { let (m, n) = IJKL_TO_MN[i][j][k][l]; if m > max || n > max { if inp[i][j][k][l] != 0.0 { - return Err("cannot define 2D Tensor4 due to non-zero values"); + return Err("the input data does not correspond to a 2D symmetric tensor"); } continue; } else if m < 3 && n < 3 { @@ -322,6 +322,10 @@ impl Tensor4 { /// even if it corresponds to a minor-symmetric tensor. /// * `mandel` -- the [Mandel] representation /// + /// # Panics + /// + /// This function panics if the input matrix is not (9 x 9) + /// /// # Examples /// /// ``` @@ -372,12 +376,12 @@ impl Tensor4 { || inp.at(m, n) != inp.at(r, s) || inp.at(m, n) != inp.at(u, v) { - return Err("minor-symmetric Tensor4 does not pass symmetry check"); + return Err("the input data does not correspond to a symmetric tensor"); } } else { if m > max || n > max { if inp.at(m, n) != 0.0 { - return Err("cannot define 2D Tensor4 due to non-zero values"); + return Err("the input data does not correspond to a 2D symmetric tensor"); } continue; } else if m < 3 && n < 3 { @@ -576,6 +580,56 @@ impl Tensor4 { } } + /// Adds another tensor to this one + /// + /// ```text + /// self += α other + /// ``` + /// + /// # Examples + /// + /// ``` + /// use russell_lab::approx_eq; + /// use russell_tensor::{Mandel, MN_TO_IJKL, Tensor4, StrError}; + /// + /// fn main() -> Result<(), StrError> { + /// let mut inp = [[0.0; 9]; 9]; + /// for m in 0..4 { + /// for n in 0..4 { + /// let (i, j, k, l) = MN_TO_IJKL[m][n]; + /// inp[m][n] = 1.0; + /// } + /// } + /// + /// let mut dd = Tensor4::new(Mandel::General); + /// let ee = Tensor4::from_matrix(&inp, Mandel::General)?; + /// dd.update(2.0, &ee)?; + /// + /// assert_eq!( + /// format!("{:.0}", dd.to_matrix()), + /// "┌ ┐\n\ + /// │ 2 2 2 2 0 0 0 0 0 │\n\ + /// │ 2 2 2 2 0 0 0 0 0 │\n\ + /// │ 2 2 2 2 0 0 0 0 0 │\n\ + /// │ 2 2 2 2 0 0 0 0 0 │\n\ + /// │ 0 0 0 0 0 0 0 0 0 │\n\ + /// │ 0 0 0 0 0 0 0 0 0 │\n\ + /// │ 0 0 0 0 0 0 0 0 0 │\n\ + /// │ 0 0 0 0 0 0 0 0 0 │\n\ + /// │ 0 0 0 0 0 0 0 0 0 │\n\ + /// └ ┘" + /// ); + /// Ok(()) + /// } + /// ``` + pub fn update(&mut self, alpha: f64, other: &Tensor4) -> Result<(), StrError> { + if other.mandel() != self.mandel() { + return Err("tensors are incompatible"); + } + mat_update(&mut self.mat, alpha, &other.mat).unwrap(); + Ok(()) + } + /// Returns a nested array (standard components; not Mandel) representing this tensor /// /// # Examples @@ -1150,10 +1204,16 @@ mod tests { #[test] fn from_array_fails_captures_errors() { let res = Tensor4::from_array(&SamplesTensor4::SAMPLE1, Mandel::Symmetric); - assert_eq!(res.err(), Some("minor-symmetric Tensor4 does not pass symmetry check")); + assert_eq!( + res.err(), + Some("the input data does not correspond to a symmetric tensor") + ); let res = Tensor4::from_array(&SamplesTensor4::SYM_SAMPLE1, Mandel::Symmetric2D); - assert_eq!(res.err(), Some("cannot define 2D Tensor4 due to non-zero values")); + assert_eq!( + res.err(), + Some("the input data does not correspond to a 2D symmetric tensor") + ); } #[test] @@ -1188,13 +1248,19 @@ mod tests { let mut inp = [[0.0; 9]; 9]; inp[0][3] = 1e-15; let res = Tensor4::from_matrix(&inp, Mandel::Symmetric); - assert_eq!(res.err(), Some("minor-symmetric Tensor4 does not pass symmetry check")); + assert_eq!( + res.err(), + Some("the input data does not correspond to a symmetric tensor") + ); inp[0][3] = 0.0; inp[0][4] = 1.0; inp[0][7] = 1.0; let res = Tensor4::from_matrix(&inp, Mandel::Symmetric2D); - assert_eq!(res.err(), Some("cannot define 2D Tensor4 due to non-zero values")); + assert_eq!( + res.err(), + Some("the input data does not correspond to a 2D symmetric tensor") + ); } #[test] @@ -1267,6 +1333,26 @@ mod tests { } } + #[test] + fn update_works() { + let mut dd = Tensor4::new(Mandel::Symmetric2D); + let ee = Tensor4::from_array(&SamplesTensor4::SYM_2D_SAMPLE1, Mandel::Symmetric2D).unwrap(); + dd.update(2.0, &ee).unwrap(); + for i in 0..3 { + for j in 0..3 { + for k in 0..3 { + for l in 0..3 { + approx_eq( + dd.get(i, j, k, l), + 2.0 * SamplesTensor4::SYM_2D_SAMPLE1[i][j][k][l], + 1e-14, + ); + } + } + } + } + } + #[test] fn to_array_works() { // general From 8507788196e6e1b2bba2d8d523407b482696c5b3 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Sun, 12 May 2024 10:54:53 +1000 Subject: [PATCH 11/44] Tensor2: hold Mandel. Remove inline --- russell_tensor/src/tensor2.rs | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/russell_tensor/src/tensor2.rs b/russell_tensor/src/tensor2.rs index aadfa159..c2cbc04b 100644 --- a/russell_tensor/src/tensor2.rs +++ b/russell_tensor/src/tensor2.rs @@ -65,6 +65,9 @@ pub struct Tensor2 { /// * Symmetric in 3D: `vec.dim = 6` /// * Symmetric in 2D: `vec.dim = 4` pub vec: Vector, + + /// Holds the Mandel enum + mandel: Mandel, } impl Tensor2 { @@ -93,6 +96,7 @@ impl Tensor2 { pub fn new(mandel: Mandel) -> Self { Tensor2 { vec: Vector::new(mandel.dim()), + mandel, } } @@ -160,9 +164,8 @@ impl Tensor2 { } /// Returns the Mandel representation associated with this Tensor2 - #[inline] pub fn mandel(&self) -> Mandel { - Mandel::new(self.vec.dim()) + self.mandel } /// Sets the Tensor2 with standard components given in matrix form @@ -356,7 +359,6 @@ impl Tensor2 { /// Ok(()) /// } /// ``` - #[inline] pub fn from_matrix(tt: &dyn AsMatrix3x3, mandel: Mandel) -> Result { let mut res = Tensor2::new(mandel); res.set_matrix(tt)?; @@ -596,7 +598,6 @@ impl Tensor2 { } /// Set all values to zero - #[inline] pub fn clear(&mut self) { self.vec.fill(0.0); } @@ -767,6 +768,10 @@ impl Tensor2 { /// Adds another tensor to this one /// + /// ```text + /// self += α other + /// ``` + /// /// # Examples /// /// ``` @@ -797,7 +802,6 @@ impl Tensor2 { /// Ok(()) /// } /// ``` - #[inline] pub fn add(&mut self, alpha: f64, other: &Tensor2) -> Result<(), StrError> { let dim = self.vec.dim(); if other.vec.dim() != dim { @@ -1154,7 +1158,6 @@ impl Tensor2 { /// Ok(()) /// } /// ``` - #[inline] pub fn trace(&self) -> f64 { self.vec[0] + self.vec[1] + self.vec[2] } @@ -1399,7 +1402,6 @@ impl Tensor2 { /// Ok(()) /// } /// ``` - #[inline] pub fn invariant_ii1(&self) -> f64 { self.trace() } @@ -1461,7 +1463,6 @@ impl Tensor2 { /// Ok(()) /// } /// ``` - #[inline] pub fn invariant_ii3(&self) -> f64 { self.determinant() } @@ -1549,7 +1550,6 @@ impl Tensor2 { /// Ok(()) /// } /// ``` - #[inline] pub fn invariant_jj3(&self) -> f64 { self.deviator_determinant() } @@ -1580,7 +1580,6 @@ impl Tensor2 { /// Ok(()) /// } /// ``` - #[inline] pub fn invariant_sigma_m(&self) -> f64 { self.trace() / 3.0 } @@ -1612,7 +1611,6 @@ impl Tensor2 { /// Ok(()) /// } /// ``` - #[inline] pub fn invariant_sigma_d(&self) -> f64 { self.deviator_norm() * SQRT_3_BY_2 } @@ -1639,7 +1637,6 @@ impl Tensor2 { /// Ok(()) /// } /// ``` - #[inline] pub fn invariant_eps_v(&self) -> f64 { self.trace() } @@ -1666,7 +1663,6 @@ impl Tensor2 { /// Ok(()) /// } /// ``` - #[inline] pub fn invariant_eps_d(&self) -> f64 { self.deviator_norm() * SQRT_2_BY_3 } @@ -1732,7 +1728,6 @@ impl Tensor2 { /// r = ‖dev(T)‖ /// l = cos(3θ) = (3 √3 J3)/(2 pow(J2,1.5)) /// ``` - #[inline] pub fn invariants_octahedral(&self) -> (f64, f64, Option) { let distance = self.invariant_ii1() / SQRT_3; let radius = self.deviator_norm(); From bf928d09db55c07ad1533c130906098d0be7247c Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Sun, 12 May 2024 10:55:27 +1000 Subject: [PATCH 12/44] Tensor4: hold Mandel. Rename update to add. Remove inline --- russell_tensor/src/tensor4.rs | 97 +++++++++++++++++++++++++++++------ 1 file changed, 80 insertions(+), 17 deletions(-) diff --git a/russell_tensor/src/tensor4.rs b/russell_tensor/src/tensor4.rs index 1ccb57ff..7dd21082 100644 --- a/russell_tensor/src/tensor4.rs +++ b/russell_tensor/src/tensor4.rs @@ -116,6 +116,9 @@ pub struct Tensor4 { /// * Minor-symmetric in 3D: `(nrow,ncol) = (6,6)` /// * Minor-symmetric in 2D: `(nrow,ncol) = (4,4)` pub mat: Matrix, + + /// Holds the Mandel enum + mandel: Mandel, } impl Tensor4 { @@ -145,6 +148,7 @@ impl Tensor4 { let dim = mandel.dim(); Tensor4 { mat: Matrix::new(dim, dim), + mandel, } } @@ -169,9 +173,8 @@ impl Tensor4 { } /// Returns the Mandel representation associated with this Tensor4 - #[inline] pub fn mandel(&self) -> Mandel { - Mandel::new(self.mat.nrow()) + self.mandel } /// Creates a new Tensor4 constructed from a nested array @@ -310,7 +313,7 @@ impl Tensor4 { } } } - Ok(Tensor4 { mat }) + Ok(Tensor4 { mat, mandel }) } /// Creates a new Tensor4 constructed from a matrix with standard components @@ -454,7 +457,7 @@ impl Tensor4 { } } } - Ok(Tensor4 { mat }) + Ok(Tensor4 { mat, mandel }) } /// Returns the (i,j,k,l) component (standard; not Mandel) @@ -622,7 +625,7 @@ impl Tensor4 { /// Ok(()) /// } /// ``` - pub fn update(&mut self, alpha: f64, other: &Tensor4) -> Result<(), StrError> { + pub fn add(&mut self, alpha: f64, other: &Tensor4) -> Result<(), StrError> { if other.mandel() != self.mandel() { return Err("tensors are incompatible"); } @@ -841,6 +844,7 @@ impl Tensor4 { Tensor4 { // 1 2 3 4 5 6 7 8 9 mat: Matrix::diagonal(&[1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), + mandel: Mandel::General, } } @@ -870,7 +874,10 @@ impl Tensor4 { /// └ ┘ /// ``` pub fn constant_tt() -> Self { - let mut tt = Tensor4 { mat: Matrix::new(9, 9) }; + let mut tt = Tensor4 { + mat: Matrix::new(9, 9), + mandel: Mandel::General, + }; tt.mat.set(0, 0, 1.0); tt.mat.set(1, 1, 1.0); tt.mat.set(2, 2, 1.0); @@ -908,8 +915,15 @@ impl Tensor4 { /// └ ┘ /// ``` pub fn constant_jj(reduced_6x6: bool) -> Self { - let n = if reduced_6x6 { 6 } else { 9 }; - let mut jj = Tensor4 { mat: Matrix::new(n, n) }; + let (n, mandel) = if reduced_6x6 { + (6, Mandel::Symmetric) + } else { + (9, Mandel::General) + }; + let mut jj = Tensor4 { + mat: Matrix::new(n, n), + mandel, + }; jj.mat.set(0, 0, 1.0); jj.mat.set(0, 1, 1.0); jj.mat.set(0, 2, 1.0); @@ -947,8 +961,15 @@ impl Tensor4 { /// └ ┘ /// ``` pub fn constant_pp_iso(reduced_6x6: bool) -> Self { - let n = if reduced_6x6 { 6 } else { 9 }; - let mut pp_iso = Tensor4 { mat: Matrix::new(n, n) }; + let (n, mandel) = if reduced_6x6 { + (6, Mandel::Symmetric) + } else { + (9, Mandel::General) + }; + let mut pp_iso = Tensor4 { + mat: Matrix::new(n, n), + mandel, + }; pp_iso.mat.set(0, 0, ONE_BY_3); pp_iso.mat.set(0, 1, ONE_BY_3); pp_iso.mat.set(0, 2, ONE_BY_3); @@ -987,8 +1008,15 @@ impl Tensor4 { /// └ ┘ /// ``` pub fn constant_pp_sym(reduced_6x6: bool) -> Self { - let n = if reduced_6x6 { 6 } else { 9 }; - let mut pp_sym = Tensor4 { mat: Matrix::new(n, n) }; + let (n, mandel) = if reduced_6x6 { + (6, Mandel::Symmetric) + } else { + (9, Mandel::General) + }; + let mut pp_sym = Tensor4 { + mat: Matrix::new(n, n), + mandel, + }; pp_sym.mat.set(0, 0, 1.0); pp_sym.mat.set(1, 1, 1.0); pp_sym.mat.set(2, 2, 1.0); @@ -1024,7 +1052,10 @@ impl Tensor4 { /// └ ┘ /// ``` pub fn constant_pp_skew() -> Self { - let mut pp_skew = Tensor4 { mat: Matrix::new(9, 9) }; + let mut pp_skew = Tensor4 { + mat: Matrix::new(9, 9), + mandel: Mandel::General, + }; pp_skew.mat.set(6, 6, 1.0); pp_skew.mat.set(7, 7, 1.0); pp_skew.mat.set(8, 8, 1.0); @@ -1056,7 +1087,10 @@ impl Tensor4 { /// └ ┘ /// ``` pub fn constant_pp_dev() -> Self { - let mut pp_dev = Tensor4 { mat: Matrix::new(9, 9) }; + let mut pp_dev = Tensor4 { + mat: Matrix::new(9, 9), + mandel: Mandel::General, + }; pp_dev.mat.set(0, 0, TWO_BY_3); pp_dev.mat.set(0, 1, -ONE_BY_3); pp_dev.mat.set(0, 2, -ONE_BY_3); @@ -1101,8 +1135,15 @@ impl Tensor4 { /// └ ┘ /// ``` pub fn constant_pp_symdev(reduced_6x6: bool) -> Self { - let n = if reduced_6x6 { 6 } else { 9 }; - let mut pp_symdev = Tensor4 { mat: Matrix::new(n, n) }; + let (n, mandel) = if reduced_6x6 { + (6, Mandel::Symmetric) + } else { + (9, Mandel::General) + }; + let mut pp_symdev = Tensor4 { + mat: Matrix::new(n, n), + mandel, + }; pp_symdev.mat.set(0, 0, TWO_BY_3); pp_symdev.mat.set(0, 1, -ONE_BY_3); pp_symdev.mat.set(0, 2, -ONE_BY_3); @@ -1337,7 +1378,7 @@ mod tests { fn update_works() { let mut dd = Tensor4::new(Mandel::Symmetric2D); let ee = Tensor4::from_array(&SamplesTensor4::SYM_2D_SAMPLE1, Mandel::Symmetric2D).unwrap(); - dd.update(2.0, &ee).unwrap(); + dd.add(2.0, &ee).unwrap(); for i in 0..3 { for j in 0..3 { for k in 0..3 { @@ -1663,6 +1704,7 @@ mod tests { fn constant_ii_works() { let ii = Tensor4::constant_ii(); assert_eq!(ii.mat.dims(), (9, 9)); + assert_eq!(ii.mandel, Mandel::General); for i in 0..9 { for j in 0..9 { assert_eq!(ii.mat.get(i, j), IDENTITY4[i][j]); @@ -1673,6 +1715,8 @@ mod tests { #[test] fn constant_tt_works() { let tt = Tensor4::constant_tt(); + assert_eq!(tt.mat.dims(), (9, 9)); + assert_eq!(tt.mandel, Mandel::General); for i in 0..9 { for j in 0..9 { assert_eq!(tt.mat.get(i, j), TRANSPOSITION[i][j]); @@ -1683,6 +1727,8 @@ mod tests { #[test] fn constant_jj_works() { let jj = Tensor4::constant_jj(false); + assert_eq!(jj.mat.dims(), (9, 9)); + assert_eq!(jj.mandel, Mandel::General); for i in 0..9 { for j in 0..9 { assert_eq!(jj.mat.get(i, j), TRACE_PROJECTION[i][j]); @@ -1699,12 +1745,16 @@ mod tests { #[test] fn constant_pp_iso_works() { let pp_iso = Tensor4::constant_pp_iso(false); + assert_eq!(pp_iso.mat.dims(), (9, 9)); + assert_eq!(pp_iso.mandel, Mandel::General); for i in 0..9 { for j in 0..9 { assert_eq!(pp_iso.mat.get(i, j), P_ISO[i][j]); } } let pp_iso = Tensor4::constant_pp_iso(true); + assert_eq!(pp_iso.mat.dims(), (6, 6)); + assert_eq!(pp_iso.mandel, Mandel::Symmetric); for i in 0..6 { for j in 0..6 { assert_eq!(pp_iso.mat.get(i, j), P_ISO[i][j]); @@ -1715,12 +1765,16 @@ mod tests { #[test] fn constant_pp_sym_works() { let pp_sym = Tensor4::constant_pp_sym(false); + assert_eq!(pp_sym.mat.dims(), (9, 9)); + assert_eq!(pp_sym.mandel, Mandel::General); for i in 0..9 { for j in 0..9 { assert_eq!(pp_sym.mat.get(i, j), P_SYM[i][j]); } } let pp_sym = Tensor4::constant_pp_sym(true); + assert_eq!(pp_sym.mat.dims(), (6, 6)); + assert_eq!(pp_sym.mandel, Mandel::Symmetric); for i in 0..6 { for j in 0..6 { assert_eq!(pp_sym.mat.get(i, j), P_SYM[i][j]); @@ -1731,6 +1785,8 @@ mod tests { #[test] fn constant_pp_skew_works() { let pp_skew = Tensor4::constant_pp_skew(); + assert_eq!(pp_skew.mat.dims(), (9, 9)); + assert_eq!(pp_skew.mandel, Mandel::General); for i in 0..9 { for j in 0..9 { assert_eq!(pp_skew.mat.get(i, j), P_SKEW[i][j]); @@ -1741,6 +1797,8 @@ mod tests { #[test] fn constant_pp_dev_works() { let pp_dev = Tensor4::constant_pp_dev(); + assert_eq!(pp_dev.mat.dims(), (9, 9)); + assert_eq!(pp_dev.mandel, Mandel::General); for i in 0..9 { for j in 0..9 { assert_eq!(pp_dev.mat.get(i, j), P_DEV[i][j]); @@ -1752,6 +1810,7 @@ mod tests { fn constant_pp_symdev_works() { let pp_symdev = Tensor4::constant_pp_symdev(false); assert_eq!(pp_symdev.mat.dims(), (9, 9)); + assert_eq!(pp_symdev.mandel, Mandel::General); for i in 0..9 { for j in 0..9 { assert_eq!(pp_symdev.mat.get(i, j), P_SYMDEV[i][j]); @@ -1759,6 +1818,7 @@ mod tests { } let pp_symdev = Tensor4::constant_pp_symdev(true); assert_eq!(pp_symdev.mat.dims(), (6, 6)); + assert_eq!(pp_symdev.mandel, Mandel::Symmetric); for i in 0..6 { for j in 0..6 { assert_eq!(pp_symdev.mat.get(i, j), P_SYMDEV[i][j]); @@ -1771,6 +1831,7 @@ mod tests { let mut pp_symdev = Tensor4::new(Mandel::General); pp_symdev.set_pp_symdev(); assert_eq!(pp_symdev.mat.dims(), (9, 9)); + assert_eq!(pp_symdev.mandel, Mandel::General); for i in 0..9 { for j in 0..9 { assert_eq!(pp_symdev.mat.get(i, j), P_SYMDEV[i][j]); @@ -1779,6 +1840,7 @@ mod tests { let mut pp_symdev = Tensor4::new(Mandel::Symmetric); pp_symdev.set_pp_symdev(); assert_eq!(pp_symdev.mat.dims(), (6, 6)); + assert_eq!(pp_symdev.mandel, Mandel::Symmetric); for i in 0..6 { for j in 0..6 { assert_eq!(pp_symdev.mat.get(i, j), P_SYMDEV[i][j]); @@ -1787,6 +1849,7 @@ mod tests { let mut pp_symdev = Tensor4::new(Mandel::Symmetric2D); pp_symdev.set_pp_symdev(); assert_eq!(pp_symdev.mat.dims(), (4, 4)); + assert_eq!(pp_symdev.mandel, Mandel::Symmetric2D); for i in 0..4 { for j in 0..4 { assert_eq!(pp_symdev.mat.get(i, j), P_SYMDEV[i][j]); From 8c5a18c64123a3e172b4a99ff5c0a9e067e6bb46 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Sun, 12 May 2024 10:55:45 +1000 Subject: [PATCH 13/44] Impl serialize for Mandel --- russell_tensor/src/enums.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/russell_tensor/src/enums.rs b/russell_tensor/src/enums.rs index 6fd877cb..344aa7c7 100644 --- a/russell_tensor/src/enums.rs +++ b/russell_tensor/src/enums.rs @@ -1,5 +1,7 @@ +use serde::{Deserialize, Serialize}; + /// Specifies the Mandel representation -#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize)] pub enum Mandel { /// General representation of a 3×3 Tensor2 as a 9D vector /// From 2d15e6a28b1b27353ae3a46df8b2714d89249343 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Sun, 12 May 2024 11:46:15 +1000 Subject: [PATCH 14/44] Rename methods add to update --- russell_tensor/src/tensor2.rs | 10 +++++----- russell_tensor/src/tensor4.rs | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/russell_tensor/src/tensor2.rs b/russell_tensor/src/tensor2.rs index c2cbc04b..ecb9d702 100644 --- a/russell_tensor/src/tensor2.rs +++ b/russell_tensor/src/tensor2.rs @@ -802,7 +802,7 @@ impl Tensor2 { /// Ok(()) /// } /// ``` - pub fn add(&mut self, alpha: f64, other: &Tensor2) -> Result<(), StrError> { + pub fn update(&mut self, alpha: f64, other: &Tensor2) -> Result<(), StrError> { let dim = self.vec.dim(); if other.vec.dim() != dim { return Err("tensors are incompatible"); @@ -2343,7 +2343,7 @@ mod tests { let mut a = Tensor2::new(Mandel::General); let b = Tensor2::new(Mandel::Symmetric); assert_eq!(a.mirror(&b).err(), Some("tensors are incompatible")); - assert_eq!(a.add(1.0, &b).err(), Some("tensors are incompatible")); + assert_eq!(a.update(1.0, &b).err(), Some("tensors are incompatible")); } #[test] @@ -2363,7 +2363,7 @@ mod tests { ) .unwrap(); a.mirror(&b).unwrap(); - a.add(10.0, &c).unwrap(); + a.update(10.0, &c).unwrap(); let out = a.to_matrix(); assert_eq!( format!("{:.1}", out), @@ -2389,7 +2389,7 @@ mod tests { ) .unwrap(); a.mirror(&b).unwrap(); - a.add(10.0, &c).unwrap(); + a.update(10.0, &c).unwrap(); let out = a.to_matrix(); assert_eq!( format!("{:.1}", out), @@ -2415,7 +2415,7 @@ mod tests { ) .unwrap(); a.mirror(&b).unwrap(); - a.add(10.0, &c).unwrap(); + a.update(10.0, &c).unwrap(); let out = a.to_matrix(); assert_eq!( format!("{:.1}", out), diff --git a/russell_tensor/src/tensor4.rs b/russell_tensor/src/tensor4.rs index 7dd21082..bd7e92a9 100644 --- a/russell_tensor/src/tensor4.rs +++ b/russell_tensor/src/tensor4.rs @@ -625,7 +625,7 @@ impl Tensor4 { /// Ok(()) /// } /// ``` - pub fn add(&mut self, alpha: f64, other: &Tensor4) -> Result<(), StrError> { + pub fn update(&mut self, alpha: f64, other: &Tensor4) -> Result<(), StrError> { if other.mandel() != self.mandel() { return Err("tensors are incompatible"); } @@ -1378,7 +1378,7 @@ mod tests { fn update_works() { let mut dd = Tensor4::new(Mandel::Symmetric2D); let ee = Tensor4::from_array(&SamplesTensor4::SYM_2D_SAMPLE1, Mandel::Symmetric2D).unwrap(); - dd.add(2.0, &ee).unwrap(); + dd.update(2.0, &ee).unwrap(); for i in 0..3 { for j in 0..3 { for k in 0..3 { From b4b5437fba05305664628cbccc05d971e63e40fe Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Sun, 12 May 2024 11:48:24 +1000 Subject: [PATCH 15/44] Rename to_array and to_matrix to as_array and as_matrix in Tensor4 --- russell_tensor/src/tensor4.rs | 36 +++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/russell_tensor/src/tensor4.rs b/russell_tensor/src/tensor4.rs index bd7e92a9..d8d2f9c5 100644 --- a/russell_tensor/src/tensor4.rs +++ b/russell_tensor/src/tensor4.rs @@ -663,7 +663,7 @@ impl Tensor4 { /// Ok(()) /// } /// ``` - pub fn to_array(&self) -> Vec>>> { + pub fn as_array(&self) -> Vec>>> { let mut dd = vec![vec![vec![vec![0.0; 3]; 3]; 3]; 3]; let dim = self.mat.dims().0; if dim < 9 { @@ -725,7 +725,7 @@ impl Tensor4 { /// Ok(()) /// } /// ``` - pub fn to_matrix(&self) -> Matrix { + pub fn as_matrix(&self) -> Matrix { let mut res = Matrix::new(9, 9); for m in 0..9 { for n in 0..9 { @@ -1398,7 +1398,7 @@ mod tests { fn to_array_works() { // general let dd = Tensor4::from_array(&SamplesTensor4::SAMPLE1, Mandel::General).unwrap(); - let res = dd.to_array(); + let res = dd.as_array(); for i in 0..3 { for j in 0..3 { for k in 0..3 { @@ -1411,7 +1411,7 @@ mod tests { // symmetric 3D let dd = Tensor4::from_array(&SamplesTensor4::SYM_SAMPLE1, Mandel::Symmetric).unwrap(); - let res = dd.to_array(); + let res = dd.as_array(); for i in 0..3 { for j in 0..3 { for k in 0..3 { @@ -1424,7 +1424,7 @@ mod tests { // symmetric 2D let dd = Tensor4::from_array(&SamplesTensor4::SYM_2D_SAMPLE1, Mandel::Symmetric2D).unwrap(); - let res = dd.to_array(); + let res = dd.as_array(); for i in 0..3 { for j in 0..3 { for k in 0..3 { @@ -1440,7 +1440,7 @@ mod tests { fn to_matrix_works() { // general let dd = Tensor4::from_array(&SamplesTensor4::SAMPLE1, Mandel::General).unwrap(); - let mat = dd.to_matrix(); + let mat = dd.as_matrix(); for m in 0..9 { for n in 0..9 { approx_eq(mat.get(m, n), SamplesTensor4::SAMPLE1_STD_MATRIX[m][n], 1e-13); @@ -1449,7 +1449,7 @@ mod tests { // symmetric 3D let dd = Tensor4::from_array(&SamplesTensor4::SYM_SAMPLE1, Mandel::Symmetric).unwrap(); - let mat = dd.to_matrix(); + let mat = dd.as_matrix(); assert_eq!(mat.dims(), (9, 9)); for m in 0..9 { for n in 0..9 { @@ -1459,7 +1459,7 @@ mod tests { // symmetric 2D let dd = Tensor4::from_array(&SamplesTensor4::SYM_2D_SAMPLE1, Mandel::Symmetric2D).unwrap(); - let mat = dd.to_matrix(); + let mat = dd.as_matrix(); assert_eq!(mat.dims(), (9, 9)); for m in 0..9 { for n in 0..9 { @@ -1489,7 +1489,7 @@ mod tests { ], ]; let dd = Tensor4::from_array(data, Mandel::General).unwrap(); - let m1 = dd.to_matrix(); + let m1 = dd.as_matrix(); let correct = &[ [18.0, 10.0, 2.0, 16.0, 8.0, 14.0, 12.0, 4.0, 6.0], [90.0, 50.0, 10.0, 80.0, 40.0, 70.0, 60.0, 20.0, 30.0], @@ -1503,7 +1503,7 @@ mod tests { ]; mat_approx_eq(&m1, correct, 1e-13); let ee = Tensor4::from_matrix(correct, Mandel::General).unwrap(); - let m2 = ee.to_matrix(); + let m2 = ee.as_matrix(); mat_approx_eq(&m2, correct, 1e-13); // Symmetric 3D @@ -1525,7 +1525,7 @@ mod tests { ], ]; let dd = Tensor4::from_array(data, Mandel::Symmetric).unwrap(); - let m1 = dd.to_matrix(); + let m1 = dd.as_matrix(); let correct = &[ [6.0, 4.0, 2.0, 10.0, 8.0, 12.0, 10.0, 8.0, 12.0], [12.0, 8.0, 4.0, 20.0, 16.0, 24.0, 20.0, 16.0, 24.0], @@ -1539,7 +1539,7 @@ mod tests { ]; mat_approx_eq(&m1, correct, 1e-13); let ee = Tensor4::from_matrix(correct, Mandel::Symmetric).unwrap(); - let m2 = ee.to_matrix(); + let m2 = ee.as_matrix(); mat_approx_eq(&m2, correct, 1e-13); // Symmetric 2D @@ -1561,7 +1561,7 @@ mod tests { ], ]; let dd = Tensor4::from_array(data, Mandel::Symmetric2D).unwrap(); - let m1 = dd.to_matrix(); + let m1 = dd.as_matrix(); let correct = &[ [6.0, 4.0, 2.0, 8.0, 0.0, 0.0, 8.0, 0.0, 0.0], [12.0, 8.0, 4.0, 16.0, 0.0, 0.0, 16.0, 0.0, 0.0], @@ -1575,7 +1575,7 @@ mod tests { ]; mat_approx_eq(&m1, correct, 1e-13); let ee = Tensor4::from_matrix(correct, Mandel::Symmetric2D).unwrap(); - let m2 = ee.to_matrix(); + let m2 = ee.as_matrix(); mat_approx_eq(&m2, correct, 1e-13); } @@ -1595,7 +1595,7 @@ mod tests { fn sym_set_works() { let dd = generate_dd(); assert_eq!( - format!("{:.0}", dd.to_matrix()), + format!("{:.0}", dd.as_matrix()), "┌ ┐\n\ │ 1111 1122 1133 1112 1123 1113 1112 1123 1113 │\n\ │ 2211 2222 2233 2212 2223 2213 2212 2223 2213 │\n\ @@ -1646,7 +1646,7 @@ mod tests { let mut cloned = dd.clone(); cloned.mat.set(0, 0, 9999.0); assert_eq!( - format!("{:.0}", dd.to_matrix()), + format!("{:.0}", dd.as_matrix()), "┌ ┐\n\ │ 1111 1122 1133 1112 1123 1113 1112 1123 1113 │\n\ │ 2211 2222 2233 2212 2223 2213 2212 2223 2213 │\n\ @@ -1660,7 +1660,7 @@ mod tests { └ ┘" ); assert_eq!( - format!("{:.0}", cloned.to_matrix()), + format!("{:.0}", cloned.as_matrix()), "┌ ┐\n\ │ 9999 1122 1133 1112 1123 1113 1112 1123 1113 │\n\ │ 2211 2222 2233 2212 2223 2213 2212 2223 2213 │\n\ @@ -1679,7 +1679,7 @@ mod tests { // deserialize let from_json: Tensor4 = serde_json::from_str(&json).unwrap(); assert_eq!( - format!("{:.0}", from_json.to_matrix()), + format!("{:.0}", from_json.as_matrix()), "┌ ┐\n\ │ 1111 1122 1133 1112 1123 1113 1112 1123 1113 │\n\ │ 2211 2222 2233 2212 2223 2213 2212 2223 2213 │\n\ From c2443821095389b31497d70a82ee2cde380cc9a1 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Sun, 12 May 2024 12:04:19 +1000 Subject: [PATCH 16/44] Improve as_array and as_matrix functions of Tensor4 --- russell_tensor/src/tensor4.rs | 110 ++++++++++++++++++++++++++++++---- 1 file changed, 100 insertions(+), 10 deletions(-) diff --git a/russell_tensor/src/tensor4.rs b/russell_tensor/src/tensor4.rs index d8d2f9c5..7d2ae574 100644 --- a/russell_tensor/src/tensor4.rs +++ b/russell_tensor/src/tensor4.rs @@ -633,7 +633,7 @@ impl Tensor4 { Ok(()) } - /// Returns a nested array (standard components; not Mandel) representing this tensor + /// Returns a 3x3x3x3 array with the standard components /// /// # Examples /// @@ -651,7 +651,7 @@ impl Tensor4 { /// } /// /// let dd = Tensor4::from_matrix(&inp, Mandel::General)?; - /// let arr = dd.to_array(); + /// let arr = dd.as_array(); /// /// for m in 0..9 { /// for n in 0..9 { @@ -665,6 +665,46 @@ impl Tensor4 { /// ``` pub fn as_array(&self) -> Vec>>> { let mut dd = vec![vec![vec![vec![0.0; 3]; 3]; 3]; 3]; + self.to_array(&mut dd); + dd + } + + /// Converts this tensor to a 3x3x3x3 array with the standard components + /// + /// # Panics + /// + /// A panic will occur if the array is not 3x3x3x3, i.e., `vec![vec![vec![vec![0.0; 3]; 3]; 3]; 3]` + /// + /// # Examples + /// + /// ``` + /// use russell_lab::approx_eq; + /// use russell_tensor::{Mandel, MN_TO_IJKL, Tensor4, StrError}; + /// + /// fn main() -> Result<(), StrError> { + /// let mut inp = [[0.0; 9]; 9]; + /// for m in 0..9 { + /// for n in 0..9 { + /// let (i, j, k, l) = MN_TO_IJKL[m][n]; + /// inp[m][n] = (1000 * (i + 1) + 100 * (j + 1) + 10 * (k + 1) + (l + 1)) as f64; + /// } + /// } + /// + /// let dd = Tensor4::from_matrix(&inp, Mandel::General)?; + /// let mut arr = vec![vec![vec![vec![0.0; 3]; 3]; 3]; 3]; + /// dd.to_array(&mut arr); + /// + /// for m in 0..9 { + /// for n in 0..9 { + /// let (i, j, k, l) = MN_TO_IJKL[m][n]; + /// let val = (1000 * (i + 1) + 100 * (j + 1) + 10 * (k + 1) + (l + 1)) as f64; + /// approx_eq(arr[i][j][k][l], val, 1e-12); + /// } + /// } + /// Ok(()) + /// } + /// ``` + pub fn to_array(&self, dd: &mut Vec>>>) { let dim = self.mat.dims().0; if dim < 9 { for m in 0..dim { @@ -689,10 +729,11 @@ impl Tensor4 { } } } - dd } - /// Returns a matrix (standard components; not Mandel) representing this tensor + /// Returns a 9x9 matrix with the standard components + /// + /// **Note:** The matrix will have the standard components (not Mandel) and 9x9 dimension. /// /// # Examples /// @@ -709,7 +750,7 @@ impl Tensor4 { /// } /// let dd = Tensor4::from_matrix(&inp, Mandel::General)?; /// assert_eq!( - /// format!("{:.0}", dd.to_matrix()), + /// format!("{:.0}", dd.as_matrix()), /// "┌ ┐\n\ /// │ 1111 1122 1133 1112 1123 1113 1121 1132 1131 │\n\ /// │ 2211 2222 2233 2212 2223 2213 2221 2232 2231 │\n\ @@ -726,14 +767,63 @@ impl Tensor4 { /// } /// ``` pub fn as_matrix(&self) -> Matrix { - let mut res = Matrix::new(9, 9); + let mut mat = Matrix::new(9, 9); + self.to_matrix(&mut mat); + mat + } + + /// Converts this tensor to a 9x9 matrix with the standard components + /// + /// # Input + /// + /// * `mat` -- The resulting 9x9 matrix. + /// + /// # Panics + /// + /// A panic will occur if the matrix is not 9x9 + /// + /// # Examples + /// + /// ``` + /// use russell_lab::Matrix; + /// use russell_tensor::{Mandel, MN_TO_IJKL, Tensor4, StrError}; + /// + /// fn main() -> Result<(), StrError> { + /// let mut inp = [[0.0; 9]; 9]; + /// for m in 0..9 { + /// for n in 0..9 { + /// let (i, j, k, l) = MN_TO_IJKL[m][n]; + /// inp[m][n] = (1000 * (i + 1) + 100 * (j + 1) + 10 * (k + 1) + (l + 1)) as f64; + /// } + /// } + /// let dd = Tensor4::from_matrix(&inp, Mandel::General)?; + /// let mut mat = Matrix::new(9, 9); + /// dd.to_matrix(&mut mat); + /// assert_eq!( + /// format!("{:.0}", mat), + /// "┌ ┐\n\ + /// │ 1111 1122 1133 1112 1123 1113 1121 1132 1131 │\n\ + /// │ 2211 2222 2233 2212 2223 2213 2221 2232 2231 │\n\ + /// │ 3311 3322 3333 3312 3323 3313 3321 3332 3331 │\n\ + /// │ 1211 1222 1233 1212 1223 1213 1221 1232 1231 │\n\ + /// │ 2311 2322 2333 2312 2323 2313 2321 2332 2331 │\n\ + /// │ 1311 1322 1333 1312 1323 1313 1321 1332 1331 │\n\ + /// │ 2111 2122 2133 2112 2123 2113 2121 2132 2131 │\n\ + /// │ 3211 3222 3233 3212 3223 3213 3221 3232 3231 │\n\ + /// │ 3111 3122 3133 3112 3123 3113 3121 3132 3131 │\n\ + /// └ ┘" + /// ); + /// Ok(()) + /// } + /// ``` + pub fn to_matrix(&self, mat: &mut Matrix) { + assert_eq!(mat.dims(), (9, 9)); for m in 0..9 { for n in 0..9 { let (i, j, k, l) = MN_TO_IJKL[m][n]; - res.set(m, n, self.get(i, j, k, l)); + mat.set(m, n, self.get(i, j, k, l)); } } - res } /// Sets the (i,j,k,l) component of a minor-symmetric Tensor4 @@ -1395,7 +1485,7 @@ mod tests { } #[test] - fn to_array_works() { + fn as_array_and_to_array_work() { // general let dd = Tensor4::from_array(&SamplesTensor4::SAMPLE1, Mandel::General).unwrap(); let res = dd.as_array(); @@ -1437,7 +1527,7 @@ mod tests { } #[test] - fn to_matrix_works() { + fn as_matrix_and_to_matrix_work() { // general let dd = Tensor4::from_array(&SamplesTensor4::SAMPLE1, Mandel::General).unwrap(); let mat = dd.as_matrix(); From b2209ad6d6d930645f344f40e56d7250c81d36cc Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Sun, 12 May 2024 13:04:11 +1000 Subject: [PATCH 17/44] [wip] Use assert in Tensor2 to compare Mandel. Improve doc comments about panics --- russell_tensor/src/tensor2.rs | 464 ++++++++++++++++++++++------------ 1 file changed, 303 insertions(+), 161 deletions(-) diff --git a/russell_tensor/src/tensor2.rs b/russell_tensor/src/tensor2.rs index ecb9d702..dfb7911a 100644 --- a/russell_tensor/src/tensor2.rs +++ b/russell_tensor/src/tensor2.rs @@ -56,7 +56,7 @@ use serde::{Deserialize, Serialize}; /// * You may perform operations on `vec` directly because it is isomorphic with the tensor itself /// * For example, the norm of the tensor equals `vec.norm()` /// * However, you must be careful when setting a single component of `vec` directly -/// because you may "break" the Mandel representation. +/// because the Mandel representation may become inconsistent (with incorrect values). #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Tensor2 { /// Holds the components in Mandel basis as a vector. @@ -274,11 +274,11 @@ impl Tensor2 { Ok(()) } - /// Creates a new Tensor2 constructed from a matrix + /// Creates a new Tensor2 constructed from a 3x3 matrix /// /// # Input /// - /// * `tt` -- the standard (not Mandel) Tij components given with respect to an orthonormal Cartesian basis + /// * `tt` -- the standard (not Mandel) Tij components with respect to an orthonormal Cartesian basis /// * `mandel` -- the [Mandel] representation /// /// # Notes @@ -287,6 +287,10 @@ impl Tensor2 { /// * If symmetric, the off-diagonal components must equal each other /// * If 2D, `data[1][2]` and `data[0][2]` must be equal to zero /// + /// # Panics + /// + /// A panic will occur if the matrix is not 3x3. + /// /// # Examples /// /// ``` @@ -397,7 +401,18 @@ impl Tensor2 { res } - /// Returns the (i,j) component (standard; not Mandel) + /// Returns the standard (i,j) component + /// + /// **Note:** Returns the standard component; not Mandel. + /// + /// # Input + /// + /// * `i` -- the first index in `(0, 1, 2)` + /// * `j` -- the first index in `(0, 1, 2)` + /// + /// # Panics + /// + /// A panic will occur if the indices are out of range. /// /// # Examples /// @@ -451,7 +466,9 @@ impl Tensor2 { } } - /// Returns a matrix (standard components; not Mandel) representing this tensor + /// Returns a 3x3 matrix with the standard components + /// + /// **Note:** The matrix will have the standard components (not Mandel) and 3x3 dimension. /// /// # Examples /// @@ -465,7 +482,7 @@ impl Tensor2 { /// [0.0, 0.0, 1.0], /// ], Mandel::Symmetric2D)?; /// assert_eq!( - /// format!("{:.1}", a.to_matrix()), + /// format!("{:.1}", a.as_matrix()), /// "┌ ┐\n\ /// │ 1.0 1.0 0.0 │\n\ /// │ 1.0 -1.0 0.0 │\n\ @@ -475,34 +492,77 @@ impl Tensor2 { /// Ok(()) /// } /// ``` - pub fn to_matrix(&self) -> Matrix { - let mut tt = Matrix::new(3, 3); + pub fn as_matrix(&self) -> Matrix { + let mut mat = Matrix::new(3, 3); + self.to_matrix(&mut mat); + mat + } + + /// Converts this tensor to a 3x3 matrix with the standard components + /// + /// # Input + /// + /// * `mat` -- the resulting 3x3 matrix + /// + /// # Panics + /// + /// A panic will occur if the matrix is not 3x3 + /// + /// # Examples + /// + /// ``` + /// use russell_lab::Matrix; + /// use russell_tensor::{Mandel, Tensor2, StrError}; + /// + /// fn main() -> Result<(), StrError> { + /// let a = Tensor2::from_matrix(&[ + /// [1.0, 1.0, 0.0], + /// [1.0, -1.0, 0.0], + /// [0.0, 0.0, 1.0], + /// ], Mandel::Symmetric2D)?; + /// let mut mat = Matrix::new(3, 3); + /// a.to_matrix&mut mat); + /// assert_eq!( + /// format!("{:.1}", mat), + /// "┌ ┐\n\ + /// │ 1.0 1.0 0.0 │\n\ + /// │ 1.0 -1.0 0.0 │\n\ + /// │ 0.0 0.0 1.0 │\n\ + /// └ ┘" + /// ); + /// Ok(()) + /// } + /// ``` + pub fn to_matrix(&self, mat: &mut Matrix) { + assert_eq!(mat.dims(), (3, 3)); let dim = self.vec.dim(); if dim < 9 { for m in 0..dim { let (i, j) = M_TO_IJ[m]; - tt.set(i, j, self.get(i, j)); + mat.set(i, j, self.get(i, j)); if i != j { - tt.set(j, i, tt.get(i, j)); + mat.set(j, i, mat.get(i, j)); } } } else { for i in 0..3 { for j in 0..3 { - tt.set(i, j, self.get(i, j)); + mat.set(i, j, self.get(i, j)); } } } - tt } - /// Returns a 2x2 matrix (standard components; not Mandel) representing this tensor (2D) + /// Returns a 2x2 matrix with the standard components + /// + /// # Notes /// - /// This function returns the third diagonal component T22 and the 2x2 matrix + /// 1. The matrix will have the standard components (not Mandel) and 2x2 dimension + /// 2. This function returns the third diagonal component T22 and the 2x2 matrix /// /// # Panics /// - /// This function works only if the Tensor is Symmetric2D + /// A panic will occur if the tensor is not [Mandel::Symmetric2D] /// /// # Examples /// @@ -515,7 +575,7 @@ impl Tensor2 { /// [2.0, 3.0, 0.0], /// [0.0, 0.0, 4.0], /// ], Mandel::Symmetric2D)?; - /// let (t22, res) = tt.to_matrix_2d(); + /// let (t22, res) = tt.as_matrix_2d(); /// assert_eq!(t22, 4.0); /// assert_eq!( /// format!("{:.1}", res), @@ -527,8 +587,8 @@ impl Tensor2 { /// Ok(()) /// } /// ``` - pub fn to_matrix_2d(&self) -> (f64, Matrix) { - assert_eq!(self.mandel(), Mandel::Symmetric2D); + pub fn as_matrix_2d(&self) -> (f64, Matrix) { + assert_eq!(self.mandel, Mandel::Symmetric2D); let mut tt = Matrix::new(2, 2); tt.set(0, 0, self.get(0, 0)); tt.set(0, 1, self.get(0, 1)); @@ -537,7 +597,11 @@ impl Tensor2 { (self.get(2, 2), tt) } - /// Returns a General Tensor2 regardless of this tensor's Mandel type + /// Returns a general Tensor2 regardless of Mandel type + /// + /// # Output + /// + /// Returns a [Mandel::General] tensor. /// /// # Examples /// @@ -560,7 +624,7 @@ impl Tensor2 { /// └ ┘" /// ); /// - /// let tt_gen = tt.to_general(); + /// let tt_gen = tt.as_general(); /// assert_eq!( /// format!("{:.2}", tt_gen.vec), /// "┌ ┐\n\ @@ -578,7 +642,7 @@ impl Tensor2 { /// Ok(()) /// } /// ``` - pub fn to_general(&self) -> Tensor2 { + pub fn as_general(&self) -> Tensor2 { let mut res = Tensor2::new(Mandel::General); res.vec[0] = self.vec[0]; res.vec[1] = self.vec[1]; @@ -608,12 +672,16 @@ impl Tensor2 { /// σᵢⱼ = value /// ``` /// - /// **Note:** Only the diagonal and upper-diagonal components need to be set. + /// # Notes + /// + /// 1. Only the diagonal and upper-diagonal components need to be set. + /// 2. The tensor must be symmetric and (i,j) must correspond to the possible + /// combination due to the space dimension, otherwise a panic may occur. /// /// # Panics /// - /// The tensor must be symmetric and (i,j) must correspond to the possible - /// combination due to the space dimension, otherwise a panic may occur. + /// 1. A panic will occur if the tensor is [Mandel::General] + /// 2. A panic will occur if the indices are out of range /// /// # Examples /// @@ -653,7 +721,7 @@ impl Tensor2 { /// } /// ``` pub fn sym_set(&mut self, i: usize, j: usize, value: f64) { - assert!(self.mandel() != Mandel::General); + assert!(self.mandel != Mandel::General); let m = IJ_TO_M_SYM[i][j]; if i == j { self.vec[m] = value; @@ -665,17 +733,20 @@ impl Tensor2 { /// Updates the (i,j) component of a symmetric Tensor2 /// /// ```text - /// σᵢⱼ += α·value + /// σᵢⱼ += α value /// ``` /// - /// **Note:** Only the diagonal and upper-diagonal components **must** be set. + /// # Notes /// - /// # Panics + /// 1. Only the diagonal and upper-diagonal components need to be handled. + /// 2. The tensor must be symmetric and (i,j) must correspond to the possible + /// combination due to the space dimension, otherwise a panic may occur. /// - /// The tensor must be Symmetric or Symmetric2D and (i,j) must correspond to the possible - /// combination due to the space dimension, otherwise a panic may occur. + /// # Panics /// - /// This function will panic also if i > j (lower-diagonal) + /// 1. A panic will occur if the indices are out of range + /// 2. A panic will occur if the tensor is [Mandel::General] + /// 3. A panic will occur if `i > j` (lower-diagonal) /// /// # Examples /// @@ -703,7 +774,7 @@ impl Tensor2 { /// } /// ``` pub fn sym_add(&mut self, i: usize, j: usize, alpha: f64, value: f64) { - assert!(self.mandel() != Mandel::General); + assert!(self.mandel != Mandel::General); assert!(i <= j); let m = IJ_TO_M_SYM[i][j]; if i == j { @@ -715,6 +786,10 @@ impl Tensor2 { /// Sets this tensor equal to another one /// + /// # Panics + /// + /// A panic will occur if the tensors have different [Mandel] + /// /// # Examples /// /// ``` @@ -745,11 +820,9 @@ impl Tensor2 { /// Ok(()) /// } /// ``` - pub fn mirror(&mut self, other: &Tensor2) -> Result<(), StrError> { + pub fn mirror(&mut self, other: &Tensor2) { + assert_eq!(self.mandel, other.mandel); let dim = self.vec.dim(); - if other.vec.dim() != dim { - return Err("tensors are incompatible"); - } self.vec[0] = other.vec[0]; self.vec[1] = other.vec[1]; self.vec[2] = other.vec[2]; @@ -763,7 +836,6 @@ impl Tensor2 { self.vec[7] = other.vec[7]; self.vec[8] = other.vec[8]; } - Ok(()) } /// Adds another tensor to this one @@ -772,6 +844,10 @@ impl Tensor2 { /// self += α other /// ``` /// + /// # Panics + /// + /// A panic will occur if the tensors have different [Mandel] + /// /// # Examples /// /// ``` @@ -802,11 +878,9 @@ impl Tensor2 { /// Ok(()) /// } /// ``` - pub fn update(&mut self, alpha: f64, other: &Tensor2) -> Result<(), StrError> { + pub fn update(&mut self, alpha: f64, other: &Tensor2) { + assert_eq!(self.mandel, other.mandel); let dim = self.vec.dim(); - if other.vec.dim() != dim { - return Err("tensors are incompatible"); - } self.vec[0] += alpha * other.vec[0]; self.vec[1] += alpha * other.vec[1]; self.vec[2] += alpha * other.vec[2]; @@ -820,7 +894,6 @@ impl Tensor2 { self.vec[7] += alpha * other.vec[7]; self.vec[8] += alpha * other.vec[8]; } - Ok(()) } /// Calculates the determinant @@ -873,9 +946,13 @@ impl Tensor2 { /// [Aᵀ]ᵢⱼ = [A]ⱼᵢ /// ``` /// - /// ## Input/Output + /// ## Output + /// + /// * `at` -- a Tensor2 to hold the transpose tensor; with the same [Mandel] as this tensor + /// + /// # Panics /// - /// * `ai` -- a Tensor2 with matching dimensions to hold the transpose tensor + /// A panic will occur if `at` has a different [Mandel]. /// /// # Examples /// @@ -902,11 +979,9 @@ impl Tensor2 { /// Ok(()) /// } /// ``` - pub fn transpose(&self, at: &mut Tensor2) -> Result<(), StrError> { + pub fn transpose(&self, at: &mut Tensor2) { + assert_eq!(at.mandel, self.mandel); let dim = self.vec.dim(); - if at.vec.dim() != dim { - return Err("tensors are incompatible"); - } at.vec[0] = self.vec[0]; at.vec[1] = self.vec[1]; at.vec[2] = self.vec[2]; @@ -920,7 +995,6 @@ impl Tensor2 { at.vec[7] = -self.vec[7]; at.vec[8] = -self.vec[8]; } - Ok(()) } /// Calculates the inverse tensor @@ -931,9 +1005,9 @@ impl Tensor2 { /// A · A⁻¹ = I /// ``` /// - /// ## Input/Output + /// ## Output /// - /// * `ai` -- a Tensor2 with matching dimensions to hold the inverse tensor + /// * `ai` -- a Tensor2 to hold the inverse tensor; with the same [Mandel] as this tensor /// * `tolerance` -- a tolerance for the determinant such that the inverse is computed only if |det| > tolerance /// /// ## Output @@ -941,6 +1015,10 @@ impl Tensor2 { /// * If the determinant is zero, the inverse is not computed and returns `None` /// * Otherwise, the inverse is computed and returns the determinant /// + /// # Panics + /// + /// A panic will occur if `ai` has a different [Mandel]. + /// /// # Examples /// /// ``` @@ -972,11 +1050,9 @@ impl Tensor2 { /// Ok(()) /// } /// ``` - pub fn inverse(&self, ai: &mut Tensor2, tolerance: f64) -> Result, StrError> { + pub fn inverse(&self, ai: &mut Tensor2, tolerance: f64) -> Option { + assert_eq!(ai.mandel, self.mandel); let dim = self.vec.dim(); - if ai.vec.dim() != dim { - return Err("tensors are incompatible"); - } let a = &self.vec; match dim { 4 => { @@ -986,7 +1062,7 @@ impl Tensor2 { ai.vec[1] = (a[0] * a[2]) / det; ai.vec[2] = (a[0] * a[1] - a[3] * a[3] / 2.0) / det; ai.vec[3] = -((a[2] * a[3]) / det); - return Ok(Some(det)); + return Some(det); } } 6 => { @@ -1000,7 +1076,7 @@ impl Tensor2 { ai.vec[3] = (-2.0 * a[2] * a[3] + SQRT_2 * a[4] * a[5]) / (2.0 * det); ai.vec[4] = (-2.0 * a[0] * a[4] + SQRT_2 * a[3] * a[5]) / (2.0 * det); ai.vec[5] = (SQRT_2 * a[3] * a[4] - 2.0 * a[1] * a[5]) / (2.0 * det); - return Ok(Some(det)); + return Some(det); } } _ => { @@ -1023,11 +1099,11 @@ impl Tensor2 { ai.vec[6] = -((SQRT_2 * a[2] * a[6] + a[5] * a[7] - a[4] * a[8]) / (SQRT_2 * det)); ai.vec[7] = -((a[5] * a[6] + SQRT_2 * a[0] * a[7] - a[3] * a[8]) / (SQRT_2 * det)); ai.vec[8] = (a[4] * a[6] + a[3] * a[7] - SQRT_2 * a[1] * a[8]) / (SQRT_2 * det); - return Ok(Some(det)); + return Some(det); } } } - Ok(None) + None } /// Calculates the squared tensor @@ -1036,9 +1112,13 @@ impl Tensor2 { /// A² = A · A /// ``` /// - /// ## Input/Output + /// ## Output + /// + /// * `a2` -- a Tensor2 to hold the squared tensor; with the same [Mandel] as this tensor + /// + /// # Panics /// - /// * `a2` -- a Tensor2 with matching dimensions to hold the squared tensor + /// A panic will occur if `a2` has a different [Mandel]. /// /// # Examples /// @@ -1066,11 +1146,9 @@ impl Tensor2 { /// Ok(()) /// } /// ``` - pub fn squared(&self, a2: &mut Tensor2) -> Result<(), StrError> { + pub fn squared(&self, a2: &mut Tensor2) { + assert_eq!(a2.mandel, self.mandel); let dim = self.vec.dim(); - if a2.vec.dim() != dim { - return Err("tensors are incompatible"); - } let a = &self.vec; match dim { 4 => { @@ -1132,7 +1210,6 @@ impl Tensor2 { / SQRT_2; } } - Ok(()) } /// Calculates the trace @@ -1206,6 +1283,14 @@ impl Tensor2 { /// dev(σ) = σ - ⅓ tr(σ) I /// ``` /// + /// ## Output + /// + /// * `dev` -- a Tensor2 to hold the deviator tensor; with the same [Mandel] as this tensor + /// + /// # Panics + /// + /// A panic will occur if `dev` has a different [Mandel]. + /// /// # Examples /// /// ``` @@ -1234,11 +1319,9 @@ impl Tensor2 { /// Ok(()) /// } /// ``` - pub fn deviator(&self, dev: &mut Tensor2) -> Result<(), StrError> { + pub fn deviator(&self, dev: &mut Tensor2) { + assert_eq!(dev.mandel, self.mandel); let dim = self.vec.dim(); - if dev.vec.dim() != dim { - return Err("tensors are incompatible"); - } let m = (self.vec[0] + self.vec[1] + self.vec[2]) / 3.0; dev.vec[0] = self.vec[0] - m; dev.vec[1] = self.vec[1] - m; @@ -1253,7 +1336,6 @@ impl Tensor2 { dev.vec[7] = self.vec[7]; dev.vec[8] = self.vec[8]; } - Ok(()) } /// Calculates the norm of the deviator tensor @@ -2018,6 +2100,19 @@ mod tests { assert_eq!(ii.vec.as_data(), &IDENTITY2[0..4]); } + #[test] + #[should_panic(expected = "the len is 3 but the index is 3")] + fn get_panics_on_incorrect_input() { + #[rustfmt::skip] + let comps_std = &[ + [1.0, 2.0, 3.0], + [4.0, 5.0, 6.0], + [7.0, 8.0, 9.0], + ]; + let tt = Tensor2::from_matrix(comps_std, Mandel::General).unwrap(); + tt.get(3, 3); + } + #[test] fn get_works() { // general @@ -2064,7 +2159,21 @@ mod tests { } #[test] - fn to_matrix_works() { + #[should_panic] + fn to_matrix_panics_on_incorrect_input() { + #[rustfmt::skip] + let comps_std = &[ + [1.0, 2.0, 3.0], + [4.0, 5.0, 6.0], + [7.0, 8.0, 9.0], + ]; + let tt = Tensor2::from_matrix(comps_std, Mandel::General).unwrap(); + let mut mat = Matrix::new(2, 2); + tt.to_matrix(&mut mat); + } + + #[test] + fn as_matrix_and_to_matrix_work() { // general #[rustfmt::skip] let comps_std = &[ @@ -2073,7 +2182,7 @@ mod tests { [7.0, 8.0, 9.0], ]; let tt = Tensor2::from_matrix(comps_std, Mandel::General).unwrap(); - let res = tt.to_matrix(); + let res = tt.as_matrix(); for i in 0..3 { for j in 0..3 { approx_eq(res.get(i, j), comps_std[i][j], 1e-14); @@ -2088,7 +2197,7 @@ mod tests { [6.0, 5.0, 3.0], ]; let tt = Tensor2::from_matrix(comps_std, Mandel::Symmetric).unwrap(); - let res = tt.to_matrix(); + let res = tt.as_matrix(); for i in 0..3 { for j in 0..3 { approx_eq(res.get(i, j), comps_std[i][j], 1e-14); @@ -2103,7 +2212,7 @@ mod tests { [0.0, 0.0, 3.0], ]; let tt = Tensor2::from_matrix(comps_std, Mandel::Symmetric2D).unwrap(); - let res = tt.to_matrix(); + let res = tt.as_matrix(); for i in 0..3 { for j in 0..3 { approx_eq(res.get(i, j), comps_std[i][j], 1e-14); @@ -2113,7 +2222,7 @@ mod tests { #[test] #[should_panic] - fn to_matrix_2d_panics_on_3d() { + fn as_matrix_2d_panics_on_3d() { #[rustfmt::skip] let comps_std = &[ [1.0, 4.0, 0.0], @@ -2121,11 +2230,11 @@ mod tests { [0.0, 0.0, 3.0], ]; let tt = Tensor2::from_matrix(comps_std, Mandel::Symmetric).unwrap(); - tt.to_matrix_2d(); + tt.as_matrix_2d(); } #[test] - fn to_matrix_2d_works() { + fn as_matrix_2d_works() { #[rustfmt::skip] let comps_std = &[ [1.0, 4.0, 0.0], @@ -2133,7 +2242,7 @@ mod tests { [0.0, 0.0, 3.0], ]; let tt = Tensor2::from_matrix(comps_std, Mandel::Symmetric2D).unwrap(); - let (t22, res) = tt.to_matrix_2d(); + let (t22, res) = tt.as_matrix_2d(); assert_eq!(t22, 3.0); assert_eq!( format!("{:.1}", res), @@ -2150,7 +2259,7 @@ mod tests { [0.0, 0.0, 4.0], ]; let tt = Tensor2::from_matrix(data, Mandel::Symmetric2D).unwrap(); - let (t22, a) = tt.to_matrix_2d(); + let (t22, a) = tt.as_matrix_2d(); assert_eq!(t22, 4.0); assert_eq!( format!("{:.1}", a), @@ -2162,15 +2271,36 @@ mod tests { } #[test] - fn to_general_works() { + fn as_general_works() { let tt = Tensor2::from_matrix( &[[1.0, 2.0 / SQRT_2, 0.0], [2.0 / SQRT_2, 3.0, 0.0], [0.0, 0.0, 4.0]], Mandel::Symmetric2D, ) .unwrap(); - let tt_gen = tt.to_general(); - println!("{:.2}", tt.vec); - println!("{:.2}", tt_gen.vec); + let tt_gen = tt.as_general(); + assert_eq!( + format!("{:.2}", tt.vec), + "┌ ┐\n\ + │ 1.00 │\n\ + │ 3.00 │\n\ + │ 4.00 │\n\ + │ 2.00 │\n\ + └ ┘" + ); + assert_eq!( + format!("{:.2}", tt_gen.vec), + "┌ ┐\n\ + │ 1.00 │\n\ + │ 3.00 │\n\ + │ 4.00 │\n\ + │ 2.00 │\n\ + │ 0.00 │\n\ + │ 0.00 │\n\ + │ 0.00 │\n\ + │ 0.00 │\n\ + │ 0.00 │\n\ + └ ┘" + ); // general #[rustfmt::skip] @@ -2180,7 +2310,7 @@ mod tests { [7.0, 8.0, 9.0], ]; let tt = Tensor2::from_matrix(comps_std, Mandel::General).unwrap(); - let res = tt.to_general(); + let res = tt.as_general(); assert_eq!(res.vec.dim(), 9); for i in 0..3 { for j in 0..3 { @@ -2196,7 +2326,7 @@ mod tests { [6.0, 5.0, 3.0], ]; let tt = Tensor2::from_matrix(comps_std, Mandel::Symmetric).unwrap(); - let res = tt.to_general(); + let res = tt.as_general(); assert_eq!(res.vec.dim(), 9); for i in 0..3 { for j in 0..3 { @@ -2212,7 +2342,7 @@ mod tests { [0.0, 0.0, 3.0], ]; let tt = Tensor2::from_matrix(comps_std, Mandel::Symmetric2D).unwrap(); - let res = tt.to_general(); + let res = tt.as_general(); assert_eq!(res.vec.dim(), 9); for i in 0..3 { for j in 0..3 { @@ -2221,6 +2351,20 @@ mod tests { } } + #[test] + #[should_panic(expected = "self.mandel != Mandel::General")] + fn sym_set_panics_on_non_sym() { + let mut a = Tensor2::new(Mandel::General); + a.sym_set(3, 3, 3.0); + } + + #[test] + #[should_panic(expected = "the len is 3 but the index is 3")] + fn sym_set_panics_on_incorrect_indices() { + let mut a = Tensor2::new(Mandel::Symmetric); + a.sym_set(3, 3, 3.0); + } + #[test] fn sym_set_works() { let mut a = Tensor2::new(Mandel::Symmetric); @@ -2230,7 +2374,7 @@ mod tests { a.sym_set(0, 1, 4.0); a.sym_set(1, 0, 4.0); a.sym_set(2, 0, 5.0); - let out = a.to_matrix(); + let out = a.as_matrix(); assert_eq!( format!("{:.1}", out), "┌ ┐\n\ @@ -2255,31 +2399,24 @@ mod tests { } #[test] - #[should_panic] + #[should_panic(expected = "self.mandel != Mandel::General")] fn sym_add_panics_on_non_sym() { - // symmetric 2D - #[rustfmt::skip] - let comps_std = &[ - [1.0, 4.0, 0.0], - [4.0, 2.0, 0.0], - [0.0, 0.0, 3.0], - ]; - let mut a = Tensor2::from_matrix(comps_std, Mandel::General).unwrap(); + let mut a = Tensor2::new(Mandel::General); a.sym_add(0, 0, 1.0, 1.0); } #[test] - #[should_panic] - fn sym_add_panics_on_i_greater_than_j() { - // symmetric 2D - #[rustfmt::skip] - let comps_std = &[ - [1.0, 4.0, 0.0], - [4.0, 2.0, 0.0], - [0.0, 0.0, 3.0], - ]; - let mut a = Tensor2::from_matrix(comps_std, Mandel::Symmetric2D).unwrap(); - a.sym_add(1, 0, 1.0, 1.0); + #[should_panic(expected = "the len is 3 but the index is 3")] + fn sym_add_panics_on_incorrect_indices() { + let mut a = Tensor2::new(Mandel::Symmetric); + a.sym_add(3, 3, 5.0, 6.0); + } + + #[test] + #[should_panic(expected = "i <= j")] + fn sym_add_panics_on_lower_diagonal() { + let mut a = Tensor2::new(Mandel::Symmetric2D); + a.sym_add(1, 0, 5.0, 6.0); } #[test] @@ -2296,7 +2433,7 @@ mod tests { a.sym_add(1, 1, 10.0, 10.0); a.sym_add(2, 2, 10.0, 10.0); a.sym_add(0, 1, 10.0, 10.0); // must not do (1,0) - let out = a.to_matrix(); + let out = a.as_matrix(); assert_eq!( format!("{:.1}", out), "┌ ┐\n\ @@ -2320,7 +2457,7 @@ mod tests { a.sym_add(0, 1, 10.0, 10.0); // must nod do (1,0) a.sym_add(0, 2, 10.0, 10.0); // must not do (2,0) a.sym_add(1, 2, 10.0, 10.0); // must not do (2,1) - let out = a.to_matrix(); + let out = a.as_matrix(); assert_eq!( format!("{:.1}", out), "┌ ┐\n\ @@ -2333,21 +2470,22 @@ mod tests { #[test] #[should_panic] - fn sym_add_panics_on_lower_diagonal() { - let mut a = Tensor2::new(Mandel::Symmetric2D); - a.sym_add(1, 0, 1.0, 0.0); + fn mirror_panics_panics_on_incorrect_input() { + let mut a = Tensor2::new(Mandel::General); + let b = Tensor2::new(Mandel::Symmetric); + a.mirror(&b); } #[test] - fn mirror_and_add_capture_errors() { + #[should_panic] + fn update_panics_on_incorrect_input() { let mut a = Tensor2::new(Mandel::General); let b = Tensor2::new(Mandel::Symmetric); - assert_eq!(a.mirror(&b).err(), Some("tensors are incompatible")); - assert_eq!(a.update(1.0, &b).err(), Some("tensors are incompatible")); + a.update(1.0, &b); } #[test] - fn mirror_and_add_work() { + fn mirror_and_update_work() { // general let mut a = Tensor2::new(Mandel::General); #[rustfmt::skip] @@ -2362,9 +2500,9 @@ mod tests { Mandel::General, ) .unwrap(); - a.mirror(&b).unwrap(); - a.update(10.0, &c).unwrap(); - let out = a.to_matrix(); + a.mirror(&b); + a.update(10.0, &c); + let out = a.as_matrix(); assert_eq!( format!("{:.1}", out), "┌ ┐\n\ @@ -2388,9 +2526,9 @@ mod tests { Mandel::Symmetric, ) .unwrap(); - a.mirror(&b).unwrap(); - a.update(10.0, &c).unwrap(); - let out = a.to_matrix(); + a.mirror(&b); + a.update(10.0, &c); + let out = a.as_matrix(); assert_eq!( format!("{:.1}", out), "┌ ┐\n\ @@ -2414,9 +2552,9 @@ mod tests { Mandel::Symmetric2D, ) .unwrap(); - a.mirror(&b).unwrap(); - a.update(10.0, &c).unwrap(); - let out = a.to_matrix(); + a.mirror(&b); + a.update(10.0, &c); + let out = a.as_matrix(); assert_eq!( format!("{:.1}", out), "┌ ┐\n\ @@ -2440,7 +2578,7 @@ mod tests { let mut cloned = tt.clone(); cloned.vec[0] = -1.0; assert_eq!( - format!("{:.1}", tt.to_matrix()), + format!("{:.1}", tt.as_matrix()), "┌ ┐\n\ │ 1.0 2.0 3.0 │\n\ │ 4.0 5.0 6.0 │\n\ @@ -2448,7 +2586,7 @@ mod tests { └ ┘" ); assert_eq!( - format!("{:.1}", cloned.to_matrix()), + format!("{:.1}", cloned.as_matrix()), "┌ ┐\n\ │ -1.0 2.0 3.0 │\n\ │ 4.0 5.0 6.0 │\n\ @@ -2461,7 +2599,7 @@ mod tests { // deserialize let from_json: Tensor2 = serde_json::from_str(&json).unwrap(); assert_eq!( - format!("{:.1}", from_json.to_matrix()), + format!("{:.1}", from_json.as_matrix()), "┌ ┐\n\ │ 1.0 2.0 3.0 │\n\ │ 4.0 5.0 6.0 │\n\ @@ -2520,15 +2658,16 @@ mod tests { } #[test] - fn transpose_catches_errors() { + #[should_panic] + fn transpose_panics_on_incorrect_input() { let tt = Tensor2::new(Mandel::General); let mut tt2 = Tensor2::new(Mandel::Symmetric); - assert_eq!(tt.transpose(&mut tt2).err(), Some("tensors are incompatible")); + tt.transpose(&mut tt2); } fn check_transpose(tt: &Tensor2, tt_tran: &Tensor2) { - let aa = tt.to_matrix(); - let aa_tran = tt_tran.to_matrix(); + let aa = tt.as_matrix(); + let aa_tran = tt_tran.as_matrix(); for i in 1..3 { for j in 1..3 { assert_eq!(aa.get(i, j), aa_tran.get(j, i)); @@ -2542,34 +2681,35 @@ mod tests { let s = &SamplesTensor2::TENSOR_T; let tt = Tensor2::from_matrix(&s.matrix, Mandel::General).unwrap(); let mut tt2 = Tensor2::new(Mandel::General); - tt.transpose(&mut tt2).unwrap(); + tt.transpose(&mut tt2); check_transpose(&tt, &tt2); // symmetric 3D let s = &SamplesTensor2::TENSOR_U; let tt = Tensor2::from_matrix(&s.matrix, Mandel::Symmetric).unwrap(); let mut tt2 = Tensor2::new(Mandel::Symmetric); - tt.transpose(&mut tt2).unwrap(); + tt.transpose(&mut tt2); check_transpose(&tt, &tt2); // symmetric 2D let s = &SamplesTensor2::TENSOR_Y; let tt = Tensor2::from_matrix(&s.matrix, Mandel::Symmetric2D).unwrap(); let mut tt2 = Tensor2::new(Mandel::Symmetric2D); - tt.transpose(&mut tt2).unwrap(); + tt.transpose(&mut tt2); check_transpose(&tt, &tt2); } #[test] - fn inverse_catches_errors() { + #[should_panic] + fn inverse_panics_on_incorrect_input() { let tt = Tensor2::new(Mandel::General); let mut tti = Tensor2::new(Mandel::Symmetric); - assert_eq!(tt.inverse(&mut tti, 0.0).err(), Some("tensors are incompatible")); + tt.inverse(&mut tti, 0.0); } fn check_inverse(tt: &Tensor2, tti: &Tensor2, tol: f64) { - let aa = tt.to_matrix(); - let aai = tti.to_matrix(); + let aa = tt.as_matrix(); + let aai = tti.as_matrix(); let mut ii = Matrix::new(3, 3); mat_mat_mul(&mut ii, 1.0, &aa, &aai, 0.0).unwrap(); for i in 0..3 { @@ -2594,14 +2734,14 @@ mod tests { ]; let tt = Tensor2::from_matrix(comps_std, Mandel::General).unwrap(); let mut tti = Tensor2::new(Mandel::General); - let res = tt.inverse(&mut tti, 1e-10).unwrap(); + let res = tt.inverse(&mut tti, 1e-10); assert_eq!(res, None); // general with non-zero determinant let s = &SamplesTensor2::TENSOR_T; let tt = Tensor2::from_matrix(&s.matrix, Mandel::General).unwrap(); let mut tti = Tensor2::new(Mandel::General); - if let Some(det) = tt.inverse(&mut tti, 1e-10).unwrap() { + if let Some(det) = tt.inverse(&mut tti, 1e-10) { assert_eq!(det, s.determinant); } else { panic!("zero determinant found"); @@ -2612,14 +2752,14 @@ mod tests { let s = &SamplesTensor2::TENSOR_X; let tt = Tensor2::from_matrix(&s.matrix, Mandel::Symmetric).unwrap(); let mut tti = Tensor2::new(Mandel::Symmetric); - let res = tt.inverse(&mut tti, 1e-10).unwrap(); + let res = tt.inverse(&mut tti, 1e-10); assert_eq!(res, None); // symmetric 3D let s = &SamplesTensor2::TENSOR_U; let tt = Tensor2::from_matrix(&s.matrix, Mandel::Symmetric).unwrap(); let mut tti = Tensor2::new(Mandel::Symmetric); - if let Some(det) = tt.inverse(&mut tti, 1e-10).unwrap() { + if let Some(det) = tt.inverse(&mut tti, 1e-10) { approx_eq(det, s.determinant, 1e-14); } else { panic!("zero determinant found"); @@ -2630,14 +2770,14 @@ mod tests { let s = &SamplesTensor2::TENSOR_X; let tt = Tensor2::from_matrix(&s.matrix, Mandel::Symmetric2D).unwrap(); let mut tti = Tensor2::new(Mandel::Symmetric2D); - let res = tt.inverse(&mut tti, 1e-10).unwrap(); + let res = tt.inverse(&mut tti, 1e-10); assert_eq!(res, None); // symmetric 2D let s = &SamplesTensor2::TENSOR_Y; let tt = Tensor2::from_matrix(&s.matrix, Mandel::Symmetric2D).unwrap(); let mut tti = Tensor2::new(Mandel::Symmetric2D); - if let Some(det) = tt.inverse(&mut tti, 1e-10).unwrap() { + if let Some(det) = tt.inverse(&mut tti, 1e-10) { assert_eq!(det, s.determinant); } else { panic!("zero determinant found"); @@ -2646,15 +2786,16 @@ mod tests { } #[test] - fn squared_catches_errors() { + #[should_panic] + fn squared_panics_on_incorrect_input() { let tt = Tensor2::new(Mandel::General); let mut tt2 = Tensor2::new(Mandel::Symmetric); - assert_eq!(tt.squared(&mut tt2).err(), Some("tensors are incompatible")); + tt.squared(&mut tt2); } fn check_squared(tt: &Tensor2, tt2: &Tensor2, tol: f64) { - let aa = tt.to_matrix(); - let aa2 = tt2.to_matrix(); + let aa = tt.as_matrix(); + let aa2 = tt2.as_matrix(); let mut aa2_correct = Matrix::new(3, 3); mat_mat_mul(&mut aa2_correct, 1.0, &aa, &aa, 0.0).unwrap(); mat_approx_eq(&aa2, &aa2_correct, tol); @@ -2666,21 +2807,21 @@ mod tests { let s = &SamplesTensor2::TENSOR_T; let tt = Tensor2::from_matrix(&s.matrix, Mandel::General).unwrap(); let mut tt2 = Tensor2::new(Mandel::General); - tt.squared(&mut tt2).unwrap(); + tt.squared(&mut tt2); check_squared(&tt, &tt2, 1e-13); // symmetric 3D let s = &SamplesTensor2::TENSOR_U; let tt = Tensor2::from_matrix(&s.matrix, Mandel::Symmetric).unwrap(); let mut tt2 = Tensor2::new(Mandel::Symmetric); - tt.squared(&mut tt2).unwrap(); + tt.squared(&mut tt2); check_squared(&tt, &tt2, 1e-14); // symmetric 2D let s = &SamplesTensor2::TENSOR_Y; let tt = Tensor2::from_matrix(&s.matrix, Mandel::Symmetric2D).unwrap(); let mut tt2 = Tensor2::new(Mandel::Symmetric2D); - tt.squared(&mut tt2).unwrap(); + tt.squared(&mut tt2); check_squared(&tt, &tt2, 1e-15); } @@ -2730,10 +2871,11 @@ mod tests { } #[test] - fn deviator_catches_errors() { + #[should_panic] + fn deviator_panics_on_incorrect_input() { let tt = Tensor2::new(Mandel::General); let mut dev = Tensor2::new(Mandel::Symmetric); - assert_eq!(tt.deviator(&mut dev).err(), Some("tensors are incompatible")); + tt.deviator(&mut dev); } #[test] @@ -2747,10 +2889,10 @@ mod tests { ]; let tt = Tensor2::from_matrix(comps_std, Mandel::General).unwrap(); let mut dev = Tensor2::new(Mandel::General); - tt.deviator(&mut dev).unwrap(); + tt.deviator(&mut dev); approx_eq(dev.trace(), 0.0, 1e-15); assert_eq!( - format!("{:.1}", dev.to_matrix()), + format!("{:.1}", dev.as_matrix()), "┌ ┐\n\ │ -4.0 2.0 3.0 │\n\ │ 4.0 0.0 6.0 │\n\ @@ -2769,10 +2911,10 @@ mod tests { ]; let tt = Tensor2::from_matrix(comps_std, Mandel::Symmetric).unwrap(); let mut dev = Tensor2::new(Mandel::Symmetric); - tt.deviator(&mut dev).unwrap(); + tt.deviator(&mut dev); approx_eq(dev.trace(), 0.0, 1e-15); assert_eq!( - format!("{:.1}", dev.to_matrix()), + format!("{:.1}", dev.as_matrix()), "┌ ┐\n\ │ 1.0 -3.0 4.0 │\n\ │ -3.0 -6.0 1.0 │\n\ @@ -2791,10 +2933,10 @@ mod tests { ]; let tt = Tensor2::from_matrix(comps_std, Mandel::Symmetric2D).unwrap(); let mut dev = Tensor2::new(Mandel::Symmetric2D); - tt.deviator(&mut dev).unwrap(); + tt.deviator(&mut dev); approx_eq(dev.trace(), 0.0, 1e-15); assert_eq!( - format!("{:.1}", dev.to_matrix()), + format!("{:.1}", dev.as_matrix()), "┌ ┐\n\ │ -1.0 4.0 0.0 │\n\ │ 4.0 0.0 0.0 │\n\ From fad4800261a92ca561574b8b127ee3721b9039cf Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Sun, 12 May 2024 13:09:46 +1000 Subject: [PATCH 18/44] [wip] Improve doc comments --- russell_tensor/src/tensor2.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/russell_tensor/src/tensor2.rs b/russell_tensor/src/tensor2.rs index dfb7911a..7998b7fe 100644 --- a/russell_tensor/src/tensor2.rs +++ b/russell_tensor/src/tensor2.rs @@ -786,9 +786,13 @@ impl Tensor2 { /// Sets this tensor equal to another one /// + /// ```text + /// self := other + /// ``` + /// /// # Panics /// - /// A panic will occur if the tensors have different [Mandel] + /// A panic will occur if the tensors have different [Mandel]. /// /// # Examples /// @@ -846,7 +850,7 @@ impl Tensor2 { /// /// # Panics /// - /// A panic will occur if the tensors have different [Mandel] + /// A panic will occur if the tensors have different [Mandel]. /// /// # Examples /// From 9113eedfaf7abc09a97c34f418f6ea0f9df87bec Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Sun, 12 May 2024 13:16:26 +1000 Subject: [PATCH 19/44] [wip] Use assert in Tensor4. Improve doc comments --- russell_tensor/src/tensor4.rs | 73 +++++++++++++++++++++++++++-------- 1 file changed, 56 insertions(+), 17 deletions(-) diff --git a/russell_tensor/src/tensor4.rs b/russell_tensor/src/tensor4.rs index 7d2ae574..8b91974d 100644 --- a/russell_tensor/src/tensor4.rs +++ b/russell_tensor/src/tensor4.rs @@ -316,7 +316,7 @@ impl Tensor4 { Ok(Tensor4 { mat, mandel }) } - /// Creates a new Tensor4 constructed from a matrix with standard components + /// Creates a new Tensor4 constructed from a 9x9 matrix with standard components /// /// # Input /// @@ -327,7 +327,7 @@ impl Tensor4 { /// /// # Panics /// - /// This function panics if the input matrix is not (9 x 9) + /// A panic will occur if the matrix is not 9x9. /// /// # Examples /// @@ -589,6 +589,10 @@ impl Tensor4 { /// self += α other /// ``` /// + /// # Panics + /// + /// A panic will occur if the tensors have different [Mandel]. + /// /// # Examples /// /// ``` @@ -606,7 +610,7 @@ impl Tensor4 { /// /// let mut dd = Tensor4::new(Mandel::General); /// let ee = Tensor4::from_matrix(&inp, Mandel::General)?; - /// dd.update(2.0, &ee)?; + /// dd.update(2.0, &ee); /// /// assert_eq!( /// format!("{:.0}", dd.to_matrix()), @@ -625,12 +629,9 @@ impl Tensor4 { /// Ok(()) /// } /// ``` - pub fn update(&mut self, alpha: f64, other: &Tensor4) -> Result<(), StrError> { - if other.mandel() != self.mandel() { - return Err("tensors are incompatible"); - } + pub fn update(&mut self, alpha: f64, other: &Tensor4) { + assert_eq!(other.mandel, self.mandel); mat_update(&mut self.mat, alpha, &other.mat).unwrap(); - Ok(()) } /// Returns a 3x3x3x3 array with the standard components @@ -776,7 +777,7 @@ impl Tensor4 { /// /// # Input /// - /// * `mat` -- The resulting 9x9 matrix. + /// * `mat` -- the resulting 9x9 matrix /// /// # Panics /// @@ -828,10 +829,15 @@ impl Tensor4 { /// Sets the (i,j,k,l) component of a minor-symmetric Tensor4 /// + /// # Notes + /// + /// 1. The tensor must be symmetric and (i,j) must correspond to the possible + /// combination due to the space dimension, otherwise a panic may occur. + /// /// # Panics /// - /// The tensor must be symmetric and (i,j) must correspond to the possible - /// combination due to the space dimension, otherwise a panic may occur. + /// 1. A panic will occur if the tensor is [Mandel::General] + /// 2. A panic will occur if the indices are out of range /// /// # Examples /// @@ -864,6 +870,7 @@ impl Tensor4 { /// } /// ``` pub fn sym_set(&mut self, i: usize, j: usize, k: usize, l: usize, value: f64) { + assert!(self.mandel != Mandel::General); let (m, n) = IJKL_TO_MN_SYM[i][j][k][l]; if m < 3 && n < 3 { self.mat.set(m, n, value); @@ -876,6 +883,14 @@ impl Tensor4 { /// Sets this tensor equal to another one /// + /// ```text + /// self := other + /// ``` + /// + /// # Panics + /// + /// A panic will occur if the tensors have different [Mandel]. + /// /// # Examples /// /// ``` @@ -902,8 +917,9 @@ impl Tensor4 { /// Ok(()) /// } /// ``` - pub fn mirror(&mut self, other: &Tensor4) -> Result<(), StrError> { - mat_copy(&mut self.mat, &other.mat) + pub fn mirror(&mut self, other: &Tensor4) { + assert_eq!(other.mandel, self.mandel); + mat_copy(&mut self.mat, &other.mat).unwrap(); } /// Returns the fourth-order identity tensor (II) @@ -1464,11 +1480,19 @@ mod tests { } } + #[test] + #[should_panic] + fn update_panics_on_incorrect_input() { + let mut dd = Tensor4::new(Mandel::Symmetric2D); + let ee = Tensor4::new(Mandel::Symmetric); + dd.update(2.0, &ee); + } + #[test] fn update_works() { let mut dd = Tensor4::new(Mandel::Symmetric2D); let ee = Tensor4::from_array(&SamplesTensor4::SYM_2D_SAMPLE1, Mandel::Symmetric2D).unwrap(); - dd.update(2.0, &ee).unwrap(); + dd.update(2.0, &ee); for i in 0..3 { for j in 0..3 { for k in 0..3 { @@ -1681,6 +1705,20 @@ mod tests { dd } + #[test] + #[should_panic(expected = "self.mandel != Mandel::General")] + fn sym_set_panics_on_non_sym() { + let mut dd = Tensor4::new(Mandel::General); + dd.sym_set(0, 0, 0, 0, 1.0); + } + + #[test] + #[should_panic(expected = "the len is 3 but the index is 3")] + fn sym_set_panics_on_incorrect_indices() { + let mut dd = Tensor4::new(Mandel::Symmetric2D); + dd.sym_set(0, 0, 0, 3, 5.0); + } + #[test] fn sym_set_works() { let dd = generate_dd(); @@ -1701,10 +1739,11 @@ mod tests { } #[test] - fn mirror_captures_errors() { + #[should_panic] + fn mirror_panics_on_incorrect_input() { let dd = Tensor4::new(Mandel::Symmetric); let mut ee = Tensor4::new(Mandel::General); - assert_eq!(ee.mirror(&dd).err(), Some("matrices are incompatible")); + ee.mirror(&dd); } #[test] @@ -1725,7 +1764,7 @@ mod tests { ) .unwrap(); let mut ee = Tensor4::new(Mandel::General); - ee.mirror(&dd).unwrap(); + ee.mirror(&dd); mat_approx_eq(&dd.mat, &ee.mat, 1e-15); } From 1bbce46950a01a62c9d58e742e9be3dbcc96e671 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Sun, 12 May 2024 15:45:59 +1000 Subject: [PATCH 20/44] [wip] Use assert in derivatives_t2 functions. Improve doc comments --- russell_tensor/src/derivatives_t2.rs | 344 +++++++++++++++++---------- 1 file changed, 212 insertions(+), 132 deletions(-) diff --git a/russell_tensor/src/derivatives_t2.rs b/russell_tensor/src/derivatives_t2.rs index 7566b9c4..a4c86da4 100644 --- a/russell_tensor/src/derivatives_t2.rs +++ b/russell_tensor/src/derivatives_t2.rs @@ -1,4 +1,4 @@ -use crate::{Mandel, StrError, Tensor2, ONE_BY_3, SQRT_3, TOL_J2, TWO_BY_3}; +use crate::{Mandel, Tensor2, ONE_BY_3, SQRT_3, TOL_J2, TWO_BY_3}; /// Calculates the first derivative of the norm w.r.t. the defining Tensor2 /// @@ -8,24 +8,31 @@ use crate::{Mandel, StrError, Tensor2, ONE_BY_3, SQRT_3, TOL_J2, TWO_BY_3}; /// dσ ‖σ‖ /// ``` /// -/// # Results +/// # Output /// -/// If `‖σ‖ > 0`, returns `‖σ‖` and `d1` is valid; -/// Otherwise, returns `None` and `d1` is unchanged. -pub fn deriv1_norm(d1: &mut Tensor2, sigma: &Tensor2) -> Result, StrError> { +/// If `‖σ‖ > 0`, returns `‖σ‖`; otherwise, returns `None`. +/// +/// * `d1` -- a tensor to hold the resulting derivative; with the same [Mandel] as `sigma` +/// +/// # Input +/// +/// * `sigma` -- the tensor; with the same [Mandel] as `d1` +/// +/// # Panics +/// +/// A panic will occur if the tensors have different [Mandel]. +pub fn deriv1_norm(d1: &mut Tensor2, sigma: &Tensor2) -> Option { + assert_eq!(d1.mandel(), sigma.mandel()); let dim = d1.vec.dim(); - if sigma.vec.dim() != dim { - return Err("sigma and d1 tensors are incompatible"); - } let n = sigma.norm(); if n > 0.0 { - d1.mirror(sigma).unwrap(); + d1.mirror(sigma); for i in 0..dim { d1.vec[i] /= n; } - return Ok(Some(n)); + return Some(n); } - Ok(None) + None } /// Calculates the first derivative of the J2 invariant w.r.t. the stress tensor @@ -40,12 +47,22 @@ pub fn deriv1_norm(d1: &mut Tensor2, sigma: &Tensor2) -> Result, Str /// (σ is symmetric) /// ``` /// -/// Note: `sigma` must be Symmetric or Symmetric2D. -pub fn deriv1_invariant_jj2(d1: &mut Tensor2, sigma: &Tensor2) -> Result<(), StrError> { - if sigma.mandel() == Mandel::General { - return Err("sigma tensor must be Symmetric or Symmetric2D"); - } - sigma.deviator(d1) +/// # Output +/// +/// * `d1` -- a tensor to hold the resulting derivative; with the same [Mandel] as `sigma` +/// +/// # Input +/// +/// * `sigma` -- the [Mandel::Symmetric] or [Mandel::Symmetric2D] tensor; with the same [Mandel] as `d1` +/// +/// # Panics +/// +/// 1. A panic will occur if `sigma` is [Mandel::General] +/// 2. A panic will occur if the tensors have different [Mandel]. +pub fn deriv1_invariant_jj2(d1: &mut Tensor2, sigma: &Tensor2) { + assert!(sigma.mandel() != Mandel::General); + assert_eq!(d1.mandel(), sigma.mandel()); + sigma.deviator(d1); } /// Calculates the first derivative of the J3 invariant w.r.t. the stress tensor @@ -60,25 +77,29 @@ pub fn deriv1_invariant_jj2(d1: &mut Tensor2, sigma: &Tensor2) -> Result<(), Str /// (σ is symmetric) /// ``` /// -/// Note: `sigma` must be Symmetric or Symmetric2D. -pub fn deriv1_invariant_jj3(d1: &mut Tensor2, s: &mut Tensor2, sigma: &Tensor2) -> Result<(), StrError> { - if sigma.mandel() == Mandel::General { - return Err("sigma tensor must be Symmetric or Symmetric2D"); - } - let dim = sigma.vec.dim(); - if s.vec.dim() != dim { - return Err("s tensor is incompatible"); - } - if d1.vec.dim() != dim { - return Err("d1 tensor is incompatible"); - } +/// # Output +/// +/// * `d1` -- a tensor to hold the resulting derivative; with the same [Mandel] as `sigma` +/// * `s` -- the resulting deviator tensor; with the same [Mandel] as `sigma` +/// +/// # Input +/// +/// * `sigma` -- the [Mandel::Symmetric] or [Mandel::Symmetric2D] tensor; with the same [Mandel] as `d1` and `s` +/// +/// # Panics +/// +/// 1. A panic will occur if `sigma` is [Mandel::General] +/// 2. A panic will occur if the tensors have different [Mandel]. +pub fn deriv1_invariant_jj3(d1: &mut Tensor2, s: &mut Tensor2, sigma: &Tensor2) { + assert!(sigma.mandel() != Mandel::General); + assert_eq!(d1.mandel(), sigma.mandel()); + assert_eq!(s.mandel(), sigma.mandel()); let jj2 = sigma.invariant_jj2(); - sigma.deviator(s).unwrap(); - s.squared(d1).unwrap(); + sigma.deviator(s); + s.squared(d1); d1.vec[0] -= TWO_BY_3 * jj2; d1.vec[1] -= TWO_BY_3 * jj2; d1.vec[2] -= TWO_BY_3 * jj2; - Ok(()) } /// Calculates the first derivative of the mean stress invariant w.r.t. the stress tensor @@ -88,18 +109,27 @@ pub fn deriv1_invariant_jj3(d1: &mut Tensor2, s: &mut Tensor2, sigma: &Tensor2) /// ─── = ─ I /// dσ 3 /// ``` -pub fn deriv1_invariant_sigma_m(d1: &mut Tensor2, sigma: &Tensor2) -> Result<(), StrError> { +/// +/// # Output +/// +/// * `d1` -- a tensor to hold the resulting derivative; with the same [Mandel] as `sigma` +/// +/// # Input +/// +/// * `sigma` -- the tensor; with the same [Mandel] as `d1` +/// +/// # Panics +/// +/// A panic will occur if the tensors have different [Mandel]. +pub fn deriv1_invariant_sigma_m(d1: &mut Tensor2, sigma: &Tensor2) { + assert_eq!(d1.mandel(), sigma.mandel()); let dim = d1.vec.dim(); - if sigma.vec.dim() != dim { - return Err("sigma and d1 tensors are incompatible"); - } d1.vec[0] = ONE_BY_3; d1.vec[1] = ONE_BY_3; d1.vec[2] = ONE_BY_3; for i in 3..dim { d1.vec[i] = 0.0; } - Ok(()) } /// Calculates the first derivative of the deviatoric stress invariant (von Mises) w.r.t. the stress tensor @@ -114,30 +144,33 @@ pub fn deriv1_invariant_sigma_m(d1: &mut Tensor2, sigma: &Tensor2) -> Result<(), /// (σ is symmetric) /// ``` /// -/// Note: `sigma` must be Symmetric or Symmetric2D. +/// # Output /// -/// # Results +/// * If `J2 > TOL_J2`, returns `J2`; otherwise, returns `None`. +/// * `d1` -- a tensor to hold the resulting derivative; with the same [Mandel] as `sigma` /// -/// If `J2 > TOL_J2`, returns `J2` and `result` is valid; -/// Otherwise, returns `None` and `result` is invalid. -pub fn deriv1_invariant_sigma_d(d1: &mut Tensor2, sigma: &Tensor2) -> Result, StrError> { - if sigma.mandel() == Mandel::General { - return Err("sigma tensor must be Symmetric or Symmetric2D"); - } +/// # Input +/// +/// * `sigma` -- the [Mandel::Symmetric] or [Mandel::Symmetric2D] tensor; with the same [Mandel] as `d1` +/// +/// # Panics +/// +/// 1. A panic will occur if `sigma` is [Mandel::General] +/// 2. A panic will occur if the tensors have different [Mandel]. +pub fn deriv1_invariant_sigma_d(d1: &mut Tensor2, sigma: &Tensor2) -> Option { + assert!(sigma.mandel() != Mandel::General); + assert_eq!(d1.mandel(), sigma.mandel()); let dim = sigma.vec.dim(); - if d1.vec.dim() != dim { - return Err("d1 tensor is incompatible"); - } let jj2 = sigma.invariant_jj2(); if jj2 > TOL_J2 { let a = 0.5 * SQRT_3 / f64::powf(jj2, 0.5); - deriv1_invariant_jj2(d1, sigma).unwrap(); + deriv1_invariant_jj2(d1, sigma); for i in 0..dim { d1.vec[i] *= a; } - return Ok(Some(jj2)); + return Some(jj2); } - Ok(None) + None } /// Calculates the first derivative of the Lode invariant w.r.t. the stress tensor @@ -160,35 +193,37 @@ pub fn deriv1_invariant_sigma_d(d1: &mut Tensor2, sigma: &Tensor2) -> Result TOL_J2`, returns `J2`; otherwise, returns `None`. +/// * `d1` -- a tensor to hold the resulting derivative; with the same [Mandel] as `sigma` +/// * `s` -- the resulting deviator tensor; with the same [Mandel] as `sigma` /// -/// If `J2 > TOL_J2`, returns `J2` and `d1` is valid; -/// Otherwise, returns `None` and `d1` is unchanged. -pub fn deriv1_invariant_lode(d1: &mut Tensor2, s: &mut Tensor2, sigma: &Tensor2) -> Result, StrError> { - if sigma.mandel() == Mandel::General { - return Err("sigma tensor must be Symmetric or Symmetric2D"); - } +/// # Input +/// +/// * `sigma` -- the [Mandel::Symmetric] or [Mandel::Symmetric2D] tensor; with the same [Mandel] as `d1` +/// +/// # Panics +/// +/// 1. A panic will occur if `sigma` is [Mandel::General] +/// 2. A panic will occur if the tensors have different [Mandel]. +pub fn deriv1_invariant_lode(d1: &mut Tensor2, s: &mut Tensor2, sigma: &Tensor2) -> Option { + assert!(sigma.mandel() != Mandel::General); + assert_eq!(d1.mandel(), sigma.mandel()); + assert_eq!(s.mandel(), sigma.mandel()); let dim = sigma.vec.dim(); - if s.vec.dim() != dim { - return Err("s tensor is incompatible"); - } - if d1.vec.dim() != dim { - return Err("d1 tensor is incompatible"); - } let jj2 = sigma.invariant_jj2(); if jj2 > TOL_J2 { - deriv1_invariant_jj3(d1, s, sigma).unwrap(); // d1 := dJ3/dσ + deriv1_invariant_jj3(d1, s, sigma); // d1 := dJ3/dσ let jj3 = sigma.invariant_jj3(); let a = 1.5 * SQRT_3 / f64::powf(jj2, 1.5); let b = 2.25 * SQRT_3 / f64::powf(jj2, 2.5); for i in 0..dim { d1.vec[i] = a * d1.vec[i] - b * jj3 * s.vec[i]; } - return Ok(Some(jj2)); + return Some(jj2); } - Ok(None) + None } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -223,20 +258,20 @@ mod tests { fn analytical_deriv(fn_name: F, d1: &mut Tensor2, sigma: &Tensor2) { match fn_name { F::Norm => { - deriv1_norm(d1, sigma).unwrap().unwrap(); + deriv1_norm(d1, sigma).unwrap(); } - F::J2 => deriv1_invariant_jj2(d1, sigma).unwrap(), + F::J2 => deriv1_invariant_jj2(d1, sigma), F::J3 => { let mut s = Tensor2::new(sigma.mandel()); - deriv1_invariant_jj3(d1, &mut s, sigma).unwrap(); + deriv1_invariant_jj3(d1, &mut s, sigma); } - F::SigmaM => deriv1_invariant_sigma_m(d1, sigma).unwrap(), + F::SigmaM => deriv1_invariant_sigma_m(d1, sigma), F::SigmaD => { - deriv1_invariant_sigma_d(d1, sigma).unwrap().unwrap(); + deriv1_invariant_sigma_d(d1, sigma).unwrap(); } F::Lode => { let mut s = Tensor2::new(sigma.mandel()); - deriv1_invariant_lode(d1, &mut s, sigma).unwrap().unwrap(); + deriv1_invariant_lode(d1, &mut s, sigma).unwrap(); } }; } @@ -294,8 +329,8 @@ mod tests { fn numerical_deriv(sigma: &Tensor2, fn_name: F) -> Matrix { let mut args = ArgsNumDeriv { fn_name, - sigma_mat: sigma.to_matrix(), - sigma: sigma.to_general(), + sigma_mat: sigma.as_matrix(), + sigma: sigma.as_general(), i: 0, j: 0, }; @@ -326,7 +361,7 @@ mod tests { let res = deriv1_central5(x, &mut args, f_sigma_mandel).unwrap(); num_deriv.vec[m] = res; } - num_deriv.to_matrix() + num_deriv.as_matrix() } // checks ∂f/∂σᵢⱼ @@ -334,7 +369,7 @@ mod tests { let sigma = Tensor2::from_matrix(&sample.matrix, mandel).unwrap(); let mut d1 = Tensor2::new(mandel); analytical_deriv(fn_name, &mut d1, &sigma); - let ana = d1.to_matrix(); + let ana = d1.as_matrix(); let num = numerical_deriv(&sigma, fn_name); let num_mandel = numerical_deriv_mandel(&sigma, fn_name); /* @@ -402,72 +437,117 @@ mod tests { check_deriv(F::Lode, Mandel::Symmetric2D, &SamplesTensor2::TENSOR_Z, 1e-10, v); } - // -- check None and errors ---------------------------------------------------------------------- - #[test] fn check_for_none() { let sigma = Tensor2::from_matrix(&SamplesTensor2::TENSOR_O.matrix, Mandel::Symmetric).unwrap(); let mut d1 = Tensor2::new(Mandel::Symmetric); let mut s = Tensor2::new(Mandel::Symmetric); - assert_eq!(deriv1_norm(&mut d1, &sigma).unwrap(), None); - assert_eq!(deriv1_invariant_sigma_d(&mut d1, &sigma).unwrap(), None); - assert_eq!(deriv1_invariant_lode(&mut d1, &mut s, &sigma).unwrap(), None); + assert_eq!(deriv1_norm(&mut d1, &sigma), None); + assert_eq!(deriv1_invariant_sigma_d(&mut d1, &sigma), None); + assert_eq!(deriv1_invariant_lode(&mut d1, &mut s, &sigma), None); + } + + // check panics + + #[test] + #[should_panic] + fn deriv1_norm_panics_on_different_mandel() { + let mut d1_gen = Tensor2::new(Mandel::General); + let sigma_sym = Tensor2::new(Mandel::Symmetric); + deriv1_norm(&mut d1_gen, &sigma_sym); } #[test] - fn check_errors() { + #[should_panic(expected = "sigma.mandel() != Mandel::General")] + fn deriv1_invariant_jj2_panics_on_on_sym() { + let mut d1_gen = Tensor2::new(Mandel::General); let sigma_gen = Tensor2::new(Mandel::General); + deriv1_invariant_jj2(&mut d1_gen, &sigma_gen); + } + + #[test] + #[should_panic] + fn deriv1_invariant_jj2_panics_on_different_mandel() { + let mut d1_gen = Tensor2::new(Mandel::General); let sigma_sym = Tensor2::new(Mandel::Symmetric); + deriv1_invariant_jj2(&mut d1_gen, &sigma_sym); + } + + #[test] + #[should_panic(expected = "sigma.mandel() != Mandel::General")] + fn deriv1_invariant_jj3_panics_on_non_sym() { + let mut d1_gen = Tensor2::new(Mandel::General); let mut s_gen = Tensor2::new(Mandel::General); + let sigma_gen = Tensor2::new(Mandel::General); + deriv1_invariant_jj3(&mut d1_gen, &mut s_gen, &sigma_gen); + } + + #[test] + #[should_panic] + fn deriv1_invariant_jj3_panics_on_different_mandel1() { + let mut d1_gen = Tensor2::new(Mandel::General); + let mut s_gen = Tensor2::new(Mandel::General); + let sigma_sym = Tensor2::new(Mandel::Symmetric); + deriv1_invariant_jj3(&mut d1_gen, &mut s_gen, &sigma_sym); + } + + #[test] + #[should_panic] + fn deriv1_invariant_jj3_panics_on_different_mandel2() { + let mut d1_gen = Tensor2::new(Mandel::General); let mut s_sym = Tensor2::new(Mandel::Symmetric); + let sigma_sym = Tensor2::new(Mandel::Symmetric); + deriv1_invariant_jj3(&mut d1_gen, &mut s_sym, &sigma_sym); + } + + #[test] + #[should_panic] + fn deriv1_invariant_sigma_m_panics_on_different_mandel() { + let mut d1_gen = Tensor2::new(Mandel::General); + let sigma_sym = Tensor2::new(Mandel::Symmetric); + deriv1_invariant_sigma_m(&mut d1_gen, &sigma_sym); + } + + #[test] + #[should_panic(expected = "sigma.mandel() != Mandel::General")] + fn deriv1_invariant_sigma_d_panics_on_non_sym() { let mut d1_gen = Tensor2::new(Mandel::General); - assert_eq!( - deriv1_norm(&mut d1_gen, &sigma_sym).err(), - Some("sigma and d1 tensors are incompatible") - ); - assert_eq!( - deriv1_invariant_jj2(&mut d1_gen, &sigma_gen).err(), - Some("sigma tensor must be Symmetric or Symmetric2D") - ); - assert_eq!( - deriv1_invariant_jj2(&mut d1_gen, &sigma_sym).err(), - Some("tensors are incompatible") - ); - assert_eq!( - deriv1_invariant_jj3(&mut d1_gen, &mut s_gen, &sigma_gen).err(), - Some("sigma tensor must be Symmetric or Symmetric2D") - ); - assert_eq!( - deriv1_invariant_jj3(&mut d1_gen, &mut s_gen, &sigma_sym).err(), - Some("s tensor is incompatible") - ); - assert_eq!( - deriv1_invariant_jj3(&mut d1_gen, &mut s_sym, &sigma_sym).err(), - Some("d1 tensor is incompatible") - ); - assert_eq!( - deriv1_invariant_sigma_m(&mut d1_gen, &sigma_sym).err(), - Some("sigma and d1 tensors are incompatible") - ); - assert_eq!( - deriv1_invariant_sigma_d(&mut d1_gen, &sigma_gen).err(), - Some("sigma tensor must be Symmetric or Symmetric2D") - ); - assert_eq!( - deriv1_invariant_sigma_d(&mut d1_gen, &sigma_sym).err(), - Some("d1 tensor is incompatible") - ); - assert_eq!( - deriv1_invariant_lode(&mut d1_gen, &mut s_gen, &sigma_gen).err(), - Some("sigma tensor must be Symmetric or Symmetric2D") - ); - assert_eq!( - deriv1_invariant_lode(&mut d1_gen, &mut s_gen, &sigma_sym).err(), - Some("s tensor is incompatible") - ); - assert_eq!( - deriv1_invariant_lode(&mut d1_gen, &mut s_sym, &sigma_sym).err(), - Some("d1 tensor is incompatible") - ); + let sigma_gen = Tensor2::new(Mandel::General); + deriv1_invariant_sigma_d(&mut d1_gen, &sigma_gen); + } + + #[test] + #[should_panic] + fn deriv1_invariant_sigma_d_panics_on_different_mandel() { + let mut d1_gen = Tensor2::new(Mandel::General); + let sigma_sym = Tensor2::new(Mandel::Symmetric); + deriv1_invariant_sigma_d(&mut d1_gen, &sigma_sym); + } + + #[test] + #[should_panic(expected = "sigma.mandel() != Mandel::General")] + fn deriv1_invariant_lode_panics_on_non_sym() { + let mut d1_gen = Tensor2::new(Mandel::General); + let mut s_gen = Tensor2::new(Mandel::General); + let sigma_gen = Tensor2::new(Mandel::General); + deriv1_invariant_lode(&mut d1_gen, &mut s_gen, &sigma_gen); + } + + #[test] + #[should_panic] + fn deriv1_invariant_lode_panics_on_different_mandel1() { + let mut d1_gen = Tensor2::new(Mandel::General); + let mut s_gen = Tensor2::new(Mandel::General); + let sigma_sym = Tensor2::new(Mandel::Symmetric); + deriv1_invariant_lode(&mut d1_gen, &mut s_gen, &sigma_sym); + } + + #[test] + #[should_panic] + fn deriv1_invariant_lode_panics_on_different_mandel2() { + let mut d1_gen = Tensor2::new(Mandel::General); + let mut s_sym = Tensor2::new(Mandel::Symmetric); + let sigma_sym = Tensor2::new(Mandel::Symmetric); + deriv1_invariant_lode(&mut d1_gen, &mut s_sym, &sigma_sym); } } From 954138ae4880423212c5aac09224fb755218127c Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Sun, 12 May 2024 17:18:46 +1000 Subject: [PATCH 21/44] [sparse] Improve grammar in doc comments --- russell_sparse/src/complex_solver_klu.rs | 2 +- russell_sparse/src/complex_solver_mumps.rs | 2 +- russell_sparse/src/complex_solver_umfpack.rs | 2 +- russell_sparse/src/coo_matrix.rs | 2 +- russell_sparse/src/csc_matrix.rs | 2 +- russell_sparse/src/csr_matrix.rs | 2 +- russell_sparse/src/solver_klu.rs | 2 +- russell_sparse/src/solver_mumps.rs | 2 +- russell_sparse/src/solver_umfpack.rs | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/russell_sparse/src/complex_solver_klu.rs b/russell_sparse/src/complex_solver_klu.rs index 78c4fb2b..f753c6da 100644 --- a/russell_sparse/src/complex_solver_klu.rs +++ b/russell_sparse/src/complex_solver_klu.rs @@ -248,7 +248,7 @@ impl ComplexLinSolTrait for ComplexSolverKLU { /// /// # Input /// - /// * `mat` -- the coefficient matrix A; must be square and, if symmetric, [Sym::YesFull]. + /// * `mat` -- the coefficient matrix A; it must be square and, if symmetric, [Sym::YesFull]. /// * `rhs` -- the right-hand side vector with know values an dimension equal to mat.nrow /// * `verbose` -- NOT AVAILABLE /// diff --git a/russell_sparse/src/complex_solver_mumps.rs b/russell_sparse/src/complex_solver_mumps.rs index f69c4006..922d1184 100644 --- a/russell_sparse/src/complex_solver_mumps.rs +++ b/russell_sparse/src/complex_solver_mumps.rs @@ -340,7 +340,7 @@ impl ComplexLinSolTrait for ComplexSolverMUMPS { /// /// # Input /// - /// * `mat` -- the coefficient matrix A; must be square and, if symmetric, [Sym::YesLower]. + /// * `mat` -- the coefficient matrix A; it must be square and, if symmetric, [Sym::YesLower]. /// * `rhs` -- the right-hand side vector with know values an dimension equal to mat.nrow /// * `verbose` -- shows messages /// diff --git a/russell_sparse/src/complex_solver_umfpack.rs b/russell_sparse/src/complex_solver_umfpack.rs index f1a68d74..771ac40f 100644 --- a/russell_sparse/src/complex_solver_umfpack.rs +++ b/russell_sparse/src/complex_solver_umfpack.rs @@ -300,7 +300,7 @@ impl ComplexLinSolTrait for ComplexSolverUMFPACK { /// /// # Input /// - /// * `mat` -- the coefficient matrix A; must be square and, if symmetric, [Sym::YesFull]. + /// * `mat` -- the coefficient matrix A; it must be square and, if symmetric, [Sym::YesFull]. /// * `rhs` -- the right-hand side vector with know values an dimension equal to mat.nrow /// * `verbose` -- shows messages /// diff --git a/russell_sparse/src/coo_matrix.rs b/russell_sparse/src/coo_matrix.rs index 099b778b..62f665b2 100644 --- a/russell_sparse/src/coo_matrix.rs +++ b/russell_sparse/src/coo_matrix.rs @@ -423,7 +423,7 @@ where /// /// # Input /// - /// * `a` -- where to store the dense matrix; must be (nrow, ncol) + /// * `a` -- where to store the dense matrix; it must be (nrow, ncol) /// /// # Examples /// diff --git a/russell_sparse/src/csc_matrix.rs b/russell_sparse/src/csc_matrix.rs index acaae0f7..8042fcf7 100644 --- a/russell_sparse/src/csc_matrix.rs +++ b/russell_sparse/src/csc_matrix.rs @@ -637,7 +637,7 @@ where /// /// # Input /// - /// * `a` -- where to store the dense matrix; must be (nrow, ncol) + /// * `a` -- where to store the dense matrix; it must be (nrow, ncol) /// /// # Examples /// diff --git a/russell_sparse/src/csr_matrix.rs b/russell_sparse/src/csr_matrix.rs index 73380bbe..65df7aaf 100644 --- a/russell_sparse/src/csr_matrix.rs +++ b/russell_sparse/src/csr_matrix.rs @@ -611,7 +611,7 @@ where /// /// # Input /// - /// * `a` -- where to store the dense matrix; must be (nrow, ncol) + /// * `a` -- where to store the dense matrix; it must be (nrow, ncol) /// /// # Examples /// diff --git a/russell_sparse/src/solver_klu.rs b/russell_sparse/src/solver_klu.rs index 6703fd91..d1200341 100644 --- a/russell_sparse/src/solver_klu.rs +++ b/russell_sparse/src/solver_klu.rs @@ -246,7 +246,7 @@ impl LinSolTrait for SolverKLU { /// /// # Input /// - /// * `mat` -- the coefficient matrix A; must be square and, if symmetric, [Sym::YesFull]. + /// * `mat` -- the coefficient matrix A; it must be square and, if symmetric, [Sym::YesFull]. /// * `rhs` -- the right-hand side vector with know values an dimension equal to mat.nrow /// * `verbose` -- NOT AVAILABLE /// diff --git a/russell_sparse/src/solver_mumps.rs b/russell_sparse/src/solver_mumps.rs index 75b9b559..7803f616 100644 --- a/russell_sparse/src/solver_mumps.rs +++ b/russell_sparse/src/solver_mumps.rs @@ -326,7 +326,7 @@ impl LinSolTrait for SolverMUMPS { /// /// # Input /// - /// * `mat` -- the coefficient matrix A; must be square and, if symmetric, [Sym::YesLower]. + /// * `mat` -- the coefficient matrix A; it must be square and, if symmetric, [Sym::YesLower]. /// * `rhs` -- the right-hand side vector with know values an dimension equal to mat.nrow /// * `verbose` -- shows messages /// diff --git a/russell_sparse/src/solver_umfpack.rs b/russell_sparse/src/solver_umfpack.rs index 78c9384c..0ced8eef 100644 --- a/russell_sparse/src/solver_umfpack.rs +++ b/russell_sparse/src/solver_umfpack.rs @@ -286,7 +286,7 @@ impl LinSolTrait for SolverUMFPACK { /// /// # Input /// - /// * `mat` -- the coefficient matrix A; must be square and, if symmetric, [Sym::YesFull]. + /// * `mat` -- the coefficient matrix A; it must be square and, if symmetric, [Sym::YesFull]. /// * `rhs` -- the right-hand side vector with know values an dimension equal to mat.nrow /// * `verbose` -- shows messages /// From 2eddc312adcff88f400b8718f3baf85133026ec8 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Sun, 12 May 2024 18:20:48 +1000 Subject: [PATCH 22/44] [tensor][Important][wip] Make vec, mat, and mandel pub(crate) --- russell_tensor/src/tensor2.rs | 12 ++---------- russell_tensor/src/tensor4.rs | 14 +++----------- 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/russell_tensor/src/tensor2.rs b/russell_tensor/src/tensor2.rs index 7998b7fe..fed7d2a6 100644 --- a/russell_tensor/src/tensor2.rs +++ b/russell_tensor/src/tensor2.rs @@ -49,14 +49,6 @@ use serde::{Deserialize, Serialize}; /// └ ┘ 01 │ T01 * √2 │ 3 /// └ ┘ /// ``` -/// -/// # Notes -/// -/// * The tensor is represented as a 9D, 6D or 4D vector and saved as `vec` -/// * You may perform operations on `vec` directly because it is isomorphic with the tensor itself -/// * For example, the norm of the tensor equals `vec.norm()` -/// * However, you must be careful when setting a single component of `vec` directly -/// because the Mandel representation may become inconsistent (with incorrect values). #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Tensor2 { /// Holds the components in Mandel basis as a vector. @@ -64,10 +56,10 @@ pub struct Tensor2 { /// * General: `vec.dim = 9` /// * Symmetric in 3D: `vec.dim = 6` /// * Symmetric in 2D: `vec.dim = 4` - pub vec: Vector, + pub(crate) vec: Vector, /// Holds the Mandel enum - mandel: Mandel, + pub(crate) mandel: Mandel, } impl Tensor2 { diff --git a/russell_tensor/src/tensor4.rs b/russell_tensor/src/tensor4.rs index 8b91974d..640f62db 100644 --- a/russell_tensor/src/tensor4.rs +++ b/russell_tensor/src/tensor4.rs @@ -100,14 +100,6 @@ use serde::{Deserialize, Serialize}; /// ---------------------------------------- /// 3 0 3 1 3 2 3 3 /// ``` -/// -/// # Notes -/// -/// * The tensor is represented as a (9D x 9D), (6D x 6D) or (4D x 4D) matrix and saved as `mat` -/// * You may perform operations on `mat` directly because it is isomorphic with the tensor itself -/// * For example, the norm of the tensor equals `mat.norm()` -/// * However, you must be careful when setting a single component of `mat` directly -/// because you may "break" the Mandel representation. #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Tensor4 { /// Holds the components in Mandel basis as matrix. @@ -115,10 +107,10 @@ pub struct Tensor4 { /// * General: `(nrow,ncol) = (9,9)` /// * Minor-symmetric in 3D: `(nrow,ncol) = (6,6)` /// * Minor-symmetric in 2D: `(nrow,ncol) = (4,4)` - pub mat: Matrix, + pub(crate) mat: Matrix, /// Holds the Mandel enum - mandel: Mandel, + pub(crate) mandel: Mandel, } impl Tensor4 { @@ -1302,7 +1294,7 @@ impl Tensor4 { self.mat.set(2, 1, -ONE_BY_3); self.mat.set(2, 2, TWO_BY_3); self.mat.set(3, 3, 1.0); - if self.mandel().dim() > 4 { + if self.mandel.dim() > 4 { self.mat.set(4, 4, 1.0); self.mat.set(5, 5, 1.0); } From 8f560afdec4c2cbf1933d83c87f7cf97113daf9f Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Sun, 12 May 2024 18:28:05 +1000 Subject: [PATCH 23/44] [tensor][wip] Implement symmetric member function of Mandel --- russell_tensor/src/enums.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/russell_tensor/src/enums.rs b/russell_tensor/src/enums.rs index 344aa7c7..98c85962 100644 --- a/russell_tensor/src/enums.rs +++ b/russell_tensor/src/enums.rs @@ -79,6 +79,15 @@ impl Mandel { Mandel::Symmetric2D => true, } } + + /// Returns whether the Mandel vector or matrix corresponds a symmetric tensor or not + pub fn symmetric(&self) -> bool { + if *self == Mandel::General { + false + } else { + true + } + } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -105,11 +114,17 @@ mod tests { #[test] fn member_functions_work() { + // dim assert_eq!(Mandel::General.dim(), 9); assert_eq!(Mandel::Symmetric.dim(), 6); assert_eq!(Mandel::Symmetric2D.dim(), 4); + // two_dim assert_eq!(Mandel::General.two_dim(), false); assert_eq!(Mandel::Symmetric.two_dim(), false); assert_eq!(Mandel::Symmetric2D.two_dim(), true); + // symmetric + assert_eq!(Mandel::General.symmetric(), false); + assert_eq!(Mandel::Symmetric.symmetric(), true); + assert_eq!(Mandel::Symmetric2D.symmetric(), true); } } From 4a70314d4e3590ba95320b895abeb54556b0dcaf Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Sun, 12 May 2024 18:29:26 +1000 Subject: [PATCH 24/44] [wip] Improve asserts in derivatives_t2 --- russell_tensor/src/derivatives_t2.rs | 58 ++++++++++++++-------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/russell_tensor/src/derivatives_t2.rs b/russell_tensor/src/derivatives_t2.rs index a4c86da4..29cd6f7c 100644 --- a/russell_tensor/src/derivatives_t2.rs +++ b/russell_tensor/src/derivatives_t2.rs @@ -1,4 +1,7 @@ -use crate::{Mandel, Tensor2, ONE_BY_3, SQRT_3, TOL_J2, TWO_BY_3}; +use crate::{Tensor2, ONE_BY_3, SQRT_3, TOL_J2, TWO_BY_3}; + +#[allow(unused)] +use crate::Mandel; // for documentation /// Calculates the first derivative of the norm w.r.t. the defining Tensor2 /// @@ -22,7 +25,7 @@ use crate::{Mandel, Tensor2, ONE_BY_3, SQRT_3, TOL_J2, TWO_BY_3}; /// /// A panic will occur if the tensors have different [Mandel]. pub fn deriv1_norm(d1: &mut Tensor2, sigma: &Tensor2) -> Option { - assert_eq!(d1.mandel(), sigma.mandel()); + assert_eq!(d1.mandel, sigma.mandel); let dim = d1.vec.dim(); let n = sigma.norm(); if n > 0.0 { @@ -57,11 +60,11 @@ pub fn deriv1_norm(d1: &mut Tensor2, sigma: &Tensor2) -> Option { /// /// # Panics /// -/// 1. A panic will occur if `sigma` is [Mandel::General] +/// 1. A panic will occur if `sigma` is not symmetric. /// 2. A panic will occur if the tensors have different [Mandel]. pub fn deriv1_invariant_jj2(d1: &mut Tensor2, sigma: &Tensor2) { - assert!(sigma.mandel() != Mandel::General); - assert_eq!(d1.mandel(), sigma.mandel()); + assert!(sigma.mandel.symmetric()); + assert_eq!(d1.mandel, sigma.mandel); sigma.deviator(d1); } @@ -88,12 +91,12 @@ pub fn deriv1_invariant_jj2(d1: &mut Tensor2, sigma: &Tensor2) { /// /// # Panics /// -/// 1. A panic will occur if `sigma` is [Mandel::General] +/// 1. A panic will occur if `sigma` is not symmetric. /// 2. A panic will occur if the tensors have different [Mandel]. pub fn deriv1_invariant_jj3(d1: &mut Tensor2, s: &mut Tensor2, sigma: &Tensor2) { - assert!(sigma.mandel() != Mandel::General); - assert_eq!(d1.mandel(), sigma.mandel()); - assert_eq!(s.mandel(), sigma.mandel()); + assert!(sigma.mandel.symmetric()); + assert_eq!(d1.mandel, sigma.mandel); + assert_eq!(s.mandel, sigma.mandel); let jj2 = sigma.invariant_jj2(); sigma.deviator(s); s.squared(d1); @@ -122,7 +125,7 @@ pub fn deriv1_invariant_jj3(d1: &mut Tensor2, s: &mut Tensor2, sigma: &Tensor2) /// /// A panic will occur if the tensors have different [Mandel]. pub fn deriv1_invariant_sigma_m(d1: &mut Tensor2, sigma: &Tensor2) { - assert_eq!(d1.mandel(), sigma.mandel()); + assert_eq!(d1.mandel, sigma.mandel); let dim = d1.vec.dim(); d1.vec[0] = ONE_BY_3; d1.vec[1] = ONE_BY_3; @@ -155,11 +158,11 @@ pub fn deriv1_invariant_sigma_m(d1: &mut Tensor2, sigma: &Tensor2) { /// /// # Panics /// -/// 1. A panic will occur if `sigma` is [Mandel::General] +/// 1. A panic will occur if `sigma` is not symmetric. /// 2. A panic will occur if the tensors have different [Mandel]. pub fn deriv1_invariant_sigma_d(d1: &mut Tensor2, sigma: &Tensor2) -> Option { - assert!(sigma.mandel() != Mandel::General); - assert_eq!(d1.mandel(), sigma.mandel()); + assert!(sigma.mandel.symmetric()); + assert_eq!(d1.mandel, sigma.mandel); let dim = sigma.vec.dim(); let jj2 = sigma.invariant_jj2(); if jj2 > TOL_J2 { @@ -205,12 +208,12 @@ pub fn deriv1_invariant_sigma_d(d1: &mut Tensor2, sigma: &Tensor2) -> Option Option { - assert!(sigma.mandel() != Mandel::General); - assert_eq!(d1.mandel(), sigma.mandel()); - assert_eq!(s.mandel(), sigma.mandel()); + assert!(sigma.mandel.symmetric()); + assert_eq!(d1.mandel, sigma.mandel); + assert_eq!(s.mandel, sigma.mandel); let dim = sigma.vec.dim(); let jj2 = sigma.invariant_jj2(); if jj2 > TOL_J2 { @@ -230,11 +233,8 @@ pub fn deriv1_invariant_lode(d1: &mut Tensor2, s: &mut Tensor2, sigma: &Tensor2) #[cfg(test)] mod tests { - use super::Tensor2; - use crate::{ - deriv1_invariant_jj2, deriv1_invariant_jj3, deriv1_invariant_lode, deriv1_invariant_sigma_d, - deriv1_invariant_sigma_m, deriv1_norm, Mandel, SampleTensor2, SamplesTensor2, StrError, - }; + use super::*; + use crate::{SampleTensor2, SamplesTensor2, StrError}; use russell_lab::{deriv1_central5, mat_approx_eq, Matrix}; // Defines f(σ) @@ -262,7 +262,7 @@ mod tests { } F::J2 => deriv1_invariant_jj2(d1, sigma), F::J3 => { - let mut s = Tensor2::new(sigma.mandel()); + let mut s = Tensor2::new(sigma.mandel); deriv1_invariant_jj3(d1, &mut s, sigma); } F::SigmaM => deriv1_invariant_sigma_m(d1, sigma), @@ -270,7 +270,7 @@ mod tests { deriv1_invariant_sigma_d(d1, sigma).unwrap(); } F::Lode => { - let mut s = Tensor2::new(sigma.mandel()); + let mut s = Tensor2::new(sigma.mandel); deriv1_invariant_lode(d1, &mut s, sigma).unwrap(); } }; @@ -447,7 +447,7 @@ mod tests { assert_eq!(deriv1_invariant_lode(&mut d1, &mut s, &sigma), None); } - // check panics + // check assertions ----------------------------------------------------------------------------- #[test] #[should_panic] @@ -458,7 +458,7 @@ mod tests { } #[test] - #[should_panic(expected = "sigma.mandel() != Mandel::General")] + #[should_panic(expected = "sigma.mandel.symmetric()")] fn deriv1_invariant_jj2_panics_on_on_sym() { let mut d1_gen = Tensor2::new(Mandel::General); let sigma_gen = Tensor2::new(Mandel::General); @@ -474,7 +474,7 @@ mod tests { } #[test] - #[should_panic(expected = "sigma.mandel() != Mandel::General")] + #[should_panic(expected = "sigma.mandel.symmetric()")] fn deriv1_invariant_jj3_panics_on_non_sym() { let mut d1_gen = Tensor2::new(Mandel::General); let mut s_gen = Tensor2::new(Mandel::General); @@ -509,7 +509,7 @@ mod tests { } #[test] - #[should_panic(expected = "sigma.mandel() != Mandel::General")] + #[should_panic(expected = "sigma.mandel.symmetric()")] fn deriv1_invariant_sigma_d_panics_on_non_sym() { let mut d1_gen = Tensor2::new(Mandel::General); let sigma_gen = Tensor2::new(Mandel::General); @@ -525,7 +525,7 @@ mod tests { } #[test] - #[should_panic(expected = "sigma.mandel() != Mandel::General")] + #[should_panic(expected = "sigma.mandel.symmetric()")] fn deriv1_invariant_lode_panics_on_non_sym() { let mut d1_gen = Tensor2::new(Mandel::General); let mut s_gen = Tensor2::new(Mandel::General); From 328e361441a09ddc8e9dcc8a2ba829186e9efe0a Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Sun, 12 May 2024 20:36:27 +1000 Subject: [PATCH 25/44] Impl method sym2d_as_symmetric of Tensor2 --- russell_tensor/src/tensor2.rs | 93 +++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/russell_tensor/src/tensor2.rs b/russell_tensor/src/tensor2.rs index fed7d2a6..87dc415b 100644 --- a/russell_tensor/src/tensor2.rs +++ b/russell_tensor/src/tensor2.rs @@ -653,6 +653,62 @@ impl Tensor2 { res } + /// Returns a Symmetric tensor from a Symmetric2D tensor + /// + /// # Output + /// + /// Returns a [Mandel::Symmetric] tensor if this tensor is [Mandel::Symmetric2D]. + /// + /// # Panics + /// + /// A panic will occur if this tensor is not [Mandel::Symmetric2D]. + /// + /// # Examples + /// + /// ``` + /// use russell_tensor::{Mandel, Tensor2, StrError, SQRT_2}; + /// + /// fn main() -> Result<(), StrError> { + /// let tt = Tensor2::from_matrix(&[ + /// [1.0, 2.0/SQRT_2, 0.0], + /// [2.0/SQRT_2, 3.0, 0.0], + /// [0.0, 0.0, 4.0], + /// ], Mandel::Symmetric2D)?; + /// assert_eq!( + /// format!("{:.2}", tt.vec), + /// "┌ ┐\n\ + /// │ 1.00 │\n\ + /// │ 3.00 │\n\ + /// │ 4.00 │\n\ + /// │ 2.00 │\n\ + /// └ ┘" + /// ); + /// + /// let tt_sym = tt.sym2d_as_symmetric(); + /// assert_eq!( + /// format!("{:.2}", tt_sym.vec), + /// "┌ ┐\n\ + /// │ 1.00 │\n\ + /// │ 3.00 │\n\ + /// │ 4.00 │\n\ + /// │ 2.00 │\n\ + /// │ 0.00 │\n\ + /// │ 0.00 │\n\ + /// └ ┘" + /// ); + /// Ok(()) + /// } + /// ``` + pub fn sym2d_as_symmetric(&self) -> Tensor2 { + assert_eq!(self.mandel, Mandel::Symmetric2D); + let mut res = Tensor2::new(Mandel::Symmetric); + res.vec[0] = self.vec[0]; + res.vec[1] = self.vec[1]; + res.vec[2] = self.vec[2]; + res.vec[3] = self.vec[3]; + res + } + /// Set all values to zero pub fn clear(&mut self) { self.vec.fill(0.0); @@ -2347,6 +2403,43 @@ mod tests { } } + #[test] + #[should_panic] + fn sym2d_as_symmetric_panics_on_non_sym2d() { + let tt = Tensor2::new(Mandel::Symmetric); + tt.sym2d_as_symmetric(); + } + + #[test] + fn sym2d_as_symmetric_works() { + let tt = Tensor2::from_matrix( + &[[1.0, 2.0 / SQRT_2, 0.0], [2.0 / SQRT_2, 3.0, 0.0], [0.0, 0.0, 4.0]], + Mandel::Symmetric2D, + ) + .unwrap(); + let tt_sym = tt.sym2d_as_symmetric(); + assert_eq!( + format!("{:.2}", tt.vec), + "┌ ┐\n\ + │ 1.00 │\n\ + │ 3.00 │\n\ + │ 4.00 │\n\ + │ 2.00 │\n\ + └ ┘" + ); + assert_eq!( + format!("{:.2}", tt_sym.vec), + "┌ ┐\n\ + │ 1.00 │\n\ + │ 3.00 │\n\ + │ 4.00 │\n\ + │ 2.00 │\n\ + │ 0.00 │\n\ + │ 0.00 │\n\ + └ ┘" + ); + } + #[test] #[should_panic(expected = "self.mandel != Mandel::General")] fn sym_set_panics_on_non_sym() { From 39d7f4078e908c213fb25be5aa2b0d3b1bbf628d Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Sun, 12 May 2024 21:07:28 +1000 Subject: [PATCH 26/44] [Important][wip] Rewrite error handling in derivatives_t4 --- russell_tensor/src/derivatives_t4.rs | 736 +++++++++++++-------------- 1 file changed, 358 insertions(+), 378 deletions(-) diff --git a/russell_tensor/src/derivatives_t4.rs b/russell_tensor/src/derivatives_t4.rs index e30b1a88..83a39730 100644 --- a/russell_tensor/src/derivatives_t4.rs +++ b/russell_tensor/src/derivatives_t4.rs @@ -1,7 +1,6 @@ -use crate::{ - deriv1_invariant_jj2, deriv1_invariant_jj3, t2_dyad_t2, t2_odyad_t2, t2_qsd_t2, t2_ssd, Mandel, StrError, Tensor2, - Tensor4, ONE_BY_3, SQRT_3, TOL_J2, TWO_BY_3, -}; +use crate::{deriv1_invariant_jj2, deriv1_invariant_jj3, t2_dyad_t2, t2_odyad_t2, t2_qsd_t2, t2_ssd}; +use crate::{Mandel, Tensor2, Tensor4}; +use crate::{ONE_BY_3, SQRT_3, TOL_J2, TWO_BY_3}; use russell_lab::{mat_add, mat_mat_mul, mat_update}; /// Calculates the derivative of the inverse tensor w.r.t. the defining Tensor2 @@ -28,13 +27,17 @@ use russell_lab::{mat_add, mat_mat_mul, mat_update}; /// /// * `ai` -- the pre-computed inverse tensor /// * `a` -- the defining tensor -pub fn deriv_inverse_tensor(dai_da: &mut Tensor4, ai: &Tensor2) -> Result<(), StrError> { +/// +/// # Panics +/// +/// A panic will occur if the tensors have different [Mandel]. +pub fn deriv_inverse_tensor(dai_da: &mut Tensor4, ai: &Tensor2) { let mut ai_t = ai.clone(); - ai.transpose(&mut ai_t).unwrap(); - t2_odyad_t2(dai_da, -1.0, &ai, &ai_t) + ai.transpose(&mut ai_t); + t2_odyad_t2(dai_da, -1.0, &ai, &ai_t); } -/// Calculates the derivative of the inverse tensor w.r.t. the defining Tensor2 (symmetric) +/// Calculates the derivative of the inverse tensor w.r.t. a symmetric Tensor2 /// /// ```text /// dA⁻¹ 1 _ @@ -54,23 +57,23 @@ pub fn deriv_inverse_tensor(dai_da: &mut Tensor4, ai: &Tensor2) -> Result<(), St /// /// ## Output /// -/// * `dai_da` -- the derivative of the inverse tensor (must be Symmetric) +/// * `dai_da` -- the derivative of the inverse tensor; it must be [Mandel::Symmetric] /// /// ## Input /// -/// * `ai` -- the pre-computed inverse tensor (must be Symmetric or Symmetric2D) -pub fn deriv_inverse_tensor_sym(dai_da: &mut Tensor4, ai: &Tensor2) -> Result<(), StrError> { - if ai.mandel() == Mandel::General { - return Err("ai tensor must be Symmetric or Symmetric2D"); - } - if dai_da.mandel() != Mandel::Symmetric { - return Err("dai_da tensor must be Symmetric"); - } - t2_ssd(dai_da, -0.5, ai).unwrap(); - Ok(()) +/// * `ai` -- the pre-computed inverse tensor; it must be symmetric +/// +/// # Panics +/// +/// 1. A panic will occur if `dai_da` is not [Mandel::Symmetric] +/// 2. A panic will occur if `ai` is not symmetric +pub fn deriv_inverse_tensor_sym(dai_da: &mut Tensor4, ai: &Tensor2) { + assert_eq!(dai_da.mandel, Mandel::Symmetric); + assert!(ai.mandel.symmetric()); + t2_ssd(dai_da, -0.5, ai); } -/// Calculates the derivative of the squared tensor w.r.t. the defining Tensor2 +/// Calculates the derivative of the squared tensor w.r.t. a Tensor2 /// /// ```text /// dA² _ _ @@ -86,26 +89,24 @@ pub fn deriv_inverse_tensor_sym(dai_da: &mut Tensor4, ai: &Tensor2) -> Result<() /// ∂Aₖₗ /// ``` /// +/// **Note:** Two temporary Tensor2 and a Tensor4 are allocated in this function. +/// /// ## Output /// -/// * `da2_da` -- the derivative of the squared tensor (must be General) -/// * `ii` -- second-order identity tensor +/// * `da2_da` -- the derivative of the squared tensor; it must be [Mandel::General] +/// * `ii` -- second-order identity tensor; must have the same [Mandel] as tensor `a` /// /// ## Input /// -/// * `a` -- the given tensor +/// * `a` -- the second-order tensor; must have the same [Mandel] as tensor `ii` /// -/// ## Note +/// # Panics /// -/// Two temporary Tensor2 and a Tensor4 are allocated in this function. -pub fn deriv_squared_tensor(da2_da: &mut Tensor4, ii: &mut Tensor2, a: &Tensor2) -> Result<(), StrError> { - if da2_da.mandel() != Mandel::General { - return Err("da2_da tensor must be General"); - } - let dim = a.vec.dim(); - if ii.vec.dim() != dim { - return Err("ii tensor is incompatible"); - } +/// 1. A panic will occur if `da2_da` is not [Mandel::General] +/// 2. A panic will occur if `ii` has a different [Mandel] than `a` +pub fn deriv_squared_tensor(da2_da: &mut Tensor4, ii: &mut Tensor2, a: &Tensor2) { + assert_eq!(da2_da.mandel, Mandel::General); + assert_eq!(ii.mandel, a.mandel); // set identity tensor ii.clear(); @@ -114,13 +115,13 @@ pub fn deriv_squared_tensor(da2_da: &mut Tensor4, ii: &mut Tensor2, a: &Tensor2) ii.vec[2] = 1.0; // compute A odyad I - t2_odyad_t2(da2_da, 1.0, &a, &ii).unwrap(); + t2_odyad_t2(da2_da, 1.0, &a, &ii); // compute I odyad transpose(A) let mut at = a.clone(); - a.transpose(&mut at).unwrap(); + a.transpose(&mut at); let mut ii_odyad_at = Tensor4::new(Mandel::General); - t2_odyad_t2(&mut ii_odyad_at, 1.0, &ii, &at).unwrap(); + t2_odyad_t2(&mut ii_odyad_at, 1.0, &ii, &at); // compute A odyad I + I odyad transpose(A) for m in 0..9 { @@ -128,10 +129,9 @@ pub fn deriv_squared_tensor(da2_da: &mut Tensor4, ii: &mut Tensor2, a: &Tensor2) da2_da.mat.set(m, n, da2_da.mat.get(m, n) + ii_odyad_at.mat.get(m, n)); } } - Ok(()) } -/// Calculates the derivative of the squared tensor w.r.t. the defining Tensor2 (symmetric) +/// Calculates the derivative of the squared tensor w.r.t. a symmetric Tensor2 /// /// ```text /// dA² 1 _ _ @@ -151,29 +151,27 @@ pub fn deriv_squared_tensor(da2_da: &mut Tensor4, ii: &mut Tensor2, a: &Tensor2) /// /// ## Output /// -/// * `da2_da` -- the derivative of the squared tensor (must be Symmetric) -/// * `ii` -- second-order identity tensor +/// * `da2_da` -- the derivative of the squared tensor; it must be [Mandel::Symmetric] +/// * `ii` -- second-order identity tensor; must have the same [Mandel] as tensor `a` /// /// ## Input /// -/// * `a` -- the given tensor (must be Symmetric or Symmetric2D) -pub fn deriv_squared_tensor_sym(da2_da: &mut Tensor4, ii: &mut Tensor2, a: &Tensor2) -> Result<(), StrError> { - if a.mandel() == Mandel::General { - return Err("'a' tensor must be Symmetric or Symmetric2D"); - } - if da2_da.mandel() != Mandel::Symmetric { - return Err("da2_da tensor must be Symmetric"); - } - let dim = a.vec.dim(); - if ii.vec.dim() != dim { - return Err("ii tensor is incompatible"); - } +/// * `a` -- the second-order tensor; it must be symmetric +/// +/// # Panics +/// +/// 1. A panic will occur if `da2_da` is not [Mandel::Symmetric] +/// 2. A panic will occur if `a` is not symmetric +/// 3. A panic will occur if `ii` has a different [Mandel] than `a` +pub fn deriv_squared_tensor_sym(da2_da: &mut Tensor4, ii: &mut Tensor2, a: &Tensor2) { + assert_eq!(da2_da.mandel, Mandel::Symmetric); + assert!(a.mandel.symmetric()); + assert_eq!(ii.mandel, a.mandel); ii.clear(); ii.vec[0] = 1.0; ii.vec[1] = 1.0; ii.vec[2] = 1.0; - t2_qsd_t2(da2_da, 0.5, a, &ii).unwrap(); - Ok(()) + t2_qsd_t2(da2_da, 0.5, a, &ii); } /// Calculates the second derivative of the J2 invariant w.r.t. the stress tensor @@ -186,18 +184,19 @@ pub fn deriv_squared_tensor_sym(da2_da: &mut Tensor4, ii: &mut Tensor2, a: &Tens /// /// ## Output /// -/// * `d2` -- the second derivative of J2 (must be Symmetric) +/// * `d2` -- the second derivative of J2; it must be [Mandel::Symmetric] /// /// ## Input /// -/// * `sigma` -- the given tensor (must be Symmetric or Symmetric2D) -pub fn deriv2_invariant_jj2(d2: &mut Tensor4, sigma: &Tensor2) -> Result<(), StrError> { - if sigma.mandel() == Mandel::General { - return Err("sigma tensor must be Symmetric or Symmetric2D"); - } - if d2.mandel() != Mandel::Symmetric { - return Err("d2 tensor must be Symmetric"); - } +/// * `sigma` -- the given tensor; it must be symmetric +/// +/// # Panics +/// +/// 1. A panic will occur if `d2` is not [Mandel::Symmetric] +/// 2. A panic will occur if `sigma` is not symmetric +pub fn deriv2_invariant_jj2(d2: &mut Tensor4, sigma: &Tensor2) { + assert_eq!(d2.mandel, Mandel::Symmetric); + assert!(sigma.mandel.symmetric()); d2.mat.fill(0.0); d2.mat.set(0, 0, TWO_BY_3); d2.mat.set(0, 1, -ONE_BY_3); @@ -211,7 +210,6 @@ pub fn deriv2_invariant_jj2(d2: &mut Tensor4, sigma: &Tensor2) -> Result<(), Str d2.mat.set(3, 3, 1.0); d2.mat.set(4, 4, 1.0); d2.mat.set(5, 5, 1.0); - Ok(()) } /// Holds auxiliary data to compute the second derivative of the J3 invariant @@ -234,17 +232,14 @@ pub struct AuxDeriv2InvariantJ3 { impl AuxDeriv2InvariantJ3 { /// Returns a new instance - pub fn new(mandel: Mandel) -> Result { - if mandel == Mandel::General { - return Err("mandel must be Symmetric or Symmetric2D"); - } - Ok(AuxDeriv2InvariantJ3 { - s: Tensor2::new(mandel), - ii: Tensor2::identity(mandel), + pub fn new() -> Self { + AuxDeriv2InvariantJ3 { + s: Tensor2::new(Mandel::Symmetric), + ii: Tensor2::identity(Mandel::Symmetric), psd: Tensor4::constant_pp_symdev(true), aa: Tensor4::new(Mandel::Symmetric), bb: Tensor4::new(Mandel::Symmetric), - }) + } } } @@ -262,24 +257,29 @@ impl AuxDeriv2InvariantJ3 { /// /// ## Output /// -/// * `d2` -- the second derivative of J3 (must be Symmetric) +/// * `d2` -- the second derivative of J3; it must be [Mandel::Symmetric] /// /// ## Input /// -/// * `sigma` -- the given tensor (must be Symmetric or Symmetric2D) -pub fn deriv2_invariant_jj3(d2: &mut Tensor4, aux: &mut AuxDeriv2InvariantJ3, sigma: &Tensor2) -> Result<(), StrError> { - if sigma.mandel() != aux.s.mandel() { - return Err("sigma tensor is incompatible"); - } - if d2.mandel() != Mandel::Symmetric { - return Err("d2 tensor must be Symmetric"); - } - sigma.deviator(&mut aux.s).unwrap(); - t2_qsd_t2(&mut aux.aa, 0.5, &mut aux.s, &aux.ii).unwrap(); // aa := 0.5 qsd(s,I) - t2_dyad_t2(&mut aux.bb, -TWO_BY_3, &aux.ii, &aux.s).unwrap(); // bb := -⅔ I ⊗ s +/// * `sigma` -- the given tensor; it must be symmetric +/// +/// # Panics +/// +/// 1. A panic will occur if `d2` is not [Mandel::Symmetric] +/// 2. A panic will occur if `sigma` is not symmetric +pub fn deriv2_invariant_jj3(d2: &mut Tensor4, aux: &mut AuxDeriv2InvariantJ3, sigma: &Tensor2) { + assert_eq!(d2.mandel, Mandel::Symmetric); + assert!(sigma.mandel.symmetric()); + if sigma.mandel == Mandel::Symmetric2D { + let sig3d = sigma.sym2d_as_symmetric(); + sig3d.deviator(&mut aux.s); + } else { + sigma.deviator(&mut aux.s); + } + t2_qsd_t2(&mut aux.aa, 0.5, &mut aux.s, &aux.ii); // aa := 0.5 qsd(s,I) + t2_dyad_t2(&mut aux.bb, -TWO_BY_3, &aux.ii, &aux.s); // bb := -⅔ I ⊗ s mat_mat_mul(&mut d2.mat, 1.0, &aux.aa.mat, &aux.psd.mat, 0.0).unwrap(); // d2 := 0.5 qsd(s,I) : Psd mat_update(&mut d2.mat, 1.0, &aux.bb.mat).unwrap(); // d2 += -⅔ I ⊗ s - Ok(()) } /// Holds auxiliary data to compute the second derivative of the deviatoric invariant @@ -293,14 +293,11 @@ pub struct AuxDeriv2InvariantSigmaD { impl AuxDeriv2InvariantSigmaD { /// Returns a new instance - pub fn new(mandel: Mandel) -> Result { - if mandel == Mandel::General { - return Err("mandel must be Symmetric or Symmetric2D"); - } - Ok(AuxDeriv2InvariantSigmaD { - d1_jj2: Tensor2::new(mandel), + pub fn new() -> Self { + AuxDeriv2InvariantSigmaD { + d1_jj2: Tensor2::new(Mandel::Symmetric), d2_jj2: Tensor4::new(Mandel::Symmetric), - }) + } } } @@ -322,37 +319,37 @@ impl AuxDeriv2InvariantSigmaD { /// /// ## Output /// -/// * `d2` -- the second derivative of l (must be Symmetric) +/// * If `J2 > TOL_J2`, returns `J2`; otherwise, returns None. +/// * `d2` -- the second derivative of `l`; it must be [Mandel::Symmetric] /// /// ## Input /// -/// * `sigma` -- the given tensor (must be Symmetric or Symmetric2D) +/// * `sigma` -- the given tensor; it must be symmetric /// -/// # Returns +/// # Panics /// -/// If `J2 > TOL_J2`, returns `J2` and the derivative in `d2`. Otherwise, returns None. -pub fn deriv2_invariant_sigma_d( - d2: &mut Tensor4, - aux: &mut AuxDeriv2InvariantSigmaD, - sigma: &Tensor2, -) -> Result, StrError> { - if sigma.mandel() != aux.d1_jj2.mandel() { - return Err("sigma tensor is incompatible"); - } - if d2.mandel() != Mandel::Symmetric { - return Err("d2 tensor must be Symmetric"); - } +/// 1. A panic will occur if `d2` is not [Mandel::Symmetric] +/// 2. A panic will occur if `sigma` is not symmetric +pub fn deriv2_invariant_sigma_d(d2: &mut Tensor4, aux: &mut AuxDeriv2InvariantSigmaD, sigma: &Tensor2) -> Option { + assert_eq!(d2.mandel, Mandel::Symmetric); + assert!(sigma.mandel.symmetric()); let jj2 = sigma.invariant_jj2(); if jj2 > TOL_J2 { let a = 0.5 * SQRT_3 / f64::powf(jj2, 0.5); let b = 0.25 * SQRT_3 / f64::powf(jj2, 1.5); - deriv1_invariant_jj2(&mut aux.d1_jj2, sigma).unwrap(); - deriv2_invariant_jj2(&mut aux.d2_jj2, sigma).unwrap(); - t2_dyad_t2(d2, -b, &aux.d1_jj2, &aux.d1_jj2).unwrap(); + if sigma.mandel == Mandel::Symmetric2D { + let sig3d = sigma.sym2d_as_symmetric(); + deriv1_invariant_jj2(&mut aux.d1_jj2, &sig3d); + deriv2_invariant_jj2(&mut aux.d2_jj2, &sig3d); + } else { + deriv1_invariant_jj2(&mut aux.d1_jj2, sigma); + deriv2_invariant_jj2(&mut aux.d2_jj2, sigma); + } + t2_dyad_t2(d2, -b, &aux.d1_jj2, &aux.d1_jj2); mat_update(&mut d2.mat, a, &aux.d2_jj2.mat).unwrap(); - return Ok(Some(jj2)); + return Some(jj2); } - Ok(None) + None } /// Holds auxiliary data to compute the second derivative of the Lode invariant @@ -387,21 +384,18 @@ pub struct AuxDeriv2InvariantLode { impl AuxDeriv2InvariantLode { /// Returns a new instance - pub fn new(mandel: Mandel) -> Result { - if mandel == Mandel::General { - return Err("mandel must be Symmetric or Symmetric2D"); - } - Ok(AuxDeriv2InvariantLode { - aux_jj3: AuxDeriv2InvariantJ3::new(mandel).unwrap(), - s: Tensor2::new(mandel), - d1_jj2: Tensor2::new(mandel), - d1_jj3: Tensor2::new(mandel), + pub fn new() -> Self { + AuxDeriv2InvariantLode { + aux_jj3: AuxDeriv2InvariantJ3::new(), + s: Tensor2::new(Mandel::Symmetric), + d1_jj2: Tensor2::new(Mandel::Symmetric), + d1_jj3: Tensor2::new(Mandel::Symmetric), d2_jj2: Tensor4::new(Mandel::Symmetric), d2_jj3: Tensor4::new(Mandel::Symmetric), d1_jj2_dy_d1_jj2: Tensor4::new(Mandel::Symmetric), d1_jj2_dy_d1_jj3: Tensor4::new(Mandel::Symmetric), d1_jj3_dy_d1_jj2: Tensor4::new(Mandel::Symmetric), - }) + } } } @@ -423,60 +417,57 @@ impl AuxDeriv2InvariantLode { /// /// ## Output /// -/// * `d2` -- the second derivative of l (must be Symmetric) +/// * If `J2 > TOL_J2`, returns `J2`; otherwise, returns None. +/// * `d2` -- the second derivative of `l`; it must be [Mandel::Symmetric] /// /// ## Input /// -/// * `sigma` -- the given tensor (must be Symmetric or Symmetric2D) +/// * `sigma` -- the given tensor; it must be symmetric /// -/// # Returns +/// # Panics /// -/// If `J2 > TOL_J2`, returns `J2` and the derivative in `d2`. Otherwise, returns None. -pub fn deriv2_invariant_lode( - d2: &mut Tensor4, - aux: &mut AuxDeriv2InvariantLode, - sigma: &Tensor2, -) -> Result, StrError> { - if sigma.mandel() != aux.s.mandel() { - return Err("sigma tensor is incompatible"); - } - if d2.mandel() != Mandel::Symmetric { - return Err("d2 tensor must be Symmetric"); - } +/// 1. A panic will occur if `d2` is not [Mandel::Symmetric] +/// 2. A panic will occur if `sigma` is not symmetric +pub fn deriv2_invariant_lode(d2: &mut Tensor4, aux: &mut AuxDeriv2InvariantLode, sigma: &Tensor2) -> Option { + assert_eq!(d2.mandel, Mandel::Symmetric); + assert!(sigma.mandel.symmetric()); let jj2 = sigma.invariant_jj2(); if jj2 > TOL_J2 { let jj3 = sigma.invariant_jj3(); let a = 1.5 * SQRT_3 / f64::powf(jj2, 1.5); let b = 2.25 * SQRT_3 / f64::powf(jj2, 2.5); let c = 5.625 * SQRT_3 / f64::powf(jj2, 3.5); - deriv1_invariant_jj2(&mut aux.d1_jj2, sigma).unwrap(); - deriv1_invariant_jj3(&mut aux.d1_jj3, &mut aux.s, sigma).unwrap(); - deriv2_invariant_jj2(&mut aux.d2_jj2, sigma).unwrap(); - deriv2_invariant_jj3(&mut aux.d2_jj3, &mut aux.aux_jj3, sigma).unwrap(); - t2_dyad_t2(&mut aux.d1_jj2_dy_d1_jj2, 1.0, &aux.d1_jj2, &aux.d1_jj2).unwrap(); - t2_dyad_t2(&mut aux.d1_jj2_dy_d1_jj3, 1.0, &aux.d1_jj2, &aux.d1_jj3).unwrap(); - t2_dyad_t2(&mut aux.d1_jj3_dy_d1_jj2, 1.0, &aux.d1_jj3, &aux.d1_jj2).unwrap(); + if sigma.mandel == Mandel::Symmetric2D { + let sig3d = sigma.sym2d_as_symmetric(); + deriv1_invariant_jj2(&mut aux.d1_jj2, &sig3d); + deriv1_invariant_jj3(&mut aux.d1_jj3, &mut aux.s, &sig3d); + deriv2_invariant_jj2(&mut aux.d2_jj2, &sig3d); + deriv2_invariant_jj3(&mut aux.d2_jj3, &mut aux.aux_jj3, &sig3d); + } else { + deriv1_invariant_jj2(&mut aux.d1_jj2, sigma); + deriv1_invariant_jj3(&mut aux.d1_jj3, &mut aux.s, sigma); + deriv2_invariant_jj2(&mut aux.d2_jj2, sigma); + deriv2_invariant_jj3(&mut aux.d2_jj3, &mut aux.aux_jj3, sigma); + } + t2_dyad_t2(&mut aux.d1_jj2_dy_d1_jj2, 1.0, &aux.d1_jj2, &aux.d1_jj2); + t2_dyad_t2(&mut aux.d1_jj2_dy_d1_jj3, 1.0, &aux.d1_jj2, &aux.d1_jj3); + t2_dyad_t2(&mut aux.d1_jj3_dy_d1_jj2, 1.0, &aux.d1_jj3, &aux.d1_jj2); mat_add(&mut d2.mat, a, &aux.d2_jj3.mat, -b * jj3, &aux.d2_jj2.mat).unwrap(); mat_update(&mut d2.mat, -b, &aux.d1_jj3_dy_d1_jj2.mat).unwrap(); mat_update(&mut d2.mat, -b, &aux.d1_jj2_dy_d1_jj3.mat).unwrap(); mat_update(&mut d2.mat, c * jj3, &aux.d1_jj2_dy_d1_jj2.mat).unwrap(); - return Ok(Some(jj2)); + return Some(jj2); } - Ok(None) + None } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #[cfg(test)] mod tests { - use super::{Tensor2, Tensor4}; - use crate::{ - deriv1_invariant_jj2, deriv1_invariant_jj3, deriv1_invariant_lode, deriv1_invariant_sigma_d, - deriv2_invariant_jj2, deriv2_invariant_jj3, deriv2_invariant_lode, deriv2_invariant_sigma_d, - deriv_inverse_tensor, deriv_inverse_tensor_sym, deriv_squared_tensor, deriv_squared_tensor_sym, - AuxDeriv2InvariantJ3, AuxDeriv2InvariantLode, AuxDeriv2InvariantSigmaD, Mandel, SamplesTensor2, StrError, - MN_TO_IJKL, SQRT_2, - }; + use super::*; + use crate::{deriv1_invariant_lode, deriv1_invariant_sigma_d}; + use crate::{SamplesTensor2, StrError, MN_TO_IJKL, SQRT_2}; use russell_lab::{approx_eq, deriv1_central5, mat_approx_eq, Matrix}; // Holds arguments for numerical differentiation corresponding to ∂aiᵢⱼ/∂aₖₗ @@ -502,7 +493,7 @@ mod tests { let original = args.a_mat.get(args.k, args.l); args.a_mat.set(args.k, args.l, x); args.a.set_matrix(&args.a_mat).unwrap(); - args.a.inverse(&mut args.ai, 1e-10).unwrap().unwrap(); + args.a.inverse(&mut args.ai, 1e-10).unwrap(); args.a_mat.set(args.k, args.l, original); Ok(args.ai.get(args.i, args.j)) } @@ -510,14 +501,14 @@ mod tests { fn component_of_inverse_mandel(x: f64, args: &mut ArgsNumDerivInverseMandel) -> Result { let original = args.a.vec[args.n]; args.a.vec[args.n] = x; - args.a.inverse(&mut args.ai, 1e-10).unwrap().unwrap(); + args.a.inverse(&mut args.ai, 1e-10).unwrap(); args.a.vec[args.n] = original; Ok(args.ai.vec[args.m]) } fn numerical_deriv_inverse(a: &Tensor2) -> Matrix { let mut args = ArgsNumDerivInverse { - a_mat: a.to_matrix(), + a_mat: a.as_matrix(), a: Tensor2::new(Mandel::General), ai: Tensor2::new(Mandel::General), i: 0, @@ -539,7 +530,7 @@ mod tests { fn numerical_deriv_inverse_mandel(a: &Tensor2) -> Matrix { let mut args = ArgsNumDerivInverseMandel { - a: a.to_general(), + a: a.as_general(), ai: Tensor2::new(Mandel::General), m: 0, n: 0, @@ -554,7 +545,7 @@ mod tests { num_deriv.mat.set(m, n, res); } } - num_deriv.to_matrix() + num_deriv.as_matrix() } fn numerical_deriv_inverse_sym_mandel(a: &Tensor2) -> Matrix { @@ -582,21 +573,21 @@ mod tests { num_deriv.mat.set(m, n, res); } } - num_deriv.to_matrix() + num_deriv.as_matrix() } fn check_deriv_inverse(a: &Tensor2, tol: f64) { // compute inverse tensor - let mut ai = Tensor2::new(a.mandel()); - a.inverse(&mut ai, 1e-10).unwrap().unwrap(); + let mut ai = Tensor2::new(a.mandel); + a.inverse(&mut ai, 1e-10).unwrap(); // compute analytical derivative let mut dd_ana = Tensor4::new(Mandel::General); - deriv_inverse_tensor(&mut dd_ana, &ai).unwrap(); + deriv_inverse_tensor(&mut dd_ana, &ai); // check using index expression - let arr = dd_ana.to_array(); - let mat = ai.to_matrix(); + let arr = dd_ana.as_array(); + let mat = ai.as_matrix(); for i in 0..3 { for j in 0..3 { for k in 0..3 { @@ -608,7 +599,7 @@ mod tests { } // check using numerical derivative - let ana = dd_ana.to_matrix(); + let ana = dd_ana.as_matrix(); let num = numerical_deriv_inverse(&a); let num_mandel = numerical_deriv_inverse_mandel(&a); mat_approx_eq(&ana, &num, tol); @@ -617,16 +608,16 @@ mod tests { fn check_deriv_inverse_sym(a: &Tensor2, tol: f64) { // compute inverse tensor - let mut ai = Tensor2::new(a.mandel()); - a.inverse(&mut ai, 1e-10).unwrap().unwrap(); + let mut ai = Tensor2::new(a.mandel); + a.inverse(&mut ai, 1e-10).unwrap(); // compute analytical derivative let mut dd_ana = Tensor4::new(Mandel::Symmetric); - deriv_inverse_tensor_sym(&mut dd_ana, &ai).unwrap(); + deriv_inverse_tensor_sym(&mut dd_ana, &ai); // check using index expression - let arr = dd_ana.to_array(); - let mat = ai.to_matrix(); + let arr = dd_ana.as_array(); + let mat = ai.as_matrix(); for i in 0..3 { for j in 0..3 { for k in 0..3 { @@ -642,7 +633,7 @@ mod tests { } // check using numerical derivative - let ana = dd_ana.to_matrix(); + let ana = dd_ana.as_matrix(); let num = numerical_deriv_inverse_sym_mandel(&a); mat_approx_eq(&ana, &num, tol); } @@ -665,22 +656,6 @@ mod tests { check_deriv_inverse(&a, 1e-12); } - #[test] - fn deriv_inverse_tensor_sym_captures_errors() { - let ai = Tensor2::new(Mandel::General); - let mut dai_da = Tensor4::new(Mandel::Symmetric); - assert_eq!( - deriv_inverse_tensor_sym(&mut dai_da, &ai).err(), - Some("ai tensor must be Symmetric or Symmetric2D") - ); - let ai = Tensor2::new(Mandel::Symmetric2D); - let mut dai_da = Tensor4::new(Mandel::Symmetric2D); - assert_eq!( - deriv_inverse_tensor_sym(&mut dai_da, &ai).err(), - Some("dai_da tensor must be Symmetric") - ); - } - #[test] fn deriv_inverse_tensor_sym_works() { // symmetric @@ -719,7 +694,7 @@ mod tests { let original = args.a_mat.get(args.k, args.l); args.a_mat.set(args.k, args.l, x); args.a.set_matrix(&args.a_mat).unwrap(); - args.a.squared(&mut args.a2).unwrap(); + args.a.squared(&mut args.a2); args.a_mat.set(args.k, args.l, original); Ok(args.a2.get(args.i, args.j)) } @@ -727,14 +702,14 @@ mod tests { fn component_of_squared_mandel(x: f64, args: &mut ArgsNumDerivSquaredMandel) -> Result { let original = args.a.vec[args.n]; args.a.vec[args.n] = x; - args.a.squared(&mut args.a2).unwrap(); + args.a.squared(&mut args.a2); args.a.vec[args.n] = original; Ok(args.a2.vec[args.m]) } fn numerical_deriv_squared(a: &Tensor2) -> Matrix { let mut args = ArgsNumDerivSquared { - a_mat: a.to_matrix(), + a_mat: a.as_matrix(), a: Tensor2::new(Mandel::General), a2: Tensor2::new(Mandel::General), i: 0, @@ -756,7 +731,7 @@ mod tests { fn numerical_deriv_squared_mandel(a: &Tensor2) -> Matrix { let mut args = ArgsNumDerivSquaredMandel { - a: a.to_general(), + a: a.as_general(), a2: Tensor2::new(Mandel::General), m: 0, n: 0, @@ -771,7 +746,7 @@ mod tests { num_deriv.mat.set(m, n, res); } } - num_deriv.to_matrix() + num_deriv.as_matrix() } fn numerical_deriv_squared_sym_mandel(a: &Tensor2) -> Matrix { @@ -799,18 +774,18 @@ mod tests { num_deriv.mat.set(m, n, res); } } - num_deriv.to_matrix() + num_deriv.as_matrix() } fn check_deriv_squared(a: &Tensor2, tol: f64) { // compute analytical derivative let mut dd_ana = Tensor4::new(Mandel::General); - let mut ii = Tensor2::new(a.mandel()); - deriv_squared_tensor(&mut dd_ana, &mut ii, &a).unwrap(); + let mut ii = Tensor2::new(a.mandel); + deriv_squared_tensor(&mut dd_ana, &mut ii, &a); // check using index expression - let arr = dd_ana.to_array(); - let mat = a.to_matrix(); + let arr = dd_ana.as_array(); + let mat = a.as_matrix(); let del = Matrix::diagonal(&[1.0, 1.0, 1.0]); for i in 0..3 { for j in 0..3 { @@ -827,7 +802,7 @@ mod tests { } // check using numerical derivative - let ana = dd_ana.to_matrix(); + let ana = dd_ana.as_matrix(); let num = numerical_deriv_squared(&a); let num_mandel = numerical_deriv_squared_mandel(&a); mat_approx_eq(&ana, &num, tol); @@ -837,12 +812,12 @@ mod tests { fn check_deriv_squared_sym(a: &Tensor2, tol: f64) { // compute analytical derivative let mut dd_ana = Tensor4::new(Mandel::Symmetric); - let mut ii = Tensor2::new(a.mandel()); - deriv_squared_tensor_sym(&mut dd_ana, &mut ii, &a).unwrap(); + let mut ii = Tensor2::new(a.mandel); + deriv_squared_tensor_sym(&mut dd_ana, &mut ii, &a); // check using index expression - let arr = dd_ana.to_array(); - let mat = a.to_matrix(); + let arr = dd_ana.as_array(); + let mat = a.as_matrix(); let del = Matrix::diagonal(&[1.0, 1.0, 1.0]); for i in 0..3 { for j in 0..3 { @@ -862,28 +837,11 @@ mod tests { } // check using numerical derivative - let ana = dd_ana.to_matrix(); + let ana = dd_ana.as_matrix(); let num = numerical_deriv_squared_sym_mandel(&a); mat_approx_eq(&ana, &num, tol); } - #[test] - fn deriv_squared_tensor_captures_errors() { - let a = Tensor2::new(Mandel::General); - let mut ii = Tensor2::new(Mandel::General); - let mut da2_da = Tensor4::new(Mandel::Symmetric); - assert_eq!( - deriv_squared_tensor(&mut da2_da, &mut ii, &a).err(), - Some("da2_da tensor must be General") - ); - let mut da2_da = Tensor4::new(Mandel::General); - let mut ii = Tensor2::new(Mandel::Symmetric); - assert_eq!( - deriv_squared_tensor(&mut da2_da, &mut ii, &a).err(), - Some("ii tensor is incompatible") - ); - } - #[test] fn deriv_squared_tensor_works() { // general @@ -898,33 +856,10 @@ mod tests { // symmetric 2d let s = &SamplesTensor2::TENSOR_Y; - let a = Tensor2::from_matrix(&s.matrix, Mandel::General).unwrap(); + let a = Tensor2::from_matrix(&s.matrix, Mandel::Symmetric2D).unwrap(); check_deriv_squared(&a, 1e-10); } - #[test] - fn deriv_squared_tensor_sym_captures_errors() { - let a = Tensor2::new(Mandel::General); - let mut ii = Tensor2::new(Mandel::General); - let mut da2_da = Tensor4::new(Mandel::Symmetric); - assert_eq!( - deriv_squared_tensor_sym(&mut da2_da, &mut ii, &a).err(), - Some("'a' tensor must be Symmetric or Symmetric2D") - ); - let a = Tensor2::new(Mandel::Symmetric2D); - let mut da2_da = Tensor4::new(Mandel::Symmetric2D); - assert_eq!( - deriv_squared_tensor_sym(&mut da2_da, &mut ii, &a).err(), - Some("da2_da tensor must be Symmetric") - ); - let mut da2_da = Tensor4::new(Mandel::Symmetric); - let mut ii = Tensor2::new(Mandel::Symmetric); - assert_eq!( - deriv_squared_tensor_sym(&mut da2_da, &mut ii, &a).err(), - Some("ii tensor is incompatible") - ); - } - #[test] fn deriv_squared_tensor_sym_works() { // symmetric @@ -962,16 +897,16 @@ mod tests { args.sigma.vec[args.n] = x; match args.inv { Invariant::J2 => { - deriv1_invariant_jj2(&mut args.d1, &args.sigma).unwrap(); + deriv1_invariant_jj2(&mut args.d1, &args.sigma); } Invariant::J3 => { - deriv1_invariant_jj3(&mut args.d1, &mut args.s, &args.sigma).unwrap(); + deriv1_invariant_jj3(&mut args.d1, &mut args.s, &args.sigma); } Invariant::SigmaD => { - deriv1_invariant_sigma_d(&mut args.d1, &args.sigma).unwrap().unwrap(); + deriv1_invariant_sigma_d(&mut args.d1, &args.sigma).unwrap(); } Invariant::Lode => { - deriv1_invariant_lode(&mut args.d1, &mut args.s, &args.sigma).unwrap(); + deriv1_invariant_lode(&mut args.d1, &mut args.s, &args.sigma); } }; args.sigma.vec[args.n] = original; @@ -1005,20 +940,20 @@ mod tests { num_deriv.mat.set(m, n, res); } } - num_deriv.to_matrix() + num_deriv.as_matrix() } fn check_deriv2_jj2(sigma: &Tensor2, tol: f64) { // compute analytical derivative let mut dd2_ana = Tensor4::new(Mandel::Symmetric); - deriv2_invariant_jj2(&mut dd2_ana, &sigma).unwrap(); + deriv2_invariant_jj2(&mut dd2_ana, &sigma); // compare with Psymdev let pp_symdev = Tensor4::constant_pp_symdev(true); mat_approx_eq(&dd2_ana.mat, &pp_symdev.mat, 1e-15); // check using numerical derivative - let ana = dd2_ana.to_matrix(); + let ana = dd2_ana.as_matrix(); let num = numerical_deriv2_inv_sym_mandel(&sigma, Invariant::J2); // println!("{}", ana); // println!("{}", num); @@ -1028,11 +963,11 @@ mod tests { fn check_deriv2_jj3(sigma: &Tensor2, tol: f64) { // compute analytical derivative let mut dd2_ana = Tensor4::new(Mandel::Symmetric); - let mut aux = AuxDeriv2InvariantJ3::new(sigma.mandel()).unwrap(); - deriv2_invariant_jj3(&mut dd2_ana, &mut aux, &sigma).unwrap(); + let mut aux = AuxDeriv2InvariantJ3::new(); + deriv2_invariant_jj3(&mut dd2_ana, &mut aux, &sigma); // check using numerical derivative - let ana = dd2_ana.to_matrix(); + let ana = dd2_ana.as_matrix(); let num = numerical_deriv2_inv_sym_mandel(&sigma, Invariant::J3); // println!("{}", ana); // println!("{}", num); @@ -1042,13 +977,11 @@ mod tests { fn check_deriv2_sigma_d(sigma: &Tensor2, tol: f64) { // compute analytical derivative let mut dd2_ana = Tensor4::new(Mandel::Symmetric); - let mut aux = AuxDeriv2InvariantSigmaD::new(sigma.mandel()).unwrap(); - deriv2_invariant_sigma_d(&mut dd2_ana, &mut aux, &sigma) - .unwrap() - .unwrap(); + let mut aux = AuxDeriv2InvariantSigmaD::new(); + deriv2_invariant_sigma_d(&mut dd2_ana, &mut aux, &sigma).unwrap(); // check using numerical derivative - let ana = dd2_ana.to_matrix(); + let ana = dd2_ana.as_matrix(); let num = numerical_deriv2_inv_sym_mandel(&sigma, Invariant::SigmaD); // println!("{}", ana); // println!("{}", num); @@ -1058,11 +991,11 @@ mod tests { fn check_deriv2_lode(sigma: &Tensor2, tol: f64) { // compute analytical derivative let mut dd2_ana = Tensor4::new(Mandel::Symmetric); - let mut aux = AuxDeriv2InvariantLode::new(sigma.mandel()).unwrap(); - deriv2_invariant_lode(&mut dd2_ana, &mut aux, &sigma).unwrap().unwrap(); + let mut aux = AuxDeriv2InvariantLode::new(); + deriv2_invariant_lode(&mut dd2_ana, &mut aux, &sigma).unwrap(); // check using numerical derivative - let ana = dd2_ana.to_matrix(); + let ana = dd2_ana.as_matrix(); let num = numerical_deriv2_inv_sym_mandel(&sigma, Invariant::Lode); // println!("{}", ana); // println!("{}", num); @@ -1080,11 +1013,11 @@ mod tests { check_deriv2_jj2(&sigma, 1e-11); // symmetric 2d - let sigma = Tensor2::from_matrix(&SamplesTensor2::TENSOR_X.matrix, Mandel::Symmetric).unwrap(); + let sigma = Tensor2::from_matrix(&SamplesTensor2::TENSOR_X.matrix, Mandel::Symmetric2D).unwrap(); check_deriv2_jj2(&sigma, 1e-11); // symmetric 2d - let sigma = Tensor2::from_matrix(&SamplesTensor2::TENSOR_Y.matrix, Mandel::Symmetric).unwrap(); + let sigma = Tensor2::from_matrix(&SamplesTensor2::TENSOR_Y.matrix, Mandel::Symmetric2D).unwrap(); check_deriv2_jj2(&sigma, 1e-11); // zero @@ -1107,11 +1040,11 @@ mod tests { check_deriv2_jj3(&sigma, 1e-10); // symmetric 2d - let sigma = Tensor2::from_matrix(&SamplesTensor2::TENSOR_X.matrix, Mandel::Symmetric).unwrap(); + let sigma = Tensor2::from_matrix(&SamplesTensor2::TENSOR_X.matrix, Mandel::Symmetric2D).unwrap(); check_deriv2_jj3(&sigma, 1e-10); // symmetric 2d - let sigma = Tensor2::from_matrix(&SamplesTensor2::TENSOR_Y.matrix, Mandel::Symmetric).unwrap(); + let sigma = Tensor2::from_matrix(&SamplesTensor2::TENSOR_Y.matrix, Mandel::Symmetric2D).unwrap(); check_deriv2_jj3(&sigma, 1e-10); // zero @@ -1128,8 +1061,8 @@ mod tests { // identity let sigma = Tensor2::from_matrix(&SamplesTensor2::TENSOR_I.matrix, Mandel::Symmetric).unwrap(); let mut d2 = Tensor4::new(Mandel::Symmetric); - let mut aux = AuxDeriv2InvariantSigmaD::new(sigma.mandel()).unwrap(); - assert_eq!(deriv2_invariant_sigma_d(&mut d2, &mut aux, &sigma).unwrap(), None); + let mut aux = AuxDeriv2InvariantSigmaD::new(); + assert_eq!(deriv2_invariant_sigma_d(&mut d2, &mut aux, &sigma), None); } #[test] @@ -1143,11 +1076,11 @@ mod tests { check_deriv2_sigma_d(&sigma, 1e-11); // symmetric 2d - let sigma = Tensor2::from_matrix(&SamplesTensor2::TENSOR_X.matrix, Mandel::Symmetric).unwrap(); + let sigma = Tensor2::from_matrix(&SamplesTensor2::TENSOR_X.matrix, Mandel::Symmetric2D).unwrap(); check_deriv2_sigma_d(&sigma, 1e-11); // symmetric 2d - let sigma = Tensor2::from_matrix(&SamplesTensor2::TENSOR_Y.matrix, Mandel::Symmetric).unwrap(); + let sigma = Tensor2::from_matrix(&SamplesTensor2::TENSOR_Y.matrix, Mandel::Symmetric2D).unwrap(); check_deriv2_sigma_d(&sigma, 1e-11); } @@ -1156,8 +1089,8 @@ mod tests { // identity let sigma = Tensor2::from_matrix(&SamplesTensor2::TENSOR_I.matrix, Mandel::Symmetric).unwrap(); let mut d2 = Tensor4::new(Mandel::Symmetric); - let mut aux = AuxDeriv2InvariantLode::new(sigma.mandel()).unwrap(); - assert_eq!(deriv2_invariant_lode(&mut d2, &mut aux, &sigma).unwrap(), None); + let mut aux = AuxDeriv2InvariantLode::new(); + assert_eq!(deriv2_invariant_lode(&mut d2, &mut aux, &sigma), None); } #[test] @@ -1171,116 +1104,22 @@ mod tests { check_deriv2_lode(&sigma, 1e-11); // symmetric 2d - let sigma = Tensor2::from_matrix(&SamplesTensor2::TENSOR_X.matrix, Mandel::Symmetric).unwrap(); + let sigma = Tensor2::from_matrix(&SamplesTensor2::TENSOR_X.matrix, Mandel::Symmetric2D).unwrap(); check_deriv2_lode(&sigma, 1e-10); // symmetric 2d - let sigma = Tensor2::from_matrix(&SamplesTensor2::TENSOR_Y.matrix, Mandel::Symmetric).unwrap(); + let sigma = Tensor2::from_matrix(&SamplesTensor2::TENSOR_Y.matrix, Mandel::Symmetric2D).unwrap(); check_deriv2_lode(&sigma, 1e-9); } - #[test] - fn deriv2_invariant_jj2_captures_errors() { - let sigma = Tensor2::new(Mandel::General); - let mut d2 = Tensor4::new(Mandel::Symmetric); - assert_eq!( - deriv2_invariant_jj2(&mut d2, &sigma).err(), - Some("sigma tensor must be Symmetric or Symmetric2D") - ); - let sigma = Tensor2::new(Mandel::Symmetric2D); - let mut d2 = Tensor4::new(Mandel::Symmetric2D); - assert_eq!( - deriv2_invariant_jj2(&mut d2, &sigma).err(), - Some("d2 tensor must be Symmetric") - ); - } - - #[test] - fn second_deriv_jj3_handles_errors() { - assert_eq!( - AuxDeriv2InvariantJ3::new(Mandel::General).err(), - Some("mandel must be Symmetric or Symmetric2D") - ); - let mut aux = AuxDeriv2InvariantJ3::new(Mandel::Symmetric).unwrap(); - let mut d2 = Tensor4::new(Mandel::Symmetric); - let sigma = Tensor2::new(Mandel::General); - assert_eq!( - deriv2_invariant_jj3(&mut d2, &mut aux, &sigma).err(), - Some("sigma tensor is incompatible") - ); - let sigma = Tensor2::new(Mandel::Symmetric2D); - assert_eq!( - deriv2_invariant_jj3(&mut d2, &mut aux, &sigma).err(), - Some("sigma tensor is incompatible") - ); - let sigma = Tensor2::new(Mandel::Symmetric); - let mut d2 = Tensor4::new(Mandel::Symmetric2D); - assert_eq!( - deriv2_invariant_jj3(&mut d2, &mut aux, &sigma).err(), - Some("d2 tensor must be Symmetric") - ); - } - - #[test] - fn second_deriv_sigma_d_handles_errors() { - assert_eq!( - AuxDeriv2InvariantSigmaD::new(Mandel::General).err(), - Some("mandel must be Symmetric or Symmetric2D") - ); - let mut aux = AuxDeriv2InvariantSigmaD::new(Mandel::Symmetric).unwrap(); - let mut d2 = Tensor4::new(Mandel::Symmetric); - let sigma = Tensor2::new(Mandel::General); - assert_eq!( - deriv2_invariant_sigma_d(&mut d2, &mut aux, &sigma).err(), - Some("sigma tensor is incompatible") - ); - let sigma = Tensor2::new(Mandel::Symmetric2D); - assert_eq!( - deriv2_invariant_sigma_d(&mut d2, &mut aux, &sigma).err(), - Some("sigma tensor is incompatible") - ); - let sigma = Tensor2::new(Mandel::Symmetric); - let mut d2 = Tensor4::new(Mandel::Symmetric2D); - assert_eq!( - deriv2_invariant_sigma_d(&mut d2, &mut aux, &sigma).err(), - Some("d2 tensor must be Symmetric") - ); - } - - #[test] - fn second_deriv_lode_handles_errors() { - assert_eq!( - AuxDeriv2InvariantLode::new(Mandel::General).err(), - Some("mandel must be Symmetric or Symmetric2D") - ); - let mut aux = AuxDeriv2InvariantLode::new(Mandel::Symmetric).unwrap(); - let mut d2 = Tensor4::new(Mandel::Symmetric); - let sigma = Tensor2::new(Mandel::General); - assert_eq!( - deriv2_invariant_lode(&mut d2, &mut aux, &sigma).err(), - Some("sigma tensor is incompatible") - ); - let sigma = Tensor2::new(Mandel::Symmetric2D); - assert_eq!( - deriv2_invariant_lode(&mut d2, &mut aux, &sigma).err(), - Some("sigma tensor is incompatible") - ); - let sigma = Tensor2::new(Mandel::Symmetric); - let mut d2 = Tensor4::new(Mandel::Symmetric2D); - assert_eq!( - deriv2_invariant_lode(&mut d2, &mut aux, &sigma).err(), - Some("d2 tensor must be Symmetric") - ); - } - #[test] fn example_second_deriv_jj3_lode() { let sigma = Tensor2::from_matrix(&SamplesTensor2::TENSOR_U.matrix, Mandel::Symmetric).unwrap(); let mut s = Tensor2::new(Mandel::Symmetric); - sigma.deviator(&mut s).unwrap(); - let mut aux = AuxDeriv2InvariantJ3::new(sigma.mandel()).unwrap(); + sigma.deviator(&mut s); + let mut aux = AuxDeriv2InvariantJ3::new(); let mut d2 = Tensor4::new(Mandel::Symmetric); - deriv2_invariant_jj3(&mut d2, &mut aux, &sigma).unwrap(); + deriv2_invariant_jj3(&mut d2, &mut aux, &sigma); // println!("sigma =\n{:.1}", sigma.to_matrix()); // println!("sigma_mandel =\n{}", sigma.vec); @@ -1289,16 +1128,16 @@ mod tests { #[rustfmt::skip] let correct = [ - [-16.0/9.0 , 14.0/9.0 , 2.0/9.0 , 2.0*SQRT_2/3.0 , -10.0*SQRT_2/3.0 , SQRT_2 ], - [ 14.0/9.0 , 2.0/9.0 , -16.0/9.0 , 2.0*SQRT_2/3.0 , 5.0*SQRT_2/3.0 , -2.0*SQRT_2 ], - [ 2.0/9.0 , -16.0/9.0 , 14.0/9.0 , -4.0*SQRT_2/3.0 , 5.0*SQRT_2/3.0 , SQRT_2 ], - [ 2.0*SQRT_2/3.0 , 2.0*SQRT_2/3.0 , -4.0*SQRT_2/3.0 , -7.0/3.0 , 3.0 , 5.0 ], - [-10.0*SQRT_2/3.0 , 5.0*SQRT_2/3.0 , 5.0*SQRT_2/3.0 , 3.0 , 8.0/3.0 , 2.0 ], + [-16.0/9.0 , 14.0/9.0 , 2.0/9.0 , 2.0*SQRT_2/3.0 , -10.0*SQRT_2/3.0 , SQRT_2 ], + [ 14.0/9.0 , 2.0/9.0 , -16.0/9.0 , 2.0*SQRT_2/3.0 , 5.0*SQRT_2/3.0 , -2.0*SQRT_2 ], + [ 2.0/9.0 , -16.0/9.0 , 14.0/9.0 , -4.0*SQRT_2/3.0 , 5.0*SQRT_2/3.0 , SQRT_2 ], + [ 2.0*SQRT_2/3.0 , 2.0*SQRT_2/3.0 , -4.0*SQRT_2/3.0 , -7.0/3.0 , 3.0 , 5.0 ], + [-10.0*SQRT_2/3.0 , 5.0*SQRT_2/3.0 , 5.0*SQRT_2/3.0 , 3.0 , 8.0/3.0 , 2.0 ], [ SQRT_2 ,-2.0*SQRT_2 , SQRT_2 , 5.0 , 2.0 , -1.0/3.0 ], ]; mat_approx_eq(&d2.mat, &correct, 1e-15); - let mut aux = AuxDeriv2InvariantLode::new(sigma.mandel()).unwrap(); + let mut aux = AuxDeriv2InvariantLode::new(); deriv2_invariant_lode(&mut d2, &mut aux, &sigma).unwrap(); // println!("d2 = \n{}", d2.mat); @@ -1314,4 +1153,145 @@ mod tests { ]; mat_approx_eq(&d2.mat, &correct, 1e-16); } + + // check assertions ----------------------------------------------------------------------------- + + #[test] + #[should_panic] + fn deriv_inverse_tensor_panics_on_different_mandel() { + let ai = Tensor2::new(Mandel::General); + let mut dai_da = Tensor4::new(Mandel::Symmetric); // wrong; it must be the same as `ai` + deriv_inverse_tensor(&mut dai_da, &ai); + } + + #[test] + #[should_panic] + fn deriv_inverse_tensor_sym_panics_on_non_sym1() { + let ai = Tensor2::new(Mandel::Symmetric2D); + let mut dai_da = Tensor4::new(Mandel::Symmetric2D); // wrong; it must be Symmetric + deriv_inverse_tensor_sym(&mut dai_da, &ai); + } + + #[test] + #[should_panic(expected = "ai.mandel.symmetric()")] + fn deriv_inverse_tensor_sym_panics_on_non_sym2() { + let ai = Tensor2::new(Mandel::General); // wrong; it must be symmetric + let mut dai_da = Tensor4::new(Mandel::Symmetric); + deriv_inverse_tensor_sym(&mut dai_da, &ai); + } + + #[test] + #[should_panic] + fn deriv_squared_tensor_panics_on_non_general() { + let a = Tensor2::new(Mandel::Symmetric2D); + let mut ii = Tensor2::new(Mandel::Symmetric2D); + let mut da2_da = Tensor4::new(Mandel::Symmetric); // wrong; it must be general + deriv_squared_tensor(&mut da2_da, &mut ii, &a); + } + + #[test] + #[should_panic] + fn deriv_squared_tensor_panics_on_different_mandel() { + let a = Tensor2::new(Mandel::Symmetric2D); + let mut ii = Tensor2::new(Mandel::Symmetric); // wrong; it must be the same as `a` + let mut da2_da = Tensor4::new(Mandel::General); + deriv_squared_tensor(&mut da2_da, &mut ii, &a); + } + + #[test] + #[should_panic] + fn deriv_squared_tensor_sym_panics_on_non_sym1() { + let a = Tensor2::new(Mandel::Symmetric2D); + let mut ii = Tensor2::new(Mandel::Symmetric2D); + let mut da2_da = Tensor4::new(Mandel::Symmetric2D); // wrong; it must be Symmetric + deriv_squared_tensor_sym(&mut da2_da, &mut ii, &a); + } + + #[test] + #[should_panic(expected = "a.mandel.symmetric()")] + fn deriv_squared_tensor_sym_panics_on_non_sym2() { + let a = Tensor2::new(Mandel::General); // wrong; it must be symmetric + let mut ii = Tensor2::new(Mandel::General); + let mut da2_da = Tensor4::new(Mandel::Symmetric); + deriv_squared_tensor_sym(&mut da2_da, &mut ii, &a); + } + + #[test] + #[should_panic] + fn deriv_squared_tensor_sym_panics_on_different_mandel() { + let a = Tensor2::new(Mandel::Symmetric2D); + let mut ii = Tensor2::new(Mandel::Symmetric); // wrong; it must be the same as `a` + let mut da2_da = Tensor4::new(Mandel::Symmetric); + deriv_squared_tensor_sym(&mut da2_da, &mut ii, &a); + } + + #[test] + #[should_panic] + fn deriv2_invariant_jj2_panics_on_non_sym1() { + let sigma = Tensor2::new(Mandel::Symmetric2D); + let mut d2 = Tensor4::new(Mandel::Symmetric2D); // wrong; it must be Symmetric + deriv2_invariant_jj2(&mut d2, &sigma); + } + + #[test] + #[should_panic(expected = "sigma.mandel.symmetric()")] + fn deriv2_invariant_jj2_panics_on_non_sym2() { + let sigma = Tensor2::new(Mandel::General); // wrong; it must be symmetric + let mut d2 = Tensor4::new(Mandel::Symmetric); + deriv2_invariant_jj2(&mut d2, &sigma); + } + + #[test] + #[should_panic] + fn deriv2_invariant_jj3_panics_on_non_sym1() { + let mut aux = AuxDeriv2InvariantJ3::new(); + let sigma = Tensor2::new(Mandel::Symmetric); + let mut d2 = Tensor4::new(Mandel::Symmetric2D); // wrong; it must be Symmetric + deriv2_invariant_jj3(&mut d2, &mut aux, &sigma); + } + + #[test] + #[should_panic(expected = "sigma.mandel.symmetric()")] + fn deriv2_invariant_jj3_panics_on_non_sym2() { + let mut aux = AuxDeriv2InvariantJ3::new(); + let sigma = Tensor2::new(Mandel::General); // wrong; it must be symmetric + let mut d2 = Tensor4::new(Mandel::Symmetric); + deriv2_invariant_jj3(&mut d2, &mut aux, &sigma); + } + + #[test] + #[should_panic] + fn deriv2_invariant_sigma_d_panics_on_non_sym1() { + let mut aux = AuxDeriv2InvariantSigmaD::new(); + let sigma = Tensor2::new(Mandel::Symmetric2D); + let mut d2 = Tensor4::new(Mandel::Symmetric2D); // wrong; it must be Symmetric + deriv2_invariant_sigma_d(&mut d2, &mut aux, &sigma); + } + + #[test] + #[should_panic(expected = "sigma.mandel.symmetric()")] + fn deriv2_invariant_sigma_d_panics_on_non_sym2() { + let mut aux = AuxDeriv2InvariantSigmaD::new(); + let sigma = Tensor2::new(Mandel::General); // wrong; it must be symmetric + let mut d2 = Tensor4::new(Mandel::Symmetric); + deriv2_invariant_sigma_d(&mut d2, &mut aux, &sigma); + } + + #[test] + #[should_panic] + fn deriv2_invariant_lode_panics_on_non_sym1() { + let mut aux = AuxDeriv2InvariantLode::new(); + let sigma = Tensor2::new(Mandel::Symmetric2D); + let mut d2 = Tensor4::new(Mandel::Symmetric2D); // wrong; it must be Symmetric + deriv2_invariant_lode(&mut d2, &mut aux, &sigma); + } + + #[test] + #[should_panic(expected = "sigma.mandel.symmetric()")] + fn deriv2_invariant_lode_panics_on_non_sym2() { + let mut aux = AuxDeriv2InvariantLode::new(); + let sigma = Tensor2::new(Mandel::General); // wrong; it must be symmetric + let mut d2 = Tensor4::new(Mandel::Symmetric); + deriv2_invariant_lode(&mut d2, &mut aux, &sigma); + } } From 1d67cd6f3c349e59debe7521230831d87d51d7cc Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Sun, 12 May 2024 22:17:16 +1000 Subject: [PATCH 27/44] [tensor][wip][Important] Improve operation functions --- russell_tensor/src/operations.rs | 1167 +++++++++++++++++++++--------- 1 file changed, 807 insertions(+), 360 deletions(-) diff --git a/russell_tensor/src/operations.rs b/russell_tensor/src/operations.rs index 9a9d0516..eb55f667 100644 --- a/russell_tensor/src/operations.rs +++ b/russell_tensor/src/operations.rs @@ -1,8 +1,7 @@ use super::{Tensor2, Tensor4}; -use crate::{StrError, SQRT_2}; -use russell_lab::{ - mat_mat_mul, mat_vec_mul, mat_vec_mul_update, vec_inner, vec_mat_mul, vec_outer, vec_outer_update, Vector, -}; +use crate::{Mandel, SQRT_2}; +use russell_lab::{mat_mat_mul, mat_vec_mul, mat_vec_mul_update, vec_inner, vec_mat_mul, vec_outer, vec_outer_update}; +use russell_lab::{StrError, Vector}; /// Performs the double-dot (ddot) operation between two Tensor2 (inner product) /// @@ -26,6 +25,19 @@ use russell_lab::{ /// m /// ``` /// +/// # Input +/// +/// * `a` -- first tensor; with the same [Mandel] as `b` +/// * `b` -- second tensor; with the same [Mandel] as `a` +/// +/// # Output +/// +/// Returns the scalar result of `a : b`. +/// +/// # Panics +/// +/// A panic will occur `a` and `b` have different [Mandel] +/// /// # Examples /// /// ``` @@ -45,18 +57,15 @@ use russell_lab::{ /// [0.0, 4.0, 1.0], /// ], Mandel::General)?; /// -/// let res = t2_ddot_t2(&a.to_general(), &b)?; +/// let res = t2_ddot_t2(&a.to_general(), &b); /// /// approx_eq(res, 8.0, 1e-15); /// Ok(()) /// } /// ``` -#[inline] -pub fn t2_ddot_t2(a: &Tensor2, b: &Tensor2) -> Result { - if a.vec.dim() != b.vec.dim() { - return Err("tensors are incompatible"); - } - Ok(vec_inner(&a.vec, &b.vec)) +pub fn t2_ddot_t2(a: &Tensor2, b: &Tensor2) -> f64 { + assert_eq!(a.mandel, b.mandel); + vec_inner(&a.vec, &b.vec) } /// Performs the single dot operation between two Tensor2 (matrix multiplication) @@ -76,6 +85,20 @@ pub fn t2_ddot_t2(a: &Tensor2, b: &Tensor2) -> Result { /// /// **Important:** Even if `a` and `b` are symmetric, the result `c` /// may not be symmetric. Therefore, `c` must be a General tensor. +/// +/// # Output +/// +/// * `c` -- the resulting tensor; it must be [Mandel::General] +/// +/// # Input +/// +/// * `a` -- first tensor; with the same [Mandel] as `b` +/// * `b` -- second tensor; with the same [Mandel] as `a` +/// +/// # Panics +/// +/// 1. A panic will occur if `c` is not [Mandel::General] +/// 2. A panic will occur the `a` and `b` have different [Mandel] /// /// # Examples /// @@ -96,7 +119,7 @@ pub fn t2_ddot_t2(a: &Tensor2, b: &Tensor2) -> Result { /// ], Mandel::General)?; /// /// let mut c = Tensor2::new(Mandel::General); -/// t2_dot_t2(&mut c, &a, &b)?; +/// t2_dot_t2(&mut c, &a, &b); /// assert_eq!( /// format!("{:.1}", c.to_matrix()), /// "┌ ┐\n\ @@ -109,14 +132,10 @@ pub fn t2_ddot_t2(a: &Tensor2, b: &Tensor2) -> Result { /// } /// ``` #[rustfmt::skip] -pub fn t2_dot_t2(c: &mut Tensor2, a: &Tensor2, b: &Tensor2) -> Result<(), StrError> { +pub fn t2_dot_t2(c: &mut Tensor2, a: &Tensor2, b: &Tensor2) { + assert_eq!(c.mandel, Mandel::General); + assert_eq!(b.mandel, a.mandel); let dim = a.vec.dim(); - if c.vec.dim() != 9 { - return Err("'c' tensor must be General"); - } - if b.vec.dim() != dim { - return Err("'a' and 'b' tensors must be compatible"); - } let a = &a.vec; let b = &b.vec; let c = &mut c.vec; @@ -152,7 +171,6 @@ pub fn t2_dot_t2(c: &mut Tensor2, a: &Tensor2, b: &Tensor2) -> Result<(), StrErr c[7] = (-2.0 * (a[4] - a[7]) * b[1] + 2.0 * (a[4] + a[7]) * b[2] - SQRT_2 * (a[5] - a[8]) * (b[3] + b[6]) - 2.0 * a[2] * (b[4] - b[7]) + 2.0 * a[1] * (b[4] + b[7]) + SQRT_2 * (a[3] - a[6]) * (b[5] + b[8])) / 4.0; c[8] = (-2.0 * (a[5] - a[8]) * b[0] + 2.0 * (a[5] + a[8]) * b[2] - SQRT_2 * (a[4] - a[7]) * (b[3] - b[6]) + SQRT_2 * (a[3] + a[6]) * (b[4] + b[7]) - 2.0 * a[2] * (b[5] - b[8]) + 2.0 * a[0] * (b[5] + b[8])) / 4.0; } - Ok(()) } /// Performs the single dot operation between a Tensor2 and a vector @@ -170,6 +188,21 @@ pub fn t2_dot_t2(c: &mut Tensor2, a: &Tensor2, b: &Tensor2) -> Result<(), StrErr /// j /// ``` /// +/// # Output +/// +/// * `v` -- the resulting vector; with `dim` compatible with the dimension of `a` (2D or 3D) +/// +/// # Input +/// +/// * `alpha` -- the `α` multiplier +/// * `a` -- the second-order tensor +/// * `u` -- the 2D or 3D vector; with `dim` compatible with the dimension of `a` (2D or 3D) +/// +/// # Panics +/// +/// 1. If `a` is 2D, a panic will occur if `u` or `v` are not `2D` +/// 2. If `a` is 3D, a panic will occur if `u` or `v` are not `3D` +/// /// # Examples /// /// ``` @@ -185,7 +218,7 @@ pub fn t2_dot_t2(c: &mut Tensor2, a: &Tensor2, b: &Tensor2) -> Result<(), StrErr /// /// let u = Vector::from(&[1.0, 2.0]); /// let mut v = Vector::new(2); -/// t2_dot_vec(&mut v, 2.0, &a, &u)?; +/// t2_dot_vec(&mut v, 2.0, &a, &u); /// /// assert_eq!( /// format!("{:.1}", v), @@ -197,22 +230,19 @@ pub fn t2_dot_t2(c: &mut Tensor2, a: &Tensor2, b: &Tensor2) -> Result<(), StrErr /// Ok(()) /// } /// ``` -pub fn t2_dot_vec(v: &mut Vector, alpha: f64, a: &Tensor2, u: &Vector) -> Result<(), StrError> { +pub fn t2_dot_vec(v: &mut Vector, alpha: f64, a: &Tensor2, u: &Vector) { if a.vec.dim() == 4 { - if v.dim() != 2 || u.dim() != 2 { - return Err("vectors must have dim = 2"); - } + assert_eq!(v.dim(), 2); + assert_eq!(u.dim(), 2); v[0] = alpha * (a.get(0, 0) * u[0] + a.get(0, 1) * u[1]); v[1] = alpha * (a.get(1, 0) * u[0] + a.get(1, 1) * u[1]); } else { - if v.dim() != 3 || u.dim() != 3 { - return Err("vectors must have dim = 3"); - } + assert_eq!(v.dim(), 3); + assert_eq!(u.dim(), 3); v[0] = alpha * (a.get(0, 0) * u[0] + a.get(0, 1) * u[1] + a.get(0, 2) * u[2]); v[1] = alpha * (a.get(1, 0) * u[0] + a.get(1, 1) * u[1] + a.get(1, 2) * u[2]); v[2] = alpha * (a.get(2, 0) * u[0] + a.get(2, 1) * u[1] + a.get(2, 2) * u[2]); } - Ok(()) } /// Performs the single dot operation between a vector and a Tensor2 @@ -230,6 +260,21 @@ pub fn t2_dot_vec(v: &mut Vector, alpha: f64, a: &Tensor2, u: &Vector) -> Result /// i /// ``` /// +/// # Output +/// +/// * `v` -- the resulting vector; with `dim` compatible with the dimension of `a` (2D or 3D) +/// +/// # Input +/// +/// * `alpha` -- the `α` multiplier +/// * `u` -- the 2D or 3D vector; with `dim` compatible with the dimension of `a` (2D or 3D) +/// * `a` -- the second-order tensor +/// +/// # Panics +/// +/// 1. If `a` is 2D, a panic will occur if `u` or `v` are not `2D` +/// 2. If `a` is 3D, a panic will occur if `u` or `v` are not `3D` +/// /// # Examples /// /// ``` @@ -245,7 +290,7 @@ pub fn t2_dot_vec(v: &mut Vector, alpha: f64, a: &Tensor2, u: &Vector) -> Result /// ], Mandel::Symmetric2D)?; /// /// let mut v = Vector::new(2); -/// vec_dot_t2(&mut v, 2.0, &u, &a)?; +/// vec_dot_t2(&mut v, 2.0, &u, &a); /// /// assert_eq!( /// format!("{:.1}", v), @@ -257,22 +302,19 @@ pub fn t2_dot_vec(v: &mut Vector, alpha: f64, a: &Tensor2, u: &Vector) -> Result /// Ok(()) /// } /// ``` -pub fn vec_dot_t2(v: &mut Vector, alpha: f64, u: &Vector, a: &Tensor2) -> Result<(), StrError> { +pub fn vec_dot_t2(v: &mut Vector, alpha: f64, u: &Vector, a: &Tensor2) { if a.vec.dim() == 4 { - if v.dim() != 2 || u.dim() != 2 { - return Err("vectors must have dim = 2"); - } + assert_eq!(v.dim(), 2); + assert_eq!(u.dim(), 2); v[0] = alpha * (u[0] * a.get(0, 0) + u[1] * a.get(1, 0)); v[1] = alpha * (u[0] * a.get(0, 1) + u[1] * a.get(1, 1)); } else { - if v.dim() != 3 || u.dim() != 3 { - return Err("vectors must have dim = 3"); - } + assert_eq!(v.dim(), 3); + assert_eq!(u.dim(), 3); v[0] = alpha * (u[0] * a.get(0, 0) + u[1] * a.get(1, 0) + u[2] * a.get(2, 0)); v[1] = alpha * (u[0] * a.get(0, 1) + u[1] * a.get(1, 1) + u[2] * a.get(2, 1)); v[2] = alpha * (u[0] * a.get(0, 2) + u[1] * a.get(1, 2) + u[2] * a.get(2, 2)); } - Ok(()) } /// Performs the dyadic product between two vectors resulting in a second-order tensor @@ -280,18 +322,33 @@ pub fn vec_dot_t2(v: &mut Vector, alpha: f64, u: &Vector, a: &Tensor2) -> Result /// Computes: /// /// ```text -/// T = α u ⊗ v +/// A = α u ⊗ v /// ``` /// /// With orthonormal Cartesian components: /// /// ```text -/// Tᵢⱼ = α uᵢ vⱼ +/// Aᵢⱼ = α uᵢ vⱼ /// ``` /// /// **Important:** The dyadic product between two vectors may result in a **non-symmetric** -/// second-order tensor. Therefore, if the input tensor `T` is symmetric, an error may occur. -/// Thus, make sure that the you expect `u ⊗ v` to be symmetric when passing a symmetric tensor `T`. +/// second-order tensor. Therefore, if the input tensor `A` is symmetric, an error may occur. +/// Thus, make sure that the you expect `u ⊗ v` to be symmetric when passing a symmetric tensor `A`. +/// +/// # Output +/// +/// * `A` -- the resulting second-order tensor +/// +/// # Input +/// +/// * `alpha` -- the `α` multiplier +/// * `u` -- the 2D or 3D vector; with `dim` compatible with the dimension of `T` (2D or 3D) +/// * `v` -- the 2D or 3D vector; with `dim` compatible with the dimension of `T` (2D or 3D) +/// +/// # Panics +/// +/// 1. If `A` is 2D, a panic will occur if `u` or `v` are not `2D` +/// 2. If `A` is 3D, a panic will occur if `u` or `v` are not `3D` /// /// # Examples /// @@ -317,37 +374,35 @@ pub fn vec_dot_t2(v: &mut Vector, alpha: f64, u: &Vector, a: &Tensor2) -> Result /// Ok(()) /// } /// ``` -pub fn vec_dyad_vec(tt: &mut Tensor2, alpha: f64, u: &Vector, v: &Vector) -> Result<(), StrError> { - let dim = tt.vec.dim(); +pub fn vec_dyad_vec(a: &mut Tensor2, alpha: f64, u: &Vector, v: &Vector) -> Result<(), StrError> { + let dim = a.vec.dim(); if dim == 4 { - if u.dim() != 2 || v.dim() != 2 { - return Err("vectors must have dim = 2"); - } + assert_eq!(v.dim(), 2); + assert_eq!(u.dim(), 2); if (u[0] * v[1]) != (u[1] * v[0]) { return Err("dyadic product between u and v does not generate a symmetric tensor"); } - tt.vec[0] = alpha * u[0] * v[0]; - tt.vec[1] = alpha * u[1] * v[1]; - tt.vec[2] = 0.0; - tt.vec[3] = alpha * (u[0] * v[1] + u[1] * v[0]) / SQRT_2; + a.vec[0] = alpha * u[0] * v[0]; + a.vec[1] = alpha * u[1] * v[1]; + a.vec[2] = 0.0; + a.vec[3] = alpha * (u[0] * v[1] + u[1] * v[0]) / SQRT_2; } else { - if u.dim() != 3 || v.dim() != 3 { - return Err("vectors must have dim = 3"); - } - tt.vec[0] = alpha * u[0] * v[0]; - tt.vec[1] = alpha * u[1] * v[1]; - tt.vec[2] = alpha * u[2] * v[2]; - tt.vec[3] = alpha * (u[0] * v[1] + u[1] * v[0]) / SQRT_2; - tt.vec[4] = alpha * (u[1] * v[2] + u[2] * v[1]) / SQRT_2; - tt.vec[5] = alpha * (u[0] * v[2] + u[2] * v[0]) / SQRT_2; + assert_eq!(v.dim(), 3); + assert_eq!(u.dim(), 3); + a.vec[0] = alpha * u[0] * v[0]; + a.vec[1] = alpha * u[1] * v[1]; + a.vec[2] = alpha * u[2] * v[2]; + a.vec[3] = alpha * (u[0] * v[1] + u[1] * v[0]) / SQRT_2; + a.vec[4] = alpha * (u[1] * v[2] + u[2] * v[1]) / SQRT_2; + a.vec[5] = alpha * (u[0] * v[2] + u[2] * v[0]) / SQRT_2; if dim == 6 { if (u[0] * v[1]) != (u[1] * v[0]) || (u[1] * v[2]) != (u[2] * v[1]) || (u[0] * v[2]) != (u[2] * v[0]) { return Err("dyadic product between u and v does not generate a symmetric tensor"); } } else { - tt.vec[6] = alpha * (u[0] * v[1] - u[1] * v[0]) / SQRT_2; - tt.vec[7] = alpha * (u[1] * v[2] - u[2] * v[1]) / SQRT_2; - tt.vec[8] = alpha * (u[0] * v[2] - u[2] * v[0]) / SQRT_2; + a.vec[6] = alpha * (u[0] * v[1] - u[1] * v[0]) / SQRT_2; + a.vec[7] = alpha * (u[1] * v[2] - u[2] * v[1]) / SQRT_2; + a.vec[8] = alpha * (u[0] * v[2] - u[2] * v[0]) / SQRT_2; } } Ok(()) @@ -371,6 +426,19 @@ pub fn vec_dyad_vec(tt: &mut Tensor2, alpha: f64, u: &Vector, v: &Vector) -> Res /// Dₘₙ = α aₘ bₙ /// ``` /// +/// # Output +/// +/// * `dd` -- the tensor `D`; with the same [Mandel] as `a` and `b` +/// +/// # Input +/// +/// * `a` -- first tensor; with the same [Mandel] as `b` and `dd` +/// * `b` -- second tensor; with the same [Mandel] as `a` and `dd` +/// +/// # Panics +/// +/// A panic will occur the tensors have different [Mandel] +/// /// # Examples /// /// ``` @@ -390,7 +458,7 @@ pub fn vec_dyad_vec(tt: &mut Tensor2, alpha: f64, u: &Vector, v: &Vector) -> Res /// ], Mandel::General)?; /// /// let mut dd = Tensor4::new(Mandel::General); -/// t2_dyad_t2(&mut dd, 1.0, &a, &b)?; +/// t2_dyad_t2(&mut dd, 1.0, &a, &b); /// /// assert_eq!( /// format!("{:.1}", dd.to_matrix()), @@ -409,9 +477,10 @@ pub fn vec_dyad_vec(tt: &mut Tensor2, alpha: f64, u: &Vector, v: &Vector) -> Res /// Ok(()) /// } /// ``` -#[inline] -pub fn t2_dyad_t2(dd: &mut Tensor4, alpha: f64, a: &Tensor2, b: &Tensor2) -> Result<(), StrError> { - vec_outer(&mut dd.mat, alpha, &a.vec, &b.vec) +pub fn t2_dyad_t2(dd: &mut Tensor4, alpha: f64, a: &Tensor2, b: &Tensor2) { + assert_eq!(a.mandel, dd.mandel); + assert_eq!(b.mandel, dd.mandel); + vec_outer(&mut dd.mat, alpha, &a.vec, &b.vec).unwrap(); } /// Performs the dyadic product between two Tensor2 resulting in a Tensor4 (with update) @@ -434,6 +503,19 @@ pub fn t2_dyad_t2(dd: &mut Tensor4, alpha: f64, a: &Tensor2, b: &Tensor2) -> Res /// Dₘₙ += α aₘ bₙ /// ``` /// +/// # Output +/// +/// * `dd` -- the tensor `D`; with the same [Mandel] as `a` and `b` +/// +/// # Input +/// +/// * `a` -- first tensor; with the same [Mandel] as `b` and `dd` +/// * `b` -- second tensor; with the same [Mandel] as `a` and `dd` +/// +/// # Panics +/// +/// A panic will occur the tensors have different [Mandel] +/// /// # Examples /// /// ``` @@ -477,9 +559,10 @@ pub fn t2_dyad_t2(dd: &mut Tensor4, alpha: f64, a: &Tensor2, b: &Tensor2) -> Res /// Ok(()) /// } /// ``` -#[inline] -pub fn t2_dyad_t2_update(dd: &mut Tensor4, alpha: f64, a: &Tensor2, b: &Tensor2) -> Result<(), StrError> { - vec_outer_update(&mut dd.mat, alpha, &a.vec, &b.vec) +pub fn t2_dyad_t2_update(dd: &mut Tensor4, alpha: f64, a: &Tensor2, b: &Tensor2) { + assert_eq!(a.mandel, dd.mandel); + assert_eq!(b.mandel, dd.mandel); + vec_outer_update(&mut dd.mat, alpha, &a.vec, &b.vec).unwrap(); } /// Performs the overbar dyadic product between two Tensor2 resulting in a (general) Tensor4 @@ -498,15 +581,25 @@ pub fn t2_dyad_t2_update(dd: &mut Tensor4, alpha: f64, a: &Tensor2, b: &Tensor2) /// ``` /// /// **Important:** The result is **not** necessarily minor-symmetric; therefore `D` must be General. +/// +/// # Output +/// +/// * `dd` -- the tensor `D`; it must be [Mandel::General] +/// +/// # Input +/// +/// * `a` -- first tensor; with the same [Mandel] as `b` +/// * `b` -- second tensor; with the same [Mandel] as `a` +/// +/// # Panics +/// +/// 1. A panic will occur if `dd` is not [Mandel::General] +/// 2. A panic will occur the `a` and `b` have different [Mandel] #[rustfmt::skip] -pub fn t2_odyad_t2(dd: &mut Tensor4, s: f64, aa: &Tensor2, bb: &Tensor2) -> Result<(), StrError> { - if dd.mat.dims().1 != 9 { - return Err("D tensor must be General"); - } +pub fn t2_odyad_t2(dd: &mut Tensor4, s: f64, aa: &Tensor2, bb: &Tensor2) { + assert_eq!(dd.mandel, Mandel::General); + assert_eq!(bb.mandel, aa.mandel); let dim = aa.vec.dim(); - if bb.vec.dim() != dim { - return Err("A and B tensors must be compatible"); - } let a = &aa.vec; let b = &bb.vec; let tsq2 = 2.0 * SQRT_2; @@ -781,7 +874,6 @@ pub fn t2_odyad_t2(dd: &mut Tensor4, s: f64, aa: &Tensor2, bb: &Tensor2) -> Resu dd.mat.set(8,7, s*(SQRT_2*(a[3] + a[6])*b[2] + SQRT_2*a[2]*(b[3] + b[6]) - (a[5] + a[8])*(b[4] - b[7]) - (a[4] - a[7])*(b[5] + b[8]))/4.0); dd.mat.set(8,8, s*(a[2]*b[0] + a[0]*b[2] - a[5]*b[5] + a[8]*b[8])/2.0); } - Ok(()) } /// Performs the underbar dyadic product between two Tensor2 resulting in a (general) Tensor4 @@ -800,15 +892,25 @@ pub fn t2_odyad_t2(dd: &mut Tensor4, s: f64, aa: &Tensor2, bb: &Tensor2) -> Resu /// ``` /// /// **Important:** The result is **not** necessarily minor-symmetric; therefore `D` must be General. +/// +/// # Output +/// +/// * `dd` -- the tensor `D`; it must be [Mandel::General] +/// +/// # Input +/// +/// * `a` -- first tensor; with the same [Mandel] as `b` +/// * `b` -- second tensor; with the same [Mandel] as `a` +/// +/// # Panics +/// +/// 1. A panic will occur if `dd` is not [Mandel::General] +/// 2. A panic will occur the `a` and `b` have different [Mandel] #[rustfmt::skip] -pub fn t2_udyad_t2(dd: &mut Tensor4, s: f64, aa: &Tensor2, bb: &Tensor2) -> Result<(), StrError> { - if dd.mat.dims().1 != 9 { - return Err("D tensor must be General"); - } +pub fn t2_udyad_t2(dd: &mut Tensor4, s: f64, aa: &Tensor2, bb: &Tensor2) { + assert_eq!(dd.mandel, Mandel::General); + assert_eq!(bb.mandel, aa.mandel); let dim = aa.vec.dim(); - if bb.vec.dim() != dim { - return Err("A and B tensors must be compatible"); - } let a = &aa.vec; let b = &bb.vec; let tsq2 = 2.0 * SQRT_2; @@ -1083,7 +1185,6 @@ pub fn t2_udyad_t2(dd: &mut Tensor4, s: f64, aa: &Tensor2, bb: &Tensor2) -> Resu dd.mat.set(8,7, s*(-(SQRT_2*(a[3] + a[6])*b[2]) - SQRT_2*a[2]*(b[3] + b[6]) + (a[5] + a[8])*(b[4] - b[7]) + (a[4] - a[7])*(b[5] + b[8]))/4.0); dd.mat.set(8,8, s*(-(a[2]*b[0]) - a[0]*b[2] + a[5]*b[5] - a[8]*b[8])/2.0); } - Ok(()) } /// Performs the self-sum-dyadic (ssd) operation with a Tensor2 yielding a minor-symmetric Tensor4 @@ -1106,16 +1207,18 @@ pub fn t2_udyad_t2(dd: &mut Tensor4, s: f64, aa: &Tensor2, bb: &Tensor2) -> Resu /// /// # Output /// -/// * `dd` -- The result is minor-symmetric; therefore `dd` must be Symmetric (but not 2D). +/// * `dd` -- The resulting tensor (minor-symmetric); it must be [Mandel::Symmetric] /// /// # Input /// /// * `aa` -- Second-order tensor, symmetric or not. +/// +/// # Panics +/// +/// A panic will occur if `dd` is not [Mandel::Symmetric] #[rustfmt::skip] -pub fn t2_ssd(dd: &mut Tensor4, s: f64, aa: &Tensor2) -> Result<(), StrError> { - if dd.mat.dims().1 != 6 { - return Err("D tensor must be Symmetric"); - } +pub fn t2_ssd(dd: &mut Tensor4, s: f64, aa: &Tensor2) { + assert_eq!(dd.mandel, Mandel::Symmetric); let dim = aa.vec.dim(); let a = &aa.vec; if dim == 4 { @@ -1245,7 +1348,6 @@ pub fn t2_ssd(dd: &mut Tensor4, s: f64, aa: &Tensor2) -> Result<(), StrError> { dd.mat.set(5,4, s*(SQRT_2*a[2]*(a[3] + a[6]) + (a[4] - a[7])*(a[5] + a[8]))); dd.mat.set(5,5, s*(2.0*a[0]*a[2] + a[5]*a[5] - a[8]*a[8])); } - Ok(()) } /// Performs the quad-sum-dyadic (qsd) operation with two Tensor2 yielding a minor-symmetric Tensor4 @@ -1268,21 +1370,22 @@ pub fn t2_ssd(dd: &mut Tensor4, s: f64, aa: &Tensor2) -> Result<(), StrError> { /// /// # Output /// -/// * `dd` -- The result is minor-symmetric; therefore `dd` must be Symmetric (but not 2D). +/// * `dd` -- The resulting tensor (minor-symmetric); it must be [Mandel::Symmetric] /// /// # Input /// -/// * `aa` -- Second-order tensor, symmetric or not (with the same Mandel type as `bb`) -/// * `bb` -- Second-order tensor, symmetric or not (with the same Mandel type as `aa`) +/// * `aa` -- Second-order tensor, symmetric or not; with the same [Mandel] as `bb` +/// * `bb` -- Second-order tensor, symmetric or not; with the same [Mandel] as `aa` +/// +/// # Panics +/// +/// 1. A panic will occur if `dd` is not [Mandel::Symmetric] +/// 2. A panic will occur `aa` and `bb` have different [Mandel] #[rustfmt::skip] -pub fn t2_qsd_t2(dd: &mut Tensor4, s: f64, aa: &Tensor2, bb: &Tensor2) -> Result<(), StrError> { - if dd.mat.dims().1 != 6 { - return Err("D tensor must be Symmetric"); - } +pub fn t2_qsd_t2(dd: &mut Tensor4, s: f64, aa: &Tensor2, bb: &Tensor2) { + assert_eq!(dd.mandel, Mandel::Symmetric); + assert_eq!(bb.mandel, aa.mandel); let dim = aa.vec.dim(); - if bb.vec.dim() != dim { - return Err("A and B tensors must be compatible"); - } let a = &aa.vec; let b = &bb.vec; if dim == 4 { @@ -1412,7 +1515,6 @@ pub fn t2_qsd_t2(dd: &mut Tensor4, s: f64, aa: &Tensor2, bb: &Tensor2) -> Result dd.mat.set(5,4, s*(SQRT_2*(a[3] + a[6])*b[2] + SQRT_2*a[2]*(b[3] + b[6]) + (a[5] + a[8])*(b[4] - b[7]) + (a[4] - a[7])*(b[5] + b[8]))); dd.mat.set(5,5, s*(2.0*(a[2]*b[0] + a[0]*b[2] + a[5]*b[5] - a[8]*b[8]))); } - Ok(()) } /// Performs the double-dot (ddot) operation between a Tensor4 and a Tensor2 @@ -1435,6 +1537,20 @@ pub fn t2_qsd_t2(dd: &mut Tensor4, s: f64, aa: &Tensor2, bb: &Tensor2) -> Result /// n /// ``` /// +/// # Output +/// +/// * `b` -- the resulting second-order tensor; with the same [Mandel] as `a` and `dd` +/// +/// # Input +/// +/// * `alpha` -- the scalar multiplier +/// * `dd` -- the fourth-order tensor; with the same [Mandel] as `a` and `b` +/// * `a` -- the input second-order tensor; with the same [Mandel] as `b` and `dd` +/// +/// # Panics +/// +/// A panic will occur the tensors have different [Mandel] +/// /// # Examples /// /// ``` @@ -1460,7 +1576,7 @@ pub fn t2_qsd_t2(dd: &mut Tensor4, s: f64, aa: &Tensor2, bb: &Tensor2) -> Result /// ], Mandel::General)?; /// /// let mut b = Tensor2::new(Mandel::General); -/// t4_ddot_t2(&mut b, 1.0, &dd, &a)?; +/// t4_ddot_t2(&mut b, 1.0, &dd, &a); /// /// assert_eq!( /// format!("{:.1}", b.to_matrix()), @@ -1473,9 +1589,10 @@ pub fn t2_qsd_t2(dd: &mut Tensor4, s: f64, aa: &Tensor2, bb: &Tensor2) -> Result /// Ok(()) /// } /// ``` -#[inline] -pub fn t4_ddot_t2(b: &mut Tensor2, alpha: f64, dd: &Tensor4, a: &Tensor2) -> Result<(), StrError> { - mat_vec_mul(&mut b.vec, alpha, &dd.mat, &a.vec) +pub fn t4_ddot_t2(b: &mut Tensor2, alpha: f64, dd: &Tensor4, a: &Tensor2) { + assert_eq!(a.mandel, dd.mandel); + assert_eq!(b.mandel, dd.mandel); + mat_vec_mul(&mut b.vec, alpha, &dd.mat, &a.vec).unwrap(); } /// Performs the double-dot (ddot) operation between a Tensor4 and a Tensor2 with update @@ -1500,6 +1617,21 @@ pub fn t4_ddot_t2(b: &mut Tensor2, alpha: f64, dd: &Tensor4, a: &Tensor2) -> Res /// n /// ``` /// +/// # Output +/// +/// * `b` -- the resulting second-order tensor; with the same [Mandel] as `a` and `dd` +/// +/// # Input +/// +/// * `alpha` -- the scalar multiplier +/// * `a` -- the input second-order tensor; with the same [Mandel] as `b` and `dd` +/// * `dd` -- the fourth-order tensor; with the same [Mandel] as `a` and `b` +/// * `beta` -- the other scalar multiplier +/// +/// # Panics +/// +/// A panic will occur the tensors have different [Mandel] +/// /// # Examples /// /// ``` @@ -1542,9 +1674,10 @@ pub fn t4_ddot_t2(b: &mut Tensor2, alpha: f64, dd: &Tensor4, a: &Tensor2) -> Res /// Ok(()) /// } /// ``` -#[inline] -pub fn t4_ddot_t2_update(b: &mut Tensor2, alpha: f64, dd: &Tensor4, a: &Tensor2, beta: f64) -> Result<(), StrError> { - mat_vec_mul_update(&mut b.vec, alpha, &dd.mat, &a.vec, beta) +pub fn t4_ddot_t2_update(b: &mut Tensor2, alpha: f64, dd: &Tensor4, a: &Tensor2, beta: f64) { + assert_eq!(a.mandel, dd.mandel); + assert_eq!(b.mandel, dd.mandel); + mat_vec_mul_update(&mut b.vec, alpha, &dd.mat, &a.vec, beta).unwrap(); } /// Performs the double-dot (ddot) operation between a Tensor2 and a Tensor4 @@ -1562,6 +1695,20 @@ pub fn t4_ddot_t2_update(b: &mut Tensor2, alpha: f64, dd: &Tensor4, a: &Tensor2, /// i j /// ``` /// +/// # Output +/// +/// * `b` -- the resulting second-order tensor; with the same [Mandel] as `a` and `dd` +/// +/// # Input +/// +/// * `alpha` -- the scalar multiplier +/// * `a` -- the input second-order tensor; with the same [Mandel] as `b` and `dd` +/// * `dd` -- the fourth-order tensor; with the same [Mandel] as `a` and `b` +/// +/// # Panics +/// +/// A panic will occur the tensors have different [Mandel] +/// /// # Examples /// /// ``` @@ -1600,9 +1747,10 @@ pub fn t4_ddot_t2_update(b: &mut Tensor2, alpha: f64, dd: &Tensor4, a: &Tensor2, /// Ok(()) /// } /// ``` -#[inline] -pub fn t2_ddot_t4(b: &mut Tensor2, alpha: f64, a: &Tensor2, dd: &Tensor4) -> Result<(), StrError> { - vec_mat_mul(&mut b.vec, alpha, &a.vec, &dd.mat) +pub fn t2_ddot_t4(b: &mut Tensor2, alpha: f64, a: &Tensor2, dd: &Tensor4) { + assert_eq!(a.mandel, dd.mandel); + assert_eq!(b.mandel, dd.mandel); + vec_mat_mul(&mut b.vec, alpha, &a.vec, &dd.mat).unwrap(); } /// Performs the double-dot (ddot) operation between two Tensor4 @@ -1627,6 +1775,20 @@ pub fn t2_ddot_t4(b: &mut Tensor2, alpha: f64, a: &Tensor2, dd: &Tensor4) -> Res /// m /// ``` /// +/// # Output +/// +/// * `ee` -- the resulting fourth-order tensor; with the same [Mandel] as `cc` and `dd` +/// +/// # Input +/// +/// * `alpha` -- the scalar multiplier +/// * `a` -- the input second-order tensor; with the same [Mandel] as `b` and `dd` +/// * `dd` -- the fourth-order tensor; with the same [Mandel] as `a` and `b` +/// +/// # Panics +/// +/// A panic will occur the tensors have different [Mandel] +/// /// # Examples /// /// ``` @@ -1680,9 +1842,52 @@ pub fn t2_ddot_t4(b: &mut Tensor2, alpha: f64, a: &Tensor2, dd: &Tensor4) -> Res /// Ok(()) /// } /// ``` -#[inline] -pub fn t4_ddot_t4(ee: &mut Tensor4, alpha: f64, cc: &Tensor4, dd: &Tensor4) -> Result<(), StrError> { - mat_mat_mul(&mut ee.mat, alpha, &cc.mat, &dd.mat, 0.0) +pub fn t4_ddot_t4(ee: &mut Tensor4, alpha: f64, cc: &Tensor4, dd: &Tensor4) { + assert_eq!(cc.mandel, dd.mandel); + assert_eq!(ee.mandel, dd.mandel); + mat_mat_mul(&mut ee.mat, alpha, &cc.mat, &dd.mat, 0.0).unwrap(); +} + +/// Performs the double-dot (ddot) operation between two Tensor4 with update +/// +/// Computes: +/// +/// ```text +/// E = α C : D + β E +/// ``` +/// +/// With orthonormal Cartesian components: +/// +/// ```text +/// Eᵢⱼₖₗ = α (Σ Σ Cᵢⱼₛₜ : Dₛₜₖₗ) + β Eᵢⱼₖₗ +/// s t +/// ``` +/// +/// Or, in Mandel basis: +/// +/// ```text +/// Eₘₙ = α (Σ Cₘₐ Dₐₙ) + β Eₘₙ +/// m +/// ``` +/// +/// # Output +/// +/// * `ee` -- the resulting fourth-order tensor; with the same [Mandel] as `cc` and `dd` +/// +/// # Input +/// +/// * `alpha` -- the scalar multiplier +/// * `a` -- the input second-order tensor; with the same [Mandel] as `b` and `dd` +/// * `dd` -- the fourth-order tensor; with the same [Mandel] as `a` and `b` +/// * `beta` -- the other scalar multiplier +/// +/// # Panics +/// +/// A panic will occur the tensors have different [Mandel] +pub fn t4_ddot_t4_update(ee: &mut Tensor4, alpha: f64, cc: &Tensor4, dd: &Tensor4, beta: f64) { + assert_eq!(cc.mandel, dd.mandel); + assert_eq!(ee.mandel, dd.mandel); + mat_mat_mul(&mut ee.mat, alpha, &cc.mat, &dd.mat, beta).unwrap(); } /// Computes Tensor2 double-dot Tensor4 double-dot Tensor2 @@ -1708,18 +1913,31 @@ pub fn t4_ddot_t4(ee: &mut Tensor4, alpha: f64, cc: &Tensor4, dd: &Tensor4) -> R /// ``` /// /// Note: the Lagrange multiplier in Plasticity needs this operation. -pub fn t2_ddot_t4_ddot_t2(a: &Tensor2, dd: &Tensor4, b: &Tensor2) -> Result { +/// +/// # Input +/// +/// * `a` -- the first second-order tensor; with the same [Mandel] as `b` and `dd` +/// * `dd` -- the fourth-order tensor; with the same [Mandel] as `a` and `b` +/// * `b` -- the second second-order tensor; with the same [Mandel] as `a` and `dd` +/// +/// # Output +/// +/// Returns the scalar results. +/// +/// # Panics +/// +/// A panic will occur the tensors have different [Mandel] +pub fn t2_ddot_t4_ddot_t2(a: &Tensor2, dd: &Tensor4, b: &Tensor2) -> f64 { + assert_eq!(a.mandel, dd.mandel); + assert_eq!(b.mandel, dd.mandel); let dim = a.vec.dim(); - if b.vec.dim() != dim || dd.mat.dims().0 != dim { - return Err("tensors are incompatible"); - } let mut s = 0.0; for m in 0..dim { for n in 0..dim { s += a.vec[m] * dd.mat.get(m, n) * b.vec[n]; } } - Ok(s) + s } /// Computes Tensor4 double-dot Tensor2 dyadic Tensor2 double-dot Tensor4 @@ -1745,18 +1963,26 @@ pub fn t2_ddot_t4_ddot_t2(a: &Tensor2, dd: &Tensor4, b: &Tensor2) -> Result Result<(), StrError> { +/// +/// # Output +/// +/// * `ee` -- the resulting fourth-order tensor; with the same [Mandel] as the other tensors +/// +/// # Input +/// +/// * `alpha` -- the scalar multiplier +/// * `a` -- the first second-order tensor; with the same [Mandel] as the other tensors +/// * `b` -- the second second-order tensor; with the same [Mandel] as the other tensors +/// * `dd` -- the fourth-order tensor; with the same [Mandel] as the other tensors +/// +/// # Panics +/// +/// A panic will occur the tensors have different [Mandel] +pub fn t4_ddot_t2_dyad_t2_ddot_t4(ee: &mut Tensor4, alpha: f64, a: &Tensor2, b: &Tensor2, dd: &Tensor4) { + assert_eq!(a.mandel, dd.mandel); + assert_eq!(b.mandel, dd.mandel); + assert_eq!(ee.mandel, dd.mandel); let dim = a.vec.dim(); - if b.vec.dim() != dim || dd.mat.dims().0 != dim { - return Err("tensors are incompatible"); - } ee.mat.fill(0.0); for m in 0..dim { for n in 0..dim { @@ -1768,7 +1994,6 @@ pub fn t4_ddot_t2_dyad_t2_ddot_t4( } } } - Ok(()) } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1779,6 +2004,14 @@ mod tests { use crate::{Mandel, SamplesTensor4, MN_TO_IJKL}; use russell_lab::{approx_eq, mat_approx_eq, vec_approx_eq, Matrix}; + #[test] + #[should_panic] + fn t2_ddot_t2_panics_on_different_mandel() { + let a = Tensor2::new(Mandel::Symmetric); + let b = Tensor2::new(Mandel::General); + t2_ddot_t2(&a, &b); + } + #[test] fn t2_ddot_t2_works() { // general : general @@ -1794,7 +2027,7 @@ mod tests { [6.0, 5.0, 4.0], [3.0, 2.0, 1.0], ], Mandel::General).unwrap(); - let s = t2_ddot_t2(&a, &b).unwrap(); + let s = t2_ddot_t2(&a, &b); assert_eq!(s, 165.0); // sym-3D : sym-3D @@ -1810,24 +2043,9 @@ mod tests { [5.0, 2.0, 4.0], [6.0, 4.0, 1.0], ], Mandel::Symmetric).unwrap(); - let s = t2_ddot_t2(&a, &b).unwrap(); + let s = t2_ddot_t2(&a, &b); approx_eq(s, 162.0, 1e-13); - // sym-3D : general - #[rustfmt::skip] - let a = Tensor2::from_matrix(&[ - [1.0, 4.0, 6.0], - [4.0, 2.0, 5.0], - [6.0, 5.0, 3.0], - ], Mandel::Symmetric).unwrap(); - #[rustfmt::skip] - let b = Tensor2::from_matrix(&[ - [9.0, 8.0, 7.0], - [6.0, 5.0, 4.0], - [3.0, 2.0, 1.0], - ], Mandel::General).unwrap(); - assert_eq!(t2_ddot_t2(&a, &b).err(), Some("tensors are incompatible")); - // sym-2D : sym-2D #[rustfmt::skip] let a = Tensor2::from_matrix(&[ @@ -1841,37 +2059,26 @@ mod tests { [5.0, 2.0, 0.0], [0.0, 0.0, 1.0], ], Mandel::Symmetric2D).unwrap(); - let s = t2_ddot_t2(&a, &b).unwrap(); + let s = t2_ddot_t2(&a, &b); approx_eq(s, 50.0, 1e-13); + } - // sym-2D : sym-3D - #[rustfmt::skip] - let a = Tensor2::from_matrix(&[ - [1.0, 4.0, 0.0], - [4.0, 2.0, 0.0], - [0.0, 0.0, 3.0], - ], Mandel::Symmetric2D).unwrap(); - #[rustfmt::skip] - let b = Tensor2::from_matrix(&[ - [3.0, 5.0, 6.0], - [5.0, 2.0, 4.0], - [6.0, 4.0, 1.0], - ], Mandel::Symmetric).unwrap(); - let s = t2_ddot_t2(&a.to_general(), &b.to_general()).unwrap(); - approx_eq(s, 50.0, 1e-13); + #[test] + #[should_panic] + fn t2_dot_t2_panics_on_non_general() { + let a = Tensor2::new(Mandel::Symmetric); + let b = Tensor2::new(Mandel::Symmetric); + let mut c = Tensor2::new(Mandel::Symmetric); // wrong; it must be General + t2_dot_t2(&mut c, &a, &b); } #[test] - fn t2_dot_t2_captures_errors() { + #[should_panic] + fn t2_dot_t2_panics_on_different_mandel() { let a = Tensor2::new(Mandel::Symmetric); - let b = Tensor2::new(Mandel::General); - let mut c = Tensor2::new(Mandel::Symmetric); - assert_eq!(t2_dot_t2(&mut c, &a, &b).err(), Some("'c' tensor must be General")); + let b = Tensor2::new(Mandel::General); // wrong; it must be the same as `a` let mut c = Tensor2::new(Mandel::General); - assert_eq!( - t2_dot_t2(&mut c, &a, &b).err(), - Some("'a' and 'b' tensors must be compatible") - ); + t2_dot_t2(&mut c, &a, &b); } #[test] @@ -1890,7 +2097,7 @@ mod tests { [3.0, 2.0, 1.0], ], Mandel::General).unwrap(); let mut c = Tensor2::new(Mandel::General); - t2_dot_t2(&mut c, &a, &b).unwrap(); + t2_dot_t2(&mut c, &a, &b); #[rustfmt::skip] let correct = Tensor2::from_matrix(&[ [ 30.0, 24.0, 18.0], @@ -1913,7 +2120,7 @@ mod tests { [6.0, 4.0, 1.0], ], Mandel::Symmetric).unwrap(); let mut c = Tensor2::new(Mandel::General); - t2_dot_t2(&mut c, &a, &b).unwrap(); + t2_dot_t2(&mut c, &a, &b); #[rustfmt::skip] let correct = Tensor2::from_matrix(&[ [59.0, 37.0, 28.0], @@ -1936,7 +2143,7 @@ mod tests { [0.0, 0.0, 1.0], ], Mandel::Symmetric2D).unwrap(); let mut c = Tensor2::new(Mandel::General); - t2_dot_t2(&mut c, &a, &b).unwrap(); + t2_dot_t2(&mut c, &a, &b); #[rustfmt::skip] let correct = Tensor2::from_matrix(&[ [23.0, 13.0, 0.0], @@ -1947,16 +2154,39 @@ mod tests { } #[test] - fn t2_dot_vec_fails_on_wrong_input() { - let mut v = Vector::new(3); - let a = Tensor2::new(Mandel::General); - let u = Vector::new(4); - let res = t2_dot_vec(&mut v, 1.0, &a, &u); - assert_eq!(res.err(), Some("vectors must have dim = 3")); + #[should_panic] + fn t2_dot_vec_panics_on_non_2d_vector_v() { + let mut v = Vector::new(3); // wrong; it must be 2 + let a = Tensor2::new(Mandel::Symmetric2D); + let u = Vector::new(2); + t2_dot_vec(&mut v, 1.0, &a, &u); + } + #[test] + #[should_panic] + fn t2_dot_vec_panics_on_non_2d_vector_u() { + let mut v = Vector::new(2); let a = Tensor2::new(Mandel::Symmetric2D); - let res = t2_dot_vec(&mut v, 1.0, &a, &u); - assert_eq!(res.err(), Some("vectors must have dim = 2")); + let u = Vector::new(3); // wrong; it must be 2 + t2_dot_vec(&mut v, 1.0, &a, &u); + } + + #[test] + #[should_panic] + fn t2_dot_vec_panics_on_non_3d_vector_v() { + let mut v = Vector::new(2); // wrong; it must be 3 + let a = Tensor2::new(Mandel::General); + let u = Vector::new(3); + t2_dot_vec(&mut v, 1.0, &a, &u); + } + + #[test] + #[should_panic] + fn t2_dot_vec_panics_on_non_3d_vector_u() { + let mut v = Vector::new(3); + let a = Tensor2::new(Mandel::General); + let u = Vector::new(2); // wrong; it must be 3 + t2_dot_vec(&mut v, 1.0, &a, &u); } #[test] @@ -1970,7 +2200,7 @@ mod tests { ], Mandel::General).unwrap(); let u = Vector::from(&[-2.0, -3.0, -4.0]); let mut v = Vector::new(3); - t2_dot_vec(&mut v, 2.0, &a, &u).unwrap(); + t2_dot_vec(&mut v, 2.0, &a, &u); vec_approx_eq(&v, &[-40.0, -94.0, -148.0], 1e-13); // sym-3D . vec @@ -1982,7 +2212,7 @@ mod tests { ], Mandel::Symmetric).unwrap(); let u = Vector::from(&[-2.0, -3.0, -4.0]); let mut v = Vector::new(3); - t2_dot_vec(&mut v, 2.0, &a, &u).unwrap(); + t2_dot_vec(&mut v, 2.0, &a, &u); vec_approx_eq(&v, &[-40.0, -86.0, -120.0], 1e-13); // sym-2D . vec @@ -1994,21 +2224,44 @@ mod tests { ], Mandel::Symmetric2D).unwrap(); let u = Vector::from(&[-2.0, -3.0]); let mut v = Vector::new(2); - t2_dot_vec(&mut v, 2.0, &a, &u).unwrap(); + t2_dot_vec(&mut v, 2.0, &a, &u); vec_approx_eq(&v, &[-16.0, -38.0], 1e-13); } #[test] - fn vec_dot_t2_fails_on_wrong_input() { - let mut v = Vector::new(3); - let a = Tensor2::new(Mandel::General); - let u = Vector::new(4); - let res = vec_dot_t2(&mut v, 1.0, &u, &a); - assert_eq!(res.err(), Some("vectors must have dim = 3")); + #[should_panic] + fn vec_dot_t2_panics_on_non_2d_vector_v() { + let mut v = Vector::new(3); // wrong; it must be 2 + let a = Tensor2::new(Mandel::Symmetric2D); + let u = Vector::new(2); + vec_dot_t2(&mut v, 1.0, &u, &a); + } + #[test] + #[should_panic] + fn vec_dot_t2_panics_on_non_2d_vector_u() { + let mut v = Vector::new(2); let a = Tensor2::new(Mandel::Symmetric2D); - let res = vec_dot_t2(&mut v, 1.0, &u, &a); - assert_eq!(res.err(), Some("vectors must have dim = 2")); + let u = Vector::new(3); // wrong; it must be 2 + vec_dot_t2(&mut v, 1.0, &u, &a); + } + + #[test] + #[should_panic] + fn vec_dot_t2_panics_on_non_3d_vector_v() { + let mut v = Vector::new(2); // wrong; it must be 3 + let a = Tensor2::new(Mandel::General); + let u = Vector::new(3); + vec_dot_t2(&mut v, 1.0, &u, &a); + } + + #[test] + #[should_panic] + fn vec_dot_t2_panics_on_non_3d_vector_u() { + let mut v = Vector::new(3); + let a = Tensor2::new(Mandel::General); + let u = Vector::new(2); // wrong; it must be 3 + vec_dot_t2(&mut v, 1.0, &u, &a); } #[test] @@ -2022,7 +2275,7 @@ mod tests { [7.0, 8.0, 9.0], ], Mandel::General).unwrap(); let mut v = Vector::new(3); - vec_dot_t2(&mut v, 2.0, &u, &a).unwrap(); + vec_dot_t2(&mut v, 2.0, &u, &a); vec_approx_eq(&v, &[-84.0, -102.0, -120.0], 1e-13); // sym-3D . vec @@ -2034,7 +2287,7 @@ mod tests { [3.0, 6.0, 9.0], ], Mandel::Symmetric).unwrap(); let mut v = Vector::new(3); - vec_dot_t2(&mut v, 2.0, &u, &a).unwrap(); + vec_dot_t2(&mut v, 2.0, &u, &a); vec_approx_eq(&v, &[-40.0, -86.0, -120.0], 1e-13); // sym-2D . vec @@ -2046,53 +2299,60 @@ mod tests { [0.0, 0.0, 9.0], ], Mandel::Symmetric2D).unwrap(); let mut v = Vector::new(2); - vec_dot_t2(&mut v, 2.0, &u, &a).unwrap(); + vec_dot_t2(&mut v, 2.0, &u, &a); vec_approx_eq(&v, &[-16.0, -38.0], 1e-13); } #[test] - fn vec_dyad_vec_captures_errors() { - // general - const WRONG: f64 = 123.0; - let u = Vector::from(&[-2.0, -3.0, -4.0, WRONG]); - let v = Vector::from(&[4.0, 3.0, 2.0]); - let mut tt = Tensor2::new(Mandel::General); - assert_eq!( - vec_dyad_vec(&mut tt, 1.0, &u, &v).err(), - Some("vectors must have dim = 3") - ); - let u = Vector::from(&[-2.0, -3.0, -4.0]); - let v = Vector::from(&[4.0, 3.0, 2.0, WRONG]); - assert_eq!( - vec_dyad_vec(&mut tt, 1.0, &u, &v).err(), - Some("vectors must have dim = 3") - ); + #[should_panic] + fn vec_dyad_vec_panics_on_non_2d_vector_u() { + let mut a = Tensor2::new(Mandel::Symmetric2D); + let u = Vector::new(3); // wrong; it must be 2 + let v = Vector::new(2); + let _ = vec_dyad_vec(&mut a, 1.0, &u, &v); + } - // symmetric 3D - let u = Vector::from(&[-2.0, -3.0, -4.0]); - let v = Vector::from(&[4.0, 3.0, 2.0]); - let mut tt = Tensor2::new(Mandel::Symmetric); - assert_eq!( - vec_dyad_vec(&mut tt, 1.0, &u, &v).err(), - Some("dyadic product between u and v does not generate a symmetric tensor") - ); + #[test] + #[should_panic] + fn vec_dyad_vec_panics_on_non_2d_vector_v() { + let mut a = Tensor2::new(Mandel::Symmetric2D); + let u = Vector::new(2); + let v = Vector::new(3); // wrong; it must be 2 + let _ = vec_dyad_vec(&mut a, 1.0, &u, &v); + } + + #[test] + #[should_panic] + fn vec_dyad_vec_panics_on_non_3d_vector_u() { + let mut a = Tensor2::new(Mandel::General); + let u = Vector::new(2); // wrong; it must be 3 + let v = Vector::new(3); + let _ = vec_dyad_vec(&mut a, 1.0, &u, &v); + } + + #[test] + #[should_panic] + fn vec_dyad_vec_panics_on_non_3d_vector_v() { + let mut a = Tensor2::new(Mandel::General); + let u = Vector::new(3); + let v = Vector::new(2); // wrong; it must be 3 + let _ = vec_dyad_vec(&mut a, 1.0, &u, &v); + } + #[test] + fn vec_dyad_vec_captures_errors() { // symmetric 2D - let u = Vector::from(&[-2.0, -3.0, WRONG]); - let v = Vector::from(&[4.0, 3.0]); let mut tt = Tensor2::new(Mandel::Symmetric2D); - assert_eq!( - vec_dyad_vec(&mut tt, 1.0, &u, &v).err(), - Some("vectors must have dim = 2") - ); let u = Vector::from(&[-2.0, -3.0]); - let v = Vector::from(&[4.0, 3.0, WRONG]); + let v = Vector::from(&[4.0, 3.0]); assert_eq!( vec_dyad_vec(&mut tt, 1.0, &u, &v).err(), - Some("vectors must have dim = 2") + Some("dyadic product between u and v does not generate a symmetric tensor") ); - let u = Vector::from(&[-2.0, -3.0]); - let v = Vector::from(&[4.0, 3.0]); + // symmetric 3D + let u = Vector::from(&[-2.0, -3.0, -4.0]); + let v = Vector::from(&[4.0, 3.0, 2.0]); + let mut tt = Tensor2::new(Mandel::Symmetric); assert_eq!( vec_dyad_vec(&mut tt, 1.0, &u, &v).err(), Some("dyadic product between u and v does not generate a symmetric tensor") @@ -2136,6 +2396,24 @@ mod tests { vec_approx_eq(&tt.vec, correct, 1e-14); } + #[test] + #[should_panic] + fn t2_dyad_t2_panics_on_different_mandel1() { + let a = Tensor2::new(Mandel::Symmetric2D); + let b = Tensor2::new(Mandel::Symmetric); // wrong; it must be Symmetric2D + let mut dd = Tensor4::new(Mandel::Symmetric2D); + t2_dyad_t2(&mut dd, 1.0, &a, &b); + } + + #[test] + #[should_panic] + fn t2_dyad_t2_panics_on_different_mandel2() { + let a = Tensor2::new(Mandel::Symmetric2D); + let b = Tensor2::new(Mandel::Symmetric2D); + let mut dd = Tensor4::new(Mandel::Symmetric); // wrong; it must be Symmetric2D + t2_dyad_t2(&mut dd, 1.0, &a, &b); + } + #[test] fn t2_dyad_t2_works() { // general dyad general @@ -2152,8 +2430,8 @@ mod tests { [0.5, 0.5, 0.5], ], Mandel::General).unwrap(); let mut dd = Tensor4::new(Mandel::General); - t2_dyad_t2(&mut dd, 2.0, &a, &b).unwrap(); - let mat = dd.to_matrix(); + t2_dyad_t2(&mut dd, 2.0, &a, &b); + let mat = dd.as_matrix(); assert_eq!( format!("{:.1}", mat), "┌ ┐\n\ @@ -2183,8 +2461,8 @@ mod tests { [0.5, 0.5, 0.5], ], Mandel::Symmetric).unwrap(); let mut dd = Tensor4::new(Mandel::Symmetric); - t2_dyad_t2(&mut dd, 2.0, &a, &b).unwrap(); - let mat = dd.to_matrix(); + t2_dyad_t2(&mut dd, 2.0, &a, &b); + let mat = dd.as_matrix(); assert_eq!( format!("{:.1}", mat), "┌ ┐\n\ @@ -2214,8 +2492,8 @@ mod tests { [0.0, 0.0, 0.5], ], Mandel::Symmetric2D).unwrap(); let mut dd = Tensor4::new(Mandel::Symmetric2D); - t2_dyad_t2(&mut dd, 2.0, &a, &b).unwrap(); - let mat = dd.to_matrix(); + t2_dyad_t2(&mut dd, 2.0, &a, &b); + let mat = dd.as_matrix(); assert_eq!( format!("{:.1}", mat), "┌ ┐\n\ @@ -2232,6 +2510,24 @@ mod tests { ); } + #[test] + #[should_panic] + fn t2_dyad_t2_update_panics_on_different_mandel1() { + let a = Tensor2::new(Mandel::Symmetric2D); + let b = Tensor2::new(Mandel::Symmetric); // wrong; it must be Symmetric2D + let mut dd = Tensor4::new(Mandel::Symmetric2D); + t2_dyad_t2_update(&mut dd, 1.0, &a, &b); + } + + #[test] + #[should_panic] + fn t2_dyad_t2_update_panics_on_different_mandel2() { + let a = Tensor2::new(Mandel::Symmetric2D); + let b = Tensor2::new(Mandel::Symmetric2D); + let mut dd = Tensor4::new(Mandel::Symmetric); // wrong; it must be Symmetric2D + t2_dyad_t2_update(&mut dd, 1.0, &a, &b); + } + #[test] fn t2_dyad_t2_update_works() { // general dyad general @@ -2249,8 +2545,8 @@ mod tests { ], Mandel::General).unwrap(); let mat = Matrix::filled(9, 9, 0.1); let mut dd = Tensor4::from_matrix(&mat, Mandel::General).unwrap(); - t2_dyad_t2_update(&mut dd, 2.0, &a, &b).unwrap(); - let mat = dd.to_matrix(); + t2_dyad_t2_update(&mut dd, 2.0, &a, &b); + let mat = dd.as_matrix(); let correct = "┌ ┐\n\ │ 1.1 1.1 1.1 1.1 1.1 1.1 1.1 1.1 1.1 │\n\ │ 5.1 5.1 5.1 5.1 5.1 5.1 5.1 5.1 5.1 │\n\ @@ -2266,9 +2562,9 @@ mod tests { } fn check_dyad(s: f64, a_ten: &Tensor2, b_ten: &Tensor2, dd_ten: &Tensor4, tol: f64) { - let a = a_ten.to_matrix(); - let b = b_ten.to_matrix(); - let dd = dd_ten.to_matrix(); + let a = a_ten.as_matrix(); + let b = b_ten.as_matrix(); + let dd = dd_ten.as_matrix(); let mut correct = Matrix::new(9, 9); for m in 0..9 { for n in 0..9 { @@ -2295,8 +2591,8 @@ mod tests { [3.0, 2.0, 1.0], ], Mandel::General).unwrap(); let mut dd = Tensor4::new(Mandel::General); - t2_dyad_t2(&mut dd, 2.0, &a, &b).unwrap(); - let mat = dd.to_matrix(); + t2_dyad_t2(&mut dd, 2.0, &a, &b); + let mat = dd.as_matrix(); // println!("{:.1}", mat); let correct = Matrix::from(&[ [18.0, 10.0, 2.0, 16.0, 8.0, 14.0, 12.0, 4.0, 6.0], @@ -2326,8 +2622,8 @@ mod tests { [6.0, 4.0, 1.0], ], Mandel::Symmetric).unwrap(); let mut dd = Tensor4::new(Mandel::Symmetric); - t2_dyad_t2(&mut dd, 2.0, &a, &b).unwrap(); - let mat = dd.to_matrix(); + t2_dyad_t2(&mut dd, 2.0, &a, &b); + let mat = dd.as_matrix(); // println!("{:.1}", mat); let correct = Matrix::from(&[ [6.0, 4.0, 2.0, 10.0, 8.0, 12.0, 10.0, 8.0, 12.0], @@ -2357,8 +2653,8 @@ mod tests { [0.0, 0.0, 1.0], ], Mandel::Symmetric2D).unwrap(); let mut dd = Tensor4::new(Mandel::Symmetric2D); - t2_dyad_t2(&mut dd, 2.0, &a, &b).unwrap(); - let mat = dd.to_matrix(); + t2_dyad_t2(&mut dd, 2.0, &a, &b); + let mat = dd.as_matrix(); // println!("{:.1}", mat); let correct = Matrix::from(&[ [6.0, 4.0, 2.0, 8.0, 0.0, 0.0, 8.0, 0.0, 0.0], @@ -2376,26 +2672,27 @@ mod tests { } #[test] - fn t2_odyad_t2_captures_errors() { - let a = Tensor2::new(Mandel::General); - let b = Tensor2::new(Mandel::General); - let mut dd = Tensor4::new(Mandel::Symmetric); - assert_eq!( - t2_odyad_t2(&mut dd, 1.0, &a, &b).err(), - Some("D tensor must be General") - ); - let a = Tensor2::new(Mandel::Symmetric); + #[should_panic] + fn t2_odyad_t2_panics_on_non_general() { + let a = Tensor2::new(Mandel::Symmetric2D); + let b = Tensor2::new(Mandel::Symmetric2D); + let mut dd = Tensor4::new(Mandel::Symmetric2D); // wrong; it must be General + t2_odyad_t2(&mut dd, 1.0, &a, &b); + } + + #[test] + #[should_panic] + fn t2_odyad_t2_panics_on_different_mandel() { + let a = Tensor2::new(Mandel::Symmetric2D); + let b = Tensor2::new(Mandel::Symmetric); // wrong; it must be the same as `a` let mut dd = Tensor4::new(Mandel::General); - assert_eq!( - t2_odyad_t2(&mut dd, 1.0, &a, &b).err(), - Some("A and B tensors must be compatible") - ); + t2_odyad_t2(&mut dd, 1.0, &a, &b); } fn check_odyad(s: f64, a_ten: &Tensor2, b_ten: &Tensor2, dd_ten: &Tensor4, tol: f64) { - let a = a_ten.to_matrix(); - let b = b_ten.to_matrix(); - let dd = dd_ten.to_matrix(); + let a = a_ten.as_matrix(); + let b = b_ten.as_matrix(); + let dd = dd_ten.as_matrix(); let mut correct = Matrix::new(9, 9); for m in 0..9 { for n in 0..9 { @@ -2422,8 +2719,8 @@ mod tests { [3.0, 2.0, 1.0], ], Mandel::General).unwrap(); let mut dd = Tensor4::new(Mandel::General); - t2_odyad_t2(&mut dd, 2.0, &a, &b).unwrap(); - let mat = dd.to_matrix(); + t2_odyad_t2(&mut dd, 2.0, &a, &b); + let mat = dd.as_matrix(); let correct = Matrix::from(&[ [18.0, 32.0, 42.0, 16.0, 28.0, 14.0, 36.0, 48.0, 54.0], [48.0, 50.0, 48.0, 40.0, 40.0, 32.0, 60.0, 60.0, 72.0], @@ -2452,8 +2749,8 @@ mod tests { [6.0, 4.0, 1.0], ], Mandel::Symmetric).unwrap(); let mut dd = Tensor4::new(Mandel::General); - t2_odyad_t2(&mut dd, 2.0, &a, &b).unwrap(); - let mat = dd.to_matrix(); + t2_odyad_t2(&mut dd, 2.0, &a, &b); + let mat = dd.as_matrix(); let correct = Matrix::from(&[ [6.0, 40.0, 72.0, 10.0, 48.0, 12.0, 24.0, 60.0, 36.0], [40.0, 8.0, 40.0, 16.0, 16.0, 32.0, 20.0, 20.0, 50.0], @@ -2482,8 +2779,8 @@ mod tests { [0.0, 0.0, 1.0], ], Mandel::Symmetric2D).unwrap(); let mut dd = Tensor4::new(Mandel::General); - t2_odyad_t2(&mut dd, 2.0, &a, &b).unwrap(); - let mat = dd.to_matrix(); + t2_odyad_t2(&mut dd, 2.0, &a, &b); + let mat = dd.as_matrix(); // println!("{:.1}", mat); let correct = Matrix::from(&[ [6.0, 32.0, 0.0, 8.0, 0.0, 0.0, 24.0, 0.0, 0.0], @@ -2501,26 +2798,27 @@ mod tests { } #[test] - fn t2_udyad_t2_captures_errors() { - let a = Tensor2::new(Mandel::General); - let b = Tensor2::new(Mandel::General); - let mut dd = Tensor4::new(Mandel::Symmetric); - assert_eq!( - t2_udyad_t2(&mut dd, 1.0, &a, &b).err(), - Some("D tensor must be General") - ); - let a = Tensor2::new(Mandel::Symmetric); + #[should_panic] + fn t2_udyad_t2_panics_on_non_general() { + let a = Tensor2::new(Mandel::Symmetric2D); + let b = Tensor2::new(Mandel::Symmetric2D); + let mut dd = Tensor4::new(Mandel::Symmetric2D); // wrong; it must be General + t2_udyad_t2(&mut dd, 1.0, &a, &b); + } + + #[test] + #[should_panic] + fn t2_udyad_t2_panics_on_different_mandel() { + let a = Tensor2::new(Mandel::Symmetric2D); + let b = Tensor2::new(Mandel::Symmetric); // wrong; it must be the same as `a` let mut dd = Tensor4::new(Mandel::General); - assert_eq!( - t2_udyad_t2(&mut dd, 1.0, &a, &b).err(), - Some("A and B tensors must be compatible") - ); + t2_udyad_t2(&mut dd, 1.0, &a, &b); } fn check_udyad(s: f64, a_ten: &Tensor2, b_ten: &Tensor2, dd_ten: &Tensor4, tol: f64) { - let a = a_ten.to_matrix(); - let b = b_ten.to_matrix(); - let dd = dd_ten.to_matrix(); + let a = a_ten.as_matrix(); + let b = b_ten.as_matrix(); + let dd = dd_ten.as_matrix(); let mut correct = Matrix::new(9, 9); for m in 0..9 { for n in 0..9 { @@ -2547,8 +2845,8 @@ mod tests { [3.0, 2.0, 1.0], ], Mandel::General).unwrap(); let mut dd = Tensor4::new(Mandel::General); - t2_udyad_t2(&mut dd, 2.0, &a, &b).unwrap(); - let mat = dd.to_matrix(); + t2_udyad_t2(&mut dd, 2.0, &a, &b); + let mat = dd.as_matrix(); let correct = Matrix::from(&[ [18.0, 32.0, 42.0, 36.0, 48.0, 54.0, 16.0, 28.0, 14.0], [48.0, 50.0, 48.0, 60.0, 60.0, 72.0, 40.0, 40.0, 32.0], @@ -2577,8 +2875,8 @@ mod tests { [6.0, 4.0, 1.0], ], Mandel::Symmetric).unwrap(); let mut dd = Tensor4::new(Mandel::General); - t2_udyad_t2(&mut dd, 2.0, &a, &b).unwrap(); - let mat = dd.to_matrix(); + t2_udyad_t2(&mut dd, 2.0, &a, &b); + let mat = dd.as_matrix(); let correct = Matrix::from(&[ [6.0, 40.0, 72.0, 24.0, 60.0, 36.0, 10.0, 48.0, 12.0], [40.0, 8.0, 40.0, 20.0, 20.0, 50.0, 16.0, 16.0, 32.0], @@ -2607,7 +2905,7 @@ mod tests { [0.0, 0.0, 1.0], ], Mandel::Symmetric2D).unwrap(); let mut dd = Tensor4::new(Mandel::General); - t2_udyad_t2(&mut dd, 2.0, &a, &b).unwrap(); + t2_udyad_t2(&mut dd, 2.0, &a, &b); let mandel_mat = Matrix::from(&[ [6.0, 32.0, 0.0, 16.0 * SQRT_2, 0.0, 0.0, 8.0 * SQRT_2, 0.0, 0.0], [32.0, 8.0, 0.0, 16.0 * SQRT_2, 0.0, 0.0, 0.0, 0.0, 0.0], @@ -2620,7 +2918,7 @@ mod tests { [0.0, 0.0, 0.0, 0.0, -8.0, -8.0, 0.0, -16.0, -10.0], ]); mat_approx_eq(&dd.mat, &mandel_mat, 1e-14); - let mat = dd.to_matrix(); + let mat = dd.as_matrix(); let correct = Matrix::from(&[ [6.0, 32.0, 0.0, 24.0, 0.0, 0.0, 8.0, 0.0, 0.0], [32.0, 8.0, 0.0, 16.0, 0.0, 0.0, 16.0, 0.0, 0.0], @@ -2637,15 +2935,16 @@ mod tests { } #[test] - fn t2_ssd_captures_errors() { - let a = Tensor2::new(Mandel::General); - let mut dd = Tensor4::new(Mandel::General); - assert_eq!(t2_ssd(&mut dd, 1.0, &a).err(), Some("D tensor must be Symmetric")); + #[should_panic] + fn t2_ssd_panics_on_non_sym() { + let a = Tensor2::new(Mandel::Symmetric2D); + let mut dd = Tensor4::new(Mandel::Symmetric2D); // wrong; it must be Symmetric + t2_ssd(&mut dd, 1.0, &a); } fn check_ssd(s: f64, a_ten: &Tensor2, dd_ten: &Tensor4, tol: f64) { - let a = a_ten.to_matrix(); - let dd = dd_ten.to_matrix(); + let a = a_ten.as_matrix(); + let dd = dd_ten.as_matrix(); let mut correct = Matrix::new(9, 9); for m in 0..9 { for n in 0..9 { @@ -2666,8 +2965,8 @@ mod tests { [7.0, 8.0, 9.0], ], Mandel::General).unwrap(); let mut dd = Tensor4::new(Mandel::Symmetric); - t2_ssd(&mut dd, 2.0, &a).unwrap(); - let mat = dd.to_matrix(); + t2_ssd(&mut dd, 2.0, &a); + let mat = dd.as_matrix(); let correct = Matrix::from(&[ [4.0, 16.0, 36.0, 8.0, 24.0, 12.0, 8.0, 24.0, 12.0], [64.0, 100.0, 144.0, 80.0, 120.0, 96.0, 80.0, 120.0, 96.0], @@ -2690,8 +2989,8 @@ mod tests { [6.0, 5.0, 3.0], ], Mandel::Symmetric).unwrap(); let mut dd = Tensor4::new(Mandel::Symmetric); - t2_ssd(&mut dd, 2.0, &a).unwrap(); - let mat = dd.to_matrix(); + t2_ssd(&mut dd, 2.0, &a); + let mat = dd.as_matrix(); let correct = Matrix::from(&[ [4.0, 64.0, 144.0, 16.0, 96.0, 24.0, 16.0, 96.0, 24.0], [64.0, 16.0, 100.0, 32.0, 40.0, 80.0, 32.0, 40.0, 80.0], @@ -2714,8 +3013,8 @@ mod tests { [0.0, 0.0, 3.0], ], Mandel::Symmetric2D).unwrap(); let mut dd = Tensor4::new(Mandel::Symmetric); - t2_ssd(&mut dd, 2.0, &a).unwrap(); - let mat = dd.to_matrix(); + t2_ssd(&mut dd, 2.0, &a); + let mat = dd.as_matrix(); let correct = Matrix::from(&[ [4.0, 64.0, 0.0, 16.0, 0.0, 0.0, 16.0, 0.0, 0.0], [64.0, 16.0, 0.0, 32.0, 0.0, 0.0, 32.0, 0.0, 0.0], @@ -2732,26 +3031,27 @@ mod tests { } #[test] - fn t2_qsd_t2_captures_errors() { - let a = Tensor2::new(Mandel::General); - let b = Tensor2::new(Mandel::General); - let mut dd = Tensor4::new(Mandel::General); - assert_eq!( - t2_qsd_t2(&mut dd, 1.0, &a, &b).err(), - Some("D tensor must be Symmetric") - ); - let a = Tensor2::new(Mandel::Symmetric); + #[should_panic] + fn t2_qsd_t2_panics_on_non_sym() { + let a = Tensor2::new(Mandel::Symmetric2D); + let b = Tensor2::new(Mandel::Symmetric2D); + let mut dd = Tensor4::new(Mandel::Symmetric2D); // wrong; it must be Symmetric + t2_qsd_t2(&mut dd, 1.0, &a, &b); + } + + #[test] + #[should_panic] + fn t2_qsd_t2_panics_on_different_mandel() { + let a = Tensor2::new(Mandel::Symmetric2D); + let b = Tensor2::new(Mandel::Symmetric); // wrong; it must be the same as `a` let mut dd = Tensor4::new(Mandel::Symmetric); - assert_eq!( - t2_qsd_t2(&mut dd, 1.0, &a, &b).err(), - Some("A and B tensors must be compatible") - ); + t2_qsd_t2(&mut dd, 1.0, &a, &b); } fn check_qsd(s: f64, a_ten: &Tensor2, b_ten: &Tensor2, dd_ten: &Tensor4, tol: f64) { - let a = a_ten.to_matrix(); - let b = b_ten.to_matrix(); - let dd = dd_ten.to_matrix(); + let a = a_ten.as_matrix(); + let b = b_ten.as_matrix(); + let dd = dd_ten.as_matrix(); let mut correct = Matrix::new(9, 9); for m in 0..9 { for n in 0..9 { @@ -2786,8 +3086,8 @@ mod tests { [3.0, 2.0, 1.0], ], Mandel::General).unwrap(); let mut dd = Tensor4::new(Mandel::Symmetric); - t2_qsd_t2(&mut dd, 2.0, &a, &b).unwrap(); - let mat = dd.to_matrix(); + t2_qsd_t2(&mut dd, 2.0, &a, &b); + let mat = dd.as_matrix(); let correct = Matrix::from(&[ [72.0, 128.0, 168.0, 104.0, 152.0, 136.0, 104.0, 152.0, 136.0], [192.0, 200.0, 192.0, 200.0, 200.0, 208.0, 200.0, 200.0, 208.0], @@ -2816,8 +3116,8 @@ mod tests { [6.0, 4.0, 1.0], ], Mandel::Symmetric).unwrap(); let mut dd = Tensor4::new(Mandel::Symmetric); - t2_qsd_t2(&mut dd, 2.0, &a, &b).unwrap(); - let mat = dd.to_matrix(); + t2_qsd_t2(&mut dd, 2.0, &a, &b); + let mat = dd.as_matrix(); let correct = Matrix::from(&[ [24.0, 160.0, 288.0, 68.0, 216.0, 96.0, 68.0, 216.0, 96.0], [160.0, 32.0, 160.0, 72.0, 72.0, 164.0, 72.0, 72.0, 164.0], @@ -2846,8 +3146,8 @@ mod tests { [0.0, 0.0, 1.0], ], Mandel::Symmetric2D).unwrap(); let mut dd = Tensor4::new(Mandel::Symmetric); - t2_qsd_t2(&mut dd, 2.0, &a, &b).unwrap(); - let mat = dd.to_matrix(); + t2_qsd_t2(&mut dd, 2.0, &a, &b); + let mat = dd.as_matrix(); let correct = Matrix::from(&[ [24.0, 128.0, 0.0, 64.0, 0.0, 0.0, 64.0, 0.0, 0.0], [128.0, 32.0, 0.0, 64.0, 0.0, 0.0, 64.0, 0.0, 0.0], @@ -2863,6 +3163,24 @@ mod tests { check_qsd(2.0, &a, &b, &dd, 1e-14); } + #[test] + #[should_panic] + fn t4_ddot_t2_panics_on_different_mandel1() { + let a = Tensor2::new(Mandel::Symmetric); // wrong; it must be the same as `dd` + let mut b = Tensor2::new(Mandel::Symmetric2D); + let dd = Tensor4::new(Mandel::Symmetric2D); + t4_ddot_t2(&mut b, 1.0, &dd, &a); + } + + #[test] + #[should_panic] + fn t4_ddot_t2_panics_on_different_mandel2() { + let a = Tensor2::new(Mandel::Symmetric2D); + let mut b = Tensor2::new(Mandel::Symmetric); // wrong; it must be the same as `dd` + let dd = Tensor4::new(Mandel::Symmetric2D); + t4_ddot_t2(&mut b, 1.0, &dd, &a); + } + #[test] fn t4_ddot_t2_works() { let dd = Tensor4::from_matrix(&SamplesTensor4::SYM_2D_SAMPLE1_STD_MATRIX, Mandel::Symmetric2D).unwrap(); @@ -2872,8 +3190,8 @@ mod tests { [-2.0, 2.0, 0.0], [ 0.0, 0.0, -3.0]], Mandel::Symmetric2D).unwrap(); let mut b = Tensor2::new(Mandel::Symmetric2D); - t4_ddot_t2(&mut b, 1.0, &dd, &a).unwrap(); - let out = b.to_matrix(); + t4_ddot_t2(&mut b, 1.0, &dd, &a); + let out = b.as_matrix(); assert_eq!( format!("{:.1}", out), "┌ ┐\n\ @@ -2884,6 +3202,24 @@ mod tests { ); } + #[test] + #[should_panic] + fn t4_ddot_t2_update_panics_on_different_mandel1() { + let a = Tensor2::new(Mandel::Symmetric); // wrong; it must be the same as `dd` + let mut b = Tensor2::new(Mandel::Symmetric2D); + let dd = Tensor4::new(Mandel::Symmetric2D); + t4_ddot_t2_update(&mut b, 1.0, &dd, &a, 1.0); + } + + #[test] + #[should_panic] + fn t4_ddot_update_t2_panics_on_different_mandel2() { + let a = Tensor2::new(Mandel::Symmetric2D); + let mut b = Tensor2::new(Mandel::Symmetric); // wrong; it must be the same as `dd` + let dd = Tensor4::new(Mandel::Symmetric2D); + t4_ddot_t2_update(&mut b, 1.0, &dd, &a, 1.0); + } + #[test] fn t4_ddot_t2_update_works() { let dd = Tensor4::from_matrix(&SamplesTensor4::SYM_2D_SAMPLE1_STD_MATRIX, Mandel::Symmetric2D).unwrap(); @@ -2899,8 +3235,8 @@ mod tests { [-1000.0, -1000.0, 0.0], [ 0.0, 0.0, -1000.0], ], Mandel::Symmetric2D).unwrap(); - t4_ddot_t2_update(&mut b, 1.0, &dd, &a, 2.0).unwrap(); - let out = b.to_matrix(); + t4_ddot_t2_update(&mut b, 1.0, &dd, &a, 2.0); + let out = b.as_matrix(); assert_eq!( format!("{:.1}", out), "┌ ┐\n\ @@ -2911,6 +3247,24 @@ mod tests { ); } + #[test] + #[should_panic] + fn t2_ddot_t4_panics_on_different_mandel1() { + let a = Tensor2::new(Mandel::Symmetric); // wrong; it must be the same as `dd` + let mut b = Tensor2::new(Mandel::Symmetric2D); + let dd = Tensor4::new(Mandel::Symmetric2D); + t2_ddot_t4(&mut b, 1.0, &a, &dd); + } + + #[test] + #[should_panic] + fn t2_ddot_t4_panics_on_different_mandel2() { + let a = Tensor2::new(Mandel::Symmetric2D); + let mut b = Tensor2::new(Mandel::Symmetric); // wrong; it must be the same as `dd` + let dd = Tensor4::new(Mandel::Symmetric2D); + t2_ddot_t4(&mut b, 1.0, &a, &dd); + } + #[test] fn t2_ddot_t4_works() { let dd = Tensor4::from_matrix(&SamplesTensor4::SYM_2D_SAMPLE1_STD_MATRIX, Mandel::Symmetric2D).unwrap(); @@ -2920,8 +3274,8 @@ mod tests { [-2.0, 2.0, 0.0], [ 0.0, 0.0, -3.0]], Mandel::Symmetric2D).unwrap(); let mut b = Tensor2::new(Mandel::Symmetric2D); - t2_ddot_t4(&mut b, 1.0, &a, &dd).unwrap(); - let out = b.to_matrix(); + t2_ddot_t4(&mut b, 1.0, &a, &dd); + let out = b.as_matrix(); assert_eq!( format!("{:.1}", out), "┌ ┐\n\ @@ -2932,28 +3286,108 @@ mod tests { ); } + #[test] + #[should_panic] + fn t4_ddot_t4_panics_on_different_mandel1() { + let cc = Tensor4::new(Mandel::Symmetric); // wrong; it must be the same as `dd` + let dd = Tensor4::new(Mandel::Symmetric2D); + let mut ee = Tensor4::new(Mandel::Symmetric2D); + t4_ddot_t4(&mut ee, 1.0, &cc, &dd); + } + + #[test] + #[should_panic] + fn t4_ddot_t4_panics_on_different_mandel2() { + let cc = Tensor4::new(Mandel::Symmetric2D); + let dd = Tensor4::new(Mandel::Symmetric); // wrong; it must be the same as `dd` + let mut ee = Tensor4::new(Mandel::Symmetric2D); + t4_ddot_t4(&mut ee, 1.0, &cc, &dd); + } + #[test] fn t4_ddot_t4_works() { let cc = Tensor4::from_matrix(&SamplesTensor4::SYM_2D_SAMPLE1_STD_MATRIX, Mandel::Symmetric2D).unwrap(); let mut ee = Tensor4::new(Mandel::Symmetric2D); - t4_ddot_t4(&mut ee, 1.0, &cc, &cc).unwrap(); - let out = ee.to_matrix(); + t4_ddot_t4(&mut ee, 2.0, &cc, &cc); + let out = ee.as_matrix(); + assert_eq!( + format!("{:.1}", out), + "┌ ┐\n\ + │ 820.0 872.0 924.0 1288.0 0.0 0.0 1288.0 0.0 0.0 │\n\ + │ 1120.0 1202.0 1284.0 1858.0 0.0 0.0 1858.0 0.0 0.0 │\n\ + │ 1420.0 1532.0 1644.0 2428.0 0.0 0.0 2428.0 0.0 0.0 │\n\ + │ 2620.0 2852.0 3084.0 4708.0 0.0 0.0 4708.0 0.0 0.0 │\n\ + │ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 │\n\ + │ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 │\n\ + │ 2620.0 2852.0 3084.0 4708.0 0.0 0.0 4708.0 0.0 0.0 │\n\ + │ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 │\n\ + │ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 │\n\ + └ ┘" + ); + } + + #[test] + #[should_panic] + fn t4_ddot_t4_update_panics_on_different_mandel1() { + let cc = Tensor4::new(Mandel::Symmetric); // wrong; it must be the same as `dd` + let dd = Tensor4::new(Mandel::Symmetric2D); + let mut ee = Tensor4::new(Mandel::Symmetric2D); + t4_ddot_t4(&mut ee, 1.0, &cc, &dd); + } + + #[test] + #[should_panic] + fn t4_ddot_t4_update_panics_on_different_mandel2() { + let cc = Tensor4::new(Mandel::Symmetric2D); + let dd = Tensor4::new(Mandel::Symmetric); // wrong; it must be the same as `dd` + let mut ee = Tensor4::new(Mandel::Symmetric2D); + t4_ddot_t4(&mut ee, 1.0, &cc, &dd); + } + + #[test] + fn t4_ddot_t4_update_works() { + let cc = Tensor4::from_matrix(&SamplesTensor4::SYM_2D_SAMPLE1_STD_MATRIX, Mandel::Symmetric2D).unwrap(); + let mut mat = Matrix::new(9, 9); + mat.set(0, 0, 0.1); + mat.set(1, 1, 0.1); + mat.set(2, 2, 0.1); + let mut ee = Tensor4::from_matrix(&mat, Mandel::Symmetric2D).unwrap(); + t4_ddot_t4_update(&mut ee, 2.0, &cc, &cc, 2.0); + let out = ee.as_matrix(); assert_eq!( format!("{:.1}", out), "┌ ┐\n\ - │ 410.0 436.0 462.0 644.0 0.0 0.0 644.0 0.0 0.0 │\n\ - │ 560.0 601.0 642.0 929.0 0.0 0.0 929.0 0.0 0.0 │\n\ - │ 710.0 766.0 822.0 1214.0 0.0 0.0 1214.0 0.0 0.0 │\n\ - │ 1310.0 1426.0 1542.0 2354.0 0.0 0.0 2354.0 0.0 0.0 │\n\ + │ 820.2 872.0 924.0 1288.0 0.0 0.0 1288.0 0.0 0.0 │\n\ + │ 1120.0 1202.2 1284.0 1858.0 0.0 0.0 1858.0 0.0 0.0 │\n\ + │ 1420.0 1532.0 1644.2 2428.0 0.0 0.0 2428.0 0.0 0.0 │\n\ + │ 2620.0 2852.0 3084.0 4708.0 0.0 0.0 4708.0 0.0 0.0 │\n\ │ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 │\n\ │ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 │\n\ - │ 1310.0 1426.0 1542.0 2354.0 0.0 0.0 2354.0 0.0 0.0 │\n\ + │ 2620.0 2852.0 3084.0 4708.0 0.0 0.0 4708.0 0.0 0.0 │\n\ │ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 │\n\ │ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 │\n\ └ ┘" ); } + #[test] + #[should_panic] + fn t2_ddot_t4_ddot_t2_panics_on_different_mandel1() { + let a = Tensor2::new(Mandel::Symmetric); // wrong; it must be the same as `dd` + let b = Tensor2::new(Mandel::Symmetric2D); + let dd = Tensor4::new(Mandel::Symmetric2D); + t2_ddot_t4_ddot_t2(&a, &dd, &b); + } + + #[test] + #[should_panic] + fn t2_ddot_t4_ddot_t2_panics_on_different_mandel2() { + let a = Tensor2::new(Mandel::Symmetric2D); + let b = Tensor2::new(Mandel::Symmetric); // wrong; it must be the same as `dd` + let dd = Tensor4::new(Mandel::Symmetric2D); + t2_ddot_t4_ddot_t2(&a, &dd, &b); + } + #[test] fn t2_ddot_t4_ddot_t2_works() { #[rustfmt::skip] @@ -2970,15 +3404,38 @@ mod tests { ], Mandel::General).unwrap(); let mat = Matrix::filled(9, 9, -1.0); let dd = Tensor4::from_matrix(&mat, Mandel::General).unwrap(); - let s = t2_ddot_t4_ddot_t2(&a, &dd, &b).unwrap(); + let s = t2_ddot_t4_ddot_t2(&a, &dd, &b); approx_eq(s, -2025.0, 1e-15); + } + + #[test] + #[should_panic] + fn t4_ddot_t2_dyad_t2_ddot_t4_panics_on_different_mandel1() { + let a = Tensor2::new(Mandel::Symmetric); // wrong; it must be the same as `ee` + let b = Tensor2::new(Mandel::Symmetric2D); + let dd = Tensor4::new(Mandel::Symmetric2D); + let mut ee = Tensor4::new(Mandel::Symmetric2D); + t4_ddot_t2_dyad_t2_ddot_t4(&mut ee, 2.0, &a, &b, &dd); + } - let b = Tensor2::from_matrix( - &[[-1.0, -2.0, 0.0], [-2.0, 2.0, 0.0], [0.0, 0.0, -3.0]], - Mandel::Symmetric2D, - ) - .unwrap(); - assert_eq!(t2_ddot_t4_ddot_t2(&a, &dd, &b).err(), Some("tensors are incompatible")); + #[test] + #[should_panic] + fn t4_ddot_t2_dyad_t2_ddot_t4_panics_on_different_mandel2() { + let a = Tensor2::new(Mandel::Symmetric2D); + let b = Tensor2::new(Mandel::Symmetric); // wrong; it must be the same as `ee` + let dd = Tensor4::new(Mandel::Symmetric2D); + let mut ee = Tensor4::new(Mandel::Symmetric2D); + t4_ddot_t2_dyad_t2_ddot_t4(&mut ee, 2.0, &a, &b, &dd); + } + + #[test] + #[should_panic] + fn t4_ddot_t2_dyad_t2_ddot_t4_panics_on_different_mandel3() { + let a = Tensor2::new(Mandel::Symmetric2D); + let b = Tensor2::new(Mandel::Symmetric2D); + let dd = Tensor4::new(Mandel::Symmetric); // wrong; it must be the same as `ee` + let mut ee = Tensor4::new(Mandel::Symmetric2D); + t4_ddot_t2_dyad_t2_ddot_t4(&mut ee, 2.0, &a, &b, &dd); } #[test] @@ -2998,7 +3455,7 @@ mod tests { let mat = Matrix::filled(9, 9, -1.0); let dd = Tensor4::from_matrix(&mat, Mandel::General).unwrap(); let mut ee = Tensor4::new(Mandel::General); - t4_ddot_t2_dyad_t2_ddot_t4(&mut ee, 2.0, &a, &b, &dd).unwrap(); + t4_ddot_t2_dyad_t2_ddot_t4(&mut ee, 2.0, &a, &b, &dd); let correct = [ [4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050.], [4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050.], @@ -3010,16 +3467,6 @@ mod tests { [4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050.], [4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050.], ]; - mat_approx_eq(&ee.to_matrix(), &correct, 1e-11); - - let b = Tensor2::from_matrix( - &[[-1.0, -2.0, 0.0], [-2.0, 2.0, 0.0], [0.0, 0.0, -3.0]], - Mandel::Symmetric2D, - ) - .unwrap(); - assert_eq!( - t4_ddot_t2_dyad_t2_ddot_t4(&mut ee, 2.0, &a, &b, &dd).err(), - Some("tensors are incompatible") - ); + mat_approx_eq(&ee.as_matrix(), &correct, 1e-11); } } From 4724441e34d8b9cd755fc485db557d4e578bf56c Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Sun, 12 May 2024 22:18:06 +1000 Subject: [PATCH 28/44] [wip] Update spectral2.rs --- russell_tensor/src/spectral2.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/russell_tensor/src/spectral2.rs b/russell_tensor/src/spectral2.rs index cf83a32d..f8c2b018 100644 --- a/russell_tensor/src/spectral2.rs +++ b/russell_tensor/src/spectral2.rs @@ -39,13 +39,13 @@ impl Spectral2 { /// /// TODO: Rewrite this function to avoid temporary memory allocation pub fn decompose(&mut self, tt: &Tensor2) -> Result<(), StrError> { - if tt.mandel() != self.mandel { + if tt.mandel != self.mandel { return Err("the mandel representation is incompatible"); } let dim = tt.vec.dim(); if dim == 4 { // eigenvalues and eigenvectors - let (t22, mut a) = tt.to_matrix_2d(); + let (t22, mut a) = tt.as_matrix_2d(); let mut l = Vector::new(2); let mut v = Matrix::new(2, 2); mat_eigen_sym_jacobi(&mut l, &mut v, &mut a)?; @@ -64,7 +64,7 @@ impl Spectral2 { self.projectors[2].vec[2] = 1.0; } else { // eigenvalues and eigenvectors - let mut a = tt.to_matrix(); + let mut a = tt.as_matrix(); let mut v = Matrix::new(3, 3); mat_eigen_sym_jacobi(&mut self.lambda, &mut v, &mut a)?; @@ -83,7 +83,7 @@ impl Spectral2 { /// Composes a new tensor from the eigenprojectors and diagonal values (lambda) pub fn compose(&self, composed: &mut Tensor2, lambda: &Vector) -> Result<(), StrError> { - if composed.mandel() != self.mandel { + if composed.mandel != self.mandel { return Err("the mandel representation is incompatible"); } if lambda.dim() != 3 { @@ -155,26 +155,26 @@ mod tests { if let Some(correct_lambda) = sample.eigenvalues { if let Some(correct_projectors) = sample.eigenprojectors { // perform spectral decomposition of symmetric matrix - let mandel = spec.projectors[0].mandel(); + let mandel = spec.projectors[0].mandel; let tt = Tensor2::from_matrix(&sample.matrix, mandel).unwrap(); spec.decompose(&tt).unwrap(); // print results if verbose { - println!("a =\n{}", tt.to_matrix()); + println!("a =\n{}", tt.as_matrix()); println!("λ = {}, {}, {}", spec.lambda[0], spec.lambda[1], spec.lambda[2]); - println!("P0 =\n{}", spec.projectors[0].to_matrix()); - println!("P1 =\n{}", spec.projectors[1].to_matrix()); - println!("P2 =\n{}", spec.projectors[2].to_matrix()); + println!("P0 =\n{}", spec.projectors[0].as_matrix()); + println!("P1 =\n{}", spec.projectors[1].as_matrix()); + println!("P2 =\n{}", spec.projectors[2].as_matrix()); } // check eigenvalues vec_approx_eq(&spec.lambda, &correct_lambda, tol_lambda); // check eigenprojectors - let pp0 = spec.projectors[0].to_matrix(); - let pp1 = spec.projectors[1].to_matrix(); - let pp2 = spec.projectors[2].to_matrix(); + let pp0 = spec.projectors[0].as_matrix(); + let pp1 = spec.projectors[1].as_matrix(); + let pp2 = spec.projectors[2].as_matrix(); let correct0 = Matrix::from(&correct_projectors[0]); let correct1 = Matrix::from(&correct_projectors[1]); let correct2 = Matrix::from(&correct_projectors[2]); @@ -185,7 +185,7 @@ mod tests { // compose let mut tt_new = Tensor2::new(mandel); spec.compose(&mut tt_new, &spec.lambda).unwrap(); - let a_new = tt_new.to_matrix(); + let a_new = tt_new.as_matrix(); let a = Matrix::from(&sample.matrix); mat_approx_eq(&a, &a_new, tol_spectral); } From 8565988dc9c6d5531d3714cf2de0c1b392f83d49 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Sun, 12 May 2024 22:18:52 +1000 Subject: [PATCH 29/44] [wip] Update lin_elasticity --- russell_tensor/src/lin_elasticity.rs | 30 ++++++++++++++++------------ 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/russell_tensor/src/lin_elasticity.rs b/russell_tensor/src/lin_elasticity.rs index 099a5bfa..a39079e0 100644 --- a/russell_tensor/src/lin_elasticity.rs +++ b/russell_tensor/src/lin_elasticity.rs @@ -196,11 +196,15 @@ impl LinElasticity { /// /// # Output /// - /// * `stress` -- the stress tensor σ + /// * `stress` -- the stress tensor σ; with the same [Mandel] as `strain` /// /// # Input /// - /// * `strain` -- the strain tensor ε + /// * `strain` -- the strain tensor ε; with the same [Mandel] as `stress` + /// + /// # Panics + /// + /// A panic will occur if the tensors have different [Mandel] /// /// # Examples /// @@ -287,8 +291,8 @@ impl LinElasticity { /// Ok(()) /// } /// ``` - pub fn calc_stress(&self, stress: &mut Tensor2, strain: &Tensor2) -> Result<(), StrError> { - t4_ddot_t2(stress, 1.0, &self.dd, strain) + pub fn calc_stress(&self, stress: &mut Tensor2, strain: &Tensor2) { + t4_ddot_t2(stress, 1.0, &self.dd, strain); } /// Calculates and sets the out-of-plane strain in the Plane-Stress case @@ -373,7 +377,7 @@ mod tests { // plane-stress // from Bhatti page 511 (Young divided by 1000) let ela = LinElasticity::new(3000.0, 0.2, false, true); - let out = ela.dd.to_matrix(); + let out = ela.dd.as_matrix(); assert_eq!( format!("{}", out), "┌ ┐\n\ @@ -392,7 +396,7 @@ mod tests { // plane-strain // from Bhatti page 519 let ela = LinElasticity::new(30000.0, 0.3, true, false); - let out = ela.dd.to_matrix(); + let out = ela.dd.as_matrix(); assert_eq!( format!("{:.1}", out), "┌ ┐\n\ @@ -455,8 +459,8 @@ mod tests { Mandel::Symmetric2D, ).unwrap(); let mut stress = Tensor2::new(Mandel::Symmetric2D); - ela.calc_stress(&mut stress, &strain).unwrap(); - let out = stress.to_matrix(); + ela.calc_stress(&mut stress, &strain); + let out = stress.as_matrix(); assert_eq!( format!("{:.3}", out), "┌ ┐\n\ @@ -479,8 +483,8 @@ mod tests { Mandel::Symmetric2D, ).unwrap(); let mut stress = Tensor2::new(Mandel::Symmetric2D); - ela.calc_stress(&mut stress, &strain).unwrap(); - let out = stress.to_matrix(); + ela.calc_stress(&mut stress, &strain); + let out = stress.as_matrix(); assert_eq!( format!("{:.6}", out), "┌ ┐\n\ @@ -494,7 +498,7 @@ mod tests { // sum of first 3 rows = 1800 // sum of other rows = 720 let ela = LinElasticity::new(900.0, 0.25, false, false); - let out = ela.dd.to_matrix(); + let out = ela.dd.as_matrix(); assert_eq!( format!("{}", out), "┌ ┐\n\ @@ -516,8 +520,8 @@ mod tests { [1.0, 1.0, 1.0]], Mandel::Symmetric).unwrap(); let mut stress = Tensor2::new(Mandel::Symmetric); - ela.calc_stress(&mut stress, &strain).unwrap(); - let out = stress.to_matrix(); + ela.calc_stress(&mut stress, &strain); + let out = stress.as_matrix(); assert_eq!( format!("{:.0}", out), "┌ ┐\n\ From dd9d62906ca977e5d035997728b1ab926e8e22f2 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Sun, 12 May 2024 22:39:08 +1000 Subject: [PATCH 30/44] Impl Tensor2 and Tensor4 vector and matrix member functions --- russell_tensor/README.md | 6 +-- .../allocating_second_order_tensors.rs | 6 +-- russell_tensor/src/tensor2.rs | 53 ++++++++++--------- russell_tensor/src/tensor4.rs | 21 +++++--- 4 files changed, 48 insertions(+), 38 deletions(-) diff --git a/russell_tensor/README.md b/russell_tensor/README.md index 6365d9d7..2670a1d7 100644 --- a/russell_tensor/README.md +++ b/russell_tensor/README.md @@ -78,7 +78,7 @@ fn main() -> Result<(), StrError> { Mandel::General, )?; assert_eq!( - format!("{:.1}", a.vec), + format!("{:.1}", a.vector()), "┌ ┐\n\ │ 1.0 │\n\ │ 5.0 │\n\ @@ -102,7 +102,7 @@ fn main() -> Result<(), StrError> { Mandel::Symmetric, )?; assert_eq!( - format!("{:.1}", b.vec), + format!("{:.1}", b.vector()), "┌ ┐\n\ │ 1.0 │\n\ │ 2.0 │\n\ @@ -119,7 +119,7 @@ fn main() -> Result<(), StrError> { Mandel::Symmetric2D, )?; assert_eq!( - format!("{:.1}", c.vec), + format!("{:.1}", c.vector()), "┌ ┐\n\ │ 1.0 │\n\ │ 2.0 │\n\ diff --git a/russell_tensor/examples/allocating_second_order_tensors.rs b/russell_tensor/examples/allocating_second_order_tensors.rs index c7239464..142db1ec 100644 --- a/russell_tensor/examples/allocating_second_order_tensors.rs +++ b/russell_tensor/examples/allocating_second_order_tensors.rs @@ -11,7 +11,7 @@ fn main() -> Result<(), StrError> { Mandel::General, )?; assert_eq!( - format!("{:.1}", a.vec), + format!("{:.1}", a.vector()), "┌ ┐\n\ │ 1.0 │\n\ │ 5.0 │\n\ @@ -35,7 +35,7 @@ fn main() -> Result<(), StrError> { Mandel::Symmetric, )?; assert_eq!( - format!("{:.1}", b.vec), + format!("{:.1}", b.vector()), "┌ ┐\n\ │ 1.0 │\n\ │ 2.0 │\n\ @@ -52,7 +52,7 @@ fn main() -> Result<(), StrError> { Mandel::Symmetric2D, )?; assert_eq!( - format!("{:.1}", c.vec), + format!("{:.1}", c.vector()), "┌ ┐\n\ │ 1.0 │\n\ │ 2.0 │\n\ diff --git a/russell_tensor/src/tensor2.rs b/russell_tensor/src/tensor2.rs index 87dc415b..6cc63b0e 100644 --- a/russell_tensor/src/tensor2.rs +++ b/russell_tensor/src/tensor2.rs @@ -76,13 +76,13 @@ impl Tensor2 { /// /// fn main() { /// let a = Tensor2::new(Mandel::General); - /// assert_eq!(a.vec.as_data(), &[0.0,0.0,0.0, 0.0,0.0,0.0, 0.0,0.0,0.0]); + /// assert_eq!(a.vector().as_data(), &[0.0,0.0,0.0, 0.0,0.0,0.0, 0.0,0.0,0.0]); /// /// let b = Tensor2::new(Mandel::Symmetric); - /// assert_eq!(b.vec.as_data(), &[0.0,0.0,0.0, 0.0,0.0,0.0]); + /// assert_eq!(b.vector().as_data(), &[0.0,0.0,0.0, 0.0,0.0,0.0]); /// /// let c = Tensor2::new(Mandel::Symmetric2D); - /// assert_eq!(c.vec.as_data(), &[0.0,0.0,0.0, 0.0]); + /// assert_eq!(c.vector().as_data(), &[0.0,0.0,0.0, 0.0]); /// } /// ``` pub fn new(mandel: Mandel) -> Self { @@ -160,6 +160,11 @@ impl Tensor2 { self.mandel } + /// Returns an access to the underlying Mandel vector + pub fn vector(&self) -> &Vector { + &self.vec + } + /// Sets the Tensor2 with standard components given in matrix form /// /// # Input @@ -187,7 +192,7 @@ impl Tensor2 { /// [SQRT_2 * 7.0, SQRT_2 * 8.0, 9.0], /// ])?; /// assert_eq!( - /// format!("{:.1}", a.vec), + /// format!("{:.1}", a.vector()), /// "┌ ┐\n\ /// │ 1.0 │\n\ /// │ 5.0 │\n\ @@ -209,7 +214,7 @@ impl Tensor2 { /// [6.0 / SQRT_2, 5.0 / SQRT_2, 3.0], /// ])?; /// assert_eq!( - /// format!("{:.1}", b.vec), + /// format!("{:.1}", b.vector()), /// "┌ ┐\n\ /// │ 1.0 │\n\ /// │ 2.0 │\n\ @@ -228,7 +233,7 @@ impl Tensor2 { /// [ 0.0, 0.0, 3.0], /// ])?; /// assert_eq!( - /// format!("{:.1}", c.vec), + /// format!("{:.1}", c.vector()), /// "┌ ┐\n\ /// │ 1.0 │\n\ /// │ 2.0 │\n\ @@ -299,7 +304,7 @@ impl Tensor2 { /// Mandel::General, /// )?; /// assert_eq!( - /// format!("{:.1}", a.vec), + /// format!("{:.1}", a.vector()), /// "┌ ┐\n\ /// │ 1.0 │\n\ /// │ 5.0 │\n\ @@ -323,7 +328,7 @@ impl Tensor2 { /// Mandel::Symmetric, /// )?; /// assert_eq!( - /// format!("{:.1}", b.vec), + /// format!("{:.1}", b.vector()), /// "┌ ┐\n\ /// │ 1.0 │\n\ /// │ 2.0 │\n\ @@ -344,7 +349,7 @@ impl Tensor2 { /// Mandel::Symmetric2D, /// )?; /// assert_eq!( - /// format!("{:.1}", c.vec), + /// format!("{:.1}", c.vector()), /// "┌ ┐\n\ /// │ 1.0 │\n\ /// │ 2.0 │\n\ @@ -371,7 +376,7 @@ impl Tensor2 { /// let ii = Tensor2::identity(Mandel::General); /// /// assert_eq!( - /// format!("{}", ii.vec), + /// format!("{}", ii.vector()), /// "┌ ┐\n\ /// │ 1 │\n\ /// │ 1 │\n\ @@ -607,7 +612,7 @@ impl Tensor2 { /// [0.0, 0.0, 4.0], /// ], Mandel::Symmetric2D)?; /// assert_eq!( - /// format!("{:.2}", tt.vec), + /// format!("{:.2}", tt.vector()), /// "┌ ┐\n\ /// │ 1.00 │\n\ /// │ 3.00 │\n\ @@ -618,7 +623,7 @@ impl Tensor2 { /// /// let tt_gen = tt.as_general(); /// assert_eq!( - /// format!("{:.2}", tt_gen.vec), + /// format!("{:.2}", tt_gen.vector()), /// "┌ ┐\n\ /// │ 1.00 │\n\ /// │ 3.00 │\n\ @@ -675,7 +680,7 @@ impl Tensor2 { /// [0.0, 0.0, 4.0], /// ], Mandel::Symmetric2D)?; /// assert_eq!( - /// format!("{:.2}", tt.vec), + /// format!("{:.2}", tt.vector()), /// "┌ ┐\n\ /// │ 1.00 │\n\ /// │ 3.00 │\n\ @@ -686,7 +691,7 @@ impl Tensor2 { /// /// let tt_sym = tt.sym2d_as_symmetric(); /// assert_eq!( - /// format!("{:.2}", tt_sym.vec), + /// format!("{:.2}", tt_sym.vector()), /// "┌ ┐\n\ /// │ 1.00 │\n\ /// │ 3.00 │\n\ @@ -1027,7 +1032,7 @@ impl Tensor2 { /// [1.2, 2.2, 3.2], /// [1.3, 2.3, 3.3], /// ], Mandel::General)?; - /// vec_approx_eq(&at.vec, &at_correct.vec, 1e-15); + /// vec_approx_eq(&at.vector(), &at_correct.vector(), 1e-15); /// Ok(()) /// } /// ``` @@ -1193,7 +1198,7 @@ impl Tensor2 { /// [ 72.0, 123.0, 100.0], /// [ 42.0, 70.0, 63.0], /// ], Mandel::General)?; - /// vec_approx_eq(&a2.vec, &a2_correct.vec, 1e-13); + /// vec_approx_eq(&a2.vector(), &a2_correct.vec, 1e-13); /// /// Ok(()) /// } @@ -1879,40 +1884,40 @@ mod tests { use russell_lab::{approx_eq, mat_approx_eq, mat_mat_mul, math::PI, vec_approx_eq, Matrix}; #[test] - fn new_and_mandel_work() { + fn new_and_getters_work() { // general let tt = Tensor2::new(Mandel::General); let correct = &[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]; - assert_eq!(tt.vec.as_data(), correct); assert_eq!(tt.mandel(), Mandel::General); + assert_eq!(tt.vector().as_data(), correct); // symmetric 3D let tt = Tensor2::new(Mandel::Symmetric); let correct = &[0.0, 0.0, 0.0, 0.0, 0.0, 0.0]; - assert_eq!(tt.vec.as_data(), correct); assert_eq!(tt.mandel(), Mandel::Symmetric); + assert_eq!(tt.vector().as_data(), correct); let tt = Tensor2::new_sym(false); - assert_eq!(tt.vec.as_data(), correct); assert_eq!(tt.mandel(), Mandel::Symmetric); + assert_eq!(tt.vector().as_data(), correct); let tt = Tensor2::new_sym_ndim(3); - assert_eq!(tt.vec.as_data(), correct); assert_eq!(tt.mandel(), Mandel::Symmetric); + assert_eq!(tt.vector().as_data(), correct); // symmetric 2D let tt = Tensor2::new(Mandel::Symmetric2D); let correct = &[0.0, 0.0, 0.0, 0.0]; - assert_eq!(tt.vec.as_data(), correct); assert_eq!(tt.mandel(), Mandel::Symmetric2D); + assert_eq!(tt.vector().as_data(), correct); let tt = Tensor2::new_sym(true); - assert_eq!(tt.vec.as_data(), correct); assert_eq!(tt.mandel(), Mandel::Symmetric2D); + assert_eq!(tt.vector().as_data(), correct); let tt = Tensor2::new_sym_ndim(2); - assert_eq!(tt.vec.as_data(), correct); assert_eq!(tt.mandel(), Mandel::Symmetric2D); + assert_eq!(tt.vector().as_data(), correct); } #[test] diff --git a/russell_tensor/src/tensor4.rs b/russell_tensor/src/tensor4.rs index 640f62db..fdf9cafa 100644 --- a/russell_tensor/src/tensor4.rs +++ b/russell_tensor/src/tensor4.rs @@ -169,6 +169,11 @@ impl Tensor4 { self.mandel } + /// Returns an access to the underlying Mandel matrix + pub fn matrix(&self) -> &Matrix { + &self.mat + } + /// Creates a new Tensor4 constructed from a nested array /// /// # Input @@ -1311,33 +1316,33 @@ mod tests { use russell_lab::{approx_eq, mat_approx_eq}; #[test] - fn new_and_mandel_work() { + fn new_and_getters_work() { // general let dd = Tensor4::new(Mandel::General); - assert_eq!(dd.mat.as_data().len(), 81); + assert_eq!(dd.matrix().as_data().len(), 81); assert_eq!(dd.mandel(), Mandel::General); // symmetric let dd = Tensor4::new(Mandel::Symmetric); - assert_eq!(dd.mat.as_data().len(), 36); assert_eq!(dd.mandel(), Mandel::Symmetric); + assert_eq!(dd.matrix().as_data().len(), 36); let dd = Tensor4::new_sym(false); - assert_eq!(dd.mat.as_data().len(), 36); assert_eq!(dd.mandel(), Mandel::Symmetric); + assert_eq!(dd.matrix().as_data().len(), 36); let dd = Tensor4::new_sym_ndim(3); - assert_eq!(dd.mat.as_data().len(), 36); assert_eq!(dd.mandel(), Mandel::Symmetric); + assert_eq!(dd.matrix().as_data().len(), 36); // symmetric 2d let dd = Tensor4::new(Mandel::Symmetric2D); - assert_eq!(dd.mat.as_data().len(), 16); assert_eq!(dd.mandel(), Mandel::Symmetric2D); + assert_eq!(dd.matrix().as_data().len(), 16); let dd = Tensor4::new_sym(true); - assert_eq!(dd.mat.as_data().len(), 16); assert_eq!(dd.mandel(), Mandel::Symmetric2D); + assert_eq!(dd.matrix().as_data().len(), 16); let dd = Tensor4::new_sym_ndim(2); - assert_eq!(dd.mat.as_data().len(), 16); assert_eq!(dd.mandel(), Mandel::Symmetric2D); + assert_eq!(dd.matrix().as_data().len(), 16); } #[test] From c3b7e935190bf1bd705337ba712692de042ce26a Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Sun, 12 May 2024 22:52:28 +1000 Subject: [PATCH 31/44] [tensor] Fix doc tests --- russell_tensor/src/lin_elasticity.rs | 22 +++++++-------- russell_tensor/src/operations.rs | 27 +++++++++---------- russell_tensor/src/tensor2.rs | 40 ++++++++++++++-------------- russell_tensor/src/tensor4.rs | 18 ++++++------- 4 files changed, 53 insertions(+), 54 deletions(-) diff --git a/russell_tensor/src/lin_elasticity.rs b/russell_tensor/src/lin_elasticity.rs index a39079e0..3d3b3e54 100644 --- a/russell_tensor/src/lin_elasticity.rs +++ b/russell_tensor/src/lin_elasticity.rs @@ -33,7 +33,7 @@ impl LinElasticity { /// /// // 3D /// let ela = LinElasticity::new(900.0, 0.25, false, false); - /// let out = ela.get_modulus().to_matrix(); + /// let out = ela.get_modulus().as_matrix(); /// assert_eq!( /// format!("{}", out), /// "┌ ┐\n\ @@ -51,7 +51,7 @@ impl LinElasticity { /// /// // 2D plane-strain /// let ela = LinElasticity::new(900.0, 0.25, true, false); - /// let out = ela.get_modulus().to_matrix(); + /// let out = ela.get_modulus().as_matrix(); /// assert_eq!( /// format!("{}", out), /// "┌ ┐\n\ @@ -69,7 +69,7 @@ impl LinElasticity { /// /// // 2D plane-stress /// let ela = LinElasticity::new(3000.0, 0.2, false, true); - /// let out = ela.get_modulus().to_matrix(); + /// let out = ela.get_modulus().as_matrix(); /// assert_eq!( /// format!("{}", out), /// "┌ ┐\n\ @@ -109,7 +109,7 @@ impl LinElasticity { /// use russell_tensor::LinElasticity; /// let mut ela = LinElasticity::new(3000.0, 0.2, false, true); /// ela.set_young_poisson(6000.0, 0.2); - /// let out = ela.get_modulus().to_matrix(); + /// let out = ela.get_modulus().as_matrix(); /// assert_eq!( /// format!("{}", out), /// "┌ ┐\n\ @@ -168,7 +168,7 @@ impl LinElasticity { /// ``` /// use russell_tensor::LinElasticity; /// let ela = LinElasticity::new(3000.0, 0.2, false, true); - /// let out = ela.get_modulus().to_matrix(); + /// let out = ela.get_modulus().as_matrix(); /// assert_eq!( /// format!("{}", out), /// "┌ ┐\n\ @@ -228,7 +228,7 @@ impl LinElasticity { /// // sum of first 3 rows = 1800 /// // sum of other rows = 720 /// let ela = LinElasticity::new(900.0, 0.25, false, false); - /// let out = ela.get_modulus().to_matrix(); + /// let out = ela.get_modulus().as_matrix(); /// assert_eq!( /// format!("{}", out), /// "┌ ┐\n\ @@ -245,8 +245,8 @@ impl LinElasticity { /// ); /// let strain = Tensor2::from_matrix(strain_matrix_3d, Mandel::Symmetric)?; /// let mut stress = Tensor2::new(Mandel::Symmetric); - /// ela.calc_stress(&mut stress, &strain)?; - /// let out = stress.to_matrix(); + /// ela.calc_stress(&mut stress, &strain); + /// let out = stress.as_matrix(); /// assert_eq!( /// format!("{:.0}", out), /// "┌ ┐\n\ @@ -260,7 +260,7 @@ impl LinElasticity { /// // sum of first 3 rows = 1800 /// // sum of other rows = 720 /// let ela = LinElasticity::new(900.0, 0.25, true, false); - /// let out = ela.get_modulus().to_matrix(); + /// let out = ela.get_modulus().as_matrix(); /// println!("{}", out); /// assert_eq!( /// format!("{}", out), @@ -278,8 +278,8 @@ impl LinElasticity { /// ); /// let strain = Tensor2::from_matrix(strain_matrix_2d, Mandel::Symmetric2D)?; /// let mut stress = Tensor2::new(Mandel::Symmetric2D); - /// ela.calc_stress(&mut stress, &strain)?; - /// let out = stress.to_matrix(); + /// ela.calc_stress(&mut stress, &strain); + /// let out = stress.as_matrix(); /// assert_eq!( /// format!("{:.0}", out), /// "┌ ┐\n\ diff --git a/russell_tensor/src/operations.rs b/russell_tensor/src/operations.rs index eb55f667..aa797995 100644 --- a/russell_tensor/src/operations.rs +++ b/russell_tensor/src/operations.rs @@ -57,7 +57,7 @@ use russell_lab::{StrError, Vector}; /// [0.0, 4.0, 1.0], /// ], Mandel::General)?; /// -/// let res = t2_ddot_t2(&a.to_general(), &b); +/// let res = t2_ddot_t2(&a.as_general(), &b); /// /// approx_eq(res, 8.0, 1e-15); /// Ok(()) @@ -121,7 +121,7 @@ pub fn t2_ddot_t2(a: &Tensor2, b: &Tensor2) -> f64 { /// let mut c = Tensor2::new(Mandel::General); /// t2_dot_t2(&mut c, &a, &b); /// assert_eq!( -/// format!("{:.1}", c.to_matrix()), +/// format!("{:.1}", c.as_matrix()), /// "┌ ┐\n\ /// │ 4.0 1.0 5.0 │\n\ /// │ -2.0 3.0 -5.0 │\n\ @@ -364,7 +364,7 @@ pub fn vec_dot_t2(v: &mut Vector, alpha: f64, u: &Vector, a: &Tensor2) { /// vec_dyad_vec(&mut tt, 1.0, &u, &v)?; /// /// assert_eq!( -/// format!("{:.1}", tt.to_matrix()), +/// format!("{:.1}", tt.as_matrix()), /// "┌ ┐\n\ /// │ 2.0 2.0 2.0 │\n\ /// │ 2.0 2.0 2.0 │\n\ @@ -461,7 +461,7 @@ pub fn vec_dyad_vec(a: &mut Tensor2, alpha: f64, u: &Vector, v: &Vector) -> Resu /// t2_dyad_t2(&mut dd, 1.0, &a, &b); /// /// assert_eq!( -/// format!("{:.1}", dd.to_matrix()), +/// format!("{:.1}", dd.as_matrix()), /// "┌ ┐\n\ /// │ 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 │\n\ /// │ -1.0 -2.0 -3.0 -4.0 -5.0 -6.0 -7.0 -8.0 -9.0 │\n\ @@ -539,11 +539,10 @@ pub fn t2_dyad_t2(dd: &mut Tensor4, alpha: f64, a: &Tensor2, b: &Tensor2) { /// /// let mat = Matrix::filled(9, 9, 0.5); /// let mut dd = Tensor4::from_matrix(&mat, Mandel::General)?; -/// t2_dyad_t2_update(&mut dd, 1.0, &a, &b)?; +/// t2_dyad_t2_update(&mut dd, 1.0, &a, &b); /// -/// println!("{:.1}", dd.to_matrix()); /// assert_eq!( -/// format!("{:.1}", dd.to_matrix()), +/// format!("{:.1}", dd.as_matrix()), /// "┌ ┐\n\ /// │ 1.5 2.5 3.5 4.5 5.5 6.5 7.5 8.5 9.5 │\n\ /// │ 1.5 2.5 3.5 4.5 5.5 6.5 7.5 8.5 9.5 │\n\ @@ -1579,7 +1578,7 @@ pub fn t2_qsd_t2(dd: &mut Tensor4, s: f64, aa: &Tensor2, bb: &Tensor2) { /// t4_ddot_t2(&mut b, 1.0, &dd, &a); /// /// assert_eq!( -/// format!("{:.1}", b.to_matrix()), +/// format!("{:.1}", b.as_matrix()), /// "┌ ┐\n\ /// │ 285.0 2850.0 0.0 │\n\ /// │ -570.0 -285.0 0.0 │\n\ @@ -1661,10 +1660,10 @@ pub fn t4_ddot_t2(b: &mut Tensor2, alpha: f64, dd: &Tensor4, a: &Tensor2) { /// [0.0, 1.0, 0.0], /// [0.0, 0.0, 1.0], /// ], Mandel::General)?; -/// t4_ddot_t2_update(&mut b, 1.0, &dd, &a, 1000.0)?; +/// t4_ddot_t2_update(&mut b, 1.0, &dd, &a, 1000.0); /// /// assert_eq!( -/// format!("{:.1}", b.to_matrix()), +/// format!("{:.1}", b.as_matrix()), /// "┌ ┐\n\ /// │ 1285.0 2850.0 0.0 │\n\ /// │ -570.0 715.0 0.0 │\n\ @@ -1734,10 +1733,10 @@ pub fn t4_ddot_t2_update(b: &mut Tensor2, alpha: f64, dd: &Tensor4, a: &Tensor2, /// ], Mandel::General)?; /// /// let mut b = Tensor2::new(Mandel::General); -/// t2_ddot_t4(&mut b, 1.0, &a, &dd)?; +/// t2_ddot_t4(&mut b, 1.0, &a, &dd); /// /// assert_eq!( -/// format!("{:.1}", b.to_matrix()), +/// format!("{:.1}", b.as_matrix()), /// "┌ ┐\n\ /// │ 31.0 124.0 186.0 │\n\ /// │ 217.0 62.0 155.0 │\n\ @@ -1827,9 +1826,9 @@ pub fn t2_ddot_t4(b: &mut Tensor2, alpha: f64, a: &Tensor2, dd: &Tensor4) { /// )?; /// /// let mut ee = Tensor4::new(Mandel::General); -/// t4_ddot_t4(&mut ee, 1.0, &cc, &dd)?; +/// t4_ddot_t4(&mut ee, 1.0, &cc, &dd); /// -/// let out = ee.to_matrix(); +/// let out = ee.as_matrix(); /// for i in 0..9 { /// for j in 0..9 { /// if i == j { diff --git a/russell_tensor/src/tensor2.rs b/russell_tensor/src/tensor2.rs index 6cc63b0e..fbbf61b5 100644 --- a/russell_tensor/src/tensor2.rs +++ b/russell_tensor/src/tensor2.rs @@ -518,7 +518,7 @@ impl Tensor2 { /// [0.0, 0.0, 1.0], /// ], Mandel::Symmetric2D)?; /// let mut mat = Matrix::new(3, 3); - /// a.to_matrix&mut mat); + /// a.to_matrix(&mut mat); /// assert_eq!( /// format!("{:.1}", mat), /// "┌ ┐\n\ @@ -748,7 +748,7 @@ impl Tensor2 { /// a.sym_set(2, 2, 3.0); /// a.sym_set(0, 1, 4.0); /// assert_eq!( - /// format!("{:.1}", a.to_matrix()), + /// format!("{:.1}", a.as_matrix()), /// "┌ ┐\n\ /// │ 1.0 4.0 0.0 │\n\ /// │ 4.0 2.0 0.0 │\n\ @@ -764,7 +764,7 @@ impl Tensor2 { /// b.sym_set(1, 0, 4.0); /// b.sym_set(2, 0, 5.0); /// assert_eq!( - /// format!("{:.1}", b.to_matrix()), + /// format!("{:.1}", b.as_matrix()), /// "┌ ┐\n\ /// │ 1.0 4.0 5.0 │\n\ /// │ 4.0 2.0 0.0 │\n\ @@ -816,7 +816,7 @@ impl Tensor2 { /// a.sym_add(0, 1, 2.0, 10.0); /// /// assert_eq!( - /// format!("{:.1}", a.to_matrix()), + /// format!("{:.1}", a.as_matrix()), /// "┌ ┐\n\ /// │ 1.0 22.0 3.0 │\n\ /// │ 22.0 5.0 6.0 │\n\ @@ -867,7 +867,7 @@ impl Tensor2 { /// a.mirror(&b); /// /// assert_eq!( - /// format!("{:.1}", a.to_matrix()), + /// format!("{:.1}", a.as_matrix()), /// "┌ ┐\n\ /// │ 10.0 20.0 30.0 │\n\ /// │ 40.0 50.0 60.0 │\n\ @@ -922,10 +922,10 @@ impl Tensor2 { /// [70.0, 80.0, 90.0], /// ], Mandel::General)?; /// - /// a.add(2.0, &b); + /// a.update(2.0, &b); /// /// assert_eq!( - /// format!("{:.1}", a.to_matrix()), + /// format!("{:.1}", a.as_matrix()), /// "┌ ┐\n\ /// │ 21.0 42.0 63.0 │\n\ /// │ 84.0 105.0 126.0 │\n\ @@ -1025,14 +1025,14 @@ impl Tensor2 { /// ], Mandel::General)?; /// /// let mut at = Tensor2::new(Mandel::General); - /// a.transpose(&mut at)?; + /// a.transpose(&mut at); /// /// let at_correct = Tensor2::from_matrix(&[ /// [1.1, 2.1, 3.1], /// [1.2, 2.2, 3.2], /// [1.3, 2.3, 3.3], /// ], Mandel::General)?; - /// vec_approx_eq(&at.vector(), &at_correct.vector(), 1e-15); + /// vec_approx_eq(at.vector(), at_correct.vector(), 1e-15); /// Ok(()) /// } /// ``` @@ -1091,14 +1091,14 @@ impl Tensor2 { /// /// let mut ai = Tensor2::new(Mandel::General); /// - /// if let Some(det) = a.inverse(&mut ai, 1e-10)? { + /// if let Some(det) = a.inverse(&mut ai, 1e-10) { /// assert_eq!(det, 827.0); /// } else { /// panic!("determinant is zero"); /// } /// - /// let a_mat = a.to_matrix(); - /// let ai_mat = ai.to_matrix(); + /// let a_mat = a.as_matrix(); + /// let ai_mat = ai.as_matrix(); /// let mut a_times_ai = Matrix::new(3, 3); /// mat_mat_mul(&mut a_times_ai, 1.0, &a_mat, &ai_mat, 0.0)?; /// @@ -1191,14 +1191,14 @@ impl Tensor2 { /// ], Mandel::General)?; /// /// let mut a2 = Tensor2::new(Mandel::General); - /// a.squared(&mut a2)?; + /// a.squared(&mut a2); /// /// let a2_correct = Tensor2::from_matrix(&[ /// [200.0, 330.0, 270.0], /// [ 72.0, 123.0, 100.0], /// [ 42.0, 70.0, 63.0], /// ], Mandel::General)?; - /// vec_approx_eq(&a2.vector(), &a2_correct.vec, 1e-13); + /// vec_approx_eq(a2.vector(), a2_correct.vector(), 1e-13); /// /// Ok(()) /// } @@ -1362,11 +1362,11 @@ impl Tensor2 { /// ], Mandel::General)?; /// /// let mut dev = Tensor2::new(Mandel::General); - /// a.deviator(&mut dev).unwrap(); + /// a.deviator(&mut dev); /// approx_eq(dev.trace(), 0.0, 1e-15); /// /// assert_eq!( - /// format!("{:.1}", dev.to_matrix()), + /// format!("{:.1}", dev.as_matrix()), /// "┌ ┐\n\ /// │ -4.0 2.0 3.0 │\n\ /// │ 4.0 0.0 6.0 │\n\ @@ -1424,11 +1424,11 @@ impl Tensor2 { /// ], Mandel::General)?; /// /// let mut dev = Tensor2::new(Mandel::General); - /// a.deviator(&mut dev).unwrap(); + /// a.deviator(&mut dev); /// approx_eq(dev.trace(), 0.0, 1e-15); /// /// assert_eq!( - /// format!("{:.1}", dev.to_matrix()), + /// format!("{:.1}", dev.as_matrix()), /// "┌ ┐\n\ /// │ -5.0 1.0 2.0 │\n\ /// │ 3.0 1.0 4.0 │\n\ @@ -1477,11 +1477,11 @@ impl Tensor2 { /// ], Mandel::General)?; /// /// let mut dev = Tensor2::new(Mandel::General); - /// a.deviator(&mut dev).unwrap(); + /// a.deviator(&mut dev); /// approx_eq(dev.trace(), 0.0, 1e-15); /// /// assert_eq!( - /// format!("{:.1}", dev.to_matrix()), + /// format!("{:.1}", dev.as_matrix()), /// "┌ ┐\n\ /// │ -5.0 1.0 2.0 │\n\ /// │ 3.0 1.0 4.0 │\n\ diff --git a/russell_tensor/src/tensor4.rs b/russell_tensor/src/tensor4.rs index fdf9cafa..d3a3a0be 100644 --- a/russell_tensor/src/tensor4.rs +++ b/russell_tensor/src/tensor4.rs @@ -127,13 +127,13 @@ impl Tensor4 { /// /// fn main() { /// let cc = Tensor4::new(Mandel::General); - /// assert_eq!(cc.mat.dims(), (9,9)); + /// assert_eq!(cc.matrix().dims(), (9,9)); /// /// let dd = Tensor4::new(Mandel::Symmetric); - /// assert_eq!(dd.mat.dims(), (6,6)); + /// assert_eq!(dd.matrix().dims(), (6,6)); /// /// let ee = Tensor4::new(Mandel::Symmetric2D); - /// assert_eq!(ee.mat.dims(), (4,4)); + /// assert_eq!(ee.matrix().dims(), (4,4)); /// } /// ``` pub fn new(mandel: Mandel) -> Self { @@ -200,7 +200,7 @@ impl Tensor4 { /// } /// let dd = Tensor4::from_array(&inp, Mandel::General)?; /// assert_eq!( - /// format!("{:.0}", dd.to_matrix()), + /// format!("{:.0}", dd.as_matrix()), /// "┌ ┐\n\ /// │ 1111 1122 1133 1112 1123 1113 1121 1132 1131 │\n\ /// │ 2211 2222 2233 2212 2223 2213 2221 2232 2231 │\n\ @@ -341,7 +341,7 @@ impl Tensor4 { /// } /// let dd = Tensor4::from_matrix(&inp, Mandel::General)?; /// assert_eq!( - /// format!("{:.0}", dd.to_matrix()), + /// format!("{:.0}", dd.as_matrix()), /// "┌ ┐\n\ /// │ 1111 1122 1133 1112 1123 1113 1121 1132 1131 │\n\ /// │ 2211 2222 2233 2212 2223 2213 2221 2232 2231 │\n\ @@ -610,7 +610,7 @@ impl Tensor4 { /// dd.update(2.0, &ee); /// /// assert_eq!( - /// format!("{:.0}", dd.to_matrix()), + /// format!("{:.0}", dd.as_matrix()), /// "┌ ┐\n\ /// │ 2 2 2 2 0 0 0 0 0 │\n\ /// │ 2 2 2 2 0 0 0 0 0 │\n\ @@ -851,7 +851,7 @@ impl Tensor4 { /// } /// } /// assert_eq!( - /// format!("{:.0}", dd.to_matrix()), + /// format!("{:.0}", dd.as_matrix()), /// "┌ ┐\n\ /// │ 1111 1122 1133 1112 0 0 1112 0 0 │\n\ /// │ 2211 2222 2233 2212 0 0 2212 0 0 │\n\ @@ -908,9 +908,9 @@ impl Tensor4 { /// ], Mandel::General)?; /// /// let mut ee = Tensor4::new(Mandel::General); - /// ee.mirror(&dd)?; + /// ee.mirror(&dd); /// - /// mat_approx_eq(&dd.mat, &ee.mat, 1e-15); + /// mat_approx_eq(dd.matrix(), ee.matrix(), 1e-15); /// Ok(()) /// } /// ``` From a30e3db97372c1c8a64b4dcf00fd8c22d3a34aa1 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Sun, 12 May 2024 23:03:59 +1000 Subject: [PATCH 32/44] [tensor2] Improve tests --- russell_tensor/src/tensor2.rs | 200 ++++++++++++++-------------------- 1 file changed, 81 insertions(+), 119 deletions(-) diff --git a/russell_tensor/src/tensor2.rs b/russell_tensor/src/tensor2.rs index fbbf61b5..dc42761f 100644 --- a/russell_tensor/src/tensor2.rs +++ b/russell_tensor/src/tensor2.rs @@ -2835,11 +2835,8 @@ mod tests { let s = &SamplesTensor2::TENSOR_T; let tt = Tensor2::from_matrix(&s.matrix, Mandel::General).unwrap(); let mut tti = Tensor2::new(Mandel::General); - if let Some(det) = tt.inverse(&mut tti, 1e-10) { - assert_eq!(det, s.determinant); - } else { - panic!("zero determinant found"); - } + let det = tt.inverse(&mut tti, 1e-10).unwrap(); + assert_eq!(det, s.determinant); check_inverse(&tt, &tti, 1e-15); // symmetric 3D with zero determinant @@ -2853,11 +2850,8 @@ mod tests { let s = &SamplesTensor2::TENSOR_U; let tt = Tensor2::from_matrix(&s.matrix, Mandel::Symmetric).unwrap(); let mut tti = Tensor2::new(Mandel::Symmetric); - if let Some(det) = tt.inverse(&mut tti, 1e-10) { - approx_eq(det, s.determinant, 1e-14); - } else { - panic!("zero determinant found"); - } + let det = tt.inverse(&mut tti, 1e-10).unwrap(); + approx_eq(det, s.determinant, 1e-14); check_inverse(&tt, &tti, 1e-13); // symmetric 2D with zero determinant @@ -2871,11 +2865,8 @@ mod tests { let s = &SamplesTensor2::TENSOR_Y; let tt = Tensor2::from_matrix(&s.matrix, Mandel::Symmetric2D).unwrap(); let mut tti = Tensor2::new(Mandel::Symmetric2D); - if let Some(det) = tt.inverse(&mut tti, 1e-10) { - assert_eq!(det, s.determinant); - } else { - panic!("zero determinant found"); - } + let det = tt.inverse(&mut tti, 1e-10).unwrap(); + assert_eq!(det, s.determinant); check_inverse(&tt, &tti, 1e-15); } @@ -3049,23 +3040,20 @@ mod tests { tol_det: f64, tol_dev_norm: f64, tol_dev_det: f64, - verbose: bool, ) { let tt = Tensor2::from_matrix(&sample.matrix, mandel).unwrap(); - if verbose { - println!("{}", sample.desc); - println!(" err(norm) = {:?}", tt.norm() - sample.norm); - println!(" err(trace) = {:?}", tt.trace() - sample.trace); - println!(" err(determinant) = {:?}", tt.determinant() - sample.determinant); - println!( - " err(deviator_norm) = {:?}", - tt.deviator_norm() - sample.deviator_norm - ); - println!( - " err(deviator_determinant) = {:?}", - tt.deviator_determinant() - sample.deviator_determinant - ); - } + // println!("{}", sample.desc); + // println!(" err(norm) = {:?}", tt.norm() - sample.norm); + // println!(" err(trace) = {:?}", tt.trace() - sample.trace); + // println!(" err(determinant) = {:?}", tt.determinant() - sample.determinant); + // println!( + // " err(deviator_norm) = {:?}", + // tt.deviator_norm() - sample.deviator_norm + // ); + // println!( + // " err(deviator_determinant) = {:?}", + // tt.deviator_determinant() - sample.deviator_determinant + // ); approx_eq(tt.norm(), sample.norm, tol_norm); approx_eq(tt.trace(), sample.trace, tol_trace); approx_eq(tt.determinant(), sample.determinant, tol_det); @@ -3076,66 +3064,51 @@ mod tests { #[test] #[rustfmt::skip] fn properties_are_correct() { - let verb = false; // norm trace det dev_norm dev_det - check_sample(&SamplesTensor2::TENSOR_O, Mandel::General, 1e-15, 1e-15, 1e-15, 1e-15, 1e-15, verb); - check_sample(&SamplesTensor2::TENSOR_I, Mandel::General, 1e-15, 1e-15, 1e-15, 1e-15, 1e-15, verb); - check_sample(&SamplesTensor2::TENSOR_X, Mandel::General, 1e-15, 1e-15, 1e-15, 1e-15, 1e-13, verb); - check_sample(&SamplesTensor2::TENSOR_Y, Mandel::General, 1e-13, 1e-15, 1e-15, 1e-15, 1e-15, verb); - check_sample(&SamplesTensor2::TENSOR_Z, Mandel::General, 1e-15, 1e-15, 1e-14, 1e-14, 1e-15, verb); - check_sample(&SamplesTensor2::TENSOR_U, Mandel::General, 1e-13, 1e-15, 1e-14, 1e-14, 1e-13, verb); - check_sample(&SamplesTensor2::TENSOR_S, Mandel::General, 1e-13, 1e-15, 1e-14, 1e-15, 1e-13, verb); - check_sample(&SamplesTensor2::TENSOR_R, Mandel::General, 1e-13, 1e-15, 1e-13, 1e-13, 1e-15, verb); - check_sample(&SamplesTensor2::TENSOR_T, Mandel::General, 1e-13, 1e-15, 1e-15, 1e-14, 1e-15, verb); - - let verb = false; + check_sample(&SamplesTensor2::TENSOR_O, Mandel::General, 1e-15, 1e-15, 1e-15, 1e-15, 1e-15); + check_sample(&SamplesTensor2::TENSOR_I, Mandel::General, 1e-15, 1e-15, 1e-15, 1e-15, 1e-15); + check_sample(&SamplesTensor2::TENSOR_X, Mandel::General, 1e-15, 1e-15, 1e-15, 1e-15, 1e-13); + check_sample(&SamplesTensor2::TENSOR_Y, Mandel::General, 1e-13, 1e-15, 1e-15, 1e-15, 1e-15); + check_sample(&SamplesTensor2::TENSOR_Z, Mandel::General, 1e-15, 1e-15, 1e-14, 1e-14, 1e-15); + check_sample(&SamplesTensor2::TENSOR_U, Mandel::General, 1e-13, 1e-15, 1e-14, 1e-14, 1e-13); + check_sample(&SamplesTensor2::TENSOR_S, Mandel::General, 1e-13, 1e-15, 1e-14, 1e-15, 1e-13); + check_sample(&SamplesTensor2::TENSOR_R, Mandel::General, 1e-13, 1e-15, 1e-13, 1e-13, 1e-15); + check_sample(&SamplesTensor2::TENSOR_T, Mandel::General, 1e-13, 1e-15, 1e-15, 1e-14, 1e-15); // norm trace det dev_norm dev_det - check_sample(&SamplesTensor2::TENSOR_O, Mandel::Symmetric, 1e-15, 1e-15, 1e-15, 1e-15, 1e-15, verb); - check_sample(&SamplesTensor2::TENSOR_I, Mandel::Symmetric, 1e-15, 1e-15, 1e-15, 1e-15, 1e-15, verb); - check_sample(&SamplesTensor2::TENSOR_X, Mandel::Symmetric, 1e-15, 1e-15, 1e-15, 1e-15, 1e-13, verb); - check_sample(&SamplesTensor2::TENSOR_Y, Mandel::Symmetric, 1e-13, 1e-15, 1e-15, 1e-15, 1e-15, verb); - check_sample(&SamplesTensor2::TENSOR_Z, Mandel::Symmetric, 1e-15, 1e-15, 1e-14, 1e-14, 1e-14, verb); - check_sample(&SamplesTensor2::TENSOR_U, Mandel::Symmetric, 1e-13, 1e-15, 1e-14, 1e-14, 1e-13, verb); - check_sample(&SamplesTensor2::TENSOR_S, Mandel::Symmetric, 1e-13, 1e-15, 1e-14, 1e-15, 1e-13, verb); - - let verb = false; + check_sample(&SamplesTensor2::TENSOR_O, Mandel::Symmetric, 1e-15, 1e-15, 1e-15, 1e-15, 1e-15); + check_sample(&SamplesTensor2::TENSOR_I, Mandel::Symmetric, 1e-15, 1e-15, 1e-15, 1e-15, 1e-15); + check_sample(&SamplesTensor2::TENSOR_X, Mandel::Symmetric, 1e-15, 1e-15, 1e-15, 1e-15, 1e-13); + check_sample(&SamplesTensor2::TENSOR_Y, Mandel::Symmetric, 1e-13, 1e-15, 1e-15, 1e-15, 1e-15); + check_sample(&SamplesTensor2::TENSOR_Z, Mandel::Symmetric, 1e-15, 1e-15, 1e-14, 1e-14, 1e-14); + check_sample(&SamplesTensor2::TENSOR_U, Mandel::Symmetric, 1e-13, 1e-15, 1e-14, 1e-14, 1e-13); + check_sample(&SamplesTensor2::TENSOR_S, Mandel::Symmetric, 1e-13, 1e-15, 1e-14, 1e-15, 1e-13); // norm trace det dev_norm dev_det - check_sample(&SamplesTensor2::TENSOR_O, Mandel::Symmetric2D, 1e-15, 1e-15, 1e-15, 1e-15, 1e-15, verb); - check_sample(&SamplesTensor2::TENSOR_I, Mandel::Symmetric2D, 1e-15, 1e-15, 1e-15, 1e-15, 1e-15, verb); - check_sample(&SamplesTensor2::TENSOR_X, Mandel::Symmetric2D, 1e-15, 1e-15, 1e-15, 1e-15, 1e-13, verb); - check_sample(&SamplesTensor2::TENSOR_Y, Mandel::Symmetric2D, 1e-13, 1e-15, 1e-15, 1e-15, 1e-15, verb); - check_sample(&SamplesTensor2::TENSOR_Z, Mandel::Symmetric2D, 1e-15, 1e-15, 1e-14, 1e-14, 1e-14, verb); + check_sample(&SamplesTensor2::TENSOR_O, Mandel::Symmetric2D, 1e-15, 1e-15, 1e-15, 1e-15, 1e-15); + check_sample(&SamplesTensor2::TENSOR_I, Mandel::Symmetric2D, 1e-15, 1e-15, 1e-15, 1e-15, 1e-15); + check_sample(&SamplesTensor2::TENSOR_X, Mandel::Symmetric2D, 1e-15, 1e-15, 1e-15, 1e-15, 1e-13); + check_sample(&SamplesTensor2::TENSOR_Y, Mandel::Symmetric2D, 1e-13, 1e-15, 1e-15, 1e-15, 1e-15); + check_sample(&SamplesTensor2::TENSOR_Z, Mandel::Symmetric2D, 1e-15, 1e-15, 1e-14, 1e-14, 1e-14); } /// --- PRINCIPAL INVARIANTS ------------------------------------------------------------------------------------------- - fn check_iis( - sample: &SampleTensor2, - mandel: Mandel, - tol_a: f64, - tol_b: f64, - tol_c: f64, - tol_d: f64, - verbose: bool, - ) { + fn check_iis(sample: &SampleTensor2, mandel: Mandel, tol_a: f64, tol_b: f64, tol_c: f64, tol_d: f64) { let tt = Tensor2::from_matrix(&sample.matrix, mandel).unwrap(); let jj2 = -sample.deviator_second_invariant; let jj3 = sample.deviator_determinant; - if verbose { - println!("{}", sample.desc); - println!(" err(I1) = {:?}", f64::abs(tt.invariant_ii1() - sample.trace)); - println!( - " err(I2) = {:?}", - f64::abs(tt.invariant_ii2() - sample.second_invariant) - ); - println!(" err(I3) = {:?}", f64::abs(tt.invariant_ii3() - sample.determinant)); - println!(" err(J2) = {:?}", f64::abs(tt.invariant_jj2() - jj2)); - println!(" err(J3) = {:?}", f64::abs(tt.invariant_jj3() - jj3)); - if mandel == Mandel::Symmetric || mandel == Mandel::Symmetric2D { - let norm_s = tt.deviator_norm(); - println!(" err(J2 - ½‖s‖²) = {:?}", f64::abs(jj2 - norm_s * norm_s / 2.0)); - } - } + // println!("{}", sample.desc); + // println!(" err(I1) = {:?}", f64::abs(tt.invariant_ii1() - sample.trace)); + // println!( + // " err(I2) = {:?}", + // f64::abs(tt.invariant_ii2() - sample.second_invariant) + // ); + // println!(" err(I3) = {:?}", f64::abs(tt.invariant_ii3() - sample.determinant)); + // println!(" err(J2) = {:?}", f64::abs(tt.invariant_jj2() - jj2)); + // println!(" err(J3) = {:?}", f64::abs(tt.invariant_jj3() - jj3)); + // if mandel == Mandel::Symmetric || mandel == Mandel::Symmetric2D { + // let norm_s = tt.deviator_norm(); + // println!(" err(J2 - ½‖s‖²) = {:?}", f64::abs(jj2 - norm_s * norm_s / 2.0)); + // } approx_eq(tt.invariant_ii1(), sample.trace, tol_a); approx_eq(tt.invariant_ii2(), sample.second_invariant, tol_b); approx_eq(tt.invariant_ii3(), sample.determinant, tol_b); @@ -3150,32 +3123,29 @@ mod tests { #[test] #[rustfmt::skip] fn principal_invariants_are_correct() { - let verb = false; - check_iis(&SamplesTensor2::TENSOR_O, Mandel::General, 1e-15, 1e-15, 1e-15, 1e-15, verb); - check_iis(&SamplesTensor2::TENSOR_I, Mandel::General, 1e-15, 1e-15, 1e-15, 1e-15, verb); - check_iis(&SamplesTensor2::TENSOR_X, Mandel::General, 1e-15, 1e-15, 1e-13, 1e-13, verb); - check_iis(&SamplesTensor2::TENSOR_Y, Mandel::General, 1e-15, 1e-15, 1e-15, 1e-15, verb); - check_iis(&SamplesTensor2::TENSOR_Z, Mandel::General, 1e-15, 1e-14, 1e-15, 1e-15, verb); - check_iis(&SamplesTensor2::TENSOR_U, Mandel::General, 1e-15, 1e-14, 1e-13, 1e-13, verb); - check_iis(&SamplesTensor2::TENSOR_S, Mandel::General, 1e-15, 1e-14, 1e-13, 1e-13, verb); - check_iis(&SamplesTensor2::TENSOR_R, Mandel::General, 1e-15, 1e-13, 1e-15, 1e-15, verb); - check_iis(&SamplesTensor2::TENSOR_T, Mandel::General, 1e-15, 1e-15, 1e-15, 1e-15, verb); - - let verb = false; - check_iis(&SamplesTensor2::TENSOR_O, Mandel::Symmetric, 1e-15, 1e-15, 1e-15, 1e-15, verb); - check_iis(&SamplesTensor2::TENSOR_I, Mandel::Symmetric, 1e-15, 1e-15, 1e-15, 1e-15, verb); - check_iis(&SamplesTensor2::TENSOR_X, Mandel::Symmetric, 1e-15, 1e-15, 1e-13, 1e-15, verb); - check_iis(&SamplesTensor2::TENSOR_Y, Mandel::Symmetric, 1e-13, 1e-15, 1e-15, 1e-15, verb); - check_iis(&SamplesTensor2::TENSOR_Z, Mandel::Symmetric, 1e-15, 1e-14, 1e-14, 1e-15, verb); - check_iis(&SamplesTensor2::TENSOR_U, Mandel::Symmetric, 1e-15, 1e-14, 1e-13, 1e-13, verb); - check_iis(&SamplesTensor2::TENSOR_S, Mandel::Symmetric, 1e-15, 1e-14, 1e-13, 1e-14, verb); - - let verb = false; - check_iis(&SamplesTensor2::TENSOR_O, Mandel::Symmetric2D, 1e-15, 1e-15, 1e-15, 1e-15, verb); - check_iis(&SamplesTensor2::TENSOR_I, Mandel::Symmetric2D, 1e-15, 1e-15, 1e-15, 1e-15, verb); - check_iis(&SamplesTensor2::TENSOR_X, Mandel::Symmetric2D, 1e-15, 1e-15, 1e-13, 1e-15, verb); - check_iis(&SamplesTensor2::TENSOR_Y, Mandel::Symmetric2D, 1e-15, 1e-15, 1e-15, 1e-15, verb); - check_iis(&SamplesTensor2::TENSOR_Z, Mandel::Symmetric2D, 1e-15, 1e-14, 1e-15, 1e-15, verb); + check_iis(&SamplesTensor2::TENSOR_O, Mandel::General, 1e-15, 1e-15, 1e-15, 1e-15); + check_iis(&SamplesTensor2::TENSOR_I, Mandel::General, 1e-15, 1e-15, 1e-15, 1e-15); + check_iis(&SamplesTensor2::TENSOR_X, Mandel::General, 1e-15, 1e-15, 1e-13, 1e-13); + check_iis(&SamplesTensor2::TENSOR_Y, Mandel::General, 1e-15, 1e-15, 1e-15, 1e-15); + check_iis(&SamplesTensor2::TENSOR_Z, Mandel::General, 1e-15, 1e-14, 1e-15, 1e-15); + check_iis(&SamplesTensor2::TENSOR_U, Mandel::General, 1e-15, 1e-14, 1e-13, 1e-13); + check_iis(&SamplesTensor2::TENSOR_S, Mandel::General, 1e-15, 1e-14, 1e-13, 1e-13); + check_iis(&SamplesTensor2::TENSOR_R, Mandel::General, 1e-15, 1e-13, 1e-15, 1e-15); + check_iis(&SamplesTensor2::TENSOR_T, Mandel::General, 1e-15, 1e-15, 1e-15, 1e-15); + + check_iis(&SamplesTensor2::TENSOR_O, Mandel::Symmetric, 1e-15, 1e-15, 1e-15, 1e-15); + check_iis(&SamplesTensor2::TENSOR_I, Mandel::Symmetric, 1e-15, 1e-15, 1e-15, 1e-15); + check_iis(&SamplesTensor2::TENSOR_X, Mandel::Symmetric, 1e-15, 1e-15, 1e-13, 1e-15); + check_iis(&SamplesTensor2::TENSOR_Y, Mandel::Symmetric, 1e-13, 1e-15, 1e-15, 1e-15); + check_iis(&SamplesTensor2::TENSOR_Z, Mandel::Symmetric, 1e-15, 1e-14, 1e-14, 1e-15); + check_iis(&SamplesTensor2::TENSOR_U, Mandel::Symmetric, 1e-15, 1e-14, 1e-13, 1e-13); + check_iis(&SamplesTensor2::TENSOR_S, Mandel::Symmetric, 1e-15, 1e-14, 1e-13, 1e-14); + + check_iis(&SamplesTensor2::TENSOR_O, Mandel::Symmetric2D, 1e-15, 1e-15, 1e-15, 1e-15); + check_iis(&SamplesTensor2::TENSOR_I, Mandel::Symmetric2D, 1e-15, 1e-15, 1e-15, 1e-15); + check_iis(&SamplesTensor2::TENSOR_X, Mandel::Symmetric2D, 1e-15, 1e-15, 1e-13, 1e-15); + check_iis(&SamplesTensor2::TENSOR_Y, Mandel::Symmetric2D, 1e-15, 1e-15, 1e-15, 1e-15); + check_iis(&SamplesTensor2::TENSOR_Z, Mandel::Symmetric2D, 1e-15, 1e-14, 1e-15, 1e-15); } /// --- OCTAHEDRAL INVARIANTS ------------------------------------------------------------------------------------------ @@ -3185,19 +3155,11 @@ mod tests { } fn check_lode(l: Option, correct: f64, tol: f64, must_be_none: bool) { - match l { - Some(ll) => { - if must_be_none { - panic!("Lode invariant must be None"); - } else { - approx_eq(ll, correct, tol); - } - } - None => { - if !must_be_none { - panic!("Lode invariant must not be None"); - } - } + if must_be_none { + assert!(l.is_none()); + } else { + let lode = l.unwrap(); + approx_eq(lode, correct, tol); } } From fb2c6f101e7d2b6f0206a1a6c63a040ca1a9a72e Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Sun, 12 May 2024 23:09:01 +1000 Subject: [PATCH 33/44] [tensor] Simplify test --- russell_tensor/src/samples_tensor2.rs | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/russell_tensor/src/samples_tensor2.rs b/russell_tensor/src/samples_tensor2.rs index a9219e86..c5737cd7 100644 --- a/russell_tensor/src/samples_tensor2.rs +++ b/russell_tensor/src/samples_tensor2.rs @@ -272,21 +272,15 @@ mod tests { use russell_lab::{mat_approx_eq, Matrix}; fn check_spectral(sample: &SampleTensor2, tolerance: f64) { - match sample.eigenvalues { - Some(l) => match sample.eigenprojectors { - Some(pps) => { - let mut m = Matrix::new(3, 3); - for i in 0..3 { - for j in 0..3 { - m.set(i, j, l[0] * pps[0][i][j] + l[1] * pps[1][i][j] + l[2] * pps[2][i][j]); - } - } - mat_approx_eq(&m, &sample.matrix, tolerance); - } - None => panic!("eigenprojectors are not available for this tensor"), - }, - None => panic!("eigenvalues are not available for this tensor"), + let l = sample.eigenvalues.unwrap(); + let pps = sample.eigenprojectors.unwrap(); + let mut m = Matrix::new(3, 3); + for i in 0..3 { + for j in 0..3 { + m.set(i, j, l[0] * pps[0][i][j] + l[1] * pps[1][i][j] + l[2] * pps[2][i][j]); + } } + mat_approx_eq(&m, &sample.matrix, tolerance); } #[test] From 8a1e65f6f9a1f3dc78c2b9a98db331e5e2ae976c Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Sun, 12 May 2024 23:10:40 +1000 Subject: [PATCH 34/44] [tensor] Simplify test --- russell_tensor/src/spectral2.rs | 47 +++++++++++++-------------------- 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/russell_tensor/src/spectral2.rs b/russell_tensor/src/spectral2.rs index f8c2b018..1cf3d90b 100644 --- a/russell_tensor/src/spectral2.rs +++ b/russell_tensor/src/spectral2.rs @@ -36,8 +36,6 @@ impl Spectral2 { /// # Results /// /// The results are available in [Spectral2::lambda] and [Spectral2::projectors]. - /// - /// TODO: Rewrite this function to avoid temporary memory allocation pub fn decompose(&mut self, tt: &Tensor2) -> Result<(), StrError> { if tt.mandel != self.mandel { return Err("the mandel representation is incompatible"); @@ -144,14 +142,7 @@ mod tests { ); } - fn check( - spec: &mut Spectral2, - sample: &SampleTensor2, - tol_lambda: f64, - tol_proj: f64, - tol_spectral: f64, - verbose: bool, - ) { + fn check(spec: &mut Spectral2, sample: &SampleTensor2, tol_lambda: f64, tol_proj: f64, tol_spectral: f64) { if let Some(correct_lambda) = sample.eigenvalues { if let Some(correct_projectors) = sample.eigenprojectors { // perform spectral decomposition of symmetric matrix @@ -160,13 +151,11 @@ mod tests { spec.decompose(&tt).unwrap(); // print results - if verbose { - println!("a =\n{}", tt.as_matrix()); - println!("λ = {}, {}, {}", spec.lambda[0], spec.lambda[1], spec.lambda[2]); - println!("P0 =\n{}", spec.projectors[0].as_matrix()); - println!("P1 =\n{}", spec.projectors[1].as_matrix()); - println!("P2 =\n{}", spec.projectors[2].as_matrix()); - } + // println!("a =\n{}", tt.as_matrix()); + // println!("λ = {}, {}, {}", spec.lambda[0], spec.lambda[1], spec.lambda[2]); + // println!("P0 =\n{}", spec.projectors[0].as_matrix()); + // println!("P1 =\n{}", spec.projectors[1].as_matrix()); + // println!("P2 =\n{}", spec.projectors[2].as_matrix()); // check eigenvalues vec_approx_eq(&spec.lambda, &correct_lambda, tol_lambda); @@ -195,23 +184,23 @@ mod tests { #[test] fn decompose_and_compose_work_3d() { let mut spec = Spectral2::new(false); - check(&mut spec, &SamplesTensor2::TENSOR_O, 1e-15, 1e-15, 1e-15, false); - check(&mut spec, &SamplesTensor2::TENSOR_I, 1e-15, 1e-15, 1e-15, false); - check(&mut spec, &SamplesTensor2::TENSOR_X, 1e-15, 1e-15, 1e-15, false); - check(&mut spec, &SamplesTensor2::TENSOR_Y, 1e-13, 1e-15, 1e-15, false); - check(&mut spec, &SamplesTensor2::TENSOR_Z, 1e-14, 1e-15, 1e-15, false); - check(&mut spec, &SamplesTensor2::TENSOR_U, 1e-13, 1e-15, 1e-14, false); - check(&mut spec, &SamplesTensor2::TENSOR_S, 1e-13, 1e-14, 1e-14, false); + check(&mut spec, &SamplesTensor2::TENSOR_O, 1e-15, 1e-15, 1e-15); + check(&mut spec, &SamplesTensor2::TENSOR_I, 1e-15, 1e-15, 1e-15); + check(&mut spec, &SamplesTensor2::TENSOR_X, 1e-15, 1e-15, 1e-15); + check(&mut spec, &SamplesTensor2::TENSOR_Y, 1e-13, 1e-15, 1e-15); + check(&mut spec, &SamplesTensor2::TENSOR_Z, 1e-14, 1e-15, 1e-15); + check(&mut spec, &SamplesTensor2::TENSOR_U, 1e-13, 1e-15, 1e-14); + check(&mut spec, &SamplesTensor2::TENSOR_S, 1e-13, 1e-14, 1e-14); } #[test] fn decompose_and_compose_work_2d() { let mut spec = Spectral2::new(true); - check(&mut spec, &SamplesTensor2::TENSOR_O, 1e-15, 1e-15, 1e-15, false); - check(&mut spec, &SamplesTensor2::TENSOR_I, 1e-15, 1e-15, 1e-15, false); - check(&mut spec, &SamplesTensor2::TENSOR_X, 1e-15, 1e-15, 1e-15, false); - check(&mut spec, &SamplesTensor2::TENSOR_Y, 1e-13, 1e-15, 1e-15, false); - check(&mut spec, &SamplesTensor2::TENSOR_Z, 1e-14, 1e-15, 1e-15, false); + check(&mut spec, &SamplesTensor2::TENSOR_O, 1e-15, 1e-15, 1e-15); + check(&mut spec, &SamplesTensor2::TENSOR_I, 1e-15, 1e-15, 1e-15); + check(&mut spec, &SamplesTensor2::TENSOR_X, 1e-15, 1e-15, 1e-15); + check(&mut spec, &SamplesTensor2::TENSOR_Y, 1e-13, 1e-15, 1e-15); + check(&mut spec, &SamplesTensor2::TENSOR_Z, 1e-14, 1e-15, 1e-15); } #[test] From a44b1e0c148ca8b496e303c7524f54868600e62d Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Sun, 12 May 2024 23:12:53 +1000 Subject: [PATCH 35/44] [tensor] Simplify test --- russell_tensor/src/spectral2.rs | 71 ++++++++++++++++----------------- 1 file changed, 35 insertions(+), 36 deletions(-) diff --git a/russell_tensor/src/spectral2.rs b/russell_tensor/src/spectral2.rs index 1cf3d90b..2d6e1fc0 100644 --- a/russell_tensor/src/spectral2.rs +++ b/russell_tensor/src/spectral2.rs @@ -143,42 +143,41 @@ mod tests { } fn check(spec: &mut Spectral2, sample: &SampleTensor2, tol_lambda: f64, tol_proj: f64, tol_spectral: f64) { - if let Some(correct_lambda) = sample.eigenvalues { - if let Some(correct_projectors) = sample.eigenprojectors { - // perform spectral decomposition of symmetric matrix - let mandel = spec.projectors[0].mandel; - let tt = Tensor2::from_matrix(&sample.matrix, mandel).unwrap(); - spec.decompose(&tt).unwrap(); - - // print results - // println!("a =\n{}", tt.as_matrix()); - // println!("λ = {}, {}, {}", spec.lambda[0], spec.lambda[1], spec.lambda[2]); - // println!("P0 =\n{}", spec.projectors[0].as_matrix()); - // println!("P1 =\n{}", spec.projectors[1].as_matrix()); - // println!("P2 =\n{}", spec.projectors[2].as_matrix()); - - // check eigenvalues - vec_approx_eq(&spec.lambda, &correct_lambda, tol_lambda); - - // check eigenprojectors - let pp0 = spec.projectors[0].as_matrix(); - let pp1 = spec.projectors[1].as_matrix(); - let pp2 = spec.projectors[2].as_matrix(); - let correct0 = Matrix::from(&correct_projectors[0]); - let correct1 = Matrix::from(&correct_projectors[1]); - let correct2 = Matrix::from(&correct_projectors[2]); - mat_approx_eq(&correct0, &pp0, tol_proj); - mat_approx_eq(&correct1, &pp1, tol_proj); - mat_approx_eq(&correct2, &pp2, tol_proj); - - // compose - let mut tt_new = Tensor2::new(mandel); - spec.compose(&mut tt_new, &spec.lambda).unwrap(); - let a_new = tt_new.as_matrix(); - let a = Matrix::from(&sample.matrix); - mat_approx_eq(&a, &a_new, tol_spectral); - } - }; + let correct_lambda = sample.eigenvalues.unwrap(); + let correct_projectors = sample.eigenprojectors.unwrap(); + + // perform spectral decomposition of symmetric matrix + let mandel = spec.projectors[0].mandel; + let tt = Tensor2::from_matrix(&sample.matrix, mandel).unwrap(); + spec.decompose(&tt).unwrap(); + + // print results + // println!("a =\n{}", tt.as_matrix()); + // println!("λ = {}, {}, {}", spec.lambda[0], spec.lambda[1], spec.lambda[2]); + // println!("P0 =\n{}", spec.projectors[0].as_matrix()); + // println!("P1 =\n{}", spec.projectors[1].as_matrix()); + // println!("P2 =\n{}", spec.projectors[2].as_matrix()); + + // check eigenvalues + vec_approx_eq(&spec.lambda, &correct_lambda, tol_lambda); + + // check eigenprojectors + let pp0 = spec.projectors[0].as_matrix(); + let pp1 = spec.projectors[1].as_matrix(); + let pp2 = spec.projectors[2].as_matrix(); + let correct0 = Matrix::from(&correct_projectors[0]); + let correct1 = Matrix::from(&correct_projectors[1]); + let correct2 = Matrix::from(&correct_projectors[2]); + mat_approx_eq(&correct0, &pp0, tol_proj); + mat_approx_eq(&correct1, &pp1, tol_proj); + mat_approx_eq(&correct2, &pp2, tol_proj); + + // compose + let mut tt_new = Tensor2::new(mandel); + spec.compose(&mut tt_new, &spec.lambda).unwrap(); + let a_new = tt_new.as_matrix(); + let a = Matrix::from(&sample.matrix); + mat_approx_eq(&a, &a_new, tol_spectral); } #[test] From 6a7c23b085701a3ae271d6e81781e96b1c68cd14 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Sun, 12 May 2024 23:47:20 +1000 Subject: [PATCH 36/44] [Important] Split operations file --- russell_tensor/src/lib.rs | 10 +- russell_tensor/src/operations_mix1.rs | 1010 +++++++++++++++++ russell_tensor/src/operations_mix2.rs | 1455 +++++++++++++++++++++++++ russell_tensor/src/operations_t2.rs | 810 ++++++++++++++ russell_tensor/src/operations_t4.rs | 235 ++++ 5 files changed, 3518 insertions(+), 2 deletions(-) create mode 100644 russell_tensor/src/operations_mix1.rs create mode 100644 russell_tensor/src/operations_mix2.rs create mode 100644 russell_tensor/src/operations_t2.rs create mode 100644 russell_tensor/src/operations_t4.rs diff --git a/russell_tensor/src/lib.rs b/russell_tensor/src/lib.rs index e252da3f..aedc7fb7 100644 --- a/russell_tensor/src/lib.rs +++ b/russell_tensor/src/lib.rs @@ -18,7 +18,10 @@ mod derivatives_t2; mod derivatives_t4; mod enums; mod lin_elasticity; -mod operations; +mod operations_mix1; +mod operations_mix2; +mod operations_t2; +mod operations_t4; mod samples_tensor2; mod samples_tensor4; mod spectral2; @@ -31,7 +34,10 @@ pub use crate::derivatives_t2::*; pub use crate::derivatives_t4::*; pub use crate::enums::*; pub use crate::lin_elasticity::*; -pub use crate::operations::*; +pub use crate::operations_mix1::*; +pub use crate::operations_mix2::*; +pub use crate::operations_t2::*; +pub use crate::operations_t4::*; pub use crate::samples_tensor2::*; pub use crate::samples_tensor4::*; pub use crate::spectral2::*; diff --git a/russell_tensor/src/operations_mix1.rs b/russell_tensor/src/operations_mix1.rs new file mode 100644 index 00000000..dc088822 --- /dev/null +++ b/russell_tensor/src/operations_mix1.rs @@ -0,0 +1,1010 @@ +use super::{Tensor2, Tensor4}; +use russell_lab::{mat_vec_mul, mat_vec_mul_update, vec_mat_mul, vec_outer, vec_outer_update}; + +#[allow(unused)] +use crate::Mandel; // for documentation + +/// Performs the dyadic product between two Tensor2 resulting a Tensor4 +/// +/// ```text +/// D = α a ⊗ b +/// ``` +/// +/// With orthonormal Cartesian components: +/// +/// ```text +/// Dᵢⱼₖₗ = α aᵢⱼ bₖₗ +/// ``` +/// +/// Or, in Mandel basis: +/// +/// ```text +/// Dₘₙ = α aₘ bₙ +/// ``` +/// +/// # Output +/// +/// * `dd` -- the tensor `D`; with the same [Mandel] as `a` and `b` +/// +/// # Input +/// +/// * `a` -- first tensor; with the same [Mandel] as `b` and `dd` +/// * `b` -- second tensor; with the same [Mandel] as `a` and `dd` +/// +/// # Panics +/// +/// A panic will occur the tensors have different [Mandel] +/// +/// # Examples +/// +/// ``` +/// use russell_tensor::{t2_dyad_t2, Mandel, Tensor2, Tensor4, StrError}; +/// +/// fn main() -> Result<(), StrError> { +/// let a = Tensor2::from_matrix(&[ +/// [ 1.0, 10.0, 0.0], +/// [-2.0, -1.0, 0.0], +/// [ 0.0, 0.0, 2.0], +/// ], Mandel::General)?; +/// +/// let b = Tensor2::from_matrix(&[ +/// [1.0, 4.0, 6.0], +/// [7.0, 2.0, 5.0], +/// [9.0, 8.0, 3.0], +/// ], Mandel::General)?; +/// +/// let mut dd = Tensor4::new(Mandel::General); +/// t2_dyad_t2(&mut dd, 1.0, &a, &b); +/// +/// assert_eq!( +/// format!("{:.1}", dd.as_matrix()), +/// "┌ ┐\n\ +/// │ 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 │\n\ +/// │ -1.0 -2.0 -3.0 -4.0 -5.0 -6.0 -7.0 -8.0 -9.0 │\n\ +/// │ 2.0 4.0 6.0 8.0 10.0 12.0 14.0 16.0 18.0 │\n\ +/// │ 10.0 20.0 30.0 40.0 50.0 60.0 70.0 80.0 90.0 │\n\ +/// │ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 │\n\ +/// │ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 │\n\ +/// │ -2.0 -4.0 -6.0 -8.0 -10.0 -12.0 -14.0 -16.0 -18.0 │\n\ +/// │ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 │\n\ +/// │ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 │\n\ +/// └ ┘" +/// ); +/// Ok(()) +/// } +/// ``` +pub fn t2_dyad_t2(dd: &mut Tensor4, alpha: f64, a: &Tensor2, b: &Tensor2) { + assert_eq!(a.mandel, dd.mandel); + assert_eq!(b.mandel, dd.mandel); + vec_outer(&mut dd.mat, alpha, &a.vec, &b.vec).unwrap(); +} + +/// Performs the dyadic product between two Tensor2 resulting in a Tensor4 (with update) +/// +/// Computes: +/// +/// ```text +/// D += α a ⊗ b +/// ``` +/// +/// With orthonormal Cartesian components: +/// +/// ```text +/// Dᵢⱼₖₗ += α aᵢⱼ bₖₗ +/// ``` +/// +/// Or, in Mandel basis: +/// +/// ```text +/// Dₘₙ += α aₘ bₙ +/// ``` +/// +/// # Output +/// +/// * `dd` -- the tensor `D`; with the same [Mandel] as `a` and `b` +/// +/// # Input +/// +/// * `a` -- first tensor; with the same [Mandel] as `b` and `dd` +/// * `b` -- second tensor; with the same [Mandel] as `a` and `dd` +/// +/// # Panics +/// +/// A panic will occur the tensors have different [Mandel] +/// +/// # Examples +/// +/// ``` +/// use russell_lab::Matrix; +/// use russell_tensor::{t2_dyad_t2_update, Mandel, StrError, Tensor2, Tensor4}; +/// +/// fn main() -> Result<(), StrError> { +/// #[rustfmt::skip] +/// let a = Tensor2::from_matrix(&[ +/// [ 1.0, 10.0, 0.0], +/// [ 2.0, 1.0, 0.0], +/// [ 0.0, 0.0, 2.0], +/// ], Mandel::General)?; +/// +/// #[rustfmt::skip] +/// let b = Tensor2::from_matrix(&[ +/// [1.0, 4.0, 6.0], +/// [7.0, 2.0, 5.0], +/// [9.0, 8.0, 3.0], +/// ], Mandel::General)?; +/// +/// let mat = Matrix::filled(9, 9, 0.5); +/// let mut dd = Tensor4::from_matrix(&mat, Mandel::General)?; +/// t2_dyad_t2_update(&mut dd, 1.0, &a, &b); +/// +/// assert_eq!( +/// format!("{:.1}", dd.as_matrix()), +/// "┌ ┐\n\ +/// │ 1.5 2.5 3.5 4.5 5.5 6.5 7.5 8.5 9.5 │\n\ +/// │ 1.5 2.5 3.5 4.5 5.5 6.5 7.5 8.5 9.5 │\n\ +/// │ 2.5 4.5 6.5 8.5 10.5 12.5 14.5 16.5 18.5 │\n\ +/// │ 10.5 20.5 30.5 40.5 50.5 60.5 70.5 80.5 90.5 │\n\ +/// │ 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 │\n\ +/// │ 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 │\n\ +/// │ 2.5 4.5 6.5 8.5 10.5 12.5 14.5 16.5 18.5 │\n\ +/// │ 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 │\n\ +/// │ 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 │\n\ +/// └ ┘" +/// ); +/// Ok(()) +/// } +/// ``` +pub fn t2_dyad_t2_update(dd: &mut Tensor4, alpha: f64, a: &Tensor2, b: &Tensor2) { + assert_eq!(a.mandel, dd.mandel); + assert_eq!(b.mandel, dd.mandel); + vec_outer_update(&mut dd.mat, alpha, &a.vec, &b.vec).unwrap(); +} + +/// Performs the double-dot (ddot) operation between a Tensor4 and a Tensor2 +/// +/// ```text +/// b = α D : a +/// ``` +/// +/// With orthonormal Cartesian components: +/// +/// ```text +/// bᵢⱼ = α Σ Σ Dᵢⱼₖₗ aₖₗ +/// k l +/// ``` +/// +/// Or, in Mandel basis: +/// +/// ```text +/// bₘ = α Σ Dₘₙ aₙ +/// n +/// ``` +/// +/// # Output +/// +/// * `b` -- the resulting second-order tensor; with the same [Mandel] as `a` and `dd` +/// +/// # Input +/// +/// * `alpha` -- the scalar multiplier +/// * `dd` -- the fourth-order tensor; with the same [Mandel] as `a` and `b` +/// * `a` -- the input second-order tensor; with the same [Mandel] as `b` and `dd` +/// +/// # Panics +/// +/// A panic will occur the tensors have different [Mandel] +/// +/// # Examples +/// +/// ``` +/// use russell_tensor::{t4_ddot_t2, Mandel, Tensor2, Tensor4, StrError}; +/// +/// fn main() -> Result<(), StrError> { +/// let dd = Tensor4::from_matrix(&[ +/// [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0], +/// [ -1.0, -2.0, -3.0, -4.0, -5.0, -6.0, -7.0, -8.0, -9.0], +/// [ 2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0, 18.0], +/// [ 10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0], +/// [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], +/// [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], +/// [ -2.0, -4.0, -6.0, -8.0,-10.0,-12.0,-14.0,-16.0,-18.0], +/// [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], +/// [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], +/// ], Mandel::General)?; +/// +/// let a = Tensor2::from_matrix(&[ +/// [1.0, 4.0, 6.0], +/// [7.0, 2.0, 5.0], +/// [9.0, 8.0, 3.0], +/// ], Mandel::General)?; +/// +/// let mut b = Tensor2::new(Mandel::General); +/// t4_ddot_t2(&mut b, 1.0, &dd, &a); +/// +/// assert_eq!( +/// format!("{:.1}", b.as_matrix()), +/// "┌ ┐\n\ +/// │ 285.0 2850.0 0.0 │\n\ +/// │ -570.0 -285.0 0.0 │\n\ +/// │ 0.0 0.0 570.0 │\n\ +/// └ ┘" +/// ); +/// Ok(()) +/// } +/// ``` +pub fn t4_ddot_t2(b: &mut Tensor2, alpha: f64, dd: &Tensor4, a: &Tensor2) { + assert_eq!(a.mandel, dd.mandel); + assert_eq!(b.mandel, dd.mandel); + mat_vec_mul(&mut b.vec, alpha, &dd.mat, &a.vec).unwrap(); +} + +/// Performs the double-dot (ddot) operation between a Tensor4 and a Tensor2 with update +/// +/// Computes: +/// +/// ```text +/// b = α D : a + β b +/// ``` +/// +/// With orthonormal Cartesian components: +/// +/// ```text +/// bᵢⱼ = α Σ Σ Dᵢⱼₖₗ aₖₗ + β bᵢⱼ +/// k l +/// ``` +/// +/// Or, in Mandel basis: +/// +/// ```text +/// bₘ = α Σ Dₘₙ aₙ + β bₘ +/// n +/// ``` +/// +/// # Output +/// +/// * `b` -- the resulting second-order tensor; with the same [Mandel] as `a` and `dd` +/// +/// # Input +/// +/// * `alpha` -- the scalar multiplier +/// * `a` -- the input second-order tensor; with the same [Mandel] as `b` and `dd` +/// * `dd` -- the fourth-order tensor; with the same [Mandel] as `a` and `b` +/// * `beta` -- the other scalar multiplier +/// +/// # Panics +/// +/// A panic will occur the tensors have different [Mandel] +/// +/// # Examples +/// +/// ``` +/// use russell_tensor::{t4_ddot_t2_update, Mandel, Tensor2, Tensor4, StrError}; +/// +/// fn main() -> Result<(), StrError> { +/// let dd = Tensor4::from_matrix(&[ +/// [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0], +/// [ -1.0, -2.0, -3.0, -4.0, -5.0, -6.0, -7.0, -8.0, -9.0], +/// [ 2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0, 18.0], +/// [ 10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0], +/// [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], +/// [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], +/// [ -2.0, -4.0, -6.0, -8.0,-10.0,-12.0,-14.0,-16.0,-18.0], +/// [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], +/// [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], +/// ], Mandel::General)?; +/// +/// let a = Tensor2::from_matrix(&[ +/// [1.0, 4.0, 6.0], +/// [7.0, 2.0, 5.0], +/// [9.0, 8.0, 3.0], +/// ], Mandel::General)?; +/// +/// let mut b = Tensor2::from_matrix(&[ +/// [1.0, 0.0, 0.0], +/// [0.0, 1.0, 0.0], +/// [0.0, 0.0, 1.0], +/// ], Mandel::General)?; +/// t4_ddot_t2_update(&mut b, 1.0, &dd, &a, 1000.0); +/// +/// assert_eq!( +/// format!("{:.1}", b.as_matrix()), +/// "┌ ┐\n\ +/// │ 1285.0 2850.0 0.0 │\n\ +/// │ -570.0 715.0 0.0 │\n\ +/// │ 0.0 0.0 1570.0 │\n\ +/// └ ┘" +/// ); +/// Ok(()) +/// } +/// ``` +pub fn t4_ddot_t2_update(b: &mut Tensor2, alpha: f64, dd: &Tensor4, a: &Tensor2, beta: f64) { + assert_eq!(a.mandel, dd.mandel); + assert_eq!(b.mandel, dd.mandel); + mat_vec_mul_update(&mut b.vec, alpha, &dd.mat, &a.vec, beta).unwrap(); +} + +/// Performs the double-dot (ddot) operation between a Tensor2 and a Tensor4 +/// +/// Computes: +/// +/// ```text +/// b = α a : D +/// ``` +/// +/// With orthonormal Cartesian components: +/// +/// ```text +/// bₖₗ = α Σ Σ aᵢⱼ Dᵢⱼₖₗ +/// i j +/// ``` +/// +/// # Output +/// +/// * `b` -- the resulting second-order tensor; with the same [Mandel] as `a` and `dd` +/// +/// # Input +/// +/// * `alpha` -- the scalar multiplier +/// * `a` -- the input second-order tensor; with the same [Mandel] as `b` and `dd` +/// * `dd` -- the fourth-order tensor; with the same [Mandel] as `a` and `b` +/// +/// # Panics +/// +/// A panic will occur the tensors have different [Mandel] +/// +/// # Examples +/// +/// ``` +/// use russell_tensor::{t2_ddot_t4, Mandel, Tensor2, Tensor4, StrError}; +/// +/// fn main() -> Result<(), StrError> { +/// let a = Tensor2::from_matrix(&[ +/// [1.0, 4.0, 6.0], +/// [7.0, 2.0, 5.0], +/// [9.0, 8.0, 3.0], +/// ], Mandel::General)?; +/// +/// let dd = Tensor4::from_matrix(&[ +/// [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0], +/// [ -1.0, -2.0, -3.0, -4.0, -5.0, -6.0, -7.0, -8.0, -9.0], +/// [ 2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0, 18.0], +/// [ 10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0], +/// [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], +/// [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], +/// [ -2.0, -4.0, -6.0, -8.0,-10.0,-12.0,-14.0,-16.0,-18.0], +/// [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], +/// [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], +/// ], Mandel::General)?; +/// +/// let mut b = Tensor2::new(Mandel::General); +/// t2_ddot_t4(&mut b, 1.0, &a, &dd); +/// +/// assert_eq!( +/// format!("{:.1}", b.as_matrix()), +/// "┌ ┐\n\ +/// │ 31.0 124.0 186.0 │\n\ +/// │ 217.0 62.0 155.0 │\n\ +/// │ 279.0 248.0 93.0 │\n\ +/// └ ┘" +/// ); +/// Ok(()) +/// } +/// ``` +pub fn t2_ddot_t4(b: &mut Tensor2, alpha: f64, a: &Tensor2, dd: &Tensor4) { + assert_eq!(a.mandel, dd.mandel); + assert_eq!(b.mandel, dd.mandel); + vec_mat_mul(&mut b.vec, alpha, &a.vec, &dd.mat).unwrap(); +} + +/// Computes Tensor2 double-dot Tensor4 double-dot Tensor2 +/// +/// Computes: +/// +/// ```text +/// s = a : D : b +/// ``` +/// +/// With orthonormal Cartesian components: +/// +/// ```text +/// s = Σ Σ Σ Σ aᵢⱼ Dᵢⱼₖₗ bₖₗ +/// i j k l +/// ``` +/// +/// Or, in Mandel basis: +/// +/// ```text +/// s = Σ Σ aₘ Dₘₙ bₙ +/// m n +/// ``` +/// +/// Note: the Lagrange multiplier in Plasticity needs this operation. +/// +/// # Input +/// +/// * `a` -- the first second-order tensor; with the same [Mandel] as `b` and `dd` +/// * `dd` -- the fourth-order tensor; with the same [Mandel] as `a` and `b` +/// * `b` -- the second second-order tensor; with the same [Mandel] as `a` and `dd` +/// +/// # Output +/// +/// Returns the scalar results. +/// +/// # Panics +/// +/// A panic will occur the tensors have different [Mandel] +pub fn t2_ddot_t4_ddot_t2(a: &Tensor2, dd: &Tensor4, b: &Tensor2) -> f64 { + assert_eq!(a.mandel, dd.mandel); + assert_eq!(b.mandel, dd.mandel); + let dim = a.vec.dim(); + let mut s = 0.0; + for m in 0..dim { + for n in 0..dim { + s += a.vec[m] * dd.mat.get(m, n) * b.vec[n]; + } + } + s +} + +/// Computes Tensor4 double-dot Tensor2 dyadic Tensor2 double-dot Tensor4 +/// +/// Computes: +/// +/// ```text +/// E = α (D : a) ⊗ (b : D) +/// ``` +/// +/// With orthonormal Cartesian components: +/// +/// ```text +/// Eᵢⱼₖₗ = α Σ Σ Σ Σ (Dᵢⱼₛₜ aₛₜ) (bₒₚ Dₒₚₖₗ) +/// s t o p +/// ``` +/// +/// Or, in Mandel basis: +/// +/// ```text +/// Eₘₙ = α Σ Σ (Dₘₐ aₐ) (bₑ Dₑₙ) +/// a e +/// ``` +/// +/// Note: the elastoplastic modulus in Plasticity needs this operation. +/// +/// # Output +/// +/// * `ee` -- the resulting fourth-order tensor; with the same [Mandel] as the other tensors +/// +/// # Input +/// +/// * `alpha` -- the scalar multiplier +/// * `a` -- the first second-order tensor; with the same [Mandel] as the other tensors +/// * `b` -- the second second-order tensor; with the same [Mandel] as the other tensors +/// * `dd` -- the fourth-order tensor; with the same [Mandel] as the other tensors +/// +/// # Panics +/// +/// A panic will occur the tensors have different [Mandel] +pub fn t4_ddot_t2_dyad_t2_ddot_t4(ee: &mut Tensor4, alpha: f64, a: &Tensor2, b: &Tensor2, dd: &Tensor4) { + assert_eq!(a.mandel, dd.mandel); + assert_eq!(b.mandel, dd.mandel); + assert_eq!(ee.mandel, dd.mandel); + let dim = a.vec.dim(); + ee.mat.fill(0.0); + for m in 0..dim { + for n in 0..dim { + for p in 0..dim { + for q in 0..dim { + ee.mat + .add(m, n, alpha * dd.mat.get(m, p) * a.vec[p] * b.vec[q] * dd.mat.get(q, n)); + } + } + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#[cfg(test)] +mod tests { + use super::*; + use crate::{Mandel, SamplesTensor4, MN_TO_IJKL}; + use russell_lab::{approx_eq, mat_approx_eq, Matrix}; + + #[test] + #[should_panic] + fn t2_dyad_t2_panics_on_different_mandel1() { + let a = Tensor2::new(Mandel::Symmetric2D); + let b = Tensor2::new(Mandel::Symmetric); // wrong; it must be Symmetric2D + let mut dd = Tensor4::new(Mandel::Symmetric2D); + t2_dyad_t2(&mut dd, 1.0, &a, &b); + } + + #[test] + #[should_panic] + fn t2_dyad_t2_panics_on_different_mandel2() { + let a = Tensor2::new(Mandel::Symmetric2D); + let b = Tensor2::new(Mandel::Symmetric2D); + let mut dd = Tensor4::new(Mandel::Symmetric); // wrong; it must be Symmetric2D + t2_dyad_t2(&mut dd, 1.0, &a, &b); + } + + #[test] + fn t2_dyad_t2_works() { + // general dyad general + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 2.0, 3.0], + [4.0, 5.0, 6.0], + [7.0, 8.0, 9.0], + ], Mandel::General).unwrap(); + #[rustfmt::skip] + let b = Tensor2::from_matrix(&[ + [0.5, 0.5, 0.5], + [0.5, 0.5, 0.5], + [0.5, 0.5, 0.5], + ], Mandel::General).unwrap(); + let mut dd = Tensor4::new(Mandel::General); + t2_dyad_t2(&mut dd, 2.0, &a, &b); + let mat = dd.as_matrix(); + assert_eq!( + format!("{:.1}", mat), + "┌ ┐\n\ + │ 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 │\n\ + │ 5.0 5.0 5.0 5.0 5.0 5.0 5.0 5.0 5.0 │\n\ + │ 9.0 9.0 9.0 9.0 9.0 9.0 9.0 9.0 9.0 │\n\ + │ 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 │\n\ + │ 6.0 6.0 6.0 6.0 6.0 6.0 6.0 6.0 6.0 │\n\ + │ 3.0 3.0 3.0 3.0 3.0 3.0 3.0 3.0 3.0 │\n\ + │ 4.0 4.0 4.0 4.0 4.0 4.0 4.0 4.0 4.0 │\n\ + │ 8.0 8.0 8.0 8.0 8.0 8.0 8.0 8.0 8.0 │\n\ + │ 7.0 7.0 7.0 7.0 7.0 7.0 7.0 7.0 7.0 │\n\ + └ ┘" + ); + + // sym-3D dyad general + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 2.0, 3.0], + [2.0, 5.0, 6.0], + [3.0, 6.0, 9.0], + ], Mandel::Symmetric).unwrap(); + #[rustfmt::skip] + let b = Tensor2::from_matrix(&[ + [0.5, 0.5, 0.5], + [0.5, 0.5, 0.5], + [0.5, 0.5, 0.5], + ], Mandel::Symmetric).unwrap(); + let mut dd = Tensor4::new(Mandel::Symmetric); + t2_dyad_t2(&mut dd, 2.0, &a, &b); + let mat = dd.as_matrix(); + assert_eq!( + format!("{:.1}", mat), + "┌ ┐\n\ + │ 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 │\n\ + │ 5.0 5.0 5.0 5.0 5.0 5.0 5.0 5.0 5.0 │\n\ + │ 9.0 9.0 9.0 9.0 9.0 9.0 9.0 9.0 9.0 │\n\ + │ 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 │\n\ + │ 6.0 6.0 6.0 6.0 6.0 6.0 6.0 6.0 6.0 │\n\ + │ 3.0 3.0 3.0 3.0 3.0 3.0 3.0 3.0 3.0 │\n\ + │ 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 │\n\ + │ 6.0 6.0 6.0 6.0 6.0 6.0 6.0 6.0 6.0 │\n\ + │ 3.0 3.0 3.0 3.0 3.0 3.0 3.0 3.0 3.0 │\n\ + └ ┘" + ); + + // sym-2D dyad sym-2D + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 2.0, 0.0], + [2.0, 5.0, 0.0], + [0.0, 0.0, 9.0], + ], Mandel::Symmetric2D).unwrap(); + #[rustfmt::skip] + let b = Tensor2::from_matrix(&[ + [0.5, 0.5, 0.0], + [0.5, 0.5, 0.0], + [0.0, 0.0, 0.5], + ], Mandel::Symmetric2D).unwrap(); + let mut dd = Tensor4::new(Mandel::Symmetric2D); + t2_dyad_t2(&mut dd, 2.0, &a, &b); + let mat = dd.as_matrix(); + assert_eq!( + format!("{:.1}", mat), + "┌ ┐\n\ + │ 1.0 1.0 1.0 1.0 0.0 0.0 1.0 0.0 0.0 │\n\ + │ 5.0 5.0 5.0 5.0 0.0 0.0 5.0 0.0 0.0 │\n\ + │ 9.0 9.0 9.0 9.0 0.0 0.0 9.0 0.0 0.0 │\n\ + │ 2.0 2.0 2.0 2.0 0.0 0.0 2.0 0.0 0.0 │\n\ + │ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 │\n\ + │ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 │\n\ + │ 2.0 2.0 2.0 2.0 0.0 0.0 2.0 0.0 0.0 │\n\ + │ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 │\n\ + │ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 │\n\ + └ ┘" + ); + } + + #[test] + #[should_panic] + fn t2_dyad_t2_update_panics_on_different_mandel1() { + let a = Tensor2::new(Mandel::Symmetric2D); + let b = Tensor2::new(Mandel::Symmetric); // wrong; it must be Symmetric2D + let mut dd = Tensor4::new(Mandel::Symmetric2D); + t2_dyad_t2_update(&mut dd, 1.0, &a, &b); + } + + #[test] + #[should_panic] + fn t2_dyad_t2_update_panics_on_different_mandel2() { + let a = Tensor2::new(Mandel::Symmetric2D); + let b = Tensor2::new(Mandel::Symmetric2D); + let mut dd = Tensor4::new(Mandel::Symmetric); // wrong; it must be Symmetric2D + t2_dyad_t2_update(&mut dd, 1.0, &a, &b); + } + + #[test] + fn t2_dyad_t2_update_works() { + // general dyad general + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 2.0, 3.0], + [4.0, 5.0, 6.0], + [7.0, 8.0, 9.0], + ], Mandel::General).unwrap(); + #[rustfmt::skip] + let b = Tensor2::from_matrix(&[ + [0.5, 0.5, 0.5], + [0.5, 0.5, 0.5], + [0.5, 0.5, 0.5], + ], Mandel::General).unwrap(); + let mat = Matrix::filled(9, 9, 0.1); + let mut dd = Tensor4::from_matrix(&mat, Mandel::General).unwrap(); + t2_dyad_t2_update(&mut dd, 2.0, &a, &b); + let mat = dd.as_matrix(); + let correct = "┌ ┐\n\ + │ 1.1 1.1 1.1 1.1 1.1 1.1 1.1 1.1 1.1 │\n\ + │ 5.1 5.1 5.1 5.1 5.1 5.1 5.1 5.1 5.1 │\n\ + │ 9.1 9.1 9.1 9.1 9.1 9.1 9.1 9.1 9.1 │\n\ + │ 2.1 2.1 2.1 2.1 2.1 2.1 2.1 2.1 2.1 │\n\ + │ 6.1 6.1 6.1 6.1 6.1 6.1 6.1 6.1 6.1 │\n\ + │ 3.1 3.1 3.1 3.1 3.1 3.1 3.1 3.1 3.1 │\n\ + │ 4.1 4.1 4.1 4.1 4.1 4.1 4.1 4.1 4.1 │\n\ + │ 8.1 8.1 8.1 8.1 8.1 8.1 8.1 8.1 8.1 │\n\ + │ 7.1 7.1 7.1 7.1 7.1 7.1 7.1 7.1 7.1 │\n\ + └ ┘"; + assert_eq!(format!("{:.1}", mat), correct); + } + + fn check_dyad(s: f64, a_ten: &Tensor2, b_ten: &Tensor2, dd_ten: &Tensor4, tol: f64) { + let a = a_ten.as_matrix(); + let b = b_ten.as_matrix(); + let dd = dd_ten.as_matrix(); + let mut correct = Matrix::new(9, 9); + for m in 0..9 { + for n in 0..9 { + let (i, j, k, l) = MN_TO_IJKL[m][n]; + correct.set(m, n, s * a.get(i, j) * b.get(k, l)); + } + } + mat_approx_eq(&dd, &correct, tol); + } + + #[test] + fn t2_dyad_t2_works_extra() { + // general dyad general + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 2.0, 3.0], + [4.0, 5.0, 6.0], + [7.0, 8.0, 9.0], + ], Mandel::General).unwrap(); + #[rustfmt::skip] + let b = Tensor2::from_matrix(&[ + [9.0, 8.0, 7.0], + [6.0, 5.0, 4.0], + [3.0, 2.0, 1.0], + ], Mandel::General).unwrap(); + let mut dd = Tensor4::new(Mandel::General); + t2_dyad_t2(&mut dd, 2.0, &a, &b); + let mat = dd.as_matrix(); + // println!("{:.1}", mat); + let correct = Matrix::from(&[ + [18.0, 10.0, 2.0, 16.0, 8.0, 14.0, 12.0, 4.0, 6.0], + [90.0, 50.0, 10.0, 80.0, 40.0, 70.0, 60.0, 20.0, 30.0], + [162.0, 90.0, 18.0, 144.0, 72.0, 126.0, 108.0, 36.0, 54.0], + [36.0, 20.0, 4.0, 32.0, 16.0, 28.0, 24.0, 8.0, 12.0], + [108.0, 60.0, 12.0, 96.0, 48.0, 84.0, 72.0, 24.0, 36.0], + [54.0, 30.0, 6.0, 48.0, 24.0, 42.0, 36.0, 12.0, 18.0], + [72.0, 40.0, 8.0, 64.0, 32.0, 56.0, 48.0, 16.0, 24.0], + [144.0, 80.0, 16.0, 128.0, 64.0, 112.0, 96.0, 32.0, 48.0], + [126.0, 70.0, 14.0, 112.0, 56.0, 98.0, 84.0, 28.0, 42.0], + ]); + mat_approx_eq(&mat, &correct, 1e-13); + check_dyad(2.0, &a, &b, &dd, 1e-13); + + // symmetric dyad symmetric + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 4.0, 6.0], + [4.0, 2.0, 5.0], + [6.0, 5.0, 3.0], + ], Mandel::Symmetric).unwrap(); + #[rustfmt::skip] + let b = Tensor2::from_matrix(&[ + [3.0, 5.0, 6.0], + [5.0, 2.0, 4.0], + [6.0, 4.0, 1.0], + ], Mandel::Symmetric).unwrap(); + let mut dd = Tensor4::new(Mandel::Symmetric); + t2_dyad_t2(&mut dd, 2.0, &a, &b); + let mat = dd.as_matrix(); + // println!("{:.1}", mat); + let correct = Matrix::from(&[ + [6.0, 4.0, 2.0, 10.0, 8.0, 12.0, 10.0, 8.0, 12.0], + [12.0, 8.0, 4.0, 20.0, 16.0, 24.0, 20.0, 16.0, 24.0], + [18.0, 12.0, 6.0, 30.0, 24.0, 36.0, 30.0, 24.0, 36.0], + [24.0, 16.0, 8.0, 40.0, 32.0, 48.0, 40.0, 32.0, 48.0], + [30.0, 20.0, 10.0, 50.0, 40.0, 60.0, 50.0, 40.0, 60.0], + [36.0, 24.0, 12.0, 60.0, 48.0, 72.0, 60.0, 48.0, 72.0], + [24.0, 16.0, 8.0, 40.0, 32.0, 48.0, 40.0, 32.0, 48.0], + [30.0, 20.0, 10.0, 50.0, 40.0, 60.0, 50.0, 40.0, 60.0], + [36.0, 24.0, 12.0, 60.0, 48.0, 72.0, 60.0, 48.0, 72.0], + ]); + mat_approx_eq(&mat, &correct, 1e-13); + check_dyad(2.0, &a, &b, &dd, 1e-13); + + // symmetric 2D dyad symmetric 2D + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 4.0, 0.0], + [4.0, 2.0, 0.0], + [0.0, 0.0, 3.0], + ], Mandel::Symmetric2D).unwrap(); + #[rustfmt::skip] + let b = Tensor2::from_matrix(&[ + [3.0, 4.0, 0.0], + [4.0, 2.0, 0.0], + [0.0, 0.0, 1.0], + ], Mandel::Symmetric2D).unwrap(); + let mut dd = Tensor4::new(Mandel::Symmetric2D); + t2_dyad_t2(&mut dd, 2.0, &a, &b); + let mat = dd.as_matrix(); + // println!("{:.1}", mat); + let correct = Matrix::from(&[ + [6.0, 4.0, 2.0, 8.0, 0.0, 0.0, 8.0, 0.0, 0.0], + [12.0, 8.0, 4.0, 16.0, 0.0, 0.0, 16.0, 0.0, 0.0], + [18.0, 12.0, 6.0, 24.0, 0.0, 0.0, 24.0, 0.0, 0.0], + [24.0, 16.0, 8.0, 32.0, 0.0, 0.0, 32.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [24.0, 16.0, 8.0, 32.0, 0.0, 0.0, 32.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + ]); + mat_approx_eq(&mat, &correct, 1e-14); + check_dyad(2.0, &a, &b, &dd, 1e-15); + } + + #[test] + #[should_panic] + fn t4_ddot_t2_panics_on_different_mandel1() { + let a = Tensor2::new(Mandel::Symmetric); // wrong; it must be the same as `dd` + let mut b = Tensor2::new(Mandel::Symmetric2D); + let dd = Tensor4::new(Mandel::Symmetric2D); + t4_ddot_t2(&mut b, 1.0, &dd, &a); + } + + #[test] + #[should_panic] + fn t4_ddot_t2_panics_on_different_mandel2() { + let a = Tensor2::new(Mandel::Symmetric2D); + let mut b = Tensor2::new(Mandel::Symmetric); // wrong; it must be the same as `dd` + let dd = Tensor4::new(Mandel::Symmetric2D); + t4_ddot_t2(&mut b, 1.0, &dd, &a); + } + + #[test] + fn t4_ddot_t2_works() { + let dd = Tensor4::from_matrix(&SamplesTensor4::SYM_2D_SAMPLE1_STD_MATRIX, Mandel::Symmetric2D).unwrap(); + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [-1.0, -2.0, 0.0], + [-2.0, 2.0, 0.0], + [ 0.0, 0.0, -3.0]], Mandel::Symmetric2D).unwrap(); + let mut b = Tensor2::new(Mandel::Symmetric2D); + t4_ddot_t2(&mut b, 1.0, &dd, &a); + let out = b.as_matrix(); + assert_eq!( + format!("{:.1}", out), + "┌ ┐\n\ + │ -46.0 -154.0 0.0 │\n\ + │ -154.0 -64.0 0.0 │\n\ + │ 0.0 0.0 -82.0 │\n\ + └ ┘" + ); + } + + #[test] + #[should_panic] + fn t4_ddot_t2_update_panics_on_different_mandel1() { + let a = Tensor2::new(Mandel::Symmetric); // wrong; it must be the same as `dd` + let mut b = Tensor2::new(Mandel::Symmetric2D); + let dd = Tensor4::new(Mandel::Symmetric2D); + t4_ddot_t2_update(&mut b, 1.0, &dd, &a, 1.0); + } + + #[test] + #[should_panic] + fn t4_ddot_update_t2_panics_on_different_mandel2() { + let a = Tensor2::new(Mandel::Symmetric2D); + let mut b = Tensor2::new(Mandel::Symmetric); // wrong; it must be the same as `dd` + let dd = Tensor4::new(Mandel::Symmetric2D); + t4_ddot_t2_update(&mut b, 1.0, &dd, &a, 1.0); + } + + #[test] + fn t4_ddot_t2_update_works() { + let dd = Tensor4::from_matrix(&SamplesTensor4::SYM_2D_SAMPLE1_STD_MATRIX, Mandel::Symmetric2D).unwrap(); + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [-1.0, -2.0, 0.0], + [-2.0, 2.0, 0.0], + [ 0.0, 0.0, -3.0], + ], Mandel::Symmetric2D).unwrap(); + #[rustfmt::skip] + let mut b = Tensor2::from_matrix(&[ + [-1000.0, -1000.0, 0.0], + [-1000.0, -1000.0, 0.0], + [ 0.0, 0.0, -1000.0], + ], Mandel::Symmetric2D).unwrap(); + t4_ddot_t2_update(&mut b, 1.0, &dd, &a, 2.0); + let out = b.as_matrix(); + assert_eq!( + format!("{:.1}", out), + "┌ ┐\n\ + │ -2046.0 -2154.0 0.0 │\n\ + │ -2154.0 -2064.0 0.0 │\n\ + │ 0.0 0.0 -2082.0 │\n\ + └ ┘" + ); + } + + #[test] + #[should_panic] + fn t2_ddot_t4_panics_on_different_mandel1() { + let a = Tensor2::new(Mandel::Symmetric); // wrong; it must be the same as `dd` + let mut b = Tensor2::new(Mandel::Symmetric2D); + let dd = Tensor4::new(Mandel::Symmetric2D); + t2_ddot_t4(&mut b, 1.0, &a, &dd); + } + + #[test] + #[should_panic] + fn t2_ddot_t4_panics_on_different_mandel2() { + let a = Tensor2::new(Mandel::Symmetric2D); + let mut b = Tensor2::new(Mandel::Symmetric); // wrong; it must be the same as `dd` + let dd = Tensor4::new(Mandel::Symmetric2D); + t2_ddot_t4(&mut b, 1.0, &a, &dd); + } + + #[test] + fn t2_ddot_t4_works() { + let dd = Tensor4::from_matrix(&SamplesTensor4::SYM_2D_SAMPLE1_STD_MATRIX, Mandel::Symmetric2D).unwrap(); + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [-1.0, -2.0, 0.0], + [-2.0, 2.0, 0.0], + [ 0.0, 0.0, -3.0]], Mandel::Symmetric2D).unwrap(); + let mut b = Tensor2::new(Mandel::Symmetric2D); + t2_ddot_t4(&mut b, 1.0, &a, &dd); + let out = b.as_matrix(); + assert_eq!( + format!("{:.1}", out), + "┌ ┐\n\ + │ -90.0 -144.0 0.0 │\n\ + │ -144.0 -96.0 0.0 │\n\ + │ 0.0 0.0 -102.0 │\n\ + └ ┘" + ); + } + + #[test] + #[should_panic] + fn t2_ddot_t4_ddot_t2_panics_on_different_mandel1() { + let a = Tensor2::new(Mandel::Symmetric); // wrong; it must be the same as `dd` + let b = Tensor2::new(Mandel::Symmetric2D); + let dd = Tensor4::new(Mandel::Symmetric2D); + t2_ddot_t4_ddot_t2(&a, &dd, &b); + } + + #[test] + #[should_panic] + fn t2_ddot_t4_ddot_t2_panics_on_different_mandel2() { + let a = Tensor2::new(Mandel::Symmetric2D); + let b = Tensor2::new(Mandel::Symmetric); // wrong; it must be the same as `dd` + let dd = Tensor4::new(Mandel::Symmetric2D); + t2_ddot_t4_ddot_t2(&a, &dd, &b); + } + + #[test] + fn t2_ddot_t4_ddot_t2_works() { + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 2.0, 3.0], + [4.0, 5.0, 6.0], + [7.0, 8.0, 9.0], + ], Mandel::General).unwrap(); + #[rustfmt::skip] + let b = Tensor2::from_matrix(&[ + [9.0, 8.0, 7.0], + [6.0, 5.0, 4.0], + [3.0, 2.0, 1.0], + ], Mandel::General).unwrap(); + let mat = Matrix::filled(9, 9, -1.0); + let dd = Tensor4::from_matrix(&mat, Mandel::General).unwrap(); + let s = t2_ddot_t4_ddot_t2(&a, &dd, &b); + approx_eq(s, -2025.0, 1e-15); + } + + #[test] + #[should_panic] + fn t4_ddot_t2_dyad_t2_ddot_t4_panics_on_different_mandel1() { + let a = Tensor2::new(Mandel::Symmetric); // wrong; it must be the same as `ee` + let b = Tensor2::new(Mandel::Symmetric2D); + let dd = Tensor4::new(Mandel::Symmetric2D); + let mut ee = Tensor4::new(Mandel::Symmetric2D); + t4_ddot_t2_dyad_t2_ddot_t4(&mut ee, 2.0, &a, &b, &dd); + } + + #[test] + #[should_panic] + fn t4_ddot_t2_dyad_t2_ddot_t4_panics_on_different_mandel2() { + let a = Tensor2::new(Mandel::Symmetric2D); + let b = Tensor2::new(Mandel::Symmetric); // wrong; it must be the same as `ee` + let dd = Tensor4::new(Mandel::Symmetric2D); + let mut ee = Tensor4::new(Mandel::Symmetric2D); + t4_ddot_t2_dyad_t2_ddot_t4(&mut ee, 2.0, &a, &b, &dd); + } + + #[test] + #[should_panic] + fn t4_ddot_t2_dyad_t2_ddot_t4_panics_on_different_mandel3() { + let a = Tensor2::new(Mandel::Symmetric2D); + let b = Tensor2::new(Mandel::Symmetric2D); + let dd = Tensor4::new(Mandel::Symmetric); // wrong; it must be the same as `ee` + let mut ee = Tensor4::new(Mandel::Symmetric2D); + t4_ddot_t2_dyad_t2_ddot_t4(&mut ee, 2.0, &a, &b, &dd); + } + + #[test] + fn t4_ddot_t2_dyad_t2_ddot_t4_works() { + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 2.0, 3.0], + [4.0, 5.0, 6.0], + [7.0, 8.0, 9.0], + ], Mandel::General).unwrap(); + #[rustfmt::skip] + let b = Tensor2::from_matrix(&[ + [9.0, 8.0, 7.0], + [6.0, 5.0, 4.0], + [3.0, 2.0, 1.0], + ], Mandel::General).unwrap(); + let mat = Matrix::filled(9, 9, -1.0); + let dd = Tensor4::from_matrix(&mat, Mandel::General).unwrap(); + let mut ee = Tensor4::new(Mandel::General); + t4_ddot_t2_dyad_t2_ddot_t4(&mut ee, 2.0, &a, &b, &dd); + let correct = [ + [4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050.], + [4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050.], + [4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050.], + [4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050.], + [4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050.], + [4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050.], + [4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050.], + [4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050.], + [4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050.], + ]; + mat_approx_eq(&ee.as_matrix(), &correct, 1e-11); + } +} diff --git a/russell_tensor/src/operations_mix2.rs b/russell_tensor/src/operations_mix2.rs new file mode 100644 index 00000000..34dd08bf --- /dev/null +++ b/russell_tensor/src/operations_mix2.rs @@ -0,0 +1,1455 @@ +use super::{Tensor2, Tensor4}; +use crate::{Mandel, SQRT_2}; + +/// Performs the overbar dyadic product between two Tensor2 resulting in a (general) Tensor4 +/// +/// Computes: +/// +/// ```text +/// _ +/// D = s A ⊗ B +/// ``` +/// +/// With orthonormal Cartesian components: +/// +/// ```text +/// Dᵢⱼₖₗ = s Aᵢₖ Bⱼₗ +/// ``` +/// +/// **Important:** The result is **not** necessarily minor-symmetric; therefore `D` must be General. +/// +/// # Output +/// +/// * `dd` -- the tensor `D`; it must be [Mandel::General] +/// +/// # Input +/// +/// * `a` -- first tensor; with the same [Mandel] as `b` +/// * `b` -- second tensor; with the same [Mandel] as `a` +/// +/// # Panics +/// +/// 1. A panic will occur if `dd` is not [Mandel::General] +/// 2. A panic will occur the `a` and `b` have different [Mandel] +#[rustfmt::skip] +pub fn t2_odyad_t2(dd: &mut Tensor4, s: f64, aa: &Tensor2, bb: &Tensor2) { + assert_eq!(dd.mandel, Mandel::General); + assert_eq!(bb.mandel, aa.mandel); + let dim = aa.vec.dim(); + let a = &aa.vec; + let b = &bb.vec; + let tsq2 = 2.0 * SQRT_2; + if dim == 4 { + dd.mat.set(0,0, s*a[0]*b[0]); + dd.mat.set(0,1, s*(a[3]*b[3])/2.0); + dd.mat.set(0,2, 0.0); + dd.mat.set(0,3, s*(a[3]*b[0] + a[0]*b[3])/2.0); + dd.mat.set(0,4, 0.0); + dd.mat.set(0,5, 0.0); + dd.mat.set(0,6, s*(-(a[3]*b[0]) + a[0]*b[3])/2.0); + dd.mat.set(0,7, 0.0); + dd.mat.set(0,8, 0.0); + + dd.mat.set(1,0, s*(a[3]*b[3])/2.0); + dd.mat.set(1,1, s*a[1]*b[1]); + dd.mat.set(1,2, 0.0); + dd.mat.set(1,3, s*(a[3]*b[1] + a[1]*b[3])/2.0); + dd.mat.set(1,4, 0.0); + dd.mat.set(1,5, 0.0); + dd.mat.set(1,6, s*(a[3]*b[1] - a[1]*b[3])/2.0); + dd.mat.set(1,7, 0.0); + dd.mat.set(1,8, 0.0); + + dd.mat.set(2,0, 0.0); + dd.mat.set(2,1, 0.0); + dd.mat.set(2,2, s*a[2]*b[2]); + dd.mat.set(2,3, 0.0); + dd.mat.set(2,4, 0.0); + dd.mat.set(2,5, 0.0); + dd.mat.set(2,6, 0.0); + dd.mat.set(2,7, 0.0); + dd.mat.set(2,8, 0.0); + + dd.mat.set(3,0, s*(a[3]*b[0] + a[0]*b[3])/2.0); + dd.mat.set(3,1, s*(a[3]*b[1] + a[1]*b[3])/2.0); + dd.mat.set(3,2, 0.0); + dd.mat.set(3,3, s*(a[1]*b[0] + a[0]*b[1] + a[3]*b[3])/2.0); + dd.mat.set(3,4, 0.0); + dd.mat.set(3,5, 0.0); + dd.mat.set(3,6, s*(-(a[1]*b[0]) + a[0]*b[1])/2.0); + dd.mat.set(3,7, 0.0); + dd.mat.set(3,8, 0.0); + + dd.mat.set(4,0, 0.0); + dd.mat.set(4,1, 0.0); + dd.mat.set(4,2, 0.0); + dd.mat.set(4,3, 0.0); + dd.mat.set(4,4, s*(a[2]*b[1] + a[1]*b[2])/2.0); + dd.mat.set(4,5, s*(a[3]*b[2] + a[2]*b[3])/tsq2); + dd.mat.set(4,6, 0.0); + dd.mat.set(4,7, s*(-(a[2]*b[1]) + a[1]*b[2])/2.0); + dd.mat.set(4,8, s*(a[3]*b[2] - a[2]*b[3])/tsq2); + + dd.mat.set(5,0, 0.0); + dd.mat.set(5,1, 0.0); + dd.mat.set(5,2, 0.0); + dd.mat.set(5,3, 0.0); + dd.mat.set(5,4, s*(a[3]*b[2] + a[2]*b[3])/tsq2); + dd.mat.set(5,5, s*(a[2]*b[0] + a[0]*b[2])/2.0); + dd.mat.set(5,6, 0.0); + dd.mat.set(5,7, s*(a[3]*b[2] - a[2]*b[3])/tsq2); + dd.mat.set(5,8, s*(-(a[2]*b[0]) + a[0]*b[2])/2.0); + + dd.mat.set(6,0, s*(-(a[3]*b[0]) + a[0]*b[3])/2.0); + dd.mat.set(6,1, s*(a[3]*b[1] - a[1]*b[3])/2.0); + dd.mat.set(6,2, 0.0); + dd.mat.set(6,3, s*(-(a[1]*b[0]) + a[0]*b[1])/2.0); + dd.mat.set(6,4, 0.0); + dd.mat.set(6,5, 0.0); + dd.mat.set(6,6, s*(a[1]*b[0] + a[0]*b[1] - a[3]*b[3])/2.0); + dd.mat.set(6,7, 0.0); + dd.mat.set(6,8, 0.0); + + dd.mat.set(7,0, 0.0); + dd.mat.set(7,1, 0.0); + dd.mat.set(7,2, 0.0); + dd.mat.set(7,3, 0.0); + dd.mat.set(7,4, s*(-(a[2]*b[1]) + a[1]*b[2])/2.0); + dd.mat.set(7,5, s*(a[3]*b[2] - a[2]*b[3])/tsq2); + dd.mat.set(7,6, 0.0); + dd.mat.set(7,7, s*(a[2]*b[1] + a[1]*b[2])/2.0); + dd.mat.set(7,8, s*(a[3]*b[2] + a[2]*b[3])/tsq2); + + dd.mat.set(8,0, 0.0); + dd.mat.set(8,1, 0.0); + dd.mat.set(8,2, 0.0); + dd.mat.set(8,3, 0.0); + dd.mat.set(8,4, s*(a[3]*b[2] - a[2]*b[3])/tsq2); + dd.mat.set(8,5, s*(-(a[2]*b[0]) + a[0]*b[2])/2.0); + dd.mat.set(8,6, 0.0); + dd.mat.set(8,7, s*(a[3]*b[2] + a[2]*b[3])/tsq2); + dd.mat.set(8,8, s*(a[2]*b[0] + a[0]*b[2])/2.0); + } else if dim == 6 { + dd.mat.set(0,0, s*a[0]*b[0]); + dd.mat.set(0,1, s*(a[3]*b[3])/2.0); + dd.mat.set(0,2, s*(a[5]*b[5])/2.0); + dd.mat.set(0,3, s*(a[3]*b[0] + a[0]*b[3])/2.0); + dd.mat.set(0,4, s*(a[5]*b[3] + a[3]*b[5])/tsq2); + dd.mat.set(0,5, s*(a[5]*b[0] + a[0]*b[5])/2.0); + dd.mat.set(0,6, s*(-(a[3]*b[0]) + a[0]*b[3])/2.0); + dd.mat.set(0,7, s*(-(a[5]*b[3]) + a[3]*b[5])/tsq2); + dd.mat.set(0,8, s*(-(a[5]*b[0]) + a[0]*b[5])/2.0); + + dd.mat.set(1,0, s*(a[3]*b[3])/2.0); + dd.mat.set(1,1, s*a[1]*b[1]); + dd.mat.set(1,2, s*(a[4]*b[4])/2.0); + dd.mat.set(1,3, s*(a[3]*b[1] + a[1]*b[3])/2.0); + dd.mat.set(1,4, s*(a[4]*b[1] + a[1]*b[4])/2.0); + dd.mat.set(1,5, s*(a[4]*b[3] + a[3]*b[4])/tsq2); + dd.mat.set(1,6, s*(a[3]*b[1] - a[1]*b[3])/2.0); + dd.mat.set(1,7, s*(-(a[4]*b[1]) + a[1]*b[4])/2.0); + dd.mat.set(1,8, s*(-(a[4]*b[3]) + a[3]*b[4])/tsq2); + + dd.mat.set(2,0, s*(a[5]*b[5])/2.0); + dd.mat.set(2,1, s*(a[4]*b[4])/2.0); + dd.mat.set(2,2, s*a[2]*b[2]); + dd.mat.set(2,3, s*(a[5]*b[4] + a[4]*b[5])/tsq2); + dd.mat.set(2,4, s*(a[4]*b[2] + a[2]*b[4])/2.0); + dd.mat.set(2,5, s*(a[5]*b[2] + a[2]*b[5])/2.0); + dd.mat.set(2,6, s*(a[5]*b[4] - a[4]*b[5])/tsq2); + dd.mat.set(2,7, s*(a[4]*b[2] - a[2]*b[4])/2.0); + dd.mat.set(2,8, s*(a[5]*b[2] - a[2]*b[5])/2.0); + + dd.mat.set(3,0, s*(a[3]*b[0] + a[0]*b[3])/2.0); + dd.mat.set(3,1, s*(a[3]*b[1] + a[1]*b[3])/2.0); + dd.mat.set(3,2, s*(a[5]*b[4] + a[4]*b[5])/tsq2); + dd.mat.set(3,3, s*(a[1]*b[0] + a[0]*b[1] + a[3]*b[3])/2.0); + dd.mat.set(3,4, s*(SQRT_2*a[5]*b[1] + a[4]*b[3] + a[3]*b[4] + SQRT_2*a[1]*b[5])/4.0); + dd.mat.set(3,5, s*(SQRT_2*a[4]*b[0] + a[5]*b[3] + SQRT_2*a[0]*b[4] + a[3]*b[5])/4.0); + dd.mat.set(3,6, s*(-(a[1]*b[0]) + a[0]*b[1])/2.0); + dd.mat.set(3,7, s*(-(SQRT_2*a[5]*b[1]) - a[4]*b[3] + a[3]*b[4] + SQRT_2*a[1]*b[5])/4.0); + dd.mat.set(3,8, s*(-(SQRT_2*a[4]*b[0]) - a[5]*b[3] + SQRT_2*a[0]*b[4] + a[3]*b[5])/4.0); + + dd.mat.set(4,0, s*(a[5]*b[3] + a[3]*b[5])/tsq2); + dd.mat.set(4,1, s*(a[4]*b[1] + a[1]*b[4])/2.0); + dd.mat.set(4,2, s*(a[4]*b[2] + a[2]*b[4])/2.0); + dd.mat.set(4,3, s*(SQRT_2*a[5]*b[1] + a[4]*b[3] + a[3]*b[4] + SQRT_2*a[1]*b[5])/4.0); + dd.mat.set(4,4, s*(a[2]*b[1] + a[1]*b[2] + a[4]*b[4])/2.0); + dd.mat.set(4,5, s*(SQRT_2*a[3]*b[2] + SQRT_2*a[2]*b[3] + a[5]*b[4] + a[4]*b[5])/4.0); + dd.mat.set(4,6, s*(SQRT_2*a[5]*b[1] - a[4]*b[3] + a[3]*b[4] - SQRT_2*a[1]*b[5])/4.0); + dd.mat.set(4,7, s*(-(a[2]*b[1]) + a[1]*b[2])/2.0); + dd.mat.set(4,8, s*(SQRT_2*a[3]*b[2] - SQRT_2*a[2]*b[3] + a[5]*b[4] - a[4]*b[5])/4.0); + + dd.mat.set(5,0, s*(a[5]*b[0] + a[0]*b[5])/2.0); + dd.mat.set(5,1, s*(a[4]*b[3] + a[3]*b[4])/tsq2); + dd.mat.set(5,2, s*(a[5]*b[2] + a[2]*b[5])/2.0); + dd.mat.set(5,3, s*(SQRT_2*a[4]*b[0] + a[5]*b[3] + SQRT_2*a[0]*b[4] + a[3]*b[5])/4.0); + dd.mat.set(5,4, s*(SQRT_2*a[3]*b[2] + SQRT_2*a[2]*b[3] + a[5]*b[4] + a[4]*b[5])/4.0); + dd.mat.set(5,5, s*(a[2]*b[0] + a[0]*b[2] + a[5]*b[5])/2.0); + dd.mat.set(5,6, s*(-(SQRT_2*a[4]*b[0]) + a[5]*b[3] + SQRT_2*a[0]*b[4] - a[3]*b[5])/4.0); + dd.mat.set(5,7, s*(SQRT_2*a[3]*b[2] - SQRT_2*a[2]*b[3] - a[5]*b[4] + a[4]*b[5])/4.0); + dd.mat.set(5,8, s*(-(a[2]*b[0]) + a[0]*b[2])/2.0); + + dd.mat.set(6,0, s*(-(a[3]*b[0]) + a[0]*b[3])/2.0); + dd.mat.set(6,1, s*(a[3]*b[1] - a[1]*b[3])/2.0); + dd.mat.set(6,2, s*(a[5]*b[4] - a[4]*b[5])/tsq2); + dd.mat.set(6,3, s*(-(a[1]*b[0]) + a[0]*b[1])/2.0); + dd.mat.set(6,4, s*(SQRT_2*a[5]*b[1] - a[4]*b[3] + a[3]*b[4] - SQRT_2*a[1]*b[5])/4.0); + dd.mat.set(6,5, s*(-(SQRT_2*a[4]*b[0]) + a[5]*b[3] + SQRT_2*a[0]*b[4] - a[3]*b[5])/4.0); + dd.mat.set(6,6, s*(a[1]*b[0] + a[0]*b[1] - a[3]*b[3])/2.0); + dd.mat.set(6,7, s*(-(SQRT_2*a[5]*b[1]) + a[4]*b[3] + a[3]*b[4] - SQRT_2*a[1]*b[5])/4.0); + dd.mat.set(6,8, s*(SQRT_2*a[4]*b[0] - a[5]*b[3] + SQRT_2*a[0]*b[4] - a[3]*b[5])/4.0); + + dd.mat.set(7,0, s*(-(a[5]*b[3]) + a[3]*b[5])/tsq2); + dd.mat.set(7,1, s*(-(a[4]*b[1]) + a[1]*b[4])/2.0); + dd.mat.set(7,2, s*(a[4]*b[2] - a[2]*b[4])/2.0); + dd.mat.set(7,3, s*(-(SQRT_2*a[5]*b[1]) - a[4]*b[3] + a[3]*b[4] + SQRT_2*a[1]*b[5])/4.0); + dd.mat.set(7,4, s*(-(a[2]*b[1]) + a[1]*b[2])/2.0); + dd.mat.set(7,5, s*(SQRT_2*a[3]*b[2] - SQRT_2*a[2]*b[3] - a[5]*b[4] + a[4]*b[5])/4.0); + dd.mat.set(7,6, s*(-(SQRT_2*a[5]*b[1]) + a[4]*b[3] + a[3]*b[4] - SQRT_2*a[1]*b[5])/4.0); + dd.mat.set(7,7, s*(a[2]*b[1] + a[1]*b[2] - a[4]*b[4])/2.0); + dd.mat.set(7,8, s*(SQRT_2*a[3]*b[2] + SQRT_2*a[2]*b[3] - a[5]*b[4] - a[4]*b[5])/4.0); + + dd.mat.set(8,0, s*(-(a[5]*b[0]) + a[0]*b[5])/2.0); + dd.mat.set(8,1, s*(-(a[4]*b[3]) + a[3]*b[4])/tsq2); + dd.mat.set(8,2, s*(a[5]*b[2] - a[2]*b[5])/2.0); + dd.mat.set(8,3, s*(-(SQRT_2*a[4]*b[0]) - a[5]*b[3] + SQRT_2*a[0]*b[4] + a[3]*b[5])/4.0); + dd.mat.set(8,4, s*(SQRT_2*a[3]*b[2] - SQRT_2*a[2]*b[3] + a[5]*b[4] - a[4]*b[5])/4.0); + dd.mat.set(8,5, s*(-(a[2]*b[0]) + a[0]*b[2])/2.0); + dd.mat.set(8,6, s*(SQRT_2*a[4]*b[0] - a[5]*b[3] + SQRT_2*a[0]*b[4] - a[3]*b[5])/4.0); + dd.mat.set(8,7, s*(SQRT_2*a[3]*b[2] + SQRT_2*a[2]*b[3] - a[5]*b[4] - a[4]*b[5])/4.0); + dd.mat.set(8,8, s*(a[2]*b[0] + a[0]*b[2] - a[5]*b[5])/2.0); + } else { + dd.mat.set(0,0, s*a[0]*b[0]); + dd.mat.set(0,1, s*((a[3] + a[6])*(b[3] + b[6]))/2.0); + dd.mat.set(0,2, s*((a[5] + a[8])*(b[5] + b[8]))/2.0); + dd.mat.set(0,3, s*(a[3]*b[0] + a[6]*b[0] + a[0]*(b[3] + b[6]))/2.0); + dd.mat.set(0,4, s*((a[5] + a[8])*(b[3] + b[6]) + (a[3] + a[6])*(b[5] + b[8]))/tsq2); + dd.mat.set(0,5, s*(a[5]*b[0] + a[8]*b[0] + a[0]*(b[5] + b[8]))/2.0); + dd.mat.set(0,6, s*(-(a[3]*b[0]) - a[6]*b[0] + a[0]*(b[3] + b[6]))/2.0); + dd.mat.set(0,7, s*(-((a[5] + a[8])*(b[3] + b[6])) + (a[3] + a[6])*(b[5] + b[8]))/tsq2); + dd.mat.set(0,8, s*(-(a[5]*b[0]) - a[8]*b[0] + a[0]*(b[5] + b[8]))/2.0); + + dd.mat.set(1,0, s*((a[3] - a[6])*(b[3] - b[6]))/2.0); + dd.mat.set(1,1, s*a[1]*b[1]); + dd.mat.set(1,2, s*((a[4] + a[7])*(b[4] + b[7]))/2.0); + dd.mat.set(1,3, s*(a[3]*b[1] - a[6]*b[1] + a[1]*(b[3] - b[6]))/2.0); + dd.mat.set(1,4, s*(a[4]*b[1] + a[7]*b[1] + a[1]*(b[4] + b[7]))/2.0); + dd.mat.set(1,5, s*((a[4] + a[7])*(b[3] - b[6]) + (a[3] - a[6])*(b[4] + b[7]))/tsq2); + dd.mat.set(1,6, s*(a[3]*b[1] - a[6]*b[1] + a[1]*(-b[3] + b[6]))/2.0); + dd.mat.set(1,7, s*(-(a[4]*b[1]) - a[7]*b[1] + a[1]*(b[4] + b[7]))/2.0); + dd.mat.set(1,8, s*(-((a[4] + a[7])*(b[3] - b[6])) + (a[3] - a[6])*(b[4] + b[7]))/tsq2); + + dd.mat.set(2,0, s*((a[5] - a[8])*(b[5] - b[8]))/2.0); + dd.mat.set(2,1, s*((a[4] - a[7])*(b[4] - b[7]))/2.0); + dd.mat.set(2,2, s*a[2]*b[2]); + dd.mat.set(2,3, s*((a[5] - a[8])*(b[4] - b[7]) + (a[4] - a[7])*(b[5] - b[8]))/tsq2); + dd.mat.set(2,4, s*(a[4]*b[2] - a[7]*b[2] + a[2]*(b[4] - b[7]))/2.0); + dd.mat.set(2,5, s*(a[5]*b[2] - a[8]*b[2] + a[2]*(b[5] - b[8]))/2.0); + dd.mat.set(2,6, s*((a[5] - a[8])*(b[4] - b[7]) - (a[4] - a[7])*(b[5] - b[8]))/tsq2); + dd.mat.set(2,7, s*(a[4]*b[2] - a[7]*b[2] + a[2]*(-b[4] + b[7]))/2.0); + dd.mat.set(2,8, s*(a[5]*b[2] - a[8]*b[2] + a[2]*(-b[5] + b[8]))/2.0); + + dd.mat.set(3,0, s*(a[3]*b[0] - a[6]*b[0] + a[0]*(b[3] - b[6]))/2.0); + dd.mat.set(3,1, s*(a[3]*b[1] + a[6]*b[1] + a[1]*(b[3] + b[6]))/2.0); + dd.mat.set(3,2, s*((a[5] + a[8])*(b[4] + b[7]) + (a[4] + a[7])*(b[5] + b[8]))/tsq2); + dd.mat.set(3,3, s*(a[1]*b[0] + a[0]*b[1] + a[3]*b[3] - a[6]*b[6])/2.0); + dd.mat.set(3,4, s*(SQRT_2*(a[5] + a[8])*b[1] + (a[4] + a[7])*(b[3] + b[6]) + (a[3] + a[6])*(b[4] + b[7]) + SQRT_2*a[1]*(b[5] + b[8]))/4.0); + dd.mat.set(3,5, s*(SQRT_2*(a[4] + a[7])*b[0] + (a[5] + a[8])*(b[3] - b[6]) + SQRT_2*a[0]*(b[4] + b[7]) + (a[3] - a[6])*(b[5] + b[8]))/4.0); + dd.mat.set(3,6, s*(-(a[1]*b[0]) + a[0]*b[1] - a[6]*b[3] + a[3]*b[6])/2.0); + dd.mat.set(3,7, s*(-(SQRT_2*(a[5] + a[8])*b[1]) - (a[4] + a[7])*(b[3] + b[6]) + (a[3] + a[6])*(b[4] + b[7]) + SQRT_2*a[1]*(b[5] + b[8]))/4.0); + dd.mat.set(3,8, s*(-(SQRT_2*(a[4] + a[7])*b[0]) - (a[5] + a[8])*(b[3] - b[6]) + SQRT_2*a[0]*(b[4] + b[7]) + (a[3] - a[6])*(b[5] + b[8]))/4.0); + + dd.mat.set(4,0, s*((a[5] - a[8])*(b[3] - b[6]) + (a[3] - a[6])*(b[5] - b[8]))/tsq2); + dd.mat.set(4,1, s*(a[4]*b[1] - a[7]*b[1] + a[1]*(b[4] - b[7]))/2.0); + dd.mat.set(4,2, s*(a[4]*b[2] + a[7]*b[2] + a[2]*(b[4] + b[7]))/2.0); + dd.mat.set(4,3, s*(SQRT_2*(a[5] - a[8])*b[1] + (a[4] - a[7])*(b[3] - b[6]) + (a[3] - a[6])*(b[4] - b[7]) + SQRT_2*a[1]*(b[5] - b[8]))/4.0); + dd.mat.set(4,4, s*(a[2]*b[1] + a[1]*b[2] + a[4]*b[4] - a[7]*b[7])/2.0); + dd.mat.set(4,5, s*(SQRT_2*(a[3] - a[6])*b[2] + SQRT_2*a[2]*(b[3] - b[6]) + (a[5] - a[8])*(b[4] + b[7]) + (a[4] + a[7])*(b[5] - b[8]))/4.0); + dd.mat.set(4,6, s*(SQRT_2*(a[5] - a[8])*b[1] - (a[4] - a[7])*(b[3] - b[6]) + (a[3] - a[6])*(b[4] - b[7]) - SQRT_2*a[1]*(b[5] - b[8]))/4.0); + dd.mat.set(4,7, s*(-(a[2]*b[1]) + a[1]*b[2] - a[7]*b[4] + a[4]*b[7])/2.0); + dd.mat.set(4,8, s*(SQRT_2*(a[3] - a[6])*b[2] - SQRT_2*a[2]*(b[3] - b[6]) + (a[5] - a[8])*(b[4] + b[7]) - (a[4] + a[7])*(b[5] - b[8]))/4.0); + + dd.mat.set(5,0, s*(a[5]*b[0] - a[8]*b[0] + a[0]*(b[5] - b[8]))/2.0); + dd.mat.set(5,1, s*((a[4] - a[7])*(b[3] + b[6]) + (a[3] + a[6])*(b[4] - b[7]))/tsq2); + dd.mat.set(5,2, s*(a[5]*b[2] + a[8]*b[2] + a[2]*(b[5] + b[8]))/2.0); + dd.mat.set(5,3, s*(SQRT_2*(a[4] - a[7])*b[0] + (a[5] - a[8])*(b[3] + b[6]) + SQRT_2*a[0]*(b[4] - b[7]) + (a[3] + a[6])*(b[5] - b[8]))/4.0); + dd.mat.set(5,4, s*(SQRT_2*(a[3] + a[6])*b[2] + SQRT_2*a[2]*(b[3] + b[6]) + (a[5] + a[8])*(b[4] - b[7]) + (a[4] - a[7])*(b[5] + b[8]))/4.0); + dd.mat.set(5,5, s*(a[2]*b[0] + a[0]*b[2] + a[5]*b[5] - a[8]*b[8])/2.0); + dd.mat.set(5,6, s*(-(SQRT_2*(a[4] - a[7])*b[0]) + (a[5] - a[8])*(b[3] + b[6]) + SQRT_2*a[0]*(b[4] - b[7]) - (a[3] + a[6])*(b[5] - b[8]))/4.0); + dd.mat.set(5,7, s*(SQRT_2*(a[3] + a[6])*b[2] - SQRT_2*a[2]*(b[3] + b[6]) - (a[5] + a[8])*(b[4] - b[7]) + (a[4] - a[7])*(b[5] + b[8]))/4.0); + dd.mat.set(5,8, s*(-(a[2]*b[0]) + a[0]*b[2] - a[8]*b[5] + a[5]*b[8])/2.0); + + dd.mat.set(6,0, s*(-(a[3]*b[0]) + a[6]*b[0] + a[0]*(b[3] - b[6]))/2.0); + dd.mat.set(6,1, s*(a[3]*b[1] + a[6]*b[1] - a[1]*(b[3] + b[6]))/2.0); + dd.mat.set(6,2, s*((a[5] + a[8])*(b[4] + b[7]) - (a[4] + a[7])*(b[5] + b[8]))/tsq2); + dd.mat.set(6,3, s*(-(a[1]*b[0]) + a[0]*b[1] + a[6]*b[3] - a[3]*b[6])/2.0); + dd.mat.set(6,4, s*(SQRT_2*(a[5] + a[8])*b[1] - (a[4] + a[7])*(b[3] + b[6]) + (a[3] + a[6])*(b[4] + b[7]) - SQRT_2*a[1]*(b[5] + b[8]))/4.0); + dd.mat.set(6,5, s*(-(SQRT_2*(a[4] + a[7])*b[0]) + (a[5] + a[8])*(b[3] - b[6]) + SQRT_2*a[0]*(b[4] + b[7]) - (a[3] - a[6])*(b[5] + b[8]))/4.0); + dd.mat.set(6,6, s*(a[1]*b[0] + a[0]*b[1] - a[3]*b[3] + a[6]*b[6])/2.0); + dd.mat.set(6,7, s*(-(SQRT_2*(a[5] + a[8])*b[1]) + (a[4] + a[7])*(b[3] + b[6]) + (a[3] + a[6])*(b[4] + b[7]) - SQRT_2*a[1]*(b[5] + b[8]))/4.0); + dd.mat.set(6,8, s*(SQRT_2*(a[4] + a[7])*b[0] - (a[5] + a[8])*(b[3] - b[6]) + SQRT_2*a[0]*(b[4] + b[7]) - (a[3] - a[6])*(b[5] + b[8]))/4.0); + + dd.mat.set(7,0, s*(-((a[5] - a[8])*(b[3] - b[6])) + (a[3] - a[6])*(b[5] - b[8]))/tsq2); + dd.mat.set(7,1, s*(-(a[4]*b[1]) + a[7]*b[1] + a[1]*(b[4] - b[7]))/2.0); + dd.mat.set(7,2, s*(a[4]*b[2] + a[7]*b[2] - a[2]*(b[4] + b[7]))/2.0); + dd.mat.set(7,3, s*(-(SQRT_2*(a[5] - a[8])*b[1]) - (a[4] - a[7])*(b[3] - b[6]) + (a[3] - a[6])*(b[4] - b[7]) + SQRT_2*a[1]*(b[5] - b[8]))/4.0); + dd.mat.set(7,4, s*(-(a[2]*b[1]) + a[1]*b[2] + a[7]*b[4] - a[4]*b[7])/2.0); + dd.mat.set(7,5, s*(SQRT_2*(a[3] - a[6])*b[2] - SQRT_2*a[2]*(b[3] - b[6]) - (a[5] - a[8])*(b[4] + b[7]) + (a[4] + a[7])*(b[5] - b[8]))/4.0); + dd.mat.set(7,6, s*(-(SQRT_2*(a[5] - a[8])*b[1]) + (a[4] - a[7])*(b[3] - b[6]) + (a[3] - a[6])*(b[4] - b[7]) - SQRT_2*a[1]*(b[5] - b[8]))/4.0); + dd.mat.set(7,7, s*(a[2]*b[1] + a[1]*b[2] - a[4]*b[4] + a[7]*b[7])/2.0); + dd.mat.set(7,8, s*(SQRT_2*(a[3] - a[6])*b[2] + SQRT_2*a[2]*(b[3] - b[6]) - (a[5] - a[8])*(b[4] + b[7]) - (a[4] + a[7])*(b[5] - b[8]))/4.0); + + dd.mat.set(8,0, s*(-(a[5]*b[0]) + a[8]*b[0] + a[0]*(b[5] - b[8]))/2.0); + dd.mat.set(8,1, s*(-((a[4] - a[7])*(b[3] + b[6])) + (a[3] + a[6])*(b[4] - b[7]))/tsq2); + dd.mat.set(8,2, s*(a[5]*b[2] + a[8]*b[2] - a[2]*(b[5] + b[8]))/2.0); + dd.mat.set(8,3, s*(-(SQRT_2*(a[4] - a[7])*b[0]) - (a[5] - a[8])*(b[3] + b[6]) + SQRT_2*a[0]*(b[4] - b[7]) + (a[3] + a[6])*(b[5] - b[8]))/4.0); + dd.mat.set(8,4, s*(SQRT_2*(a[3] + a[6])*b[2] - SQRT_2*a[2]*(b[3] + b[6]) + (a[5] + a[8])*(b[4] - b[7]) - (a[4] - a[7])*(b[5] + b[8]))/4.0); + dd.mat.set(8,5, s*(-(a[2]*b[0]) + a[0]*b[2] + a[8]*b[5] - a[5]*b[8])/2.0); + dd.mat.set(8,6, s*(SQRT_2*(a[4] - a[7])*b[0] - (a[5] - a[8])*(b[3] + b[6]) + SQRT_2*a[0]*(b[4] - b[7]) - (a[3] + a[6])*(b[5] - b[8]))/4.0); + dd.mat.set(8,7, s*(SQRT_2*(a[3] + a[6])*b[2] + SQRT_2*a[2]*(b[3] + b[6]) - (a[5] + a[8])*(b[4] - b[7]) - (a[4] - a[7])*(b[5] + b[8]))/4.0); + dd.mat.set(8,8, s*(a[2]*b[0] + a[0]*b[2] - a[5]*b[5] + a[8]*b[8])/2.0); + } +} + +/// Performs the underbar dyadic product between two Tensor2 resulting in a (general) Tensor4 +/// +/// Computes: +/// +/// ```text +/// D = s A ⊗ B +/// ‾ +/// ``` +/// +/// With orthonormal Cartesian components: +/// +/// ```text +/// Dᵢⱼₖₗ = s Aᵢₗ Bⱼₖ +/// ``` +/// +/// **Important:** The result is **not** necessarily minor-symmetric; therefore `D` must be General. +/// +/// # Output +/// +/// * `dd` -- the tensor `D`; it must be [Mandel::General] +/// +/// # Input +/// +/// * `a` -- first tensor; with the same [Mandel] as `b` +/// * `b` -- second tensor; with the same [Mandel] as `a` +/// +/// # Panics +/// +/// 1. A panic will occur if `dd` is not [Mandel::General] +/// 2. A panic will occur the `a` and `b` have different [Mandel] +#[rustfmt::skip] +pub fn t2_udyad_t2(dd: &mut Tensor4, s: f64, aa: &Tensor2, bb: &Tensor2) { + assert_eq!(dd.mandel, Mandel::General); + assert_eq!(bb.mandel, aa.mandel); + let dim = aa.vec.dim(); + let a = &aa.vec; + let b = &bb.vec; + let tsq2 = 2.0 * SQRT_2; + if dim == 4 { + dd.mat.set(0,0, s*a[0]*b[0]); + dd.mat.set(0,1, s*(a[3]*b[3])/2.0); + dd.mat.set(0,2, 0.0); + dd.mat.set(0,3, s*(a[3]*b[0] + a[0]*b[3])/2.0); + dd.mat.set(0,4, 0.0); + dd.mat.set(0,5, 0.0); + dd.mat.set(0,6, s*(a[3]*b[0] - a[0]*b[3])/2.0); + dd.mat.set(0,7, 0.0); + dd.mat.set(0,8, 0.0); + + dd.mat.set(1,0, s*(a[3]*b[3])/2.0); + dd.mat.set(1,1, s*a[1]*b[1]); + dd.mat.set(1,2, 0.0); + dd.mat.set(1,3, s*(a[3]*b[1] + a[1]*b[3])/2.0); + dd.mat.set(1,4, 0.0); + dd.mat.set(1,5, 0.0); + dd.mat.set(1,6, s*(-(a[3]*b[1]) + a[1]*b[3])/2.0); + dd.mat.set(1,7, 0.0); + dd.mat.set(1,8, 0.0); + + dd.mat.set(2,0, 0.0); + dd.mat.set(2,1, 0.0); + dd.mat.set(2,2, s*a[2]*b[2]); + dd.mat.set(2,3, 0.0); + dd.mat.set(2,4, 0.0); + dd.mat.set(2,5, 0.0); + dd.mat.set(2,6, 0.0); + dd.mat.set(2,7, 0.0); + dd.mat.set(2,8, 0.0); + + dd.mat.set(3,0, s*(a[3]*b[0] + a[0]*b[3])/2.0); + dd.mat.set(3,1, s*(a[3]*b[1] + a[1]*b[3])/2.0); + dd.mat.set(3,2, 0.0); + dd.mat.set(3,3, s*(a[1]*b[0] + a[0]*b[1] + a[3]*b[3])/2.0); + dd.mat.set(3,4, 0.0); + dd.mat.set(3,5, 0.0); + dd.mat.set(3,6, s*(a[1]*b[0] - a[0]*b[1])/2.0); + dd.mat.set(3,7, 0.0); + dd.mat.set(3,8, 0.0); + + dd.mat.set(4,0, 0.0); + dd.mat.set(4,1, 0.0); + dd.mat.set(4,2, 0.0); + dd.mat.set(4,3, 0.0); + dd.mat.set(4,4, s*(a[2]*b[1] + a[1]*b[2])/2.0); + dd.mat.set(4,5, s*(a[3]*b[2] + a[2]*b[3])/tsq2); + dd.mat.set(4,6, 0.0); + dd.mat.set(4,7, s*(a[2]*b[1] - a[1]*b[2])/2.0); + dd.mat.set(4,8, s*(-(a[3]*b[2]) + a[2]*b[3])/tsq2); + + dd.mat.set(5,0, 0.0); + dd.mat.set(5,1, 0.0); + dd.mat.set(5,2, 0.0); + dd.mat.set(5,3, 0.0); + dd.mat.set(5,4, s*(a[3]*b[2] + a[2]*b[3])/tsq2); + dd.mat.set(5,5, s*(a[2]*b[0] + a[0]*b[2])/2.0); + dd.mat.set(5,6, 0.0); + dd.mat.set(5,7, s*(-(a[3]*b[2]) + a[2]*b[3])/tsq2); + dd.mat.set(5,8, s*(a[2]*b[0] - a[0]*b[2])/2.0); + + dd.mat.set(6,0, s*(-(a[3]*b[0]) + a[0]*b[3])/2.0); + dd.mat.set(6,1, s*(a[3]*b[1] - a[1]*b[3])/2.0); + dd.mat.set(6,2, 0.0); + dd.mat.set(6,3, s*(-(a[1]*b[0]) + a[0]*b[1])/2.0); + dd.mat.set(6,4, 0.0); + dd.mat.set(6,5, 0.0); + dd.mat.set(6,6, s*(-(a[1]*b[0]) - a[0]*b[1] + a[3]*b[3])/2.0); + dd.mat.set(6,7, 0.0); + dd.mat.set(6,8, 0.0); + + dd.mat.set(7,0, 0.0); + dd.mat.set(7,1, 0.0); + dd.mat.set(7,2, 0.0); + dd.mat.set(7,3, 0.0); + dd.mat.set(7,4, s*(-(a[2]*b[1]) + a[1]*b[2])/2.0); + dd.mat.set(7,5, s*(a[3]*b[2] - a[2]*b[3])/tsq2); + dd.mat.set(7,6, 0.0); + dd.mat.set(7,7, s*(-(a[2]*b[1]) - a[1]*b[2])/2.0); + dd.mat.set(7,8, s*(-(a[3]*b[2] + a[2]*b[3])/tsq2)); + + dd.mat.set(8,0, 0.0); + dd.mat.set(8,1, 0.0); + dd.mat.set(8,2, 0.0); + dd.mat.set(8,3, 0.0); + dd.mat.set(8,4, s*(a[3]*b[2] - a[2]*b[3])/tsq2); + dd.mat.set(8,5, s*(-(a[2]*b[0]) + a[0]*b[2])/2.0); + dd.mat.set(8,6, 0.0); + dd.mat.set(8,7, s*(-(a[3]*b[2] + a[2]*b[3])/tsq2)); + dd.mat.set(8,8, s*(-(a[2]*b[0]) - a[0]*b[2])/2.0); + } else if dim == 6 { + dd.mat.set(0,0, s*a[0]*b[0]); + dd.mat.set(0,1, s*(a[3]*b[3])/2.0); + dd.mat.set(0,2, s*(a[5]*b[5])/2.0); + dd.mat.set(0,3, s*(a[3]*b[0] + a[0]*b[3])/2.0); + dd.mat.set(0,4, s*(a[5]*b[3] + a[3]*b[5])/tsq2); + dd.mat.set(0,5, s*(a[5]*b[0] + a[0]*b[5])/2.0); + dd.mat.set(0,6, s*(a[3]*b[0] - a[0]*b[3])/2.0); + dd.mat.set(0,7, s*(a[5]*b[3] - a[3]*b[5])/tsq2); + dd.mat.set(0,8, s*(a[5]*b[0] - a[0]*b[5])/2.0); + + dd.mat.set(1,0, s*(a[3]*b[3])/2.0); + dd.mat.set(1,1, s*a[1]*b[1]); + dd.mat.set(1,2, s*(a[4]*b[4])/2.0); + dd.mat.set(1,3, s*(a[3]*b[1] + a[1]*b[3])/2.0); + dd.mat.set(1,4, s*(a[4]*b[1] + a[1]*b[4])/2.0); + dd.mat.set(1,5, s*(a[4]*b[3] + a[3]*b[4])/tsq2); + dd.mat.set(1,6, s*(-(a[3]*b[1]) + a[1]*b[3])/2.0); + dd.mat.set(1,7, s*(a[4]*b[1] - a[1]*b[4])/2.0); + dd.mat.set(1,8, s*(a[4]*b[3] - a[3]*b[4])/tsq2); + + dd.mat.set(2,0, s*(a[5]*b[5])/2.0); + dd.mat.set(2,1, s*(a[4]*b[4])/2.0); + dd.mat.set(2,2, s*a[2]*b[2]); + dd.mat.set(2,3, s*(a[ 5]*b[4] + a[4]*b[5])/tsq2); + dd.mat.set(2,4, s*(a[4]*b[2] + a[2]*b[4])/2.0); + dd.mat.set(2,5, s*(a[5]*b[2] + a[2]*b[5])/2.0); + dd.mat.set(2,6, s*(-(a[5]*b[4]) + a[4]*b[5])/tsq2); + dd.mat.set(2,7, s*(-(a[4]*b[2]) + a[2]*b[4])/2.0); + dd.mat.set(2,8, s*(-(a[5]*b[2]) + a[2]*b[5])/2.0); + + dd.mat.set(3,0, s*(a[3]*b[0] + a[0]*b[3])/2.0); + dd.mat.set(3,1, s*(a[3]*b[1] + a[1]*b[3])/2.0); + dd.mat.set(3,2, s*(a[5]*b[4] + a[4]*b[5])/tsq2); + dd.mat.set(3,3, s*(a[1]*b[0] + a[0]*b[1] + a[3]*b[3])/2.0); + dd.mat.set(3,4, s*(SQRT_2*a[5]*b[1] + a[4]*b[3] + a[3]*b[4] + SQRT_2*a[1]*b[5])/4.0); + dd.mat.set(3,5, s*(SQRT_2*a[4]*b[0] + a[5]*b[3] + SQRT_2*a[0]*b[4] + a[3]*b[5])/4.0); + dd.mat.set(3,6, s*(a[1]*b[0] - a[0]*b[1])/2.0); + dd.mat.set(3,7, s*(SQRT_2*a[5]*b[1] + a[4]*b[3] - a[3]*b[4] - SQRT_2*a[1]*b[5])/4.0); + dd.mat.set(3,8, s*(SQRT_2*a[4]*b[0] + a[5]*b[3] - SQRT_2*a[0]*b[4] - a[3]*b[5])/4.0); + + dd.mat.set(4,0, s*(a[5]*b[3] + a[3]*b[5])/tsq2); + dd.mat.set(4,1, s*(a[4]*b[1] + a[1]*b[4])/2.0); + dd.mat.set(4,2, s*(a[4]*b[2] + a[2]*b[4])/2.0); + dd.mat.set(4,3, s*(SQRT_2*a[5]*b[1] + a[4]*b[3] + a[3]*b[4] + SQRT_2*a[1]*b[5])/4.0); + dd.mat.set(4,4, s*(a[2]*b[1] + a[1]*b[2] + a[4]*b[4])/2.0); + dd.mat.set(4,5, s*(SQRT_2*a[3]*b[2] + SQRT_2*a[2]*b[3] + a[5]*b[4] + a[4]*b[5])/4.0); + dd.mat.set(4,6, s*(-(SQRT_2*a[5]*b[1]) + a[4]*b[3] - a[3]*b[4] + SQRT_2*a[1]*b[5])/4.0); + dd.mat.set(4,7, s*(a[2]*b[1] - a[1]*b[2])/2.0); + dd.mat.set(4,8, s*(-(SQRT_2*a[3]*b[2]) + SQRT_2*a[2]*b[3] - a[5]*b[4] + a[4]*b[5])/4.0); + + dd.mat.set(5,0, s*(a[5]*b[0] + a[0]*b[5])/2.0); + dd.mat.set(5,1, s*(a[4]*b[3] + a[3]*b[4])/tsq2); + dd.mat.set(5,2, s*(a[5]*b[2] + a[2]*b[5])/2.0); + dd.mat.set(5,3, s*(SQRT_2*a[4]*b[0] + a[5]*b[3] + SQRT_2*a[0]*b[4] + a[3]*b[5])/4.0); + dd.mat.set(5,4, s*(SQRT_2*a[3]*b[2] + SQRT_2*a[2]*b[3] + a[5]*b[4] + a[4]*b[5])/4.0); + dd.mat.set(5,5, s*(a[2]*b[0] + a[0]*b[2] + a[5]*b[5])/2.0); + dd.mat.set(5,6, s*(SQRT_2*a[4]*b[0] - a[5]*b[3] - SQRT_2*a[0]*b[4] + a[3]*b[5])/4.0); + dd.mat.set(5,7, s*(-(SQRT_2*a[3]*b[2]) + SQRT_2*a[2]*b[3] + a[5]*b[4] - a[4]*b[5])/4.0); + dd.mat.set(5,8, s*(a[2]*b[0] - a[0]*b[2])/2.0); + + dd.mat.set(6,0, s*(-(a[3]*b[0]) + a[0]*b[3])/2.0); + dd.mat.set(6,1, s*(a[3]*b[1] - a[1]*b[3])/2.0); + dd.mat.set(6,2, s*(a[5]*b[4] - a[4]*b[5])/tsq2); + dd.mat.set(6,3, s*(-(a[1]*b[0]) + a[0]*b[1])/2.0); + dd.mat.set(6,4, s*(SQRT_2*a[5]*b[1] - a[4]*b[3] + a[3]*b[4] - SQRT_2*a[1]*b[5])/4.0); + dd.mat.set(6,5, s*(-(SQRT_2*a[4]*b[0]) + a[5]*b[3] + SQRT_2*a[0]*b[4] - a[3]*b[5])/4.0); + dd.mat.set(6,6, s*(-(a[1]*b[0]) - a[0]*b[1] + a[3]*b[3])/2.0); + dd.mat.set(6,7, s*(SQRT_2*a[5]*b[1] - a[4]*b[3] - a[3]*b[4] + SQRT_2*a[1]*b[5])/4.0); + dd.mat.set(6,8, s*(-(SQRT_2*a[4]*b[0]) + a[5]*b[3] - SQRT_2*a[0]*b[4] + a[3]*b[5])/4.0); + + dd.mat.set(7,0, s*(-(a[5]*b[3]) + a[3]*b[5])/tsq2); + dd.mat.set(7,1, s*(-(a[4]*b[1]) + a[1]*b[4])/2.0); + dd.mat.set(7,2, s*(a[4]*b[2] - a[2]*b[4])/2.0); + dd.mat.set(7,3, s*(-(SQRT_2*a[5]*b[1]) - a[4]*b[3] + a[3]*b[4] + SQRT_2*a[1]*b[5])/4.0); + dd.mat.set(7,4, s*(-(a[2]*b[1]) + a[1]*b[2])/2.0); + dd.mat.set(7,5, s*(SQRT_2*a[3]*b[2] - SQRT_2*a[2]*b[3] - a[5]*b[4] + a[4]*b[5])/4.0); + dd.mat.set(7,6, s*(SQRT_2*a[5]*b[1] - a[4]*b[3] - a[3]*b[4] + SQRT_2*a[1]*b[5])/4.0); + dd.mat.set(7,7, s*(-(a[2]*b[1]) - a[1]*b[2] + a[4]*b[4])/2.0); + dd.mat.set(7,8, s*(-(SQRT_2*a[3]*b[2]) - SQRT_2*a[2]*b[3] + a[5]*b[4] + a[4]*b[5])/4.0); + + dd.mat.set(8,0, s*(-(a[5]*b[0]) + a[0]*b[5])/2.0); + dd.mat.set(8,1, s*(-(a[4]*b[3]) + a[3]*b[4])/tsq2); + dd.mat.set(8,2, s*(a[5]*b[2] - a[2]*b[5])/2.0); + dd.mat.set(8,3, s*(-(SQRT_2*a[4]*b[0]) - a[5]*b[3] + SQRT_2*a[0]*b[4] + a[3]*b[5])/4.0); + dd.mat.set(8,4, s*(SQRT_2*a[3]*b[2] - SQRT_2*a[2]*b[3] + a[5]*b[4] - a[4]*b[5])/4.0); + dd.mat.set(8,5, s*(-(a[2]*b[0]) + a[0]*b[2])/2.0); + dd.mat.set(8,6, s*(-(SQRT_2*a[4]*b[0]) + a[5]*b[3] - SQRT_2*a[0]*b[4] + a[3]*b[5])/4.0); + dd.mat.set(8,7, s*(-(SQRT_2*a[3]*b[2]) - SQRT_2*a[2]*b[3] + a[5]*b[4] + a[4]*b[5])/4.0); + dd.mat.set(8,8, s*(-(a[2]*b[0]) - a[0]*b[2] + a[5]*b[5])/2.0); + } else { + dd.mat.set(0,0, s*a[0]*b[0]); + dd.mat.set(0,1, s*((a[3] + a[6])*(b[3] + b[6]))/2.0); + dd.mat.set(0,2, s*((a[5] + a[8])*(b[5] + b[8]))/2.0); + dd.mat.set(0,3, s*(a[3]*b[0] + a[6]*b[0] + a[0]*(b[3] + b[6]))/2.0); + dd.mat.set(0,4, s*((a[5] + a[8])*(b[3] + b[6]) + (a[3] + a[6])*(b[5] + b[8]))/tsq2); + dd.mat.set(0,5, s*(a[5]*b[0] + a[8]*b[0] + a[0]*(b[5] + b[8]))/2.0); + dd.mat.set(0,6, s*(a[3]*b[0] + a[6]*b[0] - a[0]*(b[3] + b[6]))/2.0); + dd.mat.set(0,7, s*((a[5] + a[8])*(b[3] + b[6]) - (a[3] + a[6])*(b[5] + b[8]))/tsq2); + dd.mat.set(0,8, s*(a[5]*b[0] + a[8]*b[0] - a[0]*(b[5] + b[8]))/2.0); + + dd.mat.set(1,0, s*((a[3] - a[6])*(b[3] - b[6]))/2.0); + dd.mat.set(1,1, s*a[1]*b[1]); + dd.mat.set(1,2, s*((a[4] + a[7])*(b[4] + b[7]))/2.0); + dd.mat.set(1,3, s*(a[3]*b[1] - a[6]*b[1] + a[1]*(b[3] - b[6]))/2.0); + dd.mat.set(1,4, s*(a[4]*b[1] + a[7]*b[1] + a[1]*(b[4] + b[7]))/2.0); + dd.mat.set(1,5, s*((a[4] + a[7])*(b[3] - b[6]) + (a[3] - a[6])*(b[4] + b[7]))/tsq2); + dd.mat.set(1,6, s*(-(a[3]*b[1]) + a[6]*b[1] + a[1]*(b[3] - b[6]))/2.0); + dd.mat.set(1,7, s*(a[4]*b[1] + a[7]*b[1] - a[1]*(b[4] + b[7]))/2.0); + dd.mat.set(1,8, s*((a[4] + a[7])*(b[3] - b[6]) - (a[3] - a[6])*(b[4] + b[7]))/tsq2); + + dd.mat.set(2,0, s*((a[5] - a[8])*(b[5] - b[8]))/2.0); + dd.mat.set(2,1, s*((a[4] - a[7])*(b[4] - b[7]))/2.0); + dd.mat.set(2,2, s*a[2]*b[2]); + dd.mat.set(2,3, s*((a[5] - a[8])*(b[4] - b[7]) + (a[4] - a[7])*(b[5] - b[8]))/tsq2); + dd.mat.set(2,4, s*(a[4]*b[2] - a[7]*b[2] + a[2]*(b[4] - b[7]))/2.0); + dd.mat.set(2,5, s*(a[5]*b[2] - a[8]*b[2] + a[2]*(b[5] - b[8]))/2.0); + dd.mat.set(2,6, s*(-((a[5] - a[8])*(b[4] - b[7])) + (a[4] - a[7])*(b[5] - b[8]))/tsq2); + dd.mat.set(2,7, s*(-(a[4]*b[2]) + a[7]*b[2] + a[2]*(b[4] - b[7]))/2.0); + dd.mat.set(2,8, s*(-(a[5]*b[2]) + a[8]*b[2] + a[2]*(b[5] - b[8]))/2.0); + + dd.mat.set(3,0, s*(a[3]*b[0] - a[6]*b[0] + a[0]*(b[3] - b[6]))/2.0); + dd.mat.set(3,1, s*(a[3]*b[1] + a[6]*b[1] + a[1]*(b[3] + b[6]))/2.0); + dd.mat.set(3,2, s*((a[5] + a[8])*(b[4] + b[7]) + (a[4] + a[7])*(b[5] + b[8]))/tsq2); + dd.mat.set(3,3, s*(a[1]*b[0] + a[0]*b[1] + a[3]*b[3] - a[6]*b[6])/2.0); + dd.mat.set(3,4, s*(SQRT_2*(a[5] + a[8])*b[1] + (a[4] + a[7])*(b[3] + b[6]) + (a[3] + a[6])*(b[4] + b[7]) + SQRT_2*a[1]*(b[5] + b[8]))/4.0); + dd.mat.set(3,5, s*(SQRT_2*(a[4] + a[7])*b[0] + (a[5] + a[8])*(b[3] - b[6]) + SQRT_2*a[0]*(b[4] + b[7]) + (a[3] - a[6])*(b[5] + b[8]))/4.0); + dd.mat.set(3,6, s*(a[1]*b[0] - a[0]*b[1] + a[6]*b[3] - a[3]*b[6])/2.0); + dd.mat.set(3,7, s*(SQRT_2*(a[5] + a[8])*b[1] + (a[4] + a[7])*(b[3] + b[6]) - (a[3] + a[6])*(b[4] + b[7]) - SQRT_2*a[1]*(b[5] + b[8]))/4.0); + dd.mat.set(3,8, s*(SQRT_2*(a[4] + a[7])*b[0] + (a[5] + a[8])*(b[3] - b[6]) - SQRT_2*a[0]*(b[4] + b[7]) - (a[3] - a[6])*(b[5] + b[8]))/4.0); + + dd.mat.set(4,0, s*((a[5] - a[8])*(b[3] - b[6]) + (a[3] - a[6])*(b[5] - b[8]))/tsq2); + dd.mat.set(4,1, s*(a[4]*b[1] - a[7]*b[1] + a[1]*(b[4] - b[7]))/2.0); + dd.mat.set(4,2, s*(a[4]*b[2] + a[7]*b[2] + a[2]*(b[4] + b[7]))/2.0); + dd.mat.set(4,3, s*(SQRT_2*(a[5] - a[8])*b[1] + (a[4] - a[7])*(b[3] - b[6]) + (a[3] - a[6])*(b[4] - b[7]) + SQRT_2*a[1]*(b[5] - b[8]))/4.0); + dd.mat.set(4,4, s*(a[2]*b[1] + a[1]*b[2] + a[4]*b[4] - a[7]*b[7])/2.0); + dd.mat.set(4,5, s*(SQRT_2*(a[3] - a[6])*b[2] + SQRT_2*a[2]*(b[3] - b[6]) + (a[5] - a[8])*(b[4] + b[7]) + (a[4] + a[7])*(b[5] - b[8]))/4.0); + dd.mat.set(4,6, s*(-(SQRT_2*(a[5] - a[8])*b[1]) + (a[4] - a[7])*(b[3] - b[6]) - (a[3] - a[6])*(b[4] - b[7]) + SQRT_2*a[1]*(b[5] - b[8]))/4.0); + dd.mat.set(4,7, s*(a[2]*b[1] - a[1]*b[2] + a[7]*b[4] - a[4]*b[7])/2.0); + dd.mat.set(4,8, s*(-(SQRT_2*(a[3] - a[6])*b[2]) + SQRT_2*a[2]*(b[3] - b[6]) - (a[5] - a[8])*(b[4] + b[7]) + (a[4] + a[7])*(b[5] - b[8]))/4.0); + + dd.mat.set(5,0, s*(a[5]*b[0] - a[8]*b[0] + a[0]*(b[5] - b[8]))/2.0); + dd.mat.set(5,1, s*((a[4] - a[7])*(b[3] + b[6]) + (a[3] + a[6])*(b[4] - b[7]))/tsq2); + dd.mat.set(5,2, s*(a[5]*b[2] + a[8]*b[2] + a[2]*(b[5] + b[8]))/2.0); + dd.mat.set(5,3, s*(SQRT_2*(a[4] - a[7])*b[0] + (a[5] - a[8])*(b[3] + b[6]) + SQRT_2*a[0]*(b[4] - b[7]) + (a[3] + a[6])*(b[5] - b[8]))/4.0); + dd.mat.set(5,4, s*(SQRT_2*(a[3] + a[6])*b[2] + SQRT_2*a[2]*(b[3] + b[6]) + (a[5] + a[8])*(b[4] - b[7]) + (a[4] - a[7])*(b[5] + b[8]))/4.0); + dd.mat.set(5,5, s*(a[2]*b[0] + a[0]*b[2] + a[5]*b[5] - a[8]*b[8])/2.0); + dd.mat.set(5,6, s*(SQRT_2*(a[4] - a[7])*b[0] - (a[5] - a[8])*(b[3] + b[6]) - SQRT_2*a[0]*(b[4] - b[7]) + (a[3] + a[6])*(b[5] - b[8]))/4.0); + dd.mat.set(5,7, s*(-(SQRT_2*(a[3] + a[6])*b[2]) + SQRT_2*a[2]*(b[3] + b[6]) + (a[5] + a[8])*(b[4] - b[7]) - (a[4] - a[7])*(b[5] + b[8]))/4.0); + dd.mat.set(5,8, s*(a[2]*b[0] - a[0]*b[2] + a[8]*b[5] - a[5]*b[8])/2.0); + + dd.mat.set(6,0, s*(-(a[3]*b[0]) + a[6]*b[0] + a[0]*(b[3] - b[6]))/2.0); + dd.mat.set(6,1, s*(a[3]*b[1] + a[6]*b[1] - a[1]*(b[3] + b[6]))/2.0); + dd.mat.set(6,2, s*((a[5] + a[8])*(b[4] + b[7]) - (a[4] + a[7])*(b[5] + b[8]))/tsq2); + dd.mat.set(6,3, s*(-(a[1]*b[0]) + a[0]*b[1] + a[6]*b[3] - a[3]*b[6])/2.0); + dd.mat.set(6,4, s*(SQRT_2*(a[5] + a[8])*b[1] - (a[4] + a[7])*(b[3] + b[6]) + (a[3] + a[6])*(b[4] + b[7]) - SQRT_2*a[1]*(b[5] + b[8]))/4.0); + dd.mat.set(6,5, s*(-(SQRT_2*(a[4] + a[7])*b[0]) + (a[5] + a[8])*(b[3] - b[6]) + SQRT_2*a[0]*(b[4] + b[7]) - (a[3] - a[6])*(b[5] + b[8]))/4.0); + dd.mat.set(6,6, s*(-(a[1]*b[0]) - a[0]*b[1] + a[3]*b[3] - a[6]*b[6])/2.0); + dd.mat.set(6,7, s*(SQRT_2*(a[5] + a[8])*b[1] - (a[4] + a[7])*(b[3] + b[6]) - (a[3] + a[6])*(b[4] + b[7]) + SQRT_2*a[1]*(b[5] + b[8]))/4.0); + dd.mat.set(6,8, s*(-(SQRT_2*(a[4] + a[7])*b[0]) + (a[5] + a[8])*(b[3] - b[6]) - SQRT_2*a[0]*(b[4] + b[7]) + (a[3] - a[6])*(b[5] + b[8]))/4.0); + + dd.mat.set(7,0, s*(-((a[5] - a[8])*(b[3] - b[6])) + (a[3] - a[6])*(b[5] - b[8]))/tsq2); + dd.mat.set(7,1, s*(-(a[4]*b[1]) + a[7]*b[1] + a[1]*(b[4] - b[7]))/2.0); + dd.mat.set(7,2, s*(a[4]*b[2] + a[7]*b[2] - a[2]*(b[4] + b[7]))/2.0); + dd.mat.set(7,3, s*(-(SQRT_2*(a[5] - a[8])*b[1]) - (a[4] - a[7])*(b[3] - b[6]) + (a[3] - a[6])*(b[4] - b[7]) + SQRT_2*a[1]*(b[5] - b[8]))/4.0); + dd.mat.set(7,4, s*(-(a[2]*b[1]) + a[1]*b[2] + a[7]*b[4] - a[4]*b[7])/2.0); + dd.mat.set(7,5, s*(SQRT_2*(a[3] - a[6])*b[2] - SQRT_2*a[2]*(b[3] - b[6]) - (a[5] - a[8])*(b[4] + b[7]) + (a[4] + a[7])*(b[5] - b[8]))/4.0); + dd.mat.set(7,6, s*(SQRT_2*(a[5] - a[8])*b[1] - (a[4] - a[7])*(b[3] - b[6]) - (a[3] - a[6])*(b[4] - b[7]) + SQRT_2*a[1]*(b[5] - b[8]))/4.0); + dd.mat.set(7,7, s*(-(a[2]*b[1]) - a[1]*b[2] + a[4]*b[4] - a[7]*b[7])/2.0); + dd.mat.set(7,8, s*(-(SQRT_2*(a[3] - a[6])*b[2]) - SQRT_2*a[2]*(b[3] - b[6]) + (a[5] - a[8])*(b[4] + b[7]) + (a[4] + a[7])*(b[5] - b[8]))/4.0); + + dd.mat.set(8,0, s*(-(a[5]*b[0]) + a[8]*b[0] + a[0]*(b[5] - b[8]))/2.0); + dd.mat.set(8,1, s*(-((a[4] - a[7])*(b[3] + b[6])) + (a[3] + a[6])*(b[4] - b[7]))/tsq2); + dd.mat.set(8,2, s*(a[5]*b[2] + a[8]*b[2] - a[2]*(b[5] + b[8]))/2.0); + dd.mat.set(8,3, s*(-(SQRT_2*(a[4] - a[7])*b[0]) - (a[5] - a[8])*(b[3] + b[6]) + SQRT_2*a[0]*(b[4] - b[7]) + (a[3] + a[6])*(b[5] - b[8]))/4.0); + dd.mat.set(8,4, s*(SQRT_2*(a[3] + a[6])*b[2] - SQRT_2*a[2]*(b[3] + b[6]) + (a[5] + a[8])*(b[4] - b[7]) - (a[4] - a[7])*(b[5] + b[8]))/4.0); + dd.mat.set(8,5, s*(-(a[2]*b[0]) + a[0]*b[2] + a[8]*b[5] - a[5]*b[8])/2.0); + dd.mat.set(8,6, s*(-(SQRT_2*(a[4] - a[7])*b[0]) + (a[5] - a[8])*(b[3] + b[6]) - SQRT_2*a[0]*(b[4] - b[7]) + (a[3] + a[6])*(b[5] - b[8]))/4.0); + dd.mat.set(8,7, s*(-(SQRT_2*(a[3] + a[6])*b[2]) - SQRT_2*a[2]*(b[3] + b[6]) + (a[5] + a[8])*(b[4] - b[7]) + (a[4] - a[7])*(b[5] + b[8]))/4.0); + dd.mat.set(8,8, s*(-(a[2]*b[0]) - a[0]*b[2] + a[5]*b[5] - a[8]*b[8])/2.0); + } +} + +/// Performs the self-sum-dyadic (ssd) operation with a Tensor2 yielding a minor-symmetric Tensor4 +/// +/// Computes: +/// +/// ```text +/// _ +/// D = s (A ⊗ A + A ⊗ A) +/// ‾ +/// ``` +/// +/// With orthonormal Cartesian components: +/// +/// ```text +/// Dᵢⱼₖₗ = s (Aᵢₖ Aⱼₗ + Aᵢₗ Aⱼₖ) +/// ``` +/// +/// **Important:** Even if `A` is Symmetric 2D, the result may not be expressed by a Symmetric 2D Tensor4. +/// +/// # Output +/// +/// * `dd` -- The resulting tensor (minor-symmetric); it must be [Mandel::Symmetric] +/// +/// # Input +/// +/// * `aa` -- Second-order tensor, symmetric or not. +/// +/// # Panics +/// +/// A panic will occur if `dd` is not [Mandel::Symmetric] +#[rustfmt::skip] +pub fn t2_ssd(dd: &mut Tensor4, s: f64, aa: &Tensor2) { + assert_eq!(dd.mandel, Mandel::Symmetric); + let dim = aa.vec.dim(); + let a = &aa.vec; + if dim == 4 { + dd.mat.set(0,0, s*(2.0*a[0]*a[0])); + dd.mat.set(0,1, s*(a[3]*a[3])); + dd.mat.set(0,2, 0.0); + dd.mat.set(0,3, s*(2.0*a[0]*a[3])); + dd.mat.set(0,4, 0.0); + dd.mat.set(0,5, 0.0); + + dd.mat.set(1,0, s*(a[3]*a[3])); + dd.mat.set(1,1, s*(2.0*a[1]*a[1])); + dd.mat.set(1,2, 0.0); + dd.mat.set(1,3, s*(2.0*a[1]*a[3])); + dd.mat.set(1,4, 0.0); + dd.mat.set(1,5, 0.0); + + dd.mat.set(2,0, 0.0); + dd.mat.set(2,1, 0.0); + dd.mat.set(2,2, s*(2.0*a[2]*a[2])); + dd.mat.set(2,3, 0.0); + dd.mat.set(2,4, 0.0); + dd.mat.set(2,5, 0.0); + + dd.mat.set(3,0, s*(2.0*a[0]*a[3])); + dd.mat.set(3,1, s*(2.0*a[1]*a[3])); + dd.mat.set(3,2, 0.0); + dd.mat.set(3,3, s*(2.0*a[0]*a[1] + a[3]*a[3])); + dd.mat.set(3,4, 0.0); + dd.mat.set(3,5, 0.0); + + dd.mat.set(4,0, 0.0); + dd.mat.set(4,1, 0.0); + dd.mat.set(4,2, 0.0); + dd.mat.set(4,3, 0.0); + dd.mat.set(4,4, s*(2.0*a[1]*a[2])); + dd.mat.set(4,5, s*(SQRT_2*a[2]*a[3])); + + dd.mat.set(5,0, 0.0); + dd.mat.set(5,1, 0.0); + dd.mat.set(5,2, 0.0); + dd.mat.set(5,3, 0.0); + dd.mat.set(5,4, s*(SQRT_2*a[2]*a[3])); + dd.mat.set(5,5, s*(2.0*a[0]*a[2])); + } else if dim == 6 { + dd.mat.set(0,0, s*(2.0*a[0]*a[0])); + dd.mat.set(0,1, s*(a[3]*a[3])); + dd.mat.set(0,2, s*(a[5]*a[5])); + dd.mat.set(0,3, s*(2.0*a[0]*a[3])); + dd.mat.set(0,4, s*(SQRT_2*a[3]*a[5])); + dd.mat.set(0,5, s*(2.0*a[ 0]*a[5])); + + dd.mat.set(1,0, s*(a[3]*a[3])); + dd.mat.set(1,1, s*(2.0*a[1]*a[1])); + dd.mat.set(1,2, s*(a[4]*a[4])); + dd.mat.set(1,3, s*(2.0*a[1]*a[3])); + dd.mat.set(1,4, s*(2.0*a[1]*a[4])); + dd.mat.set(1,5, s*(SQRT_2*a[3]*a[4])); + + dd.mat.set(2,0, s*(a[5]*a[5])); + dd.mat.set(2,1, s*(a[4]*a[4])); + dd.mat.set(2,2, s*(2.0*a[2]*a[2])); + dd.mat.set(2,3, s*(SQRT_2*a[4]*a[ 5])); + dd.mat.set(2,4, s*(2.0*a[2]*a[4])); + dd.mat.set(2,5, s*(2.0*a[2]*a[5])); + + dd.mat.set(3,0, s*(2.0*a[0]*a[3])); + dd.mat.set(3,1, s*(2.0*a[1]*a[3])); + dd.mat.set(3,2, s*(SQRT_2*a[4]* a[5])); + dd.mat.set(3,3, s*(2.0*a[0]*a[1] + a[3]*a[3])); + dd.mat.set(3,4, s*(a[3]*a[4] + SQRT_2*a[1]*a[5])); + dd.mat.set(3,5, s*(SQRT_2*a[0]*a[4] + a[3]*a[5])); + + dd.mat.set(4,0, s*(SQRT_2*a[3]*a[5])); + dd.mat.set(4,1, s*(2.0*a[1]*a[4])); + dd.mat.set(4,2, s*(2.0*a[2]*a[4])); + dd.mat.set(4,3, s*(a[3]*a[4] + SQRT_2*a[1]*a[5])); + dd.mat.set(4,4, s*(2.0*a[1]*a[2] + a[4]*a[4])); + dd.mat.set(4,5, s*(SQRT_2*a[2]*a[3] + a[4]*a[5])); + + dd.mat.set(5,0, s*(2.0*a[0]*a[5])); + dd.mat.set(5,1, s*(SQRT_2*a[3]*a[4])); + dd.mat.set(5,2, s*(2.0*a[2]*a[5])); + dd.mat.set(5,3, s*(SQRT_2*a[0]* a[4] + a[3]*a[5])); + dd.mat.set(5,4, s*(SQRT_2*a[2]*a[3] + a[4]*a[5])); + dd.mat.set(5,5, s*(2.0*a[0]*a[2] + a[5]*a[5])); + } else { + dd.mat.set(0,0, s*(2.0*a[0]*a[0])); + dd.mat.set(0,1, s*((a[3] + a[6])*(a[3] + a[6]))); + dd.mat.set(0,2, s*((a[5] + a[8])*(a[5] + a[8]))); + dd.mat.set(0,3, s*(2.0*a[0]*(a[3] + a[6]))); + dd.mat.set(0,4, s*(SQRT_2*(a[3] + a[6])*(a[5] + a[8]))); + dd.mat.set(0,5, s*(2.0*a[0]*(a[5] + a[8]))); + + dd.mat.set(1,0, s*((a[3] - a[6])*(a[3] - a[6]))); + dd.mat.set(1,1, s*(2.0*a[1]*a[1])); + dd.mat.set(1,2, s*((a[4] + a[7])*(a[4] + a[7]))); + dd.mat.set(1,3, s*(2.0*a[1]*(a[3] - a[6]))); + dd.mat.set(1,4, s*(2.0*a[1]*(a[4] + a[7]))); + dd.mat.set(1,5, s*(SQRT_2*(a[3] - a[6])*(a[4] + a[7]))); + + dd.mat.set(2,0, s*((a[5] - a[8])*(a[5] - a[8]))); + dd.mat.set(2,1, s*((a[4] - a[7])*(a[4] - a[7]))); + dd.mat.set(2,2, s*(2.0*a[2]*a[2])); + dd.mat.set(2,3, s*(SQRT_2*(a[4] - a[7])*(a[5] - a[8]))); + dd.mat.set(2,4, s*(2.0*a[2]*(a[4] - a[7]))); + dd.mat.set(2,5, s*(2.0*a[2]*(a[5] - a[8]))); + + dd.mat.set(3,0, s*(2.0*a[0]*(a[3] - a[6]))); + dd.mat.set(3,1, s*(2.0*a[1]*(a[3] + a[6]))); + dd.mat.set(3,2, s*(SQRT_2*(a[4] + a[7])*(a[5] + a[8]))); + dd.mat.set(3,3, s*(2.0*a[0]*a[1] + a[3]*a[3] - a[6]*a[6])); + dd.mat.set(3,4, s*((a[3] + a[6])*(a[4] + a[7]) + SQRT_2*a[1]*(a[5] + a[8]))); + dd.mat.set(3,5, s*(SQRT_2*a[0]*(a[4] + a[7]) + (a[3] - a[6])*(a[5] + a[8]))); + + dd.mat.set(4,0, s*(SQRT_2*(a[3] - a[6])*(a[5] - a[8]))); + dd.mat.set(4,1, s*(2.0*a[1]*(a[4] - a[7]))); + dd.mat.set(4,2, s*(2.0*a[2]*(a[4] + a[7]))); + dd.mat.set(4,3, s*((a[3] - a[6])*(a[4] - a[7]) + SQRT_2*a[1]*(a[5] - a[8]))); + dd.mat.set(4,4, s*(2.0*a[1]*a[2] + a[4]*a[4] - a[7]*a[7])); + dd.mat.set(4,5, s*(SQRT_2*a[2]*(a[3] - a[6]) + (a[4] + a[7])*(a[5] - a[8]))); + + dd.mat.set(5,0, s*(2.0*a[0]*(a[5] - a[8]))); + dd.mat.set(5,1, s*(SQRT_2*(a[3] + a[6])*(a[4] - a[7]))); + dd.mat.set(5,2, s*(2.0*a[2]*(a[5] + a[8]))); + dd.mat.set(5,3, s*(SQRT_2*a[0]*(a[4] - a[7]) + (a[3] + a[6])*(a[5] - a[8]))); + dd.mat.set(5,4, s*(SQRT_2*a[2]*(a[3] + a[6]) + (a[4] - a[7])*(a[5] + a[8]))); + dd.mat.set(5,5, s*(2.0*a[0]*a[2] + a[5]*a[5] - a[8]*a[8])); + } +} + +/// Performs the quad-sum-dyadic (qsd) operation with two Tensor2 yielding a minor-symmetric Tensor4 +/// +/// Computes: +/// +/// ```text +/// _ _ +/// D = s (A ⊗ B + A ⊗ B + B ⊗ A + B ⊗ A) +/// ‾ ‾ +/// ``` +/// +/// With orthonormal Cartesian components: +/// +/// ```text +/// Dᵢⱼₖₗ = s (Aᵢₖ Bⱼₗ + Aᵢₗ Bⱼₖ + Bᵢₖ Aⱼₗ + Bᵢₗ Aⱼₖ) +/// ``` +/// +/// **Important:** Even if `A` and `B` are Symmetric 2D, the result may not be expressed by a Symmetric 2D Tensor4. +/// +/// # Output +/// +/// * `dd` -- The resulting tensor (minor-symmetric); it must be [Mandel::Symmetric] +/// +/// # Input +/// +/// * `aa` -- Second-order tensor, symmetric or not; with the same [Mandel] as `bb` +/// * `bb` -- Second-order tensor, symmetric or not; with the same [Mandel] as `aa` +/// +/// # Panics +/// +/// 1. A panic will occur if `dd` is not [Mandel::Symmetric] +/// 2. A panic will occur `aa` and `bb` have different [Mandel] +#[rustfmt::skip] +pub fn t2_qsd_t2(dd: &mut Tensor4, s: f64, aa: &Tensor2, bb: &Tensor2) { + assert_eq!(dd.mandel, Mandel::Symmetric); + assert_eq!(bb.mandel, aa.mandel); + let dim = aa.vec.dim(); + let a = &aa.vec; + let b = &bb.vec; + if dim == 4 { + dd.mat.set(0,0, s*(4.0*a[0]*b[0])); + dd.mat.set(0,1, s*(2.0*a[3]*b[3])); + dd.mat.set(0,2, 0.0); + dd.mat.set(0,3, s*(2.0*(a[3]*b[0] + a[0]*b[3]))); + dd.mat.set(0,4, 0.0); + dd.mat.set(0,5, 0.0); + + dd.mat.set(1,0, s*(2.0*a[3]*b[3])); + dd.mat.set(1,1, s*(4.0*a[1]*b[1])); + dd.mat.set(1,2, 0.0); + dd.mat.set(1,3, s*(2.0*(a[3]*b[1] + a[1]*b[3]))); + dd.mat.set(1,4, 0.0); + dd.mat.set(1,5, 0.0); + + dd.mat.set(2,0, 0.0); + dd.mat.set(2,1, 0.0); + dd.mat.set(2,2, s*(4.0*a[2]*b[2])); + dd.mat.set(2,3, 0.0); + dd.mat.set(2,4, 0.0); + dd.mat.set(2,5, 0.0); + + dd.mat.set(3,0, s*(2.0*(a[3]*b[0] + a[0]*b[3]))); + dd.mat.set(3,1, s*(2.0*(a[3]*b[1] + a[1]*b[3]))); + dd.mat.set(3,2, 0.0); + dd.mat.set(3,3, s*(2.0*(a[1]*b[0] + a[0]*b[1] + a[3]*b[3]))); + dd.mat.set(3,4, 0.0); + dd.mat.set(3,5, 0.0); + + dd.mat.set(4,0, 0.0); + dd.mat.set(4,1, 0.0); + dd.mat.set(4,2, 0.0); + dd.mat.set(4,3, 0.0); + dd.mat.set(4,4, s*(2.0*(a[2]*b[1] + a[1]*b[2]))); + dd.mat.set(4,5, s*(SQRT_2*(a[3]*b[2] + a[2]*b[3]))); + + dd.mat.set(5,0, 0.0); + dd.mat.set(5,1, 0.0); + dd.mat.set(5,2, 0.0); + dd.mat.set(5,3, 0.0); + dd.mat.set(5,4, s*(SQRT_2*(a[3]*b[2] + a[2]*b[3]))); + dd.mat.set(5,5, s*(2.0*(a[2]*b[0] + a[0]*b[2]))); + } else if dim == 6 { + dd.mat.set(0,0, s*(4.0*a[0]*b[0])); + dd.mat.set(0,1, s*(2.0*a[3]*b[3])); + dd.mat.set(0,2, s*(2.0*a[5]*b[5])); + dd.mat.set(0,3, s*(2.0*(a[3]*b[0] + a[0]*b[3]))); + dd.mat.set(0,4, s*(SQRT_2*(a[5]*b[3] + a[3]*b[5]))); + dd.mat.set(0,5, s*(2.0*(a[5]*b[0] + a[0]*b[5]))); + + dd.mat.set(1,0, s*(2.0*a[3]*b[3])); + dd.mat.set(1,1, s*(4.0*a[1]*b[1])); + dd.mat.set(1,2, s*(2.0*a[4]*b[4])); + dd.mat.set(1,3, s*(2.0*(a[3]*b[1] + a[1]*b[3]))); + dd.mat.set(1,4, s*(2.0*(a[4]*b[1] + a[1]*b[4]))); + dd.mat.set(1,5, s*(SQRT_2*(a[4]*b[3] + a[3]*b[4]))); + + dd.mat.set(2,0, s*(2.0*a[5]*b[5])); + dd.mat.set(2,1, s*(2.0*a[4]*b[4])); + dd.mat.set(2,2, s*(4.0*a[2]*b[2])); + dd.mat.set(2,3, s*(SQRT_2*(a[5]*b[4] + a[4]*b[5]))); + dd.mat.set(2,4, s*(2.0*(a[4]*b[2] + a[2]*b[4]))); + dd.mat.set(2,5, s*(2.0*(a[5]*b[2] + a[2]*b[5]))); + + dd.mat.set(3,0, s*(2.0*(a[3]*b[0] + a[0]*b[3]))); + dd.mat.set(3,1, s*(2.0*(a[3]*b[1] + a[1]*b[3]))); + dd.mat.set(3,2, s*(SQRT_2*(a[5]*b[4] + a[4]*b[5]))); + dd.mat.set(3,3, s*(2.0*(a[1]*b[0] + a[0]*b[1] + a[3]*b[3]))); + dd.mat.set(3,4, s*(SQRT_2*a[5]*b[1] + a[4]*b[3] + a[3]*b[4] + SQRT_2*a[1]*b[5])); + dd.mat.set(3,5, s*(SQRT_2*a[4]*b[0] + a[5]*b[3] + SQRT_2*a[0]*b[4] + a[3]*b[5])); + + dd.mat.set(4,0, s*(SQRT_2*(a[5]*b[3] + a[3]*b[5]))); + dd.mat.set(4,1, s*(2.0*(a[4]*b[1] + a[1]*b[4]))); + dd.mat.set(4,2, s*(2.0*(a[4]*b[2] + a[2]*b[4]))); + dd.mat.set(4,3, s*(SQRT_2*a[5]*b[1] + a[4]*b[3] + a[3]*b[4] + SQRT_2*a[1]*b[5])); + dd.mat.set(4,4, s*(2.0*(a[2]*b[1] + a[1]*b[2] + a[4]*b[4]))); + dd.mat.set(4,5, s*(SQRT_2*a[3]*b[2] + SQRT_2*a[2]*b[3] + a[5]*b[4] + a[4]*b[5])); + + dd.mat.set(5,0, s*(2.0*(a[5]*b[0] + a[0]*b[5]))); + dd.mat.set(5,1, s*(SQRT_2*(a[4]*b[3] + a[3]*b[4]))); + dd.mat.set(5,2, s*(2.0*(a[5]*b[2] + a[2]*b[5]))); + dd.mat.set(5,3, s*(SQRT_2*a[4]*b[0] + a[5]*b[3] + SQRT_2*a[0]*b[4] + a[3]*b[5])); + dd.mat.set(5,4, s*(SQRT_2*a[3]*b[2] + SQRT_2*a[2]*b[3] + a[5]*b[4] + a[4]*b[5])); + dd.mat.set(5,5, s*(2.0*(a[2]*b[0] + a[0]*b[2] + a[5]*b[5]))); + } else { + dd.mat.set(0,0, s*(4.0*a[0]*b[0])); + dd.mat.set(0,1, s*(2.0*(a[3] + a[6])*(b[3] + b[6]))); + dd.mat.set(0,2, s*(2.0*(a[5] + a[8])*(b[5] + b[8]))); + dd.mat.set(0,3, s*(2.0*(a[3]*b[0] + a[6]*b[0] + a[0]*(b[3] + b[6])))); + dd.mat.set(0,4, s*(SQRT_2*((a[5] + a[8])*(b[3] + b[6]) + (a[3] + a[6])*(b[5] + b[8])))); + dd.mat.set(0,5, s*(2.0*(a[5]*b[0] + a[8]*b[0] + a[0]*(b[5] + b[8])))); + + dd.mat.set(1,0, s*(2.0*(a[3] - a[6])*(b[3] - b[6]))); + dd.mat.set(1,1, s*(4.0*a[1]*b[1])); + dd.mat.set(1,2, s*(2.0*(a[4] + a[7])*(b[4] + b[7]))); + dd.mat.set(1,3, s*(2.0*(a[3]*b[1] - a[6]*b[1] + a[1]*(b[3] - b[6])))); + dd.mat.set(1,4, s*(2.0*(a[4]*b[1] + a[7]*b[1] + a[1]*(b[4] + b[7])))); + dd.mat.set(1,5, s*(SQRT_2*((a[4] + a[7])*(b[3] - b[6]) + (a[3] - a[6])*(b[4] + b[7])))); + + dd.mat.set(2,0, s*(2.0*(a[5] - a[8])*(b[5] - b[8]))); + dd.mat.set(2,1, s*(2.0*(a[4] - a[7])*(b[4] - b[7]))); + dd.mat.set(2,2, s*(4.0*a[2]*b[2])); + dd.mat.set(2,3, s*(SQRT_2*((a[5] - a[8])*(b[4] - b[7]) + (a[4] - a[7])*(b[5] - b[8])))); + dd.mat.set(2,4, s*(2.0*(a[4]*b[2] - a[7]*b[2] + a[2]*(b[4] - b[7])))); + dd.mat.set(2,5, s*(2.0*(a[5]*b[2] - a[8]*b[2] + a[2]*(b[5] - b[8])))); + + dd.mat.set(3,0, s*(2.0*(a[3]*b[0] - a[6]*b[0] + a[0]*(b[3] - b[6])))); + dd.mat.set(3,1, s*(2.0*(a[3]*b[1] + a[6]*b[1] + a[1]*(b[3] + b[6])))); + dd.mat.set(3,2, s*(SQRT_2*((a[5] + a[8])*(b[4] + b[7]) + (a[4] + a[7])*(b[5] + b[8])))); + dd.mat.set(3,3, s*(2.0*(a[1]*b[0] + a[0]*b[1] + a[3]*b[3] - a[6]*b[6]))); + dd.mat.set(3,4, s*(SQRT_2*(a[5] + a[8])*b[1] + (a[4] + a[7])*(b[3] + b[6]) + (a[3] + a[6])*(b[4] + b[7]) + SQRT_2*a[1]*(b[5] + b[8]))); + dd.mat.set(3,5, s*(SQRT_2*(a[4] + a[7])*b[0] + (a[5] + a[8])*(b[3] - b[6]) + SQRT_2*a[0]*(b[4] + b[7]) + (a[3] - a[6])*(b[5] + b[8]))); + + dd.mat.set(4,0, s*(SQRT_2*((a[5] - a[8])*(b[3] - b[6]) + (a[3] - a[6])*(b[5] - b[8])))); + dd.mat.set(4,1, s*(2.0*(a[4]*b[1] - a[7]*b[1] + a[1]*(b[4] - b[7])))); + dd.mat.set(4,2, s*(2.0*(a[4]*b[2] + a[7]*b[2] + a[2]*(b[4] + b[7])))); + dd.mat.set(4,3, s*(SQRT_2*(a[5] - a[8])*b[1] + (a[4] - a[7])*(b[3] - b[6]) + (a[3] - a[6])*(b[4] - b[7]) + SQRT_2*a[1]*(b[5] - b[8]))); + dd.mat.set(4,4, s*(2.0*(a[2]*b[1] + a[1]*b[2] + a[4]*b[4] - a[7]*b[7]))); + dd.mat.set(4,5, s*(SQRT_2*(a[3] - a[6])*b[2] + SQRT_2*a[2]*(b[3] - b[6]) + (a[5] - a[8])*(b[4] + b[7]) + (a[4] + a[7])*(b[5] - b[8]))); + + dd.mat.set(5,0, s*(2.0*(a[5]*b[0] - a[8]*b[0] + a[0]*(b[5] - b[8])))); + dd.mat.set(5,1, s*(SQRT_2*((a[4] - a[7])*(b[3] + b[6]) + (a[3] + a[6])*(b[4] - b[7])))); + dd.mat.set(5,2, s*(2.0*(a[5]*b[2] + a[8]*b[2] + a[2]*(b[5] + b[8])))); + dd.mat.set(5,3, s*(SQRT_2*(a[4] - a[7])*b[0] + (a[5] - a[8])*(b[3] + b[6]) + SQRT_2*a[0]*(b[4] - b[7]) + (a[3] + a[6])*(b[5] - b[8]))); + dd.mat.set(5,4, s*(SQRT_2*(a[3] + a[6])*b[2] + SQRT_2*a[2]*(b[3] + b[6]) + (a[5] + a[8])*(b[4] - b[7]) + (a[4] - a[7])*(b[5] + b[8]))); + dd.mat.set(5,5, s*(2.0*(a[2]*b[0] + a[0]*b[2] + a[5]*b[5] - a[8]*b[8]))); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#[cfg(test)] +mod tests { + use super::*; + use crate::{Mandel, MN_TO_IJKL}; + use russell_lab::{mat_approx_eq, Matrix}; + + #[test] + #[should_panic] + fn t2_odyad_t2_panics_on_non_general() { + let a = Tensor2::new(Mandel::Symmetric2D); + let b = Tensor2::new(Mandel::Symmetric2D); + let mut dd = Tensor4::new(Mandel::Symmetric2D); // wrong; it must be General + t2_odyad_t2(&mut dd, 1.0, &a, &b); + } + + #[test] + #[should_panic] + fn t2_odyad_t2_panics_on_different_mandel() { + let a = Tensor2::new(Mandel::Symmetric2D); + let b = Tensor2::new(Mandel::Symmetric); // wrong; it must be the same as `a` + let mut dd = Tensor4::new(Mandel::General); + t2_odyad_t2(&mut dd, 1.0, &a, &b); + } + + fn check_odyad(s: f64, a_ten: &Tensor2, b_ten: &Tensor2, dd_ten: &Tensor4, tol: f64) { + let a = a_ten.as_matrix(); + let b = b_ten.as_matrix(); + let dd = dd_ten.as_matrix(); + let mut correct = Matrix::new(9, 9); + for m in 0..9 { + for n in 0..9 { + let (i, j, k, l) = MN_TO_IJKL[m][n]; + correct.set(m, n, s * a.get(i, k) * b.get(j, l)); + } + } + mat_approx_eq(&dd, &correct, tol); + } + + #[test] + fn t2_odyad_t2_works() { + // general odyad general + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 2.0, 3.0], + [4.0, 5.0, 6.0], + [7.0, 8.0, 9.0], + ], Mandel::General).unwrap(); + #[rustfmt::skip] + let b = Tensor2::from_matrix(&[ + [9.0, 8.0, 7.0], + [6.0, 5.0, 4.0], + [3.0, 2.0, 1.0], + ], Mandel::General).unwrap(); + let mut dd = Tensor4::new(Mandel::General); + t2_odyad_t2(&mut dd, 2.0, &a, &b); + let mat = dd.as_matrix(); + let correct = Matrix::from(&[ + [18.0, 32.0, 42.0, 16.0, 28.0, 14.0, 36.0, 48.0, 54.0], + [48.0, 50.0, 48.0, 40.0, 40.0, 32.0, 60.0, 60.0, 72.0], + [42.0, 32.0, 18.0, 28.0, 16.0, 14.0, 48.0, 36.0, 54.0], + [12.0, 20.0, 24.0, 10.0, 16.0, 8.0, 24.0, 30.0, 36.0], + [24.0, 20.0, 12.0, 16.0, 10.0, 8.0, 30.0, 24.0, 36.0], + [6.0, 8.0, 6.0, 4.0, 4.0, 2.0, 12.0, 12.0, 18.0], + [72.0, 80.0, 84.0, 64.0, 70.0, 56.0, 90.0, 96.0, 108.0], + [84.0, 80.0, 72.0, 70.0, 64.0, 56.0, 96.0, 90.0, 108.0], + [126.0, 128.0, 126.0, 112.0, 112.0, 98.0, 144.0, 144.0, 162.0], + ]); + mat_approx_eq(&mat, &correct, 1e-13); + check_odyad(2.0, &a, &b, &dd, 1e-13); + + // symmetric odyad symmetric + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 4.0, 6.0], + [4.0, 2.0, 5.0], + [6.0, 5.0, 3.0], + ], Mandel::Symmetric).unwrap(); + #[rustfmt::skip] + let b = Tensor2::from_matrix(&[ + [3.0, 5.0, 6.0], + [5.0, 2.0, 4.0], + [6.0, 4.0, 1.0], + ], Mandel::Symmetric).unwrap(); + let mut dd = Tensor4::new(Mandel::General); + t2_odyad_t2(&mut dd, 2.0, &a, &b); + let mat = dd.as_matrix(); + let correct = Matrix::from(&[ + [6.0, 40.0, 72.0, 10.0, 48.0, 12.0, 24.0, 60.0, 36.0], + [40.0, 8.0, 40.0, 16.0, 16.0, 32.0, 20.0, 20.0, 50.0], + [72.0, 40.0, 6.0, 48.0, 10.0, 12.0, 60.0, 24.0, 36.0], + [10.0, 16.0, 48.0, 4.0, 32.0, 8.0, 40.0, 24.0, 60.0], + [48.0, 16.0, 10.0, 32.0, 4.0, 8.0, 24.0, 40.0, 60.0], + [12.0, 32.0, 12.0, 8.0, 8.0, 2.0, 48.0, 48.0, 72.0], + [24.0, 20.0, 60.0, 40.0, 24.0, 48.0, 12.0, 50.0, 30.0], + [60.0, 20.0, 24.0, 24.0, 40.0, 48.0, 50.0, 12.0, 30.0], + [36.0, 50.0, 36.0, 60.0, 60.0, 72.0, 30.0, 30.0, 18.0], + ]); + mat_approx_eq(&mat, &correct, 1e-13); + check_odyad(2.0, &a, &b, &dd, 1e-13); + + // symmetric 2D odyad symmetric 2D + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 4.0, 0.0], + [4.0, 2.0, 0.0], + [0.0, 0.0, 3.0], + ], Mandel::Symmetric2D).unwrap(); + #[rustfmt::skip] + let b = Tensor2::from_matrix(&[ + [3.0, 4.0, 0.0], + [4.0, 2.0, 0.0], + [0.0, 0.0, 1.0], + ], Mandel::Symmetric2D).unwrap(); + let mut dd = Tensor4::new(Mandel::General); + t2_odyad_t2(&mut dd, 2.0, &a, &b); + let mat = dd.as_matrix(); + // println!("{:.1}", mat); + let correct = Matrix::from(&[ + [6.0, 32.0, 0.0, 8.0, 0.0, 0.0, 24.0, 0.0, 0.0], + [32.0, 8.0, 0.0, 16.0, 0.0, 0.0, 16.0, 0.0, 0.0], + [0.0, 0.0, 6.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [8.0, 16.0, 0.0, 4.0, 0.0, 0.0, 32.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 4.0, 8.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 8.0, 2.0, 0.0, 0.0, 0.0], + [24.0, 16.0, 0.0, 32.0, 0.0, 0.0, 12.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 12.0, 24.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 24.0, 18.0], + ]); + mat_approx_eq(&mat, &correct, 1e-14); + check_odyad(2.0, &a, &b, &dd, 1e-15); + } + + #[test] + #[should_panic] + fn t2_udyad_t2_panics_on_non_general() { + let a = Tensor2::new(Mandel::Symmetric2D); + let b = Tensor2::new(Mandel::Symmetric2D); + let mut dd = Tensor4::new(Mandel::Symmetric2D); // wrong; it must be General + t2_udyad_t2(&mut dd, 1.0, &a, &b); + } + + #[test] + #[should_panic] + fn t2_udyad_t2_panics_on_different_mandel() { + let a = Tensor2::new(Mandel::Symmetric2D); + let b = Tensor2::new(Mandel::Symmetric); // wrong; it must be the same as `a` + let mut dd = Tensor4::new(Mandel::General); + t2_udyad_t2(&mut dd, 1.0, &a, &b); + } + + fn check_udyad(s: f64, a_ten: &Tensor2, b_ten: &Tensor2, dd_ten: &Tensor4, tol: f64) { + let a = a_ten.as_matrix(); + let b = b_ten.as_matrix(); + let dd = dd_ten.as_matrix(); + let mut correct = Matrix::new(9, 9); + for m in 0..9 { + for n in 0..9 { + let (i, j, k, l) = MN_TO_IJKL[m][n]; + correct.set(m, n, s * a.get(i, l) * b.get(j, k)); + } + } + mat_approx_eq(&dd, &correct, tol); + } + + #[test] + fn t2_udyad_t2_works() { + // general udyad general + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 2.0, 3.0], + [4.0, 5.0, 6.0], + [7.0, 8.0, 9.0], + ], Mandel::General).unwrap(); + #[rustfmt::skip] + let b = Tensor2::from_matrix(&[ + [9.0, 8.0, 7.0], + [6.0, 5.0, 4.0], + [3.0, 2.0, 1.0], + ], Mandel::General).unwrap(); + let mut dd = Tensor4::new(Mandel::General); + t2_udyad_t2(&mut dd, 2.0, &a, &b); + let mat = dd.as_matrix(); + let correct = Matrix::from(&[ + [18.0, 32.0, 42.0, 36.0, 48.0, 54.0, 16.0, 28.0, 14.0], + [48.0, 50.0, 48.0, 60.0, 60.0, 72.0, 40.0, 40.0, 32.0], + [42.0, 32.0, 18.0, 48.0, 36.0, 54.0, 28.0, 16.0, 14.0], + [12.0, 20.0, 24.0, 24.0, 30.0, 36.0, 10.0, 16.0, 8.0], + [24.0, 20.0, 12.0, 30.0, 24.0, 36.0, 16.0, 10.0, 8.0], + [6.0, 8.0, 6.0, 12.0, 12.0, 18.0, 4.0, 4.0, 2.0], + [72.0, 80.0, 84.0, 90.0, 96.0, 108.0, 64.0, 70.0, 56.0], + [84.0, 80.0, 72.0, 96.0, 90.0, 108.0, 70.0, 64.0, 56.0], + [126.0, 128.0, 126.0, 144.0, 144.0, 162.0, 112.0, 112.0, 98.0], + ]); + mat_approx_eq(&mat, &correct, 1e-13); + check_udyad(2.0, &a, &b, &dd, 1e-13); + + // symmetric udyad symmetric + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 4.0, 6.0], + [4.0, 2.0, 5.0], + [6.0, 5.0, 3.0], + ], Mandel::Symmetric).unwrap(); + #[rustfmt::skip] + let b = Tensor2::from_matrix(&[ + [3.0, 5.0, 6.0], + [5.0, 2.0, 4.0], + [6.0, 4.0, 1.0], + ], Mandel::Symmetric).unwrap(); + let mut dd = Tensor4::new(Mandel::General); + t2_udyad_t2(&mut dd, 2.0, &a, &b); + let mat = dd.as_matrix(); + let correct = Matrix::from(&[ + [6.0, 40.0, 72.0, 24.0, 60.0, 36.0, 10.0, 48.0, 12.0], + [40.0, 8.0, 40.0, 20.0, 20.0, 50.0, 16.0, 16.0, 32.0], + [72.0, 40.0, 6.0, 60.0, 24.0, 36.0, 48.0, 10.0, 12.0], + [10.0, 16.0, 48.0, 40.0, 24.0, 60.0, 4.0, 32.0, 8.0], + [48.0, 16.0, 10.0, 24.0, 40.0, 60.0, 32.0, 4.0, 8.0], + [12.0, 32.0, 12.0, 48.0, 48.0, 72.0, 8.0, 8.0, 2.0], + [24.0, 20.0, 60.0, 12.0, 50.0, 30.0, 40.0, 24.0, 48.0], + [60.0, 20.0, 24.0, 50.0, 12.0, 30.0, 24.0, 40.0, 48.0], + [36.0, 50.0, 36.0, 30.0, 30.0, 18.0, 60.0, 60.0, 72.0], + ]); + mat_approx_eq(&mat, &correct, 1e-13); + check_udyad(2.0, &a, &b, &dd, 1e-13); + + // symmetric 2D udyad symmetric 2D + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 4.0, 0.0], + [4.0, 2.0, 0.0], + [0.0, 0.0, 3.0], + ], Mandel::Symmetric2D).unwrap(); + #[rustfmt::skip] + let b = Tensor2::from_matrix(&[ + [3.0, 4.0, 0.0], + [4.0, 2.0, 0.0], + [0.0, 0.0, 1.0], + ], Mandel::Symmetric2D).unwrap(); + let mut dd = Tensor4::new(Mandel::General); + t2_udyad_t2(&mut dd, 2.0, &a, &b); + let mandel_mat = Matrix::from(&[ + [6.0, 32.0, 0.0, 16.0 * SQRT_2, 0.0, 0.0, 8.0 * SQRT_2, 0.0, 0.0], + [32.0, 8.0, 0.0, 16.0 * SQRT_2, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 6.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [16.0 * SQRT_2, 16.0 * SQRT_2, 0.0, 40.0, 0.0, 0.0, 4.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 8.0, 16.0, 0.0, 4.0, 8.0], + [0.0, 0.0, 0.0, 0.0, 16.0, 10.0, 0.0, 8.0, 8.0], + [-8.0 * SQRT_2, 0.0, 0.0, -4.0, 0.0, 0.0, 24.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, -4.0, -8.0, 0.0, -8.0, -16.0], + [0.0, 0.0, 0.0, 0.0, -8.0, -8.0, 0.0, -16.0, -10.0], + ]); + mat_approx_eq(&dd.mat, &mandel_mat, 1e-14); + let mat = dd.as_matrix(); + let correct = Matrix::from(&[ + [6.0, 32.0, 0.0, 24.0, 0.0, 0.0, 8.0, 0.0, 0.0], + [32.0, 8.0, 0.0, 16.0, 0.0, 0.0, 16.0, 0.0, 0.0], + [0.0, 0.0, 6.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [8.0, 16.0, 0.0, 32.0, 0.0, 0.0, 4.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 8.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 8.0, 2.0], + [24.0, 16.0, 0.0, 12.0, 0.0, 0.0, 32.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 12.0, 24.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 24.0, 18.0, 0.0, 0.0, 0.0], + ]); + mat_approx_eq(&mat, &correct, 1e-14); + check_udyad(2.0, &a, &b, &dd, 1e-15); + } + + #[test] + #[should_panic] + fn t2_ssd_panics_on_non_sym() { + let a = Tensor2::new(Mandel::Symmetric2D); + let mut dd = Tensor4::new(Mandel::Symmetric2D); // wrong; it must be Symmetric + t2_ssd(&mut dd, 1.0, &a); + } + + fn check_ssd(s: f64, a_ten: &Tensor2, dd_ten: &Tensor4, tol: f64) { + let a = a_ten.as_matrix(); + let dd = dd_ten.as_matrix(); + let mut correct = Matrix::new(9, 9); + for m in 0..9 { + for n in 0..9 { + let (i, j, k, l) = MN_TO_IJKL[m][n]; + correct.set(m, n, s * (a.get(i, k) * a.get(j, l) + a.get(i, l) * a.get(j, k))); + } + } + mat_approx_eq(&dd, &correct, tol); + } + + #[test] + fn t2_ssd_works() { + // general + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 2.0, 3.0], + [4.0, 5.0, 6.0], + [7.0, 8.0, 9.0], + ], Mandel::General).unwrap(); + let mut dd = Tensor4::new(Mandel::Symmetric); + t2_ssd(&mut dd, 2.0, &a); + let mat = dd.as_matrix(); + let correct = Matrix::from(&[ + [4.0, 16.0, 36.0, 8.0, 24.0, 12.0, 8.0, 24.0, 12.0], + [64.0, 100.0, 144.0, 80.0, 120.0, 96.0, 80.0, 120.0, 96.0], + [196.0, 256.0, 324.0, 224.0, 288.0, 252.0, 224.0, 288.0, 252.0], + [16.0, 40.0, 72.0, 26.0, 54.0, 36.0, 26.0, 54.0, 36.0], + [112.0, 160.0, 216.0, 134.0, 186.0, 156.0, 134.0, 186.0, 156.0], + [28.0, 64.0, 108.0, 44.0, 84.0, 60.0, 44.0, 84.0, 60.0], + [16.0, 40.0, 72.0, 26.0, 54.0, 36.0, 26.0, 54.0, 36.0], + [112.0, 160.0, 216.0, 134.0, 186.0, 156.0, 134.0, 186.0, 156.0], + [28.0, 64.0, 108.0, 44.0, 84.0, 60.0, 44.0, 84.0, 60.0], + ]); + mat_approx_eq(&mat, &correct, 1e-13); + check_ssd(2.0, &a, &dd, 1e-13); + + // symmetric + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 4.0, 6.0], + [4.0, 2.0, 5.0], + [6.0, 5.0, 3.0], + ], Mandel::Symmetric).unwrap(); + let mut dd = Tensor4::new(Mandel::Symmetric); + t2_ssd(&mut dd, 2.0, &a); + let mat = dd.as_matrix(); + let correct = Matrix::from(&[ + [4.0, 64.0, 144.0, 16.0, 96.0, 24.0, 16.0, 96.0, 24.0], + [64.0, 16.0, 100.0, 32.0, 40.0, 80.0, 32.0, 40.0, 80.0], + [144.0, 100.0, 36.0, 120.0, 60.0, 72.0, 120.0, 60.0, 72.0], + [16.0, 32.0, 120.0, 36.0, 64.0, 58.0, 36.0, 64.0, 58.0], + [96.0, 40.0, 60.0, 64.0, 62.0, 84.0, 64.0, 62.0, 84.0], + [24.0, 80.0, 72.0, 58.0, 84.0, 78.0, 58.0, 84.0, 78.0], + [16.0, 32.0, 120.0, 36.0, 64.0, 58.0, 36.0, 64.0, 58.0], + [96.0, 40.0, 60.0, 64.0, 62.0, 84.0, 64.0, 62.0, 84.0], + [24.0, 80.0, 72.0, 58.0, 84.0, 78.0, 58.0, 84.0, 78.0], + ]); + mat_approx_eq(&mat, &correct, 1e-13); + check_ssd(2.0, &a, &dd, 1e-13); + + // symmetric 2D + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 4.0, 0.0], + [4.0, 2.0, 0.0], + [0.0, 0.0, 3.0], + ], Mandel::Symmetric2D).unwrap(); + let mut dd = Tensor4::new(Mandel::Symmetric); + t2_ssd(&mut dd, 2.0, &a); + let mat = dd.as_matrix(); + let correct = Matrix::from(&[ + [4.0, 64.0, 0.0, 16.0, 0.0, 0.0, 16.0, 0.0, 0.0], + [64.0, 16.0, 0.0, 32.0, 0.0, 0.0, 32.0, 0.0, 0.0], + [0.0, 0.0, 36.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [16.0, 32.0, 0.0, 36.0, 0.0, 0.0, 36.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 12.0, 24.0, 0.0, 12.0, 24.0], + [0.0, 0.0, 0.0, 0.0, 24.0, 6.0, 0.0, 24.0, 6.0], + [16.0, 32.0, 0.0, 36.0, 0.0, 0.0, 36.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 12.0, 24.0, 0.0, 12.0, 24.0], + [0.0, 0.0, 0.0, 0.0, 24.0, 6.0, 0.0, 24.0, 6.0], + ]); + mat_approx_eq(&mat, &correct, 1e-13); + check_ssd(2.0, &a, &dd, 1e-14); + } + + #[test] + #[should_panic] + fn t2_qsd_t2_panics_on_non_sym() { + let a = Tensor2::new(Mandel::Symmetric2D); + let b = Tensor2::new(Mandel::Symmetric2D); + let mut dd = Tensor4::new(Mandel::Symmetric2D); // wrong; it must be Symmetric + t2_qsd_t2(&mut dd, 1.0, &a, &b); + } + + #[test] + #[should_panic] + fn t2_qsd_t2_panics_on_different_mandel() { + let a = Tensor2::new(Mandel::Symmetric2D); + let b = Tensor2::new(Mandel::Symmetric); // wrong; it must be the same as `a` + let mut dd = Tensor4::new(Mandel::Symmetric); + t2_qsd_t2(&mut dd, 1.0, &a, &b); + } + + fn check_qsd(s: f64, a_ten: &Tensor2, b_ten: &Tensor2, dd_ten: &Tensor4, tol: f64) { + let a = a_ten.as_matrix(); + let b = b_ten.as_matrix(); + let dd = dd_ten.as_matrix(); + let mut correct = Matrix::new(9, 9); + for m in 0..9 { + for n in 0..9 { + let (i, j, k, l) = MN_TO_IJKL[m][n]; + correct.set(m, n, s * a.get(i, l) * b.get(j, k)); + correct.set( + m, + n, + s * (a.get(i, k) * b.get(j, l) + + a.get(i, l) * b.get(j, k) + + b.get(i, k) * a.get(j, l) + + b.get(i, l) * a.get(j, k)), + ); + } + } + mat_approx_eq(&dd, &correct, tol); + } + + #[test] + fn t2_qsd_t2_works() { + // general qsd general + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 2.0, 3.0], + [4.0, 5.0, 6.0], + [7.0, 8.0, 9.0], + ], Mandel::General).unwrap(); + #[rustfmt::skip] + let b = Tensor2::from_matrix(&[ + [9.0, 8.0, 7.0], + [6.0, 5.0, 4.0], + [3.0, 2.0, 1.0], + ], Mandel::General).unwrap(); + let mut dd = Tensor4::new(Mandel::Symmetric); + t2_qsd_t2(&mut dd, 2.0, &a, &b); + let mat = dd.as_matrix(); + let correct = Matrix::from(&[ + [72.0, 128.0, 168.0, 104.0, 152.0, 136.0, 104.0, 152.0, 136.0], + [192.0, 200.0, 192.0, 200.0, 200.0, 208.0, 200.0, 200.0, 208.0], + [168.0, 128.0, 72.0, 152.0, 104.0, 136.0, 152.0, 104.0, 136.0], + [168.0, 200.0, 216.0, 188.0, 212.0, 208.0, 188.0, 212.0, 208.0], + [216.0, 200.0, 168.0, 212.0, 188.0, 208.0, 212.0, 188.0, 208.0], + [264.0, 272.0, 264.0, 272.0, 272.0, 280.0, 272.0, 272.0, 280.0], + [168.0, 200.0, 216.0, 188.0, 212.0, 208.0, 188.0, 212.0, 208.0], + [216.0, 200.0, 168.0, 212.0, 188.0, 208.0, 212.0, 188.0, 208.0], + [264.0, 272.0, 264.0, 272.0, 272.0, 280.0, 272.0, 272.0, 280.0], + ]); + mat_approx_eq(&mat, &correct, 1e-13); + check_qsd(2.0, &a, &b, &dd, 1e-13); + + // symmetric qsd symmetric + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 4.0, 6.0], + [4.0, 2.0, 5.0], + [6.0, 5.0, 3.0], + ], Mandel::Symmetric).unwrap(); + #[rustfmt::skip] + let b = Tensor2::from_matrix(&[ + [3.0, 5.0, 6.0], + [5.0, 2.0, 4.0], + [6.0, 4.0, 1.0], + ], Mandel::Symmetric).unwrap(); + let mut dd = Tensor4::new(Mandel::Symmetric); + t2_qsd_t2(&mut dd, 2.0, &a, &b); + let mat = dd.as_matrix(); + let correct = Matrix::from(&[ + [24.0, 160.0, 288.0, 68.0, 216.0, 96.0, 68.0, 216.0, 96.0], + [160.0, 32.0, 160.0, 72.0, 72.0, 164.0, 72.0, 72.0, 164.0], + [288.0, 160.0, 24.0, 216.0, 68.0, 96.0, 216.0, 68.0, 96.0], + [68.0, 72.0, 216.0, 96.0, 130.0, 146.0, 96.0, 130.0, 146.0], + [216.0, 72.0, 68.0, 130.0, 96.0, 146.0, 130.0, 96.0, 146.0], + [96.0, 164.0, 96.0, 146.0, 146.0, 164.0, 146.0, 146.0, 164.0], + [68.0, 72.0, 216.0, 96.0, 130.0, 146.0, 96.0, 130.0, 146.0], + [216.0, 72.0, 68.0, 130.0, 96.0, 146.0, 130.0, 96.0, 146.0], + [96.0, 164.0, 96.0, 146.0, 146.0, 164.0, 146.0, 146.0, 164.0], + ]); + mat_approx_eq(&mat, &correct, 1e-13); + check_qsd(2.0, &a, &b, &dd, 1e-13); + + // symmetric 2D qsd symmetric 2D + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 4.0, 0.0], + [4.0, 2.0, 0.0], + [0.0, 0.0, 3.0], + ], Mandel::Symmetric2D).unwrap(); + #[rustfmt::skip] + let b = Tensor2::from_matrix(&[ + [3.0, 4.0, 0.0], + [4.0, 2.0, 0.0], + [0.0, 0.0, 1.0], + ], Mandel::Symmetric2D).unwrap(); + let mut dd = Tensor4::new(Mandel::Symmetric); + t2_qsd_t2(&mut dd, 2.0, &a, &b); + let mat = dd.as_matrix(); + let correct = Matrix::from(&[ + [24.0, 128.0, 0.0, 64.0, 0.0, 0.0, 64.0, 0.0, 0.0], + [128.0, 32.0, 0.0, 64.0, 0.0, 0.0, 64.0, 0.0, 0.0], + [0.0, 0.0, 24.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [64.0, 64.0, 0.0, 80.0, 0.0, 0.0, 80.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 16.0, 32.0, 0.0, 16.0, 32.0], + [0.0, 0.0, 0.0, 0.0, 32.0, 20.0, 0.0, 32.0, 20.0], + [64.0, 64.0, 0.0, 80.0, 0.0, 0.0, 80.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 16.0, 32.0, 0.0, 16.0, 32.0], + [0.0, 0.0, 0.0, 0.0, 32.0, 20.0, 0.0, 32.0, 20.0], + ]); + mat_approx_eq(&mat, &correct, 1e-13); + check_qsd(2.0, &a, &b, &dd, 1e-14); + } +} diff --git a/russell_tensor/src/operations_t2.rs b/russell_tensor/src/operations_t2.rs new file mode 100644 index 00000000..771cfe2e --- /dev/null +++ b/russell_tensor/src/operations_t2.rs @@ -0,0 +1,810 @@ +use super::Tensor2; +use crate::{Mandel, SQRT_2}; +use russell_lab::vec_inner; +use russell_lab::{StrError, Vector}; + +/// Performs the double-dot (ddot) operation between two Tensor2 (inner product) +/// +/// Computes: +/// +/// ```text +/// s = a : b +/// ``` +/// +/// With orthonormal Cartesian components: +/// +/// ```text +/// s = Σ Σ aᵢⱼ bᵢⱼ +/// i j +/// ``` +/// +/// Or, in Mandel basis: +/// +/// ```text +/// s = Σ aₘ bₘ +/// m +/// ``` +/// +/// # Input +/// +/// * `a` -- first tensor; with the same [Mandel] as `b` +/// * `b` -- second tensor; with the same [Mandel] as `a` +/// +/// # Output +/// +/// Returns the scalar result of `a : b`. +/// +/// # Panics +/// +/// A panic will occur `a` and `b` have different [Mandel] +/// +/// # Examples +/// +/// ``` +/// use russell_lab::approx_eq; +/// use russell_tensor::{t2_ddot_t2, Mandel, Tensor2, StrError}; +/// +/// fn main() -> Result<(), StrError> { +/// let a = Tensor2::from_matrix(&[ +/// [1.0, 1.0, 0.0], +/// [1.0, -1.0, 0.0], +/// [0.0, 0.0, 1.0], +/// ], Mandel::Symmetric2D)?; +/// +/// let b = Tensor2::from_matrix(&[ +/// [1.0, 2.0, 0.0], +/// [3.0, -1.0, 5.0], +/// [0.0, 4.0, 1.0], +/// ], Mandel::General)?; +/// +/// let res = t2_ddot_t2(&a.as_general(), &b); +/// +/// approx_eq(res, 8.0, 1e-15); +/// Ok(()) +/// } +/// ``` +pub fn t2_ddot_t2(a: &Tensor2, b: &Tensor2) -> f64 { + assert_eq!(a.mandel, b.mandel); + vec_inner(&a.vec, &b.vec) +} + +/// Performs the single dot operation between two Tensor2 (matrix multiplication) +/// +/// Computes: +/// +/// ```text +/// c = a · b +/// ``` +/// +/// With orthonormal Cartesian components: +/// +/// ```text +/// cᵢⱼ = Σ aᵢₖ bₖⱼ +/// k +/// ``` +/// +/// **Important:** Even if `a` and `b` are symmetric, the result `c` +/// may not be symmetric. Therefore, `c` must be a General tensor. +/// +/// # Output +/// +/// * `c` -- the resulting tensor; it must be [Mandel::General] +/// +/// # Input +/// +/// * `a` -- first tensor; with the same [Mandel] as `b` +/// * `b` -- second tensor; with the same [Mandel] as `a` +/// +/// # Panics +/// +/// 1. A panic will occur if `c` is not [Mandel::General] +/// 2. A panic will occur the `a` and `b` have different [Mandel] +/// +/// # Examples +/// +/// ``` +/// use russell_tensor::{t2_dot_t2, Mandel, Tensor2, StrError}; +/// +/// fn main() -> Result<(), StrError> { +/// let a = Tensor2::from_matrix(&[ +/// [1.0, 1.0, 0.0], +/// [1.0, -1.0, 0.0], +/// [0.0, 0.0, 1.0], +/// ], Mandel::General)?; +/// +/// let b = Tensor2::from_matrix(&[ +/// [1.0, 2.0, 0.0], +/// [3.0, -1.0, 5.0], +/// [0.0, 4.0, 1.0], +/// ], Mandel::General)?; +/// +/// let mut c = Tensor2::new(Mandel::General); +/// t2_dot_t2(&mut c, &a, &b); +/// assert_eq!( +/// format!("{:.1}", c.as_matrix()), +/// "┌ ┐\n\ +/// │ 4.0 1.0 5.0 │\n\ +/// │ -2.0 3.0 -5.0 │\n\ +/// │ 0.0 4.0 1.0 │\n\ +/// └ ┘" +/// ); +/// Ok(()) +/// } +/// ``` +#[rustfmt::skip] +pub fn t2_dot_t2(c: &mut Tensor2, a: &Tensor2, b: &Tensor2) { + assert_eq!(c.mandel, Mandel::General); + assert_eq!(b.mandel, a.mandel); + let dim = a.vec.dim(); + let a = &a.vec; + let b = &b.vec; + let c = &mut c.vec; + let tsq2 = 2.0 * SQRT_2; + if dim == 4 { + c[0] = a[0] * b[0] + (a[3] * b[3]) / 2.0; + c[1] = a[1] * b[1] + (a[3] * b[3]) / 2.0; + c[2] = a[2] * b[2]; + c[3] = (a[3] * (b[0] + b[1]) + (a[0] + a[1]) * b[3]) / 2.0; + c[4] = 0.0; + c[5] = 0.0; + c[6] = (a[3] * (-b[0] + b[1]) + (a[0] - a[1]) * b[3]) / 2.0; + c[7] = 0.0; + c[8] = 0.0; + } else if dim == 6 { + c[0] = (2.0 * a[0] * b[0] + a[3] * b[3] + a[5] * b[5]) / 2.0; + c[1] = (2.0 * a[1] * b[1] + a[3] * b[3] + a[4] * b[4]) / 2.0; + c[2] = (2.0 * a[2] * b[2] + a[4] * b[4] + a[5] * b[5]) / 2.0; + c[3] = (SQRT_2 * a[3] * (b[0] + b[1]) + SQRT_2 * a[0] * b[3] + SQRT_2 * a[1] * b[3] + a[5] * b[4] + a[4] * b[5]) / tsq2; + c[4] = (SQRT_2 * a[4] * (b[1] + b[2]) + a[5] * b[3] + SQRT_2 * a[1] * b[4] + SQRT_2 * a[2] * b[4] + a[3] * b[5]) / tsq2; + c[5] = (SQRT_2 * a[5] * (b[0] + b[2]) + a[4] * b[3] + a[3] * b[4] + SQRT_2 * a[0] * b[5] + SQRT_2 * a[2] * b[5]) / tsq2; + c[6] = (SQRT_2 * a[3] * (-b[0] + b[1]) + SQRT_2 * a[0] * b[3] - SQRT_2 * a[1] * b[3] + a[5] * b[4] - a[4] * b[5]) / tsq2; + c[7] = (SQRT_2 * a[4] * (-b[1] + b[2]) - a[5] * b[3] + SQRT_2 * a[1] * b[4] - SQRT_2 * a[2] * b[4] + a[3] * b[5]) / tsq2; + c[8] = (SQRT_2 * a[5] * (-b[0] + b[2]) - a[4] * b[3] + a[3] * b[4] + SQRT_2 * a[0] * b[5] - SQRT_2 * a[2] * b[5]) / tsq2; + } else { + c[0] = (2.0 * a[0] * b[0] + (a[3] + a[6]) * (b[3] - b[6]) + (a[5] + a[8]) * (b[5] - b[8])) / 2.0; + c[1] = (2.0 * a[1] * b[1] + (a[3] - a[6]) * (b[3] + b[6]) + (a[4] + a[7]) * (b[4] - b[7])) / 2.0; + c[2] = (2.0 * a[2] * b[2] + (a[4] - a[7]) * (b[4] + b[7]) + (a[5] - a[8]) * (b[5] + b[8])) / 2.0; + c[3] = (2.0 * (a[3] - a[6]) * b[0] + 2.0 * (a[3] + a[6]) * b[1] + 2.0 * a[1] * (b[3] - b[6]) + 2.0 * a[0] * (b[3] + b[6]) + SQRT_2 * (a[5] + a[8]) * (b[4] - b[7]) + SQRT_2 * (a[4] + a[7]) * (b[5] - b[8])) / 4.0; + c[4] = (2.0 * (a[4] - a[7]) * b[1] + 2.0 * (a[4] + a[7]) * b[2] + SQRT_2 * (a[5] - a[8]) * (b[3] + b[6]) + 2.0 * a[2] * (b[4] - b[7]) + 2.0 * a[1] * (b[4] + b[7]) + SQRT_2 * (a[3] - a[6]) * (b[5] + b[8])) / 4.0; + c[5] = (2.0 * (a[5] - a[8]) * b[0] + 2.0 * (a[5] + a[8]) * b[2] + SQRT_2 * (a[4] - a[7]) * (b[3] - b[6]) + SQRT_2 * (a[3] + a[6]) * (b[4] + b[7]) + 2.0 * a[2] * (b[5] - b[8]) + 2.0 * a[0] * (b[5] + b[8])) / 4.0; + c[6] = (-2.0 * (a[3] - a[6]) * b[0] + 2.0 * (a[3] + a[6]) * b[1] - 2.0 * a[1] * (b[3] - b[6]) + 2.0 * a[0] * (b[3] + b[6]) + SQRT_2 * (a[5] + a[8]) * (b[4] - b[7]) - SQRT_2 * (a[4] + a[7]) * (b[5] - b[8])) / 4.0; + c[7] = (-2.0 * (a[4] - a[7]) * b[1] + 2.0 * (a[4] + a[7]) * b[2] - SQRT_2 * (a[5] - a[8]) * (b[3] + b[6]) - 2.0 * a[2] * (b[4] - b[7]) + 2.0 * a[1] * (b[4] + b[7]) + SQRT_2 * (a[3] - a[6]) * (b[5] + b[8])) / 4.0; + c[8] = (-2.0 * (a[5] - a[8]) * b[0] + 2.0 * (a[5] + a[8]) * b[2] - SQRT_2 * (a[4] - a[7]) * (b[3] - b[6]) + SQRT_2 * (a[3] + a[6]) * (b[4] + b[7]) - 2.0 * a[2] * (b[5] - b[8]) + 2.0 * a[0] * (b[5] + b[8])) / 4.0; + } +} + +/// Performs the single dot operation between a Tensor2 and a vector +/// +/// Computes: +/// +/// ```text +/// v = α a · u +/// ``` +/// +/// With orthonormal Cartesian components: +/// +/// ```text +/// vᵢ = α Σ aᵢⱼ uⱼ +/// j +/// ``` +/// +/// # Output +/// +/// * `v` -- the resulting vector; with `dim` compatible with the dimension of `a` (2D or 3D) +/// +/// # Input +/// +/// * `alpha` -- the `α` multiplier +/// * `a` -- the second-order tensor +/// * `u` -- the 2D or 3D vector; with `dim` compatible with the dimension of `a` (2D or 3D) +/// +/// # Panics +/// +/// 1. If `a` is 2D, a panic will occur if `u` or `v` are not `2D` +/// 2. If `a` is 3D, a panic will occur if `u` or `v` are not `3D` +/// +/// # Examples +/// +/// ``` +/// use russell_lab::Vector; +/// use russell_tensor::{t2_dot_vec, Mandel, Tensor2, StrError}; +/// +/// fn main() -> Result<(), StrError> { +/// let a = Tensor2::from_matrix(&[ +/// [1.0, 1.0, 0.0], +/// [1.0, -1.0, 0.0], +/// [0.0, 0.0, 1.0], +/// ], Mandel::Symmetric2D)?; +/// +/// let u = Vector::from(&[1.0, 2.0]); +/// let mut v = Vector::new(2); +/// t2_dot_vec(&mut v, 2.0, &a, &u); +/// +/// assert_eq!( +/// format!("{:.1}", v), +/// "┌ ┐\n\ +/// │ 6.0 │\n\ +/// │ -2.0 │\n\ +/// └ ┘" +/// ); +/// Ok(()) +/// } +/// ``` +pub fn t2_dot_vec(v: &mut Vector, alpha: f64, a: &Tensor2, u: &Vector) { + if a.vec.dim() == 4 { + assert_eq!(v.dim(), 2); + assert_eq!(u.dim(), 2); + v[0] = alpha * (a.get(0, 0) * u[0] + a.get(0, 1) * u[1]); + v[1] = alpha * (a.get(1, 0) * u[0] + a.get(1, 1) * u[1]); + } else { + assert_eq!(v.dim(), 3); + assert_eq!(u.dim(), 3); + v[0] = alpha * (a.get(0, 0) * u[0] + a.get(0, 1) * u[1] + a.get(0, 2) * u[2]); + v[1] = alpha * (a.get(1, 0) * u[0] + a.get(1, 1) * u[1] + a.get(1, 2) * u[2]); + v[2] = alpha * (a.get(2, 0) * u[0] + a.get(2, 1) * u[1] + a.get(2, 2) * u[2]); + } +} + +/// Performs the single dot operation between a vector and a Tensor2 +/// +/// Computes: +/// +/// ```text +/// v = α u · a +/// ``` +/// +/// With orthonormal Cartesian components: +/// +/// ```text +/// vⱼ = α Σ uᵢ aᵢⱼ +/// i +/// ``` +/// +/// # Output +/// +/// * `v` -- the resulting vector; with `dim` compatible with the dimension of `a` (2D or 3D) +/// +/// # Input +/// +/// * `alpha` -- the `α` multiplier +/// * `u` -- the 2D or 3D vector; with `dim` compatible with the dimension of `a` (2D or 3D) +/// * `a` -- the second-order tensor +/// +/// # Panics +/// +/// 1. If `a` is 2D, a panic will occur if `u` or `v` are not `2D` +/// 2. If `a` is 3D, a panic will occur if `u` or `v` are not `3D` +/// +/// # Examples +/// +/// ``` +/// use russell_lab::Vector; +/// use russell_tensor::{vec_dot_t2, Mandel, Tensor2, StrError}; +/// +/// fn main() -> Result<(), StrError> { +/// let u = Vector::from(&[1.0, 2.0]); +/// let a = Tensor2::from_matrix(&[ +/// [1.0, 1.0, 0.0], +/// [1.0, -1.0, 0.0], +/// [0.0, 0.0, 1.0], +/// ], Mandel::Symmetric2D)?; +/// +/// let mut v = Vector::new(2); +/// vec_dot_t2(&mut v, 2.0, &u, &a); +/// +/// assert_eq!( +/// format!("{:.1}", v), +/// "┌ ┐\n\ +/// │ 6.0 │\n\ +/// │ -2.0 │\n\ +/// └ ┘" +/// ); +/// Ok(()) +/// } +/// ``` +pub fn vec_dot_t2(v: &mut Vector, alpha: f64, u: &Vector, a: &Tensor2) { + if a.vec.dim() == 4 { + assert_eq!(v.dim(), 2); + assert_eq!(u.dim(), 2); + v[0] = alpha * (u[0] * a.get(0, 0) + u[1] * a.get(1, 0)); + v[1] = alpha * (u[0] * a.get(0, 1) + u[1] * a.get(1, 1)); + } else { + assert_eq!(v.dim(), 3); + assert_eq!(u.dim(), 3); + v[0] = alpha * (u[0] * a.get(0, 0) + u[1] * a.get(1, 0) + u[2] * a.get(2, 0)); + v[1] = alpha * (u[0] * a.get(0, 1) + u[1] * a.get(1, 1) + u[2] * a.get(2, 1)); + v[2] = alpha * (u[0] * a.get(0, 2) + u[1] * a.get(1, 2) + u[2] * a.get(2, 2)); + } +} + +/// Performs the dyadic product between two vectors resulting in a second-order tensor +/// +/// Computes: +/// +/// ```text +/// A = α u ⊗ v +/// ``` +/// +/// With orthonormal Cartesian components: +/// +/// ```text +/// Aᵢⱼ = α uᵢ vⱼ +/// ``` +/// +/// **Important:** The dyadic product between two vectors may result in a **non-symmetric** +/// second-order tensor. Therefore, if the input tensor `A` is symmetric, an error may occur. +/// Thus, make sure that the you expect `u ⊗ v` to be symmetric when passing a symmetric tensor `A`. +/// +/// # Output +/// +/// * `A` -- the resulting second-order tensor +/// +/// # Input +/// +/// * `alpha` -- the `α` multiplier +/// * `u` -- the 2D or 3D vector; with `dim` compatible with the dimension of `T` (2D or 3D) +/// * `v` -- the 2D or 3D vector; with `dim` compatible with the dimension of `T` (2D or 3D) +/// +/// # Panics +/// +/// 1. If `A` is 2D, a panic will occur if `u` or `v` are not `2D` +/// 2. If `A` is 3D, a panic will occur if `u` or `v` are not `3D` +/// +/// # Examples +/// +/// ``` +/// use russell_lab::Vector; +/// use russell_tensor::{vec_dyad_vec, Mandel, Tensor2, StrError}; +/// +/// fn main() -> Result<(), StrError> { +/// let u = Vector::from(&[1.0, 1.0, 1.0]); +/// let v = Vector::from(&[2.0, 2.0, 2.0]); +/// +/// let mut tt = Tensor2::new(Mandel::Symmetric); +/// vec_dyad_vec(&mut tt, 1.0, &u, &v)?; +/// +/// assert_eq!( +/// format!("{:.1}", tt.as_matrix()), +/// "┌ ┐\n\ +/// │ 2.0 2.0 2.0 │\n\ +/// │ 2.0 2.0 2.0 │\n\ +/// │ 2.0 2.0 2.0 │\n\ +/// └ ┘" +/// ); +/// Ok(()) +/// } +/// ``` +pub fn vec_dyad_vec(a: &mut Tensor2, alpha: f64, u: &Vector, v: &Vector) -> Result<(), StrError> { + let dim = a.vec.dim(); + if dim == 4 { + assert_eq!(v.dim(), 2); + assert_eq!(u.dim(), 2); + if (u[0] * v[1]) != (u[1] * v[0]) { + return Err("dyadic product between u and v does not generate a symmetric tensor"); + } + a.vec[0] = alpha * u[0] * v[0]; + a.vec[1] = alpha * u[1] * v[1]; + a.vec[2] = 0.0; + a.vec[3] = alpha * (u[0] * v[1] + u[1] * v[0]) / SQRT_2; + } else { + assert_eq!(v.dim(), 3); + assert_eq!(u.dim(), 3); + a.vec[0] = alpha * u[0] * v[0]; + a.vec[1] = alpha * u[1] * v[1]; + a.vec[2] = alpha * u[2] * v[2]; + a.vec[3] = alpha * (u[0] * v[1] + u[1] * v[0]) / SQRT_2; + a.vec[4] = alpha * (u[1] * v[2] + u[2] * v[1]) / SQRT_2; + a.vec[5] = alpha * (u[0] * v[2] + u[2] * v[0]) / SQRT_2; + if dim == 6 { + if (u[0] * v[1]) != (u[1] * v[0]) || (u[1] * v[2]) != (u[2] * v[1]) || (u[0] * v[2]) != (u[2] * v[0]) { + return Err("dyadic product between u and v does not generate a symmetric tensor"); + } + } else { + a.vec[6] = alpha * (u[0] * v[1] - u[1] * v[0]) / SQRT_2; + a.vec[7] = alpha * (u[1] * v[2] - u[2] * v[1]) / SQRT_2; + a.vec[8] = alpha * (u[0] * v[2] - u[2] * v[0]) / SQRT_2; + } + } + Ok(()) +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#[cfg(test)] +mod tests { + use super::*; + use crate::Mandel; + use russell_lab::{approx_eq, vec_approx_eq}; + + #[test] + #[should_panic] + fn t2_ddot_t2_panics_on_different_mandel() { + let a = Tensor2::new(Mandel::Symmetric); + let b = Tensor2::new(Mandel::General); + t2_ddot_t2(&a, &b); + } + + #[test] + fn t2_ddot_t2_works() { + // general : general + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 2.0, 3.0], + [4.0, 5.0, 6.0], + [7.0, 8.0, 9.0], + ], Mandel::General).unwrap(); + #[rustfmt::skip] + let b = Tensor2::from_matrix(&[ + [9.0, 8.0, 7.0], + [6.0, 5.0, 4.0], + [3.0, 2.0, 1.0], + ], Mandel::General).unwrap(); + let s = t2_ddot_t2(&a, &b); + assert_eq!(s, 165.0); + + // sym-3D : sym-3D + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 4.0, 6.0], + [4.0, 2.0, 5.0], + [6.0, 5.0, 3.0], + ], Mandel::Symmetric).unwrap(); + #[rustfmt::skip] + let b = Tensor2::from_matrix(&[ + [3.0, 5.0, 6.0], + [5.0, 2.0, 4.0], + [6.0, 4.0, 1.0], + ], Mandel::Symmetric).unwrap(); + let s = t2_ddot_t2(&a, &b); + approx_eq(s, 162.0, 1e-13); + + // sym-2D : sym-2D + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 4.0, 0.0], + [4.0, 2.0, 0.0], + [0.0, 0.0, 3.0], + ], Mandel::Symmetric2D).unwrap(); + #[rustfmt::skip] + let b = Tensor2::from_matrix(&[ + [3.0, 5.0, 0.0], + [5.0, 2.0, 0.0], + [0.0, 0.0, 1.0], + ], Mandel::Symmetric2D).unwrap(); + let s = t2_ddot_t2(&a, &b); + approx_eq(s, 50.0, 1e-13); + } + + #[test] + #[should_panic] + fn t2_dot_t2_panics_on_non_general() { + let a = Tensor2::new(Mandel::Symmetric); + let b = Tensor2::new(Mandel::Symmetric); + let mut c = Tensor2::new(Mandel::Symmetric); // wrong; it must be General + t2_dot_t2(&mut c, &a, &b); + } + + #[test] + #[should_panic] + fn t2_dot_t2_panics_on_different_mandel() { + let a = Tensor2::new(Mandel::Symmetric); + let b = Tensor2::new(Mandel::General); // wrong; it must be the same as `a` + let mut c = Tensor2::new(Mandel::General); + t2_dot_t2(&mut c, &a, &b); + } + + #[test] + fn t2_dot_t2_works() { + // general . general + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 2.0, 3.0], + [4.0, 5.0, 6.0], + [7.0, 8.0, 9.0], + ], Mandel::General).unwrap(); + #[rustfmt::skip] + let b = Tensor2::from_matrix(&[ + [9.0, 8.0, 7.0], + [6.0, 5.0, 4.0], + [3.0, 2.0, 1.0], + ], Mandel::General).unwrap(); + let mut c = Tensor2::new(Mandel::General); + t2_dot_t2(&mut c, &a, &b); + #[rustfmt::skip] + let correct = Tensor2::from_matrix(&[ + [ 30.0, 24.0, 18.0], + [ 84.0, 69.0, 54.0], + [138.0, 114.0, 90.0], + ], Mandel::General).unwrap(); + vec_approx_eq(&c.vec, &correct.vec, 1e-13); + + // sym-3D . sym-3D + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 4.0, 6.0], + [4.0, 2.0, 5.0], + [6.0, 5.0, 3.0], + ], Mandel::Symmetric).unwrap(); + #[rustfmt::skip] + let b = Tensor2::from_matrix(&[ + [3.0, 5.0, 6.0], + [5.0, 2.0, 4.0], + [6.0, 4.0, 1.0], + ], Mandel::Symmetric).unwrap(); + let mut c = Tensor2::new(Mandel::General); + t2_dot_t2(&mut c, &a, &b); + #[rustfmt::skip] + let correct = Tensor2::from_matrix(&[ + [59.0, 37.0, 28.0], + [52.0, 44.0, 37.0], + [61.0, 52.0, 59.0], + ], Mandel::General).unwrap(); + vec_approx_eq(&c.vec, &correct.vec, 1e-13); + + // sym-2D . sym-2D + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 4.0, 0.0], + [4.0, 2.0, 0.0], + [0.0, 0.0, 3.0], + ], Mandel::Symmetric2D).unwrap(); + #[rustfmt::skip] + let b = Tensor2::from_matrix(&[ + [3.0, 5.0, 0.0], + [5.0, 2.0, 0.0], + [0.0, 0.0, 1.0], + ], Mandel::Symmetric2D).unwrap(); + let mut c = Tensor2::new(Mandel::General); + t2_dot_t2(&mut c, &a, &b); + #[rustfmt::skip] + let correct = Tensor2::from_matrix(&[ + [23.0, 13.0, 0.0], + [22.0, 24.0, 0.0], + [ 0.0, 0.0, 3.0], + ], Mandel::General).unwrap(); + vec_approx_eq(&c.vec, &correct.vec, 1e-13); + } + + #[test] + #[should_panic] + fn t2_dot_vec_panics_on_non_2d_vector_v() { + let mut v = Vector::new(3); // wrong; it must be 2 + let a = Tensor2::new(Mandel::Symmetric2D); + let u = Vector::new(2); + t2_dot_vec(&mut v, 1.0, &a, &u); + } + + #[test] + #[should_panic] + fn t2_dot_vec_panics_on_non_2d_vector_u() { + let mut v = Vector::new(2); + let a = Tensor2::new(Mandel::Symmetric2D); + let u = Vector::new(3); // wrong; it must be 2 + t2_dot_vec(&mut v, 1.0, &a, &u); + } + + #[test] + #[should_panic] + fn t2_dot_vec_panics_on_non_3d_vector_v() { + let mut v = Vector::new(2); // wrong; it must be 3 + let a = Tensor2::new(Mandel::General); + let u = Vector::new(3); + t2_dot_vec(&mut v, 1.0, &a, &u); + } + + #[test] + #[should_panic] + fn t2_dot_vec_panics_on_non_3d_vector_u() { + let mut v = Vector::new(3); + let a = Tensor2::new(Mandel::General); + let u = Vector::new(2); // wrong; it must be 3 + t2_dot_vec(&mut v, 1.0, &a, &u); + } + + #[test] + fn t2_dot_vec_works() { + // general . vec + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 2.0, 3.0], + [4.0, 5.0, 6.0], + [7.0, 8.0, 9.0], + ], Mandel::General).unwrap(); + let u = Vector::from(&[-2.0, -3.0, -4.0]); + let mut v = Vector::new(3); + t2_dot_vec(&mut v, 2.0, &a, &u); + vec_approx_eq(&v, &[-40.0, -94.0, -148.0], 1e-13); + + // sym-3D . vec + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 2.0, 3.0], + [2.0, 5.0, 6.0], + [3.0, 6.0, 9.0], + ], Mandel::Symmetric).unwrap(); + let u = Vector::from(&[-2.0, -3.0, -4.0]); + let mut v = Vector::new(3); + t2_dot_vec(&mut v, 2.0, &a, &u); + vec_approx_eq(&v, &[-40.0, -86.0, -120.0], 1e-13); + + // sym-2D . vec + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 2.0, 0.0], + [2.0, 5.0, 0.0], + [0.0, 0.0, 9.0], + ], Mandel::Symmetric2D).unwrap(); + let u = Vector::from(&[-2.0, -3.0]); + let mut v = Vector::new(2); + t2_dot_vec(&mut v, 2.0, &a, &u); + vec_approx_eq(&v, &[-16.0, -38.0], 1e-13); + } + + #[test] + #[should_panic] + fn vec_dot_t2_panics_on_non_2d_vector_v() { + let mut v = Vector::new(3); // wrong; it must be 2 + let a = Tensor2::new(Mandel::Symmetric2D); + let u = Vector::new(2); + vec_dot_t2(&mut v, 1.0, &u, &a); + } + + #[test] + #[should_panic] + fn vec_dot_t2_panics_on_non_2d_vector_u() { + let mut v = Vector::new(2); + let a = Tensor2::new(Mandel::Symmetric2D); + let u = Vector::new(3); // wrong; it must be 2 + vec_dot_t2(&mut v, 1.0, &u, &a); + } + + #[test] + #[should_panic] + fn vec_dot_t2_panics_on_non_3d_vector_v() { + let mut v = Vector::new(2); // wrong; it must be 3 + let a = Tensor2::new(Mandel::General); + let u = Vector::new(3); + vec_dot_t2(&mut v, 1.0, &u, &a); + } + + #[test] + #[should_panic] + fn vec_dot_t2_panics_on_non_3d_vector_u() { + let mut v = Vector::new(3); + let a = Tensor2::new(Mandel::General); + let u = Vector::new(2); // wrong; it must be 3 + vec_dot_t2(&mut v, 1.0, &u, &a); + } + + #[test] + fn vec_dot_t2_works() { + // general . vec + let u = Vector::from(&[-2.0, -3.0, -4.0]); + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 2.0, 3.0], + [4.0, 5.0, 6.0], + [7.0, 8.0, 9.0], + ], Mandel::General).unwrap(); + let mut v = Vector::new(3); + vec_dot_t2(&mut v, 2.0, &u, &a); + vec_approx_eq(&v, &[-84.0, -102.0, -120.0], 1e-13); + + // sym-3D . vec + let u = Vector::from(&[-2.0, -3.0, -4.0]); + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 2.0, 3.0], + [2.0, 5.0, 6.0], + [3.0, 6.0, 9.0], + ], Mandel::Symmetric).unwrap(); + let mut v = Vector::new(3); + vec_dot_t2(&mut v, 2.0, &u, &a); + vec_approx_eq(&v, &[-40.0, -86.0, -120.0], 1e-13); + + // sym-2D . vec + let u = Vector::from(&[-2.0, -3.0]); + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 2.0, 0.0], + [2.0, 5.0, 0.0], + [0.0, 0.0, 9.0], + ], Mandel::Symmetric2D).unwrap(); + let mut v = Vector::new(2); + vec_dot_t2(&mut v, 2.0, &u, &a); + vec_approx_eq(&v, &[-16.0, -38.0], 1e-13); + } + + #[test] + #[should_panic] + fn vec_dyad_vec_panics_on_non_2d_vector_u() { + let mut a = Tensor2::new(Mandel::Symmetric2D); + let u = Vector::new(3); // wrong; it must be 2 + let v = Vector::new(2); + let _ = vec_dyad_vec(&mut a, 1.0, &u, &v); + } + + #[test] + #[should_panic] + fn vec_dyad_vec_panics_on_non_2d_vector_v() { + let mut a = Tensor2::new(Mandel::Symmetric2D); + let u = Vector::new(2); + let v = Vector::new(3); // wrong; it must be 2 + let _ = vec_dyad_vec(&mut a, 1.0, &u, &v); + } + + #[test] + #[should_panic] + fn vec_dyad_vec_panics_on_non_3d_vector_u() { + let mut a = Tensor2::new(Mandel::General); + let u = Vector::new(2); // wrong; it must be 3 + let v = Vector::new(3); + let _ = vec_dyad_vec(&mut a, 1.0, &u, &v); + } + + #[test] + #[should_panic] + fn vec_dyad_vec_panics_on_non_3d_vector_v() { + let mut a = Tensor2::new(Mandel::General); + let u = Vector::new(3); + let v = Vector::new(2); // wrong; it must be 3 + let _ = vec_dyad_vec(&mut a, 1.0, &u, &v); + } + + #[test] + fn vec_dyad_vec_captures_errors() { + // symmetric 2D + let mut tt = Tensor2::new(Mandel::Symmetric2D); + let u = Vector::from(&[-2.0, -3.0]); + let v = Vector::from(&[4.0, 3.0]); + assert_eq!( + vec_dyad_vec(&mut tt, 1.0, &u, &v).err(), + Some("dyadic product between u and v does not generate a symmetric tensor") + ); + // symmetric 3D + let u = Vector::from(&[-2.0, -3.0, -4.0]); + let v = Vector::from(&[4.0, 3.0, 2.0]); + let mut tt = Tensor2::new(Mandel::Symmetric); + assert_eq!( + vec_dyad_vec(&mut tt, 1.0, &u, &v).err(), + Some("dyadic product between u and v does not generate a symmetric tensor") + ); + } + + #[test] + fn vec_dyad_vec_works() { + // general + let u = Vector::from(&[-2.0, -3.0, -4.0]); + let v = Vector::from(&[4.0, 3.0, 2.0]); + let mut tt = Tensor2::new(Mandel::General); + vec_dyad_vec(&mut tt, 2.0, &u, &v).unwrap(); + let correct = &[ + -16.0, + -18.0, + -16.0, + -18.0 * SQRT_2, + -18.0 * SQRT_2, + -20.0 * SQRT_2, + 6.0 * SQRT_2, + 6.0 * SQRT_2, + 12.0 * SQRT_2, + ]; + vec_approx_eq(&tt.vec, correct, 1e-14); + + // symmetric 3D + let u = Vector::from(&[-2.0, -3.0, -4.0]); + let v = Vector::from(&[2.0, 3.0, 4.0]); + let mut tt = Tensor2::new(Mandel::Symmetric); + vec_dyad_vec(&mut tt, 2.0, &u, &v).unwrap(); + let correct = &[-8.0, -18.0, -32.0, -12.0 * SQRT_2, -24.0 * SQRT_2, -16.0 * SQRT_2]; + vec_approx_eq(&tt.vec, correct, 1e-14); + + // symmetric 2D + let u = Vector::from(&[-2.0, -3.0]); + let v = Vector::from(&[2.0, 3.0]); + let mut tt = Tensor2::new(Mandel::Symmetric2D); + vec_dyad_vec(&mut tt, 2.0, &u, &v).unwrap(); + let correct = &[-8.0, -18.0, 0.0, -12.0 * SQRT_2]; + vec_approx_eq(&tt.vec, correct, 1e-14); + } +} diff --git a/russell_tensor/src/operations_t4.rs b/russell_tensor/src/operations_t4.rs new file mode 100644 index 00000000..ce50d0f2 --- /dev/null +++ b/russell_tensor/src/operations_t4.rs @@ -0,0 +1,235 @@ +use super::Tensor4; +use russell_lab::mat_mat_mul; + +#[allow(unused)] +use crate::Mandel; // for documentation + +/// Performs the double-dot (ddot) operation between two Tensor4 +/// +/// Computes: +/// +/// ```text +/// E = α C : D +/// ``` +/// +/// With orthonormal Cartesian components: +/// +/// ```text +/// Eᵢⱼₖₗ = α Σ Σ Cᵢⱼₛₜ : Dₛₜₖₗ +/// s t +/// ``` +/// +/// Or, in Mandel basis: +/// +/// ```text +/// Eₘₙ = α Σ Cₘₐ Dₐₙ +/// m +/// ``` +/// +/// # Output +/// +/// * `ee` -- the resulting fourth-order tensor; with the same [Mandel] as `cc` and `dd` +/// +/// # Input +/// +/// * `alpha` -- the scalar multiplier +/// * `a` -- the input second-order tensor; with the same [Mandel] as `b` and `dd` +/// * `dd` -- the fourth-order tensor; with the same [Mandel] as `a` and `b` +/// +/// # Panics +/// +/// A panic will occur the tensors have different [Mandel] +/// +/// # Examples +/// +/// ``` +/// use russell_lab::approx_eq; +/// use russell_tensor::{Mandel, t4_ddot_t4, StrError, Tensor4}; +/// +/// fn main() -> Result<(), StrError> { +/// let cc = Tensor4::from_matrix( +/// &[ +/// [1.0, 2.0, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], +/// [1.0, 1.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], +/// [1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], +/// [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0], +/// [0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0], +/// [0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0], +/// [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0], +/// [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0], +/// [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0], +/// ], +/// Mandel::General, +/// )?; +/// +/// let dd = Tensor4::from_matrix( +/// &[ +/// [-1.0, 1.0 / 3.0, 5.0 / 3.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], +/// [1.0, -2.0 / 3.0, -1.0 / 3.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], +/// [0.0, 1.0 / 3.0, -1.0 / 3.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], +/// [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0], +/// [0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0], +/// [0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0], +/// [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0], +/// [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0], +/// [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0], +/// ], +/// Mandel::General, +/// )?; +/// +/// let mut ee = Tensor4::new(Mandel::General); +/// t4_ddot_t4(&mut ee, 1.0, &cc, &dd); +/// +/// let out = ee.as_matrix(); +/// for i in 0..9 { +/// for j in 0..9 { +/// if i == j { +/// approx_eq(out.get(i, j), 1.0, 1e-15); +/// } else { +/// approx_eq(out.get(i, j), 0.0, 1e-15); +/// } +/// } +/// } +/// Ok(()) +/// } +/// ``` +pub fn t4_ddot_t4(ee: &mut Tensor4, alpha: f64, cc: &Tensor4, dd: &Tensor4) { + assert_eq!(cc.mandel, dd.mandel); + assert_eq!(ee.mandel, dd.mandel); + mat_mat_mul(&mut ee.mat, alpha, &cc.mat, &dd.mat, 0.0).unwrap(); +} + +/// Performs the double-dot (ddot) operation between two Tensor4 with update +/// +/// Computes: +/// +/// ```text +/// E = α C : D + β E +/// ``` +/// +/// With orthonormal Cartesian components: +/// +/// ```text +/// Eᵢⱼₖₗ = α (Σ Σ Cᵢⱼₛₜ : Dₛₜₖₗ) + β Eᵢⱼₖₗ +/// s t +/// ``` +/// +/// Or, in Mandel basis: +/// +/// ```text +/// Eₘₙ = α (Σ Cₘₐ Dₐₙ) + β Eₘₙ +/// m +/// ``` +/// +/// # Output +/// +/// * `ee` -- the resulting fourth-order tensor; with the same [Mandel] as `cc` and `dd` +/// +/// # Input +/// +/// * `alpha` -- the scalar multiplier +/// * `a` -- the input second-order tensor; with the same [Mandel] as `b` and `dd` +/// * `dd` -- the fourth-order tensor; with the same [Mandel] as `a` and `b` +/// * `beta` -- the other scalar multiplier +/// +/// # Panics +/// +/// A panic will occur the tensors have different [Mandel] +pub fn t4_ddot_t4_update(ee: &mut Tensor4, alpha: f64, cc: &Tensor4, dd: &Tensor4, beta: f64) { + assert_eq!(cc.mandel, dd.mandel); + assert_eq!(ee.mandel, dd.mandel); + mat_mat_mul(&mut ee.mat, alpha, &cc.mat, &dd.mat, beta).unwrap(); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#[cfg(test)] +mod tests { + use super::*; + use crate::{Mandel, SamplesTensor4}; + use russell_lab::Matrix; + + #[test] + #[should_panic] + fn t4_ddot_t4_panics_on_different_mandel1() { + let cc = Tensor4::new(Mandel::Symmetric); // wrong; it must be the same as `dd` + let dd = Tensor4::new(Mandel::Symmetric2D); + let mut ee = Tensor4::new(Mandel::Symmetric2D); + t4_ddot_t4(&mut ee, 1.0, &cc, &dd); + } + + #[test] + #[should_panic] + fn t4_ddot_t4_panics_on_different_mandel2() { + let cc = Tensor4::new(Mandel::Symmetric2D); + let dd = Tensor4::new(Mandel::Symmetric); // wrong; it must be the same as `dd` + let mut ee = Tensor4::new(Mandel::Symmetric2D); + t4_ddot_t4(&mut ee, 1.0, &cc, &dd); + } + + #[test] + fn t4_ddot_t4_works() { + let cc = Tensor4::from_matrix(&SamplesTensor4::SYM_2D_SAMPLE1_STD_MATRIX, Mandel::Symmetric2D).unwrap(); + let mut ee = Tensor4::new(Mandel::Symmetric2D); + t4_ddot_t4(&mut ee, 2.0, &cc, &cc); + let out = ee.as_matrix(); + assert_eq!( + format!("{:.1}", out), + "┌ ┐\n\ + │ 820.0 872.0 924.0 1288.0 0.0 0.0 1288.0 0.0 0.0 │\n\ + │ 1120.0 1202.0 1284.0 1858.0 0.0 0.0 1858.0 0.0 0.0 │\n\ + │ 1420.0 1532.0 1644.0 2428.0 0.0 0.0 2428.0 0.0 0.0 │\n\ + │ 2620.0 2852.0 3084.0 4708.0 0.0 0.0 4708.0 0.0 0.0 │\n\ + │ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 │\n\ + │ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 │\n\ + │ 2620.0 2852.0 3084.0 4708.0 0.0 0.0 4708.0 0.0 0.0 │\n\ + │ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 │\n\ + │ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 │\n\ + └ ┘" + ); + } + + #[test] + #[should_panic] + fn t4_ddot_t4_update_panics_on_different_mandel1() { + let cc = Tensor4::new(Mandel::Symmetric); // wrong; it must be the same as `dd` + let dd = Tensor4::new(Mandel::Symmetric2D); + let mut ee = Tensor4::new(Mandel::Symmetric2D); + t4_ddot_t4(&mut ee, 1.0, &cc, &dd); + } + + #[test] + #[should_panic] + fn t4_ddot_t4_update_panics_on_different_mandel2() { + let cc = Tensor4::new(Mandel::Symmetric2D); + let dd = Tensor4::new(Mandel::Symmetric); // wrong; it must be the same as `dd` + let mut ee = Tensor4::new(Mandel::Symmetric2D); + t4_ddot_t4(&mut ee, 1.0, &cc, &dd); + } + + #[test] + fn t4_ddot_t4_update_works() { + let cc = Tensor4::from_matrix(&SamplesTensor4::SYM_2D_SAMPLE1_STD_MATRIX, Mandel::Symmetric2D).unwrap(); + let mut mat = Matrix::new(9, 9); + mat.set(0, 0, 0.1); + mat.set(1, 1, 0.1); + mat.set(2, 2, 0.1); + let mut ee = Tensor4::from_matrix(&mat, Mandel::Symmetric2D).unwrap(); + t4_ddot_t4_update(&mut ee, 2.0, &cc, &cc, 2.0); + let out = ee.as_matrix(); + assert_eq!( + format!("{:.1}", out), + "┌ ┐\n\ + │ 820.2 872.0 924.0 1288.0 0.0 0.0 1288.0 0.0 0.0 │\n\ + │ 1120.0 1202.2 1284.0 1858.0 0.0 0.0 1858.0 0.0 0.0 │\n\ + │ 1420.0 1532.0 1644.2 2428.0 0.0 0.0 2428.0 0.0 0.0 │\n\ + │ 2620.0 2852.0 3084.0 4708.0 0.0 0.0 4708.0 0.0 0.0 │\n\ + │ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 │\n\ + │ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 │\n\ + │ 2620.0 2852.0 3084.0 4708.0 0.0 0.0 4708.0 0.0 0.0 │\n\ + │ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 │\n\ + │ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 │\n\ + └ ┘" + ); + } +} From 873640d5ac901baddb4b8056562262dd5cd6d96a Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Mon, 13 May 2024 00:09:05 +1000 Subject: [PATCH 37/44] Improve plasticity modulus function --- russell_tensor/src/operations_mix1.rs | 99 ++++++++++++++++++++------- 1 file changed, 74 insertions(+), 25 deletions(-) diff --git a/russell_tensor/src/operations_mix1.rs b/russell_tensor/src/operations_mix1.rs index dc088822..0dd28451 100644 --- a/russell_tensor/src/operations_mix1.rs +++ b/russell_tensor/src/operations_mix1.rs @@ -451,21 +451,21 @@ pub fn t2_ddot_t4_ddot_t2(a: &Tensor2, dd: &Tensor4, b: &Tensor2) -> f64 { /// Computes: /// /// ```text -/// E = α (D : a) ⊗ (b : D) +/// E = α D + β (D : a) ⊗ (b : D) /// ``` /// /// With orthonormal Cartesian components: /// /// ```text -/// Eᵢⱼₖₗ = α Σ Σ Σ Σ (Dᵢⱼₛₜ aₛₜ) (bₒₚ Dₒₚₖₗ) -/// s t o p +/// Eᵢⱼₖₗ = α Dᵢⱼₖₗ + β Σ Σ Σ Σ (Dᵢⱼₛₜ aₛₜ) (bₒₚ Dₒₚₖₗ) +/// s t o p /// ``` /// /// Or, in Mandel basis: /// /// ```text -/// Eₘₙ = α Σ Σ (Dₘₐ aₐ) (bₑ Dₑₙ) -/// a e +/// Eₘₙ = α Dₘₙ + β Σ Σ (Dₘₐ aₐ) (bₑ Dₑₙ) +/// a e /// ``` /// /// Note: the elastoplastic modulus in Plasticity needs this operation. @@ -476,26 +476,27 @@ pub fn t2_ddot_t4_ddot_t2(a: &Tensor2, dd: &Tensor4, b: &Tensor2) -> f64 { /// /// # Input /// -/// * `alpha` -- the scalar multiplier +/// * `alpha` -- the first scalar multiplier +/// * `dd` -- the fourth-order tensor; with the same [Mandel] as the other tensors +/// * `beta` -- the second scalar multiplier /// * `a` -- the first second-order tensor; with the same [Mandel] as the other tensors /// * `b` -- the second second-order tensor; with the same [Mandel] as the other tensors -/// * `dd` -- the fourth-order tensor; with the same [Mandel] as the other tensors /// /// # Panics /// /// A panic will occur the tensors have different [Mandel] -pub fn t4_ddot_t2_dyad_t2_ddot_t4(ee: &mut Tensor4, alpha: f64, a: &Tensor2, b: &Tensor2, dd: &Tensor4) { +pub fn t4_ddot_t2_dyad_t2_ddot_t4(ee: &mut Tensor4, alpha: f64, dd: &Tensor4, beta: f64, a: &Tensor2, b: &Tensor2) { assert_eq!(a.mandel, dd.mandel); assert_eq!(b.mandel, dd.mandel); assert_eq!(ee.mandel, dd.mandel); let dim = a.vec.dim(); - ee.mat.fill(0.0); for m in 0..dim { for n in 0..dim { + ee.mat.set(m, n, alpha * dd.mat.get(m, n)); for p in 0..dim { for q in 0..dim { ee.mat - .add(m, n, alpha * dd.mat.get(m, p) * a.vec[p] * b.vec[q] * dd.mat.get(q, n)); + .add(m, n, beta * dd.mat.get(m, p) * a.vec[p] * b.vec[q] * dd.mat.get(q, n)); } } } @@ -953,7 +954,7 @@ mod tests { let b = Tensor2::new(Mandel::Symmetric2D); let dd = Tensor4::new(Mandel::Symmetric2D); let mut ee = Tensor4::new(Mandel::Symmetric2D); - t4_ddot_t2_dyad_t2_ddot_t4(&mut ee, 2.0, &a, &b, &dd); + t4_ddot_t2_dyad_t2_ddot_t4(&mut ee, 2.0, &dd, 3.0, &a, &b); } #[test] @@ -963,7 +964,7 @@ mod tests { let b = Tensor2::new(Mandel::Symmetric); // wrong; it must be the same as `ee` let dd = Tensor4::new(Mandel::Symmetric2D); let mut ee = Tensor4::new(Mandel::Symmetric2D); - t4_ddot_t2_dyad_t2_ddot_t4(&mut ee, 2.0, &a, &b, &dd); + t4_ddot_t2_dyad_t2_ddot_t4(&mut ee, 2.0, &dd, 3.0, &a, &b); } #[test] @@ -973,11 +974,11 @@ mod tests { let b = Tensor2::new(Mandel::Symmetric2D); let dd = Tensor4::new(Mandel::Symmetric); // wrong; it must be the same as `ee` let mut ee = Tensor4::new(Mandel::Symmetric2D); - t4_ddot_t2_dyad_t2_ddot_t4(&mut ee, 2.0, &a, &b, &dd); + t4_ddot_t2_dyad_t2_ddot_t4(&mut ee, 2.0, &dd, 3.0, &a, &b); } #[test] - fn t4_ddot_t2_dyad_t2_ddot_t4_works() { + fn t4_ddot_t2_dyad_t2_ddot_t4_works1() { #[rustfmt::skip] let a = Tensor2::from_matrix(&[ [1.0, 2.0, 3.0], @@ -993,18 +994,66 @@ mod tests { let mat = Matrix::filled(9, 9, -1.0); let dd = Tensor4::from_matrix(&mat, Mandel::General).unwrap(); let mut ee = Tensor4::new(Mandel::General); - t4_ddot_t2_dyad_t2_ddot_t4(&mut ee, 2.0, &a, &b, &dd); + t4_ddot_t2_dyad_t2_ddot_t4(&mut ee, 2.0, &dd, 3.0, &a, &b); + let correct = [ + [6073., 6073., 6073., 6073., 6073., 6073., 6073., 6073., 6073.], + [6073., 6073., 6073., 6073., 6073., 6073., 6073., 6073., 6073.], + [6073., 6073., 6073., 6073., 6073., 6073., 6073., 6073., 6073.], + [6073., 6073., 6073., 6073., 6073., 6073., 6073., 6073., 6073.], + [6073., 6073., 6073., 6073., 6073., 6073., 6073., 6073., 6073.], + [6073., 6073., 6073., 6073., 6073., 6073., 6073., 6073., 6073.], + [6073., 6073., 6073., 6073., 6073., 6073., 6073., 6073., 6073.], + [6073., 6073., 6073., 6073., 6073., 6073., 6073., 6073., 6073.], + [6073., 6073., 6073., 6073., 6073., 6073., 6073., 6073., 6073.], + ]; + mat_approx_eq(&ee.as_matrix(), &correct, 1e-15); + } + + /* + #[test] + fn t4_ddot_t2_dyad_t2_ddot_t4_works2() { + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 2.0, 3.0], + [2.0, 5.0, 6.0], + [3.0, 6.0, 9.0], + ], Mandel::Symmetric).unwrap(); + #[rustfmt::skip] + let b = Tensor2::from_matrix(&[ + [1.0, 4.0, 5.0], + [4.0, 2.0, 6.0], + [5.0, 6.0, 3.0], + ], Mandel::Symmetric).unwrap(); + let dd = Tensor4::from_matrix( + &[ + [1.0, 2.0, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [2.0, 4.0, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [3.0, 3.0, 5.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + ], + Mandel::Symmetric, + ) + .unwrap(); + let mut ee = Tensor4::new(Mandel::Symmetric); + t4_ddot_t2_dyad_t2_ddot_t4(&mut ee, 2.0, &dd, 3.0, &a, &b); + println!("{}", ee.as_matrix()); let correct = [ - [4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050.], - [4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050.], - [4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050.], - [4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050.], - [4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050.], - [4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050.], - [4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050.], - [4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050.], - [4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050., 4050.], + [6073., 6073., 6073., 6073., 6073., 6073., 6073., 6073., 6073.], + [6073., 6073., 6073., 6073., 6073., 6073., 6073., 6073., 6073.], + [6073., 6073., 6073., 6073., 6073., 6073., 6073., 6073., 6073.], + [6073., 6073., 6073., 6073., 6073., 6073., 6073., 6073., 6073.], + [6073., 6073., 6073., 6073., 6073., 6073., 6073., 6073., 6073.], + [6073., 6073., 6073., 6073., 6073., 6073., 6073., 6073., 6073.], + [6073., 6073., 6073., 6073., 6073., 6073., 6073., 6073., 6073.], + [6073., 6073., 6073., 6073., 6073., 6073., 6073., 6073., 6073.], + [6073., 6073., 6073., 6073., 6073., 6073., 6073., 6073., 6073.], ]; - mat_approx_eq(&ee.as_matrix(), &correct, 1e-11); + mat_approx_eq(&ee.as_matrix(), &correct, 1e-15); } + */ } From c2e965a9b9ef0ae60505963c36c8928c161c4e7b Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Mon, 13 May 2024 09:12:33 +1000 Subject: [PATCH 38/44] Impl mutable access to vec and mat in Tensor2 and Tensor4 --- russell_tensor/src/tensor2.rs | 8 +++++++- russell_tensor/src/tensor4.rs | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/russell_tensor/src/tensor2.rs b/russell_tensor/src/tensor2.rs index dc42761f..22c833f9 100644 --- a/russell_tensor/src/tensor2.rs +++ b/russell_tensor/src/tensor2.rs @@ -165,6 +165,11 @@ impl Tensor2 { &self.vec } + /// Returns a mutable access to the underlying Mandel vector + pub fn vector_mut(&mut self) -> &mut Vector { + &mut self.vec + } + /// Sets the Tensor2 with standard components given in matrix form /// /// # Input @@ -1886,10 +1891,11 @@ mod tests { #[test] fn new_and_getters_work() { // general - let tt = Tensor2::new(Mandel::General); + let mut tt = Tensor2::new(Mandel::General); let correct = &[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]; assert_eq!(tt.mandel(), Mandel::General); assert_eq!(tt.vector().as_data(), correct); + tt.vector_mut()[0] = 1.0; // symmetric 3D let tt = Tensor2::new(Mandel::Symmetric); diff --git a/russell_tensor/src/tensor4.rs b/russell_tensor/src/tensor4.rs index d3a3a0be..262c141e 100644 --- a/russell_tensor/src/tensor4.rs +++ b/russell_tensor/src/tensor4.rs @@ -174,6 +174,11 @@ impl Tensor4 { &self.mat } + /// Returns a mutable access to the underlying Mandel matrix + pub fn matrix_mut(&mut self) -> &mut Matrix { + &mut self.mat + } + /// Creates a new Tensor4 constructed from a nested array /// /// # Input @@ -1318,9 +1323,10 @@ mod tests { #[test] fn new_and_getters_work() { // general - let dd = Tensor4::new(Mandel::General); + let mut dd = Tensor4::new(Mandel::General); assert_eq!(dd.matrix().as_data().len(), 81); assert_eq!(dd.mandel(), Mandel::General); + dd.matrix_mut().set(0, 0, 1.0); // symmetric let dd = Tensor4::new(Mandel::Symmetric); From 2276be36afeb2edd6f9508de8d31a68f78c48f19 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Mon, 13 May 2024 10:43:15 +1000 Subject: [PATCH 39/44] Impl dim methods of Tensor2 and Tensor4 --- russell_tensor/src/tensor2.rs | 12 ++++++++++++ russell_tensor/src/tensor4.rs | 16 ++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/russell_tensor/src/tensor2.rs b/russell_tensor/src/tensor2.rs index 22c833f9..a54c79bb 100644 --- a/russell_tensor/src/tensor2.rs +++ b/russell_tensor/src/tensor2.rs @@ -160,6 +160,11 @@ impl Tensor2 { self.mandel } + /// Returns the Mandel vector dimension (4, 6, or 9) + pub fn dim(&self) -> usize { + self.vec.dim() + } + /// Returns an access to the underlying Mandel vector pub fn vector(&self) -> &Vector { &self.vec @@ -1894,6 +1899,7 @@ mod tests { let mut tt = Tensor2::new(Mandel::General); let correct = &[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]; assert_eq!(tt.mandel(), Mandel::General); + assert_eq!(tt.dim(), 9); assert_eq!(tt.vector().as_data(), correct); tt.vector_mut()[0] = 1.0; @@ -1901,28 +1907,34 @@ mod tests { let tt = Tensor2::new(Mandel::Symmetric); let correct = &[0.0, 0.0, 0.0, 0.0, 0.0, 0.0]; assert_eq!(tt.mandel(), Mandel::Symmetric); + assert_eq!(tt.dim(), 6); assert_eq!(tt.vector().as_data(), correct); let tt = Tensor2::new_sym(false); assert_eq!(tt.mandel(), Mandel::Symmetric); + assert_eq!(tt.dim(), 6); assert_eq!(tt.vector().as_data(), correct); let tt = Tensor2::new_sym_ndim(3); assert_eq!(tt.mandel(), Mandel::Symmetric); + assert_eq!(tt.dim(), 6); assert_eq!(tt.vector().as_data(), correct); // symmetric 2D let tt = Tensor2::new(Mandel::Symmetric2D); let correct = &[0.0, 0.0, 0.0, 0.0]; assert_eq!(tt.mandel(), Mandel::Symmetric2D); + assert_eq!(tt.dim(), 4); assert_eq!(tt.vector().as_data(), correct); let tt = Tensor2::new_sym(true); assert_eq!(tt.mandel(), Mandel::Symmetric2D); + assert_eq!(tt.dim(), 4); assert_eq!(tt.vector().as_data(), correct); let tt = Tensor2::new_sym_ndim(2); assert_eq!(tt.mandel(), Mandel::Symmetric2D); + assert_eq!(tt.dim(), 4); assert_eq!(tt.vector().as_data(), correct); } diff --git a/russell_tensor/src/tensor4.rs b/russell_tensor/src/tensor4.rs index 262c141e..daf91a4e 100644 --- a/russell_tensor/src/tensor4.rs +++ b/russell_tensor/src/tensor4.rs @@ -169,6 +169,11 @@ impl Tensor4 { self.mandel } + /// Returns the Mandel matrix dimension (4, 6, or 9) + pub fn dim(&self) -> usize { + self.mat.dims().0 + } + /// Returns an access to the underlying Mandel matrix pub fn matrix(&self) -> &Matrix { &self.mat @@ -1325,29 +1330,40 @@ mod tests { // general let mut dd = Tensor4::new(Mandel::General); assert_eq!(dd.matrix().as_data().len(), 81); + assert_eq!(dd.dim(), 9); assert_eq!(dd.mandel(), Mandel::General); dd.matrix_mut().set(0, 0, 1.0); // symmetric let dd = Tensor4::new(Mandel::Symmetric); assert_eq!(dd.mandel(), Mandel::Symmetric); + assert_eq!(dd.dim(), 6); assert_eq!(dd.matrix().as_data().len(), 36); + let dd = Tensor4::new_sym(false); assert_eq!(dd.mandel(), Mandel::Symmetric); + assert_eq!(dd.dim(), 6); assert_eq!(dd.matrix().as_data().len(), 36); + let dd = Tensor4::new_sym_ndim(3); assert_eq!(dd.mandel(), Mandel::Symmetric); + assert_eq!(dd.dim(), 6); assert_eq!(dd.matrix().as_data().len(), 36); // symmetric 2d let dd = Tensor4::new(Mandel::Symmetric2D); assert_eq!(dd.mandel(), Mandel::Symmetric2D); + assert_eq!(dd.dim(), 4); assert_eq!(dd.matrix().as_data().len(), 16); + let dd = Tensor4::new_sym(true); assert_eq!(dd.mandel(), Mandel::Symmetric2D); + assert_eq!(dd.dim(), 4); assert_eq!(dd.matrix().as_data().len(), 16); + let dd = Tensor4::new_sym_ndim(2); assert_eq!(dd.mandel(), Mandel::Symmetric2D); + assert_eq!(dd.dim(), 4); assert_eq!(dd.matrix().as_data().len(), 16); } From f32472bd98b5771f7207519e620a423267b00a7b Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Mon, 13 May 2024 12:12:37 +1000 Subject: [PATCH 40/44] Impl tensor add for Tensor2 and Tensor4 --- russell_tensor/src/operations_t2.rs | 62 ++++++++++++++++++++++++++++- russell_tensor/src/operations_t4.rs | 60 +++++++++++++++++++++++++++- 2 files changed, 118 insertions(+), 4 deletions(-) diff --git a/russell_tensor/src/operations_t2.rs b/russell_tensor/src/operations_t2.rs index 771cfe2e..6520ca9c 100644 --- a/russell_tensor/src/operations_t2.rs +++ b/russell_tensor/src/operations_t2.rs @@ -1,8 +1,23 @@ use super::Tensor2; use crate::{Mandel, SQRT_2}; -use russell_lab::vec_inner; +use russell_lab::{vec_add, vec_inner}; use russell_lab::{StrError, Vector}; +/// Adds two second-order tensors +/// +/// ```text +/// c := α⋅a + β⋅b +/// ``` +/// +/// # Panics +/// +/// A panic will occur the tensors have different [Mandel] +pub fn t2_add(c: &mut Tensor2, alpha: f64, a: &Tensor2, beta: f64, b: &Tensor2) { + assert_eq!(b.mandel, a.mandel); + assert_eq!(c.mandel, a.mandel); + vec_add(&mut c.vec, alpha, &a.vec, beta, &b.vec).unwrap(); +} + /// Performs the double-dot (ddot) operation between two Tensor2 (inner product) /// /// Computes: @@ -414,7 +429,50 @@ pub fn vec_dyad_vec(a: &mut Tensor2, alpha: f64, u: &Vector, v: &Vector) -> Resu mod tests { use super::*; use crate::Mandel; - use russell_lab::{approx_eq, vec_approx_eq}; + use russell_lab::{approx_eq, mat_approx_eq, vec_approx_eq}; + + #[test] + #[should_panic] + fn t2_add_panics_on_different_mandel1() { + let a = Tensor2::new(Mandel::Symmetric2D); + let b = Tensor2::new(Mandel::Symmetric); // wrong; it must be the same as `a` + let mut c = Tensor2::new(Mandel::Symmetric2D); + t2_add(&mut c, 2.0, &a, 3.0, &b); + } + + #[test] + #[should_panic] + fn t2_add_panics_on_different_mandel2() { + let a = Tensor2::new(Mandel::Symmetric2D); + let b = Tensor2::new(Mandel::Symmetric2D); + let mut c = Tensor2::new(Mandel::Symmetric); // wrong; it must be the same as `a` + t2_add(&mut c, 2.0, &a, 3.0, &b); + } + + #[test] + fn t2_add_works() { + #[rustfmt::skip] + let a = Tensor2::from_matrix(&[ + [1.0, 4.0, 0.0], + [4.0, 2.0, 0.0], + [0.0, 0.0, 3.0], + ], Mandel::Symmetric2D).unwrap(); + #[rustfmt::skip] + let b = Tensor2::from_matrix(&[ + [3.0, 5.0, 0.0], + [5.0, 2.0, 0.0], + [0.0, 0.0, 1.0], + ], Mandel::Symmetric2D).unwrap(); + let mut c = Tensor2::new(Mandel::Symmetric2D); + t2_add(&mut c, 2.0, &a, 3.0, &b); + #[rustfmt::skip] + let correct = &[ + [11.0, 23.0, 0.0], + [23.0, 10.0, 0.0], + [ 0.0, 0.0, 9.0], + ]; + mat_approx_eq(&c.as_matrix(), correct, 1e-14); + } #[test] #[should_panic] diff --git a/russell_tensor/src/operations_t4.rs b/russell_tensor/src/operations_t4.rs index ce50d0f2..9899e246 100644 --- a/russell_tensor/src/operations_t4.rs +++ b/russell_tensor/src/operations_t4.rs @@ -1,9 +1,24 @@ use super::Tensor4; -use russell_lab::mat_mat_mul; +use russell_lab::{mat_add, mat_mat_mul}; #[allow(unused)] use crate::Mandel; // for documentation +/// Adds two fourth-order tensors +/// +/// ```text +/// c := α⋅a + β⋅b +/// ``` +/// +/// # Panics +/// +/// A panic will occur the tensors have different [Mandel] +pub fn t4_add(c: &mut Tensor4, alpha: f64, a: &Tensor4, beta: f64, b: &Tensor4) { + assert_eq!(b.mandel, a.mandel); + assert_eq!(c.mandel, a.mandel); + mat_add(&mut c.mat, alpha, &a.mat, beta, &b.mat).unwrap(); +} + /// Performs the double-dot (ddot) operation between two Tensor4 /// /// Computes: @@ -147,7 +162,48 @@ pub fn t4_ddot_t4_update(ee: &mut Tensor4, alpha: f64, cc: &Tensor4, dd: &Tensor mod tests { use super::*; use crate::{Mandel, SamplesTensor4}; - use russell_lab::Matrix; + use russell_lab::{mat_approx_eq, Matrix}; + + #[test] + #[should_panic] + fn t4_add_panics_on_different_mandel1() { + let a = Tensor4::new(Mandel::Symmetric2D); + let b = Tensor4::new(Mandel::Symmetric); // wrong; it must be the same as `a` + let mut c = Tensor4::new(Mandel::Symmetric2D); + t4_add(&mut c, 2.0, &a, 3.0, &b); + } + + #[test] + #[should_panic] + fn t4_add_panics_on_different_mandel2() { + let a = Tensor4::new(Mandel::Symmetric2D); + let b = Tensor4::new(Mandel::Symmetric2D); + let mut c = Tensor4::new(Mandel::Symmetric); // wrong; it must be the same as `a` + t4_add(&mut c, 2.0, &a, 3.0, &b); + } + + #[test] + fn t4_add_works() { + let mut a = Tensor4::new(Mandel::Symmetric2D); + let mut b = Tensor4::new(Mandel::Symmetric2D); + let mut c = Tensor4::new(Mandel::Symmetric2D); + a.sym_set(0, 0, 0, 0, 1.0); + b.sym_set(0, 0, 0, 0, 1.0); + t4_add(&mut c, 2.0, &a, 3.0, &b); + #[rustfmt::skip] + let correct = &[ + [5.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + ]; + mat_approx_eq(&c.as_matrix(), correct, 1e-14); + } #[test] #[should_panic] From e6df18c931fef3c4af7a6fe1bf0deac7f223a9e4 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Mon, 13 May 2024 13:34:06 +1000 Subject: [PATCH 41/44] [wip] Impl compliance modulus in LinElasticity --- russell_tensor/src/lin_elasticity.rs | 115 ++++++++++++++++++++------- 1 file changed, 87 insertions(+), 28 deletions(-) diff --git a/russell_tensor/src/lin_elasticity.rs b/russell_tensor/src/lin_elasticity.rs index 3d3b3e54..5ec46f33 100644 --- a/russell_tensor/src/lin_elasticity.rs +++ b/russell_tensor/src/lin_elasticity.rs @@ -1,30 +1,51 @@ use crate::{t4_ddot_t2, Mandel, StrError, Tensor2, Tensor4}; +use russell_lab::mat_inverse; /// Implements the linear elasticity equations for small-strain problems pub struct LinElasticity { - /// Young's modulus + /// Holds the Young's modulus young: f64, - /// Poisson's coefficient + /// Holds the Poisson's coefficient poisson: f64, - /// Plane-stress flag + /// Holds the Mandel representation enum + mandel: Mandel, + + /// Holds the plane-stress flag plane_stress: bool, - /// Elasticity modulus (on Mandel basis) such that σ = D : ε + /// Holds the elastic rigiDity (stiffness) modulus + /// + /// The rigiDity modulus `D` is such that: + /// + /// ```text + /// σ = D : ε + /// ``` dd: Tensor4, + + /// Holds the elastic Compliance modulus + /// + /// The Compliance modulus `C` is such that: + /// + /// ```text + /// ε = C : σ + /// ``` + /// + /// The compliance modulus is calculate as `C = D⁻¹` + cc: Option, } impl LinElasticity { - /// Creates a new linear-elasticity structure + /// Allocates a new instance /// /// # Input /// /// * `young` -- Young's modulus /// * `poisson` -- Poisson's coefficient /// * `two_dim` -- 2D instead of 3D - /// * `plane_stress` -- if `two_dim == 2`, specifies a Plane-Stress problem. - /// Note: if true, this flag automatically turns `two_dim` to true. + /// * `plane_stress` -- specifies a Plane-Stress problem and + /// automatically set `two_dim` as appropriate. /// /// # Examples /// @@ -33,9 +54,9 @@ impl LinElasticity { /// /// // 3D /// let ela = LinElasticity::new(900.0, 0.25, false, false); - /// let out = ela.get_modulus().as_matrix(); + /// let dd = ela.get_rigidity().as_matrix(); /// assert_eq!( - /// format!("{}", out), + /// format!("{}", dd), /// "┌ ┐\n\ /// │ 1080 360 360 0 0 0 0 0 0 │\n\ /// │ 360 1080 360 0 0 0 0 0 0 │\n\ @@ -51,9 +72,9 @@ impl LinElasticity { /// /// // 2D plane-strain /// let ela = LinElasticity::new(900.0, 0.25, true, false); - /// let out = ela.get_modulus().as_matrix(); + /// let dd = ela.get_rigidity().as_matrix(); /// assert_eq!( - /// format!("{}", out), + /// format!("{}", dd), /// "┌ ┐\n\ /// │ 1080 360 360 0 0 0 0 0 0 │\n\ /// │ 360 1080 360 0 0 0 0 0 0 │\n\ @@ -69,9 +90,9 @@ impl LinElasticity { /// /// // 2D plane-stress /// let ela = LinElasticity::new(3000.0, 0.2, false, true); - /// let out = ela.get_modulus().as_matrix(); + /// let dd = ela.get_rigidity().as_matrix(); /// assert_eq!( - /// format!("{}", out), + /// format!("{}", dd), /// "┌ ┐\n\ /// │ 3125 625 0 0 0 0 0 0 0 │\n\ /// │ 625 3125 0 0 0 0 0 0 0 │\n\ @@ -94,10 +115,12 @@ impl LinElasticity { let mut res = LinElasticity { young, poisson, + mandel, plane_stress, dd: Tensor4::new(mandel), + cc: None, }; - res.calc_modulus(); + res.calc_rigidity(); res } @@ -107,11 +130,13 @@ impl LinElasticity { /// /// ``` /// use russell_tensor::LinElasticity; - /// let mut ela = LinElasticity::new(3000.0, 0.2, false, true); + /// let two_dim = true; + /// let plane_stress = true; + /// let mut ela = LinElasticity::new(3000.0, 0.2, two_dim, plane_stress); /// ela.set_young_poisson(6000.0, 0.2); - /// let out = ela.get_modulus().as_matrix(); + /// let dd = ela.get_rigidity().as_matrix(); /// assert_eq!( - /// format!("{}", out), + /// format!("{}", dd), /// "┌ ┐\n\ /// │ 6250 1250 0 0 0 0 0 0 0 │\n\ /// │ 1250 6250 0 0 0 0 0 0 0 │\n\ @@ -128,14 +153,20 @@ impl LinElasticity { pub fn set_young_poisson(&mut self, young: f64, poisson: f64) { self.young = young; self.poisson = poisson; - self.calc_modulus(); + self.calc_rigidity(); + if self.cc.is_some() { + self.calc_compliance(); + } } /// Sets the bulk (K) and shear (G) moduli pub fn set_bulk_shear(&mut self, bulk: f64, shear: f64) { self.young = 9.0 * bulk * shear / (3.0 * bulk + shear); self.poisson = (3.0 * bulk - 2.0 * shear) / (6.0 * bulk + 2.0 * shear); - self.calc_modulus(); + self.calc_rigidity(); + if self.cc.is_some() { + self.calc_compliance(); + } } /// Returns the Young's modulus and Poisson's coefficient @@ -155,9 +186,9 @@ impl LinElasticity { ) } - /// Returns an access to the elasticity modulus + /// Returns an access to the elastic rigidity (stiffness) modulus /// - /// Returns D from: + /// The rigiDity modulus `D` is such that: /// /// ```text /// σ = D : ε @@ -168,7 +199,7 @@ impl LinElasticity { /// ``` /// use russell_tensor::LinElasticity; /// let ela = LinElasticity::new(3000.0, 0.2, false, true); - /// let out = ela.get_modulus().as_matrix(); + /// let out = ela.get_rigidity().as_matrix(); /// assert_eq!( /// format!("{}", out), /// "┌ ┐\n\ @@ -184,10 +215,26 @@ impl LinElasticity { /// └ ┘" /// ); /// ``` - pub fn get_modulus(&self) -> &Tensor4 { + pub fn get_rigidity(&self) -> &Tensor4 { &self.dd } + /// Calculates and returns an access to the elastic compliance modulus + /// + /// The Compliance modulus `C` is such that: + /// + /// ```text + /// ε = C : σ + /// ``` + /// + /// The compliance modulus is calculate as `C = D⁻¹` + pub fn get_compliance(&mut self) -> &Tensor4 { + if self.cc.is_none() { + self.calc_compliance(); + } + self.cc.as_ref().unwrap() + } + /// Calculates stress from strain /// /// ```text @@ -228,7 +275,7 @@ impl LinElasticity { /// // sum of first 3 rows = 1800 /// // sum of other rows = 720 /// let ela = LinElasticity::new(900.0, 0.25, false, false); - /// let out = ela.get_modulus().as_matrix(); + /// let out = ela.get_rigidity().as_matrix(); /// assert_eq!( /// format!("{}", out), /// "┌ ┐\n\ @@ -260,7 +307,7 @@ impl LinElasticity { /// // sum of first 3 rows = 1800 /// // sum of other rows = 720 /// let ela = LinElasticity::new(900.0, 0.25, true, false); - /// let out = ela.get_modulus().as_matrix(); + /// let out = ela.get_rigidity().as_matrix(); /// println!("{}", out); /// assert_eq!( /// format!("{}", out), @@ -335,8 +382,8 @@ impl LinElasticity { Ok(eps_zz) } - /// Computes elasticity modulus - fn calc_modulus(&mut self) { + /// Calculates the rigidity modulus + fn calc_rigidity(&mut self) { if self.plane_stress { let c = self.young / (1.0 - self.poisson * self.poisson); self.dd.mat.set(0, 0, c); @@ -362,6 +409,18 @@ impl LinElasticity { self.dd.mat.set(5, 5, self.dd.mat.get(3, 3)); } } + + /// Calculates the compliance modulus + fn calc_compliance(&mut self) { + let cc = match self.cc.as_mut() { + Some(c) => c, + None => { + self.cc = Some(Tensor4::new(self.mandel)); + self.cc.as_mut().unwrap() + } + }; + mat_inverse(&mut cc.mat, &self.dd.mat).unwrap(); + } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -440,7 +499,7 @@ mod tests { #[test] fn get_modulus_works() { let ela = LinElasticity::new(3000.0, 0.2, false, true); - let dd = ela.get_modulus(); + let dd = ela.get_rigidity(); assert_eq!(dd.mat.get(0, 0), 3125.0); } From a5c808f7742669561b15d047a89a20789a75c083 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Mon, 13 May 2024 15:13:48 +1000 Subject: [PATCH 42/44] Fix compliance modulus calculation in LinElasticity --- russell_tensor/src/lin_elasticity.rs | 164 +++++++++++++++++---------- 1 file changed, 103 insertions(+), 61 deletions(-) diff --git a/russell_tensor/src/lin_elasticity.rs b/russell_tensor/src/lin_elasticity.rs index 5ec46f33..d5ab5980 100644 --- a/russell_tensor/src/lin_elasticity.rs +++ b/russell_tensor/src/lin_elasticity.rs @@ -9,9 +9,6 @@ pub struct LinElasticity { /// Holds the Poisson's coefficient poisson: f64, - /// Holds the Mandel representation enum - mandel: Mandel, - /// Holds the plane-stress flag plane_stress: bool, @@ -23,17 +20,6 @@ pub struct LinElasticity { /// σ = D : ε /// ``` dd: Tensor4, - - /// Holds the elastic Compliance modulus - /// - /// The Compliance modulus `C` is such that: - /// - /// ```text - /// ε = C : σ - /// ``` - /// - /// The compliance modulus is calculate as `C = D⁻¹` - cc: Option, } impl LinElasticity { @@ -54,7 +40,7 @@ impl LinElasticity { /// /// // 3D /// let ela = LinElasticity::new(900.0, 0.25, false, false); - /// let dd = ela.get_rigidity().as_matrix(); + /// let dd = ela.get_modulus().as_matrix(); /// assert_eq!( /// format!("{}", dd), /// "┌ ┐\n\ @@ -72,7 +58,7 @@ impl LinElasticity { /// /// // 2D plane-strain /// let ela = LinElasticity::new(900.0, 0.25, true, false); - /// let dd = ela.get_rigidity().as_matrix(); + /// let dd = ela.get_modulus().as_matrix(); /// assert_eq!( /// format!("{}", dd), /// "┌ ┐\n\ @@ -90,7 +76,7 @@ impl LinElasticity { /// /// // 2D plane-stress /// let ela = LinElasticity::new(3000.0, 0.2, false, true); - /// let dd = ela.get_rigidity().as_matrix(); + /// let dd = ela.get_modulus().as_matrix(); /// assert_eq!( /// format!("{}", dd), /// "┌ ┐\n\ @@ -115,15 +101,18 @@ impl LinElasticity { let mut res = LinElasticity { young, poisson, - mandel, plane_stress, dd: Tensor4::new(mandel), - cc: None, }; res.calc_rigidity(); res } + /// Returns the Mandel representation + pub fn mandel(&self) -> Mandel { + self.dd.mandel + } + /// Sets the Young's modulus and Poisson's coefficient /// /// # Examples @@ -134,7 +123,7 @@ impl LinElasticity { /// let plane_stress = true; /// let mut ela = LinElasticity::new(3000.0, 0.2, two_dim, plane_stress); /// ela.set_young_poisson(6000.0, 0.2); - /// let dd = ela.get_rigidity().as_matrix(); + /// let dd = ela.get_modulus().as_matrix(); /// assert_eq!( /// format!("{}", dd), /// "┌ ┐\n\ @@ -154,9 +143,6 @@ impl LinElasticity { self.young = young; self.poisson = poisson; self.calc_rigidity(); - if self.cc.is_some() { - self.calc_compliance(); - } } /// Sets the bulk (K) and shear (G) moduli @@ -164,9 +150,6 @@ impl LinElasticity { self.young = 9.0 * bulk * shear / (3.0 * bulk + shear); self.poisson = (3.0 * bulk - 2.0 * shear) / (6.0 * bulk + 2.0 * shear); self.calc_rigidity(); - if self.cc.is_some() { - self.calc_compliance(); - } } /// Returns the Young's modulus and Poisson's coefficient @@ -199,7 +182,7 @@ impl LinElasticity { /// ``` /// use russell_tensor::LinElasticity; /// let ela = LinElasticity::new(3000.0, 0.2, false, true); - /// let out = ela.get_rigidity().as_matrix(); + /// let out = ela.get_modulus().as_matrix(); /// assert_eq!( /// format!("{}", out), /// "┌ ┐\n\ @@ -215,26 +198,10 @@ impl LinElasticity { /// └ ┘" /// ); /// ``` - pub fn get_rigidity(&self) -> &Tensor4 { + pub fn get_modulus(&self) -> &Tensor4 { &self.dd } - /// Calculates and returns an access to the elastic compliance modulus - /// - /// The Compliance modulus `C` is such that: - /// - /// ```text - /// ε = C : σ - /// ``` - /// - /// The compliance modulus is calculate as `C = D⁻¹` - pub fn get_compliance(&mut self) -> &Tensor4 { - if self.cc.is_none() { - self.calc_compliance(); - } - self.cc.as_ref().unwrap() - } - /// Calculates stress from strain /// /// ```text @@ -275,7 +242,7 @@ impl LinElasticity { /// // sum of first 3 rows = 1800 /// // sum of other rows = 720 /// let ela = LinElasticity::new(900.0, 0.25, false, false); - /// let out = ela.get_rigidity().as_matrix(); + /// let out = ela.get_modulus().as_matrix(); /// assert_eq!( /// format!("{}", out), /// "┌ ┐\n\ @@ -307,7 +274,7 @@ impl LinElasticity { /// // sum of first 3 rows = 1800 /// // sum of other rows = 720 /// let ela = LinElasticity::new(900.0, 0.25, true, false); - /// let out = ela.get_rigidity().as_matrix(); + /// let out = ela.get_modulus().as_matrix(); /// println!("{}", out); /// assert_eq!( /// format!("{}", out), @@ -382,6 +349,52 @@ impl LinElasticity { Ok(eps_zz) } + /// Calculates the elastic compliance modulus + /// + /// **Note:** The compliance modulus is not available for plane-stress. + /// + /// The Compliance modulus `C` is such that: + /// + /// ```text + /// ε = C : σ + /// ``` + /// + /// The compliance modulus is calculate as `C = D⁻¹` + /// + /// # Panics + /// + /// A panic will occur if `cc` has a different [Mandel] than `dd`. + /// + /// # Examples + /// + /// ``` + /// use russell_lab::mat_approx_eq; + /// use russell_tensor::*; + /// + /// fn main() -> Result<(), StrError> { + /// // calculate C + /// let ela = LinElasticity::new(900.0, 0.25, false, false); + /// let mut cc = Tensor4::new(ela.mandel()); + /// ela.calc_compliance(&mut cc).unwrap(); + /// + /// // check + /// let (kk, gg) = ela.get_bulk_shear(); + /// let psd = Tensor4::constant_pp_symdev(true); + /// let piso = Tensor4::constant_pp_iso(true); + /// let mut correct = Tensor4::new(Mandel::Symmetric); + /// t4_add(&mut correct, 1.0 / (3.0 * kk), &piso, 1.0 / (2.0 * gg), &psd); + /// mat_approx_eq(cc.matrix(), correct.matrix(), 1e-15); + /// Ok(()) + /// } + /// ``` + pub fn calc_compliance(&self, cc: &mut Tensor4) -> Result<(), StrError> { + if self.plane_stress { + return Err("The compliance modulus is not available for plane-stress"); + } + let _ = mat_inverse(&mut cc.mat, &self.dd.mat).map_err(|_| "cannot invert the rigidity modulus D")?; + Ok(()) + } + /// Calculates the rigidity modulus fn calc_rigidity(&mut self) { if self.plane_stress { @@ -409,18 +422,6 @@ impl LinElasticity { self.dd.mat.set(5, 5, self.dd.mat.get(3, 3)); } } - - /// Calculates the compliance modulus - fn calc_compliance(&mut self) { - let cc = match self.cc.as_mut() { - Some(c) => c, - None => { - self.cc = Some(Tensor4::new(self.mandel)); - self.cc.as_mut().unwrap() - } - }; - mat_inverse(&mut cc.mat, &self.dd.mat).unwrap(); - } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -428,8 +429,8 @@ impl LinElasticity { #[cfg(test)] mod tests { use super::LinElasticity; - use crate::{Mandel, Tensor2}; - use russell_lab::approx_eq; + use crate::{t4_add, Mandel, Tensor2, Tensor4}; + use russell_lab::{approx_eq, mat_approx_eq}; #[test] fn new_works() { @@ -499,7 +500,7 @@ mod tests { #[test] fn get_modulus_works() { let ela = LinElasticity::new(3000.0, 0.2, false, true); - let dd = ela.get_rigidity(); + let dd = ela.get_modulus(); assert_eq!(dd.mat.get(0, 0), 3125.0); } @@ -622,4 +623,45 @@ mod tests { let eps_zz = ela.out_of_plane_strain(&stress).unwrap(); approx_eq(eps_zz, 0.0050847, 1e-4); } + + #[test] + fn calc_compliance_modulus_handles_errors() { + let ela = LinElasticity::new(900.0, 0.25, false, true); // plane-stress + let mut cc = Tensor4::new(ela.mandel()); + assert_eq!( + ela.calc_compliance(&mut cc).err(), + Some("The compliance modulus is not available for plane-stress") + ); + let ela = LinElasticity::new(0.0, 0.0, true, false); // two-dim + assert_eq!( + ela.calc_compliance(&mut cc).err(), + Some("cannot invert the rigidity modulus D") + ); + } + + #[test] + fn compliance_modulus_works() { + // calculate C + let mut ela = LinElasticity::new(900.0, 0.25, false, false); + let mut cc = Tensor4::new(ela.mandel()); + ela.calc_compliance(&mut cc).unwrap(); + + // check + let (kk, gg) = ela.get_bulk_shear(); + let psd = Tensor4::constant_pp_symdev(true); + let piso = Tensor4::constant_pp_iso(true); + let mut correct = Tensor4::new(Mandel::Symmetric); + t4_add(&mut correct, 1.0 / (3.0 * kk), &piso, 1.0 / (2.0 * gg), &psd); + mat_approx_eq(&cc.mat, &correct.mat, 1e-15); + + // change parameters + let (kk, gg) = (1.0 / 6.0, 1.0 / 4.0); + ela.set_bulk_shear(kk, gg); + ela.calc_compliance(&mut cc).unwrap(); + + // check again + t4_add(&mut correct, 1.0 / (3.0 * kk), &piso, 1.0 / (2.0 * gg), &psd); + // println!("{}", cc.as_matrix()); + mat_approx_eq(&cc.mat, &correct.mat, 1e-15); + } } From b786c285222ea570c46b7d2102ed01191c68feb3 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Tue, 14 May 2024 17:15:08 +1000 Subject: [PATCH 43/44] Improve set_tensor (mirror) method. Impl set_mandel_vector method of Tensor2 --- russell_tensor/src/derivatives_t2.rs | 2 +- russell_tensor/src/tensor2.rs | 182 ++++++++++++++++++++++----- 2 files changed, 149 insertions(+), 35 deletions(-) diff --git a/russell_tensor/src/derivatives_t2.rs b/russell_tensor/src/derivatives_t2.rs index 29cd6f7c..cf8bf6c7 100644 --- a/russell_tensor/src/derivatives_t2.rs +++ b/russell_tensor/src/derivatives_t2.rs @@ -29,7 +29,7 @@ pub fn deriv1_norm(d1: &mut Tensor2, sigma: &Tensor2) -> Option { let dim = d1.vec.dim(); let n = sigma.norm(); if n > 0.0 { - d1.mirror(sigma); + d1.set_tensor(1.0, sigma); for i in 0..dim { d1.vec[i] /= n; } diff --git a/russell_tensor/src/tensor2.rs b/russell_tensor/src/tensor2.rs index a54c79bb..f7a1c5c9 100644 --- a/russell_tensor/src/tensor2.rs +++ b/russell_tensor/src/tensor2.rs @@ -847,10 +847,75 @@ impl Tensor2 { } } - /// Sets this tensor equal to another one + /// Sets this tensor equal to another tensor (given as a Mandel vector) /// /// ```text - /// self := other + /// self := α other + /// ``` + /// + /// # Panics + /// + /// A panic will occur if the other tensor has an incorrect dimension. + /// + /// # Examples + /// + /// ``` + /// use russell_lab::Vector; + /// use russell_tensor::{Mandel, Tensor2, StrError, SQRT_2}; + /// + /// fn main() -> Result<(), StrError> { + /// let mut a = Tensor2::from_matrix(&[ + /// [1.0, 2.0, 3.0], + /// [4.0, 5.0, 6.0], + /// [7.0, 8.0, 9.0], + /// ], Mandel::General)?; + /// let v_mandel = &Vector::from(&[ + /// 1.0, + /// 5.0, + /// 9.0, + /// 6.0 / SQRT_2, + /// 14.0 / SQRT_2, + /// 10.0 / SQRT_2, + /// -2.0 / SQRT_2, + /// -2.0 / SQRT_2, + /// -4.0 / SQRT_2, + /// ]); + /// + /// a.set_mandel_vector(2.0, &v_mandel); + /// + /// assert_eq!( + /// format!("{:.1}", a.as_matrix()), + /// "┌ ┐\n\ + /// │ 2.0 4.0 6.0 │\n\ + /// │ 8.0 10.0 12.0 │\n\ + /// │ 14.0 16.0 18.0 │\n\ + /// └ ┘" + /// ); + /// Ok(()) + /// } + /// ``` + pub fn set_mandel_vector(&mut self, alpha: f64, other: &Vector) { + assert_eq!(self.mandel.dim(), other.dim()); + let dim = self.vec.dim(); + self.vec[0] = alpha * other[0]; + self.vec[1] = alpha * other[1]; + self.vec[2] = alpha * other[2]; + self.vec[3] = alpha * other[3]; + if dim > 4 { + self.vec[4] = alpha * other[4]; + self.vec[5] = alpha * other[5]; + } + if dim > 6 { + self.vec[6] = alpha * other[6]; + self.vec[7] = alpha * other[7]; + self.vec[8] = alpha * other[8]; + } + } + + /// Sets this tensor equal to another tensor + /// + /// ```text + /// self := α other /// ``` /// /// # Panics @@ -874,34 +939,34 @@ impl Tensor2 { /// [70.0, 80.0, 90.0], /// ], Mandel::General)?; /// - /// a.mirror(&b); + /// a.set_tensor(2.0, &b); /// /// assert_eq!( /// format!("{:.1}", a.as_matrix()), - /// "┌ ┐\n\ - /// │ 10.0 20.0 30.0 │\n\ - /// │ 40.0 50.0 60.0 │\n\ - /// │ 70.0 80.0 90.0 │\n\ - /// └ ┘" + /// "┌ ┐\n\ + /// │ 20.0 40.0 60.0 │\n\ + /// │ 80.0 100.0 120.0 │\n\ + /// │ 140.0 160.0 180.0 │\n\ + /// └ ┘" /// ); /// Ok(()) /// } /// ``` - pub fn mirror(&mut self, other: &Tensor2) { + pub fn set_tensor(&mut self, alpha: f64, other: &Tensor2) { assert_eq!(self.mandel, other.mandel); let dim = self.vec.dim(); - self.vec[0] = other.vec[0]; - self.vec[1] = other.vec[1]; - self.vec[2] = other.vec[2]; - self.vec[3] = other.vec[3]; + self.vec[0] = alpha * other.vec[0]; + self.vec[1] = alpha * other.vec[1]; + self.vec[2] = alpha * other.vec[2]; + self.vec[3] = alpha * other.vec[3]; if dim > 4 { - self.vec[4] = other.vec[4]; - self.vec[5] = other.vec[5]; + self.vec[4] = alpha * other.vec[4]; + self.vec[5] = alpha * other.vec[5]; } if dim > 6 { - self.vec[6] = other.vec[6]; - self.vec[7] = other.vec[7]; - self.vec[8] = other.vec[8]; + self.vec[6] = alpha * other.vec[6]; + self.vec[7] = alpha * other.vec[7]; + self.vec[8] = alpha * other.vec[8]; } } @@ -1891,7 +1956,7 @@ impl Tensor2 { mod tests { use super::Tensor2; use crate::{Mandel, SampleTensor2, SamplesTensor2, IDENTITY2, SQRT_2, SQRT_2_BY_3, SQRT_3, SQRT_3_BY_2}; - use russell_lab::{approx_eq, mat_approx_eq, mat_mat_mul, math::PI, vec_approx_eq, Matrix}; + use russell_lab::{approx_eq, mat_approx_eq, mat_mat_mul, math::PI, vec_approx_eq, Matrix, Vector}; #[test] fn new_and_getters_work() { @@ -2582,10 +2647,59 @@ mod tests { #[test] #[should_panic] - fn mirror_panics_panics_on_incorrect_input() { + fn set_mandel_vector_panics_panics_on_incorrect_input() { + let mut a = Tensor2::new(Mandel::Symmetric2D); + let b = Vector::new(1); + a.set_mandel_vector(2.0, &b); + } + + #[test] + fn set_mandel_vector_works() { + // general + let mut tt = Tensor2::new(Mandel::General); + const NOISE: f64 = 1234.568; + tt.vec.fill(NOISE); + tt.set_mandel_vector( + 2.0, + &Vector::from(&[ + 1.0, + 5.0, + 9.0, + 6.0 / SQRT_2, + 14.0 / SQRT_2, + 10.0 / SQRT_2, + -2.0 / SQRT_2, + -2.0 / SQRT_2, + -4.0 / SQRT_2, + ]), + ); + let correct = &[[2.0, 4.0, 6.0], [8.0, 10.0, 12.0], [14.0, 16.0, 18.0]]; + mat_approx_eq(&tt.as_matrix(), correct, 1e-14); + + // symmetric 3D + let mut tt = Tensor2::new(Mandel::Symmetric); + tt.vec.fill(NOISE); + tt.set_mandel_vector( + 2.0, + &Vector::from(&[1.0, 2.0, 3.0, 4.0 * SQRT_2, 5.0 * SQRT_2, 6.0 * SQRT_2]), + ); + let correct = &[[2.0, 8.0, 12.0], [8.0, 4.0, 10.0], [12.0, 10.0, 6.0]]; + mat_approx_eq(&tt.as_matrix(), correct, 1e-14); + + // symmetric 2D + let mut tt = Tensor2::new(Mandel::Symmetric2D); + tt.vec.fill(NOISE); + tt.set_mandel_vector(2.0, &Vector::from(&[1.0, 2.0, 3.0, 4.0 * SQRT_2])); + let correct = &[[2.0, 8.0, 0.0], [8.0, 4.0, 0.0], [0.0, 0.0, 6.0]]; + mat_approx_eq(&tt.as_matrix(), correct, 1e-14); + } + + #[test] + #[should_panic] + fn set_tensor_panics_panics_on_incorrect_input() { let mut a = Tensor2::new(Mandel::General); let b = Tensor2::new(Mandel::Symmetric); - a.mirror(&b); + a.set_tensor(2.0, &b); } #[test] @@ -2597,7 +2711,7 @@ mod tests { } #[test] - fn mirror_and_update_work() { + fn set_tensor_and_update_work() { // general let mut a = Tensor2::new(Mandel::General); #[rustfmt::skip] @@ -2612,15 +2726,15 @@ mod tests { Mandel::General, ) .unwrap(); - a.mirror(&b); + a.set_tensor(2.0, &b); a.update(10.0, &c); let out = a.as_matrix(); assert_eq!( format!("{:.1}", out), "┌ ┐\n\ - │ 1001.0 1003.0 1001.0 │\n\ - │ 1002.0 1002.0 1002.0 │\n\ - │ 1003.0 1001.0 1003.0 │\n\ + │ 1002.0 1006.0 1002.0 │\n\ + │ 1004.0 1004.0 1004.0 │\n\ + │ 1006.0 1002.0 1006.0 │\n\ └ ┘" ); @@ -2638,15 +2752,15 @@ mod tests { Mandel::Symmetric, ) .unwrap(); - a.mirror(&b); + a.set_tensor(2.0, &b); a.update(10.0, &c); let out = a.as_matrix(); assert_eq!( format!("{:.1}", out), "┌ ┐\n\ - │ 1001.0 1003.0 1001.0 │\n\ - │ 1003.0 1002.0 1002.0 │\n\ - │ 1001.0 1002.0 1003.0 │\n\ + │ 1002.0 1006.0 1002.0 │\n\ + │ 1006.0 1004.0 1004.0 │\n\ + │ 1002.0 1004.0 1006.0 │\n\ └ ┘" ); @@ -2664,15 +2778,15 @@ mod tests { Mandel::Symmetric2D, ) .unwrap(); - a.mirror(&b); + a.set_tensor(2.0, &b); a.update(10.0, &c); let out = a.as_matrix(); assert_eq!( format!("{:.1}", out), "┌ ┐\n\ - │ 1001.0 1003.0 0.0 │\n\ - │ 1003.0 1002.0 0.0 │\n\ - │ 0.0 0.0 1003.0 │\n\ + │ 1002.0 1006.0 0.0 │\n\ + │ 1006.0 1004.0 0.0 │\n\ + │ 0.0 0.0 1006.0 │\n\ └ ┘" ); } From 9bb864cbed0b3ea789b10938eb315fc359347a33 Mon Sep 17 00:00:00 2001 From: Dorival Pedroso Date: Tue, 14 May 2024 17:33:07 +1000 Subject: [PATCH 44/44] Improve set_tensor method of Tensor4 --- russell_tensor/src/tensor4.rs | 59 ++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/russell_tensor/src/tensor4.rs b/russell_tensor/src/tensor4.rs index daf91a4e..bf3461e4 100644 --- a/russell_tensor/src/tensor4.rs +++ b/russell_tensor/src/tensor4.rs @@ -1,6 +1,6 @@ use super::{IJKL_TO_MN, IJKL_TO_MN_SYM, MN_TO_IJKL, SQRT_2}; use crate::{AsMatrix9x9, Mandel, StrError, ONE_BY_3, TWO_BY_3}; -use russell_lab::{mat_copy, mat_update, Matrix}; +use russell_lab::{mat_update, Matrix}; use serde::{Deserialize, Serialize}; /// Implements a fourth order-tensor, minor-symmetric or not @@ -888,10 +888,10 @@ impl Tensor4 { } } - /// Sets this tensor equal to another one + /// Sets this tensor equal to another tensor /// /// ```text - /// self := other + /// self := α other /// ``` /// /// # Panics @@ -905,7 +905,7 @@ impl Tensor4 { /// use russell_tensor::{Mandel, Tensor4, StrError}; /// /// fn main() -> Result<(), StrError> { - /// let dd = Tensor4::from_matrix(&[ + /// let data = &[ /// [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0], /// [ -1.0, -2.0, -3.0, -4.0, -5.0, -6.0, -7.0, -8.0, -9.0], /// [ 2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0, 18.0], @@ -915,18 +915,24 @@ impl Tensor4 { /// [ -2.0, -4.0, -6.0, -8.0,-10.0,-12.0,-14.0,-16.0,-18.0], /// [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], /// [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], - /// ], Mandel::General)?; - /// + /// ]; + /// let dd = Tensor4::from_matrix(data, Mandel::General)?; /// let mut ee = Tensor4::new(Mandel::General); - /// ee.mirror(&dd); /// - /// mat_approx_eq(dd.matrix(), ee.matrix(), 1e-15); + /// ee.set_tensor(1.0, &dd); + /// + /// mat_approx_eq(&dd.as_matrix(), data, 1e-14); /// Ok(()) /// } /// ``` - pub fn mirror(&mut self, other: &Tensor4) { + pub fn set_tensor(&mut self, alpha: f64, other: &Tensor4) { assert_eq!(other.mandel, self.mandel); - mat_copy(&mut self.mat, &other.mat).unwrap(); + let dim = self.mat.dims().0; + for i in 0..dim { + for j in 0..dim { + self.mat.set(i, j, alpha * other.mat.get(i, j)); + } + } } /// Returns the fourth-order identity tensor (II) @@ -1323,7 +1329,7 @@ mod tests { use super::{Tensor4, MN_TO_IJKL}; use crate::{Mandel, SamplesTensor4}; use crate::{IDENTITY4, P_DEV, P_ISO, P_SKEW, P_SYM, P_SYMDEV, TRACE_PROJECTION, TRANSPOSITION}; - use russell_lab::{approx_eq, mat_approx_eq}; + use russell_lab::{approx_eq, mat_approx_eq, Matrix}; #[test] fn new_and_getters_work() { @@ -1759,16 +1765,16 @@ mod tests { #[test] #[should_panic] - fn mirror_panics_on_incorrect_input() { + fn set_tensor_panics_on_incorrect_input() { let dd = Tensor4::new(Mandel::Symmetric); let mut ee = Tensor4::new(Mandel::General); - ee.mirror(&dd); + ee.set_tensor(2.0, &dd); } #[test] - fn mirror_works() { - let dd = Tensor4::from_matrix( - &[ + fn set_tensor_works() { + #[rustfmt::skip] + let dd = Tensor4::from_matrix(&[ [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0], [5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0], [9.0, 9.0, 9.0, 9.0, 9.0, 9.0, 9.0, 9.0, 9.0], @@ -1778,13 +1784,22 @@ mod tests { [2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0], [6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0], [3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0], - ], - Mandel::General, - ) - .unwrap(); + ], Mandel::General).unwrap(); let mut ee = Tensor4::new(Mandel::General); - ee.mirror(&dd); - mat_approx_eq(&dd.mat, &ee.mat, 1e-15); + ee.set_tensor(2.0, &dd); + #[rustfmt::skip] + let correct = Matrix::from(&[ + [ 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0], + [10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0], + [18.0, 18.0, 18.0, 18.0, 18.0, 18.0, 18.0, 18.0, 18.0], + [ 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0], + [12.0, 12.0, 12.0, 12.0, 12.0, 12.0, 12.0, 12.0, 12.0], + [ 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0], + [ 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0], + [12.0, 12.0, 12.0, 12.0, 12.0, 12.0, 12.0, 12.0, 12.0], + [ 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0, 6.0], + ]); + mat_approx_eq(&ee.as_matrix(), &correct, 1e-14); } #[test]