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

/**
 * @file ForcingTerms.h
 * ForcingTerms class header.
 */
#ifndef NonLinearSolver_ForcingTerms_h
#define NonLinearSolver_ForcingTerms_h

#include <string>						// HASA string
#include "../PetscUtils/PetscUtils.h"	// USES PetscUtils
#include "NonLinearSolver.def"

NONLINEARSOLVER_NAMESPACE_BEGIN

// :TODO: make Initialize() and TestConvergence() private/protected?
// :TODO: Use non-linear tolerance as lower bound in forcing term strategies.

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

/**
 * Forcing terms abstract base class.
 * Implements an abstract forcing term strategy class for use with the NonLinearSolver class.
 * Derived classes should implement TestConvergence().
 * NonLinearSolver::Solve() calls SetLinearTolerances() before starting the non-linear solution process,
 * so that the initial tolerance settings are those set in KSP.
 * The solver calls TestConvergence() in each linear iteration to test for convergence of the linear solver.
 */
class ForcingTerms
{

public:
	ForcingTerms(void);
	virtual ~ForcingTerms(void);

	/**
	 * Initializes the object data.
	 * Called by the non-linear solver before the first iteration.
	 * @param relativeTolerance Initial relative tolerance for the linear solver.
	 * @param absoluteTolerance Initial absolute tolerance for the linear solver.
	 * @param divergenceTolerance Initial divergence tolerance for the linear solver.
	 * @param maxIterationCount Initial maximum number of iterations for the linear solver.
	 * @param nonlinearAbsoluteTolerance Absolute tolerance on the nonlinear residual norm.
	 * @return non-zero error code on failure, 0 on success.
	 */
	virtual PetscErrorCode Initialize(PetscReal relativeTolerance, PetscReal absoluteTolerance, PetscReal divergenceTolerance, PetscInt maxIterationCount, PetscReal nonlinearAbsoluteTolerance);

	/**
	 * Test convergence of the linear solver.
	 * Called by the non-linear solver in each linear iteration of the Jacobian system solver.
	 * @param[in] ksp PETSc linear equation solver object.
	 * @param[in] it current linear iteration within the current non-linear iteration.
	 * @param[in] rnorm linear residual norm.
	 * @param[out] pReason reason for convergence or non-convergence.
	 * @return non-zero error code on failure, 0 on success.
	 */
	virtual PetscErrorCode TestConvergence(KSP ksp, PetscInt it, PetscReal rnorm, KSPConvergedReason* pReason) = 0;

	/**
	 * Get the name of the forcing term strategy.
	 * @return name of the forcing term strategy.
	 */
	virtual std::string GetName(void) const;

	/**
	 * Get the most recent relative tolerance.
	 * @return relative tolerance.
	 */
	virtual PetscReal GetRelativeTolerance(void) const;

	/**
	 * Get the most recent absolute tolerance.
	 * @return absolute tolerance.
	 */
	virtual PetscReal GetAbsoluteTolerance(void) const;

	/**
	 * Get the most recent divergence tolerance.
	 * @return divergence tolerance.
	 */
	virtual PetscReal GetDivergenceTolerance(void) const;

	/**
	 * Get the most recent maximum iteration count.
	 * @return maximum iteration count.
	 */
	virtual PetscInt GetMaxIterationCount(void) const;

	/**
	 * Get the absolute tolerance on the nonlinear residual norm.
	 * @return absolute tolerance on the nonlinear residual norm.
	 */
	virtual PetscReal GetNonlinearAbsoluteTolerance(void) const;

protected:
	// MEMBER VARIABLES
	PetscErrorCode mErr;					///< Error code of the most recent PETSc error.
	std::string mName;						///< Name of the forcing term strategy.
	PetscReal mRelativeTolerance;			///< Relative tolerance for the linear solver.
	PetscReal mAbsoluteTolerance;			///< Absolute tolerance for the linear solver.
	PetscReal mDivergenceTolerance;			///< Divergence tolerance for the linear solver.
	PetscInt mMaxIterationCount;			///< Maximum number of iterations for the linear solver.
	PetscReal mNonlinearAbsoluteTolerance;	///< Absolute tolerance on the nonlinear residual norm.
};

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

/**
 * Fixed forcing terms class.
 * Implements a forcing term strategy where the forcing term is the same in each non-linear iteration.
 */
class ForcingTermsFixed : public ForcingTerms
{

public:
	ForcingTermsFixed(void);
	virtual ~ForcingTermsFixed(void);
	virtual PetscErrorCode Initialize(PetscReal relativeTolerance, PetscReal absoluteTolerance, PetscReal divergenceTolerance, PetscInt maxIterationCount, PetscReal nonlinearAbsoluteTolerance);
	virtual PetscErrorCode TestConvergence(KSP ksp, PetscInt it, PetscReal rnorm, KSPConvergedReason* pReason);

private:
	// MEMBER VARIABLES
	void* mDefaultConvergedContext;		///< Default converged context, needed for KSPDefaultConverged.
};

//============================= DEMBO & STEIHAUG =============================

/**
 * Dembo and Steihaug forcing terms class.
 * Implements the Dembo and Steihaug forcing term strategy.
 */
class ForcingTermsDemboSteihaug : public ForcingTerms
{

public:
	ForcingTermsDemboSteihaug(void);
	virtual ~ForcingTermsDemboSteihaug(void);
	virtual PetscErrorCode Initialize(PetscReal relativeTolerance, PetscReal absoluteTolerance, PetscReal divergenceTolerance, PetscInt maxIterationCount, PetscReal nonlinearAbsoluteTolerance);
	virtual PetscErrorCode TestConvergence(KSP ksp, PetscInt it, PetscReal rnorm, KSPConvergedReason* pReason);

private:
	/**
	 * Updates the tolerances.
	 * Called by TestConvergence() in the first convergence test of every non-linear iteration.
	 * @return non-zero error code on failure, 0 on success.
	 */
	virtual PetscErrorCode UpdateTolerances(void);

	// MEMBER VARIABLES
	PetscInt mNonLinearIterationCount;	///< Current non-linear iteration.
	PetscReal mNonLinearResidualNorm;	///< Norm of the initial linear residual of the current non-linear iteration. For right preconditioning equal to the non-linear residual at the start of the current Newton iteration.
	void* mDefaultConvergedContext;		///< Default converged context, needed for KSPDefaultConverged.
};

//============================= EISENSTAT & WALKER ===========================

/**
 * Eisenstat and Walker forcing terms class.
 * Implements the Eisenstat and Walker forcing term strategy.
 */
class ForcingTermsEisenstatWalker : public ForcingTerms
{

public:
	ForcingTermsEisenstatWalker(void);
	virtual ~ForcingTermsEisenstatWalker(void);
	virtual PetscErrorCode Initialize(PetscReal relativeTolerance, PetscReal absoluteTolerance, PetscReal divergenceTolerance, PetscInt maxIterationCount, PetscReal nonlinearAbsoluteTolerance);
	virtual PetscErrorCode TestConvergence(KSP ksp, PetscInt it, PetscReal rnorm, KSPConvergedReason* pReason);

private:
	/**
	 * Updates the tolerances.
	 * Called by TestConvergence() in the first convergence test of every non-linear iteration.
	 * @return non-zero error code on failure, 0 on success.
	 */
	virtual PetscErrorCode UpdateTolerances(void);

	// MEMBER VARIABLES
	PetscInt mNonLinearIterationCount;		///< Current non-linear iteration.
	PetscReal mLinearResidualNorm;			///< Norm of the current linear residual.
	PetscReal mLinearResidualNormOld;		///< Norm of the final linear residual of the previous non-linear iteration.
	PetscReal mNonLinearResidualNorm;		///< Norm of the initial linear residual of the current non-linear iteration. For right preconditioning equal to the non-linear residual at the start of the current Newton iteration.
	PetscReal mNonLinearResidualNormOld;	///< Norm of the initial linear residual of the previous non-linear iteration. For right preconditioning equal to the non-linear residual at the start of the previous Newton iteration.
	PetscReal mRelativeToleranceOld;		///< Relative tolerance for the linear solver in the previous non-linear iteration.
	void* mDefaultConvergedContext;			///< Default converged context, needed for KSPDefaultConverged.
};

//============================= ITERATIONS ===================================

/**
 * Iteration based forcing terms class.
 * Implements a forcing term strategy that runs a given amount of linear iterations.
 * Each non-linear iteration the user is prompted for the desired amount of iterations.
 * Tolerance values are not checked.
 */
class ForcingTermsIterations : public ForcingTerms
{

public:
	ForcingTermsIterations(void);
	virtual ~ForcingTermsIterations(void);
	virtual PetscErrorCode Initialize(PetscReal relativeTolerance, PetscReal absoluteTolerance, PetscReal divergenceTolerance, PetscInt maxIterationCount, PetscReal nonlinearAbsoluteTolerance);
	virtual PetscErrorCode TestConvergence(KSP ksp, PetscInt it, PetscReal rnorm, KSPConvergedReason* pReason);

private:
	/**
	 * Updates the tolerances.
	 * Called by TestConvergence() in the first convergence test of every non-linear iteration.
	 * @return non-zero error code on failure, 0 on success.
	 */
	virtual PetscErrorCode UpdateTolerances(void);

	// MEMBER VARIABLES
	PetscInt mNonLinearIterationCount;		///< Current non-linear iteration.
};

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

NONLINEARSOLVER_NAMESPACE_END

#endif // NonLinearSolver_ForcingTerms_h
