The preceding chapter was devoted to the development of several multigrid schemes. We now turn to the practical issues of writing multigrid programs and determining whether they work. This will lead us to issues such as data structures, complexity, predictive tools, diagnostic tools, and performance.
Complexity
Writing multigrid programs can be both fun and challenging. The experience of many practitioners suggests that such programs should be highly modular. This allows them to evolve from simple relaxation programs and makes them much easier to check and debug. Also, the various components of the program (for example, relaxation, interpolation, and restriction subroutines) can be replaced individually.
Choosing a manageable data structure for a multigrid program is essential. Modern programming languages are replete with devices that make data management easy. For example, in most languages, one can declare a structure that groups together all the associated information for each grid level. In a structured language, for instance, a V-cycle could be written along the lines of the following pseudocode:
declare structure:
grid = { double Ddim_array f %% the right hand side
double Ddim_array v %% the current approximation }
declare Grid: array of structure grid
for j = 0 to coarsest - 1
Grid[j].v <- relax(Grid[j].v, Grid[j].f, num_sweeps_down)
Grid[j+1].f <- restrict(Grid[j].f - apply_operator(Grid[j].v))
endfor
Grid[coarsest].v = direct_solve(Grid[coarsest].v, Grid[coarsest].f)
for j = coarsest-1 to 1
Grid[j].v <- Grid[j].v + interpolate(Grid[j+1].v)
Grid[j].v <- relax(Grid[j].v, Grid[j].f, num_sweeps_down)
endfor