// 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_TEMPLATE_ELEMENT_TRANSFORMATION #define MFEM_TEMPLATE_ELEMENT_TRANSFORMATION #include "../config/tconfig.hpp" #include "tevaluator.hpp" #include "../mesh/element.hpp" namespace mfem { // Templated element transformation classes, cf. eltrans.?pp /** @brief Element transformation class, templated on a mesh type and an integration rule. It is constructed from a mesh (e.g. class TMesh) and shape evaluator (e.g. class ShapeEvaluator) objects. Allows computation of physical coordinates and Jacobian matrices corresponding to the reference integration points. The desired result is specified through the template subclass Result and stored in an object of the same type. */ template class TElementTransformation { public: typedef real_t real_type; typedef typename Mesh_t::FE_type FE_type; typedef typename Mesh_t::FESpace_type FESpace_type; typedef typename Mesh_t::nodeLayout_type nodeLayout_type; typedef ShapeEvaluator ShapeEval; typedef TElementTransformation T_type; /// Enumeration for the result type of the TElementTransformation::Eval() /// method. The types can obtained by summing constants from this enumeration /// and used as a template parameter in struct Result. enum EvalOperations { EvalNone = 0, EvalCoordinates = 1, EvalJacobians = 2, LoadAttributes = 4, LoadElementIdxs = 8 }; /// Determines at compile-time the operations needed for given coefficient /// and kernel template struct Get { static const int EvalOps = (EvalCoordinates * coeff_t::uses_coordinates + EvalJacobians * coeff_t::uses_Jacobians + LoadAttributes * coeff_t::uses_attributes + LoadElementIdxs * coeff_t::uses_element_idxs) | (EvalJacobians * kernel_t::uses_Jacobians); }; /** @brief Templated struct Result, used to specify the type result that is computed by the TElementTransformation::Eval() method and stored in this structure. @tparam EvalOps is a sum (bitwise or) of constants from the enum EvalOperations @tparam NE is the number of elements to be processed in the Eval() method. @tparam impl_traits_t specifies additional parameters and types to be used by the Eval() method */ template struct Result; static const int dim = Mesh_t::dim; static const int sdim = Mesh_t::space_dim; static const int dofs = FE_type::dofs; static const int qpts = IR::qpts; protected: #ifdef MFEM_TEMPLATE_ELTRANS_HAS_NODE_DOFS TTensor3 nodes_dof; #endif ShapeEval evaluator; FESpace_type fes; nodeLayout_type node_layout; const real_t *nodes; const Element* const *elements; template inline MFEM_ALWAYS_INLINE void SetAttributes(int el, vint_t (&attrib)[NE]) const { const int vsize = sizeof(vint_t)/sizeof(attrib[0][0]); for (int i = 0; i < NE; i++) { for (int j = 0; j < vsize; i++) { attrib[i][j] = elements[el+j+i*vsize]->GetAttribute(); } } } public: // Constructor. TElementTransformation(const Mesh_t &mesh, const ShapeEval &eval) : evaluator(eval), fes(mesh.t_fes), node_layout(mesh.node_layout), nodes(mesh.Nodes.GetData()), // real_t = double elements(mesh.m_mesh.GetElementsArray()) { } /// Evaluate coordinates and/or Jacobian matrices at quadrature points. template inline MFEM_ALWAYS_INLINE void Eval(int el, Result &F) { F.Eval(el, *this); } #ifdef MFEM_TEMPLATE_ENABLE_SERIALIZE template inline MFEM_ALWAYS_INLINE void EvalSerialized(int el, const typename impl_traits_t::vreal_t *nodeData, Result &F) { F.EvalSerialized(el, *this, nodeData); } #endif // Specialization of the Result<> class // Case EvalOps = 0 = EvalNone template struct Result<0,it_t> { static const int ne = it_t::batch_size; typedef typename it_t::vreal_t vreal_t; // x_type x; // Jt_type Jt; // int attrib[NE]; // int first_elem_idx; inline MFEM_ALWAYS_INLINE void Eval(int el, T_type &T) { // T.SetAttributes(el, attrib); // first_elem_idx = el; } #ifdef MFEM_TEMPLATE_ENABLE_SERIALIZE inline MFEM_ALWAYS_INLINE void EvalSerialized(int el, T_type &T, const vreal_t *nodeData) { } #endif }; // Case EvalOps = 1 = EvalCoordinates template struct Result<1,it_t> { static const int ne = it_t::batch_size; typedef typename it_t::vreal_t vreal_t; #ifdef MFEM_TEMPLATE_ELTRANS_RESULT_HAS_NODES typedef TTensor3 x_type; #else typedef TTensor3 x_type; #endif x_type x; typedef TTensor3 nodes_dof_t; #ifdef MFEM_TEMPLATE_ELTRANS_RESULT_HAS_NODES nodes_dof_t nodes_dof; #endif inline MFEM_ALWAYS_INLINE void Eval(int el, T_type &T) { #ifdef MFEM_TEMPLATE_ELTRANS_HAS_NODE_DOFS MFEM_STATIC_ASSERT(ne == 1, "only ne == 1 is supported"); TTensor3 &nodes_dof = T.nodes_dof; #elif !defined(MFEM_TEMPLATE_ELTRANS_RESULT_HAS_NODES) nodes_dof_t nodes_dof; #endif T.fes.SetElement(el); T.fes.VectorExtract(T.node_layout, T.nodes, nodes_dof.layout, nodes_dof); T.evaluator.Calc(nodes_dof.layout.merge_23(), nodes_dof, x.layout.merge_23(), x); } #ifdef MFEM_TEMPLATE_ENABLE_SERIALIZE inline MFEM_ALWAYS_INLINE void EvalSerialized(int el, T_type &T, const vreal_t *nodeData) { const int SS = sizeof(nodeData[0])/sizeof(nodeData[0][0]); MFEM_ASSERT(el % (SS*ne) == 0, "invalid element index: " << el); T.evaluator.Calc(nodes_dof_t::layout.merge_23(), &nodeData[el/SS*nodes_dof_t::size], x.layout.merge_23(), x); } #endif }; // Case EvalOps = 2 = EvalJacobians template struct Result<2,it_t> { static const int ne = it_t::batch_size; typedef typename it_t::vreal_t vreal_t; #ifdef MFEM_TEMPLATE_ELTRANS_RESULT_HAS_NODES typedef TTensor4 Jt_type; #else typedef TTensor4 Jt_type; #endif Jt_type Jt; typedef TTensor3 nodes_dof_t; #ifdef MFEM_TEMPLATE_ELTRANS_RESULT_HAS_NODES nodes_dof_t nodes_dof; #endif inline MFEM_ALWAYS_INLINE void Eval(int el, T_type &T) { #ifdef MFEM_TEMPLATE_ELTRANS_HAS_NODE_DOFS MFEM_STATIC_ASSERT(ne == 1, "only ne == 1 is supported"); TTensor3 &nodes_dof = T.nodes_dof; #elif !defined(MFEM_TEMPLATE_ELTRANS_RESULT_HAS_NODES) nodes_dof_t nodes_dof; #endif T.fes.SetElement(el); T.fes.VectorExtract(T.node_layout, T.nodes, nodes_dof.layout, nodes_dof); T.evaluator.CalcGrad(nodes_dof.layout.merge_23(), nodes_dof, Jt.layout.merge_34(), Jt); } #ifdef MFEM_TEMPLATE_ENABLE_SERIALIZE inline MFEM_ALWAYS_INLINE void EvalSerialized(int el, T_type &T, const vreal_t *nodeData) { const int SS = sizeof(nodeData[0])/sizeof(nodeData[0][0]); MFEM_ASSERT(el % (SS*ne) == 0, "invalid element index: " << el); T.evaluator.CalcGrad(nodes_dof_t::layout.merge_23(), &nodeData[el/SS*nodes_dof_t::size], Jt.layout.merge_34(), Jt); } #endif }; // Case EvalOps = 3 = EvalCoordinates|EvalJacobians template struct Result<3,it_t> { static const int ne = it_t::batch_size; typedef typename it_t::vreal_t vreal_t; typedef TTensor3 x_type; x_type x; #ifdef MFEM_TEMPLATE_ELTRANS_RESULT_HAS_NODES typedef TTensor4 Jt_type; #else typedef TTensor4 Jt_type; #endif Jt_type Jt; typedef TTensor3 nodes_dof_t; #ifdef MFEM_TEMPLATE_ELTRANS_RESULT_HAS_NODES nodes_dof_t nodes_dof; #endif inline MFEM_ALWAYS_INLINE void Eval(int el, T_type &T) { #ifdef MFEM_TEMPLATE_ELTRANS_HAS_NODE_DOFS MFEM_STATIC_ASSERT(ne == 1, "only ne == 1 is supported"); TTensor3 &nodes_dof = T.nodes_dof; #elif !defined(MFEM_TEMPLATE_ELTRANS_RESULT_HAS_NODES) nodes_dof_t nodes_dof; #endif T.fes.SetElement(el); T.fes.VectorExtract(T.node_layout, T.nodes, nodes_dof.layout, nodes_dof); T.evaluator.Calc(nodes_dof.layout.merge_23(), nodes_dof, x.layout.merge_23(), x); T.evaluator.CalcGrad(nodes_dof.layout.merge_23(), nodes_dof, Jt.layout.merge_34(), Jt); } #ifdef MFEM_TEMPLATE_ENABLE_SERIALIZE inline MFEM_ALWAYS_INLINE void EvalSerialized(int el, T_type &T, const vreal_t *nodeData) { const int SS = sizeof(nodeData[0])/sizeof(nodeData[0][0]); MFEM_ASSERT(el % (SS*ne) == 0, "invalid element index: " << el); T.evaluator.Calc(nodes_dof_t::layout.merge_23(), &nodeData[el/SS*nodes_dof_t::size], x.layout.merge_23(), x); T.evaluator.CalcGrad(nodes_dof_t::layout.merge_23(), &nodeData[el/SS*nodes_dof_t::size], Jt.layout.merge_34(), Jt); } #endif }; // Case EvalOps = 6 = EvalJacobians|LoadAttributes template struct Result<6,it_t> { static const int ne = it_t::batch_size; typedef typename it_t::vreal_t vreal_t; typedef typename it_t::vint_t vint_t; #ifdef MFEM_TEMPLATE_ELTRANS_RESULT_HAS_NODES typedef TTensor4 Jt_type; #else typedef TTensor4 Jt_type; #endif Jt_type Jt; typedef TTensor3 nodes_dof_t; #ifdef MFEM_TEMPLATE_ELTRANS_RESULT_HAS_NODES nodes_dof_t nodes_dof; #endif vint_t attrib[ne]; inline MFEM_ALWAYS_INLINE void Eval(int el, T_type &T) { #ifdef MFEM_TEMPLATE_ELTRANS_HAS_NODE_DOFS MFEM_STATIC_ASSERT(ne == 1, "only ne == 1 is supported"); TTensor3 &nodes_dof = T.nodes_dof; #elif !defined(MFEM_TEMPLATE_ELTRANS_RESULT_HAS_NODES) nodes_dof_t nodes_dof; #endif T.fes.SetElement(el); T.fes.VectorExtract(T.node_layout, T.nodes, nodes_dof.layout, nodes_dof); T.evaluator.CalcGrad(nodes_dof.layout.merge_23(), nodes_dof, Jt.layout.merge_34(), Jt); T.SetAttributes(el, attrib); } #ifdef MFEM_TEMPLATE_ENABLE_SERIALIZE inline MFEM_ALWAYS_INLINE void EvalSerialized(int el, T_type &T, const vreal_t *nodeData) { const int SS = sizeof(nodeData[0])/sizeof(nodeData[0][0]); MFEM_ASSERT(el % (SS*ne) == 0, "invalid element index: " << el); T.evaluator.CalcGrad(nodes_dof_t::layout.merge_23(), &nodeData[el/SS*nodes_dof_t::size], Jt.layout.merge_34(), Jt); T.SetAttributes(el, attrib); } #endif }; // Case EvalOps = 10 = EvalJacobians|LoadElementIdxs template struct Result<10,it_t> { static const int ne = it_t::batch_size; typedef typename it_t::vreal_t vreal_t; #ifdef MFEM_TEMPLATE_ELTRANS_RESULT_HAS_NODES typedef TTensor4 Jt_type; #else typedef TTensor4 Jt_type; #endif Jt_type Jt; typedef TTensor3 nodes_dof_t; #ifdef MFEM_TEMPLATE_ELTRANS_RESULT_HAS_NODES nodes_dof_t nodes_dof; #endif int first_elem_idx; inline MFEM_ALWAYS_INLINE void Eval(int el, T_type &T) { #ifdef MFEM_TEMPLATE_ELTRANS_HAS_NODE_DOFS MFEM_STATIC_ASSERT(ne == 1, "only ne == 1 is supported"); TTensor3 &nodes_dof = T.nodes_dof; #elif !defined(MFEM_TEMPLATE_ELTRANS_RESULT_HAS_NODES) nodes_dof_t nodes_dof; #endif T.fes.SetElement(el); T.fes.VectorExtract(T.node_layout, T.nodes, nodes_dof.layout, nodes_dof); T.evaluator.CalcGrad(nodes_dof.layout.merge_23(), nodes_dof, Jt.layout.merge_34(), Jt); first_elem_idx = el; } #ifdef MFEM_TEMPLATE_ENABLE_SERIALIZE inline MFEM_ALWAYS_INLINE void EvalSerialized(int el, T_type &T, const vreal_t *nodeData) { const int SS = sizeof(nodeData[0])/sizeof(nodeData[0][0]); MFEM_ASSERT(el % (SS*ne) == 0, "invalid element index: " << el); T.evaluator.CalcGrad(nodes_dof_t::layout.merge_23(), &nodeData[el/SS*nodes_dof_t::size], Jt.layout.merge_34(), Jt); first_elem_idx = el; } #endif }; }; } // namespace mfem #endif // MFEM_TEMPLATE_ELEMENT_TRANSFORMATION