diff --git a/docs/changelog.rst b/docs/changelog.rst index 69df605..30745d3 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,153 +1,186 @@ ********* Changelog ********* +v0.9.0 +====== + +API Changes +----------- + +* VectorPartitioned::asDofs(dofval_u, dofval_p, dofval) -> + VectorPartitioned::dofsFromParitioned(dofval_u, dofval_p, dofval) + +* VectorPartitioned::asNode(dofval_u, dofval_p, nodevec) -> + VectorPartitioned::nodeFromPartitioned(dofval_u, dofval_p, nodevec) + +* VectorPartitioned::asElement(dofval_u, dofval_p, elemvec) -> + VectorPartitioned::elementFromPartitioned(dofval_u, dofval_p, elemvec) + +* Version defines as replaced by ``#define GOOSEFEM_VERSION``, + added convenience methods ``GooseFEM::version()`` and ``GooseFEM::version_dependencies()``. + +Deprecating in next version +---------------------------- + +* VectorPartitioned::assembleDofs_u +* VectorPartitioned::assembleDofs_p +* Mesh::Renumber::get +* Mesh::Reordered::get + +Changes under the hood +---------------------- + +* Overloading from Vector (also in Python API) +* Overloading from QuadratureBase (also in Python API) +* Added doxygen docs (published to GitHub pages) + v0.8.6 ====== * String-define safety: stringification + unquoting. v0.8.2 ====== * Using setuptools_scm to manage version (#169) v0.8.1 ====== * Various documentation updates: using doxygen (e.g. #168, #167, #157, #150) * Adding autodocs using doxygen/breathe. * Adding autodocs Python API with references to the C++ docs. * Using GitHub pages for doxygen docs (#156, #155) * Adding version information (incl. git commit hash) (#166) * Adding GooseFEM::Element::Quad4::Quadrature::interp_N_vector * Generalizing GooseFEM::Mesh::Quad4::Map::FineLayer2Regular::mapToRegular * Generalising implementation: * Internally deriving from Vector * Python API: unifying Element * Python API: fixing overloaded methods * Removing internal use of deprecated method * Using "initQuadratureBase" in derived Quadrature classes * Introducing QuadratureBase class -> avoids copies of convenience functions * [CI] Using ctest command to improve output in case of test failure * Restructuring environment (#154) * Fixing readthedocs setup (#153) v0.8.0 ====== * [CI] Using gcc-8 * Adding Mesh::Quad4::FineLayer::elementsLayer * Stitch: Adding nodesets to example * Stitch: Adding hybrid example. Adding assertions. * Making API more functional * Adding Mesh::ManualStich * Adding Mesh::Stitch * Minor style update * [CMake] Minor updates in testing * [CI] improve comments (#142) * Combining tests MeshQuad4 (#141) * Using clang on Windows (#139) v0.7.0 ====== * Adding ``Mesh::Quad4::FineLayer::elementgrid_leftright`` v0.6.1 ====== * Minor bugfix ``Mesh::Quad4::FineLayer::elementgrid_around_ravel``: allowing huge sizes. v0.6.0 ====== * Adding ``Mesh::Quad4::FineLayer::elementgrid_around_ravel`` * ``FineLayer::elementgrid_ravel``: Adding test * Renaming ``elementMatrix`` -> ``elementgrid`` everywhere * Adding ``Mesh::Quad4::FineLayer::elementgrid_ravel`` * Adding ``GOOFEM_WIP_ASSERT`` to assert if code needs to be generalized * API change: renaming ``Mesh::Quad4::Regular::elementMatrix`` -> M``esh::Quad4::Regular::elementgrid``. v0.5.1 ====== * FineLayer - replica: bug-fix in size detection. * Updated examples to new GMat API. v0.5.0 ====== * Renaming ``MatrixDiagonal::AsDiagonal`` -> ``MatrixDiagonal::Todiagonal`` to maintain API consistency. * Adding ``Mesh::elemmap2nodemap``. Updating Python API. * Adding ``roll`` to FineLayer. * Adding ``Mesh::centers`` and ``Mesh::defaultElementType``. * Mapping connectivity on generating FineLayer-object. * Switching to new GMat API. * Solver: force factorization on the first call. * Sorting output of ``GooseFEM::Mesh::elem2node``. Adding checks. * Switched to GitHub CI. * Adding ``todense`` to sparse matrix classes. * Adding ``dot`` to ``MatrixPartitioned``. v0.4.2 ====== * CMake: using Eigen's CMake target. v0.4.1 ====== API additions ------------- * Added "AllocateElemmat". v0.4.0 ====== API additions ------------- * Added "AllocateQtensor", "AllocateQscalar", "AllocateDofval", "AllocateNodevec", "AllocateElemvec". API changes ----------- * Removing Paraview interface: replaced by external libraries "XDMFWrite_HighFive" and "XDMFWrite_h5py". * Element*: "dV" now only returns raw data, the "asTensor" member function (and free function) can be used to convert the 'qscalar' to a 'qtensor'. * Separating sparse solver in separate class to offer more flexibility in the future. * Adding "dot" to "Matrix". Other updates ------------- * Applying clang-format to source, python API, tests, and examples.. * Adding test GMatElastoPlasticQPot. * Adding test based on hybrid material definitions. * Formatting update: renaming all return variables "out" to "ret". * Correction zero allocation to allows for dofval.size() > nodevec.size() * Formatting update xt::amax and xt::sum. * Renaming private function to begin with caps when the function allocates its return data. * Reducing copies when using Eigen. * Reducing default size examples. * Supporting Windows (#87). * Removing xtensor_fixed. * Using xt::has_shape. diff --git a/docs/examples/statics/FixedDisplacements_LinearElastic/manual_partition.cpp b/docs/examples/statics/FixedDisplacements_LinearElastic/manual_partition.cpp index c1475a5..8a36871 100644 --- a/docs/examples/statics/FixedDisplacements_LinearElastic/manual_partition.cpp +++ b/docs/examples/statics/FixedDisplacements_LinearElastic/manual_partition.cpp @@ -1,156 +1,156 @@ #include #include #include #include int main() { // mesh // ---- // define mesh GooseFEM::Mesh::Quad4::Regular mesh(5, 5); // mesh dimensions size_t nelem = mesh.nelem(); size_t nne = mesh.nne(); size_t ndim = mesh.ndim(); // mesh definitions xt::xtensor coor = mesh.coor(); xt::xtensor conn = mesh.conn(); xt::xtensor dofs = mesh.dofs(); // node sets xt::xtensor nodesLft = mesh.nodesLeftEdge(); xt::xtensor nodesRgt = mesh.nodesRightEdge(); xt::xtensor nodesTop = mesh.nodesTopEdge(); xt::xtensor nodesBot = mesh.nodesBottomEdge(); // fixed displacements DOFs // ------------------------ xt::xtensor iip = xt::concatenate(xt::xtuple( xt::view(dofs, xt::keep(nodesRgt), 0), xt::view(dofs, xt::keep(nodesTop), 1), xt::view(dofs, xt::keep(nodesLft), 0), xt::view(dofs, xt::keep(nodesBot), 1))); // simulation variables // -------------------- // vector definition GooseFEM::VectorPartitioned vector(conn, dofs, iip); // allocate system matrix GooseFEM::MatrixPartitioned K(conn, dofs, iip); GooseFEM::MatrixPartitionedSolver<> Solver; // nodal quantities xt::xtensor disp = xt::zeros(coor.shape()); xt::xtensor fint = xt::zeros(coor.shape()); xt::xtensor fext = xt::zeros(coor.shape()); xt::xtensor fres = xt::zeros(coor.shape()); // DOF values xt::xtensor u_u = xt::zeros({vector.nnu()}); xt::xtensor fres_u = xt::zeros({vector.nnu()}); xt::xtensor fext_p = xt::zeros({vector.nnp()}); // element vectors xt::xtensor ue = xt::empty({nelem, nne, ndim}); xt::xtensor fe = xt::empty({nelem, nne, ndim}); xt::xtensor Ke = xt::empty({nelem, nne * ndim, nne * ndim}); // element/material definition // --------------------------- // element definition GooseFEM::Element::Quad4::QuadraturePlanar elem(vector.AsElement(coor)); size_t nip = elem.nip(); // material definition GMatElastic::Cartesian3d::Array<2> mat({nelem, nip}, 1.0, 1.0); // integration point tensors xt::xtensor Eps = xt::empty({nelem, nip, 3ul, 3ul}); xt::xtensor Sig = xt::empty({nelem, nip, 3ul, 3ul}); xt::xtensor C = xt::empty({nelem, nip, 3ul, 3ul, 3ul, 3ul}); // solve // ----- // strain vector.asElement(disp, ue); elem.symGradN_vector(ue, Eps); // stress & tangent mat.setStrain(Eps); mat.stress(Sig); mat.tangent(C); // internal force elem.int_gradN_dot_tensor2_dV(Sig, fe); vector.assembleNode(fe, fint); // stiffness matrix elem.int_gradN_dot_tensor4_dot_gradNT_dV(C, Ke); K.assemble(Ke); // set fixed displacements xt::xtensor u_p = xt::concatenate(xt::xtuple( +0.1 * xt::ones({nodesRgt.size()}), -0.1 * xt::ones({nodesTop.size()}), xt::zeros({nodesLft.size()}), xt::zeros({nodesBot.size()}))); // residual xt::noalias(fres) = fext - fint; // partition vector.asDofs_u(fres, fres_u); // solve Solver.solve_u(K, fres_u, u_p, u_u); // assemble to nodal vector - vector.asNode(u_u, u_p, disp); + vector.nodeFromPartitioned(u_u, u_p, disp); // post-process // ------------ // compute strain and stress vector.asElement(disp, ue); elem.symGradN_vector(ue, Eps); mat.setStrain(Eps); mat.stress(Sig); // internal force elem.int_gradN_dot_tensor2_dV(Sig, fe); vector.assembleNode(fe, fint); // apply reaction force vector.asDofs_p(fint, fext_p); // residual xt::noalias(fres) = fext - fint; // partition vector.asDofs_u(fres, fres_u); // print residual std::cout << xt::sum(xt::abs(fres_u))[0] / xt::sum(xt::abs(fext_p))[0] << std::endl; // average stress per node xt::xtensor dV = elem.AsTensor<2>(elem.dV()); xt::xtensor SigAv = xt::average(Sig, dV, {1}); // write output H5Easy::File file("output.h5", H5Easy::File::Overwrite); H5Easy::dump(file, "/coor", coor); H5Easy::dump(file, "/conn", conn); H5Easy::dump(file, "/disp", disp); H5Easy::dump(file, "/Sig", SigAv); return 0; } diff --git a/include/GooseFEM/VectorPartitioned.h b/include/GooseFEM/VectorPartitioned.h index e97b0b2..f91ec71 100644 --- a/include/GooseFEM/VectorPartitioned.h +++ b/include/GooseFEM/VectorPartitioned.h @@ -1,143 +1,151 @@ /* (c - GPLv3) T.W.J. de Geus (Tom) | tom@geus.me | www.geus.me | github.com/tdegeus/GooseFEM */ #ifndef GOOSEFEM_VECTORPARTITIONED_H #define GOOSEFEM_VECTORPARTITIONED_H #include "config.h" #include "Vector.h" namespace GooseFEM { /* "nodevec" - nodal vectors - [nnode, ndim] "elemvec" - nodal vectors stored per element - [nelem, nne, ndim] "dofval" - DOF values - [ndof] "dofval_u" - DOF values (Unknown) "== dofval[iiu]" - [nnu] "dofval_p" - DOF values (Prescribed) "== dofval[iiu]" - [nnp] */ class VectorPartitioned : public Vector { public: - // making overloads from parent visible - using Vector::asDofs; - using Vector::asNode; - using Vector::asElement; - using Vector::AsDofs; - using Vector::AsNode; - using Vector::AsElement; - // Constructor VectorPartitioned() = default; VectorPartitioned( const xt::xtensor& conn, const xt::xtensor& dofs, const xt::xtensor& iip); // Dimensions size_t nnu() const; // number of unknown DOFs size_t nnp() const; // number of prescribed DOFs // DOF lists xt::xtensor iiu() const; // unknown DOFs xt::xtensor iip() const; // prescribed DOFs // Copy (part of) nodevec/dofval to another nodevec/dofval void copy_u( const xt::xtensor& nodevec_src, xt::xtensor& nodevec_dest) const; // "iiu" updated void copy_p( const xt::xtensor& nodevec_src, xt::xtensor& nodevec_dest) const; // "iip" updated // Convert to "dofval" (overwrite entries that occur more than once) - void asDofs( + void dofsFromParitioned( const xt::xtensor& dofval_u, const xt::xtensor& dofval_p, xt::xtensor& dofval) const; void asDofs_u(const xt::xtensor& dofval, xt::xtensor& dofval_u) const; void asDofs_u(const xt::xtensor& nodevec, xt::xtensor& dofval_u) const; void asDofs_u(const xt::xtensor& elemvec, xt::xtensor& dofval_u) const; void asDofs_p(const xt::xtensor& dofval, xt::xtensor& dofval_p) const; void asDofs_p(const xt::xtensor& nodevec, xt::xtensor& dofval_p) const; void asDofs_p(const xt::xtensor& elemvec, xt::xtensor& dofval_p) const; // Convert to "nodevec" (overwrite entries that occur more than once) -- (auto allocation below) - void asNode( + void nodeFromPartitioned( const xt::xtensor& dofval_u, const xt::xtensor& dofval_p, xt::xtensor& nodevec) const; // Convert to "elemvec" (overwrite entries that occur more than once) -- (auto allocation below) - void asElement( + void elementFromPartitioned( const xt::xtensor& dofval_u, const xt::xtensor& dofval_p, xt::xtensor& elemvec) const; // Assemble "dofval" (adds entries that occur more that once) -- (auto allocation below) + + [[ deprecated ]] void assembleDofs_u(const xt::xtensor& nodevec, xt::xtensor& dofval_u) const; + + [[ deprecated ]] void assembleDofs_u(const xt::xtensor& elemvec, xt::xtensor& dofval_u) const; + + [[ deprecated ]] void assembleDofs_p(const xt::xtensor& nodevec, xt::xtensor& dofval_p) const; + + [[ deprecated ]] void assembleDofs_p(const xt::xtensor& elemvec, xt::xtensor& dofval_p) const; // Auto-allocation of the functions above - xt::xtensor AsDofs( + xt::xtensor DofsFromParitioned( const xt::xtensor& dofval_u, const xt::xtensor& dofval_p) const; xt::xtensor AsDofs_u(const xt::xtensor& dofval) const; xt::xtensor AsDofs_u(const xt::xtensor& nodevec) const; xt::xtensor AsDofs_u(const xt::xtensor& elemvec) const; xt::xtensor AsDofs_p(const xt::xtensor& dofval) const; xt::xtensor AsDofs_p(const xt::xtensor& nodevec) const; xt::xtensor AsDofs_p(const xt::xtensor& elemvec) const; - xt::xtensor AsNode( + xt::xtensor NodeFromPartitioned( const xt::xtensor& dofval_u, const xt::xtensor& dofval_p) const; - xt::xtensor AsElement( + xt::xtensor ElementFromPartitioned( const xt::xtensor& dofval_u, const xt::xtensor& dofval_p) const; + + [[ deprecated ]] xt::xtensor AssembleDofs_u(const xt::xtensor& nodevec) const; + + [[ deprecated ]] xt::xtensor AssembleDofs_u(const xt::xtensor& elemvec) const; + + [[ deprecated ]] xt::xtensor AssembleDofs_p(const xt::xtensor& nodevec) const; + + [[ deprecated ]] xt::xtensor AssembleDofs_p(const xt::xtensor& elemvec) const; xt::xtensor Copy_u( const xt::xtensor& nodevec_src, const xt::xtensor& nodevec_dest) const; /** Copy prescribed DOFs from an "src" to "dest". \param nodevec_src The input, from which to copy the prescribed DOFs. \param nodevec_dest The destination, to which to copy the prescribed DOFs. \returns The result after copying. */ xt::xtensor Copy_p( const xt::xtensor& nodevec_src, const xt::xtensor& nodevec_dest) const; protected: // Bookkeeping xt::xtensor m_iiu; // DOF-numbers that are unknown [nnu] xt::xtensor m_iip; // DOF-numbers that are prescribed [nnp] // DOFs per node, such that iiu = arange(nnu), iip = nnu + arange(nnp) xt::xtensor m_part; // Dimensions size_t m_nnu; // number of unknown DOFs size_t m_nnp; // number of prescribed DOFs }; } // namespace GooseFEM #include "VectorPartitioned.hpp" #endif diff --git a/include/GooseFEM/VectorPartitioned.hpp b/include/GooseFEM/VectorPartitioned.hpp index c9aa041..964e2a5 100644 --- a/include/GooseFEM/VectorPartitioned.hpp +++ b/include/GooseFEM/VectorPartitioned.hpp @@ -1,433 +1,433 @@ /* (c - GPLv3) T.W.J. de Geus (Tom) | tom@geus.me | www.geus.me | github.com/tdegeus/GooseFEM */ #ifndef GOOSEFEM_VECTORPARTITIONED_HPP #define GOOSEFEM_VECTORPARTITIONED_HPP #include "Mesh.h" #include "VectorPartitioned.h" namespace GooseFEM { inline VectorPartitioned::VectorPartitioned( const xt::xtensor& conn, const xt::xtensor& dofs, const xt::xtensor& iip) : Vector(conn, dofs), m_iip(iip) { m_iiu = xt::setdiff1d(m_dofs, m_iip); m_nnp = m_iip.size(); m_nnu = m_iiu.size(); m_part = Mesh::Reorder({m_iiu, m_iip}).apply(m_dofs); GOOSEFEM_ASSERT(xt::amax(m_iip)() <= xt::amax(m_dofs)()); } inline size_t VectorPartitioned::nnu() const { return m_nnu; } inline size_t VectorPartitioned::nnp() const { return m_nnp; } inline xt::xtensor VectorPartitioned::iiu() const { return m_iiu; } inline xt::xtensor VectorPartitioned::iip() const { return m_iip; } inline void VectorPartitioned::copy_u( const xt::xtensor& nodevec_src, xt::xtensor& nodevec_dest) const { GOOSEFEM_ASSERT(xt::has_shape(nodevec_src, {m_nnode, m_ndim})); GOOSEFEM_ASSERT(xt::has_shape(nodevec_dest, {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) { if (m_part(m, i) < m_nnu) { nodevec_dest(m, i) = nodevec_src(m, i); } } } } inline void VectorPartitioned::copy_p( const xt::xtensor& nodevec_src, xt::xtensor& nodevec_dest) const { GOOSEFEM_ASSERT(xt::has_shape(nodevec_src, {m_nnode, m_ndim})); GOOSEFEM_ASSERT(xt::has_shape(nodevec_dest, {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) { if (m_part(m, i) >= m_nnu) { nodevec_dest(m, i) = nodevec_src(m, i); } } } } -inline void VectorPartitioned::asDofs( +inline void VectorPartitioned::dofsFromParitioned( const xt::xtensor& dofval_u, const xt::xtensor& dofval_p, xt::xtensor& dofval) const { GOOSEFEM_ASSERT(dofval_u.size() == m_nnu); GOOSEFEM_ASSERT(dofval_p.size() == m_nnp); GOOSEFEM_ASSERT(dofval.size() == m_ndof); dofval.fill(0.0); #pragma omp parallel for for (size_t d = 0; d < m_nnu; ++d) { dofval(m_iiu(d)) = dofval_u(d); } #pragma omp parallel for for (size_t d = 0; d < m_nnp; ++d) { dofval(m_iip(d)) = dofval_p(d); } } inline void VectorPartitioned::asDofs_u( const xt::xtensor& dofval, xt::xtensor& dofval_u) const { GOOSEFEM_ASSERT(dofval.size() == m_ndof); GOOSEFEM_ASSERT(dofval_u.size() == m_nnu); #pragma omp parallel for for (size_t d = 0; d < m_nnu; ++d) { dofval_u(d) = dofval(m_iiu(d)); } } inline void VectorPartitioned::asDofs_u( const xt::xtensor& nodevec, xt::xtensor& dofval_u) const { GOOSEFEM_ASSERT(xt::has_shape(nodevec, {m_nnode, m_ndim})); GOOSEFEM_ASSERT(dofval_u.size() == m_nnu); dofval_u.fill(0.0); #pragma omp parallel for for (size_t m = 0; m < m_nnode; ++m) { for (size_t i = 0; i < m_ndim; ++i) { if (m_part(m, i) < m_nnu) { dofval_u(m_part(m, i)) = nodevec(m, i); } } } } inline void VectorPartitioned::asDofs_p( const xt::xtensor& dofval, xt::xtensor& dofval_p) const { GOOSEFEM_ASSERT(dofval.size() == m_ndof); GOOSEFEM_ASSERT(dofval_p.size() == m_nnp); #pragma omp parallel for for (size_t d = 0; d < m_nnp; ++d) { dofval_p(d) = dofval(m_iip(d)); } } inline void VectorPartitioned::asDofs_p( const xt::xtensor& nodevec, xt::xtensor& dofval_p) const { GOOSEFEM_ASSERT(xt::has_shape(nodevec, {m_nnode, m_ndim})); GOOSEFEM_ASSERT(dofval_p.size() == m_nnp); dofval_p.fill(0.0); #pragma omp parallel for for (size_t m = 0; m < m_nnode; ++m) { for (size_t i = 0; i < m_ndim; ++i) { if (m_part(m, i) >= m_nnu) { dofval_p(m_part(m, i) - m_nnu) = nodevec(m, i); } } } } inline void VectorPartitioned::asDofs_u( const xt::xtensor& elemvec, xt::xtensor& dofval_u) const { GOOSEFEM_ASSERT(xt::has_shape(elemvec, {m_nelem, m_nne, m_ndim})); GOOSEFEM_ASSERT(dofval_u.size() == m_nnu); dofval_u.fill(0.0); #pragma omp parallel for 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) { if (m_part(m_conn(e, m), i) < m_nnu) { dofval_u(m_part(m_conn(e, m), i)) = elemvec(e, m, i); } } } } } inline void VectorPartitioned::asDofs_p( const xt::xtensor& elemvec, xt::xtensor& dofval_p) const { GOOSEFEM_ASSERT(xt::has_shape(elemvec, {m_nelem, m_nne, m_ndim})); GOOSEFEM_ASSERT(dofval_p.size() == m_nnp); dofval_p.fill(0.0); #pragma omp parallel for 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) { if (m_part(m_conn(e, m), i) >= m_nnu) { dofval_p(m_part(m_conn(e, m), i) - m_nnu) = elemvec(e, m, i); } } } } } -inline void VectorPartitioned::asNode( +inline void VectorPartitioned::nodeFromPartitioned( const xt::xtensor& dofval_u, const xt::xtensor& dofval_p, xt::xtensor& nodevec) const { GOOSEFEM_ASSERT(dofval_u.size() == m_nnu); GOOSEFEM_ASSERT(dofval_p.size() == m_nnp); 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) { if (m_part(m, i) < m_nnu) { nodevec(m, i) = dofval_u(m_part(m, i)); } else { nodevec(m, i) = dofval_p(m_part(m, i) - m_nnu); } } } } -inline void VectorPartitioned::asElement( +inline void VectorPartitioned::elementFromPartitioned( const xt::xtensor& dofval_u, const xt::xtensor& dofval_p, xt::xtensor& elemvec) const { GOOSEFEM_ASSERT(dofval_u.size() == m_nnu); GOOSEFEM_ASSERT(dofval_p.size() == m_nnp); GOOSEFEM_ASSERT(xt::has_shape(elemvec, {m_nelem, m_nne, m_ndim})); #pragma omp parallel for 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) { if (m_part(m_conn(e, m), i) < m_nnu) { elemvec(e, m, i) = dofval_u(m_part(m_conn(e, m), i)); } else { elemvec(e, m, i) = dofval_p(m_part(m_conn(e, m), i) - m_nnu); } } } } } inline void VectorPartitioned::assembleDofs_u( const xt::xtensor& nodevec, xt::xtensor& dofval_u) const { GOOSEFEM_ASSERT(xt::has_shape(nodevec, {m_nnode, m_ndim})); GOOSEFEM_ASSERT(dofval_u.size() == m_nnu); dofval_u.fill(0.0); for (size_t m = 0; m < m_nnode; ++m) { for (size_t i = 0; i < m_ndim; ++i) { if (m_part(m, i) < m_nnu) { dofval_u(m_part(m, i)) += nodevec(m, i); } } } } inline void VectorPartitioned::assembleDofs_p( const xt::xtensor& nodevec, xt::xtensor& dofval_p) const { GOOSEFEM_ASSERT(xt::has_shape(nodevec, {m_nnode, m_ndim})); GOOSEFEM_ASSERT(dofval_p.size() == m_nnp); dofval_p.fill(0.0); for (size_t m = 0; m < m_nnode; ++m) { for (size_t i = 0; i < m_ndim; ++i) { if (m_part(m, i) >= m_nnu) { dofval_p(m_part(m, i) - m_nnu) += nodevec(m, i); } } } } inline void VectorPartitioned::assembleDofs_u( const xt::xtensor& elemvec, xt::xtensor& dofval_u) const { GOOSEFEM_ASSERT(xt::has_shape(elemvec, {m_nelem, m_nne, m_ndim})); GOOSEFEM_ASSERT(dofval_u.size() == m_nnu); dofval_u.fill(0.0); 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) { if (m_part(m_conn(e, m), i) < m_nnu) { dofval_u(m_part(m_conn(e, m), i)) += elemvec(e, m, i); } } } } } inline void VectorPartitioned::assembleDofs_p( const xt::xtensor& elemvec, xt::xtensor& dofval_p) const { GOOSEFEM_ASSERT(xt::has_shape(elemvec, {m_nelem, m_nne, m_ndim})); GOOSEFEM_ASSERT(dofval_p.size() == m_nnp); dofval_p.fill(0.0); 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) { if (m_part(m_conn(e, m), i) >= m_nnu) { dofval_p(m_part(m_conn(e, m), i) - m_nnu) += elemvec(e, m, i); } } } } } -inline xt::xtensor VectorPartitioned::AsDofs( +inline xt::xtensor VectorPartitioned::DofsFromParitioned( const xt::xtensor& dofval_u, const xt::xtensor& dofval_p) const { xt::xtensor dofval = xt::empty({m_ndof}); - this->asDofs(dofval_u, dofval_p, dofval); + this->dofsFromParitioned(dofval_u, dofval_p, dofval); return dofval; } inline xt::xtensor VectorPartitioned::AsDofs_u(const xt::xtensor& dofval) const { xt::xtensor dofval_u = xt::empty({m_nnu}); this->asDofs_u(dofval, dofval_u); return dofval_u; } inline xt::xtensor VectorPartitioned::AsDofs_u(const xt::xtensor& nodevec) const { xt::xtensor dofval_u = xt::empty({m_nnu}); this->asDofs_u(nodevec, dofval_u); return dofval_u; } inline xt::xtensor VectorPartitioned::AsDofs_p(const xt::xtensor& dofval) const { xt::xtensor dofval_p = xt::empty({m_nnp}); this->asDofs_p(dofval, dofval_p); return dofval_p; } inline xt::xtensor VectorPartitioned::AsDofs_p(const xt::xtensor& nodevec) const { xt::xtensor dofval_p = xt::empty({m_nnp}); this->asDofs_p(nodevec, dofval_p); return dofval_p; } inline xt::xtensor VectorPartitioned::AsDofs_u(const xt::xtensor& elemvec) const { xt::xtensor dofval_u = xt::empty({m_nnu}); this->asDofs_u(elemvec, dofval_u); return dofval_u; } inline xt::xtensor VectorPartitioned::AsDofs_p(const xt::xtensor& elemvec) const { xt::xtensor dofval_p = xt::empty({m_nnp}); this->asDofs_p(elemvec, dofval_p); return dofval_p; } -inline xt::xtensor VectorPartitioned::AsNode( +inline xt::xtensor VectorPartitioned::NodeFromPartitioned( const xt::xtensor& dofval_u, const xt::xtensor& dofval_p) const { xt::xtensor nodevec = xt::empty({m_nnode, m_ndim}); - this->asNode(dofval_u, dofval_p, nodevec); + this->nodeFromPartitioned(dofval_u, dofval_p, nodevec); return nodevec; } -inline xt::xtensor VectorPartitioned::AsElement( +inline xt::xtensor VectorPartitioned::ElementFromPartitioned( const xt::xtensor& dofval_u, const xt::xtensor& dofval_p) const { xt::xtensor elemvec = xt::empty({m_nelem, m_nne, m_ndim}); - this->asElement(dofval_u, dofval_p, elemvec); + this->elementFromPartitioned(dofval_u, dofval_p, elemvec); return elemvec; } inline xt::xtensor VectorPartitioned::AssembleDofs_u(const xt::xtensor& nodevec) const { xt::xtensor dofval_u = xt::empty({m_nnu}); this->assembleDofs_u(nodevec, dofval_u); return dofval_u; } inline xt::xtensor VectorPartitioned::AssembleDofs_p(const xt::xtensor& nodevec) const { xt::xtensor dofval_p = xt::empty({m_nnp}); this->assembleDofs_p(nodevec, dofval_p); return dofval_p; } inline xt::xtensor VectorPartitioned::AssembleDofs_u(const xt::xtensor& elemvec) const { xt::xtensor dofval_u = xt::empty({m_nnu}); this->assembleDofs_u(elemvec, dofval_u); return dofval_u; } inline xt::xtensor VectorPartitioned::AssembleDofs_p(const xt::xtensor& elemvec) const { xt::xtensor dofval_p = xt::empty({m_nnp}); this->assembleDofs_p(elemvec, dofval_p); return dofval_p; } inline xt::xtensor VectorPartitioned::Copy_u( const xt::xtensor& nodevec_src, const xt::xtensor& nodevec_dest) const { xt::xtensor ret = nodevec_dest; this->copy_u(nodevec_src, ret); return ret; } inline xt::xtensor VectorPartitioned::Copy_p( const xt::xtensor& nodevec_src, const xt::xtensor& nodevec_dest) const { xt::xtensor ret = nodevec_dest; this->copy_p(nodevec_src, ret); return ret; } } // namespace GooseFEM #endif diff --git a/python/VectorPartitioned.hpp b/python/VectorPartitioned.hpp index a285073..2faedab 100644 --- a/python/VectorPartitioned.hpp +++ b/python/VectorPartitioned.hpp @@ -1,250 +1,123 @@ /* ================================================================================================= (c - GPLv3) T.W.J. de Geus (Tom) | tom@geus.me | www.geus.me | github.com/tdegeus/GooseFEM ================================================================================================= */ #include #include #include namespace py = pybind11; void init_VectorPartitioned(py::module& m) { py::class_(m, "VectorPartitioned") .def( py::init< const xt::xtensor&, const xt::xtensor&, const xt::xtensor&>(), "Switch between dofval/nodevec/elemvec", py::arg("conn"), py::arg("dofs"), py::arg("iip")) .def("nnu", &GooseFEM::VectorPartitioned::nnu, "Number of unknown degrees-of-freedom") .def("nnp", &GooseFEM::VectorPartitioned::nnp, "Number of prescribed degrees-of-freedom") .def("iiu", &GooseFEM::VectorPartitioned::iiu, "Return unknown degrees-of-freedom") .def("iip", &GooseFEM::VectorPartitioned::iip, "Return prescribed degrees-of-freedom") .def( - "AsDofs", + "DofsFromParitioned", py::overload_cast&, const xt::xtensor&>( - &GooseFEM::VectorPartitioned::AsDofs, py::const_), + &GooseFEM::VectorPartitioned::DofsFromParitioned, py::const_), "Set 'dofval", py::arg("dofval_u"), py::arg("dofval_p")) .def( "AsDofs_u", py::overload_cast&>( &GooseFEM::VectorPartitioned::AsDofs_u, py::const_), "Set 'dofval", py::arg("nodevec")) .def( "AsDofs_u", py::overload_cast&>( &GooseFEM::VectorPartitioned::AsDofs_u, py::const_), "Set 'dofval", py::arg("elemvec")) .def( "AsDofs_p", py::overload_cast&>( &GooseFEM::VectorPartitioned::AsDofs_p, py::const_), "Set 'dofval", py::arg("nodevec")) .def( "AsDofs_p", py::overload_cast&>( &GooseFEM::VectorPartitioned::AsDofs_p, py::const_), "Set 'dofval", py::arg("elemvec")) .def( - "AsNode", + "NodeFromPartitioned", py::overload_cast&, const xt::xtensor&>( - &GooseFEM::VectorPartitioned::AsNode, py::const_), + &GooseFEM::VectorPartitioned::NodeFromPartitioned, py::const_), "Set 'nodevec", py::arg("dofval_u"), py::arg("dofval_p")) .def( - "AsElement", + "ElementFromPartitioned", py::overload_cast&, const xt::xtensor&>( - &GooseFEM::VectorPartitioned::AsElement, py::const_), + &GooseFEM::VectorPartitioned::ElementFromPartitioned, py::const_), "Set 'elemvec", py::arg("dofval_u"), py::arg("dofval_p")) .def( "AssembleDofs_u", py::overload_cast&>( &GooseFEM::VectorPartitioned::AssembleDofs_u, py::const_), "Assemble 'dofval'", py::arg("nodevec")) .def( "AssembleDofs_u", py::overload_cast&>( &GooseFEM::VectorPartitioned::AssembleDofs_u, py::const_), "Assemble 'dofval'", py::arg("elemvec")) .def( "AssembleDofs_p", py::overload_cast&>( &GooseFEM::VectorPartitioned::AssembleDofs_p, py::const_), "Assemble 'dofval'", py::arg("nodevec")) .def( "AssembleDofs_p", py::overload_cast&>( &GooseFEM::VectorPartitioned::AssembleDofs_p, py::const_), "Assemble 'dofval'", py::arg("elemvec")) .def("Copy_u", &GooseFEM::VectorPartitioned::Copy_u, "Copy iiu") .def("Copy_p", &GooseFEM::VectorPartitioned::Copy_p, "Copy iip") - // Overloaded method from Vector - - .def("nelem", &GooseFEM::VectorPartitioned::nelem, "Number of element") - - .def("nne", &GooseFEM::VectorPartitioned::nne, "Number of nodes per element") - - .def("nnode", &GooseFEM::VectorPartitioned::nnode, "Number of nodes") - - .def("ndim", &GooseFEM::VectorPartitioned::ndim, "Number of dimensions") - - .def("ndof", &GooseFEM::VectorPartitioned::ndof, "Number of degrees-of-freedom") - - .def("dofs", &GooseFEM::VectorPartitioned::dofs, "Return degrees-of-freedom") - - .def( - "Copy", - &GooseFEM::Vector::Copy, - py::arg("nodevec_src"), - py::arg("nodevec_dest")) - - .def( - "AsDofs", - py::overload_cast&>(&GooseFEM::Vector::AsDofs, py::const_), - "Set 'dofval", - py::arg("nodevec")) - - .def( - "AsDofs", - py::overload_cast&>(&GooseFEM::Vector::AsDofs, py::const_), - "Set 'dofval", - py::arg("elemvec")) - - .def( - "AsNode", - py::overload_cast&>(&GooseFEM::Vector::AsNode, py::const_), - "Set 'nodevec", - py::arg("dofval")) - - .def( - "AsNode", - py::overload_cast&>(&GooseFEM::Vector::AsNode, py::const_), - "Set 'nodevec", - py::arg("elemvec")) - - .def( - "AsElement", - py::overload_cast&>( - &GooseFEM::Vector::AsElement, py::const_), - "Set 'elemvec", - py::arg("dofval")) - - .def( - "AsElement", - py::overload_cast&>( - &GooseFEM::Vector::AsElement, py::const_), - "Set 'elemvec", - py::arg("nodevec")) - - .def( - "AssembleDofs", - py::overload_cast&>( - &GooseFEM::Vector::AssembleDofs, py::const_), - "Assemble 'dofval'", - py::arg("nodevec")) - - .def( - "AssembleDofs", - py::overload_cast&>( - &GooseFEM::Vector::AssembleDofs, py::const_), - "Assemble 'dofval'", - py::arg("elemvec")) - - .def( - "AssembleNode", - py::overload_cast&>( - &GooseFEM::Vector::AssembleNode, py::const_), - "Assemble 'nodevec'", - py::arg("elemvec")) - - .def( - "ShapeDofval", - &GooseFEM::Vector::ShapeDofval) - - .def( - "ShapeNodevec", - &GooseFEM::Vector::ShapeNodevec) - - .def( - "ShapeElemvec", - &GooseFEM::Vector::ShapeElemvec) - - .def( - "ShapeElemmat", - &GooseFEM::Vector::ShapeElemmat) - - .def( - "AllocateDofval", - py::overload_cast<>(&GooseFEM::Vector::AllocateDofval, py::const_)) - - .def( - "AllocateDofval", - py::overload_cast(&GooseFEM::Vector::AllocateDofval, py::const_)) - - .def( - "AllocateNodevec", - py::overload_cast<>(&GooseFEM::Vector::AllocateNodevec, py::const_)) - - .def( - "AllocateNodevec", - py::overload_cast(&GooseFEM::Vector::AllocateNodevec, py::const_)) - - .def( - "AllocateElemvec", - py::overload_cast<>(&GooseFEM::Vector::AllocateElemvec, py::const_)) - - .def( - "AllocateElemvec", - py::overload_cast(&GooseFEM::Vector::AllocateElemvec, py::const_)) - - .def( - "AllocateElemmat", - py::overload_cast<>(&GooseFEM::Vector::AllocateElemmat, py::const_)) - - .def( - "AllocateElemmat", - py::overload_cast(&GooseFEM::Vector::AllocateElemmat, py::const_)) - .def("__repr__", [](const GooseFEM::VectorPartitioned&) { return ""; }); } diff --git a/python/VectorPartitionedTyings.hpp b/python/VectorPartitionedTyings.hpp index e0d0ffa..a41fbce 100644 --- a/python/VectorPartitionedTyings.hpp +++ b/python/VectorPartitionedTyings.hpp @@ -1,184 +1,57 @@ /* ================================================================================================= (c - GPLv3) T.W.J. de Geus (Tom) | tom@geus.me | www.geus.me | github.com/tdegeus/GooseFEM ================================================================================================= */ #include #include #include namespace py = pybind11; void init_VectorPartitionedTyings(py::module& m) { py::class_(m, "VectorPartitionedTyings") .def( py::init< const xt::xtensor&, const xt::xtensor&, const Eigen::SparseMatrix&, const Eigen::SparseMatrix&, const Eigen::SparseMatrix&>(), "Switch between dofval/nodevec/elemvec", py::arg("conn"), py::arg("dofs"), py::arg("Cdu"), py::arg("Cdp"), py::arg("Cdi")) .def("nnu", &GooseFEM::VectorPartitionedTyings::nnu, "Number of unknown DOFs") .def("nnp", &GooseFEM::VectorPartitionedTyings::nnp, "Number of prescribed DOFs") .def("nni", &GooseFEM::VectorPartitionedTyings::nni, "Number of independent DOFs") .def("nnd", &GooseFEM::VectorPartitionedTyings::nnd, "Number of dependent DOFs") .def("iiu", &GooseFEM::VectorPartitionedTyings::iiu, "Unknown DOFs") .def("iip", &GooseFEM::VectorPartitionedTyings::iip, "Prescribed DOFs") .def("iii", &GooseFEM::VectorPartitionedTyings::iii, "Independent DOFs") .def("iid", &GooseFEM::VectorPartitionedTyings::iid, "Dependent DOFs") .def( "AsDofs_i", &GooseFEM::VectorPartitionedTyings::AsDofs_i, "Set 'dofval", py::arg("nodevec")) - // Overloaded methods from Vector - - .def("nelem", &GooseFEM::Vector::nelem, "Number of element") - - .def("nne", &GooseFEM::Vector::nne, "Number of nodes per element") - - .def("nnode", &GooseFEM::Vector::nnode, "Number of nodes") - - .def("ndim", &GooseFEM::Vector::ndim, "Number of dimensions") - - .def("ndof", &GooseFEM::Vector::ndof, "Number of degrees-of-freedom") - - .def("dofs", &GooseFEM::Vector::dofs, "Return degrees-of-freedom") - - .def( - "Copy", - &GooseFEM::Vector::Copy, - py::arg("nodevec_src"), - py::arg("nodevec_dest")) - - .def( - "AsDofs", - py::overload_cast&>(&GooseFEM::Vector::AsDofs, py::const_), - "Set 'dofval", - py::arg("nodevec")) - - .def( - "AsDofs", - py::overload_cast&>(&GooseFEM::Vector::AsDofs, py::const_), - "Set 'dofval", - py::arg("elemvec")) - - .def( - "AsNode", - py::overload_cast&>(&GooseFEM::Vector::AsNode, py::const_), - "Set 'nodevec", - py::arg("dofval")) - - .def( - "AsNode", - py::overload_cast&>(&GooseFEM::Vector::AsNode, py::const_), - "Set 'nodevec", - py::arg("elemvec")) - - .def( - "AsElement", - py::overload_cast&>( - &GooseFEM::Vector::AsElement, py::const_), - "Set 'elemvec", - py::arg("dofval")) - - .def( - "AsElement", - py::overload_cast&>( - &GooseFEM::Vector::AsElement, py::const_), - "Set 'elemvec", - py::arg("nodevec")) - - .def( - "AssembleDofs", - py::overload_cast&>( - &GooseFEM::Vector::AssembleDofs, py::const_), - "Assemble 'dofval'", - py::arg("nodevec")) - - .def( - "AssembleDofs", - py::overload_cast&>( - &GooseFEM::Vector::AssembleDofs, py::const_), - "Assemble 'dofval'", - py::arg("elemvec")) - - .def( - "AssembleNode", - py::overload_cast&>( - &GooseFEM::Vector::AssembleNode, py::const_), - "Assemble 'nodevec'", - py::arg("elemvec")) - - .def( - "ShapeDofval", - &GooseFEM::Vector::ShapeDofval) - - .def( - "ShapeNodevec", - &GooseFEM::Vector::ShapeNodevec) - - .def( - "ShapeElemvec", - &GooseFEM::Vector::ShapeElemvec) - - .def( - "ShapeElemmat", - &GooseFEM::Vector::ShapeElemmat) - - .def( - "AllocateDofval", - py::overload_cast<>(&GooseFEM::Vector::AllocateDofval, py::const_)) - - .def( - "AllocateDofval", - py::overload_cast(&GooseFEM::Vector::AllocateDofval, py::const_)) - - .def( - "AllocateNodevec", - py::overload_cast<>(&GooseFEM::Vector::AllocateNodevec, py::const_)) - - .def( - "AllocateNodevec", - py::overload_cast(&GooseFEM::Vector::AllocateNodevec, py::const_)) - - .def( - "AllocateElemvec", - py::overload_cast<>(&GooseFEM::Vector::AllocateElemvec, py::const_)) - - .def( - "AllocateElemvec", - py::overload_cast(&GooseFEM::Vector::AllocateElemvec, py::const_)) - - .def( - "AllocateElemmat", - py::overload_cast<>(&GooseFEM::Vector::AllocateElemmat, py::const_)) - - .def( - "AllocateElemmat", - py::overload_cast(&GooseFEM::Vector::AllocateElemmat, py::const_)) - .def("__repr__", [](const GooseFEM::VectorPartitionedTyings&) { return ""; }); } diff --git a/test/basic/VectorPartitioned.cpp b/test/basic/VectorPartitioned.cpp index 9d06944..2322530 100644 --- a/test/basic/VectorPartitioned.cpp +++ b/test/basic/VectorPartitioned.cpp @@ -1,68 +1,68 @@ #include #include #include #include #define ISCLOSE(a,b) REQUIRE_THAT((a), Catch::WithinAbs((b), 1.e-12)); TEST_CASE("GooseFEM::VectorPartitioned", "VectorPartitioned.h") { SECTION("asDofs") { GooseFEM::Mesh::Quad4::Regular mesh(9, 9); auto dofs = mesh.dofs(); auto nodesLeft = mesh.nodesLeftOpenEdge(); auto nodesRight = mesh.nodesRightOpenEdge(); auto nodesTop = mesh.nodesTopEdge(); auto nodesBottom = mesh.nodesBottomEdge(); xt::view(dofs, xt::keep(nodesRight)) = xt::view(dofs, xt::keep(nodesLeft)); size_t ni = nodesBottom.size(); xt::xtensor iip = xt::empty({4 * ni}); xt::view(iip, xt::range(0 * ni, 1 * ni)) = xt::view(dofs, xt::keep(nodesTop), 0); xt::view(iip, xt::range(1 * ni, 2 * ni)) = xt::view(dofs, xt::keep(nodesTop), 1); xt::view(iip, xt::range(2 * ni, 3 * ni)) = xt::view(dofs, xt::keep(nodesBottom), 0); xt::view(iip, xt::range(3 * ni, 4 * ni)) = xt::view(dofs, xt::keep(nodesBottom), 1); xt::xtensor u = xt::random::randn({mesh.nnode(), mesh.ndim()}); xt::view(u, xt::keep(nodesRight)) = xt::view(u, xt::keep(nodesLeft)); GooseFEM::VectorPartitioned vector(mesh.conn(), dofs, iip); auto u_u = vector.AsDofs_u(u); auto u_p = vector.AsDofs_p(u); - REQUIRE(xt::allclose(u, vector.AsNode(u_u, u_p))); + REQUIRE(xt::allclose(u, vector.NodeFromPartitioned(u_u, u_p))); } SECTION("copy_u, copy_p") { GooseFEM::Mesh::Quad4::Regular mesh(9, 9); auto dofs = mesh.dofs(); auto nodesLeft = mesh.nodesLeftOpenEdge(); auto nodesRight = mesh.nodesRightOpenEdge(); auto nodesTop = mesh.nodesTopEdge(); auto nodesBottom = mesh.nodesBottomEdge(); xt::view(dofs, xt::keep(nodesRight)) = xt::view(dofs, xt::keep(nodesLeft)); size_t ni = nodesBottom.size(); xt::xtensor iip = xt::empty({4 * ni}); xt::view(iip, xt::range(0 * ni, 1 * ni)) = xt::view(dofs, xt::keep(nodesTop), 0); xt::view(iip, xt::range(1 * ni, 2 * ni)) = xt::view(dofs, xt::keep(nodesTop), 1); xt::view(iip, xt::range(2 * ni, 3 * ni)) = xt::view(dofs, xt::keep(nodesBottom), 0); xt::view(iip, xt::range(3 * ni, 4 * ni)) = xt::view(dofs, xt::keep(nodesBottom), 1); xt::xtensor u = xt::random::randn({mesh.nnode(), mesh.ndim()}); xt::view(u, xt::keep(nodesRight)) = xt::view(u, xt::keep(nodesLeft)); GooseFEM::VectorPartitioned vector(mesh.conn(), dofs, iip); xt::xtensor v = xt::empty({mesh.nnode(), mesh.ndim()}); vector.copy_u(u, v); vector.copy_p(u, v); REQUIRE(xt::allclose(u, v)); } }