MFEM v4.10.0
Finite element discretization library
Loading...
Searching...
No Matches
prestriction.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 "../config/config.hpp"
13
14#ifdef MFEM_USE_MPI
15
16#include "restriction.hpp"
17#include "prestriction.hpp"
18#include "pgridfunc.hpp"
19#include "pfespace.hpp"
20#include "fespace.hpp"
21#include "fe/face_map_utils.hpp"
22#include "../general/forall.hpp"
23
24namespace mfem
25{
26
28 ElementDofOrdering f_ordering,
29 FaceType type)
30 : H1FaceRestriction(fes, f_ordering, type, false),
31 type(type),
32 interpolations(fes, f_ordering, type)
33{
34 if (nf==0) { return; }
35 x_interp.UseDevice(true);
36
37 // Check that the space is H1 (not currently implemented for ND or RT spaces)
38 const bool is_h1 = dynamic_cast<const H1_FECollection*>(fes.FEColl());
39 MFEM_VERIFY(is_h1, "ParNCH1FaceRestriction is only implemented for H1 spaces.")
40
41 CheckFESpace(f_ordering);
42
43 ComputeScatterIndicesAndOffsets(f_ordering, type);
44
45 ComputeGatherIndices(f_ordering, type);
46}
47
53
55{
56 // Assumes all elements have the same number of dofs
57 const int nface_dofs = face_dofs;
58 const int vd = vdim;
59 auto d_y = Reshape(y.ReadWrite(), nface_dofs, vd, nf);
60 auto &nc_interp_config = interpolations.GetNCFaceInterpConfig();
61 const int num_nc_faces = nc_interp_config.Size();
62 if ( num_nc_faces == 0 ) { return; }
63 auto interp_config_ptr = nc_interp_config.Read();
64 const int nc_size = interpolations.GetNumInterpolators();
65 auto d_interp = Reshape(interpolations.GetInterpolators().Read(),
66 nface_dofs, nface_dofs, nc_size);
67 static constexpr int max_nd = 16*16;
68 MFEM_VERIFY(nface_dofs<=max_nd, "Too many degrees of freedom.");
69 mfem::forall_2D(num_nc_faces, nface_dofs, 1, [=] MFEM_HOST_DEVICE (int nc_face)
70 {
71 MFEM_SHARED real_t dof_values[max_nd];
72 const NCInterpConfig conf = interp_config_ptr[nc_face];
73 if ( conf.is_non_conforming && conf.master_side == 0 )
74 {
75 const int interp_index = conf.index;
76 const int face = conf.face_index;
77 for (int c = 0; c < vd; ++c)
78 {
79 MFEM_FOREACH_THREAD(dof,x,nface_dofs)
80 {
81 dof_values[dof] = d_y(dof, c, face);
82 }
83 MFEM_SYNC_THREAD;
84 MFEM_FOREACH_THREAD(dof_out,x,nface_dofs)
85 {
86 real_t res = 0.0;
87 for (int dof_in = 0; dof_in<nface_dofs; dof_in++)
88 {
89 res += d_interp(dof_out, dof_in, interp_index)*dof_values[dof_in];
90 }
91 d_y(dof_out, c, face) = res;
92 }
93 MFEM_SYNC_THREAD;
94 }
95 }
96 });
97}
98
99void ParNCH1FaceRestriction::AddMultTranspose(const Vector &x, Vector &y,
100 const real_t a) const
101{
102 MFEM_VERIFY(a == 1.0, "General coefficient case is not yet supported!");
103 if (nf==0) { return; }
104 NonconformingTransposeInterpolation(x);
105 H1FaceRestriction::AddMultTranspose(x_interp, y);
106}
107
108void ParNCH1FaceRestriction::AddMultTransposeInPlace(Vector &x, Vector &y) const
109{
110 if (nf==0) { return; }
111 NonconformingTransposeInterpolationInPlace(x);
112 H1FaceRestriction::AddMultTranspose(x, y);
113}
114
115void ParNCH1FaceRestriction::NonconformingTransposeInterpolation(
116 const Vector& x) const
117{
118 if (x_interp.Size()==0)
119 {
120 x_interp.SetSize(x.Size());
121 }
122 x_interp = x;
123 NonconformingTransposeInterpolationInPlace(x_interp);
124}
125
126void ParNCH1FaceRestriction::NonconformingTransposeInterpolationInPlace(
127 Vector& x) const
128{
129 // Assumes all elements have the same number of dofs
130 const int nface_dofs = face_dofs;
131 const int vd = vdim;
132 if ( type==FaceType::Interior )
133 {
134 // Interpolation from slave to master face dofs
135 auto d_x = Reshape(x.ReadWrite(), nface_dofs, vd, nf);
136 auto &nc_interp_config = interpolations.GetNCFaceInterpConfig();
137 const int num_nc_faces = nc_interp_config.Size();
138 if ( num_nc_faces == 0 ) { return; }
139 auto interp_config_ptr = nc_interp_config.Read();
140 const int nc_size = interpolations.GetNumInterpolators();
141 auto d_interp = Reshape(interpolations.GetInterpolators().Read(),
142 nface_dofs, nface_dofs, nc_size);
143 static constexpr int max_nd = 1024;
144 MFEM_VERIFY(nface_dofs<=max_nd, "Too many degrees of freedom.");
145 mfem::forall_2D(num_nc_faces, nface_dofs, 1,
146 [=] MFEM_HOST_DEVICE (int nc_face)
147 {
148 MFEM_SHARED real_t dof_values[max_nd];
149 const NCInterpConfig conf = interp_config_ptr[nc_face];
150 const int master_side = conf.master_side;
151 if ( conf.is_non_conforming && master_side==0 )
152 {
153 const int interp_index = conf.index;
154 const int face = conf.face_index;
155 // Interpolation from fine to coarse
156 for (int c = 0; c < vd; ++c)
157 {
158 MFEM_FOREACH_THREAD(dof,x,nface_dofs)
159 {
160 dof_values[dof] = d_x(dof, c, face);
161 }
162 MFEM_SYNC_THREAD;
163 MFEM_FOREACH_THREAD(dof_out,x,nface_dofs)
164 {
165 real_t res = 0.0;
166 for (int dof_in = 0; dof_in<nface_dofs; dof_in++)
167 {
168 res += d_interp(dof_in, dof_out, interp_index)*dof_values[dof_in];
169 }
170 d_x(dof_out, c, face) = res;
171 }
172 MFEM_SYNC_THREAD;
173 }
174 }
175 });
176 }
177}
178
179void ParNCH1FaceRestriction::ComputeScatterIndicesAndOffsets(
180 const ElementDofOrdering f_ordering,
181 const FaceType face_type)
182{
183 Mesh &mesh = *fes.GetMesh();
184
185 // Initialization of the offsets
186 for (int i = 0; i <= ndofs; ++i)
187 {
188 gather_offsets[i] = 0;
189 }
190
191 // Computation of scatter indices and offsets
192 int f_ind = 0;
193 for (int f = 0; f < mesh.GetNumFacesWithGhost(); ++f)
194 {
195 Mesh::FaceInformation face = mesh.GetFaceInformation(f);
196 if ( face.IsNonconformingCoarse() )
197 {
198 // We skip nonconforming coarse faces as they are treated
199 // by the corresponding nonconforming fine faces.
200 continue;
201 }
202 else if (face_type==FaceType::Interior && face.IsInterior())
203 {
204 if ( face.IsConforming() )
205 {
206 interpolations.RegisterFaceConformingInterpolation(face,f_ind);
207 SetFaceDofsScatterIndices(face, f_ind, f_ordering);
208 f_ind++;
209 }
210 else // Non-conforming face
211 {
212 SetFaceDofsScatterIndices(face, f_ind, f_ordering);
213 if ( face.element[0].conformity==Mesh::ElementConformity::Superset )
214 {
215 // In this case the local face is the master (coarse) face, thus
216 // we need to interpolate the values on the slave (fine) face.
217 interpolations.RegisterFaceCoarseToFineInterpolation(face,f_ind);
218 }
219 else
220 {
221 // Treated as a conforming face since we only extract values from
222 // the local slave (fine) face.
223 interpolations.RegisterFaceConformingInterpolation(face,f_ind);
224 }
225 f_ind++;
226 }
227 }
228 else if (face_type==FaceType::Boundary && face.IsBoundary())
229 {
230 interpolations.RegisterFaceConformingInterpolation(face,f_ind);
231 SetFaceDofsScatterIndices(face, f_ind, f_ordering);
232 f_ind++;
233 }
234 }
235 MFEM_VERIFY(f_ind==nf, "Unexpected number of faces.");
236
237 // Summation of the offsets
238 for (int i = 1; i <= ndofs; ++i)
239 {
240 gather_offsets[i] += gather_offsets[i - 1];
241 }
242
243 // Transform the interpolation matrix map into a contiguous memory structure.
244 interpolations.LinearizeInterpolatorMapIntoVector();
245 interpolations.InitializeNCInterpConfig();
246}
247
248void ParNCH1FaceRestriction::ComputeGatherIndices(
249 const ElementDofOrdering f_ordering,
250 const FaceType face_type)
251{
252 Mesh &mesh = *fes.GetMesh();
253
254 // Computation of gather_indices
255 int f_ind = 0;
256 for (int f = 0; f < mesh.GetNumFacesWithGhost(); ++f)
257 {
258 Mesh::FaceInformation face = mesh.GetFaceInformation(f);
259 if ( face.IsNonconformingCoarse() )
260 {
261 // We skip nonconforming coarse faces as they are treated
262 // by the corresponding nonconforming fine faces.
263 continue;
264 }
265 else if (face.IsOfFaceType(face_type))
266 {
267 SetFaceDofsGatherIndices(face, f_ind, f_ordering);
268 f_ind++;
269 }
270 }
271 MFEM_VERIFY(f_ind==nf, "Unexpected number of faces.");
272
273 // Reset offsets to their correct value
274 for (int i = ndofs; i > 0; --i)
275 {
276 gather_offsets[i] = gather_offsets[i - 1];
277 }
278 gather_offsets[0] = 0;
279}
280
281ParL2FaceRestriction::ParL2FaceRestriction(const ParFiniteElementSpace &pfes_,
282 ElementDofOrdering f_ordering,
283 FaceType type,
284 L2FaceValues m,
285 bool build)
286 : L2FaceRestriction(pfes_, f_ordering, type, m, false),
287 pfes(pfes_)
288{
289 if (!build) { return; }
290 if (nf==0) { return; }
291
292 CheckFESpace();
293
294 ComputeScatterIndicesAndOffsets();
295
296 ComputeGatherIndices();
297}
298
300 ElementDofOrdering f_ordering,
301 FaceType type,
302 L2FaceValues m)
303 : ParL2FaceRestriction(fes, f_ordering, type, m, true)
304{ }
305
307 const Vector& x, Vector& y) const
308{
309 MFEM_ASSERT(
311 "This method should be called when m == L2FaceValues::DoubleValued.");
312
313 Vector face_nbr_data = GetLVectorFaceNbrData(fes, x, type);
314
315 // Early return only after calling ParGridFunction::ExchangeFaceNbrData,
316 // otherwise MPI communication can hang.
317 if (nf == 0) { return; }
318
319 // Assumes all elements have the same number of dofs
320 const int nface_dofs = face_dofs;
321 const int vd = vdim;
322 const bool t = byvdim;
323 const int threshold = ndofs;
324 const int nsdofs = pfes.GetFaceNbrVSize() / vd;
325 auto d_indices1 = scatter_indices1.Read();
326 auto d_indices2 = scatter_indices2.Read();
327 auto d_x = Reshape(x.Read(), t?vd:ndofs, t?ndofs:vd);
328 const int ne_shared = nsdofs / elem_dofs;
329 const int nedof = elem_dofs;
330 // Note: the shape of face_nbr_data, as determined by
331 // ParFiniteElementSpace::ExchangeFaceNbrData, is (elem_dofs, vdim,
332 // ne_shared), independent of the ordering (byNODES or byVDIM) of the finite
333 // element space.
334 auto d_x_shared = Reshape(face_nbr_data.Read(), elem_dofs, vd, ne_shared);
335 auto d_y = Reshape(y.Write(), nface_dofs, vd, 2, nf);
336 mfem::forall(nfdofs, [=] MFEM_HOST_DEVICE (int i)
337 {
338 const int dof = i % nface_dofs;
339 const int face = i / nface_dofs;
340 const int idx1 = d_indices1[i];
341 for (int c = 0; c < vd; ++c)
342 {
343 d_y(dof, c, 0, face) = d_x(t?c:idx1, t?idx1:c);
344 }
345 const int idx2 = d_indices2[i];
346 for (int c = 0; c < vd; ++c)
347 {
348 if (idx2>-1 && idx2<threshold) // interior face
349 {
350 d_y(dof, c, 1, face) = d_x(t?c:idx2, t?idx2:c);
351 }
352 else if (idx2>=threshold) // shared boundary
353 {
354 const int e_shared = (idx2 - threshold) / nedof;
355 const int i_shared = (idx2 - threshold) % nedof;
356 d_y(dof, c, 1, face) = d_x_shared(i_shared,c,e_shared);
357 }
358 else // true boundary
359 {
360 d_y(dof, c, 1, face) = 0.0;
361 }
362 }
363 });
364}
365
367{
369 {
371 }
372 else
373 {
375 }
376}
377
378static MFEM_HOST_DEVICE int AddNnz(const int iE, int *I, const int dofs)
379{
380 int val = AtomicAdd(I[iE],dofs);
381 return val;
382}
383
385 const bool keep_nbr_block) const
386{
387 if (keep_nbr_block)
388 {
389 return L2FaceRestriction::FillI(mat, keep_nbr_block);
390 }
391 const int nface_dofs = face_dofs;
392 const int Ndofs = ndofs;
393 auto d_indices1 = scatter_indices1.Read();
394 auto d_indices2 = scatter_indices2.Read();
395 auto I = mat.ReadWriteI();
396 mfem::forall(nf*nface_dofs, [=] MFEM_HOST_DEVICE (int fdof)
397 {
398 const int f = fdof/nface_dofs;
399 const int iF = fdof%nface_dofs;
400 const int iE1 = d_indices1[f*nface_dofs+iF];
401 if (iE1 < Ndofs)
402 {
403 AddNnz(iE1,I,nface_dofs);
404 }
405 const int iE2 = d_indices2[f*nface_dofs+iF];
406 if (iE2 < Ndofs)
407 {
408 AddNnz(iE2,I,nface_dofs);
409 }
410 });
411}
412
414 SparseMatrix &face_mat) const
415{
416 const int nface_dofs = face_dofs;
417 const int Ndofs = ndofs;
418 auto d_indices1 = scatter_indices1.Read();
419 auto d_indices2 = scatter_indices2.Read();
420 auto I = mat.ReadWriteI();
421 auto I_face = face_mat.ReadWriteI();
422 mfem::forall(ne*elem_dofs*vdim+1, [=] MFEM_HOST_DEVICE (int i)
423 {
424 I_face[i] = 0;
425 });
426 mfem::forall(nf*nface_dofs, [=] MFEM_HOST_DEVICE (int fdof)
427 {
428 const int f = fdof/nface_dofs;
429 const int iF = fdof%nface_dofs;
430 const int iE1 = d_indices1[f*nface_dofs+iF];
431 if (iE1 < Ndofs)
432 {
433 for (int jF = 0; jF < nface_dofs; jF++)
434 {
435 const int jE2 = d_indices2[f*nface_dofs+jF];
436 if (jE2 < Ndofs)
437 {
438 AddNnz(iE1,I,1);
439 }
440 else
441 {
442 AddNnz(iE1,I_face,1);
443 }
444 }
445 }
446 const int iE2 = d_indices2[f*nface_dofs+iF];
447 if (iE2 < Ndofs)
448 {
449 for (int jF = 0; jF < nface_dofs; jF++)
450 {
451 const int jE1 = d_indices1[f*nface_dofs+jF];
452 if (jE1 < Ndofs)
453 {
454 AddNnz(iE2,I,1);
455 }
456 else
457 {
458 AddNnz(iE2,I_face,1);
459 }
460 }
461 }
462 });
463}
464
466 SparseMatrix &mat,
467 const bool keep_nbr_block) const
468{
469 if (keep_nbr_block)
470 {
471 return L2FaceRestriction::FillJAndData(ea_data, mat, keep_nbr_block);
472 }
473 const int nface_dofs = face_dofs;
474 const int Ndofs = ndofs;
475 auto d_indices1 = scatter_indices1.Read();
476 auto d_indices2 = scatter_indices2.Read();
477 auto mat_fea = Reshape(ea_data.Read(), nface_dofs, nface_dofs, 2, nf);
478 auto I = mat.ReadWriteI();
479 auto J = mat.WriteJ();
480 auto Data = mat.WriteData();
481 mfem::forall(nf*nface_dofs, [=] MFEM_HOST_DEVICE (int fdof)
482 {
483 const int f = fdof/nface_dofs;
484 const int iF = fdof%nface_dofs;
485 const int iE1 = d_indices1[f*nface_dofs+iF];
486 if (iE1 < Ndofs)
487 {
488 const int offset = AddNnz(iE1,I,nface_dofs);
489 for (int jF = 0; jF < nface_dofs; jF++)
490 {
491 const int jE2 = d_indices2[f*nface_dofs+jF];
492 J[offset+jF] = jE2;
493 Data[offset+jF] = mat_fea(jF,iF,1,f);
494 }
495 }
496 const int iE2 = d_indices2[f*nface_dofs+iF];
497 if (iE2 < Ndofs)
498 {
499 const int offset = AddNnz(iE2,I,nface_dofs);
500 for (int jF = 0; jF < nface_dofs; jF++)
501 {
502 const int jE1 = d_indices1[f*nface_dofs+jF];
503 J[offset+jF] = jE1;
504 Data[offset+jF] = mat_fea(jF,iF,0,f);
505 }
506 }
507 });
508}
509
511 SparseMatrix &mat,
512 SparseMatrix &face_mat) const
513{
514 const int nface_dofs = face_dofs;
515 const int Ndofs = ndofs;
516 auto d_indices1 = scatter_indices1.Read();
517 auto d_indices2 = scatter_indices2.Read();
518 auto mat_fea = Reshape(ea_data.Read(), nface_dofs, nface_dofs, 2, nf);
519 auto I = mat.ReadWriteI();
520 auto I_face = face_mat.ReadWriteI();
521 auto J = mat.WriteJ();
522 auto J_face = face_mat.WriteJ();
523 auto Data = mat.WriteData();
524 auto Data_face = face_mat.WriteData();
525 mfem::forall(nf*nface_dofs, [=] MFEM_HOST_DEVICE (int fdof)
526 {
527 const int f = fdof/nface_dofs;
528 const int iF = fdof%nface_dofs;
529 const int iE1 = d_indices1[f*nface_dofs+iF];
530 if (iE1 < Ndofs)
531 {
532 for (int jF = 0; jF < nface_dofs; jF++)
533 {
534 const int jE2 = d_indices2[f*nface_dofs+jF];
535 if (jE2 < Ndofs)
536 {
537 const int offset = AddNnz(iE1,I,1);
538 J[offset] = jE2;
539 Data[offset] = mat_fea(jF,iF,1,f);
540 }
541 else
542 {
543 const int offset = AddNnz(iE1,I_face,1);
544 J_face[offset] = jE2-Ndofs;
545 Data_face[offset] = mat_fea(jF,iF,1,f);
546 }
547 }
548 }
549 const int iE2 = d_indices2[f*nface_dofs+iF];
550 if (iE2 < Ndofs)
551 {
552 for (int jF = 0; jF < nface_dofs; jF++)
553 {
554 const int jE1 = d_indices1[f*nface_dofs+jF];
555 if (jE1 < Ndofs)
556 {
557 const int offset = AddNnz(iE2,I,1);
558 J[offset] = jE1;
559 Data[offset] = mat_fea(jF,iF,0,f);
560 }
561 else
562 {
563 const int offset = AddNnz(iE2,I_face,1);
564 J_face[offset] = jE1-Ndofs;
565 Data_face[offset] = mat_fea(jF,iF,0,f);
566 }
567 }
568 }
569 });
570}
571
572void ParL2FaceRestriction::ComputeScatterIndicesAndOffsets()
573{
574 Mesh &mesh = *fes.GetMesh();
575
576 // Initialization of the offsets
577 for (int i = 0; i <= ndofs; ++i)
578 {
579 gather_offsets[i] = 0;
580 }
581
582 // Computation of scatter indices and offsets
583 int f_ind=0;
584 for (int f = 0; f < pfes.GetNF(); ++f)
585 {
586 Mesh::FaceInformation face = mesh.GetFaceInformation(f);
587 if (type==FaceType::Interior && face.IsInterior())
588 {
589 SetFaceDofsScatterIndices1(face,f_ind);
591 {
592 if (face.IsShared())
593 {
595 }
596 else
597 {
599 }
600 }
601 f_ind++;
602 }
603 else if (type==FaceType::Boundary && face.IsBoundary())
604 {
605 SetFaceDofsScatterIndices1(face,f_ind);
607 {
609 }
610 f_ind++;
611 }
612 }
613 MFEM_VERIFY(f_ind==nf, "Unexpected number of faces.");
614
615 // Summation of the offsets
616 for (int i = 1; i <= ndofs; ++i)
617 {
618 gather_offsets[i] += gather_offsets[i - 1];
619 }
620}
621
622
623void ParL2FaceRestriction::ComputeGatherIndices()
624{
625 Mesh &mesh = *fes.GetMesh();
626
627 // Computation of gather_indices
628 int f_ind = 0;
629 for (int f = 0; f < fes.GetNF(); ++f)
630 {
631 Mesh::FaceInformation face = mesh.GetFaceInformation(f);
632 if (face.IsOfFaceType(type))
633 {
634 SetFaceDofsGatherIndices1(face,f_ind);
637 face.IsLocal())
638 {
640 }
641 f_ind++;
642 }
643 }
644 MFEM_VERIFY(f_ind==nf, "Unexpected number of faces.");
645
646 // Reset offsets to their correct value
647 for (int i = ndofs; i > 0; --i)
648 {
649 gather_offsets[i] = gather_offsets[i - 1];
650 }
651 gather_offsets[0] = 0;
652}
653
655 ElementDofOrdering f_ordering,
656 FaceType type,
657 L2FaceValues m)
658 : L2FaceRestriction(fes, f_ordering, type, m, false),
659 NCL2FaceRestriction(fes, f_ordering, type, m, false),
660 ParL2FaceRestriction(fes, f_ordering, type, m, false)
661{
662 if (nf==0) { return; }
663 x_interp.UseDevice(true);
664
665 CheckFESpace();
666
667 ComputeScatterIndicesAndOffsets();
668
669 ComputeGatherIndices();
670}
671
673 const Vector& x, Vector& y) const
674{
675 if (nf == 0) { return; }
676 MFEM_ASSERT(
678 "This method should be called when m == L2FaceValues::SingleValued.");
679 // Assumes all elements have the same number of dofs
680 const int nface_dofs = face_dofs;
681 const int vd = vdim;
682 const bool t = byvdim;
683 const int threshold = ndofs;
684 auto d_indices1 = scatter_indices1.Read();
685 auto d_x = Reshape(x.Read(), t?vd:ndofs, t?ndofs:vd);
686 auto d_y = Reshape(y.Write(), nface_dofs, vd, nf);
687 auto interp_config_ptr = interpolations.GetFaceInterpConfig().Read();
688 auto interpolators = interpolations.GetInterpolators().Read();
689 const int nc_size = interpolations.GetNumInterpolators();
690 auto d_interp = Reshape(interpolators, nface_dofs, nface_dofs, nc_size);
691 static constexpr int max_nd = 16*16;
692 MFEM_VERIFY(nface_dofs<=max_nd, "Too many degrees of freedom.");
693 mfem::forall_2D(nf, nface_dofs, 1, [=] MFEM_HOST_DEVICE (int face)
694 {
695 MFEM_SHARED real_t dof_values[max_nd];
696 const InterpConfig conf = interp_config_ptr[face];
697 const int master_side = conf.master_side;
698 const int interp_index = conf.index;
699 const int side = 0;
700 if ( !conf.is_non_conforming || side!=master_side )
701 {
702 MFEM_FOREACH_THREAD(dof,x,nface_dofs)
703 {
704 const int i = face*nface_dofs + dof;
705 const int idx = d_indices1[i];
706 if (idx>-1 && idx<threshold) // interior face
707 {
708 for (int c = 0; c < vd; ++c)
709 {
710 d_y(dof, c, face) = d_x(t?c:idx, t?idx:c);
711 }
712 }
713 else // true boundary
714 {
715 for (int c = 0; c < vd; ++c)
716 {
717 d_y(dof, c, face) = 0.0;
718 }
719 }
720 }
721 }
722 else // Interpolation from coarse to fine
723 {
724 for (int c = 0; c < vd; ++c)
725 {
726 MFEM_FOREACH_THREAD(dof,x,nface_dofs)
727 {
728 const int i = face*nface_dofs + dof;
729 const int idx = d_indices1[i];
730 if (idx>-1 && idx<threshold) // interior face
731 {
732 dof_values[dof] = d_x(t?c:idx, t?idx:c);
733 }
734 else // true boundary
735 {
736 dof_values[dof] = 0.0;
737 }
738 }
739 MFEM_SYNC_THREAD;
740 MFEM_FOREACH_THREAD(dof_out,x,nface_dofs)
741 {
742 real_t res = 0.0;
743 for (int dof_in = 0; dof_in<nface_dofs; dof_in++)
744 {
745 res += d_interp(dof_out, dof_in, interp_index)*dof_values[dof_in];
746 }
747 d_y(dof_out, c, face) = res;
748 }
749 MFEM_SYNC_THREAD;
750 }
751 }
752 });
753}
754
761
763{
765 {
767 }
769 {
771 }
773 {
775 }
777 {
779 }
780 else
781 {
782 MFEM_ABORT("Unknown type and multiplicity combination.");
783 }
784}
785
787 const real_t a) const
788{
789 MFEM_VERIFY(a == 1.0, "General coefficient case is not yet supported!");
790 if (nf==0) { return; }
792 {
794 {
797 }
798 else // Single Valued
799 {
802 }
803 }
804 else
805 {
807 {
809 }
810 else // Single valued
811 {
813 }
814 }
815}
816
845
847 const bool keep_nbr_block) const
848{
849 if (keep_nbr_block)
850 {
851 return NCL2FaceRestriction::FillI(mat, keep_nbr_block);
852 }
853 const int nface_dofs = face_dofs;
854 const int Ndofs = ndofs;
855 auto d_indices1 = scatter_indices1.Read();
856 auto d_indices2 = scatter_indices2.Read();
857 auto I = mat.ReadWriteI();
858 mfem::forall(nf*nface_dofs, [=] MFEM_HOST_DEVICE (int fdof)
859 {
860 const int f = fdof/nface_dofs;
861 const int iF = fdof%nface_dofs;
862 const int iE1 = d_indices1[f*nface_dofs+iF];
863 if (iE1 < Ndofs)
864 {
865 AddNnz(iE1,I,nface_dofs);
866 }
867 const int iE2 = d_indices2[f*nface_dofs+iF];
868 if (iE2 < Ndofs)
869 {
870 AddNnz(iE2,I,nface_dofs);
871 }
872 });
873}
874
876 SparseMatrix &face_mat) const
877{
878 MFEM_ABORT("Not yet implemented.");
879}
880
882 SparseMatrix &mat,
883 const bool keep_nbr_block) const
884{
885 if (keep_nbr_block)
886 {
887 return NCL2FaceRestriction::FillJAndData(fea_data, mat, keep_nbr_block);
888 }
889 const int nface_dofs = face_dofs;
890 const int Ndofs = ndofs;
891 auto d_indices1 = scatter_indices1.Read();
892 auto d_indices2 = scatter_indices2.Read();
893 auto I = mat.ReadWriteI();
894 auto mat_fea = Reshape(fea_data.Read(), nface_dofs, nface_dofs, 2, nf);
895 auto J = mat.WriteJ();
896 auto Data = mat.WriteData();
897 auto interp_config_ptr = interpolations.GetFaceInterpConfig().Read();
898 auto interpolators = interpolations.GetInterpolators().Read();
899 const int nc_size = interpolations.GetNumInterpolators();
900 auto d_interp = Reshape(interpolators, nface_dofs, nface_dofs, nc_size);
901 mfem::forall(nf*nface_dofs, [=] MFEM_HOST_DEVICE (int fdof)
902 {
903 const int f = fdof/nface_dofs;
904 const InterpConfig conf = interp_config_ptr[f];
905 const int master_side = conf.master_side;
906 const int interp_index = conf.index;
907 const int iF = fdof%nface_dofs;
908 const int iE1 = d_indices1[f*nface_dofs+iF];
909 if (iE1 < Ndofs)
910 {
911 const int offset1 = AddNnz(iE1,I,nface_dofs);
912 for (int jF = 0; jF < nface_dofs; jF++)
913 {
914 const int jE2 = d_indices2[f*nface_dofs+jF];
915 J[offset1+jF] = jE2;
916 real_t val2 = 0.0;
917 if ( conf.is_non_conforming && master_side==0 )
918 {
919 for (int kF = 0; kF < nface_dofs; kF++)
920 {
921 val2 += d_interp(kF, iF, interp_index) * mat_fea(jF,kF,1,f);
922 }
923 }
924 else if ( conf.is_non_conforming && master_side==1 )
925 {
926 for (int kF = 0; kF < nface_dofs; kF++)
927 {
928 val2 += mat_fea(kF,iF,1,f) * d_interp(kF, jF, interp_index);
929 }
930 }
931 else
932 {
933 val2 = mat_fea(jF,iF,1,f);
934 }
935 Data[offset1+jF] = val2;
936 }
937 }
938 const int iE2 = d_indices2[f*nface_dofs+iF];
939 if (iE2 < Ndofs)
940 {
941 const int offset2 = AddNnz(iE2,I,nface_dofs);
942 for (int jF = 0; jF < nface_dofs; jF++)
943 {
944 const int jE1 = d_indices1[f*nface_dofs+jF];
945 J[offset2+jF] = jE1;
946 real_t val1 = 0.0;
947 if ( conf.is_non_conforming && master_side==0 )
948 {
949 for (int kF = 0; kF < nface_dofs; kF++)
950 {
951 val1 += mat_fea(kF,iF,0,f) * d_interp(kF, jF, interp_index);
952 }
953 }
954 else if ( conf.is_non_conforming && master_side==1 )
955 {
956 for (int kF = 0; kF < nface_dofs; kF++)
957 {
958 val1 += d_interp(kF, iF, interp_index) * mat_fea(jF,kF,0,f);
959 }
960 }
961 else
962 {
963 val1 = mat_fea(jF,iF,0,f);
964 }
965 Data[offset2+jF] = val1;
966 }
967 }
968 });
969}
970
972 SparseMatrix &mat,
973 SparseMatrix &face_mat) const
974{
975 MFEM_ABORT("Not yet implemented.");
976}
977
978void ParNCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
979{
980 Mesh &mesh = *fes.GetMesh();
981
982 // Initialization of the offsets
983 for (int i = 0; i <= ndofs; ++i)
984 {
985 gather_offsets[i] = 0;
986 }
987
988 // Computation of scatter and offsets indices
989 int f_ind=0;
990 for (int f = 0; f < mesh.GetNumFacesWithGhost(); ++f)
991 {
992 Mesh::FaceInformation face = mesh.GetFaceInformation(f);
993 if ( face.IsNonconformingCoarse() )
994 {
995 // We skip nonconforming coarse faces as they are treated
996 // by the corresponding nonconforming fine faces.
997 continue;
998 }
999 else if ( type==FaceType::Interior && face.IsInterior() )
1000 {
1001 if ( face.IsConforming() )
1002 {
1003 SetFaceDofsScatterIndices1(face,f_ind);
1005 {
1006 if ( face.IsShared() )
1007 {
1009 }
1010 else
1011 {
1013 }
1014 }
1015 }
1016 else // Non-conforming face
1017 {
1018 SetFaceDofsScatterIndices1(face,f_ind);
1020 {
1021 if ( face.IsShared() )
1022 {
1024 }
1025 else // local nonconforming slave
1026 {
1028 }
1029 }
1030 }
1031 f_ind++;
1032 }
1033 else if (type==FaceType::Boundary && face.IsBoundary())
1034 {
1035 SetFaceDofsScatterIndices1(face,f_ind);
1037 {
1039 }
1040 f_ind++;
1041 }
1042 }
1043 MFEM_VERIFY(f_ind==nf, "Unexpected number of " <<
1044 (type==FaceType::Interior? "interior" : "boundary") <<
1045 " faces: " << f_ind << " vs " << nf );
1046
1047 // Summation of the offsets
1048 for (int i = 1; i <= ndofs; ++i)
1049 {
1050 gather_offsets[i] += gather_offsets[i - 1];
1051 }
1052}
1053
1054void ParNCL2FaceRestriction::ComputeGatherIndices()
1055{
1056 Mesh &mesh = *fes.GetMesh();
1057
1058 // Computation of gather_indices
1059 int f_ind = 0;
1060 for (int f = 0; f < mesh.GetNumFacesWithGhost(); ++f)
1061 {
1062 Mesh::FaceInformation face = mesh.GetFaceInformation(f);
1063 if ( face.IsNonconformingCoarse() )
1064 {
1065 // We skip nonconforming coarse faces as they are treated
1066 // by the corresponding nonconforming fine faces.
1067 continue;
1068 }
1069 else if ( face.IsOfFaceType(type) )
1070 {
1071 SetFaceDofsGatherIndices1(face,f_ind);
1074 face.IsLocal())
1075 {
1077 }
1078 f_ind++;
1079 }
1080 }
1081 MFEM_VERIFY(f_ind==nf, "Unexpected number of " <<
1082 (type==FaceType::Interior? "interior" : "boundary") <<
1083 " faces: " << f_ind << " vs " << nf );
1084
1085 // Switch back offsets to their correct value
1086 for (int i = ndofs; i > 0; --i)
1087 {
1088 gather_offsets[i] = gather_offsets[i - 1];
1089 }
1090 gather_offsets[0] = 0;
1091}
1092
1093} // namespace mfem
1094
1095#endif
MFEM_HOST_DEVICE T AtomicAdd(T &add, const T val)
Definition backends.hpp:116
const T * Read(bool on_dev=true) const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), on_dev).
Definition array.hpp:410
Operator that extracts face degrees of freedom for H1, ND, or RT FiniteElementSpaces.
void CheckFESpace(const ElementDofOrdering f_ordering)
Verify that ConformingFaceRestriction is built from a supported finite element space.
const FiniteElementSpace & fes
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 GetNF() const
Returns number of faces (i.e. co-dimension 1 entities) in the mesh.
Definition fespace.hpp:873
const FiniteElementCollection * FEColl() const
Definition fespace.hpp:854
Mesh * GetMesh() const
Returns the mesh.
Definition fespace.hpp:639
Arbitrary order H1-conforming (continuous) finite elements.
Definition fe_coll.hpp:291
const Array< NCInterpConfig > & GetNCFaceInterpConfig() const
Return an array containing the interpolation configuration for each face registered with RegisterFace...
int GetNumInterpolators() const
Return the total number of interpolators.
const Vector & GetInterpolators() const
Return an mfem::Vector containing the interpolators in the following format: face_dofs x face_dofs x ...
const Array< InterpConfig > & GetFaceInterpConfig() const
Return an array containing the interpolation configuration for each face registered with RegisterFace...
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...
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 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.
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.
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...
Mesh data type.
Definition mesh.hpp:67
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.
void FillI(SparseMatrix &mat, const bool keep_nbr_block=false) const override
Fill the I array of SparseMatrix corresponding to the sparsity pattern given by this NCL2FaceRestrict...
void FillJAndData(const Vector &fea_data, SparseMatrix &mat, const bool keep_nbr_block=false) const override
Fill the J and Data arrays of the SparseMatrix corresponding to the sparsity pattern given by this NC...
void SingleValuedNonconformingTransposeInterpolation(const Vector &x) const
Apply a change of basis from fine element basis to coarse element basis for the coarse face dofs....
void SingleValuedNonconformingTransposeInterpolationInPlace(Vector &x) const
Apply a change of basis from fine element basis to coarse element basis for the coarse face dofs....
const InterpolationManager & interpolations
void DoubleValuedNonconformingTransposeInterpolation(const Vector &x) const
Apply a change of basis from fine element basis to coarse element basis for the coarse face dofs....
void DoubleValuedNonconformingInterpolation(Vector &x) const
Apply a change of basis from coarse element basis to fine element basis for the coarse face dofs.
void DoubleValuedNonconformingTransposeInterpolationInPlace(Vector &x) const
Apply a change of basis from fine element basis to coarse element basis for the coarse face dofs....
Abstract parallel finite element space.
Definition pfespace.hpp:31
Operator that extracts Face degrees of freedom in parallel.
const ParFiniteElementSpace & pfes
void DoubleValuedConformingMult(const Vector &x, Vector &y) const override
Scatter the degrees of freedom, i.e. goes from L-Vector to face E-Vector. Should only be used with co...
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 FillJAndData(const Vector &ea_data, SparseMatrix &mat, SparseMatrix &face_mat) const
void FillI(SparseMatrix &mat, const bool keep_nbr_block=false) const override
ParL2FaceRestriction(const ParFiniteElementSpace &pfes_, ElementDofOrdering f_ordering, FaceType type, L2FaceValues m, bool build)
Constructs an ParL2FaceRestriction.
ParNCH1FaceRestriction(const ParFiniteElementSpace &fes, ElementDofOrdering f_ordering, FaceType type)
Constructs an ParNCH1FaceRestriction.
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 NonconformingInterpolation(Vector &x) const
Apply a change of basis from coarse element basis to fine element basis for the coarse face dofs.
InterpolationManager interpolations
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 Mult(const Vector &x, Vector &y) const override
Scatter the degrees of freedom, i.e. goes from L-Vector to face E-Vector.
ParNCL2FaceRestriction(const ParFiniteElementSpace &fes, ElementDofOrdering f_ordering, FaceType type, L2FaceValues m=L2FaceValues::DoubleValued)
Constructs an ParNCL2FaceRestriction.
void SingleValuedNonconformingMult(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 DoubleValuedNonconformingMult(const Vector &x, Vector &y) const override
Scatter the degrees of freedom, i.e. goes from L-Vector to face E-Vector. Should only be used with no...
void FillI(SparseMatrix &mat, const bool keep_nbr_block=false) const override
Fill the I array of SparseMatrix corresponding to the sparsity pattern given by this ParNCL2FaceRestr...
void AddMultTransposeInPlace(Vector &x, Vector &y) const override
Gather the degrees of freedom, i.e. goes from face E-Vector to L-Vector.
void FillJAndData(const Vector &fea_data, SparseMatrix &mat, SparseMatrix &face_mat) const
Data type sparse matrix.
Definition sparsemat.hpp:51
int * ReadWriteI(bool on_dev=true)
int * WriteJ(bool on_dev=true)
real_t * WriteData(bool on_dev=true)
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
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
virtual real_t * Write(bool on_dev=true)
Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), on_dev).
Definition vector.hpp:528
real_t a
Definition lissajous.cpp:41
real_t f(const Vector &p)
Vector GetLVectorFaceNbrData(const FiniteElementSpace &fes, const Vector &x, FaceType ftype)
Return the face-neighbor data given the L-vector x.
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
void forall_2D(int N, int X, int Y, lambda &&body)
Definition forall.hpp:1220
float real_t
Definition config.hpp:46
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