KASKADE 7 development version
agglomerationPreconditioner.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-2015 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 AGGLOMERATIONPRECONDITIONER_HH
14#define AGGLOMERATIONPRECONDITIONER_HH
15
16#include <fem/views.hh>
20
21
22namespace Kaskade
23{
27 namespace AgglomerationPreconditionerDetails
28 {
29 template <class Entry>
30 class AgglomerationPreconditionerEngine
31 {
32 using field_type = typename Entry::field_type;
33 static int const blocksize = Entry::rows;
34
35 public:
36 // Matrix has to satisfy the Dune::NumaBCRSMatrix interface
37 template <class Matrix, class Space>
38 AgglomerationPreconditionerEngine(Matrix const& A, Space const& space)
39 {
40 using std::begin; using std::end;
41
42 // The coarse grid projection is B = P^T A P, and its diagonal that we use here is
43 // B_{ii} = \sum_{k,l} (P^T)_{ik}^T A_{kl} P_{li}. Entries in the prolongation P are
44 // just ones (or identities in case of multicomponent systems), such that we can
45 // drop the transposition of the entries. Accessing P row-wise is more efficient in
46 // the CRS data structures, hence we write B_{ii} = \sum_{k,l} (P^T)_{ik} A_{kl} (P^T)_{il}.
47 // Let K_j denote the set of column indices of nonzero entries in the i-th row of P^T.
48 // Then we can write B_{ii} = \sum_{k,l\in K_i} A_{kl}.
49
50 // create prolongation
51 size_t const n = space.degreesOfFreedom(); // rows: total degrees of freedom
52 size_t const m = space.indexSet().size(0); // columns: number of cells
53 NumaCRSPatternCreator<> creator(n,m);
54 for (auto ci=space.gridView().template begin<0>(); ci!=space.gridView().template end<0>(); ++ci)
55 {
56 auto cidx = space.indexSet().index(*ci); // cell index is coarse (column) index
57 auto const& gidx = space.mapper().globalIndices(cidx); // FE indices are fine (row) indices
58 creator.addElements(begin(gidx),end(gidx),&cidx,&cidx+1,true); // scatter into the one column (col index is trivially sorted)
59 }
60 P = NumaBCRSMatrix<Entry>(std::make_shared<NumaCRSPattern<>>(creator), // create prolongation matrix with all
61 unitMatrix<typename Entry::field_type,blocksize>()); // nonzero entries equal to 1 (identity
62 // for multicomponent systems).
63 Pt = NumaBCRSMatrix<Entry>(P,false,true,false); // create transposed matrix
64
65 // create and invert diagonal of projected Galerkin matrix
66 inverseDiagonalPAP.resize(m,0);
67 for (size_t i=0; i<m; ++i)
68 {
69 auto const& row = P[i];
70 auto ki = rangeView(row.getindexptr(),row.getindexptr()+row.size()); // column indices of i-th row of P (sorted)
71
72 // sum up A_{kl} for k,l \in K_i
73 for (auto k: ki)
74 {
75 auto const& Ak = A[k];
76 auto al = begin(Ak); // iterator of row A_k
77 auto l = begin(ki); // iterator of K_i
78 while (al!=end(Ak) && l!=end(ki))
79 {
80 if (al.index()==*l) // match found A_{kl} with l \in K_i
81 {
82 inverseDiagonalPAP[i] += *al;
83 ++al;
84 ++l;
85 }
86 else if (*l < al.index()) // no match: advance smaller index
87 ++l;
88 else
89 ++al;
90 }
91 // invert (P^TAP)_ii
92 inverseDiagonalPAP[i].invert();
93 }
94 }
95 }
96
97 // adds P (P^TAP)^{-1} P^T y to x
98 template <class Domain, class Range>
99 void applyAgglomerated(Domain& x, Range const& y)
100 {
101 // Compute P (P^TAP)^{-1} P^T y
102 size_t const m = inverseDiagonalPAP.size();
104
105 Pt.mv(y,z); // z = P^T y
106 for (size_t i=0; i<m; ++i) // z = (P^TAP)^{-1} z
107 z[i] = inverseDiagonalPAP[i] * z[i]; // = (P^TAP)^{-1}P^T y
108 P.umv(z,x); // x <- P z = P(P^TAP)^{-1}P^T y
109 }
110
111 // adds P (P^TAP)^{-1} P^T y to x
112 template <class Domain, class Range>
113 field_type applyAgglomeratedDp(Domain& x, Range const& y)
114 {
115 applyAgglomerated(x,y);
116
117 field_type dp = 0; // compute dp = x^T y
118 for (size_t i=0; i<P.N(); ++i)
119 dp += x[i] * y[i];
120 return dp;
121 }
122
123 private:
124 std::vector<Entry> inverseDiagonalPAP; // diag(P^TAP)^{-1}
125 NumaBCRSMatrix<Entry> P, Pt; // prolongation P and restriction Pt, stored separately here because application of transpose is inefficient
126 };
127 }
128
156 template <class Operator>
157 class AgglomerationPreconditioner: public SymmetricPreconditioner<typename Operator::domain_type,typename Operator::range_type>
158 {
160 using typename Base::field_type;
161 using typename Base::domain_type;
162 using typename Base::range_type;
163
164 static int const blocksize = range_type::block_type::dimension;
165 static_assert(blocksize==domain_type::block_type::dimension,"Operator matrix entries must be square for Jacobi preconditioning.");
167
168
169 public:
170 static int const category = Dune::SolverCategory::sequential;
171
178 template <class Space>
179 AgglomerationPreconditioner(Operator const& opA, Space const& space)
180 : jac1(opA), // fine grid Jacobi preconditioner
181 engine(opA.getmat(),space)
182 {}
183
187 virtual void apply(domain_type& x, range_type const& y)
188 {
189 jac1.applyDp(x,y); // apply fine grid Jacobi
190 engine.apply(x,y); // apply agglomerated Jacobi
191 }
192
196 virtual field_type applyDp(domain_type& x, range_type const& y)
197 {
198 jac1.applyDp(x,y); // apply fine grid Jacobi
199 return engine.applyDp(x,y); // apply agglomerated Jacobi
200 }
201
205 virtual bool requiresInitializedInput() const
206 {
207 // we apply the fine grid Jacobi first, hence rely on that.
208 return jac1.requiresInitializedInput();
209 }
210
211 private:
212 JacobiPreconditioner<Operator> jac1; // diag(A)^{-1}
213 AgglomerationPreconditionerDetails::AgglomerationPreconditionerEngine<Entry> engine;
214 };
215
216
217
218 template <class Assembler,
219 int firstRow,
220 int firstCol>
221 class AgglomerationPreconditioner<AssembledGalerkinOperator<Assembler,firstRow,firstRow+1,firstCol,firstCol+1,
222 IstlInterfaceDetail::RangeBlockSelector<firstRow,firstRow+1,firstCol,firstCol+1>,
223 true>>
224 : public SymmetricPreconditioner<typename AssembledGalerkinOperator<Assembler,firstRow,firstRow+1,firstCol,firstCol+1,
225 IstlInterfaceDetail::RangeBlockSelector<firstRow,firstRow+1,firstCol,firstCol+1>,
226 true>::domain_type,typename AssembledGalerkinOperator<Assembler,firstRow,firstRow+1,firstCol,firstCol+1,
227 IstlInterfaceDetail::RangeBlockSelector<firstRow,firstRow+1,firstCol,firstCol+1>,
228 true>::range_type>
229 {
230 using Operator = AssembledGalerkinOperator<Assembler,firstRow,firstRow+1,firstCol,firstCol+1,
231 IstlInterfaceDetail::RangeBlockSelector<firstRow,firstRow+1,firstCol,firstCol+1>,
232 true>;
234 using typename Base::field_type;
235 using typename Base::domain_type;
236 using typename Base::range_type;
237
238 using RangeEntry = typename boost::fusion::result_of::value_at_c<typename range_type::Sequence,0>::type::block_type;
239 using DomainEntry = typename boost::fusion::result_of::value_at_c<typename domain_type::Sequence,0>::type::block_type;
240 static int const blocksize = RangeEntry::dimension;
241 static_assert(blocksize==DomainEntry::dimension,"Matrixe entries have to be square for Jacobi preconditioning");
243
244
245 public:
246 static int const category = Dune::SolverCategory::sequential;
247
254 template <class Space>
255 AgglomerationPreconditioner(Operator const& opA, Space const& space)
256 : jac1(opA), // fine grid Jacobi preconditioner
257 engine(opA.template get<NumaBCRSMatrix<Entry>>(),space)
258 {}
259
263 virtual void apply(domain_type& x, range_type const& y)
264 {
265 jac1.applyDp(x,y); // apply fine grid Jacobi
266 auto const& y0 = boost::fusion::at_c<0>(y.data);
267 auto& x0 = boost::fusion::at_c<0>(x.data);
268 engine.applyDp(x0,y0); // apply agglomerated Jacobi
269 }
270
274 virtual field_type applyDp(domain_type& x, range_type const& y)
275 {
276 jac1.applyDp(x,y); // apply fine grid Jacobi
277 auto const& y0 = boost::fusion::at_c<0>(y.data);
278 auto& x0 = boost::fusion::at_c<0>(x.data);
279 return engine.applyDp(x0,y0); // apply agglomerated Jacobi
280 }
281
285 virtual bool requiresInitializedInput() const
286 {
287 // we apply the fine grid Jacobi first, hence rely on that.
288 return jac1.requiresInitializedInput();
289 }
290
291 private:
292 JacobiPreconditioner<Operator> jac1; // diag(A)^{-1}
293 AgglomerationPreconditionerDetails::AgglomerationPreconditionerEngine<Entry> engine;
294 };
295
296
297}
298
299#endif
An agglomeration-based preconditioner for elliptic problems discretized with higher-order Lagrange fi...
AgglomerationPreconditioner(Operator const &opA, Space const &space)
Constructor.
virtual bool requiresInitializedInput() const
Returns true if the target vector x has to be initialized to zero before calling apply or applyDp.
virtual field_type applyDp(domain_type &x, range_type const &y)
Computes and returns .
virtual void apply(domain_type &x, range_type const &y)
Computes .
An adaptor presenting a Dune::LinearOperator <domain_type,range_type> interface to a contiguous sub-b...
A NUMA-aware compressed row storage matrix adhering mostly to the Dune ISTL interface (to complete....
Interface for symmetric preconditioners.
RangeView< It > rangeView(It first, It last)
Convenience function for constructing range views on the fly.
Definition: views.hh:59