IPPL (Independent Parallel Particle Layer)
IPPL
Loading...
Searching...
No Matches
Preconditioner.h
Go to the documentation of this file.
1//
2// General pre-conditioners for the pre-conditioned Conjugate Gradient Solver
3// such as Jacobi, Polynomial Newton, Polynomial Chebyshev, Richardson, Gauss-Seidel
4// provides a function operator() that returns the preconditioned field
5//
6
7#ifndef IPPL_PRECONDITIONER_H
8#define IPPL_PRECONDITIONER_H
9
10#include "Expression/IpplOperations.h" // get the function apply()
11
12// Expands to a lambda that acts as a wrapper for a differential operator
13// fun: the function for which to create the wrapper, such as ippl::laplace
14// type: the argument type, which should match the LHS type for the solver
15#define IPPL_SOLVER_OPERATOR_WRAPPER(fun, type) \
16 [](type arg) { \
17 return fun(arg); \
18 }
19
20namespace ippl {
21 template <typename Field>
23 constexpr static unsigned Dim = Field::dim;
24 using mesh_type = typename Field::Mesh_t;
25 using layout_type = typename Field::Layout_t;
26
28 : type_m("Identity") {}
29
30 preconditioner(std::string name)
31 : type_m(name) {}
32
33 virtual ~preconditioner() = default;
34
35 // Placeholder for the function operator, actually implemented in the derived classes
36 virtual Field operator()(Field& u) {
37 Field res = u.deepCopy();
38 return res;
39 }
40
41 // Placeholder for setting additional fields, actually implemented in the derived classes
42 virtual void init_fields(Field& b) {
43 Field res = b.deepCopy();
44 return;
45 }
46
47 std::string get_type() { return type_m; };
48
49 protected:
50 std::string type_m;
51 };
52
57 template <typename Field, typename InvDiagF>
58 struct jacobi_preconditioner : public preconditioner<Field> {
59 constexpr static unsigned Dim = Field::dim;
60 using mesh_type = typename Field::Mesh_t;
61 using layout_type = typename Field::Layout_t;
62
63 jacobi_preconditioner(InvDiagF&& inverse_diagonal, double w = 1.0)
64 : preconditioner<Field>("jacobi")
65 , w_m(w) {
66 inverse_diagonal_m = std::move(inverse_diagonal);
67 }
68
69 Field operator()(Field& u) override {
70 mesh_type& mesh = u.get_mesh();
71 layout_type& layout = u.getLayout();
72 Field res(mesh, layout);
73
74 res = inverse_diagonal_m(u);
75 res = w_m * res;
76 return res;
77 }
78
79 protected:
81 double w_m; // Damping factor
82 };
83
88 template <typename Field, typename OperatorF>
90 constexpr static unsigned Dim = Field::dim;
91 using mesh_type = typename Field::Mesh_t;
92 using layout_type = typename Field::Layout_t;
93
94 polynomial_newton_preconditioner(OperatorF&& op, double alpha, double beta,
95 unsigned int max_level = 6, double zeta = 1e-3,
96 double* eta = nullptr)
97 : preconditioner<Field>("polynomial_newton")
98 , alpha_m(alpha)
99 , beta_m(beta)
100 , level_m(max_level)
101 , zeta_m(zeta)
102 , eta_m(eta) {
103 op_m = std::move(op);
104 }
105
106 // Memory management is needed because of runtime defined eta_m
108 if (eta_m != nullptr) {
109 delete[] eta_m;
110 eta_m = nullptr;
111 }
112 }
113
115 : preconditioner<Field>("polynomial_newton")
116 , level_m(other.level_m)
117 , alpha_m(other.alpha_m)
118 , beta_m(other.beta_m)
119 , zeta_m(other.zeta_m)
120 , eta_m(other.eta_m) {
121 op_m = std::move(other.op_m);
122 }
123
127
128 Field recursive_preconditioner(Field& u, unsigned int level) {
129 mesh_type& mesh = u.get_mesh();
130 layout_type& layout = u.getLayout();
131 // Define etas if not defined yet
132 if (eta_m == nullptr) {
133 // Precompute the etas for later use
134 eta_m = new double[level_m + 1];
135 eta_m[0] = 2.0 / ((alpha_m + beta_m) * (1.0 + zeta_m));
136 if (level_m > 0) {
137 eta_m[1] =
138 2.0
139 / (1.0 + 2 * alpha_m * eta_m[0] - alpha_m * eta_m[0] * alpha_m * eta_m[0]);
140 }
141 for (unsigned int i = 2; i < level_m + 1; ++i) {
142 eta_m[i] = 2.0 / (1.0 + 2 * eta_m[i - 1] - eta_m[i - 1] * eta_m[i - 1]);
143 }
144 }
145
146 Field res(mesh, layout);
147 // Base case
148 if (level == 0) {
149 res = eta_m[0] * u;
150 return res;
151 }
152 // Recursive case
153 Field PAPr(mesh, layout);
154 Field Pr(mesh, layout);
155
156 Pr = recursive_preconditioner(u, level - 1);
157 PAPr = op_m(Pr);
158 PAPr = recursive_preconditioner(PAPr, level - 1);
159 res = eta_m[level] * (2.0 * Pr - PAPr);
160 return res;
161 }
162
164
165 protected:
166 OperatorF op_m; // Operator to be preconditioned
167 double alpha_m; // Smallest Eigenvalue
168 double beta_m; // Largest Eigenvalue
169 unsigned int level_m; // Number of recursive calls
170 double zeta_m; // smallest (alpha + beta) is multiplied by (1+zeta) to avoid clustering of
171 // Eigenvalues
172 double* eta_m = nullptr; // Size is determined at runtime
173 };
174
179 template <typename Field, typename OperatorF>
181 constexpr static unsigned Dim = Field::dim;
182 using mesh_type = typename Field::Mesh_t;
183 using layout_type = typename Field::Layout_t;
184
185 polynomial_chebyshev_preconditioner(OperatorF&& op, double alpha, double beta,
186 unsigned int degree = 63, double zeta = 1e-3)
187 : preconditioner<Field>("polynomial_chebyshev")
188 , alpha_m(alpha)
189 , beta_m(beta)
190 , degree_m(degree)
191 , zeta_m(zeta)
192 , rho_m(nullptr) {
193 op_m = std::move(op);
194 }
195
196 // Memory management is needed because of runtime defined rho_m
198 if (rho_m != nullptr) {
199 delete[] rho_m;
200 rho_m = nullptr;
201 }
202 }
203
205 : preconditioner<Field>("polynomial_chebyshev")
206 , degree_m(other.degree_m)
207 , theta_m(other.theta_m)
208 , sigma_m(other.sigma_m)
209 , delta_m(other.delta_m)
210 , alpha_m(other.delta_m)
211 , beta_m(other.delta_m)
212 , zeta_m(other.zeta_m)
213 , rho_m(other.rho_m) {
214 op_m = std::move(other.op_m);
215 }
216
221
222 Field operator()(Field& r) override {
223 mesh_type& mesh = r.get_mesh();
224 layout_type& layout = r.getLayout();
225
226 Field res(mesh, layout);
227 Field x(mesh, layout);
228 Field x_old(mesh, layout);
229 Field A(mesh, layout);
230 Field z(mesh, layout);
231
232 // Precompute the coefficients if not done yet
233 if (rho_m == nullptr) {
234 // Start precomputing the coefficients
235 theta_m = (beta_m + alpha_m) / 2.0 * (1.0 + zeta_m);
236 delta_m = (beta_m - alpha_m) / 2.0;
238
239 rho_m = new double[degree_m + 1];
240 rho_m[0] = 1.0 / sigma_m;
241 for (unsigned int i = 1; i < degree_m + 1; ++i) {
242 rho_m[i] = 1.0 / (2.0 * sigma_m - rho_m[i - 1]);
243 }
244 } // End of precomputing the coefficients
245
246 res = r.deepCopy();
247
248 x_old = r / theta_m;
249 A = op_m(r);
250 x = 2.0 * rho_m[1] / delta_m * (2.0 * r - A / theta_m);
251
252 if (degree_m == 0) {
253 return x_old;
254 }
255
256 if (degree_m == 1) {
257 return x;
258 }
259 for (unsigned int i = 2; i < degree_m + 1; ++i) {
260 A = op_m(x);
261 z = 2.0 / delta_m * (r - A);
262 res = rho_m[i] * (2 * sigma_m * x - rho_m[i - 1] * x_old + z);
263 x_old = x.deepCopy();
264 x = res.deepCopy();
265 }
266 return res;
267 }
268
269 protected:
270 OperatorF op_m;
271 double alpha_m;
272 double beta_m;
273 double delta_m;
274 double theta_m;
275 double sigma_m;
276 unsigned degree_m;
277 double zeta_m;
278 double* rho_m = nullptr; // Size is determined at runtime
279 };
280
284 template <typename Field, typename UpperAndLowerF, typename InvDiagF>
286 constexpr static unsigned Dim = Field::dim;
287 using mesh_type = typename Field::Mesh_t;
288 using layout_type = typename Field::Layout_t;
289
290 richardson_preconditioner(UpperAndLowerF&& upper_and_lower, InvDiagF&& inverse_diagonal,
291 unsigned innerloops = 5)
292 : preconditioner<Field>("Richardson")
293 , innerloops_m(innerloops) {
294 upper_and_lower_m = std::move(upper_and_lower);
295 inverse_diagonal_m = std::move(inverse_diagonal);
296 }
297
298 Field operator()(Field& r) override {
299 mesh_type& mesh = r.get_mesh();
300 layout_type& layout = r.getLayout();
301 Field g(mesh, layout);
302
303 g = 0;
304 for (unsigned int j = 0; j < innerloops_m; ++j) {
306 g = r - ULg_m;
307
308 // The inverse diagonal is applied to the
309 // vector itself to return the result usually.
310 // However, the operator for FEM already
311 // returns the result of inv_diag * itself
312 // due to the matrix-free evaluation.
313 // Therefore, we need this if to differentiate
314 // the two cases.
315 if constexpr (std::is_same_v<InvDiagF, std::function<double(Field)>>) {
316 g = inverse_diagonal_m(g) * g;
317 } else {
318 g = inverse_diagonal_m(g);
319 }
320 }
321 return g;
322 }
323
324 void init_fields(Field& b) override {
325 layout_type& layout = b.getLayout();
326 mesh_type& mesh = b.get_mesh();
327
328 ULg_m = Field(mesh, layout);
329 }
330
331 protected:
332 UpperAndLowerF upper_and_lower_m;
334 unsigned innerloops_m;
336 };
337
345 template <typename Field, typename OperatorF, typename InvDiagF>
347 constexpr static unsigned Dim = Field::dim;
348 using mesh_type = typename Field::Mesh_t;
349 using layout_type = typename Field::Layout_t;
350
351 richardson_preconditioner_alt(OperatorF&& op, InvDiagF&& inverse_diagonal,
352 unsigned innerloops = 5)
353 : preconditioner<Field>("Richardson_alt")
354 , innerloops_m(innerloops) {
355 op_m = std::move(op);
356 inverse_diagonal_m = std::move(inverse_diagonal);
357 }
358
359 Field operator()(Field& r) override {
360 mesh_type& mesh = r.get_mesh();
361 layout_type& layout = r.getLayout();
362 Field g(mesh, layout);
363 Field g_old(mesh, layout);
364 g = 0;
365 g_old = 0;
366
367 for (unsigned int j = 0; j < innerloops_m; ++j) {
368 Ag_m = op_m(g);
369 g = r - Ag_m;
370
371 // The inverse diagonal is applied to the
372 // vector itself to return the result usually.
373 // However, the operator for FEM already
374 // returns the result of inv_diag * itself
375 // due to the matrix-free evaluation.
376 // Therefore, we need this if to differentiate
377 // the two cases.
378 if constexpr (std::is_same_v<InvDiagF, std::function<double(Field)>>) {
379 g = g_old + inverse_diagonal_m(g) * g;
380 } else {
381 g = g_old + inverse_diagonal_m(g);
382 }
383 g_old = g.deepCopy();
384 }
385 return g;
386 }
387
388 void init_fields(Field& b) override {
389 layout_type& layout = b.getLayout();
390 mesh_type& mesh = b.get_mesh();
391
392 Ag_m = Field(mesh, layout);
393 }
394
395 protected:
396 OperatorF op_m;
398 unsigned innerloops_m;
400 };
401
405 template <typename Field, typename LowerF, typename UpperF, typename InvDiagF>
406 struct gs_preconditioner : public preconditioner<Field> {
407 constexpr static unsigned Dim = Field::dim;
408 using mesh_type = typename Field::Mesh_t;
409 using layout_type = typename Field::Layout_t;
410
411 gs_preconditioner(LowerF&& lower, UpperF&& upper, InvDiagF&& inverse_diagonal,
412 unsigned innerloops, unsigned outerloops)
413 : preconditioner<Field>("Gauss-Seidel")
414 , innerloops_m(innerloops)
415 , outerloops_m(outerloops) {
416 lower_m = std::move(lower);
417 upper_m = std::move(upper);
418 inverse_diagonal_m = std::move(inverse_diagonal);
419 }
420
421 Field operator()(Field& b) override {
422 layout_type& layout = b.getLayout();
423 mesh_type& mesh = b.get_mesh();
424
425 Field x(mesh, layout);
426
427 x = 0; // Initial guess
428
429 for (unsigned int k = 0; k < outerloops_m; ++k) {
430 UL_m = upper_m(x);
431 r_m = b - UL_m;
432 for (unsigned int j = 0; j < innerloops_m; ++j) {
433 UL_m = lower_m(x);
434 x = r_m - UL_m;
435 // The inverse diagonal is applied to the
436 // vector itself to return the result usually.
437 // However, the operator for FEM already
438 // returns the result of inv_diag * itself
439 // due to the matrix-free evaluation.
440 // Therefore, we need this if to differentiate
441 // the two cases.
442 if constexpr (std::is_same_v<InvDiagF, std::function<double(Field)>>) {
443 x = inverse_diagonal_m(x) * x;
444 } else {
445 x = inverse_diagonal_m(x);
446 }
447 }
448 UL_m = lower_m(x);
449 r_m = b - UL_m;
450 for (unsigned int j = 0; j < innerloops_m; ++j) {
451 UL_m = upper_m(x);
452 x = r_m - UL_m;
453 // The inverse diagonal is applied to the
454 // vector itself to return the result usually.
455 // However, the operator for FEM already
456 // returns the result of inv_diag * itself
457 // due to the matrix-free evaluation.
458 // Therefore, we need this if to differentiate
459 // the two cases.
460 if constexpr (std::is_same_v<InvDiagF, std::function<double(Field)>>) {
461 x = inverse_diagonal_m(x) * x;
462 } else {
463 x = inverse_diagonal_m(x);
464 }
465 }
466 }
467 return x;
468 }
469
470 void init_fields(Field& b) override {
471 layout_type& layout = b.getLayout();
472 mesh_type& mesh = b.get_mesh();
473
474 UL_m = Field(mesh, layout);
475 r_m = Field(mesh, layout);
476 }
477
478 protected:
479 LowerF lower_m;
480 UpperF upper_m;
482 unsigned innerloops_m;
483 unsigned outerloops_m;
486 };
487
491 template <typename Field, typename LowerF, typename UpperF, typename InvDiagF, typename DiagF>
492 struct ssor_preconditioner : public preconditioner<Field> {
493 constexpr static unsigned Dim = Field::dim;
494 using mesh_type = typename Field::Mesh_t;
495 using layout_type = typename Field::Layout_t;
496
497 ssor_preconditioner(LowerF&& lower, UpperF&& upper, InvDiagF&& inverse_diagonal,
498 DiagF&& diagonal, unsigned innerloops, unsigned outerloops,
499 double omega)
500 : preconditioner<Field>("ssor")
501 , innerloops_m(innerloops)
502 , outerloops_m(outerloops)
503 , omega_m(omega) {
504 lower_m = std::move(lower);
505 upper_m = std::move(upper);
506 inverse_diagonal_m = std::move(inverse_diagonal);
507 diagonal_m = std::move(diagonal);
508 }
509
510 Field operator()(Field& b) override {
511 static IpplTimings::TimerRef initTimer = IpplTimings::getTimer("SSOR Init");
512 IpplTimings::startTimer(initTimer);
513
514 double D;
515
516 layout_type& layout = b.getLayout();
517 mesh_type& mesh = b.get_mesh();
518
519 Field x(mesh, layout);
520
521 x = 0; // Initial guess
522
523 IpplTimings::stopTimer(initTimer);
524
525 static IpplTimings::TimerRef loopTimer = IpplTimings::getTimer("SSOR loop");
526 IpplTimings::startTimer(loopTimer);
527
528 // The inverse diagonal is applied to the
529 // vector itself to return the result usually.
530 // However, the operator for FEM already
531 // returns the result of inv_diag * itself
532 // due to the matrix-free evaluation.
533 // Therefore, we need this if to differentiate
534 // the two cases.
535 for (unsigned int k = 0; k < outerloops_m; ++k) {
536 if constexpr (std::is_same_v<InvDiagF, std::function<double(Field)>>) {
537 UL_m = upper_m(x);
538 D = diagonal_m(x);
539 r_m = omega_m * (b - UL_m) + (1.0 - omega_m) * D * x;
540
541 for (unsigned int j = 0; j < innerloops_m; ++j) {
542 UL_m = lower_m(x);
543 x = r_m - omega_m * UL_m;
544 x = inverse_diagonal_m(x) * x;
545 }
546 UL_m = lower_m(x);
547 D = diagonal_m(x);
548 r_m = omega_m * (b - UL_m) + (1.0 - omega_m) * D * x;
549 for (unsigned int j = 0; j < innerloops_m; ++j) {
550 UL_m = upper_m(x);
551 x = r_m - omega_m * UL_m;
552 x = inverse_diagonal_m(x) * x;
553 }
554 } else {
555 UL_m = upper_m(x);
556 r_m = omega_m * (b - UL_m) + (1.0 - omega_m) * diagonal_m(x);
557
558 for (unsigned int j = 0; j < innerloops_m; ++j) {
559 UL_m = lower_m(x);
560 x = r_m - omega_m * UL_m;
561 x = inverse_diagonal_m(x);
562 }
563 UL_m = lower_m(x);
564 r_m = omega_m * (b - UL_m) + (1.0 - omega_m) * diagonal_m(x);
565 for (unsigned int j = 0; j < innerloops_m; ++j) {
566 UL_m = upper_m(x);
567 x = r_m - omega_m * UL_m;
568 x = inverse_diagonal_m(x);
569 }
570 }
571 }
572 IpplTimings::stopTimer(loopTimer);
573 return x;
574 }
575
576 void init_fields(Field& b) override {
577 layout_type& layout = b.getLayout();
578 mesh_type& mesh = b.get_mesh();
579
580 UL_m = Field(mesh, layout);
581 r_m = Field(mesh, layout);
582 }
583
584 protected:
585 LowerF lower_m;
586 UpperF upper_m;
589 unsigned innerloops_m;
590 unsigned outerloops_m;
591 double omega_m;
594 };
595
603 template <typename Field, typename Functor>
604 double powermethod(Functor&& f, Field& x_0, unsigned int max_iter = 5000, double tol = 1e-3) {
605 unsigned int i = 0;
606 using mesh_type = typename Field::Mesh_t;
607 using layout_type = typename Field::Layout_t;
608 mesh_type& mesh = x_0.get_mesh();
609 layout_type& layout = x_0.getLayout();
610 Field x_new(mesh, layout);
611 Field x_diff(mesh, layout);
612 double error = 1.0;
613 double lambda = 1.0;
614 while (error > tol && i < max_iter) {
615 x_new = f(x_0);
616 lambda = norm(x_new);
617 x_diff = x_new - lambda * x_0;
618 error = norm(x_diff);
619 x_new = x_new / lambda;
620 x_0 = x_new.deepCopy();
621 ++i;
622 }
623 if (i == max_iter) {
624 std::cerr << "Powermethod did not converge, lambda_max : " << lambda
625 << ", error : " << error << std::endl;
626 }
627 return lambda;
628 }
629
638 template <typename Field, typename Functor>
639 double adapted_powermethod(Functor&& f, Field& x_0, double lambda_max,
640 unsigned int max_iter = 5000, double tol = 1e-3) {
641 unsigned int i = 0;
642 using mesh_type = typename Field::Mesh_t;
643 using layout_type = typename Field::Layout_t;
644 mesh_type& mesh = x_0.get_mesh();
645 layout_type& layout = x_0.getLayout();
646 Field x_new(mesh, layout);
647 Field x_diff(mesh, layout);
648 double error = 1.0;
649 double lambda = 1.0;
650 while (error > tol && i < max_iter) {
651 x_new = f(x_0);
652 x_new = x_new - lambda_max * x_0;
653 lambda = -norm(x_new); // We know that lambda < 0;
654 x_diff = x_new - lambda * x_0;
655 error = norm(x_diff);
656 x_new = x_new / -lambda;
657 x_0 = x_new.deepCopy();
658 ++i;
659 }
660 lambda = lambda + lambda_max;
661 if (i == max_iter) {
662 std::cerr << "Powermethod did not converge, lambda_min : " << lambda
663 << ", error : " << error << std::endl;
664 }
665 return lambda;
666 }
667
668 /*
669 // Use the powermethod to compute the eigenvalues if no analytical solution is known
670 beta = powermethod(std::move(op_m), x_0);
671 // Trick for computing the smallest Eigenvalue of SPD Matrix
672 alpha = adapted_powermethod(std::move(op_m), x_0, beta);
673 */
674
675} // namespace ippl
676
677#endif // IPPL_PRECONDITIONER_H
ippl::Field< T, Dim, Mesh_t< Dim >, Centering_t< Dim >, ViewArgs... > Field
Definition datatypes.h:29
Definition Archive.h:20
double powermethod(Functor &&f, Field &x_0, unsigned int max_iter=5000, double tol=1e-3)
double adapted_powermethod(Functor &&f, Field &x_0, double lambda_max, unsigned int max_iter=5000, double tol=1e-3)
T norm(const FEMVector< T > &v, int p=2)
Definition FEMVector.h:425
Layout_t & getLayout() const
Definition BareField.h:134
Field deepCopy() const
Definition Field.hpp:26
KOKKOS_INLINE_FUNCTION Mesh_t & get_mesh() const
Definition Field.h:64
virtual ~preconditioner()=default
static constexpr unsigned Dim
preconditioner(std::string name)
typename Field::Layout_t layout_type
virtual Field operator()(Field &u)
virtual void init_fields(Field &b)
std::string get_type()
typename Field::Mesh_t mesh_type
jacobi_preconditioner(InvDiagF &&inverse_diagonal, double w=1.0)
typename Field::Layout_t layout_type
static constexpr unsigned Dim
Field operator()(Field &u) override
typename Field::Mesh_t mesh_type
Field recursive_preconditioner(Field &u, unsigned int level)
typename Field::Layout_t layout_type
polynomial_newton_preconditioner(const polynomial_newton_preconditioner &other)
polynomial_newton_preconditioner & operator=(const polynomial_newton_preconditioner &other)
polynomial_newton_preconditioner(OperatorF &&op, double alpha, double beta, unsigned int max_level=6, double zeta=1e-3, double *eta=nullptr)
polynomial_chebyshev_preconditioner(const polynomial_chebyshev_preconditioner &other)
polynomial_chebyshev_preconditioner & operator=(const polynomial_chebyshev_preconditioner &other)
polynomial_chebyshev_preconditioner(OperatorF &&op, double alpha, double beta, unsigned int degree=63, double zeta=1e-3)
Field operator()(Field &r) override
void init_fields(Field &b) override
richardson_preconditioner(UpperAndLowerF &&upper_and_lower, InvDiagF &&inverse_diagonal, unsigned innerloops=5)
typename Field::Mesh_t mesh_type
static constexpr unsigned Dim
typename Field::Layout_t layout_type
richardson_preconditioner_alt(OperatorF &&op, InvDiagF &&inverse_diagonal, unsigned innerloops=5)
Field operator()(Field &r) override
void init_fields(Field &b) override
static constexpr unsigned Dim
typename Field::Layout_t layout_type
gs_preconditioner(LowerF &&lower, UpperF &&upper, InvDiagF &&inverse_diagonal, unsigned innerloops, unsigned outerloops)
typename Field::Layout_t layout_type
typename Field::Mesh_t mesh_type
Field operator()(Field &b) override
static constexpr unsigned Dim
void init_fields(Field &b) override
static constexpr unsigned Dim
ssor_preconditioner(LowerF &&lower, UpperF &&upper, InvDiagF &&inverse_diagonal, DiagF &&diagonal, unsigned innerloops, unsigned outerloops, double omega)
void init_fields(Field &b) override
Field operator()(Field &b) override
typename Field::Layout_t layout_type
typename Field::Mesh_t mesh_type
Timing::TimerRef TimerRef
static TimerRef getTimer(const char *nm)
static void stopTimer(TimerRef t)
static void startTimer(TimerRef t)