Fast Methods for Cosmological Simulations
FastSim serves as a tool for quick N-body simulations in modified gravity.
multigrid_solver.cpp
Go to the documentation of this file.
1 #include "multigrid_solver.h"
2 #include <iostream>
3 
4 #define BOOST_LOG_DYN_LINK 1
5 #include <boost/log/trivial.hpp>
6 
7 // Simple int-int a^b power-function
8 inline size_t power(size_t a, size_t b){
9  size_t res = 1;
10  for(size_t i = 0; i < b; i++) {
11  res *= a;
12  }
13 #ifdef _BOUNDSCHECK
14  assert( (pow(1.0*a,1.0*b) - double(res)) < 0.5);
15 #endif
16  return res;
17 }
18 
19 //================================================
20 // The L operator ( EOM is written as L(f) = 0 )
21 //================================================
22 
23 template<size_t NDIM, typename T>
24 T MultiGridSolver<NDIM,T>::l_operator(const size_t level, const std::vector<size_t>& index_list, const bool addsource, const T h) const
25 {
26  T l, source, kinetic;
27  size_t i = index_list[0];
28 
29  // Compute the standard kinetic term [D^2 f]
30  kinetic = -2.0 * NDIM * _f[level][ i ];
31  for(size_t k = 1; k < 2*NDIM + 1; k++){
32  kinetic += _f[level][ index_list[k] ];
33  }
34 
35  // The right hand side of the PDE
36  T *rho = _ext_field[0]->get_y(level);
37  source = rho[i];
38 
39  // Add the source term arising from restricting the equation down to the lower level
40  if( level > 0 && addsource ){
41  source += _source[level][i];
42  }
43 
44  // The full equation
45  l = kinetic/(h*h) - source;
46 
47  return l;
48 }
49 
50 //================================================
51 // The derivative of the L-operator dL/df
52 // or more accurately dL_{ijk..} / df_{ijk..}
53 //================================================
54 
55 template<size_t NDIM, typename T>
56 T MultiGridSolver<NDIM,T>::dl_operator(const size_t level, const std::vector<size_t>& index_list, const T h) const
57 {
58  // The derivtive dL/df
59  T dl = -2.0*NDIM/(h*h);
60 
61  // Sanity check
62  if(fabs(dl) < 1e-10){
63  BOOST_LOG_TRIVIAL(error) << "Error: dl close to 0" << std::endl;
64  exit(1);
65  }
66 
67  return dl;
68 }
69 
70 //================================================
71 // override for different method of updating,
72 // default solver use Newton`s method:
73 // f_new = f_old - L / dL/df
74 //================================================
75 
76 template<size_t NDIM, typename T>
77 T MultiGridSolver<NDIM,T>::upd_operator(const T f, const size_t level, const std::vector<size_t>& index_list, const T h) const
78 {
79  T l = l_operator(level, index_list, true, h);
80  T dl = dl_operator(level, index_list, h);
81  return f - l/dl;
82 }
83 
84 //================================================
85 // The driver routine for solving the PDE
86 //================================================
87 
88 template<size_t NDIM, typename T>
90  // Init some variables
91  _istep_vcycle = 0;
92  _tot_sweeps_domain_grid = 0;
93  _res_domain_array.clear();
94 
95  if(_verbose){
96  BOOST_LOG_TRIVIAL(debug) << std::endl;
97  BOOST_LOG_TRIVIAL(debug) << "===============================================================" << std::endl;
98  BOOST_LOG_TRIVIAL(debug) << "==> Starting multigrid solver" << std::endl;
99  BOOST_LOG_TRIVIAL(debug) << "===============================================================\n" << std::endl;
100  }
101 
102  // Pre-solve on domaingrid
103  solve_current_level(0);
104 
105  // Set the initial residual
106  _rms_res_i = _rms_res;
107  _rms_res_old = 0.0;
108 
109  // Check if we already have convergence
110  Exit_Status status = check_convergence();
111 
112  // The V-cycle
113  while(status == Exit_Status::ITERATE) {
114  ++_istep_vcycle;
115 
116  if(_verbose){
117  BOOST_LOG_TRIVIAL(debug) << std::endl;
118  BOOST_LOG_TRIVIAL(debug) << "===============================================================" << std::endl;
119  BOOST_LOG_TRIVIAL(debug) << "==> Starting V-cycle istep = " << _istep_vcycle << " Res = " << _rms_res << std::endl;
120  BOOST_LOG_TRIVIAL(debug) << "===============================================================\n" << std::endl;
121  }
122 
123  if (_Nlevel == 1)
124  // no V-cycle, solve on domaingrid
125  solve_current_level(0);
126  else
127  {// Go down to the bottom (from finest grid [0] to coarsest grid [_Nlevel-1])
128  recursive_go_down(0);
129 
130  // Go up to the top
131  recursive_go_up(_Nlevel-2);
132  }
133 
134  // Check for errors in the computation (NaN) and exit if true
135  _f.get_grid(0).check_for_nan(true);
136 
137  // Check for convergence
138  status = check_convergence();
139  }
140 
141  return status;
142 }
143 
144 template<size_t NDIM, typename T>
146  return _f.get_y(level);
147 }
148 
149 template<size_t NDIM, typename T>
150 T const * MultiGridSolver<NDIM,T>::get_y(size_t level) const{
151  return _f.get_y(level);
152 }
153 
154 template<size_t NDIM, typename T>
155 void MultiGridSolver<NDIM,T>::set_epsilon(double eps_converge){
156  _eps_converge = eps_converge;
157 }
158 
159 template<size_t NDIM, typename T>
161  assert(field->get_Nlevel() >= _Nlevel);
162  _ext_field.push_back(field);
163 }
164 
165 template<size_t NDIM, typename T>
167  _maxsteps = maxsteps;
168 }
169 
170 template<size_t NDIM, typename T>
172  _conv_criterion_residual = use_residual;
173 }
174 
175 template<size_t NDIM, typename T>
176 void MultiGridSolver<NDIM,T>::set_ngs_sweeps(size_t ngs_fine, size_t ngs_coarse){
177  _ngs_fine = ngs_fine;
178  _ngs_coarse = ngs_coarse;
179 }
180 
181 template<size_t NDIM, typename T>
183  // Check that N is divisible by 2^{Nlevel - 1} which is required for the restriction to make sense
184  assert( ( _N / power(2, N - 1) ) * power(2, N - 1) == _N);
185 
186  // We should have atleast 1 level
187  assert(N >= 1);
188 
189  // set new _Nlevel
190  _Nlevel = N;
191 }
192 
193 template<size_t NDIM, typename T>
194 size_t MultiGridSolver<NDIM,T>::get_N(size_t level) const{
195  return _f.get_N(level);
196 }
197 
198 template<size_t NDIM, typename T>
199 size_t MultiGridSolver<NDIM,T>::get_Ntot(size_t level) const{
200  return _f.get_Ntot(level);
201 }
202 
203 template<size_t NDIM, typename T>
205  return _Nlevel;
206 }
207 
208 template<size_t NDIM, typename T>
209 MultiGridSolver<NDIM,T>::MultiGridSolver(size_t N, size_t Nmin, bool verbose) :
210  _N(N), _Ntot(power(N, NDIM)), _Nmin(Nmin), _Nlevel(int(log2(N / _Nmin) + 1)), _verbose(verbose),
211  _rms_res(0.0), _rms_res_i(0.0), _rms_res_old(0.0) {
212 
213  // Check that N is divisible by 2^{Nlevel - 1} which is required for the restriction to make sense
214  assert( ( _N / power(2, _Nlevel - 1) ) * power(2, _Nlevel - 1) == _N);
215 
216  // We should have atleast 1 level
217  assert(_Nlevel >= 1);
218 
219  // Allocate memory
223  }
224 
225 //================================================
226 // The initial guess for the solver at the
227 // domain level (level = 0)
228 //================================================
229 
230 template<size_t NDIM, typename T>
232  T *f = _f.get_y(0);
233  std::fill_n(f, _Ntot, guess);
234 }
235 
236 template<size_t NDIM, typename T>
238  T *f = _f.get_y(0);
239  std::copy( &guess[0], &guess[0] + _Ntot, &f[0] );
240 }
241 
242 template<size_t NDIM, typename T>
244  T *f = _f.get_y(0);
245  T *guess = guessgrid.get_y();
246  std::copy( &guess[0], &guess[0] + _Ntot, &f[0] );
247 }
248 
249 //================================================
250 // Given a cell i = (ix,iy,iz, ...) it computes
251 // the grid-index of the 2NDIM neighboring cells
252 // 0: (ix ,iy , iz, ...)
253 // 1: (ix-1,iy , iz, ...)
254 // 2: (ix+1,iy , iz, ...)
255 // 3: (ix, iy-1, iz, ...)
256 // 4: (ix, iy+1, iz, ...)
257 // ...
258 //
259 // Assuming periodic boundary conditions
260 //================================================
261 
262 template<size_t NDIM, typename T>
263 void MultiGridSolver<NDIM,T>::get_neighbor_gridindex(std::vector<size_t>& index_list, size_t i, size_t N){
264  index_list = std::vector<size_t>(2*NDIM+1);
265  index_list[0] = i;
266  for(size_t j = 0, n = 1; j < NDIM; j++, n *= N){
267  size_t ii = i/n % N;
268  size_t iminus = ii >= 1 ? ii - 1 : N - 1;
269  size_t iplus = ii <= N-2 ? ii + 1 : 0;
270  index_list[2*j+1] = i + (iminus - ii) * n;
271  index_list[2*j+2] = i + (iplus - ii) * n;
272  }
273 }
274 
275 //================================================
276 // Calculates the residual in each cell at
277 // a given level and stores it in [res]. Returns
278 // the rms-residual over the whole grid
279 //================================================
280 
281 template<size_t NDIM, typename T>
283  size_t N = get_N(level);
284  size_t Ntot = get_Ntot(level);
285 
286  // Gridspacing
287  const T h = 1.0/T( get_N(level) );
288 
289  // Calculate and store (minus) the residual in each cell
290 #ifdef OPENMP
291 #pragma omp parallel for
292 #endif
293  for (size_t i = 0; i < Ntot; i++) {
294  std::vector<size_t> index_list;
295  get_neighbor_gridindex(index_list, i, N);
296  res[i] = -l_operator(level, index_list, true, h);
297  }
298 
299  // Calculate and return RMS residual
300  return res.rms_norm();
301 }
302 
303 //================================================
304 // Criterion for defining convergence.
305 // Standard ways are: based on residual or
306 // the ratio of the residual to the initial
307 // residual (err).
308 //================================================
309 
310 template<size_t NDIM, typename T>
312  // Compute ratio of residual to initial residual
313  double err = _rms_res_i != 0.0 ? _rms_res/_rms_res_i : 1.0;
314  Exit_Status converged = Exit_Status::ITERATE;
315 
316  // Print out some information
317  if(_verbose){
318  BOOST_LOG_TRIVIAL(debug) << " Checking for convergence at step = " << _istep_vcycle << std::endl;
319  BOOST_LOG_TRIVIAL(debug) << " Residual = " << _rms_res << " Residual_old = " << _rms_res_old << std::endl;
320  BOOST_LOG_TRIVIAL(debug) << " Residual_i = " << _rms_res_i << " Err = " << err << std::endl;
321  }
322 
323  // Convergence criterion
325 
326  // Convergence criterion based on the residual
327  if(_rms_res < _eps_converge){
328  if(_verbose || true){
329  BOOST_LOG_TRIVIAL(debug) << std::endl;
330  BOOST_LOG_TRIVIAL(debug) << " The solution has converged res = " << _rms_res << " < " << _eps_converge << " istep = " << _istep_vcycle << "\n" << std::endl;
331  }
332  converged = Exit_Status::SUCCESS;
333  } else {
334  if(_verbose){
335  BOOST_LOG_TRIVIAL(debug) << " The solution has not yet converged res = " << _rms_res << " !< " << _eps_converge << std::endl;
336  }
337  }
338 
339  } else {
340 
341  // Convergence criterion based on the ratio of the residual
342  if(err < _eps_converge){
343  if(_verbose || true){
344  BOOST_LOG_TRIVIAL(debug) << std::endl;
345  BOOST_LOG_TRIVIAL(debug) << " The solution has converged err = " << err << " < " << _eps_converge << " ( res = " << _rms_res << " ) istep = " << _istep_vcycle << "\n" << std::endl;
346  }
347  converged = Exit_Status::SUCCESS;
348  } else {
349  if(_verbose){
350  BOOST_LOG_TRIVIAL(debug) << " The solution has not yet converged err = " << err << " !< " << _eps_converge << std::endl;
351  }
352  }
353  }
354 
355  if(_verbose && (_rms_res > _rms_res_old && _istep_vcycle > 1) ){
356  BOOST_LOG_TRIVIAL(debug) << " Warning: Residual_old > Residual" << std::endl;
357  }
358 
359  // Define converged if istep exceeds maxsteps to avoid infinite loop...
360  if(_istep_vcycle >= _maxsteps){
361  BOOST_LOG_TRIVIAL(debug) << " WARNING: MultigridSolver failed to converge! Reached istep = maxsteps = " << _maxsteps << std::endl;
362  BOOST_LOG_TRIVIAL(debug) << " res = " << _rms_res << " res_old = " << _rms_res_old << " res_i = " << _rms_res_i << std::endl;
363  converged = Exit_Status::MAX_STEPS;
364  }
365 
366  return converged;
367 }
368 
369 template<size_t NDIM, typename T>
371 
372 template<size_t NDIM, typename T>
374 
375 //================================================
376 // Prolonge up solution phi from course grid
377 // to fine grid. Using trilinear prolongation
378 //================================================
379 
380 template<size_t NDIM, typename T>
382  size_t twotondim = 1 << NDIM;
383  size_t NTop = get_N(to_level);
384  size_t NtotTop = get_Ntot(to_level);
385  size_t NBottom = NTop/2;
386 
387  // Compute NTop, Ntop^2, ... , Ntop^{Ndim-1} and similar for Nbottom
388  std::vector<size_t> nBottomPow(NDIM, 1);
389  std::vector<size_t> nTopPow(NDIM, 1);
390  for(size_t j = 0, n = 1, m = 1; j < NDIM; j++, n *= NBottom, m *= NTop){
391  nBottomPow[j] = n;
392  nTopPow[j] = m;
393  }
394 
395  // Trilinear prolongation
396 #ifdef OPENMP
397 #pragma omp parallel for
398 #endif
399  for (size_t i = 0; i < NtotTop; i++) {
400  std::vector<double> fac(NDIM, 0.0);
401  std::vector<int> iplus(NDIM, 0);
402 
403  double norm = 1.0;
404  int iBottom = 0;
405 
406  // Compute the shift in index from iBottom to the cells corresponding to ix -> ix+1
407  // The fac is the weight for the trilinear interpolation
408  for(size_t j = 0; j < NDIM; j++){
409  size_t ii = i/nTopPow[j] % NTop;
410  size_t iiBottom = ii/2;
411  iplus[j] = (iiBottom == NBottom-1 ? 1 - NBottom : 1) * nBottomPow[j];
412  fac[j] = ii % 2 == 0 ? 0.0 : 1.0;
413  iBottom += iiBottom * nBottomPow[j];
414  norm *= (1.0 + fac[j]);
415  }
416 
417  // Compute the sum Top[i] = Sum fac_i * Top[iBottom + d_i]
418  // + Sum fac_i fac_j * Top[iBottom + d_i + d_j]
419  // + Sum fac_i fac_j fac_k * Top[iBottom + d_i + d_j + d_k]
420  // + ... +
421  // + fac_1 ... fac_NDIM * Top[iBottom + d_1 + ... + d_NDIM]
422  Top[i] = Bottom[iBottom];
423  for(size_t k = 1; k < twotondim; k++){
424  double termfac = 1.0;
425  int iAdd = 0;
426  std::bitset<NDIM> bits = std::bitset<NDIM>(k);
427  for(size_t j = 0; j < NDIM; j++){
428  iAdd = bits[j] * iplus[j];
429  termfac *= 1.0 + bits[j] * (fac[j] - 1.0) ;
430  }
431  Top[i] += T(termfac) * Bottom[iBottom + iAdd];
432  }
433  Top[i] *= 1.0/T(norm);
434  }
435 }
436 
437 //================================================
438 // The Gauss-Seidel Sweeps with standard chess-
439 // board (first black then white) ordering of
440 // gridnodes if _ngridcolours = 2
441 //================================================
442 
443 template<size_t NDIM, typename T>
444 void MultiGridSolver<NDIM,T>::GaussSeidelSweep(size_t level, size_t curcolor, T *f){
445  size_t N = get_N(level);
446  size_t Ntot = get_Ntot(level);
447 
448  // Gridspacing
449  const T h = 1.0/T( get_N(level) );
450 
451 #ifdef OPENMP
452 #pragma omp parallel for
453 #endif
454  for (size_t i = 0; i < Ntot; i++) {
455  // Compute cell-color
456  size_t color = 0;
457  for(size_t j = 0, n = 1; j < NDIM; j++, n *= N){
458  color += ( i / n % N );
459  }
460  color = color % _ngridcolours;
461 
462  // Only select cells with right color
463  if( color == curcolor ){
464 
465  // Update the solution f = f - L / (dL/df)
466  std::vector<size_t> index_list;
467  get_neighbor_gridindex(index_list, i, N);
468  f[i] = upd_operator(f[i], level, index_list, h);
469  }
470  }
471 }
472 
473 //================================================
474 // Solve the equation on the current level
475 //================================================
476 
477 template<size_t NDIM, typename T>
479  size_t ngs_sweeps;
480 
481  if(_verbose)
482  BOOST_LOG_TRIVIAL(debug) << " Performing Newton-Gauss-Seidel sweeps at level " << level << std::endl;
483 
484  // Number of sweeps we do
485  if(level == 0)
486  ngs_sweeps = _ngs_fine;
487  else
488  ngs_sweeps = _ngs_coarse;
489 
490  // Do N Gauss-Seidel Sweeps
491  for (size_t i = 0; i < ngs_sweeps; i++) {
492  if(level == 0)
494 
495  // Sweep through grid according to sum of coord's mod _ngridcolours
496  // Standard is _ngridcolours = 2 -> chess-board ordering
497  for(size_t j = 0; j < _ngridcolours; j++)
498  GaussSeidelSweep(level, j, _f[level]);
499 
500  // Calculate residual and output quite often.
501  // For debug, but this is quite useful so keep it for now
502  if(_verbose){
503  if( (level > 0 && (i == 1 || i == ngs_sweeps-1) ) || (level == 0) ){
504  BOOST_LOG_TRIVIAL(debug) << " level = " << std::setw(5) << level << " NGS Sweep = " << std::setw(5) << i;
505  BOOST_LOG_TRIVIAL(debug) << " Residual = " << std::setw(10) << calculate_residual(level, _res.get_grid(level)) << std::endl;
506  }
507  }
508 
509  // Compute and store the residual after every sweep on the domaingrid
510  if(level == 0 && _store_all_residual){
511  _res_domain_array.push_back( calculate_residual(level, _res.get_grid(level)) );
512  }
513  }
514  if(_verbose) BOOST_LOG_TRIVIAL(debug) << std::endl;
515 
516  // Compute the residual
517  double curres = calculate_residual(level, _res.get_grid(level));
518 
519  // Store domaingrid residual
520  if (level == 0){
522  _rms_res = curres;
523  }
524 
525 }
526 
527 //================================================
528 // V-cycle go all the way up
529 //================================================
530 
531 template<size_t NDIM, typename T>
533  size_t from_level = to_level + 1;
534 
535  // Restrict down R[f] and store in _res (used as temp-array)
536  _f.restrict_down(to_level, _res.get_grid(from_level));
537  check_solution(from_level, _res.get_grid(from_level));
538 
539  // Make prolongation array ready at from_level
540  make_prolongation_array(_f.get_grid(from_level), _res.get_grid(from_level), _res.get_grid(from_level));
541 
542  // Prolonge up solution from-level to to-level and store in _res (used as temp array)
543  if(_verbose)
544  BOOST_LOG_TRIVIAL(debug) << " Prolonge solution from level: " << to_level+1 << " -> " << to_level << std::endl;
545  prolonge_up_array(to_level, _res.get_grid(from_level), _res.get_grid(to_level));
546 
547  // Correct solution at to_level (temp array _res contains the correction P[f-R[f]])
548  correct_sol(_f.get_grid(to_level), _res.get_grid(to_level), to_level);
549 
550  // Calculate new residual
551  calculate_residual(to_level, _res.get_grid(to_level));
552 
553  // Solve on the level we just went up to
554  solve_current_level(to_level);
555 
556  // Continue going up
557  if(to_level > 0)
558  recursive_go_up(to_level-1);
559  else {
560  return;
561  }
562 }
563 
564 template<size_t NDIM, typename T>
565 void MultiGridSolver<NDIM,T>::correct_sol(Grid<NDIM,T>& f, const Grid<NDIM,T>& corr, const size_t level)
566 {
567  f += corr;
568 }
569 
570 //================================================
571 // Make the array we are going to prolonge up
572 // Assumes [Rf] contains the restiction of f
573 // from the upper level and returns [df]
574 // containing df = f - R[f]
575 //================================================
576 
577 template<size_t NDIM, typename T>
579  size_t Ntot = f.get_Ntot();
580  df = f - Rf;
581 }
582 
583 //================================================
584 // Make new source
585 //================================================
586 
587 template<size_t NDIM, typename T>
589  size_t N = get_N(level);
590  size_t Ntot = get_Ntot(level);
591 
592  // Gridspacing
593  const T h = 1.0/T( get_N(level) );
594 
595  // Calculate the new source
596 #ifdef OPENMP
597 #pragma omp parallel for
598 #endif
599  for(size_t i = 0; i < Ntot; i++){
600  std::vector<size_t> index_list;
601  get_neighbor_gridindex(index_list, i, N);
602  T res = l_operator(level, index_list, false, h);
603  _source[level][i] = _res[level][i] + res;
604  }
605 }
606 
607 //================================================
608 // V-cycle go all the way down
609 //================================================
610 
611 template<size_t NDIM, typename T>
613  size_t to_level = from_level + 1;
614 
615  // Check if we are at the bottom
616  if(to_level >= _Nlevel) {
617  if(_verbose) {
618  BOOST_LOG_TRIVIAL(debug) << " - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -" << std::endl;
619  BOOST_LOG_TRIVIAL(debug) << " We have reached the bottom level = " << from_level << " Start going up." << std::endl;
620  BOOST_LOG_TRIVIAL(debug) << " - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n" << std::endl;
621  }
622  return;
623  }
624 
625  if(_verbose)
626  BOOST_LOG_TRIVIAL(debug) << " Going down from level " << from_level << " -> " << to_level << std::endl;
627 
628  // Restrict residual and solution
629  _res.restrict_down(from_level, _res.get_grid(to_level));
630  _f.restrict_down(from_level, _f.get_grid(to_level));
631  check_solution(to_level);
632 
633  // Make new source
634  make_new_source(to_level);
635 
636  // Solve on current level
637  solve_current_level(to_level);
638 
639  // Recursive call
640  recursive_go_down(to_level);
641 }
642 
643 template<size_t NDIM, typename T>
645  _N = _Ntot = _Nlevel = 0;
646  _f.clear();
647  _res.clear();
648  _source.clear();
649  _ext_field.clear();
650  _res_domain_array.clear();
651 }
652 
653 // Explicit template specialisation
654 template class MultiGridSolver<3,long double>;
655 
656 template class MultiGridSolver<3,double>;
657 template class MultiGridSolver<2,double>;
658 template class MultiGridSolver<1,double>;
659 template class MultiGridSolver<3,float>;
660 template class MultiGridSolver<2,float>;
661 template class MultiGridSolver<1,float>;
MultiGrid< NDIM, T > _source
Grid< NDIM, T > & get_grid(size_t level=0)
size_t _tot_sweeps_domain_grid
MultiGrid< NDIM, T > _f
void set_ngs_sweeps(size_t ngs_fine, size_t ngs_coarse)
T * get_y(size_t level=0)
void recursive_go_down(size_t from_level)
void make_prolongation_array(Grid< NDIM, T > &f, Grid< NDIM, T > &Rf, Grid< NDIM, T > &df)
void set_convergence_criterion_residual(bool use_residual)
size_t get_Nlevel() const
void set_epsilon(double eps_converge)
Exit_Status solve()
size_t get_N(size_t level=0) const
double calculate_residual(size_t level, Grid< NDIM, T > &res)
size_t get_Ntot(size_t level=0) const
Definition: grid.h:21
std::vector< MultiGrid< NDIM, T > * > _ext_field
void solve_current_level(size_t level)
virtual T upd_operator(const T f, const size_t level, const std::vector< size_t > &index_list, const T h) const
size_t get_Nlevel() const
Definition: multigrid.cpp:111
T * get_y()
Definition: grid.cpp:41
virtual T l_operator(const size_t level, const std::vector< size_t > &index_list, bool addsource, const T h) const
size_t get_Ntot() const
Definition: grid.cpp:141
void recursive_go_up(size_t to_level)
size_t power(size_t a, size_t b)
void set_maxsteps(size_t maxsteps)
void make_new_source(size_t level)
void prolonge_up_array(size_t to_level, Grid< NDIM, T > &BottomGrid, Grid< NDIM, T > &TopGrid)
void set_initial_guess(T guess)
void set_Nlevel(size_t N)
MultiGrid< NDIM, T > _res
float pow(float base, unsigned long int exp)
Definition: precision.hpp:39
void GaussSeidelSweep(size_t level, size_t curcolor, T *f)
static int m[2]
Definition: ccl_emu17.c:25
virtual Exit_Status check_convergence()
virtual void check_solution(size_t level, Grid< NDIM, T > &sol)
void add_external_grid(MultiGrid< NDIM, T > *field)
double rms_norm()
Definition: grid.h:238
std::vector< double > _res_domain_array
void get_neighbor_gridindex(std::vector< size_t > &index_list, size_t i, size_t ngrid)
virtual T dl_operator(const size_t level, const std::vector< size_t > &index_list, const T h) const
virtual void correct_sol(Grid< NDIM, T > &f, const Grid< NDIM, T > &corr, const size_t level)