diff --git a/include/GooseFEM/Matrix.h b/include/GooseFEM/Matrix.h index 1128efa..6798c0c 100644 --- a/include/GooseFEM/Matrix.h +++ b/include/GooseFEM/Matrix.h @@ -1,121 +1,121 @@ /* (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 { // forward declaration template class MatrixSolver; class Matrix { public: // Constructors Matrix() = default; Matrix(const xt::xtensor& conn, const xt::xtensor& dofs); // 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); // Overwrite with a dense (sub-) matrix void set( const xt::xtensor& rows, const xt::xtensor& cols, const xt::xtensor& matrix); // Add a dense (sub-) matrix to the current matrix void add( const xt::xtensor& rows, const xt::xtensor& cols, const xt::xtensor& matrix); // Return as dense matrix void todense(xt::xtensor& ret) const; // 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; // Auto-allocation of the functions above xt::xtensor Todense() const; xt::xtensor Dot(const xt::xtensor& x) const; xt::xtensor Dot(const xt::xtensor& x) const; private: // The matrix Eigen::SparseMatrix m_A; // Matrix entries std::vector> m_T; // Signal changes to data bool m_changed = true; // 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 // grant access to solver class template friend class MatrixSolver; // 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; }; template >> class MatrixSolver { public: // Constructors MatrixSolver() = default; // Solve // x_u = A_uu \ (b_u - A_up * x_p) void solve(Matrix& matrix, const xt::xtensor& b, xt::xtensor& x); void solve(Matrix& matrix, const xt::xtensor& b, xt::xtensor& x); // Auto-allocation of the functions above xt::xtensor Solve(Matrix& matrix, const xt::xtensor& b); xt::xtensor Solve(Matrix& matrix, const xt::xtensor& b); private: Solver m_solver; // solver - bool m_factor = false; // signal to force factorization + bool m_factor = true; // signal to force factorization void factorize(Matrix& matrix); // compute inverse (evaluated by "solve") }; } // namespace GooseFEM #include "Matrix.hpp" #endif diff --git a/include/GooseFEM/MatrixDiagonal.h b/include/GooseFEM/MatrixDiagonal.h index baaddde..ba276bf 100644 --- a/include/GooseFEM/MatrixDiagonal.h +++ b/include/GooseFEM/MatrixDiagonal.h @@ -1,83 +1,83 @@ /* (c - GPLv3) T.W.J. de Geus (Tom) | tom@geus.me | www.geus.me | github.com/tdegeus/GooseFEM */ #ifndef GOOSEFEM_MATRIXDIAGONAL_H #define GOOSEFEM_MATRIXDIAGONAL_H #include "config.h" namespace GooseFEM { class MatrixDiagonal { public: // Constructors MatrixDiagonal() = default; MatrixDiagonal(const xt::xtensor& conn, const xt::xtensor& dofs); // 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 // Set matrix components void set(const xt::xtensor& A); // assemble from matrices stored per element [nelem, nne*ndim, nne*ndim] // WARNING: ignores any off-diagonal terms 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 = A \ b void solve(const xt::xtensor& b, xt::xtensor& x); void solve(const xt::xtensor& b, xt::xtensor& x); // Return matrix as diagonal matrix (column) xt::xtensor AsDiagonal() const; // 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 diagonal matrix, and its inverse (re-used to solve different RHS) xt::xtensor m_A; xt::xtensor m_inv; // Signal changes to data compare to the last inverse - bool m_factor = false; + bool m_factor = true; // 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(); }; } // namespace GooseFEM #include "MatrixDiagonal.hpp" #endif diff --git a/include/GooseFEM/MatrixDiagonalPartitioned.h b/include/GooseFEM/MatrixDiagonalPartitioned.h index 3b9cde3..041ad9e 100644 --- a/include/GooseFEM/MatrixDiagonalPartitioned.h +++ b/include/GooseFEM/MatrixDiagonalPartitioned.h @@ -1,142 +1,142 @@ /* (c - GPLv3) T.W.J. de Geus (Tom) | tom@geus.me | www.geus.me | github.com/tdegeus/GooseFEM */ #ifndef GOOSEFEM_MATRIXDIAGONALPARTITIONED_H #define GOOSEFEM_MATRIXDIAGONALPARTITIONED_H #include "config.h" namespace GooseFEM { class MatrixDiagonalPartitioned { public: // Constructors MatrixDiagonalPartitioned() = default; MatrixDiagonalPartitioned( const xt::xtensor& conn, const xt::xtensor& dofs, const xt::xtensor& iip); // 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 size_t nnu() const; // number of unknown DOFs size_t nnp() const; // number of prescribed DOFs // DOF lists xt::xtensor dofs() const; // DOFs xt::xtensor iiu() const; // unknown DOFs xt::xtensor iip() const; // prescribed DOFs // Assemble from matrices stored per element [nelem, nne*ndim, nne*ndim] // WARNING: ignores any off-diagonal terms 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; void dot_u( const xt::xtensor& x_u, const xt::xtensor& x_p, xt::xtensor& b_u) const; void dot_p( const xt::xtensor& x_u, const xt::xtensor& x_p, xt::xtensor& b_p) const; // Solve: // x_u = A_uu \ ( b_u - A_up * x_p ) = A_uu \ b_u void solve(const xt::xtensor& b, xt::xtensor& x); // modified with "x_u" void solve(const xt::xtensor& b, xt::xtensor& x); // modified with "x_u" void solve_u( const xt::xtensor& b_u, const xt::xtensor& x_p, xt::xtensor& x_u); // Get right-hand-size for corresponding to the prescribed DOFs: // b_p = A_pu * x_u + A_pp * x_p = A_pp * x_p void reaction( const xt::xtensor& x, xt::xtensor& b) const; // modified with "b_p" void reaction( const xt::xtensor& x, xt::xtensor& b) const; // modified with "b_p" void reaction_p( const xt::xtensor& x_u, const xt::xtensor& x_p, xt::xtensor& b_p) const; // Return matrix as diagonal matrix (column) xt::xtensor AsDiagonal() const; // Auto-allocation of the functions above xt::xtensor Dot(const xt::xtensor& x) const; xt::xtensor Dot(const xt::xtensor& x) const; xt::xtensor Dot_u( const xt::xtensor& x_u, const xt::xtensor& x_p) const; xt::xtensor Dot_p( const xt::xtensor& x_u, const xt::xtensor& x_p) const; xt::xtensor Solve(const xt::xtensor& b, const xt::xtensor& x); xt::xtensor Solve(const xt::xtensor& b, const xt::xtensor& x); xt::xtensor Solve_u( const xt::xtensor& b_u, const xt::xtensor& x_p); xt::xtensor Reaction( const xt::xtensor& x, const xt::xtensor& b) const; xt::xtensor Reaction( const xt::xtensor& x, const xt::xtensor& b) const; xt::xtensor Reaction_p( const xt::xtensor& x_u, const xt::xtensor& x_p) const; private: // The diagonal matrix, and its inverse (re-used to solve different RHS) xt::xtensor m_Auu; xt::xtensor m_App; xt::xtensor m_inv_uu; // Signal changes to data compare to the last inverse - bool m_factor = false; + bool m_factor = true; // Bookkeeping xt::xtensor m_conn; // connectivity [nelem, nne ] xt::xtensor m_dofs; // DOF-numbers per node [nnode, ndim] xt::xtensor m_part; // DOF-numbers per node, renumbered [nnode, ndim] xt::xtensor m_iiu; // DOF-numbers that are unknown [nnu] xt::xtensor m_iip; // DOF-numbers that are prescribed [nnp] // 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 size_t m_nnu; // number of unknown DOFs size_t m_nnp; // number of prescribed DOFs // Compute inverse (automatically evaluated by "solve") void factorize(); }; } // namespace GooseFEM #include "MatrixDiagonalPartitioned.hpp" #endif diff --git a/include/GooseFEM/MatrixPartitioned.h b/include/GooseFEM/MatrixPartitioned.h index ee7747f..4d4ca9b 100644 --- a/include/GooseFEM/MatrixPartitioned.h +++ b/include/GooseFEM/MatrixPartitioned.h @@ -1,185 +1,185 @@ /* (c - GPLv3) T.W.J. de Geus (Tom) | tom@geus.me | www.geus.me | github.com/tdegeus/GooseFEM */ #ifndef GOOSEFEM_MATRIXPARTITIONED_H #define GOOSEFEM_MATRIXPARTITIONED_H #include "config.h" #include #include #include namespace GooseFEM { // forward declaration template class MatrixPartitionedSolver; class MatrixPartitioned { public: // Constructors MatrixPartitioned() = default; MatrixPartitioned( const xt::xtensor& conn, const xt::xtensor& dofs, const xt::xtensor& iip); // 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 size_t nnu() const; // number of unknown DOFs size_t nnp() const; // number of prescribed DOFs // DOF lists xt::xtensor dofs() const; // DOFs xt::xtensor iiu() const; // unknown DOFs xt::xtensor iip() const; // prescribed DOFs // Assemble from matrices stored per element [nelem, nne*ndim, nne*ndim] void assemble(const xt::xtensor& elemmat); // Overwrite with a dense (sub-) matrix void set( const xt::xtensor& rows, const xt::xtensor& cols, const xt::xtensor& matrix); // Add a dense (sub-) matrix to the current matrix void add( const xt::xtensor& rows, const xt::xtensor& cols, const xt::xtensor& matrix); // Return as dense matrix void todense(xt::xtensor& ret) const; // 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; // Get right-hand-size for corresponding to the prescribed DOFs: // b_p = A_pu * x_u + A_pp * x_p = A_pp * x_p void reaction( const xt::xtensor& x, xt::xtensor& b) const; // modified with "b_p" void reaction( const xt::xtensor& x, xt::xtensor& b) const; // modified with "b_p" void reaction_p( const xt::xtensor& x_u, const xt::xtensor& x_p, xt::xtensor& b_p) const; // Auto-allocation of the functions above xt::xtensor Todense() const; xt::xtensor Dot(const xt::xtensor& x) const; xt::xtensor Dot(const xt::xtensor& x) const; xt::xtensor Reaction( const xt::xtensor& x, const xt::xtensor& b) const; xt::xtensor Reaction( const xt::xtensor& x, const xt::xtensor& b) const; xt::xtensor Reaction_p( const xt::xtensor& x_u, const xt::xtensor& x_p) const; private: // The matrix Eigen::SparseMatrix m_Auu; Eigen::SparseMatrix m_Aup; Eigen::SparseMatrix m_Apu; Eigen::SparseMatrix m_App; // Matrix entries std::vector> m_Tuu; std::vector> m_Tup; std::vector> m_Tpu; std::vector> m_Tpp; // Signal changes to data compare to the last inverse bool m_changed = true; // Bookkeeping xt::xtensor m_conn; // connectivity [nelem, nne ] xt::xtensor m_dofs; // DOF-numbers per node [nnode, ndim] xt::xtensor m_part; // DOF-numbers per node, renumbered [nnode, ndim] xt::xtensor m_iiu; // unknown DOFs [nnu] xt::xtensor m_iip; // prescribed DOFs [nnp] // 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 size_t m_nnu; // number of unknown DOFs size_t m_nnp; // number of prescribed DOFs // grant access to solver class template friend class MatrixPartitionedSolver; // Convert arrays (Eigen version of VectorPartitioned, which contains public functions) Eigen::VectorXd AsDofs_u(const xt::xtensor& dofval) const; Eigen::VectorXd AsDofs_u(const xt::xtensor& nodevec) const; Eigen::VectorXd AsDofs_p(const xt::xtensor& dofval) const; Eigen::VectorXd AsDofs_p(const xt::xtensor& nodevec) const; }; template >> class MatrixPartitionedSolver { public: // Constructors MatrixPartitionedSolver() = default; // Solve: // x_u = A_uu \ ( b_u - A_up * x_p ) void solve( MatrixPartitioned& matrix, const xt::xtensor& b, xt::xtensor& x); // modified with "x_u" void solve( MatrixPartitioned& matrix, const xt::xtensor& b, xt::xtensor& x); // modified with "x_u" void solve_u( MatrixPartitioned& matrix, const xt::xtensor& b_u, const xt::xtensor& x_p, xt::xtensor& x_u); // Auto-allocation of the functions above xt::xtensor Solve( MatrixPartitioned& matrix, const xt::xtensor& b, const xt::xtensor& x); xt::xtensor Solve( MatrixPartitioned& matrix, const xt::xtensor& b, const xt::xtensor& x); xt::xtensor Solve_u( MatrixPartitioned& matrix, const xt::xtensor& b_u, const xt::xtensor& x_p); private: Solver m_solver; // solver - bool m_factor = false; // signal to force factorization + bool m_factor = true; // signal to force factorization void factorize(MatrixPartitioned& matrix); // compute inverse (evaluated by "solve") }; } // namespace GooseFEM #include "MatrixPartitioned.hpp" #endif diff --git a/include/GooseFEM/MatrixPartitionedTyings.h b/include/GooseFEM/MatrixPartitionedTyings.h index 070cb41..edafddf 100644 --- a/include/GooseFEM/MatrixPartitionedTyings.h +++ b/include/GooseFEM/MatrixPartitionedTyings.h @@ -1,177 +1,177 @@ /* (c - GPLv3) T.W.J. de Geus (Tom) | tom@geus.me | www.geus.me | github.com/tdegeus/GooseFEM */ #ifndef GOOSEFEM_MATRIXPARTITIONEDTYINGS_H #define GOOSEFEM_MATRIXPARTITIONEDTYINGS_H #include "config.h" #include #include #include namespace GooseFEM { // forward declaration template class MatrixPartitionedTyingsSolver; class MatrixPartitionedTyings { public: // Constructors MatrixPartitionedTyings() = default; MatrixPartitionedTyings( const xt::xtensor& conn, const xt::xtensor& dofs, const Eigen::SparseMatrix& Cdu, const Eigen::SparseMatrix& Cdp); // 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 size_t nnu() const; // number of independent, unknown DOFs size_t nnp() const; // number of independent, prescribed DOFs size_t nni() const; // number of independent DOFs size_t nnd() const; // number of dependent DOFs // DOF lists xt::xtensor dofs() const; // DOFs xt::xtensor iiu() const; // independent, unknown DOFs xt::xtensor iip() const; // independent, prescribed DOFs xt::xtensor iii() const; // independent DOFs xt::xtensor iid() const; // dependent DOFs // Assemble from matrices stored per element [nelem, nne*ndim, nne*ndim] void assemble(const xt::xtensor& elemmat); private: // The matrix Eigen::SparseMatrix m_Auu; Eigen::SparseMatrix m_Aup; Eigen::SparseMatrix m_Apu; Eigen::SparseMatrix m_App; Eigen::SparseMatrix m_Aud; Eigen::SparseMatrix m_Apd; Eigen::SparseMatrix m_Adu; Eigen::SparseMatrix m_Adp; Eigen::SparseMatrix m_Add; // The matrix for which the tyings have been applied Eigen::SparseMatrix m_ACuu; Eigen::SparseMatrix m_ACup; Eigen::SparseMatrix m_ACpu; Eigen::SparseMatrix m_ACpp; // Matrix entries std::vector> m_Tuu; std::vector> m_Tup; std::vector> m_Tpu; std::vector> m_Tpp; std::vector> m_Tud; std::vector> m_Tpd; std::vector> m_Tdu; std::vector> m_Tdp; std::vector> m_Tdd; // Signal changes to data bool m_changed = true; // Bookkeeping xt::xtensor m_conn; // connectivity [nelem, nne ] xt::xtensor m_dofs; // DOF-numbers per node [nnode, ndim] xt::xtensor m_iiu; // unknown DOFs [nnu] xt::xtensor m_iip; // prescribed DOFs [nnp] xt::xtensor m_iid; // dependent DOFs [nnd] // 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 size_t m_nnu; // number of independent, unknown DOFs size_t m_nnp; // number of independent, prescribed DOFs size_t m_nni; // number of independent DOFs size_t m_nnd; // number of dependent DOFs // Tyings Eigen::SparseMatrix m_Cdu; Eigen::SparseMatrix m_Cdp; Eigen::SparseMatrix m_Cud; Eigen::SparseMatrix m_Cpd; // grant access to solver class template friend class MatrixPartitionedTyingsSolver; // Convert arrays (Eigen version of VectorPartitioned, which contains public functions) Eigen::VectorXd AsDofs_u(const xt::xtensor& dofval) const; Eigen::VectorXd AsDofs_u(const xt::xtensor& nodevec) const; Eigen::VectorXd AsDofs_p(const xt::xtensor& dofval) const; Eigen::VectorXd AsDofs_p(const xt::xtensor& nodevec) const; Eigen::VectorXd AsDofs_d(const xt::xtensor& dofval) const; Eigen::VectorXd AsDofs_d(const xt::xtensor& nodevec) const; }; template >> class MatrixPartitionedTyingsSolver { public: // Constructors MatrixPartitionedTyingsSolver() = default; // Solve: // A' = A_ii + K_id * C_di + C_di^T * K_di + C_di^T * K_dd * C_di // b' = b_i + C_di^T * b_d // x_u = A'_uu \ ( b'_u - A'_up * x_p ) // x_i = [x_u, x_p] // x_d = C_di * x_i void solve( MatrixPartitionedTyings& matrix, const xt::xtensor& b, xt::xtensor& x); // updates x_u and x_d void solve( MatrixPartitionedTyings& matrix, const xt::xtensor& b, xt::xtensor& x); // updates x_u and x_d void solve_u( MatrixPartitionedTyings& matrix, const xt::xtensor& b_u, const xt::xtensor& b_d, const xt::xtensor& x_p, xt::xtensor& x_u); // Auto-allocation of the functions above xt::xtensor Solve( MatrixPartitionedTyings& matrix, const xt::xtensor& b, const xt::xtensor& x); xt::xtensor Solve( MatrixPartitionedTyings& matrix, const xt::xtensor& b, const xt::xtensor& x); xt::xtensor Solve_u( MatrixPartitionedTyings& matrix, const xt::xtensor& b_u, const xt::xtensor& b_d, const xt::xtensor& x_p); private: Solver m_solver; // solver - bool m_factor = false; // signal to force factorization + bool m_factor = true; // signal to force factorization void factorize(MatrixPartitionedTyings& matrix); // compute inverse (evaluated by "solve") }; } // namespace GooseFEM #include "MatrixPartitionedTyings.hpp" #endif diff --git a/test/basic/MatrixParitioned.cpp b/test/basic/MatrixParitioned.cpp index 1af23e7..7a5736a 100644 --- a/test/basic/MatrixParitioned.cpp +++ b/test/basic/MatrixParitioned.cpp @@ -1,103 +1,110 @@ #include #include #include #include #include #define ISCLOSE(a,b) REQUIRE_THAT((a), Catch::WithinAbs((b), 1.e-12)); TEST_CASE("GooseFEM::MatrixPartitioned", "MatrixPartitioned.h") { SECTION("solve") { GooseFEM::Mesh::Quad4::Regular mesh(2, 2); size_t nne = mesh.nne(); size_t ndim = mesh.ndim(); size_t nelem = mesh.nelem(); size_t nnode = mesh.nnode(); auto dofs = mesh.dofs(); size_t npp = xt::amax(dofs)(); npp = (npp - npp % 2) / 2; xt::xtensor iip = xt::arange a = xt::empty({nelem, nne * ndim, nne * ndim}); xt::xtensor b = xt::random::rand({nnode * ndim}); for (size_t e = 0; e < nelem; ++e) { xt::xtensor ae = xt::random::rand({nne * ndim, nne * ndim}); ae = (ae + xt::transpose(ae)) / 2.0; xt::view(a, e, xt::all(), xt::all()) = ae; } GooseFEM::MatrixPartitioned A(mesh.conn(), dofs, iip); GooseFEM::MatrixPartitionedSolver<> Solver; A.assemble(a); xt::xtensor C = A.Dot(b); xt::xtensor B = Solver.Solve(A, C); REQUIRE(B.size() == b.size()); REQUIRE(xt::allclose(B, b)); + + // check that allocating a different Solver instance still works + GooseFEM::MatrixPartitionedSolver<> NewSolver; + xt::xtensor NB = NewSolver.Solve(A, C); + + REQUIRE(NB.size() == b.size()); + REQUIRE(xt::allclose(NB, b)); } SECTION("set/add/dot/solve - dofval") { xt::xtensor a = xt::random::rand({10, 10}); xt::xtensor x = xt::random::rand({10}); xt::xtensor b = xt::zeros({10}); xt::xtensor A = a + xt::transpose(a); for (size_t i = 0; i < A.shape(0); ++i) { for (size_t j = 0; j < A.shape(1); ++j) { b(i) += A(i, j) * x(j); } } xt::xtensor conn = xt::zeros({1, 5}); xt::xtensor dofs = xt::arange(10).reshape({5, 2}); xt::xtensor iip = xt::arange Solver; K.set(xt::arange(10), xt::arange(10), a); K.add(xt::arange(10), xt::arange(10), xt::transpose(a)); REQUIRE(xt::allclose(A, K.Todense())); REQUIRE(xt::allclose(b, K.Dot(x))); REQUIRE(xt::allclose(x, Solver.Solve(K, b))); } SECTION("set/add/dot/solve - nodevec") { xt::xtensor a = xt::random::rand({10, 10}); xt::xtensor x = xt::random::rand({5, 2}); xt::xtensor b = xt::zeros({5, 2}); xt::xtensor A = a + xt::transpose(a); for (size_t m = 0; m < x.shape(0); ++m) { for (size_t n = 0; n < x.shape(0); ++n) { for (size_t i = 0; i < x.shape(1); ++i) { for (size_t j = 0; j < x.shape(1); ++j) { b(m, i) += A(m * x.shape(1) + i, n * x.shape(1) + j) * x(n, j); } } } } xt::xtensor conn = xt::zeros({1, 5}); xt::xtensor dofs = xt::arange(10).reshape({5, 2}); xt::xtensor iip = xt::arange Solver; K.set(xt::arange(10), xt::arange(10), a); K.add(xt::arange(10), xt::arange(10), xt::transpose(a)); REQUIRE(xt::allclose(A, K.Todense())); REQUIRE(xt::allclose(b, K.Dot(x))); REQUIRE(xt::allclose(x, Solver.Solve(K, b))); } }