// Copyright (c) 2010-2025, 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. // L2 Finite Element classes #include "fe_l2.hpp" #include "fe_h1.hpp" #include "../coefficient.hpp" namespace mfem { using namespace std; L2_SegmentElement::L2_SegmentElement(const int p, const int btype) : NodalTensorFiniteElement(1, p, VerifyOpen(btype), L2_DOF_MAP) { const real_t *op = poly1d.OpenPoints(p, btype); #ifndef MFEM_THREAD_SAFE shape_x.SetSize(p + 1); dshape_x.SetDataAndSize(NULL, p + 1); #endif for (int i = 0; i <= p; i++) { Nodes.IntPoint(i).x = op[i]; } } void L2_SegmentElement::CalcShape(const IntegrationPoint &ip, Vector &shape) const { basis1d.ScaleIntegrated(map_type == VALUE); basis1d.Eval(ip.x, shape); } void L2_SegmentElement::CalcDShape(const IntegrationPoint &ip, DenseMatrix &dshape) const { #ifdef MFEM_THREAD_SAFE Vector shape_x(dof), dshape_x(dshape.Data(), dof); #else dshape_x.SetData(dshape.Data()); #endif basis1d.ScaleIntegrated(map_type == VALUE); basis1d.Eval(ip.x, shape_x, dshape_x); } void L2_SegmentElement::ProjectDelta(int vertex, Vector &dofs) const { const int p = order; const real_t *op = poly1d.OpenPoints(p, b_type); switch (vertex) { case 0: for (int i = 0; i <= p; i++) { dofs(i) = poly1d.CalcDelta(p,(1.0 - op[i])); } break; case 1: for (int i = 0; i <= p; i++) { dofs(i) = poly1d.CalcDelta(p,op[i]); } break; } } L2_QuadrilateralElement::L2_QuadrilateralElement(const int p, const int btype) : NodalTensorFiniteElement(2, p, VerifyOpen(btype), L2_DOF_MAP) { const real_t *op = poly1d.OpenPoints(p, b_type); #ifndef MFEM_THREAD_SAFE shape_x.SetSize(p + 1); shape_y.SetSize(p + 1); dshape_x.SetSize(p + 1); dshape_y.SetSize(p + 1); #endif for (int o = 0, j = 0; j <= p; j++) for (int i = 0; i <= p; i++) { Nodes.IntPoint(o++).Set2(op[i], op[j]); } } void L2_QuadrilateralElement::CalcShape(const IntegrationPoint &ip, Vector &shape) const { const int p = order; #ifdef MFEM_THREAD_SAFE Vector shape_x(p+1), shape_y(p+1); #endif basis1d.ScaleIntegrated(map_type == VALUE); basis1d.Eval(ip.x, shape_x); basis1d.Eval(ip.y, shape_y); for (int o = 0, j = 0; j <= p; j++) for (int i = 0; i <= p; i++) { shape(o++) = shape_x(i)*shape_y(j); } } void L2_QuadrilateralElement::CalcDShape(const IntegrationPoint &ip, DenseMatrix &dshape) const { const int p = order; #ifdef MFEM_THREAD_SAFE Vector shape_x(p+1), shape_y(p+1), dshape_x(p+1), dshape_y(p+1); #endif basis1d.ScaleIntegrated(map_type == VALUE); basis1d.Eval(ip.x, shape_x, dshape_x); basis1d.Eval(ip.y, shape_y, dshape_y); for (int o = 0, j = 0; j <= p; j++) for (int i = 0; i <= p; i++) { dshape(o,0) = dshape_x(i)* shape_y(j); dshape(o,1) = shape_x(i)*dshape_y(j); o++; } } void L2_QuadrilateralElement::ProjectDelta(int vertex, Vector &dofs) const { const int p = order; const real_t *op = poly1d.OpenPoints(p, b_type); #ifdef MFEM_THREAD_SAFE Vector shape_x(p+1), shape_y(p+1); #endif for (int i = 0; i <= p; i++) { shape_x(i) = poly1d.CalcDelta(p,(1.0 - op[i])); shape_y(i) = poly1d.CalcDelta(p,op[i]); } switch (vertex) { case 0: for (int o = 0, j = 0; j <= p; j++) for (int i = 0; i <= p; i++) { dofs[o++] = shape_x(i)*shape_x(j); } break; case 1: for (int o = 0, j = 0; j <= p; j++) for (int i = 0; i <= p; i++) { dofs[o++] = shape_y(i)*shape_x(j); } break; case 2: for (int o = 0, j = 0; j <= p; j++) for (int i = 0; i <= p; i++) { dofs[o++] = shape_y(i)*shape_y(j); } break; case 3: for (int o = 0, j = 0; j <= p; j++) for (int i = 0; i <= p; i++) { dofs[o++] = shape_x(i)*shape_y(j); } break; } } void L2_QuadrilateralElement::ProjectDiv(const FiniteElement &fe, ElementTransformation &Trans, DenseMatrix &div) const { if (basis1d.IsIntegratedType()) { // Compute subcell integrals of the divergence const int fe_ndof = fe.GetDof(); Vector div_shape(fe_ndof); div.SetSize(dof, fe_ndof); div = 0.0; const IntegrationRule &ir = IntRules.Get(geom_type, fe.GetOrder()); const real_t *gll_pts = poly1d.GetPoints(order+1, BasisType::GaussLobatto); // Loop over subcells for (int iy = 0; iy < order+1; ++iy) { const real_t hy = gll_pts[iy+1] - gll_pts[iy]; for (int ix = 0; ix < order+1; ++ix) { const int i = ix + iy*(order+1); const real_t hx = gll_pts[ix+1] - gll_pts[ix]; // Loop over subcell quadrature points for (int iq = 0; iq < ir.Size(); ++iq) { IntegrationPoint ip = ir[iq]; ip.x = gll_pts[ix] + hx*ip.x; ip.y = gll_pts[iy] + hy*ip.y; Trans.SetIntPoint(&ip); fe.CalcDivShape(ip, div_shape); real_t w = ip.weight; if (map_type == VALUE) { const real_t detJ = Trans.Weight(); w /= detJ; } else if (map_type == INTEGRAL) { w *= hx*hy; } for (int j = 0; j < fe_ndof; j++) { const real_t div_j = div_shape(j); div(i,j) += w*div_j; } } } } // Filter small entries for (int i = 0; i < dof; ++i) { for (int j = 0; j < fe_ndof; j++) { if (std::fabs(div(i,j)) < 1e-12) { div(i,j) = 0.0; } } } } else { // Fall back on standard nodal interpolation NodalFiniteElement::ProjectDiv(fe, Trans, div); } } void L2_QuadrilateralElement::Project(Coefficient &coeff, ElementTransformation &Trans, Vector &dofs) const { if (basis1d.IsIntegratedType()) { const IntegrationRule &ir = IntRules.Get(geom_type, order); const real_t *gll_pts = poly1d.GetPoints(order+1, BasisType::GaussLobatto); dofs = 0.0; // Loop over subcells for (int iy = 0; iy < order+1; ++iy) { const real_t hy = gll_pts[iy+1] - gll_pts[iy]; for (int ix = 0; ix < order+1; ++ix) { const int i = ix + iy*(order+1); const real_t hx = gll_pts[ix+1] - gll_pts[ix]; // Loop over subcell quadrature points for (int iq = 0; iq < ir.Size(); ++iq) { IntegrationPoint ip = ir[iq]; ip.x = gll_pts[ix] + hx*ip.x; ip.y = gll_pts[iy] + hy*ip.y; Trans.SetIntPoint(&ip); const real_t val = coeff.Eval(Trans, ip); real_t w = ip.weight; if (map_type == INTEGRAL) { w *= hx*hy*Trans.Weight(); } dofs[i] += val*w; } } } } else { NodalFiniteElement::Project(coeff, Trans, dofs); } } L2_HexahedronElement::L2_HexahedronElement(const int p, const int btype) : NodalTensorFiniteElement(3, p, VerifyOpen(btype), L2_DOF_MAP) { const real_t *op = poly1d.OpenPoints(p, btype); #ifndef MFEM_THREAD_SAFE shape_x.SetSize(p + 1); shape_y.SetSize(p + 1); shape_z.SetSize(p + 1); dshape_x.SetSize(p + 1); dshape_y.SetSize(p + 1); dshape_z.SetSize(p + 1); #endif for (int o = 0, k = 0; k <= p; k++) for (int j = 0; j <= p; j++) for (int i = 0; i <= p; i++) { Nodes.IntPoint(o++).Set3(op[i], op[j], op[k]); } } void L2_HexahedronElement::CalcShape(const IntegrationPoint &ip, Vector &shape) const { const int p = order; #ifdef MFEM_THREAD_SAFE Vector shape_x(p+1), shape_y(p+1), shape_z(p+1); #endif basis1d.ScaleIntegrated(map_type == VALUE); basis1d.Eval(ip.x, shape_x); basis1d.Eval(ip.y, shape_y); basis1d.Eval(ip.z, shape_z); for (int o = 0, k = 0; k <= p; k++) for (int j = 0; j <= p; j++) for (int i = 0; i <= p; i++) { shape(o++) = shape_x(i)*shape_y(j)*shape_z(k); } } void L2_HexahedronElement::CalcDShape(const IntegrationPoint &ip, DenseMatrix &dshape) const { const int p = order; #ifdef MFEM_THREAD_SAFE Vector shape_x(p+1), shape_y(p+1), shape_z(p+1); Vector dshape_x(p+1), dshape_y(p+1), dshape_z(p+1); #endif basis1d.ScaleIntegrated(map_type == VALUE); basis1d.Eval(ip.x, shape_x, dshape_x); basis1d.Eval(ip.y, shape_y, dshape_y); basis1d.Eval(ip.z, shape_z, dshape_z); for (int o = 0, k = 0; k <= p; k++) for (int j = 0; j <= p; j++) for (int i = 0; i <= p; i++) { dshape(o,0) = dshape_x(i)* shape_y(j)* shape_z(k); dshape(o,1) = shape_x(i)*dshape_y(j)* shape_z(k); dshape(o,2) = shape_x(i)* shape_y(j)*dshape_z(k); o++; } } void L2_HexahedronElement::ProjectDelta(int vertex, Vector &dofs) const { const int p = order; const real_t *op = poly1d.OpenPoints(p, b_type); #ifdef MFEM_THREAD_SAFE Vector shape_x(p+1), shape_y(p+1); #endif for (int i = 0; i <= p; i++) { shape_x(i) = poly1d.CalcDelta(p,(1.0 - op[i])); shape_y(i) = poly1d.CalcDelta(p,op[i]); } switch (vertex) { case 0: for (int o = 0, k = 0; k <= p; k++) for (int j = 0; j <= p; j++) for (int i = 0; i <= p; i++) { dofs[o++] = shape_x(i)*shape_x(j)*shape_x(k); } break; case 1: for (int o = 0, k = 0; k <= p; k++) for (int j = 0; j <= p; j++) for (int i = 0; i <= p; i++) { dofs[o++] = shape_y(i)*shape_x(j)*shape_x(k); } break; case 2: for (int o = 0, k = 0; k <= p; k++) for (int j = 0; j <= p; j++) for (int i = 0; i <= p; i++) { dofs[o++] = shape_y(i)*shape_y(j)*shape_x(k); } break; case 3: for (int o = 0, k = 0; k <= p; k++) for (int j = 0; j <= p; j++) for (int i = 0; i <= p; i++) { dofs[o++] = shape_x(i)*shape_y(j)*shape_x(k); } break; case 4: for (int o = 0, k = 0; k <= p; k++) for (int j = 0; j <= p; j++) for (int i = 0; i <= p; i++) { dofs[o++] = shape_x(i)*shape_x(j)*shape_y(k); } break; case 5: for (int o = 0, k = 0; k <= p; k++) for (int j = 0; j <= p; j++) for (int i = 0; i <= p; i++) { dofs[o++] = shape_y(i)*shape_x(j)*shape_y(k); } break; case 6: for (int o = 0, k = 0; k <= p; k++) for (int j = 0; j <= p; j++) for (int i = 0; i <= p; i++) { dofs[o++] = shape_y(i)*shape_y(j)*shape_y(k); } break; case 7: for (int o = 0, k = 0; k <= p; k++) for (int j = 0; j <= p; j++) for (int i = 0; i <= p; i++) { dofs[o++] = shape_x(i)*shape_y(j)*shape_y(k); } break; } } void L2_HexahedronElement::ProjectDiv(const FiniteElement &fe, ElementTransformation &Trans, DenseMatrix &div) const { if (basis1d.IsIntegratedType()) { // Compute subcell integrals of the divergence const int fe_ndof = fe.GetDof(); Vector div_shape(fe_ndof); div.SetSize(dof, fe_ndof); div = 0.0; const IntegrationRule &ir = IntRules.Get(geom_type, fe.GetOrder()); const real_t *gll_pts = poly1d.GetPoints(order+1, BasisType::GaussLobatto); // Loop over subcells for (int iz = 0; iz < order+1; ++iz) { const real_t hz = gll_pts[iz+1] - gll_pts[iz]; for (int iy = 0; iy < order+1; ++iy) { const real_t hy = gll_pts[iy+1] - gll_pts[iy]; for (int ix = 0; ix < order+1; ++ix) { const int i = ix + iy*(order+1) + iz*(order+1)*(order+1); const real_t hx = gll_pts[ix+1] - gll_pts[ix]; // Loop over subcell quadrature points for (int iq = 0; iq < ir.Size(); ++iq) { IntegrationPoint ip = ir[iq]; ip.x = gll_pts[ix] + hx*ip.x; ip.y = gll_pts[iy] + hy*ip.y; ip.z = gll_pts[iz] + hz*ip.z; Trans.SetIntPoint(&ip); fe.CalcDivShape(ip, div_shape); real_t w = ip.weight; if (map_type == VALUE) { const real_t detJ = Trans.Weight(); w /= detJ; } else if (map_type == INTEGRAL) { w *= hx*hy*hz; } for (int j = 0; j < fe_ndof; j++) { const real_t div_j = div_shape(j); div(i,j) += w*div_j; } } } } } // Filter small entries for (int i = 0; i < dof; ++i) { for (int j = 0; j < fe_ndof; j++) { if (std::fabs(div(i,j)) < 1e-12) { div(i,j) = 0.0; } } } } else { // Fall back on standard nodal interpolation NodalFiniteElement::ProjectDiv(fe, Trans, div); } } void L2_HexahedronElement::Project(Coefficient &coeff, ElementTransformation &Trans, Vector &dofs) const { if (basis1d.IsIntegratedType()) { const IntegrationRule &ir = IntRules.Get(geom_type, order); const real_t *gll_pts = poly1d.GetPoints(order+1, BasisType::GaussLobatto); dofs = 0.0; // Loop over subcells for (int iz = 0; iz < order+1; ++iz) { const real_t hz = gll_pts[iz+1] - gll_pts[iz]; for (int iy = 0; iy < order+1; ++iy) { const real_t hy = gll_pts[iy+1] - gll_pts[iy]; for (int ix = 0; ix < order+1; ++ix) { const real_t hx = gll_pts[ix+1] - gll_pts[ix]; const int i = ix + iy*(order+1) + iz*(order+1)*(order+1); // Loop over subcell quadrature points for (int iq = 0; iq < ir.Size(); ++iq) { IntegrationPoint ip = ir[iq]; ip.x = gll_pts[ix] + hx*ip.x; ip.y = gll_pts[iy] + hy*ip.y; ip.z = gll_pts[iz] + hz*ip.z; Trans.SetIntPoint(&ip); const real_t val = coeff.Eval(Trans, ip); real_t w = ip.weight; if (map_type == INTEGRAL) { const real_t detJ = Trans.Weight(); w *= detJ*hx*hy*hz; } dofs[i] += val*w; } } } } } else { NodalFiniteElement::Project(coeff, Trans, dofs); } } L2_TriangleElement::L2_TriangleElement(const int p, const int btype) : NodalFiniteElement(2, Geometry::TRIANGLE, ((p + 1)*(p + 2))/2, p, FunctionSpace::Pk) { const real_t *op = poly1d.OpenPoints(p, VerifyOpen(btype)); #ifndef MFEM_THREAD_SAFE shape_x.SetSize(p + 1); shape_y.SetSize(p + 1); shape_l.SetSize(p + 1); dshape_x.SetSize(p + 1); dshape_y.SetSize(p + 1); dshape_l.SetSize(p + 1); u.SetSize(dof); du.SetSize(dof, dim); #else Vector shape_x(p + 1), shape_y(p + 1), shape_l(p + 1); #endif for (int o = 0, j = 0; j <= p; j++) for (int i = 0; i + j <= p; i++) { real_t w = op[i] + op[j] + op[p-i-j]; Nodes.IntPoint(o++).Set2(op[i]/w, op[j]/w); } DenseMatrix T(dof); for (int k = 0; k < dof; k++) { IntegrationPoint &ip = Nodes.IntPoint(k); poly1d.CalcBasis(p, ip.x, shape_x); poly1d.CalcBasis(p, ip.y, shape_y); poly1d.CalcBasis(p, 1. - ip.x - ip.y, shape_l); for (int o = 0, j = 0; j <= p; j++) for (int i = 0; i + j <= p; i++) { T(o++, k) = shape_x(i)*shape_y(j)*shape_l(p-i-j); } } Ti.Factor(T); // mfem::out << "L2_TriangleElement(" << p << ") : "; Ti.TestInversion(); } void L2_TriangleElement::CalcShape(const IntegrationPoint &ip, Vector &shape) const { const int p = order; #ifdef MFEM_THREAD_SAFE Vector shape_x(p + 1), shape_y(p + 1), shape_l(p + 1), u(dof); #endif poly1d.CalcBasis(p, ip.x, shape_x); poly1d.CalcBasis(p, ip.y, shape_y); poly1d.CalcBasis(p, 1. - ip.x - ip.y, shape_l); for (int o = 0, j = 0; j <= p; j++) for (int i = 0; i + j <= p; i++) { u(o++) = shape_x(i)*shape_y(j)*shape_l(p-i-j); } Ti.Mult(u, shape); } void L2_TriangleElement::CalcDShape(const IntegrationPoint &ip, DenseMatrix &dshape) const { const int p = order; #ifdef MFEM_THREAD_SAFE Vector shape_x(p + 1), shape_y(p + 1), shape_l(p + 1); Vector dshape_x(p + 1), dshape_y(p + 1), dshape_l(p + 1); DenseMatrix du(dof, dim); #endif poly1d.CalcBasis(p, ip.x, shape_x, dshape_x); poly1d.CalcBasis(p, ip.y, shape_y, dshape_y); poly1d.CalcBasis(p, 1. - ip.x - ip.y, shape_l, dshape_l); for (int o = 0, j = 0; j <= p; j++) for (int i = 0; i + j <= p; i++) { int k = p - i - j; du(o,0) = ((dshape_x(i)* shape_l(k)) - ( shape_x(i)*dshape_l(k)))*shape_y(j); du(o,1) = ((dshape_y(j)* shape_l(k)) - ( shape_y(j)*dshape_l(k)))*shape_x(i); o++; } Ti.Mult(du, dshape); } void L2_TriangleElement::ProjectDelta(int vertex, Vector &dofs) const { switch (vertex) { case 0: for (int i = 0; i < dof; i++) { const IntegrationPoint &ip = Nodes.IntPoint(i); dofs[i] = pow(1.0 - ip.x - ip.y, order); } break; case 1: for (int i = 0; i < dof; i++) { const IntegrationPoint &ip = Nodes.IntPoint(i); dofs[i] = pow(ip.x, order); } break; case 2: for (int i = 0; i < dof; i++) { const IntegrationPoint &ip = Nodes.IntPoint(i); dofs[i] = pow(ip.y, order); } break; } } L2_TetrahedronElement::L2_TetrahedronElement(const int p, const int btype) : NodalFiniteElement(3, Geometry::TETRAHEDRON, ((p + 1)*(p + 2)*(p + 3))/6, p, FunctionSpace::Pk) { const real_t *op = poly1d.OpenPoints(p, VerifyOpen(btype)); #ifndef MFEM_THREAD_SAFE shape_x.SetSize(p + 1); shape_y.SetSize(p + 1); shape_z.SetSize(p + 1); shape_l.SetSize(p + 1); dshape_x.SetSize(p + 1); dshape_y.SetSize(p + 1); dshape_z.SetSize(p + 1); dshape_l.SetSize(p + 1); u.SetSize(dof); du.SetSize(dof, dim); #else Vector shape_x(p + 1), shape_y(p + 1), shape_z(p + 1), shape_l(p + 1); #endif for (int o = 0, k = 0; k <= p; k++) for (int j = 0; j + k <= p; j++) for (int i = 0; i + j + k <= p; i++) { real_t w = op[i] + op[j] + op[k] + op[p-i-j-k]; Nodes.IntPoint(o++).Set3(op[i]/w, op[j]/w, op[k]/w); } DenseMatrix T(dof); for (int m = 0; m < dof; m++) { IntegrationPoint &ip = Nodes.IntPoint(m); poly1d.CalcBasis(p, ip.x, shape_x); poly1d.CalcBasis(p, ip.y, shape_y); poly1d.CalcBasis(p, ip.z, shape_z); poly1d.CalcBasis(p, 1. - ip.x - ip.y - ip.z, shape_l); for (int o = 0, k = 0; k <= p; k++) for (int j = 0; j + k <= p; j++) for (int i = 0; i + j + k <= p; i++) { T(o++, m) = shape_x(i)*shape_y(j)*shape_z(k)*shape_l(p-i-j-k); } } Ti.Factor(T); // mfem::out << "L2_TetrahedronElement(" << p << ") : "; Ti.TestInversion(); } void L2_TetrahedronElement::CalcShape(const IntegrationPoint &ip, Vector &shape) const { const int p = order; #ifdef MFEM_THREAD_SAFE Vector shape_x(p + 1), shape_y(p + 1), shape_z(p + 1), shape_l(p + 1); Vector u(dof); #endif poly1d.CalcBasis(p, ip.x, shape_x); poly1d.CalcBasis(p, ip.y, shape_y); poly1d.CalcBasis(p, ip.z, shape_z); poly1d.CalcBasis(p, 1. - ip.x - ip.y - ip.z, shape_l); for (int o = 0, k = 0; k <= p; k++) for (int j = 0; j + k <= p; j++) for (int i = 0; i + j + k <= p; i++) { u(o++) = shape_x(i)*shape_y(j)*shape_z(k)*shape_l(p-i-j-k); } Ti.Mult(u, shape); } void L2_TetrahedronElement::CalcDShape(const IntegrationPoint &ip, DenseMatrix &dshape) const { const int p = order; #ifdef MFEM_THREAD_SAFE Vector shape_x(p + 1), shape_y(p + 1), shape_z(p + 1), shape_l(p + 1); Vector dshape_x(p + 1), dshape_y(p + 1), dshape_z(p + 1), dshape_l(p + 1); DenseMatrix du(dof, dim); #endif poly1d.CalcBasis(p, ip.x, shape_x, dshape_x); poly1d.CalcBasis(p, ip.y, shape_y, dshape_y); poly1d.CalcBasis(p, ip.z, shape_z, dshape_z); poly1d.CalcBasis(p, 1. - ip.x - ip.y - ip.z, shape_l, dshape_l); for (int o = 0, k = 0; k <= p; k++) for (int j = 0; j + k <= p; j++) for (int i = 0; i + j + k <= p; i++) { int l = p - i - j - k; du(o,0) = ((dshape_x(i)* shape_l(l)) - ( shape_x(i)*dshape_l(l)))*shape_y(j)*shape_z(k); du(o,1) = ((dshape_y(j)* shape_l(l)) - ( shape_y(j)*dshape_l(l)))*shape_x(i)*shape_z(k); du(o,2) = ((dshape_z(k)* shape_l(l)) - ( shape_z(k)*dshape_l(l)))*shape_x(i)*shape_y(j); o++; } Ti.Mult(du, dshape); } void L2_TetrahedronElement::ProjectDelta(int vertex, Vector &dofs) const { switch (vertex) { case 0: for (int i = 0; i < dof; i++) { const IntegrationPoint &ip = Nodes.IntPoint(i); dofs[i] = pow(1.0 - ip.x - ip.y - ip.z, order); } break; case 1: for (int i = 0; i < dof; i++) { const IntegrationPoint &ip = Nodes.IntPoint(i); dofs[i] = pow(ip.x, order); } break; case 2: for (int i = 0; i < dof; i++) { const IntegrationPoint &ip = Nodes.IntPoint(i); dofs[i] = pow(ip.y, order); } break; case 3: for (int i = 0; i < dof; i++) { const IntegrationPoint &ip = Nodes.IntPoint(i); dofs[i] = pow(ip.z, order); } break; } } L2_WedgeElement::L2_WedgeElement(const int p, const int btype) : NodalFiniteElement(3, Geometry::PRISM, ((p + 1)*(p + 1)*(p + 2))/2, p, FunctionSpace::Qk), TriangleFE(p, btype), SegmentFE(p, btype) { #ifndef MFEM_THREAD_SAFE t_shape.SetSize(TriangleFE.GetDof()); s_shape.SetSize(SegmentFE.GetDof()); t_dshape.SetSize(TriangleFE.GetDof(), 2); s_dshape.SetSize(SegmentFE.GetDof(), 1); #endif t_dof.SetSize(dof); s_dof.SetSize(dof); // Interior DoFs int m=0; for (int k=0; k<=p; k++) { int l=0; for (int j=0; j<=p; j++) { for (int i=0; i<=j; i++) { t_dof[m] = l; s_dof[m] = k; l++; m++; } } } // Define Nodes const IntegrationRule & t_Nodes = TriangleFE.GetNodes(); const IntegrationRule & s_Nodes = SegmentFE.GetNodes(); for (int i=0; i= 1. For this reason we force the points // to be open in the z direction whenever closed points are requested. // This should be regarded as a limitation of this choice of basis function. // If a truly closed set of points is needed consider using // L2_BergotPyramidElement instead. real_t a = 1.0; if (IsClosedType(btype) && p > 0) { a = (poly1d.GetPoints(p, BasisType::GaussLegendre))[p]; } #ifndef MFEM_THREAD_SAFE shape_x.SetSize(p + 1); shape_y.SetSize(p + 1); shape_z.SetSize(p + 1); dshape_x.SetSize(p + 1); dshape_y.SetSize(p + 1); dshape_z.SetSize(p + 1); u.SetSize(dof); du.SetSize(dof, dim); #else Vector shape_x(p + 1); Vector shape_y(p + 1); Vector shape_z(p + 1); #endif int o = 0; for (int k = 0; k <= p; k++) for (int j = 0; j <= p; j++) for (int i = 0; i <= p; i++) { Nodes.IntPoint(o++).Set3(op[i] * (1.0 - a * op[k]), op[j] * (1.0 - a * op[k]), a * op[k]); } MFEM_ASSERT(o == dof, "Number of nodes does not match the " "number of degrees of freedom"); DenseMatrix T(dof); for (int m = 0; m < dof; m++) { const IntegrationPoint &ip = Nodes.IntPoint(m); real_t x = ip.x; real_t y = ip.y; real_t z = ip.z; Vector xy({x,y}); CalcHomogenizedScaLegendre(p, mu0(z, xy, 1), mu1(z, xy, 1), shape_x); CalcHomogenizedScaLegendre(p, mu0(z, xy, 2), mu1(z, xy, 2), shape_y); CalcHomogenizedScaLegendre(p, mu0(z), mu1(z), shape_z); o = 0; for (int k = 0; k <= p; k++) { for (int j = 0; j <= p; j++) { for (int i = 0; i <= p; i++, o++) { T(o, m) = shape_x[i] * shape_y[j] * shape_z[k]; } } } } Ti.Factor(T); } void L2_FuentesPyramidElement::CalcShape(const IntegrationPoint &ip, Vector &shape) const { const int p = order; #ifdef MFEM_THREAD_SAFE Vector shape_x(p + 1); Vector shape_y(p + 1); Vector shape_z(p + 1); Vector u(dof); #endif real_t x = ip.x; real_t y = ip.y; real_t z = ip.z; Vector xy({x,y}); if (z < 1.0) { CalcHomogenizedScaLegendre(p, mu0(z, xy, 1), mu1(z, xy, 1), shape_x); CalcHomogenizedScaLegendre(p, mu0(z, xy, 2), mu1(z, xy, 2), shape_y); } else { shape_x = 0.0; shape_x(0) = 1.0; shape_y = 0.0; shape_y(0) = 1.0; } CalcHomogenizedScaLegendre(p, mu0(z), mu1(z), shape_z); int o = 0; for (int k = 0; k <= p; k++) for (int j = 0; j <= p; j++) for (int i = 0; i <= p; i++, o++) { u[o] = shape_x[i] * shape_y[j] * shape_z[k]; } Ti.Mult(u, shape); } void L2_FuentesPyramidElement::CalcDShape(const IntegrationPoint &ip, DenseMatrix &dshape) const { const int p = order; #ifdef MFEM_THREAD_SAFE Vector shape_x(p + 1); Vector shape_y(p + 1); Vector shape_z(p + 1); Vector dshape_x(p + 1); Vector dshape_y(p + 1); Vector dshape_z(p + 1); DenseMatrix du(dof, dim); #endif Poly_1D::CalcLegendre(p, ip.x / (1.0 - ip.z), shape_x.GetData(), dshape_x.GetData()); Poly_1D::CalcLegendre(p, ip.y / (1.0 - ip.z), shape_y.GetData(), dshape_y.GetData()); Poly_1D::CalcLegendre(p, ip.z, shape_z.GetData(), dshape_z.GetData()); int o = 0; for (int k = 0; k <= p; k++) for (int j = 0; j <= p; j++) for (int i = 0; i <= p; i++, o++) { du(o, 0) = dshape_x[i] * shape_y[j] * shape_z[k] / (1.0 - ip.z); du(o, 1) = shape_x[i] * dshape_y[j] * shape_z[k] / (1.0 - ip.z); du(o, 2) = shape_x[i] * shape_y[j] * dshape_z[k] + (ip.x * dshape_x[i] * shape_y[j] + ip.y * shape_x[i] * dshape_y[j]) * shape_z[k] / pow(1.0 - ip.z, 2); } Ti.Mult(du, dshape); } L2_BergotPyramidElement::L2_BergotPyramidElement(const int p, const int btype) : NodalFiniteElement(3, Geometry::PYRAMID, (p + 1)*(p + 2)*(2*p + 3)/6, p, FunctionSpace::Pk) { const real_t *op = poly1d.OpenPoints(p, VerifyOpen(btype)); #ifndef MFEM_THREAD_SAFE shape_x.SetSize(p + 1); shape_y.SetSize(p + 1); shape_z.SetSize(p + 1); dshape_x.SetSize(p + 1); dshape_y.SetSize(p + 1); dshape_z.SetSize(p + 1); dshape_z_dt.SetSize(p + 1); u.SetSize(dof); du.SetSize(dof, dim); #else Vector shape_x(p + 1); Vector shape_y(p + 1); Vector shape_z(p + 1); Vector dshape_z_dt(p + 1); #endif int o = 0; for (int k = 0; k <= p; k++) for (int j = 0; j <= p - k; j++) { const real_t wjk = op[j] + op[k] + op[p-j-k]; for (int i = 0; i <= p - k; i++) { const real_t wik = op[i] + op[k] + op[p-i-k]; const real_t w = wik * wjk * op[p-k]; Nodes.IntPoint(o++).Set3(op[i] * (op[j] + op[p-j-k]) / w, op[j] * (op[j] + op[p-j-k]) / w, op[k] * op[p-k] / w); } } MFEM_ASSERT(o == dof, "Number of nodes does not match the " "number of degrees of freedom"); DenseMatrix T(dof); for (int m = 0; m < dof; m++) { const IntegrationPoint &ip = Nodes.IntPoint(m); const real_t x = (ip.z < 1.0) ? (ip.x / (1.0 - ip.z)) : 0.0; const real_t y = (ip.z < 1.0) ? (ip.y / (1.0 - ip.z)) : 0.0; const real_t z = ip.z; poly1d.CalcLegendre(p, x, shape_x.GetData()); poly1d.CalcLegendre(p, y, shape_y.GetData()); o = 0; for (int i = 0; i <= p; i++) { for (int j = 0; j <= p; j++) { int maxij = std::max(i, j); FuentesPyramid::CalcScaledJacobi(p-maxij, 2.0 * (maxij + 1.0), z, 1.0, shape_z); for (int k = 0; k <= p - maxij; k++) { T(o++, m) = shape_x(i) * shape_y(j) * shape_z(k) * pow(1.0 - ip.z, maxij); } } } } Ti.Factor(T); } void L2_BergotPyramidElement::CalcShape(const IntegrationPoint &ip, Vector &shape) const { const int p = order; #ifdef MFEM_THREAD_SAFE Vector shape_x(p + 1); Vector shape_y(p + 1); Vector shape_z(p + 1); Vector u(dof); #endif const real_t x = (ip.z < 1.0) ? (ip.x / (1.0 - ip.z)) : 0.0; const real_t y = (ip.z < 1.0) ? (ip.y / (1.0 - ip.z)) : 0.0; const real_t z = ip.z; poly1d.CalcLegendre(p, x, shape_x.GetData()); poly1d.CalcLegendre(p, y, shape_y.GetData()); int o = 0; for (int i = 0; i <= p; i++) { for (int j = 0; j <= p; j++) { int maxij = std::max(i, j); FuentesPyramid::CalcScaledJacobi(p-maxij, 2.0 * (maxij + 1.0), z, 1.0, shape_z); for (int k = 0; k <= p - maxij; k++) { u[o++] = shape_x(i) * shape_y(j) * shape_z(k) * pow(1.0 - ip.z, maxij); } } } Ti.Mult(u, shape); } void L2_BergotPyramidElement::CalcDShape(const IntegrationPoint &ip, DenseMatrix &dshape) const { const int p = order; #ifdef MFEM_THREAD_SAFE Vector shape_x(p + 1); Vector shape_y(p + 1); Vector shape_z(p + 1); Vector dshape_x(p + 1); Vector dshape_y(p + 1); Vector dshape_z(p + 1); Vector dshape_z_dt(p + 1); DenseMatrix du(dof, dim); #endif const real_t x = (ip.z < 1.0) ? (ip.x / (1.0 - ip.z)) : 0.0; const real_t y = (ip.z < 1.0) ? (ip.y / (1.0 - ip.z)) : 0.0; const real_t z = ip.z; Poly_1D::CalcLegendre(p, x, shape_x.GetData(), dshape_x.GetData()); Poly_1D::CalcLegendre(p, y, shape_y.GetData(), dshape_y.GetData()); int o = 0; for (int i = 0; i <= p; i++) { for (int j = 0; j <= p; j++) { int maxij = std::max(i, j); FuentesPyramid::CalcScaledJacobi(p-maxij, 2.0 * (maxij + 1.0), z, 1.0, shape_z, dshape_z, dshape_z_dt); for (int k = 0; k <= p - maxij; k++, o++) { du(o,0) = dshape_x(i) * shape_y(j) * shape_z(k) * pow(1.0 - ip.z, maxij - 1); du(o,1) = shape_x(i) * dshape_y(j) * shape_z(k) * pow(1.0 - ip.z, maxij - 1); du(o,2) = shape_x(i) * shape_y(j) * dshape_z(k) * pow(1.0 - ip.z, maxij) + (ip.x * dshape_x(i) * shape_y(j) + ip.y * shape_x(i) * dshape_y(j)) * shape_z(k) * pow(1.0 - ip.z, maxij - 2) - ((maxij > 0) ? (maxij * shape_x(i) * shape_y(j) * shape_z(k) * pow(1.0 - ip.z, maxij - 1)) : 0.0); } } } Ti.Mult(du, dshape); } }