// 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. // Serendipity Finite Element classes #include "fe_ser.hpp" #include "fe_fixed_order.hpp" namespace mfem { using namespace std; H1Ser_QuadrilateralElement::H1Ser_QuadrilateralElement(const int p) : ScalarFiniteElement(2, Geometry::SQUARE, (p*p + 3*p +6) / 2, p, FunctionSpace::Qk) { // Store the dof_map of the associated TensorBasisElement, which will be used // to create the serendipity dof map. Its size is larger than the size of // the serendipity element. TensorBasisElement tbeTemp = TensorBasisElement(2, p, BasisType::GaussLobatto, TensorBasisElement::DofMapType::Sr_DOF_MAP); const Array tp_dof_map = tbeTemp.GetDofMap(); const double *cp = poly1d.ClosedPoints(p, BasisType::GaussLobatto); // Fixing the Nodes is exactly the same as the H1_QuadrilateralElement // constructor except we only use those values of the associated tensor // product dof_map that are <= the number of serendipity Dofs e.g. only DoFs // 0-7 out of the 9 tensor product dofs (at quadratic order) int o = 0; for (int j = 0; j <= p; j++) { for (int i = 0; i <= p; i++) { if (tp_dof_map[o] < Nodes.Size()) { Nodes.IntPoint(tp_dof_map[o]).x = cp[i]; Nodes.IntPoint(tp_dof_map[o]).y = cp[j]; } o++; } } } void H1Ser_QuadrilateralElement::CalcShape(const IntegrationPoint &ip, Vector &shape) const { int p = (this)->GetOrder(); double x = ip.x, y = ip.y; Poly_1D::Basis edgeNodalBasis(poly1d.GetBasis(p, BasisType::GaussLobatto)); Vector nodalX(p+1); Vector nodalY(p+1); edgeNodalBasis.Eval(x, nodalX); edgeNodalBasis.Eval(y, nodalY); // First, fix edge-based shape functions. Use a nodal interpolant for edge // points, weighted by the linear function that vanishes on opposite edge. for (int i = 0; i < p-1; i++) { shape(4 + 0*(p-1) + i) = (nodalX(i+1))*(1.-y); // south edge 0->1 shape(4 + 1*(p-1) + i) = (nodalY(i+1))*x; // east edge 1->2 shape(4 + 3*(p-1) - i - 1) = (nodalX(i+1)) * y; // north edge 3->2 shape(4 + 4*(p-1) - i - 1) = (nodalY(i+1)) * (1. - x); // west edge 0->3 } BiLinear2DFiniteElement bilinear = BiLinear2DFiniteElement(); Vector bilinearsAtIP(4); bilinear.CalcShape(ip, bilinearsAtIP); const double *edgePts(poly1d.ClosedPoints(p, BasisType::GaussLobatto)); // Next, set the shape function associated with vertex V, evaluated at (x,y) // to be: bilinear function associated to V, evaluated at (x,y) - sum (shape // function at edge point P, weighted by bilinear function for V evaluated at // P) where the sum is taken only for points P on edges incident to V. double vtx0fix =0; double vtx1fix =0; double vtx2fix =0; double vtx3fix =0; for (int i = 0; i 3) { double *legX = new double[p-1]; double *legY = new double[p-1]; Poly_1D *storeLegendre = new Poly_1D(); storeLegendre->CalcLegendre(p-2, x, legX); storeLegendre->CalcLegendre(p-2, y, legY); int interior_total = 0; for (int j = 4; j < p + 1; j++) { for (int k = 0; k < j-3; k++) { shape(4 + 4*(p-1) + interior_total) = legX[k] * legY[j-4-k] * x * (1. - x) * y * (1. - y); interior_total++; } } delete[] legX; delete[] legY; delete storeLegendre; } } void H1Ser_QuadrilateralElement::CalcDShape(const IntegrationPoint &ip, DenseMatrix &dshape) const { int p = (this)->GetOrder(); double x = ip.x, y = ip.y; Poly_1D::Basis edgeNodalBasis(poly1d.GetBasis(p, BasisType::GaussLobatto)); Vector nodalX(p+1); Vector DnodalX(p+1); Vector nodalY(p+1); Vector DnodalY(p+1); edgeNodalBasis.Eval(x, nodalX, DnodalX); edgeNodalBasis.Eval(y, nodalY, DnodalY); for (int i = 0; i < p-1; i++) { dshape(4 + 0*(p-1) + i,0) = DnodalX(i+1) * (1.-y); dshape(4 + 0*(p-1) + i,1) = -nodalX(i+1); dshape(4 + 1*(p-1) + i,0) = nodalY(i+1); dshape(4 + 1*(p-1) + i,1) = DnodalY(i+1)*x; dshape(4 + 3*(p-1) - i - 1,0) = DnodalX(i+1)*y; dshape(4 + 3*(p-1) - i - 1,1) = nodalX(i+1); dshape(4 + 4*(p-1) - i - 1,0) = -nodalY(i+1); dshape(4 + 4*(p-1) - i - 1,1) = DnodalY(i+1) * (1.-x); } BiLinear2DFiniteElement bilinear = BiLinear2DFiniteElement(); DenseMatrix DbilinearsAtIP(4); bilinear.CalcDShape(ip, DbilinearsAtIP); const double *edgePts(poly1d.ClosedPoints(p, BasisType::GaussLobatto)); dshape(0,0) = DbilinearsAtIP(0,0); dshape(0,1) = DbilinearsAtIP(0,1); dshape(1,0) = DbilinearsAtIP(1,0); dshape(1,1) = DbilinearsAtIP(1,1); dshape(2,0) = DbilinearsAtIP(2,0); dshape(2,1) = DbilinearsAtIP(2,1); dshape(3,0) = DbilinearsAtIP(3,0); dshape(3,1) = DbilinearsAtIP(3,1); for (int i = 0; i 3) { double *legX = new double[p-1]; double *legY = new double[p-1]; double *DlegX = new double[p-1]; double *DlegY = new double[p-1]; Poly_1D *storeLegendre = new Poly_1D(); storeLegendre->CalcLegendre(p-2, x, legX, DlegX); storeLegendre->CalcLegendre(p-2, y, legY, DlegY); int interior_total = 0; for (int j = 4; j < p + 1; j++) { for (int k = 0; k < j-3; k++) { dshape(4 + 4*(p-1) + interior_total, 0) = legY[j-4-k]*y*(1-y) * (DlegX[k]*x*(1-x) + legX[k]*(1-2*x)); dshape(4 + 4*(p-1) + interior_total, 1) = legX[k]*x*(1-x) * (DlegY[j-4-k]*y*(1-y) + legY[j-4-k]*(1-2*y)); interior_total++; } } delete[] legX; delete[] legY; delete[] DlegX; delete[] DlegY; delete storeLegendre; } } void H1Ser_QuadrilateralElement::GetLocalInterpolation(ElementTransformation &Trans, DenseMatrix &I) const { // For p<=4, the basis is nodal; for p>4, the quad-interior functions are // non-nodal. if (order <= 4) { NodalLocalInterpolation(Trans, I, *this); } else { ScalarLocalInterpolation(Trans, I, *this); } } }