#ifdef PETSC_RCS_HEADER
static char vcid[] = "$Id: gsles.c,v 1.5 2000/01/10 03:54:18 knepley Exp $";
#endif

#include "petscsles.h"            /*I "petscsles.h" I*/
#include "gsolver.h"              /*I "gsolver.h" I*/

#undef  __FUNCT__
#define __FUNCT__ "GVecSolutionKSPMonitor"
/*@
  GVecSolutionKSPMonitor - Monitors solution at each KSP iteration.

  Collective on KSP

  Input Parameters:
. ksp   - The Krylov subspace context
. it    - The number of iterations so far
. rnorm - The current (approximate) residual norm
. ctx   - A viewer

  Level: advanced

.keywords: grid vector, ksp, monitor, solution
.seealso: KSPDefaultMonitor(),KSPSetMonitor(),GVecResidualKSPMonitor(),
          GVecRhsKSPMonitor(), GVecErrorKSPMonitor()
@*/
int GVecSolutionKSPMonitor(KSP ksp, int it, PetscReal rnorm, void *ctx)
{
  PetscViewer viewer = (PetscViewer) ctx;
  Vec         x;
  int         ierr;

  PetscFunctionBegin;
  PetscValidHeaderSpecific(ksp,    KSP_COOKIE);
  PetscValidHeaderSpecific(viewer, PETSC_VIEWER_COOKIE);
  ierr = KSPBuildSolution(ksp, PETSC_NULL, &x);                                                           CHKERRQ(ierr);
  ierr = VecView(x, viewer);                                                                              CHKERRQ(ierr);
  PetscFunctionReturn(0);
}

#undef  __FUNCT__
#define __FUNCT__ "GVecResidualKSPMonitor"
/*@
  GVecResidualKSPMonitor - Monitors residual at each KSP iteration.

  Collective on KSP

  Input Parameters:
. ksp   - The Krylov subspace context
. it    - The number of iterations so far
. rnorm - The current (approximate) residual norm
. ctx   - A viewer

  Level: advanced

.keywords: grid vector, ksp, monitor, residual
.seealso: KSPDefaultMonitor(),KSPSetMonitor(),GVecSolutionKSPMonitor(),
          GVecRhsKSPMonitor(), GVecErrorKSPMonitor()
@*/
int GVecResidualKSPMonitor(KSP ksp, int it, PetscReal rnorm, void *ctx)
{
  PetscViewer viewer = (PetscViewer) ctx;
  Vec         r;
  int         ierr;

  PetscFunctionBegin;
  PetscValidHeaderSpecific(ksp,    KSP_COOKIE);
  PetscValidHeaderSpecific(viewer, PETSC_VIEWER_COOKIE);
  ierr = KSPBuildResidual(ksp, PETSC_NULL, PETSC_NULL, &r);                                               CHKERRQ(ierr);
  ierr = VecView(r, viewer);                                                                              CHKERRQ(ierr);
  PetscFunctionReturn(0);
}

#undef  __FUNCT__
#define __FUNCT__ "GVecRhsKSPMonitor"
/*@
  GVecRhsKSPMonitor - Displays the right hand side for the linear system at the first iteration.

  Collective on KSP

  Input Parameters:
. ksp   - The Krylov subspace context
. it    - The number of iterations so far
. rnorm - The current (approximate) residual norm
. ctx   - A viewer

  Level: advanced

.keywords: grid vector, ksp, monitor, rhs
.seealso: KSPDefaultMonitor(),KSPSetMonitor(),GVecSolutionKSPMonitor(),
          GVecResidualKSPMonitor(), GVecErrorKSPMonitor()
@*/
int GVecRhsKSPMonitor(KSP ksp, int it, PetscReal rnorm, void *ctx)
{
  PetscViewer viewer = (PetscViewer) ctx;
  Vec         b;
  int         ierr;

  PetscFunctionBegin;
  PetscValidHeaderSpecific(ksp,    KSP_COOKIE);
  PetscValidHeaderSpecific(viewer, PETSC_VIEWER_COOKIE);
  if (!it) {
    ierr = KSPGetRhs(ksp, &b);                                                                            CHKERRQ(ierr);
    ierr = VecView(b, viewer);                                                                            CHKERRQ(ierr);
  }
  PetscFunctionReturn(0);
}

#undef  __FUNCT__
#define __FUNCT__ "GVecErrorKSPMonitor"
/*@
  GVecErrorKSPMonitor - Displays the error at each iteration.

  Collective on KSP

  Input Parameters:
. ksp      - The Krylov subspace context
. it       - The number of iterations so far
. rnorm    - The current (approximate) residual norm
. errorCtx - A GVecErrorKSPMonitorCtx

  Notes: 
  The final argument to KSPSetMonitor() with this routine must be
  a pointer to a GVecErrorKSPMonitorCtx.

  Level: advanced

.keywords: grid vector, ksp, monitor, error
.seealso: KSPDefaultMonitor(),KSPSetMonitor(),,GVecSolutionKSPMonitor(),
          GVecResidualKSPMonitor(), GVecRhsKSPMonitor()
@*/
int GVecErrorKSPMonitor(KSP ksp, int it, PetscReal rnorm, void *errorCtx)
{
  GVecErrorKSPMonitorCtx *ctx  = (GVecErrorKSPMonitorCtx *) errorCtx;
  PetscScalar             mone = -1.0;
  GVec                    x, e;
  PetscReal               norm_2, norm_max;
  int                     ierr;

  PetscFunctionBegin;
  PetscValidHeaderSpecific(ksp, KSP_COOKIE);
  ierr = KSPBuildSolution(ksp, PETSC_NULL, &x);                                                           CHKERRQ(ierr);
  ierr = GVecGetWorkGVec(ctx->solution, &e);                                                              CHKERRQ(ierr);
  ierr = VecWAXPY(&mone, x, ctx->solution, e);                                                            CHKERRQ(ierr);
  ierr = VecView(e, ctx->error_viewer);                                                                   CHKERRQ(ierr);

  /* Compute 2-norm and max-norm of error */
  if (ctx->norm_error_viewer) {
    ierr = VecNorm(e, NORM_2, &norm_2);                                                                   CHKERRQ(ierr);
    ierr = VecNorm(e, NORM_MAX, &norm_max);                                                               CHKERRQ(ierr);
    if (ctx->isInner == PETSC_FALSE) {
      PetscViewerASCIIPrintf(ctx->norm_error_viewer, "Iteration %d residual norm %g error 2-norm %g error max-norm %g\n",
                             it, rnorm, norm_2, norm_max);
    } else {
      PetscViewerASCIIPrintf(ctx->norm_error_viewer, "  Inner iteration %d residual norm %g error 2-norm %g error max-norm %g\n",
                             it, rnorm, norm_2, norm_max);
    }
  }

  ierr = GVecRestoreWorkGVec(ctx->solution, &e);                                                          CHKERRQ(ierr);
  PetscFunctionReturn(0);
}

#include "petscmg.h"
#undef  __FUNCT__
#define __FUNCT__ "GVecPCMGSetMonitor"
/* ------------------------------------------------------------------------------*/
/*
  GVecPCMGSetMonitor - Sets GVec KSP monitors for each level of multigrid.

   Input Parameters:
.    pc - preconditioner context for multigrid
*/
int GVecPCMGSetMonitor(PC pc, int views)
{
  char         name[25];
  int          levels, i,ierr, solution,rhs,residual,all;
  SLES         subsles;
  KSP          ksp;
  PetscViewer *viewers;
  PetscTruth   match;

  PetscFunctionBegin;
  PetscValidHeaderSpecific(pc, PC_COOKIE);
  ierr = PetscTypeCompare((PetscObject) pc, PCMG, &match);                                                CHKERRQ(ierr);
  if (match == PETSC_FALSE) PetscFunctionReturn(0);

  solution = (views & GVECPCMGMONITOR_SOLUTION);
  residual = (views & GVECPCMGMONITOR_RESIDUAL);
  rhs      = (views & GVECPCMGMONITOR_RHS);
  all      = (solution != 0) + (residual != 0) + (rhs != 0);

  ierr = MGGetLevels(pc, &levels);                                                                        CHKERRQ(ierr);

  ierr = PetscMalloc(all*levels * sizeof(PetscViewer), &viewers);                                         CHKERRQ(ierr);

  for(i = 0; i < levels; i++) {
    ierr = MGGetSmoother(pc, i, &subsles);                                                                CHKERRQ(ierr);
    ierr = SLESGetKSP(subsles, &ksp);                                                                     CHKERRQ(ierr);
    if (rhs) {
      sprintf(name, "Right hand side %d",i);
      ierr = PetscViewerDrawOpen(PETSC_COMM_WORLD, 0, name, PETSC_DECIDE, PETSC_DECIDE, 200, 200, viewers); CHKERRQ(ierr);
      ierr = KSPSetMonitor(ksp, GVecRhsKSPMonitor, *viewers++, PETSC_NULL);                               CHKERRQ(ierr);
    }
    if (solution) {
      sprintf(name, "Solution %d",i);
      ierr = PetscViewerDrawOpen(PETSC_COMM_WORLD, 0, name, PETSC_DECIDE, PETSC_DECIDE, 200, 200, viewers); CHKERRQ(ierr);
      ierr = KSPSetMonitor(ksp, GVecSolutionKSPMonitor, *viewers++, PETSC_NULL);                          CHKERRQ(ierr);
    }
    if (residual) {
      sprintf(name, "Residual %d",i);
      ierr = PetscViewerDrawOpen(PETSC_COMM_WORLD, 0, name, PETSC_DECIDE, PETSC_DECIDE, 200, 200, viewers); CHKERRQ(ierr);
      ierr = KSPSetMonitor(ksp, GVecResidualKSPMonitor, *viewers++, PETSC_NULL);                          CHKERRQ(ierr);
    }
  }

  PetscFunctionReturn(0);
}

EXTERN_C_BEGIN
#undef  __FUNCT__
#define __FUNCT__ "GVecKSPOptionsChecker_Private"
int GVecKSPOptionsChecker_Private(KSP ksp)
{
  char       *prefix, string[64], *p;
  int         ierr, loc[4], nmax;
  MPI_Comm    comm;
  PetscViewer viewer;
  PetscDraw   draw;
  PetscTruth  opt;


  PetscFunctionBegin;
  PetscValidHeaderSpecific(ksp, KSP_COOKIE);
  ierr = KSPGetOptionsPrefix(ksp, &prefix);                                                               CHKERRQ(ierr);
  ierr = PetscObjectGetComm((PetscObject) ksp, &comm);                                                    CHKERRQ(ierr);

  nmax = 4;
  loc[0] = PETSC_DECIDE; loc[1] = PETSC_DECIDE; loc[2] = 300; loc[3] = 300;
  ierr = PetscOptionsGetIntArray(prefix, "-gvec_ksp_solutionmonitor", loc, &nmax, &opt);                  CHKERRQ(ierr);
  if (opt) {
    ierr = PetscViewerDrawOpen(comm, 0, 0, loc[0], loc[1], loc[2], loc[3], &viewer);                      CHKERRQ(ierr);
    ierr = PetscViewerDrawGetDraw(viewer, 0, &draw);                                                      CHKERRQ(ierr);
    ierr = PetscDrawSetTitle(draw, "Approx. Solution");                                                   CHKERRQ(ierr);
    PetscLogObjectParent(ksp, (PetscObject) viewer);
    ierr = KSPSetMonitor(ksp, GVecSolutionKSPMonitor, (void *) viewer, PETSC_NULL);                       CHKERRQ(ierr);
  }
  nmax = 4;
  loc[0] = PETSC_DECIDE; loc[1] = PETSC_DECIDE; loc[2] = 300; loc[3] = 300;
  ierr = PetscOptionsGetIntArray(prefix, "-gvec_ksp_residualmonitor", loc, &nmax, &opt);                  CHKERRQ(ierr);
  if (opt) {
    ierr = PetscViewerDrawOpen(comm, 0, 0, loc[0], loc[1], loc[2], loc[3], &viewer);                      CHKERRQ(ierr);
    ierr = PetscViewerDrawGetDraw(viewer, 0, &draw);                                                      CHKERRQ(ierr);
    ierr = PetscDrawSetTitle(draw, "Residual");                                                           CHKERRQ(ierr);
    PetscLogObjectParent(ksp, (PetscObject) viewer);
    ierr = KSPSetMonitor(ksp, GVecResidualKSPMonitor, (void *) viewer, PETSC_NULL);                       CHKERRQ(ierr);
  }
  nmax = 4;
  loc[0] = PETSC_DECIDE; loc[1] = PETSC_DECIDE; loc[2] = 300; loc[3] = 300;
  ierr = PetscOptionsGetIntArray(prefix, "-gvec_ksp_rhsmonitor", loc, &nmax, &opt);                       CHKERRQ(ierr);
  if (opt) {
    ierr = PetscViewerDrawOpen(comm, 0, 0, loc[0], loc[1], loc[2], loc[3], &viewer);                      CHKERRQ(ierr);
    ierr = PetscViewerDrawGetDraw(viewer, 0, &draw);                                                      CHKERRQ(ierr);
    ierr = PetscDrawSetTitle(draw, "Rhs");                                                                CHKERRQ(ierr);
    PetscLogObjectParent(ksp, (PetscObject) viewer);
    ierr = KSPSetMonitor(ksp, GVecRhsKSPMonitor, (void *) viewer, PETSC_NULL);                            CHKERRQ(ierr);
  }
  ierr = PetscOptionsGetString(prefix, "-gvec_mg_monitor", string, 64, &opt);                             CHKERRQ(ierr);
  if (opt) {
    PC  pc;
    int all = 0;

    ierr = PetscStrstr(string, "nosolution", &p);                                                         CHKERRQ(ierr);
    if (!p) all |= GVECPCMGMONITOR_SOLUTION;
    ierr = PetscStrstr(string, "noresidual", &p);                                                         CHKERRQ(ierr);
    if (!p) all |= GVECPCMGMONITOR_RESIDUAL;
    ierr = PetscStrstr(string, "norhs",      &p);                                                         CHKERRQ(ierr);
    if (!p) all |= GVECPCMGMONITOR_RHS;
    ierr = KSPGetPC(ksp, &pc);                                                                            CHKERRQ(ierr);
    ierr = GVecPCMGSetMonitor(pc, all);                                                                   CHKERRQ(ierr);
  }
  ierr = PetscOptionsHasName(PETSC_NULL, "-help", &opt);                                                  CHKERRQ(ierr);
  if (opt) {
    char *pprefix;
    int   len = 2;

    if (prefix != PETSC_NULL) {
      ierr = PetscStrlen(prefix, &len);                                                                   CHKERRQ(ierr);
    }
    ierr = PetscMalloc((len+2) * sizeof(char), &pprefix);                                                 CHKERRQ(ierr);
    PetscStrcpy(pprefix, "-");
    if (prefix != PETSC_NULL)
      PetscStrcat(pprefix, prefix);
    PetscPrintf(comm," Additional KSP Monitor options for grid vectors\n");
    PetscPrintf(comm,"   %sgvec_ksp_solutionmonitor\n", pprefix);
    PetscPrintf(comm,"   %sgvec_ksp_residualmonitor\n", pprefix);
    PetscPrintf(comm,"   %sgvec_ksp_rhsmonitor\n", pprefix);
    PetscPrintf(comm,"   %sgvec_mg_monitor [nosolution,norhs,noresidual]\n", pprefix);
    ierr = PetscFree(pprefix);                                                                            CHKERRQ(ierr);
  }
  PetscFunctionReturn(0);
}
EXTERN_C_END

/*--------------------------------------------------- Uzawa Methods --------------------------------------------------*/
#undef  __FUNCT__
#define __FUNCT__ "GMatCreateUzawa"
/*@
   GMatCreateUzawa - Creates a container matrix for the Uzawa system matrix B^T A^{-1} B.

   Input Parameter:
+  A    - The square (0,0) block of the original saddle-point matrix
.  B    - The rectangular (0,1) block of the original saddle-point matrix
-  sles - The solver used to invert A

   Output Parameter:
.  gmat - The discrete operator

  Level: advanced

.seealso: GVecCreate()
@*/
int GMatCreateUzawa(GMat A, GMat B, SLES sles, GMat *gmat)
{
  UzawaContext *uCtx;
  MPI_Comm      comm;
  Grid          grid, grid2;
  int           M, m, N, n;
  int           O, o, P, p;
  int           ierr;

  PetscFunctionBegin;
  PetscValidHeaderSpecific(A,    MAT_COOKIE);
  PetscValidHeaderSpecific(B,    MAT_COOKIE);
  PetscValidHeaderSpecific(sles, SLES_COOKIE);
  PetscValidPointer(gmat);
  ierr = GMatGetGrid(A, &grid);                                                                           CHKERRQ(ierr);
  ierr = GMatGetGrid(B, &grid2);                                                                          CHKERRQ(ierr);
  PetscValidHeaderSpecific(grid,  GRID_COOKIE);
  PetscValidHeaderSpecific(grid2, GRID_COOKIE);
  if (grid != grid2) SETERRQ(PETSC_ERR_ARG_INCOMP, "Matrices must all have the same underlying grid.");
  ierr = PetscNew(UzawaContext, &uCtx);                                                                   CHKERRQ(ierr);
  uCtx->A    = A;
  uCtx->B    = B;
  uCtx->sles = sles;
  ierr = MatGetSize(A, &M, &N);                                                                           CHKERRQ(ierr);
  ierr = MatGetLocalSize(A, &m, &n);                                                                      CHKERRQ(ierr);
  ierr = MatGetSize(B, &O, &P);                                                                           CHKERRQ(ierr);
  ierr = MatGetLocalSize(B, &o, &p);                                                                      CHKERRQ(ierr);
  if ((m != o) || (n != o) || (M != O) || (N != O)) {
    SETERRQ(PETSC_ERR_ARG_INCOMP, "Incommensurate sizes, B^T A B must be a legal matrix");
  }
  ierr = PetscObjectGetComm((PetscObject) grid, &comm);                                                   CHKERRQ(ierr);
  ierr = MatCreateShell(comm, p, p, P, P, uCtx, gmat);                                                    CHKERRQ(ierr);
  ierr = MatShellSetOperation(*gmat, MATOP_MULT, (void (*)(void)) GMatMatMultUzawa);                      CHKERRQ(ierr);
  ierr = VecCreateMPI(comm, m, M, &uCtx->work);                                                           CHKERRQ(ierr);
  ierr = VecDuplicate(uCtx->work, &uCtx->work2);                                                          CHKERRQ(ierr);
  PetscFunctionReturn(0);
}

#undef __FUNCT__  
#define __FUNCT__ "GMatMatMultUzawa"
/*@
  GMatMatMultUzawa - This function applies B^T A^{-1} B to a vector,
  which is the Schur complement matrix.

  Input Parameters:
+ mat - The grid matrix
- x   - The input grid vector

  Output Parameter:
. y - The output grid vector B^T A^{-1} B x

  Level: advanced

.seealso: GMatEvaluateOperatorGalerkin
@*/
int GMatMatMultUzawa(GMat mat, GVec x, GVec y)
{
  UzawaContext *ctx;
  int           its;
  int           ierr;

  PetscFunctionBegin;
  PetscValidHeaderSpecific(mat, MAT_COOKIE);
  PetscValidHeaderSpecific(x,   VEC_COOKIE);
  PetscValidHeaderSpecific(y,   VEC_COOKIE);
  ierr = MatShellGetContext(mat, (void **) &ctx);                                                         CHKERRQ(ierr);
  ierr = MatMult(ctx->B, x, ctx->work);                                                                   CHKERRQ(ierr);
  ierr = SLESSolve(ctx->sles, ctx->work, ctx->work2, &its);                                               CHKERRQ(ierr);
  ierr = MatMultTranspose(ctx->B, ctx->work2, y);                                                         CHKERRQ(ierr);
  PetscFunctionReturn(0);
}

#undef  __FUNCT__
#define __FUNCT__ "GMatDestroyUzawa"
/*@
   GMatDestroyUzawa - Destroys a container matrix for the Uzawa
   system matrix B^T A B.

   Input Parameter:
.  gmat - The container matrix from GMatCreateUzawa()

  Level: advanced

.seealso: GVecCreate()
@*/
int GMatDestroyUzawa(GMat gmat)
{
  UzawaContext *ctx;
  int           ierr;

  PetscFunctionBegin;
  PetscValidHeaderSpecific(gmat, MAT_COOKIE);

  ierr = MatShellGetContext(gmat, (void **) &ctx);                                                        CHKERRQ(ierr);
  if (ctx != PETSC_NULL) {
    ierr = VecDestroy(ctx->work);                                                                         CHKERRQ(ierr);
    ierr = VecDestroy(ctx->work2);                                                                        CHKERRQ(ierr);
    ierr = PetscFree(ctx);                                                                                CHKERRQ(ierr);
  }
  ierr = MatDestroy(gmat);                                                                                CHKERRQ(ierr);
  PetscFunctionReturn(0);
}
