diff --git a/NumLib/Fem/FiniteElement/C0IsoparametricElements.h b/NumLib/Fem/FiniteElement/C0IsoparametricElements.h new file mode 100644 index 0000000000000000000000000000000000000000..7635f6ce50aec47d046c9cdeeadf00da17692119 --- /dev/null +++ b/NumLib/Fem/FiniteElement/C0IsoparametricElements.h @@ -0,0 +1,34 @@ +/** + * \author Norihiro Watanabe + * \date 2013-08-13 + * + * \copyright + * Copyright (c) 2013, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + * + */ + + +#ifndef C0ISOPARAMETRICELEMENTS_H_ +#define C0ISOPARAMETRICELEMENTS_H_ + +#include "MeshLib/Elements/Quad.h" +#include "NumLib/Fem/ShapeFunction/ShapeQuad4.h" +#include "NumLib/Fem/Integration/IntegrationGaussRegular.h" + +#include "TemplateIsoparametric.h" + +namespace NumLib +{ + +template <class T_SHAPE_VECTOR, class T_DSHAPE_MATRIX, class T_JACOBIAN_MATRIX> +struct FeQUAD4 +{ + typedef TemplateIsoparametric<MeshLib::Quad, ShapeQuad4, IntegrationGaussRegular<2>, T_SHAPE_VECTOR, T_DSHAPE_MATRIX, T_JACOBIAN_MATRIX> type; +}; + +} // NumLib + +#endif //C0ISOPARAMETRICELEMENTS_H_ diff --git a/NumLib/Fem/FiniteElement/TemplateIsoparametric.h b/NumLib/Fem/FiniteElement/TemplateIsoparametric.h new file mode 100644 index 0000000000000000000000000000000000000000..9e17f5339c00b9af1ea3c1650f07f4a989a7b028 --- /dev/null +++ b/NumLib/Fem/FiniteElement/TemplateIsoparametric.h @@ -0,0 +1,116 @@ +/** + * \author Norihiro Watanabe + * \date 2013-08-13 + * + * \copyright + * Copyright (c) 2013, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + * + */ + + +#ifndef TEMPLATEISOPARAMETRIC_H_ +#define TEMPLATEISOPARAMETRIC_H_ + + +#include <cassert> + +#include "../CoordinatesMapping/ShapeMatrices.h" +#include "../CoordinatesMapping/NaturalCoordinatesMapping.h" + +namespace NumLib +{ + +/** + * \brief Template class for isoparametric elements + * + * \tparam T_ELEMENT Mesh element class + * \tparam T_SHAPE Shape function + * \tparam T_INTEGRAL Integration method + * \tparam T_NODAL_VECTOR Nodal vector class + * \tparam T_DIM_NODAL_MATRIX Matrix class for a size of dim * nnodes + * \tparam T_DIM_MATRIX Matrix class for a size of dim * dim + */ +template < + class T_MESH_ELEMENT, + class T_SHAPE, + class T_INTEGRAL, + class T_NODAL_VECTOR, + class T_DIM_NODAL_MATRIX, + class T_DIM_MATRIX + > +class TemplateIsoparametric +{ +public: + typedef T_MESH_ELEMENT MeshElementType; + typedef T_SHAPE ShapeFunctionType; + typedef T_INTEGRAL IntegrationMethod; + typedef T_NODAL_VECTOR NodalVectorType; + typedef T_DIM_NODAL_MATRIX DimNodalMatrixType; + typedef T_DIM_MATRIX DimMatrixType; + typedef ShapeMatrices<NodalVectorType, DimNodalMatrixType, DimMatrixType> ShapeMatricesType; + typedef NaturalCoordinatesMapping<MeshElementType, ShapeFunctionType, ShapeMatricesType> NaturalCoordsMappingType; + + /** + * Constructor without specifying a mesh element. setMeshElement() must be called afterwards. + */ + TemplateIsoparametric() + : _ele(nullptr) + { + } + + /** + * Construct this object for the given mesh element. + * + * @param e Mesh element object + */ + TemplateIsoparametric(const MeshElementType &e) + : _ele(&e) + { + } + + ~TemplateIsoparametric() {} + + /// return current mesh element + const MeshElementType* getMeshElement() const {return _ele;} + + /// Sets the mesh element + void setMeshElement(const MeshElementType &e) + { + this->_ele = &e; + } + + /** + * compute shape functions + * + * @param natural_pt position in natural coordinates + * @param shape evaluated shape function matrices + */ + void computeShapeFunctions(const double *natural_pt, ShapeMatricesType &shape) const + { + NaturalCoordsMappingType::computeShapeMatrices(*_ele, natural_pt, shape); + } + + /** + * compute shape functions + * + * @tparam T_SHAPE_MATRIX_TYPE shape matrix types to be calculated + * @param natural_pt position in natural coordinates + * @param shape evaluated shape function matrices + */ + template <ShapeMatrixType T_SHAPE_MATRIX_TYPE> + void computeShapeFunctions(const double *natural_pt, ShapeMatricesType &shape) const + { + NaturalCoordsMappingType::template computeShapeMatrices<T_SHAPE_MATRIX_TYPE>(*_ele, natural_pt, shape); + } + + +private: + const MeshElementType* _ele; +}; + +} // NumLib + +#endif //TEMPLATEISOPARAMETRIC_H_ diff --git a/Tests/NumLib/TestFeQuad4.cpp b/Tests/NumLib/TestFeQuad4.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7bb94c75d0f0bd88364a7cfc1717a0091e363313 --- /dev/null +++ b/Tests/NumLib/TestFeQuad4.cpp @@ -0,0 +1,295 @@ +/** + * \file TestFEM.cpp + * \author Norihiro Watanabe + * \date 2012-08-03 + * + * \copyright + * Copyright (c) 2013, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + * + */ + +#include <gtest/gtest.h> + +#include <vector> +#include <cmath> +#include <Eigen/Eigen> + +#include "MeshLib/Elements/Quad.h" +#include "NumLib/Fem/CoordinatesMapping/ShapeMatrices.h" +#include "NumLib/Fem/FiniteElement/C0IsoparametricElements.h" + +#include "Tests/TestTools.h" + +using namespace NumLib; + +namespace +{ + +static const unsigned dim = 2; +static const unsigned e_nnodes = 4; + +template <class T_MATRIX_TYPES> +class NumLibFemIsoQuad4Test : public ::testing::Test +{ + public: + // Matrix types + typedef typename T_MATRIX_TYPES::NodalMatrixType NodalMatrix; + typedef typename T_MATRIX_TYPES::NodalVectorType NodalVector; + typedef typename T_MATRIX_TYPES::DimNodalMatrixType DimNodalMatrix; + typedef typename T_MATRIX_TYPES::DimMatrixType DimMatrix; + // Finite element type + typedef typename NumLib::FeQUAD4<NodalVector, DimNodalMatrix, DimMatrix>::type FeQUAD4Type; + // Shape matrix data type + typedef typename FeQUAD4Type::ShapeMatricesType ShapeMatricesType; + + public: + NumLibFemIsoQuad4Test() : + D(dim, dim), + expectedM(e_nnodes,e_nnodes), + expectedK(e_nnodes,e_nnodes), + integration_method(2) + { + // create a quad element used for testing + unitSquareQuad = createSquareQuad(1.0); + + // set a conductivity tensor + setIdentityMatrix(dim, D); + D *= conductivity; + + // set expected matrices + setExpectedMassMatrix(expectedM); + setExpectedLaplaceMatrix(conductivity, expectedK); + + // for destructor + vec_eles.push_back(unitSquareQuad); + for (auto e : vec_eles) + for (unsigned i=0; i<e->getNNodes(true); i++) + vec_nodes.push_back(e->getNode(i)); + } + + ~NumLibFemIsoQuad4Test() + { + for (auto itr = vec_nodes.begin(); itr!=vec_nodes.end(); ++itr ) + delete *itr; + for (auto itr = vec_eles.begin(); itr!=vec_eles.end(); ++itr ) + delete *itr; + } + + // Quad: square shape with a length of 1m + MeshLib::Quad* createSquareQuad(double h) + { + MeshLib::Node** nodes = new MeshLib::Node*[e_nnodes]; + nodes[0] = new MeshLib::Node(0.0, 0.0, 0.0); + nodes[1] = new MeshLib::Node( h, 0.0, 0.0); + nodes[2] = new MeshLib::Node( h, h, 0.0); + nodes[3] = new MeshLib::Node(0.0, h, 0.0); + return new MeshLib::Quad(nodes); + } + + // set an identity matrix + template <class T_MATRIX, typename ID_TYPE=signed> + void setIdentityMatrix(unsigned dim, T_MATRIX &m) const + { + for (unsigned i=0; i<dim; i++) + for (unsigned j=0; j<dim; j++) + m(i,j) = 0.0; + for (unsigned i=0; i<dim; i++) + m(i,i) = 1.0; + } + + // copy upper triangles to lower triangles in a matrix + template <class T_MATRIX, typename ID_TYPE=signed> + void copyUpperToLower(const ID_TYPE dim, T_MATRIX &m) const + { + for (ID_TYPE i=0; i<dim; i++) + for (ID_TYPE j=0; j<i; j++) + m(i,j) = m(j,i); + } + + // set an expected mass matrix for 1m x 1m + template <class T_MATRIX, typename ID_TYPE=signed> + void setExpectedMassMatrix(T_MATRIX &m) + { + // set upper triangle entries + m(0,0) = 1.0; m(0,1) = 1./2; m(0,2) = 1./4; m(0,3) = 1./2; + m(1,1) = 1.0; m(1,2) = 1./2; m(1,3) = 1./4; + m(2,2) = 1.0; m(2,3) = 1./2; + m(3,3) = 1.0; + // make symmetric + copyUpperToLower(4, m); + m *= 1./9.; + } + + // set an expected laplace matrix for 1m x 1m + template <class T_MATRIX, typename ID_TYPE=signed> + void setExpectedLaplaceMatrix(double k, T_MATRIX &m) + { + // set upper triangle entries + m(0,0) = 4.0; m(0,1) = -1.0; m(0,2) = -2.0; m(0,3) = -1.0; + m(1,1) = 4.0; m(1,2) = -1.0; m(1,3) = -2.0; + m(2,2) = 4.0; m(2,3) = -1.0; + m(3,3) = 4.0; + // make symmetric + copyUpperToLower(4, m); + m *= k/6.; + } + + static const double conductivity; + static const double eps; + DimMatrix D; + NodalMatrix expectedM; + NodalMatrix expectedK; + typename FeQUAD4Type::IntegrationMethod integration_method; + + std::vector<const MeshLib::Node*> vec_nodes; + std::vector<const MeshLib::Quad*> vec_eles; + MeshLib::Quad* unitSquareQuad; + +}; // NumLibFemIsoQuad4Test + +template <class T_MATRIX_TYPES> +const double NumLibFemIsoQuad4Test<T_MATRIX_TYPES>::conductivity = 1e-11; + +template <class T_MATRIX_TYPES> +const double NumLibFemIsoQuad4Test<T_MATRIX_TYPES>::eps = std::numeric_limits<double>::epsilon(); + +} // namespace + +#ifdef OGS_USE_EIGEN + +struct EigenFixedMatrixTypes +{ + typedef Eigen::Matrix<double, e_nnodes, e_nnodes, Eigen::RowMajor> NodalMatrixType; + typedef Eigen::Matrix<double, e_nnodes, 1> NodalVectorType; + typedef Eigen::Matrix<double, dim, e_nnodes, Eigen::RowMajor> DimNodalMatrixType; + typedef Eigen::Matrix<double, dim, dim, Eigen::RowMajor> DimMatrixType; +}; + +struct EigenDynamicMatrixTypes +{ + typedef Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> NodalMatrixType; + typedef NodalMatrixType DimNodalMatrixType; + typedef NodalMatrixType DimMatrixType; + typedef Eigen::VectorXd NodalVectorType; +}; + +#endif // OGS_USE_EIGEN + +typedef ::testing::Types< +#ifdef OGS_USE_EIGEN + EigenFixedMatrixTypes + , EigenDynamicMatrixTypes +#endif + > MatrixTypes; + +TYPED_TEST_CASE(NumLibFemIsoQuad4Test, MatrixTypes); + +TYPED_TEST(NumLibFemIsoQuad4Test, CheckMassMatrix) +{ + // Refer to typedefs in the fixture + typedef typename TestFixture::FeQUAD4Type FeQUAD4Type; + typedef typename TestFixture::NodalMatrix NodalMatrix; + typedef typename TestFixture::ShapeMatricesType ShapeMatricesType; + + // create a finite element object + FeQUAD4Type fe(*this->unitSquareQuad); + + // evaluate a mass matrix M = int{ N^T D N }dA_e + NodalMatrix M(e_nnodes, e_nnodes); + ShapeMatricesType shape(dim, e_nnodes); + for (std::size_t i=0; i < this->integration_method.getNPoints(); i++) { + shape.setZero(); + auto wp = this->integration_method.getWeightedPoint(i); + fe.template computeShapeFunctions<ShapeMatrixType::N_J>(wp.getCoords(), shape); + M.noalias() += shape.N * shape.N.transpose() * shape.detJ * wp.getWeight(); + } + + ASSERT_ARRAY_NEAR(this->expectedM.data(), M.data(), M.size(), this->eps); +} + +TYPED_TEST(NumLibFemIsoQuad4Test, CheckLaplaceMatrix) +{ + // Refer to typedefs in the fixture + typedef typename TestFixture::FeQUAD4Type FeQUAD4Type; + typedef typename TestFixture::NodalMatrix NodalMatrix; + typedef typename TestFixture::ShapeMatricesType ShapeMatricesType; + + // create a finite element object + FeQUAD4Type fe(*this->unitSquareQuad); + + // evaluate a Laplace matrix K = int{ dNdx^T D dNdx }dA_e + NodalMatrix K(e_nnodes, e_nnodes); + ShapeMatricesType shape(dim, e_nnodes); + for (std::size_t i=0; i < this->integration_method.getNPoints(); i++) { + shape.setZero(); + auto wp = this->integration_method.getWeightedPoint(i); + fe.template computeShapeFunctions<ShapeMatrixType::DNDX>(wp.getCoords(), shape); + K.noalias() += shape.dNdx.transpose() * this->D * shape.dNdx * shape.detJ * wp.getWeight(); + } + ASSERT_ARRAY_NEAR(this->expectedK.data(), K.data(), K.size(), this->eps); +} + +TYPED_TEST(NumLibFemIsoQuad4Test, CheckMassLaplaceMatrices) +{ + // Refer to typedefs in the fixture + typedef typename TestFixture::FeQUAD4Type FeQUAD4Type; + typedef typename TestFixture::NodalMatrix NodalMatrix; + typedef typename TestFixture::ShapeMatricesType ShapeMatricesType; + + // create a finite element object + FeQUAD4Type fe(*this->unitSquareQuad); + + // evaluate both mass and laplace matrices at once + NodalMatrix M(e_nnodes, e_nnodes); + NodalMatrix K(e_nnodes, e_nnodes); + ShapeMatricesType shape(dim, e_nnodes); + for (std::size_t i=0; i < this->integration_method.getNPoints(); i++) { + shape.setZero(); + auto wp = this->integration_method.getWeightedPoint(i); + fe.computeShapeFunctions(wp.getCoords(), shape); + M.noalias() += shape.N * shape.N.transpose() * shape.detJ * wp.getWeight(); + K.noalias() += shape.dNdx.transpose() * this->D * shape.dNdx * shape.detJ * wp.getWeight(); + } + ASSERT_ARRAY_NEAR(this->expectedM.data(), M.data(), M.size(), this->eps); + ASSERT_ARRAY_NEAR(this->expectedK.data(), K.data(), K.size(), this->eps); +} + +TYPED_TEST(NumLibFemIsoQuad4Test, CheckGaussIntegrationLevel) +{ + // Refer to typedefs in the fixture + typedef typename TestFixture::FeQUAD4Type FeQUAD4Type; + typedef typename TestFixture::NodalMatrix NodalMatrix; + typedef typename TestFixture::ShapeMatricesType ShapeMatricesType; + + // create a finite element object with gauss quadrature level 2 + FeQUAD4Type fe(*this->unitSquareQuad); + + // evaluate a mass matrix + NodalMatrix M(e_nnodes, e_nnodes); + ShapeMatricesType shape(dim, e_nnodes); + ASSERT_EQ(4u, this->integration_method.getNPoints()); + for (std::size_t i=0; i < this->integration_method.getNPoints(); i++) { + shape.setZero(); + auto wp = this->integration_method.getWeightedPoint(i); + fe.computeShapeFunctions(wp.getCoords(), shape); + M.noalias() += shape.N * shape.N.transpose() * shape.detJ * wp.getWeight(); + } + ASSERT_ARRAY_NEAR(this->expectedM.data(), M.data(), M.size(), this->eps); + + // Change gauss quadrature level to 3 + this->integration_method.setIntegrationOrder(3); + M *= .0; + ASSERT_EQ(9u, this->integration_method.getNPoints()); + for (std::size_t i=0; i < this->integration_method.getNPoints(); i++) { + shape.setZero(); + auto wp = this->integration_method.getWeightedPoint(i); + fe.computeShapeFunctions(wp.getCoords(), shape); + M.noalias() += shape.N * shape.N.transpose() * shape.detJ * wp.getWeight(); + } + ASSERT_ARRAY_NEAR(this->expectedM.data(), M.data(), M.size(), this->eps); +} + +