/*
 * Copyright (C) 2008-2012 Delft University of Technology - All rights reserved.
 *
 * Author: Reijer Idema
 */

/**
 * @file Preconditioner.h
 * Preconditioner class header.
 */
#ifndef NonLinearSolver_Preconditioner_h
#define NonLinearSolver_Preconditioner_h

#include "../PetscUtils/PetscUtils.h"	// USES PetscUtils
#include "NonLinearProblem.h"			// PreconditionerJacobian HASA NonLinearProblem (not owned)
#include "NonLinearSolver.def"

NONLINEARSOLVER_NAMESPACE_BEGIN

// :TODO: make Initialize() and Calculate() private/protected?

//============================= ABC ==========================================

/**
 * %Preconditioner abstract base class.
 * Implements an abstract preconditioner class for use with the NonLinearSolver class.
 * Derived classes should implement GetMatrix() to return the preconditioner matrix
 * Calculate() to set the preconditioner matrix and structure flag.
 * NonLinearSolver::Solve() calls GetMatrix() and sets the returned matrix as preconditioner in SNESSetJacobian().
 * The solver calls Calculate() in each non-linear iteration to update the preconditioner if needed.
 */
class Preconditioner
{
public:
	Preconditioner(void);
	virtual ~Preconditioner(void);

	/**
	 * Initializes the preconditioner.
	 * Called by the non-linear solver before the first iteration.
	 * After initialization the proper preconditioner matrix should be returned when calling GetMatrix().
	 * @param rProblem non-linear problem.
	 * @return non-zero error code on failure, 0 on success.
	 */
	virtual PetscErrorCode Initialize(NonLinearProblem& rProblem);

	/**
	 * Get the name of the preconditioner matrix.
	 * @return name of the preconditioner matrix.
	 */
	virtual std::string GetName(void) const;

	/**
	 * Gets the preconditioner matrix.
	 * This function is called by the non-linear solver, and the returned matrix is set as preconditioner in SNESSetJacobian().
	 * @return preconditioner matrix.
	 */
	virtual Mat GetMatrix(void) = 0;

	/**
	 * Calculates the preconditioner matrix and matrix structure flag.
	 * This function is called by the non-linear solver in each non-linear iteration,
	 * to update the preconditioner matrix and matrix structure flag if needed.
	 * The matrix structure flag should be set to
	 * SAME_NONZERO_PATTERN, DIFFERENT_NONZERO_PATTERN, SAME_PRECONDITIONER, or SUBSET_NONZERO_PATTERN.
	 * @param[in] snes PETSc non-linear equation solver object.
	 * @param[in] pX current non-linear solution iterate.
	 * @param[in] pJ current Jacobian matrix.
	 * @param[in,out] ppM preconditioner matrix.
	 * @param[in,out] pFlag preconditioner matrix structure flag.
	 * @return non-zero error code on failure, 0 on success.
	 */
	virtual PetscErrorCode Calculate(SNES snes, PetscUtils::ConstVec pX, PetscUtils::ConstMat pJ, Mat* ppM, MatStructure* pFlag) = 0;

protected:
	// MEMBER VARIABLES
	std::string mName;				///< Name of the preconditioner matrix.
	NonLinearProblem* mpProblem;	///< Problem for which this is the preconditioner (not owned).
};

//============================= JACOBIAN =====================================

/**
 * Jacobian preconditioner class.
 * Implements a preconditioner where matrix is the Jacobian matrix of the non-linear problem,
 * and the matrix structure flag depends on NonLinearProblem::IsJacobianMatrixStructureSame().
 */
class PreconditionerJacobian : public Preconditioner
{
public:
	PreconditionerJacobian(void);
	// :NOTE: Inherits destructor from the base class.

	/**
	 * Gets the current Jacobian matrix from the non-linear problem.
	 * This function is called by the non-linear solver before the first iteration,
	 * and the returned matrix is set as preconditioner in SNESSetJacobian().
	 * @return preconditioner matrix.
	 */
	virtual Mat GetMatrix(void);

	/**
	 * Calculates the preconditioner matrix and sets the matrix structure flag.
	 * Checks whether the preconditioner matrix still points to the Jacobina matrix,
	 * and sets the matrix structure flag depending on NonLinearProblem::IsJacobianMatrixStructureSame().
	 * This function is called by the non-linear solver in each non-linear iteration,
	 * to update the preconditioner matrix and matrix structure flag.
	 * @param[in] snes PETSc non-linear equation solver object.
	 * @param[in] pX current non-linear solution iterate.
	 * @param[in] pJ current Jacobian matrix.
	 * @param[in,out] ppM preconditioner matrix.
	 * @param[in,out] pFlag preconditioner matrix structure flag.
	 * @return non-zero error code if the preconditioner matrix does not point to the correct object, 0 otherwise.
	 */
	virtual PetscErrorCode Calculate(SNES snes, PetscUtils::ConstVec pX, PetscUtils::ConstMat pJ, Mat* ppM, MatStructure* pFlag);
};

//============================= INITIAL JACOBIAN =============================

/**
 * Initial Jacobian preconditioner class.
 * Implements a preconditioner where matrix is the initial Jacobian matrix of the non-linear problem,
 * and the matrix structure flag is set to SAME_PRECONDITIONER.
 *
 * \note The same preconditioning can be accomplished by using the Jacobian and
 * setting the SNES preconditioner lag to -1 or -2 (command line option: -snes_lag_preconditioner).
 */
class PreconditionerInitialJacobian : public Preconditioner
{
public:
	PreconditionerInitialJacobian(void);
	virtual ~PreconditionerInitialJacobian(void);
	virtual PetscErrorCode Initialize(NonLinearProblem& rProblem);

	/**
	 * Gets the initial Jacobian matrix from the non-linear problem.
	 * This function is called by the non-linear solver before the first iteration,
	 * and the returned matrix is set as preconditioner in SNESSetJacobian().
	 * @return preconditioner matrix.
	 */
	virtual Mat GetMatrix(void);

	/**
	 * Calculates the preconditioner matrix and sets the matrix structure flag.
	 * Checks whether the preconditioner matrix still points to the Jacobina matrix,
	 * and sets the matrix structure flag depending on NonLinearProblem::IsJacobianMatrixStructureSame().
	 * This function is called by the non-linear solver in each non-linear iteration,
	 * to update the preconditioner matrix and matrix structure flag.
	 * @param[in] snes PETSc non-linear equation solver object.
	 * @param[in] pX current non-linear solution iterate.
	 * @param[in] pJ current Jacobian matrix.
	 * @param[in,out] ppM preconditioner matrix.
	 * @param[in,out] pFlag preconditioner matrix structure flag.
	 * @return non-zero error code if the preconditioner matrix does not point to the correct object, 0 otherwise.
	 */
	virtual PetscErrorCode Calculate(SNES snes, PetscUtils::ConstVec pX, PetscUtils::ConstMat pJ, Mat* ppM, MatStructure* pFlag);

private:
	// MEMBER VARIABLES
	Mat mpOwnedMatrix;		///< %Preconditioner matrix created in SetMatrixCopy() (owned)
};

//============================= FIXED ========================================

/**
 * Fixed matrix preconditioner class.
 * Implements a preconditioner where the matrix is set to a fixed matrix,
 * and the matrix structure flag is always SAME_PRECONDITIONER.
 */
class PreconditionerFixed : public Preconditioner
{
public:
	PreconditionerFixed(void);
	virtual ~PreconditionerFixed(void);

	/**
	 * Sets the preconditioner matrix to be the provided matrix.
	 * Warning: the matrix is assumed to stay the same, if the matrix may change use SetMatrixCopy() instead.
	 * @param pMat matrix to use as preconditioner.
	 * @return \c true on success, \c false on failure.
	 */
	virtual bool SetMatrix(Mat pMat);

	/**
	 * Sets the preconditioner matrix to be a copy of the provided matrix.
	 * @param pMat matrix to copy as preconditioner.
	 * @return \c true on success, \c false on failure.
	 */
	virtual bool SetMatrixCopy(Mat pMat);

	/**
	 * Gets the preconditioner matrix as set by SetMatrix() or SetMatrixCopy().
	 * This function is called by the non-linear solver before the first iteration,
	 * and the returned matrix is set as preconditioner in SNESSetJacobian().
	 * @return preconditioner matrix.
	 */
	virtual Mat GetMatrix(void);

	/**
	 * Calculates the preconditioner matrix and sets the matrix structure flag.
	 * Checks whether the preconditioner matrix still points to the correct object,
	 * and sets the matrix structure flag to SAME_PRECONDITIONER.
	 * This function is called by the non-linear solver in each non-linear iteration,
	 * to update the preconditioner matrix and matrix structure flag if needed.
	 * @param[in] snes PETSc non-linear equation solver object.
	 * @param[in] pX current non-linear solution iterate.
	 * @param[in] pJ current Jacobian matrix.
	 * @param[in,out] ppM preconditioner matrix.
	 * @param[in,out] pFlag preconditioner matrix structure flag.
	 * @return non-zero error code if the preconditioner matrix does not point to the correct object, 0 otherwise.
	 */
	virtual PetscErrorCode Calculate(SNES snes, PetscUtils::ConstVec pX, PetscUtils::ConstMat pJ, Mat* ppM, MatStructure* pFlag);

private:
	// MEMBER VARIABLES
	Mat mpMatrix;			///< %Preconditioner matrix (not owned).
	Mat mpOwnedMatrix;		///< %Preconditioner matrix created in SetMatrixCopy() (owned)
};

//============================================================================

NONLINEARSOLVER_NAMESPACE_END

#endif // NonLinearSolver_Preconditioner_h
