diff --git a/include/GooseFEM/Matrix.h b/include/GooseFEM/Matrix.h index 58efb4a..3a6a7ed 100644 --- a/include/GooseFEM/Matrix.h +++ b/include/GooseFEM/Matrix.h @@ -1,91 +1,96 @@ /* (c - GPLv3) T.W.J. de Geus (Tom) | tom@geus.me | www.geus.me | github.com/tdegeus/GooseFEM */ #ifndef GOOSEFEM_MATRIX_H #define GOOSEFEM_MATRIX_H #include "config.h" #include #include #include namespace GooseFEM { template >> class Matrix { public: // Constructors Matrix() = default; Matrix(const xt::xtensor& conn, const xt::xtensor& dofs); + // Copy constructor and copy-assignment operator + // (needed because "m_solver" cannot be copied) + Matrix(const Matrix& other); + Matrix& operator=(const Matrix &other); + // Dimensions size_t nelem() const; // number of elements size_t nne() const; // number of nodes per element size_t nnode() const; // number of nodes size_t ndim() const; // number of dimensions size_t ndof() const; // number of DOFs // DOF lists xt::xtensor dofs() const; // DOFs // Assemble from matrices stored per element [nelem, nne*ndim, nne*ndim] void assemble(const xt::xtensor& elemmat); // Dot-product: // b_i = A_ij * x_j void dot(const xt::xtensor& x, xt::xtensor& b) const; void dot(const xt::xtensor& x, xt::xtensor& b) const; // Solve // x_u = A_uu \ ( b_u - A_up * x_p ) void solve(const xt::xtensor& b, xt::xtensor& x); void solve(const xt::xtensor& b, xt::xtensor& x); // Auto-allocation of the functions above xt::xtensor Dot(const xt::xtensor& x) const; xt::xtensor Dot(const xt::xtensor& x) const; xt::xtensor Solve(const xt::xtensor& b); xt::xtensor Solve(const xt::xtensor& b); private: // The matrix Eigen::SparseMatrix m_A; // Matrix entries std::vector> m_T; // Solver (re-used to solve different RHS) Solver m_solver; // Signal changes to data compare to the last inverse bool m_factor = false; // Bookkeeping xt::xtensor m_conn; // connectivity [nelem, nne] xt::xtensor m_dofs; // DOF-numbers per node [nnode, ndim] // Dimensions size_t m_nelem; // number of elements size_t m_nne; // number of nodes per element size_t m_nnode; // number of nodes size_t m_ndim; // number of dimensions size_t m_ndof; // number of DOFs // Compute inverse (automatically evaluated by "solve") void factorize(); // Convert arrays (Eigen version of Vector, which contains public functions) Eigen::VectorXd asDofs(const xt::xtensor& nodevec) const; void asNode(const Eigen::VectorXd& dofval, xt::xtensor& nodevec) const; }; } // namespace GooseFEM #include "Matrix.hpp" #endif diff --git a/include/GooseFEM/Matrix.hpp b/include/GooseFEM/Matrix.hpp index ff35d3e..cb8aedc 100644 --- a/include/GooseFEM/Matrix.hpp +++ b/include/GooseFEM/Matrix.hpp @@ -1,214 +1,243 @@ /* (c - GPLv3) T.W.J. de Geus (Tom) | tom@geus.me | www.geus.me | github.com/tdegeus/GooseFEM */ #ifndef GOOSEFEM_MATRIX_HPP #define GOOSEFEM_MATRIX_HPP #include "Matrix.h" namespace GooseFEM { template inline Matrix::Matrix( const xt::xtensor& conn, const xt::xtensor& dofs) : m_conn(conn), m_dofs(dofs) { m_nelem = m_conn.shape(0); m_nne = m_conn.shape(1); m_nnode = m_dofs.shape(0); m_ndim = m_dofs.shape(1); m_ndof = xt::amax(m_dofs)[0] + 1; m_T.reserve(m_nelem * m_nne * m_ndim * m_nne * m_ndim); m_A.resize(m_ndof, m_ndof); GOOSEFEM_ASSERT(xt::amax(m_conn)[0] + 1 == m_nnode); GOOSEFEM_ASSERT(m_ndof <= m_nnode * m_ndim); } +template +inline Matrix::Matrix(const Matrix& other) +{ + m_conn = other.m_conn; + m_dofs = other.m_dofs; + m_nelem = other.m_nelem; + m_nne = other.m_nne; + m_nnode = other.m_nnode; + m_ndim = other.m_ndim; + m_ndof = other.m_ndof; + m_T.reserve(m_nelem * m_nne * m_ndim * m_nne * m_ndim); + m_A.resize(m_ndof, m_ndof); +} + +template +inline Matrix& Matrix::operator=(const Matrix& other) +{ + m_conn = other.m_conn; + m_dofs = other.m_dofs; + m_nelem = other.m_nelem; + m_nne = other.m_nne; + m_nnode = other.m_nnode; + m_ndim = other.m_ndim; + m_ndof = other.m_ndof; + m_T.reserve(m_nelem * m_nne * m_ndim * m_nne * m_ndim); + m_A.resize(m_ndof, m_ndof); + return *this; +} + template inline size_t Matrix::nelem() const { return m_nelem; } template inline size_t Matrix::nne() const { return m_nne; } template inline size_t Matrix::nnode() const { return m_nnode; } template inline size_t Matrix::ndim() const { return m_ndim; } template inline size_t Matrix::ndof() const { return m_ndof; } template inline xt::xtensor Matrix::dofs() const { return m_dofs; } template inline void Matrix::factorize() { if (!m_factor) { return; } m_solver.compute(m_A); m_factor = false; } template inline void Matrix::assemble(const xt::xtensor& elemmat) { GOOSEFEM_ASSERT(xt::has_shape(elemmat, {m_nelem, m_nne * m_ndim, m_nne * m_ndim})); m_T.clear(); for (size_t e = 0; e < m_nelem; ++e) { for (size_t m = 0; m < m_nne; ++m) { for (size_t i = 0; i < m_ndim; ++i) { for (size_t n = 0; n < m_nne; ++n) { for (size_t j = 0; j < m_ndim; ++j) { m_T.push_back(Eigen::Triplet( m_dofs(m_conn(e, m), i), m_dofs(m_conn(e, n), j), elemmat(e, m * m_ndim + i, n * m_ndim + j))); } } } } } m_A.setFromTriplets(m_T.begin(), m_T.end()); m_factor = true; } template inline void Matrix::dot(const xt::xtensor& x, xt::xtensor& b) const { GOOSEFEM_ASSERT(xt::has_shape(b, {m_nnode, m_ndim})); GOOSEFEM_ASSERT(xt::has_shape(x, {m_nnode, m_ndim})); Eigen::VectorXd B = m_A * this->asDofs(x); this->asNode(B, b); } template inline void Matrix::dot(const xt::xtensor& x, xt::xtensor& b) const { GOOSEFEM_ASSERT(b.size() == m_ndof); GOOSEFEM_ASSERT(x.size() == m_ndof); Eigen::VectorXd B = m_A * Eigen::Map(x.data(), m_ndof); std::copy(B.data(), B.data() + m_ndof, b.begin()); } template inline void Matrix::solve(const xt::xtensor& b, xt::xtensor& x) { GOOSEFEM_ASSERT(xt::has_shape(b, {m_nnode, m_ndim})); GOOSEFEM_ASSERT(xt::has_shape(x, {m_nnode, m_ndim})); this->factorize(); Eigen::VectorXd B = this->asDofs(b); Eigen::VectorXd X = m_solver.solve(B); this->asNode(X, x); } template inline void Matrix::solve(const xt::xtensor& b, xt::xtensor& x) { GOOSEFEM_ASSERT(b.size() == m_ndof); GOOSEFEM_ASSERT(x.size() == m_ndof); this->factorize(); Eigen::VectorXd X = m_solver.solve(Eigen::Map(b.data(), m_ndof)); std::copy(X.data(), X.data() + m_ndof, x.begin()); } template inline xt::xtensor Matrix::Dot(const xt::xtensor& x) const { xt::xtensor b = xt::empty({m_nnode, m_ndim}); this->dot(x, b); return b; } template inline xt::xtensor Matrix::Dot(const xt::xtensor& x) const { xt::xtensor b = xt::empty({m_ndof}); this->dot(x, b); return b; } template inline xt::xtensor Matrix::Solve(const xt::xtensor& b) { xt::xtensor x = xt::empty({m_nnode, m_ndim}); this->solve(b, x); return x; } template inline xt::xtensor Matrix::Solve(const xt::xtensor& b) { xt::xtensor x = xt::empty({m_ndof}); this->solve(b, x); return x; } template inline Eigen::VectorXd Matrix::asDofs(const xt::xtensor& nodevec) const { GOOSEFEM_ASSERT(xt::has_shape(nodevec, {m_nnode, m_ndim})); Eigen::VectorXd dofval(m_ndof, 1); #pragma omp parallel for for (size_t m = 0; m < m_nnode; ++m) { for (size_t i = 0; i < m_ndim; ++i) { dofval(m_dofs(m, i)) = nodevec(m, i); } } return dofval; } template inline void Matrix::asNode(const Eigen::VectorXd& dofval, xt::xtensor& nodevec) const { GOOSEFEM_ASSERT(static_cast(dofval.size()) == m_ndof); GOOSEFEM_ASSERT(xt::has_shape(nodevec, {m_nnode, m_ndim})); #pragma omp parallel for for (size_t m = 0; m < m_nnode; ++m) { for (size_t i = 0; i < m_ndim; ++i) { nodevec(m, i) = dofval(m_dofs(m, i)); } } } } // namespace GooseFEM #endif