KASKADE 7 development version
partitionedspace.hh
Go to the documentation of this file.
1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2/* */
3/* This file is part of the library KASKADE 7 */
4/* https://www.zib.de/research/projects/kaskade7-finite-element-toolbox */
5/* */
6/* Copyright (C) 2023-2023 Zuse Institute Berlin */
7/* */
8/* KASKADE 7 is distributed under the terms of the ZIB Academic License. */
9/* see $KASKADE/academic.txt */
10/* */
11/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
12
13#ifndef PARTITIONEDSPACE_HH
14#define PARTITIONEDSPACE_HH
15
16#include <algorithm>
17#include <functional>
18#include <map>
19#include <vector>
20
21#include <boost/compressed_pair.hpp>
22#include <boost/iterator/transform_iterator.hpp>
23
24#include "fem/firstless.hh"
25#include "fem/gridBasics.hh"
27#include "fem/lagrangespace.hh"
28#include "fem/views.hh"
29
30namespace Kaskade
31{
32 // forward declarations
33 template <class,class> class ContinuousLagrangeBoundaryMapper;
34
35 namespace PartitionedSpaceDetail
36 {
37 // an empty class for assotiating no additional data with localized ansatz functions
38 struct Empty {};
39
40 // --------------------------------------------------------------------------------------------
41
45 template <class Pair>
47 {
48 typedef typename Pair::first_const_reference result_type;
49
50 typename Pair::first_const_reference operator()(Pair const& pair) const
51 {
52 return pair.first();
53 }
54 };
55
56 // Overload here as compressed pair's entries are accessed by method call.
57 struct FirstLess
58 {
59 template <class Data>
60 bool operator()(boost::compressed_pair<size_t,Data> const& a, boost::compressed_pair<size_t,Data> const& b)
61 {
62 return a.first() < b.first();
63 }
64 };
65
66 // --------------------------------------------------------------------------------------------
67
68 // Dummy tagger assigning the same tag to all cells, leading to a globally continuous space.
70 {
71 template <class Cell>
72 int operator()(Cell const& cell) const
73 {
74 auto x = cell.geometry().center();
75 return x[0]+x[1] < 1? 0: 1;
76 }
77 };
78
79 // --------------------------------------------------------------------------------------------
80
88 inline bool onSimplexFace(int dim, int codim, int id, int localFaceId)
89 {
90 assert(dim<=3);
91
92 if(codim==0) return false; // cell is never part of a face
93 if(codim==1) return id==localFaceId; // ... face only if it is the same face
94
95 if(dim==2)
96 {
97 if(localFaceId==0) return (id==0 || id==1);
98 if(localFaceId==1) return (id==0 || id==2);
99 if(localFaceId==2) return (id==2 || id==1);
100 }
101 else if(dim==3)
102 {
103 // edges
104 if(codim==2)
105 {
106 if(localFaceId==0) return (id>=0 && id<3);
107 if(localFaceId==1) return (id==0 || id==3 || id==4);
108 if(localFaceId==2) return (id==1 || id==3 || id==5);
109 if(localFaceId==3) return (id==2 || id==4 || id==5);
110 }
111 // vertices
112 if(codim==3)
113 {
114 if(localFaceId==0) return (id>=0 && id<3);
115 if(localFaceId==1) return (id==0 || id==1 || id==3);
116 if(localFaceId==2) return (id==0 || id==2 || id==3);
117 if(localFaceId==3) return (id==1 || id==2 || id==3);
118 }
119 return false;
120 }
121
122 return false; // never get here
123 }
124
125 static constexpr int defaultIndex = -94279;
126
127 template <class Face>
128 int getBoundaryId(Face const& face, std::vector<int> const& boundaryIds)
129 {
130 size_t boundarySegmentIndex = face.boundarySegmentIndex();
131 if(boundarySegmentIndex < boundaryIds.size()) return boundaryIds[boundarySegmentIndex];
132 return defaultIndex;
133 }
134
135 inline bool usedId(int id, std::vector<int> const& usedIds)
136 {
137 if (id==defaultIndex) return false;
138 return std::find(usedIds.begin(), usedIds.end(), id) != usedIds.end();
139 }
140
141 template <class Face>
142 inline bool considerFace(Face const& face, std::vector<int> const& boundaryIds, std::vector<int> const& usedIds)
143 {
144 if(boundaryIds.empty()) return face.boundary();
145 return (face.boundary() && usedId(getBoundaryId(face,boundaryIds),usedIds));
146 }
147
149 {
152 RestrictToBoundary(std::vector<int> const& boundaryIds_, std::vector<int> const& usedIds_) : boundaryIds(boundaryIds_), usedIds(usedIds_)
153 {}
154
155 template <class Data, class GridView, class Cell>
156 void treatBoundary(Data& data, GridView const& gridView, Cell const& cell, int codim, int subentity) const
157 {
158 // ignore shape functions that are associated with codim-0 entities
159 assert(cell.type().isSimplex());
160 if(codim > 0)
161 for(auto const& face: intersections(gridView,cell))
162 // only consider boundary faces
164 && onSimplexFace(GridView::dimension,codim,subentity,face.indexInInside()))
165 data.first() = true;
166 }
167
168 std::vector<int> const boundaryIds;
169 std::vector<int> const usedIds;
170 };
171
172 template <class Policy>
173 constexpr bool isRestriction()
174 {
175 return std::is_same<Policy,RestrictToBoundary>::value;
176 }
177
180 {
181 template <class Data, class GridView, class Cell> void treatBoundary(Data&, GridView const&, Cell const&, int, int) const {}
182 };
183 } // End of namespace PartitionedSpaceDetail
184
185 // --------------------------------------------------------------------------------------------
186 // --------------------------------------------------------------------------------------------
187
193 template <class SFData>
195
231 template <class Implementation,
233 class SFData = PartitionedSpaceDetail::Empty>
235 {
236 public:
237 typedef typename Implementation::Grid Grid;
239 typedef typename Implementation::ShapeFunctionSet ShapeFunctionSet;
240 typedef typename Implementation::Converter Converter;
241 typedef typename Implementation::Combiner Combiner;
242 typedef typename Implementation::Scalar Scalar;
243 typedef typename Implementation::GridView GridView;
244 typedef typename GridView::IndexSet IndexSet;
245
255 typedef std::pair<size_t,int> IndexPair;
256
257 private:
258 // For each shape function (or localized ansatz function) on a cell we store both
259 // the global index and (optionally) some additional data. For hierarchic mappers, e.g.,
260 // this might be some sign for edge functions. Since often this is not needed, and
261 // SFData is empty, we use the compressed pair memory optimization to avoid overhead.
262 typedef boost::compressed_pair<size_t,SFData> Data;
263
264 // To extract the global index from the Data compressed pair.
266
267 // An iterator extracting the global indices of localized ansatz functions.
268 typedef boost::transform_iterator<First,typename std::vector<Data>::const_iterator> GlobalIndexIterator;
269
270 // An iterator for accessing (global,local) index pairs of localized ansatz functions.
271 typedef std::vector<IndexPair>::const_iterator SortedIndexIterator;
272
273 static constexpr int dim = Grid::dimension;
274
275 public:
278
282 static bool const globalSupport = false;
283
290 UniformPartitionedMapper(Implementation const& impl, Tagger const& tagger_)
291 : tagger(tagger_)
292 , implementation(impl)
293 {
294 update();
295 }
296
300 GridView const& gridView() const
301 {
302 return implementation.gridView();
303 }
304
308 int maxOrder() const
309 {
310 return order;
311 }
312
313
319 {
320 return GlobalIndexRange(GlobalIndexIterator(globIdx[0].begin(),First()),
321 GlobalIndexIterator(globIdx[0].begin(),First()));
322 }
323
324
335 {
336 return globalIndices(implementation.indexSet().index(cell));
337 }
338
349 {
350 return GlobalIndexRange(GlobalIndexIterator(globIdx[n].begin(),First()),
351 GlobalIndexIterator(globIdx[n].end(), First()));
352 }
353
358 {
359 static std::vector<IndexPair> dummy; // empty
360 return SortedIndexRange(dummy.begin(),dummy.end());
361 }
362
368 {
369 return sortedIndices(implementation.indexSet().index(cell));
370 }
371
377 {
378 return SortedIndexRange(sortedIdx[n].begin(),sortedIdx[n].end());
379 }
380
388 size_t size() const
389 {
390 return nDofs;
391 }
392
399 ShapeFunctionSet const& shapefunctions(Cell const& cell, bool contained=false) const
400 {
401 if (contained || implementation.indexSet().contains(cell))
402 return shapefunctions(implementation.indexSet().index(cell));
403 else
404 return implementation.shapeFunctions(cell);
405 }
406
407 // TODO: Check where I'm used. doc me or remove me.
409 {
410 return shapefunctions_non_const(implementation.indexSet().index(cell));
411 }
412
416 ShapeFunctionSet const& shapefunctions(size_t n) const
417 {
418 return *sfs[n];
419 }
420
421 // TODO: Check where I'm used. doc me or remove me.
423 {
424 return *sfs[n];
425 }
426
431 ShapeFunctionSet const& lowerShapeFunctions(Cell const& cell) const
432 {
433 if (globalIndices(cell).empty())
434 return implementation.emptyShapeFunctionSet();
435 else
436 return implementation.lowerShapeFunctions(cell);
437 }
438
444 Combiner combiner(Cell const& cell, size_t index) const
445 {
446 assert(implementation.indexSet().index(cell)==index);
447 return Combiner(rangeView(globIdx[index].begin(),globIdx[index].end()),
448 shapefunctions(index));
449 }
450
451
452 // just returns the index, since nothing is restricted
453 std::pair<bool,size_t> unrestrictedToRestrictedIndex(size_t unrestrictedIndex)
454 {
455 return std::make_pair(true, unrestrictedIndex);
456 }
457
463 void update()
464 {
465 GridView const& gridView = implementation.gridView();
466 IndexSet const& indexSet = implementation.indexSet();
467
468 // Notify the implementation of the update request.
469 implementation.update();
470
471 // First allocate the memory needed for storing global indices
472 // to prevent frequent reallocation.
473 size_t const nCells = indexSet.size(0);
474 sfs.resize(nCells);
475 globIdx.resize(nCells);
476 sortedIdx.resize(nCells);
477
478
479 // In count, for any tag and any geometry type that occurs in the indexSet
480 // we store a (sorted) list of the global entity indices included.
481 // We request that there is a fixed number of dofs associated with each
482 // tag-subentity type incidence, stored as second entry,
483 // such that equal length index blocks can be assigned
484 // to each such incidence. The later layout of dofs is thus as
485 // follows (example continuous 2D simplex mesh with three triangles (two with tag 0),
486 // order 3):
487 // tag 0: [Interior Triangle Nodes, Interior Edge Nodes, Vertex Nodes]
488 // tag 1: [Interior Triangle Nodes, Interior Edge Nodes, Vertex Nodes]
489 // with substructuring
490 // [ [[t1],[t2]], [[e1a,e1b],[e2a,e2b],[e3a,e3b],[e4a,e4b],[e5a,e5b]], [v1,v2,v3,v4],
491 // [[t3]], [[e5a,e5b],[e6a,e6b],[e7a,e7b]], [v3,v4,v5] ]
492 // where the first row corresponds to tag 0 and the second row to tag 1.
493 // The start index for the dofs on the tag-type incidence is stored along
494 // as third entry.
495 using Tag = decltype(tagger(Cell()));
496 using Key = std::pair<Tag,Dune::GeometryType>;
497 using IncidenceData = std::tuple<std::vector<size_t>,int,size_t>;
498 std::map<Key,IncidenceData> count;
499
500
501 // In a two-stage approach, we first count the number of dofs for each tag-type incidence
502 // by stepping through all cells, then compute the start indices for each such incidence.
503 order = 0;
504 for(auto const& cell : elements(gridView))
505 {
506 size_t const cellIndex = indexSet.index(cell);
507 auto tag = tagger(cell);
508
509 // Store a shape function set even if the current cell does not belong to our
510 // support subdomain. This is necessary because we return a *reference*
511 // to a shape function set for all cells.
512 ShapeFunctionSet const& sf = implementation.shapeFunctions(cell);
513 sfs[cellIndex] = &sf;
514
515
516 // Skip this cell and let it's set of dofs empty if it is not in our support.
517 if (!implementation.inSupport(cell))
518 {
519 globIdx[cellIndex].clear(); // We must clear the indices explicitly, as on refinement
520 sortedIdx[cellIndex].clear(); // there may be leftovers from the previous grid.
521 continue;
522 }
523
524 // Track the maximum shape function order encountered on any cell.
525 order = std::max(order,sf.order());
526
527 size_t localNumberOfShapeFunctions = sf.size();
528 globIdx[cellIndex].resize(localNumberOfShapeFunctions);
529 sortedIdx[cellIndex].resize(localNumberOfShapeFunctions);
530
531 // For each (local) shape function, have it's global index computed and noted.
532 for(size_t i=0; i<localNumberOfShapeFunctions; ++i)
533 {
534 Dune::GeometryType gt;
535 int subentity, codim, indexInSubentity;
536 SFData sfData;
537 implementation.entityIndex(cell,sf[i],i,gt,subentity,codim,indexInSubentity,sfData);
538
539 // compute the global index of the subentity to which the shape function is associated
540 int gt_idx = implementation.subIndex(cell,subentity,codim);
541
542 // Find the the data for (tag,geometry type) incidences, inserting an empty datum
543 // if there is none so far.
544 auto mi = count.insert(std::pair(Key(tag,gt),
545 IncidenceData{{},implementation.dofOnEntity(gt),0})).first;
546 assert(mi!=count.end());
547
548 // register the concrete entity (possibly several times)
549 std::get<0>(mi->second).push_back(gt_idx);
550 }
551 }
552
553 // Compute start indices by building partial sums. For that we need to
554 // have the number of entities in each tag-type incidence, which we obtain
555 // by making the list of actual entity indices unique.
556 size_t start = 0;
557 for (auto& entry: count)
558 {
559 // TODO: sorting might be expensive. Do profiling.
560 auto& idxs = std::get<0>(entry.second);
561 int const dofPerEntity = std::get<1>(entry.second);
562
563 std::sort(idxs.begin(),idxs.end());
564 idxs.erase(std::unique(idxs.begin(),idxs.end()),idxs.end());
565
566 std::get<2>(entry.second) = start; // enter the start position
567 start += dofPerEntity*idxs.size(); // add the count to obtain next start position
568 }
569
570 // Now that we have seen all, start contains the total number of dofs.
571 nDofs = start;
572
573 // Step through all cells and compute the global ansatz function
574 // indices of all shape functions on that cell.
575 for(auto const& cell : elements(gridView))
576 {
577 size_t const cellIndex = indexSet.index(cell);
578 auto tag = tagger(cell);
579
580 // Skip this cell if there are no dofs associated.
581 if (globIdx[cellIndex].empty())
582 continue;
583
584 // For each (local) shape function, have it's global index computed and noted.
585 auto const& localSfs = *sfs[cellIndex];
586 for(size_t i=0; i<localSfs.size(); ++i)
587 {
588 Dune::GeometryType gt;
589 int subentity, codim, indexInSubentity;
590 SFData sfData;
591 implementation.entityIndex(cell,localSfs[i],i,gt,subentity,codim,indexInSubentity,sfData);
592
593 // compute the global index of the subentity to which the shape function is associated
594 int gt_idx = implementation.subIndex(cell,subentity,codim);
595
596 // Find the count of (tag,geometry type) incidences, inserting zero
597 // if there is none so far.
598 auto mi = count.find(Key(tag,gt));
599 assert(mi!=count.end());
600
601 auto const& [eIdx,dofPerEntity,start] = mi->second;
602 auto pos = std::lower_bound(begin(eIdx),end(eIdx),gt_idx);
603 assert(pos != end(eIdx));
604 int globalIndex = start + (pos-begin(eIdx))*dofPerEntity;
605
606 globIdx[cellIndex][i] = Data(globalIndex,sfData);
607 sortedIdx[cellIndex][i] = std::make_pair(globIdx[cellIndex][i].first(),i);
608 }
609
610
611 // sort the index pairs according to the global index
612 std::sort(sortedIdx[cellIndex].begin(),sortedIdx[cellIndex].end(),FirstLess());
613
614 // make sure that any assigned shape function forms at most one ansatz function.
615 // This is a functionality restriction, but seems quite reasonable as a debugging check.
616 // Note that not all shape functions need not be assigned. If a shape function is mapped
617 // Jakob: Are negative indices for unused shape function really allowed? Are'nt they simply
618 // not present in globIdx, such that the check idx[j]<0 below is nonsense?
619 // to negative global index, it takes not part at all (e.g. in hp-methods).
620#ifndef NDEBUG
621 std::vector<size_t> idx(globIdx[cellIndex].size());
622 for (int j=0; j<idx.size(); ++j)
623 idx[j] = globIdx[cellIndex][j].first();
624 std::sort(idx.begin(),idx.end());
625 for (int j=1; j<idx.size(); ++j)
626 assert(idx[j]>idx[j-1] || idx[j]<0);
627#endif
628 }
629 }
630
631
632 private:
633 // The number of dofs.
634 size_t nDofs = 0;
635
636 // In globIdx, for each cell there are the global ansatz function indices on that cell.
637 std::vector<std::vector<Data>> globIdx;
638
639 // In sortedIdx, for each cell there is a vector of (global index, local index) pairs, sorted ascendingly
640 // by the global index. This could be computed outside on demand, but the sorting takes time and may be
641 // amortized over multiple assembly passes/matrix blocks if cached here.
642 std::vector<std::vector<IndexPair>> sortedIdx;
643
644 // In sfs, for each cell there is a pointer to the shape function set on this cell cached.
645 std::vector<ShapeFunctionSet const*> sfs;
646
647 // The maximum polynomial ansatz order of shape functions on the cells.
648 int order;
649
650 // The provider of cell tags.
651 Tagger tagger;
652
653 protected:
654 Implementation implementation;
655 };
656
657
658 // --------------------------------------------------------------------------------------------
659 // --------------------------------------------------------------------------------------------
660
682 template <class Tagger, class GV, class ScalarType=double>
684 : public UniformPartitionedMapper<ContinuousLagrangeMapperImplementation<ScalarType,GV>,Tagger>
685 {
688
689 public:
691 typedef GV GridView;
694 static int const continuity = -1;
695
701 template <int m>
703
704 template <int m>
705 struct [[deprecated]] Element
706 {
708 };
709
717 PiecewiseContinuousLagrangeMapper(GridView const& gridView, int order, Tagger const& tagger)
718 : Base(Implementation(gridView,order),tagger)
719 {
720 assert(order > 0);
721 }
722 };
723
724} // end of namespace Kaskade
725
726#endif
A class for representing finite element functions.
A degrees of freedom manager for piecewise continuous FEFunctionSpace s of local polynomials.
PiecewiseContinuousLagrangeMapper(GridView const &gridView, int order, Tagger const &tagger)
Constructor.
DEPRECATED. Use boost::iterator_range instead.
Definition: views.hh:25
Base class for piecewise continuous mappers.
GlobalIndexRange globalIndices(size_t n) const
Returns an immutable sequence containing the global indices of the ansatz functions with support inte...
Implementation::GridView GridView
void update()
(Re)computes the internal data.
RangeView< GlobalIndexIterator > GlobalIndexRange
ShapeFunctionSet const & shapefunctions(Cell const &cell, bool contained=false) const
Returns the set of shape functions defined on this cell.
ShapeFunctionSet & shapefunctions_non_const(Cell const &cell)
GlobalIndexRange initGlobalIndexRange() const
Returns an empty range just for initialization purposes, since RangeView is not default constructible...
Combiner combiner(Cell const &cell, size_t index) const
Returns a combiner for the given leaf cell.
ShapeFunctionSet const & lowerShapeFunctions(Cell const &cell) const
Returns a shape function set for a lower ansatz order (or an empty shape function set if there is no ...
GlobalIndexRange globalIndices(Cell const &cell) const
Returns an immutable sequence containing the global indices of the ansatz functions with support inte...
SortedIndexRange sortedIndices(Cell const &cell) const
Returns an immutable sequence of (global index, local index) pairs sorted in ascending global index o...
int maxOrder() const
Returns the maximal polynomial order of shape functions encountered in any cell.
RangeView< SortedIndexIterator > SortedIndexRange
Implementation::Combiner Combiner
ShapeFunctionSet & shapefunctions_non_const(size_t n)
GridView const & gridView() const
Returns the grid view used.
SortedIndexRange sortedIndices(size_t n) const
Returns an immutable sequence of (global index, local index) pairs sorted in ascending global index o...
Implementation::Converter Converter
static bool const globalSupport
Whether the ansatz functions have global support (i.e. lead to dense matrices).
static SortedIndexRange initSortedIndexRange()
Returns an empty range just for initialization, since RangeView is not default constructible.
ShapeFunctionSet const & shapefunctions(size_t n) const
Returns the set of shape functions defined on this cell.
size_t size() const
Returns the number of global scalar degrees of freedom managed.
Implementation::ShapeFunctionSet ShapeFunctionSet
std::pair< size_t, int > IndexPair
Indexing information for localized ansatz functions.
std::pair< bool, size_t > unrestrictedToRestrictedIndex(size_t unrestrictedIndex)
UniformPartitionedMapper(Implementation const &impl, Tagger const &tagger_)
Constructor.
int count(Cell const &cell, int codim)
Computes the number of subentities of codimension c of a given entity.
Definition: fixdune.hh:716
typename GridView::template Codim< 0 >::Entity Cell
The type of cells (entities of codimension 0) in the grid view.
Definition: gridBasics.hh:35
typename GridView::Intersection Face
The type of faces in the grid view.
Definition: gridBasics.hh:42
Dune::FieldVector< T, n > max(Dune::FieldVector< T, n > x, Dune::FieldVector< T, n > const &y)
Componentwise maximum.
Definition: fixdune.hh:110
Lagrange Finite Elements.
bool considerFace(Face const &face, std::vector< int > const &boundaryIds, std::vector< int > const &usedIds)
int getBoundaryId(Face const &face, std::vector< int > const &boundaryIds)
bool usedId(int id, std::vector< int > const &usedIds)
bool onSimplexFace(int dim, int codim, int id, int localFaceId)
check if a given subentity E is part of a certain simplex face F
RangeView< It > rangeView(It first, It last)
Convenience function for constructing range views on the fly.
Definition: views.hh:59
typename GetScalar< Type >::type ScalarType
Extracts the scalar field type from linear algebra data types.
A comparator functor that supports sorting std::pair by their first component.
Definition: firstless.hh:22
void treatBoundary(Data &, GridView const &, Cell const &, int, int) const
A functor for extracting the first component of a boost compressed pair.
Pair::first_const_reference operator()(Pair const &pair) const
bool operator()(boost::compressed_pair< size_t, Data > const &a, boost::compressed_pair< size_t, Data > const &b)
void treatBoundary(Data &data, GridView const &gridView, Cell const &cell, int codim, int subentity) const
RestrictToBoundary(RestrictToBoundary const &other)
RestrictToBoundary(std::vector< int > const &boundaryIds_, std::vector< int > const &usedIds_)
FunctionSpaceElement< FEFunctionSpace< PiecewiseContinuousLagrangeMapper >, m > type