/*
 * @file: solveSP.cpp Implicit solver for nonlinear problems
 */
/*
 * $Id$
 */
/*
 * 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/numerics/solveProb.h"
#include "cantera/base/clockWC.h"
#include "cantera/numerics/ctlapack.h"
#include "cantera/base/stringUtils.h"

/* Standard include files */

#include <cstdio>
#include <cstdlib>
#include <cmath>

#include <vector>

using namespace std;
namespace Cantera
{

/***************************************************************************
 *       STATIC ROUTINES DEFINED IN THIS FILE
 ***************************************************************************/

static doublereal calcWeightedNorm(const doublereal [], const doublereal dx[], size_t);

/***************************************************************************
 *                    LAPACK PROTOTYPES
 ***************************************************************************/


/***************************************************************************
 *  solveSP Class Definitinos
 ***************************************************************************/
//================================================================================================
// Main constructor
solveProb::solveProb(ResidEval* resid) :
    m_residFunc(resid),
    m_neq(0),
    m_atol(0),
    m_rtol(1.0E-4),
    m_maxstep(1000),
    m_ioflag(0)
{
    m_neq =   m_residFunc->nEquations();

    // Dimension solution vector
    size_t dim1 = std::max<size_t>(1, m_neq);

    m_atol.resize(dim1, 1.0E-9);
    m_netProductionRatesSave.resize(dim1, 0.0);
    m_numEqn1.resize(dim1, 0.0);
    m_numEqn2.resize(dim1, 0.0);
    m_CSolnSave.resize(dim1, 0.0);
    m_CSolnSP.resize(dim1, 0.0);
    m_CSolnSPInit.resize(dim1, 0.0);
    m_CSolnSPOld.resize(dim1, 0.0);
    m_wtResid.resize(dim1, 0.0);
    m_wtSpecies.resize(dim1, 0.0);
    m_resid.resize(dim1, 0.0);
    m_ipiv.resize(dim1, 0);
    m_topBounds.resize(dim1, 1.0);
    m_botBounds.resize(dim1, 0.0);

    m_Jac.resize(dim1, dim1, 0.0);
    m_JacCol.resize(dim1, 0);
    for (size_t k = 0; k < dim1; k++) {
        m_JacCol[k] = m_Jac.ptrColumn(k);
    }

}
//================================================================================================
// Empty destructor
solveProb::~solveProb()
{
}
//================================================================================================
/*
 * The following calculation is a Newton's method to
 * get the surface fractions of the surface and bulk species by
 * requiring that the
 * surface species production rate = 0 and that the bulk fractions are
 * proportional to their production rates.
 */
int solveProb::solve(int ifunc, doublereal time_scale,
                     doublereal reltol)
{
    doublereal EXTRA_ACCURACY = 0.001;
    if (ifunc == SOLVEPROB_JACOBIAN) {
        EXTRA_ACCURACY *= 0.001;
    }
    int info = 0;
    size_t label_t = npos; /* Species IDs for time control */
    size_t label_d; /* Species IDs for damping control */
    size_t label_t_old = npos;
    doublereal label_factor = 1.0;
    int iter=0; // iteration number on numlinear solver
    int iter_max=1000; // maximum number of nonlinear iterations
    int nrhs=1;
    doublereal deltaT = 1.0E-10; // Delta time step
    doublereal damp=1.0, tmp;
    //  Weighted L2 norm of the residual.  Currently, this is only
    //  used for IO purposes. It doesn't control convergence.
    //  Therefore, it is turned off when DEBUG_SOLVEPROB isn't defined.
    doublereal  resid_norm;
    doublereal inv_t = 0.0;
    doublereal t_real = 0.0, update_norm = 1.0E6;

    bool do_time = false, not_converged = true;

#ifdef DEBUG_SOLVEPROB
#ifdef DEBUG_SOLVEPROB_TIME
    doublereal         t1;
#endif
#else
    if (m_ioflag > 1) {
        m_ioflag = 1;
    }
#endif

#ifdef DEBUG_SOLVEPROB
#ifdef DEBUG_SOLVEPROB_TIME
    Cantera::clockWC wc;
    if (m_ioflag) {
        t1 = wc.secondsWC();
    }
#endif
#endif

    /*
     *       Set the initial value of the do_time parameter
     */
    if (ifunc == SOLVEPROB_INITIALIZE || ifunc == SOLVEPROB_TRANSIENT) {
        do_time = true;
    }

    /*
     *  upload the initial conditions
     */
    m_residFunc->getInitialConditions(t_real, DATA_PTR(m_CSolnSP), DATA_PTR(m_numEqn1));

    /*
     * Store the initial guess in the soln vector,
     *  CSolnSP, and in an separate vector CSolnSPInit.
     */
    std::copy(m_CSolnSP.begin(), m_CSolnSP.end(), m_CSolnSPInit.begin());



    if (m_ioflag) {
        print_header(m_ioflag, ifunc, time_scale, reltol,
                     DATA_PTR(m_netProductionRatesSave));
    }

    /*
     *  Quick return when there isn't a surface problem to solve
     */
    if (m_neq == 0) {
        not_converged = false;
        update_norm = 0.0;
    }

    /* ------------------------------------------------------------------
     *                         Start of Newton's method
     * ------------------------------------------------------------------
     */
    while (not_converged && iter < iter_max) {
        iter++;
        /*
         *    Store previous iteration's solution in the old solution vector
         */
        std::copy(m_CSolnSP.begin(), m_CSolnSP.end(), m_CSolnSPOld.begin());

        /*
         * Evaluate the largest surface species for each surface phase every
         * 5 iterations.
         */
        //  if (iter%5 == 4) {
        //    evalSurfLarge(DATA_PTR(m_CSolnSP));
        // }

        /*
         *    Calculate the value of the time step
         *       - heuristics to stop large oscillations in deltaT
         */
        if (do_time) {
            /* don't hurry increase in time step at the same time as damping */
            if (damp < 1.0) {
                label_factor = 1.0;
            }
            tmp = calc_t(DATA_PTR(m_netProductionRatesSave), DATA_PTR(m_CSolnSP),
                         &label_t, &label_t_old,  &label_factor, m_ioflag);
            if (iter < 10) {
                inv_t = tmp;
            } else if (tmp > 2.0*inv_t) {
                inv_t =  2.0*inv_t;
            } else {
                inv_t = tmp;
            }

            /*
             *   Check end condition
             */

            if (ifunc == SOLVEPROB_TRANSIENT) {
                tmp = t_real + 1.0/inv_t;
                if (tmp > time_scale) {
                    inv_t = 1.0/(time_scale - t_real);
                }
            }
        } else {
            /* make steady state calc a step of 1 million seconds to
               prevent singular jacobians for some pathological cases */
            inv_t = 1.0e-6;
        }
        deltaT = 1.0/inv_t;

        /*
         * Call the routine to numerically evaluation the jacobian
         * and residual for the current iteration.
         */
        resjac_eval(m_JacCol, DATA_PTR(m_resid), DATA_PTR(m_CSolnSP),
                    DATA_PTR(m_CSolnSPOld), do_time, deltaT);

        /*
         * Calculate the weights. Make sure the calculation is carried
         * out on the first iteration.
         */
        if (iter%4 == 1) {
            calcWeights(DATA_PTR(m_wtSpecies), DATA_PTR(m_wtResid),
                        DATA_PTR(m_CSolnSP));
        }

        /*
         *    Find the weighted norm of the residual
         */
        resid_norm = calcWeightedNorm(DATA_PTR(m_wtResid), DATA_PTR(m_resid), m_neq);

#ifdef DEBUG_SOLVEPROB
        if (m_ioflag > 1) {
            printIterationHeader(m_ioflag, damp, inv_t, t_real, iter, do_time);
            /*
             *    Print out the residual and jacobian
             */
            printResJac(m_ioflag, m_neq, m_Jac, DATA_PTR(m_resid),
                        DATA_PTR(m_wtResid), resid_norm);
        }
#endif

        /*
         *  Solve Linear system (with LAPACK).  The solution is in resid[]
         */

        ct_dgetrf(m_neq, m_neq, m_JacCol[0], m_neq, DATA_PTR(m_ipiv), info);
        if (info==0) {
            ct_dgetrs(ctlapack::NoTranspose, m_neq, nrhs, m_JacCol[0],
                      m_neq, DATA_PTR(m_ipiv), DATA_PTR(m_resid), m_neq,
                      info);
        }
        /*
         *    Force convergence if residual is small to avoid
         *    "nan" results from the linear solve.
         */
        else {
            if (m_ioflag) {
                printf("solveSurfSS: Zero pivot, assuming converged: %g (%d)\n",
                       resid_norm, info);
            }
            for (size_t jcol = 0; jcol < m_neq; jcol++) {
                m_resid[jcol] = 0.0;
            }

            /* print out some helpful info */
            if (m_ioflag > 1) {
                printf("-----\n");
                printf("solveSurfProb: iter %d t_real %g delta_t %g\n\n",
                       iter,t_real, 1.0/inv_t);
                printf("solveSurfProb: init guess, current concentration,"
                       "and prod rate:\n");

                printf("-----\n");
            }
            if (do_time) {
                t_real += time_scale;
            }
#ifdef DEBUG_SOLVEPROB
            if (m_ioflag) {
                printf("\nResidual is small, forcing convergence!\n");
            }
#endif
        }

        /*
         *    Calculate the Damping factor needed to keep all unknowns
         *    between 0 and 1, and not allow too large a change (factor of 2)
         *    in any unknown.
         */


        damp = calc_damping(DATA_PTR(m_CSolnSP), DATA_PTR(m_resid), m_neq, &label_d);


        /*
         *    Calculate the weighted norm of the update vector
         *       Here, resid is the delta of the solution, in concentration
         *       units.
         */
        update_norm = calcWeightedNorm(DATA_PTR(m_wtSpecies),
                                       DATA_PTR(m_resid), m_neq);
        /*
         *    Update the solution vector and real time
         *    Crop the concentrations to zero.
         */
        for (size_t irow = 0; irow < m_neq; irow++) {
            m_CSolnSP[irow] -= damp * m_resid[irow];
        }


        if (do_time) {
            t_real += damp/inv_t;
        }

        if (m_ioflag) {
            printIteration(m_ioflag, damp, label_d, label_t,  inv_t, t_real, iter,
                           update_norm, resid_norm,
                           DATA_PTR(m_netProductionRatesSave),
                           DATA_PTR(m_CSolnSP), DATA_PTR(m_resid),
                           DATA_PTR(m_wtSpecies), m_neq, do_time);
        }

        if (ifunc == SOLVEPROB_TRANSIENT) {
            not_converged = (t_real < time_scale);
        } else {
            if (do_time) {
                if (t_real > time_scale ||
                        (resid_norm < 1.0e-7 &&
                         update_norm*time_scale/t_real < EXTRA_ACCURACY))  {
                    do_time = false;
#ifdef DEBUG_SOLVEPROB
                    if (m_ioflag > 1) {
                        printf("\t\tSwitching to steady solve.\n");
                    }
#endif
                }
            } else {
                not_converged = ((update_norm > EXTRA_ACCURACY) ||
                                 (resid_norm  > EXTRA_ACCURACY));
            }
        }
    }  /* End of Newton's Method while statement */

    /*
     *  End Newton's method.  If not converged, print error message and
     *  recalculate sdot's at equal site fractions.
     */
    if (not_converged) {
        if (m_ioflag) {
            printf("#$#$#$# Error in solveProb $#$#$#$ \n");
            printf("Newton iter on surface species did not converge, "
                   "update_norm = %e \n", update_norm);
            printf("Continuing anyway\n");
        }
    }
#ifdef DEBUG_SOLVEPROB
#ifdef DEBUG_SOLVEPROB_TIME
    if (m_ioflag) {
        printf("\nEnd of solve, time used: %e\n", wc.secondsWC()-t1);
    }
#endif
#endif

    /*
     *        Decide on what to return in the solution vector
     *        - right now, will always return the last solution
     *          no matter how bad
     */
    if (m_ioflag) {
        fun_eval(DATA_PTR(m_resid), DATA_PTR(m_CSolnSP), DATA_PTR(m_CSolnSPOld),
                 false, deltaT);
        resid_norm = calcWeightedNorm(DATA_PTR(m_wtResid),
                                      DATA_PTR(m_resid), m_neq);
        printFinal(m_ioflag, damp, label_d, label_t,  inv_t, t_real, iter,
                   update_norm, resid_norm, DATA_PTR(m_netProductionRatesSave),
                   DATA_PTR(m_CSolnSP), DATA_PTR(m_resid),
                   DATA_PTR(m_wtSpecies),
                   DATA_PTR(m_wtResid), m_neq, do_time);
    }

    /*
     *        Return with the appropriate flag
     */
    if (update_norm > 1.0) {
        return -1;
    }
    return 0;
}
//================================================================================================
/*
 * Update the surface states of the surface phases.
 */
void solveProb::reportState(doublereal* const CSolnSP) const
{
    std::copy(m_CSolnSP.begin(), m_CSolnSP.end(), CSolnSP);
}
//================================================================================================
/*
 * This calculates the net production rates of all species
 *
 * This calculates the function eval.
 *      (should switch to special_species formulation for sum condition)
 *
 * @internal
 *   This routine uses the m_numEqn1 and m_netProductionRatesSave vectors
 *   as temporary internal storage.
 */
void solveProb::fun_eval(doublereal* const resid, const doublereal* const CSoln,
                         const doublereal* const CSolnOld,  const bool do_time,
                         const doublereal deltaT)
{
    if (do_time) {
        m_residFunc->evalSimpleTD(0.0, CSoln, CSolnOld, deltaT, resid);
    } else {
        m_residFunc->evalSS(0.0, CSoln, resid);
    }
}
//================================================================================================
/*
 * Calculate the Jacobian and residual
 *
 * @internal
 *   This routine uses the m_numEqn2 vector
 *   as temporary internal storage.
 */
void solveProb::resjac_eval(std::vector<doublereal*> &JacCol,
                            doublereal resid[], doublereal CSoln[],
                            const doublereal CSolnOld[], const bool do_time,
                            const doublereal deltaT)
{
    doublereal dc, cSave, sd;
    doublereal* col_j;
    /*
     * Calculate the residual
     */
    fun_eval(resid, CSoln, CSolnOld, do_time, deltaT);
    /*
     * Now we will look over the columns perturbing each unknown.
     */

    for (size_t kCol = 0; kCol < m_neq; kCol++) {
        cSave = CSoln[kCol];
        sd = fabs(cSave) + fabs(CSoln[kCol]) + m_atol[kCol] * 1.0E6;
        if (sd < 1.0E-200) {
            sd = 1.0E-4;
        }
        dc = std::max(1.0E-11 * sd, fabs(cSave) * 1.0E-6);
        CSoln[kCol] += dc;
        fun_eval(DATA_PTR(m_numEqn2), CSoln, CSolnOld, do_time, deltaT);
        col_j = JacCol[kCol];
        for (size_t i = 0; i < m_neq; i++) {
            col_j[i] = (m_numEqn2[i] - resid[i])/dc;
        }
        CSoln[kCol] = cSave;
    }

}
//================================================================================================
#define APPROACH 0.50
//  This function calculates a damping factor for the Newton iteration update
//  vector, dxneg, to insure that all solution components stay within perscribed bounds
/*
 *  The default for this class is that all solution components are bounded between zero and one.
 *  this is because the original unknowns were mole fractions and surface site fractions.
 *
 *      dxneg[] = negative of the update vector.
 *
 * The constant "APPROACH" sets the fraction of the distance to the boundary
 * that the step can take.  If the full step would not force any fraction
 * outside of the bounds, then Newton's method is mostly allowed to operate normally.
 * There is also some solution damping employed.
 *
 *  @param x       Vector of the current solution components
 *  @param dxneg   Vector of the negative of the full solution update vector.
 *  @param dim     Size of the solution vector
 *  @param label   return int, stating which solution component caused the most damping.
 */
doublereal solveProb::calc_damping(doublereal x[], doublereal dxneg[], size_t dim, size_t* label)
{
    doublereal  damp = 1.0, xnew, xtop, xbot;
    static doublereal damp_old = 1.0;
    *label = npos;

    for (size_t i = 0; i < dim; i++) {
        doublereal topBounds = m_topBounds[i];
        doublereal botBounds = m_botBounds[i];
        /*
         * Calculate the new suggested new value of x[i]
         */
        double delta_x = - dxneg[i];
        xnew = x[i] - damp * dxneg[i];

        /*
         *  Calculate the allowed maximum and minimum values of x[i]
         *   - Only going to allow x[i] to converge to  the top and bottom bounds by a
         *     single order of magnitude at one time
         */
        bool canCrossOrigin = false;
        if (topBounds > 0.0 && botBounds < 0.0) {
            canCrossOrigin = true;
        }

        xtop = topBounds - 0.1 * fabs(topBounds - x[i]);

        xbot = botBounds + 0.1 * fabs(x[i] - botBounds);

        if (xnew > xtop) {
            damp = - APPROACH * (xtop - x[i]) / dxneg[i];
            *label = i;
        } else if (xnew < xbot) {
            damp = APPROACH * (x[i] - xbot) / dxneg[i];
            *label = i;
        }
        // else  if (fabs(xnew) > 2.0*MAX(fabs(x[i]), 1.0E-10)) {
        //    damp = 0.5 * MAX(fabs(x[i]), 1.0E-9)/ fabs(xnew);
        //    *label = i;
        //     }
        double denom = fabs(x[i]) + 1.0E5 * m_atol[i];
        if ((fabs(delta_x) / denom) > 0.3) {
            double newdamp = 0.3 * denom / fabs(delta_x);
            if (canCrossOrigin) {
                if (xnew * x[i] < 0.0) {
                    if (fabs(x[i]) < 1.0E8 * m_atol[i]) {
                        newdamp = 2.0 * fabs(x[i]) / fabs(delta_x);
                    }
                }
            }
            damp = std::min(damp, newdamp);
        }

    }

    /*
     * Only allow the damping parameter to increase by a factor of three each
     * iteration. Heuristic to avoid oscillations in the value of damp
     */
    if (damp > damp_old*3) {
        damp = damp_old*3;
        *label = npos;
    }

    /*
     *      Save old value of the damping parameter for use
     *      in subsequent calls.
     */
    damp_old = damp;
    return damp;

}
#undef APPROACH
//================================================================================================
/*
 *    This function calculates the norm  of an update, dx[],
 *    based on the weighted values of x.
 */
static doublereal calcWeightedNorm(const doublereal wtX[], const doublereal dx[], size_t dim)
{
    doublereal norm = 0.0;
    doublereal tmp;
    if (dim == 0) {
        return 0.0;
    }
    for (size_t i = 0; i < dim; i++) {
        tmp = dx[i] / wtX[i];
        norm += tmp * tmp;
    }
    return (sqrt(norm/dim));
}
//================================================================================================
/*
 * Calculate the weighting factors for norms wrt both the species
 * concentration unknowns and the residual unknowns.
 *
 */
void solveProb::calcWeights(doublereal wtSpecies[], doublereal wtResid[],
                            const doublereal CSoln[])
{
    /*
     * First calculate the weighting factor
     */

    for (size_t k = 0; k < m_neq; k++) {
        wtSpecies[k] = m_atol[k] + m_rtol * fabs(CSoln[k]);
    }
    /*
     * Now do the residual Weights. Since we have the Jacobian, we
     * will use it to generate a number based on the what a significant
     * change in a solution variable does to each residual.
     * This is a row sum scale operation.
     */
    for (size_t k = 0; k < m_neq; k++) {
        wtResid[k] = 0.0;
        for (size_t jcol = 0; jcol < m_neq; jcol++) {
            wtResid[k] += fabs(m_Jac(k,jcol) * wtSpecies[jcol]);
        }
    }
}
//================================================================================================
/*
 *    This routine calculates a pretty conservative 1/del_t based
 *    on  MAX_i(sdot_i/(X_i*SDen0)).  This probably guarantees
 *    diagonal dominance.
 *
 *     Small surface fractions are allowed to intervene in the del_t
 *     determination, no matter how small.  This may be changed.
 *     Now minimum changed to 1.0e-12,
 *
 *     Maximum time step set to time_scale.
 */
doublereal solveProb::
calc_t(doublereal netProdRateSolnSP[], doublereal Csoln[],
       size_t* label, size_t* label_old, doublereal* label_factor, int ioflag)
{
    doublereal tmp, inv_timeScale=0.0;
    for (size_t k = 0; k < m_neq; k++) {
        if (Csoln[k] <= 1.0E-10) {
            tmp = 1.0E-10;
        } else {
            tmp = Csoln[k];
        }
        tmp = fabs(netProdRateSolnSP[k]/ tmp);


        if (netProdRateSolnSP[k]> 0.0) {
            tmp /= 100.;
        }
        if (tmp > inv_timeScale) {
            inv_timeScale = tmp;
            *label = k;
        }
    }

    /*
     * Increase time step exponentially as same species repeatedly
     * controls time step
     */
    if (*label == *label_old) {
        *label_factor *= 1.5;
    } else {
        *label_old = *label;
        *label_factor = 1.0;
    }
    inv_timeScale = inv_timeScale / *label_factor;
#ifdef DEBUG_SOLVEPROB
    if (ioflag > 1) {
        if (*label_factor > 1.0) {
            printf("Delta_t increase due to repeated controlling species = %e\n",
                   *label_factor);
        }
        int kkin = m_kinSpecIndex[*label];

        string sn = "  "
                    printf("calc_t: spec=%d(%s)  sf=%e  pr=%e  dt=%e\n",
                           *label, sn.c_str(), XMolSolnSP[*label],
                           netProdRateSolnSP[*label], 1.0/inv_timeScale);
    }
#endif

    return (inv_timeScale);

}
//====================================================================================================================
// Set the bottom and top bounds on the solution vector
/*
 *  The default is for the bottom is 0.0, while the default for the top is 1.0
 *
 *   @param botBounds Vector of bottom bounds
 *   @param topBounds vector of top bounds
 */
void solveProb::setBounds(const doublereal botBounds[], const doublereal topBounds[])
{
    for (size_t k = 0; k < m_neq; k++) {
        m_botBounds[k] = botBounds[k];
        m_topBounds[k] = topBounds[k];
    }
}
//====================================================================================================================
/*
 *  printResJac(): prints out the residual and Jacobian.
 *
 */
#ifdef DEBUG_SOLVEPROB
void solveProb::printResJac(int ioflag, int neq, const Array2D& Jac,
                            doublereal resid[], doublereal wtRes[],
                            doublereal norm)
{

}
#endif
//================================================================================================
/*
 * Optional printing at the start of the solveProb problem
 */
void solveProb::print_header(int ioflag, int ifunc, doublereal time_scale,
                             doublereal reltol,
                             doublereal netProdRate[])
{
    int damping = 1;
    if (ioflag) {
        printf("\n================================ SOLVEPROB CALL SETUP "
               "========================================\n");
        if (ifunc == SOLVEPROB_INITIALIZE) {
            printf("\n  SOLVEPROB Called with Initialization turned on\n");
            printf("     Time scale input = %9.3e\n", time_scale);
        } else if (ifunc == SOLVEPROB_RESIDUAL) {
            printf("\n   SOLVEPROB Called to calculate steady state residual\n");
            printf("           from a good initial guess\n");
        } else if (ifunc == SOLVEPROB_JACOBIAN)  {
            printf("\n   SOLVEPROB Called to calculate steady state jacobian\n");
            printf("           from a good initial guess\n");
        } else if (ifunc == SOLVEPROB_TRANSIENT) {
            printf("\n   SOLVEPROB Called to integrate surface in time\n");
            printf("           for a total of %9.3e sec\n", time_scale);
        } else {
            fprintf(stderr,"Unknown ifunc flag = %d\n", ifunc);
            exit(EXIT_FAILURE);
        }



        if (damping) {
            printf("     Damping is ON   \n");
        } else {
            printf("     Damping is OFF  \n");
        }

        printf("     Reltol = %9.3e, Abstol = %9.3e\n", reltol, m_atol[0]);
    }

    /*
     *   Print out the initial guess
     */
#ifdef DEBUG_SOLVEPROB
    if (ioflag > 1) {
        printf("\n================================ INITIAL GUESS "
               "========================================\n");
        int kindexSP = 0;
        for (int isp = 0; isp < m_numSurfPhases; isp++) {
            InterfaceKinetics* m_kin = m_objects[isp];
            int surfIndex = m_kin->surfacePhaseIndex();
            int nPhases = m_kin->nPhases();
            m_kin->getNetProductionRates(netProdRate);
            updateMFKinSpecies(XMolKinSpecies, isp);

            printf("\n IntefaceKinetics Object # %d\n\n", isp);

            printf("\t  Number of Phases = %d\n", nPhases);
            printf("\t  Phase:SpecName      Prod_Rate  MoleFraction   kindexSP\n");
            printf("\t  -------------------------------------------------------"
                   "----------\n");

            int kspindex = 0;
            bool inSurfacePhase = false;
            for (int ip = 0; ip < nPhases; ip++) {
                if (ip == surfIndex) {
                    inSurfacePhase = true;
                } else {
                    inSurfacePhase = false;
                }
                ThermoPhase& THref = m_kin->thermo(ip);
                int nsp = THref.nSpecies();
                string pname = THref.id();
                for (int k = 0; k < nsp; k++) {
                    string sname = THref.speciesName(k);
                    string cname = pname + ":" + sname;
                    if (inSurfacePhase) {
                        printf("\t  %-24s %10.3e %10.3e      %d\n", cname.c_str(),
                               netProdRate[kspindex], XMolKinSpecies[kspindex],
                               kindexSP);
                        kindexSP++;
                    } else {
                        printf("\t  %-24s %10.3e %10.3e\n", cname.c_str(),
                               netProdRate[kspindex], XMolKinSpecies[kspindex]);
                    }
                    kspindex++;
                }
            }
            printf("=========================================================="
                   "=================================\n");
        }
    }
#endif
    if (ioflag == 1) {
        printf("\n\n\t Iter    Time       Del_t      Damp      DelX   "
               "     Resid    Name-Time    Name-Damp\n");
        printf("\t -----------------------------------------------"
               "------------------------------------\n");
    }
}
//================================================================================================
void solveProb::printIteration(int ioflag, doublereal damp, size_t label_d,
                               size_t label_t,
                               doublereal inv_t, doublereal t_real, int iter,
                               doublereal update_norm, doublereal resid_norm,
                               doublereal netProdRate[], doublereal CSolnSP[],
                               doublereal resid[],
                               doublereal wtSpecies[], size_t dim, bool do_time)
{
    size_t i, k;
    string nm;
    if (ioflag == 1) {

        printf("\t%6d ", iter);
        if (do_time) {
            printf("%9.4e %9.4e ", t_real, 1.0/inv_t);
        } else
            for (i = 0; i < 22; i++) {
                printf(" ");
            }
        if (damp < 1.0) {
            printf("%9.4e ", damp);
        } else
            for (i = 0; i < 11; i++) {
                printf(" ");
            }
        printf("%9.4e %9.4e", update_norm, resid_norm);
        if (do_time) {
            k = label_t;
            printf(" %s", int2str(k).c_str());
        } else {
            for (i = 0; i < 16; i++) {
                printf(" ");
            }
        }
        if (label_d != npos) {
            k = label_d;
            printf(" %s", int2str(k).c_str());
        }
        printf("\n");
    }
#ifdef DEBUG_SOLVEPROB
    else if (ioflag > 1) {

        updateMFSolnSP(XMolSolnSP);
        printf("\n\t      Weighted norm of update = %10.4e\n", update_norm);

        printf("\t  Name            Prod_Rate        XMol       Conc   "
               "  Conc_Old     wtConc");
        if (damp < 1.0) {
            printf(" UnDamped_Conc");
        }
        printf("\n");
        printf("\t---------------------------------------------------------"
               "-----------------------------\n");
        int kindexSP = 0;
        for (int isp = 0; isp < m_numSurfPhases; isp++) {
            int nsp = m_nSpeciesSurfPhase[isp];
            InterfaceKinetics* m_kin = m_objects[isp];
            //int surfPhaseIndex = m_kinObjPhaseIDSurfPhase[isp];
            m_kin->getNetProductionRates(DATA_PTR(m_numEqn1));
            for (int k = 0; k < nsp; k++, kindexSP++) {
                int kspIndex = m_kinSpecIndex[kindexSP];
                nm = m_kin->kineticsSpeciesName(kspIndex);
                printf("\t%-16s  %10.3e   %10.3e  %10.3e  %10.3e %10.3e ",
                       nm.c_str(),
                       m_numEqn1[kspIndex],
                       XMolSolnSP[kindexSP],
                       CSolnSP[kindexSP], CSolnSP[kindexSP]+damp*resid[kindexSP],
                       wtSpecies[kindexSP]);
                if (damp < 1.0) {
                    printf("%10.4e ", CSolnSP[kindexSP]+(damp-1.0)*resid[kindexSP]);
                    if (label_d == kindexSP) {
                        printf(" Damp ");
                    }
                }
                if (label_t == kindexSP) {
                    printf(" Tctrl");
                }
                printf("\n");
            }

        }

        printf("\t--------------------------------------------------------"
               "------------------------------\n");
    }
#endif
} /* printIteration */

//================================================================================================
void solveProb::printFinal(int ioflag, doublereal damp, size_t label_d, size_t label_t,
                           doublereal inv_t, doublereal t_real, int iter,
                           doublereal update_norm, doublereal resid_norm,
                           doublereal netProdRateKinSpecies[], const doublereal CSolnSP[],
                           const doublereal resid[],
                           const doublereal wtSpecies[], const doublereal wtRes[],
                           size_t dim, bool do_time)
{
    size_t i, k;
    string nm;
    if (ioflag == 1) {

        printf("\tFIN%3d ", iter);
        if (do_time) {
            printf("%9.4e %9.4e ", t_real, 1.0/inv_t);
        } else
            for (i = 0; i < 22; i++) {
                printf(" ");
            }
        if (damp < 1.0) {
            printf("%9.4e ", damp);
        } else
            for (i = 0; i < 11; i++) {
                printf(" ");
            }
        printf("%9.4e %9.4e", update_norm, resid_norm);
        if (do_time) {
            k = label_t;
            printf(" %s", int2str(k).c_str());
        } else {
            for (i = 0; i < 16; i++) {
                printf(" ");
            }
        }
        if (label_d != npos) {
            k = label_d;

            printf(" %s", int2str(k).c_str());
        }
        printf(" -- success\n");
    }
#ifdef DEBUG_SOLVEPROB
    else if (ioflag > 1) {


        printf("\n================================== FINAL RESULT ========="
               "==================================================\n");

        printf("\n        Weighted norm of solution update = %10.4e\n", update_norm);
        printf("        Weighted norm of residual update = %10.4e\n\n", resid_norm);

        printf("  Name            Prod_Rate        XMol       Conc   "
               "  wtConc      Resid   Resid/wtResid   wtResid");
        if (damp < 1.0) {
            printf(" UnDamped_Conc");
        }
        printf("\n");
        printf("---------------------------------------------------------------"
               "---------------------------------------------\n");

        for (int k = 0; k < m_neq; k++, k++) {
            printf("%-16s  %10.3e   %10.3e  %10.3e  %10.3e %10.3e  %10.3e %10.3e",
                   nm.c_str(),
                   m_numEqn1[k],
                   XMolSolnSP[k],
                   CSolnSP[k],
                   wtSpecies[k],
                   resid[k],
                   resid[k]/wtRes[k], wtRes[k]);
            if (damp < 1.0) {
                printf("%10.4e ", CSolnSP[k]+(damp-1.0)*resid[k]);
                if (label_d == k) {
                    printf(" Damp ");
                }
            }
            if (label_t == k) {
                printf(" Tctrl");
            }
            printf("\n");
        }

        printf("\n");
        printf("==============================================================="
               "============================================\n\n");
    }
#endif
}
//================================================================================================
#ifdef DEBUG_SOLVEPROB
void solveProb::
printIterationHeader(int ioflag, doublereal damp,doublereal inv_t, doublereal t_real,
                     int iter, bool do_time)
{
    if (ioflag > 1) {
        printf("\n===============================Iteration %5d "
               "=================================\n", iter);
        if (do_time) {
            printf("   Transient step with: Real Time_n-1 = %10.4e sec,", t_real);
            printf(" Time_n = %10.4e sec\n", t_real + 1.0/inv_t);
            printf("                        Delta t = %10.4e sec", 1.0/inv_t);
        } else {
            printf("   Steady Solve ");
        }
        if (damp < 1.0) {
            printf(", Damping value =  %10.4e\n", damp);
        } else {
            printf("\n");
        }
    }
}
#endif
//================================================================================================
void solveProb::setAtol(const doublereal atol[])
{
    for (size_t k = 0; k < m_neq; k++, k++) {
        m_atol[k] = atol[k];
    }
}
//================================================================================================
void solveProb::setAtolConst(const doublereal atolconst)
{
    for (size_t k = 0; k < m_neq; k++, k++) {
        m_atol[k] = atolconst;
    }
}
//================================================================================================


}
