MFEM v4.10.0
Finite element discretization library
Loading...
Searching...
No Matches
restriction.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2026, Lawrence Livermore National Security, LLC. Produced
2// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3// LICENSE and NOTICE for details. LLNL-CODE-806117.
4//
5// This file is part of the MFEM library. For more information and source code
6// availability visit https://mfem.org.
7//
8// MFEM is free software; you can redistribute it and/or modify it under the
9// terms of the BSD-3 license. We welcome feedback and contributions, see file
10// CONTRIBUTING.md for details.
11
12#include "restriction.hpp"
14#include "gridfunc.hpp"
15#include "fespace.hpp"
16#include "pgridfunc.hpp"
17#include "qspace.hpp"
18#include "fe/face_map_utils.hpp"
19#include "../general/forall.hpp"
20
21#include <climits>
22
23namespace mfem
24{
25
27 ElementDofOrdering e_ordering)
28 : fes(f),
29 ne(fes.GetNE()),
30 vdim(fes.GetVDim()),
31 byvdim(fes.GetOrdering() == Ordering::byVDIM),
32 ndofs(fes.GetNDofs()),
33 dof(fes.GetTypicalFE()->GetDof()),
34 nedofs(ne*dof),
35 offsets(ndofs+1),
36 indices(ne*dof),
37 gather_map(ne*dof)
38{
39 // Assuming all finite elements are the same.
40 MFEM_VERIFY(!f.IsVariableOrder(), "Variable-order spaces are not supported");
41
42 height = vdim*ne*dof;
43 width = fes.GetVSize();
44 const bool dof_reorder = (e_ordering == ElementDofOrdering::LEXICOGRAPHIC);
45 const int *dof_map = NULL;
46 if (dof_reorder && ne > 0)
47 {
48 for (int e = 0; e < ne; ++e)
49 {
50 const FiniteElement *fe = fes.GetFE(e);
51 auto el_t = dynamic_cast<const TensorBasisElement*>(fe);
52 auto el_n = dynamic_cast<const NodalFiniteElement*>(fe);
53 auto el_p = dynamic_cast<const H1Pos_TriangleElement*>(fe) ||
54 dynamic_cast<const H1Pos_TetrahedronElement*>(fe);
55 if (el_t || el_n || el_p) { continue; }
56 MFEM_ABORT("Finite element not suitable for lexicographic ordering");
57 }
58 const FiniteElement *fe = fes.GetTypicalFE();
59 auto el_t = dynamic_cast<const TensorBasisElement*>(fe);
60 auto el_n = dynamic_cast<const NodalFiniteElement*>(fe);
61 auto el_p_tri = dynamic_cast<const H1Pos_TriangleElement*>(fe);
62 auto el_p_tet = dynamic_cast<const H1Pos_TetrahedronElement*>(fe);
63 const Array<int> &fe_dof_map =
64 el_n ? el_n->GetLexicographicOrdering() :
65 el_t ? el_t->GetDofMap() :
66 el_p_tri ? el_p_tri->GetDofMap() :
67 el_p_tet->GetDofMap();
68 MFEM_VERIFY(fe_dof_map.Size() > 0, "invalid dof map");
69 dof_map = fe_dof_map.HostRead();
70 }
71 const Table& e2dTable = fes.GetElementToDofTable();
72 const int* element_map = e2dTable.GetJ();
73 // We will be keeping a count of how many local nodes point to its global dof
74 for (int i = 0; i <= ndofs; ++i)
75 {
76 offsets[i] = 0;
77 }
78 for (int e = 0; e < ne; ++e)
79 {
80 for (int d = 0; d < dof; ++d)
81 {
82 const int sgid = element_map[dof*e + d]; // signed
83 const int gid = (sgid >= 0) ? sgid : -1 - sgid;
84 ++offsets[gid + 1];
85 }
86 }
87 // Aggregate to find offsets for each global dof
88 for (int i = 1; i <= ndofs; ++i)
89 {
90 offsets[i] += offsets[i - 1];
91 }
92 // For each global dof, fill in all local nodes that point to it
93 for (int e = 0; e < ne; ++e)
94 {
95 for (int d = 0; d < dof; ++d)
96 {
97 const int sdid = dof_reorder ? dof_map[d] : 0; // signed
98 const int did = (!dof_reorder)?d:(sdid >= 0 ? sdid : -1-sdid);
99 const int sgid = element_map[dof*e + did]; // signed
100 const int gid = (sgid >= 0) ? sgid : -1-sgid;
101 const int lid = dof*e + d;
102 const bool plus = (sgid >= 0 && sdid >= 0) || (sgid < 0 && sdid < 0);
103 gather_map[lid] = plus ? gid : -1-gid;
104 indices[offsets[gid]++] = plus ? lid : -1-lid;
105 }
106 }
107 // We shifted the offsets vector by 1 by using it as a counter.
108 // Now we shift it back.
109 for (int i = ndofs; i > 0; --i)
110 {
111 offsets[i] = offsets[i - 1];
112 }
113 offsets[0] = 0;
114}
115
116void ElementRestriction::Mult(const Vector& x, Vector& y) const
117{
118 // Assumes all elements have the same number of dofs
119 const int nd = dof;
120 const int vd = vdim;
121 const bool t = byvdim;
122 auto d_x = Reshape(x.Read(), t?vd:ndofs, t?ndofs:vd);
123 auto d_y = Reshape(y.Write(), nd, vd, ne);
124 auto d_gather_map = gather_map.Read();
125 mfem::forall(dof*ne, [=] MFEM_HOST_DEVICE (int i)
126 {
127 const int gid = d_gather_map[i];
128 const bool plus = gid >= 0;
129 const int j = plus ? gid : -1-gid;
130 for (int c = 0; c < vd; ++c)
131 {
132 const real_t dof_value = d_x(t?c:j, t?j:c);
133 d_y(i % nd, c, i / nd) = plus ? dof_value : -dof_value;
134 }
135 });
136}
137
139{
140 // Assumes all elements have the same number of dofs
141 const int nd = dof;
142 const int vd = vdim;
143 const bool t = byvdim;
144 auto d_x = Reshape(x.Read(), t?vd:ndofs, t?ndofs:vd);
145 auto d_y = Reshape(y.Write(), nd, vd, ne);
146 auto d_gather_map = gather_map.Read();
147
148 mfem::forall(dof*ne, [=] MFEM_HOST_DEVICE (int i)
149 {
150 const int gid = d_gather_map[i];
151 const int j = gid >= 0 ? gid : -1-gid;
152 for (int c = 0; c < vd; ++c)
153 {
154 d_y(i % nd, c, i / nd) = d_x(t?c:j, t?j:c);
155 }
156 });
157}
158
159template <bool ADD>
160void ElementRestriction::TAddMultTranspose(const Vector& x, Vector& y) const
161{
162 // Assumes all elements have the same number of dofs
163 const int nd = dof;
164 const int vd = vdim;
165 const bool t = byvdim;
166 auto d_offsets = offsets.Read();
167 auto d_indices = indices.Read();
168 auto d_x = Reshape(x.Read(), nd, vd, ne);
169 auto d_y = Reshape(ADD ? y.ReadWrite() : y.Write(), t?vd:ndofs, t?ndofs:vd);
170 mfem::forall(ndofs, [=] MFEM_HOST_DEVICE (int i)
171 {
172 const int offset = d_offsets[i];
173 const int next_offset = d_offsets[i + 1];
174 for (int c = 0; c < vd; ++c)
175 {
176 real_t dof_value = 0;
177 for (int j = offset; j < next_offset; ++j)
178 {
179 const int idx_j = (d_indices[j] >= 0) ? d_indices[j] : -1 - d_indices[j];
180 dof_value += ((d_indices[j] >= 0) ? d_x(idx_j % nd, c, idx_j / nd) :
181 -d_x(idx_j % nd, c, idx_j / nd));
182 }
183 if (ADD) { d_y(t?c:i,t?i:c) += dof_value; }
184 else { d_y(t?c:i,t?i:c) = dof_value; }
185 }
186 });
187}
188
190{
191 constexpr bool ADD = false;
192 TAddMultTranspose<ADD>(x, y);
193}
194
196 const real_t a) const
197{
198 MFEM_VERIFY(a == 1.0, "General coefficient case is not yet supported!");
199 constexpr bool ADD = true;
200 TAddMultTranspose<ADD>(x, y);
201}
202
204{
205 // Assumes all elements have the same number of dofs
206 const int nd = dof;
207 const int vd = vdim;
208 const bool t = byvdim;
209 auto d_offsets = offsets.Read();
210 auto d_indices = indices.Read();
211 auto d_x = Reshape(x.Read(), nd, vd, ne);
212 auto d_y = Reshape(y.Write(), t?vd:ndofs, t?ndofs:vd);
213 mfem::forall(ndofs, [=] MFEM_HOST_DEVICE (int i)
214 {
215 const int offset = d_offsets[i];
216 const int next_offset = d_offsets[i + 1];
217 for (int c = 0; c < vd; ++c)
218 {
219 real_t dof_value = 0;
220 for (int j = offset; j < next_offset; ++j)
221 {
222 const int idx_j = (d_indices[j] >= 0) ? d_indices[j] : -1 - d_indices[j];
223 dof_value += d_x(idx_j % nd, c, idx_j / nd);
224 }
225 d_y(t?c:i,t?i:c) = dof_value;
226 }
227 });
228}
229
231{
232 // Assumes all elements have the same number of dofs
233 const int nd = dof;
234 const int vd = vdim;
235 const bool t = byvdim;
236 auto d_offsets = offsets.Read();
237 auto d_indices = indices.Read();
238 auto d_x = Reshape(x.Read(), nd, vd, ne);
239 auto d_y = Reshape(y.Write(), t?vd:ndofs, t?ndofs:vd);
240 mfem::forall(ndofs, [=] MFEM_HOST_DEVICE (int i)
241 {
242 const int next_offset = d_offsets[i + 1];
243 for (int c = 0; c < vd; ++c)
244 {
245 real_t dof_value = 0;
246 const int j = next_offset - 1;
247 const int idx_j = (d_indices[j] >= 0) ? d_indices[j] : -1 - d_indices[j];
248 dof_value = (d_indices[j] >= 0) ? d_x(idx_j % nd, c, idx_j / nd) :
249 -d_x(idx_j % nd, c, idx_j / nd);
250 d_y(t?c:i,t?i:c) = dof_value;
251 }
252 });
253}
254
256{
257 // Assumes all elements have the same number of dofs
258 const int nd = dof;
259 const int vd = vdim;
260 const bool t = byvdim;
261
262 Array<char> processed(vd * ndofs);
263 processed = 0;
264
265 auto d_offsets = offsets.HostRead();
266 auto d_indices = indices.HostRead();
267 auto d_x = Reshape(processed.HostReadWrite(), t?vd:ndofs, t?ndofs:vd);
268 auto d_y = Reshape(y.HostWrite(), nd, vd, ne);
269 for (int i = 0; i < ndofs; ++i)
270 {
271 const int offset = d_offsets[i];
272 const int next_offset = d_offsets[i+1];
273 for (int c = 0; c < vd; ++c)
274 {
275 for (int j = offset; j < next_offset; ++j)
276 {
277 const int idx_j = d_indices[j];
278 if (d_x(t?c:i,t?i:c))
279 {
280 d_y(idx_j % nd, c, idx_j / nd) = 0.0;
281 }
282 else
283 {
284 d_y(idx_j % nd, c, idx_j / nd) = 1.0;
285 d_x(t?c:i,t?i:c) = 1;
286 }
287 }
288 }
289 }
290}
291
293 SparseMatrix &mat) const
294{
295 mat.GetMemoryI().New(mat.Height()+1, mat.GetMemoryI().GetMemoryType());
296 const int nnz = FillI(mat);
297 mat.GetMemoryJ().New(nnz, mat.GetMemoryJ().GetMemoryType());
298 mat.GetMemoryData().New(nnz, mat.GetMemoryData().GetMemoryType());
299 FillJAndData(mat_ea, mat);
300}
301
302static MFEM_HOST_DEVICE int GetMinElt(const int *my_elts, const int nbElts,
303 const int *nbr_elts, const int nbrNbElts)
304{
305 // Find the minimal element index found in both my_elts[] and nbr_elts[]
306 int min_el = INT_MAX;
307 for (int i = 0; i < nbElts; i++)
308 {
309 const int e_i = my_elts[i];
310 if (e_i >= min_el) { continue; }
311 for (int j = 0; j < nbrNbElts; j++)
312 {
313 if (e_i==nbr_elts[j])
314 {
315 min_el = e_i; // we already know e_i < min_el
316 break;
317 }
318 }
319 }
320 return min_el;
321}
322
323/** Returns the index where a non-zero entry should be added and increment the
324 number of non-zeros for the row i_L. */
325static MFEM_HOST_DEVICE int GetAndIncrementNnzIndex(const int i_L, int* I)
326{
327 int ind = AtomicAdd(I[i_L],1);
328 return ind;
329}
330
332{
333 const int all_dofs = ndofs;
334 const int vd = vdim;
335 const int elt_dofs = dof;
336 auto I = mat.ReadWriteI();
337 auto d_offsets = offsets.Read();
338 auto d_indices = indices.Read();
339 auto d_gather_map = gather_map.Read();
340
341 Array<int> ij_elts(indices.Size() * 2);
342 auto d_ij_elts = Reshape(ij_elts.Write(), indices.Size(), 2);
343
344 mfem::forall(vd*all_dofs+1, [=] MFEM_HOST_DEVICE (int i_L)
345 {
346 I[i_L] = 0;
347 });
348 mfem::forall(ne*elt_dofs, [=] MFEM_HOST_DEVICE (int l_dof)
349 {
350 const int e = l_dof/elt_dofs;
351 const int i = l_dof%elt_dofs;
352
353 const int i_gm = e*elt_dofs + i;
354 const int i_L = d_gather_map[i_gm];
355 const int i_offset = d_offsets[i_L];
356 const int i_next_offset = d_offsets[i_L+1];
357 const int i_nbElts = i_next_offset - i_offset;
358
359 int *i_elts = &d_ij_elts(i_offset, 0);
360 for (int e_i = 0; e_i < i_nbElts; ++e_i)
361 {
362 const int i_E = d_indices[i_offset+e_i];
363 i_elts[e_i] = i_E/elt_dofs;
364 }
365 for (int j = 0; j < elt_dofs; j++)
366 {
367 const int j_gm = e*elt_dofs + j;
368 const int j_L = d_gather_map[j_gm];
369 const int j_offset = d_offsets[j_L];
370 const int j_next_offset = d_offsets[j_L+1];
371 const int j_nbElts = j_next_offset - j_offset;
372 if (i_nbElts == 1 || j_nbElts == 1) // no assembly required
373 {
374 GetAndIncrementNnzIndex(i_L, I);
375 }
376 else // assembly required
377 {
378 int *j_elts = &d_ij_elts(j_offset, 1);
379 for (int e_j = 0; e_j < j_nbElts; ++e_j)
380 {
381 const int j_E = d_indices[j_offset+e_j];
382 const int elt = j_E/elt_dofs;
383 j_elts[e_j] = elt;
384 }
385 int min_e = GetMinElt(i_elts, i_nbElts, j_elts, j_nbElts);
386 if (e == min_e) // add the nnz only once
387 {
388 GetAndIncrementNnzIndex(i_L, I);
389 }
390 }
391 }
392 });
393 // We need to sum the entries of I, we do it on CPU as it is very sequential.
394 auto h_I = mat.HostReadWriteI();
395 const int nTdofs = vd*all_dofs;
396 int sum = 0;
397 for (int i = 0; i < nTdofs; i++)
398 {
399 const int nnz = h_I[i];
400 h_I[i] = sum;
401 sum+=nnz;
402 }
403 h_I[nTdofs] = sum;
404 // We return the number of nnz
405 return h_I[nTdofs];
406}
407
409 SparseMatrix &mat) const
410{
411 const int all_dofs = ndofs;
412 const int vd = vdim;
413 const int elt_dofs = dof;
414 auto I = mat.ReadWriteI();
415 auto J = mat.WriteJ();
416 auto Data = mat.WriteData();
417 auto d_offsets = offsets.Read();
418 auto d_indices = indices.Read();
419 auto d_gather_map = gather_map.Read();
420 auto mat_ea = Reshape(ea_data.Read(), elt_dofs, elt_dofs, ne);
421
422 Array<int> ij_B_el(indices.Size() * 4);
423 auto d_ij_B_el = Reshape(ij_B_el.Write(), indices.Size(), 4);
424
425 mfem::forall(ne*elt_dofs, [=] MFEM_HOST_DEVICE (int l_dof)
426 {
427 const int e = l_dof/elt_dofs;
428 const int i = l_dof%elt_dofs;
429
430 const int i_gm = e*elt_dofs + i;
431 const int i_L = d_gather_map[i_gm];
432 const int i_offset = d_offsets[i_L];
433 const int i_next_offset = d_offsets[i_L+1];
434 const int i_nbElts = i_next_offset - i_offset;
435
436 int *i_elts = &d_ij_B_el(i_offset, 0);
437 int *i_B = &d_ij_B_el(i_offset, 1);
438 for (int e_i = 0; e_i < i_nbElts; ++e_i)
439 {
440 const int i_E = d_indices[i_offset+e_i];
441 i_elts[e_i] = i_E/elt_dofs;
442 i_B[e_i] = i_E%elt_dofs;
443 }
444 for (int j = 0; j < elt_dofs; j++)
445 {
446 const int j_gm = e*elt_dofs + j;
447 const int j_L = d_gather_map[j_gm];
448 const int j_offset = d_offsets[j_L];
449 const int j_next_offset = d_offsets[j_L+1];
450 const int j_nbElts = j_next_offset - j_offset;
451 if (i_nbElts == 1 || j_nbElts == 1) // no assembly required
452 {
453 const int nnz = GetAndIncrementNnzIndex(i_L, I);
454 J[nnz] = j_L;
455 Data[nnz] = mat_ea(j,i,e);
456 }
457 else // assembly required
458 {
459 int *j_elts = &d_ij_B_el(j_offset, 2);
460 int *j_B = &d_ij_B_el(j_offset, 3);
461 for (int e_j = 0; e_j < j_nbElts; ++e_j)
462 {
463 const int j_E = d_indices[j_offset+e_j];
464 const int elt = j_E/elt_dofs;
465 j_elts[e_j] = elt;
466 j_B[e_j] = j_E%elt_dofs;
467 }
468 int min_e = GetMinElt(i_elts, i_nbElts, j_elts, j_nbElts);
469 if (e == min_e) // add the nnz only once
470 {
471 real_t val = 0.0;
472 for (int k = 0; k < i_nbElts; k++)
473 {
474 const int e_i = i_elts[k];
475 const int i_Bloc = i_B[k];
476 for (int l = 0; l < j_nbElts; l++)
477 {
478 const int e_j = j_elts[l];
479 const int j_Bloc = j_B[l];
480 if (e_i == e_j)
481 {
482 val += mat_ea(j_Bloc, i_Bloc, e_i);
483 }
484 }
485 }
486 const int nnz = GetAndIncrementNnzIndex(i_L, I);
487 J[nnz] = j_L;
488 Data[nnz] = val;
489 }
490 }
491 }
492 });
493 // We need to shift again the entries of I, we do it on CPU as it is very
494 // sequential.
495 auto h_I = mat.HostReadWriteI();
496 const int size = vd*all_dofs;
497 for (int i = 0; i < size; i++)
498 {
499 h_I[size-i] = h_I[size-(i+1)];
500 }
501 h_I[0] = 0;
502}
503
505 : ne(fes.GetNE()),
506 vdim(fes.GetVDim()),
507 byvdim(fes.GetOrdering() == Ordering::byVDIM),
508 ndof(fes.GetTypicalFE()->GetDof()),
509 ndofs(fes.GetNDofs())
510{
511 height = vdim*ne*ndof;
512 width = vdim*ne*ndof;
513}
514
516{
517 const int nd = ndof;
518 const int vd = vdim;
519 const bool t = byvdim;
520 auto d_x = Reshape(x.Read(), t?vd:ndofs, t?ndofs:vd);
521 auto d_y = Reshape(y.Write(), nd, vd, ne);
522 mfem::forall(ndofs, [=] MFEM_HOST_DEVICE (int i)
523 {
524 const int idx = i;
525 const int dof = idx % nd;
526 const int e = idx / nd;
527 for (int c = 0; c < vd; ++c)
528 {
529 d_y(dof, c, e) = d_x(t?c:idx, t?idx:c);
530 }
531 });
532}
533
534template <bool ADD>
535void L2ElementRestriction::TAddMultTranspose(const Vector &x, Vector &y) const
536{
537 const int nd = ndof;
538 const int vd = vdim;
539 const bool t = byvdim;
540 auto d_x = Reshape(x.Read(), nd, vd, ne);
541 auto d_y = Reshape(ADD ? y.ReadWrite() : y.Write(), t?vd:ndofs, t?ndofs:vd);
542 mfem::forall(ndofs, [=] MFEM_HOST_DEVICE (int i)
543 {
544 const int idx = i;
545 const int dof = idx % nd;
546 const int e = idx / nd;
547 for (int c = 0; c < vd; ++c)
548 {
549 if (ADD) { d_y(t?c:idx,t?idx:c) += d_x(dof, c, e); }
550 else { d_y(t?c:idx,t?idx:c) = d_x(dof, c, e); }
551 }
552 });
553}
554
556{
557 constexpr bool ADD = false;
558 TAddMultTranspose<ADD>(x, y);
559}
560
562 const real_t a) const
563{
564 MFEM_VERIFY(a == 1.0, "General coefficient case is not yet supported!");
565 constexpr bool ADD = true;
566 TAddMultTranspose<ADD>(x, y);
567}
568
570{
571 const int elem_dofs = ndof;
572 const int vd = vdim;
573 auto I = mat.WriteI();
574 const int isize = mat.Height() + 1;
575 const int interior_dofs = ne*elem_dofs*vd;
576 mfem::forall(isize, [=] MFEM_HOST_DEVICE (int dof)
577 {
578 I[dof] = dof<interior_dofs ? elem_dofs : 0;
579 });
580}
581
582static MFEM_HOST_DEVICE int AddNnz(const int iE, int *I, const int dofs)
583{
584 int val = AtomicAdd(I[iE],dofs);
585 return val;
586}
587
589 SparseMatrix &mat) const
590{
591 const int elem_dofs = ndof;
592 const int vd = vdim;
593 auto I = mat.ReadWriteI();
594 auto J = mat.WriteJ();
595 auto Data = mat.WriteData();
596 auto mat_ea = Reshape(ea_data.Read(), elem_dofs, elem_dofs, ne);
597 mfem::forall(ne*elem_dofs*vd, [=] MFEM_HOST_DEVICE (int iE)
598 {
599 const int offset = AddNnz(iE,I,elem_dofs);
600 const int e = iE/elem_dofs;
601 const int i = iE%elem_dofs;
602 for (int j = 0; j < elem_dofs; j++)
603 {
604 J[offset+j] = e*elem_dofs+j;
605 Data[offset+j] = mat_ea(j,i,e);
606 }
607 });
608}
609
611 const FiniteElementSpace &fes,
612 const ElementDofOrdering f_ordering,
613 const FaceType type,
614 bool build)
615 : fes(fes),
616 nf(fes.GetNFbyType(type)),
617 vdim(fes.GetVDim()),
618 byvdim(fes.GetOrdering() == Ordering::byVDIM),
619 face_dofs(nf > 0 ? fes.GetFaceElement(0)->GetDof() : 0),
620 elem_dofs(fes.GetTypicalFE()->GetDof()),
621 nfdofs(nf*face_dofs),
622 ndofs(fes.GetNDofs()),
623 scatter_indices(nf*face_dofs),
624 gather_offsets(ndofs+1),
625 gather_indices(nf*face_dofs),
626 face_map(face_dofs)
627{
629 width = fes.GetVSize();
630 if (nf==0) { return; }
631
632 CheckFESpace(f_ordering);
633
634 // Get the mapping from lexicographic DOF ordering to native ordering.
635 const TensorBasisElement* el =
636 dynamic_cast<const TensorBasisElement*>(fes.GetTypicalFE());
637 const Array<int> &dof_map_ = el->GetDofMap();
638 if (dof_map_.Size() > 0)
639 {
640 vol_dof_map.MakeRef(dof_map_);
641 }
642 else
643 {
644 // For certain types of elements dof_map_ is empty. In this case, that
645 // means the element is already ordered lexicographically, so the
646 // permutation is the identity.
648 for (int i = 0; i < elem_dofs; ++i) { vol_dof_map[i] = i; }
649 }
650
651 if (!build) { return; }
652 ComputeScatterIndicesAndOffsets(f_ordering, type);
653 ComputeGatherIndices(f_ordering,type);
654}
655
657 const FiniteElementSpace &fes,
658 const ElementDofOrdering f_ordering,
659 const FaceType type)
660 : ConformingFaceRestriction(fes, f_ordering, type, true)
661{ }
662
664 const bool useAbs) const
665{
666 if (nf==0) { return; }
667 // Assumes all elements have the same number of dofs
668 const int nface_dofs = face_dofs;
669 const int vd = vdim;
670 const bool t = byvdim;
671 auto d_indices = scatter_indices.Read();
672 auto d_x = Reshape(x.Read(), t?vd:ndofs, t?ndofs:vd);
673 auto d_y = Reshape(y.Write(), nface_dofs, vd, nf);
674 mfem::forall(nfdofs, [=] MFEM_HOST_DEVICE (int i)
675 {
676 const int s_idx = d_indices[i];
677 const int sgn = (useAbs || s_idx >= 0) ? 1 : -1;
678 const int idx = (s_idx >= 0) ? s_idx : -1 - s_idx;
679 const int dof = i % nface_dofs;
680 const int face = i / nface_dofs;
681 for (int c = 0; c < vd; ++c)
682 {
683 d_y(dof, c, face) = sgn*d_x(t?c:idx, t?idx:c);
684 }
685 });
686}
687
688static void ConformingFaceRestriction_AddMultTranspose(
689 const int ndofs,
690 const int face_dofs,
691 const int nf,
692 const int vdim,
693 const bool by_vdim,
694 const Array<int> &gather_offsets,
695 const Array<int> &gather_indices,
696 const Vector &x,
697 Vector &y,
698 bool use_signs,
699 const real_t a)
700{
701 MFEM_VERIFY(a == 1.0, "General coefficient case is not yet supported!");
702 if (nf==0) { return; }
703 // Assumes all elements have the same number of dofs
704 auto d_offsets = gather_offsets.Read();
705 auto d_indices = gather_indices.Read();
706 auto d_x = Reshape(x.Read(), face_dofs, vdim, nf);
707 auto d_y = Reshape(y.ReadWrite(), by_vdim?vdim:ndofs, by_vdim?ndofs:vdim);
708 mfem::forall(ndofs, [=] MFEM_HOST_DEVICE (int i)
709 {
710 const int offset = d_offsets[i];
711 const int next_offset = d_offsets[i + 1];
712 for (int c = 0; c < vdim; ++c)
713 {
714 real_t dof_value = 0;
715 for (int j = offset; j < next_offset; ++j)
716 {
717 const int s_idx_j = d_indices[j];
718 const real_t sgn = (s_idx_j >= 0 || !use_signs) ? 1.0 : -1.0;
719 const int idx_j = (s_idx_j >= 0) ? s_idx_j : -1 - s_idx_j;
720 dof_value += sgn*d_x(idx_j % face_dofs, c, idx_j / face_dofs);
721 }
722 d_y(by_vdim?c:i,by_vdim?i:c) += dof_value;
723 }
724 });
725}
726
728 const Vector& x, Vector& y, const real_t a) const
729{
730 ConformingFaceRestriction_AddMultTranspose(
732 true, a);
733}
734
736 const Vector& x, Vector& y, const real_t a) const
737{
738 ConformingFaceRestriction_AddMultTranspose(
740 false, a);
741}
742
744 f_ordering)
745{
746#ifdef MFEM_USE_MPI
747
748 // If the underlying finite element space is parallel, ensure the face
749 // neighbor information is generated.
750 if (const ParFiniteElementSpace *pfes
751 = dynamic_cast<const ParFiniteElementSpace*>(&fes))
752 {
753 pfes->GetParMesh()->ExchangeFaceNbrData();
754 }
755
756#endif
757
758#ifdef MFEM_DEBUG
759 const FiniteElement *fe0 = fes.GetTypicalFE();
760 const TensorBasisElement *tfe = dynamic_cast<const TensorBasisElement*>(fe0);
761 MFEM_VERIFY(tfe != NULL,
762 "ConformingFaceRestriction only supports TensorBasisElements");
763 MFEM_VERIFY(tfe->GetBasisType()==BasisType::GaussLobatto ||
765 "ConformingFaceRestriction only supports Gauss-Lobatto and Bernstein bases");
766
767 // Assuming all finite elements are using Gauss-Lobatto.
768 const bool dof_reorder = (f_ordering == ElementDofOrdering::LEXICOGRAPHIC);
769 if (dof_reorder && nf > 0)
770 {
771 for (int f = 0; f < fes.GetNF(); ++f)
772 {
773 const FiniteElement *fe = fes.GetFaceElement(f);
774 const TensorBasisElement* el =
775 dynamic_cast<const TensorBasisElement*>(fe);
776 if (el) { continue; }
777 MFEM_ABORT("Finite element not suitable for lexicographic ordering");
778 }
779 }
780#endif
781}
782
783void ConformingFaceRestriction::ComputeScatterIndicesAndOffsets(
784 const ElementDofOrdering f_ordering,
785 const FaceType type)
786{
787 Mesh &mesh = *fes.GetMesh();
788
789 // Initialization of the offsets
790 for (int i = 0; i <= ndofs; ++i)
791 {
792 gather_offsets[i] = 0;
793 }
794
795 // Computation of scatter indices and offsets
796 int f_ind = 0;
797 for (int f = 0; f < fes.GetNF(); ++f)
798 {
799 Mesh::FaceInformation face = mesh.GetFaceInformation(f);
800 if ( face.IsNonconformingCoarse() )
801 {
802 // We skip nonconforming coarse faces as they are treated
803 // by the corresponding nonconforming fine faces.
804 continue;
805 }
806 else if ( face.IsOfFaceType(type) )
807 {
808 SetFaceDofsScatterIndices(face, f_ind, f_ordering);
809 f_ind++;
810 }
811 }
812 MFEM_VERIFY(f_ind==nf, "Unexpected number of faces.");
813
814 // Summation of the offsets
815 for (int i = 1; i <= ndofs; ++i)
816 {
817 gather_offsets[i] += gather_offsets[i - 1];
818 }
819}
820
821void ConformingFaceRestriction::ComputeGatherIndices(
822 const ElementDofOrdering f_ordering,
823 const FaceType type)
824{
825 Mesh &mesh = *fes.GetMesh();
826
827 // Computation of gather_indices
828 int f_ind = 0;
829 for (int f = 0; f < fes.GetNF(); ++f)
830 {
831 Mesh::FaceInformation face = mesh.GetFaceInformation(f);
832 if ( face.IsNonconformingCoarse() )
833 {
834 // We skip nonconforming coarse faces as they are treated
835 // by the corresponding nonconforming fine faces.
836 continue;
837 }
838 else if ( face.IsOfFaceType(type) )
839 {
840 SetFaceDofsGatherIndices(face, f_ind, f_ordering);
841 f_ind++;
842 }
843 }
844 MFEM_VERIFY(f_ind==nf, "Unexpected number of faces.");
845
846 // Reset offsets to their initial value
847 for (int i = ndofs; i > 0; --i)
848 {
849 gather_offsets[i] = gather_offsets[i - 1];
850 }
851 gather_offsets[0] = 0;
852}
853
855 const Mesh::FaceInformation &face,
856 const int face_index,
857 const ElementDofOrdering f_ordering)
858{
859 MFEM_ASSERT(!(face.IsNonconformingCoarse()),
860 "This method should not be used on nonconforming coarse faces.");
861 MFEM_ASSERT(face.element[0].orientation==0,
862 "FaceRestriction used on degenerated mesh.");
863 MFEM_VERIFY(f_ordering == ElementDofOrdering::LEXICOGRAPHIC,
864 "NATIVE ordering is not supported yet");
865
867
868 const Table& e2dTable = fes.GetElementToDofTable();
869 const int* elem_map = e2dTable.GetJ();
870 const int elem_index = face.element[0].index;
871
872 for (int face_dof = 0; face_dof < face_dofs; ++face_dof)
873 {
874 const int lex_volume_dof = face_map[face_dof];
875 const int s_volume_dof = AsConst(vol_dof_map)[lex_volume_dof]; // signed
876 const int volume_dof = UnsignIndex(s_volume_dof);
877 const int s_global_dof = elem_map[elem_index*elem_dofs + volume_dof];
878 const int global_dof = UnsignIndex(s_global_dof);
879 const int restriction_dof = face_dofs*face_index + face_dof;
880 scatter_indices[restriction_dof] = s_global_dof;
881 ++gather_offsets[global_dof + 1];
882 }
883}
884
886 const Mesh::FaceInformation &face,
887 const int face_index,
888 const ElementDofOrdering f_ordering)
889{
890 MFEM_ASSERT(!(face.IsNonconformingCoarse()),
891 "This method should not be used on nonconforming coarse faces.");
892 MFEM_VERIFY(f_ordering == ElementDofOrdering::LEXICOGRAPHIC,
893 "NATIVE ordering is not supported yet");
894
896
897 const Table& e2dTable = fes.GetElementToDofTable();
898 const int* elem_map = e2dTable.GetJ();
899 const int elem_index = face.element[0].index;
900
901 for (int face_dof = 0; face_dof < face_dofs; ++face_dof)
902 {
903 const int lex_volume_dof = face_map[face_dof];
904 const int s_volume_dof = AsConst(vol_dof_map)[lex_volume_dof];
905 const int volume_dof = UnsignIndex(s_volume_dof);
906 const int s_global_dof = elem_map[elem_index*elem_dofs + volume_dof];
907 const int sgn = (s_global_dof >= 0) ? 1 : -1;
908 const int global_dof = UnsignIndex(s_global_dof);
909 const int restriction_dof = face_dofs*face_index + face_dof;
910 const int s_restriction_dof = (sgn >= 0) ? restriction_dof : -1 -
911 restriction_dof;
912 gather_indices[gather_offsets[global_dof]++] = s_restriction_dof;
913 }
914}
915
916// Permute dofs or quads on a face for e2 to match with the ordering of e1
917int PermuteFaceL2(const int dim, const int face_id1,
918 const int face_id2, const int orientation,
919 const int size1d, const int index)
920{
921 switch (dim)
922 {
923 case 1:
924 return 0;
925 case 2:
926 return internal::PermuteFace2D(face_id1, face_id2, orientation, size1d, index);
927 case 3:
928 return internal::PermuteFace3D(face_id1, face_id2, orientation, size1d, index);
929 default:
930 MFEM_ABORT("Unsupported dimension.");
931 return 0;
932 }
933}
934
936 const ElementDofOrdering f_ordering,
937 const FaceType type,
938 const L2FaceValues m,
939 bool build)
940 : fes(fes),
941 ordering(f_ordering),
942 nf(fes.GetNFbyType(type)),
943 ne(fes.GetNE()),
944 vdim(fes.GetVDim()),
945 byvdim(fes.GetOrdering() == Ordering::byVDIM),
946 face_dofs(fes.GetTypicalTraceElement()->GetDof()),
947 elem_dofs(fes.GetTypicalFE()->GetDof()),
948 nfdofs(nf*face_dofs),
949 ndofs(fes.GetNDofs()),
950 type(type),
951 m(m),
952 scatter_indices1(nf*face_dofs),
953 scatter_indices2(m==L2FaceValues::DoubleValued?nf*face_dofs:0),
954 gather_offsets(ndofs+1),
955 gather_indices((m==L2FaceValues::DoubleValued? 2 : 1)*nf*face_dofs),
956 face_map(face_dofs)
957{
959 width = fes.GetVSize();
960 if (!build) { return; }
961
962 CheckFESpace();
963 ComputeScatterIndicesAndOffsets();
964 ComputeGatherIndices();
965}
966
968 const ElementDofOrdering f_ordering,
969 const FaceType type,
970 const L2FaceValues m)
971 : L2FaceRestriction(fes, f_ordering, type, m, true)
972{ }
973
975 Vector& y) const
976{
977 if (nf == 0) { return; }
978 MFEM_ASSERT(
980 "This method should be called when m == L2FaceValues::SingleValued.");
981 // Assumes all elements have the same number of dofs
982 const int nface_dofs = face_dofs;
983 const int vd = vdim;
984 const bool t = byvdim;
985 auto d_indices1 = scatter_indices1.Read();
986 auto d_x = Reshape(x.Read(), t?vd:ndofs, t?ndofs:vd);
987 auto d_y = Reshape(y.Write(), nface_dofs, vd, nf);
988 mfem::forall(nfdofs, [=] MFEM_HOST_DEVICE (int i)
989 {
990 const int dof = i % nface_dofs;
991 const int face = i / nface_dofs;
992 const int idx1 = d_indices1[i];
993 for (int c = 0; c < vd; ++c)
994 {
995 d_y(dof, c, face) = d_x(t?c:idx1, t?idx1:c);
996 }
997 });
998}
999
1001 Vector& y) const
1002{
1003 MFEM_ASSERT(
1005 "This method should be called when m == L2FaceValues::DoubleValued.");
1006 // Assumes all elements have the same number of dofs
1007 const int nface_dofs = face_dofs;
1008 const int vd = vdim;
1009 const bool t = byvdim;
1010 auto d_indices1 = scatter_indices1.Read();
1011 auto d_indices2 = scatter_indices2.Read();
1012 auto d_x = Reshape(x.Read(), t?vd:ndofs, t?ndofs:vd);
1013 auto d_y = Reshape(y.Write(), nface_dofs, vd, 2, nf);
1014 mfem::forall(nfdofs, [=] MFEM_HOST_DEVICE (int i)
1015 {
1016 const int dof = i % nface_dofs;
1017 const int face = i / nface_dofs;
1018 const int idx1 = d_indices1[i];
1019 for (int c = 0; c < vd; ++c)
1020 {
1021 d_y(dof, c, 0, face) = d_x(t?c:idx1, t?idx1:c);
1022 }
1023 const int idx2 = d_indices2[i];
1024 for (int c = 0; c < vd; ++c)
1025 {
1026 d_y(dof, c, 1, face) = idx2==-1 ? 0.0 : d_x(t?c:idx2, t?idx2:c);
1027 }
1028 });
1029}
1030
1031void L2FaceRestriction::Mult(const Vector& x, Vector& y) const
1032{
1033 if (nf==0) { return; }
1035 {
1037 }
1038 else
1039 {
1041 }
1042}
1043
1045 const Vector& x, Vector& y) const
1046{
1047 // Assumes all elements have the same number of dofs
1048 const int nface_dofs = face_dofs;
1049 const int vd = vdim;
1050 const bool t = byvdim;
1051 auto d_offsets = gather_offsets.Read();
1052 auto d_indices = gather_indices.Read();
1053 auto d_x = Reshape(x.Read(), nface_dofs, vd, nf);
1054 auto d_y = Reshape(y.ReadWrite(), t?vd:ndofs, t?ndofs:vd);
1055 mfem::forall(ndofs, [=] MFEM_HOST_DEVICE (int i)
1056 {
1057 const int offset = d_offsets[i];
1058 const int next_offset = d_offsets[i + 1];
1059 for (int c = 0; c < vd; ++c)
1060 {
1061 real_t dof_value = 0;
1062 for (int j = offset; j < next_offset; ++j)
1063 {
1064 int idx_j = d_indices[j];
1065 dof_value += d_x(idx_j % nface_dofs, c, idx_j / nface_dofs);
1066 }
1067 d_y(t?c:i,t?i:c) += dof_value;
1068 }
1069 });
1070}
1071
1073 const Vector& x, Vector& y) const
1074{
1075 // Assumes all elements have the same number of dofs
1076 const int nface_dofs = face_dofs;
1077 const int vd = vdim;
1078 const bool t = byvdim;
1079 const int dofs = nfdofs;
1080 auto d_offsets = gather_offsets.Read();
1081 auto d_indices = gather_indices.Read();
1082 auto d_x = Reshape(x.Read(), nface_dofs, vd, 2, nf);
1083 auto d_y = Reshape(y.ReadWrite(), t?vd:ndofs, t?ndofs:vd);
1084 mfem::forall(ndofs, [=] MFEM_HOST_DEVICE (int i)
1085 {
1086 const int offset = d_offsets[i];
1087 const int next_offset = d_offsets[i + 1];
1088 for (int c = 0; c < vd; ++c)
1089 {
1090 real_t dof_value = 0;
1091 for (int j = offset; j < next_offset; ++j)
1092 {
1093 int idx_j = d_indices[j];
1094 bool isE1 = idx_j < dofs;
1095 idx_j = isE1 ? idx_j : idx_j - dofs;
1096 dof_value += isE1 ?
1097 d_x(idx_j % nface_dofs, c, 0, idx_j / nface_dofs)
1098 :d_x(idx_j % nface_dofs, c, 1, idx_j / nface_dofs);
1099 }
1100 d_y(t?c:i,t?i:c) += dof_value;
1101 }
1102 });
1103}
1104
1106 const real_t a) const
1107{
1108 MFEM_VERIFY(a == 1.0, "General coefficient case is not yet supported!");
1109 if (nf==0) { return; }
1111 {
1113 }
1114 else
1115 {
1117 }
1118}
1119
1121 const bool keep_nbr_block) const
1122{
1123 const int nface_dofs = face_dofs;
1124 auto d_indices1 = scatter_indices1.Read();
1125 auto d_indices2 = scatter_indices2.Read();
1126 auto I = mat.ReadWriteI();
1127 mfem::forall(nf*nface_dofs, [=] MFEM_HOST_DEVICE (int fdof)
1128 {
1129 const int iE1 = d_indices1[fdof];
1130 const int iE2 = d_indices2[fdof];
1131 AddNnz(iE1,I,nface_dofs);
1132 AddNnz(iE2,I,nface_dofs);
1133 });
1134}
1135
1137 SparseMatrix &mat,
1138 const bool keep_nbr_block) const
1139{
1140 const int nface_dofs = face_dofs;
1141 auto d_indices1 = scatter_indices1.Read();
1142 auto d_indices2 = scatter_indices2.Read();
1143 auto I = mat.ReadWriteI();
1144 auto mat_fea = Reshape(fea_data.Read(), nface_dofs, nface_dofs, 2, nf);
1145 auto J = mat.WriteJ();
1146 auto Data = mat.WriteData();
1147 mfem::forall(nf*nface_dofs, [=] MFEM_HOST_DEVICE (int fdof)
1148 {
1149 const int f = fdof/nface_dofs;
1150 const int iF = fdof%nface_dofs;
1151 const int iE1 = d_indices1[f*nface_dofs+iF];
1152 const int iE2 = d_indices2[f*nface_dofs+iF];
1153 const int offset1 = AddNnz(iE1,I,nface_dofs);
1154 const int offset2 = AddNnz(iE2,I,nface_dofs);
1155 for (int jF = 0; jF < nface_dofs; jF++)
1156 {
1157 const int jE1 = d_indices1[f*nface_dofs+jF];
1158 const int jE2 = d_indices2[f*nface_dofs+jF];
1159 J[offset2+jF] = jE1;
1160 J[offset1+jF] = jE2;
1161 Data[offset2+jF] = mat_fea(jF,iF,0,f);
1162 Data[offset1+jF] = mat_fea(jF,iF,1,f);
1163 }
1164 });
1165}
1166
1168 Vector &ea_data) const
1169{
1170 const int nface_dofs = face_dofs;
1171 const int nelem_dofs = elem_dofs;
1172 const int NE = ne;
1174 {
1175 auto d_indices1 = scatter_indices1.Read();
1176 auto d_indices2 = scatter_indices2.Read();
1177 auto mat_fea = Reshape(fea_data.Read(), nface_dofs, nface_dofs, 2, nf);
1178 auto mat_ea = Reshape(ea_data.ReadWrite(), nelem_dofs, nelem_dofs, ne);
1179 mfem::forall(nf, [=] MFEM_HOST_DEVICE (int f)
1180 {
1181 const int e1 = d_indices1[f*nface_dofs]/nelem_dofs;
1182 const int e2 = d_indices2[f*nface_dofs]/nelem_dofs;
1183 for (int j = 0; j < nface_dofs; j++)
1184 {
1185 const int jB1 = d_indices1[f*nface_dofs+j]%nelem_dofs;
1186 for (int i = 0; i < nface_dofs; i++)
1187 {
1188 const int iB1 = d_indices1[f*nface_dofs+i]%nelem_dofs;
1189 AtomicAdd(mat_ea(iB1,jB1,e1), mat_fea(i,j,0,f));
1190 }
1191 }
1192 if (e2 < NE)
1193 {
1194 for (int j = 0; j < nface_dofs; j++)
1195 {
1196 const int jB2 = d_indices2[f*nface_dofs+j]%nelem_dofs;
1197 for (int i = 0; i < nface_dofs; i++)
1198 {
1199 const int iB2 = d_indices2[f*nface_dofs+i]%nelem_dofs;
1200 AtomicAdd(mat_ea(iB2,jB2,e2), mat_fea(i,j,1,f));
1201 }
1202 }
1203 }
1204 });
1205 }
1206 else
1207 {
1208 auto d_indices = scatter_indices1.Read();
1209 auto mat_fea = Reshape(fea_data.Read(), nface_dofs, nface_dofs, nf);
1210 auto mat_ea = Reshape(ea_data.ReadWrite(), nelem_dofs, nelem_dofs, ne);
1211 mfem::forall(nf, [=] MFEM_HOST_DEVICE (int f)
1212 {
1213 const int e = d_indices[f*nface_dofs]/nelem_dofs;
1214 for (int j = 0; j < nface_dofs; j++)
1215 {
1216 const int jE = d_indices[f*nface_dofs+j]%nelem_dofs;
1217 for (int i = 0; i < nface_dofs; i++)
1218 {
1219 const int iE = d_indices[f*nface_dofs+i]%nelem_dofs;
1220 AtomicAdd(mat_ea(iE,jE,e), mat_fea(i,j,f));
1221 }
1222 }
1223 });
1224 }
1225}
1226
1228{
1229#ifdef MFEM_USE_MPI
1230
1231 // If the underlying finite element space is parallel, ensure the face
1232 // neighbor information is generated.
1233 if (const ParFiniteElementSpace *pfes
1234 = dynamic_cast<const ParFiniteElementSpace*>(&fes))
1235 {
1236 pfes->GetParMesh()->ExchangeFaceNbrData();
1237 }
1238
1239#endif
1240
1241#ifdef MFEM_DEBUG
1242 // If fespace == L2
1243 const FiniteElement *fe0 = fes.GetTypicalFE();
1244 const TensorBasisElement *tfe = dynamic_cast<const TensorBasisElement*>(fe0);
1245 MFEM_VERIFY(tfe != NULL &&
1248 "Only Gauss-Lobatto and Bernstein basis are supported in "
1249 "L2FaceRestriction.");
1250 if (nf==0) { return; }
1251 const bool dof_reorder = (ordering == ElementDofOrdering::LEXICOGRAPHIC);
1252 if (!dof_reorder)
1253 {
1254 MFEM_ABORT("Non-Tensor L2FaceRestriction not yet implemented.");
1255 }
1256 if (dof_reorder && nf > 0)
1257 {
1258 for (int f = 0; f < fes.GetNF(); ++f)
1259 {
1261 const TensorBasisElement* el = dynamic_cast<const TensorBasisElement*>(fe);
1262 if (el) { continue; }
1263 MFEM_ABORT("Finite element not suitable for lexicographic ordering");
1264 }
1265 }
1266#endif
1267}
1268
1269void L2FaceRestriction::ComputeScatterIndicesAndOffsets()
1270{
1271 Mesh &mesh = *fes.GetMesh();
1272 // Initialization of the offsets
1273 for (int i = 0; i <= ndofs; ++i)
1274 {
1275 gather_offsets[i] = 0;
1276 }
1277
1278 // Computation of scatter indices and offsets
1279 int f_ind=0;
1280 for (int f = 0; f < fes.GetNF(); ++f)
1281 {
1282 Mesh::FaceInformation face = mesh.GetFaceInformation(f);
1283 MFEM_ASSERT(!face.IsShared(),
1284 "Unexpected shared face in L2FaceRestriction.");
1285 if ( face.IsOfFaceType(type) )
1286 {
1287 SetFaceDofsScatterIndices1(face,f_ind);
1289 {
1290 if ( type==FaceType::Interior && face.IsInterior() )
1291 {
1293 }
1294 else if ( type==FaceType::Boundary && face.IsBoundary() )
1295 {
1297 }
1298 }
1299 f_ind++;
1300 }
1301 }
1302 MFEM_VERIFY(f_ind==nf, "Unexpected number of faces.");
1303
1304 // Summation of the offsets
1305 for (int i = 1; i <= ndofs; ++i)
1306 {
1307 gather_offsets[i] += gather_offsets[i - 1];
1308 }
1309}
1310
1311void L2FaceRestriction::ComputeGatherIndices()
1312{
1313 Mesh &mesh = *fes.GetMesh();
1314 // Computation of gather_indices
1315 int f_ind = 0;
1316 for (int f = 0; f < fes.GetNF(); ++f)
1317 {
1318 Mesh::FaceInformation face = mesh.GetFaceInformation(f);
1319 MFEM_ASSERT(!face.IsShared(),
1320 "Unexpected shared face in L2FaceRestriction.");
1321 if ( face.IsOfFaceType(type) )
1322 {
1323 SetFaceDofsGatherIndices1(face,f_ind);
1326 face.IsLocal())
1327 {
1329 }
1330 f_ind++;
1331 }
1332 }
1333 MFEM_VERIFY(f_ind==nf, "Unexpected number of faces.");
1334
1335 // Reset offsets to their correct value
1336 for (int i = ndofs; i > 0; --i)
1337 {
1338 gather_offsets[i] = gather_offsets[i - 1];
1339 }
1340 gather_offsets[0] = 0;
1341}
1342
1344 const Mesh::FaceInformation &face,
1345 const int face_index)
1346{
1347 MFEM_ASSERT(!(face.IsNonconformingCoarse()),
1348 "This method should not be used on nonconforming coarse faces.");
1349 const Table& e2dTable = fes.GetElementToDofTable();
1350 const int* elem_map = e2dTable.GetJ();
1351 const int face_id1 = face.element[0].local_face_id;
1352 const int elem_index = face.element[0].index;
1353 fes.GetTypicalFE()->GetFaceMap(face_id1, face_map);
1354
1355 for (int face_dof_elem1 = 0; face_dof_elem1 < face_dofs; ++face_dof_elem1)
1356 {
1357 const int volume_dof_elem1 = face_map[face_dof_elem1];
1358 const int global_dof_elem1 = elem_map[elem_index*elem_dofs + volume_dof_elem1];
1359 const int restriction_dof_elem1 = face_dofs*face_index + face_dof_elem1;
1360 scatter_indices1[restriction_dof_elem1] = global_dof_elem1;
1361 ++gather_offsets[global_dof_elem1 + 1];
1362 }
1363}
1364
1366 const Mesh::FaceInformation &face,
1367 const int face_index)
1368{
1369 MFEM_ASSERT(face.IsLocal(),
1370 "This method should only be used on local faces.");
1371 const Table& e2dTable = fes.GetElementToDofTable();
1372 const int* elem_map = e2dTable.GetJ();
1373 const int elem_index = face.element[1].index;
1374 const int face_id1 = face.element[0].local_face_id;
1375 const int face_id2 = face.element[1].local_face_id;
1376 const int orientation = face.element[1].orientation;
1377 const int dim = fes.GetMesh()->Dimension();
1378 const int dof1d = fes.GetTypicalFE()->GetOrder()+1;
1379 fes.GetTypicalFE()->GetFaceMap(face_id2, face_map);
1380
1381 for (int face_dof_elem1 = 0; face_dof_elem1 < face_dofs; ++face_dof_elem1)
1382 {
1383 const int face_dof_elem2 = PermuteFaceL2(dim, face_id1, face_id2,
1384 orientation, dof1d,
1385 face_dof_elem1);
1386 const int volume_dof_elem2 = face_map[face_dof_elem2];
1387 const int global_dof_elem2 = elem_map[elem_index*elem_dofs + volume_dof_elem2];
1388 const int restriction_dof_elem2 = face_dofs*face_index + face_dof_elem1;
1389 scatter_indices2[restriction_dof_elem2] = global_dof_elem2;
1390 ++gather_offsets[global_dof_elem2 + 1];
1391 }
1392}
1393
1395 const Mesh::FaceInformation &face,
1396 const int face_index)
1397{
1398#ifdef MFEM_USE_MPI
1399 MFEM_ASSERT(face.IsShared(),
1400 "This method should only be used on shared faces.");
1401 const int elem_index = face.element[1].index;
1402 const int face_id1 = face.element[0].local_face_id;
1403 const int face_id2 = face.element[1].local_face_id;
1404 const int orientation = face.element[1].orientation;
1405 const int dim = fes.GetMesh()->Dimension();
1406 const int dof1d = fes.GetTypicalFE()->GetOrder()+1;
1407 fes.GetTypicalFE()->GetFaceMap(face_id2, face_map);
1408
1409 for (int face_dof_elem1 = 0; face_dof_elem1 < face_dofs; ++face_dof_elem1)
1410 {
1411 const int face_dof_elem2 = PermuteFaceL2(dim, face_id1, face_id2,
1412 orientation, dof1d, face_dof_elem1);
1413 const int volume_dof_elem2 = face_map[face_dof_elem2];
1414 // Encode the volume DOF index and element index
1415 const int global_dof_elem2 = elem_index*elem_dofs + volume_dof_elem2;
1416 const int restriction_dof_elem2 = face_dofs*face_index + face_dof_elem1;
1417 // Trick to differentiate dof location inter/shared
1418 scatter_indices2[restriction_dof_elem2] = ndofs + global_dof_elem2;
1419 }
1420#endif
1421}
1422
1424 const Mesh::FaceInformation &face,
1425 const int face_index)
1426{
1427 MFEM_ASSERT(face.IsBoundary(),
1428 "This method should only be used on boundary faces.");
1429
1430 for (int d = 0; d < face_dofs; ++d)
1431 {
1432 const int restriction_dof_elem2 = face_dofs*face_index + d;
1433 scatter_indices2[restriction_dof_elem2] = -1;
1434 }
1435}
1436
1438 const Mesh::FaceInformation &face,
1439 const int face_index)
1440{
1441 MFEM_ASSERT(!(face.IsNonconformingCoarse()),
1442 "This method should not be used on nonconforming coarse faces.");
1443 const Table& e2dTable = fes.GetElementToDofTable();
1444 const int* elem_map = e2dTable.GetJ();
1445 const int face_id1 = face.element[0].local_face_id;
1446 const int elem_index = face.element[0].index;
1447 fes.GetTypicalFE()->GetFaceMap(face_id1, face_map);
1448
1449 for (int face_dof_elem1 = 0; face_dof_elem1 < face_dofs; ++face_dof_elem1)
1450 {
1451 const int volume_dof_elem1 = face_map[face_dof_elem1];
1452 const int global_dof_elem1 = elem_map[elem_index*elem_dofs + volume_dof_elem1];
1453 const int restriction_dof_elem1 = face_dofs*face_index + face_dof_elem1;
1454 // We don't shift restriction_dof_elem1 to express that it's elem1 of the face
1455 gather_indices[gather_offsets[global_dof_elem1]++] = restriction_dof_elem1;
1456 }
1457}
1458
1460 const Mesh::FaceInformation &face,
1461 const int face_index)
1462{
1463 MFEM_ASSERT(face.IsLocal(),
1464 "This method should only be used on local faces.");
1465 const Table& e2dTable = fes.GetElementToDofTable();
1466 const int* elem_map = e2dTable.GetJ();
1467 const int elem_index = face.element[1].index;
1468 const int face_id1 = face.element[0].local_face_id;
1469 const int face_id2 = face.element[1].local_face_id;
1470 const int orientation = face.element[1].orientation;
1471 const int dim = fes.GetMesh()->Dimension();
1472 const int dof1d = fes.GetTypicalFE()->GetOrder()+1;
1473 fes.GetTypicalFE()->GetFaceMap(face_id2, face_map);
1474
1475 for (int face_dof_elem1 = 0; face_dof_elem1 < face_dofs; ++face_dof_elem1)
1476 {
1477 const int face_dof_elem2 = PermuteFaceL2(dim, face_id1, face_id2,
1478 orientation, dof1d,
1479 face_dof_elem1);
1480 const int volume_dof_elem2 = face_map[face_dof_elem2];
1481 const int global_dof_elem2 = elem_map[elem_index*elem_dofs + volume_dof_elem2];
1482 const int restriction_dof_elem2 = face_dofs*face_index + face_dof_elem1;
1483 // We shift restriction_dof_elem2 to express that it's elem2 of the face
1484 gather_indices[gather_offsets[global_dof_elem2]++] = nfdofs +
1485 restriction_dof_elem2;
1486 }
1487}
1488
1490{
1491 EnsureNormalDerivativeRestriction();
1492 normal_deriv_restr->Mult(x, y);
1493}
1494
1496 Vector &y) const
1497{
1498 EnsureNormalDerivativeRestriction();
1499 normal_deriv_restr->AddMultTranspose(x, y);
1500}
1501
1502void L2FaceRestriction::EnsureNormalDerivativeRestriction() const
1503{
1504 if (!normal_deriv_restr)
1505 {
1506 normal_deriv_restr.reset(
1508 }
1509}
1510
1512 ElementDofOrdering ordering_,
1513 FaceType type)
1514 : fes(fes_),
1515 ordering(ordering_),
1516 interp_config(fes.GetNFbyType(type)),
1517 nc_cpt(0)
1518{ }
1519
1521 const Mesh::FaceInformation &face,
1522 int face_index)
1523{
1524 interp_config[face_index] = InterpConfig();
1525}
1526
1528 const Mesh::FaceInformation &face,
1529 int face_index)
1530{
1531 MFEM_ASSERT(!face.IsConforming(),
1532 "Registering face as nonconforming even though it is not.");
1533 const DenseMatrix* ptMat = face.point_matrix;
1534 // In the case of nonconforming slave shared face the master face is elem1.
1535 const int master_side =
1537 const int face_key = (master_side == 0 ? 1000 : 0) +
1538 face.element[0].local_face_id +
1539 6*face.element[1].local_face_id +
1540 36*face.element[1].orientation ;
1541 // Unfortunately we can't trust uniqueness of the ptMat to identify the
1542 // transformation.
1543 Key key(ptMat, face_key);
1544 auto itr = interp_map.find(key);
1545 if ( itr == interp_map.end() )
1546 {
1547 const DenseMatrix* interpolator =
1548 GetCoarseToFineInterpolation(face,ptMat);
1549 interp_map[key] = {nc_cpt, interpolator};
1550 interp_config[face_index] = {master_side, nc_cpt};
1551 nc_cpt++;
1552 }
1553 else
1554 {
1555 interp_config[face_index] = {master_side, itr->second.first};
1556 }
1557}
1558
1559const DenseMatrix* InterpolationManager::GetCoarseToFineInterpolation(
1560 const Mesh::FaceInformation &face,
1561 const DenseMatrix* ptMat)
1562{
1564 "The following interpolation operator is only implemented for"
1565 "lexicographic ordering.");
1566 MFEM_VERIFY(!face.IsConforming(),
1567 "This method should not be called on conforming faces.")
1568 const int face_id1 = face.element[0].local_face_id;
1569 const int face_id2 = face.element[1].local_face_id;
1570
1571 const bool is_ghost_slave =
1572 face.element[0].conformity == Mesh::ElementConformity::Superset;
1573 const int master_face_id = is_ghost_slave ? face_id1 : face_id2;
1574
1575 // Computation of the interpolation matrix from master
1576 // (coarse) face to slave (fine) face.
1577 // Assumes all trace elements are the same.
1578 const FiniteElement *trace_fe = fes.GetTypicalTraceElement();
1579 const int face_dofs = trace_fe->GetDof();
1580 const TensorBasisElement* el =
1581 dynamic_cast<const TensorBasisElement*>(trace_fe);
1582 const auto dof_map = el->GetDofMap();
1583 DenseMatrix* interpolator = new DenseMatrix(face_dofs,face_dofs);
1584 Vector shape(face_dofs);
1585
1587 isotr.SetIdentityTransformation(trace_fe->GetGeomType());
1588 isotr.SetPointMat(*ptMat);
1589 DenseMatrix native_interpolator(face_dofs,face_dofs);
1590 trace_fe->GetLocalInterpolation(isotr, native_interpolator);
1591
1592 if (trace_fe->GetMapType() == FiniteElement::INTEGRAL)
1593 {
1594 // Handle potentially inverted Jacobian matrix
1595 isotr.SetIntPoint(&Geometries.GetCenter(trace_fe->GetGeomType()));
1596 native_interpolator *= (isotr.Weight() >= 0) ? 1.0 : -1.0;
1597 }
1598
1599 const int dim = trace_fe->GetDim()+1;
1600 const int dof1d = trace_fe->GetOrder()+1;
1601 int orientation_i = face.element[1].orientation;
1602 const int orientation_j = face.element[1].orientation;
1603
1604 // In 2D, need to flip orientation of the segments`
1605 if (trace_fe->GetGeomType() == Geometry::SEGMENT && !is_ghost_slave)
1606 {
1607 orientation_i = 1;
1608 }
1609
1610 for (int i = 0; i < face_dofs; i++)
1611 {
1612 const int ni = (dof_map.Size()==0) ? i : dof_map[i];
1613 int li = ToLexOrdering(dim, master_face_id, dof1d, i);
1614 if ( !is_ghost_slave )
1615 {
1616 // master side is elem 2, so we permute to order dofs as elem 1.
1617 li = PermuteFaceL2(dim, face_id2, face_id1,
1618 orientation_i, dof1d, li);
1619 }
1620 for (int j = 0; j < face_dofs; j++)
1621 {
1622 int lj = ToLexOrdering(dim, master_face_id, dof1d, j);
1623 if ( !is_ghost_slave )
1624 {
1625 // master side is elem 2, so we permute to order dofs as elem 1.
1626 lj = PermuteFaceL2(dim, face_id2, face_id1,
1627 orientation_j, dof1d, lj);
1628 }
1629 const int nj = (dof_map.Size()==0) ? j : dof_map[j];
1630 (*interpolator)(li,lj) = native_interpolator(ni,nj);
1631 }
1632 }
1633 return interpolator;
1634}
1635
1637{
1638 // Assumes all trace elements are the same.
1639 const FiniteElement *trace_fe = fes.GetTypicalTraceElement();
1640 const int face_dofs = trace_fe->GetDof();
1641 const int nc_size = static_cast<int>(interp_map.size());
1642 MFEM_VERIFY(nc_cpt==nc_size, "Unexpected number of interpolators.");
1643 interpolators.SetSize(face_dofs*face_dofs*nc_size);
1644 auto d_interp = Reshape(interpolators.HostWrite(),face_dofs,face_dofs,nc_size);
1645 for (auto val : interp_map)
1646 {
1647 const int idx = val.second.first;
1648 const DenseMatrix &interpolator = *val.second.second;
1649 for (int i = 0; i < face_dofs; i++)
1650 {
1651 for (int j = 0; j < face_dofs; j++)
1652 {
1653 d_interp(i,j,idx) = interpolator(i,j);
1654 }
1655 }
1656 delete val.second.second;
1657 }
1658 interp_map.clear();
1659}
1660
1662{
1663 // Count nonconforming faces
1664 int num_nc_faces = 0;
1665 for (int i = 0; i < interp_config.Size(); i++)
1666 {
1667 if ( interp_config[i].is_non_conforming )
1668 {
1669 num_nc_faces++;
1670 }
1671 }
1672 // Set nc_interp_config
1673 nc_interp_config.SetSize(num_nc_faces);
1674 int nc_index = 0;
1675 for (int i = 0; i < interp_config.Size(); i++)
1676 {
1677 auto & config = interp_config[i];
1678 if ( config.is_non_conforming )
1679 {
1680 nc_interp_config[nc_index] = NCInterpConfig(i, config);
1681 nc_index++;
1682 }
1683 }
1684}
1685
1687 const ElementDofOrdering f_ordering,
1688 const FaceType type,
1689 const L2FaceValues m,
1690 bool build)
1691 : L2FaceRestriction(fes, f_ordering, type, m, false),
1692 interpolations(fes.GetInterpolationManager(ordering, type))
1693{
1694 if (!build) { return; }
1695 x_interp.UseDevice(true);
1696
1697 CheckFESpace();
1698
1699 ComputeScatterIndicesAndOffsets();
1700
1701 ComputeGatherIndices();
1702}
1703
1705 const ElementDofOrdering f_ordering,
1706 const FaceType type,
1707 const L2FaceValues m)
1708 : NCL2FaceRestriction(fes, f_ordering, type, m, true)
1709{ }
1710
1717
1719 Vector& y) const
1720{
1721 if (nf == 0) { return; }
1722 // Assumes all elements have the same number of dofs
1723 const int nface_dofs = face_dofs;
1724 const int vd = vdim;
1725 auto d_y = Reshape(y.ReadWrite(), nface_dofs, vd, 2, nf);
1726 auto &nc_interp_config = interpolations.GetNCFaceInterpConfig();
1727 const int num_nc_faces = nc_interp_config.Size();
1728 if ( num_nc_faces == 0 ) { return; }
1729 auto interp_config_ptr = nc_interp_config.Read();
1730 const int nc_size = interpolations.GetNumInterpolators();
1731 auto d_interp = Reshape(interpolations.GetInterpolators().Read(),
1732 nface_dofs, nface_dofs, nc_size);
1733 static constexpr int max_nd = 16*16;
1734 MFEM_VERIFY(nface_dofs<=max_nd, "Too many degrees of freedom.");
1735 mfem::forall_2D(num_nc_faces, nface_dofs, 1, [=] MFEM_HOST_DEVICE (int nc_face)
1736 {
1737 MFEM_SHARED real_t dof_values[max_nd];
1738 const NCInterpConfig conf = interp_config_ptr[nc_face];
1739 if ( conf.is_non_conforming )
1740 {
1741 const int master_side = conf.master_side;
1742 const int interp_index = conf.index;
1743 const int face = conf.face_index;
1744 for (int c = 0; c < vd; ++c)
1745 {
1746 MFEM_FOREACH_THREAD(dof,x,nface_dofs)
1747 {
1748 dof_values[dof] = d_y(dof, c, master_side, face);
1749 }
1750 MFEM_SYNC_THREAD;
1751 MFEM_FOREACH_THREAD(dof_out,x,nface_dofs)
1752 {
1753 real_t res = 0.0;
1754 for (int dof_in = 0; dof_in<nface_dofs; dof_in++)
1755 {
1756 res += d_interp(dof_out, dof_in, interp_index)*dof_values[dof_in];
1757 }
1758 d_y(dof_out, c, master_side, face) = res;
1759 }
1760 MFEM_SYNC_THREAD;
1761 }
1762 }
1763 });
1764}
1765
1766void NCL2FaceRestriction::Mult(const Vector& x, Vector& y) const
1767{
1768 if ( type==FaceType::Interior && m==L2FaceValues::DoubleValued )
1769 {
1770 DoubleValuedNonconformingMult(x, y);
1771 }
1772 else if ( type==FaceType::Boundary && m==L2FaceValues::DoubleValued )
1773 {
1774 DoubleValuedConformingMult(x, y);
1775 }
1776 else // Single valued (assumes no nonconforming master on elem1)
1777 {
1778 SingleValuedConformingMult(x, y);
1779 }
1780}
1781
1782void NCL2FaceRestriction::SingleValuedNonconformingTransposeInterpolation(
1783 const Vector& x) const
1784{
1785 MFEM_ASSERT(
1786 m == L2FaceValues::SingleValued,
1787 "This method should be called when m == L2FaceValues::SingleValued.");
1788 if (x_interp.Size()==0)
1789 {
1790 x_interp.SetSize(x.Size());
1791 }
1792 x_interp = x;
1793 SingleValuedNonconformingTransposeInterpolationInPlace(x_interp);
1794}
1795
1796
1797void NCL2FaceRestriction::SingleValuedNonconformingTransposeInterpolationInPlace(
1798 Vector& x) const
1799{
1800 // Assumes all elements have the same number of dofs
1801 const int nface_dofs = face_dofs;
1802 const int vd = vdim;
1803 // Interpolation
1804 auto d_x = Reshape(x_interp.ReadWrite(), nface_dofs, vd, nf);
1805 auto &nc_interp_config = interpolations.GetNCFaceInterpConfig();
1806 const int num_nc_faces = nc_interp_config.Size();
1807 if ( num_nc_faces == 0 ) { return; }
1808 auto interp_config_ptr = nc_interp_config.Read();
1809 auto interpolators = interpolations.GetInterpolators().Read();
1810 const int nc_size = interpolations.GetNumInterpolators();
1811 auto d_interp = Reshape(interpolators, nface_dofs, nface_dofs, nc_size);
1812 static constexpr int max_nd = 16*16;
1813 MFEM_VERIFY(nface_dofs<=max_nd, "Too many degrees of freedom.");
1814 mfem::forall_2D(num_nc_faces, nface_dofs, 1, [=] MFEM_HOST_DEVICE (int nc_face)
1815 {
1816 MFEM_SHARED real_t dof_values[max_nd];
1817 const NCInterpConfig conf = interp_config_ptr[nc_face];
1818 const int master_side = conf.master_side;
1819 const int interp_index = conf.index;
1820 const int face = conf.face_index;
1821 if ( conf.is_non_conforming && master_side==0 )
1822 {
1823 // Interpolation from fine to coarse
1824 for (int c = 0; c < vd; ++c)
1825 {
1826 MFEM_FOREACH_THREAD(dof,x,nface_dofs)
1827 {
1828 dof_values[dof] = d_x(dof, c, face);
1829 }
1830 MFEM_SYNC_THREAD;
1831 MFEM_FOREACH_THREAD(dof_out,x,nface_dofs)
1832 {
1833 real_t res = 0.0;
1834 for (int dof_in = 0; dof_in<nface_dofs; dof_in++)
1835 {
1836 res += d_interp(dof_in, dof_out, interp_index)*dof_values[dof_in];
1837 }
1838 d_x(dof_out, c, face) = res;
1839 }
1840 MFEM_SYNC_THREAD;
1841 }
1842 }
1843 });
1844}
1845
1846void NCL2FaceRestriction::DoubleValuedNonconformingTransposeInterpolation(
1847 const Vector& x) const
1848{
1849 MFEM_ASSERT(
1850 m == L2FaceValues::DoubleValued,
1851 "This method should be called when m == L2FaceValues::DoubleValued.");
1852 if (x_interp.Size()==0)
1853 {
1854 x_interp.SetSize(x.Size());
1855 }
1856 x_interp = x;
1857 DoubleValuedNonconformingTransposeInterpolationInPlace(x_interp);
1858}
1859
1860void NCL2FaceRestriction::DoubleValuedNonconformingTransposeInterpolationInPlace(
1861 Vector& x) const
1862{
1863 // Assumes all elements have the same number of dofs
1864 const int nface_dofs = face_dofs;
1865 const int vd = vdim;
1866 // Interpolation
1867 auto d_x = Reshape(x.ReadWrite(), nface_dofs, vd, 2, nf);
1868 auto &nc_interp_config = interpolations.GetNCFaceInterpConfig();
1869 const int num_nc_faces = nc_interp_config.Size();
1870 if ( num_nc_faces == 0 ) { return; }
1871 auto interp_config_ptr = nc_interp_config.Read();
1872 auto interpolators = interpolations.GetInterpolators().Read();
1873 const int nc_size = interpolations.GetNumInterpolators();
1874 auto d_interp = Reshape(interpolators, nface_dofs, nface_dofs, nc_size);
1875 static constexpr int max_nd = 16*16;
1876 MFEM_VERIFY(nface_dofs<=max_nd, "Too many degrees of freedom.");
1877 mfem::forall_2D(num_nc_faces, nface_dofs, 1, [=] MFEM_HOST_DEVICE (int nc_face)
1878 {
1879 MFEM_SHARED real_t dof_values[max_nd];
1880 const NCInterpConfig conf = interp_config_ptr[nc_face];
1881 const int master_side = conf.master_side;
1882 const int interp_index = conf.index;
1883 const int face = conf.face_index;
1884 if ( conf.is_non_conforming )
1885 {
1886 // Interpolation from fine to coarse
1887 for (int c = 0; c < vd; ++c)
1888 {
1889 MFEM_FOREACH_THREAD(dof,x,nface_dofs)
1890 {
1891 dof_values[dof] = d_x(dof, c, master_side, face);
1892 }
1893 MFEM_SYNC_THREAD;
1894 MFEM_FOREACH_THREAD(dof_out,x,nface_dofs)
1895 {
1896 real_t res = 0.0;
1897 for (int dof_in = 0; dof_in<nface_dofs; dof_in++)
1898 {
1899 res += d_interp(dof_in, dof_out, interp_index)*dof_values[dof_in];
1900 }
1901 d_x(dof_out, c, master_side, face) = res;
1902 }
1903 MFEM_SYNC_THREAD;
1904 }
1905 }
1906 });
1907}
1908
1909void NCL2FaceRestriction::AddMultTranspose(const Vector& x, Vector& y,
1910 const real_t a) const
1911{
1912 MFEM_VERIFY(a == 1.0, "General coefficient case is not yet supported!");
1913 if (nf==0) { return; }
1914 if (type==FaceType::Interior)
1915 {
1916 if ( m==L2FaceValues::DoubleValued )
1917 {
1918 DoubleValuedNonconformingTransposeInterpolation(x);
1919 DoubleValuedConformingAddMultTranspose(x_interp, y);
1920 }
1921 else if ( m==L2FaceValues::SingleValued )
1922 {
1923 SingleValuedNonconformingTransposeInterpolation(x);
1924 SingleValuedConformingAddMultTranspose(x_interp, y);
1925 }
1926 }
1927 else
1928 {
1929 if ( m==L2FaceValues::DoubleValued )
1930 {
1931 DoubleValuedConformingAddMultTranspose(x, y);
1932 }
1933 else if ( m==L2FaceValues::SingleValued )
1934 {
1935 SingleValuedConformingAddMultTranspose(x, y);
1936 }
1937 }
1938}
1939
1940void NCL2FaceRestriction::AddMultTransposeInPlace(Vector& x, Vector& y) const
1941{
1942 if (nf==0) { return; }
1943 if (type==FaceType::Interior)
1944 {
1945 if ( m==L2FaceValues::DoubleValued )
1946 {
1947 DoubleValuedNonconformingTransposeInterpolationInPlace(x);
1948 DoubleValuedConformingAddMultTranspose(x, y);
1949 }
1950 else if ( m==L2FaceValues::SingleValued )
1951 {
1952 SingleValuedNonconformingTransposeInterpolationInPlace(x);
1953 SingleValuedConformingAddMultTranspose(x, y);
1954 }
1955 }
1956 else
1957 {
1958 if ( m==L2FaceValues::DoubleValued )
1959 {
1960 DoubleValuedConformingAddMultTranspose(x, y);
1961 }
1962 else if ( m==L2FaceValues::SingleValued )
1963 {
1964 SingleValuedConformingAddMultTranspose(x, y);
1965 }
1966 }
1967}
1968
1969void NCL2FaceRestriction::FillI(SparseMatrix &mat,
1970 const bool keep_nbr_block) const
1971{
1972 const int nface_dofs = face_dofs;
1973 auto d_indices1 = scatter_indices1.Read();
1974 auto d_indices2 = scatter_indices2.Read();
1975 auto I = mat.ReadWriteI();
1976 mfem::forall(nf*nface_dofs, [=] MFEM_HOST_DEVICE (int fdof)
1977 {
1978 const int iE1 = d_indices1[fdof];
1979 const int iE2 = d_indices2[fdof];
1980 AddNnz(iE1,I,nface_dofs);
1981 AddNnz(iE2,I,nface_dofs);
1982 });
1983}
1984
1985void NCL2FaceRestriction::FillJAndData(const Vector &fea_data,
1986 SparseMatrix &mat,
1987 const bool keep_nbr_block) const
1988{
1989 const int nface_dofs = face_dofs;
1990 auto d_indices1 = scatter_indices1.Read();
1991 auto d_indices2 = scatter_indices2.Read();
1992 auto I = mat.ReadWriteI();
1993 auto mat_fea = Reshape(fea_data.Read(), nface_dofs, nface_dofs, 2, nf);
1994 auto J = mat.WriteJ();
1995 auto Data = mat.WriteData();
1996 auto interp_config_ptr = interpolations.GetFaceInterpConfig().Read();
1997 auto interpolators = interpolations.GetInterpolators().Read();
1998 const int nc_size = interpolations.GetNumInterpolators();
1999 auto d_interp = Reshape(interpolators, nface_dofs, nface_dofs, nc_size);
2000 mfem::forall(nf*nface_dofs, [=] MFEM_HOST_DEVICE (int fdof)
2001 {
2002 const int f = fdof/nface_dofs;
2003 const InterpConfig conf = interp_config_ptr[f];
2004 const int master_side = conf.master_side;
2005 const int interp_index = conf.index;
2006 const int iF = fdof%nface_dofs;
2007 const int iE1 = d_indices1[f*nface_dofs+iF];
2008 const int iE2 = d_indices2[f*nface_dofs+iF];
2009 const int offset1 = AddNnz(iE1,I,nface_dofs);
2010 const int offset2 = AddNnz(iE2,I,nface_dofs);
2011 for (int jF = 0; jF < nface_dofs; jF++)
2012 {
2013 const int jE1 = d_indices1[f*nface_dofs+jF];
2014 const int jE2 = d_indices2[f*nface_dofs+jF];
2015 J[offset2+jF] = jE1;
2016 J[offset1+jF] = jE2;
2017 real_t val1 = 0.0;
2018 real_t val2 = 0.0;
2019 if ( conf.is_non_conforming && master_side==0 )
2020 {
2021 for (int kF = 0; kF < nface_dofs; kF++)
2022 {
2023 val1 += mat_fea(kF,iF,0,f) * d_interp(kF, jF, interp_index);
2024 val2 += d_interp(kF, iF, interp_index) * mat_fea(jF,kF,1,f);
2025 }
2026 }
2027 else if ( conf.is_non_conforming && master_side==1 )
2028 {
2029 for (int kF = 0; kF < nface_dofs; kF++)
2030 {
2031 val1 += d_interp(kF, iF, interp_index) * mat_fea(jF,kF,0,f);
2032 val2 += mat_fea(kF,iF,1,f) * d_interp(kF, jF, interp_index);
2033 }
2034 }
2035 else
2036 {
2037 val1 = mat_fea(jF,iF,0,f);
2038 val2 = mat_fea(jF,iF,1,f);
2039 }
2040 Data[offset2+jF] = val1;
2041 Data[offset1+jF] = val2;
2042 }
2043 });
2044}
2045
2046void NCL2FaceRestriction::AddFaceMatricesToElementMatrices(
2047 const Vector &fea_data,
2048 Vector &ea_data)
2049const
2050{
2051 const int nface_dofs = face_dofs;
2052 const int nelem_dofs = elem_dofs;
2053 const int NE = ne;
2054 if (m==L2FaceValues::DoubleValued)
2055 {
2056 auto d_indices1 = scatter_indices1.Read();
2057 auto d_indices2 = scatter_indices2.Read();
2058 auto mat_fea = Reshape(fea_data.Read(), nface_dofs, nface_dofs, 2, nf);
2059 auto mat_ea = Reshape(ea_data.ReadWrite(), nelem_dofs, nelem_dofs, ne);
2060 auto interp_config_ptr = interpolations.GetFaceInterpConfig().Read();
2061 auto interpolators = interpolations.GetInterpolators().Read();
2062 const int nc_size = interpolations.GetNumInterpolators();
2063 auto d_interp = Reshape(interpolators, nface_dofs, nface_dofs, nc_size);
2064 mfem::forall(nf, [=] MFEM_HOST_DEVICE (int f)
2065 {
2066 const InterpConfig conf = interp_config_ptr[f];
2067 const int master_side = conf.master_side;
2068 const int interp_index = conf.index;
2069 const int e1 = d_indices1[f*nface_dofs]/nelem_dofs;
2070 const int e2 = d_indices2[f*nface_dofs]/nelem_dofs;
2071 for (int j = 0; j < nface_dofs; j++)
2072 {
2073 const int jB1 = d_indices1[f*nface_dofs+j]%nelem_dofs;
2074 for (int i = 0; i < nface_dofs; i++)
2075 {
2076 const int iB1 = d_indices1[f*nface_dofs+i]%nelem_dofs;
2077 real_t val = 0.0;
2078 if ( conf.is_non_conforming && master_side==0 )
2079 {
2080 for (int k = 0; k < nface_dofs; k++)
2081 {
2082 for (int l = 0; l < nface_dofs; l++)
2083 {
2084 val += d_interp(l, j, interp_index)
2085 * mat_fea(k,l,0,f)
2086 * d_interp(k, i, interp_index);
2087 }
2088 }
2089 }
2090 else
2091 {
2092 val = mat_fea(i,j,0,f);
2093 }
2094 AtomicAdd(mat_ea(iB1,jB1,e1), val);
2095 }
2096 }
2097 if (e2 < NE)
2098 {
2099 for (int j = 0; j < nface_dofs; j++)
2100 {
2101 const int jB2 = d_indices2[f*nface_dofs+j]%nelem_dofs;
2102 for (int i = 0; i < nface_dofs; i++)
2103 {
2104 const int iB2 = d_indices2[f*nface_dofs+i]%nelem_dofs;
2105 real_t val = 0.0;
2106 if ( conf.is_non_conforming && master_side==1 )
2107 {
2108 for (int k = 0; k < nface_dofs; k++)
2109 {
2110 for (int l = 0; l < nface_dofs; l++)
2111 {
2112 val += d_interp(l, j, interp_index)
2113 * mat_fea(k,l,1,f)
2114 * d_interp(k, i, interp_index);
2115 }
2116 }
2117 }
2118 else
2119 {
2120 val = mat_fea(i,j,1,f);
2121 }
2122 AtomicAdd(mat_ea(iB2,jB2,e2), val);
2123 }
2124 }
2125 }
2126 });
2127 }
2128 else
2129 {
2130 auto d_indices = scatter_indices1.Read();
2131 auto mat_fea = Reshape(fea_data.Read(), nface_dofs, nface_dofs, nf);
2132 auto mat_ea = Reshape(ea_data.ReadWrite(), nelem_dofs, nelem_dofs, ne);
2133 auto interp_config_ptr = interpolations.GetFaceInterpConfig().Read();
2134 auto interpolators = interpolations.GetInterpolators().Read();
2135 const int nc_size = interpolations.GetNumInterpolators();
2136 auto d_interp = Reshape(interpolators, nface_dofs, nface_dofs, nc_size);
2137 mfem::forall(nf, [=] MFEM_HOST_DEVICE (int f)
2138 {
2139 const InterpConfig conf = interp_config_ptr[f];
2140 const int master_side = conf.master_side;
2141 const int interp_index = conf.index;
2142 const int e = d_indices[f*nface_dofs]/nelem_dofs;
2143 for (int j = 0; j < nface_dofs; j++)
2144 {
2145 const int jE = d_indices[f*nface_dofs+j]%nelem_dofs;
2146 for (int i = 0; i < nface_dofs; i++)
2147 {
2148 const int iE = d_indices[f*nface_dofs+i]%nelem_dofs;
2149 real_t val = 0.0;
2150 if ( conf.is_non_conforming && master_side==0 )
2151 {
2152 for (int k = 0; k < nface_dofs; k++)
2153 {
2154 for (int l = 0; l < nface_dofs; l++)
2155 {
2156 val += d_interp(l, j, interp_index)
2157 * mat_fea(k,l,f)
2158 * d_interp(k, i, interp_index);
2159 }
2160 }
2161 }
2162 else
2163 {
2164 val = mat_fea(i,j,f);
2165 }
2166 AtomicAdd(mat_ea(iE,jE,e), val);
2167 }
2168 }
2169 });
2170 }
2171}
2172
2173int ToLexOrdering(const int dim, const int face_id, const int size1d,
2174 const int index)
2175{
2176 switch (dim)
2177 {
2178 case 1:
2179 return 0;
2180 case 2:
2181 return internal::ToLexOrdering2D(face_id, size1d, index);
2182 case 3:
2183 return internal::ToLexOrdering3D(face_id, size1d, index%size1d, index/size1d);
2184 default:
2185 MFEM_ABORT("Unsupported dimension.");
2186 return 0;
2187 }
2188}
2189
2190void NCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
2191{
2192 Mesh &mesh = *fes.GetMesh();
2193
2194 // Initialization of the offsets
2195 for (int i = 0; i <= ndofs; ++i)
2196 {
2197 gather_offsets[i] = 0;
2198 }
2199
2200 // Computation of scatter and offsets indices
2201 int f_ind=0;
2202 for (int f = 0; f < fes.GetNF(); ++f)
2203 {
2204 Mesh::FaceInformation face = mesh.GetFaceInformation(f);
2205 if ( face.IsNonconformingCoarse() )
2206 {
2207 // We skip nonconforming coarse faces as they are treated
2208 // by the corresponding nonconforming fine faces.
2209 continue;
2210 }
2211 else if ( type==FaceType::Interior && face.IsInterior() )
2212 {
2213 SetFaceDofsScatterIndices1(face,f_ind);
2214 if ( m==L2FaceValues::DoubleValued )
2215 {
2216 PermuteAndSetFaceDofsScatterIndices2(face,f_ind);
2217 }
2218 f_ind++;
2219 }
2220 else if ( type==FaceType::Boundary && face.IsBoundary() )
2221 {
2222 SetFaceDofsScatterIndices1(face,f_ind);
2223 if ( m==L2FaceValues::DoubleValued )
2224 {
2225 SetBoundaryDofsScatterIndices2(face,f_ind);
2226 }
2227 f_ind++;
2228 }
2229 }
2230 MFEM_VERIFY(f_ind==nf, "Unexpected number of " <<
2231 (type==FaceType::Interior? "interior" : "boundary") <<
2232 " faces: " << f_ind << " vs " << nf );
2233
2234 // Summation of the offsets
2235 for (int i = 1; i <= ndofs; ++i)
2236 {
2237 gather_offsets[i] += gather_offsets[i - 1];
2238 }
2239}
2240
2241void NCL2FaceRestriction::ComputeGatherIndices()
2242{
2243 Mesh &mesh = *fes.GetMesh();
2244 // Computation of gather_indices
2245 int f_ind = 0;
2246 for (int f = 0; f < fes.GetNF(); ++f)
2247 {
2248 Mesh::FaceInformation face = mesh.GetFaceInformation(f);
2249 MFEM_ASSERT(!face.IsShared(),
2250 "Unexpected shared face in NCL2FaceRestriction.");
2251 if ( face.IsNonconformingCoarse() )
2252 {
2253 // We skip nonconforming coarse faces as they are treated
2254 // by the corresponding nonconforming fine faces.
2255 continue;
2256 }
2257 else if ( face.IsOfFaceType(type) )
2258 {
2259 SetFaceDofsGatherIndices1(face,f_ind);
2260 if ( m==L2FaceValues::DoubleValued &&
2261 type==FaceType::Interior &&
2262 face.IsInterior() )
2263 {
2264 PermuteAndSetFaceDofsGatherIndices2(face,f_ind);
2265 }
2266 f_ind++;
2267 }
2268 }
2269 MFEM_VERIFY(f_ind==nf, "Unexpected number of " <<
2270 (type==FaceType::Interior? "interior" : "boundary") <<
2271 " faces: " << f_ind << " vs " << nf );
2272
2273 // Switch back offsets to their correct value
2274 for (int i = ndofs; i > 0; --i)
2275 {
2276 gather_offsets[i] = gather_offsets[i - 1];
2277 }
2278 gather_offsets[0] = 0;
2279}
2280
2281static int GetSharedVSize(const FiniteElementSpace &fes)
2282{
2283#ifdef MFEM_USE_MPI
2284 if (auto pfes = dynamic_cast<const ParFiniteElementSpace*>(&fes))
2285 {
2286 const_cast<ParFiniteElementSpace*>(pfes)->ExchangeFaceNbrData();
2287 return pfes->GetFaceNbrVSize();
2288 }
2289#endif
2290 return 0;
2291}
2292
2293L2InterfaceFaceRestriction::L2InterfaceFaceRestriction(
2294 const FiniteElementSpace& fes_,
2295 const ElementDofOrdering ordering_,
2296 const FaceType type_)
2297 : fes(fes_),
2298 ordering(ordering_),
2299 type(type_),
2300 nfaces(fes.GetNFbyType(type)),
2301 vdim(fes.GetVDim()),
2302 byvdim(fes.GetOrdering() == Ordering::byVDIM),
2303 face_dofs(fes.GetTypicalTraceElement()->GetDof()),
2304 nfdofs(face_dofs*nfaces),
2305 ndofs(fes.GetNDofs()),
2306 nsdofs(GetSharedVSize(fes))
2307{
2308 height = nfdofs;
2309 width = ndofs;
2310
2311#ifdef MFEM_USE_MPI
2312 auto pfes = dynamic_cast<const ParFiniteElementSpace*>(&fes);
2313#endif
2314
2315 const Table &face2dof = fes.GetFaceToDofTable();
2316
2317 const Mesh &mesh = *fes.GetMesh();
2318 int face_idx = 0;
2321 gather_map = -1;
2322
2323 Array<int> dofs;
2324 for (int f = 0; f < mesh.GetNumFacesWithGhost(); ++f)
2325 {
2327 if (!face.IsOfFaceType(type) || face.IsNonconformingCoarse()) { continue; }
2328
2329 if (f < mesh.GetNumFaces())
2330 {
2331 // Local face
2332 face2dof.GetRow(f, dofs);
2333 for (int i = 0; i < face_dofs; ++i)
2334 {
2335 scatter_map[i + face_idx*face_dofs] = dofs[i];
2336 gather_map[dofs[i]] = i + face_idx*face_dofs;
2337 }
2338 }
2339 else
2340 {
2341 // Shared (non-conforming) ghost face
2342#ifdef MFEM_USE_MPI
2343 MFEM_ASSERT(pfes != nullptr, "");
2344 pfes->GetFaceNbrFaceVDofs(f, dofs);
2345 for (int i = 0; i < face_dofs; ++i)
2346 {
2347 scatter_map[i + face_idx*face_dofs] = ndofs + dofs[i];
2348 gather_map[ndofs + dofs[i]] = i + face_idx*face_dofs;
2349 }
2350#endif
2351 }
2352 ++face_idx;
2353 }
2354}
2355
2357{
2358 const int NDOFS = ndofs;
2359 const int nd = face_dofs;
2360 const int nf = nfaces;
2361 const int vd = vdim;
2362 const bool t = byvdim;
2363 const int *map = scatter_map.Read();
2364
2365 Vector face_nbr_data = GetLVectorFaceNbrData(fes, x, type);
2366 MFEM_ASSERT(face_nbr_data.Size() / vd == nsdofs, "");
2367
2368 const auto d_x = Reshape(x.Read(), t?vd:ndofs, t?ndofs:vd);
2369 const auto d_x_shared = Reshape(face_nbr_data.Read(),
2370 t?vd:nsdofs, t?nsdofs:vd);
2371 auto d_y = Reshape(y.Write(), nd, vd, nf);
2372
2373 mfem::forall(nd*nf, [=] MFEM_HOST_DEVICE (int i)
2374 {
2375 const int j = map[i];
2376 for (int c = 0; c < vd; ++c)
2377 {
2378 if (j < NDOFS) { d_y(i % nd, c, i / nd) = d_x(t?c:j, t?j:c); }
2379 else { d_y(i % nd, c, i / nd) = d_x_shared(t?c:(j-NDOFS), t?(j-NDOFS):c); }
2380 }
2381 });
2382}
2383
2385 const Vector &x, Vector &y, const real_t a) const
2386{
2387 const int nd = face_dofs;
2388 const int nf = nfaces;
2389 const int vd = vdim;
2390 const bool t = byvdim;
2391 const int *map = gather_map.Read();
2392
2393 const auto d_x = Reshape(x.Read(), nd, vd, nf);
2394 auto d_y = Reshape(y.ReadWrite(), t?vd:ndofs, t?ndofs:vd);
2395
2396 mfem::forall(ndofs, [=] MFEM_HOST_DEVICE (int i)
2397 {
2398 const int j = map[i];
2399 if (j < 0) { return; }
2400 for (int c = 0; c < vd; ++c)
2401 {
2402 d_y(t?c:i, t?i:c) += a*d_x(j % nd, c, j / nd);
2403 }
2404 });
2405}
2406
2408 const Vector &x, Vector &y) const
2409{
2410 const int nd = face_dofs;
2411 const int nf = nfaces;
2412 const int vd = vdim;
2413 const bool t = byvdim;
2414 const int *map = gather_map.Read();
2415
2416 const auto d_x = Reshape(x.Read(), nd, vd, nf);
2417 auto d_y = Reshape(y.Write(), t?vd:(ndofs+nsdofs), t?(ndofs+nsdofs):vd);
2418 y = 0.0;
2419
2420 mfem::forall(ndofs + nsdofs, [=] MFEM_HOST_DEVICE (int i)
2421 {
2422 const int j = map[i];
2423 if (j < 0) { return; }
2424 for (int c = 0; c < vd; ++c)
2425 {
2426 d_y(t?c:i, t?i:c) = d_x(j % nd, c, j / nd);
2427 }
2428 });
2429}
2430
2432{
2433 return gather_map;
2434}
2435
2437{
2438 return scatter_map;
2439}
2440
2442 const FiniteElementSpace &fes, const Vector &x, FaceType ftype)
2443{
2444#ifdef MFEM_USE_MPI
2445 if (ftype == FaceType::Interior)
2446 {
2447 if (auto *pfes = const_cast<ParFiniteElementSpace*>
2448 (dynamic_cast<const ParFiniteElementSpace*>(&fes)))
2449 {
2450 if (auto *x_gf = const_cast<ParGridFunction*>
2451 (dynamic_cast<const ParGridFunction*>(&x)))
2452 {
2453 Vector &gf_face_nbr = x_gf->FaceNbrData();
2454 if (gf_face_nbr.Size() == 0) { x_gf->ExchangeFaceNbrData(); }
2455 gf_face_nbr.Read();
2456 return Vector(gf_face_nbr, 0, gf_face_nbr.Size());
2457 }
2458 else
2459 {
2460 ParGridFunction gf(pfes, const_cast<Vector&>(x));
2462 x.SyncMemory(gf);
2463 return std::move(gf.FaceNbrData());
2464 }
2465 }
2466 }
2467#endif
2468 return Vector();
2469}
2470
2471} // namespace mfem
MFEM_HOST_DEVICE T AtomicAdd(T &add, const T val)
Definition backends.hpp:116
const T * HostRead() const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), false).
Definition array.hpp:414
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition array.hpp:869
int Size() const
Return the logical size of the array.
Definition array.hpp:192
void MakeRef(T *data_, int size_, bool own_data=false)
Make this Array a reference to a pointer.
Definition array.hpp:1082
T * Write(bool on_dev=true)
Shortcut for mfem::Write(a.GetMemory(), a.Size(), on_dev).
Definition array.hpp:418
const T * Read(bool on_dev=true) const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), on_dev).
Definition array.hpp:410
T * HostReadWrite()
Shortcut for mfem::ReadWrite(a.GetMemory(), a.Size(), false).
Definition array.hpp:430
@ GaussLobatto
Closed type.
Definition fe_base.hpp:36
@ Positive
Bernstein polynomials.
Definition fe_base.hpp:37
Operator that extracts face degrees of freedom for H1, ND, or RT FiniteElementSpaces.
ConformingFaceRestriction(const FiniteElementSpace &fes, const ElementDofOrdering f_ordering, const FaceType type, bool build)
Construct a ConformingFaceRestriction.
void CheckFESpace(const ElementDofOrdering f_ordering)
Verify that ConformingFaceRestriction is built from a supported finite element space.
void SetFaceDofsGatherIndices(const Mesh::FaceInformation &face, const int face_index, const ElementDofOrdering f_ordering)
Set the gathering indices of elem1 for the interior face described by the face.
void MultInternal(const Vector &x, Vector &y, const bool useAbs=false) const
const FiniteElementSpace & fes
void AddMultTranspose(const Vector &x, Vector &y, const real_t a=1.0) const override
Gather the degrees of freedom, i.e. goes from face E-Vector to L-Vector.
void SetFaceDofsScatterIndices(const Mesh::FaceInformation &face, const int face_index, const ElementDofOrdering f_ordering)
Set the scattering indices of elem1, and increment the offsets for the face described by the face.
void AddAbsMultTranspose(const Vector &x, Vector &y, const real_t a=1.0) const override
Gather the degrees of freedom, i.e. goes from face E-Vector to L-Vector not taking into account signs...
Data type dense matrix using column-major storage.
Definition densemat.hpp:24
ElementRestriction(const FiniteElementSpace &, ElementDofOrdering)
const FiniteElementSpace & fes
void Mult(const Vector &x, Vector &y) const override
Operator application: y=A(x).
void AbsMult(const Vector &x, Vector &y) const override
Compute Mult without applying signs based on DOF orientations.
void FillSparseMatrix(const Vector &mat_ea, SparseMatrix &mat) const
Fill a Sparse Matrix with Element Matrices.
void AddMultTranspose(const Vector &x, Vector &y, const real_t a=1.0) const override
Add the E-vector degrees of freedom x to the L-vector degrees of freedom y.
void FillJAndData(const Vector &ea_data, SparseMatrix &mat) const
void MultTranspose(const Vector &x, Vector &y) const override
Action of the transpose operator: y=A^t(x). The default behavior in class Operator is to generate an ...
void MultLeftInverse(const Vector &x, Vector &y) const
void BooleanMask(Vector &y) const
Fills the E-vector y with boolean values 0.0 and 1.0 such that each each entry of the L-vector is uni...
void AbsMultTranspose(const Vector &x, Vector &y) const override
Compute MultTranspose without applying signs based on DOF orientations.
int FillI(SparseMatrix &mat) const
real_t Weight()
Return the weight of the Jacobian matrix of the transformation at the currently set IntegrationPoint....
Definition eltrans.hpp:144
void SetIntPoint(const IntegrationPoint *ip)
Set the integration point ip that weights and Jacobians will be evaluated at.
Definition eltrans.hpp:106
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
Definition fespace.hpp:210
const Table & GetElementToDofTable() const
Return a reference to the internal Table that stores the lists of scalar dofs, for each mesh element,...
Definition fespace.hpp:1278
const Table & GetFaceToDofTable() const
Return a reference to the internal Table that stores the lists of scalar dofs, for each face in the m...
Definition fespace.hpp:1291
int GetNF() const
Returns number of faces (i.e. co-dimension 1 entities) in the mesh.
Definition fespace.hpp:873
virtual const FiniteElement * GetFE(int i) const
Returns pointer to the FiniteElement in the FiniteElementCollection associated with i'th element in t...
Definition fespace.cpp:3860
const FiniteElement * GetTypicalTraceElement() const
Return a "typical" trace element.
Definition fespace.cpp:3999
const FiniteElement * GetFaceElement(int i) const
Returns pointer to the FiniteElement in the FiniteElementCollection associated with i'th face in the ...
Definition fespace.cpp:3949
Mesh * GetMesh() const
Returns the mesh.
Definition fespace.hpp:639
int GetVSize() const
Return the number of vector dofs, i.e. GetNDofs() x GetVDim().
Definition fespace.hpp:824
const FiniteElement * GetTypicalFE() const
Return GetFE(0) if the local mesh is not empty; otherwise return a typical FE based on the Geometry t...
Definition fespace.cpp:3896
Abstract class for all finite elements.
Definition fe_base.hpp:294
int GetOrder() const
Returns the order of the finite element. In the case of anisotropic orders, returns the maximum order...
Definition fe_base.hpp:414
virtual void GetFaceMap(const int face_id, Array< int > &face_map) const
Return the mapping from lexicographic face DOFs to lexicographic element DOFs for the given local fac...
Definition fe_base.cpp:511
int GetDof() const
Returns the number of degrees of freedom in the finite element.
Definition fe_base.hpp:410
const IntegrationPoint & GetCenter(int GeomType) const
Return the center of the given Geometry::Type, GeomType.
Definition geom.hpp:75
Arbitrary order H1 elements in 2D utilizing the Bernstein basis on a triangle.
Definition fe_pos.hpp:182
InterpolationManager(const FiniteElementSpace &fes, ElementDofOrdering ordering, FaceType type)
Constructor.
std::pair< const DenseMatrix *, int > Key
const Array< NCInterpConfig > & GetNCFaceInterpConfig() const
Return an array containing the interpolation configuration for each face registered with RegisterFace...
void RegisterFaceCoarseToFineInterpolation(const Mesh::FaceInformation &face, int face_index)
Register the face with face and index face_index as a nonconforming (master-slave) face,...
int GetNumInterpolators() const
Return the total number of interpolators.
Array< NCInterpConfig > nc_interp_config
const Vector & GetInterpolators() const
Return an mfem::Vector containing the interpolators in the following format: face_dofs x face_dofs x ...
void LinearizeInterpolatorMapIntoVector()
Transform the interpolation matrix map into a contiguous memory structure.
const FiniteElementSpace & fes
const ElementDofOrdering ordering
Array< InterpConfig > interp_config
void RegisterFaceConformingInterpolation(const Mesh::FaceInformation &face, int face_index)
Register the face with face and index face_index as a conforming face for the interpolation of the de...
A standard isoparametric element transformation.
Definition eltrans.hpp:629
L2ElementRestriction(const FiniteElementSpace &)
void AddMultTranspose(const Vector &x, Vector &y, const real_t a=1.0) const override
Add the E-vector degrees of freedom x to the L-vector degrees of freedom y.
void FillJAndData(const Vector &ea_data, SparseMatrix &mat) const
void MultTranspose(const Vector &x, Vector &y) const override
Action of the transpose operator: y=A^t(x). The default behavior in class Operator is to generate an ...
void FillI(SparseMatrix &mat) const
void Mult(const Vector &x, Vector &y) const override
Operator application: y=A(x).
Operator that extracts Face degrees of freedom for L2 spaces.
void PermuteAndSetFaceDofsGatherIndices2(const Mesh::FaceInformation &face, const int face_index)
Permute and set the gathering indices of elem2 for the interior face described by the face....
virtual void FillI(SparseMatrix &mat, const bool keep_nbr_block=false) const
Fill the I array of SparseMatrix corresponding to the sparsity pattern given by this L2FaceRestrictio...
Array< int > scatter_indices2
void SingleValuedConformingMult(const Vector &x, Vector &y) const
Scatter the degrees of freedom, i.e. goes from L-Vector to face E-Vector. Should only be used with co...
L2FaceRestriction(const FiniteElementSpace &fes, const ElementDofOrdering f_ordering, const FaceType type, const L2FaceValues m, bool build)
Constructs an L2FaceRestriction.
void NormalDerivativeMult(const Vector &x, Vector &y) const override
For each face, sets y to the partial derivative of x with respect to the reference coordinate perpend...
void AddMultTranspose(const Vector &x, Vector &y, const real_t a=1.0) const override
Gather the degrees of freedom, i.e. goes from face E-Vector to L-Vector.
void NormalDerivativeAddMultTranspose(const Vector &x, Vector &y) const override
Add the face reference-normal derivative degrees of freedom in x to the element degrees of freedom in...
void Mult(const Vector &x, Vector &y) const override
Scatter the degrees of freedom, i.e. goes from L-Vector to face E-Vector.
void SingleValuedConformingAddMultTranspose(const Vector &x, Vector &y) const
Gather the degrees of freedom, i.e. goes from face E-Vector to L-Vector. Should only be used with con...
std::unique_ptr< L2NormalDerivativeFaceRestriction > normal_deriv_restr
void PermuteAndSetSharedFaceDofsScatterIndices2(const Mesh::FaceInformation &face, const int face_index)
Permute and set the scattering indices of elem2 for the shared face described by the face....
void CheckFESpace()
Verify that L2FaceRestriction is built from an L2 FESpace.
virtual void DoubleValuedConformingMult(const Vector &x, Vector &y) const
Scatter the degrees of freedom, i.e. goes from L-Vector to face E-Vector. Should only be used with co...
void PermuteAndSetFaceDofsScatterIndices2(const Mesh::FaceInformation &face, const int face_index)
Permute and set the scattering indices of elem2, and increment the offsets for the face described by ...
Array< int > scatter_indices1
const L2FaceValues m
const FiniteElementSpace & fes
void SetBoundaryDofsScatterIndices2(const Mesh::FaceInformation &face, const int face_index)
Set the scattering indices of elem2 for the boundary face described by the face.
void SetFaceDofsScatterIndices1(const Mesh::FaceInformation &face, const int face_index)
Set the scattering indices of elem1, and increment the offsets for the face described by the face....
void SetFaceDofsGatherIndices1(const Mesh::FaceInformation &face, const int face_index)
Set the gathering indices of elem1 for the interior face described by the face.
const ElementDofOrdering ordering
void DoubleValuedConformingAddMultTranspose(const Vector &x, Vector &y) const
Gather the degrees of freedom, i.e. goes from face E-Vector to L-Vector. Should only be used with con...
virtual void FillJAndData(const Vector &fea_data, SparseMatrix &mat, const bool keep_nbr_block=false) const
Fill the J and Data arrays of the SparseMatrix corresponding to the sparsity pattern given by this L2...
virtual void AddFaceMatricesToElementMatrices(const Vector &fea_data, Vector &ea_data) const
This method adds the DG face matrices to the element matrices.
const int face_dofs
Number of dofs on each face.
const int ndofs
Number of dofs in the space (L-vector size)
const int nfdofs
Total number of dofs on the faces (E-vector size)
Array< int > scatter_map
Scatter map.
const FiniteElementSpace & fes
The finite element space.
Array< int > gather_map
Gather map.
const int vdim
vdim of the space
const Array< int > & ScatterMap() const
Return the low-level mapping from L-dofs to E-dofs.
const int nfaces
Number of faces of the requested type.
void Mult(const Vector &x, Vector &y) const override
Scatter the degrees of freedom, i.e. goes from L-Vector to face E-Vector.
const int nsdofs
Number of shared face neighbor (ghost) dofs.
const Array< int > & GatherMap() const override
Low-level access to the underlying gather map.
void MultTransposeShared(const Vector &x, Vector &y) const
Gather degrees of freedom, from face E-vector to L-vector and shared (ghost) DOFs.
void AddMultTranspose(const Vector &x, Vector &y, const real_t a=1.0) const override
Gather the degrees of freedom, i.e. goes from face E-Vector to L-Vector.
const FaceType type
Face type (interior or boundary)
const bool byvdim
DOF ordering (by nodes or by vdim)
Class to compute face normal derivatives (in reference coordinate) of an L2 grid function (used inter...
MemoryType GetMemoryType() const
Return a MemoryType that is currently valid. If both the host and the device pointers are currently v...
void New(int size)
Allocate host memory for size entries with the current host memory type returned by MemoryManager::Ge...
Mesh data type.
Definition mesh.hpp:67
int GetNumFaces() const
Return the number of faces (3D), edges (2D) or vertices (1D).
Definition mesh.cpp:7302
int Dimension() const
Dimension of the reference space used within the elements.
Definition mesh.hpp:1314
FaceInformation GetFaceInformation(int f) const
Definition mesh.cpp:1368
int GetNumFacesWithGhost() const
Return the number of faces (3D), edges (2D) or vertices (1D) including ghost faces.
Definition mesh.cpp:7313
Operator that extracts face degrees of freedom for L2 nonconforming spaces.
const InterpolationManager & interpolations
virtual void DoubleValuedNonconformingMult(const Vector &x, Vector &y) const
Scatter the degrees of freedom, i.e. goes from L-Vector to face E-Vector. Should only be used with no...
void DoubleValuedNonconformingInterpolation(Vector &x) const
Apply a change of basis from coarse element basis to fine element basis for the coarse face dofs.
NCL2FaceRestriction(const FiniteElementSpace &fes, const ElementDofOrdering f_ordering, const FaceType type, const L2FaceValues m, bool build)
Constructs an NCL2FaceRestriction, this is a specialization of a L2FaceRestriction for nonconforming ...
Class for standard nodal finite elements.
Definition fe_base.hpp:798
int width
Dimension of the input / number of columns in the matrix.
Definition operator.hpp:30
int Height() const
Get the height (size of output) of the Operator. Synonym with NumRows().
Definition operator.hpp:68
int height
Dimension of the output / number of rows in the matrix.
Definition operator.hpp:29
The ordering method used when the number of unknowns per mesh node (vector dimension) is bigger than ...
Definition ordering.hpp:13
Abstract parallel finite element space.
Definition pfespace.hpp:31
Class for parallel grid function.
Definition pgridfunc.hpp:50
Data type sparse matrix.
Definition sparsemat.hpp:51
int * ReadWriteI(bool on_dev=true)
int * WriteJ(bool on_dev=true)
Memory< int > & GetMemoryI()
int * WriteI(bool on_dev=true)
Memory< int > & GetMemoryJ()
Memory< real_t > & GetMemoryData()
real_t * WriteData(bool on_dev=true)
Table stores the connectivity of elements of TYPE I to elements of TYPE II. For example,...
Definition table.hpp:43
int * GetJ()
Definition table.hpp:128
void GetRow(int i, Array< int > &row) const
Return row i in array row (the Table must be finalized)
Definition table.cpp:233
const Array< int > & GetDofMap() const
Get an Array<int> that maps lexicographically ordered indices to the indices of the respective nodes/...
Definition fe_base.hpp:1353
Vector data type.
Definition vector.hpp:82
virtual const real_t * Read(bool on_dev=true) const
Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), on_dev).
Definition vector.hpp:520
virtual real_t * ReadWrite(bool on_dev=true)
Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), on_dev).
Definition vector.hpp:536
void SyncMemory(const Vector &v) const
Update the memory location of the vector to match v.
Definition vector.hpp:272
int Size() const
Returns the size of the vector.
Definition vector.hpp:234
virtual void UseDevice(bool use_dev) const
Enable execution of Vector operations using the mfem::Device.
Definition vector.hpp:145
void SetSize(int s)
Resize the vector to size s.
Definition vector.hpp:633
virtual real_t * HostWrite()
Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), false).
Definition vector.hpp:532
virtual real_t * Write(bool on_dev=true)
Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), on_dev).
Definition vector.hpp:528
int dim
Definition ex24.cpp:53
int index(int i, int j, int nx, int ny)
Definition life.cpp:236
real_t a
Definition lissajous.cpp:41
real_t f(const Vector &p)
mfem::real_t real_t
Vector GetLVectorFaceNbrData(const FiniteElementSpace &fes, const Vector &x, FaceType ftype)
Return the face-neighbor data given the L-vector x.
if(k >=N)
Definition forall.hpp:738
T * Write(Memory< T > &mem, int size, bool on_dev=true)
Get a pointer for write access to mem with the mfem::Device's DeviceMemoryClass, if on_dev = true,...
Definition device.hpp:386
Geometry Geometries
Definition fe.cpp:49
MFEM_HOST_DEVICE DeviceTensor< sizeof...(Dims), T > Reshape(T *ptr, Dims... dims)
Wrap a pointer as a DeviceTensor with automatically deduced template parameters.
Definition dtensor.hpp:138
MFEM_HOST_DEVICE int UnsignIndex(int i)
Definition globals.hpp:118
int ToLexOrdering(const int dim, const int face_id, const int size1d, const int index)
Convert a dof face index from Native ordering to lexicographic ordering for quads and hexes.
void forall_2D(int N, int X, int Y, lambda &&body)
Definition forall.hpp:1220
const T & AsConst(const T &a)
Utility function similar to std::as_const in c++17.
Definition array.hpp:453
float real_t
Definition config.hpp:46
int PermuteFaceL2(const int dim, const int face_id1, const int face_id2, const int orientation, const int size1d, const int index)
Compute the dof face index of elem2 corresponding to the given dof face index.
ElementDofOrdering
Constants describing the possible orderings of the DOFs in one element.
Definition fespace.hpp:49
std::function< real_t(const Vector &)> f(real_t mass_coeff)
Definition lor_mms.hpp:30
void forall(int N, lambda &&body)
Definition forall.hpp:1134
FaceType
Definition mesh.hpp:49
This structure is used as a human readable output format that deciphers the information contained in ...
Definition mesh.hpp:2098
bool IsBoundary() const
Return true if the face is a boundary face.
Definition mesh.hpp:2145
bool IsNonconformingCoarse() const
Return true if the face is a nonconforming coarse face.
Definition mesh.hpp:2182
bool IsOfFaceType(FaceType type) const
Return true if the face is of the same type as type.
Definition mesh.hpp:2151
struct mfem::Mesh::FaceInformation::@15 element[2]
Information about the adjacent elements.
bool IsLocal() const
Return true if the face is a local interior face which is NOT a master nonconforming face.
Definition mesh.hpp:2123
bool IsConforming() const
Return true if the face is a conforming face.
Definition mesh.hpp:2165
ElementConformity conformity
Definition mesh.hpp:2106
bool IsShared() const
Return true if the face is a shared interior face which is NOT a master nonconforming face.
Definition mesh.hpp:2130
const DenseMatrix * point_matrix
The point matrix for nonconforming faces.
Definition mesh.hpp:2119