/**
 *  @file ResidJacEval.cpp
 *
 */
/*
 * $Revision$
 * $Date$
 */
/*
 * Copyright 2004 Sandia Corporation. Under the terms of Contract
 * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government
 * retains certain rights in this software.
 * See file License.txt for licensing information.
 */

#include "cantera/base/ct_defs.h"
#include "cantera/numerics/ctlapack.h"
#include "cantera/numerics/ResidJacEval.h"

#include <iostream>
#include <vector>

using namespace std;

namespace Cantera
{
//====================================================================================================================

ResidJacEval::ResidJacEval(doublereal atol) :
    ResidEval(),
    m_atol(atol)
{
}
//====================================================================================================================
//   Copy Constructor for the %ResidJacEval object
/*
 */
ResidJacEval::ResidJacEval(const ResidJacEval& right) :
    ResidEval()
{
    *this = operator=(right);
}
//====================================================================================================================
ResidJacEval::~ResidJacEval()
{
}
//====================================================================================================================
ResidJacEval& ResidJacEval::operator=(const ResidJacEval& right)
{
    if (this == &right) {
        return *this;
    }

    ResidEval::operator=(right);

    m_atol = right.m_atol;
    neq_   = right.neq_;

    return *this;
}
//====================================================================================================================
// Duplication routine for objects which inherit from %ResidJacEval
/*
 *  This virtual routine can be used to duplicate %ResidJacEval objects
 *  inherited from %ResidJacEval even if the application only has
 *  a pointer to %ResidJacEval to work with.
 *
 *  These routines are basically wrappers around the derived copy
 *  constructor.
 */
ResidJacEval* ResidJacEval::duplMyselfAsResidJacEval() const
{
    ResidJacEval* ff = new ResidJacEval(*this);
    return ff;
}
//====================================================================================================================
int ResidJacEval::nEquations() const
{
    return neq_;
}
//====================================================================================================================
// Set a global value of the absolute tolerance
/*
 *  @param atol   Value of atol
 */
void ResidJacEval::setAtol(doublereal atol)
{
    m_atol = atol;
    if (m_atol <= 0.0) {
        throw CanteraError("ResidJacEval::setAtol",
                           "atol must be greater than zero");
    }
}
//====================================================================================================================
//! Fill in the initial conditions
/*!
 * Values for both the solution and the value of ydot may be provided.
 *
 * @param t0            Time                    (input)
 * @param y             Solution vector (output)
 * @param ydot          Rate of change of solution vector. (output)
 */
int ResidJacEval::
getInitialConditions(doublereal t0, doublereal* const y, doublereal* const ydot)
{
    for (int i = 0; i < neq_; i++) {
        y[i] = 0.0;
    }
    if (ydot) {
        for (int i = 0; i < neq_; i++) {
            ydot[i] = 0.0;
        }
    }
    return 1;
}
//====================================================================================================================
// This function may be used to create output at various points in the execution of an application.
/*
 *
 *  @param ifunc     identity of the call
 *                          0  Initial call
 *                          1  Called at the end of every successful time step
 *                         -1  Called at the end of every unsuccessful time step
 *                          2  Called at the end of every call to integrateRJE()
 *
 * @param t             Time                    (input)
 * @param delta_t       The current value of the time step (input)
 * @param y             Solution vector (input, do not modify)
 * @param ydot          Rate of change of solution vector. (input)
 */
void ResidJacEval::
user_out2(const int ifunc, const doublereal t, const doublereal deltaT,
          const doublereal* y, const doublereal* ydot)
{
}

//====================================================================================================================
// This function may be used to create output at various points in the execution of an application.
/*
 *  This routine calls user_out2().
 *
 *  @param ifunc     identity of the call
 * @param t             Time                    (input)
 * @param y             Solution vector (input, do not modify)
 * @param ydot          Rate of change of solution vector. (input)
 */
void ResidJacEval::
user_out(const int ifunc, const doublereal t,
         const doublereal* y, const doublereal* ydot)
{
    user_out2(ifunc, t, 0.0, y, ydot);
}
//====================================================================================================================
//! Evaluate the time tracking equations, if any
/*!
 * Evaluate time integrated quantities that are calculated at the
 * end of every successful time step.  This call is made once at the end of every successful
 * time step that advances the time. It's also made once at the start of the time stepping.
 *
 * @param t             Time                    (input)
 * @param delta_t       The current value of the time step (input)
 * @param y             Solution vector (input, do not modify)
 * @param ydot          Rate of change of solution vector. (input, do not modify)
 */
int ResidJacEval::
evalTimeTrackingEqns(const doublereal t, const doublereal delta_t, const doublereal* y,
                     const doublereal* ydot)
{
    return 1;
}
//====================================================================================================================
//  Return a vector of delta y's for calculation of the numerical Jacobian
/*
 *   There is a default algorithm provided.
 *
 *        delta_y[i] = atol[i] + 1.0E-6 ysoln[i]
 *        delta_y[i] = atol[i] + MAX(1.0E-6 ysoln[i] * 0.01 * solnWeights[i])
 *
 * @param t             Time                    (input)
 * @param y             Solution vector (input, do not modify)
 * @param ydot          Rate of change of solution vector. (input, do not modify)
 * @param delta_y       Value of the delta to be used in calculating the numerical jacobian
 * @param solnWeights   Value of the solution weights that are used in determining convergence (default = 0)
 *
 * @return Returns a flag to indicate that operation is successful.
 *            1  Means a successful operation
 *            0  Means an unsuccessful operation
 */
int ResidJacEval::
calcDeltaSolnVariables(const doublereal t, const doublereal* const ySoln,
                       const doublereal* const ySolnDot, doublereal* const deltaYSoln,
                       const doublereal* const solnWeights)
{
    if (!solnWeights) {
        for (int i = 0; i < neq_; i++) {
            deltaYSoln[i] = m_atol + fabs(1.0E-6 * ySoln[i]);
        }
    } else {
        for (int i = 0; i < neq_; i++) {
            deltaYSoln[i] = std::max(1.0E-2 * solnWeights[i], 1.0E-6 * fabs(ySoln[i]));
        }
    }
    return 1;
}
//====================================================================================================================
//  Returns a vector of column scale factors that can be used to column scale Jacobians.
/*
 *  Default to yScales[] = 1.0
 *
 * @param t             Time                    (input)
 * @param y             Solution vector (input, do not modify)
 * @param y_old         Old Solution vector (input, do not modify)
 * @param yScales       Value of the column scales
 */
void ResidJacEval::
calcSolnScales(const doublereal t, const doublereal* const ysoln, const doublereal* const ysolnOld,
               doublereal* const ysolnScales)
{
    if (ysolnScales) {
        if (ysolnScales[0] == 0.0) {
            for (int i = 0; i < neq_; i++) {
                ysolnScales[i] = 1.0;
            }
        }
    }
}
//====================================================================================================================
// Filter the solution predictions
/*
 * Codes might provide a predicted step change. This routine filters the predicted
 * solution vector eliminating illegal directions.
 *
 * @param t             Time                    (input)
 * @param y             Solution vector (input, output)
 * @param step          Proposed step in the solution that will be cropped
 */
doublereal ResidJacEval::filterNewStep(doublereal t, const doublereal* const ybase, doublereal* const step)
{
    return 0.0;
}
//====================================================================================================================
// Filter the solution predictions
/*
 * Codes might provide a predicted solution vector. This routine filters the predicted
 * solution vector.
 *
 * @param t             Time                    (input)
 * @param y             Solution vector (input, output)
 */
doublereal ResidJacEval::filterSolnPrediction(doublereal t, doublereal* const y)
{
    return 0.0;
}
//====================================================================================================================
// Evaluate any stopping criteria other than a final time limit
/*
 *  If we are to stop the time integration for any reason other than reaching a final time limit, tout,
 *  provide a test here. This call is made at the end of every successful time step iteration
 *
 *  @return    If true, the the time stepping is stopped. If false, then time stepping is stopped if t >= tout
 *             Defaults to false.
 *
 * @param t             Time                    (input)
 * @param delta_t       The current value of the time step (input)
 * @param y             Solution vector (input, do not modify)
 * @param ydot          Rate of change of solution vector. (input, do not modify)
 */
bool ResidJacEval::
evalStoppingCritera(const doublereal t,
                    const doublereal delta_t,
                    const doublereal* const y,
                    const doublereal* const ydot)
{
    return false;
}
//====================================================================================================================
// Multiply the matrix by another matrix that leads to better conditioning
/*
 *  Provide a left sided matrix that will multiply the current jacobian, after scaling
 *  and lead to a better conditioned system.
 *  This routine is called just before the matrix is factored.
 *
 *  Original Problem:
 *        J delta_x = - Resid
 *
 *  New problem:
 *      M (J delta_x) = - M Resid
 *
 *  @param    matrix     Pointer to the current jacobian (if zero, it's already been factored)
 *  @param    nrows      offsets for the matrix
 *  @param    rhs        residual vector. This also needs to be lhs multiplied by M
 */
int ResidJacEval::
matrixConditioning(doublereal* const matrix, const int nrows, doublereal* const rhs)
{
    return 1;
}
//====================================================================================================================
// Evaluate the residual function
/*
 * @param t             Time                    (input)
 * @param delta_t       The current value of the time step (input)
 * @param y             Solution vector (input, do not modify)
 * @param ydot          Rate of change of solution vector. (input, do not modify)
 * @param resid         Value of the residual that is computed (output)
 * @param evalType      Type of the residual being computed (defaults to Base_ResidEval)
 * @param id_x          Index of the variable that is being numerically differenced to find
 *                      the jacobian (defaults to -1, which indicates that no variable is being
 *                      differenced or that the residual doesn't take this issue into account)
 * @param delta_x       Value of the delta used in the numerical differencing
 */
int ResidJacEval::
evalResidNJ(const doublereal t, const doublereal deltaT,  const doublereal* y,
            const doublereal* ydot, doublereal* const resid, const ResidEval_Type_Enum evalType,
            const size_t id_x, const doublereal delta_x)
{
    throw CanteraError("ResidJacEval::evalResidNJ()", "Not implemented\n");
    return 1;
}
//====================================================================================================================
int ResidJacEval::eval(const doublereal t, const doublereal* const y, const doublereal* const ydot,
                       doublereal* const r)
{
    double deltaT = -1.0;
    int flag = evalResidNJ(t, deltaT, y, ydot, r);
    return flag;
}
//====================================================================================================================
// Calculate an analytical jacobian and the residual at the current time and values.
/*
 *  Only called if the jacFormation method is set to analytical
 *
 * @param t             Time                    (input)
 * @param delta_t       The current value of the time step (input)
 * @param y             Solution vector (input, do not modify)
 * @param ydot          Rate of change of solution vector. (input, do not modify)
 * @param J             Reference to the SquareMatrix object to be calculated (output)
 * @param resid         Value of the residual that is computed (output)
 */
int ResidJacEval::
evalJacobian(const doublereal t, const doublereal delta_t, doublereal cj,
             const doublereal* const y,
             const doublereal* const ydot,
             GeneralMatrix& J,
             doublereal* const resid)
{
    doublereal* const* jac_colPts = J.colPts();
    return evalJacobianDP(t, delta_t, cj, y, ydot, jac_colPts, resid);
}
//====================================================================================================================
// Calculate an analytical jacobian and the residual at the current time and values.
/*
 *  Only called if the jacFormation method is set to analytical
 *
 * @param t             Time                    (input)
 * @param delta_t       The current value of the time step (input)
 * @param c_j           The current value of the coefficient of the time derivative
 * @param y             Solution vector (input, do not modify)
 * @param ydot          Rate of change of solution vector. (input, do not modify)
 * @param jac_colPts    Reference to the SquareMatrix object to be calculated (output)
 * @param resid         Value of the residual that is computed (output)
 */
int ResidJacEval::
evalJacobianDP(const doublereal t, const doublereal delta_t,
               const doublereal c_j,
               const doublereal* const y,
               const doublereal* const ydot,
               doublereal* const* jac_colPts,
               doublereal* const resid)
{
    throw CanteraError("ResidJacEval::evalJacobianDP()", "Not implemented\n");
    return 1;
}
//====================================================================================================================

}
