// 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 ADEXAMPLE_HPP #define ADEXAMPLE_HPP #include "mfem.hpp" #include "admfem.hpp" #include #include #include namespace mfem { /// Example: Implementation of the residual evaluation for p-Laplacian /// problem. The residual is evaluated at the integration points for PDE /// parameters vparam and state fields (derivatives with respect to x,y,z and /// primal field) stored in vector uu. template class MyResidualFunctor { public: /// The operator returns the first derivative of the energy with respect to /// all state variables. These are set in vector uu and consist of the /// derivatives with respect to x,y,z and the primal field. The derivative is /// stored in vector rr with length equal to the length of vector uu. void operator()(TParamVector &vparam, TStateVector &uu, TStateVector &rr) { MFEM_ASSERT(residual_size==4, "PLaplacianResidual residual_size should be equal to 4!"); double pp = vparam[0]; double ee = vparam[1]; double ff = vparam[2]; // The vector rr holds the gradients of the following expression: // (u_x^2+u_y^2+u_z^2+\varepsilon^2)^(p/2)-f.u, // where u_x,u_y,u_z are the gradients of the scalar field u. // The state vector is defined as uu=[u_x,u_y,u_z,u]. TDataType norm2 = uu[0] * uu[0] + uu[1] * uu[1] + uu[2] * uu[2]; TDataType tvar = pow(ee * ee + norm2, (pp - 2.0) / 2.0); rr[0] = tvar * uu[0]; rr[1] = tvar * uu[1]; rr[2] = tvar * uu[2]; rr[3] = -ff; } }; /// Defines template class (functor) for evaluating the energy of the /// p-Laplacian problem. The input parameters vparam are: vparam[0] - the /// p-Laplacian power, vparam[1] small value ensuring exciting of an unique /// solution, and vparam[2] - the distributed external input to the PDE. The /// template parameter TDataType will be replaced by the compiler with the /// appropriate AD type for automatic differentiation. The TParamVector /// represents the vector type used for the parameter vector, and TStateVector /// the vector type used for the state vector. The template parameters /// state_size and param_size provide information for the size of the state and /// the parameters vectors. template class MyEnergyFunctor { public: /// Returns the energy of a p-Laplacian for state field input provided in /// vector uu and parameters provided in vector vparam. TDataType operator()(TParamVector &vparam, TStateVector &uu) { MFEM_ASSERT(state_size==4,"MyEnergyFunctor state_size should be equal to 4!"); MFEM_ASSERT(param_size==3,"MyEnergyFunctor param_size should be equal to 3!"); double pp = vparam[0]; double ee = vparam[1]; double ff = vparam[2]; TDataType u = uu[3]; TDataType norm2 = uu[0] * uu[0] + uu[1] * uu[1] + uu[2] * uu[2]; TDataType rez = pow(ee * ee + norm2, pp / 2.0) / pp - ff * u; return rez; } }; /// Implements integrator for a p-Laplacian problem. The integrator is based on /// a class QFunction utilized for evaluating the energy, the first derivative /// (residual) and the Hessian of the energy (the Jacobian of the residual). /// The template parameter CQVectAutoDiff represents the automatically /// differentiated energy or residual implemented by the user. /// CQVectAutoDiff::VectorFunc(Vector parameters, Vector state,Vector residual) /// evaluates the residual at an integration point. /// CQVectAutoDiff::Jacobian(Vector parameters, Vector state, Matrix hessian) /// evaluates the Hessian of the energy(the Jacobian of the residual). template class pLaplaceAD : public NonlinearFormIntegrator { protected: Coefficient *pp; Coefficient *coeff; Coefficient *load; CQVectAutoDiff rdf; public: pLaplaceAD() { coeff = nullptr; pp = nullptr; load = nullptr; vparam.SetSize(3); vparam[0] = 2.0; // default power vparam[1] = 1e-8; // default epsilon vparam[2] = 1.0; // default load } pLaplaceAD(Coefficient &pp_) : pp(&pp_), coeff(nullptr), load(nullptr) { vparam.SetSize(3); vparam[0] = 2.0; // default power vparam[1] = 1e-8; // default epsilon vparam[2] = 1.0; // default load } pLaplaceAD(Coefficient &pp_, Coefficient &q, Coefficient &ld_) : pp(&pp_), coeff(&q), load(&ld_) { vparam.SetSize(3); vparam[0] = 2.0; // default power vparam[1] = 1e-8; // default epsilon vparam[2] = 1.0; // default load } virtual ~pLaplaceAD() {} virtual double GetElementEnergy(const FiniteElement &el, ElementTransformation &trans, const Vector &elfun) { double energy = 0.0; const int ndof = el.GetDof(); const int ndim = el.GetDim(); const int spaceDim = trans.GetSpaceDim(); bool square = (ndim == spaceDim); int order = 2 * el.GetOrder() + trans.OrderGrad(&el); const IntegrationRule &ir(IntRules.Get(el.GetGeomType(), order)); Vector shapef(ndof); // derivatives in isoparametric coordinates DenseMatrix dshape_iso(ndof, ndim); // derivatives in physical space DenseMatrix dshape_xyz(ndof, spaceDim); Vector grad(spaceDim); Vector uu(4); //[diff_x,diff_y,diff_z,u] uu = 0.0; // Calculates the functional/energy at an integration point. MyEnergyFunctor qfunc; double w; double detJ; for (int i = 0; i < ir.GetNPoints(); i++) { const IntegrationPoint &ip = ir.IntPoint(i); trans.SetIntPoint(&ip); w = trans.Weight(); detJ = (square ? w : w * w); w = ip.weight * w; el.CalcDShape(ip, dshape_iso); el.CalcShape(ip, shapef); // AdjugateJacobian = / adj(J), if J is square // \ adj(J^t.J).J^t, otherwise Mult(dshape_iso, trans.AdjugateJacobian(), dshape_xyz); // dshape_xyz should be divided by detJ for obtaining the real value // calculate the gradient dshape_xyz.MultTranspose(elfun, grad); // set the power if (pp != nullptr) { vparam[0] = pp->Eval(trans, ip); } // set the coefficient ensuring positiveness of the tangent matrix if (coeff != nullptr) { vparam[1] = coeff->Eval(trans, ip); } // add the contribution from the load if (load != nullptr) { vparam[2] = load->Eval(trans, ip); } // fill the values of vector uu for (int jj = 0; jj < spaceDim; jj++) { uu[jj] = grad[jj] / detJ; } uu[3] = shapef * elfun; // the energy is taken directly from the templated function energy = energy + w * qfunc(vparam,uu); } return energy; } virtual void AssembleElementVector(const FiniteElement &el, ElementTransformation &trans, const Vector &elfun, Vector &elvect) { MFEM_PERF_BEGIN("AssembleElementVector"); const int ndof = el.GetDof(); const int ndim = el.GetDim(); const int spaceDim = trans.GetSpaceDim(); int order = 2 * el.GetOrder() + trans.OrderGrad(&el); const IntegrationRule &ir(IntRules.Get(el.GetGeomType(), order)); Vector shapef(ndof); DenseMatrix dshape_iso(ndof, ndim); DenseMatrix dshape_xyz(ndof, spaceDim); Vector lvec(ndof); elvect.SetSize(ndof); elvect = 0.0; DenseMatrix B(ndof, 4); // [diff_x,diff_y,diff_z, shape] Vector uu(4); // [diff_x,diff_y,diff_z,u] Vector du(4); B = 0.0; uu = 0.0; double w; for (int i = 0; i < ir.GetNPoints(); i++) { const IntegrationPoint &ip = ir.IntPoint(i); trans.SetIntPoint(&ip); w = trans.Weight(); w = ip.weight * w; el.CalcDShape(ip, dshape_iso); el.CalcShape(ip, shapef); Mult(dshape_iso, trans.InverseJacobian(), dshape_xyz); // set the matrix B for (int jj = 0; jj < spaceDim; jj++) { B.SetCol(jj, dshape_xyz.GetColumn(jj)); } B.SetCol(3, shapef); // set the power if (pp != nullptr) { vparam[0] = pp->Eval(trans, ip); } // set the coefficient ensuring positiveness of the tangent matrix if (coeff != nullptr) { vparam[1] = coeff->Eval(trans, ip); } // add the contribution from the load if (load != nullptr) { vparam[2] = load->Eval(trans, ip); } // calculate uu B.MultTranspose(elfun, uu); // calculate derivative of the energy with respect to uu rdf.VectorFunc(vparam,uu,du); B.Mult(du, lvec); elvect.Add(w, lvec); } // end integration loop MFEM_PERF_END("AssembleElementVector"); } virtual void AssembleElementGrad(const FiniteElement &el, ElementTransformation &trans, const Vector &elfun, DenseMatrix &elmat) { MFEM_PERF_BEGIN("AssembleElementGrad"); const int ndof = el.GetDof(); const int ndim = el.GetDim(); const int spaceDim = trans.GetSpaceDim(); int order = 2 * el.GetOrder() + trans.OrderGrad(&el); const IntegrationRule &ir(IntRules.Get(el.GetGeomType(), order)); Vector shapef(ndof); DenseMatrix dshape_iso(ndof, ndim); DenseMatrix dshape_xyz(ndof, spaceDim); elmat.SetSize(ndof, ndof); elmat = 0.0; DenseMatrix B(ndof, 4); // [diff_x,diff_y,diff_z, shape] DenseMatrix A(ndof, 4); Vector uu(4); // [diff_x,diff_y,diff_z,u] DenseMatrix duu(4, 4); B = 0.0; uu = 0.0; double w; for (int i = 0; i < ir.GetNPoints(); i++) { const IntegrationPoint &ip = ir.IntPoint(i); trans.SetIntPoint(&ip); w = trans.Weight(); w = ip.weight * w; el.CalcDShape(ip, dshape_iso); el.CalcShape(ip, shapef); Mult(dshape_iso, trans.InverseJacobian(), dshape_xyz); // set the matrix B for (int jj = 0; jj < spaceDim; jj++) { B.SetCol(jj, dshape_xyz.GetColumn(jj)); } B.SetCol(3, shapef); // set the power if (pp != nullptr) { vparam[0] = pp->Eval(trans, ip); } // set the coefficient ensuring positiveness of the tangent matrix if (coeff != nullptr) { vparam[1] = coeff->Eval(trans, ip); } // add the contribution from the load if (load != nullptr) { vparam[2] = load->Eval(trans, ip); } // calculate uu B.MultTranspose(elfun, uu); // calculate derivative of the energy with respect to uu rdf.Jacobian(vparam,uu,duu); Mult(B, duu, A); AddMult_a_ABt(w, A, B, elmat); } // end integration loop MFEM_PERF_END("AssembleElementGrad"); } private: Vector vparam; // [power, epsilon, load] }; /// Implements hand-coded integrator for a p-Laplacian problem. Utilized as /// alternative for the pLaplaceAD class based on automatic differentiation. class pLaplace : public NonlinearFormIntegrator { protected: Coefficient *pp; Coefficient *coeff; Coefficient *load; public: pLaplace() { coeff = nullptr; pp = nullptr; load = nullptr; } pLaplace(Coefficient &pp_) : pp(&pp_), coeff(nullptr), load(nullptr) {} pLaplace(Coefficient &pp_, Coefficient &q, Coefficient &ld_) : pp(&pp_), coeff(&q), load(&ld_) {} virtual ~pLaplace() {} virtual double GetElementEnergy(const FiniteElement &el, ElementTransformation &trans, const Vector &elfun) { double energy = 0.0; const int ndof = el.GetDof(); const int ndim = el.GetDim(); const int spaceDim = trans.GetSpaceDim(); bool square = (ndim == spaceDim); int order = 2 * el.GetOrder() + trans.OrderGrad(&el); const IntegrationRule &ir(IntRules.Get(el.GetGeomType(), order)); Vector shapef(ndof); DenseMatrix dshape_iso(ndof, ndim); DenseMatrix dshape_xyz(ndof, spaceDim); Vector grad(spaceDim); double w; double detJ; double nrgrad2; double ppp = 2.0; double eee = 0.0; for (int i = 0; i < ir.GetNPoints(); i++) { const IntegrationPoint &ip = ir.IntPoint(i); trans.SetIntPoint(&ip); w = trans.Weight(); detJ = (square ? w : w * w); w = ip.weight * w; el.CalcDShape(ip, dshape_iso); el.CalcShape(ip, shapef); // AdjugateJacobian = / adj(J), if J is square // \ adj(J^t.J).J^t, otherwise Mult(dshape_iso, trans.AdjugateJacobian(), dshape_xyz); // dshape_xyz should be divided by detJ for obtaining the real value // calculate the gradient dshape_xyz.MultTranspose(elfun, grad); nrgrad2 = grad * grad / (detJ * detJ); // set the power if (pp != nullptr) { ppp = pp->Eval(trans, ip); } // set the coefficient ensuring positiveness of the tangent matrix if (coeff != nullptr) { eee = coeff->Eval(trans, ip); } energy = energy + w * std::pow(nrgrad2 + eee * eee, ppp / 2.0) / ppp; // add the contribution from the load if (load != nullptr) { energy = energy - w * (shapef * elfun) * load->Eval(trans, ip); } } return energy; } virtual void AssembleElementVector(const FiniteElement &el, ElementTransformation &trans, const Vector &elfun, Vector &elvect) { MFEM_PERF_BEGIN("AssembleElementVector"); const int ndof = el.GetDof(); const int ndim = el.GetDim(); const int spaceDim = trans.GetSpaceDim(); bool square = (ndim == spaceDim); int order = 2 * el.GetOrder() + trans.OrderGrad(&el); const IntegrationRule &ir(IntRules.Get(el.GetGeomType(), order)); Vector shapef(ndof); DenseMatrix dshape_iso(ndof, ndim); DenseMatrix dshape_xyz(ndof, spaceDim); Vector grad(spaceDim); Vector lvec(ndof); elvect.SetSize(ndof); elvect = 0.0; double w; double detJ; double nrgrad; double aa; double ppp = 2.0; double eee = 0.0; for (int i = 0; i < ir.GetNPoints(); i++) { const IntegrationPoint &ip = ir.IntPoint(i); trans.SetIntPoint(&ip); w = trans.Weight(); detJ = (square ? w : w * w); w = ip.weight * w; el.CalcDShape(ip, dshape_iso); el.CalcShape(ip, shapef); // AdjugateJacobian = / adj(J), if J is square // \ adj(J^t.J).J^t, otherwise Mult(dshape_iso, trans.AdjugateJacobian(), dshape_xyz); // dshape_xyz should be divided by detJ for obtaining the real value // calculate the gradient dshape_xyz.MultTranspose(elfun, grad); nrgrad = grad.Norml2() / detJ; // grad is not scaled so far, i.e., grad=grad/detJ // set the power if (pp != nullptr) { ppp = pp->Eval(trans, ip); } // set the coefficient ensuring positiveness of the tangent matrix if (coeff != nullptr) { eee = coeff->Eval(trans, ip); } // compute (norm of the gradient)^2 + epsilon^2 aa = nrgrad * nrgrad + eee * eee; aa = std::pow(aa, (ppp - 2.0) / 2.0); dshape_xyz.Mult(grad, lvec); elvect.Add(w * aa / (detJ * detJ), lvec); // add loading if (load != nullptr) { elvect.Add(-w * load->Eval(trans, ip), shapef); } } // end integration loop MFEM_PERF_END("AssembleElementVector"); } virtual void AssembleElementGrad(const FiniteElement &el, ElementTransformation &trans, const Vector &elfun, DenseMatrix &elmat) { MFEM_PERF_BEGIN("AssembleElementGrad"); const int ndof = el.GetDof(); const int ndim = el.GetDim(); const int spaceDim = trans.GetSpaceDim(); bool square = (ndim == spaceDim); int order = 2 * el.GetOrder() + trans.OrderGrad(&el); const IntegrationRule &ir(IntRules.Get(el.GetGeomType(), order)); DenseMatrix dshape_iso(ndof, ndim); DenseMatrix dshape_xyz(ndof, spaceDim); Vector grad(spaceDim); Vector lvec(ndof); // set the size of the element matrix elmat.SetSize(ndof, ndof); elmat = 0.0; double w; // integration weight double detJ; double nrgrad; // norm of the gradient double aa0; // original nonlinear diffusion coefficient double aa1; // gradient of the above double ppp = 2.0; // power in the P-Laplacian double eee = 0.0; // regularization parameter for (int i = 0; i < ir.GetNPoints(); i++) { const IntegrationPoint &ip = ir.IntPoint(i); trans.SetIntPoint(&ip); w = trans.Weight(); detJ = (square ? w : w * w); w = ip.weight * w; el.CalcDShape(ip, dshape_iso); // AdjugateJacobian = / adj(J), if J is square // \ adj(J^t.J).J^t, otherwise Mult(dshape_iso, trans.AdjugateJacobian(), dshape_xyz); // dshape_xyz should be divided by detJ for obtaining the real value // grad is not scaled so far,i.e., grad=grad/detJ // set the power if (pp != nullptr) { ppp = pp->Eval(trans, ip); } // set the coefficient ensuring positiveness of the tangent matrix if (coeff != nullptr) { eee = coeff->Eval(trans, ip); } // calculate the gradient dshape_xyz.MultTranspose(elfun, grad); nrgrad = grad.Norml2() / detJ; // (u_x^2+u_y^2+u_z^2+\varepsilon^2) aa0 = nrgrad * nrgrad + eee * eee; aa1 = std::pow(aa0, (ppp - 2.0) / 2.0); aa0 = (ppp - 2.0) * std::pow(aa0, (ppp - 4.0) / 2.0); dshape_xyz.Mult(grad, lvec); w = w / (detJ * detJ); AddMult_a_VVt(w * aa0 / (detJ * detJ), lvec, elmat); AddMult_a_AAt(w * aa1, dshape_xyz, elmat); } // end integration loop MFEM_PERF_END("AssembleElementGrad"); } }; /// Implements AD enabled integrator for a p-Laplacian problem. The tangent /// matrix is computed using the residual of the element. The template argument /// should be equal to the size of the residual vector (element vector), i.e., /// the user should specify the size to match the exact vector size for the /// considered order of the shape functions. template class pLaplaceSL : public NonlinearFormIntegrator { protected: Coefficient *pp; Coefficient *coeff; Coefficient *load; public: pLaplaceSL() { coeff = nullptr; pp = nullptr; load = nullptr; } pLaplaceSL(Coefficient &pp_) : pp(&pp_), coeff(nullptr), load(nullptr) {} pLaplaceSL(Coefficient &pp_, Coefficient &q, Coefficient &ld_) : pp(&pp_), coeff(&q), load(&ld_) {} virtual ~pLaplaceSL() {} virtual double GetElementEnergy(const FiniteElement &el, ElementTransformation &trans, const Vector &elfun) { double energy = 0.0; const int ndof = el.GetDof(); const int ndim = el.GetDim(); const int spaceDim = trans.GetSpaceDim(); bool square = (ndim == spaceDim); int order = 2 * el.GetOrder() + trans.OrderGrad(&el); const IntegrationRule &ir(IntRules.Get(el.GetGeomType(), order)); Vector shapef(ndof); DenseMatrix dshape_iso(ndof, ndim); DenseMatrix dshape_xyz(ndof, spaceDim); Vector grad(spaceDim); double w; double detJ; double nrgrad2; double ppp = 2.0; double eee = 0.0; for (int i = 0; i < ir.GetNPoints(); i++) { const IntegrationPoint &ip = ir.IntPoint(i); trans.SetIntPoint(&ip); w = trans.Weight(); detJ = (square ? w : w * w); w = ip.weight * w; el.CalcDShape(ip, dshape_iso); el.CalcShape(ip, shapef); // AdjugateJacobian = / adj(J), if J is square // \ adj(J^t.J).J^t, otherwise Mult(dshape_iso, trans.AdjugateJacobian(), dshape_xyz); // dshape_xyz should be divided by detJ for obtaining the real value // calculate the gradient dshape_xyz.MultTranspose(elfun, grad); nrgrad2 = grad * grad / (detJ * detJ); // set the power if (pp != nullptr) { ppp = pp->Eval(trans, ip); } // set the coefficient ensuring positiveness of the tangent matrix if (coeff != nullptr) { eee = coeff->Eval(trans, ip); } energy = energy + w * std::pow(nrgrad2 + eee * eee, ppp / 2.0) / ppp; // add the contribution from the load if (load != nullptr) { energy = energy - w * (shapef * elfun) * load->Eval(trans, ip); } } return energy; } virtual void AssembleElementVector(const FiniteElement &el, ElementTransformation &trans, const Vector &elfun, Vector &elvect) { MFEM_PERF_BEGIN("AssembleElementVector"); const int ndof = el.GetDof(); const int ndim = el.GetDim(); const int spaceDim = trans.GetSpaceDim(); bool square = (ndim == spaceDim); int order = 2 * el.GetOrder() + trans.OrderGrad(&el); const IntegrationRule &ir(IntRules.Get(el.GetGeomType(), order)); Vector shapef(ndof); DenseMatrix dshape_iso(ndof, ndim); DenseMatrix dshape_xyz(ndof, spaceDim); Vector grad(spaceDim); Vector lvec(ndof); elvect.SetSize(ndof); elvect = 0.0; double w; double detJ; double nrgrad; double aa; double ppp = 2.0; double eee = 0.0; for (int i = 0; i < ir.GetNPoints(); i++) { const IntegrationPoint &ip = ir.IntPoint(i); trans.SetIntPoint(&ip); w = trans.Weight(); detJ = (square ? w : w * w); w = ip.weight * w; //w; el.CalcDShape(ip, dshape_iso); el.CalcShape(ip, shapef); // AdjugateJacobian = / adj(J), if J is square // \ adj(J^t.J).J^t, otherwise Mult(dshape_iso, trans.AdjugateJacobian(), dshape_xyz); // dshape_xyz should be divided by detJ for obtaining the real value // calculate the gradient dshape_xyz.MultTranspose(elfun, grad); nrgrad = grad.Norml2() / detJ; // grad is not scaled so far, i.e., grad=grad/detJ // set the power if (pp != nullptr) { ppp = pp->Eval(trans, ip); } // set the coefficient ensuring positiveness of the tangent matrix if (coeff != nullptr) { eee = coeff->Eval(trans, ip); } aa = nrgrad * nrgrad + eee * eee; aa = std::pow(aa, (ppp - 2.0) / 2.0); dshape_xyz.Mult(grad, lvec); elvect.Add(w * aa / (detJ * detJ), lvec); // add loading if (load != nullptr) { elvect.Add(-w * load->Eval(trans, ip), shapef); } } // end integration loop MFEM_PERF_END("AssembleElementVector"); } virtual void AssembleElementGrad(const FiniteElement &el, ElementTransformation &trans, const Vector &elfun, DenseMatrix &elmat) { MFEM_PERF_BEGIN("AssembleElementGrad"); const int ndof = el.GetDof(); const int ndim = el.GetDim(); const int spaceDim = trans.GetSpaceDim(); bool square = (ndim == spaceDim); int order = 2 * el.GetOrder() + trans.OrderGrad(&el); const IntegrationRule &ir(IntRules.Get(el.GetGeomType(), order)); DenseMatrix dshape_iso(ndof, ndim); DenseMatrix dshape_xyz(ndof, spaceDim); elmat.SetSize(ndof, ndof); elmat = 0.0; double w; double detJ; double ppp = 2.0; double eee = 0.0; mfem::Vector param(3); param=0.0; // Computes the residual at an integration point. The implementation is a // copy of the integration loop in AssembleElementVector. auto resfun = [&](mfem::Vector& vparam, mfem::ad::ADVectorType& uu, mfem::ad::ADVectorType& vres) { vres.SetSize(uu.Size()); vres=0.0; mfem::ad::ADVectorType grad(spaceDim); mfem::ad::ADFloatType nrgrad; mfem::ad::ADFloatType aa; mfem::ad::ADVectorType lvec(ndof); for (int q = 0; q < ir.GetNPoints(); q++) { lvec=0.0; const IntegrationPoint &ip = ir.IntPoint(q); trans.SetIntPoint(&ip); w = trans.Weight(); detJ = (square ? w : w * w); w = ip.weight * w; el.CalcDShape(ip, dshape_iso); // AdjugateJacobian = / adj(J), if J is square // \ adj(J^t.J).J^t, otherwise Mult(dshape_iso, trans.AdjugateJacobian(), dshape_xyz); // dshape_xyz should be divided by detJ for obtaining the real value // grad is not scaled so far,i.e., grad=grad/detJ // set the power if (pp != nullptr) { ppp = pp->Eval(trans, ip); } // set the coefficient ensuring positiveness of the tangent matrix if (coeff != nullptr) { eee = coeff->Eval(trans, ip); } grad=0.0; // calculate the gradient for (int i=0; i fdr(resfun); fdr.Jacobian(param, bla, elmat); MFEM_PERF_END("AssembleElementGrad"); } }; } // namespace mfem #endif