KASKADE 7 development version
hb.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) 2012-2012 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
19#ifndef HB_HH
20#define HB_HH
21
22#include <cassert>
23#include <cmath>
24#include <iostream>
25#include <vector>
26
27#include <boost/utility/result_of.hpp>
28
29#include "dune/grid/common/grid.hh"
30#include "dune/istl/preconditioners.hh"
31
32#include "fem/barycentric.hh"
34
35
36namespace Kaskade {
37
63 template <class Grid, class Domain, class Range>
65 {
66 private:
67 typedef typename Grid::LevelGridView::IndexSet::IndexType Index;
68 typedef std::vector<Index> NodeSet;
69
70 static int const dim = Grid::dimension;
71
72 public:
78 explicit HierarchicalBasisPreconditioner(Grid const& grid)
79 {
80 // make sure the grid satisfies our assumptions: a purely simplicial grid
81 assert(grid.leafIndexSet().types(0).size()==1 && grid.leafIndexSet().types(0)[0].isSimplex()
82 && "HierarchicalBasisPreconditioner currently requires purely simplicial grids");
83
84
85 // extract and store relevant sizes
86 j = grid.maxLevel();
87 n = grid.leafIndexSet().size(dim);
88
89
90 typedef typename Grid::template Codim<0>::LevelIterator CellLevelIterator;
91 typedef typename Grid::template Codim<dim>::LevelIterator LeafVertexIterator;
92
93 // extract the coarse grid nodes
94 nodesOnLevel.resize(j+1);
95 for (LeafVertexIterator i=grid.levelGridView(0).template begin<dim>(); i!=grid.levelGridView(0).template end<dim>(); ++i)
96 nodesOnLevel[0].push_back(grid.leafIndexSet().index(*i));
97
98 // extract the coarse grid elements' vertices
99 coarseElement.reserve(grid.levelIndexSet(0).size(0));
100 for (CellLevelIterator ci=grid.levelGridView(0).template begin<0>(); ci!=grid.levelGridView(0).template end<0>(); ++ci)
101 {
103 for (int i=0; i<=dim; ++i)
104 idx[i] = grid.leafIndexSet().index((ci->template subEntity<dim>(i)));
105 coarseElement.push_back(idx);
106 }
107
108 // compute the parents of each node on level >0
109 parents.resize(n,std::make_pair(n,n)); // invalid sentinel (n,n) denotes not yet processed nodes.
110 for (int k=1; k<=j; ++k)
111 {
112 // On each level, look out where nodes of this level are located in their father cell. Their barycentric coordinates
113 // should have exactly two nonzero entries of size 0.5. The corresponding corners of the father cell are the parents.
114 for (CellLevelIterator ci=grid.levelGridView(k).template begin<0>(); ci!=grid.levelGridView(k).template end<0>(); ++ci)
115 {
116 typedef typename Grid::template Codim<0>::Entity Cell;
117 Cell father = ci->father();
118
119 // Extract indices of father's vertices
120 // @TODO Variable is never read! Remove or keep for future extension of the preconditioner??
121// Index fatherCornerIndex[dim+1];
122// for (int i=0; i<=dim; ++i)
123// fatherCornerIndex[i] = grid.leafIndexSet().index(*(father->template subEntity<dim>(i)));
124
125 // consider each vertex of current cell in turn
126 for (int i=0; i<=dim; ++i)
127 {
128 typename Grid::template Codim<dim>::Entity vertex = ci->template subEntity<dim>(i);
129 Index idx = grid.leafIndexSet().index(vertex);
130
131
132 // Edge midpoints can be reached from multiple cells depending on the spatial dimension. The parent nodes
133 // are independent of from which cell we look. Thus we check wether we've already done the work.
134 if (parents[idx].first==n && vertex.level()>0) // not yet processed (but has parents)
135 {
136 // Obtain barycentric coordinates of child corner in father. For child nodes located on an edge,
137 // there will be exactly two nonzero entries associated to the vertices. Note that the barycentric
138 // coordinates as implemented in fem/barycentric.hh are shifted by index 1 compared to the
139 // reference element vertex numbering.
140 Dune::FieldVector<typename Grid::ctype,dim+1> relativeCoord = barycentric(ci->geometryInFather().corner(i));
141
142 // Extract the two coordinates with value 0.5
143 int coords[dim+1];
144 int pos = 0;
145 for (int m=0; m<=dim; ++m)
146 if (relativeCoord[m] > 0.4)
147 coords[pos++] = (m+1)%(dim+1);
148
149 if (pos==2)
150 {
151 // that's an edge midpoint: obtain the leaf indices of those father corners
152 parents[idx].first = grid.leafIndexSet().index((father.template subEntity<dim>(coords[0])));
153 parents[idx].second = grid.leafIndexSet().index((father.template subEntity<dim>(coords[1])));
154
155 // write down that this node appeared first on level k
156 nodesOnLevel[k].push_back(idx);
157 }
158 }
159 }
160 }
161 }
162
163 // perform sanity checks
164 #ifndef NDEBUG
165 // check that each level >0 node has parents
166 int count = nodesOnLevel[0].size();
167 for (int k=1; k<=j; ++k)
168 {
169 for (typename NodeSet::const_iterator i=nodesOnLevel[k].begin(); i!=nodesOnLevel[k].end(); ++i)
170 assert(parents[*i].first<n && parents[*i].second<n);
171 count += nodesOnLevel[k].size();
172 }
173 assert(count==n);
174 #endif
175 }
176
180 virtual void pre (Domain& x, Range& b)
181 {
182 // actually, does nothing :)
183 }
184
188 virtual void apply(Domain& v, const Range& d)
189 {
190 using namespace boost::fusion;
191 // We modify the residual (in-place), hence we have to copy it here.
192 Range r = d;
193
194 // apply smoother and recursively restrict the residual downwards through the mesh hierarchy
195 for (int k=j; k>0; --k)
196 {
197 // correct scaling for diagonal coefficients
198 typename Domain::field_type a = std::pow(2.0,(Grid::dimension-2.0)*(k-j));
199
200 // for all nodes on level k
201 for (typename NodeSet::const_iterator i=nodesOnLevel[k].begin(); i!=nodesOnLevel[k].end(); ++i)
202 {
203 // apply one Jacobi step, care for correct scaling of lower level hierarchical basis functions
204 // Beispiel at_c<0>(estSol.data)[*j];
205
206 at_c<0>(v.data)[*i] = at_c<0>(r.data)[*i];
207 at_c<0>(v.data)[*i] /= a;
208
209 // restrict the residual
210 at_c<0>(r.data)[parents[*i].first] += 0.5 * at_c<0>(r.data)[*i];
211 at_c<0>(r.data)[parents[*i].second] += 0.5 * at_c<0>(r.data)[*i];
212 }
213 }
214
215 // "solve" on coarse grid.
216 coarseGridSolution(v,r);
217
218 // prolongate the correction recursively upwards through the mesh hierarchy, adding up
219 // all the corrections from different levels
220 for (int k=1; k<=j; ++k)
221 // the nodes on level k get contributions of 0.5 from each of their parent nodes - that's all
222 for (typename NodeSet::const_iterator i=nodesOnLevel[k].begin(); i!=nodesOnLevel[k].end(); ++i)
223 at_c<0>(v.data)[*i] += 0.5*(at_c<0>(v.data)[parents[*i].first]+at_c<0>(v.data)[parents[*i].second]);
224 }
225
226 virtual typename SymmetricPreconditioner<Domain,Range>::field_type applyDp(Domain& v, const Range& d) override
227 {
228 apply(v,d);
229 return v*d;
230 }
231
235 virtual void post (Domain& x)
236 {
237 // actually, does nothing :)
238 }
239
240 virtual bool requiresInitializedInput() const
241 {
242 return false;
243 }
244
245 private:
246 std::vector<NodeSet> nodesOnLevel; // hierarchical basis node sets
247 std::vector<std::pair<Index,Index> > parents; // parent nodes of child nodes on level >0
248 int j; // maximum grid level
249 Index n; // total number of nodes
250 std::vector<Dune::FieldVector<Index,dim+1> > coarseElement;
251
252 // This coarse grid solution employs a simple Jacobi iteration, where the
253 // matrix is simply patched together from identical elemental stiffness matrices.
254 // TODO: maybe it would be better to explicitly form the coarse grid matrix and use a direct solver
255 void coarseGridSolution(Domain& v, Range& r) const {
256 using namespace boost::fusion;
257 // get the correct scaling for the coarse grid
258 typename Domain::field_type a = std::pow(2.0,-(Grid::dimension-2.0));
259
260 // Initialize correction to zero
261 for (typename NodeSet::const_iterator i=nodesOnLevel[0].begin(); i!=nodesOnLevel[0].end(); ++i)
262 at_c<0>(v.data)[*i] = 0;
263 Domain dv = v; // TODO: that's a looooong vector (overkill)
264
265 // perform a couple of Jacobi iterations
266 for (int k=0; k<20; ++k)
267 {
268 // add correction: solve with diagonal and set residual to zero
269 for (typename NodeSet::const_iterator i=nodesOnLevel[0].begin(); i!=nodesOnLevel[0].end(); ++i)
270 {
271 at_c<0>(dv.data)[*i] = at_c<0>(r.data)[*i];
272 at_c<0>(v.data)[*i] += at_c<0>(dv.data)[*i] / a;
273 }
274
275 // update the residual. We are geometry agnostic here and use a rough approximation of the Laplacian: On each
276 // coarse grid element we assume we have the following element matrices in 1D, 2D, 3D, respectively:
277 // [ 1 -1 ] [ 2 -1 -1 ] [ 3 -1 -1 -1 ]
278 // [ -1 1 ], 1/12 [-1 2 -1 ], 1/60 [ -1 3 -1 -1 ]
279 // [-1 -1 2 ] [ -1 -1 3 -1 ]
280 // [ -1 -1 -1 3 ]
281 // Obviously, the matrixes are just s*(-e*e^T + (d+1)*I), where e is the vector with all entries zero. In order
282 // to have an invertible matrix, we add a little bit more of the identity, i.e. we end up with element matrices
283 // s*(-e*e^T + (d+1.1)*I) with s=1, 1/12, 1/60 for d=1,2,3, respectively. In this form, multiplication with the
284 // total stiffness matrix is just summing up the contributions of the elemental matrices that can efficiently
285 // been computed.
286 typename Dune::Preconditioner<Domain,Range>::field_type s = dim==1? 1.0: dim==2? 1/12.0 : 1/60.0;
287 for (int i=0; i<coarseElement.size(); ++i)
288 {
289 //typename Domain::block_type eTdv = 0;
290 double eTdv = 0;
291 for (int m=0; m<=dim; ++m)
292 eTdv += at_c<0>(dv.data)[coarseElement[i][m]];
293 for (int m=0; m<=dim; ++m)
294 at_c<0>(r.data)[coarseElement[i][m]] -= a*s*(-eTdv+(dim+1.1)*at_c<0>(dv.data)[coarseElement[i][m]]);
295 }
296 }
297 }
298 };
299
300}
301
302#endif
A hierarchical basis preconditioner.
Definition: hb.hh:65
HierarchicalBasisPreconditioner(Grid const &grid)
Constructor.
Definition: hb.hh:78
virtual bool requiresInitializedInput() const
Returns true if the target vector x has to be initialized to zero before calling apply or applyDp.
Definition: hb.hh:240
virtual void pre(Domain &x, Range &b)
Has to be called before the first call to apply.
Definition: hb.hh:180
virtual SymmetricPreconditioner< Domain, Range >::field_type applyDp(Domain &v, const Range &d) override
Computes and returns .
Definition: hb.hh:226
virtual void post(Domain &x)
Has to be called after the last call to apply.
Definition: hb.hh:235
virtual void apply(Domain &v, const Range &d)
applies the preconditioner to the residual
Definition: hb.hh:188
Interface for symmetric preconditioners.
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
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