// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced // at the Lawrence Livermore National Laboratory. All Rights reserved. See files // LICENSE and NOTICE for details. LLNL-CODE-806117. // // This file is part of the MFEM library. For more information and source code // availability visit https://mfem.org. // // MFEM is free software; you can redistribute it and/or modify it under the // terms of the BSD-3 license. We welcome feedback and contributions, see file // CONTRIBUTING.md for details. #include "qspace.hpp" #include "qfunction.hpp" #include "../general/forall.hpp" namespace mfem { QuadratureSpaceBase::QuadratureSpaceBase(Mesh &mesh_, Geometry::Type geom, const IntegrationRule &ir) : mesh(mesh_), order(ir.GetOrder()) { for (int g = 0; g < Geometry::NumGeom; g++) { int_rule[g] = nullptr; } int_rule[geom] = &ir; } void QuadratureSpaceBase::ConstructIntRules(int dim) { Array geoms; mesh.GetGeometries(dim, geoms); for (Geometry::Type geom : geoms) { int_rule[geom] = &IntRules.Get(geom, order); } } const Array &QuadratureSpaceBase::Offsets( QSpaceOffsetStorage storage) const { if (storage == QSpaceOffsetStorage::COMPRESSED || offsets.Size() > 1) { return offsets; } else { if (full_offset_cache.Size() == 0) { const int nq = size / ne; full_offset_cache.SetSize(ne + 1); int *d_full_offset_cache = full_offset_cache.Write(); mfem::forall(ne + 1, [=] MFEM_HOST_DEVICE (int e) { d_full_offset_cache[e] = nq * e; }); } return full_offset_cache; } } namespace { void ScaleByQuadratureWeights(Vector &weights, const IntegrationRule &ir) { const int N = weights.Size(); const int n = ir.Size(); real_t *d_weights = weights.ReadWrite(); const real_t *d_w = ir.GetWeights().Read(); mfem::forall(N, [=] MFEM_HOST_DEVICE (int i) { d_weights[i] *= d_w[i%n]; }); } } // anonymous namespace void QuadratureSpaceBase::ConstructWeights() const { // First get the Jacobian determinants (without the quadrature weight // contributions). We also store the pointer to the Vector object, so that // we know when the cached weights are invalidated. nodes_sequence = mesh.GetNodesSequence(); weights = GetGeometricFactorWeights(); // Then scale by the quadrature weights. const IntegrationRule &ir = GetIntRule(0); ScaleByQuadratureWeights(weights, ir); } const Vector &QuadratureSpaceBase::GetWeights() const { if (GetNE() == 0) { return weights; } if (weights.Size() == 0 || nodes_sequence != mesh.GetNodesSequence()) { ConstructWeights(); } return weights; } real_t QuadratureSpaceBase::Integrate(Coefficient &coeff) const { QuadratureFunction qf(const_cast(this)); coeff.Project(qf); return qf.Integrate(); } void QuadratureSpaceBase::Integrate(VectorCoefficient &coeff, Vector &integrals) const { const int vdim = coeff.GetVDim(); QuadratureFunction qf(const_cast(this), vdim); coeff.Project(qf); qf.Integrate(integrals); } void QuadratureSpace::ConstructOffsets() { const int num_elem = mesh.GetNE(); ne = num_elem; if (mesh.GetNumGeometries(mesh.Dimension()) == 1) { Array geoms; mesh.GetGeometries(mesh.Dimension(), geoms); offsets.SetSize(1); offsets.HostWrite(); offsets[0] = int_rule[geoms[0]]->GetNPoints(); size = num_elem * offsets[0]; } else { offsets.SetSize(num_elem + 1); int offset = 0; for (int i = 0; i < num_elem; i++) { offsets[i] = offset; const Geometry::Type geom = mesh.GetElementBaseGeometry(i); MFEM_ASSERT(int_rule[geom] != nullptr, "Missing integration rule."); offset += int_rule[geom]->GetNPoints(); } offsets[num_elem] = offset; size = offsets.Last(); } } void QuadratureSpace::Construct() { ConstructIntRules(mesh.Dimension()); ConstructOffsets(); } QuadratureSpace::QuadratureSpace(Mesh *mesh_, std::istream &in) : QuadratureSpaceBase(*mesh_) { const char *msg = "invalid input stream"; std::string ident; in >> ident; MFEM_VERIFY(ident == "QuadratureSpace", msg); in >> ident; MFEM_VERIFY(ident == "Type:", msg); in >> ident; if (ident == "default_quadrature") { in >> ident; MFEM_VERIFY(ident == "Order:", msg); in >> order; } else { MFEM_ABORT("unknown QuadratureSpace type: " << ident); return; } Construct(); } QuadratureSpace::QuadratureSpace(Mesh &mesh_, const IntegrationRule &ir) : QuadratureSpaceBase(mesh_, mesh_.GetTypicalElementGeometry(), ir) { MFEM_VERIFY(mesh.GetNumGeometries(mesh.Dimension()) <= 1, "Constructor not valid for mixed meshes"); ConstructOffsets(); } void QuadratureSpace::Save(std::ostream &os) const { os << "QuadratureSpace\n" << "Type: default_quadrature\n" << "Order: " << order << '\n'; } const Vector &QuadratureSpace::GetGeometricFactorWeights() const { auto flags = GeometricFactors::DETERMINANTS; // TODO: assumes only one integration rule. This should be fixed once // Mesh::GetGeometricFactors acceps a QuadratureSpace instead of // IntegrationRule. const IntegrationRule &ir = GetIntRule(0); auto *geom = mesh.GetGeometricFactors(ir, flags); return geom->detJ; } FaceQuadratureSpace::FaceQuadratureSpace(Mesh &mesh_, int order_, FaceType face_type_) : QuadratureSpaceBase(mesh_, order_), face_type(face_type_), face_indices(mesh.GetFaceIndices(face_type_)), face_indices_inv(mesh.GetInvFaceIndices(face_type_)) { Construct(); } FaceQuadratureSpace::FaceQuadratureSpace(Mesh &mesh_, const IntegrationRule &ir, FaceType face_type_) : QuadratureSpaceBase(mesh_, mesh_.GetTypicalFaceGeometry(), ir), face_type(face_type_), face_indices(mesh.GetFaceIndices(face_type_)), face_indices_inv(mesh.GetInvFaceIndices(face_type_)) { MFEM_VERIFY(mesh.GetNumGeometries(mesh.Dimension() - 1) <= 1, "Constructor not valid for mixed meshes"); ConstructOffsets(); } void FaceQuadratureSpace::ConstructOffsets() { ne = face_indices.Size(); if (mesh.GetNumGeometries(mesh.Dimension() - 1) == 1) { Array geoms; mesh.GetGeometries(mesh.Dimension() - 1, geoms); offsets.SetSize(1); offsets.HostWrite(); offsets[0] = int_rule[geoms[0]]->GetNPoints(); size = ne * offsets[0]; } else { offsets.SetSize(face_indices.Size() + 1); int offset = 0; for (int i = 0; i < mesh.GetNFbyType(face_type); ++i) { offsets[i] = offset; Geometry::Type geom = mesh.GetFaceGeometry(face_indices[i]); MFEM_ASSERT(int_rule[geom] != nullptr, "Missing integration rule"); offset += int_rule[geom]->GetNPoints(); } offsets[face_indices.Size()] = size = offset; } } void FaceQuadratureSpace::Construct() { ConstructIntRules(mesh.Dimension() - 1); ConstructOffsets(); } int FaceQuadratureSpace::GetPermutedIndex(int idx, int iq) const { const int f_idx = face_indices[idx]; if (Geometry::IsTensorProduct(GetGeometry(idx))) { const int dim = mesh.Dimension(); const IntegrationRule &ir = GetIntRule(idx); const int q1d = (int)floor(pow(ir.GetNPoints(), 1.0/(dim-1)) + 0.5); const Mesh::FaceInformation face = mesh.GetFaceInformation(f_idx); return ToLexOrdering(dim, face.element[0].local_face_id, q1d, iq); } else { return iq; } } ElementTransformation *FaceQuadratureSpace::GetTransformation(int idx) { ElementTransformation *T = mesh.GetFaceTransformation(face_indices[idx]); if (face_type == FaceType::Boundary) { T->Attribute = mesh.GetBdrFaceAttributes()[idx]; } return T; } int FaceQuadratureSpace::GetEntityIndex(const ElementTransformation &T) const { auto get_face_index = [this](const int idx) { const auto it = face_indices_inv.find(idx); if (it == face_indices_inv.end()) { return -1; } else { return it->second; } }; switch (T.ElementType) { case ElementTransformation::FACE: return get_face_index(T.ElementNo); case ElementTransformation::BDR_ELEMENT: case ElementTransformation::BDR_FACE: return get_face_index(mesh.GetBdrElementFaceIndex(T.ElementNo)); default: MFEM_ABORT("Invalid element type."); return -1; } } void FaceQuadratureSpace::Save(std::ostream &os) const { os << "FaceQuadratureSpace\n" << "Type: default_quadrature\n" << "Order: " << order << '\n'; } const Vector &FaceQuadratureSpace::GetGeometricFactorWeights() const { auto flags = FaceGeometricFactors::DETERMINANTS; // TODO: assumes only one integration rule. This should be fixed once // Mesh::GetFaceGeometricFactors acceps a QuadratureSpace instead of // IntegrationRule. const IntegrationRule &ir = GetIntRule(0); auto *geom = mesh.GetFaceGeometricFactors(ir, flags, face_type); return geom->detJ; } } // namespace mfem