/*--------------------------------------------------------------------------*/
/* ALBERTA:  an Adaptive multi Level finite element toolbox using           */
/*           Bisectioning refinement and Error control by Residual          */
/*           Techniques for scientific Applications                         */
/*                                                                          */
/* file:     MG.c                                                           */
/*                                                                          */
/* description:  abstract framework for multigrid                           */
/*                                                                          */
/*--------------------------------------------------------------------------*/
/*                                                                          */
/*  authors:   Alfred Schmidt                                               */
/*             Zentrum fuer Technomathematik                                */
/*             Fachbereich 3 Mathematik/Informatik                          */
/*             Universitaet Bremen                                          */
/*             Bibliothekstr. 2                                             */
/*             D-28359 Bremen, Germany                                      */
/*                                                                          */
/*             Kunibert G. Siebert                                          */
/*             Institut fuer Mathematik                                     */
/*             Universitaet Augsburg                                        */
/*             Universitaetsstr. 14                                         */
/*             D-86159 Augsburg, Germany                                    */
/*                                                                          */
/*  http://www.mathematik.uni-freiburg.de/IAM/ALBERTA                       */
/*                                                                          */
/*  (c) by A. Schmidt and K.G. Siebert (1996-2003)                          */
/*                                                                          */
/*--------------------------------------------------------------------------*/

#include "alberta.h"

/****************************************************************************/
/* REAL recursive_MG_iteration(MULTI_GRID_INFO *mg_info, int level)         */ 
/*  return value:                                                           */ 
/*   residual after MG iteration, if level=max_level-1                      */ 
/*   0.0                          otherwise                                 */ 
/****************************************************************************/

static void recursive_MG_iteration(MULTI_GRID_INFO *mg_info, int level)
{
  FUNCNAME("recursive_MG_iteration");
  int  cycle;
  REAL resid;

  INFO(mg_info->info, 6,"on level %d\n", level);

  if (level <= mg_info->exact_level) {
    mg_info->exact_solver(mg_info, level);
  }
  else {
    if (mg_info->pre_smooth)
      mg_info->pre_smooth(mg_info, level, mg_info->n_pre_smooth);

    for (cycle = 0; cycle < mg_info->cycle; cycle++) {
      if ((cycle > 0) && mg_info->in_smooth)
	mg_info->in_smooth(mg_info, level, mg_info->n_in_smooth);
      mg_info->mg_restrict(mg_info, level);
      recursive_MG_iteration(mg_info, level-1);
      mg_info->mg_prolongate(mg_info, level);
    }

    if (mg_info->post_smooth)
      mg_info->post_smooth(mg_info, level, mg_info->n_post_smooth);
  }

  if (mg_info->info >= 6) {
    if (level < mg_info->mg_levels-1)
      resid = mg_info->mg_resid(mg_info, level);
    else
      resid = 0.0; /* would be uninitialized, otherwise */
    MSG("end resid on level %2d: %10.2le\n", level, resid);
  }
  return;
}




/****************************************************************************/
/*  int MG(MULTI_GRID_INFO *mg_info)                                        */ 
/*  return value:                                                           */ 
/*    -2:  NULL mg_info                                                      */ 
/*    -1:  error during init_multi_grid                                     */ 
/*     0:  initial residual <= tolerance                                    */ 
/*   n>0:  number of MG iterations done                                     */ 
/*   n>max_iter:  tolerance not reached after max_iter MG iterations        */ 
/****************************************************************************/

int MG(MULTI_GRID_INFO *mg_info)
{
  FUNCNAME("MG");
  int iter, level;
  REAL resid, resid_old;

  if (!mg_info) {
    ERROR("no mg_info\n");
    return(-2);
  }
  if (!(mg_info->mg_resid && mg_info->mg_restrict && mg_info->mg_prolongate
        && mg_info->exact_solver)) {
    ERROR("missing mg_info entry\n");
    return(-2);
  }

  if (mg_info->init_multi_grid) {
    if (mg_info->init_multi_grid(mg_info)) {
      INFO(mg_info->info, 1,"error in init_multi_grid\n");
      return(-1);
    }
  }

  if (mg_info->cycle > 1)
    INFO(mg_info->info, 2,"smooth=(%d,%d,%d), cycle=W(%d), exact=%d\n", 
			   mg_info->n_pre_smooth, mg_info->n_in_smooth,
			   mg_info->n_post_smooth,
			   mg_info->cycle, mg_info->exact_level);
  else
    INFO(mg_info->info, 2,"smooth=(%d,%d), cycle=V(%d), exact=%d\n", 
			   mg_info->n_pre_smooth, mg_info->n_post_smooth,
			   mg_info->cycle, mg_info->exact_level);

  level = mg_info->mg_levels-1;
  resid_old = mg_info->mg_resid(mg_info, level);
  INFO(mg_info->info, 2,"start    resid = %10.2le\n", resid_old);
  if (resid_old <= mg_info->tolerance) {
    INFO(mg_info->info, 1,"resid < tol; no MG iterations needed\n");
    if (mg_info->exit_multi_grid) mg_info->exit_multi_grid(mg_info);
    return(0);
  }

  for (iter = 0; iter < mg_info->max_iter; iter++)
  {
    recursive_MG_iteration(mg_info, level);
    resid = mg_info->mg_resid(mg_info, level);
    INFO(mg_info->info, 2,"iter %2d: resid = %10.2le, rate = %7.4lf\n",
			   iter+1, resid, resid/resid_old);
    resid_old = resid;
    if (resid <= mg_info->tolerance) {
      INFO(mg_info->info, 1,"convergence after iter %2d: resid = %10.2le\n",
			     iter+1, resid);
      break;
    }
  }

  if (mg_info->exit_multi_grid)
    mg_info->exit_multi_grid(mg_info);

  return(iter+1);
}
