// Copyright (c) 2010-2023, Lawrence Livermore National Security, LLC. Produced // at the Lawrence Livermore National Laboratory. All Rights reserved. See files // LICENSE and NOTICE for details. LLNL-CODE-806117. // // This file is part of the MFEM library. For more information and source code // availability visit https://mfem.org. // // MFEM is free software; you can redistribute it and/or modify it under the // terms of the BSD-3 license. We welcome feedback and contributions, see file // CONTRIBUTING.md for details. #ifndef MFEM_LIBCEED_COEFF #define MFEM_LIBCEED_COEFF #ifdef MFEM_USE_CEED #include "../../../general/forall.hpp" #include "../../../config/config.hpp" #include "../../../linalg/vector.hpp" #include "../../../linalg/dtensor.hpp" #include "../../../mesh/mesh.hpp" #include "../../gridfunc.hpp" #include "../../qfunction.hpp" #include "util.hpp" #include "ceed.hpp" namespace mfem { class Mesh; class IntegrationRule; class Coefficient; class VectorCoefficient; class GridFunction; namespace ceed { struct Coefficient { const int ncomp; Coefficient(int ncomp_) : ncomp(ncomp_) { } virtual bool IsConstant() const { return true; } virtual ~Coefficient() { } }; struct VariableCoefficient : Coefficient { CeedVector coeffVector = nullptr; const CeedEvalMode emode; VariableCoefficient(int ncomp_, CeedEvalMode emode_) : Coefficient(ncomp_), emode(emode_) { } virtual bool IsConstant() const override { return false; } ~VariableCoefficient() { CeedVectorDestroy(&coeffVector); } }; struct GridCoefficient : VariableCoefficient { const mfem::GridFunction &gf; CeedBasis basis; CeedElemRestriction restr; GridCoefficient(const mfem::GridFunction &gf_) : VariableCoefficient(gf_.VectorDim(), CEED_EVAL_INTERP), gf(gf_) { InitVector(gf, coeffVector); } }; struct QuadCoefficient : VariableCoefficient { mfem::Vector coeff; CeedElemRestriction restr; QuadCoefficient(int ncomp_) : VariableCoefficient(ncomp_, CEED_EVAL_NONE) { } }; /** @brief Initializes an mfem::ceed::Coefficient @a coeff_ptr from an mfem::Coefficient @a Q, an mfem::Mesh @a mesh, and an mfem::IntegrationRule @a ir. @param[in] Q is the coefficient from the `Integrator`. @param[in] mesh is the mesh. @param[in] ir is the integration rule. @param[out] coeff_ptr is the structure to store the coefficient for the `CeedOperator`. @param[out] ctx is the Context associated to the QFunction. */ template void InitCoefficient(mfem::Coefficient *Q, mfem::Mesh &mesh, const mfem::IntegrationRule &ir, Coefficient*& coeff_ptr, Context &ctx) { if ( Q == nullptr ) { Coefficient *ceedCoeff = new Coefficient(1); ctx.coeff = 1.0; coeff_ptr = ceedCoeff; } else if (ConstantCoefficient *const_coeff = dynamic_cast(Q)) { Coefficient *ceedCoeff = new Coefficient(1); ctx.coeff = const_coeff->constant; coeff_ptr = ceedCoeff; } else if (GridFunctionCoefficient* gf_coeff = dynamic_cast(Q)) { GridCoefficient *ceedCoeff = new GridCoefficient(*gf_coeff->GetGridFunction()); coeff_ptr = ceedCoeff; } else if (QuadratureFunctionCoefficient *cQ = dynamic_cast(Q)) { QuadCoefficient *ceedCoeff = new QuadCoefficient(1); const int ne = mesh.GetNE(); const int nq = ir.GetNPoints(); const mfem::QuadratureFunction &qFun = cQ->GetQuadFunction(); MFEM_VERIFY(qFun.Size() == nq * ne, "Incompatible QuadratureFunction dimension \n"); MFEM_VERIFY(&ir == &qFun.GetSpace()->GetIntRule(0), "IntegrationRule used within integrator and in" " QuadratureFunction appear to be different"); qFun.Read(); ceedCoeff->coeff.MakeRef(const_cast(qFun),0); InitVector(ceedCoeff->coeff, ceedCoeff->coeffVector); coeff_ptr = ceedCoeff; } else { QuadCoefficient *ceedCoeff = new QuadCoefficient(1); const int ne = mesh.GetNE(); const int nq = ir.GetNPoints(); ceedCoeff->coeff.SetSize(nq * ne); auto C = Reshape(ceedCoeff->coeff.HostWrite(), nq, ne); for (int e = 0; e < ne; ++e) { mfem::ElementTransformation &T = *mesh.GetElementTransformation(e); for (int q = 0; q < nq; ++q) { C(q,e) = Q->Eval(T, ir.IntPoint(q)); } } InitVector(ceedCoeff->coeff, ceedCoeff->coeffVector); coeff_ptr = ceedCoeff; } } /** @brief Initializes an mfem::ceed::Coefficient @a coeff_ptr from an mfem::VectorCoefficient @a VQ, an mfem::Mesh @a mesh, and an mfem::IntegrationRule @a ir. @param[in] VQ is the vector coefficient from the `Integrator`. @param[in] mesh is the mesh. @param[in] ir is the integration rule. @param[out] coeff_ptr is the structure to store the coefficient for the `CeedOperator`. @param[out] ctx is the Context associated to the QFunction. */ template void InitCoefficient(mfem::VectorCoefficient *VQ, mfem::Mesh &mesh, const mfem::IntegrationRule &ir, Coefficient *&coeff_ptr, Context &ctx) { if (VectorConstantCoefficient *const_coeff = dynamic_cast(VQ)) { const int vdim = const_coeff->GetVDim(); const mfem::Vector &val = const_coeff->GetVec(); Coefficient *ceedCoeff = new Coefficient(vdim); for (int i = 0; i < vdim; i++) { ctx.coeff[i] = val[i]; } coeff_ptr = ceedCoeff; } else if (VectorGridFunctionCoefficient* vgf_coeff = dynamic_cast(VQ)) { GridCoefficient *ceedCoeff = new GridCoefficient(*vgf_coeff->GetGridFunction()); coeff_ptr = ceedCoeff; } else if (VectorQuadratureFunctionCoefficient *cQ = dynamic_cast(VQ)) { QuadCoefficient *ceedCoeff = new QuadCoefficient(cQ->GetVDim()); const int dim = mesh.Dimension(); const int ne = mesh.GetNE(); const int nq = ir.GetNPoints(); const mfem::QuadratureFunction &qFun = cQ->GetQuadFunction(); MFEM_VERIFY(qFun.Size() == dim * nq * ne, "Incompatible QuadratureFunction dimension \n"); MFEM_VERIFY(&ir == &qFun.GetSpace()->GetIntRule(0), "IntegrationRule used within integrator and in" " QuadratureFunction appear to be different"); qFun.Read(); ceedCoeff->coeff.MakeRef(const_cast(qFun),0); InitVector(ceedCoeff->coeff, ceedCoeff->coeffVector); coeff_ptr = ceedCoeff; } else { const int dim = mesh.Dimension(); QuadCoefficient *ceedCoeff = new QuadCoefficient(dim); const int ne = mesh.GetNE(); const int nq = ir.GetNPoints(); ceedCoeff->coeff.SetSize(dim * nq * ne); auto C = Reshape(ceedCoeff->coeff.HostWrite(), dim, nq, ne); mfem::DenseMatrix Q_ir; for (int e = 0; e < ne; ++e) { mfem::ElementTransformation &T = *mesh.GetElementTransformation(e); VQ->Eval(Q_ir, T, ir); for (int q = 0; q < nq; ++q) { for (int i = 0; i < dim; ++i) { C(i,q,e) = Q_ir(i,q); } } } InitVector(ceedCoeff->coeff, ceedCoeff->coeffVector); coeff_ptr = ceedCoeff; } } /** @brief Initializes an mfem::ceed::Coefficient @a coeff_ptr from an mfem::Coefficient @a Q, an mfem::Mesh @a mesh, and an mfem::IntegrationRule @a ir for the elements given by the indices @a indices. @param[in] Q is the coefficient from the `Integrator`. @param[in] mesh is the mesh. @param[in] ir is the integration rule. @param[in] nelem The number of elements. @param[in] indices The indices of the elements of same type in the `FiniteElementSpace`. @param[out] coeff_ptr is the structure to store the coefficient for the `CeedOperator`. @param[out] ctx is the Context associated to the QFunction. */ template void InitCoefficientWithIndices(mfem::Coefficient *Q, mfem::Mesh &mesh, const mfem::IntegrationRule &ir, int nelem, const int* indices, Coefficient*& coeff_ptr, Context &ctx) { if ( Q == nullptr ) { Coefficient *ceedCoeff = new Coefficient(1); ctx.coeff = 1.0; coeff_ptr = ceedCoeff; } else if (ConstantCoefficient *const_coeff = dynamic_cast(Q)) { Coefficient *ceedCoeff = new Coefficient(1); ctx.coeff = const_coeff->constant; coeff_ptr = ceedCoeff; } else if (GridFunctionCoefficient* gf_coeff = dynamic_cast(Q)) { GridCoefficient *ceedCoeff = new GridCoefficient(*gf_coeff->GetGridFunction()); coeff_ptr = ceedCoeff; } else if (QuadratureFunctionCoefficient *cQ = dynamic_cast(Q)) { QuadCoefficient *ceedCoeff = new QuadCoefficient(1); const int ne = mesh.GetNE(); const int nq = ir.GetNPoints(); const mfem::QuadratureFunction &qFun = cQ->GetQuadFunction(); MFEM_VERIFY(qFun.Size() == nq * ne, "Incompatible QuadratureFunction dimension \n"); MFEM_VERIFY(&ir == &qFun.GetSpace()->GetIntRule(0), "IntegrationRule used within integrator and in" " QuadratureFunction appear to be different"); ceedCoeff->coeff.SetSize(nq * nelem); Memory m_indices((int*)indices, nelem, false); auto in = Reshape(qFun.Read(), nq, ne); auto d_indices = Read(m_indices, nelem); auto out = Reshape(ceedCoeff->coeff.Write(), nq, nelem); mfem::forall(nelem * nq, [=] MFEM_HOST_DEVICE (int i) { const int q = i%nq; const int sub_e = i/nq; const int e = d_indices[sub_e]; out(q, sub_e) = in(q, e); }); m_indices.DeleteDevice(); InitVector(ceedCoeff->coeff, ceedCoeff->coeffVector); coeff_ptr = ceedCoeff; } else { QuadCoefficient *ceedCoeff = new QuadCoefficient(1); const int nq = ir.GetNPoints(); ceedCoeff->coeff.SetSize(nq * nelem); auto C = Reshape(ceedCoeff->coeff.HostWrite(), nq, nelem); for (int i = 0; i < nelem; ++i) { const int e = indices[i]; mfem::ElementTransformation &T = *mesh.GetElementTransformation(e); for (int q = 0; q < nq; ++q) { C(q, i) = Q->Eval(T, ir.IntPoint(q)); } } InitVector(ceedCoeff->coeff, ceedCoeff->coeffVector); coeff_ptr = ceedCoeff; } } /** @brief Initializes an mfem::ceed::Coefficient @a coeff_ptr from an mfem::VectorCoefficient @a Q, an mfem::Mesh @a mesh, and an mfem::IntegrationRule @a ir for the elements given by the indices @a indices. @param[in] VQ is the vector coefficient from the `Integrator`. @param[in] mesh is the mesh. @param[in] ir is the integration rule. @param[in] nelem The number of elements. @param[in] indices The indices of the elements of same type in the `FiniteElementSpace`. @param[out] coeff_ptr is the structure to store the coefficient for the `CeedOperator`. @param[out] ctx is the Context associated to the QFunction. */ template void InitCoefficientWithIndices(mfem::VectorCoefficient *VQ, mfem::Mesh &mesh, const mfem::IntegrationRule &ir, int nelem, const int* indices, Coefficient *&coeff_ptr, Context &ctx) { if (VectorConstantCoefficient *const_coeff = dynamic_cast(VQ)) { const int vdim = const_coeff->GetVDim(); const mfem::Vector &val = const_coeff->GetVec(); Coefficient *ceedCoeff = new Coefficient(vdim); for (int i = 0; i < vdim; i++) { ctx.coeff[i] = val[i]; } coeff_ptr = ceedCoeff; } else if (VectorGridFunctionCoefficient* vgf_coeff = dynamic_cast(VQ)) { GridCoefficient *ceedCoeff = new GridCoefficient(*vgf_coeff->GetGridFunction()); coeff_ptr = ceedCoeff; } else if (VectorQuadratureFunctionCoefficient *cQ = dynamic_cast(VQ)) { QuadCoefficient *ceedCoeff = new QuadCoefficient(cQ->GetVDim()); const int dim = mesh.Dimension(); const int ne = mesh.GetNE(); const int nq = ir.GetNPoints(); const mfem::QuadratureFunction &qFun = cQ->GetQuadFunction(); MFEM_VERIFY(qFun.Size() == dim * nq * ne, "Incompatible QuadratureFunction dimension \n"); MFEM_VERIFY(&ir == &qFun.GetSpace()->GetIntRule(0), "IntegrationRule used within integrator and in" " QuadratureFunction appear to be different"); ceedCoeff->coeff.SetSize(dim * nq * nelem); Memory m_indices((int*)indices, nelem, false); auto in = Reshape(qFun.Read(), dim, nq, ne); auto d_indices = Read(m_indices, nelem); auto out = Reshape(ceedCoeff->coeff.Write(), dim, nq, nelem); mfem::forall(nelem * nq, [=] MFEM_HOST_DEVICE (int i) { const int q = i%nq; const int sub_e = i/nq; const int e = d_indices[sub_e]; for (int d = 0; d < dim; d++) { out(d, q, sub_e) = in(d, q, e); } }); m_indices.DeleteDevice(); InitVector(ceedCoeff->coeff, ceedCoeff->coeffVector); coeff_ptr = ceedCoeff; } else { const int dim = mesh.Dimension(); QuadCoefficient *ceedCoeff = new QuadCoefficient(dim); const int nq = ir.GetNPoints(); ceedCoeff->coeff.SetSize(dim * nq * nelem); auto C = Reshape(ceedCoeff->coeff.HostWrite(), dim, nq, nelem); mfem::DenseMatrix Q_ir; for (int i = 0; i < nelem; ++i) { const int e = indices[i]; mfem::ElementTransformation &T = *mesh.GetElementTransformation(e); VQ->Eval(Q_ir, T, ir); for (int q = 0; q < nq; ++q) { for (int d = 0; d < dim; ++d) { C(d, q, i) = Q_ir(d, q); } } } InitVector(ceedCoeff->coeff, ceedCoeff->coeffVector); coeff_ptr = ceedCoeff; } } template void InitCoefficient(Coeff *Q, mfem::Mesh &mesh, const mfem::IntegrationRule &ir, int nelem, const int* indices, Coefficient *&coeff_ptr, Context &ctx) { if (indices) { InitCoefficientWithIndices(Q, mesh, ir, nelem, indices, coeff_ptr, ctx); } else { InitCoefficient(Q, mesh, ir, coeff_ptr, ctx); } } } // namespace ceed } // namespace mfem #endif #endif // MFEM_LIBCEED_COEFF