svMultiPhysics
Loading...
Searching...
No Matches
mat_fun.h
1// SPDX-FileCopyrightText: Copyright (c) Stanford University, The Regents of the University of California, and others.
2// SPDX-License-Identifier: BSD-3-Clause
3
4#ifndef MAT_FUN_H
5#define MAT_FUN_H
6#include "eigen3/Eigen/Core"
7#include "eigen3/Eigen/Dense"
8#include "eigen3/unsupported/Eigen/CXX11/Tensor"
9#include <stdexcept>
10
11#include "Array.h"
12#include "Tensor4.h"
13#include "Vector.h"
15
16/// @brief The classes defined here duplicate the data structures in the
17/// Fortran MATFUN module defined in MATFUN.f.
18///
19/// This module defines data structures for generally performed matrix and tensor operations.
20///
21/// \todo [TODO:DaveP] this should just be a namespace?
22//
23namespace mat_fun {
24 // Define templated type aliases for Eigen matrices and tensors for convenience
25 template<size_t nsd>
26 using Matrix = Eigen::Matrix<double, nsd, nsd>;
27
28 template<size_t nsd>
29 using Tensor = Eigen::TensorFixedSize<double, Eigen::Sizes<nsd, nsd, nsd, nsd>>;
30
31 // Function to convert Array<double> to Eigen::Matrix
32 template <typename MatrixType>
33 MatrixType convert_to_eigen_matrix(const Array<double>& src) {
34 MatrixType mat;
35 for (int i = 0; i < mat.rows(); ++i)
36 for (int j = 0; j < mat.cols(); ++j)
37 mat(i, j) = src(i, j);
38 return mat;
39 }
40
41 // Function to convert Eigen::Matrix to Array<double>
42 template <typename MatrixType>
43 void convert_to_array(const MatrixType& mat, Array<double>& dest) {
44 for (int i = 0; i < mat.rows(); ++i)
45 for (int j = 0; j < mat.cols(); ++j)
46 dest(i, j) = mat(i, j);
47 }
48
49 // Function to convert a higher-dimensional array like Dm
50 template <typename MatrixType>
51 void copy_Dm(const MatrixType& mat, Array<double>& dest) {
52 if ((mat.rows() != dest.nrows()) || (mat.cols() != dest.ncols())) {
53 const std::string mat_dims = "(" + std::to_string(mat.rows()) + "x" + std::to_string(mat.cols()) + ")";
54 const std::string dest_dims = "(" + std::to_string(dest.nrows()) + "x" + std::to_string(dest.ncols()) + ")";
55 svmp::raise<svmp::FE::InvalidArgumentException>(
56 "The 'mat" + mat_dims + "' and 'dest" + dest_dims +
57 "' arrays have incompatible sizes.");
58 }
59
60 for (int i = 0; i < mat.rows(); ++i) {
61 for (int j = 0; j < mat.cols(); ++j) {
62 dest(i, j) = mat(i, j);
63 }
64 }
65 }
66
67 template <int nsd>
68 Eigen::Matrix<double, nsd, 1> cross_product(const Eigen::Matrix<double, nsd, 1>& u, const Eigen::Matrix<double, nsd, 1>& v) {
69 if constexpr (nsd == 2) {
70 return Eigen::Matrix<double, 2, 1>(v(1), - v(0));
71 }
72 else if constexpr (nsd == 3) {
73 return u.cross(v);
74 }
75 else {
76 throw std::runtime_error("[cross_product] Invalid number of spatial dimensions '" + std::to_string(nsd) + "'. Valid dimensions are 2 or 3.");
77 }
78 }
79
80 double mat_ddot(const Array<double>& A, const Array<double>& B, const int nd);
81
82 template <int nsd>
83 double double_dot_product(const Matrix<nsd>& A, const Matrix<nsd>& B) {
84 return A.cwiseProduct(B).sum();
85 }
86
87 double mat_det(const Array<double>& A, const int nd);
88 Array<double> mat_dev(const Array<double>& A, const int nd);
89
90 Array<double> mat_dyad_prod(const Vector<double>& u, const Vector<double>& v, const int nd);
91
92 Array<double> mat_id(const int nsd);
93 Array<double> mat_inv(const Array<double>& A, const int nd, bool debug = false);
94 Array<double> mat_inv_ge(const Array<double>& A, const int nd, bool debug = false);
95 Array<double> mat_inv_lp(const Array<double>& A, const int nd);
96
97 /// @brief Multiply a matrix by a vector, returning A*v.
98 ///
99 /// @param[in] A matrix with as many columns as v has entries.
100 /// @param[in] v vector.
101 /// @return the product, of size rows(A).
102 ///
103 /// Throws InvalidArgumentException if the sizes are incompatible.
104 Vector<double> mat_mul(const Array<double>& A, const Vector<double>& v);
105
106 /// @brief Multiply two matrices, returning A*B.
107 ///
108 /// @param[in] A left operand.
109 /// @param[in] B right operand, with as many rows as A has columns.
110 /// @return the product, of size rows(A) by cols(B).
111 ///
112 /// Throws InvalidArgumentException if the sizes are incompatible. The
113 /// result is freshly allocated, so the operands may alias it, as in
114 /// A = mat_mul(A, B).
115 Array<double> mat_mul(const Array<double>& A, const Array<double>& B);
116
117 /// @brief Multiply two matrices, writing A*B into an existing result.
118 ///
119 /// @param[in] A left operand.
120 /// @param[in] B right operand, with as many rows as A has columns.
121 /// @param[out] result the product. The caller sizes it rows(A) by cols(B).
122 ///
123 /// Throws InvalidArgumentException if the sizes are incompatible. Preferred
124 /// in loops, where it reuses the caller's storage instead of allocating a
125 /// result on every call. The result must not alias A or B.
126 void mat_mul(const Array<double>& A, const Array<double>& B, Array<double>& result);
127
128 /// @brief Matrix product with the operand shape supplied at compile time.
129 ///
130 /// Overloads of mat_mul rather than differently named helpers, so a call
131 /// site states the shape and otherwise reads exactly as before:
132 ///
133 /// @code
134 /// mat_mul(Dm, Bm.rslice(b), DBm); // runtime shape check
135 /// mat_mul<6, 6, 3>(Dm, Bm.rslice(b), DBm); // no check, same arguments
136 /// @endcode
137 ///
138 /// The generic mat_mul overload above will dispatch to this overload
139 /// when the shapes are known at compile time.
140 ///
141 /// @tparam M rows of A and of the result
142 /// @tparam K columns of A and rows of B, the contracted dimension
143 /// @tparam N columns of B and of the result
144 template <int M, int K, int N>
145 void mat_mul(const Array<double>& A, const Array<double>& B,
146 Array<double>& C)
147 {
148 Eigen::Map<const Eigen::Matrix<double, M, K>> a(A.data());
149 Eigen::Map<const Eigen::Matrix<double, K, N>> b(B.data());
150 Eigen::Map<Eigen::Matrix<double, M, N>> c(C.data());
151
152 c.noalias() = a * b;
153 }
154
155 /// @brief As above, but with the column count known only at run time.
156 ///
157 /// For operands with one column per element node, where the width depends on
158 /// the element type. The row counts are still compile-time, which is where
159 /// most of the benefit comes from.
160 template <int M, int K>
161 void mat_mul(const Array<double>& A, const Array<double>& B,
162 Array<double>& C)
163 {
164 Eigen::Map<const Eigen::Matrix<double, M, K>> a(A.data());
165 Eigen::Map<const Eigen::Matrix<double, K, Eigen::Dynamic>> b(B.data(), K, B.ncols());
166 Eigen::Map<Eigen::Matrix<double, M, Eigen::Dynamic>> c(C.data(), M, C.ncols());
167
168 c.noalias() = a * b;
169 }
170
171 Array<double> mat_symm(const Array<double>& A, const int nd);
172 Array<double> mat_symm_prod(const Vector<double>& u, const Vector<double>& v, const int nd);
173
174 double mat_trace(const Array<double>& A, const int nd);
175
176 Tensor4<double> ten_asym_prod12(const Array<double>& A, const Array<double>& B, const int nd);
177 Tensor4<double> ten_ddot(const Tensor4<double>& A, const Tensor4<double>& B, const int nd);
178 Tensor4<double> ten_ddot_2412(const Tensor4<double>& A, const Tensor4<double>& B, const int nd);
179 Tensor4<double> ten_ddot_3424(const Tensor4<double>& A, const Tensor4<double>& B, const int nd);
180
181 /**
182 * @brief Contracts two 4th order tensors A and B over two dimensions,
183 *
184 */
185 template <int nsd>
186 Tensor<nsd>
187 double_dot_product(const Tensor<nsd>& A, const std::array<int, 2>& dimsA,
188 const Tensor<nsd>& B, const std::array<int, 2>& dimsB) {
189
190 // Define the contraction dimensions
191 Eigen::array<Eigen::IndexPair<int>, 2> contractionDims = {
192 Eigen::IndexPair<int>(dimsA[0], dimsB[0]), // Contract A's dimsA[0] with B's dimsB[0]
193 Eigen::IndexPair<int>(dimsA[1], dimsB[1]) // Contract A's dimsA[1] with B's dimsB[1]
194 };
195
196 // Return the double dot product
197 return A.contract(B, contractionDims);
198
199 // For some reason, in this case the Eigen::Tensor contract function is
200 // faster than a for loop implementation.
201 }
202
203 Tensor4<double> ten_dyad_prod(const Array<double>& A, const Array<double>& B, const int nd);
204
205 /**
206 * @brief Compute the dyadic product of two 2nd order tensors A and B, C_ijkl = A_ij * B_kl
207 *
208 * @tparam nsd, the number of spatial dimensions
209 * @param A, the first 2nd order tensor
210 * @param B, the second 2nd order tensor
211 * @return Tensor<nsd>
212 */
213 template <int nsd>
214 Tensor<nsd>
215 dyadic_product(const Matrix<nsd>& A, const Matrix<nsd>& B) {
216 // Initialize the result tensor
217 Tensor<nsd> C;
218
219 // Compute the dyadic product: C_ijkl = A_ij * B_kl
220 for (int i = 0; i < nsd; ++i) {
221 for (int j = 0; j < nsd; ++j) {
222 for (int k = 0; k < nsd; ++k) {
223 for (int l = 0; l < nsd; ++l) {
224 C(i,j,k,l) = A(i,j) * B(k,l);
225 }
226 }
227 }
228 }
229 // For some reason, in this case the Eigen::Tensor contract function is
230 // slower than the for loop implementation
231
232 return C;
233 }
234
235 Tensor4<double> ten_ids(const int nd);
236
237 /**
238 * @brief Create a 4th order identity tensor:
239 * I_ijkl = 0.5 * (δ_ik * δ_jl + δ_il * δ_jk)
240 *
241 * @tparam nsd, the number of spatial dimensions
242 * @return Tensor<nsd>
243 */
244 template <int nsd>
245 Tensor<nsd>
247 // Initialize as zero
248 Tensor<nsd> I;
249 I.setZero();
250
251 // Set only non-zero entries
252 for (int i = 0; i < nsd; ++i) {
253 for (int j = 0; j < nsd; ++j) {
254 I(i,j,i,j) += 0.5;
255 I(i,j,j,i) += 0.5;
256 }
257 }
258
259 return I;
260 }
261
262 Array<double> ten_mddot(const Tensor4<double>& A, const Array<double>& B, const int nd);
263
264 Tensor4<double> ten_symm_prod(const Array<double>& A, const Array<double>& B, const int nd);
265
266 /// @brief Create a 4th order tensor from symmetric outer product of two matrices: C_ijkl = 0.5 * (A_ik * B_jl + A_il * B_jk)
267 ///
268 /// Reproduces 'FUNCTION TEN_SYMMPROD(A, B, nd) RESULT(C)'.
269 //
270 template <int nsd>
271 Tensor<nsd>
272 symmetric_dyadic_product(const Matrix<nsd>& A, const Matrix<nsd>& B) {
273
274 // Initialize the result tensor
275 Tensor<nsd> C;
276
277 // Compute the symmetric product: C_ijkl = 0.5 * (A_ik * B_jl + A_il * B_jk)
278 for (int i = 0; i < nsd; ++i) {
279 for (int j = 0; j < nsd; ++j) {
280 for (int k = 0; k < nsd; ++k) {
281 for (int l = 0; l < nsd; ++l) {
282 C(i,j,k,l) = 0.5 * (A(i,k) * B(j,l) + A(i,l) * B(j,k));
283 }
284 }
285 }
286 }
287 // For some reason, in this case the for loop implementation is faster
288 // than the Eigen::Tensor contract method
289
290 // Return the symmetric product
291 return C;
292 }
293
294 Tensor4<double> ten_transpose(const Tensor4<double>& A, const int nd);
295
296 /**
297 * @brief Performs a tensor transpose operation on a 4th order tensor A, B_ijkl = A_klij
298 *
299 * @tparam nsd, the number of spatial dimensions
300 * @param A, the input 4th order tensor
301 * @return Tensor<nsd>
302 */
303 template <int nsd>
304 Tensor<nsd>
305 transpose(const Tensor<nsd>& A) {
306
307 // Initialize the result tensor
308 Tensor<nsd> B;
309
310 // Permute the tensor indices to perform the transpose operation
311 for (int i = 0; i < nsd; ++i) {
312 for (int j = 0; j < nsd; ++j) {
313 for (int k = 0; k < nsd; ++k) {
314 for (int l = 0; l < nsd; ++l) {
315 B(i,j,k,l) = A(k,l,i,j);
316 }
317 }
318 }
319 }
320
321 return B;
322 }
323
324 Array<double> transpose(const Array<double>& A);
325
326 void ten_init(const int nd);
327
328};
329
330#endif
Exception hierarchy for error handling in the FE library.
The Tensor4 template class implements a simple interface to 4th order tensors.
Definition Tensor4.h:20
The Vector template class is used for storing int and double data.
Definition Vector.h:26
Eigen::Matrix< T, static_cast< int >(M), static_cast< int >(N)> Matrix
Fixed-size matrix for element-level computations.
Definition Matrix.h:41
The classes defined here duplicate the data structures in the Fortran MATFUN module defined in MATFUN...
Definition mat_fun.cpp:15
Tensor< nsd > symmetric_dyadic_product(const Matrix< nsd > &A, const Matrix< nsd > &B)
Create a 4th order tensor from symmetric outer product of two matrices: C_ijkl = 0....
Definition mat_fun.h:272
void ten_init(const int nd)
Initialize tensor index pointer.
Definition mat_fun.cpp:779
Tensor4< double > ten_symm_prod(const Array< double > &A, const Array< double > &B, const int nd)
Create a 4th order tensor from symmetric outer product of two matrices.
Definition mat_fun.cpp:871
Array< double > ten_mddot(const Tensor4< double > &A, const Array< double > &B, const int nd)
Double dot product of a 4th order tensor and a 2nd order tensor.
Definition mat_fun.cpp:841
Array< double > mat_symm(const Array< double > &A, const int nd)
Symmetric part of a matrix, S = (A + A.T)/2.
Definition mat_fun.cpp:574
Tensor4< double > ten_ids(const int nd)
Create a 4th order order symmetric identity tensor.
Definition mat_fun.cpp:822
Array< double > transpose(const Array< double > &A)
Reproduces Fortran TRANSPOSE.
Definition mat_fun.cpp:907
Vector< double > mat_mul(const Array< double > &A, const Vector< double > &v)
Multiply a matrix by a vector.
Definition mat_fun.cpp:465
double mat_trace(const Array< double > &A, const int nd)
Trace of second order matrix of rank nd.
Definition mat_fun.cpp:606
Tensor4< double > ten_ddot(const Tensor4< double > &A, const Tensor4< double > &B, const int nd)
Double dot product of 2 4th order tensors T_ijkl = A_ijmn * B_klmn.
Definition mat_fun.cpp:645
Tensor4< double > ten_dyad_prod(const Array< double > &A, const Array< double > &B, const int nd)
Create a 4th order tensor from outer product of two matrices.
Definition mat_fun.cpp:803
Array< double > mat_inv_lp(const Array< double > &A, const int nd)
This function computes inverse of a square matrix using Lapack functions (DGETRF + DGETRI)
Definition mat_fun.cpp:397
Array< double > mat_inv(const Array< double > &A, const int nd, bool debug)
This function computes inverse of a square matrix.
Definition mat_fun.cpp:110
Array< double > mat_inv_ge(const Array< double > &Ain, const int n, bool debug)
This function computes inverse of a square matrix using Gauss Elimination method.
Definition mat_fun.cpp:167
Array< double > mat_symm_prod(const Vector< double > &u, const Vector< double > &v, const int nd)
Create a matrix from symmetric product of two vectors.
Definition mat_fun.cpp:591
Array< double > mat_dyad_prod(const Vector< double > &u, const Vector< double > &v, const int nd)
Create a matrix from outer product of two vectors.
Definition mat_fun.cpp:82
Tensor< nsd > fourth_order_identity()
Create a 4th order identity tensor: I_ijkl = 0.5 * (δ_ik * δ_jl + δ_il * δ_jk)
Definition mat_fun.h:246
double mat_ddot(const Array< double > &A, const Array< double > &B, const int nd)
Double dot product of 2 square matrices.
Definition mat_fun.cpp:21
Tensor4< double > ten_ddot_2412(const Tensor4< double > &A, const Tensor4< double > &B, const int nd)
T_ijkl = A_imjn * B_mnkl.
Definition mat_fun.cpp:690
Tensor4< double > ten_asym_prod12(const Array< double > &A, const Array< double > &B, const int nd)
Create a 4th order tensor from antisymmetric outer product of two matrices.
Definition mat_fun.cpp:623
Tensor< nsd > dyadic_product(const Matrix< nsd > &A, const Matrix< nsd > &B)
Compute the dyadic product of two 2nd order tensors A and B, C_ijkl = A_ij * B_kl.
Definition mat_fun.h:215