/*
** (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 <algorithm>
#include <string>
#include <iostream>
#include <iomanip>
#include <fstream>
#include <stdiostream.h>

#include <Utility.H>
#include <REAL.H>
#include <IntVect.H>
#include <Box.H>
#include <Grid.H>
#include <globals.H>
#include <Array.H>
#include <GRID_F.H>
#include <ParmParse.H>
#include <iomanip.h>
#include <VisMF.H>
#include <BCTypes.H>

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

#define N_STATE 5
#define NDIM 3

#define ARLIM(x) x[0],x[1],x[2]

int Grid::slope_order = 4;
int Grid::init_iter   = 2;
Real Grid::fixed_dt   = -1.0;

// ************************************************************************
// ** constructor ** 
// ************************************************************************

Grid::Grid(std::string probin_file_read)
{
   probin_file = probin_file_read;

   numscal = N_STATE - NDIM;

   parseInputs();

   lo = prob_domain.smallEnd().getVect();
   hi = prob_domain.bigEnd().getVect();

}

// ************************************************************************
// ** parseInputs ** 
// ************************************************************************

void
Grid::parseInputs()
{
    ParmParse pp("grid");

    Array<int> n_cell(NDIM);
    pp.getarr("n_cell",n_cell,0,NDIM);
    IntVect pd_lo(0,0,0);
    IntVect pd_hi(n_cell[0]-1,n_cell[1]-1,n_cell[2]-1);
    prob_domain = Box(pd_lo,pd_hi);

    Array<Real> real_lo(NDIM);
    pp.getarr("prob_lo",real_lo,0,NDIM);

    Array<Real> real_hi(NDIM);
    pp.getarr("prob_hi",real_hi,0,NDIM);

    prob_lo[0] = real_lo[0];
    prob_lo[1] = real_lo[1];
    prob_lo[2] = real_lo[2];

    prob_hi[0] = real_hi[0];
    prob_hi[1] = real_hi[1];
    prob_hi[2] = real_hi[2];

    prob_size[0] = real_hi[0] - real_lo[0];
    prob_size[1] = real_hi[1] - real_lo[1];
    prob_size[2] = real_hi[2] - real_lo[2];

    dx[0] = prob_size[0]/ (Real) n_cell[0] ;
    dx[1] = prob_size[1]/ (Real) n_cell[1] ;
    dx[2] = prob_size[2]/ (Real) n_cell[2] ;

    _hx = dx[0]; 
    _hy = dx[1]; 
    _hz = dx[2]; 

    is_conserv.resize(numscal);
    int in_inputs = pp.countval("is_conserv");
    if (in_inputs != numscal) {
       std::cout << "WRONG NUMBER OF INPUTS FOR grid.is_conserv " << std::endl;
       exit(0);
    }
     
    pp.getarr("is_conserv",is_conserv,0,numscal);

    for (int i = 0; i < numscal; i++) {
       if (is_conserv[i] != 0 && is_conserv[i] != 1) {
         std::cout << "each value of is_conserv must be 0 or 1 " << std::endl;
         exit(0);
       }
    }

    visc_coef = 0.0;
    pp.query("visc_coef",visc_coef);

    diff_coef.resize(numscal);
    in_inputs = pp.countval("diff_coef");
    if (in_inputs != numscal) {
       std::cout << "WRONG NUMBER OF INPUTS FOR grid.diff_coef " << std::endl;
       exit(0);
    }
     
    pp.getarr("diff_coef",diff_coef,0,numscal);

    if (diff_coef[0] > 0.0) {
       std::cout << "DENSITY SHOULD NOT DIFFUSE " << std::endl;
       exit(0);
    }

    pp.query("slope_order",slope_order);

    if (slope_order != 0 && slope_order != 2 && slope_order != 4) {
      std::cout << "slope_order must be 0 or 2 or 4" << std::endl;
      exit(0);
    }

    pp.query("init_iter",init_iter);
    pp.query("restart",restart_file);

    pp.query("fixed_dt",fixed_dt);
}

// ************************************************************************
// ** init ** 
// ************************************************************************

// decide whether to initialize from beginning or from restart
void
Grid::init(int& nstep, Real& time, Real& dt)
{
    int probin_file_length = probin_file.length();

    Array<int> probin_file_name(probin_file_length);

    for (int i = 0; i < probin_file_length; i++)
        probin_file_name[i] = probin_file[i];

    FORT_PROBINIT(probin_file_name.dataPtr(),
                  &probin_file_length);

    if (restart_file != "") {

      restart(restart_file,nstep,time,dt);

    } else {

      initialInit(nstep,time,dt);

    }
}

// ************************************************************************
// ** initialInit ** 
// ************************************************************************

void 
Grid::initialInit(int& nstep, Real& time, Real& dt) 
{

   nstep = 0;
   time = 0.0;
   dt = 0.0;

   Box sz(BoxLib::grow(prob_domain,1));

   // alloc space for state variables
   state.resize(sz,N_STATE);
   staten.resize(sz,N_STATE);
   slopex.resize(sz,N_STATE);
   slopey.resize(sz,N_STATE);
   slopez.resize(sz,N_STATE);
   rhonph.resize(sz,1);

   visc.resize(sz,NDIM);
   visc.setVal(0.0);

   diff.resize(sz,numscal);
   diff.setVal(0.0);

   Box p_size(BoxLib::surroundingNodes(prob_domain)); 
   pressure.resize(p_size,1);
   pressure.setVal(0.0);

   gradp.resize(sz,NDIM);
   gradp.setVal(0.0);

   force.resize(sz,NDIM);
   force.setVal(0.0);

   scalforce.resize(sz,numscal);
   scalforce.setVal(0.0);

   edgex.resize(sz,N_STATE);
   edgey.resize(sz,N_STATE);
   edgez.resize(sz,N_STATE);

   buildLinks();

   proj = new hg_projector(prob_domain,dx);

   macproj = new mac_projector(prob_domain,dx);

   diffuse_op = new diffuser(prob_domain,dx);

   FORT_INITDATA(u,v,w,state.dataPtr(Density),ARLIM(lo),ARLIM(hi),dx,&time,
                 &numscal);

   FORT_SETVELBC(u,v,w,ARLIM(lo),ARLIM(hi),
                 &bcx_lo,&bcx_hi,&bcy_lo,&bcy_hi,&bcz_lo,&bcz_hi,
                 &visc_coef,dx,&time);

   for (int n = 0; n < numscal; n++) {
     FORT_SETSCALBC(state.dataPtr(Density+n),ARLIM(lo),ARLIM(hi),
                    &bcx_lo,&bcx_hi,&bcy_lo,&bcy_hi,&bcz_lo,&bcz_hi,
                    &n,dx,&time);
   }

   FArrayBox * rhotemp = new FArrayBox(sz,1);
   rhotemp->setVal(1.);

   FArrayBox divusrc;
   divusrc.resize(p_size,1);
   FORT_MKDIVUNOD(divusrc.dataPtr(),u,v,w,rho,&time,dx,ARLIM(lo),ARLIM(hi));
   proj->project(&state,&pressure,rhotemp,&divusrc,time,dt);

   delete rhotemp;

   pressure.setVal(0.0);

   FORT_SETVELBC(u,v,w,ARLIM(lo),ARLIM(hi),
                 &bcx_lo,&bcx_hi,&bcy_lo,&bcy_hi,&bcz_lo,&bcz_hi,
                 &visc_coef,dx,&time);
}

// ************************************************************************
// ** restart ** 
// ************************************************************************

void
Grid::restart(std::string& filename, int& nstep, Real& time, Real& dt)
{

   std::ifstream is(filename.c_str(),std::ios::in);

   is >> nstep;
   is >> time;
   is >> dt;

   is >> prob_domain;
   is >> bcx_lo;
   is >> bcx_hi;
   is >> bcy_lo;
   is >> bcy_hi;
   is >> bcz_lo;
   is >> bcz_hi;

   Box sz(BoxLib::grow(prob_domain,1));

   // alloc space for state variables
   state.resize(sz,N_STATE);
   staten.resize(sz,N_STATE);
   slopex.resize(sz,N_STATE);
   slopey.resize(sz,N_STATE);
   slopez.resize(sz,N_STATE);
   rhonph.resize(sz,1);
   gradp.resize(sz,NDIM);

   force.resize(sz,NDIM);
   force.setVal(0.0);

   scalforce.resize(sz,numscal);
   scalforce.setVal(0.0);

   visc.resize(sz,NDIM);
   visc.setVal(0.0);

   diff.resize(sz,numscal);
   diff.setVal(0.0);

   edgex.resize(sz,N_STATE);
   edgey.resize(sz,N_STATE);
   edgez.resize(sz,N_STATE);

   Box p_size(BoxLib::surroundingNodes(prob_domain)); 
   pressure.resize(p_size,1);

   state.readFrom(is);

   pressure.readFrom(is);

   buildLinks();

   FORT_SETVELBC(u,v,w,ARLIM(lo),ARLIM(hi),
                 &bcx_lo,&bcx_hi,&bcy_lo,&bcy_hi,&bcz_lo,&bcz_hi,
                 &visc_coef,dx,&time);

   for (int n = 0; n < numscal; n++) {
     FORT_SETSCALBC(state.dataPtr(Density+n),ARLIM(lo),ARLIM(hi),
                    &bcx_lo,&bcx_hi,&bcy_lo,&bcy_hi,&bcz_lo,&bcz_hi,
                    &n,dx,&time);
   }

   proj = new hg_projector(prob_domain,dx);

   macproj = new mac_projector(prob_domain,dx);

   diffuse_op = new diffuser(prob_domain,dx);
}

// ************************************************************************
// ** buildLinks ** 
// ************************************************************************

void
Grid::buildLinks()
{

      // build links to components of old-time state variables
   u     = state.dataPtr(0);
   v     = state.dataPtr(1);
   w     = state.dataPtr(2);
   rho   = state.dataPtr(NDIM);

   // build links to components of new-time state variables
   un    = staten.dataPtr(0);
   vn    = staten.dataPtr(1);
   wn    = staten.dataPtr(2);
   rhon  = staten.dataPtr(NDIM);

   rhohalf = rhonph.dataPtr();

   // build links to pressure gradient
   px    = gradp.dataPtr();
   py    = gradp.dataPtr(1);
   pz    = gradp.dataPtr(2);

   _hx = dx[0];
   _hy = dx[1];
   _hz = dx[2];

   uadv = new Real[(prob_domain.length()[0]+1)* prob_domain.length()[1] *
                    prob_domain.length()[2]   ];
   vadv = new Real[ prob_domain.length()[0]   *(prob_domain.length()[1]+1) *
                    prob_domain.length()[2]   ];
   wadv = new Real[ prob_domain.length()[0]   * prob_domain.length()[1] *
                   (prob_domain.length()[2]+1)];

   utrans = new Real[(prob_domain.length()[0]+1)* prob_domain.length()[1] *
                      prob_domain.length()[2]   ];
   vtrans = new Real[ prob_domain.length()[0]   *(prob_domain.length()[1]+1) *
                      prob_domain.length()[2]   ];
   wtrans = new Real[ prob_domain.length()[0]   * prob_domain.length()[1] *
                     (prob_domain.length()[2]+1)];
}

// ************************************************************************
// ** destructor ** 
// ************************************************************************

Grid::~Grid() {
   delete proj;
   delete macproj;
   delete diffuse_op;

   delete uadv;
   delete vadv;
   delete wadv;
   delete utrans;
   delete vtrans;
   delete wtrans;
}

// ************************************************************************
// ** writeCheckPoint ** 
// ************************************************************************

void 
Grid::writeCheckPoint(int nstep, Real time, Real dt,
		      std::string& check_file_root) 
{
  // construct file name
   std::string chkfile = check_file_root;
   char buf[sizeof(int)+1];
   sprintf (buf,"%04d",nstep);
   chkfile += buf;

   const char* chkfile_sep_str ="/";

   if (chkfile.length() == 0) {
     BoxLib::Error("invalid check_file_root");
   }

   if (strchr(chkfile.c_str(), *chkfile_sep_str) == 0) {
     // No slashes in the path, so no need to create directory.
   } else {
     // Make copy of the directory pathname so we can write to it.
     char* path = new char[chkfile.length() + 1];
     (void) strcpy(path, chkfile.c_str());

     char* slash = strrchr(path, *chkfile_sep_str);
     int fileNameLength = strlen(slash);
     int entireNameLength = strlen(path);
     int dirNameLength = entireNameLength - fileNameLength;

     char* chkdir= new char[dirNameLength+1];
     strncpy(chkdir,path,dirNameLength);
     chkdir[dirNameLength] = path[entireNameLength];
     
     // create the directory
     if (!BoxLib::UtilCreateDirectory(chkdir,0755)){
       BoxLib::CreateDirectoryFailed(chkdir);
     }
     delete [] chkdir;
     delete [] path;
   }

      // open stream
   std::ofstream os(chkfile.c_str(),std::ios::out);
   os.precision(15);

   os << nstep << '\n';
   os << time << '\n';
   os << dt << '\n';

   os << prob_domain << '\n';
   os << bcx_lo << '\n';
   os << bcx_hi << '\n';
   os << bcy_lo << '\n';
   os << bcy_hi << '\n';
   os << bcz_lo << '\n';
   os << bcz_hi << '\n';
   state.writeOn(os);
   pressure.writeOn(os);
}

// ************************************************************************
// ** initialIter ** 
// ************************************************************************

void 
Grid::initialIter(Real time, Real dt) 
{
   for (int iter = 0; iter < init_iter; iter++) {
     timeStep(time,dt);
   }

}

// ************************************************************************
// ** advance ** 
// ************************************************************************

void 
Grid::advance(Real time, Real dt) 
{
   timeStep(time,dt);

   state.copy(staten,0,0,N_STATE);
   staten.setVal(0.);
}

// ************************************************************************
// ** timeStep ** 
// ************************************************************************

void 
Grid::timeStep(Real time, Real dt) 
{
   FORT_SETVELBC(u,v,w,ARLIM(lo),ARLIM(hi),
                 &bcx_lo,&bcx_hi,&bcy_lo,&bcy_hi,&bcz_lo,&bcz_hi,
                 &visc_coef,dx,&time);
   getSlopes(0,NDIM);

   getGradP();
   getForce(time);
   getViscTerm();

   makeAdvVels(time,dt);

   for (int n = 0; n < numscal; n++) {
     FORT_SETSCALBC(state.dataPtr(Density+n),ARLIM(lo),ARLIM(hi),
                    &bcx_lo,&bcx_hi,&bcy_lo,&bcy_hi,&bcz_lo,&bcz_hi,
                    &n,dx,&time);
   }
   getSlopes(Density,N_STATE-NDIM);

   updateScalars(time,dt);

   getRhoHalf(time,dt);

   updateVelocity(time,dt);
}

// ************************************************************************
// ** getSlopes ** 
// ************************************************************************

void 
Grid::getSlopes(int first_comp, int num_comp)
{

   int numx = prob_domain.length()[0]+2;
   Real *dfromx = new Real[4*numx];

   FORT_SLOPEX(state.dataPtr(first_comp),slopex.dataPtr(first_comp),
               dfromx,ARLIM(lo),ARLIM(hi),&num_comp,&bcx_lo,&bcx_hi,
               &slope_order);

   delete dfromx;


   int numy = prob_domain.length()[1]+2;
   Real *dfromy = new Real[4*numy];

   FORT_SLOPEY(state.dataPtr(first_comp),slopey.dataPtr(first_comp),
               dfromy,ARLIM(lo),ARLIM(hi),&num_comp,&bcy_lo,&bcy_hi,
               &slope_order);

   delete dfromy;

   int numz = prob_domain.length()[2]+2;
   Real *dfromz = new Real[4*numz];

   FORT_SLOPEZ(state.dataPtr(first_comp),slopez.dataPtr(first_comp),
               dfromz,ARLIM(lo),ARLIM(hi),&num_comp,&bcz_lo,&bcz_hi,
               &slope_order);

   delete dfromz;

}

// ************************************************************************
// ** getViscTerm ** 
// ************************************************************************

void 
Grid::getViscTerm()
{
   if (visc_coef > 0.) {
    for (int n = 0; n < NDIM; n++) {
     FORT_LAPLAC(state.dataPtr(n),visc.dataPtr(n),
                 ARLIM(lo),ARLIM(hi),dx,&visc_coef,
                 &bcx_lo,&bcx_hi,&bcy_lo,&bcy_hi,&bcz_lo,&bcz_hi);
    }
   }
}

// ************************************************************************
// ** getDiffTerm ** 
// ************************************************************************

void 
Grid::getDiffTerm()
{
   for (int n = 0; n < numscal; n++) {
     Real dc = diff_coef[n];
     if (dc > 0.) {
       FORT_LAPLAC(state.dataPtr(Density+n),diff.dataPtr(n),
                   ARLIM(lo),ARLIM(hi),dx,&dc,
                   &bcx_lo,&bcx_hi,&bcy_lo,&bcy_hi,&bcz_lo,&bcz_hi);
     }
   }
}

// ************************************************************************
// ** getGradP ** 
// ************************************************************************

void 
Grid::getGradP()
{
   proj->gradient(gradp,pressure);
}

// ************************************************************************
// ** getForce **
// ************************************************************************

void
Grid::getForce(Real current_time)
{
  FORT_MKFORCE(force.dataPtr(),u,v,w,rho,
               &gravity,&visc_coef,&current_time,dx,ARLIM(lo),ARLIM(hi));
}

// ************************************************************************
// ** getScalForce **
// ************************************************************************

void
Grid::getScalForce(Real current_time)
{
  FORT_MKSCALFORCE(scalforce.dataPtr(),state.dataPtr(Density),
                   &current_time,dx,ARLIM(lo),ARLIM(hi),&numscal);
}

// ************************************************************************
// ** makeAdvVel ** 
// ************************************************************************

void 
Grid::makeAdvVels(Real time, Real dt)
{
   Real halftime = time + .5*dt;

// Reset the bcs if inflow profile (for the case of time-dep inflow)
   if (bcx_lo == INLET || bcx_hi == INLET || 
       bcy_lo == INLET || bcy_hi == INLET ||
       bcz_lo == INLET || bcz_hi == INLET ) {
     FORT_SETVELBC(u,v,w,ARLIM(lo),ARLIM(hi),
                   &bcx_lo,&bcx_hi,&bcy_lo,&bcy_hi,&bcz_lo,&bcz_hi,
                   &visc_coef,dx,&halftime);
   }

   int numx = prob_domain.length()[0]+2;
   int numy = prob_domain.length()[1]+2;
   int numz = prob_domain.length()[2]+2;

   Real *stlft = new Real[2*numx];
   Real *strgt = stlft + numx;
   Real *stbot = new Real[2*numy];
   Real *sttop = stbot + numy;
   Real *stdwn = new Real[2*numz];
   Real *stup  = stdwn + numz;

   Real * ux = slopex.dataPtr(Xvel);
   Real * vx = slopex.dataPtr(Yvel);
   Real * wx = slopex.dataPtr(Zvel);
   Real * uy = slopey.dataPtr(Xvel);
   Real * vy = slopey.dataPtr(Yvel);
   Real * wy = slopey.dataPtr(Zvel);
   Real * uz = slopez.dataPtr(Xvel);
   Real * vz = slopez.dataPtr(Yvel);
   Real * wz = slopez.dataPtr(Zvel);

// This extrapolates u to the x-edges and v to the y-edges only.
   FORT_MKADVVEL(u,ux,uy,uz,v,vx,vy,vz,w,wx,wy,wz,rho,force.dataPtr(),px,py,pz,
                 visc.dataPtr(),dx,dt,ARLIM(lo),ARLIM(hi),
                 stlft,strgt,sttop,stbot,stdwn,stup,
                 uadv,vadv,wadv,utrans,vtrans,wtrans,
                 &bcx_lo,&bcx_hi,&bcy_lo,&bcy_hi,&bcz_lo,&bcz_hi,&visc_coef);

   delete stlft;
   delete stbot;
   delete stdwn;

// Create the cell-based source S for the divergence constraint Div(U) = S
   FArrayBox divusrc;
   divusrc.resize(prob_domain,1);
   FORT_MKDIVUCC(divusrc.dataPtr(),u,v,w,rho,&halftime,dx,ARLIM(lo),ARLIM(hi));

// This enforces the div(U) = S condition on the edge-based half-time
//  normal velocities
   macproj->project(uadv,vadv,wadv,rho,divusrc);
}

// ************************************************************************
// ** updateScalars ** 
// ************************************************************************

void 
Grid::updateScalars(Real time, Real dt)
{
   Real newtime = time+dt;

   int numx = prob_domain.length()[0]+1;
   int numy = prob_domain.length()[1]+1;
   int numz = prob_domain.length()[2]+1;

   Real *stleft = new Real[2*numx];
   Real *strght = stleft + numx;
   Real *stbot  = new Real[2*numy];
   Real *sttop  = stbot + numy;
   Real *stdwn  = new Real[2*numz];
   Real *stup   = stdwn + numz;

   if (bcx_lo == INLET || bcx_hi == INLET || 
       bcy_lo == INLET || bcy_hi == INLET ||
       bcz_lo == INLET || bcz_hi == INLET ) {
     Real halftime = time + .5*dt;
     for (int n = 0; n < numscal; n++) {
       FORT_SETSCALBC(state.dataPtr(Density+n),ARLIM(lo),ARLIM(hi),
                      &bcx_lo,&bcx_hi,&bcy_lo,&bcy_hi,&bcz_lo,&bcz_hi,
                      &n,dx,&halftime);
     }
   }

   getDiffTerm();
   getScalForce(time);

   FORT_MKSCAFLUX(state.dataPtr(Density) , 
                  edgex.dataPtr(Density) ,  edgey.dataPtr(Density),
                  edgez.dataPtr(Density) ,  
                  slopex.dataPtr(Density), slopey.dataPtr(Density),
                  slopez.dataPtr(Density), 
                  uadv, vadv, wadv, utrans, vtrans, wtrans, u,
                  diff.dataPtr(), scalforce.dataPtr(),
   	          stleft,strght,stbot,sttop,stdwn,stup,
   	          ARLIM(lo),ARLIM(hi),dx,&dt,
                  &bcx_lo,&bcx_hi,&bcy_lo,&bcy_hi,&bcz_lo,&bcz_hi,&numscal);

   delete stleft;
   delete stbot;
   delete stdwn;

   Real halftime = time + 0.5*dt;
   getScalForce(halftime);

   FORT_SCALUPD(state.dataPtr(Density), staten.dataPtr(Density),
                edgex.dataPtr(Density), edgey.dataPtr(Density),
                edgez.dataPtr(Density), 
                uadv, vadv, wadv, diff.dataPtr(), scalforce.dataPtr(),
	        ARLIM(lo),ARLIM(hi),dx,&dt,is_conserv.dataPtr(),
                &numscal); 

   for (int n = 0; n < numscal; n++) {

     FORT_SETSCALBC(staten.dataPtr(Density+n),ARLIM(lo),ARLIM(hi),
                    &bcx_lo,&bcx_hi,&bcy_lo,&bcy_hi,&bcz_lo,&bcz_hi,
                    &n,dx,&newtime);

     if (diff_coef[n] > 0.0) {
       FArrayBox temp(BoxLib::grow(prob_domain,1),1);
       temp.setVal(0.);
       Real mu = .5*diff_coef[n]*dt;
       diffuse_op->solveScal(&staten,&temp,mu,Density+n);
       staten.copy(temp,0,Density+n,1);

       FORT_SETSCALBC(staten.dataPtr(Density+n),ARLIM(lo),ARLIM(hi),
                      &bcx_lo,&bcx_hi,&bcy_lo,&bcy_hi,&bcz_lo,&bcz_hi,
                      &n,dx,&newtime);
     }
   }
}

// ************************************************************************
// ** getRhoHalf ** 
// ************************************************************************

void 
Grid::getRhoHalf(Real time, Real dt)
{

   int src_comp = Density;
   int dest_comp = 0;
   rhonph.copy(state ,src_comp,dest_comp,1);
   rhonph.plus(staten,src_comp,dest_comp,1);
   rhonph.mult(0.5);

   if (bcx_lo == INLET || bcx_hi == INLET ||
       bcy_lo == INLET || bcy_hi == INLET ||
       bcz_lo == INLET || bcz_hi == INLET) {
     Real halftime = time + 0.5*dt;
     int n = 0;
     FORT_SETSCALBC(rhohalf,ARLIM(lo),ARLIM(hi),
                    &bcx_lo,&bcx_hi,&bcy_lo,&bcy_hi,&bcz_lo,&bcz_hi,
                    &n,dx,&halftime);
   }
}

// ************************************************************************
// ** updateVelocity ** 
// ************************************************************************

void 
Grid::updateVelocity(Real time, Real dt)
{
   int numx = prob_domain.length()[0]+1;
   int numy = prob_domain.length()[1]+1;
   int numz = prob_domain.length()[2]+1;

   Real *stlft = new Real[6*numx];
   Real *strgt = stlft + 3*numx;
   Real *stbot = new Real[6*numy];
   Real *sttop = stbot + 3*numy;
   Real *stdwn = new Real[6*numz];
   Real *stup  = stdwn + 3*numz;

   FORT_MKVELFLUX(state.dataPtr() , 
                  edgex.dataPtr() ,  edgey.dataPtr(), edgez.dataPtr() ,
                  slopex.dataPtr(), slopey.dataPtr(), slopez.dataPtr(),
                  uadv, vadv, wadv, utrans, vtrans, wtrans,
                  rho, px, py, pz, visc.dataPtr(), 
	          stlft,strgt,stbot,sttop,stdwn,stup,
	          ARLIM(lo),ARLIM(hi),dx,&dt,
                  force.dataPtr(),&visc_coef,
                  &bcx_lo,&bcx_hi,&bcy_lo,&bcy_hi,&bcz_lo,&bcz_hi);

   delete stlft;
   delete stbot;
   delete stdwn;

   Real halftime = time + 0.5*dt;
   getForce(halftime);

   FORT_VELUPD(u,v,w,un,vn,wn,visc.dataPtr(),gradp.dataPtr(),rhohalf,
  	       uadv,vadv,wadv,
               edgex.dataPtr(),edgey.dataPtr(),edgez.dataPtr(),
               force.dataPtr(),&dt,ARLIM(lo),ARLIM(hi),dx);

   Real newtime = time+dt;
   FORT_SETVELBC(un,vn,wn,ARLIM(lo),ARLIM(hi),
                 &bcx_lo,&bcx_hi,&bcy_lo,&bcy_hi,&bcz_lo,&bcz_hi,
                 &visc_coef,dx,&newtime);

   if (visc_coef > 0.0) {
     Real mu = .5*visc_coef*dt;
     for (int n = 0; n < NDIM; n++) {
       FArrayBox temp(BoxLib::grow(prob_domain,1),1);
       temp.setVal(0.);
       diffuse_op->solveVel(&staten,&temp,rhohalf,mu,n);
       staten.copy(temp,0,n,1);
     } 
   } 

   FORT_SETVELBC(un,vn,wn,ARLIM(lo),ARLIM(hi),
                 &bcx_lo,&bcx_hi,&bcy_lo,&bcy_hi,&bcz_lo,&bcz_hi,
                 &visc_coef,dx,&newtime);

// Create the node-based source S_t for the divergence constraint Div(U_t) = S_t
   FArrayBox divusrc, divutmp;
   Box p_size(BoxLib::surroundingNodes(prob_domain));
   divusrc.resize(p_size,1);
   divutmp.resize(p_size,1);
   FORT_MKDIVUNOD(divutmp.dataPtr(),u ,v ,w ,rho ,&time,dx,ARLIM(lo),ARLIM(hi));
   FORT_MKDIVUNOD(divusrc.dataPtr(),un,vn,wn,rhon,&newtime,dx,ARLIM(lo),ARLIM(hi));
   divusrc.minus(divutmp);
   divusrc.mult(1./dt);

// staten.minus(state,0,0,NDIM);
   staten.divide(dt,0,NDIM);

   proj->project(&staten,&pressure,&rhonph,&divusrc,time,dt);

   staten.mult(dt,0,NDIM);
// staten.plus(state,0,0,NDIM);

   FORT_SETVELBC(un,vn,wn,ARLIM(lo),ARLIM(hi),
                 &bcx_lo,&bcx_hi,&bcy_lo,&bcy_hi,&bcz_lo,&bcz_hi,
                 &visc_coef,dx,&newtime);
}

// ************************************************************************
// ** estimateDt ** 
// ************************************************************************

Real 
Grid::estimateDt(Real & dt, Real cfl) 
{
   getGradP();

  Real cfl_temp = cfl;
  if (fixed_dt > 0) cfl_temp = 0.9;

   FORT_CMPDT(u,v,w,rho,px,py,pz,force.dataPtr(),dx,&dt,&cfl_temp,
	      ARLIM(lo),ARLIM(hi)); 

  if (fixed_dt > 0) {
    if (fixed_dt > dt) {
      std::cout << "WARNING: fixed_dt may be too big" << std::endl;
      std::cout << "fixed_dt = " << fixed_dt 
           << " with CFL =0.9 computed_dt = " << dt << std::endl;
    }
    dt = fixed_dt; 
  }
  std::cout << "Computing dt to be " << dt << std::endl;

   return dt;
}

// ************************************************************************
// ** DeriveFunc ** 
// ************************************************************************

// ------------  Define assoc between var name and derive func
typedef void (*DeriveFunc) (Real* state, Real* derval, 
                            const int& der_lo_1, const int& der_lo_2,
                            const int& der_lo_3, 
                            const int& der_hi_1, const int& der_hi_2,
                            const int& der_hi_3, 
                            const int& lo_1, const int& lo_2,
                            const int& lo_3, 
                            const int& hi_1, const int& hi_2,
                            const int& hi_3, const Real* dx);

// ************************************************************************
// ** DeriveRec ** 
// ************************************************************************

class DeriveRec
{
public:
    DeriveRec(std::string name, DeriveFunc f)
        : varname(name), func(f) {};
    ~DeriveRec() {};
    std::string       varname;
    DeriveFunc    func;
};

DeriveRec vor_rec("vort", FORT_DERVORT);

static DeriveRec deriveList[] = {
    vor_rec,
};

int numDerive = sizeof(deriveList)/sizeof(DeriveRec);

// ************************************************************************
// ** deriveData ** 
// ************************************************************************

void Grid::deriveData(FArrayBox& derval, const std::string& name, Real time)
{
     // is it a state variable
   if (name == "x_velocity") {
       derval.copy(state,prob_domain,0,prob_domain,0,1);
       return;
   } else if (name == "y_velocity") {
       derval.copy(state,prob_domain,1,prob_domain,0,1);
       return;
   } else if (name == "z_velocity") {
       derval.copy(state,prob_domain,2,prob_domain,0,1);
       return;
   } else if (name == "density") {
       derval.copy(state,prob_domain,3,prob_domain,0,1);
       return;
   } else if (name == "tracer") {
       derval.copy(state,prob_domain,4,prob_domain,0,1);
       return;
//  IF YOU WISH TO ADD A NEW SCALAR VARIABLE, UNCOMMENT THESE LINES
// } else if (name == "new_scalar") {
//     derval.copy(state,prob_domain,5,prob_domain,0,1);
//     return;
   } else if (name == "pressure") {
       FORT_DERAVGP(pressure.dataPtr(), derval.dataPtr(), ARLIM(lo), ARLIM(hi));
       return;
   } else if (name == "px") {
       derval.copy(gradp,prob_domain,0,prob_domain,0,1);
       return;
   } else if (name == "py") {
       derval.copy(gradp,prob_domain,1,prob_domain,0,1);
       return;
   } else if (name == "pz") {
       derval.copy(gradp,prob_domain,2,prob_domain,0,1);
       return;
   }

   const int* derlo = derval.loVect();
   const int* derhi = derval.hiVect();

   // if we got here, check list of known derived quantities
   for (int i = 0; i < numDerive; i++) {
       if (name == deriveList[i].varname) {

           FORT_SETCELLVELBC(u,v,w,ARLIM(lo),ARLIM(hi),
                             &bcx_lo,&bcx_hi,&bcy_lo,&bcy_hi,&bcz_lo,&bcz_hi,
                             &visc_coef,dx,&time);

           deriveList[i].func(state.dataPtr(),derval.dataPtr(),
                              ARLIM(derlo),ARLIM(derhi),
                              ARLIM(lo),ARLIM(hi),dx);

           FORT_SETVELBC(u,v,w,ARLIM(lo),ARLIM(hi),
                         &bcx_lo,&bcx_hi,&bcy_lo,&bcy_hi,&bcz_lo,&bcz_hi,
                         &visc_coef,dx,&time);
           return;
       }
   }

      // if we got here, dont know how to derive this
    std::cerr << "Dont know how to derive " << name << std::endl;
    abort();
}

// ************************************************************************
// ** writePlotFile ** 
// ************************************************************************

// -------------------------------------------------------------
void
Grid::writePlotFile( int nstep, Real time, std::string& plot_file_root)
{
  std::string pltfile = plot_file_root;
  char buf[sizeof(int)+1];
  sprintf(buf,"%04d",nstep);
  pltfile += buf;

  // create directory
  if (!(BoxLib::UtilCreateDirectory(pltfile,0755))) {
   BoxLib::CreateDirectoryFailed(pltfile);
  }

  std::string HeaderFileName = pltfile + "/Header";

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

  std::ofstream HeaderFile;

  HeaderFile.rdbuf()->pubsetbuf(io_buffer.dataPtr(), io_buffer.size());

  int old_prec;

  HeaderFile.open(HeaderFileName.c_str(), std::ios::out|std::ios::trunc);

  if (!HeaderFile.good())
    BoxLib::FileOpenFailed(HeaderFileName);

  old_prec = HeaderFile.precision(15);

  static const std::string RunstatString("write_pltfile");

  int finest_level = 0;
  for (int k = 0; k <= finest_level; k++)
    {
      writePlotFile(pltfile, HeaderFile,nstep,time);
    }

  HeaderFile.precision(old_prec);

  if (!HeaderFile.good())
    BoxLib::Error("Amr::writePlotFile() failed");
}

std::string
Grid::thePlotFileType()
{
    // Increment this whenever the writePlotFile() format changes.
    //
    static const std::string the_plot_file_type("HyperCLaw-V1.1");
    return the_plot_file_type;
}

void
Grid::writePlotFile(std::string& dir, std::ostream& os, int nstep,
                    Real time)
{
    int i, n;
    int finest_level = 0;

    // plotfile type
    os << thePlotFileType() << '\n';

    // number of components
    int numPlotComp = N_STATE + 1 + NDIM + numDerive;
    os << numPlotComp << '\n';

    // names
    os << "x_velocity"   << '\n';
    os << "y_velocity"   << '\n';
    os << "z_velocity"   << '\n';
    os << "density" << '\n';
    os << "tracer"  << '\n';
//  IF YOU WISH TO ADD A NEW SCALAR VARIABLE, UNCOMMENT THIS LINE 
//  os << "new_scalar"  << '\n';

    os << "pressure"<< '\n';
    os << "px"      << '\n';
    os << "py"      << '\n';
    os << "pz"      << '\n';
    os << "vort"    << '\n';

    static const std::string MultiFabBaseName("/MultiFab");

    // dimensions
    os << NDIM << '\n';
    // current time
    os << time << '\n';
    // finest_level
    os << finest_level << '\n';
    // problem_domain
    for (i = 0; i < NDIM; i++) os << prob_lo[i] << ' ';
    os << '\n';
    for (i = 0; i < NDIM; i++) os << prob_hi[i] << ' ';
    os << '\n';
    os << '\n';
    // domain
    os << prob_domain << ' ';
    os << '\n';

    os << nstep << '\n';
    for (int k = 0; k < NDIM; k++) {
       os << dx[k] << ' ';
    }
    os << '\n';

    int irz_temp = 0;
    os << irz_temp << '\n';

    // write bndry data
    os << "0\n";


    int thisLevel = 0;
    int numGridsOnLevel = 1;
    int IAMRSteps = 0;
    char buf[64];
    sprintf(buf, "Level_%d",thisLevel);
    std::string Level = buf;
    std::string FullPath = dir;
    if (!FullPath.empty() && FullPath[FullPath.length()-1] != '/')
      FullPath += '/';
    FullPath += Level;
    
    if (!BoxLib::UtilCreateDirectory(FullPath, 0755))
      BoxLib::CreateDirectoryFailed(FullPath);
    
    // now write state data
    os << thisLevel << ' ' << numGridsOnLevel << ' ' << time << '\n';
    os << IAMRSteps << '\n';
    for (n = 0; n < NDIM; n++) {
      os << prob_lo[n] << ' ' << prob_hi[n] << '\n';
    }

    std::string PathNameInHeader = Level;
    PathNameInHeader += MultiFabBaseName;
    os << PathNameInHeader << '\n';
    FullPath += MultiFabBaseName;

    // copy all of the data into the multifab.
    BoxArray bs(1);
    bs.set(0,prob_domain);
    FArrayBox temp(prob_domain,1);
    FArrayBox dat(prob_domain,numPlotComp);
    MultiFab mf(bs,numPlotComp,0,Fab_allocate);
    
    int counter = 0;

    //  Writing the state data: u, v, rho, scalars
    dat.copy(state,0,0,N_STATE);
    counter += N_STATE;

//  Writing the pressure
    FORT_DERAVGP(pressure.dataPtr(), temp.dataPtr(), ARLIM(lo), ARLIM(hi));
    dat.copy(temp,0,counter,1);
    counter++;
  
//  Writing the pressure gradient
    proj->gradient(gradp,pressure);
    dat.copy(gradp,0,counter,NDIM);
    counter += NDIM;

//  Writing the vorticity
    temp.resize(prob_domain,numDerive);
    for(i = 0; i < numDerive; i++) {
      FORT_SETCELLVELBC(u,v,w,ARLIM(lo),ARLIM(hi),
                        &bcx_lo,&bcx_hi,&bcy_lo,&bcy_hi,&bcz_lo,&bcz_hi,
                        &visc_coef,dx,&time);
      deriveList[i].func(state.dataPtr(),temp.dataPtr(),
			 ARLIM(lo),ARLIM(hi),ARLIM(lo),ARLIM(hi),dx);
      FORT_SETVELBC(u,v,w,ARLIM(lo),ARLIM(hi),
                    &bcx_lo,&bcx_hi,&bcy_lo,&bcy_hi,&bcz_lo,&bcz_hi,
                    &visc_coef,dx,&time);
      dat.copy(temp,0,counter,1);
      counter++;
    }
    mf[0].copy(dat);
    VisMF::Write(mf,FullPath,VisMF::OneFilePerCPU);

}

