KASKADE 7 development version
coarsening.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) 2002-2020 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 COARSENING_HH
14#define COARSENING_HH
15
16
17#include <boost/timer/timer.hpp>
18#include <boost/type_traits/remove_pointer.hpp>
19#include <boost/type_traits/remove_reference.hpp>
20
21#include <boost/fusion/algorithm.hpp>
22#include <boost/fusion/sequence.hpp>
23
24#include "fem/errorest.hh"
26#include "fem/fetransfer.hh"
27#include "fem/firstless.hh"
28#include "fem/fixfusion.hh"
29#include "fem/iterate_grid.hh"
30#include "utilities/power.hh"
31
33
34namespace Kaskade
35{
36 namespace CoarseningDetail
37 {
38
39 using namespace boost::fusion;
40
41 // A class representing a local projector for a given FE space
42 template <class Sp>
43 struct Projector
44 {
45 typedef Sp Space;
46
47 Space const* space; // pointer to FE space
48 std::vector<size_t> gidx; // global indices
49 DynamicMatrix<Dune::FieldMatrix<typename Space::Scalar,1,1>> q; // stores a factor of the projection matrix Q Q^T.
50
51 // these are declared here to be used in GetLocalTransferProjection - avoid frequent reallocations
53 std::vector<int> idx;
54 };
55
56 // A functor creating local projectors for a given FE space
58 {
59 template <class T> struct result {};
60
61 template <class SpacePtr>
62 struct result<CreateProjection(SpacePtr)> {
63 typedef typename boost::remove_pointer<typename boost::remove_reference<SpacePtr>::type>::type Space;
65 };
66
67 template <class SpacePtr>
68 typename result<CreateProjection(SpacePtr)>::type operator()(SpacePtr space) const
69 {
70 typename result<CreateProjection(SpacePtr)>::type p;
71 p.space = space;
72 return p;
73 }
74 };
75
76
77 // A functor filling a local projector with actual data for a given cell
78 template <class Cell>
80 {
81 // father points to the father cell,
82 // children contains pointer to its children
83 GetLocalTransferProjection(Cell father_, std::vector<Cell> const& children_): father(father_), children(children_)
84 { }
85
86 template <class Projector>
87 void operator()(Projector& p) const
88 {
89 typedef typename Projector::Space Space;
90 // We could use LocalTransfer here, but this is conceptual and computational overkill.
91 // It supports different FE spaces (we need only one here)
92 // and creates new shape function sets just in case the cell vanishes on coarsening.
93 // We do the projection here before coarsening...
94 // Moreover we know here that all children of the father cell are leafs.
95
96 // Following a direct implementation exploiting our a priori knowledge for simplicity and performance.
97 // Obtain all global indices associated to leaf cells within this father cell
98 p.gidx.clear();
99 for (int i=0; i<children.size(); ++i)
100 {
101 auto gi = p.space->mapper().globalIndices(children[i]);
102 p.gidx.insert(p.gidx.end(),gi.begin(),gi.end());
103 }
104 // global indices can be duplicate - make unique
105 std::sort(p.gidx.begin(),p.gidx.end());
106 p.gidx.erase(std::unique(p.gidx.begin(),p.gidx.end()),p.gidx.end());
107
108 // Step through all children and obtain the prolongation matrix. Scatter this to the
109 // prolongation matrix P for all children's degrees of freedom.
110 p.q.setSize(p.gidx.size(),p.space->mapper().shapefunctions(father).size());
111// p.q = 0 // causes compilation error with dune-2.4.0 and clang++ on OS X (Darwin)
112 p.q.fill(0);
113 typedef typename Space::Grid Grid;
114 typedef typename Space::Scalar Scalar;
115 int const sfComponents = Space::sfComponents;
116 DynamicMatrix<Dune::FieldMatrix<Scalar,sfComponents,1>> afValues; // declare out of loop to prevent frequent reallocations
117 std::vector<Dune::FieldVector<typename Grid::ctype,Grid::dimension> > iNodes; // declare out of loop to prevent frequent reallocations
118 for (int i=0; i<children.size(); ++i)
119 {
120 // Compute the mapping of global indices on the child to the set of global indices on all children.
121 auto gi = p.space->mapper().globalIndices(children[i]);
122 p.idx.resize(gi.size());
123 for (int j=0; j<gi.size(); ++j)
124 p.idx[j] = std::lower_bound(p.gidx.begin(),p.gidx.end(),gi[j]) - p.gidx.begin();
125
126 // Compute local prolongation matrix p.pLocal. As father and child spaces are the same, we can use the same shape function set
127 // (assuming that the type of cell doesn't change...).
128 auto const& sfs = p.space->mapper().shapefunctions(children[i]);
129 localProlongationMatrix(*p.space,children[i],*p.space,father,p.pLocal,sfs,sfs);
130
131 // Enter prolongation values on this child into the whole prolongation matrix for all children
132 for(size_t k=0; k<p.idx.size(); ++k)
133 for(int j=0; j<p.q.M(); ++j)
134 p.q[p.idx[k]][j] = p.pLocal[k][j];
135 }
136
137 // Now the projection onto a coarser space is P P^+, where P^+, the pseudoinverse of P, is the restriction matrix.
138 // Note that this formulation of the (not uniquely defined restriction) is different from the interpolation-based
139 // restriction as obtained from LocalTransfer. Nevertheless, both make sense and both appear to work very similar.
140 // In fact one might argue that the pseudoinverse formulation here makes even more sense, as it leads to a
141 // least-squares approximation of the fine grid solution by the coarse grid restriction. "Least squares" is, however,
142 // a norm-dependent concept, and the euclidean norm of FE coefficient vectors as implicitly used here might not
143 // be the best suited.
144 // With a QR decomposition of P = Q [R;0], the pseudoinverse is P^+ = [R^{-1} 0] Q^T and hence the projection
145 // P P^+ = Q [I 0; 0 0] Q^T. If Q = [Q1 Q2], then P P^+ = Q1 Q1^T holds. The image space of Q1 is the same as that
146 // of P. For computing Q1, we therefore do not need to compute a full QR decomposition, but only the first columns of
147 // Q. The most simple method is Gram-Schmidt. Though it is numerically instable if the columns of P are almost linear
148 // dependent, we use it here, as the columns of P are associated to the shape functions on the father cell and
149 // should be comfortably linearly independent, though not orthogonal.
150 // Instead of forming Q1 Q1^T, we store just Q1, and apply the product if needed. As Q1 is high and tall, this is
151 // (slightly) more efficient.
152 for (int j=0; j<p.q.M(); ++j) // step through all columns
153 {
154 Scalar tmp = 0;
155
156 // Normalize column j.
157 for (int i=0; i<p.q.N(); ++i)
158 tmp += square(p.q[i][j]);
159 tmp = std::sqrt(tmp);
160 for (int i=0; i<p.q.N(); ++i)
161 p.q[i][j] /= tmp;
162
163 // Orthogonalize remaining columns.
164 for (int k=j+1; k<p.q.M(); ++k)
165 {
166 tmp = 0;
167 for (int i=0; i<p.q.N(); ++i)
168 tmp += p.q[i][j] * p.q[i][k];
169 for (int i=0; i<p.q.N(); ++i)
170 p.q[i][k] -= tmp*p.q[i][j];
171 }
172 }
173 }
174
175 private:
176 Cell father;
177 std::vector<Cell> const& children;
178 };
179
180
181 // A functor that applies the appropriate projector provided on construction to the
182 // FE function that is given as argument.
183 template <class Projectors>
185 {
186 // Construct the functor, giving a list of (local) projectors indexed by the FE space index.
187 ProjectCoefficients(Projectors const& projectors_): projectors(projectors_) {}
188
189 // Apply the appropriate projector to the provided FE function. The pair consists of a variable description
190 // (containing the space index) and the FE function itself.
191 template <class Pair>
192 void operator()(Pair const& pair) const
193 {
194 typedef typename boost::remove_reference<typename result_of::value_at_c<Pair,0>::type>::type VarDesc;
195 typedef typename boost::remove_reference<typename result_of::value_at_c<Pair,1>::type>::type Function;
196
197 int const sIdx = VarDesc::spaceIndex;
198 auto const& proj = at_c<sIdx>(projectors);
199 int const n = proj.gidx.size();
200
201 // Multiplication of projector Q Q^T and coefficient vector. The
202 // multiplication has to be done manually because of
203 // type-incompatible entries. This results in the coefficients of
204 // the projection residual.
205 auto const& q = proj.q;
206 int const m = q.M();
207 typename Function::StorageType x(n);
208 typename Function::StorageType y(m);
209
210 // y <- Q^T v
211 for (int i=0; i<m; ++i) {
212 y[i] = 0;
213 for (int j=0; j<n; ++j)
214 y[i].axpy(q[j][i][0][0],(at_c<1>(pair).coefficients())[proj.gidx[j]]);
215 }
216
217 // x <- Q y
218 for (int i=0; i<n; ++i) {
219 typename Function::StorageValueType x(0);
220 for (int j=0; j<m; ++j)
221 x.axpy(q[i][j][0][0],y[j]);
222 (at_c<1>(pair).coefficients())[proj.gidx[i]] = x;
223 }
224 }
225
226 private:
227 Projectors const& projectors;
228 };
229
230 // Convenience function for template type deduction.
231 template <class Projectors>
233
234
235
237 {
238 public:
239 GroupByCell(std::vector<size_t> const& groups_)
240 : nGroups(*std::max_element(groups_.begin(),groups_.end())+1)
241 , groups(&groups_)
242 {}
243
244 size_t operator[](size_t idx) const { return (*groups)[idx]; }
245
246 size_t nGroups;
247
248 private:
249 std::vector<size_t> const* groups;
250 };
251
252
253 } // End of namespace CoarseningDetail
254
255
262 template <class VariableSetDescription, class Scaling>
264 typename VariableSetDescription::VariableSet const& sol,
265 Scaling const& scaling,
266 std::vector<std::pair<double,double> > const& tol,
267 GridManager<typename VariableSetDescription::Grid>& gridManager, int verbosity=1, int minRefLevel=0)
268 {
269 std::vector<bool> tmp;
270 coarsening(varDesc, sol, scaling, tol, gridManager, tmp, verbosity, minRefLevel);
271 }
272
288 template <class VariableSetDescription, class Scaling>
290 typename VariableSetDescription::VariableSet const& sol,
291 Scaling const& scaling,
292 std::vector<std::pair<double,double>> const& tol,
294 std::vector<bool>& coarsenedCells, int verbosity=1, int minRefLevel=0)
295 {
296 using namespace boost::fusion;
297 using namespace CoarseningDetail;
298
299 typedef typename VariableSetDescription::Grid Grid;
300 typedef typename Grid::LeafIndexSet IndexSet;
301 typedef typename Grid::template Codim<0>::Entity Cell;
302 typedef typename Cell::HierarchicIterator HierarchicIterator;
303
304 IndexSet const& indexSet = varDesc.indexSet;
305
306 // A set of FE functions that will contain the projection.
307 typename VariableSetDescription::VariableSet pSol(sol);
308
309 // A container of local projectors - defined here to avoid multiple allocations inside the cell loop
310 auto projectors = as_vector(transform(varDesc.spaces,CreateProjection()));
311
312 // a container for caching computed cell pointers to children. Prevents doubled hierarchical grid traversal
313 // and frequent reallocation
314 std::vector<Cell> children;
315
316 // A mapping from leaf cells to their coarsening group number. A
317 // coarsening group is defined as follows. If all the direct
318 // children of a non-leaf cell are leafs (i.e. none of the children
319 // has been refined), these children form a coarsening group. Either
320 // all or none cells of a coarsening group are marked for
321 // coarsening. The special group number 0 collects all cells which
322 // are not element of a regular coarsening group (and hence cannot be
323 // removed during refinement).
324 std::vector<size_t> coarseningGroup(indexSet.size(0),0);
325 size_t coarseningGroupCount = 0;
326
327 // Step through all leaf cells and process their fathers.
328 for (auto const& cell: Dune::elements(gridManager.grid().leafGridView())) // TODO: parallelize this loop
329 // Father has already been processed if cell belongs to a regular
330 // cell group. No father exists if the level is 0.
331 if (coarseningGroup[indexSet.index(cell)]==0 && cell.level()>0) // not yet processed, not on coarse grid
332 {
333 // Only process fathers which are suitable for coarsening.
334 Cell father = cell.father();
335 bool canBeCoarsened = true;
336 children.clear();
337 HierarchicIterator first = father.hbegin(father.level()+1), last = father.hend(father.level()+1);
338 for (HierarchicIterator hi=first; canBeCoarsened && hi!=last; ++hi) // early termination in case we find a non-leaf child
339 {
340 canBeCoarsened = canBeCoarsened && hi->isLeaf();
341 children.push_back(*hi);
342 }
343
344 if (canBeCoarsened)
345 {
346 // Gather all the children into a new coarsening group.
347 ++coarseningGroupCount;
348 for (auto const& c: children)
349 coarseningGroup[indexSet.index(c)] = coarseningGroupCount; // not coarseningGroupCount-1: group number 0 is for not coarsenable cells
350 // Compute the projections to the locally coarser subspace.
351 for_each(projectors,GetLocalTransferProjection<Cell>(father,children));
352
353 // Apply the projectors to the FE functions.
355 }
356 }
357
358 // Create the projection residual (i.e. the error introduced by coarsening).
359 pSol -= sol;
360
361 // Compute scaled L2 norms of the function and difference,
362 // attributing the cell contributions to its coarsening group.
363 ErrorestDetail::GroupedSummationCollector<GroupByCell> sum{GroupByCell(coarseningGroup)};
365 join(pSol.data,sol.data),varDesc.spaces,scaling,sum);
366
367 // For each variable, compute the (squared) relative error
368 // contribution, that is e_i^2 = |p_i|^2/(atol^2+|f|^2*rtol^2),
369 // where f is the function and p the difference to its hierarchic
370 // projection.
371 std::vector<double> norm2(varDesc.noOfVariables,0);
372 for (int i=0; i<varDesc.noOfVariables; ++i)
373 for (int j=0; j<=coarseningGroupCount; ++j)
374 norm2[i] += sum.sums[j][i+varDesc.noOfVariables];
375 if ( verbosity>0 )
376 {
377 std::cout << "coarsening solution norms2: ";
378 std::copy(norm2.begin(),norm2.end(),
379 std::ostream_iterator<double>(std::cout," ")); std::cout << '\n';
380 };
381
382 for (int i=0; i<varDesc.noOfVariables; ++i) {
383 double toli = power(tol[i].first,2) + norm2[i]*power(tol[i].second,2);
384 for (int j=0; j<=coarseningGroupCount; ++j)
385 sum.sums[j][i] /= toli;
386 }
387
388
389 // Select a maximal subset C of the coarsening groups, such that for
390 // each variable the sum of C's relative errors is below 1.
391 //
392 // A greedy algorithm may work as follows: Maintain a vector E_i (of
393 // size the number of variables) that contains the remaining allowed
394 // relative error. Of the remaining coarsening groups j with
395 // relative errors e_ji select the one for which max_i e_ji / E_i is
396 // minimal.
397 //
398 // However, this has quadratic complexity. Thus we resort to the
399 // following simplification. First we assign to each coarsening
400 // group j the maximal relative error e_j = max_i e_ji encountered
401 // in any variable. Subsequently, these are sorted in ascending
402 // order. Then the first k groups for which sum_{j=0}^k e_j <= 1
403 // hold are selected. Non-coarsenable cells (in the irregular
404 // coarsening group 0) are ignored.
405 //
406 // WARNING: Despite the fact that in principle we can compute the
407 // projection error exactly, the value we obtain is just an
408 // estimate. This is not only due to the heuristic suboptimal
409 // selection of C, but also due to the fact that the coarsening
410 // errors are modeled strictly locally. In conforming meshes, mesh
411 // adaptation can lead to nonlocal effects, e.g. the removal of no
412 // longer needed green closures (the introduced error is not taken
413 // into account), the introduction of green closures on newly
414 // coarsened cells (in which case the error could be below our
415 // estimate, depending on the actual transfer - currently the
416 // transfer is suboptimal and our error estimate should be exact),
417 // or a cell group can actually be retained for mesh topology
418 // reasons even if marked for coarsening (in which case the actual
419 // local error would be zero).
420 std::vector<std::pair<double,size_t>> e(coarseningGroupCount);
421 for (int j=0; j<coarseningGroupCount; ++j)
422 {
423 e[j].second = j+1; // omit the irregular coarsening group 0
424 for (int i=0; i<varDesc.noOfVariables; ++i)
425 e[j].first = std::max(e[j].first,sum.sums[j+1][i]);
426 }
427
428 std::sort(e.begin(),e.end(),FirstLess());
429
430 std::vector<size_t> selectedCoarseningGroups;
431 double totalError = 0;
432 for (int i=0; i<e.size(); ++i) {
433 totalError += e[i].first;
434 if (totalError<=1)
435 selectedCoarseningGroups.push_back(e[i].second);
436 else
437 break;
438 }
439
440 // for compression: need to keep track of coarsening history
441 coarsenedCells.clear() ;
442 coarsenedCells.resize( indexSet.size(0), false ) ;
443 // no coarsening: just stop here
444 // return ;
445
446 // If no cell can be coarsened, we don't need to touch the grid.
447 if (selectedCoarseningGroups.empty())
448 return;
449
450 // Now we sweep over the whole grid and mark all cells for
451 // coarsening which belong to a selected coarsening group.
452 std::sort(selectedCoarseningGroups.begin(),selectedCoarseningGroups.end());
453 for (Cell const& c: Dune::elements(gridManager.grid().leafGridView()))
454 {
455 auto idx = indexSet.index(c);
456 if (c.level()>minRefLevel && std::binary_search(selectedCoarseningGroups.begin(),selectedCoarseningGroups.end(),
457 coarseningGroup[idx])) // for uniform refinement: just delete level
458 {
459 coarsenedCells[idx] = true ; // for compression: coarsening history
460 gridManager.mark(-1,c);
461 }
462 }
463
464 gridManager.adaptAtOnce();
465 }
466} /* end of namespace Kaskade */
467
468#endif
Function is the interface for evaluatable functions .
Definition: concepts.hh:324
GroupByCell(std::vector< size_t > const &groups_)
Definition: coarsening.hh:239
size_t operator[](size_t idx) const
Definition: coarsening.hh:244
A LAPACK-compatible dense matrix class with shape specified at runtime.
void setSize(size_type r, size_type c)
Resizes the matrix to r x c, leaving the entries in an undefined state.
Grid const & grid() const
Returns a const reference on the owned grid.
Definition: gridmanager.hh:292
bool adaptAtOnce()
DEPRECATED Performs grid refinement without prolongating registered FE functions.
Definition: gridmanager.hh:390
bool mark(int refCount, typename Grid::template Codim< 0 >::Entity const &cell)
Marks the given cell for refinement or coarsening.
Definition: gridmanager.hh:330
Class that takes care of the transfer of data (of each FunctionSpaceElement) after modification of th...
Definition: gridmanager.hh:680
A class that stores information about a set of variables.
Definition: variables.hh:537
IndexSet const & indexSet
index set
Definition: variables.hh:820
static int const noOfVariables
Number of variables in this set.
Definition: variables.hh:578
Spaces spaces
A heterogeneous sequence of pointers to (const) spaces involved.
Definition: variables.hh:807
typename Variables_Detail::ImplicitIdVariableDescriptions< VariableList >::type Variables
type of boost::fusion vector of VariableDescription s
Definition: variables.hh:543
SpaceType< Spaces, 0 >::type::Grid Grid
Grid type.
Definition: variables.hh:571
A class for storing a heterogeneous collection of FunctionSpaceElement s.
Definition: variables.hh:341
A callable that allows to implement weighted (scaled) norms.
Definition: concepts.hh:405
Tools for transfer of data between grids.
void for_each2(Seq1 const &s1, Seq2 &s2, Func const &func)
Definition: fixfusion.hh:28
void coarsening(VariableSetDescription const &varDesc, typename VariableSetDescription::VariableSet const &sol, Scaling const &scaling, std::vector< std::pair< double, double > > const &tol, GridManager< typename VariableSetDescription::Grid > &gridManager, int verbosity=1, int minRefLevel=0)
Definition: coarsening.hh:263
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< T, n > max(Dune::FieldVector< T, n > x, Dune::FieldVector< T, n > const &y)
Componentwise maximum.
Definition: fixdune.hh:110
R power(R base, int exp)
Computes integral powers of values in a multiplicative half-group.
Definition: power.hh:30
R square(R x)
returns the square of its argument.
Definition: power.hh:49
ProjectCoefficients< Projectors > getCoefficientProjectors(Projectors const &ps)
Definition: coarsening.hh:232
void axpy(Dune::BCRSMatrix< Dune::FieldMatrix< Scalar, 1, 1 > > const &P, Dune::BlockVector< Dune::FieldVector< Scalar, n > > const &x, Dune::BlockVector< Dune::FieldVector< Scalar, n > > &y, Scalar alpha=1.0)
Compute . If resetSolution=true computes .
void scaledTwoNormSquared(Variables const &varDesc, Functions const &f, Spaces const &spaces, Scaling const &scaling, Collector &sum)
Evaluates the square of the scaled -norms of a set of functions.
void localProlongationMatrix(ChildSpace const &childSpace, Cell< typename ChildSpace::Grid > const &child, FatherSpace const &fatherSpace, Cell< typename FatherSpace::Grid > const &father, DynamicMatrix< Dune::FieldMatrix< typename ChildSpace::Scalar, 1, 1 > > &prolongation, typename ChildSpace::Mapper::ShapeFunctionSet const &childSfs, typename FatherSpace::Mapper::ShapeFunctionSet const &fatherSfs)
Computes a local prolongation matrix for global shape functions from father cell to child cell.
Definition: fetransfer.hh:195
boost::remove_pointer< typenameboost::remove_reference< SpacePtr >::type >::type Space
Definition: coarsening.hh:63
result< CreateProjection(SpacePtr)>::type operator()(SpacePtr space) const
Definition: coarsening.hh:68
GetLocalTransferProjection(Cell father_, std::vector< Cell > const &children_)
Definition: coarsening.hh:83
ProjectCoefficients(Projectors const &projectors_)
Definition: coarsening.hh:187
void operator()(Pair const &pair) const
Definition: coarsening.hh:192
DynamicMatrix< Dune::FieldMatrix< typename Space::Scalar, 1, 1 > > q
Definition: coarsening.hh:49
DynamicMatrix< Dune::FieldMatrix< typename Space::Scalar, 1, 1 > > pLocal
Definition: coarsening.hh:52
A Collector coalescing the information of multi-variable functions on cell groups.
Definition: errorest.hh:156
A comparator functor that supports sorting std::pair by their first component.
Definition: firstless.hh:22