LCOV - code coverage report
Current view: top level - src/FEM - LagrangeSpace.hpp (source / functions) Coverage Total Hit
Test: final_report.info Lines: 28.8 % 725 209
Test Date: 2025-09-03 08:53:20 Functions: 54.3 % 313 170

            Line data    Source code
       1              : 
       2              : namespace ippl {
       3              : 
       4              :     // LagrangeSpace constructor, which calls the FiniteElementSpace constructor,
       5              :     // and decomposes the elements among ranks according to layout.
       6              :     template <typename T, unsigned Dim, unsigned Order, typename ElementType,
       7              :               typename QuadratureType, typename FieldLHS, typename FieldRHS>
       8          408 :     LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS, FieldRHS>::LagrangeSpace(
       9              :         UniformCartesian<T, Dim>& mesh, ElementType& ref_element, const QuadratureType& quadrature,
      10              :         const Layout_t& layout)
      11              :         : FiniteElementSpace<T, Dim, getLagrangeNumElementDOFs(Dim, Order), ElementType,
      12          408 :                              QuadratureType, FieldLHS, FieldRHS>(mesh, ref_element, quadrature) {
      13              :         // Assert that the dimension is either 1, 2 or 3.
      14              :         static_assert(Dim >= 1 && Dim <= 3,
      15              :                       "Finite Element space only supports 1D, 2D and 3D meshes");
      16              : 
      17              :         // Initialize the elementIndices view
      18          408 :         initializeElementIndices(layout);
      19          408 :     }
      20              : 
      21              :     // LagrangeSpace constructor, which calls the FiniteElementSpace constructor.
      22              :     template <typename T, unsigned Dim, unsigned Order, typename ElementType,
      23              :               typename QuadratureType, typename FieldLHS, typename FieldRHS>
      24           12 :     LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS, FieldRHS>::LagrangeSpace(
      25              :         UniformCartesian<T, Dim>& mesh, ElementType& ref_element, const QuadratureType& quadrature)
      26              :         : FiniteElementSpace<T, Dim, getLagrangeNumElementDOFs(Dim, Order), ElementType,
      27           12 :                              QuadratureType, FieldLHS, FieldRHS>(mesh, ref_element, quadrature) {
      28              :         // Assert that the dimension is either 1, 2 or 3.
      29              :         static_assert(Dim >= 1 && Dim <= 3,
      30              :                       "Finite Element space only supports 1D, 2D and 3D meshes");
      31           12 :     }
      32              : 
      33              :     // LagrangeSpace initializer, to be made available to the FEMPoissonSolver 
      34              :     // such that we can call it from setRhs.
      35              :     // Sets the correct mesh ad decomposes the elements among ranks according to layout.
      36              :     template <typename T, unsigned Dim, unsigned Order, typename ElementType,
      37              :               typename QuadratureType, typename FieldLHS, typename FieldRHS>
      38            0 :     void LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS, FieldRHS>::initialize(
      39              :         UniformCartesian<T, Dim>& mesh, const Layout_t& layout)
      40              :     {
      41              :         FiniteElementSpace<T, Dim, getLagrangeNumElementDOFs(Dim, Order), ElementType,
      42            0 :                            QuadratureType, FieldLHS, FieldRHS>::setMesh(mesh);
      43              : 
      44              :         // Initialize the elementIndices view
      45            0 :         initializeElementIndices(layout);
      46            0 :     }
      47              : 
      48              :     // Initialize element indices Kokkos View by distributing elements among MPI ranks.
      49              :     template <typename T, unsigned Dim, unsigned Order, typename ElementType,
      50              :               typename QuadratureType, typename FieldLHS, typename FieldRHS>
      51          408 :     void LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
      52              :                        FieldRHS>::initializeElementIndices(const Layout_t& layout) {
      53          408 :         const auto& ldom = layout.getLocalNDIndex();
      54          408 :         int npoints      = ldom.size();
      55          408 :         auto first       = ldom.first();
      56          408 :         auto last        = ldom.last();
      57          408 :         ippl::Vector<double, Dim> bounds;
      58              : 
      59         1260 :         for (size_t d = 0; d < Dim; ++d) {
      60          852 :             bounds[d] = this->nr_m[d] - 1;
      61              :         }
      62              : 
      63          408 :         int upperBoundaryPoints = -1;
      64              : 
      65              :         // We iterate over the local domain points, getting the corresponding elements, 
      66              :         // while tagging upper boundary points such that they can be removed after.
      67          408 :         Kokkos::View<size_t*> points("npoints", npoints);
      68          408 :         Kokkos::parallel_reduce(
      69            0 :             "ComputePoints", npoints,
      70        10758 :             KOKKOS_CLASS_LAMBDA(const int i, int& local) {
      71         9942 :                 int idx = i;
      72         9942 :                 indices_t val;
      73         9942 :                 bool isBoundary = false;
      74        37710 :                 for (unsigned int d = 0; d < Dim; ++d) {
      75        27768 :                     int range = last[d] - first[d] + 1;
      76        27768 :                     val[d]    = first[d] + (idx % range);
      77        27768 :                     idx /= range;
      78        27768 :                     if (val[d] == bounds[d]) {
      79         5358 :                         isBoundary = true;
      80              :                     }
      81              :                 }
      82         9942 :                 points(i) = (!isBoundary) * (this->getElementIndex(val));
      83         9942 :                 local += isBoundary;
      84         9942 :             },
      85          816 :             Kokkos::Sum<int>(upperBoundaryPoints));
      86          408 :         Kokkos::fence();
      87              : 
      88              :         // The elementIndices will be the same array as computed above,
      89              :         // with the tagged upper boundary points removed.
      90          408 :         int elementsPerRank = npoints - upperBoundaryPoints;
      91          408 :         elementIndices      = Kokkos::View<size_t*>("i", elementsPerRank);
      92          408 :         Kokkos::View<size_t> index("index");
      93              : 
      94          408 :         Kokkos::parallel_for(
      95        20700 :             "RemoveNaNs", npoints, KOKKOS_CLASS_LAMBDA(const int i) {
      96        19884 :                 if ((points(i) != 0) || (i == 0)) {
      97        11124 :                     const size_t idx    = Kokkos::atomic_fetch_add(&index(), 1);
      98        16686 :                     elementIndices(idx) = points(i);
      99              :                 }
     100              :             });
     101          408 :     }
     102              : 
     103              :     ///////////////////////////////////////////////////////////////////////
     104              :     /// Degree of Freedom operations //////////////////////////////////////
     105              :     ///////////////////////////////////////////////////////////////////////
     106              : 
     107              :     template <typename T, unsigned Dim, unsigned Order, typename ElementType,
     108              :               typename QuadratureType, typename FieldLHS, typename FieldRHS>
     109           12 :     KOKKOS_FUNCTION size_t LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
     110              :                                          FieldRHS>::numGlobalDOFs() const {
     111           12 :         size_t num_global_dofs = 1;
     112           36 :         for (size_t d = 0; d < Dim; ++d) {
     113           24 :             num_global_dofs *= this->nr_m[d] * Order;
     114              :         }
     115              : 
     116           12 :         return num_global_dofs;
     117              :     }
     118              : 
     119              :     template <typename T, unsigned Dim, unsigned Order, typename ElementType,
     120              :               typename QuadratureType, typename FieldLHS, typename FieldRHS>
     121              :     KOKKOS_FUNCTION
     122       113032 :     size_t LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS, FieldRHS>::getLocalDOFIndex
     123              :     (const size_t& elementIndex, const size_t& globalDOFIndex) const {
     124              :         static_assert(Dim == 1 || Dim == 2 || Dim == 3, "Dim must be 1, 2 or 3");
     125              :         // TODO fix not order independent, only works for order 1
     126              :         static_assert(Order == 1, "Only order 1 is supported at the moment");
     127              : 
     128              :         // Get all the global DOFs for the element
     129       113032 :         const Vector<size_t, numElementDOFs> global_dofs =
     130       113032 :             this->getGlobalDOFIndices(elementIndex);
     131              : 
     132              :         // Find the global DOF in the vector and return the local DOF index
     133              :         // Note: It is important that this only works because the global_dofs 
     134              :         // are already arranged in the correct order from getGlobalDOFIndices
     135       399232 :         for (size_t i = 0; i < global_dofs.dim; ++i) {
     136       398536 :             if (global_dofs[i] == globalDOFIndex) {
     137       112336 :                 return i;
     138              :             }
     139              :         }
     140          696 :         return std::numeric_limits<size_t>::quiet_NaN();
     141              :         //throw IpplException("LagrangeSpace::getLocalDOFIndex()",
     142              :         //                    "FEM Lagrange Space: Global DOF not found in specified element");
     143       113032 :     }
     144              : 
     145              :     template <typename T, unsigned Dim, unsigned Order, typename ElementType,
     146              :               typename QuadratureType, typename FieldLHS, typename FieldRHS>
     147              :     KOKKOS_FUNCTION size_t
     148          336 :     LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
     149              :                   FieldRHS>::getGlobalDOFIndex(const size_t& elementIndex,
     150              :                                                const size_t& localDOFIndex) const {
     151          336 :         const auto global_dofs = this->getGlobalDOFIndices(elementIndex);
     152              : 
     153          672 :         return global_dofs[localDOFIndex];
     154          336 :     }
     155              : 
     156              :     template <typename T, unsigned Dim, unsigned Order, typename ElementType,
     157              :               typename QuadratureType, typename FieldLHS, typename FieldRHS>
     158              :     KOKKOS_FUNCTION Vector<size_t, LagrangeSpace<T, Dim, Order, ElementType, QuadratureType,
     159              :                                                  FieldLHS, FieldRHS>::numElementDOFs>
     160          452 :     LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
     161              :                   FieldRHS>::getLocalDOFIndices() const {
     162          452 :         Vector<size_t, numElementDOFs> localDOFs;
     163              : 
     164         3756 :         for (size_t dof = 0; dof < numElementDOFs; ++dof) {
     165         3304 :             localDOFs[dof] = dof;
     166              :         }
     167              : 
     168          452 :         return localDOFs;
     169              :     }
     170              : 
     171              :     template <typename T, unsigned Dim, unsigned Order, typename ElementType,
     172              :               typename QuadratureType, typename FieldLHS, typename FieldRHS>
     173              :     KOKKOS_FUNCTION Vector<size_t, LagrangeSpace<T, Dim, Order, ElementType, QuadratureType,
     174              :                                                  FieldLHS, FieldRHS>::numElementDOFs>
     175       137820 :     LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
     176              :                   FieldRHS>::getGlobalDOFIndices(const size_t& elementIndex) const {
     177       137820 :         Vector<size_t, numElementDOFs> globalDOFs(0);
     178              : 
     179              :         // get element pos
     180       137820 :         indices_t elementPos = this->getElementNDIndex(elementIndex);
     181              : 
     182              :         // Compute the vector to multiply the ndindex with
     183       137820 :         ippl::Vector<size_t, Dim> vec(1);
     184       325080 :         for (size_t d = 1; d < dim; ++d) {
     185       448028 :             for (size_t d2 = d; d2 < Dim; ++d2) {
     186       260768 :                 vec[d2] *= this->nr_m[d - 1];
     187              :             }
     188              :         }
     189       137820 :         vec *= Order;  // Multiply each dimension by the order
     190       137820 :         size_t smallestGlobalDOF = elementPos.dot(vec);
     191              : 
     192              :         // Add the vertex DOFs
     193       137820 :         globalDOFs[0] = smallestGlobalDOF;
     194       137820 :         globalDOFs[1] = smallestGlobalDOF + Order;
     195              : 
     196              :         if (Dim >= 2) {
     197       113752 :             globalDOFs[2] = globalDOFs[1] + this->nr_m[0] * Order;
     198       113752 :             globalDOFs[3] = globalDOFs[0] + this->nr_m[0] * Order;
     199              :         }
     200              :         if (Dim >= 3) {
     201        73508 :             globalDOFs[4] = globalDOFs[0] + this->nr_m[1] * this->nr_m[0] * Order;
     202        73508 :             globalDOFs[5] = globalDOFs[1] + this->nr_m[1] * this->nr_m[0] * Order;
     203        73508 :             globalDOFs[6] = globalDOFs[2] + this->nr_m[1] * this->nr_m[0] * Order;
     204        73508 :             globalDOFs[7] = globalDOFs[3] + this->nr_m[1] * this->nr_m[0] * Order;
     205              :         }
     206              : 
     207              :         if (Order > 1) {
     208              :             // If the order is greater than 1, there are edge and face DOFs, otherwise the work is
     209              :             // done
     210              : 
     211              :             // Add the edge DOFs
     212              :             if (Dim >= 2) {
     213              :                 for (size_t i = 0; i < Order - 1; ++i) {
     214              :                     globalDOFs[8 + i]                   = globalDOFs[0] + i + 1;
     215              :                     globalDOFs[8 + Order - 1 + i]       = globalDOFs[1] + (i + 1) * this->nr_m[1];
     216              :                     globalDOFs[8 + 2 * (Order - 1) + i] = globalDOFs[2] - (i + 1);
     217              :                     globalDOFs[8 + 3 * (Order - 1) + i] = globalDOFs[3] - (i + 1) * this->nr_m[1];
     218              :                 }
     219              :             }
     220              :             if (Dim >= 3) {
     221              :                 // TODO
     222              :             }
     223              : 
     224              :             // Add the face DOFs
     225              :             if (Dim >= 2) {
     226              :                 for (size_t i = 0; i < Order - 1; ++i) {
     227              :                     for (size_t j = 0; j < Order - 1; ++j) {
     228              :                         // TODO CHECK
     229              :                         globalDOFs[8 + 4 * (Order - 1) + i * (Order - 1) + j] =
     230              :                             globalDOFs[0] + (i + 1) + (j + 1) * this->nr_m[1];
     231              :                         globalDOFs[8 + 4 * (Order - 1) + (Order - 1) * (Order - 1) + i * (Order - 1)
     232              :                                    + j] = globalDOFs[1] + (i + 1) + (j + 1) * this->nr_m[1];
     233              :                         globalDOFs[8 + 4 * (Order - 1) + 2 * (Order - 1) * (Order - 1)
     234              :                                    + i * (Order - 1) + j] =
     235              :                             globalDOFs[2] - (i + 1) + (j + 1) * this->nr_m[1];
     236              :                         globalDOFs[8 + 4 * (Order - 1) + 3 * (Order - 1) * (Order - 1)
     237              :                                    + i * (Order - 1) + j] =
     238              :                             globalDOFs[3] - (i + 1) + (j + 1) * this->nr_m[1];
     239              :                     }
     240              :                 }
     241              :             }
     242              :         }
     243              : 
     244       137820 :         return globalDOFs;
     245       137820 :     }
     246              : 
     247              :     ///////////////////////////////////////////////////////////////////////
     248              :     /// Basis functions and gradients /////////////////////////////////////
     249              :     ///////////////////////////////////////////////////////////////////////
     250              : 
     251              :     template <typename T, unsigned Dim, unsigned Order, typename ElementType,
     252              :               typename QuadratureType, typename FieldLHS, typename FieldRHS>
     253              :     KOKKOS_FUNCTION T
     254       379040 :     LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS, FieldRHS>::
     255              :         evaluateRefElementShapeFunction(
     256              :             const size_t& localDOF,
     257              :             const LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
     258              :                                 FieldRHS>::point_t& localPoint) const {
     259              :         static_assert(Order == 1, "Only order 1 is supported at the moment");
     260              :         // Assert that the local vertex index is valid.
     261       379040 :         assert(localDOF < numElementDOFs
     262              :                && "The local vertex index is invalid");  // TODO assumes 1st order Lagrange
     263              : 
     264       379040 :         assert(this->ref_element_m.isPointInRefElement(localPoint)
     265              :                && "Point is not in reference element");
     266              : 
     267              :         // Get the local vertex indices for the local vertex index.
     268              :         // TODO fix not order independent, only works for order 1
     269       379040 :         const point_t ref_element_point = this->ref_element_m.getLocalVertices()[localDOF];
     270              : 
     271              :         // The variable that accumulates the product of the shape functions.
     272       379040 :         T product = 1;
     273              : 
     274      1444944 :         for (size_t d = 0; d < Dim; d++) {
     275      1065904 :             if (localPoint[d] < ref_element_point[d]) {
     276       532952 :                 product *= localPoint[d];
     277              :             } else {
     278       532952 :                 product *= 1.0 - localPoint[d];
     279              :             }
     280              :         }
     281              : 
     282       379040 :         return product;
     283       379040 :     }
     284              : 
     285              :     template <typename T, unsigned Dim, unsigned Order, typename ElementType,
     286              :               typename QuadratureType, typename FieldLHS, typename FieldRHS>
     287              :     KOKKOS_FUNCTION typename LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
     288              :                                            FieldRHS>::point_t
     289       262600 :     LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS, FieldRHS>::
     290              :         evaluateRefElementShapeFunctionGradient(
     291              :             const size_t& localDOF,
     292              :             const LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
     293              :                                 FieldRHS>::point_t& localPoint) const {
     294              :         // TODO fix not order independent, only works for order 1
     295              :         static_assert(Order == 1 && "Only order 1 is supported at the moment");
     296              : 
     297              :         // Assert that the local vertex index is valid.
     298       262600 :         assert(localDOF < numElementDOFs && "The local vertex index is invalid");
     299              : 
     300       262600 :         assert(this->ref_element_m.isPointInRefElement(localPoint)
     301              :                && "Point is not in reference element");
     302              : 
     303              :         // Get the local dof nd_index
     304       262600 :         const vertex_points_t local_vertex_points = this->ref_element_m.getLocalVertices();
     305              : 
     306       262600 :         const point_t& local_vertex_point = local_vertex_points[localDOF];
     307              : 
     308       262600 :         point_t gradient(1);
     309              : 
     310              :         // To construct the gradient we need to loop over the dimensions and multiply the
     311              :         // shape functions in each dimension except the current one. The one of the current
     312              :         // dimension is replaced by the derivative of the shape function in that dimension,
     313              :         // which is either 1 or -1.
     314      1043664 :         for (size_t d = 0; d < Dim; d++) {
     315              :             // The variable that accumulates the product of the shape functions.
     316       781064 :             T product = 1;
     317              : 
     318      3111120 :             for (size_t d2 = 0; d2 < Dim; d2++) {
     319      2330056 :                 if (d2 == d) {
     320       781064 :                     if (localPoint[d] < local_vertex_point[d]) {
     321       390532 :                         product *= 1;
     322              :                     } else {
     323       390532 :                         product *= -1;
     324              :                     }
     325              :                 } else {
     326      1548992 :                     if (localPoint[d2] < local_vertex_point[d2]) {
     327       774496 :                         product *= localPoint[d2];
     328              :                     } else {
     329       774496 :                         product *= 1.0 - localPoint[d2];
     330              :                     }
     331              :                 }
     332              :             }
     333              : 
     334       781064 :             gradient[d] = product;
     335              :         }
     336              : 
     337       262600 :         return gradient;
     338       262600 :     }
     339              : 
     340              :     ///////////////////////////////////////////////////////////////////////
     341              :     /// Assembly operations ///////////////////////////////////////////////
     342              :     ///////////////////////////////////////////////////////////////////////
     343              : 
     344              :     template <typename T, unsigned Dim, unsigned Order, typename ElementType,
     345              :               typename QuadratureType, typename FieldLHS, typename FieldRHS>
     346              :     template <typename F>
     347            8 :     FieldLHS LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
     348              :                            FieldRHS>::evaluateAx(FieldLHS& field, F& evalFunction) const {
     349            8 :         Inform m("");
     350              : 
     351              :         // start a timer
     352            8 :         static IpplTimings::TimerRef evalAx = IpplTimings::getTimer("evaluateAx");
     353            8 :         IpplTimings::startTimer(evalAx);
     354              : 
     355              :         // get number of ghost cells in field
     356            8 :         const int nghost = field.getNghost();
     357              : 
     358              :         // create a new field for result with view initialized to zero (views are initialized to
     359              :         // zero by default)
     360            8 :         FieldLHS resultField(field.get_mesh(), field.getLayout(), nghost);
     361              : 
     362              :         // List of quadrature weights
     363            8 :         const Vector<T, QuadratureType::numElementNodes> w =
     364            8 :             this->quadrature_m.getWeightsForRefElement();
     365              : 
     366              :         // List of quadrature nodes
     367            8 :         const Vector<point_t, QuadratureType::numElementNodes> q =
     368            8 :             this->quadrature_m.getIntegrationNodesForRefElement();
     369              : 
     370              :         // TODO move outside of evaluateAx (I think it is possible for other problems as well)
     371              :         // Gradients of the basis functions for the DOF at the quadrature nodes
     372            8 :         Vector<Vector<point_t, numElementDOFs>, QuadratureType::numElementNodes> grad_b_q;
     373           16 :         for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
     374           48 :             for (size_t i = 0; i < numElementDOFs; ++i) {
     375           40 :                 grad_b_q[k][i] = this->evaluateRefElementShapeFunctionGradient(i, q[k]);
     376              :             }
     377              :         }
     378              : 
     379              :         // Make local element matrix -- does not change through the element mesh
     380              :         // Element matrix
     381            8 :         Vector<Vector<T, numElementDOFs>, numElementDOFs> A_K;
     382              : 
     383              :         // 1. Compute the Galerkin element matrix A_K
     384           48 :         for (size_t i = 0; i < numElementDOFs; ++i) {
     385          312 :             for (size_t j = 0; j < numElementDOFs; ++j) {
     386          272 :                 A_K[i][j] = 0.0;
     387          544 :                 for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
     388          272 :                     A_K[i][j] += w[k] * evalFunction(i, j, grad_b_q[k]);
     389              :                 }
     390              :             }
     391              :         }
     392              : 
     393              :         // Get field data and atomic result data,
     394              :         // since it will be added to during the kokkos loop
     395            8 :         ViewType view             = field.getView();
     396            8 :         AtomicViewType resultView = resultField.getView();
     397              : 
     398              :         // Get boundary conditions from field
     399            8 :         BConds<FieldLHS, Dim>& bcField = field.getFieldBC();
     400            8 :         FieldBC bcType = bcField[0]->getBCType();
     401              : 
     402              :         // Get domain information
     403            8 :         auto ldom = (field.getLayout()).getLocalNDIndex();
     404              : 
     405              :         using exec_space  = typename Kokkos::View<const size_t*>::execution_space;
     406              :         using policy_type = Kokkos::RangePolicy<exec_space>;
     407              : 
     408              :         // start a timer
     409            8 :         static IpplTimings::TimerRef outer_loop = IpplTimings::getTimer("evaluateAx: outer loop");
     410            8 :         IpplTimings::startTimer(outer_loop);
     411              : 
     412              :         // Loop over elements to compute contributions
     413            8 :         Kokkos::parallel_for(
     414           16 :             "Loop over elements", policy_type(0, elementIndices.extent(0)),
     415          152 :             KOKKOS_CLASS_LAMBDA(const size_t index) {
     416            0 :                 const size_t elementIndex                            = elementIndices(index);
     417          136 :                 const Vector<size_t, numElementDOFs> local_dof = this->getLocalDOFIndices();
     418            0 :                 const Vector<size_t, numElementDOFs> global_dofs =
     419          136 :                     this->getGlobalDOFIndices(elementIndex);
     420          136 :                 Vector<indices_t, numElementDOFs> global_dof_ndindices;
     421              : 
     422         1176 :                 for (size_t i = 0; i < numElementDOFs; ++i) {
     423         1040 :                     global_dof_ndindices[i] = this->getMeshVertexNDIndex(global_dofs[i]);
     424              :                 }
     425              : 
     426              :                 // local DOF indices (both i and j go from 0 to numDOFs-1 in the element)
     427              :                 size_t i, j;
     428              : 
     429              :                 // global DOF n-dimensional indices (Vector of N indices representing indices in
     430              :                 // each dimension)
     431          136 :                 indices_t I_nd, J_nd;
     432              : 
     433              :                 // 2. Compute the contribution to resultAx = A*x with A_K
     434         1176 :                 for (i = 0; i < numElementDOFs; ++i) {
     435            0 :                     I_nd = global_dof_ndindices[i];
     436              : 
     437              :                     // Handle boundary DOFs
     438              :                     // If Zero Dirichlet BCs, skip this DOF
     439              :                     // If Constant Dirichlet BCs, identity
     440         1040 :                     if ((bcType == CONSTANT_FACE) && (this->isDOFOnBoundary(I_nd))) {
     441            0 :                         for (unsigned d = 0; d < Dim; ++d) {
     442            0 :                             I_nd[d] = I_nd[d] - ldom[d].first() + nghost;
     443              :                         }
     444            0 :                         apply(resultView, I_nd) =  apply(view, I_nd);
     445            0 :                         continue;
     446         1040 :                     } else if ((bcType == ZERO_FACE) && (this->isDOFOnBoundary(I_nd))) {
     447            0 :                         continue;
     448              :                     }
     449              : 
     450              :                     // get the appropriate index for the Kokkos view of the field
     451         1752 :                     for (unsigned d = 0; d < Dim; ++d) {
     452            0 :                         I_nd[d] = I_nd[d] - ldom[d].first() + nghost;
     453              :                     }
     454              : 
     455         3924 :                     for (j = 0; j < numElementDOFs; ++j) {
     456            0 :                         J_nd = global_dof_ndindices[j];
     457              : 
     458              :                         // Skip boundary DOFs (Zero & Constant Dirichlet BCs)
     459            0 :                         if (((bcType == ZERO_FACE) || (bcType == CONSTANT_FACE)) 
     460         3480 :                             && this->isDOFOnBoundary(J_nd)) {
     461            0 :                             continue;
     462              :                         }
     463              : 
     464              :                         // get the appropriate index for the Kokkos view of the field
     465         8040 :                         for (unsigned d = 0; d < Dim; ++d) {
     466            0 :                             J_nd[d] = J_nd[d] - ldom[d].first() + nghost;
     467              :                         }
     468              : 
     469         2020 :                         apply(resultView, I_nd) += A_K[i][j] * apply(view, J_nd);
     470              :                     }
     471              :                 }
     472            0 :             });
     473            8 :         IpplTimings::stopTimer(outer_loop);
     474              : 
     475            8 :         if (bcType == PERIODIC_FACE) {
     476            0 :             resultField.accumulateHalo();
     477            0 :             bcField.apply(resultField);
     478            0 :             bcField.assignGhostToPhysical(resultField);
     479              :         } else {
     480            8 :             resultField.accumulateHalo_noghost();
     481              :         }
     482              : 
     483            8 :         IpplTimings::stopTimer(evalAx);
     484              : 
     485            8 :         return resultField;
     486            8 :     }
     487              : 
     488              :     template <typename T, unsigned Dim, unsigned Order, typename ElementType,
     489              :               typename QuadratureType, typename FieldLHS, typename FieldRHS>
     490              :     template <typename F>
     491            0 :     FieldLHS LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
     492              :                            FieldRHS>::evaluateAx_lower(FieldLHS& field, F& evalFunction) const {
     493            0 :         Inform m("");
     494              : 
     495              :         // start a timer
     496            0 :         static IpplTimings::TimerRef evalAx = IpplTimings::getTimer("evaluateAx");
     497            0 :         IpplTimings::startTimer(evalAx);
     498              : 
     499              :         // get number of ghost cells in field
     500            0 :         const int nghost = field.getNghost();
     501              : 
     502              :         // create a new field for result with view initialized to zero (views are initialized to
     503              :         // zero by default)
     504            0 :         FieldLHS resultField(field.get_mesh(), field.getLayout(), nghost);
     505              : 
     506              :         // List of quadrature weights
     507            0 :         const Vector<T, QuadratureType::numElementNodes> w =
     508            0 :             this->quadrature_m.getWeightsForRefElement();
     509              : 
     510              :         // List of quadrature nodes
     511            0 :         const Vector<point_t, QuadratureType::numElementNodes> q =
     512            0 :             this->quadrature_m.getIntegrationNodesForRefElement();
     513              : 
     514              :         // TODO move outside of evaluateAx (I think it is possible for other problems as well)
     515              :         // Gradients of the basis functions for the DOF at the quadrature nodes
     516            0 :         Vector<Vector<point_t, numElementDOFs>, QuadratureType::numElementNodes> grad_b_q;
     517            0 :         for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
     518            0 :             for (size_t i = 0; i < numElementDOFs; ++i) {
     519            0 :                 grad_b_q[k][i] = this->evaluateRefElementShapeFunctionGradient(i, q[k]);
     520              :             }
     521              :         }
     522              : 
     523              :         // Make local element matrix -- does not change through the element mesh
     524              :         // Element matrix
     525            0 :         Vector<Vector<T, numElementDOFs>, numElementDOFs> A_K;
     526              : 
     527              :         // 1. Compute the Galerkin element matrix A_K
     528            0 :         for (size_t i = 0; i < numElementDOFs; ++i) {
     529            0 :             for (size_t j = 0; j < numElementDOFs; ++j) {
     530            0 :                 A_K[i][j] = 0.0;
     531            0 :                 for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
     532            0 :                     A_K[i][j] += w[k] * evalFunction(i, j, grad_b_q[k]);
     533              :                 }
     534              :             }
     535              :         }
     536              : 
     537              :         // Get field data and atomic result data,
     538              :         // since it will be added to during the kokkos loop
     539            0 :         ViewType view             = field.getView();
     540            0 :         AtomicViewType resultView = resultField.getView();
     541              : 
     542              :         // Get boundary conditions from field
     543            0 :         BConds<FieldLHS, Dim>& bcField = field.getFieldBC();
     544            0 :         FieldBC bcType = bcField[0]->getBCType();
     545              : 
     546              :         // Get domain information
     547            0 :         auto ldom = (field.getLayout()).getLocalNDIndex();
     548              : 
     549              :         using exec_space  = typename Kokkos::View<const size_t*>::execution_space;
     550              :         using policy_type = Kokkos::RangePolicy<exec_space>;
     551              : 
     552              :         // start a timer
     553            0 :         static IpplTimings::TimerRef outer_loop = IpplTimings::getTimer("evaluateAx: outer loop");
     554            0 :         IpplTimings::startTimer(outer_loop);
     555              : 
     556              :         // Loop over elements to compute contributions
     557            0 :         Kokkos::parallel_for(
     558            0 :             "Loop over elements", policy_type(0, elementIndices.extent(0)),
     559            0 :             KOKKOS_CLASS_LAMBDA(const size_t index) {
     560            0 :                 const size_t elementIndex                            = elementIndices(index);
     561            0 :                 const Vector<size_t, numElementDOFs> local_dof = this->getLocalDOFIndices();
     562            0 :                 const Vector<size_t, numElementDOFs> global_dofs =
     563            0 :                     this->getGlobalDOFIndices(elementIndex);
     564            0 :                 Vector<indices_t, numElementDOFs> global_dof_ndindices;
     565              : 
     566            0 :                 for (size_t i = 0; i < numElementDOFs; ++i) {
     567            0 :                     global_dof_ndindices[i] = this->getMeshVertexNDIndex(global_dofs[i]);
     568              :                 }
     569              : 
     570              :                 // local DOF indices
     571              :                 size_t i, j;
     572              : 
     573              :                 // global DOF n-dimensional indices (Vector of N indices representing indices in
     574              :                 // each dimension)
     575            0 :                 indices_t I_nd, J_nd;
     576              : 
     577              :                 // 2. Compute the contribution to resultAx = A*x with A_K
     578            0 :                 for (i = 0; i < numElementDOFs; ++i) {
     579            0 :                     I_nd = global_dof_ndindices[i];
     580              : 
     581              :                     // Handle boundary DOFs
     582              :                     // If Zero Dirichlet BCs, skip this DOF
     583              :                     // If Constant Dirichlet BCs, identity
     584            0 :                     if ((bcType == CONSTANT_FACE) && (this->isDOFOnBoundary(I_nd))) {
     585            0 :                         for (unsigned d = 0; d < Dim; ++d) {
     586            0 :                             I_nd[d] = I_nd[d] - ldom[d].first() + nghost;
     587              :                         }
     588            0 :                         apply(resultView, I_nd) =  apply(view, I_nd);
     589            0 :                         continue;
     590            0 :                     } else if ((bcType == ZERO_FACE) && (this->isDOFOnBoundary(I_nd))) {
     591            0 :                         continue;
     592              :                     }
     593              : 
     594              :                     // get the appropriate index for the Kokkos view of the field
     595            0 :                     for (unsigned d = 0; d < Dim; ++d) {
     596            0 :                         I_nd[d] = I_nd[d] - ldom[d].first() + nghost;
     597              :                     }
     598              : 
     599            0 :                     for (j = 0; j < numElementDOFs; ++j) {
     600            0 :                         J_nd = global_dof_ndindices[j];
     601              : 
     602            0 :                         if (global_dofs[i] >= global_dofs[j]) {
     603            0 :                             continue;
     604              :                         }
     605              : 
     606              :                         // Skip boundary DOFs (Zero & Constant Dirichlet BCs)
     607            0 :                         if (((bcType == ZERO_FACE) || (bcType == CONSTANT_FACE)) 
     608            0 :                             && this->isDOFOnBoundary(J_nd)) {
     609            0 :                             continue;
     610              :                         }
     611              : 
     612              :                         // get the appropriate index for the Kokkos view of the field
     613            0 :                         for (unsigned d = 0; d < Dim; ++d) {
     614            0 :                             J_nd[d] = J_nd[d] - ldom[d].first() + nghost;
     615              :                         }
     616              : 
     617            0 :                         apply(resultView, I_nd) += A_K[i][j] * apply(view, J_nd);
     618              :                     }
     619              :                 }
     620            0 :             });
     621            0 :         IpplTimings::stopTimer(outer_loop);
     622              : 
     623            0 :         if (bcType == PERIODIC_FACE) {
     624            0 :             resultField.accumulateHalo();
     625            0 :             bcField.apply(resultField);
     626            0 :             bcField.assignGhostToPhysical(resultField);
     627              :         } else {
     628            0 :             resultField.accumulateHalo_noghost();
     629              :         }
     630              : 
     631            0 :         IpplTimings::stopTimer(evalAx);
     632              : 
     633            0 :         return resultField;
     634            0 :     }
     635              : 
     636              :     template <typename T, unsigned Dim, unsigned Order, typename ElementType,
     637              :               typename QuadratureType, typename FieldLHS, typename FieldRHS>
     638              :     template <typename F>
     639            0 :     FieldLHS LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
     640              :                            FieldRHS>::evaluateAx_upper(FieldLHS& field, F& evalFunction) const {
     641            0 :         Inform m("");
     642              : 
     643              :         // start a timer
     644            0 :         static IpplTimings::TimerRef evalAx = IpplTimings::getTimer("evaluateAx");
     645            0 :         IpplTimings::startTimer(evalAx);
     646              : 
     647              :         // get number of ghost cells in field
     648            0 :         const int nghost = field.getNghost();
     649              : 
     650              :         // create a new field for result with view initialized to zero (views are initialized to
     651              :         // zero by default)
     652            0 :         FieldLHS resultField(field.get_mesh(), field.getLayout(), nghost);
     653              : 
     654              :         // List of quadrature weights
     655            0 :         const Vector<T, QuadratureType::numElementNodes> w =
     656            0 :             this->quadrature_m.getWeightsForRefElement();
     657              : 
     658              :         // List of quadrature nodes
     659            0 :         const Vector<point_t, QuadratureType::numElementNodes> q =
     660            0 :             this->quadrature_m.getIntegrationNodesForRefElement();
     661              : 
     662              :         // TODO move outside of evaluateAx (I think it is possible for other problems as well)
     663              :         // Gradients of the basis functions for the DOF at the quadrature nodes
     664            0 :         Vector<Vector<point_t, numElementDOFs>, QuadratureType::numElementNodes> grad_b_q;
     665            0 :         for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
     666            0 :             for (size_t i = 0; i < numElementDOFs; ++i) {
     667            0 :                 grad_b_q[k][i] = this->evaluateRefElementShapeFunctionGradient(i, q[k]);
     668              :             }
     669              :         }
     670              : 
     671              :         // Make local element matrix -- does not change through the element mesh
     672              :         // Element matrix
     673            0 :         Vector<Vector<T, numElementDOFs>, numElementDOFs> A_K;
     674              : 
     675              :         // 1. Compute the Galerkin element matrix A_K
     676            0 :         for (size_t i = 0; i < numElementDOFs; ++i) {
     677            0 :             for (size_t j = 0; j < numElementDOFs; ++j) {
     678            0 :                 A_K[i][j] = 0.0;
     679            0 :                 for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
     680            0 :                     A_K[i][j] += w[k] * evalFunction(i, j, grad_b_q[k]);
     681              :                 }
     682              :             }
     683              :         }
     684              : 
     685              :         // Get field data and atomic result data,
     686              :         // since it will be added to during the kokkos loop
     687            0 :         ViewType view             = field.getView();
     688            0 :         AtomicViewType resultView = resultField.getView();
     689              : 
     690              :         // Get boundary conditions from field
     691            0 :         BConds<FieldLHS, Dim>& bcField = field.getFieldBC();
     692            0 :         FieldBC bcType = bcField[0]->getBCType();
     693              : 
     694              :         // Get domain information
     695            0 :         auto ldom = (field.getLayout()).getLocalNDIndex();
     696              : 
     697              :         using exec_space  = typename Kokkos::View<const size_t*>::execution_space;
     698              :         using policy_type = Kokkos::RangePolicy<exec_space>;
     699              : 
     700              :         // start a timer
     701            0 :         static IpplTimings::TimerRef outer_loop = IpplTimings::getTimer("evaluateAx: outer loop");
     702            0 :         IpplTimings::startTimer(outer_loop);
     703              : 
     704              :         // Loop over elements to compute contributions
     705            0 :         Kokkos::parallel_for(
     706            0 :             "Loop over elements", policy_type(0, elementIndices.extent(0)),
     707            0 :             KOKKOS_CLASS_LAMBDA(const size_t index) {
     708            0 :                 const size_t elementIndex                            = elementIndices(index);
     709            0 :                 const Vector<size_t, numElementDOFs> local_dof = this->getLocalDOFIndices();
     710            0 :                 const Vector<size_t, numElementDOFs> global_dofs =
     711            0 :                     this->getGlobalDOFIndices(elementIndex);
     712            0 :                 Vector<indices_t, numElementDOFs> global_dof_ndindices;
     713              : 
     714            0 :                 for (size_t i = 0; i < numElementDOFs; ++i) {
     715            0 :                     global_dof_ndindices[i] = this->getMeshVertexNDIndex(global_dofs[i]);
     716              :                 }
     717              : 
     718              :                 // local DOF indices
     719              :                 size_t i, j;
     720              : 
     721              :                 // global DOF n-dimensional indices (Vector of N indices representing indices in
     722              :                 // each dimension)
     723            0 :                 indices_t I_nd, J_nd;
     724              : 
     725              :                 // 2. Compute the contribution to resultAx = A*x with A_K
     726            0 :                 for (i = 0; i < numElementDOFs; ++i) {
     727            0 :                     I_nd = global_dof_ndindices[i];
     728              : 
     729              :                     // Handle boundary DOFs
     730              :                     // If Zero Dirichlet BCs, skip this DOF
     731              :                     // If Constant Dirichlet BCs, identity
     732            0 :                     if ((bcType == CONSTANT_FACE) && (this->isDOFOnBoundary(I_nd))) {
     733            0 :                         for (unsigned d = 0; d < Dim; ++d) {
     734            0 :                             I_nd[d] = I_nd[d] - ldom[d].first() + nghost;
     735              :                         }
     736            0 :                         apply(resultView, I_nd) =  apply(view, I_nd);
     737            0 :                         continue;
     738            0 :                     } else if ((bcType == ZERO_FACE) && (this->isDOFOnBoundary(I_nd))) {
     739            0 :                         continue;
     740              :                     }
     741              : 
     742              :                     // get the appropriate index for the Kokkos view of the field
     743            0 :                     for (unsigned d = 0; d < Dim; ++d) {
     744            0 :                         I_nd[d] = I_nd[d] - ldom[d].first() + nghost;
     745              :                     }
     746              : 
     747            0 :                     for (j = 0; j < numElementDOFs; ++j) {
     748            0 :                         J_nd = global_dof_ndindices[j];
     749              : 
     750            0 :                         if (global_dofs[i] <= global_dofs[j]) {
     751            0 :                             continue;
     752              :                         }
     753              : 
     754              :                         // Skip boundary DOFs (Zero & Constant Dirichlet BCs)
     755            0 :                         if (((bcType == ZERO_FACE) || (bcType == CONSTANT_FACE)) 
     756            0 :                             && this->isDOFOnBoundary(J_nd)) {
     757            0 :                             continue;
     758              :                         }
     759              : 
     760              :                         // get the appropriate index for the Kokkos view of the field
     761            0 :                         for (unsigned d = 0; d < Dim; ++d) {
     762            0 :                             J_nd[d] = J_nd[d] - ldom[d].first() + nghost;
     763              :                         }
     764              : 
     765            0 :                         apply(resultView, I_nd) += A_K[i][j] * apply(view, J_nd);
     766              :                     }
     767              :                 }
     768            0 :             });
     769            0 :         IpplTimings::stopTimer(outer_loop);
     770              : 
     771            0 :         if (bcType == PERIODIC_FACE) {
     772            0 :             resultField.accumulateHalo();
     773            0 :             bcField.apply(resultField);
     774            0 :             bcField.assignGhostToPhysical(resultField);
     775              :         } else {
     776            0 :             resultField.accumulateHalo_noghost();
     777              :         }
     778              : 
     779            0 :         IpplTimings::stopTimer(evalAx);
     780              : 
     781            0 :         return resultField;
     782            0 :     }
     783              : 
     784              :     template <typename T, unsigned Dim, unsigned Order, typename ElementType,
     785              :               typename QuadratureType, typename FieldLHS, typename FieldRHS>
     786              :     template <typename F>
     787            0 :     FieldLHS LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
     788              :                            FieldRHS>::evaluateAx_upperlower(FieldLHS& field, F& evalFunction) const {
     789            0 :         Inform m("");
     790              : 
     791              :         // start a timer
     792            0 :         static IpplTimings::TimerRef evalAx = IpplTimings::getTimer("evaluateAx");
     793            0 :         IpplTimings::startTimer(evalAx);
     794              : 
     795              :         // get number of ghost cells in field
     796            0 :         const int nghost = field.getNghost();
     797              : 
     798              :         // create a new field for result with view initialized to zero (views are initialized to
     799              :         // zero by default)
     800            0 :         FieldLHS resultField(field.get_mesh(), field.getLayout(), nghost);
     801              : 
     802              :         // List of quadrature weights
     803            0 :         const Vector<T, QuadratureType::numElementNodes> w =
     804            0 :             this->quadrature_m.getWeightsForRefElement();
     805              : 
     806              :         // List of quadrature nodes
     807            0 :         const Vector<point_t, QuadratureType::numElementNodes> q =
     808            0 :             this->quadrature_m.getIntegrationNodesForRefElement();
     809              : 
     810              :         // TODO move outside of evaluateAx (I think it is possible for other problems as well)
     811              :         // Gradients of the basis functions for the DOF at the quadrature nodes
     812            0 :         Vector<Vector<point_t, numElementDOFs>, QuadratureType::numElementNodes> grad_b_q;
     813            0 :         for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
     814            0 :             for (size_t i = 0; i < numElementDOFs; ++i) {
     815            0 :                 grad_b_q[k][i] = this->evaluateRefElementShapeFunctionGradient(i, q[k]);
     816              :             }
     817              :         }
     818              : 
     819              :         // Make local element matrix -- does not change through the element mesh
     820              :         // Element matrix
     821            0 :         Vector<Vector<T, numElementDOFs>, numElementDOFs> A_K;
     822              : 
     823              :         // 1. Compute the Galerkin element matrix A_K
     824            0 :         for (size_t i = 0; i < numElementDOFs; ++i) {
     825            0 :             for (size_t j = 0; j < numElementDOFs; ++j) {
     826            0 :                 A_K[i][j] = 0.0;
     827            0 :                 for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
     828            0 :                     A_K[i][j] += w[k] * evalFunction(i, j, grad_b_q[k]);
     829              :                 }
     830              :             }
     831              :         }
     832              : 
     833              :         // Get field data and atomic result data,
     834              :         // since it will be added to during the kokkos loop
     835            0 :         ViewType view             = field.getView();
     836            0 :         AtomicViewType resultView = resultField.getView();
     837              : 
     838              :         // Get boundary conditions from field
     839            0 :         BConds<FieldLHS, Dim>& bcField = field.getFieldBC();
     840            0 :         FieldBC bcType = bcField[0]->getBCType();
     841              : 
     842              :         // Get domain information
     843            0 :         auto ldom = (field.getLayout()).getLocalNDIndex();
     844              : 
     845              :         using exec_space  = typename Kokkos::View<const size_t*>::execution_space;
     846              :         using policy_type = Kokkos::RangePolicy<exec_space>;
     847              : 
     848              :         // start a timer
     849            0 :         static IpplTimings::TimerRef outer_loop = IpplTimings::getTimer("evaluateAx: outer loop");
     850            0 :         IpplTimings::startTimer(outer_loop);
     851              : 
     852              :         // Loop over elements to compute contributions
     853            0 :         Kokkos::parallel_for(
     854            0 :             "Loop over elements", policy_type(0, elementIndices.extent(0)),
     855            0 :             KOKKOS_CLASS_LAMBDA(const size_t index) {
     856            0 :                 const size_t elementIndex                            = elementIndices(index);
     857            0 :                 const Vector<size_t, numElementDOFs> local_dof = this->getLocalDOFIndices();
     858            0 :                 const Vector<size_t, numElementDOFs> global_dofs =
     859            0 :                     this->getGlobalDOFIndices(elementIndex);
     860            0 :                 Vector<indices_t, numElementDOFs> global_dof_ndindices;
     861              : 
     862            0 :                 for (size_t i = 0; i < numElementDOFs; ++i) {
     863            0 :                     global_dof_ndindices[i] = this->getMeshVertexNDIndex(global_dofs[i]);
     864              :                 }
     865              : 
     866              :                 // local DOF indices
     867              :                 size_t i, j;
     868              : 
     869              :                 // global DOF n-dimensional indices (Vector of N indices representing indices in
     870              :                 // each dimension)
     871            0 :                 indices_t I_nd, J_nd;
     872              : 
     873              :                 // 2. Compute the contribution to resultAx = A*x with A_K
     874            0 :                 for (i = 0; i < numElementDOFs; ++i) {
     875            0 :                     I_nd = global_dof_ndindices[i];
     876              : 
     877              :                     // Handle boundary DOFs
     878              :                     // If Zero Dirichlet BCs, skip this DOF
     879              :                     // If Constant Dirichlet BCs, identity
     880            0 :                     if ((bcType == CONSTANT_FACE) && (this->isDOFOnBoundary(I_nd))) {
     881            0 :                         for (unsigned d = 0; d < Dim; ++d) {
     882            0 :                             I_nd[d] = I_nd[d] - ldom[d].first() + nghost;
     883              :                         }
     884            0 :                         apply(resultView, I_nd) =  apply(view, I_nd);
     885            0 :                         continue;
     886            0 :                     } else if ((bcType == ZERO_FACE) && (this->isDOFOnBoundary(I_nd))) {
     887            0 :                         continue;
     888              :                     }
     889              : 
     890              :                     // get the appropriate index for the Kokkos view of the field
     891            0 :                     for (unsigned d = 0; d < Dim; ++d) {
     892            0 :                         I_nd[d] = I_nd[d] - ldom[d].first() + nghost;
     893              :                     }
     894              : 
     895            0 :                     for (j = 0; j < numElementDOFs; ++j) {
     896            0 :                         J_nd = global_dof_ndindices[j];
     897              : 
     898            0 :                         if (global_dofs[i] == global_dofs[j]) {
     899            0 :                             continue;
     900              :                         }
     901              : 
     902              :                         // Skip boundary DOFs (Zero & Constant Dirichlet BCs)
     903            0 :                         if (((bcType == ZERO_FACE) || (bcType == CONSTANT_FACE)) 
     904            0 :                             && this->isDOFOnBoundary(J_nd)) {
     905            0 :                             continue;
     906              :                         }
     907              : 
     908              :                         // get the appropriate index for the Kokkos view of the field
     909            0 :                         for (unsigned d = 0; d < Dim; ++d) {
     910            0 :                             J_nd[d] = J_nd[d] - ldom[d].first() + nghost;
     911              :                         }
     912              : 
     913            0 :                         apply(resultView, I_nd) += A_K[i][j] * apply(view, J_nd);
     914              :                     }
     915              :                 }
     916            0 :             });
     917            0 :         IpplTimings::stopTimer(outer_loop);
     918              : 
     919            0 :         if (bcType == PERIODIC_FACE) {
     920            0 :             resultField.accumulateHalo();
     921            0 :             bcField.apply(resultField);
     922            0 :             bcField.assignGhostToPhysical(resultField);
     923              :         } else {
     924            0 :             resultField.accumulateHalo_noghost();
     925              :         }
     926              : 
     927            0 :         IpplTimings::stopTimer(evalAx);
     928              : 
     929            0 :         return resultField;
     930            0 :     }
     931              : 
     932              :     template <typename T, unsigned Dim, unsigned Order, typename ElementType,
     933              :               typename QuadratureType, typename FieldLHS, typename FieldRHS>
     934              :     template <typename F>
     935            0 :     FieldLHS LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
     936              :                            FieldRHS>::evaluateAx_inversediag(FieldLHS& field, F& evalFunction) const {
     937            0 :         Inform m("");
     938              : 
     939              :         // start a timer
     940            0 :         static IpplTimings::TimerRef evalAx = IpplTimings::getTimer("evaluateAx");
     941            0 :         IpplTimings::startTimer(evalAx);
     942              : 
     943              :         // get number of ghost cells in field
     944            0 :         const int nghost = field.getNghost();
     945              : 
     946              :         // create a new field for result with view initialized to zero (views are initialized to
     947              :         // zero by default)
     948            0 :         FieldLHS resultField(field.get_mesh(), field.getLayout(), nghost);
     949              : 
     950              :         // List of quadrature weights
     951            0 :         const Vector<T, QuadratureType::numElementNodes> w =
     952            0 :             this->quadrature_m.getWeightsForRefElement();
     953              : 
     954              :         // List of quadrature nodes
     955            0 :         const Vector<point_t, QuadratureType::numElementNodes> q =
     956            0 :             this->quadrature_m.getIntegrationNodesForRefElement();
     957              : 
     958              :         // TODO move outside of evaluateAx (I think it is possible for other problems as well)
     959              :         // Gradients of the basis functions for the DOF at the quadrature nodes
     960            0 :         Vector<Vector<point_t, numElementDOFs>, QuadratureType::numElementNodes> grad_b_q;
     961            0 :         for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
     962            0 :             for (size_t i = 0; i < numElementDOFs; ++i) {
     963            0 :                 grad_b_q[k][i] = this->evaluateRefElementShapeFunctionGradient(i, q[k]);
     964              :             }
     965              :         }
     966              : 
     967              :         // Make local element matrix -- does not change through the element mesh
     968              :         // Element matrix
     969            0 :         Vector<Vector<T, numElementDOFs>, numElementDOFs> A_K;
     970              : 
     971              :         // 1. Compute the Galerkin element matrix A_K
     972            0 :         for (size_t i = 0; i < numElementDOFs; ++i) {
     973            0 :             for (size_t j = 0; j < numElementDOFs; ++j) {
     974            0 :                 A_K[i][j] = 0.0;
     975            0 :                 for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
     976            0 :                     A_K[i][j] += w[k] * evalFunction(i, j, grad_b_q[k]);
     977              :                 }
     978              :             }
     979              :         }
     980              : 
     981              :         // Get field data and atomic result data,
     982              :         // since it will be added to during the kokkos loop
     983            0 :         ViewType view             = field.getView();
     984            0 :         AtomicViewType resultView = resultField.getView();
     985              : 
     986              :         // Get boundary conditions from field
     987            0 :         BConds<FieldLHS, Dim>& bcField = field.getFieldBC();
     988            0 :         FieldBC bcType = bcField[0]->getBCType();
     989              : 
     990              :         // Get domain information
     991            0 :         auto ldom = (field.getLayout()).getLocalNDIndex();
     992              : 
     993              :         using exec_space  = typename Kokkos::View<const size_t*>::execution_space;
     994              :         using policy_type = Kokkos::RangePolicy<exec_space>;
     995              : 
     996              :         // start a timer
     997            0 :         static IpplTimings::TimerRef outer_loop = IpplTimings::getTimer("evaluateAx: outer loop");
     998            0 :         IpplTimings::startTimer(outer_loop);
     999              : 
    1000              :         // Loop over elements to compute contributions
    1001            0 :         Kokkos::parallel_for(
    1002            0 :             "Loop over elements", policy_type(0, elementIndices.extent(0)),
    1003            0 :             KOKKOS_CLASS_LAMBDA(const size_t index) {
    1004            0 :                 const size_t elementIndex                            = elementIndices(index);
    1005            0 :                 const Vector<size_t, numElementDOFs> local_dof = this->getLocalDOFIndices();
    1006            0 :                 const Vector<size_t, numElementDOFs> global_dofs =
    1007            0 :                     this->getGlobalDOFIndices(elementIndex);
    1008            0 :                 Vector<indices_t, numElementDOFs> global_dof_ndindices;
    1009              : 
    1010            0 :                 for (size_t i = 0; i < numElementDOFs; ++i) {
    1011            0 :                     global_dof_ndindices[i] = this->getMeshVertexNDIndex(global_dofs[i]);
    1012              :                 }
    1013              : 
    1014              :                 // local DOF indices
    1015              :                 size_t i, j;
    1016              : 
    1017              :                 // global DOF n-dimensional indices (Vector of N indices representing indices in
    1018              :                 // each dimension)
    1019            0 :                 indices_t I_nd, J_nd;
    1020              : 
    1021              :                 // 2. Compute the contribution to resultAx = A*x with A_K
    1022            0 :                 for (i = 0; i < numElementDOFs; ++i) {
    1023            0 :                     I_nd = global_dof_ndindices[i];
    1024              : 
    1025              :                     // Handle boundary DOFs
    1026              :                     // If Zero Dirichlet BCs, skip this DOF
    1027              :                     // If Constant Dirichlet BCs, identity
    1028            0 :                     if ((bcType == CONSTANT_FACE) && (this->isDOFOnBoundary(I_nd))) {
    1029            0 :                         for (unsigned d = 0; d < Dim; ++d) {
    1030            0 :                             I_nd[d] = I_nd[d] - ldom[d].first() + nghost;
    1031              :                         }
    1032            0 :                         apply(resultView, I_nd) = 1.0;
    1033            0 :                         continue;
    1034            0 :                     } else if ((bcType == ZERO_FACE) && (this->isDOFOnBoundary(I_nd))) {
    1035            0 :                         continue;
    1036              :                     }
    1037              : 
    1038              :                     // get the appropriate index for the Kokkos view of the field
    1039            0 :                     for (unsigned d = 0; d < Dim; ++d) {
    1040            0 :                         I_nd[d] = I_nd[d] - ldom[d].first() + nghost;
    1041              :                     }
    1042              : 
    1043            0 :                     for (j = 0; j < numElementDOFs; ++j) {
    1044            0 :                         if (global_dofs[i] == global_dofs[j]) {
    1045            0 :                             J_nd = global_dof_ndindices[j];
    1046              : 
    1047              :                             // Skip boundary DOFs (Zero & Constant Dirichlet BCs)
    1048            0 :                             if (((bcType == ZERO_FACE) || (bcType == CONSTANT_FACE)) 
    1049            0 :                                 && this->isDOFOnBoundary(J_nd)) {
    1050            0 :                                 continue;
    1051              :                             }
    1052              : 
    1053              :                             // get the appropriate index for the Kokkos view of the field
    1054            0 :                             for (unsigned d = 0; d < Dim; ++d) {
    1055            0 :                                 J_nd[d] = J_nd[d] - ldom[d].first() + nghost;
    1056              :                             }
    1057              : 
    1058              :                             // sum up all contributions of element matrix
    1059            0 :                             apply(resultView, I_nd) += A_K[i][j];
    1060              :                         }
    1061              :                     }
    1062              :                 }
    1063            0 :             });
    1064              : 
    1065            0 :         if (bcType == PERIODIC_FACE) {
    1066            0 :             resultField.accumulateHalo();
    1067            0 :             bcField.apply(resultField);
    1068            0 :             bcField.assignGhostToPhysical(resultField);
    1069              :         } else {
    1070            0 :             resultField.accumulateHalo_noghost();
    1071              :         }
    1072              : 
    1073              :         // apply the inverse diagonal after already summed all contributions from element matrices
    1074              :         using index_array_type = typename RangePolicy<Dim, exec_space>::index_array_type;
    1075            0 :         ippl::parallel_for("Loop over result view to apply inverse", field.getFieldRangePolicy(),
    1076            0 :             KOKKOS_LAMBDA(const index_array_type& args) {
    1077            0 :                 if (apply(resultView, args) != 0.0) {
    1078            0 :                     apply(resultView, args) = (1.0 / apply(resultView, args)) * apply(view, args);
    1079              :                 }
    1080              :             });
    1081            0 :         IpplTimings::stopTimer(outer_loop);
    1082              : 
    1083            0 :         IpplTimings::stopTimer(evalAx);
    1084              : 
    1085            0 :         return resultField;
    1086            0 :     }
    1087              : 
    1088              :     template <typename T, unsigned Dim, unsigned Order, typename ElementType,
    1089              :               typename QuadratureType, typename FieldLHS, typename FieldRHS>
    1090              :     template <typename F>
    1091            0 :     FieldLHS LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
    1092              :                            FieldRHS>::evaluateAx_diag(FieldLHS& field, F& evalFunction) const {
    1093            0 :         Inform m("");
    1094              : 
    1095              :         // start a timer
    1096            0 :         static IpplTimings::TimerRef evalAx = IpplTimings::getTimer("evaluateAx");
    1097            0 :         IpplTimings::startTimer(evalAx);
    1098              : 
    1099              :         // get number of ghost cells in field
    1100            0 :         const int nghost = field.getNghost();
    1101              : 
    1102              :         // create a new field for result with view initialized to zero (views are initialized to
    1103              :         // zero by default)
    1104            0 :         FieldLHS resultField(field.get_mesh(), field.getLayout(), nghost);
    1105              : 
    1106              :         // List of quadrature weights
    1107            0 :         const Vector<T, QuadratureType::numElementNodes> w =
    1108            0 :             this->quadrature_m.getWeightsForRefElement();
    1109              : 
    1110              :         // List of quadrature nodes
    1111            0 :         const Vector<point_t, QuadratureType::numElementNodes> q =
    1112            0 :             this->quadrature_m.getIntegrationNodesForRefElement();
    1113              : 
    1114              :         // TODO move outside of evaluateAx (I think it is possible for other problems as well)
    1115              :         // Gradients of the basis functions for the DOF at the quadrature nodes
    1116            0 :         Vector<Vector<point_t, numElementDOFs>, QuadratureType::numElementNodes> grad_b_q;
    1117            0 :         for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
    1118            0 :             for (size_t i = 0; i < numElementDOFs; ++i) {
    1119            0 :                 grad_b_q[k][i] = this->evaluateRefElementShapeFunctionGradient(i, q[k]);
    1120              :             }
    1121              :         }
    1122              : 
    1123              :         // Make local element matrix -- does not change through the element mesh
    1124              :         // Element matrix
    1125            0 :         Vector<Vector<T, numElementDOFs>, numElementDOFs> A_K;
    1126              : 
    1127              :         // 1. Compute the Galerkin element matrix A_K
    1128            0 :         for (size_t i = 0; i < numElementDOFs; ++i) {
    1129            0 :             for (size_t j = 0; j < numElementDOFs; ++j) {
    1130            0 :                 A_K[i][j] = 0.0;
    1131            0 :                 for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
    1132            0 :                     A_K[i][j] += w[k] * evalFunction(i, j, grad_b_q[k]);
    1133              :                 }
    1134              :             }
    1135              :         }
    1136              : 
    1137              :         // Get field data and atomic result data,
    1138              :         // since it will be added to during the kokkos loop
    1139            0 :         ViewType view             = field.getView();
    1140            0 :         AtomicViewType resultView = resultField.getView();
    1141              : 
    1142              :         // Get boundary conditions from field
    1143            0 :         BConds<FieldLHS, Dim>& bcField = field.getFieldBC();
    1144            0 :         FieldBC bcType = bcField[0]->getBCType();
    1145              : 
    1146              :         // Get domain information
    1147            0 :         auto ldom = (field.getLayout()).getLocalNDIndex();
    1148              : 
    1149              :         using exec_space  = typename Kokkos::View<const size_t*>::execution_space;
    1150              :         using policy_type = Kokkos::RangePolicy<exec_space>;
    1151              : 
    1152              :         // start a timer
    1153            0 :         static IpplTimings::TimerRef outer_loop = IpplTimings::getTimer("evaluateAx: outer loop");
    1154            0 :         IpplTimings::startTimer(outer_loop);
    1155              : 
    1156              :         // Loop over elements to compute contributions
    1157            0 :         Kokkos::parallel_for(
    1158            0 :             "Loop over elements", policy_type(0, elementIndices.extent(0)),
    1159            0 :             KOKKOS_CLASS_LAMBDA(const size_t index) {
    1160            0 :                 const size_t elementIndex                            = elementIndices(index);
    1161            0 :                 const Vector<size_t, numElementDOFs> local_dof = this->getLocalDOFIndices();
    1162            0 :                 const Vector<size_t, numElementDOFs> global_dofs =
    1163            0 :                     this->getGlobalDOFIndices(elementIndex);
    1164            0 :                 Vector<indices_t, numElementDOFs> global_dof_ndindices;
    1165              : 
    1166            0 :                 for (size_t i = 0; i < numElementDOFs; ++i) {
    1167            0 :                     global_dof_ndindices[i] = this->getMeshVertexNDIndex(global_dofs[i]);
    1168              :                 }
    1169              : 
    1170              :                 // local DOF indices
    1171              :                 size_t i, j;
    1172              : 
    1173              :                 // global DOF n-dimensional indices (Vector of N indices representing indices in
    1174              :                 // each dimension)
    1175            0 :                 indices_t I_nd, J_nd;
    1176              : 
    1177              :                 // 2. Compute the contribution to resultAx = A*x with A_K
    1178            0 :                 for (i = 0; i < numElementDOFs; ++i) {
    1179            0 :                     I_nd = global_dof_ndindices[i];
    1180              : 
    1181              :                     // Handle boundary DOFs
    1182              :                     // If Zero Dirichlet BCs, skip this DOF
    1183              :                     // If Constant Dirichlet BCs, identity
    1184            0 :                     if ((bcType == CONSTANT_FACE) && (this->isDOFOnBoundary(I_nd))) {
    1185            0 :                         for (unsigned d = 0; d < Dim; ++d) {
    1186            0 :                             I_nd[d] = I_nd[d] - ldom[d].first() + nghost;
    1187              :                         }
    1188            0 :                         apply(resultView, I_nd) =  apply(view, I_nd);
    1189            0 :                         continue;
    1190            0 :                     } else if ((bcType == ZERO_FACE) && (this->isDOFOnBoundary(I_nd))) {
    1191            0 :                         continue;
    1192              :                     }
    1193              : 
    1194              :                     // get the appropriate index for the Kokkos view of the field
    1195            0 :                     for (unsigned d = 0; d < Dim; ++d) {
    1196            0 :                         I_nd[d] = I_nd[d] - ldom[d].first() + nghost;
    1197              :                     }
    1198              : 
    1199            0 :                     for (j = 0; j < numElementDOFs; ++j) {
    1200            0 :                         if (global_dofs[i] == global_dofs[j]) {
    1201            0 :                             J_nd = global_dof_ndindices[j];
    1202              : 
    1203              :                             // Skip boundary DOFs (Zero & Constant Dirichlet BCs)
    1204            0 :                             if (((bcType == ZERO_FACE) || (bcType == CONSTANT_FACE)) 
    1205            0 :                                 && this->isDOFOnBoundary(J_nd)) {
    1206            0 :                                 continue;
    1207              :                             }
    1208              : 
    1209              :                             // get the appropriate index for the Kokkos view of the field
    1210            0 :                             for (unsigned d = 0; d < Dim; ++d) {
    1211            0 :                                 J_nd[d] = J_nd[d] - ldom[d].first() + nghost;
    1212              :                             }
    1213              : 
    1214            0 :                             apply(resultView, I_nd) += A_K[i][j] * apply(view, J_nd);
    1215              :                         }
    1216              :                     }
    1217              :                 }
    1218            0 :             });
    1219            0 :         IpplTimings::stopTimer(outer_loop);
    1220              : 
    1221            0 :         if (bcType == PERIODIC_FACE) {
    1222            0 :             resultField.accumulateHalo();
    1223            0 :             bcField.apply(resultField);
    1224            0 :             bcField.assignGhostToPhysical(resultField);
    1225              :         } else {
    1226            0 :             resultField.accumulateHalo_noghost();
    1227              :         }
    1228              : 
    1229            0 :         IpplTimings::stopTimer(evalAx);
    1230              : 
    1231            0 :         return resultField;
    1232            0 :     }
    1233              : 
    1234              :     template <typename T, unsigned Dim, unsigned Order, typename ElementType,
    1235              :               typename QuadratureType, typename FieldLHS, typename FieldRHS>
    1236              :     template <typename F>
    1237            0 :     FieldLHS LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
    1238              :                            FieldRHS>::evaluateAx_lift(FieldLHS& field, F& evalFunction) const {
    1239            0 :         Inform m("");
    1240              : 
    1241              :         // get number of ghost cells in field
    1242            0 :         const int nghost = field.getNghost();
    1243              : 
    1244              :         // create a new field for result with view initialized to zero (views are initialized to
    1245              :         // zero by default)
    1246            0 :         FieldLHS resultField(field.get_mesh(), field.getLayout(), nghost);
    1247              : 
    1248              :         // List of quadrature weights
    1249            0 :         const Vector<T, QuadratureType::numElementNodes> w =
    1250            0 :             this->quadrature_m.getWeightsForRefElement();
    1251              : 
    1252              :         // List of quadrature nodes
    1253            0 :         const Vector<point_t, QuadratureType::numElementNodes> q =
    1254            0 :             this->quadrature_m.getIntegrationNodesForRefElement();
    1255              : 
    1256              :         // TODO move outside of evaluateAx (I think it is possible for other problems as well)
    1257              :         // Gradients of the basis functions for the DOF at the quadrature nodes
    1258            0 :         Vector<Vector<point_t, numElementDOFs>, QuadratureType::numElementNodes> grad_b_q;
    1259            0 :         for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
    1260            0 :             for (size_t i = 0; i < numElementDOFs; ++i) {
    1261            0 :                 grad_b_q[k][i] = this->evaluateRefElementShapeFunctionGradient(i, q[k]);
    1262              :             }
    1263              :         }
    1264              : 
    1265              :         // Make local element matrix -- does not change through the element mesh
    1266              :         // Element matrix
    1267            0 :         Vector<Vector<T, numElementDOFs>, numElementDOFs> A_K;
    1268              : 
    1269              :         // 1. Compute the Galerkin element matrix A_K
    1270            0 :         for (size_t i = 0; i < numElementDOFs; ++i) {
    1271            0 :             for (size_t j = 0; j < numElementDOFs; ++j) {
    1272            0 :                 A_K[i][j] = 0.0;
    1273            0 :                 for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
    1274            0 :                     A_K[i][j] += w[k] * evalFunction(i, j, grad_b_q[k]);
    1275              :                 }
    1276              :             }
    1277              :         }
    1278              : 
    1279              :         // Get field data and atomic result data,
    1280              :         // since it will be added to during the kokkos loop
    1281            0 :         ViewType view             = field.getView();
    1282            0 :         AtomicViewType resultView = resultField.getView();
    1283              : 
    1284              :         // Get domain information
    1285            0 :         auto ldom = (field.getLayout()).getLocalNDIndex();
    1286              : 
    1287              :         using exec_space  = typename Kokkos::View<const size_t*>::execution_space;
    1288              :         using policy_type = Kokkos::RangePolicy<exec_space>;
    1289              : 
    1290              :         // Loop over elements to compute contributions
    1291            0 :         Kokkos::parallel_for(
    1292            0 :             "Loop over elements", policy_type(0, elementIndices.extent(0)),
    1293            0 :             KOKKOS_CLASS_LAMBDA(const size_t index) {
    1294            0 :                 const size_t elementIndex                            = elementIndices(index);
    1295            0 :                 const Vector<size_t, numElementDOFs> local_dof = this->getLocalDOFIndices();
    1296            0 :                 const Vector<size_t, numElementDOFs> global_dofs =
    1297            0 :                     this->getGlobalDOFIndices(elementIndex);
    1298            0 :                 Vector<indices_t, numElementDOFs> global_dof_ndindices;
    1299              : 
    1300            0 :                 for (size_t i = 0; i < numElementDOFs; ++i) {
    1301            0 :                     global_dof_ndindices[i] = this->getMeshVertexNDIndex(global_dofs[i]);
    1302              :                 }
    1303              : 
    1304              :                 // local DOF indices (both i and j go from 0 to numDOFs-1 in the element)
    1305              :                 size_t i, j;
    1306              : 
    1307              :                 // global DOF n-dimensional indices (Vector of N indices representing indices in
    1308              :                 // each dimension)
    1309            0 :                 indices_t I_nd, J_nd;
    1310              : 
    1311              :                 // 2. Compute the contribution to resultAx = A*x with A_K
    1312            0 :                 for (i = 0; i < numElementDOFs; ++i) {
    1313            0 :                     I_nd = global_dof_ndindices[i];
    1314              : 
    1315              :                     // Skip if on a row of the matrix
    1316            0 :                     if (this->isDOFOnBoundary(I_nd)) {
    1317            0 :                         continue;
    1318              :                     }
    1319              : 
    1320              :                     // get the appropriate index for the Kokkos view of the field
    1321            0 :                     for (unsigned d = 0; d < Dim; ++d) {
    1322            0 :                         I_nd[d] = I_nd[d] - ldom[d].first() + nghost;
    1323              :                     }
    1324              : 
    1325            0 :                     for (j = 0; j < numElementDOFs; ++j) {
    1326            0 :                         J_nd = global_dof_ndindices[j];
    1327              : 
    1328              :                         // Contribute to lifting only if on a boundary DOF
    1329            0 :                         if (this->isDOFOnBoundary(J_nd)) {
    1330              :                             // get the appropriate index for the Kokkos view of the field
    1331            0 :                             for (unsigned d = 0; d < Dim; ++d) {
    1332            0 :                                 J_nd[d] = J_nd[d] - ldom[d].first() + nghost;
    1333              :                             }
    1334            0 :                             apply(resultView, I_nd) += A_K[i][j] * apply(view, J_nd);
    1335            0 :                             continue;
    1336            0 :                         }
    1337              : 
    1338              :                     }
    1339              :                 }
    1340            0 :             });
    1341            0 :         resultField.accumulateHalo();
    1342              : 
    1343            0 :         return resultField;
    1344            0 :     }
    1345              : 
    1346              :     template <typename T, unsigned Dim, unsigned Order, typename ElementType,
    1347              :               typename QuadratureType, typename FieldLHS, typename FieldRHS>
    1348           20 :     void LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
    1349              :                        FieldRHS>::evaluateLoadVector(FieldRHS& field) const {
    1350           20 :         Inform m("");
    1351              : 
    1352              :         // start a timer
    1353           20 :         static IpplTimings::TimerRef evalLoadV = IpplTimings::getTimer("evaluateLoadVector");
    1354           20 :         IpplTimings::startTimer(evalLoadV);
    1355              : 
    1356              :         // List of quadrature weights
    1357           20 :         const Vector<T, QuadratureType::numElementNodes> w =
    1358           20 :             this->quadrature_m.getWeightsForRefElement();
    1359              : 
    1360              :         // List of quadrature nodes
    1361           20 :         const Vector<point_t, QuadratureType::numElementNodes> q =
    1362           20 :             this->quadrature_m.getIntegrationNodesForRefElement();
    1363              : 
    1364           20 :         const indices_t zeroNdIndex = Vector<size_t, Dim>(0);
    1365              : 
    1366              :         // Evaluate the basis functions for the DOF at the quadrature nodes
    1367           20 :         Vector<Vector<T, numElementDOFs>, QuadratureType::numElementNodes> basis_q;
    1368          648 :         for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
    1369         5108 :             for (size_t i = 0; i < numElementDOFs; ++i) {
    1370         4480 :                 basis_q[k][i] = this->evaluateRefElementShapeFunction(i, q[k]);
    1371              :             }
    1372              :         }
    1373              : 
    1374              :         // Absolute value of det Phi_K
    1375           20 :         const T absDetDPhi = Kokkos::abs(this->ref_element_m.getDeterminantOfTransformationJacobian(
    1376           40 :             this->getElementMeshVertexPoints(zeroNdIndex)));
    1377              : 
    1378              :         // Get domain information and ghost cells
    1379           20 :         auto ldom        = (field.getLayout()).getLocalNDIndex();
    1380           20 :         const int nghost = field.getNghost();
    1381              : 
    1382              :         // Get boundary conditions from field
    1383           20 :         BConds<FieldRHS, Dim>& bcField = field.getFieldBC();
    1384           20 :         FieldBC bcType = bcField[0]->getBCType();
    1385              : 
    1386           20 :         FieldRHS temp_field(field.get_mesh(), field.getLayout(), nghost);
    1387           20 :         temp_field.setFieldBC(bcField);
    1388              : 
    1389              :         // Get field data and make it atomic,
    1390              :         // since it will be added to during the kokkos loop
    1391              :         // We work with a temporary field since we need to use field
    1392              :         // to evaluate the load vector; then we assign temp to RHS field
    1393           20 :         AtomicViewType atomic_view = temp_field.getView();
    1394              : 
    1395              :         using exec_space  = typename Kokkos::View<const size_t*>::execution_space;
    1396              :         using policy_type = Kokkos::RangePolicy<exec_space>;
    1397              : 
    1398              :         // start a timer
    1399           20 :         static IpplTimings::TimerRef outer_loop =
    1400           20 :             IpplTimings::getTimer("evaluateLoadVec: outer loop");
    1401           20 :         IpplTimings::startTimer(outer_loop);
    1402              : 
    1403              :         // Loop over elements to compute contributions
    1404           20 :         Kokkos::parallel_for(
    1405           40 :             "Loop over elements", policy_type(0, elementIndices.extent(0)),
    1406          344 :             KOKKOS_CLASS_LAMBDA(size_t index) {
    1407            0 :                 const size_t elementIndex                              = elementIndices(index);
    1408          304 :                 const Vector<size_t, numElementDOFs> local_dofs  = this->getLocalDOFIndices();
    1409            0 :                 const Vector<size_t, numElementDOFs> global_dofs =
    1410          304 :                     this->getGlobalDOFIndices(elementIndex);
    1411              : 
    1412              :                 size_t i, I;
    1413              : 
    1414              :                 // 1. Compute b_K
    1415         4720 :                 for (i = 0; i < numElementDOFs; ++i) {
    1416            0 :                     I = global_dofs[i];
    1417              : 
    1418              :                     // TODO fix for higher order
    1419         2208 :                     auto dof_ndindex_I = this->getMeshVertexNDIndex(I);
    1420              : 
    1421              :                     // Skip boundary DOFs (Zero and Constant Dirichlet BCs)
    1422            0 :                     if (((bcType == ZERO_FACE) || (bcType == CONSTANT_FACE))
    1423         2208 :                         && (this->isDOFOnBoundary(dof_ndindex_I))) {
    1424            0 :                         continue;
    1425              :                     }
    1426              : 
    1427              :                     // calculate the contribution of this element
    1428            0 :                     T contrib = 0;
    1429        57264 :                     for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
    1430            0 :                         T val = 0;
    1431       499104 :                         for (size_t j = 0; j < numElementDOFs; ++j) {
    1432              :                             // get field index corresponding to this DOF
    1433            0 :                             size_t J           = global_dofs[j];
    1434       442800 :                             auto dof_ndindex_J = this->getMeshVertexNDIndex(J);
    1435      1763712 :                             for (unsigned d = 0; d < Dim; ++d) {
    1436            0 :                                 dof_ndindex_J[d] = dof_ndindex_J[d] - ldom[d].first() + nghost;
    1437              :                             }
    1438              : 
    1439              :                             // get field value at DOF and interpolate to q_k
    1440       442800 :                             val += basis_q[k][j] * apply(field, dof_ndindex_J);
    1441              :                         }
    1442              : 
    1443            0 :                         contrib += w[k] * basis_q[k][i] * absDetDPhi * val;
    1444              :                     }
    1445              : 
    1446              :                     // get the appropriate index for the Kokkos view of the field
    1447         3720 :                     for (unsigned d = 0; d < Dim; ++d) {
    1448            0 :                         dof_ndindex_I[d] = dof_ndindex_I[d] - ldom[d].first() + nghost;
    1449              :                     }
    1450              : 
    1451              :                     // add the contribution of the element to the field
    1452          960 :                     apply(atomic_view, dof_ndindex_I) += contrib;
    1453              : 
    1454              :                 }
    1455            0 :             });
    1456           20 :         IpplTimings::stopTimer(outer_loop);
    1457              : 
    1458           20 :         temp_field.accumulateHalo();
    1459              : 
    1460           20 :         if ((bcType == PERIODIC_FACE) || (bcType == CONSTANT_FACE)) {
    1461            0 :             bcField.apply(temp_field);
    1462            0 :             bcField.assignGhostToPhysical(temp_field);
    1463              :         }
    1464              : 
    1465           20 :         field = temp_field;
    1466              : 
    1467           20 :         IpplTimings::stopTimer(evalLoadV);
    1468           20 :     }
    1469              : 
    1470              :     template <typename T, unsigned Dim, unsigned Order, typename ElementType,
    1471              :               typename QuadratureType, typename FieldLHS, typename FieldRHS>
    1472              :     template <typename F>
    1473            0 :     T LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS, FieldRHS>::computeErrorL2(
    1474              :         const FieldLHS& u_h, const F& u_sol) const {
    1475            0 :         if (this->quadrature_m.getOrder() < (2 * Order + 1)) {
    1476              :             // throw exception
    1477            0 :             throw IpplException(
    1478              :                 "LagrangeSpace::computeErrorL2()",
    1479              :                 "Order of quadrature rule for error computation should be > 2*p + 1");
    1480              :         }
    1481              : 
    1482              :         // List of quadrature weights
    1483            0 :         const Vector<T, QuadratureType::numElementNodes> w =
    1484            0 :             this->quadrature_m.getWeightsForRefElement();
    1485              : 
    1486              :         // List of quadrature nodes
    1487            0 :         const Vector<point_t, QuadratureType::numElementNodes> q =
    1488            0 :             this->quadrature_m.getIntegrationNodesForRefElement();
    1489              : 
    1490              :         // Evaluate the basis functions for the DOF at the quadrature nodes
    1491            0 :         Vector<Vector<T, numElementDOFs>, QuadratureType::numElementNodes> basis_q;
    1492            0 :         for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
    1493            0 :             for (size_t i = 0; i < numElementDOFs; ++i) {
    1494            0 :                 basis_q[k][i] = this->evaluateRefElementShapeFunction(i, q[k]);
    1495              :             }
    1496              :         }
    1497              : 
    1498            0 :         const indices_t zeroNdIndex = Vector<size_t, Dim>(0);
    1499              : 
    1500              :         // Absolute value of det Phi_K
    1501            0 :         const T absDetDPhi = Kokkos::abs(this->ref_element_m.getDeterminantOfTransformationJacobian(
    1502            0 :             this->getElementMeshVertexPoints(zeroNdIndex)));
    1503              : 
    1504              :         // Variable to sum the error to
    1505            0 :         T error = 0;
    1506              : 
    1507              :         // Get domain information and ghost cells
    1508            0 :         auto ldom        = (u_h.getLayout()).getLocalNDIndex();
    1509            0 :         const int nghost = u_h.getNghost();
    1510              : 
    1511              :         using exec_space  = typename Kokkos::View<const size_t*>::execution_space;
    1512              :         using policy_type = Kokkos::RangePolicy<exec_space>;
    1513              : 
    1514              :         // Loop over elements to compute contributions
    1515            0 :         Kokkos::parallel_reduce(
    1516            0 :             "Compute error over elements", policy_type(0, elementIndices.extent(0)),
    1517            0 :             KOKKOS_CLASS_LAMBDA(size_t index, double& local) {
    1518            0 :                 const size_t elementIndex = elementIndices(index);
    1519            0 :                 const Vector<size_t, numElementDOFs> global_dofs =
    1520            0 :                     this->getGlobalDOFIndices(elementIndex);
    1521              : 
    1522              :                 // contribution of this element to the error
    1523            0 :                 T contrib = 0;
    1524            0 :                 for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
    1525            0 :                     T val_u_sol = u_sol(this->ref_element_m.localToGlobal(
    1526            0 :                         this->getElementMeshVertexPoints(this->getElementNDIndex(elementIndex)),
    1527            0 :                         q[k]));
    1528              : 
    1529            0 :                     T val_u_h = 0;
    1530            0 :                     for (size_t i = 0; i < numElementDOFs; ++i) {
    1531              :                         // get field index corresponding to this DOF
    1532            0 :                         size_t I           = global_dofs[i];
    1533            0 :                         auto dof_ndindex_I = this->getMeshVertexNDIndex(I);
    1534            0 :                         for (unsigned d = 0; d < Dim; ++d) {
    1535            0 :                             dof_ndindex_I[d] = dof_ndindex_I[d] - ldom[d].first() + nghost;
    1536              :                         }
    1537              : 
    1538              :                         // get field value at DOF and interpolate to q_k
    1539            0 :                         val_u_h += basis_q[k][i] * apply(u_h, dof_ndindex_I);
    1540              :                     }
    1541              : 
    1542            0 :                     contrib += w[k] * Kokkos::pow(val_u_sol - val_u_h, 2) * absDetDPhi;
    1543              :                 }
    1544            0 :                 local += contrib;
    1545            0 :             },
    1546            0 :             Kokkos::Sum<double>(error));
    1547              : 
    1548              :         // MPI reduce
    1549            0 :         T global_error = 0.0;
    1550            0 :         Comm->allreduce(error, global_error, 1, std::plus<T>());
    1551              : 
    1552            0 :         return Kokkos::sqrt(global_error);
    1553            0 :     }
    1554              : 
    1555              :     template <typename T, unsigned Dim, unsigned Order, typename ElementType,
    1556              :               typename QuadratureType, typename FieldLHS, typename FieldRHS>
    1557            0 :     T LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS, FieldRHS>::computeAvg(
    1558              :         const FieldLHS& u_h) const {
    1559            0 :         if (this->quadrature_m.getOrder() < (2 * Order + 1)) {
    1560              :             // throw exception
    1561            0 :             throw IpplException(
    1562              :                 "LagrangeSpace::computeAvg()",
    1563              :                 "Order of quadrature rule for error computation should be > 2*p + 1");
    1564              :         }
    1565              : 
    1566              :         // List of quadrature weights
    1567            0 :         const Vector<T, QuadratureType::numElementNodes> w =
    1568            0 :             this->quadrature_m.getWeightsForRefElement();
    1569              : 
    1570              :         // List of quadrature nodes
    1571            0 :         const Vector<point_t, QuadratureType::numElementNodes> q =
    1572            0 :             this->quadrature_m.getIntegrationNodesForRefElement();
    1573              : 
    1574              :         // Evaluate the basis functions for the DOF at the quadrature nodes
    1575            0 :         Vector<Vector<T, numElementDOFs>, QuadratureType::numElementNodes> basis_q;
    1576            0 :         for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
    1577            0 :             for (size_t i = 0; i < numElementDOFs; ++i) {
    1578            0 :                 basis_q[k][i] = this->evaluateRefElementShapeFunction(i, q[k]);
    1579              :             }
    1580              :         }
    1581              : 
    1582            0 :         const indices_t zeroNdIndex = Vector<size_t, Dim>(0);
    1583              : 
    1584              :         // Absolute value of det Phi_K
    1585            0 :         const T absDetDPhi = Kokkos::abs(this->ref_element_m.getDeterminantOfTransformationJacobian(
    1586            0 :             this->getElementMeshVertexPoints(zeroNdIndex)));
    1587              : 
    1588              :         // Variable to sum the error to
    1589            0 :         T avg = 0;
    1590              : 
    1591              :         // Get domain information and ghost cells
    1592            0 :         auto ldom        = (u_h.getLayout()).getLocalNDIndex();
    1593            0 :         const int nghost = u_h.getNghost();
    1594              : 
    1595              :         using exec_space  = typename Kokkos::View<const size_t*>::execution_space;
    1596              :         using policy_type = Kokkos::RangePolicy<exec_space>;
    1597              : 
    1598              :         // Loop over elements to compute contributions
    1599            0 :         Kokkos::parallel_reduce(
    1600            0 :             "Compute average over elements", policy_type(0, elementIndices.extent(0)),
    1601            0 :             KOKKOS_CLASS_LAMBDA(size_t index, double& local) {
    1602            0 :                 const size_t elementIndex = elementIndices(index);
    1603            0 :                 const Vector<size_t, numElementDOFs> global_dofs =
    1604            0 :                     this->getGlobalDOFIndices(elementIndex);
    1605              : 
    1606              :                 // contribution of this element to the error
    1607            0 :                 T contrib = 0;
    1608            0 :                 for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
    1609            0 :                     T val_u_h = 0;
    1610            0 :                     for (size_t i = 0; i < numElementDOFs; ++i) {
    1611              :                         // get field index corresponding to this DOF
    1612            0 :                         size_t I           = global_dofs[i];
    1613            0 :                         auto dof_ndindex_I = this->getMeshVertexNDIndex(I);
    1614            0 :                         for (unsigned d = 0; d < Dim; ++d) {
    1615            0 :                             dof_ndindex_I[d] = dof_ndindex_I[d] - ldom[d].first() + nghost;
    1616              :                         }
    1617              : 
    1618              :                         // get field value at DOF and interpolate to q_k
    1619            0 :                         val_u_h += basis_q[k][i] * apply(u_h, dof_ndindex_I);
    1620              :                     }
    1621              : 
    1622            0 :                     contrib += w[k] * val_u_h * absDetDPhi;
    1623              :                 }
    1624            0 :                 local += contrib;
    1625            0 :             },
    1626            0 :             Kokkos::Sum<double>(avg));
    1627              : 
    1628              :         // MPI reduce
    1629            0 :         T global_avg = 0.0;
    1630            0 :         Comm->allreduce(avg, global_avg, 1, std::plus<T>());
    1631              : 
    1632            0 :         return global_avg;
    1633            0 :     }
    1634              : 
    1635              : }  // namespace ippl
        

Generated by: LCOV version 2.0-1