MFEM v4.7.0
Finite element discretization library
Loading...
Searching...
No Matches
fespace.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2024, Lawrence Livermore National Security, LLC. Produced
2// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3// LICENSE and NOTICE for details. LLNL-CODE-806117.
4//
5// This file is part of the MFEM library. For more information and source code
6// availability visit https://mfem.org.
7//
8// MFEM is free software; you can redistribute it and/or modify it under the
9// terms of the BSD-3 license. We welcome feedback and contributions, see file
10// CONTRIBUTING.md for details.
11
12// Implementation of FiniteElementSpace
13
14#include "../general/text.hpp"
15#include "../general/forall.hpp"
17#include "fem.hpp"
19
20#include <cmath>
21#include <cstdarg>
22
23using namespace std;
24
25namespace mfem
26{
27
28template <> void Ordering::
29DofsToVDofs<Ordering::byNODES>(int ndofs, int vdim, Array<int> &dofs)
30{
31 // static method
32 int size = dofs.Size();
33 dofs.SetSize(size*vdim);
34 for (int vd = 1; vd < vdim; vd++)
35 {
36 for (int i = 0; i < size; i++)
37 {
38 dofs[i+size*vd] = Map<byNODES>(ndofs, vdim, dofs[i], vd);
39 }
40 }
41}
42
43template <> void Ordering::
44DofsToVDofs<Ordering::byVDIM>(int ndofs, int vdim, Array<int> &dofs)
45{
46 // static method
47 int size = dofs.Size();
48 dofs.SetSize(size*vdim);
49 for (int vd = vdim-1; vd >= 0; vd--)
50 {
51 for (int i = 0; i < size; i++)
52 {
53 dofs[i+size*vd] = Map<byVDIM>(ndofs, vdim, dofs[i], vd);
54 }
55 }
56}
57
58
60 : mesh(NULL), fec(NULL), vdim(0), ordering(Ordering::byNODES),
61 ndofs(0), nvdofs(0), nedofs(0), nfdofs(0), nbdofs(0),
62 bdofs(NULL),
63 elem_dof(NULL), elem_fos(NULL), bdr_elem_dof(NULL), bdr_elem_fos(NULL),
64 face_dof(NULL),
65 NURBSext(NULL), own_ext(false),
66 cP_is_set(false),
67 Th(Operator::ANY_TYPE),
68 sequence(0), mesh_sequence(0), orders_changed(false), relaxed_hp(false)
69{ }
70
72 Mesh *mesh_,
73 const FiniteElementCollection *fec_)
74{
75 mesh_ = mesh_ ? mesh_ : orig.mesh;
76 fec_ = fec_ ? fec_ : orig.fec;
77
78 NURBSExtension *nurbs_ext = NULL;
79 if (orig.NURBSext && orig.NURBSext != orig.mesh->NURBSext)
80 {
81#ifdef MFEM_USE_MPI
82 ParNURBSExtension *pNURBSext =
83 dynamic_cast<ParNURBSExtension *>(orig.NURBSext);
84 if (pNURBSext)
85 {
86 nurbs_ext = new ParNURBSExtension(*pNURBSext);
87 }
88 else
89#endif
90 {
91 nurbs_ext = new NURBSExtension(*orig.NURBSext);
92 }
93 }
94
95 Constructor(mesh_, nurbs_ext, fec_, orig.vdim, orig.ordering);
96}
97
99 const FiniteElementSpace &fes, const Array<int> *perm)
100{
101 MFEM_VERIFY(cP == NULL, "");
102 MFEM_VERIFY(cR == NULL, "");
103
104 SparseMatrix *perm_mat = NULL, *perm_mat_tr = NULL;
105 if (perm)
106 {
107 // Note: although n and fes.GetVSize() are typically equal, in
108 // variable-order spaces they may differ, since nonconforming edges/faces
109 // my have fictitious DOFs.
110 int n = perm->Size();
111 perm_mat = new SparseMatrix(n, fes.GetVSize());
112 for (int i=0; i<n; ++i)
113 {
114 real_t s;
115 int j = DecodeDof((*perm)[i], s);
116 perm_mat->Set(i, j, s);
117 }
118 perm_mat->Finalize();
119 perm_mat_tr = Transpose(*perm_mat);
120 }
121
122 if (fes.GetConformingProlongation() != NULL)
123 {
124 if (perm) { cP.reset(Mult(*perm_mat, *fes.GetConformingProlongation())); }
125 else { cP.reset(new SparseMatrix(*fes.GetConformingProlongation())); }
126 cP_is_set = true;
127 }
128 else if (perm != NULL)
129 {
130 cP.reset(perm_mat);
131 cP_is_set = true;
132 perm_mat = NULL;
133 }
134 if (fes.GetConformingRestriction() != NULL)
135 {
136 if (perm) { cR.reset(Mult(*fes.GetConformingRestriction(), *perm_mat_tr)); }
137 else { cR.reset(new SparseMatrix(*fes.GetConformingRestriction())); }
138 }
139 else if (perm != NULL)
140 {
141 cR.reset(perm_mat_tr);
142 perm_mat_tr = NULL;
143 }
144
145 delete perm_mat;
146 delete perm_mat_tr;
147}
148
150{
151 MFEM_VERIFY(mesh_sequence == mesh->GetSequence(),
152 "Space has not been Updated() after a Mesh change.");
153 MFEM_VERIFY(i >= 0 && i < GetNE(), "Invalid element index");
154 MFEM_VERIFY(p >= 0 && p <= MaxVarOrder, "Order out of range");
155 MFEM_ASSERT(!elem_order.Size() || elem_order.Size() == GetNE(),
156 "Internal error");
157
158 if (elem_order.Size()) // already a variable-order space
159 {
160 if (elem_order[i] != p)
161 {
162 elem_order[i] = p;
163 orders_changed = true;
164 }
165 }
166 else // convert space to variable-order space
167 {
170
171 elem_order[i] = p;
172 orders_changed = true;
173 }
174}
175
177{
178 MFEM_VERIFY(mesh_sequence == mesh->GetSequence(),
179 "Space has not been Updated() after a Mesh change.");
180 MFEM_VERIFY(i >= 0 && i < GetNE(), "Invalid element index");
181 MFEM_ASSERT(!elem_order.Size() || elem_order.Size() == GetNE(),
182 "Internal error");
183
184 return GetElementOrderImpl(i);
185}
186
188{
189 // (this is an internal version of GetElementOrder without asserts and checks)
190 return elem_order.Size() ? elem_order[i] : fec->GetOrder();
191}
192
193void FiniteElementSpace::GetVDofs(int vd, Array<int>& dofs, int ndofs_) const
194{
195 if (ndofs_ < 0) { ndofs_ = this->ndofs; }
196
198 {
199 for (int i = 0; i < dofs.Size(); i++)
200 {
201 dofs[i] = Ordering::Map<Ordering::byNODES>(ndofs_, vdim, i, vd);
202 }
203 }
204 else
205 {
206 for (int i = 0; i < dofs.Size(); i++)
207 {
208 dofs[i] = Ordering::Map<Ordering::byVDIM>(ndofs_, vdim, i, vd);
209 }
210 }
211}
212
213void FiniteElementSpace::DofsToVDofs(Array<int> &dofs, int ndofs_) const
214{
215 if (vdim == 1) { return; }
216 if (ndofs_ < 0) { ndofs_ = this->ndofs; }
217
219 {
220 Ordering::DofsToVDofs<Ordering::byNODES>(ndofs_, vdim, dofs);
221 }
222 else
223 {
224 Ordering::DofsToVDofs<Ordering::byVDIM>(ndofs_, vdim, dofs);
225 }
226}
227
228void FiniteElementSpace::DofsToVDofs(int vd, Array<int> &dofs, int ndofs_) const
229{
230 if (vdim == 1) { return; }
231 if (ndofs_ < 0) { ndofs_ = this->ndofs; }
232
234 {
235 for (int i = 0; i < dofs.Size(); i++)
236 {
237 dofs[i] = Ordering::Map<Ordering::byNODES>(ndofs_, vdim, dofs[i], vd);
238 }
239 }
240 else
241 {
242 for (int i = 0; i < dofs.Size(); i++)
243 {
244 dofs[i] = Ordering::Map<Ordering::byVDIM>(ndofs_, vdim, dofs[i], vd);
245 }
246 }
247}
248
249int FiniteElementSpace::DofToVDof(int dof, int vd, int ndofs_) const
250{
251 if (vdim == 1) { return dof; }
252 if (ndofs_ < 0) { ndofs_ = this->ndofs; }
253
255 {
256 return Ordering::Map<Ordering::byNODES>(ndofs_, vdim, dof, vd);
257 }
258 else
259 {
260 return Ordering::Map<Ordering::byVDIM>(ndofs_, vdim, dof, vd);
261 }
262}
263
264// static function
266{
267 int n = vdofs.Size(), *vdof = vdofs;
268 for (int i = 0; i < n; i++)
269 {
270 int j;
271 if ((j = vdof[i]) < 0)
272 {
273 vdof[i] = -1-j;
274 }
275 }
276}
277
279 DofTransformation &doftrans) const
280{
281 GetElementDofs(i, vdofs, doftrans);
282 DofsToVDofs(vdofs);
283 doftrans.SetVDim(vdim, ordering);
284}
285
288{
290 GetElementVDofs(i, vdofs, DoFTrans);
291 return DoFTrans.GetDofTransformation() ? &DoFTrans : NULL;
292}
293
295 DofTransformation &doftrans) const
296{
297 GetBdrElementDofs(i, vdofs, doftrans);
298 DofsToVDofs(vdofs);
299 doftrans.SetVDim(vdim, ordering);
300}
301
309
311{
312 GetPatchDofs(i, vdofs);
313 DofsToVDofs(vdofs);
314}
315
317{
318 GetFaceDofs(i, vdofs);
319 DofsToVDofs(vdofs);
320}
321
323{
324 GetEdgeDofs(i, vdofs);
325 DofsToVDofs(vdofs);
326}
327
329{
330 GetVertexDofs(i, vdofs);
331 DofsToVDofs(vdofs);
332}
333
335{
336 GetElementInteriorDofs(i, vdofs);
337 DofsToVDofs(vdofs);
338}
339
341{
342 GetEdgeInteriorDofs(i, vdofs);
343 DofsToVDofs(vdofs);
344}
345
347{
348 if (elem_dof) { return; }
349
350 // TODO: can we call GetElementDofs only once per element?
351 Table *el_dof = new Table;
352 Table *el_fos = (mesh->Dimension() > 2) ? (new Table) : NULL;
353 Array<int> dofs;
354 Array<int> F, Fo;
355 el_dof -> MakeI (mesh -> GetNE());
356 if (el_fos) { el_fos -> MakeI (mesh -> GetNE()); }
357 for (int i = 0; i < mesh -> GetNE(); i++)
358 {
359 GetElementDofs (i, dofs);
360 el_dof -> AddColumnsInRow (i, dofs.Size());
361
362 if (el_fos)
363 {
364 mesh->GetElementFaces(i, F, Fo);
365 el_fos -> AddColumnsInRow (i, Fo.Size());
366 }
367 }
368 el_dof -> MakeJ();
369 if (el_fos) { el_fos -> MakeJ(); }
370 for (int i = 0; i < mesh -> GetNE(); i++)
371 {
372 GetElementDofs (i, dofs);
373 el_dof -> AddConnections (i, (int *)dofs, dofs.Size());
374
375 if (el_fos)
376 {
377 mesh->GetElementFaces(i, F, Fo);
378 el_fos -> AddConnections (i, (int *)Fo, Fo.Size());
379 }
380 }
381 el_dof -> ShiftUpI();
382 if (el_fos) { el_fos -> ShiftUpI(); }
383 elem_dof = el_dof;
384 elem_fos = el_fos;
385}
386
388{
389 if (bdr_elem_dof) { return; }
390
391 Table *bel_dof = new Table;
392 Table *bel_fos = (mesh->Dimension() == 3) ? (new Table) : NULL;
393 Array<int> dofs;
394 int F, Fo;
395 bel_dof->MakeI(mesh->GetNBE());
396 if (bel_fos) { bel_fos->MakeI(mesh->GetNBE()); }
397 for (int i = 0; i < mesh->GetNBE(); i++)
398 {
399 GetBdrElementDofs(i, dofs);
400 bel_dof->AddColumnsInRow(i, dofs.Size());
401
402 if (bel_fos)
403 {
404 bel_fos->AddAColumnInRow(i);
405 }
406 }
407 bel_dof->MakeJ();
408 if (bel_fos) { bel_fos->MakeJ(); }
409 for (int i = 0; i < mesh->GetNBE(); i++)
410 {
411 GetBdrElementDofs(i, dofs);
412 bel_dof->AddConnections(i, (int *)dofs, dofs.Size());
413
414 if (bel_fos)
415 {
416 mesh->GetBdrElementFace(i, &F, &Fo);
417 bel_fos->AddConnection(i, Fo);
418 }
419 }
420 bel_dof->ShiftUpI();
421 if (bel_fos) { bel_fos->ShiftUpI(); }
422 bdr_elem_dof = bel_dof;
423 bdr_elem_fos = bel_fos;
424}
425
427{
428 // Here, "face" == (dim-1)-dimensional mesh entity.
429
430 if (face_dof) { return; }
431
432 if (NURBSext) { BuildNURBSFaceToDofTable(); return; }
433
434 Table *fc_dof = new Table;
435 Array<int> dofs;
436 fc_dof->MakeI(mesh->GetNumFaces());
437 for (int i = 0; i < fc_dof->Size(); i++)
438 {
439 GetFaceDofs(i, dofs, 0);
440 fc_dof->AddColumnsInRow(i, dofs.Size());
441 }
442 fc_dof->MakeJ();
443 for (int i = 0; i < fc_dof->Size(); i++)
444 {
445 GetFaceDofs(i, dofs, 0);
446 fc_dof->AddConnections(i, (int *)dofs, dofs.Size());
447 }
448 fc_dof->ShiftUpI();
449 face_dof = fc_dof;
450}
451
453{
454 delete elem_dof;
455 delete elem_fos;
456 elem_dof = NULL;
457 elem_fos = NULL;
459}
460
462{
463 Array<int> dof_marker(ndofs);
464
465 dof_marker = -1;
466
467 int *J = elem_dof->GetJ(), nnz = elem_dof->Size_of_connections();
468 for (int k = 0, dof_counter = 0; k < nnz; k++)
469 {
470 const int sdof = J[k]; // signed dof
471 const int dof = (sdof < 0) ? -1-sdof : sdof;
472 int new_dof = dof_marker[dof];
473 if (new_dof < 0)
474 {
475 dof_marker[dof] = new_dof = dof_counter++;
476 }
477 J[k] = (sdof < 0) ? -1-new_dof : new_dof; // preserve the sign of sdof
478 }
479}
480
482{
483 if (dof_elem_array.Size()) { return; }
484
486
489 dof_elem_array = -1;
490 for (int i = 0; i < mesh -> GetNE(); i++)
491 {
492 const int *dofs = elem_dof -> GetRow(i);
493 const int n = elem_dof -> RowSize(i);
494 for (int j = 0; j < n; j++)
495 {
496 int dof = DecodeDof(dofs[j]);
497 if (dof_elem_array[dof] < 0)
498 {
499 dof_elem_array[dof] = i;
500 dof_ldof_array[dof] = j;
501 }
502 }
503 }
504}
505
506void MarkDofs(const Array<int> &dofs, Array<int> &mark_array)
507{
508 for (auto d : dofs)
509 {
510 mark_array[d >= 0 ? d : -1 - d] = -1;
511 }
512}
513
515 Array<int> &ess_vdofs,
516 int component) const
517{
518 Array<int> dofs;
519 ess_vdofs.SetSize(GetVSize());
520 ess_vdofs = 0;
521 for (int i = 0; i < GetNBE(); i++)
522 {
523 if (bdr_attr_is_ess[GetBdrAttribute(i)-1])
524 {
525 if (component < 0)
526 {
527 // Mark all components.
528 GetBdrElementVDofs(i, dofs);
529 }
530 else
531 {
532 GetBdrElementDofs(i, dofs);
533 for (auto &d : dofs) { d = DofToVDof(d, component); }
534 }
535 MarkDofs(dofs, ess_vdofs);
536 }
537 }
538
539 // mark possible hidden boundary edges in a non-conforming mesh, also
540 // local DOFs affected by boundary elements on other processors
541 if (Nonconforming())
542 {
543 Array<int> bdr_verts, bdr_edges, bdr_faces;
544 mesh->ncmesh->GetBoundaryClosure(bdr_attr_is_ess, bdr_verts, bdr_edges,
545 bdr_faces);
546 for (auto v : bdr_verts)
547 {
548 if (component < 0)
549 {
550 GetVertexVDofs(v, dofs);
551 }
552 else
553 {
554 GetVertexDofs(v, dofs);
555 for (auto &d : dofs) { d = DofToVDof(d, component); }
556 }
557 MarkDofs(dofs, ess_vdofs);
558 }
559 for (auto e : bdr_edges)
560 {
561 if (component < 0)
562 {
563 GetEdgeVDofs(e, dofs);
564 }
565 else
566 {
567 GetEdgeDofs(e, dofs);
568 for (auto &d : dofs) { d = DofToVDof(d, component); }
569 }
570 MarkDofs(dofs, ess_vdofs);
571 }
572 for (auto f : bdr_faces)
573 {
574 if (component < 0)
575 {
576 GetEntityVDofs(2, f, dofs);
577 }
578 else
579 {
580 GetEntityDofs(2, f, dofs);
581 for (auto &d : dofs) { d = DofToVDof(d, component); }
582 }
583 MarkDofs(dofs, ess_vdofs);
584 }
585 }
586}
587
589 Array<int> &ess_tdof_list,
590 int component) const
591{
592 Array<int> ess_vdofs, ess_tdofs;
593 GetEssentialVDofs(bdr_attr_is_ess, ess_vdofs, component);
595 if (!R)
596 {
597 ess_tdofs.MakeRef(ess_vdofs);
598 }
599 else
600 {
601 R->BooleanMult(ess_vdofs, ess_tdofs);
602#ifdef MFEM_DEBUG
603 // Verify that in boolean arithmetic: P^T ess_dofs = R ess_dofs
604 Array<int> ess_tdofs2(ess_tdofs.Size());
605 GetConformingProlongation()->BooleanMultTranspose(ess_vdofs, ess_tdofs2);
606
607 int counter = 0;
608 std::string error_msg = "failed dof: ";
609 auto ess_tdofs_ = ess_tdofs.HostRead();
610 auto ess_tdofs2_ = ess_tdofs2.HostRead();
611 for (int i = 0; i < ess_tdofs2.Size(); ++i)
612 {
613 if (bool(ess_tdofs_[i]) != bool(ess_tdofs2_[i]))
614 {
615 error_msg += std::to_string(i) += "(R ";
616 error_msg += std::to_string(bool(ess_tdofs_[i])) += " P^T ";
617 error_msg += std::to_string(bool(ess_tdofs2_[i])) += ") ";
618 counter++;
619 }
620 }
621
622 MFEM_ASSERT(R->Height() == GetConformingProlongation()->Width(), "!");
623 MFEM_ASSERT(R->Width() == GetConformingProlongation()->Height(), "!");
624 MFEM_ASSERT(R->Width() == ess_vdofs.Size(), "!");
625 MFEM_VERIFY(counter == 0, "internal MFEM error: counter = " << counter
626 << ' ' << error_msg);
627#endif
628 }
629 MarkerToList(ess_tdofs, ess_tdof_list);
630}
631
633 int component)
634{
635 if (mesh->bdr_attributes.Size())
636 {
637 Array<int> ess_bdr(mesh->bdr_attributes.Max());
638 ess_bdr = 1;
639 GetEssentialTrueDofs(ess_bdr, boundary_dofs, component);
640 }
641 else
642 {
643 boundary_dofs.DeleteAll();
644 }
645}
646
647// static method
649 Array<int> &list)
650{
651 int num_marked = 0;
652 marker.HostRead(); // make sure we can read the array on host
653 for (int i = 0; i < marker.Size(); i++)
654 {
655 if (marker[i]) { num_marked++; }
656 }
657 list.SetSize(0);
658 list.HostWrite();
659 list.Reserve(num_marked);
660 for (int i = 0; i < marker.Size(); i++)
661 {
662 if (marker[i]) { list.Append(i); }
663 }
664}
665
666// static method
667void FiniteElementSpace::ListToMarker(const Array<int> &list, int marker_size,
668 Array<int> &marker, int mark_val)
669{
670 list.HostRead(); // make sure we can read the array on host
671 marker.SetSize(marker_size);
672 marker.HostWrite();
673 marker = 0;
674 for (int i = 0; i < list.Size(); i++)
675 {
676 marker[list[i]] = mark_val;
677 }
678}
679
681 Array<int> &cdofs)
682{
684 if (cP) { cP->BooleanMultTranspose(dofs, cdofs); }
685 else { dofs.Copy(cdofs); }
686}
687
689 Array<int> &dofs)
690{
692 if (cR) { cR->BooleanMultTranspose(cdofs, dofs); }
693 else { cdofs.Copy(dofs); }
694}
695
698{
699 int i, j;
700 Array<int> d_vdofs, c_vdofs;
701 SparseMatrix *R;
702
703 R = new SparseMatrix (cfes -> GetVSize(), GetVSize());
704
705 for (i = 0; i < mesh -> GetNE(); i++)
706 {
707 this -> GetElementVDofs (i, d_vdofs);
708 cfes -> GetElementVDofs (i, c_vdofs);
709
710#ifdef MFEM_DEBUG
711 if (d_vdofs.Size() != c_vdofs.Size())
712 {
713 mfem_error ("FiniteElementSpace::D2C_GlobalRestrictionMatrix (...)");
714 }
715#endif
716
717 for (j = 0; j < d_vdofs.Size(); j++)
718 {
719 R -> Set (c_vdofs[j], d_vdofs[j], 1.0);
720 }
721 }
722
723 R -> Finalize();
724
725 return R;
726}
727
730{
731 int i, j;
732 Array<int> d_dofs, c_dofs;
733 SparseMatrix *R;
734
735 R = new SparseMatrix (cfes -> GetNDofs(), ndofs);
736
737 for (i = 0; i < mesh -> GetNE(); i++)
738 {
739 this -> GetElementDofs (i, d_dofs);
740 cfes -> GetElementDofs (i, c_dofs);
741
742#ifdef MFEM_DEBUG
743 if (c_dofs.Size() != 1)
744 mfem_error ("FiniteElementSpace::"
745 "D2Const_GlobalRestrictionMatrix (...)");
746#endif
747
748 for (j = 0; j < d_dofs.Size(); j++)
749 {
750 R -> Set (c_dofs[0], d_dofs[j], 1.0);
751 }
752 }
753
754 R -> Finalize();
755
756 return R;
757}
758
761{
762 SparseMatrix *R;
763 DenseMatrix loc_restr;
764 Array<int> l_dofs, h_dofs, l_vdofs, h_vdofs;
765
766 int lvdim = lfes->GetVDim();
767 R = new SparseMatrix (lvdim * lfes -> GetNDofs(), lvdim * ndofs);
768
769 Geometry::Type cached_geom = Geometry::INVALID;
770 const FiniteElement *h_fe = NULL;
771 const FiniteElement *l_fe = NULL;
773
774 for (int i = 0; i < mesh -> GetNE(); i++)
775 {
776 this -> GetElementDofs (i, h_dofs);
777 lfes -> GetElementDofs (i, l_dofs);
778
779 // Assuming 'loc_restr' depends only on the Geometry::Type.
781 if (geom != cached_geom)
782 {
783 h_fe = this -> GetFE (i);
784 l_fe = lfes -> GetFE (i);
786 h_fe->Project(*l_fe, T, loc_restr);
787 cached_geom = geom;
788 }
789
790 for (int vd = 0; vd < lvdim; vd++)
791 {
792 l_dofs.Copy(l_vdofs);
793 lfes->DofsToVDofs(vd, l_vdofs);
794
795 h_dofs.Copy(h_vdofs);
796 this->DofsToVDofs(vd, h_vdofs);
797
798 R -> SetSubMatrix (l_vdofs, h_vdofs, loc_restr, 1);
799 }
800 }
801
802 R -> Finalize();
803
804 return R;
805}
806
808 SparseMatrix& deps, Array<int>& master_dofs, Array<int>& slave_dofs,
809 DenseMatrix& I, int skipfirst)
811 for (int i = skipfirst; i < slave_dofs.Size(); i++)
812 {
813 int sdof = slave_dofs[i];
814 if (!deps.RowSize(sdof)) // not processed yet
815 {
816 for (int j = 0; j < master_dofs.Size(); j++)
817 {
818 real_t coef = I(i, j);
819 if (std::abs(coef) > 1e-12)
820 {
821 int mdof = master_dofs[j];
822 if (mdof != sdof && mdof != (-1-sdof))
823 {
824 deps.Add(sdof, mdof, coef);
825 }
826 }
827 }
828 }
829 }
830}
831
833 SparseMatrix &deps, Array<int> &master_dofs, const FiniteElement *master_fe,
834 Array<int> &slave_dofs, int slave_face, const DenseMatrix *pm) const
835{
836 // In variable-order spaces in 3D, we need to only constrain interior face
837 // DOFs (this is done one level up), since edge dependencies can be more
838 // complex and are primarily handled by edge-edge dependencies. The one
839 // exception is edges of slave faces that lie in the interior of the master
840 // face, which are not covered by edge-edge relations. This function finds
841 // such edges and makes them constrained by the master face.
842 // See also https://github.com/mfem/mfem/pull/1423#issuecomment-633916643
843
844 Array<int> V, E, Eo; // TODO: LocalArray
845 mesh->GetFaceVertices(slave_face, V);
846 mesh->GetFaceEdges(slave_face, E, Eo);
847 MFEM_ASSERT(V.Size() == E.Size(), "");
848
849 DenseMatrix I;
851 edge_T.SetFE(&SegmentFE);
852
853 // constrain each edge of the slave face
854 for (int i = 0; i < E.Size(); i++)
855 {
856 int a = i, b = (i+1) % V.Size();
857 if (V[a] > V[b]) { std::swap(a, b); }
858
859 DenseMatrix &edge_pm = edge_T.GetPointMat();
860 edge_pm.SetSize(2, 2);
861
862 // copy two points from the face point matrix
863 real_t mid[2];
864 for (int j = 0; j < 2; j++)
865 {
866 edge_pm(j, 0) = (*pm)(j, a);
867 edge_pm(j, 1) = (*pm)(j, b);
868 mid[j] = 0.5*((*pm)(j, a) + (*pm)(j, b));
869 }
870
871 // check that the edge does not coincide with the master face's edge
872 const real_t eps = 1e-14;
873 if (mid[0] > eps && mid[0] < 1-eps &&
874 mid[1] > eps && mid[1] < 1-eps)
875 {
876 int order = GetEdgeDofs(E[i], slave_dofs, 0);
877
878 const auto *edge_fe = fec->GetFE(Geometry::SEGMENT, order);
879 edge_fe->GetTransferMatrix(*master_fe, edge_T, I);
880
881 AddDependencies(deps, master_dofs, slave_dofs, I, 0);
882 }
883 }
884}
885
886bool FiniteElementSpace::DofFinalizable(int dof, const Array<bool>& finalized,
887 const SparseMatrix& deps)
888{
889 const int* dep = deps.GetRowColumns(dof);
890 int ndep = deps.RowSize(dof);
891
892 // are all constraining DOFs finalized?
893 for (int i = 0; i < ndep; i++)
894 {
895 if (!finalized[dep[i]]) { return false; }
896 }
897 return true;
898}
899
901 Geometry::Type master_geom,
902 int variant) const
903{
904 // In NC meshes with prisms/tets, a special constraint occurs where a
905 // prism/tet edge is slave to another element's face (see illustration
906 // here: https://github.com/mfem/mfem/pull/713#issuecomment-495786362)
907 // Rather than introduce a new edge-face constraint type, we handle such
908 // cases as degenerate face-face constraints, where the point-matrix
909 // rectangle has zero height. This method returns DOFs for the first edge
910 // of the rectangle, duplicated in the orthogonal direction, to resemble
911 // DOFs for a quadrilateral face. The extra DOFs are ignored by
912 // FiniteElementSpace::AddDependencies.
913
914 Array<int> edof;
915 int order = GetEdgeDofs(-1 - index, edof, variant);
916
919 int nn = 2*nv + ne;
920
921 dofs.SetSize(nn*nn);
922 if (!dofs.Size()) { return 0; }
923
924 dofs = edof[0];
925
926 // copy first two vertex DOFs
927 for (int i = 0; i < nv; i++)
928 {
929 dofs[i] = edof[i];
930 dofs[nv+i] = edof[nv+i];
931 }
932 // copy first edge DOFs
933 int face_vert = Geometry::NumVerts[master_geom];
934 for (int i = 0; i < ne; i++)
935 {
936 dofs[face_vert*nv + i] = edof[2*nv + i];
937 }
938
939 return order;
940}
941
943{
944 // return the number of vertex and edge DOFs that precede inner DOFs
945 const int nv = fec->GetNumDof(Geometry::POINT, order);
946 const int ne = fec->GetNumDof(Geometry::SEGMENT, order);
947
948 return Geometry::NumVerts[geom] * (geom == Geometry::SEGMENT ? nv : (nv + ne));
949}
950
952 Geometry::Type master_geom,
953 int variant) const
954{
955 switch (entity)
956 {
957 case 0:
958 GetVertexDofs(index, dofs);
959 return 0;
960
961 case 1:
962 return GetEdgeDofs(index, dofs, variant);
963
964 default:
965 if (index >= 0)
966 {
967 return GetFaceDofs(index, dofs, variant);
968 }
969 else
970 {
971 return GetDegenerateFaceDofs(index, dofs, master_geom, variant);
972 }
973 }
974}
975
977 Geometry::Type master_geom,
978 int variant) const
979{
980 const int n = GetEntityDofs(entity, index, dofs, master_geom, variant);
981 DofsToVDofs(dofs);
982 return n;
983}
984
986{
987#ifdef MFEM_USE_MPI
988 MFEM_VERIFY(dynamic_cast<const ParFiniteElementSpace*>(this) == NULL,
989 "This method should not be used with a ParFiniteElementSpace!");
990#endif
991
992 if (cP_is_set) { return; }
993 cP_is_set = true;
994
995 if (FEColl()->GetContType() == FiniteElementCollection::DISCONTINUOUS)
996 {
997 cP.reset();
998 cR.reset();
999 cR_hp.reset();
1000 R_transpose.reset();
1001 return;
1002 }
1003
1004 Array<int> master_dofs, slave_dofs, highest_dofs;
1005
1007 DenseMatrix I;
1008
1009 // For each slave DOF, the dependency matrix will contain a row that
1010 // expresses the slave DOF as a linear combination of its immediate master
1011 // DOFs. Rows of independent DOFs will remain empty.
1012 SparseMatrix deps(ndofs);
1013
1014 // Inverse dependencies for the cR_hp matrix in variable order spaces:
1015 // For each master edge/face with more DOF sets, the inverse dependency
1016 // matrix contains a row that expresses the master true DOF (lowest order)
1017 // as a linear combination of the highest order set of DOFs.
1018 SparseMatrix inv_deps(ndofs);
1019
1020 // collect local face/edge dependencies
1021 for (int entity = 2; entity >= 1; entity--)
1022 {
1023 const NCMesh::NCList &list = mesh->ncmesh->GetNCList(entity);
1024 if (!list.masters.Size()) { continue; }
1025
1026 // loop through all master edges/faces, constrain their slave edges/faces
1027 for (const NCMesh::Master &master : list.masters)
1028 {
1029 Geometry::Type master_geom = master.Geom();
1030
1031 int p = GetEntityDofs(entity, master.index, master_dofs, master_geom);
1032 if (!master_dofs.Size()) { continue; }
1033
1034 const FiniteElement *master_fe = fec->GetFE(master_geom, p);
1035 if (!master_fe) { continue; }
1036
1037 switch (master_geom)
1038 {
1039 case Geometry::SQUARE: T.SetFE(&QuadrilateralFE); break;
1040 case Geometry::TRIANGLE: T.SetFE(&TriangleFE); break;
1041 case Geometry::SEGMENT: T.SetFE(&SegmentFE); break;
1042 default: MFEM_ABORT("unsupported geometry");
1043 }
1044
1045 for (int si = master.slaves_begin; si < master.slaves_end; si++)
1046 {
1047 const NCMesh::Slave &slave = list.slaves[si];
1048
1049 int q = GetEntityDofs(entity, slave.index, slave_dofs, master_geom);
1050 if (!slave_dofs.Size()) { break; }
1051
1052 const FiniteElement *slave_fe = fec->GetFE(slave.Geom(), q);
1053 list.OrientedPointMatrix(slave, T.GetPointMat());
1054 slave_fe->GetTransferMatrix(*master_fe, T, I);
1055
1056 // variable order spaces: face edges need to be handled separately
1057 int skipfirst = 0;
1058 if (IsVariableOrder() && entity == 2 && slave.index >= 0)
1059 {
1060 skipfirst = GetNumBorderDofs(master_geom, q);
1061 }
1062
1063 // make each slave DOF dependent on all master DOFs
1064 AddDependencies(deps, master_dofs, slave_dofs, I, skipfirst);
1065
1066 if (skipfirst)
1067 {
1068 // constrain internal edge DOFs if they were skipped
1069 const auto *pm = list.point_matrices[master_geom][slave.matrix];
1070 AddEdgeFaceDependencies(deps, master_dofs, master_fe,
1071 slave_dofs, slave.index, pm);
1072 }
1073 }
1074
1075 // Add inverse dependencies for the cR_hp matrix; if a master has
1076 // more DOF sets, the lowest order set interpolates the highest one.
1077 if (IsVariableOrder())
1078 {
1079 int nvar = GetNVariants(entity, master.index);
1080 if (nvar > 1)
1081 {
1082 int q = GetEntityDofs(entity, master.index, highest_dofs,
1083 master_geom, nvar-1);
1084 const auto *highest_fe = fec->GetFE(master_geom, q);
1085
1086 T.SetIdentityTransformation(master_geom);
1087 master_fe->GetTransferMatrix(*highest_fe, T, I);
1088
1089 // add dependencies only for the inner dofs
1090 int skip = GetNumBorderDofs(master_geom, p);
1091 AddDependencies(inv_deps, highest_dofs, master_dofs, I, skip);
1092 }
1093 }
1094 }
1095 }
1096
1097 // variable order spaces: enforce minimum rule on conforming edges/faces
1098 if (IsVariableOrder())
1099 {
1100 for (int entity = 1; entity < mesh->Dimension(); entity++)
1101 {
1102 const Table &ent_dofs = (entity == 1) ? var_edge_dofs : var_face_dofs;
1103 int num_ent = (entity == 1) ? mesh->GetNEdges() : mesh->GetNFaces();
1104 MFEM_ASSERT(ent_dofs.Size() == num_ent+1, "");
1105
1106 // add constraints within edges/faces holding multiple DOF sets
1108 for (int i = 0; i < num_ent; i++)
1109 {
1110 if (ent_dofs.RowSize(i) <= 1) { continue; }
1111
1112 Geometry::Type geom =
1113 (entity == 1) ? Geometry::SEGMENT : mesh->GetFaceGeometry(i);
1114
1115 if (geom != last_geom)
1116 {
1118 last_geom = geom;
1119 }
1120
1121 // get lowest order variant DOFs and FE
1122 int p = GetEntityDofs(entity, i, master_dofs, geom, 0);
1123 const auto *master_fe = fec->GetFE(geom, p);
1124 if (!master_fe) { break; }
1125
1126 // constrain all higher order DOFs: interpolate lowest order function
1127 for (int variant = 1; ; variant++)
1128 {
1129 int q = GetEntityDofs(entity, i, slave_dofs, geom, variant);
1130 if (q < 0) { break; }
1131
1132 const auto *slave_fe = fec->GetFE(geom, q);
1133 slave_fe->GetTransferMatrix(*master_fe, T, I);
1134
1135 AddDependencies(deps, master_dofs, slave_dofs, I);
1136 }
1137 }
1138 }
1139 }
1140
1141 deps.Finalize();
1142 inv_deps.Finalize();
1143
1144 // DOFs that stayed independent are true DOFs
1145 int n_true_dofs = 0;
1146 for (int i = 0; i < ndofs; i++)
1147 {
1148 if (!deps.RowSize(i)) { n_true_dofs++; }
1149 }
1150
1151 // if all dofs are true dofs leave cP and cR NULL
1152 if (n_true_dofs == ndofs)
1153 {
1154 cP.reset();
1155 cR.reset();
1156 cR_hp.reset();
1157 R_transpose.reset();
1158 return;
1159 }
1160
1161 // create the conforming prolongation matrix cP
1162 cP.reset(new SparseMatrix(ndofs, n_true_dofs));
1163
1164 // create the conforming restriction matrix cR
1165 int *cR_J;
1166 {
1167 int *cR_I = Memory<int>(n_true_dofs+1);
1168 real_t *cR_A = Memory<real_t>(n_true_dofs);
1169 cR_J = Memory<int>(n_true_dofs);
1170 for (int i = 0; i < n_true_dofs; i++)
1171 {
1172 cR_I[i] = i;
1173 cR_A[i] = 1.0;
1174 }
1175 cR_I[n_true_dofs] = n_true_dofs;
1176 cR.reset(new SparseMatrix(cR_I, cR_J, cR_A, n_true_dofs, ndofs));
1177 }
1178
1179 // In var. order spaces, create the restriction matrix cR_hp which is similar
1180 // to cR, but has interpolation in the extra master edge/face DOFs.
1181 if (IsVariableOrder())
1182 {
1183 cR_hp.reset(new SparseMatrix(n_true_dofs, ndofs));
1184 }
1185 else
1186 {
1187 cR_hp.reset();
1188 }
1189
1190 Array<bool> finalized(ndofs);
1191 finalized = false;
1192
1193 Array<int> cols;
1194 Vector srow;
1195
1196 // put identity in the prolongation matrix for true DOFs, initialize cR_hp
1197 for (int i = 0, true_dof = 0; i < ndofs; i++)
1198 {
1199 if (!deps.RowSize(i)) // true dof
1200 {
1201 cP->Add(i, true_dof, 1.0);
1202 cR_J[true_dof] = i;
1203 finalized[i] = true;
1204
1205 if (cR_hp)
1206 {
1207 if (inv_deps.RowSize(i))
1208 {
1209 inv_deps.GetRow(i, cols, srow);
1210 cR_hp->AddRow(true_dof, cols, srow);
1211 }
1212 else
1213 {
1214 cR_hp->Add(true_dof, i, 1.0);
1215 }
1216 }
1217
1218 true_dof++;
1219 }
1220 }
1221
1222 // Now calculate cP rows of slave DOFs as combinations of cP rows of their
1223 // master DOFs. It is possible that some slave DOFs depend on DOFs that are
1224 // themselves slaves. Here we resolve such indirect constraints by first
1225 // calculating rows of the cP matrix for DOFs whose master DOF cP rows are
1226 // already known (in the first iteration these are the true DOFs). In the
1227 // second iteration, slaves of slaves can be 'finalized' (given a row in the
1228 // cP matrix), in the third iteration slaves of slaves of slaves, etc.
1229 bool finished;
1230 int n_finalized = n_true_dofs;
1231 do
1232 {
1233 finished = true;
1234 for (int dof = 0; dof < ndofs; dof++)
1235 {
1236 if (!finalized[dof] && DofFinalizable(dof, finalized, deps))
1237 {
1238 const int* dep_col = deps.GetRowColumns(dof);
1239 const real_t* dep_coef = deps.GetRowEntries(dof);
1240 int n_dep = deps.RowSize(dof);
1241
1242 for (int j = 0; j < n_dep; j++)
1243 {
1244 cP->GetRow(dep_col[j], cols, srow);
1245 srow *= dep_coef[j];
1246 cP->AddRow(dof, cols, srow);
1247 }
1248
1249 finalized[dof] = true;
1250 n_finalized++;
1251 finished = false;
1252 }
1253 }
1254 }
1255 while (!finished);
1256
1257 // If everything is consistent (mesh, face orientations, etc.), we should
1258 // be able to finalize all slave DOFs, otherwise it's a serious error.
1259 MFEM_VERIFY(n_finalized == ndofs,
1260 "Error creating cP matrix: n_finalized = "
1261 << n_finalized << ", ndofs = " << ndofs);
1262
1263 cP->Finalize();
1264 if (cR_hp) { cR_hp->Finalize(); }
1265
1266 if (vdim > 1)
1267 {
1270 if (cR_hp) { MakeVDimMatrix(*cR_hp); }
1271 }
1272}
1273
1275{
1276 if (vdim == 1) { return; }
1277
1278 int height = mat.Height();
1279 int width = mat.Width();
1280
1281 SparseMatrix *vmat = new SparseMatrix(vdim*height, vdim*width);
1282
1283 Array<int> dofs, vdofs;
1284 Vector srow;
1285 for (int i = 0; i < height; i++)
1286 {
1287 mat.GetRow(i, dofs, srow);
1288 for (int vd = 0; vd < vdim; vd++)
1289 {
1290 dofs.Copy(vdofs);
1291 DofsToVDofs(vd, vdofs, width);
1292 vmat->SetRow(DofToVDof(i, vd, height), vdofs, srow);
1293 }
1294 }
1295 vmat->Finalize();
1296
1297 mat.Swap(*vmat);
1298 delete vmat;
1299}
1300
1301
1303{
1304 if (Conforming()) { return NULL; }
1306 return cP.get();
1307}
1308
1310{
1311 if (Conforming()) { return NULL; }
1313 if (cR && !R_transpose) { R_transpose.reset(new TransposeOperator(*cR)); }
1314 return cR.get();
1315}
1316
1318{
1319 if (Conforming()) { return NULL; }
1321 return IsVariableOrder() ? cR_hp.get() : cR.get();
1322}
1323
1325{
1326 GetRestrictionOperator(); // Ensure that R_transpose is built
1327 return R_transpose.get();
1328}
1329
1331{
1333 return P ? (P->Width() / vdim) : ndofs;
1334}
1335
1337 ElementDofOrdering e_ordering) const
1338{
1339 // Check if we have a discontinuous space using the FE collection:
1340 if (IsDGSpace())
1341 {
1342 // TODO: when VDIM is 1, we can return IdentityOperator.
1343 if (L2E_nat.Ptr() == NULL)
1344 {
1345 // The input L-vector layout is:
1346 // * ND x NE x VDIM, for Ordering::byNODES, or
1347 // * VDIM x ND x NE, for Ordering::byVDIM.
1348 // The output E-vector layout is: ND x VDIM x NE.
1350 }
1352 }
1353 if (e_ordering == ElementDofOrdering::LEXICOGRAPHIC)
1354 {
1355 if (L2E_lex.Ptr() == NULL)
1356 {
1357 L2E_lex.Reset(new ElementRestriction(*this, e_ordering));
1358 }
1360 }
1361 // e_ordering == ElementDofOrdering::NATIVE
1362 if (L2E_nat.Ptr() == NULL)
1363 {
1364 L2E_nat.Reset(new ElementRestriction(*this, e_ordering));
1365 }
1367}
1368
1370 ElementDofOrdering f_ordering, FaceType type, L2FaceValues mul) const
1371{
1372 const bool is_dg_space = IsDGSpace();
1373 const L2FaceValues m = (is_dg_space && mul==L2FaceValues::DoubleValued) ?
1375 key_face key = std::make_tuple(is_dg_space, f_ordering, type, m);
1376 auto itr = L2F.find(key);
1377 if (itr != L2F.end())
1378 {
1379 return itr->second;
1380 }
1381 else
1382 {
1383 FaceRestriction *res;
1384 if (is_dg_space)
1385 {
1386 if (Conforming())
1387 {
1388 res = new L2FaceRestriction(*this, f_ordering, type, m);
1389 }
1390 else
1391 {
1392 res = new NCL2FaceRestriction(*this, f_ordering, type, m);
1393 }
1394 }
1395 else
1396 {
1397 res = new ConformingFaceRestriction(*this, f_ordering, type);
1398 }
1399 L2F[key] = res;
1400 return res;
1401 }
1402}
1403
1405 const IntegrationRule &ir) const
1406{
1407 for (int i = 0; i < E2Q_array.Size(); i++)
1408 {
1409 const QuadratureInterpolator *qi = E2Q_array[i];
1410 if (qi->IntRule == &ir) { return qi; }
1411 }
1412
1414 E2Q_array.Append(qi);
1415 return qi;
1416}
1417
1419 const QuadratureSpace &qs) const
1420{
1421 for (int i = 0; i < E2Q_array.Size(); i++)
1422 {
1423 const QuadratureInterpolator *qi = E2Q_array[i];
1424 if (qi->qspace == &qs) { return qi; }
1425 }
1426
1428 E2Q_array.Append(qi);
1429 return qi;
1430}
1431
1434 const IntegrationRule &ir, FaceType type) const
1435{
1436 if (type==FaceType::Interior)
1437 {
1438 for (int i = 0; i < E2IFQ_array.Size(); i++)
1439 {
1441 if (qi->IntRule == &ir) { return qi; }
1442 }
1443
1445 type);
1446 E2IFQ_array.Append(qi);
1447 return qi;
1448 }
1449 else //Boundary
1450 {
1451 for (int i = 0; i < E2BFQ_array.Size(); i++)
1452 {
1454 if (qi->IntRule == &ir) { return qi; }
1455 }
1456
1458 type);
1459 E2BFQ_array.Append(qi);
1460 return qi;
1461 }
1462}
1463
1465 const int coarse_ndofs, const Table &coarse_elem_dof,
1466 const Table *coarse_elem_fos, const DenseTensor localP[]) const
1467{
1468 /// TODO: Implement DofTransformation support
1469
1470 MFEM_VERIFY(mesh->GetLastOperation() == Mesh::REFINE, "");
1471
1472 Array<int> dofs, coarse_dofs, coarse_vdofs;
1473 Vector row;
1474
1475 Mesh::GeometryList elem_geoms(*mesh);
1476
1477 SparseMatrix *P;
1478 if (elem_geoms.Size() == 1)
1479 {
1480 const int coarse_ldof = localP[elem_geoms[0]].SizeJ();
1481 P = new SparseMatrix(GetVSize(), coarse_ndofs*vdim, coarse_ldof);
1482 }
1483 else
1484 {
1485 P = new SparseMatrix(GetVSize(), coarse_ndofs*vdim);
1486 }
1487
1488 Array<int> mark(P->Height());
1489 mark = 0;
1490
1492
1493 for (int k = 0; k < mesh->GetNE(); k++)
1494 {
1495 const Embedding &emb = rtrans.embeddings[k];
1497 const DenseMatrix &lP = localP[geom](emb.matrix);
1498 const int fine_ldof = localP[geom].SizeI();
1499
1500 elem_dof->GetRow(k, dofs);
1501 coarse_elem_dof.GetRow(emb.parent, coarse_dofs);
1502
1503 for (int vd = 0; vd < vdim; vd++)
1504 {
1505 coarse_dofs.Copy(coarse_vdofs);
1506 DofsToVDofs(vd, coarse_vdofs, coarse_ndofs);
1507
1508 for (int i = 0; i < fine_ldof; i++)
1509 {
1510 int r = DofToVDof(dofs[i], vd);
1511 int m = (r >= 0) ? r : (-1 - r);
1512
1513 if (!mark[m])
1514 {
1515 lP.GetRow(i, row);
1516 P->SetRow(r, coarse_vdofs, row);
1517 mark[m] = 1;
1518 }
1519 }
1520 }
1521 }
1522
1523 MFEM_ASSERT(mark.Sum() == P->Height(), "Not all rows of P set.");
1524 if (elem_geoms.Size() != 1) { P->Finalize(); }
1525 return P;
1526}
1527
1529 Geometry::Type geom, DenseTensor &localP) const
1530{
1531 const FiniteElement *fe = fec->FiniteElementForGeometry(geom);
1532
1534 const DenseTensor &pmats = rtrans.point_matrices[geom];
1535
1536 int nmat = pmats.SizeK();
1537 int ldof = fe->GetDof();
1538
1540 isotr.SetIdentityTransformation(geom);
1541
1542 // calculate local interpolation matrices for all refinement types
1543 localP.SetSize(ldof, ldof, nmat);
1544 for (int i = 0; i < nmat; i++)
1545 {
1546 isotr.SetPointMat(pmats(i));
1547 fe->GetLocalInterpolation(isotr, localP(i));
1548 }
1549}
1550
1552 const Table* old_elem_dof,
1553 const Table* old_elem_fos)
1554{
1555 MFEM_VERIFY(GetNE() >= old_elem_dof->Size(),
1556 "Previous mesh is not coarser.");
1557
1558 Mesh::GeometryList elem_geoms(*mesh);
1559
1561 for (int i = 0; i < elem_geoms.Size(); i++)
1562 {
1563 GetLocalRefinementMatrices(elem_geoms[i], localP[elem_geoms[i]]);
1564 }
1565
1566 return RefinementMatrix_main(old_ndofs, *old_elem_dof, old_elem_fos,
1567 localP);
1568}
1569
1571 const FiniteElementSpace* fespace, Table* old_elem_dof, Table* old_elem_fos,
1572 int old_ndofs)
1573 : fespace(fespace),
1574 old_elem_dof(old_elem_dof),
1575 old_elem_fos(old_elem_fos)
1576{
1577 MFEM_VERIFY(fespace->GetNE() >= old_elem_dof->Size(),
1578 "Previous mesh is not coarser.");
1579
1580 width = old_ndofs * fespace->GetVDim();
1581 height = fespace->GetVSize();
1582
1583 Mesh::GeometryList elem_geoms(*fespace->GetMesh());
1584
1585 for (int i = 0; i < elem_geoms.Size(); i++)
1586 {
1587 fespace->GetLocalRefinementMatrices(elem_geoms[i], localP[elem_geoms[i]]);
1588 }
1589
1590 ConstructDoFTransArray();
1591}
1592
1594 const FiniteElementSpace *fespace, const FiniteElementSpace *coarse_fes)
1595 : Operator(fespace->GetVSize(), coarse_fes->GetVSize()),
1596 fespace(fespace), old_elem_dof(NULL), old_elem_fos(NULL)
1597{
1598 Mesh::GeometryList elem_geoms(*fespace->GetMesh());
1599
1600 for (int i = 0; i < elem_geoms.Size(); i++)
1601 {
1602 fespace->GetLocalRefinementMatrices(*coarse_fes, elem_geoms[i],
1603 localP[elem_geoms[i]]);
1604 }
1605
1606 // Make a copy of the coarse elem_dof Table.
1607 old_elem_dof = new Table(coarse_fes->GetElementToDofTable());
1608
1609 // Make a copy of the coarse elem_fos Table if it exists.
1610 if (coarse_fes->GetElementToFaceOrientationTable())
1611 {
1612 old_elem_fos = new Table(*coarse_fes->GetElementToFaceOrientationTable());
1613 }
1614
1615 ConstructDoFTransArray();
1616}
1617
1619{
1620 delete old_elem_dof;
1621 delete old_elem_fos;
1622 for (int i=0; i<old_DoFTransArray.Size(); i++)
1623 {
1624 delete old_DoFTransArray[i];
1625 }
1626}
1627
1628void FiniteElementSpace::RefinementOperator::ConstructDoFTransArray()
1629{
1630 old_DoFTransArray.SetSize(Geometry::NUM_GEOMETRIES);
1631 for (int i=0; i<old_DoFTransArray.Size(); i++)
1632 {
1633 old_DoFTransArray[i] = NULL;
1634 }
1635
1636 const FiniteElementCollection *fec_ref = fespace->FEColl();
1637 if (dynamic_cast<const ND_FECollection*>(fec_ref))
1638 {
1639 const FiniteElement *nd_tri =
1641 if (nd_tri)
1642 {
1643 old_DoFTransArray[Geometry::TRIANGLE] =
1644 new ND_TriDofTransformation(nd_tri->GetOrder());
1645 }
1646
1647 const FiniteElement *nd_tet =
1649 if (nd_tet)
1650 {
1651 old_DoFTransArray[Geometry::TETRAHEDRON] =
1652 new ND_TetDofTransformation(nd_tet->GetOrder());
1653 }
1654
1655 const FiniteElement *nd_pri =
1657 if (nd_pri)
1658 {
1659 old_DoFTransArray[Geometry::PRISM] =
1660 new ND_WedgeDofTransformation(nd_pri->GetOrder());
1661 }
1662 }
1663}
1664
1666 Vector &y) const
1667{
1668 Mesh* mesh_ref = fespace->GetMesh();
1669 const CoarseFineTransformations &trans_ref =
1670 mesh_ref->GetRefinementTransforms();
1671
1672 Array<int> dofs, vdofs, old_dofs, old_vdofs, old_Fo;
1673
1674 int rvdim = fespace->GetVDim();
1675 int old_ndofs = width / rvdim;
1676
1677 Vector subY, subX;
1678
1679 for (int k = 0; k < mesh_ref->GetNE(); k++)
1680 {
1681 const Embedding &emb = trans_ref.embeddings[k];
1682 const Geometry::Type geom = mesh_ref->GetElementBaseGeometry(k);
1683 const DenseMatrix &lP = localP[geom](emb.matrix);
1684
1685 subY.SetSize(lP.Height());
1686
1687 DofTransformation *doftrans = fespace->GetElementDofs(k, dofs);
1688 old_elem_dof->GetRow(emb.parent, old_dofs);
1689
1690 if (!doftrans)
1691 {
1692 for (int vd = 0; vd < rvdim; vd++)
1693 {
1694 dofs.Copy(vdofs);
1695 fespace->DofsToVDofs(vd, vdofs);
1696 old_dofs.Copy(old_vdofs);
1697 fespace->DofsToVDofs(vd, old_vdofs, old_ndofs);
1698
1699 x.GetSubVector(old_vdofs, subX);
1700 lP.Mult(subX, subY);
1701 y.SetSubVector(vdofs, subY);
1702 }
1703 }
1704 else
1705 {
1706 old_elem_fos->GetRow(emb.parent, old_Fo);
1707 old_DoFTrans.SetDofTransformation(*old_DoFTransArray[geom]);
1708 old_DoFTrans.SetFaceOrientations(old_Fo);
1709
1710 doftrans->SetVDim();
1711 for (int vd = 0; vd < rvdim; vd++)
1712 {
1713 dofs.Copy(vdofs);
1714 fespace->DofsToVDofs(vd, vdofs);
1715 old_dofs.Copy(old_vdofs);
1716 fespace->DofsToVDofs(vd, old_vdofs, old_ndofs);
1717
1718 x.GetSubVector(old_vdofs, subX);
1719 old_DoFTrans.InvTransformPrimal(subX);
1720 lP.Mult(subX, subY);
1721 doftrans->TransformPrimal(subY);
1722 y.SetSubVector(vdofs, subY);
1723 }
1724 doftrans->SetVDim(rvdim, fespace->GetOrdering());
1725 }
1726 }
1727}
1728
1730 Vector &y) const
1731{
1732 y = 0.0;
1733
1734 Mesh* mesh_ref = fespace->GetMesh();
1735 const CoarseFineTransformations &trans_ref =
1736 mesh_ref->GetRefinementTransforms();
1737
1738 Array<char> processed(fespace->GetVSize());
1739 processed = 0;
1740
1741 Array<int> f_dofs, c_dofs, f_vdofs, c_vdofs, old_Fo;
1742
1743 int rvdim = fespace->GetVDim();
1744 int old_ndofs = width / rvdim;
1745
1746 Vector subY, subX, subYt;
1747
1748 for (int k = 0; k < mesh_ref->GetNE(); k++)
1749 {
1750 const Embedding &emb = trans_ref.embeddings[k];
1751 const Geometry::Type geom = mesh_ref->GetElementBaseGeometry(k);
1752 const DenseMatrix &lP = localP[geom](emb.matrix);
1753
1754 DofTransformation *doftrans = fespace->GetElementDofs(k, f_dofs);
1755 old_elem_dof->GetRow(emb.parent, c_dofs);
1756
1757 if (!doftrans)
1758 {
1759 subY.SetSize(lP.Width());
1760
1761 for (int vd = 0; vd < rvdim; vd++)
1762 {
1763 f_dofs.Copy(f_vdofs);
1764 fespace->DofsToVDofs(vd, f_vdofs);
1765 c_dofs.Copy(c_vdofs);
1766 fespace->DofsToVDofs(vd, c_vdofs, old_ndofs);
1767
1768 x.GetSubVector(f_vdofs, subX);
1769 for (int p = 0; p < f_dofs.Size(); ++p)
1770 {
1771 if (processed[DecodeDof(f_dofs[p])])
1772 {
1773 subX[p] = 0.0;
1774 }
1775 }
1776 lP.MultTranspose(subX, subY);
1777 y.AddElementVector(c_vdofs, subY);
1778 }
1779 }
1780 else
1781 {
1782 subYt.SetSize(lP.Width());
1783
1784 old_elem_fos->GetRow(emb.parent, old_Fo);
1785 old_DoFTrans.SetDofTransformation(*old_DoFTransArray[geom]);
1786 old_DoFTrans.SetFaceOrientations(old_Fo);
1787
1788 doftrans->SetVDim();
1789 for (int vd = 0; vd < rvdim; vd++)
1790 {
1791 f_dofs.Copy(f_vdofs);
1792 fespace->DofsToVDofs(vd, f_vdofs);
1793 c_dofs.Copy(c_vdofs);
1794 fespace->DofsToVDofs(vd, c_vdofs, old_ndofs);
1795
1796 x.GetSubVector(f_vdofs, subX);
1797 doftrans->InvTransformDual(subX);
1798 for (int p = 0; p < f_dofs.Size(); ++p)
1799 {
1800 if (processed[DecodeDof(f_dofs[p])])
1801 {
1802 subX[p] = 0.0;
1803 }
1804 }
1805 lP.MultTranspose(subX, subYt);
1806 old_DoFTrans.TransformDual(subYt);
1807 y.AddElementVector(c_vdofs, subYt);
1808 }
1809 doftrans->SetVDim(rvdim, fespace->GetOrdering());
1810 }
1811
1812 for (int p = 0; p < f_dofs.Size(); ++p)
1813 {
1814 processed[DecodeDof(f_dofs[p])] = 1;
1815 }
1816 }
1817}
1818
1819namespace internal
1820{
1821
1822// Used in GetCoarseToFineMap() below.
1823struct RefType
1824{
1825 Geometry::Type geom;
1826 int num_children;
1827 const Pair<int,int> *children;
1828
1829 RefType(Geometry::Type g, int n, const Pair<int,int> *c)
1830 : geom(g), num_children(n), children(c) { }
1831
1832 bool operator<(const RefType &other) const
1833 {
1834 if (geom < other.geom) { return true; }
1835 if (geom > other.geom) { return false; }
1836 if (num_children < other.num_children) { return true; }
1837 if (num_children > other.num_children) { return false; }
1838 for (int i = 0; i < num_children; i++)
1839 {
1840 if (children[i].one < other.children[i].one) { return true; }
1841 if (children[i].one > other.children[i].one) { return false; }
1842 }
1843 return false; // everything is equal
1844 }
1845};
1846
1847void GetCoarseToFineMap(const CoarseFineTransformations &cft,
1848 const mfem::Mesh &fine_mesh,
1849 Table &coarse_to_fine,
1850 Array<int> &coarse_to_ref_type,
1851 Table &ref_type_to_matrix,
1852 Array<Geometry::Type> &ref_type_to_geom)
1853{
1854 const int fine_ne = cft.embeddings.Size();
1855 int coarse_ne = -1;
1856 for (int i = 0; i < fine_ne; i++)
1857 {
1858 coarse_ne = std::max(coarse_ne, cft.embeddings[i].parent);
1859 }
1860 coarse_ne++;
1861
1862 coarse_to_ref_type.SetSize(coarse_ne);
1863 coarse_to_fine.SetDims(coarse_ne, fine_ne);
1864
1865 Array<int> cf_i(coarse_to_fine.GetI(), coarse_ne+1);
1866 Array<Pair<int,int> > cf_j(fine_ne);
1867 cf_i = 0;
1868 for (int i = 0; i < fine_ne; i++)
1869 {
1870 cf_i[cft.embeddings[i].parent+1]++;
1871 }
1872 cf_i.PartialSum();
1873 MFEM_ASSERT(cf_i.Last() == cf_j.Size(), "internal error");
1874 for (int i = 0; i < fine_ne; i++)
1875 {
1876 const Embedding &e = cft.embeddings[i];
1877 cf_j[cf_i[e.parent]].one = e.matrix; // used as sort key below
1878 cf_j[cf_i[e.parent]].two = i;
1879 cf_i[e.parent]++;
1880 }
1881 std::copy_backward(cf_i.begin(), cf_i.end()-1, cf_i.end());
1882 cf_i[0] = 0;
1883 for (int i = 0; i < coarse_ne; i++)
1884 {
1885 std::sort(&cf_j[cf_i[i]], cf_j.GetData() + cf_i[i+1]);
1886 }
1887 for (int i = 0; i < fine_ne; i++)
1888 {
1889 coarse_to_fine.GetJ()[i] = cf_j[i].two;
1890 }
1891
1892 using std::map;
1893 using std::pair;
1894
1895 map<RefType,int> ref_type_map;
1896 for (int i = 0; i < coarse_ne; i++)
1897 {
1898 const int num_children = cf_i[i+1]-cf_i[i];
1899 MFEM_ASSERT(num_children > 0, "");
1900 const int fine_el = cf_j[cf_i[i]].two;
1901 // Assuming the coarse and the fine elements have the same geometry:
1902 const Geometry::Type geom = fine_mesh.GetElementBaseGeometry(fine_el);
1903 const RefType ref_type(geom, num_children, &cf_j[cf_i[i]]);
1904 pair<map<RefType,int>::iterator,bool> res =
1905 ref_type_map.insert(
1906 pair<const RefType,int>(ref_type, (int)ref_type_map.size()));
1907 coarse_to_ref_type[i] = res.first->second;
1908 }
1909
1910 ref_type_to_matrix.MakeI((int)ref_type_map.size());
1911 ref_type_to_geom.SetSize((int)ref_type_map.size());
1912 for (map<RefType,int>::iterator it = ref_type_map.begin();
1913 it != ref_type_map.end(); ++it)
1914 {
1915 ref_type_to_matrix.AddColumnsInRow(it->second, it->first.num_children);
1916 ref_type_to_geom[it->second] = it->first.geom;
1917 }
1918
1919 ref_type_to_matrix.MakeJ();
1920 for (map<RefType,int>::iterator it = ref_type_map.begin();
1921 it != ref_type_map.end(); ++it)
1922 {
1923 const RefType &rt = it->first;
1924 for (int j = 0; j < rt.num_children; j++)
1925 {
1926 ref_type_to_matrix.AddConnection(it->second, rt.children[j].one);
1927 }
1928 }
1929 ref_type_to_matrix.ShiftUpI();
1930}
1931
1932} // namespace internal
1933
1934
1935/// TODO: Implement DofTransformation support
1937 const FiniteElementSpace *f_fes, const FiniteElementSpace *c_fes,
1938 BilinearFormIntegrator *mass_integ)
1939 : Operator(c_fes->GetVSize(), f_fes->GetVSize()),
1940 fine_fes(f_fes)
1941{
1942 MFEM_VERIFY(c_fes->GetOrdering() == f_fes->GetOrdering() &&
1943 c_fes->GetVDim() == f_fes->GetVDim(),
1944 "incompatible coarse and fine FE spaces");
1945
1947 Mesh *f_mesh = f_fes->GetMesh();
1948 const CoarseFineTransformations &rtrans = f_mesh->GetRefinementTransforms();
1949
1950 Mesh::GeometryList elem_geoms(*f_mesh);
1952 for (int gi = 0; gi < elem_geoms.Size(); gi++)
1953 {
1954 const Geometry::Type geom = elem_geoms[gi];
1955 DenseTensor &lP = localP[geom], &lM = localM[geom];
1956 const FiniteElement *fine_fe =
1957 f_fes->fec->FiniteElementForGeometry(geom);
1958 const FiniteElement *coarse_fe =
1959 c_fes->fec->FiniteElementForGeometry(geom);
1960 const DenseTensor &pmats = rtrans.point_matrices[geom];
1961
1962 lP.SetSize(fine_fe->GetDof(), coarse_fe->GetDof(), pmats.SizeK());
1963 lM.SetSize(fine_fe->GetDof(), fine_fe->GetDof(), pmats.SizeK());
1964 emb_tr.SetIdentityTransformation(geom);
1965 for (int i = 0; i < pmats.SizeK(); i++)
1966 {
1967 emb_tr.SetPointMat(pmats(i));
1968 // Get the local interpolation matrix for this refinement type
1969 fine_fe->GetTransferMatrix(*coarse_fe, emb_tr, lP(i));
1970 // Get the local mass matrix for this refinement type
1971 mass_integ->AssembleElementMatrix(*fine_fe, emb_tr, lM(i));
1972 }
1973 }
1974
1975 Table ref_type_to_matrix;
1976 internal::GetCoarseToFineMap(rtrans, *f_mesh, coarse_to_fine,
1977 coarse_to_ref_type, ref_type_to_matrix,
1978 ref_type_to_geom);
1979 MFEM_ASSERT(coarse_to_fine.Size() == c_fes->GetNE(), "");
1980
1981 const int total_ref_types = ref_type_to_geom.Size();
1982 int num_ref_types[Geometry::NumGeom], num_fine_elems[Geometry::NumGeom];
1983 Array<int> ref_type_to_coarse_elem_offset(total_ref_types);
1984 ref_type_to_fine_elem_offset.SetSize(total_ref_types);
1985 std::fill(num_ref_types, num_ref_types+Geometry::NumGeom, 0);
1986 std::fill(num_fine_elems, num_fine_elems+Geometry::NumGeom, 0);
1987 for (int i = 0; i < total_ref_types; i++)
1988 {
1989 Geometry::Type g = ref_type_to_geom[i];
1990 ref_type_to_coarse_elem_offset[i] = num_ref_types[g];
1991 ref_type_to_fine_elem_offset[i] = num_fine_elems[g];
1992 num_ref_types[g]++;
1993 num_fine_elems[g] += ref_type_to_matrix.RowSize(i);
1994 }
1995 DenseTensor localPtMP[Geometry::NumGeom];
1996 for (int g = 0; g < Geometry::NumGeom; g++)
1997 {
1998 if (num_ref_types[g] == 0) { continue; }
1999 const int fine_dofs = localP[g].SizeI();
2000 const int coarse_dofs = localP[g].SizeJ();
2001 localPtMP[g].SetSize(coarse_dofs, coarse_dofs, num_ref_types[g]);
2002 localR[g].SetSize(coarse_dofs, fine_dofs, num_fine_elems[g]);
2003 }
2004 for (int i = 0; i < total_ref_types; i++)
2005 {
2006 Geometry::Type g = ref_type_to_geom[i];
2007 DenseMatrix &lPtMP = localPtMP[g](ref_type_to_coarse_elem_offset[i]);
2008 int lR_offset = ref_type_to_fine_elem_offset[i]; // offset in localR[g]
2009 const int *mi = ref_type_to_matrix.GetRow(i);
2010 const int nm = ref_type_to_matrix.RowSize(i);
2011 lPtMP = 0.0;
2012 for (int s = 0; s < nm; s++)
2013 {
2014 DenseMatrix &lP = localP[g](mi[s]);
2015 DenseMatrix &lM = localM[g](mi[s]);
2016 DenseMatrix &lR = localR[g](lR_offset+s);
2017 MultAtB(lP, lM, lR); // lR = lP^T lM
2018 mfem::AddMult(lR, lP, lPtMP); // lPtMP += lP^T lM lP
2019 }
2020 DenseMatrixInverse lPtMP_inv(lPtMP);
2021 for (int s = 0; s < nm; s++)
2022 {
2023 DenseMatrix &lR = localR[g](lR_offset+s);
2024 lPtMP_inv.Mult(lR); // lR <- (P^T M P)^{-1} P^T M
2025 }
2026 }
2027
2028 // Make a copy of the coarse element-to-dof Table.
2029 coarse_elem_dof = new Table(c_fes->GetElementToDofTable());
2030}
2031
2036
2038 Vector &y) const
2039{
2040 Array<int> c_vdofs, f_vdofs;
2041 Vector loc_x, loc_y;
2042 DenseMatrix loc_x_mat, loc_y_mat;
2043 const int fine_vdim = fine_fes->GetVDim();
2044 const int coarse_ndofs = height/fine_vdim;
2045 for (int coarse_el = 0; coarse_el < coarse_to_fine.Size(); coarse_el++)
2046 {
2047 coarse_elem_dof->GetRow(coarse_el, c_vdofs);
2048 fine_fes->DofsToVDofs(c_vdofs, coarse_ndofs);
2049 loc_y.SetSize(c_vdofs.Size());
2050 loc_y = 0.0;
2051 loc_y_mat.UseExternalData(loc_y.GetData(), c_vdofs.Size()/fine_vdim,
2052 fine_vdim);
2053 const int ref_type = coarse_to_ref_type[coarse_el];
2054 const Geometry::Type geom = ref_type_to_geom[ref_type];
2055 const int *fine_elems = coarse_to_fine.GetRow(coarse_el);
2056 const int num_fine_elems = coarse_to_fine.RowSize(coarse_el);
2057 const int lR_offset = ref_type_to_fine_elem_offset[ref_type];
2058 for (int s = 0; s < num_fine_elems; s++)
2059 {
2060 const DenseMatrix &lR = localR[geom](lR_offset+s);
2061 fine_fes->GetElementVDofs(fine_elems[s], f_vdofs);
2062 x.GetSubVector(f_vdofs, loc_x);
2063 loc_x_mat.UseExternalData(loc_x.GetData(), f_vdofs.Size()/fine_vdim,
2064 fine_vdim);
2065 mfem::AddMult(lR, loc_x_mat, loc_y_mat);
2066 }
2067 y.SetSubVector(c_vdofs, loc_y);
2068 }
2069}
2070
2072 DenseTensor &localR) const
2073{
2074 const FiniteElement *fe = fec->FiniteElementForGeometry(geom);
2075
2076 const CoarseFineTransformations &dtrans =
2078 const DenseTensor &pmats = dtrans.point_matrices[geom];
2079
2080 const int nmat = pmats.SizeK();
2081 const int ldof = fe->GetDof();
2082
2084 isotr.SetIdentityTransformation(geom);
2085
2086 // calculate local restriction matrices for all refinement types
2087 localR.SetSize(ldof, ldof, nmat);
2088 for (int i = 0; i < nmat; i++)
2089 {
2090 isotr.SetPointMat(pmats(i));
2091 fe->GetLocalRestriction(isotr, localR(i));
2092 }
2093}
2094
2096 const Table* old_elem_dof,
2097 const Table* old_elem_fos)
2098{
2099 /// TODO: Implement DofTransformation support
2100
2101 MFEM_VERIFY(Nonconforming(), "Not implemented for conforming meshes.");
2102 MFEM_VERIFY(old_ndofs, "Missing previous (finer) space.");
2103 MFEM_VERIFY(ndofs <= old_ndofs, "Previous space is not finer.");
2104
2105 Array<int> dofs, old_dofs, old_vdofs;
2106 Vector row;
2107
2108 Mesh::GeometryList elem_geoms(*mesh);
2109
2111 for (int i = 0; i < elem_geoms.Size(); i++)
2112 {
2113 GetLocalDerefinementMatrices(elem_geoms[i], localR[elem_geoms[i]]);
2114 }
2115
2116 SparseMatrix *R = new SparseMatrix(ndofs*vdim, old_ndofs*vdim);
2117
2118 Array<int> mark(R->Height());
2119 mark = 0;
2120
2121 const CoarseFineTransformations &dtrans =
2123
2124 MFEM_ASSERT(dtrans.embeddings.Size() == old_elem_dof->Size(), "");
2125
2127 int num_marked = 0;
2128 for (int k = 0; k < dtrans.embeddings.Size(); k++)
2129 {
2130 const Embedding &emb = dtrans.embeddings[k];
2132 DenseMatrix &lR = localR[geom](emb.matrix);
2133
2134 elem_dof->GetRow(emb.parent, dofs);
2135 old_elem_dof->GetRow(k, old_dofs);
2136
2137 for (int vd = 0; vd < vdim; vd++)
2138 {
2139 old_dofs.Copy(old_vdofs);
2140 DofsToVDofs(vd, old_vdofs, old_ndofs);
2141
2142 for (int i = 0; i < lR.Height(); i++)
2143 {
2144 if (!std::isfinite(lR(i, 0))) { continue; }
2145
2146 int r = DofToVDof(dofs[i], vd);
2147 int m = (r >= 0) ? r : (-1 - r);
2148
2149 if (is_dg || !mark[m])
2150 {
2151 lR.GetRow(i, row);
2152 R->SetRow(r, old_vdofs, row);
2153
2154 mark[m] = 1;
2155 num_marked++;
2156 }
2157 }
2158 }
2159 }
2160
2161 if (!is_dg)
2162 {
2163 MFEM_VERIFY(num_marked == R->Height(),
2164 "internal error: not all rows of R were set.");
2165 }
2166
2167 R->Finalize(); // no-op if fixed width
2168 return R;
2169}
2170
2172 const FiniteElementSpace &coarse_fes, Geometry::Type geom,
2173 DenseTensor &localP) const
2174{
2175 // Assumptions: see the declaration of the method.
2176
2177 const FiniteElement *fine_fe = fec->FiniteElementForGeometry(geom);
2178 const FiniteElement *coarse_fe =
2179 coarse_fes.fec->FiniteElementForGeometry(geom);
2180
2182 const DenseTensor &pmats = rtrans.point_matrices[geom];
2183
2184 int nmat = pmats.SizeK();
2185
2187 isotr.SetIdentityTransformation(geom);
2188
2189 // Calculate the local interpolation matrices for all refinement types
2190 localP.SetSize(fine_fe->GetDof(), coarse_fe->GetDof(), nmat);
2191 for (int i = 0; i < nmat; i++)
2192 {
2193 isotr.SetPointMat(pmats(i));
2194 fine_fe->GetTransferMatrix(*coarse_fe, isotr, localP(i));
2195 }
2196}
2197
2199 const FiniteElementCollection *fec_,
2200 int vdim_, int ordering_)
2201{
2202 mesh = mesh_;
2203 fec = fec_;
2204 vdim = vdim_;
2205 ordering = (Ordering::Type) ordering_;
2206
2207 elem_dof = NULL;
2208 elem_fos = NULL;
2209 face_dof = NULL;
2210
2211 sequence = 0;
2212 orders_changed = false;
2213 relaxed_hp = false;
2214
2216
2217 const NURBSFECollection *nurbs_fec =
2218 dynamic_cast<const NURBSFECollection *>(fec_);
2219 if (nurbs_fec)
2220 {
2221 MFEM_VERIFY(mesh_->NURBSext, "NURBS FE space requires a NURBS mesh.");
2222
2223 if (NURBSext_ == NULL)
2224 {
2225 NURBSext = mesh_->NURBSext;
2226 own_ext = 0;
2227 }
2228 else
2229 {
2230 NURBSext = NURBSext_;
2231 own_ext = 1;
2232 }
2233 UpdateNURBS();
2234 cP.reset();
2235 cR.reset();
2236 cR_hp.reset();
2237 R_transpose.reset();
2238 cP_is_set = false;
2239
2241 }
2242 else
2243 {
2244 NURBSext = NULL;
2245 own_ext = 0;
2246 Construct();
2247 }
2248
2250}
2251
2253{
2255
2257 for (int i=0; i<DoFTransArray.Size(); i++)
2258 {
2259 DoFTransArray[i] = NULL;
2260 }
2261 if (mesh->Dimension() < 3) { return; }
2262 if (dynamic_cast<const ND_FECollection*>(fec))
2263 {
2264 const FiniteElement *nd_tri =
2266 if (nd_tri)
2267 {
2269 new ND_TriDofTransformation(nd_tri->GetOrder());
2270 }
2271
2272 const FiniteElement *nd_tet =
2274 if (nd_tet)
2275 {
2277 new ND_TetDofTransformation(nd_tet->GetOrder());
2278 }
2279
2280 const FiniteElement *nd_pri =
2282 if (nd_pri)
2283 {
2285 new ND_WedgeDofTransformation(nd_pri->GetOrder());
2286 }
2287 }
2288}
2289
2291{
2292 if (NURBSext && !own_ext)
2293 {
2294 mfem_error("FiniteElementSpace::StealNURBSext");
2295 }
2296 own_ext = 0;
2297
2298 return NURBSext;
2299}
2300
2302{
2303 MFEM_VERIFY(NURBSext, "NURBSExt not defined.");
2304
2305 nvdofs = 0;
2306 nedofs = 0;
2307 nfdofs = 0;
2308 nbdofs = 0;
2309 bdofs = NULL;
2310
2311 delete face_dof;
2312 face_dof = NULL;
2314
2315 dynamic_cast<const NURBSFECollection *>(fec)->Reset();
2316
2317 ndofs = NURBSext->GetNDof();
2320
2322 sequence++;
2323}
2324
2326{
2327 if (face_dof) { return; }
2328
2329 const int dim = mesh->Dimension();
2330
2331 // Find bdr to face mapping
2333 face_to_be = -1;
2334 for (int b = 0; b < GetNBE(); b++)
2335 {
2337 face_to_be[f] = b;
2338 }
2339
2340 // Loop over faces in correct order, to prevent a sort
2341 // Sort will destroy orientation info in ordering of dofs
2342 Array<Connection> face_dof_list;
2343 Array<int> row;
2344 for (int f = 0; f < GetNF(); f++)
2345 {
2346 int b = face_to_be[f];
2347 if (b == -1) { continue; }
2348 // FIXME: this assumes that the boundary element and the face element have
2349 // the same orientation.
2350 if (dim > 1)
2351 {
2352 const Element *fe = mesh->GetFace(f);
2353 const Element *be = mesh->GetBdrElement(b);
2354 const int nv = be->GetNVertices();
2355 const int *fv = fe->GetVertices();
2356 const int *bv = be->GetVertices();
2357 for (int i = 0; i < nv; i++)
2358 {
2359 MFEM_VERIFY(fv[i] == bv[i],
2360 "non-matching face and boundary elements detected!");
2361 }
2362 }
2363 GetBdrElementDofs(b, row);
2364 Connection conn(f,0);
2365 for (int i = 0; i < row.Size(); i++)
2366 {
2367 conn.to = row[i];
2368 face_dof_list.Append(conn);
2369 }
2370 }
2371 face_dof = new Table(GetNF(), face_dof_list);
2372}
2373
2375{
2376 // This method should be used only for non-NURBS spaces.
2377 MFEM_VERIFY(!NURBSext, "internal error");
2378
2379 // Variable order space needs a nontrivial P matrix + also ghost elements
2380 // in parallel, we thus require the mesh to be NC.
2381 MFEM_VERIFY(!IsVariableOrder() || Nonconforming(),
2382 "Variable order space requires a nonconforming mesh.");
2383
2384 elem_dof = NULL;
2385 elem_fos = NULL;
2386 bdr_elem_dof = NULL;
2387 bdr_elem_fos = NULL;
2388 face_dof = NULL;
2389
2390 ndofs = 0;
2391 nvdofs = nedofs = nfdofs = nbdofs = 0;
2392 bdofs = NULL;
2393
2394 cP = NULL;
2395 cR = NULL;
2396 cR_hp = NULL;
2397 cP_is_set = false;
2398 R_transpose = NULL;
2399 // 'Th' is initialized/destroyed before this method is called.
2400
2401 int dim = mesh->Dimension();
2402 int order = fec->GetOrder();
2403
2404 MFEM_VERIFY((mesh->GetNumGeometries(dim) > 0) || (mesh->GetNE() == 0),
2405 "Mesh was not correctly finalized.");
2406
2407 bool mixed_elements = (mesh->GetNumGeometries(dim) > 1);
2408 bool mixed_faces = (dim > 2 && mesh->GetNumGeometries(2) > 1);
2409
2410 Array<VarOrderBits> edge_orders, face_orders;
2411 if (IsVariableOrder())
2412 {
2413 // for variable order spaces, calculate orders of edges and faces
2414 CalcEdgeFaceVarOrders(edge_orders, face_orders);
2415 }
2416 else if (mixed_faces)
2417 {
2418 // for mixed faces we also create the var_face_dofs table, see below
2419 face_orders.SetSize(mesh->GetNFaces());
2420 face_orders = (VarOrderBits(1) << order);
2421 }
2422
2423 // assign vertex DOFs
2424 if (mesh->GetNV())
2425 {
2426 nvdofs = mesh->GetNV() * fec->GetNumDof(Geometry::POINT, order);
2427 }
2428
2429 // assign edge DOFs
2430 if (mesh->GetNEdges())
2431 {
2432 if (IsVariableOrder())
2433 {
2434 nedofs = MakeDofTable(1, edge_orders, var_edge_dofs, &var_edge_orders);
2435 }
2436 else
2437 {
2438 // the simple case: all edges are of the same order
2440 var_edge_dofs.Clear(); // ensure any old var_edge_dof table is dumped.
2441 }
2442 }
2443
2444 // assign face DOFs
2445 if (mesh->GetNFaces())
2446 {
2447 if (IsVariableOrder() || mixed_faces)
2448 {
2449 // NOTE: for simplicity, we also use Table var_face_dofs for mixed faces
2450 nfdofs = MakeDofTable(2, face_orders, var_face_dofs,
2451 IsVariableOrder() ? &var_face_orders : NULL);
2452 uni_fdof = -1;
2453 }
2454 else
2455 {
2456 // the simple case: all faces are of the same geometry and order
2457 uni_fdof = fec->GetNumDof(mesh->GetFaceGeometry(0), order);
2459 var_face_dofs.Clear(); // ensure any old var_face_dof table is dumped.
2460 }
2461 }
2462
2463 // assign internal ("bubble") DOFs
2464 if (mesh->GetNE() && dim > 0)
2465 {
2466 if (IsVariableOrder() || mixed_elements)
2467 {
2468 bdofs = new int[mesh->GetNE()+1];
2469 bdofs[0] = 0;
2470 for (int i = 0; i < mesh->GetNE(); i++)
2471 {
2472 int p = GetElementOrderImpl(i);
2474 bdofs[i+1] = nbdofs;
2475 }
2476 }
2477 else
2478 {
2479 // the simple case: all elements are the same
2480 bdofs = NULL;
2482 nbdofs = mesh->GetNE() * fec->GetNumDof(geom, order);
2483 }
2484 }
2485
2487
2489
2490 // record the current mesh sequence number to detect refinement etc.
2492
2493 // increment our sequence number to let GridFunctions know they need updating
2494 sequence++;
2495
2496 // DOFs are now assigned according to current element orders
2497 orders_changed = false;
2498
2499 // Do not build elem_dof Table here: in parallel it has to be constructed
2500 // later.
2501}
2502
2504{
2505 MFEM_ASSERT(bits != 0, "invalid bit mask");
2506 for (int order = 0; bits != 0; order++, bits >>= 1)
2507 {
2508 if (bits & 1) { return order; }
2509 }
2510 return 0;
2511}
2512
2514 Array<VarOrderBits> &edge_orders, Array<VarOrderBits> &face_orders) const
2515{
2516 MFEM_ASSERT(IsVariableOrder(), "");
2517 MFEM_ASSERT(Nonconforming(), "");
2518 MFEM_ASSERT(elem_order.Size() == mesh->GetNE(), "");
2519
2520 edge_orders.SetSize(mesh->GetNEdges()); edge_orders = 0;
2521 face_orders.SetSize(mesh->GetNFaces()); face_orders = 0;
2522
2523 // Calculate initial edge/face orders, as required by incident elements.
2524 // For each edge/face we accumulate in a bit-mask the orders of elements
2525 // sharing the edge/face.
2526 Array<int> E, F, ori;
2527 for (int i = 0; i < mesh->GetNE(); i++)
2528 {
2529 int order = elem_order[i];
2530 MFEM_ASSERT(order <= MaxVarOrder, "");
2531 VarOrderBits mask = (VarOrderBits(1) << order);
2532
2533 mesh->GetElementEdges(i, E, ori);
2534 for (int j = 0; j < E.Size(); j++)
2535 {
2536 edge_orders[E[j]] |= mask;
2537 }
2538
2539 if (mesh->Dimension() > 2)
2540 {
2541 mesh->GetElementFaces(i, F, ori);
2542 for (int j = 0; j < F.Size(); j++)
2543 {
2544 face_orders[F[j]] |= mask;
2545 }
2546 }
2547 }
2548
2549 if (relaxed_hp)
2550 {
2551 // for relaxed conformity we don't need the masters to match the minimum
2552 // orders of the slaves, we can stop now
2553 return;
2554 }
2555
2556 // Iterate while minimum orders propagate by master/slave relations
2557 // (and new orders also propagate from faces to incident edges).
2558 // See https://github.com/mfem/mfem/pull/1423#issuecomment-638930559
2559 // for an illustration of why this is necessary in hp meshes.
2560 bool done;
2561 do
2562 {
2563 done = true;
2564
2565 // propagate from slave edges to master edges
2566 const NCMesh::NCList &edge_list = mesh->ncmesh->GetEdgeList();
2567 for (const NCMesh::Master &master : edge_list.masters)
2568 {
2569 VarOrderBits slave_orders = 0;
2570 for (int i = master.slaves_begin; i < master.slaves_end; i++)
2571 {
2572 slave_orders |= edge_orders[edge_list.slaves[i].index];
2573 }
2574
2575 int min_order = MinOrder(slave_orders);
2576 if (min_order < MinOrder(edge_orders[master.index]))
2577 {
2578 edge_orders[master.index] |= (VarOrderBits(1) << min_order);
2579 done = false;
2580 }
2581 }
2582
2583 // propagate from slave faces(+edges) to master faces(+edges)
2584 const NCMesh::NCList &face_list = mesh->ncmesh->GetFaceList();
2585 for (const NCMesh::Master &master : face_list.masters)
2586 {
2587 VarOrderBits slave_orders = 0;
2588 for (int i = master.slaves_begin; i < master.slaves_end; i++)
2589 {
2590 const NCMesh::Slave &slave = face_list.slaves[i];
2591 if (slave.index >= 0)
2592 {
2593 slave_orders |= face_orders[slave.index];
2594
2595 mesh->GetFaceEdges(slave.index, E, ori);
2596 for (int j = 0; j < E.Size(); j++)
2597 {
2598 slave_orders |= edge_orders[E[j]];
2599 }
2600 }
2601 else
2602 {
2603 // degenerate face (i.e., edge-face constraint)
2604 slave_orders |= edge_orders[-1 - slave.index];
2605 }
2606 }
2607
2608 int min_order = MinOrder(slave_orders);
2609 if (min_order < MinOrder(face_orders[master.index]))
2610 {
2611 face_orders[master.index] |= (VarOrderBits(1) << min_order);
2612 done = false;
2613 }
2614 }
2615
2616 // make sure edges support (new) orders required by incident faces
2617 for (int i = 0; i < mesh->GetNFaces(); i++)
2618 {
2619 mesh->GetFaceEdges(i, E, ori);
2620 for (int j = 0; j < E.Size(); j++)
2621 {
2622 edge_orders[E[j]] |= face_orders[i];
2623 }
2624 }
2625 }
2626 while (!done);
2627}
2628
2630 const Array<int> &entity_orders,
2631 Table &entity_dofs,
2632 Array<char> *var_ent_order)
2633{
2634 // The tables var_edge_dofs and var_face_dofs hold DOF assignments for edges
2635 // and faces of a variable order space, in which each edge/face may host
2636 // several DOF sets, called DOF set variants. Example: an edge 'i' shared by
2637 // 4 hexes of orders 2, 3, 4, 5 will hold four DOF sets, each starting at
2638 // indices e.g. 100, 101, 103, 106, respectively. These numbers are stored
2639 // in row 'i' of var_edge_dofs. Variant zero is always the lowest order DOF
2640 // set, followed by consecutive ranges of higher order DOFs. Variable order
2641 // faces are handled similarly by var_face_dofs, which holds at most two DOF
2642 // set variants per face. The tables are empty for constant-order spaces.
2643
2644 int num_ent = entity_orders.Size();
2645 int total_dofs = 0;
2646
2647 Array<Connection> list;
2648 list.Reserve(2*num_ent);
2649
2650 if (var_ent_order)
2651 {
2652 var_ent_order->SetSize(0);
2653 var_ent_order->Reserve(num_ent);
2654 }
2655
2656 // assign DOFs according to order bit masks
2657 for (int i = 0; i < num_ent; i++)
2658 {
2659 auto geom = (ent_dim == 1) ? Geometry::SEGMENT : mesh->GetFaceGeometry(i);
2660
2661 VarOrderBits bits = entity_orders[i];
2662 for (int order = 0; bits != 0; order++, bits >>= 1)
2663 {
2664 if (bits & 1)
2665 {
2666 int dofs = fec->GetNumDof(geom, order);
2667 list.Append(Connection(i, total_dofs));
2668 total_dofs += dofs;
2669 if (var_ent_order) { var_ent_order->Append(order); }
2670 }
2671 }
2672 }
2673
2674 // append a dummy row as terminator
2675 list.Append(Connection(num_ent, total_dofs));
2676
2677 // build the table
2678 entity_dofs.MakeFromList(num_ent+1, list);
2679 return total_dofs;
2680}
2681
2682int FiniteElementSpace::FindDofs(const Table &var_dof_table,
2683 int row, int ndof) const
2684{
2685 const int *beg = var_dof_table.GetRow(row);
2686 const int *end = var_dof_table.GetRow(row + 1); // terminator, see above
2687
2688 while (beg < end)
2689 {
2690 // return the appropriate range of DOFs
2691 if ((beg[1] - beg[0]) == ndof) { return beg[0]; }
2692 beg++;
2693 }
2694
2695 MFEM_ABORT("DOFs not found for ndof = " << ndof);
2696 return 0;
2697}
2698
2699int FiniteElementSpace::GetEdgeOrder(int edge, int variant) const
2700{
2701 if (!IsVariableOrder()) { return fec->GetOrder(); }
2702
2703 const int* beg = var_edge_dofs.GetRow(edge);
2704 const int* end = var_edge_dofs.GetRow(edge + 1);
2705 if (variant >= end - beg) { return -1; } // past last variant
2706
2707 return var_edge_orders[var_edge_dofs.GetI()[edge] + variant];
2708}
2709
2710int FiniteElementSpace::GetFaceOrder(int face, int variant) const
2711{
2712 if (!IsVariableOrder())
2713 {
2714 // face order can be different from fec->GetOrder()
2715 Geometry::Type geom = mesh->GetFaceGeometry(face);
2716 return fec->FiniteElementForGeometry(geom)->GetOrder();
2717 }
2718
2719 const int* beg = var_face_dofs.GetRow(face);
2720 const int* end = var_face_dofs.GetRow(face + 1);
2721 if (variant >= end - beg) { return -1; } // past last variant
2722
2723 return var_face_orders[var_face_dofs.GetI()[face] + variant];
2724}
2725
2726int FiniteElementSpace::GetNVariants(int entity, int index) const
2727{
2728 MFEM_ASSERT(IsVariableOrder(), "");
2729 const Table &dof_table = (entity == 1) ? var_edge_dofs : var_face_dofs;
2730
2731 MFEM_ASSERT(index >= 0 && index < dof_table.Size(), "");
2732 return dof_table.GetRow(index + 1) - dof_table.GetRow(index);
2733}
2734
2735static const char* msg_orders_changed =
2736 "Element orders changed, you need to Update() the space first.";
2737
2739 DofTransformation &doftrans) const
2740{
2741 MFEM_VERIFY(!orders_changed, msg_orders_changed);
2742
2743 if (elem_dof)
2744 {
2745 elem_dof->GetRow(elem, dofs);
2746
2748 {
2749 Array<int> Fo;
2750 elem_fos -> GetRow (elem, Fo);
2751 doftrans.SetDofTransformation(
2753 doftrans.SetFaceOrientations(Fo);
2754 doftrans.SetVDim();
2755 }
2756 return;
2757 }
2758
2759 Array<int> V, E, Eo, F, Fo; // TODO: LocalArray
2760
2761 int dim = mesh->Dimension();
2762 auto geom = mesh->GetElementGeometry(elem);
2763 int order = GetElementOrderImpl(elem);
2764
2765 int nv = fec->GetNumDof(Geometry::POINT, order);
2766 int ne = (dim > 1) ? fec->GetNumDof(Geometry::SEGMENT, order) : 0;
2767 int nb = (dim > 0) ? fec->GetNumDof(geom, order) : 0;
2768
2769 if (nv) { mesh->GetElementVertices(elem, V); }
2770 if (ne) { mesh->GetElementEdges(elem, E, Eo); }
2771
2772 int nfd = 0;
2773 if (dim > 2 && fec->HasFaceDofs(geom, order))
2774 {
2775 mesh->GetElementFaces(elem, F, Fo);
2776 for (int i = 0; i < F.Size(); i++)
2777 {
2778 nfd += fec->GetNumDof(mesh->GetFaceGeometry(F[i]), order);
2779 }
2781 {
2782 doftrans.SetDofTransformation(
2784 doftrans.SetFaceOrientations(Fo);
2785 doftrans.SetVDim();
2786 }
2787 }
2788
2789 dofs.SetSize(0);
2790 dofs.Reserve(nv*V.Size() + ne*E.Size() + nfd + nb);
2791
2792 if (nv) // vertex DOFs
2793 {
2794 for (int i = 0; i < V.Size(); i++)
2795 {
2796 for (int j = 0; j < nv; j++)
2797 {
2798 dofs.Append(V[i]*nv + j);
2799 }
2800 }
2801 }
2802
2803 if (ne) // edge DOFs
2804 {
2805 for (int i = 0; i < E.Size(); i++)
2806 {
2807 int ebase = IsVariableOrder() ? FindEdgeDof(E[i], ne) : E[i]*ne;
2808 const int *ind = fec->GetDofOrdering(Geometry::SEGMENT, order, Eo[i]);
2809
2810 for (int j = 0; j < ne; j++)
2811 {
2812 dofs.Append(EncodeDof(nvdofs + ebase, ind[j]));
2813 }
2814 }
2815 }
2816
2817 if (nfd) // face DOFs
2818 {
2819 for (int i = 0; i < F.Size(); i++)
2820 {
2821 auto fgeom = mesh->GetFaceGeometry(F[i]);
2822 int nf = fec->GetNumDof(fgeom, order);
2823
2824 int fbase = (var_face_dofs.Size() > 0) ? FindFaceDof(F[i], nf) : F[i]*nf;
2825 const int *ind = fec->GetDofOrdering(fgeom, order, Fo[i]);
2826
2827 for (int j = 0; j < nf; j++)
2828 {
2829 dofs.Append(EncodeDof(nvdofs + nedofs + fbase, ind[j]));
2830 }
2831 }
2832 }
2833
2834 if (nb) // interior ("bubble") DOFs
2835 {
2836 int bbase = bdofs ? bdofs[elem] : elem*nb;
2837 bbase += nvdofs + nedofs + nfdofs;
2838
2839 for (int j = 0; j < nb; j++)
2840 {
2841 dofs.Append(bbase + j);
2842 }
2843 }
2844}
2845
2847 Array<int> &dofs) const
2848{
2850 GetElementDofs(elem, dofs, DoFTrans);
2851 return DoFTrans.GetDofTransformation() ? &DoFTrans : NULL;
2852}
2853
2855 DofTransformation &doftrans) const
2856{
2857 MFEM_VERIFY(!orders_changed, msg_orders_changed);
2858
2859 if (bdr_elem_dof)
2860 {
2861 bdr_elem_dof->GetRow(bel, dofs);
2862
2864 {
2865 Array<int> Fo;
2866 bdr_elem_fos -> GetRow (bel, Fo);
2867 doftrans.SetDofTransformation(
2869 doftrans.SetFaceOrientations(Fo);
2870 doftrans.SetVDim();
2871 }
2872 return;
2873 }
2874
2875 Array<int> V, E, Eo; // TODO: LocalArray
2876 int F, oF;
2877
2878 int dim = mesh->Dimension();
2879 auto geom = mesh->GetBdrElementGeometry(bel);
2880 int order = fec->GetOrder();
2881
2882 if (IsVariableOrder()) // determine order from adjacent element
2883 {
2884 int elem, info;
2885 mesh->GetBdrElementAdjacentElement(bel, elem, info);
2886 order = elem_order[elem];
2887 }
2888
2889 int nv = fec->GetNumDof(Geometry::POINT, order);
2890 int ne = (dim > 1) ? fec->GetNumDof(Geometry::SEGMENT, order) : 0;
2891 int nf = (dim > 2) ? fec->GetNumDof(geom, order) : 0;
2892
2893 if (nv) { mesh->GetBdrElementVertices(bel, V); }
2894 if (ne) { mesh->GetBdrElementEdges(bel, E, Eo); }
2895 if (nf)
2896 {
2897 mesh->GetBdrElementFace(bel, &F, &oF);
2898
2900 {
2901 mfem::Array<int> Fo(1);
2902 Fo[0] = oF;
2903 doftrans.SetDofTransformation(
2905 doftrans.SetFaceOrientations(Fo);
2906 doftrans.SetVDim();
2907 }
2908 }
2909
2910 dofs.SetSize(0);
2911 dofs.Reserve(nv*V.Size() + ne*E.Size() + nf);
2912
2913 if (nv) // vertex DOFs
2914 {
2915 for (int i = 0; i < V.Size(); i++)
2916 {
2917 for (int j = 0; j < nv; j++)
2918 {
2919 dofs.Append(V[i]*nv + j);
2920 }
2921 }
2922 }
2923
2924 if (ne) // edge DOFs
2925 {
2926 for (int i = 0; i < E.Size(); i++)
2927 {
2928 int ebase = IsVariableOrder() ? FindEdgeDof(E[i], ne) : E[i]*ne;
2929 const int *ind = fec->GetDofOrdering(Geometry::SEGMENT, order, Eo[i]);
2930
2931 for (int j = 0; j < ne; j++)
2932 {
2933 dofs.Append(EncodeDof(nvdofs + ebase, ind[j]));
2934 }
2935 }
2936 }
2937
2938 if (nf) // face DOFs
2939 {
2940 int fbase = (var_face_dofs.Size() > 0) ? FindFaceDof(F, nf) : F*nf;
2941 const int *ind = fec->GetDofOrdering(geom, order, oF);
2942
2943 for (int j = 0; j < nf; j++)
2944 {
2945 dofs.Append(EncodeDof(nvdofs + nedofs + fbase, ind[j]));
2946 }
2947 }
2948}
2949
2957
2959 int variant) const
2960{
2961 MFEM_VERIFY(!orders_changed, msg_orders_changed);
2962
2963 // If face_dof is already built, use it.
2964 // If it is not and we have a NURBS space, build the face_dof and use it.
2965 if ((face_dof && variant == 0) ||
2966 (NURBSext && (BuildNURBSFaceToDofTable(), true)))
2967 {
2968 face_dof->GetRow(face, dofs);
2969 return fec->GetOrder();
2970 }
2971
2972 int order, nf, fbase;
2973 int dim = mesh->Dimension();
2974 auto fgeom = (dim > 2) ? mesh->GetFaceGeometry(face) : Geometry::INVALID;
2975
2976 if (var_face_dofs.Size() > 0) // variable orders or *mixed* faces
2977 {
2978 const int* beg = var_face_dofs.GetRow(face);
2979 const int* end = var_face_dofs.GetRow(face + 1);
2980 if (variant >= end - beg) { return -1; } // past last face DOFs
2981
2982 fbase = beg[variant];
2983 nf = beg[variant+1] - fbase;
2984
2985 order = !IsVariableOrder() ? fec->GetOrder() :
2986 var_face_orders[var_face_dofs.GetI()[face] + variant];
2987 MFEM_ASSERT(fec->GetNumDof(fgeom, order) == nf, [&]()
2988 {
2989 std::stringstream msg;
2990 msg << "fec->GetNumDof(" << (fgeom == Geometry::SQUARE ? "square" : "triangle")
2991 << ", " << order << ") = " << fec->GetNumDof(fgeom, order) << " nf " << nf;
2992 msg << " face " << face << " variant " << variant << std::endl;
2993 return msg.str();
2994 }());
2995 }
2996 else
2997 {
2998 if (variant > 0) { return -1; }
2999 order = fec->GetOrder();
3000 nf = (dim > 2) ? fec->GetNumDof(fgeom, order) : 0;
3001 fbase = face*nf;
3002 }
3003
3004 // for 1D, 2D and 3D faces
3005 int nv = fec->GetNumDof(Geometry::POINT, order);
3006 int ne = (dim > 1) ? fec->GetNumDof(Geometry::SEGMENT, order) : 0;
3007
3008 Array<int> V, E, Eo;
3009 if (nv) { mesh->GetFaceVertices(face, V); }
3010 if (ne) { mesh->GetFaceEdges(face, E, Eo); }
3011
3012 dofs.SetSize(0);
3013 dofs.Reserve(V.Size() * nv + E.Size() * ne + nf);
3014
3015 if (nv) // vertex DOFs
3016 {
3017 for (int i = 0; i < V.Size(); i++)
3018 {
3019 for (int j = 0; j < nv; j++)
3020 {
3021 dofs.Append(V[i]*nv + j);
3022 }
3023 }
3024 }
3025 if (ne) // edge DOFs
3026 {
3027 for (int i = 0; i < E.Size(); i++)
3028 {
3029 int ebase = IsVariableOrder() ? FindEdgeDof(E[i], ne) : E[i]*ne;
3030 const int *ind = fec->GetDofOrdering(Geometry::SEGMENT, order, Eo[i]);
3031
3032 for (int j = 0; j < ne; j++)
3033 {
3034 dofs.Append(EncodeDof(nvdofs + ebase, ind[j]));
3035 }
3036 }
3037 }
3038 for (int j = 0; j < nf; j++)
3039 {
3040 dofs.Append(nvdofs + nedofs + fbase + j);
3041 }
3042
3043 return order;
3044}
3045
3047 int variant) const
3048{
3049 MFEM_VERIFY(!orders_changed, msg_orders_changed);
3050
3051 int order, ne, base;
3052 if (IsVariableOrder())
3053 {
3054 const int* beg = var_edge_dofs.GetRow(edge);
3055 const int* end = var_edge_dofs.GetRow(edge + 1);
3056 if (variant >= end - beg) { return -1; } // past last edge DOFs
3057
3058 base = beg[variant];
3059 ne = beg[variant+1] - base;
3060
3061 order = var_edge_orders[var_edge_dofs.GetI()[edge] + variant];
3062 MFEM_ASSERT(fec->GetNumDof(Geometry::SEGMENT, order) == ne, "");
3063 }
3064 else
3065 {
3066 if (variant > 0) { return -1; }
3067 order = fec->GetOrder();
3068 ne = fec->GetNumDof(Geometry::SEGMENT, order);
3069 base = edge*ne;
3070 }
3071
3072 Array<int> V; // TODO: LocalArray
3073 int nv = fec->GetNumDof(Geometry::POINT, order);
3074 if (nv) { mesh->GetEdgeVertices(edge, V); }
3075
3076 dofs.SetSize(0);
3077 dofs.Reserve(2*nv + ne);
3078
3079 for (int i = 0; i < 2; i++)
3080 {
3081 for (int j = 0; j < nv; j++)
3082 {
3083 dofs.Append(V[i]*nv + j);
3084 }
3085 }
3086 for (int j = 0; j < ne; j++)
3087 {
3088 dofs.Append(nvdofs + base + j);
3089 }
3090
3091 return order;
3092}
3093
3095{
3097 dofs.SetSize(nv);
3098 for (int j = 0; j < nv; j++)
3099 {
3100 dofs[j] = i*nv+j;
3101 }
3102}
3103
3105{
3106 MFEM_VERIFY(!orders_changed, msg_orders_changed);
3107
3109 int base = bdofs ? bdofs[i] : i*nb;
3110
3111 dofs.SetSize(nb);
3112 base += nvdofs + nedofs + nfdofs;
3113 for (int j = 0; j < nb; j++)
3114 {
3115 dofs[j] = base + j;
3116 }
3117}
3118
3124
3126{
3127 MFEM_VERIFY(!IsVariableOrder(), "not implemented");
3128
3129 int nf, base;
3130 if (var_face_dofs.Size() > 0) // mixed faces
3131 {
3132 base = var_face_dofs.GetRow(i)[0];
3133 nf = var_face_dofs.GetRow(i)[1] - base;
3134 }
3135 else
3136 {
3137 auto geom = mesh->GetFaceGeometry(0);
3138 nf = fec->GetNumDof(geom, fec->GetOrder());
3139 base = i*nf;
3140 }
3141
3142 dofs.SetSize(nf);
3143 for (int j = 0; j < nf; j++)
3144 {
3145 dofs[j] = nvdofs + nedofs + base + j;
3146 }
3147}
3148
3150{
3151 MFEM_VERIFY(!IsVariableOrder(), "not implemented");
3152
3154 dofs.SetSize (ne);
3155 for (int j = 0, k = nvdofs+i*ne; j < ne; j++, k++)
3156 {
3157 dofs[j] = k;
3158 }
3159}
3160
3162{
3163 MFEM_ASSERT(NURBSext,
3164 "FiniteElementSpace::GetPatchDofs needs a NURBSExtension");
3165 NURBSext->GetPatchDofs(patch, dofs);
3166}
3167
3169{
3170 if (i < 0 || i >= mesh->GetNE())
3171 {
3172 if (mesh->GetNE() == 0)
3173 {
3174 MFEM_ABORT("Empty MPI partitions are not permitted!");
3175 }
3176 MFEM_ABORT("Invalid element id:" << i << "; minimum allowed:" << 0 <<
3177 ", maximum allowed:" << mesh->GetNE()-1);
3178 }
3179
3180 const FiniteElement *FE =
3182
3183 if (NURBSext)
3184 {
3185 NURBSext->LoadFE(i, FE);
3186 }
3187 else
3188 {
3189#ifdef MFEM_DEBUG
3190 // consistency check: fec->GetOrder() and FE->GetOrder() should return
3191 // the same value (for standard, constant-order spaces)
3192 if (!IsVariableOrder() && FE->GetDim() > 0)
3193 {
3194 MFEM_ASSERT(FE->GetOrder() == fec->GetOrder(),
3195 "internal error: " <<
3196 FE->GetOrder() << " != " << fec->GetOrder());
3197 }
3198#endif
3199 }
3200
3201 return FE;
3202}
3203
3205{
3206 int order = fec->GetOrder();
3207
3208 if (IsVariableOrder()) // determine order from adjacent element
3209 {
3210 int elem, info;
3211 mesh->GetBdrElementAdjacentElement(i, elem, info);
3212 order = elem_order[elem];
3213 }
3214
3215 const FiniteElement *BE;
3216 switch (mesh->Dimension())
3217 {
3218 case 1:
3219 BE = fec->GetFE(Geometry::POINT, order);
3220 break;
3221 case 2:
3222 BE = fec->GetFE(Geometry::SEGMENT, order);
3223 break;
3224 case 3:
3225 default:
3226 BE = fec->GetFE(mesh->GetBdrElementGeometry(i), order);
3227 }
3228
3229 if (NURBSext)
3230 {
3231 NURBSext->LoadBE(i, BE);
3232 }
3233
3234 return BE;
3235}
3236
3238{
3239 MFEM_VERIFY(!IsVariableOrder(), "not implemented");
3240
3241 const FiniteElement *fe;
3242 switch (mesh->Dimension())
3243 {
3244 case 1:
3246 break;
3247 case 2:
3249 break;
3250 case 3:
3251 default:
3253 }
3254
3255 if (NURBSext)
3256 {
3257 // Ensure 'face_to_be' is built:
3259 MFEM_ASSERT(face_to_be[i] >= 0,
3260 "NURBS mesh: only boundary faces are supported!");
3261 NURBSext->LoadBE(face_to_be[i], fe);
3262 }
3263
3264 return fe;
3265}
3266
3268 int variant) const
3269{
3270 MFEM_ASSERT(mesh->Dimension() > 1, "No edges with mesh dimension < 2");
3271
3272 int eo = IsVariableOrder() ? GetEdgeOrder(i, variant) : fec->GetOrder();
3273 return fec->GetFE(Geometry::SEGMENT, eo);
3274}
3275
3277 int i, Geometry::Type geom_type) const
3278{
3279 return fec->TraceFiniteElementForGeometry(geom_type);
3280}
3281
3286
3288{
3289 R_transpose.reset();
3290 cR.reset();
3291 cR_hp.reset();
3292 cP.reset();
3293 Th.Clear();
3294 L2E_nat.Clear();
3295 L2E_lex.Clear();
3296 for (int i = 0; i < E2Q_array.Size(); i++)
3297 {
3298 delete E2Q_array[i];
3299 }
3300 E2Q_array.SetSize(0);
3301 for (auto &x : L2F)
3302 {
3303 delete x.second;
3304 }
3305 L2F.clear();
3306 for (int i = 0; i < E2IFQ_array.Size(); i++)
3307 {
3308 delete E2IFQ_array[i];
3309 }
3310 E2IFQ_array.SetSize(0);
3311 for (int i = 0; i < E2BFQ_array.Size(); i++)
3312 {
3313 delete E2BFQ_array[i];
3314 }
3315 E2BFQ_array.SetSize(0);
3316
3318
3321
3322 if (NURBSext)
3323 {
3324 if (own_ext) { delete NURBSext; }
3325 delete face_dof;
3327 }
3328 else
3329 {
3330 delete elem_dof;
3331 delete elem_fos;
3332 delete bdr_elem_dof;
3333 delete bdr_elem_fos;
3334 delete face_dof;
3335 delete [] bdofs;
3336 }
3338}
3339
3341{
3342 for (int i = 0; i < DoFTransArray.Size(); i++)
3343 {
3344 delete DoFTransArray[i];
3345 }
3346 DoFTransArray.SetSize(0);
3347}
3348
3350 const FiniteElementSpace &coarse_fes, OperatorHandle &T) const
3351{
3352 // Assumptions: see the declaration of the method.
3353
3354 if (T.Type() == Operator::MFEM_SPARSEMAT)
3355 {
3356 Mesh::GeometryList elem_geoms(*mesh);
3357
3359 for (int i = 0; i < elem_geoms.Size(); i++)
3360 {
3361 GetLocalRefinementMatrices(coarse_fes, elem_geoms[i],
3362 localP[elem_geoms[i]]);
3363 }
3364 T.Reset(RefinementMatrix_main(coarse_fes.GetNDofs(),
3365 coarse_fes.GetElementToDofTable(),
3366 coarse_fes.
3368 localP));
3369 }
3370 else
3371 {
3372 T.Reset(new RefinementOperator(this, &coarse_fes));
3373 }
3374}
3375
3377 const FiniteElementSpace &coarse_fes, OperatorHandle &T) const
3378{
3379 const SparseMatrix *coarse_P = coarse_fes.GetConformingProlongation();
3380
3381 Operator::Type req_type = T.Type();
3382 GetTransferOperator(coarse_fes, T);
3383
3384 if (req_type == Operator::MFEM_SPARSEMAT)
3385 {
3387 {
3388 T.Reset(mfem::Mult(*cR, *T.As<SparseMatrix>()));
3389 }
3390 if (coarse_P)
3391 {
3392 T.Reset(mfem::Mult(*T.As<SparseMatrix>(), *coarse_P));
3393 }
3394 }
3395 else
3396 {
3397 const int RP_case = bool(GetConformingRestriction()) + 2*bool(coarse_P);
3398 if (RP_case == 0) { return; }
3399 const bool owner = T.OwnsOperator();
3400 T.SetOperatorOwner(false);
3401 switch (RP_case)
3402 {
3403 case 1:
3404 T.Reset(new ProductOperator(cR.get(), T.Ptr(), false, owner));
3405 break;
3406 case 2:
3407 T.Reset(new ProductOperator(T.Ptr(), coarse_P, owner, false));
3408 break;
3409 case 3:
3411 cR.get(), T.Ptr(), coarse_P, false, owner, false));
3412 break;
3413 }
3414 }
3415}
3416
3418{
3420
3421 Array<char> new_order(mesh->GetNE());
3422 switch (mesh->GetLastOperation())
3423 {
3424 case Mesh::REFINE:
3425 {
3426 for (int i = 0; i < mesh->GetNE(); i++)
3427 {
3428 new_order[i] = elem_order[cf_tr.embeddings[i].parent];
3429 }
3430 break;
3431 }
3432 default:
3433 MFEM_ABORT("not implemented yet");
3434 }
3435
3436 mfem::Swap(elem_order, new_order);
3437}
3438
3439void FiniteElementSpace::Update(bool want_transform)
3440{
3441 if (!orders_changed)
3442 {
3443 if (mesh->GetSequence() == mesh_sequence)
3444 {
3445 return; // mesh and space are in sync, no-op
3446 }
3447 if (want_transform && mesh->GetSequence() != mesh_sequence + 1)
3448 {
3449 MFEM_ABORT("Error in update sequence. Space needs to be updated after "
3450 "each mesh modification.");
3451 }
3452 }
3453 else
3454 {
3455 if (mesh->GetSequence() != mesh_sequence)
3456 {
3457 MFEM_ABORT("Updating space after both mesh change and element order "
3458 "change is not supported. Please update separately after "
3459 "each change.");
3460 }
3461 }
3462
3463 if (NURBSext)
3464 {
3465 UpdateNURBS();
3466 return;
3467 }
3468
3469 Table* old_elem_dof = NULL;
3470 Table* old_elem_fos = NULL;
3471 int old_ndofs;
3472 bool old_orders_changed = orders_changed;
3473
3474 // save old DOF table
3475 if (want_transform)
3476 {
3477 old_elem_dof = elem_dof;
3478 old_elem_fos = elem_fos;
3479 elem_dof = NULL;
3480 elem_fos = NULL;
3481 old_ndofs = ndofs;
3482 }
3483
3484 // update the 'elem_order' array if the mesh has changed
3486 {
3488 }
3489
3490 Destroy(); // calls Th.Clear()
3491 Construct();
3493
3494 if (want_transform)
3495 {
3496 MFEM_VERIFY(!old_orders_changed, "Interpolation for element order change "
3497 "is not implemented yet, sorry.");
3498
3499 // calculate appropriate GridFunction transformation
3500 switch (mesh->GetLastOperation())
3501 {
3502 case Mesh::REFINE:
3503 {
3505 {
3506 Th.Reset(new RefinementOperator(this, old_elem_dof,
3507 old_elem_fos, old_ndofs));
3508 // The RefinementOperator takes ownership of 'old_elem_dof', so
3509 // we no longer own it:
3510 old_elem_dof = NULL;
3511 old_elem_fos = NULL;
3512 }
3513 else
3514 {
3515 // calculate fully assembled matrix
3516 Th.Reset(RefinementMatrix(old_ndofs, old_elem_dof,
3517 old_elem_fos));
3518 }
3519 break;
3520 }
3521
3522 case Mesh::DEREFINE:
3523 {
3525 Th.Reset(DerefinementMatrix(old_ndofs, old_elem_dof, old_elem_fos));
3526 if (cP && cR)
3527 {
3528 Th.SetOperatorOwner(false);
3529 Th.Reset(new TripleProductOperator(cP.get(), cR.get(), Th.Ptr(),
3530 false, false, true));
3531 }
3532 break;
3533 }
3534
3535 default:
3536 break;
3537 }
3538
3539 delete old_elem_dof;
3540 delete old_elem_fos;
3541 }
3542}
3543
3545{
3546 mesh = new_mesh;
3547}
3548
3549void FiniteElementSpace::Save(std::ostream &os) const
3550{
3551 int fes_format = 90; // the original format, v0.9
3552 bool nurbs_unit_weights = false;
3553
3554 // Determine the format that should be used.
3555 if (!NURBSext)
3556 {
3557 // TODO: if this is a variable-order FE space, use fes_format = 100.
3558 }
3559 else
3560 {
3561 const NURBSFECollection *nurbs_fec =
3562 dynamic_cast<const NURBSFECollection *>(fec);
3563 MFEM_VERIFY(nurbs_fec, "invalid FE collection");
3564 nurbs_fec->SetOrder(NURBSext->GetOrder());
3565 const real_t eps = 5e-14;
3566 nurbs_unit_weights = (NURBSext->GetWeights().Min() >= 1.0-eps &&
3567 NURBSext->GetWeights().Max() <= 1.0+eps);
3569 (NURBSext != mesh->NURBSext && !nurbs_unit_weights) ||
3570 (NURBSext->GetMaster().Size() != 0 ))
3571 {
3572 fes_format = 100; // v1.0 format
3573 }
3574 }
3575
3576 os << (fes_format == 90 ?
3577 "FiniteElementSpace\n" : "MFEM FiniteElementSpace v1.0\n")
3578 << "FiniteElementCollection: " << fec->Name() << '\n'
3579 << "VDim: " << vdim << '\n'
3580 << "Ordering: " << ordering << '\n';
3581
3582 if (fes_format == 100) // v1.0
3583 {
3584 if (!NURBSext)
3585 {
3586 // TODO: this is a variable-order FE space --> write 'element_orders'.
3587 }
3588 else if (NURBSext != mesh->NURBSext)
3589 {
3591 {
3592 os << "NURBS_order\n" << NURBSext->GetOrder() << '\n';
3593 }
3594 else
3595 {
3596 os << "NURBS_orders\n";
3597 // 1 = do not write the size, just the entries:
3598 NURBSext->GetOrders().Save(os, 1);
3599 }
3600 // If periodic BCs are given, write connectivity
3601 if (NURBSext->GetMaster().Size() != 0 )
3602 {
3603 os <<"NURBS_periodic\n";
3604 NURBSext->GetMaster().Save(os);
3605 NURBSext->GetSlave().Save(os);
3606 }
3607 // If the weights are not unit, write them to the output:
3608 if (!nurbs_unit_weights)
3609 {
3610 os << "NURBS_weights\n";
3611 NURBSext->GetWeights().Print(os, 1);
3612 }
3613 }
3614 os << "End: MFEM FiniteElementSpace v1.0\n";
3615 }
3616}
3617
3619{
3620 string buff;
3621 int fes_format = 0, ord;
3623
3624 Destroy();
3625
3626 input >> std::ws;
3627 getline(input, buff); // 'FiniteElementSpace'
3628 filter_dos(buff);
3629 if (buff == "FiniteElementSpace") { fes_format = 90; /* v0.9 */ }
3630 else if (buff == "MFEM FiniteElementSpace v1.0") { fes_format = 100; }
3631 else { MFEM_ABORT("input stream is not a FiniteElementSpace!"); }
3632 getline(input, buff, ' '); // 'FiniteElementCollection:'
3633 input >> std::ws;
3634 getline(input, buff);
3635 filter_dos(buff);
3636 r_fec = FiniteElementCollection::New(buff.c_str());
3637 getline(input, buff, ' '); // 'VDim:'
3638 input >> vdim;
3639 getline(input, buff, ' '); // 'Ordering:'
3640 input >> ord;
3641
3642 NURBSFECollection *nurbs_fec = dynamic_cast<NURBSFECollection*>(r_fec);
3643 NURBSExtension *nurbs_ext = NULL;
3644 if (fes_format == 90) // original format, v0.9
3645 {
3646 if (nurbs_fec)
3647 {
3648 MFEM_VERIFY(m->NURBSext, "NURBS FE collection requires a NURBS mesh!");
3649 const int order = nurbs_fec->GetOrder();
3650 if (order != m->NURBSext->GetOrder() &&
3652 {
3653 nurbs_ext = new NURBSExtension(m->NURBSext, order);
3654 }
3655 }
3656 }
3657 else if (fes_format == 100) // v1.0
3658 {
3659 while (1)
3660 {
3661 skip_comment_lines(input, '#');
3662 MFEM_VERIFY(input.good(), "error reading FiniteElementSpace v1.0");
3663 getline(input, buff);
3664 filter_dos(buff);
3665 if (buff == "NURBS_order" || buff == "NURBS_orders")
3666 {
3667 MFEM_VERIFY(nurbs_fec,
3668 buff << ": NURBS FE collection is required!");
3669 MFEM_VERIFY(m->NURBSext, buff << ": NURBS mesh is required!");
3670 MFEM_VERIFY(!nurbs_ext, buff << ": order redefinition!");
3671 if (buff == "NURBS_order")
3672 {
3673 int order;
3674 input >> order;
3675 nurbs_ext = new NURBSExtension(m->NURBSext, order);
3676 }
3677 else
3678 {
3679 Array<int> orders;
3680 orders.Load(m->NURBSext->GetNKV(), input);
3681 nurbs_ext = new NURBSExtension(m->NURBSext, orders);
3682 }
3683 }
3684 else if (buff == "NURBS_periodic")
3685 {
3686 Array<int> master, slave;
3687 master.Load(input);
3688 slave.Load(input);
3689 nurbs_ext->ConnectBoundaries(master,slave);
3690 }
3691 else if (buff == "NURBS_weights")
3692 {
3693 MFEM_VERIFY(nurbs_ext, "NURBS_weights: NURBS_orders have to be "
3694 "specified before NURBS_weights!");
3695 nurbs_ext->GetWeights().Load(input, nurbs_ext->GetNDof());
3696 }
3697 else if (buff == "element_orders")
3698 {
3699 MFEM_VERIFY(!nurbs_fec, "section element_orders cannot be used "
3700 "with a NURBS FE collection");
3701 MFEM_ABORT("element_orders: not implemented yet!");
3702 }
3703 else if (buff == "End: MFEM FiniteElementSpace v1.0")
3704 {
3705 break;
3706 }
3707 else
3708 {
3709 MFEM_ABORT("unknown section: " << buff);
3710 }
3711 }
3712 }
3713
3714 Constructor(m, nurbs_ext, r_fec, vdim, ord);
3715
3716 return r_fec;
3717}
3718
3725
3726} // namespace mfem
T Max() const
Find the maximal element in the array, using the comparison operator < for class T.
Definition array.cpp:68
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:53
const T * HostRead() const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), false).
Definition array.hpp:321
T Sum()
Return the sum of all the array entries using the '+'' operator for class 'T'.
Definition array.cpp:115
void Reserve(int capacity)
Ensures that the allocated size is at least the given size.
Definition array.hpp:162
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition array.hpp:697
int Size() const
Return the logical size of the array.
Definition array.hpp:144
void MakeRef(T *data_, int size_, bool own_data=false)
Make this Array a reference to a pointer.
Definition array.hpp:882
void DeleteAll()
Delete the whole array.
Definition array.hpp:864
int Append(const T &el)
Append element 'el' to array, resize if necessary.
Definition array.hpp:769
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:40
void Copy(Array &copy) const
Create a copy of the internal array to the provided copy.
Definition array.hpp:874
T * HostWrite()
Shortcut for mfem::Write(a.GetMemory(), a.Size(), false).
Definition array.hpp:329
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:220
void MultTranspose(const real_t *x, real_t *y) const
Multiply a vector with the transpose matrix.
Definition densemat.cpp:262
void SetSize(int s)
Change the size of the DenseMatrix to s x s.
Definition densemat.hpp:105
void UseExternalData(real_t *d, int h, int w)
Change the data array and the size of the DenseMatrix.
Definition densemat.hpp:80
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
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
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.
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.
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:126
@ DISCONTINUOUS
Field is discontinuous across element interfaces.
Definition fe_coll.hpp:48
const int * GetDofOrdering(Geometry::Type geom, int p, int ori) const
Variable order version of DofOrderForOrientation().
Definition fe_coll.hpp:215
int GetOrder() const
Return the order (polynomial degree) of the FE collection, corresponding to the order/degree returned...
Definition fe_coll.hpp:225
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:203
virtual const FiniteElement * TraceFiniteElementForGeometry(Geometry::Type GeomType) const
Definition fe_coll.hpp:97
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:191
virtual const FiniteElement * FiniteElementForGeometry(Geometry::Type GeomType) const =0
virtual void Mult(const Vector &x, Vector &y) const
Operator application: y=A(x).
Definition fespace.cpp:2037
DerefinementOperator(const FiniteElementSpace *f_fes, const FiniteElementSpace *c_fes, BilinearFormIntegrator *mass_integ)
TODO: Implement DofTransformation support.
Definition fespace.cpp:1936
GridFunction interpolation operator applicable after mesh refinement.
Definition fespace.hpp:417
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:1729
RefinementOperator(const FiniteElementSpace *fespace, Table *old_elem_dof, Table *old_elem_fos, int old_ndofs)
Definition fespace.cpp:1570
virtual void Mult(const Vector &x, Vector &y) const
Operator application: y=A(x).
Definition fespace.cpp:1665
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
Definition fespace.hpp:220
void Save(std::ostream &out) const
Save finite element space to output stream out.
Definition fespace.cpp:3549
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:976
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:193
DofTransformation DoFTrans
Definition fespace.hpp:275
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:1013
const SparseMatrix * GetConformingRestriction() const
The returned SparseMatrix is owned by the FiniteElementSpace.
Definition fespace.cpp:1309
void ReorderElementToDofTable()
Reorder the scalar DOFs based on the element ordering.
Definition fespace.cpp:461
Array< char > var_face_orders
Definition fespace.hpp:259
bool IsVariableOrder() const
Returns true if the space contains elements of varying polynomial orders.
Definition fespace.hpp:581
void BuildNURBSFaceToDofTable() const
Generates partial face_dof table for a NURBS space.
Definition fespace.cpp:2325
static void AddDependencies(SparseMatrix &deps, Array< int > &master_dofs, Array< int > &slave_dofs, DenseMatrix &I, int skipfirst=0)
Definition fespace.cpp:807
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:1136
Array< int > dof_ldof_array
Definition fespace.hpp:268
Array< StatelessDofTransformation * > DoFTransArray
Definition fespace.hpp:274
void DofsToVDofs(Array< int > &dofs, int ndofs=-1) const
Compute the full set of vdofs corresponding to each entry in dofs.
Definition fespace.cpp:213
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:3149
Array< FaceQuadratureInterpolator * > E2BFQ_array
Definition fespace.hpp:311
const FiniteElement * GetBE(int i) const
Returns pointer to the FiniteElement in the FiniteElementCollection associated with i'th boundary fac...
Definition fespace.cpp:3204
Array< FaceQuadratureInterpolator * > E2IFQ_array
Definition fespace.hpp:310
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:2846
int GetNumElementInteriorDofs(int i) const
Returns the number of degrees of freedom associated with the interior of the specified element.
Definition fespace.cpp:3119
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:322
NURBSExtension * NURBSext
Definition fespace.hpp:270
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:2958
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:3376
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:265
int GetEdgeOrder(int edge, int variant=0) const
Definition fespace.cpp:2699
bool Nonconforming() const
Definition fespace.hpp:566
void GetVertexVDofs(int i, Array< int > &vdofs) const
Returns the indices of the degrees of freedom for the specified vertices.
Definition fespace.cpp:328
void GetVertexDofs(int i, Array< int > &dofs) const
Returns the indices of the degrees of freedom for the specified vertices.
Definition fespace.cpp:3094
OperatorHandle L2E_lex
Definition fespace.hpp:293
void BuildFaceToDofTable() const
Definition fespace.cpp:426
int GetFaceOrder(int face, int variant=0) const
Returns the polynomial degree of the i'th face finite element.
Definition fespace.cpp:2710
int GetNumBorderDofs(Geometry::Type geom, int order) const
Definition fespace.cpp:942
FiniteElementSpace()
Default constructor: the object is invalid until initialized using the method Load().
Definition fespace.cpp:59
Array< int > dof_elem_array
Definition fespace.hpp:268
int FindFaceDof(int face, int ndof) const
Similar to FindEdgeDof, but used for mixed meshes too.
Definition fespace.hpp:373
static int MinOrder(VarOrderBits bits)
Return the minimum order (least significant bit set) in the bit mask.
Definition fespace.cpp:2503
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:667
MFEM_DEPRECATED void RebuildElementToDofTable()
(
Definition fespace.cpp:452
int GetNDofs() const
Returns number of degrees of freedom. This is the number of Local Degrees of Freedom.
Definition fespace.hpp:710
bool orders_changed
True if at least one element order changed (variable-order space only).
Definition fespace.hpp:322
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:2095
void GetLocalRefinementMatrices(Geometry::Type geom, DenseTensor &localP) const
Definition fespace.cpp:1528
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:340
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:3349
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:697
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:588
int GetNBE() const
Returns number of boundary elements in the mesh.
Definition fespace.hpp:749
virtual void UpdateMeshPointer(Mesh *new_mesh)
Definition fespace.cpp:3544
int GetDegenerateFaceDofs(int index, Array< int > &dofs, Geometry::Type master_geom, int variant) const
Definition fespace.cpp:900
const QuadratureInterpolator * GetQuadratureInterpolator(const IntegrationRule &ir) const
Return a QuadratureInterpolator that interpolates E-vectors to quadrature point values and/or derivat...
Definition fespace.cpp:1404
int GetBdrAttribute(int i) const
Definition fespace.hpp:787
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:951
virtual const Operator * GetRestrictionOperator() const
An abstract operator that performs the same action as GetRestrictionMatrix.
Definition fespace.hpp:616
static constexpr int MaxVarOrder
Definition fespace.hpp:346
virtual void CopyProlongationAndRestriction(const FiniteElementSpace &fes, const Array< int > *perm)
Copies the prolongation and restriction matrices from fes.
Definition fespace.cpp:98
Array< char > var_edge_orders
Definition fespace.hpp:259
std::unique_ptr< Operator > R_transpose
Operator computing the action of the transpose of the restriction.
Definition fespace.hpp:287
bool Conforming() const
Definition fespace.hpp:565
void UpdateElementOrders()
Resize the elem_order array on mesh change.
Definition fespace.cpp:3417
void MakeVDimMatrix(SparseMatrix &mat) const
Replicate 'mat' in the vector dimension, according to vdim ordering mode.
Definition fespace.cpp:1274
int GetNF() const
Returns number of faces (i.e. co-dimension 1 entities) in the mesh.
Definition fespace.hpp:746
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:760
SparseMatrix * D2Const_GlobalRestrictionMatrix(FiniteElementSpace *cfes)
Generate the global restriction matrix from a discontinuous FE space to the piecewise constant FE spa...
Definition fespace.cpp:729
const FiniteElementCollection * fec
Associated FE collection (not owned).
Definition fespace.hpp:231
FiniteElementCollection * Load(Mesh *m, std::istream &input)
Read a FiniteElementSpace from a stream. The returned FiniteElementCollection is owned by the caller.
Definition fespace.cpp:3618
void BuildBdrElementToDofTable() const
Definition fespace.cpp:387
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:287
Array< QuadratureInterpolator * > E2Q_array
Definition fespace.hpp:309
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:3168
Ordering::Type GetOrdering() const
Return the ordering method.
Definition fespace.hpp:725
NURBSExtension * StealNURBSext()
Definition fespace.cpp:2290
void BuildDofToArrays()
Initialize internal data that enables the use of the methods GetElementForDof() and GetLocalDofForDof...
Definition fespace.cpp:481
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:3161
const Operator * GetRestrictionTransposeOperator() const
Return an operator that performs the transpose of GetRestrictionOperator.
Definition fespace.cpp:1324
Array< char > elem_order
Definition fespace.hpp:246
static bool DofFinalizable(int dof, const Array< bool > &finalized, const SparseMatrix &deps)
Definition fespace.cpp:886
Array< int > face_to_be
Definition fespace.hpp:272
void GetLocalDerefinementMatrices(Geometry::Type geom, DenseTensor &localR) const
Definition fespace.cpp:2071
void BuildConformingInterpolation() const
Calculate the cP and cR matrices for a nonconforming mesh.
Definition fespace.cpp:985
int GetNE() const
Returns number of elements in the mesh.
Definition fespace.hpp:740
int vdim
Vector dimension (number of unknowns per degree of freedom).
Definition fespace.hpp:234
Table var_face_dofs
NOTE: also used for spaces with mixed faces.
Definition fespace.hpp:255
OperatorHandle L2E_nat
The element restriction operators, see GetElementRestriction().
Definition fespace.hpp:293
int * bdofs
internal DOFs of elements if mixed/var-order; NULL otherwise
Definition fespace.hpp:250
const ElementRestrictionOperator * GetElementRestriction(ElementDofOrdering e_ordering) const
Return an Operator that converts L-vectors to E-vectors.
Definition fespace.cpp:1336
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:3267
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:3046
std::tuple< bool, ElementDofOrdering, FaceType, L2FaceValues > key_face
The face restriction operators, see GetFaceRestriction().
Definition fespace.hpp:295
std::unique_ptr< SparseMatrix > cR
Conforming restriction matrix such that cR.cP=I.
Definition fespace.hpp:282
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:334
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:316
virtual void Update(bool want_transform=true)
Reflect changes in the mesh: update number of DOFs, etc. Also, calculate GridFunction transformation ...
Definition fespace.cpp:3439
OperatorHandle Th
Transformation to apply to GridFunctions after space Update().
Definition fespace.hpp:290
void CalcEdgeFaceVarOrders(Array< VarOrderBits > &edge_orders, Array< VarOrderBits > &face_orders) const
Definition fespace.cpp:2513
SparseMatrix * RefinementMatrix(int old_ndofs, const Table *old_elem_dof, const Table *old_elem_fos)
Definition fespace.cpp:1551
std::uint64_t VarOrderBits
Bit-mask representing a set of orders needed by an edge/face.
Definition fespace.hpp:345
SparseMatrix * RefinementMatrix_main(const int coarse_ndofs, const Table &coarse_elem_dof, const Table *coarse_elem_fos, const DenseTensor localP[]) const
Definition fespace.cpp:1464
int ndofs
Number of degrees of freedom. Number of unknowns is ndofs * vdim.
Definition fespace.hpp:242
std::unique_ptr< SparseMatrix > cP
Definition fespace.hpp:280
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:832
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:3276
int MakeDofTable(int ent_dim, const Array< int > &entity_orders, Table &entity_dofs, Array< char > *var_ent_order)
Definition fespace.cpp:2629
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:648
int GetElementOrder(int i) const
Returns the order of the i'th finite element.
Definition fespace.cpp:176
Mesh * mesh
The mesh that FE space lives on (not owned).
Definition fespace.hpp:228
const FiniteElement * GetFaceElement(int i) const
Returns pointer to the FiniteElement in the FiniteElementCollection associated with i'th face in the ...
Definition fespace.cpp:3237
Ordering::Type ordering
Definition fespace.hpp:239
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:680
void SetElementOrder(int i, int p)
Sets the order of the i'th finite element.
Definition fespace.cpp:149
const SparseMatrix * GetHpConformingRestriction() const
The returned SparseMatrix is owned by the FiniteElementSpace.
Definition fespace.cpp:1317
const SparseMatrix * GetConformingProlongation() const
The returned SparseMatrix is owned by the FiniteElementSpace.
Definition fespace.cpp:1302
const FiniteElementCollection * FEColl() const
Definition fespace.hpp:727
Mesh * GetMesh() const
Returns the mesh.
Definition fespace.hpp:559
int GetElementOrderImpl(int i) const
Return element order: internal version of GetElementOrder without checks.
Definition fespace.cpp:187
int GetNVariants(int entity, int index) const
Return number of possible DOF variants for edge/face (var. order spaces).
Definition fespace.cpp:2726
int GetVSize() const
Return the number of vector dofs, i.e. GetNDofs() x GetVDim().
Definition fespace.hpp:713
const Table * GetElementToFaceOrientationTable() const
Definition fespace.hpp:1132
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:2950
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:632
int GetVDim() const
Returns vector dimension.
Definition fespace.hpp:706
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:1433
bool IsDGSpace() const
Return whether or not the space is discontinuous (L2)
Definition fespace.hpp:1313
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:3125
int GetNConformingDofs() const
Definition fespace.cpp:1330
static int DecodeDof(int dof)
Helper to return the DOF associated with a sign encoded DOF.
Definition fespace.hpp:1017
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:3104
void Constructor(Mesh *mesh, NURBSExtension *ext, const FiniteElementCollection *fec, int vdim=1, int ordering=Ordering::byNODES)
Help function for constructors + Load().
Definition fespace.cpp:2198
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:303
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:688
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:514
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:249
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:1369
int FindEdgeDof(int edge, int ndof) const
Definition fespace.hpp:369
void GetPatchVDofs(int i, Array< int > &vdofs) const
Returns indices of degrees of freedom in vdofs for NURBS patch i.
Definition fespace.cpp:310
void BuildElementToDofTable() const
Definition fespace.cpp:346
std::unique_ptr< SparseMatrix > cR_hp
A version of the conforming restriction matrix for variable-order spaces.
Definition fespace.hpp:284
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:2682
Abstract class for all finite elements.
Definition fe_base.hpp:239
int GetOrder() const
Returns the order of the finite element. In the case of anisotropic orders, returns the maximum order...
Definition fe_base.hpp:333
int GetDim() const
Returns the reference space dimension for the finite element.
Definition fe_base.hpp:316
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:119
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:113
Geometry::Type GetGeomType() const
Returns the Geometry::Type of the reference element.
Definition fe_base.hpp:326
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:107
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:126
int GetDof() const
Returns the number of degrees of freedom in the finite element.
Definition fe_base.hpp:329
static const int NumGeom
Definition geom.hpp:42
static const int NumVerts[NumGeom]
Definition geom.hpp:49
Class for an integration rule - an Array of IntegrationPoint.
Definition intrules.hpp:100
A standard isoparametric element transformation.
Definition eltrans.hpp:363
void SetPointMat(const DenseMatrix &pm)
Set the underlying point matrix describing the transformation.
Definition eltrans.hpp:402
void SetFE(const FiniteElement *FE)
Set the element that will be used to compute the transformations.
Definition eltrans.hpp:382
void SetIdentityTransformation(Geometry::Type GeomType)
Set the FiniteElement Geometry for the reference elements being used.
Definition eltrans.cpp:379
const DenseMatrix & GetPointMat() const
Return the stored point matrix.
Definition eltrans.hpp:405
Operator that converts L2 FiniteElementSpace L-vectors to E-vectors.
Operator that extracts Face degrees of freedom for L2 spaces.
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:1419
Mesh data type.
Definition mesh.hpp:56
void GetFaceEdges(int i, Array< int > &edges, Array< int > &o) const
Definition mesh.cpp:7039
Operation GetLastOperation() const
Return type of last modification of the mesh.
Definition mesh.hpp:2236
int GetNEdges() const
Return the number of edges.
Definition mesh.hpp:1232
void GetBdrElementFace(int i, int *f, int *o) const
Definition mesh.cpp:7252
Array< int > bdr_attributes
A list of all unique boundary attributes used by the Mesh.
Definition mesh.hpp:282
NURBSExtension * NURBSext
Optional NURBS mesh extension.
Definition mesh.hpp:290
int GetNumFaces() const
Return the number of faces (3D), edges (2D) or vertices (1D).
Definition mesh.cpp:6250
void GetBdrElementVertices(int i, Array< int > &v) const
Returns the indices of the vertices of boundary element i.
Definition mesh.hpp:1442
Geometry::Type GetFaceGeometry(int i) const
Return the Geometry::Type associated with face i.
Definition mesh.cpp:1452
Geometry::Type GetElementGeometry(int i) const
Definition mesh.hpp:1371
Geometry::Type GetBdrElementGeometry(int i) const
Definition mesh.hpp:1376
void GetElementVertices(int i, Array< int > &v) const
Returns the indices of the vertices of element i.
Definition mesh.hpp:1438
int GetBdrElementFaceIndex(int be_idx) const
Return the local face (codimension-1) index for the given boundary element index.
Definition mesh.hpp:1518
int GetNFaces() const
Return the number of faces in a 3D mesh.
Definition mesh.hpp:1235
long GetSequence() const
Definition mesh.hpp:2242
const CoarseFineTransformations & GetRefinementTransforms() const
Definition mesh.cpp:11082
int GetNE() const
Returns number of elements.
Definition mesh.hpp:1226
const Element * GetFace(int i) const
Return pointer to the i'th face element object.
Definition mesh.hpp:1310
int Dimension() const
Dimension of the reference space used within the elements.
Definition mesh.hpp:1160
const Element * GetBdrElement(int i) const
Return pointer to the i'th boundary element object.
Definition mesh.hpp:1298
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:7007
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:7201
int GetNV() const
Returns number of vertices. Vertices are only at the corners of elements, where you would expect them...
Definition mesh.hpp:1223
void GetEdgeVertices(int i, Array< int > &vert) const
Returns the indices of the vertices of edge i.
Definition mesh.cpp:7069
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:7271
void GetFaceVertices(int i, Array< int > &vert) const
Returns the indices of the vertices of face i.
Definition mesh.hpp:1456
int GetNBE() const
Returns number of boundary elements.
Definition mesh.hpp:1229
NCMesh * ncmesh
Optional nonconforming mesh extension.
Definition mesh.hpp:291
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:6985
int GetNumGeometries(int dim) const
Return the number of geometries of the given dimension present in the mesh.
Definition mesh.cpp:6961
Geometry::Type GetElementBaseGeometry(int i) const
Definition mesh.hpp:1385
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:320
const CoarseFineTransformations & GetDerefinementTransforms() const
Definition ncmesh.cpp:4725
const NCList & GetFaceList()
Return the current list of conforming and nonconforming faces.
Definition ncmesh.hpp:298
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:5286
const NCList & GetEdgeList()
Return the current list of conforming and nonconforming edges.
Definition ncmesh.hpp:305
Arbitrary order H(curl)-conforming Nedelec finite elements.
Definition fe_coll.hpp:465
DoF transformation implementation for the Nedelec basis on tetrahedra.
Definition doftrans.hpp:345
DoF transformation implementation for the Nedelec basis on wedge elements.
Definition doftrans.hpp:354
const Vector & GetWeights() const
Definition nurbs.hpp:555
const Array< int > & GetSlave() const
Definition nurbs.hpp:467
int GetNKV() const
Definition nurbs.hpp:491
void LoadBE(int i, const FiniteElement *BE) const
Definition nurbs.cpp:4066
Table * GetElementDofTable()
Definition nurbs.hpp:527
const Array< int > & GetMaster() const
Definition nurbs.hpp:465
void LoadFE(int i, const FiniteElement *FE) const
Definition nurbs.cpp:4045
void GetPatchDofs(const int patch, Array< int > &dofs)
Definition nurbs.cpp:3820
Table * GetBdrElementDofTable()
Definition nurbs.hpp:529
int GetNDof() const
Definition nurbs.hpp:501
int GetOrder() const
If all orders are identical, return that number. Otherwise, return NURBSFECollection::VariableOrder.
Definition nurbs.hpp:489
const Array< int > & GetOrders() const
Read-only access to the orders of all knot vectors.
Definition nurbs.hpp:486
Arbitrary order non-uniform rational B-splines (NURBS) finite elements.
Definition fe_coll.hpp:682
int GetOrder() const
Get the order of the NURBS collection: either a positive number, when using fixed order,...
Definition fe_coll.hpp:714
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:3480
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:25
int width
Dimension of the input / number of columns in the matrix.
Definition operator.hpp:28
int Height() const
Get the height (size of output) of the Operator. Synonym with NumRows().
Definition operator.hpp:66
int height
Dimension of the output / number of rows in the matrix.
Definition operator.hpp:27
int Width() const
Get the width (size of input) of the Operator. Synonym with NumCols().
Definition operator.hpp:72
Type
Enumeration defining IDs for some classes derived from Operator.
Definition operator.hpp:284
@ ANY_TYPE
ID for the base class Operator, i.e. any type.
Definition operator.hpp:285
@ MFEM_SPARSEMAT
ID for class SparseMatrix.
Definition operator.hpp:286
The ordering method used when the number of unknowns per mesh node (vector dimension) is bigger than ...
Definition fespace.hpp:30
Type
Ordering methods:
Definition fespace.hpp:34
A pair of objects.
Abstract parallel finite element space.
Definition pfespace.hpp:29
General product operator: x -> (A*B)(x) = A(B(x)).
Definition operator.hpp:797
A class that performs interpolation from an E-vector to quadrature point values and/or derivatives (Q...
const IntegrationRule * IntRule
Not owned.
const QuadratureSpace * qspace
Not owned.
Class representing the storage layout of a QuadratureFunction.
Definition qspace.hpp:120
Data type sparse matrix.
Definition sparsemat.hpp:51
virtual int GetRow(const int row, Array< int > &cols, Vector &srow) const
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.
virtual void Finalize(int skip_zeros=1)
Finalize the matrix initialization, switching the storage format from LIL to CSR.
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 Set(const int i, const int j, const real_t val)
int * GetJ()
Definition table.hpp:114
void AddConnections(int r, const int *c, int nc)
Definition table.cpp:104
int RowSize(int i) const
Definition table.hpp:108
void ShiftUpI()
Definition table.cpp:115
void Clear()
Definition table.cpp:381
void GetRow(int i, Array< int > &row) const
Return row i in array row (the Table must be finalized)
Definition table.cpp:187
void AddConnection(int r, int c)
Definition table.hpp:80
void MakeI(int nrows)
Next 7 methods are used together with the default constructor.
Definition table.cpp:81
int Size() const
Returns the number of TYPE I elements.
Definition table.hpp:92
int Size_of_connections() const
Definition table.hpp:98
void AddColumnsInRow(int r, int ncol)
Definition table.hpp:78
void MakeFromList(int nrows, const Array< Connection > &list)
Definition table.cpp:280
void MakeJ()
Definition table.cpp:91
int * GetI()
Definition table.hpp:113
void AddAColumnInRow(int r)
Definition table.hpp:77
The transpose of a given operator. Switches the roles of the methods Mult() and MultTranspose().
Definition operator.hpp:750
General triple product operator x -> A*B*C*x, with ownership of the factors.
Definition operator.hpp:861
Vector data type.
Definition vector.hpp:80
void Print(std::ostream &out=mfem::out, int width=8) const
Prints vector to stream out.
Definition vector.cpp:755
void SetSubVector(const Array< int > &dofs, const real_t value)
Set the entries listed in dofs to the given value.
Definition vector.cpp:604
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:670
void Load(std::istream **in, int np, int *dim)
Reads a vector from multiple files.
Definition vector.cpp:49
real_t Max() const
Returns the maximal element of the vector.
Definition vector.cpp:922
void SetSize(int s)
Resize the vector to size s.
Definition vector.hpp:538
real_t * GetData() const
Return a pointer to the beginning of the Vector data.
Definition vector.hpp:227
real_t Min() const
Returns the minimal element of the vector.
Definition vector.cpp:1212
void GetSubVector(const Array< int > &dofs, Vector &elemvect) const
Extract entries listed in dofs to the output Vector elemvect.
Definition vector.cpp:578
Vector & Add(const real_t a, const Vector &Va)
(*this) += a * Va
Definition vector.cpp:247
int dim
Definition ex24.cpp:53
int index(int i, int j, int nx, int ny)
Definition life.cpp:235
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 Swap(Array< T > &, Array< T > &)
Definition array.hpp:648
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:476
void Transpose(const Table &A, Table &At, int ncols_A_)
Transpose a Table.
Definition table.cpp:414
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:1345
BiLinear2DFiniteElement QuadrilateralFE
@ byNODES
NQPT x VDIM x NE (values) / NQPT x VDIM x DIM x NE (grads)
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:43
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:3719
void MarkDofs(const Array< int > &dofs, Array< int > &mark_array)
Definition fespace.cpp:506
ElementDofOrdering
Constants describing the possible orderings of the DOFs in one element.
Definition fespace.hpp:75
@ LEXICOGRAPHIC
Lexicographic ordering for tensor-product FiniteElements.
@ 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:47
real_t p(const Vector &x, real_t t)
RefCoord s[3]
bool operator<(const Data &d1, const Data &d2)
Definition nurbs_ex1.cpp:53
Defines the coarse-fine transformations of all fine elements.
Definition ncmesh.hpp:72
Array< Embedding > embeddings
Fine element positions in their parents.
Definition ncmesh.hpp:74
DenseTensor point_matrices[Geometry::NumGeom]
Definition ncmesh.hpp:78
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:51
int parent
Coarse Element index in the coarse mesh.
Definition ncmesh.hpp:53
unsigned matrix
Definition ncmesh.hpp:59
int index
Mesh number.
Definition ncmesh.hpp:190
Geometry::Type Geom() const
Definition ncmesh.hpp:195
Lists all edges/faces in the nonconforming mesh.
Definition ncmesh.hpp:230
Array< Slave > slaves
All MeshIds corresponding to slave faces.
Definition ncmesh.hpp:233
Array< Master > masters
All MeshIds corresponding to master faces.
Definition ncmesh.hpp:232
Nonconforming edge/face within a bigger edge/face.
Definition ncmesh.hpp:216
unsigned matrix
index into NCList::point_matrices[geom]
Definition ncmesh.hpp:218