// Copyright (c) 2010-2023, Lawrence Livermore National Security, LLC. Produced // at the Lawrence Livermore National Laboratory. All Rights reserved. See files // LICENSE and NOTICE for details. LLNL-CODE-806117. // // This file is part of the MFEM library. For more information and source code // availability visit https://mfem.org. // // MFEM is free software; you can redistribute it and/or modify it under the // terms of the BSD-3 license. We welcome feedback and contributions, see file // CONTRIBUTING.md for details. #ifndef MFEM_TEMPLATE_FINITE_ELEMENTS #define MFEM_TEMPLATE_FINITE_ELEMENTS #include "../config/tconfig.hpp" #include "fe_coll.hpp" namespace mfem { // Templated finite element classes, cf. fe.?pp /** @brief Store mass-like matrix B for each integration point on the reference element. For tensor product evaluation, this is only called on the 1D reference element, and higher dimensions are put together from that. The element mass matrix can be written \f$ M_E = B^T D_E B \f$ where the B built here is the B, and is unchanging across the mesh. The diagonal matrix \f$ D_E \f$ then contains all the element-specific geometry and physics data. @param fe the element we are calculating on @param ir the integration rule to calculate the shape matrix on @param B must be (nip x dof) with column major storage @param dof_map the inverse of dof_map is applied to reorder local dofs. */ template void CalcShapeMatrix(const FiniteElement &fe, const IntegrationRule &ir, real_t *B, const Array *dof_map = NULL) { // - B must be (nip x dof) with column major storage // - The inverse of dof_map is applied to reorder the local dofs. int nip = ir.GetNPoints(); int dof = fe.GetDof(); Vector shape(dof); for (int ip = 0; ip < nip; ip++) { fe.CalcShape(ir.IntPoint(ip), shape); for (int id = 0; id < dof; id++) { int orig_id = dof_map ? (*dof_map)[id] : id; B[ip+nip*id] = shape(orig_id); } } } /** @brief store gradient matrix G for each integration point on the reference element. For tensor product evaluation, this is only called on the 1D reference element, and higher dimensions are put together from that. The element stiffness matrix can be written \f[ S_E = \sum_{k=1}^{nq} G_{k,i}^T (D_E^G)_{k,k} G_{k,j} \f] where \f$ nq \f$ is the number of quadrature points, \f$ D_E^G \f$ contains all the information about the element geometry and coefficients (Jacobians etc.), and \f$ G \f$ is the matrix built in this routine, which is the same for all elements in a mesh. @param fe the element we are calculating on @param ir the integration rule to calculate the gradients on @param[out] G must be (nip x dim x dof) with column major storage @param[in] dof_map the inverse of dof_map is applied to reorder local dofs. */ template void CalcGradTensor(const FiniteElement &fe, const IntegrationRule &ir, real_t *G, const Array *dof_map = NULL) { // - G must be (nip x dim x dof) with column major storage // - The inverse of dof_map is applied to reorder the local dofs. int dim = fe.GetDim(); int nip = ir.GetNPoints(); int dof = fe.GetDof(); DenseMatrix dshape(dof, dim); for (int ip = 0; ip < nip; ip++) { fe.CalcDShape(ir.IntPoint(ip), dshape); for (int id = 0; id < dof; id++) { int orig_id = dof_map ? (*dof_map)[id] : id; for (int d = 0; d < dim; d++) { G[ip+nip*(d+dim*id)] = dshape(orig_id, d); } } } } template void CalcShapes(const FiniteElement &fe, const IntegrationRule &ir, real_t *B, real_t *G, const Array *dof_map) { if (B) { mfem::CalcShapeMatrix(fe, ir, B, dof_map); } if (G) { mfem::CalcGradTensor(fe, ir, G, dof_map); } } // H1 finite elements template class H1_FiniteElement; template class H1_FiniteElement { public: static const Geometry::Type geom = Geometry::SEGMENT; static const int dim = 1; static const int degree = P; static const int dofs = P+1; static const bool tensor_prod = true; static const int dofs_1d = P+1; // Type for run-time parameter for the constructor typedef int parameter_type; protected: const FiniteElement *my_fe; const Array *my_dof_map; parameter_type type; // run-time specified basis type void Init(const parameter_type type_) { type = type_; if (type == BasisType::Positive) { H1Pos_SegmentElement *fe = new H1Pos_SegmentElement(P); my_fe = fe; my_dof_map = &fe->GetDofMap(); } else { int pt_type = BasisType::GetQuadrature1D(type); H1_SegmentElement *fe = new H1_SegmentElement(P, pt_type); my_fe = fe; my_dof_map = &fe->GetDofMap(); } } public: H1_FiniteElement(const parameter_type type_ = BasisType::GaussLobatto) { Init(type_); } H1_FiniteElement(const FiniteElementCollection &fec) { const H1_FECollection *h1_fec = dynamic_cast(&fec); MFEM_ASSERT(h1_fec, "invalid FiniteElementCollection"); Init(h1_fec->GetBasisType()); } ~H1_FiniteElement() { delete my_fe; } template void CalcShapes(const IntegrationRule &ir, real_t *B, real_t *G) const { mfem::CalcShapes(*my_fe, ir, B, G, my_dof_map); } template void Calc1DShapes(const IntegrationRule &ir, real_t *B, real_t *G) const { CalcShapes(ir, B, G); } const Array *GetDofMap() const { return my_dof_map; } }; template class H1_FiniteElement { public: static const Geometry::Type geom = Geometry::TRIANGLE; static const int dim = 2; static const int degree = P; static const int dofs = ((P + 1)*(P + 2))/2; static const bool tensor_prod = false; // Type for run-time parameter for the constructor typedef int parameter_type; protected: const FiniteElement *my_fe; parameter_type type; // run-time specified basis type void Init(const parameter_type type_) { type = type_; if (type == BasisType::Positive) { my_fe = new H1Pos_TriangleElement(P); } else { int pt_type = BasisType::GetQuadrature1D(type); my_fe = new H1_TriangleElement(P, pt_type); } } public: H1_FiniteElement(const parameter_type type_ = BasisType::GaussLobatto) { Init(type_); } H1_FiniteElement(const FiniteElementCollection &fec) { const H1_FECollection *h1_fec = dynamic_cast(&fec); MFEM_ASSERT(h1_fec, "invalid FiniteElementCollection"); Init(h1_fec->GetBasisType()); } ~H1_FiniteElement() { delete my_fe; } template void CalcShapes(const IntegrationRule &ir, real_t *B, real_t *G) const { mfem::CalcShapes(*my_fe, ir, B, G, NULL); } const Array *GetDofMap() const { return NULL; } }; template class H1_FiniteElement { public: static const Geometry::Type geom = Geometry::SQUARE; static const int dim = 2; static const int degree = P; static const int dofs = (P+1)*(P+1); static const bool tensor_prod = true; static const int dofs_1d = P+1; // Type for run-time parameter for the constructor typedef int parameter_type; protected: const FiniteElement *my_fe, *my_fe_1d; const Array *my_dof_map; parameter_type type; // run-time specified basis type void Init(const parameter_type type_) { type = type_; if (type == BasisType::Positive) { H1Pos_QuadrilateralElement *fe = new H1Pos_QuadrilateralElement(P); my_fe = fe; my_dof_map = &fe->GetDofMap(); my_fe_1d = new L2Pos_SegmentElement(P); } else { int pt_type = BasisType::GetQuadrature1D(type); H1_QuadrilateralElement *fe = new H1_QuadrilateralElement(P, pt_type); my_fe = fe; my_dof_map = &fe->GetDofMap(); my_fe_1d = new L2_SegmentElement(P, pt_type); } } public: H1_FiniteElement(const parameter_type type_ = BasisType::GaussLobatto) { Init(type_); } H1_FiniteElement(const FiniteElementCollection &fec) { const H1_FECollection *h1_fec = dynamic_cast(&fec); MFEM_ASSERT(h1_fec, "invalid FiniteElementCollection"); Init(h1_fec->GetBasisType()); } ~H1_FiniteElement() { delete my_fe; delete my_fe_1d; } template void CalcShapes(const IntegrationRule &ir, real_t *B, real_t *G) const { mfem::CalcShapes(*my_fe, ir, B, G, my_dof_map); } template void Calc1DShapes(const IntegrationRule &ir, real_t *B, real_t *G) const { mfem::CalcShapes(*my_fe_1d, ir, B, G, NULL); } const Array *GetDofMap() const { return my_dof_map; } }; template class H1_FiniteElement { public: static const Geometry::Type geom = Geometry::TETRAHEDRON; static const int dim = 3; static const int degree = P; static const int dofs = ((P + 1)*(P + 2)*(P + 3))/6; static const bool tensor_prod = false; // Type for run-time parameter for the constructor typedef int parameter_type; protected: const FiniteElement *my_fe; parameter_type type; // run-time specified basis type void Init(const parameter_type type_) { type = type_; if (type == BasisType::Positive) { my_fe = new H1Pos_TetrahedronElement(P); } else { int pt_type = BasisType::GetQuadrature1D(type); my_fe = new H1_TetrahedronElement(P, pt_type); } } public: H1_FiniteElement(const parameter_type type_ = BasisType::GaussLobatto) { Init(type_); } H1_FiniteElement(const FiniteElementCollection &fec) { const H1_FECollection *h1_fec = dynamic_cast(&fec); MFEM_ASSERT(h1_fec, "invalid FiniteElementCollection"); Init(h1_fec->GetBasisType()); } ~H1_FiniteElement() { delete my_fe; } template void CalcShapes(const IntegrationRule &ir, real_t *B, real_t *G) const { mfem::CalcShapes(*my_fe, ir, B, G, NULL); } const Array *GetDofMap() const { return NULL; } }; template class H1_FiniteElement { public: static const Geometry::Type geom = Geometry::CUBE; static const int dim = 3; static const int degree = P; static const int dofs = (P+1)*(P+1)*(P+1); static const bool tensor_prod = true; static const int dofs_1d = P+1; // Type for run-time parameter for the constructor typedef int parameter_type; protected: const FiniteElement *my_fe, *my_fe_1d; const Array *my_dof_map; parameter_type type; // run-time specified basis type void Init(const parameter_type type_) { type = type_; if (type == BasisType::Positive) { H1Pos_HexahedronElement *fe = new H1Pos_HexahedronElement(P); my_fe = fe; my_dof_map = &fe->GetDofMap(); my_fe_1d = new L2Pos_SegmentElement(P); } else { int pt_type = BasisType::GetQuadrature1D(type); H1_HexahedronElement *fe = new H1_HexahedronElement(P, pt_type); my_fe = fe; my_dof_map = &fe->GetDofMap(); my_fe_1d = new L2_SegmentElement(P, pt_type); } } public: H1_FiniteElement(const parameter_type type_ = BasisType::GaussLobatto) { Init(type_); } H1_FiniteElement(const FiniteElementCollection &fec) { const H1_FECollection *h1_fec = dynamic_cast(&fec); MFEM_ASSERT(h1_fec, "invalid FiniteElementCollection"); Init(h1_fec->GetBasisType()); } ~H1_FiniteElement() { delete my_fe; delete my_fe_1d; } template void CalcShapes(const IntegrationRule &ir, real_t *B, real_t *G) const { mfem::CalcShapes(*my_fe, ir, B, G, my_dof_map); } template void Calc1DShapes(const IntegrationRule &ir, real_t *B, real_t *G) const { mfem::CalcShapes(*my_fe_1d, ir, B, G, NULL); } const Array *GetDofMap() const { return my_dof_map; } }; // L2 finite elements template class L2_FiniteElement_base { public: static const Geometry::Type geom = G; static const int dim = Geometry::Constants::Dimension; static const int degree = P; static const int dofs = DOFS; static const bool tensor_prod = TP; static const int dofs_1d = P+1; // Type for run-time parameter for the constructor typedef int parameter_type; protected: const FiniteElement *my_fe, *my_fe_1d; parameter_type type; // run-time specified basis type void Init(const parameter_type type_) { type = type_; switch (type) { case BasisType::Positive: my_fe = new L2Pos_FE_type(P); my_fe_1d = (TP && dim != 1) ? new L2Pos_SegmentElement(P) : NULL; break; default: int pt_type = BasisType::GetQuadrature1D(type); my_fe = new L2_FE_type(P, pt_type); my_fe_1d = (TP && dim != 1) ? new L2_SegmentElement(P, pt_type) : NULL; } } L2_FiniteElement_base(const parameter_type type) { Init(type); } L2_FiniteElement_base(const FiniteElementCollection &fec) { const L2_FECollection *l2_fec = dynamic_cast(&fec); MFEM_ASSERT(l2_fec, "invalid FiniteElementCollection"); Init(l2_fec->GetBasisType()); } ~L2_FiniteElement_base() { delete my_fe; delete my_fe_1d; } public: template void CalcShapes(const IntegrationRule &ir, real_t *B, real_t *Grad) const { mfem::CalcShapes(*my_fe, ir, B, Grad, NULL); } template void Calc1DShapes(const IntegrationRule &ir, real_t *B, real_t *Grad) const { mfem::CalcShapes(dim == 1 ? *my_fe : *my_fe_1d, ir, B, Grad, NULL); } const Array *GetDofMap() const { return NULL; } }; template class L2_FiniteElement; template class L2_FiniteElement : public L2_FiniteElement_base< Geometry::SEGMENT,P,L2_SegmentElement,L2Pos_SegmentElement,P+1,true> { protected: typedef L2_FiniteElement_base base_class; public: typedef typename base_class::parameter_type parameter_type; L2_FiniteElement(const parameter_type type_ = BasisType::GaussLegendre) : base_class(type_) { } L2_FiniteElement(const FiniteElementCollection &fec) : base_class(fec) { } }; template class L2_FiniteElement : public L2_FiniteElement_base { protected: typedef L2_FiniteElement_base base_class; public: typedef typename base_class::parameter_type parameter_type; L2_FiniteElement(const parameter_type type_ = BasisType::GaussLegendre) : base_class(type_) { } L2_FiniteElement(const FiniteElementCollection &fec) : base_class(fec) { } }; template class L2_FiniteElement : public L2_FiniteElement_base { protected: typedef L2_FiniteElement_base base_class; public: typedef typename base_class::parameter_type parameter_type; L2_FiniteElement(const parameter_type type_ = BasisType::GaussLegendre) : base_class(type_) { } L2_FiniteElement(const FiniteElementCollection &fec) : base_class(fec) { } }; template class L2_FiniteElement : public L2_FiniteElement_base { protected: typedef L2_FiniteElement_base base_class; public: typedef typename base_class::parameter_type parameter_type; L2_FiniteElement(const parameter_type type_ = BasisType::GaussLegendre) : base_class(type_) { } L2_FiniteElement(const FiniteElementCollection &fec) : base_class(fec) { } }; template class L2_FiniteElement : public L2_FiniteElement_base { protected: typedef L2_FiniteElement_base base_class; public: typedef typename base_class::parameter_type parameter_type; L2_FiniteElement(const parameter_type type_ = BasisType::GaussLegendre) : base_class(type_) { } L2_FiniteElement(const FiniteElementCollection &fec) : base_class(fec) { } }; } // namespace mfem #endif // MFEM_TEMPLATE_FINITE_ELEMENTS