From 4a4d541c46722448fc6b32c5784f959293910b5f Mon Sep 17 00:00:00 2001 From: Sebastian Grimberg Date: Thu, 14 Dec 2023 17:45:55 -0800 Subject: [PATCH 01/32] Upgrade MaterialOperator and add MaterialPropertyCoefficient class to handle material property coefficients on the mesh --- palace/fem/coefficient.cpp | 59 +-- palace/fem/coefficient.hpp | 606 ++++++++++------------------- palace/models/materialoperator.cpp | 591 ++++++++++++++++++++++++---- palace/models/materialoperator.hpp | 215 ++++++++-- palace/utils/configfile.hpp | 6 + 5 files changed, 943 insertions(+), 534 deletions(-) diff --git a/palace/fem/coefficient.cpp b/palace/fem/coefficient.cpp index c83c88244..9b8802c1d 100644 --- a/palace/fem/coefficient.cpp +++ b/palace/fem/coefficient.cpp @@ -6,57 +6,64 @@ namespace palace { -void BdrGridFunctionCoefficient::GetElementTransformations(mfem::ElementTransformation &T, - const mfem::IntegrationPoint &ip, - mfem::ElementTransformation *&T1, - mfem::ElementTransformation *&T2, - mfem::Vector *C1) +void BdrGridFunctionCoefficient::GetBdrElementNeighborTransformations( + int i, const mfem::ParMesh &mesh, const std::unordered_map &local_to_shared, + mfem::FaceElementTransformations &FET, mfem::IsoparametricTransformation &T1, + mfem::IsoparametricTransformation &T2, const mfem::IntegrationPoint *ip) { - // Return transformations for elements attached to boundary element T. T1 always exists - // but T2 may not if the element is truly a single-sided boundary. - MFEM_ASSERT(T.ElementType == mfem::ElementTransformation::BDR_ELEMENT, - "Unexpected element type in BdrGridFunctionCoefficient!"); - MFEM_ASSERT(&mesh == T.mesh, "Invalid mesh for BdrGridFunctionCoefficient!"); - int i, o; + // Return transformations for elements attached to the given boundary element. FET.Elem1 + // always exists but FET.Elem2 may not if the element is truly a single-sided boundary. + int f, o; int iel1, iel2, info1, info2; - mesh.GetBdrElementFace(T.ElementNo, &i, &o); - mesh.GetFaceElements(i, &iel1, &iel2); - mesh.GetFaceInfos(i, &info1, &info2); + mesh.GetBdrElementFace(i, &f, &o); + mesh.GetFaceElements(f, &iel1, &iel2); + mesh.GetFaceInfos(f, &info1, &info2); // Master faces can never be boundary elements, thus only need to check for the state of // info2 and el2, and do not need to access the ncface numbering. See mfem::Mesh::FaceInfo // for details. - mfem::FaceElementTransformations *FET; if (info2 >= 0 && iel2 < 0) { // Face is shared with another subdomain. - const int &ishared = local_to_shared.at(i); - FET = mesh.GetSharedFaceTransformations(ishared); + const int &ishared = local_to_shared.at(f); + mesh.GetSharedFaceTransformations(ishared, &FET, &T1, &T2); } else { // Face is either internal to the subdomain, or a true one-sided boundary. - FET = mesh.GetFaceElementTransformations(i); + mesh.GetFaceElementTransformations(f, &FET, &T1, &T2); } // Boundary elements and boundary faces may have different orientations so adjust the // integration point if necessary. See mfem::GridFunction::GetValue and GetVectorValue. - mfem::IntegrationPoint fip = - mfem::Mesh::TransformBdrElementToFace(FET->GetGeometryType(), o, ip); - FET->SetAllIntPoints(&fip); - T1 = &FET->GetElement1Transformation(); - T2 = (info2 >= 0) ? &FET->GetElement2Transformation() : nullptr; + if (ip) + { + mfem::IntegrationPoint fip = + mfem::Mesh::TransformBdrElementToFace(FET.GetGeometryType(), o, *ip); + FET.SetAllIntPoints(&fip); + } +} + +void BdrGridFunctionCoefficient::GetBdrElementNeighborTransformations( + mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip, mfem::Vector *C1) +{ + // Get the element transformations neighboring the element, and set the integration point + // too. + MFEM_ASSERT(T.ElementType == mfem::ElementTransformation::BDR_ELEMENT, + "Unexpected element type in BdrGridFunctionCoefficient!"); + GetBdrElementNeighborTransformations(T.ElementNo, mesh, local_to_shared, FET, T1, T2, + &ip); // If desired, get vector pointing from center of boundary element into element 1 for // orientations. if (C1) { mfem::Vector CF(T.GetSpaceDim()); - mfem::ElementTransformation &TF = *mesh.GetFaceTransformation(i); - TF.Transform(mfem::Geometries.GetCenter(mesh.GetFaceGeometry(i)), CF); + mesh.GetFaceTransformation(T.ElementNo, &TF); + TF.Transform(mfem::Geometries.GetCenter(mesh.GetFaceGeometry(T.ElementNo)), CF); C1->SetSize(T.GetSpaceDim()); - T1->Transform(mfem::Geometries.GetCenter(T1->GetGeometryType()), *C1); + FET.Elem1->Transform(mfem::Geometries.GetCenter(FET.Elem1->GetGeometryType()), *C1); *C1 -= CF; // Points into element 1 from the face } } diff --git a/palace/fem/coefficient.hpp b/palace/fem/coefficient.hpp index 211e3a1d4..aa50e823f 100644 --- a/palace/fem/coefficient.hpp +++ b/palace/fem/coefficient.hpp @@ -5,8 +5,8 @@ #define PALACE_FEM_COEFFICIENT_HPP #include -#include #include +#include #include #include #include @@ -22,173 +22,39 @@ namespace palace // comm on shared faces after a call to ExchangeFaceNbrData. // -enum class MaterialPropertyType -{ - INV_PERMEABILITY, - PERMITTIVITY_REAL, - PERMITTIVITY_IMAG, - PERMITTIVITY_ABS, - CONDUCTIVITY, - INV_LONDON_DEPTH, - INV_Z0, - INV_PERMEABILITY_C0 -}; - -enum class MeshElementType -{ - ELEMENT, - BDR_ELEMENT, - SUBMESH, - BDR_SUBMESH -}; - -// Returns the property value of the material for the given index. Two separate classes for -// domain element access and boundary element access, which returns the material property of -// the neighboring domain element. -template -class MaterialPropertyCoefficient : public mfem::MatrixCoefficient -{ -private: - const MaterialOperator &mat_op; - const double coef; - - static int GetAttribute(mfem::ElementTransformation &T) - { - if constexpr (ElemType == MeshElementType::SUBMESH || - ElemType == MeshElementType::BDR_SUBMESH) - { - MFEM_ASSERT( - T.ElementType == mfem::ElementTransformation::ELEMENT, - "Invalid usage of MaterialPropertyCoefficient for given MeshElementType!"); - const mfem::ParSubMesh &submesh = *static_cast(T.mesh); - const mfem::ParMesh &mesh = *submesh.GetParent(); - if constexpr (ElemType == MeshElementType::SUBMESH) - { - MFEM_ASSERT( - submesh.GetFrom() == mfem::SubMesh::From::Domain, - "Invalid usage of MaterialPropertyCoefficient for given MeshElementType!"); - return mesh.GetAttribute(submesh.GetParentElementIDMap()[T.ElementNo]); - } - else if constexpr (ElemType == MeshElementType::BDR_SUBMESH) - { - MFEM_ASSERT( - submesh.GetFrom() == mfem::SubMesh::From::Boundary, - "Invalid usage of MaterialPropertyCoefficient for given MeshElementType!"); - int i, o, iel1, iel2; - mesh.GetBdrElementFace(submesh.GetParentElementIDMap()[T.ElementNo], &i, &o); - mesh.GetFaceElements(i, &iel1, &iel2); -#if defined(MFEM_DEBUG) - int info1, info2, nc; - mesh.GetFaceInfos(i, &info1, &info2, &nc); - MFEM_VERIFY(nc == -1 && iel2 < 0 && info2 < 0, - "MaterialPropertyCoefficient should only be used for exterior " - "(single-sided) boundaries!"); -#endif - return mesh.GetAttribute(iel1); - } - } - else if constexpr (ElemType == MeshElementType::ELEMENT) - { - MFEM_ASSERT( - T.ElementType == mfem::ElementTransformation::ELEMENT, - "Invalid usage of MaterialPropertyCoefficient for given MeshElementType!"); - return T.Attribute; - } - else if constexpr (ElemType == MeshElementType::BDR_ELEMENT) - { - MFEM_ASSERT( - T.ElementType == mfem::ElementTransformation::BDR_ELEMENT, - "Invalid usage of MaterialPropertyCoefficient for given MeshElementType!"); - int i, o, iel1, iel2; - const mfem::Mesh &mesh = *T.mesh; - mesh.GetBdrElementFace(T.ElementNo, &i, &o); - mesh.GetFaceElements(i, &iel1, &iel2); -#if defined(MFEM_DEBUG) - int info1, info2, nc; - mesh.GetFaceInfos(i, &info1, &info2, &nc); - MFEM_VERIFY(nc == -1 && iel2 < 0 && info2 < 0, - "MaterialPropertyCoefficient should only be used for exterior " - "(single-sided) boundaries!"); -#endif - return mesh.GetAttribute(iel1); - } - MFEM_ABORT("Unsupported element type in MaterialPropertyCoefficient!"); - return 0; - } - -public: - MaterialPropertyCoefficient(const MaterialOperator &op, double c = 1.0) - : mfem::MatrixCoefficient(op.SpaceDimension()), mat_op(op), coef(c) - { - } - - void Eval(mfem::DenseMatrix &K, mfem::ElementTransformation &T, - const mfem::IntegrationPoint &ip) override - { - const int attr = GetAttribute(T); - if constexpr (MatType == MaterialPropertyType::INV_PERMEABILITY) - { - K = mat_op.GetInvPermeability(attr); - } - else if constexpr (MatType == MaterialPropertyType::PERMITTIVITY_REAL) - { - K = mat_op.GetPermittivityReal(attr); - } - else if constexpr (MatType == MaterialPropertyType::PERMITTIVITY_IMAG) - { - K = mat_op.GetPermittivityImag(attr); - } - else if constexpr (MatType == MaterialPropertyType::PERMITTIVITY_ABS) - { - K = mat_op.GetPermittivityAbs(attr); - } - else if constexpr (MatType == MaterialPropertyType::CONDUCTIVITY) - { - K = mat_op.GetConductivity(attr); - } - else if constexpr (MatType == MaterialPropertyType::INV_LONDON_DEPTH) - { - K = mat_op.GetInvLondonDepth(attr); - } - else if constexpr (MatType == MaterialPropertyType::INV_Z0) - { - K = mat_op.GetInvImpedance(attr); - } - else if constexpr (MatType == MaterialPropertyType::INV_PERMEABILITY_C0) - { - K.SetSize(height, width); - Mult(mat_op.GetInvPermeability(attr), mat_op.GetLightSpeed(attr), K); - } - else - { - MFEM_ABORT("MaterialPropertyCoefficient::Eval() is not implemented for this " - "material property type!"); - } - K *= coef; - } -}; - // Base class for coefficients which need to evaluate a GridFunction in a domain element // attached to a boundary element, or both domain elements on either side for internal // boundaries. class BdrGridFunctionCoefficient { protected: - mfem::ParMesh &mesh; - const std::map &local_to_shared; + // XX TODO: For thread-safety (multiple threads evaluating a coefficient simultaneously), + // the FET, FET.Elem1, and FET.Elem2 objects cannot be shared + const mfem::ParMesh &mesh; + const std::unordered_map &local_to_shared; + mfem::FaceElementTransformations FET; + mfem::IsoparametricTransformation T1, T2, TF; - void GetElementTransformations(mfem::ElementTransformation &T, - const mfem::IntegrationPoint &ip, - mfem::ElementTransformation *&T1, - mfem::ElementTransformation *&T2, - mfem::Vector *C1 = nullptr); + void GetBdrElementNeighborTransformations(mfem::ElementTransformation &T, + const mfem::IntegrationPoint &ip, + mfem::Vector *C1 = nullptr); public: - BdrGridFunctionCoefficient(mfem::ParMesh &mesh, const std::map &local_to_shared) + BdrGridFunctionCoefficient(const mfem::ParMesh &mesh, + const std::unordered_map &local_to_shared) : mesh(mesh), local_to_shared(local_to_shared) { } + // For a boundary element, return the element transformation objects for the neighboring + // domain elements. FET.Elem2 may be nullptr if the boundary is a true one-sided boundary, + // but if it is shared with another subdomain then it will be populated. Expects + // ParMesh::ExchangeFaceNbrData has been called already. + static void GetBdrElementNeighborTransformations( + int i, const mfem::ParMesh &mesh, const std::unordered_map &local_to_shared, + mfem::FaceElementTransformations &FET, mfem::IsoparametricTransformation &T1, + mfem::IsoparametricTransformation &T2, const mfem::IntegrationPoint *ip = nullptr); + // Return normal vector to the boundary element at an integration point (it is assumed // that the element transformation has already been configured at the integration point of // interest). @@ -212,11 +78,12 @@ class BdrCurrentVectorCoefficient : public mfem::VectorCoefficient, mfem::Vector C1, W, VU, VL, nor; public: - BdrCurrentVectorCoefficient(const mfem::ParGridFunction &gf, const MaterialOperator &op) - : mfem::VectorCoefficient(gf.ParFESpace()->GetParMesh()->SpaceDimension()), + BdrCurrentVectorCoefficient(const mfem::ParGridFunction &gf, + const MaterialOperator &mat_op) + : mfem::VectorCoefficient(mat_op.SpaceDimension()), BdrGridFunctionCoefficient(*gf.ParFESpace()->GetParMesh(), - op.GetLocalToSharedFaceMap()), - B(gf), mat_op(op), C1(gf.VectorDim()), W(gf.VectorDim()), VU(gf.VectorDim()), + mat_op.GetLocalToSharedFaceMap()), + B(gf), mat_op(mat_op), C1(gf.VectorDim()), W(gf.VectorDim()), VU(gf.VectorDim()), VL(gf.VectorDim()), nor(gf.VectorDim()) { } @@ -226,18 +93,17 @@ class BdrCurrentVectorCoefficient : public mfem::VectorCoefficient, { // Get neighboring elements. MFEM_ASSERT(vdim == 3, "BdrJVectorCoefficient expects a mesh in 3D space!"); - mfem::ElementTransformation *T1, *T2; - GetElementTransformations(T, ip, T1, T2, &C1); + GetBdrElementNeighborTransformations(T, ip, &C1); // For interior faces, compute J_s = -n x H = -n x μ⁻¹(B1 - B2), where B1 (B2) is B in // el1 (el2) and n points out from el1. - B.GetVectorValue(*T1, T1->GetIntPoint(), W); - mat_op.GetInvPermeability(T1->Attribute).Mult(W, VU); - if (T2) + B.GetVectorValue(*FET.Elem1, FET.Elem1->GetIntPoint(), W); + mat_op.GetInvPermeability(FET.Elem1->Attribute).Mult(W, VU); + if (FET.Elem2) { // Double-sided, not a true boundary. - B.GetVectorValue(*T2, T2->GetIntPoint(), W); - mat_op.GetInvPermeability(T2->Attribute).Mult(W, VL); + B.GetVectorValue(*FET.Elem2, FET.Elem2->GetIntPoint(), W); + mat_op.GetInvPermeability(FET.Elem2->Attribute).Mult(W, VL); VU -= VL; } @@ -270,10 +136,10 @@ class BdrChargeCoefficient : public mfem::Coefficient, public BdrGridFunctionCoe mfem::Vector C1, W, VU, VL, nor; public: - BdrChargeCoefficient(const mfem::ParGridFunction &gf, const MaterialOperator &op) + BdrChargeCoefficient(const mfem::ParGridFunction &gf, const MaterialOperator &mat_op) : mfem::Coefficient(), BdrGridFunctionCoefficient(*gf.ParFESpace()->GetParMesh(), - op.GetLocalToSharedFaceMap()), - E(gf), mat_op(op), C1(gf.VectorDim()), W(gf.VectorDim()), VU(gf.VectorDim()), + mat_op.GetLocalToSharedFaceMap()), + E(gf), mat_op(mat_op), C1(gf.VectorDim()), W(gf.VectorDim()), VU(gf.VectorDim()), VL(gf.VectorDim()), nor(gf.VectorDim()) { } @@ -281,17 +147,16 @@ class BdrChargeCoefficient : public mfem::Coefficient, public BdrGridFunctionCoe double Eval(mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip) override { // Get neighboring elements. - mfem::ElementTransformation *T1, *T2; - GetElementTransformations(T, ip, T1, T2, &C1); + GetBdrElementNeighborTransformations(T, ip, &C1); // For interior faces, compute D ⋅ n = ε (E1 - E2) ⋅ n, where E1 (E2) is E in el1 (el2) // to get a single-valued function. - E.GetVectorValue(*T1, T1->GetIntPoint(), W); - mat_op.GetPermittivityReal(T1->Attribute).Mult(W, VU); - if (T2) + E.GetVectorValue(*FET.Elem1, FET.Elem1->GetIntPoint(), W); + mat_op.GetPermittivityReal(FET.Elem1->Attribute).Mult(W, VU); + if (FET.Elem2) { - E.GetVectorValue(*T2, T2->GetIntPoint(), W); - mat_op.GetPermittivityReal(T2->Attribute).Mult(W, VL); + E.GetVectorValue(*FET.Elem2, FET.Elem2->GetIntPoint(), W); + mat_op.GetPermittivityReal(FET.Elem2->Attribute).Mult(W, VL); VU -= VL; } @@ -312,27 +177,26 @@ class BdrFluxCoefficient : public mfem::Coefficient, public BdrGridFunctionCoeff mfem::Vector V, VL, nor; public: - BdrFluxCoefficient(const mfem::ParGridFunction &gf, mfem::Vector d, - const std::map &local_to_shared) - : mfem::Coefficient(), - BdrGridFunctionCoefficient(*gf.ParFESpace()->GetParMesh(), local_to_shared), B(gf), - dir(std::move(d)), V(gf.VectorDim()), VL(gf.VectorDim()), nor(gf.VectorDim()) + BdrFluxCoefficient(const mfem::ParGridFunction &gf, const MaterialOperator &mat_op, + const mfem::Vector &d) + : mfem::Coefficient(), BdrGridFunctionCoefficient(*gf.ParFESpace()->GetParMesh(), + mat_op.GetLocalToSharedFaceMap()), + B(gf), dir(d), V(gf.VectorDim()), VL(gf.VectorDim()), nor(gf.VectorDim()) { } double Eval(mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip) override { // Get neighboring elements. - mfem::ElementTransformation *T1, *T2; - GetElementTransformations(T, ip, T1, T2); + GetBdrElementNeighborTransformations(T, ip); // For interior faces, compute the average value. Since this is only used for // continuous (normal or tangential) values, we don't care that we average out the // discontinuous (tangential or normal) parts. - B.GetVectorValue(*T1, T1->GetIntPoint(), V); - if (T2) + B.GetVectorValue(*FET.Elem1, FET.Elem1->GetIntPoint(), V); + if (FET.Elem2) { - B.GetVectorValue(*T2, T2->GetIntPoint(), VL); + B.GetVectorValue(*FET.Elem2, FET.Elem2->GetIntPoint(), VL); V += VL; V *= 0.5; } @@ -343,6 +207,7 @@ class BdrFluxCoefficient : public mfem::Coefficient, public BdrGridFunctionCoeff } }; +// Helper for DielectricInterfaceCoefficient. enum class DielectricInterfaceType { DEFAULT, @@ -373,46 +238,46 @@ class DielectricInterfaceCoefficient : public mfem::Coefficient, mfem::Vector &V) { // Get neighboring elements. - mfem::ElementTransformation *T1, *T2; - GetElementTransformations(T, ip, T1, T2, &C1); + GetBdrElementNeighborTransformations(T, ip, &C1); // Get the single-sided solution. - if (!T2) + if (!FET.Elem2) { // Ignore side, solution is single-valued. - E.GetVectorValue(*T1, T1->GetIntPoint(), V); - return T1->Attribute; + E.GetVectorValue(*FET.Elem1, FET.Elem1->GetIntPoint(), V); + return FET.Elem1->Attribute; } if (!side.Size()) { // With no side specified, try to take the solution from the element which corresponds // to the vacuum domain, or at least the one with the higher speed of light. - if (mat_op.GetLightSpeedMin(T2->Attribute) > mat_op.GetLightSpeedMax(T1->Attribute)) + if (mat_op.GetLightSpeedMin(FET.Elem2->Attribute) > + mat_op.GetLightSpeedMax(FET.Elem1->Attribute)) { - E.GetVectorValue(*T2, T2->GetIntPoint(), V); - return T2->Attribute; + E.GetVectorValue(*FET.Elem2, FET.Elem2->GetIntPoint(), V); + return FET.Elem2->Attribute; } - E.GetVectorValue(*T1, T1->GetIntPoint(), V); - return T1->Attribute; + E.GetVectorValue(*FET.Elem1, FET.Elem1->GetIntPoint(), V); + return FET.Elem1->Attribute; } if (C1 * side < 0.0) { // Get solution in el2. - E.GetVectorValue(*T2, T2->GetIntPoint(), V); - return T2->Attribute; + E.GetVectorValue(*FET.Elem2, FET.Elem2->GetIntPoint(), V); + return FET.Elem2->Attribute; } // Get solution in el1. - E.GetVectorValue(*T1, T1->GetIntPoint(), V); - return T1->Attribute; + E.GetVectorValue(*FET.Elem1, FET.Elem1->GetIntPoint(), V); + return FET.Elem1->Attribute; } public: DielectricInterfaceCoefficient(const mfem::ParGridFunction &gf, - const MaterialOperator &op, double ti, double ei, - mfem::Vector s) + const MaterialOperator &mat_op, double ti, double ei, + const mfem::Vector &s) : mfem::Coefficient(), BdrGridFunctionCoefficient(*gf.ParFESpace()->GetParMesh(), - op.GetLocalToSharedFaceMap()), - E(gf), mat_op(op), ts(ti), epsilon(ei), side(std::move(s)), C1(gf.VectorDim()), + mat_op.GetLocalToSharedFaceMap()), + E(gf), mat_op(mat_op), ts(ti), epsilon(ei), side(s), C1(gf.VectorDim()), V(gf.VectorDim()), nor(gf.VectorDim()) { } @@ -477,6 +342,7 @@ inline double DielectricInterfaceCoefficient:: return 0.5 * ts * epsilon * (V * V); } +// Helper for EnergyDensityCoefficient. enum class EnergyDensityType { ELECTRIC, @@ -498,10 +364,10 @@ class EnergyDensityCoefficient : public mfem::Coefficient, public BdrGridFunctio const mfem::IntegrationPoint &ip, int attr); public: - EnergyDensityCoefficient(const GridFunctionType &gf, const MaterialOperator &op) + EnergyDensityCoefficient(const GridFunctionType &gf, const MaterialOperator &mat_op) : mfem::Coefficient(), BdrGridFunctionCoefficient(*gf.ParFESpace()->GetParMesh(), - op.GetLocalToSharedFaceMap()), - U(gf), mat_op(op), V(gf.ParFESpace()->GetParMesh()->SpaceDimension()) + mat_op.GetLocalToSharedFaceMap()), + U(gf), mat_op(mat_op), V(mat_op.SpaceDimension()) { } @@ -514,19 +380,20 @@ class EnergyDensityCoefficient : public mfem::Coefficient, public BdrGridFunctio if (T.ElementType == mfem::ElementTransformation::BDR_ELEMENT) { // Get neighboring elements. - mfem::ElementTransformation *T1, *T2; - GetElementTransformations(T, ip, T1, T2); + GetBdrElementNeighborTransformations(T, ip); - // For interior faces, compute the value on the side where the material property is - // larger (typically should choose the non-vacuum side). - if (T2 && - mat_op.GetLightSpeedMax(T2->Attribute) < mat_op.GetLightSpeedMin(T1->Attribute)) + // For interior faces, compute the value on the side where the speed of light is + // smaller (typically should choose the non-vacuum side). + if (FET.Elem2 && mat_op.GetLightSpeedMax(FET.Elem2->Attribute) < + mat_op.GetLightSpeedMin(FET.Elem1->Attribute)) { - return GetLocalEnergyDensity(*T2, T2->GetIntPoint(), T2->Attribute); + return GetLocalEnergyDensity(*FET.Elem2, FET.Elem2->GetIntPoint(), + FET.Elem2->Attribute); } else { - return GetLocalEnergyDensity(*T1, T1->GetIntPoint(), T1->Attribute); + return GetLocalEnergyDensity(*FET.Elem1, FET.Elem1->GetIntPoint(), + FET.Elem1->Attribute); } } MFEM_ABORT("Unsupported element type in EnergyDensityCoefficient!"); @@ -591,11 +458,11 @@ class BdrFieldVectorCoefficient : public mfem::VectorCoefficient, const MaterialOperator &mat_op; public: - BdrFieldVectorCoefficient(const mfem::ParGridFunction &gf, const MaterialOperator &op) - : mfem::VectorCoefficient(gf.ParFESpace()->GetParMesh()->SpaceDimension()), + BdrFieldVectorCoefficient(const mfem::ParGridFunction &gf, const MaterialOperator &mat_op) + : mfem::VectorCoefficient(mat_op.SpaceDimension()), BdrGridFunctionCoefficient(*gf.ParFESpace()->GetParMesh(), - op.GetLocalToSharedFaceMap()), - U(gf), mat_op(op) + mat_op.GetLocalToSharedFaceMap()), + U(gf), mat_op(mat_op) { } @@ -603,19 +470,18 @@ class BdrFieldVectorCoefficient : public mfem::VectorCoefficient, const mfem::IntegrationPoint &ip) override { // Get neighboring elements. - mfem::ElementTransformation *T1, *T2; - GetElementTransformations(T, ip, T1, T2); + GetBdrElementNeighborTransformations(T, ip); - // For interior faces, compute the value on the side where the material property is - // larger (typically should choose the non-vacuum side). - if (T2 && - mat_op.GetLightSpeedMax(T2->Attribute) < mat_op.GetLightSpeedMin(T1->Attribute)) + // For interior faces, compute the value on the side where the speed of light is + // smaller (typically should choose the non-vacuum side). + if (FET.Elem2 && mat_op.GetLightSpeedMax(FET.Elem2->Attribute) < + mat_op.GetLightSpeedMin(FET.Elem1->Attribute)) { - U.GetVectorValue(*T2, T2->GetIntPoint(), V); + U.GetVectorValue(*FET.Elem2, FET.Elem2->GetIntPoint(), V); } else { - U.GetVectorValue(*T1, T1->GetIntPoint(), V); + U.GetVectorValue(*FET.Elem1, FET.Elem1->GetIntPoint(), V); } } }; @@ -627,210 +493,197 @@ class BdrFieldCoefficient : public mfem::Coefficient, public BdrGridFunctionCoef const MaterialOperator &mat_op; public: - BdrFieldCoefficient(const mfem::ParGridFunction &gf, const MaterialOperator &op) + BdrFieldCoefficient(const mfem::ParGridFunction &gf, const MaterialOperator &mat_op) : mfem::Coefficient(), BdrGridFunctionCoefficient(*gf.ParFESpace()->GetParMesh(), - op.GetLocalToSharedFaceMap()), - U(gf), mat_op(op) + mat_op.GetLocalToSharedFaceMap()), + U(gf), mat_op(mat_op) { } double Eval(mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip) override { // Get neighboring elements. - mfem::ElementTransformation *T1, *T2; - GetElementTransformations(T, ip, T1, T2); + GetBdrElementNeighborTransformations(T, ip); - // For interior faces, compute the value on the side where the material property is - // larger (typically should choose the non-vacuum side). - if (T2 && - mat_op.GetLightSpeedMax(T2->Attribute) < mat_op.GetLightSpeedMin(T1->Attribute)) + // For interior faces, compute the value on the side where the speed of light is + // smaller (typically should choose the non-vacuum side). + if (FET.Elem2 && mat_op.GetLightSpeedMax(FET.Elem2->Attribute) < + mat_op.GetLightSpeedMin(FET.Elem1->Attribute)) { - return U.GetValue(*T2, T2->GetIntPoint()); + return U.GetValue(*FET.Elem2, FET.Elem2->GetIntPoint()); } else { - return U.GetValue(*T1, T1->GetIntPoint()); + return U.GetValue(*FET.Elem1, FET.Elem1->GetIntPoint()); } } }; -// Wraps a mfem::MatrixCoefficient to compute a scalar coefficient as nᵀ M n. Only works -// for square matrix coefficients of size equal to the spatial dimension. -class NormalProjectedCoefficient : public mfem::Coefficient -{ - std::unique_ptr c; - mfem::DenseMatrix K; - mfem::Vector nor; - -public: - NormalProjectedCoefficient(std::unique_ptr &&coef) - : mfem::Coefficient(), c(std::move(coef)), K(c->GetHeight(), c->GetWidth()), - nor(c->GetHeight()) - { - } - - double Eval(mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip) override - { - c->Eval(K, T, ip); - BdrGridFunctionCoefficient::GetNormal(T, nor); - return K.InnerProduct(nor, nor); - } -}; +// +// More helpful coefficient types. Wrapper coefficients allow additions of scalar and vector +// or matrix coefficients. Restricted coefficients only compute the coefficient if for the +// given list of attributes. Sum coefficients own a list of coefficients to add. +// class VectorWrappedCoefficient : public mfem::VectorCoefficient { private: - std::unique_ptr c; + std::unique_ptr coeff; public: - VectorWrappedCoefficient(int d, std::unique_ptr &&coef) - : mfem::VectorCoefficient(d), c(std::move(coef)) + VectorWrappedCoefficient(int dim, std::unique_ptr &&coeff) + : mfem::VectorCoefficient(dim), coeff(std::move(coeff)) { } - void SetTime(double t) override - { - mfem::VectorCoefficient::SetTime(t); - c->SetTime(t); - } - void Eval(mfem::Vector &V, mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip) override { V.SetSize(vdim); - V = c->Eval(T, ip); + V = coeff->Eval(T, ip); } }; class MatrixWrappedCoefficient : public mfem::MatrixCoefficient { private: - std::unique_ptr c; + std::unique_ptr coeff; public: - MatrixWrappedCoefficient(int d, std::unique_ptr &&coef) - : mfem::MatrixCoefficient(d), c(std::move(coef)) - { - } - - void SetTime(double t) override + MatrixWrappedCoefficient(int dim, std::unique_ptr &&coeff) + : mfem::MatrixCoefficient(dim), coeff(std::move(coeff)) { - mfem::MatrixCoefficient::SetTime(t); - c->SetTime(t); } void Eval(mfem::DenseMatrix &K, mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip) override { - K.Diag(c->Eval(T, ip), height); + K.Diag(coeff->Eval(T, ip), height); } }; -class SumCoefficient : public mfem::Coefficient +class RestrictedCoefficient : public mfem::Coefficient { private: - std::vector, const mfem::Array *>> c; + std::unique_ptr coeff; + const mfem::Array &attr; - void AddCoefficient(std::unique_ptr &&coef, - const mfem::Array *marker) +public: + RestrictedCoefficient(std::unique_ptr &&coeff, + const mfem::Array &attr) + : mfem::Coefficient(), coeff(std::move(coeff)), attr(attr) { - c.emplace_back(std::move(coef), marker); } -public: - SumCoefficient() : mfem::Coefficient() {} - - bool empty() const { return c.empty(); } - - void AddCoefficient(std::unique_ptr &&coef) + double Eval(mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip) override { - AddCoefficient(std::move(coef), nullptr); + return (attr.Find(T.Attribute) < 0) ? 0.0 : coeff->Eval(T, ip); } +}; - void AddCoefficient(std::unique_ptr &&coef, - const mfem::Array &marker) +class RestrictedVectorCoefficient : public mfem::VectorCoefficient +{ +private: + std::unique_ptr coeff; + const mfem::Array &attr; + +public: + RestrictedVectorCoefficient(std::unique_ptr &&coeff, + const mfem::Array &attr) + : mfem::VectorCoefficient(coeff->GetVDim()), coeff(std::move(coeff)), attr(attr) { - AddCoefficient(std::move(coef), &marker); } - void SetTime(double t) override + void Eval(mfem::Vector &V, mfem::ElementTransformation &T, + const mfem::IntegrationPoint &ip) override { - mfem::Coefficient::SetTime(t); - for (auto &[coef, marker] : c) + if (attr.Find(T.Attribute) < 0) { - coef->SetTime(t); + V.SetSize(vdim); + V = 0.0; } - } - - double Eval(mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip) override - { - double val = 0.0; - for (auto &[coef, marker] : c) + else { - if (!marker || (*marker)[T.Attribute - 1]) - { - val += coef->Eval(T, ip); - } + coeff->Eval(V, T, ip); } - return val; } }; -class SumVectorCoefficient : public mfem::VectorCoefficient +class RestrictedMatrixCoefficient : public mfem::MatrixCoefficient { private: - std::vector, const mfem::Array *>> - c; + std::unique_ptr coeff; + const mfem::Array &attr; - void AddCoefficient(std::unique_ptr &&coef, - const mfem::Array *marker) +public: + RestrictedMatrixCoefficient(std::unique_ptr &&coeff, + const mfem::Array &attr) + : mfem::MatrixCoefficient(coeff->GetHeight(), coeff->GetWidth()), + coeff(std::move(coeff)), attr(attr) { - MFEM_VERIFY(coef->GetVDim() == vdim, - "Invalid VectorCoefficient dimensions for SumVectorCoefficient!"); - c.emplace_back(std::move(coef), marker); } - void AddCoefficient(std::unique_ptr &&coef, - const mfem::Array *marker) + void Eval(mfem::DenseMatrix &K, mfem::ElementTransformation &T, + const mfem::IntegrationPoint &ip) override { - c.emplace_back(std::make_unique(vdim, std::move(coef)), - marker); + if (attr.Find(T.Attribute) < 0) + { + K.SetSize(height, width); + K = 0.0; + } + else + { + coeff->Eval(K, T, ip); + } } +}; + +class SumCoefficient : public mfem::Coefficient +{ +private: + std::vector, double>> c; public: - SumVectorCoefficient(int d) : mfem::VectorCoefficient(d) {} + SumCoefficient() : mfem::Coefficient() {} bool empty() const { return c.empty(); } - void AddCoefficient(std::unique_ptr &&coef) + void AddCoefficient(std::unique_ptr &&coeff, double a = 1.0) { - AddCoefficient(std::move(coef), nullptr); + c.emplace_back(std::move(coeff), a); } - void AddCoefficient(std::unique_ptr &&coef, - const mfem::Array &marker) + double Eval(mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip) override { - AddCoefficient(std::move(coef), &marker); + double val = 0.0; + for (auto &[coeff, a] : c) + { + val += a * coeff->Eval(T, ip); + } + return val; } +}; - void AddCoefficient(std::unique_ptr &&coef) - { - AddCoefficient(std::move(coef), nullptr); - } +class SumVectorCoefficient : public mfem::VectorCoefficient +{ +private: + std::vector, double>> c; + +public: + SumVectorCoefficient(int d) : mfem::VectorCoefficient(d) {} + + bool empty() const { return c.empty(); } - void AddCoefficient(std::unique_ptr &&coef, - const mfem::Array &marker) + void AddCoefficient(std::unique_ptr &&coeff, double a = 1.0) { - AddCoefficient(std::move(coef), &marker); + MFEM_VERIFY(coeff->GetVDim() == vdim, + "Invalid VectorCoefficient dimensions for SumVectorCoefficient!"); + c.emplace_back(std::move(coeff), a); } - void SetTime(double t) override + void AddCoefficient(std::unique_ptr &&coeff, double a = 1.0) { - mfem::VectorCoefficient::SetTime(t); - for (auto &[coef, marker] : c) - { - coef->SetTime(t); - } + c.emplace_back(std::make_unique(vdim, std::move(coeff)), a); } void Eval(mfem::Vector &V, mfem::ElementTransformation &T, @@ -839,13 +692,10 @@ class SumVectorCoefficient : public mfem::VectorCoefficient mfem::Vector U(vdim); V.SetSize(vdim); V = 0.0; - for (auto &[coef, marker] : c) + for (auto &[coeff, a] : c) { - if (!marker || (*marker)[T.Attribute - 1]) - { - coef->Eval(U, T, ip); - V += U; - } + coeff->Eval(U, T, ip); + V.Add(a, U); } } }; @@ -853,25 +703,7 @@ class SumVectorCoefficient : public mfem::VectorCoefficient class SumMatrixCoefficient : public mfem::MatrixCoefficient { private: - std::vector, const mfem::Array *>> - c; - - void AddCoefficient(std::unique_ptr &&coef, - const mfem::Array *marker) - { - MFEM_VERIFY(coef->GetHeight() == height && coef->GetWidth() == width, - "Invalid MatrixCoefficient dimensions for SumMatrixCoefficient!"); - c.emplace_back(std::move(coef), marker); - } - - void AddCoefficient(std::unique_ptr &&coef, - const mfem::Array *marker) - { - MFEM_VERIFY(width == height, "MatrixWrappedCoefficient can only be constructed for " - "square MatrixCoefficient objects!"); - c.emplace_back(std::make_unique(height, std::move(coef)), - marker); - } + std::vector, double>> c; public: SumMatrixCoefficient(int d) : mfem::MatrixCoefficient(d) {} @@ -879,35 +711,18 @@ class SumMatrixCoefficient : public mfem::MatrixCoefficient bool empty() const { return c.empty(); } - void AddCoefficient(std::unique_ptr &&coef) - { - AddCoefficient(std::move(coef), nullptr); - } - - void AddCoefficient(std::unique_ptr &&coef, - const mfem::Array &marker) - { - AddCoefficient(std::move(coef), &marker); - } - - void AddCoefficient(std::unique_ptr &&coef) + void AddCoefficient(std::unique_ptr &&coeff, double a) { - AddCoefficient(std::move(coef), nullptr); - } - - void AddCoefficient(std::unique_ptr &&coef, - const mfem::Array &marker) - { - AddCoefficient(std::move(coef), &marker); + MFEM_VERIFY(coeff->GetHeight() == height && coeff->GetWidth() == width, + "Invalid MatrixCoefficient dimensions for SumMatrixCoefficient!"); + c.emplace_back(std::move(coeff), a); } - void SetTime(double t) override + void AddCoefficient(std::unique_ptr &&coeff, double a) { - mfem::MatrixCoefficient::SetTime(t); - for (auto &[coef, marker] : c) - { - coef->SetTime(t); - } + MFEM_VERIFY(width == height, "MatrixWrappedCoefficient can only be constructed for " + "square MatrixCoefficient objects!"); + c.emplace_back(std::make_unique(height, std::move(coeff)), a); } void Eval(mfem::DenseMatrix &K, mfem::ElementTransformation &T, @@ -916,13 +731,10 @@ class SumMatrixCoefficient : public mfem::MatrixCoefficient mfem::DenseMatrix M(height, width); K.SetSize(height, width); K = 0.0; - for (auto &[coef, marker] : c) + for (auto &[coeff, a] : c) { - if (!marker || (*marker)[T.Attribute - 1]) - { - coef->Eval(M, T, ip); - K += M; - } + coeff->Eval(M, T, ip); + K.Add(a, M); } } }; diff --git a/palace/models/materialoperator.cpp b/palace/models/materialoperator.cpp index 01aa9cf2f..2046b7814 100644 --- a/palace/models/materialoperator.cpp +++ b/palace/models/materialoperator.cpp @@ -6,6 +6,7 @@ #include #include #include +#include "fem/coefficient.hpp" #include "utils/communication.hpp" #include "utils/geodata.hpp" #include "utils/iodata.hpp" @@ -277,54 +278,177 @@ mfem::DenseMatrix ToDenseMatrix(const config::SymmetricMatrixData &data) return M; } +auto BuildLocalToSharedFaceMap(const mfem::ParMesh &mesh) +{ + // Construct shared face mapping for boundary coefficients. The inverse mapping is + // constructed as part of mfem::ParMesh, but we need this mapping when looping over + // all mesh faces. + std::unordered_map l2s; + l2s.reserve(mesh.GetNSharedFaces()); + for (int i = 0; i < mesh.GetNSharedFaces(); i++) + { + l2s[mesh.GetSharedFace(i)] = i; + } + return l2s; +} + +auto BuildAttributeGlobalToLocal(const mfem::ParMesh &mesh) +{ + // Set up sparse map from global domain attributes to local ones on this process. + // Include ghost elements for all shared faces so we have their material properties + // stored locally. + std::unordered_map loc_attr; + mfem::FaceElementTransformations FET; + mfem::IsoparametricTransformation T1, T2; + int count = 0; + for (int i = 0; i < mesh.GetNE(); i++) + { + const int attr = mesh.GetAttribute(i); + if (loc_attr.find(attr) == loc_attr.end()) + { + loc_attr[attr] = ++count; + } + } + for (int i = 0; i < mesh.GetNSharedFaces(); i++) + { + mesh.GetSharedFaceTransformations(i, &FET, &T1, &T2); + int attr = FET.Elem1->Attribute; + if (loc_attr.find(attr) == loc_attr.end()) + { + loc_attr[attr] = ++count; + } + attr = FET.Elem2->Attribute; + if (loc_attr.find(attr) == loc_attr.end()) + { + loc_attr[attr] = ++count; + } + } + return loc_attr; +} + +auto GetBdrNeighborAttribute(int i, const mfem::ParMesh &mesh, + const std::unordered_map &face_loc_to_shared, + mfem::FaceElementTransformations &FET, + mfem::IsoparametricTransformation &T1, + mfem::IsoparametricTransformation &T2) +{ + // For internal boundaries, use the element which corresponds to the vacuum domain, or + // at least the one with the higher speed of light. + BdrGridFunctionCoefficient::GetBdrElementNeighborTransformations( + i, mesh, face_loc_to_shared, FET, T1, T2); + // return (FET.Elem2 && GetLightSpeedMin(FET.Elem2->Attribute) > + // GetLightSpeedMax(FET.Elem1->Attribute)) + // ? FET.Elem2->Attribute + // : FET.Elem1->Attribute; + return (FET.Elem2 && FET.Elem2->Attribute < FET.Elem1->Attribute) ? FET.Elem2->Attribute + : FET.Elem1->Attribute; +} + +auto BuildBdrAttributeGlobalToLocal(const mfem::ParMesh &mesh, + const std::unordered_map &face_loc_to_shared) +{ + // Set up sparse map from global boundary attributes to local ones on this process. Each + // original global boundary attribute maps to a key-value pairing of global domain + // attributes which neighbor the given boundary and local boundary attributes. + std::unordered_map> loc_bdr_attr; + mfem::FaceElementTransformations FET; + mfem::IsoparametricTransformation T1, T2; + int count = 0; + for (int i = 0; i < mesh.GetNBE(); i++) + { + const int attr = mesh.GetBdrAttribute(i); + const int nbr_attr = GetBdrNeighborAttribute(i, mesh, face_loc_to_shared, FET, T1, T2); + auto &bdr_attr_map = loc_bdr_attr[attr]; + if (bdr_attr_map.find(nbr_attr) == bdr_attr_map.end()) + { + bdr_attr_map[nbr_attr] = ++count; + } + } + return loc_bdr_attr; +} + } // namespace -MaterialOperator::MaterialOperator(const IoData &iodata, mfem::ParMesh &mesh) +MaterialOperator::MaterialOperator(const IoData &iodata, mfem::ParMesh &mesh) : mesh(mesh) { + mesh.ExchangeFaceNbrData(); + face_loc_to_shared = BuildLocalToSharedFaceMap(mesh); + loc_attr = BuildAttributeGlobalToLocal(mesh); + loc_bdr_attr = BuildBdrAttributeGlobalToLocal(mesh, face_loc_to_shared); + SetUpMaterialProperties(iodata, mesh); } -void MaterialOperator::SetUpMaterialProperties(const IoData &iodata, mfem::ParMesh &mesh) +void MaterialOperator::SetUpMaterialProperties(const IoData &iodata, + const mfem::ParMesh &mesh) { // Check that material attributes have been specified correctly. The mesh attributes may // be non-contiguous and when no material attribute is specified the elements are deleted // from the mesh so as to not cause problems. MFEM_VERIFY(!iodata.domains.materials.empty(), "Materials must be non-empty!"); - int attr_max = mesh.attributes.Max(); - mfem::Array attr_marker(attr_max); - attr_marker = 0; - for (auto attr : mesh.attributes) { - attr_marker[attr - 1] = 1; + int attr_max = mesh.attributes.Size() ? mesh.attributes.Max() : 0; + mfem::Array attr_marker(attr_max); + attr_marker = 0; + for (auto attr : mesh.attributes) + { + attr_marker[attr - 1] = 1; + } + for (const auto &data : iodata.domains.materials) + { + for (auto attr : data.attributes) + { + MFEM_VERIFY( + attr > 0 && attr <= attr_max, + "Material attribute tags must be non-negative and correspond to attributes " + "in the mesh!"); + MFEM_VERIFY(attr_marker[attr - 1], "Unknown material attribute " << attr << "!"); + } + } } - for (const auto &data : iodata.domains.materials) + + // Set up material properties of the different domain regions, represented with element- + // wise constant matrix-valued coefficients for the relative permeability, permittivity, + // and other material properties. + mfem::Array mat_marker(iodata.domains.materials.size()); + mat_marker = 0; + int nmats = 0; + for (std::size_t i = 0; i < iodata.domains.materials.size(); i++) { + const auto &data = iodata.domains.materials[i]; for (auto attr : data.attributes) { - MFEM_VERIFY( - attr > 0 && attr <= attr_max, - "Material attribute tags must be non-negative and correspond to attributes " - "in the mesh!"); - MFEM_VERIFY(attr_marker[attr - 1], "Unknown material attribute " << attr << "!"); + if (loc_attr.find(attr) != loc_attr.end()) + { + mat_marker[i] = 1; + nmats++; + break; + } } } + attr_mat.SetSize(loc_attr.size()); + attr_mat = -1; - // Set up material properties of the different domain regions, represented with piece-wise - // constant matrix-valued coefficients for the relative permeability and permittivity, - // and other material properties. const int sdim = mesh.SpaceDimension(); - mat_muinv.resize(attr_max, mfem::DenseMatrix(sdim)); - mat_epsilon.resize(attr_max, mfem::DenseMatrix(sdim)); - mat_epsilon_imag.resize(attr_max, mfem::DenseMatrix(sdim)); - mat_epsilon_abs.resize(attr_max, mfem::DenseMatrix(sdim)); - mat_invz0.resize(attr_max, mfem::DenseMatrix(sdim)); - mat_c0.resize(attr_max, mfem::DenseMatrix(sdim)); - mat_sigma.resize(attr_max, mfem::DenseMatrix(sdim)); - mat_invLondon.resize(attr_max, mfem::DenseMatrix(sdim)); - mat_c0_min.resize(attr_max, 0.0); - mat_c0_max.resize(attr_max, 0.0); - for (const auto &data : iodata.domains.materials) + mat_muinv.SetSize(sdim, sdim, nmats); + mat_epsilon.SetSize(sdim, sdim, nmats); + mat_epsilon_imag.SetSize(sdim, sdim, nmats); + mat_epsilon_abs.SetSize(sdim, sdim, nmats); + mat_invz0.SetSize(sdim, sdim, nmats); + mat_c0.SetSize(sdim, sdim, nmats); + mat_sigma.SetSize(sdim, sdim, nmats); + mat_invLondon.SetSize(sdim, sdim, nmats); + mat_c0_min.SetSize(nmats); + mat_c0_max.SetSize(nmats); + + int count = 0; + for (std::size_t i = 0; i < iodata.domains.materials.size(); i++) { + if (!mat_marker[i]) + { + continue; + } + const auto &data = iodata.domains.materials[i]; if (iodata.problem.type == config::ProblemData::Type::ELECTROSTATIC) { MFEM_VERIFY(IsValid(data.epsilon_r), "Material has no valid permittivity defined!"); @@ -362,83 +486,386 @@ void MaterialOperator::SetUpMaterialProperties(const IoData &iodata, mfem::ParMe "electrical conductivity!"); } } + + // Map all attributes to this material property index. for (auto attr : data.attributes) { - MFEM_VERIFY( - mat_c0_min.at(attr - 1) == 0.0 && mat_c0_max.at(attr - 1) == 0.0, - "Detected multiple definitions of material properties for domain attribute " - << attr << "!"); - - // Compute the inverse of the input permeability matrix. - mfem::DenseMatrix mu_r = ToDenseMatrix(data.mu_r); - mfem::DenseMatrixInverse(mu_r, true).GetInverseMatrix(mat_muinv.at(attr - 1)); - - // Material permittivity: Im{ε} = - ε * tan(δ) - mfem::DenseMatrix T(sdim, sdim); - mat_epsilon.at(attr - 1) = ToDenseMatrix(data.epsilon_r); - Mult(mat_epsilon.at(attr - 1), ToDenseMatrix(data.tandelta), T); - T *= -1.0; - mat_epsilon_imag.at(attr - 1) = T; - - // ε * √(I + tan(δ) * tan(δ)ᵀ) - MultAAt(ToDenseMatrix(data.tandelta), T); - for (int i = 0; i < T.Height(); i++) + auto it = loc_attr.find(attr); + if (it != loc_attr.end()) { - T(i, i) += 1.0; + MFEM_VERIFY( + attr_mat[it->second - 1] < 0, + "Detected multiple definitions of material properties for domain attribute " + << attr << "!"); + attr_mat[it->second - 1] = count; } - Mult(mat_epsilon.at(attr - 1), MatrixSqrt(T), mat_epsilon_abs.at(attr - 1)); + } + + // Compute the inverse of the input permeability matrix. + mfem::DenseMatrix mu_r = ToDenseMatrix(data.mu_r); + mfem::DenseMatrixInverse(mu_r, true).GetInverseMatrix(mat_muinv(count)); + + // Material permittivity: Re{ε} = ε, Im{ε} = -ε * tan(δ) + mfem::DenseMatrix T(sdim, sdim); + mat_epsilon(count) = ToDenseMatrix(data.epsilon_r); + Mult(mat_epsilon(count), ToDenseMatrix(data.tandelta), T); + T *= -1.0; + mat_epsilon_imag(count) = T; + if (mat_epsilon_imag(count).MaxMaxNorm() > 0.0) + { + for (auto attr : data.attributes) + { + losstan_attr.Append(attr); + } + } - // √μ⁻¹ ε - Mult(mat_muinv.at(attr - 1), mat_epsilon.at(attr - 1), mat_invz0.at(attr - 1)); - mat_invz0.at(attr - 1) = MatrixSqrt(mat_invz0.at(attr - 1)); + // ε * √(I + tan(δ) * tan(δ)ᵀ) + MultAAt(ToDenseMatrix(data.tandelta), T); + for (int d = 0; d < T.Height(); d++) + { + T(d, d) += 1.0; + } + Mult(mat_epsilon(count), MatrixSqrt(T), mat_epsilon_abs(count)); - // (√μ ε)⁻¹ - mfem::DenseMatrixInverse(mat_epsilon.at(attr - 1), true).GetInverseMatrix(T); - Mult(mat_muinv.at(attr - 1), T, mat_c0.at(attr - 1)); - mat_c0.at(attr - 1) = MatrixSqrt(mat_c0.at(attr - 1)); - mat_c0_min.at(attr - 1) = mat_c0.at(attr - 1).CalcSingularvalue(sdim - 1); - mat_c0_max.at(attr - 1) = mat_c0.at(attr - 1).CalcSingularvalue(0); + // √μ⁻¹ ε + Mult(mat_muinv(count), mat_epsilon(count), mat_invz0(count)); + mat_invz0(count) = MatrixSqrt(mat_invz0(count)); - // Electrical conductivity, σ - mat_sigma.at(attr - 1) = ToDenseMatrix(data.sigma); + // (√μ ε)⁻¹ + mfem::DenseMatrixInverse(mat_epsilon(count), true).GetInverseMatrix(T); + Mult(mat_muinv(count), T, mat_c0(count)); + mat_c0(count) = MatrixSqrt(mat_c0(count)); + mat_c0_min[count] = mat_c0(count).CalcSingularvalue(sdim - 1); + mat_c0_max[count] = mat_c0(count).CalcSingularvalue(0); - // λ⁻² * μ⁻¹ - mat_invLondon.at(attr - 1) = mat_muinv.at(attr - 1); - mat_invLondon.at(attr - 1) *= - std::abs(data.lambda_L) > 0.0 ? std::pow(data.lambda_L, -2.0) : 0.0; + // Electrical conductivity, σ + mat_sigma(count) = ToDenseMatrix(data.sigma); + if (mat_sigma(count).MaxMaxNorm() > 0.0) + { + for (auto attr : data.attributes) + { + conductivity_attr.Append(attr); + } } + + // λ⁻² * μ⁻¹ + mat_invLondon(count) = mat_muinv(count); + mat_invLondon(count) *= + std::abs(data.lambda_L) > 0.0 ? std::pow(data.lambda_L, -2.0) : 0.0; + if (mat_invLondon(count).MaxMaxNorm() > 0.0) + { + for (auto attr : data.attributes) + { + london_attr.Append(attr); + } + } + + count++; } +} - // Construct shared face mapping for boundary coefficients. This is useful to have in one - // place alongside material properties so we construct and store it here. - for (int i = 0; i < mesh.GetNSharedFaces(); i++) +mfem::Array MaterialOperator::GetBdrAttributeToMaterial() const +{ + // Construct map from all (contiguous) local boundary attributes to the material index in + // the neighboring element. + int bdr_attr_max = 0; + for (const auto &[attr, bdr_attr_map] : loc_bdr_attr) { - local_to_shared[mesh.GetSharedFace(i)] = i; + bdr_attr_max += bdr_attr_map.size(); } + mfem::Array bdr_attr_mat(bdr_attr_max); + bdr_attr_mat = -1; + for (const auto &[attr, bdr_attr_map] : loc_bdr_attr) + { + for (auto it = bdr_attr_map.begin(); it != bdr_attr_map.end(); ++it) + { + MFEM_ASSERT(it->second > 0 && it->second <= bdr_attr_max, + "Invalid local boundary attribute " << it->second << "!"); + bdr_attr_mat[it->second - 1] = AttrToMat(it->first); + } + } + return bdr_attr_mat; +} + +MaterialPropertyCoefficient::MaterialPropertyCoefficient( + const mfem::Array &attr_mat_, const mfem::DenseTensor &mat_coeff_, double a) + : mfem::MatrixCoefficient(0, 0), attr_mat(attr_mat_), mat_coeff(mat_coeff_) +{ + for (int k = 0; k < mat_coeff.SizeK(); k++) + { + mat_coeff(k) *= a; + } + height = mat_coeff.SizeI(); + width = mat_coeff.SizeJ(); +} + +namespace +{ - // Mark selected material attributes from the mesh as having certain local properties. - mfem::Array losstan_mats, conductivity_mats, london_mats; - losstan_mats.Reserve(attr_max); - conductivity_mats.Reserve(attr_max); - london_mats.Reserve(attr_max); - for (int i = 0; i < attr_max; i++) +void UpdateProperty(mfem::DenseTensor &mat_coeff, int k, double coeff, double a) +{ + // Constant diagonal coefficient. + if (mat_coeff.SizeI() == 0 && mat_coeff.SizeJ() == 0) + { + // Initialize the coefficient material properties. + MFEM_VERIFY(k == 0 && mat_coeff.SizeK() == 1, + "Unexpected initial size for MaterialPropertyCoefficient!"); + mat_coeff.SetSize(1, 1, mat_coeff.SizeK()); + mat_coeff(0, 0, k) = a * coeff; + } + else { - if (mat_epsilon_imag.at(i).MaxMaxNorm() > 0.0) + MFEM_VERIFY(mat_coeff.SizeI() == mat_coeff.SizeJ(), + "Invalid dimensions for MaterialPropertyCoefficient update!"); + for (int i = 0; i < mat_coeff.SizeI(); i++) { - losstan_mats.Append(i + 1); // Markers are 1-based + mat_coeff(i, i, k) += a * coeff; } - if (mat_sigma.at(i).MaxMaxNorm() > 0.0) + } +} + +void UpdateProperty(mfem::DenseTensor &mat_coeff, int k, const mfem::DenseMatrix &coeff, + double a) +{ + if (mat_coeff.SizeI() == 0 && mat_coeff.SizeJ() == 0) + { + // Initialize the coefficient material properties. + MFEM_VERIFY(k == 0 && mat_coeff.SizeK() == 1, + "Unexpected initial size for MaterialPropertyCoefficient!"); + mat_coeff.SetSize(coeff.Height(), coeff.Width(), mat_coeff.SizeK()); + mat_coeff(k).Set(a, coeff); + } + else if (coeff.Height() == mat_coeff.SizeI() && coeff.Width() == mat_coeff.SizeJ()) + { + // Add as full matrix. + mat_coeff(k).Add(a, coeff); + } + else if (coeff.Height() == 1 && coeff.Width() == 1) + { + // Add as diagonal. + UpdateProperty(mat_coeff, k, coeff(0, 0), a); + } + else if (mat_coeff.SizeI() == 1 && mat_coeff.SizeJ() == 1) + { + // Convert to matrix coefficient and previous data add as diagonal. + mfem::DenseTensor mat_coeff_scalar(mat_coeff); + mat_coeff.SetSize(coeff.Height(), coeff.Width(), mat_coeff_scalar.SizeK()); + mat_coeff = 0.0; + for (int l = 0; l < mat_coeff.SizeK(); l++) + { + UpdateProperty(mat_coeff, l, mat_coeff_scalar(0, 0, l), 1.0); + } + mat_coeff(k).Add(a, coeff); + } + else + { + MFEM_ABORT("Invalid dimensions when updating material property at index " << k << "!"); + } +} + +bool Equals(const mfem::DenseMatrix &mat_coeff, double coeff, double a) +{ + MFEM_VERIFY(mat_coeff.Height() == mat_coeff.Width(), + "Invalid dimensions for MaterialPropertyCoefficient update!"); + constexpr double tol = 1.0e-9; + for (int i = 0; i < mat_coeff.Height(); i++) + { + if (std::abs(mat_coeff(i, i) - a * coeff) >= tol * std::abs(mat_coeff(i, i))) + { + return false; + } + for (int j = 0; j < mat_coeff.Width(); j++) + { + if (j != i && std::abs(mat_coeff(i, j)) > 0.0) + { + return false; + } + } + } + return true; +} + +bool Equals(const mfem::DenseMatrix &mat_coeff, const mfem::DenseMatrix &coeff, double a) +{ + if (coeff.Height() == 1 && coeff.Width() == 1) + { + return Equals(mat_coeff, coeff(0, 0), a); + } + else + { + constexpr double tol = 1.0e-9; + mfem::DenseMatrix T(mat_coeff); + T.Add(-a, coeff); + return (T.MaxMaxNorm() < tol * mat_coeff.MaxMaxNorm()); + } +} + +} // namespace + +void MaterialPropertyCoefficient::AddCoefficient(const mfem::Array &attr_mat_, + const mfem::DenseTensor &mat_coeff_, + double a) +{ + if (empty()) + { + attr_mat = attr_mat_; + mat_coeff = mat_coeff_; + for (int k = 0; k < mat_coeff.SizeK(); k++) + { + mat_coeff(k) *= a; + } + } + else if (attr_mat_ == attr_mat) + { + MFEM_VERIFY(mat_coeff_.SizeK() == mat_coeff.SizeK(), + "Invalid dimensions for MaterialPropertyCoefficient::AddCoefficient!"); + for (int k = 0; k < mat_coeff.SizeK(); k++) + { + UpdateProperty(mat_coeff, k, mat_coeff_(k), a); + } + } + else + { + for (int k = 0; k < mat_coeff_.SizeK(); k++) + { + // Get list of all attributes which use this material property. + mfem::Array attr_list; + attr_list.Reserve(attr_mat_.Size()); + for (int i = 0; i < attr_mat_.Size(); i++) + { + if (attr_mat_[i] == k) + { + attr_list.Append(i + 1); + } + } + + // Add or update the material property. + AddMaterialProperty(attr_list, mat_coeff_(k), a); + } + } + height = mat_coeff.SizeI(); + width = mat_coeff.SizeJ(); +} + +template +void MaterialPropertyCoefficient::AddMaterialProperty(const mfem::Array &attr_list, + const T &coeff, double a) +{ + // Preprocess the attribute list. If any of the given attributes already have material + // properties assigned, then they all need to point to the same material and it is + // updated in place. Otherwise a new material is added for these attributes. + int mat_idx = -1, attr_max = attr_mat.Size(); + for (auto attr : attr_list) + { + if (mat_idx < 0) { - conductivity_mats.Append(i + 1); + mat_idx = (attr > attr_mat.Size()) ? -1 : attr_mat[attr - 1]; } - if (mat_invLondon.at(i).MaxMaxNorm() > 0.0) + else { - london_mats.Append(i + 1); + MFEM_VERIFY(attr <= attr_mat.Size() && mat_idx == attr_mat[attr - 1], + "All attributes for AddMaterialProperty must correspond to the same " + "existing material if it exists!"); } + attr_max = std::max(attr, attr_max); } - mesh::AttrToMarker(attr_max, losstan_mats, losstan_marker); - mesh::AttrToMarker(attr_max, conductivity_mats, conductivity_marker); - mesh::AttrToMarker(attr_max, london_mats, london_marker); + + if (mat_idx < 0) + { + // Check if we can reuse an existing material. + for (int k = 0; k < mat_coeff.SizeK(); k++) + { + if (Equals(mat_coeff(k), coeff, a)) + { + mat_idx = k; + break; + } + } + if (mat_idx < 0) + { + // Append a new material and assign the attributes to it. + const mfem::DenseTensor mat_coeff_backup(mat_coeff); + mat_coeff.SetSize(mat_coeff_backup.SizeI(), mat_coeff_backup.SizeJ(), + mat_coeff_backup.SizeK() + 1); + for (int k = 0; k < mat_coeff_backup.SizeK(); k++) + { + mat_coeff(k) = mat_coeff_backup(k); + } + mat_idx = mat_coeff.SizeK() - 1; + } + mat_coeff(mat_idx) = 0.0; // Zero out so we can add + + // Copy the previous attribute materials, initialize no material to all new ones, then + // populate. + attr_mat.SetSize(attr_max, -1); + for (auto attr : attr_list) + { + attr_mat[attr - 1] = mat_idx; + } + } + UpdateProperty(mat_coeff, mat_idx, coeff, a); + height = mat_coeff.SizeI(); + width = mat_coeff.SizeJ(); +} + +void MaterialPropertyCoefficient::RestrictCoefficient(const mfem::Array &attr_list) +{ + // Create a new material property coefficient with materials corresponding to only the + // unique ones in the given attribute list. + const mfem::Array attr_mat_orig(attr_mat); + const mfem::DenseTensor mat_coeff_orig(mat_coeff); + attr_mat = -1; + mat_coeff.SetSize(mat_coeff_orig.SizeI(), mat_coeff_orig.SizeJ(), 0); + for (auto attr : attr_list) + { + if (attr_mat[attr - 1] >= 0) + { + // Attribute has already been processed. + continue; + } + + // Find all attributes in restricted list of attributes which map to this material index + // and process them together. + const int orig_mat_idx = attr_mat_orig[attr - 1]; + const int new_mat_idx = mat_coeff.SizeK(); + for (auto attr2 : attr_list) + { + if (attr_mat_orig[attr2 - 1] == orig_mat_idx) + { + attr_mat[attr2 - 1] = new_mat_idx; + } + } + + // Append the new material property. + const mfem::DenseTensor mat_coeff_backup(mat_coeff); + mat_coeff.SetSize(mat_coeff_backup.SizeI(), mat_coeff_backup.SizeJ(), + mat_coeff_backup.SizeK() + 1); + for (int k = 0; k < mat_coeff_backup.SizeK(); k++) + { + mat_coeff(k) = mat_coeff_backup(k); + } + mat_coeff(new_mat_idx) = mat_coeff_orig(orig_mat_idx); + } + height = mat_coeff.SizeI(); + width = mat_coeff.SizeJ(); } +void MaterialPropertyCoefficient::NormalProjectedCoefficient(const mfem::Vector &normal) +{ + mfem::DenseTensor mat_coeff_backup(mat_coeff); + mat_coeff.SetSize(1, 1, mat_coeff_backup.SizeK()); + for (int k = 0; k < mat_coeff.SizeK(); k++) + { + mat_coeff(k) = mat_coeff_backup(k).InnerProduct(normal, normal); + } + height = mat_coeff.SizeI(); + width = mat_coeff.SizeJ(); +} + +template void MaterialPropertyCoefficient::AddMaterialProperty(const mfem::Array &, + const mfem::DenseMatrix &, + double); +template void MaterialPropertyCoefficient::AddMaterialProperty(const mfem::Array &, + const double &, double); + } // namespace palace diff --git a/palace/models/materialoperator.hpp b/palace/models/materialoperator.hpp index 459dd8729..ce58b97a0 100644 --- a/palace/models/materialoperator.hpp +++ b/palace/models/materialoperator.hpp @@ -4,7 +4,7 @@ #ifndef PALACE_MODELS_MATERIAL_OPERATOR_HPP #define PALACE_MODELS_MATERIAL_OPERATOR_HPP -#include +#include #include #include @@ -19,44 +19,201 @@ class IoData; class MaterialOperator { private: - // Material properties for domain attributes: relative permeability, relative - // permittivity, and others (like electrical conductivity and London penetration depth - // for superconductors. The i-1-th entry of each Vector is the property for mesh domain - // attribute i. Marker arrays contain a 1 for each domain attribute labeled, and 0 else. - std::vector mat_muinv, mat_epsilon, mat_epsilon_imag, mat_epsilon_abs, - mat_invz0, mat_c0, mat_sigma, mat_invLondon; - std::vector mat_c0_min, mat_c0_max; - mfem::Array losstan_marker, conductivity_marker, london_marker; - void SetUpMaterialProperties(const IoData &iodata, mfem::ParMesh &mesh); + // Reference to underlying mesh object (not owned). + const mfem::ParMesh &mesh; + + // Mapping from the local attribute to material index. + mfem::Array attr_mat; + + // Material properties: relative permeability, relative permittivity, and others (like + // electrical conductivity and London penetration depth for superconductors. + mfem::DenseTensor mat_muinv, mat_epsilon, mat_epsilon_imag, mat_epsilon_abs, mat_invz0, + mat_c0, mat_sigma, mat_invLondon; + mfem::Array mat_c0_min, mat_c0_max; + + // Domain attributes with nonzero loss tangent, electrical conductivity, London + // penetration depth. + mfem::Array losstan_attr, conductivity_attr, london_attr; // Shared face mapping for boundary coefficients. - std::map local_to_shared; + std::unordered_map face_loc_to_shared; + + // Attribute mapping for (global, 1-based) domain and boundary attributes to those on this + // process (still 1-based). For boundaries, the inner map is a mapping from neighboring + // domain attribute to the resulting local boundary attribute (to discern boundary + // elements with global boundary attribute which borders more than one domain). Interior + // boundaries use as neighbor the element which corresponds to the vacuum domain, or at + // least the one with the higher speed of light. + std::unordered_map loc_attr; + std::unordered_map> loc_bdr_attr; + + void SetUpMaterialProperties(const IoData &iodata, const mfem::ParMesh &mesh); + + const auto AttrToMat(int attr) const + { + MFEM_ASSERT(loc_attr.find(attr) != loc_attr.end(), + "Missing local domain attribute for attribute " << attr << "!"); + return attr_mat[loc_attr.at(attr) - 1]; + } + + const auto Wrap(const mfem::DenseTensor &data, int attr) const + { + const int k = AttrToMat(attr); + return mfem::DenseMatrix(const_cast(data.GetData(k)), data.SizeI(), + data.SizeJ()); + } public: MaterialOperator(const IoData &iodata, mfem::ParMesh &mesh); - int SpaceDimension() const { return mat_muinv.front().Height(); } + int SpaceDimension() const { return mat_muinv.SizeI(); } + + const auto GetInvPermeability(int attr) const { return Wrap(mat_muinv, attr); } + const auto GetPermittivityReal(int attr) const { return Wrap(mat_epsilon, attr); } + const auto GetPermittivityImag(int attr) const { return Wrap(mat_epsilon_imag, attr); } + const auto GetPermittivityAbs(int attr) const { return Wrap(mat_epsilon_abs, attr); } + const auto GetInvImpedance(int attr) const { return Wrap(mat_invz0, attr); } + const auto GetLightSpeed(int attr) const { return Wrap(mat_c0, attr); } + const auto GetConductivity(int attr) const { return Wrap(mat_sigma, attr); } + const auto GetInvLondonDepth(int attr) const { return Wrap(mat_invLondon, attr); } + + auto GetLightSpeedMin(int attr) const { return mat_c0_min[AttrToMat(attr)]; } + auto GetLightSpeedMax(int attr) const { return mat_c0_max[AttrToMat(attr)]; } + + const auto &GetInvPermeability() const { return mat_muinv; } + const auto &GetPermittivityReal() const { return mat_epsilon; } + const auto &GetPermittivityImag() const { return mat_epsilon_imag; } + const auto &GetPermittivityAbs() const { return mat_epsilon_abs; } + const auto &GetInvImpedance() const { return mat_invz0; } + const auto &GetLightSpeed() const { return mat_c0; } + const auto &GetConductivity() const { return mat_sigma; } + const auto &GetInvLondonDepth() const { return mat_invLondon; } + + bool HasLossTangent() const { return (losstan_attr.Size() > 0); } + bool HasConductivity() const { return (conductivity_attr.Size() > 0); } + bool HasLondonDepth() const { return (london_attr.Size() > 0); } + + const auto &GetAttributeToMaterial() const { return attr_mat; } + mfem::Array GetBdrAttributeToMaterial() const; + + const auto &GetLocalToSharedFaceMap() const { return face_loc_to_shared; } + + const auto &GetAttributeGlobalToLocal() const { return loc_attr; } + + const auto &GetBdrAttributeGlobalToLocal() const { return loc_bdr_attr; } + + template + auto GetAttributeGlobalToLocal(const T &attr_list) const + { + // Skip any entries in the input global attribute list which are not on local to this + // process. + const auto &loc_attr = GetAttributeGlobalToLocal(); + mfem::Array loc_attr_list; + for (auto attr : attr_list) + { + if (loc_attr.find(attr) != loc_attr.end()) + { + loc_attr_list.Append(loc_attr.at(attr)); + } + } + return loc_attr_list; + } + + template + auto GetBdrAttributeGlobalToLocal(const T &attr_list) const + { + // Skip any entries in the input global boundary attribute list which are not on local + // to this process. + const auto &loc_bdr_attr = GetBdrAttributeGlobalToLocal(); + mfem::Array loc_attr_list; + for (auto attr : attr_list) + { + if (loc_bdr_attr.find(attr) != loc_bdr_attr.end()) + { + const auto &bdr_attr_map = loc_bdr_attr.at(attr); + for (auto it = bdr_attr_map.begin(); it != bdr_attr_map.end(); ++it) + { + loc_attr_list.Append(it->second); + } + } + } + return loc_attr_list; + } + + auto GetAttributeGlobalToLocal(const int attr) const + { + return GetAttributeGlobalToLocal(std::vector{attr}); + } + + auto GetBdrAttributeGlobalToLocal(const int attr) const + { + return GetBdrAttributeGlobalToLocal(std::vector{attr}); + } + + const auto &GetMesh() const { return mesh; } +}; + +// +// Material property represented as a piecewise constant coefficient over mesh elements. Can +// be scalar-valued or matrix-valued. +// +class MaterialPropertyCoefficient : public mfem::Coefficient, public mfem::MatrixCoefficient +{ +private: + // Map attribute to material index (coeff = mat_coeff[attr_mat[attr - 1]], for 1-based + // attributes). + mfem::Array attr_mat; + + // Material properry coefficients, ordered by material index. + mfem::DenseTensor mat_coeff; + +public: + MaterialPropertyCoefficient() : mfem::MatrixCoefficient(0, 0) {} + MaterialPropertyCoefficient(const mfem::Array &attr_mat_, + const mfem::DenseTensor &mat_coeff_, double a = 1.0); + + bool empty() const { return mat_coeff.TotalSize() == 0; } + + const auto &GetAttributeToMaterial() const { return attr_mat; } + const auto &GetMaterialProperties() const { return mat_coeff; } + + void AddCoefficient(const mfem::Array &attr_mat_, + const mfem::DenseTensor &mat_coeff_, double a = 1.0); + + template + void AddMaterialProperty(const mfem::Array &attr_list, const T &coeff, + double a = 1.0); + template + void AddMaterialProperty(int attr, const T &coeff, double a = 1.0) + { + mfem::Array attr_list(1); + attr_list[0] = attr; + AddMaterialProperty(attr_list, coeff, a); + } - const auto &GetLocalToSharedFaceMap() const { return local_to_shared; } + void RestrictCoefficient(const mfem::Array &attr_list); - const auto &GetInvPermeability(int attr) const { return mat_muinv[attr - 1]; } - const auto &GetPermittivityReal(int attr) const { return mat_epsilon[attr - 1]; } - const auto &GetPermittivityImag(int attr) const { return mat_epsilon_imag[attr - 1]; } - const auto &GetPermittivityAbs(int attr) const { return mat_epsilon_abs[attr - 1]; } - const auto &GetInvImpedance(int attr) const { return mat_invz0[attr - 1]; } - const auto &GetLightSpeed(int attr) const { return mat_c0[attr - 1]; } - const auto &GetLightSpeedMin(int attr) const { return mat_c0_min[attr - 1]; } - const auto &GetLightSpeedMax(int attr) const { return mat_c0_max[attr - 1]; } - const auto &GetConductivity(int attr) const { return mat_sigma[attr - 1]; } - const auto &GetInvLondonDepth(int attr) const { return mat_invLondon[attr - 1]; } + void NormalProjectedCoefficient(const mfem::Vector &normal); - bool HasLossTangent() const { return (losstan_marker.Max() > 0); } - bool HasConductivity() const { return (conductivity_marker.Max() > 0); } - bool HasLondonDepth() const { return (london_marker.Max() > 0); } + double Eval(mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip) override + { + MFEM_ASSERT(T.Attribute <= attr_mat.Size(), + "Out of bounds attribute for MaterialPropertyCoefficient (" + << T.Attribute << " > " << attr_mat.Size() << ")!"); + MFEM_ASSERT(mat_coeff.SizeI() == 1 && mat_coeff.SizeJ() == 1, + "Invalid access of matrix-valued MaterialPropertyCoefficient using scalar " + "coefficient interface!"); + return mat_coeff(0, 0, attr_mat[T.Attribute - 1]); + } - const auto &GetLossTangentMarker() const { return losstan_marker; } - const auto &GetConductivityMarker() const { return conductivity_marker; } - const auto &GetLondonDepthMarker() const { return london_marker; } + void Eval(mfem::DenseMatrix &K, mfem::ElementTransformation &T, + const mfem::IntegrationPoint &ip) override + { + MFEM_ASSERT(T.Attribute <= attr_mat.Size(), + "Out of bounds attribute for MaterialPropertyCoefficient (" + << T.Attribute << " > " << attr_mat.Size() << ")!"); + K = mat_coeff(attr_mat[T.Attribute - 1]); + } }; } // namespace palace diff --git a/palace/utils/configfile.hpp b/palace/utils/configfile.hpp index 5f73488bd..37e4b2428 100644 --- a/palace/utils/configfile.hpp +++ b/palace/utils/configfile.hpp @@ -29,6 +29,10 @@ struct DataVector std::vector vecdata = {}; public: + [[nodiscard]] const auto &operator[](int i) const { return vecdata[i]; } + [[nodiscard]] auto &operator[](int i) { return vecdata[i]; } + [[nodiscard]] const auto &at(int i) const { return vecdata.at(i); } + [[nodiscard]] auto &at(int i) { return vecdata.at(i); } [[nodiscard]] auto size() const { return vecdata.size(); } [[nodiscard]] auto empty() const { return vecdata.empty(); } [[nodiscard]] auto begin() const { return vecdata.begin(); } @@ -49,7 +53,9 @@ struct DataMap std::map mapdata = {}; public: + [[nodiscard]] const auto &operator[](int i) const { return mapdata[i]; } [[nodiscard]] auto &operator[](int i) { return mapdata[i]; } + [[nodiscard]] const auto &at(int i) const { return mapdata.at(i); } [[nodiscard]] auto &at(int i) { return mapdata.at(i); } [[nodiscard]] auto size() const { return mapdata.size(); } [[nodiscard]] auto empty() const { return mapdata.empty(); } From ff2df792047b78a215091d7abb1353b80591598b Mon Sep 17 00:00:00 2001 From: Sebastian Grimberg Date: Thu, 14 Dec 2023 18:31:30 -0800 Subject: [PATCH 02/32] WIP: Upgrade models for new coefficient interface --- palace/drivers/drivensolver.cpp | 10 +- palace/drivers/eigensolver.cpp | 4 +- palace/drivers/transientsolver.cpp | 4 +- palace/fem/lumpedelement.cpp | 55 ++-- palace/fem/lumpedelement.hpp | 24 +- palace/models/domainpostoperator.cpp | 46 ++- palace/models/domainpostoperator.hpp | 15 +- palace/models/farfieldboundaryoperator.cpp | 73 +++-- palace/models/farfieldboundaryoperator.hpp | 26 +- palace/models/lumpedportoperator.cpp | 299 ++++++++++-------- palace/models/lumpedportoperator.hpp | 57 ++-- palace/models/surfaceconductivityoperator.cpp | 135 ++++---- palace/models/surfaceconductivityoperator.hpp | 27 +- palace/models/surfacecurrentoperator.cpp | 71 ++--- palace/models/surfacecurrentoperator.hpp | 13 +- palace/models/surfaceimpedanceoperator.cpp | 188 ++++++----- palace/models/surfaceimpedanceoperator.hpp | 38 ++- palace/models/surfacepostoperator.cpp | 80 +++-- palace/models/surfacepostoperator.hpp | 20 +- 19 files changed, 637 insertions(+), 548 deletions(-) diff --git a/palace/drivers/drivensolver.cpp b/palace/drivers/drivensolver.cpp index 11cd6b5e3..8744885a6 100644 --- a/palace/drivers/drivensolver.cpp +++ b/palace/drivers/drivensolver.cpp @@ -54,7 +54,7 @@ DrivenSolver::Solve(const std::vector> &mesh) con bool first = true; for (const auto &[idx, data] : spaceop.GetLumpedPortOp()) { - if (data.IsExcited()) + if (data.excitation) { if (first) { @@ -69,7 +69,7 @@ DrivenSolver::Solve(const std::vector> &mesh) con first = true; for (const auto &[idx, data] : spaceop.GetWavePortOp()) { - if (data.IsExcited()) + if (data.excitation) { if (first) { @@ -508,7 +508,7 @@ void DrivenSolver::PostprocessPorts(const PostOperator &postop, const double Iinc = (std::abs(Vinc) > 0.0) ? data.GetExcitationPower() / Vinc : 0.0; const std::complex Vi = postop.GetPortVoltage(lumped_port_op, idx); const std::complex Ii = postop.GetPortCurrent(lumped_port_op, idx); - port_data.push_back({idx, data.IsExcited(), + port_data.push_back({idx, data.excitation, iodata.DimensionalizeValue(IoData::ValueType::VOLTAGE, Vinc), iodata.DimensionalizeValue(IoData::ValueType::CURRENT, Iinc), iodata.DimensionalizeValue(IoData::ValueType::VOLTAGE, Vi), @@ -642,7 +642,7 @@ void DrivenSolver::PostprocessSParameters(const PostOperator &postop, int source_idx = -1; for (const auto &[idx, data] : lumped_port_op) { - if (data.IsExcited()) + if (data.excitation) { if (src_lumped_port || src_wave_port) { @@ -654,7 +654,7 @@ void DrivenSolver::PostprocessSParameters(const PostOperator &postop, } for (const auto &[idx, data] : wave_port_op) { - if (data.IsExcited()) + if (data.excitation) { if (src_lumped_port || src_wave_port) { diff --git a/palace/drivers/eigensolver.cpp b/palace/drivers/eigensolver.cpp index 4535c2a00..3736558e7 100644 --- a/palace/drivers/eigensolver.cpp +++ b/palace/drivers/eigensolver.cpp @@ -542,7 +542,7 @@ void EigenSolver::PostprocessEPR(const PostOperator &postop, epr_L_data.reserve(lumped_port_op.Size()); for (const auto &[idx, data] : lumped_port_op) { - if (std::abs(data.GetL()) > 0.0) + if (std::abs(data.L) > 0.0) { const double pj = postop.GetInductorParticipation(lumped_port_op, idx, Em); epr_L_data.push_back({idx, pj}); @@ -582,7 +582,7 @@ void EigenSolver::PostprocessEPR(const PostOperator &postop, epr_IO_data.reserve(lumped_port_op.Size()); for (const auto &[idx, data] : lumped_port_op) { - if (std::abs(data.GetR()) > 0.0) + if (std::abs(data.R) > 0.0) { const double Kl = postop.GetExternalKappa(lumped_port_op, idx, Em); const double Ql = (Kl == 0.0) ? mfem::infinity() : omega.real() / std::abs(Kl); diff --git a/palace/drivers/transientsolver.cpp b/palace/drivers/transientsolver.cpp index 348bd4ead..ee293fe7f 100644 --- a/palace/drivers/transientsolver.cpp +++ b/palace/drivers/transientsolver.cpp @@ -49,7 +49,7 @@ TransientSolver::Solve(const std::vector> &mesh) bool first = true; for (const auto &[idx, data] : spaceop.GetLumpedPortOp()) { - if (data.IsExcited()) + if (data.excitation) { if (first) { @@ -367,7 +367,7 @@ void TransientSolver::PostprocessPorts(const PostOperator &postop, (std::abs(Vinc) > 0.0) ? data.GetExcitationPower() * J_coef * J_coef / Vinc : 0.0; const double Vi = postop.GetPortVoltage(lumped_port_op, idx).real(); const double Ii = postop.GetPortCurrent(lumped_port_op, idx).real(); - port_data.push_back({idx, data.IsExcited(), + port_data.push_back({idx, data.excitation, iodata.DimensionalizeValue(IoData::ValueType::VOLTAGE, Vinc), iodata.DimensionalizeValue(IoData::ValueType::CURRENT, Iinc), iodata.DimensionalizeValue(IoData::ValueType::VOLTAGE, Vi), diff --git a/palace/fem/lumpedelement.cpp b/palace/fem/lumpedelement.cpp index 40086394d..6ba110d93 100644 --- a/palace/fem/lumpedelement.cpp +++ b/palace/fem/lumpedelement.cpp @@ -3,13 +3,17 @@ #include "lumpedelement.hpp" +#include "fem/coefficient.hpp" #include "fem/integrator.hpp" #include "utils/communication.hpp" namespace palace { -double LumpedElementData::GetArea(mfem::ParFiniteElementSpace &fespace) +namespace +{ + +double GetArea(mfem::ParFiniteElementSpace &fespace, mfem::Array &attr_marker) { mfem::ConstantCoefficient one_func(1.0); mfem::LinearForm s(&fespace); @@ -24,16 +28,22 @@ double LumpedElementData::GetArea(mfem::ParFiniteElementSpace &fespace) return dot; } +} // namespace + UniformElementData::UniformElementData(const std::array &input_dir, - const mfem::Array &marker, + const mfem::Array &attr_list, mfem::ParFiniteElementSpace &fespace) - : LumpedElementData(fespace.GetParMesh()->SpaceDimension(), marker), - bounding_box(mesh::GetBoundingBox(*fespace.GetParMesh(), marker, true)), direction(3) + : LumpedElementData(attr_list) { + const mfem::ParMesh &mesh = *fespace.GetParMesh(); + int bdr_attr_max = mesh.bdr_attributes.Size() ? mesh.bdr_attributes.Max() : 0; + mfem::Array attr_marker = mesh::AttrToMarker(bdr_attr_max, attr_list); + bounding_box = mesh::GetBoundingBox(mesh, attr_marker, true); + // Check that the bounding box discovered matches the area. This validates that the // boundary elements form a right angled quadrilateral port. constexpr double rel_tol = 1.0e-6; - double A = GetArea(fespace); + double A = GetArea(fespace, attr_marker); MFEM_VERIFY((!bounding_box.planar || (std::abs(A - bounding_box.Area()) / A < rel_tol)), "Discovered bounding box area " << bounding_box.Area() << " and integrated area " << A @@ -74,16 +84,16 @@ UniformElementData::UniformElementData(const std::array &input_dir, "Specified direction does not align sufficiently with bounding box axes: " << deviation_deg[0] << ' ' << deviation_deg[1] << ' ' << deviation_deg[2] << " tolerance " << angle_error_deg << '!'); + direction.SetSize(input_dir.size()); std::copy(input_dir.begin(), input_dir.end(), direction.begin()); direction /= direction.Norml2(); // Compute the length from the most aligned normal direction. l = lengths[std::distance(deviation_deg.begin(), std::min_element(deviation_deg.begin(), deviation_deg.end()))]; - MFEM_ASSERT( - (l - mesh::GetProjectedLength(*fespace.GetParMesh(), marker, true, input_dir)) / l < - rel_tol, - "Bounding box discovered length should match projected length!"); + MFEM_ASSERT((l - mesh::GetProjectedLength(mesh, attr_marker, true, input_dir)) / l < + rel_tol, + "Bounding box discovered length should match projected length!"); w = A / l; } @@ -92,21 +102,25 @@ UniformElementData::GetModeCoefficient(double coef) const { mfem::Vector source = direction; source *= coef; - return std::make_unique(source); + return std::make_unique( + std::make_unique(source), attr_list); } CoaxialElementData::CoaxialElementData(const std::array &direction, - const mfem::Array &marker, + const mfem::Array &attr_list, mfem::ParFiniteElementSpace &fespace) - : LumpedElementData(fespace.GetParMesh()->SpaceDimension(), marker), - bounding_ball(mesh::GetBoundingBall(*fespace.GetParMesh(), marker, true)), - sign(direction[0] > 0) + : LumpedElementData(attr_list) { + const mfem::ParMesh &mesh = *fespace.GetParMesh(); + int bdr_attr_max = mesh.bdr_attributes.Size() ? mesh.bdr_attributes.Max() : 0; + mfem::Array attr_marker = mesh::AttrToMarker(bdr_attr_max, attr_list); + bounding_ball = mesh::GetBoundingBall(mesh, attr_marker, true); MFEM_VERIFY(bounding_ball.planar, "Boundary elements must be coplanar to define a coaxial lumped element!"); + sign = (direction[0] > 0); // Get inner radius of annulus assuming full 2π circumference. - double A = GetArea(fespace); + double A = GetArea(fespace, attr_marker); MFEM_VERIFY(bounding_ball.radius > 0.0 && std::pow(bounding_ball.radius, 2) - A / M_PI > 0.0, "Coaxial element boundary is not defined correctly: Radius " @@ -117,17 +131,18 @@ CoaxialElementData::CoaxialElementData(const std::array &direction, std::unique_ptr CoaxialElementData::GetModeCoefficient(double coef) const { - double scoef = (sign ? 1.0 : -1.0) * coef; - mfem::Vector x0(3); + coef = (sign ? 1.0 : -1.0) * coef; + mfem::Vector x0(bounding_ball.center.size()); std::copy(bounding_ball.center.begin(), bounding_ball.center.end(), x0.begin()); - auto Source = [scoef, x0](const mfem::Vector &x, mfem::Vector &f) -> void + auto Source = [coef, x0](const mfem::Vector &x, mfem::Vector &f) -> void { f = x; f -= x0; double oor = 1.0 / f.Norml2(); - f *= scoef * oor * oor; + f *= coef * oor * oor; }; - return std::make_unique(dim, Source); + return std::make_unique( + std::make_unique(x0.Size(), Source), attr_list); } } // namespace palace diff --git a/palace/fem/lumpedelement.hpp b/palace/fem/lumpedelement.hpp index 29e8b635c..f154e058c 100644 --- a/palace/fem/lumpedelement.hpp +++ b/palace/fem/lumpedelement.hpp @@ -18,20 +18,14 @@ namespace palace class LumpedElementData { protected: - // Spatial dimension. - const int dim; - - // Marker for all boundary attributes making up this lumped element boundary. - mfem::Array attr_marker; - - double GetArea(mfem::ParFiniteElementSpace &fespace); + // List of all boundary attributes making up this lumped element boundary. + mfem::Array attr_list; public: - LumpedElementData(int d, const mfem::Array &marker) : dim(d), attr_marker(marker) {} + LumpedElementData(const mfem::Array &attr_list) : attr_list(attr_list) {} virtual ~LumpedElementData() = default; - mfem::Array &GetMarker() { return attr_marker; } - const mfem::Array &GetMarker() const { return attr_marker; } + const auto &GetAttrList() const { return attr_list; } virtual double GetGeometryLength() const = 0; virtual double GetGeometryWidth() const = 0; @@ -42,7 +36,7 @@ class LumpedElementData class UniformElementData : public LumpedElementData { -protected: +private: // Bounding box defining the rectangular lumped port. mesh::BoundingBox bounding_box; @@ -53,7 +47,8 @@ class UniformElementData : public LumpedElementData double l, w; public: - UniformElementData(const std::array &input_dir, const mfem::Array &marker, + UniformElementData(const std::array &input_dir, + const mfem::Array &attr_list, mfem::ParFiniteElementSpace &fespace); double GetGeometryLength() const override { return l; } @@ -65,7 +60,7 @@ class UniformElementData : public LumpedElementData class CoaxialElementData : public LumpedElementData { -protected: +private: // Bounding ball defined by boundary element. mesh::BoundingBall bounding_ball; @@ -76,7 +71,8 @@ class CoaxialElementData : public LumpedElementData double ra; public: - CoaxialElementData(const std::array &direction, const mfem::Array &marker, + CoaxialElementData(const std::array &direction, + const mfem::Array &attr_list, mfem::ParFiniteElementSpace &fespace); double GetGeometryLength() const override { return std::log(bounding_ball.radius / ra); } diff --git a/palace/models/domainpostoperator.cpp b/palace/models/domainpostoperator.cpp index 151645e14..84d319feb 100644 --- a/palace/models/domainpostoperator.cpp +++ b/palace/models/domainpostoperator.cpp @@ -3,8 +3,9 @@ #include "domainpostoperator.hpp" +#include #include "fem/bilinearform.hpp" -#include "fem/coefficient.hpp" +#include "fem/fespace.hpp" #include "fem/integrator.hpp" #include "models/materialoperator.hpp" #include "utils/communication.hpp" @@ -14,21 +15,21 @@ namespace palace { DomainPostOperator::DomainPostOperator(const IoData &iodata, const MaterialOperator &mat_op, - const mfem::ParFiniteElementSpace *nd_fespace, - const mfem::ParFiniteElementSpace *rt_fespace) + const FiniteElementSpace *nd_fespace, + const FiniteElementSpace *rt_fespace) { // Mass operators are always partially assembled. - constexpr auto MatTypeEps = MaterialPropertyType::PERMITTIVITY_REAL; - constexpr auto MatTypeMuInv = MaterialPropertyType::INV_PERMEABILITY; if (nd_fespace) { // Construct ND mass matrix to compute the electric field energy integral as: // E_elec = 1/2 Re{∫_Ω Dᴴ E dV} as (M_eps * e)ᴴ e. // Only the real part of the permeability contributes to the energy (imaginary part // cancels out in the inner product due to symmetry). - MaterialPropertyCoefficient epsilon_func(mat_op); + MaterialPropertyCoefficient epsilon_func(mat_op.GetAttributeToMaterial(), + mat_op.GetPermittivityReal()); BilinearForm m_nd(*nd_fespace); - m_nd.AddDomainIntegrator(epsilon_func); + m_nd.AddDomainIntegrator( + (mfem::MatrixCoefficient &)epsilon_func); M_ND = m_nd.PartialAssemble(); D.SetSize(M_ND->Height()); D.UseDevice(true); @@ -38,9 +39,10 @@ DomainPostOperator::DomainPostOperator(const IoData &iodata, const MaterialOpera { // Construct RT mass matrix to compute the magnetic field energy integral as: // E_mag = 1/2 Re{∫_Ω Bᴴ H dV} as (M_muinv * b)ᴴ b. - MaterialPropertyCoefficient muinv_func(mat_op); + MaterialPropertyCoefficient muinv_func(mat_op.GetAttributeToMaterial(), + mat_op.GetInvPermeability()); BilinearForm m_rt(*rt_fespace); - m_rt.AddDomainIntegrator(muinv_func); + m_rt.AddDomainIntegrator((mfem::MatrixCoefficient &)muinv_func); M_RT = m_rt.PartialAssemble(); H.SetSize(M_RT->Height()); H.UseDevice(true); @@ -48,33 +50,27 @@ DomainPostOperator::DomainPostOperator(const IoData &iodata, const MaterialOpera // Use the provided domain postprocessing indices for postprocessing the electric and // magnetic field energy in specific regions of the domain. - const auto &mesh = nd_fespace ? *nd_fespace->GetParMesh() : *rt_fespace->GetParMesh(); - int attr_max = mesh.attributes.Max(); for (const auto &[idx, data] : iodata.domains.postpro.energy) { - mfem::Array attr_marker(attr_max); - attr_marker = 0; - for (auto attr : data.attributes) - { - attr_marker[attr - 1] = 1; - } std::unique_ptr M_ND_i, M_RT_i; if (nd_fespace) { - SumMatrixCoefficient epsilon_func_i(nd_fespace->GetParMesh()->SpaceDimension()); - epsilon_func_i.AddCoefficient( - std::make_unique>(mat_op), attr_marker); + MaterialPropertyCoefficient epsilon_func(mat_op.GetAttributeToMaterial(), + mat_op.GetPermittivityReal()); + epsilon_func.RestrictCoefficient(mat_op.GetAttributeGlobalToLocal(data.attributes)); BilinearForm m_nd_i(*nd_fespace); - m_nd_i.AddDomainIntegrator(epsilon_func_i); + m_nd_i.AddDomainIntegrator( + (mfem::MatrixCoefficient &)epsilon_func); M_ND_i = m_nd_i.PartialAssemble(); } if (rt_fespace) { - SumMatrixCoefficient muinv_func_i(rt_fespace->GetParMesh()->SpaceDimension()); - muinv_func_i.AddCoefficient( - std::make_unique>(mat_op), attr_marker); + MaterialPropertyCoefficient muinv_func(mat_op.GetAttributeToMaterial(), + mat_op.GetInvPermeability()); + muinv_func.RestrictCoefficient(mat_op.GetAttributeGlobalToLocal(data.attributes)); BilinearForm m_rt_i(*rt_fespace); - m_rt_i.AddDomainIntegrator(muinv_func_i); + m_rt_i.AddDomainIntegrator( + (mfem::MatrixCoefficient &)muinv_func); M_RT_i = m_rt_i.PartialAssemble(); } M_i.emplace(idx, std::make_pair(std::move(M_ND_i), std::move(M_RT_i))); diff --git a/palace/models/domainpostoperator.hpp b/palace/models/domainpostoperator.hpp index 05b0134bf..dc80c7ed9 100644 --- a/palace/models/domainpostoperator.hpp +++ b/palace/models/domainpostoperator.hpp @@ -7,12 +7,21 @@ #include #include #include -#include #include "linalg/operator.hpp" +#include "linalg/vector.hpp" + +namespace mfem +{ + +class ParComplexGridFunction; +class ParGridFunction; + +} // namespace mfem namespace palace { +class FiniteElementSpace; class IoData; class MaterialOperator; @@ -31,8 +40,8 @@ class DomainPostOperator public: DomainPostOperator(const IoData &iodata, const MaterialOperator &mat_op, - const mfem::ParFiniteElementSpace *nd_fespace, - const mfem::ParFiniteElementSpace *rt_fespace); + const FiniteElementSpace *nd_fespace, + const FiniteElementSpace *rt_fespace); // Access data structures for postprocessing domains. const auto &GetDomains() const { return M_i; } diff --git a/palace/models/farfieldboundaryoperator.cpp b/palace/models/farfieldboundaryoperator.cpp index 3a8b5e624..f0d302a33 100644 --- a/palace/models/farfieldboundaryoperator.cpp +++ b/palace/models/farfieldboundaryoperator.cpp @@ -3,7 +3,6 @@ #include "farfieldboundaryoperator.hpp" -#include "fem/coefficient.hpp" #include "models/materialoperator.hpp" #include "utils/communication.hpp" #include "utils/geodata.hpp" @@ -14,28 +13,27 @@ namespace palace { FarfieldBoundaryOperator::FarfieldBoundaryOperator(const IoData &iodata, - const MaterialOperator &mat, + const MaterialOperator &mat_op, const mfem::ParMesh &mesh) - : mat_op(mat) + : mat_op(mat_op), farfield_attr(SetUpBoundaryProperties(iodata, mesh)) { - // Set up impedance boundary conditions. - SetUpBoundaryProperties(iodata, mesh); - // Print out BC info for all farfield attributes. - if (farfield_marker.Size() && farfield_marker.Max() > 0) + if (farfield_attr.Size()) { Mpi::Print("\nConfiguring Robin absorbing BC (order {:d}) at attributes:\n", order); - utils::PrettyPrintMarker(farfield_marker); + std::sort(farfield_attr.begin(), farfield_attr.end()); + utils::PrettyPrint(farfield_attr); } } -void FarfieldBoundaryOperator::SetUpBoundaryProperties(const IoData &iodata, - const mfem::ParMesh &mesh) +mfem::Array +FarfieldBoundaryOperator::SetUpBoundaryProperties(const IoData &iodata, + const mfem::ParMesh &mesh) { // Check that impedance boundary attributes have been specified correctly. - int bdr_attr_max = mesh.bdr_attributes.Size() ? mesh.bdr_attributes.Max() : 0; if (!iodata.boundaries.farfield.empty()) { + int bdr_attr_max = mesh.bdr_attributes.Size() ? mesh.bdr_attributes.Max() : 0; mfem::Array bdr_attr_marker(bdr_attr_max); bdr_attr_marker = 0; for (auto attr : mesh.bdr_attributes) @@ -56,30 +54,32 @@ void FarfieldBoundaryOperator::SetUpBoundaryProperties(const IoData &iodata, order = iodata.boundaries.farfield.order; // Mark selected boundary attributes from the mesh as farfield. - MFEM_VERIFY(iodata.boundaries.farfield.attributes.empty() || order < 2 || + mfem::Array farfield_bcs; + farfield_bcs.Append(iodata.boundaries.farfield.attributes.data(), + iodata.boundaries.farfield.attributes.size()); + MFEM_VERIFY(farfield_bcs.Size() == 0 || order < 2 || iodata.problem.type == config::ProblemData::Type::DRIVEN, "Second-order farfield boundaries are only available for frequency " "domain driven simulations!"); - mesh::AttrToMarker(bdr_attr_max, iodata.boundaries.farfield.attributes, farfield_marker); + return farfield_bcs; } void FarfieldBoundaryOperator::AddDampingBdrCoefficients(double coef, - SumMatrixCoefficient &fb) + MaterialPropertyCoefficient &fb) { // First-order absorbing boundary condition. - if (farfield_marker.Size() && farfield_marker.Max() > 0) + if (farfield_attr.Size()) { - constexpr auto MatType = MaterialPropertyType::INV_Z0; - constexpr auto ElemType = MeshElementType::BDR_ELEMENT; - fb.AddCoefficient( - std::make_unique>(mat_op, coef), - farfield_marker); + MaterialPropertyCoefficient invz0_func(mat_op.GetBdrAttributeToMaterial(), + mat_op.GetInvImpedance()); + invz0_func.RestrictCoefficient(mat_op.GetBdrAttributeGlobalToLocal(farfield_attr)); + fb.AddCoefficient(invz0_func.GetAttributeToMaterial(), + invz0_func.GetMaterialProperties(), coef); } } -void FarfieldBoundaryOperator::AddExtraSystemBdrCoefficients(double omega, - SumCoefficient &dfbr, - SumCoefficient &dfbi) +void FarfieldBoundaryOperator::AddExtraSystemBdrCoefficients( + double omega, MaterialPropertyCoefficient &dfbr, MaterialPropertyCoefficient &dfbi) { // Contribution for second-order absorbing BC. See Jin Section 9.3 for reference. The β // coefficient for the second-order ABC is 1/(2ik+2/r). Taking the radius of curvature as @@ -87,15 +87,26 @@ void FarfieldBoundaryOperator::AddExtraSystemBdrCoefficients(double omega, // purely imaginary. Multiplying through by μ⁻¹ we get the material coefficient to ω as // 1 / (μ √με). Also, this implementation ignores the divergence term ∇⋅Eₜ, as COMSOL // does as well. - if (farfield_marker.Size() && farfield_marker.Max() > 0 && order > 1) + if (farfield_attr.Size() && order > 1) { - constexpr auto MatType = MaterialPropertyType::INV_PERMEABILITY_C0; - constexpr auto ElemType = MeshElementType::BDR_ELEMENT; - dfbi.AddCoefficient( - std::make_unique( - std::make_unique>(mat_op, - 0.5 / omega)), - farfield_marker); + mfem::DenseTensor muinvc0(mat_op.GetLightSpeed()); + for (int k = 0; k < muinvc0.SizeK(); k++) + { + Mult(mat_op.GetInvPermeability()(k), mat_op.GetLightSpeed()(k), muinvc0(k)); + } + MaterialPropertyCoefficient muinvc0_func(mat_op.GetBdrAttributeToMaterial(), muinvc0); + muinvc0_func.RestrictCoefficient(mat_op.GetBdrAttributeGlobalToLocal(farfield_attr)); + + // Instead getting the correct normal of farfield boundary elements, just pick the + // the first element normal. This is fine as long as the farfield material properties + // are not anisotropic. + mfem::Vector normal(mat_op.SpaceDimension()); + normal = 0.0; + normal(0) = 1.0; + muinvc0_func.NormalProjectedCoefficient(normal); + + dfbi.AddCoefficient(muinvc0_func.GetAttributeToMaterial(), + muinvc0_func.GetMaterialProperties(), 0.5 / omega); } } diff --git a/palace/models/farfieldboundaryoperator.hpp b/palace/models/farfieldboundaryoperator.hpp index d9e068339..18a34e24c 100644 --- a/palace/models/farfieldboundaryoperator.hpp +++ b/palace/models/farfieldboundaryoperator.hpp @@ -11,8 +11,7 @@ namespace palace class IoData; class MaterialOperator; -class SumCoefficient; -class SumMatrixCoefficient; +class MaterialPropertyCoefficient; // // A class handling farfield, or absorbing, boundaries. @@ -20,31 +19,32 @@ class SumMatrixCoefficient; class FarfieldBoundaryOperator { private: - // Reference to input data (not owned). + // Reference to material property data (not owned). const MaterialOperator &mat_op; + // List of all absorbing boundary condition attributes. + mfem::Array farfield_attr; + // First- or second-order absorbing boundary condition. int order; - // Marker for all absorbing boundary condition attributes. - mfem::Array farfield_marker; - void SetUpBoundaryProperties(const IoData &iodata, const mfem::ParMesh &mesh); + mfem::Array SetUpBoundaryProperties(const IoData &iodata, const mfem::ParMesh &mesh); public: - FarfieldBoundaryOperator(const IoData &iodata, const MaterialOperator &mat, + FarfieldBoundaryOperator(const IoData &iodata, const MaterialOperator &mat_op, const mfem::ParMesh &mesh); + // Returns array of farfield BC attributes. + const auto &GetAttrList() const { return farfield_attr; } + // Returns order of absorbing BC approximation. int GetOrder() const { return order; } - // Returns array marking farfield BC attributes. - const mfem::Array &GetMarker() const { return farfield_marker; } - // Add contributions to system matrices from first- or second-order absorbing boundary // condition. - void AddDampingBdrCoefficients(double coef, SumMatrixCoefficient &fb); - void AddExtraSystemBdrCoefficients(double omega, SumCoefficient &dfbr, - SumCoefficient &dfbi); + void AddDampingBdrCoefficients(double coef, MaterialPropertyCoefficient &fb); + void AddExtraSystemBdrCoefficients(double omega, MaterialPropertyCoefficient &dfbr, + MaterialPropertyCoefficient &dfbi); }; } // namespace palace diff --git a/palace/models/lumpedportoperator.cpp b/palace/models/lumpedportoperator.cpp index d7e98f6f4..ac1dab0f8 100644 --- a/palace/models/lumpedportoperator.cpp +++ b/palace/models/lumpedportoperator.cpp @@ -3,10 +3,8 @@ #include "lumpedportoperator.hpp" -#include #include "fem/coefficient.hpp" #include "fem/integrator.hpp" -#include "fem/lumpedelement.hpp" #include "models/materialoperator.hpp" #include "utils/communication.hpp" #include "utils/geodata.hpp" @@ -18,8 +16,9 @@ namespace palace using namespace std::complex_literals; LumpedPortData::LumpedPortData(const config::LumpedPortData &data, + const MaterialOperator &mat_op, mfem::ParFiniteElementSpace &h1_fespace) - : excitation(data.excitation), s(nullptr), v(nullptr) + : mat_op(mat_op) { // Check inputs. Only one of the circuit or per square properties should be specified // for the port boundary. @@ -31,6 +30,7 @@ LumpedPortData::LumpedPortData(const config::LumpedPortData &data, MFEM_VERIFY(!(has_circ && has_surf), "Lumped port boundary has both R/L/C and Rs/Ls/Cs defined, " "should only use one!"); + excitation = data.excitation; if (excitation) { if (has_circ) @@ -50,20 +50,17 @@ LumpedPortData::LumpedPortData(const config::LumpedPortData &data, // Construct the port elements allowing for a possible multielement lumped port. for (const auto &elem : data.elements) { - mfem::Array attr_marker; - mesh::AttrToMarker(h1_fespace.GetParMesh()->bdr_attributes.Size() - ? h1_fespace.GetParMesh()->bdr_attributes.Max() - : 0, - elem.attributes, attr_marker); + mfem::Array attr_list; + attr_list.Append(elem.attributes.data(), elem.attributes.size()); switch (elem.coordinate_system) { case config::internal::ElementData::CoordinateSystem::CYLINDRICAL: elems.push_back( - std::make_unique(elem.direction, attr_marker, h1_fespace)); + std::make_unique(elem.direction, attr_list, h1_fespace)); break; case config::internal::ElementData::CoordinateSystem::CARTESIAN: elems.push_back( - std::make_unique(elem.direction, attr_marker, h1_fespace)); + std::make_unique(elem.direction, attr_list, h1_fespace)); break; } } @@ -158,20 +155,33 @@ double LumpedPortData::GetExcitationVoltage() const void LumpedPortData::InitializeLinearForms(mfem::ParFiniteElementSpace &nd_fespace) const { + const auto &mesh = *nd_fespace.GetParMesh(); + mfem::Array attr_marker; + if (!s || !v) + { + mfem::Array attr_list; + for (const auto &elem : elems) + { + attr_list.Append(elem->GetAttrList()); + } + int bdr_attr_max = mesh.bdr_attributes.Size() ? mesh.bdr_attributes.Max() : 0; + mesh::AttrToMarker(bdr_attr_max, attr_list, attr_marker); + } + // The port S-parameter, or the projection of the field onto the port mode, is computed // as: (E x H_inc) ⋅ n = E ⋅ (E_inc / Z_s), integrated over the port surface. if (!s) { - SumVectorCoefficient fb(nd_fespace.GetParMesh()->SpaceDimension()); + SumVectorCoefficient fb(mesh.SpaceDimension()); for (const auto &elem : elems) { const double Rs = R * GetToSquare(*elem); const double Hinc = 1.0 / std::sqrt(Rs * elem->GetGeometryWidth() * elem->GetGeometryLength() * elems.size()); - fb.AddCoefficient(elem->GetModeCoefficient(Hinc), elem->GetMarker()); + fb.AddCoefficient(elem->GetModeCoefficient(Hinc)); } s = std::make_unique(&nd_fespace); - s->AddBoundaryIntegrator(new VectorFEBoundaryLFIntegrator(fb)); + s->AddBoundaryIntegrator(new VectorFEBoundaryLFIntegrator(fb), attr_marker); s->UseFastAssembly(false); s->Assemble(); } @@ -185,15 +195,14 @@ void LumpedPortData::InitializeLinearForms(mfem::ParFiniteElementSpace &nd_fespa // averaging function as a vector coefficient and the solution expansion coefficients. if (!v) { - SumVectorCoefficient fb(nd_fespace.GetParMesh()->SpaceDimension()); + SumVectorCoefficient fb(mesh.SpaceDimension()); for (const auto &elem : elems) { fb.AddCoefficient( - elem->GetModeCoefficient(1.0 / (elem->GetGeometryWidth() * elems.size())), - elem->GetMarker()); + elem->GetModeCoefficient(1.0 / (elem->GetGeometryWidth() * elems.size()))); } v = std::make_unique(&nd_fespace); - v->AddBoundaryIntegrator(new VectorFEBoundaryLFIntegrator(fb)); + v->AddBoundaryIntegrator(new VectorFEBoundaryLFIntegrator(fb), attr_marker); v->UseFastAssembly(false); v->Assemble(); } @@ -208,22 +217,26 @@ std::complex LumpedPortData::GetSParameter(mfem::ParComplexGridFunction return dot; } -double LumpedPortData::GetPower(mfem::ParGridFunction &E, mfem::ParGridFunction &B, - const MaterialOperator &mat_op) const +double LumpedPortData::GetPower(mfem::ParGridFunction &E, mfem::ParGridFunction &B) const { // Compute port power, (E x H) ⋅ n = E ⋅ (-n x H), integrated over the port surface // using the computed E and H = μ⁻¹ B fields. The linear form is reconstructed from // scratch each time due to changing H. The BdrCurrentVectorCoefficient computes -n x H, // where n is an outward normal. auto &nd_fespace = *E.ParFESpace(); - SumVectorCoefficient fb(nd_fespace.GetParMesh()->SpaceDimension()); + const auto &mesh = *nd_fespace.GetParMesh(); + SumVectorCoefficient fb(mesh.SpaceDimension()); + mfem::Array attr_list; for (const auto &elem : elems) { - fb.AddCoefficient(std::make_unique(B, mat_op), - elem->GetMarker()); + fb.AddCoefficient(std::make_unique( + std::make_unique(B, mat_op), elem->GetAttrList())); + attr_list.Append(elem->GetAttrList()); } + int bdr_attr_max = mesh.bdr_attributes.Size() ? mesh.bdr_attributes.Max() : 0; + mfem::Array attr_marker = mesh::AttrToMarker(bdr_attr_max, attr_list); mfem::LinearForm p(&nd_fespace); - p.AddBoundaryIntegrator(new VectorFEBoundaryLFIntegrator(fb)); + p.AddBoundaryIntegrator(new VectorFEBoundaryLFIntegrator(fb), attr_marker); p.UseFastAssembly(false); p.Assemble(); double dot = p * E; @@ -232,26 +245,32 @@ double LumpedPortData::GetPower(mfem::ParGridFunction &E, mfem::ParGridFunction } std::complex LumpedPortData::GetPower(mfem::ParComplexGridFunction &E, - mfem::ParComplexGridFunction &B, - const MaterialOperator &mat_op) const + mfem::ParComplexGridFunction &B) const { // Compute port power, (E x H⋆) ⋅ n = E ⋅ (-n x H⋆), integrated over the port surface // using the computed E and H = μ⁻¹ B fields. The linear form is reconstructed from // scratch each time due to changing H. The BdrCurrentVectorCoefficient computes -n x H, // where n is an outward normal. auto &nd_fespace = *E.ParFESpace(); - SumVectorCoefficient fbr(nd_fespace.GetParMesh()->SpaceDimension()); - SumVectorCoefficient fbi(nd_fespace.GetParMesh()->SpaceDimension()); + const auto &mesh = *nd_fespace.GetParMesh(); + SumVectorCoefficient fbr(mesh.SpaceDimension()); + SumVectorCoefficient fbi(mesh.SpaceDimension()); + mfem::Array attr_list; for (const auto &elem : elems) { - fbr.AddCoefficient(std::make_unique(B.real(), mat_op), - elem->GetMarker()); - fbi.AddCoefficient(std::make_unique(B.imag(), mat_op), - elem->GetMarker()); + fbr.AddCoefficient(std::make_unique( + std::make_unique(B.real(), mat_op), + elem->GetAttrList())); + fbi.AddCoefficient(std::make_unique( + std::make_unique(B.imag(), mat_op), + elem->GetAttrList())); + attr_list.Append(elem->GetAttrList()); } + int bdr_attr_max = mesh.bdr_attributes.Size() ? mesh.bdr_attributes.Max() : 0; + mfem::Array attr_marker = mesh::AttrToMarker(bdr_attr_max, attr_list); mfem::LinearForm pr(&nd_fespace), pi(&nd_fespace); - pr.AddBoundaryIntegrator(new VectorFEBoundaryLFIntegrator(fbr)); - pi.AddBoundaryIntegrator(new VectorFEBoundaryLFIntegrator(fbi)); + pr.AddBoundaryIntegrator(new VectorFEBoundaryLFIntegrator(fbr), attr_marker); + pi.AddBoundaryIntegrator(new VectorFEBoundaryLFIntegrator(fbi), attr_marker); pr.UseFastAssembly(false); pi.UseFastAssembly(false); pr.Assemble(); @@ -280,26 +299,27 @@ std::complex LumpedPortData::GetVoltage(mfem::ParComplexGridFunction &E) return dot; } -LumpedPortOperator::LumpedPortOperator(const IoData &iodata, +LumpedPortOperator::LumpedPortOperator(const IoData &iodata, const MaterialOperator &mat_op, mfem::ParFiniteElementSpace &h1_fespace) { // Set up lumped port boundary conditions. - SetUpBoundaryProperties(iodata, h1_fespace); + SetUpBoundaryProperties(iodata, mat_op, h1_fespace); PrintBoundaryInfo(iodata, *h1_fespace.GetParMesh()); } void LumpedPortOperator::SetUpBoundaryProperties(const IoData &iodata, + const MaterialOperator &mat_op, mfem::ParFiniteElementSpace &h1_fespace) { // Check that lumped port boundary attributes have been specified correctly. - int bdr_attr_max = h1_fespace.GetParMesh()->bdr_attributes.Size() - ? h1_fespace.GetParMesh()->bdr_attributes.Max() - : 0; if (!iodata.boundaries.lumpedport.empty()) { - mfem::Array bdr_attr_marker(bdr_attr_max); + const auto &mesh = *h1_fespace.GetParMesh(); + int bdr_attr_max = mesh.bdr_attributes.Size() ? mesh.bdr_attributes.Max() : 0; + mfem::Array bdr_attr_marker(bdr_attr_max), port_marker(bdr_attr_max); bdr_attr_marker = 0; - for (auto attr : h1_fespace.GetParMesh()->bdr_attributes) + port_marker = 0; + for (auto attr : mesh.bdr_attributes) { bdr_attr_marker[attr - 1] = 1; } @@ -314,6 +334,9 @@ void LumpedPortOperator::SetUpBoundaryProperties(const IoData &iodata, "boundaries in the mesh!"); MFEM_VERIFY(bdr_attr_marker[attr - 1], "Unknown port boundary attribute " << attr << "!"); + MFEM_VERIFY(!port_marker[attr - 1], + "Boundary attribute is assigned to more than one lumped port!"); + port_marker[attr - 1] = 1; } } } @@ -322,41 +345,7 @@ void LumpedPortOperator::SetUpBoundaryProperties(const IoData &iodata, // Set up lumped port data structures. for (const auto &[idx, data] : iodata.boundaries.lumpedport) { - ports.try_emplace(idx, data, h1_fespace); - } - - // Mark selected boundary attributes from the mesh for lumped ports. - port_marker.SetSize(bdr_attr_max); - port_Rs_marker.SetSize(bdr_attr_max); - port_Ls_marker.SetSize(bdr_attr_max); - port_Cs_marker.SetSize(bdr_attr_max); - port_marker = 0; - port_Rs_marker = 0; - port_Ls_marker = 0; - port_Cs_marker = 0; - for (const auto &[idx, data] : ports) - { - for (const auto &elem : data.GetElements()) - { - for (int i = 0; i < elem->GetMarker().Size(); i++) - { - MFEM_VERIFY(!(port_marker[i] && elem->GetMarker()[i]), - "Boundary attribute is assigned to more than one lumped port!"); - port_marker[i] = port_marker[i] || elem->GetMarker()[i]; - if (std::abs(data.GetR()) > 0.0) - { - port_Rs_marker[i] = port_Rs_marker[i] || elem->GetMarker()[i]; - } - if (std::abs(data.GetL()) > 0.0) - { - port_Ls_marker[i] = port_Ls_marker[i] || elem->GetMarker()[i]; - } - if (std::abs(data.GetC()) > 0.0) - { - port_Cs_marker[i] = port_Cs_marker[i] || elem->GetMarker()[i]; - } - } - } + ports.try_emplace(idx, data, mat_op, h1_fespace); } } @@ -370,19 +359,14 @@ void LumpedPortOperator::PrintBoundaryInfo(const IoData &iodata, const mfem::Par Mpi::Print("\nConfiguring Robin impedance BC for lumped ports at attributes:\n"); for (const auto &[idx, data] : ports) { - for (const auto &elem : data.GetElements()) + for (const auto &elem : data.elems) { - for (int i = 0; i < elem->GetMarker().Size(); i++) + for (auto attr : elem->GetAttrList()) { - if (!elem->GetMarker()[i]) - { - continue; - } - const int attr = i + 1; mfem::Vector normal = mesh::GetSurfaceNormal(mesh, attr); - const double Rs = data.GetR() * data.GetToSquare(*elem); - const double Ls = data.GetL() * data.GetToSquare(*elem); - const double Cs = data.GetC() / data.GetToSquare(*elem); + const double Rs = data.R * data.GetToSquare(*elem); + const double Ls = data.L * data.GetToSquare(*elem); + const double Cs = data.C / data.GetToSquare(*elem); bool comma = false; Mpi::Print(" {:d}:", attr); if (std::abs(Rs) > 0.0) @@ -434,30 +418,30 @@ void LumpedPortOperator::PrintBoundaryInfo(const IoData &iodata, const mfem::Par { bool comma = false; Mpi::Print(" Index = {:d}:", idx); - if (std::abs(data.GetR()) > 0.0) + if (std::abs(data.R) > 0.0) { Mpi::Print(" R = {:.3e} Ω", - iodata.DimensionalizeValue(IoData::ValueType::IMPEDANCE, data.GetR())); + iodata.DimensionalizeValue(IoData::ValueType::IMPEDANCE, data.R)); comma = true; } - if (std::abs(data.GetL()) > 0.0) + if (std::abs(data.L) > 0.0) { if (comma) { Mpi::Print(","); } Mpi::Print(" L = {:.3e} H", - iodata.DimensionalizeValue(IoData::ValueType::INDUCTANCE, data.GetL())); + iodata.DimensionalizeValue(IoData::ValueType::INDUCTANCE, data.L)); comma = true; } - if (std::abs(data.GetC()) > 0.0) + if (std::abs(data.C) > 0.0) { if (comma) { Mpi::Print(","); } Mpi::Print(" C = {:.3e} F", - iodata.DimensionalizeValue(IoData::ValueType::CAPACITANCE, data.GetC())); + iodata.DimensionalizeValue(IoData::ValueType::CAPACITANCE, data.C)); } Mpi::Print("\n"); } @@ -466,7 +450,7 @@ void LumpedPortOperator::PrintBoundaryInfo(const IoData &iodata, const mfem::Par bool first = true; for (const auto &[idx, data] : ports) { - if (!data.IsExcited()) + if (!data.excitation) { continue; } @@ -475,14 +459,11 @@ void LumpedPortOperator::PrintBoundaryInfo(const IoData &iodata, const mfem::Par Mpi::Print("\nConfiguring lumped port excitation source term at attributes:\n"); first = false; } - for (const auto &elem : data.GetElements()) + for (const auto &elem : data.elems) { - for (int i = 0; i < elem->GetMarker().Size(); i++) + for (auto attr : elem->GetAttrList()) { - if (elem->GetMarker()[i]) - { - Mpi::Print(" {:d}: Index = {:d}\n", i + 1, idx); - } + Mpi::Print(" {:d}: Index = {:d}\n", attr, idx); } } } @@ -495,56 +476,117 @@ const LumpedPortData &LumpedPortOperator::GetPort(int idx) const return it->second; } -void LumpedPortOperator::AddStiffnessBdrCoefficients(double coef, SumMatrixCoefficient &fb) +mfem::Array LumpedPortOperator::GetAttrList() const { - // Add lumped inductor boundaries to the bilinear form. + mfem::Array attr_list; for (const auto &[idx, data] : ports) { - if (data.GetL() == 0.0) + for (const auto &elem : data.elems) { - continue; + attr_list.Append(elem->GetAttrList()); + } + } + return attr_list; +} + +mfem::Array LumpedPortOperator::GetRsAttrList() const +{ + mfem::Array attr_list; + for (const auto &[idx, data] : ports) + { + if (std::abs(data.R) > 0.0) + { + for (const auto &elem : data.elems) + { + attr_list.Append(elem->GetAttrList()); + } } - for (const auto &elem : data.GetElements()) + } + return attr_list; +} + +mfem::Array LumpedPortOperator::GetLsAttrList() const +{ + mfem::Array attr_list; + for (const auto &[idx, data] : ports) + { + if (std::abs(data.L) > 0.0) { - const double Ls = data.GetL() * data.GetToSquare(*elem); - fb.AddCoefficient(std::make_unique(coef / Ls), - elem->GetMarker()); + for (const auto &elem : data.elems) + { + attr_list.Append(elem->GetAttrList()); + } } } + return attr_list; } -void LumpedPortOperator::AddMassBdrCoefficients(double coef, SumMatrixCoefficient &fb) +mfem::Array LumpedPortOperator::GetCsAttrList() const { - // Add lumped mass boundaries to the bilinear form. + mfem::Array attr_list; for (const auto &[idx, data] : ports) { - if (data.GetC() == 0.0) + if (std::abs(data.C) > 0.0) { - continue; + for (const auto &elem : data.elems) + { + attr_list.Append(elem->GetAttrList()); + } } - for (const auto &elem : data.GetElements()) + } + return attr_list; +} + +void LumpedPortOperator::AddStiffnessBdrCoefficients(double coef, + MaterialPropertyCoefficient &fb) +{ + // Add lumped inductor boundaries to the bilinear form. + for (const auto &[idx, data] : ports) + { + if (std::abs(data.L) > 0.0) { - const double Cs = data.GetC() / data.GetToSquare(*elem); - fb.AddCoefficient(std::make_unique(coef * Cs), - elem->GetMarker()); + for (const auto &elem : data.elems) + { + const double Ls = data.L * data.GetToSquare(*elem); + fb.AddMaterialProperty( + data.mat_op.GetBdrAttributeGlobalToLocal(elem->GetAttrList()), coef / Ls); + } } } } -void LumpedPortOperator::AddDampingBdrCoefficients(double coef, SumMatrixCoefficient &fb) +void LumpedPortOperator::AddDampingBdrCoefficients(double coef, + MaterialPropertyCoefficient &fb) { // Add lumped resistor boundaries to the bilinear form. for (const auto &[idx, data] : ports) { - if (data.GetR() == 0.0) + if (std::abs(data.R) > 0.0) { - continue; + for (const auto &elem : data.elems) + { + const double Rs = data.R * data.GetToSquare(*elem); + fb.AddMaterialProperty( + data.mat_op.GetBdrAttributeGlobalToLocal(elem->GetAttrList()), coef / Rs); + } } - for (const auto &elem : data.GetElements()) + } +} + +void LumpedPortOperator::AddMassBdrCoefficients(double coef, + MaterialPropertyCoefficient &fb) +{ + // Add lumped capacitance boundaries to the bilinear form. + for (const auto &[idx, data] : ports) + { + if (std::abs(data.C) > 0.0) { - const double Rs = data.GetR() * data.GetToSquare(*elem); - fb.AddCoefficient(std::make_unique(coef / Rs), - elem->GetMarker()); + for (const auto &elem : data.elems) + { + const double Cs = data.C / data.GetToSquare(*elem); + fb.AddMaterialProperty( + data.mat_op.GetBdrAttributeGlobalToLocal(elem->GetAttrList()), coef * Cs); + } } } } @@ -559,19 +601,18 @@ void LumpedPortOperator::AddExcitationBdrCoefficients(SumVectorCoefficient &fb) // works for time domain simulations requiring RHS -U_inc(t). for (const auto &[idx, data] : ports) { - if (!data.IsExcited()) + if (!data.excitation) { continue; } - MFEM_VERIFY(std::abs(data.GetR()) > 0.0, + MFEM_VERIFY(std::abs(data.R) > 0.0, "Unexpected zero resistance in excited lumped port!"); - for (const auto &elem : data.GetElements()) + for (const auto &elem : data.elems) { - const double Rs = data.GetR() * data.GetToSquare(*elem); - const double Hinc = - 1.0 / std::sqrt(Rs * elem->GetGeometryWidth() * elem->GetGeometryLength() * - data.GetElements().size()); - fb.AddCoefficient(elem->GetModeCoefficient(2.0 * Hinc), elem->GetMarker()); + const double Rs = data.R * data.GetToSquare(*elem); + const double Hinc = 1.0 / std::sqrt(Rs * elem->GetGeometryWidth() * + elem->GetGeometryLength() * data.elems.size()); + fb.AddCoefficient(elem->GetModeCoefficient(2.0 * Hinc)); } } } diff --git a/palace/models/lumpedportoperator.hpp b/palace/models/lumpedportoperator.hpp index ec08264af..6e5a00e98 100644 --- a/palace/models/lumpedportoperator.hpp +++ b/palace/models/lumpedportoperator.hpp @@ -16,7 +16,7 @@ namespace palace class IoData; class MaterialOperator; -class SumMatrixCoefficient; +class MaterialPropertyCoefficient; class SumVectorCoefficient; namespace config @@ -31,37 +31,33 @@ struct LumpedPortData; // class LumpedPortData { -private: - bool excitation; - double R, L, C; +public: + // Reference to material property data (not owned). + const MaterialOperator &mat_op; // To accomodate multielement lumped ports, a port may be made up of elements with // different attributes and directions which add in parallel. std::vector> elems; + // Lumped port properties. + double R, L, C; + bool excitation; + +private: // Linear forms for postprocessing integrated quantities on the port. mutable std::unique_ptr s, v; + void InitializeLinearForms(mfem::ParFiniteElementSpace &nd_fespace) const; public: - LumpedPortData(const config::LumpedPortData &data, + LumpedPortData(const config::LumpedPortData &data, const MaterialOperator &mat_op, mfem::ParFiniteElementSpace &h1_fespace); - const std::vector> &GetElements() const - { - return elems; - } - double GetToSquare(const LumpedElementData &elem) const { return elem.GetGeometryWidth() / elem.GetGeometryLength() * elems.size(); } - bool IsExcited() const { return excitation; } - double GetR() const { return R; } - double GetL() const { return L; } - double GetC() const { return C; } - std::complex GetCharacteristicImpedance(double omega = 0.0) const; double GetExcitationPower() const; @@ -69,10 +65,8 @@ class LumpedPortData std::complex GetSParameter(mfem::ParComplexGridFunction &E) const; std::complex GetPower(mfem::ParComplexGridFunction &E, - mfem::ParComplexGridFunction &B, - const MaterialOperator &mat_op) const; - double GetPower(mfem::ParGridFunction &E, mfem::ParGridFunction &B, - const MaterialOperator &mat_op) const; + mfem::ParComplexGridFunction &B) const; + double GetPower(mfem::ParGridFunction &E, mfem::ParGridFunction &B) const; std::complex GetVoltage(mfem::ParComplexGridFunction &E) const; double GetVoltage(mfem::ParGridFunction &E) const; }; @@ -87,13 +81,14 @@ class LumpedPortOperator // calculate circuit properties like voltage and current on lumped or multielement lumped // ports. std::map ports; - mfem::Array port_marker, port_Rs_marker, port_Ls_marker, port_Cs_marker; - void SetUpBoundaryProperties(const IoData &iodata, + + void SetUpBoundaryProperties(const IoData &iodata, const MaterialOperator &mat_op, mfem::ParFiniteElementSpace &h1_fespace); void PrintBoundaryInfo(const IoData &iodata, const mfem::ParMesh &mesh); public: - LumpedPortOperator(const IoData &iodata, mfem::ParFiniteElementSpace &h1_fespace); + LumpedPortOperator(const IoData &iodata, const MaterialOperator &mat_op, + mfem::ParFiniteElementSpace &h1_fespace); // Access data structures for the lumped port with the given index. const LumpedPortData &GetPort(int idx) const; @@ -103,17 +98,17 @@ class LumpedPortOperator auto rend() const { return ports.rend(); } auto Size() const { return ports.size(); } - // Returns array marking lumped port attributes. - const mfem::Array &GetMarker() const { return port_marker; } - const mfem::Array &GetRsMarker() const { return port_Rs_marker; } - const mfem::Array &GetLsMarker() const { return port_Ls_marker; } - const mfem::Array &GetCsMarker() const { return port_Cs_marker; } + // Returns array of lumped port attributes. + mfem::Array GetAttrList() const; + mfem::Array GetRsAttrList() const; + mfem::Array GetLsAttrList() const; + mfem::Array GetCsAttrList() const; // Add contributions to system matrices from lumped elements with nonzero inductance, - // capacitance, and/or resistance. - void AddStiffnessBdrCoefficients(double coef, SumMatrixCoefficient &fb); - void AddMassBdrCoefficients(double coef, SumMatrixCoefficient &fb); - void AddDampingBdrCoefficients(double coef, SumMatrixCoefficient &fb); + // resistance, and/or capacitance. + void AddStiffnessBdrCoefficients(double coef, MaterialPropertyCoefficient &fb); + void AddDampingBdrCoefficients(double coef, MaterialPropertyCoefficient &fb); + void AddMassBdrCoefficients(double coef, MaterialPropertyCoefficient &fb); // Add contributions to the right-hand side source term vector for an incident field at // excited port boundaries, -U_inc/(iω) for the real version (versus the full -U_inc for diff --git a/palace/models/surfaceconductivityoperator.cpp b/palace/models/surfaceconductivityoperator.cpp index 329d4c6c3..044b412e0 100644 --- a/palace/models/surfaceconductivityoperator.cpp +++ b/palace/models/surfaceconductivityoperator.cpp @@ -3,7 +3,7 @@ #include "surfaceconductivityoperator.hpp" -#include "fem/coefficient.hpp" +#include "models/materialoperator.hpp" #include "utils/communication.hpp" #include "utils/geodata.hpp" #include "utils/iodata.hpp" @@ -14,9 +14,11 @@ namespace palace using namespace std::complex_literals; SurfaceConductivityOperator::SurfaceConductivityOperator(const IoData &iodata, + const MaterialOperator &mat_op, const mfem::ParMesh &mesh) + : mat_op(mat_op) { - // Set up finite conductivity boundary conditions. + // Print out BC info for all finite conductivity boundary attributes. SetUpBoundaryProperties(iodata, mesh); PrintBoundaryInfo(iodata, mesh); } @@ -25,11 +27,12 @@ void SurfaceConductivityOperator::SetUpBoundaryProperties(const IoData &iodata, const mfem::ParMesh &mesh) { // Check that conductivity boundary attributes have been specified correctly. - int bdr_attr_max = mesh.bdr_attributes.Size() ? mesh.bdr_attributes.Max() : 0; if (!iodata.boundaries.conductivity.empty()) { - mfem::Array bdr_attr_marker(bdr_attr_max); + int bdr_attr_max = mesh.bdr_attributes.Size() ? mesh.bdr_attributes.Max() : 0; + mfem::Array bdr_attr_marker(bdr_attr_max), conductivity_marker(bdr_attr_max); bdr_attr_marker = 0; + conductivity_marker = 0; for (auto attr : mesh.bdr_attributes) { bdr_attr_marker[attr - 1] = 1; @@ -43,78 +46,61 @@ void SurfaceConductivityOperator::SetUpBoundaryProperties(const IoData &iodata, "correspond to attributes in the mesh!"); MFEM_VERIFY(bdr_attr_marker[attr - 1], "Unknown conductivity boundary attribute " << attr << "!"); + MFEM_VERIFY(!conductivity_marker[attr - 1], + "Multiple definitions of conductivity boundary properties for boundary " + "attribute " + << attr << "!"); + conductivity_marker[attr - 1] = 1; } } } // Finite conductivity boundaries are defined using the user provided surface conductivity // and optionally conductor thickness. - bdr_sigma.SetSize(bdr_attr_max); - bdr_mu.SetSize(bdr_attr_max); - bdr_h.SetSize(bdr_attr_max); - bdr_sigma = 0.0; - bdr_mu = 0.0; - bdr_h = 0.0; + boundaries.reserve(iodata.boundaries.conductivity.size()); for (const auto &data : iodata.boundaries.conductivity) { MFEM_VERIFY(data.sigma > 0.0 && data.mu_r > 0.0, "Conductivity boundary has no conductivity or no " "permeability defined!"); MFEM_VERIFY(data.h >= 0.0, "Conductivity boundary should have non-negative thickness!"); - for (auto attr : data.attributes) + auto &bdr = boundaries.emplace_back(); + bdr.sigma = data.sigma; + bdr.mu = data.mu_r; + bdr.h = data.h; + if (data.external) { - MFEM_VERIFY( - bdr_sigma(attr - 1) == 0.0 && bdr_mu(attr - 1) == 0.0 && bdr_h(attr - 1) == 0.0, - "Multiple definitions of conductivity boundary properties for boundary attribute " - << attr << "!"); - bdr_sigma(attr - 1) = data.sigma; - bdr_mu(attr - 1) = data.mu_r; - bdr_h(attr - 1) = data.h; - if (data.external) - { - // External surfaces have twice the effective thickness since the BC is applied at - // one side. - bdr_h(attr - 1) *= 2.0; - } - } - } - - // Mark selected boundary attributes from the mesh as finite conductivity. - mfem::Array conductivity_bcs; - for (const auto &data : iodata.boundaries.conductivity) - { - for (auto attr : data.attributes) - { - conductivity_bcs.Append(attr); + // External surfaces have twice the effective thickness since the BC is applied at one + // side. + bdr.h *= 2.0; } + bdr.attr_list.Append(data.attributes.data(), data.attributes.size()); } - MFEM_VERIFY(conductivity_bcs.Size() == 0 || + MFEM_VERIFY(boundaries.empty() || iodata.problem.type == config::ProblemData::Type::DRIVEN, "Finite conductivity boundaries are only available for frequency " "domain driven simulations!"); - mesh::AttrToMarker(bdr_attr_max, conductivity_bcs, conductivity_marker); } void SurfaceConductivityOperator::PrintBoundaryInfo(const IoData &iodata, const mfem::ParMesh &mesh) { - if (conductivity_marker.Size() && conductivity_marker.Max() == 0) + if (boundaries.empty()) { return; } Mpi::Print("\nConfiguring Robin finite conductivity BC at attributes:\n"); - for (int i = 0; i < conductivity_marker.Size(); i++) + for (const auto &bdr : boundaries) { - if (conductivity_marker[i]) + for (auto attr : bdr.attr_list) { - const int attr = i + 1; mfem::Vector normal = mesh::GetSurfaceNormal(mesh, attr); Mpi::Print(" {:d}: σ = {:.3e} S/m", attr, - iodata.DimensionalizeValue(IoData::ValueType::CONDUCTIVITY, bdr_sigma(i))); - if (bdr_h(i) > 0.0) + iodata.DimensionalizeValue(IoData::ValueType::CONDUCTIVITY, bdr.sigma)); + if (bdr.h > 0.0) { Mpi::Print(", h = {:.3e} m", - iodata.DimensionalizeValue(IoData::ValueType::LENGTH, bdr_h(i))); + iodata.DimensionalizeValue(IoData::ValueType::LENGTH, bdr.h)); } if (mesh.SpaceDimension() == 3) { @@ -129,44 +115,43 @@ void SurfaceConductivityOperator::PrintBoundaryInfo(const IoData &iodata, } } -void SurfaceConductivityOperator::AddExtraSystemBdrCoefficients(double omega, - SumMatrixCoefficient &fbr, - SumMatrixCoefficient &fbi) +mfem::Array SurfaceConductivityOperator::GetAttrList() const { - if (conductivity_marker.Size() && conductivity_marker.Max() > 0) + mfem::Array attr_list; + for (const auto &bdr : boundaries) { - // If the provided conductor thickness is empty (zero), prescribe a surface impedance - // (1+i)/σδ, where δ is the skin depth. If it is nonzero, use a finite thickness - // modification which correctly produces the DC limit when h << δ. See the Ansys HFSS - // user manual section titled "Surface Impedance Boundary Condition for Metal Traces of - // Finite Thickness." - mfem::Vector vr(bdr_sigma.Size()), vi(bdr_sigma.Size()); - for (int i = 0; i < bdr_sigma.Size(); i++) + attr_list.Append(bdr.attr_list); + } + return attr_list; +} + +void SurfaceConductivityOperator::AddExtraSystemBdrCoefficients( + double omega, MaterialPropertyCoefficient &fbr, MaterialPropertyCoefficient &fbi) +{ + // If the provided conductor thickness is empty (zero), prescribe a surface impedance + // (1+i)/σδ, where δ is the skin depth. If it is nonzero, use a finite thickness + // modification which correctly produces the DC limit when h << δ. See the Ansys HFSS + // user manual section titled "Surface Impedance Boundary Condition for Metal Traces of + // Finite Thickness." + for (const auto &bdr : boundaries) + { + if (std::abs(bdr.sigma) > 0.0) { - if (bdr_sigma(i) > 0.0) - { - double delta = std::sqrt(2.0 / (bdr_mu(i) * bdr_sigma(i) * omega)); - std::complex Z = 1.0 / (bdr_sigma(i) * delta); - Z.imag(Z.real()); - if (bdr_h(i) > 0.0) - { - double nu = bdr_h(i) / delta; - double den = std::cosh(nu) - std::cos(nu); - Z.real(Z.real() * (std::sinh(nu) + std::sin(nu)) / den); - Z.imag(Z.imag() * (std::sinh(nu) - std::sin(nu)) / den); - } - // The BC term has coefficient iω/Z (like for standard lumped surface impedance). - std::complex s(1i * omega / Z); - vr(i) = s.real(); - vi(i) = s.imag(); - } - else + double delta = std::sqrt(2.0 / (bdr.mu * bdr.sigma * omega)); + std::complex Z = 1.0 / (bdr.sigma * delta); + Z.imag(Z.real()); + if (bdr.h > 0.0) { - vr(i) = vi(i) = 0.0; // Not a conductivity boundary + double nu = bdr.h / delta; + double den = std::cosh(nu) - std::cos(nu); + Z.real(Z.real() * (std::sinh(nu) + std::sin(nu)) / den); + Z.imag(Z.imag() * (std::sinh(nu) - std::sin(nu)) / den); } + // The BC term has coefficient iω/Z (like for standard lumped surface impedance). + std::complex s(1i * omega / Z); + fbr.AddMaterialProperty(mat_op.GetBdrAttributeGlobalToLocal(bdr.attr_list), s.real()); + fbi.AddMaterialProperty(mat_op.GetBdrAttributeGlobalToLocal(bdr.attr_list), s.imag()); } - fbr.AddCoefficient(std::make_unique(vr), conductivity_marker); - fbi.AddCoefficient(std::make_unique(vi), conductivity_marker); } } diff --git a/palace/models/surfaceconductivityoperator.hpp b/palace/models/surfaceconductivityoperator.hpp index 7648e9647..62668cbec 100644 --- a/palace/models/surfaceconductivityoperator.hpp +++ b/palace/models/surfaceconductivityoperator.hpp @@ -4,13 +4,15 @@ #ifndef PALACE_MODELS_SURFACE_CONDUCTIVITY_OPERATOR_HPP #define PALACE_MODELS_SURFACE_CONDUCTIVITY_OPERATOR_HPP +#include #include namespace palace { class IoData; -class SumMatrixCoefficient; +class MaterialOperator; +class MaterialPropertyCoefficient; // // A class handling finite conductivity boundaries. @@ -18,22 +20,31 @@ class SumMatrixCoefficient; class SurfaceConductivityOperator { private: + // Reference to material property data (not owned). + const MaterialOperator &mat_op; + // Surface properties for finite conductivity boundary attributes: conductor conductivity // and permeability, and (optionally) thickness. - mfem::Vector bdr_sigma, bdr_mu, bdr_h; - mfem::Array conductivity_marker; + struct ConductivityData + { + double sigma, mu, h; + mfem::Array attr_list; + }; + std::vector boundaries; + void SetUpBoundaryProperties(const IoData &iodata, const mfem::ParMesh &mesh); void PrintBoundaryInfo(const IoData &iodata, const mfem::ParMesh &mesh); public: - SurfaceConductivityOperator(const IoData &iodata, const mfem::ParMesh &mesh); + SurfaceConductivityOperator(const IoData &iodata, const MaterialOperator &mat_op, + const mfem::ParMesh &mesh); - // Returns array marking finite conductivity boundary attributes. - const mfem::Array &GetMarker() const { return conductivity_marker; } + // Returns array of finite conductivity boundary attributes. + mfem::Array GetAttrList() const; // Add contributions to system matrix for a finite conductivity boundary condition. - void AddExtraSystemBdrCoefficients(double omega, SumMatrixCoefficient &fbr, - SumMatrixCoefficient &fbi); + void AddExtraSystemBdrCoefficients(double omega, MaterialPropertyCoefficient &fbr, + MaterialPropertyCoefficient &fbi); }; } // namespace palace diff --git a/palace/models/surfacecurrentoperator.cpp b/palace/models/surfacecurrentoperator.cpp index 5c546a0a0..ef081f339 100644 --- a/palace/models/surfacecurrentoperator.cpp +++ b/palace/models/surfacecurrentoperator.cpp @@ -3,7 +3,6 @@ #include "surfacecurrentoperator.hpp" -#include #include "fem/coefficient.hpp" #include "utils/communication.hpp" #include "utils/geodata.hpp" @@ -19,20 +18,17 @@ SurfaceCurrentData::SurfaceCurrentData(const config::SurfaceCurrentData &data, // sources. for (const auto &elem : data.elements) { - mfem::Array attr_marker; - mesh::AttrToMarker(h1_fespace.GetParMesh()->bdr_attributes.Size() - ? h1_fespace.GetParMesh()->bdr_attributes.Max() - : 0, - elem.attributes, attr_marker); + mfem::Array attr_list; + attr_list.Append(elem.attributes.data(), elem.attributes.size()); switch (elem.coordinate_system) { case config::internal::ElementData::CoordinateSystem::CYLINDRICAL: elems.push_back( - std::make_unique(elem.direction, attr_marker, h1_fespace)); + std::make_unique(elem.direction, attr_list, h1_fespace)); break; case config::internal::ElementData::CoordinateSystem::CARTESIAN: elems.push_back( - std::make_unique(elem.direction, attr_marker, h1_fespace)); + std::make_unique(elem.direction, attr_list, h1_fespace)); break; } } @@ -56,14 +52,14 @@ void SurfaceCurrentOperator::SetUpBoundaryProperties( const IoData &iodata, mfem::ParFiniteElementSpace &h1_fespace) { // Check that surface current boundary attributes have been specified correctly. - int bdr_attr_max = h1_fespace.GetParMesh()->bdr_attributes.Size() - ? h1_fespace.GetParMesh()->bdr_attributes.Max() - : 0; if (!iodata.boundaries.current.empty()) { - mfem::Array bdr_attr_marker(bdr_attr_max); + const auto &mesh = *h1_fespace.GetParMesh(); + int bdr_attr_max = mesh.bdr_attributes.Size() ? mesh.bdr_attributes.Max() : 0; + mfem::Array bdr_attr_marker(bdr_attr_max), source_marker(bdr_attr_max); bdr_attr_marker = 0; - for (auto attr : h1_fespace.GetParMesh()->bdr_attributes) + source_marker = 0; + for (auto attr : mesh.bdr_attributes) { bdr_attr_marker[attr - 1] = 1; } @@ -78,6 +74,10 @@ void SurfaceCurrentOperator::SetUpBoundaryProperties( "correspond to boundaries in the mesh!"); MFEM_VERIFY(bdr_attr_marker[attr - 1], "Unknown surface current boundary attribute " << attr << "!"); + MFEM_VERIFY( + !source_marker[attr - 1], + "Boundary attribute is assigned to more than one surface current source!"); + source_marker[attr - 1] = 1; } } } @@ -88,23 +88,6 @@ void SurfaceCurrentOperator::SetUpBoundaryProperties( { sources.try_emplace(idx, data, h1_fespace); } - - // Mark selected boundary attributes from the mesh for current sources. - source_marker.SetSize(bdr_attr_max); - source_marker = 0; - for (const auto &[idx, data] : sources) - { - for (const auto &elem : data.GetElements()) - { - for (int i = 0; i < elem->GetMarker().Size(); i++) - { - MFEM_VERIFY( - !(source_marker[i] && elem->GetMarker()[i]), - "Boundary attribute is assigned to more than one surface current source!"); - source_marker[i] = source_marker[i] || elem->GetMarker()[i]; - } - } - } } void SurfaceCurrentOperator::PrintBoundaryInfo(const IoData &iodata, @@ -117,15 +100,10 @@ void SurfaceCurrentOperator::PrintBoundaryInfo(const IoData &iodata, Mpi::Print("\nConfiguring surface current excitation source term at attributes:\n"); for (const auto &[idx, data] : sources) { - for (const auto &elem : data.GetElements()) + for (const auto &elem : data.elems) { - for (int i = 0; i < elem->GetMarker().Size(); i++) + for (auto attr : elem->GetAttrList()) { - if (!elem->GetMarker()[i]) - { - continue; - } - const int attr = i + 1; mfem::Vector normal = mesh::GetSurfaceNormal(mesh, attr); Mpi::Print(" {:d}: Index = {:d}", attr, idx); if (mesh.SpaceDimension() == 3) @@ -149,6 +127,19 @@ const SurfaceCurrentData &SurfaceCurrentOperator::GetSource(int idx) const return it->second; } +mfem::Array SurfaceCurrentOperator::GetAttrList() const +{ + mfem::Array attr_list; + for (const auto &[idx, data] : sources) + { + for (const auto &elem : data.elems) + { + attr_list.Append(elem->GetAttrList()); + } + } + return attr_list; +} + void SurfaceCurrentOperator::AddExcitationBdrCoefficients(SumVectorCoefficient &fb) { // Construct the RHS source term for surface current boundaries, which looks like @@ -173,10 +164,10 @@ void SurfaceCurrentOperator::AddExcitationBdrCoefficients(const SurfaceCurrentDa { // Add excited boundaries to the linear form, with a unit current distributed across // all elements of the current source in parallel. - for (const auto &elem : data.GetElements()) + for (const auto &elem : data.elems) { - const double Jinc = 1.0 / (elem->GetGeometryWidth() * data.GetElements().size()); - fb.AddCoefficient(elem->GetModeCoefficient(-Jinc), elem->GetMarker()); + const double Jinc = 1.0 / (elem->GetGeometryWidth() * data.elems.size()); + fb.AddCoefficient(elem->GetModeCoefficient(-Jinc)); } } diff --git a/palace/models/surfacecurrentoperator.hpp b/palace/models/surfacecurrentoperator.hpp index 719b91aa3..c5cbd681e 100644 --- a/palace/models/surfacecurrentoperator.hpp +++ b/palace/models/surfacecurrentoperator.hpp @@ -28,7 +28,7 @@ struct SurfaceCurrentData; // class SurfaceCurrentData { -private: +public: // To accomodate multielement surface current sources, a current source may be made up // of elements with different attributes and directions which add to deliver the same // total source current. @@ -38,11 +38,6 @@ class SurfaceCurrentData SurfaceCurrentData(const config::SurfaceCurrentData &data, mfem::ParFiniteElementSpace &h1_fespace); - const std::vector> &GetElements() const - { - return elems; - } - double GetExcitationCurrent() const; }; @@ -55,7 +50,7 @@ class SurfaceCurrentOperator // Mapping from source index to data structure containing source surface current // information. std::map sources; - mfem::Array source_marker; + void SetUpBoundaryProperties(const IoData &iodata, mfem::ParFiniteElementSpace &h1_fespace); void PrintBoundaryInfo(const IoData &iodata, const mfem::ParMesh &mesh); @@ -71,8 +66,8 @@ class SurfaceCurrentOperator auto rend() const { return sources.rend(); } auto Size() const { return sources.size(); } - // Returns array marking surface current source attributes. - const mfem::Array &GetMarker() const { return source_marker; } + // Returns array of surface current source attributes. + mfem::Array GetAttrList() const; // Add contributions to the right-hand side source term vector for a surface current // excitation at the specified boundaries, -J_inc for the real version (versus the diff --git a/palace/models/surfaceimpedanceoperator.cpp b/palace/models/surfaceimpedanceoperator.cpp index 07b984c0c..84e14678e 100644 --- a/palace/models/surfaceimpedanceoperator.cpp +++ b/palace/models/surfaceimpedanceoperator.cpp @@ -3,7 +3,7 @@ #include "surfaceimpedanceoperator.hpp" -#include "fem/coefficient.hpp" +#include "models/materialoperator.hpp" #include "utils/communication.hpp" #include "utils/geodata.hpp" #include "utils/iodata.hpp" @@ -12,9 +12,11 @@ namespace palace { SurfaceImpedanceOperator::SurfaceImpedanceOperator(const IoData &iodata, + const MaterialOperator &mat_op, const mfem::ParMesh &mesh) + : mat_op(mat_op) { - // Set up impedance boundary conditions. + // Print out BC info for all impedance boundary attributes. SetUpBoundaryProperties(iodata, mesh); PrintBoundaryInfo(iodata, mesh); } @@ -23,11 +25,12 @@ void SurfaceImpedanceOperator::SetUpBoundaryProperties(const IoData &iodata, const mfem::ParMesh &mesh) { // Check that impedance boundary attributes have been specified correctly. - int bdr_attr_max = mesh.bdr_attributes.Size() ? mesh.bdr_attributes.Max() : 0; if (!iodata.boundaries.impedance.empty()) { - mfem::Array bdr_attr_marker(bdr_attr_max); + int bdr_attr_max = mesh.bdr_attributes.Size() ? mesh.bdr_attributes.Max() : 0; + mfem::Array bdr_attr_marker(bdr_attr_max), impedance_marker(bdr_attr_max); bdr_attr_marker = 0; + impedance_marker = 0; for (auto attr : mesh.bdr_attributes) { bdr_attr_marker[attr - 1] = 1; @@ -41,104 +44,68 @@ void SurfaceImpedanceOperator::SetUpBoundaryProperties(const IoData &iodata, "to attributes in the mesh!"); MFEM_VERIFY(bdr_attr_marker[attr - 1], "Unknown impedance boundary attribute " << attr << "!"); + MFEM_VERIFY( + !impedance_marker[attr - 1], + "Multiple definitions of impedance boundary properties for boundary attribute " + << attr << "!"); + impedance_marker[attr - 1] = 1; } } } // Impedance boundaries are defined using the user provided impedance per square. - Z_Rsinv.SetSize(bdr_attr_max); - Z_Lsinv.SetSize(bdr_attr_max); - Z_Cs.SetSize(bdr_attr_max); - Z_Rsinv = 0.0; - Z_Lsinv = 0.0; - Z_Cs = 0.0; + boundaries.reserve(iodata.boundaries.impedance.size()); for (const auto &data : iodata.boundaries.impedance) { - for (auto attr : data.attributes) - { - MFEM_VERIFY( - Z_Rsinv(attr - 1) == 0.0 && Z_Lsinv(attr - 1) == 0.0 && Z_Cs(attr - 1) == 0.0, - "Multiple definitions of impedance boundary properties for boundary attribute " - << attr << "!"); - Z_Rsinv(attr - 1) = (std::abs(data.Rs) > 0.0) ? 1.0 / data.Rs : 0.0; - Z_Lsinv(attr - 1) = (std::abs(data.Ls) > 0.0) ? 1.0 / data.Ls : 0.0; - Z_Cs(attr - 1) = (std::abs(data.Cs) > 0.0) ? data.Cs : 0.0; - MFEM_VERIFY(std::abs(Z_Rsinv(attr - 1)) + std::abs(Z_Lsinv(attr - 1)) + - std::abs(Z_Cs(attr - 1)) > - 0.0, - "Impedance boundary has no Rs, Ls, or Cs defined!"); - } - } - - // Mark selected boundary attributes from the mesh as impedance. - mfem::Array impedance_bcs, impedance_Rs_bcs, impedance_Ls_bcs, impedance_Cs_bcs; - for (const auto &data : iodata.boundaries.impedance) - { - for (auto attr : data.attributes) - { - impedance_bcs.Append(attr); - if (std::abs(Z_Rsinv(attr - 1)) > 0.0) - { - impedance_Rs_bcs.Append(attr); - } - if (std::abs(Z_Lsinv(attr - 1)) > 0.0) - { - impedance_Ls_bcs.Append(attr); - } - if (std::abs(Z_Cs(attr - 1)) > 0.0) - { - impedance_Cs_bcs.Append(attr); - } - } + MFEM_VERIFY(std::abs(data.Rs) + std::abs(data.Ls) + std::abs(data.Cs) > 0.0, + "Impedance boundary has no Rs, Ls, or Cs defined!"); + auto &bdr = boundaries.emplace_back(); + bdr.Rs = data.Rs; + bdr.Ls = data.Ls; + bdr.Cs = data.Cs; + bdr.attr_list.Append(data.attributes.data(), data.attributes.size()); } - mesh::AttrToMarker(bdr_attr_max, impedance_bcs, impedance_marker); - mesh::AttrToMarker(bdr_attr_max, impedance_Rs_bcs, impedance_Rs_marker); - mesh::AttrToMarker(bdr_attr_max, impedance_Ls_bcs, impedance_Ls_marker); - mesh::AttrToMarker(bdr_attr_max, impedance_Cs_bcs, impedance_Cs_marker); } void SurfaceImpedanceOperator::PrintBoundaryInfo(const IoData &iodata, const mfem::ParMesh &mesh) { - if (impedance_marker.Size() && impedance_marker.Max() == 0) + if (boundaries.empty()) { return; } Mpi::Print("\nConfiguring Robin impedance BC at attributes:\n"); - for (int i = 0; i < impedance_marker.Size(); i++) + for (const auto &bdr : boundaries) { - if (impedance_marker[i]) + for (auto attr : bdr.attr_list) { - const int attr = i + 1; mfem::Vector normal = mesh::GetSurfaceNormal(mesh, attr); bool comma = false; Mpi::Print(" {:d}:", attr); - if (std::abs(Z_Rsinv(i)) > 0.0) + if (std::abs(bdr.Rs) > 0.0) { - Mpi::Print( - " Rs = {:.3e} Ω/sq", - iodata.DimensionalizeValue(IoData::ValueType::IMPEDANCE, 1.0 / Z_Rsinv(i))); + Mpi::Print(" Rs = {:.3e} Ω/sq", + iodata.DimensionalizeValue(IoData::ValueType::IMPEDANCE, bdr.Rs)); comma = true; } - if (std::abs(Z_Lsinv(i)) > 0.0) + if (std::abs(bdr.Ls) > 0.0) { if (comma) { Mpi::Print(","); } - Mpi::Print( - " Ls = {:.3e} H/sq", - iodata.DimensionalizeValue(IoData::ValueType::INDUCTANCE, 1.0 / Z_Lsinv(i))); + Mpi::Print(" Ls = {:.3e} H/sq", + iodata.DimensionalizeValue(IoData::ValueType::INDUCTANCE, bdr.Ls)); comma = true; } - if (std::abs(Z_Cs(i)) > 0.0) + if (std::abs(bdr.Cs) > 0.0) { if (comma) { Mpi::Print(","); } Mpi::Print(" Cs = {:.3e} F/sq", - iodata.DimensionalizeValue(IoData::ValueType::CAPACITANCE, Z_Cs(i))); + iodata.DimensionalizeValue(IoData::ValueType::CAPACITANCE, bdr.Cs)); comma = true; } if (comma) @@ -158,39 +125,94 @@ void SurfaceImpedanceOperator::PrintBoundaryInfo(const IoData &iodata, } } -void SurfaceImpedanceOperator::AddStiffnessBdrCoefficients(double coef, - SumMatrixCoefficient &fb) +mfem::Array SurfaceImpedanceOperator::GetAttrList() const { - // Lumped inductor boundaries. - if (impedance_Ls_marker.Size() && impedance_Ls_marker.Max() > 0) + mfem::Array attr_list; + for (const auto &bdr : boundaries) { - mfem::Vector v(Z_Lsinv); - v *= coef; - auto f = std::make_unique(v); - fb.AddCoefficient(std::make_unique(v), impedance_Ls_marker); + attr_list.Append(bdr.attr_list); } + return attr_list; } -void SurfaceImpedanceOperator::AddMassBdrCoefficients(double coef, SumMatrixCoefficient &fb) +mfem::Array SurfaceImpedanceOperator::GetRsAttrList() const { - // Lumped capacitor boundaries. - if (impedance_Cs_marker.Size() && impedance_Cs_marker.Max() > 0) + mfem::Array attr_list; + for (const auto &bdr : boundaries) + { + if (std::abs(bdr.Rs) > 0.0) + { + attr_list.Append(bdr.attr_list); + } + } + return attr_list; +} + +mfem::Array SurfaceImpedanceOperator::GetLsAttrList() const +{ + mfem::Array attr_list; + for (const auto &bdr : boundaries) + { + if (std::abs(bdr.Ls) > 0.0) + { + attr_list.Append(bdr.attr_list); + } + } + return attr_list; +} + +mfem::Array SurfaceImpedanceOperator::GetCsAttrList() const +{ + mfem::Array attr_list; + for (const auto &bdr : boundaries) + { + if (std::abs(bdr.Cs) > 0.0) + { + attr_list.Append(bdr.attr_list); + } + } + return attr_list; +} + +void SurfaceImpedanceOperator::AddStiffnessBdrCoefficients(double coef, + MaterialPropertyCoefficient &fb) +{ + // Lumped inductor boundaries. + for (const auto &bdr : boundaries) { - mfem::Vector v(Z_Cs); - v *= coef; - fb.AddCoefficient(std::make_unique(v), impedance_Cs_marker); + if (std::abs(bdr.Ls) > 0.0) + { + fb.AddMaterialProperty(mat_op.GetBdrAttributeGlobalToLocal(bdr.attr_list), + coef / bdr.Ls); + } } } void SurfaceImpedanceOperator::AddDampingBdrCoefficients(double coef, - SumMatrixCoefficient &fb) + MaterialPropertyCoefficient &fb) { // Lumped resistor boundaries. - if (impedance_Rs_marker.Size() && impedance_Rs_marker.Max() > 0) + for (const auto &bdr : boundaries) { - mfem::Vector v(Z_Rsinv); - v *= coef; - fb.AddCoefficient(std::make_unique(v), impedance_Rs_marker); + if (std::abs(bdr.Rs) > 0.0) + { + fb.AddMaterialProperty(mat_op.GetBdrAttributeGlobalToLocal(bdr.attr_list), + coef / bdr.Rs); + } + } +} + +void SurfaceImpedanceOperator::AddMassBdrCoefficients(double coef, + MaterialPropertyCoefficient &fb) +{ + // Lumped capacitor boundaries. + for (const auto &bdr : boundaries) + { + if (std::abs(bdr.Cs) > 0.0) + { + fb.AddMaterialProperty(mat_op.GetBdrAttributeGlobalToLocal(bdr.attr_list), + coef * bdr.Cs); + } } } diff --git a/palace/models/surfaceimpedanceoperator.hpp b/palace/models/surfaceimpedanceoperator.hpp index 17b8620ef..9b9ee6990 100644 --- a/palace/models/surfaceimpedanceoperator.hpp +++ b/palace/models/surfaceimpedanceoperator.hpp @@ -4,13 +4,15 @@ #ifndef PALACE_MODELS_SURFACE_IMPEDANCE_OPERATOR_HPP #define PALACE_MODELS_SURFACE_IMPEDANCE_OPERATOR_HPP +#include #include namespace palace { class IoData; -class SumMatrixCoefficient; +class MaterialOperator; +class MaterialPropertyCoefficient; // // A class handling impedance boundaries. @@ -18,29 +20,37 @@ class SumMatrixCoefficient; class SurfaceImpedanceOperator { private: + // Reference to material property data (not owned). + const MaterialOperator &mat_op; + // Surface properties for impedance boundary attributes: surface resistance, capacitance, // and inductance. - mfem::Vector Z_Rsinv, Z_Lsinv, Z_Cs; - mfem::Array impedance_marker, impedance_Rs_marker, impedance_Ls_marker, - impedance_Cs_marker; + struct ImpedanceData + { + double Rs, Ls, Cs; + mfem::Array attr_list; + }; + std::vector boundaries; + void SetUpBoundaryProperties(const IoData &iodata, const mfem::ParMesh &mesh); void PrintBoundaryInfo(const IoData &iodata, const mfem::ParMesh &mesh); public: - SurfaceImpedanceOperator(const IoData &iodata, const mfem::ParMesh &mesh); + SurfaceImpedanceOperator(const IoData &iodata, const MaterialOperator &mat_op, + const mfem::ParMesh &mesh); - // Returns array marking surface impedance attributes. - const mfem::Array &GetMarker() const { return impedance_marker; } - const mfem::Array &GetRsMarker() const { return impedance_Rs_marker; } - const mfem::Array &GetLsMarker() const { return impedance_Ls_marker; } - const mfem::Array &GetCsMarker() const { return impedance_Cs_marker; } + // Returns array of surface impedance attributes. + mfem::Array GetAttrList() const; + mfem::Array GetRsAttrList() const; + mfem::Array GetLsAttrList() const; + mfem::Array GetCsAttrList() const; // Add contributions to system matrices from impedance boundaries with nonzero inductance, - // capacitance, and/or resistance. For boundaries with more than R/L/C, impedances add in + // resistance, and/or capacitance. For boundaries with more than R/L/C, impedances add in // parallel. - void AddStiffnessBdrCoefficients(double coef, SumMatrixCoefficient &fb); - void AddMassBdrCoefficients(double coef, SumMatrixCoefficient &fb); - void AddDampingBdrCoefficients(double coef, SumMatrixCoefficient &fb); + void AddStiffnessBdrCoefficients(double coef, MaterialPropertyCoefficient &fb); + void AddDampingBdrCoefficients(double coef, MaterialPropertyCoefficient &fb); + void AddMassBdrCoefficients(double coef, MaterialPropertyCoefficient &fb); }; } // namespace palace diff --git a/palace/models/surfacepostoperator.cpp b/palace/models/surfacepostoperator.cpp index d1bd57896..57011dfc5 100644 --- a/palace/models/surfacepostoperator.cpp +++ b/palace/models/surfacepostoperator.cpp @@ -4,7 +4,6 @@ #include "surfacepostoperator.hpp" #include -#include #include "fem/integrator.hpp" #include "models/materialoperator.hpp" #include "utils/communication.hpp" @@ -15,7 +14,7 @@ namespace palace { SurfacePostOperator::InterfaceDielectricData::InterfaceDielectricData( - const config::InterfaceDielectricData &data, mfem::ParMesh &mesh) + const config::InterfaceDielectricData &data, const mfem::ParMesh &mesh) : ts(data.ts), tandelta(data.tandelta) { // Calculate surface dielectric loss according to the formulas from J. Wenner et al., @@ -71,50 +70,58 @@ SurfacePostOperator::InterfaceDielectricData::InterfaceDielectricData( side /= side.Norml2(); } - // Store markers for this element of the postprocessing boundary. - mesh::AttrToMarker(mesh.bdr_attributes.Size() ? mesh.bdr_attributes.Max() : 0, - elem.attributes, attr_markers.emplace_back()); + // Store boundary attributes for this element of the postprocessing boundary. + auto &attr_list = attr_lists.emplace_back(); + attr_list.Append(elem.attributes.data(), elem.attributes.size()); } } std::unique_ptr SurfacePostOperator::InterfaceDielectricData::GetCoefficient( - int i, const mfem::ParGridFunction &U, const MaterialOperator &mat_op) const + std::size_t i, const mfem::ParGridFunction &U, const MaterialOperator &mat_op) const { + auto MakeRestricted = [&](std::unique_ptr &&coeff) + { return std::make_unique(std::move(coeff), attr_lists[i]); }; switch (type) { case DielectricInterfaceType::MA: - return std::make_unique>( - U, mat_op, ts, epsilon, sides[i]); + return MakeRestricted( + std::make_unique>( + U, mat_op, ts, epsilon, sides[i])); case DielectricInterfaceType::MS: - return std::make_unique>( - U, mat_op, ts, epsilon, sides[i]); + return MakeRestricted( + std::make_unique>( + U, mat_op, ts, epsilon, sides[i])); case DielectricInterfaceType::SA: - return std::make_unique>( - U, mat_op, ts, epsilon, sides[i]); + return MakeRestricted( + std::make_unique>( + U, mat_op, ts, epsilon, sides[i])); case DielectricInterfaceType::DEFAULT: - return std::make_unique< - DielectricInterfaceCoefficient>( - U, mat_op, ts, epsilon, sides[i]); + return MakeRestricted( + std::make_unique< + DielectricInterfaceCoefficient>( + U, mat_op, ts, epsilon, sides[i])); } return {}; // For compiler warning } SurfacePostOperator::SurfaceChargeData::SurfaceChargeData( - const config::CapacitanceData &data, mfem::ParMesh &mesh) + const config::CapacitanceData &data, const mfem::ParMesh &mesh) { - mesh::AttrToMarker(mesh.bdr_attributes.Size() ? mesh.bdr_attributes.Max() : 0, - data.attributes, attr_markers.emplace_back()); + // Store boundary attributes for this element of the postprocessing boundary. + auto &attr_list = attr_lists.emplace_back(); + attr_list.Append(data.attributes.data(), data.attributes.size()); } std::unique_ptr SurfacePostOperator::SurfaceChargeData::GetCoefficient( - int i, const mfem::ParGridFunction &U, const MaterialOperator &mat_op) const + std::size_t i, const mfem::ParGridFunction &U, const MaterialOperator &mat_op) const { - return std::make_unique(U, mat_op); + return std::make_unique( + std::make_unique(U, mat_op), attr_lists[0]); } SurfacePostOperator::SurfaceFluxData::SurfaceFluxData(const config::InductanceData &data, - mfem::ParMesh &mesh) + const mfem::ParMesh &mesh) { // Store information about the global direction for orientation. Note the true boundary // normal is used in calculating the flux, this is just used to determine the sign. @@ -122,18 +129,16 @@ SurfacePostOperator::SurfaceFluxData::SurfaceFluxData(const config::InductanceDa std::copy(data.direction.begin(), data.direction.end(), direction.begin()); direction /= direction.Norml2(); - // Construct the coefficient for this postprocessing boundary (copies the direction - // vector). - mesh::AttrToMarker(mesh.bdr_attributes.Size() ? mesh.bdr_attributes.Max() : 0, - data.attributes, attr_markers.emplace_back()); + // Store boundary attributes for this element of the postprocessing boundary. + auto &attr_list = attr_lists.emplace_back(); + attr_list.Append(data.attributes.data(), data.attributes.size()); } -std::unique_ptr -SurfacePostOperator::SurfaceFluxData::GetCoefficient(int i, const mfem::ParGridFunction &U, - const MaterialOperator &mat_op) const +std::unique_ptr SurfacePostOperator::SurfaceFluxData::GetCoefficient( + std::size_t i, const mfem::ParGridFunction &U, const MaterialOperator &mat_op) const { - return std::make_unique(U, direction, - mat_op.GetLocalToSharedFaceMap()); + return std::make_unique( + std::make_unique(U, mat_op, direction), attr_lists[0]); } SurfacePostOperator::SurfacePostOperator(const IoData &iodata, @@ -248,13 +253,18 @@ double SurfacePostOperator::GetLocalSurfaceIntegral(const SurfaceData &data, const mfem::ParGridFunction &U) const { // Integrate the coefficient over the boundary attributes making up this surface index. - std::vector> fb; - mfem::LinearForm s(const_cast(ones.FESpace())); - for (int i = 0; i < static_cast(data.attr_markers.size()); i++) + const auto &mesh = *U.ParFESpace()->GetParMesh(); + SumCoefficient fb; + mfem::Array attr_list; + for (std::size_t i = 0; i < data.attr_lists.size(); i++) { - fb.emplace_back(data.GetCoefficient(i, U, mat_op)); - s.AddBoundaryIntegrator(new BoundaryLFIntegrator(*fb.back()), data.attr_markers[i]); + fb.AddCoefficient(data.GetCoefficient(i, U, mat_op)); + attr_list.Append(data.attr_lists[i]); } + int bdr_attr_max = mesh.bdr_attributes.Size() ? mesh.bdr_attributes.Max() : 0; + mfem::Array attr_marker = mesh::AttrToMarker(bdr_attr_max, attr_list); + mfem::LinearForm s(ones.FESpace()); + s.AddBoundaryIntegrator(new BoundaryLFIntegrator(fb), attr_marker); s.UseFastAssembly(false); s.Assemble(); return s * ones; diff --git a/palace/models/surfacepostoperator.hpp b/palace/models/surfacepostoperator.hpp index 12bd1d733..49303bc4f 100644 --- a/palace/models/surfacepostoperator.hpp +++ b/palace/models/surfacepostoperator.hpp @@ -10,6 +10,8 @@ #include #include "fem/coefficient.hpp" +// XX TODO: Rename BoundaryPostOperator for config file consistency? + namespace palace { @@ -35,12 +37,12 @@ class SurfacePostOperator // information for surface loss, charge, or magnetic flux. struct SurfaceData { - mutable std::vector> attr_markers; + std::vector> attr_lists; virtual ~SurfaceData() = default; virtual std::unique_ptr - GetCoefficient(int i, const mfem::ParGridFunction &U, + GetCoefficient(std::size_t i, const mfem::ParGridFunction &U, const MaterialOperator &mat_op) const = 0; }; struct InterfaceDielectricData : public SurfaceData @@ -50,28 +52,28 @@ class SurfacePostOperator std::vector sides; InterfaceDielectricData(const config::InterfaceDielectricData &data, - mfem::ParMesh &mesh); + const mfem::ParMesh &mesh); std::unique_ptr - GetCoefficient(int i, const mfem::ParGridFunction &U, + GetCoefficient(std::size_t i, const mfem::ParGridFunction &U, const MaterialOperator &mat_op) const override; }; struct SurfaceChargeData : public SurfaceData { - SurfaceChargeData(const config::CapacitanceData &data, mfem::ParMesh &mesh); + SurfaceChargeData(const config::CapacitanceData &data, const mfem::ParMesh &mesh); std::unique_ptr - GetCoefficient(int i, const mfem::ParGridFunction &U, + GetCoefficient(std::size_t i, const mfem::ParGridFunction &U, const MaterialOperator &mat_op) const override; }; struct SurfaceFluxData : public SurfaceData { mfem::Vector direction; - SurfaceFluxData(const config::InductanceData &data, mfem::ParMesh &mesh); + SurfaceFluxData(const config::InductanceData &data, const mfem::ParMesh &mesh); std::unique_ptr - GetCoefficient(int i, const mfem::ParGridFunction &U, + GetCoefficient(std::size_t i, const mfem::ParGridFunction &U, const MaterialOperator &mat_op) const override; }; std::map eps_surfs; @@ -82,7 +84,7 @@ class SurfacePostOperator const MaterialOperator &mat_op; // Unit function used for computing surface integrals. - mfem::GridFunction ones; + mutable mfem::GridFunction ones; double GetLocalSurfaceIntegral(const SurfaceData &data, const mfem::ParGridFunction &U) const; From 9f8b76f64799639d1b44ebdc0304894aea411e31 Mon Sep 17 00:00:00 2001 From: Sebastian Grimberg Date: Thu, 14 Dec 2023 18:39:59 -0800 Subject: [PATCH 03/32] WIP: Upgrade models continued (wave ports) --- palace/models/postoperator.cpp | 63 +-- palace/models/postoperator.hpp | 7 + palace/models/waveportoperator.cpp | 756 ++++++++++++++++------------- palace/models/waveportoperator.hpp | 95 ++-- 4 files changed, 508 insertions(+), 413 deletions(-) diff --git a/palace/models/postoperator.cpp b/palace/models/postoperator.cpp index ee17ca3bd..9e394d18c 100644 --- a/palace/models/postoperator.cpp +++ b/palace/models/postoperator.cpp @@ -78,18 +78,16 @@ PostOperator::PostOperator(const IoData &iodata, SpaceOperator &spaceop, B->real(), mat_op); } - // Initialize data collection objects and register additional fields associated with wave - // ports (only constructed in SpaceOperator). - InitializeDataCollection(iodata); + // Add wave port boundary mode postprocessing when available. for (const auto &[idx, data] : spaceop.GetWavePortOp()) { - paraview_bdr.RegisterVCoeffField( - "nxH^0_" + std::to_string(idx) + "_real", - const_cast(&data.GetModeCoefficientReal())); - paraview_bdr.RegisterVCoeffField( - "nxH^0_" + std::to_string(idx) + "_imag", - const_cast(&data.GetModeCoefficientImag())); + auto ret = port_E0.insert(std::make_pair(idx, WavePortFieldData())); + ret.first->second.E0r = data.GetModeFieldCoefficientReal(); + ret.first->second.E0i = data.GetModeFieldCoefficientImag(); } + + // Initialize data collection objects. + InitializeDataCollection(iodata); } PostOperator::PostOperator(const IoData &iodata, LaplaceOperator &laplaceop, @@ -260,6 +258,13 @@ void PostOperator::InitializeDataCollection(const IoData &iodata) paraview.RegisterCoeffField("Um", Um.get()); paraview_bdr.RegisterCoeffField("Um", Um.get()); } + + // Add wave port boundary mode postprocessing when available. + for (const auto &[idx, data] : port_E0) + { + paraview_bdr.RegisterVCoeffField("E0_" + std::to_string(idx) + "_real", data.E0r.get()); + paraview_bdr.RegisterVCoeffField("E0_" + std::to_string(idx) + "_imag", data.E0i.get()); + } } void PostOperator::SetEGridFunction(const ComplexVector &e) @@ -342,13 +347,13 @@ void PostOperator::UpdatePorts(const LumpedPortOperator &lumped_port_op, double omega > 0.0, "Frequency domain lumped port postprocessing requires nonzero frequency!"); vi.S = data.GetSParameter(*E); - vi.P = data.GetPower(*E, *B, mat_op); + vi.P = data.GetPower(*E, *B); vi.V = data.GetVoltage(*E); vi.Z = data.GetCharacteristicImpedance(omega); } else { - vi.P = data.GetPower(E->real(), B->real(), mat_op); + vi.P = data.GetPower(E->real(), B->real()); vi.V = data.GetVoltage(E->real()); vi.S = vi.Z = 0.0; } @@ -369,7 +374,7 @@ void PostOperator::UpdatePorts(const WavePortOperator &wave_port_op, double omeg "Frequency domain wave port postprocessing requires nonzero frequency!"); auto &vi = wave_port_vi[idx]; vi.S = data.GetSParameter(*E); - vi.P = data.GetPower(*E, *B, mat_op); + vi.P = data.GetPower(*E, *B); vi.V = vi.Z = 0.0; // Not yet implemented (Z = V² / P, I = V / Z) } wave_port_init = true; @@ -418,10 +423,10 @@ double PostOperator::GetLumpedInductorEnergy(const LumpedPortOperator &lumped_po double U = 0.0; for (const auto &[idx, data] : lumped_port_op) { - if (std::abs(data.GetL()) > 0.0) + if (std::abs(data.L) > 0.0) { std::complex Ij = GetPortCurrent(lumped_port_op, idx); - U += 0.5 * std::abs(data.GetL()) * std::real(Ij * std::conj(Ij)); + U += 0.5 * std::abs(data.L) * std::real(Ij * std::conj(Ij)); } } return U; @@ -435,10 +440,10 @@ PostOperator::GetLumpedCapacitorEnergy(const LumpedPortOperator &lumped_port_op) double U = 0.0; for (const auto &[idx, data] : lumped_port_op) { - if (std::abs(data.GetC()) > 0.0) + if (std::abs(data.C) > 0.0) { std::complex Vj = GetPortVoltage(lumped_port_op, idx); - U += 0.5 * std::abs(data.GetC()) * std::real(Vj * std::conj(Vj)); + U += 0.5 * std::abs(data.C) * std::real(Vj * std::conj(Vj)); } } return U; @@ -452,7 +457,7 @@ std::complex PostOperator::GetSParameter(const LumpedPortOperator &lumpe const LumpedPortData &data = lumped_port_op.GetPort(idx); const LumpedPortData &src_data = lumped_port_op.GetPort(source_idx); const auto it = lumped_port_vi.find(idx); - MFEM_VERIFY(src_data.IsExcited(), + MFEM_VERIFY(src_data.excitation, "Lumped port index " << source_idx << " is not marked for excitation!"); MFEM_VERIFY(it != lumped_port_vi.end(), "Could not find lumped port when calculating port S-parameters!"); @@ -462,9 +467,9 @@ std::complex PostOperator::GetSParameter(const LumpedPortOperator &lumpe Sij.real(Sij.real() - 1.0); } // Generalized S-parameters if the ports are resistive (avoids divide-by-zero). - if (std::abs(data.GetR()) > 0.0) + if (std::abs(data.R) > 0.0) { - Sij *= std::sqrt(src_data.GetR() / data.GetR()); + Sij *= std::sqrt(src_data.R / data.R); } return Sij; } @@ -478,7 +483,7 @@ std::complex PostOperator::GetSParameter(const WavePortOperator &wave_po const WavePortData &data = wave_port_op.GetPort(idx); const WavePortData &src_data = wave_port_op.GetPort(source_idx); const auto it = wave_port_vi.find(idx); - MFEM_VERIFY(src_data.IsExcited(), + MFEM_VERIFY(src_data.excitation, "Wave port index " << source_idx << " is not marked for excitation!"); MFEM_VERIFY(it != wave_port_vi.end(), "Could not find wave port when calculating port S-parameters!"); @@ -489,8 +494,8 @@ std::complex PostOperator::GetSParameter(const WavePortOperator &wave_po } // Port de-embedding: S_demb = S exp(-ikₙᵢ dᵢ) exp(-ikₙⱼ dⱼ) (distance offset is default // 0 unless specified). - Sij *= std::exp(1i * src_data.GetPropagationConstant() * src_data.GetOffsetDistance()); - Sij *= std::exp(1i * data.GetPropagationConstant() * data.GetOffsetDistance()); + Sij *= std::exp(1i * src_data.kn0 * src_data.d_offset); + Sij *= std::exp(1i * data.kn0 * data.d_offset); return Sij; } @@ -561,7 +566,7 @@ double PostOperator::GetInductorParticipation(const LumpedPortOperator &lumped_p // thus zero current. const LumpedPortData &data = lumped_port_op.GetPort(idx); std::complex Imj = GetPortCurrent(lumped_port_op, idx); - return std::copysign(0.5 * std::abs(data.GetL()) * std::real(Imj * std::conj(Imj)) / Em, + return std::copysign(0.5 * std::abs(data.L) * std::real(Imj * std::conj(Imj)) / Em, Imj.real()); // mean(I²) = (I_r² + I_i²) / 2 } @@ -576,7 +581,7 @@ double PostOperator::GetExternalKappa(const LumpedPortOperator &lumped_port_op, // Q_mj = ω_m / κ_mj. const LumpedPortData &data = lumped_port_op.GetPort(idx); std::complex Imj = GetPortCurrent(lumped_port_op, idx); - return std::copysign(0.5 * std::abs(data.GetR()) * std::real(Imj * std::conj(Imj)) / Em, + return std::copysign(0.5 * std::abs(data.R) * std::real(Imj * std::conj(Imj)) / Em, Imj.real()); // mean(I²) = (I_r² + I_i²) / 2 } @@ -669,18 +674,20 @@ void PostOperator::WriteFields(int step, double time, const ErrorIndicator *indi paraview_bdr.SetTime(time); if (first_save || indicator) { + // No need for these to be parallel objects, since the data is local to each process and + // there isn't a need to ever access the element neighbors. mfem::L2_FECollection pwconst_fec(0, mesh.Dimension()); - mfem::ParFiniteElementSpace pwconst_fespace(&mesh, &pwconst_fec); - std::unique_ptr rank, eta; + mfem::FiniteElementSpace pwconst_fespace(&mesh, &pwconst_fec); + std::unique_ptr rank, eta; if (first_save) { - rank = std::make_unique(&pwconst_fespace); + rank = std::make_unique(&pwconst_fespace); *rank = mesh.GetMyRank() + 1; paraview.RegisterField("Rank", rank.get()); } if (indicator) { - eta = std::make_unique(&pwconst_fespace); + eta = std::make_unique(&pwconst_fespace); MFEM_VERIFY(eta->Size() == indicator->Local().Size(), "Size mismatch for provided ErrorIndicator for postprocessing!"); *eta = indicator->Local(); diff --git a/palace/models/postoperator.hpp b/palace/models/postoperator.hpp index 94910b35c..10de7c8c5 100644 --- a/palace/models/postoperator.hpp +++ b/palace/models/postoperator.hpp @@ -50,6 +50,13 @@ class PostOperator std::unique_ptr Esr, Esi, Bsr, Bsi, As, Jsr, Jsi; std::unique_ptr Vs, Ue, Um, Qsr, Qsi; + // Wave port boundary mode field postprocessing. + struct WavePortFieldData + { + std::unique_ptr E0r, E0i; + }; + std::map port_E0; + // Lumped and wave port voltage and current (R, L, and C branches) caches updated when // the grid functions are set. struct PortPostData diff --git a/palace/models/waveportoperator.cpp b/palace/models/waveportoperator.cpp index e45f0b335..c5ada093a 100644 --- a/palace/models/waveportoperator.cpp +++ b/palace/models/waveportoperator.cpp @@ -7,7 +7,6 @@ #include #include "fem/bilinearform.hpp" #include "fem/coefficient.hpp" -#include "fem/fespace.hpp" #include "fem/integrator.hpp" #include "linalg/arpack.hpp" #include "linalg/iterative.hpp" @@ -35,16 +34,19 @@ void GetEssentialTrueDofs(mfem::ParGridFunction &E0t, mfem::ParGridFunction &E0n mfem::ParGridFunction &port_E0t, mfem::ParGridFunction &port_E0n, mfem::ParTransferMap &port_nd_transfer, mfem::ParTransferMap &port_h1_transfer, - const mfem::Array &dbc_marker, + const mfem::Array &dbc_attr, mfem::Array &port_nd_dbc_tdof_list, mfem::Array &port_h1_dbc_tdof_list) { - mfem::ParFiniteElementSpace &nd_fespace = *E0t.ParFESpace(); - mfem::ParFiniteElementSpace &h1_fespace = *E0n.ParFESpace(); - mfem::ParFiniteElementSpace &port_nd_fespace = *port_E0t.ParFESpace(); - mfem::ParFiniteElementSpace &port_h1_fespace = *port_E0n.ParFESpace(); - - mfem::Array nd_dbc_tdof_list, h1_dbc_tdof_list; + auto &nd_fespace = *E0t.ParFESpace(); + auto &h1_fespace = *E0n.ParFESpace(); + auto &port_nd_fespace = *port_E0t.ParFESpace(); + auto &port_h1_fespace = *port_E0n.ParFESpace(); + const auto &mesh = *nd_fespace.GetParMesh(); + + mfem::Array dbc_marker, nd_dbc_tdof_list, h1_dbc_tdof_list; + mesh::AttrToMarker(mesh.bdr_attributes.Size() ? mesh.bdr_attributes.Max() : 0, dbc_attr, + dbc_marker); nd_fespace.GetEssentialTrueDofs(dbc_marker, nd_dbc_tdof_list); h1_fespace.GetEssentialTrueDofs(dbc_marker, h1_dbc_tdof_list); @@ -78,49 +80,69 @@ void GetEssentialTrueDofs(mfem::ParGridFunction &E0t, mfem::ParGridFunction &E0n } } +void GetInitialSpace(const mfem::ParFiniteElementSpace &nd_fespace, + const mfem::ParFiniteElementSpace &h1_fespace, + const mfem::Array &nd_dbc_tdof_list, + const mfem::Array &h1_dbc_tdof_list, ComplexVector &v) +{ + // Initial space chosen as such that B v₀ = y₀, with y₀ = [y₀ₜ, 0, ... 0]ᵀ ⟂ null(A) + // (with Aₜₜ nonsingular). See Lee, Sun, and Cendes, 1991 for reference. + // Note: When the eigenvalue solver uses a standard ℓ²-inner product instead of B-inner + // product (since we use a general non-Hermitian solver due to complex symmetric B), then + // we just use v0 = y0 directly. + v.SetSize(nd_fespace.GetTrueVSize() + h1_fespace.GetTrueVSize()); + // linalg::SetRandomReal(nd_fespace.GetComm(), v); + v = std::complex(1.0, 0.0); + linalg::SetSubVector(v, nd_dbc_tdof_list, 0.0); + for (int i = nd_fespace.GetTrueVSize(); + i < nd_fespace.GetTrueVSize() + h1_fespace.GetTrueVSize(); i++) + { + v.Real()[i] = v.Imag()[i] = 0.0; + } +} + constexpr bool skip_zeros = false; std::unique_ptr GetBtt(const MaterialOperator &mat_op, - const mfem::ParFiniteElementSpace &nd_fespace) + const FiniteElementSpace &nd_fespace) { // Mass matrix: Bₜₜ = (μ⁻¹ u, v). - constexpr auto MatType = MaterialPropertyType::INV_PERMEABILITY; - constexpr auto ElemType = MeshElementType::BDR_SUBMESH; - MaterialPropertyCoefficient muinv_func(mat_op); + MaterialPropertyCoefficient muinv_func(mat_op.GetBdrAttributeToMaterial(), + mat_op.GetInvPermeability()); BilinearForm btt(nd_fespace); - btt.AddDomainIntegrator(muinv_func); + btt.AddDomainIntegrator((mfem::MatrixCoefficient &)muinv_func); return std::make_unique(btt.FullAssemble(skip_zeros), nd_fespace); } std::unique_ptr GetBtn(const MaterialOperator &mat_op, - const mfem::ParFiniteElementSpace &nd_fespace, - const mfem::ParFiniteElementSpace &h1_fespace) + const FiniteElementSpace &nd_fespace, + const FiniteElementSpace &h1_fespace) { // Mass matrix: Bₜₙ = (μ⁻¹ ∇ₜ u, v). - constexpr auto MatType = MaterialPropertyType::INV_PERMEABILITY; - constexpr auto ElemType = MeshElementType::BDR_SUBMESH; - MaterialPropertyCoefficient muinv_func(mat_op); + MaterialPropertyCoefficient muinv_func(mat_op.GetBdrAttributeToMaterial(), + mat_op.GetInvPermeability()); BilinearForm btn(h1_fespace, nd_fespace); - btn.AddDomainIntegrator(muinv_func); + btn.AddDomainIntegrator( + (mfem::MatrixCoefficient &)muinv_func); return std::make_unique(btn.FullAssemble(skip_zeros), h1_fespace, nd_fespace, false); } -std::array, 3> -GetBnn(const MaterialOperator &mat_op, const mfem::ParFiniteElementSpace &h1_fespace) +std::array, 3> GetBnn(const MaterialOperator &mat_op, + const FiniteElementSpace &h1_fespace, + const mfem::Vector &normal) { // Mass matrix: Bₙₙ = (μ⁻¹ ∇ₜ u, ∇ₜ v) - ω² (ε u, v) = Bₙₙ₁ - ω² Bₙₙ₂. - constexpr auto MatTypeMuInv = MaterialPropertyType::INV_PERMEABILITY; - constexpr auto ElemType = MeshElementType::BDR_SUBMESH; - MaterialPropertyCoefficient muinv_func(mat_op); + MaterialPropertyCoefficient muinv_func(mat_op.GetBdrAttributeToMaterial(), + mat_op.GetInvPermeability()); BilinearForm bnn1(h1_fespace); - bnn1.AddDomainIntegrator(muinv_func); + bnn1.AddDomainIntegrator((mfem::MatrixCoefficient &)muinv_func); - constexpr auto MatTypeEpsReal = MaterialPropertyType::PERMITTIVITY_REAL; - NormalProjectedCoefficient epsilon_func( - std::make_unique>(mat_op)); + MaterialPropertyCoefficient epsilon_func(mat_op.GetBdrAttributeToMaterial(), + mat_op.GetPermittivityReal()); + epsilon_func.NormalProjectedCoefficient(normal); BilinearForm bnn2r(h1_fespace); - bnn2r.AddDomainIntegrator(epsilon_func); + bnn2r.AddDomainIntegrator((mfem::Coefficient &)epsilon_func); // Contribution for loss tangent: ε -> ε * (1 - i tan(δ)). if (!mat_op.HasLossTangent()) @@ -129,31 +151,32 @@ GetBnn(const MaterialOperator &mat_op, const mfem::ParFiniteElementSpace &h1_fes std::make_unique(bnn2r.FullAssemble(skip_zeros), h1_fespace), nullptr}; } - constexpr auto MatTypeEpsImag = MaterialPropertyType::PERMITTIVITY_IMAG; - NormalProjectedCoefficient negepstandelta_func( - std::make_unique>(mat_op)); + MaterialPropertyCoefficient negepstandelta_func(mat_op.GetBdrAttributeToMaterial(), + mat_op.GetPermittivityImag()); + negepstandelta_func.NormalProjectedCoefficient(normal); BilinearForm bnn2i(h1_fespace); - bnn2i.AddDomainIntegrator(negepstandelta_func); + bnn2i.AddDomainIntegrator((mfem::Coefficient &)negepstandelta_func); return {std::make_unique(bnn1.FullAssemble(skip_zeros), h1_fespace), std::make_unique(bnn2r.FullAssemble(skip_zeros), h1_fespace), std::make_unique(bnn2i.FullAssemble(skip_zeros), h1_fespace)}; } -std::array, 3> -GetAtt(const MaterialOperator &mat_op, const mfem::ParFiniteElementSpace &nd_fespace) +std::array, 3> GetAtt(const MaterialOperator &mat_op, + const FiniteElementSpace &nd_fespace, + const mfem::Vector &normal) { // Stiffness matrix: Aₜₜ = (μ⁻¹ ∇ₜ x u, ∇ₜ x v) - ω² (ε u, v) = Aₜₜ₁ - ω² Aₜₜ₂. - constexpr auto MatTypeMuInv = MaterialPropertyType::INV_PERMEABILITY; - constexpr auto ElemType = MeshElementType::BDR_SUBMESH; - NormalProjectedCoefficient muinv_func( - std::make_unique>(mat_op)); + MaterialPropertyCoefficient muinv_func(mat_op.GetBdrAttributeToMaterial(), + mat_op.GetInvPermeability()); + muinv_func.NormalProjectedCoefficient(normal); BilinearForm att1(nd_fespace); - att1.AddDomainIntegrator(muinv_func); + att1.AddDomainIntegrator((mfem::Coefficient &)muinv_func); - constexpr auto MatTypeEpsReal = MaterialPropertyType::PERMITTIVITY_REAL; - MaterialPropertyCoefficient epsilon_func(mat_op); + MaterialPropertyCoefficient epsilon_func(mat_op.GetBdrAttributeToMaterial(), + mat_op.GetPermittivityReal()); BilinearForm att2r(nd_fespace); - att2r.AddDomainIntegrator(epsilon_func); + att2r.AddDomainIntegrator( + (mfem::MatrixCoefficient &)epsilon_func); // Contribution for loss tangent: ε -> ε * (1 - i tan(δ)). if (!mat_op.HasLossTangent()) @@ -162,10 +185,11 @@ GetAtt(const MaterialOperator &mat_op, const mfem::ParFiniteElementSpace &nd_fes std::make_unique(att2r.FullAssemble(skip_zeros), nd_fespace), nullptr}; } - constexpr auto MatTypeEpsImag = MaterialPropertyType::PERMITTIVITY_IMAG; - MaterialPropertyCoefficient negepstandelta_func(mat_op); + MaterialPropertyCoefficient negepstandelta_func(mat_op.GetBdrAttributeToMaterial(), + mat_op.GetPermittivityImag()); BilinearForm att2i(nd_fespace); - att2i.AddDomainIntegrator(negepstandelta_func); + att2i.AddDomainIntegrator( + (mfem::MatrixCoefficient &)negepstandelta_func); return {std::make_unique(att1.FullAssemble(skip_zeros), nd_fespace), std::make_unique(att2r.FullAssemble(skip_zeros), nd_fespace), std::make_unique(att2i.FullAssemble(skip_zeros), nd_fespace)}; @@ -241,13 +265,15 @@ GetSystemMatrices(std::unique_ptr Btt, std::unique_ptr dbc_tdof_list.Append(tdof + nd_tdof_offset); } - mfem::Vector d(B3->Height()); - d = 0.0; - mfem::SparseMatrix diag(d); - mfem::HypreParMatrix Diag(B3->GetComm(), B3->GetGlobalNumRows(), B3->GetRowStarts(), - &diag); - A1.reset(mfem::Add(1.0, *A1, 1.0, Diag)); - B3.reset(mfem::Add(1.0, *B3, 1.0, Diag)); + { + mfem::Vector d(B3->Height()); + d = 0.0; + mfem::SparseMatrix diag(d); + mfem::HypreParMatrix Diag(B3->GetComm(), B3->GetGlobalNumRows(), B3->GetRowStarts(), + &diag); + A1.reset(mfem::Add(1.0, *A1, 1.0, Diag)); + B3.reset(mfem::Add(1.0, *B3, 1.0, Diag)); + } A1->EliminateBC(dbc_tdof_list, Operator::DIAG_ZERO); A2r->EliminateBC(dbc_tdof_list, Operator::DIAG_ZERO); @@ -266,27 +292,6 @@ GetSystemMatrices(std::unique_ptr Btt, std::unique_ptr std::move(B3), std::move(B4r), std::move(B4i)}; } -void GetInitialSpace(const mfem::ParFiniteElementSpace &nd_fespace, - const mfem::ParFiniteElementSpace &h1_fespace, - const mfem::Array &nd_dbc_tdof_list, - const mfem::Array &h1_dbc_tdof_list, ComplexVector &v) -{ - // Initial space chosen as such that B v₀ = y₀, with y₀ = [y₀ₜ, 0, ... 0]ᵀ ⟂ null(A) - // (with Aₜₜ nonsingular). See Lee, Sun, and Cendes, 1991 for reference. - // Note: When the eigenvalue solver uses a standard ℓ²-inner product instead of B-inner - // product (since we use a general non-Hermitian solver due to complex symmetric B), then - // we just use v0 = y0 directly. - v.SetSize(nd_fespace.GetTrueVSize() + h1_fespace.GetTrueVSize()); - // linalg::SetRandomReal(nd_fespace.GetComm(), v); - v = std::complex(1.0, 0.0); - linalg::SetSubVector(v, nd_dbc_tdof_list, 0.0); - for (int i = nd_fespace.GetTrueVSize(); - i < nd_fespace.GetTrueVSize() + h1_fespace.GetTrueVSize(); i++) - { - v.Real()[i] = v.Imag()[i] = 0.0; - } -} - void NormalizeWithSign(const mfem::ParGridFunction &S0t, mfem::ParComplexGridFunction &E0t, mfem::ParComplexGridFunction &E0n, mfem::LinearForm &sr, mfem::LinearForm &si) @@ -294,12 +299,8 @@ void NormalizeWithSign(const mfem::ParGridFunction &S0t, mfem::ParComplexGridFun // Normalize grid functions to a chosen polarization direction and unit power, |E x H⋆| ⋅ // n, integrated over the port surface (+n is the direction of propagation). The n x H // coefficients are updated implicitly as the only store references to the Et, En grid - // functions as well as kₙ, ω. We choose a (rather arbitrary) sign constraint to at least - // make results for the same port consistent between frequencies/meshes. - sr = 0.0; - si = 0.0; - sr.Assemble(); - si.Assemble(); + // functions. We choose a (rather arbitrary) sign constraint to at least make results for + // the same port consistent between frequencies/meshes. // |E x H⋆| ⋅ n = |E ⋅ (-n x H⋆)| double sign = sr * S0t; @@ -326,120 +327,198 @@ void NormalizeWithSign(const mfem::ParGridFunction &S0t, mfem::ParComplexGridFun // port_E0n->imag().ExchangeFaceNbrData(); } -// Computes boundary modal n x H, where +n is the direction of wave propagation: n x H = +// Helper for BdrSubmeshEVectorCoefficient and BdrSubmeshHVectorCoefficient. +enum class ValueType +{ + REAL, + IMAG +}; + +// Return as a vector coefficient the boundary mode electric field. +template +class BdrSubmeshEVectorCoefficient : public mfem::VectorCoefficient +{ +private: + const mfem::ParComplexGridFunction &Et, &En; + const mfem::ParSubMesh &submesh; + const std::unordered_map &submesh_parent_elems; + mfem::IsoparametricTransformation T_loc; + +public: + BdrSubmeshEVectorCoefficient(const mfem::ParComplexGridFunction &Et, + const mfem::ParComplexGridFunction &En, + const mfem::ParSubMesh &submesh, + const std::unordered_map &submesh_parent_elems) + : mfem::VectorCoefficient(Et.real().VectorDim()), Et(Et), En(En), submesh(submesh), + submesh_parent_elems(submesh_parent_elems) + { + } + + void Eval(mfem::Vector &V, mfem::ElementTransformation &T, + const mfem::IntegrationPoint &ip) override + { + // Always do the GridFunction evaluation in the submesh. + mfem::ElementTransformation *T_submesh = nullptr; + if (T.mesh == submesh.GetParent()) + { + MFEM_ASSERT(T.ElementType == mfem::ElementTransformation::BDR_ELEMENT, + "BdrSubmeshEVectorCoefficient requires ElementType::BDR_ELEMENT when not " + "used on a SubMesh!"); + auto it = submesh_parent_elems.find(T.ElementNo); + if (it == submesh_parent_elems.end()) + { + // Just return zero for a parent boundary element not in the submesh. + V.SetSize(vdim); + V = 0.0; + return; + } + else + { + submesh.GetElementTransformation(it->second, &T_loc); + T_loc.SetIntPoint(&ip); + T_submesh = &T_loc; + } + } + else if (T.mesh == &submesh) + { + MFEM_ASSERT(T.ElementType == mfem::ElementTransformation::ELEMENT, + "BdrSubmeshEVectorCoefficient requires ElementType::ELEMENT when used on " + "a SubMesh!"); + T_submesh = &T; + } + else + { + MFEM_ABORT("Invalid mesh for BdrSubmeshEVectorCoefficient!"); + } + + // Compute Eₜ + n ⋅ Eₙ . + mfem::Vector U, nor; + BdrGridFunctionCoefficient::GetNormal(*T_submesh, nor); + if constexpr (Type == ValueType::REAL) + { + Et.real().GetVectorValue(*T_submesh, ip, V); + auto Vn = En.real().GetValue(*T_submesh, ip); + V.Add(Vn, nor); + } + else + { + Et.imag().GetVectorValue(*T_submesh, ip, V); + auto Vn = En.imag().GetValue(*T_submesh, ip); + V.Add(Vn, nor); + } + } +}; + +// Computes boundary mode n x H, where +n is the direction of wave propagation: n x H = // -1/(iωμ) (ikₙ Eₜ + ∇ₜ Eₙ), using the tangential and normal electric field component grid -// functions evaluated on the (single-sided) boundary element. The intent of this vector -// grid function is to be dotted with a function E which is only in the tangential -// component, so the fact that we use the full ∇ Eₙ in the element is fine. We use only the -// real part of kn. -template +// functions evaluated on the (single-sided) boundary element. +template class BdrSubmeshHVectorCoefficient : public mfem::VectorCoefficient { private: const mfem::ParComplexGridFunction &Et, &En; const MaterialOperator &mat_op; - - mfem::ParSubMesh &submesh; - const mfem::ParMesh &parent; - std::unordered_map submesh_elem_ids; - + const mfem::ParSubMesh &submesh; + const std::unordered_map &submesh_parent_elems; + mfem::IsoparametricTransformation T_loc; std::complex kn; double omega; - mfem::ParSubMesh &GetSubMesh(mfem::ParMesh &mesh) - { - MFEM_ASSERT( - mfem::ParSubMesh::IsParSubMesh(&mesh), - "BdrSubmeshHVectorCoefficient requires the input grid function coefficients " - "to be defined on a SubMesh!"); - mfem::ParSubMesh &submesh = *static_cast(&mesh); - MFEM_ASSERT(submesh.GetFrom() == mfem::SubMesh::From::Boundary, - "BdrSubmeshHVectorCoefficient requires a SubMesh created using " - "CreateFromBoundary!"); - return submesh; - } - public: BdrSubmeshHVectorCoefficient(const mfem::ParComplexGridFunction &Et, const mfem::ParComplexGridFunction &En, - const MaterialOperator &mat_op) - : mfem::VectorCoefficient(Et.ParFESpace()->GetParMesh()->SpaceDimension()), Et(Et), - En(En), mat_op(mat_op), submesh(GetSubMesh(*Et.ParFESpace()->GetParMesh())), - parent(*submesh.GetParent()), kn(0.0), omega(0.0) + const MaterialOperator &mat_op, + const mfem::ParSubMesh &submesh, + const std::unordered_map &submesh_parent_elems, + std::complex kn, double omega) + : mfem::VectorCoefficient(Et.real().VectorDim()), Et(Et), En(En), mat_op(mat_op), + submesh(submesh), submesh_parent_elems(submesh_parent_elems), kn(kn), omega(omega) { - // Construct mapping from parent (boundary) element indices to submesh (domain) - // elements. - const mfem::Array &parent_element_ids = submesh.GetParentElementIDMap(); - for (int i = 0; i < parent_element_ids.Size(); i++) - { - submesh_elem_ids[parent_element_ids[i]] = i; - } } void Eval(mfem::Vector &V, mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip) override { - mfem::ElementTransformation *submesh_T = nullptr; - int attr = 0; - if (T.mesh == &parent) + // Always do the GridFunction evaluation in the submesh. + mfem::ElementTransformation *T_submesh = nullptr; + if (T.mesh == submesh.GetParent()) { MFEM_ASSERT(T.ElementType == mfem::ElementTransformation::BDR_ELEMENT, "BdrSubmeshHVectorCoefficient requires ElementType::BDR_ELEMENT when not " "used on a SubMesh!"); - auto it = submesh_elem_ids.find(T.ElementNo); - if (it == submesh_elem_ids.end()) + auto it = submesh_parent_elems.find(T.ElementNo); + if (it == submesh_parent_elems.end()) { - // Just return zero for a boundary face not in the submesh. + // Just return zero for a parent boundary element not in the submesh. V.SetSize(vdim); V = 0.0; return; } else { - submesh_T = submesh.GetElementTransformation(it->second); - submesh_T->SetIntPoint(&ip); + submesh.GetElementTransformation(it->second, &T_loc); + T_loc.SetIntPoint(&ip); + T_submesh = &T_loc; } - - int i, o, iel1, iel2; - parent.GetBdrElementFace(T.ElementNo, &i, &o); - parent.GetFaceElements(i, &iel1, &iel2); - attr = parent.GetAttribute(iel1); } else if (T.mesh == &submesh) { MFEM_ASSERT(T.ElementType == mfem::ElementTransformation::ELEMENT, "BdrSubmeshHVectorCoefficient requires ElementType::ELEMENT when used on " "a SubMesh!"); - submesh_T = &T; - - int i, o, iel1, iel2; - parent.GetBdrElementFace(submesh.GetParentElementIDMap()[T.ElementNo], &i, &o); - parent.GetFaceElements(i, &iel1, &iel2); - attr = parent.GetAttribute(iel1); + T_submesh = &T; } else { - MFEM_ABORT("Invalid use of BdrSubmeshHVectorCoefficient on an unrecognized mesh!"); + MFEM_ABORT("Invalid mesh for BdrSubmeshHVectorCoefficient!"); } - // Compute Re/Im{-1/i (ikₙ Eₜ + ∇ₜ Eₙ)}. + // Get the attribute in the neighboring domain element of the parent mesh. + int attr = [&T, this]() + { + int i = -1, o, iel1, iel2; + if (T.mesh == submesh.GetParent()) + { + MFEM_ASSERT( + T.ElementType == mfem::ElementTransformation::BDR_ELEMENT, + "BdrSubmeshHVectorCoefficient requires ElementType::BDR_ELEMENT when not " + "used on a SubMesh!"); + T.mesh->GetBdrElementFace(T.ElementNo, &i, &o); + } + else if (T.mesh == &submesh) + { + MFEM_ASSERT(T.ElementType == mfem::ElementTransformation::ELEMENT, + "BdrSubmeshHVectorCoefficient requires ElementType::ELEMENT when used " + "on a SubMesh!"); + submesh.GetParent()->GetBdrElementFace(submesh.GetParentElementIDMap()[T.ElementNo], + &i, &o); + } + else + { + MFEM_ABORT("Invalid mesh for BdrSubmeshHVectorCoefficient!"); + } + submesh.GetParent()->GetFaceElements(i, &iel1, &iel2); + return submesh.GetParent()->GetAttribute(iel1); + }(); + + // Compute Re/Im{-1/i (ikₙ Eₜ + ∇ₜ Eₙ)} (t-gradient evaluated in boundary element). mfem::Vector U; - if constexpr (RealPart) + if constexpr (Type == ValueType::REAL) { - Et.real().GetVectorValue(*submesh_T, ip, U); + Et.real().GetVectorValue(*T_submesh, ip, U); U *= -kn.real(); mfem::Vector dU; - En.imag().GetGradient(*submesh_T, dU); + En.imag().GetGradient(*T_submesh, dU); U -= dU; } else { - Et.imag().GetVectorValue(*submesh_T, ip, U); + Et.imag().GetVectorValue(*T_submesh, ip, U); U *= -kn.real(); mfem::Vector dU; - En.real().GetGradient(*submesh_T, dU); + En.real().GetGradient(*T_submesh, dU); U += dU; } @@ -448,51 +527,40 @@ class BdrSubmeshHVectorCoefficient : public mfem::VectorCoefficient mat_op.GetInvPermeability(attr).Mult(U, V); V *= (1.0 / omega); } - - void SetFrequency(double w, std::complex k) - { - omega = w; - kn = k; - } }; } // namespace WavePortData::WavePortData(const config::WavePortData &data, const MaterialOperator &mat_op, - const mfem::ParFiniteElementSpace &nd_fespace, - const mfem::ParFiniteElementSpace &h1_fespace, - const mfem::Array &dbc_marker) + mfem::ParFiniteElementSpace &nd_fespace, + mfem::ParFiniteElementSpace &h1_fespace, + const mfem::Array &dbc_attr) + : mat_op(mat_op) { - excitation = data.excitation; mode_idx = data.mode_idx; d_offset = data.d_offset; + excitation = data.excitation; + kn0 = 0.0; + omega0 = 0.0; // Construct the SubMesh. MFEM_VERIFY(!data.attributes.empty(), "Wave port boundary found with no attributes!"); - mfem::ParMesh &mesh = *nd_fespace.GetParMesh(); - attr_list.Reserve(data.attributes.size()); - for (auto attr : data.attributes) - { - attr_list.Append(attr); - } - mesh::AttrToMarker(nd_fespace.GetParMesh()->bdr_attributes.Size() - ? nd_fespace.GetParMesh()->bdr_attributes.Max() - : 0, - attr_list, attr_marker); + const auto &mesh = *nd_fespace.GetParMesh(); + attr_list.Append(data.attributes.data(), data.attributes.size()); port_mesh = std::make_unique( mfem::ParSubMesh::CreateFromBoundary(mesh, attr_list)); - int p_nd = nd_fespace.GetMaxElementOrder(); - int p_h1 = h1_fespace.GetMaxElementOrder(); - port_nd_fec = std::make_unique(p_nd, mesh.Dimension() - 1); - port_h1_fec = std::make_unique(p_h1, mesh.Dimension() - 1); + port_nd_fec = std::make_unique(nd_fespace.GetMaxElementOrder(), + port_mesh->Dimension()); + port_h1_fec = std::make_unique(h1_fespace.GetMaxElementOrder(), + port_mesh->Dimension()); port_nd_fespace = std::make_unique(port_mesh.get(), port_nd_fec.get()); port_h1_fespace = std::make_unique(port_mesh.get(), port_h1_fec.get()); - mfem::ParGridFunction E0t(const_cast(&nd_fespace)), - E0n(const_cast(&h1_fespace)); + mfem::ParGridFunction E0t(&nd_fespace); + mfem::ParGridFunction E0n(&h1_fespace); port_E0t = std::make_unique(port_nd_fespace.get()); port_E0n = std::make_unique(port_h1_fespace.get()); @@ -501,10 +569,20 @@ WavePortData::WavePortData(const config::WavePortData &data, const MaterialOpera port_h1_transfer = std::make_unique( mfem::ParSubMesh::CreateTransferMap(E0n, port_E0n->real())); + // Construct mapping from parent (boundary) element indices to submesh (domain) + // elements. + { + const mfem::Array &parent_elems = port_mesh->GetParentElementIDMap(); + for (int i = 0; i < parent_elems.Size(); i++) + { + submesh_parent_elems[parent_elems[i]] = i; + } + } + // Extract Dirichlet BC true dofs for the port FE spaces. mfem::Array port_nd_dbc_tdof_list, port_h1_dbc_tdof_list; GetEssentialTrueDofs(E0t, E0n, port_E0t->real(), port_E0n->real(), *port_nd_transfer, - *port_h1_transfer, dbc_marker, port_nd_dbc_tdof_list, + *port_h1_transfer, dbc_attr, port_nd_dbc_tdof_list, port_h1_dbc_tdof_list); // Construct operators for the generalized eigenvalue problem: @@ -533,14 +611,15 @@ WavePortData::WavePortData(const config::WavePortData &data, const MaterialOpera mu_eps_max = 1.0 / (c_min * c_min); // Pre-compute problem matrices such that: - // A = A₁ - ω² A₂, B = A₁ - 1 / (μₘ εₘ) B₄ - ω² A₂ + 1/Θ² B₃ . + // A = A₁ - ω² A₂, B = [A₁ - 1 / (μₘ εₘ) B₄] - ω² A₂ + 1/Θ² B₃ . { - std::unique_ptr A1, B4r, B4i; + std::unique_ptr B4r, B4i; { + mfem::Vector normal = mesh::GetSurfaceNormal(*port_mesh); auto Btt = GetBtt(mat_op, *port_nd_fespace); auto Btn = GetBtn(mat_op, *port_nd_fespace, *port_h1_fespace); - auto [Bnn1, Bnn2r, Bnn2i] = GetBnn(mat_op, *port_h1_fespace); - auto [Att1, Att2r, Att2i] = GetAtt(mat_op, *port_nd_fespace); + auto [Bnn1, Bnn2r, Bnn2i] = GetBnn(mat_op, *port_h1_fespace, normal); + auto [Att1, Att2r, Att2i] = GetAtt(mat_op, *port_nd_fespace, normal); auto system_mats = GetSystemMatrices( std::move(Btt), std::move(Btn), std::move(Bnn1), std::move(Bnn2r), @@ -556,36 +635,14 @@ WavePortData::WavePortData(const config::WavePortData &data, const MaterialOpera // Allocate storage for the eigenvalue problem operators. We have sparsity(A2) = // sparsity(B3) = sparsity(B4) ⊆ sparsity(A1). Precompute the frequency independent - // contributions to A and B. - P = std::make_unique( - std::make_unique(*A1), nullptr); - if (A2i) - { - A = std::make_unique( - std::make_unique(*A1), - std::make_unique(*A2i)); - B = std::make_unique( - std::make_unique(*A1), - std::make_unique(*A2i)); - - auto &Br = *static_cast(B->Real()); - Br.Add(-1.0 / mu_eps_max, *B4r); - - auto &Ai = *static_cast(A->Imag()); - auto &Bi = *static_cast(B->Imag()); - Ai *= 0.0; - Bi *= 0.0; - Bi.Add(-1.0 / mu_eps_max, *B4i); - } - else + // contributions to A and B. In order to support GPU, we avoid the in-place + // HypreParMatrix addition and use the Hypre variant which creates a new matrix but does + // support GPUs. + B1r.reset(mfem::Add(1.0, *A1, -1.0 / mu_eps_max, *B4r)); + if (B4i) { - A = std::make_unique( - std::make_unique(*A1), nullptr); - B = std::make_unique( - std::make_unique(*A1), nullptr); - - auto &Br = *static_cast(B->Real()); - Br.Add(-1.0 / mu_eps_max, *B4r); + B1i = std::move(B4i); + *B1i *= -1.0 / mu_eps_max; } } @@ -637,36 +694,42 @@ WavePortData::WavePortData(const config::WavePortData &data, const MaterialOpera #else #error "Wave port solver requires building with SuperLU_DIST, STRUMPACK, or MUMPS!" #endif - std::unique_ptr> pc; - if (pc_type == config::LinearSolverData::Type::SUPERLU) - { + + auto pc = std::make_unique>( + [&]() -> std::unique_ptr + { + if (pc_type == config::LinearSolverData::Type::SUPERLU) + { #if defined(MFEM_USE_SUPERLU) - auto slu = std::make_unique( - port_comm, config::LinearSolverData::SymFactType::DEFAULT, false, ksp_print - 1); - // slu->GetSolver().SetColumnPermutation(mfem::superlu::NATURAL); - pc = std::make_unique>(std::move(slu)); + auto slu = std::make_unique( + port_comm, config::LinearSolverData::SymFactType::DEFAULT, false, + ksp_print - 1); + // slu->GetSolver().SetColumnPermutation(mfem::superlu::NATURAL); + return slu; #endif - } - else if (pc_type == config::LinearSolverData::Type::STRUMPACK) - { + } + else if (pc_type == config::LinearSolverData::Type::STRUMPACK) + { #if defined(MFEM_USE_STRUMPACK) - auto strumpack = std::make_unique( - port_comm, config::LinearSolverData::SymFactType::DEFAULT, - config::LinearSolverData::CompressionType::NONE, 0.0, 0, 0, ksp_print - 1); - // strumpack->SetReorderingStrategy(strumpack::ReorderingStrategy::NATURAL); - pc = std::make_unique>(std::move(strumpack)); + auto strumpack = std::make_unique( + port_comm, config::LinearSolverData::SymFactType::DEFAULT, + config::LinearSolverData::CompressionType::NONE, 0.0, 0, 0, ksp_print - 1); + // strumpack->SetReorderingStrategy(strumpack::ReorderingStrategy::NATURAL); + return strumpack; #endif - } - else // config::LinearSolverData::Type::MUMPS - { + } + else if (pc_type == config::LinearSolverData::Type::MUMPS) + { #if defined(MFEM_USE_MUMPS) - auto mumps = std::make_unique( - port_comm, mfem::MUMPSSolver::SYMMETRIC_INDEFINITE, - config::LinearSolverData::SymFactType::DEFAULT, 0.0, ksp_print - 1); - // mumps->SetReorderingStrategy(mfem::MUMPSSolver::AMD); - pc = std::make_unique>(std::move(mumps)); + auto mumps = std::make_unique( + port_comm, mfem::MUMPSSolver::SYMMETRIC_INDEFINITE, + config::LinearSolverData::SymFactType::DEFAULT, 0.0, ksp_print - 1); + // mumps->SetReorderingStrategy(mfem::MUMPSSolver::AMD); + return mumps; #endif - } + } + return {}; + }()); ksp = std::make_unique(std::move(gmres), std::move(pc)); // Define the eigenvalue solver. @@ -701,21 +764,6 @@ WavePortData::WavePortData(const config::WavePortData &data, const MaterialOpera eigen->SetLinearSolver(*ksp); } - // Coefficients store references to kₙ, ω so they are updated implicitly at each new - // solve. Also, μ⁻¹ is persistent, so no copy is OK. - kn0 = 0.0; - omega0 = 0.0; - port_nxH0r_func = - std::make_unique>(*port_E0t, *port_E0n, mat_op); - port_nxH0i_func = - std::make_unique>(*port_E0t, *port_E0n, mat_op); - port_sr = std::make_unique(port_nd_fespace.get()); - port_si = std::make_unique(port_nd_fespace.get()); - port_sr->AddDomainIntegrator(new VectorFEDomainLFIntegrator(*port_nxH0r_func)); - port_si->AddDomainIntegrator(new VectorFEDomainLFIntegrator(*port_nxH0i_func)); - port_sr->UseFastAssembly(false); - port_si->UseFastAssembly(false); - // Configure port mode sign convention: 1ᵀ Re{-n x H} >= 0 on the "upper-right quadrant" // of the wave port boundary, in order to deal with symmetry effectively. { @@ -782,26 +830,31 @@ void WavePortData::Initialize(double omega) // Use pre-computed matrices to construct and solve the generalized eigenvalue problem for // the desired wave port mode. + std::unique_ptr A, B; double theta2 = mu_eps_max * omega * omega; { - auto &Pr = *static_cast(P->Real()); - Pr *= 0.0; - - auto &Ar = *static_cast(A->Real()); - auto &Br = *static_cast(B->Real()); - Ar.Add(-omega * omega + omega0 * omega0, *A2r); - Br.Add(-omega * omega + omega0 * omega0, *A2r); - Br.Add(1.0 / theta2 - ((omega0 == 0.0) ? 0.0 : 1.0 / (mu_eps_max * omega0 * omega0)), - *B3); - Pr.Add(1.0, Br); - + std::unique_ptr Ar(mfem::Add(1.0, *A1, -omega * omega, *A2r)); if (A2i) { - auto &Ai = *static_cast(A->Imag()); - auto &Bi = *static_cast(B->Imag()); - Ai.Add(-omega * omega + omega0 * omega0, *A2i); - Bi.Add(-omega * omega + omega0 * omega0, *A2i); - Pr.Add(1.0, Bi); + auto Ai = std::make_unique(*A2i); + *Ai *= -omega * omega; + A = std::make_unique(std::move(Ar), std::move(Ai)); + } + else + { + A = std::make_unique(std::move(Ar), nullptr); + } + + std::unique_ptr Br(mfem::Add(1.0, *B1r, -omega * omega, *A2r)); + Br.reset(mfem::Add(1.0, *Br, 1.0 / theta2, *B3)); + if (B1i) + { + std::unique_ptr Bi(mfem::Add(1.0, *B1i, -omega * omega, *A2i)); + B = std::make_unique(std::move(Br), std::move(Bi)); + } + else + { + B = std::make_unique(std::move(Br), nullptr); } } @@ -809,7 +862,8 @@ void WavePortData::Initialize(double omega) std::complex lambda; if (port_comm != MPI_COMM_NULL) { - ksp->SetOperators(*B, *P); + ComplexWrapperOperator P(B->Real(), nullptr); // Non-owning constructor + ksp->SetOperators(*B, P); eigen->SetOperators(*A, *B, EigenvalueSolver::ScaleType::NONE); eigen->SetInitialSpace(v0); int num_conv = eigen->Solve(); @@ -828,10 +882,6 @@ void WavePortData::Initialize(double omega) << "(λ = " << lambda << ")!"); kn0 = std::sqrt(theta2 - theta2 / lambda); omega0 = omega; - static_cast *>(port_nxH0r_func.get()) - ->SetFrequency(omega0, kn0); - static_cast *>(port_nxH0i_func.get()) - ->SetFrequency(omega0, kn0); // Separate the computed field out into eₜ and eₙ and and transform back to true // electric field variables: Eₜ = eₜ/kₙ and Eₙ = ieₙ. @@ -860,11 +910,58 @@ void WavePortData::Initialize(double omega) port_E0n->real().SetFromTrueDofs(e0n.Real()); port_E0n->imag().SetFromTrueDofs(e0n.Imag()); - // Normalize the mode for a chosen polarization direction and unit power, |E x H⋆| ⋅ n, - // integrated over the port surface (+n is the direction of propagation). + // Configure the linear forms for computing S-parameters (projection of the field onto the + // port mode). Normalize the mode for a chosen polarization direction and unit power, + // |E x H⋆| ⋅ n, integrated over the port surface (+n is the direction of propagation). + BdrSubmeshHVectorCoefficient port_nxH0r_func( + *port_E0t, *port_E0n, mat_op, *port_mesh, submesh_parent_elems, kn0, omega0); + BdrSubmeshHVectorCoefficient port_nxH0i_func( + *port_E0t, *port_E0n, mat_op, *port_mesh, submesh_parent_elems, kn0, omega0); + port_sr = std::make_unique(port_nd_fespace.get()); + port_si = std::make_unique(port_nd_fespace.get()); + port_sr->AddDomainIntegrator(new VectorFEDomainLFIntegrator(port_nxH0r_func)); + port_si->AddDomainIntegrator(new VectorFEDomainLFIntegrator(port_nxH0i_func)); + port_sr->UseFastAssembly(false); + port_si->UseFastAssembly(false); + port_sr->Assemble(); + port_si->Assemble(); NormalizeWithSign(*port_S0t, *port_E0t, *port_E0n, *port_sr, *port_si); } +std::unique_ptr +WavePortData::GetModeExcitationCoefficientReal() const +{ + return std::make_unique( + std::make_unique>( + *port_E0t, *port_E0n, mat_op, *port_mesh, submesh_parent_elems, kn0, omega0), + attr_list); +} + +std::unique_ptr +WavePortData::GetModeExcitationCoefficientImag() const +{ + return std::make_unique( + std::make_unique>( + *port_E0t, *port_E0n, mat_op, *port_mesh, submesh_parent_elems, kn0, omega0), + attr_list); +} + +std::unique_ptr WavePortData::GetModeFieldCoefficientReal() const +{ + return std::make_unique( + std::make_unique>( + *port_E0t, *port_E0n, *port_mesh, submesh_parent_elems), + attr_list); +} + +std::unique_ptr WavePortData::GetModeFieldCoefficientImag() const +{ + return std::make_unique( + std::make_unique>( + *port_E0t, *port_E0n, *port_mesh, submesh_parent_elems), + attr_list); +} + double WavePortData::GetExcitationPower() const { // The computed port modes are normalized such that the power integrated over the port is @@ -886,16 +983,18 @@ std::complex WavePortData::GetSParameter(mfem::ParComplexGridFunction &E } std::complex WavePortData::GetPower(mfem::ParComplexGridFunction &E, - mfem::ParComplexGridFunction &B, - const MaterialOperator &mat_op) const + mfem::ParComplexGridFunction &B) const { // Compute port power, (E x H) ⋅ n = E ⋅ (-n x H), integrated over the port surface // using the computed E and H = μ⁻¹ B fields. The linear form is reconstructed from // scratch each time due to changing H. The BdrCurrentVectorCoefficient computes -n x H, // where n is an outward normal. auto &nd_fespace = *E.ParFESpace(); + const auto &mesh = *nd_fespace.GetParMesh(); BdrCurrentVectorCoefficient nxHr_func(B.real(), mat_op); BdrCurrentVectorCoefficient nxHi_func(B.imag(), mat_op); + int bdr_attr_max = mesh.bdr_attributes.Size() ? mesh.bdr_attributes.Max() : 0; + mfem::Array attr_marker = mesh::AttrToMarker(bdr_attr_max, attr_list); mfem::LinearForm pr(&nd_fespace), pi(&nd_fespace); pr.AddBoundaryIntegrator(new VectorFEBoundaryLFIntegrator(nxHr_func), attr_marker); pi.AddBoundaryIntegrator(new VectorFEBoundaryLFIntegrator(nxHi_func), attr_marker); @@ -909,10 +1008,12 @@ std::complex WavePortData::GetPower(mfem::ParComplexGridFunction &E, return dot; } -WavePortOperator::WavePortOperator(const IoData &iod, const MaterialOperator &mat, - const mfem::ParFiniteElementSpace &nd_fespace, - const mfem::ParFiniteElementSpace &h1_fespace) - : iodata(iod), mat_op(mat), suppress_output(false) +WavePortOperator::WavePortOperator(const IoData &iodata, const MaterialOperator &mat_op, + mfem::ParFiniteElementSpace &nd_fespace, + mfem::ParFiniteElementSpace &h1_fespace) + : suppress_output(false), + fc(iodata.DimensionalizeValue(IoData::ValueType::FREQUENCY, 1.0)), + kc(1.0 / iodata.DimensionalizeValue(IoData::ValueType::LENGTH, 1.0)) { // Set up wave port boundary conditions. MFEM_VERIFY(nd_fespace.GetParMesh() == h1_fespace.GetParMesh(), @@ -921,20 +1022,20 @@ WavePortOperator::WavePortOperator(const IoData &iod, const MaterialOperator &ma PrintBoundaryInfo(iodata, *nd_fespace.GetParMesh()); } -void WavePortOperator::SetUpBoundaryProperties( - const IoData &iodata, const MaterialOperator &mat_op, - const mfem::ParFiniteElementSpace &nd_fespace, - const mfem::ParFiniteElementSpace &h1_fespace) +void WavePortOperator::SetUpBoundaryProperties(const IoData &iodata, + const MaterialOperator &mat_op, + mfem::ParFiniteElementSpace &nd_fespace, + mfem::ParFiniteElementSpace &h1_fespace) { // Check that wave port boundary attributes have been specified correctly. - int bdr_attr_max = nd_fespace.GetParMesh()->bdr_attributes.Size() - ? nd_fespace.GetParMesh()->bdr_attributes.Max() - : 0; + const auto &mesh = *nd_fespace.GetParMesh(); + int bdr_attr_max = mesh.bdr_attributes.Size() ? mesh.bdr_attributes.Max() : 0; if (!iodata.boundaries.waveport.empty()) { - mfem::Array bdr_attr_marker(bdr_attr_max); + mfem::Array bdr_attr_marker(bdr_attr_max), port_marker(bdr_attr_max); bdr_attr_marker = 0; - for (auto attr : nd_fespace.GetParMesh()->bdr_attributes) + port_marker = 0; + for (auto attr : mesh.bdr_attributes) { bdr_attr_marker[attr - 1] = 1; } @@ -947,6 +1048,9 @@ void WavePortOperator::SetUpBoundaryProperties( "boundaries in the mesh!"); MFEM_VERIFY(bdr_attr_marker[attr - 1], "Unknown port boundary attribute " << attr << "!"); + MFEM_VERIFY(!port_marker[attr - 1], + "Boundary attribute is assigned to more than one wave port!"); + port_marker[attr - 1] = 1; } } } @@ -955,7 +1059,7 @@ void WavePortOperator::SetUpBoundaryProperties( // wave port modes. This includes all PEC surfaces, but may also include others like when // a kinetic inductance or other BC is applied for the 3D simulation but should be // considered as PEC for the 2D problem. - mfem::Array dbc_bcs, dbc_marker; + mfem::Array dbc_bcs; dbc_bcs.Reserve(static_cast(iodata.boundaries.pec.attributes.size() + iodata.boundaries.auxpec.attributes.size())); for (auto attr : iodata.boundaries.pec.attributes) @@ -978,29 +1082,15 @@ void WavePortOperator::SetUpBoundaryProperties( // so allow for duplicates in the attribute list. dbc_bcs.Sort(); dbc_bcs.Unique(); - mesh::AttrToMarker(bdr_attr_max, dbc_bcs, dbc_marker); // Set up wave port data structures. for (const auto &[idx, data] : iodata.boundaries.waveport) { - ports.try_emplace(idx, data, mat_op, nd_fespace, h1_fespace, dbc_marker); + ports.try_emplace(idx, data, mat_op, nd_fespace, h1_fespace, dbc_bcs); } MFEM_VERIFY( ports.empty() || iodata.problem.type == config::ProblemData::Type::DRIVEN, "Wave port boundaries are only available for frequency domain driven simulations!"); - - // Mark selected boundary attributes from the mesh for wave ports. - port_marker.SetSize(bdr_attr_max); - port_marker = 0; - for (const auto &[idx, data] : ports) - { - for (int i = 0; i < data.GetMarker().Size(); i++) - { - MFEM_VERIFY(!(port_marker[i] && data.GetMarker()[i]), - "Boundary attribute is assigned to more than one wave port!"); - port_marker[i] = port_marker[i] || data.GetMarker()[i]; - } - } } void WavePortOperator::PrintBoundaryInfo(const IoData &iodata, const mfem::ParMesh &mesh) @@ -1013,17 +1103,11 @@ void WavePortOperator::PrintBoundaryInfo(const IoData &iodata, const mfem::ParMe Mpi::Print("\nConfiguring Robin impedance BC for wave ports at attributes:\n"); for (const auto &[idx, data] : ports) { - for (int i = 0; i < data.GetMarker().Size(); i++) + for (auto attr : data.GetAttrList()) { - if (!data.GetMarker()[i]) - { - continue; - } - const int attr = i + 1; mfem::Vector normal = mesh::GetSurfaceNormal(mesh, attr); - Mpi::Print( - " {:d}: Index = {:d}, mode = {:d}, d = {:.3e} m", attr, idx, data.GetModeIndex(), - iodata.DimensionalizeValue(IoData::ValueType::LENGTH, data.GetOffsetDistance())); + Mpi::Print(" {:d}: Index = {:d}, mode = {:d}, d = {:.3e} m", attr, idx, data.mode_idx, + iodata.DimensionalizeValue(IoData::ValueType::LENGTH, data.d_offset)); if (mesh.SpaceDimension() == 3) { Mpi::Print(", n = ({:+.1f}, {:+.1f}, {:+.1f})", normal(0), normal(1), normal(2)); @@ -1040,7 +1124,7 @@ void WavePortOperator::PrintBoundaryInfo(const IoData &iodata, const mfem::ParMe bool first = true; for (const auto &[idx, data] : ports) { - if (!data.IsExcited()) + if (!data.excitation) { continue; } @@ -1049,12 +1133,9 @@ void WavePortOperator::PrintBoundaryInfo(const IoData &iodata, const mfem::ParMe Mpi::Print("\nConfiguring wave port excitation source term at attributes:\n"); first = false; } - for (int i = 0; i < data.GetMarker().Size(); i++) + for (auto attr : data.GetAttrList()) { - if (data.GetMarker()[i]) - { - Mpi::Print(" {:d}: Index = {:d}\n", i + 1, idx); - } + Mpi::Print(" {:d}: Index = {:d}\n", attr, idx); } } } @@ -1066,13 +1147,23 @@ const WavePortData &WavePortOperator::GetPort(int idx) const return it->second; } +mfem::Array WavePortOperator::GetAttrList() const +{ + mfem::Array attr_list; + for (const auto &[idx, data] : ports) + { + attr_list.Append(data.GetAttrList()); + } + return attr_list; +} + void WavePortOperator::Initialize(double omega) { bool init = false, first = true; for (const auto &[idx, data] : ports) { - init = init || (data.GetOperatingFrequency() != omega); - first = first && (data.GetOperatingFrequency() == 0.0); + init = init || (data.omega0 != omega); + first = first && (data.omega0 == 0.0); } if (!init) { @@ -1081,10 +1172,9 @@ void WavePortOperator::Initialize(double omega) BlockTimer bt(Timer::WAVEPORT); if (!suppress_output) { - const double freq = iodata.DimensionalizeValue(IoData::ValueType::FREQUENCY, omega); Mpi::Print( - "\nCalculating boundary modes at wave ports for ω/2π = {:.3e} GHz ({:.3e})\n", freq, - omega); + "\nCalculating boundary modes at wave ports for ω/2π = {:.3e} GHz ({:.3e})\n", + omega * fc, omega); } for (auto &[idx, data] : ports) { @@ -1097,30 +1187,32 @@ void WavePortOperator::Initialize(double omega) " H1: {:d}, ND: {:d}\n", idx, data.GlobalTrueH1Size(), data.GlobalTrueNDSize()); } - double k0 = 1.0 / iodata.DimensionalizeValue(IoData::ValueType::LENGTH, 1.0); - Mpi::Print(" Port {:d}, mode {:d}: kₙ = {:.3e}{:+.3e}i m⁻¹\n", idx, - data.GetModeIndex(), k0 * data.GetPropagationConstant().real(), - k0 * data.GetPropagationConstant().imag()); + Mpi::Print(" Port {:d}, mode {:d}: kₙ = {:.3e}{:+.3e}i m⁻¹\n", idx, data.mode_idx, + data.kn0.real() * kc, data.kn0.imag() * kc); } } } void WavePortOperator::AddExtraSystemBdrCoefficients(double omega, - SumMatrixCoefficient &fbr, - SumMatrixCoefficient &fbi) + MaterialPropertyCoefficient &fbr, + MaterialPropertyCoefficient &fbi) { // Add wave port boundaries to the bilinear form. This looks a lot like the lumped port // boundary, except the iω / Z_s coefficient goes to ikₙ / μ where kₙ is specific to the // port mode at the given operating frequency (note only the real part of the propagation // constant contributes). Initialize(omega); - for (auto &[idx, data] : ports) + for (const auto &[idx, data] : ports) { - constexpr auto MatType = MaterialPropertyType::INV_PERMEABILITY; - constexpr auto ElemType = MeshElementType::BDR_ELEMENT; - fbi.AddCoefficient(std::make_unique>( - mat_op, data.GetPropagationConstant().real()), - data.GetMarker()); + const MaterialOperator &mat_op = data.mat_op; + MaterialPropertyCoefficient muinv_func(mat_op.GetBdrAttributeToMaterial(), + mat_op.GetInvPermeability()); + muinv_func.RestrictCoefficient(mat_op.GetBdrAttributeGlobalToLocal(data.GetAttrList())); + // fbr.AddCoefficient(muinv_func.GetAttributeToMaterial(), + // muinv_func.GetMaterialProperties(), + // -data.kn0.imag()); + fbi.AddCoefficient(muinv_func.GetAttributeToMaterial(), + muinv_func.GetMaterialProperties(), data.kn0.real()); } } @@ -1131,18 +1223,14 @@ void WavePortOperator::AddExcitationBdrCoefficients(double omega, SumVectorCoeff // modal solution (stored as a grid function and coefficient during initialization). // Likewise for the imaginary part. Initialize(omega); - for (auto &[idx, data] : ports) + for (const auto &[idx, data] : ports) { - if (!data.IsExcited()) + if (!data.excitation) { continue; } - fbr.AddCoefficient(std::make_unique( - 2.0 * omega, data.GetModeCoefficientImag()), - data.GetMarker()); - fbi.AddCoefficient(std::make_unique( - -2.0 * omega, data.GetModeCoefficientReal()), - data.GetMarker()); + fbr.AddCoefficient(data.GetModeExcitationCoefficientImag(), 2.0 * omega); + fbi.AddCoefficient(data.GetModeExcitationCoefficientReal(), -2.0 * omega); } } diff --git a/palace/models/waveportoperator.hpp b/palace/models/waveportoperator.hpp index 4bb368b5a..06fca08cd 100644 --- a/palace/models/waveportoperator.hpp +++ b/palace/models/waveportoperator.hpp @@ -7,7 +7,9 @@ #include #include #include +#include #include +#include "fem/fespace.hpp" #include "linalg/eps.hpp" #include "linalg/ksp.hpp" #include "linalg/operator.hpp" @@ -18,7 +20,7 @@ namespace palace class IoData; class MaterialOperator; -class SumMatrixCoefficient; +class MaterialPropertyCoefficient; class SumVectorCoefficient; namespace config @@ -33,27 +35,32 @@ struct WavePortData; // class WavePortData { -private: - bool excitation; +public: + // Reference to material property data (not owned). + const MaterialOperator &mat_op; + + // Wave port properties. int mode_idx; double d_offset; + bool excitation; + std::complex kn0; + double omega0; - // Attribute list and marker for all boundary attributes making up this port boundary. - // Mutable because some MFEM API calls are not const correct. - mfem::Array attr_list; - mutable mfem::Array attr_marker; - +private: // SubMesh data structures to define finite element spaces and grid functions on the // SubMesh corresponding to this port boundary. std::unique_ptr port_mesh; std::unique_ptr port_nd_fec, port_h1_fec; - std::unique_ptr port_nd_fespace, port_h1_fespace; + std::unique_ptr port_nd_fespace, port_h1_fespace; std::unique_ptr port_nd_transfer, port_h1_transfer; + std::unordered_map submesh_parent_elems; + + // List of all boundary attributes making up this port boundary. + mfem::Array attr_list; // Operator storage for repeated boundary mode eigenvalue problem solves. double mu_eps_max; - std::unique_ptr A2r, A2i, B3; - std::unique_ptr A, B, P; + std::unique_ptr A1, A2r, A2i, B1r, B1i, B3; ComplexVector v0, e0, e0t, e0n; // Eigenvalue solver for boundary modes. @@ -62,42 +69,32 @@ class WavePortData std::unique_ptr eigen; std::unique_ptr ksp; - // Grid functions storing the last computed electric field mode on the port and the - // associated propagation constant. Also the coefficient for the incident port mode - // (n x H_inc) computed from the electric field mode. - std::unique_ptr port_E0t, port_E0n; - std::unique_ptr port_nxH0r_func, port_nxH0i_func; - std::unique_ptr port_sr, port_si; + // Stored objects for computing functions of the port modes for use as an excitation or + // in postprocessing. std::unique_ptr port_S0t; - std::complex kn0; - double omega0; + std::unique_ptr port_sr, port_si; + + // Grid functions storing the last computed electric field mode on the port. + std::unique_ptr port_E0t, port_E0n; public: WavePortData(const config::WavePortData &data, const MaterialOperator &mat_op, - const mfem::ParFiniteElementSpace &nd_fespace, - const mfem::ParFiniteElementSpace &h1_fespace, - const mfem::Array &dbc_marker); + mfem::ParFiniteElementSpace &nd_fespace, + mfem::ParFiniteElementSpace &h1_fespace, const mfem::Array &dbc_attr); ~WavePortData(); - const mfem::Array &GetMarker() const { return attr_marker; } - mfem::Array &GetMarker() { return attr_marker; } + const auto &GetAttrList() const { return attr_list; } void Initialize(double omega); HYPRE_BigInt GlobalTrueNDSize() const { return port_nd_fespace->GlobalTrueVSize(); } HYPRE_BigInt GlobalTrueH1Size() const { return port_h1_fespace->GlobalTrueVSize(); } - std::complex GetPropagationConstant() const { return kn0; } - double GetOperatingFrequency() const { return omega0; } + std::unique_ptr GetModeExcitationCoefficientReal() const; + std::unique_ptr GetModeExcitationCoefficientImag() const; - bool IsExcited() const { return excitation; } - int GetModeIndex() const { return mode_idx; } - double GetOffsetDistance() const { return d_offset; } - - const mfem::VectorCoefficient &GetModeCoefficientReal() const { return *port_nxH0r_func; } - mfem::VectorCoefficient &GetModeCoefficientReal() { return *port_nxH0r_func; } - const mfem::VectorCoefficient &GetModeCoefficientImag() const { return *port_nxH0i_func; } - mfem::VectorCoefficient &GetModeCoefficientImag() { return *port_nxH0i_func; } + std::unique_ptr GetModeFieldCoefficientReal() const; + std::unique_ptr GetModeFieldCoefficientImag() const; std::complex GetCharacteristicImpedance() const { @@ -114,8 +111,7 @@ class WavePortData std::complex GetSParameter(mfem::ParComplexGridFunction &E) const; std::complex GetPower(mfem::ParComplexGridFunction &E, - mfem::ParComplexGridFunction &B, - const MaterialOperator &mat_op) const; + mfem::ParComplexGridFunction &B) const; std::complex GetVoltage(mfem::ParComplexGridFunction &E) const { MFEM_ABORT("GetVoltage is not yet implemented for wave port boundaries!"); @@ -129,28 +125,25 @@ class WavePortData class WavePortOperator { private: - // References to configuration file and material property data (not owned). - const IoData &iodata; - const MaterialOperator &mat_op; + // Mapping from port index to data structure containing port information. + std::map ports; // Flag which forces no printing during WavePortData::Print(). bool suppress_output; + double fc, kc; - // Mapping from port index to data structure containing port information. - std::map ports; - mfem::Array port_marker; void SetUpBoundaryProperties(const IoData &iodata, const MaterialOperator &mat_op, - const mfem::ParFiniteElementSpace &nd_fespace, - const mfem::ParFiniteElementSpace &h1_fespace); + mfem::ParFiniteElementSpace &nd_fespace, + mfem::ParFiniteElementSpace &h1_fespace); void PrintBoundaryInfo(const IoData &iodata, const mfem::ParMesh &mesh); // Compute boundary modes for all wave port boundaries at the specified frequency. void Initialize(double omega); public: - WavePortOperator(const IoData &iod, const MaterialOperator &mat, - const mfem::ParFiniteElementSpace &nd_fespace, - const mfem::ParFiniteElementSpace &h1_fespace); + WavePortOperator(const IoData &iodata, const MaterialOperator &mat_op, + mfem::ParFiniteElementSpace &nd_fespace, + mfem::ParFiniteElementSpace &h1_fespace); // Access data structures for the wave port with the given index. const WavePortData &GetPort(int idx) const; @@ -163,12 +156,12 @@ class WavePortOperator // Enable or suppress all outputs (log printing and fields to disk). void SetSuppressOutput(bool suppress) { suppress_output = suppress; } - // Returns array marking wave port attributes. - const mfem::Array &GetMarker() const { return port_marker; } + // Returns array of wave port attributes. + mfem::Array GetAttrList() const; // Add contributions to system matrix from wave ports. - void AddExtraSystemBdrCoefficients(double omega, SumMatrixCoefficient &fbr, - SumMatrixCoefficient &fbi); + void AddExtraSystemBdrCoefficients(double omega, MaterialPropertyCoefficient &fbr, + MaterialPropertyCoefficient &fbi); // Add contributions to the right-hand side source term vector for an incident field at // excited port boundaries. From e40cac22f3265caaac57e9dfdc562871aa33fe3c Mon Sep 17 00:00:00 2001 From: Sebastian Grimberg Date: Thu, 14 Dec 2023 19:26:58 -0800 Subject: [PATCH 04/32] Finalize migration to new coefficient data structure --- palace/fem/multigrid.hpp | 42 ++-- palace/linalg/divfree.cpp | 15 +- palace/linalg/divfree.hpp | 4 +- palace/linalg/errorestimator.cpp | 30 ++- palace/linalg/errorestimator.hpp | 18 +- palace/linalg/hcurl.cpp | 30 +-- palace/linalg/hcurl.hpp | 4 +- palace/models/curlcurloperator.cpp | 105 +++++----- palace/models/curlcurloperator.hpp | 13 +- palace/models/laplaceoperator.cpp | 100 +++++----- palace/models/laplaceoperator.hpp | 10 +- palace/models/spaceoperator.cpp | 306 +++++++++++++---------------- palace/models/spaceoperator.hpp | 40 ++-- 13 files changed, 353 insertions(+), 364 deletions(-) diff --git a/palace/fem/multigrid.hpp b/palace/fem/multigrid.hpp index 3b8b59f45..0ea5b6ec0 100644 --- a/palace/fem/multigrid.hpp +++ b/palace/fem/multigrid.hpp @@ -8,6 +8,7 @@ #include #include #include "fem/fespace.hpp" +#include "utils/geodata.hpp" #include "utils/iodata.hpp" namespace palace::fem @@ -77,7 +78,7 @@ template inline FiniteElementSpaceHierarchy ConstructFiniteElementSpaceHierarchy( int mg_max_levels, const std::vector> &mesh, const std::vector> &fecs, - const mfem::Array *dbc_marker = nullptr, + const mfem::Array *dbc_attr = nullptr, std::vector> *dbc_tdof_lists = nullptr) { MFEM_VERIFY(!mesh.empty() && !fecs.empty() && @@ -87,9 +88,15 @@ inline FiniteElementSpaceHierarchy ConstructFiniteElementSpaceHierarchy( std::max(1, mg_max_levels)); FiniteElementSpaceHierarchy fespaces( std::make_unique(mesh[coarse_mesh_l].get(), fecs[0].get())); - if (dbc_marker && dbc_tdof_lists) + + mfem::Array dbc_marker; + if (dbc_attr && dbc_tdof_lists) { - fespaces.GetFinestFESpace().GetEssentialTrueDofs(*dbc_marker, + int bdr_attr_max = mesh[coarse_mesh_l]->bdr_attributes.Size() + ? mesh[coarse_mesh_l]->bdr_attributes.Max() + : 0; + dbc_marker = mesh::AttrToMarker(bdr_attr_max, *dbc_attr); + fespaces.GetFinestFESpace().GetEssentialTrueDofs(dbc_marker, dbc_tdof_lists->emplace_back()); } @@ -97,9 +104,9 @@ inline FiniteElementSpaceHierarchy ConstructFiniteElementSpaceHierarchy( for (std::size_t l = coarse_mesh_l + 1; l < mesh.size(); l++) { fespaces.AddLevel(std::make_unique(mesh[l].get(), fecs[0].get())); - if (dbc_marker && dbc_tdof_lists) + if (dbc_attr && dbc_tdof_lists) { - fespaces.GetFinestFESpace().GetEssentialTrueDofs(*dbc_marker, + fespaces.GetFinestFESpace().GetEssentialTrueDofs(dbc_marker, dbc_tdof_lists->emplace_back()); } } @@ -109,9 +116,9 @@ inline FiniteElementSpaceHierarchy ConstructFiniteElementSpaceHierarchy( { fespaces.AddLevel( std::make_unique(mesh.back().get(), fecs[l].get())); - if (dbc_marker && dbc_tdof_lists) + if (dbc_attr && dbc_tdof_lists) { - fespaces.GetFinestFESpace().GetEssentialTrueDofs(*dbc_marker, + fespaces.GetFinestFESpace().GetEssentialTrueDofs(dbc_marker, dbc_tdof_lists->emplace_back()); } } @@ -124,9 +131,9 @@ inline FiniteElementSpaceHierarchy ConstructFiniteElementSpaceHierarchy( // of the provided finite element space objects. template inline AuxiliaryFiniteElementSpaceHierarchy ConstructAuxiliaryFiniteElementSpaceHierarchy( - const FiniteElementSpaceHierarchy &primal_fespaces, + FiniteElementSpaceHierarchy &primal_fespaces, const std::vector> &fecs, - const mfem::Array *dbc_marker = nullptr, + const mfem::Array *dbc_attr = nullptr, std::vector> *dbc_tdof_lists = nullptr) { MFEM_VERIFY((primal_fespaces.GetNumLevels() > 0) && !fecs.empty() && @@ -136,9 +143,13 @@ inline AuxiliaryFiniteElementSpaceHierarchy ConstructAuxiliaryFiniteElementSpace AuxiliaryFiniteElementSpaceHierarchy fespaces( std::make_unique(primal_fespaces.GetFESpaceAtLevel(0), mesh, fecs[0].get())); - if (dbc_marker && dbc_tdof_lists) + + mfem::Array dbc_marker; + if (dbc_attr && dbc_tdof_lists) { - fespaces.GetFinestFESpace().GetEssentialTrueDofs(*dbc_marker, + int bdr_attr_max = mesh->bdr_attributes.Size() ? mesh->bdr_attributes.Max() : 0; + dbc_marker = mesh::AttrToMarker(bdr_attr_max, *dbc_attr); + fespaces.GetFinestFESpace().GetEssentialTrueDofs(dbc_marker, dbc_tdof_lists->emplace_back()); } @@ -153,11 +164,12 @@ inline AuxiliaryFiniteElementSpaceHierarchy ConstructAuxiliaryFiniteElementSpace fespaces.AddLevel(std::make_unique( primal_fespaces.GetFESpaceAtLevel(l), primal_fespaces.GetFESpaceAtLevel(l).GetParMesh(), fecs[0].get())); - if (dbc_marker && dbc_tdof_lists) + if (dbc_attr && dbc_tdof_lists) { - fespaces.GetFinestFESpace().GetEssentialTrueDofs(*dbc_marker, + fespaces.GetFinestFESpace().GetEssentialTrueDofs(dbc_marker, dbc_tdof_lists->emplace_back()); } + mesh = primal_fespaces.GetFESpaceAtLevel(l).GetParMesh(); } @@ -167,9 +179,9 @@ inline AuxiliaryFiniteElementSpaceHierarchy ConstructAuxiliaryFiniteElementSpace { fespaces.AddLevel(std::make_unique( primal_fespaces.GetFESpaceAtLevel(l), mesh, fecs[l - l0].get())); - if (dbc_marker && dbc_tdof_lists) + if (dbc_attr && dbc_tdof_lists) { - fespaces.GetFinestFESpace().GetEssentialTrueDofs(*dbc_marker, + fespaces.GetFinestFESpace().GetEssentialTrueDofs(dbc_marker, dbc_tdof_lists->emplace_back()); } } diff --git a/palace/linalg/divfree.cpp b/palace/linalg/divfree.cpp index dbce20a46..f26682707 100644 --- a/palace/linalg/divfree.cpp +++ b/palace/linalg/divfree.cpp @@ -6,7 +6,6 @@ #include #include #include "fem/bilinearform.hpp" -#include "fem/coefficient.hpp" #include "fem/fespace.hpp" #include "fem/integrator.hpp" #include "linalg/amg.hpp" @@ -18,15 +17,14 @@ namespace palace { -DivFreeSolver::DivFreeSolver(const MaterialOperator &mat_op, - const FiniteElementSpace &nd_fespace, - const AuxiliaryFiniteElementSpaceHierarchy &h1_fespaces, +DivFreeSolver::DivFreeSolver(const MaterialOperator &mat_op, FiniteElementSpace &nd_fespace, + AuxiliaryFiniteElementSpaceHierarchy &h1_fespaces, const std::vector> &h1_bdr_tdof_lists, double tol, int max_it, int print) { constexpr bool skip_zeros = false; - constexpr auto MatType = MaterialPropertyType::PERMITTIVITY_REAL; - MaterialPropertyCoefficient epsilon_func(mat_op); + MaterialPropertyCoefficient epsilon_func(mat_op.GetAttributeToMaterial(), + mat_op.GetPermittivityReal()); { auto M_mg = std::make_unique(h1_fespaces.GetNumLevels()); for (std::size_t l = 0; l < h1_fespaces.GetNumLevels(); l++) @@ -34,7 +32,7 @@ DivFreeSolver::DivFreeSolver(const MaterialOperator &mat_op, // Force coarse level operator to be fully assembled always. const auto &h1_fespace_l = h1_fespaces.GetFESpaceAtLevel(l); BilinearForm m(h1_fespace_l); - m.AddDomainIntegrator(epsilon_func); + m.AddDomainIntegrator((mfem::MatrixCoefficient &)epsilon_func); auto M_l = std::make_unique(m.Assemble(skip_zeros), h1_fespace_l); M_l->SetEssentialTrueDofs(h1_bdr_tdof_lists[l], Operator::DiagonalPolicy::DIAG_ONE); M_mg->AddOperator(std::move(M_l)); @@ -43,7 +41,8 @@ DivFreeSolver::DivFreeSolver(const MaterialOperator &mat_op, } { BilinearForm weakdiv(nd_fespace, h1_fespaces.GetFinestFESpace()); - weakdiv.AddDomainIntegrator(epsilon_func); + weakdiv.AddDomainIntegrator( + (mfem::MatrixCoefficient &)epsilon_func); WeakDiv = std::make_unique(weakdiv.Assemble(skip_zeros), nd_fespace, h1_fespaces.GetFinestFESpace(), false); } diff --git a/palace/linalg/divfree.hpp b/palace/linalg/divfree.hpp index a19d0efdd..167b61d23 100644 --- a/palace/linalg/divfree.hpp +++ b/palace/linalg/divfree.hpp @@ -46,8 +46,8 @@ class DivFreeSolver mutable Vector psi, rhs; public: - DivFreeSolver(const MaterialOperator &mat_op, const FiniteElementSpace &nd_fespace, - const AuxiliaryFiniteElementSpaceHierarchy &h1_fespaces, + DivFreeSolver(const MaterialOperator &mat_op, FiniteElementSpace &nd_fespace, + AuxiliaryFiniteElementSpaceHierarchy &h1_fespaces, const std::vector> &h1_bdr_tdof_lists, double tol, int max_it, int print); diff --git a/palace/linalg/errorestimator.cpp b/palace/linalg/errorestimator.cpp index 0cf5acd64..3c05a4364 100644 --- a/palace/linalg/errorestimator.cpp +++ b/palace/linalg/errorestimator.cpp @@ -5,7 +5,6 @@ #include #include "fem/bilinearform.hpp" -#include "fem/coefficient.hpp" #include "fem/integrator.hpp" #include "linalg/iterative.hpp" #include "linalg/jacobi.hpp" @@ -61,10 +60,11 @@ FluxProjector::FluxProjector(const MaterialOperator &mat_op, BlockTimer bt(Timer::CONSTRUCTESTIMATOR); { // Flux operator is always partially assembled. - constexpr auto MatType = MaterialPropertyType::INV_PERMEABILITY; - MaterialPropertyCoefficient muinv_func(mat_op); + MaterialPropertyCoefficient muinv_func(mat_op.GetAttributeToMaterial(), + mat_op.GetInvPermeability()); BilinearForm flux(nd_fespace); - flux.AddDomainIntegrator(muinv_func); + flux.AddDomainIntegrator( + (mfem::MatrixCoefficient &)muinv_func); Flux = std::make_unique(flux.PartialAssemble(), nd_fespace); } M = GetMassMatrix(nd_fespace); @@ -83,10 +83,10 @@ FluxProjector::FluxProjector(const MaterialOperator &mat_op, BlockTimer bt(Timer::CONSTRUCTESTIMATOR); { // Flux operator is always partially assembled. - constexpr auto MatType = MaterialPropertyType::PERMITTIVITY_REAL; - MaterialPropertyCoefficient epsilon_func(mat_op); + MaterialPropertyCoefficient epsilon_func(mat_op.GetAttributeToMaterial(), + mat_op.GetPermittivityReal()); BilinearForm flux(h1_fespace, h1d_fespace); - flux.AddDomainIntegrator(epsilon_func); + flux.AddDomainIntegrator((mfem::MatrixCoefficient &)epsilon_func); Flux = std::make_unique(flux.PartialAssemble(), h1_fespace, h1d_fespace, false); } @@ -140,13 +140,12 @@ void FluxProjector::Mult(const VecType &x, VecType &y) const } template -CurlFluxErrorEstimator::CurlFluxErrorEstimator( - const MaterialOperator &mat_op, const FiniteElementSpace &nd_fespace, double tol, - int max_it, int print) +CurlFluxErrorEstimator::CurlFluxErrorEstimator(const MaterialOperator &mat_op, + FiniteElementSpace &nd_fespace, + double tol, int max_it, int print) : mat_op(mat_op), nd_fespace(nd_fespace), projector(mat_op, nd_fespace, tol, max_it, print), F(nd_fespace.GetTrueVSize()), - F_gf(const_cast(&nd_fespace)), - U_gf(const_cast(&nd_fespace)) + F_gf(&nd_fespace), U_gf(&nd_fespace) { } @@ -259,15 +258,14 @@ ErrorIndicator CurlFluxErrorEstimator::ComputeIndicators(const VecType } GradFluxErrorEstimator::GradFluxErrorEstimator(const MaterialOperator &mat_op, - const FiniteElementSpace &h1_fespace, - double tol, int max_it, int print) + FiniteElementSpace &h1_fespace, double tol, + int max_it, int print) : mat_op(mat_op), h1_fespace(h1_fespace), h1d_fespace(std::make_unique( h1_fespace.GetParMesh(), h1_fespace.FEColl(), h1_fespace.GetParMesh()->SpaceDimension(), mfem::Ordering::byNODES)), projector(mat_op, h1_fespace, *h1d_fespace, tol, max_it, print), - F(h1d_fespace->GetTrueVSize()), F_gf(h1d_fespace.get()), - U_gf(const_cast(&h1_fespace)) + F(h1d_fespace->GetTrueVSize()), F_gf(h1d_fespace.get()), U_gf(&h1_fespace) { } diff --git a/palace/linalg/errorestimator.hpp b/palace/linalg/errorestimator.hpp index e2705b37e..852829420 100644 --- a/palace/linalg/errorestimator.hpp +++ b/palace/linalg/errorestimator.hpp @@ -56,11 +56,11 @@ class CurlFluxErrorEstimator typename std::conditional::value, mfem::ParComplexGridFunction, mfem::ParGridFunction>::type; - // Reference to input data (not owned). + // Reference to material property data (not owned). const MaterialOperator &mat_op; // Finite element space used to represent U and F. - const FiniteElementSpace &nd_fespace; + FiniteElementSpace &nd_fespace; // Global L2 projection solver. FluxProjector projector; @@ -70,9 +70,8 @@ class CurlFluxErrorEstimator mutable GridFunctionType F_gf, U_gf; public: - CurlFluxErrorEstimator(const MaterialOperator &mat_op, - const FiniteElementSpace &nd_fespace, double tol, int max_it, - int print); + CurlFluxErrorEstimator(const MaterialOperator &mat_op, FiniteElementSpace &nd_fespace, + double tol, int max_it, int print); // Compute elemental error indicators given a vector of true DOF. ErrorIndicator ComputeIndicators(const VecType &U) const; @@ -89,11 +88,11 @@ class CurlFluxErrorEstimator // denotes a smooth reconstruction of ε ∇Uₕ. class GradFluxErrorEstimator { - // Reference to input data (not owned). + // Reference to material property data (not owned). const MaterialOperator &mat_op; // Finite element space used to represent U. - const FiniteElementSpace &h1_fespace; + FiniteElementSpace &h1_fespace; // Vector H1 space used to represent the components of F, ordered by component. std::unique_ptr h1d_fespace; @@ -106,9 +105,8 @@ class GradFluxErrorEstimator mutable mfem::ParGridFunction F_gf, U_gf; public: - GradFluxErrorEstimator(const MaterialOperator &mat_op, - const FiniteElementSpace &h1_fespace, double tol, int max_it, - int print); + GradFluxErrorEstimator(const MaterialOperator &mat_op, FiniteElementSpace &h1_fespace, + double tol, int max_it, int print); // Compute elemental error indicators given a vector of true DOF. ErrorIndicator ComputeIndicators(const Vector &U) const; diff --git a/palace/linalg/hcurl.cpp b/palace/linalg/hcurl.cpp index f8954dc4a..ff7bca0f9 100644 --- a/palace/linalg/hcurl.cpp +++ b/palace/linalg/hcurl.cpp @@ -5,7 +5,6 @@ #include #include "fem/bilinearform.hpp" -#include "fem/coefficient.hpp" #include "fem/fespace.hpp" #include "fem/integrator.hpp" #include "linalg/ams.hpp" @@ -18,21 +17,21 @@ namespace palace { WeightedHCurlNormSolver::WeightedHCurlNormSolver( - const MaterialOperator &mat_op, const FiniteElementSpaceHierarchy &nd_fespaces, - const AuxiliaryFiniteElementSpaceHierarchy &h1_fespaces, + const MaterialOperator &mat_op, FiniteElementSpaceHierarchy &nd_fespaces, + AuxiliaryFiniteElementSpaceHierarchy &h1_fespaces, const std::vector> &nd_dbc_tdof_lists, const std::vector> &h1_dbc_tdof_lists, double tol, int max_it, int print) { - constexpr bool skip_zeros = false; - constexpr auto MatTypeMuInv = MaterialPropertyType::INV_PERMEABILITY; - constexpr auto MatTypeEps = MaterialPropertyType::PERMITTIVITY_REAL; - MaterialPropertyCoefficient muinv_func(mat_op); - MaterialPropertyCoefficient epsilon_func(mat_op); + MFEM_VERIFY(h1_fespaces.GetNumLevels() == nd_fespaces.GetNumLevels(), + "Multigrid hierarchy mismatch for auxiliary space preconditioning!"); + const auto n_levels = nd_fespaces.GetNumLevels(); { - MFEM_VERIFY(h1_fespaces.GetNumLevels() == nd_fespaces.GetNumLevels(), - "Multigrid hierarchy mismatch for auxiliary space preconditioning!"); - const auto n_levels = nd_fespaces.GetNumLevels(); + constexpr bool skip_zeros = false; + MaterialPropertyCoefficient muinv_func(mat_op.GetAttributeToMaterial(), + mat_op.GetInvPermeability()); + MaterialPropertyCoefficient epsilon_func(mat_op.GetAttributeToMaterial(), + mat_op.GetPermittivityReal()); auto A_mg = std::make_unique(n_levels); for (bool aux : {false, true}) { @@ -45,11 +44,14 @@ WeightedHCurlNormSolver::WeightedHCurlNormSolver( BilinearForm a(fespace_l); if (aux) { - a.AddDomainIntegrator(epsilon_func); + a.AddDomainIntegrator( + (mfem::MatrixCoefficient &)epsilon_func); } else { - a.AddDomainIntegrator(muinv_func, epsilon_func); + a.AddDomainIntegrator( + (mfem::MatrixCoefficient &)muinv_func, + (mfem::MatrixCoefficient &)epsilon_func); } auto A_l = std::make_unique(a.Assemble(skip_zeros), fespace_l); A_l->SetEssentialTrueDofs(dbc_tdof_lists_l, Operator::DiagonalPolicy::DIAG_ONE); @@ -72,7 +74,7 @@ WeightedHCurlNormSolver::WeightedHCurlNormSolver( nd_fespaces.GetFESpaceAtLevel(0), h1_fespaces.GetFESpaceAtLevel(0), 1, 1, 1, false, false, 0)); std::unique_ptr> pc; - if (nd_fespaces.GetNumLevels() > 1) + if (n_levels > 1) { const auto G = h1_fespaces.GetDiscreteInterpolators(); const int mg_smooth_order = diff --git a/palace/linalg/hcurl.hpp b/palace/linalg/hcurl.hpp index 43d41b378..53e6dc23e 100644 --- a/palace/linalg/hcurl.hpp +++ b/palace/linalg/hcurl.hpp @@ -39,8 +39,8 @@ class WeightedHCurlNormSolver public: WeightedHCurlNormSolver(const MaterialOperator &mat_op, - const FiniteElementSpaceHierarchy &nd_fespaces, - const AuxiliaryFiniteElementSpaceHierarchy &h1_fespaces, + FiniteElementSpaceHierarchy &nd_fespaces, + AuxiliaryFiniteElementSpaceHierarchy &h1_fespaces, const std::vector> &nd_dbc_tdof_lists, const std::vector> &h1_dbc_tdof_lists, double tol, int max_it, int print); diff --git a/palace/models/curlcurloperator.cpp b/palace/models/curlcurloperator.cpp index 3a72b69a0..bc2d295d1 100644 --- a/palace/models/curlcurloperator.cpp +++ b/palace/models/curlcurloperator.cpp @@ -16,10 +16,42 @@ namespace palace { -namespace +CurlCurlOperator::CurlCurlOperator(const IoData &iodata, + const std::vector> &mesh) + : print_hdr(true), dbc_attr(SetUpBoundaryProperties(iodata, *mesh.back())), + nd_fecs(fem::ConstructFECollections( + iodata.solver.order, mesh.back()->Dimension(), iodata.solver.linear.mg_max_levels, + iodata.solver.linear.mg_coarsen_type, false)), + h1_fecs(fem::ConstructFECollections( + iodata.solver.order, mesh.back()->Dimension(), iodata.solver.linear.mg_max_levels, + iodata.solver.linear.mg_coarsen_type, false)), + rt_fec(std::make_unique(iodata.solver.order - 1, + mesh.back()->Dimension())), + nd_fespaces(fem::ConstructFiniteElementSpaceHierarchy( + iodata.solver.linear.mg_max_levels, mesh, nd_fecs, &dbc_attr, &dbc_tdof_lists)), + h1_fespaces(fem::ConstructAuxiliaryFiniteElementSpaceHierarchy( + nd_fespaces, h1_fecs)), + rt_fespace(nd_fespaces.GetFinestFESpace(), mesh.back().get(), rt_fec.get()), + mat_op(iodata, *mesh.back()), surf_j_op(iodata, GetH1Space()) { + // Finalize setup. + BilinearForm::pa_order_threshold = iodata.solver.pa_order_threshold; + fem::DefaultIntegrationOrder::q_order_jac = iodata.solver.q_order_jac; + fem::DefaultIntegrationOrder::q_order_extra_pk = iodata.solver.q_order_extra; + fem::DefaultIntegrationOrder::q_order_extra_qk = iodata.solver.q_order_extra; + CheckBoundaryProperties(); -mfem::Array SetUpBoundaryProperties(const IoData &iodata, const mfem::ParMesh &mesh) + // Print essential BC information. + if (dbc_attr.Size()) + { + Mpi::Print("\nConfiguring Dirichlet BC at attributes:\n"); + std::sort(dbc_attr.begin(), dbc_attr.end()); + utils::PrettyPrint(dbc_attr); + } +} + +mfem::Array CurlCurlOperator::SetUpBoundaryProperties(const IoData &iodata, + const mfem::ParMesh &mesh) { int bdr_attr_max = mesh.bdr_attributes.Size() ? mesh.bdr_attributes.Max() : 0; if (!iodata.boundaries.pec.empty()) @@ -53,7 +85,7 @@ mfem::Array SetUpBoundaryProperties(const IoData &iodata, const mfem::ParMe } // Mark selected boundary attributes from the mesh as essential (Dirichlet). - mfem::Array dbc_bcs, dbc_marker; + mfem::Array dbc_bcs; dbc_bcs.Reserve(static_cast(iodata.boundaries.pec.attributes.size())); for (auto attr : iodata.boundaries.pec.attributes) { @@ -63,49 +95,16 @@ mfem::Array SetUpBoundaryProperties(const IoData &iodata, const mfem::ParMe } dbc_bcs.Append(attr); } - mesh::AttrToMarker(bdr_attr_max, dbc_bcs, dbc_marker); - return dbc_marker; -} - -} // namespace - -CurlCurlOperator::CurlCurlOperator(const IoData &iodata, - const std::vector> &mesh) - : print_hdr(true), dbc_marker(SetUpBoundaryProperties(iodata, *mesh.back())), - nd_fecs(fem::ConstructFECollections( - iodata.solver.order, mesh.back()->Dimension(), iodata.solver.linear.mg_max_levels, - iodata.solver.linear.mg_coarsen_type, false)), - h1_fecs(fem::ConstructFECollections( - iodata.solver.order, mesh.back()->Dimension(), iodata.solver.linear.mg_max_levels, - iodata.solver.linear.mg_coarsen_type, false)), - rt_fec(std::make_unique(iodata.solver.order - 1, - mesh.back()->Dimension())), - nd_fespaces(fem::ConstructFiniteElementSpaceHierarchy( - iodata.solver.linear.mg_max_levels, mesh, nd_fecs, &dbc_marker, &dbc_tdof_lists)), - h1_fespaces(fem::ConstructAuxiliaryFiniteElementSpaceHierarchy( - nd_fespaces, h1_fecs)), - rt_fespace(nd_fespaces.GetFinestFESpace(), mesh.back().get(), rt_fec.get()), - mat_op(iodata, *mesh.back()), surf_j_op(iodata, GetH1Space()) -{ - // Finalize setup. - BilinearForm::pa_order_threshold = iodata.solver.pa_order_threshold; - fem::DefaultIntegrationOrder::q_order_jac = iodata.solver.q_order_jac; - fem::DefaultIntegrationOrder::q_order_extra_pk = iodata.solver.q_order_extra; - fem::DefaultIntegrationOrder::q_order_extra_qk = iodata.solver.q_order_extra; - CheckBoundaryProperties(); - - // Print essential BC information. - if (dbc_marker.Size() && dbc_marker.Max() > 0) - { - Mpi::Print("\nConfiguring Dirichlet BC at attributes:\n"); - utils::PrettyPrintMarker(dbc_marker); - } + return dbc_bcs; } void CurlCurlOperator::CheckBoundaryProperties() { // A final check that no boundary attribute is assigned multiple boundary conditions. - const auto &surf_j_marker = surf_j_op.GetMarker(); + const mfem::ParMesh &mesh = GetMesh(); + int bdr_attr_max = mesh.bdr_attributes.Size() ? mesh.bdr_attributes.Max() : 0; + const auto dbc_marker = mesh::AttrToMarker(bdr_attr_max, dbc_attr); + const auto surf_j_marker = mesh::AttrToMarker(bdr_attr_max, surf_j_op.GetAttrList()); for (int i = 0; i < dbc_marker.Size(); i++) { MFEM_VERIFY(dbc_marker[i] + surf_j_marker[i] <= 1, @@ -116,8 +115,9 @@ void CurlCurlOperator::CheckBoundaryProperties() namespace { -void PrintHeader(const FiniteElementSpace &h1_fespace, const FiniteElementSpace &nd_fespace, - const FiniteElementSpace &rt_fespace, bool &print_hdr) +void PrintHeader(const mfem::ParFiniteElementSpace &h1_fespace, + const mfem::ParFiniteElementSpace &nd_fespace, + const mfem::ParFiniteElementSpace &rt_fespace, bool &print_hdr) { if (print_hdr) { @@ -131,19 +131,18 @@ void PrintHeader(const FiniteElementSpace &h1_fespace, const FiniteElementSpace ? "Partial" : "Full"); - // Every process is guaranteed to have at least one element, and assumes no variable - // order spaces are used. - mfem::ParMesh &mesh = *nd_fespace.GetParMesh(); + auto &mesh = *nd_fespace.GetParMesh(); const int q_order = fem::DefaultIntegrationOrder::Get( *nd_fespace.GetFE(0), *nd_fespace.GetFE(0), *mesh.GetElementTransformation(0)); - Mpi::Print(" Default integration order: {:d}\n Mesh geometries:\n", q_order); + Mpi::Print(" Mesh geometries:\n"); for (auto geom : mesh::CheckElements(mesh).GetGeomTypes()) { const auto *fe = nd_fespace.FEColl()->FiniteElementForGeometry(geom); MFEM_VERIFY(fe, "MFEM does not support ND spaces on geometry = " << mfem::Geometry::Name[geom] << "!"); - Mpi::Print(" {}: P = {:d}, Q = {:d}\n", mfem::Geometry::Name[geom], fe->GetDof(), - mfem::IntRules.Get(geom, q_order).GetNPoints()); + Mpi::Print(" {}: P = {:d}, Q = {:d} (quadrature order = {:d})\n", + mfem::Geometry::Name[geom], fe->GetDof(), + mfem::IntRules.Get(geom, q_order).GetNPoints(), q_order); } Mpi::Print("\nAssembling multigrid hierarchy:\n"); @@ -166,10 +165,10 @@ std::unique_ptr CurlCurlOperator::GetStiffnessMatrix() nd_fespace_l.GetMaxElementOrder(), nd_fespace_l.GlobalTrueVSize()); } constexpr bool skip_zeros = false; - constexpr auto MatType = MaterialPropertyType::INV_PERMEABILITY; - MaterialPropertyCoefficient muinv_func(mat_op); + MaterialPropertyCoefficient muinv_func(mat_op.GetAttributeToMaterial(), + mat_op.GetInvPermeability()); BilinearForm k(nd_fespace_l); - k.AddDomainIntegrator(muinv_func); + k.AddDomainIntegrator((mfem::MatrixCoefficient &)muinv_func); auto K_l = std::make_unique( (l > 0) ? k.Assemble(skip_zeros) : k.FullAssemble(skip_zeros), nd_fespace_l); if (print_hdr) @@ -198,7 +197,7 @@ void CurlCurlOperator::GetExcitationVector(int idx, Vector &RHS) // Assemble the surface current excitation +J. The SurfaceCurrentOperator assembles -J // (meant for time or frequency domain Maxwell discretization, so we multiply by -1 to // retrieve +J). - SumVectorCoefficient fb(GetNDSpace().GetParMesh()->SpaceDimension()); + SumVectorCoefficient fb(GetMesh().SpaceDimension()); surf_j_op.AddExcitationBdrCoefficients(idx, fb); RHS.SetSize(GetNDSpace().GetTrueVSize()); RHS = 0.0; diff --git a/palace/models/curlcurloperator.hpp b/palace/models/curlcurloperator.hpp index 69a5bff5f..caaa553b2 100644 --- a/palace/models/curlcurloperator.hpp +++ b/palace/models/curlcurloperator.hpp @@ -27,10 +27,9 @@ class CurlCurlOperator // Helper variable for log file printing. bool print_hdr; - // Essential boundary condition markers. - mfem::Array dbc_marker; + // Essential boundary condition attributes. + mfem::Array dbc_attr; std::vector> dbc_tdof_lists; - void CheckBoundaryProperties(); // Objects defining the finite element spaces for the magnetic vector potential // (Nedelec) and magnetic flux density (Raviart-Thomas) on the given mesh. The H1 spaces @@ -48,6 +47,9 @@ class CurlCurlOperator // Operator for source current excitation. SurfaceCurrentOperator surf_j_op; + mfem::Array SetUpBoundaryProperties(const IoData &iodata, const mfem::ParMesh &mesh); + void CheckBoundaryProperties(); + public: CurlCurlOperator(const IoData &iodata, const std::vector> &mesh); @@ -70,8 +72,11 @@ class CurlCurlOperator auto &GetRTSpace() { return rt_fespace; } const auto &GetRTSpace() const { return rt_fespace; } + // Access the underlying mesh object. + const auto &GetMesh() const { return *GetNDSpace().GetParMesh(); } + // Return the number of true (conforming) dofs on the finest ND space. - auto GlobalTrueVSize() { return GetNDSpace().GlobalTrueVSize(); } + auto GlobalTrueVSize() const { return GetNDSpace().GlobalTrueVSize(); } // Construct and return system matrix representing discretized curl-curl operator for // Ampere's law. diff --git a/palace/models/laplaceoperator.cpp b/palace/models/laplaceoperator.cpp index 9e5447080..dcbb9740e 100644 --- a/palace/models/laplaceoperator.cpp +++ b/palace/models/laplaceoperator.cpp @@ -4,7 +4,6 @@ #include "laplaceoperator.hpp" #include "fem/bilinearform.hpp" -#include "fem/coefficient.hpp" #include "fem/integrator.hpp" #include "fem/multigrid.hpp" #include "linalg/rap.hpp" @@ -16,10 +15,36 @@ namespace palace { -namespace +LaplaceOperator::LaplaceOperator(const IoData &iodata, + const std::vector> &mesh) + : print_hdr(true), dbc_attr(SetUpBoundaryProperties(iodata, *mesh.back())), + h1_fecs(fem::ConstructFECollections( + iodata.solver.order, mesh.back()->Dimension(), iodata.solver.linear.mg_max_levels, + iodata.solver.linear.mg_coarsen_type, false)), + nd_fec(std::make_unique(iodata.solver.order, + mesh.back()->Dimension())), + h1_fespaces(fem::ConstructFiniteElementSpaceHierarchy( + iodata.solver.linear.mg_max_levels, mesh, h1_fecs, &dbc_attr, &dbc_tdof_lists)), + nd_fespace(h1_fespaces.GetFinestFESpace(), mesh.back().get(), nd_fec.get()), + mat_op(iodata, *mesh.back()), source_attr_lists(ConstructSources(iodata)) { + // Finalize setup. + BilinearForm::pa_order_threshold = iodata.solver.pa_order_threshold; + fem::DefaultIntegrationOrder::q_order_jac = iodata.solver.q_order_jac; + fem::DefaultIntegrationOrder::q_order_extra_pk = iodata.solver.q_order_extra; + fem::DefaultIntegrationOrder::q_order_extra_qk = iodata.solver.q_order_extra; + + // Print essential BC information. + if (dbc_attr.Size()) + { + Mpi::Print("\nConfiguring Dirichlet BC at attributes:\n"); + std::sort(dbc_attr.begin(), dbc_attr.end()); + utils::PrettyPrint(dbc_attr); + } +} -mfem::Array SetUpBoundaryProperties(const IoData &iodata, const mfem::ParMesh &mesh) +mfem::Array LaplaceOperator::SetUpBoundaryProperties(const IoData &iodata, + const mfem::ParMesh &mesh) { int bdr_attr_max = mesh.bdr_attributes.Size() ? mesh.bdr_attributes.Max() : 0; if (!iodata.boundaries.pec.empty() || !iodata.boundaries.lumpedport.empty()) @@ -68,7 +93,7 @@ mfem::Array SetUpBoundaryProperties(const IoData &iodata, const mfem::ParMe } // Mark selected boundary attributes from the mesh as essential (Dirichlet). - mfem::Array dbc_bcs, dbc_marker; + mfem::Array dbc_bcs; for (auto attr : iodata.boundaries.pec.attributes) { if (attr <= 0 || attr > bdr_attr_max) @@ -89,17 +114,16 @@ mfem::Array SetUpBoundaryProperties(const IoData &iodata, const mfem::ParMe } MFEM_VERIFY(dbc_bcs.Size() > 0, "Electrostatic problem is ill-posed without any Dirichlet boundaries!"); - mesh::AttrToMarker(bdr_attr_max, dbc_bcs, dbc_marker); - return dbc_marker; + return dbc_bcs; } -std::map> ConstructSources(const IoData &iodata) +std::map> LaplaceOperator::ConstructSources(const IoData &iodata) { // Construct mapping from terminal index to list of associated attributes. - std::map> source_attr_lists; + std::map> attr_lists; for (const auto &[idx, data] : iodata.boundaries.lumpedport) { - mfem::Array &attr_list = source_attr_lists[idx]; + mfem::Array &attr_list = attr_lists[idx]; for (const auto &elem : data.elements) { for (auto attr : elem.attributes) @@ -108,43 +132,14 @@ std::map> ConstructSources(const IoData &iodata) } } } - return source_attr_lists; -} - -} // namespace - -LaplaceOperator::LaplaceOperator(const IoData &iodata, - const std::vector> &mesh) - : print_hdr(true), dbc_marker(SetUpBoundaryProperties(iodata, *mesh.back())), - h1_fecs(fem::ConstructFECollections( - iodata.solver.order, mesh.back()->Dimension(), iodata.solver.linear.mg_max_levels, - iodata.solver.linear.mg_coarsen_type, false)), - nd_fec(std::make_unique(iodata.solver.order, - mesh.back()->Dimension())), - h1_fespaces(fem::ConstructFiniteElementSpaceHierarchy( - iodata.solver.linear.mg_max_levels, mesh, h1_fecs, &dbc_marker, &dbc_tdof_lists)), - nd_fespace(h1_fespaces.GetFinestFESpace(), mesh.back().get(), nd_fec.get()), - mat_op(iodata, *mesh.back()), source_attr_lists(ConstructSources(iodata)) -{ - // Finalize setup. - BilinearForm::pa_order_threshold = iodata.solver.pa_order_threshold; - fem::DefaultIntegrationOrder::q_order_jac = iodata.solver.q_order_jac; - fem::DefaultIntegrationOrder::q_order_extra_pk = iodata.solver.q_order_extra; - fem::DefaultIntegrationOrder::q_order_extra_qk = iodata.solver.q_order_extra; - - // Print essential BC information. - if (dbc_marker.Size() && dbc_marker.Max() > 0) - { - Mpi::Print("\nConfiguring Dirichlet BC at attributes:\n"); - utils::PrettyPrintMarker(dbc_marker); - } + return attr_lists; } namespace { -void PrintHeader(const FiniteElementSpace &h1_fespace, const FiniteElementSpace &nd_fespace, - bool &print_hdr) +void PrintHeader(const mfem::ParFiniteElementSpace &h1_fespace, + const mfem::ParFiniteElementSpace &nd_fespace, bool &print_hdr) { if (print_hdr) { @@ -157,19 +152,18 @@ void PrintHeader(const FiniteElementSpace &h1_fespace, const FiniteElementSpace ? "Partial" : "Full"); - // Every process is guaranteed to have at least one element, and assumes no variable - // order spaces are used. - mfem::ParMesh &mesh = *h1_fespace.GetParMesh(); + auto &mesh = *h1_fespace.GetParMesh(); const int q_order = fem::DefaultIntegrationOrder::Get( *h1_fespace.GetFE(0), *h1_fespace.GetFE(0), *mesh.GetElementTransformation(0)); - Mpi::Print(" Default integration order: {:d}\n Mesh geometries:\n", q_order); + Mpi::Print(" Mesh geometries:\n"); for (auto geom : mesh::CheckElements(mesh).GetGeomTypes()) { const auto *fe = h1_fespace.FEColl()->FiniteElementForGeometry(geom); MFEM_VERIFY(fe, "MFEM does not support H1 spaces on geometry = " << mfem::Geometry::Name[geom] << "!"); - Mpi::Print(" {}: P = {:d}, Q = {:d}\n", mfem::Geometry::Name[geom], fe->GetDof(), - mfem::IntRules.Get(geom, q_order).GetNPoints()); + Mpi::Print(" {}: P = {:d}, Q = {:d} (quadrature order = {:d})\n", + mfem::Geometry::Name[geom], fe->GetDof(), + mfem::IntRules.Get(geom, q_order).GetNPoints(), q_order); } Mpi::Print("\nAssembling multigrid hierarchy:\n"); @@ -192,10 +186,10 @@ std::unique_ptr LaplaceOperator::GetStiffnessMatrix() h1_fespace_l.GetMaxElementOrder(), h1_fespace_l.GlobalTrueVSize()); } constexpr bool skip_zeros = false; - constexpr auto MatType = MaterialPropertyType::PERMITTIVITY_REAL; - MaterialPropertyCoefficient epsilon_func(mat_op); + MaterialPropertyCoefficient epsilon_func(mat_op.GetAttributeToMaterial(), + mat_op.GetPermittivityReal()); BilinearForm k(h1_fespace_l); - k.AddDomainIntegrator(epsilon_func); + k.AddDomainIntegrator((mfem::MatrixCoefficient &)epsilon_func); auto K_l = std::make_unique( (l > 0) ? k.Assemble(skip_zeros) : k.FullAssemble(skip_zeros), h1_fespace_l); if (print_hdr) @@ -228,9 +222,9 @@ void LaplaceOperator::GetExcitationVector(int idx, const Operator &K, Vector &X, x = 0.0; // Get a marker of all boundary attributes with the given source surface index. - mfem::Array source_marker; - const mfem::Array &source_list = source_attr_lists[idx]; - mesh::AttrToMarker(dbc_marker.Size(), source_list, source_marker); + const mfem::ParMesh &mesh = GetMesh(); + int bdr_attr_max = mesh.bdr_attributes.Size() ? mesh.bdr_attributes.Max() : 0; + mfem::Array source_marker = mesh::AttrToMarker(bdr_attr_max, source_attr_lists[idx]); mfem::ConstantCoefficient one(1.0); x.ProjectBdrCoefficient(one, source_marker); // Values are only correct on master diff --git a/palace/models/laplaceoperator.hpp b/palace/models/laplaceoperator.hpp index 22c4471a0..54f2eae22 100644 --- a/palace/models/laplaceoperator.hpp +++ b/palace/models/laplaceoperator.hpp @@ -28,7 +28,7 @@ class LaplaceOperator bool print_hdr; // Essential boundary condition markers. - mfem::Array dbc_marker; + mfem::Array dbc_attr; std::vector> dbc_tdof_lists; // Objects defining the finite element spaces for the electrostatic potential (H1) and @@ -44,6 +44,9 @@ class LaplaceOperator // Boundary attributes for each terminal index. std::map> source_attr_lists; + mfem::Array SetUpBoundaryProperties(const IoData &iodata, const mfem::ParMesh &mesh); + std::map> ConstructSources(const IoData &iodata); + public: LaplaceOperator(const IoData &iodata, const std::vector> &mesh); @@ -62,8 +65,11 @@ class LaplaceOperator auto &GetNDSpace() { return nd_fespace; } const auto &GetNDSpace() const { return nd_fespace; } + // Access the underlying mesh object. + const auto &GetMesh() const { return *GetH1Space().GetParMesh(); } + // Return the number of true (conforming) dofs on the finest H1 space. - auto GlobalTrueVSize() { return GetH1Space().GlobalTrueVSize(); } + auto GlobalTrueVSize() const { return GetH1Space().GlobalTrueVSize(); } // Construct and return system matrix representing discretized Laplace operator for // Gauss's law. diff --git a/palace/models/spaceoperator.cpp b/palace/models/spaceoperator.cpp index f5686cb03..32ea3a76e 100644 --- a/palace/models/spaceoperator.cpp +++ b/palace/models/spaceoperator.cpp @@ -19,10 +19,48 @@ namespace palace using namespace std::complex_literals; -namespace +SpaceOperator::SpaceOperator(const IoData &iodata, + const std::vector> &mesh) + : pc_mat_real(iodata.solver.linear.pc_mat_real), + pc_mat_shifted(iodata.solver.linear.pc_mat_shifted), print_hdr(true), + print_prec_hdr(true), dbc_attr(SetUpBoundaryProperties(iodata, *mesh.back())), + nd_fecs(fem::ConstructFECollections( + iodata.solver.order, mesh.back()->Dimension(), iodata.solver.linear.mg_max_levels, + iodata.solver.linear.mg_coarsen_type, false)), + h1_fecs(fem::ConstructFECollections( + iodata.solver.order, mesh.back()->Dimension(), iodata.solver.linear.mg_max_levels, + iodata.solver.linear.mg_coarsen_type, false)), + rt_fec(std::make_unique(iodata.solver.order - 1, + mesh.back()->Dimension())), + nd_fespaces(fem::ConstructFiniteElementSpaceHierarchy( + iodata.solver.linear.mg_max_levels, mesh, nd_fecs, &dbc_attr, &nd_dbc_tdof_lists)), + h1_fespaces(fem::ConstructAuxiliaryFiniteElementSpaceHierarchy( + nd_fespaces, h1_fecs, &dbc_attr, &h1_dbc_tdof_lists)), + rt_fespace(nd_fespaces.GetFinestFESpace(), mesh.back().get(), rt_fec.get()), + mat_op(iodata, *mesh.back()), farfield_op(iodata, mat_op, *mesh.back()), + surf_sigma_op(iodata, mat_op, *mesh.back()), surf_z_op(iodata, mat_op, *mesh.back()), + lumped_port_op(iodata, mat_op, GetH1Space()), + wave_port_op(iodata, mat_op, GetNDSpace(), GetH1Space()), + surf_j_op(iodata, GetH1Space()) { + // Finalize setup. + BilinearForm::pa_order_threshold = iodata.solver.pa_order_threshold; + fem::DefaultIntegrationOrder::q_order_jac = iodata.solver.q_order_jac; + fem::DefaultIntegrationOrder::q_order_extra_pk = iodata.solver.q_order_extra; + fem::DefaultIntegrationOrder::q_order_extra_qk = iodata.solver.q_order_extra; + CheckBoundaryProperties(); + + // Print essential BC information. + if (dbc_attr.Size()) + { + Mpi::Print("\nConfiguring Dirichlet PEC BC at attributes:\n"); + std::sort(dbc_attr.begin(), dbc_attr.end()); + utils::PrettyPrint(dbc_attr); + } +} -mfem::Array SetUpBoundaryProperties(const IoData &iodata, const mfem::ParMesh &mesh) +mfem::Array SpaceOperator::SetUpBoundaryProperties(const IoData &iodata, + const mfem::ParMesh &mesh) { int bdr_attr_max = mesh.bdr_attributes.Size() ? mesh.bdr_attributes.Max() : 0; if (!iodata.boundaries.pec.empty()) @@ -56,7 +94,7 @@ mfem::Array SetUpBoundaryProperties(const IoData &iodata, const mfem::ParMe } // Mark selected boundary attributes from the mesh as essential (Dirichlet). - mfem::Array dbc_bcs, dbc_marker; + mfem::Array dbc_bcs; dbc_bcs.Reserve(static_cast(iodata.boundaries.pec.attributes.size())); for (auto attr : iodata.boundaries.pec.attributes) { @@ -66,70 +104,38 @@ mfem::Array SetUpBoundaryProperties(const IoData &iodata, const mfem::ParMe } dbc_bcs.Append(attr); } - mesh::AttrToMarker(bdr_attr_max, dbc_bcs, dbc_marker); - return dbc_marker; -} - -} // namespace - -SpaceOperator::SpaceOperator(const IoData &iodata, - const std::vector> &mesh) - : pc_mat_real(iodata.solver.linear.pc_mat_real), - pc_mat_shifted(iodata.solver.linear.pc_mat_shifted), print_hdr(true), - print_prec_hdr(true), dbc_marker(SetUpBoundaryProperties(iodata, *mesh.back())), - nd_fecs(fem::ConstructFECollections( - iodata.solver.order, mesh.back()->Dimension(), iodata.solver.linear.mg_max_levels, - iodata.solver.linear.mg_coarsen_type, false)), - h1_fecs(fem::ConstructFECollections( - iodata.solver.order, mesh.back()->Dimension(), iodata.solver.linear.mg_max_levels, - iodata.solver.linear.mg_coarsen_type, false)), - rt_fec(std::make_unique(iodata.solver.order - 1, - mesh.back()->Dimension())), - nd_fespaces(fem::ConstructFiniteElementSpaceHierarchy( - iodata.solver.linear.mg_max_levels, mesh, nd_fecs, &dbc_marker, - &nd_dbc_tdof_lists)), - h1_fespaces(fem::ConstructAuxiliaryFiniteElementSpaceHierarchy( - nd_fespaces, h1_fecs, &dbc_marker, &h1_dbc_tdof_lists)), - rt_fespace(nd_fespaces.GetFinestFESpace(), mesh.back().get(), rt_fec.get()), - mat_op(iodata, *mesh.back()), farfield_op(iodata, mat_op, *mesh.back()), - surf_sigma_op(iodata, *mesh.back()), surf_z_op(iodata, *mesh.back()), - lumped_port_op(iodata, GetH1Space()), - wave_port_op(iodata, mat_op, GetNDSpace(), GetH1Space()), - surf_j_op(iodata, GetH1Space()) -{ - // Finalize setup. - BilinearForm::pa_order_threshold = iodata.solver.pa_order_threshold; - fem::DefaultIntegrationOrder::q_order_jac = iodata.solver.q_order_jac; - fem::DefaultIntegrationOrder::q_order_extra_pk = iodata.solver.q_order_extra; - fem::DefaultIntegrationOrder::q_order_extra_qk = iodata.solver.q_order_extra; - CheckBoundaryProperties(); - - // Print essential BC information. - if (dbc_marker.Size() && dbc_marker.Max() > 0) - { - Mpi::Print("\nConfiguring Dirichlet PEC BC at attributes:\n"); - utils::PrettyPrintMarker(dbc_marker); - } + return dbc_bcs; } void SpaceOperator::CheckBoundaryProperties() { // Mark selected boundary attributes from the mesh as having some Dirichlet, Neumann, or // mixed BC applied. - const auto &farfield_marker = farfield_op.GetMarker(); - const auto &surf_sigma_marker = surf_sigma_op.GetMarker(); - const auto &surf_z_Rs_marker = surf_z_op.GetRsMarker(); - const auto &surf_z_Ls_marker = surf_z_op.GetLsMarker(); - const auto &lumped_port_Rs_marker = lumped_port_op.GetRsMarker(); - const auto &lumped_port_Ls_marker = lumped_port_op.GetLsMarker(); - const auto &wave_port_marker = wave_port_op.GetMarker(); - aux_bdr_marker.SetSize(dbc_marker.Size()); + const mfem::ParMesh &mesh = GetMesh(); + int bdr_attr_max = mesh.bdr_attributes.Size() ? mesh.bdr_attributes.Max() : 0; + const auto dbc_marker = mesh::AttrToMarker(bdr_attr_max, dbc_attr); + const auto farfield_marker = mesh::AttrToMarker(bdr_attr_max, farfield_op.GetAttrList()); + const auto surf_sigma_marker = + mesh::AttrToMarker(bdr_attr_max, surf_sigma_op.GetAttrList()); + const auto surf_z_Rs_marker = mesh::AttrToMarker(bdr_attr_max, surf_z_op.GetRsAttrList()); + const auto surf_z_Ls_marker = mesh::AttrToMarker(bdr_attr_max, surf_z_op.GetLsAttrList()); + const auto lumped_port_Rs_marker = + mesh::AttrToMarker(bdr_attr_max, lumped_port_op.GetRsAttrList()); + const auto lumped_port_Ls_marker = + mesh::AttrToMarker(bdr_attr_max, lumped_port_op.GetLsAttrList()); + const auto wave_port_marker = + mesh::AttrToMarker(bdr_attr_max, wave_port_op.GetAttrList()); + mfem::Array aux_bdr_marker(dbc_marker.Size()); for (int i = 0; i < dbc_marker.Size(); i++) { aux_bdr_marker[i] = (dbc_marker[i] || farfield_marker[i] || surf_sigma_marker[i] || surf_z_Rs_marker[i] || surf_z_Ls_marker[i] || lumped_port_Rs_marker[i] || lumped_port_Ls_marker[i] || wave_port_marker[i]); + if (aux_bdr_marker[i]) + { + aux_bdr_attr.Append(i + 1); + } } // aux_bdr_marker = 1; // Mark all boundaries (including material interfaces // // added during mesh preprocessing) @@ -140,45 +146,27 @@ void SpaceOperator::CheckBoundaryProperties() aux_bdr_marker, aux_bdr_tdof_lists.emplace_back()); } - // A final check that no boundary attribute is assigned multiple boundary conditions. The - // one exception is that a lumped port boundary attribute can be also be assigned some - // other condition, in which case the fact that it is a port is just used for - // postprocessing. - const auto &surf_z_marker = surf_z_op.GetMarker(); - const auto &lumped_port_marker = lumped_port_op.GetMarker(); - const auto &surf_j_marker = surf_j_op.GetMarker(); - bool first = true; + // A final check that no boundary attribute is assigned multiple boundary conditions. + const auto surf_z_marker = mesh::AttrToMarker(bdr_attr_max, surf_z_op.GetAttrList()); + const auto lumped_port_marker = + mesh::AttrToMarker(bdr_attr_max, lumped_port_op.GetAttrList()); + const auto surf_j_marker = mesh::AttrToMarker(bdr_attr_max, surf_j_op.GetAttrList()); for (int i = 0; i < dbc_marker.Size(); i++) { - if (lumped_port_marker[i]) - { - if (dbc_marker[i]) - { - if (first) - { - Mpi::Print("\n"); - first = false; - } - Mpi::Warning("Lumped port boundary {:d} also marked as PEC!\nBoundary " - "condition/excitation will be ignored!\n", - i + 1); - } - } - else - { - MFEM_VERIFY(dbc_marker[i] + farfield_marker[i] + surf_sigma_marker[i] + - surf_z_marker[i] + wave_port_marker[i] + surf_j_marker[i] <= - 1, - "Boundary attributes should not be specified with multiple BC!"); - } + MFEM_VERIFY(dbc_marker[i] + farfield_marker[i] + surf_sigma_marker[i] + + surf_z_marker[i] + lumped_port_marker[i] + wave_port_marker[i] + + surf_j_marker[i] <= + 1, + "Boundary attributes should not be specified with multiple BC!"); } } namespace { -void PrintHeader(const FiniteElementSpace &h1_fespace, const FiniteElementSpace &nd_fespace, - const FiniteElementSpace &rt_fespace, bool &print_hdr) +void PrintHeader(const mfem::ParFiniteElementSpace &h1_fespace, + const mfem::ParFiniteElementSpace &nd_fespace, + const mfem::ParFiniteElementSpace &rt_fespace, bool &print_hdr) { if (print_hdr) { @@ -192,81 +180,85 @@ void PrintHeader(const FiniteElementSpace &h1_fespace, const FiniteElementSpace ? "Partial" : "Full"); - // Every process is guaranteed to have at least one element, and assumes no variable - // order spaces are used. - mfem::ParMesh &mesh = *nd_fespace.GetParMesh(); + auto &mesh = *nd_fespace.GetParMesh(); const int q_order = fem::DefaultIntegrationOrder::Get( *nd_fespace.GetFE(0), *nd_fespace.GetFE(0), *mesh.GetElementTransformation(0)); - Mpi::Print(" Default integration order: {:d}\n Mesh geometries:\n", q_order); + Mpi::Print(" Mesh geometries:\n"); for (auto geom : mesh::CheckElements(mesh).GetGeomTypes()) { const auto *fe = nd_fespace.FEColl()->FiniteElementForGeometry(geom); MFEM_VERIFY(fe, "MFEM does not support ND spaces on geometry = " << mfem::Geometry::Name[geom] << "!"); - Mpi::Print(" {}: P = {:d}, Q = {:d}\n", mfem::Geometry::Name[geom], fe->GetDof(), - mfem::IntRules.Get(geom, q_order).GetNPoints()); + Mpi::Print(" {}: P = {:d}, Q = {:d} (quadrature order = {:d})\n", + mfem::Geometry::Name[geom], fe->GetDof(), + mfem::IntRules.Get(geom, q_order).GetNPoints(), q_order); } } print_hdr = false; } -template -std::unique_ptr BuildOperator(const FiniteElementSpace &fespace, T1 *df, T2 *f, - T3 *dfb, T4 *fb, std::size_t l, bool skip_zeros) +std::unique_ptr +BuildOperator(const FiniteElementSpace &fespace, const MaterialPropertyCoefficient *df, + const MaterialPropertyCoefficient *f, const MaterialPropertyCoefficient *dfb, + const MaterialPropertyCoefficient *fb, std::size_t l, bool skip_zeros) { BilinearForm a(fespace); if (df && !df->empty() && f && !f->empty()) { - a.AddDomainIntegrator(*df, *f); + a.AddDomainIntegrator((mfem::MatrixCoefficient &)*df, + (mfem::MatrixCoefficient &)*f); } else { if (df && !df->empty()) { - a.AddDomainIntegrator(*df); + a.AddDomainIntegrator((mfem::MatrixCoefficient &)*df); } if (f && !f->empty()) { - a.AddDomainIntegrator(*f); + a.AddDomainIntegrator((mfem::MatrixCoefficient &)*f); } } if (dfb && !dfb->empty() && fb && !fb->empty()) { - a.AddBoundaryIntegrator(*dfb, *fb); + a.AddBoundaryIntegrator((mfem::Coefficient &)*dfb, + (mfem::MatrixCoefficient &)*fb); } else { if (dfb && !dfb->empty()) { - a.AddBoundaryIntegrator(*dfb); + a.AddBoundaryIntegrator((mfem::Coefficient &)*dfb); } if (fb && !fb->empty()) { - a.AddBoundaryIntegrator(*fb); + a.AddBoundaryIntegrator((mfem::MatrixCoefficient &)*fb); } } return (l > 0) ? a.Assemble(skip_zeros) : a.FullAssemble(skip_zeros); } -template -std::unique_ptr BuildOperator(const FiniteElementSpace &fespace, T1 *df, T2 *f, - T3 *dfb, T4 *fb, bool skip_zeros) +std::unique_ptr +BuildOperator(const FiniteElementSpace &fespace, const MaterialPropertyCoefficient *df, + const MaterialPropertyCoefficient *f, const MaterialPropertyCoefficient *dfb, + const MaterialPropertyCoefficient *fb, bool skip_zeros) { return BuildOperator(fespace, df, f, dfb, fb, 1, skip_zeros); } -template -std::unique_ptr BuildAuxOperator(const FiniteElementSpace &fespace, T1 *f, T2 *fb, +std::unique_ptr BuildAuxOperator(const FiniteElementSpace &fespace, + const MaterialPropertyCoefficient *f, + const MaterialPropertyCoefficient *fb, std::size_t l, bool skip_zeros) { BilinearForm a(fespace); if (f && !f->empty()) { - a.AddDomainIntegrator(*f); + a.AddDomainIntegrator((mfem::MatrixCoefficient &)*f); } if (fb && !fb->empty()) { - a.AddBoundaryIntegrator(*fb); + a.AddBoundaryIntegrator((mfem::MatrixCoefficient &)*fb); } return (l > 0) ? a.Assemble(skip_zeros) : a.FullAssemble(skip_zeros); } @@ -278,8 +270,7 @@ std::unique_ptr SpaceOperator::GetStiffnessMatrix(Operator::DiagonalPolicy diag_policy) { PrintHeader(GetH1Space(), GetNDSpace(), GetRTSpace(), print_hdr); - const int sdim = GetNDSpace().GetParMesh()->SpaceDimension(); - SumMatrixCoefficient df(sdim), f(sdim), fb(sdim); + MaterialPropertyCoefficient df, f, fb; AddStiffnessCoefficients(1.0, df, f); AddStiffnessBdrCoefficients(1.0, fb); if (df.empty() && f.empty() && fb.empty()) @@ -288,7 +279,7 @@ SpaceOperator::GetStiffnessMatrix(Operator::DiagonalPolicy diag_policy) } constexpr bool skip_zeros = false; - auto k = BuildOperator(GetNDSpace(), &df, &f, (SumCoefficient *)nullptr, &fb, skip_zeros); + auto k = BuildOperator(GetNDSpace(), &df, &f, nullptr, &fb, skip_zeros); if constexpr (std::is_same::value) { auto K = std::make_unique(std::move(k), nullptr, GetNDSpace()); @@ -308,8 +299,7 @@ std::unique_ptr SpaceOperator::GetDampingMatrix(Operator::DiagonalPolicy diag_policy) { PrintHeader(GetH1Space(), GetNDSpace(), GetRTSpace(), print_hdr); - const int sdim = GetNDSpace().GetParMesh()->SpaceDimension(); - SumMatrixCoefficient f(sdim), fb(sdim); + MaterialPropertyCoefficient f, fb; AddDampingCoefficients(1.0, f); AddDampingBdrCoefficients(1.0, fb); if (f.empty() && fb.empty()) @@ -318,8 +308,7 @@ SpaceOperator::GetDampingMatrix(Operator::DiagonalPolicy diag_policy) } constexpr bool skip_zeros = false; - auto c = BuildOperator(GetNDSpace(), (SumCoefficient *)nullptr, &f, - (SumCoefficient *)nullptr, &fb, skip_zeros); + auto c = BuildOperator(GetNDSpace(), nullptr, &f, nullptr, &fb, skip_zeros); if constexpr (std::is_same::value) { auto C = std::make_unique(std::move(c), nullptr, GetNDSpace()); @@ -338,15 +327,14 @@ template std::unique_ptr SpaceOperator::GetMassMatrix(Operator::DiagonalPolicy diag_policy) { PrintHeader(GetH1Space(), GetNDSpace(), GetRTSpace(), print_hdr); - const int sdim = GetNDSpace().GetParMesh()->SpaceDimension(); - SumMatrixCoefficient fr(sdim), fi(sdim), fbr(sdim); + MaterialPropertyCoefficient fr, fi, fbr, fbi; AddRealMassCoefficients(1.0, fr); AddRealMassBdrCoefficients(1.0, fbr); if constexpr (std::is_same::value) { AddImagMassCoefficients(1.0, fi); } - if (fr.empty() && fbr.empty() && fi.empty()) + if (fr.empty() && fi.empty() && fbr.empty() && fbi.empty()) { return {}; } @@ -355,13 +343,11 @@ std::unique_ptr SpaceOperator::GetMassMatrix(Operator::DiagonalPolicy std::unique_ptr mr, mi; if (!fr.empty() || !fbr.empty()) { - mr = BuildOperator(GetNDSpace(), (SumCoefficient *)nullptr, &fr, - (SumCoefficient *)nullptr, &fbr, skip_zeros); + mr = BuildOperator(GetNDSpace(), nullptr, &fr, nullptr, &fbr, skip_zeros); } - if (!fi.empty()) + if (!fi.empty() || !fbi.empty()) { - mi = BuildOperator(GetNDSpace(), (SumCoefficient *)nullptr, &fi, - (SumCoefficient *)nullptr, (SumCoefficient *)nullptr, skip_zeros); + mi = BuildOperator(GetNDSpace(), nullptr, &fi, nullptr, &fbi, skip_zeros); } if constexpr (std::is_same::value) { @@ -383,9 +369,7 @@ std::unique_ptr SpaceOperator::GetExtraSystemMatrix(double omega, Operator::DiagonalPolicy diag_policy) { PrintHeader(GetH1Space(), GetNDSpace(), GetRTSpace(), print_hdr); - const int sdim = GetNDSpace().GetParMesh()->SpaceDimension(); - SumMatrixCoefficient fbr(sdim), fbi(sdim); - SumCoefficient dfbr, dfbi; + MaterialPropertyCoefficient dfbr, dfbi, fbr, fbi; AddExtraSystemBdrCoefficients(omega, dfbr, dfbi, fbr, fbi); if (dfbr.empty() && fbr.empty() && dfbi.empty() && fbi.empty()) { @@ -396,13 +380,11 @@ SpaceOperator::GetExtraSystemMatrix(double omega, Operator::DiagonalPolicy diag_ std::unique_ptr ar, ai; if (!dfbr.empty() || !fbr.empty()) { - ar = BuildOperator(GetNDSpace(), (SumCoefficient *)nullptr, (SumCoefficient *)nullptr, - &dfbr, &fbr, skip_zeros); + ar = BuildOperator(GetNDSpace(), nullptr, nullptr, &dfbr, &fbr, skip_zeros); } if (!dfbi.empty() || !fbi.empty()) { - ai = BuildOperator(GetNDSpace(), (SumCoefficient *)nullptr, (SumCoefficient *)nullptr, - &dfbi, &fbi, skip_zeros); + ai = BuildOperator(GetNDSpace(), nullptr, nullptr, &dfbi, &fbi, skip_zeros); } if constexpr (std::is_same::value) { @@ -681,9 +663,7 @@ std::unique_ptr SpaceOperator::GetPreconditionerMatrix(double a0, doub Mpi::Print(" Level {:d}{} (p = {:d}): {:d} unknowns", l, aux ? " (auxiliary)" : "", fespace_l.GetMaxElementOrder(), fespace_l.GlobalTrueVSize()); } - const int sdim = GetNDSpace().GetParMesh()->SpaceDimension(); - SumMatrixCoefficient dfr(sdim), fr(sdim), fi(sdim), fbr(sdim), fbi(sdim); - SumCoefficient dfbr, dfbi; + MaterialPropertyCoefficient dfr, fr, dfi, fi, dfbr, dfbi, fbr, fbi; if (!std::is_same::value || pc_mat_real || l == 0) { // Real-valued system matrix (approximation) for preconditioning. @@ -715,11 +695,10 @@ std::unique_ptr SpaceOperator::GetPreconditionerMatrix(double a0, doub br = aux ? BuildAuxOperator(fespace_l, &fr, &fbr, l, skip_zeros) : BuildOperator(fespace_l, &dfr, &fr, &dfbr, &fbr, l, skip_zeros); } - if (!fi.empty() || !dfbi.empty() || !fbi.empty()) + if (!dfi.empty() || !fi.empty() || !dfbi.empty() || !fbi.empty()) { bi = aux ? BuildAuxOperator(fespace_l, &fi, &fbi, l, skip_zeros) - : BuildOperator(fespace_l, (SumCoefficient *)nullptr, &fi, &dfbi, &fbi, l, - skip_zeros); + : BuildOperator(fespace_l, &dfi, &fi, &dfbi, &fbi, l, skip_zeros); } if (print_prec_hdr) { @@ -750,40 +729,37 @@ std::unique_ptr SpaceOperator::GetPreconditionerMatrix(double a0, doub return B; } -void SpaceOperator::AddStiffnessCoefficients(double coef, SumMatrixCoefficient &df, - SumMatrixCoefficient &f) +void SpaceOperator::AddStiffnessCoefficients(double coef, MaterialPropertyCoefficient &df, + MaterialPropertyCoefficient &f) { - constexpr auto MatType = MaterialPropertyType::INV_PERMEABILITY; - df.AddCoefficient(std::make_unique>(mat_op, coef)); + // Contribution from material permeability. + df.AddCoefficient(mat_op.GetAttributeToMaterial(), mat_op.GetInvPermeability(), coef); // Contribution for London superconductors. if (mat_op.HasLondonDepth()) { - constexpr auto MatTypeL = MaterialPropertyType::INV_LONDON_DEPTH; - f.AddCoefficient(std::make_unique>(mat_op, coef), - mat_op.GetLondonDepthMarker()); + df.AddCoefficient(mat_op.GetAttributeToMaterial(), mat_op.GetInvLondonDepth(), coef); } } -void SpaceOperator::AddStiffnessBdrCoefficients(double coef, SumMatrixCoefficient &fb) +void SpaceOperator::AddStiffnessBdrCoefficients(double coef, + MaterialPropertyCoefficient &fb) { // Robin BC contributions due to surface impedance and lumped ports (inductance). surf_z_op.AddStiffnessBdrCoefficients(coef, fb); lumped_port_op.AddStiffnessBdrCoefficients(coef, fb); } -void SpaceOperator::AddDampingCoefficients(double coef, SumMatrixCoefficient &f) +void SpaceOperator::AddDampingCoefficients(double coef, MaterialPropertyCoefficient &f) { // Contribution for domain conductivity. if (mat_op.HasConductivity()) { - constexpr auto MatType = MaterialPropertyType::CONDUCTIVITY; - f.AddCoefficient(std::make_unique>(mat_op, coef), - mat_op.GetConductivityMarker()); + f.AddCoefficient(mat_op.GetAttributeToMaterial(), mat_op.GetConductivity(), coef); } } -void SpaceOperator::AddDampingBdrCoefficients(double coef, SumMatrixCoefficient &fb) +void SpaceOperator::AddDampingBdrCoefficients(double coef, MaterialPropertyCoefficient &fb) { // Robin BC contributions due to surface impedance, lumped ports, and absorbing // boundaries (resistance). @@ -792,40 +768,37 @@ void SpaceOperator::AddDampingBdrCoefficients(double coef, SumMatrixCoefficient lumped_port_op.AddDampingBdrCoefficients(coef, fb); } -void SpaceOperator::AddRealMassCoefficients(double coef, SumMatrixCoefficient &f) +void SpaceOperator::AddRealMassCoefficients(double coef, MaterialPropertyCoefficient &f) { - constexpr auto MatType = MaterialPropertyType::PERMITTIVITY_REAL; - f.AddCoefficient(std::make_unique>(mat_op, coef)); + f.AddCoefficient(mat_op.GetAttributeToMaterial(), mat_op.GetPermittivityReal(), coef); } -void SpaceOperator::AddRealMassBdrCoefficients(double coef, SumMatrixCoefficient &fb) +void SpaceOperator::AddRealMassBdrCoefficients(double coef, MaterialPropertyCoefficient &fb) { // Robin BC contributions due to surface impedance and lumped ports (capacitance). surf_z_op.AddMassBdrCoefficients(coef, fb); lumped_port_op.AddMassBdrCoefficients(coef, fb); } -void SpaceOperator::AddImagMassCoefficients(double coef, SumMatrixCoefficient &f) +void SpaceOperator::AddImagMassCoefficients(double coef, MaterialPropertyCoefficient &f) { // Contribution for loss tangent: ε -> ε * (1 - i tan(δ)). if (mat_op.HasLossTangent()) { - constexpr auto MatType = MaterialPropertyType::PERMITTIVITY_IMAG; - f.AddCoefficient(std::make_unique>(mat_op, coef), - mat_op.GetLossTangentMarker()); + f.AddCoefficient(mat_op.GetAttributeToMaterial(), mat_op.GetPermittivityImag(), coef); } } -void SpaceOperator::AddAbsMassCoefficients(double coef, SumMatrixCoefficient &f) +void SpaceOperator::AddAbsMassCoefficients(double coef, MaterialPropertyCoefficient &f) { - constexpr auto MatType = MaterialPropertyType::PERMITTIVITY_ABS; - f.AddCoefficient(std::make_unique>(mat_op, coef)); + f.AddCoefficient(mat_op.GetAttributeToMaterial(), mat_op.GetPermittivityAbs(), coef); } -void SpaceOperator::AddExtraSystemBdrCoefficients(double omega, SumCoefficient &dfbr, - SumCoefficient &dfbi, - SumMatrixCoefficient &fbr, - SumMatrixCoefficient &fbi) +void SpaceOperator::AddExtraSystemBdrCoefficients(double omega, + MaterialPropertyCoefficient &dfbr, + MaterialPropertyCoefficient &dfbi, + MaterialPropertyCoefficient &fbr, + MaterialPropertyCoefficient &fbi) { // Contribution for second-order farfield boundaries and finite conductivity boundaries. farfield_op.AddExtraSystemBdrCoefficients(omega, dfbr, dfbi); @@ -884,7 +857,7 @@ bool SpaceOperator::AddExcitationVector1Internal(Vector &RHS1) // integration or frequency sweep later. MFEM_VERIFY(RHS1.Size() == GetNDSpace().GetTrueVSize(), "Invalid T-vector size for AddExcitationVector1Internal!"); - SumVectorCoefficient fb(GetNDSpace().GetParMesh()->SpaceDimension()); + SumVectorCoefficient fb(GetMesh().SpaceDimension()); lumped_port_op.AddExcitationBdrCoefficients(fb); surf_j_op.AddExcitationBdrCoefficients(fb); if (fb.empty()) @@ -905,8 +878,7 @@ bool SpaceOperator::AddExcitationVector2Internal(double omega, ComplexVector &RH // specified frequency. MFEM_VERIFY(RHS2.Size() == GetNDSpace().GetTrueVSize(), "Invalid T-vector size for AddExcitationVector2Internal!"); - SumVectorCoefficient fbr(GetNDSpace().GetParMesh()->SpaceDimension()), - fbi(GetNDSpace().GetParMesh()->SpaceDimension()); + SumVectorCoefficient fbr(GetMesh().SpaceDimension()), fbi(GetMesh().SpaceDimension()); wave_port_op.AddExcitationBdrCoefficients(omega, fbr, fbi); if (fbr.empty() && fbi.empty()) { diff --git a/palace/models/spaceoperator.hpp b/palace/models/spaceoperator.hpp index 0da71efa3..d8a929313 100644 --- a/palace/models/spaceoperator.hpp +++ b/palace/models/spaceoperator.hpp @@ -23,8 +23,6 @@ namespace palace { class IoData; -class SumCoefficient; -class SumMatrixCoefficient; // // A class handling spatial discretization of the governing equations. @@ -38,10 +36,9 @@ class SpaceOperator // Helper variables for log file printing. bool print_hdr, print_prec_hdr; - // Perfect electrical conductor essential boundary condition markers. - mfem::Array dbc_marker, aux_bdr_marker; + // Perfect electrical conductor essential boundary condition attributes. + mfem::Array dbc_attr, aux_bdr_attr; std::vector> nd_dbc_tdof_lists, h1_dbc_tdof_lists, aux_bdr_tdof_lists; - void CheckBoundaryProperties(); // Objects defining the finite element spaces for the electric field (Nedelec) and // magnetic flux density (Raviart-Thomas) on the given mesh. The H1 spaces are used for @@ -64,20 +61,24 @@ class SpaceOperator WavePortOperator wave_port_op; SurfaceCurrentOperator surf_j_op; + mfem::Array SetUpBoundaryProperties(const IoData &iodata, const mfem::ParMesh &mesh); + void CheckBoundaryProperties(); + // Helper functions for building the bilinear forms corresponding to the discretized // operators in Maxwell's equations. - void AddStiffnessCoefficients(double coef, SumMatrixCoefficient &df, - SumMatrixCoefficient &f); - void AddStiffnessBdrCoefficients(double coef, SumMatrixCoefficient &fb); - void AddDampingCoefficients(double coef, SumMatrixCoefficient &f); - void AddDampingBdrCoefficients(double coef, SumMatrixCoefficient &fb); - void AddRealMassCoefficients(double coef, SumMatrixCoefficient &f); - void AddRealMassBdrCoefficients(double coef, SumMatrixCoefficient &fb); - void AddImagMassCoefficients(double coef, SumMatrixCoefficient &f); - void AddAbsMassCoefficients(double coef, SumMatrixCoefficient &f); - void AddExtraSystemBdrCoefficients(double omega, SumCoefficient &dfbr, - SumCoefficient &dfbi, SumMatrixCoefficient &fbr, - SumMatrixCoefficient &fbi); + void AddStiffnessCoefficients(double coef, MaterialPropertyCoefficient &df, + MaterialPropertyCoefficient &f); + void AddStiffnessBdrCoefficients(double coef, MaterialPropertyCoefficient &fb); + void AddDampingCoefficients(double coef, MaterialPropertyCoefficient &f); + void AddDampingBdrCoefficients(double coef, MaterialPropertyCoefficient &fb); + void AddRealMassCoefficients(double coef, MaterialPropertyCoefficient &f); + void AddRealMassBdrCoefficients(double coef, MaterialPropertyCoefficient &fb); + void AddImagMassCoefficients(double coef, MaterialPropertyCoefficient &f); + void AddAbsMassCoefficients(double coef, MaterialPropertyCoefficient &f); + void AddExtraSystemBdrCoefficients(double omega, MaterialPropertyCoefficient &dfbr, + MaterialPropertyCoefficient &dfbi, + MaterialPropertyCoefficient &fbr, + MaterialPropertyCoefficient &fbi); // Helper functions for excitation vector assembly. bool AddExcitationVector1Internal(Vector &RHS); @@ -128,8 +129,11 @@ class SpaceOperator auto &GetRTSpace() { return rt_fespace; } const auto &GetRTSpace() const { return rt_fespace; } + // Access the underlying mesh object. + const auto &GetMesh() const { return *GetNDSpace().GetParMesh(); } + // Return the number of true (conforming) dofs on the finest ND space. - auto GlobalTrueVSize() { return GetNDSpace().GlobalTrueVSize(); } + auto GlobalTrueVSize() const { return GetNDSpace().GlobalTrueVSize(); } // Construct any part of the frequency-dependent complex linear system matrix: // A = K + iω C - ω² (Mr + i Mi) + A2(ω) . From c5d73b1b18d190e4a31a43db01d7e93d05988110 Mon Sep 17 00:00:00 2001 From: Sebastian Grimberg Date: Thu, 14 Dec 2023 20:05:07 -0800 Subject: [PATCH 05/32] Working through runtime issues: Still no support for h-multigrid, but works otherwise --- palace/fem/bilinearform.cpp | 1 + palace/linalg/divfree.cpp | 2 +- palace/linalg/errorestimator.cpp | 4 +- palace/linalg/hcurl.cpp | 4 +- palace/models/curlcurloperator.cpp | 2 +- palace/models/domainpostoperator.cpp | 8 +-- palace/models/farfieldboundaryoperator.cpp | 5 +- palace/models/laplaceoperator.cpp | 2 +- palace/models/materialoperator.cpp | 83 +++++++++++++++++++++- palace/models/materialoperator.hpp | 32 ++++----- palace/models/spaceoperator.cpp | 38 +++++++--- palace/models/waveportoperator.cpp | 22 +++--- 12 files changed, 150 insertions(+), 53 deletions(-) diff --git a/palace/fem/bilinearform.cpp b/palace/fem/bilinearform.cpp index a9edd2429..06e1b04cc 100644 --- a/palace/fem/bilinearform.cpp +++ b/palace/fem/bilinearform.cpp @@ -103,6 +103,7 @@ std::unique_ptr BilinearForm::PartialAssemble() const mfem::GridFunction *new_mesh_nodes = mesh.GetNodes(); new_mesh_nodes->MakeOwner(mesh_fec); delete mesh_fespace; + mesh.ExchangeFaceNbrData(); // Deleted in SetNodalFESpace } } diff --git a/palace/linalg/divfree.cpp b/palace/linalg/divfree.cpp index f26682707..6a1a6af91 100644 --- a/palace/linalg/divfree.cpp +++ b/palace/linalg/divfree.cpp @@ -23,7 +23,7 @@ DivFreeSolver::DivFreeSolver(const MaterialOperator &mat_op, FiniteElementSpace double tol, int max_it, int print) { constexpr bool skip_zeros = false; - MaterialPropertyCoefficient epsilon_func(mat_op.GetAttributeToMaterial(), + MaterialPropertyCoefficient epsilon_func(mat_op, mat_op.GetAttributeToMaterial(), mat_op.GetPermittivityReal()); { auto M_mg = std::make_unique(h1_fespaces.GetNumLevels()); diff --git a/palace/linalg/errorestimator.cpp b/palace/linalg/errorestimator.cpp index 3c05a4364..ccdb69cbf 100644 --- a/palace/linalg/errorestimator.cpp +++ b/palace/linalg/errorestimator.cpp @@ -60,7 +60,7 @@ FluxProjector::FluxProjector(const MaterialOperator &mat_op, BlockTimer bt(Timer::CONSTRUCTESTIMATOR); { // Flux operator is always partially assembled. - MaterialPropertyCoefficient muinv_func(mat_op.GetAttributeToMaterial(), + MaterialPropertyCoefficient muinv_func(mat_op, mat_op.GetAttributeToMaterial(), mat_op.GetInvPermeability()); BilinearForm flux(nd_fespace); flux.AddDomainIntegrator( @@ -83,7 +83,7 @@ FluxProjector::FluxProjector(const MaterialOperator &mat_op, BlockTimer bt(Timer::CONSTRUCTESTIMATOR); { // Flux operator is always partially assembled. - MaterialPropertyCoefficient epsilon_func(mat_op.GetAttributeToMaterial(), + MaterialPropertyCoefficient epsilon_func(mat_op, mat_op.GetAttributeToMaterial(), mat_op.GetPermittivityReal()); BilinearForm flux(h1_fespace, h1d_fespace); flux.AddDomainIntegrator((mfem::MatrixCoefficient &)epsilon_func); diff --git a/palace/linalg/hcurl.cpp b/palace/linalg/hcurl.cpp index ff7bca0f9..35d0e3347 100644 --- a/palace/linalg/hcurl.cpp +++ b/palace/linalg/hcurl.cpp @@ -28,9 +28,9 @@ WeightedHCurlNormSolver::WeightedHCurlNormSolver( const auto n_levels = nd_fespaces.GetNumLevels(); { constexpr bool skip_zeros = false; - MaterialPropertyCoefficient muinv_func(mat_op.GetAttributeToMaterial(), + MaterialPropertyCoefficient muinv_func(mat_op, mat_op.GetAttributeToMaterial(), mat_op.GetInvPermeability()); - MaterialPropertyCoefficient epsilon_func(mat_op.GetAttributeToMaterial(), + MaterialPropertyCoefficient epsilon_func(mat_op, mat_op.GetAttributeToMaterial(), mat_op.GetPermittivityReal()); auto A_mg = std::make_unique(n_levels); for (bool aux : {false, true}) diff --git a/palace/models/curlcurloperator.cpp b/palace/models/curlcurloperator.cpp index bc2d295d1..12958d7da 100644 --- a/palace/models/curlcurloperator.cpp +++ b/palace/models/curlcurloperator.cpp @@ -165,7 +165,7 @@ std::unique_ptr CurlCurlOperator::GetStiffnessMatrix() nd_fespace_l.GetMaxElementOrder(), nd_fespace_l.GlobalTrueVSize()); } constexpr bool skip_zeros = false; - MaterialPropertyCoefficient muinv_func(mat_op.GetAttributeToMaterial(), + MaterialPropertyCoefficient muinv_func(mat_op, mat_op.GetAttributeToMaterial(), mat_op.GetInvPermeability()); BilinearForm k(nd_fespace_l); k.AddDomainIntegrator((mfem::MatrixCoefficient &)muinv_func); diff --git a/palace/models/domainpostoperator.cpp b/palace/models/domainpostoperator.cpp index 84d319feb..1740dd265 100644 --- a/palace/models/domainpostoperator.cpp +++ b/palace/models/domainpostoperator.cpp @@ -25,7 +25,7 @@ DomainPostOperator::DomainPostOperator(const IoData &iodata, const MaterialOpera // E_elec = 1/2 Re{∫_Ω Dᴴ E dV} as (M_eps * e)ᴴ e. // Only the real part of the permeability contributes to the energy (imaginary part // cancels out in the inner product due to symmetry). - MaterialPropertyCoefficient epsilon_func(mat_op.GetAttributeToMaterial(), + MaterialPropertyCoefficient epsilon_func(mat_op, mat_op.GetAttributeToMaterial(), mat_op.GetPermittivityReal()); BilinearForm m_nd(*nd_fespace); m_nd.AddDomainIntegrator( @@ -39,7 +39,7 @@ DomainPostOperator::DomainPostOperator(const IoData &iodata, const MaterialOpera { // Construct RT mass matrix to compute the magnetic field energy integral as: // E_mag = 1/2 Re{∫_Ω Bᴴ H dV} as (M_muinv * b)ᴴ b. - MaterialPropertyCoefficient muinv_func(mat_op.GetAttributeToMaterial(), + MaterialPropertyCoefficient muinv_func(mat_op, mat_op.GetAttributeToMaterial(), mat_op.GetInvPermeability()); BilinearForm m_rt(*rt_fespace); m_rt.AddDomainIntegrator((mfem::MatrixCoefficient &)muinv_func); @@ -55,7 +55,7 @@ DomainPostOperator::DomainPostOperator(const IoData &iodata, const MaterialOpera std::unique_ptr M_ND_i, M_RT_i; if (nd_fespace) { - MaterialPropertyCoefficient epsilon_func(mat_op.GetAttributeToMaterial(), + MaterialPropertyCoefficient epsilon_func(mat_op, mat_op.GetAttributeToMaterial(), mat_op.GetPermittivityReal()); epsilon_func.RestrictCoefficient(mat_op.GetAttributeGlobalToLocal(data.attributes)); BilinearForm m_nd_i(*nd_fespace); @@ -65,7 +65,7 @@ DomainPostOperator::DomainPostOperator(const IoData &iodata, const MaterialOpera } if (rt_fespace) { - MaterialPropertyCoefficient muinv_func(mat_op.GetAttributeToMaterial(), + MaterialPropertyCoefficient muinv_func(mat_op, mat_op.GetAttributeToMaterial(), mat_op.GetInvPermeability()); muinv_func.RestrictCoefficient(mat_op.GetAttributeGlobalToLocal(data.attributes)); BilinearForm m_rt_i(*rt_fespace); diff --git a/palace/models/farfieldboundaryoperator.cpp b/palace/models/farfieldboundaryoperator.cpp index f0d302a33..4cce923df 100644 --- a/palace/models/farfieldboundaryoperator.cpp +++ b/palace/models/farfieldboundaryoperator.cpp @@ -70,7 +70,7 @@ void FarfieldBoundaryOperator::AddDampingBdrCoefficients(double coef, // First-order absorbing boundary condition. if (farfield_attr.Size()) { - MaterialPropertyCoefficient invz0_func(mat_op.GetBdrAttributeToMaterial(), + MaterialPropertyCoefficient invz0_func(mat_op, mat_op.GetBdrAttributeToMaterial(), mat_op.GetInvImpedance()); invz0_func.RestrictCoefficient(mat_op.GetBdrAttributeGlobalToLocal(farfield_attr)); fb.AddCoefficient(invz0_func.GetAttributeToMaterial(), @@ -94,7 +94,8 @@ void FarfieldBoundaryOperator::AddExtraSystemBdrCoefficients( { Mult(mat_op.GetInvPermeability()(k), mat_op.GetLightSpeed()(k), muinvc0(k)); } - MaterialPropertyCoefficient muinvc0_func(mat_op.GetBdrAttributeToMaterial(), muinvc0); + MaterialPropertyCoefficient muinvc0_func(mat_op, mat_op.GetBdrAttributeToMaterial(), + muinvc0); muinvc0_func.RestrictCoefficient(mat_op.GetBdrAttributeGlobalToLocal(farfield_attr)); // Instead getting the correct normal of farfield boundary elements, just pick the diff --git a/palace/models/laplaceoperator.cpp b/palace/models/laplaceoperator.cpp index dcbb9740e..50dc2fe9c 100644 --- a/palace/models/laplaceoperator.cpp +++ b/palace/models/laplaceoperator.cpp @@ -186,7 +186,7 @@ std::unique_ptr LaplaceOperator::GetStiffnessMatrix() h1_fespace_l.GetMaxElementOrder(), h1_fespace_l.GlobalTrueVSize()); } constexpr bool skip_zeros = false; - MaterialPropertyCoefficient epsilon_func(mat_op.GetAttributeToMaterial(), + MaterialPropertyCoefficient epsilon_func(mat_op, mat_op.GetAttributeToMaterial(), mat_op.GetPermittivityReal()); BilinearForm k(h1_fespace_l); k.AddDomainIntegrator((mfem::MatrixCoefficient &)epsilon_func); diff --git a/palace/models/materialoperator.cpp b/palace/models/materialoperator.cpp index 2046b7814..9a57b5112 100644 --- a/palace/models/materialoperator.cpp +++ b/palace/models/materialoperator.cpp @@ -587,9 +587,57 @@ mfem::Array MaterialOperator::GetBdrAttributeToMaterial() const return bdr_attr_mat; } +int MaterialOperator::GetAttributeGlobalToLocal(mfem::ElementTransformation &T) const +{ + if (T.GetDimension() == T.GetSpaceDim()) + { + // Domain element. + auto it = loc_attr.find(T.Attribute); + MFEM_ASSERT(it != loc_attr.end(), "Invalid domain attribute " << T.Attribute << "!"); + return it->second; + } + else + { + // Boundary element (or boundary submesh domain). + auto bdr_attr_map = loc_bdr_attr.find(T.Attribute); + MFEM_ASSERT(bdr_attr_map != loc_bdr_attr.end(), + "Invalid domain attribute " << T.Attribute << "!"); + const int nbr_attr = [&]() + { + // XX TODO INCORRECT FOR H-MULTIGRID: T.ElementNo SHOULD BE USED TO FIND THE MESH + // NEIGHBOR ON THE COARSE MESH + + mfem::FaceElementTransformations FET; // XX TODO: Preallocate these for all elements + mfem::IsoparametricTransformation T1, T2; + if (const auto *submesh = dynamic_cast(T.mesh)) + { + MFEM_ASSERT(T.ElementType == mfem::ElementTransformation::ELEMENT, + "Unexpected element type in GetAttributeGlobalToLocal!"); + return GetBdrNeighborAttribute(submesh->GetParentElementIDMap()[T.ElementNo], + *submesh->GetParent(), face_loc_to_shared, FET, T1, + T2); + } + else + { + MFEM_ASSERT(T.ElementType == mfem::ElementTransformation::BDR_ELEMENT, + "Unexpected element type in GetAttributeGlobalToLocal!"); + return GetBdrNeighborAttribute(T.ElementNo, + *static_cast(T.mesh), + face_loc_to_shared, FET, T1, T2); + } + }(); + auto it = bdr_attr_map->second.find(nbr_attr); + MFEM_ASSERT(it != bdr_attr_map->second.end(), + "Invalid domain attribute " << nbr_attr << "!"); + return it->second; + } +} + MaterialPropertyCoefficient::MaterialPropertyCoefficient( - const mfem::Array &attr_mat_, const mfem::DenseTensor &mat_coeff_, double a) - : mfem::MatrixCoefficient(0, 0), attr_mat(attr_mat_), mat_coeff(mat_coeff_) + const MaterialOperator &mat_op, const mfem::Array &attr_mat_, + const mfem::DenseTensor &mat_coeff_, double a) + : mfem::MatrixCoefficient(0, 0), mat_op(mat_op), attr_mat(attr_mat_), + mat_coeff(mat_coeff_) { for (int k = 0; k < mat_coeff.SizeK(); k++) { @@ -862,6 +910,37 @@ void MaterialPropertyCoefficient::NormalProjectedCoefficient(const mfem::Vector width = mat_coeff.SizeJ(); } +double MaterialPropertyCoefficient::Eval(mfem::ElementTransformation &T, + const mfem::IntegrationPoint &ip) +{ + const int attr = mat_op.GetAttributeGlobalToLocal(T); + MFEM_ASSERT(attr <= attr_mat.Size(), + "Out of bounds attribute for MaterialPropertyCoefficient (" + << attr << " > " << attr_mat.Size() << ")!"); + MFEM_ASSERT(mat_coeff.SizeI() == 1 && mat_coeff.SizeJ() == 1, + "Invalid access of matrix-valued MaterialPropertyCoefficient using scalar " + "coefficient interface!"); + return (attr_mat[attr - 1] < 0) ? 0.0 : mat_coeff(0, 0, attr_mat[attr - 1]); +} + +void MaterialPropertyCoefficient::Eval(mfem::DenseMatrix &K, mfem::ElementTransformation &T, + const mfem::IntegrationPoint &ip) +{ + const int attr = mat_op.GetAttributeGlobalToLocal(T); + MFEM_ASSERT(attr <= attr_mat.Size(), + "Out of bounds attribute for MaterialPropertyCoefficient (" + << attr << " > " << attr_mat.Size() << ")!"); + if (attr_mat[attr - 1] < 0) + { + K.SetSize(mat_coeff.SizeI(), mat_coeff.SizeJ()); + K = 0.0; + } + else + { + K = mat_coeff(attr_mat[attr - 1]); + } +} + template void MaterialPropertyCoefficient::AddMaterialProperty(const mfem::Array &, const mfem::DenseMatrix &, double); diff --git a/palace/models/materialoperator.hpp b/palace/models/materialoperator.hpp index ce58b97a0..59085b6bc 100644 --- a/palace/models/materialoperator.hpp +++ b/palace/models/materialoperator.hpp @@ -150,6 +150,8 @@ class MaterialOperator return GetBdrAttributeGlobalToLocal(std::vector{attr}); } + int GetAttributeGlobalToLocal(mfem::ElementTransformation &T) const; + const auto &GetMesh() const { return mesh; } }; @@ -160,6 +162,9 @@ class MaterialOperator class MaterialPropertyCoefficient : public mfem::Coefficient, public mfem::MatrixCoefficient { private: + // Reference to material property data (not owned). + const MaterialOperator &mat_op; + // Map attribute to material index (coeff = mat_coeff[attr_mat[attr - 1]], for 1-based // attributes). mfem::Array attr_mat; @@ -168,8 +173,12 @@ class MaterialPropertyCoefficient : public mfem::Coefficient, public mfem::Matri mfem::DenseTensor mat_coeff; public: - MaterialPropertyCoefficient() : mfem::MatrixCoefficient(0, 0) {} - MaterialPropertyCoefficient(const mfem::Array &attr_mat_, + MaterialPropertyCoefficient(const MaterialOperator &mat_op) + : mfem::MatrixCoefficient(0, 0), mat_op(mat_op) + { + } + MaterialPropertyCoefficient(const MaterialOperator &mat_op, + const mfem::Array &attr_mat_, const mfem::DenseTensor &mat_coeff_, double a = 1.0); bool empty() const { return mat_coeff.TotalSize() == 0; } @@ -195,25 +204,10 @@ class MaterialPropertyCoefficient : public mfem::Coefficient, public mfem::Matri void NormalProjectedCoefficient(const mfem::Vector &normal); - double Eval(mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip) override - { - MFEM_ASSERT(T.Attribute <= attr_mat.Size(), - "Out of bounds attribute for MaterialPropertyCoefficient (" - << T.Attribute << " > " << attr_mat.Size() << ")!"); - MFEM_ASSERT(mat_coeff.SizeI() == 1 && mat_coeff.SizeJ() == 1, - "Invalid access of matrix-valued MaterialPropertyCoefficient using scalar " - "coefficient interface!"); - return mat_coeff(0, 0, attr_mat[T.Attribute - 1]); - } + double Eval(mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip) override; void Eval(mfem::DenseMatrix &K, mfem::ElementTransformation &T, - const mfem::IntegrationPoint &ip) override - { - MFEM_ASSERT(T.Attribute <= attr_mat.Size(), - "Out of bounds attribute for MaterialPropertyCoefficient (" - << T.Attribute << " > " << attr_mat.Size() << ")!"); - K = mat_coeff(attr_mat[T.Attribute - 1]); - } + const mfem::IntegrationPoint &ip) override; }; } // namespace palace diff --git a/palace/models/spaceoperator.cpp b/palace/models/spaceoperator.cpp index 32ea3a76e..5b721c8e4 100644 --- a/palace/models/spaceoperator.cpp +++ b/palace/models/spaceoperator.cpp @@ -216,7 +216,14 @@ BuildOperator(const FiniteElementSpace &fespace, const MaterialPropertyCoefficie } if (f && !f->empty()) { - a.AddDomainIntegrator((mfem::MatrixCoefficient &)*f); + if (f->GetMaterialProperties().SizeI() == 1) + { + a.AddDomainIntegrator((mfem::Coefficient &)*f); + } + else + { + a.AddDomainIntegrator((mfem::MatrixCoefficient &)*f); + } } } if (dfb && !dfb->empty() && fb && !fb->empty()) @@ -232,7 +239,14 @@ BuildOperator(const FiniteElementSpace &fespace, const MaterialPropertyCoefficie } if (fb && !fb->empty()) { - a.AddBoundaryIntegrator((mfem::MatrixCoefficient &)*fb); + if (fb->GetMaterialProperties().SizeI() == 1) + { + a.AddBoundaryIntegrator((mfem::Coefficient &)*fb); + } + else + { + a.AddBoundaryIntegrator((mfem::MatrixCoefficient &)*fb); + } } } return (l > 0) ? a.Assemble(skip_zeros) : a.FullAssemble(skip_zeros); @@ -258,7 +272,14 @@ std::unique_ptr BuildAuxOperator(const FiniteElementSpace &fespace, } if (fb && !fb->empty()) { - a.AddBoundaryIntegrator((mfem::MatrixCoefficient &)*fb); + if (fb->GetMaterialProperties().SizeI() == 1) + { + a.AddBoundaryIntegrator((mfem::Coefficient &)*fb); + } + else + { + a.AddBoundaryIntegrator((mfem::MatrixCoefficient &)*fb); + } } return (l > 0) ? a.Assemble(skip_zeros) : a.FullAssemble(skip_zeros); } @@ -270,7 +291,7 @@ std::unique_ptr SpaceOperator::GetStiffnessMatrix(Operator::DiagonalPolicy diag_policy) { PrintHeader(GetH1Space(), GetNDSpace(), GetRTSpace(), print_hdr); - MaterialPropertyCoefficient df, f, fb; + MaterialPropertyCoefficient df(mat_op), f(mat_op), fb(mat_op); AddStiffnessCoefficients(1.0, df, f); AddStiffnessBdrCoefficients(1.0, fb); if (df.empty() && f.empty() && fb.empty()) @@ -299,7 +320,7 @@ std::unique_ptr SpaceOperator::GetDampingMatrix(Operator::DiagonalPolicy diag_policy) { PrintHeader(GetH1Space(), GetNDSpace(), GetRTSpace(), print_hdr); - MaterialPropertyCoefficient f, fb; + MaterialPropertyCoefficient f(mat_op), fb(mat_op); AddDampingCoefficients(1.0, f); AddDampingBdrCoefficients(1.0, fb); if (f.empty() && fb.empty()) @@ -327,7 +348,7 @@ template std::unique_ptr SpaceOperator::GetMassMatrix(Operator::DiagonalPolicy diag_policy) { PrintHeader(GetH1Space(), GetNDSpace(), GetRTSpace(), print_hdr); - MaterialPropertyCoefficient fr, fi, fbr, fbi; + MaterialPropertyCoefficient fr(mat_op), fi(mat_op), fbr(mat_op), fbi(mat_op); AddRealMassCoefficients(1.0, fr); AddRealMassBdrCoefficients(1.0, fbr); if constexpr (std::is_same::value) @@ -369,7 +390,7 @@ std::unique_ptr SpaceOperator::GetExtraSystemMatrix(double omega, Operator::DiagonalPolicy diag_policy) { PrintHeader(GetH1Space(), GetNDSpace(), GetRTSpace(), print_hdr); - MaterialPropertyCoefficient dfbr, dfbi, fbr, fbi; + MaterialPropertyCoefficient dfbr(mat_op), dfbi(mat_op), fbr(mat_op), fbi(mat_op); AddExtraSystemBdrCoefficients(omega, dfbr, dfbi, fbr, fbi); if (dfbr.empty() && fbr.empty() && dfbi.empty() && fbi.empty()) { @@ -663,7 +684,8 @@ std::unique_ptr SpaceOperator::GetPreconditionerMatrix(double a0, doub Mpi::Print(" Level {:d}{} (p = {:d}): {:d} unknowns", l, aux ? " (auxiliary)" : "", fespace_l.GetMaxElementOrder(), fespace_l.GlobalTrueVSize()); } - MaterialPropertyCoefficient dfr, fr, dfi, fi, dfbr, dfbi, fbr, fbi; + MaterialPropertyCoefficient dfr(mat_op), fr(mat_op), dfi(mat_op), fi(mat_op), + dfbr(mat_op), dfbi(mat_op), fbr(mat_op), fbi(mat_op); if (!std::is_same::value || pc_mat_real || l == 0) { // Real-valued system matrix (approximation) for preconditioning. diff --git a/palace/models/waveportoperator.cpp b/palace/models/waveportoperator.cpp index c5ada093a..3e774e706 100644 --- a/palace/models/waveportoperator.cpp +++ b/palace/models/waveportoperator.cpp @@ -107,7 +107,7 @@ std::unique_ptr GetBtt(const MaterialOperator &mat_op, const FiniteElementSpace &nd_fespace) { // Mass matrix: Bₜₜ = (μ⁻¹ u, v). - MaterialPropertyCoefficient muinv_func(mat_op.GetBdrAttributeToMaterial(), + MaterialPropertyCoefficient muinv_func(mat_op, mat_op.GetBdrAttributeToMaterial(), mat_op.GetInvPermeability()); BilinearForm btt(nd_fespace); btt.AddDomainIntegrator((mfem::MatrixCoefficient &)muinv_func); @@ -119,7 +119,7 @@ std::unique_ptr GetBtn(const MaterialOperator &mat_op, const FiniteElementSpace &h1_fespace) { // Mass matrix: Bₜₙ = (μ⁻¹ ∇ₜ u, v). - MaterialPropertyCoefficient muinv_func(mat_op.GetBdrAttributeToMaterial(), + MaterialPropertyCoefficient muinv_func(mat_op, mat_op.GetBdrAttributeToMaterial(), mat_op.GetInvPermeability()); BilinearForm btn(h1_fespace, nd_fespace); btn.AddDomainIntegrator( @@ -133,12 +133,12 @@ std::array, 3> GetBnn(const MaterialOperator &mat_o const mfem::Vector &normal) { // Mass matrix: Bₙₙ = (μ⁻¹ ∇ₜ u, ∇ₜ v) - ω² (ε u, v) = Bₙₙ₁ - ω² Bₙₙ₂. - MaterialPropertyCoefficient muinv_func(mat_op.GetBdrAttributeToMaterial(), + MaterialPropertyCoefficient muinv_func(mat_op, mat_op.GetBdrAttributeToMaterial(), mat_op.GetInvPermeability()); BilinearForm bnn1(h1_fespace); bnn1.AddDomainIntegrator((mfem::MatrixCoefficient &)muinv_func); - MaterialPropertyCoefficient epsilon_func(mat_op.GetBdrAttributeToMaterial(), + MaterialPropertyCoefficient epsilon_func(mat_op, mat_op.GetBdrAttributeToMaterial(), mat_op.GetPermittivityReal()); epsilon_func.NormalProjectedCoefficient(normal); BilinearForm bnn2r(h1_fespace); @@ -151,8 +151,8 @@ std::array, 3> GetBnn(const MaterialOperator &mat_o std::make_unique(bnn2r.FullAssemble(skip_zeros), h1_fespace), nullptr}; } - MaterialPropertyCoefficient negepstandelta_func(mat_op.GetBdrAttributeToMaterial(), - mat_op.GetPermittivityImag()); + MaterialPropertyCoefficient negepstandelta_func( + mat_op, mat_op.GetBdrAttributeToMaterial(), mat_op.GetPermittivityImag()); negepstandelta_func.NormalProjectedCoefficient(normal); BilinearForm bnn2i(h1_fespace); bnn2i.AddDomainIntegrator((mfem::Coefficient &)negepstandelta_func); @@ -166,13 +166,13 @@ std::array, 3> GetAtt(const MaterialOperator &mat_o const mfem::Vector &normal) { // Stiffness matrix: Aₜₜ = (μ⁻¹ ∇ₜ x u, ∇ₜ x v) - ω² (ε u, v) = Aₜₜ₁ - ω² Aₜₜ₂. - MaterialPropertyCoefficient muinv_func(mat_op.GetBdrAttributeToMaterial(), + MaterialPropertyCoefficient muinv_func(mat_op, mat_op.GetBdrAttributeToMaterial(), mat_op.GetInvPermeability()); muinv_func.NormalProjectedCoefficient(normal); BilinearForm att1(nd_fespace); att1.AddDomainIntegrator((mfem::Coefficient &)muinv_func); - MaterialPropertyCoefficient epsilon_func(mat_op.GetBdrAttributeToMaterial(), + MaterialPropertyCoefficient epsilon_func(mat_op, mat_op.GetBdrAttributeToMaterial(), mat_op.GetPermittivityReal()); BilinearForm att2r(nd_fespace); att2r.AddDomainIntegrator( @@ -185,8 +185,8 @@ std::array, 3> GetAtt(const MaterialOperator &mat_o std::make_unique(att2r.FullAssemble(skip_zeros), nd_fespace), nullptr}; } - MaterialPropertyCoefficient negepstandelta_func(mat_op.GetBdrAttributeToMaterial(), - mat_op.GetPermittivityImag()); + MaterialPropertyCoefficient negepstandelta_func( + mat_op, mat_op.GetBdrAttributeToMaterial(), mat_op.GetPermittivityImag()); BilinearForm att2i(nd_fespace); att2i.AddDomainIntegrator( (mfem::MatrixCoefficient &)negepstandelta_func); @@ -1205,7 +1205,7 @@ void WavePortOperator::AddExtraSystemBdrCoefficients(double omega, for (const auto &[idx, data] : ports) { const MaterialOperator &mat_op = data.mat_op; - MaterialPropertyCoefficient muinv_func(mat_op.GetBdrAttributeToMaterial(), + MaterialPropertyCoefficient muinv_func(mat_op, mat_op.GetBdrAttributeToMaterial(), mat_op.GetInvPermeability()); muinv_func.RestrictCoefficient(mat_op.GetBdrAttributeGlobalToLocal(data.GetAttrList())); // fbr.AddCoefficient(muinv_func.GetAttributeToMaterial(), From a1a67038dfa6a0b3173751438dfe22b31dcff397 Mon Sep 17 00:00:00 2001 From: Sebastian Grimberg Date: Fri, 15 Dec 2023 16:11:26 -0800 Subject: [PATCH 06/32] GetSharedFaceTransformationsByLocalIndex can be used to replace GetSharedFaceTransformations and the need for passing around a local to shared face map --- palace/fem/coefficient.cpp | 12 ++++----- palace/fem/coefficient.hpp | 40 ++++++++++------------------- palace/models/materialoperator.cpp | 41 +++++------------------------- palace/models/materialoperator.hpp | 9 ++----- 4 files changed, 27 insertions(+), 75 deletions(-) diff --git a/palace/fem/coefficient.cpp b/palace/fem/coefficient.cpp index 9b8802c1d..5a2e40f7f 100644 --- a/palace/fem/coefficient.cpp +++ b/palace/fem/coefficient.cpp @@ -7,9 +7,9 @@ namespace palace { void BdrGridFunctionCoefficient::GetBdrElementNeighborTransformations( - int i, const mfem::ParMesh &mesh, const std::unordered_map &local_to_shared, - mfem::FaceElementTransformations &FET, mfem::IsoparametricTransformation &T1, - mfem::IsoparametricTransformation &T2, const mfem::IntegrationPoint *ip) + int i, const mfem::ParMesh &mesh, mfem::FaceElementTransformations &FET, + mfem::IsoparametricTransformation &T1, mfem::IsoparametricTransformation &T2, + const mfem::IntegrationPoint *ip) { // Return transformations for elements attached to the given boundary element. FET.Elem1 // always exists but FET.Elem2 may not if the element is truly a single-sided boundary. @@ -25,8 +25,7 @@ void BdrGridFunctionCoefficient::GetBdrElementNeighborTransformations( if (info2 >= 0 && iel2 < 0) { // Face is shared with another subdomain. - const int &ishared = local_to_shared.at(f); - mesh.GetSharedFaceTransformations(ishared, &FET, &T1, &T2); + mesh.GetSharedFaceTransformationsByLocalIndex(f, &FET, &T1, &T2); } else { @@ -51,8 +50,7 @@ void BdrGridFunctionCoefficient::GetBdrElementNeighborTransformations( // too. MFEM_ASSERT(T.ElementType == mfem::ElementTransformation::BDR_ELEMENT, "Unexpected element type in BdrGridFunctionCoefficient!"); - GetBdrElementNeighborTransformations(T.ElementNo, mesh, local_to_shared, FET, T1, T2, - &ip); + GetBdrElementNeighborTransformations(T.ElementNo, mesh, FET, T1, T2, &ip); // If desired, get vector pointing from center of boundary element into element 1 for // orientations. diff --git a/palace/fem/coefficient.hpp b/palace/fem/coefficient.hpp index aa50e823f..f66429980 100644 --- a/palace/fem/coefficient.hpp +++ b/palace/fem/coefficient.hpp @@ -6,7 +6,6 @@ #include #include -#include #include #include #include @@ -31,7 +30,6 @@ class BdrGridFunctionCoefficient // XX TODO: For thread-safety (multiple threads evaluating a coefficient simultaneously), // the FET, FET.Elem1, and FET.Elem2 objects cannot be shared const mfem::ParMesh &mesh; - const std::unordered_map &local_to_shared; mfem::FaceElementTransformations FET; mfem::IsoparametricTransformation T1, T2, TF; @@ -40,20 +38,16 @@ class BdrGridFunctionCoefficient mfem::Vector *C1 = nullptr); public: - BdrGridFunctionCoefficient(const mfem::ParMesh &mesh, - const std::unordered_map &local_to_shared) - : mesh(mesh), local_to_shared(local_to_shared) - { - } + BdrGridFunctionCoefficient(const mfem::ParMesh &mesh) : mesh(mesh) {} // For a boundary element, return the element transformation objects for the neighboring // domain elements. FET.Elem2 may be nullptr if the boundary is a true one-sided boundary, // but if it is shared with another subdomain then it will be populated. Expects // ParMesh::ExchangeFaceNbrData has been called already. static void GetBdrElementNeighborTransformations( - int i, const mfem::ParMesh &mesh, const std::unordered_map &local_to_shared, - mfem::FaceElementTransformations &FET, mfem::IsoparametricTransformation &T1, - mfem::IsoparametricTransformation &T2, const mfem::IntegrationPoint *ip = nullptr); + int i, const mfem::ParMesh &mesh, mfem::FaceElementTransformations &FET, + mfem::IsoparametricTransformation &T1, mfem::IsoparametricTransformation &T2, + const mfem::IntegrationPoint *ip = nullptr); // Return normal vector to the boundary element at an integration point (it is assumed // that the element transformation has already been configured at the integration point of @@ -81,10 +75,9 @@ class BdrCurrentVectorCoefficient : public mfem::VectorCoefficient, BdrCurrentVectorCoefficient(const mfem::ParGridFunction &gf, const MaterialOperator &mat_op) : mfem::VectorCoefficient(mat_op.SpaceDimension()), - BdrGridFunctionCoefficient(*gf.ParFESpace()->GetParMesh(), - mat_op.GetLocalToSharedFaceMap()), - B(gf), mat_op(mat_op), C1(gf.VectorDim()), W(gf.VectorDim()), VU(gf.VectorDim()), - VL(gf.VectorDim()), nor(gf.VectorDim()) + BdrGridFunctionCoefficient(*gf.ParFESpace()->GetParMesh()), B(gf), mat_op(mat_op), + C1(gf.VectorDim()), W(gf.VectorDim()), VU(gf.VectorDim()), VL(gf.VectorDim()), + nor(gf.VectorDim()) { } @@ -137,8 +130,7 @@ class BdrChargeCoefficient : public mfem::Coefficient, public BdrGridFunctionCoe public: BdrChargeCoefficient(const mfem::ParGridFunction &gf, const MaterialOperator &mat_op) - : mfem::Coefficient(), BdrGridFunctionCoefficient(*gf.ParFESpace()->GetParMesh(), - mat_op.GetLocalToSharedFaceMap()), + : mfem::Coefficient(), BdrGridFunctionCoefficient(*gf.ParFESpace()->GetParMesh()), E(gf), mat_op(mat_op), C1(gf.VectorDim()), W(gf.VectorDim()), VU(gf.VectorDim()), VL(gf.VectorDim()), nor(gf.VectorDim()) { @@ -179,8 +171,7 @@ class BdrFluxCoefficient : public mfem::Coefficient, public BdrGridFunctionCoeff public: BdrFluxCoefficient(const mfem::ParGridFunction &gf, const MaterialOperator &mat_op, const mfem::Vector &d) - : mfem::Coefficient(), BdrGridFunctionCoefficient(*gf.ParFESpace()->GetParMesh(), - mat_op.GetLocalToSharedFaceMap()), + : mfem::Coefficient(), BdrGridFunctionCoefficient(*gf.ParFESpace()->GetParMesh()), B(gf), dir(d), V(gf.VectorDim()), VL(gf.VectorDim()), nor(gf.VectorDim()) { } @@ -275,8 +266,7 @@ class DielectricInterfaceCoefficient : public mfem::Coefficient, DielectricInterfaceCoefficient(const mfem::ParGridFunction &gf, const MaterialOperator &mat_op, double ti, double ei, const mfem::Vector &s) - : mfem::Coefficient(), BdrGridFunctionCoefficient(*gf.ParFESpace()->GetParMesh(), - mat_op.GetLocalToSharedFaceMap()), + : mfem::Coefficient(), BdrGridFunctionCoefficient(*gf.ParFESpace()->GetParMesh()), E(gf), mat_op(mat_op), ts(ti), epsilon(ei), side(s), C1(gf.VectorDim()), V(gf.VectorDim()), nor(gf.VectorDim()) { @@ -365,8 +355,7 @@ class EnergyDensityCoefficient : public mfem::Coefficient, public BdrGridFunctio public: EnergyDensityCoefficient(const GridFunctionType &gf, const MaterialOperator &mat_op) - : mfem::Coefficient(), BdrGridFunctionCoefficient(*gf.ParFESpace()->GetParMesh(), - mat_op.GetLocalToSharedFaceMap()), + : mfem::Coefficient(), BdrGridFunctionCoefficient(*gf.ParFESpace()->GetParMesh()), U(gf), mat_op(mat_op), V(mat_op.SpaceDimension()) { } @@ -460,9 +449,7 @@ class BdrFieldVectorCoefficient : public mfem::VectorCoefficient, public: BdrFieldVectorCoefficient(const mfem::ParGridFunction &gf, const MaterialOperator &mat_op) : mfem::VectorCoefficient(mat_op.SpaceDimension()), - BdrGridFunctionCoefficient(*gf.ParFESpace()->GetParMesh(), - mat_op.GetLocalToSharedFaceMap()), - U(gf), mat_op(mat_op) + BdrGridFunctionCoefficient(*gf.ParFESpace()->GetParMesh()), U(gf), mat_op(mat_op) { } @@ -494,8 +481,7 @@ class BdrFieldCoefficient : public mfem::Coefficient, public BdrGridFunctionCoef public: BdrFieldCoefficient(const mfem::ParGridFunction &gf, const MaterialOperator &mat_op) - : mfem::Coefficient(), BdrGridFunctionCoefficient(*gf.ParFESpace()->GetParMesh(), - mat_op.GetLocalToSharedFaceMap()), + : mfem::Coefficient(), BdrGridFunctionCoefficient(*gf.ParFESpace()->GetParMesh()), U(gf), mat_op(mat_op) { } diff --git a/palace/models/materialoperator.cpp b/palace/models/materialoperator.cpp index 9a57b5112..5b5c98249 100644 --- a/palace/models/materialoperator.cpp +++ b/palace/models/materialoperator.cpp @@ -278,20 +278,6 @@ mfem::DenseMatrix ToDenseMatrix(const config::SymmetricMatrixData &data) return M; } -auto BuildLocalToSharedFaceMap(const mfem::ParMesh &mesh) -{ - // Construct shared face mapping for boundary coefficients. The inverse mapping is - // constructed as part of mfem::ParMesh, but we need this mapping when looping over - // all mesh faces. - std::unordered_map l2s; - l2s.reserve(mesh.GetNSharedFaces()); - for (int i = 0; i < mesh.GetNSharedFaces(); i++) - { - l2s[mesh.GetSharedFace(i)] = i; - } - return l2s; -} - auto BuildAttributeGlobalToLocal(const mfem::ParMesh &mesh) { // Set up sparse map from global domain attributes to local ones on this process. @@ -327,25 +313,18 @@ auto BuildAttributeGlobalToLocal(const mfem::ParMesh &mesh) } auto GetBdrNeighborAttribute(int i, const mfem::ParMesh &mesh, - const std::unordered_map &face_loc_to_shared, mfem::FaceElementTransformations &FET, mfem::IsoparametricTransformation &T1, mfem::IsoparametricTransformation &T2) { // For internal boundaries, use the element which corresponds to the vacuum domain, or // at least the one with the higher speed of light. - BdrGridFunctionCoefficient::GetBdrElementNeighborTransformations( - i, mesh, face_loc_to_shared, FET, T1, T2); - // return (FET.Elem2 && GetLightSpeedMin(FET.Elem2->Attribute) > - // GetLightSpeedMax(FET.Elem1->Attribute)) - // ? FET.Elem2->Attribute - // : FET.Elem1->Attribute; + BdrGridFunctionCoefficient::GetBdrElementNeighborTransformations(i, mesh, FET, T1, T2); return (FET.Elem2 && FET.Elem2->Attribute < FET.Elem1->Attribute) ? FET.Elem2->Attribute : FET.Elem1->Attribute; } -auto BuildBdrAttributeGlobalToLocal(const mfem::ParMesh &mesh, - const std::unordered_map &face_loc_to_shared) +auto BuildBdrAttributeGlobalToLocal(const mfem::ParMesh &mesh) { // Set up sparse map from global boundary attributes to local ones on this process. Each // original global boundary attribute maps to a key-value pairing of global domain @@ -357,7 +336,7 @@ auto BuildBdrAttributeGlobalToLocal(const mfem::ParMesh &mesh, for (int i = 0; i < mesh.GetNBE(); i++) { const int attr = mesh.GetBdrAttribute(i); - const int nbr_attr = GetBdrNeighborAttribute(i, mesh, face_loc_to_shared, FET, T1, T2); + const int nbr_attr = GetBdrNeighborAttribute(i, mesh, FET, T1, T2); auto &bdr_attr_map = loc_bdr_attr[attr]; if (bdr_attr_map.find(nbr_attr) == bdr_attr_map.end()) { @@ -372,9 +351,8 @@ auto BuildBdrAttributeGlobalToLocal(const mfem::ParMesh &mesh, MaterialOperator::MaterialOperator(const IoData &iodata, mfem::ParMesh &mesh) : mesh(mesh) { mesh.ExchangeFaceNbrData(); - face_loc_to_shared = BuildLocalToSharedFaceMap(mesh); loc_attr = BuildAttributeGlobalToLocal(mesh); - loc_bdr_attr = BuildBdrAttributeGlobalToLocal(mesh, face_loc_to_shared); + loc_bdr_attr = BuildBdrAttributeGlobalToLocal(mesh); SetUpMaterialProperties(iodata, mesh); } @@ -604,9 +582,6 @@ int MaterialOperator::GetAttributeGlobalToLocal(mfem::ElementTransformation &T) "Invalid domain attribute " << T.Attribute << "!"); const int nbr_attr = [&]() { - // XX TODO INCORRECT FOR H-MULTIGRID: T.ElementNo SHOULD BE USED TO FIND THE MESH - // NEIGHBOR ON THE COARSE MESH - mfem::FaceElementTransformations FET; // XX TODO: Preallocate these for all elements mfem::IsoparametricTransformation T1, T2; if (const auto *submesh = dynamic_cast(T.mesh)) @@ -614,16 +589,14 @@ int MaterialOperator::GetAttributeGlobalToLocal(mfem::ElementTransformation &T) MFEM_ASSERT(T.ElementType == mfem::ElementTransformation::ELEMENT, "Unexpected element type in GetAttributeGlobalToLocal!"); return GetBdrNeighborAttribute(submesh->GetParentElementIDMap()[T.ElementNo], - *submesh->GetParent(), face_loc_to_shared, FET, T1, - T2); + *submesh->GetParent(), FET, T1, T2); } else { MFEM_ASSERT(T.ElementType == mfem::ElementTransformation::BDR_ELEMENT, "Unexpected element type in GetAttributeGlobalToLocal!"); - return GetBdrNeighborAttribute(T.ElementNo, - *static_cast(T.mesh), - face_loc_to_shared, FET, T1, T2); + return GetBdrNeighborAttribute( + T.ElementNo, *static_cast(T.mesh), FET, T1, T2); } }(); auto it = bdr_attr_map->second.find(nbr_attr); diff --git a/palace/models/materialoperator.hpp b/palace/models/materialoperator.hpp index 59085b6bc..255069472 100644 --- a/palace/models/materialoperator.hpp +++ b/palace/models/materialoperator.hpp @@ -35,15 +35,12 @@ class MaterialOperator // penetration depth. mfem::Array losstan_attr, conductivity_attr, london_attr; - // Shared face mapping for boundary coefficients. - std::unordered_map face_loc_to_shared; - // Attribute mapping for (global, 1-based) domain and boundary attributes to those on this // process (still 1-based). For boundaries, the inner map is a mapping from neighboring // domain attribute to the resulting local boundary attribute (to discern boundary // elements with global boundary attribute which borders more than one domain). Interior - // boundaries use as neighbor the element which corresponds to the vacuum domain, or at - // least the one with the higher speed of light. + // boundaries use as neighbor the element with the smaller domain attribute in order to + // be consistent when the interior boundary element normals are not aligned. std::unordered_map loc_attr; std::unordered_map> loc_bdr_attr; @@ -96,8 +93,6 @@ class MaterialOperator const auto &GetAttributeToMaterial() const { return attr_mat; } mfem::Array GetBdrAttributeToMaterial() const; - const auto &GetLocalToSharedFaceMap() const { return face_loc_to_shared; } - const auto &GetAttributeGlobalToLocal() const { return loc_attr; } const auto &GetBdrAttributeGlobalToLocal() const { return loc_bdr_attr; } From f947fee3ac8b873a8a1ba83f976afbcc36f16c12 Mon Sep 17 00:00:00 2001 From: Sebastian Grimberg Date: Mon, 8 Jan 2024 16:38:58 -0800 Subject: [PATCH 07/32] Fix regression in boundary coefficients postprocessing --- palace/fem/coefficient.cpp | 7 +++--- palace/fem/coefficient.hpp | 1 + palace/models/surfacepostoperator.cpp | 33 +++++++++++---------------- palace/models/surfacepostoperator.hpp | 2 -- 4 files changed, 18 insertions(+), 25 deletions(-) diff --git a/palace/fem/coefficient.cpp b/palace/fem/coefficient.cpp index 5a2e40f7f..37256889a 100644 --- a/palace/fem/coefficient.cpp +++ b/palace/fem/coefficient.cpp @@ -56,9 +56,10 @@ void BdrGridFunctionCoefficient::GetBdrElementNeighborTransformations( // orientations. if (C1) { - mfem::Vector CF(T.GetSpaceDim()); - mesh.GetFaceTransformation(T.ElementNo, &TF); - TF.Transform(mfem::Geometries.GetCenter(mesh.GetFaceGeometry(T.ElementNo)), CF); + int f = mesh.GetBdrElementFaceIndex(T.ElementNo); + CF.SetSize(T.GetSpaceDim()); + mesh.GetFaceTransformation(f, &TF); + TF.Transform(mfem::Geometries.GetCenter(mesh.GetFaceGeometry(f)), CF); C1->SetSize(T.GetSpaceDim()); FET.Elem1->Transform(mfem::Geometries.GetCenter(FET.Elem1->GetGeometryType()), *C1); diff --git a/palace/fem/coefficient.hpp b/palace/fem/coefficient.hpp index f66429980..342771adf 100644 --- a/palace/fem/coefficient.hpp +++ b/palace/fem/coefficient.hpp @@ -32,6 +32,7 @@ class BdrGridFunctionCoefficient const mfem::ParMesh &mesh; mfem::FaceElementTransformations FET; mfem::IsoparametricTransformation T1, T2, TF; + mfem::Vector CF; void GetBdrElementNeighborTransformations(mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip, diff --git a/palace/models/surfacepostoperator.cpp b/palace/models/surfacepostoperator.cpp index 57011dfc5..6a3574b92 100644 --- a/palace/models/surfacepostoperator.cpp +++ b/palace/models/surfacepostoperator.cpp @@ -80,27 +80,21 @@ std::unique_ptr SurfacePostOperator::InterfaceDielectricData::GetCoefficient( std::size_t i, const mfem::ParGridFunction &U, const MaterialOperator &mat_op) const { - auto MakeRestricted = [&](std::unique_ptr &&coeff) - { return std::make_unique(std::move(coeff), attr_lists[i]); }; switch (type) { case DielectricInterfaceType::MA: - return MakeRestricted( - std::make_unique>( - U, mat_op, ts, epsilon, sides[i])); + return std::make_unique>( + U, mat_op, ts, epsilon, sides[i]); case DielectricInterfaceType::MS: - return MakeRestricted( - std::make_unique>( - U, mat_op, ts, epsilon, sides[i])); + return std::make_unique>( + U, mat_op, ts, epsilon, sides[i]); case DielectricInterfaceType::SA: - return MakeRestricted( - std::make_unique>( - U, mat_op, ts, epsilon, sides[i])); + return std::make_unique>( + U, mat_op, ts, epsilon, sides[i]); case DielectricInterfaceType::DEFAULT: - return MakeRestricted( - std::make_unique< - DielectricInterfaceCoefficient>( - U, mat_op, ts, epsilon, sides[i])); + return std::make_unique< + DielectricInterfaceCoefficient>( + U, mat_op, ts, epsilon, sides[i]); } return {}; // For compiler warning } @@ -116,8 +110,7 @@ SurfacePostOperator::SurfaceChargeData::SurfaceChargeData( std::unique_ptr SurfacePostOperator::SurfaceChargeData::GetCoefficient( std::size_t i, const mfem::ParGridFunction &U, const MaterialOperator &mat_op) const { - return std::make_unique( - std::make_unique(U, mat_op), attr_lists[0]); + return std::make_unique(U, mat_op); } SurfacePostOperator::SurfaceFluxData::SurfaceFluxData(const config::InductanceData &data, @@ -137,8 +130,7 @@ SurfacePostOperator::SurfaceFluxData::SurfaceFluxData(const config::InductanceDa std::unique_ptr SurfacePostOperator::SurfaceFluxData::GetCoefficient( std::size_t i, const mfem::ParGridFunction &U, const MaterialOperator &mat_op) const { - return std::make_unique( - std::make_unique(U, mat_op, direction), attr_lists[0]); + return std::make_unique(U, mat_op, direction); } SurfacePostOperator::SurfacePostOperator(const IoData &iodata, @@ -258,7 +250,8 @@ double SurfacePostOperator::GetLocalSurfaceIntegral(const SurfaceData &data, mfem::Array attr_list; for (std::size_t i = 0; i < data.attr_lists.size(); i++) { - fb.AddCoefficient(data.GetCoefficient(i, U, mat_op)); + fb.AddCoefficient(std::make_unique( + data.GetCoefficient(i, U, mat_op), data.attr_lists[i])); attr_list.Append(data.attr_lists[i]); } int bdr_attr_max = mesh.bdr_attributes.Size() ? mesh.bdr_attributes.Max() : 0; diff --git a/palace/models/surfacepostoperator.hpp b/palace/models/surfacepostoperator.hpp index 49303bc4f..fc348e9ea 100644 --- a/palace/models/surfacepostoperator.hpp +++ b/palace/models/surfacepostoperator.hpp @@ -10,8 +10,6 @@ #include #include "fem/coefficient.hpp" -// XX TODO: Rename BoundaryPostOperator for config file consistency? - namespace palace { From 1a4e08d6349eb2a71bd0657618b190a69db75b4d Mon Sep 17 00:00:00 2001 From: Sebastian Grimberg Date: Mon, 8 Jan 2024 16:39:05 -0800 Subject: [PATCH 08/32] Fix regression for wave ports --- palace/models/materialoperator.hpp | 3 +++ palace/models/waveportoperator.cpp | 6 +----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/palace/models/materialoperator.hpp b/palace/models/materialoperator.hpp index 255069472..b3811d653 100644 --- a/palace/models/materialoperator.hpp +++ b/palace/models/materialoperator.hpp @@ -86,6 +86,9 @@ class MaterialOperator const auto &GetConductivity() const { return mat_sigma; } const auto &GetInvLondonDepth() const { return mat_invLondon; } + const auto &GetLightSpeedMin() const { return mat_c0_min; } + const auto &GetLightSpeedMax() const { return mat_c0_max; } + bool HasLossTangent() const { return (losstan_attr.Size() > 0); } bool HasConductivity() const { return (conductivity_attr.Size() > 0); } bool HasLondonDepth() const { return (london_attr.Size() > 0); } diff --git a/palace/models/waveportoperator.cpp b/palace/models/waveportoperator.cpp index 3e774e706..62ece9ef7 100644 --- a/palace/models/waveportoperator.cpp +++ b/palace/models/waveportoperator.cpp @@ -601,11 +601,7 @@ WavePortData::WavePortData(const config::WavePortData &data, const MaterialOpera // Reference: Lee, Sun, and Cendes, Full-wave analysis of dielectric waveguides using // tangential vector finite elements, IEEE Trans. Microwave Theory Tech. // (1991). - double c_min = mfem::infinity(); - for (auto attr : mesh.attributes) - { - c_min = std::min(c_min, mat_op.GetLightSpeedMin(attr)); - } + double c_min = mat_op.GetLightSpeedMin().Min(); MFEM_VERIFY(c_min > 0.0 && c_min < mfem::infinity(), "Invalid material speed of light detected in WavePortOperator!"); mu_eps_max = 1.0 / (c_min * c_min); From 11220572c1115e4ec57fb1aa1b13d01ee4d87c89 Mon Sep 17 00:00:00 2001 From: Sebastian Grimberg Date: Mon, 18 Dec 2023 18:55:44 -0800 Subject: [PATCH 09/32] WIP: palace::Mesh class and with it use composition instead of inheritance for palace::FiniteElementSpace --- palace/drivers/basesolver.cpp | 10 +- palace/drivers/basesolver.hpp | 12 +- palace/drivers/drivensolver.cpp | 3 +- palace/drivers/drivensolver.hpp | 10 +- palace/drivers/eigensolver.cpp | 3 +- palace/drivers/eigensolver.hpp | 10 +- palace/drivers/electrostaticsolver.cpp | 3 +- palace/drivers/electrostaticsolver.hpp | 4 +- palace/drivers/magnetostaticsolver.cpp | 3 +- palace/drivers/magnetostaticsolver.hpp | 4 +- palace/drivers/transientsolver.cpp | 3 +- palace/drivers/transientsolver.hpp | 10 +- palace/fem/CMakeLists.txt | 1 + palace/fem/fespace.cpp | 72 +++++------- palace/fem/fespace.hpp | 78 +++++++++---- palace/fem/mesh.cpp | 156 +++++++++++++++++++++++++ palace/fem/mesh.hpp | 142 ++++++++++++++++++++++ palace/fem/multigrid.hpp | 53 ++++----- palace/linalg/ams.cpp | 23 ++-- palace/linalg/ams.hpp | 15 ++- palace/linalg/errorestimator.cpp | 26 ++--- palace/linalg/ksp.cpp | 22 ++-- palace/linalg/ksp.hpp | 4 +- palace/main.cpp | 16 ++- palace/models/curlcurloperator.cpp | 9 +- palace/models/curlcurloperator.hpp | 6 +- palace/models/laplaceoperator.cpp | 7 +- palace/models/laplaceoperator.hpp | 6 +- palace/models/materialoperator.cpp | 121 +------------------ palace/models/materialoperator.hpp | 64 +--------- palace/models/postoperator.cpp | 31 ++--- palace/models/spaceoperator.cpp | 17 +-- palace/models/spaceoperator.hpp | 6 +- palace/models/waveportoperator.cpp | 64 +++++----- palace/models/waveportoperator.hpp | 3 +- 35 files changed, 589 insertions(+), 428 deletions(-) create mode 100644 palace/fem/mesh.cpp create mode 100644 palace/fem/mesh.hpp diff --git a/palace/drivers/basesolver.cpp b/palace/drivers/basesolver.cpp index 4495f41b9..b86449da4 100644 --- a/palace/drivers/basesolver.cpp +++ b/palace/drivers/basesolver.cpp @@ -11,6 +11,7 @@ #include "drivers/transientsolver.hpp" #include "fem/errorindicator.hpp" #include "fem/fespace.hpp" +#include "fem/mesh.hpp" #include "linalg/ksp.hpp" #include "models/domainpostoperator.hpp" #include "models/postoperator.hpp" @@ -136,8 +137,7 @@ BaseSolver::BaseSolver(const IoData &iodata, bool root, int size, int num_thread } } -void BaseSolver::SolveEstimateMarkRefine( - std::vector> &mesh) const +void BaseSolver::SolveEstimateMarkRefine(std::vector> &mesh) const { const auto &refinement = iodata.model.refinement; const bool use_amr = [&]() @@ -155,7 +155,7 @@ void BaseSolver::SolveEstimateMarkRefine( "the sequence of a priori refinements\n"); mesh.erase(mesh.begin(), mesh.end() - 1); constexpr bool refine = true, fix_orientation = true; - mesh.back()->Finalize(refine, fix_orientation); + mesh.back()->Get().Finalize(refine, fix_orientation); } MPI_Comm comm = mesh.back()->GetComm(); @@ -206,7 +206,7 @@ void BaseSolver::SolveEstimateMarkRefine( refinement.update_fraction); // Refine. - auto &fine_mesh = *mesh.back(); + mfem::ParMesh &fine_mesh = *mesh.back(); const auto initial_elem_count = fine_mesh.GetGlobalNE(); fine_mesh.GeneralRefinement(marked_elements, -1, refinement.max_nc_levels); const auto final_elem_count = fine_mesh.GetGlobalNE(); @@ -249,7 +249,7 @@ void BaseSolver::SaveMetadata(const FiniteElementSpaceHierarchy &fespaces) const return; } const auto &fespace = fespaces.GetFinestFESpace(); - HYPRE_BigInt ne = fespace.GetParMesh()->GetNE(); + HYPRE_BigInt ne = fespace.GetParMesh().GetNE(); Mpi::GlobalSum(1, &ne, fespace.GetComm()); std::vector ndofs(fespaces.GetNumLevels()); for (std::size_t l = 0; l < fespaces.GetNumLevels(); l++) diff --git a/palace/drivers/basesolver.hpp b/palace/drivers/basesolver.hpp index d6ca996e9..bfab97850 100644 --- a/palace/drivers/basesolver.hpp +++ b/palace/drivers/basesolver.hpp @@ -9,18 +9,12 @@ #include #include -namespace mfem -{ - -class ParMesh; - -} // namespace mfem - namespace palace { class ErrorIndicator; class FiniteElementSpaceHierarchy; +class Mesh; class IoData; class PostOperator; class Timer; @@ -83,7 +77,7 @@ class BaseSolver // Performs a solve using the mesh sequence, then reports error indicators and the number // of global true dofs. virtual std::pair - Solve(const std::vector> &mesh) const = 0; + Solve(const std::vector> &mesh) const = 0; public: BaseSolver(const IoData &iodata, bool root, int size = 0, int num_thread = 0, @@ -92,7 +86,7 @@ class BaseSolver // Performs adaptive mesh refinement using the solve-estimate-mark-refine paradigm. // Dispatches to the Solve method for the driver specific calculations. - void SolveEstimateMarkRefine(std::vector> &mesh) const; + void SolveEstimateMarkRefine(std::vector> &mesh) const; // These methods write different simulation metadata to a JSON file in post_dir. void SaveMetadata(const FiniteElementSpaceHierarchy &fespaces) const; diff --git a/palace/drivers/drivensolver.cpp b/palace/drivers/drivensolver.cpp index 8744885a6..d5f1f9ba6 100644 --- a/palace/drivers/drivensolver.cpp +++ b/palace/drivers/drivensolver.cpp @@ -6,6 +6,7 @@ #include #include #include "fem/errorindicator.hpp" +#include "fem/mesh.hpp" #include "linalg/errorestimator.hpp" #include "linalg/ksp.hpp" #include "linalg/operator.hpp" @@ -27,7 +28,7 @@ namespace palace using namespace std::complex_literals; std::pair -DrivenSolver::Solve(const std::vector> &mesh) const +DrivenSolver::Solve(const std::vector> &mesh) const { // Set up the spatial discretization and frequency sweep. BlockTimer bt0(Timer::CONSTRUCT); diff --git a/palace/drivers/drivensolver.hpp b/palace/drivers/drivensolver.hpp index 2a92b9e99..fbebbf44f 100644 --- a/palace/drivers/drivensolver.hpp +++ b/palace/drivers/drivensolver.hpp @@ -8,19 +8,13 @@ #include #include "drivers/basesolver.hpp" -namespace mfem -{ - -class ParMesh; - -} // namespace mfem - namespace palace { class ErrorIndicator; class IoData; class LumpedPortOperator; +class Mesh; class PostOperator; class SpaceOperator; class SurfaceCurrentOperator; @@ -61,7 +55,7 @@ class DrivenSolver : public BaseSolver double omega) const; std::pair - Solve(const std::vector> &mesh) const override; + Solve(const std::vector> &mesh) const override; public: using BaseSolver::BaseSolver; diff --git a/palace/drivers/eigensolver.cpp b/palace/drivers/eigensolver.cpp index 3736558e7..39c7f0471 100644 --- a/palace/drivers/eigensolver.cpp +++ b/palace/drivers/eigensolver.cpp @@ -5,6 +5,7 @@ #include #include "fem/errorindicator.hpp" +#include "fem/mesh.hpp" #include "linalg/arpack.hpp" #include "linalg/divfree.hpp" #include "linalg/errorestimator.hpp" @@ -25,7 +26,7 @@ namespace palace using namespace std::complex_literals; std::pair -EigenSolver::Solve(const std::vector> &mesh) const +EigenSolver::Solve(const std::vector> &mesh) const { // Construct and extract the system matrices defining the eigenvalue problem. The diagonal // values for the mass matrix PEC dof shift the Dirichlet eigenvalues out of the diff --git a/palace/drivers/eigensolver.hpp b/palace/drivers/eigensolver.hpp index 45077717d..291199e16 100644 --- a/palace/drivers/eigensolver.hpp +++ b/palace/drivers/eigensolver.hpp @@ -9,19 +9,13 @@ #include #include "drivers/basesolver.hpp" -namespace mfem -{ - -class ParMesh; - -} // namespace mfem - namespace palace { class ErrorIndicator; class IoData; class LumpedPortOperator; +class Mesh; class PostOperator; class Timer; @@ -45,7 +39,7 @@ class EigenSolver : public BaseSolver int i, std::complex omega, double Em) const; std::pair - Solve(const std::vector> &mesh) const override; + Solve(const std::vector> &mesh) const override; public: using BaseSolver::BaseSolver; diff --git a/palace/drivers/electrostaticsolver.cpp b/palace/drivers/electrostaticsolver.cpp index 70527b036..09812d13f 100644 --- a/palace/drivers/electrostaticsolver.cpp +++ b/palace/drivers/electrostaticsolver.cpp @@ -5,6 +5,7 @@ #include #include "fem/errorindicator.hpp" +#include "fem/mesh.hpp" #include "linalg/errorestimator.hpp" #include "linalg/ksp.hpp" #include "linalg/operator.hpp" @@ -18,7 +19,7 @@ namespace palace { std::pair -ElectrostaticSolver::Solve(const std::vector> &mesh) const +ElectrostaticSolver::Solve(const std::vector> &mesh) const { // Construct the system matrix defining the linear operator. Dirichlet boundaries are // handled eliminating the rows and columns of the system matrix for the corresponding diff --git a/palace/drivers/electrostaticsolver.hpp b/palace/drivers/electrostaticsolver.hpp index 2e503110b..948259a38 100644 --- a/palace/drivers/electrostaticsolver.hpp +++ b/palace/drivers/electrostaticsolver.hpp @@ -16,7 +16,6 @@ namespace mfem template class Array; class DenseMatrix; -class ParMesh; } // namespace mfem @@ -26,6 +25,7 @@ namespace palace class ErrorIndicator; class IoData; class LaplaceOperator; +class Mesh; class PostOperator; class Timer; @@ -43,7 +43,7 @@ class ElectrostaticSolver : public BaseSolver const mfem::DenseMatrix &Cm) const; std::pair - Solve(const std::vector> &mesh) const override; + Solve(const std::vector> &mesh) const override; public: using BaseSolver::BaseSolver; diff --git a/palace/drivers/magnetostaticsolver.cpp b/palace/drivers/magnetostaticsolver.cpp index 64172feab..6c25c6e7e 100644 --- a/palace/drivers/magnetostaticsolver.cpp +++ b/palace/drivers/magnetostaticsolver.cpp @@ -5,6 +5,7 @@ #include #include "fem/errorindicator.hpp" +#include "fem/mesh.hpp" #include "linalg/errorestimator.hpp" #include "linalg/ksp.hpp" #include "linalg/operator.hpp" @@ -19,7 +20,7 @@ namespace palace { std::pair -MagnetostaticSolver::Solve(const std::vector> &mesh) const +MagnetostaticSolver::Solve(const std::vector> &mesh) const { // Construct the system matrix defining the linear operator. Dirichlet boundaries are // handled eliminating the rows and columns of the system matrix for the corresponding diff --git a/palace/drivers/magnetostaticsolver.hpp b/palace/drivers/magnetostaticsolver.hpp index db6a08d8d..41cfd7bb2 100644 --- a/palace/drivers/magnetostaticsolver.hpp +++ b/palace/drivers/magnetostaticsolver.hpp @@ -13,7 +13,6 @@ namespace mfem { class DenseMatrix; -class ParMesh; } // namespace mfem @@ -23,6 +22,7 @@ namespace palace class CurlCurlOperator; class ErrorIndicator; class IoData; +class Mesh; class PostOperator; class SurfaceCurrentOperator; class Timer; @@ -41,7 +41,7 @@ class MagnetostaticSolver : public BaseSolver const mfem::DenseMatrix &Mm) const; std::pair - Solve(const std::vector> &mesh) const override; + Solve(const std::vector> &mesh) const override; public: using BaseSolver::BaseSolver; diff --git a/palace/drivers/transientsolver.cpp b/palace/drivers/transientsolver.cpp index ee293fe7f..a5dbca955 100644 --- a/palace/drivers/transientsolver.cpp +++ b/palace/drivers/transientsolver.cpp @@ -5,6 +5,7 @@ #include #include "fem/errorindicator.hpp" +#include "fem/mesh.hpp" #include "linalg/errorestimator.hpp" #include "linalg/vector.hpp" #include "models/lumpedportoperator.hpp" @@ -21,7 +22,7 @@ namespace palace { std::pair -TransientSolver::Solve(const std::vector> &mesh) const +TransientSolver::Solve(const std::vector> &mesh) const { // Set up the spatial discretization and time integrators for the E and B fields. BlockTimer bt0(Timer::CONSTRUCT); diff --git a/palace/drivers/transientsolver.hpp b/palace/drivers/transientsolver.hpp index 26a0e4b14..5ee860194 100644 --- a/palace/drivers/transientsolver.hpp +++ b/palace/drivers/transientsolver.hpp @@ -9,19 +9,13 @@ #include #include "drivers/basesolver.hpp" -namespace mfem -{ - -class ParMesh; - -} // namespace mfem - namespace palace { class ErrorIndicator; class IoData; class LumpedPortOperator; +class Mesh; class PostOperator; class SurfaceCurrentOperator; class Timer; @@ -50,7 +44,7 @@ class TransientSolver : public BaseSolver double J_coef) const; std::pair - Solve(const std::vector> &mesh) const override; + Solve(const std::vector> &mesh) const override; public: using BaseSolver::BaseSolver; diff --git a/palace/fem/CMakeLists.txt b/palace/fem/CMakeLists.txt index 714e5b801..2ee66a422 100644 --- a/palace/fem/CMakeLists.txt +++ b/palace/fem/CMakeLists.txt @@ -14,6 +14,7 @@ target_sources(${LIB_TARGET_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/integrator.cpp ${CMAKE_CURRENT_SOURCE_DIR}/interpolator.cpp ${CMAKE_CURRENT_SOURCE_DIR}/lumpedelement.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/mesh.cpp ${CMAKE_CURRENT_SOURCE_DIR}/integ/curlcurl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/integ/curlcurlmass.cpp ${CMAKE_CURRENT_SOURCE_DIR}/integ/diffusion.cpp diff --git a/palace/fem/fespace.cpp b/palace/fem/fespace.cpp index 69b07ea25..ba74a1f1b 100644 --- a/palace/fem/fespace.cpp +++ b/palace/fem/fespace.cpp @@ -11,17 +11,25 @@ namespace palace { -std::size_t FiniteElementSpace::global_id = 0; +std::size_t FiniteElementSpace::GetGlobalId() +{ + static std::size_t global_id = 0; + std::size_t id; + PalacePragmaOmp(critical(GetGlobalId)) + { + id = global_id++; + } + return id; +} std::size_t FiniteElementSpace::GetId() const { PalacePragmaOmp(critical(GetId)) { - if (!init || GetSequence() != prev_sequence) + if (sequence != fespace.GetSequence()) { - id = global_id++; - prev_sequence = GetSequence(); - init = true; + id = GetGlobalId(); + sequence = fespace.GetSequence(); } } return id; @@ -29,62 +37,40 @@ std::size_t FiniteElementSpace::GetId() const const Operator &AuxiliaryFiniteElementSpace::BuildDiscreteInterpolator() const { - // G is always partially assembled. - const int dim = GetParMesh()->Dimension(); - const auto aux_map_type = FEColl()->GetMapType(dim); - const auto primal_map_type = primal_fespace.FEColl()->GetMapType(dim); + // Allow finite element spaces to be swapped in their order (intended as deriv(aux) -> + // primal). G is always partially assembled. + const int dim = Dimension(); + const bool swap = + (GetFEColl().GetMapType(dim) == primal_fespace.GetFEColl().GetDerivMapType(dim)); + const FiniteElementSpace &trial_fespace = swap ? primal_fespace : *this; + const FiniteElementSpace &test_fespace = swap ? *this : primal_fespace; + const auto aux_map_type = trial_fespace.GetFEColl().GetMapType(dim); + const auto primal_map_type = test_fespace.GetFEColl().GetMapType(dim); if (aux_map_type == mfem::FiniteElement::VALUE && primal_map_type == mfem::FiniteElement::H_CURL) { // Discrete gradient interpolator - DiscreteLinearOperator interp(*this, primal_fespace); - interp.AddDomainInterpolator(); - G = std::make_unique(interp.PartialAssemble(), *this, primal_fespace, - true); - } - else if (primal_map_type == mfem::FiniteElement::VALUE && - aux_map_type == mfem::FiniteElement::H_CURL) - { - // Discrete gradient interpolator (spaces reversed) - DiscreteLinearOperator interp(primal_fespace, *this); + DiscreteLinearOperator interp(trial_fespace, test_fespace); interp.AddDomainInterpolator(); - G = std::make_unique(interp.PartialAssemble(), primal_fespace, *this, + G = std::make_unique(interp.PartialAssemble(), trial_fespace, test_fespace, true); } else if (aux_map_type == mfem::FiniteElement::H_CURL && primal_map_type == mfem::FiniteElement::H_DIV) { // Discrete curl interpolator - DiscreteLinearOperator interp(*this, primal_fespace); + DiscreteLinearOperator interp(trial_fespace, test_fespace); interp.AddDomainInterpolator(); - G = std::make_unique(interp.PartialAssemble(), *this, primal_fespace, - true); - } - else if (primal_map_type == mfem::FiniteElement::H_CURL && - aux_map_type == mfem::FiniteElement::H_DIV) - { - // Discrete curl interpolator (spaces reversed) - DiscreteLinearOperator interp(primal_fespace, *this); - interp.AddDomainInterpolator(); - G = std::make_unique(interp.PartialAssemble(), primal_fespace, *this, + G = std::make_unique(interp.PartialAssemble(), trial_fespace, test_fespace, true); } else if (aux_map_type == mfem::FiniteElement::H_DIV && primal_map_type == mfem::FiniteElement::INTEGRAL) { // Discrete divergence interpolator - DiscreteLinearOperator interp(*this, primal_fespace); - interp.AddDomainInterpolator(); - G = std::make_unique(interp.PartialAssemble(), *this, primal_fespace, - true); - } - else if (primal_map_type == mfem::FiniteElement::H_DIV && - aux_map_type == mfem::FiniteElement::INTEGRAL) - { - // Discrete divergence interpolator (spaces reversed) - DiscreteLinearOperator interp(primal_fespace, *this); + DiscreteLinearOperator interp(trial_fespace, test_fespace); interp.AddDomainInterpolator(); - G = std::make_unique(interp.PartialAssemble(), primal_fespace, *this, + G = std::make_unique(interp.PartialAssemble(), trial_fespace, test_fespace, true); } else @@ -104,7 +90,7 @@ BaseFiniteElementSpaceHierarchy::BuildProlongationAtLevel(std::size_t l MFEM_VERIFY(l >= 0 && l < GetNumLevels() - 1, "Can only construct a finite element space prolongation with more than one " "space in the hierarchy!"); - if (fespaces[l]->GetParMesh() != fespaces[l + 1]->GetParMesh()) + if (&fespaces[l]->GetMesh() != &fespaces[l + 1]->GetMesh()) { P[l] = std::make_unique( std::make_unique(*fespaces[l], *fespaces[l + 1]), diff --git a/palace/fem/fespace.hpp b/palace/fem/fespace.hpp index 05d495e89..6328fb3de 100644 --- a/palace/fem/fespace.hpp +++ b/palace/fem/fespace.hpp @@ -7,35 +7,73 @@ #include #include #include +#include "fem/mesh.hpp" #include "linalg/operator.hpp" namespace palace { // -// Wrapper for MFEM's ParFiniteElementSpace class, where the finite element space object -// is constructed with a unique ID associated with it. This is useful for defining equality -// operations between spaces (either different spaces on the same mesh, or the same space -// type on different meshes). +// Wrapper for MFEM's ParFiniteElementSpace class, with extensions for Palace. // -class FiniteElementSpace : public mfem::ParFiniteElementSpace +class FiniteElementSpace { private: - static std::size_t global_id; + // Underlying MFEM object. + mfem::ParFiniteElementSpace fespace; + + // Reference to the underlying mesh object (not owned). + Mesh &mesh; + + // Members used to define equality between two spaces. + mutable long int sequence; mutable std::size_t id; - mutable long int prev_sequence; - mutable bool init = false; + static std::size_t GetGlobalId(); public: - using mfem::ParFiniteElementSpace::ParFiniteElementSpace; - FiniteElementSpace(const mfem::ParFiniteElementSpace &fespace) - : mfem::ParFiniteElementSpace(fespace) + template + FiniteElementSpace(Mesh &mesh, T &&...args) + : fespace(&mesh.Get(), std::forward(args)...), mesh(mesh), + sequence(fespace.GetSequence()), id(GetGlobalId()) { } + virtual ~FiniteElementSpace() = default; + + const auto &Get() const { return fespace; } + auto &Get() { return fespace; } + + operator const mfem::ParFiniteElementSpace &() const { return Get(); } + operator mfem::ParFiniteElementSpace &() { return Get(); } + + const auto &GetFEColl() const { return *Get().FEColl(); } + auto &GetFEColl() { return *Get().FEColl(); } + + const auto &GetMesh() const { return mesh; } + auto &GetMesh() { return mesh; } + + const auto &GetParMesh() const { return mesh.Get(); } + auto &GetParMesh() { return mesh.Get(); } + + auto GetVDim() const { return Get().GetVDim(); } + auto GetVSize() const { return Get().GetVSize(); } + auto GetTrueVSize() const { return Get().GetTrueVSize(); } + auto GlobalTrueVSize() const { return Get().GlobalTrueVSize(); } + auto Dimension() const { return mesh.Get().Dimension(); } + auto SpaceDimension() const { return mesh.Get().SpaceDimension(); } + auto GetMaxElementOrder() const { return Get().GetMaxElementOrder(); } // Get the ID associated with the instance of this class. If the underlying sequence has // changed (due to a mesh update, for example), regenerate the ID. std::size_t GetId() const; + + // Operator overload for equality comparisons between two spaces. + bool operator==(const FiniteElementSpace &fespace) const + { + return GetId() == fespace.GetId(); + } + + // Get the associated MPI communicator. + MPI_Comm GetComm() const { return fespace.GetComm(); } }; // @@ -60,7 +98,7 @@ class AuxiliaryFiniteElementSpace : public FiniteElementSpace // Return the discrete gradient or discrete curl matrix interpolating from the auxiliary // to the primal space, constructing it on the fly as necessary. - const Operator &GetDiscreteInterpolator() const + const auto &GetDiscreteInterpolator() const { return G ? *G : BuildDiscreteInterpolator(); } @@ -83,8 +121,8 @@ class BaseFiniteElementSpaceHierarchy const Operator &BuildProlongationAtLevel(std::size_t l) const; public: - BaseFiniteElementSpaceHierarchy() = default; - BaseFiniteElementSpaceHierarchy(std::unique_ptr &&fespace) + BaseFiniteElementSpaceHierarchy() = default; + BaseFiniteElementSpaceHierarchy(std::unique_ptr &&fespace) { AddLevel(std::move(fespace)); } @@ -97,33 +135,33 @@ class BaseFiniteElementSpaceHierarchy P.push_back(nullptr); } - FESpace &GetFESpaceAtLevel(std::size_t l) + auto &GetFESpaceAtLevel(std::size_t l) { MFEM_ASSERT(l >= 0 && l < GetNumLevels(), "Out of bounds request for finite element space at level " << l << "!"); return *fespaces[l]; } - const FESpace &GetFESpaceAtLevel(std::size_t l) const + const auto &GetFESpaceAtLevel(std::size_t l) const { MFEM_ASSERT(l >= 0 && l < GetNumLevels(), "Out of bounds request for finite element space at level " << l << "!"); return *fespaces[l]; } - FESpace &GetFinestFESpace() + auto &GetFinestFESpace() { MFEM_ASSERT(GetNumLevels() > 0, "Out of bounds request for finite element space at level 0!"); return *fespaces.back(); } - const FESpace &GetFinestFESpace() const + const auto &GetFinestFESpace() const { MFEM_ASSERT(GetNumLevels() > 0, "Out of bounds request for finite element space at level 0!"); return *fespaces.back(); } - const Operator &GetProlongationAtLevel(std::size_t l) const + const auto &GetProlongationAtLevel(std::size_t l) const { MFEM_ASSERT(l >= 0 && l < GetNumLevels() - 1, "Out of bounds request for finite element space prolongation at level " @@ -161,7 +199,7 @@ class AuxiliaryFiniteElementSpaceHierarchy using BaseFiniteElementSpaceHierarchy< AuxiliaryFiniteElementSpace>::BaseFiniteElementSpaceHierarchy; - const Operator &GetDiscreteInterpolatorAtLevel(std::size_t l) const + const auto &GetDiscreteInterpolatorAtLevel(std::size_t l) const { return GetFESpaceAtLevel(l).GetDiscreteInterpolator(); } diff --git a/palace/fem/mesh.cpp b/palace/fem/mesh.cpp new file mode 100644 index 000000000..71740c4c6 --- /dev/null +++ b/palace/fem/mesh.cpp @@ -0,0 +1,156 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#include "mesh.hpp" + +#include "fem/coefficient.hpp" +#include "fem/fespace.hpp" + +namespace palace +{ + +namespace +{ + +const auto &GetParentMesh(const mfem::ParMesh &mesh) +{ + // Get the parent mesh if the mesh is a boundary submesh (no submesh of submesh + // capabilities, for now). + const auto *submesh = dynamic_cast(&mesh); + if (submesh && submesh->GetFrom() == mfem::SubMesh::From::Boundary) + { + return *submesh->GetParent(); + } + return mesh; +} + +auto &GetParentMesh(mfem::ParMesh &mesh) +{ + return const_cast( + GetParentMesh(const_cast(mesh))); +} + +auto BuildAttributeGlobalToLocal(const mfem::ParMesh &mesh) +{ + // Set up sparse map from global domain attributes to local ones on this process. + // Include ghost elements for all shared faces so we have their material properties + // stored locally. + std::unordered_map loc_attr; + mfem::FaceElementTransformations FET; + mfem::IsoparametricTransformation T1, T2; + int count = 0; + for (int i = 0; i < mesh.GetNE(); i++) + { + const int attr = mesh.GetAttribute(i); + if (loc_attr.find(attr) == loc_attr.end()) + { + loc_attr[attr] = ++count; + } + } + for (int i = 0; i < mesh.GetNSharedFaces(); i++) + { + mesh.GetSharedFaceTransformations(i, &FET, &T1, &T2); + int attr = FET.Elem1->Attribute; + if (loc_attr.find(attr) == loc_attr.end()) + { + loc_attr[attr] = ++count; + } + attr = FET.Elem2->Attribute; + if (loc_attr.find(attr) == loc_attr.end()) + { + loc_attr[attr] = ++count; + } + } + return loc_attr; +} + +auto GetBdrNeighborAttribute(int i, const mfem::ParMesh &mesh, + mfem::FaceElementTransformations &FET, + mfem::IsoparametricTransformation &T1, + mfem::IsoparametricTransformation &T2) +{ + // For internal boundaries, use the element which corresponds to the domain with lower + // attribute number (ensures all boundary elements are aligned). + BdrGridFunctionCoefficient::GetBdrElementNeighborTransformations(i, mesh, FET, T1, T2); + return (FET.Elem2 && FET.Elem2->Attribute < FET.Elem1->Attribute) ? FET.Elem2->Attribute + : FET.Elem1->Attribute; +} + +auto BuildBdrAttributeGlobalToLocal(const mfem::ParMesh &mesh) +{ + // Set up sparse map from global boundary attributes to local ones on this process. Each + // original global boundary attribute maps to a key-value pairing of global domain + // attributes which neighbor the given boundary and local boundary attributes. + std::unordered_map> loc_bdr_attr; + mfem::FaceElementTransformations FET; + mfem::IsoparametricTransformation T1, T2; + int count = 0; + for (int i = 0; i < mesh.GetNBE(); i++) + { + const int attr = mesh.GetBdrAttribute(i); + const int nbr_attr = GetBdrNeighborAttribute(i, mesh, FET, T1, T2); + auto &bdr_attr_map = loc_bdr_attr[attr]; + if (bdr_attr_map.find(nbr_attr) == bdr_attr_map.end()) + { + bdr_attr_map[nbr_attr] = ++count; + } + } + return loc_bdr_attr; +} + +} // namespace + +void Mesh::Rebuild() const +{ + // Attribute mappings, etc. are always constructed for the parent mesh (use boundary + // attribute maps for the domain attributes of a boundary submesh, for example). + auto &parent_mesh = GetParentMesh(*mesh); + parent_mesh.ExchangeFaceNbrData(); + loc_attr.clear(); + loc_bdr_attr.clear(); + loc_attr = BuildAttributeGlobalToLocal(parent_mesh); + loc_bdr_attr = BuildBdrAttributeGlobalToLocal(parent_mesh); +} + +int Mesh::GetAttributeGlobalToLocal(const mfem::ElementTransformation &T) const +{ + if (T.GetDimension() == T.GetSpaceDim()) + { + // Domain element. + auto it = loc_attr.find(T.Attribute); + MFEM_ASSERT(it != loc_attr.end(), "Invalid domain attribute " << T.Attribute << "!"); + return it->second; + } + else + { + // Boundary element (or boundary submesh domain). + auto bdr_attr_map = loc_bdr_attr.find(T.Attribute); + MFEM_ASSERT(bdr_attr_map != loc_bdr_attr.end(), + "Invalid domain attribute " << T.Attribute << "!"); + const int nbr_attr = [&]() + { + mfem::FaceElementTransformations FET; // XX TODO: Preallocate these for all elements + mfem::IsoparametricTransformation T1, T2; + if (const auto *submesh = dynamic_cast(T.mesh)) + { + MFEM_ASSERT(T.ElementType == mfem::ElementTransformation::ELEMENT, + "Unexpected element type in GetAttributeGlobalToLocal!"); + return GetBdrNeighborAttribute(submesh->GetParentElementIDMap()[T.ElementNo], + *submesh->GetParent(), FET, T1, T2); + } + else + { + MFEM_ASSERT(T.ElementType == mfem::ElementTransformation::BDR_ELEMENT, + "Unexpected element type in GetAttributeGlobalToLocal!"); + return GetBdrNeighborAttribute( + T.ElementNo, *static_cast(T.mesh), FET, T1, T2); + } + }(); + auto it = bdr_attr_map->second.find(nbr_attr); + MFEM_ASSERT(it != bdr_attr_map->second.end(), + "Invalid domain attribute " << nbr_attr << "!"); + return it->second; + } +} + +} // namespace palace diff --git a/palace/fem/mesh.hpp b/palace/fem/mesh.hpp new file mode 100644 index 000000000..9425d88c6 --- /dev/null +++ b/palace/fem/mesh.hpp @@ -0,0 +1,142 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef PALACE_FEM_MESH_HPP +#define PALACE_FEM_MESH_HPP + +#include +#include +#include +#include + +namespace palace +{ + +// +// Wrapper for MFEM's ParMesh class, with extensions for Palace. +// +class Mesh +{ +private: + // Underlying MFEM object (can also point to a derived class of mfem::ParMesh, such as + // mfem::ParSubMesh). + mutable std::unique_ptr mesh; + + // Sequence to track mfem::Mesh::sequence and determine if geometry factors need updating. + mutable long int sequence; + + // Attribute mapping for (global, 1-based) domain and boundary attributes to those on this + // process (still 1-based). For boundaries, the inner map is a mapping from neighboring + // domain attribute to the resulting local boundary attribute (to discern boundary + // elements with global boundary attribute which borders more than one domain). Interior + // boundaries use as neighbor the element with the smaller domain attribute in order to + // be consistent when the interior boundary element normals are not aligned. + mutable std::unordered_map loc_attr; + mutable std::unordered_map> loc_bdr_attr; + + void CheckSequenceRebuild() const + { + if (sequence != mesh->GetSequence()) + { + Rebuild(); + sequence = mesh->GetSequence(); + } + } + void Rebuild() const; + +public: + template + Mesh(std::unique_ptr &&mesh) : mesh(std::move(mesh)) + { + this->mesh->EnsureNodes(); + Rebuild(); + sequence = this->mesh->GetSequence(); + } + + template + Mesh(T &&...args) : Mesh(std::make_unique(std::forward(args)...)) + { + } + + const auto &Get() const { return *mesh; } + auto &Get() { return *mesh; } + + operator const mfem::ParMesh &() const { return Get(); } + operator mfem::ParMesh &() { return Get(); } + + operator const std::unique_ptr &() const { return mesh; } + operator std::unique_ptr &() { return mesh; } + + auto Dimension() const { return Get().Dimension(); } + auto SpaceDimension() const { return Get().SpaceDimension(); } + auto GetNE() const { return Get().GetNE(); } + auto GetNBE() const { return Get().GetNBE(); } + + const auto &GetAttributeGlobalToLocal() const + { + CheckSequenceRebuild(); + return loc_attr; + } + + const auto &GetBdrAttributeGlobalToLocal() const + { + CheckSequenceRebuild(); + return loc_bdr_attr; + } + + template + auto GetAttributeGlobalToLocal(const T &attr_list) const + { + // Skip any entries in the input global attribute list which are not on local to this + // process. + const auto &loc_attr = GetAttributeGlobalToLocal(); + mfem::Array loc_attr_list; + for (auto attr : attr_list) + { + if (loc_attr.find(attr) != loc_attr.end()) + { + loc_attr_list.Append(loc_attr.at(attr)); + } + } + return loc_attr_list; + } + + template + auto GetBdrAttributeGlobalToLocal(const T &attr_list) const + { + // Skip any entries in the input global boundary attribute list which are not on local + // to this process. + const auto &loc_bdr_attr = GetBdrAttributeGlobalToLocal(); + mfem::Array loc_attr_list; + for (auto attr : attr_list) + { + if (loc_bdr_attr.find(attr) != loc_bdr_attr.end()) + { + const auto &bdr_attr_map = loc_bdr_attr.at(attr); + for (auto it = bdr_attr_map.begin(); it != bdr_attr_map.end(); ++it) + { + loc_attr_list.Append(it->second); + } + } + } + return loc_attr_list; + } + + auto GetAttributeGlobalToLocal(const int attr) const + { + return GetAttributeGlobalToLocal(std::vector{attr}); + } + + auto GetBdrAttributeGlobalToLocal(const int attr) const + { + return GetBdrAttributeGlobalToLocal(std::vector{attr}); + } + + int GetAttributeGlobalToLocal(const mfem::ElementTransformation &T) const; + + MPI_Comm GetComm() const { return mesh->GetComm(); } +}; + +} // namespace palace + +#endif // PALACE_FEM_MESH_HPP diff --git a/palace/fem/multigrid.hpp b/palace/fem/multigrid.hpp index 0ea5b6ec0..33537c91f 100644 --- a/palace/fem/multigrid.hpp +++ b/palace/fem/multigrid.hpp @@ -8,6 +8,7 @@ #include #include #include "fem/fespace.hpp" +#include "fem/mesh.hpp" #include "utils/geodata.hpp" #include "utils/iodata.hpp" @@ -76,7 +77,7 @@ ConstructFECollections(int p, int dim, int mg_max_levels, // element collections. Additionally, Dirichlet boundary conditions are marked. template inline FiniteElementSpaceHierarchy ConstructFiniteElementSpaceHierarchy( - int mg_max_levels, const std::vector> &mesh, + int mg_max_levels, const std::vector> &mesh, const std::vector> &fecs, const mfem::Array *dbc_attr = nullptr, std::vector> *dbc_tdof_lists = nullptr) @@ -87,39 +88,38 @@ inline FiniteElementSpaceHierarchy ConstructFiniteElementSpaceHierarchy( int coarse_mesh_l = std::max(0, static_cast(mesh.size() + fecs.size()) - 1 - std::max(1, mg_max_levels)); FiniteElementSpaceHierarchy fespaces( - std::make_unique(mesh[coarse_mesh_l].get(), fecs[0].get())); + std::make_unique(*mesh[coarse_mesh_l], fecs[0].get())); mfem::Array dbc_marker; if (dbc_attr && dbc_tdof_lists) { - int bdr_attr_max = mesh[coarse_mesh_l]->bdr_attributes.Size() - ? mesh[coarse_mesh_l]->bdr_attributes.Max() + int bdr_attr_max = mesh[coarse_mesh_l]->Get().bdr_attributes.Size() + ? mesh[coarse_mesh_l]->Get().bdr_attributes.Max() : 0; dbc_marker = mesh::AttrToMarker(bdr_attr_max, *dbc_attr); - fespaces.GetFinestFESpace().GetEssentialTrueDofs(dbc_marker, - dbc_tdof_lists->emplace_back()); + fespaces.GetFinestFESpace().Get().GetEssentialTrueDofs(dbc_marker, + dbc_tdof_lists->emplace_back()); } // h-refinement for (std::size_t l = coarse_mesh_l + 1; l < mesh.size(); l++) { - fespaces.AddLevel(std::make_unique(mesh[l].get(), fecs[0].get())); + fespaces.AddLevel(std::make_unique(*mesh[l], fecs[0].get())); if (dbc_attr && dbc_tdof_lists) { - fespaces.GetFinestFESpace().GetEssentialTrueDofs(dbc_marker, - dbc_tdof_lists->emplace_back()); + fespaces.GetFinestFESpace().Get().GetEssentialTrueDofs( + dbc_marker, dbc_tdof_lists->emplace_back()); } } // p-refinement for (std::size_t l = 1; l < fecs.size(); l++) { - fespaces.AddLevel( - std::make_unique(mesh.back().get(), fecs[l].get())); + fespaces.AddLevel(std::make_unique(*mesh.back(), fecs[l].get())); if (dbc_attr && dbc_tdof_lists) { - fespaces.GetFinestFESpace().GetEssentialTrueDofs(dbc_marker, - dbc_tdof_lists->emplace_back()); + fespaces.GetFinestFESpace().Get().GetEssentialTrueDofs( + dbc_marker, dbc_tdof_lists->emplace_back()); } } @@ -139,38 +139,39 @@ inline AuxiliaryFiniteElementSpaceHierarchy ConstructAuxiliaryFiniteElementSpace MFEM_VERIFY((primal_fespaces.GetNumLevels() > 0) && !fecs.empty() && (!dbc_tdof_lists || dbc_tdof_lists->empty()), "Empty mesh or FE collection for FE space construction!"); - mfem::ParMesh *mesh = primal_fespaces.GetFESpaceAtLevel(0).GetParMesh(); + Mesh *mesh = &primal_fespaces.GetFESpaceAtLevel(0).GetMesh(); AuxiliaryFiniteElementSpaceHierarchy fespaces( std::make_unique(primal_fespaces.GetFESpaceAtLevel(0), - mesh, fecs[0].get())); + *mesh, fecs[0].get())); mfem::Array dbc_marker; if (dbc_attr && dbc_tdof_lists) { - int bdr_attr_max = mesh->bdr_attributes.Size() ? mesh->bdr_attributes.Max() : 0; + int bdr_attr_max = + mesh->Get().bdr_attributes.Size() ? mesh->Get().bdr_attributes.Max() : 0; dbc_marker = mesh::AttrToMarker(bdr_attr_max, *dbc_attr); - fespaces.GetFinestFESpace().GetEssentialTrueDofs(dbc_marker, - dbc_tdof_lists->emplace_back()); + fespaces.GetFinestFESpace().Get().GetEssentialTrueDofs(dbc_marker, + dbc_tdof_lists->emplace_back()); } // h-refinement std::size_t l; for (l = 1; l < primal_fespaces.GetNumLevels(); l++) { - if (primal_fespaces.GetFESpaceAtLevel(l).GetParMesh() == mesh) + if (&primal_fespaces.GetFESpaceAtLevel(l).GetMesh() == mesh) { break; } fespaces.AddLevel(std::make_unique( primal_fespaces.GetFESpaceAtLevel(l), - primal_fespaces.GetFESpaceAtLevel(l).GetParMesh(), fecs[0].get())); + primal_fespaces.GetFESpaceAtLevel(l).GetMesh(), fecs[0].get())); if (dbc_attr && dbc_tdof_lists) { - fespaces.GetFinestFESpace().GetEssentialTrueDofs(dbc_marker, - dbc_tdof_lists->emplace_back()); + fespaces.GetFinestFESpace().Get().GetEssentialTrueDofs( + dbc_marker, dbc_tdof_lists->emplace_back()); } - mesh = primal_fespaces.GetFESpaceAtLevel(l).GetParMesh(); + mesh = &primal_fespaces.GetFESpaceAtLevel(l).GetMesh(); } // p-refinement @@ -178,11 +179,11 @@ inline AuxiliaryFiniteElementSpaceHierarchy ConstructAuxiliaryFiniteElementSpace for (; l < primal_fespaces.GetNumLevels(); l++) { fespaces.AddLevel(std::make_unique( - primal_fespaces.GetFESpaceAtLevel(l), mesh, fecs[l - l0].get())); + primal_fespaces.GetFESpaceAtLevel(l), *mesh, fecs[l - l0].get())); if (dbc_attr && dbc_tdof_lists) { - fespaces.GetFinestFESpace().GetEssentialTrueDofs(dbc_marker, - dbc_tdof_lists->emplace_back()); + fespaces.GetFinestFESpace().Get().GetEssentialTrueDofs( + dbc_marker, dbc_tdof_lists->emplace_back()); } } diff --git a/palace/linalg/ams.cpp b/palace/linalg/ams.cpp index 21925df15..bbd97f8cb 100644 --- a/palace/linalg/ams.cpp +++ b/palace/linalg/ams.cpp @@ -11,16 +11,15 @@ namespace palace { -HypreAmsSolver::HypreAmsSolver(const FiniteElementSpace &nd_fespace, - const AuxiliaryFiniteElementSpace &h1_fespace, int cycle_it, +HypreAmsSolver::HypreAmsSolver(FiniteElementSpace &nd_fespace, + AuxiliaryFiniteElementSpace &h1_fespace, int cycle_it, int smooth_it, int agg_coarsen, bool vector_interp, bool op_singular, int print) : mfem::HypreSolver(), // From the Hypre docs for AMS: cycles 1, 5, 8, 11, 13 are fastest, 7 yields fewest its // (MFEM default is 13). 14 is similar to 11/13 but is cheaper in that is uses additive // scalar Pi-space corrections. - cycle_type(vector_interp ? 1 : 14), - space_dim(nd_fespace.GetParMesh()->SpaceDimension()), + cycle_type(vector_interp ? 1 : 14), space_dim(nd_fespace.SpaceDimension()), // When used as the coarse solver of geometric multigrid, always do only a single // V-cycle. ams_it(cycle_it), ams_smooth_it(smooth_it), @@ -47,8 +46,8 @@ HypreAmsSolver::~HypreAmsSolver() HYPRE_AMSDestroy(ams); } -void HypreAmsSolver::ConstructAuxiliaryMatrices( - const FiniteElementSpace &nd_fespace, const AuxiliaryFiniteElementSpace &h1_fespace) +void HypreAmsSolver::ConstructAuxiliaryMatrices(FiniteElementSpace &nd_fespace, + AuxiliaryFiniteElementSpace &h1_fespace) { // Set up the auxiliary space objects for the preconditioner. Mostly the same as MFEM's // HypreAMS:Init. Start with the discrete gradient matrix. @@ -64,12 +63,11 @@ void HypreAmsSolver::ConstructAuxiliaryMatrices( // Vertex coordinates for the lowest order case, or Nedelec interpolation matrix or // matrices for order > 1. - mfem::ParMesh &mesh = *h1_fespace.GetParMesh(); + mfem::ParMesh &mesh = h1_fespace.GetParMesh(); if (h1_fespace.GetMaxElementOrder() == 1) { - mfem::ParGridFunction x_coord(const_cast(&h1_fespace)), - y_coord(const_cast(&h1_fespace)), - z_coord(const_cast(&h1_fespace)); + mfem::ParGridFunction x_coord(&h1_fespace.Get()), y_coord(&h1_fespace.Get()), + z_coord(&h1_fespace.Get()); if (mesh.GetNodes()) { mesh.GetNodes()->GetNodalValues(x_coord, 1); @@ -117,10 +115,9 @@ void HypreAmsSolver::ConstructAuxiliaryMatrices( else { // Fall back to MFEM legacy assembly for identity interpolator. - mfem::ParFiniteElementSpace h1d_fespace(&mesh, h1_fespace.FEColl(), space_dim, + mfem::ParFiniteElementSpace h1d_fespace(&mesh, &h1_fespace.GetFEColl(), space_dim, mfem::Ordering::byVDIM); - mfem::DiscreteLinearOperator pi(&h1d_fespace, - const_cast(&nd_fespace)); + mfem::DiscreteLinearOperator pi(&h1d_fespace, &nd_fespace.Get()); pi.AddDomainInterpolator(new mfem::IdentityInterpolator); pi.SetAssemblyLevel(mfem::AssemblyLevel::LEGACY); pi.Assemble(); diff --git a/palace/linalg/ams.hpp b/palace/linalg/ams.hpp index 50386bd2a..5a083f735 100644 --- a/palace/linalg/ams.hpp +++ b/palace/linalg/ams.hpp @@ -40,8 +40,8 @@ class HypreAmsSolver : public mfem::HypreSolver std::unique_ptr x, y, z; // Helper function to set up the auxiliary objects required by the AMS solver. - void ConstructAuxiliaryMatrices(const FiniteElementSpace &nd_fespace, - const AuxiliaryFiniteElementSpace &h1_fespace); + void ConstructAuxiliaryMatrices(FiniteElementSpace &nd_fespace, + AuxiliaryFiniteElementSpace &h1_fespace); // Helper function to construct and configure the AMS solver. void InitializeSolver(); @@ -49,12 +49,11 @@ class HypreAmsSolver : public mfem::HypreSolver public: // Constructor requires the ND space, but will construct the H1 and (H1)ᵈ spaces // internally as needed. - HypreAmsSolver(const FiniteElementSpace &nd_fespace, - const AuxiliaryFiniteElementSpace &h1_fespace, int cycle_it, int smooth_it, - int agg_coarsen, bool vector_interp, bool op_singular, int print); - HypreAmsSolver(const IoData &iodata, bool coarse_solver, - const FiniteElementSpace &nd_fespace, - const AuxiliaryFiniteElementSpace &h1_fespace, int print) + HypreAmsSolver(FiniteElementSpace &nd_fespace, AuxiliaryFiniteElementSpace &h1_fespace, + int cycle_it, int smooth_it, int agg_coarsen, bool vector_interp, + bool op_singular, int print); + HypreAmsSolver(const IoData &iodata, bool coarse_solver, FiniteElementSpace &nd_fespace, + AuxiliaryFiniteElementSpace &h1_fespace, int print) : HypreAmsSolver( nd_fespace, h1_fespace, coarse_solver ? 1 : iodata.solver.linear.mg_cycle_it, iodata.solver.linear.mg_smooth_it, diff --git a/palace/linalg/errorestimator.cpp b/palace/linalg/errorestimator.cpp index ccdb69cbf..f43a00fcb 100644 --- a/palace/linalg/errorestimator.cpp +++ b/palace/linalg/errorestimator.cpp @@ -23,8 +23,8 @@ namespace auto GetMassMatrix(const FiniteElementSpace &fespace) { constexpr bool skip_zeros = false; - const int dim = fespace.GetParMesh()->Dimension(); - const auto type = fespace.FEColl()->GetRangeType(dim); + const int dim = fespace.Dimension(); + const auto type = fespace.GetFEColl().GetRangeType(dim); BilinearForm m(fespace); if (type == mfem::FiniteElement::SCALAR) { @@ -145,7 +145,7 @@ CurlFluxErrorEstimator::CurlFluxErrorEstimator(const MaterialOperator & double tol, int max_it, int print) : mat_op(mat_op), nd_fespace(nd_fespace), projector(mat_op, nd_fespace, tol, max_it, print), F(nd_fespace.GetTrueVSize()), - F_gf(&nd_fespace), U_gf(&nd_fespace) + F_gf(&nd_fespace.Get()), U_gf(&nd_fespace.Get()) { } @@ -171,7 +171,7 @@ ErrorIndicator CurlFluxErrorEstimator::ComputeIndicators(const VecType // Loop over elements and accumulate the estimates from this component. The discontinuous // flux is μ⁻¹ ∇ × U. - auto &mesh = *nd_fespace.GetParMesh(); + const auto &mesh = nd_fespace.GetParMesh(); Vector estimates(mesh.GetNE()); double norm2 = 0.0; PalacePragmaOmp(parallel reduction(+ : norm2)) @@ -188,9 +188,9 @@ ErrorIndicator CurlFluxErrorEstimator::ComputeIndicators(const VecType PalacePragmaOmp(for schedule(static)) for (int e = 0; e < mesh.GetNE(); e++) { - const mfem::FiniteElement &fe = *nd_fespace.GetFE(e); + const mfem::FiniteElement &fe = *nd_fespace.Get().GetFE(e); mesh.GetElementTransformation(e, &T); - nd_fespace.GetElementDofs(e, dofs, dof_trans); + nd_fespace.Get().GetElementDofs(e, dofs, dof_trans); Interp.SetSize(fe.GetDof(), V_ip.Size()); Curl.SetSize(fe.GetDof(), V_ip.Size()); const int q_order = fem::DefaultIntegrationOrder::Get(fe, fe, T); @@ -262,10 +262,10 @@ GradFluxErrorEstimator::GradFluxErrorEstimator(const MaterialOperator &mat_op, int max_it, int print) : mat_op(mat_op), h1_fespace(h1_fespace), h1d_fespace(std::make_unique( - h1_fespace.GetParMesh(), h1_fespace.FEColl(), - h1_fespace.GetParMesh()->SpaceDimension(), mfem::Ordering::byNODES)), + h1_fespace.GetMesh(), &h1_fespace.GetFEColl(), h1_fespace.SpaceDimension(), + mfem::Ordering::byNODES)), projector(mat_op, h1_fespace, *h1d_fespace, tol, max_it, print), - F(h1d_fespace->GetTrueVSize()), F_gf(h1d_fespace.get()), U_gf(&h1_fespace) + F(h1d_fespace->GetTrueVSize()), F_gf(&h1d_fespace->Get()), U_gf(&h1_fespace.Get()) { } @@ -280,7 +280,7 @@ ErrorIndicator GradFluxErrorEstimator::ComputeIndicators(const Vector &U) const // Loop over elements and accumulate the estimates from this component. The discontinuous // flux is ε ∇U. - auto &mesh = *h1_fespace.GetParMesh(); + const auto &mesh = h1_fespace.GetParMesh(); Vector estimates(mesh.GetNE()); double norm2 = 0.0; PalacePragmaOmp(parallel reduction(+ : norm2)) @@ -296,11 +296,11 @@ ErrorIndicator GradFluxErrorEstimator::ComputeIndicators(const Vector &U) const PalacePragmaOmp(for schedule(static)) for (int e = 0; e < mesh.GetNE(); e++) { - const mfem::FiniteElement &fe = *h1d_fespace->GetFE(e); + const mfem::FiniteElement &fe = *h1d_fespace->Get().GetFE(e); mesh.GetElementTransformation(e, &T); - h1_fespace.GetElementDofs(e, dofs); + h1_fespace.Get().GetElementDofs(e, dofs); vdofs = dofs; - h1d_fespace->DofsToVDofs(vdofs); + h1d_fespace->Get().DofsToVDofs(vdofs); Interp.SetSize(fe.GetDof()); Grad.SetSize(fe.GetDof(), V_ip.Size()); const int q_order = fem::DefaultIntegrationOrder::Get(fe, fe, T); diff --git a/palace/linalg/ksp.cpp b/palace/linalg/ksp.cpp index 090106d77..5d079d638 100644 --- a/palace/linalg/ksp.cpp +++ b/palace/linalg/ksp.cpp @@ -60,13 +60,17 @@ std::unique_ptr> ConfigureKrylovSolver(MPI_Comm comm, // Configure preconditioning side (only for GMRES). if (iodata.solver.linear.pc_side_type != config::LinearSolverData::SideType::DEFAULT && - type != config::LinearSolverData::KspType::GMRES) + (type != config::LinearSolverData::KspType::GMRES || + type != config::LinearSolverData::KspType::FGMRES)) { - Mpi::Warning(comm, - "Preconditioner side will be ignored for non-GMRES iterative solvers!\n"); + Mpi::Warning( + comm, + "Preconditioner side will be ignored for non-GMRES/FGMRES iterative solvers!\n"); } - else + else if (type == config::LinearSolverData::KspType::GMRES || + type == config::LinearSolverData::KspType::FGMRES) { + // Because FGMRES inherits from GMRES, this is OK. auto *gmres = static_cast *>(ksp.get()); switch (iodata.solver.linear.pc_side_type) { @@ -118,8 +122,8 @@ auto MakeWrapperSolver(U &&...args) template std::unique_ptr> ConfigurePreconditionerSolver(MPI_Comm comm, const IoData &iodata, - const FiniteElementSpaceHierarchy &fespaces, - const AuxiliaryFiniteElementSpaceHierarchy *aux_fespaces) + FiniteElementSpaceHierarchy &fespaces, + AuxiliaryFiniteElementSpaceHierarchy *aux_fespaces) { // Create the real-valued solver first. std::unique_ptr> pc; @@ -215,9 +219,9 @@ ConfigurePreconditionerSolver(MPI_Comm comm, const IoData &iodata, } // namespace template -BaseKspSolver::BaseKspSolver( - const IoData &iodata, const FiniteElementSpaceHierarchy &fespaces, - const AuxiliaryFiniteElementSpaceHierarchy *aux_fespaces) +BaseKspSolver::BaseKspSolver(const IoData &iodata, + FiniteElementSpaceHierarchy &fespaces, + AuxiliaryFiniteElementSpaceHierarchy *aux_fespaces) : BaseKspSolver( ConfigureKrylovSolver(fespaces.GetFinestFESpace().GetComm(), iodata), ConfigurePreconditionerSolver(fespaces.GetFinestFESpace().GetComm(), diff --git a/palace/linalg/ksp.hpp b/palace/linalg/ksp.hpp index 4af3f4da6..60c0338f6 100644 --- a/palace/linalg/ksp.hpp +++ b/palace/linalg/ksp.hpp @@ -40,8 +40,8 @@ class BaseKspSolver mutable int ksp_mult, ksp_mult_it; public: - BaseKspSolver(const IoData &iodata, const FiniteElementSpaceHierarchy &fespaces, - const AuxiliaryFiniteElementSpaceHierarchy *aux_fespaces = nullptr); + BaseKspSolver(const IoData &iodata, FiniteElementSpaceHierarchy &fespaces, + AuxiliaryFiniteElementSpaceHierarchy *aux_fespaces = nullptr); BaseKspSolver(std::unique_ptr> &&ksp, std::unique_ptr> &&pc); diff --git a/palace/main.cpp b/palace/main.cpp index 3d0bf26a4..a05dbca03 100644 --- a/palace/main.cpp +++ b/palace/main.cpp @@ -15,6 +15,7 @@ #include "drivers/transientsolver.hpp" #include "fem/errorindicator.hpp" #include "fem/libceed/utils.hpp" +#include "fem/mesh.hpp" #include "linalg/slepc.hpp" #include "utils/communication.hpp" #include "utils/geodata.hpp" @@ -279,10 +280,17 @@ int main(int argc, char *argv[]) // Read the mesh from file, refine, partition, and distribute it. Then nondimensionalize // it and the input parameters. - std::vector> mesh; - mesh.push_back(mesh::ReadMesh(world_comm, iodata, false, true, true, false)); - iodata.NondimensionalizeInputs(*mesh[0]); - mesh::RefineMesh(iodata, mesh); + std::vector> mesh; + { + std::vector> mfem_mesh; + mfem_mesh.push_back(mesh::ReadMesh(world_comm, iodata, false, true, true, false)); + iodata.NondimensionalizeInputs(*mfem_mesh[0]); + mesh::RefineMesh(iodata, mfem_mesh); + for (auto &m : mfem_mesh) + { + mesh.push_back(std::make_unique(std::move(m))); + } + } // Run the problem driver. solver->SolveEstimateMarkRefine(mesh); diff --git a/palace/models/curlcurloperator.cpp b/palace/models/curlcurloperator.cpp index 12958d7da..0f3b2a7f5 100644 --- a/palace/models/curlcurloperator.cpp +++ b/palace/models/curlcurloperator.cpp @@ -6,6 +6,7 @@ #include "fem/bilinearform.hpp" #include "fem/coefficient.hpp" #include "fem/integrator.hpp" +#include "fem/mesh.hpp" #include "fem/multigrid.hpp" #include "linalg/rap.hpp" #include "utils/communication.hpp" @@ -17,7 +18,7 @@ namespace palace { CurlCurlOperator::CurlCurlOperator(const IoData &iodata, - const std::vector> &mesh) + const std::vector> &mesh) : print_hdr(true), dbc_attr(SetUpBoundaryProperties(iodata, *mesh.back())), nd_fecs(fem::ConstructFECollections( iodata.solver.order, mesh.back()->Dimension(), iodata.solver.linear.mg_max_levels, @@ -31,7 +32,7 @@ CurlCurlOperator::CurlCurlOperator(const IoData &iodata, iodata.solver.linear.mg_max_levels, mesh, nd_fecs, &dbc_attr, &dbc_tdof_lists)), h1_fespaces(fem::ConstructAuxiliaryFiniteElementSpaceHierarchy( nd_fespaces, h1_fecs)), - rt_fespace(nd_fespaces.GetFinestFESpace(), mesh.back().get(), rt_fec.get()), + rt_fespace(nd_fespaces.GetFinestFESpace(), *mesh.back(), rt_fec.get()), mat_op(iodata, *mesh.back()), surf_j_op(iodata, GetH1Space()) { // Finalize setup. @@ -205,11 +206,11 @@ void CurlCurlOperator::GetExcitationVector(int idx, Vector &RHS) { return; } - mfem::LinearForm rhs(&GetNDSpace()); + mfem::LinearForm rhs(&GetNDSpace().Get()); rhs.AddBoundaryIntegrator(new VectorFEBoundaryLFIntegrator(fb)); rhs.UseFastAssembly(false); rhs.Assemble(); - GetNDSpace().GetProlongationMatrix()->AddMultTranspose(rhs, RHS, -1.0); + GetNDSpace().Get().GetProlongationMatrix()->AddMultTranspose(rhs, RHS, -1.0); linalg::SetSubVector(RHS, dbc_tdof_lists.back(), 0.0); } diff --git a/palace/models/curlcurloperator.hpp b/palace/models/curlcurloperator.hpp index caaa553b2..fb3a4b35d 100644 --- a/palace/models/curlcurloperator.hpp +++ b/palace/models/curlcurloperator.hpp @@ -17,6 +17,7 @@ namespace palace { class IoData; +class Mesh; // // A class handling discretization of curl-curl problems for magnetostatics. @@ -51,8 +52,7 @@ class CurlCurlOperator void CheckBoundaryProperties(); public: - CurlCurlOperator(const IoData &iodata, - const std::vector> &mesh); + CurlCurlOperator(const IoData &iodata, const std::vector> &mesh); // Return material operator for postprocessing. const MaterialOperator &GetMaterialOp() const { return mat_op; } @@ -73,7 +73,7 @@ class CurlCurlOperator const auto &GetRTSpace() const { return rt_fespace; } // Access the underlying mesh object. - const auto &GetMesh() const { return *GetNDSpace().GetParMesh(); } + const auto &GetMesh() const { return GetNDSpace().GetMesh(); } // Return the number of true (conforming) dofs on the finest ND space. auto GlobalTrueVSize() const { return GetNDSpace().GlobalTrueVSize(); } diff --git a/palace/models/laplaceoperator.cpp b/palace/models/laplaceoperator.cpp index 50dc2fe9c..44186be63 100644 --- a/palace/models/laplaceoperator.cpp +++ b/palace/models/laplaceoperator.cpp @@ -5,6 +5,7 @@ #include "fem/bilinearform.hpp" #include "fem/integrator.hpp" +#include "fem/mesh.hpp" #include "fem/multigrid.hpp" #include "linalg/rap.hpp" #include "utils/communication.hpp" @@ -16,7 +17,7 @@ namespace palace { LaplaceOperator::LaplaceOperator(const IoData &iodata, - const std::vector> &mesh) + const std::vector> &mesh) : print_hdr(true), dbc_attr(SetUpBoundaryProperties(iodata, *mesh.back())), h1_fecs(fem::ConstructFECollections( iodata.solver.order, mesh.back()->Dimension(), iodata.solver.linear.mg_max_levels, @@ -25,7 +26,7 @@ LaplaceOperator::LaplaceOperator(const IoData &iodata, mesh.back()->Dimension())), h1_fespaces(fem::ConstructFiniteElementSpaceHierarchy( iodata.solver.linear.mg_max_levels, mesh, h1_fecs, &dbc_attr, &dbc_tdof_lists)), - nd_fespace(h1_fespaces.GetFinestFESpace(), mesh.back().get(), nd_fec.get()), + nd_fespace(h1_fespaces.GetFinestFESpace(), *mesh.back(), nd_fec.get()), mat_op(iodata, *mesh.back()), source_attr_lists(ConstructSources(iodata)) { // Finalize setup. @@ -218,7 +219,7 @@ void LaplaceOperator::GetExcitationVector(int idx, const Operator &K, Vector &X, { // Apply the Dirichlet BCs to the solution vector: V = 1 on terminal boundaries with the // given index, V = 0 on all ground and other terminal boundaries. - mfem::ParGridFunction x(&GetH1Space()); + mfem::ParGridFunction x(&GetH1Space().Get()); x = 0.0; // Get a marker of all boundary attributes with the given source surface index. diff --git a/palace/models/laplaceoperator.hpp b/palace/models/laplaceoperator.hpp index 54f2eae22..d6b62ef88 100644 --- a/palace/models/laplaceoperator.hpp +++ b/palace/models/laplaceoperator.hpp @@ -17,6 +17,7 @@ namespace palace { class IoData; +class Mesh; // // A class handling discretization of Laplace problems for electrostatics. @@ -48,8 +49,7 @@ class LaplaceOperator std::map> ConstructSources(const IoData &iodata); public: - LaplaceOperator(const IoData &iodata, - const std::vector> &mesh); + LaplaceOperator(const IoData &iodata, const std::vector> &mesh); // Return material operator for postprocessing. const MaterialOperator &GetMaterialOp() const { return mat_op; } @@ -66,7 +66,7 @@ class LaplaceOperator const auto &GetNDSpace() const { return nd_fespace; } // Access the underlying mesh object. - const auto &GetMesh() const { return *GetH1Space().GetParMesh(); } + const auto &GetMesh() const { return GetH1Space().GetMesh(); } // Return the number of true (conforming) dofs on the finest H1 space. auto GlobalTrueVSize() const { return GetH1Space().GlobalTrueVSize(); } diff --git a/palace/models/materialoperator.cpp b/palace/models/materialoperator.cpp index 5b5c98249..62df303bd 100644 --- a/palace/models/materialoperator.cpp +++ b/palace/models/materialoperator.cpp @@ -278,82 +278,10 @@ mfem::DenseMatrix ToDenseMatrix(const config::SymmetricMatrixData &data) return M; } -auto BuildAttributeGlobalToLocal(const mfem::ParMesh &mesh) -{ - // Set up sparse map from global domain attributes to local ones on this process. - // Include ghost elements for all shared faces so we have their material properties - // stored locally. - std::unordered_map loc_attr; - mfem::FaceElementTransformations FET; - mfem::IsoparametricTransformation T1, T2; - int count = 0; - for (int i = 0; i < mesh.GetNE(); i++) - { - const int attr = mesh.GetAttribute(i); - if (loc_attr.find(attr) == loc_attr.end()) - { - loc_attr[attr] = ++count; - } - } - for (int i = 0; i < mesh.GetNSharedFaces(); i++) - { - mesh.GetSharedFaceTransformations(i, &FET, &T1, &T2); - int attr = FET.Elem1->Attribute; - if (loc_attr.find(attr) == loc_attr.end()) - { - loc_attr[attr] = ++count; - } - attr = FET.Elem2->Attribute; - if (loc_attr.find(attr) == loc_attr.end()) - { - loc_attr[attr] = ++count; - } - } - return loc_attr; -} - -auto GetBdrNeighborAttribute(int i, const mfem::ParMesh &mesh, - mfem::FaceElementTransformations &FET, - mfem::IsoparametricTransformation &T1, - mfem::IsoparametricTransformation &T2) -{ - // For internal boundaries, use the element which corresponds to the vacuum domain, or - // at least the one with the higher speed of light. - BdrGridFunctionCoefficient::GetBdrElementNeighborTransformations(i, mesh, FET, T1, T2); - return (FET.Elem2 && FET.Elem2->Attribute < FET.Elem1->Attribute) ? FET.Elem2->Attribute - : FET.Elem1->Attribute; -} - -auto BuildBdrAttributeGlobalToLocal(const mfem::ParMesh &mesh) -{ - // Set up sparse map from global boundary attributes to local ones on this process. Each - // original global boundary attribute maps to a key-value pairing of global domain - // attributes which neighbor the given boundary and local boundary attributes. - std::unordered_map> loc_bdr_attr; - mfem::FaceElementTransformations FET; - mfem::IsoparametricTransformation T1, T2; - int count = 0; - for (int i = 0; i < mesh.GetNBE(); i++) - { - const int attr = mesh.GetBdrAttribute(i); - const int nbr_attr = GetBdrNeighborAttribute(i, mesh, FET, T1, T2); - auto &bdr_attr_map = loc_bdr_attr[attr]; - if (bdr_attr_map.find(nbr_attr) == bdr_attr_map.end()) - { - bdr_attr_map[nbr_attr] = ++count; - } - } - return loc_bdr_attr; -} - } // namespace -MaterialOperator::MaterialOperator(const IoData &iodata, mfem::ParMesh &mesh) : mesh(mesh) +MaterialOperator::MaterialOperator(const IoData &iodata, const Mesh &mesh) : mesh(mesh) { - mesh.ExchangeFaceNbrData(); - loc_attr = BuildAttributeGlobalToLocal(mesh); - loc_bdr_attr = BuildBdrAttributeGlobalToLocal(mesh); - SetUpMaterialProperties(iodata, mesh); } @@ -388,6 +316,7 @@ void MaterialOperator::SetUpMaterialProperties(const IoData &iodata, // Set up material properties of the different domain regions, represented with element- // wise constant matrix-valued coefficients for the relative permeability, permittivity, // and other material properties. + const auto &loc_attr = this->mesh.GetAttributeGlobalToLocal(); mfem::Array mat_marker(iodata.domains.materials.size()); mat_marker = 0; int nmats = 0; @@ -546,6 +475,7 @@ mfem::Array MaterialOperator::GetBdrAttributeToMaterial() const { // Construct map from all (contiguous) local boundary attributes to the material index in // the neighboring element. + const auto &loc_bdr_attr = mesh.GetBdrAttributeGlobalToLocal(); int bdr_attr_max = 0; for (const auto &[attr, bdr_attr_map] : loc_bdr_attr) { @@ -565,47 +495,6 @@ mfem::Array MaterialOperator::GetBdrAttributeToMaterial() const return bdr_attr_mat; } -int MaterialOperator::GetAttributeGlobalToLocal(mfem::ElementTransformation &T) const -{ - if (T.GetDimension() == T.GetSpaceDim()) - { - // Domain element. - auto it = loc_attr.find(T.Attribute); - MFEM_ASSERT(it != loc_attr.end(), "Invalid domain attribute " << T.Attribute << "!"); - return it->second; - } - else - { - // Boundary element (or boundary submesh domain). - auto bdr_attr_map = loc_bdr_attr.find(T.Attribute); - MFEM_ASSERT(bdr_attr_map != loc_bdr_attr.end(), - "Invalid domain attribute " << T.Attribute << "!"); - const int nbr_attr = [&]() - { - mfem::FaceElementTransformations FET; // XX TODO: Preallocate these for all elements - mfem::IsoparametricTransformation T1, T2; - if (const auto *submesh = dynamic_cast(T.mesh)) - { - MFEM_ASSERT(T.ElementType == mfem::ElementTransformation::ELEMENT, - "Unexpected element type in GetAttributeGlobalToLocal!"); - return GetBdrNeighborAttribute(submesh->GetParentElementIDMap()[T.ElementNo], - *submesh->GetParent(), FET, T1, T2); - } - else - { - MFEM_ASSERT(T.ElementType == mfem::ElementTransformation::BDR_ELEMENT, - "Unexpected element type in GetAttributeGlobalToLocal!"); - return GetBdrNeighborAttribute( - T.ElementNo, *static_cast(T.mesh), FET, T1, T2); - } - }(); - auto it = bdr_attr_map->second.find(nbr_attr); - MFEM_ASSERT(it != bdr_attr_map->second.end(), - "Invalid domain attribute " << nbr_attr << "!"); - return it->second; - } -} - MaterialPropertyCoefficient::MaterialPropertyCoefficient( const MaterialOperator &mat_op, const mfem::Array &attr_mat_, const mfem::DenseTensor &mat_coeff_, double a) @@ -886,7 +775,7 @@ void MaterialPropertyCoefficient::NormalProjectedCoefficient(const mfem::Vector double MaterialPropertyCoefficient::Eval(mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip) { - const int attr = mat_op.GetAttributeGlobalToLocal(T); + const int attr = mat_op.GetMesh().GetAttributeGlobalToLocal(T); MFEM_ASSERT(attr <= attr_mat.Size(), "Out of bounds attribute for MaterialPropertyCoefficient (" << attr << " > " << attr_mat.Size() << ")!"); @@ -899,7 +788,7 @@ double MaterialPropertyCoefficient::Eval(mfem::ElementTransformation &T, void MaterialPropertyCoefficient::Eval(mfem::DenseMatrix &K, mfem::ElementTransformation &T, const mfem::IntegrationPoint &ip) { - const int attr = mat_op.GetAttributeGlobalToLocal(T); + const int attr = mat_op.GetMesh().GetAttributeGlobalToLocal(T); MFEM_ASSERT(attr <= attr_mat.Size(), "Out of bounds attribute for MaterialPropertyCoefficient (" << attr << " > " << attr_mat.Size() << ")!"); diff --git a/palace/models/materialoperator.hpp b/palace/models/materialoperator.hpp index b3811d653..965791e79 100644 --- a/palace/models/materialoperator.hpp +++ b/palace/models/materialoperator.hpp @@ -4,9 +4,8 @@ #ifndef PALACE_MODELS_MATERIAL_OPERATOR_HPP #define PALACE_MODELS_MATERIAL_OPERATOR_HPP -#include -#include #include +#include "fem/mesh.hpp" namespace palace { @@ -20,7 +19,7 @@ class MaterialOperator { private: // Reference to underlying mesh object (not owned). - const mfem::ParMesh &mesh; + const Mesh &mesh; // Mapping from the local attribute to material index. mfem::Array attr_mat; @@ -35,19 +34,11 @@ class MaterialOperator // penetration depth. mfem::Array losstan_attr, conductivity_attr, london_attr; - // Attribute mapping for (global, 1-based) domain and boundary attributes to those on this - // process (still 1-based). For boundaries, the inner map is a mapping from neighboring - // domain attribute to the resulting local boundary attribute (to discern boundary - // elements with global boundary attribute which borders more than one domain). Interior - // boundaries use as neighbor the element with the smaller domain attribute in order to - // be consistent when the interior boundary element normals are not aligned. - std::unordered_map loc_attr; - std::unordered_map> loc_bdr_attr; - void SetUpMaterialProperties(const IoData &iodata, const mfem::ParMesh &mesh); const auto AttrToMat(int attr) const { + const auto &loc_attr = mesh.GetAttributeGlobalToLocal(); MFEM_ASSERT(loc_attr.find(attr) != loc_attr.end(), "Missing local domain attribute for attribute " << attr << "!"); return attr_mat[loc_attr.at(attr) - 1]; @@ -61,7 +52,7 @@ class MaterialOperator } public: - MaterialOperator(const IoData &iodata, mfem::ParMesh &mesh); + MaterialOperator(const IoData &iodata, const Mesh &mesh); int SpaceDimension() const { return mat_muinv.SizeI(); } @@ -96,60 +87,17 @@ class MaterialOperator const auto &GetAttributeToMaterial() const { return attr_mat; } mfem::Array GetBdrAttributeToMaterial() const; - const auto &GetAttributeGlobalToLocal() const { return loc_attr; } - - const auto &GetBdrAttributeGlobalToLocal() const { return loc_bdr_attr; } - template auto GetAttributeGlobalToLocal(const T &attr_list) const { - // Skip any entries in the input global attribute list which are not on local to this - // process. - const auto &loc_attr = GetAttributeGlobalToLocal(); - mfem::Array loc_attr_list; - for (auto attr : attr_list) - { - if (loc_attr.find(attr) != loc_attr.end()) - { - loc_attr_list.Append(loc_attr.at(attr)); - } - } - return loc_attr_list; + return mesh.GetAttributeGlobalToLocal(attr_list); } - template auto GetBdrAttributeGlobalToLocal(const T &attr_list) const { - // Skip any entries in the input global boundary attribute list which are not on local - // to this process. - const auto &loc_bdr_attr = GetBdrAttributeGlobalToLocal(); - mfem::Array loc_attr_list; - for (auto attr : attr_list) - { - if (loc_bdr_attr.find(attr) != loc_bdr_attr.end()) - { - const auto &bdr_attr_map = loc_bdr_attr.at(attr); - for (auto it = bdr_attr_map.begin(); it != bdr_attr_map.end(); ++it) - { - loc_attr_list.Append(it->second); - } - } - } - return loc_attr_list; + return mesh.GetBdrAttributeGlobalToLocal(attr_list); } - auto GetAttributeGlobalToLocal(const int attr) const - { - return GetAttributeGlobalToLocal(std::vector{attr}); - } - - auto GetBdrAttributeGlobalToLocal(const int attr) const - { - return GetBdrAttributeGlobalToLocal(std::vector{attr}); - } - - int GetAttributeGlobalToLocal(mfem::ElementTransformation &T) const; - const auto &GetMesh() const { return mesh; } }; diff --git a/palace/models/postoperator.cpp b/palace/models/postoperator.cpp index 9e394d18c..5ca813321 100644 --- a/palace/models/postoperator.cpp +++ b/palace/models/postoperator.cpp @@ -44,12 +44,12 @@ PostOperator::PostOperator(const IoData &iodata, SpaceOperator &spaceop, dom_post_op(iodata, spaceop.GetMaterialOp(), &spaceop.GetNDSpace(), &spaceop.GetRTSpace()), has_imaginary(iodata.problem.type != config::ProblemData::Type::TRANSIENT), - E(&spaceop.GetNDSpace()), B(&spaceop.GetRTSpace()), V(std::nullopt), A(std::nullopt), - lumped_port_init(false), wave_port_init(false), - paraview(CreateParaviewPath(iodata, name), spaceop.GetNDSpace().GetParMesh()), + E(&spaceop.GetNDSpace().Get()), B(&spaceop.GetRTSpace().Get()), V(std::nullopt), + A(std::nullopt), lumped_port_init(false), wave_port_init(false), + paraview(CreateParaviewPath(iodata, name), &spaceop.GetNDSpace().GetParMesh()), paraview_bdr(CreateParaviewPath(iodata, name) + "_boundary", - spaceop.GetNDSpace().GetParMesh()), - interp_op(iodata, *spaceop.GetNDSpace().GetParMesh()) + &spaceop.GetNDSpace().GetParMesh()), + interp_op(iodata, spaceop.GetNDSpace().GetParMesh()) { Esr = std::make_unique(E->real(), mat_op); Bsr = std::make_unique(B->real(), mat_op); @@ -95,13 +95,13 @@ PostOperator::PostOperator(const IoData &iodata, LaplaceOperator &laplaceop, : mat_op(laplaceop.GetMaterialOp()), surf_post_op(iodata, laplaceop.GetMaterialOp(), laplaceop.GetH1Space()), dom_post_op(iodata, laplaceop.GetMaterialOp(), &laplaceop.GetNDSpace(), nullptr), - has_imaginary(false), E(&laplaceop.GetNDSpace()), B(std::nullopt), - V(&laplaceop.GetH1Space()), A(std::nullopt), lumped_port_init(false), + has_imaginary(false), E(&laplaceop.GetNDSpace().Get()), B(std::nullopt), + V(&laplaceop.GetH1Space().Get()), A(std::nullopt), lumped_port_init(false), wave_port_init(false), - paraview(CreateParaviewPath(iodata, name), laplaceop.GetNDSpace().GetParMesh()), + paraview(CreateParaviewPath(iodata, name), &laplaceop.GetNDSpace().GetParMesh()), paraview_bdr(CreateParaviewPath(iodata, name) + "_boundary", - laplaceop.GetNDSpace().GetParMesh()), - interp_op(iodata, *laplaceop.GetNDSpace().GetParMesh()) + &laplaceop.GetNDSpace().GetParMesh()), + interp_op(iodata, laplaceop.GetNDSpace().GetParMesh()) { // Note: When using this constructor, you should not use any of the magnetic field related // postprocessing functions (magnetic field energy, inductor energy, surface currents, @@ -122,12 +122,13 @@ PostOperator::PostOperator(const IoData &iodata, CurlCurlOperator &curlcurlop, : mat_op(curlcurlop.GetMaterialOp()), surf_post_op(iodata, curlcurlop.GetMaterialOp(), curlcurlop.GetH1Space()), dom_post_op(iodata, curlcurlop.GetMaterialOp(), nullptr, &curlcurlop.GetRTSpace()), - has_imaginary(false), E(std::nullopt), B(&curlcurlop.GetRTSpace()), V(std::nullopt), - A(&curlcurlop.GetNDSpace()), lumped_port_init(false), wave_port_init(false), - paraview(CreateParaviewPath(iodata, name), curlcurlop.GetNDSpace().GetParMesh()), + has_imaginary(false), E(std::nullopt), B(&curlcurlop.GetRTSpace().Get()), + V(std::nullopt), A(&curlcurlop.GetNDSpace().Get()), lumped_port_init(false), + wave_port_init(false), + paraview(CreateParaviewPath(iodata, name), &curlcurlop.GetNDSpace().GetParMesh()), paraview_bdr(CreateParaviewPath(iodata, name) + "_boundary", - curlcurlop.GetNDSpace().GetParMesh()), - interp_op(iodata, *curlcurlop.GetNDSpace().GetParMesh()) + &curlcurlop.GetNDSpace().GetParMesh()), + interp_op(iodata, curlcurlop.GetNDSpace().GetParMesh()) { // Note: When using this constructor, you should not use any of the electric field related // postprocessing functions (electric field energy, capacitor energy, surface charge, diff --git a/palace/models/spaceoperator.cpp b/palace/models/spaceoperator.cpp index 5b721c8e4..448f79eaf 100644 --- a/palace/models/spaceoperator.cpp +++ b/palace/models/spaceoperator.cpp @@ -7,6 +7,7 @@ #include "fem/bilinearform.hpp" #include "fem/coefficient.hpp" #include "fem/integrator.hpp" +#include "fem/mesh.hpp" #include "fem/multigrid.hpp" #include "linalg/rap.hpp" #include "utils/communication.hpp" @@ -20,7 +21,7 @@ namespace palace using namespace std::complex_literals; SpaceOperator::SpaceOperator(const IoData &iodata, - const std::vector> &mesh) + const std::vector> &mesh) : pc_mat_real(iodata.solver.linear.pc_mat_real), pc_mat_shifted(iodata.solver.linear.pc_mat_shifted), print_hdr(true), print_prec_hdr(true), dbc_attr(SetUpBoundaryProperties(iodata, *mesh.back())), @@ -36,7 +37,7 @@ SpaceOperator::SpaceOperator(const IoData &iodata, iodata.solver.linear.mg_max_levels, mesh, nd_fecs, &dbc_attr, &nd_dbc_tdof_lists)), h1_fespaces(fem::ConstructAuxiliaryFiniteElementSpaceHierarchy( nd_fespaces, h1_fecs, &dbc_attr, &h1_dbc_tdof_lists)), - rt_fespace(nd_fespaces.GetFinestFESpace(), mesh.back().get(), rt_fec.get()), + rt_fespace(nd_fespaces.GetFinestFESpace(), *mesh.back(), rt_fec.get()), mat_op(iodata, *mesh.back()), farfield_op(iodata, mat_op, *mesh.back()), surf_sigma_op(iodata, mat_op, *mesh.back()), surf_z_op(iodata, mat_op, *mesh.back()), lumped_port_op(iodata, mat_op, GetH1Space()), @@ -142,7 +143,7 @@ void SpaceOperator::CheckBoundaryProperties() // // As tested, this does not eliminate all DC modes! for (std::size_t l = 0; l < GetH1Spaces().GetNumLevels(); l++) { - GetH1Spaces().GetFESpaceAtLevel(l).GetEssentialTrueDofs( + GetH1Spaces().GetFESpaceAtLevel(l).Get().GetEssentialTrueDofs( aux_bdr_marker, aux_bdr_tdof_lists.emplace_back()); } @@ -886,11 +887,11 @@ bool SpaceOperator::AddExcitationVector1Internal(Vector &RHS1) { return false; } - mfem::LinearForm rhs1(&GetNDSpace()); + mfem::LinearForm rhs1(&GetNDSpace().Get()); rhs1.AddBoundaryIntegrator(new VectorFEBoundaryLFIntegrator(fb)); rhs1.UseFastAssembly(false); rhs1.Assemble(); - GetNDSpace().GetProlongationMatrix()->AddMultTranspose(rhs1, RHS1); + GetNDSpace().Get().GetProlongationMatrix()->AddMultTranspose(rhs1, RHS1); return true; } @@ -906,15 +907,15 @@ bool SpaceOperator::AddExcitationVector2Internal(double omega, ComplexVector &RH { return false; } - mfem::LinearForm rhs2r(&GetNDSpace()), rhs2i(&GetNDSpace()); + mfem::LinearForm rhs2r(&GetNDSpace().Get()), rhs2i(&GetNDSpace().Get()); rhs2r.AddBoundaryIntegrator(new VectorFEBoundaryLFIntegrator(fbr)); rhs2i.AddBoundaryIntegrator(new VectorFEBoundaryLFIntegrator(fbi)); rhs2r.UseFastAssembly(false); rhs2i.UseFastAssembly(false); rhs2r.Assemble(); rhs2i.Assemble(); - GetNDSpace().GetProlongationMatrix()->AddMultTranspose(rhs2r, RHS2.Real()); - GetNDSpace().GetProlongationMatrix()->AddMultTranspose(rhs2i, RHS2.Imag()); + GetNDSpace().Get().GetProlongationMatrix()->AddMultTranspose(rhs2r, RHS2.Real()); + GetNDSpace().Get().GetProlongationMatrix()->AddMultTranspose(rhs2i, RHS2.Imag()); return true; } diff --git a/palace/models/spaceoperator.hpp b/palace/models/spaceoperator.hpp index d8a929313..1b3c07f32 100644 --- a/palace/models/spaceoperator.hpp +++ b/palace/models/spaceoperator.hpp @@ -23,6 +23,7 @@ namespace palace { class IoData; +class Mesh; // // A class handling spatial discretization of the governing equations. @@ -85,8 +86,7 @@ class SpaceOperator bool AddExcitationVector2Internal(double omega, ComplexVector &RHS); public: - SpaceOperator(const IoData &iodata, - const std::vector> &mesh); + SpaceOperator(const IoData &iodata, const std::vector> &mesh); // Return list of all PEC boundary true dofs for all finite element space levels. const std::vector> &GetNDDbcTDofLists() const @@ -130,7 +130,7 @@ class SpaceOperator const auto &GetRTSpace() const { return rt_fespace; } // Access the underlying mesh object. - const auto &GetMesh() const { return *GetNDSpace().GetParMesh(); } + const auto &GetMesh() const { return GetNDSpace().GetMesh(); } // Return the number of true (conforming) dofs on the finest ND space. auto GlobalTrueVSize() const { return GetNDSpace().GlobalTrueVSize(); } diff --git a/palace/models/waveportoperator.cpp b/palace/models/waveportoperator.cpp index 62ece9ef7..f648c6dc3 100644 --- a/palace/models/waveportoperator.cpp +++ b/palace/models/waveportoperator.cpp @@ -547,22 +547,20 @@ WavePortData::WavePortData(const config::WavePortData &data, const MaterialOpera MFEM_VERIFY(!data.attributes.empty(), "Wave port boundary found with no attributes!"); const auto &mesh = *nd_fespace.GetParMesh(); attr_list.Append(data.attributes.data(), data.attributes.size()); - port_mesh = std::make_unique( - mfem::ParSubMesh::CreateFromBoundary(mesh, attr_list)); + port_mesh = std::make_unique(std::make_unique( + mfem::ParSubMesh::CreateFromBoundary(mesh, attr_list))); port_nd_fec = std::make_unique(nd_fespace.GetMaxElementOrder(), port_mesh->Dimension()); port_h1_fec = std::make_unique(h1_fespace.GetMaxElementOrder(), port_mesh->Dimension()); - port_nd_fespace = - std::make_unique(port_mesh.get(), port_nd_fec.get()); - port_h1_fespace = - std::make_unique(port_mesh.get(), port_h1_fec.get()); + port_nd_fespace = std::make_unique(*port_mesh, port_nd_fec.get()); + port_h1_fespace = std::make_unique(*port_mesh, port_h1_fec.get()); mfem::ParGridFunction E0t(&nd_fespace); mfem::ParGridFunction E0n(&h1_fespace); - port_E0t = std::make_unique(port_nd_fespace.get()); - port_E0n = std::make_unique(port_h1_fespace.get()); + port_E0t = std::make_unique(&port_nd_fespace->Get()); + port_E0n = std::make_unique(&port_h1_fespace->Get()); port_nd_transfer = std::make_unique( mfem::ParSubMesh::CreateTransferMap(E0t, port_E0t->real())); @@ -572,7 +570,8 @@ WavePortData::WavePortData(const config::WavePortData &data, const MaterialOpera // Construct mapping from parent (boundary) element indices to submesh (domain) // elements. { - const mfem::Array &parent_elems = port_mesh->GetParentElementIDMap(); + const auto &port_submesh = static_cast(port_mesh->Get()); + const mfem::Array &parent_elems = port_submesh.GetParentElementIDMap(); for (int i = 0; i < parent_elems.Size(); i++) { submesh_parent_elems[parent_elems[i]] = i; @@ -764,7 +763,7 @@ WavePortData::WavePortData(const config::WavePortData &data, const MaterialOpera // of the wave port boundary, in order to deal with symmetry effectively. { Vector bbmin, bbmax; - port_mesh->GetBoundingBox(bbmin, bbmax); + port_mesh->Get().GetBoundingBox(bbmin, bbmax); const int dim = port_mesh->SpaceDimension(); double la = 0.0, lb = 0.0; @@ -804,7 +803,7 @@ WavePortData::WavePortData(const config::WavePortData &data, const MaterialOpera } }; mfem::VectorFunctionCoefficient tfunc(dim, TDirection); - port_S0t = std::make_unique(port_nd_fespace.get()); + port_S0t = std::make_unique(&port_nd_fespace->Get()); port_S0t->ProjectCoefficient(tfunc); } } @@ -909,52 +908,59 @@ void WavePortData::Initialize(double omega) // Configure the linear forms for computing S-parameters (projection of the field onto the // port mode). Normalize the mode for a chosen polarization direction and unit power, // |E x H⋆| ⋅ n, integrated over the port surface (+n is the direction of propagation). - BdrSubmeshHVectorCoefficient port_nxH0r_func( - *port_E0t, *port_E0n, mat_op, *port_mesh, submesh_parent_elems, kn0, omega0); - BdrSubmeshHVectorCoefficient port_nxH0i_func( - *port_E0t, *port_E0n, mat_op, *port_mesh, submesh_parent_elems, kn0, omega0); - port_sr = std::make_unique(port_nd_fespace.get()); - port_si = std::make_unique(port_nd_fespace.get()); - port_sr->AddDomainIntegrator(new VectorFEDomainLFIntegrator(port_nxH0r_func)); - port_si->AddDomainIntegrator(new VectorFEDomainLFIntegrator(port_nxH0i_func)); - port_sr->UseFastAssembly(false); - port_si->UseFastAssembly(false); - port_sr->Assemble(); - port_si->Assemble(); - NormalizeWithSign(*port_S0t, *port_E0t, *port_E0n, *port_sr, *port_si); + { + const auto &port_submesh = static_cast(port_mesh->Get()); + BdrSubmeshHVectorCoefficient port_nxH0r_func( + *port_E0t, *port_E0n, mat_op, port_submesh, submesh_parent_elems, kn0, omega0); + BdrSubmeshHVectorCoefficient port_nxH0i_func( + *port_E0t, *port_E0n, mat_op, port_submesh, submesh_parent_elems, kn0, omega0); + port_sr = std::make_unique(&port_nd_fespace->Get()); + port_si = std::make_unique(&port_nd_fespace->Get()); + port_sr->AddDomainIntegrator(new VectorFEDomainLFIntegrator(port_nxH0r_func)); + port_si->AddDomainIntegrator(new VectorFEDomainLFIntegrator(port_nxH0i_func)); + port_sr->UseFastAssembly(false); + port_si->UseFastAssembly(false); + port_sr->Assemble(); + port_si->Assemble(); + NormalizeWithSign(*port_S0t, *port_E0t, *port_E0n, *port_sr, *port_si); + } } std::unique_ptr WavePortData::GetModeExcitationCoefficientReal() const { + const auto &port_submesh = static_cast(port_mesh->Get()); return std::make_unique( std::make_unique>( - *port_E0t, *port_E0n, mat_op, *port_mesh, submesh_parent_elems, kn0, omega0), + *port_E0t, *port_E0n, mat_op, port_submesh, submesh_parent_elems, kn0, omega0), attr_list); } std::unique_ptr WavePortData::GetModeExcitationCoefficientImag() const { + const auto &port_submesh = static_cast(port_mesh->Get()); return std::make_unique( std::make_unique>( - *port_E0t, *port_E0n, mat_op, *port_mesh, submesh_parent_elems, kn0, omega0), + *port_E0t, *port_E0n, mat_op, port_submesh, submesh_parent_elems, kn0, omega0), attr_list); } std::unique_ptr WavePortData::GetModeFieldCoefficientReal() const { + const auto &port_submesh = static_cast(port_mesh->Get()); return std::make_unique( std::make_unique>( - *port_E0t, *port_E0n, *port_mesh, submesh_parent_elems), + *port_E0t, *port_E0n, port_submesh, submesh_parent_elems), attr_list); } std::unique_ptr WavePortData::GetModeFieldCoefficientImag() const { + const auto &port_submesh = static_cast(port_mesh->Get()); return std::make_unique( std::make_unique>( - *port_E0t, *port_E0n, *port_mesh, submesh_parent_elems), + *port_E0t, *port_E0n, port_submesh, submesh_parent_elems), attr_list); } @@ -969,7 +975,7 @@ std::complex WavePortData::GetSParameter(mfem::ParComplexGridFunction &E { // Compute port S-parameter, or the projection of the field onto the port mode: // (E x H_inc⋆) ⋅ n = E ⋅ (-n x H_inc⋆), integrated over the port surface. - mfem::ParComplexGridFunction port_E(port_nd_fespace.get()); + mfem::ParComplexGridFunction port_E(&port_nd_fespace->Get()); port_nd_transfer->Transfer(E.real(), port_E.real()); port_nd_transfer->Transfer(E.imag(), port_E.imag()); std::complex dot(-((*port_sr) * port_E.real()) - ((*port_si) * port_E.imag()), diff --git a/palace/models/waveportoperator.hpp b/palace/models/waveportoperator.hpp index 06fca08cd..fd5861fa1 100644 --- a/palace/models/waveportoperator.hpp +++ b/palace/models/waveportoperator.hpp @@ -10,6 +10,7 @@ #include #include #include "fem/fespace.hpp" +#include "fem/mesh.hpp" #include "linalg/eps.hpp" #include "linalg/ksp.hpp" #include "linalg/operator.hpp" @@ -49,7 +50,7 @@ class WavePortData private: // SubMesh data structures to define finite element spaces and grid functions on the // SubMesh corresponding to this port boundary. - std::unique_ptr port_mesh; + std::unique_ptr port_mesh; std::unique_ptr port_nd_fec, port_h1_fec; std::unique_ptr port_nd_fespace, port_h1_fespace; std::unique_ptr port_nd_transfer, port_h1_transfer; From b788f1d30ec02fc80e4adb6d75c306ef69c4aaa7 Mon Sep 17 00:00:00 2001 From: Sebastian Grimberg Date: Tue, 19 Dec 2023 10:59:53 -0800 Subject: [PATCH 10/32] WIP: Upgrade libCEED interface for mesh geometry factor quadrature data assembly --- palace/fem/CMakeLists.txt | 4 +- palace/fem/libceed/basis.cpp | 230 +++------ palace/fem/libceed/basis.hpp | 45 +- palace/fem/libceed/{utils.cpp => ceed.cpp} | 56 ++- palace/fem/libceed/{utils.hpp => ceed.hpp} | 21 +- palace/fem/libceed/coefficient.cpp | 182 +++++++ palace/fem/libceed/coefficient.hpp | 130 +---- palace/fem/libceed/hash.hpp | 174 ------- palace/fem/libceed/integrator.cpp | 555 +++++++++++++++++++++ palace/fem/libceed/integrator.hpp | 483 ++---------------- palace/fem/libceed/operator.cpp | 77 ++- palace/fem/libceed/operator.hpp | 16 +- palace/fem/libceed/restriction.cpp | 160 ++---- palace/fem/libceed/restriction.hpp | 26 +- palace/main.cpp | 2 +- 15 files changed, 1088 insertions(+), 1073 deletions(-) rename palace/fem/libceed/{utils.cpp => ceed.cpp} (63%) rename palace/fem/libceed/{utils.hpp => ceed.hpp} (73%) create mode 100644 palace/fem/libceed/coefficient.cpp delete mode 100644 palace/fem/libceed/hash.hpp create mode 100644 palace/fem/libceed/integrator.cpp diff --git a/palace/fem/CMakeLists.txt b/palace/fem/CMakeLists.txt index 2ee66a422..3811fd8d6 100644 --- a/palace/fem/CMakeLists.txt +++ b/palace/fem/CMakeLists.txt @@ -27,7 +27,9 @@ target_sources(${LIB_TARGET_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/integ/mixedvecgrad.cpp ${CMAKE_CURRENT_SOURCE_DIR}/integ/vecfemass.cpp ${CMAKE_CURRENT_SOURCE_DIR}/libceed/basis.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/libceed/ceed.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/libceed/coefficient.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/libceed/integrator.cpp ${CMAKE_CURRENT_SOURCE_DIR}/libceed/operator.cpp ${CMAKE_CURRENT_SOURCE_DIR}/libceed/restriction.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/libceed/utils.cpp ) diff --git a/palace/fem/libceed/basis.cpp b/palace/fem/libceed/basis.cpp index 72e8d6288..124270cf4 100644 --- a/palace/fem/libceed/basis.cpp +++ b/palace/fem/libceed/basis.cpp @@ -3,78 +3,24 @@ #include "basis.hpp" -#include "fem/libceed/hash.hpp" -#include "fem/libceed/utils.hpp" -#include "utils/omp.hpp" +#include namespace palace::ceed { -namespace internal -{ - -static std::unordered_map basis_map; -static std::unordered_map interp_basis_map; - -void ClearBasisCache() -{ - for (auto [k, v] : basis_map) - { - Ceed ceed; - PalaceCeedCallBackend(CeedBasisGetCeed(v, &ceed)); - PalaceCeedCall(ceed, CeedBasisDestroy(&v)); - } - for (auto [k, v] : interp_basis_map) - { - Ceed ceed; - PalaceCeedCallBackend(CeedBasisGetCeed(v, &ceed)); - PalaceCeedCall(ceed, CeedBasisDestroy(&v)); - } - basis_map.clear(); - interp_basis_map.clear(); -} - -} // namespace internal - namespace { -inline CeedElemTopology GetCeedTopology(mfem::Geometry::Type geom) -{ - switch (geom) - { - case mfem::Geometry::SEGMENT: - return CEED_TOPOLOGY_LINE; - case mfem::Geometry::TRIANGLE: - return CEED_TOPOLOGY_TRIANGLE; - case mfem::Geometry::SQUARE: - return CEED_TOPOLOGY_QUAD; - case mfem::Geometry::TETRAHEDRON: - return CEED_TOPOLOGY_TET; - case mfem::Geometry::CUBE: - return CEED_TOPOLOGY_HEX; - case mfem::Geometry::PRISM: - return CEED_TOPOLOGY_PRISM; - case mfem::Geometry::PYRAMID: - return CEED_TOPOLOGY_PYRAMID; - default: - MFEM_ABORT("This type of element is not supported!"); - return CEED_TOPOLOGY_LINE; // Silence compiler warning - } -} - -void InitTensorBasis(const mfem::ParFiniteElementSpace &fespace, - const mfem::FiniteElement &fe, const mfem::IntegrationRule &ir, - Ceed ceed, CeedBasis *basis) +void InitTensorBasis(const mfem::FiniteElement &fe, const mfem::IntegrationRule &ir, + CeedInt num_comp, Ceed ceed, CeedBasis *basis) { + // The x-coordinates of the first `Q` points of the integration rule are the points of + // the corresponding 1D rule. We also scale the weights accordingly. const mfem::DofToQuad &maps = fe.GetDofToQuad(ir, mfem::DofToQuad::TENSOR); const int dim = fe.GetDim(); - const int ncomp = fespace.GetVDim(); const int P = maps.ndof; const int Q = maps.nqpt; mfem::Vector qX(Q), qW(Q); - // The x-coordinates of the first `Q` points of the integration rule are the points of - // the corresponding 1D rule. We also scale the weights accordingly. double w_sum = 0.0; for (int i = 0; i < Q; i++) { @@ -84,18 +30,17 @@ void InitTensorBasis(const mfem::ParFiniteElementSpace &fespace, w_sum += ip.weight; } qW *= 1.0 / w_sum; - PalaceCeedCall(ceed, CeedBasisCreateTensorH1(ceed, dim, ncomp, P, Q, maps.Bt.GetData(), + + PalaceCeedCall(ceed, CeedBasisCreateTensorH1(ceed, dim, num_comp, P, Q, maps.Bt.GetData(), maps.Gt.GetData(), qX.GetData(), qW.GetData(), basis)); } -void InitNonTensorBasis(const mfem::ParFiniteElementSpace &fespace, - const mfem::FiniteElement &fe, const mfem::IntegrationRule &ir, - Ceed ceed, CeedBasis *basis) +void InitNonTensorBasis(const mfem::FiniteElement &fe, const mfem::IntegrationRule &ir, + CeedInt num_comp, Ceed ceed, CeedBasis *basis) { const mfem::DofToQuad &maps = fe.GetDofToQuad(ir, mfem::DofToQuad::FULL); const int dim = fe.GetDim(); - const int ncomp = fespace.GetVDim(); const int P = maps.ndof; const int Q = maps.nqpt; mfem::DenseMatrix qX(dim, Q); @@ -114,61 +59,62 @@ void InitNonTensorBasis(const mfem::ParFiniteElementSpace &fespace, } qW(i) = ip.weight; } + if (fe.GetMapType() == mfem::FiniteElement::H_DIV) { - PalaceCeedCall(ceed, CeedBasisCreateHdiv(ceed, GetCeedTopology(fe.GetGeomType()), ncomp, - P, Q, maps.Bt.GetData(), maps.Gt.GetData(), - qX.GetData(), qW.GetData(), basis)); + PalaceCeedCall(ceed, + CeedBasisCreateHdiv(ceed, GetCeedTopology(fe.GetGeomType()), num_comp, P, + Q, maps.Bt.GetData(), maps.Gt.GetData(), + qX.GetData(), qW.GetData(), basis)); } else if (fe.GetMapType() == mfem::FiniteElement::H_CURL) { PalaceCeedCall(ceed, - CeedBasisCreateHcurl(ceed, GetCeedTopology(fe.GetGeomType()), ncomp, P, - Q, maps.Bt.GetData(), maps.Gt.GetData(), + CeedBasisCreateHcurl(ceed, GetCeedTopology(fe.GetGeomType()), num_comp, + P, Q, maps.Bt.GetData(), maps.Gt.GetData(), qX.GetData(), qW.GetData(), basis)); } else { - PalaceCeedCall(ceed, CeedBasisCreateH1(ceed, GetCeedTopology(fe.GetGeomType()), ncomp, - P, Q, maps.Bt.GetData(), maps.Gt.GetData(), - qX.GetData(), qW.GetData(), basis)); + PalaceCeedCall(ceed, + CeedBasisCreateH1(ceed, GetCeedTopology(fe.GetGeomType()), num_comp, P, + Q, maps.Bt.GetData(), maps.Gt.GetData(), qX.GetData(), + qW.GetData(), basis)); } } -#if 0 -void InitCeedInterpolatorBasis(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::FiniteElement &trial_fe, - const mfem::FiniteElement &test_fe, - Ceed ceed, - CeedBasis *basis) +void InitCeedInterpolatorBasis(const mfem::FiniteElement &trial_fe, + const mfem::FiniteElement &test_fe, CeedInt trial_num_comp, + CeedInt test_num_comp, Ceed ceed, CeedBasis *basis) { - // Basis projection operator using libCEED - CeedBasis trial_basis, test_basis; - const int P = std::max(trial_fe.GetDof(), test_fe.GetDof()), ir_order_max = 100; - int ir_order = std::max(trial_fe.GetOrder(), test_fe.GetOrder()); - for (; ir_order < ir_order_max; ir_order++) - { - if (IntRules.Get(trial_fe.GetGeomType(), ir_order).GetNPoints() >= P) { break; } - } - const mfem::IntegrationRule &ir = IntRules.Get(trial_fe.GetGeomType(), ir_order); - InitBasis(trial_fespace, trial_fe, ir, ceed, &trial_basis); - InitBasis(test_fespace, test_fe, ir, ceed, &test_basis); - PalaceCeedCall(ceed, CeedBasisCreateProjection(trial_basis, test_basis, basis)); + // Basis projection operator using libCEED + CeedBasis trial_basis, test_basis; + const int P = std::max(trial_fe.GetDof(), test_fe.GetDof()), ir_order_max = 100; + int ir_order = std::max(trial_fe.GetOrder(), test_fe.GetOrder()); + for (; ir_order < ir_order_max; ir_order++) + { + if (mfem::IntRules.Get(trial_fe.GetGeomType(), ir_order).GetNPoints() >= P) + { + break; + } + } + const mfem::IntegrationRule &ir = mfem::IntRules.Get(trial_fe.GetGeomType(), ir_order); + + InitBasis(trial_fe, ir, trial_num_comp, ceed, &trial_basis), + InitBasis(test_fe, ir, test_num_comp, ceed, &test_basis); + PalaceCeedCall(ceed, CeedBasisCreateProjection(trial_basis, test_basis, basis)); + PalaceCeedCall(ceed, CeedBasisDestroy(&trial_basis)); + PalaceCeedCall(ceed, CeedBasisDestroy(&test_basis)); } -#endif -void InitMFEMInterpolatorBasis(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::FiniteElement &trial_fe, - const mfem::FiniteElement &test_fe, Ceed ceed, - CeedBasis *basis) +void InitMfemInterpolatorBasis(const mfem::FiniteElement &trial_fe, + const mfem::FiniteElement &test_fe, CeedInt trial_num_comp, + CeedInt test_num_comp, Ceed ceed, CeedBasis *basis) { - MFEM_VERIFY( - trial_fespace.GetVDim() == test_fespace.GetVDim(), - "libCEED discrete linear operator requires same vdim for trial and test FE spaces!"); + MFEM_VERIFY(trial_num_comp == test_num_comp && trial_num_comp == 1, + "libCEED discrete linear operator requires same vdim = 1 for trial and test " + "FE spaces!"); const int dim = trial_fe.GetDim(); - const int ncomp = trial_fespace.GetVDim(); const int trial_P = trial_fe.GetDof(); const int test_P = test_fe.GetDof(); mfem::DenseMatrix qX(dim, test_P), Gt(trial_P, test_P * dim), Bt; @@ -206,89 +152,59 @@ void InitMFEMInterpolatorBasis(const mfem::ParFiniteElementSpace &trial_fespace, Gt = 0.0; qX = 0.0; qW = 0.0; + PalaceCeedCall(ceed, CeedBasisCreateH1(ceed, GetCeedTopology(trial_fe.GetGeomType()), - ncomp, trial_P, test_P, Bt.GetData(), Gt.GetData(), - qX.GetData(), qW.GetData(), basis)); + trial_num_comp, trial_P, test_P, Bt.GetData(), + Gt.GetData(), qX.GetData(), qW.GetData(), basis)); } } // namespace -void InitBasis(const mfem::ParFiniteElementSpace &fespace, const mfem::FiniteElement &fe, - const mfem::IntegrationRule &ir, Ceed ceed, CeedBasis *basis) +void InitBasis(const mfem::FiniteElement &fe, const mfem::IntegrationRule &ir, + CeedInt num_comp, Ceed ceed, CeedBasis *basis) { - // Check for fespace -> basis in hash table. - internal::BasisKey key(ceed, fespace, fe, ir); - - // Initialize or retrieve key values (avoid simultaneous search and write). - auto basis_itr = internal::basis_map.end(); - PalacePragmaOmp(critical(InitBasis)) + if constexpr (false) { - basis_itr = internal::basis_map.find(key); + std::cout << "New basis (" << ceed << ", " << &fe << ", " << &ir << ")\n"; } - if (basis_itr == internal::basis_map.end()) + const bool tensor = dynamic_cast(&fe) != nullptr; + const bool vector = fe.GetRangeType() == mfem::FiniteElement::VECTOR; + if (tensor && !vector) { - const bool tensor = dynamic_cast(&fe) != nullptr; - const bool vector = fe.GetRangeType() == mfem::FiniteElement::VECTOR; - if (tensor && !vector) - { - InitTensorBasis(fespace, fe, ir, ceed, basis); - } - else - { - InitNonTensorBasis(fespace, fe, ir, ceed, basis); - } - PalacePragmaOmp(critical(InitBasis)) - { - internal::basis_map[key] = *basis; - } - // std::cout << "New basis (" << ceed << ", " << &fe << ", " << &ir << ")\n"; + InitTensorBasis(fe, ir, num_comp, ceed, basis); } else { - *basis = basis_itr->second; - // std::cout << "Reusing basis (" << ceed << ", " << &fe << ", " << &ir << ")\n"; + InitNonTensorBasis(fe, ir, num_comp, ceed, basis); } } -void InitInterpolatorBasis(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::FiniteElement &trial_fe, - const mfem::FiniteElement &test_fe, Ceed ceed, CeedBasis *basis) +void InitInterpolatorBasis(const mfem::FiniteElement &trial_fe, + const mfem::FiniteElement &test_fe, CeedInt trial_num_comp, + CeedInt test_num_comp, Ceed ceed, CeedBasis *basis) { - // Check for fespace -> basis in hash table. - internal::InterpBasisKey key(ceed, trial_fespace, test_fespace, trial_fe, test_fe); - - // Initialize or retrieve key values (avoid simultaneous search and write). - auto basis_itr = internal::interp_basis_map.end(); - PalacePragmaOmp(critical(InitInterpBasis)) + if constexpr (false) { - basis_itr = internal::interp_basis_map.find(key); + std::cout << "New interpolator basis (" << ceed << ", " << &trial_fe << ", " << &test_fe + << ")\n"; } - if (basis_itr == internal::interp_basis_map.end()) + if constexpr (false) { -#if 0 - if (trial_fe.GetMapType() == test_fe.GetMapType()) - { - InitCeedInterpolatorBasis(trial_fespace, test_fespace, trial_fe, test_fe, ceed, basis); - } - else -#endif + if (trial_fe.GetMapType() == test_fe.GetMapType()) { - InitMFEMInterpolatorBasis(trial_fespace, test_fespace, trial_fe, test_fe, ceed, + InitCeedInterpolatorBasis(trial_fe, test_fe, trial_num_comp, test_num_comp, ceed, basis); } - PalacePragmaOmp(critical(InitInterpBasis)) + else { - internal::interp_basis_map[key] = *basis; + InitMfemInterpolatorBasis(trial_fe, test_fe, trial_num_comp, test_num_comp, ceed, + basis); } - // std::cout << "New interpolator basis (" << ceed << ", " << &trial_fe - // << ", " << &test_fe << ")\n"; } else { - *basis = basis_itr->second; - // std::cout << "Reusing interpolator basis (" << ceed << ", " << &trial_fe - // << ", " << &test_fe << ")\n"; + InitMfemInterpolatorBasis(trial_fe, test_fe, trial_num_comp, test_num_comp, ceed, + basis); } } diff --git a/palace/fem/libceed/basis.hpp b/palace/fem/libceed/basis.hpp index d0e214946..0405bdaac 100644 --- a/palace/fem/libceed/basis.hpp +++ b/palace/fem/libceed/basis.hpp @@ -4,48 +4,25 @@ #ifndef PALACE_LIBCEED_BASIS_HPP #define PALACE_LIBCEED_BASIS_HPP -#include -#include -#include -#include +#include "fem/libceed/ceed.hpp" -namespace palace::ceed +namespace mfem { -void InitBasis(const mfem::ParFiniteElementSpace &fespace, const mfem::FiniteElement &fe, - const mfem::IntegrationRule &ir, Ceed ceed, CeedBasis *basis); +class FiniteElement; +class IntegrationRule; -inline void InitBasis(const mfem::ParFiniteElementSpace &fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, - bool use_bdr, Ceed ceed, CeedBasis *basis) -{ - const mfem::FiniteElement &fe = - use_bdr ? *fespace.GetBE(indices[0]) : *fespace.GetFE(indices[0]); - InitBasis(fespace, fe, ir, ceed, basis); -} - -void InitInterpolatorBasis(const mfem::ParFiniteElementSpace &trial_fes, - const mfem::ParFiniteElementSpace &test_fes, - const mfem::FiniteElement &trial_fe, - const mfem::FiniteElement &test_fe, Ceed ceed, CeedBasis *basis); - -inline void InitInterpolatorBasis(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const std::vector &indices, Ceed ceed, - CeedBasis *basis) -{ - const mfem::FiniteElement &trial_fe = *trial_fespace.GetFE(indices[0]); - const mfem::FiniteElement &test_fe = *test_fespace.GetFE(indices[0]); - InitInterpolatorBasis(trial_fespace, test_fespace, trial_fe, test_fe, ceed, basis); -} +} // namespace mfem -namespace internal +namespace palace::ceed { -// Destroy the cached CeedBasis objects. -void ClearBasisCache(); +void InitBasis(const mfem::FiniteElement &fe, const mfem::IntegrationRule &ir, int num_comp, + Ceed ceed, CeedBasis *basis); -} // namespace internal +void InitInterpolatorBasis(const mfem::FiniteElement &trial_fe, + const mfem::FiniteElement &test_fe, int trial_num_comp, + int test_num_comp, Ceed ceed, CeedBasis *basis); } // namespace palace::ceed diff --git a/palace/fem/libceed/utils.cpp b/palace/fem/libceed/ceed.cpp similarity index 63% rename from palace/fem/libceed/utils.cpp rename to palace/fem/libceed/ceed.cpp index 9ada7e1ab..43c555eb4 100644 --- a/palace/fem/libceed/utils.cpp +++ b/palace/fem/libceed/ceed.cpp @@ -1,10 +1,8 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -#include "utils.hpp" +#include "ceed.hpp" -#include "fem/libceed/basis.hpp" -#include "fem/libceed/restriction.hpp" #include "utils/omp.hpp" #if defined(MFEM_USE_OPENMP) @@ -63,10 +61,6 @@ void Initialize(const char *resource, const char *jit_source_dir) void Finalize() { - // Destroy global basis and element restriction caches. - internal::ClearBasisCache(); - internal::ClearRestrictionCache(); - // Destroy Ceed context(s). for (std::size_t i = 0; i < internal::ceeds.size(); i++) { @@ -105,4 +99,52 @@ void InitCeedVector(const mfem::Vector &v, Ceed ceed, CeedVector *cv) ceed, CeedVectorSetArray(*cv, mem, CEED_USE_POINTER, const_cast(data))); } +CeedElemTopology GetCeedTopology(mfem::Geometry::Type geom) +{ + switch (geom) + { + case mfem::Geometry::SEGMENT: + return CEED_TOPOLOGY_LINE; + case mfem::Geometry::TRIANGLE: + return CEED_TOPOLOGY_TRIANGLE; + case mfem::Geometry::SQUARE: + return CEED_TOPOLOGY_QUAD; + case mfem::Geometry::TETRAHEDRON: + return CEED_TOPOLOGY_TET; + case mfem::Geometry::CUBE: + return CEED_TOPOLOGY_HEX; + case mfem::Geometry::PRISM: + return CEED_TOPOLOGY_PRISM; + case mfem::Geometry::PYRAMID: + return CEED_TOPOLOGY_PYRAMID; + default: + MFEM_ABORT("This type of element is not supported!"); + return CEED_TOPOLOGY_LINE; // Silence compiler warning + } +} + +mfem::Geometry::Type GetMfemTopology(CeedElemTopology geom) +{ + switch (geom) + { + case CEED_TOPOLOGY_LINE: + return mfem::Geometry::SEGMENT; + case CEED_TOPOLOGY_TRIANGLE: + return mfem::Geometry::TRIANGLE; + case CEED_TOPOLOGY_QUAD: + return mfem::Geometry::SQUARE; + case CEED_TOPOLOGY_TET: + return mfem::Geometry::TETRAHEDRON; + case CEED_TOPOLOGY_HEX: + return mfem::Geometry::CUBE; + case CEED_TOPOLOGY_PRISM: + return mfem::Geometry::PRISM; + case CEED_TOPOLOGY_PYRAMID: + return mfem::Geometry::PYRAMID; + default: + MFEM_ABORT("This type of element is not supported!"); + return mfem::Geometry::SEGMENT; // Silence compiler warning + } +} + } // namespace palace::ceed diff --git a/palace/fem/libceed/utils.hpp b/palace/fem/libceed/ceed.hpp similarity index 73% rename from palace/fem/libceed/utils.hpp rename to palace/fem/libceed/ceed.hpp index 6009727c6..69a2a2286 100644 --- a/palace/fem/libceed/utils.hpp +++ b/palace/fem/libceed/ceed.hpp @@ -1,11 +1,11 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -#ifndef PALACE_LIBCEED_UTILS_HPP -#define PALACE_LIBCEED_UTILS_HPP +#ifndef PALACE_LIBCEED_CEED_HPP +#define PALACE_LIBCEED_CEED_HPP -#include #include +#include #include #include #include @@ -37,6 +37,13 @@ namespace palace::ceed { +// Useful alias templates for libCEED objects specific to a specific Ceed context and +// element geometry type. +template +using CeedGeomObjectMap = std::unordered_map; +template +using CeedObjectMap = std::unordered_map>; + // Call libCEED's CeedInit for the given resource. The specific device to use is set prior // to this using mfem::Device. void Initialize(const char *resource, const char *jit_source_dir); @@ -50,6 +57,12 @@ std::string Print(); // Initialize a CeedVector from an mfem::Vector. void InitCeedVector(const mfem::Vector &v, Ceed ceed, CeedVector *cv); +// Convert an MFEM geometry type to a libCEED one. +CeedElemTopology GetCeedTopology(mfem::Geometry::Type geom); + +// Convert a libCEED geometry type to an MFEM one. +mfem::Geometry::Type GetMfemTopology(CeedElemTopology geom); + namespace internal { @@ -60,4 +73,4 @@ const std::vector &GetCeedObjects(); } // namespace palace::ceed -#endif // PALACE_LIBCEED_UTILS_HPP +#endif // PALACE_LIBCEED_OPERATOR_HPP diff --git a/palace/fem/libceed/coefficient.cpp b/palace/fem/libceed/coefficient.cpp new file mode 100644 index 000000000..b0374de3c --- /dev/null +++ b/palace/fem/libceed/coefficient.cpp @@ -0,0 +1,182 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#include "coefficient.hpp" + +#include +#include "fem/libceed/ceed.hpp" +#include "models/materialoperator.hpp" + +#include "fem/qfunctions/coeff_qf.h" + +namespace palace::ceed +{ + +namespace +{ + +inline constexpr auto DefaultNumAttr() +{ + return 64; +} + +template +inline constexpr auto CoeffDim() +{ + return DIM * (DIM + 1) / 2; +} + +template +auto InitDefaultCoefficient() +{ + // All entries are value-initialized to zero, including the material property coefficient. + std::vector ctx(2 + DefaultNumAttr() + CoeffDim(), {0}); + ctx[0].first = DefaultNumAttr(); + ctx[1 + DefaultNumAttr()].first = 1; + return ctx; +} + +template +void MakeDiagonalCoefficient(CeedIntScalar *mat_coeff, CeedScalar a, CeedInt k); + +template <> +void MakeDiagonalCoefficient<1>(CeedIntScalar *mat_coeff, CeedScalar a, CeedInt k) +{ + mat_coeff[k].second = a; +} + +template <> +void MakeDiagonalCoefficient<2>(CeedIntScalar *mat_coeff, CeedScalar a, CeedInt k) +{ + mat_coeff[3 * k + 0].second = a; + mat_coeff[3 * k + 1].second = 0.0; + mat_coeff[3 * k + 2].second = a; +} + +template <> +void MakeDiagonalCoefficient<3>(CeedIntScalar *mat_coeff, CeedScalar a, CeedInt k) +{ + mat_coeff[6 * k + 0].second = a; + mat_coeff[6 * k + 1].second = 0.0; + mat_coeff[6 * k + 2].second = 0.0; + mat_coeff[6 * k + 3].second = a; + mat_coeff[6 * k + 4].second = 0.0; + mat_coeff[6 * k + 5].second = a; +} + +inline auto *AttrMat(CeedIntScalar *ctx) +{ + return ctx + 1; +} + +inline auto *MatCoeff(CeedIntScalar *ctx) +{ + const CeedInt num_attr = ctx[0].first; + return ctx + 2 + num_attr; +} + +} // namespace + +template +std::vector PopulateCoefficientContext(const MaterialPropertyCoefficient *Q, + double a) +{ + if (!Q) + { + // All attributes map to identity coefficient. + auto ctx = InitDefaultCoefficient(); + MakeDiagonalCoefficient(MatCoeff(ctx.data()), a, 0); + return ctx; + } + + const auto &attr_mat = Q->GetAttributeToMaterial(); + const auto &mat_coeff = Q->GetMaterialProperties(); + MFEM_VERIFY(attr_mat.Size() > 0, "Empty attributes for MaterialPropertyCoefficient!"); + MFEM_VERIFY(mat_coeff.SizeK() > 0, + "Empty material properties for MaterialPropertyCoefficient!"); + MFEM_VERIFY(attr_mat.Max() < mat_coeff.SizeK(), + "Invalid attribute material property for MaterialPropertyCoefficient (" + << attr_mat.Max() << " vs. " << mat_coeff.SizeK() << ")!"); + MFEM_VERIFY(mat_coeff.SizeI() == mat_coeff.SizeJ() && + (mat_coeff.SizeI() == 1 || mat_coeff.SizeI() == DIM), + "Dimension mismatch for MaterialPropertyCoefficient and libCEED integrator!"); + + // Map unassigned attributes to zero material property coefficient (the last material + // property is reserved for zero). + std::vector ctx(2 + attr_mat.Size() + + CoeffDim() * (mat_coeff.SizeK() + 1)); + ctx[0].first = attr_mat.Size(); + const int zero_mat = mat_coeff.SizeK(); + for (int i = 0; i < attr_mat.Size(); i++) + { + const int k = attr_mat[i]; + AttrMat(ctx.data())[i].first = (k < 0) ? zero_mat : k; + } + + // Copy material properties: Matrix-valued material properties are always assumed to be + // symmetric and we store only the lower triangular part. + ctx[1 + attr_mat.Size()].first = mat_coeff.SizeK() + 1; + const int dim = mat_coeff.SizeI(); + for (int k = 0; k < mat_coeff.SizeK(); k++) + { + if (dim == 1) + { + // Copy as diagonal matrix coefficient. + MakeDiagonalCoefficient(MatCoeff(ctx.data()), a * mat_coeff(0, 0, k), k); + } + else + { + for (int dj = 0; dj < dim; ++dj) + { + for (int di = dj; di < dim; ++di) + { + const int idx = (dj * dim) - (((dj - 1) * dj) / 2) + di - dj; + MatCoeff(ctx.data())[CoeffDim() * k + idx].second = + a * mat_coeff(di, dj, k); // Column-major + } + } + } + } + + return ctx; +} + +template +std::vector +PopulateCoefficientContext(const MaterialPropertyCoefficient *Q, + const MaterialPropertyCoefficient *Q_mass, double a, + double a_mass) +{ + auto ctx = PopulateCoefficientContext(Q, a); + auto ctx_mass = PopulateCoefficientContext(Q_mass, a_mass); + ctx.insert(ctx.end(), ctx_mass.begin(), ctx_mass.end()); + return ctx; +} + +template std::vector +PopulateCoefficientContext<1>(const MaterialPropertyCoefficient *, double); +template std::vector +PopulateCoefficientContext<2>(const MaterialPropertyCoefficient *, double); +template std::vector +PopulateCoefficientContext<3>(const MaterialPropertyCoefficient *, double); + +template std::vector +PopulateCoefficientContext<2, 1>(const MaterialPropertyCoefficient *, + const MaterialPropertyCoefficient *, double, double); +template std::vector +PopulateCoefficientContext<3, 1>(const MaterialPropertyCoefficient *, + const MaterialPropertyCoefficient *, double, double); +template std::vector +PopulateCoefficientContext<1, 2>(const MaterialPropertyCoefficient *, + const MaterialPropertyCoefficient *, double, double); +template std::vector +PopulateCoefficientContext<1, 3>(const MaterialPropertyCoefficient *, + const MaterialPropertyCoefficient *, double, double); +template std::vector +PopulateCoefficientContext<2, 2>(const MaterialPropertyCoefficient *, + const MaterialPropertyCoefficient *, double, double); +template std::vector +PopulateCoefficientContext<3, 3>(const MaterialPropertyCoefficient *, + const MaterialPropertyCoefficient *, double, double); + +} // namespace palace::ceed diff --git a/palace/fem/libceed/coefficient.hpp b/palace/fem/libceed/coefficient.hpp index c5c7698c6..d91c826b8 100644 --- a/palace/fem/libceed/coefficient.hpp +++ b/palace/fem/libceed/coefficient.hpp @@ -5,127 +5,29 @@ #define PALACE_LIBCEED_COEFFICIENT_HPP #include -#include -#include -namespace palace::ceed -{ +union CeedIntScalar; -struct QuadratureCoefficient +namespace palace { - int ncomp; - mfem::Vector data; -}; -inline void InitCoefficient(mfem::Coefficient &Q, mfem::ParMesh &mesh, - const mfem::IntegrationRule &ir, - const std::vector &indices, bool use_bdr, - QuadratureCoefficient &coeff) -{ - const auto ne = indices.size(); - const auto nqpts = ir.GetNPoints(); - coeff.ncomp = 1; - coeff.data.SetSize(ne * nqpts); - auto C = mfem::Reshape(coeff.data.HostWrite(), nqpts, ne); - mfem::IsoparametricTransformation T; - for (std::size_t i = 0; i < ne; ++i) - { - const auto e = indices[i]; - if (use_bdr) - { - mesh.GetBdrElementTransformation(e, &T); - } - else - { - mesh.GetElementTransformation(e, &T); - } - for (int q = 0; q < nqpts; ++q) - { - const mfem::IntegrationPoint &ip = ir.IntPoint(q); - T.SetIntPoint(&ip); - C(q, i) = Q.Eval(T, ip); - } - } -} +class MaterialPropertyCoefficient; -inline void InitCoefficient(mfem::VectorCoefficient &VQ, mfem::ParMesh &mesh, - const mfem::IntegrationRule &ir, - const std::vector &indices, bool use_bdr, - QuadratureCoefficient &coeff) +namespace ceed { - const auto ne = indices.size(); - const auto vdim = VQ.GetVDim(); - const auto nqpts = ir.GetNPoints(); - coeff.ncomp = vdim; - coeff.data.SetSize(ne * nqpts * vdim); - auto C = mfem::Reshape(coeff.data.HostWrite(), vdim, nqpts, ne); - mfem::IsoparametricTransformation T; - mfem::DenseMatrix Q_ip(vdim, nqpts); - for (std::size_t i = 0; i < ne; ++i) - { - const auto e = indices[i]; - if (use_bdr) - { - mesh.GetBdrElementTransformation(e, &T); - } - else - { - mesh.GetElementTransformation(e, &T); - } - VQ.Eval(Q_ip, T, ir); - for (int q = 0; q < nqpts; ++q) - { - for (int d = 0; d < vdim; ++d) - { - C(d, q, i) = Q_ip(d, q); - } - } - } -} -inline void InitCoefficient(mfem::MatrixCoefficient &MQ, mfem::ParMesh &mesh, - const mfem::IntegrationRule &ir, - const std::vector &indices, bool use_bdr, - QuadratureCoefficient &coeff) -{ - // Assumes matrix coefficient is symmetric. - const auto ne = indices.size(); - const auto vdim = MQ.GetVDim(); - const auto ncomp = (vdim * (vdim + 1)) / 2; - const auto nqpts = ir.GetNPoints(); - coeff.ncomp = ncomp; - coeff.data.SetSize(ne * nqpts * ncomp); - auto C = mfem::Reshape(coeff.data.HostWrite(), ncomp, nqpts, ne); - mfem::IsoparametricTransformation T; - mfem::DenseMatrix Q_ip(vdim); - for (std::size_t i = 0; i < ne; ++i) - { - const auto e = indices[i]; - if (use_bdr) - { - mesh.GetBdrElementTransformation(e, &T); - } - else - { - mesh.GetElementTransformation(e, &T); - } - for (int q = 0; q < nqpts; ++q) - { - const mfem::IntegrationPoint &ip = ir.IntPoint(q); - T.SetIntPoint(&ip); - MQ.Eval(Q_ip, T, ip); - for (int dj = 0; dj < vdim; ++dj) - { - for (int di = dj; di < vdim; ++di) - { - const int idx = (dj * vdim) - (((dj - 1) * dj) / 2) + di - dj; - C(idx, q, i) = Q_ip(di, dj); // Column-major - } - } - } - } -} +template +std::vector PopulateCoefficientContext(const MaterialPropertyCoefficient *Q, + double a = 1.0); + +template +std::vector +PopulateCoefficientContext(const MaterialPropertyCoefficient *Q, + const MaterialPropertyCoefficient *Q_mass, double a = 1.0, + double a_mass = 1.0); + +} // namespace ceed -} // namespace palace::ceed +} // namespace palace #endif // PALACE_LIBCEED_COEFFICIENT_HPP diff --git a/palace/fem/libceed/hash.hpp b/palace/fem/libceed/hash.hpp deleted file mode 100644 index 83a6f75c4..000000000 --- a/palace/fem/libceed/hash.hpp +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -#ifndef PALACE_LIBCEED_HASH_HPP -#define PALACE_LIBCEED_HASH_HPP - -#include -#include -#include -#include -#include "fem/fespace.hpp" - -namespace palace::ceed -{ - -// Base case for combining hashes. -inline void CeedHashCombine(std::size_t &seed) {} - -// See for example https://onlinelibrary.wiley.com/doi/abs/10.1002/asi.10170, the source -// of https://www.boost.org/doc/libs/1_35_0/doc/html/boost/hash_combine_id241013.html. -template -inline void CeedHashCombine(std::size_t &seed, const T &v, const U &...args) -{ - std::hash hasher; - seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); - (CeedHashCombine(seed, args), ...); -} - -namespace internal -{ - -struct FiniteElementKey -{ - mfem::Geometry::Type type; - int order, P; - int space, range_type, map_type, deriv_type, deriv_range_type, deriv_map_type; - FiniteElementKey(const mfem::FiniteElement &fe) - : type(fe.GetGeomType()), order(fe.GetOrder()), P(fe.GetDof()), space(fe.Space()), - range_type(fe.GetRangeType()), map_type(fe.GetMapType()), - deriv_type(fe.GetDerivType()), deriv_range_type(fe.GetDerivRangeType()), - deriv_map_type(fe.GetDerivMapType()) - { - } - bool operator==(const FiniteElementKey &k) const - { - return (type == k.type && order == k.order && P == k.P && space == k.space && - range_type == k.range_type && map_type == k.map_type && - deriv_type == k.deriv_type && deriv_range_type == k.deriv_range_type && - deriv_map_type == k.deriv_map_type); - } -}; - -using FiniteElementPairKey = std::pair; - -struct FiniteElementPairHash -{ - std::size_t operator()(const FiniteElementPairKey &k) const - { - std::size_t hash = 0; - CeedHashCombine(hash, k.first, k.second); - return hash; - } -}; - -struct BasisKey -{ - Ceed ceed; - FiniteElementKey fe; - int qorder, nqpts, ncomp; - BasisKey(Ceed ceed, const mfem::ParFiniteElementSpace &fespace, - const mfem::FiniteElement &fe, const mfem::IntegrationRule &ir) - : ceed(ceed), fe(fe), qorder(ir.GetOrder()), nqpts(ir.GetNPoints()), - ncomp(fespace.GetVDim()) - { - } - bool operator==(const BasisKey &k) const - { - return (ceed == k.ceed && fe == k.fe && qorder == k.qorder && nqpts == k.nqpts && - ncomp == k.ncomp); - } -}; - -struct BasisHash -{ - std::size_t operator()(const BasisKey &k) const - { - std::size_t hash = 0; - CeedHashCombine(hash, k.ceed, k.fe, k.qorder, k.nqpts, k.ncomp); - return hash; - } -}; - -struct InterpBasisKey -{ - Ceed ceed; - FiniteElementKey trial_fe, test_fe; - int ncomp; - InterpBasisKey(Ceed ceed, const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::FiniteElement &trial_fe, const mfem::FiniteElement &test_fe) - : ceed(ceed), trial_fe(trial_fe), test_fe(test_fe), ncomp(trial_fespace.GetVDim()) - { - } - bool operator==(const InterpBasisKey &k) const - { - return (ceed == k.ceed && trial_fe == k.trial_fe && test_fe == k.test_fe && - ncomp == k.ncomp); - } -}; - -struct InterpBasisHash -{ - std::size_t operator()(const InterpBasisKey &k) const - { - std::size_t hash = 0; - CeedHashCombine(hash, k.ceed, k.trial_fe, k.test_fe, k.ncomp); - return hash; - } -}; - -struct RestrKey -{ - Ceed ceed; - std::size_t fespace, first_elem; - bool use_bdr, unique_interp_restr, unique_interp_range_restr; - RestrKey(Ceed ceed, const FiniteElementSpace &fespace, std::size_t first_elem, - bool use_bdr, bool unique_interp_restr, bool unique_interp_range_restr) - : ceed(ceed), fespace(fespace.GetId()), first_elem(first_elem), use_bdr(use_bdr), - unique_interp_restr(unique_interp_restr), - unique_interp_range_restr(unique_interp_range_restr) - { - } - bool operator==(const RestrKey &k) const - { - return (ceed == k.ceed && fespace == k.fespace && first_elem == k.first_elem && - use_bdr == k.use_bdr && unique_interp_restr == k.unique_interp_restr && - unique_interp_range_restr == k.unique_interp_range_restr); - } -}; - -struct RestrHash -{ - std::size_t operator()(const RestrKey &k) const - { - std::size_t hash = 0; - CeedHashCombine(hash, k.ceed, k.fespace, k.first_elem, k.use_bdr, k.unique_interp_restr, - k.unique_interp_range_restr); - return hash; - } -}; - -} // namespace internal - -} // namespace palace::ceed - -namespace std -{ - -template <> -struct hash -{ - std::size_t operator()(const palace::ceed::internal::FiniteElementKey &k) const noexcept - { - std::size_t hash = 0; - palace::ceed::CeedHashCombine(hash, k.type, k.order, k.P, k.space, k.range_type, - k.map_type, k.deriv_type, k.deriv_range_type, - k.deriv_map_type); - return hash; - } -}; - -} // namespace std - -#endif // PALACE_LIBCEED_HASH_HPP diff --git a/palace/fem/libceed/integrator.cpp b/palace/fem/libceed/integrator.cpp new file mode 100644 index 000000000..b465eef1e --- /dev/null +++ b/palace/fem/libceed/integrator.cpp @@ -0,0 +1,555 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#include "integrator.hpp" + +#include +#include +#include "utils/diagnostic.hpp" + +PalacePragmaDiagnosticPush +PalacePragmaDiagnosticDisableUnused + +#include "fem/qfunctions/apply_qf.h" +#include "fem/qfunctions/geom_qf.h" + +PalacePragmaDiagnosticPop + +namespace palace::ceed +{ + +namespace +{ + +void AddQFunctionActiveInputsOutputs(const IntegratorInfo &info, Ceed ceed, + CeedBasis trial_basis, CeedBasis test_basis, + CeedQFunction qf) +{ + CeedInt trial_num_comp, test_num_comp; + PalaceCeedCall(ceed, CeedBasisGetNumComponents(trial_basis, &trial_num_comp)); + PalaceCeedCall(ceed, CeedBasisGetNumComponents(test_basis, &test_num_comp)); + + // Inputs + if (info.trial_ops & EvalMode::None) + { + PalaceCeedCall(ceed, CeedQFunctionAddInput(qf, "u", trial_num_comp, CEED_EVAL_NONE)); + } + if (info.trial_ops & EvalMode::Interp) + { + CeedInt q_comp; + PalaceCeedCall( + ceed, CeedBasisGetNumQuadratureComponents(trial_basis, CEED_EVAL_INTERP, &q_comp)); + PalaceCeedCall( + ceed, CeedQFunctionAddInput(qf, "u", trial_num_comp * q_comp, CEED_EVAL_INTERP)); + } + if (info.trial_ops & EvalMode::Grad) + { + CeedInt q_comp; + PalaceCeedCall( + ceed, CeedBasisGetNumQuadratureComponents(trial_basis, CEED_EVAL_GRAD, &q_comp)); + PalaceCeedCall( + ceed, CeedQFunctionAddInput(qf, "grad_u", trial_num_comp * q_comp, CEED_EVAL_GRAD)); + } + if (info.trial_ops & EvalMode::Div) + { + CeedInt q_comp; + PalaceCeedCall( + ceed, CeedBasisGetNumQuadratureComponents(trial_basis, CEED_EVAL_DIV, &q_comp)); + PalaceCeedCall( + ceed, CeedQFunctionAddInput(qf, "div_u", trial_num_comp * q_comp, CEED_EVAL_DIV)); + } + if (info.trial_ops & EvalMode::Curl) + { + CeedInt q_comp; + PalaceCeedCall( + ceed, CeedBasisGetNumQuadratureComponents(trial_basis, CEED_EVAL_CURL, &q_comp)); + PalaceCeedCall( + ceed, CeedQFunctionAddInput(qf, "curl_u", trial_num_comp * q_comp, CEED_EVAL_CURL)); + } + + // Outputs + if (info.test_ops & EvalMode::None) + { + PalaceCeedCall(ceed, CeedQFunctionAddOutput(qf, "v", test_num_comp, CEED_EVAL_NONE)); + } + if (info.test_ops & EvalMode::Interp) + { + CeedInt q_comp; + PalaceCeedCall( + ceed, CeedBasisGetNumQuadratureComponents(test_basis, CEED_EVAL_INTERP, &q_comp)); + PalaceCeedCall( + ceed, CeedQFunctionAddOutput(qf, "v", test_num_comp * q_comp, CEED_EVAL_INTERP)); + } + if (info.test_ops & EvalMode::Grad) + { + CeedInt q_comp; + PalaceCeedCall( + ceed, CeedBasisGetNumQuadratureComponents(test_basis, CEED_EVAL_GRAD, &q_comp)); + PalaceCeedCall( + ceed, CeedQFunctionAddOutput(qf, "grad_v", test_num_comp * q_comp, CEED_EVAL_GRAD)); + } + if (info.test_ops & EvalMode::Div) + { + CeedInt q_comp; + PalaceCeedCall(ceed, + CeedBasisGetNumQuadratureComponents(test_basis, CEED_EVAL_DIV, &q_comp)); + PalaceCeedCall( + ceed, CeedQFunctionAddOutput(qf, "div_v", test_num_comp * q_comp, CEED_EVAL_DIV)); + } + if (info.test_ops & EvalMode::Curl) + { + CeedInt q_comp; + PalaceCeedCall( + ceed, CeedBasisGetNumQuadratureComponents(test_basis, CEED_EVAL_CURL, &q_comp)); + PalaceCeedCall( + ceed, CeedQFunctionAddOutput(qf, "curl_v", test_num_comp * q_comp, CEED_EVAL_CURL)); + } +} + +void AddOperatorActiveFields(const IntegratorInfo &info, Ceed ceed, + CeedElemRestriction trial_restr, + CeedElemRestriction test_restr, CeedBasis trial_basis, + CeedBasis test_basis, CeedOperator op) +{ + if (info.trial_ops & EvalMode::None) + { + PalaceCeedCall(ceed, CeedOperatorSetField(op, "u", trial_restr, CEED_BASIS_NONE, + CEED_VECTOR_ACTIVE)); + } + if (info.trial_ops & EvalMode::Interp) + { + PalaceCeedCall( + ceed, CeedOperatorSetField(op, "u", trial_restr, trial_basis, CEED_VECTOR_ACTIVE)); + } + if (info.trial_ops & EvalMode::Grad) + { + PalaceCeedCall(ceed, CeedOperatorSetField(op, "grad_u", trial_restr, trial_basis, + CEED_VECTOR_ACTIVE)); + } + if (info.trial_ops & EvalMode::Div) + { + PalaceCeedCall(ceed, CeedOperatorSetField(op, "div_u", trial_restr, trial_basis, + CEED_VECTOR_ACTIVE)); + } + if (info.trial_ops & EvalMode::Curl) + { + PalaceCeedCall(ceed, CeedOperatorSetField(op, "curl_u", trial_restr, trial_basis, + CEED_VECTOR_ACTIVE)); + } + + if (info.test_ops & EvalMode::None) + { + PalaceCeedCall(ceed, CeedOperatorSetField(op, "v", test_restr, CEED_BASIS_NONE, + CEED_VECTOR_ACTIVE)); + } + if (info.test_ops & EvalMode::Interp) + { + PalaceCeedCall( + ceed, CeedOperatorSetField(op, "v", test_restr, test_basis, CEED_VECTOR_ACTIVE)); + } + if (info.test_ops & EvalMode::Grad) + { + PalaceCeedCall(ceed, CeedOperatorSetField(op, "grad_v", test_restr, test_basis, + CEED_VECTOR_ACTIVE)); + } + if (info.test_ops & EvalMode::Div) + { + PalaceCeedCall(ceed, CeedOperatorSetField(op, "div_v", test_restr, test_basis, + CEED_VECTOR_ACTIVE)); + } + if (info.test_ops & EvalMode::Curl) + { + PalaceCeedCall(ceed, CeedOperatorSetField(op, "curl_v", test_restr, test_basis, + CEED_VECTOR_ACTIVE)); + } +} + +std::vector QuadratureDataSetup(const IntegratorInfo &info, Ceed ceed, + CeedElemRestriction trial_restr, + CeedBasis trial_basis, CeedVector *q_data, + CeedElemRestriction *q_data_restr) +{ + // Operator application at each quadrature point should be square, so just use the inputs + // and ignore the outputs. + CeedInt trial_num_comp; + PalaceCeedCall(ceed, CeedBasisGetNumComponents(trial_basis, &trial_num_comp)); + + std::vector active_input_sizes; + if (info.trial_ops & EvalMode::None) + { + active_input_sizes.push_back(trial_num_comp); + } + if (info.trial_ops & EvalMode::Interp) + { + CeedInt q_comp; + PalaceCeedCall( + ceed, CeedBasisGetNumQuadratureComponents(trial_basis, CEED_EVAL_INTERP, &q_comp)); + active_input_sizes.push_back(trial_num_comp * q_comp); + } + if (info.trial_ops & EvalMode::Grad) + { + CeedInt q_comp; + PalaceCeedCall( + ceed, CeedBasisGetNumQuadratureComponents(trial_basis, CEED_EVAL_GRAD, &q_comp)); + active_input_sizes.push_back(trial_num_comp * q_comp); + } + if (info.trial_ops & EvalMode::Div) + { + CeedInt q_comp; + PalaceCeedCall( + ceed, CeedBasisGetNumQuadratureComponents(trial_basis, CEED_EVAL_DIV, &q_comp)); + active_input_sizes.push_back(trial_num_comp * q_comp); + } + if (info.trial_ops & EvalMode::Curl) + { + CeedInt q_comp; + PalaceCeedCall( + ceed, CeedBasisGetNumQuadratureComponents(trial_basis, CEED_EVAL_CURL, &q_comp)); + active_input_sizes.push_back(trial_num_comp * q_comp); + } + + CeedInt num_elem, num_qpts, q_data_size = 0; + PalaceCeedCall(ceed, CeedElemRestrictionGetNumElements(trial_restr, &num_elem)); + PalaceCeedCall(ceed, CeedBasisGetNumQuadraturePoints(trial_basis, &num_qpts)); + for (auto size : active_input_sizes) + { + q_data_size += size * (size + 1) / 2; + } + + PalaceCeedCall(ceed, CeedVectorCreate(ceed, num_elem * num_qpts * q_data_size, q_data)); + PalaceCeedCall(ceed, + CeedElemRestrictionCreateStrided(ceed, num_elem, num_qpts, q_data_size, + num_elem * num_qpts * q_data_size, + CEED_STRIDES_BACKEND, q_data_restr)); + + return active_input_sizes; +} + +void QuadratureDataAssembly(const std::vector &qf_active_sizes, + const IntegratorInfo &info, Ceed ceed, + CeedElemRestriction trial_restr, CeedElemRestriction test_restr, + CeedBasis trial_basis, CeedBasis test_basis, CeedVector q_data, + CeedElemRestriction q_data_restr, CeedOperator *op) +{ + // Assemble the quadrature data, destroy the operator, and create a new one for the + // actual operator application. + PalaceCeedCall(ceed, + CeedOperatorApply(*op, CEED_VECTOR_NONE, q_data, CEED_REQUEST_IMMEDIATE)); + PalaceCeedCall(ceed, CeedOperatorDestroy(op)); + + MFEM_VERIFY(!qf_active_sizes.empty() && qf_active_sizes.size() <= 2, + "Invalid number of active QFunction input/output fields (" + << qf_active_sizes.size() << ")!"); + CeedQFunction apply_qf; + CeedInt qf_size_1 = qf_active_sizes[0], + qf_size_2 = (qf_active_sizes.size() > 1) ? qf_active_sizes[1] : 0; + switch (10 * qf_size_1 + qf_size_2) + { + case 1: + case 10: + PalaceCeedCall(ceed, CeedQFunctionCreateInterior( + ceed, 1, f_apply_1, + PalaceQFunctionRelativePath(f_apply_1_loc), &apply_qf)); + break; + case 2: + case 20: + PalaceCeedCall(ceed, CeedQFunctionCreateInterior( + ceed, 1, f_apply_2, + PalaceQFunctionRelativePath(f_apply_2_loc), &apply_qf)); + break; + case 3: + case 30: + PalaceCeedCall(ceed, CeedQFunctionCreateInterior( + ceed, 1, f_apply_3, + PalaceQFunctionRelativePath(f_apply_3_loc), &apply_qf)); + break; + case 22: + PalaceCeedCall(ceed, CeedQFunctionCreateInterior( + ceed, 1, f_apply_22, + PalaceQFunctionRelativePath(f_apply_22_loc), &apply_qf)); + break; + case 33: + PalaceCeedCall(ceed, CeedQFunctionCreateInterior( + ceed, 1, f_apply_33, + PalaceQFunctionRelativePath(f_apply_33_loc), &apply_qf)); + break; + case 12: + PalaceCeedCall(ceed, CeedQFunctionCreateInterior( + ceed, 1, f_apply_12, + PalaceQFunctionRelativePath(f_apply_12_loc), &apply_qf)); + break; + case 13: + PalaceCeedCall(ceed, CeedQFunctionCreateInterior( + ceed, 1, f_apply_13, + PalaceQFunctionRelativePath(f_apply_13_loc), &apply_qf)); + break; + case 21: + PalaceCeedCall(ceed, CeedQFunctionCreateInterior( + ceed, 1, f_apply_21, + PalaceQFunctionRelativePath(f_apply_21_loc), &apply_qf)); + break; + case 31: + PalaceCeedCall(ceed, CeedQFunctionCreateInterior( + ceed, 1, f_apply_31, + PalaceQFunctionRelativePath(f_apply_31_loc), &apply_qf)); + break; + default: + MFEM_ABORT("Invalid number of QFunction input/output components (" + << qf_size_1 << ", " << qf_size_2 << ")!"); + apply_qf = nullptr; // Silence compiler warning + } + + // Inputs + { + CeedInt q_data_size; + PalaceCeedCall(ceed, CeedElemRestrictionGetNumComponents(q_data_restr, &q_data_size)); + PalaceCeedCall(ceed, + CeedQFunctionAddInput(apply_qf, "q_data", q_data_size, CEED_EVAL_NONE)); + } + + // Active inputs/outputs + AddQFunctionActiveInputsOutputs(info, ceed, trial_basis, test_basis, apply_qf); + + // Create the operator. + PalaceCeedCall(ceed, CeedOperatorCreate(ceed, apply_qf, nullptr, nullptr, op)); + PalaceCeedCall(ceed, CeedQFunctionDestroy(&apply_qf)); + + PalaceCeedCall( + ceed, CeedOperatorSetField(*op, "q_data", q_data_restr, CEED_BASIS_NONE, q_data)); + + AddOperatorActiveFields(info, ceed, trial_restr, test_restr, trial_basis, test_basis, + *op); + + PalaceCeedCall(ceed, CeedOperatorCheckReady(*op)); +} + +} // namespace + +int CeedGeometryDataGetSpaceDimension(CeedElemRestriction geom_data_restr, CeedInt dim, + CeedInt *space_dim) +{ + if (space_dim) + { + Ceed ceed; + CeedInt geom_data_size; + PalaceCeedCallBackend(CeedElemRestrictionGetCeed(geom_data_restr, &ceed)); + PalaceCeedCall(ceed, + CeedElemRestrictionGetNumComponents(geom_data_restr, &geom_data_size)); + *space_dim = (geom_data_size - 2) / dim; + MFEM_ASSERT(2 + (*space_dim) * dim == geom_data_size, + "Invalid size for geometry quadrature data!"); + } + return CEED_ERROR_SUCCESS; +} + +void AssembleCeedGeometryData(Ceed ceed, CeedElemRestriction mesh_restr, + CeedBasis mesh_basis, CeedVector mesh_nodes, + CeedVector geom_data, CeedElemRestriction geom_data_restr) +{ + CeedInt dim, space_dim, num_elem, num_qpts; + PalaceCeedCall(ceed, CeedBasisGetDimension(mesh_basis, &dim)); + PalaceCeedCall(ceed, CeedBasisGetNumComponents(mesh_basis, &space_dim)); + PalaceCeedCall(ceed, CeedElemRestrictionGetNumElements(mesh_restr, &num_elem)); + PalaceCeedCall(ceed, CeedBasisGetNumQuadraturePoints(mesh_basis, &num_qpts)); + + // Create the QFunction that builds the operator (i.e. computes its quadrature data). + CeedQFunction build_qf; + switch (10 * space_dim + dim) + { + case 22: + PalaceCeedCall(ceed, CeedQFunctionCreateInterior( + ceed, 1, f_build_geom_factor_22, + PalaceQFunctionRelativePath(f_build_geom_factor_22_loc), + &build_qf)); + break; + case 33: + PalaceCeedCall(ceed, CeedQFunctionCreateInterior( + ceed, 1, f_build_geom_factor_33, + PalaceQFunctionRelativePath(f_build_geom_factor_33_loc), + &build_qf)); + break; + case 21: + PalaceCeedCall(ceed, CeedQFunctionCreateInterior( + ceed, 1, f_build_geom_factor_21, + PalaceQFunctionRelativePath(f_build_geom_factor_21_loc), + &build_qf)); + break; + case 32: + PalaceCeedCall(ceed, CeedQFunctionCreateInterior( + ceed, 1, f_build_geom_factor_32, + PalaceQFunctionRelativePath(f_build_geom_factor_32_loc), + &build_qf)); + break; + default: + MFEM_ABORT("Invalid value of (dim, space_dim) = (" + << dim << ", " << space_dim << ") for geometry factor quadrature data!"); + build_qf = nullptr; // Silence compiler warning + } + + // Inputs + PalaceCeedCall(ceed, CeedQFunctionAddInput(build_qf, "q_w", 1, CEED_EVAL_WEIGHT)); + PalaceCeedCall( + ceed, CeedQFunctionAddInput(build_qf, "grad_x", space_dim * dim, CEED_EVAL_GRAD)); + + // Outputs + { + CeedInt geom_data_size; + PalaceCeedCall(ceed, + CeedElemRestrictionGetNumComponents(geom_data_restr, &geom_data_size)); + MFEM_VERIFY(geom_data_size == 2 + space_dim * dim, + "Insufficient storage for geometry quadrature data!"); + PalaceCeedCall(ceed, CeedQFunctionAddOutput(build_qf, "geom_data", geom_data_size, + CEED_EVAL_NONE)); + } + + // Create the operator that builds the quadrature data. + CeedOperator build_op; + PalaceCeedCall(ceed, CeedOperatorCreate(ceed, build_qf, nullptr, nullptr, &build_op)); + PalaceCeedCall(ceed, CeedQFunctionDestroy(&build_qf)); + + PalaceCeedCall(ceed, CeedOperatorSetField(build_op, "q_w", CEED_ELEMRESTRICTION_NONE, + mesh_basis, CEED_VECTOR_NONE)); + PalaceCeedCall(ceed, CeedOperatorSetField(build_op, "grad_x", mesh_restr, mesh_basis, + CEED_VECTOR_ACTIVE)); + + PalaceCeedCall(ceed, CeedOperatorSetField(build_op, "geom_data", geom_data_restr, + CEED_BASIS_NONE, CEED_VECTOR_ACTIVE)); + + PalaceCeedCall(ceed, CeedOperatorCheckReady(build_op)); + + // Compute the quadrature data for the operator. + PalaceCeedCall( + ceed, CeedOperatorApply(build_op, mesh_nodes, geom_data, CEED_REQUEST_IMMEDIATE)); + PalaceCeedCall(ceed, CeedOperatorDestroy(&build_op)); +} + +void AssembleCeedOperator(const IntegratorInfo &info, void *ctx, std::size_t ctx_size, + Ceed ceed, CeedElemRestriction trial_restr, + CeedElemRestriction test_restr, CeedBasis trial_basis, + CeedBasis test_basis, CeedVector geom_data, + CeedElemRestriction geom_data_restr, CeedOperator *op) +{ + // If we are going to be assembling the quadrature data, construct the storage vector for + // it (to be owned by the operator). + CeedVector q_data = nullptr; + CeedElemRestriction q_data_restr = nullptr; + std::vector qf_active_sizes; + if (info.assemble_q_data) + { + qf_active_sizes = + QuadratureDataSetup(info, ceed, trial_restr, trial_basis, &q_data, &q_data_restr); + } + + // Create the QFunction that defines the action of the operator (or its setup). + CeedQFunction apply_qf; + PalaceCeedCall(ceed, CeedQFunctionCreateInterior(ceed, 1, info.apply_qf, + info.apply_qf_path.c_str(), &apply_qf)); + + CeedQFunctionContext apply_ctx; + PalaceCeedCall(ceed, CeedQFunctionContextCreate(ceed, &apply_ctx)); + PalaceCeedCall(ceed, CeedQFunctionContextSetData(apply_ctx, CEED_MEM_HOST, + CEED_COPY_VALUES, ctx_size, ctx)); + PalaceCeedCall(ceed, CeedQFunctionSetContext(apply_qf, apply_ctx)); + PalaceCeedCall(ceed, CeedQFunctionContextDestroy(&apply_ctx)); + + // Inputs + { + CeedInt geom_data_size; + PalaceCeedCall(ceed, + CeedElemRestrictionGetNumComponents(geom_data_restr, &geom_data_size)); + PalaceCeedCall( + ceed, CeedQFunctionAddInput(apply_qf, "geom_data", geom_data_size, CEED_EVAL_NONE)); + } + if (info.trial_ops & EvalMode::Weight) + { + PalaceCeedCall(ceed, CeedQFunctionAddInput(apply_qf, "q_w", 1, CEED_EVAL_WEIGHT)); + } + MFEM_VERIFY(!(info.test_ops & EvalMode::Weight), + "CeedOperator should not have quadrature weight output!"); + + // Active inputs/outputs + if (!info.assemble_q_data) + { + AddQFunctionActiveInputsOutputs(info, ceed, trial_basis, test_basis, apply_qf); + } + else + { + CeedInt q_data_size; + PalaceCeedCall(ceed, CeedElemRestrictionGetNumComponents(q_data_restr, &q_data_size)); + PalaceCeedCall(ceed, + CeedQFunctionAddOutput(apply_qf, "q_data", q_data_size, CEED_EVAL_NONE)); + } + + // Create the operator. + PalaceCeedCall(ceed, CeedOperatorCreate(ceed, apply_qf, nullptr, nullptr, op)); + PalaceCeedCall(ceed, CeedQFunctionDestroy(&apply_qf)); + + PalaceCeedCall(ceed, CeedOperatorSetField(*op, "geom_data", geom_data_restr, + CEED_BASIS_NONE, geom_data)); + if (info.trial_ops & EvalMode::Weight) + { + PalaceCeedCall(ceed, CeedOperatorSetField(*op, "q_w", CEED_ELEMRESTRICTION_NONE, + trial_basis, CEED_VECTOR_NONE)); + } + + if (!info.assemble_q_data) + { + AddOperatorActiveFields(info, ceed, trial_restr, test_restr, trial_basis, test_basis, + *op); + } + else + { + PalaceCeedCall(ceed, CeedOperatorSetField(*op, "q_data", q_data_restr, CEED_BASIS_NONE, + CEED_VECTOR_ACTIVE)); + } + + PalaceCeedCall(ceed, CeedOperatorCheckReady(*op)); + + // Assemble the quadrature data and create the actual operator. + if (info.assemble_q_data) + { + QuadratureDataAssembly(qf_active_sizes, info, ceed, trial_restr, test_restr, + trial_basis, test_basis, q_data, q_data_restr, op); + + // Cleanup (these are now owned by the operator). + PalaceCeedCall(ceed, CeedElemRestrictionDestroy(&q_data_restr)); + PalaceCeedCall(ceed, CeedVectorDestroy(&q_data)); + } +} + +void AssembleCeedInterpolator(Ceed ceed, CeedElemRestriction trial_restr, + CeedElemRestriction test_restr, CeedBasis interp_basis, + CeedOperator *op, CeedOperator *op_t) +{ + // Create the QFunction that defines the action of the operator (only an identity as + // element dof multiplicity is handled outside of libCEED). + CeedQFunction apply_qf, apply_qf_t; + PalaceCeedCall(ceed, CeedQFunctionCreateIdentity(ceed, 1, CEED_EVAL_INTERP, + CEED_EVAL_NONE, &apply_qf)); + PalaceCeedCall(ceed, CeedQFunctionCreateIdentity(ceed, 1, CEED_EVAL_NONE, + CEED_EVAL_INTERP, &apply_qf_t)); + + // Create the operator. + PalaceCeedCall(ceed, CeedOperatorCreate(ceed, apply_qf, nullptr, nullptr, op)); + PalaceCeedCall(ceed, CeedQFunctionDestroy(&apply_qf)); + + PalaceCeedCall(ceed, CeedOperatorSetField(*op, "input", trial_restr, interp_basis, + CEED_VECTOR_ACTIVE)); + PalaceCeedCall(ceed, CeedOperatorSetField(*op, "output", test_restr, CEED_BASIS_NONE, + CEED_VECTOR_ACTIVE)); + + PalaceCeedCall(ceed, CeedOperatorCheckReady(*op)); + + // Create the transpose operator. + PalaceCeedCall(ceed, CeedOperatorCreate(ceed, apply_qf_t, nullptr, nullptr, op_t)); + PalaceCeedCall(ceed, CeedQFunctionDestroy(&apply_qf_t)); + + PalaceCeedCall(ceed, CeedOperatorSetField(*op_t, "input", test_restr, CEED_BASIS_NONE, + CEED_VECTOR_ACTIVE)); + PalaceCeedCall(ceed, CeedOperatorSetField(*op_t, "output", trial_restr, interp_basis, + CEED_VECTOR_ACTIVE)); + + PalaceCeedCall(ceed, CeedOperatorCheckReady(*op_t)); +} + +} // namespace palace::ceed diff --git a/palace/fem/libceed/integrator.hpp b/palace/fem/libceed/integrator.hpp index 897d1add9..20161ac37 100644 --- a/palace/fem/libceed/integrator.hpp +++ b/palace/fem/libceed/integrator.hpp @@ -6,474 +6,69 @@ #include #include -#include -#include -#include "fem/libceed/basis.hpp" -#include "fem/libceed/coefficient.hpp" -#include "fem/libceed/restriction.hpp" -#include "fem/libceed/utils.hpp" +#include "fem/libceed/ceed.hpp" namespace palace::ceed { // Evaluation modes for CeedOperator fields for various integrators. -enum class EvalMode +enum EvalMode : unsigned int { - None, - Interp, - Grad, - Div, - Curl, - InterpAndGrad, - InterpAndDiv, - InterpAndCurl + Weight = 1 << 0, + None = 1 << 1, + Interp = 1 << 2, + Grad = 1 << 3, + Div = 1 << 4, + Curl = 1 << 5 }; // Data structure for CeedOperator construction for various integrators. struct IntegratorInfo { // QFunctions for operator construction and application. - CeedQFunctionUser build_qf, apply_qf; + CeedQFunctionUser apply_qf; // Path and name of the QFunctions for operator construction and application. - std::string build_qf_path, apply_qf_path; + std::string apply_qf_path; // Evaluation modes for the test and trial basis. - EvalMode trial_op, test_op; + unsigned int trial_ops, test_ops; - // Size of the data at each quadrature point. - int qdata_size; -}; - -// Helper function which combines quadrature data assembly and operator assembly in a single -// method. -template -inline void AssembleCeedOperator(const CeedIntegratorInfo &info, - const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, - const std::vector &indices, const bool use_bdr, - const std::vector &Q, Ceed ceed, - CeedOperator *op, CeedOperator *op_t) -{ - // Assemble quadrature data. - CeedVector qdata; - CeedElemRestriction qdata_restr; - AssembleCeedQuadratureData(info, trial_fespace, test_fespace, ir, indices, use_bdr, Q, - ceed, &qdata, &qdata_restr); - - // Assemble the operator (no transpose). - AssembleCeedOperator(info, trial_fespace, test_fespace, ir, indices, use_bdr, qdata, - qdata_restr, ceed, op); - *op_t = nullptr; - - // Cleanup (these are now owned by the operator). - PalaceCeedCall(ceed, CeedElemRestrictionDestroy(&qdata_restr)); - PalaceCeedCall(ceed, CeedVectorDestroy(&qdata)); -} - -// Create libCEED quadrature data and element restriction for use in a partially assembled -// libCEED operator. -template -inline void -AssembleCeedQuadratureData(const CeedIntegratorInfo &info, - const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, - const bool use_bdr, const std::vector &Q, - Ceed ceed, CeedVector *qdata, CeedElemRestriction *qdata_restr) -{ - MFEM_VERIFY(trial_fespace.GetParMesh() == test_fespace.GetParMesh(), - "Trial and test finite element spaces must correspond to the same mesh!"); - const mfem::ParMesh &mesh = *trial_fespace.GetParMesh(); - MFEM_VERIFY(mesh.GetNodes(), "The mesh has no nodal FE space!"); - const mfem::GridFunction &mesh_nodes = *mesh.GetNodes(); - MFEM_VERIFY(dynamic_cast(mesh_nodes.FESpace()), - "Unexpected non-parallel FiniteElementSpace for mesh nodes!"); - const mfem::ParFiniteElementSpace &mesh_fespace = - *dynamic_cast(mesh_nodes.FESpace()); - - CeedInt ne = static_cast(indices.size()); - CeedInt dim = mesh.Dimension() - use_bdr; - CeedInt space_dim = mesh.SpaceDimension(); - - CeedElemRestriction mesh_restr; - CeedBasis mesh_basis; - CeedInt nqpts, qdata_size = info.qdata_size; - InitRestriction(mesh_fespace, indices, use_bdr, ceed, &mesh_restr); - InitBasis(mesh_fespace, ir, indices, use_bdr, ceed, &mesh_basis); - PalaceCeedCall(ceed, CeedBasisGetNumQuadraturePoints(mesh_basis, &nqpts)); - - // Strided restrictions are cheap to construct and not stored in the global cache. - PalaceCeedCall(ceed, CeedVectorCreate(ceed, ne * nqpts * qdata_size, qdata)); - PalaceCeedCall(ceed, CeedElemRestrictionCreateStrided(ceed, ne, nqpts, qdata_size, - ne * nqpts * qdata_size, - CEED_STRIDES_BACKEND, qdata_restr)); - - // Create the QFunction that builds the operator (i.e. computes its quadrature data). - CeedQFunction build_qf; - PalaceCeedCall(ceed, CeedQFunctionCreateInterior(ceed, 1, info.build_qf, - info.build_qf_path.c_str(), &build_qf)); - - CeedQFunctionContext build_ctx; - PalaceCeedCall(ceed, CeedQFunctionContextCreate(ceed, &build_ctx)); - PalaceCeedCall(ceed, - CeedQFunctionContextSetData(build_ctx, CEED_MEM_HOST, CEED_COPY_VALUES, - sizeof(info.ctx), (void *)&info.ctx)); - PalaceCeedCall(ceed, CeedQFunctionSetContext(build_qf, build_ctx)); - PalaceCeedCall(ceed, CeedQFunctionContextDestroy(&build_ctx)); - - // Inputs - for (std::size_t i = 0; i < Q.size(); i++) - { - std::string name = "coeff" + std::to_string(i + 1); - const CeedInt ncomp = Q[i].ncomp; - PalaceCeedCall(ceed, - CeedQFunctionAddInput(build_qf, name.c_str(), ncomp, CEED_EVAL_NONE)); - } - PalaceCeedCall(ceed, - CeedQFunctionAddInput(build_qf, "dx", dim * space_dim, CEED_EVAL_GRAD)); - PalaceCeedCall(ceed, CeedQFunctionAddInput(build_qf, "weights", 1, CEED_EVAL_WEIGHT)); - - // Output - PalaceCeedCall(ceed, - CeedQFunctionAddOutput(build_qf, "qdata", qdata_size, CEED_EVAL_NONE)); - - // Create the operator that builds the quadrature data for the actual operator. - CeedOperator build_op; - PalaceCeedCall(ceed, CeedOperatorCreate(ceed, build_qf, nullptr, nullptr, &build_op)); - PalaceCeedCall(ceed, CeedQFunctionDestroy(&build_qf)); - - for (std::size_t i = 0; i < Q.size(); i++) - { - std::string name = "coeff" + std::to_string(i + 1); - const CeedInt ncomp = Q[i].ncomp; - CeedInt strides[3] = {ncomp, 1, ncomp * nqpts}; - CeedElemRestriction coeff_restr; - CeedVector coeff_vector; - - PalaceCeedCall(ceed, CeedElemRestrictionCreateStrided(ceed, ne, nqpts, ncomp, - ne * nqpts * ncomp, strides, - &coeff_restr)); - InitCeedVector(Q[i].data, ceed, &coeff_vector); - - PalaceCeedCall(ceed, CeedOperatorSetField(build_op, name.c_str(), coeff_restr, - CEED_BASIS_NONE, coeff_vector)); - - PalaceCeedCall(ceed, CeedElemRestrictionDestroy(&coeff_restr)); - PalaceCeedCall(ceed, CeedVectorDestroy(&coeff_vector)); - } - PalaceCeedCall(ceed, CeedOperatorSetField(build_op, "dx", mesh_restr, mesh_basis, - CEED_VECTOR_ACTIVE)); - PalaceCeedCall(ceed, CeedOperatorSetField(build_op, "weights", CEED_ELEMRESTRICTION_NONE, - mesh_basis, CEED_VECTOR_NONE)); - PalaceCeedCall(ceed, CeedOperatorSetField(build_op, "qdata", *qdata_restr, - CEED_BASIS_NONE, CEED_VECTOR_ACTIVE)); - - PalaceCeedCall(ceed, CeedOperatorCheckReady(build_op)); - - // Compute the quadrature data for the operator. - CeedVector nodes; - InitCeedVector(mesh_nodes, ceed, &nodes); - - PalaceCeedCall(ceed, CeedOperatorApply(build_op, nodes, *qdata, CEED_REQUEST_IMMEDIATE)); + // Control whether or not to pre-assemble the quadrature data or compute it during + // operator application in true matrix-free fashion. + bool assemble_q_data; - PalaceCeedCall(ceed, CeedVectorDestroy(&nodes)); - PalaceCeedCall(ceed, CeedOperatorDestroy(&build_op)); -} - -// Create libCEED operator using the given quadrature data and element restriction. -template -inline void AssembleCeedOperator(const CeedIntegratorInfo &info, - const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, - const std::vector &indices, const bool use_bdr, - CeedVector qdata, CeedElemRestriction qdata_restr, - Ceed ceed, CeedOperator *op) -{ - MFEM_VERIFY(trial_fespace.GetParMesh() == test_fespace.GetParMesh(), - "Trial and test finite element spaces must correspond to the same mesh!"); - const mfem::ParMesh &mesh = *trial_fespace.GetParMesh(); - - CeedInt dim = mesh.Dimension() - use_bdr; - CeedInt curl_dim = (dim < 3) ? 1 : dim; - CeedInt trial_vdim = trial_fespace.GetVDim(); - CeedInt test_vdim = test_fespace.GetVDim(); - bool trial_vectorfe = - (trial_fespace.FEColl()->GetRangeType(dim) == mfem::FiniteElement::VECTOR); - bool test_vectorfe = - (test_fespace.FEColl()->GetRangeType(dim) == mfem::FiniteElement::VECTOR); - - CeedElemRestriction trial_restr, test_restr; - CeedBasis trial_basis, test_basis; - InitRestriction(trial_fespace, indices, use_bdr, ceed, &trial_restr); - InitRestriction(test_fespace, indices, use_bdr, ceed, &test_restr); - InitBasis(trial_fespace, ir, indices, use_bdr, ceed, &trial_basis); - InitBasis(test_fespace, ir, indices, use_bdr, ceed, &test_basis); - - CeedInt trial_nqpts, test_nqpts, mesh_nqpts, qdata_size; - PalaceCeedCall(ceed, CeedBasisGetNumQuadraturePoints(trial_basis, &trial_nqpts)); - PalaceCeedCall(ceed, CeedBasisGetNumQuadraturePoints(test_basis, &test_nqpts)); - PalaceCeedCall(ceed, CeedElemRestrictionGetElementSize(qdata_restr, &mesh_nqpts)); - PalaceCeedCall(ceed, CeedElemRestrictionGetNumComponents(qdata_restr, &qdata_size)); - MFEM_VERIFY(trial_nqpts == test_nqpts && trial_nqpts == mesh_nqpts, - "Trial and test basis must have the same number of quadrature points!"); - - // Create the QFunction that defines the action of the operator. - CeedQFunction apply_qf; - PalaceCeedCall(ceed, CeedQFunctionCreateInterior(ceed, 1, info.apply_qf, - info.apply_qf_path.c_str(), &apply_qf)); - - CeedQFunctionContext apply_ctx; - PalaceCeedCall(ceed, CeedQFunctionContextCreate(ceed, &apply_ctx)); - PalaceCeedCall(ceed, - CeedQFunctionContextSetData(apply_ctx, CEED_MEM_HOST, CEED_COPY_VALUES, - sizeof(info.ctx), (void *)&info.ctx)); - PalaceCeedCall(ceed, CeedQFunctionSetContext(apply_qf, apply_ctx)); - PalaceCeedCall(ceed, CeedQFunctionContextDestroy(&apply_ctx)); - - // Inputs - switch (info.trial_op) - { - case EvalMode::None: - PalaceCeedCall(ceed, - CeedQFunctionAddInput(apply_qf, "u", trial_vdim, CEED_EVAL_NONE)); - break; - case EvalMode::Interp: - PalaceCeedCall(ceed, CeedQFunctionAddInput(apply_qf, "u", - trial_vdim * (trial_vectorfe ? dim : 1), - CEED_EVAL_INTERP)); - break; - case EvalMode::Grad: - MFEM_VERIFY(!trial_vectorfe, "EvalMode::Grad is not intended for vector FE!"); - PalaceCeedCall( - ceed, CeedQFunctionAddInput(apply_qf, "gu", trial_vdim * dim, CEED_EVAL_GRAD)); - break; - case EvalMode::Div: - PalaceCeedCall(ceed, - CeedQFunctionAddInput(apply_qf, "du", trial_vdim, CEED_EVAL_DIV)); - break; - case EvalMode::Curl: - PalaceCeedCall(ceed, CeedQFunctionAddInput(apply_qf, "cu", trial_vdim * curl_dim, - CEED_EVAL_CURL)); - break; - case EvalMode::InterpAndGrad: - MFEM_VERIFY(!trial_vectorfe, - "EvalMode::InterpAndGrad is not intended for vector FE!"); - PalaceCeedCall(ceed, - CeedQFunctionAddInput(apply_qf, "u", trial_vdim, CEED_EVAL_INTERP)); - PalaceCeedCall( - ceed, CeedQFunctionAddInput(apply_qf, "gu", trial_vdim * dim, CEED_EVAL_GRAD)); - break; - case EvalMode::InterpAndDiv: - MFEM_VERIFY(trial_vectorfe, "EvalMode::InterpAndDiv is only intended for vector FE!"); - PalaceCeedCall( - ceed, CeedQFunctionAddInput(apply_qf, "u", trial_vdim * dim, CEED_EVAL_INTERP)); - PalaceCeedCall(ceed, - CeedQFunctionAddInput(apply_qf, "du", trial_vdim, CEED_EVAL_DIV)); - break; - case EvalMode::InterpAndCurl: - MFEM_VERIFY(trial_vectorfe, - "EvalMode::InterpAndCurl is only intended for vector FE!"); - PalaceCeedCall( - ceed, CeedQFunctionAddInput(apply_qf, "u", trial_vdim * dim, CEED_EVAL_INTERP)); - PalaceCeedCall(ceed, CeedQFunctionAddInput(apply_qf, "cu", trial_vdim * curl_dim, - CEED_EVAL_CURL)); - break; - } - PalaceCeedCall(ceed, - CeedQFunctionAddInput(apply_qf, "qdata", qdata_size, CEED_EVAL_NONE)); - - // Output - switch (info.test_op) + IntegratorInfo() + : apply_qf(nullptr), apply_qf_path(""), trial_ops(0), test_ops(0), + assemble_q_data(false) { - case EvalMode::None: - PalaceCeedCall(ceed, - CeedQFunctionAddOutput(apply_qf, "v", test_vdim, CEED_EVAL_NONE)); - break; - case EvalMode::Interp: - PalaceCeedCall(ceed, CeedQFunctionAddOutput(apply_qf, "v", - test_vdim * (test_vectorfe ? dim : 1), - CEED_EVAL_INTERP)); - break; - case EvalMode::Grad: - MFEM_VERIFY(!test_vectorfe, "EvalMode::Grad is not intended for vector FE!"); - PalaceCeedCall( - ceed, CeedQFunctionAddOutput(apply_qf, "gv", test_vdim * dim, CEED_EVAL_GRAD)); - break; - case EvalMode::Div: - PalaceCeedCall(ceed, - CeedQFunctionAddOutput(apply_qf, "dv", test_vdim, CEED_EVAL_DIV)); - break; - case EvalMode::Curl: - PalaceCeedCall(ceed, CeedQFunctionAddOutput(apply_qf, "cv", test_vdim * curl_dim, - CEED_EVAL_CURL)); - break; - case EvalMode::InterpAndGrad: - MFEM_VERIFY(!test_vectorfe, "EvalMode::InterpAndGrad is not intended for vector FE!"); - PalaceCeedCall(ceed, - CeedQFunctionAddOutput(apply_qf, "v", test_vdim, CEED_EVAL_INTERP)); - PalaceCeedCall( - ceed, CeedQFunctionAddOutput(apply_qf, "gv", test_vdim * dim, CEED_EVAL_GRAD)); - break; - case EvalMode::InterpAndDiv: - MFEM_VERIFY(test_vectorfe, "EvalMode::InterpAndDiv is only intended for vector FE!"); - PalaceCeedCall( - ceed, CeedQFunctionAddOutput(apply_qf, "v", test_vdim * dim, CEED_EVAL_INTERP)); - PalaceCeedCall(ceed, - CeedQFunctionAddOutput(apply_qf, "dv", test_vdim, CEED_EVAL_DIV)); - break; - case EvalMode::InterpAndCurl: - MFEM_VERIFY(test_vectorfe, "EvalMode::InterpAndCurl is only intended for vector FE!"); - PalaceCeedCall( - ceed, CeedQFunctionAddOutput(apply_qf, "v", test_vdim * dim, CEED_EVAL_INTERP)); - PalaceCeedCall(ceed, CeedQFunctionAddOutput(apply_qf, "cv", test_vdim * curl_dim, - CEED_EVAL_CURL)); - break; } +}; - // Create the operator. - PalaceCeedCall(ceed, CeedOperatorCreate(ceed, apply_qf, nullptr, nullptr, op)); - PalaceCeedCall(ceed, CeedQFunctionDestroy(&apply_qf)); +// Helper function to get the geometry space dimension. +int CeedGeometryDataGetSpaceDimension(CeedElemRestriction geom_data_restr, CeedInt dim, + CeedInt *space_dim); - switch (info.trial_op) - { - case EvalMode::None: - PalaceCeedCall(ceed, CeedOperatorSetField(*op, "u", trial_restr, CEED_BASIS_NONE, - CEED_VECTOR_ACTIVE)); - break; - case EvalMode::Interp: - PalaceCeedCall(ceed, CeedOperatorSetField(*op, "u", trial_restr, trial_basis, - CEED_VECTOR_ACTIVE)); - break; - case EvalMode::Grad: - PalaceCeedCall(ceed, CeedOperatorSetField(*op, "gu", trial_restr, trial_basis, - CEED_VECTOR_ACTIVE)); - break; - case EvalMode::Div: - PalaceCeedCall(ceed, CeedOperatorSetField(*op, "du", trial_restr, trial_basis, - CEED_VECTOR_ACTIVE)); - break; - case EvalMode::Curl: - PalaceCeedCall(ceed, CeedOperatorSetField(*op, "cu", trial_restr, trial_basis, - CEED_VECTOR_ACTIVE)); - break; - case EvalMode::InterpAndGrad: - PalaceCeedCall(ceed, CeedOperatorSetField(*op, "u", trial_restr, trial_basis, - CEED_VECTOR_ACTIVE)); - PalaceCeedCall(ceed, CeedOperatorSetField(*op, "gu", trial_restr, trial_basis, - CEED_VECTOR_ACTIVE)); - break; - case EvalMode::InterpAndDiv: - PalaceCeedCall(ceed, CeedOperatorSetField(*op, "u", trial_restr, trial_basis, - CEED_VECTOR_ACTIVE)); - PalaceCeedCall(ceed, CeedOperatorSetField(*op, "du", trial_restr, trial_basis, - CEED_VECTOR_ACTIVE)); - break; - case EvalMode::InterpAndCurl: - PalaceCeedCall(ceed, CeedOperatorSetField(*op, "u", trial_restr, trial_basis, - CEED_VECTOR_ACTIVE)); - PalaceCeedCall(ceed, CeedOperatorSetField(*op, "cu", trial_restr, trial_basis, - CEED_VECTOR_ACTIVE)); - break; - } - PalaceCeedCall(ceed, - CeedOperatorSetField(*op, "qdata", qdata_restr, CEED_BASIS_NONE, qdata)); - switch (info.test_op) - { - case EvalMode::None: - PalaceCeedCall(ceed, CeedOperatorSetField(*op, "v", test_restr, CEED_BASIS_NONE, - CEED_VECTOR_ACTIVE)); - break; - case EvalMode::Interp: - PalaceCeedCall( - ceed, CeedOperatorSetField(*op, "v", test_restr, test_basis, CEED_VECTOR_ACTIVE)); - break; - case EvalMode::Grad: - PalaceCeedCall(ceed, CeedOperatorSetField(*op, "gv", test_restr, test_basis, - CEED_VECTOR_ACTIVE)); - break; - case EvalMode::Div: - PalaceCeedCall(ceed, CeedOperatorSetField(*op, "dv", test_restr, test_basis, - CEED_VECTOR_ACTIVE)); - break; - case EvalMode::Curl: - PalaceCeedCall(ceed, CeedOperatorSetField(*op, "cv", test_restr, test_basis, - CEED_VECTOR_ACTIVE)); - break; - case EvalMode::InterpAndGrad: - PalaceCeedCall( - ceed, CeedOperatorSetField(*op, "v", test_restr, test_basis, CEED_VECTOR_ACTIVE)); - PalaceCeedCall(ceed, CeedOperatorSetField(*op, "gv", test_restr, test_basis, - CEED_VECTOR_ACTIVE)); - break; - case EvalMode::InterpAndDiv: - PalaceCeedCall( - ceed, CeedOperatorSetField(*op, "v", test_restr, test_basis, CEED_VECTOR_ACTIVE)); - PalaceCeedCall(ceed, CeedOperatorSetField(*op, "dv", test_restr, test_basis, - CEED_VECTOR_ACTIVE)); - break; - case EvalMode::InterpAndCurl: - PalaceCeedCall( - ceed, CeedOperatorSetField(*op, "v", test_restr, test_basis, CEED_VECTOR_ACTIVE)); - PalaceCeedCall(ceed, CeedOperatorSetField(*op, "cv", test_restr, test_basis, - CEED_VECTOR_ACTIVE)); - break; - } - - PalaceCeedCall(ceed, CeedOperatorCheckReady(*op)); -} +// Assemble libCEED mesh geometry factor quadrature data for use in a partially assembled +// libCEED operator. +void AssembleCeedGeometryData(Ceed ceed, CeedElemRestriction mesh_restr, + CeedBasis mesh_basis, CeedVector mesh_nodes, + CeedVector geom_data, CeedElemRestriction geom_data_restr); + +// Construct libCEED operator using the given quadrature data, element restriction, and +// basis objects. +void AssembleCeedOperator(const IntegratorInfo &info, void *ctx, std::size_t ctx_size, + Ceed ceed, CeedElemRestriction trial_restr, + CeedElemRestriction test_restr, CeedBasis trial_basis, + CeedBasis test_basis, CeedVector geom_data, + CeedElemRestriction geom_data_restr, CeedOperator *op); // Construct libCEED operators for interpolation operations and their transpose between -// the two spaces. The operation for interpolation is decided by the conformity of the trial -// and test spaces. -inline void AssembleCeedInterpolator(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const std::vector &indices, Ceed ceed, - CeedOperator *op, CeedOperator *op_t) -{ - CeedInt trial_vdim = trial_fespace.GetVDim(); - CeedInt test_vdim = test_fespace.GetVDim(); - MFEM_VERIFY(trial_vdim == 1 && test_vdim == 1, - "AssembleCeedInterpolator does not support spaces with vdim > 1!"); - - CeedElemRestriction trial_restr, test_restr; - CeedBasis basis_ctof; - InitRestriction(trial_fespace, indices, false, true, false, ceed, &trial_restr); - InitRestriction(test_fespace, indices, false, true, true, ceed, &test_restr); - InitInterpolatorBasis(trial_fespace, test_fespace, indices, ceed, &basis_ctof); - - // Create the QFunction that defines the action of the operator (only an identity as - // element dof multiplicity is handled outside of libCEED). - CeedQFunction apply_qf, apply_qf_t; - PalaceCeedCall(ceed, CeedQFunctionCreateIdentity(ceed, trial_vdim, CEED_EVAL_INTERP, - CEED_EVAL_NONE, &apply_qf)); - PalaceCeedCall(ceed, CeedQFunctionCreateIdentity(ceed, trial_vdim, CEED_EVAL_NONE, - CEED_EVAL_INTERP, &apply_qf_t)); - - // Create the operator. - PalaceCeedCall(ceed, CeedOperatorCreate(ceed, apply_qf, nullptr, nullptr, op)); - PalaceCeedCall(ceed, CeedQFunctionDestroy(&apply_qf)); - - PalaceCeedCall(ceed, CeedOperatorSetField(*op, "input", trial_restr, basis_ctof, - CEED_VECTOR_ACTIVE)); - PalaceCeedCall(ceed, CeedOperatorSetField(*op, "output", test_restr, CEED_BASIS_NONE, - CEED_VECTOR_ACTIVE)); - - PalaceCeedCall(ceed, CeedOperatorCheckReady(*op)); - - // Create the transpose operator. - PalaceCeedCall(ceed, CeedOperatorCreate(ceed, apply_qf_t, nullptr, nullptr, op_t)); - PalaceCeedCall(ceed, CeedQFunctionDestroy(&apply_qf_t)); - - PalaceCeedCall(ceed, CeedOperatorSetField(*op_t, "input", test_restr, CEED_BASIS_NONE, - CEED_VECTOR_ACTIVE)); - PalaceCeedCall(ceed, CeedOperatorSetField(*op_t, "output", trial_restr, basis_ctof, - CEED_VECTOR_ACTIVE)); - - PalaceCeedCall(ceed, CeedOperatorCheckReady(*op_t)); -} +// the two spaces. Note that contributions for shared degrees of freedom are added, so the +// output of the operator application must be scaled by the inverse multiplicity. +void AssembleCeedInterpolator(Ceed ceed, CeedElemRestriction trial_restr, + CeedElemRestriction test_restr, CeedBasis interp_basis, + CeedOperator *op, CeedOperator *op_t); } // namespace palace::ceed diff --git a/palace/fem/libceed/operator.cpp b/palace/fem/libceed/operator.cpp index 6646f9df3..63f0728a5 100644 --- a/palace/fem/libceed/operator.cpp +++ b/palace/fem/libceed/operator.cpp @@ -4,9 +4,9 @@ #include "operator.hpp" #include -#include +#include #include -#include "fem/libceed/utils.hpp" +#include "fem/fespace.hpp" #include "utils/omp.hpp" namespace palace::ceed @@ -481,4 +481,77 @@ std::unique_ptr CeedOperatorFullAssemble(const Operator &op, return mat; } +std::unique_ptr CeedOperatorCoarsen(const Operator &op_fine, + const FiniteElementSpace &fespace_coarse) +{ + auto SingleOperatorCoarsen = + [&fespace_coarse](Ceed ceed, CeedOperator op_fine, CeedOperator *op_coarse) + { + CeedBasis basis_fine; + CeedElemTopology geom; + PalaceCeedCall(ceed, CeedOperatorGetActiveBasis(op_fine, &basis_fine)); + PalaceCeedCall(ceed, CeedBasisGetTopology(basis_fine, &geom)); + + const auto &geom_data = + fespace_coarse.GetMesh().GetCeedGeomFactorData(ceed).at(GetMfemTopology(geom)); + CeedElemRestriction restr_coarse = fespace_coarse.GetCeedElemRestriction( + ceed, GetMfemTopology(geom), geom_data->indices); + CeedBasis basis_coarse = fespace_coarse.GetCeedBasis(ceed, GetMfemTopology(geom)); + + PalaceCeedCall(ceed, CeedOperatorMultigridLevelCreate(op_fine, nullptr, restr_coarse, + basis_coarse, op_coarse, nullptr, + nullptr)); + }; + + // Initialize the coarse operator. + auto op_coarse = std::make_unique(fespace_coarse.GetVSize(), + fespace_coarse.GetVSize()); + + // Assemble the coarse operator by coarsening each sub-operator (over threads, geometry + // types, integrators) of the original fine operator. We loop over Ceed contexts because + // extracting the Ceed context from a CeedOperator returns a different pointer (created + // with CeedReferenceCopy) and we need the original ones to access the FiniteElementSpace + // and Mesh object caches. + MFEM_VERIFY(internal::GetCeedObjects().size() == op_fine.Size(), + "Unexpected size mismatch in multithreaded libCEED contexts!"); + const std::size_t nt = internal::GetCeedObjects().size(); + PalacePragmaOmp(parallel for schedule(static)) + for (std::size_t i = 0; i < nt; i++) + { + Ceed ceed = internal::GetCeedObjects()[i]; + + // Initialize the composite operator on each thread. + CeedOperator loc_op; + PalaceCeedCall(ceed, CeedCompositeOperatorCreate(ceed, &loc_op)); + + bool composite; + PalaceCeedCall(ceed, CeedOperatorIsComposite(op_fine[i], &composite)); + if (composite) + { + CeedInt nloc_ops_fine; + CeedOperator *loc_ops_fine; + PalaceCeedCall(ceed, CeedCompositeOperatorGetNumSub(op_fine[i], &nloc_ops_fine)); + PalaceCeedCall(ceed, CeedCompositeOperatorGetSubList(op_fine[i], &loc_ops_fine)); + for (CeedInt k = 0; k < nloc_ops_fine; k++) + { + CeedOperator sub_op; + SingleOperatorCoarsen(ceed, loc_ops_fine[k], &sub_op); + PalaceCeedCall(ceed, CeedCompositeOperatorAddSub(loc_op, sub_op)); + PalaceCeedCall(ceed, CeedOperatorDestroy(&sub_op)); + } + } + else + { + CeedOperator sub_op; + SingleOperatorCoarsen(ceed, op_fine[i], &sub_op); + PalaceCeedCall(ceed, CeedCompositeOperatorAddSub(loc_op, sub_op)); + PalaceCeedCall(ceed, CeedOperatorDestroy(&sub_op)); + } + PalaceCeedCall(ceed, CeedOperatorCheckReady(loc_op)); + op_coarse->AddOper(loc_op); // Thread-safe + } + + return op_coarse; +} + } // namespace palace::ceed diff --git a/palace/fem/libceed/operator.hpp b/palace/fem/libceed/operator.hpp index d352eaea0..13cc0d1e4 100644 --- a/palace/fem/libceed/operator.hpp +++ b/palace/fem/libceed/operator.hpp @@ -7,16 +7,14 @@ #include #include #include - -// Forward declarations of libCEED objects. -typedef struct CeedOperator_private *CeedOperator; -typedef struct CeedVector_private *CeedVector; +#include "fem/libceed/ceed.hpp" +#include "linalg/operator.hpp" +#include "linalg/vector.hpp" namespace palace { -using Operator = mfem::Operator; -using Vector = mfem::Vector; +class FiniteElementSpace; namespace ceed { @@ -70,6 +68,12 @@ class SymmetricOperator : public Operator std::unique_ptr CeedOperatorFullAssemble(const Operator &op, bool skip_zeros, bool set); +// Construct a coarse-level ceed::Operator, reusing the quadrature data and quadrature +// function from the fine-level operator. Only available for square operators (same input +// and output spaces). +std::unique_ptr CeedOperatorCoarsen(const Operator &op_fine, + const FiniteElementSpace &fespace_coarse); + } // namespace ceed } // namespace palace diff --git a/palace/fem/libceed/restriction.cpp b/palace/fem/libceed/restriction.cpp index 8703feef5..9d431fafe 100644 --- a/palace/fem/libceed/restriction.cpp +++ b/palace/fem/libceed/restriction.cpp @@ -3,64 +3,43 @@ #include "restriction.hpp" -#include "fem/fespace.hpp" -#include "fem/libceed/hash.hpp" -#include "fem/libceed/utils.hpp" -#include "utils/omp.hpp" +#include namespace palace::ceed { -namespace internal -{ - -static std::unordered_map restr_map; - -void ClearRestrictionCache() -{ - for (auto [k, v] : restr_map) - { - Ceed ceed; - PalaceCeedCallBackend(CeedElemRestrictionGetCeed(v, &ceed)); - PalaceCeedCall(ceed, CeedElemRestrictionDestroy(&v)); - } - restr_map.clear(); -} - -} // namespace internal - namespace { -void InitLexicoRestr(const mfem::ParFiniteElementSpace &fespace, +void InitLexicoRestr(const mfem::FiniteElementSpace &fespace, const std::vector &indices, bool use_bdr, Ceed ceed, CeedElemRestriction *restr) { - const std::size_t ne = indices.size(); + const std::size_t num_elem = indices.size(); const mfem::FiniteElement &fe = use_bdr ? *fespace.GetBE(indices[0]) : *fespace.GetFE(indices[0]); const int P = fe.GetDof(); const mfem::TensorBasisElement *tfe = dynamic_cast(&fe); const mfem::Array &dof_map = tfe->GetDofMap(); - CeedInt compstride = + CeedInt comp_stride = (fespace.GetOrdering() == mfem::Ordering::byVDIM) ? 1 : fespace.GetNDofs(); - const int stride = (compstride == 1) ? fespace.GetVDim() : 1; - mfem::Array tp_el_dof(ne * P), dofs; - mfem::Array tp_el_orients(ne * P); + const int stride = (comp_stride == 1) ? fespace.GetVDim() : 1; + mfem::Array tp_el_dof(num_elem * P), dofs; + mfem::Array tp_el_orients(num_elem * P); bool use_el_orients = false; mfem::DofTransformation dof_trans; - for (std::size_t i = 0; i < ne; i++) + for (std::size_t i = 0; i < num_elem; i++) { // No need to handle DofTransformation for tensor-product elements. - const int elem_index = indices[i]; + const int e = indices[i]; if (use_bdr) { - fespace.GetBdrElementDofs(elem_index, dofs, dof_trans); + fespace.GetBdrElementDofs(e, dofs, dof_trans); } else { - fespace.GetElementDofs(elem_index, dofs, dof_trans); + fespace.GetElementDofs(e, dofs, dof_trans); } MFEM_VERIFY(!dof_trans.GetDofTransformation(), "Unexpected DofTransformation for lexicographic element " @@ -80,7 +59,7 @@ void InitLexicoRestr(const mfem::ParFiniteElementSpace &fespace, if (use_el_orients) { PalaceCeedCall(ceed, CeedElemRestrictionCreateOriented( - ceed, ne, P, fespace.GetVDim(), compstride, + ceed, num_elem, P, fespace.GetVDim(), comp_stride, fespace.GetVDim() * fespace.GetNDofs(), CEED_MEM_HOST, CEED_COPY_VALUES, tp_el_dof.GetData(), tp_el_orients.GetData(), restr)); @@ -88,40 +67,49 @@ void InitLexicoRestr(const mfem::ParFiniteElementSpace &fespace, else { PalaceCeedCall(ceed, CeedElemRestrictionCreate( - ceed, ne, P, fespace.GetVDim(), compstride, + ceed, num_elem, P, fespace.GetVDim(), comp_stride, fespace.GetVDim() * fespace.GetNDofs(), CEED_MEM_HOST, CEED_COPY_VALUES, tp_el_dof.GetData(), restr)); } } -void InitNativeRestr(const mfem::ParFiniteElementSpace &fespace, - const std::vector &indices, bool use_bdr, bool has_dof_trans, - bool is_interp_range, Ceed ceed, CeedElemRestriction *restr) +void InitNativeRestr(const mfem::FiniteElementSpace &fespace, + const std::vector &indices, bool use_bdr, bool is_interp_range, + Ceed ceed, CeedElemRestriction *restr) { - const std::size_t ne = indices.size(); + const std::size_t num_elem = indices.size(); const mfem::FiniteElement &fe = use_bdr ? *fespace.GetBE(indices[0]) : *fespace.GetFE(indices[0]); const int P = fe.GetDof(); - CeedInt compstride = + CeedInt comp_strid = (fespace.GetOrdering() == mfem::Ordering::byVDIM) ? 1 : fespace.GetNDofs(); - const int stride = (compstride == 1) ? fespace.GetVDim() : 1; - mfem::Array tp_el_dof(ne * P), dofs; + const int stride = (comp_strid == 1) ? fespace.GetVDim() : 1; + mfem::Array tp_el_dof(num_elem * P), dofs; mfem::Array tp_el_orients; mfem::Array tp_el_curl_orients; bool use_el_orients = false; mfem::DofTransformation dof_trans; mfem::Vector el_trans_j; + if (use_bdr) + { + fespace.GetBdrElementDofs(indices[0], dofs, dof_trans); + } + else + { + fespace.GetElementDofs(indices[0], dofs, dof_trans); + } + const bool has_dof_trans = dof_trans.GetDofTransformation() && !dof_trans.IsIdentity(); if (!has_dof_trans) { - tp_el_orients.SetSize(ne * P); + tp_el_orients.SetSize(num_elem * P); } else { - tp_el_curl_orients.SetSize(ne * P * 3, 0); + tp_el_curl_orients.SetSize(num_elem * P * 3, 0); el_trans_j.SetSize(P); } - for (std::size_t i = 0; i < ne; i++) + for (std::size_t i = 0; i < num_elem; i++) { const auto e = indices[i]; if (use_bdr) @@ -194,7 +182,7 @@ void InitNativeRestr(const mfem::ParFiniteElementSpace &fespace, if (has_dof_trans) { PalaceCeedCall(ceed, CeedElemRestrictionCreateCurlOriented( - ceed, ne, P, fespace.GetVDim(), compstride, + ceed, num_elem, P, fespace.GetVDim(), comp_strid, fespace.GetVDim() * fespace.GetNDofs(), CEED_MEM_HOST, CEED_COPY_VALUES, tp_el_dof.GetData(), tp_el_curl_orients.GetData(), restr)); @@ -202,7 +190,7 @@ void InitNativeRestr(const mfem::ParFiniteElementSpace &fespace, else if (use_el_orients) { PalaceCeedCall(ceed, CeedElemRestrictionCreateOriented( - ceed, ne, P, fespace.GetVDim(), compstride, + ceed, num_elem, P, fespace.GetVDim(), comp_strid, fespace.GetVDim() * fespace.GetNDofs(), CEED_MEM_HOST, CEED_COPY_VALUES, tp_el_dof.GetData(), tp_el_orients.GetData(), restr)); @@ -210,7 +198,7 @@ void InitNativeRestr(const mfem::ParFiniteElementSpace &fespace, else { PalaceCeedCall(ceed, CeedElemRestrictionCreate( - ceed, ne, P, fespace.GetVDim(), compstride, + ceed, num_elem, P, fespace.GetVDim(), comp_strid, fespace.GetVDim() * fespace.GetNDofs(), CEED_MEM_HOST, CEED_COPY_VALUES, tp_el_dof.GetData(), restr)); } @@ -218,80 +206,30 @@ void InitNativeRestr(const mfem::ParFiniteElementSpace &fespace, } // namespace -void InitRestriction(const mfem::ParFiniteElementSpace &fespace, +void InitRestriction(const mfem::FiniteElementSpace &fespace, const std::vector &indices, bool use_bdr, bool is_interp, - bool is_range, Ceed ceed, CeedElemRestriction *restr) + bool is_interp_range, Ceed ceed, CeedElemRestriction *restr) { - // Check for fespace -> restriction in hash table. - // The restriction for an interpolator range space is slightly different as - // the output is a primal vector instead of a dual vector, and lexicographic - // ordering is never used (no use of tensor-product basis). - // A palace::FiniteElementSpace can be checked for uniqueness so we can use this to reuse - // restrictions across different libCEED operators. For mixed meshes or multiple threads, - // the space elements are partitioned in a non-overlapping manner so we just need the - // index of the first element, and if it is a domain or boundary element, to determine the - // partition. - const FiniteElementSpace *restr_fespace = - dynamic_cast(&fespace); - MFEM_VERIFY(restr_fespace, "ceed::InitRestriction requires a palace::FiniteElementSpace " - "object for space comparisons!"); + if constexpr (false) + { + std::cout << "New element restriction (" << ceed << ", " << &fespace << ", " + << indices[0] << ", " << use_bdr << ", " << is_interp << ", " + << is_interp_range << ")\n"; + } const mfem::FiniteElement &fe = use_bdr ? *fespace.GetBE(indices[0]) : *fespace.GetFE(indices[0]); const mfem::TensorBasisElement *tfe = dynamic_cast(&fe); const bool vector = fe.GetRangeType() == mfem::FiniteElement::VECTOR; - mfem::Array dofs; - mfem::DofTransformation dof_trans; - if (use_bdr) - { - fespace.GetBdrElementDofs(indices[0], dofs, dof_trans); - } - else - { - fespace.GetElementDofs(indices[0], dofs, dof_trans); - } - const bool has_dof_trans = dof_trans.GetDofTransformation() && !dof_trans.IsIdentity(); - const bool unique_interp_restr = - (is_interp && tfe && tfe->GetDofMap().Size() > 0 && !vector); - const bool unique_interp_range_restr = (is_interp && is_range && has_dof_trans); - internal::RestrKey key(ceed, *restr_fespace, indices[0], use_bdr, unique_interp_restr, - unique_interp_range_restr); - - // Initialize or retrieve key values (avoid simultaneous search and write). - auto restr_itr = internal::restr_map.end(); - PalacePragmaOmp(critical(InitRestriction)) - { - restr_itr = internal::restr_map.find(key); - } - if (restr_itr == internal::restr_map.end()) + const bool lexico = (tfe && tfe->GetDofMap().Size() > 0 && !vector && !is_interp); + if (lexico) { - const bool lexico = (tfe && tfe->GetDofMap().Size() > 0 && !vector && !is_interp); - if (lexico) - { - // Lexicographic ordering using dof_map. - InitLexicoRestr(fespace, indices, use_bdr, ceed, restr); - } - else - { - // Native ordering. - InitNativeRestr(fespace, indices, use_bdr, has_dof_trans, is_interp && is_range, ceed, - restr); - } - PalacePragmaOmp(critical(InitRestriction)) - { - internal::restr_map[key] = *restr; - } - // std::cout << "New element restriction (" << ceed << ", " << &fespace - // << ", " << indices[0] << ", " << use_bdr - // << ", " << unique_interp_restr - // << ", " << unique_interp_range_restr << ")\n"; + // Lexicographic ordering using dof_map. + InitLexicoRestr(fespace, indices, use_bdr, ceed, restr); } else { - *restr = restr_itr->second; - // std::cout << "Reusing element restriction (" << ceed << ", " << &fespace - // << ", " << indices[0] << ", " << use_bdr << ", " - // << ", " << unique_interp_restr - // << ", " << unique_interp_range_restr << ")\n"; + // Native ordering. + InitNativeRestr(fespace, indices, use_bdr, is_interp_range, ceed, restr); } } diff --git a/palace/fem/libceed/restriction.hpp b/palace/fem/libceed/restriction.hpp index 22218eef4..2b16b269d 100644 --- a/palace/fem/libceed/restriction.hpp +++ b/palace/fem/libceed/restriction.hpp @@ -4,32 +4,22 @@ #ifndef PALACE_LIBCEED_RESTRICTION_HPP #define PALACE_LIBCEED_RESTRICTION_HPP -#include #include -#include -#include +#include "fem/libceed/ceed.hpp" -namespace palace::ceed +namespace mfem { -void InitRestriction(const mfem::ParFiniteElementSpace &fespace, - const std::vector &indices, bool use_bdr, bool is_interp, - bool is_range, Ceed ceed, CeedElemRestriction *restr); +class FiniteElementSpace; -inline void InitRestriction(const mfem::ParFiniteElementSpace &fespace, - const std::vector &indices, bool use_bdr, Ceed ceed, - CeedElemRestriction *restr) -{ - InitRestriction(fespace, indices, use_bdr, false, false, ceed, restr); -} +} // namespace mfem -namespace internal +namespace palace::ceed { -// Destroy the cached CeedElemRestriction objects. -void ClearRestrictionCache(); - -} // namespace internal +void InitRestriction(const mfem::FiniteElementSpace &fespace, + const std::vector &indices, bool use_bdr, bool is_interp, + bool is_interp_range, Ceed ceed, CeedElemRestriction *restr); } // namespace palace::ceed diff --git a/palace/main.cpp b/palace/main.cpp index a05dbca03..86098b883 100644 --- a/palace/main.cpp +++ b/palace/main.cpp @@ -14,7 +14,7 @@ #include "drivers/magnetostaticsolver.hpp" #include "drivers/transientsolver.hpp" #include "fem/errorindicator.hpp" -#include "fem/libceed/utils.hpp" +#include "fem/libceed/ceed.hpp" #include "fem/mesh.hpp" #include "linalg/slepc.hpp" #include "utils/communication.hpp" From 30460678f779d04d484f50815d60fd8a9abd8dd6 Mon Sep 17 00:00:00 2001 From: Sebastian Grimberg Date: Tue, 19 Dec 2023 12:31:08 -0800 Subject: [PATCH 11/32] WIP: Precompute and store mesh geometry factor quadrature data for libCEED operators --- palace/fem/bilinearform.cpp | 368 ++++++++++++++++++++---------------- palace/fem/bilinearform.hpp | 100 +++++----- palace/fem/fespace.cpp | 171 +++++++++++++++-- palace/fem/fespace.hpp | 72 +++++-- palace/fem/mesh.cpp | 280 +++++++++++++++++++++++++-- palace/fem/mesh.hpp | 50 +++++ 6 files changed, 788 insertions(+), 253 deletions(-) diff --git a/palace/fem/bilinearform.cpp b/palace/fem/bilinearform.cpp index 06e1b04cc..eb9b594c6 100644 --- a/palace/fem/bilinearform.cpp +++ b/palace/fem/bilinearform.cpp @@ -3,110 +3,36 @@ #include "bilinearform.hpp" -#include -#include #include "fem/fespace.hpp" -#include "fem/libceed/hash.hpp" -#include "fem/libceed/utils.hpp" +#include "fem/libceed/basis.hpp" +#include "fem/libceed/ceed.hpp" +#include "fem/mesh.hpp" #include "utils/omp.hpp" namespace palace { -namespace -{ - -using ceed::internal::FiniteElementKey; -using ceed::internal::FiniteElementPairHash; -using ceed::internal::FiniteElementPairKey; - -// Count the number of elements of each type in the local mesh. -std::unordered_map, FiniteElementPairHash> -GetElementIndices(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, bool use_bdr, int start, - int stop) +void BilinearForm::AssembleQuadratureData() { - std::unordered_map counts, offsets; - std::unordered_map, FiniteElementPairHash> - element_indices; - - // Count the number of elements of each type and order. - for (int i = start; i < stop; i++) + for (auto &integ : domain_integs) { - const mfem::FiniteElement &trial_fe = - use_bdr ? *trial_fespace.GetBE(i) : *trial_fespace.GetFE(i); - const mfem::FiniteElement &test_fe = - use_bdr ? *test_fespace.GetBE(i) : *test_fespace.GetFE(i); - FiniteElementPairKey key = - std::make_pair(FiniteElementKey(trial_fe), FiniteElementKey(test_fe)); - auto value = counts.find(key); - if (value == counts.end()) - { - counts[key] = 1; - } - else - { - value->second++; - } + integ->AssembleQuadratureData(); } - - // Populate the indices arrays for each element type. - for (const auto &value : counts) + for (auto &integ : boundary_integs) { - offsets[value.first] = 0; - element_indices[value.first] = std::vector(value.second); + integ->AssembleQuadratureData(); } - for (int i = start; i < stop; i++) - { - const mfem::FiniteElement &trial_fe = - use_bdr ? *trial_fespace.GetBE(i) : *trial_fespace.GetFE(i); - const mfem::FiniteElement &test_fe = - use_bdr ? *test_fespace.GetBE(i) : *test_fespace.GetFE(i); - FiniteElementPairKey key = - std::make_pair(FiniteElementKey(trial_fe), FiniteElementKey(test_fe)); - int &offset = offsets[key]; - std::vector &indices = element_indices[key]; - indices[offset++] = i; - } - - return element_indices; } -} // namespace - -std::unique_ptr BilinearForm::PartialAssemble() const +std::unique_ptr +BilinearForm::PartialAssemble(const FiniteElementSpace &trial_fespace, + const FiniteElementSpace &test_fespace) const { - MFEM_VERIFY(trial_fespace.GetParMesh() == test_fespace.GetParMesh(), + MFEM_VERIFY(&trial_fespace.GetMesh() == &test_fespace.GetMesh(), "Trial and test finite element spaces must correspond to the same mesh!"); - mfem::ParMesh &mesh = *trial_fespace.GetParMesh(); - { - // In the following, we copy the mesh FE space for the nodes as a - // palace::FiniteElementSpace and replace it in the nodal grid function. Unfortunately - // mfem::ParFiniteElementSpace does not have a move constructor to make this more - // efficient, but it's only done once for the lifetime of the mesh. - mesh.EnsureNodes(); - mfem::GridFunction *mesh_nodes = mesh.GetNodes(); - mfem::FiniteElementSpace *mesh_fespace = mesh_nodes->FESpace(); - MFEM_VERIFY(dynamic_cast(mesh_fespace), - "Unexpected non-parallel FiniteElementSpace for mesh nodes!"); - if (!dynamic_cast(mesh_fespace)) - { - // Ensure the FiniteElementCollection associated with the original nodes is not - // deleted. - auto *new_mesh_fespace = - new FiniteElementSpace(*static_cast(mesh_fespace)); - mfem::FiniteElementCollection *mesh_fec = mesh_nodes->OwnFEC(); - MFEM_VERIFY(mesh_fec, "Replacing the FiniteElementSpace for mesh nodes is only " - "possible when it owns its fec/fes members!"); - mesh_nodes->MakeOwner(nullptr); - mesh.SetNodalFESpace(new_mesh_fespace); - mfem::GridFunction *new_mesh_nodes = mesh.GetNodes(); - new_mesh_nodes->MakeOwner(mesh_fec); - delete mesh_fespace; - mesh.ExchangeFaceNbrData(); // Deleted in SetNodalFESpace - } - } + const auto &mesh = trial_fespace.GetMesh(); + // Initialize the operator. std::unique_ptr op; if (&trial_fespace == &test_fespace) { @@ -120,117 +46,239 @@ std::unique_ptr BilinearForm::PartialAssemble() const } // Assemble the libCEED operator in parallel, each thread builds a composite operator. - // This should work fine if some threads create an empty operator (no elements or bounday + // This should work fine if some threads create an empty operator (no elements or boundary // elements). const std::size_t nt = ceed::internal::GetCeedObjects().size(); PalacePragmaOmp(parallel for schedule(static)) for (std::size_t i = 0; i < nt; i++) { Ceed ceed = ceed::internal::GetCeedObjects()[i]; - CeedOperator loc_op, loc_op_t; + + // Initialize the composite operator on each thread. + CeedOperator loc_op; PalaceCeedCall(ceed, CeedCompositeOperatorCreate(ceed, &loc_op)); - PalaceCeedCall(ceed, CeedCompositeOperatorCreate(ceed, &loc_op_t)); - // Domain integrators first. - if (!domain_integs.empty()) + for (const auto &[geom, geom_data] : mesh.GetCeedGeomFactorData(ceed)) { - const int ne = mesh.GetNE(); - const int stride = (ne + nt - 1) / nt; - const int start = i * stride; - const int stop = std::min(start + stride, ne); - const bool use_bdr = false; + const auto trial_map_type = + trial_fespace.GetFEColl().GetMapType(mfem::Geometry::Dimension[geom]); + const auto test_map_type = + test_fespace.GetFEColl().GetMapType(mfem::Geometry::Dimension[geom]); - const auto element_indices = - GetElementIndices(trial_fespace, test_fespace, use_bdr, start, stop); - - for (const auto &value : element_indices) + if (mfem::Geometry::Dimension[geom] == mesh.Dimension() && !domain_integs.empty()) { - const std::vector &indices = value.second; - const int q_order = fem::DefaultIntegrationOrder::Get(trial_fespace, test_fespace, - indices, use_bdr); - const mfem::IntegrationRule &ir = - mfem::IntRules.Get(mesh.GetElementGeometry(indices[0]), q_order); + // Assemble domain integrators on this element geometry type. + CeedElemRestriction trial_restr = + trial_fespace.GetCeedElemRestriction(ceed, geom, geom_data->indices); + CeedElemRestriction test_restr = + test_fespace.GetCeedElemRestriction(ceed, geom, geom_data->indices); + CeedBasis trial_basis = trial_fespace.GetCeedBasis(ceed, geom); + CeedBasis test_basis = test_fespace.GetCeedBasis(ceed, geom); for (const auto &integ : domain_integs) { - CeedOperator sub_op, sub_op_t; - integ->Assemble(trial_fespace, test_fespace, ir, indices, ceed, &sub_op, - &sub_op_t); - + CeedOperator sub_op; + integ->SetMapTypes(trial_map_type, test_map_type); + integ->Assemble(ceed, trial_restr, test_restr, trial_basis, test_basis, + geom_data->geom_data_vec, geom_data->geom_data_restr, &sub_op); PalaceCeedCall(ceed, CeedCompositeOperatorAddSub(loc_op, sub_op)); PalaceCeedCall(ceed, CeedOperatorDestroy(&sub_op)); - if (sub_op_t) - { - PalaceCeedCall(ceed, CeedCompositeOperatorAddSub(loc_op_t, sub_op_t)); - PalaceCeedCall(ceed, CeedOperatorDestroy(&sub_op_t)); - } } } - } - - // Boundary integrators next. - if (!boundary_integs.empty()) - { - const int nbe = mesh.GetNBE(); - const int stride = (nbe + nt - 1) / nt; - const int start = i * stride; - const int stop = std::min(start + stride, nbe); - const bool use_bdr = true; - - const auto element_indices = - GetElementIndices(trial_fespace, test_fespace, use_bdr, start, stop); - - for (const auto &value : element_indices) + else if (mfem::Geometry::Dimension[geom] == mesh.Dimension() - 1 && + !boundary_integs.empty()) { - const std::vector &indices = value.second; - const int q_order = fem::DefaultIntegrationOrder::Get(trial_fespace, test_fespace, - indices, use_bdr); - const mfem::IntegrationRule &ir = - mfem::IntRules.Get(mesh.GetBdrElementGeometry(indices[0]), q_order); + // Assemble boundary integrators on this element geometry type. + CeedElemRestriction trial_restr = + trial_fespace.GetCeedElemRestriction(ceed, geom, geom_data->indices); + CeedElemRestriction test_restr = + test_fespace.GetCeedElemRestriction(ceed, geom, geom_data->indices); + CeedBasis trial_basis = trial_fespace.GetCeedBasis(ceed, geom); + CeedBasis test_basis = test_fespace.GetCeedBasis(ceed, geom); for (const auto &integ : boundary_integs) { - CeedOperator sub_op, sub_op_t; - integ->AssembleBoundary(trial_fespace, test_fespace, ir, indices, ceed, &sub_op, - &sub_op_t); - + CeedOperator sub_op; + integ->SetMapTypes(trial_map_type, test_map_type); + integ->Assemble(ceed, trial_restr, test_restr, trial_basis, test_basis, + geom_data->geom_data_vec, geom_data->geom_data_restr, &sub_op); PalaceCeedCall(ceed, CeedCompositeOperatorAddSub(loc_op, sub_op)); PalaceCeedCall(ceed, CeedOperatorDestroy(&sub_op)); - if (sub_op_t) - { - PalaceCeedCall(ceed, CeedCompositeOperatorAddSub(loc_op_t, sub_op_t)); - PalaceCeedCall(ceed, CeedOperatorDestroy(&sub_op_t)); - } } } } - PalaceCeedCall(ceed, CeedOperatorCheckReady(loc_op)); - PalaceCeedCall(ceed, CeedOperatorCheckReady(loc_op_t)); - op->AddOper(loc_op, loc_op_t); // Thread-safe + op->AddOper(loc_op); // Thread-safe } return op; } std::unique_ptr BilinearForm::FullAssemble(const ceed::Operator &op, - bool skip_zeros) + bool skip_zeros, bool set) +{ + return ceed::CeedOperatorFullAssemble(op, skip_zeros, set); +} + +namespace +{ + +bool UseFullAssembly(const FiniteElementSpace &trial_fespace, + const FiniteElementSpace &test_fespace, int pa_order_threshold) { - return ceed::CeedOperatorFullAssemble(op, skip_zeros, false); + // Returns order such that the miniumum for all element types is 1. MFEM's + // RT_FECollection actually already returns order + 1 for GetOrder() for historical + // reasons. + const auto &trial_fec = trial_fespace.GetFEColl(); + const auto &test_fec = test_fespace.GetFEColl(); + int max_order = std::max( + dynamic_cast(&trial_fec) ? trial_fec.GetOrder() + 1 + : trial_fec.GetOrder(), + dynamic_cast(&test_fec) ? test_fec.GetOrder() + 1 + : test_fec.GetOrder()); + return (max_order < pa_order_threshold); +} + +bool UseFullAssembly(const FiniteElementSpace &fespace, int pa_order_threshold) +{ + return UseFullAssembly(fespace, fespace, pa_order_threshold); +} + +} // namespace + +std::unique_ptr BilinearForm::Assemble(bool skip_zeros) const +{ + if (UseFullAssembly(trial_fespace, test_fespace, pa_order_threshold)) + { + return FullAssemble(skip_zeros); + } + else + { + return PartialAssemble(); + } +} + +template +std::vector> +BilinearForm::Assemble(const BaseFiniteElementSpaceHierarchy &fespaces, bool skip_zeros, + std::size_t l0) const +{ + // Only available for square operators (same teset and trial spaces). + MFEM_VERIFY(&trial_fespace == &test_fespace && + &fespaces.GetFinestFESpace() == &trial_fespace, + "Assembly on a FiniteElementSpaceHierarchy should have the same BilinearForm " + "spaces and fine space of the hierarchy!"); + + // First partially assemble all of the operators. + std::vector> pa_ops; + pa_ops.reserve(fespaces.GetNumLevels() - l0); + for (std::size_t l = l0; l < fespaces.GetNumLevels(); l++) + { + if (l > l0 && &fespaces.GetFESpaceAtLevel(l).GetMesh() == + &fespaces.GetFESpaceAtLevel(l - 1).GetMesh()) + { + pa_ops.push_back( + ceed::CeedOperatorCoarsen(*pa_ops.back(), fespaces.GetFESpaceAtLevel(l))); + } + else + { + pa_ops.push_back( + PartialAssemble(fespaces.GetFESpaceAtLevel(l), fespaces.GetFESpaceAtLevel(l))); + } + } + + // Construct the final operators using full or partial assemble as needed. Force the + // coarse-level operator to be fully assembled always. + std::vector> ops; + ops.reserve(fespaces.GetNumLevels() - 1); + for (std::size_t l = l0; l < fespaces.GetNumLevels(); l++) + { + if (l == 0 || UseFullAssembly(fespaces.GetFESpaceAtLevel(l), pa_order_threshold)) + { + ops.push_back(FullAssemble(*pa_ops[l - l0], skip_zeros)); + } + else + { + ops.push_back(std::move(pa_ops[l - l0])); + } + } + + return ops; } std::unique_ptr DiscreteLinearOperator::PartialAssemble() const { + MFEM_VERIFY(&trial_fespace.GetMesh() == &test_fespace.GetMesh(), + "Trial and test finite element spaces must correspond to the same mesh!"); + const auto &mesh = trial_fespace.GetMesh(); + + // Initialize the operator. + auto op = + std::make_unique(test_fespace.GetVSize(), trial_fespace.GetVSize()); + + // Assemble the libCEED operator in parallel, each thread builds a composite operator. + // This should work fine if some threads create an empty operator (no elements or bounday + // elements). + const std::size_t nt = ceed::internal::GetCeedObjects().size(); + PalacePragmaOmp(parallel for schedule(static)) + for (std::size_t i = 0; i < nt; i++) + { + Ceed ceed = ceed::internal::GetCeedObjects()[i]; + + // Initialize the composite operators for each thread. + CeedOperator loc_op, loc_op_t; + PalaceCeedCall(ceed, CeedCompositeOperatorCreate(ceed, &loc_op)); + PalaceCeedCall(ceed, CeedCompositeOperatorCreate(ceed, &loc_op_t)); + + for (const auto &[geom, geom_data] : mesh.GetCeedGeomFactorData(ceed)) + { + if (mfem::Geometry::Dimension[geom] == mesh.Dimension() && !domain_interps.empty()) + { + // Assemble domain interpolators on this element geometry type. + CeedElemRestriction trial_restr = + trial_fespace.GetInterpCeedElemRestriction(ceed, geom, geom_data->indices); + CeedElemRestriction test_restr = + test_fespace.GetInterpRangeCeedElemRestriction(ceed, geom, geom_data->indices); + + // Construct the interpolator basis. + CeedBasis interp_basis; + const mfem::FiniteElement &trial_fe = + *trial_fespace.GetFEColl().FiniteElementForGeometry(geom); + const mfem::FiniteElement &test_fe = + *test_fespace.GetFEColl().FiniteElementForGeometry(geom); + const int trial_vdim = trial_fespace.GetVDim(); + const int test_vdim = test_fespace.GetVDim(); + ceed::InitInterpolatorBasis(trial_fe, test_fe, trial_vdim, test_vdim, ceed, + &interp_basis); + + for (const auto &interp : domain_interps) + { + CeedOperator sub_op, sub_op_t; + interp->Assemble(ceed, trial_restr, test_restr, interp_basis, &sub_op, &sub_op_t); + PalaceCeedCall(ceed, CeedCompositeOperatorAddSub(loc_op, sub_op)); + PalaceCeedCall(ceed, CeedCompositeOperatorAddSub(loc_op_t, sub_op_t)); + PalaceCeedCall(ceed, CeedOperatorDestroy(&sub_op)); + PalaceCeedCall(ceed, CeedOperatorDestroy(&sub_op_t)); + } + + // Basis is owned by the operator. + PalaceCeedCall(ceed, CeedBasisDestroy(&interp_basis)); + } + } + PalaceCeedCall(ceed, CeedOperatorCheckReady(loc_op)); + PalaceCeedCall(ceed, CeedOperatorCheckReady(loc_op_t)); + op->AddOper(loc_op, loc_op_t); // Thread-safe + } + // Construct dof multiplicity vector for scaling to account for dofs shared between // elements (on host, then copy to device). - const auto &test_fespace = a.GetTestSpace(); Vector test_multiplicity(test_fespace.GetVSize()); test_multiplicity = 0.0; mfem::Array dofs; auto *h_mult = test_multiplicity.HostReadWrite(); - for (int i = 0; i < test_fespace.GetNE(); i++) + for (int i = 0; i < test_fespace.GetMesh().GetNE(); i++) { - test_fespace.GetElementVDofs(i, dofs); + test_fespace.Get().GetElementVDofs(i, dofs); for (int j = 0; j < dofs.Size(); j++) { const int k = dofs[j]; @@ -239,16 +287,16 @@ std::unique_ptr DiscreteLinearOperator::PartialAssemble() const } test_multiplicity.UseDevice(true); test_multiplicity.Reciprocal(); - - auto op = a.PartialAssemble(); op->SetDofMultiplicity(std::move(test_multiplicity)); + return op; } -std::unique_ptr -DiscreteLinearOperator::FullAssemble(const ceed::Operator &op, bool skip_zeros) -{ - return ceed::CeedOperatorFullAssemble(op, skip_zeros, true); -} +template std::vector> +BilinearForm::Assemble(const BaseFiniteElementSpaceHierarchy &, bool, + std::size_t) const; +template std::vector> +BilinearForm::Assemble(const BaseFiniteElementSpaceHierarchy &, + bool, std::size_t) const; } // namespace palace diff --git a/palace/fem/bilinearform.hpp b/palace/fem/bilinearform.hpp index 20a465bfe..9059785e3 100644 --- a/palace/fem/bilinearform.hpp +++ b/palace/fem/bilinearform.hpp @@ -13,6 +13,10 @@ namespace palace { +class FiniteElementSpace; +template +class BaseFiniteElementSpaceHierarchy; + // // This class implements bilinear and mixed bilinear forms based on integrators assembled // using the libCEED library. Assembly in the form of a partially assembled operator or @@ -22,41 +26,30 @@ class BilinearForm { protected: // Domain and range finite element spaces. - const mfem::ParFiniteElementSpace &trial_fespace, &test_fespace; + const FiniteElementSpace &trial_fespace, &test_fespace; // List of domain and boundary integrators making up the bilinear form. std::vector> domain_integs, boundary_integs; + std::unique_ptr + PartialAssemble(const FiniteElementSpace &trial_fespace, + const FiniteElementSpace &test_fespace) const; + public: // Order above which to use partial assembly vs. full. inline static int pa_order_threshold = 1; public: - BilinearForm(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace) + BilinearForm(const FiniteElementSpace &trial_fespace, + const FiniteElementSpace &test_fespace) : trial_fespace(trial_fespace), test_fespace(test_fespace) { } - BilinearForm(const mfem::ParFiniteElementSpace &fespace) : BilinearForm(fespace, fespace) - { - } + BilinearForm(const FiniteElementSpace &fespace) : BilinearForm(fespace, fespace) {} const auto &GetTrialSpace() const { return trial_fespace; } const auto &GetTestSpace() const { return test_fespace; } - // Returns order such that the miniumum for all element types is 1. MFEM's RT_FECollection - // actually already returns order + 1 for GetOrder() for historical reasons. - auto GetMaxElementOrder() const - { - const auto &trial_fec = *trial_fespace.FEColl(); - const auto &test_fec = *test_fespace.FEColl(); - return std::max( - dynamic_cast(&trial_fec) ? trial_fec.GetOrder() + 1 - : trial_fec.GetOrder(), - dynamic_cast(&test_fec) ? test_fec.GetOrder() + 1 - : test_fec.GetOrder()); - } - template void AddDomainIntegrator(U &&...args) { @@ -69,27 +62,33 @@ class BilinearForm boundary_integs.push_back(std::make_unique(std::forward(args)...)); } - std::unique_ptr Assemble(bool skip_zeros) const + void AssembleQuadratureData(); + + std::unique_ptr PartialAssemble() const { - if (GetMaxElementOrder() >= pa_order_threshold) - { - return PartialAssemble(); - } - else - { - return FullAssemble(skip_zeros); - } + return PartialAssemble(GetTrialSpace(), GetTestSpace()); } - std::unique_ptr PartialAssemble() const; - std::unique_ptr FullAssemble(bool skip_zeros) const { - return FullAssemble(*PartialAssemble(), skip_zeros); + return FullAssemble(*PartialAssemble(), skip_zeros, false); + } + + static std::unique_ptr FullAssemble(const ceed::Operator &op, + bool skip_zeros) + { + return FullAssemble(op, skip_zeros, false); } static std::unique_ptr FullAssemble(const ceed::Operator &op, - bool skip_zeros); + bool skip_zeros, bool set); + + std::unique_ptr Assemble(bool skip_zeros) const; + + template + std::vector> + Assemble(const BaseFiniteElementSpaceHierarchy &fespaces, bool skip_zeros, + std::size_t l0 = 0) const; }; // Discrete linear operators map primal vectors to primal vectors for interpolation between @@ -97,45 +96,40 @@ class BilinearForm class DiscreteLinearOperator { private: - BilinearForm a; + // Domain and range finite element spaces. + const FiniteElementSpace &trial_fespace, &test_fespace; + + // List of domain interpolators making up the discrete linear operator. + std::vector> domain_interps; public: - DiscreteLinearOperator(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace) - : a(trial_fespace, test_fespace) + DiscreteLinearOperator(const FiniteElementSpace &trial_fespace, + const FiniteElementSpace &test_fespace) + : trial_fespace(trial_fespace), test_fespace(test_fespace) { } - const auto &GetTrialSpace() const { return a.GetTrialSpace(); } - const auto &GetTestSpace() const { return a.GetTestSpace(); } + const auto &GetTrialSpace() const { return trial_fespace; } + const auto &GetTestSpace() const { return test_fespace; } template void AddDomainInterpolator(U &&...args) { - a.AddDomainIntegrator(std::forward(args)...); - } - - std::unique_ptr Assemble(bool skip_zeros) const - { - if (a.GetMaxElementOrder() >= a.pa_order_threshold) - { - return PartialAssemble(); - } - else - { - return FullAssemble(skip_zeros); - } + domain_interps.push_back(std::make_unique(std::forward(args)...)); } std::unique_ptr PartialAssemble() const; std::unique_ptr FullAssemble(bool skip_zeros) const { - return FullAssemble(*a.PartialAssemble(), skip_zeros); + return BilinearForm::FullAssemble(*PartialAssemble(), skip_zeros, true); } static std::unique_ptr FullAssemble(const ceed::Operator &op, - bool skip_zeros); + bool skip_zeros) + { + return BilinearForm::FullAssemble(op, skip_zeros, true); + } }; } // namespace palace diff --git a/palace/fem/fespace.cpp b/palace/fem/fespace.cpp index ba74a1f1b..9f0664cf5 100644 --- a/palace/fem/fespace.cpp +++ b/palace/fem/fespace.cpp @@ -5,34 +5,181 @@ #include "fem/bilinearform.hpp" #include "fem/integrator.hpp" +#include "fem/libceed/basis.hpp" +#include "fem/libceed/restriction.hpp" #include "linalg/rap.hpp" #include "utils/omp.hpp" namespace palace { -std::size_t FiniteElementSpace::GetGlobalId() +const CeedBasis FiniteElementSpace::GetCeedBasis(Ceed ceed, mfem::Geometry::Type geom) const { - static std::size_t global_id = 0; - std::size_t id; - PalacePragmaOmp(critical(GetGlobalId)) + // No two threads should ever be calling this simultaneously with the same Ceed context. + auto it = basis.find(ceed); + if (it == basis.end()) { - id = global_id++; + PalacePragmaOmp(critical(InitBasis)) + { + it = basis.emplace(ceed, ceed::CeedGeomObjectMap()).first; + } + } + auto &basis_map = it->second; + auto basis_it = basis_map.find(geom); + if (basis_it != basis_map.end()) + { + return basis_it->second; + } + auto val = BuildCeedBasis(*this, ceed, geom); + basis_map.emplace(geom, val); + return val; +} + +const CeedElemRestriction +FiniteElementSpace::GetCeedElemRestriction(Ceed ceed, mfem::Geometry::Type geom, + const std::vector &indices) const +{ + // No two threads should ever be calling this simultaneously with the same Ceed context. + auto it = restr.find(ceed); + if (it == restr.end()) + { + PalacePragmaOmp(critical(InitRestriction)) + { + it = restr.emplace(ceed, ceed::CeedGeomObjectMap()).first; + } + } + auto &restr_map = it->second; + auto restr_it = restr_map.find(geom); + if (restr_it != restr_map.end()) + { + return restr_it->second; + } + auto val = BuildCeedElemRestriction(*this, ceed, geom, indices); + restr_map.emplace(geom, val); + return val; +} + +const CeedElemRestriction +FiniteElementSpace::GetInterpCeedElemRestriction(Ceed ceed, mfem::Geometry::Type geom, + const std::vector &indices) const +{ + const mfem::FiniteElement &fe = *GetFEColl().FiniteElementForGeometry(geom); + if (!HasUniqueInterpRestriction(fe)) + { + return GetCeedElemRestriction(ceed, geom, indices); + } + // No two threads should ever be calling this simultaneously with the same Ceed context. + auto it = interp_restr.find(ceed); + if (it == interp_restr.end()) + { + PalacePragmaOmp(critical(InitInterpRestriction)) + { + it = interp_restr.emplace(ceed, ceed::CeedGeomObjectMap()).first; + } + } + auto &restr_map = it->second; + auto restr_it = restr_map.find(geom); + if (restr_it != restr_map.end()) + { + return restr_it->second; + } + auto val = BuildCeedElemRestriction(*this, ceed, geom, indices, true, false); + restr_map.emplace(geom, val); + return val; +} + +const CeedElemRestriction +FiniteElementSpace::GetInterpRangeCeedElemRestriction(Ceed ceed, mfem::Geometry::Type geom, + const std::vector &indices) const +{ + const mfem::FiniteElement &fe = *GetFEColl().FiniteElementForGeometry(geom); + if (!HasUniqueInterpRangeRestriction(fe)) + { + return GetInterpCeedElemRestriction(ceed, geom, indices); + } + // No two threads should ever be calling this simultaneously with the same Ceed context. + auto it = interp_range_restr.find(ceed); + if (it == interp_range_restr.end()) + { + PalacePragmaOmp(critical(InitInterpRangeRestriction)) + { + it = interp_range_restr.emplace(ceed, ceed::CeedGeomObjectMap()) + .first; + } + } + auto &restr_map = it->second; + auto restr_it = restr_map.find(geom); + if (restr_it != restr_map.end()) + { + return restr_it->second; } - return id; + auto val = BuildCeedElemRestriction(*this, ceed, geom, indices, true, true); + restr_map.emplace(geom, val); + return val; } -std::size_t FiniteElementSpace::GetId() const +void FiniteElementSpace::DestroyCeedObjects() { - PalacePragmaOmp(critical(GetId)) + for (auto &[ceed, basis_map] : basis) { - if (sequence != fespace.GetSequence()) + for (auto &[key, val] : basis_map) { - id = GetGlobalId(); - sequence = fespace.GetSequence(); + PalaceCeedCall(ceed, CeedBasisDestroy(&val)); } } - return id; + basis.clear(); + for (auto &[ceed, restr_map] : restr) + { + for (auto &[key, val] : restr_map) + { + PalaceCeedCall(ceed, CeedElemRestrictionDestroy(&val)); + } + } + restr.clear(); + for (auto &[ceed, restr_map] : interp_restr) + { + for (auto &[key, val] : restr_map) + { + PalaceCeedCall(ceed, CeedElemRestrictionDestroy(&val)); + } + } + interp_restr.clear(); + for (auto &[ceed, restr_map] : interp_range_restr) + { + for (auto &[key, val] : restr_map) + { + PalaceCeedCall(ceed, CeedElemRestrictionDestroy(&val)); + } + } + interp_range_restr.clear(); +} + +CeedBasis FiniteElementSpace::BuildCeedBasis(const mfem::FiniteElementSpace &fespace, + Ceed ceed, mfem::Geometry::Type geom) +{ + // Find the appropriate integration rule for the element. + mfem::IsoparametricTransformation T; + T.SetFE(fespace.GetMesh()->GetNodalFESpace()->FEColl()->FiniteElementForGeometry(geom)); + const int q_order = fem::DefaultIntegrationOrder::Get(T); + const mfem::IntegrationRule &ir = mfem::IntRules.Get(geom, q_order); + + // Build the libCEED basis. + CeedBasis val; + const mfem::FiniteElement &fe = *fespace.FEColl()->FiniteElementForGeometry(geom); + const int vdim = fespace.GetVDim(); + ceed::InitBasis(fe, ir, vdim, ceed, &val); + return val; +} + +CeedElemRestriction FiniteElementSpace::BuildCeedElemRestriction( + const mfem::FiniteElementSpace &fespace, Ceed ceed, mfem::Geometry::Type geom, + const std::vector &indices, bool is_interp, bool is_interp_range) +{ + // Construct the libCEED element restriction for this element type. + CeedElemRestriction val; + const bool use_bdr = (mfem::Geometry::Dimension[geom] != fespace.GetMesh()->Dimension()); + ceed::InitRestriction(fespace, indices, use_bdr, is_interp, is_interp_range, ceed, &val); + return val; } const Operator &AuxiliaryFiniteElementSpace::BuildDiscreteInterpolator() const diff --git a/palace/fem/fespace.hpp b/palace/fem/fespace.hpp index 6328fb3de..369d2a2f6 100644 --- a/palace/fem/fespace.hpp +++ b/palace/fem/fespace.hpp @@ -7,6 +7,7 @@ #include #include #include +#include "fem/libceed/ceed.hpp" #include "fem/mesh.hpp" #include "linalg/operator.hpp" @@ -25,19 +26,36 @@ class FiniteElementSpace // Reference to the underlying mesh object (not owned). Mesh &mesh; - // Members used to define equality between two spaces. - mutable long int sequence; - mutable std::size_t id; - static std::size_t GetGlobalId(); + // Members for constructing libCEED operators. + mutable ceed::CeedObjectMap basis; + mutable ceed::CeedObjectMap restr, interp_restr, interp_range_restr; + + bool HasUniqueInterpRestriction(const mfem::FiniteElement &fe) const + { + // For interpolation operators and tensor-product elements, we need native (not + // lexicographic) ordering. + const mfem::TensorBasisElement *tfe = + dynamic_cast(&fe); + return (tfe && tfe->GetDofMap().Size() > 0 && + fe.GetRangeType() != mfem::FiniteElement::VECTOR); + } + + bool HasUniqueInterpRangeRestriction(const mfem::FiniteElement &fe) const + { + // The range restriction for interpolation operators needs to use a special + // DofTransformation (not equal to the transpose of the domain restriction). + const auto geom = fe.GetGeomType(); + const auto *dof_trans = fespace.FEColl()->DofTransformationForGeometry(geom); + return (dof_trans && !dof_trans->IsIdentity()); + } public: template FiniteElementSpace(Mesh &mesh, T &&...args) - : fespace(&mesh.Get(), std::forward(args)...), mesh(mesh), - sequence(fespace.GetSequence()), id(GetGlobalId()) + : fespace(&mesh.Get(), std::forward(args)...), mesh(mesh) { } - virtual ~FiniteElementSpace() = default; + virtual ~FiniteElementSpace() { DestroyCeedObjects(); } const auto &Get() const { return fespace; } auto &Get() { return fespace; } @@ -62,15 +80,37 @@ class FiniteElementSpace auto SpaceDimension() const { return mesh.Get().SpaceDimension(); } auto GetMaxElementOrder() const { return Get().GetMaxElementOrder(); } - // Get the ID associated with the instance of this class. If the underlying sequence has - // changed (due to a mesh update, for example), regenerate the ID. - std::size_t GetId() const; - - // Operator overload for equality comparisons between two spaces. - bool operator==(const FiniteElementSpace &fespace) const - { - return GetId() == fespace.GetId(); - } + // Return the basis object for elements of the given element geometry type. + const CeedBasis GetCeedBasis(Ceed ceed, mfem::Geometry::Type geom) const; + + // Return the element restriction object for the given element set (all with the same + // geometry type). + const CeedElemRestriction GetCeedElemRestriction(Ceed ceed, mfem::Geometry::Type geom, + const std::vector &indices) const; + + // If the space has a special element restriction for discrete interpolators, return that. + // Otherwise return the same restiction as given by GetCeedElemRestriction. + const CeedElemRestriction + GetInterpCeedElemRestriction(Ceed ceed, mfem::Geometry::Type geom, + const std::vector &indices) const; + + // If the space has a special element restriction for the range space of discrete + // interpolators, return that. Otherwise return the same restiction as given by + // GetCeedElemRestriction. + const CeedElemRestriction + GetInterpRangeCeedElemRestriction(Ceed ceed, mfem::Geometry::Type geom, + const std::vector &indices) const; + + // Clear the cached basis and element restriction objects owned by the finite element + // space. + void DestroyCeedObjects(); + + static CeedBasis BuildCeedBasis(const mfem::FiniteElementSpace &fespace, Ceed ceed, + mfem::Geometry::Type geom); + static CeedElemRestriction + BuildCeedElemRestriction(const mfem::FiniteElementSpace &fespace, Ceed ceed, + mfem::Geometry::Type geom, const std::vector &indices, + bool is_interp = false, bool is_interp_range = false); // Get the associated MPI communicator. MPI_Comm GetComm() const { return fespace.GetComm(); } diff --git a/palace/fem/mesh.cpp b/palace/fem/mesh.cpp index 71740c4c6..8e6691116 100644 --- a/palace/fem/mesh.cpp +++ b/palace/fem/mesh.cpp @@ -5,10 +5,33 @@ #include "fem/coefficient.hpp" #include "fem/fespace.hpp" +#include "fem/libceed/integrator.hpp" +#include "utils/omp.hpp" namespace palace { +namespace ceed +{ + +namespace +{ + +CeedGeomFactorData CeedGeomFactorDataCreate(Ceed ceed) +{ + return std::make_unique(ceed); +} + +} // namespace + +CeedGeomFactorData_private::~CeedGeomFactorData_private() +{ + PalaceCeedCall(ceed, CeedVectorDestroy(&geom_data_vec)); + PalaceCeedCall(ceed, CeedElemRestrictionDestroy(&geom_data_restr)); +} + +} // namespace ceed + namespace { @@ -30,6 +53,18 @@ auto &GetParentMesh(mfem::ParMesh &mesh) GetParentMesh(const_cast(mesh))); } +auto GetBdrNeighborAttribute(int i, const mfem::ParMesh &mesh, + mfem::FaceElementTransformations &FET, + mfem::IsoparametricTransformation &T1, + mfem::IsoparametricTransformation &T2) +{ + // For internal boundaries, use the element which corresponds to the domain with lower + // attribute number (ensures all boundary elements are aligned). + BdrGridFunctionCoefficient::GetBdrElementNeighborTransformations(i, mesh, FET, T1, T2); + return (FET.Elem2 && FET.Elem2->Attribute < FET.Elem1->Attribute) ? FET.Elem2->Attribute + : FET.Elem1->Attribute; +} + auto BuildAttributeGlobalToLocal(const mfem::ParMesh &mesh) { // Set up sparse map from global domain attributes to local ones on this process. @@ -64,18 +99,6 @@ auto BuildAttributeGlobalToLocal(const mfem::ParMesh &mesh) return loc_attr; } -auto GetBdrNeighborAttribute(int i, const mfem::ParMesh &mesh, - mfem::FaceElementTransformations &FET, - mfem::IsoparametricTransformation &T1, - mfem::IsoparametricTransformation &T2) -{ - // For internal boundaries, use the element which corresponds to the domain with lower - // attribute number (ensures all boundary elements are aligned). - BdrGridFunctionCoefficient::GetBdrElementNeighborTransformations(i, mesh, FET, T1, T2); - return (FET.Elem2 && FET.Elem2->Attribute < FET.Elem1->Attribute) ? FET.Elem2->Attribute - : FET.Elem1->Attribute; -} - auto BuildBdrAttributeGlobalToLocal(const mfem::ParMesh &mesh) { // Set up sparse map from global boundary attributes to local ones on this process. Each @@ -98,6 +121,218 @@ auto BuildBdrAttributeGlobalToLocal(const mfem::ParMesh &mesh) return loc_bdr_attr; } +auto GetElementIndices(const mfem::ParMesh &mesh, bool use_bdr, int start, int stop) +{ + // Count the number of elements of each type in the local mesh. + std::unordered_map counts; + for (int i = start; i < stop; i++) + { + const auto geom = use_bdr ? mesh.GetBdrElementGeometry(i) : mesh.GetElementGeometry(i); + auto it = counts.find(geom); + if (it == counts.end()) + { + counts[geom] = 1; + } + else + { + it->second++; + } + } + + // Populate the indices arrays for each element geometry. + std::unordered_map offsets; + std::unordered_map> element_indices; + for (auto it = counts.begin(); it != counts.end(); ++it) + { + offsets[it->first] = 0; + element_indices[it->first] = std::vector(it->second); + } + for (int i = start; i < stop; i++) + { + const auto geom = use_bdr ? mesh.GetBdrElementGeometry(i) : mesh.GetElementGeometry(i); + auto &offset = offsets[geom]; + auto &indices = element_indices[geom]; + indices[offset++] = i; + } + + return element_indices; +} + +template +auto AssembleGeometryData(const mfem::GridFunction &mesh_nodes, Ceed ceed, + mfem::Geometry::Type geom, std::vector &indices, + T GetCeedAttribute) +{ + const mfem::FiniteElementSpace &mesh_fespace = *mesh_nodes.FESpace(); + const mfem::Mesh &mesh = *mesh_fespace.GetMesh(); + + auto data = ceed::CeedGeomFactorDataCreate(ceed); + data->dim = mfem::Geometry::Dimension[geom]; + data->space_dim = mesh.SpaceDimension(); + data->indices = std::move(indices); + const std::size_t num_elem = data->indices.size(); + + // Allocate data structures for geometry factor data (attribute + quadrature weight + + // Jacobian). + CeedElemRestriction mesh_restr = + FiniteElementSpace::BuildCeedElemRestriction(mesh_fespace, ceed, geom, data->indices); + CeedBasis mesh_basis = FiniteElementSpace::BuildCeedBasis(mesh_fespace, ceed, geom); + CeedInt num_qpts, geom_data_size = 2 + data->space_dim * data->dim; + PalaceCeedCall(ceed, CeedBasisGetNumQuadraturePoints(mesh_basis, &num_qpts)); + + // Data for quadrature point i, component j, element k is found at index i * strides[0] + + // j * strides[1] + k * strides[2]. + CeedMemType mem; + CeedInt strides[3]; + PalaceCeedCall(ceed, CeedGetPreferredMemType(ceed, &mem)); + if (mfem::Device::Allows(mfem::Backend::DEVICE_MASK) && mem == CEED_MEM_DEVICE) + { + // GPU backends have CEED_STRIDES_BACKEND = {1, num_elem * num_qpts, num_qpts}. + strides[0] = 1; + strides[1] = num_elem * num_qpts; + strides[2] = num_qpts; + } + else + { + // CPU backends have CEED_STRIDES_BACKEND = {1, num_qpts, num_qpts * geom_data_size}. + strides[0] = 1; + strides[1] = num_qpts; + strides[2] = num_qpts * geom_data_size; + } + PalaceCeedCall(ceed, + CeedElemRestrictionCreateStrided(ceed, num_elem, num_qpts, geom_data_size, + num_elem * num_qpts * geom_data_size, + strides, &data->geom_data_restr)); + + // Compute element attribute quadrature data. All inputs to a QFunction require the same + // number of quadrature points, so we store the attribute at each quadrature point. This + // is the first component of the quadrature data. + data->geom_data.SetSize(num_elem * num_qpts * geom_data_size); + for (std::size_t k = 0; k < num_elem; k++) + { + const auto attr = GetCeedAttribute(data->indices[k]); + for (CeedInt i = 0; i < num_qpts; i++) + { + data->geom_data[i * strides[0] + k * strides[2]] = attr; + } + } + ceed::InitCeedVector(data->geom_data, ceed, &data->geom_data_vec); + + // Compute the required geometry factors at quadrature points. + CeedVector mesh_nodes_vec; + ceed::InitCeedVector(mesh_nodes, ceed, &mesh_nodes_vec); + + ceed::AssembleCeedGeometryData(ceed, mesh_restr, mesh_basis, mesh_nodes_vec, + data->geom_data_vec, data->geom_data_restr); + + PalaceCeedCall(ceed, CeedVectorDestroy(&mesh_nodes_vec)); + PalaceCeedCall(ceed, CeedElemRestrictionDestroy(&mesh_restr)); + PalaceCeedCall(ceed, CeedBasisDestroy(&mesh_basis)); + + return data; +} + +auto BuildCeedGeomFactorData( + const mfem::ParMesh &mesh, const std::unordered_map &loc_attr, + const std::unordered_map> &loc_bdr_attr, Ceed ceed) +{ + // Create a list of the element indices in the mesh corresponding to a given thread and + // element geometry type and corresponding geometry factor data. libCEED operators will be + // constructed in parallel over threads, where each thread builds a composite operator + // with sub-operators for each geometry. + std::size_t i; + const std::size_t nt = ceed::internal::GetCeedObjects().size(); + for (i = 0; i < nt; i++) + { + if (ceed == ceed::internal::GetCeedObjects()[i]) + { + break; + } + } + MFEM_VERIFY(i < nt, "Unable to find matching Ceed context in BuildCeedGeomFactorData!"); + mfem::FaceElementTransformations FET; + mfem::IsoparametricTransformation T1, T2; + ceed::CeedGeomObjectMap geom_data; + + // First domain elements. + { + const int num_elem = mesh.GetNE(); + const int stride = (num_elem + nt - 1) / nt; + const int start = i * stride; + const int stop = std::min(start + stride, num_elem); + constexpr bool use_bdr = false; + auto element_indices = GetElementIndices(mesh, use_bdr, start, stop); + auto GetCeedAttribute = [&]() -> std::function + { + if (const auto *submesh = dynamic_cast(&mesh)) + { + MFEM_VERIFY(submesh->GetFrom() == mfem::SubMesh::From::Boundary, + "Unexpected non-SubMesh object for BuildCeedGeomFactorData with Mesh " + "with (dim, space_dim) = (" + << mesh.Dimension() << ", " << mesh.SpaceDimension() << ")!"); + return [&](int i) + { + // Mesh is actually a boundary submesh, so we use the boundary attribute mappings + // from the parent mesh. + const int attr = mesh.GetAttribute(i); + const int nbr_attr = GetBdrNeighborAttribute(submesh->GetParentElementIDMap()[i], + *submesh->GetParent(), FET, T1, T2); + MFEM_ASSERT(loc_bdr_attr.find(attr) != loc_bdr_attr.end() && + loc_bdr_attr.at(attr).find(nbr_attr) != + loc_bdr_attr.at(attr).end(), + "Missing local boundary attribute for attribute " << attr << "!"); + return loc_bdr_attr.at(attr).at(nbr_attr); + }; + } + else + { + return [&](int i) + { + const int attr = mesh.GetAttribute(i); + MFEM_ASSERT(loc_attr.find(attr) != loc_attr.end(), + "Missing local domain attribute for attribute " << attr << "!"); + return attr; + }; + } + }(); + for (auto &[geom, indices] : element_indices) + { + ceed::CeedGeomFactorData data = + AssembleGeometryData(*mesh.GetNodes(), ceed, geom, indices, GetCeedAttribute); + geom_data.emplace(geom, std::move(data)); + } + } + + // Then boundary elements (no support for boundary integrators on meshes embedded in + // higher dimensional space for now). + if (mesh.Dimension() == mesh.SpaceDimension()) + { + const int nbe = mesh.GetNBE(); + const int stride = (nbe + nt - 1) / nt; + const int start = i * stride; + const int stop = std::min(start + stride, nbe); + constexpr bool use_bdr = true; + auto element_indices = GetElementIndices(mesh, use_bdr, start, stop); + auto GetCeedAttribute = [&](int i) + { + const int attr = mesh.GetBdrAttribute(i); + const int nbr_attr = GetBdrNeighborAttribute(i, mesh, FET, T1, T2); + MFEM_ASSERT(loc_bdr_attr.find(attr) != loc_bdr_attr.end() && + loc_bdr_attr.at(attr).find(nbr_attr) != loc_bdr_attr.at(attr).end(), + "Missing local boundary attribute for attribute " << attr << "!"); + return loc_bdr_attr.at(attr).at(nbr_attr); + }; + for (auto &[geom, indices] : element_indices) + { + ceed::CeedGeomFactorData data = + AssembleGeometryData(*mesh.GetNodes(), ceed, geom, indices, GetCeedAttribute); + geom_data.emplace(geom, std::move(data)); + } + } + + return geom_data; +} + } // namespace void Mesh::Rebuild() const @@ -153,4 +388,25 @@ int Mesh::GetAttributeGlobalToLocal(const mfem::ElementTransformation &T) const } } +const ceed::CeedGeomObjectMap & +Mesh::GetCeedGeomFactorData(Ceed ceed) const +{ + // No two threads should ever be calling this simultaneously with the same Ceed context. + auto it = geom_data.find(ceed); + if (it == geom_data.end()) + { + auto val = BuildCeedGeomFactorData(*mesh, loc_attr, loc_bdr_attr, ceed); + PalacePragmaOmp(critical(InitCeedGeomFactorData)) + { + it = geom_data.emplace(ceed, std::move(val)).first; + } + } + return it->second; +} + +void Mesh::DestroyCeedGeomFactorData() const +{ + geom_data.clear(); +} + } // namespace palace diff --git a/palace/fem/mesh.hpp b/palace/fem/mesh.hpp index 9425d88c6..3e23c444d 100644 --- a/palace/fem/mesh.hpp +++ b/palace/fem/mesh.hpp @@ -8,10 +8,46 @@ #include #include #include +#include "fem/libceed/ceed.hpp" namespace palace { +namespace ceed +{ + +// +// Data structure for geometry information stored at quadrature points. +// +struct CeedGeomFactorData_private +{ + // Dimension of this element topology and space dimension of the underlying mesh. + int dim, space_dim; + + // Element indices from the mfem::Mesh used to construct Ceed objects with these geometry + // factors. + std::vector indices; + + // Mesh geometry factor data: {attr, w * |J|, adj(J)^T / |J|}. Jacobian matrix is + // space_dim x dim, stored column-major by component. + mfem::Vector geom_data; + + // Objects for libCEED interface to the quadrature data. + CeedVector geom_data_vec; + CeedElemRestriction geom_data_restr; + Ceed ceed; + + CeedGeomFactorData_private(Ceed ceed) + : dim(0), space_dim(0), geom_data_vec(nullptr), geom_data_restr(nullptr), ceed(ceed) + { + } + ~CeedGeomFactorData_private(); +}; + +using CeedGeomFactorData = std::unique_ptr; + +} // namespace ceed + // // Wrapper for MFEM's ParMesh class, with extensions for Palace. // @@ -34,6 +70,15 @@ class Mesh mutable std::unordered_map loc_attr; mutable std::unordered_map> loc_bdr_attr; + // Mesh data structures for assembling libCEED operators on a (mixed) mesh: + // - Mesh element indices for threads and element geometry types. + // - Attributes for domain and boundary elements. The attributes are not the same as the + // MFEM mesh element attributes, they correspond to the local (still 1-based) + // attributes above. + // - Geometry factor quadrature point data (w |J| and adj(J)^T / |J|) for domain and + // boundary elements. + mutable ceed::CeedObjectMap geom_data; + void CheckSequenceRebuild() const { if (sequence != mesh->GetSequence()) @@ -134,6 +179,11 @@ class Mesh int GetAttributeGlobalToLocal(const mfem::ElementTransformation &T) const; + const ceed::CeedGeomObjectMap & + GetCeedGeomFactorData(Ceed ceed) const; + + void DestroyCeedGeomFactorData() const; + MPI_Comm GetComm() const { return mesh->GetComm(); } }; From 15bde4ea3e33b311b20dbb8e7e4c605f91d586b9 Mon Sep 17 00:00:00 2001 From: Sebastian Grimberg Date: Tue, 19 Dec 2023 14:13:24 -0800 Subject: [PATCH 12/32] WIP: Add new libCEED QFunctions making use of geometry factor quadrature data --- palace/fem/qfunctions/apply_qf.h | 191 +++ palace/fem/qfunctions/coeff_qf.h | 86 ++ palace/fem/qfunctions/curlcurl_qf.h | 174 --- palace/fem/qfunctions/curlcurlmass_qf.h | 402 ------ palace/fem/qfunctions/diffusion_qf.h | 239 ---- palace/fem/qfunctions/diffusionmass_qf.h | 253 ---- palace/fem/qfunctions/divdiv_qf.h | 125 -- palace/fem/qfunctions/divdivmass_qf.h | 252 ---- palace/fem/qfunctions/geom_qf.h | 106 ++ palace/fem/qfunctions/grad_qf.h | 254 ---- palace/fem/qfunctions/h1_build_qf.h | 66 + palace/fem/qfunctions/h1_qf.h | 69 ++ palace/fem/qfunctions/hcurl_build_qf.h | 94 ++ palace/fem/qfunctions/hcurl_qf.h | 224 +--- palace/fem/qfunctions/hcurlh1d_build_qf.h | 99 ++ palace/fem/qfunctions/hcurlh1d_qf.h | 96 ++ palace/fem/qfunctions/hcurlhdiv_build_qf.h | 194 +++ palace/fem/qfunctions/hcurlhdiv_qf.h | 464 ++----- palace/fem/qfunctions/hcurlmass_build_qf.h | 122 ++ palace/fem/qfunctions/hcurlmass_qf.h | 130 ++ palace/fem/qfunctions/hdiv_build_qf.h | 99 ++ palace/fem/qfunctions/hdiv_qf.h | 229 +--- palace/fem/qfunctions/hdivmass_build_qf.h | 109 ++ palace/fem/qfunctions/hdivmass_qf.h | 123 ++ palace/fem/qfunctions/l2_build_qf.h | 68 + palace/fem/qfunctions/l2_qf.h | 72 ++ palace/fem/qfunctions/l2mass_build_qf.h | 131 ++ palace/fem/qfunctions/l2mass_qf.h | 141 +++ palace/fem/qfunctions/mass_qf.h | 333 ----- palace/fem/qfunctions/utils_geom_qf.h | 107 ++ palace/fem/qfunctions/utils_qf.h | 1298 +++++--------------- palace/fem/qfunctions/vecfemass_qf.h | 82 -- 32 files changed, 2695 insertions(+), 3737 deletions(-) create mode 100644 palace/fem/qfunctions/apply_qf.h create mode 100644 palace/fem/qfunctions/coeff_qf.h delete mode 100644 palace/fem/qfunctions/curlcurl_qf.h delete mode 100644 palace/fem/qfunctions/curlcurlmass_qf.h delete mode 100644 palace/fem/qfunctions/diffusion_qf.h delete mode 100644 palace/fem/qfunctions/diffusionmass_qf.h delete mode 100644 palace/fem/qfunctions/divdiv_qf.h delete mode 100644 palace/fem/qfunctions/divdivmass_qf.h create mode 100644 palace/fem/qfunctions/geom_qf.h delete mode 100644 palace/fem/qfunctions/grad_qf.h create mode 100644 palace/fem/qfunctions/h1_build_qf.h create mode 100644 palace/fem/qfunctions/h1_qf.h create mode 100644 palace/fem/qfunctions/hcurl_build_qf.h create mode 100644 palace/fem/qfunctions/hcurlh1d_build_qf.h create mode 100644 palace/fem/qfunctions/hcurlh1d_qf.h create mode 100644 palace/fem/qfunctions/hcurlhdiv_build_qf.h create mode 100644 palace/fem/qfunctions/hcurlmass_build_qf.h create mode 100644 palace/fem/qfunctions/hcurlmass_qf.h create mode 100644 palace/fem/qfunctions/hdiv_build_qf.h create mode 100644 palace/fem/qfunctions/hdivmass_build_qf.h create mode 100644 palace/fem/qfunctions/hdivmass_qf.h create mode 100644 palace/fem/qfunctions/l2_build_qf.h create mode 100644 palace/fem/qfunctions/l2_qf.h create mode 100644 palace/fem/qfunctions/l2mass_build_qf.h create mode 100644 palace/fem/qfunctions/l2mass_qf.h delete mode 100644 palace/fem/qfunctions/mass_qf.h create mode 100644 palace/fem/qfunctions/utils_geom_qf.h delete mode 100644 palace/fem/qfunctions/vecfemass_qf.h diff --git a/palace/fem/qfunctions/apply_qf.h b/palace/fem/qfunctions/apply_qf.h new file mode 100644 index 000000000..2d39dbb57 --- /dev/null +++ b/palace/fem/qfunctions/apply_qf.h @@ -0,0 +1,191 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef PALACE_LIBCEED_APPLY_QF_H +#define PALACE_LIBCEED_APPLY_QF_H + +// libCEED QFunctions for application of a generic operator with assembled quadrature data. +// in[0] is (symmetric) quadrature data, shape [ncomp=vdim*(vdim+1)/2, Q] +// in[1] is active vector, shape [ncomp=vdim, Q] +// out[0] is active vector, shape [ncomp=vdim, Q] + +// For pairwise apply functions, the inputs and outputs come in pairs and the quadrature +// data is arranged to be applied with the first vdim*(vdim+1)/2 components for the first +// input/output and the remainder for the second. + +CEED_QFUNCTION(f_apply_1)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *__restrict__ qd = in[0], *__restrict__ u = in[1]; + CeedScalar *__restrict__ v = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + v[i] = qd[i] * u[i]; + } + return 0; +} + +CEED_QFUNCTION(f_apply_2)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *__restrict__ qd = in[0], *__restrict__ u = in[1]; + CeedScalar *__restrict__ v = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + const CeedScalar u0 = u[i + Q * 0]; + const CeedScalar u1 = u[i + Q * 1]; + v[i + Q * 0] = qd[i + Q * 0] * u0 + qd[i + Q * 1] * u1; + v[i + Q * 1] = qd[i + Q * 1] * u0 + qd[i + Q * 2] * u1; + } + return 0; +} + +CEED_QFUNCTION(f_apply_3)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *__restrict__ qd = in[0], *__restrict__ u = in[1]; + CeedScalar *__restrict__ v = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + const CeedScalar u0 = u[i + Q * 0]; + const CeedScalar u1 = u[i + Q * 1]; + const CeedScalar u2 = u[i + Q * 2]; + v[i + Q * 0] = qd[i + Q * 0] * u0 + qd[i + Q * 1] * u1 + qd[i + Q * 2] * u2; + v[i + Q * 1] = qd[i + Q * 1] * u0 + qd[i + Q * 3] * u1 + qd[i + Q * 4] * u2; + v[i + Q * 2] = qd[i + Q * 2] * u0 + qd[i + Q * 4] * u1 + qd[i + Q * 5] * u2; + } + return 0; +} + +CEED_QFUNCTION(f_apply_22)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *__restrict__ qd1 = in[0], *__restrict__ qd2 = in[0] + 3 * Q, + *__restrict__ u1 = in[1], *__restrict__ u2 = in[2]; + CeedScalar *__restrict__ v1 = out[0], *__restrict__ v2 = out[1]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + const CeedScalar u10 = u1[i + Q * 0]; + const CeedScalar u11 = u1[i + Q * 1]; + v1[i + Q * 0] = qd1[i + Q * 0] * u10 + qd1[i + Q * 1] * u11; + v1[i + Q * 1] = qd1[i + Q * 1] * u10 + qd1[i + Q * 2] * u11; + + const CeedScalar u20 = u2[i + Q * 0]; + const CeedScalar u21 = u2[i + Q * 1]; + v2[i + Q * 0] = qd2[i + Q * 0] * u20 + qd2[i + Q * 1] * u21; + v2[i + Q * 1] = qd2[i + Q * 1] * u20 + qd2[i + Q * 2] * u21; + } + return 0; +} + +CEED_QFUNCTION(f_apply_33)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *__restrict__ qd1 = in[0], *__restrict__ qd2 = in[0] + 6 * Q, + *__restrict__ u1 = in[1], *__restrict__ u2 = in[2]; + CeedScalar *__restrict__ v1 = out[0], *__restrict__ v2 = out[1]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + const CeedScalar u10 = u1[i + Q * 0]; + const CeedScalar u11 = u1[i + Q * 1]; + const CeedScalar u12 = u1[i + Q * 2]; + v1[i + Q * 0] = qd1[i + Q * 0] * u10 + qd1[i + Q * 1] * u11 + qd1[i + Q * 2] * u12; + v1[i + Q * 1] = qd1[i + Q * 1] * u10 + qd1[i + Q * 3] * u11 + qd1[i + Q * 4] * u12; + v1[i + Q * 2] = qd1[i + Q * 2] * u10 + qd1[i + Q * 4] * u11 + qd1[i + Q * 5] * u12; + + const CeedScalar u20 = u2[i + Q * 0]; + const CeedScalar u21 = u2[i + Q * 1]; + const CeedScalar u22 = u2[i + Q * 2]; + v2[i + Q * 0] = qd2[i + Q * 0] * u20 + qd2[i + Q * 1] * u21 + qd2[i + Q * 2] * u22; + v2[i + Q * 1] = qd2[i + Q * 1] * u20 + qd2[i + Q * 3] * u21 + qd2[i + Q * 4] * u22; + v2[i + Q * 2] = qd2[i + Q * 2] * u20 + qd2[i + Q * 4] * u21 + qd2[i + Q * 5] * u22; + } + return 0; +} + +CEED_QFUNCTION(f_apply_12)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *__restrict__ qd1 = in[0], *__restrict__ qd2 = in[0] + Q, + *__restrict__ u1 = in[1], *__restrict__ u2 = in[2]; + CeedScalar *__restrict__ v1 = out[0], *__restrict__ v2 = out[1]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + v1[i] = qd1[i] * u1[i]; + + const CeedScalar u20 = u2[i + Q * 0]; + const CeedScalar u21 = u2[i + Q * 1]; + v2[i + Q * 0] = qd2[i + Q * 0] * u20 + qd2[i + Q * 1] * u21; + v2[i + Q * 1] = qd2[i + Q * 1] * u20 + qd2[i + Q * 2] * u21; + } + return 0; +} + +CEED_QFUNCTION(f_apply_13)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *__restrict__ qd1 = in[0], *__restrict__ qd2 = in[0] + Q, + *__restrict__ u1 = in[1], *__restrict__ u2 = in[2]; + CeedScalar *__restrict__ v1 = out[0], *__restrict__ v2 = out[1]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + v1[i] = qd1[i] * u1[i]; + + const CeedScalar u20 = u2[i + Q * 0]; + const CeedScalar u21 = u2[i + Q * 1]; + const CeedScalar u22 = u2[i + Q * 2]; + v2[i + Q * 0] = qd2[i + Q * 0] * u20 + qd2[i + Q * 1] * u21 + qd2[i + Q * 2] * u22; + v2[i + Q * 1] = qd2[i + Q * 1] * u20 + qd2[i + Q * 3] * u21 + qd2[i + Q * 4] * u22; + v2[i + Q * 2] = qd2[i + Q * 2] * u20 + qd2[i + Q * 4] * u21 + qd2[i + Q * 5] * u22; + } + return 0; +} + +CEED_QFUNCTION(f_apply_21)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *__restrict__ qd1 = in[0], *__restrict__ qd2 = in[0] + 3 * Q, + *__restrict__ u1 = in[1], *__restrict__ u2 = in[2]; + CeedScalar *__restrict__ v1 = out[0], *__restrict__ v2 = out[1]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + const CeedScalar u10 = u1[i + Q * 0]; + const CeedScalar u11 = u1[i + Q * 1]; + v1[i + Q * 0] = qd1[i + Q * 0] * u10 + qd1[i + Q * 1] * u11; + v1[i + Q * 1] = qd1[i + Q * 1] * u10 + qd1[i + Q * 2] * u11; + + v2[i] = qd2[i] * u2[i]; + } + return 0; +} + +CEED_QFUNCTION(f_apply_31)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *__restrict__ qd1 = in[0], *__restrict__ qd2 = in[0] + 6 * Q, + *__restrict__ u1 = in[1], *__restrict__ u2 = in[2]; + CeedScalar *__restrict__ v1 = out[0], *__restrict__ v2 = out[1]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + const CeedScalar u10 = u1[i + Q * 0]; + const CeedScalar u11 = u1[i + Q * 1]; + const CeedScalar u12 = u1[i + Q * 2]; + v1[i + Q * 0] = qd1[i + Q * 0] * u10 + qd1[i + Q * 1] * u11 + qd1[i + Q * 2] * u12; + v1[i + Q * 1] = qd1[i + Q * 1] * u10 + qd1[i + Q * 3] * u11 + qd1[i + Q * 4] * u12; + v1[i + Q * 2] = qd1[i + Q * 2] * u10 + qd1[i + Q * 4] * u11 + qd1[i + Q * 5] * u12; + + v2[i] = qd2[i] * u2[i]; + } + return 0; +} + +#endif // PALACE_LIBCEED_APPLY_QF_H diff --git a/palace/fem/qfunctions/coeff_qf.h b/palace/fem/qfunctions/coeff_qf.h new file mode 100644 index 000000000..6c45c21b6 --- /dev/null +++ b/palace/fem/qfunctions/coeff_qf.h @@ -0,0 +1,86 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef PALACE_LIBCEED_COEFF_QF_H +#define PALACE_LIBCEED_COEFF_QF_H + +union CeedIntScalar +{ + CeedInt first; + CeedScalar second; +}; + +// The first entry of ctx is the number of (1-based) attributes, followed by the entries of +// the attribute to material index array (these are 0-based). +// The next entry is the number of material property coefficients, followed by the +// coefficients. +// Pair coefficients are two coefficient contexts arranged contiguously in memory. + +CEED_QFUNCTION_HELPER const CeedIntScalar *AttrMat(const CeedIntScalar *ctx) +{ + return ctx + 1; +} + +CEED_QFUNCTION_HELPER const CeedIntScalar *MatCoeff(const CeedIntScalar *ctx) +{ + const CeedInt num_attr = ctx[0].first; + return ctx + 2 + num_attr; +} + +CEED_QFUNCTION_HELPER CeedScalar CoeffUnpack1(const CeedIntScalar *ctx, const CeedInt attr) +{ + const CeedInt k = AttrMat(ctx)[attr - 1].first; + return MatCoeff(ctx)[k].second; +} + +CEED_QFUNCTION_HELPER void CoeffUnpack1(const CeedIntScalar *ctx, const CeedInt attr, + CeedScalar coeff[1]) +{ + coeff[0] = CoeffUnpack1(ctx, attr); +} + +CEED_QFUNCTION_HELPER void CoeffUnpack2(const CeedIntScalar *ctx, const CeedInt attr, + CeedScalar coeff[3]) +{ + const CeedInt k = AttrMat(ctx)[attr - 1].first; + const CeedIntScalar *mat_coeff = MatCoeff(ctx); + coeff[0] = mat_coeff[3 * k + 0].second; + coeff[1] = mat_coeff[3 * k + 1].second; + coeff[2] = mat_coeff[3 * k + 2].second; +} + +CEED_QFUNCTION_HELPER void CoeffUnpack3(const CeedIntScalar *ctx, const CeedInt attr, + CeedScalar coeff[6]) +{ + const CeedInt k = AttrMat(ctx)[attr - 1].first; + const CeedIntScalar *mat_coeff = MatCoeff(ctx); + coeff[0] = mat_coeff[6 * k + 0].second; + coeff[1] = mat_coeff[6 * k + 1].second; + coeff[2] = mat_coeff[6 * k + 2].second; + coeff[3] = mat_coeff[6 * k + 3].second; + coeff[4] = mat_coeff[6 * k + 4].second; + coeff[5] = mat_coeff[6 * k + 5].second; +} + +CEED_QFUNCTION_HELPER const CeedIntScalar *CoeffPairSecond1(const CeedIntScalar *ctx) +{ + const CeedInt num_attr = ctx[0].first; + const CeedInt num_mat = ctx[1 + num_attr].first; + return ctx + 2 + num_attr + num_mat; +} + +CEED_QFUNCTION_HELPER const CeedIntScalar *CoeffPairSecond2(const CeedIntScalar *ctx) +{ + const CeedInt num_attr = ctx[0].first; + const CeedInt num_mat = ctx[1 + num_attr].first; + return ctx + 2 + num_attr + 3 * num_mat; +} + +CEED_QFUNCTION_HELPER const CeedIntScalar *CoeffPairSecond3(const CeedIntScalar *ctx) +{ + const CeedInt num_attr = ctx[0].first; + const CeedInt num_mat = ctx[1 + num_attr].first; + return ctx + 2 + num_attr + 6 * num_mat; +} + +#endif // PALACE_LIBCEED_COEFF_QF_H diff --git a/palace/fem/qfunctions/curlcurl_qf.h b/palace/fem/qfunctions/curlcurl_qf.h deleted file mode 100644 index ce7b34da6..000000000 --- a/palace/fem/qfunctions/curlcurl_qf.h +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -#ifndef PALACE_LIBCEED_CURLCURL_QF_H -#define PALACE_LIBCEED_CURLCURL_QF_H - -#include "utils_qf.h" - -struct CurlCurlContext -{ - CeedInt dim, space_dim, curl_dim; - CeedScalar coeff; -}; - -// libCEED QFunction for building quadrature data for a curl-curl operator with a scalar -// constant coefficient. -CEED_QFUNCTION(f_build_curlcurl_const_scalar)(void *ctx, CeedInt Q, - const CeedScalar *const *in, - CeedScalar *const *out) -{ - // At every quadrature point, compute qw / det(J) J^T C J and store the symmetric part of - // the result. In 2D, compute and store qw * c / det(J). - // in[0] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[1] is quadrature weights, size (Q) - CurlCurlContext *bc = (CurlCurlContext *)ctx; - const CeedScalar coeff = bc->coeff; - const CeedScalar *J = in[0], *qw = in[1]; - CeedScalar *qd = out[0]; - switch (100 * bc->space_dim + 10 * bc->dim + bc->curl_dim) - { - case 221: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qd[i] = qw[i] * coeff / DetJ22(J + i, Q); - } - break; - case 321: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qd[i] = qw[i] * coeff / DetJ32(J + i, Q); - } - break; - case 333: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ33(J + i, Q, &coeff, 1, 1, qw[i], Q, qd + i); - } - break; - } - return 0; -} - -// libCEED QFunction for building quadrature data for a curl-curl operator with a scalar -// coefficient evaluated at quadrature points. -CEED_QFUNCTION(f_build_curlcurl_quad_scalar)(void *ctx, CeedInt Q, - const CeedScalar *const *in, - CeedScalar *const *out) -{ - // At every quadrature point, compute qw / det(J) J^T C J and store the symmetric part of - // the result. In 2D, compute and store qw * c / det(J). - // in[0] is coefficients with shape [ncomp=1, Q] - // in[1] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[2] is quadrature weights, size (Q) - CurlCurlContext *bc = (CurlCurlContext *)ctx; - const CeedScalar *c = in[0], *J = in[1], *qw = in[2]; - CeedScalar *qd = out[0]; - switch (100 * bc->space_dim + 10 * bc->dim + bc->curl_dim) - { - case 221: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qd[i] = qw[i] * c[i] / DetJ22(J + i, Q); - } - break; - case 321: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qd[i] = qw[i] * c[i] / DetJ32(J + i, Q); - } - break; - case 333: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ33(J + i, Q, c + i, Q, 1, qw[i], Q, qd + i); - } - break; - } - return 0; -} - -// libCEED QFunction for building quadrature data for a curl-curl operator with a vector -// coefficient evaluated at quadrature points. -CEED_QFUNCTION(f_build_curlcurl_quad_vector)(void *ctx, CeedInt Q, - const CeedScalar *const *in, - CeedScalar *const *out) -{ - // At every quadrature point, compute qw / det(J) J^T C J and store the symmetric part of - // the result. In 2D, compute and store qw * c / det(J). - // in[0] is coefficients with shape [ncomp=space_dim, Q] - // in[1] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[2] is quadrature weights, size (Q) - CurlCurlContext *bc = (CurlCurlContext *)ctx; - const CeedScalar *c = in[0], *J = in[1], *qw = in[2]; - CeedScalar *qd = out[0]; - switch (100 * bc->space_dim + 10 * bc->dim + bc->curl_dim) - { - case 333: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ33(J + i, Q, c + i, Q, 3, qw[i], Q, qd + i); - } - break; - } - return 0; -} - -// libCEED QFunction for building quadrature data for a curl-curl operator -// with a matrix coefficient evaluated at quadrature points -CEED_QFUNCTION(f_build_curlcurl_quad_matrix)(void *ctx, CeedInt Q, - const CeedScalar *const *in, - CeedScalar *const *out) -{ - // At every quadrature point, compute qw / det(J) J^T C J and store the symmetric part of - // the result. In 2D, compute and store qw * c / det(J). - // in[0] is coefficients with shape [ncomp=space_dim*(space_dim+1)/2, Q] - // in[1] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[2] is quadrature weights, size (Q) - CurlCurlContext *bc = (CurlCurlContext *)ctx; - const CeedScalar *c = in[0], *J = in[1], *qw = in[2]; - CeedScalar *qd = out[0]; - switch (100 * bc->space_dim + 10 * bc->dim + bc->curl_dim) - { - case 333: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ33(J + i, Q, c + i, Q, 6, qw[i], Q, qd + i); - } - break; - } - return 0; -} - -// libCEED QFunction for applying a curl-curl operator. -CEED_QFUNCTION(f_apply_curlcurl)(void *ctx, CeedInt Q, const CeedScalar *const *in, - CeedScalar *const *out) -{ - // in[0], out[0] have shape [curl_dim, ncomp=1, Q] - CurlCurlContext *bc = (CurlCurlContext *)ctx; - const CeedScalar *uc = in[0], *qd = in[1]; - CeedScalar *vc = out[0]; - switch (10 * bc->dim + bc->curl_dim) - { - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - vc[i] = qd[i] * uc[i]; - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - const CeedScalar uc0 = uc[i + Q * 0]; - const CeedScalar uc1 = uc[i + Q * 1]; - const CeedScalar uc2 = uc[i + Q * 2]; - vc[i + Q * 0] = qd[i + Q * 0] * uc0 + qd[i + Q * 1] * uc1 + qd[i + Q * 2] * uc2; - vc[i + Q * 1] = qd[i + Q * 1] * uc0 + qd[i + Q * 3] * uc1 + qd[i + Q * 4] * uc2; - vc[i + Q * 2] = qd[i + Q * 2] * uc0 + qd[i + Q * 4] * uc1 + qd[i + Q * 5] * uc2; - } - break; - } - return 0; -} - -#endif // PALACE_LIBCEED_CURLCURL_QF_H diff --git a/palace/fem/qfunctions/curlcurlmass_qf.h b/palace/fem/qfunctions/curlcurlmass_qf.h deleted file mode 100644 index 39e409209..000000000 --- a/palace/fem/qfunctions/curlcurlmass_qf.h +++ /dev/null @@ -1,402 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -#ifndef PALACE_LIBCEED_CURLCURL_MASS_QF_H -#define PALACE_LIBCEED_CURLCURL_MASS_QF_H - -#include "utils_qf.h" - -struct CurlCurlMassContext -{ - CeedInt dim, space_dim, curl_dim; -}; - -// libCEED QFunction for building quadrature data for a curl-curl + mass operator with -// scalar coefficients evaluated at quadrature points. -CEED_QFUNCTION(f_build_curlcurl_mass_quad_scalar_scalar)(void *ctx, CeedInt Q, - const CeedScalar *const *in, - CeedScalar *const *out) -{ - // At every quadrature point, compute qw / det(J) J^T C J (3D) or qw * c / det(J) (2D) and - // qw / det(J) adj(J) C adj(J)^T and store the result. - // in[0] is curl-curl coefficients with shape [ncomp=1, Q] - // in[1] is mass coefficients with shape [ncomp=1, Q] - // in[2] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[3] is quadrature weights, size (Q) - CurlCurlMassContext *bc = (CurlCurlMassContext *)ctx; - const CeedScalar *cc = in[0], *cm = in[1], *J = in[2], *qw = in[3]; - CeedScalar *qdc = out[0], *qdm = out[0] + Q * bc->curl_dim * (bc->curl_dim + 1) / 2; - switch (100 * bc->space_dim + 10 * bc->dim + bc->curl_dim) - { - case 221: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qdc[i] = qw[i] * cc[i] / DetJ22(J + i, Q); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt22(J + i, Q, cm + i, Q, 1, qw[i], Q, qdm + i); - } - break; - case 321: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qdc[i] = qw[i] * cc[i] / DetJ32(J + i, Q); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt32(J + i, Q, cm + i, Q, 1, qw[i], Q, qdm + i); - } - break; - case 333: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ33(J + i, Q, cc + i, Q, 1, qw[i], Q, qdc + i); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt33(J + i, Q, cm + i, Q, 1, qw[i], Q, qdm + i); - } - break; - } - return 0; -} - -// libCEED QFunction for building quadrature data for a curl-curl + mass operator with -// scalar and vector coefficients evaluated at quadrature points. -CEED_QFUNCTION(f_build_curlcurl_mass_quad_scalar_vector)(void *ctx, CeedInt Q, - const CeedScalar *const *in, - CeedScalar *const *out) -{ - // At every quadrature point, compute qw / det(J) J^T C J (3D) or qw * c / det(J) (2D) and - // qw / det(J) adj(J) C adj(J)^T and store the result. - // in[0] is curl-curl coefficients with shape [ncomp=1, Q] - // in[1] is mass coefficients with shape [ncomp=space_dim, Q] - // in[2] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[3] is quadrature weights, size (Q) - CurlCurlMassContext *bc = (CurlCurlMassContext *)ctx; - const CeedScalar *cc = in[0], *cm = in[1], *J = in[2], *qw = in[3]; - CeedScalar *qdc = out[0], *qdm = out[0] + Q * bc->curl_dim * (bc->curl_dim + 1) / 2; - switch (100 * bc->space_dim + 10 * bc->dim + bc->curl_dim) - { - case 221: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qdc[i] = qw[i] * cc[i] / DetJ22(J + i, Q); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt22(J + i, Q, cm + i, Q, 2, qw[i], Q, qdm + i); - } - break; - case 321: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qdc[i] = qw[i] * cc[i] / DetJ32(J + i, Q); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt32(J + i, Q, cm + i, Q, 3, qw[i], Q, qdm + i); - } - break; - case 333: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ33(J + i, Q, cc + i, Q, 1, qw[i], Q, qdc + i); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt33(J + i, Q, cm + i, Q, 3, qw[i], Q, qdm + i); - } - break; - } - return 0; -} - -// libCEED QFunction for building quadrature data for a curl-curl + mass operator with -// scalar and matrix coefficients evaluated at quadrature points. -CEED_QFUNCTION(f_build_curlcurl_mass_quad_scalar_matrix)(void *ctx, CeedInt Q, - const CeedScalar *const *in, - CeedScalar *const *out) -{ - // At every quadrature point, compute qw / det(J) J^T C J (3D) or qw * c / det(J) (2D) and - // qw / det(J) adj(J) C adj(J)^T and store the result. - // in[0] is curl-curl coefficients with shape [ncomp=1, Q] - // in[1] is mass coefficients with shape [ncomp=space_dim*(space_dim+1)/2, Q] - // in[2] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[3] is quadrature weights, size (Q) - CurlCurlMassContext *bc = (CurlCurlMassContext *)ctx; - const CeedScalar *cc = in[0], *cm = in[1], *J = in[2], *qw = in[3]; - CeedScalar *qdc = out[0], *qdm = out[0] + Q * bc->curl_dim * (bc->curl_dim + 1) / 2; - switch (100 * bc->space_dim + 10 * bc->dim + bc->curl_dim) - { - case 221: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qdc[i] = qw[i] * cc[i] / DetJ22(J + i, Q); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt22(J + i, Q, cm + i, Q, 3, qw[i], Q, qdm + i); - } - break; - case 321: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qdc[i] = qw[i] * cc[i] / DetJ32(J + i, Q); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt32(J + i, Q, cm + i, Q, 6, qw[i], Q, qdm + i); - } - break; - case 333: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ33(J + i, Q, cc + i, Q, 1, qw[i], Q, qdc + i); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt33(J + i, Q, cm + i, Q, 6, qw[i], Q, qdm + i); - } - break; - } - return 0; -} - -// libCEED QFunction for building quadrature data for a curl-curl + mass operator with -// vector and scalar coefficients evaluated at quadrature points. -CEED_QFUNCTION(f_build_curlcurl_mass_quad_vector_scalar)(void *ctx, CeedInt Q, - const CeedScalar *const *in, - CeedScalar *const *out) -{ - // At every quadrature point, compute qw / det(J) J^T C J (3D) or qw * c / det(J) (2D) and - // qw / det(J) adj(J) C adj(J)^T and store the result. - // in[0] is curl-curl coefficients with shape [ncomp=space_dim, Q] - // in[1] is mass coefficients with shape [ncomp=1, Q] - // in[2] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[3] is quadrature weights, size (Q) - CurlCurlMassContext *bc = (CurlCurlMassContext *)ctx; - const CeedScalar *cc = in[0], *cm = in[1], *J = in[2], *qw = in[3]; - CeedScalar *qdc = out[0], *qdm = out[0] + Q * bc->curl_dim * (bc->curl_dim + 1) / 2; - switch (100 * bc->space_dim + 10 * bc->dim + bc->curl_dim) - { - case 333: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ33(J + i, Q, cc + i, Q, 3, qw[i], Q, qdc + i); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt33(J + i, Q, cm + i, Q, 1, qw[i], Q, qdm + i); - } - break; - } - return 0; -} - -// libCEED QFunction for building quadrature data for a curl-curl + mass operator with -// vector coefficients evaluated at quadrature points. -CEED_QFUNCTION(f_build_curlcurl_mass_quad_vector_vector)(void *ctx, CeedInt Q, - const CeedScalar *const *in, - CeedScalar *const *out) -{ - // At every quadrature point, compute qw / det(J) J^T C J (3D) or qw * c / det(J) (2D) and - // qw / det(J) adj(J) C adj(J)^T and store the result. - // in[0] is curl-curl coefficients with shape [ncomp=space_dim, Q] - // in[1] is mass coefficients with shape [ncomp=space_dim, Q] - // in[2] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[3] is quadrature weights, size (Q) - CurlCurlMassContext *bc = (CurlCurlMassContext *)ctx; - const CeedScalar *cc = in[0], *cm = in[1], *J = in[2], *qw = in[3]; - CeedScalar *qdc = out[0], *qdm = out[0] + Q * bc->curl_dim * (bc->curl_dim + 1) / 2; - switch (100 * bc->space_dim + 10 * bc->dim + bc->curl_dim) - { - case 333: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ33(J + i, Q, cc + i, Q, 3, qw[i], Q, qdc + i); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt33(J + i, Q, cm + i, Q, 3, qw[i], Q, qdm + i); - } - break; - } - return 0; -} - -// libCEED QFunction for building quadrature data for a curl-curl + mass operator with -// vector and matrix coefficients evaluated at quadrature points. -CEED_QFUNCTION(f_build_curlcurl_mass_quad_vector_matrix)(void *ctx, CeedInt Q, - const CeedScalar *const *in, - CeedScalar *const *out) -{ - // At every quadrature point, compute qw / det(J) J^T C J (3D) or qw * c / det(J) (2D) and - // qw / det(J) adj(J) C adj(J)^T and store the result. - // in[0] is curl-curl coefficients with shape [ncomp=space_dim, Q] - // in[1] is mass coefficients with shape [ncomp=space_dim*(space_dim+1)/2, Q] - // in[2] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[3] is quadrature weights, size (Q) - CurlCurlMassContext *bc = (CurlCurlMassContext *)ctx; - const CeedScalar *cc = in[0], *cm = in[1], *J = in[2], *qw = in[3]; - CeedScalar *qdc = out[0], *qdm = out[0] + Q * bc->curl_dim * (bc->curl_dim + 1) / 2; - switch (100 * bc->space_dim + 10 * bc->dim + bc->curl_dim) - { - case 333: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ33(J + i, Q, cc + i, Q, 3, qw[i], Q, qdc + i); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt33(J + i, Q, cm + i, Q, 6, qw[i], Q, qdm + i); - } - break; - } - return 0; -} - -// libCEED QFunction for building quadrature data for a curl-curl + mass operator with -// matrix and scalar coefficients evaluated at quadrature points. -CEED_QFUNCTION(f_build_curlcurl_mass_quad_matrix_scalar)(void *ctx, CeedInt Q, - const CeedScalar *const *in, - CeedScalar *const *out) -{ - // At every quadrature point, compute qw / det(J) J^T C J (3D) or qw * c / det(J) (2D) and - // qw / det(J) adj(J) C adj(J)^T and store the result. - // in[0] is curl-curl coefficients with shape [ncomp=space_dim*(space_dim+1)/2, Q] - // in[1] is mass coefficients with shape [ncomp=1, Q] - // in[2] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[3] is quadrature weights, size (Q) - CurlCurlMassContext *bc = (CurlCurlMassContext *)ctx; - const CeedScalar *cc = in[0], *cm = in[1], *J = in[2], *qw = in[3]; - CeedScalar *qdc = out[0], *qdm = out[0] + Q * bc->curl_dim * (bc->curl_dim + 1) / 2; - switch (100 * bc->space_dim + 10 * bc->dim + bc->curl_dim) - { - case 333: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ33(J + i, Q, cc + i, Q, 6, qw[i], Q, qdc + i); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt33(J + i, Q, cm + i, Q, 1, qw[i], Q, qdm + i); - } - break; - } - return 0; -} - -// libCEED QFunction for building quadrature data for a curl-curl + mass operator with -// matrix and vector coefficients evaluated at quadrature points. -CEED_QFUNCTION(f_build_curlcurl_mass_quad_matrix_vector)(void *ctx, CeedInt Q, - const CeedScalar *const *in, - CeedScalar *const *out) -{ - // At every quadrature point, compute qw / det(J) J^T C J (3D) or qw * c / det(J) (2D) and - // qw / det(J) adj(J) C adj(J)^T and store the result. - // in[0] is curl-curl coefficients with shape [ncomp=space_dim*(space_dim+1)/2, Q] - // in[1] is mass coefficients with shape [ncomp=space_dim, Q] - // in[2] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[3] is quadrature weights, size (Q) - CurlCurlMassContext *bc = (CurlCurlMassContext *)ctx; - const CeedScalar *cc = in[0], *cm = in[1], *J = in[2], *qw = in[3]; - CeedScalar *qdc = out[0], *qdm = out[0] + Q * bc->curl_dim * (bc->curl_dim + 1) / 2; - switch (100 * bc->space_dim + 10 * bc->dim + bc->curl_dim) - { - case 333: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ33(J + i, Q, cc + i, Q, 6, qw[i], Q, qdc + i); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt33(J + i, Q, cm + i, Q, 3, qw[i], Q, qdm + i); - } - break; - } - return 0; -} - -// libCEED QFunction for building quadrature data for a curl-curl + mass operator with -// matrix coefficients evaluated at quadrature points. -CEED_QFUNCTION(f_build_curlcurl_mass_quad_matrix_matrix)(void *ctx, CeedInt Q, - const CeedScalar *const *in, - CeedScalar *const *out) -{ - // At every quadrature point, compute qw / det(J) J^T C J (3D) or qw * c / det(J) (2D) and - // qw / det(J) adj(J) C adj(J)^T and store the result. - // in[0] is curl-curl coefficients with shape [ncomp=space_dim*(space_dim+1)/2, Q] - // in[1] is mass coefficients with shape [ncomp=space_dim*(space_dim+1)/2, Q] - // in[2] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[3] is quadrature weights, size (Q) - CurlCurlMassContext *bc = (CurlCurlMassContext *)ctx; - const CeedScalar *cc = in[0], *cm = in[1], *J = in[2], *qw = in[3]; - CeedScalar *qdc = out[0], *qdm = out[0] + Q * bc->curl_dim * (bc->curl_dim + 1) / 2; - switch (100 * bc->space_dim + 10 * bc->dim + bc->curl_dim) - { - case 333: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ33(J + i, Q, cc + i, Q, 6, qw[i], Q, qdc + i); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt33(J + i, Q, cm + i, Q, 6, qw[i], Q, qdm + i); - } - break; - } - return 0; -} - -// libCEED QFunction for applying a curl-curl + mass operator. -CEED_QFUNCTION(f_apply_curlcurl_mass)(void *ctx, CeedInt Q, const CeedScalar *const *in, - CeedScalar *const *out) -{ - // in[0], out[0] have shape [dim, ncomp=1, Q] - // in[1], out[1] have shape [curl_dim, ncomp=1, Q] - CurlCurlMassContext *bc = (CurlCurlMassContext *)ctx; - const CeedScalar *u = in[0], *uc = in[1], *qdc = in[2], - *qdm = in[2] + Q * bc->curl_dim * (bc->curl_dim + 1) / 2; - CeedScalar *v = out[0], *vc = out[1]; - switch (10 * bc->dim + bc->curl_dim) - { - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - vc[i] = qdc[i] * uc[i]; - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - const CeedScalar u0 = u[i + Q * 0]; - const CeedScalar u1 = u[i + Q * 1]; - v[i + Q * 0] = qdm[i + Q * 0] * u0 + qdm[i + Q * 1] * u1; - v[i + Q * 1] = qdm[i + Q * 1] * u0 + qdm[i + Q * 2] * u1; - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - const CeedScalar uc0 = uc[i + Q * 0]; - const CeedScalar uc1 = uc[i + Q * 1]; - const CeedScalar uc2 = uc[i + Q * 2]; - vc[i + Q * 0] = qdc[i + Q * 0] * uc0 + qdc[i + Q * 1] * uc1 + qdc[i + Q * 2] * uc2; - vc[i + Q * 1] = qdc[i + Q * 1] * uc0 + qdc[i + Q * 3] * uc1 + qdc[i + Q * 4] * uc2; - vc[i + Q * 2] = qdc[i + Q * 2] * uc0 + qdc[i + Q * 4] * uc1 + qdc[i + Q * 5] * uc2; - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - const CeedScalar u0 = u[i + Q * 0]; - const CeedScalar u1 = u[i + Q * 1]; - const CeedScalar u2 = u[i + Q * 2]; - v[i + Q * 0] = qdm[i + Q * 0] * u0 + qdm[i + Q * 1] * u1 + qdm[i + Q * 2] * u2; - v[i + Q * 1] = qdm[i + Q * 1] * u0 + qdm[i + Q * 3] * u1 + qdm[i + Q * 4] * u2; - v[i + Q * 2] = qdm[i + Q * 2] * u0 + qdm[i + Q * 4] * u1 + qdm[i + Q * 5] * u2; - } - break; - } - return 0; -} - -#endif // PALACE_LIBCEED_CURLCURL_MASS_QF_H diff --git a/palace/fem/qfunctions/diffusion_qf.h b/palace/fem/qfunctions/diffusion_qf.h deleted file mode 100644 index 45e0f30d6..000000000 --- a/palace/fem/qfunctions/diffusion_qf.h +++ /dev/null @@ -1,239 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -#ifndef PALACE_LIBCEED_DIFFUSION_QF_H -#define PALACE_LIBCEED_DIFFUSION_QF_H - -#include "utils_qf.h" - -struct DiffusionContext -{ - CeedInt dim, space_dim; - CeedScalar coeff; -}; - -// libCEED QFunction for building quadrature data for a diffusion operator with a scalar -// constant coefficient. -CEED_QFUNCTION(f_build_diff_const_scalar)(void *ctx, CeedInt Q, const CeedScalar *const *in, - CeedScalar *const *out) -{ - // At every quadrature point, compute qw / det(J) adj(J) C adj(J)^T and store the - // symmetric part of the result. - // in[0] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[1] is quadrature weights, size (Q) - DiffusionContext *bc = (DiffusionContext *)ctx; - const CeedScalar coeff = bc->coeff; - const CeedScalar *J = in[0], *qw = in[1]; - CeedScalar *qd = out[0]; - switch (10 * bc->space_dim + bc->dim) - { - case 11: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qd[i] = qw[i] * coeff / J[i]; - } - break; - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt21(J + i, Q, &coeff, 1, 1, qw[i], Q, qd + i); - } - break; - case 22: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt22(J + i, Q, &coeff, 1, 1, qw[i], Q, qd + i); - } - break; - case 32: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt32(J + i, Q, &coeff, 1, 1, qw[i], Q, qd + i); - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt33(J + i, Q, &coeff, 1, 1, qw[i], Q, qd + i); - } - break; - } - return 0; -} - -// libCEED QFunction for building quadrature data for a diffusion operator -// with a scalar coefficient evaluated at quadrature points -CEED_QFUNCTION(f_build_diff_quad_scalar)(void *ctx, CeedInt Q, const CeedScalar *const *in, - CeedScalar *const *out) -{ - // At every quadrature point, compute qw / det(J) adj(J) C adj(J)^T and store the - // symmetric part of the result. - // in[0] is coefficients with shape [ncomp=1, Q] - // in[1] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[2] is quadrature weights, size (Q) - DiffusionContext *bc = (DiffusionContext *)ctx; - const CeedScalar *c = in[0], *J = in[1], *qw = in[2]; - CeedScalar *qd = out[0]; - switch (10 * bc->space_dim + bc->dim) - { - case 11: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qd[i] = qw[i] * c[i] / J[i]; - } - break; - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt21(J + i, Q, c + i, Q, 1, qw[i], Q, qd + i); - } - break; - case 22: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt22(J + i, Q, c + i, Q, 1, qw[i], Q, qd + i); - } - break; - case 32: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt32(J + i, Q, c + i, Q, 1, qw[i], Q, qd + i); - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt33(J + i, Q, c + i, Q, 1, qw[i], Q, qd + i); - } - break; - } - return 0; -} - -// libCEED QFunction for building quadrature data for a diffusion operator with a vector -// coefficient evaluated at quadrature points. -CEED_QFUNCTION(f_build_diff_quad_vector)(void *ctx, CeedInt Q, const CeedScalar *const *in, - CeedScalar *const *out) -{ - // At every quadrature point, compute qw / det(J) adj(J) C adj(J)^T and store the - // symmetric part of the result. - // in[0] is coefficients with shape [ncomp=space_dim, Q] - // in[1] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[2] is quadrature weights, size (Q) - DiffusionContext *bc = (DiffusionContext *)ctx; - const CeedScalar *c = in[0], *J = in[1], *qw = in[2]; - CeedScalar *qd = out[0]; - switch (10 * bc->space_dim + bc->dim) - { - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt21(J + i, Q, c + i, Q, 2, qw[i], Q, qd + i); - } - break; - case 22: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt22(J + i, Q, c + i, Q, 2, qw[i], Q, qd + i); - } - break; - case 32: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt32(J + i, Q, c + i, Q, 3, qw[i], Q, qd + i); - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt33(J + i, Q, c + i, Q, 3, qw[i], Q, qd + i); - } - break; - } - return 0; -} - -// libCEED QFunction for building quadrature data for a diffusion operator with a matrix -// coefficient evaluated at quadrature points. -CEED_QFUNCTION(f_build_diff_quad_matrix)(void *ctx, CeedInt Q, const CeedScalar *const *in, - CeedScalar *const *out) -{ - // At every quadrature point, compute qw / det(J) adj(J) C adj(J)^T and store the - // symmetric part of the result. - // in[0] is coefficients with shape [ncomp=space_dim*(space_dim+1)/2, Q] - // in[1] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[2] is quadrature weights, size (Q) - DiffusionContext *bc = (DiffusionContext *)ctx; - const CeedScalar *c = in[0], *J = in[1], *qw = in[2]; - CeedScalar *qd = out[0]; - switch (10 * bc->space_dim + bc->dim) - { - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt21(J + i, Q, c + i, Q, 3, qw[i], Q, qd + i); - } - break; - case 22: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt22(J + i, Q, c + i, Q, 3, qw[i], Q, qd + i); - } - break; - case 32: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt32(J + i, Q, c + i, Q, 6, qw[i], Q, qd + i); - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt33(J + i, Q, c + i, Q, 6, qw[i], Q, qd + i); - } - break; - } - return 0; -} - -// libCEED QFunction for applying a diffusion operator. -CEED_QFUNCTION(f_apply_diff)(void *ctx, CeedInt Q, const CeedScalar *const *in, - CeedScalar *const *out) -{ - // in[0], out[0] have shape [dim, ncomp=1, Q] - DiffusionContext *bc = (DiffusionContext *)ctx; - const CeedScalar *ug = in[0], *qd = in[1]; - CeedScalar *vg = out[0]; - switch (bc->dim) - { - case 1: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - vg[i] = qd[i] * ug[i]; - } - break; - case 2: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - const CeedScalar ug0 = ug[i + Q * 0]; - const CeedScalar ug1 = ug[i + Q * 1]; - vg[i + Q * 0] = qd[i + Q * 0] * ug0 + qd[i + Q * 1] * ug1; - vg[i + Q * 1] = qd[i + Q * 1] * ug0 + qd[i + Q * 2] * ug1; - } - break; - case 3: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - const CeedScalar ug0 = ug[i + Q * 0]; - const CeedScalar ug1 = ug[i + Q * 1]; - const CeedScalar ug2 = ug[i + Q * 2]; - vg[i + Q * 0] = qd[i + Q * 0] * ug0 + qd[i + Q * 1] * ug1 + qd[i + Q * 2] * ug2; - vg[i + Q * 1] = qd[i + Q * 1] * ug0 + qd[i + Q * 3] * ug1 + qd[i + Q * 4] * ug2; - vg[i + Q * 2] = qd[i + Q * 2] * ug0 + qd[i + Q * 4] * ug1 + qd[i + Q * 5] * ug2; - } - break; - } - return 0; -} - -#endif // PALACE_LIBCEED_DIFFUSION_QF_H diff --git a/palace/fem/qfunctions/diffusionmass_qf.h b/palace/fem/qfunctions/diffusionmass_qf.h deleted file mode 100644 index caead2bd1..000000000 --- a/palace/fem/qfunctions/diffusionmass_qf.h +++ /dev/null @@ -1,253 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -#ifndef PALACE_LIBCEED_DIFFUSION_MASS_QF_H -#define PALACE_LIBCEED_DIFFUSION_MASS_QF_H - -#include "utils_qf.h" - -struct DiffusionMassContext -{ - CeedInt dim, space_dim; -}; - -// libCEED QFunction for building quadrature data for a diffusion + mass operator with a -// scalar coefficient evaluated at quadrature points. -CEED_QFUNCTION(f_build_diff_mass_quad_scalar)(void *ctx, CeedInt Q, - const CeedScalar *const *in, - CeedScalar *const *out) -{ - // At every quadrature point, compute qw / det(J) adj(J) C adj(J)^T and qw * c * det(J) - // and store the result. - // in[0] is diffusion coefficients with shape [ncomp=1, Q] - // in[1] is mass coefficients with shape [ncomp=1, Q] - // in[2] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[3] is quadrature weights, size (Q) - DiffusionMassContext *bc = (DiffusionMassContext *)ctx; - const CeedScalar *cd = in[0], *cm = in[1], *J = in[2], *qw = in[3]; - CeedScalar *qdd = out[0], *qdm = out[0] + Q * bc->dim * (bc->dim + 1) / 2; - switch (10 * bc->space_dim + bc->dim) - { - case 11: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qdd[i] = qw[i] * cd[i] / J[i]; - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qdm[i] = qw[i] * cm[i] * J[i]; - } - break; - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt21(J + i, Q, cd + i, Q, 1, qw[i], Q, qdd + i); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qdm[i] = qw[i] * cm[i] * DetJ21(J + i, Q); - } - break; - case 22: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt22(J + i, Q, cd + i, Q, 1, qw[i], Q, qdd + i); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qdm[i] = qw[i] * cm[i] * DetJ22(J + i, Q); - } - break; - case 32: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt32(J + i, Q, cd + i, Q, 1, qw[i], Q, qdd + i); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qdm[i] = qw[i] * cm[i] * DetJ32(J + i, Q); - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt33(J + i, Q, cd + i, Q, 1, qw[i], Q, qdd + i); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qdm[i] = qw[i] * cm[i] * DetJ33(J + i, Q); - } - break; - } - return 0; -} - -// libCEED QFunction for building quadrature data for a diffusion + mass operator with a -// vector coefficient evaluated at quadrature points. -CEED_QFUNCTION(f_build_diff_mass_quad_vector)(void *ctx, CeedInt Q, - const CeedScalar *const *in, - CeedScalar *const *out) -{ - // At every quadrature point, compute qw / det(J) adj(J) C adj(J)^T and qw * c * det(J) - // and store the result. - // in[0] is diffusion coefficients with shape [ncomp=space_dim, Q] - // in[1] is mass coefficients with shape [ncomp=1, Q] - // in[2] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[3] is quadrature weights, size (Q) - DiffusionMassContext *bc = (DiffusionMassContext *)ctx; - const CeedScalar *cd = in[0], *cm = in[1], *J = in[2], *qw = in[3]; - CeedScalar *qdd = out[0], *qdm = out[0] + Q * bc->dim * (bc->dim + 1) / 2; - switch (10 * bc->space_dim + bc->dim) - { - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt21(J + i, Q, cd + i, Q, 2, qw[i], Q, qdd + i); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qdm[i] = qw[i] * cm[i] * DetJ21(J + i, Q); - } - break; - case 22: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt22(J + i, Q, cd + i, Q, 2, qw[i], Q, qdd + i); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qdm[i] = qw[i] * cm[i] * DetJ22(J + i, Q); - } - break; - case 32: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt32(J + i, Q, cd + i, Q, 3, qw[i], Q, qdd + i); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qdm[i] = qw[i] * cm[i] * DetJ32(J + i, Q); - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt33(J + i, Q, cd + i, Q, 3, qw[i], Q, qdd + i); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qdm[i] = qw[i] * cm[i] * DetJ33(J + i, Q); - } - break; - } - return 0; -} - -// libCEED QFunction for building quadrature data for a diffusion + mass operator with a -// matrix coefficient evaluated at quadrature points. -CEED_QFUNCTION(f_build_diff_mass_quad_matrix)(void *ctx, CeedInt Q, - const CeedScalar *const *in, - CeedScalar *const *out) -{ - // At every quadrature point, compute qw / det(J) adj(J) C adj(J)^T and qw * c * det(J) - // and store the result. - // in[0] is diffusion coefficients with shape [ncomp=space_dim*(space_dim+1)/2, Q] - // in[1] is mass coefficients with shape [ncomp=1, Q] - // in[2] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[3] is quadrature weights, size (Q) - DiffusionMassContext *bc = (DiffusionMassContext *)ctx; - const CeedScalar *cd = in[0], *cm = in[1], *J = in[2], *qw = in[3]; - CeedScalar *qdd = out[0], *qdm = out[0] + Q * bc->dim * (bc->dim + 1) / 2; - switch (10 * bc->space_dim + bc->dim) - { - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt21(J + i, Q, cd + i, Q, 3, qw[i], Q, qdd + i); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qdm[i] = qw[i] * cm[i] * DetJ21(J + i, Q); - } - break; - case 22: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt22(J + i, Q, cd + i, Q, 3, qw[i], Q, qdd + i); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qdm[i] = qw[i] * cm[i] * DetJ22(J + i, Q); - } - break; - case 32: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt32(J + i, Q, cd + i, Q, 6, qw[i], Q, qdd + i); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qdm[i] = qw[i] * cm[i] * DetJ32(J + i, Q); - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt33(J + i, Q, cd + i, Q, 6, qw[i], Q, qdd + i); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qdm[i] = qw[i] * cm[i] * DetJ33(J + i, Q); - } - break; - } - return 0; -} - -// libCEED QFunction for applying a diffusion + mass operator. -CEED_QFUNCTION(f_apply_diff_mass)(void *ctx, CeedInt Q, const CeedScalar *const *in, - CeedScalar *const *out) -{ - // in[0], out[0] have shape [ncomp=1, Q] - // in[1], out[1] have shape [dim, ncomp=1, Q] - DiffusionMassContext *bc = (DiffusionMassContext *)ctx; - const CeedScalar *u = in[0], *ug = in[1], *qdd = in[2], - *qdm = in[2] + Q * bc->dim * (bc->dim + 1) / 2; - CeedScalar *v = out[0], *vg = out[1]; - switch (bc->dim) - { - case 1: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - vg[i] = qdd[i] * ug[i]; - } - break; - case 2: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - const CeedScalar ug0 = ug[i + Q * 0]; - const CeedScalar ug1 = ug[i + Q * 1]; - vg[i + Q * 0] = qdd[i + Q * 0] * ug0 + qdd[i + Q * 1] * ug1; - vg[i + Q * 1] = qdd[i + Q * 1] * ug0 + qdd[i + Q * 2] * ug1; - } - break; - case 3: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - const CeedScalar ug0 = ug[i + Q * 0]; - const CeedScalar ug1 = ug[i + Q * 1]; - const CeedScalar ug2 = ug[i + Q * 2]; - vg[i + Q * 0] = qdd[i + Q * 0] * ug0 + qdd[i + Q * 1] * ug1 + qdd[i + Q * 2] * ug2; - vg[i + Q * 1] = qdd[i + Q * 1] * ug0 + qdd[i + Q * 3] * ug1 + qdd[i + Q * 4] * ug2; - vg[i + Q * 2] = qdd[i + Q * 2] * ug0 + qdd[i + Q * 4] * ug1 + qdd[i + Q * 5] * ug2; - } - break; - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - v[i] = qdm[i] * u[i]; - } - return 0; -} - -#endif // PALACE_LIBCEED_DIFFUSION_MASS_QF_H diff --git a/palace/fem/qfunctions/divdiv_qf.h b/palace/fem/qfunctions/divdiv_qf.h deleted file mode 100644 index f95bc2782..000000000 --- a/palace/fem/qfunctions/divdiv_qf.h +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -#ifndef PALACE_LIBCEED_DIVDIV_QF_H -#define PALACE_LIBCEED_DIVDIV_QF_H - -#include "utils_qf.h" - -struct DivDivContext -{ - CeedInt dim, space_dim; - CeedScalar coeff; -}; - -// libCEED QFunction for building quadrature data for a div-div operator with a constant -// coefficient. -CEED_QFUNCTION(f_build_divdiv_const)(void *ctx, CeedInt Q, const CeedScalar *const *in, - CeedScalar *const *out) -{ - // At every quadrature point, compute and store qw * c / det(J). - // in[0] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[1] is quadrature weights, size (Q) - DivDivContext *bc = (DivDivContext *)ctx; - const CeedScalar coeff = bc->coeff; - const CeedScalar *J = in[0], *qw = in[1]; - CeedScalar *qd = out[0]; - switch (10 * bc->space_dim + bc->dim) - { - case 11: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qd[i] = qw[i] * coeff / J[i]; - } - break; - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qd[i] = qw[i] * coeff / DetJ21(J + i, Q); - } - break; - case 22: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qd[i] = qw[i] * coeff / DetJ22(J + i, Q); - } - break; - case 32: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qd[i] = qw[i] * coeff / DetJ32(J + i, Q); - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qd[i] = qw[i] * coeff / DetJ33(J + i, Q); - } - break; - } - return 0; -} - -// libCEED QFunction for building quadrature data for a div-div operator with a coefficient -// evaluated at quadrature points. -CEED_QFUNCTION(f_build_divdiv_quad)(void *ctx, CeedInt Q, const CeedScalar *const *in, - CeedScalar *const *out) -{ - // At every quadrature point, compute and store qw * c / det(J). - // in[0] is coefficients, size (Q) - // in[1] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[2] is quadrature weights, size (Q) - DivDivContext *bc = (DivDivContext *)ctx; - const CeedScalar *c = in[0], *J = in[1], *qw = in[2]; - CeedScalar *qd = out[0]; - switch (10 * bc->space_dim + bc->dim) - { - case 11: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qd[i] = qw[i] * c[i] / J[i]; - } - break; - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qd[i] = qw[i] * c[i] / DetJ21(J + i, Q); - } - break; - case 22: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qd[i] = qw[i] * c[i] / DetJ22(J + i, Q); - } - break; - case 32: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qd[i] = qw[i] * c[i] / DetJ32(J + i, Q); - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qd[i] = qw[i] * c[i] / DetJ33(J + i, Q); - } - break; - } - return 0; -} - -// libCEED QFunction for applying a div-div operator. -CEED_QFUNCTION(f_apply_divdiv)(void *ctx, CeedInt Q, const CeedScalar *const *in, - CeedScalar *const *out) -{ - // in[0], out[0] have shape [ncomp=1, Q] - const CeedScalar *ud = in[0], *qd = in[1]; - CeedScalar *vd = out[0]; - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - vd[i] = qd[i] * ud[i]; - } - return 0; -} - -#endif // PALACE_LIBCEED_DIVDIV_QF_H diff --git a/palace/fem/qfunctions/divdivmass_qf.h b/palace/fem/qfunctions/divdivmass_qf.h deleted file mode 100644 index 30e025fa7..000000000 --- a/palace/fem/qfunctions/divdivmass_qf.h +++ /dev/null @@ -1,252 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -#ifndef PALACE_LIBCEED_DIVDIV_MASS_QF_H -#define PALACE_LIBCEED_DIVDIV_MASS_QF_H - -#include "utils_qf.h" - -struct DivDivMassContext -{ - CeedInt dim, space_dim; -}; - -// libCEED QFunction for building quadrature data for a div-div + mass operator with a -// scalar coefficient evaluated at quadrature points. -CEED_QFUNCTION(f_build_divdiv_mass_quad_scalar)(void *ctx, CeedInt Q, - const CeedScalar *const *in, - CeedScalar *const *out) -{ - // At every quadrature point, compute qw * c / det(J) and qw / det(J) J^T C J and store - // the result. - // in[0] is div-div coefficients with shape [ncomp=1, Q] - // in[1] is mass coefficients with shape [ncomp=1, Q] - // in[2] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[3] is quadrature weights, size (Q) - DivDivMassContext *bc = (DivDivMassContext *)ctx; - const CeedScalar *cd = in[0], *cm = in[1], *J = in[2], *qw = in[3]; - CeedScalar *qdd = out[0], *qdm = out[0] + Q; - switch (10 * bc->space_dim + bc->dim) - { - case 11: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qdd[i] = qw[i] * cd[i] / J[i]; - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qdm[i] = qw[i] * cm[i] * J[i]; - } - break; - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qdd[i] = qw[i] * cd[i] / DetJ21(J + i, Q); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ21(J + i, Q, cm + i, Q, 1, qw[i], Q, qdm + i); - } - break; - case 22: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qdd[i] = qw[i] * cd[i] / DetJ22(J + i, Q); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ22(J + i, Q, cm + i, Q, 1, qw[i], Q, qdm + i); - } - break; - case 32: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qdd[i] = qw[i] * cd[i] / DetJ32(J + i, Q); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ32(J + i, Q, cm + i, Q, 1, qw[i], Q, qdm + i); - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qdd[i] = qw[i] * cd[i] / DetJ33(J + i, Q); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ33(J + i, Q, cm + i, Q, 1, qw[i], Q, qdm + i); - } - break; - } - return 0; -} - -// libCEED QFunction for building quadrature data for a div-div + mass operator with a -// vector coefficient evaluated at quadrature points. -CEED_QFUNCTION(f_build_divdiv_mass_quad_vector)(void *ctx, CeedInt Q, - const CeedScalar *const *in, - CeedScalar *const *out) -{ - // At every quadrature point, compute qw * c / det(J) and qw / det(J) J^T C J and store - // the result. - // in[0] is div-div coefficients with shape [ncomp=1, Q] - // in[1] is mass coefficients with shape [ncomp=space_dim, Q] - // in[2] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[3] is quadrature weights, size (Q) - DivDivMassContext *bc = (DivDivMassContext *)ctx; - const CeedScalar *cd = in[0], *cm = in[1], *J = in[2], *qw = in[3]; - CeedScalar *qdd = out[0], *qdm = out[0] + Q; - switch (10 * bc->space_dim + bc->dim) - { - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qdd[i] = qw[i] * cd[i] / DetJ21(J + i, Q); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ21(J + i, Q, cm + i, Q, 2, qw[i], Q, qdm + i); - } - break; - case 22: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qdd[i] = qw[i] * cd[i] / DetJ22(J + i, Q); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ22(J + i, Q, cm + i, Q, 2, qw[i], Q, qdm + i); - } - break; - case 32: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qdd[i] = qw[i] * cd[i] / DetJ32(J + i, Q); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ32(J + i, Q, cm + i, Q, 3, qw[i], Q, qdm + i); - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qdd[i] = qw[i] * cd[i] / DetJ33(J + i, Q); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ33(J + i, Q, cm + i, Q, 3, qw[i], Q, qdm + i); - } - break; - } - return 0; -} - -// libCEED QFunction for building quadrature data for a div-div + mass operator with a -// matrix coefficient evaluated at quadrature points. -CEED_QFUNCTION(f_build_divdiv_mass_quad_matrix)(void *ctx, CeedInt Q, - const CeedScalar *const *in, - CeedScalar *const *out) -{ - // At every quadrature point, compute qw * c / det(J) and qw / det(J) J^T C J and store - // the result. - // in[0] is div-div coefficients with shape [ncomp=1, Q] - // in[1] is mass coefficients with shape [ncomp=space_dim*(space_dim+1)/2, Q] - // in[2] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[3] is quadrature weights, size (Q) - DivDivMassContext *bc = (DivDivMassContext *)ctx; - const CeedScalar *cd = in[0], *cm = in[1], *J = in[2], *qw = in[3]; - CeedScalar *qdd = out[0], *qdm = out[0] + Q; - switch (10 * bc->space_dim + bc->dim) - { - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qdd[i] = qw[i] * cd[i] / DetJ21(J + i, Q); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ21(J + i, Q, cm + i, Q, 3, qw[i], Q, qdm + i); - } - break; - case 22: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qdd[i] = qw[i] * cd[i] / DetJ22(J + i, Q); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ22(J + i, Q, cm + i, Q, 3, qw[i], Q, qdm + i); - } - break; - case 32: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qdd[i] = qw[i] * cd[i] / DetJ32(J + i, Q); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ32(J + i, Q, cm + i, Q, 6, qw[i], Q, qdm + i); - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qdd[i] = qw[i] * cd[i] / DetJ33(J + i, Q); - } - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ33(J + i, Q, cm + i, Q, 6, qw[i], Q, qdm + i); - } - break; - } - return 0; -} - -// libCEED QFunction for applying a div-div + mass operator. -CEED_QFUNCTION(f_apply_divdiv_mass)(void *ctx, CeedInt Q, const CeedScalar *const *in, - CeedScalar *const *out) -{ - // in[0], out[0] have shape [dim, ncomp=1, Q] - // in[1], out[1] have shape [ncomp=1, Q] - DivDivMassContext *bc = (DivDivMassContext *)ctx; - const CeedScalar *u = in[0], *ud = in[1], *qdd = in[2], *qdm = in[2] + Q; - CeedScalar *v = out[0], *vd = out[1]; - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - vd[i] = qdd[i] * ud[i]; - } - switch (bc->dim) - { - case 1: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - v[i] = qdm[i] * u[i]; - } - break; - case 2: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - const CeedScalar u0 = u[i + Q * 0]; - const CeedScalar u1 = u[i + Q * 1]; - v[i + Q * 0] = qdm[i + Q * 0] * u0 + qdm[i + Q * 1] * u1; - v[i + Q * 1] = qdm[i + Q * 1] * u0 + qdm[i + Q * 2] * u1; - } - break; - case 3: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - const CeedScalar u0 = u[i + Q * 0]; - const CeedScalar u1 = u[i + Q * 1]; - const CeedScalar u2 = u[i + Q * 2]; - v[i + Q * 0] = qdm[i + Q * 0] * u0 + qdm[i + Q * 1] * u1 + qdm[i + Q * 2] * u2; - v[i + Q * 1] = qdm[i + Q * 1] * u0 + qdm[i + Q * 3] * u1 + qdm[i + Q * 4] * u2; - v[i + Q * 2] = qdm[i + Q * 2] * u0 + qdm[i + Q * 4] * u1 + qdm[i + Q * 5] * u2; - } - break; - } - return 0; -} - -#endif // MFEM_LIBCEED_DIVDIV_MASS_QF_H diff --git a/palace/fem/qfunctions/geom_qf.h b/palace/fem/qfunctions/geom_qf.h new file mode 100644 index 000000000..99aab18bd --- /dev/null +++ b/palace/fem/qfunctions/geom_qf.h @@ -0,0 +1,106 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef PALACE_LIBCEED_GEOM_QF_H +#define PALACE_LIBCEED_GEOM_QF_H + +#include "utils_geom_qf.h" +#include "utils_qf.h" + +// libCEED QFunction for building geometry factors for integration and transformations. +// At every quadrature point, compute qw * det(J) and adj(J)^T / |J| and store the result. +// in[0] is quadrature weights, shape [Q] +// in[1] is Jacobians, shape [qcomp=dim, ncomp=space_dim, Q] +// out[0] is quadrature data, stored as {attribute, Jacobian determinant, (transpose) +// adjugate Jacobian} quadrature data, shape [ncomp=2+space_dim*dim, Q] + +CEED_QFUNCTION(f_build_geom_factor_22)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *qw = in[0], *J = in[1]; + CeedScalar *wdetJ = out[0] + Q, *adjJt = out[0] + 2 * Q; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + CeedScalar J_loc[4], adjJt_loc[4]; + MatUnpack22(J + i, Q, J_loc); + const CeedScalar detJ = AdjJt22(J_loc, adjJt_loc); + + wdetJ[i] = qw[i] * detJ; + adjJt[i + Q * 0] = adjJt_loc[0] / detJ; + adjJt[i + Q * 1] = adjJt_loc[1] / detJ; + adjJt[i + Q * 2] = adjJt_loc[2] / detJ; + adjJt[i + Q * 3] = adjJt_loc[3] / detJ; + } + return 0; +} + +CEED_QFUNCTION(f_build_geom_factor_33)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *qw = in[0], *J = in[1]; + CeedScalar *wdetJ = out[0] + Q, *adjJt = out[0] + 2 * Q; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + CeedScalar J_loc[9], adjJt_loc[9]; + MatUnpack33(J + i, Q, J_loc); + const CeedScalar detJ = AdjJt33(J_loc, adjJt_loc); + + wdetJ[i] = qw[i] * detJ; + adjJt[i + Q * 0] = adjJt_loc[0] / detJ; + adjJt[i + Q * 1] = adjJt_loc[1] / detJ; + adjJt[i + Q * 2] = adjJt_loc[2] / detJ; + adjJt[i + Q * 3] = adjJt_loc[3] / detJ; + adjJt[i + Q * 4] = adjJt_loc[4] / detJ; + adjJt[i + Q * 5] = adjJt_loc[5] / detJ; + adjJt[i + Q * 6] = adjJt_loc[6] / detJ; + adjJt[i + Q * 7] = adjJt_loc[7] / detJ; + adjJt[i + Q * 8] = adjJt_loc[8] / detJ; + } + return 0; +} + +CEED_QFUNCTION(f_build_geom_factor_21)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *qw = in[0], *J = in[1]; + CeedScalar *wdetJ = out[0] + Q, *adjJt = out[0] + 2 * Q; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + CeedScalar J_loc[2], adjJt_loc[2]; + MatUnpack21(J + i, Q, J_loc); + const CeedScalar detJ = AdjJt21(J_loc, adjJt_loc); + + wdetJ[i] = qw[i] * detJ; + adjJt[i + Q * 0] = adjJt_loc[0] / detJ; + adjJt[i + Q * 1] = adjJt_loc[1] / detJ; + } + return 0; +} + +CEED_QFUNCTION(f_build_geom_factor_32)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *qw = in[0], *J = in[1]; + CeedScalar *wdetJ = out[0] + Q, *adjJt = out[0] + 2 * Q; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + CeedScalar J_loc[6], adjJt_loc[6]; + MatUnpack32(J + i, Q, J_loc); + const CeedScalar detJ = AdjJt32(J_loc, adjJt_loc); + + wdetJ[i] = qw[i] * detJ; + adjJt[i + Q * 0] = adjJt_loc[0] / detJ; + adjJt[i + Q * 1] = adjJt_loc[1] / detJ; + adjJt[i + Q * 2] = adjJt_loc[2] / detJ; + adjJt[i + Q * 3] = adjJt_loc[3] / detJ; + adjJt[i + Q * 4] = adjJt_loc[4] / detJ; + adjJt[i + Q * 5] = adjJt_loc[5] / detJ; + } + return 0; +} + +#endif // PALACE_LIBCEED_GEOM_QF_H diff --git a/palace/fem/qfunctions/grad_qf.h b/palace/fem/qfunctions/grad_qf.h deleted file mode 100644 index 5a0dfab91..000000000 --- a/palace/fem/qfunctions/grad_qf.h +++ /dev/null @@ -1,254 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -#ifndef PALACE_LIBCEED_GRAD_QF_H -#define PALACE_LIBCEED_GRAD_QF_H - -#include "utils_qf.h" - -struct GradContext -{ - CeedInt dim, space_dim; - CeedScalar coeff; -}; - -// libCEED QFunction for building quadrature data for a gradient operator with a scalar -// constant coefficient. -CEED_QFUNCTION(f_build_grad_const_scalar)(void *ctx, CeedInt Q, const CeedScalar *const *in, - CeedScalar *const *out) -{ - // At every quadrature point, compute qw C adj(J)^T and store the result. - // in[0] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[1] is quadrature weights, size (Q) - GradContext *bc = (GradContext *)ctx; - const CeedScalar coeff = bc->coeff; - const CeedScalar *J = in[0], *qw = in[1]; - CeedScalar *qd = out[0]; - switch (10 * bc->space_dim + bc->dim) - { - case 11: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qd[i] = qw[i] * coeff; - } - break; - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultCAdjJt21(J + i, Q, &coeff, 1, 1, qw[i], Q, qd + i); - } - break; - case 22: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultCAdjJt22(J + i, Q, &coeff, 1, 1, qw[i], Q, qd + i); - } - break; - case 32: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultCAdjJt32(J + i, Q, &coeff, 1, 1, qw[i], Q, qd + i); - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultCAdjJt33(J + i, Q, &coeff, 1, 1, qw[i], Q, qd + i); - } - break; - } - return 0; -} - -// libCEED QFunction for building quadrature data for a gradient operator with a scalar -// coefficient evaluated at quadrature points. -CEED_QFUNCTION(f_build_grad_quad_scalar)(void *ctx, CeedInt Q, const CeedScalar *const *in, - CeedScalar *const *out) -{ - // At every quadrature point, compute qw C adj(J)^T and store the result. - // in[0] is coefficients, size (Q) - // in[1] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[2] is quadrature weights, size (Q) - GradContext *bc = (GradContext *)ctx; - const CeedScalar *c = in[0], *J = in[1], *qw = in[2]; - CeedScalar *qd = out[0]; - switch (10 * bc->space_dim + bc->dim) - { - case 11: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qd[i] = qw[i] * c[i]; - } - break; - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultCAdjJt21(J + i, Q, c + i, Q, 1, qw[i], Q, qd + i); - } - break; - case 22: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultCAdjJt22(J + i, Q, c + i, Q, 1, qw[i], Q, qd + i); - } - break; - case 32: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultCAdjJt32(J + i, Q, c + i, Q, 1, qw[i], Q, qd + i); - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultCAdjJt33(J + i, Q, c + i, Q, 1, qw[i], Q, qd + i); - } - break; - } - return 0; -} - -// libCEED QFunction for building quadrature data for a gradient operator with a vector -// coefficient evaluated at quadrature points. -CEED_QFUNCTION(f_build_grad_quad_vector)(void *ctx, CeedInt Q, const CeedScalar *const *in, - CeedScalar *const *out) -{ - // At every quadrature point, compute qw C adj(J)^T and store the result. - // in[0] is coefficients with shape [ncomp=vdim, Q] - // in[1] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[2] is quadrature weights, size (Q) - GradContext *bc = (GradContext *)ctx; - const CeedScalar *c = in[0], *J = in[1], *qw = in[2]; - CeedScalar *qd = out[0]; - switch (10 * bc->space_dim + bc->dim) - { - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultCAdjJt21(J + i, Q, c + i, Q, 2, qw[i], Q, qd + i); - } - break; - case 22: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultCAdjJt22(J + i, Q, c + i, Q, 2, qw[i], Q, qd + i); - } - break; - case 32: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultCAdjJt32(J + i, Q, c + i, Q, 3, qw[i], Q, qd + i); - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultCAdjJt33(J + i, Q, c + i, Q, 3, qw[i], Q, qd + i); - } - break; - } - return 0; -} - -// libCEED QFunction for building quadrature data for a gradient operator with a matrix -// coefficient evaluated at quadrature points. -CEED_QFUNCTION(f_build_grad_quad_matrix)(void *ctx, CeedInt Q, const CeedScalar *const *in, - CeedScalar *const *out) -{ - // At every quadrature point, compute qw C adj(J)^T and store the result. - // in[0] is coefficients with shape [ncomp=vdim*(vdim+1)/2, Q] - // in[1] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[2] is quadrature weights, size (Q) - GradContext *bc = (GradContext *)ctx; - const CeedScalar *c = in[0], *J = in[1], *qw = in[2]; - CeedScalar *qd = out[0]; - switch (10 * bc->space_dim + bc->dim) - { - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultCAdjJt21(J + i, Q, c + i, Q, 3, qw[i], Q, qd + i); - } - break; - case 22: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultCAdjJt22(J + i, Q, c + i, Q, 3, qw[i], Q, qd + i); - } - break; - case 32: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultCAdjJt32(J + i, Q, c + i, Q, 6, qw[i], Q, qd + i); - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultCAdjJt33(J + i, Q, c + i, Q, 6, qw[i], Q, qd + i); - } - break; - } - return 0; -} - -// libCEED QFunction for applying a gradient operator. -CEED_QFUNCTION(f_apply_grad)(void *ctx, CeedInt Q, const CeedScalar *const *in, - CeedScalar *const *out) -{ - // in[0] has shape [dim, ncomp=1, Q] - // out[0] has shape [ncomp=space_dim, Q] - GradContext *bc = (GradContext *)ctx; - const CeedScalar *ug = in[0], *qd = in[1]; - CeedScalar *v = out[0]; - switch (10 * bc->space_dim + bc->dim) - { - case 11: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - v[i] = qd[i] * ug[i]; - } - break; - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - const CeedScalar ug0 = ug[i + Q * 0]; - v[i + Q * 0] = qd[i + Q * 0] * ug0; - v[i + Q * 1] = qd[i + Q * 1] * ug0; - } - break; - case 22: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - const CeedScalar ug0 = ug[i + Q * 0]; - const CeedScalar ug1 = ug[i + Q * 1]; - v[i + Q * 0] = qd[i + Q * 0] * ug0 + qd[i + Q * 2] * ug1; - v[i + Q * 1] = qd[i + Q * 1] * ug0 + qd[i + Q * 3] * ug1; - } - break; - case 32: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - const CeedScalar ug0 = ug[i + Q * 0]; - const CeedScalar ug1 = ug[i + Q * 1]; - v[i + Q * 0] = qd[i + Q * 0] * ug0 + qd[i + Q * 3] * ug1; - v[i + Q * 1] = qd[i + Q * 1] * ug0 + qd[i + Q * 4] * ug1; - v[i + Q * 2] = qd[i + Q * 2] * ug0 + qd[i + Q * 5] * ug1; - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - const CeedScalar ug0 = ug[i + Q * 0]; - const CeedScalar ug1 = ug[i + Q * 1]; - const CeedScalar ug2 = ug[i + Q * 2]; - v[i + Q * 0] = qd[i + Q * 0] * ug0 + qd[i + Q * 3] * ug1 + qd[i + Q * 6] * ug2; - v[i + Q * 1] = qd[i + Q * 1] * ug0 + qd[i + Q * 4] * ug1 + qd[i + Q * 7] * ug2; - v[i + Q * 2] = qd[i + Q * 2] * ug0 + qd[i + Q * 5] * ug1 + qd[i + Q * 8] * ug2; - } - break; - } - return 0; -} - -#endif // PALACE_LIBCEED_GRAD_QF_H diff --git a/palace/fem/qfunctions/h1_build_qf.h b/palace/fem/qfunctions/h1_build_qf.h new file mode 100644 index 000000000..34b21066a --- /dev/null +++ b/palace/fem/qfunctions/h1_build_qf.h @@ -0,0 +1,66 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef PALACE_LIBCEED_H1_BUILD_QF_H +#define PALACE_LIBCEED_H1_BUILD_QF_H + +#include "coeff_qf.h" + +// Build functions replace active vector output with quadrature point data, stored as a +// symmetric matrix, and remove active vector input. + +CEED_QFUNCTION(f_build_h1_1)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q; + CeedScalar *qd = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + const CeedScalar coeff = CoeffUnpack1((const CeedIntScalar *)ctx, (CeedInt)attr[i]); + + qd[i] = coeff * wdetJ[i]; + } + return 0; +} + +CEED_QFUNCTION(f_build_h1_2)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q; + CeedScalar *qd = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + CeedScalar coeff[3]; + CoeffUnpack2((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + + qd[i + Q * 0] = wdetJ[i] * coeff[0]; + qd[i + Q * 1] = wdetJ[i] * coeff[1]; + qd[i + Q * 2] = wdetJ[i] * coeff[2]; + } + return 0; +} + +CEED_QFUNCTION(f_build_h1_3)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q; + CeedScalar *qd = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + CeedScalar coeff[6]; + CoeffUnpack3((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + + qd[i + Q * 0] = wdetJ[i] * coeff[0]; + qd[i + Q * 1] = wdetJ[i] * coeff[1]; + qd[i + Q * 2] = wdetJ[i] * coeff[2]; + qd[i + Q * 3] = wdetJ[i] * coeff[3]; + qd[i + Q * 4] = wdetJ[i] * coeff[4]; + qd[i + Q * 5] = wdetJ[i] * coeff[5]; + } + return 0; +} + +#endif // PALACE_LIBCEED_H1_BUILD_QF_H diff --git a/palace/fem/qfunctions/h1_qf.h b/palace/fem/qfunctions/h1_qf.h new file mode 100644 index 000000000..b8c922c45 --- /dev/null +++ b/palace/fem/qfunctions/h1_qf.h @@ -0,0 +1,69 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef PALACE_LIBCEED_H1_QF_H +#define PALACE_LIBCEED_H1_QF_H + +#include "coeff_qf.h" + +// libCEED QFunctions for H1 operators (Piola transformation u = ̂u). +// in[0] is geometry quadrature data, shape [ncomp=2+space_dim*dim, Q] +// in[1] is active vector, shape [ncomp=vdim, Q] +// out[0] is active vector, shape [ncomp=vdim, Q] + +CEED_QFUNCTION(f_apply_h1_1)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *u = in[1]; + CeedScalar *v = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + const CeedScalar coeff = CoeffUnpack1((const CeedIntScalar *)ctx, (CeedInt)attr[i]); + + v[i] = coeff * wdetJ[i] * u[i]; + } + return 0; +} + +CEED_QFUNCTION(f_apply_h1_2)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *u = in[1]; + CeedScalar *v = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + CeedScalar coeff[3]; + CoeffUnpack2((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + + const CeedScalar u0 = u[i + Q * 0]; + const CeedScalar u1 = u[i + Q * 1]; + v[i + Q * 0] = wdetJ[i] * (coeff[0] * u0 + coeff[1] * u1); + v[i + Q * 1] = wdetJ[i] * (coeff[1] * u0 + coeff[2] * u1); + } + return 0; +} + +CEED_QFUNCTION(f_apply_h1_3)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *u = in[1]; + CeedScalar *v = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + CeedScalar coeff[6]; + CoeffUnpack3((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + + const CeedScalar u0 = u[i + Q * 0]; + const CeedScalar u1 = u[i + Q * 1]; + const CeedScalar u2 = u[i + Q * 2]; + v[i + Q * 0] = wdetJ[i] * (coeff[0] * u0 + coeff[1] * u1 + coeff[2] * u2); + v[i + Q * 1] = wdetJ[i] * (coeff[1] * u0 + coeff[3] * u1 + coeff[4] * u2); + v[i + Q * 2] = wdetJ[i] * (coeff[2] * u0 + coeff[4] * u1 + coeff[5] * u2); + } + return 0; +} + +#endif // PALACE_LIBCEED_H1_QF_H diff --git a/palace/fem/qfunctions/hcurl_build_qf.h b/palace/fem/qfunctions/hcurl_build_qf.h new file mode 100644 index 000000000..d1f91a4da --- /dev/null +++ b/palace/fem/qfunctions/hcurl_build_qf.h @@ -0,0 +1,94 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef PALACE_LIBCEED_HCURL_BUILD_QF_H +#define PALACE_LIBCEED_HCURL_BUILD_QF_H + +#include "coeff_qf.h" +#include "utils_qf.h" + +// Build functions replace active vector output with quadrature point data, stored as a +// symmetric matrix, and remove active vector input. + +CEED_QFUNCTION(f_build_hcurl_22)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q; + CeedScalar *qd = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + CeedScalar coeff[3], adjJt_loc[4], qd_loc[3]; + CoeffUnpack2((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack22(adjJt + i, Q, adjJt_loc); + MultAtBA22(adjJt_loc, coeff, qd_loc); + + qd[i + Q * 0] = wdetJ[i] * qd_loc[0]; + qd[i + Q * 1] = wdetJ[i] * qd_loc[1]; + qd[i + Q * 2] = wdetJ[i] * qd_loc[2]; + } + return 0; +} + +CEED_QFUNCTION(f_build_hcurl_33)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q; + CeedScalar *qd = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + CeedScalar coeff[6], adjJt_loc[9], qd_loc[6]; + CoeffUnpack3((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack33(adjJt + i, Q, adjJt_loc); + MultAtBA33(adjJt_loc, coeff, qd_loc); + + qd[i + Q * 0] = wdetJ[i] * qd_loc[0]; + qd[i + Q * 1] = wdetJ[i] * qd_loc[1]; + qd[i + Q * 2] = wdetJ[i] * qd_loc[2]; + qd[i + Q * 3] = wdetJ[i] * qd_loc[3]; + qd[i + Q * 4] = wdetJ[i] * qd_loc[4]; + qd[i + Q * 5] = wdetJ[i] * qd_loc[5]; + } + return 0; +} + +CEED_QFUNCTION(f_build_hcurl_21)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q; + CeedScalar *qd = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + CeedScalar coeff[3], adjJt_loc[2], qd_loc[1]; + CoeffUnpack2((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack21(adjJt + i, Q, adjJt_loc); + MultAtBA21(adjJt_loc, coeff, qd_loc); + + qd[i + Q * 0] = wdetJ[i] * qd_loc[0]; + } + return 0; +} + +CEED_QFUNCTION(f_build_hcurl_32)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q; + CeedScalar *qd = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + CeedScalar coeff[6], adjJt_loc[6], qd_loc[3]; + CoeffUnpack3((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack32(adjJt + i, Q, adjJt_loc); + MultAtBA32(adjJt_loc, coeff, qd_loc); + + qd[i + Q * 0] = wdetJ[i] * qd_loc[0]; + qd[i + Q * 1] = wdetJ[i] * qd_loc[1]; + qd[i + Q * 2] = wdetJ[i] * qd_loc[2]; + } + return 0; +} + +#endif // PALACE_LIBCEED_HCURL_BUILD_QF_H diff --git a/palace/fem/qfunctions/hcurl_qf.h b/palace/fem/qfunctions/hcurl_qf.h index f133bc9b7..ab6772828 100644 --- a/palace/fem/qfunctions/hcurl_qf.h +++ b/palace/fem/qfunctions/hcurl_qf.h @@ -4,190 +4,90 @@ #ifndef PALACE_LIBCEED_HCURL_QF_H #define PALACE_LIBCEED_HCURL_QF_H +#include "coeff_qf.h" #include "utils_qf.h" -#include "vecfemass_qf.h" -// libCEED QFunction for building quadrature data for an H(curl) mass operator with a scalar -// constant coefficient. -CEED_QFUNCTION(f_build_hcurl_const_scalar)(void *ctx, CeedInt Q, - const CeedScalar *const *in, - CeedScalar *const *out) +// libCEED QFunctions for H(curl) operators (Piola transformation u = adj(J)^T / det(J) ̂u). +// in[0] is geometry quadrature data, shape [ncomp=2+space_dim*dim, Q] +// in[1] is active vector, shape [qcomp=dim, ncomp=1, Q] +// out[0] is active vector, shape [qcomp=dim, ncomp=1, Q] + +CEED_QFUNCTION(f_apply_hcurl_22)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) { - // At every quadrature point, compute qw / det(J) adj(J) C adj(J)^T and store the - // symmetric part of the result. - // in[0] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[1] is quadrature weights, size (Q) - VectorFEMassContext *bc = (VectorFEMassContext *)ctx; - const CeedScalar coeff = bc->coeff; - const CeedScalar *J = in[0], *qw = in[1]; - CeedScalar *qd = out[0]; - switch (10 * bc->space_dim + bc->dim) + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *u = in[1]; + CeedScalar *v = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { - case 11: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qd[i] = qw[i] * coeff / J[i]; - } - break; - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt21(J + i, Q, &coeff, 1, 1, qw[i], Q, qd + i); - } - break; - case 22: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt22(J + i, Q, &coeff, 1, 1, qw[i], Q, qd + i); - } - break; - case 32: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt32(J + i, Q, &coeff, 1, 1, qw[i], Q, qd + i); - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt33(J + i, Q, &coeff, 1, 1, qw[i], Q, qd + i); - } - break; + const CeedScalar u_loc[2] = {u[i + Q * 0], u[i + Q * 1]}; + CeedScalar coeff[3], adjJt_loc[4], v_loc[2]; + CoeffUnpack2((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack22(adjJt + i, Q, adjJt_loc); + MultAtBCx22(adjJt_loc, coeff, adjJt_loc, u_loc, v_loc); + + v[i + Q * 0] = wdetJ[i] * v_loc[0]; + v[i + Q * 1] = wdetJ[i] * v_loc[1]; } return 0; } -// libCEED QFunction for building quadrature data for an H(curl) mass operator with a scalar -// coefficient evaluated at quadrature points. -CEED_QFUNCTION(f_build_hcurl_quad_scalar)(void *ctx, CeedInt Q, const CeedScalar *const *in, - CeedScalar *const *out) +CEED_QFUNCTION(f_apply_hcurl_33)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) { - // At every quadrature point, compute qw / det(J) adj(J) C adj(J)^T and store the - // symmetric part of the result. - // in[0] is coefficients with shape [ncomp=1, Q] - // in[1] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[2] is quadrature weights, size (Q) - VectorFEMassContext *bc = (VectorFEMassContext *)ctx; - const CeedScalar *c = in[0], *J = in[1], *qw = in[2]; - CeedScalar *qd = out[0]; - switch (10 * bc->space_dim + bc->dim) + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *u = in[1]; + CeedScalar *v = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { - case 11: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qd[i] = qw[i] * c[i] / J[i]; - } - break; - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt21(J + i, Q, c + i, Q, 1, qw[i], Q, qd + i); - } - break; - case 22: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt22(J + i, Q, c + i, Q, 1, qw[i], Q, qd + i); - } - break; - case 32: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt32(J + i, Q, c + i, Q, 1, qw[i], Q, qd + i); - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt33(J + i, Q, c + i, Q, 1, qw[i], Q, qd + i); - } - break; + const CeedScalar u_loc[3] = {u[i + Q * 0], u[i + Q * 1], u[i + Q * 2]}; + CeedScalar coeff[6], adjJt_loc[9], v_loc[3]; + CoeffUnpack3((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack33(adjJt + i, Q, adjJt_loc); + MultAtBCx33(adjJt_loc, coeff, adjJt_loc, u_loc, v_loc); + + v[i + Q * 0] = wdetJ[i] * v_loc[0]; + v[i + Q * 1] = wdetJ[i] * v_loc[1]; + v[i + Q * 2] = wdetJ[i] * v_loc[2]; } return 0; } -// libCEED QFunction for building quadrature data for an H(curl) mass operator with a vector -// coefficient evaluated at quadrature points. -CEED_QFUNCTION(f_build_hcurl_quad_vector)(void *ctx, CeedInt Q, const CeedScalar *const *in, - CeedScalar *const *out) +CEED_QFUNCTION(f_apply_hcurl_21)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) { - // At every quadrature point, compute qw / det(J) adj(J) C adj(J)^T and store the - // symmetric part of the result. - // in[0] is coefficients with shape [ncomp=space_dim, Q] - // in[1] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[2] is quadrature weights, size (Q) - VectorFEMassContext *bc = (VectorFEMassContext *)ctx; - const CeedScalar *c = in[0], *J = in[1], *qw = in[2]; - CeedScalar *qd = out[0]; - switch (10 * bc->space_dim + bc->dim) + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *u = in[1]; + CeedScalar *v = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt21(J + i, Q, c + i, Q, 2, qw[i], Q, qd + i); - } - break; - case 22: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt22(J + i, Q, c + i, Q, 2, qw[i], Q, qd + i); - } - break; - case 32: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt32(J + i, Q, c + i, Q, 3, qw[i], Q, qd + i); - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt33(J + i, Q, c + i, Q, 3, qw[i], Q, qd + i); - } - break; + const CeedScalar u_loc[1] = {u[i + Q * 0]}; + CeedScalar coeff[3], adjJt_loc[2], v_loc[2]; + CoeffUnpack2((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack21(adjJt + i, Q, adjJt_loc); + MultAtBCx21(adjJt_loc, coeff, adjJt_loc, u_loc, v_loc); + + v[i + Q * 0] = wdetJ[i] * v_loc[0]; } return 0; } -// libCEED QFunction for building quadrature data for an H(curl) mass operator with a matrix -// coefficient evaluated at quadrature points. -CEED_QFUNCTION(f_build_hcurl_quad_matrix)(void *ctx, CeedInt Q, const CeedScalar *const *in, - CeedScalar *const *out) +CEED_QFUNCTION(f_apply_hcurl_32)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) { - // At every quadrature point, compute qw / det(J) adj(J) C adj(J)^T and store the - // symmetric part of the result. - // in[0] is coefficients with shape [ncomp=space_dim*(space_dim+1)/2, Q] - // in[1] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[2] is quadrature weights, size (Q) - VectorFEMassContext *bc = (VectorFEMassContext *)ctx; - const CeedScalar *c = in[0], *J = in[1], *qw = in[2]; - CeedScalar *qd = out[0]; - switch (10 * bc->space_dim + bc->dim) + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *u = in[1]; + CeedScalar *v = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt21(J + i, Q, c + i, Q, 3, qw[i], Q, qd + i); - } - break; - case 22: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt22(J + i, Q, c + i, Q, 3, qw[i], Q, qd + i); - } - break; - case 32: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt32(J + i, Q, c + i, Q, 6, qw[i], Q, qd + i); - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultAdjJCAdjJt33(J + i, Q, c + i, Q, 6, qw[i], Q, qd + i); - } - break; + const CeedScalar u_loc[2] = {u[i + Q * 0], u[i + Q * 1]}; + CeedScalar coeff[6], adjJt_loc[6], v_loc[3]; + CoeffUnpack3((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack32(adjJt + i, Q, adjJt_loc); + MultAtBCx32(adjJt_loc, coeff, adjJt_loc, u_loc, v_loc); + + v[i + Q * 0] = wdetJ[i] * v_loc[0]; + v[i + Q * 1] = wdetJ[i] * v_loc[1]; } return 0; } diff --git a/palace/fem/qfunctions/hcurlh1d_build_qf.h b/palace/fem/qfunctions/hcurlh1d_build_qf.h new file mode 100644 index 000000000..68d47c93e --- /dev/null +++ b/palace/fem/qfunctions/hcurlh1d_build_qf.h @@ -0,0 +1,99 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef PALACE_LIBCEED_HCURL_H1D_BUILD_QF_H +#define PALACE_LIBCEED_HCURL_H1D_BUILD_QF_H + +#include "coeff_qf.h" +#include "utils_qf.h" + +// Build functions replace active vector output with quadrature point data and remove active +// vector input. + +CEED_QFUNCTION(f_build_hcurlh1d_22)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q; + CeedScalar *qd = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + CeedScalar coeff[3], adjJt_loc[4], qd_loc[4]; + CoeffUnpack2((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack22(adjJt + i, Q, adjJt_loc); + MultBA22(adjJt_loc, coeff, qd_loc); + + qd[i + Q * 0] = wdetJ[i] * qd_loc[0]; + qd[i + Q * 1] = wdetJ[i] * qd_loc[1]; + qd[i + Q * 2] = wdetJ[i] * qd_loc[2]; + qd[i + Q * 3] = wdetJ[i] * qd_loc[3]; + } + return 0; +} + +CEED_QFUNCTION(f_build_hcurlh1d_33)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q; + CeedScalar *qd = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + CeedScalar coeff[6], adjJt_loc[9], qd_loc[9]; + CoeffUnpack3((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack33(adjJt + i, Q, adjJt_loc); + MultBA33(adjJt_loc, coeff, qd_loc); + + qd[i + Q * 0] = wdetJ[i] * qd_loc[0]; + qd[i + Q * 1] = wdetJ[i] * qd_loc[1]; + qd[i + Q * 2] = wdetJ[i] * qd_loc[2]; + qd[i + Q * 3] = wdetJ[i] * qd_loc[3]; + qd[i + Q * 4] = wdetJ[i] * qd_loc[4]; + qd[i + Q * 5] = wdetJ[i] * qd_loc[5]; + qd[i + Q * 6] = wdetJ[i] * qd_loc[6]; + qd[i + Q * 7] = wdetJ[i] * qd_loc[7]; + qd[i + Q * 8] = wdetJ[i] * qd_loc[8]; + } + return 0; +} + +CEED_QFUNCTION(f_build_hcurlh1d_21)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q; + CeedScalar *qd = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + CeedScalar coeff[3], adjJt_loc[2], qd_loc[1]; + CoeffUnpack2((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack21(adjJt + i, Q, adjJt_loc); + MultBA21(adjJt_loc, coeff, qd_loc); + + qd[i + Q * 0] = wdetJ[i] * qd_loc[0]; + } + return 0; +} + +CEED_QFUNCTION(f_build_hcurlh1d_32)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q; + CeedScalar *qd = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + CeedScalar coeff[6], adjJt_loc[6], qd_loc[4]; + CoeffUnpack3((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack32(adjJt + i, Q, adjJt_loc); + MultBA32(adjJt_loc, coeff, qd_loc); + + qd[i + Q * 0] = wdetJ[i] * qd_loc[0]; + qd[i + Q * 1] = wdetJ[i] * qd_loc[1]; + qd[i + Q * 2] = wdetJ[i] * qd_loc[2]; + qd[i + Q * 3] = wdetJ[i] * qd_loc[3]; + } + return 0; +} + +#endif // PALACE_LIBCEED_HCURL_H1D_BUILD_QF_H diff --git a/palace/fem/qfunctions/hcurlh1d_qf.h b/palace/fem/qfunctions/hcurlh1d_qf.h new file mode 100644 index 000000000..4e0f3d224 --- /dev/null +++ b/palace/fem/qfunctions/hcurlh1d_qf.h @@ -0,0 +1,96 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef PALACE_LIBCEED_HCURL_H1D_QF_H +#define PALACE_LIBCEED_HCURL_H1D_QF_H + +#include "coeff_qf.h" +#include "utils_qf.h" + +// libCEED QFunctions for mixed H(curl)-(H1)ᵈ operators (Piola transformation u = +// adj(J)^T / det(J) ̂u and u = ̂u) +// in[0] is geometry quadrature data, shape [ncomp=2+space_dim*dim, Q] +// in[1] is active vector, shape [qcomp=dim, ncomp=1, Q] +// out[0] is active vector, shape [ncomp=space_dim, Q] + +CEED_QFUNCTION(f_apply_hcurlh1d_22)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *u = in[1]; + CeedScalar *v = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + const CeedScalar u_loc[2] = {u[i + Q * 0], u[i + Q * 1]}; + CeedScalar coeff[3], adjJt_loc[4], v_loc[2]; + CoeffUnpack2((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack22(adjJt + i, Q, adjJt_loc); + MultBAx22(adjJt_loc, coeff, u_loc, v_loc); + + v[i + Q * 0] = wdetJ[i] * v_loc[0]; + v[i + Q * 1] = wdetJ[i] * v_loc[1]; + } + return 0; +} + +CEED_QFUNCTION(f_apply_hcurlh1d_33)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *u = in[1]; + CeedScalar *v = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + const CeedScalar u_loc[3] = {u[i + Q * 0], u[i + Q * 1], u[i + Q * 2]}; + CeedScalar coeff[6], adjJt_loc[9], v_loc[3]; + CoeffUnpack3((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack33(adjJt + i, Q, adjJt_loc); + MultBAx33(adjJt_loc, coeff, u_loc, v_loc); + + v[i + Q * 0] = wdetJ[i] * v_loc[0]; + v[i + Q * 1] = wdetJ[i] * v_loc[1]; + v[i + Q * 2] = wdetJ[i] * v_loc[2]; + } + return 0; +} + +CEED_QFUNCTION(f_apply_hcurlh1d_21)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *u = in[1]; + CeedScalar *v = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + const CeedScalar u_loc[1] = {u[i + Q * 0]}; + CeedScalar coeff[3], adjJt_loc[2], v_loc[1]; + CoeffUnpack2((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack21(adjJt + i, Q, adjJt_loc); + MultBAx21(adjJt_loc, coeff, u_loc, v_loc); + + v[i + Q * 0] = wdetJ[i] * v_loc[0]; + } + return 0; +} + +CEED_QFUNCTION(f_apply_hcurlh1d_32)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *u = in[1]; + CeedScalar *v = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + const CeedScalar u_loc[2] = {u[i + Q * 0], u[i + Q * 1]}; + CeedScalar coeff[6], adjJt_loc[6], v_loc[2]; + CoeffUnpack3((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack32(adjJt + i, Q, adjJt_loc); + MultBAx32(adjJt_loc, coeff, u_loc, v_loc); + + v[i + Q * 0] = wdetJ[i] * v_loc[0]; + v[i + Q * 1] = wdetJ[i] * v_loc[1]; + } + return 0; +} + +#endif // PALACE_LIBCEED_HCURL_H1D_QF_H diff --git a/palace/fem/qfunctions/hcurlhdiv_build_qf.h b/palace/fem/qfunctions/hcurlhdiv_build_qf.h new file mode 100644 index 000000000..abbf7bd08 --- /dev/null +++ b/palace/fem/qfunctions/hcurlhdiv_build_qf.h @@ -0,0 +1,194 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef PALACE_LIBCEED_HCURL_HDIV_BUILD_QF_H +#define PALACE_LIBCEED_HCURL_HDIV_BUILD_QF_H + +#include "coeff_qf.h" +#include "utils_geom_qf.h" +#include "utils_qf.h" + +// Build functions replace active vector output with quadrature point data and remove active +// vector input. + +CEED_QFUNCTION(f_build_hcurlhdiv_22)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q; + CeedScalar *qd = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + CeedScalar coeff[3], adjJt_loc[4], J_loc[4], qd_loc[4]; + CoeffUnpack2((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack22(adjJt + i, Q, adjJt_loc); + AdjJt22(adjJt_loc, J_loc); + MultAtBC22(J_loc, coeff, adjJt_loc, qd_loc); + + qd[i + Q * 0] = wdetJ[i] * qd_loc[0]; + qd[i + Q * 1] = wdetJ[i] * qd_loc[1]; + qd[i + Q * 2] = wdetJ[i] * qd_loc[2]; + qd[i + Q * 3] = wdetJ[i] * qd_loc[3]; + } + return 0; +} + +CEED_QFUNCTION(f_build_hcurlhdiv_33)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q; + CeedScalar *qd = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + CeedScalar coeff[6], adjJt_loc[9], J_loc[9], qd_loc[9]; + CoeffUnpack3((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack33(adjJt + i, Q, adjJt_loc); + AdjJt33(adjJt_loc, J_loc); + MultAtBC33(J_loc, coeff, adjJt_loc, qd_loc); + + qd[i + Q * 0] = wdetJ[i] * qd_loc[0]; + qd[i + Q * 1] = wdetJ[i] * qd_loc[1]; + qd[i + Q * 2] = wdetJ[i] * qd_loc[2]; + qd[i + Q * 3] = wdetJ[i] * qd_loc[3]; + qd[i + Q * 4] = wdetJ[i] * qd_loc[4]; + qd[i + Q * 5] = wdetJ[i] * qd_loc[5]; + qd[i + Q * 6] = wdetJ[i] * qd_loc[6]; + qd[i + Q * 7] = wdetJ[i] * qd_loc[7]; + qd[i + Q * 8] = wdetJ[i] * qd_loc[8]; + } + return 0; +} + +CEED_QFUNCTION(f_build_hcurlhdiv_21)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q; + CeedScalar *qd = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + CeedScalar coeff[3], adjJt_loc[2], J_loc[2], qd_loc[1]; + CoeffUnpack2((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack21(adjJt + i, Q, adjJt_loc); + AdjJt21(adjJt_loc, J_loc); + MultAtBC21(J_loc, coeff, adjJt_loc, qd_loc); + + qd[i + Q * 0] = wdetJ[i] * qd_loc[0]; + } + return 0; +} + +CEED_QFUNCTION(f_build_hcurlhdiv_32)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q; + CeedScalar *qd = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + CeedScalar coeff[6], adjJt_loc[6], J_loc[6], qd_loc[4]; + CoeffUnpack3((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack32(adjJt + i, Q, adjJt_loc); + AdjJt32(adjJt_loc, J_loc); + MultAtBC32(J_loc, coeff, adjJt_loc, qd_loc); + + qd[i + Q * 0] = wdetJ[i] * qd_loc[0]; + qd[i + Q * 1] = wdetJ[i] * qd_loc[1]; + qd[i + Q * 2] = wdetJ[i] * qd_loc[2]; + qd[i + Q * 3] = wdetJ[i] * qd_loc[3]; + } + return 0; +} + +CEED_QFUNCTION(f_build_hdivhcurl_22)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q; + CeedScalar *qd = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + CeedScalar coeff[3], adjJt_loc[4], J_loc[4], qd_loc[4]; + CoeffUnpack2((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack22(adjJt + i, Q, adjJt_loc); + AdjJt22(adjJt_loc, J_loc); + MultAtBC22(adjJt_loc, coeff, J_loc, qd_loc); + + qd[i + Q * 0] = wdetJ[i] * qd_loc[0]; + qd[i + Q * 1] = wdetJ[i] * qd_loc[1]; + qd[i + Q * 2] = wdetJ[i] * qd_loc[2]; + qd[i + Q * 3] = wdetJ[i] * qd_loc[3]; + } + return 0; +} + +CEED_QFUNCTION(f_build_hdivhcurl_33)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q; + CeedScalar *qd = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + CeedScalar coeff[6], adjJt_loc[9], J_loc[9], qd_loc[9]; + CoeffUnpack3((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack33(adjJt + i, Q, adjJt_loc); + AdjJt33(adjJt_loc, J_loc); + MultAtBC33(adjJt_loc, coeff, J_loc, qd_loc); + + qd[i + Q * 0] = wdetJ[i] * qd_loc[0]; + qd[i + Q * 1] = wdetJ[i] * qd_loc[1]; + qd[i + Q * 2] = wdetJ[i] * qd_loc[2]; + qd[i + Q * 3] = wdetJ[i] * qd_loc[3]; + qd[i + Q * 4] = wdetJ[i] * qd_loc[4]; + qd[i + Q * 5] = wdetJ[i] * qd_loc[5]; + qd[i + Q * 6] = wdetJ[i] * qd_loc[6]; + qd[i + Q * 7] = wdetJ[i] * qd_loc[7]; + qd[i + Q * 8] = wdetJ[i] * qd_loc[8]; + } + return 0; +} + +CEED_QFUNCTION(f_build_hdivhcurl_21)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q; + CeedScalar *qd = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + CeedScalar coeff[3], adjJt_loc[2], J_loc[2], qd_loc[1]; + CoeffUnpack2((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack21(adjJt + i, Q, adjJt_loc); + AdjJt21(adjJt_loc, J_loc); + MultAtBC21(adjJt_loc, coeff, J_loc, qd_loc); + + qd[i + Q * 0] = wdetJ[i] * qd_loc[0]; + } + return 0; +} + +CEED_QFUNCTION(f_build_hdivhcurl_32)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q; + CeedScalar *qd = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + CeedScalar coeff[6], adjJt_loc[6], J_loc[6], qd_loc[4]; + CoeffUnpack3((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack32(adjJt + i, Q, adjJt_loc); + AdjJt32(adjJt_loc, J_loc); + MultAtBC32(adjJt_loc, coeff, J_loc, qd_loc); + + qd[i + Q * 0] = wdetJ[i] * qd_loc[0]; + qd[i + Q * 1] = wdetJ[i] * qd_loc[1]; + qd[i + Q * 2] = wdetJ[i] * qd_loc[2]; + qd[i + Q * 3] = wdetJ[i] * qd_loc[3]; + } + return 0; +} + +#endif // PALACE_LIBCEED_HCURL_HDIV_BUILD_QF_H diff --git a/palace/fem/qfunctions/hcurlhdiv_qf.h b/palace/fem/qfunctions/hcurlhdiv_qf.h index 65272f665..7b1c5856a 100644 --- a/palace/fem/qfunctions/hcurlhdiv_qf.h +++ b/palace/fem/qfunctions/hcurlhdiv_qf.h @@ -1,386 +1,186 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -#ifndef PALACE_LIBCEED_HCURLHDIV_QF_H -#define PALACE_LIBCEED_HCURLHDIV_QF_H +#ifndef PALACE_LIBCEED_HCURL_HDIV_QF_H +#define PALACE_LIBCEED_HCURL_HDIV_QF_H +#include "coeff_qf.h" +#include "utils_geom_qf.h" #include "utils_qf.h" -#include "vecfemass_qf.h" -// libCEED QFunction for building quadrature data for a mixed H(curl)-H(div) mass operator -// with a scalar constant coefficient. -CEED_QFUNCTION(f_build_hcurlhdiv_const_scalar)(void *ctx, CeedInt Q, - const CeedScalar *const *in, - CeedScalar *const *out) +// libCEED QFunctions for mixed H(curl)-H(div) operators (Piola transformations u = +// adj(J)^T / det(J) ̂u and u = J / det(J) ̂u). +// Note: J / det(J) = adj(adj(J)^T / det(J))^T +// in[0] is geometry quadrature data, shape [ncomp=2+space_dim*dim, Q] +// in[1] is active vector, shape [qcomp=dim, ncomp=1, Q] +// out[0] is active vector, shape [qcomp=dim, ncomp=1, Q] + +CEED_QFUNCTION(f_apply_hcurlhdiv_22)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) { - // At every quadrature point, compute qw / det(J) adj(J) C J and store the - // result. - // in[0] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[1] is quadrature weights, size (Q) - VectorFEMassContext *bc = (VectorFEMassContext *)ctx; - const CeedScalar coeff = bc->coeff; - const CeedScalar *J = in[0], *qw = in[1]; - CeedScalar *qd = out[0]; - switch (10 * bc->space_dim + bc->dim) + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *u = in[1]; + CeedScalar *v = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { - case 11: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qd[i] = qw[i] * coeff; - } - break; - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCAdjJt21(J + i, Q, &coeff, 1, 1, qw[i], Q, qd + i); - } - break; - case 22: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCAdjJt22(J + i, Q, &coeff, 1, 1, qw[i], Q, qd + i); - } - break; - case 32: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCAdjJt32(J + i, Q, &coeff, 1, 1, qw[i], Q, qd + i); - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCAdjJt33(J + i, Q, &coeff, 1, 1, qw[i], Q, qd + i); - } - break; + const CeedScalar u_loc[2] = {u[i + Q * 0], u[i + Q * 1]}; + CeedScalar coeff[3], adjJt_loc[4], J_loc[4], v_loc[2]; + CoeffUnpack2((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack22(adjJt + i, Q, adjJt_loc); + AdjJt22(adjJt_loc, J_loc); + MultAtBCx22(J_loc, coeff, adjJt_loc, u_loc, v_loc); + + v[i + Q * 0] = wdetJ[i] * v_loc[0]; + v[i + Q * 1] = wdetJ[i] * v_loc[1]; } return 0; } -// libCEED QFunction for building quadrature data for a mixed H(curl)-H(div) mass operator -// with a scalar coefficient evaluated at quadrature points. -CEED_QFUNCTION(f_build_hcurlhdiv_quad_scalar)(void *ctx, CeedInt Q, - const CeedScalar *const *in, - CeedScalar *const *out) +CEED_QFUNCTION(f_apply_hcurlhdiv_33)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) { - // At every quadrature point, compute qw / det(J) adj(J) C J and store the - // result. - // in[0] is coefficients with shape [ncomp=1, Q] - // in[1] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[2] is quadrature weights, size (Q) - VectorFEMassContext *bc = (VectorFEMassContext *)ctx; - const CeedScalar *c = in[0], *J = in[1], *qw = in[2]; - CeedScalar *qd = out[0]; - switch (10 * bc->space_dim + bc->dim) + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *u = in[1]; + CeedScalar *v = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { - case 11: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qd[i] = qw[i] * c[i]; - } - break; - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCAdjJt21(J + i, Q, c + i, Q, 1, qw[i], Q, qd + i); - } - break; - case 22: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCAdjJt22(J + i, Q, c + i, Q, 1, qw[i], Q, qd + i); - } - break; - case 32: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCAdjJt32(J + i, Q, c + i, Q, 1, qw[i], Q, qd + i); - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCAdjJt33(J + i, Q, c + i, Q, 1, qw[i], Q, qd + i); - } - break; + const CeedScalar u_loc[3] = {u[i + Q * 0], u[i + Q * 1], u[i + Q * 2]}; + CeedScalar coeff[6], adjJt_loc[9], J_loc[9], v_loc[3]; + CoeffUnpack3((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack33(adjJt + i, Q, adjJt_loc); + AdjJt33(adjJt_loc, J_loc); + MultAtBCx33(J_loc, coeff, adjJt_loc, u_loc, v_loc); + + v[i + Q * 0] = wdetJ[i] * v_loc[0]; + v[i + Q * 1] = wdetJ[i] * v_loc[1]; + v[i + Q * 2] = wdetJ[i] * v_loc[2]; } return 0; } -// libCEED QFunction for building quadrature data for a mixed H(curl)-H(div) mass operator -// with a vector coefficient evaluated at quadrature points. -CEED_QFUNCTION(f_build_hcurlhdiv_quad_vector)(void *ctx, CeedInt Q, - const CeedScalar *const *in, - CeedScalar *const *out) +CEED_QFUNCTION(f_apply_hcurlhdiv_21)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) { - // At every quadrature point, compute qw / det(J) adj(J) C J and store the - // result. - // in[0] is coefficients with shape [ncomp=space_dim, Q] - // in[1] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[2] is quadrature weights, size (Q) - VectorFEMassContext *bc = (VectorFEMassContext *)ctx; - const CeedScalar *c = in[0], *J = in[1], *qw = in[2]; - CeedScalar *qd = out[0]; - switch (10 * bc->space_dim + bc->dim) + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *u = in[1]; + CeedScalar *v = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCAdjJt21(J + i, Q, c + i, Q, 2, qw[i], Q, qd + i); - } - break; - case 22: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCAdjJt22(J + i, Q, c + i, Q, 2, qw[i], Q, qd + i); - } - break; - case 32: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCAdjJt32(J + i, Q, c + i, Q, 3, qw[i], Q, qd + i); - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCAdjJt33(J + i, Q, c + i, Q, 3, qw[i], Q, qd + i); - } - break; + const CeedScalar u_loc[1] = {u[i + Q * 0]}; + CeedScalar coeff[3], adjJt_loc[2], J_loc[2], v_loc[2]; + CoeffUnpack2((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack21(adjJt + i, Q, adjJt_loc); + AdjJt21(adjJt_loc, J_loc); + MultAtBCx21(J_loc, coeff, adjJt_loc, u_loc, v_loc); + + v[i + Q * 0] = wdetJ[i] * v_loc[0]; } return 0; } -// libCEED QFunction for building quadrature data for a mixed H(curl)-H(div) mass operator -// with a matrix coefficient evaluated at quadrature points. -CEED_QFUNCTION(f_build_hcurlhdiv_quad_matrix)(void *ctx, CeedInt Q, - const CeedScalar *const *in, - CeedScalar *const *out) +CEED_QFUNCTION(f_apply_hcurlhdiv_32)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) { - // At every quadrature point, compute qw / det(J) adj(J) C J and store the - // result. - // in[0] is coefficients with shape [ncomp=space_dim*(space_dim+1)/2, Q] - // in[1] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[2] is quadrature weights, size (Q) - VectorFEMassContext *bc = (VectorFEMassContext *)ctx; - const CeedScalar *c = in[0], *J = in[1], *qw = in[2]; - CeedScalar *qd = out[0]; - switch (10 * bc->space_dim + bc->dim) + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *u = in[1]; + CeedScalar *v = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCAdjJt21(J + i, Q, c + i, Q, 3, qw[i], Q, qd + i); - } - break; - case 22: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCAdjJt22(J + i, Q, c + i, Q, 3, qw[i], Q, qd + i); - } - break; - case 32: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCAdjJt32(J + i, Q, c + i, Q, 6, qw[i], Q, qd + i); - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCAdjJt33(J + i, Q, c + i, Q, 6, qw[i], Q, qd + i); - } - break; + const CeedScalar u_loc[2] = {u[i + Q * 0], u[i + Q * 1]}; + CeedScalar coeff[6], adjJt_loc[6], J_loc[6], v_loc[3]; + CoeffUnpack3((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack32(adjJt + i, Q, adjJt_loc); + AdjJt32(adjJt_loc, J_loc); + MultAtBCx32(J_loc, coeff, adjJt_loc, u_loc, v_loc); + + v[i + Q * 0] = wdetJ[i] * v_loc[0]; + v[i + Q * 1] = wdetJ[i] * v_loc[1]; } return 0; } -// libCEED QFunction for building quadrature data for a mixed H(div)-H(curl) mass operator -// with a scalar constant coefficient. -CEED_QFUNCTION(f_build_hdivhcurl_const_scalar)(void *ctx, CeedInt Q, - const CeedScalar *const *in, - CeedScalar *const *out) +CEED_QFUNCTION(f_apply_hdivhcurl_22)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) { - // At every quadrature point, compute qw / det(J) adj(J) C J and store the - // result. - // in[0] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[1] is quadrature weights, size (Q) - VectorFEMassContext *bc = (VectorFEMassContext *)ctx; - const CeedScalar coeff = bc->coeff; - const CeedScalar *J = in[0], *qw = in[1]; - CeedScalar *qd = out[0]; - switch (10 * bc->space_dim + bc->dim) + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *u = in[1]; + CeedScalar *v = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { - case 11: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qd[i] = qw[i] * coeff; - } - break; - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCAdjJt21(J + i, Q, &coeff, 1, 1, qw[i], Q, qd + i); - } - break; - case 22: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCAdjJt22(J + i, Q, &coeff, 1, 1, qw[i], Q, qd + i); - } - break; - case 32: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCAdjJt32(J + i, Q, &coeff, 1, 1, qw[i], Q, qd + i); - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCAdjJt33(J + i, Q, &coeff, 1, 1, qw[i], Q, qd + i); - } - break; + const CeedScalar u_loc[2] = {u[i + Q * 0], u[i + Q * 1]}; + CeedScalar coeff[3], adjJt_loc[4], J_loc[4], v_loc[2]; + CoeffUnpack2((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack22(adjJt + i, Q, adjJt_loc); + AdjJt22(adjJt_loc, J_loc); + MultAtBCx22(adjJt_loc, coeff, J_loc, u_loc, v_loc); + + v[i + Q * 0] = wdetJ[i] * v_loc[0]; + v[i + Q * 1] = wdetJ[i] * v_loc[1]; } return 0; } -// libCEED QFunction for building quadrature data for a mixed H(div)-H(curl) mass operator -// with a scalar coefficient evaluated at quadrature points. -CEED_QFUNCTION(f_build_hdivhcurl_quad_scalar)(void *ctx, CeedInt Q, - const CeedScalar *const *in, - CeedScalar *const *out) +CEED_QFUNCTION(f_apply_hdivhcurl_33)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) { - // At every quadrature point, compute qw / det(J) adj(J) C J and store the - // result. - // in[0] is coefficients with shape [ncomp=1, Q] - // in[1] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[2] is quadrature weights, size (Q) - VectorFEMassContext *bc = (VectorFEMassContext *)ctx; - const CeedScalar *c = in[0], *J = in[1], *qw = in[2]; - CeedScalar *qd = out[0]; - switch (10 * bc->space_dim + bc->dim) + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *u = in[1]; + CeedScalar *v = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { - case 11: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qd[i] = qw[i] * c[i]; - } - break; - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCAdjJt21(J + i, Q, c + i, Q, 1, qw[i], Q, qd + i); - } - break; - case 22: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCAdjJt22(J + i, Q, c + i, Q, 1, qw[i], Q, qd + i); - } - break; - case 32: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCAdjJt32(J + i, Q, c + i, Q, 1, qw[i], Q, qd + i); - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCAdjJt33(J + i, Q, c + i, Q, 1, qw[i], Q, qd + i); - } - break; + const CeedScalar u_loc[3] = {u[i + Q * 0], u[i + Q * 1], u[i + Q * 2]}; + CeedScalar coeff[6], adjJt_loc[9], J_loc[9], v_loc[3]; + CoeffUnpack3((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack33(adjJt + i, Q, adjJt_loc); + AdjJt33(adjJt_loc, J_loc); + MultAtBCx33(adjJt_loc, coeff, J_loc, u_loc, v_loc); + + v[i + Q * 0] = wdetJ[i] * v_loc[0]; + v[i + Q * 1] = wdetJ[i] * v_loc[1]; + v[i + Q * 2] = wdetJ[i] * v_loc[2]; } return 0; } -// libCEED QFunction for building quadrature data for a mixed H(div)-H(curl) mass operator -// with a vector coefficient evaluated at quadrature points. -CEED_QFUNCTION(f_build_hdivhcurl_quad_vector)(void *ctx, CeedInt Q, - const CeedScalar *const *in, - CeedScalar *const *out) +CEED_QFUNCTION(f_apply_hdivhcurl_21)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) { - // At every quadrature point, compute qw / det(J) adj(J) C J and store the - // result. - // in[0] is coefficients with shape [ncomp=space_dim, Q] - // in[1] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[2] is quadrature weights, size (Q) - VectorFEMassContext *bc = (VectorFEMassContext *)ctx; - const CeedScalar *c = in[0], *J = in[1], *qw = in[2]; - CeedScalar *qd = out[0]; - switch (10 * bc->space_dim + bc->dim) + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *u = in[1]; + CeedScalar *v = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCAdjJt21(J + i, Q, c + i, Q, 2, qw[i], Q, qd + i); - } - break; - case 22: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCAdjJt22(J + i, Q, c + i, Q, 2, qw[i], Q, qd + i); - } - break; - case 32: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCAdjJt32(J + i, Q, c + i, Q, 3, qw[i], Q, qd + i); - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCAdjJt33(J + i, Q, c + i, Q, 3, qw[i], Q, qd + i); - } - break; + const CeedScalar u_loc[1] = {u[i + Q * 0]}; + CeedScalar coeff[3], adjJt_loc[2], J_loc[2], v_loc[2]; + CoeffUnpack2((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack21(adjJt + i, Q, adjJt_loc); + AdjJt21(adjJt_loc, J_loc); + MultAtBCx21(adjJt_loc, coeff, J_loc, u_loc, v_loc); + + v[i + Q * 0] = wdetJ[i] * v_loc[0]; } return 0; } -// libCEED QFunction for building quadrature data for a mixed H(div)-H(curl) mass operator -// with a matrix coefficient evaluated at quadrature points. -CEED_QFUNCTION(f_build_hdivhcurl_quad_matrix)(void *ctx, CeedInt Q, - const CeedScalar *const *in, - CeedScalar *const *out) +CEED_QFUNCTION(f_apply_hdivhcurl_32)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) { - // At every quadrature point, compute qw / det(J) adj(J) C J and store the - // result. - // in[0] is coefficients with shape [ncomp=space_dim*(space_dim+1)/2, Q] - // in[1] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[2] is quadrature weights, size (Q) - VectorFEMassContext *bc = (VectorFEMassContext *)ctx; - const CeedScalar *c = in[0], *J = in[1], *qw = in[2]; - CeedScalar *qd = out[0]; - switch (10 * bc->space_dim + bc->dim) + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *u = in[1]; + CeedScalar *v = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCAdjJt21(J + i, Q, c + i, Q, 3, qw[i], Q, qd + i); - } - break; - case 22: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCAdjJt22(J + i, Q, c + i, Q, 3, qw[i], Q, qd + i); - } - break; - case 32: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCAdjJt32(J + i, Q, c + i, Q, 6, qw[i], Q, qd + i); - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCAdjJt33(J + i, Q, c + i, Q, 6, qw[i], Q, qd + i); - } - break; + const CeedScalar u_loc[2] = {u[i + Q * 0], u[i + Q * 1]}; + CeedScalar coeff[6], adjJt_loc[6], J_loc[6], v_loc[3]; + CoeffUnpack3((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack32(adjJt + i, Q, adjJt_loc); + AdjJt32(adjJt_loc, J_loc); + MultAtBCx32(adjJt_loc, coeff, J_loc, u_loc, v_loc); + + v[i + Q * 0] = wdetJ[i] * v_loc[0]; + v[i + Q * 1] = wdetJ[i] * v_loc[1]; } return 0; } -#endif // PALACE_LIBCEED_HCURLHDIV_QF_H +#endif // PALACE_LIBCEED_HCURL_HDIV_QF_H diff --git a/palace/fem/qfunctions/hcurlmass_build_qf.h b/palace/fem/qfunctions/hcurlmass_build_qf.h new file mode 100644 index 000000000..fa0ad2854 --- /dev/null +++ b/palace/fem/qfunctions/hcurlmass_build_qf.h @@ -0,0 +1,122 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef PALACE_LIBCEED_HCURL_MASS_BUILD_QF_H +#define PALACE_LIBCEED_HCURL_MASS_BUILD_QF_H + +#include "coeff_qf.h" +#include "utils_qf.h" + +// Build functions replace active vector output with quadrature point data, stored as a +// symmetric matrix, and remove active vector input. + +CEED_QFUNCTION(f_build_hcurlmass_22)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q; + CeedScalar *__restrict__ qd1 = out[0], *__restrict__ qd2 = out[0] + Q; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + { + const CeedScalar coeff = CoeffUnpack1((const CeedIntScalar *)ctx, (CeedInt)attr[i]); + + qd1[i + Q * 0] = coeff * wdetJ[i]; + } + { + CeedScalar coeff[3], adjJt_loc[4], qd_loc[3]; + CoeffUnpack2(CoeffPairSecond1((const CeedIntScalar *)ctx), (CeedInt)attr[i], coeff); + MatUnpack22(adjJt + i, Q, adjJt_loc); + MultAtBA22(adjJt_loc, coeff, qd_loc); + + qd2[i + Q * 0] = wdetJ[i] * qd_loc[0]; + qd2[i + Q * 1] = wdetJ[i] * qd_loc[1]; + qd2[i + Q * 2] = wdetJ[i] * qd_loc[2]; + } + } + return 0; +} + +CEED_QFUNCTION(f_build_hcurlmass_33)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q; + CeedScalar *__restrict__ qd1 = out[0], *__restrict__ qd2 = out[0] + Q; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + { + const CeedScalar coeff = CoeffUnpack1((const CeedIntScalar *)ctx, (CeedInt)attr[i]); + + qd1[i + Q * 0] = coeff * wdetJ[i]; + } + { + CeedScalar coeff[6], adjJt_loc[9], qd_loc[6]; + CoeffUnpack3(CoeffPairSecond1((const CeedIntScalar *)ctx), (CeedInt)attr[i], coeff); + MatUnpack33(adjJt + i, Q, adjJt_loc); + MultAtBA33(adjJt_loc, coeff, qd_loc); + + qd2[i + Q * 0] = wdetJ[i] * qd_loc[0]; + qd2[i + Q * 1] = wdetJ[i] * qd_loc[1]; + qd2[i + Q * 2] = wdetJ[i] * qd_loc[2]; + qd2[i + Q * 3] = wdetJ[i] * qd_loc[3]; + qd2[i + Q * 4] = wdetJ[i] * qd_loc[4]; + qd2[i + Q * 5] = wdetJ[i] * qd_loc[5]; + } + } + return 0; +} + +CEED_QFUNCTION(f_build_hcurlmass_21)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q; + CeedScalar *__restrict__ qd1 = out[0], *__restrict__ qd2 = out[0] + Q; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + { + const CeedScalar coeff = CoeffUnpack1((const CeedIntScalar *)ctx, (CeedInt)attr[i]); + + qd1[i + Q * 0] = coeff * wdetJ[i]; + } + { + CeedScalar coeff[3], adjJt_loc[2], qd_loc[1]; + CoeffUnpack2(CoeffPairSecond1((const CeedIntScalar *)ctx), (CeedInt)attr[i], coeff); + MatUnpack21(adjJt + i, Q, adjJt_loc); + MultAtBA21(adjJt_loc, coeff, qd_loc); + + qd2[i + Q * 0] = wdetJ[i] * qd_loc[0]; + } + } + return 0; +} + +CEED_QFUNCTION(f_build_hcurlmass_32)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q; + CeedScalar *__restrict__ qd1 = out[0], *__restrict__ qd2 = out[0] + Q; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + { + const CeedScalar coeff = CoeffUnpack1((const CeedIntScalar *)ctx, (CeedInt)attr[i]); + + qd1[i + Q * 0] = coeff * wdetJ[i]; + } + { + CeedScalar coeff[6], adjJt_loc[6], qd_loc[3]; + CoeffUnpack3(CoeffPairSecond1((const CeedIntScalar *)ctx), (CeedInt)attr[i], coeff); + MatUnpack32(adjJt + i, Q, adjJt_loc); + MultAtBA32(adjJt_loc, coeff, qd_loc); + + qd2[i + Q * 0] = wdetJ[i] * qd_loc[0]; + qd2[i + Q * 1] = wdetJ[i] * qd_loc[1]; + qd2[i + Q * 2] = wdetJ[i] * qd_loc[2]; + } + } + return 0; +} + +#endif // PALACE_LIBCEED_HCURL_MASS_BUILD_QF_H diff --git a/palace/fem/qfunctions/hcurlmass_qf.h b/palace/fem/qfunctions/hcurlmass_qf.h new file mode 100644 index 000000000..bff8d1f5f --- /dev/null +++ b/palace/fem/qfunctions/hcurlmass_qf.h @@ -0,0 +1,130 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef PALACE_LIBCEED_HCURL_MASS_QF_H +#define PALACE_LIBCEED_HCURL_MASS_QF_H + +#include "coeff_qf.h" +#include "utils_qf.h" + +// libCEED QFunctions for H(curl) + H1 mass operators (Piola transformation u = +// adj(J)^T / det(J) ̂u and u = ̂u). +// in[0] is geometry quadrature data, shape [ncomp=2+space_dim*dim, Q] +// in[1] is active vector, shape [ncomp=1, Q] +// in[2] is active vector gradient, shape [qcomp=dim, ncomp=1, Q] +// out[0] is active vector, shape [ncomp=1, Q] +// out[1] is active vector gradient, shape [qcomp=dim, ncomp=1, Q] + +CEED_QFUNCTION(f_apply_hcurlmass_22)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *u = in[1], + *gradu = in[2]; + CeedScalar *__restrict__ v = out[0], *__restrict__ gradv = out[1]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + { + const CeedScalar coeff = CoeffUnpack1((const CeedIntScalar *)ctx, (CeedInt)attr[i]); + + v[i] = coeff * wdetJ[i] * u[i]; + } + { + const CeedScalar u_loc[2] = {gradu[i + Q * 0], gradu[i + Q * 1]}; + CeedScalar coeff[3], adjJt_loc[4], v_loc[2]; + CoeffUnpack2(CoeffPairSecond1((const CeedIntScalar *)ctx), (CeedInt)attr[i], coeff); + MatUnpack22(adjJt + i, Q, adjJt_loc); + MultAtBCx22(adjJt_loc, coeff, adjJt_loc, u_loc, v_loc); + + gradv[i + Q * 0] = wdetJ[i] * v_loc[0]; + gradv[i + Q * 1] = wdetJ[i] * v_loc[1]; + } + } + return 0; +} + +CEED_QFUNCTION(f_apply_hcurlmass_33)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *u = in[1], + *gradu = in[2]; + CeedScalar *__restrict__ v = out[0], *__restrict__ gradv = out[1]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + { + const CeedScalar coeff = CoeffUnpack1((const CeedIntScalar *)ctx, (CeedInt)attr[i]); + + v[i] = coeff * wdetJ[i] * u[i]; + } + { + const CeedScalar u_loc[3] = {gradu[i + Q * 0], gradu[i + Q * 1], gradu[i + Q * 2]}; + CeedScalar coeff[6], adjJt_loc[9], v_loc[3]; + CoeffUnpack3(CoeffPairSecond1((const CeedIntScalar *)ctx), (CeedInt)attr[i], coeff); + MatUnpack33(adjJt + i, Q, adjJt_loc); + MultAtBCx33(adjJt_loc, coeff, adjJt_loc, u_loc, v_loc); + + gradv[i + Q * 0] = wdetJ[i] * v_loc[0]; + gradv[i + Q * 1] = wdetJ[i] * v_loc[1]; + gradv[i + Q * 2] = wdetJ[i] * v_loc[2]; + } + } + return 0; +} + +CEED_QFUNCTION(f_apply_hcurlmass_21)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *u = in[1], + *gradu = in[2]; + CeedScalar *__restrict__ v = out[0], *__restrict__ gradv = out[1]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + { + const CeedScalar coeff = CoeffUnpack1((const CeedIntScalar *)ctx, (CeedInt)attr[i]); + + v[i] = coeff * wdetJ[i] * u[i]; + } + { + const CeedScalar u_loc[1] = {gradu[i + Q * 0]}; + CeedScalar coeff[3], adjJt_loc[2], v_loc[2]; + CoeffUnpack2(CoeffPairSecond1((const CeedIntScalar *)ctx), (CeedInt)attr[i], coeff); + MatUnpack21(adjJt + i, Q, adjJt_loc); + MultAtBCx21(adjJt_loc, coeff, adjJt_loc, u_loc, v_loc); + + gradv[i + Q * 0] = wdetJ[i] * v_loc[0]; + } + } + return 0; +} + +CEED_QFUNCTION(f_apply_hcurlmass_32)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *u = in[1], + *gradu = in[2]; + CeedScalar *__restrict__ v = out[0], *__restrict__ gradv = out[1]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + { + const CeedScalar coeff = CoeffUnpack1((const CeedIntScalar *)ctx, (CeedInt)attr[i]); + + v[i] = coeff * wdetJ[i] * u[i]; + } + { + const CeedScalar u_loc[2] = {gradu[i + Q * 0], gradu[i + Q * 1]}; + CeedScalar coeff[6], adjJt_loc[6], v_loc[3]; + CoeffUnpack3(CoeffPairSecond1((const CeedIntScalar *)ctx), (CeedInt)attr[i], coeff); + MatUnpack32(adjJt + i, Q, adjJt_loc); + MultAtBCx32(adjJt_loc, coeff, adjJt_loc, u_loc, v_loc); + + gradv[i + Q * 0] = wdetJ[i] * v_loc[0]; + gradv[i + Q * 1] = wdetJ[i] * v_loc[1]; + } + } + return 0; +} + +#endif // PALACE_LIBCEED_HCURL_MASS_QF_H diff --git a/palace/fem/qfunctions/hdiv_build_qf.h b/palace/fem/qfunctions/hdiv_build_qf.h new file mode 100644 index 000000000..ffc395703 --- /dev/null +++ b/palace/fem/qfunctions/hdiv_build_qf.h @@ -0,0 +1,99 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef PALACE_LIBCEED_HDIV_BUILD_QF_H +#define PALACE_LIBCEED_HDIV_BUILD_QF_H + +#include "coeff_qf.h" +#include "utils_geom_qf.h" +#include "utils_qf.h" + +// Build functions replace active vector output with quadrature point data, stored as a +// symmetric matrix, and remove active vector input. + +CEED_QFUNCTION(f_build_hdiv_22)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q; + CeedScalar *qd = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + CeedScalar coeff[3], adjJt_loc[4], J_loc[4], qd_loc[3]; + CoeffUnpack2((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack22(adjJt + i, Q, adjJt_loc); + AdjJt22(adjJt_loc, J_loc); + MultAtBA22(J_loc, coeff, qd_loc); + + qd[i + Q * 0] = wdetJ[i] * qd_loc[0]; + qd[i + Q * 1] = wdetJ[i] * qd_loc[1]; + qd[i + Q * 2] = wdetJ[i] * qd_loc[2]; + } + return 0; +} + +CEED_QFUNCTION(f_build_hdiv_33)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q; + CeedScalar *qd = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + CeedScalar coeff[6], adjJt_loc[9], J_loc[9], qd_loc[6]; + CoeffUnpack3((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack33(adjJt + i, Q, adjJt_loc); + AdjJt33(adjJt_loc, J_loc); + MultAtBA33(J_loc, coeff, qd_loc); + + qd[i + Q * 0] = wdetJ[i] * qd_loc[0]; + qd[i + Q * 1] = wdetJ[i] * qd_loc[1]; + qd[i + Q * 2] = wdetJ[i] * qd_loc[2]; + qd[i + Q * 3] = wdetJ[i] * qd_loc[3]; + qd[i + Q * 4] = wdetJ[i] * qd_loc[4]; + qd[i + Q * 5] = wdetJ[i] * qd_loc[5]; + } + return 0; +} + +CEED_QFUNCTION(f_build_hdiv_21)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q; + CeedScalar *qd = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + CeedScalar coeff[3], adjJt_loc[2], J_loc[2], qd_loc[1]; + CoeffUnpack2((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack21(adjJt + i, Q, adjJt_loc); + AdjJt21(adjJt_loc, J_loc); + MultAtBA21(J_loc, coeff, qd_loc); + + qd[i + Q * 0] = wdetJ[i] * qd_loc[0]; + } + return 0; +} + +CEED_QFUNCTION(f_build_hdiv_32)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q; + CeedScalar *qd = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + CeedScalar coeff[6], adjJt_loc[6], J_loc[6], qd_loc[3]; + CoeffUnpack3((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack32(adjJt + i, Q, adjJt_loc); + AdjJt32(adjJt_loc, J_loc); + MultAtBA32(J_loc, coeff, qd_loc); + + qd[i + Q * 0] = wdetJ[i] * qd_loc[0]; + qd[i + Q * 1] = wdetJ[i] * qd_loc[1]; + qd[i + Q * 2] = wdetJ[i] * qd_loc[2]; + } + return 0; +} + +#endif // PALACE_LIBCEED_HDIV_BUILD_QF_H diff --git a/palace/fem/qfunctions/hdiv_qf.h b/palace/fem/qfunctions/hdiv_qf.h index 927fd67fb..eca04ff74 100644 --- a/palace/fem/qfunctions/hdiv_qf.h +++ b/palace/fem/qfunctions/hdiv_qf.h @@ -4,189 +4,96 @@ #ifndef PALACE_LIBCEED_HDIV_QF_H #define PALACE_LIBCEED_HDIV_QF_H +#include "coeff_qf.h" +#include "utils_geom_qf.h" #include "utils_qf.h" -#include "vecfemass_qf.h" -// libCEED QFunction for building quadrature data for an H(div) mass operator with a scalar -// constant coefficient. -CEED_QFUNCTION(f_build_hdiv_const_scalar)(void *ctx, CeedInt Q, const CeedScalar *const *in, - CeedScalar *const *out) +// libCEED QFunctions for H(div) operators (Piola transformation u = J / det(J) ̂u). +// Note: J / det(J) = adj(adj(J)^T / det(J))^T +// in[0] is geometry quadrature data, shape [ncomp=2+space_dim*dim, Q] +// in[1] is active vector, shape [qcomp=dim, ncomp=1, Q] +// out[0] is active vector, shape [qcomp=dim, ncomp=1, Q] + +CEED_QFUNCTION(f_apply_hdiv_22)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) { - // At every quadrature point, compute qw / det(J) J^T C J and store the symmetric part of - // the result. - // in[0] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[1] is quadrature weights, size (Q) - VectorFEMassContext *bc = (VectorFEMassContext *)ctx; - const CeedScalar coeff = bc->coeff; - const CeedScalar *J = in[0], *qw = in[1]; - CeedScalar *qd = out[0]; - switch (10 * bc->space_dim + bc->dim) + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *u = in[1]; + CeedScalar *v = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { - case 11: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qd[i] = qw[i] * coeff * J[i]; - } - break; - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ21(J + i, Q, &coeff, 1, 1, qw[i], Q, qd + i); - } - break; - case 22: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ22(J + i, Q, &coeff, 1, 1, qw[i], Q, qd + i); - } - break; - case 32: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ32(J + i, Q, &coeff, 1, 1, qw[i], Q, qd + i); - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ33(J + i, Q, &coeff, 1, 1, qw[i], Q, qd + i); - } - break; + const CeedScalar u_loc[2] = {u[i + Q * 0], u[i + Q * 1]}; + CeedScalar coeff[3], adjJt_loc[4], J_loc[4], v_loc[2]; + CoeffUnpack2((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack22(adjJt + i, Q, adjJt_loc); + AdjJt22(adjJt_loc, J_loc); + MultAtBCx22(J_loc, coeff, J_loc, u_loc, v_loc); + + v[i + Q * 0] = wdetJ[i] * v_loc[0]; + v[i + Q * 1] = wdetJ[i] * v_loc[1]; } return 0; } -// libCEED QFunction for building quadrature data for an H(div) mass operator with a scalar -// coefficient evaluated at quadrature points. -CEED_QFUNCTION(f_build_hdiv_quad_scalar)(void *ctx, CeedInt Q, const CeedScalar *const *in, - CeedScalar *const *out) +CEED_QFUNCTION(f_apply_hdiv_33)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) { - // At every quadrature point, compute qw / det(J) J^T C J and store the symmetric part of - // the result. - // in[0] is coefficients with shape [ncomp=1, Q] - // in[1] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[2] is quadrature weights, size (Q) - VectorFEMassContext *bc = (VectorFEMassContext *)ctx; - const CeedScalar *c = in[0], *J = in[1], *qw = in[2]; - CeedScalar *qd = out[0]; - switch (10 * bc->space_dim + bc->dim) + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *u = in[1]; + CeedScalar *v = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { - case 11: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qd[i] = qw[i] * c[i] * J[i]; - } - break; - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ21(J + i, Q, c + i, Q, 1, qw[i], Q, qd + i); - } - break; - case 22: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ22(J + i, Q, c + i, Q, 1, qw[i], Q, qd + i); - } - break; - case 32: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ32(J + i, Q, c + i, Q, 1, qw[i], Q, qd + i); - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ33(J + i, Q, c + i, Q, 1, qw[i], Q, qd + i); - } - break; + const CeedScalar u_loc[3] = {u[i + Q * 0], u[i + Q * 1], u[i + Q * 2]}; + CeedScalar coeff[6], adjJt_loc[9], J_loc[9], v_loc[3]; + CoeffUnpack3((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack33(adjJt + i, Q, adjJt_loc); + AdjJt33(adjJt_loc, J_loc); + MultAtBCx33(J_loc, coeff, J_loc, u_loc, v_loc); + + v[i + Q * 0] = wdetJ[i] * v_loc[0]; + v[i + Q * 1] = wdetJ[i] * v_loc[1]; + v[i + Q * 2] = wdetJ[i] * v_loc[2]; } return 0; } -// libCEED QFunction for building quadrature data for an H(div) mass operator with a vector -// coefficient evaluated at quadrature points. -CEED_QFUNCTION(f_build_hdiv_quad_vector)(void *ctx, CeedInt Q, const CeedScalar *const *in, - CeedScalar *const *out) +CEED_QFUNCTION(f_apply_hdiv_21)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) { - // At every quadrature point, compute qw / det(J) J^T C J and store the symmetric part of - // the result. - // in[0] is coefficients with shape [ncomp=space_dim, Q] - // in[1] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[2] is quadrature weights, size (Q) - VectorFEMassContext *bc = (VectorFEMassContext *)ctx; - const CeedScalar *c = in[0], *J = in[1], *qw = in[2]; - CeedScalar *qd = out[0]; - switch (10 * bc->space_dim + bc->dim) + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *u = in[1]; + CeedScalar *v = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ21(J + i, Q, c + i, Q, 2, qw[i], Q, qd + i); - } - break; - case 22: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ22(J + i, Q, c + i, Q, 2, qw[i], Q, qd + i); - } - break; - case 32: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ32(J + i, Q, c + i, Q, 3, qw[i], Q, qd + i); - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ33(J + i, Q, c + i, Q, 3, qw[i], Q, qd + i); - } - break; + const CeedScalar u_loc[1] = {u[i + Q * 0]}; + CeedScalar coeff[3], adjJt_loc[2], J_loc[2], v_loc[2]; + CoeffUnpack2((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack21(adjJt + i, Q, adjJt_loc); + AdjJt21(adjJt_loc, J_loc); + MultAtBCx21(J_loc, coeff, J_loc, u_loc, v_loc); + + v[i + Q * 0] = wdetJ[i] * v_loc[0]; } return 0; } -// libCEED QFunction for building quadrature data for an H(div) mass operator with a matrix -// coefficient evaluated at quadrature points. -CEED_QFUNCTION(f_build_hdiv_quad_matrix)(void *ctx, CeedInt Q, const CeedScalar *const *in, - CeedScalar *const *out) +CEED_QFUNCTION(f_apply_hdiv_32)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) { - // At every quadrature point, compute qw / det(J) J^T C J and store the symmetric part of - // the result. - // in[0] is coefficients with shape [ncomp=space_dim*(space_dim+1)/2, Q] - // in[1] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[2] is quadrature weights, size (Q) - VectorFEMassContext *bc = (VectorFEMassContext *)ctx; - const CeedScalar *c = in[0], *J = in[1], *qw = in[2]; - CeedScalar *qd = out[0]; - switch (10 * bc->space_dim + bc->dim) + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *u = in[1]; + CeedScalar *v = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ21(J + i, Q, c + i, Q, 3, qw[i], Q, qd + i); - } - break; - case 22: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ22(J + i, Q, c + i, Q, 3, qw[i], Q, qd + i); - } - break; - case 32: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ32(J + i, Q, c + i, Q, 6, qw[i], Q, qd + i); - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - MultJtCJ33(J + i, Q, c + i, Q, 6, qw[i], Q, qd + i); - } - break; + const CeedScalar u_loc[2] = {u[i + Q * 0], u[i + Q * 1]}; + CeedScalar coeff[6], adjJt_loc[6], J_loc[6], v_loc[3]; + CoeffUnpack3((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack32(adjJt + i, Q, adjJt_loc); + AdjJt32(adjJt_loc, J_loc); + MultAtBCx32(J_loc, coeff, J_loc, u_loc, v_loc); + + v[i + Q * 0] = wdetJ[i] * v_loc[0]; + v[i + Q * 1] = wdetJ[i] * v_loc[1]; } return 0; } diff --git a/palace/fem/qfunctions/hdivmass_build_qf.h b/palace/fem/qfunctions/hdivmass_build_qf.h new file mode 100644 index 000000000..b9dbd650f --- /dev/null +++ b/palace/fem/qfunctions/hdivmass_build_qf.h @@ -0,0 +1,109 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef PALACE_LIBCEED_HDIV_MASS_BUILD_QF_H +#define PALACE_LIBCEED_HDIV_MASS_BUILD_QF_H + +#include "coeff_qf.h" +#include "utils_geom_qf.h" +#include "utils_qf.h" + +// Build functions replace active vector output with quadrature point data, stored as a +// symmetric matrix, and remove active vector input. + +CEED_QFUNCTION(f_build_hdivmass_22)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *qw = in[1]; + CeedScalar *__restrict__ qd1 = out[0], *__restrict__ qd2 = out[0] + 3 * Q; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + { + CeedScalar coeff[3], adjJt_loc[4], qd_loc[3]; + CoeffUnpack2((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack22(adjJt + i, Q, adjJt_loc); + MultAtBA22(adjJt_loc, coeff, qd_loc); + + qd1[i + Q * 0] = wdetJ[i] * qd_loc[0]; + qd1[i + Q * 1] = wdetJ[i] * qd_loc[1]; + qd1[i + Q * 2] = wdetJ[i] * qd_loc[2]; + } + { + const CeedScalar coeff = + CoeffUnpack1(CoeffPairSecond2((const CeedIntScalar *)ctx), (CeedInt)attr[i]); + + qd2[i] = coeff * qw[i] * qw[i] / wdetJ[i]; + } + } + return 0; +} + +CEED_QFUNCTION(f_build_hdivmass_33)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q; + CeedScalar *__restrict__ qd1 = out[0], *__restrict__ qd2 = out[0] + 6 * Q; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + { + CeedScalar coeff[6], adjJt_loc[9], qd_loc[6]; + CoeffUnpack3((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack33(adjJt + i, Q, adjJt_loc); + MultAtBA33(adjJt_loc, coeff, qd_loc); + + qd1[i + Q * 0] = wdetJ[i] * qd_loc[0]; + qd1[i + Q * 1] = wdetJ[i] * qd_loc[1]; + qd1[i + Q * 2] = wdetJ[i] * qd_loc[2]; + qd1[i + Q * 3] = wdetJ[i] * qd_loc[3]; + qd1[i + Q * 4] = wdetJ[i] * qd_loc[4]; + qd1[i + Q * 5] = wdetJ[i] * qd_loc[5]; + } + { + CeedScalar coeff[6], adjJt_loc[9], J_loc[9], qd_loc[6]; + CoeffUnpack3(CoeffPairSecond3((const CeedIntScalar *)ctx), (CeedInt)attr[i], coeff); + MatUnpack33(adjJt + i, Q, adjJt_loc); + AdjJt33(adjJt_loc, J_loc); + MultAtBA33(J_loc, coeff, qd_loc); + + qd2[i + Q * 0] = wdetJ[i] * qd_loc[0]; + qd2[i + Q * 1] = wdetJ[i] * qd_loc[1]; + qd2[i + Q * 2] = wdetJ[i] * qd_loc[2]; + qd2[i + Q * 3] = wdetJ[i] * qd_loc[3]; + qd2[i + Q * 4] = wdetJ[i] * qd_loc[4]; + qd2[i + Q * 5] = wdetJ[i] * qd_loc[5]; + } + } + return 0; +} + +CEED_QFUNCTION(f_build_hdivmass_32)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *qw = in[1]; + CeedScalar *__restrict__ qd1 = out[0], *__restrict__ qd2 = out[0] + 3 * Q; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + { + CeedScalar coeff[6], adjJt_loc[6], qd_loc[3]; + CoeffUnpack3((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack32(adjJt + i, Q, adjJt_loc); + MultAtBA32(adjJt_loc, coeff, qd_loc); + + qd1[i + Q * 0] = wdetJ[i] * qd_loc[0]; + qd1[i + Q * 1] = wdetJ[i] * qd_loc[1]; + qd1[i + Q * 2] = wdetJ[i] * qd_loc[2]; + } + { + const CeedScalar coeff = + CoeffUnpack1(CoeffPairSecond3((const CeedIntScalar *)ctx), (CeedInt)attr[i]); + + qd2[i] = coeff * qw[i] * qw[i] / wdetJ[i]; + } + } + return 0; +} + +#endif // PALACE_LIBCEED_CURLCURL_MASS_BUILD_QF_H diff --git a/palace/fem/qfunctions/hdivmass_qf.h b/palace/fem/qfunctions/hdivmass_qf.h new file mode 100644 index 000000000..bc933dd77 --- /dev/null +++ b/palace/fem/qfunctions/hdivmass_qf.h @@ -0,0 +1,123 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef PALACE_LIBCEED_HDIV_MASS_QF_H +#define PALACE_LIBCEED_HDIV_MASS_QF_H + +#include "coeff_qf.h" +#include "utils_geom_qf.h" +#include "utils_qf.h" + +// libCEED QFunctions for H(div) + H(curl) mass operators in 3D (Piola transformations u = +// J / det(J) ̂u and u = adj(J)^T / det(J) ̂u). +// Note: J / det(J) = adj(adj(J)^T / det(J))^T +// in[0] is geometry quadrature data, shape [ncomp=2+space_dim*dim, Q] +// in[1] is active vector, shape [qcomp=dim, ncomp=1, Q] +// in[2] is active vector curl, shape [qcomp=dim, ncomp=1, Q] +// out[0] is active vector, shape [qcomp=dim, ncomp=1, Q] +// out[1] is active vector curl, shape [qcomp=dim, ncomp=1, Q] + +// In 2D, this actually uses the L2 Piola transformation on the curl (u = 1 / det(J) ̂u) and +// the curl is has qcomp=1. +// in[0] is geometry quadrature data, shape [ncomp=2+space_dim*dim, Q] +// in[1] is quadrature weights, shape [Q] +// in[2] is active vector, shape [qcomp=dim, ncomp=1, Q] +// in[3] is active vector curl, shape [ncomp=1, Q] +// out[0] is active vector, shape [qcomp=dim, ncomp=1, Q] +// out[1] is active vector curl, shape [ncomp=1, Q] + +CEED_QFUNCTION(f_apply_hdivmass_22)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *qw = in[1], + *u = in[2], *curlu = in[3]; + CeedScalar *__restrict__ v = out[0], *__restrict__ curlv = out[1]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + { + const CeedScalar u_loc[2] = {u[i + Q * 0], u[i + Q * 1]}; + CeedScalar coeff[3], adjJt_loc[4], v_loc[2]; + CoeffUnpack2((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack22(adjJt + i, Q, adjJt_loc); + MultAtBCx22(adjJt_loc, coeff, adjJt_loc, u_loc, v_loc); + + v[i + Q * 0] = wdetJ[i] * v_loc[0]; + v[i + Q * 1] = wdetJ[i] * v_loc[1]; + } + { + const CeedScalar coeff = + CoeffUnpack1(CoeffPairSecond2((const CeedIntScalar *)ctx), (CeedInt)attr[i]); + + curlv[i] = (coeff * qw[i] * qw[i] / wdetJ[i]) * curlu[i]; + } + } + return 0; +} + +CEED_QFUNCTION(f_apply_hdivmass_33)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *u = in[1], + *curlu = in[2]; + CeedScalar *__restrict__ v = out[0], *__restrict__ curlv = out[1]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + { + const CeedScalar u_loc[3] = {u[i + Q * 0], u[i + Q * 1], u[i + Q * 2]}; + CeedScalar coeff[6], adjJt_loc[9], v_loc[3]; + CoeffUnpack3((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack33(adjJt + i, Q, adjJt_loc); + MultAtBCx33(adjJt_loc, coeff, adjJt_loc, u_loc, v_loc); + + v[i + Q * 0] = wdetJ[i] * v_loc[0]; + v[i + Q * 1] = wdetJ[i] * v_loc[1]; + v[i + Q * 2] = wdetJ[i] * v_loc[2]; + } + { + const CeedScalar u_loc[3] = {curlu[i + Q * 0], curlu[i + Q * 1], curlu[i + Q * 2]}; + CeedScalar coeff[6], adjJt_loc[9], J_loc[9], v_loc[3]; + CoeffUnpack3(CoeffPairSecond3((const CeedIntScalar *)ctx), (CeedInt)attr[i], coeff); + MatUnpack33(adjJt + i, Q, adjJt_loc); + AdjJt33(adjJt_loc, J_loc); + MultAtBCx33(J_loc, coeff, J_loc, u_loc, v_loc); + + curlv[i + Q * 0] = wdetJ[i] * v_loc[0]; + curlv[i + Q * 1] = wdetJ[i] * v_loc[1]; + curlv[i + Q * 2] = wdetJ[i] * v_loc[2]; + } + } + return 0; +} + +CEED_QFUNCTION(f_apply_hdivmass_32)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *qw = in[1], + *u = in[2], *curlu = in[3]; + CeedScalar *__restrict__ v = out[0], *__restrict__ curlv = out[1]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + { + const CeedScalar u_loc[2] = {u[i + Q * 0], u[i + Q * 1]}; + CeedScalar coeff[6], adjJt_loc[6], v_loc[3]; + CoeffUnpack3((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack32(adjJt + i, Q, adjJt_loc); + MultAtBCx32(adjJt_loc, coeff, adjJt_loc, u_loc, v_loc); + + v[i + Q * 0] = wdetJ[i] * v_loc[0]; + v[i + Q * 1] = wdetJ[i] * v_loc[1]; + } + { + const CeedScalar coeff = + CoeffUnpack1(CoeffPairSecond3((const CeedIntScalar *)ctx), (CeedInt)attr[i]); + + curlv[i] = (coeff * qw[i] * qw[i] / wdetJ[i]) * curlu[i]; + } + } + return 0; +} + +#endif // PALACE_LIBCEED_CURLCURL_MASS_QF_H diff --git a/palace/fem/qfunctions/l2_build_qf.h b/palace/fem/qfunctions/l2_build_qf.h new file mode 100644 index 000000000..40ce42dbc --- /dev/null +++ b/palace/fem/qfunctions/l2_build_qf.h @@ -0,0 +1,68 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef PALACE_LIBCEED_L2_BUILD_QF_H +#define PALACE_LIBCEED_L2_BUILD_QF_H + +#include "coeff_qf.h" + +// Build functions replace active vector output with quadrature point data, stored as a +// symmetric matrix, and remove active vector input. + +CEED_QFUNCTION(f_build_l2_1)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *qw = in[1]; + CeedScalar *qd = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + const CeedScalar coeff = CoeffUnpack1((const CeedIntScalar *)ctx, (CeedInt)attr[i]); + + qd[i] = coeff * qw[i] * qw[i] / wdetJ[i]; + } + return 0; +} + +CEED_QFUNCTION(f_build_l2_2)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *qw = in[1]; + CeedScalar *qd = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + CeedScalar coeff[3]; + CoeffUnpack2((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + const CeedScalar w = qw[i] * qw[i] / wdetJ[i]; + + qd[i + Q * 0] = w * coeff[0]; + qd[i + Q * 1] = w * coeff[1]; + qd[i + Q * 2] = w * coeff[2]; + } + return 0; +} + +CEED_QFUNCTION(f_build_l2_3)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *qw = in[1]; + CeedScalar *qd = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + CeedScalar coeff[6]; + CoeffUnpack3((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + const CeedScalar w = qw[i] * qw[i] / wdetJ[i]; + + qd[i + Q * 0] = w * coeff[0]; + qd[i + Q * 1] = w * coeff[1]; + qd[i + Q * 2] = w * coeff[2]; + qd[i + Q * 3] = w * coeff[3]; + qd[i + Q * 4] = w * coeff[4]; + qd[i + Q * 5] = w * coeff[5]; + } + return 0; +} + +#endif // PALACE_LIBCEED_L2_BUILD_QF_H diff --git a/palace/fem/qfunctions/l2_qf.h b/palace/fem/qfunctions/l2_qf.h new file mode 100644 index 000000000..738c67e1a --- /dev/null +++ b/palace/fem/qfunctions/l2_qf.h @@ -0,0 +1,72 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef PALACE_LIBCEED_L2_QF_H +#define PALACE_LIBCEED_L2_QF_H + +#include "coeff_qf.h" + +// libCEED QFunctions for L2 operators (Piola transformation u = 1 / det(J) ̂u). +// in[0] is geometry quadrature data, shape [ncomp=2+space_dim*dim, Q] +// in[1] is quadrature weights, shape [Q] +// in[2] is active vector, shape [ncomp=vdim, Q] +// out[0] is active vector, shape [ncomp=vdim, Q] + +CEED_QFUNCTION(f_apply_l2_1)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *qw = in[1], *u = in[2]; + CeedScalar *v = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + const CeedScalar coeff = CoeffUnpack1((const CeedIntScalar *)ctx, (CeedInt)attr[i]); + + v[i] = (coeff * qw[i] * qw[i] / wdetJ[i]) * u[i]; + } + return 0; +} + +CEED_QFUNCTION(f_apply_l2_2)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *qw = in[1], *u = in[2]; + CeedScalar *v = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + CeedScalar coeff[3]; + CoeffUnpack2((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + const CeedScalar w = qw[i] * qw[i] / wdetJ[i]; + + const CeedScalar u0 = u[i + Q * 0]; + const CeedScalar u1 = u[i + Q * 1]; + v[i + Q * 0] = w * (coeff[0] * u0 + coeff[1] * u1); + v[i + Q * 1] = w * (coeff[1] * u0 + coeff[2] * u1); + } + return 0; +} + +CEED_QFUNCTION(f_apply_l2_3)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *qw = in[1], *u = in[2]; + CeedScalar *v = out[0]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + CeedScalar coeff[6]; + CoeffUnpack3((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + const CeedScalar w = qw[i] * qw[i] / wdetJ[i]; + + const CeedScalar u0 = u[i + Q * 0]; + const CeedScalar u1 = u[i + Q * 1]; + const CeedScalar u2 = u[i + Q * 2]; + v[i + Q * 0] = w * (coeff[0] * u0 + coeff[1] * u1 + coeff[2] * u2); + v[i + Q * 1] = w * (coeff[1] * u0 + coeff[3] * u1 + coeff[4] * u2); + v[i + Q * 2] = w * (coeff[2] * u0 + coeff[4] * u1 + coeff[5] * u2); + } + return 0; +} + +#endif // PALACE_LIBCEED_L2_QF_H diff --git a/palace/fem/qfunctions/l2mass_build_qf.h b/palace/fem/qfunctions/l2mass_build_qf.h new file mode 100644 index 000000000..31af99300 --- /dev/null +++ b/palace/fem/qfunctions/l2mass_build_qf.h @@ -0,0 +1,131 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef PALACE_LIBCEED_L2_MASS_BUILD_QF_H +#define PALACE_LIBCEED_L2_MASS_BUILD_QF_H + +#include "coeff_qf.h" +#include "utils_geom_qf.h" +#include "utils_qf.h" + +// Build functions replace active vector output with quadrature point data, stored as a +// symmetric matrix, and remove active vector input. + +CEED_QFUNCTION(f_build_l2mass_22)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *qw = in[1]; + CeedScalar *qd1 = out[0], *qd2 = out[0] + 3 * Q; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + { + CeedScalar coeff[3], adjJt_loc[4], J_loc[4], qd_loc[3]; + CoeffUnpack2((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack22(adjJt + i, Q, adjJt_loc); + AdjJt22(adjJt_loc, J_loc); + MultAtBA22(J_loc, coeff, qd_loc); + + qd1[i + Q * 0] = wdetJ[i] * qd_loc[0]; + qd1[i + Q * 1] = wdetJ[i] * qd_loc[1]; + qd1[i + Q * 2] = wdetJ[i] * qd_loc[2]; + } + { + const CeedScalar coeff = + CoeffUnpack1(CoeffPairSecond2((const CeedIntScalar *)ctx), (CeedInt)attr[i]); + + qd2[i] = coeff * qw[i] * qw[i] / wdetJ[i]; + } + } + return 0; +} + +CEED_QFUNCTION(f_build_l2mass_33)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *qw = in[1]; + CeedScalar *qd1 = out[0], *qd2 = out[0] + 6 * Q; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + { + CeedScalar coeff[6], adjJt_loc[9], J_loc[9], qd_loc[6]; + CoeffUnpack3((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack33(adjJt + i, Q, adjJt_loc); + AdjJt33(adjJt_loc, J_loc); + MultAtBA33(J_loc, coeff, qd_loc); + + qd1[i + Q * 0] = wdetJ[i] * qd_loc[0]; + qd1[i + Q * 1] = wdetJ[i] * qd_loc[1]; + qd1[i + Q * 2] = wdetJ[i] * qd_loc[2]; + qd1[i + Q * 3] = wdetJ[i] * qd_loc[3]; + qd1[i + Q * 4] = wdetJ[i] * qd_loc[4]; + qd1[i + Q * 5] = wdetJ[i] * qd_loc[5]; + } + { + const CeedScalar coeff = + CoeffUnpack1(CoeffPairSecond3((const CeedIntScalar *)ctx), (CeedInt)attr[i]); + + qd2[i] = coeff * qw[i] * qw[i] / wdetJ[i]; + } + } + return 0; +} + +CEED_QFUNCTION(f_build_l2mass_21)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *qw = in[1]; + CeedScalar *qd1 = out[0], *qd2 = out[0] + Q; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + { + CeedScalar coeff[3], adjJt_loc[2], J_loc[2], qd_loc[1]; + CoeffUnpack2((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack21(adjJt + i, Q, adjJt_loc); + AdjJt21(adjJt_loc, J_loc); + MultAtBA21(J_loc, coeff, qd_loc); + + qd1[i + Q * 0] = wdetJ[i] * qd_loc[0]; + } + { + const CeedScalar coeff = + CoeffUnpack1(CoeffPairSecond2((const CeedIntScalar *)ctx), (CeedInt)attr[i]); + + qd2[i] = coeff * qw[i] * qw[i] / wdetJ[i]; + } + } + return 0; +} + +CEED_QFUNCTION(f_build_l2mass_32)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *qw = in[1]; + CeedScalar *qd1 = out[0], *qd2 = out[0] + 3 * Q; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + { + CeedScalar coeff[6], adjJt_loc[6], J_loc[6], qd_loc[3]; + CoeffUnpack3((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack32(adjJt + i, Q, adjJt_loc); + AdjJt32(adjJt_loc, J_loc); + MultAtBA32(J_loc, coeff, qd_loc); + + qd1[i + Q * 0] = wdetJ[i] * qd_loc[0]; + qd1[i + Q * 1] = wdetJ[i] * qd_loc[1]; + qd1[i + Q * 2] = wdetJ[i] * qd_loc[2]; + } + { + const CeedScalar coeff = + CoeffUnpack1(CoeffPairSecond3((const CeedIntScalar *)ctx), (CeedInt)attr[i]); + + qd2[i] = coeff * qw[i] * qw[i] / wdetJ[i]; + } + } + return 0; +} + +#endif // PALACE_LIBCEED_L2_MASS_BUILD_QF_H diff --git a/palace/fem/qfunctions/l2mass_qf.h b/palace/fem/qfunctions/l2mass_qf.h new file mode 100644 index 000000000..03e048f3d --- /dev/null +++ b/palace/fem/qfunctions/l2mass_qf.h @@ -0,0 +1,141 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef PALACE_LIBCEED_L2_MASS_QF_H +#define PALACE_LIBCEED_L2_MASS_QF_H + +#include "coeff_qf.h" +#include "utils_geom_qf.h" +#include "utils_qf.h" + +// libCEED QFunctions for L2 + H(div) mass operators (Piola transformations u = 1 / det(J) ̂u +// and u = J / det(J) ̂u). +// Note: J / det(J) = adj(adj(J)^T / det(J))^T +// in[0] is geometry quadrature data, shape [ncomp=2+space_dim*dim, Q] +// in[1] is quadrature weights, shape [Q] +// in[2] is active vector, shape [qcomp=dim, ncomp=1, Q] +// in[3] is active vector divergence, shape [ncomp=1, Q] +// out[0] is active vector, shape [qcomp=dim, ncomp=1, Q] +// out[1] is active vector divergence, shape [ncomp=1, Q] + +CEED_QFUNCTION(f_apply_l2mass_22)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *qw = in[1], + *u = in[2], *divu = in[3]; + CeedScalar *__restrict__ v = out[0], *__restrict__ divv = out[1]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + { + const CeedScalar u_loc[2] = {u[i + Q * 0], u[i + Q * 1]}; + CeedScalar coeff[3], adjJt_loc[4], J_loc[4], v_loc[2]; + CoeffUnpack2((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack22(adjJt + i, Q, adjJt_loc); + AdjJt22(adjJt_loc, J_loc); + MultAtBCx22(J_loc, coeff, J_loc, u_loc, v_loc); + + v[i + Q * 0] = wdetJ[i] * v_loc[0]; + v[i + Q * 1] = wdetJ[i] * v_loc[1]; + } + { + const CeedScalar coeff = + CoeffUnpack1(CoeffPairSecond2((const CeedIntScalar *)ctx), (CeedInt)attr[i]); + + divv[i] = (coeff * qw[i] * qw[i] / wdetJ[i]) * divu[i]; + } + } + return 0; +} + +CEED_QFUNCTION(f_apply_l2mass_33)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *qw = in[1], + *u = in[2], *divu = in[3]; + CeedScalar *__restrict__ v = out[0], *__restrict__ divv = out[1]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + { + const CeedScalar u_loc[3] = {u[i + Q * 0], u[i + Q * 1], u[i + Q * 2]}; + CeedScalar coeff[6], adjJt_loc[9], J_loc[9], v_loc[3]; + CoeffUnpack3((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack33(adjJt + i, Q, adjJt_loc); + AdjJt33(adjJt_loc, J_loc); + MultAtBCx33(J_loc, coeff, J_loc, u_loc, v_loc); + + v[i + Q * 0] = wdetJ[i] * v_loc[0]; + v[i + Q * 1] = wdetJ[i] * v_loc[1]; + v[i + Q * 2] = wdetJ[i] * v_loc[2]; + } + { + const CeedScalar coeff = + CoeffUnpack1(CoeffPairSecond3((const CeedIntScalar *)ctx), (CeedInt)attr[i]); + + divv[i] = (coeff * qw[i] * qw[i] / wdetJ[i]) * divu[i]; + } + } + return 0; +} + +CEED_QFUNCTION(f_apply_l2mass_21)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *qw = in[1], + *u = in[2], *divu = in[3]; + CeedScalar *__restrict__ v = out[0], *__restrict__ divv = out[1]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + { + const CeedScalar u_loc[1] = {u[i + Q * 0]}; + CeedScalar coeff[3], adjJt_loc[2], J_loc[2], v_loc[2]; + CoeffUnpack2((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack21(adjJt + i, Q, adjJt_loc); + AdjJt21(adjJt_loc, J_loc); + MultAtBCx21(J_loc, coeff, J_loc, u_loc, v_loc); + + v[i + Q * 0] = wdetJ[i] * v_loc[0]; + } + { + const CeedScalar coeff = + CoeffUnpack1(CoeffPairSecond2((const CeedIntScalar *)ctx), (CeedInt)attr[i]); + + divv[i] = (coeff * qw[i] * qw[i] / wdetJ[i]) * divu[i]; + } + } + return 0; +} + +CEED_QFUNCTION(f_apply_l2mass_32)(void *ctx, CeedInt Q, const CeedScalar *const *in, + CeedScalar *const *out) +{ + const CeedScalar *attr = in[0], *wdetJ = in[0] + Q, *adjJt = in[0] + 2 * Q, *qw = in[1], + *u = in[2], *divu = in[3]; + CeedScalar *__restrict__ v = out[0], *__restrict__ divv = out[1]; + + CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) + { + { + const CeedScalar u_loc[2] = {u[i + Q * 0], u[i + Q * 1]}; + CeedScalar coeff[6], adjJt_loc[6], J_loc[6], v_loc[3]; + CoeffUnpack3((const CeedIntScalar *)ctx, (CeedInt)attr[i], coeff); + MatUnpack32(adjJt + i, Q, adjJt_loc); + AdjJt32(adjJt_loc, J_loc); + MultAtBCx32(J_loc, coeff, J_loc, u_loc, v_loc); + + v[i + Q * 0] = wdetJ[i] * v_loc[0]; + v[i + Q * 1] = wdetJ[i] * v_loc[1]; + } + { + const CeedScalar coeff = + CoeffUnpack1(CoeffPairSecond3((const CeedIntScalar *)ctx), (CeedInt)attr[i]); + + divv[i] = (coeff * qw[i] * qw[i] / wdetJ[i]) * divu[i]; + } + } + return 0; +} + +#endif // PALACE_LIBCEED_L2_MASS_QF_H diff --git a/palace/fem/qfunctions/mass_qf.h b/palace/fem/qfunctions/mass_qf.h deleted file mode 100644 index a764b229d..000000000 --- a/palace/fem/qfunctions/mass_qf.h +++ /dev/null @@ -1,333 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -#ifndef PALACE_LIBCEED_MASS_QF_H -#define PALACE_LIBCEED_MASS_QF_H - -#include "utils_qf.h" - -struct MassContext -{ - CeedInt dim, space_dim, vdim; - CeedScalar coeff; -}; - -// libCEED QFunction for building quadrature data for a mass operator with a scalar constant -// coefficient. -CEED_QFUNCTION(f_build_mass_const_scalar)(void *ctx, CeedInt Q, const CeedScalar *const *in, - CeedScalar *const *out) -{ - // At every quadrature point, compute and store qw * c * det(J). - // in[0] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[1] is quadrature weights, size (Q) - MassContext *bc = (MassContext *)ctx; - const CeedScalar coeff = bc->coeff; - const CeedScalar *J = in[0], *qw = in[1]; - CeedScalar *qd = out[0]; - switch (10 * bc->space_dim + bc->dim) - { - case 11: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qd[i] = qw[i] * coeff * J[i]; - } - break; - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qd[i] = qw[i] * coeff * DetJ21(J + i, Q); - } - break; - case 22: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qd[i] = qw[i] * coeff * DetJ22(J + i, Q); - } - break; - case 32: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qd[i] = qw[i] * coeff * DetJ32(J + i, Q); - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qd[i] = qw[i] * coeff * DetJ33(J + i, Q); - } - break; - } - return 0; -} - -// libCEED QFunction for building quadrature data for a mass operator with a scalar -// coefficient evaluated at quadrature points. -CEED_QFUNCTION(f_build_mass_quad_scalar)(void *ctx, CeedInt Q, const CeedScalar *const *in, - CeedScalar *const *out) -{ - // At every quadrature point, compute and store qw * c * det(J). - // in[0] is coefficients, size (Q) - // in[1] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[2] is quadrature weights, size (Q) - MassContext *bc = (MassContext *)ctx; - const CeedScalar *c = in[0], *J = in[1], *qw = in[2]; - CeedScalar *qd = out[0]; - switch (10 * bc->space_dim + bc->dim) - { - case 11: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qd[i] = qw[i] * c[i] * J[i]; - } - break; - case 21: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qd[i] = qw[i] * c[i] * DetJ21(J + i, Q); - } - break; - case 22: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qd[i] = qw[i] * c[i] * DetJ22(J + i, Q); - } - break; - case 32: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qd[i] = qw[i] * c[i] * DetJ32(J + i, Q); - } - break; - case 33: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - qd[i] = qw[i] * c[i] * DetJ33(J + i, Q); - } - break; - } - return 0; -} - -// libCEED QFunction for building quadrature data for a mass operator with a vector -// coefficient evaluated at quadrature points. -CEED_QFUNCTION(f_build_mass_quad_vector)(void *ctx, CeedInt Q, const CeedScalar *const *in, - CeedScalar *const *out) -{ - // At every quadrature point, compute and store qw * det(J) C. - // in[0] is coefficients with shape [ncomp=vdim, Q] - // in[1] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[2] is quadrature weights, size (Q) - MassContext *bc = (MassContext *)ctx; - const CeedScalar *c = in[0], *J = in[1], *qw = in[2]; - CeedScalar *qd = out[0]; - switch (100 * bc->space_dim + 10 * bc->dim + bc->vdim) - { - case 212: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - const CeedScalar wdetJi = qw[i] * DetJ21(J + i, Q); - CeedPragmaSIMD for (CeedInt d = 0; d < 2; d++) - { - qd[i + Q * d] = wdetJi * c[i + Q * d]; - } - } - break; - case 222: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - const CeedScalar wdetJi = qw[i] * DetJ22(J + i, Q); - CeedPragmaSIMD for (CeedInt d = 0; d < 2; d++) - { - qd[i + Q * d] = wdetJi * c[i + Q * d]; - } - } - break; - case 323: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - const CeedScalar wdetJi = qw[i] * DetJ32(J + i, Q); - CeedPragmaSIMD for (CeedInt d = 0; d < 3; d++) - { - qd[i + Q * d] = wdetJi * c[i + Q * d]; - } - } - break; - case 333: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - const CeedScalar wdetJi = qw[i] * DetJ33(J + i, Q); - CeedPragmaSIMD for (CeedInt d = 0; d < 3; d++) - { - qd[i + Q * d] = wdetJi * c[i + Q * d]; - } - } - break; - } - return 0; -} - -// libCEED QFunction for building quadrature data for a mass operator with a matrix -// coefficient evaluated at quadrature points. -CEED_QFUNCTION(f_build_mass_quad_matrix)(void *ctx, CeedInt Q, const CeedScalar *const *in, - CeedScalar *const *out) -{ - // At every quadrature point, compute and store qw * det(J) C. - // in[0] is coefficients with shape [ncomp=vdim*(vdim+1)/2, Q] - // in[1] is Jacobians with shape [dim, ncomp=space_dim, Q] - // in[2] is quadrature weights, size (Q) - MassContext *bc = (MassContext *)ctx; - const CeedScalar *c = in[0], *J = in[1], *qw = in[2]; - CeedScalar *qd = out[0]; - switch (100 * bc->space_dim + 10 * bc->dim + bc->vdim) - { - case 212: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - const CeedScalar wdetJi = qw[i] * DetJ21(J + i, Q); - CeedPragmaSIMD for (CeedInt d = 0; d < 3; d++) - { - qd[i + Q * d] = wdetJi * c[i + Q * d]; - } - } - break; - case 222: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - const CeedScalar wdetJi = qw[i] * DetJ22(J + i, Q); - CeedPragmaSIMD for (CeedInt d = 0; d < 3; d++) - { - qd[i + Q * d] = wdetJi * c[i + Q * d]; - } - } - break; - case 323: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - const CeedScalar wdetJi = qw[i] * DetJ32(J + i, Q); - CeedPragmaSIMD for (CeedInt d = 0; d < 6; d++) - { - qd[i + Q * d] = wdetJi * c[i + Q * d]; - } - } - break; - case 333: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - const CeedScalar wdetJi = qw[i] * DetJ33(J + i, Q); - CeedPragmaSIMD for (CeedInt d = 0; d < 6; d++) - { - qd[i + Q * d] = wdetJi * c[i + Q * d]; - } - } - break; - } - return 0; -} - -// libCEED QFunction for applying a mass operator with a scalar coefficient. -CEED_QFUNCTION(f_apply_mass_scalar)(void *ctx, CeedInt Q, const CeedScalar *const *in, - CeedScalar *const *out) -{ - // in[0], out[0] have shape [ncomp=vdim, Q] - MassContext *bc = (MassContext *)ctx; - const CeedScalar *u = in[0], *qd = in[1]; - CeedScalar *v = out[0]; - switch (bc->vdim) - { - case 1: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - v[i] = qd[i] * u[i]; - } - break; - case 2: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - const CeedScalar qdi = qd[i]; - CeedPragmaSIMD for (CeedInt d = 0; d < 2; d++) - { - v[i + Q * d] = qdi * u[i + Q * d]; - } - } - break; - case 3: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - const CeedScalar qdi = qd[i]; - CeedPragmaSIMD for (CeedInt d = 0; d < 3; d++) - { - v[i + Q * d] = qdi * u[i + Q * d]; - } - } - break; - } - return 0; -} - -// libCEED QFunction for applying a mass operator with a vector coefficient. -CEED_QFUNCTION(f_apply_mass_vector)(void *ctx, CeedInt Q, const CeedScalar *const *in, - CeedScalar *const *out) -{ - // in[0], out[0] have shape [ncomp=vdim, Q] - MassContext *bc = (MassContext *)ctx; - const CeedScalar *u = in[0], *qd = in[1]; - CeedScalar *v = out[0]; - switch (bc->vdim) - { - case 2: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - CeedPragmaSIMD for (CeedInt d = 0; d < 2; d++) - { - v[i + Q * d] = qd[i + Q * d] * u[i + Q * d]; - } - } - break; - case 3: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - CeedPragmaSIMD for (CeedInt d = 0; d < 3; d++) - { - v[i + Q * d] = qd[i + Q * d] * u[i + Q * d]; - } - } - break; - } - return 0; -} - -// libCEED QFunction for applying a mass operator with a matrix coefficient. -CEED_QFUNCTION(f_apply_mass_matrix)(void *ctx, CeedInt Q, const CeedScalar *const *in, - CeedScalar *const *out) -{ - // in[0], out[0] have shape [ncomp=vdim, Q] - MassContext *bc = (MassContext *)ctx; - const CeedScalar *u = in[0], *qd = in[1]; - CeedScalar *v = out[0]; - switch (bc->vdim) - { - case 2: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - const CeedScalar u0 = u[i + Q * 0]; - const CeedScalar u1 = u[i + Q * 1]; - v[i + Q * 0] = qd[i + Q * 0] * u0 + qd[i + Q * 1] * u1; - v[i + Q * 1] = qd[i + Q * 1] * u0 + qd[i + Q * 2] * u1; - } - break; - case 3: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - const CeedScalar u0 = u[i + Q * 0]; - const CeedScalar u1 = u[i + Q * 1]; - const CeedScalar u2 = u[i + Q * 2]; - v[i + Q * 0] = qd[i + Q * 0] * u0 + qd[i + Q * 1] * u1 + qd[i + Q * 2] * u2; - v[i + Q * 1] = qd[i + Q * 1] * u0 + qd[i + Q * 3] * u1 + qd[i + Q * 4] * u2; - v[i + Q * 2] = qd[i + Q * 2] * u0 + qd[i + Q * 4] * u1 + qd[i + Q * 5] * u2; - } - break; - } - return 0; -} - -#endif // PALACE_LIBCEED_MASS_QF_H diff --git a/palace/fem/qfunctions/utils_geom_qf.h b/palace/fem/qfunctions/utils_geom_qf.h new file mode 100644 index 000000000..b71970a0e --- /dev/null +++ b/palace/fem/qfunctions/utils_geom_qf.h @@ -0,0 +1,107 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef PALACE_LIBCEED_UTILS_GEOM_QF_H +#define PALACE_LIBCEED_UTILS_GEOM_QF_H + +#include + +CEED_QFUNCTION_HELPER CeedScalar DetJ22(const CeedScalar J[4]) +{ + // J: 0 2 + // 1 3 + return J[0] * J[3] - J[1] * J[2]; +} + +CEED_QFUNCTION_HELPER CeedScalar DetJ33(const CeedScalar J[9]) +{ + // J: 0 3 6 + // 1 4 7 + // 2 5 8 + return J[0] * (J[4] * J[8] - J[5] * J[7]) - J[1] * (J[3] * J[8] - J[5] * J[6]) + + J[2] * (J[3] * J[7] - J[4] * J[6]); +} + +CEED_QFUNCTION_HELPER CeedScalar DetJ21(const CeedScalar J[2]) +{ + // J: 0 + // 1 + return sqrt(J[0] * J[0] + J[1] * J[1]); +} + +CEED_QFUNCTION_HELPER CeedScalar DetJ32(const CeedScalar J[6]) +{ + // J: 0 3 + // 1 4 + // 2 5 + const CeedScalar E = J[0] * J[0] + J[1] * J[1] + J[2] * J[2]; + const CeedScalar G = J[3] * J[3] + J[4] * J[4] + J[5] * J[5]; + const CeedScalar F = J[0] * J[3] + J[1] * J[4] + J[2] * J[5]; + return sqrt(E * G - F * F); +} + +template +CEED_QFUNCTION_HELPER CeedScalar AdjJt22(const CeedScalar J[4], CeedScalar adjJt[4]) +{ + // Compute adj(J)^T / det(J) and store the result. + // J: 0 2 adj(J): J22 -J12 + // 1 3 -J21 J11 + adjJt[0] = J[3]; + adjJt[1] = -J[2]; + adjJt[2] = -J[1]; + adjJt[3] = J[0]; + return ComputeDet ? (J[0] * J[3] - J[1] * J[2]) : 0.0; +} + +template +CEED_QFUNCTION_HELPER CeedScalar AdjJt33(const CeedScalar J[9], CeedScalar adjJt[9]) +{ + // Compute adj(J)^T / det(J) and store the result. + // J: 0 3 6 + // 1 4 7 + // 2 5 8 + adjJt[0] = J[4] * J[8] - J[7] * J[5]; + adjJt[3] = J[7] * J[2] - J[1] * J[8]; + adjJt[6] = J[1] * J[5] - J[4] * J[2]; + adjJt[1] = J[6] * J[5] - J[3] * J[8]; + adjJt[4] = J[0] * J[8] - J[6] * J[2]; + adjJt[7] = J[3] * J[2] - J[0] * J[5]; + adjJt[2] = J[3] * J[7] - J[6] * J[4]; + adjJt[5] = J[6] * J[1] - J[0] * J[7]; + adjJt[8] = J[0] * J[4] - J[3] * J[1]; + return ComputeDet ? (J[0] * adjJt[0] + J[1] * adjJt[1] + J[2] * adjJt[2]) : 0.0; +} + +template +CEED_QFUNCTION_HELPER CeedScalar AdjJt21(const CeedScalar J[2], CeedScalar adjJt[2]) +{ + // Compute adj(J)^T / det(J) and store the result. + // J: 0 adj(J): 1/sqrt(J^T J) J^T + // 1 + const CeedScalar d = sqrt(J[0] * J[0] + J[1] * J[1]); + adjJt[0] = J[0] / d; + adjJt[1] = J[1] / d; + return ComputeDet ? d : 0.0; +} + +template +CEED_QFUNCTION_HELPER CeedScalar AdjJt32(const CeedScalar J[6], CeedScalar adjJt[6]) +{ + // Compute adj(J)^T / det(J) and store the result. + // J: 0 3 + // 1 4 + // 2 5 + const CeedScalar E = J[0] * J[0] + J[1] * J[1] + J[2] * J[2]; + const CeedScalar G = J[3] * J[3] + J[4] * J[4] + J[5] * J[5]; + const CeedScalar F = J[0] * J[3] + J[1] * J[4] + J[2] * J[5]; + const CeedScalar d = sqrt(E * G - F * F); + adjJt[0] = (G * J[0] - F * J[3]) / d; + adjJt[1] = (G * J[1] - F * J[4]) / d; + adjJt[2] = (G * J[2] - F * J[5]) / d; + adjJt[3] = (E * J[3] - F * J[0]) / d; + adjJt[4] = (E * J[4] - F * J[1]) / d; + adjJt[5] = (E * J[5] - F * J[2]) / d; + return ComputeDet ? d : 0.0; +} + +#endif // PALACE_LIBCEED_UTILS_QF_H diff --git a/palace/fem/qfunctions/utils_qf.h b/palace/fem/qfunctions/utils_qf.h index 62b0d6afc..76b322e21 100644 --- a/palace/fem/qfunctions/utils_qf.h +++ b/palace/fem/qfunctions/utils_qf.h @@ -4,1033 +4,395 @@ #ifndef PALACE_LIBCEED_UTILS_QF_H #define PALACE_LIBCEED_UTILS_QF_H -#include +CEED_QFUNCTION_HELPER void MatUnpack22(const CeedScalar *A, const CeedInt A_stride, + CeedScalar A_loc[4]) +{ + A_loc[0] = A[A_stride * 0]; + A_loc[1] = A[A_stride * 1]; + A_loc[2] = A[A_stride * 2]; + A_loc[3] = A[A_stride * 3]; +} -CEED_QFUNCTION_HELPER CeedScalar DetJ22(const CeedScalar *J, const CeedInt J_stride) +CEED_QFUNCTION_HELPER void MatUnpack33(const CeedScalar *A, const CeedInt A_stride, + CeedScalar A_loc[9]) { - // J: 0 2 - // 1 3 - return J[J_stride * 0] * J[J_stride * 3] - J[J_stride * 1] * J[J_stride * 2]; + A_loc[0] = A[A_stride * 0]; + A_loc[1] = A[A_stride * 1]; + A_loc[2] = A[A_stride * 2]; + A_loc[3] = A[A_stride * 3]; + A_loc[4] = A[A_stride * 4]; + A_loc[5] = A[A_stride * 5]; + A_loc[6] = A[A_stride * 6]; + A_loc[7] = A[A_stride * 7]; + A_loc[8] = A[A_stride * 8]; } -CEED_QFUNCTION_HELPER CeedScalar DetJ21(const CeedScalar *J, const CeedInt J_stride) +CEED_QFUNCTION_HELPER void MatUnpack21(const CeedScalar *A, const CeedInt A_stride, + CeedScalar A_loc[2]) { - // J: 0 - // 1 - return sqrt(J[J_stride * 0] * J[J_stride * 0] + J[J_stride * 1] * J[J_stride * 1]); + A_loc[0] = A[A_stride * 0]; + A_loc[1] = A[A_stride * 1]; } -CEED_QFUNCTION_HELPER CeedScalar DetJ33(const CeedScalar *J, const CeedInt J_stride) +CEED_QFUNCTION_HELPER void MatUnpack32(const CeedScalar *A, const CeedInt A_stride, + CeedScalar A_loc[6]) { - // J: 0 3 6 - // 1 4 7 - // 2 5 8 - return J[J_stride * 0] * - (J[J_stride * 4] * J[J_stride * 8] - J[J_stride * 5] * J[J_stride * 7]) - - J[J_stride * 1] * - (J[J_stride * 3] * J[J_stride * 8] - J[J_stride * 5] * J[J_stride * 6]) + - J[J_stride * 2] * - (J[J_stride * 3] * J[J_stride * 7] - J[J_stride * 4] * J[J_stride * 6]); + A_loc[0] = A[A_stride * 0]; + A_loc[1] = A[A_stride * 1]; + A_loc[2] = A[A_stride * 2]; + A_loc[3] = A[A_stride * 3]; + A_loc[4] = A[A_stride * 4]; + A_loc[5] = A[A_stride * 5]; } -CEED_QFUNCTION_HELPER CeedScalar DetJ32(const CeedScalar *J, const CeedInt J_stride) +CEED_QFUNCTION_HELPER void MultAtBCx22(const CeedScalar A[4], const CeedScalar B[3], + const CeedScalar C[4], const CeedScalar x[2], + CeedScalar y[2]) { - // J: 0 3 - // 1 4 - // 2 5 - const CeedScalar E = J[J_stride * 0] * J[J_stride * 0] + - J[J_stride * 1] * J[J_stride * 1] + - J[J_stride * 2] * J[J_stride * 2]; - const CeedScalar G = J[J_stride * 3] * J[J_stride * 3] + - J[J_stride * 4] * J[J_stride * 4] + - J[J_stride * 5] * J[J_stride * 5]; - const CeedScalar F = J[J_stride * 0] * J[J_stride * 3] + - J[J_stride * 1] * J[J_stride * 4] + - J[J_stride * 2] * J[J_stride * 5]; - return sqrt(E * G - F * F); + // A: 0 2 B: 0 1 C: 0 2 + // 1 3 1 2 1 3 + CeedScalar z[2]; + + y[0] = C[0] * x[0] + C[2] * x[1]; + y[1] = C[1] * x[0] + C[3] * x[1]; + + z[0] = B[0] * y[0] + B[1] * y[1]; + z[1] = B[1] * y[0] + B[2] * y[1]; + + y[0] = A[0] * z[0] + A[1] * z[1]; + y[1] = A[2] * z[0] + A[3] * z[1]; } -CEED_QFUNCTION_HELPER void MultAdjJCAdjJt22(const CeedScalar *J, const CeedInt J_stride, - const CeedScalar *c, const CeedInt c_stride, - const CeedInt c_comp, const CeedScalar qw, - const CeedInt qd_stride, CeedScalar *qd) +CEED_QFUNCTION_HELPER void MultAtBCx33(const CeedScalar A[9], const CeedScalar B[6], + const CeedScalar C[9], const CeedScalar x[3], + CeedScalar y[3]) { - // Compute qw / det(J) adj(J) C adj(J)^T and store the symmetric part of the result. - // J: 0 2 adj(J): J22 -J12 qd: 0 1 - // 1 3 -J21 J11 1 2 - const CeedScalar J11 = J[J_stride * 0]; - const CeedScalar J21 = J[J_stride * 1]; - const CeedScalar J12 = J[J_stride * 2]; - const CeedScalar J22 = J[J_stride * 3]; - const CeedScalar w = qw / (J11 * J22 - J21 * J12); - if (c_comp == 3) // Matrix coefficient (symmetric) - { - // First compute entries of R = C adj(J)^T. - // c: 0 1 - // 1 2 - const CeedScalar R11 = c[c_stride * 0] * J22 - c[c_stride * 1] * J12; - const CeedScalar R21 = c[c_stride * 1] * J22 - c[c_stride * 2] * J12; - const CeedScalar R12 = -c[c_stride * 0] * J21 + c[c_stride * 1] * J11; - const CeedScalar R22 = -c[c_stride * 1] * J21 + c[c_stride * 2] * J11; - qd[qd_stride * 0] = w * (J22 * R11 - J12 * R21); - qd[qd_stride * 1] = w * (J11 * R21 - J21 * R11); - qd[qd_stride * 2] = w * (J11 * R22 - J21 * R12); - } - else if (c_comp == 2) // Vector coefficient - { - // c: 0 - // 1 - qd[qd_stride * 0] = w * (c[c_stride * 1] * J12 * J12 + c[c_stride * 0] * J22 * J22); - qd[qd_stride * 1] = -w * (c[c_stride * 1] * J11 * J12 + c[c_stride * 0] * J21 * J22); - qd[qd_stride * 2] = w * (c[c_stride * 1] * J11 * J11 + c[c_stride * 0] * J21 * J21); - } - else // Scalar coefficient - { - qd[qd_stride * 0] = w * c[c_stride * 0] * (J12 * J12 + J22 * J22); - qd[qd_stride * 1] = -w * c[c_stride * 0] * (J11 * J12 + J21 * J22); - qd[qd_stride * 2] = w * c[c_stride * 0] * (J11 * J11 + J21 * J21); - } + // A: 0 3 6 B: 0 1 2 C: 0 3 6 + // 1 4 7 1 3 4 1 4 7 + // 2 5 8 2 4 5 2 5 8 + CeedScalar z[3]; + + y[0] = C[0] * x[0] + C[3] * x[1] + C[6] * x[2]; + y[1] = C[1] * x[0] + C[4] * x[1] + C[7] * x[2]; + y[2] = C[2] * x[0] + C[5] * x[1] + C[8] * x[2]; + + z[0] = B[0] * y[0] + B[1] * y[1] + B[2] * y[2]; + z[1] = B[1] * y[0] + B[3] * y[1] + B[4] * y[2]; + z[2] = B[2] * y[0] + B[4] * y[1] + B[5] * y[2]; + + y[0] = A[0] * z[0] + A[1] * z[1] + A[2] * z[2]; + y[1] = A[3] * z[0] + A[4] * z[1] + A[5] * z[2]; + y[2] = A[6] * z[0] + A[7] * z[1] + A[8] * z[2]; } -CEED_QFUNCTION_HELPER void MultAdjJCAdjJt21(const CeedScalar *J, const CeedInt J_stride, - const CeedScalar *c, const CeedInt c_stride, - const CeedInt c_comp, const CeedScalar qw, - const CeedInt qd_stride, CeedScalar *qd) +CEED_QFUNCTION_HELPER void MultAtBCx21(const CeedScalar A[2], const CeedScalar B[3], + const CeedScalar C[2], const CeedScalar x[1], + CeedScalar y[2]) { - // Compute qw / det(J) adj(J) C adj(J)^T and store the symmetric part of the result. - // J: 0 adj(J): 1/sqrt(J^T J) J^T qd: 0 - // 1 - const CeedScalar J11 = J[J_stride * 0]; - const CeedScalar J21 = J[J_stride * 1]; - const CeedScalar d = J11 * J11 + J21 * J21; - const CeedScalar w = qw / sqrt(d); - if (c_comp == 3) // Matrix coefficient (symmetric) - { - // First compute entries of R = C adj(J)^T. - // c: 0 1 - // 1 2 - const CeedScalar R11 = c[c_stride * 0] * J11 + c[c_stride * 1] * J21; - const CeedScalar R21 = c[c_stride * 1] * J11 + c[c_stride * 2] * J21; - qd[qd_stride * 0] = w * (J11 * R11 + J21 * R21) / d; - } - else if (c_comp == 2) // Vector coefficient - { - // c: 0 - // 1 - qd[qd_stride * 0] = w * (c[c_stride * 0] * J11 * J11 + c[c_stride * 1] * J21 * J21) / d; - } - else // Scalar coefficient - { - qd[qd_stride * 0] = w * c[c_stride * 0]; - } + // A: 0 B: 0 1 C: 0 + // 1 1 2 1 + CeedScalar z[2]; + + y[0] = C[0] * x[0]; + y[1] = C[1] * x[0]; + + z[0] = B[0] * y[0] + B[1] * y[1]; + z[1] = B[1] * y[0] + B[2] * y[1]; + + y[0] = A[0] * z[0] + A[1] * z[1]; + y[1] = 0.0; } -CEED_QFUNCTION_HELPER void MultAdjJCAdjJt33(const CeedScalar *J, const CeedInt J_stride, - const CeedScalar *c, const CeedInt c_stride, - const CeedInt c_comp, const CeedScalar qw, - const CeedInt qd_stride, CeedScalar *qd) +CEED_QFUNCTION_HELPER void MultAtBCx32(const CeedScalar A[6], const CeedScalar B[6], + const CeedScalar C[6], const CeedScalar x[2], + CeedScalar y[3]) { - // Compute qw / det(J) adj(J) C adj(J)^T and store the symmetric part of the result. - // J: 0 3 6 qd: 0 1 2 - // 1 4 7 1 3 4 - // 2 5 8 2 4 5 - const CeedScalar J11 = J[J_stride * 0]; - const CeedScalar J21 = J[J_stride * 1]; - const CeedScalar J31 = J[J_stride * 2]; - const CeedScalar J12 = J[J_stride * 3]; - const CeedScalar J22 = J[J_stride * 4]; - const CeedScalar J32 = J[J_stride * 5]; - const CeedScalar J13 = J[J_stride * 6]; - const CeedScalar J23 = J[J_stride * 7]; - const CeedScalar J33 = J[J_stride * 8]; - const CeedScalar A11 = J22 * J33 - J23 * J32; - const CeedScalar A21 = J23 * J31 - J21 * J33; - const CeedScalar A31 = J21 * J32 - J22 * J31; - const CeedScalar A12 = J13 * J32 - J12 * J33; - const CeedScalar A22 = J11 * J33 - J13 * J31; - const CeedScalar A32 = J12 * J31 - J11 * J32; - const CeedScalar A13 = J12 * J23 - J13 * J22; - const CeedScalar A23 = J13 * J21 - J11 * J23; - const CeedScalar A33 = J11 * J22 - J12 * J21; - const CeedScalar w = qw / (J11 * A11 + J21 * A12 + J31 * A13); - if (c_comp == 6) // Matrix coefficient (symmetric) - { - // First compute entries of R = C adj(J)^T. - // c: 0 1 2 - // 1 3 4 - // 2 4 5 - const CeedScalar R11 = - c[c_stride * 0] * A11 + c[c_stride * 1] * A12 + c[c_stride * 2] * A13; - const CeedScalar R21 = - c[c_stride * 1] * A11 + c[c_stride * 3] * A12 + c[c_stride * 4] * A13; - const CeedScalar R31 = - c[c_stride * 2] * A11 + c[c_stride * 4] * A12 + c[c_stride * 5] * A13; - const CeedScalar R12 = - c[c_stride * 0] * A21 + c[c_stride * 1] * A22 + c[c_stride * 2] * A23; - const CeedScalar R22 = - c[c_stride * 1] * A21 + c[c_stride * 3] * A22 + c[c_stride * 4] * A23; - const CeedScalar R32 = - c[c_stride * 2] * A21 + c[c_stride * 4] * A22 + c[c_stride * 5] * A23; - const CeedScalar R13 = - c[c_stride * 0] * A31 + c[c_stride * 1] * A32 + c[c_stride * 2] * A33; - const CeedScalar R23 = - c[c_stride * 1] * A31 + c[c_stride * 3] * A32 + c[c_stride * 4] * A33; - const CeedScalar R33 = - c[c_stride * 2] * A31 + c[c_stride * 4] * A32 + c[c_stride * 5] * A33; - qd[qd_stride * 0] = w * (A11 * R11 + A12 * R21 + A13 * R31); - qd[qd_stride * 1] = w * (A11 * R12 + A12 * R22 + A13 * R32); - qd[qd_stride * 2] = w * (A11 * R13 + A12 * R23 + A13 * R33); - qd[qd_stride * 3] = w * (A21 * R12 + A22 * R22 + A23 * R32); - qd[qd_stride * 4] = w * (A21 * R13 + A22 * R23 + A23 * R33); - qd[qd_stride * 5] = w * (A31 * R13 + A32 * R23 + A33 * R33); - } - else if (c_comp == 3) // Vector coefficient - { - // c: 0 - // 1 - // 2 - qd[qd_stride * 0] = w * (c[c_stride * 0] * A11 * A11 + c[c_stride * 1] * A12 * A12 + - c[c_stride * 2] * A13 * A13); - qd[qd_stride * 1] = w * (c[c_stride * 0] * A11 * A21 + c[c_stride * 1] * A12 * A22 + - c[c_stride * 2] * A13 * A23); - qd[qd_stride * 2] = w * (c[c_stride * 0] * A11 * A31 + c[c_stride * 1] * A12 * A32 + - c[c_stride * 2] * A13 * A33); - qd[qd_stride * 3] = w * (c[c_stride * 0] * A21 * A21 + c[c_stride * 1] * A22 * A22 + - c[c_stride * 2] * A23 * A23); - qd[qd_stride * 4] = w * (c[c_stride * 0] * A21 * A31 + c[c_stride * 1] * A22 * A32 + - c[c_stride * 2] * A23 * A33); - qd[qd_stride * 5] = w * (c[c_stride * 0] * A31 * A31 + c[c_stride * 1] * A32 * A32 + - c[c_stride * 2] * A33 * A33); - } - else // Scalar coefficient - { - qd[qd_stride * 0] = w * c[c_stride * 0] * (A11 * A11 + A12 * A12 + A13 * A13); - qd[qd_stride * 1] = w * c[c_stride * 0] * (A11 * A21 + A12 * A22 + A13 * A23); - qd[qd_stride * 2] = w * c[c_stride * 0] * (A11 * A31 + A12 * A32 + A13 * A33); - qd[qd_stride * 3] = w * c[c_stride * 0] * (A21 * A21 + A22 * A22 + A23 * A23); - qd[qd_stride * 4] = w * c[c_stride * 0] * (A21 * A31 + A22 * A32 + A23 * A33); - qd[qd_stride * 5] = w * c[c_stride * 0] * (A31 * A31 + A32 * A32 + A33 * A33); - } + // A: 0 3 B: 0 1 2 C: 0 3 + // 1 4 1 3 4 1 4 + // 2 5 2 4 5 2 5 + CeedScalar z[3]; + + y[0] = C[0] * x[0] + C[3] * x[1]; + y[1] = C[1] * x[0] + C[4] * x[1]; + y[2] = C[2] * x[0] + C[5] * x[1]; + + z[0] = B[0] * y[0] + B[1] * y[1] + B[2] * y[2]; + z[1] = B[1] * y[0] + B[3] * y[1] + B[4] * y[2]; + z[2] = B[2] * y[0] + B[4] * y[1] + B[5] * y[2]; + + y[0] = A[0] * z[0] + A[1] * z[1] + A[2] * z[2]; + y[1] = A[3] * z[0] + A[4] * z[1] + A[5] * z[2]; + y[2] = 0.0; } -CEED_QFUNCTION_HELPER void MultAdjJCAdjJt32(const CeedScalar *J, const CeedInt J_stride, - const CeedScalar *c, const CeedInt c_stride, - const CeedInt c_comp, const CeedScalar qw, - const CeedInt qd_stride, CeedScalar *qd) +CEED_QFUNCTION_HELPER void MultBAx22(const CeedScalar A[4], const CeedScalar B[3], + const CeedScalar x[2], CeedScalar y[2]) { - // Compute qw / det(J) adj(J) C adj(J)^T and store the symmetric part of the result. - // J: 0 3 qd: 0 1 - // 1 4 1 2 - // 2 5 - const CeedScalar J11 = J[J_stride * 0]; - const CeedScalar J21 = J[J_stride * 1]; - const CeedScalar J31 = J[J_stride * 2]; - const CeedScalar J12 = J[J_stride * 3]; - const CeedScalar J22 = J[J_stride * 4]; - const CeedScalar J32 = J[J_stride * 5]; - const CeedScalar E = J11 * J11 + J21 * J21 + J31 * J31; - const CeedScalar G = J12 * J12 + J22 * J22 + J32 * J32; - const CeedScalar F = J11 * J12 + J21 * J22 + J31 * J32; - const CeedScalar d = E * G - F * F; - const CeedScalar w = qw / sqrt(d); - if (c_comp == 6) // Matrix coefficient (symmetric) - { - // First compute entries of R = C adj(J)^T. - // c: 0 1 2 - // 1 3 4 - // 2 4 5 - const CeedScalar R11 = - G * (c[c_stride * 0] * J11 + c[c_stride * 1] * J21 + c[c_stride * 2] * J31) - - F * (c[c_stride * 0] * J12 + c[c_stride * 1] * J22 + c[c_stride * 2] * J32); - const CeedScalar R21 = - G * (c[c_stride * 1] * J11 + c[c_stride * 3] * J21 + c[c_stride * 4] * J31) - - F * (c[c_stride * 1] * J12 + c[c_stride * 3] * J22 + c[c_stride * 4] * J32); - const CeedScalar R31 = - G * (c[c_stride * 2] * J11 + c[c_stride * 4] * J21 + c[c_stride * 5] * J31) - - F * (c[c_stride * 2] * J12 + c[c_stride * 4] * J22 + c[c_stride * 5] * J32); - const CeedScalar R12 = - E * (c[c_stride * 0] * J12 + c[c_stride * 1] * J22 + c[c_stride * 2] * J32) - - F * (c[c_stride * 0] * J11 + c[c_stride * 1] * J21 + c[c_stride * 2] * J31); - const CeedScalar R22 = - E * (c[c_stride * 1] * J12 + c[c_stride * 3] * J22 + c[c_stride * 4] * J32) - - F * (c[c_stride * 1] * J11 + c[c_stride * 3] * J21 + c[c_stride * 4] * J31); - const CeedScalar R32 = - E * (c[c_stride * 2] * J12 + c[c_stride * 4] * J22 + c[c_stride * 5] * J32) - - F * (c[c_stride * 2] * J11 + c[c_stride * 4] * J21 + c[c_stride * 5] * J31); - qd[qd_stride * 0] = w * - (G * (J11 * R11 + J21 * R21 + J31 * R31) - - F * (J12 * R11 + J22 * R21 + J32 * R31)) / - d; - qd[qd_stride * 1] = w * - (G * (J11 * R12 + J21 * R22 + J31 * R32) - - F * (J12 * R12 + J22 * R22 + J32 * R32)) / - d; - qd[qd_stride * 2] = w * - (E * (J12 * R12 + J22 * R22 + J32 * R32) - - F * (J11 * R12 + J21 * R22 + J31 * R32)) / - d; - } - else if (c_comp == 3) // Vector coefficient - { - // First compute entries of R = C adj(J)^T. - // c: 0 - // 1 - // 2 - const CeedScalar R11 = c[c_stride * 0] * (G * J11 - F * J12); - const CeedScalar R21 = c[c_stride * 1] * (G * J21 - F * J22); - const CeedScalar R31 = c[c_stride * 2] * (G * J31 - F * J32); - const CeedScalar R12 = c[c_stride * 0] * (E * J12 - F * J11); - const CeedScalar R22 = c[c_stride * 1] * (E * J22 - F * J21); - const CeedScalar R32 = c[c_stride * 2] * (E * J32 - F * J31); - qd[qd_stride * 0] = w * - (G * (J11 * R11 + J21 * R21 + J31 * R31) - - F * (J12 * R11 + J22 * R21 + J32 * R31)) / - d; - qd[qd_stride * 1] = w * - (G * (J11 * R12 + J21 * R22 + J31 * R32) - - F * (J12 * R12 + J22 * R22 + J32 * R32)) / - d; - qd[qd_stride * 2] = w * - (E * (J12 * R12 + J22 * R22 + J32 * R32) - - F * (J11 * R12 + J21 * R22 + J31 * R32)) / - d; - } - else // Scalar coefficient - { - qd[qd_stride * 0] = w * c[c_stride * 0] * G; - qd[qd_stride * 1] = -w * c[c_stride * 0] * F; - qd[qd_stride * 2] = w * c[c_stride * 0] * E; - } + // A: 0 2 B: 0 1 + // 1 3 1 2 + CeedScalar z[2]; + + z[0] = A[0] * x[0] + A[2] * x[1]; + z[1] = A[1] * x[0] + A[3] * x[1]; + + y[0] = B[0] * z[0] + B[1] * z[1]; + y[1] = B[1] * z[0] + B[2] * z[1]; } -CEED_QFUNCTION_HELPER void MultJtCJ22(const CeedScalar *J, const CeedInt J_stride, - const CeedScalar *c, const CeedInt c_stride, - const CeedInt c_comp, const CeedScalar qw, - const CeedInt qd_stride, CeedScalar *qd) +CEED_QFUNCTION_HELPER void MultBAx33(const CeedScalar A[9], const CeedScalar B[6], + const CeedScalar x[3], CeedScalar y[3]) { - // Compute qw / det(J) J^T C J and store the symmetric part of the result. - // J: 0 2 qd: 0 1 - // 1 3 1 2 - const CeedScalar J11 = J[J_stride * 0]; - const CeedScalar J21 = J[J_stride * 1]; - const CeedScalar J12 = J[J_stride * 2]; - const CeedScalar J22 = J[J_stride * 3]; - const CeedScalar w = qw / (J11 * J22 - J21 * J12); - if (c_comp == 3) // Matrix coefficient (symmetric) - { - // First compute entries of R = C J. - // c: 0 1 - // 1 2 - const CeedScalar R11 = c[c_stride * 0] * J11 + c[c_stride * 1] * J21; - const CeedScalar R21 = c[c_stride * 1] * J11 + c[c_stride * 2] * J21; - const CeedScalar R12 = c[c_stride * 0] * J12 + c[c_stride * 1] * J22; - const CeedScalar R22 = c[c_stride * 1] * J12 + c[c_stride * 2] * J22; - qd[qd_stride * 0] = w * (J11 * R11 + J21 * R21); - qd[qd_stride * 1] = w * (J11 * R12 + J21 * R22); - qd[qd_stride * 2] = w * (J12 * R12 + J22 * R22); - } - else if (c_comp == 2) // Vector coefficient - { - // c: 0 - // 1 - qd[qd_stride * 0] = w * (c[c_stride * 0] * J11 * J11 + c[c_stride * 1] * J21 * J21); - qd[qd_stride * 1] = w * (c[c_stride * 0] * J11 * J12 + c[c_stride * 1] * J21 * J22); - qd[qd_stride * 2] = w * (c[c_stride * 0] * J12 * J12 + c[c_stride * 1] * J22 * J22); - } - else // Scalar coefficient - { - qd[qd_stride * 0] = w * c[c_stride * 0] * (J11 * J11 + J21 * J21); - qd[qd_stride * 1] = w * c[c_stride * 0] * (J11 * J12 + J21 * J22); - qd[qd_stride * 2] = w * c[c_stride * 0] * (J12 * J12 + J22 * J22); - } + // A: 0 3 6 B: 0 1 2 + // 1 4 7 1 3 4 + // 2 5 8 2 4 5 + CeedScalar z[3]; + + z[0] = A[0] * x[0] + A[3] * x[1] + A[6] * x[2]; + z[1] = A[1] * x[0] + A[4] * x[1] + A[7] * x[2]; + z[2] = A[2] * x[0] + A[5] * x[1] + A[8] * x[2]; + + y[0] = B[0] * z[0] + B[1] * z[1] + B[2] * z[2]; + y[1] = B[1] * z[0] + B[3] * z[1] + B[4] * z[2]; + y[2] = B[2] * z[0] + B[4] * z[1] + B[5] * z[2]; } -CEED_QFUNCTION_HELPER void MultJtCJ21(const CeedScalar *J, const CeedInt J_stride, - const CeedScalar *c, const CeedInt c_stride, - const CeedInt c_comp, const CeedScalar qw, - const CeedInt qd_stride, CeedScalar *qd) +CEED_QFUNCTION_HELPER void MultBAx21(const CeedScalar A[2], const CeedScalar B[3], + const CeedScalar x[1], CeedScalar y[1]) { - // Compute qw / det(J) J^T C J and store the symmetric part of the result. - // J: 0 qd: 0 - // 1 - const CeedScalar J11 = J[J_stride * 0]; - const CeedScalar J21 = J[J_stride * 1]; - if (c_comp == 3) // Matrix coefficient (symmetric) - { - // First compute entries of R = C J. - // c: 0 1 - // 1 2 - const CeedScalar w = qw / sqrt(J11 * J11 + J21 * J21); - const CeedScalar R11 = c[c_stride * 0] * J11 + c[c_stride * 1] * J21; - const CeedScalar R21 = c[c_stride * 1] * J11 + c[c_stride * 2] * J21; - qd[qd_stride * 0] = w * (J11 * R11 + J21 * R21); - } - else if (c_comp == 2) // Vector coefficient - { - // c: 0 - // 1 - const CeedScalar w = qw / sqrt(J11 * J11 + J21 * J21); - qd[qd_stride * 0] = w * (c[c_stride * 0] * J11 * J11 + c[c_stride * 1] * J21 * J21); - } - else // Scalar coefficient - { - qd[qd_stride * 0] = qw * c[c_stride * 0] * sqrt(J11 * J11 + J21 * J21); - } + // A: 0 B: 0 1 + // 1 1 2 + CeedScalar z[2]; + + z[0] = A[0] * x[0]; + z[1] = A[1] * x[0]; + + y[0] = B[0] * z[0] + B[1] * z[1]; + y[1] = B[1] * z[0] + B[2] * z[1]; } -CEED_QFUNCTION_HELPER void MultJtCJ33(const CeedScalar *J, const CeedInt J_stride, - const CeedScalar *c, const CeedInt c_stride, - const CeedInt c_comp, const CeedScalar qw, - const CeedInt qd_stride, CeedScalar *qd) +CEED_QFUNCTION_HELPER void MultBAx32(const CeedScalar A[6], const CeedScalar B[6], + const CeedScalar x[2], CeedScalar y[2]) { - // Compute qw / det(J) J^T C J and store the symmetric part of the result. - // J: 0 3 6 qd: 0 1 2 - // 1 4 7 1 3 4 - // 2 5 8 2 4 5 - const CeedScalar J11 = J[J_stride * 0]; - const CeedScalar J21 = J[J_stride * 1]; - const CeedScalar J31 = J[J_stride * 2]; - const CeedScalar J12 = J[J_stride * 3]; - const CeedScalar J22 = J[J_stride * 4]; - const CeedScalar J32 = J[J_stride * 5]; - const CeedScalar J13 = J[J_stride * 6]; - const CeedScalar J23 = J[J_stride * 7]; - const CeedScalar J33 = J[J_stride * 8]; - const CeedScalar w = qw / (J11 * (J22 * J33 - J23 * J32) + J21 * (J13 * J32 - J12 * J33) + - J31 * (J12 * J23 - J13 * J22)); - if (c_comp == 6) // Matrix coefficient (symmetric) - { - // First compute entries of R = C J. - // c: 0 1 2 - // 1 3 4 - // 2 4 5 - const CeedScalar R11 = - c[c_stride * 0] * J11 + c[c_stride * 1] * J21 + c[c_stride * 2] * J31; - const CeedScalar R21 = - c[c_stride * 1] * J11 + c[c_stride * 3] * J21 + c[c_stride * 4] * J31; - const CeedScalar R31 = - c[c_stride * 2] * J11 + c[c_stride * 4] * J21 + c[c_stride * 5] * J31; - const CeedScalar R12 = - c[c_stride * 0] * J12 + c[c_stride * 1] * J22 + c[c_stride * 2] * J32; - const CeedScalar R22 = - c[c_stride * 1] * J12 + c[c_stride * 3] * J22 + c[c_stride * 4] * J32; - const CeedScalar R32 = - c[c_stride * 2] * J12 + c[c_stride * 4] * J22 + c[c_stride * 5] * J32; - const CeedScalar R13 = - c[c_stride * 0] * J13 + c[c_stride * 1] * J23 + c[c_stride * 2] * J33; - const CeedScalar R23 = - c[c_stride * 1] * J13 + c[c_stride * 3] * J23 + c[c_stride * 4] * J33; - const CeedScalar R33 = - c[c_stride * 2] * J13 + c[c_stride * 4] * J23 + c[c_stride * 5] * J33; - qd[qd_stride * 0] = w * (J11 * R11 + J21 * R21 + J31 * R31); - qd[qd_stride * 1] = w * (J11 * R12 + J21 * R22 + J31 * R32); - qd[qd_stride * 2] = w * (J11 * R13 + J21 * R23 + J31 * R33); - qd[qd_stride * 3] = w * (J12 * R12 + J22 * R22 + J32 * R32); - qd[qd_stride * 4] = w * (J12 * R13 + J22 * R23 + J32 * R33); - qd[qd_stride * 5] = w * (J13 * R13 + J23 * R23 + J33 * R33); - } - else if (c_comp == 3) // Vector coefficient - { - // c: 0 - // 1 - // 2 - qd[qd_stride * 0] = w * (c[c_stride * 0] * J11 * J11 + c[c_stride * 1] * J21 * J21 + - c[c_stride * 2] * J31 * J31); - qd[qd_stride * 1] = w * (c[c_stride * 0] * J11 * J12 + c[c_stride * 1] * J21 * J22 + - c[c_stride * 2] * J31 * J32); - qd[qd_stride * 2] = w * (c[c_stride * 0] * J11 * J13 + c[c_stride * 1] * J21 * J23 + - c[c_stride * 2] * J31 * J33); - qd[qd_stride * 3] = w * (c[c_stride * 0] * J12 * J12 + c[c_stride * 1] * J22 * J22 + - c[c_stride * 2] * J32 * J32); - qd[qd_stride * 4] = w * (c[c_stride * 0] * J12 * J13 + c[c_stride * 1] * J22 * J23 + - c[c_stride * 2] * J32 * J33); - qd[qd_stride * 5] = w * (c[c_stride * 0] * J13 * J13 + c[c_stride * 1] * J23 * J23 + - c[c_stride * 2] * J33 * J33); - } - else // Scalar coefficient - { - qd[qd_stride * 0] = w * c[c_stride * 0] * (J11 * J11 + J21 * J21 + J31 * J31); - qd[qd_stride * 1] = w * c[c_stride * 0] * (J11 * J12 + J21 * J22 + J31 * J32); - qd[qd_stride * 2] = w * c[c_stride * 0] * (J11 * J13 + J21 * J23 + J31 * J33); - qd[qd_stride * 3] = w * c[c_stride * 0] * (J12 * J12 + J22 * J22 + J32 * J32); - qd[qd_stride * 4] = w * c[c_stride * 0] * (J12 * J13 + J22 * J23 + J32 * J33); - qd[qd_stride * 5] = w * c[c_stride * 0] * (J13 * J13 + J23 * J23 + J33 * J33); - } + // A: 0 3 B: 0 1 2 + // 1 4 1 3 4 + // 2 5 2 4 5 + CeedScalar z[3]; + + z[0] = A[0] * x[0] + A[3] * x[1]; + z[1] = A[1] * x[0] + A[4] * x[1]; + z[2] = A[2] * x[0] + A[5] * x[1]; + + y[0] = B[0] * z[0] + B[1] * z[1] + B[2] * z[2]; + y[1] = B[1] * z[0] + B[3] * z[1] + B[4] * z[2]; + y[2] = B[2] * z[0] + B[4] * z[1] + B[5] * z[2]; } -CEED_QFUNCTION_HELPER void MultJtCJ32(const CeedScalar *J, const CeedInt J_stride, - const CeedScalar *c, const CeedInt c_stride, - const CeedInt c_comp, const CeedScalar qw, - const CeedInt qd_stride, CeedScalar *qd) +CEED_QFUNCTION_HELPER void MultAtBA22(const CeedScalar A[4], const CeedScalar B[3], + CeedScalar C[3]) { - // Compute qw / det(J) J^T C J and store the symmetric part of the result. - // J: 0 3 qd: 0 1 - // 1 4 1 2 - // 2 5 - const CeedScalar J11 = J[J_stride * 0]; - const CeedScalar J21 = J[J_stride * 1]; - const CeedScalar J31 = J[J_stride * 2]; - const CeedScalar J12 = J[J_stride * 3]; - const CeedScalar J22 = J[J_stride * 4]; - const CeedScalar J32 = J[J_stride * 5]; - const CeedScalar E = J11 * J11 + J21 * J21 + J31 * J31; - const CeedScalar G = J12 * J12 + J22 * J22 + J32 * J32; - const CeedScalar F = J11 * J12 + J21 * J22 + J31 * J32; - const CeedScalar w = qw / sqrt(E * G - F * F); - if (c_comp == 6) // Matrix coefficient (symmetric) - { - // First compute entries of R = C J. - // c: 0 1 2 - // 1 3 4 - // 2 4 5 - const CeedScalar R11 = - c[c_stride * 0] * J11 + c[c_stride * 1] * J21 + c[c_stride * 2] * J31; - const CeedScalar R21 = - c[c_stride * 1] * J11 + c[c_stride * 3] * J21 + c[c_stride * 4] * J31; - const CeedScalar R31 = - c[c_stride * 2] * J11 + c[c_stride * 4] * J21 + c[c_stride * 5] * J31; - const CeedScalar R12 = - c[c_stride * 0] * J12 + c[c_stride * 1] * J22 + c[c_stride * 2] * J32; - const CeedScalar R22 = - c[c_stride * 1] * J12 + c[c_stride * 3] * J22 + c[c_stride * 4] * J32; - const CeedScalar R32 = - c[c_stride * 2] * J12 + c[c_stride * 4] * J22 + c[c_stride * 5] * J32; - qd[qd_stride * 0] = w * (J11 * R11 + J21 * R21 + J31 * R31); - qd[qd_stride * 1] = w * (J11 * R12 + J21 * R22 + J31 * R32); - qd[qd_stride * 2] = w * (J12 * R12 + J22 * R22 + J32 * R32); - } - else if (c_comp == 3) // Vector coefficient - { - // c: 0 - // 1 - // 2 - qd[qd_stride * 0] = w * (c[c_stride * 0] * J11 * J11 + c[c_stride * 1] * J21 * J21 + - c[c_stride * 2] * J31 * J31); - qd[qd_stride * 1] = w * (c[c_stride * 0] * J11 * J12 + c[c_stride * 1] * J21 * J22 + - c[c_stride * 2] * J31 * J32); - qd[qd_stride * 2] = w * (c[c_stride * 0] * J12 * J12 + c[c_stride * 1] * J22 * J22 + - c[c_stride * 2] * J32 * J32); - } - else // Scalar coefficient - { - qd[qd_stride * 0] = w * c[c_stride * 0] * E; - qd[qd_stride * 1] = w * c[c_stride * 0] * F; - qd[qd_stride * 2] = w * c[c_stride * 0] * G; - } + // A: 0 2 B: 0 1 C: 0 1 + // 1 3 1 2 1 2 + + // First compute entries of R = B A. + const CeedScalar R11 = B[0] * A[0] + B[1] * A[1]; + const CeedScalar R21 = B[1] * A[0] + B[2] * A[1]; + const CeedScalar R12 = B[0] * A[2] + B[1] * A[3]; + const CeedScalar R22 = B[1] * A[2] + B[2] * A[3]; + + C[0] = A[0] * R11 + A[1] * R21; + C[1] = A[0] * R12 + A[1] * R22; + C[2] = A[2] * R12 + A[3] * R22; } -template -CEED_QFUNCTION_HELPER void MultJtCAdjJt22(const CeedScalar *J, const CeedInt J_stride, - const CeedScalar *c, const CeedInt c_stride, - const CeedInt c_comp, const CeedScalar qw, - const CeedInt qd_stride, CeedScalar *qd) +CEED_QFUNCTION_HELPER void MultAtBA33(const CeedScalar A[9], const CeedScalar B[6], + CeedScalar C[6]) { - // Compute qw / det(J) J^T C adj(J)^T and store the result. - // J: 0 2 adj(J): J22 -J12 qd: 0 2 - // 1 3 -J21 J11 1 3 - const CeedScalar J11 = J[J_stride * 0]; - const CeedScalar J21 = J[J_stride * 1]; - const CeedScalar J12 = J[J_stride * 2]; - const CeedScalar J22 = J[J_stride * 3]; - const CeedScalar w = qw / (J11 * J22 - J21 * J12); - if (c_comp == 3) // Matrix coefficient (symmetric) - { - // First compute entries of R = C adj(J)^T. - // c: 0 1 - // 1 2 - const CeedScalar R11 = c[c_stride * 0] * J22 - c[c_stride * 1] * J12; - const CeedScalar R21 = c[c_stride * 1] * J22 - c[c_stride * 2] * J12; - const CeedScalar R12 = -c[c_stride * 0] * J21 + c[c_stride * 1] * J11; - const CeedScalar R22 = -c[c_stride * 1] * J21 + c[c_stride * 2] * J11; - qd[qd_stride * 0] = w * (J11 * R11 + J21 * R21); - qd[qd_stride * 1] = w * (J12 * R11 + J22 * R21); - qd[qd_stride * 2] = w * (J11 * R12 + J21 * R22); - qd[qd_stride * 3] = w * (J12 * R12 + J22 * R22); - } - else if (c_comp == 2) // Vector coefficient - { - // c: 0 - // 1 - qd[qd_stride * 0] = w * (c[c_stride * 0] * J11 * J22 - c[c_stride * 1] * J12 * J21); - qd[qd_stride * 1] = w * (c[c_stride * 0] * J12 * J22 - c[c_stride * 1] * J12 * J22); - qd[qd_stride * 2] = w * (-c[c_stride * 0] * J11 * J21 + c[c_stride * 1] * J11 * J21); - qd[qd_stride * 3] = w * (-c[c_stride * 0] * J12 * J21 + c[c_stride * 1] * J11 * J22); - } - else // Scalar coefficient - { - qd[qd_stride * 0] = qw * c[c_stride * 0]; - qd[qd_stride * 1] = 0.0; - qd[qd_stride * 2] = 0.0; - qd[qd_stride * 3] = qw * c[c_stride * 0]; - } - if (Transpose && c_comp > 1) - { - const CeedScalar qd21 = qd[qd_stride * 1]; - qd[qd_stride * 1] = qd[qd_stride * 2]; - qd[qd_stride * 2] = qd21; - } + // A: 0 3 6 B: 0 1 2 C: 0 1 2 + // 1 4 7 1 3 4 1 3 4 + // 2 5 8 2 4 5 2 4 5 + + // First compute entries of R = B A. + const CeedScalar R11 = B[0] * A[0] + B[1] * A[1] + B[2] * A[2]; + const CeedScalar R21 = B[1] * A[0] + B[3] * A[1] + B[4] * A[2]; + const CeedScalar R31 = B[2] * A[0] + B[4] * A[1] + B[5] * A[2]; + const CeedScalar R12 = B[0] * A[3] + B[1] * A[4] + B[2] * A[5]; + const CeedScalar R22 = B[1] * A[3] + B[3] * A[4] + B[4] * A[5]; + const CeedScalar R32 = B[2] * A[3] + B[4] * A[4] + B[5] * A[5]; + const CeedScalar R13 = B[0] * A[6] + B[1] * A[7] + B[2] * A[8]; + const CeedScalar R23 = B[1] * A[6] + B[3] * A[7] + B[4] * A[8]; + const CeedScalar R33 = B[2] * A[6] + B[4] * A[7] + B[5] * A[8]; + + C[0] = A[0] * R11 + A[1] * R21 + A[2] * R31; + C[1] = A[0] * R12 + A[1] * R22 + A[2] * R32; + C[2] = A[0] * R13 + A[1] * R23 + A[2] * R33; + C[3] = A[3] * R12 + A[4] * R22 + A[5] * R32; + C[4] = A[3] * R13 + A[4] * R23 + A[5] * R33; + C[5] = A[6] * R13 + A[7] * R23 + A[8] * R33; +} + +CEED_QFUNCTION_HELPER void MultAtBA21(const CeedScalar A[2], const CeedScalar B[3], + CeedScalar C[1]) +{ + // A: 0 B: 0 1 C: 0 + // 1 1 2 + + // First compute entries of R = B A. + const CeedScalar R11 = B[0] * A[0] + B[1] * A[1]; + const CeedScalar R21 = B[1] * A[0] + B[2] * A[1]; + + C[0] = A[0] * R11 + A[1] * R21; } -template -CEED_QFUNCTION_HELPER void MultJtCAdjJt21(const CeedScalar *J, const CeedInt J_stride, - const CeedScalar *c, const CeedInt c_stride, - const CeedInt c_comp, const CeedScalar qw, - const CeedInt qd_stride, CeedScalar *qd) +CEED_QFUNCTION_HELPER void MultAtBA32(const CeedScalar A[6], const CeedScalar B[6], + CeedScalar C[3]) { - // Compute qw / det(J) J^T C adj(J)^T and store the result. - // J: 0 adj(J): 1/sqrt(J^T J) J^T qd: 0 - // 1 - const CeedScalar J11 = J[J_stride * 0]; - const CeedScalar J21 = J[J_stride * 1]; - const CeedScalar w = qw / (J11 * J11 + J21 * J21); - if (c_comp == 3) // Matrix coefficient (symmetric) - { - // First compute entries of R = C adj(J)^T. - // c: 0 1 - // 1 2 - const CeedScalar R11 = c[c_stride * 0] * J11 + c[c_stride * 1] * J21; - const CeedScalar R21 = c[c_stride * 1] * J11 + c[c_stride * 2] * J21; - qd[qd_stride * 0] = w * (J11 * R11 + J21 * R21); - } - else if (c_comp == 2) // Vector coefficient - { - // c: 0 - // 1 - qd[qd_stride * 0] = w * (c[c_stride * 0] * J11 * J11 + c[c_stride * 1] * J21 * J21); - } - else // Scalar coefficient - { - qd[qd_stride * 0] = qw * c[c_stride * 0]; - } + // A: 0 3 B: 0 1 2 C: 0 1 + // 1 4 1 3 4 1 2 + // 2 5 2 4 5 + + // First compute entries of R = B A. + const CeedScalar R11 = B[0] * A[0] + B[1] * A[1] + B[2] * A[2]; + const CeedScalar R21 = B[1] * A[0] + B[3] * A[1] + B[4] * A[2]; + const CeedScalar R31 = B[2] * A[0] + B[4] * A[1] + B[5] * A[2]; + const CeedScalar R12 = B[0] * A[3] + B[1] * A[4] + B[2] * A[5]; + const CeedScalar R22 = B[1] * A[3] + B[3] * A[4] + B[4] * A[5]; + const CeedScalar R32 = B[2] * A[3] + B[4] * A[4] + B[5] * A[5]; + + C[0] = A[0] * R11 + A[1] * R21 + A[2] * R31; + C[1] = A[0] * R12 + A[1] * R22 + A[2] * R32; + C[2] = A[3] * R12 + A[4] * R22 + A[5] * R32; } -template -CEED_QFUNCTION_HELPER void MultJtCAdjJt33(const CeedScalar *J, const CeedInt J_stride, - const CeedScalar *c, const CeedInt c_stride, - const CeedInt c_comp, const CeedScalar qw, - const CeedInt qd_stride, CeedScalar *qd) +CEED_QFUNCTION_HELPER void MultAtBC22(const CeedScalar A[4], const CeedScalar B[3], + const CeedScalar C[4], CeedScalar D[4]) { - // Compute qw / det(J) J^T C adj(J)^T and store the result. - // J: 0 3 6 qd: 0 3 6 - // 1 4 7 1 4 7 - // 2 5 8 2 5 8 - const CeedScalar J11 = J[J_stride * 0]; - const CeedScalar J21 = J[J_stride * 1]; - const CeedScalar J31 = J[J_stride * 2]; - const CeedScalar J12 = J[J_stride * 3]; - const CeedScalar J22 = J[J_stride * 4]; - const CeedScalar J32 = J[J_stride * 5]; - const CeedScalar J13 = J[J_stride * 6]; - const CeedScalar J23 = J[J_stride * 7]; - const CeedScalar J33 = J[J_stride * 8]; - const CeedScalar A11 = J22 * J33 - J23 * J32; - const CeedScalar A21 = J23 * J31 - J21 * J33; - const CeedScalar A31 = J21 * J32 - J22 * J31; - const CeedScalar A12 = J13 * J32 - J12 * J33; - const CeedScalar A22 = J11 * J33 - J13 * J31; - const CeedScalar A32 = J12 * J31 - J11 * J32; - const CeedScalar A13 = J12 * J23 - J13 * J22; - const CeedScalar A23 = J13 * J21 - J11 * J23; - const CeedScalar A33 = J11 * J22 - J12 * J21; - const CeedScalar w = qw / (J11 * A11 + J21 * A12 + J31 * A13); - if (c_comp == 6) // Matrix coefficient (symmetric) - { - // First compute entries of R = C adj(J)^T. - // c: 0 1 2 - // 1 3 4 - // 2 4 5 - const CeedScalar R11 = - c[c_stride * 0] * A11 + c[c_stride * 1] * A12 + c[c_stride * 2] * A13; - const CeedScalar R21 = - c[c_stride * 1] * A11 + c[c_stride * 3] * A12 + c[c_stride * 4] * A13; - const CeedScalar R31 = - c[c_stride * 2] * A11 + c[c_stride * 4] * A12 + c[c_stride * 5] * A13; - const CeedScalar R12 = - c[c_stride * 0] * A21 + c[c_stride * 1] * A22 + c[c_stride * 2] * A23; - const CeedScalar R22 = - c[c_stride * 1] * A21 + c[c_stride * 3] * A22 + c[c_stride * 4] * A23; - const CeedScalar R32 = - c[c_stride * 2] * A21 + c[c_stride * 4] * A22 + c[c_stride * 5] * A23; - const CeedScalar R13 = - c[c_stride * 0] * A31 + c[c_stride * 1] * A32 + c[c_stride * 2] * A33; - const CeedScalar R23 = - c[c_stride * 1] * A31 + c[c_stride * 3] * A32 + c[c_stride * 4] * A33; - const CeedScalar R33 = - c[c_stride * 2] * A31 + c[c_stride * 4] * A32 + c[c_stride * 5] * A33; - qd[qd_stride * 0] = w * (J11 * R11 + J21 * R21 + J31 * R31); - qd[qd_stride * 1] = w * (J12 * R11 + J22 * R21 + J32 * R31); - qd[qd_stride * 2] = w * (J13 * R11 + J23 * R21 + J33 * R31); - qd[qd_stride * 3] = w * (J11 * R12 + J21 * R22 + J31 * R32); - qd[qd_stride * 4] = w * (J12 * R12 + J22 * R22 + J32 * R32); - qd[qd_stride * 5] = w * (J13 * R12 + J23 * R22 + J33 * R32); - qd[qd_stride * 6] = w * (J11 * R13 + J21 * R23 + J31 * R33); - qd[qd_stride * 7] = w * (J12 * R13 + J22 * R23 + J32 * R33); - qd[qd_stride * 8] = w * (J13 * R13 + J23 * R23 + J33 * R33); - } - else if (c_comp == 3) // Vector coefficient - { - // c: 0 - // 1 - // 2 - qd[qd_stride * 0] = w * (c[c_stride * 0] * A11 * J11 + c[c_stride * 1] * A12 * J21 + - c[c_stride * 2] * A13 * J31); - qd[qd_stride * 1] = w * (c[c_stride * 0] * A11 * J12 + c[c_stride * 1] * A12 * J22 + - c[c_stride * 2] * A13 * J32); - qd[qd_stride * 2] = w * (c[c_stride * 0] * A11 * J13 + c[c_stride * 1] * A12 * J23 + - c[c_stride * 2] * A13 * J33); - qd[qd_stride * 3] = w * (c[c_stride * 0] * A21 * J11 + c[c_stride * 1] * A22 * J21 + - c[c_stride * 2] * A23 * J31); - qd[qd_stride * 4] = w * (c[c_stride * 0] * A21 * J12 + c[c_stride * 1] * A22 * J22 + - c[c_stride * 2] * A23 * J32); - qd[qd_stride * 5] = w * (c[c_stride * 0] * A21 * J13 + c[c_stride * 1] * A22 * J23 + - c[c_stride * 2] * A23 * J33); - qd[qd_stride * 6] = w * (c[c_stride * 0] * A31 * J11 + c[c_stride * 1] * A32 * J21 + - c[c_stride * 2] * A33 * J31); - qd[qd_stride * 7] = w * (c[c_stride * 0] * A31 * J12 + c[c_stride * 1] * A32 * J22 + - c[c_stride * 2] * A33 * J32); - qd[qd_stride * 8] = w * (c[c_stride * 0] * A31 * J13 + c[c_stride * 1] * A32 * J23 + - c[c_stride * 2] * A33 * J33); - } - else // Scalar coefficient - { - qd[qd_stride * 0] = qw * c[c_stride * 0]; - qd[qd_stride * 1] = 0.0; - qd[qd_stride * 2] = 0.0; - qd[qd_stride * 3] = 0.0; - qd[qd_stride * 4] = qw * c[c_stride * 0]; - qd[qd_stride * 5] = 0.0; - qd[qd_stride * 6] = 0.0; - qd[qd_stride * 7] = 0.0; - qd[qd_stride * 8] = qw * c[c_stride * 0]; - } - if (Transpose && c_comp > 1) - { - { - const CeedScalar qd21 = qd[qd_stride * 1]; - qd[qd_stride * 1] = qd[qd_stride * 3]; - qd[qd_stride * 3] = qd21; - } - { - const CeedScalar qd31 = qd[qd_stride * 2]; - qd[qd_stride * 2] = qd[qd_stride * 6]; - qd[qd_stride * 6] = qd31; - } - { - const CeedScalar qd32 = qd[qd_stride * 5]; - qd[qd_stride * 5] = qd[qd_stride * 7]; - qd[qd_stride * 7] = qd32; - } - } + // A, C: 0 2 B: 0 1 D: 0 2 + // 1 3 1 2 1 3 + + // First compute entries of R = B C. + const CeedScalar R11 = B[0] * C[0] + B[1] * C[1]; + const CeedScalar R21 = B[1] * C[0] + B[2] * C[1]; + const CeedScalar R12 = B[0] * C[2] + B[1] * C[3]; + const CeedScalar R22 = B[1] * C[2] + B[2] * C[3]; + + D[0] = A[0] * R11 + A[1] * R21; + D[1] = A[2] * R11 + A[3] * R21; + D[2] = A[0] * R12 + A[1] * R22; + D[3] = A[2] * R12 + A[3] * R22; } -template -CEED_QFUNCTION_HELPER void MultJtCAdjJt32(const CeedScalar *J, const CeedInt J_stride, - const CeedScalar *c, const CeedInt c_stride, - const CeedInt c_comp, const CeedScalar qw, - const CeedInt qd_stride, CeedScalar *qd) +CEED_QFUNCTION_HELPER void MultAtBC33(const CeedScalar A[9], const CeedScalar B[6], + const CeedScalar C[9], CeedScalar D[9]) { - // Compute qw / det(J) J^T C adj(J)^T and store the result. - // J: 0 3 qd: 0 2 - // 1 4 1 3 - // 2 5 - const CeedScalar J11 = J[J_stride * 0]; - const CeedScalar J21 = J[J_stride * 1]; - const CeedScalar J31 = J[J_stride * 2]; - const CeedScalar J12 = J[J_stride * 3]; - const CeedScalar J22 = J[J_stride * 4]; - const CeedScalar J32 = J[J_stride * 5]; - const CeedScalar E = J11 * J11 + J21 * J21 + J31 * J31; - const CeedScalar G = J12 * J12 + J22 * J22 + J32 * J32; - const CeedScalar F = J11 * J12 + J21 * J22 + J31 * J32; - const CeedScalar w = qw / (E * G - F * F); - if (c_comp == 6) // Matrix coefficient (symmetric) - { - // First compute entries of R = C adj(J)^T. - // c: 0 1 2 - // 1 3 4 - // 2 4 5 - const CeedScalar R11 = - G * (c[c_stride * 0] * J11 + c[c_stride * 1] * J21 + c[c_stride * 2] * J31) - - F * (c[c_stride * 0] * J12 + c[c_stride * 1] * J22 + c[c_stride * 2] * J32); - const CeedScalar R21 = - G * (c[c_stride * 1] * J11 + c[c_stride * 3] * J21 + c[c_stride * 4] * J31) - - F * (c[c_stride * 1] * J12 + c[c_stride * 3] * J22 + c[c_stride * 4] * J32); - const CeedScalar R31 = - G * (c[c_stride * 2] * J11 + c[c_stride * 4] * J21 + c[c_stride * 5] * J31) - - F * (c[c_stride * 2] * J12 + c[c_stride * 4] * J22 + c[c_stride * 5] * J32); - const CeedScalar R12 = - E * (c[c_stride * 0] * J12 + c[c_stride * 1] * J22 + c[c_stride * 2] * J32) - - F * (c[c_stride * 0] * J11 + c[c_stride * 1] * J21 + c[c_stride * 2] * J31); - const CeedScalar R22 = - E * (c[c_stride * 1] * J12 + c[c_stride * 3] * J22 + c[c_stride * 4] * J32) - - F * (c[c_stride * 1] * J11 + c[c_stride * 3] * J21 + c[c_stride * 4] * J31); - const CeedScalar R32 = - E * (c[c_stride * 2] * J12 + c[c_stride * 4] * J22 + c[c_stride * 5] * J32) - - F * (c[c_stride * 2] * J11 + c[c_stride * 4] * J21 + c[c_stride * 5] * J31); - qd[qd_stride * 0] = w * (J11 * R11 + J21 * R21 + J31 * R31); - qd[qd_stride * 1] = w * (J12 * R11 + J22 * R21 + J32 * R31); - qd[qd_stride * 2] = w * (J11 * R12 + J21 * R22 + J31 * R32); - qd[qd_stride * 3] = w * (J12 * R12 + J22 * R22 + J32 * R32); - } - else if (c_comp == 3) // Vector coefficient - { - // First compute entries of R = C adj(J)^T. - // c: 0 - // 1 - // 2 - const CeedScalar R11 = c[c_stride * 0] * (G * J11 - F * J12); - const CeedScalar R21 = c[c_stride * 1] * (G * J21 - F * J22); - const CeedScalar R31 = c[c_stride * 2] * (G * J31 - F * J32); - const CeedScalar R12 = c[c_stride * 0] * (E * J12 - F * J11); - const CeedScalar R22 = c[c_stride * 1] * (E * J22 - F * J21); - const CeedScalar R32 = c[c_stride * 2] * (E * J32 - F * J31); - qd[qd_stride * 0] = w * (J11 * R11 + J21 * R21 + J31 * R31); - qd[qd_stride * 1] = w * (J12 * R11 + J22 * R21 + J32 * R31); - qd[qd_stride * 2] = w * (J11 * R12 + J21 * R22 + J31 * R32); - qd[qd_stride * 3] = w * (J12 * R12 + J22 * R22 + J32 * R32); - } - else // Scalar coefficient - { - qd[qd_stride * 0] = qw * c[c_stride * 0]; - qd[qd_stride * 1] = 0.0; - qd[qd_stride * 2] = 0.0; - qd[qd_stride * 3] = qw * c[c_stride * 0]; - } - if (Transpose && c_comp > 1) - { - const CeedScalar qd21 = qd[qd_stride * 1]; - qd[qd_stride * 1] = qd[qd_stride * 2]; - qd[qd_stride * 2] = qd21; - } + // A, C: 0 3 6 B: 0 1 2 D: 0 3 6 + // 1 4 7 1 3 4 1 4 7 + // 2 5 8 2 4 5 2 5 8 + + // First compute entries of R = B C. + const CeedScalar R11 = B[0] * C[0] + B[1] * C[1] + B[2] * C[2]; + const CeedScalar R21 = B[1] * C[0] + B[3] * C[1] + B[4] * C[2]; + const CeedScalar R31 = B[2] * C[0] + B[4] * C[1] + B[5] * C[2]; + const CeedScalar R12 = B[0] * C[3] + B[1] * C[4] + B[2] * C[5]; + const CeedScalar R22 = B[1] * C[3] + B[3] * C[4] + B[4] * C[5]; + const CeedScalar R32 = B[2] * C[3] + B[4] * C[4] + B[5] * C[5]; + const CeedScalar R13 = B[0] * C[6] + B[1] * C[7] + B[2] * C[8]; + const CeedScalar R23 = B[1] * C[6] + B[3] * C[7] + B[4] * C[8]; + const CeedScalar R33 = B[2] * C[6] + B[4] * C[7] + B[5] * C[8]; + + D[0] = A[0] * R11 + A[1] * R21 + A[2] * R31; + D[1] = A[3] * R11 + A[4] * R21 + A[5] * R31; + D[2] = A[6] * R11 + A[7] * R21 + A[8] * R31; + D[3] = A[0] * R12 + A[1] * R22 + A[2] * R32; + D[4] = A[3] * R12 + A[4] * R22 + A[5] * R32; + D[5] = A[6] * R12 + A[7] * R22 + A[8] * R32; + D[6] = A[0] * R13 + A[1] * R23 + A[2] * R33; + D[7] = A[3] * R13 + A[4] * R23 + A[5] * R33; + D[8] = A[6] * R13 + A[7] * R23 + A[8] * R33; +} + +CEED_QFUNCTION_HELPER void MultAtBC21(const CeedScalar A[2], const CeedScalar B[3], + const CeedScalar C[2], CeedScalar D[1]) +{ + // A, C: 0 B: 0 1 D: 0 + // 1 1 2 + + // First compute entries of R = B C. + const CeedScalar R11 = B[0] * C[0] + B[1] * C[1]; + const CeedScalar R21 = B[1] * C[0] + B[2] * C[1]; + + D[0] = A[0] * R11 + A[1] * R21; +} + +CEED_QFUNCTION_HELPER void MultAtBC32(const CeedScalar A[6], const CeedScalar B[6], + const CeedScalar C[6], CeedScalar D[4]) +{ + // A, C: 0 3 B: 0 1 2 D: 0 2 + // 1 4 1 3 4 1 3 + // 2 5 2 4 5 + + // First compute entries of R = B C. + const CeedScalar R11 = B[0] * C[0] + B[1] * C[1] + B[2] * C[2]; + const CeedScalar R21 = B[1] * C[0] + B[3] * C[1] + B[4] * C[2]; + const CeedScalar R31 = B[2] * C[0] + B[4] * C[1] + B[5] * C[2]; + const CeedScalar R12 = B[0] * C[3] + B[1] * C[4] + B[2] * C[5]; + const CeedScalar R22 = B[1] * C[3] + B[3] * C[4] + B[4] * C[5]; + const CeedScalar R32 = B[2] * C[3] + B[4] * C[4] + B[5] * C[5]; + + D[0] = A[0] * R11 + A[1] * R21 + A[2] * R31; + D[1] = A[3] * R11 + A[4] * R21 + A[5] * R31; + D[2] = A[0] * R12 + A[1] * R22 + A[2] * R32; + D[3] = A[3] * R12 + A[4] * R22 + A[5] * R32; } -CEED_QFUNCTION_HELPER void MultCAdjJt22(const CeedScalar *J, const CeedInt J_stride, - const CeedScalar *c, const CeedInt c_stride, - const CeedInt c_comp, const CeedScalar qw, - const CeedInt qd_stride, CeedScalar *qd) +CEED_QFUNCTION_HELPER void MultBA22(const CeedScalar A[4], const CeedScalar B[3], + CeedScalar C[4]) { - // Compute qw C adj(J)^T and store the result. - // J: 0 2 adj(J): J22 -J12 qd: 0 2 - // 1 3 -J21 J11 1 3 - const CeedScalar J11 = J[J_stride * 0]; - const CeedScalar J21 = J[J_stride * 1]; - const CeedScalar J12 = J[J_stride * 2]; - const CeedScalar J22 = J[J_stride * 3]; - if (c_comp == 3) // Matrix coefficient (symmetric) - { - // c: 0 1 - // 1 2 - qd[qd_stride * 0] = qw * (c[c_stride * 0] * J22 - c[c_stride * 1] * J12); - qd[qd_stride * 1] = qw * (c[c_stride * 1] * J22 - c[c_stride * 2] * J12); - qd[qd_stride * 2] = qw * (-c[c_stride * 0] * J21 + c[c_stride * 1] * J11); - qd[qd_stride * 3] = qw * (-c[c_stride * 1] * J21 + c[c_stride * 2] * J11); - } - else if (c_comp == 2) // Vector coefficient - { - // c: 0 - // 1 - const CeedScalar wc0 = qw * c[c_stride * 0]; - const CeedScalar wc1 = qw * c[c_stride * 1]; - qd[qd_stride * 0] = wc0 * J22; - qd[qd_stride * 1] = -wc1 * J12; - qd[qd_stride * 2] = -wc0 * J21; - qd[qd_stride * 3] = wc1 * J11; - } - else // Scalar coefficient - { - const CeedScalar wc = qw * c[c_stride * 0]; - qd[qd_stride * 0] = wc * J22; - qd[qd_stride * 1] = -wc * J12; - qd[qd_stride * 2] = -wc * J21; - qd[qd_stride * 3] = wc * J11; - } + // A: 0 2 B: 0 1 C: 0 2 + // 1 3 1 2 1 3 + C[0] = B[0] * A[0] + B[1] * A[1]; + C[1] = B[1] * A[0] + B[2] * A[1]; + C[2] = B[0] * A[2] + B[1] * A[3]; + C[3] = B[1] * A[2] + B[2] * A[3]; } -CEED_QFUNCTION_HELPER void MultCAdjJt21(const CeedScalar *J, const CeedInt J_stride, - const CeedScalar *c, const CeedInt c_stride, - const CeedInt c_comp, const CeedScalar qw, - const CeedInt qd_stride, CeedScalar *qd) +CEED_QFUNCTION_HELPER void MultBA33(const CeedScalar A[9], const CeedScalar B[6], + CeedScalar C[9]) { - // Compute qw C adj(J)^T and store the result. - // J: 0 adj(J): 1/sqrt(J^T J) J^T qd: 0 - // 1 1 - const CeedScalar J11 = J[J_stride * 0]; - const CeedScalar J21 = J[J_stride * 1]; - const CeedScalar w = qw / sqrt(J11 * J11 + J21 * J21); - if (c_comp == 3) // Matrix coefficient (symmetric) - { - // c: 0 1 - // 1 2 - qd[qd_stride * 0] = w * (c[c_stride * 0] * J11 + c[c_stride * 1] * J21); - qd[qd_stride * 1] = w * (c[c_stride * 1] * J11 + c[c_stride * 2] * J21); - } - else if (c_comp == 2) // Vector coefficient - { - // c: 0 - // 1 - qd[qd_stride * 0] = w * c[c_stride * 0] * J11; - qd[qd_stride * 1] = w * c[c_stride * 1] * J21; - } - else // Scalar coefficient - { - const CeedScalar wc = w * c[c_stride * 0]; - qd[qd_stride * 0] = wc * J11; - qd[qd_stride * 1] = wc * J21; - } + // A: 0 3 6 B: 0 1 2 C: 0 3 6 + // 1 4 7 1 3 4 1 4 7 + // 2 5 8 2 4 5 2 5 8 + C[0] = B[0] * A[0] + B[1] * A[1] + B[2] * A[2]; + C[1] = B[1] * A[0] + B[3] * A[1] + B[4] * A[2]; + C[2] = B[2] * A[0] + B[4] * A[1] + B[5] * A[2]; + C[3] = B[0] * A[3] + B[1] * A[4] + B[2] * A[5]; + C[4] = B[1] * A[3] + B[3] * A[4] + B[4] * A[5]; + C[5] = B[2] * A[3] + B[4] * A[4] + B[5] * A[5]; + C[6] = B[0] * A[6] + B[1] * A[7] + B[2] * A[8]; + C[7] = B[1] * A[6] + B[3] * A[7] + B[4] * A[8]; + C[8] = B[2] * A[6] + B[4] * A[7] + B[5] * A[8]; } -CEED_QFUNCTION_HELPER void MultCAdjJt33(const CeedScalar *J, const CeedInt J_stride, - const CeedScalar *c, const CeedInt c_stride, - const CeedInt c_comp, const CeedScalar qw, - const CeedInt qd_stride, CeedScalar *qd) +CEED_QFUNCTION_HELPER void MultBA21(const CeedScalar A[2], const CeedScalar B[3], + CeedScalar C[2]) { - // Compute qw C adj(J)^T and store the result. - // J: 0 3 6 qd: 0 3 6 - // 1 4 7 1 4 7 - // 2 5 8 2 5 8 - const CeedScalar J11 = J[J_stride * 0]; - const CeedScalar J21 = J[J_stride * 1]; - const CeedScalar J31 = J[J_stride * 2]; - const CeedScalar J12 = J[J_stride * 3]; - const CeedScalar J22 = J[J_stride * 4]; - const CeedScalar J32 = J[J_stride * 5]; - const CeedScalar J13 = J[J_stride * 6]; - const CeedScalar J23 = J[J_stride * 7]; - const CeedScalar J33 = J[J_stride * 8]; - const CeedScalar A11 = J22 * J33 - J23 * J32; - const CeedScalar A21 = J23 * J31 - J21 * J33; - const CeedScalar A31 = J21 * J32 - J22 * J31; - const CeedScalar A12 = J13 * J32 - J12 * J33; - const CeedScalar A22 = J11 * J33 - J13 * J31; - const CeedScalar A32 = J12 * J31 - J11 * J32; - const CeedScalar A13 = J12 * J23 - J13 * J22; - const CeedScalar A23 = J13 * J21 - J11 * J23; - const CeedScalar A33 = J11 * J22 - J12 * J21; - if (c_comp == 6) // Matrix coefficient (symmetric) - { - // c: 0 1 2 - // 1 3 4 - // 2 4 5 - qd[qd_stride * 0] = - qw * (c[c_stride * 0] * A11 + c[c_stride * 1] * A12 + c[c_stride * 2] * A13); - qd[qd_stride * 1] = - qw * (c[c_stride * 1] * A11 + c[c_stride * 3] * A12 + c[c_stride * 4] * A13); - qd[qd_stride * 2] = - qw * (c[c_stride * 2] * A11 + c[c_stride * 4] * A12 + c[c_stride * 5] * A13); - qd[qd_stride * 3] = - qw * (c[c_stride * 0] * A21 + c[c_stride * 1] * A22 + c[c_stride * 2] * A23); - qd[qd_stride * 4] = - qw * (c[c_stride * 1] * A21 + c[c_stride * 3] * A22 + c[c_stride * 4] * A23); - qd[qd_stride * 5] = - qw * (c[c_stride * 2] * A21 + c[c_stride * 4] * A22 + c[c_stride * 5] * A23); - qd[qd_stride * 6] = - qw * (c[c_stride * 0] * A31 + c[c_stride * 1] * A32 + c[c_stride * 2] * A33); - qd[qd_stride * 7] = - qw * (c[c_stride * 1] * A31 + c[c_stride * 3] * A32 + c[c_stride * 4] * A33); - qd[qd_stride * 8] = - qw * (c[c_stride * 2] * A31 + c[c_stride * 4] * A32 + c[c_stride * 5] * A33); - } - else if (c_comp == 3) // Vector coefficient - { - // c: 0 - // 1 - // 2 - const CeedScalar wc0 = qw * c[c_stride * 0]; - const CeedScalar wc1 = qw * c[c_stride * 1]; - const CeedScalar wc2 = qw * c[c_stride * 2]; - qd[qd_stride * 0] = wc0 * A11; - qd[qd_stride * 1] = wc1 * A12; - qd[qd_stride * 2] = wc2 * A13; - qd[qd_stride * 3] = wc0 * A21; - qd[qd_stride * 4] = wc1 * A22; - qd[qd_stride * 5] = wc2 * A23; - qd[qd_stride * 6] = wc0 * A31; - qd[qd_stride * 7] = wc1 * A32; - qd[qd_stride * 8] = wc2 * A33; - } - else // Scalar coefficient - { - const CeedScalar wc = qw * c[c_stride * 0]; - qd[qd_stride * 0] = wc * A11; - qd[qd_stride * 1] = wc * A12; - qd[qd_stride * 2] = wc * A13; - qd[qd_stride * 3] = wc * A21; - qd[qd_stride * 4] = wc * A22; - qd[qd_stride * 5] = wc * A23; - qd[qd_stride * 6] = wc * A31; - qd[qd_stride * 7] = wc * A32; - qd[qd_stride * 8] = wc * A33; - } + // A: 0 B: 0 1 C: 0 + // 1 1 2 1 + C[0] = B[0] * A[0] + B[1] * A[1]; + C[1] = B[1] * A[0] + B[2] * A[1]; } -CEED_QFUNCTION_HELPER void MultCAdjJt32(const CeedScalar *J, const CeedInt J_stride, - const CeedScalar *c, const CeedInt c_stride, - const CeedInt c_comp, const CeedScalar qw, - const CeedInt qd_stride, CeedScalar *qd) +CEED_QFUNCTION_HELPER void MultBA32(const CeedScalar A[6], const CeedScalar B[6], + CeedScalar C[6]) { - // Compute qw C adj(J)^T and store the result. - // J: 0 3 qd: 0 3 - // 1 4 1 4 - // 2 5 2 5 - const CeedScalar J11 = J[J_stride * 0]; - const CeedScalar J21 = J[J_stride * 1]; - const CeedScalar J31 = J[J_stride * 2]; - const CeedScalar J12 = J[J_stride * 3]; - const CeedScalar J22 = J[J_stride * 4]; - const CeedScalar J32 = J[J_stride * 5]; - const CeedScalar E = J11 * J11 + J21 * J21 + J31 * J31; - const CeedScalar G = J12 * J12 + J22 * J22 + J32 * J32; - const CeedScalar F = J11 * J12 + J21 * J22 + J31 * J32; - const CeedScalar w = qw / sqrt(E * G - F * F); - if (c_comp == 6) // Matrix coefficient (symmetric) - { - // c: 0 1 2 - // 1 3 4 - // 2 4 5 - qd[qd_stride * 0] = - w * (G * (c[c_stride * 0] * J11 + c[c_stride * 1] * J21 + c[c_stride * 2] * J31) - - F * (c[c_stride * 0] * J12 + c[c_stride * 1] * J22 + c[c_stride * 2] * J32)); - qd[qd_stride * 1] = - w * (G * (c[c_stride * 1] * J11 + c[c_stride * 3] * J21 + c[c_stride * 4] * J31) - - F * (c[c_stride * 1] * J12 + c[c_stride * 3] * J22 + c[c_stride * 4] * J32)); - qd[qd_stride * 2] = - w * (G * (c[c_stride * 2] * J11 + c[c_stride * 4] * J21 + c[c_stride * 5] * J31) - - F * (c[c_stride * 2] * J12 + c[c_stride * 4] * J22 + c[c_stride * 5] * J32)); - qd[qd_stride * 3] = - w * (E * (c[c_stride * 0] * J12 + c[c_stride * 1] * J22 + c[c_stride * 2] * J32) - - F * (c[c_stride * 0] * J11 + c[c_stride * 1] * J21 + c[c_stride * 2] * J31)); - qd[qd_stride * 4] = - w * (E * (c[c_stride * 1] * J12 + c[c_stride * 3] * J22 + c[c_stride * 4] * J32) - - F * (c[c_stride * 1] * J11 + c[c_stride * 3] * J21 + c[c_stride * 4] * J31)); - qd[qd_stride * 5] = - w * (E * (c[c_stride * 2] * J12 + c[c_stride * 4] * J22 + c[c_stride * 5] * J32) - - F * (c[c_stride * 2] * J11 + c[c_stride * 4] * J21 + c[c_stride * 5] * J31)); - } - else if (c_comp == 3) // Vector coefficient - { - // c: 0 - // 1 - // 2 - const CeedScalar wc0 = w * c[c_stride * 0]; - const CeedScalar wc1 = w * c[c_stride * 1]; - const CeedScalar wc2 = w * c[c_stride * 2]; - qd[qd_stride * 0] = wc0 * (G * J11 - F * J12); - qd[qd_stride * 1] = wc1 * (G * J21 - F * J22); - qd[qd_stride * 2] = wc2 * (G * J31 - F * J32); - qd[qd_stride * 3] = wc0 * (E * J12 - F * J11); - qd[qd_stride * 4] = wc1 * (E * J22 - F * J21); - qd[qd_stride * 5] = wc2 * (E * J32 - F * J31); - } - else // Scalar coefficient - { - const CeedScalar wc = w * c[c_stride * 0]; - qd[qd_stride * 0] = wc * (G * J11 - F * J12); - qd[qd_stride * 1] = wc * (G * J21 - F * J22); - qd[qd_stride * 2] = wc * (G * J31 - F * J32); - qd[qd_stride * 3] = wc * (E * J12 - F * J11); - qd[qd_stride * 4] = wc * (E * J22 - F * J21); - qd[qd_stride * 5] = wc * (E * J32 - F * J31); - } + // A: 0 3 B: 0 1 2 C: 0 3 + // 1 4 1 3 4 1 4 + // 2 5 2 4 5 2 5 + C[0] = B[0] * A[0] + B[1] * A[1] + B[2] * A[2]; + C[1] = B[1] * A[0] + B[3] * A[1] + B[4] * A[2]; + C[2] = B[2] * A[0] + B[4] * A[1] + B[5] * A[2]; + C[3] = B[0] * A[3] + B[1] * A[4] + B[2] * A[5]; + C[4] = B[1] * A[3] + B[3] * A[4] + B[4] * A[5]; + C[5] = B[2] * A[3] + B[4] * A[4] + B[5] * A[5]; } #endif // PALACE_LIBCEED_UTILS_QF_H diff --git a/palace/fem/qfunctions/vecfemass_qf.h b/palace/fem/qfunctions/vecfemass_qf.h deleted file mode 100644 index b99ba9c88..000000000 --- a/palace/fem/qfunctions/vecfemass_qf.h +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -#ifndef PALACE_LIBCEED_VECFEMASS_QF_H -#define PALACE_LIBCEED_VECFEMASS_QF_H - -struct VectorFEMassContext -{ - CeedInt dim, space_dim; - bool sym; - CeedScalar coeff; -}; - -// libCEED QFunction for applying a symmetric or nonsymmetric vector FE mass operator. -CEED_QFUNCTION(f_apply_vecfemass)(void *ctx, CeedInt Q, const CeedScalar *const *in, - CeedScalar *const *out) -{ - // in[0], out[0] have shape [dim, ncomp=1, Q] - VectorFEMassContext *bc = (VectorFEMassContext *)ctx; - const CeedScalar *u = in[0], *qd = in[1]; - CeedScalar *v = out[0]; - switch (bc->dim) - { - case 1: - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - v[i] = qd[i] * u[i]; - } - break; - case 2: - if (bc->sym) - { - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - const CeedScalar u0 = u[i + Q * 0]; - const CeedScalar u1 = u[i + Q * 1]; - v[i + Q * 0] = qd[i + Q * 0] * u0 + qd[i + Q * 1] * u1; - v[i + Q * 1] = qd[i + Q * 1] * u0 + qd[i + Q * 2] * u1; - } - } - else - { - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - const CeedScalar u0 = u[i + Q * 0]; - const CeedScalar u1 = u[i + Q * 1]; - v[i + Q * 0] = qd[i + Q * 0] * u0 + qd[i + Q * 2] * u1; - v[i + Q * 1] = qd[i + Q * 1] * u0 + qd[i + Q * 3] * u1; - } - } - break; - case 3: - if (bc->sym) - { - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - const CeedScalar u0 = u[i + Q * 0]; - const CeedScalar u1 = u[i + Q * 1]; - const CeedScalar u2 = u[i + Q * 2]; - v[i + Q * 0] = qd[i + Q * 0] * u0 + qd[i + Q * 1] * u1 + qd[i + Q * 2] * u2; - v[i + Q * 1] = qd[i + Q * 1] * u0 + qd[i + Q * 3] * u1 + qd[i + Q * 4] * u2; - v[i + Q * 2] = qd[i + Q * 2] * u0 + qd[i + Q * 4] * u1 + qd[i + Q * 5] * u2; - } - } - else - { - CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) - { - const CeedScalar u0 = u[i + Q * 0]; - const CeedScalar u1 = u[i + Q * 1]; - const CeedScalar u2 = u[i + Q * 2]; - v[i + Q * 0] = qd[i + Q * 0] * u0 + qd[i + Q * 3] * u1 + qd[i + Q * 6] * u2; - v[i + Q * 1] = qd[i + Q * 1] * u0 + qd[i + Q * 4] * u1 + qd[i + Q * 7] * u2; - v[i + Q * 2] = qd[i + Q * 2] * u0 + qd[i + Q * 5] * u1 + qd[i + Q * 8] * u2; - } - } - break; - } - return 0; -} - -#endif // PALACE_LIBCEED_VECFEMASS_QF_H From 06f970a9dcc67678d19d57b768bf5ae7d3a38ecf Mon Sep 17 00:00:00 2001 From: Sebastian Grimberg Date: Tue, 19 Dec 2023 14:14:22 -0800 Subject: [PATCH 13/32] WIP: Update integrator classes for new libCEED coefficients and operator assembly --- palace/fem/integ/curlcurl.cpp | 162 ++++----- palace/fem/integ/curlcurlmass.cpp | 227 ++++--------- palace/fem/integ/diffusion.cpp | 157 ++++----- palace/fem/integ/diffusionmass.cpp | 152 ++++----- palace/fem/integ/divdiv.cpp | 132 ++++---- palace/fem/integ/divdivmass.cpp | 151 ++++----- palace/fem/integ/grad.cpp | 154 ++++----- palace/fem/integ/mass.cpp | 162 +++------ palace/fem/integ/mixedveccurl.cpp | 304 +++++------------ palace/fem/integ/mixedvecgrad.cpp | 257 +++++++-------- palace/fem/integ/vecfemass.cpp | 375 ++++++++++----------- palace/fem/integrator.cpp | 61 ++-- palace/fem/integrator.hpp | 474 +++++++++------------------ palace/fem/libceed/coefficient.cpp | 7 +- palace/linalg/divfree.cpp | 5 +- palace/linalg/errorestimator.cpp | 9 +- palace/linalg/hcurl.cpp | 7 +- palace/models/curlcurloperator.cpp | 8 +- palace/models/domainpostoperator.cpp | 11 +- palace/models/laplaceoperator.cpp | 8 +- palace/models/spaceoperator.cpp | 45 +-- palace/models/waveportoperator.cpp | 19 +- 22 files changed, 1069 insertions(+), 1818 deletions(-) diff --git a/palace/fem/integ/curlcurl.cpp b/palace/fem/integ/curlcurl.cpp index 3f4dd13c8..888c37994 100644 --- a/palace/fem/integ/curlcurl.cpp +++ b/palace/fem/integ/curlcurl.cpp @@ -3,117 +3,87 @@ #include "fem/integrator.hpp" -#include -#include #include "fem/libceed/coefficient.hpp" #include "fem/libceed/integrator.hpp" +#include "utils/diagnostic.hpp" -#include "fem/qfunctions/curlcurl_qf.h" +PalacePragmaDiagnosticPush +PalacePragmaDiagnosticDisableUnused -namespace palace -{ - -struct CurlCurlIntegratorInfo : public ceed::IntegratorInfo -{ - CurlCurlContext ctx; -}; +#include "fem/qfunctions/hdiv_build_qf.h" +#include "fem/qfunctions/hdiv_qf.h" +#include "fem/qfunctions/l2_build_qf.h" +#include "fem/qfunctions/l2_qf.h" -namespace -{ +PalacePragmaDiagnosticPop -CurlCurlIntegratorInfo -InitializeIntegratorInfo(const mfem::ParFiniteElementSpace &fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, - bool use_bdr, mfem::Coefficient *Q, mfem::VectorCoefficient *VQ, - mfem::MatrixCoefficient *MQ, - std::vector &coeff) +namespace palace { - MFEM_VERIFY(fespace.GetVDim() == 1, - "libCEED interface for CurlCurlIntegrator does not support vdim > 1!"); - CurlCurlIntegratorInfo info = {{0}}; +using namespace ceed; - mfem::ParMesh &mesh = *fespace.GetParMesh(); - info.ctx.dim = mesh.Dimension() - use_bdr; - info.ctx.space_dim = mesh.SpaceDimension(); - info.ctx.curl_dim = (info.ctx.dim < 3) ? 1 : info.ctx.dim; - - info.trial_op = ceed::EvalMode::Curl; - info.test_op = ceed::EvalMode::Curl; - info.qdata_size = (info.ctx.curl_dim * (info.ctx.curl_dim + 1)) / 2; - - mfem::ConstantCoefficient *const_coeff = dynamic_cast(Q); - if (const_coeff || !(Q || VQ || MQ)) +void CurlCurlIntegrator::Assemble(Ceed ceed, CeedElemRestriction trial_restr, + CeedElemRestriction test_restr, CeedBasis trial_basis, + CeedBasis test_basis, CeedVector geom_data, + CeedElemRestriction geom_data_restr, + CeedOperator *op) const +{ + IntegratorInfo info; + info.assemble_q_data = assemble_q_data; + + // Set up QFunctions. + CeedInt dim, space_dim, trial_num_comp, test_num_comp; + PalaceCeedCall(ceed, CeedBasisGetDimension(trial_basis, &dim)); + PalaceCeedCall(ceed, CeedGeometryDataGetSpaceDimension(geom_data_restr, dim, &space_dim)); + PalaceCeedCall(ceed, CeedBasisGetNumComponents(trial_basis, &trial_num_comp)); + PalaceCeedCall(ceed, CeedBasisGetNumComponents(test_basis, &test_num_comp)); + MFEM_VERIFY(trial_num_comp == test_num_comp && trial_num_comp == 1, + "CurlCurlIntegrator requires test and trial spaces with a single component!"); + switch (10 * space_dim + dim) { - info.ctx.coeff = const_coeff ? const_coeff->constant : 1.0; - - info.build_qf = f_build_curlcurl_const_scalar; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_curlcurl_const_scalar_loc); + case 22: + // Curl in 2D has a single component. + info.apply_qf = assemble_q_data ? f_build_l2_1 : f_apply_l2_1; + info.apply_qf_path = PalaceQFunctionRelativePath(assemble_q_data ? f_build_l2_1_loc + : f_apply_l2_1_loc); + break; + case 33: + info.apply_qf = assemble_q_data ? f_build_hdiv_33 : f_apply_hdiv_33; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hdiv_33_loc : f_apply_hdiv_33_loc); + break; + case 32: + // Curl in 2D has a single component. + info.apply_qf = assemble_q_data ? f_build_l2_1 : f_apply_l2_1; + info.apply_qf_path = PalaceQFunctionRelativePath(assemble_q_data ? f_build_l2_1_loc + : f_apply_l2_1_loc); + break; + default: + MFEM_ABORT("Invalid value of (dim, space_dim) = (" << dim << ", " << space_dim + << ") for CurlCurlIntegrator!"); } - else if (Q) + info.trial_ops = EvalMode::Curl; + info.test_ops = EvalMode::Curl; + if (dim < 3) { - ceed::InitCoefficient(*Q, mesh, ir, indices, use_bdr, coeff.emplace_back()); - - info.build_qf = f_build_curlcurl_quad_scalar; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_curlcurl_quad_scalar_loc); + info.trial_ops |= EvalMode::Weight; } - else if (VQ) - { - MFEM_VERIFY(VQ->GetVDim() == info.ctx.curl_dim, - "Invalid vector coefficient dimension for CurlCurlIntegrator!"); - ceed::InitCoefficient(*VQ, mesh, ir, indices, use_bdr, coeff.emplace_back()); - info.build_qf = f_build_curlcurl_quad_vector; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_curlcurl_quad_vector_loc); - } - else if (MQ) + // Set up the coefficient and assemble. + auto ctx = [&]() { - MFEM_VERIFY(MQ->GetVDim() == info.ctx.curl_dim, - "Invalid matrix coefficient dimension for CurlCurlIntegrator!"); - ceed::InitCoefficient(*MQ, mesh, ir, indices, use_bdr, coeff.emplace_back()); - - info.build_qf = f_build_curlcurl_quad_matrix; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_curlcurl_quad_matrix_loc); - } - - info.apply_qf = f_apply_curlcurl; - info.apply_qf_path = PalaceQFunctionRelativePath(f_apply_curlcurl_loc); - - return info; -} - -} // namespace - -void CurlCurlIntegrator::Assemble(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, - const std::vector &indices, Ceed ceed, - CeedOperator *op, CeedOperator *op_t) -{ - MFEM_VERIFY(&trial_fespace == &test_fespace, - "CurlCurlIntegrator requires the same test and trial spaces!"); - constexpr bool use_bdr = false; - std::vector coeff; - const auto info = - InitializeIntegratorInfo(trial_fespace, ir, indices, use_bdr, Q, VQ, MQ, coeff); - ceed::AssembleCeedOperator(info, trial_fespace, test_fespace, ir, indices, use_bdr, coeff, - ceed, op, op_t); -} - -void CurlCurlIntegrator::AssembleBoundary(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, - const std::vector &indices, Ceed ceed, - CeedOperator *op, CeedOperator *op_t) -{ - MFEM_VERIFY(&trial_fespace == &test_fespace, - "CurlCurlIntegrator requires the same test and trial spaces!"); - constexpr bool use_bdr = true; - std::vector coeff; - const auto info = - InitializeIntegratorInfo(trial_fespace, ir, indices, use_bdr, Q, VQ, MQ, coeff); - ceed::AssembleCeedOperator(info, trial_fespace, test_fespace, ir, indices, use_bdr, coeff, - ceed, op, op_t); + switch (dim) + { + case 2: + return PopulateCoefficientContext<1>(Q); + case 3: + return PopulateCoefficientContext<3>(Q); + } + return std::vector(); + }(); + AssembleCeedOperator(info, (void *)ctx.data(), ctx.size() * sizeof(CeedIntScalar), ceed, + trial_restr, test_restr, trial_basis, test_basis, geom_data, + geom_data_restr, op); } } // namespace palace diff --git a/palace/fem/integ/curlcurlmass.cpp b/palace/fem/integ/curlcurlmass.cpp index b7937b302..6bbd69e19 100644 --- a/palace/fem/integ/curlcurlmass.cpp +++ b/palace/fem/integ/curlcurlmass.cpp @@ -3,191 +3,80 @@ #include "fem/integrator.hpp" -#include -#include #include "fem/libceed/coefficient.hpp" #include "fem/libceed/integrator.hpp" -#include "fem/qfunctions/curlcurlmass_qf.h" +#include "fem/qfunctions/hdivmass_build_qf.h" +#include "fem/qfunctions/hdivmass_qf.h" namespace palace { -struct CurlCurlMassIntegratorInfo : public ceed::IntegratorInfo -{ - CurlCurlMassContext ctx; -}; - -namespace -{ +using namespace ceed; -CurlCurlMassIntegratorInfo -InitializeIntegratorInfo(const mfem::ParFiniteElementSpace &fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, - bool use_bdr, mfem::Coefficient *Qc, mfem::VectorCoefficient *VQc, - mfem::MatrixCoefficient *MQc, mfem::Coefficient *Qm, - mfem::VectorCoefficient *VQm, mfem::MatrixCoefficient *MQm, - std::vector &coeff) +void CurlCurlMassIntegrator::Assemble(Ceed ceed, CeedElemRestriction trial_restr, + CeedElemRestriction test_restr, CeedBasis trial_basis, + CeedBasis test_basis, CeedVector geom_data, + CeedElemRestriction geom_data_restr, + CeedOperator *op) const { - MFEM_VERIFY(fespace.GetVDim() == 1, - "libCEED interface for CurlCurlMassIntegrator does not support vdim > 1!"); - - CurlCurlMassIntegratorInfo info = {{0}}; - - mfem::ParMesh &mesh = *fespace.GetParMesh(); - info.ctx.dim = mesh.Dimension() - use_bdr; - info.ctx.space_dim = mesh.SpaceDimension(); - info.ctx.curl_dim = (info.ctx.dim < 3) ? 1 : info.ctx.dim; - - info.trial_op = ceed::EvalMode::InterpAndCurl; - info.test_op = ceed::EvalMode::InterpAndCurl; - info.qdata_size = (info.ctx.curl_dim * (info.ctx.curl_dim + 1)) / 2 + - (info.ctx.dim * (info.ctx.dim + 1)) / 2; - - MFEM_VERIFY((Qc || VQc || MQc) && (Qm || VQm || MQm), - "libCEED CurlCurlMassIntegrator requires both a " - "curl-curl and a mass integrator coefficient!"); - if (Qc) + IntegratorInfo info; + info.assemble_q_data = assemble_q_data; + + // Set up QFunctions. + CeedInt dim, space_dim, trial_num_comp, test_num_comp; + PalaceCeedCall(ceed, CeedBasisGetDimension(trial_basis, &dim)); + PalaceCeedCall(ceed, CeedGeometryDataGetSpaceDimension(geom_data_restr, dim, &space_dim)); + PalaceCeedCall(ceed, CeedBasisGetNumComponents(trial_basis, &trial_num_comp)); + PalaceCeedCall(ceed, CeedBasisGetNumComponents(test_basis, &test_num_comp)); + MFEM_VERIFY( + trial_num_comp == test_num_comp && trial_num_comp == 1, + "CurlCurlMassIntegrator requires test and trial spaces with a single component!"); + switch (10 * space_dim + dim) { - ceed::InitCoefficient(*Qc, mesh, ir, indices, use_bdr, coeff.emplace_back()); - - if (Qm) - { - ceed::InitCoefficient(*Qm, mesh, ir, indices, use_bdr, coeff.emplace_back()); - - info.build_qf = f_build_curlcurl_mass_quad_scalar_scalar; - info.build_qf_path = - PalaceQFunctionRelativePath(f_build_curlcurl_mass_quad_scalar_scalar_loc); - } - else if (VQm) - { - MFEM_VERIFY(VQm->GetVDim() == info.ctx.space_dim, - "Invalid vector coefficient dimension for CurlCurlMassIntegrator!"); - ceed::InitCoefficient(*VQm, mesh, ir, indices, use_bdr, coeff.emplace_back()); - - info.build_qf = f_build_curlcurl_mass_quad_scalar_vector; - info.build_qf_path = - PalaceQFunctionRelativePath(f_build_curlcurl_mass_quad_scalar_vector_loc); - } - else if (MQm) - { - MFEM_VERIFY(MQm->GetVDim() == info.ctx.space_dim, - "Invalid matrix coefficient dimension for CurlCurlMassIntegrator!"); - ceed::InitCoefficient(*MQm, mesh, ir, indices, use_bdr, coeff.emplace_back()); - - info.build_qf = f_build_curlcurl_mass_quad_scalar_matrix; - info.build_qf_path = - PalaceQFunctionRelativePath(f_build_curlcurl_mass_quad_scalar_matrix_loc); - } + case 22: + info.apply_qf = assemble_q_data ? f_build_hdivmass_22 : f_apply_hdivmass_22; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hdivmass_22_loc : f_apply_hdivmass_22_loc); + break; + case 33: + info.apply_qf = assemble_q_data ? f_build_hdivmass_33 : f_apply_hdivmass_33; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hdivmass_33_loc : f_apply_hdivmass_33_loc); + break; + case 32: + info.apply_qf = assemble_q_data ? f_build_hdivmass_32 : f_apply_hdivmass_32; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hdivmass_32_loc : f_apply_hdivmass_32_loc); + break; + default: + MFEM_ABORT("Invalid value of (dim, space_dim) = (" + << dim << ", " << space_dim << ") for CurlCurlMassIntegrator!"); } - else if (VQc) + info.trial_ops = EvalMode::Curl | EvalMode::Interp; + info.test_ops = EvalMode::Curl | EvalMode::Interp; + if (dim < 3) { - MFEM_VERIFY(VQc->GetVDim() == info.ctx.curl_dim, - "Invalid vector coefficient dimension for CurlCurlMassIntegrator!"); - ceed::InitCoefficient(*VQc, mesh, ir, indices, use_bdr, coeff.emplace_back()); - - if (Qm) - { - ceed::InitCoefficient(*Qm, mesh, ir, indices, use_bdr, coeff.emplace_back()); - - info.build_qf = f_build_curlcurl_mass_quad_vector_scalar; - info.build_qf_path = - PalaceQFunctionRelativePath(f_build_curlcurl_mass_quad_vector_scalar_loc); - } - else if (VQm) - { - MFEM_VERIFY(VQm->GetVDim() == info.ctx.space_dim, - "Invalid vector coefficient dimension for CurlCurlMassIntegrator!"); - ceed::InitCoefficient(*VQm, mesh, ir, indices, use_bdr, coeff.emplace_back()); - - info.build_qf = f_build_curlcurl_mass_quad_vector_vector; - info.build_qf_path = - PalaceQFunctionRelativePath(f_build_curlcurl_mass_quad_vector_vector_loc); - } - else if (MQm) - { - MFEM_VERIFY(MQm->GetVDim() == info.ctx.space_dim, - "Invalid matrix coefficient dimension for CurlCurlMassIntegrator!"); - ceed::InitCoefficient(*MQm, mesh, ir, indices, use_bdr, coeff.emplace_back()); - - info.build_qf = f_build_curlcurl_mass_quad_vector_matrix; - info.build_qf_path = - PalaceQFunctionRelativePath(f_build_curlcurl_mass_quad_vector_matrix_loc); - } + info.trial_ops |= EvalMode::Weight; } - else if (MQc) - { - MFEM_VERIFY(MQc->GetVDim() == info.ctx.curl_dim, - "Invalid matrix coefficient dimension for CurlCurlMassIntegrator!"); - ceed::InitCoefficient(*MQc, mesh, ir, indices, use_bdr, coeff.emplace_back()); - - if (Qm) - { - ceed::InitCoefficient(*Qm, mesh, ir, indices, use_bdr, coeff.emplace_back()); - info.build_qf = f_build_curlcurl_mass_quad_matrix_scalar; - info.build_qf_path = - PalaceQFunctionRelativePath(f_build_curlcurl_mass_quad_matrix_scalar_loc); - } - else if (VQm) + // Set up the coefficient and assemble. + auto ctx = [&]() + { + switch (10 * space_dim + dim) { - MFEM_VERIFY(VQm->GetVDim() == info.ctx.space_dim, - "Invalid vector coefficient dimension for CurlCurlMassIntegrator!"); - ceed::InitCoefficient(*VQm, mesh, ir, indices, use_bdr, coeff.emplace_back()); - - info.build_qf = f_build_curlcurl_mass_quad_matrix_vector; - info.build_qf_path = - PalaceQFunctionRelativePath(f_build_curlcurl_mass_quad_matrix_vector_loc); + case 22: + return PopulateCoefficientContext<1, 2>(Q, Q_mass); + case 33: + return PopulateCoefficientContext<3, 3>(Q, Q_mass); + case 32: + return PopulateCoefficientContext<1, 3>(Q, Q_mass); } - else if (MQm) - { - MFEM_VERIFY(MQm->GetVDim() == info.ctx.space_dim, - "Invalid matrix coefficient dimension for CurlCurlMassIntegrator!"); - ceed::InitCoefficient(*MQm, mesh, ir, indices, use_bdr, coeff.emplace_back()); - - info.build_qf = f_build_curlcurl_mass_quad_matrix_matrix; - info.build_qf_path = - PalaceQFunctionRelativePath(f_build_curlcurl_mass_quad_matrix_matrix_loc); - } - } - - info.apply_qf = f_apply_curlcurl_mass; - info.apply_qf_path = PalaceQFunctionRelativePath(f_apply_curlcurl_mass_loc); - - return info; -} - -} // namespace - -void CurlCurlMassIntegrator::Assemble(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, - const std::vector &indices, Ceed ceed, - CeedOperator *op, CeedOperator *op_t) -{ - MFEM_VERIFY(&trial_fespace == &test_fespace, - "CurlCurlMassIntegrator requires the same test and trial spaces!"); - constexpr bool use_bdr = false; - std::vector coeff; - const auto info = InitializeIntegratorInfo(trial_fespace, ir, indices, use_bdr, Qc, VQc, - MQc, Qm, VQm, MQm, coeff); - ceed::AssembleCeedOperator(info, trial_fespace, test_fespace, ir, indices, use_bdr, coeff, - ceed, op, op_t); -} - -void CurlCurlMassIntegrator::AssembleBoundary( - const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, const mfem::IntegrationRule &ir, - const std::vector &indices, Ceed ceed, CeedOperator *op, CeedOperator *op_t) -{ - MFEM_VERIFY(&trial_fespace == &test_fespace, - "CurlCurlMassIntegrator requires the same test and trial spaces!"); - constexpr bool use_bdr = true; - std::vector coeff; - const auto info = InitializeIntegratorInfo(trial_fespace, ir, indices, use_bdr, Qc, VQc, - MQc, Qm, VQm, MQm, coeff); - ceed::AssembleCeedOperator(info, trial_fespace, test_fespace, ir, indices, use_bdr, coeff, - ceed, op, op_t); + return std::vector(); + }(); + AssembleCeedOperator(info, (void *)ctx.data(), ctx.size() * sizeof(CeedIntScalar), ceed, + trial_restr, test_restr, trial_basis, test_basis, geom_data, + geom_data_restr, op); } } // namespace palace diff --git a/palace/fem/integ/diffusion.cpp b/palace/fem/integ/diffusion.cpp index f54f1b8d0..e92826622 100644 --- a/palace/fem/integ/diffusion.cpp +++ b/palace/fem/integ/diffusion.cpp @@ -3,116 +3,79 @@ #include "fem/integrator.hpp" -#include -#include #include "fem/libceed/coefficient.hpp" #include "fem/libceed/integrator.hpp" -#include "fem/qfunctions/diffusion_qf.h" +#include "fem/qfunctions/hcurl_build_qf.h" +#include "fem/qfunctions/hcurl_qf.h" namespace palace { -struct DiffusionIntegratorInfo : public ceed::IntegratorInfo -{ - DiffusionContext ctx; -}; - -namespace -{ +using namespace ceed; -DiffusionIntegratorInfo -InitializeIntegratorInfo(const mfem::ParFiniteElementSpace &fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, - bool use_bdr, mfem::Coefficient *Q, mfem::VectorCoefficient *VQ, - mfem::MatrixCoefficient *MQ, - std::vector &coeff) +void DiffusionIntegrator::Assemble(Ceed ceed, CeedElemRestriction trial_restr, + CeedElemRestriction test_restr, CeedBasis trial_basis, + CeedBasis test_basis, CeedVector geom_data, + CeedElemRestriction geom_data_restr, + CeedOperator *op) const { - MFEM_VERIFY(fespace.GetVDim() == 1, - "libCEED interface for DiffusionIntegrator does not support vdim > 1!"); - - DiffusionIntegratorInfo info = {{0}}; - - mfem::ParMesh &mesh = *fespace.GetParMesh(); - info.ctx.dim = mesh.Dimension() - use_bdr; - info.ctx.space_dim = mesh.SpaceDimension(); - - info.trial_op = ceed::EvalMode::Grad; - info.test_op = ceed::EvalMode::Grad; - info.qdata_size = (info.ctx.dim * (info.ctx.dim + 1)) / 2; - - mfem::ConstantCoefficient *const_coeff = dynamic_cast(Q); - if (const_coeff || !(Q || VQ || MQ)) - { - info.ctx.coeff = const_coeff ? const_coeff->constant : 1.0; - - info.build_qf = f_build_diff_const_scalar; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_diff_const_scalar_loc); - } - else if (Q) + IntegratorInfo info; + info.assemble_q_data = assemble_q_data; + + // Set up QFunctions. + CeedInt dim, space_dim, trial_num_comp, test_num_comp; + PalaceCeedCall(ceed, CeedBasisGetDimension(trial_basis, &dim)); + PalaceCeedCall(ceed, CeedGeometryDataGetSpaceDimension(geom_data_restr, dim, &space_dim)); + PalaceCeedCall(ceed, CeedBasisGetNumComponents(trial_basis, &trial_num_comp)); + PalaceCeedCall(ceed, CeedBasisGetNumComponents(test_basis, &test_num_comp)); + MFEM_VERIFY( + trial_num_comp == test_num_comp && trial_num_comp == 1, + "DiffusionIntegrator requires test and trial spaces with a single component!"); + switch (10 * space_dim + dim) { - ceed::InitCoefficient(*Q, mesh, ir, indices, use_bdr, coeff.emplace_back()); - - info.build_qf = f_build_diff_quad_scalar; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_diff_quad_scalar_loc); + case 22: + info.apply_qf = assemble_q_data ? f_build_hcurl_22 : f_apply_hcurl_22; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hcurl_22_loc : f_apply_hcurl_22_loc); + break; + case 33: + info.apply_qf = assemble_q_data ? f_build_hcurl_33 : f_apply_hcurl_33; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hcurl_33_loc : f_apply_hcurl_33_loc); + break; + case 21: + info.apply_qf = assemble_q_data ? f_build_hcurl_21 : f_apply_hcurl_21; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hcurl_21_loc : f_apply_hcurl_21_loc); + break; + case 32: + info.apply_qf = assemble_q_data ? f_build_hcurl_32 : f_apply_hcurl_32; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hcurl_32_loc : f_apply_hcurl_32_loc); + break; + default: + MFEM_ABORT("Invalid value of (dim, space_dim) = (" << dim << ", " << space_dim + << ") for DiffusionIntegrator!"); } - else if (VQ) - { - MFEM_VERIFY(VQ->GetVDim() == info.ctx.space_dim, - "Invalid vector coefficient dimension for DiffusionIntegrator!"); - ceed::InitCoefficient(*VQ, mesh, ir, indices, use_bdr, coeff.emplace_back()); + info.trial_ops = EvalMode::Grad; + info.test_ops = EvalMode::Grad; - info.build_qf = f_build_diff_quad_vector; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_diff_quad_vector_loc); - } - else if (MQ) + // Set up the coefficient and assemble. + auto ctx = [&]() { - MFEM_VERIFY(MQ->GetVDim() == info.ctx.space_dim, - "Invalid matrix coefficient dimension for DiffusionIntegrator!"); - ceed::InitCoefficient(*MQ, mesh, ir, indices, use_bdr, coeff.emplace_back()); - - info.build_qf = f_build_diff_quad_matrix; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_diff_quad_matrix_loc); - } - - info.apply_qf = f_apply_diff; - info.apply_qf_path = PalaceQFunctionRelativePath(f_apply_diff_loc); - - return info; -} - -} // namespace - -void DiffusionIntegrator::Assemble(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, - const std::vector &indices, Ceed ceed, - CeedOperator *op, CeedOperator *op_t) -{ - MFEM_VERIFY(&trial_fespace == &test_fespace, - "DiffusionIntegrator requires the same test and trial spaces!"); - constexpr bool use_bdr = false; - std::vector coeff; - const auto info = - InitializeIntegratorInfo(trial_fespace, ir, indices, use_bdr, Q, VQ, MQ, coeff); - ceed::AssembleCeedOperator(info, trial_fespace, test_fespace, ir, indices, use_bdr, coeff, - ceed, op, op_t); -} - -void DiffusionIntegrator::AssembleBoundary(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, - const std::vector &indices, Ceed ceed, - CeedOperator *op, CeedOperator *op_t) -{ - MFEM_VERIFY(&trial_fespace == &test_fespace, - "DiffusionIntegrator requires the same test and trial spaces!"); - constexpr bool use_bdr = true; - std::vector coeff; - const auto info = - InitializeIntegratorInfo(trial_fespace, ir, indices, use_bdr, Q, VQ, MQ, coeff); - ceed::AssembleCeedOperator(info, trial_fespace, test_fespace, ir, indices, use_bdr, coeff, - ceed, op, op_t); + switch (space_dim) + { + case 2: + return PopulateCoefficientContext<2>(Q); + case 3: + return PopulateCoefficientContext<3>(Q); + } + return std::vector(); + }(); + AssembleCeedOperator(info, (void *)ctx.data(), ctx.size() * sizeof(CeedIntScalar), ceed, + trial_restr, test_restr, trial_basis, test_basis, geom_data, + geom_data_restr, op); } } // namespace palace diff --git a/palace/fem/integ/diffusionmass.cpp b/palace/fem/integ/diffusionmass.cpp index a9b633c75..321cd6a57 100644 --- a/palace/fem/integ/diffusionmass.cpp +++ b/palace/fem/integ/diffusionmass.cpp @@ -3,110 +3,80 @@ #include "fem/integrator.hpp" -#include -#include #include "fem/libceed/coefficient.hpp" #include "fem/libceed/integrator.hpp" -#include "fem/qfunctions/diffusionmass_qf.h" +#include "fem/qfunctions/hcurlmass_build_qf.h" +#include "fem/qfunctions/hcurlmass_qf.h" namespace palace { -struct DiffusionMassIntegratorInfo : public ceed::IntegratorInfo -{ - DiffusionMassContext ctx; -}; - -namespace -{ +using namespace ceed; -DiffusionMassIntegratorInfo -InitializeIntegratorInfo(const mfem::ParFiniteElementSpace &fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, - bool use_bdr, mfem::Coefficient *Qd, mfem::VectorCoefficient *VQd, - mfem::MatrixCoefficient *MQd, mfem::Coefficient *Qm, - std::vector &coeff) +void DiffusionMassIntegrator::Assemble(Ceed ceed, CeedElemRestriction trial_restr, + CeedElemRestriction test_restr, + CeedBasis trial_basis, CeedBasis test_basis, + CeedVector geom_data, + CeedElemRestriction geom_data_restr, + CeedOperator *op) const { - MFEM_VERIFY(fespace.GetVDim() == 1, - "libCEED interface for DiffusionMassIntegrator does not support vdim > 1!"); - - DiffusionMassIntegratorInfo info = {{0}}; - - mfem::ParMesh &mesh = *fespace.GetParMesh(); - info.ctx.dim = mesh.Dimension() - use_bdr; - info.ctx.space_dim = mesh.SpaceDimension(); - - info.trial_op = ceed::EvalMode::InterpAndGrad; - info.test_op = ceed::EvalMode::InterpAndGrad; - info.qdata_size = (info.ctx.dim * (info.ctx.dim + 1)) / 2 + 1; - - MFEM_VERIFY((Qd || VQd || MQd) && Qm, "libCEED DiffusionMassIntegrator requires both a " - "diffusion and a mass integrator coefficient!"); - if (Qd) + IntegratorInfo info; + info.assemble_q_data = assemble_q_data; + + // Set up QFunctions. + CeedInt dim, space_dim, trial_num_comp, test_num_comp; + PalaceCeedCall(ceed, CeedBasisGetDimension(trial_basis, &dim)); + PalaceCeedCall(ceed, CeedGeometryDataGetSpaceDimension(geom_data_restr, dim, &space_dim)); + PalaceCeedCall(ceed, CeedBasisGetNumComponents(trial_basis, &trial_num_comp)); + PalaceCeedCall(ceed, CeedBasisGetNumComponents(test_basis, &test_num_comp)); + MFEM_VERIFY( + trial_num_comp == test_num_comp && trial_num_comp == 1, + "DiffusionMassIntegrator requires test and trial spaces with a single component!"); + switch (10 * space_dim + dim) { - ceed::InitCoefficient(*Qd, mesh, ir, indices, use_bdr, coeff.emplace_back()); - - info.build_qf = f_build_diff_mass_quad_scalar; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_diff_mass_quad_scalar_loc); + case 22: + info.apply_qf = assemble_q_data ? f_build_hcurlmass_22 : f_apply_hcurlmass_22; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hcurlmass_22_loc : f_apply_hcurlmass_22_loc); + break; + case 33: + info.apply_qf = assemble_q_data ? f_build_hcurlmass_33 : f_apply_hcurlmass_33; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hcurlmass_33_loc : f_apply_hcurlmass_33_loc); + break; + case 21: + info.apply_qf = assemble_q_data ? f_build_hcurlmass_21 : f_apply_hcurlmass_21; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hcurlmass_21_loc : f_apply_hcurlmass_21_loc); + break; + case 32: + info.apply_qf = assemble_q_data ? f_build_hcurlmass_32 : f_apply_hcurlmass_32; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hcurlmass_32_loc : f_apply_hcurlmass_32_loc); + break; + default: + MFEM_ABORT("Invalid value of (dim, space_dim) = (" + << dim << ", " << space_dim << ") for DiffusionMassIntegrator!"); } - else if (VQd) - { - MFEM_VERIFY(VQd->GetVDim() == info.ctx.space_dim, - "Invalid vector coefficient dimension for DiffusionMassIntegrator!"); - ceed::InitCoefficient(*VQd, mesh, ir, indices, use_bdr, coeff.emplace_back()); + info.trial_ops = EvalMode::Grad | EvalMode::Interp; + info.test_ops = EvalMode::Grad | EvalMode::Interp; - info.build_qf = f_build_diff_mass_quad_vector; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_diff_mass_quad_vector_loc); - } - else if (MQd) + // Set up the coefficient and assemble. + auto ctx = [&]() { - MFEM_VERIFY(MQd->GetVDim() == info.ctx.space_dim, - "Invalid matrix coefficient dimension for DiffusionMassIntegrator!"); - ceed::InitCoefficient(*MQd, mesh, ir, indices, use_bdr, coeff.emplace_back()); - - info.build_qf = f_build_diff_mass_quad_matrix; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_diff_mass_quad_matrix_loc); - } - ceed::InitCoefficient(*Qm, mesh, ir, indices, use_bdr, coeff.emplace_back()); - - info.apply_qf = f_apply_diff_mass; - info.apply_qf_path = PalaceQFunctionRelativePath(f_apply_diff_mass_loc); - - return info; -} - -} // namespace - -void DiffusionMassIntegrator::Assemble(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, - const std::vector &indices, Ceed ceed, - CeedOperator *op, CeedOperator *op_t) -{ - MFEM_VERIFY(&trial_fespace == &test_fespace, - "DiffusionMassIntegrator requires the same test and trial spaces!"); - constexpr bool use_bdr = false; - std::vector coeff; - const auto info = InitializeIntegratorInfo(trial_fespace, ir, indices, use_bdr, Qd, VQd, - MQd, Qm, coeff); - ceed::AssembleCeedOperator(info, trial_fespace, test_fespace, ir, indices, use_bdr, coeff, - ceed, op, op_t); -} - -void DiffusionMassIntegrator::AssembleBoundary( - const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, const mfem::IntegrationRule &ir, - const std::vector &indices, Ceed ceed, CeedOperator *op, CeedOperator *op_t) -{ - MFEM_VERIFY(&trial_fespace == &test_fespace, - "DiffusionMassIntegrator requires the same test and trial spaces!"); - constexpr bool use_bdr = true; - std::vector coeff; - const auto info = InitializeIntegratorInfo(trial_fespace, ir, indices, use_bdr, Qd, VQd, - MQd, Qm, coeff); - ceed::AssembleCeedOperator(info, trial_fespace, test_fespace, ir, indices, use_bdr, coeff, - ceed, op, op_t); + switch (space_dim) + { + case 2: + return PopulateCoefficientContext<2, 1>(Q, Q_mass); + case 3: + return PopulateCoefficientContext<3, 1>(Q, Q_mass); + } + return std::vector(); + }(); + AssembleCeedOperator(info, (void *)ctx.data(), ctx.size() * sizeof(CeedIntScalar), ceed, + trial_restr, test_restr, trial_basis, test_basis, geom_data, + geom_data_restr, op); } } // namespace palace diff --git a/palace/fem/integ/divdiv.cpp b/palace/fem/integ/divdiv.cpp index e0d7ba241..05185466c 100644 --- a/palace/fem/integ/divdiv.cpp +++ b/palace/fem/integ/divdiv.cpp @@ -3,95 +3,73 @@ #include "fem/integrator.hpp" -#include -#include #include "fem/libceed/coefficient.hpp" #include "fem/libceed/integrator.hpp" -#include "fem/qfunctions/divdiv_qf.h" +#include "fem/qfunctions/l2_build_qf.h" +#include "fem/qfunctions/l2_qf.h" namespace palace { -struct DivDivIntegratorInfo : public ceed::IntegratorInfo -{ - DivDivContext ctx; -}; - -namespace -{ +using namespace ceed; -DivDivIntegratorInfo -InitializeIntegratorInfo(const mfem::ParFiniteElementSpace &fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, - bool use_bdr, mfem::Coefficient *Q, - std::vector &coeff) +void DivDivIntegrator::Assemble(Ceed ceed, CeedElemRestriction trial_restr, + CeedElemRestriction test_restr, CeedBasis trial_basis, + CeedBasis test_basis, CeedVector geom_data, + CeedElemRestriction geom_data_restr, CeedOperator *op) const { - MFEM_VERIFY(fespace.GetVDim() == 1, - "libCEED interface for DivDivIntegrator does not support vdim > 1!"); - - DivDivIntegratorInfo info = {{0}}; - - mfem::ParMesh &mesh = *fespace.GetParMesh(); - info.ctx.dim = mesh.Dimension() - use_bdr; - info.ctx.space_dim = mesh.SpaceDimension(); - - info.trial_op = ceed::EvalMode::Div; - info.test_op = ceed::EvalMode::Div; - info.qdata_size = 1; - - mfem::ConstantCoefficient *const_coeff = dynamic_cast(Q); - if (const_coeff || !Q) - { - info.ctx.coeff = const_coeff ? const_coeff->constant : 1.0; - - info.build_qf = f_build_divdiv_const; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_divdiv_const_loc); - } - else if (Q) + IntegratorInfo info; + info.assemble_q_data = assemble_q_data; + + // Set up QFunctions. + CeedInt trial_num_comp, test_num_comp; + PalaceCeedCall(ceed, CeedBasisGetNumComponents(trial_basis, &trial_num_comp)); + PalaceCeedCall(ceed, CeedBasisGetNumComponents(test_basis, &test_num_comp)); + MFEM_VERIFY( + trial_num_comp == test_num_comp, + "DivDivIntegrator requires test and trial spaces with same number of components!"); + switch (trial_num_comp) { - ceed::InitCoefficient(*Q, mesh, ir, indices, use_bdr, coeff.emplace_back()); - - info.build_qf = f_build_divdiv_quad; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_divdiv_quad_loc); + case 1: + info.apply_qf = assemble_q_data ? f_build_l2_1 : f_apply_l2_1; + info.apply_qf_path = PalaceQFunctionRelativePath(assemble_q_data ? f_build_l2_1_loc + : f_apply_l2_1_loc); + break; + case 2: + info.apply_qf = assemble_q_data ? f_build_l2_2 : f_apply_l2_2; + info.apply_qf_path = PalaceQFunctionRelativePath(assemble_q_data ? f_build_l2_2_loc + : f_apply_l2_2_loc); + break; + case 3: + info.apply_qf = assemble_q_data ? f_build_l2_3 : f_apply_l2_3; + info.apply_qf_path = PalaceQFunctionRelativePath(assemble_q_data ? f_build_l2_3_loc + : f_apply_l2_3_loc); + break; + default: + MFEM_ABORT("Invalid value of num_comp = " << trial_num_comp + << " for DivDivIntegrator!"); } + info.trial_ops = EvalMode::Div | EvalMode::Weight; + info.test_ops = EvalMode::Div; - info.apply_qf = f_apply_divdiv; - info.apply_qf_path = PalaceQFunctionRelativePath(f_apply_divdiv_loc); - - return info; -} - -} // namespace - -void DivDivIntegrator::Assemble(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, - const std::vector &indices, Ceed ceed, - CeedOperator *op, CeedOperator *op_t) -{ - MFEM_VERIFY(&trial_fespace == &test_fespace, - "DivDivIntegrator requires the same test and trial spaces!"); - constexpr bool use_bdr = false; - std::vector coeff; - const auto info = InitializeIntegratorInfo(trial_fespace, ir, indices, use_bdr, Q, coeff); - ceed::AssembleCeedOperator(info, trial_fespace, test_fespace, ir, indices, use_bdr, coeff, - ceed, op, op_t); -} - -void DivDivIntegrator::AssembleBoundary(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, - const std::vector &indices, Ceed ceed, - CeedOperator *op, CeedOperator *op_t) -{ - MFEM_VERIFY(&trial_fespace == &test_fespace, - "DivDivIntegrator requires the same test and trial spaces!"); - constexpr bool use_bdr = true; - std::vector coeff; - const auto info = InitializeIntegratorInfo(trial_fespace, ir, indices, use_bdr, Q, coeff); - ceed::AssembleCeedOperator(info, trial_fespace, test_fespace, ir, indices, use_bdr, coeff, - ceed, op, op_t); + // Set up the coefficient and assemble. + auto ctx = [&]() + { + switch (trial_num_comp) + { + case 1: + return PopulateCoefficientContext<1>(Q); + case 2: + return PopulateCoefficientContext<2>(Q); + case 3: + return PopulateCoefficientContext<3>(Q); + } + return std::vector(); + }(); + AssembleCeedOperator(info, (void *)ctx.data(), ctx.size() * sizeof(CeedIntScalar), ceed, + trial_restr, test_restr, trial_basis, test_basis, geom_data, + geom_data_restr, op); } } // namespace palace diff --git a/palace/fem/integ/divdivmass.cpp b/palace/fem/integ/divdivmass.cpp index 01e9dfaff..4a86def20 100644 --- a/palace/fem/integ/divdivmass.cpp +++ b/palace/fem/integ/divdivmass.cpp @@ -3,110 +3,79 @@ #include "fem/integrator.hpp" -#include -#include #include "fem/libceed/coefficient.hpp" #include "fem/libceed/integrator.hpp" -#include "fem/qfunctions/divdivmass_qf.h" +#include "fem/qfunctions/l2mass_build_qf.h" +#include "fem/qfunctions/l2mass_qf.h" namespace palace { -struct DivDivMassIntegratorInfo : public ceed::IntegratorInfo -{ - DivDivMassContext ctx; -}; - -namespace -{ +using namespace ceed; -DivDivMassIntegratorInfo -InitializeIntegratorInfo(const mfem::ParFiniteElementSpace &fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, - bool use_bdr, mfem::Coefficient *Qd, mfem::Coefficient *Qm, - mfem::VectorCoefficient *VQm, mfem::MatrixCoefficient *MQm, - std::vector &coeff) +void DivDivMassIntegrator::Assemble(Ceed ceed, CeedElemRestriction trial_restr, + CeedElemRestriction test_restr, CeedBasis trial_basis, + CeedBasis test_basis, CeedVector geom_data, + CeedElemRestriction geom_data_restr, + CeedOperator *op) const { - MFEM_VERIFY(fespace.GetVDim() == 1, - "libCEED interface for DivDivMassIntegrator does not support vdim > 1!"); - - DivDivMassIntegratorInfo info = {{0}}; - - mfem::ParMesh &mesh = *fespace.GetParMesh(); - info.ctx.dim = mesh.Dimension() - use_bdr; - info.ctx.space_dim = mesh.SpaceDimension(); - - info.trial_op = ceed::EvalMode::InterpAndDiv; - info.test_op = ceed::EvalMode::InterpAndDiv; - info.qdata_size = 1 + (info.ctx.dim * (info.ctx.dim + 1)) / 2; - - MFEM_VERIFY(Qd && (Qm || VQm || MQm), "libCEED DivDivMassIntegrator requires both a " - "div-div and a mass integrator coefficient!"); - ceed::InitCoefficient(*Qd, mesh, ir, indices, use_bdr, coeff.emplace_back()); - if (Qm) + IntegratorInfo info; + info.assemble_q_data = assemble_q_data; + + // Set up QFunctions. + CeedInt dim, space_dim, trial_num_comp, test_num_comp; + PalaceCeedCall(ceed, CeedBasisGetDimension(trial_basis, &dim)); + PalaceCeedCall(ceed, CeedGeometryDataGetSpaceDimension(geom_data_restr, dim, &space_dim)); + PalaceCeedCall(ceed, CeedBasisGetNumComponents(trial_basis, &trial_num_comp)); + PalaceCeedCall(ceed, CeedBasisGetNumComponents(test_basis, &test_num_comp)); + MFEM_VERIFY( + trial_num_comp == test_num_comp && trial_num_comp == 1, + "DivDivMassIntegrator requires test and trial spaces with a single component!"); + switch (10 * space_dim + dim) { - ceed::InitCoefficient(*Qm, mesh, ir, indices, use_bdr, coeff.emplace_back()); - - info.build_qf = f_build_divdiv_mass_quad_scalar; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_divdiv_mass_quad_scalar_loc); + case 22: + info.apply_qf = assemble_q_data ? f_build_l2mass_22 : f_apply_l2mass_22; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_l2mass_22_loc : f_apply_l2mass_22_loc); + break; + case 33: + info.apply_qf = assemble_q_data ? f_build_l2mass_33 : f_apply_l2mass_33; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_l2mass_33_loc : f_apply_l2mass_33_loc); + break; + case 21: + info.apply_qf = assemble_q_data ? f_build_l2mass_21 : f_apply_l2mass_21; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_l2mass_21_loc : f_apply_l2mass_21_loc); + break; + case 32: + info.apply_qf = assemble_q_data ? f_build_l2mass_32 : f_apply_l2mass_32; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_l2mass_32_loc : f_apply_l2mass_32_loc); + break; + default: + MFEM_ABORT("Invalid value of (dim, space_dim) = (" << dim << ", " << space_dim + << ") for DivDivMassIntegrator!"); } - else if (VQm) - { - MFEM_VERIFY(VQm->GetVDim() == info.ctx.space_dim, - "Invalid vector coefficient dimension for DivDivMassIntegrator!"); - ceed::InitCoefficient(*VQm, mesh, ir, indices, use_bdr, coeff.emplace_back()); + info.trial_ops = EvalMode::Div | EvalMode::Interp | EvalMode::Weight; + info.test_ops = EvalMode::Div | EvalMode::Interp; - info.build_qf = f_build_divdiv_mass_quad_vector; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_divdiv_mass_quad_vector_loc); - } - else if (MQm) + // Set up the coefficient and assemble. + auto ctx = [&]() { - MFEM_VERIFY(MQm->GetVDim() == info.ctx.space_dim, - "Invalid matrix coefficient dimension for DivDivMassIntegrator!"); - ceed::InitCoefficient(*MQm, mesh, ir, indices, use_bdr, coeff.emplace_back()); - - info.build_qf = f_build_divdiv_mass_quad_matrix; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_divdiv_mass_quad_matrix_loc); - } - - info.apply_qf = f_apply_divdiv_mass; - info.apply_qf_path = PalaceQFunctionRelativePath(f_apply_divdiv_mass_loc); - - return info; -} - -} // namespace - -void DivDivMassIntegrator::Assemble(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, - const std::vector &indices, Ceed ceed, - CeedOperator *op, CeedOperator *op_t) -{ - MFEM_VERIFY(&trial_fespace == &test_fespace, - "DivDivMassIntegrator requires the same test and trial spaces!"); - constexpr bool use_bdr = false; - std::vector coeff; - const auto info = InitializeIntegratorInfo(trial_fespace, ir, indices, use_bdr, Qd, Qm, - VQm, MQm, coeff); - ceed::AssembleCeedOperator(info, trial_fespace, test_fespace, ir, indices, use_bdr, coeff, - ceed, op, op_t); -} - -void DivDivMassIntegrator::AssembleBoundary( - const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, const mfem::IntegrationRule &ir, - const std::vector &indices, Ceed ceed, CeedOperator *op, CeedOperator *op_t) -{ - MFEM_VERIFY(&trial_fespace == &test_fespace, - "DivDivMassIntegrator requires the same test and trial spaces!"); - constexpr bool use_bdr = true; - std::vector coeff; - const auto info = InitializeIntegratorInfo(trial_fespace, ir, indices, use_bdr, Qd, Qm, - VQm, MQm, coeff); - ceed::AssembleCeedOperator(info, trial_fespace, test_fespace, ir, indices, use_bdr, coeff, - ceed, op, op_t); + switch (space_dim) + { + case 2: + return PopulateCoefficientContext<1, 2>(Q, Q_mass); + case 3: + return PopulateCoefficientContext<1, 3>(Q, Q_mass); + } + return std::vector(); + }(); + AssembleCeedOperator(info, (void *)ctx.data(), ctx.size() * sizeof(CeedIntScalar), ceed, + trial_restr, test_restr, trial_basis, test_basis, geom_data, + geom_data_restr, op); } } // namespace palace diff --git a/palace/fem/integ/grad.cpp b/palace/fem/integ/grad.cpp index aaf0ddb8d..612cac575 100644 --- a/palace/fem/integ/grad.cpp +++ b/palace/fem/integ/grad.cpp @@ -3,113 +3,79 @@ #include "fem/integrator.hpp" -#include -#include #include "fem/libceed/coefficient.hpp" #include "fem/libceed/integrator.hpp" -#include "fem/qfunctions/grad_qf.h" +#include "fem/qfunctions/hcurlh1d_build_qf.h" +#include "fem/qfunctions/hcurlh1d_qf.h" namespace palace { -struct GradientIntegratorInfo : public ceed::IntegratorInfo -{ - GradContext ctx; -}; - -namespace -{ +using namespace ceed; -GradientIntegratorInfo -InitializeIntegratorInfo(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, - bool use_bdr, mfem::Coefficient *Q, mfem::VectorCoefficient *VQ, - mfem::MatrixCoefficient *MQ, - std::vector &coeff) +void GradientIntegrator::Assemble(Ceed ceed, CeedElemRestriction trial_restr, + CeedElemRestriction test_restr, CeedBasis trial_basis, + CeedBasis test_basis, CeedVector geom_data, + CeedElemRestriction geom_data_restr, + CeedOperator *op) const { - GradientIntegratorInfo info = {{0}}; - - mfem::ParMesh &mesh = *trial_fespace.GetParMesh(); - info.ctx.dim = mesh.Dimension() - use_bdr; - info.ctx.space_dim = mesh.SpaceDimension(); - MFEM_VERIFY(trial_fespace.GetVDim() == 1 && test_fespace.GetVDim() == info.ctx.space_dim, - "libCEED interface for GradientIntegrator requires trial space vdim == 1 and " - "test space vdim == space dimension!"); - - info.trial_op = ceed::EvalMode::Grad; - info.test_op = ceed::EvalMode::Interp; - info.qdata_size = info.ctx.space_dim * info.ctx.dim; - - mfem::ConstantCoefficient *const_coeff = dynamic_cast(Q); - if (const_coeff || !(Q || VQ || MQ)) + IntegratorInfo info; + info.assemble_q_data = assemble_q_data; + + // Set up QFunctions. + CeedInt dim, space_dim, trial_num_comp, test_num_comp; + PalaceCeedCall(ceed, CeedBasisGetDimension(trial_basis, &dim)); + PalaceCeedCall(ceed, CeedGeometryDataGetSpaceDimension(geom_data_restr, dim, &space_dim)); + PalaceCeedCall(ceed, CeedBasisGetNumComponents(trial_basis, &trial_num_comp)); + PalaceCeedCall(ceed, CeedBasisGetNumComponents(test_basis, &test_num_comp)); + MFEM_VERIFY(trial_num_comp == 1 && test_num_comp == space_dim, + "GradientIntegrator requires trial space with a single component and test " + "space with space_dim components!"); + switch (10 * space_dim + dim) { - info.ctx.coeff = const_coeff ? const_coeff->constant : 1.0; - - info.build_qf = f_build_grad_const_scalar; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_grad_const_scalar_loc); - } - else if (Q) - { - ceed::InitCoefficient(*Q, mesh, ir, indices, use_bdr, coeff.emplace_back()); - - info.build_qf = f_build_grad_quad_scalar; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_grad_quad_scalar_loc); + case 22: + info.apply_qf = assemble_q_data ? f_build_hcurlh1d_22 : f_apply_hcurlh1d_22; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hcurlh1d_22_loc : f_apply_hcurlh1d_22_loc); + break; + case 33: + info.apply_qf = assemble_q_data ? f_build_hcurlh1d_33 : f_apply_hcurlh1d_33; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hcurlh1d_33_loc : f_apply_hcurlh1d_33_loc); + break; + case 21: + info.apply_qf = assemble_q_data ? f_build_hcurlh1d_21 : f_apply_hcurlh1d_21; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hcurlh1d_21_loc : f_apply_hcurlh1d_21_loc); + break; + case 32: + info.apply_qf = assemble_q_data ? f_build_hcurlh1d_32 : f_apply_hcurlh1d_32; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hcurlh1d_32_loc : f_apply_hcurlh1d_32_loc); + break; + default: + MFEM_ABORT("Invalid value of (dim, space_dim) = (" << dim << ", " << space_dim + << ") for GradientIntegrator!"); } - else if (VQ) - { - MFEM_VERIFY(VQ->GetVDim() == info.ctx.space_dim, - "Invalid vector coefficient dimension for GradientIntegrator integrator!"); - ceed::InitCoefficient(*VQ, mesh, ir, indices, use_bdr, coeff.emplace_back()); + info.trial_ops = EvalMode::Grad; + info.test_ops = EvalMode::Interp; - info.build_qf = f_build_grad_quad_vector; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_grad_quad_vector_loc); - } - else if (MQ) + // Set up the coefficient and assemble. + auto ctx = [&]() { - MFEM_VERIFY(MQ->GetVDim() == info.ctx.space_dim, - "Invalid matrix coefficient dimension for GradientIntegrator integrator!"); - ceed::InitCoefficient(*MQ, mesh, ir, indices, use_bdr, coeff.emplace_back()); - - info.build_qf = f_build_grad_quad_matrix; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_grad_quad_matrix_loc); - } - - info.apply_qf = f_apply_grad; - info.apply_qf_path = PalaceQFunctionRelativePath(f_apply_grad_loc); - - return info; -} - -} // namespace - -void GradientIntegrator::Assemble(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, - const std::vector &indices, Ceed ceed, - CeedOperator *op, CeedOperator *op_t) -{ - constexpr bool use_bdr = false; - std::vector coeff; - const auto info = InitializeIntegratorInfo(trial_fespace, test_fespace, ir, indices, - use_bdr, Q, VQ, MQ, coeff); - ceed::AssembleCeedOperator(info, trial_fespace, test_fespace, ir, indices, use_bdr, coeff, - ceed, op, op_t); -} - -void GradientIntegrator::AssembleBoundary(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, - const std::vector &indices, Ceed ceed, - CeedOperator *op, CeedOperator *op_t) -{ - constexpr bool use_bdr = true; - std::vector coeff; - const auto info = InitializeIntegratorInfo(trial_fespace, test_fespace, ir, indices, - use_bdr, Q, VQ, MQ, coeff); - ceed::AssembleCeedOperator(info, trial_fespace, test_fespace, ir, indices, use_bdr, coeff, - ceed, op, op_t); + switch (space_dim) + { + case 2: + return PopulateCoefficientContext<2>(Q); + case 3: + return PopulateCoefficientContext<3>(Q); + } + return std::vector(); + }(); + AssembleCeedOperator(info, (void *)ctx.data(), ctx.size() * sizeof(CeedIntScalar), ceed, + trial_restr, test_restr, trial_basis, test_basis, geom_data, + geom_data_restr, op); } } // namespace palace diff --git a/palace/fem/integ/mass.cpp b/palace/fem/integ/mass.cpp index adecb6b5b..cecc43880 100644 --- a/palace/fem/integ/mass.cpp +++ b/palace/fem/integ/mass.cpp @@ -3,127 +3,73 @@ #include "fem/integrator.hpp" -#include -#include #include "fem/libceed/coefficient.hpp" #include "fem/libceed/integrator.hpp" -#include "fem/qfunctions/mass_qf.h" +#include "fem/qfunctions/h1_build_qf.h" +#include "fem/qfunctions/h1_qf.h" namespace palace { -struct MassIntegratorInfo : public ceed::IntegratorInfo -{ - MassContext ctx; -}; - -namespace -{ +using namespace ceed; -MassIntegratorInfo InitializeIntegratorInfo(const mfem::ParFiniteElementSpace &fespace, - const mfem::IntegrationRule &ir, - const std::vector &indices, bool use_bdr, - mfem::Coefficient *Q, - mfem::VectorCoefficient *VQ, - mfem::MatrixCoefficient *MQ, - std::vector &coeff) +void MassIntegrator::Assemble(Ceed ceed, CeedElemRestriction trial_restr, + CeedElemRestriction test_restr, CeedBasis trial_basis, + CeedBasis test_basis, CeedVector geom_data, + CeedElemRestriction geom_data_restr, CeedOperator *op) const { - MassIntegratorInfo info = {{0}}; - - mfem::ParMesh &mesh = *fespace.GetParMesh(); - info.ctx.dim = mesh.Dimension() - use_bdr; - info.ctx.space_dim = mesh.SpaceDimension(); - info.ctx.vdim = fespace.GetVDim(); - - info.trial_op = ceed::EvalMode::Interp; - info.test_op = ceed::EvalMode::Interp; - - mfem::ConstantCoefficient *const_coeff = dynamic_cast(Q); - if (const_coeff || !(Q || VQ || MQ)) + IntegratorInfo info; + info.assemble_q_data = assemble_q_data; + + // Set up QFunctions. + CeedInt trial_num_comp, test_num_comp; + PalaceCeedCall(ceed, CeedBasisGetNumComponents(trial_basis, &trial_num_comp)); + PalaceCeedCall(ceed, CeedBasisGetNumComponents(test_basis, &test_num_comp)); + MFEM_VERIFY( + trial_num_comp == test_num_comp, + "MassIntegrator requires test and trial spaces with same number of components!"); + switch (trial_num_comp) { - info.qdata_size = 1; - info.ctx.coeff = const_coeff ? const_coeff->constant : 1.0; - - info.build_qf = f_build_mass_const_scalar; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_mass_const_scalar_loc); - - info.apply_qf = f_apply_mass_scalar; - info.apply_qf_path = PalaceQFunctionRelativePath(f_apply_mass_scalar_loc); - } - else if (Q) - { - info.qdata_size = 1; - ceed::InitCoefficient(*Q, mesh, ir, indices, use_bdr, coeff.emplace_back()); - - info.build_qf = f_build_mass_quad_scalar; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_mass_quad_scalar_loc); - - info.apply_qf = f_apply_mass_scalar; - info.apply_qf_path = PalaceQFunctionRelativePath(f_apply_mass_scalar_loc); + case 1: + info.apply_qf = assemble_q_data ? f_build_h1_1 : f_apply_h1_1; + info.apply_qf_path = PalaceQFunctionRelativePath(assemble_q_data ? f_build_h1_1_loc + : f_apply_h1_1_loc); + break; + case 2: + info.apply_qf = assemble_q_data ? f_build_h1_2 : f_apply_h1_2; + info.apply_qf_path = PalaceQFunctionRelativePath(assemble_q_data ? f_build_h1_2_loc + : f_apply_h1_2_loc); + break; + case 3: + info.apply_qf = assemble_q_data ? f_build_h1_3 : f_apply_h1_3; + info.apply_qf_path = PalaceQFunctionRelativePath(assemble_q_data ? f_build_h1_3_loc + : f_apply_h1_3_loc); + break; + default: + MFEM_ABORT("Invalid value of num_comp = " << trial_num_comp + << " for MassIntegrator!"); } - else if (VQ) - { - MFEM_VERIFY(VQ->GetVDim() == info.ctx.vdim, - "Invalid vector coefficient dimension for vector MassIntegrator!"); - info.qdata_size = info.ctx.vdim; - ceed::InitCoefficient(*VQ, mesh, ir, indices, use_bdr, coeff.emplace_back()); + info.trial_ops = EvalMode::Interp; + info.test_ops = EvalMode::Interp; - info.build_qf = f_build_mass_quad_vector; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_mass_quad_vector_loc); - - info.apply_qf = f_apply_mass_vector; - info.apply_qf_path = PalaceQFunctionRelativePath(f_apply_mass_vector_loc); - } - else if (MQ) + // Set up the coefficient and assemble. + auto ctx = [&]() { - MFEM_VERIFY(MQ->GetVDim() == info.ctx.vdim, - "Invalid matrix coefficient dimension for vector MassIntegrator!"); - info.qdata_size = (info.ctx.vdim * (info.ctx.vdim + 1)) / 2; - ceed::InitCoefficient(*MQ, mesh, ir, indices, use_bdr, coeff.emplace_back()); - - info.build_qf = f_build_mass_quad_matrix; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_mass_quad_matrix_loc); - - info.apply_qf = f_apply_mass_matrix; - info.apply_qf_path = PalaceQFunctionRelativePath(f_apply_mass_matrix_loc); - } - - return info; -} - -} // namespace - -void MassIntegrator::Assemble(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, - const std::vector &indices, Ceed ceed, CeedOperator *op, - CeedOperator *op_t) -{ - MFEM_VERIFY(&trial_fespace == &test_fespace, - "MassIntegrator requires the same test and trial spaces!"); - constexpr bool use_bdr = false; - std::vector coeff; - const auto info = - InitializeIntegratorInfo(trial_fespace, ir, indices, use_bdr, Q, VQ, MQ, coeff); - ceed::AssembleCeedOperator(info, trial_fespace, test_fespace, ir, indices, use_bdr, coeff, - ceed, op, op_t); -} - -void MassIntegrator::AssembleBoundary(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, - const std::vector &indices, Ceed ceed, - CeedOperator *op, CeedOperator *op_t) -{ - MFEM_VERIFY(&trial_fespace == &test_fespace, - "MassIntegrator requires the same test and trial spaces!"); - constexpr bool use_bdr = true; - std::vector coeff; - const auto info = - InitializeIntegratorInfo(trial_fespace, ir, indices, use_bdr, Q, VQ, MQ, coeff); - ceed::AssembleCeedOperator(info, trial_fespace, test_fespace, ir, indices, use_bdr, coeff, - ceed, op, op_t); + switch (trial_num_comp) + { + case 1: + return PopulateCoefficientContext<1>(Q); + case 2: + return PopulateCoefficientContext<2>(Q); + case 3: + return PopulateCoefficientContext<3>(Q); + } + return std::vector(); + }(); + AssembleCeedOperator(info, (void *)ctx.data(), ctx.size() * sizeof(CeedIntScalar), ceed, + trial_restr, test_restr, trial_basis, test_basis, geom_data, + geom_data_restr, op); } } // namespace palace diff --git a/palace/fem/integ/mixedveccurl.cpp b/palace/fem/integ/mixedveccurl.cpp index 23068297e..5b04541f1 100644 --- a/palace/fem/integ/mixedveccurl.cpp +++ b/palace/fem/integ/mixedveccurl.cpp @@ -3,247 +3,117 @@ #include "fem/integrator.hpp" -#include -#include #include "fem/libceed/coefficient.hpp" #include "fem/libceed/integrator.hpp" +#include "utils/diagnostic.hpp" +PalacePragmaDiagnosticPush +PalacePragmaDiagnosticDisableUnused + +#include "fem/qfunctions/hcurlhdiv_build_qf.h" #include "fem/qfunctions/hcurlhdiv_qf.h" +#include "fem/qfunctions/hdiv_build_qf.h" #include "fem/qfunctions/hdiv_qf.h" -namespace palace -{ +PalacePragmaDiagnosticPop -struct MixedVectorCurlIntegratorInfo : public ceed::IntegratorInfo +namespace palace { - VectorFEMassContext ctx; -}; -namespace -{ +using namespace ceed; -MixedVectorCurlIntegratorInfo -InitializeIntegratorInfo(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, - bool use_bdr, mfem::Coefficient *Q, mfem::VectorCoefficient *VQ, - mfem::MatrixCoefficient *MQ, - std::vector &coeff, - ceed::EvalMode trial_op, ceed::EvalMode test_op) +void MixedVectorCurlIntegrator::Assemble(Ceed ceed, CeedElemRestriction trial_restr, + CeedElemRestriction test_restr, + CeedBasis trial_basis, CeedBasis test_basis, + CeedVector geom_data, + CeedElemRestriction geom_data_restr, + CeedOperator *op) const { + IntegratorInfo info; + info.assemble_q_data = assemble_q_data; + + // Set up QFunctions. + CeedInt dim, space_dim, trial_num_comp, test_num_comp; + PalaceCeedCall(ceed, CeedBasisGetDimension(trial_basis, &dim)); + PalaceCeedCall(ceed, CeedGeometryDataGetSpaceDimension(geom_data_restr, dim, &space_dim)); + MFEM_VERIFY(dim == 3 && space_dim == 3, + "MixedVectorCurlIntegrator is only availble in 3D!"); + PalaceCeedCall(ceed, CeedBasisGetNumComponents(trial_basis, &trial_num_comp)); + PalaceCeedCall(ceed, CeedBasisGetNumComponents(test_basis, &test_num_comp)); MFEM_VERIFY( - trial_fespace.GetVDim() == 1 && test_fespace.GetVDim() == 1, - "libCEED interface for MixedVectorCurlIntegrator/MixedVectorWeakCurlIntegrator does " - "not support vdim > 1!"); - - MixedVectorCurlIntegratorInfo info = {{0}}; - - mfem::ParMesh &mesh = *trial_fespace.GetParMesh(); - info.ctx.dim = mesh.Dimension() - use_bdr; - info.ctx.space_dim = mesh.SpaceDimension(); - MFEM_VERIFY( - info.ctx.dim == 3 && info.ctx.space_dim == 3, - "MixedVectorCurlIntegrator/MixedVectorWeakCurlIntegrator is only availble in 3D!"); - - int trial_map_type = trial_fespace.FEColl()->GetMapType(info.ctx.dim); - int test_map_type = test_fespace.FEColl()->GetMapType(info.ctx.dim); - MFEM_VERIFY( - (trial_op == ceed::EvalMode::Curl && trial_map_type == mfem::FiniteElement::H_CURL && - (test_op == ceed::EvalMode::Interp && - (test_map_type == mfem::FiniteElement::H_CURL || - test_map_type == mfem::FiniteElement::H_DIV))) || - (test_op == ceed::EvalMode::Curl && - test_map_type == mfem::FiniteElement::H_CURL && - (trial_op == ceed::EvalMode::Interp && - (trial_map_type == mfem::FiniteElement::H_CURL || - trial_map_type == mfem::FiniteElement::H_DIV))), - "libCEED interface for MixedVectorCurlIntegrator/MixedVectorWeakCurlIntegrator " - "requires H(curl) or mixed H(curl) and H(div) FE spaces!"); - - info.trial_op = trial_op; - info.test_op = test_op; - if (trial_map_type == mfem::FiniteElement::H_CURL && - test_map_type == mfem::FiniteElement::H_CURL) + trial_num_comp == test_num_comp && trial_num_comp == 1, + "MixedVectorCurlIntegrator requires test and trial spaces with a single component!"); + if (test_map_type == mfem::FiniteElement::H_DIV) + { + info.apply_qf = assemble_q_data ? f_build_hdiv_33 : f_apply_hdiv_33; + info.apply_qf_path = PalaceQFunctionRelativePath(assemble_q_data ? f_build_hdiv_33_loc + : f_apply_hdiv_33_loc); + } + else if (test_map_type == mfem::FiniteElement::H_CURL) { - // Quadrature data is nonsymmetric in this case. - info.qdata_size = info.ctx.dim * info.ctx.dim; - info.ctx.sym = false; + info.apply_qf = assemble_q_data ? f_build_hdivhcurl_33 : f_apply_hdivhcurl_33; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hdivhcurl_33_loc : f_apply_hdivhcurl_33_loc); } else { - info.qdata_size = (info.ctx.dim * (info.ctx.dim + 1)) / 2; - info.ctx.sym = true; + MFEM_ABORT("Invalid trial/test element map type for MixedVectorCurlIntegrator!"); } + info.trial_ops = EvalMode::Curl; + info.test_ops = EvalMode::Interp; + + // Set up the coefficient and assemble. + auto ctx = PopulateCoefficientContext<3>(Q); + AssembleCeedOperator(info, (void *)ctx.data(), ctx.size() * sizeof(CeedIntScalar), ceed, + trial_restr, test_restr, trial_basis, test_basis, geom_data, + geom_data_restr, op); +} - mfem::ConstantCoefficient *const_coeff = dynamic_cast(Q); - if (const_coeff || !(Q || VQ || MQ)) +void MixedVectorWeakCurlIntegrator::Assemble(Ceed ceed, CeedElemRestriction trial_restr, + CeedElemRestriction test_restr, + CeedBasis trial_basis, CeedBasis test_basis, + CeedVector geom_data, + CeedElemRestriction geom_data_restr, + CeedOperator *op) const +{ + IntegratorInfo info; + info.assemble_q_data = assemble_q_data; + + // Set up QFunctions. + CeedInt dim, space_dim, trial_num_comp, test_num_comp; + PalaceCeedCall(ceed, CeedBasisGetDimension(trial_basis, &dim)); + PalaceCeedCall(ceed, CeedGeometryDataGetSpaceDimension(geom_data_restr, dim, &space_dim)); + MFEM_VERIFY(dim == 3 && space_dim == 3, + "MixedVectorWeakCurlIntegrator is only availble in 3D!"); + PalaceCeedCall(ceed, CeedBasisGetNumComponents(trial_basis, &trial_num_comp)); + PalaceCeedCall(ceed, CeedBasisGetNumComponents(test_basis, &test_num_comp)); + MFEM_VERIFY(trial_num_comp == test_num_comp && trial_num_comp == 1, + "MixedVectorWeakCurlIntegrator requires test and trial spaces with a single " + "component!"); + if (trial_map_type == mfem::FiniteElement::H_DIV) { - info.ctx.coeff = const_coeff ? const_coeff->constant : 1.0; - - if (trial_map_type == mfem::FiniteElement::H_CURL && - test_map_type == mfem::FiniteElement::H_CURL) - { - if (trial_op == ceed::EvalMode::Curl) - { - info.build_qf = f_build_hdivhcurl_const_scalar; - info.build_qf_path = - PalaceQFunctionRelativePath(f_build_hdivhcurl_const_scalar_loc); - } - else // test_op == ceed::EvalMode::Curl - { - info.build_qf = f_build_hcurlhdiv_const_scalar; - info.build_qf_path = - PalaceQFunctionRelativePath(f_build_hcurlhdiv_const_scalar_loc); - } - } - else - { - info.build_qf = f_build_hdiv_const_scalar; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_hdiv_const_scalar_loc); - } + info.apply_qf = assemble_q_data ? f_build_hdiv_33 : f_apply_hdiv_33; + info.apply_qf_path = PalaceQFunctionRelativePath(assemble_q_data ? f_build_hdiv_33_loc + : f_apply_hdiv_33_loc); } - else if (Q) + else if (trial_map_type == mfem::FiniteElement::H_CURL) { - ceed::InitCoefficient(*Q, mesh, ir, indices, use_bdr, coeff.emplace_back()); - - if (trial_map_type == mfem::FiniteElement::H_CURL && - test_map_type == mfem::FiniteElement::H_CURL) - { - if (trial_op == ceed::EvalMode::Curl) - { - info.build_qf = f_build_hdivhcurl_quad_scalar; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_hdivhcurl_quad_scalar_loc); - } - else // test_op == ceed::EvalMode::Curl - { - info.build_qf = f_build_hcurlhdiv_quad_scalar; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_hcurlhdiv_quad_scalar_loc); - } - } - else - { - info.build_qf = f_build_hdiv_quad_scalar; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_hdiv_quad_scalar_loc); - } + info.apply_qf = assemble_q_data ? f_build_hcurlhdiv_33 : f_apply_hcurlhdiv_33; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hcurlhdiv_33_loc : f_apply_hcurlhdiv_33_loc); } - else if (VQ) - { - MFEM_VERIFY(VQ->GetVDim() == info.ctx.space_dim, - "Invalid vector coefficient dimension for " - "MixedVectorCurlIntegrator/MixedVectorWeakCurlIntegrator integrator!"); - ceed::InitCoefficient(*VQ, mesh, ir, indices, use_bdr, coeff.emplace_back()); - - if (trial_map_type == mfem::FiniteElement::H_CURL && - test_map_type == mfem::FiniteElement::H_CURL) - { - if (trial_op == ceed::EvalMode::Curl) - { - info.build_qf = f_build_hdivhcurl_quad_vector; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_hdivhcurl_quad_vector_loc); - } - else // test_op == ceed::EvalMode::Curl - { - info.build_qf = f_build_hcurlhdiv_quad_vector; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_hcurlhdiv_quad_vector_loc); - } - } - else - { - info.build_qf = f_build_hdiv_quad_vector; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_hdiv_quad_vector_loc); - } - } - else if (MQ) + else { - MFEM_VERIFY(MQ->GetVDim() == info.ctx.space_dim, - "Invalid matrix coefficient dimension for " - "MixedVectorCurlIntegrator/MixedVectorWeakCurlIntegrator integrator!"); - ceed::InitCoefficient(*MQ, mesh, ir, indices, use_bdr, coeff.emplace_back()); - - if (trial_map_type == mfem::FiniteElement::H_CURL && - test_map_type == mfem::FiniteElement::H_CURL) - { - if (trial_op == ceed::EvalMode::Curl) - { - info.build_qf = f_build_hdivhcurl_quad_matrix; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_hdivhcurl_quad_matrix_loc); - } - else // test_op == ceed::EvalMode::Curl - { - info.build_qf = f_build_hcurlhdiv_quad_matrix; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_hcurlhdiv_quad_matrix_loc); - } - } - else - { - info.build_qf = f_build_hdiv_quad_matrix; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_hdiv_quad_matrix_loc); - } + MFEM_ABORT("Invalid trial/test element map type for MixedVectorWeakCurlIntegrator!"); } - - info.apply_qf = f_apply_vecfemass; - info.apply_qf_path = PalaceQFunctionRelativePath(f_apply_vecfemass_loc); - - return info; -} - -} // namespace - -void MixedVectorCurlIntegrator::Assemble(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, - const std::vector &indices, Ceed ceed, - CeedOperator *op, CeedOperator *op_t) -{ - constexpr bool use_bdr = false; - std::vector coeff; - const auto info = - InitializeIntegratorInfo(trial_fespace, test_fespace, ir, indices, use_bdr, Q, VQ, MQ, - coeff, ceed::EvalMode::Curl, ceed::EvalMode::Interp); - ceed::AssembleCeedOperator(info, trial_fespace, test_fespace, ir, indices, use_bdr, coeff, - ceed, op, op_t); -} - -void MixedVectorCurlIntegrator::AssembleBoundary( - const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, const mfem::IntegrationRule &ir, - const std::vector &indices, Ceed ceed, CeedOperator *op, CeedOperator *op_t) -{ - constexpr bool use_bdr = true; - std::vector coeff; - const auto info = - InitializeIntegratorInfo(trial_fespace, test_fespace, ir, indices, use_bdr, Q, VQ, MQ, - coeff, ceed::EvalMode::Curl, ceed::EvalMode::Interp); - ceed::AssembleCeedOperator(info, trial_fespace, test_fespace, ir, indices, use_bdr, coeff, - ceed, op, op_t); -} - -void MixedVectorWeakCurlIntegrator::Assemble( - const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, const mfem::IntegrationRule &ir, - const std::vector &indices, Ceed ceed, CeedOperator *op, CeedOperator *op_t) -{ - constexpr bool use_bdr = false; - std::vector coeff; - const auto info = - InitializeIntegratorInfo(trial_fespace, test_fespace, ir, indices, use_bdr, Q, VQ, MQ, - coeff, ceed::EvalMode::Interp, ceed::EvalMode::Curl); - ceed::AssembleCeedOperator(info, trial_fespace, test_fespace, ir, indices, use_bdr, coeff, - ceed, op, op_t); -} - -void MixedVectorWeakCurlIntegrator::AssembleBoundary( - const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, const mfem::IntegrationRule &ir, - const std::vector &indices, Ceed ceed, CeedOperator *op, CeedOperator *op_t) -{ - constexpr bool use_bdr = true; - std::vector coeff; - const auto info = - InitializeIntegratorInfo(trial_fespace, test_fespace, ir, indices, use_bdr, Q, VQ, MQ, - coeff, ceed::EvalMode::Interp, ceed::EvalMode::Curl); - ceed::AssembleCeedOperator(info, trial_fespace, test_fespace, ir, indices, use_bdr, coeff, - ceed, op, op_t); + info.trial_ops = EvalMode::Interp; + info.test_ops = EvalMode::Curl; + + // Set up the coefficient and assemble. + auto ctx = PopulateCoefficientContext<3>(Q); + AssembleCeedOperator(info, (void *)ctx.data(), ctx.size() * sizeof(CeedIntScalar), ceed, + trial_restr, test_restr, trial_basis, test_basis, geom_data, + geom_data_restr, op); } } // namespace palace diff --git a/palace/fem/integ/mixedvecgrad.cpp b/palace/fem/integ/mixedvecgrad.cpp index 6ee3280c4..48188691f 100644 --- a/palace/fem/integ/mixedvecgrad.cpp +++ b/palace/fem/integ/mixedvecgrad.cpp @@ -3,168 +3,145 @@ #include "fem/integrator.hpp" -#include -#include #include "fem/libceed/coefficient.hpp" #include "fem/libceed/integrator.hpp" +#include "fem/qfunctions/hcurl_build_qf.h" #include "fem/qfunctions/hcurl_qf.h" namespace palace { -struct MixedVectorGradientIntegratorInfo : public ceed::IntegratorInfo -{ - VectorFEMassContext ctx; -}; - -namespace -{ +using namespace ceed; -MixedVectorGradientIntegratorInfo -InitializeIntegratorInfo(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, - bool use_bdr, mfem::Coefficient *Q, mfem::VectorCoefficient *VQ, - mfem::MatrixCoefficient *MQ, - std::vector &coeff) +void MixedVectorGradientIntegrator::Assemble(Ceed ceed, CeedElemRestriction trial_restr, + CeedElemRestriction test_restr, + CeedBasis trial_basis, CeedBasis test_basis, + CeedVector geom_data, + CeedElemRestriction geom_data_restr, + CeedOperator *op) const { - MFEM_VERIFY(trial_fespace.GetVDim() == 1 && test_fespace.GetVDim() == 1, - "libCEED interface for " - "MixedVectorGradientIntegrator/MixedVectorWeakDivergenceIntegrator does not " - "support vdim > 1!"); - - MixedVectorGradientIntegratorInfo info = {{0}}; - - mfem::ParMesh &mesh = *trial_fespace.GetParMesh(); - info.ctx.dim = mesh.Dimension() - use_bdr; - info.ctx.space_dim = mesh.SpaceDimension(); - - int trial_map_type = trial_fespace.FEColl()->GetMapType(info.ctx.dim); - int trial_deriv_map_type = trial_fespace.FEColl()->GetDerivMapType(info.ctx.dim); - int test_map_type = test_fespace.FEColl()->GetMapType(info.ctx.dim); - int test_deriv_map_type = test_fespace.FEColl()->GetDerivMapType(info.ctx.dim); - MFEM_VERIFY((trial_map_type == mfem::FiniteElement::H_CURL && - test_deriv_map_type == mfem::FiniteElement::H_CURL) || - (trial_deriv_map_type == mfem::FiniteElement::H_CURL && - test_map_type == mfem::FiniteElement::H_CURL), - "libCEED interface for " - "MixedVectorGradientIntegrator/MixedVectorWeakDivergenceIntegrator requires " - "mixed H1 and H(curl) FE spaces!"); - - info.trial_op = (trial_map_type == mfem::FiniteElement::H_CURL) ? ceed::EvalMode::Interp - : ceed::EvalMode::Grad; - info.test_op = (test_map_type == mfem::FiniteElement::H_CURL) ? ceed::EvalMode::Interp - : ceed::EvalMode::Grad; - info.qdata_size = (info.ctx.dim * (info.ctx.dim + 1)) / 2; - info.ctx.sym = true; - - mfem::ConstantCoefficient *const_coeff = dynamic_cast(Q); - if (const_coeff || !(Q || VQ || MQ)) + IntegratorInfo info; + info.assemble_q_data = assemble_q_data; + + // Set up QFunctions. + CeedInt dim, space_dim, trial_num_comp, test_num_comp; + PalaceCeedCall(ceed, CeedBasisGetDimension(trial_basis, &dim)); + PalaceCeedCall(ceed, CeedGeometryDataGetSpaceDimension(geom_data_restr, dim, &space_dim)); + PalaceCeedCall(ceed, CeedBasisGetNumComponents(trial_basis, &trial_num_comp)); + PalaceCeedCall(ceed, CeedBasisGetNumComponents(test_basis, &test_num_comp)); + MFEM_VERIFY(trial_num_comp == test_num_comp && trial_num_comp == 1, + "MixedVectorGradientIntegrator requires test and trial spaces with a single " + "component!"); + switch (10 * space_dim + dim) { - info.ctx.coeff = const_coeff ? const_coeff->constant : 1.0; - - info.build_qf = f_build_hcurl_const_scalar; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_hcurl_const_scalar_loc); + case 22: + info.apply_qf = assemble_q_data ? f_build_hcurl_22 : f_apply_hcurl_22; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hcurl_22_loc : f_apply_hcurl_22_loc); + break; + case 33: + info.apply_qf = assemble_q_data ? f_build_hcurl_33 : f_apply_hcurl_33; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hcurl_33_loc : f_apply_hcurl_33_loc); + break; + case 21: + info.apply_qf = assemble_q_data ? f_build_hcurl_21 : f_apply_hcurl_21; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hcurl_21_loc : f_apply_hcurl_21_loc); + break; + case 32: + info.apply_qf = assemble_q_data ? f_build_hcurl_32 : f_apply_hcurl_32; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hcurl_32_loc : f_apply_hcurl_32_loc); + break; + default: + MFEM_ABORT("Invalid value of (dim, space_dim) = (" + << dim << ", " << space_dim << ") for MixedVectorGradientIntegrator!"); } - else if (Q) - { - ceed::InitCoefficient(*Q, mesh, ir, indices, use_bdr, coeff.emplace_back()); + info.trial_ops = EvalMode::Grad; + info.test_ops = EvalMode::Interp; - info.build_qf = f_build_hcurl_quad_scalar; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_hcurl_quad_scalar_loc); - } - else if (VQ) - { - MFEM_VERIFY(VQ->GetVDim() == info.ctx.space_dim, - "Invalid vector coefficient dimension for " - "MixedVectorGradient/MixedVectorWeakDivergenceIntegrator integrator!"); - ceed::InitCoefficient(*VQ, mesh, ir, indices, use_bdr, coeff.emplace_back()); - - info.build_qf = f_build_hcurl_quad_vector; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_hcurl_quad_vector_loc); - } - else if (MQ) + // Set up the coefficient and assemble. + auto ctx = [&]() { - MFEM_VERIFY(MQ->GetVDim() == info.ctx.space_dim, - "Invalid matrix coefficient dimension for " - "MixedVectorGradient/MixedVectorWeakDivergenceIntegrator integrator!"); - ceed::InitCoefficient(*MQ, mesh, ir, indices, use_bdr, coeff.emplace_back()); - - info.build_qf = f_build_hcurl_quad_matrix; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_hcurl_quad_matrix_loc); - } - - info.apply_qf = f_apply_vecfemass; - info.apply_qf_path = PalaceQFunctionRelativePath(f_apply_vecfemass_loc); - - return info; -} - -} // namespace - -void MixedVectorGradientIntegrator::Assemble( - const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, const mfem::IntegrationRule &ir, - const std::vector &indices, Ceed ceed, CeedOperator *op, CeedOperator *op_t) -{ - constexpr bool use_bdr = false; - std::vector coeff; - const auto info = InitializeIntegratorInfo(trial_fespace, test_fespace, ir, indices, - use_bdr, Q, VQ, MQ, coeff); - ceed::AssembleCeedOperator(info, trial_fespace, test_fespace, ir, indices, use_bdr, coeff, - ceed, op, op_t); -} - -void MixedVectorGradientIntegrator::AssembleBoundary( - const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, const mfem::IntegrationRule &ir, - const std::vector &indices, Ceed ceed, CeedOperator *op, CeedOperator *op_t) -{ - constexpr bool use_bdr = true; - std::vector coeff; - const auto info = InitializeIntegratorInfo(trial_fespace, test_fespace, ir, indices, - use_bdr, Q, VQ, MQ, coeff); - ceed::AssembleCeedOperator(info, trial_fespace, test_fespace, ir, indices, use_bdr, coeff, - ceed, op, op_t); + switch (space_dim) + { + case 2: + return PopulateCoefficientContext<2>(Q); + case 3: + return PopulateCoefficientContext<3>(Q); + } + return std::vector(); + }(); + AssembleCeedOperator(info, (void *)ctx.data(), ctx.size() * sizeof(CeedIntScalar), ceed, + trial_restr, test_restr, trial_basis, test_basis, geom_data, + geom_data_restr, op); } void MixedVectorWeakDivergenceIntegrator::Assemble( - const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, const mfem::IntegrationRule &ir, - const std::vector &indices, Ceed ceed, CeedOperator *op, CeedOperator *op_t) + Ceed ceed, CeedElemRestriction trial_restr, CeedElemRestriction test_restr, + CeedBasis trial_basis, CeedBasis test_basis, CeedVector geom_data, + CeedElemRestriction geom_data_restr, CeedOperator *op) const { - // Negative coefficient comes from definition of integrator as -(Q u, grad v). - constexpr bool use_bdr = false; - std::vector coeff; - auto info = InitializeIntegratorInfo(trial_fespace, test_fespace, ir, indices, use_bdr, Q, - VQ, MQ, coeff); - info.ctx.coeff *= -1.0; - for (auto &c : coeff) + IntegratorInfo info; + info.assemble_q_data = assemble_q_data; + + // Set up QFunctions. + CeedInt dim, space_dim, trial_num_comp, test_num_comp; + PalaceCeedCall(ceed, CeedBasisGetDimension(trial_basis, &dim)); + PalaceCeedCall(ceed, CeedGeometryDataGetSpaceDimension(geom_data_restr, dim, &space_dim)); + PalaceCeedCall(ceed, CeedBasisGetNumComponents(trial_basis, &trial_num_comp)); + PalaceCeedCall(ceed, CeedBasisGetNumComponents(test_basis, &test_num_comp)); + MFEM_VERIFY( + trial_num_comp == test_num_comp && trial_num_comp == 1, + "MixedVectorWeakDivergenceIntegrator requires test and trial spaces with a single " + "component!"); + switch (10 * space_dim + dim) { - c.data *= -1.0; + case 22: + info.apply_qf = assemble_q_data ? f_build_hcurl_22 : f_apply_hcurl_22; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hcurl_22_loc : f_apply_hcurl_22_loc); + break; + case 33: + info.apply_qf = assemble_q_data ? f_build_hcurl_33 : f_apply_hcurl_33; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hcurl_33_loc : f_apply_hcurl_33_loc); + break; + case 21: + info.apply_qf = assemble_q_data ? f_build_hcurl_21 : f_apply_hcurl_21; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hcurl_21_loc : f_apply_hcurl_21_loc); + break; + case 32: + info.apply_qf = assemble_q_data ? f_build_hcurl_32 : f_apply_hcurl_32; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hcurl_32_loc : f_apply_hcurl_32_loc); + break; + default: + MFEM_ABORT("Invalid value of (dim, space_dim) = (" + << dim << ", " << space_dim + << ") for MixedVectorWeakDivergenceIntegrator!"); } - ceed::AssembleCeedOperator(info, trial_fespace, test_fespace, ir, indices, use_bdr, coeff, - ceed, op, op_t); -} + info.trial_ops = EvalMode::Interp; + info.test_ops = EvalMode::Grad; -void MixedVectorWeakDivergenceIntegrator::AssembleBoundary( - const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, const mfem::IntegrationRule &ir, - const std::vector &indices, Ceed ceed, CeedOperator *op, CeedOperator *op_t) -{ - // Negative coefficient comes from definition of integrator as -(Q u, grad v). - constexpr bool use_bdr = true; - std::vector coeff; - auto info = InitializeIntegratorInfo(trial_fespace, test_fespace, ir, indices, use_bdr, Q, - VQ, MQ, coeff); - info.ctx.coeff *= -1.0; - for (auto &c : coeff) + // Set up the coefficient and assemble. + auto ctx = [&]() { - c.data *= -1.0; - } - ceed::AssembleCeedOperator(info, trial_fespace, test_fespace, ir, indices, use_bdr, coeff, - ceed, op, op_t); + switch (space_dim) + { + case 2: + return PopulateCoefficientContext<2>(Q, -1.0); + case 3: + return PopulateCoefficientContext<3>(Q, -1.0); + } + return std::vector(); + }(); + AssembleCeedOperator(info, (void *)ctx.data(), ctx.size() * sizeof(CeedIntScalar), ceed, + trial_restr, test_restr, trial_basis, test_basis, geom_data, + geom_data_restr, op); } } // namespace palace diff --git a/palace/fem/integ/vecfemass.cpp b/palace/fem/integ/vecfemass.cpp index a0d8ef7d5..279314b56 100644 --- a/palace/fem/integ/vecfemass.cpp +++ b/palace/fem/integ/vecfemass.cpp @@ -3,220 +3,199 @@ #include "fem/integrator.hpp" -#include -#include #include "fem/libceed/coefficient.hpp" #include "fem/libceed/integrator.hpp" +#include "fem/qfunctions/hcurl_build_qf.h" #include "fem/qfunctions/hcurl_qf.h" +#include "fem/qfunctions/hcurlhdiv_build_qf.h" #include "fem/qfunctions/hcurlhdiv_qf.h" +#include "fem/qfunctions/hdiv_build_qf.h" #include "fem/qfunctions/hdiv_qf.h" namespace palace { -struct VectorFEMassIntegratorInfo : public ceed::IntegratorInfo -{ - VectorFEMassContext ctx; -}; - -namespace -{ +using namespace ceed; -VectorFEMassIntegratorInfo -InitializeIntegratorInfo(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, - bool use_bdr, mfem::Coefficient *Q, mfem::VectorCoefficient *VQ, - mfem::MatrixCoefficient *MQ, - std::vector &coeff) +void VectorFEMassIntegrator::Assemble(Ceed ceed, CeedElemRestriction trial_restr, + CeedElemRestriction test_restr, CeedBasis trial_basis, + CeedBasis test_basis, CeedVector geom_data, + CeedElemRestriction geom_data_restr, + CeedOperator *op) const { - MFEM_VERIFY(trial_fespace.GetVDim() == 1 && test_fespace.GetVDim() == 1, - "libCEED interface for VectorFEMassIntegrator does not support vdim > 1!"); - - VectorFEMassIntegratorInfo info = {{0}}; - - mfem::ParMesh &mesh = *trial_fespace.GetParMesh(); - info.ctx.dim = mesh.Dimension() - use_bdr; - info.ctx.space_dim = mesh.SpaceDimension(); - - int trial_map_type = trial_fespace.FEColl()->GetMapType(info.ctx.dim); - int test_map_type = test_fespace.FEColl()->GetMapType(info.ctx.dim); - MFEM_VERIFY((trial_map_type == mfem::FiniteElement::H_CURL || - trial_map_type == mfem::FiniteElement::H_DIV) && - (test_map_type == mfem::FiniteElement::H_CURL || - test_map_type == mfem::FiniteElement::H_DIV), - "VectorFEMassIntegrator requires H(div) or H(curl) FE spaces!"); - - info.trial_op = ceed::EvalMode::Interp; - info.test_op = ceed::EvalMode::Interp; - if (trial_map_type != test_map_type) - { - // Quadrature data is nonsymmetric in this case. - info.qdata_size = info.ctx.dim * info.ctx.dim; - info.ctx.sym = false; - } - else + IntegratorInfo info; + info.assemble_q_data = assemble_q_data; + + // Set up QFunctions. + CeedInt dim, space_dim, trial_num_comp, test_num_comp; + PalaceCeedCall(ceed, CeedBasisGetDimension(trial_basis, &dim)); + PalaceCeedCall(ceed, CeedGeometryDataGetSpaceDimension(geom_data_restr, dim, &space_dim)); + PalaceCeedCall(ceed, CeedBasisGetNumComponents(trial_basis, &trial_num_comp)); + PalaceCeedCall(ceed, CeedBasisGetNumComponents(test_basis, &test_num_comp)); + MFEM_VERIFY( + trial_num_comp == test_num_comp && trial_num_comp == 1, + "VectorFEMassIntegrator requires test and trial spaces with a single component!"); + switch (10 * space_dim + dim) { - info.qdata_size = (info.ctx.dim * (info.ctx.dim + 1)) / 2; - info.ctx.sym = true; + case 22: + if (trial_map_type == mfem::FiniteElement::H_CURL && + test_map_type == mfem::FiniteElement::H_CURL) + { + info.apply_qf = assemble_q_data ? f_build_hcurl_22 : f_apply_hcurl_22; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hcurl_22_loc : f_apply_hcurl_22_loc); + } + else if (trial_map_type == mfem::FiniteElement::H_DIV && + test_map_type == mfem::FiniteElement::H_DIV) + { + info.apply_qf = assemble_q_data ? f_build_hdiv_22 : f_apply_hdiv_22; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hdiv_22_loc : f_apply_hdiv_22_loc); + } + else if (trial_map_type == mfem::FiniteElement::H_CURL && + test_map_type == mfem::FiniteElement::H_DIV) + { + info.apply_qf = assemble_q_data ? f_build_hcurlhdiv_22 : f_apply_hcurlhdiv_22; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hcurlhdiv_22_loc : f_apply_hcurlhdiv_22_loc); + } + else if (trial_map_type == mfem::FiniteElement::H_DIV && + test_map_type == mfem::FiniteElement::H_CURL) + { + info.apply_qf = assemble_q_data ? f_build_hdivhcurl_22 : f_apply_hdivhcurl_22; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hdivhcurl_22_loc : f_apply_hdivhcurl_22_loc); + } + else + { + MFEM_ABORT("Invalid trial/test element map type for VectorFEMassIntegrator!"); + } + break; + case 33: + if (trial_map_type == mfem::FiniteElement::H_CURL && + test_map_type == mfem::FiniteElement::H_CURL) + { + info.apply_qf = assemble_q_data ? f_build_hcurl_33 : f_apply_hcurl_33; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hcurl_33_loc : f_apply_hcurl_33_loc); + } + else if (trial_map_type == mfem::FiniteElement::H_DIV && + test_map_type == mfem::FiniteElement::H_DIV) + { + info.apply_qf = assemble_q_data ? f_build_hdiv_33 : f_apply_hdiv_33; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hdiv_33_loc : f_apply_hdiv_33_loc); + } + else if (trial_map_type == mfem::FiniteElement::H_CURL && + test_map_type == mfem::FiniteElement::H_DIV) + { + info.apply_qf = assemble_q_data ? f_build_hcurlhdiv_33 : f_apply_hcurlhdiv_33; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hcurlhdiv_33_loc : f_apply_hcurlhdiv_33_loc); + } + else if (trial_map_type == mfem::FiniteElement::H_DIV && + test_map_type == mfem::FiniteElement::H_CURL) + { + info.apply_qf = assemble_q_data ? f_build_hdivhcurl_33 : f_apply_hdivhcurl_33; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hdivhcurl_33_loc : f_apply_hdivhcurl_33_loc); + } + else + { + MFEM_ABORT("Invalid trial/test element map type for VectorFEMassIntegrator!"); + } + break; + case 21: + if (trial_map_type == mfem::FiniteElement::H_CURL && + test_map_type == mfem::FiniteElement::H_CURL) + { + info.apply_qf = assemble_q_data ? f_build_hcurl_21 : f_apply_hcurl_21; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hcurl_21_loc : f_apply_hcurl_21_loc); + } + else if (trial_map_type == mfem::FiniteElement::H_DIV && + test_map_type == mfem::FiniteElement::H_DIV) + { + info.apply_qf = assemble_q_data ? f_build_hdiv_21 : f_apply_hdiv_21; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hdiv_21_loc : f_apply_hdiv_21_loc); + } + else if (trial_map_type == mfem::FiniteElement::H_CURL && + test_map_type == mfem::FiniteElement::H_DIV) + { + info.apply_qf = assemble_q_data ? f_build_hcurlhdiv_21 : f_apply_hcurlhdiv_21; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hcurlhdiv_21_loc : f_apply_hcurlhdiv_21_loc); + } + else if (trial_map_type == mfem::FiniteElement::H_DIV && + test_map_type == mfem::FiniteElement::H_CURL) + { + info.apply_qf = assemble_q_data ? f_build_hdivhcurl_21 : f_apply_hdivhcurl_21; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hdivhcurl_21_loc : f_apply_hdivhcurl_21_loc); + } + else + { + MFEM_ABORT("Invalid trial/test element map type for VectorFEMassIntegrator!"); + } + break; + case 32: + if (trial_map_type == mfem::FiniteElement::H_CURL && + test_map_type == mfem::FiniteElement::H_CURL) + { + info.apply_qf = assemble_q_data ? f_build_hcurl_32 : f_apply_hcurl_32; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hcurl_32_loc : f_apply_hcurl_32_loc); + } + else if (trial_map_type == mfem::FiniteElement::H_DIV && + test_map_type == mfem::FiniteElement::H_DIV) + { + info.apply_qf = assemble_q_data ? f_build_hdiv_32 : f_apply_hdiv_32; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hdiv_32_loc : f_apply_hdiv_32_loc); + } + else if (trial_map_type == mfem::FiniteElement::H_CURL && + test_map_type == mfem::FiniteElement::H_DIV) + { + info.apply_qf = assemble_q_data ? f_build_hcurlhdiv_32 : f_apply_hcurlhdiv_32; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hcurlhdiv_32_loc : f_apply_hcurlhdiv_32_loc); + } + else if (trial_map_type == mfem::FiniteElement::H_DIV && + test_map_type == mfem::FiniteElement::H_CURL) + { + info.apply_qf = assemble_q_data ? f_build_hdivhcurl_32 : f_apply_hdivhcurl_32; + info.apply_qf_path = PalaceQFunctionRelativePath( + assemble_q_data ? f_build_hdivhcurl_32_loc : f_apply_hdivhcurl_32_loc); + } + else + { + MFEM_ABORT("Invalid trial/test element map type for VectorFEMassIntegrator!"); + } + break; + default: + MFEM_ABORT("Invalid value of (dim, space_dim) = (" + << dim << ", " << space_dim << ") for VectorFEMassIntegrator!"); } + info.trial_ops = EvalMode::Interp; + info.test_ops = EvalMode::Interp; - mfem::ConstantCoefficient *const_coeff = dynamic_cast(Q); - if (const_coeff || !(Q || VQ || MQ)) - { - info.ctx.coeff = const_coeff ? const_coeff->constant : 1.0; - - if (trial_map_type == mfem::FiniteElement::H_CURL && - test_map_type == mfem::FiniteElement::H_CURL) - { - info.build_qf = f_build_hcurl_const_scalar; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_hcurl_const_scalar_loc); - } - else if (trial_map_type == mfem::FiniteElement::H_DIV && - test_map_type == mfem::FiniteElement::H_DIV) - { - info.build_qf = f_build_hdiv_const_scalar; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_hdiv_const_scalar_loc); - } - else if (trial_map_type == mfem::FiniteElement::H_CURL && - test_map_type == mfem::FiniteElement::H_DIV) - { - info.build_qf = f_build_hcurlhdiv_const_scalar; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_hcurlhdiv_const_scalar_loc); - } - else // trial_map_type == mfem::FiniteElement::H_DIV && test_map_type == - // mfem::FiniteElement::H_CURL - { - info.build_qf = f_build_hdivhcurl_const_scalar; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_hdivhcurl_const_scalar_loc); - } - } - else if (Q) - { - ceed::InitCoefficient(*Q, mesh, ir, indices, use_bdr, coeff.emplace_back()); - - if (trial_map_type == mfem::FiniteElement::H_CURL && - test_map_type == mfem::FiniteElement::H_CURL) - { - info.build_qf = f_build_hcurl_quad_scalar; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_hcurl_quad_scalar_loc); - } - else if (trial_map_type == mfem::FiniteElement::H_DIV && - test_map_type == mfem::FiniteElement::H_DIV) - { - info.build_qf = f_build_hdiv_quad_scalar; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_hdiv_quad_scalar_loc); - } - else if (trial_map_type == mfem::FiniteElement::H_CURL && - test_map_type == mfem::FiniteElement::H_DIV) - { - info.build_qf = f_build_hcurlhdiv_quad_scalar; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_hcurlhdiv_quad_scalar_loc); - } - else // trial_map_type == mfem::FiniteElement::H_DIV && test_map_type == - // mfem::FiniteElement::H_CURL - { - info.build_qf = f_build_hdivhcurl_quad_scalar; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_hdivhcurl_quad_scalar_loc); - } - } - else if (VQ) + // Set up the coefficient and assemble. + auto ctx = [&]() { - MFEM_VERIFY(VQ->GetVDim() == info.ctx.space_dim, - "Invalid vector coefficient dimension for VectorFEMassIntegrator!"); - ceed::InitCoefficient(*VQ, mesh, ir, indices, use_bdr, coeff.emplace_back()); - - if (trial_map_type == mfem::FiniteElement::H_CURL && - test_map_type == mfem::FiniteElement::H_CURL) - { - info.build_qf = f_build_hcurl_quad_vector; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_hcurl_quad_vector_loc); - } - else if (trial_map_type == mfem::FiniteElement::H_DIV && - test_map_type == mfem::FiniteElement::H_DIV) - { - info.build_qf = f_build_hdiv_quad_vector; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_hdiv_quad_vector_loc); - } - else if (trial_map_type == mfem::FiniteElement::H_CURL && - test_map_type == mfem::FiniteElement::H_DIV) - { - info.build_qf = f_build_hcurlhdiv_quad_vector; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_hcurlhdiv_quad_vector_loc); - } - else // trial_map_type == mfem::FiniteElement::H_DIV && test_map_type == - // mfem::FiniteElement::H_CURL - { - info.build_qf = f_build_hdivhcurl_quad_vector; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_hdivhcurl_quad_vector_loc); - } - } - else if (MQ) - { - MFEM_VERIFY(MQ->GetVDim() == info.ctx.space_dim, - "Invalid matrix coefficient dimension for VectorFEMassIntegrator!"); - ceed::InitCoefficient(*MQ, mesh, ir, indices, use_bdr, coeff.emplace_back()); - - if (trial_map_type == mfem::FiniteElement::H_CURL && - test_map_type == mfem::FiniteElement::H_CURL) - { - info.build_qf = f_build_hcurl_quad_matrix; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_hcurl_quad_matrix_loc); - } - else if (trial_map_type == mfem::FiniteElement::H_DIV && - test_map_type == mfem::FiniteElement::H_DIV) - { - info.build_qf = f_build_hdiv_quad_matrix; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_hdiv_quad_matrix_loc); - } - else if (trial_map_type == mfem::FiniteElement::H_CURL && - test_map_type == mfem::FiniteElement::H_DIV) - { - info.build_qf = f_build_hcurlhdiv_quad_matrix; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_hcurlhdiv_quad_matrix_loc); - } - else // trial_map_type == mfem::FiniteElement::H_DIV && test_map_type == - // mfem::FiniteElement::H_CURL - { - info.build_qf = f_build_hdivhcurl_quad_matrix; - info.build_qf_path = PalaceQFunctionRelativePath(f_build_hdivhcurl_quad_matrix_loc); - } - } - - info.apply_qf = f_apply_vecfemass; - info.apply_qf_path = PalaceQFunctionRelativePath(f_apply_vecfemass_loc); - - return info; -} - -} // namespace - -void VectorFEMassIntegrator::Assemble(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, - const std::vector &indices, Ceed ceed, - CeedOperator *op, CeedOperator *op_t) -{ - constexpr bool use_bdr = false; - std::vector coeff; - const auto info = InitializeIntegratorInfo(trial_fespace, test_fespace, ir, indices, - use_bdr, Q, VQ, MQ, coeff); - ceed::AssembleCeedOperator(info, trial_fespace, test_fespace, ir, indices, use_bdr, coeff, - ceed, op, op_t); -} - -void VectorFEMassIntegrator::AssembleBoundary( - const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, const mfem::IntegrationRule &ir, - const std::vector &indices, Ceed ceed, CeedOperator *op, CeedOperator *op_t) -{ - constexpr bool use_bdr = true; - std::vector coeff; - const auto info = InitializeIntegratorInfo(trial_fespace, test_fespace, ir, indices, - use_bdr, Q, VQ, MQ, coeff); - ceed::AssembleCeedOperator(info, trial_fespace, test_fespace, ir, indices, use_bdr, coeff, - ceed, op, op_t); + switch (space_dim) + { + case 2: + return PopulateCoefficientContext<2>(Q); + case 3: + return PopulateCoefficientContext<3>(Q); + } + return std::vector(); + }(); + AssembleCeedOperator(info, (void *)ctx.data(), ctx.size() * sizeof(CeedIntScalar), ceed, + trial_restr, test_restr, trial_basis, test_basis, geom_data, + geom_data_restr, op); } } // namespace palace diff --git a/palace/fem/integrator.cpp b/palace/fem/integrator.cpp index ab7a95d67..6799329c6 100644 --- a/palace/fem/integrator.cpp +++ b/palace/fem/integrator.cpp @@ -11,55 +11,38 @@ namespace palace namespace fem { -int DefaultIntegrationOrder::Get(const mfem::FiniteElement &trial_fe, - const mfem::FiniteElement &test_fe, - const mfem::ElementTransformation &T) +int DefaultIntegrationOrder::Get(const mfem::IsoparametricTransformation &T) { - return trial_fe.GetOrder() + test_fe.GetOrder() + (q_order_jac ? T.OrderW() : 0) + - (trial_fe.Space() == mfem::FunctionSpace::Pk ? q_order_extra_pk - : q_order_extra_qk); + return 2 * p_trial + (q_order_jac ? T.OrderW() : 0) + + (T.GetFE()->Space() == mfem::FunctionSpace::Pk ? q_order_extra_pk + : q_order_extra_qk); } -int DefaultIntegrationOrder::Get(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const std::vector &indices, bool use_bdr) +int DefaultIntegrationOrder::Get(const mfem::ElementTransformation &T) { - // Every process is guaranteed to have at least one element, and assumes no variable - // order spaces are used. - MFEM_ASSERT( - !indices.empty() && !trial_fespace.IsVariableOrder() && - !test_fespace.IsVariableOrder() && - ((use_bdr && trial_fespace.GetBE(indices[0]) && test_fespace.GetBE(indices[0])) || - (!use_bdr && trial_fespace.GetFE(indices[0]) && test_fespace.GetFE(indices[0]))), - "Invalid empty mesh partition or variable order space!"); - mfem::ParMesh &mesh = *trial_fespace.GetParMesh(); + const auto *T_iso = dynamic_cast(&T); + MFEM_VERIFY( + T_iso, + "Unexpected non-isoparametric element transformation to calculate quadrature order!"); + return Get(*T_iso); +} + +int DefaultIntegrationOrder::Get(const mfem::Mesh &mesh, mfem::Geometry::Type geom) +{ + MFEM_VERIFY(mesh.GetNodes(), "The mesh has no nodal FE space!"); mfem::IsoparametricTransformation T; - if (use_bdr) - { - const mfem::FiniteElement &trial_fe = *trial_fespace.GetBE(indices[0]); - const mfem::FiniteElement &test_fe = *test_fespace.GetBE(indices[0]); - mesh.GetBdrElementTransformation(indices[0], &T); - return Get(trial_fe, test_fe, T); - } - else - { - const mfem::FiniteElement &trial_fe = *trial_fespace.GetFE(indices[0]); - const mfem::FiniteElement &test_fe = *test_fespace.GetFE(indices[0]); - mesh.GetElementTransformation(indices[0], &T); - return Get(trial_fe, test_fe, T); - } + T.SetFE(mesh.GetNodalFESpace()->FEColl()->FiniteElementForGeometry(geom)); + return Get(T); } } // namespace fem -void DiscreteInterpolator::Assemble(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, - const std::vector &indices, Ceed ceed, +void DiscreteInterpolator::Assemble(Ceed ceed, CeedElemRestriction trial_restr, + CeedElemRestriction test_restr, CeedBasis interp_basis, CeedOperator *op, CeedOperator *op_t) { // Interpolators do not use an integration rule to map between the test and trial spaces. - ceed::AssembleCeedInterpolator(trial_fespace, test_fespace, indices, ceed, op, op_t); + ceed::AssembleCeedInterpolator(ceed, trial_restr, test_restr, interp_basis, op, op_t); } void VectorFEBoundaryLFIntegrator::AssembleRHSElementVect(const mfem::FiniteElement &fe, @@ -68,7 +51,7 @@ void VectorFEBoundaryLFIntegrator::AssembleRHSElementVect(const mfem::FiniteElem { const int dof = fe.GetDof(); const int dim = fe.GetDim(); - const int q_order = fem::DefaultIntegrationOrder::Get(fe, fe, T); + const int q_order = fem::DefaultIntegrationOrder::Get(T); const mfem::IntegrationRule &ir = mfem::IntRules.Get(fe.GetGeomType(), q_order); f_hat.SetSize(dim); vshape.SetSize(dof, dim); @@ -93,7 +76,7 @@ void BoundaryLFIntegrator::AssembleRHSElementVect(const mfem::FiniteElement &fe, mfem::Vector &elvect) { const int dof = fe.GetDof(); - const int q_order = fem::DefaultIntegrationOrder::Get(fe, fe, T); + const int q_order = fem::DefaultIntegrationOrder::Get(T); const mfem::IntegrationRule &ir = mfem::IntRules.Get(fe.GetGeomType(), q_order); shape.SetSize(dof); elvect.SetSize(dof); diff --git a/palace/fem/integrator.hpp b/palace/fem/integrator.hpp index 63f2c5461..693e3deba 100644 --- a/palace/fem/integrator.hpp +++ b/palace/fem/integrator.hpp @@ -4,16 +4,14 @@ #ifndef PALACE_FEM_INTEGRATOR_HPP #define PALACE_FEM_INTEGRATOR_HPP -#include #include - -// Forward declarations of libCEED objects. -typedef struct Ceed_private *Ceed; -typedef struct CeedOperator_private *CeedOperator; +#include "fem/libceed/ceed.hpp" namespace palace { +class MaterialPropertyCoefficient; + // // Classes which implement or extend bilinear and linear form integrators. // @@ -22,19 +20,16 @@ namespace fem { // Helper functions for creating an integration rule to exactly integrate polynomials of -// order p_test + p_trial + order(|J|) + q_extra. +// order 2 * p_trial + order(|J|) + q_extra. struct DefaultIntegrationOrder { + inline static int p_trial = 1; inline static bool q_order_jac = true; inline static int q_order_extra_pk = 0; inline static int q_order_extra_qk = 0; - - static int Get(const mfem::FiniteElement &trial_fe, const mfem::FiniteElement &test_fe, - const mfem::ElementTransformation &T); - - static int Get(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const std::vector &indices, bool use_bdr); + static int Get(const mfem::IsoparametricTransformation &T); + static int Get(const mfem::ElementTransformation &T); + static int Get(const mfem::Mesh &mesh, mfem::Geometry::Type geom); }; } // namespace fem @@ -42,431 +37,258 @@ struct DefaultIntegrationOrder // Base class for libCEED-based bilinear form integrators. class BilinearFormIntegrator { +protected: + const MaterialPropertyCoefficient *Q; + bool assemble_q_data; + public: + BilinearFormIntegrator(const MaterialPropertyCoefficient *Q = nullptr) + : Q(Q), assemble_q_data(false) + { + } + BilinearFormIntegrator(const MaterialPropertyCoefficient &Q) + : Q(&Q), assemble_q_data(false) + { + } virtual ~BilinearFormIntegrator() = default; - virtual void Assemble(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, - Ceed ceed, CeedOperator *op, CeedOperator *op_t) = 0; + virtual void Assemble(Ceed ceed, CeedElemRestriction trial_restr, + CeedElemRestriction test_restr, CeedBasis trial_basis, + CeedBasis test_basis, CeedVector geom_data, + CeedElemRestriction geom_data_restr, CeedOperator *op) const = 0; - virtual void AssembleBoundary(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, - const std::vector &indices, Ceed ceed, - CeedOperator *op, CeedOperator *op_t) = 0; + virtual void SetMapTypes(int trial_type, int test_type) {} + + void AssembleQuadratureData() { assemble_q_data = true; } }; // Integrator for a(u, v) = (Q u, v) for H1 elements (also for vector (H1)ᵈ spaces). class MassIntegrator : public BilinearFormIntegrator { -protected: - mfem::Coefficient *Q; - mfem::VectorCoefficient *VQ; - mfem::MatrixCoefficient *MQ; - public: - MassIntegrator() : Q(nullptr), VQ(nullptr), MQ(nullptr) {} - MassIntegrator(mfem::Coefficient &Q) : Q(&Q), VQ(nullptr), MQ(nullptr) {} - MassIntegrator(mfem::VectorCoefficient &VQ) : Q(nullptr), VQ(&VQ), MQ(nullptr) {} - MassIntegrator(mfem::MatrixCoefficient &MQ) : Q(nullptr), VQ(nullptr), MQ(&MQ) {} - - void Assemble(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, Ceed ceed, - CeedOperator *op, CeedOperator *op_t) override; - - void AssembleBoundary(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, - Ceed ceed, CeedOperator *op, CeedOperator *op_t) override; + MassIntegrator() = default; + MassIntegrator(const MaterialPropertyCoefficient &Q) : BilinearFormIntegrator(Q) {} + + void Assemble(Ceed ceed, CeedElemRestriction trial_restr, CeedElemRestriction test_restr, + CeedBasis trial_basis, CeedBasis test_basis, CeedVector geom_data, + CeedElemRestriction geom_data_restr, CeedOperator *op) const override; }; // Integrator for a(u, v) = (Q u, v) for vector finite elements. class VectorFEMassIntegrator : public BilinearFormIntegrator { protected: - mfem::Coefficient *Q; - mfem::VectorCoefficient *VQ; - mfem::MatrixCoefficient *MQ; + int trial_map_type = mfem::FiniteElement::UNKNOWN_MAP_TYPE; + int test_map_type = mfem::FiniteElement::UNKNOWN_MAP_TYPE; public: - VectorFEMassIntegrator() : Q(nullptr), VQ(nullptr), MQ(nullptr) {} - VectorFEMassIntegrator(mfem::Coefficient &Q) : Q(&Q), VQ(nullptr), MQ(nullptr) {} - VectorFEMassIntegrator(mfem::VectorCoefficient &VQ) : Q(nullptr), VQ(&VQ), MQ(nullptr) {} - VectorFEMassIntegrator(mfem::MatrixCoefficient &MQ) : Q(nullptr), VQ(nullptr), MQ(&MQ) {} - - void Assemble(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, Ceed ceed, - CeedOperator *op, CeedOperator *op_t) override; - - void AssembleBoundary(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, - Ceed ceed, CeedOperator *op, CeedOperator *op_t) override; + VectorFEMassIntegrator() = default; + VectorFEMassIntegrator(const MaterialPropertyCoefficient &Q) : BilinearFormIntegrator(Q) + { + } + + void Assemble(Ceed ceed, CeedElemRestriction trial_restr, CeedElemRestriction test_restr, + CeedBasis trial_basis, CeedBasis test_basis, CeedVector geom_data, + CeedElemRestriction geom_data_restr, CeedOperator *op) const override; + + void SetMapTypes(int trial_type, int test_type) override + { + trial_map_type = trial_type; + test_map_type = test_type; + } }; -// Integrator for a(u, v) = (Q curl u, curl v) for Nedelec elements. -class CurlCurlIntegrator : public BilinearFormIntegrator +// Integrator for a(u, v) = (Q grad u, grad v) for H1 elements. +class DiffusionIntegrator : public BilinearFormIntegrator { -protected: - mfem::Coefficient *Q; - mfem::VectorCoefficient *VQ; - mfem::MatrixCoefficient *MQ; - public: - CurlCurlIntegrator() : Q(nullptr), VQ(nullptr), MQ(nullptr) {} - CurlCurlIntegrator(mfem::Coefficient &Q) : Q(&Q), VQ(nullptr), MQ(nullptr) {} - CurlCurlIntegrator(mfem::VectorCoefficient &VQ) : Q(nullptr), VQ(&VQ), MQ(nullptr) {} - CurlCurlIntegrator(mfem::MatrixCoefficient &MQ) : Q(nullptr), VQ(nullptr), MQ(&MQ) {} - - void Assemble(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, Ceed ceed, - CeedOperator *op, CeedOperator *op_t) override; - - void AssembleBoundary(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, - Ceed ceed, CeedOperator *op, CeedOperator *op_t) override; + DiffusionIntegrator() = default; + DiffusionIntegrator(const MaterialPropertyCoefficient &Q) : BilinearFormIntegrator(Q) {} + + void Assemble(Ceed ceed, CeedElemRestriction trial_restr, CeedElemRestriction test_restr, + CeedBasis trial_basis, CeedBasis test_basis, CeedVector geom_data, + CeedElemRestriction geom_data_restr, CeedOperator *op) const override; }; -// Integrator for a(u, v) = (Qc curl u, curl v) + (Qm u, v) for Nedelec elements. -class CurlCurlMassIntegrator : public BilinearFormIntegrator +// Integrator for a(u, v) = (Q curl u, curl v) for Nedelec elements. +class CurlCurlIntegrator : public BilinearFormIntegrator { -protected: - mfem::Coefficient *Qc, *Qm; - mfem::VectorCoefficient *VQc, *VQm; - mfem::MatrixCoefficient *MQc, *MQm; - public: - CurlCurlMassIntegrator(mfem::Coefficient &Qc, mfem::Coefficient &Qm) - : Qc(&Qc), Qm(&Qm), VQc(nullptr), VQm(nullptr), MQc(nullptr), MQm(nullptr) - { - } - CurlCurlMassIntegrator(mfem::Coefficient &Qc, mfem::VectorCoefficient &VQm) - : Qc(&Qc), Qm(nullptr), VQc(nullptr), VQm(&VQm), MQc(nullptr), MQm(nullptr) - { - } - CurlCurlMassIntegrator(mfem::Coefficient &Qc, mfem::MatrixCoefficient &MQm) - : Qc(&Qc), Qm(nullptr), VQc(nullptr), VQm(nullptr), MQc(nullptr), MQm(&MQm) - { - } - CurlCurlMassIntegrator(mfem::VectorCoefficient &VQc, mfem::Coefficient &Qm) - : Qc(nullptr), Qm(&Qm), VQc(&VQc), VQm(nullptr), MQc(nullptr), MQm(nullptr) - { - } - CurlCurlMassIntegrator(mfem::VectorCoefficient &VQc, mfem::VectorCoefficient &VQm) - : Qc(nullptr), Qm(nullptr), VQc(&VQc), VQm(&VQm), MQc(nullptr), MQm(nullptr) - { - } - CurlCurlMassIntegrator(mfem::VectorCoefficient &VQc, mfem::MatrixCoefficient &MQm) - : Qc(nullptr), Qm(nullptr), VQc(&VQc), VQm(nullptr), MQc(nullptr), MQm(&MQm) - { - } - CurlCurlMassIntegrator(mfem::MatrixCoefficient &MQc, mfem::Coefficient &Qm) - : Qc(nullptr), Qm(&Qm), VQc(nullptr), VQm(nullptr), MQc(&MQc), MQm(nullptr) - { - } - CurlCurlMassIntegrator(mfem::MatrixCoefficient &MQc, mfem::VectorCoefficient &VQm) - : Qc(nullptr), Qm(nullptr), VQc(nullptr), VQm(&VQm), MQc(&MQc), MQm(nullptr) - { - } - CurlCurlMassIntegrator(mfem::MatrixCoefficient &MQc, mfem::MatrixCoefficient &MQm) - : Qc(nullptr), Qm(nullptr), VQc(nullptr), VQm(nullptr), MQc(&MQc), MQm(&MQm) - { - } + CurlCurlIntegrator() = default; + CurlCurlIntegrator(const MaterialPropertyCoefficient &Q) : BilinearFormIntegrator(Q) {} - void Assemble(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, Ceed ceed, - CeedOperator *op, CeedOperator *op_t) override; - - void AssembleBoundary(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, - Ceed ceed, CeedOperator *op, CeedOperator *op_t) override; + void Assemble(Ceed ceed, CeedElemRestriction trial_restr, CeedElemRestriction test_restr, + CeedBasis trial_basis, CeedBasis test_basis, CeedVector geom_data, + CeedElemRestriction geom_data_restr, CeedOperator *op) const override; }; -// Integrator for a(u, v) = (Q grad u, grad v) for H1 elements. -class DiffusionIntegrator : public BilinearFormIntegrator +// Integrator for a(u, v) = (Q div u, div v) for Raviart-Thomas elements. +class DivDivIntegrator : public BilinearFormIntegrator { -protected: - mfem::Coefficient *Q; - mfem::VectorCoefficient *VQ; - mfem::MatrixCoefficient *MQ; - public: - DiffusionIntegrator() : Q(nullptr), VQ(nullptr), MQ(nullptr) {} - DiffusionIntegrator(mfem::Coefficient &Q) : Q(&Q), VQ(nullptr), MQ(nullptr) {} - DiffusionIntegrator(mfem::VectorCoefficient &VQ) : Q(nullptr), VQ(&VQ), MQ(nullptr) {} - DiffusionIntegrator(mfem::MatrixCoefficient &MQ) : Q(nullptr), VQ(nullptr), MQ(&MQ) {} - - void Assemble(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, Ceed ceed, - CeedOperator *op, CeedOperator *op_t) override; - - void AssembleBoundary(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, - Ceed ceed, CeedOperator *op, CeedOperator *op_t) override; + DivDivIntegrator() = default; + DivDivIntegrator(const MaterialPropertyCoefficient &Q) : BilinearFormIntegrator(Q) {} + + void Assemble(Ceed ceed, CeedElemRestriction trial_restr, CeedElemRestriction test_restr, + CeedBasis trial_basis, CeedBasis test_basis, CeedVector geom_data, + CeedElemRestriction geom_data_restr, CeedOperator *op) const override; }; // Integrator for a(u, v) = (Qd grad u, grad v) + (Qm u, v) for H1 elements. class DiffusionMassIntegrator : public BilinearFormIntegrator { protected: - mfem::Coefficient *Qd, *Qm; - mfem::VectorCoefficient *VQd; - mfem::MatrixCoefficient *MQd; + const MaterialPropertyCoefficient *Q_mass; public: - DiffusionMassIntegrator(mfem::Coefficient &Qd, mfem::Coefficient &Qm) - : Qd(&Qd), Qm(&Qm), VQd(nullptr), MQd(nullptr) - { - } - DiffusionMassIntegrator(mfem::VectorCoefficient &VQd, mfem::Coefficient &Qm) - : Qd(nullptr), Qm(&Qm), VQd(&VQd), MQd(nullptr) + DiffusionMassIntegrator() = default; + DiffusionMassIntegrator(const MaterialPropertyCoefficient &Q, + const MaterialPropertyCoefficient &Q_mass) + : BilinearFormIntegrator(Q), Q_mass(&Q_mass) { } - DiffusionMassIntegrator(mfem::MatrixCoefficient &MQd, mfem::Coefficient &Qm) - : Qd(nullptr), Qm(&Qm), VQd(nullptr), MQd(&MQd) - { - } - - void Assemble(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, Ceed ceed, - CeedOperator *op, CeedOperator *op_t) override; - void AssembleBoundary(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, - Ceed ceed, CeedOperator *op, CeedOperator *op_t) override; + void Assemble(Ceed ceed, CeedElemRestriction trial_restr, CeedElemRestriction test_restr, + CeedBasis trial_basis, CeedBasis test_basis, CeedVector geom_data, + CeedElemRestriction geom_data_restr, CeedOperator *op) const override; }; -// Integrator for a(u, v) = (Q div u, div v) for Raviart-Thomas elements. -class DivDivIntegrator : public BilinearFormIntegrator +// Integrator for a(u, v) = (Qc curl u, curl v) + (Qm u, v) for Nedelec elements. +class CurlCurlMassIntegrator : public BilinearFormIntegrator { protected: - mfem::Coefficient *Q; + const MaterialPropertyCoefficient *Q_mass; public: - DivDivIntegrator() : Q(nullptr) {} - DivDivIntegrator(mfem::Coefficient &Q) : Q(&Q) {} - - void Assemble(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, Ceed ceed, - CeedOperator *op, CeedOperator *op_t) override; - - void AssembleBoundary(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, - Ceed ceed, CeedOperator *op, CeedOperator *op_t) override; + CurlCurlMassIntegrator() = default; + CurlCurlMassIntegrator(const MaterialPropertyCoefficient &Q, + const MaterialPropertyCoefficient &Q_mass) + : BilinearFormIntegrator(Q), Q_mass(&Q_mass) + { + } + + void Assemble(Ceed ceed, CeedElemRestriction trial_restr, CeedElemRestriction test_restr, + CeedBasis trial_basis, CeedBasis test_basis, CeedVector geom_data, + CeedElemRestriction geom_data_restr, CeedOperator *op) const override; }; // Integrator for a(u, v) = (Qd div u, div v) + (Qm u, v) for Raviart-Thomas elements. class DivDivMassIntegrator : public BilinearFormIntegrator { protected: - mfem::Coefficient *Qd, *Qm; - mfem::VectorCoefficient *VQm; - mfem::MatrixCoefficient *MQm; + const MaterialPropertyCoefficient *Q_mass; public: - DivDivMassIntegrator(mfem::Coefficient &Qd, mfem::Coefficient &Qm) - : Qd(&Qd), Qm(&Qm), VQm(nullptr), MQm(nullptr) - { - } - DivDivMassIntegrator(mfem::Coefficient &Qd, mfem::VectorCoefficient &VQm) - : Qd(&Qd), Qm(nullptr), VQm(&VQm), MQm(nullptr) + DivDivMassIntegrator() = default; + DivDivMassIntegrator(const MaterialPropertyCoefficient &Q, + const MaterialPropertyCoefficient &Q_mass) + : BilinearFormIntegrator(Q), Q_mass(&Q_mass) { } - DivDivMassIntegrator(mfem::Coefficient &Qd, mfem::MatrixCoefficient &MQm) - : Qd(&Qd), Qm(nullptr), VQm(nullptr), MQm(&MQm) - { - } - - void Assemble(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, Ceed ceed, - CeedOperator *op, CeedOperator *op_t) override; - void AssembleBoundary(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, - Ceed ceed, CeedOperator *op, CeedOperator *op_t) override; + void Assemble(Ceed ceed, CeedElemRestriction trial_restr, CeedElemRestriction test_restr, + CeedBasis trial_basis, CeedBasis test_basis, CeedVector geom_data, + CeedElemRestriction geom_data_restr, CeedOperator *op) const override; }; // Integrator for a(u, v) = (Q grad u, v) for u in H1 and v in H(curl). class MixedVectorGradientIntegrator : public BilinearFormIntegrator { -protected: - mfem::Coefficient *Q; - mfem::VectorCoefficient *VQ; - mfem::MatrixCoefficient *MQ; - public: - MixedVectorGradientIntegrator() : Q(nullptr), VQ(nullptr), MQ(nullptr) {} - MixedVectorGradientIntegrator(mfem::Coefficient &Q) : Q(&Q), VQ(nullptr), MQ(nullptr) {} - MixedVectorGradientIntegrator(mfem::VectorCoefficient &VQ) - : Q(nullptr), VQ(&VQ), MQ(nullptr) - { - } - MixedVectorGradientIntegrator(mfem::MatrixCoefficient &MQ) - : Q(nullptr), VQ(nullptr), MQ(&MQ) + MixedVectorGradientIntegrator() = default; + MixedVectorGradientIntegrator(const MaterialPropertyCoefficient &Q) + : BilinearFormIntegrator(Q) { } - void Assemble(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, Ceed ceed, - CeedOperator *op, CeedOperator *op_t) override; - - void AssembleBoundary(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, - Ceed ceed, CeedOperator *op, CeedOperator *op_t) override; + void Assemble(Ceed ceed, CeedElemRestriction trial_restr, CeedElemRestriction test_restr, + CeedBasis trial_basis, CeedBasis test_basis, CeedVector geom_data, + CeedElemRestriction geom_data_restr, CeedOperator *op) const override; }; // Integrator for a(u, v) = -(Q u, grad v) for u in H(curl) and v in H1. class MixedVectorWeakDivergenceIntegrator : public BilinearFormIntegrator { -protected: - mfem::Coefficient *Q; - mfem::VectorCoefficient *VQ; - mfem::MatrixCoefficient *MQ; - public: - MixedVectorWeakDivergenceIntegrator() : Q(nullptr), VQ(nullptr), MQ(nullptr) {} - MixedVectorWeakDivergenceIntegrator(mfem::Coefficient &Q) - : Q(&Q), VQ(nullptr), MQ(nullptr) + MixedVectorWeakDivergenceIntegrator() = default; + MixedVectorWeakDivergenceIntegrator(const MaterialPropertyCoefficient &Q) + : BilinearFormIntegrator(Q) { } - MixedVectorWeakDivergenceIntegrator(mfem::VectorCoefficient &VQ) - : Q(nullptr), VQ(&VQ), MQ(nullptr) - { - } - MixedVectorWeakDivergenceIntegrator(mfem::MatrixCoefficient &MQ) - : Q(nullptr), VQ(nullptr), MQ(&MQ) - { - } - - void Assemble(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, Ceed ceed, - CeedOperator *op, CeedOperator *op_t) override; - void AssembleBoundary(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, - Ceed ceed, CeedOperator *op, CeedOperator *op_t) override; + void Assemble(Ceed ceed, CeedElemRestriction trial_restr, CeedElemRestriction test_restr, + CeedBasis trial_basis, CeedBasis test_basis, CeedVector geom_data, + CeedElemRestriction geom_data_restr, CeedOperator *op) const override; }; // Integrator for a(u, v) = (Q curl u, v) for u in H(curl) and v in H(div). class MixedVectorCurlIntegrator : public BilinearFormIntegrator { protected: - mfem::Coefficient *Q; - mfem::VectorCoefficient *VQ; - mfem::MatrixCoefficient *MQ; + int trial_map_type = mfem::FiniteElement::UNKNOWN_MAP_TYPE; + int test_map_type = mfem::FiniteElement::UNKNOWN_MAP_TYPE; public: - MixedVectorCurlIntegrator() : Q(nullptr), VQ(nullptr), MQ(nullptr) {} - MixedVectorCurlIntegrator(mfem::Coefficient &Q) : Q(&Q), VQ(nullptr), MQ(nullptr) {} - MixedVectorCurlIntegrator(mfem::VectorCoefficient &VQ) : Q(nullptr), VQ(&VQ), MQ(nullptr) - { - } - MixedVectorCurlIntegrator(mfem::MatrixCoefficient &MQ) : Q(nullptr), VQ(nullptr), MQ(&MQ) + MixedVectorCurlIntegrator() = default; + MixedVectorCurlIntegrator(const MaterialPropertyCoefficient &Q) + : BilinearFormIntegrator(Q) { } - void Assemble(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, Ceed ceed, - CeedOperator *op, CeedOperator *op_t) override; + void Assemble(Ceed ceed, CeedElemRestriction trial_restr, CeedElemRestriction test_restr, + CeedBasis trial_basis, CeedBasis test_basis, CeedVector geom_data, + CeedElemRestriction geom_data_restr, CeedOperator *op) const override; - void AssembleBoundary(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, - Ceed ceed, CeedOperator *op, CeedOperator *op_t) override; + void SetMapTypes(int trial_type, int test_type) override + { + trial_map_type = trial_type; + test_map_type = test_type; + } }; // Integrator for a(u, v) = (Q u, curl v) for u in H(div) and v in H(curl). class MixedVectorWeakCurlIntegrator : public BilinearFormIntegrator { protected: - mfem::Coefficient *Q; - mfem::VectorCoefficient *VQ; - mfem::MatrixCoefficient *MQ; + int trial_map_type = mfem::FiniteElement::UNKNOWN_MAP_TYPE; + int test_map_type = mfem::FiniteElement::UNKNOWN_MAP_TYPE; public: - MixedVectorWeakCurlIntegrator() : Q(nullptr), VQ(nullptr), MQ(nullptr) {} - MixedVectorWeakCurlIntegrator(mfem::Coefficient &Q) : Q(&Q), VQ(nullptr), MQ(nullptr) {} - MixedVectorWeakCurlIntegrator(mfem::VectorCoefficient &VQ) - : Q(nullptr), VQ(&VQ), MQ(nullptr) - { - } - MixedVectorWeakCurlIntegrator(mfem::MatrixCoefficient &MQ) - : Q(nullptr), VQ(nullptr), MQ(&MQ) + MixedVectorWeakCurlIntegrator() = default; + MixedVectorWeakCurlIntegrator(const MaterialPropertyCoefficient &Q) + : BilinearFormIntegrator(Q) { } - void Assemble(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, Ceed ceed, - CeedOperator *op, CeedOperator *op_t) override; + void Assemble(Ceed ceed, CeedElemRestriction trial_restr, CeedElemRestriction test_restr, + CeedBasis trial_basis, CeedBasis test_basis, CeedVector geom_data, + CeedElemRestriction geom_data_restr, CeedOperator *op) const override; - void AssembleBoundary(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, - Ceed ceed, CeedOperator *op, CeedOperator *op_t) override; + void SetMapTypes(int trial_type, int test_type) override + { + trial_map_type = trial_type; + test_map_type = test_type; + } }; // Integrator for a(u, v) = (Q grad u, v) for u in H1 and v in (H1)ᵈ. class GradientIntegrator : public BilinearFormIntegrator { -protected: - mfem::Coefficient *Q; - mfem::VectorCoefficient *VQ; - mfem::MatrixCoefficient *MQ; - public: - GradientIntegrator() : Q(nullptr), VQ(nullptr), MQ(nullptr) {} - GradientIntegrator(mfem::Coefficient &Q) : Q(&Q), VQ(nullptr), MQ(nullptr) {} - GradientIntegrator(mfem::VectorCoefficient &VQ) : Q(nullptr), VQ(&VQ), MQ(nullptr) {} - GradientIntegrator(mfem::MatrixCoefficient &MQ) : Q(nullptr), VQ(nullptr), MQ(&MQ) {} - - void Assemble(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, Ceed ceed, - CeedOperator *op, CeedOperator *op_t) override; - - void AssembleBoundary(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, - Ceed ceed, CeedOperator *op, CeedOperator *op_t) override; + GradientIntegrator() = default; + GradientIntegrator(const MaterialPropertyCoefficient &Q) : BilinearFormIntegrator(Q) {} + + void Assemble(Ceed ceed, CeedElemRestriction trial_restr, CeedElemRestriction test_restr, + CeedBasis trial_basis, CeedBasis test_basis, CeedVector geom_data, + CeedElemRestriction geom_data_restr, CeedOperator *op) const override; }; // Base class for all discrete interpolators. -class DiscreteInterpolator : public BilinearFormIntegrator +class DiscreteInterpolator { public: - void Assemble(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, Ceed ceed, - CeedOperator *op, CeedOperator *op_t) override; - - void AssembleBoundary(const mfem::ParFiniteElementSpace &trial_fespace, - const mfem::ParFiniteElementSpace &test_fespace, - const mfem::IntegrationRule &ir, const std::vector &indices, - Ceed ceed, CeedOperator *op, CeedOperator *op_t) override - { - MFEM_ABORT("Boundary assembly is not implemented for DiscreteInterpolator objects!"); - } + void Assemble(Ceed ceed, CeedElemRestriction trial_restr, CeedElemRestriction test_restr, + CeedBasis interp_basis, CeedOperator *op, CeedOperator *op_t); }; // Interpolator for the identity map, where the domain space is a subspace of the range diff --git a/palace/fem/libceed/coefficient.cpp b/palace/fem/libceed/coefficient.cpp index b0374de3c..65bcef886 100644 --- a/palace/fem/libceed/coefficient.cpp +++ b/palace/fem/libceed/coefficient.cpp @@ -147,10 +147,13 @@ PopulateCoefficientContext(const MaterialPropertyCoefficient *Q, const MaterialPropertyCoefficient *Q_mass, double a, double a_mass) { + // Mass coefficient comes first, then the other one for the QFunction. auto ctx = PopulateCoefficientContext(Q, a); auto ctx_mass = PopulateCoefficientContext(Q_mass, a_mass); - ctx.insert(ctx.end(), ctx_mass.begin(), ctx_mass.end()); - return ctx; + ctx_mass.insert(ctx_mass.end(), ctx.begin(), ctx.end()); + return ctx_mass; + // ctx.insert(ctx.end(), ctx_mass.begin(), ctx_mass.end()); + // return ctx; } template std::vector diff --git a/palace/linalg/divfree.cpp b/palace/linalg/divfree.cpp index 6a1a6af91..f4ff13f0d 100644 --- a/palace/linalg/divfree.cpp +++ b/palace/linalg/divfree.cpp @@ -32,7 +32,7 @@ DivFreeSolver::DivFreeSolver(const MaterialOperator &mat_op, FiniteElementSpace // Force coarse level operator to be fully assembled always. const auto &h1_fespace_l = h1_fespaces.GetFESpaceAtLevel(l); BilinearForm m(h1_fespace_l); - m.AddDomainIntegrator((mfem::MatrixCoefficient &)epsilon_func); + m.AddDomainIntegrator(epsilon_func); auto M_l = std::make_unique(m.Assemble(skip_zeros), h1_fespace_l); M_l->SetEssentialTrueDofs(h1_bdr_tdof_lists[l], Operator::DiagonalPolicy::DIAG_ONE); M_mg->AddOperator(std::move(M_l)); @@ -41,8 +41,7 @@ DivFreeSolver::DivFreeSolver(const MaterialOperator &mat_op, FiniteElementSpace } { BilinearForm weakdiv(nd_fespace, h1_fespaces.GetFinestFESpace()); - weakdiv.AddDomainIntegrator( - (mfem::MatrixCoefficient &)epsilon_func); + weakdiv.AddDomainIntegrator(epsilon_func); WeakDiv = std::make_unique(weakdiv.Assemble(skip_zeros), nd_fespace, h1_fespaces.GetFinestFESpace(), false); } diff --git a/palace/linalg/errorestimator.cpp b/palace/linalg/errorestimator.cpp index f43a00fcb..bbec2b0f4 100644 --- a/palace/linalg/errorestimator.cpp +++ b/palace/linalg/errorestimator.cpp @@ -63,8 +63,7 @@ FluxProjector::FluxProjector(const MaterialOperator &mat_op, MaterialPropertyCoefficient muinv_func(mat_op, mat_op.GetAttributeToMaterial(), mat_op.GetInvPermeability()); BilinearForm flux(nd_fespace); - flux.AddDomainIntegrator( - (mfem::MatrixCoefficient &)muinv_func); + flux.AddDomainIntegrator(muinv_func); Flux = std::make_unique(flux.PartialAssemble(), nd_fespace); } M = GetMassMatrix(nd_fespace); @@ -86,7 +85,7 @@ FluxProjector::FluxProjector(const MaterialOperator &mat_op, MaterialPropertyCoefficient epsilon_func(mat_op, mat_op.GetAttributeToMaterial(), mat_op.GetPermittivityReal()); BilinearForm flux(h1_fespace, h1d_fespace); - flux.AddDomainIntegrator((mfem::MatrixCoefficient &)epsilon_func); + flux.AddDomainIntegrator(epsilon_func); Flux = std::make_unique(flux.PartialAssemble(), h1_fespace, h1d_fespace, false); } @@ -193,7 +192,7 @@ ErrorIndicator CurlFluxErrorEstimator::ComputeIndicators(const VecType nd_fespace.Get().GetElementDofs(e, dofs, dof_trans); Interp.SetSize(fe.GetDof(), V_ip.Size()); Curl.SetSize(fe.GetDof(), V_ip.Size()); - const int q_order = fem::DefaultIntegrationOrder::Get(fe, fe, T); + const int q_order = fem::DefaultIntegrationOrder::Get(T); const mfem::IntegrationRule &ir = mfem::IntRules.Get(mesh.GetElementGeometry(e), q_order); @@ -303,7 +302,7 @@ ErrorIndicator GradFluxErrorEstimator::ComputeIndicators(const Vector &U) const h1d_fespace->Get().DofsToVDofs(vdofs); Interp.SetSize(fe.GetDof()); Grad.SetSize(fe.GetDof(), V_ip.Size()); - const int q_order = fem::DefaultIntegrationOrder::Get(fe, fe, T); + const int q_order = fem::DefaultIntegrationOrder::Get(T); const mfem::IntegrationRule &ir = mfem::IntRules.Get(mesh.GetElementGeometry(e), q_order); diff --git a/palace/linalg/hcurl.cpp b/palace/linalg/hcurl.cpp index 35d0e3347..d1788d7c5 100644 --- a/palace/linalg/hcurl.cpp +++ b/palace/linalg/hcurl.cpp @@ -44,14 +44,11 @@ WeightedHCurlNormSolver::WeightedHCurlNormSolver( BilinearForm a(fespace_l); if (aux) { - a.AddDomainIntegrator( - (mfem::MatrixCoefficient &)epsilon_func); + a.AddDomainIntegrator(epsilon_func); } else { - a.AddDomainIntegrator( - (mfem::MatrixCoefficient &)muinv_func, - (mfem::MatrixCoefficient &)epsilon_func); + a.AddDomainIntegrator(muinv_func, epsilon_func); } auto A_l = std::make_unique(a.Assemble(skip_zeros), fespace_l); A_l->SetEssentialTrueDofs(dbc_tdof_lists_l, Operator::DiagonalPolicy::DIAG_ONE); diff --git a/palace/models/curlcurloperator.cpp b/palace/models/curlcurloperator.cpp index 0f3b2a7f5..db90d5f0f 100644 --- a/palace/models/curlcurloperator.cpp +++ b/palace/models/curlcurloperator.cpp @@ -37,6 +37,7 @@ CurlCurlOperator::CurlCurlOperator(const IoData &iodata, { // Finalize setup. BilinearForm::pa_order_threshold = iodata.solver.pa_order_threshold; + fem::DefaultIntegrationOrder::p_trial = iodata.solver.order; fem::DefaultIntegrationOrder::q_order_jac = iodata.solver.q_order_jac; fem::DefaultIntegrationOrder::q_order_extra_pk = iodata.solver.q_order_extra; fem::DefaultIntegrationOrder::q_order_extra_qk = iodata.solver.q_order_extra; @@ -132,15 +133,14 @@ void PrintHeader(const mfem::ParFiniteElementSpace &h1_fespace, ? "Partial" : "Full"); - auto &mesh = *nd_fespace.GetParMesh(); - const int q_order = fem::DefaultIntegrationOrder::Get( - *nd_fespace.GetFE(0), *nd_fespace.GetFE(0), *mesh.GetElementTransformation(0)); + const auto &mesh = *nd_fespace.GetParMesh(); Mpi::Print(" Mesh geometries:\n"); for (auto geom : mesh::CheckElements(mesh).GetGeomTypes()) { const auto *fe = nd_fespace.FEColl()->FiniteElementForGeometry(geom); MFEM_VERIFY(fe, "MFEM does not support ND spaces on geometry = " << mfem::Geometry::Name[geom] << "!"); + const int q_order = fem::DefaultIntegrationOrder::Get(mesh, geom); Mpi::Print(" {}: P = {:d}, Q = {:d} (quadrature order = {:d})\n", mfem::Geometry::Name[geom], fe->GetDof(), mfem::IntRules.Get(geom, q_order).GetNPoints(), q_order); @@ -169,7 +169,7 @@ std::unique_ptr CurlCurlOperator::GetStiffnessMatrix() MaterialPropertyCoefficient muinv_func(mat_op, mat_op.GetAttributeToMaterial(), mat_op.GetInvPermeability()); BilinearForm k(nd_fespace_l); - k.AddDomainIntegrator((mfem::MatrixCoefficient &)muinv_func); + k.AddDomainIntegrator(muinv_func); auto K_l = std::make_unique( (l > 0) ? k.Assemble(skip_zeros) : k.FullAssemble(skip_zeros), nd_fespace_l); if (print_hdr) diff --git a/palace/models/domainpostoperator.cpp b/palace/models/domainpostoperator.cpp index 1740dd265..afa3558a9 100644 --- a/palace/models/domainpostoperator.cpp +++ b/palace/models/domainpostoperator.cpp @@ -28,8 +28,7 @@ DomainPostOperator::DomainPostOperator(const IoData &iodata, const MaterialOpera MaterialPropertyCoefficient epsilon_func(mat_op, mat_op.GetAttributeToMaterial(), mat_op.GetPermittivityReal()); BilinearForm m_nd(*nd_fespace); - m_nd.AddDomainIntegrator( - (mfem::MatrixCoefficient &)epsilon_func); + m_nd.AddDomainIntegrator(epsilon_func); M_ND = m_nd.PartialAssemble(); D.SetSize(M_ND->Height()); D.UseDevice(true); @@ -42,7 +41,7 @@ DomainPostOperator::DomainPostOperator(const IoData &iodata, const MaterialOpera MaterialPropertyCoefficient muinv_func(mat_op, mat_op.GetAttributeToMaterial(), mat_op.GetInvPermeability()); BilinearForm m_rt(*rt_fespace); - m_rt.AddDomainIntegrator((mfem::MatrixCoefficient &)muinv_func); + m_rt.AddDomainIntegrator(muinv_func); M_RT = m_rt.PartialAssemble(); H.SetSize(M_RT->Height()); H.UseDevice(true); @@ -59,8 +58,7 @@ DomainPostOperator::DomainPostOperator(const IoData &iodata, const MaterialOpera mat_op.GetPermittivityReal()); epsilon_func.RestrictCoefficient(mat_op.GetAttributeGlobalToLocal(data.attributes)); BilinearForm m_nd_i(*nd_fespace); - m_nd_i.AddDomainIntegrator( - (mfem::MatrixCoefficient &)epsilon_func); + m_nd_i.AddDomainIntegrator(epsilon_func); M_ND_i = m_nd_i.PartialAssemble(); } if (rt_fespace) @@ -69,8 +67,7 @@ DomainPostOperator::DomainPostOperator(const IoData &iodata, const MaterialOpera mat_op.GetInvPermeability()); muinv_func.RestrictCoefficient(mat_op.GetAttributeGlobalToLocal(data.attributes)); BilinearForm m_rt_i(*rt_fespace); - m_rt_i.AddDomainIntegrator( - (mfem::MatrixCoefficient &)muinv_func); + m_rt_i.AddDomainIntegrator(muinv_func); M_RT_i = m_rt_i.PartialAssemble(); } M_i.emplace(idx, std::make_pair(std::move(M_ND_i), std::move(M_RT_i))); diff --git a/palace/models/laplaceoperator.cpp b/palace/models/laplaceoperator.cpp index 44186be63..b9b717fa7 100644 --- a/palace/models/laplaceoperator.cpp +++ b/palace/models/laplaceoperator.cpp @@ -31,6 +31,7 @@ LaplaceOperator::LaplaceOperator(const IoData &iodata, { // Finalize setup. BilinearForm::pa_order_threshold = iodata.solver.pa_order_threshold; + fem::DefaultIntegrationOrder::p_trial = iodata.solver.order; fem::DefaultIntegrationOrder::q_order_jac = iodata.solver.q_order_jac; fem::DefaultIntegrationOrder::q_order_extra_pk = iodata.solver.q_order_extra; fem::DefaultIntegrationOrder::q_order_extra_qk = iodata.solver.q_order_extra; @@ -153,15 +154,14 @@ void PrintHeader(const mfem::ParFiniteElementSpace &h1_fespace, ? "Partial" : "Full"); - auto &mesh = *h1_fespace.GetParMesh(); - const int q_order = fem::DefaultIntegrationOrder::Get( - *h1_fespace.GetFE(0), *h1_fespace.GetFE(0), *mesh.GetElementTransformation(0)); + const auto &mesh = *h1_fespace.GetParMesh(); Mpi::Print(" Mesh geometries:\n"); for (auto geom : mesh::CheckElements(mesh).GetGeomTypes()) { const auto *fe = h1_fespace.FEColl()->FiniteElementForGeometry(geom); MFEM_VERIFY(fe, "MFEM does not support H1 spaces on geometry = " << mfem::Geometry::Name[geom] << "!"); + const int q_order = fem::DefaultIntegrationOrder::Get(mesh, geom); Mpi::Print(" {}: P = {:d}, Q = {:d} (quadrature order = {:d})\n", mfem::Geometry::Name[geom], fe->GetDof(), mfem::IntRules.Get(geom, q_order).GetNPoints(), q_order); @@ -190,7 +190,7 @@ std::unique_ptr LaplaceOperator::GetStiffnessMatrix() MaterialPropertyCoefficient epsilon_func(mat_op, mat_op.GetAttributeToMaterial(), mat_op.GetPermittivityReal()); BilinearForm k(h1_fespace_l); - k.AddDomainIntegrator((mfem::MatrixCoefficient &)epsilon_func); + k.AddDomainIntegrator(epsilon_func); auto K_l = std::make_unique( (l > 0) ? k.Assemble(skip_zeros) : k.FullAssemble(skip_zeros), h1_fespace_l); if (print_hdr) diff --git a/palace/models/spaceoperator.cpp b/palace/models/spaceoperator.cpp index 448f79eaf..d4a898acf 100644 --- a/palace/models/spaceoperator.cpp +++ b/palace/models/spaceoperator.cpp @@ -46,6 +46,7 @@ SpaceOperator::SpaceOperator(const IoData &iodata, { // Finalize setup. BilinearForm::pa_order_threshold = iodata.solver.pa_order_threshold; + fem::DefaultIntegrationOrder::p_trial = iodata.solver.order; fem::DefaultIntegrationOrder::q_order_jac = iodata.solver.q_order_jac; fem::DefaultIntegrationOrder::q_order_extra_pk = iodata.solver.q_order_extra; fem::DefaultIntegrationOrder::q_order_extra_qk = iodata.solver.q_order_extra; @@ -181,15 +182,14 @@ void PrintHeader(const mfem::ParFiniteElementSpace &h1_fespace, ? "Partial" : "Full"); - auto &mesh = *nd_fespace.GetParMesh(); - const int q_order = fem::DefaultIntegrationOrder::Get( - *nd_fespace.GetFE(0), *nd_fespace.GetFE(0), *mesh.GetElementTransformation(0)); + const auto &mesh = *nd_fespace.GetParMesh(); Mpi::Print(" Mesh geometries:\n"); for (auto geom : mesh::CheckElements(mesh).GetGeomTypes()) { const auto *fe = nd_fespace.FEColl()->FiniteElementForGeometry(geom); MFEM_VERIFY(fe, "MFEM does not support ND spaces on geometry = " << mfem::Geometry::Name[geom] << "!"); + const int q_order = fem::DefaultIntegrationOrder::Get(mesh, geom); Mpi::Print(" {}: P = {:d}, Q = {:d} (quadrature order = {:d})\n", mfem::Geometry::Name[geom], fe->GetDof(), mfem::IntRules.Get(geom, q_order).GetNPoints(), q_order); @@ -206,48 +206,32 @@ BuildOperator(const FiniteElementSpace &fespace, const MaterialPropertyCoefficie BilinearForm a(fespace); if (df && !df->empty() && f && !f->empty()) { - a.AddDomainIntegrator((mfem::MatrixCoefficient &)*df, - (mfem::MatrixCoefficient &)*f); + a.AddDomainIntegrator(*df, *f); } else { if (df && !df->empty()) { - a.AddDomainIntegrator((mfem::MatrixCoefficient &)*df); + a.AddDomainIntegrator(*df); } if (f && !f->empty()) { - if (f->GetMaterialProperties().SizeI() == 1) - { - a.AddDomainIntegrator((mfem::Coefficient &)*f); - } - else - { - a.AddDomainIntegrator((mfem::MatrixCoefficient &)*f); - } + a.AddDomainIntegrator(*f); } } if (dfb && !dfb->empty() && fb && !fb->empty()) { - a.AddBoundaryIntegrator((mfem::Coefficient &)*dfb, - (mfem::MatrixCoefficient &)*fb); + a.AddBoundaryIntegrator(*dfb, *fb); } else { if (dfb && !dfb->empty()) { - a.AddBoundaryIntegrator((mfem::Coefficient &)*dfb); + a.AddBoundaryIntegrator(*dfb); } if (fb && !fb->empty()) { - if (fb->GetMaterialProperties().SizeI() == 1) - { - a.AddBoundaryIntegrator((mfem::Coefficient &)*fb); - } - else - { - a.AddBoundaryIntegrator((mfem::MatrixCoefficient &)*fb); - } + a.AddBoundaryIntegrator(*fb); } } return (l > 0) ? a.Assemble(skip_zeros) : a.FullAssemble(skip_zeros); @@ -269,18 +253,11 @@ std::unique_ptr BuildAuxOperator(const FiniteElementSpace &fespace, BilinearForm a(fespace); if (f && !f->empty()) { - a.AddDomainIntegrator((mfem::MatrixCoefficient &)*f); + a.AddDomainIntegrator(*f); } if (fb && !fb->empty()) { - if (fb->GetMaterialProperties().SizeI() == 1) - { - a.AddBoundaryIntegrator((mfem::Coefficient &)*fb); - } - else - { - a.AddBoundaryIntegrator((mfem::MatrixCoefficient &)*fb); - } + a.AddBoundaryIntegrator(*fb); } return (l > 0) ? a.Assemble(skip_zeros) : a.FullAssemble(skip_zeros); } diff --git a/palace/models/waveportoperator.cpp b/palace/models/waveportoperator.cpp index f648c6dc3..943211d6d 100644 --- a/palace/models/waveportoperator.cpp +++ b/palace/models/waveportoperator.cpp @@ -110,7 +110,7 @@ std::unique_ptr GetBtt(const MaterialOperator &mat_op, MaterialPropertyCoefficient muinv_func(mat_op, mat_op.GetBdrAttributeToMaterial(), mat_op.GetInvPermeability()); BilinearForm btt(nd_fespace); - btt.AddDomainIntegrator((mfem::MatrixCoefficient &)muinv_func); + btt.AddDomainIntegrator(muinv_func); return std::make_unique(btt.FullAssemble(skip_zeros), nd_fespace); } @@ -122,8 +122,7 @@ std::unique_ptr GetBtn(const MaterialOperator &mat_op, MaterialPropertyCoefficient muinv_func(mat_op, mat_op.GetBdrAttributeToMaterial(), mat_op.GetInvPermeability()); BilinearForm btn(h1_fespace, nd_fespace); - btn.AddDomainIntegrator( - (mfem::MatrixCoefficient &)muinv_func); + btn.AddDomainIntegrator(muinv_func); return std::make_unique(btn.FullAssemble(skip_zeros), h1_fespace, nd_fespace, false); } @@ -136,13 +135,13 @@ std::array, 3> GetBnn(const MaterialOperator &mat_o MaterialPropertyCoefficient muinv_func(mat_op, mat_op.GetBdrAttributeToMaterial(), mat_op.GetInvPermeability()); BilinearForm bnn1(h1_fespace); - bnn1.AddDomainIntegrator((mfem::MatrixCoefficient &)muinv_func); + bnn1.AddDomainIntegrator(muinv_func); MaterialPropertyCoefficient epsilon_func(mat_op, mat_op.GetBdrAttributeToMaterial(), mat_op.GetPermittivityReal()); epsilon_func.NormalProjectedCoefficient(normal); BilinearForm bnn2r(h1_fespace); - bnn2r.AddDomainIntegrator((mfem::Coefficient &)epsilon_func); + bnn2r.AddDomainIntegrator(epsilon_func); // Contribution for loss tangent: ε -> ε * (1 - i tan(δ)). if (!mat_op.HasLossTangent()) @@ -155,7 +154,7 @@ std::array, 3> GetBnn(const MaterialOperator &mat_o mat_op, mat_op.GetBdrAttributeToMaterial(), mat_op.GetPermittivityImag()); negepstandelta_func.NormalProjectedCoefficient(normal); BilinearForm bnn2i(h1_fespace); - bnn2i.AddDomainIntegrator((mfem::Coefficient &)negepstandelta_func); + bnn2i.AddDomainIntegrator(negepstandelta_func); return {std::make_unique(bnn1.FullAssemble(skip_zeros), h1_fespace), std::make_unique(bnn2r.FullAssemble(skip_zeros), h1_fespace), std::make_unique(bnn2i.FullAssemble(skip_zeros), h1_fespace)}; @@ -170,13 +169,12 @@ std::array, 3> GetAtt(const MaterialOperator &mat_o mat_op.GetInvPermeability()); muinv_func.NormalProjectedCoefficient(normal); BilinearForm att1(nd_fespace); - att1.AddDomainIntegrator((mfem::Coefficient &)muinv_func); + att1.AddDomainIntegrator(muinv_func); MaterialPropertyCoefficient epsilon_func(mat_op, mat_op.GetBdrAttributeToMaterial(), mat_op.GetPermittivityReal()); BilinearForm att2r(nd_fespace); - att2r.AddDomainIntegrator( - (mfem::MatrixCoefficient &)epsilon_func); + att2r.AddDomainIntegrator(epsilon_func); // Contribution for loss tangent: ε -> ε * (1 - i tan(δ)). if (!mat_op.HasLossTangent()) @@ -188,8 +186,7 @@ std::array, 3> GetAtt(const MaterialOperator &mat_o MaterialPropertyCoefficient negepstandelta_func( mat_op, mat_op.GetBdrAttributeToMaterial(), mat_op.GetPermittivityImag()); BilinearForm att2i(nd_fespace); - att2i.AddDomainIntegrator( - (mfem::MatrixCoefficient &)negepstandelta_func); + att2i.AddDomainIntegrator(negepstandelta_func); return {std::make_unique(att1.FullAssemble(skip_zeros), nd_fespace), std::make_unique(att2r.FullAssemble(skip_zeros), nd_fespace), std::make_unique(att2i.FullAssemble(skip_zeros), nd_fespace)}; From e145e54c81a1e1457b39f04e5147c214cd8eaaf4 Mon Sep 17 00:00:00 2001 From: Sebastian Grimberg Date: Tue, 19 Dec 2023 14:35:55 -0800 Subject: [PATCH 14/32] Finalize palace::Mesh and libCEED geometry factor data refactor --- .clang-format | 3 +- palace/utils/diagnostic.hpp | 5 ++- palace/utils/prettyprint.hpp | 63 ++++++++++++------------------------ 3 files changed, 27 insertions(+), 44 deletions(-) diff --git a/.clang-format b/.clang-format index a2a6ae852..e0dc9ba32 100644 --- a/.clang-format +++ b/.clang-format @@ -52,6 +52,7 @@ SpacesBeforeTrailingComments: 2 StatementMacros: ['PalacePragmaOmp', 'PalacePragmaDiagnosticPush', 'PalacePragmaDiagnosticPop', - 'PalacePragmaDiagnosticDisableDeprecated'] + 'PalacePragmaDiagnosticDisableDeprecated', + 'PalacePragmaDiagnosticDisableUnused'] TypenameMacros: ['CEED_QFUNCTION'] UseTab: Never diff --git a/palace/utils/diagnostic.hpp b/palace/utils/diagnostic.hpp index a09c9ea09..d42471820 100644 --- a/palace/utils/diagnostic.hpp +++ b/palace/utils/diagnostic.hpp @@ -7,12 +7,15 @@ #if defined(_MSC_VER) #define PalacePragmaDiagnosticPush _Pragma("warning(push)") #define PalacePragmaDiagnosticPop _Pragma("warning(pop)") -#define PalacePragmaDiagnosticDisableDeprecated _Pragma("warning(disable : 4996)") +#define PalacePragmaDiagnosticDisableDeprecated _Pragma("warning(disable:4996)") +#define PalacePragmaDiagnosticDisableUnused _Pragma("warning(disable:4505)") #else #define PalacePragmaDiagnosticPush _Pragma("GCC diagnostic push") #define PalacePragmaDiagnosticPop _Pragma("GCC diagnostic pop") #define PalacePragmaDiagnosticDisableDeprecated \ _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#define PalacePragmaDiagnosticDisableUnused \ + _Pragma("GCC diagnostic ignored \"-Wunused-function\"") #endif #endif // PALACE_UTILS_DIAGNOSTIC_HPP diff --git a/palace/utils/prettyprint.hpp b/palace/utils/prettyprint.hpp index 7c547cd59..06daac02e 100644 --- a/palace/utils/prettyprint.hpp +++ b/palace/utils/prettyprint.hpp @@ -56,70 +56,49 @@ inline std::size_t PrePrint(MPI_Comm comm, std::size_t w, std::size_t wv, std::s } // namespace internal -// Fixed column width wrapped printing with range notation for the contents of a marker -// array. +// Fixed column width wrapped printing for the contents of an array, with with range +// notation for integral types. template