-
Notifications
You must be signed in to change notification settings - Fork 739
/
dof_tools.h
2695 lines (2558 loc) · 120 KB
/
dof_tools.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// ---------------------------------------------------------------------
//
// Copyright (C) 1999 - 2021 by the deal.II authors
//
// This file is part of the deal.II library.
//
// The deal.II library is free software; you can use it, redistribute
// it, and/or modify it under the terms of the GNU Lesser General
// Public License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
// The full text of the license can be found in the file LICENSE.md at
// the top level directory of deal.II.
//
// ---------------------------------------------------------------------
#ifndef dealii_dof_tools_h
#define dealii_dof_tools_h
#include <deal.II/base/config.h>
#include <deal.II/base/exceptions.h>
#include <deal.II/base/index_set.h>
#include <deal.II/base/point.h>
#include <deal.II/dofs/dof_handler.h>
#include <deal.II/fe/component_mask.h>
#include <deal.II/hp/dof_handler.h>
#include <deal.II/lac/affine_constraints.h>
#include <map>
#include <ostream>
#include <set>
#include <vector>
DEAL_II_NAMESPACE_OPEN
// Forward declarations
#ifndef DOXYGEN
class BlockMask;
template <int dim, typename RangeNumberType>
class Function;
template <int dim, int spacedim>
class FiniteElement;
namespace hp
{
template <int dim, int spacedim>
class MappingCollection;
template <int dim, int spacedim>
class FECollection;
} // namespace hp
template <class MeshType>
class InterGridMap;
template <int dim, int spacedim>
class Mapping;
class SparsityPattern;
template <int dim, class T>
class Table;
template <typename Number>
class Vector;
namespace GridTools
{
template <typename CellIterator>
struct PeriodicFacePair;
}
namespace DoFTools
{
namespace internal
{
/*
* Default value of the face_has_flux_coupling parameter of
* make_flux_sparsity_pattern. Defined here (instead of using a default
* lambda in the parameter list) to avoid a bug in gcc where the same lambda
* gets defined multiple times.
*/
template <int dim, int spacedim>
inline bool
always_couple_on_faces(
const typename DoFHandler<dim, spacedim>::active_cell_iterator &,
const unsigned int)
{
return true;
}
} // namespace internal
} // namespace DoFTools
#endif
/**
* This is a collection of functions operating on, and manipulating the
* numbers of degrees of freedom. The documentation of the member functions
* will provide more information, but for functions that exist in multiple
* versions, there are sections in this global documentation stating some
* commonalities.
*
* <h3>Setting up sparsity patterns</h3>
*
* When assembling system matrices, the entries are usually of the form
* $a_{ij} = a(\phi_i, \phi_j)$, where $a$ is a bilinear functional, often an
* integral. When using sparse matrices, we therefore only need to reserve
* space for those $a_{ij}$ only, which are nonzero, which is the same as to
* say that the basis functions $\phi_i$ and $\phi_j$ have a nonempty
* intersection of their support. Since the support of basis functions is
* bound only on cells on which they are located or to which they are
* adjacent, to determine the sparsity pattern it is sufficient to loop over
* all cells and connect all basis functions on each cell with all other basis
* functions on that cell. There may be finite elements for which not all
* basis functions on a cell connect with each other, but no use of this case
* is made since no examples where this occurs are known to the author.
*
*
* <h3>DoF numberings on boundaries</h3>
*
* When projecting the traces of functions to the boundary or parts thereof,
* one needs to build matrices and vectors that act only on those degrees of
* freedom that are located on the boundary, rather than on all degrees of
* freedom. One could do that by simply building matrices in which the entries
* for all interior DoFs are zero, but such matrices are always very rank
* deficient and not very practical to work with.
*
* What is needed instead in this case is a numbering of the boundary degrees
* of freedom, i.e. we should enumerate all the degrees of freedom that are
* sitting on the boundary, and exclude all other (interior) degrees of
* freedom. The map_dof_to_boundary_indices() function does exactly this: it
* provides a vector with as many entries as there are degrees of freedom on
* the whole domain, with each entry being the number in the numbering of the
* boundary or numbers::invalid_dof_index if the dof is not on the
* boundary.
*
* With this vector, one can get, for any given degree of freedom, a unique
* number among those DoFs that sit on the boundary; or, if your DoF was
* interior to the domain, the result would be numbers::invalid_dof_index.
* We need this mapping, for example, to build the mass matrix on the boundary
* (for this, see make_boundary_sparsity_pattern() function, the corresponding
* section below, as well as the MatrixCreator namespace documentation).
*
* Actually, there are two map_dof_to_boundary_indices() functions, one
* producing a numbering for all boundary degrees of freedom and one producing
* a numbering for only parts of the boundary, namely those parts for which
* the boundary indicator is listed in a set of indicators given to the
* function. The latter case is needed if, for example, we would only want to
* project the boundary values for the Dirichlet part of the boundary. You
* then give the function a list of boundary indicators referring to Dirichlet
* parts on which the projection is to be performed. The parts of the boundary
* on which you want to project need not be contiguous; however, it is not
* guaranteed that the indices of each of the boundary parts are continuous,
* i.e. the indices of degrees of freedom on different parts may be
* intermixed.
*
* Degrees of freedom on the boundary but not on one of the specified boundary
* parts are given the index numbers::invalid_dof_index, as if they were in
* the interior. If no boundary indicator was given or if no face of a cell
* has a boundary indicator contained in the given list, the vector of new
* indices consists solely of numbers::invalid_dof_index.
*
* (As a side note, for corner cases: The question what a degree of freedom on
* the boundary is, is not so easy. It should really be a degree of freedom
* of which the respective basis function has nonzero values on the boundary.
* At least for Lagrange elements this definition is equal to the statement
* that the off-point, or what deal.II calls support_point, of the shape
* function, i.e. the point where the function assumes its nominal value (for
* Lagrange elements this is the point where it has the function value 1), is
* located on the boundary. We do not check this directly, the criterion is
* rather defined through the information the finite element class gives: the
* FiniteElement class defines the numbers of basis functions per vertex, per
* line, and so on and the basis functions are numbered after this
* information; a basis function is to be considered to be on the face of a
* cell (and thus on the boundary if the cell is at the boundary) according to
* it belonging to a vertex, line, etc but not to the interior of the cell.
* The finite element uses the same cell-wise numbering so that we can say
* that if a degree of freedom was numbered as one of the dofs on lines, we
* assume that it is located on the line. Where the off-point actually is, is
* a secret of the finite element (well, you can ask it, but we don't do it
* here) and not relevant in this context.)
*
*
* <h3>Setting up sparsity patterns for boundary matrices</h3>
*
* In some cases, one wants to only work with DoFs that sit on the boundary.
* One application is, for example, if rather than interpolating non-
* homogeneous boundary values, one would like to project them. For this, we
* need two things: a way to identify nodes that are located on (parts of) the
* boundary, and a way to build matrices out of only degrees of freedom that
* are on the boundary (i.e. much smaller matrices, in which we do not even
* build the large zero block that stems from the fact that most degrees of
* freedom have no support on the boundary of the domain). The first of these
* tasks is done by the map_dof_to_boundary_indices() function (described
* above).
*
* The second part requires us first to build a sparsity pattern for the
* couplings between boundary nodes, and then to actually build the components
* of this matrix. While actually computing the entries of these small
* boundary matrices is discussed in the MatrixCreator namespace, the creation
* of the sparsity pattern is done by the create_boundary_sparsity_pattern()
* function. For its work, it needs to have a numbering of all those degrees
* of freedom that are on those parts of the boundary that we are interested
* in. You can get this from the map_dof_to_boundary_indices() function. It
* then builds the sparsity pattern corresponding to integrals like
* $\int_\Gamma \varphi_{b2d(i)} \varphi_{b2d(j)} dx$, where $i$ and $j$ are
* indices into the matrix, and $b2d(i)$ is the global DoF number of a degree
* of freedom sitting on a boundary (i.e., $b2d$ is the inverse of the mapping
* returned by map_dof_to_boundary_indices() function).
*
*
* @ingroup dofs
*/
namespace DoFTools
{
/**
* The flags used in tables by certain <tt>make_*_pattern</tt> functions to
* describe whether two components of the solution couple in the bilinear
* forms corresponding to cell or face terms. An example of using these
* flags is shown in the introduction of step-46.
*
* In the descriptions of the individual elements below, remember that these
* flags are used as elements of tables of size FiniteElement::n_components
* times FiniteElement::n_components where each element indicates whether
* two components do or do not couple.
*/
enum Coupling
{
/**
* Two components do not couple.
*/
none,
/**
* Two components do couple.
*/
always,
/**
* Two components couple only if their shape functions are both nonzero on
* a given face. This flag is only used when computing integrals over
* faces of cells, e.g., in DoFTools::make_flux_sparsity_pattern().
* Use Coupling::always in general cases where gradients etc. occur on face
* integrals.
*/
nonzero
};
/**
* @name DoF couplings
* @{
*/
/**
* Map a coupling table from the user friendly organization by components to
* the organization by blocks.
*
* The return vector will be initialized to the correct length inside this
* function.
*/
template <int dim, int spacedim>
void
convert_couplings_to_blocks(const DoFHandler<dim, spacedim> &dof_handler,
const Table<2, Coupling> &table_by_component,
std::vector<Table<2, Coupling>> &tables_by_block);
/**
* Given a finite element and a table how the vector components of it couple
* with each other, compute and return a table that describes how the
* individual shape functions couple with each other.
*/
template <int dim, int spacedim>
Table<2, Coupling>
dof_couplings_from_component_couplings(
const FiniteElement<dim, spacedim> &fe,
const Table<2, Coupling> & component_couplings);
/**
* Same function as above for a collection of finite elements, returning a
* collection of tables.
*
* The function currently treats DoFTools::Couplings::nonzero the same as
* DoFTools::Couplings::always .
*/
template <int dim, int spacedim>
std::vector<Table<2, Coupling>>
dof_couplings_from_component_couplings(
const hp::FECollection<dim, spacedim> &fe,
const Table<2, Coupling> & component_couplings);
/**
* @}
*/
/**
* @name Sparsity pattern generation
* @{
*/
/**
* Compute which entries of a matrix built on the given @p dof_handler may
* possibly be nonzero, and create a sparsity pattern object that represents
* these nonzero locations.
*
* This function computes the possible positions of non-zero entries in the
* global system matrix by <i>simulating</i> which entries one would write
* to during the actual assembly of a matrix. For this, the function assumes
* that each finite element basis function is non-zero on a cell only if its
* degree of freedom is associated with the interior, a face, an edge or a
* vertex of this cell. As a result, a matrix entry $A_{ij}$ that is
* computed from two basis functions $\varphi_i$ and $\varphi_j$ with
* (global) indices $i$ and $j$ (for example, using a bilinear form
* $A_{ij}=a(\varphi_i,\varphi_j)$) can be non-zero only if these shape
* functions correspond to degrees of freedom that are defined on at least
* one common cell. Therefore, this function just loops over all cells,
* figures out the global indices of all degrees of freedom, and presumes
* that all matrix entries that couple any of these indices will result in a
* nonzero matrix entry. These will then be added to the sparsity pattern.
* As this process of generating the sparsity pattern does not take into
* account the equation to be solved later on, the resulting sparsity
* pattern is symmetric.
*
* This algorithm makes no distinction between shape functions on each cell,
* i.e., it simply couples all degrees of freedom on a cell with all other
* degrees of freedom on a cell. This is often the case, and always a safe
* assumption. However, if you know something about the structure of your
* operator and that it does not couple certain shape functions with certain
* test functions, then you can get a sparser sparsity pattern by calling a
* variant of the current function described below that allows to specify
* which vector components couple with which other vector components.
*
* The method described above lives on the assumption that coupling between
* degrees of freedom only happens if shape functions overlap on at least
* one cell. This is the case with most usual finite element formulations
* involving conforming elements. However, for formulations such as the
* Discontinuous Galerkin finite element method, the bilinear form contains
* terms on interfaces between cells that couple shape functions that live
* on one cell with shape functions that live on a neighboring cell. The
* current function would not see these couplings, and would consequently
* not allocate entries in the sparsity pattern. You would then get into
* trouble during matrix assembly because you try to write into matrix
* entries for which no space has been allocated in the sparsity pattern.
* This can be avoided by calling the DoFTools::make_flux_sparsity_pattern()
* function instead, which takes into account coupling between degrees of
* freedom on neighboring cells.
*
* There are other situations where bilinear forms contain non-local terms,
* for example in treating integral equations. These require different
* methods for building the sparsity patterns that depend on the exact
* formulation of the problem. You will have to do this yourself then.
*
* @param[in] dof_handler The DoFHandler object that describes which degrees
* of freedom live on which cells.
*
* @param[out] sparsity_pattern The sparsity pattern to be filled with
* entries.
*
* @param[in] constraints The process for generating entries described above
* is purely local to each cell. Consequently, the sparsity pattern does
* not provide for matrix entries that will only be written into during
* the elimination of hanging nodes or other constraints. They have to be
* taken care of by a subsequent call to AffineConstraints::condense().
* Alternatively, the constraints on degrees of freedom can already be
* taken into account at the time of creating the sparsity pattern. For
* this, pass the AffineConstraints object as the third argument to the
* current function. No call to AffineConstraints::condense() is then
* necessary. This process is explained in step-6, step-27, and other
* tutorial programs.
*
* @param[in] keep_constrained_dofs In case the constraints are already
* taken care of in this function by passing in a AffineConstraints object,
* it is possible to abandon some off-diagonal entries in the sparsity
* pattern if these entries will also not be written into during the actual
* assembly of the matrix this sparsity pattern later serves. Specifically,
* when using an assembly method that uses
* AffineConstraints::distribute_local_to_global(), no entries will ever be
* written into those matrix rows or columns that correspond to constrained
* degrees of freedom. In such cases, you can set the argument @p
* keep_constrained_dofs to @p false to avoid allocating these entries in
* the sparsity pattern.
*
* @param[in] subdomain_id If specified, the sparsity pattern is built only
* on cells that have a subdomain_id equal to the given argument. This is
* useful in parallel contexts where the matrix and sparsity pattern (for
* example a TrilinosWrappers::SparsityPattern) may be distributed and not
* every MPI process needs to build the entire sparsity pattern; in that
* case, it is sufficient if every process only builds that part of the
* sparsity pattern that corresponds to the subdomain_id for which it is
* responsible. This feature is used in step-32. (This argument is not
* usually needed for objects of type parallel::distributed::Triangulation
* because the current function only loops over locally owned cells anyway;
* thus, this argument typically only makes sense if you want to use the
* subdomain_id for anything other than indicating which processor owns a
* cell, for example which geometric component of the domain a cell belongs
* to.)
*
* @note The actual type of the sparsity pattern may be SparsityPattern,
* DynamicSparsityPattern, BlockSparsityPattern,
* BlockDynamicSparsityPattern, or any other class that satisfies similar
* requirements. It is assumed that the size of the sparsity pattern matches
* the number of degrees of freedom and that enough unused nonzero entries
* are left to fill the sparsity pattern if the sparsity pattern is of
* "static" kind (see
* @ref Sparsity
* for more information on what this means). The nonzero entries generated
* by this function are added to possible previous content of the object,
* i.e., previously added entries are not removed.
*
* @note If the sparsity pattern is represented by an object of type
* SparsityPattern (as opposed to, for example, DynamicSparsityPattern), you
* need to remember using SparsityPattern::compress() after generating the
* pattern.
*
* @ingroup constraints
*/
template <int dim,
int spacedim,
typename SparsityPatternType,
typename number = double>
void
make_sparsity_pattern(
const DoFHandler<dim, spacedim> &dof_handler,
SparsityPatternType & sparsity_pattern,
const AffineConstraints<number> &constraints = AffineConstraints<number>(),
const bool keep_constrained_dofs = true,
const types::subdomain_id subdomain_id = numbers::invalid_subdomain_id);
/**
* Compute which entries of a matrix built on the given @p dof_handler may
* possibly be nonzero, and create a sparsity pattern object that represents
* these nonzero locations.
*
* This function is a simple variation on the previous
* make_sparsity_pattern() function (see there for a description of all of
* the common arguments), but it provides functionality for vector finite
* elements that allows to be more specific about which variables couple in
* which equation.
*
* For example, if you wanted to solve the Stokes equations,
*
* @f{align*}{
* -\Delta \mathbf u + \nabla p &= 0,\\ \text{div}\ u &= 0
* @f}
*
* in two space dimensions, using stable Q2/Q1 mixed elements (using the
* FESystem class), then you don't want all degrees of freedom to couple in
* each equation. More specifically, in the first equation, only $u_x$ and
* $p$ appear; in the second equation, only $u_y$ and $p$ appear; and in the
* third equation, only $u_x$ and $u_y$ appear. (Note that this discussion
* only talks about vector components of the solution variable and the
* different equation, and has nothing to do with degrees of freedom, or in
* fact with any kind of discretization.) We can describe this by the
* following pattern of "couplings":
*
* @f[
* \left[
* \begin{array}{ccc}
* 1 & 0 & 1 \\
* 0 & 1 & 1 \\
* 1 & 1 & 0
* \end{array}
* \right]
* @f]
*
* where "1" indicates that two variables (i.e., vector components of the
* FESystem) couple in the respective equation, and a "0" means no coupling.
* These zeros imply that upon discretization via a standard finite element
* formulation, we will not write entries into the matrix that, for example,
* couple pressure test functions with pressure shape functions (and similar
* for the other zeros above). It is then a waste to allocate memory for
* these entries in the matrix and the sparsity pattern, and you can avoid
* this by creating a mask such as the one above that describes this to the
* (current) function that computes the sparsity pattern. As stated above,
* the mask shown above refers to components of the composed FESystem,
* rather than to degrees of freedom or shape functions.
*
* This function is designed to accept a coupling pattern, like the one
* shown above, through the @p couplings parameter, which contains values of
* type #Coupling. It builds the matrix structure just like the previous
* function, but does not create matrix elements if not specified by the
* coupling pattern. If the couplings are symmetric, then so will be the
* resulting sparsity pattern.
*
* There is a complication if some or all of the shape functions of the
* finite element in use are non-zero in more than one component (in deal.II
* speak: they are
* @ref GlossPrimitive "non-primitive finite elements").
* In this case, the coupling element corresponding to the first non-zero
* component is taken and additional ones for this component are ignored.
*
* @ingroup constraints
*/
template <int dim,
int spacedim,
typename SparsityPatternType,
typename number = double>
void
make_sparsity_pattern(
const DoFHandler<dim, spacedim> &dof_handler,
const Table<2, Coupling> & coupling,
SparsityPatternType & sparsity_pattern,
const AffineConstraints<number> &constraints = AffineConstraints<number>(),
const bool keep_constrained_dofs = true,
const types::subdomain_id subdomain_id = numbers::invalid_subdomain_id);
/**
* Construct a sparsity pattern that allows coupling degrees of freedom on
* two different but related meshes.
*
* The idea is that if the two given DoFHandler objects correspond to two
* different meshes (and potentially to different finite elements used on
* these cells), but that if the two triangulations they are based on are
* derived from the same coarse mesh through hierarchical refinement, then
* one may set up a problem where one would like to test shape functions
* from one mesh against the shape functions from another mesh. In
* particular, this means that shape functions from a cell on the first mesh
* are tested against those on the second cell that are located on the
* corresponding cell; this correspondence is something that the
* IntergridMap class can determine.
*
* This function then constructs a sparsity pattern for which the degrees of
* freedom that represent the rows come from the first given DoFHandler,
* whereas the ones that correspond to columns come from the second
* DoFHandler.
*/
template <int dim, int spacedim, typename SparsityPatternType>
void
make_sparsity_pattern(const DoFHandler<dim, spacedim> &dof_row,
const DoFHandler<dim, spacedim> &dof_col,
SparsityPatternType & sparsity);
/**
* Compute which entries of a matrix built on the given @p dof_handler may
* possibly be nonzero, and create a sparsity pattern object that represents
* these nonzero locations. This function is a variation of the
* make_sparsity_pattern() functions above in that it assumes that the
* bilinear form you want to use to generate the matrix also contains terms
* that integrate over the <i>faces</i> between cells (i.e., it contains
* "fluxes" between cells, explaining the name of the function).
*
* This function is useful for Discontinuous Galerkin methods where the
* standard make_sparsity_pattern() function would only create nonzero
* entries for all degrees of freedom on one cell coupling to all other
* degrees of freedom on the same cell; however, in DG methods, all or some
* degrees of freedom on each cell also couple to the degrees of freedom on
* other cells connected to the current one by a common face. The current
* function also creates the nonzero entries in the matrix resulting from
* these additional couplings. In other words, this function computes a
* strict super-set of nonzero entries compared to the work done by
* make_sparsity_pattern().
*
* @param[in] dof_handler The DoFHandler object that describes which degrees
* of freedom live on which cells.
*
* @param[out] sparsity_pattern The sparsity pattern to be filled with
* entries.
*
* @note The actual type of the sparsity pattern may be SparsityPattern,
* DynamicSparsityPattern, BlockSparsityPattern,
* BlockDynamicSparsityPattern, or any other class that satisfies similar
* requirements. It is assumed that the size of the sparsity pattern matches
* the number of degrees of freedom and that enough unused nonzero entries
* are left to fill the sparsity pattern if the sparsity pattern is of
* "static" kind (see
* @ref Sparsity
* for more information on what this means). The nonzero entries generated
* by this function are added to possible previous content of the object,
* i.e., previously added entries are not removed.
*
* @note If the sparsity pattern is represented by an object of type
* SparsityPattern (as opposed to, for example, DynamicSparsityPattern), you
* need to remember using SparsityPattern::compress() after generating the
* pattern.
*
* @ingroup constraints
*/
template <int dim, int spacedim, typename SparsityPatternType>
void
make_flux_sparsity_pattern(const DoFHandler<dim, spacedim> &dof_handler,
SparsityPatternType & sparsity_pattern);
/**
* This function does essentially the same as the other
* make_flux_sparsity_pattern() function but allows the specification of a
* number of additional arguments. These carry the same meaning as discussed
* in the first make_sparsity_pattern() function above.
*
* @ingroup constraints
*/
template <int dim,
int spacedim,
typename SparsityPatternType,
typename number>
void
make_flux_sparsity_pattern(
const DoFHandler<dim, spacedim> &dof_handler,
SparsityPatternType & sparsity_pattern,
const AffineConstraints<number> &constraints,
const bool keep_constrained_dofs = true,
const types::subdomain_id subdomain_id = numbers::invalid_subdomain_id);
/**
* This function does essentially the same as the other
* make_flux_sparsity_pattern() function but allows the specification of
* coupling matrices that state which components of the solution variable
* couple in each of the equations you are discretizing. This works in
* complete analogy as discussed in the second make_sparsity_pattern()
* function above.
*
* In fact, this function takes two such masks, one describing which
* variables couple with each other in the cell integrals that make up your
* bilinear form, and which variables couple with each other in the face
* integrals. If you passed masks consisting of only 1s to both of these,
* then you would get the same sparsity pattern as if you had called the
* first of the make_sparsity_pattern() functions above. By setting some of
* the entries of these masks to zeros, you can get a sparser sparsity
* pattern.
*
* @ingroup constraints
*/
template <int dim, int spacedim, typename SparsityPatternType>
void
make_flux_sparsity_pattern(
const DoFHandler<dim, spacedim> &dof,
SparsityPatternType & sparsity,
const Table<2, Coupling> & cell_integrals_mask,
const Table<2, Coupling> & face_integrals_mask,
const types::subdomain_id subdomain_id = numbers::invalid_subdomain_id);
/**
* This function does essentially the same as the previous
* make_flux_sparsity_pattern() function but allows the application of an
* AffineConstraints object. This is useful in the case where some
* components of a finite element are continuous and some discontinuous,
* allowing constraints to be imposed on the continuous part while also
* building the flux terms needed for the discontinuous part.
*
* The optional @p face_has_flux_coupling can be used to specify on which
* faces flux couplings occur. This allows for creating a sparser pattern when
* using a bilinear form where flux terms only appear on a subset of the faces
* in the triangulation. By default flux couplings are added over all internal
* faces. @p face_has_flux_coupling should be a function that takes an
* active_cell_iterator and a face index and should return true if there is a
* flux coupling over the face. When using the ::dealii::DoFHandler we could,
* for example, use
*
* @code
* auto face_has_flux_coupling =
* [](const typename DoFHandler<dim>::active_cell_iterator &cell,
* const unsigned int face_index) {
* const Point<dim> &face_center = cell->face(face_index)->center();
* return 0 < face_center[0];
* };
* @endcode
*/
template <int dim,
int spacedim,
typename SparsityPatternType,
typename number>
void
make_flux_sparsity_pattern(
const DoFHandler<dim, spacedim> &dof,
SparsityPatternType & sparsity,
const AffineConstraints<number> &constraints,
const bool keep_constrained_dofs,
const Table<2, Coupling> & couplings,
const Table<2, Coupling> & face_couplings,
const types::subdomain_id subdomain_id,
const std::function<
bool(const typename DoFHandler<dim, spacedim>::active_cell_iterator &,
const unsigned int)> &face_has_flux_coupling =
&internal::always_couple_on_faces<dim, spacedim>);
/**
* Create the sparsity pattern for boundary matrices. See the general
* documentation of this class for more information.
*
* The function does essentially what the other make_sparsity_pattern()
* functions do, but assumes that the bilinear form that is used to build
* the matrix does not consist of domain integrals, but only of integrals
* over the boundary of the domain.
*/
template <int dim, int spacedim, typename SparsityPatternType>
void
make_boundary_sparsity_pattern(
const DoFHandler<dim, spacedim> & dof,
const std::vector<types::global_dof_index> &dof_to_boundary_mapping,
SparsityPatternType & sparsity_pattern);
/**
* This function is a variation of the previous
* make_boundary_sparsity_pattern() function in which we assume that the
* boundary integrals that will give rise to the matrix extends only over
* those parts of the boundary whose boundary indicators are listed in the
* @p boundary_ids argument to this function.
*
* This function could have been written by passing a @p set of boundary_id
* numbers. However, most of the functions throughout deal.II dealing with
* boundary indicators take a mapping of boundary indicators and the
* corresponding boundary function, i.e., a std::map<types::boundary_id, const
* Function<spacedim,number>*> argument. Correspondingly, this function does
* the same, though the actual boundary function is ignored here.
* (Consequently, if you don't have any such boundary functions, just create a
* map with the boundary indicators you want and set the function pointers to
* null pointers).
*/
template <int dim,
int spacedim,
typename SparsityPatternType,
typename number>
void
make_boundary_sparsity_pattern(
const DoFHandler<dim, spacedim> &dof,
const std::map<types::boundary_id, const Function<spacedim, number> *>
& boundary_ids,
const std::vector<types::global_dof_index> &dof_to_boundary_mapping,
SparsityPatternType & sparsity);
/**
* @}
*/
/**
* @name Hanging nodes and other constraints
* @{
*/
/**
* Compute the constraints resulting from the presence of hanging nodes.
* Hanging nodes are best explained using a small picture:
*
* @image html hanging_nodes.png
*
* In order to make a finite element function globally continuous, we have
* to make sure that the dark red nodes have values that are compatible with
* the adjacent yellow nodes, so that the function has no jump when coming
* from the small cells to the large one at the top right. We therefore have
* to add conditions that constrain those "hanging nodes".
*
* The object into which these are inserted is later used to condense the
* global system matrix and right hand side, and to extend the solution
* vectors from the true degrees of freedom also to the constraint nodes.
* This function is explained in detail in the
* @ref step_6 "step-6"
* tutorial program and is used in almost all following programs as well.
*
* This function does not clear the AffineConstraints object before use, in
* order to allow adding constraints from different sources to the same
* object. You therefore need to make sure it contains only constraints you
* still want; otherwise call the AffineConstraints::clear() function.
* Since this function does not check if it would add cycles in
* @p constraints, it is recommended to call this function prior to other
* functions that constrain DoFs with respect to others such as
* make_periodicity_constraints().
* This function does not close the object since you may want to
* enter other constraints later on yourself.
*
* Using a DoFHandler with hp-capabilities, we consider constraints due to
* different finite elements used on two sides of a face between cells as
* hanging nodes as well. In other words, in hp-mode, this function computes
* all constraints due to differing mesh sizes (h) or polynomial degrees (p)
* between adjacent cells.
*
* @ingroup constraints
*/
template <int dim, int spacedim, typename number>
void
make_hanging_node_constraints(const DoFHandler<dim, spacedim> &dof_handler,
AffineConstraints<number> & constraints);
/**
* This function is used when different variables in a problem are
* discretized on different grids, where one grid is strictly coarser than
* the other. An example are optimization problems where the control
* variable is often discretized on a coarser mesh than the state variable.
*
* The function's result can be stated as follows mathematically: Let ${\cal
* T}_0$ and ${\cal T}_1$ be two meshes where ${\cal T}_1$ results from
* ${\cal T}_0$ strictly by refining or leaving alone the cells of ${\cal
* T}_0$. Using the same finite element on both, there are function spaces
* ${\cal V}_0$ and ${\cal V}_1$ associated with these meshes. Then every
* function $v_0 \in {\cal V}_0$ can of course also be represented exactly
* in ${\cal V}_1$ since by construction ${\cal V}_0 \subset {\cal V}_1$.
* However, not every function in ${\cal V}_1$ can be expressed as a linear
* combination of the shape functions of ${\cal V}_0$. The functions that
* can be represented lie in a homogeneous subspace of ${\cal V}_1$ (namely,
* ${\cal V}_0$, of course) and this subspace can be represented by a linear
* constraint of the form $CV=0$ where $V$ is the vector of nodal values of
* functions $v\in {\cal V}_1$. In other words, every function $v_h=\sum_j
* V_j \varphi_j^{(1)} \in {\cal V}_1$ that also satisfies $v_h\in {\cal
* V}_0$ automatically satisfies $CV=0$. This function computes the matrix
* $C$ in the form of a AffineConstraints object.
*
* The construction of these constraints is done as follows: for each of the
* degrees of freedom (i.e. shape functions) on the coarse grid, we compute
* its representation on the fine grid, i.e. how the linear combination of
* shape functions on the fine grid looks like that resembles the shape
* function on the coarse grid. From this information, we can then compute
* the constraints which have to hold if a solution of a linear equation on
* the fine grid shall be representable on the coarse grid. The exact
* algorithm how these constraints can be computed is rather complicated and
* is best understood by reading the source code, which contains many
* comments.
*
* The use of this function is as follows: it accepts as parameters two DoF
* Handlers, the first of which refers to the coarse grid and the second of
* which is the fine grid. On both, a finite element is represented by the
* DoF handler objects, which will usually have several vector components,
* which may belong to different base elements. The second and fourth
* parameter of this function therefore state which vector component on the
* coarse grid shall be used to restrict the stated component on the fine
* grid. The finite element used for the respective components on the two
* grids needs to be the same. An example may clarify this: consider an
* optimization problem with controls $q$ discretized on a coarse mesh and a
* state variable $u$ (and corresponding Lagrange multiplier $\lambda$)
* discretized on the fine mesh. These are discretized using piecewise
* constant discontinuous, continuous linear, and continuous linear
* elements, respectively. Only the parameter $q$ is represented on the
* coarse grid, thus the DoFHandler object on the coarse grid represents
* only one variable, discretized using piecewise constant discontinuous
* elements. Then, the parameter denoting the vector component on the coarse
* grid would be zero (the only possible choice, since the variable on the
* coarse grid is scalar). If the ordering of variables in the fine mesh
* FESystem is $u, q, \lambda$, then the fourth argument of the function
* corresponding to the vector component would be one (corresponding to the
* variable $q$; zero would be $u$, two would be $\lambda$).
*
* The function also requires an object of type IntergridMap representing
* how to get from the coarse mesh cells to the corresponding cells on the
* fine mesh. This could in principle be generated by the function itself
* from the two DoFHandler objects, but since it is probably available
* anyway in programs that use different meshes, the function simply takes
* it as an argument.
*
* The computed constraints are entered into a variable of type
* AffineConstraints; previous contents are not deleted.
*/
template <int dim, int spacedim>
void
compute_intergrid_constraints(
const DoFHandler<dim, spacedim> & coarse_grid,
const unsigned int coarse_component,
const DoFHandler<dim, spacedim> & fine_grid,
const unsigned int fine_component,
const InterGridMap<DoFHandler<dim, spacedim>> &coarse_to_fine_grid_map,
AffineConstraints<double> & constraints);
/**
* This function generates a matrix such that when a vector of data with as
* many elements as there are degrees of freedom of this component on the
* coarse grid is multiplied to this matrix, we obtain a vector with as many
* elements as there are global degrees of freedom on the fine grid. All the
* elements of the other vector components of the finite element fields on
* the fine grid are not touched.
*
* Triangulation of the fine grid can be distributed. When called in
* parallel, each process has to have a copy of the coarse grid. In this
* case, function returns transfer representation for a set of locally owned
* cells.
*
* The output of this function is a compressed format that can be used to
* construct corresponding sparse transfer matrix.
*/
template <int dim, int spacedim>
void
compute_intergrid_transfer_representation(
const DoFHandler<dim, spacedim> & coarse_grid,
const unsigned int coarse_component,
const DoFHandler<dim, spacedim> & fine_grid,
const unsigned int fine_component,
const InterGridMap<DoFHandler<dim, spacedim>> &coarse_to_fine_grid_map,
std::vector<std::map<types::global_dof_index, float>>
&transfer_representation);
/**
* @}
*/
/**
* @name Periodic boundary conditions
* @{
*/
/**
* Insert the (algebraic) constraints due to periodic boundary conditions
* into an AffineConstraints object @p constraints.
*
* Given a pair of not necessarily active boundary faces @p face_1 and @p
* face_2, this functions constrains all DoFs associated with the boundary
* described by @p face_1 to the respective DoFs of the boundary described
* by @p face_2. More precisely:
*
* If @p face_1 and @p face_2 are both active faces it adds the DoFs of @p
* face_1 to the list of constrained DoFs in @p constraints and adds
* entries to constrain them to the corresponding values of the DoFs on @p
* face_2. This happens on a purely algebraic level, meaning, the global DoF
* with (local face) index <tt>i</tt> on @p face_1 gets constraint to the
* DoF with (local face) index <tt>i</tt> on @p face_2 (possibly corrected
* for orientation, see below).
*
* Otherwise, if @p face_1 and @p face_2 are not active faces, this function
* loops recursively over the children of @p face_1 and @p face_2. If only
* one of the two faces is active, then we recursively iterate over the
* children of the non-active ones and make sure that the solution function
* on the refined side equals that on the non-refined face in much the same
* way as we enforce hanging node constraints at places where differently
* refined cells come together. (However, unlike hanging nodes, we do not
* enforce the requirement that there be only a difference of one refinement
* level between the two sides of the domain you would like to be periodic).
*
* This routine only constrains DoFs that are not already constrained. If
* this routine encounters a DoF that already is constrained (for instance
* by Dirichlet boundary conditions), the old setting of the constraint
* (dofs the entry is constrained to, inhomogeneities) is kept and nothing
* happens.
*
* The flags in the @p component_mask (see
* @ref GlossComponentMask)
* denote which components of the finite element space shall be constrained
* with periodic boundary conditions. If it is left as specified by the
* default value all components are constrained. If it is different from the
* default value, it is assumed that the number of entries equals the number
* of components of the finite element. This can be used to enforce
* periodicity in only one variable in a system of equations.
*
* @p face_orientation, @p face_flip and @p face_rotation describe an
* orientation that should be applied to @p face_1 prior to matching and
* constraining DoFs. This has nothing to do with the actual orientation of
* the given faces in their respective cells (which for boundary faces is
* always the default) but instead how you want to see periodicity to be
* enforced. For example, by using these flags, you can enforce a condition
* of the kind $u(0,y)=u(1,1-y)$ (i.e., a Moebius band) or in 3d a twisted
* torus. More precisely, these flags match local face DoF indices in the
* following manner:
*
* In 2d: <tt>face_orientation</tt> must always be <tt>true</tt>,
* <tt>face_rotation</tt> is always <tt>false</tt>, and face_flip has the
* meaning of <tt>line_flip</tt>; this implies e.g. for <tt>Q1</tt>:
*
* @code
*
* face_orientation = true, face_flip = false, face_rotation = false:
*
* face1: face2:
*
* 1 1
* | <--> |
* 0 0
*
* Resulting constraints: 0 <-> 0, 1 <-> 1
*
* (Numbers denote local face DoF indices.)
*
*
* face_orientation = true, face_flip = true, face_rotation = false:
*
* face1: face2:
*
* 0 1
* | <--> |
* 1 0
*
* Resulting constraints: 1 <-> 0, 0 <-> 1
* @endcode
*
* And similarly for the case of Q1 in 3d:
*
* @code
*
* face_orientation = true, face_flip = false, face_rotation = false:
*
* face1: face2:
*
* 2 - 3 2 - 3
* | | <--> | |
* 0 - 1 0 - 1
*
* Resulting constraints: 0 <-> 0, 1 <-> 1, 2 <-> 2, 3 <-> 3
*
* (Numbers denote local face DoF indices.)
*
*
* face_orientation = false, face_flip = false, face_rotation = false:
*
* face1: face2:
*
* 1 - 3 2 - 3
* | | <--> | |
* 0 - 2 0 - 1
*
* Resulting constraints: 0 <-> 0, 2 <-> 1, 1 <-> 2, 3 <-> 3
*
*
* face_orientation = true, face_flip = true, face_rotation = false:
*
* face1: face2:
*
* 1 - 0 2 - 3
* | | <--> | |
* 3 - 2 0 - 1