blob: 0beacc790dc50539277cc4edf586d5fd9711ccd2 [file] [log] [blame]
/*BHEADER**********************************************************************
* (c) 1997 The Regents of the University of California
*
* See the file COPYRIGHT_and_DISCLAIMER for a complete copyright
* notice, contact person, and disclaimer.
*
* $Revision$
*********************************************************************EHEADER*/
/******************************************************************************
*
*
*****************************************************************************/
#include "headers.h"
#include "smg.h"
#define DEBUG 0
/*--------------------------------------------------------------------------
* hypre_SMGSolve
* This is the main solve routine for the Schaffer multigrid method.
* This solver works for 1D, 2D, or 3D linear systems. The dimension
* is determined by the hypre_StructStencilDim argument of the matrix
* stencil. The hypre_StructGridDim argument of the matrix grid is
* allowed to be larger than the dimension of the solver, and in fact,
* this feature is used in the smaller-dimensional solves required
* in the relaxation method for both the 2D and 3D algorithms. This
* allows one to do multiple 2D or 1D solves in parallel (e.g., multiple
* 2D solves, where the 2D problems are "stacked" planes in 3D).
* The only additional requirement is that the linear system(s) data
* be contiguous in memory.
*
* Notes:
* - Iterations are counted as follows: 1 iteration consists of a
* V-cycle plus an extra pre-relaxation. If the number of MG levels
* is equal to 1, then only the extra pre-relaxation step is done at
* each iteration. When the solver exits because the maximum number
* of iterations is reached, the last extra pre-relaxation is not done.
* This allows one to use the solver as a preconditioner for conjugate
* gradient and insure symmetry.
* - hypre_SMGRelax is the relaxation routine. There are different "data"
* structures for each call to reflect different arguments and parameters.
* One important parameter sets whether or not an initial guess of zero
* is to be used in the relaxation.
* - hypre_SMGResidual computes the residual, b - Ax.
* - hypre_SemiRestrict restricts the residual to the coarse grid.
* - hypre_SemiInterp interpolates the coarse error and adds it to the
* fine grid solution.
*
*--------------------------------------------------------------------------*/
int
hypre_SMGSolve( void *smg_vdata,
hypre_StructMatrix *A,
hypre_StructVector *b,
hypre_StructVector *x )
{
hypre_SMGData *smg_data = smg_vdata;
double tol = (smg_data -> tol);
int max_iter = (smg_data -> max_iter);
int rel_change = (smg_data -> rel_change);
int zero_guess = (smg_data -> zero_guess);
int num_levels = (smg_data -> num_levels);
int num_pre_relax = (smg_data -> num_pre_relax);
int num_post_relax = (smg_data -> num_post_relax);
hypre_IndexRef base_index = (smg_data -> base_index);
hypre_IndexRef base_stride = (smg_data -> base_stride);
hypre_StructMatrix **A_l = (smg_data -> A_l);
hypre_StructMatrix **PT_l = (smg_data -> PT_l);
hypre_StructMatrix **R_l = (smg_data -> R_l);
hypre_StructVector **b_l = (smg_data -> b_l);
hypre_StructVector **x_l = (smg_data -> x_l);
hypre_StructVector **r_l = (smg_data -> r_l);
hypre_StructVector **e_l = (smg_data -> e_l);
void **relax_data_l = (smg_data -> relax_data_l);
void **residual_data_l = (smg_data -> residual_data_l);
void **restrict_data_l = (smg_data -> restrict_data_l);
void **interp_data_l = (smg_data -> interp_data_l);
int logging = (smg_data -> logging);
double *norms = (smg_data -> norms);
double *rel_norms = (smg_data -> rel_norms);
double b_dot_b, r_dot_r, eps;
double e_dot_e, x_dot_x;
int i, l;
int ierr = 0;
#if DEBUG
char filename[255];
#endif
/*-----------------------------------------------------
* Initialize some things and deal with special cases
*-----------------------------------------------------*/
hypre_BeginTiming(smg_data -> time_index);
hypre_StructMatrixDestroy(A_l[0]);
hypre_StructVectorDestroy(b_l[0]);
hypre_StructVectorDestroy(x_l[0]);
A_l[0] = hypre_StructMatrixRef(A);
b_l[0] = hypre_StructVectorRef(b);
x_l[0] = hypre_StructVectorRef(x);
(smg_data -> num_iterations) = 0;
/* if max_iter is zero, return */
if (max_iter == 0)
{
/* if using a zero initial guess, return zero */
if (zero_guess)
{
hypre_StructVectorSetConstantValues(x, 0.0);
}
hypre_EndTiming(smg_data -> time_index);
return ierr;
}
/* part of convergence check */
if (tol > 0.0)
{
/* eps = (tol^2) */
b_dot_b = hypre_StructInnerProd(b_l[0], b_l[0]);
eps = tol*tol;
/* if rhs is zero, return a zero solution */
if (b_dot_b == 0.0)
{
hypre_StructVectorSetConstantValues(x, 0.0);
if (logging > 0)
{
norms[0] = 0.0;
rel_norms[0] = 0.0;
}
hypre_EndTiming(smg_data -> time_index);
return ierr;
}
}
/*-----------------------------------------------------
* Do V-cycles:
* For each index l, "fine" = l, "coarse" = (l+1)
*-----------------------------------------------------*/
for (i = 0; i < max_iter; i++)
{
/*--------------------------------------------------
* Down cycle
*--------------------------------------------------*/
/* fine grid pre-relaxation */
if (num_levels > 1)
{
hypre_SMGRelaxSetRegSpaceRank(relax_data_l[0], 0, 0);
hypre_SMGRelaxSetRegSpaceRank(relax_data_l[0], 1, 1);
}
hypre_SMGRelaxSetMaxIter(relax_data_l[0], num_pre_relax);
hypre_SMGRelaxSetZeroGuess(relax_data_l[0], zero_guess);
hypre_SMGRelax(relax_data_l[0], A_l[0], b_l[0], x_l[0]);
zero_guess = 0;
/* compute fine grid residual (b - Ax) */
hypre_SMGResidual(residual_data_l[0], A_l[0], x_l[0], b_l[0], r_l[0]);
/* convergence check */
if (tol > 0.0)
{
r_dot_r = hypre_StructInnerProd(r_l[0], r_l[0]);
if (logging > 0)
{
norms[i] = sqrt(r_dot_r);
if (b_dot_b > 0)
rel_norms[i] = sqrt(r_dot_r/b_dot_b);
else
rel_norms[i] = 0.0;
}
/* always do at least 1 V-cycle */
if ((r_dot_r/b_dot_b < eps) && (i > 0))
{
if (rel_change)
{
if ((e_dot_e/x_dot_x) < eps)
break;
}
else
{
break;
}
}
}
if (num_levels > 1)
{
/* restrict fine grid residual */
hypre_SemiRestrict(restrict_data_l[0], R_l[0], r_l[0], b_l[1]);
#if DEBUG
if(hypre_StructStencilDim(hypre_StructMatrixStencil(A)) == 3)
{
sprintf(filename, "zout_xdown.%02d", 0);
hypre_StructVectorPrint(filename, x_l[0], 0);
sprintf(filename, "zout_rdown.%02d", 0);
hypre_StructVectorPrint(filename, r_l[0], 0);
sprintf(filename, "zout_b.%02d", 1);
hypre_StructVectorPrint(filename, b_l[1], 0);
}
#endif
for (l = 1; l <= (num_levels - 2); l++)
{
/* pre-relaxation */
hypre_SMGRelaxSetRegSpaceRank(relax_data_l[l], 0, 0);
hypre_SMGRelaxSetRegSpaceRank(relax_data_l[l], 1, 1);
hypre_SMGRelaxSetMaxIter(relax_data_l[l], num_pre_relax);
hypre_SMGRelaxSetZeroGuess(relax_data_l[l], 1);
hypre_SMGRelax(relax_data_l[l], A_l[l], b_l[l], x_l[l]);
/* compute residual (b - Ax) */
hypre_SMGResidual(residual_data_l[l],
A_l[l], x_l[l], b_l[l], r_l[l]);
/* restrict residual */
hypre_SemiRestrict(restrict_data_l[l], R_l[l], r_l[l], b_l[l+1]);
#if DEBUG
if(hypre_StructStencilDim(hypre_StructMatrixStencil(A)) == 3)
{
sprintf(filename, "zout_xdown.%02d", l);
hypre_StructVectorPrint(filename, x_l[l], 0);
sprintf(filename, "zout_rdown.%02d", l);
hypre_StructVectorPrint(filename, r_l[l], 0);
sprintf(filename, "zout_b.%02d", l+1);
hypre_StructVectorPrint(filename, b_l[l+1], 0);
}
#endif
}
/*--------------------------------------------------
* Bottom
*--------------------------------------------------*/
hypre_SMGRelaxSetZeroGuess(relax_data_l[l], 1);
hypre_SMGRelax(relax_data_l[l], A_l[l], b_l[l], x_l[l]);
#if DEBUG
if(hypre_StructStencilDim(hypre_StructMatrixStencil(A)) == 3)
{
sprintf(filename, "zout_xbottom.%02d", l);
hypre_StructVectorPrint(filename, x_l[l], 0);
}
#endif
/*--------------------------------------------------
* Up cycle
*--------------------------------------------------*/
for (l = (num_levels - 2); l >= 1; l--)
{
/* interpolate error and correct (x = x + Pe_c) */
hypre_SemiInterp(interp_data_l[l], PT_l[l], x_l[l+1], e_l[l]);
hypre_StructAxpy(1.0, e_l[l], x_l[l]);
#if DEBUG
if(hypre_StructStencilDim(hypre_StructMatrixStencil(A)) == 3)
{
sprintf(filename, "zout_eup.%02d", l);
hypre_StructVectorPrint(filename, e_l[l], 0);
sprintf(filename, "zout_xup.%02d", l);
hypre_StructVectorPrint(filename, x_l[l], 0);
}
#endif
/* post-relaxation */
hypre_SMGRelaxSetRegSpaceRank(relax_data_l[l], 0, 1);
hypre_SMGRelaxSetRegSpaceRank(relax_data_l[l], 1, 0);
hypre_SMGRelaxSetMaxIter(relax_data_l[l], num_post_relax);
hypre_SMGRelaxSetZeroGuess(relax_data_l[l], 0);
hypre_SMGRelax(relax_data_l[l], A_l[l], b_l[l], x_l[l]);
}
/* interpolate error and correct on fine grid (x = x + Pe_c) */
hypre_SemiInterp(interp_data_l[0], PT_l[0], x_l[1], e_l[0]);
hypre_SMGAxpy(1.0, e_l[0], x_l[0], base_index, base_stride);
#if DEBUG
if(hypre_StructStencilDim(hypre_StructMatrixStencil(A)) == 3)
{
sprintf(filename, "zout_eup.%02d", 0);
hypre_StructVectorPrint(filename, e_l[0], 0);
sprintf(filename, "zout_xup.%02d", 0);
hypre_StructVectorPrint(filename, x_l[0], 0);
}
#endif
}
/* part of convergence check */
if ((tol > 0.0) && (rel_change))
{
if (num_levels > 1)
{
e_dot_e = hypre_StructInnerProd(e_l[0], e_l[0]);
x_dot_x = hypre_StructInnerProd(x_l[0], x_l[0]);
}
else
{
e_dot_e = 0.0;
x_dot_x = 1.0;
}
}
/* fine grid post-relaxation */
if (num_levels > 1)
{
hypre_SMGRelaxSetRegSpaceRank(relax_data_l[0], 0, 1);
hypre_SMGRelaxSetRegSpaceRank(relax_data_l[0], 1, 0);
}
hypre_SMGRelaxSetMaxIter(relax_data_l[0], num_post_relax);
hypre_SMGRelaxSetZeroGuess(relax_data_l[0], 0);
hypre_SMGRelax(relax_data_l[0], A_l[0], b_l[0], x_l[0]);
(smg_data -> num_iterations) = (i + 1);
}
hypre_EndTiming(smg_data -> time_index);
return ierr;
}