// -------------------------------------------------------------
// PetscNonlinearSolverImplementation::p_solve
// -------------------------------------------------------------
void
PetscNonlinearSolverImplementation::p_solve(void)
{
  PetscErrorCode ierr(0);
  p_petsc_X = PETScVector(*p_X);
  int me(this->processor_rank());

  try {
    ierr = SNESSolve(p_snes, NULL, *p_petsc_X); CHKERRXX(ierr);
    SNESConvergedReason reason;
    PetscInt iter;
    ierr = SNESGetConvergedReason(p_snes, &reason); CHKERRXX(ierr);
    ierr = SNESGetIterationNumber(p_snes, &iter); CHKERRXX(ierr);

    std::string msg;
    if (reason < 0) {
      msg = 
        boost::str(boost::format("%d: PETSc SNES diverged after %d iterations, reason: %d") % 
                   me % iter % reason);
      throw Exception(msg);
    } else if (me == 0) {
      msg = 
        boost::str(boost::format("%d: PETSc SNES converged after %d iterations, reason: %d") % 
                   me % iter % reason);
      std::cerr << msg << std::endl;
    }
  } catch (const PETSC_EXCEPTION_TYPE& e) {
    throw PETScException(ierr, e);
  } catch (const Exception& e) {
    throw e;
  }
}
  /// Solve again w/ the specified RHS, put result in specified vector (specialized)
  void p_resolveImpl(const VectorType& b, VectorType& x) const
  {
    PetscErrorCode ierr(0);
    int me(this->processor_rank());
    try {
      const Vec *bvec(PETScVector(b));
      Vec *xvec(PETScVector(x));

      ierr = KSPSolve(p_KSP, *bvec, *xvec); CHKERRXX(ierr);
      int its;
      KSPConvergedReason reason;
      PetscReal rnorm;
      ierr = KSPGetIterationNumber(p_KSP, &its); CHKERRXX(ierr);
      ierr = KSPGetConvergedReason(p_KSP, &reason); CHKERRXX(ierr);
      ierr = KSPGetResidualNorm(p_KSP, &rnorm); CHKERRXX(ierr);
      std::string msg;
      if (reason < 0) {
        msg = 
          boost::str(boost::format("%d: PETSc KSP diverged after %d iterations, reason: %d") % 
                     me % its % reason);
        throw Exception(msg);
      } else if (me == 0) {
        msg = 
          boost::str(boost::format("%d: PETSc KSP converged after %d iterations, reason: %d") % 
                     me % its % reason);
        std::cerr << msg << std::endl;
      }
    } catch (const PETSC_EXCEPTION_TYPE& e) {
      throw PETScException(ierr, e);
    } catch (const Exception& e) {
      throw e;
    }
  }    
// -------------------------------------------------------------
// PetscNonlinearSolverImplementation::p_build
// -------------------------------------------------------------
void
PetscNonlinearSolverImplementation::p_build(const std::string& option_prefix)
{
  PetscErrorCode ierr(0);
  try {
    ierr  = SNESCreate(this->communicator(), &p_snes); CHKERRXX(ierr);
    p_petsc_F = PETScVector(*p_F);

    if (!p_function.empty()) {
      ierr = SNESSetFunction(p_snes, *p_petsc_F, FormFunction, 
                             static_cast<void *>(this)); CHKERRXX(ierr);
    }

    p_petsc_J = PETScMatrix(*p_J);
    
    if (!p_jacobian.empty()) {
      ierr = SNESSetJacobian(p_snes, *p_petsc_J, *p_petsc_J, FormJacobian, 
                             static_cast<void *>(this)); CHKERRXX(ierr);
    }

    // set the 
    ierr = SNESSetOptionsPrefix(p_snes, option_prefix.c_str()); CHKERRXX(ierr);
    KSP ksp;
    ierr = SNESGetKSP(p_snes, &ksp); CHKERRXX(ierr);
    ierr = KSPSetOptionsPrefix(ksp, option_prefix.c_str()); CHKERRXX(ierr);
    
    PC pc;
    ierr = KSPGetPC(ksp, &pc); CHKERRXX(ierr);
    ierr = PCSetOptionsPrefix(pc, option_prefix.c_str()); CHKERRXX(ierr);

    ierr = SNESMonitorSet(p_snes, MonitorNorms, PETSC_NULL, PETSC_NULL); CHKERRXX(ierr);

    ierr = SNESSetTolerances(p_snes, 
                             p_functionTolerance, 
                             PETSC_DEFAULT,
                             p_solutionTolerance,
                             p_maxIterations, 
                             PETSC_DEFAULT);
                             
    ierr = SNESSetFromOptions(p_snes); CHKERRXX(ierr);
    
  } catch (const PETSC_EXCEPTION_TYPE& e) {
    throw PETScException(ierr, e);
  }
}