IPPL API Reference
Independent Parallel Particle Layer C++ API
Loading...
Searching...
No Matches
LagrangeSpace.hpp
1
2namespace 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>
9 UniformCartesian<T, Dim>& mesh, ElementType& ref_element, const QuadratureType& quadrature,
10 Layout_t& layout)
11 : FiniteElementSpace<T, Dim, getLagrangeNumElementDOFs(Dim, Order), ElementType,
12 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
19
20 // Initialize the resultField
21 resultField.initialize(mesh, layout);
22 }
23
24 // LagrangeSpace constructor, which calls the FiniteElementSpace constructor.
25 template <typename T, unsigned Dim, unsigned Order, typename ElementType,
26 typename QuadratureType, typename FieldLHS, typename FieldRHS>
28 UniformCartesian<T, Dim>& mesh, ElementType& ref_element, const QuadratureType& quadrature)
29 : FiniteElementSpace<T, Dim, getLagrangeNumElementDOFs(Dim, Order), ElementType,
30 QuadratureType, FieldLHS, FieldRHS>(mesh, ref_element, quadrature) {
31 // Assert that the dimension is either 1, 2 or 3.
32 static_assert(Dim >= 1 && Dim <= 3,
33 "Finite Element space only supports 1D, 2D and 3D meshes");
34 }
35
36 // LagrangeSpace initializer, to be made available to the FEMPoissonSolver
37 // such that we can call it from setRhs.
38 // Sets the correct mesh ad decomposes the elements among ranks according to layout.
39 template <typename T, unsigned Dim, unsigned Order, typename ElementType,
40 typename QuadratureType, typename FieldLHS, typename FieldRHS>
43 {
44 FiniteElementSpace<T, Dim, getLagrangeNumElementDOFs(Dim, Order), ElementType,
45 QuadratureType, FieldLHS, FieldRHS>::setMesh(mesh);
46
47 // Initialize the elementIndices view
48 initializeElementIndices(layout);
49
50 // Initialize the resultField
51 resultField.initialize(mesh, layout);
52 }
53
54 // Initialize element indices Kokkos View by distributing elements among MPI ranks.
55 template <typename T, unsigned Dim, unsigned Order, typename ElementType,
56 typename QuadratureType, typename FieldLHS, typename FieldRHS>
57 void LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
58 FieldRHS>::initializeElementIndices(Layout_t& layout) {
59 const auto& ldom = layout.getLocalNDIndex();
60 int npoints = ldom.size();
61 auto first = ldom.first();
62 auto last = ldom.last();
64
65 for (size_t d = 0; d < Dim; ++d) {
66 bounds[d] = this->nr_m[d] - 1;
67 }
68
69 int upperBoundaryPoints = -1;
70
71 // We iterate over the local domain points, getting the corresponding elements,
72 // while tagging upper boundary points such that they can be removed after.
73 Kokkos::View<size_t*> points("npoints", npoints);
74 Kokkos::View<bool*> is_boundary("is_boundary", npoints);
75 Kokkos::parallel_reduce(
76 "ComputePoints", npoints,
77 KOKKOS_CLASS_LAMBDA(const int i, int& local) {
78 int idx = i;
79 indices_t val;
80 bool isBoundary = false;
81 for (unsigned int d = 0; d < Dim; ++d) {
82 int range = last[d] - first[d] + 1;
83 val[d] = first[d] + (idx % range);
84 idx /= range;
85 if (val[d] == bounds[d]) {
86 isBoundary = true;
87 }
88 }
89 is_boundary(i) = isBoundary;
90 points(i) = this->getElementIndex(val);
91 local += isBoundary;
92 },
93 Kokkos::Sum<int>(upperBoundaryPoints));
94 Kokkos::fence();
95
96 // The elementIndices will be the same array as computed above,
97 // with the tagged upper boundary points removed.
98 int elementsPerRank = npoints - upperBoundaryPoints;
99 elementIndices = Kokkos::View<size_t*>("i", elementsPerRank);
100 Kokkos::View<size_t> index("index");
101
102 if (elementsPerRank > 0) {
103 Kokkos::parallel_for(
104 "CompactElementIndices", npoints, KOKKOS_CLASS_LAMBDA(const int i) {
105 if (!is_boundary(i)) {
106 const size_t idx = Kokkos::atomic_fetch_add(&index(), 1);
107 elementIndices(idx) = points(i);
108 }
109 });
110 }
111 Kokkos::fence();
113
114 // Update resultField and elementIndices according to changed domain decomposition.
115 template <typename T, unsigned Dim, unsigned Order, typename ElementType,
116 typename QuadratureType, typename FieldLHS, typename FieldRHS>
117 void LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
118 FieldRHS>::updateLayout(Layout_t& layout) {
119 // repartition elements
120 initializeElementIndices(layout);
121 // update layout of resultField member variable
122 resultField.updateLayout(layout);
123 }
124
128
129 template <typename T, unsigned Dim, unsigned Order, typename ElementType,
130 typename QuadratureType, typename FieldLHS, typename FieldRHS>
131 KOKKOS_FUNCTION size_t LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
132 FieldRHS>::numGlobalDOFs() const {
133 size_t num_global_dofs = 1;
134 for (size_t d = 0; d < Dim; ++d) {
135 num_global_dofs *= this->nr_m[d] * Order;
136 }
137
138 return num_global_dofs;
139 }
140
141 template <typename T, unsigned Dim, unsigned Order, typename ElementType,
142 typename QuadratureType, typename FieldLHS, typename FieldRHS>
143 KOKKOS_FUNCTION
145 (const size_t& elementIndex, const size_t& globalDOFIndex) const {
146 static_assert(Dim == 1 || Dim == 2 || Dim == 3, "Dim must be 1, 2 or 3");
147 // TODO fix not order independent, only works for order 1
148 static_assert(Order == 1, "Only order 1 is supported at the moment");
149
150 // Get all the global DOFs for the element
151 const Vector<size_t, numElementDOFs> global_dofs =
153
154 // Find the global DOF in the vector and return the local DOF index
155 // Note: It is important that this only works because the global_dofs
156 // are already arranged in the correct order from getGlobalDOFIndices
157 for (size_t i = 0; i < global_dofs.dim; ++i) {
158 if (global_dofs[i] == globalDOFIndex) {
159 return i;
160 }
161 }
162 // commented this due to this being on device
163 // however, it would be good to throw an error in this case
164 //throw IpplException("LagrangeSpace::getLocalDOFIndex()",
165 // "FEM Lagrange Space: Global DOF not found in specified element");
166 return 0;
167 }
168
169 template <typename T, unsigned Dim, unsigned Order, typename ElementType,
170 typename QuadratureType, typename FieldLHS, typename FieldRHS>
171 KOKKOS_FUNCTION size_t
172 LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
173 FieldRHS>::getGlobalDOFIndex(const size_t& elementIndex,
174 const size_t& localDOFIndex) const {
175 const auto global_dofs = this->LagrangeSpace::getGlobalDOFIndices(elementIndex);
176
177 return global_dofs[localDOFIndex];
178 }
179
180 template <typename T, unsigned Dim, unsigned Order, typename ElementType,
181 typename QuadratureType, typename FieldLHS, typename FieldRHS>
182 KOKKOS_FUNCTION Vector<size_t, LagrangeSpace<T, Dim, Order, ElementType, QuadratureType,
183 FieldLHS, FieldRHS>::numElementDOFs>
184 LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
185 FieldRHS>::getLocalDOFIndices() const {
188 for (size_t dof = 0; dof < numElementDOFs; ++dof) {
189 localDOFs[dof] = dof;
190 }
191
192 return localDOFs;
193 }
194
195 template <typename T, unsigned Dim, unsigned Order, typename ElementType,
196 typename QuadratureType, typename FieldLHS, typename FieldRHS>
197 KOKKOS_FUNCTION Vector<size_t, LagrangeSpace<T, Dim, Order, ElementType, QuadratureType,
198 FieldLHS, FieldRHS>::numElementDOFs>
199 LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
200 FieldRHS>::getGlobalDOFIndices(const size_t& elementIndex) const {
201 Vector<size_t, numElementDOFs> globalDOFs(0);
202
203 // get element pos
204 indices_t elementPos = this->getElementNDIndex(elementIndex);
205
206 // Compute the vector to multiply the ndindex with
208 for (size_t d = 1; d < dim; ++d) {
209 for (size_t d2 = d; d2 < Dim; ++d2) {
210 vec[d2] *= this->nr_m[d - 1];
211 }
212 }
213 vec *= Order; // Multiply each dimension by the order
214 size_t smallestGlobalDOF = elementPos.dot(vec);
215
216 // Add the vertex DOFs
217 globalDOFs[0] = smallestGlobalDOF;
218 globalDOFs[1] = smallestGlobalDOF + Order;
219
220 if constexpr (Dim >= 2) {
221 globalDOFs[2] = globalDOFs[1] + this->nr_m[0] * Order;
222 globalDOFs[3] = globalDOFs[0] + this->nr_m[0] * Order;
223 }
224 if constexpr (Dim >= 3) {
225 globalDOFs[4] = globalDOFs[0] + this->nr_m[1] * this->nr_m[0] * Order;
226 globalDOFs[5] = globalDOFs[1] + this->nr_m[1] * this->nr_m[0] * Order;
227 globalDOFs[6] = globalDOFs[2] + this->nr_m[1] * this->nr_m[0] * Order;
228 globalDOFs[7] = globalDOFs[3] + this->nr_m[1] * this->nr_m[0] * Order;
229 }
230
231 if constexpr (Order > 1) {
232 // If the order is greater than 1, there are edge and face DOFs, otherwise the work is
233 // done
234
235 // Add the edge DOFs
236 if constexpr (Dim >= 2) {
237 for (size_t i = 0; i < Order - 1; ++i) {
238 globalDOFs[8 + i] = globalDOFs[0] + i + 1;
239 globalDOFs[8 + Order - 1 + i] = globalDOFs[1] + (i + 1) * this->nr_m[1];
240 globalDOFs[8 + 2 * (Order - 1) + i] = globalDOFs[2] - (i + 1);
241 globalDOFs[8 + 3 * (Order - 1) + i] = globalDOFs[3] - (i + 1) * this->nr_m[1];
242 }
243 }
244 if constexpr (Dim >= 3) {
245 // TODO
246 }
247
248 // Add the face DOFs
249 if constexpr (Dim >= 2) {
250 for (size_t i = 0; i < Order - 1; ++i) {
251 for (size_t j = 0; j < Order - 1; ++j) {
252 // TODO CHECK
253 globalDOFs[8 + 4 * (Order - 1) + i * (Order - 1) + j] =
254 globalDOFs[0] + (i + 1) + (j + 1) * this->nr_m[1];
255 globalDOFs[8 + 4 * (Order - 1) + (Order - 1) * (Order - 1) + i * (Order - 1)
256 + j] = globalDOFs[1] + (i + 1) + (j + 1) * this->nr_m[1];
257 globalDOFs[8 + 4 * (Order - 1) + 2 * (Order - 1) * (Order - 1)
258 + i * (Order - 1) + j] =
259 globalDOFs[2] - (i + 1) + (j + 1) * this->nr_m[1];
260 globalDOFs[8 + 4 * (Order - 1) + 3 * (Order - 1) * (Order - 1)
261 + i * (Order - 1) + j] =
262 globalDOFs[3] - (i + 1) + (j + 1) * this->nr_m[1];
263 }
264 }
265 }
266 }
267
268 return globalDOFs;
269 }
270
274
275 template <typename T, unsigned Dim, unsigned Order, typename ElementType,
276 typename QuadratureType, typename FieldLHS, typename FieldRHS>
277 KOKKOS_FUNCTION T
280 const size_t& localDOF,
281 const LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
282 FieldRHS>::point_t& localPoint) const {
283 static_assert(Order == 1, "Only order 1 is supported at the moment");
284 // Assert that the local vertex index is valid.
285 assert(localDOF < numElementDOFs
286 && "The local vertex index is invalid"); // TODO assumes 1st order Lagrange
287
288 assert(this->ref_element_m.isPointInRefElement(localPoint)
289 && "Point is not in reference element");
290
291 // Get the local vertex indices for the local vertex index.
292 // TODO fix not order independent, only works for order 1
293 const point_t ref_element_point = this->ref_element_m.getLocalVertices()[localDOF];
294
295 // The variable that accumulates the product of the shape functions.
296 T product = 1;
297
298 for (size_t d = 0; d < Dim; d++) {
299 if (localPoint[d] < ref_element_point[d]) {
300 product *= localPoint[d];
301 } else {
302 product *= 1.0 - localPoint[d];
303 }
304 }
305
306 return product;
307 }
308
309 template <typename T, unsigned Dim, unsigned Order, typename ElementType,
310 typename QuadratureType, typename FieldLHS, typename FieldRHS>
311 KOKKOS_FUNCTION typename LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
312 FieldRHS>::point_t
315 const size_t& localDOF,
316 const LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
317 FieldRHS>::point_t& localPoint) const {
318 // TODO fix not order independent, only works for order 1
319 static_assert(Order == 1 && "Only order 1 is supported at the moment");
320
321 // Assert that the local vertex index is valid.
322 assert(localDOF < numElementDOFs && "The local vertex index is invalid");
323
324 assert(this->ref_element_m.isPointInRefElement(localPoint)
325 && "Point is not in reference element");
326
327 // Get the local dof nd_index
328 const vertex_points_t local_vertex_points = this->ref_element_m.getLocalVertices();
329
330 const point_t& local_vertex_point = local_vertex_points[localDOF];
331
332 point_t gradient(1);
333
334 // To construct the gradient we need to loop over the dimensions and multiply the
335 // shape functions in each dimension except the current one. The one of the current
336 // dimension is replaced by the derivative of the shape function in that dimension,
337 // which is either 1 or -1.
338 for (size_t d = 0; d < Dim; d++) {
339 // The variable that accumulates the product of the shape functions.
340 T product = 1;
341
342 for (size_t d2 = 0; d2 < Dim; d2++) {
343 if (d2 == d) {
344 if (localPoint[d] < local_vertex_point[d]) {
345 product *= 1;
346 } else {
347 product *= -1;
348 }
349 } else {
350 if (localPoint[d2] < local_vertex_point[d2]) {
351 product *= localPoint[d2];
352 } else {
353 product *= 1.0 - localPoint[d2];
354 }
355 }
356 }
357
358 gradient[d] = product;
359 }
360
361 return gradient;
362 }
363
367
368 template <typename T, unsigned Dim, unsigned Order, typename ElementType,
369 typename QuadratureType, typename FieldLHS, typename FieldRHS>
370 template <typename F>
371 FieldLHS LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
372 FieldRHS>::evaluateAx(FieldLHS& field, F& evalFunction) {
373 Inform m("");
374
375 // declare timers
376 static IpplTimings::TimerRef evalAx = IpplTimings::getTimer("evaluateAx");
377 static IpplTimings::TimerRef evalAx_outer = IpplTimings::getTimer("evaluateAx: outer loop");
378 static IpplTimings::TimerRef evalAx_bc = IpplTimings::getTimer("evaluateAx: BCs");
379 static IpplTimings::TimerRef evalAx_setup = IpplTimings::getTimer("evaluateAx: setup");
380 static IpplTimings::TimerRef accumHalo = IpplTimings::getTimer("evaluateAx: accumHalo");
381
382 // start a timer
383 IpplTimings::startTimer(evalAx);
384 IpplTimings::startTimer(evalAx_setup);
385
386 // set result field to 0
387 resultField = 0;
388
389 // List of quadrature weights
391 this->quadrature_m.getWeightsForRefElement();
392
393 // List of quadrature nodes
395 this->quadrature_m.getIntegrationNodesForRefElement();
396
397 // Basis function values and gradients at the quadrature nodes
398 Vector<Vector<T, numElementDOFs>, QuadratureType::numElementNodes> b_q;
399 Vector<Vector<point_t, numElementDOFs>, QuadratureType::numElementNodes> grad_b_q;
400 for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
401 for (size_t i = 0; i < numElementDOFs; ++i) {
402 b_q[k][i] = this->evaluateRefElementShapeFunction(i, q[k]);
403 grad_b_q[k][i] = this->evaluateRefElementShapeFunctionGradient(i, q[k]);
404 }
405 }
406
407 // Make local element matrix -- does not change through the element mesh
408 // Element matrix
409 Vector<Vector<T, numElementDOFs>, numElementDOFs> A_K;
410
411 // 1. Compute the Galerkin element matrix A_K
412 for (size_t i = 0; i < numElementDOFs; ++i) {
413 for (size_t j = 0; j < numElementDOFs; ++j) {
414 A_K[i][j] = 0.0;
415 for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
416 A_K[i][j] += w[k] * evalFunction(
417 i, j, QuadratureData<T, point_t, numElementDOFs>{b_q[k], grad_b_q[k]});
418 }
419 }
420 }
421
422 // Get field data and atomic result data,
423 // since it will be added to during the kokkos loop
424 ViewType view = field.getView();
425 AtomicViewType resultView = resultField.getView();
426
427 // Get boundary conditions from field
428 BConds<FieldLHS, Dim>& bcField = field.getFieldBC();
429 FieldBC bcType = bcField[0]->getBCType();
430
431 // Get domain and ghost cell information
432 auto ldom = (field.getLayout()).getLocalNDIndex();
433 // Get number of ghost cells in field
434 const int nghost = field.getNghost();
435
436 using exec_space = typename Kokkos::View<const size_t*>::execution_space;
437 using policy_type = Kokkos::RangePolicy<exec_space>;
438
439 IpplTimings::stopTimer(evalAx_setup);
440
441 // start a timer
442 IpplTimings::startTimer(evalAx_outer);
443
444 // Loop over elements to compute contributions
445 Kokkos::parallel_for(
446 "Loop over elements", policy_type(0, elementIndices.extent(0)),
447 KOKKOS_CLASS_LAMBDA(const size_t index) {
448 const size_t elementIndex = elementIndices(index);
449 const Vector<size_t, numElementDOFs> global_dofs =
450 this->LagrangeSpace::getGlobalDOFIndices(elementIndex);
451 Vector<indices_t, numElementDOFs> global_dof_ndindices;
452
453 for (size_t i = 0; i < numElementDOFs; ++i) {
454 global_dof_ndindices[i] = this->getMeshVertexNDIndex(global_dofs[i]);
455 }
456
457 // local DOF indices (both i and j go from 0 to numDOFs-1 in the element)
458 size_t i, j;
459
460 // global DOF n-dimensional indices (Vector of N indices representing indices in
461 // each dimension)
462 indices_t I_nd, J_nd;
463
464 // 2. Compute the contribution to resultAx = A*x with A_K
465 for (i = 0; i < numElementDOFs; ++i) {
466 I_nd = global_dof_ndindices[i];
467
468 // Handle boundary DOFs
469 // If Zero Dirichlet BCs, skip this DOF
470 // If Constant Dirichlet BCs, identity
471 if ((bcType == CONSTANT_FACE) && (this->isDOFOnBoundary(I_nd))) {
472 for (unsigned d = 0; d < Dim; ++d) {
473 I_nd[d] = I_nd[d] - ldom[d].first() + nghost;
474 }
475 apply(resultView, I_nd) = apply(view, I_nd);
476 continue;
477 } else if ((bcType == ZERO_FACE) && (this->isDOFOnBoundary(I_nd))) {
478 continue;
479 }
480
481 // get the appropriate index for the Kokkos view of the field
482 for (unsigned d = 0; d < Dim; ++d) {
483 I_nd[d] = I_nd[d] - ldom[d].first() + nghost;
484 }
485
486 for (j = 0; j < numElementDOFs; ++j) {
487 J_nd = global_dof_ndindices[j];
488
489 // Skip boundary DOFs (Zero & Constant Dirichlet BCs)
490 if (((bcType == ZERO_FACE) || (bcType == CONSTANT_FACE))
491 && this->isDOFOnBoundary(J_nd)) {
492 continue;
493 }
494
495 // get the appropriate index for the Kokkos view of the field
496 for (unsigned d = 0; d < Dim; ++d) {
497 J_nd[d] = J_nd[d] - ldom[d].first() + nghost;
498 }
499
500 apply(resultView, I_nd) += A_K[i][j] * apply(view, J_nd);
501 }
502 }
503 });
504 IpplTimings::stopTimer(evalAx_outer);
505
506 // start a timer
507 IpplTimings::startTimer(evalAx_bc);
508
509 if (bcType == PERIODIC_FACE) {
510 IpplTimings::startTimer(accumHalo);
511 resultField.accumulateHalo();
512 IpplTimings::stopTimer(accumHalo);
513 bcField.apply(resultField);
514 bcField.assignGhostToPhysical(resultField);
515 } else {
516 resultField.accumulateHalo_noghost();
517 }
518
519 IpplTimings::stopTimer(evalAx_bc);
520 IpplTimings::stopTimer(evalAx);
521
522 return resultField;
523 }
524
525 template <typename T, unsigned Dim, unsigned Order, typename ElementType,
526 typename QuadratureType, typename FieldLHS, typename FieldRHS>
527 template <typename F>
528 FieldLHS LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
529 FieldRHS>::evaluateAx_lower(FieldLHS& field, F& evalFunction) {
530 Inform m("");
531
532 // declare timer
533 static IpplTimings::TimerRef evalAx_lower = IpplTimings::getTimer("evaluateAxLower");
534
535 // start a timer
536 IpplTimings::startTimer(evalAx_lower);
537
538 // set result field to 0
539 resultField = 0;
540
541 // List of quadrature weights
543 this->quadrature_m.getWeightsForRefElement();
544
545 // List of quadrature nodes
547 this->quadrature_m.getIntegrationNodesForRefElement();
548
549 // Basis function values and gradients at the quadrature nodes
550 Vector<Vector<T, numElementDOFs>, QuadratureType::numElementNodes> b_q;
551 Vector<Vector<point_t, numElementDOFs>, QuadratureType::numElementNodes> grad_b_q;
552 for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
553 for (size_t i = 0; i < numElementDOFs; ++i) {
554 b_q[k][i] = this->evaluateRefElementShapeFunction(i, q[k]);
555 grad_b_q[k][i] = this->evaluateRefElementShapeFunctionGradient(i, q[k]);
556 }
557 }
558
559 // Make local element matrix -- does not change through the element mesh
560 // Element matrix
561 Vector<Vector<T, numElementDOFs>, numElementDOFs> A_K;
562
563 // 1. Compute the Galerkin element matrix A_K
564 for (size_t i = 0; i < numElementDOFs; ++i) {
565 for (size_t j = 0; j < numElementDOFs; ++j) {
566 A_K[i][j] = 0.0;
567 for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
568 A_K[i][j] += w[k] * evalFunction(
569 i, j, QuadratureData<T, point_t, numElementDOFs>{b_q[k], grad_b_q[k]});
570 }
571 }
572 }
573
574 // Get field data and atomic result data,
575 // since it will be added to during the kokkos loop
576 ViewType view = field.getView();
577 AtomicViewType resultView = resultField.getView();
578
579 // Get boundary conditions from field
580 BConds<FieldLHS, Dim>& bcField = field.getFieldBC();
581 FieldBC bcType = bcField[0]->getBCType();
582
583 // Get domain information
584 auto ldom = (field.getLayout()).getLocalNDIndex();
585 // Get number of ghost cells in field
586 const int nghost = field.getNghost();
587
588 using exec_space = typename Kokkos::View<const size_t*>::execution_space;
589 using policy_type = Kokkos::RangePolicy<exec_space>;
590
591 // Loop over elements to compute contributions
592 Kokkos::parallel_for(
593 "Loop over elements", policy_type(0, elementIndices.extent(0)),
594 KOKKOS_CLASS_LAMBDA(const size_t index) {
595 const size_t elementIndex = elementIndices(index);
596 const Vector<size_t, numElementDOFs> global_dofs =
597 this->LagrangeSpace::getGlobalDOFIndices(elementIndex);
598 Vector<indices_t, numElementDOFs> global_dof_ndindices;
599
600 for (size_t i = 0; i < numElementDOFs; ++i) {
601 global_dof_ndindices[i] = this->getMeshVertexNDIndex(global_dofs[i]);
602 }
603
604 // local DOF indices
605 size_t i, j;
606
607 // global DOF n-dimensional indices (Vector of N indices representing indices in
608 // each dimension)
609 indices_t I_nd, J_nd;
610
611 // 2. Compute the contribution to resultAx = A*x with A_K
612 for (i = 0; i < numElementDOFs; ++i) {
613 I_nd = global_dof_ndindices[i];
614
615 // Handle boundary DOFs
616 // If Zero Dirichlet BCs, skip this DOF
617 // If Constant Dirichlet BCs, identity
618 if ((bcType == CONSTANT_FACE) && (this->isDOFOnBoundary(I_nd))) {
619 for (unsigned d = 0; d < Dim; ++d) {
620 I_nd[d] = I_nd[d] - ldom[d].first() + nghost;
621 }
622 apply(resultView, I_nd) = apply(view, I_nd);
623 continue;
624 } else if ((bcType == ZERO_FACE) && (this->isDOFOnBoundary(I_nd))) {
625 continue;
626 }
627
628 // get the appropriate index for the Kokkos view of the field
629 for (unsigned d = 0; d < Dim; ++d) {
630 I_nd[d] = I_nd[d] - ldom[d].first() + nghost;
631 }
632
633 for (j = 0; j < numElementDOFs; ++j) {
634 J_nd = global_dof_ndindices[j];
635
636 if (global_dofs[i] >= global_dofs[j]) {
637 continue;
638 }
639
640 // Skip boundary DOFs (Zero & Constant Dirichlet BCs)
641 if (((bcType == ZERO_FACE) || (bcType == CONSTANT_FACE))
642 && this->isDOFOnBoundary(J_nd)) {
643 continue;
644 }
645
646 // get the appropriate index for the Kokkos view of the field
647 for (unsigned d = 0; d < Dim; ++d) {
648 J_nd[d] = J_nd[d] - ldom[d].first() + nghost;
649 }
650
651 apply(resultView, I_nd) += A_K[i][j] * apply(view, J_nd);
652 }
653 }
654 });
655
656 if (bcType == PERIODIC_FACE) {
657 resultField.accumulateHalo();
658 bcField.apply(resultField);
659 bcField.assignGhostToPhysical(resultField);
660 } else {
661 resultField.accumulateHalo_noghost();
662 }
663
664 IpplTimings::stopTimer(evalAx_lower);
665
666 return resultField;
667 }
668
669 template <typename T, unsigned Dim, unsigned Order, typename ElementType,
670 typename QuadratureType, typename FieldLHS, typename FieldRHS>
671 template <typename F>
672 FieldLHS LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
673 FieldRHS>::evaluateAx_upper(FieldLHS& field, F& evalFunction) {
674 Inform m("");
675
676 // declare timer
677 static IpplTimings::TimerRef evalAx_upper = IpplTimings::getTimer("evaluateAxUpper");
678
679 // start a timer
680 IpplTimings::startTimer(evalAx_upper);
681
682 // set result field to 0
683 resultField = 0;
684
685 // List of quadrature weights
687 this->quadrature_m.getWeightsForRefElement();
688
689 // List of quadrature nodes
691 this->quadrature_m.getIntegrationNodesForRefElement();
692
693 // Basis function values and gradients at the quadrature nodes
694 Vector<Vector<T, numElementDOFs>, QuadratureType::numElementNodes> b_q;
695 Vector<Vector<point_t, numElementDOFs>, QuadratureType::numElementNodes> grad_b_q;
696 for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
697 for (size_t i = 0; i < numElementDOFs; ++i) {
698 b_q[k][i] = this->evaluateRefElementShapeFunction(i, q[k]);
699 grad_b_q[k][i] = this->evaluateRefElementShapeFunctionGradient(i, q[k]);
700 }
701 }
702
703 // Make local element matrix -- does not change through the element mesh
704 // Element matrix
705 Vector<Vector<T, numElementDOFs>, numElementDOFs> A_K;
706
707 // 1. Compute the Galerkin element matrix A_K
708 for (size_t i = 0; i < numElementDOFs; ++i) {
709 for (size_t j = 0; j < numElementDOFs; ++j) {
710 A_K[i][j] = 0.0;
711 for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
712 A_K[i][j] += w[k] * evalFunction(
713 i, j, QuadratureData<T, point_t, numElementDOFs>{b_q[k], grad_b_q[k]});
714 }
715 }
716 }
717
718 // Get field data and atomic result data,
719 // since it will be added to during the kokkos loop
720 ViewType view = field.getView();
721 AtomicViewType resultView = resultField.getView();
722
723 // Get boundary conditions from field
724 BConds<FieldLHS, Dim>& bcField = field.getFieldBC();
725 FieldBC bcType = bcField[0]->getBCType();
726
727 // Get domain information
728 auto ldom = (field.getLayout()).getLocalNDIndex();
729 // Get number of ghost cells in field
730 const int nghost = field.getNghost();
731
732 using exec_space = typename Kokkos::View<const size_t*>::execution_space;
733 using policy_type = Kokkos::RangePolicy<exec_space>;
734
735 // Loop over elements to compute contributions
736 Kokkos::parallel_for(
737 "Loop over elements", policy_type(0, elementIndices.extent(0)),
738 KOKKOS_CLASS_LAMBDA(const size_t index) {
739 const size_t elementIndex = elementIndices(index);
740 const Vector<size_t, numElementDOFs> global_dofs =
741 this->LagrangeSpace::getGlobalDOFIndices(elementIndex);
742 Vector<indices_t, numElementDOFs> global_dof_ndindices;
743
744 for (size_t i = 0; i < numElementDOFs; ++i) {
745 global_dof_ndindices[i] = this->getMeshVertexNDIndex(global_dofs[i]);
746 }
747
748 // local DOF indices
749 size_t i, j;
750
751 // global DOF n-dimensional indices (Vector of N indices representing indices in
752 // each dimension)
753 indices_t I_nd, J_nd;
754
755 // 2. Compute the contribution to resultAx = A*x with A_K
756 for (i = 0; i < numElementDOFs; ++i) {
757 I_nd = global_dof_ndindices[i];
758
759 // Handle boundary DOFs
760 // If Zero Dirichlet BCs, skip this DOF
761 // If Constant Dirichlet BCs, identity
762 if ((bcType == CONSTANT_FACE) && (this->isDOFOnBoundary(I_nd))) {
763 for (unsigned d = 0; d < Dim; ++d) {
764 I_nd[d] = I_nd[d] - ldom[d].first() + nghost;
765 }
766 apply(resultView, I_nd) = apply(view, I_nd);
767 continue;
768 } else if ((bcType == ZERO_FACE) && (this->isDOFOnBoundary(I_nd))) {
769 continue;
770 }
771
772 // get the appropriate index for the Kokkos view of the field
773 for (unsigned d = 0; d < Dim; ++d) {
774 I_nd[d] = I_nd[d] - ldom[d].first() + nghost;
775 }
776
777 for (j = 0; j < numElementDOFs; ++j) {
778 J_nd = global_dof_ndindices[j];
779
780 if (global_dofs[i] <= global_dofs[j]) {
781 continue;
782 }
783
784 // Skip boundary DOFs (Zero & Constant Dirichlet BCs)
785 if (((bcType == ZERO_FACE) || (bcType == CONSTANT_FACE))
786 && this->isDOFOnBoundary(J_nd)) {
787 continue;
788 }
789
790 // get the appropriate index for the Kokkos view of the field
791 for (unsigned d = 0; d < Dim; ++d) {
792 J_nd[d] = J_nd[d] - ldom[d].first() + nghost;
793 }
794
795 apply(resultView, I_nd) += A_K[i][j] * apply(view, J_nd);
796 }
797 }
798 });
799
800 if (bcType == PERIODIC_FACE) {
801 resultField.accumulateHalo();
802 bcField.apply(resultField);
803 bcField.assignGhostToPhysical(resultField);
804 } else {
805 resultField.accumulateHalo_noghost();
806 }
807
808 IpplTimings::stopTimer(evalAx_upper);
809
810 return resultField;
811 }
812
813 template <typename T, unsigned Dim, unsigned Order, typename ElementType,
814 typename QuadratureType, typename FieldLHS, typename FieldRHS>
815 template <typename F>
816 FieldLHS LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
817 FieldRHS>::evaluateAx_upperlower(FieldLHS& field, F& evalFunction) {
818 Inform m("");
819
820 // declare timer
821 static IpplTimings::TimerRef evalAx_upperlower = IpplTimings::getTimer("evaluateAxUpperLower");
822
823 // start a timer
824 IpplTimings::startTimer(evalAx_upperlower);
825
826 // set result field to 0
827 resultField = 0;
828
829 // List of quadrature weights
831 this->quadrature_m.getWeightsForRefElement();
832
833 // List of quadrature nodes
835 this->quadrature_m.getIntegrationNodesForRefElement();
836
837 // TODO move outside of evaluateAx (I think it is possible for other problems as well)
838 // Basis function values and gradients at the quadrature nodes
839 Vector<Vector<T, numElementDOFs>, QuadratureType::numElementNodes> b_q;
840 Vector<Vector<point_t, numElementDOFs>, QuadratureType::numElementNodes> grad_b_q;
841 for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
842 for (size_t i = 0; i < numElementDOFs; ++i) {
843 b_q[k][i] = this->evaluateRefElementShapeFunction(i, q[k]);
844 grad_b_q[k][i] = this->evaluateRefElementShapeFunctionGradient(i, q[k]);
845 }
846 }
847
848 // Make local element matrix -- does not change through the element mesh
849 // Element matrix
850 Vector<Vector<T, numElementDOFs>, numElementDOFs> A_K;
851
852 // 1. Compute the Galerkin element matrix A_K
853 for (size_t i = 0; i < numElementDOFs; ++i) {
854 for (size_t j = 0; j < numElementDOFs; ++j) {
855 A_K[i][j] = 0.0;
856 for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
857 A_K[i][j] += w[k] * evalFunction(
858 i, j, QuadratureData<T, point_t, numElementDOFs>{b_q[k], grad_b_q[k]});
859 }
860 }
861 }
862
863 // Get field data and atomic result data,
864 // since it will be added to during the kokkos loop
865 ViewType view = field.getView();
866 AtomicViewType resultView = resultField.getView();
867
868 // Get boundary conditions from field
869 BConds<FieldLHS, Dim>& bcField = field.getFieldBC();
870 FieldBC bcType = bcField[0]->getBCType();
871
872 // Get domain information
873 auto ldom = (field.getLayout()).getLocalNDIndex();
874 // Get number of ghost cells in field
875 const int nghost = field.getNghost();
876
877 using exec_space = typename Kokkos::View<const size_t*>::execution_space;
878 using policy_type = Kokkos::RangePolicy<exec_space>;
879
880 // Loop over elements to compute contributions
881 Kokkos::parallel_for(
882 "Loop over elements", policy_type(0, elementIndices.extent(0)),
883 KOKKOS_CLASS_LAMBDA(const size_t index) {
884 const size_t elementIndex = elementIndices(index);
885 const Vector<size_t, numElementDOFs> global_dofs =
886 this->LagrangeSpace::getGlobalDOFIndices(elementIndex);
887 Vector<indices_t, numElementDOFs> global_dof_ndindices;
888
889 for (size_t i = 0; i < numElementDOFs; ++i) {
890 global_dof_ndindices[i] = this->getMeshVertexNDIndex(global_dofs[i]);
891 }
892
893 // local DOF indices
894 size_t i, j;
895
896 // global DOF n-dimensional indices (Vector of N indices representing indices in
897 // each dimension)
898 indices_t I_nd, J_nd;
899
900 // 2. Compute the contribution to resultAx = A*x with A_K
901 for (i = 0; i < numElementDOFs; ++i) {
902 I_nd = global_dof_ndindices[i];
903
904 // Handle boundary DOFs
905 // If Zero Dirichlet BCs, skip this DOF
906 // If Constant Dirichlet BCs, identity
907 if ((bcType == CONSTANT_FACE) && (this->isDOFOnBoundary(I_nd))) {
908 for (unsigned d = 0; d < Dim; ++d) {
909 I_nd[d] = I_nd[d] - ldom[d].first() + nghost;
910 }
911 apply(resultView, I_nd) = apply(view, I_nd);
912 continue;
913 } else if ((bcType == ZERO_FACE) && (this->isDOFOnBoundary(I_nd))) {
914 continue;
915 }
916
917 // get the appropriate index for the Kokkos view of the field
918 for (unsigned d = 0; d < Dim; ++d) {
919 I_nd[d] = I_nd[d] - ldom[d].first() + nghost;
920 }
921
922 for (j = 0; j < i; ++j) {
923 J_nd = global_dof_ndindices[j];
924
925 // Skip boundary DOFs (Zero & Constant Dirichlet BCs)
926 if (((bcType == ZERO_FACE) || (bcType == CONSTANT_FACE))
927 && this->isDOFOnBoundary(J_nd)) {
928 continue;
929 }
930
931 // get the appropriate index for the Kokkos view of the field
932 for (unsigned d = 0; d < Dim; ++d) {
933 J_nd[d] = J_nd[d] - ldom[d].first() + nghost;
934 }
935
936 apply(resultView, I_nd) += A_K[i][j] * apply(view, J_nd);
937 apply(resultView, J_nd) += A_K[j][i] * apply(view, I_nd);
938 }
939 }
940 });
941
942 if (bcType == PERIODIC_FACE) {
943 resultField.accumulateHalo();
944 bcField.apply(resultField);
945 bcField.assignGhostToPhysical(resultField);
946 } else {
947 resultField.accumulateHalo_noghost();
948 }
949
950 IpplTimings::stopTimer(evalAx_upperlower);
951
952 return resultField;
953 }
954
955 template <typename T, unsigned Dim, unsigned Order, typename ElementType,
956 typename QuadratureType, typename FieldLHS, typename FieldRHS>
957 template <typename F>
958 FieldLHS LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
959 FieldRHS>::evaluateAx_inversediag(FieldLHS& field, F& evalFunction) {
960 Inform m("");
961
962 // declare timer
963 static IpplTimings::TimerRef evalAx_invdiag = IpplTimings::getTimer("evaluateAxInvDiag");
964
965 // start a timer
966 IpplTimings::startTimer(evalAx_invdiag);
967
968 // set result field to 0
969 resultField = 0;
970
971 // List of quadrature weights
973 this->quadrature_m.getWeightsForRefElement();
974
975 // List of quadrature nodes
977 this->quadrature_m.getIntegrationNodesForRefElement();
978
979 // Basis function values and gradients at the quadrature nodes
980 Vector<Vector<T, numElementDOFs>, QuadratureType::numElementNodes> b_q;
981 Vector<Vector<point_t, numElementDOFs>, QuadratureType::numElementNodes> grad_b_q;
982 for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
983 for (size_t i = 0; i < numElementDOFs; ++i) {
984 b_q[k][i] = this->evaluateRefElementShapeFunction(i, q[k]);
985 grad_b_q[k][i] = this->evaluateRefElementShapeFunctionGradient(i, q[k]);
986 }
987 }
988
989 // Make local element matrix -- does not change through the element mesh
990 // Element matrix
992
993 // 1. Compute the Galerkin element matrix A_K
994 for (size_t i = 0; i < numElementDOFs; ++i) {
995 A_K_diag[i] = 0.0;
996 for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
997 A_K_diag[i] += w[k] * evalFunction(
998 i, i, QuadratureData<T, point_t, numElementDOFs>{b_q[k], grad_b_q[k]});
999 }
1000 }
1001
1002 // Get field data and atomic result data,
1003 // since it will be added to during the kokkos loop
1004 ViewType view = field.getView();
1005 AtomicViewType resultView = resultField.getView();
1006
1007 // Get boundary conditions from field
1008 BConds<FieldLHS, Dim>& bcField = field.getFieldBC();
1009 FieldBC bcType = bcField[0]->getBCType();
1010
1011 // Get domain information
1012 auto ldom = (field.getLayout()).getLocalNDIndex();
1013 // Get number of ghost cells in field
1014 const int nghost = field.getNghost();
1015
1016 using exec_space = typename Kokkos::View<const size_t*>::execution_space;
1017 using policy_type = Kokkos::RangePolicy<exec_space>;
1018
1019 // Loop over elements to compute contributions
1020 Kokkos::parallel_for(
1021 "Loop over elements", policy_type(0, elementIndices.extent(0)),
1022 KOKKOS_CLASS_LAMBDA(const size_t index) {
1023 const size_t elementIndex = elementIndices(index);
1024 const Vector<size_t, numElementDOFs> global_dofs =
1025 this->LagrangeSpace::getGlobalDOFIndices(elementIndex);
1026 Vector<indices_t, numElementDOFs> global_dof_ndindices;
1027
1028 for (size_t i = 0; i < numElementDOFs; ++i) {
1029 global_dof_ndindices[i] = this->getMeshVertexNDIndex(global_dofs[i]);
1030 }
1031
1032 // local DOF indices
1033 size_t i;
1034
1035 // global DOF n-dimensional indices (Vector of N indices representing indices in
1036 // each dimension)
1037 indices_t I_nd;
1038
1039 // 2. Compute the contribution to resultAx = A*x with A_K
1040 for (i = 0; i < numElementDOFs; ++i) {
1041 I_nd = global_dof_ndindices[i];
1042
1043 // Handle boundary DOFs
1044 // If Zero Dirichlet BCs, skip this DOF
1045 // If Constant Dirichlet BCs, identity
1046 if ((bcType == CONSTANT_FACE) && (this->isDOFOnBoundary(I_nd))) {
1047 for (unsigned d = 0; d < Dim; ++d) {
1048 I_nd[d] = I_nd[d] - ldom[d].first() + nghost;
1049 }
1050 apply(resultView, I_nd) = 1.0;
1051 continue;
1052 } else if ((bcType == ZERO_FACE) && (this->isDOFOnBoundary(I_nd))) {
1053 continue;
1054 }
1055
1056 // get the appropriate index for the Kokkos view of the field
1057 for (unsigned d = 0; d < Dim; ++d) {
1058 I_nd[d] = I_nd[d] - ldom[d].first() + nghost;
1059 }
1060 // sum up all contributions of element matrix
1061 apply(resultView, I_nd) += A_K_diag[i];
1062 }
1063 });
1064
1065 if (bcType == PERIODIC_FACE) {
1066 resultField.accumulateHalo();
1067 bcField.apply(resultField);
1068 bcField.assignGhostToPhysical(resultField);
1069 } else {
1070 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 ippl::parallel_for("Loop over result view to apply inverse", field.getFieldRangePolicy(),
1076 KOKKOS_LAMBDA(const index_array_type& args) {
1077 if (apply(resultView, args) != 0.0) {
1078 apply(resultView, args) = (1.0 / apply(resultView, args)) * apply(view, args);
1079 }
1080 });
1081 IpplTimings::stopTimer(evalAx_invdiag);
1082
1083 return resultField;
1084 }
1085
1086 template <typename T, unsigned Dim, unsigned Order, typename ElementType,
1087 typename QuadratureType, typename FieldLHS, typename FieldRHS>
1088 template <typename F>
1089 FieldLHS LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
1090 FieldRHS>::evaluateAx_diag(FieldLHS& field, F& evalFunction) {
1091 Inform m("");
1092
1093 // declare timer
1094 static IpplTimings::TimerRef evalAx_diag = IpplTimings::getTimer("evaluateAxDiag");
1095
1096 // start a timer
1097 IpplTimings::startTimer(evalAx_diag);
1098
1099 // set result field to 0
1100 resultField = 0;
1101
1102 // List of quadrature weights
1104 this->quadrature_m.getWeightsForRefElement();
1105
1106 // List of quadrature nodes
1108 this->quadrature_m.getIntegrationNodesForRefElement();
1109
1110 // Basis function values and gradients at the quadrature nodes
1111 Vector<Vector<T, numElementDOFs>, QuadratureType::numElementNodes> b_q;
1112 Vector<Vector<point_t, numElementDOFs>, QuadratureType::numElementNodes> grad_b_q;
1113 for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
1114 for (size_t i = 0; i < numElementDOFs; ++i) {
1115 b_q[k][i] = this->evaluateRefElementShapeFunction(i, q[k]);
1116 grad_b_q[k][i] = this->evaluateRefElementShapeFunctionGradient(i, q[k]);
1117 }
1118 }
1119
1120 // Make local element matrix -- does not change through the element mesh
1121 // Element matrix
1123
1124 // 1. Compute the Galerkin element matrix A_K
1125 for (size_t i = 0; i < numElementDOFs; ++i) {
1126 A_K_diag[i] = 0.0;
1127 for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
1128 A_K_diag[i] += w[k] * evalFunction(
1129 i, i, QuadratureData<T, point_t, numElementDOFs>{b_q[k], grad_b_q[k]});
1130 }
1131 }
1132
1133 // Get field data and atomic result data,
1134 // since it will be added to during the kokkos loop
1135 ViewType view = field.getView();
1136 AtomicViewType resultView = resultField.getView();
1137
1138 // Get boundary conditions from field
1139 BConds<FieldLHS, Dim>& bcField = field.getFieldBC();
1140 FieldBC bcType = bcField[0]->getBCType();
1141
1142 // Get domain information
1143 auto ldom = (field.getLayout()).getLocalNDIndex();
1144 // Get number of ghost cells in field
1145 const int nghost = field.getNghost();
1146
1147 using exec_space = typename Kokkos::View<const size_t*>::execution_space;
1148 using policy_type = Kokkos::RangePolicy<exec_space>;
1149
1150 // Loop over elements to compute contributions
1151 Kokkos::parallel_for(
1152 "Loop over elements", policy_type(0, elementIndices.extent(0)),
1153 KOKKOS_CLASS_LAMBDA(const size_t index) {
1154 const size_t elementIndex = elementIndices(index);
1155 const Vector<size_t, numElementDOFs> global_dofs =
1156 this->LagrangeSpace::getGlobalDOFIndices(elementIndex);
1157 Vector<indices_t, numElementDOFs> global_dof_ndindices;
1158
1159 for (size_t i = 0; i < numElementDOFs; ++i) {
1160 global_dof_ndindices[i] = this->getMeshVertexNDIndex(global_dofs[i]);
1161 }
1162
1163 // local DOF indices
1164 size_t i;
1165
1166 // global DOF n-dimensional indices (Vector of N indices representing indices in
1167 // each dimension)
1168 indices_t I_nd;
1169
1170 // 2. Compute the contribution to resultAx = A*x with A_K
1171 for (i = 0; i < numElementDOFs; ++i) {
1172 I_nd = global_dof_ndindices[i];
1173
1174 // Handle boundary DOFs
1175 // If Zero Dirichlet BCs, skip this DOF
1176 // If Constant Dirichlet BCs, identity
1177 if ((bcType == CONSTANT_FACE) && (this->isDOFOnBoundary(I_nd))) {
1178 for (unsigned d = 0; d < Dim; ++d) {
1179 I_nd[d] = I_nd[d] - ldom[d].first() + nghost;
1180 }
1181 apply(resultView, I_nd) = apply(view, I_nd);
1182 continue;
1183 } else if ((bcType == ZERO_FACE) && (this->isDOFOnBoundary(I_nd))) {
1184 continue;
1185 }
1186
1187 // get the appropriate index for the Kokkos view of the field
1188 for (unsigned d = 0; d < Dim; ++d) {
1189 I_nd[d] = I_nd[d] - ldom[d].first() + nghost;
1190 }
1191 apply(resultView, I_nd) += A_K_diag[i] * apply(view, I_nd);
1192 }
1193 });
1194
1195 if (bcType == PERIODIC_FACE) {
1196 resultField.accumulateHalo();
1197 bcField.apply(resultField);
1198 bcField.assignGhostToPhysical(resultField);
1199 } else {
1200 resultField.accumulateHalo_noghost();
1201 }
1202
1203 IpplTimings::stopTimer(evalAx_diag);
1204
1205 return resultField;
1206 }
1207
1208 template <typename T, unsigned Dim, unsigned Order, typename ElementType,
1209 typename QuadratureType, typename FieldLHS, typename FieldRHS>
1210 template <typename F>
1211 FieldLHS LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
1212 FieldRHS>::evaluateAx_lift(FieldLHS& field, F& evalFunction) {
1213 Inform m("");
1214
1215 // declare timer
1216 static IpplTimings::TimerRef evalLifting = IpplTimings::getTimer("evaluateLifting");
1217
1218 // start a timer
1219 IpplTimings::startTimer(evalLifting);
1220
1221 // set result field to 0
1222 resultField = 0.0;
1223
1224 // List of quadrature weights
1226 this->quadrature_m.getWeightsForRefElement();
1227
1228 // List of quadrature nodes
1230 this->quadrature_m.getIntegrationNodesForRefElement();
1231
1232 // Basis function values and gradients at the quadrature nodes
1233 Vector<Vector<T, numElementDOFs>, QuadratureType::numElementNodes> b_q;
1234 Vector<Vector<point_t, numElementDOFs>, QuadratureType::numElementNodes> grad_b_q;
1235 for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
1236 for (size_t i = 0; i < numElementDOFs; ++i) {
1237 b_q[k][i] = this->evaluateRefElementShapeFunction(i, q[k]);
1238 grad_b_q[k][i] = this->evaluateRefElementShapeFunctionGradient(i, q[k]);
1239 }
1240 }
1241
1242 // Make local element matrix -- does not change through the element mesh
1243 // Element matrix
1244 Vector<Vector<T, numElementDOFs>, numElementDOFs> A_K;
1245
1246 // 1. Compute the Galerkin element matrix A_K
1247 for (size_t i = 0; i < numElementDOFs; ++i) {
1248 for (size_t j = 0; j < numElementDOFs; ++j) {
1249 A_K[i][j] = 0.0;
1250 for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
1251 A_K[i][j] += w[k] * evalFunction(
1252 i, j, QuadratureData<T, point_t, numElementDOFs>{b_q[k], grad_b_q[k]});
1253 }
1254 }
1255 }
1256
1257 // Get field data and atomic result data,
1258 // since it will be added to during the kokkos loop
1259 ViewType view = field.getView();
1260 AtomicViewType resultView = resultField.getView();
1261
1262 // Get domain information
1263 auto ldom = (field.getLayout()).getLocalNDIndex();
1264 // Get number of ghost cells in field
1265 const int nghost = field.getNghost();
1266
1267 using exec_space = typename Kokkos::View<const size_t*>::execution_space;
1268 using policy_type = Kokkos::RangePolicy<exec_space>;
1269
1270 // Loop over elements to compute contributions
1271 Kokkos::parallel_for(
1272 "Loop over elements", policy_type(0, elementIndices.extent(0)),
1273 KOKKOS_CLASS_LAMBDA(const size_t index) {
1274 const size_t elementIndex = elementIndices(index);
1275 const Vector<size_t, numElementDOFs> global_dofs =
1276 this->LagrangeSpace::getGlobalDOFIndices(elementIndex);
1277 Vector<indices_t, numElementDOFs> global_dof_ndindices;
1278
1279 for (size_t i = 0; i < numElementDOFs; ++i) {
1280 global_dof_ndindices[i] = this->getMeshVertexNDIndex(global_dofs[i]);
1281 }
1282
1283 // local DOF indices (both i and j go from 0 to numDOFs-1 in the element)
1284 size_t i, j;
1285
1286 // global DOF n-dimensional indices (Vector of N indices representing indices in
1287 // each dimension)
1288 indices_t I_nd, J_nd;
1289
1290 // 2. Compute the contribution to resultAx = A*x with A_K
1291 for (i = 0; i < numElementDOFs; ++i) {
1292 I_nd = global_dof_ndindices[i];
1293
1294 // Skip if on a row of the matrix
1295 if (this->isDOFOnBoundary(I_nd)) {
1296 continue;
1297 }
1298
1299 // get the appropriate index for the Kokkos view of the field
1300 for (unsigned d = 0; d < Dim; ++d) {
1301 I_nd[d] = I_nd[d] - ldom[d].first() + nghost;
1302 }
1303
1304 for (j = 0; j < numElementDOFs; ++j) {
1305 J_nd = global_dof_ndindices[j];
1306
1307 // Contribute to lifting only if on a boundary DOF
1308 if (this->isDOFOnBoundary(J_nd)) {
1309 // get the appropriate index for the Kokkos view of the field
1310 for (unsigned d = 0; d < Dim; ++d) {
1311 J_nd[d] = J_nd[d] - ldom[d].first() + nghost;
1312 }
1313 apply(resultView, I_nd) += A_K[i][j] * apply(view, J_nd);
1314 continue;
1315 }
1316
1317 }
1318 }
1319 });
1320 resultField.accumulateHalo();
1321
1322 IpplTimings::stopTimer(evalLifting);
1323
1324 return resultField;
1325 }
1326
1327 template <typename T, unsigned Dim, unsigned Order, typename ElementType,
1328 typename QuadratureType, typename FieldLHS, typename FieldRHS>
1329 void LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
1330 FieldRHS>::evaluateLoadVector(FieldRHS& field) const {
1331 Inform m("");
1332
1333 // declare timer
1334 static IpplTimings::TimerRef evalLoadV = IpplTimings::getTimer("evaluateLoadVector");
1335
1336 // start a timer
1337 IpplTimings::startTimer(evalLoadV);
1338
1339 // List of quadrature weights
1341 this->quadrature_m.getWeightsForRefElement();
1342
1343 // List of quadrature nodes
1345 this->quadrature_m.getIntegrationNodesForRefElement();
1346
1347 const indices_t zeroNdIndex = Vector<size_t, Dim>(0);
1348
1349 // Evaluate the basis functions for the DOF at the quadrature nodes
1350 Vector<Vector<T, numElementDOFs>, QuadratureType::numElementNodes> basis_q;
1351 for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
1352 for (size_t i = 0; i < numElementDOFs; ++i) {
1353 basis_q[k][i] = this->evaluateRefElementShapeFunction(i, q[k]);
1354 }
1355 }
1356
1357 // Absolute value of det Phi_K
1358 const T absDetDPhi = Kokkos::abs(this->ref_element_m.getDeterminantOfTransformationJacobian(
1359 this->getElementMeshVertexPoints(zeroNdIndex)));
1360
1361 // Get domain information and ghost cells
1362 auto ldom = (field.getLayout()).getLocalNDIndex();
1363 const int nghost = field.getNghost();
1364
1365 // Get boundary conditions from field
1366 BConds<FieldRHS, Dim>& bcField = field.getFieldBC();
1367 FieldBC bcType = bcField[0]->getBCType();
1368
1369 FieldRHS temp_field(field.get_mesh(), field.getLayout(), nghost);
1370 temp_field.setFieldBC(bcField);
1371
1372 // Get field data and make it atomic,
1373 // since it will be added to during the kokkos loop
1374 // We work with a temporary field since we need to use field
1375 // to evaluate the load vector; then we assign temp to RHS field
1376 AtomicViewType atomic_view = temp_field.getView();
1377
1378 using exec_space = typename Kokkos::View<const size_t*>::execution_space;
1379 using policy_type = Kokkos::RangePolicy<exec_space>;
1380
1381 // Loop over elements to compute contributions
1382 Kokkos::parallel_for(
1383 "Loop over elements", policy_type(0, elementIndices.extent(0)),
1384 KOKKOS_CLASS_LAMBDA(size_t index) {
1385 const size_t elementIndex = elementIndices(index);
1386 const Vector<size_t, numElementDOFs> global_dofs =
1387 this->LagrangeSpace::getGlobalDOFIndices(elementIndex);
1388
1389 size_t i, I;
1390
1391 // 1. Compute b_K
1392 for (i = 0; i < numElementDOFs; ++i) {
1393 I = global_dofs[i];
1394
1395 // TODO fix for higher order
1396 auto dof_ndindex_I = this->getMeshVertexNDIndex(I);
1397
1398 // Skip boundary DOFs (Zero and Constant Dirichlet BCs)
1399 if (((bcType == ZERO_FACE) || (bcType == CONSTANT_FACE))
1400 && (this->isDOFOnBoundary(dof_ndindex_I))) {
1401 continue;
1402 }
1403
1404 // calculate the contribution of this element
1405 T contrib = 0;
1406 for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
1407 T val = 0;
1408 for (size_t j = 0; j < numElementDOFs; ++j) {
1409 // get field index corresponding to this DOF
1410 size_t J = global_dofs[j];
1411 auto dof_ndindex_J = this->getMeshVertexNDIndex(J);
1412 for (unsigned d = 0; d < Dim; ++d) {
1413 dof_ndindex_J[d] = dof_ndindex_J[d] - ldom[d].first() + nghost;
1414 }
1415
1416 // get field value at DOF and interpolate to q_k
1417 val += basis_q[k][j] * apply(field, dof_ndindex_J);
1418 }
1419
1420 contrib += w[k] * basis_q[k][i] * absDetDPhi * val;
1421 }
1422
1423 // get the appropriate index for the Kokkos view of the field
1424 for (unsigned d = 0; d < Dim; ++d) {
1425 dof_ndindex_I[d] = dof_ndindex_I[d] - ldom[d].first() + nghost;
1426 }
1427
1428 // add the contribution of the element to the field
1429 apply(atomic_view, dof_ndindex_I) += contrib;
1430
1431 }
1432 });
1433 temp_field.accumulateHalo();
1434
1435 if (bcType == PERIODIC_FACE) {
1436 bcField.apply(temp_field);
1437 bcField.assignGhostToPhysical(temp_field);
1438 }
1439
1440 field = temp_field;
1441
1442 IpplTimings::stopTimer(evalLoadV);
1443 }
1444
1448
1449 template <typename T, unsigned Dim, unsigned Order, typename ElementType,
1450 typename QuadratureType, typename FieldLHS, typename FieldRHS>
1451 void LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
1452 FieldRHS>::evaluateLumpedMass(FieldRHS& field) const {
1453 // List of quadrature weights
1455 this->quadrature_m.getWeightsForRefElement();
1456
1457 // List of quadrature nodes
1459 this->quadrature_m.getIntegrationNodesForRefElement();
1460
1461 const indices_t zeroNdIndex = Vector<size_t, Dim>(0);
1462
1463 // Evaluate the basis functions for the DOF at the quadrature nodes
1464 Vector<Vector<T, numElementDOFs>, QuadratureType::numElementNodes> basis_q;
1465 for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
1466 for (size_t i = 0; i < numElementDOFs; ++i) {
1467 basis_q[k][i] = this->evaluateRefElementShapeFunction(i, q[k]);
1468 }
1469 }
1470
1471 // Absolute value of det Phi_K
1472 const T absDetDPhi = Kokkos::abs(this->ref_element_m.getDeterminantOfTransformationJacobian(
1473 this->getElementMeshVertexPoints(zeroNdIndex)));
1474
1475 // Get field data and make it atomic,
1476 // since it will be added to during the kokkos loop
1477 AtomicViewType atomic_view = field.getView();
1478
1479 // Get domain information and ghost cells
1480 auto ldom = (field.getLayout()).getLocalNDIndex();
1481 const int nghost = field.getNghost();
1482
1483 using exec_space = typename Kokkos::View<const size_t*>::execution_space;
1484 using policy_type = Kokkos::RangePolicy<exec_space>;
1485
1486 // Loop over elements to compute contributions
1487 Kokkos::parallel_for(
1488 "Loop over elements", policy_type(0, elementIndices.extent(0)),
1489 KOKKOS_CLASS_LAMBDA(size_t index) {
1490 const size_t elementIndex = elementIndices(index);
1491 const Vector<size_t, numElementDOFs> global_dofs =
1492 this->LagrangeSpace::getGlobalDOFIndices(elementIndex);
1493
1494 size_t i, I;
1495
1496 // 1. Compute b_K
1497 for (i = 0; i < numElementDOFs; ++i) {
1498 I = global_dofs[i];
1499
1500 // TODO fix for higher order
1501 auto dof_ndindex_I = this->getMeshVertexNDIndex(I);
1502
1503 // calculate the contribution of this element
1504 T contrib = 0;
1505 for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
1506 contrib += w[k] * basis_q[k][i] * absDetDPhi;
1507 }
1508
1509 // get the appropriate index for the Kokkos view of the field
1510 for (unsigned d = 0; d < Dim; ++d) {
1511 dof_ndindex_I[d] = dof_ndindex_I[d] - ldom[d].first() + nghost;
1512 }
1513
1514 // add the contribution of the element to the field
1515 apply(atomic_view, dof_ndindex_I) += contrib;
1516 }
1517 });
1518 field.accumulateHalo();
1519 field.fillHalo();
1520 }
1521
1522 template <typename T, unsigned Dim, unsigned Order, typename ElementType,
1523 typename QuadratureType, typename FieldLHS, typename FieldRHS>
1524 template <typename F>
1526 const FieldLHS& u_h, const F& u_sol) const {
1527 if (this->quadrature_m.getOrder() < (2 * Order + 1)) {
1528 // throw exception
1529 throw IpplException(
1530 "LagrangeSpace::computeErrorL2()",
1531 "Order of quadrature rule for error computation should be > 2*p + 1");
1532 }
1533
1534 // List of quadrature weights
1536 this->quadrature_m.getWeightsForRefElement();
1537
1538 // List of quadrature nodes
1540 this->quadrature_m.getIntegrationNodesForRefElement();
1541
1542 // Evaluate the basis functions for the DOF at the quadrature nodes
1543 Vector<Vector<T, numElementDOFs>, QuadratureType::numElementNodes> basis_q;
1544 for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
1545 for (size_t i = 0; i < numElementDOFs; ++i) {
1546 basis_q[k][i] = this->evaluateRefElementShapeFunction(i, q[k]);
1547 }
1548 }
1549
1550 const indices_t zeroNdIndex = Vector<size_t, Dim>(0);
1551
1552 // Absolute value of det Phi_K
1553 const T absDetDPhi = Kokkos::abs(this->ref_element_m.getDeterminantOfTransformationJacobian(
1554 this->getElementMeshVertexPoints(zeroNdIndex)));
1555
1556 // Variable to sum the error to
1557 T error = 0;
1558
1559 // Get domain information and ghost cells
1560 auto ldom = (u_h.getLayout()).getLocalNDIndex();
1561 const int nghost = u_h.getNghost();
1562
1563 using exec_space = typename Kokkos::View<const size_t*>::execution_space;
1564 using policy_type = Kokkos::RangePolicy<exec_space>;
1565
1566 // Loop over elements to compute contributions
1567 Kokkos::parallel_reduce(
1568 "Compute error over elements", policy_type(0, elementIndices.extent(0)),
1569 KOKKOS_CLASS_LAMBDA(size_t index, double& local) {
1570 const size_t elementIndex = elementIndices(index);
1571 const Vector<size_t, numElementDOFs> global_dofs =
1572 this->LagrangeSpace::getGlobalDOFIndices(elementIndex);
1573
1574 // contribution of this element to the error
1575 T contrib = 0;
1576 for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
1577 T val_u_sol = u_sol(this->ref_element_m.localToGlobal(
1578 this->getElementMeshVertexPoints(this->getElementNDIndex(elementIndex)),
1579 q[k]));
1580
1581 T val_u_h = 0;
1582 for (size_t i = 0; i < numElementDOFs; ++i) {
1583 // get field index corresponding to this DOF
1584 size_t I = global_dofs[i];
1585 auto dof_ndindex_I = this->getMeshVertexNDIndex(I);
1586 for (unsigned d = 0; d < Dim; ++d) {
1587 dof_ndindex_I[d] = dof_ndindex_I[d] - ldom[d].first() + nghost;
1588 }
1589
1590 // get field value at DOF and interpolate to q_k
1591 val_u_h += basis_q[k][i] * apply(u_h, dof_ndindex_I);
1592 }
1593
1594 contrib += w[k] * Kokkos::pow(val_u_sol - val_u_h, 2) * absDetDPhi;
1595 }
1596 local += contrib;
1597 },
1598 Kokkos::Sum<double>(error));
1599
1600 // MPI reduce
1601 T global_error = 0.0;
1602 Comm->allreduce(error, global_error, 1, std::plus<T>());
1603
1604 return Kokkos::sqrt(global_error);
1605 }
1606
1607 template <typename T, unsigned Dim, unsigned Order, typename ElementType,
1608 typename QuadratureType, typename FieldLHS, typename FieldRHS>
1610 const FieldLHS& u_h) const {
1611 if (this->quadrature_m.getOrder() < (2 * Order + 1)) {
1612 // throw exception
1613 throw IpplException(
1614 "LagrangeSpace::computeAvg()",
1615 "Order of quadrature rule for error computation should be > 2*p + 1");
1616 }
1617
1618 // List of quadrature weights
1620 this->quadrature_m.getWeightsForRefElement();
1621
1622 // List of quadrature nodes
1624 this->quadrature_m.getIntegrationNodesForRefElement();
1625
1626 // Evaluate the basis functions for the DOF at the quadrature nodes
1627 Vector<Vector<T, numElementDOFs>, QuadratureType::numElementNodes> basis_q;
1628 for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
1629 for (size_t i = 0; i < numElementDOFs; ++i) {
1630 basis_q[k][i] = this->evaluateRefElementShapeFunction(i, q[k]);
1631 }
1632 }
1633
1634 const indices_t zeroNdIndex = Vector<size_t, Dim>(0);
1635
1636 // Absolute value of det Phi_K
1637 const T absDetDPhi = Kokkos::abs(this->ref_element_m.getDeterminantOfTransformationJacobian(
1638 this->getElementMeshVertexPoints(zeroNdIndex)));
1639
1640 // Variable to sum the error to
1641 T avg = 0;
1642
1643 // Get domain information and ghost cells
1644 auto ldom = (u_h.getLayout()).getLocalNDIndex();
1645 const int nghost = u_h.getNghost();
1646
1647 using exec_space = typename Kokkos::View<const size_t*>::execution_space;
1648 using policy_type = Kokkos::RangePolicy<exec_space>;
1649
1650 // Loop over elements to compute contributions
1651 Kokkos::parallel_reduce(
1652 "Compute average over elements", policy_type(0, elementIndices.extent(0)),
1653 KOKKOS_CLASS_LAMBDA(size_t index, double& local) {
1654 const size_t elementIndex = elementIndices(index);
1655 const Vector<size_t, numElementDOFs> global_dofs =
1656 this->LagrangeSpace::getGlobalDOFIndices(elementIndex);
1657
1658 // contribution of this element to the error
1659 T contrib = 0;
1660 for (size_t k = 0; k < QuadratureType::numElementNodes; ++k) {
1661 T val_u_h = 0;
1662 for (size_t i = 0; i < numElementDOFs; ++i) {
1663 // get field index corresponding to this DOF
1664 size_t I = global_dofs[i];
1665 auto dof_ndindex_I = this->getMeshVertexNDIndex(I);
1666 for (unsigned d = 0; d < Dim; ++d) {
1667 dof_ndindex_I[d] = dof_ndindex_I[d] - ldom[d].first() + nghost;
1668 }
1669
1670 // get field value at DOF and interpolate to q_k
1671 val_u_h += basis_q[k][i] * apply(u_h, dof_ndindex_I);
1672 }
1673
1674 contrib += w[k] * val_u_h * absDetDPhi;
1675 }
1676 local += contrib;
1677 },
1678 Kokkos::Sum<double>(avg));
1679
1680 // MPI reduce
1681 T global_avg = 0.0;
1682 Comm->allreduce(avg, global_avg, 1, std::plus<T>());
1683
1684 return global_avg;
1685 }
1686
1690
1691 // Function to return the device struct of this Lagrange Space
1692 template <typename T, unsigned Dim, unsigned Order, typename ElementType,
1693 typename QuadratureType, typename FieldLHS, typename FieldRHS>
1695 DeviceStruct
1697 getDeviceMirror() const {
1698 DeviceStruct space_mirror;
1699 space_mirror.nr_m = this->nr_m;
1700 space_mirror.ref_element_m = this->ref_element_m;
1701 return space_mirror;
1702 }
1703
1704 // I don't know how to avoid code duplication here...
1705 // Make sure that any changes in getLocalDOFIndex, getGlobalDOFIndices,
1706 // evaluateRefElementShapeFunction, and getMeshVertexNDIndex from the
1707 // parent class FiniteElementSpace get propagated here.
1708
1709 template <typename T, unsigned Dim, unsigned Order, typename ElementType,
1710 typename QuadratureType, typename FieldLHS, typename FieldRHS>
1711 KOKKOS_FUNCTION size_t
1713 DeviceStruct::getLocalDOFIndex(const indices_t& elementNDIndex,
1714 const size_t& globalDOFIndex) const {
1715
1716 static_assert(Dim == 1 || Dim == 2 || Dim == 3, "Dim must be 1, 2 or 3");
1717 // TODO fix not order independent, only works for order 1
1718 static_assert(Order == 1, "Only order 1 is supported at the moment");
1719
1720 // Get all the global DOFs for the element
1721 const Vector<size_t, numElementDOFs> global_dofs =
1722 this->getGlobalDOFIndices(elementNDIndex);
1723
1724 // Find the global DOF in the vector and return the local DOF index
1725 // Note: It is important that this only works because the global_dofs
1726 // are already arranged in the correct order from getGlobalDOFIndices
1727 for (size_t i = 0; i < global_dofs.dim; ++i) {
1728 if (global_dofs[i] == globalDOFIndex) {
1729 return i;
1730 }
1731 }
1732 // commented this due to this being on device
1733 // however, it would be good to throw an error in this case
1734 //throw IpplException("LagrangeSpace::getLocalDOFIndex()",
1735 // "FEM Lagrange Space: Global DOF not found in specified element");
1736 return 0;
1737 }
1738
1739 template <typename T, unsigned Dim, unsigned Order, typename ElementType,
1740 typename QuadratureType, typename FieldLHS, typename FieldRHS>
1741 KOKKOS_FUNCTION Vector<size_t, LagrangeSpace<T, Dim, Order, ElementType, QuadratureType,
1742 FieldLHS, FieldRHS>::DeviceStruct::numElementDOFs>
1743 LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS, FieldRHS>::
1744 DeviceStruct::getGlobalDOFIndices(const indices_t& elementNDIndex) const {
1745
1746 Vector<size_t, numElementDOFs> globalDOFs(0);
1747
1748 // Compute the vector to multiply the ndindex with
1750 for (size_t d = 1; d < dim; ++d) {
1751 for (size_t d2 = d; d2 < Dim; ++d2) {
1752 vec[d2] *= this->nr_m[d - 1];
1753 }
1754 }
1755 vec *= Order; // Multiply each dimension by the order
1756 size_t smallestGlobalDOF = elementNDIndex.dot(vec);
1757
1758 // Add the vertex DOFs
1759 globalDOFs[0] = smallestGlobalDOF;
1760 globalDOFs[1] = smallestGlobalDOF + Order;
1761
1762 if constexpr (Dim >= 2) {
1763 globalDOFs[2] = globalDOFs[1] + this->nr_m[0] * Order;
1764 globalDOFs[3] = globalDOFs[0] + this->nr_m[0] * Order;
1765 }
1766 if constexpr (Dim >= 3) {
1767 globalDOFs[4] = globalDOFs[0] + this->nr_m[1] * this->nr_m[0] * Order;
1768 globalDOFs[5] = globalDOFs[1] + this->nr_m[1] * this->nr_m[0] * Order;
1769 globalDOFs[6] = globalDOFs[2] + this->nr_m[1] * this->nr_m[0] * Order;
1770 globalDOFs[7] = globalDOFs[3] + this->nr_m[1] * this->nr_m[0] * Order;
1771 }
1772
1773 if constexpr (Order > 1) {
1774 // If the order is greater than 1, there are edge and face DOFs, otherwise the work is
1775 // done
1776
1777 // Add the edge DOFs
1778 if constexpr (Dim >= 2) {
1779 for (size_t i = 0; i < Order - 1; ++i) {
1780 globalDOFs[8 + i] = globalDOFs[0] + i + 1;
1781 globalDOFs[8 + Order - 1 + i] = globalDOFs[1] + (i + 1) * this->nr_m[1];
1782 globalDOFs[8 + 2 * (Order - 1) + i] = globalDOFs[2] - (i + 1);
1783 globalDOFs[8 + 3 * (Order - 1) + i] = globalDOFs[3] - (i + 1) * this->nr_m[1];
1784 }
1785 }
1786 if constexpr (Dim >= 3) {
1787 // TODO
1788 }
1789
1790 // Add the face DOFs
1791 if constexpr (Dim >= 2) {
1792 for (size_t i = 0; i < Order - 1; ++i) {
1793 for (size_t j = 0; j < Order - 1; ++j) {
1794 // TODO CHECK
1795 globalDOFs[8 + 4 * (Order - 1) + i * (Order - 1) + j] =
1796 globalDOFs[0] + (i + 1) + (j + 1) * this->nr_m[1];
1797 globalDOFs[8 + 4 * (Order - 1) + (Order - 1) * (Order - 1) + i * (Order - 1)
1798 + j] = globalDOFs[1] + (i + 1) + (j + 1) * this->nr_m[1];
1799 globalDOFs[8 + 4 * (Order - 1) + 2 * (Order - 1) * (Order - 1)
1800 + i * (Order - 1) + j] =
1801 globalDOFs[2] - (i + 1) + (j + 1) * this->nr_m[1];
1802 globalDOFs[8 + 4 * (Order - 1) + 3 * (Order - 1) * (Order - 1)
1803 + i * (Order - 1) + j] =
1804 globalDOFs[3] - (i + 1) + (j + 1) * this->nr_m[1];
1805 }
1806 }
1807 }
1808 }
1809
1810 return globalDOFs;
1811 }
1812
1813 template <typename T, unsigned Dim, unsigned Order, typename ElementType,
1814 typename QuadratureType, typename FieldLHS, typename FieldRHS>
1815 KOKKOS_FUNCTION T
1816 LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS, FieldRHS>::
1817 DeviceStruct::evaluateRefElementShapeFunction(const size_t& localDOF,
1818 const LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
1819 FieldRHS>::point_t& localPoint) const {
1820
1821 static_assert(Order == 1, "Only order 1 is supported at the moment");
1822 // Assert that the local vertex index is valid.
1823 assert(localDOF < DeviceStruct::numElementDOFs
1824 && "The local vertex index is invalid"); // TODO assumes 1st order Lagrange
1825
1826 assert(this->ref_element_m.isPointInRefElement(localPoint)
1827 && "Point is not in reference element");
1828
1829 // Get the local vertex indices for the local vertex index.
1830 // TODO fix not order independent, only works for order 1
1831 const point_t ref_element_point = this->ref_element_m.getLocalVertices()[localDOF];
1832
1833 // The variable that accumulates the product of the shape functions.
1834 T product = 1;
1835
1836 for (size_t d = 0; d < Dim; d++) {
1837 if (localPoint[d] < ref_element_point[d]) {
1838 product *= localPoint[d];
1839 } else {
1840 product *= 1.0 - localPoint[d];
1841 }
1842 }
1843
1844 return product;
1845 }
1846
1847 template <typename T, unsigned Dim, unsigned Order, typename ElementType,
1848 typename QuadratureType, typename FieldLHS, typename FieldRHS>
1849 KOKKOS_FUNCTION typename LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
1850 FieldRHS>::point_t
1851 LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS, FieldRHS>::
1852 DeviceStruct::evaluateRefElementShapeFunctionGradient(const size_t& localDOF,
1853 const LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS,
1854 FieldRHS>::point_t& localPoint) const {
1855 // TODO fix not order independent, only works for order 1
1856 static_assert(Order == 1 && "Only order 1 is supported at the moment");
1857
1858 // Assert that the local vertex index is valid.
1859 assert(localDOF < numElementDOFs && "The local vertex index is invalid");
1860
1861 assert(this->ref_element_m.isPointInRefElement(localPoint)
1862 && "Point is not in reference element");
1863
1864 // Get the local dof nd_index
1865 const vertex_points_t local_vertex_points = this->ref_element_m.getLocalVertices();
1866
1867 const point_t& local_vertex_point = local_vertex_points[localDOF];
1868
1869 point_t gradient(1);
1870
1871 // To construct the gradient we need to loop over the dimensions and multiply the
1872 // shape functions in each dimension except the current one. The one of the current
1873 // dimension is replaced by the derivative of the shape function in that dimension,
1874 // which is either 1 or -1.
1875 for (size_t d = 0; d < Dim; d++) {
1876 // The variable that accumulates the product of the shape functions.
1877 T product = 1;
1878
1879 for (size_t d2 = 0; d2 < Dim; d2++) {
1880 if (d2 == d) {
1881 if (localPoint[d] < local_vertex_point[d]) {
1882 product *= 1;
1883 } else {
1884 product *= -1;
1885 }
1886 } else {
1887 if (localPoint[d2] < local_vertex_point[d2]) {
1888 product *= localPoint[d2];
1889 } else {
1890 product *= 1.0 - localPoint[d2];
1891 }
1892 }
1893 }
1894
1895 gradient[d] = product;
1896 }
1897
1898 return gradient;
1899 }
1900
1901 template <typename T, unsigned Dim, unsigned Order, typename ElementType,
1902 typename QuadratureType, typename FieldLHS, typename FieldRHS>
1903 KOKKOS_FUNCTION typename LagrangeSpace<T, Dim, Order, ElementType, QuadratureType,
1904 FieldLHS, FieldRHS>::indices_t
1905 LagrangeSpace<T, Dim, Order, ElementType, QuadratureType, FieldLHS, FieldRHS>::
1906 DeviceStruct::getMeshVertexNDIndex(const size_t& vertex_index) const {
1907 // Copy the vertex index to the index variable we can alter during the computation.
1908 size_t index = vertex_index;
1909
1910 // Create a vector to store the vertex indices in each dimension for the corresponding
1911 // vertex.
1912 indices_t vertex_indices;
1913
1914 // This is the number of vertices in each dimension.
1915 Vector<size_t, Dim> vertices_per_dim = nr_m;
1916
1917 // The number_of_lower_dim_vertices is the product of the number of vertices per
1918 // dimension, it will get divided by the current dimensions number to get the index in
1919 // that dimension
1920 size_t remaining_number_of_vertices = 1;
1921 for (const size_t num_vertices : vertices_per_dim) {
1922 remaining_number_of_vertices *= num_vertices;
1923 }
1924
1925 for (int d = Dim - 1; d >= 0; --d) {
1926 remaining_number_of_vertices /= vertices_per_dim[d];
1927 vertex_indices[d] = index / remaining_number_of_vertices;
1928 index -= vertex_indices[d] * remaining_number_of_vertices;
1929 }
1930
1931 return vertex_indices;
1932 };
1933
1934} // namespace ippl
Definition Inform.h:40
Definition IpplException.h:6
Definition BConds.h:23
Definition FieldLayout.h:166
The FiniteElementSpace class handles the mesh index mapping to vertices and elements and is the base ...
Definition FiniteElementSpace.h:41
A class representing a Lagrange space for finite element methods on a structured, rectilinear grid.
Definition LagrangeSpace.h:36
void initializeElementIndices(Layout_t &layout)
Initialize a Kokkos view containing the element indices. This distributes the elements among MPI rank...
Definition LagrangeSpace.hpp:58
T computeAvg(const FieldLHS &u_h) const
Given a field, compute the average.
Definition LagrangeSpace.hpp:1609
void initialize(UniformCartesian< T, Dim > &mesh, Layout_t &layout)
Initialize a LagrangeSpace object created with the default constructor.
Definition LagrangeSpace.hpp:41
KOKKOS_FUNCTION Vector< size_t, numElementDOFs > getGlobalDOFIndices(const size_t &element_index) const override
Get the global DOF indices (vector of global DOF indices) of an element.
Definition LagrangeSpace.hpp:200
DeviceStruct getDeviceMirror() const
Device struct definitions /////////////////////////////////////////.
Definition LagrangeSpace.hpp:1697
KOKKOS_FUNCTION size_t getLocalDOFIndex(const size_t &elementIndex, const size_t &globalDOFIndex) const override
Get the elements local DOF from the element index and global DOF index.
Definition LagrangeSpace.hpp:145
KOKKOS_FUNCTION T evaluateRefElementShapeFunction(const size_t &localDOF, const point_t &localPoint) const
Basis functions and gradients /////////////////////////////////////.
Definition LagrangeSpace.hpp:279
T computeErrorL2(const FieldLHS &u_h, const F &u_sol) const
Error norm computations ///////////////////////////////////////////.
Definition LagrangeSpace.hpp:1525
KOKKOS_FUNCTION point_t evaluateRefElementShapeFunctionGradient(const size_t &localDOF, const point_t &localPoint) const
Evaluate the gradient of the shape function of a local degree of freedom at a given point in the refe...
Definition LagrangeSpace.hpp:314
LagrangeSpace(UniformCartesian< T, Dim > &mesh, ElementType &ref_element, const QuadratureType &quadrature, Layout_t &layout)
Construct a new LagrangeSpace object.
Definition LagrangeSpace.hpp:8
KOKKOS_INLINE_FUNCTION unsigned size() const noexcept
Definition NDIndex.hpp:32
Definition UniformCartesian.h:14
Definition Vector.h:23
Definition Archive.h:20
KOKKOS_INLINE_FUNCTION constexpr decltype(auto) apply(const View &view, const Coords &coords)
Definition IpplOperations.h:64
Device struct for copies //////////////////////////////////////////.
Definition LagrangeSpace.h:290
Per-quadrature-node basis data passed to evaluateAx evaluator functors.
Definition FEMQuadratureData.h:18