// 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_EVALUATOR #define MFEM_TEMPLATE_EVALUATOR #include "../config/tconfig.hpp" #include "../linalg/ttensor.hpp" #include "../general/error.hpp" #include "fespace.hpp" namespace mfem { // Templated classes for transitioning between degrees of freedom and quadrature // points values. /** @brief Shape evaluators -- values of basis functions on the reference element @tparam FE some form of TFiniteElement, probably got from TMesh::FE_type @tparam IR some form of TIntegrationRule @tparam TP tensor product or not @tparam real_t data type for mesh nodes, solution basis, mesh basis */ template class ShapeEvaluator_base; /// ShapeEvaluator without tensor-product structure template class ShapeEvaluator_base { public: static const int DOF = FE::dofs; static const int NIP = IR::qpts; static const int DIM = FE::dim; protected: TMatrix B; TMatrix Bt; TTensor3 G; TTensor3 Gt; public: ShapeEvaluator_base(const FE &fe) { fe.CalcShapes(IR::GetIntRule(), B.data, G.data); TAssign(Bt.layout, Bt, B.layout.transpose_12(), B); TAssign(Gt.layout.merge_23(), Gt, G.layout.merge_12().transpose_12(), G); } // default copy constructor /** @brief Multi-component shape evaluation from DOFs to quadrature points. dof_layout is (DOF x NumComp) and qpt_layout is (NIP x NumComp). */ template inline MFEM_ALWAYS_INLINE void Calc(const dof_layout_t &dof_layout, const dof_data_t &dof_data, const qpt_layout_t &qpt_layout, qpt_data_t &qpt_data) const { MFEM_STATIC_ASSERT(dof_layout_t::rank == 2 && dof_layout_t::dim_1 == DOF, "invalid dof_layout_t."); MFEM_STATIC_ASSERT(qpt_layout_t::rank == 2 && qpt_layout_t::dim_1 == NIP, "invalid qpt_layout_t."); MFEM_STATIC_ASSERT(dof_layout_t::dim_2 == qpt_layout_t::dim_2, "incompatible dof- and qpt- layouts."); Mult_AB(B.layout, B, dof_layout, dof_data, qpt_layout, qpt_data); } /** @brief Multi-component shape evaluation transpose from quadrature points to DOFs. qpt_layout is (NIP x NumComp) and dof_layout is (DOF x NumComp). */ template inline MFEM_ALWAYS_INLINE void CalcT(const qpt_layout_t &qpt_layout, const qpt_data_t &qpt_data, const dof_layout_t &dof_layout, dof_data_t &dof_data) const { MFEM_STATIC_ASSERT(dof_layout_t::rank == 2 && dof_layout_t::dim_1 == DOF, "invalid dof_layout_t."); MFEM_STATIC_ASSERT(qpt_layout_t::rank == 2 && qpt_layout_t::dim_1 == NIP, "invalid qpt_layout_t."); MFEM_STATIC_ASSERT(dof_layout_t::dim_2 == qpt_layout_t::dim_2, "incompatible dof- and qpt- layouts."); Mult_AB(Bt.layout, Bt, qpt_layout, qpt_data, dof_layout, dof_data); } /** @brief Multi-component gradient evaluation from DOFs to quadrature points. dof_layout is (DOF x NumComp) and grad_layout is (NIP x DIM x NumComp). */ template inline MFEM_ALWAYS_INLINE void CalcGrad(const dof_layout_t &dof_layout, const dof_data_t &dof_data, const grad_layout_t &grad_layout, grad_data_t &grad_data) const { MFEM_STATIC_ASSERT(dof_layout_t::rank == 2 && dof_layout_t::dim_1 == DOF, "invalid dof_layout_t."); MFEM_STATIC_ASSERT(grad_layout_t::rank == 3 && grad_layout_t::dim_1 == NIP && grad_layout_t::dim_2 == DIM, "invalid grad_layout_t."); MFEM_STATIC_ASSERT(dof_layout_t::dim_2 == grad_layout_t::dim_3, "incompatible dof- and grad- layouts."); Mult_AB(G.layout.merge_12(), G, dof_layout, dof_data, grad_layout.merge_12(), grad_data); } /** @brief Multi-component gradient evaluation transpose from quadrature points to DOFs. grad_layout is (NIP x DIM x NumComp), dof_layout is (DOF x NumComp). */ template inline MFEM_ALWAYS_INLINE void CalcGradT(const grad_layout_t &grad_layout, const grad_data_t &grad_data, const dof_layout_t &dof_layout, dof_data_t &dof_data) const { MFEM_STATIC_ASSERT(dof_layout_t::rank == 2 && dof_layout_t::dim_1 == DOF, "invalid dof_layout_t."); MFEM_STATIC_ASSERT(grad_layout_t::rank == 3 && grad_layout_t::dim_1 == NIP && grad_layout_t::dim_2 == DIM, "invalid grad_layout_t."); MFEM_STATIC_ASSERT(dof_layout_t::dim_2 == grad_layout_t::dim_3, "incompatible dof- and grad- layouts."); Mult_AB(Gt.layout.merge_23(), Gt, grad_layout.merge_12(), grad_data, dof_layout, dof_data); } /** @brief Multi-component assemble. qpt_layout is (NIP x NumComp), M_layout is (DOF x DOF x NumComp) */ template inline MFEM_ALWAYS_INLINE void Assemble(const qpt_layout_t &qpt_layout, const qpt_data_t &qpt_data, const M_layout_t &M_layout, M_data_t &M_data) const { // M_{i,j,k} = \sum_{s} B_{s,i} B_{s,j} qpt_data_{s,k} // Using TensorAssemble: <1,NIP,NC> --> #if 0 TensorAssemble( B.layout, B, qpt_layout.template split_1<1,NIP>(), qpt_data, M_layout.template split_1(), M_data); #else TensorAssemble( Bt.layout, Bt, B.layout, B, qpt_layout.template split_1<1,NIP>(), qpt_data, M_layout.template split_1(), M_data); #endif } /** @brief Multi-component assemble of grad-grad element matrices. qpt_layout is (NIP x DIM x DIM x NumComp), and D_layout is (DOF x DOF x NumComp). */ template inline MFEM_ALWAYS_INLINE void AssembleGradGrad(const qpt_layout_t &qpt_layout, const qpt_data_t &qpt_data, const D_layout_t &D_layout, D_data_t &D_data) const { const int NC = qpt_layout_t::dim_4; typedef typename qpt_data_t::data_type entry_type; TTensor4 F; for (int k = 0; k < NC; k++) { // Next loop performs a batch of matrix-matrix products of size // (DIM x DIM) x (DIM x DOF) --> (DIM x DOF) for (int j = 0; j < NIP; j++) { Mult_AB(qpt_layout.ind14(j,k), qpt_data, G.layout.ind1(j), G, F.layout.ind14(j,k), F); } } // (DOF x (NIP x DIM)) x ((NIP x DIM) x DOF x NC) --> (DOF x DOF x NC) Mult_2_1(Gt.layout.merge_23(), Gt, F.layout.merge_12(), F, D_layout, D_data); } }; template class TProductShapeEvaluator; /// ShapeEvaluator with 1D tensor-product structure template class TProductShapeEvaluator<1, DOF, NIP, real_t> { protected: static const int TDOF = DOF; // total dofs TMatrix B_1d, G_1d; TMatrix Bt_1d, Gt_1d; public: TProductShapeEvaluator() { } /** @brief Multi-component shape evaluation from DOFs to quadrature points. dof_layout is (DOF x NumComp) and qpt_layout is (NIP x NumComp). */ template inline MFEM_ALWAYS_INLINE void Calc(const dof_layout_t &dof_layout, const dof_data_t &dof_data, const qpt_layout_t &qpt_layout, qpt_data_t &qpt_data) const { Mult_AB(B_1d.layout, B_1d, dof_layout, dof_data, qpt_layout, qpt_data); } /** @brief Multi-component shape evaluation transpose from quadrature points to DOFs. qpt_layout is (NIP x NumComp) and dof_layout is (DOF x NumComp). */ template inline MFEM_ALWAYS_INLINE void CalcT(const qpt_layout_t &qpt_layout, const qpt_data_t &qpt_data, const dof_layout_t &dof_layout, dof_data_t &dof_data) const { Mult_AB(Bt_1d.layout, Bt_1d, qpt_layout, qpt_data, dof_layout, dof_data); } /** @brief Multi-component gradient evaluation from DOFs to quadrature points. dof_layout is (DOF x NumComp) and grad_layout is (NIP x DIM x NumComp). */ template inline MFEM_ALWAYS_INLINE void CalcGrad(const dof_layout_t &dof_layout, const dof_data_t &dof_data, const grad_layout_t &grad_layout, grad_data_t &grad_data) const { // grad_data(nip,dim,comp) = sum(dof) G(nip,dim,dof) * dof_data(dof,comp) Mult_AB(G_1d.layout, G_1d, dof_layout, dof_data, grad_layout.merge_12(), grad_data); } /** @brief Multi-component gradient evaluation transpose from quadrature points to DOFs. grad_layout is (NIP x DIM x NumComp), dof_layout is (DOF x NumComp). */ template inline MFEM_ALWAYS_INLINE void CalcGradT(const grad_layout_t &grad_layout, const grad_data_t &grad_data, const dof_layout_t &dof_layout, dof_data_t &dof_data) const { // dof_data(dof,comp) += // sum(nip,dim) G(nip,dim,dof) * grad_data(nip,dim,comp) Mult_AB(Gt_1d.layout, Gt_1d, grad_layout.merge_12(), grad_data, dof_layout, dof_data); } /** @brief Multi-component assemble. qpt_layout is (NIP x NumComp), M_layout is (DOF x DOF x NumComp) */ template inline MFEM_ALWAYS_INLINE void Assemble(const qpt_layout_t &qpt_layout, const qpt_data_t &qpt_data, const M_layout_t &M_layout, M_data_t &M_data) const { // M_{i,j,k} = \sum_{s} B_1d_{s,i} B_{s,j} qpt_data_{s,k} // Using TensorAssemble: <1,NIP,NC> --> #if 0 TensorAssemble( B_1d.layout, B_1d, qpt_layout.template split_1<1,NIP>(), qpt_data, M_layout.template split_1(), M_data); #else TensorAssemble( Bt_1d.layout, Bt_1d, B_1d.layout, B_1d, qpt_layout.template split_1<1,NIP>(), qpt_data, M_layout.template split_1(), M_data); #endif } /** @brief Multi-component assemble of grad-grad element matrices. qpt_layout is (NIP x DIM x DIM x NumComp), and D_layout is (DOF x DOF x NumComp). */ template inline MFEM_ALWAYS_INLINE void AssembleGradGrad(const qpt_layout_t &qpt_layout, const qpt_data_t &qpt_data, const D_layout_t &D_layout, D_data_t &D_data) const { // D_{i,j,k} = \sum_{s} G_1d_{s,i} G_{s,j} qpt_data_{s,k} // Using TensorAssemble: <1,NIP,NC> --> #if 0 TensorAssemble( G_1d.layout, G_1d, qpt_layout.merge_12().merge_23().template split_1<1,NIP>(), qpt_data, D_layout.template split_1(), D_data); #else TensorAssemble( Gt_1d.layout, Gt_1d, G_1d.layout, G_1d, qpt_layout.merge_12().merge_23().template split_1<1,NIP>(), qpt_data, D_layout.template split_1(), D_data); #endif } }; /// ShapeEvaluator with 2D tensor-product structure template class TProductShapeEvaluator<2, DOF, NIP, real_t> { protected: TMatrix B_1d, G_1d; TMatrix Bt_1d, Gt_1d; public: static const int TDOF = DOF*DOF; // total dofs static const int TNIP = NIP*NIP; // total qpts TProductShapeEvaluator() { } template inline MFEM_ALWAYS_INLINE void Calc(const dof_layout_t &dof_layout, const dof_data_t &dof_data, const qpt_layout_t &qpt_layout, qpt_data_t &qpt_data) const { const int NC = dof_layout_t::dim_2; typedef typename qpt_data_t::data_type entry_type; // DOF x DOF x NC --> NIP x DOF x NC --> NIP x NIP x NC TTensor3 A; // (1) A_{i,j,k} = \sum_s B_1d_{i,s} dof_data_{s,j,k} Mult_2_1(B_1d.layout, Dx ? G_1d : B_1d, dof_layout. template split_1(), dof_data, A.layout, A); // (2) qpt_data_{i,j,k} = \sum_s B_1d_{j,s} A_{i,s,k} Mult_1_2(Bt_1d.layout, Dy ? Gt_1d : Bt_1d, A.layout, A, qpt_layout.template split_1(), qpt_data); } /** @brief Multi-component shape evaluation from DOFs to quadrature points. dof_layout is (TDOF x NumComp) and qpt_layout is (TNIP x NumComp). */ template inline MFEM_ALWAYS_INLINE void Calc(const dof_layout_t &dof_layout, const dof_data_t &dof_data, const qpt_layout_t &qpt_layout, qpt_data_t &qpt_data) const { Calc(dof_layout, dof_data, qpt_layout, qpt_data); } template inline MFEM_ALWAYS_INLINE void CalcT(const qpt_layout_t &qpt_layout, const qpt_data_t &qpt_data, const dof_layout_t &dof_layout, dof_data_t &dof_data) const { const int NC = dof_layout_t::dim_2; typedef typename qpt_data_t::data_type entry_type; // NIP x NIP X NC --> NIP x DOF x NC --> DOF x DOF x NC TTensor3 A; // (1) A_{i,j,k} = \sum_s B_1d_{s,j} qpt_data_{i,s,k} Mult_1_2(B_1d.layout, Dy ? G_1d : B_1d, qpt_layout.template split_1(), qpt_data, A.layout, A); // (2) dof_data_{i,j,k} = \sum_s B_1d_{s,i} A_{s,j,k} Mult_2_1(Bt_1d.layout, Dx ? Gt_1d : Bt_1d, A.layout, A, dof_layout.template split_1(), dof_data); } /** @brief Multi-component shape evaluation transpose from quadrature points to DOFs. qpt_layout is (TNIP x NumComp) and dof_layout is (TDOF x NumComp). */ template inline MFEM_ALWAYS_INLINE void CalcT(const qpt_layout_t &qpt_layout, const qpt_data_t &qpt_data, const dof_layout_t &dof_layout, dof_data_t &dof_data) const { CalcT(qpt_layout, qpt_data, dof_layout, dof_data); } /** @brief Multi-component gradient evaluation from DOFs to quadrature points. dof_layout is (TDOF x NumComp) and grad_layout is (TNIP x DIM x NumComp). */ template inline MFEM_ALWAYS_INLINE void CalcGrad(const dof_layout_t &dof_layout, const dof_data_t &dof_data, const grad_layout_t &grad_layout, grad_data_t &grad_data) const { Calc(dof_layout, dof_data, grad_layout.ind2(0), grad_data); Calc(dof_layout, dof_data, grad_layout.ind2(1), grad_data); } /** @brief Multi-component gradient evaluation transpose from quadrature points to DOFs. grad_layout is (TNIP x DIM x NumComp), dof_layout is (TDOF x NumComp). */ template inline MFEM_ALWAYS_INLINE void CalcGradT(const grad_layout_t &grad_layout, const grad_data_t &grad_data, const dof_layout_t &dof_layout, dof_data_t &dof_data) const { CalcT(grad_layout.ind2(0), grad_data, dof_layout, dof_data); CalcT(grad_layout.ind2(1), grad_data, dof_layout, dof_data); } /** @brief Multi-component assemble. qpt_layout is (TNIP x NumComp), M_layout is (TDOF x TDOF x NumComp) */ template inline MFEM_ALWAYS_INLINE void Assemble(const qpt_layout_t &qpt_layout, const qpt_data_t &qpt_data, const M_layout_t &M_layout, M_data_t &M_data) const { const int NC = qpt_layout_t::dim_2; typedef typename qpt_data_t::data_type entry_type; // Using TensorAssemble: --> #if 0 TTensor4 A; // qpt_data --> A TensorAssemble( B_1d.layout, B_1d, qpt_layout.template split_1(), qpt_data, A.layout, A); // A --> M TensorAssemble( B_1d.layout, B_1d, TTensor3::layout, A, M_layout.merge_23().template split_12(), M_data); #elif 1 TTensor4 A; // qpt_data --> A TensorAssemble( Bt_1d.layout, Bt_1d, B_1d.layout, B_1d, qpt_layout.template split_1(), qpt_data, A.layout, A); // A --> M TensorAssemble( Bt_1d.layout, Bt_1d, B_1d.layout, B_1d, A.layout.merge_34(), A, M_layout.merge_23().template split_12(), M_data); #else TTensor3 F3; TTensor4 F4; TTensor3 H3; for (int k = 0; k < NC; k++) { // <1,NIP1,NIP2> --> <1,NIP1,NIP2,DOF1> TensorProduct( qpt_layout.ind2(k).template split_1(). template split_1<1,NIP>(), qpt_data, B_1d.layout, B_1d, F3.layout.template split_1<1,NIP>(), F3); // --> TensorProduct( F3.layout, F3, B_1d.layout, B_1d, F4.layout, F4); // --> Mult_1_2(B_1d.layout, B_1d, F4.layout.merge_34(), F4, H3.layout, H3); // --> Mult_2_1(Bt_1d.layout, Bt_1d, H3.layout, H3, M_layout.ind3(k).template split_1(), M_data); } #endif } template inline MFEM_ALWAYS_INLINE void Assemble(const qpt_layout_t &qpt_layout, const qpt_data_t &qpt_data, const D_layout_t &D_layout, D_data_t &D_data) const { const int NC = qpt_layout_t::dim_2; typedef typename qpt_data_t::data_type entry_type; TTensor4 A; // Using TensorAssemble: --> // qpt_data --> A TensorAssemble( Bt_1d.layout, D1 == 0 ? Bt_1d : Gt_1d, B_1d.layout, D2 == 0 ? B_1d : G_1d, qpt_layout.template split_1(), qpt_data, A.layout, A); // A --> M TensorAssemble( Bt_1d.layout, D1 == 1 ? Bt_1d : Gt_1d, B_1d.layout, D2 == 1 ? B_1d : G_1d, A.layout.merge_34(), A, D_layout.merge_23().template split_12(), D_data); } /** @brief Multi-component assemble of grad-grad element matrices. qpt_layout is (TNIP x DIM x DIM x NumComp), and D_layout is (TDOF x TDOF x NumComp). */ template inline MFEM_ALWAYS_INLINE void AssembleGradGrad(const qpt_layout_t &qpt_layout, const qpt_data_t &qpt_data, const D_layout_t &D_layout, D_data_t &D_data) const { #if 1 Assemble<0,0,false>(qpt_layout.ind23(0,0), qpt_data, D_layout, D_data); Assemble<1,0,true >(qpt_layout.ind23(1,0), qpt_data, D_layout, D_data); Assemble<0,1,true >(qpt_layout.ind23(0,1), qpt_data, D_layout, D_data); Assemble<1,1,true >(qpt_layout.ind23(1,1), qpt_data, D_layout, D_data); #else const int NC = qpt_layout_t::dim_4; TTensor3 F3; TTensor4 F4; TTensor3 H3; for (int k = 0; k < NC; k++) { for (int d1 = 0; d1 < 2; d1++) { const AssignOp::Type Set = AssignOp::Set; const AssignOp::Type Add = AssignOp::Add; // <1,NIP1,NIP2> --> <1,NIP1,NIP2,DOF1> TensorProduct(qpt_layout.ind23(d1,0).ind2(k). template split_1(). template split_1<1,NIP>(), qpt_data, G_1d.layout, G_1d, F3.layout.template split_1<1,NIP>(), F3); // --> TensorProduct(F3.layout, F3, B_1d.layout, B_1d, F4.layout, F4); // <1,NIP1,NIP2> --> <1,NIP1,NIP2,DOF1> TensorProduct(qpt_layout.ind23(d1,1).ind2(k). template split_1(). template split_1<1,NIP>(), qpt_data, B_1d.layout, B_1d, F3.layout.template split_1<1,NIP>(), F3); // --> TensorProduct(F3.layout, F3, G_1d.layout, G_1d, F4.layout, F4); Mult_1_2(B_1d.layout, d1 == 0 ? B_1d : G_1d, F4.layout.merge_34(), F4, H3.layout, H3); if (d1 == 0) { Mult_2_1(Bt_1d.layout, Gt_1d, H3.layout, H3, D_layout.ind3(k).template split_1(), D_data); } else { Mult_2_1(Bt_1d.layout, Bt_1d, H3.layout, H3, D_layout.ind3(k).template split_1(), D_data); } } } #endif } }; /// ShapeEvaluator with 3D tensor-product structure template class TProductShapeEvaluator<3, DOF, NIP, real_t> { protected: TMatrix B_1d, G_1d; TMatrix Bt_1d, Gt_1d; public: static const int TDOF = DOF*DOF*DOF; // total dofs static const int TNIP = NIP*NIP*NIP; // total qpts TProductShapeEvaluator() { } template inline MFEM_ALWAYS_INLINE void Calc(const dof_layout_t &dof_layout, const dof_data_t &dof_data, const qpt_layout_t &qpt_layout, qpt_data_t &qpt_data) const { const int NC = dof_layout_t::dim_2; typedef typename qpt_data_t::data_type entry_type; TVector QDD; TVector QQD; // QDD_{i,jj,k} = \sum_s B_1d_{i,s} dof_data_{s,jj,k} Mult_2_1(B_1d.layout, Dx ? G_1d : B_1d, dof_layout.template split_1(), dof_data, TTensor3::layout, QDD); // QQD_{i,j,kk} = \sum_s B_1d_{j,s} QDD_{i,s,kk} Mult_1_2(Bt_1d.layout, Dy ? Gt_1d : Bt_1d, TTensor3::layout, QDD, TTensor3::layout, QQD); // qpt_data_{ii,j,k} = \sum_s B_1d_{j,s} QQD_{ii,s,k} Mult_1_2(Bt_1d.layout, Dz ? Gt_1d : Bt_1d, TTensor3::layout, QQD, qpt_layout.template split_1(), qpt_data); } /** @brief Multi-component shape evaluation from DOFs to quadrature points. dof_layout is (TDOF x NumComp) and qpt_layout is (TNIP x NumComp). */ template inline MFEM_ALWAYS_INLINE void Calc(const dof_layout_t &dof_layout, const dof_data_t &dof_data, const qpt_layout_t &qpt_layout, qpt_data_t &qpt_data) const { Calc(dof_layout, dof_data, qpt_layout, qpt_data); } template inline MFEM_ALWAYS_INLINE void CalcT(const qpt_layout_t &qpt_layout, const qpt_data_t &qpt_data, const dof_layout_t &dof_layout, dof_data_t &dof_data) const { const int NC = dof_layout_t::dim_2; typedef typename qpt_data_t::data_type entry_type; TVector QDD; TVector QQD; // QQD_{ii,j,k} = \sum_s B_1d_{s,j} qpt_data_{ii,s,k} Mult_1_2(B_1d.layout, Dz ? G_1d : B_1d, qpt_layout.template split_1(), qpt_data, TTensor3::layout, QQD); // QDD_{i,j,kk} = \sum_s B_1d_{s,j} QQD_{i,s,kk} Mult_1_2(B_1d.layout, Dy ? G_1d : B_1d, TTensor3::layout, QQD, TTensor3::layout, QDD); // dof_data_{i,jj,k} = \sum_s B_1d_{s,i} QDD_{s,jj,k} Mult_2_1(Bt_1d.layout, Dx ? Gt_1d : Bt_1d, TTensor3::layout, QDD, dof_layout.template split_1(), dof_data); } /** @brief Multi-component shape evaluation transpose from quadrature points to DOFs. qpt_layout is (TNIP x NumComp) and dof_layout is (TDOF x NumComp). */ template inline MFEM_ALWAYS_INLINE void CalcT(const qpt_layout_t &qpt_layout, const qpt_data_t &qpt_data, const dof_layout_t &dof_layout, dof_data_t &dof_data) const { CalcT(qpt_layout, qpt_data, dof_layout, dof_data); } /** @brief Multi-component gradient evaluation from DOFs to quadrature points. dof_layout is (TDOF x NumComp) and grad_layout is (TNIP x DIM x NumComp). */ template inline MFEM_ALWAYS_INLINE void CalcGrad(const dof_layout_t &dof_layout, const dof_data_t &dof_data, const grad_layout_t &grad_layout, grad_data_t &grad_data) const { Calc(dof_layout, dof_data, grad_layout.ind2(0), grad_data); Calc(dof_layout, dof_data, grad_layout.ind2(1), grad_data); Calc(dof_layout, dof_data, grad_layout.ind2(2), grad_data); // optimization: the x-transition (dof->nip) is done twice -- once for the // y-derivatives and second time for the z-derivatives. } /** @brief Multi-component gradient evaluation transpose from quadrature points to DOFs. grad_layout is (TNIP x DIM x NumComp), dof_layout is (TDOF x NumComp). */ template inline MFEM_ALWAYS_INLINE void CalcGradT(const grad_layout_t &grad_layout, const grad_data_t &grad_data, const dof_layout_t &dof_layout, dof_data_t &dof_data) const { CalcT(grad_layout.ind2(0), grad_data, dof_layout, dof_data); CalcT(grad_layout.ind2(1), grad_data, dof_layout, dof_data); CalcT(grad_layout.ind2(2), grad_data, dof_layout, dof_data); } /** @brief Multi-component assemble. qpt_layout is (TNIP x NumComp), M_layout is (TDOF x TDOF x NumComp) */ template inline MFEM_ALWAYS_INLINE void Assemble(const qpt_layout_t &qpt_layout, const qpt_data_t &qpt_data, const M_layout_t &M_layout, M_data_t &M_data) const { const int NC = qpt_layout_t::dim_2; typedef typename qpt_data_t::data_type entry_type; TTensor4 A1; TTensor4 A2; // Using TensorAssemble: --> #if 0 // qpt_data --> A1 TensorAssemble( B_1d.layout, B_1d, qpt_layout.template split_1(), qpt_data, A1.layout, A1); // A1 --> A2 TensorAssemble( B_1d.layout, B_1d, TTensor3::layout, A1, A2.layout, A2); // A2 --> M TensorAssemble( B_1d.layout, B_1d, TTensor3::layout, A2, M_layout.merge_23().template split_12(), M_data); #else // qpt_data --> A1 TensorAssemble( Bt_1d.layout, Bt_1d, B_1d.layout, B_1d, qpt_layout.template split_1(), qpt_data, A1.layout, A1); // A1 --> A2 TensorAssemble( Bt_1d.layout, Bt_1d, B_1d.layout, B_1d, TTensor3::layout, A1, A2.layout, A2); // A2 --> M TensorAssemble( Bt_1d.layout, Bt_1d, B_1d.layout, B_1d, TTensor3::layout, A2, M_layout.merge_23().template split_12(), M_data); #endif } template inline MFEM_ALWAYS_INLINE void Assemble(const qpt_layout_t &qpt_layout, const qpt_data_t &qpt_data, const D_layout_t &D_layout, D_data_t &D_data) const { const int NC = qpt_layout_t::dim_2; typedef typename qpt_data_t::data_type entry_type; TTensor4 A1; TTensor4 A2; // Using TensorAssemble: --> // qpt_data --> A1 TensorAssemble( Bt_1d.layout, D1 != 2 ? Bt_1d : Gt_1d, B_1d.layout, D2 != 2 ? B_1d : G_1d, qpt_layout.template split_1(), qpt_data, A1.layout, A1); // A1 --> A2 TensorAssemble( Bt_1d.layout, D1 != 1 ? Bt_1d : Gt_1d, B_1d.layout, D2 != 1 ? B_1d : G_1d, TTensor3::layout, A1, A2.layout, A2); // A2 --> M TensorAssemble( Bt_1d.layout, D1 != 0 ? Bt_1d : Gt_1d, B_1d.layout, D2 != 0 ? B_1d : G_1d, TTensor3::layout, A2, D_layout.merge_23().template split_12(), D_data); } #if 0 template inline MFEM_ALWAYS_INLINE void Assemble(int D1, int D2, const qpt_layout_t &qpt_layout, const qpt_data_t &qpt_data, const D_layout_t &D_layout, D_data_t &D_data) const { const int NC = qpt_layout_t::dim_2; TTensor4 A1; TTensor4 A2; // Using TensorAssemble: --> // qpt_data --> A1 TensorAssemble( Bt_1d.layout, D1 != 2 ? Bt_1d : Gt_1d, B_1d.layout, D2 != 2 ? B_1d : G_1d, qpt_layout.template split_1(), qpt_data, A1.layout, A1); // A1 --> A2 TensorAssemble( Bt_1d.layout, D1 != 1 ? Bt_1d : Gt_1d, B_1d.layout, D2 != 1 ? B_1d : G_1d, TTensor3::layout, A1, A2.layout, A2); // A2 --> M TensorAssemble( Bt_1d.layout, D1 != 0 ? Bt_1d : Gt_1d, B_1d.layout, D2 != 0 ? B_1d : G_1d, TTensor3::layout, A2, D_layout.merge_23().template split_12(), D_data); } #endif /** @brief Multi-component assemble of grad-grad element matrices. qpt_layout is (TNIP x DIM x DIM x NumComp), and D_layout is (TDOF x TDOF x NumComp). */ template inline MFEM_ALWAYS_INLINE void AssembleGradGrad(const qpt_layout_t &qpt_layout, const qpt_data_t &qpt_data, const D_layout_t &D_layout, D_data_t &D_data) const { #if 1 // NOTE: This function compiles into a large chunk of machine code Assemble<0,0,false>(qpt_layout.ind23(0,0), qpt_data, D_layout, D_data); Assemble<1,0,true >(qpt_layout.ind23(1,0), qpt_data, D_layout, D_data); Assemble<2,0,true >(qpt_layout.ind23(2,0), qpt_data, D_layout, D_data); Assemble<0,1,true >(qpt_layout.ind23(0,1), qpt_data, D_layout, D_data); Assemble<1,1,true >(qpt_layout.ind23(1,1), qpt_data, D_layout, D_data); Assemble<2,1,true >(qpt_layout.ind23(2,1), qpt_data, D_layout, D_data); Assemble<0,2,true >(qpt_layout.ind23(0,2), qpt_data, D_layout, D_data); Assemble<1,2,true >(qpt_layout.ind23(1,2), qpt_data, D_layout, D_data); Assemble<2,2,true >(qpt_layout.ind23(2,2), qpt_data, D_layout, D_data); #else TAssign(D_layout, D_data, 0.0); for (int d2 = 0; d2 < 3; d2++) { for (int d1 = 0; d1 < 3; d1++) { Assemble(d1, d2, qpt_layout.ind23(d1,d2), qpt_data, D_layout, D_data); } } #endif } }; /// ShapeEvaluator with tensor-product structure in any dimension template class ShapeEvaluator_base : public TProductShapeEvaluator { protected: typedef TProductShapeEvaluator base_class; using base_class::B_1d; using base_class::Bt_1d; using base_class::G_1d; using base_class::Gt_1d; public: ShapeEvaluator_base(const FE &fe) { fe.Calc1DShapes(IR::Get1DIntRule(), B_1d.data, G_1d.data); TAssign(Bt_1d.layout, Bt_1d, B_1d.layout.transpose_12(), B_1d); TAssign(Gt_1d.layout, Gt_1d, G_1d.layout.transpose_12(), G_1d); } // default copy constructor }; /// General ShapeEvaluator for any scalar FE type (L2 or H1) template class ShapeEvaluator : public ShapeEvaluator_base { public: typedef real_t real_type; static const int dim = FE::dim; static const int qpts = IR::qpts; static const bool tensor_prod = FE::tensor_prod && IR::tensor_prod; typedef FE FE_type; typedef IR IR_type; typedef ShapeEvaluator_base base_class; using base_class::Calc; using base_class::CalcT; using base_class::CalcGrad; using base_class::CalcGradT; ShapeEvaluator(const FE &fe) : base_class(fe) { } // default copy constructor }; /** @brief Field evaluators -- values of a given global FE grid function This is roughly speaking a templated version of GridFunction */ template class FieldEvaluator_base { protected: typedef typename FESpace_t::FE_type FE_type; typedef ShapeEvaluator ShapeEval_type; FESpace_t fespace; ShapeEval_type shapeEval; VecLayout_t vec_layout; /// With this constructor, fespace is a shallow copy. inline MFEM_ALWAYS_INLINE FieldEvaluator_base(const FESpace_t &tfes, const ShapeEval_type &shape_eval, const VecLayout_t &vec_layout) : fespace(tfes), shapeEval(shape_eval), vec_layout(vec_layout) { } /// This constructor creates new fespace, not a shallow copy. inline MFEM_ALWAYS_INLINE FieldEvaluator_base(const FE_type &fe, const FiniteElementSpace &fes) : fespace(fe, fes), shapeEval(fe), vec_layout(fes) { } }; /// complex_t - dof/qpt data type, real_t - ShapeEvaluator (FE basis) data type template class FieldEvaluator : public FieldEvaluator_base { public: typedef complex_t complex_type; typedef FESpace_t FESpace_type; typedef typename FESpace_t::FE_type FE_type; typedef ShapeEvaluator ShapeEval_type; typedef VecLayout_t VecLayout_type; // this type typedef FieldEvaluator T_type; static const int dofs = FE_type::dofs; static const int dim = FE_type::dim; static const int qpts = IR::qpts; static const int vdim = VecLayout_t::vec_dim; protected: typedef FieldEvaluator_base base_class; using base_class::fespace; using base_class::shapeEval; using base_class::vec_layout; const complex_t *data_in; complex_t *data_out; public: /// With this constructor, fespace is a shallow copy of tfes. inline MFEM_ALWAYS_INLINE FieldEvaluator(const FESpace_t &tfes, const ShapeEval_type &shape_eval, const VecLayout_type &vec_layout, const complex_t *global_data_in, complex_t *global_data_out) : base_class(tfes, shape_eval, vec_layout), data_in(global_data_in), data_out(global_data_out) { } /// With this constructor, fespace is a shallow copy of f.fespace. inline MFEM_ALWAYS_INLINE FieldEvaluator(const FieldEvaluator &f, const complex_t *global_data_in, complex_t *global_data_out) : base_class(f.fespace, f.shapeEval, f.vec_layout), data_in(global_data_in), data_out(global_data_out) { } /// This constructor creates a new fespace, not a shallow copy. inline MFEM_ALWAYS_INLINE FieldEvaluator(const FiniteElementSpace &fes, const complex_t *global_data_in, complex_t *global_data_out) : base_class(FE_type(*fes.FEColl()), fes), data_in(global_data_in), data_out(global_data_out) { } // Default copy constructor inline MFEM_ALWAYS_INLINE FESpace_type &FESpace() { return fespace; } inline MFEM_ALWAYS_INLINE ShapeEval_type &ShapeEval() { return shapeEval; } inline MFEM_ALWAYS_INLINE VecLayout_type &VecLayout() { return vec_layout; } inline MFEM_ALWAYS_INLINE void SetElement(int el) { fespace.SetElement(el); } /// val_layout_t is (qpts x vdim x NE) template inline MFEM_ALWAYS_INLINE void GetValues(int el, const val_layout_t &l, val_data_t &vals) { const int ne = val_layout_t::dim_3; TTensor3 val_dofs; SetElement(el); fespace.VectorExtract(vec_layout, data_in, val_dofs.layout, val_dofs); shapeEval.Calc(val_dofs.layout.merge_23(), val_dofs, l.merge_23(), vals); } /// grad_layout_t is (qpts x dim x vdim x NE) template inline MFEM_ALWAYS_INLINE void GetGradients(int el, const grad_layout_t &l, grad_data_t &grad) { const int ne = grad_layout_t::dim_4; TTensor3 val_dofs; SetElement(el); fespace.VectorExtract(vec_layout, data_in, val_dofs.layout, val_dofs); shapeEval.CalcGrad(val_dofs.layout.merge_23(), val_dofs, l.merge_34(), grad); } // TODO: add method GetValuesAndGradients() template inline MFEM_ALWAYS_INLINE void Eval(DataType &F) { // T.SetElement() must be called outside Action::Eval(vec_layout, *this, F); } template inline MFEM_ALWAYS_INLINE void Eval(int el, DataType &F) { SetElement(el); Eval(F); } template inline MFEM_ALWAYS_INLINE void Assemble(DataType &F) { // T.SetElement() must be called outside Action:: template Assemble(vec_layout, *this, F); } template inline MFEM_ALWAYS_INLINE void Assemble(int el, DataType &F) { SetElement(el); Assemble(F); } #ifdef MFEM_TEMPLATE_ENABLE_SERIALIZE template inline MFEM_ALWAYS_INLINE void EvalSerialized(const typename DataType::vcomplex_t *loc_dofs, DataType &F) { Action::EvalSerialized(*this, loc_dofs, F); } template inline MFEM_ALWAYS_INLINE void AssembleSerialized(const DataType &F, typename DataType::vcomplex_t *loc_dofs) { Action:: template AssembleSerialized(*this, F, loc_dofs); } #endif /** @brief Enumeration for the data type used by the Eval() and Assemble() methods. The types can be obtained by summing constants from this enumeration and used as a template parameter in struct Data. */ enum InOutData { None = 0, Values = 1, Gradients = 2 }; /** @brief Auxiliary templated struct AData, used by the Eval() and Assemble() methods. The template parameter IOData is "bitwise or" of constants from the enum InOutData. The parameter NE is the number of elements to be processed in the Eval() and Assemble() methods. */ template struct AData; template struct AData<0,it_t> // 0 = None { // Do we need this? }; template struct AData<1,it_t> // 1 = Values { static const int ne = it_t::batch_size; typedef typename it_t::vcomplex_t vcomplex_t; #ifdef MFEM_TEMPLATE_FIELD_EVAL_DATA_HAS_DOFS typedef TTensor3 val_dofs_t; val_dofs_t val_dofs; #else typedef TTensor3 val_dofs_t; #endif TTensor3 val_qpts; }; template struct AData<2,it_t> // 2 = Gradients { static const int ne = it_t::batch_size; typedef typename it_t::vcomplex_t vcomplex_t; #ifdef MFEM_TEMPLATE_FIELD_EVAL_DATA_HAS_DOFS typedef TTensor3 val_dofs_t; val_dofs_t val_dofs; #else typedef TTensor3 val_dofs_t; #endif TTensor4 grad_qpts; }; template struct AData<3,it_t> // 3 = Values+Gradients { static const int ne = it_t::batch_size; typedef typename it_t::vcomplex_t vcomplex_t; #ifdef MFEM_TEMPLATE_FIELD_EVAL_DATA_HAS_DOFS typedef TTensor3 val_dofs_t; val_dofs_t val_dofs; #else typedef TTensor3 val_dofs_t; #endif TTensor3 val_qpts; TTensor4 grad_qpts; }; /** @brief This struct is similar to struct AData, adding separate static data members for the input (InData) and output (OutData) data types. */ template struct BData : public AData { typedef T_type eval_type; static const int InData = IData; static const int OutData = OData; }; /** @brief This struct implements the input (Eval, EvalSerialized) and output (Assemble, AssembleSerialized) operations for the given Ops. Ops is "bitwise or" of constants from the enum InOutData. */ template struct Action; template struct Action<0,dummy> // 0 = None { // Do we need this? }; template struct Action<1,dummy> // 1 = Values { template static inline MFEM_ALWAYS_INLINE void Eval(const vec_layout_t &l, T_type &T, AData_t &D) { #ifdef MFEM_TEMPLATE_FIELD_EVAL_DATA_HAS_DOFS typename AData_t::val_dofs_t &val_dofs = D.val_dofs; #else typename AData_t::val_dofs_t val_dofs; #endif T.fespace.VectorExtract(l, T.data_in, val_dofs.layout, val_dofs); T.shapeEval.Calc(val_dofs.layout.merge_23(), val_dofs, D.val_qpts.layout.merge_23(), D.val_qpts); } template static inline MFEM_ALWAYS_INLINE void Assemble(const vec_layout_t &l, T_type &T, AData_t &D) { const AssignOp::Type Op = Add ? AssignOp::Add : AssignOp::Set; #ifdef MFEM_TEMPLATE_FIELD_EVAL_DATA_HAS_DOFS typename AData_t::val_dofs_t &val_dofs = D.val_dofs; #else typename AData_t::val_dofs_t val_dofs; #endif T.shapeEval.template CalcT( D.val_qpts.layout.merge_23(), D.val_qpts, val_dofs.layout.merge_23(), val_dofs); T.fespace.template VectorAssemble( val_dofs.layout, val_dofs, l, T.data_out); } #ifdef MFEM_TEMPLATE_ENABLE_SERIALIZE template static inline MFEM_ALWAYS_INLINE void EvalSerialized(T_type &T, const typename AData_t::vcomplex_t *loc_dofs, AData_t &D) { T.shapeEval.Calc(AData_t::val_dofs_t::layout.merge_23(), loc_dofs, D.val_qpts.layout.merge_23(), D.val_qpts); } template static inline MFEM_ALWAYS_INLINE void AssembleSerialized(T_type &T, const AData_t &D, typename AData_t::vcomplex_t *loc_dofs) { T.shapeEval.template CalcT( D.val_qpts.layout.merge_23(), D.val_qpts, AData_t::val_dofs_t::layout.merge_23(), loc_dofs); } #endif }; template struct Action<2,dummy> // 2 = Gradients { template static inline MFEM_ALWAYS_INLINE void Eval(const vec_layout_t &l, T_type &T, AData_t &D) { #ifdef MFEM_TEMPLATE_FIELD_EVAL_DATA_HAS_DOFS typename AData_t::val_dofs_t &val_dofs = D.val_dofs; #else typename AData_t::val_dofs_t val_dofs; #endif T.fespace.VectorExtract(l, T.data_in, val_dofs.layout, val_dofs); T.shapeEval.CalcGrad(val_dofs.layout.merge_23(), val_dofs, D.grad_qpts.layout.merge_34(), D.grad_qpts); } template static inline MFEM_ALWAYS_INLINE void Assemble(const vec_layout_t &l, T_type &T, AData_t &D) { const AssignOp::Type Op = Add ? AssignOp::Add : AssignOp::Set; #ifdef MFEM_TEMPLATE_FIELD_EVAL_DATA_HAS_DOFS typename AData_t::val_dofs_t &val_dofs = D.val_dofs; #else typename AData_t::val_dofs_t val_dofs; #endif T.shapeEval.template CalcGradT( D.grad_qpts.layout.merge_34(), D.grad_qpts, val_dofs.layout.merge_23(), val_dofs); T.fespace.template VectorAssemble( val_dofs.layout, val_dofs, l, T.data_out); } #ifdef MFEM_TEMPLATE_ENABLE_SERIALIZE template static inline MFEM_ALWAYS_INLINE void EvalSerialized(T_type &T, const typename AData_t::vcomplex_t *loc_dofs, AData_t &D) { T.shapeEval.CalcGrad(AData_t::val_dofs_t::layout.merge_23(), loc_dofs, D.grad_qpts.layout.merge_34(), D.grad_qpts); } template static inline MFEM_ALWAYS_INLINE void AssembleSerialized(T_type &T, const AData_t &D, typename AData_t::vcomplex_t *loc_dofs) { T.shapeEval.template CalcGradT( D.grad_qpts.layout.merge_34(), D.grad_qpts, AData_t::val_dofs_t::layout.merge_23(), loc_dofs); } #endif }; template struct Action<3,dummy> // 3 = Values+Gradients { template static inline MFEM_ALWAYS_INLINE void Eval(const vec_layout_t &l, T_type &T, AData_t &D) { #ifdef MFEM_TEMPLATE_FIELD_EVAL_DATA_HAS_DOFS typename AData_t::val_dofs_t &val_dofs = D.val_dofs; #else typename AData_t::val_dofs_t val_dofs; #endif T.fespace.VectorExtract(l, T.data_in, val_dofs.layout, val_dofs); T.shapeEval.Calc(val_dofs.layout.merge_23(), val_dofs, D.val_qpts.layout.merge_23(), D.val_qpts); T.shapeEval.CalcGrad(val_dofs.layout.merge_23(), val_dofs, D.grad_qpts.layout.merge_34(), D.grad_qpts); } template static inline MFEM_ALWAYS_INLINE void Assemble(const vec_layout_t &l, T_type &T, AData_t &D) { const AssignOp::Type Op = Add ? AssignOp::Add : AssignOp::Set; #ifdef MFEM_TEMPLATE_FIELD_EVAL_DATA_HAS_DOFS typename AData_t::val_dofs_t &val_dofs = D.val_dofs; #else typename AData_t::val_dofs_t val_dofs; #endif T.shapeEval.template CalcT( D.val_qpts.layout.merge_23(), D.val_qpts, val_dofs.layout.merge_23(), val_dofs); T.shapeEval.template CalcGradT( D.grad_qpts.layout.merge_34(), D.grad_qpts, val_dofs.layout.merge_23(), val_dofs); T.fespace.template VectorAssemble( val_dofs.layout, val_dofs, l, T.data_out); } #ifdef MFEM_TEMPLATE_ENABLE_SERIALIZE template static inline MFEM_ALWAYS_INLINE void EvalSerialized(T_type &T, const typename AData_t::vcomplex_t *loc_dofs, AData_t &D) { T.shapeEval.Calc(AData_t::val_dofs_t::layout.merge_23(), loc_dofs, D.val_qpts.layout.merge_23(), D.val_qpts); T.shapeEval.CalcGrad(AData_t::val_dofs_t::layout.merge_23(), loc_dofs, D.grad_qpts.layout.merge_34(), D.grad_qpts); } template static inline MFEM_ALWAYS_INLINE void AssembleSerialized(T_type &T, const AData_t &D, typename AData_t::vcomplex_t *loc_dofs) { T.shapeEval.template CalcT( D.val_qpts.layout.merge_23(), D.val_qpts, AData_t::val_dofs_t::layout.merge_23(), loc_dofs); T.shapeEval.template CalcGradT( D.grad_qpts.layout.merge_34(), D.grad_qpts, AData_t::val_dofs_t::layout.merge_23(), loc_dofs); } #endif }; /** @brief This struct implements element matrix computation for some combinations of input (InOps) and output (OutOps) operations. */ template struct TElementMatrix; // Case 1,1 = Values,Values template struct TElementMatrix<1,1,it_t> { // qpt_layout_t is (nip), M_layout_t is (dof x dof) // it_t::batch_size = 1 is assumed template static inline MFEM_ALWAYS_INLINE void Compute(const qpt_layout_t &a, const qpt_data_t &A, const M_layout_t &m, M_data_t &M, ShapeEval_type &ev) { ev.Assemble(a.template split_1(), A, m.template split_2(), M); } }; // Case 2,2 = Gradients,Gradients template struct TElementMatrix<2,2,it_t> { /** @brief Assemble element mass matrix @param a the layout for the quadrature point data @param A given quadrature point data for element (incl. coefficient, geometry) @param m the layout for the resulting element mass matrix @param M the resulting element mass matrix @param ev the shape evaluator qpt_layout_t is (nip), M_layout_t is (dof x dof) NE = 1 is assumed */ template static inline MFEM_ALWAYS_INLINE void Compute(const qpt_layout_t &a, const qpt_data_t &A, const M_layout_t &m, M_data_t &M, ShapeEval_type &ev) { ev.AssembleGradGrad(a.template split_3(), A, m.template split_2(), M); } }; template struct Spec { static const int InData = Values*kernel_t::in_values + Gradients*kernel_t::in_gradients; static const int OutData = Values*kernel_t::out_values + Gradients*kernel_t::out_gradients; typedef BData DataType; typedef TElementMatrix ElementMatrix; }; }; } // namespace mfem #endif // MFEM_TEMPLATE_EVALUATOR