MFEM v4.10.0
Finite element discretization library
Loading...
Searching...
No Matches
fespace.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// Implementation of FiniteElementSpace
13
14#include "../general/text.hpp"
15#include "../general/forall.hpp"
17#include "fem.hpp"
19
20#include "derefmat_op.hpp"
21
22#include <algorithm>
23#include <cmath>
24#include <cstdarg>
25#include <unordered_map>
26#include <unordered_set>
27
28using namespace std;
29
30namespace mfem
31{
32
34 : mesh(NULL), fec(NULL), vdim(0), ordering(Ordering::byNODES),
35 ndofs(0), nvdofs(0), nedofs(0), nfdofs(0), nbdofs(0),
36 bdofs(NULL),
37 elem_dof(NULL), elem_fos(NULL), bdr_elem_dof(NULL), bdr_elem_fos(NULL),
38 face_dof(NULL),
39 NURBSext(NULL), own_ext(false),
40 cP_is_set(false),
41 Th(Operator::ANY_TYPE),
42 sequence(0), mesh_sequence(0), orders_changed(false), relaxed_hp(false)
43{ }
44
46 Mesh *mesh_,
47 const FiniteElementCollection *fec_)
48{
49 mesh_ = mesh_ ? mesh_ : orig.mesh;
50 fec_ = fec_ ? fec_ : orig.fec;
51
52 NURBSExtension *nurbs_ext = NULL;
53 if (orig.NURBSext && orig.NURBSext != orig.mesh->NURBSext)
54 {
55#ifdef MFEM_USE_MPI
56 ParNURBSExtension *pNURBSext =
57 dynamic_cast<ParNURBSExtension *>(orig.NURBSext);
58 if (pNURBSext)
59 {
60 nurbs_ext = new ParNURBSExtension(*pNURBSext);
61 }
62 else
63#endif
64 {
65 nurbs_ext = new NURBSExtension(*orig.NURBSext);
66 }
67 }
68
69 Constructor(mesh_, nurbs_ext, fec_, orig.vdim, orig.ordering);
70}
71
73 const FiniteElementCollection *fec,
74 int vdim, int ordering)
75{ Constructor(mesh, NULL, fec, vdim, ordering); }
76
78 const FiniteElementCollection *fec,
79 int vdim, int ordering)
80{ Constructor(mesh, ext, fec, vdim, ordering); }
81
83 const FiniteElementSpace &fes, const Array<int> *perm)
84{
85 MFEM_VERIFY(cP == NULL, "");
86 MFEM_VERIFY(cR == NULL, "");
87
88 SparseMatrix *perm_mat = NULL, *perm_mat_tr = NULL;
89 if (perm)
90 {
91 // Note: although n and fes.GetVSize() are typically equal, in
92 // variable-order spaces they may differ, since nonconforming edges/faces
93 // my have fictitious DOFs.
94 int n = perm->Size();
95 perm_mat = new SparseMatrix(n, fes.GetVSize());
96 for (int i=0; i<n; ++i)
97 {
98 real_t s;
99 int j = DecodeDof((*perm)[i], s);
100 perm_mat->Set(i, j, s);
101 }
102 perm_mat->Finalize();
103 perm_mat_tr = Transpose(*perm_mat);
104 }
105
106 if (fes.GetConformingProlongation() != NULL)
107 {
108 if (perm) { cP.reset(Mult(*perm_mat, *fes.GetConformingProlongation())); }
109 else { cP.reset(new SparseMatrix(*fes.GetConformingProlongation())); }
110 cP_is_set = true;
111 }
112 else if (perm != NULL)
113 {
114 cP.reset(perm_mat);
115 cP_is_set = true;
116 perm_mat = NULL;
117 }
118 if (fes.GetConformingRestriction() != NULL)
119 {
120 if (perm) { cR.reset(Mult(*fes.GetConformingRestriction(), *perm_mat_tr)); }
121 else { cR.reset(new SparseMatrix(*fes.GetConformingRestriction())); }
122 }
123 else if (perm != NULL)
124 {
125 cR.reset(perm_mat_tr);
126 perm_mat_tr = NULL;
127 }
128
129 delete perm_mat;
130 delete perm_mat_tr;
131}
132
134{
135#ifdef MFEM_USE_MPI
136 MFEM_VERIFY(dynamic_cast<const ParFiniteElementSpace*>(this) == NULL,
137 "Attempting to set serial prolongation operator for "
138 "parallel finite element space.");
139#endif
140
141 if (!cP)
142 {
143 cP = std::unique_ptr<SparseMatrix>(new SparseMatrix(p));
144 }
145 else
146 {
147 *cP = p;
148 }
149 cP_is_set = true;
150}
151
153{
154#ifdef MFEM_USE_MPI
155 MFEM_VERIFY(dynamic_cast<const ParFiniteElementSpace*>(this) == NULL,
156 "Attempting to set serial restriction operator for "
157 "parallel finite element space.");
158#endif
159
160 if (!cR)
161 {
162 cR = std::unique_ptr<SparseMatrix>(new SparseMatrix(r));
163 }
164 else
165 {
166 *cR = r;
167 }
168}
169
171{
172 MFEM_VERIFY(mesh_sequence == mesh->GetSequence(),
173 "Space has not been Updated() after a Mesh change.");
174 MFEM_VERIFY(i >= 0 && i < GetNE(), "Invalid element index");
175 MFEM_VERIFY(p >= 0 && p <= MaxVarOrder, "Order out of range");
176 MFEM_ASSERT(!elem_order.Size() || elem_order.Size() == GetNE(),
177 "Internal error");
178
179 const bool change = elem_order.Size() == 0 || elem_order[i] != p;
180 if (elem_order.Size() == 0) // convert space to variable-order space
181 {
184 }
185
186 if (change)
187 {
188 elem_order[i] = p;
189 orders_changed = true;
190 }
191
192 variableOrder = true;
193}
194
196{
197 MFEM_VERIFY(mesh_sequence == mesh->GetSequence(),
198 "Space has not been Updated() after a Mesh change.");
199 MFEM_VERIFY(i >= 0 && i < GetNE(), "Invalid element index");
200 MFEM_ASSERT(!elem_order.Size() || elem_order.Size() == GetNE(),
201 "Internal error");
202
203 return GetElementOrderImpl(i);
204}
205
207{
208 // (this is an internal version of GetElementOrder without asserts and checks)
209 return elem_order.Size() ? elem_order[i] : fec->GetOrder();
210}
211
212void FiniteElementSpace::GetVDofs(int vd, Array<int>& dofs, int ndofs_) const
213{
214 if (ndofs_ < 0) { ndofs_ = this->ndofs; }
215
217 {
218 for (int i = 0; i < dofs.Size(); i++)
219 {
220 dofs[i] = Ordering::Map<Ordering::byNODES>(ndofs_, vdim, i, vd);
221 }
222 }
223 else
224 {
225 for (int i = 0; i < dofs.Size(); i++)
226 {
227 dofs[i] = Ordering::Map<Ordering::byVDIM>(ndofs_, vdim, i, vd);
228 }
229 }
230}
231
232void FiniteElementSpace::DofsToVDofs(Array<int> &dofs, int ndofs_) const
233{
234 if (vdim == 1) { return; }
235 if (ndofs_ < 0) { ndofs_ = this->ndofs; }
236
238 {
239 Ordering::DofsToVDofs<Ordering::byNODES>(ndofs_, vdim, dofs);
240 }
241 else
242 {
243 Ordering::DofsToVDofs<Ordering::byVDIM>(ndofs_, vdim, dofs);
244 }
245}
246
247void FiniteElementSpace::DofsToVDofs(int vd, Array<int> &dofs, int ndofs_) const
248{
249 if (vdim == 1) { return; }
250 if (ndofs_ < 0) { ndofs_ = this->ndofs; }
251
253 {
254 for (int i = 0; i < dofs.Size(); i++)
255 {
256 dofs[i] = Ordering::Map<Ordering::byNODES>(ndofs_, vdim, dofs[i], vd);
257 }
258 }
259 else
260 {
261 for (int i = 0; i < dofs.Size(); i++)
262 {
263 dofs[i] = Ordering::Map<Ordering::byVDIM>(ndofs_, vdim, dofs[i], vd);
264 }
265 }
266}
267
268int FiniteElementSpace::DofToVDof(int dof, int vd, int ndofs_) const
269{
270 if (vdim == 1) { return dof; }
271 if (ndofs_ < 0) { ndofs_ = this->ndofs; }
272
274 {
275 return Ordering::Map<Ordering::byNODES>(ndofs_, vdim, dof, vd);
276 }
277 else
278 {
279 return Ordering::Map<Ordering::byVDIM>(ndofs_, vdim, dof, vd);
280 }
281}
282
283// static function
285{
286 int n = vdofs.Size(), *vdof = vdofs;
287 for (int i = 0; i < n; i++) { vdof[i] = UnsignIndex(vdof[i]); }
288}
289
291 DofTransformation &doftrans) const
292{
293 GetElementDofs(i, vdofs, doftrans);
294 DofsToVDofs(vdofs);
295 doftrans.SetVDim(vdim, ordering);
296}
297
300{
301 GetElementVDofs(i, vdofs, DoFTrans);
302 return DoFTrans.GetDofTransformation() ? &DoFTrans : NULL;
303}
304
306 DofTransformation &doftrans) const
307{
308 GetBdrElementDofs(i, vdofs, doftrans);
309 DofsToVDofs(vdofs);
310 doftrans.SetVDim(vdim, ordering);
311}
312
315{
316 GetBdrElementVDofs(i, vdofs, DoFTrans);
317 return DoFTrans.GetDofTransformation() ? &DoFTrans : NULL;
318}
319
321{
322 GetPatchDofs(i, vdofs);
323 DofsToVDofs(vdofs);
324}
325
327{
328 GetFaceDofs(i, vdofs);
329 DofsToVDofs(vdofs);
330}
331
333{
334 GetEdgeDofs(i, vdofs);
335 DofsToVDofs(vdofs);
336}
337
339{
340 GetVertexDofs(i, vdofs);
341 DofsToVDofs(vdofs);
342}
343
345{
346 GetElementInteriorDofs(i, vdofs);
347 DofsToVDofs(vdofs);
348}
349
351{
352 GetEdgeInteriorDofs(i, vdofs);
353 DofsToVDofs(vdofs);
354}
355
357{
358 if (elem_dof) { return; }
359
360 // TODO: can we call GetElementDofs only once per element?
361 Table *el_dof = new Table;
362 Table *el_fos = (mesh->Dimension() > 2) ? (new Table) : NULL;
363 Array<int> dofs;
364 Array<int> F, Fo;
365 el_dof->MakeI(mesh->GetNE());
366 if (el_fos) { el_fos->MakeI(mesh->GetNE()); }
367 for (int i = 0; i < mesh->GetNE(); i++)
368 {
369 GetElementDofs(i, dofs);
370 el_dof->AddColumnsInRow(i, dofs.Size());
371
372 if (el_fos)
373 {
374 mesh->GetElementFaces(i, F, Fo);
375 el_fos->AddColumnsInRow(i, Fo.Size());
376 }
377 }
378 el_dof->MakeJ();
379 if (el_fos) { el_fos->MakeJ(); }
380 for (int i = 0; i < mesh->GetNE(); i++)
381 {
382 GetElementDofs(i, dofs);
383 el_dof->AddConnections(i, (int *)dofs, dofs.Size());
384
385 if (el_fos)
386 {
387 mesh->GetElementFaces(i, F, Fo);
388 el_fos->AddConnections(i, (int *)Fo, Fo.Size());
389 }
390 }
391 el_dof->ShiftUpI();
392 if (el_fos) { el_fos->ShiftUpI(); }
393 elem_dof = el_dof;
394 elem_fos = el_fos;
395}
396
398{
399 if (bdr_elem_dof) { return; }
400
401 Table *bel_dof = new Table;
402 Table *bel_fos = (mesh->Dimension() == 3) ? (new Table) : NULL;
403 Array<int> dofs;
404 int F, Fo;
405 bel_dof->MakeI(mesh->GetNBE());
406 if (bel_fos) { bel_fos->MakeI(mesh->GetNBE()); }
407 for (int i = 0; i < mesh->GetNBE(); i++)
408 {
409 GetBdrElementDofs(i, dofs);
410 bel_dof->AddColumnsInRow(i, dofs.Size());
411
412 if (bel_fos)
413 {
414 bel_fos->AddAColumnInRow(i);
415 }
416 }
417 bel_dof->MakeJ();
418 if (bel_fos) { bel_fos->MakeJ(); }
419 for (int i = 0; i < mesh->GetNBE(); i++)
420 {
421 GetBdrElementDofs(i, dofs);
422 bel_dof->AddConnections(i, (int *)dofs, dofs.Size());
423
424 if (bel_fos)
425 {
426 mesh->GetBdrElementFace(i, &F, &Fo);
427 bel_fos->AddConnection(i, Fo);
428 }
429 }
430 bel_dof->ShiftUpI();
431 if (bel_fos) { bel_fos->ShiftUpI(); }
432 bdr_elem_dof = bel_dof;
433 bdr_elem_fos = bel_fos;
434}
435
437{
438 // Here, "face" == (dim-1)-dimensional mesh entity.
439
440 if (face_dof) { return; }
441
442 if (NURBSext) { BuildNURBSFaceToDofTable(); return; }
443
444 Table *fc_dof = new Table;
445 Array<int> dofs;
446 fc_dof->MakeI(mesh->GetNumFaces());
447 for (int i = 0; i < fc_dof->Size(); i++)
448 {
449 GetFaceDofs(i, dofs, 0);
450 fc_dof->AddColumnsInRow(i, dofs.Size());
451 }
452 fc_dof->MakeJ();
453 for (int i = 0; i < fc_dof->Size(); i++)
454 {
455 GetFaceDofs(i, dofs, 0);
456 fc_dof->AddConnections(i, (int *)dofs, dofs.Size());
457 }
458 fc_dof->ShiftUpI();
459 face_dof = fc_dof;
460}
461
463{
464 delete elem_dof;
465 delete elem_fos;
466 elem_dof = NULL;
467 elem_fos = NULL;
469}
470
472{
473 Array<int> dof_marker(ndofs);
474
475 dof_marker = -1;
476
477 int *J = elem_dof->GetJ(), nnz = elem_dof->Size_of_connections();
478 for (int k = 0, dof_counter = 0; k < nnz; k++)
479 {
480 const int sdof = J[k]; // signed dof
481 const int dof = UnsignIndex(sdof);
482 int new_dof = dof_marker[dof];
483 if (new_dof < 0)
484 {
485 dof_marker[dof] = new_dof = dof_counter++;
486 }
487 // Preserve the sign of sdof
488 J[k] = (sdof < 0) ? FlipIndexSign(new_dof) : new_dof;
489 }
490}
491
493{
494 if (dof_elem_array.Size()) { return; }
495
497
500 dof_elem_array = -1;
501 for (int i = 0; i < mesh -> GetNE(); i++)
502 {
503 const int *dofs = elem_dof -> GetRow(i);
504 const int n = elem_dof -> RowSize(i);
505 for (int j = 0; j < n; j++)
506 {
507 int dof = DecodeDof(dofs[j]);
508 if (dof_elem_array[dof] < 0)
509 {
510 dof_elem_array[dof] = i;
511 dof_ldof_array[dof] = j;
512 }
513 }
514 }
515}
516
518{
519 if (dof_bdr_elem_array.Size()) { return; }
520
522
526 for (int i = 0; i < mesh -> GetNBE(); i++)
527 {
528 const int *dofs = bdr_elem_dof -> GetRow(i);
529 const int n = bdr_elem_dof -> RowSize(i);
530 for (int j = 0; j < n; j++)
531 {
532 int dof = DecodeDof(dofs[j]);
533 if (dof_bdr_elem_array[dof] < 0)
534 {
535 dof_bdr_elem_array[dof] = i;
536 dof_bdr_ldof_array[dof] = j;
537 }
538 }
539 }
540}
541
542void MarkDofs(const Array<int> &dofs, Array<int> &mark_array)
543{
544 for (auto d : dofs)
545 {
546 mark_array[UnsignIndex(d)] = -1;
547 }
548}
549
551 Array<int> &ess_vdofs,
552 int component) const
553{
554 Array<int> dofs;
555 ess_vdofs.SetSize(GetVSize());
556 ess_vdofs = 0;
557 for (int i = 0; i < GetNBE(); i++)
558 {
559 if (bdr_attr_is_ess[GetBdrAttribute(i)-1])
560 {
561 if (component < 0)
562 {
563 // Mark all components.
564 GetBdrElementVDofs(i, dofs);
565 }
566 else
567 {
568 GetBdrElementDofs(i, dofs);
569 for (auto &d : dofs) { d = DofToVDof(d, component); }
570 }
571 MarkDofs(dofs, ess_vdofs);
572 }
573 }
574
575 // mark possible hidden boundary edges in a non-conforming mesh, also
576 // local DOFs affected by boundary elements on other processors
577 if (Nonconforming())
578 {
579 Array<int> bdr_verts, bdr_edges, bdr_faces;
580 mesh->ncmesh->GetBoundaryClosure(bdr_attr_is_ess, bdr_verts, bdr_edges,
581 bdr_faces);
582 for (auto v : bdr_verts)
583 {
584 if (component < 0)
585 {
586 GetVertexVDofs(v, dofs);
587 }
588 else
589 {
590 GetVertexDofs(v, dofs);
591 for (auto &d : dofs) { d = DofToVDof(d, component); }
592 }
593 MarkDofs(dofs, ess_vdofs);
594 }
595 for (auto e : bdr_edges)
596 {
597 if (component < 0)
598 {
599 GetEdgeVDofs(e, dofs);
600 }
601 else
602 {
603 GetEdgeDofs(e, dofs);
604 for (auto &d : dofs) { d = DofToVDof(d, component); }
605 }
606 MarkDofs(dofs, ess_vdofs);
607 }
608 for (auto f : bdr_faces)
609 {
610 if (component < 0)
611 {
612 GetEntityVDofs(2, f, dofs);
613 }
614 else
615 {
616 GetEntityDofs(2, f, dofs);
617 for (auto &d : dofs) { d = DofToVDof(d, component); }
618 }
619 MarkDofs(dofs, ess_vdofs);
620 }
621 }
622}
623
626 int component) const
627{
628 Array<int> ess_vdofs, ess_tdofs;
629 GetEssentialVDofs(bdr_attr_is_ess, ess_vdofs, component);
631 if (!R)
632 {
633 ess_tdofs.MakeRef(ess_vdofs);
634 }
635 else
636 {
637 R->BooleanMult(ess_vdofs, ess_tdofs);
638#ifdef MFEM_DEBUG
639 // Verify that in boolean arithmetic: P^T ess_dofs = R ess_dofs
640 Array<int> ess_tdofs2(ess_tdofs.Size());
641 GetConformingProlongation()->BooleanMultTranspose(ess_vdofs, ess_tdofs2);
642
643 int counter = 0;
644 std::string error_msg = "failed dof: ";
645 auto ess_tdofs_ = ess_tdofs.HostRead();
646 auto ess_tdofs2_ = ess_tdofs2.HostRead();
647 for (int i = 0; i < ess_tdofs2.Size(); ++i)
648 {
649 if (bool(ess_tdofs_[i]) != bool(ess_tdofs2_[i]))
650 {
651 error_msg += std::to_string(i) += "(R ";
652 error_msg += std::to_string(bool(ess_tdofs_[i])) += " P^T ";
653 error_msg += std::to_string(bool(ess_tdofs2_[i])) += ") ";
654 counter++;
655 }
656 }
657
658 MFEM_ASSERT(R->Height() == GetConformingProlongation()->Width(), "!");
659 MFEM_ASSERT(R->Width() == GetConformingProlongation()->Height(), "!");
660 MFEM_ASSERT(R->Width() == ess_vdofs.Size(), "!");
661 MFEM_VERIFY(counter == 0, "internal MFEM error: counter = " << counter
662 << ' ' << error_msg);
663#endif
664 }
665 MarkerToList(ess_tdofs, ess_tdof_list);
666}
667
669 int component)
670{
671 if (mesh->bdr_attributes.Size())
672 {
673 Array<int> ess_bdr(mesh->bdr_attributes.Max());
674 ess_bdr = 1;
675 GetEssentialTrueDofs(ess_bdr, boundary_dofs, component);
676 }
677 else
678 {
679 boundary_dofs.DeleteAll();
680 }
681}
682
684 int component) const
685{
686 Array<int> dofs;
687 ext_vdofs.SetSize(GetVSize());
688 ext_vdofs = 0;
689
690 Array<int> ext_face_marker;
691 mesh->GetExteriorFaceMarker(ext_face_marker);
692 for (int i = 0; i < ext_face_marker.Size(); i++)
693 {
694 if (ext_face_marker[i])
695 {
696 if (component < 0)
697 {
698 // Mark all components.
699 GetFaceDofs(i, dofs);
700 DofsToVDofs(dofs);
701 }
702 else
703 {
704 GetFaceDofs(i, dofs);
705 for (auto &d : dofs) { d = DofToVDof(d, component); }
706 }
707 MarkDofs(dofs, ext_vdofs);
708 }
709 }
710}
711
713 int component) const
714{
715 Array<int> ext_vdofs, ext_tdofs;
716 GetExteriorVDofs(ext_vdofs, component);
718 if (!R)
719 {
720 ext_tdofs.MakeRef(ext_vdofs);
721 }
722 else
723 {
724 R->BooleanMult(ext_vdofs, ext_tdofs);
725#ifdef MFEM_DEBUG
726 // Verify that in boolean arithmetic: P^T ext_dofs = R ext_dofs
727 Array<int> ext_tdofs2(ext_tdofs.Size());
728 GetConformingProlongation()->BooleanMultTranspose(ext_vdofs, ext_tdofs2);
729
730 int counter = 0;
731 std::string error_msg = "failed dof: ";
732 auto ext_tdofs_ = ext_tdofs.HostRead();
733 auto ext_tdofs2_ = ext_tdofs2.HostRead();
734 for (int i = 0; i < ext_tdofs2.Size(); ++i)
735 {
736 if (bool(ext_tdofs_[i]) != bool(ext_tdofs2_[i]))
737 {
738 error_msg += std::to_string(i) += "(R ";
739 error_msg += std::to_string(bool(ext_tdofs_[i])) += " P^T ";
740 error_msg += std::to_string(bool(ext_tdofs2_[i])) += ") ";
741 counter++;
742 }
743 }
744
745 MFEM_ASSERT(R->Height() == GetConformingProlongation()->Width(), "!");
746 MFEM_ASSERT(R->Width() == GetConformingProlongation()->Height(), "!");
747 MFEM_ASSERT(R->Width() == ext_vdofs.Size(), "!");
748 MFEM_VERIFY(counter == 0, "internal MFEM error: counter = " << counter
749 << ' ' << error_msg);
750#endif
751 }
752 MarkerToList(ext_tdofs, ext_tdof_list);
753}
754
755// static method
757 Array<int> &list)
758{
759 int num_marked = 0;
760 marker.HostRead(); // make sure we can read the array on host
761 for (int i = 0; i < marker.Size(); i++)
762 {
763 if (marker[i]) { num_marked++; }
764 }
765 list.SetSize(0);
766 list.HostWrite();
767 list.Reserve(num_marked);
768 for (int i = 0; i < marker.Size(); i++)
769 {
770 if (marker[i]) { list.Append(i); }
771 }
772}
773
774// static method
775void FiniteElementSpace::ListToMarker(const Array<int> &list, int marker_size,
776 Array<int> &marker, int mark_val)
777{
778 list.HostRead(); // make sure we can read the array on host
779 marker.SetSize(marker_size);
780 marker.HostWrite();
781 marker = 0;
782 for (int i = 0; i < list.Size(); i++)
783 {
784 marker[list[i]] = mark_val;
785 }
786}
787
789 Array<int> &cdofs)
790{
792 if (cP) { cP->BooleanMultTranspose(dofs, cdofs); }
793 else { dofs.Copy(cdofs); }
794}
795
797 Array<int> &dofs)
798{
800 if (cR) { cR->BooleanMultTranspose(cdofs, dofs); }
801 else { cdofs.Copy(dofs); }
802}
803
806{
807 int i, j;
808 Array<int> d_vdofs, c_vdofs;
809 SparseMatrix *R;
810
811 R = new SparseMatrix (cfes -> GetVSize(), GetVSize());
812
813 for (i = 0; i < mesh -> GetNE(); i++)
814 {
815 this -> GetElementVDofs (i, d_vdofs);
816 cfes -> GetElementVDofs (i, c_vdofs);
817
818#ifdef MFEM_DEBUG
819 if (d_vdofs.Size() != c_vdofs.Size())
820 {
821 mfem_error ("FiniteElementSpace::D2C_GlobalRestrictionMatrix (...)");
822 }
823#endif
824
825 for (j = 0; j < d_vdofs.Size(); j++)
826 {
827 R -> Set (c_vdofs[j], d_vdofs[j], 1.0);
828 }
829 }
830
831 R -> Finalize();
832
833 return R;
834}
835
838{
839 int i, j;
840 Array<int> d_dofs, c_dofs;
841 SparseMatrix *R;
842
843 R = new SparseMatrix (cfes -> GetNDofs(), ndofs);
844
845 for (i = 0; i < mesh -> GetNE(); i++)
846 {
847 this -> GetElementDofs (i, d_dofs);
848 cfes -> GetElementDofs (i, c_dofs);
849
850#ifdef MFEM_DEBUG
851 if (c_dofs.Size() != 1)
852 mfem_error ("FiniteElementSpace::"
853 "D2Const_GlobalRestrictionMatrix (...)");
854#endif
855
856 for (j = 0; j < d_dofs.Size(); j++)
857 {
858 R -> Set (c_dofs[0], d_dofs[j], 1.0);
859 }
860 }
861
862 R -> Finalize();
863
864 return R;
865}
866
869{
870 SparseMatrix *R;
871 DenseMatrix loc_restr;
872 Array<int> l_dofs, h_dofs, l_vdofs, h_vdofs;
873
874 int lvdim = lfes->GetVDim();
875 R = new SparseMatrix (lvdim * lfes -> GetNDofs(), lvdim * ndofs);
876
877 Geometry::Type cached_geom = Geometry::INVALID;
878 const FiniteElement *h_fe = NULL;
879 const FiniteElement *l_fe = NULL;
881
882 for (int i = 0; i < mesh -> GetNE(); i++)
883 {
884 this -> GetElementDofs (i, h_dofs);
885 lfes -> GetElementDofs (i, l_dofs);
886
887 // Assuming 'loc_restr' depends only on the Geometry::Type.
889 if (geom != cached_geom)
890 {
891 h_fe = this -> GetFE (i);
892 l_fe = lfes -> GetFE (i);
894 h_fe->Project(*l_fe, T, loc_restr);
895 cached_geom = geom;
896 }
897
898 for (int vd = 0; vd < lvdim; vd++)
899 {
900 l_dofs.Copy(l_vdofs);
901 lfes->DofsToVDofs(vd, l_vdofs);
902
903 h_dofs.Copy(h_vdofs);
904 this->DofsToVDofs(vd, h_vdofs);
905
906 R -> SetSubMatrix (l_vdofs, h_vdofs, loc_restr, 1);
907 }
908 }
909
910 R -> Finalize();
911
912 return R;
913}
914
916 SparseMatrix& deps, Array<int>& master_dofs, Array<int>& slave_dofs,
917 DenseMatrix& I, int skipfirst)
918{
919 for (int i = skipfirst; i < slave_dofs.Size(); i++)
920 {
921 const int sdof = slave_dofs[i];
922 if (!deps.RowSize(sdof)) // not processed yet
923 {
924 for (int j = 0; j < master_dofs.Size(); j++)
925 {
926 const real_t coef = I(i, j);
927 if (std::abs(coef) > 1e-12)
928 {
929 const int mdof = master_dofs[j];
930 if (mdof != sdof && mdof != FlipIndexSign(sdof))
931 {
932 deps.Add(sdof, mdof, coef);
933 }
934 }
935 }
936 }
937 }
938}
939
941 SparseMatrix &deps, Array<int> &master_dofs, const FiniteElement *master_fe,
942 Array<int> &slave_dofs, int slave_face, const DenseMatrix *pm) const
944 // In variable-order spaces in 3D, we need to only constrain interior face
945 // DOFs (this is done one level up), since edge dependencies can be more
946 // complex and are primarily handled by edge-edge dependencies. The one
947 // exception is edges of slave faces that lie in the interior of the master
948 // face, which are not covered by edge-edge relations. This function finds
949 // such edges and makes them constrained by the master face.
950 // See also https://github.com/mfem/mfem/pull/1423#issuecomment-633916643
951
952 Array<int> V, E, Eo; // TODO: LocalArray
953 mesh->GetFaceVertices(slave_face, V);
954 mesh->GetFaceEdges(slave_face, E, Eo);
955 MFEM_ASSERT(V.Size() == E.Size(), "");
956
957 DenseMatrix I;
959 edge_T.SetFE(&SegmentFE);
960
961 // constrain each edge of the slave face
962 for (int i = 0; i < E.Size(); i++)
963 {
964 int a = i, b = (i+1) % V.Size();
965 if (V[a] > V[b]) { std::swap(a, b); }
966
967 DenseMatrix &edge_pm = edge_T.GetPointMat();
968 edge_pm.SetSize(2, 2);
970 // copy two points from the face point matrix
971 real_t mid[2];
972 for (int j = 0; j < 2; j++)
973 {
974 edge_pm(j, 0) = (*pm)(j, a);
975 edge_pm(j, 1) = (*pm)(j, b);
976 mid[j] = 0.5*((*pm)(j, a) + (*pm)(j, b));
977 }
978
979 // check that the edge does not coincide with the master face's edge
980 const real_t eps = 1e-14;
981 if (mid[0] > eps && mid[0] < 1-eps &&
982 mid[1] > eps && mid[1] < 1-eps)
983 {
984 int order = GetEdgeDofs(E[i], slave_dofs, 0);
985
986 const auto *edge_fe = fec->GetFE(Geometry::SEGMENT, order);
987 edge_fe->GetTransferMatrix(*master_fe, edge_T, I);
988
989 AddDependencies(deps, master_dofs, slave_dofs, I, 0);
990 }
991 }
992}
993
994bool FiniteElementSpace::DofFinalizable(int dof, const Array<bool>& finalized,
995 const SparseMatrix& deps)
996{
997 const int* dep = deps.GetRowColumns(dof);
998 int ndep = deps.RowSize(dof);
999
1000 // are all constraining DOFs finalized?
1001 for (int i = 0; i < ndep; i++)
1002 {
1003 if (!finalized[dep[i]]) { return false; }
1004 }
1005 return true;
1006}
1007
1009 Geometry::Type master_geom,
1010 int variant) const
1011{
1012 // In NC meshes with prisms/tets, a special constraint occurs where a
1013 // prism/tet edge is slave to another element's face (see illustration
1014 // here: https://github.com/mfem/mfem/pull/713#issuecomment-495786362)
1015 // Rather than introduce a new edge-face constraint type, we handle such
1016 // cases as degenerate face-face constraints, where the point-matrix
1017 // rectangle has zero height. This method returns DOFs for the first edge
1018 // of the rectangle, duplicated in the orthogonal direction, to resemble
1019 // DOFs for a quadrilateral face. The extra DOFs are ignored by
1020 // FiniteElementSpace::AddDependencies.
1021
1022 Array<int> edof;
1023 int order = GetEdgeDofs(FlipIndexSign(index), edof, variant);
1024
1027 int nn = 2*nv + ne;
1028
1029 dofs.SetSize(nn*nn);
1030 if (!dofs.Size()) { return 0; }
1031
1032 dofs = edof[0];
1033
1034 // copy first two vertex DOFs
1035 for (int i = 0; i < nv; i++)
1036 {
1037 dofs[i] = edof[i];
1038 dofs[nv+i] = edof[nv+i];
1039 }
1040 // copy first edge DOFs
1041 int face_vert = Geometry::NumVerts[master_geom];
1042 for (int i = 0; i < ne; i++)
1043 {
1044 dofs[face_vert*nv + i] = edof[2*nv + i];
1045 }
1046
1047 return order;
1048}
1049
1051{
1052 // return the number of vertex and edge DOFs that precede inner DOFs
1053 const int nv = fec->GetNumDof(Geometry::POINT, order);
1054 const int ne = fec->GetNumDof(Geometry::SEGMENT, order);
1055
1056 return Geometry::NumVerts[geom] * (geom == Geometry::SEGMENT ? nv : (nv + ne));
1057}
1058
1060 Geometry::Type master_geom,
1061 int variant) const
1062{
1063 switch (entity)
1064 {
1065 case 0:
1066 GetVertexDofs(index, dofs);
1067 return 0;
1068
1069 case 1:
1070 return GetEdgeDofs(index, dofs, variant);
1071
1072 default:
1073 if (index >= 0)
1074 {
1075 return GetFaceDofs(index, dofs, variant);
1076 }
1077 else
1078 {
1079 return GetDegenerateFaceDofs(index, dofs, master_geom, variant);
1080 }
1081 }
1082}
1083
1085 Geometry::Type master_geom,
1086 int variant) const
1087{
1088 const int n = GetEntityDofs(entity, index, dofs, master_geom, variant);
1089 DofsToVDofs(dofs);
1090 return n;
1091}
1092
1093// Variable-order spaces: enforce minimum rule on conforming edges/faces
1095{
1096 if (!IsVariableOrder()) { return; }
1097
1098 Array<int> master_dofs, slave_dofs;
1099
1101 DenseMatrix I;
1102
1103 for (int entity = 1; entity < mesh->Dimension(); entity++)
1104 {
1105 const Table &ent_dofs = (entity == 1) ? var_edge_dofs : var_face_dofs;
1106 const int num_ent = (entity == 1) ? mesh->GetNEdges() : mesh->GetNFaces();
1107 MFEM_ASSERT(ent_dofs.Size() >= num_ent+1, "");
1108
1109 // add constraints within edges/faces holding multiple DOF sets
1111 for (int i = 0; i < num_ent; i++)
1112 {
1113 if (ent_dofs.RowSize(i) <= 1) { continue; }
1114
1115 Geometry::Type geom =
1116 (entity == 1) ? Geometry::SEGMENT : mesh->GetFaceGeometry(i);
1117
1118 if (geom != last_geom)
1119 {
1121 last_geom = geom;
1122 }
1123
1124 // get lowest order variant DOFs and FE
1125 const int p = GetEntityDofs(entity, i, master_dofs, geom, 0);
1126 const auto *master_fe = fec->GetFE(geom, p);
1127 if (!master_fe) { break; }
1128
1129 // constrain all higher order DOFs: interpolate lowest order function
1130 for (int variant = 1; ; variant++)
1131 {
1132 const int q = GetEntityDofs(entity, i, slave_dofs, geom, variant);
1133 if (q < 0) { break; }
1134
1135 const auto *slave_fe = fec->GetFE(geom, q);
1136 slave_fe->GetTransferMatrix(*master_fe, T, I);
1137
1138 AddDependencies(deps, master_dofs, slave_dofs, I);
1139 }
1140 }
1141 }
1142}
1143
1145{
1146#ifdef MFEM_USE_MPI
1147 MFEM_VERIFY(dynamic_cast<const ParFiniteElementSpace*>(this) == NULL,
1148 "This method should not be used with a ParFiniteElementSpace!");
1149#endif
1150
1151 if (cP_is_set) { return; }
1152 cP_is_set = true;
1153
1154 if (FEColl()->GetContType() == FiniteElementCollection::DISCONTINUOUS)
1155 {
1156 cP.reset();
1157 cR.reset();
1158 cR_hp.reset();
1159 R_transpose.reset();
1160 return;
1161 }
1162
1163 Array<int> master_dofs, slave_dofs, highest_dofs;
1164
1166 DenseMatrix I;
1167
1168 // For each slave DOF, the dependency matrix will contain a row that
1169 // expresses the slave DOF as a linear combination of its immediate master
1170 // DOFs. Rows of independent DOFs will remain empty.
1171 SparseMatrix deps(ndofs);
1172
1173 // Inverse dependencies for the cR_hp matrix in variable-order spaces:
1174 // For each master edge/face with more DOF sets, the inverse dependency
1175 // matrix contains a row that expresses the master true DOF (lowest order)
1176 // as a linear combination of the highest order set of DOFs.
1177 SparseMatrix inv_deps(ndofs);
1178
1180
1181 // Collect local face/edge dependencies, starting with faces
1182 for (int entity = 2; entity >= 1; entity--)
1183 {
1184 const NCMesh::NCList &list = mesh->ncmesh->GetNCList(entity);
1185 if (!list.masters.Size()) { continue; }
1186
1187 // loop through all master edges/faces, constrain their slave edges/faces
1188 for (const NCMesh::Master &master : list.masters)
1189 {
1190 Geometry::Type master_geom = master.Geom();
1191
1192 const int p = GetEntityDofs(entity, master.index, master_dofs,
1193 master_geom);
1194 if (!master_dofs.Size()) { continue; }
1195
1196 const FiniteElement *master_fe = fec->GetFE(master_geom, p);
1197 if (!master_fe) { continue; }
1198
1199 switch (master_geom)
1200 {
1201 case Geometry::SQUARE: T.SetFE(&QuadrilateralFE); break;
1202 case Geometry::TRIANGLE: T.SetFE(&TriangleFE); break;
1203 case Geometry::SEGMENT: T.SetFE(&SegmentFE); break;
1204 default: MFEM_ABORT("unsupported geometry");
1205 }
1206
1207 for (int si = master.slaves_begin; si < master.slaves_end; si++)
1208 {
1209 const NCMesh::Slave &slave = list.slaves[si];
1210
1211 int q = GetEntityDofs(entity, slave.index, slave_dofs, master_geom);
1212 if (!slave_dofs.Size()) { break; }
1213
1214 const FiniteElement *slave_fe = fec->GetFE(slave.Geom(), q);
1215 list.OrientedPointMatrix(slave, T.GetPointMat());
1216 slave_fe->GetTransferMatrix(*master_fe, T, I);
1217
1218 // variable-order spaces: face edges need to be handled separately
1219 int skipfirst = 0;
1220 if (IsVariableOrder() && entity == 2 && slave.index >= 0)
1221 {
1222 skipfirst = GetNumBorderDofs(master_geom, q);
1223 }
1224
1225 // make each slave DOF dependent on all master DOFs
1226 AddDependencies(deps, master_dofs, slave_dofs, I, skipfirst);
1227
1228 if (skipfirst)
1229 {
1230 // constrain internal edge DOFs if they were skipped
1231 const auto *pm = list.point_matrices[master_geom][slave.matrix];
1232 AddEdgeFaceDependencies(deps, master_dofs, master_fe,
1233 slave_dofs, slave.index, pm);
1234 }
1235 }
1236
1237 // Add inverse dependencies for the cR_hp matrix; if a master has
1238 // more DOF sets, the lowest order set interpolates the highest one.
1239 if (IsVariableOrder())
1240 {
1241 int nvar = GetNVariants(entity, master.index);
1242 if (nvar > 1)
1243 {
1244 const int q = GetEntityDofs(entity, master.index, highest_dofs,
1245 master_geom, nvar-1);
1246 const auto *highest_fe = fec->GetFE(master_geom, q);
1247
1248 T.SetIdentityTransformation(master_geom);
1249 master_fe->GetTransferMatrix(*highest_fe, T, I);
1250
1251 // add dependencies only for the inner dofs
1252 const int skip = GetNumBorderDofs(master_geom, p);
1253 AddDependencies(inv_deps, highest_dofs, master_dofs, I, skip);
1254 }
1255 }
1256 }
1257 }
1258
1259 deps.Finalize();
1260 inv_deps.Finalize();
1261
1262 // DOFs that stayed independent are true DOFs
1263 int n_true_dofs = 0;
1264 for (int i = 0; i < ndofs; i++)
1265 {
1266 if (!deps.RowSize(i)) { n_true_dofs++; }
1267 }
1268
1269 // if all dofs are true dofs leave cP and cR NULL
1270 if (n_true_dofs == ndofs)
1271 {
1272 cP.reset();
1273 cR.reset();
1274 cR_hp.reset();
1275 R_transpose.reset();
1276 return;
1277 }
1278
1279 // create the conforming prolongation matrix cP
1280 cP.reset(new SparseMatrix(ndofs, n_true_dofs));
1281
1282 // create the conforming restriction matrix cR
1283 int *cR_J;
1284 {
1285 int *cR_I = Memory<int>(n_true_dofs+1);
1286 real_t *cR_A = Memory<real_t>(n_true_dofs);
1287 cR_J = Memory<int>(n_true_dofs);
1288 for (int i = 0; i < n_true_dofs; i++)
1289 {
1290 cR_I[i] = i;
1291 cR_A[i] = 1.0;
1292 }
1293 cR_I[n_true_dofs] = n_true_dofs;
1294 cR.reset(new SparseMatrix(cR_I, cR_J, cR_A, n_true_dofs, ndofs));
1295 }
1296
1297 // In variable-order spaces, create the restriction matrix cR_hp, which is
1298 // similar to cR but has interpolation of the master edge/face DOFs of
1299 // maximum order per edge/face, since the maximum order is on an adjacent
1300 // element (e.g. where projection would be computed).
1301 if (IsVariableOrder())
1302 {
1303 cR_hp.reset(new SparseMatrix(n_true_dofs, ndofs));
1304 }
1305 else
1306 {
1307 cR_hp.reset();
1308 }
1309
1310 Array<bool> finalized(ndofs);
1311 finalized = false;
1312
1313 Array<int> cols;
1314 Vector srow;
1315
1316 // Put identity in the prolongation matrix for true DOFs, and set cR_hp
1317 for (int i = 0, true_dof = 0; i < ndofs; i++)
1318 {
1319 if (!deps.RowSize(i)) // true dof
1320 {
1321 cP->Add(i, true_dof, 1.0);
1322 cR_J[true_dof] = i;
1323 finalized[i] = true;
1324
1325 if (cR_hp)
1326 {
1327 if (inv_deps.RowSize(i))
1328 {
1329 inv_deps.GetRow(i, cols, srow);
1330 cR_hp->AddRow(true_dof, cols, srow);
1331 }
1332 else
1333 {
1334 cR_hp->Add(true_dof, i, 1.0);
1335 }
1336 }
1337
1338 true_dof++;
1339 }
1340 }
1341
1342 // Now calculate cP rows of slave DOFs as combinations of cP rows of their
1343 // master DOFs. It is possible that some slave DOFs depend on DOFs that are
1344 // themselves slaves. Here we resolve such indirect constraints by first
1345 // calculating rows of the cP matrix for DOFs whose master DOF cP rows are
1346 // already known (in the first iteration these are the true DOFs). In the
1347 // second iteration, slaves of slaves can be 'finalized' (given a row in the
1348 // cP matrix), in the third iteration slaves of slaves of slaves, etc.
1349 bool finished;
1350 int n_finalized = n_true_dofs;
1351 do
1352 {
1353 finished = true;
1354 for (int dof = 0; dof < ndofs; dof++)
1355 {
1356 if (!finalized[dof] && DofFinalizable(dof, finalized, deps))
1357 {
1358 const int* dep_col = deps.GetRowColumns(dof);
1359 const real_t* dep_coef = deps.GetRowEntries(dof);
1360 int n_dep = deps.RowSize(dof);
1361
1362 for (int j = 0; j < n_dep; j++)
1363 {
1364 cP->GetRow(dep_col[j], cols, srow);
1365 srow *= dep_coef[j];
1366 cP->AddRow(dof, cols, srow);
1367 }
1368
1369 finalized[dof] = true;
1370 n_finalized++;
1371 finished = false;
1372 }
1373 }
1374 }
1375 while (!finished);
1376
1377 // If everything is consistent (mesh, face orientations, etc.), we should
1378 // be able to finalize all slave DOFs, otherwise it's a serious error.
1379 MFEM_VERIFY(n_finalized == ndofs,
1380 "Error creating cP matrix: n_finalized = "
1381 << n_finalized << ", ndofs = " << ndofs);
1382
1383 cP->Finalize();
1384 if (cR_hp) { cR_hp->Finalize(); }
1385
1386 if (vdim > 1)
1387 {
1390 if (cR_hp) { MakeVDimMatrix(*cR_hp); }
1391 }
1392}
1393
1395{
1396 if (vdim == 1) { return; }
1397
1398 int height = mat.Height();
1399 int width = mat.Width();
1400
1401 SparseMatrix *vmat = new SparseMatrix(vdim*height, vdim*width);
1402
1403 Array<int> dofs, vdofs;
1404 Vector srow;
1405 for (int i = 0; i < height; i++)
1406 {
1407 mat.GetRow(i, dofs, srow);
1408 for (int vd = 0; vd < vdim; vd++)
1409 {
1410 dofs.Copy(vdofs);
1411 DofsToVDofs(vd, vdofs, width);
1412 vmat->SetRow(DofToVDof(i, vd, height), vdofs, srow);
1413 }
1414 }
1415 vmat->Finalize();
1416
1417 mat.Swap(*vmat);
1418 delete vmat;
1419}
1420
1421
1423{
1424 if (Conforming()) { return NULL; }
1426 return cP.get();
1427}
1428
1430{
1431 if (Conforming()) { return NULL; }
1433 if (cR && !R_transpose) { R_transpose.reset(new TransposeOperator(*cR)); }
1434 return cR.get();
1435}
1436
1438{
1439 if (Conforming()) { return NULL; }
1441 return IsVariableOrder() ? cR_hp.get() : cR.get();
1442}
1443
1445{
1446 GetRestrictionOperator(); // Ensure that R_transpose is built
1447 return R_transpose.get();
1448}
1449
1451{
1453 return P ? (P->Width() / vdim) : ndofs;
1454}
1455
1457{
1458 const FiniteElement *fe = GetTypicalFE();
1460 {
1461 return GetVDim();
1462 }
1463 return GetVDim()*std::max(GetMesh()->SpaceDimension(), fe->GetRangeDim());
1464}
1465
1467{
1468 const FiniteElement *fe = GetTypicalFE();
1470 {
1471 return 2 * GetMesh()->SpaceDimension() - 3;
1472 }
1473 return GetVDim()*fe->GetCurlDim();
1474}
1475
1477 ElementDofOrdering e_ordering) const
1478{
1479 // Check if we have a discontinuous space using the FE collection:
1480 if (IsDGSpace())
1481 {
1482 // TODO: when VDIM is 1, we can return IdentityOperator.
1483 if (L2E_nat.Ptr() == NULL)
1484 {
1485 // The input L-vector layout is:
1486 // * ND x NE x VDIM, for Ordering::byNODES, or
1487 // * VDIM x ND x NE, for Ordering::byVDIM.
1488 // The output E-vector layout is: ND x VDIM x NE.
1490 }
1492 }
1493 if (e_ordering == ElementDofOrdering::LEXICOGRAPHIC)
1494 {
1495 if (L2E_lex.Ptr() == NULL)
1496 {
1497 L2E_lex.Reset(new ElementRestriction(*this, e_ordering));
1498 }
1500 }
1501 // e_ordering == ElementDofOrdering::NATIVE
1502 if (L2E_nat.Ptr() == NULL)
1503 {
1504 L2E_nat.Reset(new ElementRestriction(*this, e_ordering));
1505 }
1507}
1508
1510 ElementDofOrdering f_ordering, FaceType type, L2FaceValues mul) const
1511{
1512 const bool is_dg_space = IsDGSpace();
1513 const L2FaceValues m = (is_dg_space && mul==L2FaceValues::DoubleValued) ?
1515 auto key = std::make_tuple(is_dg_space, f_ordering, type, m);
1516 auto itr = L2F.find(key);
1517 if (itr != L2F.end())
1518 {
1519 return itr->second.get();
1520 }
1521 else
1522 {
1523 std::unique_ptr<FaceRestriction> res;
1524 if (is_dg_space)
1525 {
1526 if (Conforming())
1527 {
1528 res.reset(new L2FaceRestriction(*this, f_ordering, type, m));
1529 }
1530 else
1531 {
1532 res.reset(new NCL2FaceRestriction(*this, f_ordering, type, m));
1533 }
1534 }
1535 else if (dynamic_cast<const DG_Interface_FECollection*>(fec))
1536 {
1537 res.reset(new L2InterfaceFaceRestriction(*this, f_ordering, type));
1538 }
1539 else
1540 {
1541 res.reset(new ConformingFaceRestriction(*this, f_ordering, type));
1542 }
1543 return L2F.emplace(key, std::move(res)).first->second.get();
1544 }
1545}
1546
1548 ElementDofOrdering f_ordering, FaceType type) const
1549{
1550 const auto key = make_tuple(f_ordering, type);
1551
1552 auto it = interpolations.find(key);
1553 if (it != interpolations.end())
1554 {
1555 return *it->second;
1556 }
1557 else
1558 {
1559 auto interp = make_unique<InterpolationManager>(*this, f_ordering, type);
1560
1561 int face_idx = 0;
1562 for (int f = 0; f < mesh->GetNumFacesWithGhost(); ++f)
1563 {
1565 if (!face.IsOfFaceType(type) || face.IsNonconformingCoarse())
1566 {
1567 continue;
1568 }
1569 if (face.IsConforming() || face.IsBoundary())
1570 {
1571 interp->RegisterFaceConformingInterpolation(face, face_idx);
1572 }
1573 else
1574 {
1575 interp->RegisterFaceCoarseToFineInterpolation(face, face_idx);
1576 }
1577 ++face_idx;
1578 }
1579
1580 // Transform the interpolation matrix map into contiguous memory.
1581 interp->LinearizeInterpolatorMapIntoVector();
1582 interp->InitializeNCInterpConfig();
1583
1584 return *interpolations.emplace(key, std::move(interp)).first->second;
1585 }
1586}
1587
1589 const IntegrationRule &ir) const
1590{
1592 {
1593 return nullptr;
1594 }
1595
1596 for (int i = 0; i < E2Q_array.Size(); i++)
1597 {
1598 const QuadratureInterpolator *qi = E2Q_array[i];
1599 if (qi->IntRule == &ir) { return qi; }
1600 }
1601
1603 E2Q_array.Append(qi);
1604 return qi;
1605}
1606
1608 const QuadratureSpace &qs) const
1609{
1611 {
1612 return nullptr;
1613 }
1614
1615 for (int i = 0; i < E2Q_array.Size(); i++)
1616 {
1617 const QuadratureInterpolator *qi = E2Q_array[i];
1618 if (qi->qspace == &qs) { return qi; }
1619 }
1620
1622 E2Q_array.Append(qi);
1623 return qi;
1624}
1625
1628 const IntegrationRule &ir, FaceType type) const
1629{
1631 {
1632 return nullptr;
1633 }
1634
1635 if (type==FaceType::Interior)
1636 {
1637 for (int i = 0; i < E2IFQ_array.Size(); i++)
1638 {
1640 if (qi->IntRule == &ir) { return qi; }
1641 }
1642
1644 type);
1645 E2IFQ_array.Append(qi);
1646 return qi;
1647 }
1648 else //Boundary
1649 {
1650 for (int i = 0; i < E2BFQ_array.Size(); i++)
1651 {
1653 if (qi->IntRule == &ir) { return qi; }
1654 }
1655
1657 type);
1658 E2BFQ_array.Append(qi);
1659 return qi;
1660 }
1661}
1662
1664 const int coarse_ndofs, const Table &coarse_elem_dof,
1665 const Table *coarse_elem_fos, const DenseTensor localP[]) const
1666{
1667 /// TODO: Implement DofTransformation support
1668
1669 MFEM_VERIFY(mesh->GetLastOperation() == Mesh::REFINE, "");
1670
1671 Array<int> dofs, coarse_dofs, coarse_vdofs;
1672 Vector row;
1673
1674 Mesh::GeometryList elem_geoms(*mesh);
1675
1676 SparseMatrix *P;
1677 if (elem_geoms.Size() == 1)
1678 {
1679 const int coarse_ldof = localP[elem_geoms[0]].SizeJ();
1680 P = new SparseMatrix(GetVSize(), coarse_ndofs*vdim, coarse_ldof);
1681 }
1682 else
1683 {
1684 P = new SparseMatrix(GetVSize(), coarse_ndofs*vdim);
1685 }
1686
1687 Array<int> mark(P->Height());
1688 mark = 0;
1689
1691
1692 for (int k = 0; k < mesh->GetNE(); k++)
1693 {
1694 const Embedding &emb = rtrans.embeddings[k];
1696 const DenseMatrix &lP = localP[geom](emb.matrix);
1697 const int fine_ldof = localP[geom].SizeI();
1698
1699 elem_dof->GetRow(k, dofs);
1700 coarse_elem_dof.GetRow(emb.parent, coarse_dofs);
1701
1702 for (int vd = 0; vd < vdim; vd++)
1703 {
1704 coarse_dofs.Copy(coarse_vdofs);
1705 DofsToVDofs(vd, coarse_vdofs, coarse_ndofs);
1706
1707 for (int i = 0; i < fine_ldof; i++)
1708 {
1709 const int r = DofToVDof(dofs[i], vd);
1710 const int m = UnsignIndex(r);
1711
1712 if (!mark[m])
1713 {
1714 lP.GetRow(i, row);
1715 P->SetRow(r, coarse_vdofs, row);
1716 mark[m] = 1;
1717 }
1718 }
1719 }
1720 }
1721
1722 MFEM_ASSERT(mark.Sum() == P->Height(), "Not all rows of P set.");
1723 if (elem_geoms.Size() != 1) { P->Finalize(); }
1724 return P;
1725}
1726
1728 const int coarse_ndofs, const Table &coarse_elem_dof) const
1729{
1730 MFEM_VERIFY(mesh->GetLastOperation() == Mesh::REFINE, "");
1731
1732 Array<int> dofs, coarse_dofs, coarse_vdofs;
1733 Vector row;
1734
1735 Mesh::GeometryList elem_geoms(*mesh);
1736
1737 SparseMatrix *P = new SparseMatrix(GetVSize(), coarse_ndofs*vdim);
1738
1739 Array<int> mark(P->Height());
1740 mark = 0;
1741
1743 DenseMatrix lP;
1745 for (int k = 0; k < mesh->GetNE(); k++)
1746 {
1747 const Embedding &emb = rtrans.embeddings[k];
1749
1750 const FiniteElement *fe = GetFE(k);
1751 isotr.SetIdentityTransformation(geom);
1752 const int ldof = fe->GetDof();
1753 lP.SetSize(ldof, ldof);
1754 const DenseTensor &pmats = rtrans.point_matrices[geom];
1755 isotr.SetPointMat(pmats(emb.matrix));
1756 fe->GetLocalInterpolation(isotr, lP);
1757
1758 const int fine_ldof = lP.Height();
1759
1760 elem_dof->GetRow(k, dofs);
1761 coarse_elem_dof.GetRow(emb.parent, coarse_dofs);
1762
1763 for (int vd = 0; vd < vdim; vd++)
1764 {
1765 coarse_dofs.Copy(coarse_vdofs);
1766 DofsToVDofs(vd, coarse_vdofs, coarse_ndofs);
1767
1768 for (int i = 0; i < fine_ldof; i++)
1769 {
1770 const int r = DofToVDof(dofs[i], vd);
1771 const int m = UnsignIndex(r);
1772
1773 if (!mark[m])
1774 {
1775 lP.GetRow(i, row);
1776 P->SetRow(r, coarse_vdofs, row);
1777 mark[m] = 1;
1778 }
1779 }
1780 }
1781 }
1782
1783 MFEM_VERIFY(mark.Sum() == P->Height(), "Not all rows of P set.");
1784 P->Finalize();
1785 return P;
1786}
1787
1789 Geometry::Type geom, DenseTensor &localP) const
1790{
1791 const FiniteElement *fe = fec->FiniteElementForGeometry(geom);
1792
1794 const DenseTensor &pmats = rtrans.point_matrices[geom];
1795
1796 int nmat = pmats.SizeK();
1797 int ldof = fe->GetDof();
1798
1800 isotr.SetIdentityTransformation(geom);
1801
1802 // calculate local interpolation matrices for all refinement types
1803 localP.SetSize(ldof, ldof, nmat);
1804 for (int i = 0; i < nmat; i++)
1805 {
1806 isotr.SetPointMat(pmats(i));
1807 fe->GetLocalInterpolation(isotr, localP(i));
1808 }
1809}
1810
1812 const Table* old_elem_dof,
1813 const Table* old_elem_fos)
1814{
1815 MFEM_VERIFY(GetNE() >= old_elem_dof->Size(),
1816 "Previous mesh is not coarser.");
1817
1818 Mesh::GeometryList elem_geoms(*mesh);
1819 if (!IsVariableOrder())
1820 {
1822 for (int i = 0; i < elem_geoms.Size(); i++)
1823 {
1824 GetLocalRefinementMatrices(elem_geoms[i], localP[elem_geoms[i]]);
1825 }
1826 return RefinementMatrix_main(old_ndofs, *old_elem_dof, old_elem_fos,
1827 localP);
1828 }
1829 else
1830 {
1831 return VariableOrderRefinementMatrix(old_ndofs, *old_elem_dof);
1832 }
1833}
1834
1836 const FiniteElementSpace* fespace, Table* old_elem_dof, Table* old_elem_fos,
1837 int old_ndofs)
1838 : fespace(fespace),
1839 old_elem_dof(old_elem_dof),
1840 old_elem_fos(old_elem_fos)
1841{
1842 MFEM_VERIFY(fespace->GetNE() >= old_elem_dof->Size(),
1843 "Previous mesh is not coarser.");
1844
1845 width = old_ndofs * fespace->GetVDim();
1846 height = fespace->GetVSize();
1847
1848 Mesh::GeometryList elem_geoms(*fespace->GetMesh());
1849
1850 if (!fespace->IsVariableOrder())
1851 {
1852 for (int i = 0; i < elem_geoms.Size(); i++)
1853 {
1854 fespace->GetLocalRefinementMatrices(elem_geoms[i], localP[elem_geoms[i]]);
1855 }
1856 }
1857
1858 ConstructDoFTransArray();
1859}
1860
1862 const FiniteElementSpace *fespace, const FiniteElementSpace *coarse_fes)
1863 : Operator(fespace->GetVSize(), coarse_fes->GetVSize()),
1864 fespace(fespace), old_elem_dof(NULL), old_elem_fos(NULL)
1865{
1866 Mesh::GeometryList elem_geoms(*fespace->GetMesh());
1867
1868 if (!fespace->IsVariableOrder())
1869 {
1870 for (int i = 0; i < elem_geoms.Size(); i++)
1871 {
1872 fespace->GetLocalRefinementMatrices(*coarse_fes, elem_geoms[i],
1873 localP[elem_geoms[i]]);
1874 }
1875 }
1876
1877 // Make a copy of the coarse elem_dof Table.
1878 old_elem_dof = new Table(coarse_fes->GetElementToDofTable());
1879
1880 // Make a copy of the coarse elem_fos Table if it exists.
1881 if (coarse_fes->GetElementToFaceOrientationTable())
1882 {
1883 old_elem_fos = new Table(*coarse_fes->GetElementToFaceOrientationTable());
1884 }
1885
1886 ConstructDoFTransArray();
1887}
1888
1890{
1891 delete old_elem_dof;
1892 delete old_elem_fos;
1893 for (int i=0; i<old_DoFTransArray.Size(); i++)
1894 {
1895 delete old_DoFTransArray[i];
1896 }
1897}
1898
1899void FiniteElementSpace::RefinementOperator::ConstructDoFTransArray()
1900{
1901 old_DoFTransArray.SetSize(Geometry::NUM_GEOMETRIES);
1902 for (int i=0; i<old_DoFTransArray.Size(); i++)
1903 {
1904 old_DoFTransArray[i] = NULL;
1905 }
1906
1907 const FiniteElementCollection *fec_ref = fespace->FEColl();
1908 if (dynamic_cast<const ND_FECollection*>(fec_ref))
1909 {
1910 const FiniteElement *nd_tri =
1912 if (nd_tri)
1913 {
1914 old_DoFTransArray[Geometry::TRIANGLE] =
1915 new ND_TriDofTransformation(nd_tri->GetOrder());
1916 }
1917
1918 const FiniteElement *nd_tet =
1920 if (nd_tet)
1921 {
1922 old_DoFTransArray[Geometry::TETRAHEDRON] =
1923 new ND_TetDofTransformation(nd_tet->GetOrder());
1924 }
1925
1926 const FiniteElement *nd_pri =
1928 if (nd_pri)
1929 {
1930 old_DoFTransArray[Geometry::PRISM] =
1931 new ND_WedgeDofTransformation(nd_pri->GetOrder());
1932 }
1933
1934 const FiniteElement *nd_pyr =
1936 if (nd_pyr)
1937 {
1938 old_DoFTransArray[Geometry::PYRAMID] =
1939 new ND_PyramidDofTransformation(nd_pyr->GetOrder());
1940 }
1941 }
1942}
1943
1945 Vector &y) const
1946{
1947 Mesh* mesh_ref = fespace->GetMesh();
1948 const CoarseFineTransformations &trans_ref =
1949 mesh_ref->GetRefinementTransforms();
1950
1951 Array<int> dofs, vdofs, old_dofs, old_vdofs, old_Fo;
1952
1953 int rvdim = fespace->GetVDim();
1954 int old_ndofs = width / rvdim;
1955
1956 Vector subY, subX;
1957
1958 DenseMatrix eP;
1960 DofTransformation doftrans;
1961
1962 for (int k = 0; k < mesh_ref->GetNE(); k++)
1963 {
1964 const Embedding &emb = trans_ref.embeddings[k];
1965 const Geometry::Type geom = mesh_ref->GetElementBaseGeometry(k);
1966 if (fespace->IsVariableOrder())
1967 {
1968 const FiniteElement *fe = fespace->GetFE(k);
1969 isotr.SetIdentityTransformation(geom);
1970 const int ldof = fe->GetDof();
1971 eP.SetSize(ldof, ldof);
1972 const DenseTensor &pmats = trans_ref.point_matrices[geom];
1973 isotr.SetPointMat(pmats(emb.matrix));
1974 fe->GetLocalInterpolation(isotr, eP);
1975 }
1976 const DenseMatrix &lP = (fespace->IsVariableOrder()) ? eP : localP[geom](
1977 emb.matrix);
1978
1979 subY.SetSize(lP.Height());
1980
1981 fespace->GetElementDofs(k, dofs, doftrans);
1982 old_elem_dof->GetRow(emb.parent, old_dofs);
1983
1984 if (doftrans.IsIdentity())
1985 {
1986 for (int vd = 0; vd < rvdim; vd++)
1987 {
1988 dofs.Copy(vdofs);
1989 fespace->DofsToVDofs(vd, vdofs);
1990 old_dofs.Copy(old_vdofs);
1991 fespace->DofsToVDofs(vd, old_vdofs, old_ndofs);
1992
1993 x.GetSubVector(old_vdofs, subX);
1994 lP.Mult(subX, subY);
1995 y.SetSubVector(vdofs, subY);
1996 }
1997 }
1998 else
1999 {
2000 old_elem_fos->GetRow(emb.parent, old_Fo);
2001 old_DoFTrans.SetDofTransformation(*old_DoFTransArray[geom]);
2002 old_DoFTrans.SetFaceOrientations(old_Fo);
2003
2004 doftrans.SetVDim();
2005 for (int vd = 0; vd < rvdim; vd++)
2006 {
2007 dofs.Copy(vdofs);
2008 fespace->DofsToVDofs(vd, vdofs);
2009 old_dofs.Copy(old_vdofs);
2010 fespace->DofsToVDofs(vd, old_vdofs, old_ndofs);
2011
2012 x.GetSubVector(old_vdofs, subX);
2013 old_DoFTrans.InvTransformPrimal(subX);
2014 lP.Mult(subX, subY);
2015 doftrans.TransformPrimal(subY);
2016 y.SetSubVector(vdofs, subY);
2017 }
2018 doftrans.SetVDim(rvdim, fespace->GetOrdering());
2019 }
2020 }
2021}
2022
2024 Vector &y) const
2025{
2026 y = 0.0;
2027
2028 Mesh* mesh_ref = fespace->GetMesh();
2029 const CoarseFineTransformations &trans_ref =
2030 mesh_ref->GetRefinementTransforms();
2031
2032 Array<char> processed(fespace->GetVSize());
2033 processed = 0;
2034
2035 Array<int> f_dofs, c_dofs, f_vdofs, c_vdofs, old_Fo;
2036
2037 int rvdim = fespace->GetVDim();
2038 int old_ndofs = width / rvdim;
2039
2040 Vector subY, subX, subYt;
2041
2042 DenseMatrix eP;
2044 const FiniteElement *fe = nullptr;
2045 DofTransformation doftrans;
2046
2047 for (int k = 0; k < mesh_ref->GetNE(); k++)
2048 {
2049 const Embedding &emb = trans_ref.embeddings[k];
2050 const Geometry::Type geom = mesh_ref->GetElementBaseGeometry(k);
2051
2052 if (fespace->IsVariableOrder())
2053 {
2054 fe = fespace->GetFE(k);
2055 isotr.SetIdentityTransformation(geom);
2056 const int ldof = fe->GetDof();
2057 eP.SetSize(ldof);
2058 const DenseTensor &pmats = trans_ref.point_matrices[geom];
2059 isotr.SetPointMat(pmats(emb.matrix));
2060 fe->GetLocalInterpolation(isotr, eP);
2061 }
2062
2063 const DenseMatrix &lP = (fespace->IsVariableOrder()) ? eP : localP[geom](
2064 emb.matrix);
2065
2066 fespace->GetElementDofs(k, f_dofs, doftrans);
2067 old_elem_dof->GetRow(emb.parent, c_dofs);
2068
2069 if (doftrans.IsIdentity())
2070 {
2071 subY.SetSize(lP.Width());
2072
2073 for (int vd = 0; vd < rvdim; vd++)
2074 {
2075 f_dofs.Copy(f_vdofs);
2076 fespace->DofsToVDofs(vd, f_vdofs);
2077 c_dofs.Copy(c_vdofs);
2078 fespace->DofsToVDofs(vd, c_vdofs, old_ndofs);
2079
2080 x.GetSubVector(f_vdofs, subX);
2081 for (int p = 0; p < f_dofs.Size(); ++p)
2082 {
2083 if (processed[DecodeDof(f_dofs[p])])
2084 {
2085 subX[p] = 0.0;
2086 }
2087 }
2088 lP.MultTranspose(subX, subY);
2089 y.AddElementVector(c_vdofs, subY);
2090 }
2091 }
2092 else
2093 {
2094 subYt.SetSize(lP.Width());
2095
2096 old_elem_fos->GetRow(emb.parent, old_Fo);
2097 old_DoFTrans.SetDofTransformation(*old_DoFTransArray[geom]);
2098 old_DoFTrans.SetFaceOrientations(old_Fo);
2099
2100 doftrans.SetVDim();
2101 for (int vd = 0; vd < rvdim; vd++)
2102 {
2103 f_dofs.Copy(f_vdofs);
2104 fespace->DofsToVDofs(vd, f_vdofs);
2105 c_dofs.Copy(c_vdofs);
2106 fespace->DofsToVDofs(vd, c_vdofs, old_ndofs);
2107
2108 x.GetSubVector(f_vdofs, subX);
2109 doftrans.InvTransformDual(subX);
2110 for (int p = 0; p < f_dofs.Size(); ++p)
2111 {
2112 if (processed[DecodeDof(f_dofs[p])])
2113 {
2114 subX[p] = 0.0;
2115 }
2116 }
2117 lP.MultTranspose(subX, subYt);
2118 old_DoFTrans.TransformDual(subYt);
2119 y.AddElementVector(c_vdofs, subYt);
2120 }
2121 doftrans.SetVDim(rvdim, fespace->GetOrdering());
2122 }
2123
2124 for (int p = 0; p < f_dofs.Size(); ++p)
2125 {
2126 processed[DecodeDof(f_dofs[p])] = 1;
2127 }
2128 }
2129}
2130
2131namespace internal
2132{
2133
2134// Used in GetCoarseToFineMap() below.
2135struct RefType
2136{
2137 Geometry::Type geom;
2138 int num_children;
2139 const Pair<int,int> *children;
2140
2141 RefType(Geometry::Type g, int n, const Pair<int,int> *c)
2142 : geom(g), num_children(n), children(c) { }
2143
2144 bool operator<(const RefType &other) const
2145 {
2146 if (geom < other.geom) { return true; }
2147 if (geom > other.geom) { return false; }
2148 if (num_children < other.num_children) { return true; }
2149 if (num_children > other.num_children) { return false; }
2150 for (int i = 0; i < num_children; i++)
2151 {
2152 if (children[i].one < other.children[i].one) { return true; }
2153 if (children[i].one > other.children[i].one) { return false; }
2154 }
2155 return false; // everything is equal
2156 }
2157};
2158
2159void GetCoarseToFineMap(const CoarseFineTransformations &cft,
2160 const mfem::Mesh &fine_mesh,
2161 Table &coarse_to_fine,
2162 Array<int> &coarse_to_ref_type,
2163 Table &ref_type_to_matrix,
2164 Array<Geometry::Type> &ref_type_to_geom)
2165{
2166 const int fine_ne = cft.embeddings.Size();
2167 int coarse_ne = -1;
2168 for (int i = 0; i < fine_ne; i++)
2169 {
2170 coarse_ne = std::max(coarse_ne, cft.embeddings[i].parent);
2171 }
2172 coarse_ne++;
2173
2174 coarse_to_ref_type.SetSize(coarse_ne);
2175 coarse_to_fine.SetDims(coarse_ne, fine_ne);
2176
2177 Array<int> cf_i(coarse_to_fine.GetI(), coarse_ne+1);
2178 Array<Pair<int,int> > cf_j(fine_ne);
2179 cf_i = 0;
2180 for (int i = 0; i < fine_ne; i++)
2181 {
2182 cf_i[cft.embeddings[i].parent+1]++;
2183 }
2184 cf_i.PartialSum();
2185 MFEM_ASSERT(cf_i.Last() == cf_j.Size(), "internal error");
2186 for (int i = 0; i < fine_ne; i++)
2187 {
2188 const Embedding &e = cft.embeddings[i];
2189 cf_j[cf_i[e.parent]].one = e.matrix; // used as sort key below
2190 cf_j[cf_i[e.parent]].two = i;
2191 cf_i[e.parent]++;
2192 }
2193 std::copy_backward(cf_i.begin(), cf_i.end()-1, cf_i.end());
2194 cf_i[0] = 0;
2195 for (int i = 0; i < coarse_ne; i++)
2196 {
2197 std::sort(&cf_j[cf_i[i]], cf_j.GetData() + cf_i[i+1]);
2198 }
2199 for (int i = 0; i < fine_ne; i++)
2200 {
2201 coarse_to_fine.GetJ()[i] = cf_j[i].two;
2202 }
2203
2204 using std::map;
2205 using std::pair;
2206
2207 map<RefType,int> ref_type_map;
2208 for (int i = 0; i < coarse_ne; i++)
2209 {
2210 const int num_children = cf_i[i+1]-cf_i[i];
2211 MFEM_ASSERT(num_children > 0, "");
2212 const int fine_el = cf_j[cf_i[i]].two;
2213 // Assuming the coarse and the fine elements have the same geometry:
2214 const Geometry::Type geom = fine_mesh.GetElementBaseGeometry(fine_el);
2215 const RefType ref_type(geom, num_children, &cf_j[cf_i[i]]);
2216 pair<map<RefType,int>::iterator,bool> res =
2217 ref_type_map.insert(
2218 pair<const RefType,int>(ref_type, (int)ref_type_map.size()));
2219 coarse_to_ref_type[i] = res.first->second;
2220 }
2221
2222 ref_type_to_matrix.MakeI((int)ref_type_map.size());
2223 ref_type_to_geom.SetSize((int)ref_type_map.size());
2224 for (map<RefType,int>::iterator it = ref_type_map.begin();
2225 it != ref_type_map.end(); ++it)
2226 {
2227 ref_type_to_matrix.AddColumnsInRow(it->second, it->first.num_children);
2228 ref_type_to_geom[it->second] = it->first.geom;
2229 }
2230
2231 ref_type_to_matrix.MakeJ();
2232 for (map<RefType,int>::iterator it = ref_type_map.begin();
2233 it != ref_type_map.end(); ++it)
2234 {
2235 const RefType &rt = it->first;
2236 for (int j = 0; j < rt.num_children; j++)
2237 {
2238 ref_type_to_matrix.AddConnection(it->second, rt.children[j].one);
2239 }
2240 }
2241 ref_type_to_matrix.ShiftUpI();
2242}
2243
2244} // namespace internal
2245
2246
2247/// TODO: Implement DofTransformation support
2249 const FiniteElementSpace *f_fes, const FiniteElementSpace *c_fes,
2250 BilinearFormIntegrator *mass_integ)
2251 : Operator(c_fes->GetVSize(), f_fes->GetVSize()),
2252 fine_fes(f_fes)
2253{
2254 MFEM_VERIFY(c_fes->GetOrdering() == f_fes->GetOrdering() &&
2255 c_fes->GetVDim() == f_fes->GetVDim(),
2256 "incompatible coarse and fine FE spaces");
2257
2259 Mesh *f_mesh = f_fes->GetMesh();
2260 const CoarseFineTransformations &rtrans = f_mesh->GetRefinementTransforms();
2261
2262 Mesh::GeometryList elem_geoms(*f_mesh);
2264 for (int gi = 0; gi < elem_geoms.Size(); gi++)
2265 {
2266 const Geometry::Type geom = elem_geoms[gi];
2267 DenseTensor &lP = localP[geom], &lM = localM[geom];
2268 const FiniteElement *fine_fe =
2269 f_fes->fec->FiniteElementForGeometry(geom);
2270 const FiniteElement *coarse_fe =
2271 c_fes->fec->FiniteElementForGeometry(geom);
2272 const DenseTensor &pmats = rtrans.point_matrices[geom];
2273
2274 lP.SetSize(fine_fe->GetDof(), coarse_fe->GetDof(), pmats.SizeK());
2275 lM.SetSize(fine_fe->GetDof(), fine_fe->GetDof(), pmats.SizeK());
2276 emb_tr.SetIdentityTransformation(geom);
2277 for (int i = 0; i < pmats.SizeK(); i++)
2278 {
2279 emb_tr.SetPointMat(pmats(i));
2280 // Get the local interpolation matrix for this refinement type
2281 fine_fe->GetTransferMatrix(*coarse_fe, emb_tr, lP(i));
2282 // Get the local mass matrix for this refinement type
2283 mass_integ->AssembleElementMatrix(*fine_fe, emb_tr, lM(i));
2284 }
2285 }
2286
2287 Table ref_type_to_matrix;
2288 internal::GetCoarseToFineMap(rtrans, *f_mesh, coarse_to_fine,
2289 coarse_to_ref_type, ref_type_to_matrix,
2290 ref_type_to_geom);
2291 MFEM_ASSERT(coarse_to_fine.Size() == c_fes->GetNE(), "");
2292
2293 const int total_ref_types = ref_type_to_geom.Size();
2294 int num_ref_types[Geometry::NumGeom], num_fine_elems[Geometry::NumGeom];
2295 Array<int> ref_type_to_coarse_elem_offset(total_ref_types);
2296 ref_type_to_fine_elem_offset.SetSize(total_ref_types);
2297 std::fill(num_ref_types, num_ref_types+Geometry::NumGeom, 0);
2298 std::fill(num_fine_elems, num_fine_elems+Geometry::NumGeom, 0);
2299 for (int i = 0; i < total_ref_types; i++)
2300 {
2301 Geometry::Type g = ref_type_to_geom[i];
2302 ref_type_to_coarse_elem_offset[i] = num_ref_types[g];
2303 ref_type_to_fine_elem_offset[i] = num_fine_elems[g];
2304 num_ref_types[g]++;
2305 num_fine_elems[g] += ref_type_to_matrix.RowSize(i);
2306 }
2307 DenseTensor localPtMP[Geometry::NumGeom];
2308 for (int g = 0; g < Geometry::NumGeom; g++)
2309 {
2310 if (num_ref_types[g] == 0) { continue; }
2311 const int fine_dofs = localP[g].SizeI();
2312 const int coarse_dofs = localP[g].SizeJ();
2313 localPtMP[g].SetSize(coarse_dofs, coarse_dofs, num_ref_types[g]);
2314 localR[g].SetSize(coarse_dofs, fine_dofs, num_fine_elems[g]);
2315 }
2316 for (int i = 0; i < total_ref_types; i++)
2317 {
2318 Geometry::Type g = ref_type_to_geom[i];
2319 DenseMatrix &lPtMP = localPtMP[g](ref_type_to_coarse_elem_offset[i]);
2320 int lR_offset = ref_type_to_fine_elem_offset[i]; // offset in localR[g]
2321 const int *mi = ref_type_to_matrix.GetRow(i);
2322 const int nm = ref_type_to_matrix.RowSize(i);
2323 lPtMP = 0.0;
2324 for (int s = 0; s < nm; s++)
2325 {
2326 DenseMatrix &lP = localP[g](mi[s]);
2327 DenseMatrix &lM = localM[g](mi[s]);
2328 DenseMatrix &lR = localR[g](lR_offset+s);
2329 MultAtB(lP, lM, lR); // lR = lP^T lM
2330 mfem::AddMult(lR, lP, lPtMP); // lPtMP += lP^T lM lP
2331 }
2332 DenseMatrixInverse lPtMP_inv(lPtMP);
2333 for (int s = 0; s < nm; s++)
2334 {
2335 DenseMatrix &lR = localR[g](lR_offset+s);
2336 lPtMP_inv.Mult(lR); // lR <- (P^T M P)^{-1} P^T M
2337 }
2338 }
2339
2340 // Make a copy of the coarse element-to-dof Table.
2341 coarse_elem_dof = new Table(c_fes->GetElementToDofTable());
2342}
2343
2348
2350 Vector &y) const
2351{
2352 Array<int> c_vdofs, f_vdofs;
2353 Vector loc_x, loc_y;
2354 DenseMatrix loc_x_mat, loc_y_mat;
2355 const int fine_vdim = fine_fes->GetVDim();
2356 const int coarse_ndofs = height/fine_vdim;
2357 for (int coarse_el = 0; coarse_el < coarse_to_fine.Size(); coarse_el++)
2358 {
2359 coarse_elem_dof->GetRow(coarse_el, c_vdofs);
2360 fine_fes->DofsToVDofs(c_vdofs, coarse_ndofs);
2361 loc_y.SetSize(c_vdofs.Size());
2362 loc_y = 0.0;
2363 loc_y_mat.UseExternalData(loc_y.GetData(), c_vdofs.Size()/fine_vdim,
2364 fine_vdim);
2365 const int ref_type = coarse_to_ref_type[coarse_el];
2366 const Geometry::Type geom = ref_type_to_geom[ref_type];
2367 const int *fine_elems = coarse_to_fine.GetRow(coarse_el);
2368 const int num_fine_elems = coarse_to_fine.RowSize(coarse_el);
2369 const int lR_offset = ref_type_to_fine_elem_offset[ref_type];
2370 for (int s = 0; s < num_fine_elems; s++)
2371 {
2372 const DenseMatrix &lR = localR[geom](lR_offset+s);
2373 fine_fes->GetElementVDofs(fine_elems[s], f_vdofs);
2374 x.GetSubVector(f_vdofs, loc_x);
2375 loc_x_mat.UseExternalData(loc_x.GetData(), f_vdofs.Size()/fine_vdim,
2376 fine_vdim);
2377 mfem::AddMult(lR, loc_x_mat, loc_y_mat);
2378 }
2379 y.SetSubVector(c_vdofs, loc_y);
2380 }
2381}
2382
2384 DenseTensor &localR) const
2385{
2386 const FiniteElement *fe = fec->FiniteElementForGeometry(geom);
2387
2388 const CoarseFineTransformations &dtrans =
2390 const DenseTensor &pmats = dtrans.point_matrices[geom];
2391
2392 const int nmat = pmats.SizeK();
2393 const int ldof = fe->GetDof();
2394
2396 isotr.SetIdentityTransformation(geom);
2397
2398 // calculate local restriction matrices for all refinement types
2399 localR.SetSize(ldof, ldof, nmat);
2400 for (int i = 0; i < nmat; i++)
2401 {
2402 isotr.SetPointMat(pmats(i));
2403 fe->GetLocalRestriction(isotr, localR(i));
2404 }
2405}
2406
2408 const Table* old_elem_dof,
2409 const Table* old_elem_fos)
2410{
2411 /// TODO: Implement DofTransformation support
2412
2413 MFEM_VERIFY(Nonconforming(), "Not implemented for conforming meshes.");
2414 MFEM_VERIFY(old_ndofs, "Missing previous (finer) space.");
2415 MFEM_VERIFY(ndofs <= old_ndofs, "Previous space is not finer.");
2416
2417 Array<int> dofs, old_dofs, old_vdofs;
2418 Vector row;
2419
2420 Mesh::GeometryList elem_geoms(*mesh);
2421
2423 if (!IsVariableOrder())
2424 {
2425 for (int i = 0; i < elem_geoms.Size(); i++)
2426 {
2427 GetLocalDerefinementMatrices(elem_geoms[i], localR[elem_geoms[i]]);
2428 }
2429 }
2430
2431 SparseMatrix *R = new SparseMatrix(ndofs*vdim, old_ndofs*vdim);
2432
2433 Array<int> mark(R->Height());
2434 mark = 0;
2435
2436 const CoarseFineTransformations &dtrans =
2438
2439 MFEM_ASSERT(dtrans.embeddings.Size() == old_elem_dof->Size(), "");
2440
2442 int num_marked = 0;
2443 const FiniteElement *fe = nullptr;
2444 DenseMatrix localRVO; //for variable-order only
2445 for (int k = 0; k < dtrans.embeddings.Size(); k++)
2446 {
2447 const Embedding &emb = dtrans.embeddings[k];
2449
2450 if (IsVariableOrder())
2451 {
2452 fe = GetFE(emb.parent);
2453 const DenseTensor &pmats = dtrans.point_matrices[geom];
2454 const int ldof = fe->GetDof();
2455
2457 isotr.SetIdentityTransformation(geom);
2458
2459 localRVO.SetSize(ldof, ldof);
2460 isotr.SetPointMat(pmats(emb.matrix));
2461 // Local restriction is size ldofxldof assuming that the parent and
2462 // child are of same polynomial order.
2463 fe->GetLocalRestriction(isotr, localRVO);
2464 }
2465 DenseMatrix &lR = IsVariableOrder() ? localRVO : localR[geom](emb.matrix);
2466
2467 elem_dof->GetRow(emb.parent, dofs);
2468 old_elem_dof->GetRow(k, old_dofs);
2469 MFEM_VERIFY(old_dofs.Size() == dofs.Size(),
2470 "Parent and child must have same #dofs.");
2471
2472 for (int vd = 0; vd < vdim; vd++)
2473 {
2474 old_dofs.Copy(old_vdofs);
2475 DofsToVDofs(vd, old_vdofs, old_ndofs);
2476
2477 for (int i = 0; i < lR.Height(); i++)
2478 {
2479 if (!std::isfinite(lR(i, 0))) { continue; }
2480
2481 const int r = DofToVDof(dofs[i], vd);
2482 const int m = UnsignIndex(r);
2483
2484 if (is_dg || !mark[m])
2485 {
2486 lR.GetRow(i, row);
2487 R->SetRow(r, old_vdofs, row);
2488
2489 mark[m] = 1;
2490 num_marked++;
2491 }
2492 }
2493 }
2494 }
2495
2496 if (!is_dg && !IsVariableOrder())
2497 {
2498 MFEM_VERIFY(num_marked == R->Height(),
2499 "internal error: not all rows of R were set.");
2500 }
2501
2502 R->Finalize(); // no-op if fixed width
2503 return R;
2504}
2505
2507 const FiniteElementSpace &coarse_fes, Geometry::Type geom,
2508 DenseTensor &localP) const
2509{
2510 // Assumptions: see the declaration of the method.
2511
2512 const FiniteElement *fine_fe = fec->FiniteElementForGeometry(geom);
2513 const FiniteElement *coarse_fe =
2514 coarse_fes.fec->FiniteElementForGeometry(geom);
2515
2517 const DenseTensor &pmats = rtrans.point_matrices[geom];
2518
2519 int nmat = pmats.SizeK();
2520
2522 isotr.SetIdentityTransformation(geom);
2523
2524 // Calculate the local interpolation matrices for all refinement types
2525 localP.SetSize(fine_fe->GetDof(), coarse_fe->GetDof(), nmat);
2526 for (int i = 0; i < nmat; i++)
2527 {
2528 isotr.SetPointMat(pmats(i));
2529 fine_fe->GetTransferMatrix(*coarse_fe, isotr, localP(i));
2530 }
2531}
2532
2534 const FiniteElementCollection *fec_,
2535 int vdim_, int ordering_)
2536{
2537 mesh = mesh_;
2538 fec = fec_;
2539 vdim = vdim_;
2540 ordering = (Ordering::Type) ordering_;
2541
2542 elem_dof = NULL;
2543 elem_fos = NULL;
2544 face_dof = NULL;
2545
2546 sequence = 0;
2547 orders_changed = false;
2548 relaxed_hp = false;
2549
2551
2552 const NURBSFECollection *nurbs_fec =
2553 dynamic_cast<const NURBSFECollection *>(fec_);
2554
2555 if (nurbs_fec)
2556 {
2557 MFEM_VERIFY(mesh_->NURBSext, "NURBS FE space requires a NURBS mesh.");
2558
2559 if (NURBSext_ == NULL)
2560 {
2561 NURBSext = mesh_->NURBSext;
2562 own_ext = 0;
2563 }
2564 else
2565 {
2566 NURBSext = NURBSext_;
2567 own_ext = 1;
2568 }
2569 UpdateNURBS();
2570 cP.reset();
2571 cR.reset();
2572 cR_hp.reset();
2573 R_transpose.reset();
2574 cP_is_set = false;
2575
2577 }
2578 else
2579 {
2580 NURBSext = NULL;
2581 own_ext = 0;
2582 Construct();
2583 }
2584
2586}
2587
2589{
2591
2593 for (int i=0; i<DoFTransArray.Size(); i++)
2594 {
2595 DoFTransArray[i] = NULL;
2596 }
2597 if (mesh->Dimension() < 3) { return; }
2598 if (dynamic_cast<const ND_FECollection*>(fec))
2599 {
2600 const FiniteElement *nd_tri =
2602 if (nd_tri)
2603 {
2605 new ND_TriDofTransformation(nd_tri->GetOrder());
2606 }
2607
2608 const FiniteElement *nd_tet =
2610 if (nd_tet)
2611 {
2613 new ND_TetDofTransformation(nd_tet->GetOrder());
2614 }
2615
2616 const FiniteElement *nd_pri =
2618 if (nd_pri)
2619 {
2621 new ND_WedgeDofTransformation(nd_pri->GetOrder());
2622 }
2623
2624 const FiniteElement *nd_pyr =
2626 if (nd_pyr)
2627 {
2630 }
2631 }
2632}
2633
2635{
2636 if (NURBSext && !own_ext)
2637 {
2638 mfem_error("FiniteElementSpace::StealNURBSext");
2639 }
2640 own_ext = 0;
2641
2642 return NURBSext;
2643}
2644
2646{
2647 MFEM_VERIFY(NURBSext, "NURBSExt not defined.");
2648
2649 nvdofs = 0;
2650 nedofs = 0;
2651 nfdofs = 0;
2652 nbdofs = 0;
2653 bdofs = NULL;
2654
2655 delete face_dof;
2656 face_dof = NULL;
2658
2659 // Depending on the element type create the appropriate extensions
2660 // for the individual components.
2661 dynamic_cast<const NURBSFECollection *>(fec)->Reset();
2662
2663 if (dynamic_cast<const NURBS_HDivFECollection *>(fec))
2664 {
2665 VNURBSext.SetSize(mesh->Dimension());
2666 for (int d = 0; d < mesh->Dimension(); d++)
2667 {
2669 }
2670 }
2671
2672 if (dynamic_cast<const NURBS_HCurlFECollection *>(fec))
2673 {
2674 VNURBSext.SetSize(mesh->Dimension());
2675 for (int d = 0; d < mesh->Dimension(); d++)
2676 {
2678 }
2679 }
2680
2681 // If required: concatenate the dof tables of the individual components into
2682 // one dof table for the vector fespace.
2683 if (VNURBSext.Size() == 2)
2684 {
2685 int offset1 = VNURBSext[0]->GetNDof();
2686 ndofs = VNURBSext[0]->GetNDof() + VNURBSext[1]->GetNDof();
2687
2688 // Merge Tables
2689 elem_dof = new Table(*VNURBSext[0]->GetElementDofTable(),
2690 *VNURBSext[1]->GetElementDofTable(),offset1 );
2691
2692 bdr_elem_dof = new Table(*VNURBSext[0]->GetBdrElementDofTable(),
2693 *VNURBSext[1]->GetBdrElementDofTable(),offset1);
2694 }
2695 else if (VNURBSext.Size() == 3)
2696 {
2697 int offset1 = VNURBSext[0]->GetNDof();
2698 int offset2 = offset1 + VNURBSext[1]->GetNDof();
2699 ndofs = offset2 + VNURBSext[2]->GetNDof();
2700
2701 // Merge Tables
2702 elem_dof = new Table(*VNURBSext[0]->GetElementDofTable(),
2703 *VNURBSext[1]->GetElementDofTable(),offset1,
2704 *VNURBSext[2]->GetElementDofTable(),offset2);
2705
2706 bdr_elem_dof = new Table(*VNURBSext[0]->GetBdrElementDofTable(),
2707 *VNURBSext[1]->GetBdrElementDofTable(),offset1,
2708 *VNURBSext[2]->GetBdrElementDofTable(),offset2);
2709 }
2710 else
2711 {
2712 ndofs = NURBSext->GetNDof();
2715 }
2717 sequence++;
2718}
2719
2721{
2722 if (face_dof) { return; }
2723
2724 const int dim = mesh->Dimension();
2725
2726 // Find bdr to face mapping
2728 face_to_be = -1;
2729 for (int b = 0; b < GetNBE(); b++)
2730 {
2732 face_to_be[f] = b;
2733 }
2734
2735 // Loop over faces in correct order, to prevent a sort
2736 // Sort will destroy orientation info in ordering of dofs
2737 Array<Connection> face_dof_list;
2738 Array<int> row;
2739 for (int f = 0; f < GetNF(); f++)
2740 {
2741 int b = face_to_be[f];
2742 if (b == -1) { continue; }
2743 // FIXME: this assumes that the boundary element and the face element have
2744 // the same orientation.
2745 if (dim > 1)
2746 {
2747 const Element *fe = mesh->GetFace(f);
2748 const Element *be = mesh->GetBdrElement(b);
2749 const int nv = be->GetNVertices();
2750 const int *fv = fe->GetVertices();
2751 const int *bv = be->GetVertices();
2752 for (int i = 0; i < nv; i++)
2753 {
2754 MFEM_VERIFY(fv[i] == bv[i],
2755 "non-matching face and boundary elements detected!");
2756 }
2757 }
2758 GetBdrElementDofs(b, row);
2759 Connection conn(f,0);
2760 for (int i = 0; i < row.Size(); i++)
2761 {
2762 conn.to = row[i];
2763 face_dof_list.Append(conn);
2764 }
2765 }
2766 face_dof = new Table(GetNF(), face_dof_list);
2767}
2768
2770{
2771 // This method should be used only for non-NURBS spaces.
2772 MFEM_VERIFY(!NURBSext, "internal error");
2773
2774 // Variable-order space needs a nontrivial P matrix + also ghost elements
2775 // in parallel, we thus require the mesh to be NC.
2776 MFEM_VERIFY(!IsVariableOrder() || Nonconforming(),
2777 "Variable-order space requires a nonconforming mesh.");
2778
2779 elem_dof = NULL;
2780 elem_fos = NULL;
2781 bdr_elem_dof = NULL;
2782 bdr_elem_fos = NULL;
2783 face_dof = NULL;
2784
2785 ndofs = 0;
2786 nvdofs = nedofs = nfdofs = nbdofs = 0;
2787 bdofs = NULL;
2788
2789 cP.reset();
2790 cR.reset();
2791 cR_hp.reset();
2792 cP_is_set = false;
2793 R_transpose.reset();
2794 // 'Th' is initialized/destroyed before this method is called.
2795
2796 int dim = mesh->Dimension();
2797 int order = fec->GetOrder();
2798
2799 MFEM_VERIFY((mesh->GetNumGeometries(dim) > 0) || (mesh->GetNE() == 0),
2800 "Mesh was not correctly finalized.");
2801
2802 bool mixed_elements = (mesh->GetNumGeometries(dim) > 1);
2803 bool mixed_faces = (dim > 2 && mesh->GetNumGeometries(2) > 1);
2804
2805 Array<VarOrderBits> edge_orders, face_orders, edge_elem_orders,
2806 face_elem_orders;
2807
2808 if (IsVariableOrder())
2809 {
2810 // for variable-order spaces, calculate orders of edges and faces
2811 CalcEdgeFaceVarOrders(edge_orders, face_orders, edge_elem_orders,
2812 face_elem_orders, skip_edge, skip_face);
2813 }
2814 else if (mixed_faces)
2815 {
2816 // for mixed faces we also create the var_face_dofs table, see below
2817 face_orders.SetSize(mesh->GetNFaces());
2818 face_orders = (VarOrderBits(1) << order);
2819 }
2820
2821 // assign vertex DOFs
2822 if (mesh->GetNV())
2823 {
2824 nvdofs = mesh->GetNV() * fec->GetNumDof(Geometry::POINT, order);
2825 }
2826
2827 // assign edge DOFs
2828 if (mesh->GetNEdges())
2829 {
2830 if (IsVariableOrder())
2831 {
2832 nedofs = MakeDofTable(1, edge_orders, var_edge_dofs, &var_edge_orders);
2833 MakeDofTable(1, edge_elem_orders, loc_var_edge_dofs,
2835 // Set lnedofs from the last row of loc_var_edge_dofs
2836 Array<int> lastRow;
2838 MFEM_ASSERT(lastRow.Size() == 1, "");
2839 lnedofs = lastRow[0];
2840 }
2841 else
2842 {
2843 // the simple case: all edges are of the same order
2845 var_edge_dofs.Clear(); // ensure any old var_edge_dof table is dumped.
2846 }
2847 }
2848
2849 // assign face DOFs
2850 if (mesh->GetNFaces())
2851 {
2852 if (IsVariableOrder() || mixed_faces)
2853 {
2854 // NOTE: for simplicity, we also use Table var_face_dofs for mixed faces
2855 nfdofs = MakeDofTable(2, face_orders, var_face_dofs,
2856 IsVariableOrder() ? &var_face_orders : NULL);
2857 uni_fdof = -1;
2858
2859 if (IsVariableOrder())
2860 {
2861 MakeDofTable(2, face_elem_orders, loc_var_face_dofs,
2863 // Set lnfdofs from the last row of loc_var_face_dofs
2864 Array<int> lastRow;
2866 MFEM_ASSERT(lastRow.Size() == 1, "");
2867 lnfdofs = lastRow[0];
2868 }
2869 }
2870 else
2871 {
2872 // the simple case: all faces are of the same geometry and order
2875 var_face_dofs.Clear(); // ensure any old var_face_dof table is dumped.
2876 }
2877 }
2878
2879 // assign internal ("bubble") DOFs
2880 if (mesh->GetNE() && dim > 0)
2881 {
2882 if (IsVariableOrder() || mixed_elements)
2883 {
2884 bdofs = new int[mesh->GetNE()+1];
2885 bdofs[0] = 0;
2886 for (int i = 0; i < mesh->GetNE(); i++)
2887 {
2888 int p = GetElementOrderImpl(i);
2890 bdofs[i+1] = nbdofs;
2891 }
2892 }
2893 else
2894 {
2895 // the simple case: all elements are the same
2896 bdofs = NULL;
2898 nbdofs = mesh->GetNE() * fec->GetNumDof(geom, order);
2899 }
2900 }
2901
2903
2905
2906 // record the current mesh sequence number to detect refinement etc.
2908
2909 // increment our sequence number to let GridFunctions know they need updating
2910 sequence++;
2911
2912 // DOFs are now assigned according to current element orders
2913 orders_changed = false;
2914
2915 // Do not build elem_dof Table here: in parallel it has to be constructed
2916 // later.
2917}
2918
2919void DofMapHelper(int entity, const Table & var_ent_dofs,
2920 const Table & loc_var_ent_dofs,
2921 const Array<char> & var_ent_orders,
2922 const Array<char> & loc_var_ent_orders,
2923 Array<int> & all2local, int & ndof_all, int & ndof_loc)
2924{
2925 const int osall0 = var_ent_dofs.GetI()[entity];
2926 const int osall1 = var_ent_dofs.GetI()[entity + 1];
2927
2928 const int osloc0 = loc_var_ent_dofs.GetI()[entity];
2929 const int osloc1 = loc_var_ent_dofs.GetI()[entity + 1];
2930
2931 // loc_var_ent_orders must be a subset of var_ent_orders
2932 int j = osall0;
2933 for (int i=osloc0; i<osloc1; ++i) // Loop over local variants
2934 {
2935 const int order = loc_var_ent_orders[i];
2936 // Find the variant in var_ent_orders with the same order
2937 int na = var_ent_dofs.GetJ()[j + 1] - var_ent_dofs.GetJ()[j];
2938 while (var_ent_orders[j] != order && j < osall1 - 1)
2939 {
2940 j++;
2941 ndof_all += na;
2942 na = var_ent_dofs.GetJ()[j + 1] - var_ent_dofs.GetJ()[j];
2943 }
2944
2945 MFEM_ASSERT(var_ent_orders[j] == order, "");
2946
2947 const int n = loc_var_ent_dofs.GetJ()[i + 1] - loc_var_ent_dofs.GetJ()[i];
2948
2949 MFEM_ASSERT(n == na &&
2950 n == var_ent_dofs.GetJ()[j + 1] - var_ent_dofs.GetJ()[j], "");
2951
2952 for (int k=0; k<n; ++k) { all2local[ndof_all + k] = ndof_loc + k; }
2953
2954 ndof_loc += n;
2955 ndof_all += na;
2956 j++;
2957 }
2958
2959 // Reach the end of all variants for ndof_all
2960 while (j < osall1)
2961 {
2962 const int na = var_ent_dofs.GetJ()[j + 1] - var_ent_dofs.GetJ()[j];
2963 ndof_all += na;
2964 j++;
2965 }
2966}
2967
2969{
2970 if (!IsVariableOrder()) { return; }
2971
2972 // Set a map from all DOFs to local DOFs
2974 all2local = -1;
2975
2976 // Vertex DOFs simply have the identity mapping
2977 for (int i=0; i<nvdofs; ++i)
2978 {
2979 all2local[i] = i;
2980 }
2981
2982 // Redefine local edge DOFs
2983 int ndof_all = nvdofs;
2984 int ndof_loc = nvdofs;
2985 if (mesh->GetNEdges())
2986 {
2987 for (int edge=0; edge<mesh->GetNEdges(); ++edge)
2988 {
2990 loc_var_edge_orders, all2local, ndof_all, ndof_loc);
2991 }
2992
2993 MFEM_ASSERT(ndof_loc - nvdofs == lnedofs, "");
2994 nedofs = lnedofs;
2995 }
2996
2997 // Redefine local face DOFs
2998 if (mesh->GetNFaces())
2999 {
3000 for (int face=0; face<mesh->GetNFaces(); ++face)
3001 {
3003 loc_var_face_orders, all2local, ndof_all, ndof_loc);
3004 }
3005
3006 MFEM_ASSERT(ndof_loc - nvdofs - lnedofs == lnfdofs, "");
3007 nfdofs = lnfdofs;
3008 }
3009
3010 // The remaining DOFs simply have the identity mapping
3011 for (int i=ndof_all; i<ndofs; ++i)
3012 {
3013 all2local[i] = ndof_loc + i - ndof_all;
3014 }
3015
3017}
3018
3020{
3021 MFEM_ASSERT(bits != 0, "invalid bit mask");
3022 for (int order = 0; bits != 0; order++, bits >>= 1)
3023 {
3024 if (bits & 1) { return order; }
3025 }
3026 return 0;
3027}
3028
3029// For the serial FiniteElementSpace, there are no ghost elements, and this
3030// function just sets the sizes of edge_orders and face_orders, initializing to
3031// 0.
3033 Array<VarOrderBits> &edge_orders,
3034 Array<VarOrderBits> &face_orders) const
3035{
3036 edge_orders.SetSize(mesh->GetNEdges());
3037 face_orders.SetSize(mesh->GetNFaces());
3038
3039 edge_orders = 0;
3040 face_orders = 0;
3041}
3042
3044 Array<VarOrderBits> &edge_orders, Array<VarOrderBits> &face_orders,
3045 Array<VarOrderBits> &edge_elem_orders, Array<VarOrderBits> &face_elem_orders,
3046 Array<bool> &skip_edges, Array<bool> &skip_faces) const
3047{
3048 MFEM_ASSERT(Nonconforming(), "");
3049
3050 const bool localVar = elem_order.Size() == mesh->GetNE();
3051 const int baseOrder = fec->GetOrder();
3052
3053 ApplyGhostElementOrdersToEdgesAndFaces(edge_orders, face_orders);
3054
3055 edge_elem_orders.SetSize(mesh->GetNEdges());
3056 face_elem_orders.SetSize(mesh->GetNFaces());
3057
3058 edge_elem_orders = 0;
3059 face_elem_orders = 0;
3060
3063
3066
3067 // Calculate initial edge/face orders, as required by incident elements.
3068 // For each edge/face we accumulate in a bit-mask the orders of elements
3069 // sharing the edge/face.
3070 Array<int> E, F, ori;
3071 for (int i = 0; i < mesh->GetNE(); i++)
3072 {
3073 const int order = localVar ? elem_order[i] : baseOrder;
3074 MFEM_ASSERT(order <= MaxVarOrder, "");
3075 const VarOrderBits mask = (VarOrderBits(1) << order);
3076
3077 mesh->GetElementEdges(i, E, ori);
3078 for (int j = 0; j < E.Size(); j++)
3079 {
3080 edge_orders[E[j]] |= mask;
3081 edge_elem_orders[E[j]] |= mask;
3082
3083 if (order < edge_min_nghb_order[E[j]])
3084 {
3085 edge_min_nghb_order[E[j]] = order;
3086 }
3087 }
3088
3089 if (mesh->Dimension() > 2)
3090 {
3091 mesh->GetElementFaces(i, F, ori);
3092 for (int j = 0; j < F.Size(); j++)
3093 {
3094 face_orders[F[j]] |= mask;
3095 face_elem_orders[F[j]] |= mask;
3096
3097 if (order < face_min_nghb_order[F[j]])
3098 {
3099 face_min_nghb_order[F[j]] = order;
3100 }
3101 }
3102 }
3103 }
3104
3105 if (relaxed_hp)
3106 {
3107 // for relaxed conformity we don't need the masters to match the minimum
3108 // orders of the slaves, we can stop now
3109 return;
3110 }
3111
3112 // Iterate while minimum orders propagate by master/slave relations
3113 // (and new orders also propagate from faces to incident edges).
3114 // See https://github.com/mfem/mfem/pull/1423#issuecomment-638930559
3115 // for an illustration of why this is necessary in hp meshes.
3116 bool done;
3117 do
3118 {
3119 std::set<int> changedEdges;
3120 std::set<int> changedFaces;
3121
3122 const int numEdges = mesh->GetNEdges();
3123
3124 // Propagate from slave edges to master edges
3125 const NCMesh::NCList &edge_list = mesh->ncmesh->GetEdgeList();
3126 for (const NCMesh::Master &master : edge_list.masters)
3127 {
3128 VarOrderBits slave_orders = 0;
3129 for (int i = master.slaves_begin; i < master.slaves_end; i++)
3130 {
3131 slave_orders |= edge_orders[edge_list.slaves[i].index];
3132 }
3133
3134 if (slave_orders == 0)
3135 {
3136 continue;
3137 }
3138
3139 const int min_order_slaves = MinOrder(slave_orders);
3140 if (edge_orders[master.index] == 0 ||
3141 min_order_slaves < MinOrder(edge_orders[master.index]))
3142 {
3143 edge_orders[master.index] |= VarOrderBits(1) << min_order_slaves;
3144 changedEdges.insert(master.index);
3145 }
3146
3147 // Also apply the minimum order to all the slave edges, since they must
3148 // interpolate the master edge, which has the minimum order.
3149 const VarOrderBits min_mask = VarOrderBits(1) << MinOrder(
3150 edge_orders[master.index]);
3151 for (int i = master.slaves_begin; i < master.slaves_end; i++)
3152 {
3153 if (edge_list.slaves[i].index >= numEdges)
3154 {
3155 continue; // Skip ghost edges
3156 }
3157
3158 const VarOrderBits eo0 = edge_orders[edge_list.slaves[i].index];
3159 edge_orders[edge_list.slaves[i].index] |= min_mask;
3160 if (eo0 != edge_orders[edge_list.slaves[i].index])
3161 {
3162 changedEdges.insert(edge_list.slaves[i].index);
3163 }
3164 }
3165 }
3166
3167 // Propagate from slave faces(+edges) to master faces.
3168 const int numFaces = mesh->GetNumFaces();
3169
3170 const NCMesh::NCList &face_list = mesh->ncmesh->GetFaceList();
3171
3172 for (const NCMesh::Master &master : face_list.masters)
3173 {
3174 VarOrderBits slave_orders = 0;
3175
3176 for (int i = master.slaves_begin; i < master.slaves_end; i++)
3177 {
3178 const NCMesh::Slave &slave = face_list.slaves[i];
3179
3180 if (slave.index >= 0)
3181 {
3182 // Note that master.index >= numFaces occurs for ghost master faces.
3183
3184 slave_orders |= face_orders[slave.index];
3185
3186 if (slave.index >= numFaces)
3187 {
3188 continue; // Skip ghost faces
3189 }
3190
3191 mesh->GetFaceEdges(slave.index, E, ori);
3192 for (int j = 0; j < E.Size(); j++)
3193 {
3194 slave_orders |= edge_orders[E[j]];
3195 }
3196 }
3197 else
3198 {
3199 // degenerate face (i.e., edge-face constraint)
3200 slave_orders |= edge_orders[FlipIndexSign(slave.index)];
3201 }
3202 }
3203
3204 if (slave_orders == 0)
3205 {
3206 continue;
3207 }
3208
3209 const int min_order_slaves = MinOrder(slave_orders);
3210 if (face_orders[master.index] == 0 ||
3211 min_order_slaves < MinOrder(face_orders[master.index]))
3212 {
3213 face_orders[master.index] |= VarOrderBits(1) << min_order_slaves;
3214 changedFaces.insert(master.index);
3215 }
3216
3217 // Also apply the minimum order to all the slave faces, since they must
3218 // interpolate the master face, which has the minimum order.
3219 const VarOrderBits min_mask =
3220 VarOrderBits(1) << MinOrder(face_orders[master.index]);
3221 for (int i = master.slaves_begin; i < master.slaves_end; i++)
3222 {
3223 const NCMesh::Slave &slave = face_list.slaves[i];
3224
3225 if (slave.index >= 0 && slave.index < numFaces) // Skip ghost faces
3226 {
3227 const VarOrderBits fo0 = face_orders[slave.index];
3228 face_orders[slave.index] |= min_mask;
3229 if (fo0 != face_orders[slave.index])
3230 {
3231 changedFaces.insert(slave.index);
3232 }
3233 }
3234 }
3235 }
3236
3237 // Make sure edges support (new) orders required by incident faces.
3238 for (int i = 0; i < mesh->GetNFaces(); i++)
3239 {
3240 mesh->GetFaceEdges(i, E, ori);
3241 for (int j = 0; j < E.Size(); j++)
3242 {
3243 const VarOrderBits eo0 = edge_orders[E[j]];
3244 edge_orders[E[j]] |= face_orders[i];
3245 if (eo0 != edge_orders[E[j]])
3246 {
3247 changedEdges.insert(E[j]);
3248 }
3249 }
3250 }
3251
3252 // In the parallel case, OrderPropagation communicates orders on updated
3253 // edges and faces.
3254 done = OrderPropagation(changedEdges, changedFaces,
3255 edge_orders, face_orders);
3256 }
3257 while (!done);
3258
3259 GhostFaceOrderToEdges(face_orders, edge_orders);
3260
3261 // Some ghost edges and faces (3D) may not have any orders applied, since we
3262 // only communicate orders of neighboring ghost elements. Such ghost entities
3263 // are marked here, to be skipped by BuildParallelConformingInterpolation as
3264 // master entities constraining slave entity DOFs.
3265
3266 skip_edges.SetSize(edge_orders.Size());
3267 skip_edges = false;
3268
3269 skip_faces.SetSize(face_orders.Size());
3270 skip_faces = false;
3271
3272 for (int i=0; i<edge_orders.Size(); ++i)
3273 {
3274 if (edge_orders[i] == 0)
3275 {
3276 skip_edges[i] = true;
3277 }
3278 }
3279
3280 for (int i=0; i<face_orders.Size(); ++i)
3281 {
3282 if (face_orders[i] == 0)
3283 {
3284 skip_faces[i] = true;
3285 }
3286 }
3287}
3288
3290 const Array<VarOrderBits> &entity_orders,
3291 Table &entity_dofs,
3292 Array<char> *var_ent_order)
3293{
3294 // The tables var_edge_dofs and var_face_dofs hold DOF assignments for edges
3295 // and faces of a variable-order space, in which each edge/face may host
3296 // several DOF sets, called DOF set variants. Example: an edge 'i' shared by
3297 // 4 hexes of orders 2, 3, 4, 5 will hold four DOF sets, each starting at
3298 // indices e.g. 100, 101, 103, 106, respectively. These numbers are stored
3299 // in row 'i' of var_edge_dofs. Variant zero is always the lowest order DOF
3300 // set, followed by consecutive ranges of higher order DOFs. Variable-order
3301 // faces are handled similarly by var_face_dofs. The tables are empty for
3302 // constant-order spaces.
3303
3304 int num_ent = entity_orders.Size();
3305 int total_dofs = 0;
3306 int total_dofs_nonghost = 0;
3307
3308 Array<Connection> list;
3309 list.Reserve(2*num_ent);
3310
3311 if (var_ent_order)
3312 {
3313 var_ent_order->SetSize(0);
3314 var_ent_order->Reserve(num_ent);
3315 }
3316
3317 int nonGhost = num_ent;
3318 if (IsVariableOrder())
3319 {
3320 nonGhost -= (ent_dim == 1) ? NumGhostEdges() : NumGhostFaces();
3321 }
3322
3323 // assign DOFs according to order bit masks
3324 for (int i = 0; i < num_ent; i++)
3325 {
3326 auto geom = Geometry::SEGMENT; // ent_dim == 1 case
3327 if (ent_dim != 1)
3328 {
3329 // TODO: put this logic in mesh->GetFaceGeometry?
3330 if (i >= nonGhost) // if ghost
3331 {
3332 geom = mesh->ncmesh->GetFaceGeometry(i);
3333 }
3334 else
3335 {
3336 geom = mesh->GetFaceGeometry(i);
3337 }
3338 }
3339
3340 VarOrderBits bits = entity_orders[i];
3341 for (int order = 0; bits != 0; order++, bits >>= 1)
3342 {
3343 if (bits & 1)
3344 {
3345 const int dofs = fec->GetNumDof(geom, order);
3346 list.Append(Connection(i, total_dofs));
3347 total_dofs += dofs;
3348 if (i < nonGhost) { total_dofs_nonghost += dofs; }
3349 if (var_ent_order) { var_ent_order->Append(order); }
3350 }
3351 }
3352 }
3353
3354 // append a dummy row as terminator
3355 list.Append(Connection(num_ent, total_dofs));
3356
3357 // build the table
3358 entity_dofs.MakeFromList(num_ent+1, list);
3359 return total_dofs_nonghost;
3360}
3361
3362int FiniteElementSpace::FindDofs(const Table &var_dof_table,
3363 int row, int ndof) const
3364{
3365 const int *beg = var_dof_table.GetRow(row);
3366 const int *end = var_dof_table.GetRow(row + 1); // terminator, see above
3367
3368 while (beg < end)
3369 {
3370 // return the appropriate range of DOFs
3371 if ((beg[1] - beg[0]) == ndof) { return beg[0]; }
3372 beg++;
3373 }
3374
3375 MFEM_ABORT("DOFs not found for ndof = " << ndof);
3376 return 0;
3377}
3378
3379int FiniteElementSpace::GetEdgeOrder(int edge, int variant) const
3380{
3381 if (!IsVariableOrder()) { return fec->GetOrder(); }
3382
3383 if (edge >= var_edge_dofs.Size())
3384 {
3385 return ghost_edge_orders[edge - var_edge_dofs.Size()];
3386 }
3387
3388 const int* beg = var_edge_dofs.GetRow(edge);
3389 const int* end = var_edge_dofs.GetRow(edge + 1);
3390 if (variant >= end - beg) { return -1; } // past last variant
3391
3392 return var_edge_orders[var_edge_dofs.GetI()[edge] + variant];
3393}
3394
3395int FiniteElementSpace::GetFaceOrder(int face, int variant) const
3396{
3397 if (!IsVariableOrder())
3398 {
3399 // face order can be different from fec->GetOrder()
3400 Geometry::Type geom = mesh->GetFaceGeometry(face);
3401 return fec->FiniteElementForGeometry(geom)->GetOrder();
3402 }
3403
3404 if (face >= var_face_dofs.Size())
3405 {
3406 return ghost_face_orders[face - var_face_dofs.Size()];
3407 }
3408
3409 const int* beg = var_face_dofs.GetRow(face);
3410 const int* end = var_face_dofs.GetRow(face + 1);
3411 if (variant >= end - beg) { return -1; } // past last variant
3412
3413 return var_face_orders[var_face_dofs.GetI()[face] + variant];
3414}
3415
3416int FiniteElementSpace::GetNVariants(int entity, int index) const
3417{
3418 MFEM_ASSERT(IsVariableOrder(), "");
3419 const Table &dof_table = (entity == 1) ? var_edge_dofs : var_face_dofs;
3420
3421 MFEM_ASSERT(index >= 0 && index < dof_table.Size(), "");
3422 return dof_table.GetRow(index + 1) - dof_table.GetRow(index);
3423}
3424
3425static const char* msg_orders_changed =
3426 "Element orders changed, you need to Update() the space first.";
3427
3429 DofTransformation &doftrans) const
3430{
3431 MFEM_VERIFY(!orders_changed, msg_orders_changed);
3432
3433 doftrans.SetDofTransformation(nullptr);
3434
3435 if (elem_dof)
3436 {
3437 elem_dof->GetRow(elem, dofs);
3438
3440 {
3441 Array<int> Fo;
3442 elem_fos -> GetRow (elem, Fo);
3443 doftrans.SetDofTransformation(
3445 doftrans.SetFaceOrientations(Fo);
3446 doftrans.SetVDim();
3447 }
3448 return;
3449 }
3450
3451 Array<int> V, E, Eo, F, Fo; // TODO: LocalArray
3452
3453 const int dim = mesh->Dimension();
3454 const auto geom = mesh->GetElementGeometry(elem);
3455 const int order = GetElementOrderImpl(elem);
3456
3457 const int nv = fec->GetNumDof(Geometry::POINT, order);
3458 const int ne = (dim > 1) ? fec->GetNumDof(Geometry::SEGMENT, order) : 0;
3459 const int nb = (dim > 0) ? fec->GetNumDof(geom, order) : 0;
3460
3461 if (nv) { mesh->GetElementVertices(elem, V); }
3462 if (ne) { mesh->GetElementEdges(elem, E, Eo); }
3463
3464 int nfd = 0;
3465 if (dim > 2 && fec->HasFaceDofs(geom, order))
3466 {
3467 mesh->GetElementFaces(elem, F, Fo);
3468 for (int i = 0; i < F.Size(); i++)
3469 {
3470 nfd += fec->GetNumDof(mesh->GetFaceGeometry(F[i]), order);
3471 }
3473 {
3474 doftrans.SetDofTransformation(
3476 doftrans.SetFaceOrientations(Fo);
3477 doftrans.SetVDim();
3478 }
3479 }
3480
3481 dofs.SetSize(0);
3482 dofs.Reserve(nv*V.Size() + ne*E.Size() + nfd + nb);
3483
3484 if (nv) // vertex DOFs
3485 {
3486 for (int i = 0; i < V.Size(); i++)
3487 {
3488 for (int j = 0; j < nv; j++)
3489 {
3490 dofs.Append(V[i]*nv + j);
3491 }
3492 }
3493 }
3494
3495 if (ne) // edge DOFs
3496 {
3497 for (int i = 0; i < E.Size(); i++)
3498 {
3499 int ebase = IsVariableOrder() ? FindEdgeDof(E[i], ne) : E[i]*ne;
3500 const int *ind = fec->GetDofOrdering(Geometry::SEGMENT, order, Eo[i]);
3501
3502 for (int j = 0; j < ne; j++)
3503 {
3504 dofs.Append(EncodeDof(nvdofs + ebase, ind[j]));
3505 }
3506 }
3507 }
3508
3509 if (nfd) // face DOFs
3510 {
3511 for (int i = 0; i < F.Size(); i++)
3512 {
3513 auto fgeom = mesh->GetFaceGeometry(F[i]);
3514 int nf = fec->GetNumDof(fgeom, order);
3515
3516 int fbase = (var_face_dofs.Size() > 0) ? FindFaceDof(F[i], nf) : F[i]*nf;
3517 const int *ind = fec->GetDofOrdering(fgeom, order, Fo[i]);
3518
3519 for (int j = 0; j < nf; j++)
3520 {
3521 dofs.Append(EncodeDof(nvdofs + nedofs + fbase, ind[j]));
3522 }
3523 }
3524 }
3525
3526 if (nb) // interior ("bubble") DOFs
3527 {
3528 int bbase = bdofs ? bdofs[elem] : elem*nb;
3529 bbase += nvdofs + nedofs + nfdofs;
3530
3531 for (int j = 0; j < nb; j++)
3532 {
3533 dofs.Append(bbase + j);
3534 }
3535 }
3536}
3537
3539 Array<int> &dofs) const
3540{
3541 GetElementDofs(elem, dofs, DoFTrans);
3542 return DoFTrans.GetDofTransformation() ? &DoFTrans : NULL;
3543}
3544
3546 DofTransformation &doftrans) const
3547{
3548 MFEM_VERIFY(!orders_changed, msg_orders_changed);
3549
3550 doftrans.SetDofTransformation(nullptr);
3551
3552 if (bdr_elem_dof)
3553 {
3554 bdr_elem_dof->GetRow(bel, dofs);
3555
3557 {
3558 Array<int> Fo;
3559 bdr_elem_fos -> GetRow (bel, Fo);
3560 doftrans.SetDofTransformation(
3562 doftrans.SetFaceOrientations(Fo);
3563 doftrans.SetVDim();
3564 }
3565 return;
3566 }
3567
3568 Array<int> V, E, Eo; // TODO: LocalArray
3569 int F, oF;
3570
3571 int dim = mesh->Dimension();
3572 auto geom = mesh->GetBdrElementGeometry(bel);
3573 int order = fec->GetOrder();
3574
3575 if (elem_order.Size()) // determine order from adjacent element
3576 {
3577 int elem, info;
3578 mesh->GetBdrElementAdjacentElement(bel, elem, info);
3579 order = elem_order[elem];
3580 }
3581
3582 int nv = fec->GetNumDof(Geometry::POINT, order);
3583 int ne = (dim > 1) ? fec->GetNumDof(Geometry::SEGMENT, order) : 0;
3584 int nf = (dim > 2) ? fec->GetNumDof(geom, order) : 0;
3585
3586 if (nv) { mesh->GetBdrElementVertices(bel, V); }
3587 if (ne) { mesh->GetBdrElementEdges(bel, E, Eo); }
3588 if (nf)
3589 {
3590 mesh->GetBdrElementFace(bel, &F, &oF);
3591
3593 {
3594 mfem::Array<int> Fo(1);
3595 Fo[0] = oF;
3596 doftrans.SetDofTransformation(
3598 doftrans.SetFaceOrientations(Fo);
3599 doftrans.SetVDim();
3600 }
3601 }
3602
3603 dofs.SetSize(0);
3604 dofs.Reserve(nv*V.Size() + ne*E.Size() + nf);
3605
3606 if (nv) // vertex DOFs
3607 {
3608 for (int i = 0; i < V.Size(); i++)
3609 {
3610 for (int j = 0; j < nv; j++)
3611 {
3612 dofs.Append(V[i]*nv + j);
3613 }
3614 }
3615 }
3616
3617 if (ne) // edge DOFs
3618 {
3619 for (int i = 0; i < E.Size(); i++)
3620 {
3621 int ebase = IsVariableOrder() ? FindEdgeDof(E[i], ne) : E[i]*ne;
3622 const int *ind = fec->GetDofOrdering(Geometry::SEGMENT, order, Eo[i]);
3623
3624 for (int j = 0; j < ne; j++)
3625 {
3626 dofs.Append(EncodeDof(nvdofs + ebase, ind[j]));
3627 }
3628 }
3629 }
3630
3631 if (nf) // face DOFs
3632 {
3633 int fbase = (var_face_dofs.Size() > 0) ? FindFaceDof(F, nf) : F*nf;
3634 const int *ind = fec->GetDofOrdering(geom, order, oF);
3635
3636 for (int j = 0; j < nf; j++)
3637 {
3638 dofs.Append(EncodeDof(nvdofs + nedofs + fbase, ind[j]));
3639 }
3640 }
3641}
3642
3644 Array<int> &dofs) const
3645{
3646 GetBdrElementDofs(bel, dofs, DoFTrans);
3647 return DoFTrans.GetDofTransformation() ? &DoFTrans : NULL;
3648}
3649
3651 int variant) const
3652{
3653 MFEM_VERIFY(!orders_changed, msg_orders_changed);
3654
3655 // If face_dof is already built, use it.
3656 // If it is not and we have a NURBS space, build the face_dof and use it.
3657 if ((face_dof && variant == 0) ||
3658 (NURBSext && (BuildNURBSFaceToDofTable(), true)))
3659 {
3660 face_dof->GetRow(face, dofs);
3661 return fec->GetOrder();
3662 }
3663
3664 int order, nf, fbase;
3665 int dim = mesh->Dimension();
3666 auto fgeom = (dim > 2) ? mesh->GetFaceGeometry(face) : Geometry::INVALID;
3667
3668 if (var_face_dofs.Size() > 0) // variable orders or *mixed* faces
3669 {
3670 const int* beg = var_face_dofs.GetRow(face);
3671 const int* end = var_face_dofs.GetRow(face + 1);
3672 if (variant >= end - beg) { return -1; } // past last face DOFs
3673
3674 fbase = beg[variant];
3675 nf = beg[variant+1] - fbase;
3676
3677 order = !IsVariableOrder() ? fec->GetOrder() :
3678 var_face_orders[var_face_dofs.GetI()[face] + variant];
3679 MFEM_ASSERT(fec->GetNumDof(fgeom, order) == nf, [&]()
3680 {
3681 std::stringstream msg;
3682 msg << "fec->GetNumDof(" << (fgeom == Geometry::SQUARE ? "square" : "triangle")
3683 << ", " << order << ") = " << fec->GetNumDof(fgeom, order) << " nf " << nf;
3684 msg << " face " << face << " variant " << variant << std::endl;
3685 return msg.str();
3686 }());
3687 }
3688 else
3689 {
3690 if (variant > 0) { return -1; }
3691 order = fec->GetOrder();
3692 nf = (dim > 2) ? fec->GetNumDof(fgeom, order) : 0;
3693 fbase = face*nf;
3694 }
3695
3696 // for 1D, 2D and 3D faces
3697 int nv = fec->GetNumDof(Geometry::POINT, order);
3698 int ne = (dim > 1) ? fec->GetNumDof(Geometry::SEGMENT, order) : 0;
3699
3700 Array<int> V, E, Eo;
3701 if (nv) { mesh->GetFaceVertices(face, V); }
3702 if (ne) { mesh->GetFaceEdges(face, E, Eo); }
3703
3704 dofs.SetSize(0);
3705 dofs.Reserve(V.Size() * nv + E.Size() * ne + nf);
3706
3707 if (nv) // vertex DOFs
3708 {
3709 for (int i = 0; i < V.Size(); i++)
3710 {
3711 for (int j = 0; j < nv; j++)
3712 {
3713 dofs.Append(V[i]*nv + j);
3714 }
3715 }
3716 }
3717 if (ne) // edge DOFs
3718 {
3719 for (int i = 0; i < E.Size(); i++)
3720 {
3721 int ebase = IsVariableOrder() ? FindEdgeDof(E[i], ne) : E[i]*ne;
3722 const int *ind = fec->GetDofOrdering(Geometry::SEGMENT, order, Eo[i]);
3723
3724 for (int j = 0; j < ne; j++)
3725 {
3726 dofs.Append(EncodeDof(nvdofs + ebase, ind[j]));
3727 }
3728 }
3729 }
3730 for (int j = 0; j < nf; j++)
3731 {
3732 dofs.Append(nvdofs + nedofs + fbase + j);
3733 }
3734
3735 return order;
3736}
3737
3739 int variant) const
3740{
3741 MFEM_VERIFY(!orders_changed, msg_orders_changed);
3742
3743 int order, ne, base;
3744 if (IsVariableOrder())
3745 {
3746 const int* beg = var_edge_dofs.GetRow(edge);
3747 const int* end = var_edge_dofs.GetRow(edge + 1);
3748 if (variant >= end - beg) { return -1; } // past last edge DOFs
3749
3750 base = beg[variant];
3751 ne = beg[variant+1] - base;
3752
3753 order = var_edge_orders[var_edge_dofs.GetI()[edge] + variant];
3754 MFEM_ASSERT(fec->GetNumDof(Geometry::SEGMENT, order) == ne, "");
3755 }
3756 else
3757 {
3758 if (variant > 0) { return -1; }
3759 order = fec->GetOrder();
3760 ne = fec->GetNumDof(Geometry::SEGMENT, order);
3761 base = edge*ne;
3762 }
3763
3764 Array<int> V; // TODO: LocalArray
3765 int nv = fec->GetNumDof(Geometry::POINT, order);
3766 if (nv) { mesh->GetEdgeVertices(edge, V); }
3767
3768 dofs.SetSize(0);
3769 dofs.Reserve(2*nv + ne);
3770
3771 for (int i = 0; i < 2; i++)
3772 {
3773 for (int j = 0; j < nv; j++)
3774 {
3775 dofs.Append(V[i]*nv + j);
3776 }
3777 }
3778 for (int j = 0; j < ne; j++)
3779 {
3780 dofs.Append(nvdofs + base + j);
3781 }
3782
3783 return order;
3784}
3785
3787{
3789 dofs.SetSize(nv);
3790 for (int j = 0; j < nv; j++)
3791 {
3792 dofs[j] = i*nv+j;
3793 }
3794}
3795
3797{
3798 MFEM_VERIFY(!orders_changed, msg_orders_changed);
3799
3801 int base = bdofs ? bdofs[i] : i*nb;
3802
3803 dofs.SetSize(nb);
3804 base += nvdofs + nedofs + nfdofs;
3805 for (int j = 0; j < nb; j++)
3806 {
3807 dofs[j] = base + j;
3808 }
3809}
3810
3816
3818{
3819 MFEM_VERIFY(!IsVariableOrder(), "not implemented");
3820
3821 int nf, base;
3822 if (var_face_dofs.Size() > 0) // mixed faces
3823 {
3824 base = var_face_dofs.GetRow(i)[0];
3825 nf = var_face_dofs.GetRow(i)[1] - base;
3826 }
3827 else
3828 {
3829 auto geom = mesh->GetTypicalFaceGeometry();
3830 nf = fec->GetNumDof(geom, fec->GetOrder());
3831 base = i*nf;
3832 }
3833
3834 dofs.SetSize(nf);
3835 for (int j = 0; j < nf; j++)
3836 {
3837 dofs[j] = nvdofs + nedofs + base + j;
3838 }
3839}
3840
3842{
3843 MFEM_VERIFY(!IsVariableOrder(), "not implemented");
3844
3846 dofs.SetSize (ne);
3847 for (int j = 0, k = nvdofs+i*ne; j < ne; j++, k++)
3848 {
3849 dofs[j] = k;
3850 }
3851}
3852
3854{
3855 MFEM_ASSERT(NURBSext,
3856 "FiniteElementSpace::GetPatchDofs needs a NURBSExtension");
3857 NURBSext->GetPatchDofs(patch, dofs);
3858}
3859
3861{
3862 if (i < 0 || i >= mesh->GetNE())
3863 {
3864 if (mesh->GetNE() == 0)
3865 {
3866 MFEM_ABORT("Empty MPI partitions are not permitted!");
3867 }
3868 MFEM_ABORT("Invalid element id:" << i << "; minimum allowed:" << 0 <<
3869 ", maximum allowed:" << mesh->GetNE()-1);
3870 }
3871
3872 const FiniteElement *FE =
3874
3875 if (NURBSext)
3876 {
3877 NURBSext->LoadFE(i, FE);
3878 }
3879 else
3880 {
3881#ifdef MFEM_DEBUG
3882 // consistency check: fec->GetOrder() and FE->GetOrder() should return
3883 // the same value (for standard, constant-order spaces)
3884 if (!IsVariableOrder() && FE->GetDim() > 0)
3885 {
3886 MFEM_ASSERT(FE->GetOrder() == fec->GetOrder(),
3887 "internal error: " <<
3888 FE->GetOrder() << " != " << fec->GetOrder());
3889 }
3890#endif
3891 }
3892
3893 return FE;
3894}
3895
3897{
3898 if (mesh->GetNE() > 0) { return GetFE(0); }
3899
3901 const FiniteElement *fe = fec->FiniteElementForGeometry(geom);
3902 MFEM_VERIFY(fe != nullptr, "Could not determine a typical FE!");
3903 return fe;
3904}
3905
3907{
3908 int order = fec->GetOrder();
3909
3910 if (IsVariableOrder()) // determine order from adjacent element
3911 {
3912 int elem, info;
3913 mesh->GetBdrElementAdjacentElement(i, elem, info);
3914 order = GetElementOrderImpl(elem);
3915 }
3916
3917 const FiniteElement *BE;
3918 switch (mesh->Dimension())
3919 {
3920 case 1:
3921 BE = fec->GetFE(Geometry::POINT, order);
3922 break;
3923 case 2:
3924 BE = fec->GetFE(Geometry::SEGMENT, order);
3925 break;
3926 case 3:
3927 default:
3928 BE = fec->GetFE(mesh->GetBdrElementGeometry(i), order);
3929 }
3930
3931 if (NURBSext)
3932 {
3933 NURBSext->LoadBE(i, BE);
3934 }
3935
3936 return BE;
3937}
3938
3940{
3941 if (mesh->GetNBE() > 0) { return GetBE(0); }
3942
3944 const FiniteElement *be = fec->FiniteElementForGeometry(geom);
3945 MFEM_VERIFY(be != nullptr, "Could not determine a typical BE!");
3946 return be;
3947}
3948
3950{
3951 MFEM_VERIFY(!IsVariableOrder(), "not implemented");
3952
3953 const FiniteElement *fe;
3954 switch (mesh->Dimension())
3955 {
3956 case 1:
3958 break;
3959 case 2:
3961 break;
3962 case 3:
3963 default:
3965 }
3966
3967 if (NURBSext)
3968 {
3969 // Ensure 'face_to_be' is built:
3971 MFEM_ASSERT(face_to_be[i] >= 0,
3972 "NURBS mesh: only boundary faces are supported!");
3973 NURBSext->LoadBE(face_to_be[i], fe);
3974 }
3975
3976 return fe;
3977}
3978
3983
3985 int variant) const
3986{
3987 MFEM_ASSERT(mesh->Dimension() > 1, "No edges with mesh dimension < 2");
3988
3989 int eo = IsVariableOrder() ? GetEdgeOrder(i, variant) : fec->GetOrder();
3990 return fec->GetFE(Geometry::SEGMENT, eo);
3991}
3992
3994 int i, Geometry::Type geom_type) const
3995{
3996 return fec->GetTraceFE(geom_type, GetElementOrder(i));
3997}
3998
4003
4008
4010{
4011 R_transpose.reset();
4012 cR.reset();
4013 cR_hp.reset();
4014 cP.reset();
4015 Th.Clear();
4016 L2E_nat.Clear();
4017 L2E_lex.Clear();
4018 for (int i = 0; i < E2Q_array.Size(); i++)
4019 {
4020 delete E2Q_array[i];
4021 }
4022 E2Q_array.SetSize(0);
4023 L2F.clear();
4024 interpolations.clear();
4025 for (int i = 0; i < E2IFQ_array.Size(); i++)
4026 {
4027 delete E2IFQ_array[i];
4028 }
4029 E2IFQ_array.SetSize(0);
4030 for (int i = 0; i < E2BFQ_array.Size(); i++)
4031 {
4032 delete E2BFQ_array[i];
4033 }
4034 E2BFQ_array.SetSize(0);
4035
4037
4042
4043 for (int i = 0; i < VNURBSext.Size(); i++)
4044 {
4045 delete VNURBSext[i];
4046 }
4047
4048 if (NURBSext)
4049 {
4050 if (own_ext) { delete NURBSext; }
4051 delete face_dof;
4053 if (VNURBSext.Size() > 0 )
4054 {
4055 delete elem_dof;
4056 delete bdr_elem_dof;
4057 }
4058 }
4059 else
4060 {
4061 delete elem_dof;
4062 delete elem_fos;
4063 delete bdr_elem_dof;
4064 delete bdr_elem_fos;
4065 delete face_dof;
4066 delete [] bdofs;
4067 }
4069}
4070
4072{
4073 for (int i = 0; i < DoFTransArray.Size(); i++)
4074 {
4075 delete DoFTransArray[i];
4076 }
4077 DoFTransArray.SetSize(0);
4078}
4079
4081 const FiniteElementSpace &coarse_fes, OperatorHandle &T) const
4082{
4083 // Assumptions: see the declaration of the method.
4084
4085 if (T.Type() == Operator::MFEM_SPARSEMAT)
4086 {
4087 if (!IsVariableOrder())
4088 {
4089 Mesh::GeometryList elem_geoms(*mesh);
4090
4092 for (int i = 0; i < elem_geoms.Size(); i++)
4093 {
4094 GetLocalRefinementMatrices(coarse_fes, elem_geoms[i],
4095 localP[elem_geoms[i]]);
4096 }
4097 T.Reset(RefinementMatrix_main(coarse_fes.GetNDofs(),
4098 coarse_fes.GetElementToDofTable(),
4099 coarse_fes.
4101 localP));
4102 }
4103 else
4104 {
4106 coarse_fes.GetElementToDofTable()));
4107 }
4108 }
4109 else
4110 {
4111 T.Reset(new RefinementOperator(this, &coarse_fes));
4112 }
4113}
4114
4116 const FiniteElementSpace &coarse_fes, OperatorHandle &T) const
4117{
4118 const SparseMatrix *coarse_P = coarse_fes.GetConformingProlongation();
4119
4120 Operator::Type req_type = T.Type();
4121 GetTransferOperator(coarse_fes, T);
4122
4123 if (req_type == Operator::MFEM_SPARSEMAT)
4124 {
4126 {
4127 T.Reset(mfem::Mult(*cR, *T.As<SparseMatrix>()));
4128 }
4129 if (coarse_P)
4130 {
4131 T.Reset(mfem::Mult(*T.As<SparseMatrix>(), *coarse_P));
4132 }
4133 }
4134 else
4135 {
4136 const int RP_case = bool(GetConformingRestriction()) + 2*bool(coarse_P);
4137 if (RP_case == 0) { return; }
4138 const bool owner = T.OwnsOperator();
4139 T.SetOperatorOwner(false);
4140 switch (RP_case)
4141 {
4142 case 1:
4143 T.Reset(new ProductOperator(cR.get(), T.Ptr(), false, owner));
4144 break;
4145 case 2:
4146 T.Reset(new ProductOperator(T.Ptr(), coarse_P, owner, false));
4147 break;
4148 case 3:
4150 cR.get(), T.Ptr(), coarse_P, false, owner, false));
4151 break;
4152 }
4153 }
4154}
4155
4157{
4158 Array<char> new_order(mesh->GetNE());
4159 switch (mesh->GetLastOperation())
4160 {
4161 case Mesh::REFINE:
4162 {
4164 for (int i = 0; i < mesh->GetNE(); i++)
4165 {
4166 new_order[i] = elem_order[cf_tr.embeddings[i].parent];
4167 }
4168 break;
4169 }
4170 case Mesh::DEREFINE:
4171 {
4172 const CoarseFineTransformations &cf_tr =
4174 Table coarse_to_fine;
4175 cf_tr.MakeCoarseToFineTable(coarse_to_fine);
4176 Array<int> tabrow;
4177 for (int i = 0; i < coarse_to_fine.Size(); i++)
4178 {
4179 coarse_to_fine.GetRow(i, tabrow);
4180 // For now we require all children to be of same polynomial order.
4181 new_order[i] = elem_order[tabrow[0]];
4182 }
4183 break;
4184 }
4185 default:
4186 MFEM_ABORT("not implemented yet");
4187 }
4188
4189 mfem::Swap(elem_order, new_order);
4190}
4191
4192void FiniteElementSpace::Update(bool want_transform)
4193{
4194 lastUpdatePRef = false;
4195
4196 if (!orders_changed)
4197 {
4198 if (mesh->GetSequence() == mesh_sequence)
4199 {
4200 return; // mesh and space are in sync, no-op
4201 }
4202 if (want_transform && mesh->GetSequence() != mesh_sequence + 1)
4203 {
4204 MFEM_ABORT("Error in update sequence. Space needs to be updated after "
4205 "each mesh modification.");
4206 }
4207 }
4208 else
4209 {
4210 if (mesh->GetSequence() != mesh_sequence)
4211 {
4212 MFEM_ABORT("Updating space after both mesh change and element order "
4213 "change is not supported. Please update separately after "
4214 "each change.");
4215 }
4216 }
4217
4218 if (NURBSext)
4219 {
4220 UpdateNURBS();
4221 return;
4222 }
4223
4224 Table* old_elem_dof = NULL;
4225 Table* old_elem_fos = NULL;
4226 int old_ndofs;
4227 bool old_orders_changed = orders_changed;
4228
4229 // save old DOF table
4230 if (want_transform)
4231 {
4232 old_elem_dof = elem_dof;
4233 old_elem_fos = elem_fos;
4234 elem_dof = NULL;
4235 elem_fos = NULL;
4236 old_ndofs = ndofs;
4237 }
4238
4239 // update the 'elem_order' array if the mesh has changed
4241 {
4243 }
4244
4245 Destroy(); // calls Th.Clear()
4246 Construct();
4248
4249 if (want_transform)
4250 {
4251 MFEM_VERIFY(!old_orders_changed, "Interpolation for element order change "
4252 "is not implemented yet, sorry.");
4253
4254 // calculate appropriate GridFunction transformation
4255 switch (mesh->GetLastOperation())
4256 {
4257 case Mesh::REFINE:
4258 {
4260 {
4261 Th.Reset(new RefinementOperator(this, old_elem_dof,
4262 old_elem_fos, old_ndofs));
4263 // The RefinementOperator takes ownership of 'old_elem_dof', so
4264 // we no longer own it:
4265 old_elem_dof = NULL;
4266 old_elem_fos = NULL;
4267 }
4268 else
4269 {
4270 // calculate fully assembled matrix
4271 Th.Reset(RefinementMatrix(old_ndofs, old_elem_dof,
4272 old_elem_fos));
4273 }
4274 break;
4275 }
4276
4277 case Mesh::DEREFINE:
4278 {
4280#if 0
4281 Th.Reset(DerefinementMatrix(old_ndofs, old_elem_dof, old_elem_fos));
4282#else
4283 Th.Reset(new DerefineMatrixOp(*this, old_ndofs, old_elem_dof, old_elem_fos));
4284#endif
4285 if (IsVariableOrder())
4286 {
4287 if (cP && cR_hp)
4288 {
4289 Th.SetOperatorOwner(false);
4290 Th.Reset(new TripleProductOperator(cP.get(), cR_hp.get(), Th.Ptr(),
4291 false, false, true));
4292 }
4293 }
4294 else
4295 {
4296 if (cP && cR)
4297 {
4298 Th.SetOperatorOwner(false);
4299 Th.Reset(new TripleProductOperator(cP.get(), cR.get(), Th.Ptr(),
4300 false, false, true));
4301 }
4302 }
4303 break;
4304 }
4305
4306 default:
4307 break;
4308 }
4309
4310 delete old_elem_dof;
4311 delete old_elem_fos;
4312 }
4313}
4314
4316 bool want_transfer)
4317{
4318 if (want_transfer)
4319 {
4321 for (int i = 0; i<mesh->GetNE(); i++)
4322 {
4323 fesPrev->SetElementOrder(i, GetElementOrder(i));
4324 }
4325 fesPrev->Update(false);
4326 }
4327
4328 for (auto ref : refs)
4329 {
4330 SetElementOrder(ref.index, GetElementOrder(ref.index) + ref.delta);
4331 }
4332
4333 Update(false);
4334
4335 if (want_transfer)
4336 {
4337 PTh.reset(new PRefinementTransferOperator(*fesPrev, *this));
4338 }
4339
4340 lastUpdatePRef = true;
4341}
4342
4344{
4345 // Check whether the space type is L2 or H1
4346 if (!dynamic_cast<const L2_FECollection*>(fec) &&
4347 !dynamic_cast<const H1_FECollection*>(fec))
4348 {
4349 return false;
4350 }
4351
4352 // Check whether the mesh is purely quadrilateral or hexahedral.
4353 const int dim = mesh->Dimension();
4355 mesh->GetGeometries(dim, geoms);
4356 if (geoms.Size() != 1) { return false; }
4357 if (dim == 2 && geoms[0] != Geometry::Type::SQUARE) { return false; }
4358 else if (dim == 3 && geoms[0] != Geometry::Type::CUBE) { return false; }
4359
4360 return true;
4361}
4362
4364{
4365 mesh = new_mesh;
4366}
4367
4369 Vector &fes_node_pos,
4370 int fes_nodes_ordering) const
4371{
4372 Mesh *m = GetMesh();
4373 const int NE = m->GetNE();
4374
4375 if (NE == 0) { fes_node_pos.SetSize(0); return; }
4376
4377 const int dim = m->Dimension();
4378 Array<int> dofs;
4379 Vector e_xyz;
4380 fes_node_pos.SetSize(GetNDofs() * dim);
4381 const FiniteElementSpace *mesh_fes = m->GetNodalFESpace();
4382 FiniteElementSpace vector_fes(m, FEColl(), dim, fes_nodes_ordering);
4383
4384 for (int e = 0; e < NE; e++)
4385 {
4386 mesh_fes->GetElementVDofs(e, dofs);
4387 const int mdof_cnt = dofs.Size() / dim;
4388 mesh_nodes.GetSubVector(dofs, e_xyz); //e_xyz is ordered by nodes here
4389
4390 auto ir = GetFE(e)->GetNodes();
4391 const int fdof_cnt = ir.GetNPoints();
4392 Vector mesh_shape(mdof_cnt), gf_xyz(fdof_cnt * dim);
4393 for (int q = 0; q < fdof_cnt; q++)
4394 {
4395 mesh_fes->GetFE(e)->CalcShape(ir.IntPoint(q), mesh_shape);
4396 for (int d = 0; d < dim; d++)
4397 {
4398 Vector x(e_xyz.GetData() + d*mdof_cnt, mdof_cnt);
4399 gf_xyz(d*fdof_cnt + q) = x * mesh_shape; // order by nodes
4400 }
4401 }
4402
4403 // reuse/resize dofs.
4404 vector_fes.GetElementVDofs(e, dofs);
4405 fes_node_pos.SetSubVector(dofs, gf_xyz);
4406 }
4407}
4408
4409void FiniteElementSpace::Save(std::ostream &os) const
4410{
4411 int fes_format = 90; // the original format, v0.9
4412 bool nurbs_unit_weights = false;
4413
4414 // Determine the format that should be used.
4415 if (!NURBSext)
4416 {
4417 // TODO: if this is a variable-order FE space, use fes_format = 100.
4418 }
4419 else
4420 {
4421 const NURBSFECollection *nurbs_fec =
4422 dynamic_cast<const NURBSFECollection *>(fec);
4423 MFEM_VERIFY(nurbs_fec, "invalid FE collection");
4424 nurbs_fec->SetOrder(NURBSext->GetOrder());
4425 const real_t eps = 5e-14;
4426 nurbs_unit_weights = (NURBSext->GetWeights().Min() >= 1.0-eps &&
4427 NURBSext->GetWeights().Max() <= 1.0+eps);
4429 (NURBSext != mesh->NURBSext && !nurbs_unit_weights) ||
4430 (NURBSext->GetMaster().Size() != 0 ))
4431 {
4432 fes_format = 100; // v1.0 format
4433 }
4434 }
4435
4436 os << (fes_format == 90 ?
4437 "FiniteElementSpace\n" : "MFEM FiniteElementSpace v1.0\n")
4438 << "FiniteElementCollection: " << fec->Name() << '\n'
4439 << "VDim: " << vdim << '\n'
4440 << "Ordering: " << ordering << '\n';
4441
4442 if (fes_format == 100) // v1.0
4443 {
4444 if (!NURBSext)
4445 {
4446 // TODO: this is a variable-order FE space --> write 'element_orders'.
4447 }
4448 else if (NURBSext != mesh->NURBSext)
4449 {
4451 {
4452 os << "NURBS_order\n" << NURBSext->GetOrder() << '\n';
4453 }
4454 else
4455 {
4456 os << "NURBS_orders\n";
4457 // 1 = do not write the size, just the entries:
4458 NURBSext->GetOrders().Save(os, 1);
4459 }
4460 // If periodic BCs are given, write connectivity
4461 if (NURBSext->GetMaster().Size() != 0 )
4462 {
4463 os <<"NURBS_periodic\n";
4464 NURBSext->GetMaster().Save(os);
4465 NURBSext->GetSlave().Save(os);
4466 }
4467 // If the weights are not unit, write them to the output:
4468 if (!nurbs_unit_weights)
4469 {
4470 os << "NURBS_weights\n";
4471 NURBSext->GetWeights().Print(os, 1);
4472 }
4473 }
4474 os << "End: MFEM FiniteElementSpace v1.0\n";
4475 }
4476}
4477
4478std::shared_ptr<const PRefinementTransferOperator>
4480
4481void FiniteElementSpace
4482::GetEssentialBdrEdgesFaces(const Array<int> &bdr_attr_is_ess,
4483 std::set<int> & edges, std::set<int> & faces) const
4484{
4485 const int dim = mesh->Dimension();
4486 MFEM_VERIFY(dim == 2 || dim == 3, "");
4487
4488 for (int i = 0; i < GetNBE(); i++)
4489 {
4490 if (bdr_attr_is_ess[GetBdrAttribute(i)-1])
4491 {
4492 int f, o;
4493 mesh->GetBdrElementFace(i, &f, &o);
4494
4495 if (dim == 3)
4496 {
4497 faces.insert(f);
4498 Array<int> edges_i, cor;
4499 mesh->GetBdrElementEdges(i, edges_i, cor);
4500 for (auto edge : edges_i)
4501 {
4502 edges.insert(edge);
4503 }
4504 }
4505 else
4506 {
4507 edges.insert(f);
4508 }
4509 }
4510 }
4511
4512 if (Nonconforming())
4513 {
4514 Array<int> bdr_verts, bdr_edges, bdr_faces;
4515 mesh->ncmesh->GetBoundaryClosure(bdr_attr_is_ess, bdr_verts, bdr_edges,
4516 bdr_faces);
4517
4518 for (auto e : bdr_edges)
4519 {
4520 edges.insert(e);
4521 }
4522
4523 for (auto f : bdr_faces)
4524 {
4525 faces.insert(f);
4526 }
4527 }
4528}
4529
4531 const Array<int> &boundary_element_indices,
4532 Array<int> &boundary_edge_dofs,
4533 Array<int> *dof_edges,
4534 Array<int> *dof_boundary_elements) const
4535{
4536 MFEM_VERIFY(mesh->Dimension() >= 2,
4537 "GetBoundaryLoopEdgeDofs requires 2D or 3D meshes to find edge objects");
4538
4539 boundary_edge_dofs.SetSize(0);
4540 if (dof_edges) { dof_edges->SetSize(0); }
4541 if (dof_boundary_elements) { dof_boundary_elements->SetSize(0); }
4542
4543 // A DOF that appears in exactly one selected boundary element lies on the
4544 // bounding loop; one appearing in two or more is interior to the boundary
4545 // region and is dropped. Count occurrences of each DOF (using scratch maps,
4546 // exposed only as parallel-indexed Array<int> below) and record, on first
4547 // sight, the local edge and boundary element carrying it.
4548 //
4549 // The count is over GetEdgeDofs, which returns endpoint vertex DOFs as well
4550 // as edge-interior DOFs (relevant for collections such as ND_R2D that carry
4551 // vertex DOFs). Edge-interior DOFs occur once per edge, so the count mainly
4552 // resolves vertex DOFs: a vertex shared by several elements is interior and
4553 // dropped, while a genuine loop-corner (open-curve endpoint) vertex is kept.
4554 // This is why we count GetEdgeDofs rather than collecting GetEdgeInteriorDofs,
4555 // which would omit the endpoint vertex DOFs the method is documented to keep.
4556 // The 3D removal criterion (any edge in two or more faces) matches the
4557 // parallel version rather than a parity toggle.
4558 std::unordered_map<int, int> dof_count, dof_edge, dof_belem;
4559 Array<int> edge_dofs, edges, edge_orientations;
4560
4561 const int dim = mesh->Dimension();
4562 for (int i = 0; i < boundary_element_indices.Size(); ++i)
4563 {
4564 const int boundary_element_idx = boundary_element_indices[i];
4565 std::unordered_set<int> boundary_element_dofs;
4566
4567 if (dim == 3)
4568 {
4569 // Boundary elements are 2D faces; extract their 1D edges.
4570 int face_index, face_orientation;
4571 mesh->GetBdrElementFace(boundary_element_idx, &face_index,
4572 &face_orientation);
4573 mesh->GetFaceEdges(face_index, edges, edge_orientations);
4574 }
4575 else
4576 {
4577 // Boundary elements are 1D segments, each being a single edge.
4578 mesh->GetBdrElementEdges(boundary_element_idx, edges, edge_orientations);
4579 MFEM_VERIFY(edges.Size() == 1,
4580 "2D boundary element should have exactly one edge");
4581 }
4582
4583 for (int j = 0; j < edges.Size(); ++j)
4584 {
4585 GetEdgeDofs(edges[j], edge_dofs);
4586 for (int k = 0; k < edge_dofs.Size(); ++k)
4587 {
4588 const int dof = edge_dofs[k];
4589 // Count each DOF once per boundary element and record metadata the
4590 // first time it is seen, so H1 DOFs shared by multiple edges of the
4591 // same element are not double counted.
4592 if (boundary_element_dofs.insert(dof).second &&
4593 dof_count[dof]++ == 0)
4594 {
4595 dof_edge[dof] = edges[j];
4596 dof_belem[dof] = boundary_element_idx;
4597 }
4598 }
4599 }
4600 }
4601
4602 // Emit the DOFs seen in exactly one selected boundary element, in a
4603 // deterministic (increasing DOF index) order shared by all output arrays.
4604 std::vector<int> kept;
4605 kept.reserve(dof_count.size());
4606 for (const auto &[dof, count] : dof_count)
4607 {
4608 if (count == 1) { kept.push_back(dof); }
4609 }
4610 std::sort(kept.begin(), kept.end());
4611
4612 boundary_edge_dofs.Reserve(static_cast<int>(kept.size()));
4613 if (dof_edges) { dof_edges->Reserve(static_cast<int>(kept.size())); }
4614 if (dof_boundary_elements)
4615 {
4616 dof_boundary_elements->Reserve(static_cast<int>(kept.size()));
4617 }
4618 for (int dof : kept)
4619 {
4620 boundary_edge_dofs.Append(dof);
4621 if (dof_edges) { dof_edges->Append(dof_edge[dof]); }
4622 if (dof_boundary_elements) { dof_boundary_elements->Append(dof_belem[dof]); }
4623 }
4624}
4625
4627 const Array<int> &bdr_attrs,
4628 std::vector<Array<int>> &attr_to_elements) const
4629{
4630 // One (initially empty) list of boundary elements per requested attribute,
4631 // indexed to match bdr_attrs.
4632 attr_to_elements.assign(bdr_attrs.Size(), Array<int>());
4633
4634 // Map attribute value -> position in bdr_attrs for quick lookup.
4635 std::unordered_map<int, int> attr_to_index;
4636 for (int i = 0; i < bdr_attrs.Size(); ++i)
4637 {
4638 attr_to_index[bdr_attrs[i]] = i;
4639 }
4640
4641 // Bucket boundary elements by their attribute.
4642 for (int i = 0; i < mesh->GetNBE(); ++i)
4643 {
4644 int attr = mesh->GetBdrElement(i)->GetAttribute();
4645 auto it = attr_to_index.find(attr);
4646 if (it != attr_to_index.end())
4647 {
4648 attr_to_elements[it->second].Append(i);
4649 }
4650 }
4651}
4652
4654 int bdr_attr, Array<int> &boundary_elements) const
4655{
4656 boundary_elements.SetSize(0);
4657
4658 for (int i = 0; i < mesh->GetNBE(); ++i)
4659 {
4660 if (mesh->GetBdrElement(i)->GetAttribute() == bdr_attr)
4661 {
4662 boundary_elements.Append(i);
4663 }
4664 }
4665}
4666
4668 const Array<int> &dof_edges,
4669 const Array<int> &dof_boundary_elements,
4670 const Vector &loop_normal,
4671 Array<int> &dof_orientations) const
4672{
4673 MFEM_VERIFY(dof_edges.Size() == dof_boundary_elements.Size(),
4674 "dof_edges and dof_boundary_elements must be parallel-indexed");
4675
4676 const int ndof = dof_edges.Size();
4677 dof_orientations.SetSize(ndof);
4678
4679 Array<int> edge_verts, bdr_elem_verts;
4680 Vector edge_vec(3), to_edge_vec(3), cross_product(3);
4681 for (int i = 0; i < ndof; i++)
4682 {
4683 const int edge_id = dof_edges[i];
4684 const int bdr_elem_idx = dof_boundary_elements[i];
4685
4686 // Get edge vertices
4687 mesh->GetEdgeVertices(edge_id, edge_verts);
4688
4689 const real_t *v0 = mesh->GetVertex(edge_verts[0]);
4690 const real_t *v1 = mesh->GetVertex(edge_verts[1]);
4691
4692 // Get boundary element vertices
4693 mesh->GetBdrElement(bdr_elem_idx)->GetVertices(bdr_elem_verts);
4694
4695 // Find the third vertex (not part of the edge)
4696 int third_vertex = -1;
4697 for (int j = 0; j < bdr_elem_verts.Size(); j++)
4698 {
4699 int v = bdr_elem_verts[j];
4700 if (v != edge_verts[0] && v != edge_verts[1])
4701 {
4702 third_vertex = v;
4703 break;
4704 }
4705 }
4706
4707 if (third_vertex == -1)
4708 {
4709 MFEM_ABORT("Boundary element " << bdr_elem_idx << " has only 2 vertices, "
4710 "but 3D boundary elements must have at least 3 vertices");
4711 }
4712
4713 const real_t *v2 = mesh->GetVertex(third_vertex);
4714
4715 // Edge vector
4716 for (int j = 0; j < 3; j++) { edge_vec[j] = v1[j] - v0[j]; }
4717
4718 // Vector from third vertex to edge (use edge midpoint)
4719 for (int j = 0; j < 3; j++)
4720 {
4721 real_t edge_midpoint = (v0[j] + v1[j]) * 0.5;
4722 to_edge_vec[j] = edge_midpoint - v2[j];
4723 }
4724
4725 // Cross product: to_edge × edge
4726 to_edge_vec.cross3D(edge_vec, cross_product);
4727
4728 // Check alignment with loop normal
4729 real_t dot_product = cross_product * loop_normal;
4730 dof_orientations[i] = (dot_product > 0) ? 1 : -1;
4731 }
4732}
4733
4735{
4736 string buff;
4737 int fes_format = 0, ord;
4739
4740 Destroy();
4741
4742 input >> std::ws;
4743 getline(input, buff); // 'FiniteElementSpace'
4744 filter_dos(buff);
4745 if (buff == "FiniteElementSpace") { fes_format = 90; /* v0.9 */ }
4746 else if (buff == "MFEM FiniteElementSpace v1.0") { fes_format = 100; }
4747 else { MFEM_ABORT("input stream is not a FiniteElementSpace!"); }
4748 getline(input, buff, ' '); // 'FiniteElementCollection:'
4749 input >> std::ws;
4750 getline(input, buff);
4751 filter_dos(buff);
4752 r_fec = FiniteElementCollection::New(buff.c_str());
4753 getline(input, buff, ' '); // 'VDim:'
4754 input >> vdim;
4755 getline(input, buff, ' '); // 'Ordering:'
4756 input >> ord;
4757
4758 NURBSFECollection *nurbs_fec = dynamic_cast<NURBSFECollection*>(r_fec);
4759 if (nurbs_fec) { nurbs_fec->SetDim(m->Dimension()); }
4760 NURBSExtension *nurbs_ext = NULL;
4761 if (fes_format == 90) // original format, v0.9
4762 {
4763 if (nurbs_fec)
4764 {
4765 MFEM_VERIFY(m->NURBSext, "NURBS FE collection requires a NURBS mesh!");
4766 const int order = nurbs_fec->GetOrder();
4767 if (order != m->NURBSext->GetOrder() &&
4769 {
4770 nurbs_ext = new NURBSExtension(m->NURBSext, order);
4771 }
4772 }
4773 }
4774 else if (fes_format == 100) // v1.0
4775 {
4776 while (1)
4777 {
4778 skip_comment_lines(input, '#');
4779 MFEM_VERIFY(input.good(), "error reading FiniteElementSpace v1.0");
4780 getline(input, buff);
4781 filter_dos(buff);
4782 if (buff == "NURBS_order" || buff == "NURBS_orders")
4783 {
4784 MFEM_VERIFY(nurbs_fec,
4785 buff << ": NURBS FE collection is required!");
4786 MFEM_VERIFY(m->NURBSext, buff << ": NURBS mesh is required!");
4787 MFEM_VERIFY(!nurbs_ext, buff << ": order redefinition!");
4788 if (buff == "NURBS_order")
4789 {
4790 int order;
4791 input >> order;
4792 nurbs_ext = new NURBSExtension(m->NURBSext, order);
4793 }
4794 else
4795 {
4796 Array<int> orders;
4797 orders.Load(m->NURBSext->GetNKV(), input);
4798 nurbs_ext = new NURBSExtension(m->NURBSext, orders);
4799 }
4800 }
4801 else if (buff == "NURBS_periodic")
4802 {
4803 Array<int> master, slave;
4804 master.Load(input);
4805 slave.Load(input);
4806 nurbs_ext->ConnectBoundaries(master,slave);
4807 }
4808 else if (buff == "NURBS_weights")
4809 {
4810 MFEM_VERIFY(nurbs_ext, "NURBS_weights: NURBS_orders have to be "
4811 "specified before NURBS_weights!");
4812 nurbs_ext->GetWeights().Load(input, nurbs_ext->GetNDof());
4813 }
4814 else if (buff == "element_orders")
4815 {
4816 MFEM_VERIFY(!nurbs_fec, "section element_orders cannot be used "
4817 "with a NURBS FE collection");
4818 MFEM_ABORT("element_orders: not implemented yet!");
4819 }
4820 else if (buff == "End: MFEM FiniteElementSpace v1.0")
4821 {
4822 break;
4823 }
4824 else
4825 {
4826 MFEM_ABORT("unknown section: " << buff);
4827 }
4828 }
4829 }
4830
4831 Constructor(m, nurbs_ext, r_fec, vdim, ord);
4832
4833 return r_fec;
4834}
4835
4842} // namespace mfem
T Max() const
Find the maximal element in the array, using the comparison operator < for class T.
Definition array.cpp:69
void Load(std::istream &in, int fmt=0)
Read an Array from the stream in using format fmt. The format fmt can be:
Definition array.cpp:54
const T * HostRead() const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), false).
Definition array.hpp:414
void Reserve(int capacity)
Ensures that the allocated size is at least the given size.
Definition array.hpp:210
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition array.hpp:869
int Size() const
Return the logical size of the array.
Definition array.hpp:192
void MakeRef(T *data_, int size_, bool own_data=false)
Make this Array a reference to a pointer.
Definition array.hpp:1082
void DeleteAll()
Delete the whole array.
Definition array.hpp:1062
int Append(const T &el)
Append element 'el' to array, resize if necessary.
Definition array.hpp:941
void Save(std::ostream &out, int fmt=0) const
Save the Array to the stream out using the format fmt. The format fmt can be:
Definition array.cpp:41
void Copy(Array &copy) const
Create a copy of the internal array to the provided copy.
Definition array.hpp:1071
T Sum() const
Return the sum of all the array entries using the '+'' operator for class 'T'.
Definition array.cpp:145
T * HostWrite()
Shortcut for mfem::Write(a.GetMemory(), a.Size(), false).
Definition array.hpp:422
Abstract base class BilinearFormIntegrator.
virtual void AssembleElementMatrix(const FiniteElement &el, ElementTransformation &Trans, DenseMatrix &elmat)
Given a particular Finite Element computes the element matrix elmat.
Operator that extracts face degrees of freedom for H1, ND, or RT FiniteElementSpaces.
void Mult(const real_t *x, real_t *y) const
Matrix vector multiplication with the inverse of dense matrix.
Data type dense matrix using column-major storage.
Definition densemat.hpp:24
void Mult(const real_t *x, real_t *y) const
Matrix vector multiplication.
Definition densemat.cpp:108
void MultTranspose(const real_t *x, real_t *y) const
Multiply a vector with the transpose matrix.
Definition densemat.cpp:158
void SetSize(int s)
Change the size of the DenseMatrix to s x s.
Definition densemat.hpp:125
void UseExternalData(real_t *d, int h, int w)
Change the data array and the size of the DenseMatrix.
Definition densemat.hpp:97
void GetRow(int r, Vector &row) const
Rank 3 tensor (array of matrices)
void SetSize(int i, int j, int k, MemoryType mt_=MemoryType::PRESERVE)
int SizeJ() const
int SizeI() const
int SizeK() const
void SetFaceOrientations(const Array< int > &Fo)
Configure the transformation using face orientations for the current element.
Definition doftrans.hpp:169
void SetDofTransformation(const StatelessDofTransformation &dof_trans)
Set or change the nested StatelessDofTransformation object.
Definition doftrans.hpp:176
void InvTransformDual(real_t *v) const
Definition doftrans.cpp:107
const StatelessDofTransformation * GetDofTransformation() const
Return the nested StatelessDofTransformation object.
Definition doftrans.hpp:186
void SetVDim(int vdim=1, int ordering=0)
Set or change the vdim and ordering parameter.
Definition doftrans.hpp:190
bool IsIdentity() const
Definition doftrans.hpp:204
void TransformPrimal(real_t *v) const
Definition doftrans.cpp:17
Abstract base class that defines an interface for element restrictions.
Operator that converts FiniteElementSpace L-vectors to E-vectors.
Abstract data type element.
Definition element.hpp:29
virtual void GetVertices(Array< int > &v) const =0
Get the indices defining the vertices.
int GetAttribute() const
Return element's attribute.
Definition element.hpp:58
virtual int GetNVertices() const =0
A class that performs interpolation from a face E-vector to quadrature point values and/or derivative...
const IntegrationRule * IntRule
Not owned.
static bool SupportsFESpace(const FiniteElementSpace &fes)
Returns true if the given finite element space is supported by FaceQuadratureInterpolator.
Base class for operators that extracts Face degrees of freedom.
Collection of finite elements from the same family in multiple dimensions. This class is used to matc...
Definition fe_coll.hpp:27
static FiniteElementCollection * New(const char *name)
Factory method: return a newly allocated FiniteElementCollection according to the given name.
Definition fe_coll.cpp:124
const int * GetDofOrdering(Geometry::Type geom, int p, int ori) const
Variable order version of DofOrderForOrientation().
Definition fe_coll.hpp:238
int GetOrder() const
Return the order (polynomial degree) of the FE collection, corresponding to the order/degree returned...
Definition fe_coll.hpp:248
virtual int GetContType() const =0
int HasFaceDofs(Geometry::Type geom, int p) const
Definition fe_coll.cpp:100
virtual int DofForGeometry(Geometry::Type GeomType) const =0
int GetNumDof(Geometry::Type geom, int p) const
Variable order version of DofForGeometry().
Definition fe_coll.hpp:226
virtual const FiniteElement * TraceFiniteElementForGeometry(Geometry::Type GeomType) const
Definition fe_coll.hpp:97
const FiniteElement * GetTraceFE(Geometry::Type geom, int p) const
Variable order version of TraceFiniteElementForGeometry().
Definition fe_coll.hpp:214
virtual const char * Name() const
Definition fe_coll.hpp:79
const FiniteElement * GetFE(Geometry::Type geom, int p) const
Variable order version of FiniteElementForGeometry().
Definition fe_coll.hpp:203
@ DISCONTINUOUS
Field is discontinuous across element interfaces.
Definition fe_coll.hpp:48
virtual const FiniteElement * FiniteElementForGeometry(Geometry::Type GeomType) const =0
void Mult(const Vector &x, Vector &y) const override
Operator application: y=A(x).
Definition fespace.cpp:2349
DerefinementOperator(const FiniteElementSpace *f_fes, const FiniteElementSpace *c_fes, BilinearFormIntegrator *mass_integ)
TODO: Implement DofTransformation support.
Definition fespace.cpp:2248
GridFunction interpolation operator applicable after mesh refinement.
Definition fespace.hpp:492
virtual void MultTranspose(const Vector &x, Vector &y) const
Action of the transpose operator: y=A^t(x). The default behavior in class Operator is to generate an ...
Definition fespace.cpp:2023
RefinementOperator(const FiniteElementSpace *fespace, Table *old_elem_dof, Table *old_elem_fos, int old_ndofs)
Definition fespace.cpp:1835
virtual void Mult(const Vector &x, Vector &y) const
Operator application: y=A(x).
Definition fespace.cpp:1944
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
Definition fespace.hpp:210
std::unique_ptr< FiniteElementSpace > fesPrev
Definition fespace.hpp:311
void Save(std::ostream &out) const
Save finite element space to output stream out.
Definition fespace.cpp:4409
virtual void ApplyGhostElementOrdersToEdgesAndFaces(Array< VarOrderBits > &edge_orders, Array< VarOrderBits > &face_orders) const
Helper function for ParFiniteElementSpace.
Definition fespace.cpp:3032
int GetEntityVDofs(int entity, int index, Array< int > &dofs, Geometry::Type master_geom=Geometry::INVALID, int variant=0) const
Helper to get vertex, edge or face VDOFs (entity=0,1,2 resp.).
Definition fespace.cpp:1084
void GetVDofs(int vd, Array< int > &dofs, int ndofs=-1) const
Returns the indices of all of the VDofs for the specified dimension 'vd'.
Definition fespace.cpp:212
DofTransformation DoFTrans
Definition fespace.hpp:295
static int EncodeDof(int entity_base, int idx)
Helper to encode a sign flip into a DOF index (for Hcurl/Hdiv shapes).
Definition fespace.hpp:1149
const SparseMatrix * GetConformingRestriction() const
The returned SparseMatrix is owned by the FiniteElementSpace.
Definition fespace.cpp:1429
void ReorderElementToDofTable()
Reorder the scalar DOFs based on the element ordering.
Definition fespace.cpp:471
Array< char > var_face_orders
Definition fespace.hpp:263
int GetVectorDim() const
Return the total dimension of a vector in the space.
Definition fespace.cpp:1456
bool IsVariableOrder() const
Returns true if the space contains elements of varying polynomial orders.
Definition fespace.hpp:673
void SetRestriction(const SparseMatrix &r)
Definition fespace.cpp:152
void BuildNURBSFaceToDofTable() const
Generates partial face_dof table for a NURBS space.
Definition fespace.cpp:2720
void BuildDofToBdrArrays() const
Initialize internal data that enables the use of the methods GetBdrElementForDof() and GetBdrLocalDof...
Definition fespace.cpp:517
static void AddDependencies(SparseMatrix &deps, Array< int > &master_dofs, Array< int > &slave_dofs, DenseMatrix &I, int skipfirst=0)
Definition fespace.cpp:915
const Table & GetElementToDofTable() const
Return a reference to the internal Table that stores the lists of scalar dofs, for each mesh element,...
Definition fespace.hpp:1278
Array< int > dof_ldof_array
Definition fespace.hpp:282
Array< StatelessDofTransformation * > DoFTransArray
Definition fespace.hpp:294
void DofsToVDofs(Array< int > &dofs, int ndofs=-1) const
Compute the full set of vdofs corresponding to each entry in dofs.
Definition fespace.cpp:232
void GetEdgeInteriorDofs(int i, Array< int > &dofs) const
Returns the indices of the degrees of freedom for the interior of the specified edge.
Definition fespace.cpp:3841
Array< FaceQuadratureInterpolator * > E2BFQ_array
Definition fespace.hpp:333
const FiniteElement * GetBE(int i) const
Returns pointer to the FiniteElement in the FiniteElementCollection associated with i'th boundary fac...
Definition fespace.cpp:3906
Array< bool > skip_face
Definition fespace.hpp:272
Array< FaceQuadratureInterpolator * > E2IFQ_array
Definition fespace.hpp:332
friend struct DerefineMatrixOp
Definition fespace.hpp:215
Array< int > face_min_nghb_order
Definition fespace.hpp:268
DofTransformation * GetElementDofs(int elem, Array< int > &dofs) const
Returns indices of degrees of freedom of element 'elem'. The returned indices are offsets into an ldo...
Definition fespace.cpp:3538
int GetNumElementInteriorDofs(int i) const
Returns the number of degrees of freedom associated with the interior of the specified element.
Definition fespace.cpp:3811
std::shared_ptr< PRefinementTransferOperator > PTh
Definition fespace.hpp:316
virtual void GetExteriorTrueDofs(Array< int > &exterior_dofs, int component=-1) const
Get a list of all true dofs on the exterior of the mesh, exterior_dofs. For spaces with 'vdim' > 1,...
Definition fespace.cpp:712
void GetEdgeVDofs(int i, Array< int > &vdofs) const
Returns the indices of the degrees of freedom for the specified edge, including the DOFs for the vert...
Definition fespace.cpp:332
NURBSExtension * NURBSext
Definition fespace.hpp:286
virtual int GetFaceDofs(int face, Array< int > &dofs, int variant=0) const
Returns the indices of the degrees of freedom for the specified face, including the DOFs for the edge...
Definition fespace.cpp:3650
virtual void GetTrueTransferOperator(const FiniteElementSpace &coarse_fes, OperatorHandle &T) const
Construct and return an Operator that can be used to transfer true-dof data from coarse_fes,...
Definition fespace.cpp:4115
static void AdjustVDofs(Array< int > &vdofs)
Remove the orientation information encoded into an array of dofs Some basis function types have a rel...
Definition fespace.cpp:284
virtual void GetExteriorVDofs(Array< int > &exterior_vdofs, int component=-1) const
Mark degrees of freedom associated with exterior faces of the mesh. For spaces with 'vdim' > 1,...
Definition fespace.cpp:683
SparseMatrix * VariableOrderRefinementMatrix(const int coarse_ndofs, const Table &coarse_elem_dof) const
Definition fespace.cpp:1727
int GetEdgeOrder(int edge, int variant=0) const
Definition fespace.cpp:3379
Array< char > loc_var_face_orders
Definition fespace.hpp:264
bool Nonconforming() const
Definition fespace.hpp:650
void GetVertexVDofs(int i, Array< int > &vdofs) const
Returns the indices of the degrees of freedom for the specified vertices.
Definition fespace.cpp:338
void GetVertexDofs(int i, Array< int > &dofs) const
Returns the indices of the degrees of freedom for the specified vertices.
Definition fespace.cpp:3786
OperatorHandle L2E_lex
Definition fespace.hpp:322
void BuildFaceToDofTable() const
Definition fespace.cpp:436
int GetFaceOrder(int face, int variant=0) const
Returns the polynomial degree of the i'th face finite element.
Definition fespace.cpp:3395
int GetNumBorderDofs(Geometry::Type geom, int order) const
Definition fespace.cpp:1050
FiniteElementSpace()
Default constructor: the object is invalid until initialized using the method Load().
Definition fespace.cpp:33
Array< int > dof_elem_array
Definition fespace.hpp:281
int FindFaceDof(int face, int ndof) const
Similar to FindEdgeDof, but used for mixed meshes too.
Definition fespace.hpp:443
static int MinOrder(VarOrderBits bits)
Return the minimum order (least significant bit set) in the bit mask.
Definition fespace.cpp:3019
static void ListToMarker(const Array< int > &list, int marker_size, Array< int > &marker, int mark_val=-1)
Convert an array of indices (list) to a Boolean marker array where all indices in the list are marked...
Definition fespace.cpp:775
MFEM_DEPRECATED void RebuildElementToDofTable()
(
Definition fespace.cpp:462
int GetNDofs() const
Returns number of degrees of freedom. This is the number of Local Degrees of Freedom.
Definition fespace.hpp:821
bool orders_changed
True if at least one element order changed (variable-order space only).
Definition fespace.hpp:344
SparseMatrix * DerefinementMatrix(int old_ndofs, const Table *old_elem_dof, const Table *old_elem_fos)
Calculate GridFunction restriction matrix after mesh derefinement.
Definition fespace.cpp:2407
friend class PRefinementTransferOperator
Definition fespace.hpp:212
void GetLocalRefinementMatrices(Geometry::Type geom, DenseTensor &localP) const
Definition fespace.cpp:1788
void GetEdgeInteriorVDofs(int i, Array< int > &vdofs) const
Returns the indices of the degrees of freedom for the interior of the specified edge.
Definition fespace.cpp:350
void GetTransferOperator(const FiniteElementSpace &coarse_fes, OperatorHandle &T) const
Construct and return an Operator that can be used to transfer GridFunction data from coarse_fes,...
Definition fespace.cpp:4080
SparseMatrix * D2C_GlobalRestrictionMatrix(FiniteElementSpace *cfes)
Generate the global restriction matrix from a discontinuous FE space to the continuous FE space of th...
Definition fespace.cpp:805
virtual void GetEssentialTrueDofs(const Array< int > &bdr_attr_is_ess, Array< int > &ess_tdof_list, int component=-1) const
Get a list of essential true dofs, ess_tdof_list, corresponding to the boundary attributes marked in ...
Definition fespace.cpp:624
int GetNBE() const
Returns number of boundary elements in the mesh.
Definition fespace.hpp:876
virtual void UpdateMeshPointer(Mesh *new_mesh)
Definition fespace.cpp:4363
int GetDegenerateFaceDofs(int index, Array< int > &dofs, Geometry::Type master_geom, int variant) const
Definition fespace.cpp:1008
const QuadratureInterpolator * GetQuadratureInterpolator(const IntegrationRule &ir) const
Return a QuadratureInterpolator that interpolates E-vectors to quadrature point values and/or derivat...
Definition fespace.cpp:1588
Array< char > ghost_edge_orders
Definition fespace.hpp:265
int GetBdrAttribute(int i) const
Definition fespace.hpp:917
Array< int > dof_bdr_elem_array
Definition fespace.hpp:283
int GetEntityDofs(int entity, int index, Array< int > &dofs, Geometry::Type master_geom=Geometry::INVALID, int variant=0) const
Helper to get vertex, edge or face DOFs (entity=0,1,2 resp.).
Definition fespace.cpp:1059
const InterpolationManager & GetInterpolationManager(ElementDofOrdering f_ordering, FaceType type) const
Definition fespace.cpp:1547
virtual const Operator * GetRestrictionOperator() const
An abstract operator that performs the same action as GetRestrictionMatrix.
Definition fespace.hpp:710
static constexpr int MaxVarOrder
Definition fespace.hpp:259
virtual void CopyProlongationAndRestriction(const FiniteElementSpace &fes, const Array< int > *perm)
Copies the prolongation and restriction matrices from fes.
Definition fespace.cpp:82
Array< char > var_edge_orders
Definition fespace.hpp:263
const FiniteElement * GetTypicalBE() const
Return a "typical" boundary element.
Definition fespace.cpp:3939
std::unique_ptr< Operator > R_transpose
Operator computing the action of the transpose of the restriction.
Definition fespace.hpp:307
bool Conforming() const
Definition fespace.hpp:645
int MakeDofTable(int ent_dim, const Array< VarOrderBits > &entity_orders, Table &entity_dofs, Array< char > *var_ent_order)
Definition fespace.cpp:3289
void UpdateElementOrders()
Resize the elem_order array on mesh change.
Definition fespace.cpp:4156
void MakeVDimMatrix(SparseMatrix &mat) const
Replicate 'mat' in the vector dimension, according to vdim ordering mode.
Definition fespace.cpp:1394
int GetNF() const
Returns number of faces (i.e. co-dimension 1 entities) in the mesh.
Definition fespace.hpp:873
SparseMatrix * H2L_GlobalRestrictionMatrix(FiniteElementSpace *lfes)
Construct the restriction matrix from the FE space given by (*this) to the lower degree FE space give...
Definition fespace.cpp:868
SparseMatrix * D2Const_GlobalRestrictionMatrix(FiniteElementSpace *cfes)
Generate the global restriction matrix from a discontinuous FE space to the piecewise constant FE spa...
Definition fespace.cpp:837
const FiniteElementCollection * fec
Associated FE collection (not owned).
Definition fespace.hpp:222
void GetNodePositions(const Vector &mesh_nodes, Vector &fes_node_pos, int fes_nodes_ordering=Ordering::byNODES) const
Compute the space's node positions w.r.t. given mesh positions. The function uses FiniteElement::GetN...
Definition fespace.cpp:4368
FiniteElementCollection * Load(Mesh *m, std::istream &input)
Read a FiniteElementSpace from a stream. The returned FiniteElementCollection is owned by the caller.
Definition fespace.cpp:4734
void BuildBdrElementToDofTable() const
Definition fespace.cpp:397
DofTransformation * GetElementVDofs(int i, Array< int > &vdofs) const
Returns indices of degrees of freedom for the i'th element. The returned indices are offsets into an ...
Definition fespace.cpp:299
Array< QuadratureInterpolator * > E2Q_array
Definition fespace.hpp:331
virtual const FiniteElement * GetFE(int i) const
Returns pointer to the FiniteElement in the FiniteElementCollection associated with i'th element in t...
Definition fespace.cpp:3860
Ordering::Type GetOrdering() const
Return the ordering method.
Definition fespace.hpp:852
NURBSExtension * StealNURBSext()
Definition fespace.cpp:2634
void GetPatchDofs(int patch, Array< int > &dofs) const
Returns indices of degrees of freedom for NURBS patch index patch. Cartesian ordering is used,...
Definition fespace.cpp:3853
void BuildDofToArrays_() const
Initialize internal data that enables the use of the methods GetElementForDof() and GetLocalDofForDof...
Definition fespace.cpp:492
const Operator * GetRestrictionTransposeOperator() const
Return an operator that performs the transpose of GetRestrictionOperator.
Definition fespace.cpp:1444
Array< char > elem_order
Definition fespace.hpp:239
static bool DofFinalizable(int dof, const Array< bool > &finalized, const SparseMatrix &deps)
Definition fespace.cpp:994
Array< int > face_to_be
Definition fespace.hpp:292
void GetLocalDerefinementMatrices(Geometry::Type geom, DenseTensor &localR) const
Definition fespace.cpp:2383
void BuildConformingInterpolation() const
Calculate the cP and cR matrices for a nonconforming mesh.
Definition fespace.cpp:1144
int GetNE() const
Returns number of elements in the mesh.
Definition fespace.hpp:867
int vdim
Vector dimension (number of unknowns per degree of freedom).
Definition fespace.hpp:225
Table var_face_dofs
NOTE: also used for spaces with mixed faces.
Definition fespace.hpp:248
OperatorHandle L2E_nat
The element restriction operators, see GetElementRestriction().
Definition fespace.hpp:322
bool lastUpdatePRef
Flag to indicate whether the last update was for p-refinement.
Definition fespace.hpp:319
std::unordered_map< std::tuple< ElementDofOrdering, FaceType >, std::unique_ptr< InterpolationManager >, TupleHasher > interpolations
Definition fespace.hpp:329
int * bdofs
internal DOFs of elements if mixed/var-order; NULL otherwise
Definition fespace.hpp:243
std::unordered_map< key_face, std::unique_ptr< FaceRestriction >, TupleHasher > L2F
Definition fespace.hpp:326
const ElementRestrictionOperator * GetElementRestriction(ElementDofOrdering e_ordering) const
Return an Operator that converts L-vectors to E-vectors.
Definition fespace.cpp:1476
const FiniteElement * GetEdgeElement(int i, int variant=0) const
Returns pointer to the FiniteElement in the FiniteElementCollection associated with i'th edge in the ...
Definition fespace.cpp:3984
Array< bool > skip_edge
Definition fespace.hpp:272
Array< char > ghost_face_orders
Definition fespace.hpp:265
int GetEdgeDofs(int edge, Array< int > &dofs, int variant=0) const
Returns the indices of the degrees of freedom for the specified edge, including the DOFs for the vert...
Definition fespace.cpp:3738
std::unique_ptr< SparseMatrix > cR
Conforming restriction matrix such that cR.cP=I.
Definition fespace.hpp:302
void GetElementInteriorVDofs(int i, Array< int > &vdofs) const
Returns the indices of the degrees of freedom for the interior of the specified element.
Definition fespace.cpp:344
void GetFaceVDofs(int i, Array< int > &vdofs) const
Returns the indices of the degrees of freedom for the specified face, including the DOFs for the edge...
Definition fespace.cpp:326
Array< char > loc_var_edge_orders
Definition fespace.hpp:264
virtual void Update(bool want_transform=true)
Reflect changes in the mesh: update number of DOFs, etc. Also, calculate GridFunction transformation ...
Definition fespace.cpp:4192
OperatorHandle Th
Transformation to apply to GridFunctions after space Update().
Definition fespace.hpp:314
SparseMatrix * RefinementMatrix(int old_ndofs, const Table *old_elem_dof, const Table *old_elem_fos)
Definition fespace.cpp:1811
std::uint64_t VarOrderBits
Bit-mask representing a set of orders needed by an edge/face.
Definition fespace.hpp:258
void GetBoundaryElementsByAttribute(const Array< int > &bdr_attrs, std::vector< Array< int > > &attr_to_elements) const
Get boundary elements grouped by attribute.
Definition fespace.cpp:4626
SparseMatrix * RefinementMatrix_main(const int coarse_ndofs, const Table &coarse_elem_dof, const Table *coarse_elem_fos, const DenseTensor localP[]) const
Definition fespace.cpp:1663
int ndofs
Number of degrees of freedom. Number of unknowns is ndofs * vdim.
Definition fespace.hpp:233
std::unique_ptr< SparseMatrix > cP
Definition fespace.hpp:300
void AddEdgeFaceDependencies(SparseMatrix &deps, Array< int > &master_dofs, const FiniteElement *master_fe, Array< int > &slave_dofs, int slave_face, const DenseMatrix *pm) const
Definition fespace.cpp:940
const FiniteElement * GetTraceElement(int i, Geometry::Type geom_type) const
Return the trace element from element 'i' to the given 'geom_type'.
Definition fespace.cpp:3993
bool UsesRaggedTensorBasis() const
Return true if the mesh contains only one topology, the elements are all triangles or tetrahedrons,...
Definition fespace.hpp:1595
void ComputeLoopEdgeOrientations(const Array< int > &dof_edges, const Array< int > &dof_boundary_elements, const Vector &loop_normal, Array< int > &dof_orientations) const
Compute edge orientations relative to a boundary loop direction.
Definition fespace.cpp:4667
const FiniteElement * GetTypicalTraceElement() const
Return a "typical" trace element.
Definition fespace.cpp:3999
void GetBoundaryLoopEdgeDofs(const Array< int > &boundary_element_indices, Array< int > &boundary_edge_dofs, Array< int > *dof_edges=nullptr, Array< int > *dof_boundary_elements=nullptr) const
Extract the edge degrees of freedom of a boundary "loop".
Definition fespace.cpp:4530
static void MarkerToList(const Array< int > &marker, Array< int > &list)
Convert a Boolean marker array to a list containing all marked indices.
Definition fespace.cpp:756
Array< NURBSExtension * > VNURBSext
Definition fespace.hpp:290
int GetElementOrder(int i) const
Returns the order of the i'th finite element.
Definition fespace.cpp:195
void SetVarOrderLocalDofs()
Sets all2local. See documentation of all2local for details.
Definition fespace.cpp:2968
Mesh * mesh
The mesh that FE space lives on (not owned).
Definition fespace.hpp:219
const FiniteElement * GetFaceElement(int i) const
Returns pointer to the FiniteElement in the FiniteElementCollection associated with i'th face in the ...
Definition fespace.cpp:3949
Ordering::Type ordering
Definition fespace.hpp:230
void ConvertToConformingVDofs(const Array< int > &dofs, Array< int > &cdofs)
For a partially conforming FE space, convert a marker array (nonzero entries are true) on the partial...
Definition fespace.cpp:788
void CalcEdgeFaceVarOrders(Array< VarOrderBits > &edge_orders, Array< VarOrderBits > &face_orders, Array< VarOrderBits > &edge_elem_orders, Array< VarOrderBits > &face_elem_orders, Array< bool > &skip_edges, Array< bool > &skip_faces) const
Definition fespace.cpp:3043
virtual bool OrderPropagation(const std::set< int > &edges, const std::set< int > &faces, Array< VarOrderBits > &edge_orders, Array< VarOrderBits > &face_orders) const
Returns true if order propagation is done, for variable-order spaces.
Definition fespace.hpp:417
std::shared_ptr< const PRefinementTransferOperator > GetPrefUpdateOperator()
Definition fespace.cpp:4479
void SetElementOrder(int i, int p)
Sets the order of the i'th finite element.
Definition fespace.cpp:170
const SparseMatrix * GetHpConformingRestriction() const
The returned SparseMatrix is owned by the FiniteElementSpace.
Definition fespace.cpp:1437
const SparseMatrix * GetConformingProlongation() const
Definition fespace.cpp:1422
const FiniteElementCollection * FEColl() const
Definition fespace.hpp:854
Mesh * GetMesh() const
Returns the mesh.
Definition fespace.hpp:639
int GetElementOrderImpl(int i) const
Return element order: internal version of GetElementOrder without checks.
Definition fespace.cpp:206
int GetNVariants(int entity, int index) const
Return number of possible DOF variants for edge/face (var. order spaces).
Definition fespace.cpp:3416
Array< int > dof_bdr_ldof_array
Definition fespace.hpp:284
int GetVSize() const
Return the number of vector dofs, i.e. GetNDofs() x GetVDim().
Definition fespace.hpp:824
const Table * GetElementToFaceOrientationTable() const
Definition fespace.hpp:1274
DofTransformation * GetBdrElementDofs(int bel, Array< int > &dofs) const
Returns indices of degrees of freedom for boundary element 'bel'. The returned indices are offsets in...
Definition fespace.cpp:3643
void GetBoundaryTrueDofs(Array< int > &boundary_dofs, int component=-1)
Get a list of all boundary true dofs, boundary_dofs. For spaces with 'vdim' > 1, the 'component' para...
Definition fespace.cpp:668
int GetVDim() const
Returns the vector dimension of the finite element space.
Definition fespace.hpp:817
const FaceQuadratureInterpolator * GetFaceQuadratureInterpolator(const IntegrationRule &ir, FaceType type) const
Return a FaceQuadratureInterpolator that interpolates E-vectors to quadrature point values and/or der...
Definition fespace.cpp:1627
bool IsDGSpace() const
Return whether or not the space is discontinuous (L2)
Definition fespace.hpp:1587
void GetFaceInteriorDofs(int i, Array< int > &dofs) const
Returns the indices of the degrees of freedom for the interior of the specified face.
Definition fespace.cpp:3817
virtual void PRefineAndUpdate(const Array< pRefinement > &refs, bool want_transfer=true)
Definition fespace.cpp:4315
int GetNConformingDofs() const
Definition fespace.cpp:1450
void SetProlongation(const SparseMatrix &p)
Definition fespace.cpp:133
Array< int > edge_min_nghb_order
Minimum order among neighboring elements.
Definition fespace.hpp:268
const FiniteElement * GetTypicalFE() const
Return GetFE(0) if the local mesh is not empty; otherwise return a typical FE based on the Geometry t...
Definition fespace.cpp:3896
static int DecodeDof(int dof)
Helper to return the DOF associated with a sign encoded DOF.
Definition fespace.hpp:1153
void GetElementInteriorDofs(int i, Array< int > &dofs) const
Returns the indices of the degrees of freedom for the interior of the specified element.
Definition fespace.cpp:3796
virtual int NumGhostEdges() const
Returns the number of ghost edges (nonzero in ParFiniteElementSpace).
Definition fespace.hpp:424
void Constructor(Mesh *mesh, NURBSExtension *ext, const FiniteElementCollection *fec, int vdim=1, int ordering=Ordering::byNODES)
Help function for constructors + Load().
Definition fespace.cpp:2533
DofTransformation * GetBdrElementVDofs(int i, Array< int > &vdofs) const
Returns indices of degrees of freedom for i'th boundary element. The returned indices are offsets int...
Definition fespace.cpp:314
void ConvertFromConformingVDofs(const Array< int > &cdofs, Array< int > &dofs)
For a partially conforming FE space, convert a marker array (nonzero entries are true) on the conform...
Definition fespace.cpp:796
const FiniteElement * GetTypicalFaceElement() const
Return a "typical" face element.
Definition fespace.cpp:3979
virtual void GetEssentialVDofs(const Array< int > &bdr_attr_is_ess, Array< int > &ess_vdofs, int component=-1) const
Mark degrees of freedom associated with boundary elements with the specified boundary attributes (mar...
Definition fespace.cpp:550
virtual int NumGhostFaces() const
Returns the number of ghost faces (nonzero in ParFiniteElementSpace).
Definition fespace.hpp:427
int DofToVDof(int dof, int vd, int ndofs=-1) const
Compute a single vdof corresponding to the index dof and the vector index vd.
Definition fespace.cpp:268
virtual const FaceRestriction * GetFaceRestriction(ElementDofOrdering f_ordering, FaceType, L2FaceValues mul=L2FaceValues::DoubleValued) const
Return an Operator that converts L-vectors to E-vectors on each face.
Definition fespace.cpp:1509
int GetCurlDim() const
Return the dimension of the curl of a GridFunction defined on this space.
Definition fespace.cpp:1466
virtual void GhostFaceOrderToEdges(const Array< VarOrderBits > &face_orders, Array< VarOrderBits > &edge_orders) const
Helper function for ParFiniteElementSpace.
Definition fespace.hpp:412
int FindEdgeDof(int edge, int ndof) const
Definition fespace.hpp:439
void GetPatchVDofs(int i, Array< int > &vdofs) const
Returns indices of degrees of freedom in vdofs for NURBS patch i.
Definition fespace.cpp:320
void BuildElementToDofTable() const
Definition fespace.cpp:356
std::unique_ptr< SparseMatrix > cR_hp
A version of the conforming restriction matrix for variable-order spaces.
Definition fespace.hpp:304
int FindDofs(const Table &var_dof_table, int row, int ndof) const
Search row of a DOF table for a DOF set of size 'ndof', return first DOF.
Definition fespace.cpp:3362
void VariableOrderMinimumRule(SparseMatrix &deps) const
Definition fespace.cpp:1094
Abstract class for all finite elements.
Definition fe_base.hpp:294
int GetRangeDim() const
Returns the vector dimension for vector-valued finite elements, which is also the dimension of the in...
Definition fe_base.hpp:387
int GetOrder() const
Returns the order of the finite element. In the case of anisotropic orders, returns the maximum order...
Definition fe_base.hpp:414
int GetDim() const
Returns the reference space dimension for the finite element.
Definition fe_base.hpp:381
virtual void GetTransferMatrix(const FiniteElement &fe, ElementTransformation &Trans, DenseMatrix &I) const
Return interpolation matrix, I, which maps dofs from a coarse element, fe, to the fine dofs on this f...
Definition fe_base.cpp:129
int GetRangeType() const
Returns the FiniteElement::RangeType of the element, one of {SCALAR, VECTOR}.
Definition fe_base.hpp:427
virtual void GetLocalRestriction(ElementTransformation &Trans, DenseMatrix &R) const
Return a local restriction matrix R (Dof x Dof) mapping fine dofs to coarse dofs.
Definition fe_base.cpp:123
const IntegrationRule & GetNodes() const
Get a const reference to the nodes of the element.
Definition fe_base.hpp:476
Geometry::Type GetGeomType() const
Returns the Geometry::Type of the reference element.
Definition fe_base.hpp:407
virtual void GetLocalInterpolation(ElementTransformation &Trans, DenseMatrix &I) const
Return the local interpolation matrix I (Dof x Dof) where the fine element is the image of the base g...
Definition fe_base.cpp:117
virtual void Project(Coefficient &coeff, ElementTransformation &Trans, Vector &dofs) const
Given a coefficient and a transformation, compute its projection (approximation) in the local finite ...
Definition fe_base.cpp:136
int GetCurlDim() const
Definition fe_base.hpp:398
virtual void CalcShape(const IntegrationPoint &ip, Vector &shape) const =0
Evaluate the values of all shape functions of a scalar finite element in reference space at the given...
int GetDof() const
Returns the number of degrees of freedom in the finite element.
Definition fe_base.hpp:410
static const int NumGeom
Definition geom.hpp:46
static const int NumVerts[NumGeom]
Definition geom.hpp:53
Arbitrary order H1-conforming (continuous) finite elements.
Definition fe_coll.hpp:291
Class for an integration rule - an Array of IntegrationPoint.
Definition intrules.hpp:96
int GetNPoints() const
Returns the number of the points in the integration rule.
Definition intrules.hpp:255
This class manages the storage and computation of the interpolations from master (coarse) face to sla...
A standard isoparametric element transformation.
Definition eltrans.hpp:629
void SetPointMat(const DenseMatrix &pm)
Set the underlying point matrix describing the transformation.
Definition eltrans.hpp:668
void SetFE(const FiniteElement *FE)
Set the element that will be used to compute the transformations.
Definition eltrans.hpp:648
void SetIdentityTransformation(Geometry::Type GeomType)
Set the FiniteElement Geometry for the reference elements being used.
Definition eltrans.cpp:417
const DenseMatrix & GetPointMat() const
Return the stored point matrix.
Definition eltrans.hpp:671
Operator that converts L2 FiniteElementSpace L-vectors to E-vectors.
Operator that extracts Face degrees of freedom for L2 spaces.
Operator that extracts face degrees of freedom for L2 interface spaces.
Arbitrary order "L2-conforming" discontinuous finite elements.
Definition fe_coll.hpp:369
Class used by MFEM to store pointers to host and/or device memory.
List of mesh geometries stored as Array<Geometry::Type>.
Definition mesh.hpp:1603
Mesh data type.
Definition mesh.hpp:67
void GetFaceEdges(int i, Array< int > &edges, Array< int > &o) const
Definition mesh.cpp:8109
void GetGeometries(int dim, Array< Geometry::Type > &el_geoms) const
Return all element geometries of the given dimension present in the mesh.
Definition mesh.cpp:8025
Operation GetLastOperation() const
Return type of last modification of the mesh.
Definition mesh.hpp:2553
int GetNEdges() const
Return the number of edges.
Definition mesh.hpp:1396
void GetBdrElementFace(int i, int *f, int *o) const
Definition mesh.cpp:8369
Array< int > bdr_attributes
A list of all unique boundary attributes used by the Mesh.
Definition mesh.hpp:309
NURBSExtension * NURBSext
Optional NURBS mesh extension.
Definition mesh.hpp:317
virtual void GetExteriorFaceMarker(Array< int > &face_marker) const
Populate a marker array identifying exterior faces.
Definition mesh.cpp:1733
int GetNumFaces() const
Return the number of faces (3D), edges (2D) or vertices (1D).
Definition mesh.cpp:7302
void GetBdrElementVertices(int i, Array< int > &v) const
Returns the indices of the vertices of boundary element i.
Definition mesh.hpp:1626
Geometry::Type GetFaceGeometry(int i) const
Return the Geometry::Type associated with face i.
Definition mesh.cpp:1651
Geometry::Type GetElementGeometry(int i) const
Definition mesh.hpp:1548
Geometry::Type GetBdrElementGeometry(int i) const
Definition mesh.hpp:1560
void GetElementVertices(int i, Array< int > &v) const
Returns the indices of the vertices of element i.
Definition mesh.hpp:1622
const FiniteElementSpace * GetNodalFESpace() const
Definition mesh.cpp:7206
Geometry::Type GetTypicalElementGeometry() const
If the local mesh is not empty, return GetElementGeometry(0); otherwise, return a typical Geometry pr...
Definition mesh.cpp:1705
int GetBdrElementFaceIndex(int be_idx) const
Return the local face (codimension-1) index for the given boundary element index.
Definition mesh.hpp:1702
int GetNFaces() const
Return the number of faces in a 3D mesh.
Definition mesh.hpp:1399
long GetSequence() const
Definition mesh.hpp:2559
const CoarseFineTransformations & GetRefinementTransforms() const
Definition mesh.cpp:12237
int GetNE() const
Returns number of elements.
Definition mesh.hpp:1390
const Element * GetFace(int i) const
Return pointer to the i'th face element object.
Definition mesh.hpp:1474
int Dimension() const
Dimension of the reference space used within the elements.
Definition mesh.hpp:1314
const Element * GetBdrElement(int i) const
Return pointer to the i'th boundary element object.
Definition mesh.hpp:1462
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
void GetBdrElementEdges(int i, Array< int > &edges, Array< int > &cor) const
Return the indices and the orientations of all edges of bdr element i.
Definition mesh.cpp:8077
void GetElementFaces(int i, Array< int > &faces, Array< int > &ori) const
Return the indices and the orientations of all faces of element i.
Definition mesh.cpp:8318
int SpaceDimension() const
Dimension of the physical space containing the mesh.
Definition mesh.hpp:1317
int GetNV() const
Returns number of vertices. Vertices are only at the corners of elements, where you would expect them...
Definition mesh.hpp:1387
void GetEdgeVertices(int i, Array< int > &vert) const
Returns the indices of the vertices of edge i.
Definition mesh.cpp:8139
void GetBdrElementAdjacentElement(int bdr_el, int &el, int &info) const
For the given boundary element, bdr_el, return its adjacent element and its info, i....
Definition mesh.cpp:8388
void GetFaceVertices(int i, Array< int > &vert) const
Returns the indices of the vertices of face i.
Definition mesh.hpp:1640
int GetNBE() const
Returns number of boundary elements.
Definition mesh.hpp:1393
NCMesh * ncmesh
Optional nonconforming mesh extension.
Definition mesh.hpp:318
void GetElementEdges(int i, Array< int > &edges, Array< int > &cor) const
Return the indices and the orientations of all edges of element i.
Definition mesh.cpp:8044
int GetNumGeometries(int dim) const
Return the number of geometries of the given dimension present in the mesh.
Definition mesh.cpp:8014
Geometry::Type GetTypicalFaceGeometry() const
If the local mesh is not empty, return GetFaceGeometry(0); otherwise return a typical face geometry p...
Definition mesh.cpp:1671
Geometry::Type GetElementBaseGeometry(int i) const
Definition mesh.hpp:1569
const real_t * GetVertex(int i) const
Return pointer to vertex i's coordinates.
Definition mesh.hpp:1429
Operator that extracts face degrees of freedom for L2 nonconforming spaces.
const NCList & GetNCList(int entity)
Return vertex/edge/face list (entity = 0/1/2, respectively).
Definition ncmesh.hpp:391
const CoarseFineTransformations & GetDerefinementTransforms() const
Definition ncmesh.cpp:5255
const NCList & GetFaceList()
Return the current list of conforming and nonconforming faces.
Definition ncmesh.hpp:369
Geometry::Type GetFaceGeometry(int index) const
Return face geometry type. index is the Mesh face number.
Definition ncmesh.hpp:506
virtual void GetBoundaryClosure(const Array< int > &bdr_attr_is_ess, Array< int > &bdr_vertices, Array< int > &bdr_edges, Array< int > &bdr_faces)
Get a list of vertices (2D/3D), edges (3D) and faces (3D) that coincide with boundary elements with t...
Definition ncmesh.cpp:5827
const NCList & GetEdgeList()
Return the current list of conforming and nonconforming edges.
Definition ncmesh.hpp:376
int GetNFaces() const
Return the number of (2D) faces in the NCMesh.
Definition ncmesh.hpp:223
int GetNEdges() const
Return the number of edges in the NCMesh.
Definition ncmesh.hpp:221
Arbitrary order H(curl)-conforming Nedelec finite elements.
Definition fe_coll.hpp:526
DoF transformation implementation for the Nedelec basis on pyramid elements.
Definition doftrans.hpp:375
DoF transformation implementation for the Nedelec basis on tetrahedra.
Definition doftrans.hpp:352
DoF transformation implementation for the Nedelec basis on wedge elements.
Definition doftrans.hpp:363
NURBSExtension generally contains multiple NURBSPatch objects spanning an entire Mesh....
Definition nurbs.hpp:575
const Vector & GetWeights() const
Access function to the vector of weights weights.
Definition nurbs.hpp:1039
const Array< int > & GetSlave() const
Definition nurbs.hpp:891
int GetNKV() const
Return the number of KnotVectors.
Definition nurbs.hpp:949
void LoadBE(int i, const FiniteElement *BE) const
Load boundary element i into BE.
Definition nurbs.cpp:5002
Table * GetElementDofTable()
Definition nurbs.hpp:999
const Array< int > & GetMaster() const
Definition nurbs.hpp:889
void LoadFE(int i, const FiniteElement *FE) const
Load element i into FE.
Definition nurbs.cpp:4981
void GetPatchDofs(const int patch, Array< int > &dofs)
Return the degrees of freedom in dofs on patch patch, in Cartesian order.
Definition nurbs.cpp:4705
Table * GetBdrElementDofTable()
Definition nurbs.hpp:1003
int GetNDof() const
Return the number of active DOFs.
Definition nurbs.hpp:967
int GetOrder() const
If all KnotVector orders are identical, return that number. Otherwise, return NURBSFECollection::Vari...
Definition nurbs.hpp:946
NURBSExtension * GetCurlExtension(int component)
Definition nurbs.cpp:5199
const Array< int > & GetOrders() const
Read-only access to the orders of all KnotVectors.
Definition nurbs.hpp:942
NURBSExtension * GetDivExtension(int component)
Definition nurbs.cpp:5184
void ConnectBoundaries()
Set DOF maps for periodic BC.
Definition nurbs.cpp:3360
Arbitrary order non-uniform rational B-splines (NURBS) finite elements.
Definition fe_coll.hpp:749
int GetOrder() const
Get the order of the NURBS collection: either a positive number, when using fixed order,...
Definition fe_coll.hpp:783
virtual void SetOrder(int Order) const
Set the order and the name, based on the given Order: either a positive number for fixed order,...
Definition fe_coll.cpp:3592
virtual void SetDim(const int dim)
Definition fe_coll.hpp:778
Arbitrary order H(curl) NURBS finite elements.
Definition fe_coll.hpp:860
Arbitrary order H(div) NURBS finite elements.
Definition fe_coll.hpp:808
Pointer to an Operator of a specified type.
Definition handle.hpp:34
OpType * As() const
Return the Operator pointer statically cast to a specified OpType. Similar to the method Get().
Definition handle.hpp:104
bool OwnsOperator() const
Return true if the OperatorHandle owns the held Operator.
Definition handle.hpp:117
void SetOperatorOwner(bool own=true)
Set the ownership flag for the held Operator.
Definition handle.hpp:120
void SetType(Operator::Type tid)
Invoke Clear() and set a new type id.
Definition handle.hpp:132
Operator * Ptr() const
Access the underlying Operator pointer.
Definition handle.hpp:87
void Clear()
Clear the OperatorHandle, deleting the held Operator (if owned), while leaving the type id unchanged.
Definition handle.hpp:124
void Reset(OpType *A, bool own_A=true)
Reset the OperatorHandle to the given OpType pointer, A.
Definition handle.hpp:145
OpType * Is() const
Return the Operator pointer dynamically cast to a specified OpType.
Definition handle.hpp:108
Operator::Type Type() const
Get the currently set operator type id.
Definition handle.hpp:99
Abstract operator.
Definition operator.hpp:27
int width
Dimension of the input / number of columns in the matrix.
Definition operator.hpp:30
int Height() const
Get the height (size of output) of the Operator. Synonym with NumRows().
Definition operator.hpp:68
int height
Dimension of the output / number of rows in the matrix.
Definition operator.hpp:29
int Width() const
Get the width (size of input) of the Operator. Synonym with NumCols().
Definition operator.hpp:74
Type
Enumeration defining IDs for some classes derived from Operator.
Definition operator.hpp:319
@ ANY_TYPE
ID for the base class Operator, i.e. any type.
Definition operator.hpp:320
@ MFEM_SPARSEMAT
ID for class SparseMatrix.
Definition operator.hpp:321
The ordering method used when the number of unknowns per mesh node (vector dimension) is bigger than ...
Definition ordering.hpp:13
Type
Ordering methods:
Definition ordering.hpp:17
A pair of objects.
Abstract parallel finite element space.
Definition pfespace.hpp:31
Parallel version of NURBSExtension.
Definition nurbs.hpp:1148
General product operator: x -> (A*B)(x) = A(B(x)).
Definition operator.hpp:969
A class that performs interpolation from an E-vector to quadrature point values and/or derivatives (Q...
static bool SupportsFESpace(const FiniteElementSpace &fespace)
Returns true if the given finite element space is supported by QuadratureInterpolator.
const IntegrationRule * IntRule
Not owned.
const QuadratureSpace * qspace
Not owned.
Class representing the storage layout of a QuadratureFunction.
Definition qspace.hpp:164
Data type sparse matrix.
Definition sparsemat.hpp:51
int GetRow(const int row, Array< int > &cols, Vector &srow) const override
Extract all column indices and values from a given row.
void Add(const int i, const int j, const real_t val)
void Swap(SparseMatrix &other)
void BooleanMultTranspose(const Array< int > &x, Array< int > &y) const
y = At * x, treating all entries as booleans (zero=false, nonzero=true).
void BooleanMult(const Array< int > &x, Array< int > &y) const
y = A * x, treating all entries as booleans (zero=false, nonzero=true).
int * GetRowColumns(const int row)
Return a pointer to the column indices in a row.
int RowSize(const int i) const
Returns the number of elements in row i.
real_t * GetRowEntries(const int row)
Return a pointer to the entries in a row.
void SetRow(const int row, const Array< int > &cols, const Vector &srow)
void Finalize(int skip_zeros=1) override
Finalize the matrix initialization, switching the storage format from LIL to CSR.
void Set(const int i, const int j, const real_t val)
Table stores the connectivity of elements of TYPE I to elements of TYPE II. For example,...
Definition table.hpp:43
int * GetJ()
Definition table.hpp:128
void AddConnections(int r, const int *c, int nc)
Definition table.cpp:152
int RowSize(int i) const
Definition table.hpp:122
void ShiftUpI()
Definition table.cpp:163
void Clear()
Definition table.cpp:420
void GetRow(int i, Array< int > &row) const
Return row i in array row (the Table must be finalized)
Definition table.cpp:233
void AddConnection(int r, int c)
Definition table.hpp:89
void MakeI(int nrows)
Definition table.cpp:130
int Size() const
Returns the number of TYPE I elements.
Definition table.hpp:103
int Size_of_connections() const
Returns the number of connections in the table.
Definition table.hpp:110
void AddColumnsInRow(int r, int ncol)
Definition table.hpp:87
void MakeFromList(int nrows, const Array< Connection > &list)
Create the table from a list of connections {(from, to)}, where 'from' is a TYPE I index and 'to' is ...
Definition table.cpp:322
void MakeJ()
Definition table.cpp:140
int * GetI()
Definition table.hpp:127
void AddAColumnInRow(int r)
Definition table.hpp:86
The transpose of a given operator. Switches the roles of the methods Mult() and MultTranspose().
Definition operator.hpp:922
General triple product operator x -> A*B*C*x, with ownership of the factors.
Vector data type.
Definition vector.hpp:82
void Print(std::ostream &out=mfem::out, int width=8) const
Prints vector to stream out.
Definition vector.cpp:870
void SetSubVector(const Array< int > &dofs, const real_t value)
Set the entries listed in dofs to the given value.
Definition vector.cpp:702
void AddElementVector(const Array< int > &dofs, const Vector &elemvect)
Add elements of the elemvect Vector to the entries listed in dofs. Negative dof values cause the -dof...
Definition vector.cpp:785
void Load(std::istream **in, int np, int *dim)
Reads a vector from multiple files.
Definition vector.cpp:127
real_t Max() const
Returns the maximal element of the vector.
Definition vector.cpp:1200
void SetSize(int s)
Resize the vector to size s.
Definition vector.hpp:633
real_t * GetData() const
Return a pointer to the beginning of the Vector data.
Definition vector.hpp:243
real_t Min() const
Returns the minimal element of the vector.
Definition vector.cpp:1154
void GetSubVector(const Array< int > &dofs, Vector &elemvect) const
Extract entries listed in dofs to the output Vector elemvect.
Definition vector.cpp:676
Vector & Add(const real_t a, const Vector &Va)
(*this) += a * Va
Definition vector.cpp:326
const int * ess_tdof_list
int dim
Definition ex24.cpp:53
int index(int i, int j, int nx, int ny)
Definition life.cpp:236
real_t b
Definition lissajous.cpp:42
real_t a
Definition lissajous.cpp:41
void RemoveBasisAndRestriction(const mfem::FiniteElementSpace *fes)
Remove from ceed_basis_map and ceed_restr_map the entries associated with the given fes.
Definition util.cpp:41
Linear1DFiniteElement SegmentFE
Definition segment.cpp:52
void mfem_error(const char *msg)
Definition error.cpp:154
void Mult(const Table &A, const Table &B, Table &C)
C = A * B (as boolean matrices)
Definition table.cpp:505
void DofMapHelper(int entity, const Table &var_ent_dofs, const Table &loc_var_ent_dofs, const Array< char > &var_ent_orders, const Array< char > &loc_var_ent_orders, Array< int > &all2local, int &ndof_all, int &ndof_loc)
Definition fespace.cpp:2919
void Transpose(const Table &A, Table &At, int ncols_A_)
Transpose a Table.
Definition table.cpp:443
MFEM_HOST_DEVICE int FlipIndexSign(int i)
Signed indices i -> -1 - i are used as a convention to encode orientation.
Definition globals.hpp:117
MFEM_HOST_DEVICE int UnsignIndex(int i)
Definition globals.hpp:118
void Swap(T &a, T &b)
Swap objects of type T. The operation is performed using the most specialized swap function from the ...
Definition array.hpp:767
void filter_dos(std::string &line)
Check for, and remove, a trailing '\r' from and std::string.
Definition text.hpp:45
bool UsesTensorBasis(const FiniteElementSpace &fes)
Return true if the mesh contains only one topology and the elements are tensor elements.
Definition fespace.hpp:1644
BiLinear2DFiniteElement QuadrilateralFE
ComplexDenseMatrix * MultAtB(const ComplexDenseMatrix &A, const ComplexDenseMatrix &B)
Multiply the complex conjugate transpose of a matrix A with a matrix B. A^H*B.
float real_t
Definition config.hpp:46
void AddMult(const DenseMatrix &b, const DenseMatrix &c, DenseMatrix &a)
Matrix matrix multiplication. A += B * C.
ElementDofOrdering GetEVectorOrdering(const FiniteElementSpace &fes)
Return LEXICOGRAPHIC if mesh contains only one topology and the elements are tensor elements,...
Definition fespace.cpp:4836
void MarkDofs(const Array< int > &dofs, Array< int > &mark_array)
Definition fespace.cpp:542
ElementDofOrdering
Constants describing the possible orderings of the DOFs in one element.
Definition fespace.hpp:49
@ NATIVE
Native ordering as defined by the FiniteElement.
std::function< real_t(const Vector &)> f(real_t mass_coeff)
Definition lor_mms.hpp:30
MFEM_EXPORT Linear2DFiniteElement TriangleFE
Definition fe.cpp:32
void skip_comment_lines(std::istream &is, const char comment_char)
Check if the stream starts with comment_char. If so skip it.
Definition text.hpp:31
FaceType
Definition mesh.hpp:49
STL namespace.
real_t p(const Vector &x, real_t t)
bool operator<(const Data &d1, const Data &d2)
Definition nurbs_ex1.cpp:58
Defines the coarse-fine transformations of all fine elements.
Definition ncmesh.hpp:90
Array< Embedding > embeddings
Fine element positions in their parents.
Definition ncmesh.hpp:92
void MakeCoarseToFineTable(Table &coarse_to_fine, bool want_ghosts=false) const
Definition ncmesh.cpp:5313
DenseTensor point_matrices[Geometry::NumGeom]
Definition ncmesh.hpp:96
Helper struct for defining a connectivity table, see Table::MakeFromList.
Definition table.hpp:28
Defines the position of a fine element within a coarse element.
Definition ncmesh.hpp:69
int parent
Coarse Element index in the coarse mesh.
Definition ncmesh.hpp:71
unsigned matrix
Definition ncmesh.hpp:78
This structure is used as a human readable output format that deciphers the information contained in ...
Definition mesh.hpp:2098
bool IsBoundary() const
Return true if the face is a boundary face.
Definition mesh.hpp:2145
bool IsNonconformingCoarse() const
Return true if the face is a nonconforming coarse face.
Definition mesh.hpp:2182
bool IsOfFaceType(FaceType type) const
Return true if the face is of the same type as type.
Definition mesh.hpp:2151
bool IsConforming() const
Return true if the face is a conforming face.
Definition mesh.hpp:2165
int index
Mesh number.
Definition ncmesh.hpp:261
Geometry::Type Geom() const
Definition ncmesh.hpp:266
Lists all edges/faces in the nonconforming mesh.
Definition ncmesh.hpp:301
Array< Slave > slaves
All MeshIds corresponding to slave faces.
Definition ncmesh.hpp:304
Array< Master > masters
All MeshIds corresponding to master faces.
Definition ncmesh.hpp:303
Nonconforming edge/face within a bigger edge/face.
Definition ncmesh.hpp:287
unsigned matrix
index into NCList::point_matrices[geom]
Definition ncmesh.hpp:289