KASKADE 7 development version
prolongation.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) 2015-2018 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 MG_PROLONGATION_HH
14#define MG_PROLONGATION_HH
15
16#include <boost/fusion/include/vector.hpp>
17#include <boost/timer/timer.hpp>
18
19#include "fem/barycentric.hh"
20// #include "fem/deforminggridmanager.hh"
21#include "fem/spaces.hh"
26#include "utilities/timing.hh"
27
28namespace Kaskade
29{
42 template <class SparseIndex=size_t, class CoarseSpace, class FineSpace>
44 FineSpace const& fineSpace)
45 {
46 namespace bf = boost::fusion;
47
48 Timings& timer = Timings::instance();
49 timer.start("space prolongation");
50
51 std::vector<bf::vector<std::vector<size_t>,std::vector<size_t>,
52 DynamicMatrix<Dune::FieldMatrix<typename FineSpace::Scalar,1,1>>>> interpolationData(fineSpace.gridView().size(0));
53
54 // Intermediate shape function values, declared here to prevent frequent reallocations
55 typename CoarseSpace::Mapper::ShapeFunctionSet::SfValueArray afValues;
56
57 // Step through all the cells and perform local interpolation on each cell.
58 // TODO: do this in parallel
59 for (auto const& cell: elements(fineSpace.gridView()))
60 {
61 auto index = fineSpace.indexSet().index(cell);
62
63 // copy fine and coarse indices on cell - the mapper returns only views
64 // TODO: store views in the interpolationData itself
65 auto const& fineIndices = fineSpace.mapper().globalIndices(index);
66 auto const& coarseIndices = coarseSpace.mapper().globalIndices(index);
67 std::copy(fineIndices.begin(), fineIndices.end(), std::back_inserter(bf::at_c<0>(interpolationData[index])));
68 std::copy(coarseIndices.begin(),coarseIndices.end(),std::back_inserter(bf::at_c<1>(interpolationData[index])));
69
70 auto const& fineSfs = fineSpace.mapper().shapefunctions(cell);
71 auto const& coarseSfs = coarseSpace.mapper().shapefunctions(cell);
72
73 // Obtain interpolation nodes of target on this cell
74 auto const& iNodes(fineSpace.mapper().shapefunctions(cell).interpolationNodes());
75
76 // Evaluate coarse space global shape functions
77 evaluateGlobalShapeFunctions(coarseSpace,cell,iNodes,afValues,coarseSfs);
78
79 // Interpolate fine space global values
80 approximateGlobalValues(fineSpace,cell,afValues,bf::at_c<2>(interpolationData[index]),fineSfs);
81 }
82 timer.stop("space prolongation");
83
84 timer.start("matrix creation");
85 // Create a sparse prolongation matrix sparsity pattern. Note that the local prolongation matrices
86 // are often sparse (e.g. for Lagrangian elements). We filter out the zero entries in order not to
87 // create too densely populated prolongations, which in turn lead to very expensive conjugation
88 // computations.
89 NumaCRSPatternCreator<SparseIndex> creator(fineSpace.degreesOfFreedom(),coarseSpace.degreesOfFreedom());
90 for (auto const& block: interpolationData)
91 for (int i=0; i<bf::at_c<0>(block).size(); ++i)
92 for (int j=0; j<bf::at_c<1>(block).size(); ++j)
93 {
94 auto entry = bf::at_c<2>(block)[i][j];
95 if (std::abs(entry) > 1e-12)
96 creator.addElement(bf::at_c<0>(block)[i],bf::at_c<1>(block)[j]);
97 }
98
99 // Now create and fill the matrix itself. Overwrite already existing entries
100 // (this means the weighing is just "last one wins". If the coarse space is
101 // a subspace of the fine space (which is usually the case), the prolongation
102 // is nevertheless exact.
104 for (auto const& block: interpolationData)
105 for (int i=0; i<bf::at_c<0>(block).size(); ++i)
106 for (int j=0; j<bf::at_c<1>(block).size(); ++j)
107 {
108 auto entry = bf::at_c<2>(block)[i][j];
109 if (std::abs(entry) > 1e-12)
110 p[bf::at_c<0>(block)[i]][bf::at_c<1>(block)[j]] = entry;
111 }
112
113 timer.stop("matrix creation");
114 return p;
115 }
116
117 // ---------------------------------------------------------------------------------------------------------
118
129 template <class SparseIndex, class Mapper>
130 std::vector<NumaBCRSMatrix<Dune::FieldMatrix<typename Mapper::Scalar,1,1>,SparseIndex>>
132 {
133 std::vector<NumaBCRSMatrix<Dune::FieldMatrix<typename Mapper::Scalar,1,1>,SparseIndex>> stack;
134 int p = space.mapper().maxOrder();
135
136 if (p > 1) // there is some need for prolongation
137 {
138 H1Space<typename Mapper::Grid> coarseSpace(space.gridManager(),space.gridView(),p/2); // a coarser space (lower order)
139 stack = prolongationStack(coarseSpace);
140 stack.push_back(prolongation<SparseIndex>(coarseSpace,space));
141 }
142
143 return stack;
144 }
145
146 // ---------------------------------------------------------------------------------------------------------
147
149 namespace ProlongationDetail
150 {
151 struct Node
152 {
153 size_t p, q; // global (fine grid) indices of parent vertices
154 int level; // level of the vertex
155 };
156 }
158
172 {
173 public:
182 MGProlongation(std::vector<ProlongationDetail::Node> const& parents, std::vector<size_t> const& indexInCoarse,
183 size_t nc, int fineLevel);
184
191 std::array<size_t,2> const& parents(size_t i) const
192 {
193 return entries[i];
194 }
195
203 template <class Vector>
204 void umv(Vector const& c, Vector& f) const
205 {
206 assert(f.size()==entries.size());
207 assert(c.size()==nc);
208
209 for (size_t row=0; row<entries.size(); ++row)
210 {
211 auto e = entries[row];
212 f[row] += static_cast<typename Vector::field_type>(0.5)*(c[e[0]] + c[e[1]]);
213 }
214 }
215
227 template <class Vector>
228 void mv(Vector const& c, Vector& f) const
229 {
230 assert(f.size()==entries.size());
231 assert(c.size()==nc);
232
233 for (size_t row=0; row<entries.size(); ++row)
234 {
235 auto e = entries[row];
236 f[row] = static_cast<typename Vector::field_type>(0.5)*(c[e[0]] + c[e[1]]);
237 }
238 }
239
247 template <class Vector>
248 void mtv(Vector const& f, Vector& c) const
249 {
250 assert(f.size()==entries.size());
251 if (c.size()!=nc)
252 c.resize(nc);
253 for (size_t i=0; i<c.N(); ++i)
254 c[i] = 0;
255
256 for (size_t row=0; row<entries.size(); ++row)
257 {
258 c[entries[row][0]] += static_cast<typename Vector::field_type>(0.5)*f[row];
259 c[entries[row][1]] += static_cast<typename Vector::field_type>(0.5)*f[row];
260 }
261 }
262
266 size_t N() const
267 {
268 return entries.size();
269 }
270
274 size_t M() const
275 {
276 return nc;
277 }
278
282 template <class Real>
284 {
285 NumaCRSPatternCreator<> creator(N(),M(),false,2);
286
287 for (size_t i=0; i<N(); ++i)
288 creator.addElements(&i,&i+1,begin(entries[i]),end(entries[i]));
289
291 for (size_t i=0; i<N(); ++i)
292 for (size_t j: entries[i])
293 P[i][j] += 0.5;
294
295 return P;
296 }
297
308 template <class Entry, class Index>
309 NumaBCRSMatrix<Entry,Index> galerkinProjection(NumaBCRSMatrix<Entry,Index> const& A, bool onlyLowerTriangle = false) const
310 {
311 assert(A.N()==N() && A.M()==N());
312 Timings& timer = Timings::instance();
313
314 // First, create the sparsity pattern of the projected matrix.
315 NumaCRSPatternCreator<Index> creator(M(),M(),onlyLowerTriangle);
316
317 // If C = P^T A P, we have that C_{ij} = \sum_{k,l} P_{ki} P_{lj} A_{kl}. Hence, the entry A_{kl} contributes to
318 // all C_{ij} for which there are nonzero entries P_{ki} and P_{lj} in the rows k and l of P. Thus we can simply
319 // run through all nonzeros A_{kl} of A, look up the column indices i,j of rows k and l of P, and flag C_{ij}
320 // as nonzero.
321
322 // Step through all entries of A
323 timer.start("h conjugation pattern");
324 for (Index k=0; k<A.N(); ++k)
325 {
326 auto const& is = entries[k]; // indices i for which Pki != 0
327 auto row = A[k];
328 for (auto ca=row.begin(); ca!=row.end(); ++ca)
329 {
330 Index const l = ca.index();
331 auto const& js = entries[l]; // indices j for which Plj != 0
332 // add all combinations i,j, but be careful no index must occur twice in any range
333 if (is[0]==is[1] && js[0]==js[1])
334 creator.addElement(is[0],js[0]);
335 else if (is[0]==is[1])
336 creator.addElements(std::begin(is),std::begin(is)+1,std::begin(js),std::end(js));
337 else if (js[0]==js[1])
338 creator.addElements(std::begin(is),std::end(is),std::begin(js),std::begin(js)+1);
339 else
340 creator.addElements(std::begin(is),std::end(is),std::begin(js),std::end(js));
341
342 if (onlyLowerTriangle && k>l) // subdiagonal entry (k,l) of A -> entry (l,k) must be treated implicitly:
343 creator.addElements(std::begin(js),std::end(js),std::begin(is),std::end(is)); // add all combinations (j,i)
344 }
345 }
346 timer.stop("h conjugation pattern");
347
348 // An alternative way of computing the sparsity pattern would be to use that the nonzero entries j in column i
349 // of C are exactly those for which there is k with (nonzero P_{jk} and there is l with (nonzero P_{il} and A_{lk})).
350 // Hence we can obtain the column index set J directly by the following steps:
351 // (i) find all l with P_{li} nonzero -> L [requires to access columns of P - compute the transpose patterns once]
352 // (ii) find all k with A_{lk} nonzero for some l in L -> K [probably sorting K and removing doubled entries would be a good idea here]
353 // (iii) find all j with P_{kj} nonzero for some k in K -> J
354 // Compared to the above implementation this would have the following (dis)advantages
355 // + easy to do in parallel (since write operations are separated)
356 // + fewer scattered write accesses to memory
357 // - more complex implementation
358 // - requires the transpose pattern of P
359
360 // Create the sparse matrix.
361 timer.start("matrix creation");
362 NumaBCRSMatrix<Entry,Index> pap(creator);
363 timer.stop("matrix creation");
364
365 // Fill the sparse matrix PAP. This is done as before by stepping through all Akl entries and scatter
366 // Pki*Plj*Akl into PAPij.
367 timer.start("matrix conjugation");
368 for (Index k=0; k<A.N(); ++k)
369 {
370 auto const& is = entries[k];
371 auto row = A[k];
372 for (auto ca=row.begin(); ca!=row.end(); ++ca)
373 {
374 Index const l = ca.index();
375 auto const& js = entries[l];
376
377 if (is[0]==is[1] && js[0]==js[1]) // a coarse grid node - this means four identical contributions
378 pap[is[0]][js[0]] += *ca; // with factor 1/4. Substitute with one contribution with factor 1.
379 else
380 {
381 auto pap0 = pap[is[0]];
382 static typename Entry::field_type const quarter = 0.25;
383 pap0[js[0]] += quarter * *ca;
384 pap0[js[1]] += quarter * *ca;
385
386 auto pap1 = pap[is[1]];
387 pap1[js[0]] += quarter * *ca;
388 pap1[js[1]] += quarter * *ca;
389 }
390
391 if (onlyLowerTriangle)
392 abort(); // not yet implemented
393 }
394 }
395 timer.stop("matrix conjugation");
396
397 return pap;
398 }
399
400
401 private:
402 // Internally we treat all rows equally: A row with one entry of value one is represented as
403 // two entries which sum up to 1 (that happen to reference the same column index...). This allows a
404 // very simple and uniform implementation. The vector entries contains the column indices of
405 // the two entries in each row.
406 std::vector<std::array<size_t,2>> entries;
407
408 // Number of columns (i.e. coarse grid nodes).
409 size_t nc;
410 };
411
412 std::ostream& operator<<(std::ostream& out, MGProlongation const& p);
413
420 template <class Entry, class Index>
421 auto conjugation(MGProlongation const& p, NumaBCRSMatrix<Entry,Index> const& a, bool onlyLowerTriangle=false)
422 {
423 return p.template galerkinProjection(a,onlyLowerTriangle);
424 }
425
426 // ---------------------------------------------------------------------------------------------------------
427
429 namespace ProlongationDetail
430 {
431 // For each corner of the given cell, if there are any of them which are not corners of the father cell
432 // (i.e. created by bisection of an edge), enter the parent vertices into the parents vector.
433 template <class LeafView, class Cell, class Parents>
434 void computeParents(LeafView const& leafView, Cell const& cell, Parents& parents)
435 {
436 // If this cell is a coarse grid cell, none of its vertices have parents, and we're done.
437 if (!cell.hasFather())
438 return;
439
440 int const dim = LeafView::dimension;
441
442 // For each corner, check its position in the parent. If it has barycentric coordinates with one
443 // entry approximately one (all others zero), it is a corner of the father cell and will be treated
444 // later (or has already been treated). Otherwise there will be two entries 0.5 (all others zero),
445 // and these entries denote father corners which are the parents.
446 assert(cell.type().isSimplex());
447 auto const& geo = cell.geometryInFather();
448 int nCorners = geo.corners();
449 for (int i=0; i<nCorners; ++i)
450 {
451 // TODO: We could first obtain the index of the corner and check whether its parents have
452 // already been determined, and skip the geometric considerations in that case.
453 // This should save some time.
454 // On the other hand, obtaining indices can also be expensive, and in the current
455 // implementation we only have to get them for corners that actually are no corners
456 // in the father cell. This saves some time, too.
457 // We should check which option is faster, and whether it makes a big difference in
458 // the first place.
459
460 // barycentric coordinates of corner in the father cell
461 auto b = barycentric(geo.corner(i));
462
463 // find potential parent vertices (those with barycentric coordinates 0.5)
464 int pcount = 0;
465 int pc[2];
466 for (int k=0; k<b.N(); ++k) // check all barycentric coordinates
467 if (std::abs(b[k]-0.5) < 0.01) // if close to 0.5 accept
468 {
469 pc[pcount] = (k+1) % (dim+1); // map barycentric coordinate number to Dune corner number
470 ++pcount; // note down that we've found one more parent vertex
471 }
472
473 if (pcount == 2) // corner i is a father edge midpoint
474 {
475 auto const& is = leafView.indexSet();
476 parents.push_back(std::make_pair(is.index(cell.template subEntity<dim>(i)),
477 Node{is.index(cell.father().template subEntity<dim>(pc[0])),
478 is.index(cell.father().template subEntity<dim>(pc[1])),
479 cell.level()}));
480 }
481 }
482 }
483
484 // For each corner of the given cell, if there are any of them which are not corners of the father cell
485 // (i.e. created by bisection of an edge), enter the parent vertices into the parents vector.
486 template <class LeafView, class Cell, class Parents>
487 void computeShiftedParents(LeafView const& leafView, Cell const& cell, Parents& parents,
488 bool interpolateAtShiftedPosition=true)
489 {
490 // If this cell is a coarse grid cell, none of its vertices have parents, and we're done.
491 if (!cell.hasFather())
492 return;
493
494 int const dim = LeafView::dimension;
495
496 // For each corner, check its position in the parent. If it has barycentric coordinates with one
497 // entry approximately one (all others zero), it is a corner of the father cell and will be treated
498 // later (or has already been treated). Otherwise there will be two entries 0.5 (all others zero),
499 // and these entries denote father corners which are the direct parents.
500 assert(cell.type().isSimplex());
501 auto const& geo = cell.geometryInFather();
502 int nCorners = geo.corners();
503 for (int i=0; i<nCorners; ++i)
504 {
505 // barycentric coordinates of corner in the father cell
506 auto b = barycentric(geo.corner(i));
507
508 // find potential parent vertices (those with barycentric coordinates 0.5)
509 int pcount = 0;
510 int pc[2];
511 for (int k=0; k<b.N(); ++k) // check all barycentric coordinates
512 if (std::abs(b[k]-0.5) < 0.01) // if close to 0.5 accept
513 {
514 pc[pcount] = (k+1) % (dim+1); // map barycentric coordinate number to Dune corner number
515 ++pcount; // note down that we've found one more parent vertex
516 }
517
518 if (pcount == 2) // corner i is a father edge midpoint
519 {
520 auto x = cell.geometry().corner(i); // global (possibly displaced) vertex position
521 auto y0 = cell.father().geometry().corner(pc[0]); // global parent vertex position
522 auto y1 = cell.father().geometry().corner(pc[1]); // global parent vertex position
523
524 auto const& is = leafView.indexSet();
525 std::vector<std::pair<size_t,double>> parentVertices;
526
527 if (!interpolateAtShiftedPosition || // we ignore shifting vertices or
528 (0.5*(y0+y1)-x).two_norm() < 1e-5*(y0-y1).two_norm()) // this vertex appears not to be shifted
529 {
530 parentVertices.push_back(std::make_pair(is.index(cell.father().template subEntity<dim>(pc[0])),0.5));
531 parentVertices.push_back(std::make_pair(is.index(cell.father().template subEntity<dim>(pc[1])),0.5));
532 }
533 else // this is shifted and we want to respect it
534 {
535 // compute linear interpolation from father's corners to the current vertex
536 // TODO: consider performing the interpolation via the reference element
538 for (int j=0; j<=dim; ++j)
539 {
540 auto vj = cell.father().geometry().corner(j);
541 for (int k=0; k<dim; ++k)
542 A[k][j] = vj[k];
543 A[dim][j] = 1;
544 }
546 for (int k=0; k<dim; ++k)
547 r[k] = x[k];
548 r[dim] = 1;
549 A.solve(w,r);
550
551 for (int j=0; j<=dim; ++j)
552 parentVertices.push_back(std::make_pair(is.index(cell.father().template subEntity<dim>(j)),w[j]));
553 }
554
555 // Now that we have the contributions, insert them in the list.
556 parents.push_back(std::make_tuple(static_cast<size_t>(is.index(cell.template subEntity<dim>(i))),
557 cell.level(),parentVertices));
558 }
559 }
560 }
561
567 std::vector<MGProlongation> makeProlongationStack(std::vector<Node> parents, int maxLevel, int minLevel, size_t minNodes);
568
569 std::vector<NumaBCRSMatrix<Dune::FieldMatrix<double,1,1>>>
570 makeDeformedProlongationStack(std::vector<std::pair<int,std::vector<std::pair<size_t,double>>>>,
571 int maxLevel, int minLevel, size_t minNodes);
572 };
574
575 // ---------------------------------------------------------------------------------------------------------
576
587 template <class GridMan>
588 std::vector<MGProlongation> prolongationStack(GridMan const& gridman, int coarseLevel=0, size_t minNodes=0)
589 {
590 Timings& timer = Timings::instance();
591
592 auto const& grid = gridman.grid();
593 auto const& leafView = grid.leafGridView();
594 auto const& cellRanges = gridman.cellRanges(grid.levelGridView(0));
595
596 // In parallel compute the parent nodes for edge midpoints cell by cell
597 timer.start("parent computation");
598 std::vector<std::vector<std::pair<size_t,ProlongationDetail::Node>>> myParents(cellRanges.maxRanges());
599 parallelFor([&](int k, int n)
600 {
601 for (auto const& coarseCell: cellRanges.range(n,k))
602 for (auto const& cell: descendantElements(coarseCell,grid.maxLevel()))
603 ProlongationDetail::computeParents(leafView,cell,myParents[k]);
604 },myParents.size());
605
606 // Now that all parent nodes have been identified, consolidate them in an easily
607 // indexable array.
608 std::vector<ProlongationDetail::Node> parents(leafView.size(leafView.dimension),ProlongationDetail::Node{0,0,-1});
609 for (auto const& ps: myParents)
610 for (auto const& p: ps)
611 parents[p.first] = p.second;
612 timer.stop("parent computation");
613
614
615 // Now construct the prolongation matrices for all levels.
616 timer.start("made stack parents");
617 auto ps = ProlongationDetail::makeProlongationStack(std::move(parents),grid.maxLevel(),coarseLevel,minNodes);
618 timer.stop("made stack parents");
619 return ps;
620 }
621
622 // ---------------------------------------------------------------------------------------------------------
623
642 template <class GridMan>
643 std::vector<NumaBCRSMatrix<Dune::FieldMatrix<double,1,1>>>
644 deformedProlongationStack(GridMan const& gridman, int coarseLevel=0, size_t minNodes=0,
645 bool interpolateAtShiftedPosition=true)
646 {
647 using std::get;
648 using InterpolationNode = std::pair<size_t,double>; // (index,weight)
649 using InterpolationNodes = std::vector<InterpolationNode>; // complete sequence of interpolation nodes
650
651 Timings& timer = Timings::instance();
652
653 auto const& grid = gridman.grid();
654 auto const& leafView = grid.leafGridView();
655 auto const& cellRanges = gridman.cellRanges(grid.levelGridView(0));
656 constexpr int dim = GridMan::Grid::dimension;
657
658 // Edge midpoints may be shifted on refinement by the deforming grid manager if they are located on the boundary.
659 // In that case, linear interpolation by the two parent nodes does not provide a good prolongation. The reason
660 // is that low energy modes that can exactly be represented on the (undeformed) coarse grid are distorted and tend to have
661 // much higher energy. Consequently, the coarse grid correction cannot take large steps. Here we try to take
662 // the node displacement into account and provide a linear interpolation from the the corners of nearby cells.
663 // If the shifted edge midpoint is contained in a cell, then we take this cell. Otherwise we average over all
664 // cells that are incident to the edge (in 2D this is exactly one).
665 std::vector<std::vector<std::tuple<size_t,int,InterpolationNodes>>> interpolationData(cellRanges.maxRanges());
666 parallelFor([&](int k, int n)
667 {
668 for (auto const& coarseCell: gridman.cellRanges(grid.levelGridView(0)).range(n,k))
669 for (auto const& cell: descendantElements(coarseCell,grid.maxLevel()))
670 ProlongationDetail::computeShiftedParents(leafView,cell,interpolationData[k],interpolateAtShiftedPosition);
671 },interpolationData.size());
672
673 // Now that all possible vertex interpolations have been computed, gather them for each vertex.
674 std::vector<std::vector<std::pair<int,InterpolationNodes>>> interpolationOptions(leafView.size(dim));
675 for (auto const& idk: interpolationData)
676 for (auto const& id: idk)
677 interpolationOptions[get<0>(id)].push_back(std::make_pair(get<1>(id),get<2>(id)));
678
679 // TODO: check whether clearing interpolationData here improves performance. Reasoning: the memory may be hot in
680 // the cache, an reusing it for subsequent allocations might be faster
681
682 // Vertices can be placed on the father edge midpoint, then all interpolation options should contain just two
683 // parent cells. Otherwis, there can be at most one father cell containing the vertex. If there is one, we use
684 // this convex combination interpolation. If there is none, we average all extrapolations.
685 std::vector<std::pair<int,InterpolationNodes>> interpolation(interpolationOptions.size());
686 for (size_t k=0; k<interpolationOptions.size(); ++k)
687 {
688 auto const& i = interpolationOptions[k];
689
690 if (i.empty()) // that's a coarse grid vertex - no interpolation from parents
691 continue;
692
693 // Check that all father cells agree whether a vertex is shifted or not.
694 bool isEdgeMidpoint = false;
695 bool isShifted = false;
696 for (auto const& ip: i)
697 {
698 assert(ip.first == i[0].first); // all levels are equal
699 isEdgeMidpoint = isEdgeMidpoint || ip.second.size()==2;
700 isShifted = isShifted || ip.second.size()>2;
701 }
702 assert(isEdgeMidpoint != isShifted);
703
704 if (isEdgeMidpoint) // Standard case: vertex is just the midpoint of its
705 { // father edge. Then all interpolation options coincide:
706 interpolation[k] = i[0]; // the vertex value is just the mean of the parent node
707 continue; // values. We're done.
708 }
709
710 for (auto const& ip: i) // Vertex is shifted. Check whether there is a father cell
711 { // that contains the shifted position. Inside means the
712 bool inner = true; // barycentric interpolation weights are all nonnegative.
713 for (auto const& p: ip.second) // If there is a containing father cell, we interpolate
714 inner = inner && p.second>=0; // from its corners, omitting other father cells which
715 if (inner) // might need extrapolation.
716 interpolation[k] = ip;
717 }
718
719 if (interpolation[k].second.size()>0) // we found a father cell containing the shifted vertex
720 continue; // and inserted the interpolation weights -> done
721
722
723 interpolation[k].first = i[0].first; // level is the same for all
724 for (auto const& ip: i) // and collect all contributing coarse nodes, with plain averaging
725 for (auto const& cn: ip.second) // of all interpolation options
726 interpolation[k].second.push_back(std::make_pair(cn.first,cn.second/i.size()));
727 }
728
729 // TODO: use move semantics for interpolation instead of copy
730 timer.start("made stack parents");
731 auto ps = ProlongationDetail::makeDeformedProlongationStack(interpolation,grid.maxLevel(),coarseLevel,minNodes);
732 timer.stop("made stack parents");
733 return ps;
734 }
735
736 // ---------------------------------------------------------------------------------------------------------
737 // ---------------------------------------------------------------------------------------------------------
738
746 template <class Prolongation, class Entry, class Index>
748 {
749 public:
750
760 MultiGridStack(std::vector<Prolongation>&& ps, std::vector<NumaBCRSMatrix<Entry,Index>>&& as)
761 : prolongations(std::move(ps)), galerkinMatrices(std::move(as))
762 {
763 assert(prolongations.size()+1==galerkinMatrices.size());
764 }
765
774 MultiGridStack(std::vector<Prolongation>&& ps, NumaBCRSMatrix<Entry,Index>&& A, bool onlyLowerTriangle)
775 {
776 Timings& timer = Timings::instance();
777
778 prolongations = std::move(ps);
779 galerkinMatrices.push_back(std::move(A));
780
781 assert((prolongations.size() == 0) || (prolongations.back().N() == galerkinMatrices[0].N()));
782 assert(galerkinMatrices[0].N() == galerkinMatrices[0].M());
783
784 // Step through the prolongations from top level to bottom
785 timer.start("matrix projection");
786 for (auto pi=prolongations.crbegin(); pi!=prolongations.crend(); ++pi)
787 galerkinMatrices.insert(galerkinMatrices.begin(),conjugation(*pi,galerkinMatrices.front(),onlyLowerTriangle));
788 timer.stop("matrix projection");
789 }
790
791 MultiGridStack(MultiGridStack&& other) = default;
792
796 int levels() const
797 {
798 return galerkinMatrices.size();
799 }
800
805 Prolongation const& p(int level) const
806 {
807 assert(0<=level && level<levels()-1);
808 return prolongations[level];
809 }
810
819 NumaBCRSMatrix<Entry,Index> const& a(int level) const
820 {
821 assert(0<=level && level<levels());
822 return galerkinMatrices[level];
823 }
824
836 {
837 return galerkinMatrices[0];
838 }
839
840 void report(std::ostream& out) const
841 {
842 for (auto const& p: prolongations)
843 out << "Prolongation:\n" << p;
844
845 for (auto const& a: galerkinMatrices)
846 out << "GalerkinMatrix: \n" << a;
847 }
848
849 private:
850 std::vector<Prolongation> prolongations; // prolongations grid i -> i+1, i=0,...,n-1
851 std::vector<NumaBCRSMatrix<Entry,Index>> galerkinMatrices; // Galerkin matrices on grid i, i=0,...,n
852 };
853
854 template <typename Prolongations, typename Entry, typename Index>
855 std::ostream& operator<<(std::ostream& out, MultiGridStack<Prolongations,Entry,Index> const& mgStack) { mgStack.report(out); return out; }
856
870 template <class Prolongation, class Entry, class Index>
872 bool onlyLowerTriangle)
873 {
874 return MultiGridStack<Prolongation,Entry,Index>(std::move(ps),std::move(A),onlyLowerTriangle);
875 }
876
881 template <class GridMan, class Entry, class Index>
883 size_t minNodes=10000, bool onlyLowerTriangle=false)
884 {
885 return MultiGridStack<MGProlongation,Entry,Index>(prolongationStack(gridManager,0,minNodes),std::move(A),onlyLowerTriangle);
886 }
887
906 template <class Entry, class Index>
908 Index n=0, bool onlyLowerTriangle=false);
909
910
920 template <typename FineSpace>
921 auto makeDeepProlongationStack(FineSpace const& space, int coarseLevel=0, int minNodes=0)
922 {
923 using Real = typename FineSpace::Scalar;
925
926 // Create the P1 h-prolongation stack. We use sparse matrices as prolongation format, since this is
927 // compatible with the p-prolongation below.
928
929 auto prolongations = deformedProlongationStack(space.gridManager(),coarseLevel,minNodes);
930
931 // if the space is higher order, add another prolongation layer from P1 to higher order
932 if (space.mapper().maxOrder() > 1)
933 {
934 H1Space<typename FineSpace::Grid,Real> coarseSpace(space.gridManager(),space.gridView(),1); // a coarser space (lower order)
935 prolongations.push_back(prolongation<typename Matrix::size_type>(coarseSpace,space));
936 }
937
938 return prolongations;
939 }
940
945 template <typename FineSpace, typename Matrix>
946 auto makePMultiGridStack(FineSpace const& space, Matrix&& A, bool onlyLowerTriangle)
947 {
948 std::vector<NumaBCRSMatrix<Dune::FieldMatrix<typename FineSpace::Scalar,1,1>, typename Matrix::size_type>> prolongations;
949
950 if (space.mapper().maxOrder() > 1)
951 {
952 H1Space<typename FineSpace::Grid, typename FineSpace::Scalar> coarseSpace(space.gridManager(),space.gridView(),1); // a coarser space (lower order)
953 prolongations.push_back(prolongation<typename Matrix::size_type>(coarseSpace,space));
954 }
955
956 return makeMultiGridStack(std::move(prolongations),std::move(A),onlyLowerTriangle);
957 }
958
960 namespace ProlongationDetail
961 {
962 template <typename FineSpace, typename CoarseSpace, typename Matrix>
963 auto makePMultiGridStack(FineSpace const& fineSpace, Matrix&& fA,
964 CoarseSpace const& coarseSpace, std::unique_ptr<Matrix> cA = std::unique_ptr<Matrix>())
965 {
966 using Prolongation = NumaBCRSMatrix<Dune::FieldMatrix<typename FineSpace::Scalar,1,1>, typename Matrix::size_type>;
967 std::vector<Prolongation> prolongations;
968 prolongations.push_back(prolongation<typename Matrix::size_type>(coarseSpace,fineSpace));
969
970 std::vector<Matrix> matrices;
971 if (cA)
972 matrices.push_back(std::move(*cA));
973 else
974 matrices.push_back(conjugation(prolongations[0],fA));
975 matrices.push_back(std::move(fA));
976
977 return MultiGridStack<Prolongation,typename Matrix::block_type,
978 typename Matrix::size_type>(std::move(prolongations),std::move(matrices));
979 }
980 }
982
983
988 template <typename FineSpace, typename CoarseSpace, typename Matrix>
989 auto makePMultiGridStack(FineSpace const& fineSpace, Matrix&& fA, CoarseSpace const& coarseSpace)
990 {
991 return ProlongationDetail::makePMultiGridStack(fineSpace,std::move(fA),coarseSpace);
992 }
993
1001 template <typename FineSpace, typename CoarseSpace, typename Matrix>
1002 auto makePMultiGridStack(FineSpace const& fineSpace, Matrix&& fA, CoarseSpace const& coarseSpace, Matrix&& cA)
1003 {
1004 return ProlongationDetail::makePMultiGridStack(fineSpace,std::move(fA),coarseSpace,moveUnique(std::move(cA)));
1005 }
1006
1007}
1008
1009#endif
A LAPACK-compatible dense matrix class with shape specified at runtime.
A representation of a finite element function space defined over a domain covered by a grid.
GridManagerBase< Grid > & gridManager() const
Access to the GridManager.
GridView const & gridView() const
Provides access to the index set defining the potential support of the space.
LocalToGlobalMapper const & mapper() const
Provides read access to the node manager.
A prolongation operator for P1 finite elements from a coarser grid level to the next finer level.
size_t N() const
The number of rows.
void mtv(Vector const &f, Vector &c) const
Transpose matrix vector multiplication.
NumaBCRSMatrix< Dune::FieldMatrix< Real, 1, 1 > > asMatrix() const
Returns the prolongation in form of a sparse matrix.
MGProlongation(std::vector< ProlongationDetail::Node > const &parents, std::vector< size_t > const &indexInCoarse, size_t nc, int fineLevel)
Constructor.
void mv(Vector const &c, Vector &f) const
Matrix vector multiplcation.
size_t M() const
The number of columns.
NumaBCRSMatrix< Entry, Index > galerkinProjection(NumaBCRSMatrix< Entry, Index > const &A, bool onlyLowerTriangle=false) const
Galerkin projection of fine grid matrices. This computes .
std::array< size_t, 2 > const & parents(size_t i) const
Returns the parent nodes indices.
void umv(Vector const &c, Vector &f) const
Matrix vector multiplcation (update mode).
Class for multigrid stacks.
int levels() const
The number of grid levels.
Prolongation const & p(int level) const
Returns the prolongation from given level to next higher one.
NumaBCRSMatrix< Entry, Index > & coarseGridMatrix()
Returns the projected Galerkin matrix on the coarsest level.
MultiGridStack(std::vector< Prolongation > &&ps, std::vector< NumaBCRSMatrix< Entry, Index > > &&as)
Constructor.
void report(std::ostream &out) const
NumaBCRSMatrix< Entry, Index > const & a(int level) const
Returns the projected Galerkin matrix on the given level.
MultiGridStack(std::vector< Prolongation > &&ps, NumaBCRSMatrix< Entry, Index > &&A, bool onlyLowerTriangle)
Constructor.
MultiGridStack(MultiGridStack &&other)=default
A NUMA-aware compressed row storage matrix adhering mostly to the Dune ISTL interface (to complete....
Index M() const
The number of columns.
Index N() const
The number of rows.
A NUMA-aware creator for matrix sparsity patterns.
void addElements(IterRow const fromRow, IterRow const toRow, IterCol const fromCol, IterCol const toCol, bool colIsSorted=false)
Enters entries into the sparsity pattern.
void addElement(Index row, Index col)
Enters a single entry into the sparsity pattern.
Supports gathering and reporting execution times information for nested program parts.
Definition: timing.hh:64
static Timings & instance()
Returns a reference to a single default instance.
void stop(std::string const &name)
Stops the timing of given section.
struct Times const * start(std::string const &name)
Starts or continues the timing of given section.
void approximateGlobalValues(Space const &space, typename Space::Grid::template Codim< 0 >::Entity const &cell, DynamicMatrix< Dune::FieldMatrix< typename Space::Scalar, Space::sfComponents, 1 > > &globalValues, DynamicMatrix< Dune::FieldMatrix< typename Space::Scalar, 1, 1 > > &coeff, ShapeFunctionSet const &sfs)
Computes global shape function coefficients such that the given set of global function values at the ...
Definition: fetransfer.hh:133
void evaluateGlobalShapeFunctions(Space const &space, Cell< typename Space::GridView > const &cell, std::vector< LocalPosition< typename Space::GridView > > const &xi, DynamicMatrix< Dune::FieldMatrix< typename Space::Scalar, Space::sfComponents, 1 > > &sfValues, ShapeFunctionSet const &shapeFunctionSet)
Computes the values of global shape functions at the given points (which are supposed to be local coo...
Definition: fetransfer.hh:69
typename GridView::template Codim< 0 >::Entity Cell
The type of cells (entities of codimension 0) in the grid view.
Definition: gridBasics.hh:35
Dune::FieldVector< CoordType, dim+1 > barycentric(Dune::FieldVector< CoordType, dim > const &x)
Computes the barycentric coordinates of a point in the unit simplex.
Definition: barycentric.hh:32
MultiGridStack< Prolongation, Entry, Index > makeMultiGridStack(std::vector< Prolongation > &&ps, NumaBCRSMatrix< Entry, Index > &&A, bool onlyLowerTriangle)
Convenience routine for creating multigrid stacks.
NumaBCRSMatrix< Dune::FieldMatrix< typename FineSpace::Scalar, 1, 1 >, SparseIndex > prolongation(CoarseSpace const &coarseSpace, FineSpace const &fineSpace)
Computes an interpolation-based prolongation matrix from a (supposedly) coarser space to a finer spac...
Definition: prolongation.hh:43
auto makePMultiGridStack(FineSpace const &space, Matrix &&A, bool onlyLowerTriangle)
convenience routine for creating multigrid stacks based on coarsening by reducing the ansatz order fr...
MultiGridStack< MGProlongation, Entry, Index > makeAlgebraicMultigridStack(NumaBCRSMatrix< Entry, Index > &&A, Index n=0, bool onlyLowerTriangle=false)
Creates stack of prolongations and projected Galerkin matrices.
std::vector< NumaBCRSMatrix< Dune::FieldMatrix< double, 1, 1 > > > deformedProlongationStack(GridMan const &gridman, int coarseLevel=0, size_t minNodes=0, bool interpolateAtShiftedPosition=true)
Computes a sequence of prolongation matrices for P1 finite elements in hierarchical grids.
std::vector< NumaBCRSMatrix< Dune::FieldMatrix< typename Mapper::Scalar, 1, 1 >, SparseIndex > > prolongationStack(FEFunctionSpace< Mapper > const &space)
Computes a stack of prolongation matrices for higher order finite element spaces.
MultiGridStack< MGProlongation, Entry, Index > makeGeometricMultiGridStack(GridMan const &gridManager, NumaBCRSMatrix< Entry, Index > &&A, size_t minNodes=10000, bool onlyLowerTriangle=false)
convenience routine for creating multigrid stacks based on geometric coarsening for P1 elements
auto makeDeepProlongationStack(FineSpace const &space, int coarseLevel=0, int minNodes=0)
Convenience routine for creating a deep prolongation stack from coarse to fine grid and on from P1 to...
auto makePMultiGridStack(FineSpace const &fineSpace, Matrix &&fA, CoarseSpace const &coarseSpace, Matrix &&cA)
convenience routine for creating multigrid stacks between two spaces.
void parallelFor(Func const &f, int maxTasks=std::numeric_limits< int >::max())
A parallel for loop that executes the given functor in parallel on different CPUs.
Definition: threading.hh:489
auto moveUnique(A &&a)
Creates a dynamically allocated object from an r-value reference.
Definition: memory.hh:33
Dune::FieldVector< Scalar, dim > Vector
void conjugation(Dune::BCRSMatrix< Entry > &C, Dune::BCRSMatrix< Dune::FieldMatrix< Scalar, 1, 1 > > const &P, Dune::BCRSMatrix< Entry > const &A, bool onlyLowerTriangle=false)
Computes the triple sparse matrix product .
Definition: conjugation.hh:286
std::ostream & operator<<(std::ostream &s, std::vector< Scalar > const &vec)
Definition: dune_bridge.hh:47
NumaBCRSMatrix< Dune::FieldMatrix< double, 1, 1 > > Prolongation
Definition: amg.hh:28
Convenience typedefs and creation functions for common function spaces.