MFEM v4.7.0
Finite element discretization library
Loading...
Searching...
No Matches
prestriction.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2024, 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();
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 auto d_x_shared = Reshape(face_nbr_data.Read(),
329 t?vd:nsdofs, t?nsdofs:vd);
330 auto d_y = Reshape(y.Write(), nface_dofs, vd, 2, nf);
331 mfem::forall(nfdofs, [=] MFEM_HOST_DEVICE (int i)
332 {
333 const int dof = i % nface_dofs;
334 const int face = i / nface_dofs;
335 const int idx1 = d_indices1[i];
336 for (int c = 0; c < vd; ++c)
337 {
338 d_y(dof, c, 0, face) = d_x(t?c:idx1, t?idx1:c);
339 }
340 const int idx2 = d_indices2[i];
341 for (int c = 0; c < vd; ++c)
342 {
343 if (idx2>-1 && idx2<threshold) // interior face
344 {
345 d_y(dof, c, 1, face) = d_x(t?c:idx2, t?idx2:c);
346 }
347 else if (idx2>=threshold) // shared boundary
348 {
349 d_y(dof, c, 1, face) = d_x_shared(t?c:(idx2-threshold),
350 t?(idx2-threshold):c);
351 }
352 else // true boundary
353 {
354 d_y(dof, c, 1, face) = 0.0;
355 }
356 }
357 });
358}
359
361{
363 {
365 }
366 else
367 {
369 }
370}
371
372static MFEM_HOST_DEVICE int AddNnz(const int iE, int *I, const int dofs)
373{
374 int val = AtomicAdd(I[iE],dofs);
375 return val;
376}
377
379 const bool keep_nbr_block) const
380{
381 if (keep_nbr_block)
382 {
383 return L2FaceRestriction::FillI(mat, keep_nbr_block);
384 }
385 const int nface_dofs = face_dofs;
386 const int Ndofs = ndofs;
387 auto d_indices1 = scatter_indices1.Read();
388 auto d_indices2 = scatter_indices2.Read();
389 auto I = mat.ReadWriteI();
390 mfem::forall(nf*nface_dofs, [=] MFEM_HOST_DEVICE (int fdof)
391 {
392 const int f = fdof/nface_dofs;
393 const int iF = fdof%nface_dofs;
394 const int iE1 = d_indices1[f*nface_dofs+iF];
395 if (iE1 < Ndofs)
396 {
397 AddNnz(iE1,I,nface_dofs);
398 }
399 const int iE2 = d_indices2[f*nface_dofs+iF];
400 if (iE2 < Ndofs)
401 {
402 AddNnz(iE2,I,nface_dofs);
403 }
404 });
405}
406
408 SparseMatrix &face_mat) const
409{
410 const int nface_dofs = face_dofs;
411 const int Ndofs = ndofs;
412 auto d_indices1 = scatter_indices1.Read();
413 auto d_indices2 = scatter_indices2.Read();
414 auto I = mat.ReadWriteI();
415 auto I_face = face_mat.ReadWriteI();
416 mfem::forall(ne*elem_dofs*vdim+1, [=] MFEM_HOST_DEVICE (int i)
417 {
418 I_face[i] = 0;
419 });
420 mfem::forall(nf*nface_dofs, [=] MFEM_HOST_DEVICE (int fdof)
421 {
422 const int f = fdof/nface_dofs;
423 const int iF = fdof%nface_dofs;
424 const int iE1 = d_indices1[f*nface_dofs+iF];
425 if (iE1 < Ndofs)
426 {
427 for (int jF = 0; jF < nface_dofs; jF++)
428 {
429 const int jE2 = d_indices2[f*nface_dofs+jF];
430 if (jE2 < Ndofs)
431 {
432 AddNnz(iE1,I,1);
433 }
434 else
435 {
436 AddNnz(iE1,I_face,1);
437 }
438 }
439 }
440 const int iE2 = d_indices2[f*nface_dofs+iF];
441 if (iE2 < Ndofs)
442 {
443 for (int jF = 0; jF < nface_dofs; jF++)
444 {
445 const int jE1 = d_indices1[f*nface_dofs+jF];
446 if (jE1 < Ndofs)
447 {
448 AddNnz(iE2,I,1);
449 }
450 else
451 {
452 AddNnz(iE2,I_face,1);
453 }
454 }
455 }
456 });
457}
458
460 SparseMatrix &mat,
461 const bool keep_nbr_block) const
462{
463 if (keep_nbr_block)
464 {
465 return L2FaceRestriction::FillJAndData(ea_data, mat, keep_nbr_block);
466 }
467 const int nface_dofs = face_dofs;
468 const int Ndofs = ndofs;
469 auto d_indices1 = scatter_indices1.Read();
470 auto d_indices2 = scatter_indices2.Read();
471 auto mat_fea = Reshape(ea_data.Read(), nface_dofs, nface_dofs, 2, nf);
472 auto I = mat.ReadWriteI();
473 auto J = mat.WriteJ();
474 auto Data = mat.WriteData();
475 mfem::forall(nf*nface_dofs, [=] MFEM_HOST_DEVICE (int fdof)
476 {
477 const int f = fdof/nface_dofs;
478 const int iF = fdof%nface_dofs;
479 const int iE1 = d_indices1[f*nface_dofs+iF];
480 if (iE1 < Ndofs)
481 {
482 const int offset = AddNnz(iE1,I,nface_dofs);
483 for (int jF = 0; jF < nface_dofs; jF++)
484 {
485 const int jE2 = d_indices2[f*nface_dofs+jF];
486 J[offset+jF] = jE2;
487 Data[offset+jF] = mat_fea(jF,iF,1,f);
488 }
489 }
490 const int iE2 = d_indices2[f*nface_dofs+iF];
491 if (iE2 < Ndofs)
492 {
493 const int offset = AddNnz(iE2,I,nface_dofs);
494 for (int jF = 0; jF < nface_dofs; jF++)
495 {
496 const int jE1 = d_indices1[f*nface_dofs+jF];
497 J[offset+jF] = jE1;
498 Data[offset+jF] = mat_fea(jF,iF,0,f);
499 }
500 }
501 });
502}
503
505 SparseMatrix &mat,
506 SparseMatrix &face_mat) const
507{
508 const int nface_dofs = face_dofs;
509 const int Ndofs = ndofs;
510 auto d_indices1 = scatter_indices1.Read();
511 auto d_indices2 = scatter_indices2.Read();
512 auto mat_fea = Reshape(ea_data.Read(), nface_dofs, nface_dofs, 2, nf);
513 auto I = mat.ReadWriteI();
514 auto I_face = face_mat.ReadWriteI();
515 auto J = mat.WriteJ();
516 auto J_face = face_mat.WriteJ();
517 auto Data = mat.WriteData();
518 auto Data_face = face_mat.WriteData();
519 mfem::forall(nf*nface_dofs, [=] MFEM_HOST_DEVICE (int fdof)
520 {
521 const int f = fdof/nface_dofs;
522 const int iF = fdof%nface_dofs;
523 const int iE1 = d_indices1[f*nface_dofs+iF];
524 if (iE1 < Ndofs)
525 {
526 for (int jF = 0; jF < nface_dofs; jF++)
527 {
528 const int jE2 = d_indices2[f*nface_dofs+jF];
529 if (jE2 < Ndofs)
530 {
531 const int offset = AddNnz(iE1,I,1);
532 J[offset] = jE2;
533 Data[offset] = mat_fea(jF,iF,1,f);
534 }
535 else
536 {
537 const int offset = AddNnz(iE1,I_face,1);
538 J_face[offset] = jE2-Ndofs;
539 Data_face[offset] = mat_fea(jF,iF,1,f);
540 }
541 }
542 }
543 const int iE2 = d_indices2[f*nface_dofs+iF];
544 if (iE2 < Ndofs)
545 {
546 for (int jF = 0; jF < nface_dofs; jF++)
547 {
548 const int jE1 = d_indices1[f*nface_dofs+jF];
549 if (jE1 < Ndofs)
550 {
551 const int offset = AddNnz(iE2,I,1);
552 J[offset] = jE1;
553 Data[offset] = mat_fea(jF,iF,0,f);
554 }
555 else
556 {
557 const int offset = AddNnz(iE2,I_face,1);
558 J_face[offset] = jE1-Ndofs;
559 Data_face[offset] = mat_fea(jF,iF,0,f);
560 }
561 }
562 }
563 });
564}
565
566void ParL2FaceRestriction::ComputeScatterIndicesAndOffsets()
567{
568 Mesh &mesh = *fes.GetMesh();
569
570 // Initialization of the offsets
571 for (int i = 0; i <= ndofs; ++i)
572 {
573 gather_offsets[i] = 0;
574 }
575
576 // Computation of scatter indices and offsets
577 int f_ind=0;
578 for (int f = 0; f < pfes.GetNF(); ++f)
579 {
580 Mesh::FaceInformation face = mesh.GetFaceInformation(f);
581 if (type==FaceType::Interior && face.IsInterior())
582 {
583 SetFaceDofsScatterIndices1(face,f_ind);
585 {
586 if (face.IsShared())
587 {
589 }
590 else
591 {
593 }
594 }
595 f_ind++;
596 }
597 else if (type==FaceType::Boundary && face.IsBoundary())
598 {
599 SetFaceDofsScatterIndices1(face,f_ind);
601 {
603 }
604 f_ind++;
605 }
606 }
607 MFEM_VERIFY(f_ind==nf, "Unexpected number of faces.");
608
609 // Summation of the offsets
610 for (int i = 1; i <= ndofs; ++i)
611 {
612 gather_offsets[i] += gather_offsets[i - 1];
613 }
614}
615
616
617void ParL2FaceRestriction::ComputeGatherIndices()
618{
619 Mesh &mesh = *fes.GetMesh();
620
621 // Computation of gather_indices
622 int f_ind = 0;
623 for (int f = 0; f < fes.GetNF(); ++f)
624 {
625 Mesh::FaceInformation face = mesh.GetFaceInformation(f);
626 if (face.IsOfFaceType(type))
627 {
628 SetFaceDofsGatherIndices1(face,f_ind);
631 face.IsLocal())
632 {
634 }
635 f_ind++;
636 }
637 }
638 MFEM_VERIFY(f_ind==nf, "Unexpected number of faces.");
639
640 // Reset offsets to their correct value
641 for (int i = ndofs; i > 0; --i)
642 {
643 gather_offsets[i] = gather_offsets[i - 1];
644 }
645 gather_offsets[0] = 0;
646}
647
649 ElementDofOrdering f_ordering,
650 FaceType type,
651 L2FaceValues m)
652 : L2FaceRestriction(fes, f_ordering, type, m, false),
653 NCL2FaceRestriction(fes, f_ordering, type, m, false),
654 ParL2FaceRestriction(fes, f_ordering, type, m, false)
655{
656 if (nf==0) { return; }
657 x_interp.UseDevice(true);
658
659 CheckFESpace();
660
661 ComputeScatterIndicesAndOffsets();
662
663 ComputeGatherIndices();
664}
665
667 const Vector& x, Vector& y) const
668{
669 if (nf == 0) { return; }
670 MFEM_ASSERT(
672 "This method should be called when m == L2FaceValues::SingleValued.");
673 // Assumes all elements have the same number of dofs
674 const int nface_dofs = face_dofs;
675 const int vd = vdim;
676 const bool t = byvdim;
677 const int threshold = ndofs;
678 auto d_indices1 = scatter_indices1.Read();
679 auto d_x = Reshape(x.Read(), t?vd:ndofs, t?ndofs:vd);
680 auto d_y = Reshape(y.Write(), nface_dofs, vd, nf);
681 auto interp_config_ptr = interpolations.GetFaceInterpConfig().Read();
682 auto interpolators = interpolations.GetInterpolators().Read();
683 const int nc_size = interpolations.GetNumInterpolators();
684 auto d_interp = Reshape(interpolators, nface_dofs, nface_dofs, nc_size);
685 static constexpr int max_nd = 16*16;
686 MFEM_VERIFY(nface_dofs<=max_nd, "Too many degrees of freedom.");
687 mfem::forall_2D(nf, nface_dofs, 1, [=] MFEM_HOST_DEVICE (int face)
688 {
689 MFEM_SHARED real_t dof_values[max_nd];
690 const InterpConfig conf = interp_config_ptr[face];
691 const int master_side = conf.master_side;
692 const int interp_index = conf.index;
693 const int side = 0;
694 if ( !conf.is_non_conforming || side!=master_side )
695 {
696 MFEM_FOREACH_THREAD(dof,x,nface_dofs)
697 {
698 const int i = face*nface_dofs + dof;
699 const int idx = d_indices1[i];
700 if (idx>-1 && idx<threshold) // interior face
701 {
702 for (int c = 0; c < vd; ++c)
703 {
704 d_y(dof, c, face) = d_x(t?c:idx, t?idx:c);
705 }
706 }
707 else // true boundary
708 {
709 for (int c = 0; c < vd; ++c)
710 {
711 d_y(dof, c, face) = 0.0;
712 }
713 }
714 }
715 }
716 else // Interpolation from coarse to fine
717 {
718 for (int c = 0; c < vd; ++c)
719 {
720 MFEM_FOREACH_THREAD(dof,x,nface_dofs)
721 {
722 const int i = face*nface_dofs + dof;
723 const int idx = d_indices1[i];
724 if (idx>-1 && idx<threshold) // interior face
725 {
726 dof_values[dof] = d_x(t?c:idx, t?idx:c);
727 }
728 else // true boundary
729 {
730 dof_values[dof] = 0.0;
731 }
732 }
733 MFEM_SYNC_THREAD;
734 MFEM_FOREACH_THREAD(dof_out,x,nface_dofs)
735 {
736 real_t res = 0.0;
737 for (int dof_in = 0; dof_in<nface_dofs; dof_in++)
738 {
739 res += d_interp(dof_out, dof_in, interp_index)*dof_values[dof_in];
740 }
741 d_y(dof_out, c, face) = res;
742 }
743 MFEM_SYNC_THREAD;
744 }
745 }
746 });
747}
748
755
757{
759 {
761 }
763 {
765 }
767 {
769 }
771 {
773 }
774 else
775 {
776 MFEM_ABORT("Unknown type and multiplicity combination.");
777 }
778}
779
781 const real_t a) const
782{
783 MFEM_VERIFY(a == 1.0, "General coefficient case is not yet supported!");
784 if (nf==0) { return; }
786 {
788 {
791 }
792 else // Single Valued
793 {
796 }
797 }
798 else
799 {
801 {
803 }
804 else // Single valued
805 {
807 }
808 }
809}
810
839
841 const bool keep_nbr_block) const
842{
843 if (keep_nbr_block)
844 {
845 return NCL2FaceRestriction::FillI(mat, keep_nbr_block);
846 }
847 const int nface_dofs = face_dofs;
848 const int Ndofs = ndofs;
849 auto d_indices1 = scatter_indices1.Read();
850 auto d_indices2 = scatter_indices2.Read();
851 auto I = mat.ReadWriteI();
852 mfem::forall(nf*nface_dofs, [=] MFEM_HOST_DEVICE (int fdof)
853 {
854 const int f = fdof/nface_dofs;
855 const int iF = fdof%nface_dofs;
856 const int iE1 = d_indices1[f*nface_dofs+iF];
857 if (iE1 < Ndofs)
858 {
859 AddNnz(iE1,I,nface_dofs);
860 }
861 const int iE2 = d_indices2[f*nface_dofs+iF];
862 if (iE2 < Ndofs)
863 {
864 AddNnz(iE2,I,nface_dofs);
865 }
866 });
867}
868
870 SparseMatrix &face_mat) const
871{
872 MFEM_ABORT("Not yet implemented.");
873}
874
876 SparseMatrix &mat,
877 const bool keep_nbr_block) const
878{
879 if (keep_nbr_block)
880 {
881 return NCL2FaceRestriction::FillJAndData(fea_data, mat, keep_nbr_block);
882 }
883 const int nface_dofs = face_dofs;
884 const int Ndofs = ndofs;
885 auto d_indices1 = scatter_indices1.Read();
886 auto d_indices2 = scatter_indices2.Read();
887 auto I = mat.ReadWriteI();
888 auto mat_fea = Reshape(fea_data.Read(), nface_dofs, nface_dofs, 2, nf);
889 auto J = mat.WriteJ();
890 auto Data = mat.WriteData();
891 auto interp_config_ptr = interpolations.GetFaceInterpConfig().Read();
892 auto interpolators = interpolations.GetInterpolators().Read();
893 const int nc_size = interpolations.GetNumInterpolators();
894 auto d_interp = Reshape(interpolators, nface_dofs, nface_dofs, nc_size);
895 mfem::forall(nf*nface_dofs, [=] MFEM_HOST_DEVICE (int fdof)
896 {
897 const int f = fdof/nface_dofs;
898 const InterpConfig conf = interp_config_ptr[f];
899 const int master_side = conf.master_side;
900 const int interp_index = conf.index;
901 const int iF = fdof%nface_dofs;
902 const int iE1 = d_indices1[f*nface_dofs+iF];
903 if (iE1 < Ndofs)
904 {
905 const int offset1 = AddNnz(iE1,I,nface_dofs);
906 for (int jF = 0; jF < nface_dofs; jF++)
907 {
908 const int jE2 = d_indices2[f*nface_dofs+jF];
909 J[offset1+jF] = jE2;
910 real_t val2 = 0.0;
911 if ( conf.is_non_conforming && master_side==0 )
912 {
913 for (int kF = 0; kF < nface_dofs; kF++)
914 {
915 val2 += d_interp(kF, iF, interp_index) * mat_fea(jF,kF,1,f);
916 }
917 }
918 else if ( conf.is_non_conforming && master_side==1 )
919 {
920 for (int kF = 0; kF < nface_dofs; kF++)
921 {
922 val2 += mat_fea(kF,iF,1,f) * d_interp(kF, jF, interp_index);
923 }
924 }
925 else
926 {
927 val2 = mat_fea(jF,iF,1,f);
928 }
929 Data[offset1+jF] = val2;
930 }
931 }
932 const int iE2 = d_indices2[f*nface_dofs+iF];
933 if (iE2 < Ndofs)
934 {
935 const int offset2 = AddNnz(iE2,I,nface_dofs);
936 for (int jF = 0; jF < nface_dofs; jF++)
937 {
938 const int jE1 = d_indices1[f*nface_dofs+jF];
939 J[offset2+jF] = jE1;
940 real_t val1 = 0.0;
941 if ( conf.is_non_conforming && master_side==0 )
942 {
943 for (int kF = 0; kF < nface_dofs; kF++)
944 {
945 val1 += mat_fea(kF,iF,0,f) * d_interp(kF, jF, interp_index);
946 }
947 }
948 else if ( conf.is_non_conforming && master_side==1 )
949 {
950 for (int kF = 0; kF < nface_dofs; kF++)
951 {
952 val1 += d_interp(kF, iF, interp_index) * mat_fea(jF,kF,0,f);
953 }
954 }
955 else
956 {
957 val1 = mat_fea(jF,iF,0,f);
958 }
959 Data[offset2+jF] = val1;
960 }
961 }
962 });
963}
964
966 SparseMatrix &mat,
967 SparseMatrix &face_mat) const
968{
969 MFEM_ABORT("Not yet implemented.");
970}
971
972void ParNCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
973{
974 Mesh &mesh = *fes.GetMesh();
975
976 // Initialization of the offsets
977 for (int i = 0; i <= ndofs; ++i)
978 {
979 gather_offsets[i] = 0;
980 }
981
982 // Computation of scatter and offsets indices
983 int f_ind=0;
984 for (int f = 0; f < mesh.GetNumFacesWithGhost(); ++f)
985 {
986 Mesh::FaceInformation face = mesh.GetFaceInformation(f);
987 if ( face.IsNonconformingCoarse() )
988 {
989 // We skip nonconforming coarse faces as they are treated
990 // by the corresponding nonconforming fine faces.
991 continue;
992 }
993 else if ( type==FaceType::Interior && face.IsInterior() )
994 {
995 if ( face.IsConforming() )
996 {
998 SetFaceDofsScatterIndices1(face,f_ind);
1000 {
1001 if ( face.IsShared() )
1002 {
1004 }
1005 else
1006 {
1008 }
1009 }
1010 }
1011 else // Non-conforming face
1012 {
1014 SetFaceDofsScatterIndices1(face,f_ind);
1016 {
1017 if ( face.IsShared() )
1018 {
1020 }
1021 else // local nonconforming slave
1022 {
1024 }
1025 }
1026 }
1027 f_ind++;
1028 }
1029 else if (type==FaceType::Boundary && face.IsBoundary())
1030 {
1032 SetFaceDofsScatterIndices1(face,f_ind);
1034 {
1036 }
1037 f_ind++;
1038 }
1039 }
1040 MFEM_VERIFY(f_ind==nf, "Unexpected number of " <<
1041 (type==FaceType::Interior? "interior" : "boundary") <<
1042 " faces: " << f_ind << " vs " << nf );
1043
1044 // Summation of the offsets
1045 for (int i = 1; i <= ndofs; ++i)
1046 {
1047 gather_offsets[i] += gather_offsets[i - 1];
1048 }
1049
1050 // Transform the interpolation matrix map into a contiguous memory structure.
1053}
1054
1055void ParNCL2FaceRestriction::ComputeGatherIndices()
1056{
1057 Mesh &mesh = *fes.GetMesh();
1058
1059 // Computation of gather_indices
1060 int f_ind = 0;
1061 for (int f = 0; f < mesh.GetNumFacesWithGhost(); ++f)
1062 {
1063 Mesh::FaceInformation face = mesh.GetFaceInformation(f);
1064 if ( face.IsNonconformingCoarse() )
1065 {
1066 // We skip nonconforming coarse faces as they are treated
1067 // by the corresponding nonconforming fine faces.
1068 continue;
1069 }
1070 else if ( face.IsOfFaceType(type) )
1071 {
1072 SetFaceDofsGatherIndices1(face,f_ind);
1075 face.IsLocal())
1076 {
1078 }
1079 f_ind++;
1080 }
1081 }
1082 MFEM_VERIFY(f_ind==nf, "Unexpected number of " <<
1083 (type==FaceType::Interior? "interior" : "boundary") <<
1084 " faces: " << f_ind << " vs " << nf );
1085
1086 // Switch back offsets to their correct value
1087 for (int i = ndofs; i > 0; --i)
1088 {
1089 gather_offsets[i] = gather_offsets[i - 1];
1090 }
1091 gather_offsets[0] = 0;
1092}
1093
1094} // namespace mfem
1095
1096#endif
MFEM_HOST_DEVICE T AtomicAdd(T &add, const T val)
Definition backends.hpp:92
const T * Read(bool on_dev=true) const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), on_dev).
Definition array.hpp:317
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:746
const FiniteElementCollection * FEColl() const
Definition fespace.hpp:727
Mesh * GetMesh() const
Returns the mesh.
Definition fespace.hpp:559
Arbitrary order H1-conforming (continuous) finite elements.
Definition fe_coll.hpp:260
const Array< NCInterpConfig > & GetNCFaceInterpConfig() const
Return an array containing the interpolation configuration for each face registered with RegisterFace...
void RegisterFaceCoarseToFineInterpolation(const Mesh::FaceInformation &face, int face_index)
Register the face with face and index face_index as a conforming face for the interpolation of the de...
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 ...
void LinearizeInterpolatorMapIntoVector()
Transform the interpolation matrix map into a contiguous memory structure.
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 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:56
FaceInformation GetFaceInformation(int f) const
Definition mesh.cpp:1169
int GetNumFacesWithGhost() const
Return the number of faces (3D), edges (2D) or vertices (1D) including ghost faces.
Definition mesh.cpp:6261
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....
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:29
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:80
virtual const real_t * Read(bool on_dev=true) const
Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), on_dev).
Definition vector.hpp:474
virtual real_t * ReadWrite(bool on_dev=true)
Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), on_dev).
Definition vector.hpp:490
int Size() const
Returns the size of the vector.
Definition vector.hpp:218
virtual void UseDevice(bool use_dev) const
Enable execution of Vector operations using the mfem::Device.
Definition vector.hpp:136
virtual real_t * Write(bool on_dev=true)
Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), on_dev).
Definition vector.hpp:482
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:131
void forall_2D(int N, int X, int Y, lambda &&body)
Definition forall.hpp:763
float real_t
Definition config.hpp:43
ElementDofOrdering
Constants describing the possible orderings of the DOFs in one element.
Definition fespace.hpp:75
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:754
FaceType
Definition mesh.hpp:47
RefCoord t[3]