IPPL (Independent Parallel Particle Layer)
IPPL
Loading...
Searching...
No Matches
PCG.h
Go to the documentation of this file.
1//
2// Class PCG
3// Preconditioned Conjugate Gradient solver algorithm
4//
5
6#ifndef IPPL_PCG_H
7#define IPPL_PCG_H
8
9#include "Preconditioner.h"
10#include "SolverAlgorithm.h"
11#include "FEM/FEMVector.h"
12
13namespace ippl {
14 template <typename OperatorRet, typename LowerRet, typename UpperRet, typename UpperLowerRet,
15 typename InverseDiagRet, typename DiagRet, typename FieldLHS,
16 typename FieldRHS = FieldLHS>
17 class CG : public SolverAlgorithm<FieldLHS, FieldRHS> {
19 typedef typename Base::lhs_type::value_type T;
20
21 public:
22 using typename Base::lhs_type, typename Base::rhs_type;
23 using OperatorF = std::function<OperatorRet(lhs_type)>;
24 using LowerF = std::function<LowerRet(lhs_type)>;
25 using UpperF = std::function<UpperRet(lhs_type)>;
26 using UpperLowerF = std::function<UpperLowerRet(lhs_type)>;
27 using InverseDiagF = std::function<InverseDiagRet(lhs_type)>;
28 using DiagF = std::function<DiagRet(lhs_type)>;
29
30 virtual ~CG() = default;
31
36 virtual void setOperator(OperatorF op) { op_m = std::move(op); }
37 virtual void setPreconditioner(
38 [[maybe_unused]] OperatorF&& op, // Operator passed to chebyshev and newton
39 [[maybe_unused]] LowerF&& lower, // Operator passed to 2-step gauss-seidel and ssor
40 [[maybe_unused]] UpperF&& upper, // Operator passed to 2-step gauss-seidel and ssor
41 [[maybe_unused]] UpperLowerF&&
42 upper_and_lower, // Operator passed to 2-step gauss-seidel
43 [[maybe_unused]] InverseDiagF&&
44 inverse_diagonal, // Operator passed to jacobi, 2-step gauss-seidel and ssor
45 [[maybe_unused]] DiagF&& diagonal, // Operator passed to SSOR
46 [[maybe_unused]] double alpha, // smallest eigenvalue of the operator
47 [[maybe_unused]] double beta, // largest eigenvalue of the operator
48 [[maybe_unused]] std::string preconditioner_type =
49 "", // Name of the preconditioner that should be used
50 [[maybe_unused]] int level =
51 5, // This is a dummy default parameter, actual default parameter should be
52 // set in main
53 [[maybe_unused]] int degree =
54 31, // This is a dummy default parameter, actual default parameter should
55 // be set in main
56 [[maybe_unused]] int richardson_iterations =
57 1, // This is a dummy default parameter, actual default
58 // parameter should be set in main
59 [[maybe_unused]] int inner =
60 5, // This is a dummy default parameter, actual default parameter should be
61 // set in main
62 [[maybe_unused]] int outer =
63 1, // This is a dummy default parameter, actual default parameter should be
64 [[maybe_unused]] double omega =
65 1 // This is a dummy default parameter, actual default parameter should be
66 // set in main
67 ) {}
68
73 virtual int getIterationCount() { return iterations_m; }
74
75 virtual void operator()(lhs_type& lhs, rhs_type& rhs,
76 const ParameterList& params) override {
77 constexpr unsigned Dim = lhs_type::dim;
78 typename lhs_type::Mesh_t mesh = lhs.get_mesh();
79 typename lhs_type::Layout_t layout = lhs.getLayout();
80
81 iterations_m = 0;
82 const int maxIterations = params.get<int>("max_iterations");
83
84 // Variable names mostly based on description in
85 // https://www.cs.cmu.edu/~quake-papers/painless-conjugate-gradient.pdf
86 lhs_type r(mesh, layout);
87 lhs_type d(mesh, layout);
88
89 using bc_type = BConds<lhs_type, Dim>;
90 bc_type lhsBCs = lhs.getFieldBC();
91 bc_type bc;
92
93 bool allFacesPeriodic = true;
94 for (unsigned int i = 0; i < 2 * Dim; ++i) {
95 FieldBC bcType = lhsBCs[i]->getBCType();
96 if (bcType == PERIODIC_FACE) {
97 // If the LHS has periodic BCs, so does the residue
98 bc[i] = std::make_shared<PeriodicFace<lhs_type>>(i);
99 } else if (bcType & CONSTANT_FACE) {
100 // If the LHS has constant BCs, the residue is zero on the BCs
101 // Bitwise AND with CONSTANT_FACE will succeed for ZeroFace or ConstantFace
102 bc[i] = std::make_shared<ZeroFace<lhs_type>>(i);
103 allFacesPeriodic = false;
104 } else {
105 throw IpplException("PCG::operator()",
106 "Only periodic or constant BCs for LHS supported.");
107 return;
108 }
109 }
110
111 r = rhs - op_m(lhs);
112 d = r.deepCopy();
113 d.setFieldBC(bc);
114
115 T delta1 = innerProduct(r, d);
116 T delta0 = delta1;
117 residueNorm = std::sqrt(delta1);
118 const T tolerance = params.get<T>("tolerance") * norm(rhs);
119
120 lhs_type q(mesh, layout);
121
123 q = op_m(d);
124
125 T alpha = delta1 / innerProduct(d, q);
126 lhs = lhs + alpha * d;
127
128 // The exact residue is given by
129 // r = rhs - op_m(lhs);
130 // This correction is generally not used in practice because
131 // applying the Laplacian is computationally expensive and
132 // the correction does not have a significant effect on accuracy;
133 // in some implementations, the correction may be applied every few
134 // iterations to offset accumulated floating point errors
135 r = r - alpha * q;
136 delta0 = delta1;
137 delta1 = innerProduct(r, r);
138 T beta = delta1 / delta0;
139
140 residueNorm = std::sqrt(delta1);
141 d = r + beta * d;
142 ++iterations_m;
143 }
144
145 if (allFacesPeriodic) {
146 T avg = lhs.getVolumeAverage();
147 lhs = lhs - avg;
148 }
149 }
150
151 virtual T getResidue() const { return residueNorm; }
152
153 protected:
157 };
158
159
160 template <typename OperatorRet, typename LowerRet, typename UpperRet, typename UpperLowerRet,
161 typename InverseDiagRet, typename T>
162 class CG<OperatorRet, LowerRet, UpperRet, UpperLowerRet, InverseDiagRet, FEMVector<T>, FEMVector<T> >
163 : public SolverAlgorithm<FEMVector<T>, FEMVector<T>> {
165
166 public:
167 using typename Base::lhs_type, typename Base::rhs_type;
168 using OperatorF = std::function<OperatorRet(lhs_type)>;
169 using LowerF = std::function<LowerRet(lhs_type)>;
170 using UpperF = std::function<UpperRet(lhs_type)>;
171 using UpperLowerF = std::function<UpperLowerRet(lhs_type)>;
172 using InverseDiagF = std::function<InverseDiagRet(lhs_type)>;
173
174 virtual ~CG() = default;
175
180 virtual void setOperator(OperatorF op) { op_m = std::move(op); }
181 virtual void setPreconditioner(
182 [[maybe_unused]] OperatorF&& op, // Operator passed to chebyshev and newton
183 [[maybe_unused]] LowerF&& lower, // Operator passed to 2-step gauss-seidel
184 [[maybe_unused]] UpperF&& upper, // Operator passed to 2-step gauss-seidel
185 [[maybe_unused]] UpperLowerF&&
186 upper_and_lower, // Operator passed to 2-step gauss-seidel
187 [[maybe_unused]] InverseDiagF&&
188 inverse_diagonal, // Operator passed to jacobi and 2-step gauss-seidel
189 [[maybe_unused]] double alpha, // smallest eigenvalue of the operator
190 [[maybe_unused]] double beta, // largest eigenvalue of the operator
191 [[maybe_unused]] std::string preconditioner_type =
192 "", // Name of the preconditioner that should be used
193 [[maybe_unused]] int level =
194 5, // This is a dummy default parameter, actual default parameter should be
195 // set in main
196 [[maybe_unused]] int degree =
197 31, // This is a dummy default parameter, actual default parameter should
198 // be set in main
199 [[maybe_unused]] int richardson_iterations =
200 1, // This is a dummy default parameter, actual default
201 // parameter should be set in main
202 [[maybe_unused]] int inner =
203 5, // This is a dummy default parameter, actual default parameter should be
204 // set in main
205 [[maybe_unused]] int outer = 1 // This is a dummy default parameter, actual default
206 // parameter should be set in main
207 ) {}
208
213 virtual int getIterationCount() { return iterations_m; }
214
215 virtual void operator()(lhs_type& lhs, rhs_type& rhs,
216 const ParameterList& params) override {
217
218 //constexpr unsigned Dim = lhs_type::dim;
219 //typename lhs_type::Mesh_t mesh = lhs.get_mesh();
220 //typename lhs_type::Layout_t layout = lhs.getLayout();
221
222 iterations_m = 0;
223 const int maxIterations = params.get<int>("max_iterations");
224
225 // Variable names mostly based on description in
226 // https://www.cs.cmu.edu/~quake-papers/painless-conjugate-gradient.pdf
227 lhs_type r = lhs.deepCopy();
228 r = 0;
229 lhs_type d = lhs.deepCopy();
230 d = 0;
231
232
233 r = rhs - op_m(lhs);
234 r.setHalo(0);
235 d = r; //.deepCopy();
236 //d.setFieldBC(bc);
237 T delta1 = innerProduct(r, d);
238 T delta0 = delta1;
239 residueNorm = std::sqrt(delta1);
240 const T tolerance = params.get<T>("tolerance") * norm(rhs);
241
242 lhs_type q = lhs.deepCopy();
243 q = 0;
244
245
247 q = op_m(d);
248 d.setHalo(0);
249 T alpha = delta1 / innerProduct(d, q);
250 lhs = lhs + alpha * d;
251
252 // The exact residue is given by
253 // r = rhs - op_m(lhs);
254 // This correction is generally not used in practice because
255 // applying the Laplacian is computationally expensive and
256 // the correction does not have a significant effect on accuracy;
257 // in some implementations, the correction may be applied every few
258 // iterations to offset accumulated floating point errors
259 r = r - alpha * q;
260 delta0 = delta1;
261 r.setHalo(0);
262 delta1 = innerProduct(r, r);
263 T beta = delta1 / delta0;
264
265 residueNorm = std::sqrt(delta1);
266 d = r + beta * d;
267 ++iterations_m;
268
269
270 }
271 }
272
273 virtual T getResidue() const { return residueNorm; }
274
275 protected:
279 };
280
281
282 template <typename OperatorRet, typename LowerRet, typename UpperRet, typename UpperLowerRet,
283 typename InverseDiagRet, typename DiagRet, typename FieldLHS,
284 typename FieldRHS = FieldLHS>
285 class PCG : public CG<OperatorRet, LowerRet, UpperRet, UpperLowerRet, InverseDiagRet, DiagRet,
286 FieldLHS, FieldRHS> {
288 typedef typename Base::lhs_type::value_type T;
289
290 public:
291 using typename Base::lhs_type, typename Base::rhs_type;
292 using OperatorF = std::function<OperatorRet(lhs_type)>;
293 using LowerF = std::function<LowerRet(lhs_type)>;
294 using UpperF = std::function<UpperRet(lhs_type)>;
295 using UpperLowerF = std::function<UpperLowerRet(lhs_type)>;
296 using InverseDiagF = std::function<InverseDiagRet(lhs_type)>;
297 using DiagF = std::function<DiagRet(lhs_type)>;
298
300 : CG<OperatorRet, LowerRet, UpperRet, UpperLowerRet, InverseDiagRet, DiagRet, FieldLHS,
301 FieldRHS>()
302 , preconditioner_m(nullptr){};
303
309 OperatorF&& op, // Operator passed to chebyshev and newton
310 LowerF&& lower, // Operator passed to 2-step gauss-seidel
311 UpperF&& upper, // Operator passed to 2-step gauss-seidel
312 UpperLowerF&& upper_and_lower, // Operator passed to 2-step gauss-seidel
313 InverseDiagF&& inverse_diagonal, // Operator passed to jacobi and 2-step gauss-seidel
314 DiagF&& diagonal, // Operator passed to ssor
315 double alpha, // smallest eigenvalue of the operator
316 double beta, // largest eigenvalue of the operator
317 std::string preconditioner_type = "", // Name of the preconditioner that should be used
318 int level = 5, // This is a dummy default parameter, actual default parameter should be
319 // set in main
320 int degree = 31, // This is a dummy default parameter, actual default parameter should
321 // be set in main
322 int richardson_iterations = 4, // This is a dummy default parameter, actual default
323 // parameter should be set in main
324 int inner = 2, // This is a dummy default parameter, actual default parameter should be
325 // set in main
326 int outer = 2, // This is a dummy default parameter, actual default parameter should be
327 // set in main
328 double omega = 1.57079632679 // This is a dummy default parameter, actual default
329 // parameter should be set in main
330 // default = pi/2 as this was found optimal during hyperparameter scan for test case
331 // (see https://amas.web.psi.ch/people/aadelmann/ETH-Accel-Lecture-1/projectscompleted/cse/BSc-mbolliger.pdf)
332 ) override {
333 if (preconditioner_type == "jacobi") {
334 // Turn on damping parameter
335 /*
336 double w = 2.0 / ((alpha + beta));
337 preconditioner_m = std::move(std::make_unique<jacobi_preconditioner<FieldLHS ,
338 InverseDiagF>>(std::move(inverse_diagonal), w));
339 */
341 std::move(std::make_unique<jacobi_preconditioner<FieldLHS, InverseDiagF>>(
342 std::move(inverse_diagonal)));
343 } else if (preconditioner_type == "newton") {
344 preconditioner_m = std::move(
346 std::move(op), alpha, beta, level, 1e-3));
347 } else if (preconditioner_type == "chebyshev") {
348 preconditioner_m = std::move(
350 std::move(op), alpha, beta, degree, 1e-3));
351 } else if (preconditioner_type == "richardson") {
353 std::move(std::make_unique<
355 std::move(upper_and_lower), std::move(inverse_diagonal),
356 richardson_iterations));
357 } else if (preconditioner_type == "richardson_alt") {
359 std::move(std::make_unique<
361 std::move(op), std::move(inverse_diagonal),
362 richardson_iterations));
363 } else if (preconditioner_type == "gauss-seidel") {
364 preconditioner_m = std::move(
366 std::move(lower), std::move(upper), std::move(inverse_diagonal), inner,
367 outer));
368 } else if (preconditioner_type == "ssor") {
370 std::move(std::make_unique<
372 std::move(lower), std::move(upper), std::move(inverse_diagonal),
373 std::move(diagonal), inner, outer, omega));
374 } else {
375 preconditioner_m = std::move(std::make_unique<preconditioner<FieldLHS>>());
376 }
377 }
378
379 void operator()(lhs_type& lhs, rhs_type& rhs, const ParameterList& params) override {
380 constexpr unsigned Dim = lhs_type::dim;
381
382 if (preconditioner_m == nullptr) {
383 throw IpplException("PCG::operator()",
384 "Preconditioner has not been set for PCG solver");
385 }
386
387 typename lhs_type::Mesh_t mesh = lhs.get_mesh();
388 typename lhs_type::Layout_t layout = lhs.getLayout();
389
390 this->iterations_m = 0;
391 const int maxIterations = params.get<int>("max_iterations");
392
393 // Variable names mostly based on description in
394 // https://www.cs.cmu.edu/~quake-papers/painless-conjugate-gradient.pdf
395 lhs_type r(mesh, layout);
396 lhs_type d(mesh, layout);
397 lhs_type s(mesh, layout);
398 lhs_type q(mesh, layout);
399
400 preconditioner_m->init_fields(lhs);
401
402 using bc_type = BConds<lhs_type, Dim>;
403 bc_type lhsBCs = lhs.getFieldBC();
404 bc_type bc;
405
406 bool allFacesPeriodic = true;
407 for (unsigned int i = 0; i < 2 * Dim; ++i) {
408 FieldBC bcType = lhsBCs[i]->getBCType();
409 if (bcType == PERIODIC_FACE) {
410 // If the LHS has periodic BCs, so does the residue
411 bc[i] = std::make_shared<PeriodicFace<lhs_type>>(i);
412 } else if (bcType & CONSTANT_FACE) {
413 // If the LHS has constant BCs, the residue is zero on the BCs
414 // Bitwise AND with CONSTANT_FACE will succeed for ZeroFace or ConstantFace
415 bc[i] = std::make_shared<ZeroFace<lhs_type>>(i);
416 allFacesPeriodic = false;
417 } else {
418 throw IpplException("PCG::operator()",
419 "Only periodic or constant BCs for LHS supported.");
420 return;
421 }
422 }
423
424 r = rhs - this->op_m(lhs);
425 d = preconditioner_m->operator()(r);
426 d.setFieldBC(bc);
427
428 T delta1 = innerProduct(r, d);
429 T delta0 = delta1;
430 this->residueNorm = Kokkos::sqrt(Kokkos::abs(delta1));
431 const T tolerance = params.get<T>("tolerance") * this->residueNorm;
432
433 while (this->iterations_m < maxIterations && this->residueNorm > tolerance) {
434 q = this->op_m(d);
435 T alpha = delta1 / innerProduct(d, q);
436 lhs = lhs + alpha * d;
437
438 // The exact residue is given by
439 // r = rhs - BaseCG::op_m(lhs);
440 // This correction is generally not used in practice because
441 // applying the Laplacian is computationally expensive and
442 // the correction does not have a significant effect on accuracy;
443 // in some implementations, the correction may be applied every few
444 // iterations to offset accumulated floating point errors
445 r = r - alpha * q;
446 s = preconditioner_m->operator()(r);
447
448 delta0 = delta1;
449 delta1 = innerProduct(r, s);
450
451 T beta = delta1 / delta0;
452 this->residueNorm = Kokkos::sqrt(Kokkos::abs(delta1));
453
454 d = s + beta * d;
455 ++this->iterations_m;
456 }
457
458 if (allFacesPeriodic) {
459 T avg = lhs.getVolumeAverage();
460 lhs = lhs - avg;
461 }
462 }
463
464 protected:
465 std::unique_ptr<preconditioner<FieldLHS>> preconditioner_m;
466 };
467
468}; // namespace ippl
469
470#endif
constexpr unsigned Dim
Definition Archive.h:20
T innerProduct(const FEMVector< T > &a, const FEMVector< T > &b)
Calculate the inner product between two ippl::FEMVector(s).
Definition FEMVector.h:405
FieldBC
Definition BcTypes.h:33
@ PERIODIC_FACE
Definition BcTypes.h:34
@ CONSTANT_FACE
Definition BcTypes.h:35
T norm(const FEMVector< T > &v, int p=2)
Definition FEMVector.h:425
1D vector used in the context of FEM.
Definition FEMVector.h:32
static constexpr unsigned dim
Dummy parameter in order for the detail::Expression defined operators to work.
Definition FEMVector.h:43
FEMVector< T > deepCopy() const
Create a deep copy, where all the information of this vector is copied to a new one.
void setHalo(T setValue)
Set the halo cells to setValue.
Definition PCG.h:17
std::function< LowerRet(lhs_type)> LowerF
Definition PCG.h:24
virtual T getResidue() const
Definition PCG.h:151
std::function< OperatorRet(lhs_type)> OperatorF
Definition PCG.h:23
virtual ~CG()=default
std::function< InverseDiagRet(lhs_type)> InverseDiagF
Definition PCG.h:27
virtual int getIterationCount()
Definition PCG.h:73
virtual void setOperator(OperatorF op)
Definition PCG.h:36
std::function< UpperLowerRet(lhs_type)> UpperLowerF
Definition PCG.h:26
virtual void setPreconditioner(OperatorF &&op, LowerF &&lower, UpperF &&upper, UpperLowerF &&upper_and_lower, InverseDiagF &&inverse_diagonal, DiagF &&diagonal, double alpha, double beta, std::string preconditioner_type="", int level=5, int degree=31, int richardson_iterations=1, int inner=5, int outer=1, double omega=1)
Definition PCG.h:37
std::function< UpperRet(lhs_type)> UpperF
Definition PCG.h:25
FieldLHS lhs_type
virtual void operator()(lhs_type &lhs, rhs_type &rhs, const ParameterList &params) override
Definition PCG.h:75
std::function< DiagRet(lhs_type)> DiagF
Definition PCG.h:28
Base::lhs_type::value_type T
Definition PCG.h:19
SolverAlgorithm< FieldLHS, FieldRHS > Base
Definition PCG.h:18
virtual void operator()(lhs_type &lhs, rhs_type &rhs, const ParameterList &params) override
Definition PCG.h:215
virtual void setPreconditioner(OperatorF &&op, LowerF &&lower, UpperF &&upper, UpperLowerF &&upper_and_lower, InverseDiagF &&inverse_diagonal, double alpha, double beta, std::string preconditioner_type="", int level=5, int degree=31, int richardson_iterations=1, int inner=5, int outer=1)
Definition PCG.h:181
PCG()
Definition PCG.h:299
std::function< LowerRet(lhs_type)> LowerF
Definition PCG.h:293
void setPreconditioner(OperatorF &&op, LowerF &&lower, UpperF &&upper, UpperLowerF &&upper_and_lower, InverseDiagF &&inverse_diagonal, DiagF &&diagonal, double alpha, double beta, std::string preconditioner_type="", int level=5, int degree=31, int richardson_iterations=4, int inner=2, int outer=2, double omega=1.57079632679) override
Definition PCG.h:308
std::function< DiagRet(lhs_type)> DiagF
Definition PCG.h:297
void operator()(lhs_type &lhs, rhs_type &rhs, const ParameterList &params) override
Definition PCG.h:379
std::function< UpperRet(lhs_type)> UpperF
Definition PCG.h:294
Base::lhs_type::value_type T
Definition PCG.h:288
SolverAlgorithm< FieldLHS, FieldRHS > Base
Definition PCG.h:287
std::function< InverseDiagRet(lhs_type)> InverseDiagF
Definition PCG.h:296
std::function< UpperLowerRet(lhs_type)> UpperLowerF
Definition PCG.h:295
FieldLHS lhs_type
std::function< OperatorRet(lhs_type)> OperatorF
Definition PCG.h:292
std::unique_ptr< preconditioner< FieldRHS > > preconditioner_m
Definition PCG.h:465
T get(const std::string &key) const