/*
** (c) 1996-2000 The Regents of the University of California (through
** E.O. Lawrence Berkeley National Laboratory), subject to approval by
** the U.S. Department of Energy.  Your use of this software is under
** license -- the license agreement is attached and included in the
** directory as license.txt or you may contact Berkeley Lab's Technology
** Transfer Department at TTD@lbl.gov.  NOTICE OF U.S. GOVERNMENT RIGHTS.
** The Software was developed under funding from the U.S. Government
** which consequently retains certain rights as follows: the
** U.S. Government has been granted for itself and others acting on its
** behalf a paid-up, nonexclusive, irrevocable, worldwide license in the
** Software to reproduce, prepare derivative works, and perform publicly
** and display publicly.  Beginning five (5) years after the date
** permission to assert copyright is obtained from the U.S. Department of
** Energy, and subject to any subsequent five (5) year renewals, the
** U.S. Government is granted for itself and others acting on its behalf
** a paid-up, nonexclusive, irrevocable, worldwide license in the
** Software to reproduce, prepare derivative works, distribute copies to
** the public, perform publicly and display publicly, and to permit
** others to do so.
*/


#include <winstd.H>

#include <new>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <list>
#include <map>
#include <algorithm>

#ifndef WIN32
#include <unistd.h>
#endif

#include "Tuple.H"
#include "ParmParse.H"
#include "ParallelDescriptor.H"
#include "DataServices.H"
#include "Utility.H"
#include "FArrayBox.H"
#include "Utility.H"
#include "xtra_F.H"

#include "ChemKinDriver.H"
#include "Geometry.H"

//
// This MUST be defined if don't have pubsetbuf() in I/O Streams Library.
//
#ifdef BL_USE_SETBUF
#define pubsetbuf setbuf
#endif

static
void 
print_usage (int,
             char* argv[])
{
    std::cerr << "usage:\n";
    std::cerr << argv[0] << " file=pltfile\n";
    exit(1);
}

class IVLess
{
public:
    bool operator () (const IntVect& lhs,
                      const IntVect& rhs) const
        {
            return lhs.lexLT(rhs);
        }
};

static
void
ClonePlotfile (AmrData&                  amrData,
               PArray<MultiFab>&         mfout,
               std::string&              oFile,
               const Array<std::string>& names)
{
//    return;
    //
    // Sink up grow cells under valid region.
    //
    for (int i = 0; i < mfout.size(); i++)
        mfout[i].FillBoundary();

    if (ParallelDescriptor::IOProcessor())
        if (!BoxLib::UtilCreateDirectory(oFile,0755))
            BoxLib::CreateDirectoryFailed(oFile);
    //
    // Force other processors to wait till directory is built.
    //
    ParallelDescriptor::Barrier();
    
    std::string oFileHeader(oFile);
    oFileHeader += "/Header";
    
    VisMF::IO_Buffer io_buffer(VisMF::IO_Buffer_Size);
    
    std::ofstream os;
    
    os.rdbuf()->pubsetbuf(io_buffer.dataPtr(), io_buffer.size());
    
    os.open(oFileHeader.c_str(), std::ios::out|std::ios::binary);
    
    if (os.fail())
        BoxLib::FileOpenFailed(oFileHeader);
    //
    // Start writing plotfile.
    //
    os << amrData.PlotFileVersion() << '\n';
    int n_var = mfout[0].nComp();
    os << n_var << '\n';
    for (int n = 0; n < n_var; n++) os << names[n] << '\n';
    os << BL_SPACEDIM << '\n';
    os << amrData.Time() << '\n';
    const int finestLevel = mfout.size() - 1;
    os << finestLevel << '\n';
    for (int i = 0; i < BL_SPACEDIM; i++) os << amrData.ProbLo()[i] << ' ';
    os << '\n';
    for (int i = 0; i < BL_SPACEDIM; i++) os << amrData.ProbHi()[i] << ' ';
    os << '\n';
    for (int i = 0; i < finestLevel; i++) os << amrData.RefRatio()[i] << ' ';
    os << '\n';
    for (int i = 0; i <= finestLevel; i++) os << amrData.ProbDomain()[i] << ' ';
    os << '\n';
    for (int i = 0; i <= finestLevel; i++) os << 0 << ' ';
    os << '\n';
    for (int i = 0; i <= finestLevel; i++)
    {
        for (int k = 0; k < BL_SPACEDIM; k++)
            os << amrData.DxLevel()[i][k] << ' ';
        os << '\n';
    }
    os << amrData.CoordSys() << '\n';
    os << "0\n"; // The bndry data width.
    //
    // Write out level by level.
    //
    for (int iLevel = 0; iLevel <= finestLevel; ++iLevel)
    {
        //
        // Write state data.
        //
        int nGrids = amrData.boxArray(iLevel).size();
        char buf[64];
        sprintf(buf, "Level_%d", iLevel);
        
        if (ParallelDescriptor::IOProcessor())
        {
            os << iLevel << ' ' << nGrids << ' ' << amrData.Time() << '\n';
            os << 0 << '\n';
            
            for (int i = 0; i < nGrids; ++i)
            {
                for (int n = 0; n < BL_SPACEDIM; n++)
                {
                    os << amrData.GridLocLo()[iLevel][i][n]
                       << ' '
                       << amrData.GridLocHi()[iLevel][i][n]
                       << '\n';
                }
            }
            //
            // Build the directory to hold the MultiFabs at this level.
            //
            std::string Level(oFile);
            Level += '/';
            Level += buf;
            
            if (!BoxLib::UtilCreateDirectory(Level, 0755))
                BoxLib::CreateDirectoryFailed(Level);
        }
        //
        // Force other processors to wait till directory is built.
        //
        ParallelDescriptor::Barrier();
        //
        // Now build the full relative pathname of the MultiFab.
        //
        static const std::string MultiFabBaseName("/MultiFab");
        
        std::string PathName(oFile);
        PathName += '/';
        PathName += buf;
        PathName += MultiFabBaseName;
        
        if (ParallelDescriptor::IOProcessor())
        {
            //
            // The full name relative to the Header file.
            //
            std::string RelativePathName(buf);
            RelativePathName += MultiFabBaseName;
            os << RelativePathName << '\n';
        }
        //
        // Write out ghost cells as is ...
        //
        VisMF::Write(mfout[iLevel], PathName, VisMF::OneFilePerCPU, false);
    }
    
    os.close();
}

static
vector<std::string>
Tokenize (const std::string& instr,
          const std::string& separators)
{
    vector<char*> ptr;
    //
    // Make copy of line that we can modify.
    //
    char* line = new char[instr.size()+1];

    (void) strcpy(line, instr.c_str());

    char* token = 0;

    if (!((token = strtok(line, separators.c_str())) == 0))
    {
        ptr.push_back(token);
        while (!((token = strtok(0, separators.c_str())) == 0))
            ptr.push_back(token);
    }

    vector<std::string> tokens(ptr.size());

    for (int i = 1; i < ptr.size(); i++)
    {
        char* p = ptr[i];

        while (strchr(separators.c_str(), *(p-1)) != 0)
            *--p = 0;
    }

    for (int i = 0; i < ptr.size(); i++)
        tokens[i] = ptr[i];

    delete line;

    return tokens;
}

static
std::string
GetFileRoot(const std::string& infile)
{
    vector<std::string> tokens = Tokenize(infile,std::string("/"));
    return tokens[tokens.size()-1];
}

static
void
Twiddle (PArray<MultiFab>& conc)
{
    std::cout << "Twiddling concentration ... " << std::endl;

    static Real epsilon = 1.0e-10;
//    static Real epsilon = 0;

    for (int ispec = 0; ispec < conc[0].nComp(); ispec++)
    {
        Real c_floor = 0;

        for (int lev = 0; lev < conc.size(); lev++)
        {
            for (MFIter mfi(conc[lev]); mfi.isValid(); ++mfi)
            {
                const Box& vb = mfi.validbox();

                c_floor = std::max(c_floor,conc[lev][mfi].max(vb,ispec)*epsilon);
            }
        }

        if (ParallelDescriptor::IOProcessor())
        {
            std::cout << "c_floor(" << ispec << "): " << c_floor << std::endl;
        }

        for (int lev = 0; lev < conc.size(); lev++)
        {
            for (MFIter mfi(conc[lev]); mfi.isValid(); ++mfi)
            {
                const long N = conc[lev][mfi].box().numPts();

                Real* data = conc[lev][mfi].dataPtr(ispec);

                for (long i = 0; i < N; i++)
                {
                    data[i] = std::max(data[i],c_floor);
                }       
            }
        }
    }

    std::cout << "Done twiddling concentration. " << std::endl;
}

static
void
WriteTecPlotFile (AmrData&                  amrData,
                  int                       Nspec,
                  const Array<std::string>& chemnames,
                  const PArray<MultiFab>&   u,
                  const PArray<MultiFab>&   mole,
                  const PArray<MultiFab>&   dfd,
                  const PArray<MultiFab>&   ydot,
                  const PArray<MultiFab>&   temp,
                  const PArray<MultiFab>&   Q,
                  const PArray<MultiFab>&   dens)
{
    const int Y  = 0;
    const int FL = amrData.FinestLevel();

    BL_ASSERT(FL == 1);
    BL_ASSERT(amrData.boxArray(FL).size() == 1);

    const Box& bx = amrData.boxArray(FL)[0];
    const int  N  = bx.length(0);

    Array<Real> t(N,0);

    const Real DX = amrData.DxLevel()[FL][0];

    for (int j = 3; j < N-1; j++)
    {
        const IntVect ivj(j,Y), ivjm1(j-1,Y);

        t[j] = t[j-1] + 2*DX/(u[FL][0](ivj,Y)+u[FL][0](ivjm1,Y));
    }

    Array<Real> sum(Nspec,0);

    for (int j = 0; j < Nspec; j++)
        sum[j] = mole[FL][0](IntVect(2,Y),j);

    for (int j = 2; j < N-2; j++)
    {
        IntVect ivj(j,Y), ivjp1(j+1,Y);

        const Real divj   = dens[FL][0](ivj);
        const Real divjp1 = dens[FL][0](ivjp1);

        for (int k = 0; k < Nspec; k++)
        {
            Real tl = (dfd[FL][0](ivj,k)  /divj  +ydot[FL][0](ivj,k));
            Real tr = (dfd[FL][0](ivjp1,k)/divjp1+ydot[FL][0](ivjp1,k));

            sum[k] += (t[j+1]-t[j])*(tl+tr)/2;
        }
    }

    std::cout << std::endl << std::endl;

    for (int i = 0; i < Nspec; i++)
    {
        const Real diff = mole[FL][0](IntVect(N-2,Y),i) - sum[i];

        std::cout << "diff(" << chemnames[i] << "): " << diff << std::endl;
    }

    std::cout << std::endl << std::endl;

//        const int idx = 10; // index of CH4
    //
    // Write out TecPlot header.
    //
    std::string ofile("output.tec");

    std::cout << "Writing TecPlot output to " << ofile << std::endl;

    VisMF::IO_Buffer io_buffer(VisMF::IO_Buffer_Size);

    std::ofstream os;
    
    os.rdbuf()->pubsetbuf(io_buffer.dataPtr(), io_buffer.size());
    
    os.open(ofile.c_str(), std::ios::out);
    
    if (os.fail())
        BoxLib::FileOpenFailed(ofile);

    os << "VARIABLES=\"T\",\"Temp\",\"DTDt\",\"Q\",\"Lambda\"";

    for (int idx = 0; idx < Nspec; idx++)
    {
        char num[64];

        sprintf(num, "%d", idx);

        os << ",\"Y"     << num << "\""
           << ",\"DYDT"  << num << "\""
           << ",\"Diff"  << num << "\""
           << ",\"React" << num << "\""
           << ",\"Sum"   << num << "\"";
    }

    os << std::endl;

    os << "ZONE I=" << (N-4) << " F=POINT" << std::endl;

    for (int j = 2; j < N-2; j++)
    {
        const IntVect ivj(j,Y), ivjp1(j+1,Y);

        const Real DTDt = (temp[FL][0](ivjp1)-temp[FL][0](ivj))/(t[j+1]-t[j]);

        os << t[j]                     << ' '
           << temp[FL][0](ivjp1)       << ' '
           << DTDt                     << ' '
           << Q[FL][0](ivjp1)          << ' '
           << dfd[FL][0](ivjp1,Nspec) << ' ';

        for (int idx = 0; idx < Nspec; idx++)
        {
            const Real divj   = dens[FL][0](ivj);
            const Real divjp1 = dens[FL][0](ivjp1);

            Real a = (mole[FL][0](ivjp1,idx)-mole[FL][0](ivj,idx))/(t[j+1]-t[j]);

            Real b = .5*(dfd[FL][0](ivjp1,idx)/divjp1 +
                         dfd[FL][0](ivj,idx)/divj);

            Real c = .5*(ydot[FL][0](ivjp1,idx) + ydot[FL][0](ivj,idx));

//                Real d = .5*(mole[FL][0](ivjp1,idx) - mole[FL][0](ivj,idx)) *
//                    (u[FL][0](ivjp1,0)+u[FL][0](ivj,0))/DX;

            os << mole[FL][0](ivjp1,idx) << ' '
               << a     << ' '
               << b     << ' '
               << c     << ' '
               << (b+c) << ' ';
        }

        os << std::endl;
    }
}

//
// Position in space.
//
typedef Tuple<Real,BL_SPACEDIM> X;
//
// Holds the level info for an X.
//
struct Info
{
    Info (int lev, int idx, const IntVect& iv)
        :
        m_lev(lev),
        m_idx(idx),
        m_iv(iv)
        {}
    int     m_lev;
    int     m_idx;
    IntVect m_iv;
};

//
// Stuff we set from the AmrData object for March().
//
static int                  finest_level;
static Array<int>           ref_ratio;
static Array<Real>          prob_lo;
static Array<Real>          prob_hi;
static Array<Box>           prob_domain;
static Array<BoxArray>      boxArray;
static Array< Array<Real> > dx;
static Array< Array<Real> > dxinv;

inline
IntVect
Index (const X& x,
       int      lev)
{
    IntVect iv;

    const IntVect& lo = prob_domain[lev].smallEnd();
        
    D_TERM(iv[0]=int((x[0]-lo[0])*dxinv[lev][0]);,
           iv[1]=int((x[1]-lo[1])*dxinv[lev][1]);,
           iv[2]=int((x[2]-lo[2])*dxinv[lev][2]););

    iv += lo;

    return iv;
}

static
bool
Where (const X& x,
       int&     lev,
       int&     idx,
       IntVect& iv)
{
    for (lev = finest_level; lev >= 0; lev--)
    {
        iv = Index(x,lev);

        const BoxArray& ba = boxArray[lev];

        for (idx = 0; idx < ba.size(); idx++)
            if (ba[idx].contains(iv))
                return true;
    }

    return false;
}

static
void
DoIt (X&                      x,
      Real                    dt,
      std::vector<X>&         path,
      std::vector<Info>&      info,
      const PArray<MultiFab>& u)
{
    std::cout << "Starting point: " << x[0] << ' ' << x[1] << std::endl;

    for (;;)
    {
        int     lev, levS; // Our level in the amrData hierarchy.
        int     idx, idxS; // The index into the BoxArray at that level.
        IntVect iv,  ivS;  // The IntVect into relevant FAB at that level.

        X xk = path.back(), xS;

        if (!Where(xk,lev,idx,iv))
        {
            path.pop_back();

            break;
        }

        info.push_back(Info(lev,idx,iv));

        for (int i = 0; i < BL_SPACEDIM; i++)
            xS[i]=xk[i]+u[lev][idx](iv,i)*dt;

        if (!Where(xS,levS,idxS,ivS)) break;

        for (int i = 0; i < BL_SPACEDIM; i++)
            x[i]=xk[i]+0.5*(u[lev][idx](iv,i)+u[levS][idxS](ivS,i))*dt;

        path.push_back(x);
    }
}

static
void
WriteBilinearData (const X&                 pos,
                   const std::vector<X>&    path,
                   const std::vector<Info>& info,
                   const PArray<MultiFab>&  y,
                   const PArray<MultiFab>&  temp,
                   const PArray<MultiFab>&  rho,
                   Real                     dt)
{
    std::cout << "path.size() = " << path.size() << std::endl;

    const int Nspec = y[0].nComp();

    char buf[64];

    sprintf(buf, "out.x=%g,y=%g.raw.bl", pos[0], pos[1]);

    std::ofstream ofs;

    std::cout << "Writing Bilinear data to \"" << buf << "\"" << std::endl;

    ofs.open(buf,std::ios::out|std::ios::trunc);

    if (ofs.fail()) BoxLib::FileOpenFailed(buf);

    ofs << "VARIABLES=\"T\",\"Temp\",\"Rho\",\"X\",\"Y\"";

    for (int idx = 0; idx < Nspec; idx++)
    {
        char num[64];

        sprintf(num, "%d", idx);

        ofs << ",\"Y" << num << "\"";
    }

    ofs << std::endl;

    ofs << "ZONE I=" << path.size() << " F=POINT" << std::endl;

    for (int i = 0; i < path.size(); i++)
    {
        int     lev = info[i].m_lev;
        int     idx = info[i].m_idx;
        IntVect iv  = info[i].m_iv;
        IntVect z00 = iv;

        Real xnorm  = path[i][0]*dxinv[lev][0] - (iv[0]+.5);
        Real ynorm  = path[i][1]*dxinv[lev][1] - (iv[1]+.5);

        if (path[i][0] >= (iv[0]+.5)*dx[lev][0])
        {
            if (path[i][1] < (iv[1]+.5)*dx[lev][1])
            {
                ynorm  += 1;
                z00[1] -= 1;
            }
        }
        else
        {
            xnorm  += 1;
            z00[0] -= 1;

            if (path[i][1] < (iv[1]+.5)*dx[lev][1])
            {
                ynorm  += 1;
                z00[1] -= 1;
            }
        }

        BL_ASSERT(xnorm >= 0 && xnorm <= 1);
        BL_ASSERT(ynorm >= 0 && ynorm <= 1);

        IntVect z01(z00[0],  z00[1]+1);
        IntVect z10(z00[0]+1,z00[1]);
        IntVect z11(z00[0]+1,z00[1]+1);

        const FArrayBox& yfab = y[lev][idx];
        const FArrayBox& rfab = rho[lev][idx];
        const FArrayBox& tfab = temp[lev][idx];

        const Real T = (1-xnorm)*(1-ynorm)*tfab(z00) +
                       (1-xnorm)*ynorm*tfab(z01)     +
                       xnorm*(1-ynorm)*tfab(z10)     +
                       xnorm*ynorm*tfab(z11);

        const Real R = (1-xnorm)*(1-ynorm)*rfab(z00) +
                       (1-xnorm)*ynorm*rfab(z01)     +
                       xnorm*(1-ynorm)*rfab(z10)     +
                       xnorm*ynorm*rfab(z11);

        ofs << (dt*i)     << ' '
            << T          << ' '
            << R          << ' '
            << path[i][0] << ' '
            << path[i][1] << ' ';

        for (int j = 0; j < Nspec; j++)
        {
            const Real Y = (1-xnorm)*(1-ynorm)*yfab(z00,j) +
                           (1-xnorm)*ynorm*yfab(z01,j)     +
                           xnorm*(1-ynorm)*yfab(z10,j)     +
                           xnorm*ynorm*yfab(z11,j);

            ofs << Y << ' ';
        }

        ofs << '\n';
    }
}

static
void
WriteRawData (const X&                 pos,
              const std::vector<X>&    path,
              const std::vector<Info>& info,
              const PArray<MultiFab>&  y,
              const PArray<MultiFab>&  temp,
              const PArray<MultiFab>&  ydot,
              const PArray<MultiFab>&  dfd,
              Real                     dt)
{
    std::cout << "path.size() = " << path.size() << std::endl;

    const int Nspec = y[0].nComp();

    Array<Real> ybar(Nspec,0);
    Array<Real> ydotbar(Nspec,0);
    Array<Real> ydifbar(Nspec,0);

    char buf[64];

    sprintf(buf, "out.x=%g,y=%g.raw.tec", pos[0], pos[1]);

    std::ofstream ofs;

    std::cout << "Writing TecPlot raw data to \"" << buf << "\"" << std::endl;

    ofs.open(buf,std::ios::out|std::ios::trunc);

    if (ofs.fail())
        BoxLib::FileOpenFailed(buf);

    ofs << "VARIABLES=\"T\",\"Temp\",\"X\",\"Y\"";

    for (int idx = 0; idx < Nspec; idx++)
    {
        char num[64];

        sprintf(num, "%d", idx);

        ofs << ",\"Y"      << num << "\""
            << ",\"Ybar"   << num << "\""
            << ",\"Ybar-Y" << num << "\"";
    }

    ofs << std::endl;

    ofs << "ZONE I=" << path.size() << " F=POINT" << std::endl;

    for (int i = 0; i < path.size(); i++)
    {
        int     lev = info[i].m_lev, levm1;
        int     idx = info[i].m_idx, idxm1;
        IntVect iv  = info[i].m_iv, ivm1;

        if (i > 0)
        {
            levm1 = info[i-1].m_lev;
            idxm1 = info[i-1].m_idx;
            ivm1  = info[i-1].m_iv;
        }

        ofs << (dt*i)             << ' '
            << temp[lev][idx](iv) << ' '
            << path[i][0]         << ' '
            << path[i][1]         << ' ';

        for (int j = 0; j < Nspec; j++)
        {
            if (i == 0)
            {
                ybar[j] = y[lev][idx](iv,j);
                ydotbar[j] = 0;
                ydifbar[j] = 0;
            }
            else
            {
                ybar[j] += 0.5*dt*(ydot[lev][idx](iv,j)       +
                                   ydot[levm1][idxm1](ivm1,j) +
                                   dfd[lev][idx](iv,j)        +
                                   dfd[levm1][idxm1](ivm1,j));

                ydotbar[j] += 0.5*dt*(ydot[lev][idx](iv,j) +
                                      ydot[levm1][idxm1](ivm1,j));

                ydifbar[j] += 0.5*dt*(dfd[lev][idx](iv,j) +
                                      dfd[levm1][idxm1](ivm1,j));
            }

            ofs << y[lev][idx](iv,j)           << ' '
                << ybar[j]                     << ' '
                << (ybar[j]-y[lev][idx](iv,j)) << ' ';
        }

        ofs << '\n';

        if (i == path.size()-1)
        {
            for (int j = 0; j < Nspec; j++)
            {
                std::cout << j << ' '
                          << y[lev][idx](iv,j) << ' '
                          << ybar[j] << ' '
                          << ydotbar[j] << ' '
                          << ydifbar[j] << std::endl;
            }
        }
    }
}

static
void
WriteProcData (const X&                 pos,
               const std::vector<X>&    path,
               const std::vector<Info>& info,
               const PArray<MultiFab>&  y,
               const PArray<MultiFab>&  temp,
               const PArray<MultiFab>&  ydot,
               const PArray<MultiFab>&  dfd,
               const PArray<MultiFab>&  q,
               Real                     dt)
{
    std::cout << "path.size() = " << path.size() << std::endl;

    char buf[64];

    sprintf(buf, "out.x=%g,y=%g.proc.tec", pos[0], pos[1]);

    std::ofstream ofs;

    std::cout << "Writing TecPlot processed data to \"" << buf << "\"" << std::endl;

    ofs.open(buf,std::ios::out|std::ios::trunc);

    if (ofs.fail())
        BoxLib::FileOpenFailed(buf);

    ofs << "VARIABLES=\"T\",\"DT\",\"DQ\",\"DLambda\"";

    const int Nspec = y[0].nComp();

    BL_ASSERT(dfd[0].nComp() == Nspec+1);

    for (int idx = 0; idx < Nspec; idx++)
    {
        char num[64];

        sprintf(num, "%d", idx);

        ofs << ",\"DY"    << num << "\""
            << ",\"DYdot" << num << "\""
            << ",\"Ddfd"  << num << "\"";
    }

    ofs << '\n';

    ofs << "ZONE I=" << (path.size()-1) << " F=POINT" << std::endl;

    for (int i = 1; i < path.size(); i++)
    {
        int     lev = info[i].m_lev, levm1 = info[i-1].m_lev;
        int     idx = info[i].m_idx, idxm1 = info[i-1].m_idx;
        IntVect iv  = info[i].m_iv,  ivm1  = info[i-1].m_iv;

        ofs << (dt*(2*i-1)/2)                                     << ' '
            << ((temp[lev][idx](iv)-temp[levm1][idxm1](ivm1))/dt) << ' '
            << ((q[lev][idx](iv)+q[levm1][idxm1](ivm1))/2)        << ' '
            << ((dfd[lev][idx](iv,Nspec)+dfd[levm1][idxm1](ivm1,Nspec))/2) << ' ';

        for (int j = 0; j < Nspec; j++)
        {
            ofs << ((y[lev][idx](iv,j)-y[levm1][idxm1](ivm1,j))/dt)      << ' '
                << ((ydot[lev][idx](iv,j)+ydot[levm1][idxm1](ivm1,j))/2) << ' '
                << ((dfd[lev][idx](iv,j)+dfd[levm1][idxm1](ivm1,j))/2)   << ' ';
        }

        ofs << '\n';
    }
}

static
void
March (AmrData&                  amrData,
       const std::vector<X>&     pos,
       const PArray<MultiFab>&   u,
       const PArray<MultiFab>&   y,
       const PArray<MultiFab>&   dfd,
       const PArray<MultiFab>&   ydot,
       const PArray<MultiFab>&   temp,
       const PArray<MultiFab>&   q,
       const PArray<MultiFab>&   rho)
{
    //
    // Figure out largest timestep we can reliably use.
    //
//    const Real cfl = 1.0;
    const Real cfl = 0.5;

    Real dt = std::numeric_limits<Real>::max();

    for (int i = 0; i <= finest_level; i++)
        for (int j = 0; j < BL_SPACEDIM; j++)
            dt = std::min(dt,cfl*dx[i][j]/u[i].max(j));

    std::cout << "Marching using dt = " << dt << std::endl;

    for (int idx = 0; idx < pos.size(); idx++)
    {
        X x;

        std::vector<X>    path;
        std::vector<Info> info;

        D_TERM(x[0]=pos[idx][0];, x[1]=pos[idx][1];, x[2]=pos[idx][2];);

        path.push_back(x);

        DoIt(x,dt,path,info,u);

        BL_ASSERT(path.size() == info.size());

        WriteBilinearData(pos[idx],path,info,y,temp,rho,dt);

        WriteRawData(pos[idx],path,info,y,temp,ydot,dfd,dt);

        WriteProcData(pos[idx],path,info,y,temp,ydot,dfd,q,dt);
    }
}

static
void
WriteMF (const MultiFab* mf,
         const char*     file)
{
    std::string FullPath = file;

    if (!FullPath.empty() && FullPath[FullPath.length()-1] != '/')
        FullPath += '/';
	    
    if (ParallelDescriptor::IOProcessor())
        if (!BoxLib::UtilCreateDirectory(FullPath, 0755))
            BoxLib::CreateDirectoryFailed(FullPath);

    ParallelDescriptor::Barrier();
	    
    static const std::string MultiFabBaseName("MultiFab");

    FullPath += MultiFabBaseName;
	    
    VisMF::Write(*mf,FullPath,VisMF::OneFilePerCPU);
}

static
void
WriteOutAsMFs (const char*             name,
               const PArray<MultiFab>& y)
{
    return;

    for (int l = 0; l < y.size(); l++)
    {
        const int NGrow = y[l].nGrow();

        BoxList bl;
        for (int i = 0; i < y[l].boxArray().size(); i++)
            bl.push_back(BoxLib::grow(y[l].boxArray()[i],NGrow));

        BoxArray nba(bl);

        MultiFab ynew(nba,y[l].nComp(),0);

        for (MFIter mfi(ynew); mfi.isValid(); ++mfi)
            ynew[mfi].copy(y[l][mfi],y[l][mfi].box());

        char buf[64];
        sprintf(buf, "%s.lev=%d.mfab", name, l);

        WriteMF(&ynew,buf);
    }
}

int
main (int   argc,
      char* argv[])
{
    BoxLib::Initialize(argc,argv);

    const int MyProc = ParallelDescriptor::MyProc();

    if (argc < 2)
        print_usage(argc,argv);

    ParmParse pp;

    if (pp.contains("help"))
        print_usage(argc,argv);

    std::string infile;
    pp.get("file",infile);

    DataServices::SetBatchMode();
    FileType fileType(NEWPLT);
    DataServices dataServices(infile, fileType);

    if (!dataServices.AmrDataOk())
        //
        // This calls ParallelDescriptor::EndParallel() and exit()
        //
        DataServices::Dispatch(DataServices::ExitRequest, NULL);
    
    AmrData& amrData = dataServices.AmrDataRef();
    
    int Nlev = amrData.FinestLevel() + 1;

    std::string chemInFile  = "grimech30.dat";
    std::string chemOutFile = "grimech30.out";
    std::string thermInFile = "thermo30.dat";
    std::string tranInFile  = "transport30.dat";

//    std::string chemInFile  = "drm19_noAR.dat";
//    std::string chemOutFile = "drm19_noAR.out";
//    std::string thermInFile = "thermo12.dat";
//    std::string tranInFile  = "transport12.dat";

    ChemKinDriver ckd(chemInFile,chemOutFile,thermInFile,tranInFile);

    const Array<std::string> chemnames = ckd.speciesNames();

    const Real Patm = 1.0;
    
    Array<std::string> plotnames = amrData.PlotVarNames();
    int idT = -1;
    int idX = -1;
    int idU = -1;
    int idR = -1;
    for (int i = 0; i < plotnames.size(); ++i)
    {
        if (plotnames[i] == "temp") idT = i;
        if (plotnames[i] == "X(" + chemnames[0] + ")" ) idX = i;
        if (plotnames[i] == "x_velocity") idU = i;
        if (plotnames[i] == "density") idR = i;
    }

    std::string trElt = "C";
//    std::string trElt = "N";

    vector<std::string> trSp;
    vector<int>         trRxn;

    bool use_all_species = true;

    for (int i = 0; i < chemnames.size(); i++)
        if (use_all_species || ckd.numberOfElementXinSpeciesY(trElt,chemnames[i]) > 0)
            trSp.push_back(chemnames[i]);

    Array< std::pair<std::string,int> > speccoef;

    for (int i = 0; i < ckd.numReactions(); i++)
    {
        if (use_all_species)
        {
            trRxn.push_back(i);
        }
        else
        {
            std::map<std::string,int> net;

            speccoef = ckd.specCoeffsInReactions(i);

            for (int j = 0; j < speccoef.size(); j++)
            {
                const int          co = speccoef[j].second;
                const std::string& sp = speccoef[j].first;

                if (std::count(trSp.begin(),trSp.end(),sp) > 0)
                {
                    if (net.find(sp) == net.end())
                    {
                        net[sp] = co;
                    }
                    else
                    {
                        net[sp] += co;
                        if (net[sp] == 0) net.erase(sp);
                    }
                }
            }

            if (!net.empty())
                trRxn.push_back(i);
        }
    }

    const int Nspec = trSp.size();
    const int Nreac = trRxn.size();

    std::cout << "Nspec = " << Nspec << std::endl;
    std::cout << "Nreac = " << Nreac << std::endl;

    for (int i = 0; i < trSp.size(); i++)
        std::cout << i << ": " << chemnames[ckd.index(trSp[i])] << std::endl;

    PArray<MultiFab> temp(Nlev,PArrayManage);
    PArray<MultiFab> mole(Nlev,PArrayManage);
    PArray<MultiFab> conc(Nlev,PArrayManage);
    PArray<MultiFab> dens(Nlev,PArrayManage);

    Array<int> rxnIDs(Nreac);

    for (int i = 0; i < Nreac; i++)
        rxnIDs[i] = trRxn[i];

    FArrayBox tmpfab;

    for (int lev = 0; lev < Nlev; ++lev)
    {
        temp.set(lev,new MultiFab(amrData.boxArray(lev),1,1));
        mole.set(lev,new MultiFab(amrData.boxArray(lev),ckd.numSpecies(),1));
        dens.set(lev,new MultiFab(amrData.boxArray(lev),1,1));

        std::cout << "Reading mole, temp & density data at lev=" << lev << " ... " << std::flush;

        temp[lev].copy(amrData.GetGrids(lev,idT),0,0,1);

        amrData.FlushGrids(idT);

        dens[lev].copy(amrData.GetGrids(lev,idR),0,0,1);

        amrData.FlushGrids(idR);

        for (int j = 0; j < ckd.numSpecies(); ++j)
        {
            mole[lev].copy(amrData.GetGrids(lev,idX+j),0,j,1);

            amrData.FlushGrids(idX+j);
        }

        for (MFIter mfi(mole[lev]); mfi.isValid(); ++mfi)
        {
            const Box& box = mfi.validbox();

            int ncompM   = mole[lev].nComp();
            int ncompTnD = temp[lev].nComp();

            FORT_PUSHVTOG(box.loVect(),box.hiVect(),
                          box.loVect(),
                          box.hiVect(),
                          temp[lev][mfi].dataPtr(),
                          ARLIM(temp[lev][mfi].loVect()),
                          ARLIM(temp[lev][mfi].hiVect()),&ncompTnD);

            FORT_PUSHVTOG(box.loVect(),box.hiVect(),
                          box.loVect(),
                          box.hiVect(),
                          dens[lev][mfi].dataPtr(),
                          ARLIM(dens[lev][mfi].loVect()),
                          ARLIM(dens[lev][mfi].hiVect()),&ncompTnD);

            FORT_PUSHVTOG(box.loVect(),box.hiVect(),
                          box.loVect(),
                          box.hiVect(),
                          mole[lev][mfi].dataPtr(),
                          ARLIM(mole[lev][mfi].loVect()),
                          ARLIM(mole[lev][mfi].hiVect()),&ncompM);
        }

        temp[lev].FillBoundary();
        dens[lev].FillBoundary();
        mole[lev].FillBoundary();

#if 0
        //
        // TODO -- ask Marc why this is needed?
        //
        const BoxArray& ba = amrData.boxArray(lev);
        std::vector< PArray<FArrayBox> > auxg(ba.size());
        for (int i=0; i<ba.size();++i)
        {
            Box gbox = ba[i];
            for (int k=0; k<BL_SPACEDIM; ++k)
                if (!(geoms[lev].isPeriodic(k)))
                    gbox.grow(k,nGrow);

            BoxArray bai = BoxArray(BoxLib::complementIn(gbox,BoxList(ba)));

            if (bai.size() != 0)
            {
                auxg[i].resize(bai.size());
                for (int j=0; j<auxg[i].size(); ++j)
                    auxg[i].set(j,new FArrayBox(bai[j],nCompIn));
            }
        }
        //
        // Fill data I own.
        //
        for (MFIter mfi(state[lev]); mfi.isValid(); ++mfi)
            for (int j=0; j<auxg[mfi.index()].size(); ++j)
                auxg[mfi.index()][j].copy(state[lev][mfi],0,0,nCompIn);
        //
        // Broadcast my data to others, and get their data.
        //
        const DistributionMapping& stateDM = state[lev].DistributionMap();
        for (int i=0; i<state[lev].size(); ++i)
        {
            for (int j=0; j<auxg[i].size(); ++j)
            {
                FArrayBox& fab = auxg[i][j];
                ParallelDescriptor::Bcast(fab.dataPtr(),
                                          fab.box().numPts()*nCompIn,
                                          stateDM[i]);
            }
        }
        //
        // Copy boundary data from other grids into my grow cells.
        //
        for (MFIter mfi(state[lev]); mfi.isValid(); ++mfi)
            for (int j=0; j<auxg.size(); ++j)
                if (j!=mfi.index())
                    for (int k=0; k<auxg[j].size(); k++)
                        state[lev][mfi].copy(auxg[j][k],0,0,nCompIn);
#endif

        for (MFIter mfi(mole[lev]); mfi.isValid(); ++mfi)
        {
            FArrayBox& x = mole[lev][mfi];
            FArrayBox& t = temp[lev][mfi];

            Box bx = t.box() & amrData.ProbDomain()[lev];

            ckd.moleFracToMassFrac(x,x,bx,0,0);
            ckd.normalizeMassFrac(x,x,"N2",bx,0,0);

            ckd.massFracToMolarConc(x,x,t,Patm,bx,0,0,0);
            //
            // mole now contains concentration ...
            //
        }

        std::cout << "done" << std::endl;
    }
    //
    // Got to twiddle the concentration.
    // Then save the twiddle'd concentration.
    // Then convert the twiddle'd concentration back to mole fractions.
    //
    Twiddle(mole);

    for (int lev = 0; lev < Nlev; ++lev)
    {
        std::cout << "Building Conc lev=" << lev << " ... " << std::flush;

        conc.set(lev,new MultiFab(amrData.boxArray(lev),Nspec,0));

        for (MFIter mfi(mole[lev]); mfi.isValid(); ++mfi)
        {
            FArrayBox& c = conc[lev][mfi];
            FArrayBox& x = mole[lev][mfi];

            for (int i = 0; i < Nspec; i++)
                c.copy(x,ckd.index(trSp[i]),i,1);

            ckd.molarConcToMoleFrac(x,x,x.box(),0,0);
            //
            // mole now contains twiddle'd mole fractions.
            //
        }

        std::cout << "done" << std::endl;
    }

    PArray<MultiFab> rf(Nlev,PArrayManage);
    PArray<MultiFab> rr(Nlev,PArrayManage);

    for (int lev = 0; lev < Nlev; ++lev)
    {
        std::cout << "Building Rf & Rr lev=" << lev << " ... " << std::flush;

        rf.set(lev,new MultiFab(amrData.boxArray(lev),Nreac,0));
        rr.set(lev,new MultiFab(amrData.boxArray(lev),Nreac,0));

        for (MFIter mfi(mole[lev]); mfi.isValid(); ++mfi)
        {
            FArrayBox& f = rf[lev][mfi];
            FArrayBox& r = rr[lev][mfi];
            FArrayBox& t = temp[lev][mfi];
            FArrayBox& x = mole[lev][mfi];

            Box bx = t.box() & amrData.ProbDomain()[lev];

            ckd.fwdRevReacRatesGivenXTP(f,r,rxnIDs,x,t,Patm,bx,0,0,0,0);

            ckd.moleFracToMassFrac(x,x,bx,0,0);
            ckd.normalizeMassFrac(x,x,"N2",bx,0,0);
            //
            // mole now contains mass fractions!!!
            //
        }

        std::cout << "done" << std::endl;
    }

    std::string nfile_conc(GetFileRoot(infile)+"_conc");
    Array<std::string> nnames_conc(Nspec);
    for (int j = 0; j < Nspec; ++j)
        nnames_conc[j] = "C(" + chemnames[ckd.index(trSp[j])] + ")";

    std::cout << "Writing Concentration to " << nfile_conc << std::endl;
    ClonePlotfile(amrData,conc,nfile_conc,nnames_conc);

    std::string nfile_temp(GetFileRoot(infile)+"_temp");
    Array<std::string> nnames_temp(1);
    nnames_temp[0] = "temp";

    std::cout << "Writing Temperature to " << nfile_temp << std::endl;
    ClonePlotfile(amrData,temp,nfile_temp,nnames_temp);

    std::string nfile_mass(GetFileRoot(infile)+"_mass");
    Array<std::string> nnames_mass(ckd.numSpecies());
    for (int j = 0; j < nnames_mass.size(); ++j)
        nnames_mass[j] = "Y(" + chemnames[j] + ")";

    std::cout << "Writing Mass Fractions to " << nfile_mass << std::endl;
    ClonePlotfile(amrData,mole,nfile_mass,nnames_mass);

    WriteOutAsMFs("mass",mole);
    WriteOutAsMFs("temp",temp);

    std::string nfile_rf(GetFileRoot(infile)+"_rf");
    std::string nfile_rr(GetFileRoot(infile)+"_rr");

    Array<std::string> nnames_rf(Nreac);
    for (int j = 0; j < Nreac; ++j)
        nnames_rf[j] = ckd.reactionString(trRxn[j]);

    std::cout << "Writing Forward Rates to " << nfile_rf << std::endl;
    ClonePlotfile(amrData,rf,nfile_rf,nnames_rf);

    std::cout << "Writing Backward Rates to " << nfile_rr << std::endl;
    ClonePlotfile(amrData,rr,nfile_rr,nnames_rf);
    //
    // Free up some space; don't free up temp and mole or mass.
    //
    rf.clear(); rr.clear(); conc.clear();
    //
    // Now do velocity ...
    //
    PArray<MultiFab> u(Nlev,PArrayManage);

    for (int lev = 0; lev < Nlev; ++lev)
    {
        u.set(lev,new MultiFab(amrData.boxArray(lev),BL_SPACEDIM,0));

        u[lev].setVal(0);

        std::cout << "Reading velocity data at lev=" << lev << " ... " << std::flush;

        for (int j = 0; j < BL_SPACEDIM; ++j)
        {
            u[lev].copy(amrData.GetGrids(lev,idU+j),0,j,1);
            amrData.FlushGrids(idU+j);
        }

        std::cout << "done" << std::endl;
    }

    const char* VelName[3] = { "x_velocity", "y_velocity", "z_velocity" };
    std::string nfile_u(GetFileRoot(infile)+"_u");
    Array<std::string> nnames_u(BL_SPACEDIM);
    for (int j = 0; j < BL_SPACEDIM; ++j)
        nnames_u[j] = VelName[j];

    std::cout << "Writing Velocity to " << nfile_u << std::endl;
    ClonePlotfile(amrData,u,nfile_u,nnames_u);

//    u.clear();
    //
    // Now do a, b, c & d ...
    //
    PArray<MultiFab> a(Nlev,PArrayManage);
    PArray<MultiFab> b(Nlev,PArrayManage);
    PArray<MultiFab> c(Nlev,PArrayManage);
    PArray<MultiFab> d(Nlev,PArrayManage);

    FArrayBox rho, rhoDr, getR, cpmix;

    bool is_rz = amrData.CoordSys() == CoordSys::RZ;

    if (is_rz)
        std::cout << "is_rz: true" << std::endl;

    for (int lev = 0; lev < Nlev; ++lev)
    {
        //
        // One extra space for lambda stuff after mass fractions ...
        //
        a.set(lev,new MultiFab(amrData.boxArray(lev),Nspec+1,0));
        b.set(lev,new MultiFab(amrData.boxArray(lev),Nspec+1,0));
        c.set(lev,new MultiFab(amrData.boxArray(lev),Nspec+1,0));
        d.set(lev,new MultiFab(amrData.boxArray(lev),Nspec+1,0));

        std::cout << "Building A B C & D at lev=" << lev << " ... " << std::flush;
        //
        // We have temp and mass fractions over grow cells within ProbDomain.
        //
        for (MFIter mfi(mole[lev]); mfi.isValid(); ++mfi)
        {
            Box bx = mole[lev][mfi].box() & amrData.ProbDomain()[lev];

            rho.resize(bx,1);
            rhoDr.resize(bx,ckd.numSpecies()+1);
            getR.resize(bx,1);
            cpmix.resize(bx,1);

            rho.copy(dens[lev][mfi]);

            ckd.getCpmixGivenTY(cpmix,temp[lev][mfi],mole[lev][mfi],bx,0,0,0);
            
            ckd.getMixAveragedRhoDiff(rhoDr,mole[lev][mfi],temp[lev][mfi],Patm,bx,0,0,0);
            //
            // Stuff thermal conductivity into last component.
            //
            ckd.getMixCond(rhoDr,mole[lev][mfi],temp[lev][mfi],bx,0,0,Nspec);

            IntVect se = bx.smallEnd();
            IntVect be = bx.bigEnd();

            const Real dx = amrData.DxLevel()[lev][0];
            const Real dy = amrData.DxLevel()[lev][1];

            if (is_rz)
            {
                //
                // Set rhoDr to rhodiff*r
                //
                const Real dr = amrData.DxLevel()[lev][0];

                for (int i = se[0]; i <= be[0]; i++)
                {
                    IntVect sbse = IntVect(i,se[1]);
                    IntVect sbbe = IntVect(i,be[1]);
                    getR.setVal((i+0.5)*dr,Box(sbse,sbbe),0,1);
                }

                for (int i = 0; i < ckd.numSpecies(); i++)
                    rhoDr.mult(getR,0,i,1);
                //
                // Set rho to rho*r*dr*dr*2
                //
                for (int i = se[0]; i <= be[0]; i++)
                {
                    IntVect sbse = IntVect(i,se[1]);
                    IntVect sbbe = IntVect(i,be[1]);
                    getR.setVal((i+0.5)*dr*dr*dr*2,Box(sbse,sbbe),0,1);
                }

                rho.mult(getR);
            }

            tmpfab.resize(bx,1);

            for (int i = 0; i < Nspec+1; i++)
            {
                tmpfab.copy(rhoDr, i==Nspec?Nspec:ckd.index(trSp[i]), 0, 1);

                FArrayBox& afab = a[lev][mfi];
                FArrayBox& bfab = b[lev][mfi];
                FArrayBox& cfab = c[lev][mfi];
                FArrayBox& dfab = d[lev][mfi];

                afab.copy(tmpfab,0,i,1);
                tmpfab.shift(0,1);
                afab.plus(tmpfab,0,i,1);
                tmpfab.shift(0,-1);
                afab.divide(rho,0,i,1);

                bfab.copy(tmpfab,0,i,1);
                tmpfab.shift(0,-1);
                bfab.plus(tmpfab,0,i,1);
                tmpfab.shift(0,1);
                bfab.divide(rho,0,i,1);

                cfab.copy(tmpfab,0,i,1);
                tmpfab.shift(1,1);
                cfab.plus(tmpfab,0,i,1);
                tmpfab.shift(1,-1);
                cfab.divide(rho,0,i,1);

                dfab.copy(tmpfab,0,i,1);
                tmpfab.shift(1,-1);
                dfab.plus(tmpfab,0,i,1);
                tmpfab.shift(1,1);
                dfab.divide(rho,0,i,1);

                if (!is_rz)
                {
                    afab.divide(dx*dx*2,i,1);
                    bfab.divide(dx*dx*2,i,1);
                    cfab.divide(dy*dy*2,i,1);
                    dfab.divide(dy*dy*2,i,1);
                }

                if (i == Nspec)
                {
                    afab.divide(cpmix,0,i,1);
                    bfab.divide(cpmix,0,i,1);
                    cfab.divide(cpmix,0,i,1);
                    dfab.divide(cpmix,0,i,1);
                }
            }
        }

        amrData.FlushGrids(idR);

        const Box& domain = amrData.ProbDomain()[lev];

        a[lev].setVal(0,domain & Box(domain).shift(0,-domain.length(0)+1),0,Nspec,0);
        b[lev].setVal(0,domain & Box(domain).shift(0, domain.length(0)-1),0,Nspec,0);
        c[lev].setVal(0,domain & Box(domain).shift(1,-domain.length(1)+1),0,Nspec,0);
        d[lev].setVal(0,domain & Box(domain).shift(1, domain.length(1)-1),0,Nspec,0);

        std::cout << "done" << std::endl;
    }

    std::string nfile_rho(GetFileRoot(infile)+"_rho");
    Array<std::string> nnames_rho(1);
    nnames_rho[0] = "rho";

    std::cout << "Writing Rho to " << nfile_rho << std::endl;
    ClonePlotfile(amrData,dens,nfile_rho,nnames_rho);

    std::string nfile_a(GetFileRoot(infile)+"_a");
    Array<std::string> nnames_a(Nspec+1);
    for (int j = 0; j < Nspec; ++j)
        nnames_a[j] = "a_" + chemnames[ckd.index(trSp[j])];
    nnames_a[Nspec] = "a_lambda";

    std::cout << "Writing A to " << nfile_a << std::endl;
    ClonePlotfile(amrData,a,nfile_a,nnames_a);

    std::string nfile_b(GetFileRoot(infile)+"_b");
    Array<std::string> nnames_b(Nspec+1);
    for (int j = 0; j < Nspec; ++j)
        nnames_b[j] = "b_" + chemnames[ckd.index(trSp[j])];
    nnames_b[Nspec] = "b_lambda";

    std::cout << "Writing B to " << nfile_b << std::endl;
    ClonePlotfile(amrData,b,nfile_b,nnames_b);

    std::string nfile_c(GetFileRoot(infile)+"_c");
    Array<std::string> nnames_c(Nspec+1);
    for (int j = 0; j < Nspec; ++j)
        nnames_c[j] = "c_" + chemnames[ckd.index(trSp[j])];
    nnames_c[Nspec] = "c_lambda";

    std::cout << "Writing C to " << nfile_c << std::endl;
    ClonePlotfile(amrData,c,nfile_c,nnames_c);

    std::string nfile_d(GetFileRoot(infile)+"_d");
    Array<std::string> nnames_d(Nspec+1);
    for (int j = 0; j < Nspec; ++j)
        nnames_d[j] = "d_" + chemnames[ckd.index(trSp[j])];
    nnames_d[Nspec] = "d_lambda";

    std::cout << "Writing D to " << nfile_d << std::endl;
    ClonePlotfile(amrData,d,nfile_d,nnames_d);

    PArray<MultiFab> Q(Nlev,PArrayManage);

    for (int lev = 0; lev < Nlev; ++lev)
    {
        std::cout << "Building HeatRelease at lev=" << lev << " ... " << std::flush;

        Q.set(lev,new MultiFab(amrData.boxArray(lev),1,0));

        for (MFIter mfi(Q[lev]); mfi.isValid(); ++mfi)
        {
            Box bx = Q[lev][mfi].box() & amrData.ProbDomain()[lev];

            cpmix.resize(bx,1);

            ckd.heatRelease(Q[lev][mfi],mole[lev][mfi],temp[lev][mfi],Patm,bx,0,0,0);

            ckd.getCpmixGivenTY(cpmix,temp[lev][mfi],mole[lev][mfi],bx,0,0,0);

            Q[lev][mfi].divide(cpmix,0,0,1);

            Q[lev][mfi].divide(dens[lev][mfi],0,0,1);
        }

        std::cout << "done" << std::endl;
    }

    std::string nfile_q(GetFileRoot(infile)+"_q");
    Array<std::string> nnames_q(1);
    nnames_q[0] = "Q";

    std::cout << "Writing HeatRelease to " << nfile_q << std::endl;
    ClonePlotfile(amrData,Q,nfile_q,nnames_q);

    PArray<MultiFab> ydot(Nlev,PArrayManage);
    PArray<MultiFab> dfd(Nlev,PArrayManage);

    for (int lev = 0; lev < Nlev; ++lev)
    {
        std::cout << "Building Ydot at lev=" << lev << " ... " << std::flush;

        ydot.set(lev,new MultiFab(amrData.boxArray(lev),Nspec,0));
        dfd.set(lev,new MultiFab(amrData.boxArray(lev),Nspec+1,0));

        for (MFIter mfi(ydot[lev]); mfi.isValid(); ++mfi)
        {
            Box bx = temp[lev][mfi].box() & amrData.ProbDomain()[lev];

            ckd.reactionRateY(ydot[lev][mfi],mole[lev][mfi],temp[lev][mfi],Patm,bx,0,0,0);
        }

        std::cout << "done" << std::endl;
    }

    std::string nfile_ydot(GetFileRoot(infile)+"_ydot");
    Array<std::string> nnames_ydot(Nspec);
    for (int j = 0; j < Nspec; ++j)
        nnames_ydot[j] = "ydot_" + chemnames[ckd.index(trSp[j])];

    std::cout << "Writing Ydot to " << nfile_ydot << std::endl;
    ClonePlotfile(amrData,ydot,nfile_ydot,nnames_ydot);

    for (int lev = 0; lev < Nlev; ++lev)
    {
        std::cout << "Building DiffFluxDiv at lev=" << lev << " ... " << std::flush;

        FArrayBox tmp, massfrac;

        for (MFIter mfi(dfd[lev]); mfi.isValid(); ++mfi)
        {
            massfrac.resize(mole[lev][mfi].box(),mole[lev][mfi].nComp()+1);

            massfrac.copy(mole[lev][mfi],0,0,mole[lev][mfi].nComp());
            //
            // Stuff temperature into last component.
            //
            massfrac.copy(temp[lev][mfi],0,mole[lev][mfi].nComp(),1);

            FArrayBox& y = massfrac;
            FArrayBox& r = dfd[lev][mfi];

            BL_ASSERT(r.nComp() == y.nComp());

            y.shift(0,-1);
            r.copy(y);
            y.shift(0,1);
            r.minus(y);
            r.mult(b[lev][mfi]);

            tmp.resize(r.box(),r.nComp());

            tmp.copy(y);
            y.shift(0,1);
            tmp.minus(y);
            y.shift(0,-1);
            tmp.mult(a[lev][mfi]);

            r.minus(tmp);

            y.shift(1,-1);
            tmp.copy(y);
            y.shift(1,1);
            tmp.minus(y);
            tmp.mult(d[lev][mfi]);

            r.plus(tmp);

            tmp.copy(y);
            y.shift(1,1);
            tmp.minus(y);
            y.shift(1,-1);
            tmp.mult(c[lev][mfi]);

            r.minus(tmp);
        }

        std::cout << "done" << std::endl;
    }

    std::string nfile_dfd(GetFileRoot(infile)+"_dfd");
    Array<std::string> nnames_dfd(Nspec+1);
    for (int j = 0; j < Nspec; ++j)
        nnames_dfd[j] = "dfd_" + chemnames[ckd.index(trSp[j])];
    nnames_dfd[Nspec] = "dfd_temp";

    std::cout << "Writing DiffFluxDiv to " << nfile_dfd << std::endl;
    ClonePlotfile(amrData,dfd,nfile_dfd,nnames_dfd);

    a.clear(); b.clear(); c.clear(); d.clear();

//    WriteTecPlotFile(amrData,Nspec,chemnames,u,mole,dfd,ydot,temp,Q,dens);

    //
    // Set some globals needed by March().
    //
    prob_lo      = amrData.ProbLo();
    prob_hi      = amrData.ProbHi();
    ref_ratio    = amrData.RefRatio();
    prob_domain  = amrData.ProbDomain();
    finest_level = amrData.FinestLevel();

    boxArray.resize(finest_level+1);
    dx.resize(finest_level+1);
    dxinv.resize(finest_level+1);

    for (int l = 0; l <= finest_level; l++)
    {
        boxArray[l] = amrData.boxArray(l);
        dx[l]       = amrData.DxLevel()[l];
        dxinv[l]    = amrData.DxLevel()[l];

        for (int i = 0; i < BL_SPACEDIM; i++)
            dxinv[l][i] = 1.0/dx[l][i];
    }

    std::vector<X> x(1);

    for (int i = 0; i < x.size(); i++)
    {
        x[i][0] = .0003 * (i+1);
        x[i][1] = .0001;
    }

    March(amrData,x,u,mole,dfd,ydot,temp,Q,dens);

    BoxLib::Finalize();
}
