/*
  eam.cpp
  by Wei Cai  caiwei@mit.edu
  Last Modified : Mon Jan  1 21:26:00 2007

  FUNCTION  : MD++ implementation of EAM/MEAM potential
*/

#include "eam.h"

#define _CUBICSPLINE

inline double spline(double spline_coeff[NGRID][4],int ind, double qq)
{
  double a, b, c, d, qq2, qq3, f;
  a = spline_coeff[ind][0];
  b = spline_coeff[ind][1];
  c = spline_coeff[ind][2];
  d = spline_coeff[ind][3];
  qq2=qq*qq; qq3=qq2*qq;
  f=a+b*qq+c*qq2+d*qq3;
  return f;
}  

inline double spline1(double spline_coeff[NGRID][4],int ind, double qq)
{
  double a, b, c, d, qq2, qq3, fp;
  a = spline_coeff[ind][0];
  b = spline_coeff[ind][1];
  c = spline_coeff[ind][2];
  d = spline_coeff[ind][3];
  qq2=qq*qq; qq3=qq2*qq;
  fp=b+2*c*qq+3*d*qq2;
  return fp;
}


void compute_spline_coeff(int ngrid, double func[],double deriv[],double dr,double spline_coeff[NGRID][4])
{
  int ind;
  double a, b, c, d, f1, p1, A1, A2, dr2, dr3;
  
  dr2=dr*dr; dr3=dr2*dr;

  for(ind=0;ind<ngrid;ind++)
  {
    a = func[ind];
    b = deriv[ind];
    f1 = func[ind+1];
    p1 = deriv[ind+1];
    A1 = f1-a-b*dr;
    A2 = (p1-b)*dr;
    d = (A2-2*A1)/dr3;
    c = (3*A1-A2)/dr2;
    spline_coeff[ind][0] = a;
    spline_coeff[ind][1] = b;
    spline_coeff[ind][2] = c;
    spline_coeff[ind][3] = d;
  }
}

int EAMFrame::readeam()
{
    FILE *fp;
    char string[500];
    int i;
    
    LFile::SubHomeDir(potfile,potfile);
    
    fp=fopen(potfile,"r");
    if(fp==NULL)
    {
        FATAL("EAMFrame::readeam file ("<<potfile<<") not found!");
        return -1;
    }
    fgets(title3,100,fp);
    INFO_Printf("%s\n",title3);

    fgets(string,500,fp);
    sscanf(string,"%d %lf %lf",&ntyp,&rmass,&rlatt);
    fgets(string,500,fp);
    sscanf(string,"%le %le %le %le",&drar,&drhoar,&actual,&rmin);

    /*debug */
    actual*=0.995;
    
    actual2 = actual*actual;

    INFO_Printf("ntyp=%d rmass=%f rlatt=%f\n",ntyp,rmass,rlatt);
    INFO_Printf("dr=%e drho=%e rcut=%e rmin=%e\n",drar,drhoar,actual,rmin);

    _RLIST = 1.1 * actual;
    _SKIN = _RLIST - actual;

    for(i=0;i<eamgrid;i++)
    {
      fgets(string,500,fp);
      sscanf(string,"%le %le %le %le",&(rho[0][i]),&(rhop[0][i]),&(rho[1][i]),&(rhop[1][i]));
    }
    for(i=0;i<eamgrid;i++)
    {
      fgets(string,500,fp);
      sscanf(string,"%le %le %le %le",&(phi[0][i]),&(phip[0][i]),&(phi[1][i]),&(phip[1][i]));
    }
    if(eamfiletype==2) 
    {
        for(i=0;i<eamgrid;i++) 
        {
          fgets(string,500,fp);
          sscanf(string,"%le %le",&(phix[i]),&(phipx[i]));
        }
    }
    for(i=0;i<eamgrid;i++)
    {
      fgets(string,500,fp);
      sscanf(string,"%le %le %le %le",&(frho[0][i]),&(frhop[0][i]),&(frho[1][i]),&(frhop[1][i]));
    }  
    INFO("Finished reading EAM potential");
    
    /*   Initialize linear grid points reduntantly on each node to save
     *   transfer time.
     */
    for(i=0;i<eamgrid;i++)
    {
        rval[i] = i*drar;
        rhoval[i] = i*drhoar;
    }
    pottype = 1; /* set potential to EAM */

#ifdef _CUBICSPLINE
    /* compute spline coefficients */
    compute_spline_coeff(eamgrid,rho[0],rhop[0],drar,rho_spline[0]);
    compute_spline_coeff(eamgrid,phi[0],phip[0],drar,phi_spline[0]);
    compute_spline_coeff(eamgrid,frho[0],frhop[0],drhoar,frho_spline[0]);

    if(eamfiletype==2)
    {
      compute_spline_coeff(eamgrid,rho[1],rhop[1],drar,rho_spline[1]);
      compute_spline_coeff(eamgrid,phi[1],phip[1],drar,phi_spline[1]);
      compute_spline_coeff(eamgrid,phix,  phipx,  drar,phix_spline);
      compute_spline_coeff(eamgrid,frho[1],frhop[1],drhoar,frho_spline[1]);
    }
#endif
    return 0;
}

void EAMFrame::rhoeam()
{
    int i, j, jpt, idx, jdx, ind;
    Vector3 sij,rij;
    double r2ij, rmagg, qq, qr;
    double rhoi, rhoj;

    /*INFO("rhoeam");*/
    petrip = 0;
    rhocon = 1e-10;
    rhomax = eamgrid*drhoar;

    for(i=0;i<_NP;i++)
    {
        atpe3b[i]=0;
        rhotot[i]=0;
        nbst[i]=0;
    }
    for(i=0;i<_NP;i++)
    {
        _F[i].clear(); _EPOT_IND[i]=0;
    }
    _EPOT=0;
    _VIRIAL.clear();
    
    /*	start calculation
     *  calculate pair contribution to total energy and forces
     */

    /* do on i-particles */
    for(i=0;i<_NP;i++)
    {
        /* modify here for binary systems (0/1) */
        idx = species[i]; /* type of atom (i) */
        /* do on j-particles */
        for(j=0;j<nn[i];j++)
        {
            /* modify here for binary systems (0/1) */
            jpt=nindex[i][j];
            jdx = species[jpt]; /* type of atom (j) */
            if(i>=jpt) continue;
            sij=_SR[jpt]-_SR[i];
            sij.subint();
            rij=_H*sij;
            r2ij=rij.norm2();
            if(r2ij>actual2) continue;
            rmagg=sqrt(r2ij)-rmin;
            
            nbst[i]++;
            ind = (int)(rmagg/drar);

            if(ind>=eamgrid)
            {
                ind=eamgrid-1;
                INFO_Printf("ind = %d r=%f in RHOEAM\n",ind, rmagg+rmin);
            }
            else if(ind<0)
            {
                ind=0;
                INFO_Printf("ind = %d r=%f in RHOEAM\n",ind, rmagg+rmin);
            }
            qq=rmagg-rval[ind];

            if((idx==jdx))
            {
#ifndef _CUBICSPLINE
            rhoi=rho[jdx][ind]+ qq*rhop[jdx][ind];
#else
            //rhoi = interp(rho[jdx],rhop[jdx],drar,ind,qq);
            rhoi = spline(rho_spline[jdx],ind,qq);
#endif
            rhotot[i]+=rhoi;
            rhotot[jpt]+=rhoi;
            }
            else
            {
#ifndef _CUBICSPLINE
              rhoi=rho[jdx][ind]+ qq*rhop[jdx][ind];
              rhoj=rho[idx][ind]+ qq*rhop[idx][ind];
#else
              //rhoi = interp(rho[jdx],rhop[jdx],drar,ind,qq);
              //rhoj = interp(rho[idx],rhop[idx],drar,ind,qq);
              rhoi = spline(rho_spline[jdx],ind,qq);
              rhoj = spline(rho_spline[idx],ind,qq);
#endif
              rhotot[i]+=rhoi;
              rhotot[jpt]+=rhoj;
            }
        }
    }

    /* debug */
#if 0
    for(i=0;i<_NP;i++)
    {
        INFO_Printf("atom[%d] rho=%e\n",i,rhotot[i]);
    }
#endif
    
    for(i=0;i<_NP;i++)
    {
        /* modify here for binary systems (0/1) */
        idx = species[i]; /* type of atom (i) */
        if(rhotot[i]<rhocon)
        {
            rhotot[i]=rhocon;
        }
        if(rhotot[i]>rhomax)
        {
            rhotot[i]=rhomax;
        }
        ind = (int)(rhotot[i]/drhoar);
        if(ind>=eamgrid-1) ind=eamgrid-1;
        qr = rhotot[i] - rhoval[ind];

#ifndef _CUBICSPLINE
        embf[i] = frho[idx][ind] + qr*frhop[idx][ind];
        embfp[i] = frhop[idx][ind] +
          qr*(frhop[idx][ind+1]-frhop[idx][ind])/drhoar;
#else
        //embf[i] = interp(frho[idx],frhop[idx],drhoar,ind,qr);
        //embfp[i] = interp1(frho[idx],frhop[idx],drhoar,ind,qr);
        embf[i] = spline(frho_spline[idx],ind,qr);
        embfp[i] = spline1(frho_spline[idx],ind,qr);
#endif
        atpe3b[i] = embf[i];
        _EPOT_IND[i]+=atpe3b[i];
        _EPOT+=atpe3b[i];
    }    
}

void EAMFrame::kraeam()
{
    int i, j, jpt, idx, jdx, ind;
    Vector3 sij,rij,fij;
    double r2ij, rmagg, pp, qq, fcp, fpp, fp, denspi, denspj;
    
    /*	start calculation
     *  calculate pair contribution to total energy and forces
     */
    
    /* do on i-particles */
    for(i=0;i<_NP;i++)
    {
        /* modify here for binary systems (0/1) */
        idx = species[i]; /* type of atom (i) */
        /* do on j-particles */
        for(j=0;j<nn[i];j++)
        {
            /* modify here for binary systems (0/1) */
            jpt=nindex[i][j];
            jdx = species[jpt]; /* type of atom (j) */
            if(i>=jpt) continue;
            sij=_SR[jpt]-_SR[i];
            sij.subint();
            rij=_H*sij;
            r2ij=rij.norm2();
            if(r2ij>actual2) continue;
            rmagg=sqrt(r2ij)-rmin;

            ind = int(rmagg/drar);
            
            if(ind>=eamgrid)
            {
                ind=eamgrid-1;
                INFO_Printf("ind = %d in RHOEAM\n",ind);
            }
            else if(ind<0)
            {
                ind=0;
                INFO_Printf("ind = %d in RHOEAM\n",ind);
            }
            qq=rmagg-rval[ind];

            if(idx==jdx)
            {
#ifndef _CUBICSPLINE
              pp = phi[idx][ind] + qq*phip[idx][ind];
              fpp = phip[idx][ind] +
                qq*(phip[idx][ind+1]-phip[idx][ind])/drar;
#else
              //pp = interp(phi[idx],phip[idx],drar,ind,qq);
              //fpp = interp1(phi[idx],phip[idx],drar,ind,qq);
              pp = spline(phi_spline[idx],ind,qq);
              fpp = spline1(phi_spline[idx],ind,qq);
#endif
            }
            else
            {
#ifndef _CUBICSPLINE
              pp = phix[ind] + qq*phipx[ind];
              fpp = phipx[ind] + qq*(phipx[ind+1]-phipx[ind])/drar;
#else
              //pp = interp(phix,phipx,drar,ind,qq);
              //fpp = interp1(phix,phipx,drar,ind,qq);
              pp = spline(phix_spline,ind,qq);
              fpp = spline1(phix_spline,ind,qq);
#endif
            }
#ifndef _CUBICSPLINE
            denspi = rhop[idx][ind] +
                qq*(rhop[idx][ind+1]-rhop[idx][ind])/drar ;
            denspj = rhop[jdx][ind] +
                qq*(rhop[jdx][ind+1]-rhop[idx][ind])/drar ;
#else
            //denspi = interp1(rho[idx],rhop[idx],drar,ind,qq);
            //denspj = interp1(rho[jdx],rhop[jdx],drar,ind,qq);
            denspi = spline1(rho_spline[idx],ind,qq);
            denspj = spline1(rho_spline[jdx],ind,qq);
#endif
            fcp = denspj * embfp[i] + denspi * embfp[jpt];
            fp = (fpp + fcp) / rmagg;
            _EPOT_IND[i]+= 0.5*pp;
            _EPOT_IND[jpt]+= 0.5*pp;
            _EPOT+=pp;
            
            fij=rij*fp;
            _F[i]+=fij;
            _F[jpt]-=fij;
#if 0
            if(!(fixed[i]||fixed[jpt]))
                _VIRIAL.addnvv(-fp,rij,rij);
            else if(!(fixed[i]&&fixed[jpt]))
                _VIRIAL.addnvv(-0.5*fp,rij,rij);
#else
                _VIRIAL.addnvv(-fp,rij,rij);
#endif
        }
    }
}

int EAMFrame::readMEAM()
{
    pottype = 2; /* set potential to MEAM */
    return -1;
};

void EAMFrame::rhoMEAM()
{

}

void EAMFrame::kraMEAM()
{

}

void EAMFrame::Alloc()
{
    MDPARALLELFrame::Alloc();
    int size;
    size=_NP*allocmultiple;
    
    /* Shared Memory */
    Realloc(atpe3b,double,size);
    Realloc(rhotot,double,size);
    Realloc(embf,double,size);
    Realloc(embfp,double,size);
    Realloc(nbst,int,size);
}    

void EAMFrame::potential()
{
    refreshneighborlist(); // 2007.Jul.10 Chris Weinberger & Wei Cai
    if(pottype==1)
    {
        rhoeam();
        kraeam();
    }
    else if(pottype==2)
    {
        rhoMEAM();
        kraMEAM();
    }
    else
    {
        WARNING("Unknown pottype ("<<pottype<<")"
                " no calculation performed");
    }
}

void EAMFrame::initvars()
{
    MDPARALLELFrame::initvars();
   
    strcpy(potfile,"eamdata");
}
void EAMFrame::initparser()
{
    MDPARALLELFrame::initparser();
    bindvar("eamgrid",&eamgrid,INT);
    bindvar("pottype",&pottype,INT);
    bindvar("eamfiletype",&eamfiletype,INT);
}

int EAMFrame::exec(char *name)
{
    if(MDPARALLELFrame::exec(name)==0) return 0;
    bindcommand(name,"readeam",readeam());
    bindcommand(name,"readMEAM",readMEAM());
    return -1;
}
   
#ifdef _TEST

/* Main Program Begins */
class EAMFrame sim;

/* The main program is defined here */
#include "main.cpp"

#endif//_TEST





























































/* old code */
#if 0

inline double interp(double func[],double deriv[],double dr,int ind,double qq)
{
    double f, a, b, c, d, f1, p1, A1, A2, dr2, dr3, qq2, qq3;
//    f = func[ind] + qq*deriv[ind];
    dr2=dr*dr; dr3=dr2*dr;
    qq2=qq*qq; qq3=qq2*qq;
    a = func[ind];
    b = deriv[ind];
    f1 = func[ind+1];
    p1 = deriv[ind+1];
    A1 = f1-a-b*dr;
    A2 = (p1-b)*dr;
    d = (A2-2*A1)/dr3;
    c = (3*A1-A2)/dr2;
    f=a+b*qq+c*qq2+d*qq3;
    return f;
}
    
    
inline double interp1(double func[],double deriv[],double dr,int ind,double qq)
{
    double fp, a, b, c, d, f1, p1, A1, A2, dr2, dr3, qq2, qq3;
//    fp = deriv[ind] + qq1/dr*(deriv[ind+1]-deriv[ind]);
    dr2=dr*dr; dr3=dr2*dr;
    qq2=qq*qq; qq3=qq2*qq;
    a = func[ind];
    b = deriv[ind];
    f1 = func[ind+1];
    p1 = deriv[ind+1];
    A1 = f1-a-b*dr;
    A2 = (p1-b)*dr;
    d = (A2-2*A1)/dr3;
    c = (3*A1-A2)/dr2;
    fp=b+2*c*qq+3*d*qq2;
    return fp;
}
#endif
