MFEM  v4.5.2
Finite element discretization library
restriction.cpp
Go to the documentation of this file.
1 // Copyright (c) 2010-2023, 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"
13 #include "gridfunc.hpp"
14 #include "fespace.hpp"
15 #include "../general/forall.hpp"
16 #include <climits>
17 
18 #ifdef MFEM_USE_MPI
19 
20 #include "pfespace.hpp"
21 
22 #endif
23 
24 namespace mfem
25 {
26 
28  ElementDofOrdering e_ordering)
29  : fes(f),
30  ne(fes.GetNE()),
31  vdim(fes.GetVDim()),
32  byvdim(fes.GetOrdering() == Ordering::byVDIM),
33  ndofs(fes.GetNDofs()),
34  dof(ne > 0 ? fes.GetFE(0)->GetDof() : 0),
35  nedofs(ne*dof),
36  offsets(ndofs+1),
37  indices(ne*dof),
38  gather_map(ne*dof)
39 {
40  // Assuming all finite elements are the same.
41  height = vdim*ne*dof;
42  width = fes.GetVSize();
43  const bool dof_reorder = (e_ordering == ElementDofOrdering::LEXICOGRAPHIC);
44  const int *dof_map = NULL;
45  if (dof_reorder && ne > 0)
46  {
47  for (int e = 0; e < ne; ++e)
48  {
49  const FiniteElement *fe = fes.GetFE(e);
50  const TensorBasisElement* el =
51  dynamic_cast<const TensorBasisElement*>(fe);
52  if (el) { continue; }
53  MFEM_ABORT("Finite element not suitable for lexicographic ordering");
54  }
55  const FiniteElement *fe = fes.GetFE(0);
56  const TensorBasisElement* el =
57  dynamic_cast<const TensorBasisElement*>(fe);
58  const Array<int> &fe_dof_map = el->GetDofMap();
59  MFEM_VERIFY(fe_dof_map.Size() > 0, "invalid dof map");
60  dof_map = fe_dof_map.GetData();
61  }
62  const Table& e2dTable = fes.GetElementToDofTable();
63  const int* element_map = e2dTable.GetJ();
64  // We will be keeping a count of how many local nodes point to its global dof
65  for (int i = 0; i <= ndofs; ++i)
66  {
67  offsets[i] = 0;
68  }
69  for (int e = 0; e < ne; ++e)
70  {
71  for (int d = 0; d < dof; ++d)
72  {
73  const int sgid = element_map[dof*e + d]; // signed
74  const int gid = (sgid >= 0) ? sgid : -1 - sgid;
75  ++offsets[gid + 1];
76  }
77  }
78  // Aggregate to find offsets for each global dof
79  for (int i = 1; i <= ndofs; ++i)
80  {
81  offsets[i] += offsets[i - 1];
82  }
83  // For each global dof, fill in all local nodes that point to it
84  for (int e = 0; e < ne; ++e)
85  {
86  for (int d = 0; d < dof; ++d)
87  {
88  const int sdid = dof_reorder ? dof_map[d] : 0; // signed
89  const int did = (!dof_reorder)?d:(sdid >= 0 ? sdid : -1-sdid);
90  const int sgid = element_map[dof*e + did]; // signed
91  const int gid = (sgid >= 0) ? sgid : -1-sgid;
92  const int lid = dof*e + d;
93  const bool plus = (sgid >= 0 && sdid >= 0) || (sgid < 0 && sdid < 0);
94  gather_map[lid] = plus ? gid : -1-gid;
95  indices[offsets[gid]++] = plus ? lid : -1-lid;
96  }
97  }
98  // We shifted the offsets vector by 1 by using it as a counter.
99  // Now we shift it back.
100  for (int i = ndofs; i > 0; --i)
101  {
102  offsets[i] = offsets[i - 1];
103  }
104  offsets[0] = 0;
105 }
106 
107 void ElementRestriction::Mult(const Vector& x, Vector& y) const
108 {
109  // Assumes all elements have the same number of dofs
110  const int nd = dof;
111  const int vd = vdim;
112  const bool t = byvdim;
113  auto d_x = Reshape(x.Read(), t?vd:ndofs, t?ndofs:vd);
114  auto d_y = Reshape(y.Write(), nd, vd, ne);
115  auto d_gather_map = gather_map.Read();
116  MFEM_FORALL(i, dof*ne,
117  {
118  const int gid = d_gather_map[i];
119  const bool plus = gid >= 0;
120  const int j = plus ? gid : -1-gid;
121  for (int c = 0; c < vd; ++c)
122  {
123  const double dof_value = d_x(t?c:j, t?j:c);
124  d_y(i % nd, c, i / nd) = plus ? dof_value : -dof_value;
125  }
126  });
127 }
128 
130 {
131  // Assumes all elements have the same number of dofs
132  const int nd = dof;
133  const int vd = vdim;
134  const bool t = byvdim;
135  auto d_x = Reshape(x.Read(), t?vd:ndofs, t?ndofs:vd);
136  auto d_y = Reshape(y.Write(), nd, vd, ne);
137  auto d_gather_map = gather_map.Read();
138 
139  MFEM_FORALL(i, dof*ne,
140  {
141  const int gid = d_gather_map[i];
142  const int j = gid >= 0 ? gid : -1-gid;
143  for (int c = 0; c < vd; ++c)
144  {
145  d_y(i % nd, c, i / nd) = d_x(t?c:j, t?j:c);
146  }
147  });
148 }
149 
150 template <bool ADD>
151 void ElementRestriction::TAddMultTranspose(const Vector& x, Vector& y) const
152 {
153  // Assumes all elements have the same number of dofs
154  const int nd = dof;
155  const int vd = vdim;
156  const bool t = byvdim;
157  auto d_offsets = offsets.Read();
158  auto d_indices = indices.Read();
159  auto d_x = Reshape(x.Read(), nd, vd, ne);
160  auto d_y = Reshape(ADD ? y.ReadWrite() : y.Write(), t?vd:ndofs, t?ndofs:vd);
161  MFEM_FORALL(i, ndofs,
162  {
163  const int offset = d_offsets[i];
164  const int next_offset = d_offsets[i + 1];
165  for (int c = 0; c < vd; ++c)
166  {
167  double dof_value = 0;
168  for (int j = offset; j < next_offset; ++j)
169  {
170  const int idx_j = (d_indices[j] >= 0) ? d_indices[j] : -1 - d_indices[j];
171  dof_value += ((d_indices[j] >= 0) ? d_x(idx_j % nd, c, idx_j / nd) :
172  -d_x(idx_j % nd, c, idx_j / nd));
173  }
174  if (ADD) { d_y(t?c:i,t?i:c) += dof_value; }
175  else { d_y(t?c:i,t?i:c) = dof_value; }
176  }
177  });
178 }
179 
181 {
182  constexpr bool ADD = false;
183  TAddMultTranspose<ADD>(x, y);
184 }
185 
187  const double a) const
188 {
189  MFEM_VERIFY(a == 1.0, "General coefficient case is not yet supported!");
190  constexpr bool ADD = true;
191  TAddMultTranspose<ADD>(x, y);
192 }
193 
195 {
196  // Assumes all elements have the same number of dofs
197  const int nd = dof;
198  const int vd = vdim;
199  const bool t = byvdim;
200  auto d_offsets = offsets.Read();
201  auto d_indices = indices.Read();
202  auto d_x = Reshape(x.Read(), nd, vd, ne);
203  auto d_y = Reshape(y.Write(), t?vd:ndofs, t?ndofs:vd);
204  MFEM_FORALL(i, ndofs,
205  {
206  const int offset = d_offsets[i];
207  const int next_offset = d_offsets[i + 1];
208  for (int c = 0; c < vd; ++c)
209  {
210  double dof_value = 0;
211  for (int j = offset; j < next_offset; ++j)
212  {
213  const int idx_j = (d_indices[j] >= 0) ? d_indices[j] : -1 - d_indices[j];
214  dof_value += d_x(idx_j % nd, c, idx_j / nd);
215  }
216  d_y(t?c:i,t?i:c) = dof_value;
217  }
218  });
219 }
220 
222 {
223  // Assumes all elements have the same number of dofs
224  const int nd = dof;
225  const int vd = vdim;
226  const bool t = byvdim;
227  auto d_offsets = offsets.Read();
228  auto d_indices = indices.Read();
229  auto d_x = Reshape(x.Read(), nd, vd, ne);
230  auto d_y = Reshape(y.Write(), t?vd:ndofs, t?ndofs:vd);
231  MFEM_FORALL(i, ndofs,
232  {
233  const int next_offset = d_offsets[i + 1];
234  for (int c = 0; c < vd; ++c)
235  {
236  double dof_value = 0;
237  const int j = next_offset - 1;
238  const int idx_j = (d_indices[j] >= 0) ? d_indices[j] : -1 - d_indices[j];
239  dof_value = (d_indices[j] >= 0) ? d_x(idx_j % nd, c, idx_j / nd) :
240  -d_x(idx_j % nd, c, idx_j / nd);
241  d_y(t?c:i,t?i:c) = dof_value;
242  }
243  });
244 }
245 
247 {
248  // Assumes all elements have the same number of dofs
249  const int nd = dof;
250  const int vd = vdim;
251  const bool t = byvdim;
252 
253  Array<char> processed(vd * ndofs);
254  processed = 0;
255 
256  auto d_offsets = offsets.HostRead();
257  auto d_indices = indices.HostRead();
258  auto d_x = Reshape(processed.HostReadWrite(), t?vd:ndofs, t?ndofs:vd);
259  auto d_y = Reshape(y.HostWrite(), nd, vd, ne);
260  for (int i = 0; i < ndofs; ++i)
261  {
262  const int offset = d_offsets[i];
263  const int next_offset = d_offsets[i+1];
264  for (int c = 0; c < vd; ++c)
265  {
266  for (int j = offset; j < next_offset; ++j)
267  {
268  const int idx_j = d_indices[j];
269  if (d_x(t?c:i,t?i:c))
270  {
271  d_y(idx_j % nd, c, idx_j / nd) = 0.0;
272  }
273  else
274  {
275  d_y(idx_j % nd, c, idx_j / nd) = 1.0;
276  d_x(t?c:i,t?i:c) = 1;
277  }
278  }
279  }
280  }
281 }
282 
284  SparseMatrix &mat) const
285 {
286  mat.GetMemoryI().New(mat.Height()+1, mat.GetMemoryI().GetMemoryType());
287  const int nnz = FillI(mat);
288  mat.GetMemoryJ().New(nnz, mat.GetMemoryJ().GetMemoryType());
289  mat.GetMemoryData().New(nnz, mat.GetMemoryData().GetMemoryType());
290  FillJAndData(mat_ea, mat);
291 }
292 
293 static MFEM_HOST_DEVICE int GetMinElt(const int *my_elts, const int nbElts,
294  const int *nbr_elts, const int nbrNbElts)
295 {
296  // Find the minimal element index found in both my_elts[] and nbr_elts[]
297  int min_el = INT_MAX;
298  for (int i = 0; i < nbElts; i++)
299  {
300  const int e_i = my_elts[i];
301  if (e_i >= min_el) { continue; }
302  for (int j = 0; j < nbrNbElts; j++)
303  {
304  if (e_i==nbr_elts[j])
305  {
306  min_el = e_i; // we already know e_i < min_el
307  break;
308  }
309  }
310  }
311  return min_el;
312 }
313 
314 /** Returns the index where a non-zero entry should be added and increment the
315  number of non-zeros for the row i_L. */
316 static MFEM_HOST_DEVICE int GetAndIncrementNnzIndex(const int i_L, int* I)
317 {
318  int ind = AtomicAdd(I[i_L],1);
319  return ind;
320 }
321 
323 {
324  static constexpr int Max = MaxNbNbr;
325  const int all_dofs = ndofs;
326  const int vd = vdim;
327  const int elt_dofs = dof;
328  auto I = mat.ReadWriteI();
329  auto d_offsets = offsets.Read();
330  auto d_indices = indices.Read();
331  auto d_gather_map = gather_map.Read();
332  MFEM_FORALL(i_L, vd*all_dofs+1,
333  {
334  I[i_L] = 0;
335  });
336  MFEM_FORALL(l_dof, ne*elt_dofs,
337  {
338  const int e = l_dof/elt_dofs;
339  const int i = l_dof%elt_dofs;
340 
341  int i_elts[Max];
342  const int i_gm = e*elt_dofs + i;
343  const int i_L = d_gather_map[i_gm];
344  const int i_offset = d_offsets[i_L];
345  const int i_next_offset = d_offsets[i_L+1];
346  const int i_nbElts = i_next_offset - i_offset;
347  MFEM_ASSERT_KERNEL(
348  i_nbElts <= Max,
349  "The connectivity of this mesh is beyond the max, increase the "
350  "MaxNbNbr variable to comply with your mesh.");
351  for (int e_i = 0; e_i < i_nbElts; ++e_i)
352  {
353  const int i_E = d_indices[i_offset+e_i];
354  i_elts[e_i] = i_E/elt_dofs;
355  }
356  for (int j = 0; j < elt_dofs; j++)
357  {
358  const int j_gm = e*elt_dofs + j;
359  const int j_L = d_gather_map[j_gm];
360  const int j_offset = d_offsets[j_L];
361  const int j_next_offset = d_offsets[j_L+1];
362  const int j_nbElts = j_next_offset - j_offset;
363  if (i_nbElts == 1 || j_nbElts == 1) // no assembly required
364  {
365  GetAndIncrementNnzIndex(i_L, I);
366  }
367  else // assembly required
368  {
369  int j_elts[Max];
370  for (int e_j = 0; e_j < j_nbElts; ++e_j)
371  {
372  const int j_E = d_indices[j_offset+e_j];
373  const int elt = j_E/elt_dofs;
374  j_elts[e_j] = elt;
375  }
376  int min_e = GetMinElt(i_elts, i_nbElts, j_elts, j_nbElts);
377  if (e == min_e) // add the nnz only once
378  {
379  GetAndIncrementNnzIndex(i_L, I);
380  }
381  }
382  }
383  });
384  // We need to sum the entries of I, we do it on CPU as it is very sequential.
385  auto h_I = mat.HostReadWriteI();
386  const int nTdofs = vd*all_dofs;
387  int sum = 0;
388  for (int i = 0; i < nTdofs; i++)
389  {
390  const int nnz = h_I[i];
391  h_I[i] = sum;
392  sum+=nnz;
393  }
394  h_I[nTdofs] = sum;
395  // We return the number of nnz
396  return h_I[nTdofs];
397 }
398 
400  SparseMatrix &mat) const
401 {
402  static constexpr int Max = MaxNbNbr;
403  const int all_dofs = ndofs;
404  const int vd = vdim;
405  const int elt_dofs = dof;
406  auto I = mat.ReadWriteI();
407  auto J = mat.WriteJ();
408  auto Data = mat.WriteData();
409  auto d_offsets = offsets.Read();
410  auto d_indices = indices.Read();
411  auto d_gather_map = gather_map.Read();
412  auto mat_ea = Reshape(ea_data.Read(), elt_dofs, elt_dofs, ne);
413  MFEM_FORALL(l_dof, ne*elt_dofs,
414  {
415  const int e = l_dof/elt_dofs;
416  const int i = l_dof%elt_dofs;
417 
418  int i_elts[Max];
419  int i_B[Max];
420  const int i_gm = e*elt_dofs + i;
421  const int i_L = d_gather_map[i_gm];
422  const int i_offset = d_offsets[i_L];
423  const int i_next_offset = d_offsets[i_L+1];
424  const int i_nbElts = i_next_offset - i_offset;
425  MFEM_ASSERT_KERNEL(
426  i_nbElts <= Max,
427  "The connectivity of this mesh is beyond the max, increase the "
428  "MaxNbNbr variable to comply with your mesh.");
429  for (int e_i = 0; e_i < i_nbElts; ++e_i)
430  {
431  const int i_E = d_indices[i_offset+e_i];
432  i_elts[e_i] = i_E/elt_dofs;
433  i_B[e_i] = i_E%elt_dofs;
434  }
435  for (int j = 0; j < elt_dofs; j++)
436  {
437  const int j_gm = e*elt_dofs + j;
438  const int j_L = d_gather_map[j_gm];
439  const int j_offset = d_offsets[j_L];
440  const int j_next_offset = d_offsets[j_L+1];
441  const int j_nbElts = j_next_offset - j_offset;
442  if (i_nbElts == 1 || j_nbElts == 1) // no assembly required
443  {
444  const int nnz = GetAndIncrementNnzIndex(i_L, I);
445  J[nnz] = j_L;
446  Data[nnz] = mat_ea(j,i,e);
447  }
448  else // assembly required
449  {
450  int j_elts[Max];
451  int j_B[Max];
452  for (int e_j = 0; e_j < j_nbElts; ++e_j)
453  {
454  const int j_E = d_indices[j_offset+e_j];
455  const int elt = j_E/elt_dofs;
456  j_elts[e_j] = elt;
457  j_B[e_j] = j_E%elt_dofs;
458  }
459  int min_e = GetMinElt(i_elts, i_nbElts, j_elts, j_nbElts);
460  if (e == min_e) // add the nnz only once
461  {
462  double val = 0.0;
463  for (int k = 0; k < i_nbElts; k++)
464  {
465  const int e_i = i_elts[k];
466  const int i_Bloc = i_B[k];
467  for (int l = 0; l < j_nbElts; l++)
468  {
469  const int e_j = j_elts[l];
470  const int j_Bloc = j_B[l];
471  if (e_i == e_j)
472  {
473  val += mat_ea(j_Bloc, i_Bloc, e_i);
474  }
475  }
476  }
477  const int nnz = GetAndIncrementNnzIndex(i_L, I);
478  J[nnz] = j_L;
479  Data[nnz] = val;
480  }
481  }
482  }
483  });
484  // We need to shift again the entries of I, we do it on CPU as it is very
485  // sequential.
486  auto h_I = mat.HostReadWriteI();
487  const int size = vd*all_dofs;
488  for (int i = 0; i < size; i++)
489  {
490  h_I[size-i] = h_I[size-(i+1)];
491  }
492  h_I[0] = 0;
493 }
494 
496  : ne(fes.GetNE()),
497  vdim(fes.GetVDim()),
498  byvdim(fes.GetOrdering() == Ordering::byVDIM),
499  ndof(ne > 0 ? fes.GetFE(0)->GetDof() : 0),
500  ndofs(fes.GetNDofs())
501 {
502  height = vdim*ne*ndof;
503  width = vdim*ne*ndof;
504 }
505 
506 void L2ElementRestriction::Mult(const Vector &x, Vector &y) const
507 {
508  const int nd = ndof;
509  const int vd = vdim;
510  const bool t = byvdim;
511  auto d_x = Reshape(x.Read(), t?vd:ndofs, t?ndofs:vd);
512  auto d_y = Reshape(y.Write(), nd, vd, ne);
513  MFEM_FORALL(i, ndofs,
514  {
515  const int idx = i;
516  const int dof = idx % nd;
517  const int e = idx / nd;
518  for (int c = 0; c < vd; ++c)
519  {
520  d_y(dof, c, e) = d_x(t?c:idx, t?idx:c);
521  }
522  });
523 }
524 
525 template <bool ADD>
526 void L2ElementRestriction::TAddMultTranspose(const Vector &x, Vector &y) const
527 {
528  const int nd = ndof;
529  const int vd = vdim;
530  const bool t = byvdim;
531  auto d_x = Reshape(x.Read(), nd, vd, ne);
532  auto d_y = Reshape(ADD ? y.ReadWrite() : y.Write(), t?vd:ndofs, t?ndofs:vd);
533  MFEM_FORALL(i, ndofs,
534  {
535  const int idx = i;
536  const int dof = idx % nd;
537  const int e = idx / nd;
538  for (int c = 0; c < vd; ++c)
539  {
540  if (ADD) { d_y(t?c:idx,t?idx:c) += d_x(dof, c, e); }
541  else { d_y(t?c:idx,t?idx:c) = d_x(dof, c, e); }
542  }
543  });
544 }
545 
547 {
548  constexpr bool ADD = false;
549  TAddMultTranspose<ADD>(x, y);
550 }
551 
553  const double a) const
554 {
555  MFEM_VERIFY(a == 1.0, "General coefficient case is not yet supported!");
556  constexpr bool ADD = true;
557  TAddMultTranspose<ADD>(x, y);
558 }
559 
561 {
562  const int elem_dofs = ndof;
563  const int vd = vdim;
564  auto I = mat.WriteI();
565  const int isize = mat.Height() + 1;
566  const int interior_dofs = ne*elem_dofs*vd;
567  MFEM_FORALL(dof, isize,
568  {
569  I[dof] = dof<interior_dofs ? elem_dofs : 0;
570  });
571 }
572 
573 static MFEM_HOST_DEVICE int AddNnz(const int iE, int *I, const int dofs)
574 {
575  int val = AtomicAdd(I[iE],dofs);
576  return val;
577 }
578 
580  SparseMatrix &mat) const
581 {
582  const int elem_dofs = ndof;
583  const int vd = vdim;
584  auto I = mat.ReadWriteI();
585  auto J = mat.WriteJ();
586  auto Data = mat.WriteData();
587  auto mat_ea = Reshape(ea_data.Read(), elem_dofs, elem_dofs, ne);
588  MFEM_FORALL(iE, ne*elem_dofs*vd,
589  {
590  const int offset = AddNnz(iE,I,elem_dofs);
591  const int e = iE/elem_dofs;
592  const int i = iE%elem_dofs;
593  for (int j = 0; j < elem_dofs; j++)
594  {
595  J[offset+j] = e*elem_dofs+j;
596  Data[offset+j] = mat_ea(j,i,e);
597  }
598  });
599 }
600 
601 /** Return the face degrees of freedom returned in Lexicographic order.
602  Note: Only for quad and hex */
603 void GetFaceDofs(const int dim, const int face_id,
604  const int dof1d, Array<int> &face_map)
605 {
606  switch (dim)
607  {
608  case 1:
609  switch (face_id)
610  {
611  case 0: // WEST
612  face_map[0] = 0;
613  break;
614  case 1: // EAST
615  face_map[0] = dof1d-1;
616  break;
617  }
618  break;
619  case 2:
620  switch (face_id)
621  {
622  case 0: // SOUTH
623  for (int i = 0; i < dof1d; ++i)
624  {
625  face_map[i] = i;
626  }
627  break;
628  case 1: // EAST
629  for (int i = 0; i < dof1d; ++i)
630  {
631  face_map[i] = dof1d-1 + i*dof1d;
632  }
633  break;
634  case 2: // NORTH
635  for (int i = 0; i < dof1d; ++i)
636  {
637  face_map[i] = (dof1d-1)*dof1d + i;
638  }
639  break;
640  case 3: // WEST
641  for (int i = 0; i < dof1d; ++i)
642  {
643  face_map[i] = i*dof1d;
644  }
645  break;
646  }
647  break;
648  case 3:
649  switch (face_id)
650  {
651  case 0: // BOTTOM
652  for (int i = 0; i < dof1d; ++i)
653  {
654  for (int j = 0; j < dof1d; ++j)
655  {
656  face_map[i+j*dof1d] = i + j*dof1d;
657  }
658  }
659  break;
660  case 1: // SOUTH
661  for (int i = 0; i < dof1d; ++i)
662  {
663  for (int j = 0; j < dof1d; ++j)
664  {
665  face_map[i+j*dof1d] = i + j*dof1d*dof1d;
666  }
667  }
668  break;
669  case 2: // EAST
670  for (int i = 0; i < dof1d; ++i)
671  {
672  for (int j = 0; j < dof1d; ++j)
673  {
674  face_map[i+j*dof1d] = dof1d-1 + i*dof1d + j*dof1d*dof1d;
675  }
676  }
677  break;
678  case 3: // NORTH
679  for (int i = 0; i < dof1d; ++i)
680  {
681  for (int j = 0; j < dof1d; ++j)
682  {
683  face_map[i+j*dof1d] = (dof1d-1)*dof1d + i + j*dof1d*dof1d;
684  }
685  }
686  break;
687  case 4: // WEST
688  for (int i = 0; i < dof1d; ++i)
689  {
690  for (int j = 0; j < dof1d; ++j)
691  {
692  face_map[i+j*dof1d] = i*dof1d + j*dof1d*dof1d;
693  }
694  }
695  break;
696  case 5: // TOP
697  for (int i = 0; i < dof1d; ++i)
698  {
699  for (int j = 0; j < dof1d; ++j)
700  {
701  face_map[i+j*dof1d] = (dof1d-1)*dof1d*dof1d + i + j*dof1d;
702  }
703  }
704  break;
705  }
706  break;
707  }
708 }
709 
711  const ElementDofOrdering e_ordering,
712  const FaceType type,
713  bool build)
714  : fes(fes),
715  nf(fes.GetNFbyType(type)),
716  vdim(fes.GetVDim()),
717  byvdim(fes.GetOrdering() == Ordering::byVDIM),
718  face_dofs(nf > 0 ? fes.GetFaceElement(0)->GetDof() : 0),
719  elem_dofs(fes.GetFE(0)->GetDof()),
720  nfdofs(nf*face_dofs),
721  ndofs(fes.GetNDofs()),
722  scatter_indices(nf*face_dofs),
723  gather_offsets(ndofs+1),
724  gather_indices(nf*face_dofs),
725  face_map(face_dofs)
726 {
728  width = fes.GetVSize();
729  if (!build) { return; }
730  if (nf==0) { return; }
731 
732  CheckFESpace(e_ordering);
733 
734  ComputeScatterIndicesAndOffsets(e_ordering, type);
735 
736  ComputeGatherIndices(e_ordering,type);
737 }
738 
740  const ElementDofOrdering e_ordering,
741  const FaceType type)
742  : H1FaceRestriction(fes, e_ordering, type, true)
743 { }
744 
745 void H1FaceRestriction::Mult(const Vector& x, Vector& y) const
746 {
747  if (nf==0) { return; }
748  // Assumes all elements have the same number of dofs
749  const int nface_dofs = face_dofs;
750  const int vd = vdim;
751  const bool t = byvdim;
752  auto d_indices = scatter_indices.Read();
753  auto d_x = Reshape(x.Read(), t?vd:ndofs, t?ndofs:vd);
754  auto d_y = Reshape(y.Write(), nface_dofs, vd, nf);
755  MFEM_FORALL(i, nfdofs,
756  {
757  const int idx = d_indices[i];
758  const int dof = i % nface_dofs;
759  const int face = i / nface_dofs;
760  for (int c = 0; c < vd; ++c)
761  {
762  d_y(dof, c, face) = d_x(t?c:idx, t?idx:c);
763  }
764  });
765 }
766 
768  const double a) const
769 {
770  MFEM_VERIFY(a == 1.0, "General coefficient case is not yet supported!");
771  if (nf==0) { return; }
772  // Assumes all elements have the same number of dofs
773  const int nface_dofs = face_dofs;
774  const int vd = vdim;
775  const bool t = byvdim;
776  auto d_offsets = gather_offsets.Read();
777  auto d_indices = gather_indices.Read();
778  auto d_x = Reshape(x.Read(), nface_dofs, vd, nf);
779  auto d_y = Reshape(y.ReadWrite(), t?vd:ndofs, t?ndofs:vd);
780  MFEM_FORALL(i, ndofs,
781  {
782  const int offset = d_offsets[i];
783  const int next_offset = d_offsets[i + 1];
784  for (int c = 0; c < vd; ++c)
785  {
786  double dof_value = 0;
787  for (int j = offset; j < next_offset; ++j)
788  {
789  const int idx_j = d_indices[j];
790  dof_value += d_x(idx_j % nface_dofs, c, idx_j / nface_dofs);
791  }
792  d_y(t?c:i,t?i:c) += dof_value;
793  }
794  });
795 }
796 
798 {
799 #ifdef MFEM_USE_MPI
800 
801  // If the underlying finite element space is parallel, ensure the face
802  // neighbor information is generated.
803  if (const ParFiniteElementSpace *pfes
804  = dynamic_cast<const ParFiniteElementSpace*>(&fes))
805  {
806  pfes->GetParMesh()->ExchangeFaceNbrData();
807  }
808 
809 #endif
810 
811 #ifdef MFEM_DEBUG
812  // If fespace == H1
813  const FiniteElement *fe0 = fes.GetFE(0);
814  const TensorBasisElement *tfe = dynamic_cast<const TensorBasisElement*>(fe0);
815  MFEM_VERIFY(tfe != NULL &&
818  "Only Gauss-Lobatto and Bernstein basis are supported in "
819  "H1FaceRestriction.");
820 
821  // Assuming all finite elements are using Gauss-Lobatto.
822  const bool dof_reorder = (e_ordering == ElementDofOrdering::LEXICOGRAPHIC);
823  if (dof_reorder && nf > 0)
824  {
825  for (int f = 0; f < fes.GetNF(); ++f)
826  {
827  const FiniteElement *fe = fes.GetFaceElement(f);
828  const TensorBasisElement* el =
829  dynamic_cast<const TensorBasisElement*>(fe);
830  if (el) { continue; }
831  MFEM_ABORT("Finite element not suitable for lexicographic ordering");
832  }
833  const FiniteElement *fe = fes.GetFaceElement(0);
834  const TensorBasisElement* el =
835  dynamic_cast<const TensorBasisElement*>(fe);
836  const Array<int> &fe_dof_map = el->GetDofMap();
837  MFEM_VERIFY(fe_dof_map.Size() > 0, "invalid dof map");
838  }
839 #endif
840 }
841 
842 void H1FaceRestriction::ComputeScatterIndicesAndOffsets(
843  const ElementDofOrdering ordering,
844  const FaceType type)
845 {
846  Mesh &mesh = *fes.GetMesh();
847 
848  // Initialization of the offsets
849  for (int i = 0; i <= ndofs; ++i)
850  {
851  gather_offsets[i] = 0;
852  }
853 
854  // Computation of scatter indices and offsets
855  int f_ind = 0;
856  for (int f = 0; f < fes.GetNF(); ++f)
857  {
858  Mesh::FaceInformation face = mesh.GetFaceInformation(f);
859  if ( face.IsNonconformingCoarse() )
860  {
861  // We skip nonconforming coarse faces as they are treated
862  // by the corresponding nonconforming fine faces.
863  continue;
864  }
865  else if ( face.IsOfFaceType(type) )
866  {
867  SetFaceDofsScatterIndices(face, f_ind, ordering);
868  f_ind++;
869  }
870  }
871  MFEM_VERIFY(f_ind==nf, "Unexpected number of faces.");
872 
873  // Summation of the offsets
874  for (int i = 1; i <= ndofs; ++i)
875  {
876  gather_offsets[i] += gather_offsets[i - 1];
877  }
878 }
879 
880 void H1FaceRestriction::ComputeGatherIndices(
881  const ElementDofOrdering ordering,
882  const FaceType type)
883 {
884  Mesh &mesh = *fes.GetMesh();
885 
886  // Computation of gather_indices
887  int f_ind = 0;
888  for (int f = 0; f < fes.GetNF(); ++f)
889  {
890  Mesh::FaceInformation face = mesh.GetFaceInformation(f);
891  if ( face.IsNonconformingCoarse() )
892  {
893  // We skip nonconforming coarse faces as they are treated
894  // by the corresponding nonconforming fine faces.
895  continue;
896  }
897  else if ( face.IsOfFaceType(type) )
898  {
899  SetFaceDofsGatherIndices(face, f_ind, ordering);
900  f_ind++;
901  }
902  }
903  MFEM_VERIFY(f_ind==nf, "Unexpected number of faces.");
904 
905  // Reset offsets to their initial value
906  for (int i = ndofs; i > 0; --i)
907  {
908  gather_offsets[i] = gather_offsets[i - 1];
909  }
910  gather_offsets[0] = 0;
911 }
912 
914  const Mesh::FaceInformation &face,
915  const int face_index,
916  const ElementDofOrdering ordering)
917 {
918  MFEM_ASSERT(!(face.IsNonconformingCoarse()),
919  "This method should not be used on nonconforming coarse faces.");
920  MFEM_ASSERT(face.element[0].orientation==0,
921  "FaceRestriction used on degenerated mesh.");
922 
923  const TensorBasisElement* el =
924  dynamic_cast<const TensorBasisElement*>(fes.GetFE(0));
925  const int *dof_map = el->GetDofMap().GetData();
926  const Table& e2dTable = fes.GetElementToDofTable();
927  const int* elem_map = e2dTable.GetJ();
928  const int face_id = face.element[0].local_face_id;
929  const int dim = fes.GetMesh()->Dimension();
930  const int dof1d = fes.GetFE(0)->GetOrder()+1;
931  const int elem_index = face.element[0].index;
932  const bool dof_reorder = (ordering == ElementDofOrdering::LEXICOGRAPHIC);
933  GetFaceDofs(dim, face_id, dof1d, face_map); // Only for quad and hex
934 
935  for (int face_dof = 0; face_dof < face_dofs; ++face_dof)
936  {
937  const int nat_volume_dof = face_map[face_dof];
938  const int volume_dof = (!dof_reorder)?
939  nat_volume_dof:
940  dof_map[nat_volume_dof];
941  const int global_dof = elem_map[elem_index*elem_dofs + volume_dof];
942  const int restriction_dof = face_dofs*face_index + face_dof;
943  scatter_indices[restriction_dof] = global_dof;
944  ++gather_offsets[global_dof + 1];
945  }
946 }
947 
949  const Mesh::FaceInformation &face,
950  const int face_index,
951  const ElementDofOrdering ordering)
952 {
953  MFEM_ASSERT(!(face.IsNonconformingCoarse()),
954  "This method should not be used on nonconforming coarse faces.");
955 
956  const TensorBasisElement* el =
957  dynamic_cast<const TensorBasisElement*>(fes.GetFE(0));
958  const int *dof_map = el->GetDofMap().GetData();
959  const Table& e2dTable = fes.GetElementToDofTable();
960  const int* elem_map = e2dTable.GetJ();
961  const int face_id = face.element[0].local_face_id;
962  const int dim = fes.GetMesh()->Dimension();
963  const int dof1d = fes.GetFE(0)->GetOrder()+1;
964  const int elem_index = face.element[0].index;
965  const bool dof_reorder = (ordering == ElementDofOrdering::LEXICOGRAPHIC);
966  GetFaceDofs(dim, face_id, dof1d, face_map); // Only for quad and hex
967 
968  for (int face_dof = 0; face_dof < face_dofs; ++face_dof)
969  {
970  const int nat_volume_dof = face_map[face_dof];
971  const int volume_dof = (!dof_reorder)?nat_volume_dof:dof_map[nat_volume_dof];
972  const int global_dof = elem_map[elem_index*elem_dofs + volume_dof];
973  const int restriction_dof = face_dofs*face_index + face_dof;
974  gather_indices[gather_offsets[global_dof]++] = restriction_dof;
975  }
976 }
977 
978 static int ToLexOrdering2D(const int face_id, const int size1d, const int i)
979 {
980  if (face_id==2 || face_id==3)
981  {
982  return size1d-1-i;
983  }
984  else
985  {
986  return i;
987  }
988 }
989 
990 static int PermuteFace2D(const int face_id1, const int face_id2,
991  const int orientation,
992  const int size1d, const int index)
993 {
994  int new_index;
995  // Convert from lex ordering
996  if (face_id1==2 || face_id1==3)
997  {
998  new_index = size1d-1-index;
999  }
1000  else
1001  {
1002  new_index = index;
1003  }
1004  // Permute based on face orientations
1005  if (orientation==1)
1006  {
1007  new_index = size1d-1-new_index;
1008  }
1009  return ToLexOrdering2D(face_id2, size1d, new_index);
1010 }
1011 
1012 static int ToLexOrdering3D(const int face_id, const int size1d, const int i,
1013  const int j)
1014 {
1015  if (face_id==2 || face_id==1 || face_id==5)
1016  {
1017  return i + j*size1d;
1018  }
1019  else if (face_id==3 || face_id==4)
1020  {
1021  return (size1d-1-i) + j*size1d;
1022  }
1023  else // face_id==0
1024  {
1025  return i + (size1d-1-j)*size1d;
1026  }
1027 }
1028 
1029 static int PermuteFace3D(const int face_id1, const int face_id2,
1030  const int orientation,
1031  const int size1d, const int index)
1032 {
1033  int i=0, j=0, new_i=0, new_j=0;
1034  i = index%size1d;
1035  j = index/size1d;
1036  // Convert from lex ordering
1037  if (face_id1==3 || face_id1==4)
1038  {
1039  i = size1d-1-i;
1040  }
1041  else if (face_id1==0)
1042  {
1043  j = size1d-1-j;
1044  }
1045  // Permute based on face orientations
1046  switch (orientation)
1047  {
1048  case 0:
1049  new_i = i;
1050  new_j = j;
1051  break;
1052  case 1:
1053  new_i = j;
1054  new_j = i;
1055  break;
1056  case 2:
1057  new_i = j;
1058  new_j = (size1d-1-i);
1059  break;
1060  case 3:
1061  new_i = (size1d-1-i);
1062  new_j = j;
1063  break;
1064  case 4:
1065  new_i = (size1d-1-i);
1066  new_j = (size1d-1-j);
1067  break;
1068  case 5:
1069  new_i = (size1d-1-j);
1070  new_j = (size1d-1-i);
1071  break;
1072  case 6:
1073  new_i = (size1d-1-j);
1074  new_j = i;
1075  break;
1076  case 7:
1077  new_i = i;
1078  new_j = (size1d-1-j);
1079  break;
1080  }
1081  return ToLexOrdering3D(face_id2, size1d, new_i, new_j);
1082 }
1083 
1084 // Permute dofs or quads on a face for e2 to match with the ordering of e1
1085 int PermuteFaceL2(const int dim, const int face_id1,
1086  const int face_id2, const int orientation,
1087  const int size1d, const int index)
1088 {
1089  switch (dim)
1090  {
1091  case 1:
1092  return 0;
1093  case 2:
1094  return PermuteFace2D(face_id1, face_id2, orientation, size1d, index);
1095  case 3:
1096  return PermuteFace3D(face_id1, face_id2, orientation, size1d, index);
1097  default:
1098  MFEM_ABORT("Unsupported dimension.");
1099  return 0;
1100  }
1101 }
1102 
1104  const ElementDofOrdering e_ordering,
1105  const FaceType type,
1106  const L2FaceValues m,
1107  bool build)
1108  : fes(fes),
1109  nf(fes.GetNFbyType(type)),
1110  ne(fes.GetNE()),
1111  vdim(fes.GetVDim()),
1112  byvdim(fes.GetOrdering() == Ordering::byVDIM),
1113  face_dofs(nf > 0 ?
1114  fes.GetTraceElement(0, fes.GetMesh()->GetFaceGeometry(0))->GetDof()
1115  : 0),
1116  elem_dofs(fes.GetFE(0)->GetDof()),
1117  nfdofs(nf*face_dofs),
1118  ndofs(fes.GetNDofs()),
1119  type(type),
1120  m(m),
1121  scatter_indices1(nf*face_dofs),
1122  scatter_indices2(m==L2FaceValues::DoubleValued?nf*face_dofs:0),
1123  gather_offsets(ndofs+1),
1124  gather_indices((m==L2FaceValues::DoubleValued? 2 : 1)*nf*face_dofs),
1125  face_map(face_dofs)
1126 {
1128  width = fes.GetVSize();
1129  if (!build) { return; }
1130 
1131  CheckFESpace(e_ordering);
1132 
1133  ComputeScatterIndicesAndOffsets(e_ordering,type);
1134 
1135  ComputeGatherIndices(e_ordering, type);
1136 }
1137 
1139  const ElementDofOrdering e_ordering,
1140  const FaceType type,
1141  const L2FaceValues m)
1142  : L2FaceRestriction(fes, e_ordering, type, m, true)
1143 { }
1144 
1146  Vector& y) const
1147 {
1148  MFEM_ASSERT(
1150  "This method should be called when m == L2FaceValues::SingleValued.");
1151  // Assumes all elements have the same number of dofs
1152  const int nface_dofs = face_dofs;
1153  const int vd = vdim;
1154  const bool t = byvdim;
1155  auto d_indices1 = scatter_indices1.Read();
1156  auto d_x = Reshape(x.Read(), t?vd:ndofs, t?ndofs:vd);
1157  auto d_y = Reshape(y.Write(), nface_dofs, vd, nf);
1158  MFEM_FORALL(i, nfdofs,
1159  {
1160  const int dof = i % nface_dofs;
1161  const int face = i / nface_dofs;
1162  const int idx1 = d_indices1[i];
1163  for (int c = 0; c < vd; ++c)
1164  {
1165  d_y(dof, c, face) = d_x(t?c:idx1, t?idx1:c);
1166  }
1167  });
1168 }
1169 
1171  Vector& y) const
1172 {
1173  MFEM_ASSERT(
1175  "This method should be called when m == L2FaceValues::DoubleValued.");
1176  // Assumes all elements have the same number of dofs
1177  const int nface_dofs = face_dofs;
1178  const int vd = vdim;
1179  const bool t = byvdim;
1180  auto d_indices1 = scatter_indices1.Read();
1181  auto d_indices2 = scatter_indices2.Read();
1182  auto d_x = Reshape(x.Read(), t?vd:ndofs, t?ndofs:vd);
1183  auto d_y = Reshape(y.Write(), nface_dofs, vd, 2, nf);
1184  MFEM_FORALL(i, nfdofs,
1185  {
1186  const int dof = i % nface_dofs;
1187  const int face = i / nface_dofs;
1188  const int idx1 = d_indices1[i];
1189  for (int c = 0; c < vd; ++c)
1190  {
1191  d_y(dof, c, 0, face) = d_x(t?c:idx1, t?idx1:c);
1192  }
1193  const int idx2 = d_indices2[i];
1194  for (int c = 0; c < vd; ++c)
1195  {
1196  d_y(dof, c, 1, face) = idx2==-1 ? 0.0 : d_x(t?c:idx2, t?idx2:c);
1197  }
1198  });
1199 }
1200 
1201 void L2FaceRestriction::Mult(const Vector& x, Vector& y) const
1202 {
1203  if (nf==0) { return; }
1205  {
1207  }
1208  else
1209  {
1211  }
1212 }
1213 
1215  const Vector& x, Vector& y) const
1216 {
1217  // Assumes all elements have the same number of dofs
1218  const int nface_dofs = face_dofs;
1219  const int vd = vdim;
1220  const bool t = byvdim;
1221  auto d_offsets = gather_offsets.Read();
1222  auto d_indices = gather_indices.Read();
1223  auto d_x = Reshape(x.Read(), nface_dofs, vd, nf);
1224  auto d_y = Reshape(y.ReadWrite(), t?vd:ndofs, t?ndofs:vd);
1225  MFEM_FORALL(i, ndofs,
1226  {
1227  const int offset = d_offsets[i];
1228  const int next_offset = d_offsets[i + 1];
1229  for (int c = 0; c < vd; ++c)
1230  {
1231  double dof_value = 0;
1232  for (int j = offset; j < next_offset; ++j)
1233  {
1234  int idx_j = d_indices[j];
1235  dof_value += d_x(idx_j % nface_dofs, c, idx_j / nface_dofs);
1236  }
1237  d_y(t?c:i,t?i:c) += dof_value;
1238  }
1239  });
1240 }
1241 
1243  const Vector& x, Vector& y) const
1244 {
1245  // Assumes all elements have the same number of dofs
1246  const int nface_dofs = face_dofs;
1247  const int vd = vdim;
1248  const bool t = byvdim;
1249  const int dofs = nfdofs;
1250  auto d_offsets = gather_offsets.Read();
1251  auto d_indices = gather_indices.Read();
1252  auto d_x = Reshape(x.Read(), nface_dofs, vd, 2, nf);
1253  auto d_y = Reshape(y.ReadWrite(), t?vd:ndofs, t?ndofs:vd);
1254  MFEM_FORALL(i, ndofs,
1255  {
1256  const int offset = d_offsets[i];
1257  const int next_offset = d_offsets[i + 1];
1258  for (int c = 0; c < vd; ++c)
1259  {
1260  double dof_value = 0;
1261  for (int j = offset; j < next_offset; ++j)
1262  {
1263  int idx_j = d_indices[j];
1264  bool isE1 = idx_j < dofs;
1265  idx_j = isE1 ? idx_j : idx_j - dofs;
1266  dof_value += isE1 ?
1267  d_x(idx_j % nface_dofs, c, 0, idx_j / nface_dofs)
1268  :d_x(idx_j % nface_dofs, c, 1, idx_j / nface_dofs);
1269  }
1270  d_y(t?c:i,t?i:c) += dof_value;
1271  }
1272  });
1273 }
1274 
1276  const double a) const
1277 {
1278  MFEM_VERIFY(a == 1.0, "General coefficient case is not yet supported!");
1279  if (nf==0) { return; }
1281  {
1283  }
1284  else
1285  {
1287  }
1288 }
1289 
1291  const bool keep_nbr_block) const
1292 {
1293  const int nface_dofs = face_dofs;
1294  auto d_indices1 = scatter_indices1.Read();
1295  auto d_indices2 = scatter_indices2.Read();
1296  auto I = mat.ReadWriteI();
1297  MFEM_FORALL(fdof, nf*nface_dofs,
1298  {
1299  const int iE1 = d_indices1[fdof];
1300  const int iE2 = d_indices2[fdof];
1301  AddNnz(iE1,I,nface_dofs);
1302  AddNnz(iE2,I,nface_dofs);
1303  });
1304 }
1305 
1307  SparseMatrix &mat,
1308  const bool keep_nbr_block) const
1309 {
1310  const int nface_dofs = face_dofs;
1311  auto d_indices1 = scatter_indices1.Read();
1312  auto d_indices2 = scatter_indices2.Read();
1313  auto I = mat.ReadWriteI();
1314  auto mat_fea = Reshape(fea_data.Read(), nface_dofs, nface_dofs, 2, nf);
1315  auto J = mat.WriteJ();
1316  auto Data = mat.WriteData();
1317  MFEM_FORALL(fdof, nf*nface_dofs,
1318  {
1319  const int f = fdof/nface_dofs;
1320  const int iF = fdof%nface_dofs;
1321  const int iE1 = d_indices1[f*nface_dofs+iF];
1322  const int iE2 = d_indices2[f*nface_dofs+iF];
1323  const int offset1 = AddNnz(iE1,I,nface_dofs);
1324  const int offset2 = AddNnz(iE2,I,nface_dofs);
1325  for (int jF = 0; jF < nface_dofs; jF++)
1326  {
1327  const int jE1 = d_indices1[f*nface_dofs+jF];
1328  const int jE2 = d_indices2[f*nface_dofs+jF];
1329  J[offset2+jF] = jE1;
1330  J[offset1+jF] = jE2;
1331  Data[offset2+jF] = mat_fea(jF,iF,0,f);
1332  Data[offset1+jF] = mat_fea(jF,iF,1,f);
1333  }
1334  });
1335 }
1336 
1338  Vector &ea_data) const
1339 {
1340  const int nface_dofs = face_dofs;
1341  const int nelem_dofs = elem_dofs;
1342  const int NE = ne;
1344  {
1345  auto d_indices1 = scatter_indices1.Read();
1346  auto d_indices2 = scatter_indices2.Read();
1347  auto mat_fea = Reshape(fea_data.Read(), nface_dofs, nface_dofs, 2, nf);
1348  auto mat_ea = Reshape(ea_data.ReadWrite(), nelem_dofs, nelem_dofs, ne);
1349  MFEM_FORALL(f, nf,
1350  {
1351  const int e1 = d_indices1[f*nface_dofs]/nelem_dofs;
1352  const int e2 = d_indices2[f*nface_dofs]/nelem_dofs;
1353  for (int j = 0; j < nface_dofs; j++)
1354  {
1355  const int jB1 = d_indices1[f*nface_dofs+j]%nelem_dofs;
1356  for (int i = 0; i < nface_dofs; i++)
1357  {
1358  const int iB1 = d_indices1[f*nface_dofs+i]%nelem_dofs;
1359  AtomicAdd(mat_ea(iB1,jB1,e1), mat_fea(i,j,0,f));
1360  }
1361  }
1362  if (e2 < NE)
1363  {
1364  for (int j = 0; j < nface_dofs; j++)
1365  {
1366  const int jB2 = d_indices2[f*nface_dofs+j]%nelem_dofs;
1367  for (int i = 0; i < nface_dofs; i++)
1368  {
1369  const int iB2 = d_indices2[f*nface_dofs+i]%nelem_dofs;
1370  AtomicAdd(mat_ea(iB2,jB2,e2), mat_fea(i,j,1,f));
1371  }
1372  }
1373  }
1374  });
1375  }
1376  else
1377  {
1378  auto d_indices = scatter_indices1.Read();
1379  auto mat_fea = Reshape(fea_data.Read(), nface_dofs, nface_dofs, nf);
1380  auto mat_ea = Reshape(ea_data.ReadWrite(), nelem_dofs, nelem_dofs, ne);
1381  MFEM_FORALL(f, nf,
1382  {
1383  const int e = d_indices[f*nface_dofs]/nelem_dofs;
1384  for (int j = 0; j < nface_dofs; j++)
1385  {
1386  const int jE = d_indices[f*nface_dofs+j]%nelem_dofs;
1387  for (int i = 0; i < nface_dofs; i++)
1388  {
1389  const int iE = d_indices[f*nface_dofs+i]%nelem_dofs;
1390  AtomicAdd(mat_ea(iE,jE,e), mat_fea(i,j,f));
1391  }
1392  }
1393  });
1394  }
1395 }
1396 
1398 {
1399 #ifdef MFEM_USE_MPI
1400 
1401  // If the underlying finite element space is parallel, ensure the face
1402  // neighbor information is generated.
1403  if (const ParFiniteElementSpace *pfes
1404  = dynamic_cast<const ParFiniteElementSpace*>(&fes))
1405  {
1406  pfes->GetParMesh()->ExchangeFaceNbrData();
1407  }
1408 
1409 #endif
1410 
1411 #ifdef MFEM_DEBUG
1412  // If fespace == L2
1413  const FiniteElement *fe0 = fes.GetFE(0);
1414  const TensorBasisElement *tfe = dynamic_cast<const TensorBasisElement*>(fe0);
1415  MFEM_VERIFY(tfe != NULL &&
1418  "Only Gauss-Lobatto and Bernstein basis are supported in "
1419  "L2FaceRestriction.");
1420  if (nf==0) { return; }
1421  const bool dof_reorder = (e_ordering == ElementDofOrdering::LEXICOGRAPHIC);
1422  if (!dof_reorder)
1423  {
1424  MFEM_ABORT("Non-Tensor L2FaceRestriction not yet implemented.");
1425  }
1426  if (dof_reorder && nf > 0)
1427  {
1428  for (int f = 0; f < fes.GetNF(); ++f)
1429  {
1430  const FiniteElement *fe =
1432  const TensorBasisElement* el =
1433  dynamic_cast<const TensorBasisElement*>(fe);
1434  if (el) { continue; }
1435  MFEM_ABORT("Finite element not suitable for lexicographic ordering");
1436  }
1437  }
1438 #endif
1439 }
1440 
1441 void L2FaceRestriction::ComputeScatterIndicesAndOffsets(
1442  const ElementDofOrdering ordering,
1443  const FaceType face_type)
1444 {
1445  Mesh &mesh = *fes.GetMesh();
1446  // Initialization of the offsets
1447  for (int i = 0; i <= ndofs; ++i)
1448  {
1449  gather_offsets[i] = 0;
1450  }
1451 
1452  // Computation of scatter indices and offsets
1453  int f_ind=0;
1454  for (int f = 0; f < fes.GetNF(); ++f)
1455  {
1456  Mesh::FaceInformation face = mesh.GetFaceInformation(f);
1457  MFEM_ASSERT(!face.IsShared(),
1458  "Unexpected shared face in L2FaceRestriction.");
1459  if ( face.IsOfFaceType(face_type) )
1460  {
1461  SetFaceDofsScatterIndices1(face,f_ind);
1463  {
1464  if ( face_type==FaceType::Interior && face.IsInterior() )
1465  {
1467  }
1468  else if ( face_type==FaceType::Boundary && face.IsBoundary() )
1469  {
1470  SetBoundaryDofsScatterIndices2(face,f_ind);
1471  }
1472  }
1473  f_ind++;
1474  }
1475  }
1476  MFEM_VERIFY(f_ind==nf, "Unexpected number of faces.");
1477 
1478  // Summation of the offsets
1479  for (int i = 1; i <= ndofs; ++i)
1480  {
1481  gather_offsets[i] += gather_offsets[i - 1];
1482  }
1483 }
1484 
1485 void L2FaceRestriction::ComputeGatherIndices(
1486  const ElementDofOrdering ordering,
1487  const FaceType face_type)
1488 {
1489  Mesh &mesh = *fes.GetMesh();
1490  // Computation of gather_indices
1491  int f_ind = 0;
1492  for (int f = 0; f < fes.GetNF(); ++f)
1493  {
1494  Mesh::FaceInformation face = mesh.GetFaceInformation(f);
1495  MFEM_ASSERT(!face.IsShared(),
1496  "Unexpected shared face in L2FaceRestriction.");
1497  if ( face.IsOfFaceType(face_type) )
1498  {
1499  SetFaceDofsGatherIndices1(face,f_ind);
1500  if ( m==L2FaceValues::DoubleValued &&
1501  face_type==FaceType::Interior &&
1502  face.IsLocal())
1503  {
1505  }
1506  f_ind++;
1507  }
1508  }
1509  MFEM_VERIFY(f_ind==nf, "Unexpected number of faces.");
1510 
1511  // Reset offsets to their correct value
1512  for (int i = ndofs; i > 0; --i)
1513  {
1514  gather_offsets[i] = gather_offsets[i - 1];
1515  }
1516  gather_offsets[0] = 0;
1517 }
1518 
1520  const Mesh::FaceInformation &face,
1521  const int face_index)
1522 {
1523  MFEM_ASSERT(!(face.IsNonconformingCoarse()),
1524  "This method should not be used on nonconforming coarse faces.");
1525  const Table& e2dTable = fes.GetElementToDofTable();
1526  const int* elem_map = e2dTable.GetJ();
1527  const int face_id1 = face.element[0].local_face_id;
1528  const int dim = fes.GetMesh()->Dimension();
1529  const int dof1d = fes.GetFE(0)->GetOrder()+1;
1530  const int elem_index = face.element[0].index;
1531  GetFaceDofs(dim, face_id1, dof1d, face_map); // Only for quad and hex
1532 
1533  for (int face_dof_elem1 = 0; face_dof_elem1 < face_dofs; ++face_dof_elem1)
1534  {
1535  const int volume_dof_elem1 = face_map[face_dof_elem1];
1536  const int global_dof_elem1 = elem_map[elem_index*elem_dofs + volume_dof_elem1];
1537  const int restriction_dof_elem1 = face_dofs*face_index + face_dof_elem1;
1538  scatter_indices1[restriction_dof_elem1] = global_dof_elem1;
1539  ++gather_offsets[global_dof_elem1 + 1];
1540  }
1541 }
1542 
1544  const Mesh::FaceInformation &face,
1545  const int face_index)
1546 {
1547  MFEM_ASSERT(face.IsLocal(),
1548  "This method should only be used on local faces.");
1549  const Table& e2dTable = fes.GetElementToDofTable();
1550  const int* elem_map = e2dTable.GetJ();
1551  const int elem_index = face.element[1].index;
1552  const int face_id1 = face.element[0].local_face_id;
1553  const int face_id2 = face.element[1].local_face_id;
1554  const int orientation = face.element[1].orientation;
1555  const int dim = fes.GetMesh()->Dimension();
1556  const int dof1d = fes.GetFE(0)->GetOrder()+1;
1557  GetFaceDofs(dim, face_id2, dof1d, face_map); // Only for quad and hex
1558 
1559  for (int face_dof_elem1 = 0; face_dof_elem1 < face_dofs; ++face_dof_elem1)
1560  {
1561  const int face_dof_elem2 = PermuteFaceL2(dim, face_id1, face_id2,
1562  orientation, dof1d,
1563  face_dof_elem1);
1564  const int volume_dof_elem2 = face_map[face_dof_elem2];
1565  const int global_dof_elem2 = elem_map[elem_index*elem_dofs + volume_dof_elem2];
1566  const int restriction_dof_elem2 = face_dofs*face_index + face_dof_elem1;
1567  scatter_indices2[restriction_dof_elem2] = global_dof_elem2;
1568  ++gather_offsets[global_dof_elem2 + 1];
1569  }
1570 }
1571 
1573  const Mesh::FaceInformation &face,
1574  const int face_index)
1575 {
1576 #ifdef MFEM_USE_MPI
1577  MFEM_ASSERT(face.IsShared(),
1578  "This method should only be used on shared faces.");
1579  const int elem_index = face.element[1].index;
1580  const int face_id1 = face.element[0].local_face_id;
1581  const int face_id2 = face.element[1].local_face_id;
1582  const int orientation = face.element[1].orientation;
1583  const int dim = fes.GetMesh()->Dimension();
1584  const int dof1d = fes.GetFE(0)->GetOrder()+1;
1585  GetFaceDofs(dim, face_id2, dof1d, face_map); // Only for quad and hex
1586  Array<int> face_nbr_dofs;
1587  const ParFiniteElementSpace &pfes =
1588  static_cast<const ParFiniteElementSpace&>(this->fes);
1589  pfes.GetFaceNbrElementVDofs(elem_index, face_nbr_dofs);
1590 
1591  for (int face_dof_elem1 = 0; face_dof_elem1 < face_dofs; ++face_dof_elem1)
1592  {
1593  const int face_dof_elem2 = PermuteFaceL2(dim, face_id1, face_id2,
1594  orientation, dof1d, face_dof_elem1);
1595  const int volume_dof_elem2 = face_map[face_dof_elem2];
1596  const int global_dof_elem2 = face_nbr_dofs[volume_dof_elem2];
1597  const int restriction_dof_elem2 = face_dofs*face_index + face_dof_elem1;
1598  // Trick to differentiate dof location inter/shared
1599  scatter_indices2[restriction_dof_elem2] = ndofs+global_dof_elem2;
1600  }
1601 #endif
1602 }
1603 
1605  const Mesh::FaceInformation &face,
1606  const int face_index)
1607 {
1608  MFEM_ASSERT(face.IsBoundary(),
1609  "This method should only be used on boundary faces.");
1610 
1611  for (int d = 0; d < face_dofs; ++d)
1612  {
1613  const int restriction_dof_elem2 = face_dofs*face_index + d;
1614  scatter_indices2[restriction_dof_elem2] = -1;
1615  }
1616 }
1617 
1619  const Mesh::FaceInformation &face,
1620  const int face_index)
1621 {
1622  MFEM_ASSERT(!(face.IsNonconformingCoarse()),
1623  "This method should not be used on nonconforming coarse faces.");
1624  const Table& e2dTable = fes.GetElementToDofTable();
1625  const int* elem_map = e2dTable.GetJ();
1626  const int face_id1 = face.element[0].local_face_id;
1627  const int dim = fes.GetMesh()->Dimension();
1628  const int dof1d = fes.GetFE(0)->GetOrder()+1;
1629  const int elem_index = face.element[0].index;
1630  GetFaceDofs(dim, face_id1, dof1d, face_map); // Only for quad and hex
1631 
1632  for (int face_dof_elem1 = 0; face_dof_elem1 < face_dofs; ++face_dof_elem1)
1633  {
1634  const int volume_dof_elem1 = face_map[face_dof_elem1];
1635  const int global_dof_elem1 = elem_map[elem_index*elem_dofs + volume_dof_elem1];
1636  const int restriction_dof_elem1 = face_dofs*face_index + face_dof_elem1;
1637  // We don't shift restriction_dof_elem1 to express that it's elem1 of the face
1638  gather_indices[gather_offsets[global_dof_elem1]++] = restriction_dof_elem1;
1639  }
1640 }
1641 
1643  const Mesh::FaceInformation &face,
1644  const int face_index)
1645 {
1646  MFEM_ASSERT(face.IsLocal(),
1647  "This method should only be used on local faces.");
1648  const Table& e2dTable = fes.GetElementToDofTable();
1649  const int* elem_map = e2dTable.GetJ();
1650  const int elem_index = face.element[1].index;
1651  const int face_id1 = face.element[0].local_face_id;
1652  const int face_id2 = face.element[1].local_face_id;
1653  const int orientation = face.element[1].orientation;
1654  const int dim = fes.GetMesh()->Dimension();
1655  const int dof1d = fes.GetFE(0)->GetOrder()+1;
1656  GetFaceDofs(dim, face_id2, dof1d, face_map); // Only for quad and hex
1657 
1658  for (int face_dof_elem1 = 0; face_dof_elem1 < face_dofs; ++face_dof_elem1)
1659  {
1660  const int face_dof_elem2 = PermuteFaceL2(dim, face_id1, face_id2,
1661  orientation, dof1d,
1662  face_dof_elem1);
1663  const int volume_dof_elem2 = face_map[face_dof_elem2];
1664  const int global_dof_elem2 = elem_map[elem_index*elem_dofs + volume_dof_elem2];
1665  const int restriction_dof_elem2 = face_dofs*face_index + face_dof_elem1;
1666  // We shift restriction_dof_elem2 to express that it's elem2 of the face
1667  gather_indices[gather_offsets[global_dof_elem2]++] = nfdofs +
1668  restriction_dof_elem2;
1669  }
1670 }
1671 
1673  ElementDofOrdering ordering,
1674  FaceType type)
1675  : fes(fes),
1676  ordering(ordering),
1677  interp_config(type==FaceType::Interior ? fes.GetNFbyType(type) : 0),
1678  nc_cpt(0)
1679 { }
1680 
1682  const Mesh::FaceInformation &face,
1683  int face_index)
1684 {
1685  interp_config[face_index] = InterpConfig();
1686 }
1687 
1689  const Mesh::FaceInformation &face,
1690  int face_index)
1691 {
1692  MFEM_ASSERT(!face.IsConforming(),
1693  "Registering face as nonconforming even though it is not.");
1694  const DenseMatrix* ptMat = face.point_matrix;
1695  // In the case of nonconforming slave shared face the master face is elem1.
1696  const int master_side =
1698  const int face_key = (master_side == 0 ? 1000 : 0) +
1699  face.element[0].local_face_id +
1700  6*face.element[1].local_face_id +
1701  36*face.element[1].orientation ;
1702  // Unfortunately we can't trust unicity of the ptMat to identify the transformation.
1703  Key key(ptMat, face_key);
1704  auto itr = interp_map.find(key);
1705  if ( itr == interp_map.end() )
1706  {
1707  const DenseMatrix* interpolator =
1708  GetCoarseToFineInterpolation(face,ptMat);
1709  interp_map[key] = {nc_cpt, interpolator};
1710  interp_config[face_index] = {master_side, nc_cpt};
1711  nc_cpt++;
1712  }
1713  else
1714  {
1715  interp_config[face_index] = {master_side, itr->second.first};
1716  }
1717 }
1718 
1719 const DenseMatrix* InterpolationManager::GetCoarseToFineInterpolation(
1720  const Mesh::FaceInformation &face,
1721  const DenseMatrix* ptMat)
1722 {
1724  "The following interpolation operator is only implemented for"
1725  "lexicographic ordering.");
1726  MFEM_VERIFY(!face.IsConforming(),
1727  "This method should not be called on conforming faces.")
1728  const int face_id1 = face.element[0].local_face_id;
1729  const int face_id2 = face.element[1].local_face_id;
1730 
1731  const bool is_ghost_slave =
1733  const int master_face_id = is_ghost_slave ? face_id1 : face_id2;
1734 
1735  // Computation of the interpolation matrix from master
1736  // (coarse) face to slave (fine) face.
1737  // Assumes all trace elements are the same.
1738  const FiniteElement *trace_fe =
1740  const int face_dofs = trace_fe->GetDof();
1741  const TensorBasisElement* el =
1742  dynamic_cast<const TensorBasisElement*>(trace_fe);
1743  const auto dof_map = el->GetDofMap();
1744  DenseMatrix* interpolator = new DenseMatrix(face_dofs,face_dofs);
1745  Vector shape(face_dofs);
1746 
1748  isotr.SetIdentityTransformation(trace_fe->GetGeomType());
1749  isotr.SetPointMat(*ptMat);
1750  DenseMatrix& trans_pt_mat = isotr.GetPointMat();
1751  // PointMatrix needs to be flipped in 2D
1752  if ( trace_fe->GetGeomType()==Geometry::SEGMENT && !is_ghost_slave )
1753  {
1754  std::swap(trans_pt_mat(0,0),trans_pt_mat(0,1));
1755  }
1756  DenseMatrix native_interpolator(face_dofs,face_dofs);
1757  trace_fe->GetLocalInterpolation(isotr, native_interpolator);
1758  const int dim = trace_fe->GetDim()+1;
1759  const int dof1d = trace_fe->GetOrder()+1;
1760  const int orientation = face.element[1].orientation;
1761  for (int i = 0; i < face_dofs; i++)
1762  {
1763  const int ni = (dof_map.Size()==0) ? i : dof_map[i];
1764  int li = ToLexOrdering(dim, master_face_id, dof1d, i);
1765  if ( !is_ghost_slave )
1766  {
1767  // master side is elem 2, so we permute to order dofs as elem 1.
1768  li = PermuteFaceL2(dim, face_id2, face_id1,
1769  orientation, dof1d, li);
1770  }
1771  for (int j = 0; j < face_dofs; j++)
1772  {
1773  int lj = ToLexOrdering(dim, master_face_id, dof1d, j);
1774  if ( !is_ghost_slave )
1775  {
1776  // master side is elem 2, so we permute to order dofs as elem 1.
1777  lj = PermuteFaceL2(dim, face_id2, face_id1,
1778  orientation, dof1d, lj);
1779  }
1780  const int nj = (dof_map.Size()==0) ? j : dof_map[j];
1781  (*interpolator)(li,lj) = native_interpolator(ni,nj);
1782  }
1783  }
1784  return interpolator;
1785 }
1786 
1788 {
1789  // Assumes all trace elements are the same.
1790  const FiniteElement *trace_fe =
1792  const int face_dofs = trace_fe->GetDof();
1793  const int nc_size = interp_map.size();
1794  MFEM_VERIFY(nc_cpt==nc_size, "Unexpected number of interpolators.");
1795  interpolators.SetSize(face_dofs*face_dofs*nc_size);
1796  auto d_interp = Reshape(interpolators.HostWrite(),face_dofs,face_dofs,nc_size);
1797  for (auto val : interp_map)
1798  {
1799  const int idx = val.second.first;
1800  const DenseMatrix &interpolator = *val.second.second;
1801  for (int i = 0; i < face_dofs; i++)
1802  {
1803  for (int j = 0; j < face_dofs; j++)
1804  {
1805  d_interp(i,j,idx) = interpolator(i,j);
1806  }
1807  }
1808  delete val.second.second;
1809  }
1810  interp_map.clear();
1811 }
1812 
1814 {
1815  // Count nonconforming faces
1816  int num_nc_faces = 0;
1817  for (int i = 0; i < interp_config.Size(); i++)
1818  {
1819  if ( interp_config[i].is_non_conforming )
1820  {
1821  num_nc_faces++;
1822  }
1823  }
1824  // Set nc_interp_config
1825  nc_interp_config.SetSize(num_nc_faces);
1826  int nc_index = 0;
1827  for (int i = 0; i < interp_config.Size(); i++)
1828  {
1829  auto & config = interp_config[i];
1830  if ( config.is_non_conforming )
1831  {
1832  nc_interp_config[nc_index] = NCInterpConfig(i, config);
1833  nc_index++;
1834  }
1835  }
1836 }
1837 
1839  const ElementDofOrdering ordering,
1840  const FaceType type,
1841  const L2FaceValues m,
1842  bool build)
1843  : L2FaceRestriction(fes, ordering, type, m, false),
1844  interpolations(fes, ordering, type)
1845 {
1846  if (!build) { return; }
1847  x_interp.UseDevice(true);
1848 
1849  CheckFESpace(ordering);
1850 
1851  ComputeScatterIndicesAndOffsets(ordering, type);
1852 
1853  ComputeGatherIndices(ordering, type);
1854 }
1855 
1857  const ElementDofOrdering ordering,
1858  const FaceType type,
1859  const L2FaceValues m)
1860  : NCL2FaceRestriction(fes, ordering, type, m, true)
1861 { }
1862 
1864  const Vector& x, Vector& y) const
1865 {
1868 }
1869 
1871  Vector& y) const
1872 {
1873  // Assumes all elements have the same number of dofs
1874  const int nface_dofs = face_dofs;
1875  const int vd = vdim;
1876  auto d_y = Reshape(y.ReadWrite(), nface_dofs, vd, 2, nf);
1877  auto &nc_interp_config = interpolations.GetNCFaceInterpConfig();
1878  const int num_nc_faces = nc_interp_config.Size();
1879  if ( num_nc_faces == 0 ) { return; }
1880  auto interp_config_ptr = nc_interp_config.Read();
1881  const int nc_size = interpolations.GetNumInterpolators();
1882  auto d_interp = Reshape(interpolations.GetInterpolators().Read(),
1883  nface_dofs, nface_dofs, nc_size);
1884  static constexpr int max_nd = 16*16;
1885  MFEM_VERIFY(nface_dofs<=max_nd, "Too many degrees of freedom.");
1886  MFEM_FORALL_3D(nc_face, num_nc_faces, nface_dofs, 1, 1,
1887  {
1888  MFEM_SHARED double dof_values[max_nd];
1889  const NCInterpConfig conf = interp_config_ptr[nc_face];
1890  if ( conf.is_non_conforming )
1891  {
1892  const int master_side = conf.master_side;
1893  const int interp_index = conf.index;
1894  const int face = conf.face_index;
1895  for (int c = 0; c < vd; ++c)
1896  {
1897  MFEM_FOREACH_THREAD(dof,x,nface_dofs)
1898  {
1899  dof_values[dof] = d_y(dof, c, master_side, face);
1900  }
1901  MFEM_SYNC_THREAD;
1902  MFEM_FOREACH_THREAD(dof_out,x,nface_dofs)
1903  {
1904  double res = 0.0;
1905  for (int dof_in = 0; dof_in<nface_dofs; dof_in++)
1906  {
1907  res += d_interp(dof_out, dof_in, interp_index)*dof_values[dof_in];
1908  }
1909  d_y(dof_out, c, master_side, face) = res;
1910  }
1911  MFEM_SYNC_THREAD;
1912  }
1913  }
1914  });
1915 }
1916 
1917 void NCL2FaceRestriction::Mult(const Vector& x, Vector& y) const
1918 {
1919  if (nf==0) { return; }
1920  if ( type==FaceType::Interior && m==L2FaceValues::DoubleValued )
1921  {
1922  DoubleValuedNonconformingMult(x, y);
1923  }
1924  else if ( type==FaceType::Boundary && m==L2FaceValues::DoubleValued )
1925  {
1926  DoubleValuedConformingMult(x, y);
1927  }
1928  else // Single valued (assumes no nonconforming master on elem1)
1929  {
1930  SingleValuedConformingMult(x, y);
1931  }
1932 }
1933 
1934 void NCL2FaceRestriction::SingleValuedNonconformingTransposeInterpolation(
1935  const Vector& x) const
1936 {
1937  MFEM_ASSERT(
1938  m == L2FaceValues::SingleValued,
1939  "This method should be called when m == L2FaceValues::SingleValued.");
1940  if (x_interp.Size()==0)
1941  {
1942  x_interp.SetSize(x.Size());
1943  }
1944  x_interp = x;
1945  SingleValuedNonconformingTransposeInterpolationInPlace(x_interp);
1946 }
1947 
1948 
1949 void NCL2FaceRestriction::SingleValuedNonconformingTransposeInterpolationInPlace(
1950  Vector& x) const
1951 {
1952  // Assumes all elements have the same number of dofs
1953  const int nface_dofs = face_dofs;
1954  const int vd = vdim;
1955  // Interpolation
1956  auto d_x = Reshape(x_interp.ReadWrite(), nface_dofs, vd, nf);
1957  auto &nc_interp_config = interpolations.GetNCFaceInterpConfig();
1958  const int num_nc_faces = nc_interp_config.Size();
1959  if ( num_nc_faces == 0 ) { return; }
1960  auto interp_config_ptr = nc_interp_config.Read();
1961  auto interpolators = interpolations.GetInterpolators().Read();
1962  const int nc_size = interpolations.GetNumInterpolators();
1963  auto d_interp = Reshape(interpolators, nface_dofs, nface_dofs, nc_size);
1964  static constexpr int max_nd = 16*16;
1965  MFEM_VERIFY(nface_dofs<=max_nd, "Too many degrees of freedom.");
1966  MFEM_FORALL_3D(nc_face, num_nc_faces, nface_dofs, 1, 1,
1967  {
1968  MFEM_SHARED double dof_values[max_nd];
1969  const NCInterpConfig conf = interp_config_ptr[nc_face];
1970  const int master_side = conf.master_side;
1971  const int interp_index = conf.index;
1972  const int face = conf.face_index;
1973  if ( conf.is_non_conforming && master_side==0 )
1974  {
1975  // Interpolation from fine to coarse
1976  for (int c = 0; c < vd; ++c)
1977  {
1978  MFEM_FOREACH_THREAD(dof,x,nface_dofs)
1979  {
1980  dof_values[dof] = d_x(dof, c, face);
1981  }
1982  MFEM_SYNC_THREAD;
1983  MFEM_FOREACH_THREAD(dof_out,x,nface_dofs)
1984  {
1985  double res = 0.0;
1986  for (int dof_in = 0; dof_in<nface_dofs; dof_in++)
1987  {
1988  res += d_interp(dof_in, dof_out, interp_index)*dof_values[dof_in];
1989  }
1990  d_x(dof_out, c, face) = res;
1991  }
1992  MFEM_SYNC_THREAD;
1993  }
1994  }
1995  });
1996 }
1997 
1998 void NCL2FaceRestriction::DoubleValuedNonconformingTransposeInterpolation(
1999  const Vector& x) const
2000 {
2001  MFEM_ASSERT(
2002  m == L2FaceValues::DoubleValued,
2003  "This method should be called when m == L2FaceValues::DoubleValued.");
2004  if (x_interp.Size()==0)
2005  {
2006  x_interp.SetSize(x.Size());
2007  }
2008  x_interp = x;
2009  DoubleValuedNonconformingTransposeInterpolationInPlace(x_interp);
2010 }
2011 
2012 void NCL2FaceRestriction::DoubleValuedNonconformingTransposeInterpolationInPlace(
2013  Vector& x) const
2014 {
2015  // Assumes all elements have the same number of dofs
2016  const int nface_dofs = face_dofs;
2017  const int vd = vdim;
2018  // Interpolation
2019  auto d_x = Reshape(x.ReadWrite(), nface_dofs, vd, 2, nf);
2020  auto &nc_interp_config = interpolations.GetNCFaceInterpConfig();
2021  const int num_nc_faces = nc_interp_config.Size();
2022  if ( num_nc_faces == 0 ) { return; }
2023  auto interp_config_ptr = nc_interp_config.Read();
2024  auto interpolators = interpolations.GetInterpolators().Read();
2025  const int nc_size = interpolations.GetNumInterpolators();
2026  auto d_interp = Reshape(interpolators, nface_dofs, nface_dofs, nc_size);
2027  static constexpr int max_nd = 16*16;
2028  MFEM_VERIFY(nface_dofs<=max_nd, "Too many degrees of freedom.");
2029  MFEM_FORALL_3D(nc_face, num_nc_faces, nface_dofs, 1, 1,
2030  {
2031  MFEM_SHARED double dof_values[max_nd];
2032  const NCInterpConfig conf = interp_config_ptr[nc_face];
2033  const int master_side = conf.master_side;
2034  const int interp_index = conf.index;
2035  const int face = conf.face_index;
2036  if ( conf.is_non_conforming )
2037  {
2038  // Interpolation from fine to coarse
2039  for (int c = 0; c < vd; ++c)
2040  {
2041  MFEM_FOREACH_THREAD(dof,x,nface_dofs)
2042  {
2043  dof_values[dof] = d_x(dof, c, master_side, face);
2044  }
2045  MFEM_SYNC_THREAD;
2046  MFEM_FOREACH_THREAD(dof_out,x,nface_dofs)
2047  {
2048  double res = 0.0;
2049  for (int dof_in = 0; dof_in<nface_dofs; dof_in++)
2050  {
2051  res += d_interp(dof_in, dof_out, interp_index)*dof_values[dof_in];
2052  }
2053  d_x(dof_out, c, master_side, face) = res;
2054  }
2055  MFEM_SYNC_THREAD;
2056  }
2057  }
2058  });
2059 }
2060 
2061 void NCL2FaceRestriction::AddMultTranspose(const Vector& x, Vector& y,
2062  const double a) const
2063 {
2064  MFEM_VERIFY(a == 1.0, "General coefficient case is not yet supported!");
2065  if (nf==0) { return; }
2066  if (type==FaceType::Interior)
2067  {
2068  if ( m==L2FaceValues::DoubleValued )
2069  {
2070  DoubleValuedNonconformingTransposeInterpolation(x);
2071  DoubleValuedConformingAddMultTranspose(x_interp, y);
2072  }
2073  else if ( m==L2FaceValues::SingleValued )
2074  {
2075  SingleValuedNonconformingTransposeInterpolation(x);
2076  SingleValuedConformingAddMultTranspose(x_interp, y);
2077  }
2078  }
2079  else
2080  {
2081  if ( m==L2FaceValues::DoubleValued )
2082  {
2083  DoubleValuedConformingAddMultTranspose(x, y);
2084  }
2085  else if ( m==L2FaceValues::SingleValued )
2086  {
2087  SingleValuedConformingAddMultTranspose(x, y);
2088  }
2089  }
2090 }
2091 
2092 void NCL2FaceRestriction::AddMultTransposeInPlace(Vector& x, Vector& y) const
2093 {
2094  if (nf==0) { return; }
2095  if (type==FaceType::Interior)
2096  {
2097  if ( m==L2FaceValues::DoubleValued )
2098  {
2099  DoubleValuedNonconformingTransposeInterpolationInPlace(x);
2100  DoubleValuedConformingAddMultTranspose(x, y);
2101  }
2102  else if ( m==L2FaceValues::SingleValued )
2103  {
2104  SingleValuedNonconformingTransposeInterpolationInPlace(x);
2105  SingleValuedConformingAddMultTranspose(x, y);
2106  }
2107  }
2108  else
2109  {
2110  if ( m==L2FaceValues::DoubleValued )
2111  {
2112  DoubleValuedConformingAddMultTranspose(x, y);
2113  }
2114  else if ( m==L2FaceValues::SingleValued )
2115  {
2116  SingleValuedConformingAddMultTranspose(x, y);
2117  }
2118  }
2119 }
2120 
2121 void NCL2FaceRestriction::FillI(SparseMatrix &mat,
2122  const bool keep_nbr_block) const
2123 {
2124  MFEM_ABORT("Not yet implemented.");
2125 }
2126 
2127 void NCL2FaceRestriction::FillJAndData(const Vector &ea_data,
2128  SparseMatrix &mat,
2129  const bool keep_nbr_block) const
2130 {
2131  MFEM_ABORT("Not yet implemented.");
2132 }
2133 
2134 void NCL2FaceRestriction::AddFaceMatricesToElementMatrices(
2135  const Vector &fea_data,
2136  Vector &ea_data)
2137 const
2138 {
2139  MFEM_ABORT("Not yet implemented.");
2140 }
2141 
2142 int ToLexOrdering(const int dim, const int face_id, const int size1d,
2143  const int index)
2144 {
2145  switch (dim)
2146  {
2147  case 1:
2148  return 0;
2149  case 2:
2150  return ToLexOrdering2D(face_id, size1d, index);
2151  case 3:
2152  return ToLexOrdering3D(face_id, size1d, index%size1d, index/size1d);
2153  default:
2154  MFEM_ABORT("Unsupported dimension.");
2155  return 0;
2156  }
2157 }
2158 
2159 void NCL2FaceRestriction::ComputeScatterIndicesAndOffsets(
2160  const ElementDofOrdering ordering,
2161  const FaceType type)
2162 {
2163  Mesh &mesh = *fes.GetMesh();
2164 
2165  // Initialization of the offsets
2166  for (int i = 0; i <= ndofs; ++i)
2167  {
2168  gather_offsets[i] = 0;
2169  }
2170 
2171  // Computation of scatter and offsets indices
2172  int f_ind=0;
2173  for (int f = 0; f < fes.GetNF(); ++f)
2174  {
2175  Mesh::FaceInformation face = mesh.GetFaceInformation(f);
2176  if ( face.IsNonconformingCoarse() )
2177  {
2178  // We skip nonconforming coarse faces as they are treated
2179  // by the corresponding nonconforming fine faces.
2180  continue;
2181  }
2182  else if ( type==FaceType::Interior && face.IsInterior() )
2183  {
2184  SetFaceDofsScatterIndices1(face,f_ind);
2185  if ( m==L2FaceValues::DoubleValued )
2186  {
2187  PermuteAndSetFaceDofsScatterIndices2(face,f_ind);
2188  if ( face.IsConforming() )
2189  {
2190  interpolations.RegisterFaceConformingInterpolation(face,f_ind);
2191  }
2192  else // Non-conforming face
2193  {
2194  interpolations.RegisterFaceCoarseToFineInterpolation(face,f_ind);
2195  }
2196  }
2197  f_ind++;
2198  }
2199  else if ( type==FaceType::Boundary && face.IsBoundary() )
2200  {
2201  SetFaceDofsScatterIndices1(face,f_ind);
2202  if ( m==L2FaceValues::DoubleValued )
2203  {
2204  SetBoundaryDofsScatterIndices2(face,f_ind);
2205  }
2206  f_ind++;
2207  }
2208  }
2209  MFEM_VERIFY(f_ind==nf, "Unexpected number of " <<
2210  (type==FaceType::Interior? "interior" : "boundary") <<
2211  " faces: " << f_ind << " vs " << nf );
2212 
2213  // Summation of the offsets
2214  for (int i = 1; i <= ndofs; ++i)
2215  {
2216  gather_offsets[i] += gather_offsets[i - 1];
2217  }
2218 
2219  // Transform the interpolation matrix map into a contiguous memory structure.
2220  interpolations.LinearizeInterpolatorMapIntoVector();
2221  interpolations.InitializeNCInterpConfig();
2222 }
2223 
2224 void NCL2FaceRestriction::ComputeGatherIndices(
2225  const ElementDofOrdering ordering,
2226  const FaceType type)
2227 {
2228  Mesh &mesh = *fes.GetMesh();
2229  // Computation of gather_indices
2230  int f_ind = 0;
2231  for (int f = 0; f < fes.GetNF(); ++f)
2232  {
2233  Mesh::FaceInformation face = mesh.GetFaceInformation(f);
2234  MFEM_ASSERT(!face.IsShared(),
2235  "Unexpected shared face in NCL2FaceRestriction.");
2236  if ( face.IsNonconformingCoarse() )
2237  {
2238  // We skip nonconforming coarse faces as they are treated
2239  // by the corresponding nonconforming fine faces.
2240  continue;
2241  }
2242  else if ( face.IsOfFaceType(type) )
2243  {
2244  SetFaceDofsGatherIndices1(face,f_ind);
2245  if ( m==L2FaceValues::DoubleValued &&
2246  type==FaceType::Interior &&
2247  face.IsInterior() )
2248  {
2249  PermuteAndSetFaceDofsGatherIndices2(face,f_ind);
2250  }
2251  f_ind++;
2252  }
2253  }
2254  MFEM_VERIFY(f_ind==nf, "Unexpected number of " <<
2255  (type==FaceType::Interior? "interior" : "boundary") <<
2256  " faces: " << f_ind << " vs " << nf );
2257 
2258  // Switch back offsets to their correct value
2259  for (int i = ndofs; i > 0; --i)
2260  {
2261  gather_offsets[i] = gather_offsets[i - 1];
2262  }
2263  gather_offsets[0] = 0;
2264 }
2265 
2266 } // namespace mfem
const T * Read(bool on_dev=true) const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), on_dev).
Definition: array.hpp:307
void FillI(SparseMatrix &mat) const
Abstract class for all finite elements.
Definition: fe_base.hpp:232
T * HostReadWrite()
Shortcut for mfem::ReadWrite(a.GetMemory(), a.Size(), false).
Definition: array.hpp:327
Array< NCInterpConfig > nc_interp_config
const Array< NCInterpConfig > & GetNCFaceInterpConfig() const
Return an array containing the interpolation configuration for each face registered with RegisterFace...
void Mult(const Vector &x, Vector &y) const override
Scatter the degrees of freedom, i.e. goes from L-Vector to face E-Vector.
int * GetJ()
Definition: table.hpp:114
H1FaceRestriction(const FiniteElementSpace &fes, const ElementDofOrdering ordering, const FaceType type, bool build)
Construct an H1FaceRestriction.
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...
Operator that extracts Face degrees of freedom for H1 FiniteElementSpaces.
void FillJAndData(const Vector &ea_data, SparseMatrix &mat) const
void AddMultTranspose(const Vector &x, Vector &y, const double a=1.0) const override
Add the E-vector degrees of freedom x to the L-vector degrees of freedom y.
int Dimension() const
Definition: mesh.hpp:1047
void SetSize(int s)
Resize the vector to size s.
Definition: vector.hpp:512
Array< int > gather_indices
virtual void UseDevice(bool use_dev) const
Enable execution of Vector operations using the mfem::Device.
Definition: vector.hpp:117
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 Mult(const Table &A, const Table &B, Table &C)
C = A * B (as boolean matrices)
Definition: table.cpp:475
std::pair< const DenseMatrix *, int > Key
Geometry::Type GetFaceGeometry(int i) const
Return the Geometry::Type associated with face i.
Definition: mesh.cpp:1413
MFEM_HOST_DEVICE T AtomicAdd(T &add, const T val)
Definition: backends.hpp:84
T * GetData()
Returns the data.
Definition: array.hpp:115
int Size() const
Returns the size of the vector.
Definition: vector.hpp:199
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...
Data type dense matrix using column-major storage.
Definition: densemat.hpp:23
virtual double * HostWrite()
Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), false).
Definition: vector.hpp:460
Operator that extracts face degrees of freedom for L2 nonconforming spaces.
virtual const double * Read(bool on_dev=true) const
Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), on_dev).
Definition: vector.hpp:448
const FiniteElement * GetTraceElement(int i, Geometry::Type geom_type) const
Return the trace element from element &#39;i&#39; to the given &#39;geom_type&#39;.
Definition: fespace.cpp:3175
Abstract parallel finite element space.
Definition: pfespace.hpp:28
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...
void SetFaceDofsGatherIndices1(const Mesh::FaceInformation &face, const int face_index)
Set the gathering indices of elem1 for the interior face described by the face.
Memory< double > & GetMemoryData()
Definition: sparsemat.hpp:269
const L2FaceValues m
Memory< int > & GetMemoryI()
Definition: sparsemat.hpp:237
Array< int > gather_offsets
virtual const FiniteElement * GetFE(int i) const
Returns pointer to the FiniteElement in the FiniteElementCollection associated with i&#39;th element in t...
Definition: fespace.cpp:2783
void AddMultTranspose(const Vector &x, Vector &y, const double a=1.0) const override
Add the E-vector degrees of freedom x to the L-vector degrees of freedom y.
Array< int > gather_indices
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...
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:1188
const FiniteElement * GetFaceElement(int i) const
Returns pointer to the FiniteElement in the FiniteElementCollection associated with i&#39;th face in the ...
Definition: fespace.cpp:3135
L2ElementRestriction(const FiniteElementSpace &)
Memory< int > & GetMemoryJ()
Definition: sparsemat.hpp:253
void DoubleValuedNonconformingInterpolation(Vector &x) const
Apply a change of basis from coarse element basis to fine element basis for the coarse face dofs...
Array< InterpConfig > interp_config
DofTransformation * GetFaceNbrElementVDofs(int i, Array< int > &vdofs) const
Definition: pfespace.cpp:1464
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...
bool IsLocal() const
Return true if the face is a local interior face which is NOT a master nonconforming face...
Definition: mesh.hpp:1436
void SetPointMat(const DenseMatrix &pm)
Set the underlying point matrix describing the transformation.
Definition: eltrans.hpp:401
virtual void AddFaceMatricesToElementMatrices(const Vector &fea_data, Vector &ea_data) const
This methods adds the DG face matrices to the element matrices.
FaceType
Definition: mesh.hpp:45
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...
const FiniteElementSpace & fes
double f(const Vector &xvec)
Definition: lor_mms.hpp:32
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 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 ...
Data type sparse matrix.
Definition: sparsemat.hpp:50
void Mult(const Vector &x, Vector &y) const override
Operator application: y=A(x).
Operator that extracts Face degrees of freedom for L2 spaces.
virtual double * Write(bool on_dev=true)
Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), on_dev).
Definition: vector.hpp:456
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 CheckFESpace(const ElementDofOrdering ordering)
Verify that H1FaceRestriction is build from an H1 FESpace.
const FiniteElementSpace & fes
void LinearizeInterpolatorMapIntoVector()
Transform the interpolation matrix map into a contiguous memory structure.
int * ReadWriteI(bool on_dev=true)
Definition: sparsemat.hpp:243
Bernstein polynomials.
Definition: fe_base.hpp:32
void MultLeftInverse(const Vector &x, Vector &y) const
void MultTransposeUnsigned(const Vector &x, Vector &y) const
Compute MultTranspose without applying signs based on DOF orientations.
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...
int * WriteI(bool on_dev=true)
Definition: sparsemat.hpp:241
ElementRestriction(const FiniteElementSpace &, ElementDofOrdering)
Definition: restriction.cpp:27
void GetFaceDofs(const int dim, const int face_id, const int dof1d, Array< int > &face_map)
Return the face map that extracts the degrees of freedom for the requested local face of a quad or he...
struct mfem::Mesh::FaceInformation::@13 element[2]
const T * HostRead() const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), false).
Definition: array.hpp:311
bool IsConforming() const
Return true if the face is a conforming face.
Definition: mesh.hpp:1479
This structure is used as a human readable output format that decipheres the information contained in...
Definition: mesh.hpp:1417
const ElementDofOrdering ordering
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...
const FiniteElementSpace & fes
Definition: restriction.hpp:45
int GetNF() const
Returns number of faces (i.e. co-dimension 1 entities) in the mesh.
Definition: fespace.hpp:620
void SetFaceDofsGatherIndices(const Mesh::FaceInformation &face, const int face_index, const ElementDofOrdering ordering)
Set the gathering indices of elem1 for the interior face described by the face.
Array< int > gather_offsets
Mesh * GetMesh() const
Returns the mesh.
Definition: fespace.hpp:441
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 SetIdentityTransformation(Geometry::Type GeomType)
Set the FiniteElement Geometry for the reference elements being used.
Definition: eltrans.cpp:383
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
Definition: fespace.hpp:96
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 FillJAndData(const Vector &ea_data, SparseMatrix &mat) const
NCL2FaceRestriction(const FiniteElementSpace &fes, const ElementDofOrdering ordering, const FaceType type, const L2FaceValues m, bool build)
Constructs an NCL2FaceRestriction, this is a specialization of a L2FaceRestriction for nonconforming ...
void MultUnsigned(const Vector &x, Vector &y) const
Compute Mult without applying signs based on DOF orientations.
int GetDof() const
Returns the number of degrees of freedom in the finite element.
Definition: fe_base.hpp:322
int Height() const
Get the height (size of output) of the Operator. Synonym with NumRows().
Definition: operator.hpp:66
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...
void FillSparseMatrix(const Vector &mat_ea, SparseMatrix &mat) const
Fill a Sparse Matrix with Element Matrices.
The ordering method used when the number of unknowns per mesh node (vector dimension) is bigger than ...
Definition: fespace.hpp:29
int * WriteJ(bool on_dev=true)
Definition: sparsemat.hpp:257
int height
Dimension of the output / number of rows in the matrix.
Definition: operator.hpp:27
Array< int > scatter_indices1
void CheckFESpace(const ElementDofOrdering ordering)
Verify that L2FaceRestriction is build from an L2 FESpace.
double a
Definition: lissajous.cpp:41
L2FaceRestriction(const FiniteElementSpace &fes, const ElementDofOrdering ordering, const FaceType type, const L2FaceValues m, bool build)
Constructs an L2FaceRestriction.
virtual double * ReadWrite(bool on_dev=true)
Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), on_dev).
Definition: vector.hpp:464
A standard isoparametric element transformation.
Definition: eltrans.hpp:361
void New(int size)
Allocate host memory for size entries with the current host memory type returned by MemoryManager::Ge...
Mesh * GetMesh(int type)
Definition: ex29.cpp:218
ElementDofOrdering
Constants describing the possible orderings of the DOFs in one element.
Definition: fespace.hpp:74
int dim
Definition: ex24.cpp:53
int GetNumInterpolators() const
Return the total number of interpolators.
int FillI(SparseMatrix &mat) const
int index(int i, int j, int nx, int ny)
Definition: life.cpp:235
void Mult(const Vector &x, Vector &y) const override
Operator application: y=A(x).
const FiniteElementSpace & fes
int GetVSize() const
Return the number of vector dofs, i.e. GetNDofs() x GetVDim().
Definition: fespace.hpp:587
Lexicographic ordering for tensor-product FiniteElements.
bool IsBoundary() const
Return true if the face is a boundary face.
Definition: mesh.hpp:1459
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...
void SetFaceDofsScatterIndices(const Mesh::FaceInformation &face, const int face_index, const ElementDofOrdering ordering)
Set the scattering indices of elem1, and increment the offsets for the face described by the face...
int Size() const
Return the logical size of the array.
Definition: array.hpp:141
RefCoord t[3]
FaceInformation GetFaceInformation(int f) const
Definition: mesh.cpp:1131
ElementConformity conformity
Definition: mesh.hpp:1424
void AddMultTranspose(const Vector &x, Vector &y, const double a=1.0) const override
Gather the degrees of freedom, i.e. goes from face E-Vector to L-Vector.
Vector data type.
Definition: vector.hpp:60
bool IsShared() const
Return true if the face is a shared interior face which is NOT a master nonconforming face...
Definition: mesh.hpp:1443
const DenseMatrix & GetPointMat() const
Return the stored point matrix.
Definition: eltrans.hpp:404
bool IsNonconformingCoarse() const
Return true if the face is a nonconforming coarse face.
Definition: mesh.hpp:1496
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.
void AddMultTranspose(const Vector &x, Vector &y, const double a=1.0) const override
Gather the degrees of freedom, i.e. goes from face E-Vector to L-Vector.
int * HostReadWriteI()
Definition: sparsemat.hpp:249
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 RegisterFaceCoarseToFineInterpolation(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...
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:131
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:750
const Vector & GetInterpolators() const
Return an mfem::Vector containing the interpolators in the following format: face_dofs x face_dofs x ...
int GetOrder() const
Returns the order of the finite element. In the case of anisotropic orders, returns the maximum order...
Definition: fe_base.hpp:326
Array< int > scatter_indices
InterpolationManager interpolations
int width
Dimension of the input / number of columns in the matrix.
Definition: operator.hpp:28
MemoryType GetMemoryType() const
Return a MemoryType that is currently valid. If both the host and the device pointers are currently v...
Array< int > scatter_indices2
const DenseMatrix * point_matrix
Definition: mesh.hpp:1432
int GetBasisType() const
Definition: fe_base.hpp:1180
VDIM x NQPT x NE (values) / VDIM x DIM x NQPT x NE (grads)
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...
double f(const Vector &p)
double * WriteData(bool on_dev=true)
Definition: sparsemat.hpp:273