KASKADE 7 development version
amg.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) 2018-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 AMG_HH
14#define AMG_HH
15
16#include <tuple>
17
18#include "fem/gridmanager.hh"
19#include "fem/gridCover.hh"
21#include "mg/prolongation.hh"
22#include "fem/cellLocator.hh"
23#include "utilities/scalar.hh"
24
25namespace Kaskade
26{
27
29
50 template <class FromSpace, class ToSpace>
51 Prolongation twoGridProlongation(FromSpace const& from, ToSpace const& to)
52 {
54
55 // Prolongation matrix has as many rows as the target (fine) space dimension,
56 // and columns as the source (coarse) dimension
57 NumaCRSPatternCreator<> creator(to.degreesOfFreedom(),from.degreesOfFreedom());
58
59 // interpolate all vertices in the target grid
60 ShapeFunctionCache<typename FromSpace::Grid,Real> sfCache; // create a shape function cache
61 auto eval = from.evaluator(&sfCache,0); // to be used by the evaluator
62 std::vector<std::tuple<size_t,size_t,Real>> entries;
63
64 for (auto const& tv: Dune::vertices(to.gridView())) // step through all the vertces (i.e. Lagrange
65 { // basis functions) of the target grid
66 auto x = tv.geometry().center();
67 auto const& sc = findCell(from.gridView(),x);
68 eval.moveTo(sc);
69 eval.evaluateAt(sc.geometry().local(x));
70 auto sfCoeff = from.linearCombination(eval); // check the contribution of source dofs to
71 // that vertex.
72 auto row = to.indexSet().index(tv);
73 for (auto const& pair: sfCoeff)
74 {
75 auto col = pair.first;
76 creator.addElement(row,col);
77 entries.push_back(std::make_tuple(row,col,pair.second[0]));
78 }
79 }
80
81 // Create and fill the prolongator
83 for (auto const& entry: entries)
84 P[std::get<0>(entry)][std::get<1>(entry)] = std::get<2>(entry);
85
86 return P;
87 }
88
89
124 template <class FromSpace, class ToSpace>
125 Prolongation nonNestedProlongation(FromSpace const& from, ToSpace const& to, double tolTrunc=0.1)
126 {
127 using LeafView = typename FromSpace::Grid::LeafGridView;
129 assert(("Coarse space should have less DOFs than fine space.", from.degreesOfFreedom()<to.degreesOfFreedom()));
130
131
132 // Prolongation matrix has as many rows as the fine space dimension,
133 // and columns as the coarse dimension
134 NumaCRSPatternCreator<> creator(to.degreesOfFreedom(),from.degreesOfFreedom());
135 ShapeFunctionCache<typename FromSpace::Grid,Real> sfCache; // create a shape function cache
136 auto eval = from.evaluator(&sfCache,0); // to be used by the evaluator
137 std::vector<std::tuple<size_t,size_t,Real>> entries; // tuple = (row,column,value)
138
139 // index set of fine space elements
140 const auto& idxCoarse = from.gridView().indexSet();
141
142 // cell locator for finding closest cells to point
143 CellLocator<LeafView> cellLocator(from.gridView());
144
145 // each row might be needed to scale to keep the
146 // row total constant after truncation
147 std::vector<double> rowscale(to.degreesOfFreedom(),1.0);
148
149 // step through all vertices of fine grid
150 for( const auto& fVert : Dune::vertices(to.gridView()) )
151 {
152 // global coordinate of vertex
153 auto xglob = fVert.geometry().center();
154
155 // find coarse grid cell containing xglob
156 // (or closest cell, if not contained in any)
157 auto [cell,dist] = cellLocator.closestCell(xglob);
158
159 // check contribution of coarse DOFs to (fine grid) vertex
160 eval.moveTo(cell);
161 eval.evaluateAt(cell.geometry().local(xglob));
162 auto coeff = from.linearCombination(eval);
163
164 // get row index
165 auto row = to.indexSet().index(fVert);
166
167 // get row total, maximal row entry and
168 // rowdiff = sum of all truncated values
169 double rowtotal(0.), rowMax(0.), rowdiff(0.);
170 for(const auto& p : coeff)
171 {
172 double val = p.second[0];
173 rowtotal += fabs(val);
174 if(fabs(val) > rowMax)
175 rowMax = fabs(val);
176 }
177
178 // add entries to pattern creator, truncating small values
179 // (those smaller than tolTunc*rowtotal)
180 // scaling happens when we insert values into Numa matrix
181 for(const auto& p:coeff)
182 {
183 auto col = p.first;
184 double val = p.second[0];
185
186 if(fabs(val) >= 1e-10 && fabs(val) >= tolTrunc*rowtotal) // alternative: if(fabs(val) >= tolTrunc*rowMax)
187 {
188 creator.addElement(row,col);
189 entries.push_back(std::make_tuple(row,col,val));
190 } else {
191 rowdiff += fabs(val);
192 }
193 }
194
195 // we keep the row total unchanged
196 // by scaling the truncated rows
197 assert(("Truncation deletes all row entries! Use smaller tolerance or other criterion for truncation.", rowdiff < rowtotal ));
198 if(tolTrunc>0 && rowdiff != 0)
199 rowscale.at(row) = rowtotal/(rowtotal-rowdiff);
200 }
201
202 // Create and fill the prolongation matrix
204 for (auto const& entry: entries)
205 P[std::get<0>(entry)][std::get<1>(entry)] = rowscale.at(std::get<0>(entry))*std::get<2>(entry);
206
207 return P;
208 }
209
224 std::vector<Prolongation>
225 nonNestedProlognationStack(std::vector<Prolongation> ps)
226 {
227 // vector to return, contains the prolongation stack
228 // with each P-matrix having full column rank
229 std::vector<Prolongation> retPs;
230
231 // initalize number of levels and prolongation matrix
232 // and vector possibly containing inidices of nonzero columns
233 int levels = ps.size();
234 Prolongation Pl;
235 // initialite nonzero columns vector
236 // contaning all indices from 0 to P.M()
237 std::vector<size_t> nzCols(ps.at(levels-1).N());
238
239 // loop though the stack (not yet freed of zero columns)
240 for(int l=levels-1; l>=0; --l)
241 {
242 // P matrix (with possible zero columns)
243 // and vector of columns indices
244 Pl = ps.at(l);
245 std::vector<size_t> allCols(Pl.M());
246 std::iota(allCols.begin(),allCols.end(),0);
247
248 // delete rows corresponding to zero columns of
249 // fine space (zeroCols of previous iteration)
250 if(nzCols.size() < Pl.N() && l!=levels-1)
251 Pl = submatrix<Prolongation>(Pl,nzCols,allCols);
252
253 auto& timer = Timings::instance();
254 // check for nonzero columns in current matrix
255 timer.start("seeking nonzero columns");
256 nzCols = nonZeroColumns(Pl);
257 timer.stop("seeking nonzero columns");
258 std::cout << "Found " << Pl.M() - nzCols.size() << " zero columns in P-matrix on level " << l << ". Removing... ";
259
260 // if we have found zero columns "delete" them
261 if(nzCols.size() < Pl.M())
262 {
263 std::vector<size_t> allRows(Pl.N());
264 std::iota(allRows.begin(),allRows.end(),0);
265 Pl = submatrix<Prolongation>(Pl,allRows,nzCols);
266 }
267 std::cout << "done! \n";
268
269 // insert in front position of return vector
270 retPs.insert(retPs.begin(), Pl);
271 }
272 return retPs;
273 }
274
275
299 template <class Space>
300 std::vector<Prolongation> makeNonNestedPstack(std::initializer_list<Space> spacelist, double tolTrunc=0.1)
301 {
302 std::cout << "Build prolongation stack based on " << spacelist.size() << " spaces (levels).\n";
303 std::vector<Prolongation> ps;
304 for(auto space = spacelist.begin(); space!= spacelist.end()-1; ++space)
305 {
306 // get prolongation matrix between each pair of spaces
307 ps.push_back(nonNestedProlongation(*space, *(space+1), tolTrunc));
308 }
309
310 // put them together in a prolongation stack
312 }
313
314
333 template <class Space1,class Space2>
334 std::vector<Prolongation> makeJointPstack(Space1 space1, Space2 space2, int coarseLevel=0)
335 {
336 std::vector<Prolongation> Pstack;
337 // concat prolongation for product space (joint geometry)
338 std::cout << "grid1:\n";
339 std::vector<Prolongation> Pstack1 = makeDeepProlongationStack(space1,coarseLevel);
340 std::cout << "grid2:\n";
341 std::vector<Prolongation> Pstack2 = makeDeepProlongationStack(space2,coarseLevel);
342 assert(("At least one grid should be hierarchical.", Pstack1.size()+Pstack2.size()>0));
343 // adapt number of levels by appending identity prolongation
344 // to grid with fewer levels
345 size_t n1 = space1.grid().leafGridView().size(space1.dim), n2 = space2.grid().size(space2.dim);
346 if(Pstack1.size()<Pstack2.size())
347 {
348 while(Pstack1.size()<Pstack2.size())
349 Pstack1.push_back(sparseUnitMatrix<double,1>(n1));
350 } else if (Pstack1.size() > Pstack2.size())
351 {
352 while(Pstack1.size()>Pstack2.size())
353 Pstack2.push_back(sparseUnitMatrix<double,1>(n2));
354 }
355 assert(("Number of levels differ on the grids.",Pstack1.size() == Pstack2.size()));
356 for(int i=0; i<Pstack1.size(); ++i)
357 Pstack.push_back(diagcat(Pstack1[i],Pstack2[i]));
358 for(int i=0; i<Pstack.size(); ++i)
359 std::cout << "prolongation matrix P(" << i << "," << i+1 << ") has dimensions " << Pstack[i].N() << "x" << Pstack[i].M() << ".\n";
360
361 return Pstack;
362 }
363
364 // ----------------------------------------------------------------------------------------------
365
376 template <class Space, class Entry, class Index>
377 MultiGridStack<Prolongation,Entry,Index>
379 double volumeRatio=100)
380 {
381 assert(A.N() == space.degreesOfFreedom());
382 using Grid = typename Space::Grid;
383 std::cout << "creating cover grid..."; std::cout.flush();
384 GridManager<Grid> coverGridMan(createCoverGrid(space.gridView(),volumeRatio));
385 std::cout << " done\n"; std::cout.flush();
386
387 // Create a P1 finite element space
388 H1Space<Grid> coverSpace(coverGridMan,coverGridMan.grid().leafGridView(),1);
389 std::cout << "cover grid vertices: " << coverSpace.degreesOfFreedom() << "\n"; std::cout.flush();
390
391 // Create prolongation from cover grid to original grid
392 using Matrix = NumaBCRSMatrix<Entry,Index>;
393 std::cout << "creating prolongation..."; std::cout.flush();
394 Prolongation P = twoGridProlongation(coverSpace,space);
395 std::cout << "done\n"; std::cout.flush();
396 std::cout << "creating conjugation..."; std::cout.flush();
397 std::vector<Matrix> as{conjugation(P,A,false,true),A};
398 std::cout << "done\n"; std::cout.flush();
399 std::vector<Prolongation> ps{std::move(P)};
400
401 // It may happen that some vertices of the cover grid do not interact at all with the original grid.
402 // Then the Galerkin projection is only positive semidefinite - in particular hard for direct solvers.
403 // We add a small positive value to the affected diagonal entries.
404 std::cout << "making spd..."; std::cout.flush();
405 double maxDiagonal = 0;
406 for (Index i=0; i<as[0].N(); ++i)
407 maxDiagonal = std::max(maxDiagonal,as[0][i][i].frobenius_norm());
408 for (Index i=0; i<as[0].N(); ++i)
409 if (as[0][i][i].frobenius_norm() < 1e-8*maxDiagonal)
410 as[0][i][i] = 1e-8*maxDiagonal*unitMatrix<typename Entry::field_type,Entry::rows>();
411 std::cout << "done\n"; std::cout.flush();
412
413 std::cout << "creating multigridstack..."; std::cout.flush();
414
415 return MultiGridStack<Prolongation,Entry,Index>(std::move(ps),std::move(as));
416 }
417}
418
419#endif
std::pair< Cell, double > closestCell(Position const &pos) const
Returns a pair consisting of the closest cell to pos and the distance to this cell.
A representation of a finite element function space defined over a domain covered by a grid.
size_t degreesOfFreedom() const
Returns the dimension of the function space, i.e. the number of degrees of freedom of a one-component...
Grid const & grid() const
Returns a const reference on the owned grid.
Definition: gridmanager.hh:292
Class for multigrid stacks.
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 addElement(Index row, Index col)
Enters a single entry into the sparsity pattern.
Scalar Real
The real type on which the scalar field is based.
Definition: scalar.hh:49
This class caches values and derivatives of shape functions at quadrature points.
static Timings & instance()
Returns a reference to a single default instance.
std::vector< Prolongation > makeNonNestedPstack(std::initializer_list< Space > spacelist, double tolTrunc=0.1)
Computes an interpolation matrix for transfer between P1 finite element spaces on two different (poss...
Definition: amg.hh:300
Prolongation twoGridProlongation(FromSpace const &from, ToSpace const &to)
Computes an interpolation matrix for transfer between P1 finite element spaces on two different grids...
Definition: amg.hh:51
std::vector< Prolongation > makeJointPstack(Space1 space1, Space2 space2, int coarseLevel=0)
Computes a (joint) prolongation stack for a geometry defined by two grids.
Definition: amg.hh:334
Prolongation nonNestedProlongation(FromSpace const &from, ToSpace const &to, double tolTrunc=0.1)
Computes an interpolation matrix for transfer between P1 finite element spaces on two different (poss...
Definition: amg.hh:125
std::vector< Prolongation > nonNestedProlognationStack(std::vector< Prolongation > ps)
Computes a full column rank prolongation stack from a colum rank deficient prolongation stack.
Definition: amg.hh:225
std::unique_ptr< typename GridView::Grid > createCoverGrid(GridView const &gv, double volumeRatio=10)
Creates a cartesian structured grid occupying the bounding box of the given grid view.
Definition: gridCover.hh:70
Cell< GridView > findCell(GridView const &gv, GlobalPosition< GridView > global)
Returns a cell of the given grid containing the specified global coordinate.
Dune::FieldVector< T, n > max(Dune::FieldVector< T, n > x, Dune::FieldVector< T, n > const &y)
Componentwise maximum.
Definition: fixdune.hh:110
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...
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
MultiGridStack< Prolongation, Entry, Index > makeAuxiliarySpaceMultigridStack(Space const &space, NumaBCRSMatrix< Entry, Index > const &A, double volumeRatio=100)
creates a semi-geometric multigrid preconditioner based on an auxiliary space
Definition: amg.hh:378