/*
  md.cpp
  by Wei Cai  caiwei@stanford.edu
  Last Modified : Mon Oct  2 21:58:58 2006

  FUNCTION  :  Easy-to-use MD simulation Framefork

  Featuring :  1. Scripting input
               2. X-window display
               3. automatic simulation log
               4. convenient configuration and output file handle
               5. perfect lattice and dislocation config creator
               6. conjugate-gradient relaxation
               7. NVT MD simulation (Nose-Hoover thermostat)
               8. Cell-Verlist combined neighbor list

  This is a single CPU code.
  Previous shared-memory option removed.
  May implment MPI parallelization in the future.

  TO DO:
  1. implement new potential function (call SW, FS, EAM, etc)
  2. implement new neighbor list function (call Verlist)
  3. implement new integrator (call Gear6 or VVerlet)

  4. re-organize example script files
*/


#include "md.h"

/********************************************************************/
/* Begining of class MDFrame definition */
/********************************************************************/


/********************************************************************/
/* Initialize script file parser */
/********************************************************************/
void MDFrame::initparser()
{
    char s[100];

    /* Parser */
    bindvar("myname",myname,STRING);
    bindvar("command",command,STRING);
    bindvar("input",input,DOUBLE);
    
    /* Conjugate gradient relaxation */
    bindvar("conj_itmax",&conj_itmax,INT);
    bindvar("conj_fevalmax",&conj_fevalmax,INT);
    bindvar("conj_fixbox",&conj_fixbox,INT);
    bindvar("conj_fixdir",&conj_fixdir,INT);
    bindvar("conj_fixshape",&conj_fixshape,INT);
    bindvar("conj_fixatoms",&conj_fixatoms,INT);
    bindvar("conj_ftol",&conj_ftol,DOUBLE);
    bindvar("conj_dfpred",&conj_dfpred,DOUBLE);
    bindvar("stress",&(_EXTSTRESS[0][0]),DOUBLE);  /* in MPa */
    bindvar("stressmul",&(_EXTSTRESSMUL),DOUBLE);
    bindvar("extforce",extforce,DOUBLE);           /* in eV/A */
    bindvar("forcemul",&forcemul,DOUBLE);
    bindvar("pressureadd",&(_EXTPRESSADD),DOUBLE); /* in MPa */
    bindvar("vacuumratio",&_VACUUMRATIO,DOUBLE);
    bindvar("hprecond",&_HPRECOND,DOUBLE);
    bindvar("xprecond",&_XPRECOND,DOUBLE);
    bindvar("yprecond",&_YPRECOND,DOUBLE);
    bindvar("zprecond",&_ZPRECOND,DOUBLE);
    bindvar("conj_fixboxvec",&(conj_fixboxvec[0][0]),DOUBLE); /* in MPa */

    /* Conjugate gradient relax with constrains */
    bindvar("constrainS",&constrainS,DOUBLE);
    bindvar("constrainatoms",constrainatoms,INT);

    /* Molecular Dynamics */
    bindvar("integrator_type",&integrator_type,INT);
    bindvar("SAVEMEMORY",&_SAVEMEMORY,INT);    
    bindvar("scalevelocity",&scalevelocity,INT);
    bindvar("nspecies",&nspecies,INT);
    bindvar("totalsteps",&totalsteps,INT);
    bindvar("equilsteps",&equilsteps,INT);
    bindvar("atommass",_ATOMMASS,DOUBLE);
    bindvar("T_OBJ",&_TDES,DOUBLE);
    bindvar("DOUBLE_T",&_DOUBLET,INT);
    bindvar("allocmultiple",&allocmultiple,INT);
    bindvar("usenosehoover",&usenosehoover,INT);
    bindvar("fixedatomenergypartition",&fixedatomenergypartition,INT);
    bindvar("vt2",&vt2,DOUBLE);
    bindvar("atomTcpl",&_ATOMTCPL,DOUBLE);
    bindvar("boxTcpl",&_BOXTCPL,DOUBLE);
    bindvar("timestep",&_TIMESTEP,DOUBLE);
    bindvar("RLIST",&_RLIST,DOUBLE);
    bindvar("NIC",&_NIC,INT);
    bindvar("NNM",&_NNM,INT);
    bindvar("shearrate",&(_SHEARRATE[0][0]),DOUBLE);
    bindvar("applyshear",&applyshear,INT);

    /* Monte Carlo */
    bindvar("mcatom",&MC_atom,INT);
    bindvar("lambda0",&_LAMBDA0,DOUBLE);
    bindvar("lambda1",&_LAMBDA1,DOUBLE);
    bindvar("Ecoh",&_ECOH,DOUBLE);
    bindvar("switchfreq",&_SWITCHFREQ,INT);
    bindvar("switchoffatoms",MC_switchoffatoms,INT);
    bindvar("switchfunc",&_SWITCHFUNC,INT);
    bindvar("refpotential",&_REFPOTENTIAL,INT);
    bindvar("ecspring",&_ECSPRING,DOUBLE);
    bindvar("randseed",&_RANDSEED,INT);

    /* Nudged-Elastic-Band method */
    bindvar("nebspec",nebspec,INT);
    bindvar("annealspec",annealspec,DOUBLE);
    bindvar("chainlength",&_CHAINLENGTH,INT);

    /* Configuration manipulation */
    bindvar("crystalstructure",crystalstructure,STRING);
    bindvar("latticeconst",latticeconst,DOUBLE);
    bindvar("latticesize",latticesize,DOUBLE);
    bindvar("torquespec",torquespec,DOUBLE);
    
    bindvar("latticestructure",crystalstructure,STRING);/* for backward compatibility */
    bindvar("makecnspec",latticesize,DOUBLE);/* for backward compatibility */
    bindvar("mkdipole",input,DOUBLE);        /* for backward compatibility */
    bindvar("mkdislspec",input,DOUBLE);      /* for backward compatibility */
    bindvar("mkcylinderspec",input,DOUBLE);  /* for backward compatibility */

    
    /* File input and output */
    bindvar("savecn",&savecn,INT);
    bindvar("saveprop",&saveprop,INT);
    bindvar("savecnfreq",&savecnfreq,INT);
    bindvar("savepropfreq",&savepropfreq,INT);
    bindvar("printfreq",&printfreq,INT);
    bindvar("filecounter",&filecounter,INT);
    bindvar("incnfile",incnfile,STRING);
    bindvar("finalcnfile",finalcnfile,STRING);
    bindvar("outpropfile",outpropfile,STRING);
    bindvar("intercnfile",intercnfile,STRING);
    bindvar("potfile",potfile,STRING);
    bindvar("writevelocity",&writevelocity,INT);
    bindvar("writeall",&writeall,INT);
    
    /* Interface to Fortran codes */
    bindvar("fortranpath",fortranpath,STRING);
    bindvar("fortranexe",fortranexe,STRING);
    
    /* Interface to atomeye and other viewers */
    bindvar("atomeyepath",atomeyepath,STRING);
    bindvar("atomeyerepeat",atomeyerepeat,INT);
    bindvar("atomeyeexe",atomeyeexe,STRING);

    /* Interface to MDCASK */
    bindvar("MDCASKpot",MDCASKpot,STRING);
    
    /* Visualization */
    bindvar("win_width",&win_width,INT);
    bindvar("win_height",&win_height,INT);
    bindvar("plotfreq",&plotfreq,INT);
    bindvar("atomradius",atomradius,DOUBLE);
    bindvar("bondradius",&bondradius,DOUBLE);
    bindvar("bondlength",&bondlength,DOUBLE);
    bindvar("bondcolor",bondcolor,STRING);
    bindvar("highlightcolor",highlightcolor,STRING);
    bindvar("fixatomcolor",fixatomcolor,STRING);
    bindvar("backgroundcolor",backgroundcolor,STRING);
    bindvar("rotateangles",rotateangles,DOUBLE);

    bindvar("plot_limits",plot_limits,DOUBLE);
    bindvar("plot_atom_info",&plot_atom_info,INT);
    bindvar("plot_map_pbc",&plot_map_pbc,INT);
    bindvar("plot_color_windows",plot_color_windows,DOUBLE);
    bindvar("plot_color_bar",plot_color_bar,DOUBLE);
    bindvar("energycolorbar",plot_color_bar,DOUBLE); /* for backward compatibility */
    
    bindvar("plot_color_axis",&plot_color_axis,INT);
    bindvar("NCS",&NCS,INT);
    bindvar("autowritegiffreq",&autowritegiffreq,INT);

    for(int i=0;i<MAXSPECIES;i++)
    {
        sprintf(s,"element%d",i);
        bindvar(s,element[i],STRING);
        sprintf(s,"atomcolor%d",i);
        bindvar(s,atomcolor[i],STRING);
    }
    bindvar("atomcolor",atomcolor[0],STRING);
    for(int i=0;i<MAXCOLORS;i++)
    {
        sprintf(s,"color%02d",i);
        bindvar(s,colornames[i],STRING);
    }
}    

int MDFrame::exec(char *name)
{
    if(Organizer::exec(name)==0) return 0;

    /* Parser */
    bindcommand(name,"runcommand",runcommand());

    /* Conjugate gradient relaxation */
    bindcommand_sync(name,"relax",relax());
    
    /* Molecular Dynamics simulation */
    bindcommand_sync(name,"step",step());
    bindcommand_sync(name,"run",run());    
    bindcommand_sync(name,"eval",{refreshneighborlist();eval();});
    bindcommand_sync(name,"multieval",multieval());
    bindcommand_sync(name,"refreshnnlist",refreshneighborlist());
    bindcommand_sync(name,"calphonondisp",calphonondisp());
    bindcommand_sync(name,"calHessian",calHessian());
    bindcommand_sync(name,"readHessian",readHessian());
    bindcommand_sync(name,"calmisfit",calmisfit());
    bindcommand_sync(name,"testpotential",testpotential());
    bindcommand_sync(name,"calcentralsymmetry",calcentralsymmetry());
    bindcommand_sync(name,"findcore",findcore());
    bindcommand_sync(name,"initvelocity",initvelocity());
    bindcommand_sync(name,"multiplyvelocity",multiplyvelocity());
    bindcommand_sync(name,"randomposition",randomposition());

    /* Monte Carlo simulation */
    bindcommand_sync(name,"runMC",runMC());
    bindcommand_sync(name,"runMCSWITCH",runMCSWITCH());
    bindcommand_sync(name,"runTAMC",runTAMC());
    bindcommand_sync(name,"srand",srand((unsigned int)_RANDSEED));
    bindcommand_sync(name,"srand48",srand48((unsigned int)_RANDSEED));
    bindcommand_sync(name,"srandbytime",srand((unsigned int)time(NULL)));
    bindcommand_sync(name,"srand48bytime",srand48((unsigned int)time(NULL)));
    
    /* Nudged-Elastic-Band relaxation */
    bindcommand_sync(name,"nebrelax",nebrelax());
    bindcommand_sync(name,"constrainedrelax",constrainedrelax());
    bindcommand_sync(name,"annealpath",annealpath());
    bindcommand_sync(name,"statedistance",statedistance());
    bindcommand_sync(name,"cutpath",cutpath());
    bindcommand_sync(name,"initRchain",initRchain());
    bindcommand(name,"allocchain",AllocChain());

    /* Configuration manipulation */
    bindcommand(name,"makecrystal",makecrystal());
    bindcommand(name,"makecn",makecrystal()); /* for backward compatibility */
    bindcommand(name,"makecut",makecut());
    bindcommand(name,"makedipole",makedipole());
    bindcommand(name,"makedislcylinder",makedislcylinder());
    bindcommand(name,"makecylinder",makecylinder());
    bindcommand(name,"makedislocation",makedislocation());
    bindcommand(name,"makedisloop",makedisloop());
    bindcommand(name,"makegrainboundary",makegrainboundary());
    bindcommand(name,"makegb",makegrainboundary()); /* for backward compatibility */
    bindcommand(name,"makewave",makewave());
    bindcommand(name,"maketorquehandle",maketorquehandle());    

    bindcommand(name,"scaleH",scaleH());
    bindcommand(name,"setH",setH());
    bindcommand(name,"saveH",saveH());
    bindcommand(name,"restoreH",restoreH());
    bindcommand(name,"changeH_keepR",shiftbox());
    bindcommand(name,"changeH_keepS",redefinepbc());
    bindcommand(name,"shiftbox",shiftbox());      /* for backward compatibility */
    bindcommand(name,"redefinepbc",redefinepbc());/* for backward compatibility */
    bindcommand(name,"applystrain",applystrain());    
    bindcommand(name,"extendbox",extendbox());

    bindcommand(name,"cutslice",cutslice());
    bindcommand(name,"splicecn",splicecn());
    bindcommand(name,"cutpastecn",cutpastecn());

    bindcommand(name,"setconfig1",setconfig1());
    bindcommand(name,"setconfig2",setconfig2());
    bindcommand(name,"switchconfig",switchconfig());
    bindcommand(name,"replacefreeatom",replacefreeatom());
    bindcommand(name,"relabelatom",relabelatom());
        
    bindcommand(name,"moveatom",moveatom());
    bindcommand(name,"addtorque",addtorque());
    bindcommand(name,"printatoms_in_sphere",printatoms_in_sphere());
    bindcommand(name,"pbcshiftatom",pbcshiftatom());
    
    bindcommand(name,"fixatoms_by_ID",fixatoms_by_ID());
    bindcommand(name,"fixatoms_by_position",fixatoms_by_position());
    bindcommand(name,"fixatoms_by_pos_topol",fixatoms_by_pos_topol());
    bindcommand(name,"freeallatoms",freeallatoms());
    bindcommand(name,"reversefixedatoms",reversefixedatoms());

    bindcommand(name,"setfixedatomsspecies",setfixedatomsspecies());
    bindcommand(name,"setfixedatomsgroup",setfixedatomsgroup());
    bindcommand(name,"reversespecies",reversespecies());
    bindcommand(name,"movefixedatoms",movefixedatoms());
    bindcommand(name,"removefixedatoms",removefixedatoms());
    bindcommand(name,"markremovefixedatoms",markremovefixedatoms());
    bindcommand(name,"removeellipsoid",removeellipsoid());
    bindcommand(name,"removerectbox",removerectbox());
    bindcommand(name,"removeoverlapatoms",removeoverlapatoms());
        
    /* File input and output */
    bindcommand(name,"writecn",writefinalcnfile());
    bindcommand(name,"readcn",readcn());
    bindcommand(name,"readPOSCAR",readPOSCAR());
    bindcommand(name,"readMDCASK",readMDCASK());
    bindcommand(name,"readMDCASKJAIME",readMDCASKJAIME());
    bindcommand(name,"writeMDCASKXYZ",writeMDCASKXYZ(finalcnfile));
    bindcommand(name,"readRchain",readRchain());
    bindcommand(name,"convertXDATCAR",convertXDATCAR());
    bindcommand(name,"openintercnfile",openintercnfile());
    bindcommand(name,"writeintercn",writeintercnfile());
    bindcommand(name,"setfilecounter",setfilecounter());
    bindcommand(name,"openpropfile",openpropfile());
    bindcommand(name,"writePOSCAR",writePOSCAR());
    bindcommand(name,"writeMDCASK",writeMDCASK());
    bindcommand(name,"writePINYMD",writePINYMD(finalcnfile));
    bindcommand(name,"writeRchain",writeRchain());

    /* Interface to Fortran code */
    bindcommand(name,"fortranrelax",fortranrelax());

    /* Interface with AtomEye and other viewers */
    bindcommand(name,"atomeye",atomeye());
    bindcommand(name,"writeatomeyecfg",writeatomeyecfgfile(finalcnfile));
    bindcommand(name,"convertCNtoCFG",convertCNtoCFG());
    bindcommand(name,"writepovray",writepovray(finalcnfile));
    bindcommand(name,"writeRASMOLXYZ",writeRASMOLXYZ(finalcnfile));
    bindcommand(name,"readRASMOLXYZ",readRASMOLXYZ(incnfile));
    bindcommand(name,"writeatomtv",writeatomtv(finalcnfile));

    /* Print out energies of current configuration */
    bindcommand(name,"writeENERGY",writeENERGY(finalcnfile));
    bindcommand(name,"writeFORCE",writeFORCE(finalcnfile));
    bindcommand(name,"writePOSITION",writePOSITION(finalcnfile));
    bindcommand(name,"GnuPlotHistogram",GnuPlotHistogram());    

    /* Visualization */
    bindcommand(name,"openwin",openwin());
    bindcommand(name,"plot",plot());
    bindcommand(name,"alloccolors",alloccolors());
    bindcommand(name,"alloccolorsX",alloccolorsX());
    bindcommand(name,"testcolor",win->testcolor());
    bindcommand(name,"reversergb",win->reversergb());
    bindcommand(name,"rotate",rotate());
    bindcommand(name,"saverot",saverot());
    bindcommand(name,"wintogglepause",wintogglepause());

    return -1;
}

void MDFrame::runcommand()
{
    char extcomm[400];
    
    LFile::SubHomeDir(command,command);
    INFO("run:  "<<command);
    if(nolog)
        system(command);
    else
    {
        if (ncom==0) system("rm -f B*.log");
        strcpy(extcomm,command);
        strcpy(extcomm+strlen(extcomm)," > B");
        sprintf(extcomm+strlen(extcomm),"%d.log",ncom);
        system(extcomm); 
        sprintf(extcomm,"echo -- '\n'B%d.log: \"%s\"'\n' >> B.log",ncom,command);
        system(extcomm);
        sprintf(extcomm,"cat B%d.log >> B.log",ncom);
        system(extcomm);
        ncom++;
    }
}

void MDFrame::initvars()
{
    initparser();
    strcpy(command,"echo Hello World");
    
    strcpy(incnfile,"con.cn");
    strcpy(intercnfile,"inter.cn");
    strcpy(finalcnfile,"final.cn");
    strcpy(potfile,"../Mo.pot");
    
    strcpy(outpropfile,"prop.out");
#ifdef _CALPHONONSPECTRUM
    strcpy(phonondispfile,"dispersion.m");
#endif
    
    strcpy(bondcolor,"red");
    strcpy(highlightcolor,"purple");

    for(int i=0;i<MAXSPECIES;i++)
    {
        atomradius[i]=0.1;
        strcpy(atomcolor[i],"orange");
        strcpy(element[i],"Mo");
    }
    for(int i=0;i<MAXCOLORS;i++)
    {
        strcpy(colornames[i],"gray50");
    }

    strcpy(MDCASKpot,"SW");

    conj_fixboxvec.clear();

    strcpy(element[1],"Mo");
    strcpy(element[2],"Si");
    strcpy(element[3],"Cu");

    atomeyerepeat[0]=0;

    extforce[0] = 0;
}









/********************************************************************/
/* Coordinate transformation */
/********************************************************************/

void MDFrame::SHtoR()
{ for(int i=0;i<_NP;i++) _R[i]=_H*_SR[i]; }
void MDFrame::RHtoS()
{ Matrix33 hinv=_H.inv();
 for(int i=0;i<_NP;i++) _SR[i]=hinv*_R[i]; }
void MDFrame::RtoR0()
{ int i; SHtoR(); for(i=0;i<_NP;i++) _R0[i]=_R[i]; }
void MDFrame::R0toR()
{ int i; for(i=0;i<_NP;i++) _R[i]=_R0[i]; }

bool MDFrame::Bond(int I, int J) const
{/* Returns true if bond(i,j) is stored in I */
    return (I<=J)? ((I^J)&1)  : !((I^J)&1);
}

/* Memory Allocation */
void MDFrame::Alloc()
{
    int size;
    size=_NP*allocmultiple;
    
    /* Shared Memory */
    Realloc(_SR,Vector3,size);
    Realloc(fixed,int,size);
    memset(fixed,0,sizeof(int)*size);

    if(_SAVEMEMORY>=9) return;
    /* The following is only allocated if _SAVEMEMORY < 9 */

    Realloc(_R,Vector3,size);
    Realloc(_R0,Vector3,size);
    Realloc(image,int,size);
    Realloc(species,int,size);
    Realloc(_TOPOL,double,size);
    Realloc(group,int,size);

    memset(image,0,sizeof(int)*size);
    memset(species,0,sizeof(int)*size);
    memset(_TOPOL,0,sizeof(double)*size);
    memset(group,0,sizeof(int)*size);

    if(_SAVEMEMORY>=1) return;
    /* The following is only allocated if _SAVEMEMORY < 1 */
    
    Realloc(_VR,Vector3,size);
    Realloc(_VSR,Vector3,size);
    Realloc(_F,Vector3,size);
    Realloc(_F0,Vector3,size);
    Realloc(_EPOT_IND,double,size);
    memset(_EPOT_IND,0,sizeof(double)*size);
}


/********************************************************************/                         
/* Conjugate gradient relaxation */
/********************************************************************/                         

void MDFrame::potential_wrapper(int *n,double x[],double *f,double df[])
{/* ensure Sync in the end */
    int i, j, np0, np1;
    Matrix33 h,htran,htraninv,gh,s,hinv,hinvtran,hsigma,sigmahtranh;
    double pressure,pres,omega,vb;
    Vector3 gs;
    MP_BeginMasterOnly();
    __conj_simframe->winplot(__conj_simframe->conj_step);
    __conj_simframe->saveintercn(__conj_simframe->conj_step);
    MP_EndMasterOnly();
    
    __conj_simframe->conj_step++;
    __conj_simframe->_H.set(x);

    /* x = H / HPRECOND */
    __conj_simframe->_H*=__conj_simframe->_HPRECOND;
    
    h=__conj_simframe->_H;
    htran=h.tran();
    htraninv=htran.inv();

    np0=0;
    np1=__conj_simframe->_NP;
    /* assign _SR, _R */
    for(i=np0;i<np1;i++)
    {
        __conj_simframe->_SR[i].set(x+i*3+9);
        __conj_simframe->_SR[i].x*=__conj_simframe->_XPRECOND;
        __conj_simframe->_SR[i].y*=__conj_simframe->_YPRECOND;
        __conj_simframe->_SR[i].z*=__conj_simframe->_ZPRECOND;
        __conj_simframe->_R[i]=h*__conj_simframe->_SR[i];
    }
    MP_Sync();
    
    /* multi process functions */
    __conj_simframe->potential();
    __conj_simframe->calcprop();
    vb=0;
    
    if(__conj_simframe->conj_fixbox) gh.clear();
    else
    {
        /* fixshape relaxation still need to be implemented */
        if(__conj_simframe->conj_fixshape)
        {
            pressure=__conj_simframe->_VIRIAL.trace()/3;
            gh.clear();
        }
        else
        {
            /* PR in finite stress _EXTSTRESS */
            pres=__conj_simframe->_EXTSTRESS.trace()/3;
            pres*=__conj_simframe->_EXTSTRESSMUL;
            pres+=__conj_simframe->_EXTPRESSADD;
            pres/=160e3;/*convert from MPa to eV/A^3 */
            omega=h.det()*(1-__conj_simframe->_VACUUMRATIO);
            s=__conj_simframe->_VIRIAL;
            s[0][0]-=omega*pres;
            s[1][1]-=omega*pres;
            s[2][2]-=omega*pres;
            
            hinv=h.inv(); hinvtran=hinv.tran();
            s=s*hinvtran;
            hsigma=h*__conj_simframe->_SIGMA;
            gh=s-hsigma;
            gh*=-1;
            for(i=0;i<3;i++)
                for(j=0;j<3;j++)
                    if(__conj_simframe->conj_fixboxvec[i][j]==1)
                        gh[i][j]=0;
            sigmahtranh=(__conj_simframe->_SIGMA*htran)*h;
            vb=pres*omega+sigmahtranh.trace()*0.5;
        }
    }
    MP_BeginMasterOnly();

    /* dE/dx = gh * HPRECOND */
    gh*=__conj_simframe->_HPRECOND;
    
    gh.copytoarray(df);
    MP_EndMasterOnly();
    
    for(i=np0;i<np1;i++)
    {
        if((__conj_simframe->fixed[i])
           ||(__conj_simframe->conj_fixatoms))
            gs.clear();
        else
            gs=-htran*__conj_simframe->_F[i];
        gs.x*=__conj_simframe->_XPRECOND;
        gs.y*=__conj_simframe->_YPRECOND;
        gs.z*=__conj_simframe->_ZPRECOND;
        gs.copytoarray(df+i*3+9);
    }
    *f=__conj_simframe->_EPOT + vb;
    MP_Sync();
}

void MDFrame::potential_wrapper_c(int n,double x[],double *f,double df[])
{/* ensure Sync in the end */
    static int ncalls=0;
    potential_wrapper(&n,x,f,df);
    MP_BeginMasterOnly();
    INFO_Printf("%10d : %22.16e\n",ncalls++,*f);
    MP_EndMasterOnly();
}

int MDFrame::relax()
{
    /* This function will be executed by multiprocesses synchronously */
    
    int i,n,_NP0,_NP1;
    double *conj_space,*conj_x,*conj_g,*conj_w, pres, omega;
    Matrix33 prp, hinv, hinvtran;
    
    INFO("Relax");
    _NP0=0;
    _NP1=_NP;
    n=_NP*3+9;
    conj_step=0;
    
    conj_space=NULL;
    Realloc(conj_space,double,n*8);
    
    conj_x=conj_space;    /* Array length n  */
    conj_g=conj_space+n;  /* Array length n  */
    conj_w=conj_space+n*2;/* Array length 6n */

    MP_BeginMasterOnly();
    _H.copytoarray(conj_x);

    /* preconditioning */
    for(i=0;i<9;i++) conj_x[i]/=_HPRECOND;

    MP_EndMasterOnly();

    for(i=_NP0;i<_NP1;i++)
        _SR[i].copytoarray(conj_x+i*3+9);

    for(i=_NP0;i<_NP1;i++)
    {
        conj_x[9+i*3]  /=_XPRECOND;
        conj_x[9+i*3+1]/=_YPRECOND;
        conj_x[9+i*3+2]/=_ZPRECOND;
    }
    __conj_simframe=this;

    /* prepare _SIGMA for PR relaxation */
    pres=_EXTSTRESS.trace()/3;
    prp=_EXTSTRESS;
    prp[0][0]-=pres;
    prp[1][1]-=pres;
    prp[2][2]-=pres;
    prp*=_EXTSTRESSMUL;
    pres*=_EXTSTRESSMUL;
    pres+=_EXTPRESSADD;

    hinv=_H.inv();
    hinvtran=hinv.tran();
    omega=_H.det()*(1-__conj_simframe->_VACUUMRATIO);
    _SIGMA=((hinv*prp)*hinvtran)*omega;
    _SIGMA/=160e3;/* convert MPa to eV/A^3 */
        
    MP_Sync();
    /* All processes enter CGRelax */
    CGRelax(potential_wrapper_c,n,
            conj_ftol,conj_fevalmax,conj_dfpred,
            conj_x,conj_g,&conj_f,conj_w);

    MP_BeginMasterOnly();
    _H.set(conj_x);

    /* preconditioing */
    _H*=_HPRECOND;
    
    MP_EndMasterOnly();

    for(i=_NP0;i<_NP1;i++)
    {
        _SR[i].set(conj_x+i*3+9);
        _SR[i].x*=_XPRECOND;
        _SR[i].y*=_YPRECOND;
        _SR[i].z*=_ZPRECOND;
    }
    INFO_Printf("\tconj_f = %24.18g\n",conj_f);

    MP_Sync();
    MP_BeginMasterOnly();
    MP_Free(conj_space);
    MP_EndMasterOnly();
    return 0;
}

/* Constrained Conjugate gradient relaxation */
/* cannot run with multi-processors */
/* not sure about pre-conditioning */
/* no fixed atoms */
void MDFrame::cstr_potential_wrapper(int *n,double x[],double *f,double df[])
{/* ensure Sync in the end */
    int i, j, np0, np1, ipt;
    Matrix33 h,htran,htraninv,gh,s,hinv,hinvtran,hsigma,sigmahtranh;
    double pressure,pres,omega,vb, gn;
    Vector3 gs, ds0, dF;
#if 0
    Vector3 ds; double cs;
#endif
    
    MP_BeginMasterOnly();
    __conj_simframe->winplot(__conj_simframe->conj_step);
    __conj_simframe->saveintercn(__conj_simframe->conj_step);
    MP_EndMasterOnly();
    
    __conj_simframe->conj_step++;
    __conj_simframe->_H.set(x);

    /* x = H / HPRECOND */
    __conj_simframe->_H*=__conj_simframe->_HPRECOND;
    
    h=__conj_simframe->_H;
    htran=h.tran();
    htraninv=htran.inv();

    np0=0;
    np1=__conj_simframe->_NP;
    /* assign _SR, _R in Shared memory */
    for(i=np0;i<np1;i++)
    {
        __conj_simframe->_SR[i].set(x+i*3+9);
        __conj_simframe->_SR[i].x*=__conj_simframe->_XPRECOND;
        __conj_simframe->_SR[i].y*=__conj_simframe->_YPRECOND;
        __conj_simframe->_SR[i].z*=__conj_simframe->_ZPRECOND;
        __conj_simframe->_R[i]=h*__conj_simframe->_SR[i];
    }
    MP_Sync();

    /* multi process functions */
    __conj_simframe->potential();
    __conj_simframe->calcprop();
    vb=0;
    
    if(__conj_simframe->conj_fixbox) gh.clear();
    else
    {
        /* fixshape relaxation still need to be implemented */
        if(__conj_simframe->conj_fixshape)
        {
            pressure=__conj_simframe->_VIRIAL.trace()/3;
            gh.clear();
        }
        else
        {
            /* PR in finite stress _EXTSTRESS */
            pres=__conj_simframe->_EXTSTRESS.trace()/3;
            pres*=__conj_simframe->_EXTSTRESSMUL;
            pres+=__conj_simframe->_EXTPRESSADD;
            pres/=160e3;/*convert from MPa to eV/A^3 */
            omega=h.det()*(1-__conj_simframe->_VACUUMRATIO);
            s=__conj_simframe->_VIRIAL;
            s[0][0]-=omega*pres;
            s[1][1]-=omega*pres;
            s[2][2]-=omega*pres;
            
            hinv=h.inv(); hinvtran=hinv.tran();
            s=s*hinvtran;
            hsigma=h*__conj_simframe->_SIGMA;
            gh=s-hsigma;
            gh*=-1;
            for(i=0;i<3;i++)
                for(j=0;j<3;j++)
                    if(__conj_simframe->conj_fixboxvec[i][j]==1)
                        gh[i][j]=0;
            sigmahtranh=(__conj_simframe->_SIGMA*htran)*h;
            vb=pres*omega+sigmahtranh.trace()*0.5;
        }
    }
    MP_BeginMasterOnly();

    /* dE/dx = gh * HPRECOND */
    gh*=__conj_simframe->_HPRECOND;
    
    gh.copytoarray(df);
    MP_EndMasterOnly();

    /* apply constraint */
    /* compute out-of-plane gradient */
    gn=0;
    for(i=1;i<=__conj_simframe->constrainatoms[0];i++)
    {
        ipt=__conj_simframe->constrainatoms[i];
        ds0=__conj_simframe->_SR2[ipt]-__conj_simframe->_SR1[ipt];
        gs=-htran*__conj_simframe->_F[ipt];
        gn+=dot(gs,ds0);
    }
    gn/=__conj_simframe->constrain_dist;
    /*INFO_Printf("gn=%e\n",gn);*/
    
    /* orthogonalize force */
    for(i=1;i<=__conj_simframe->constrainatoms[0];i++)
    {
        ipt=__conj_simframe->constrainatoms[i];
        ds0=__conj_simframe->_SR2[ipt]-__conj_simframe->_SR1[ipt];
        ds0*=(-gn);
        dF=-htraninv*ds0;
        __conj_simframe->_F[ipt]+=dF;
    }
    
    /* compute out-of-plane gradient */
    gn=0;
    for(i=1;i<=__conj_simframe->constrainatoms[0];i++)
    {
        ipt=__conj_simframe->constrainatoms[i];
        ds0=__conj_simframe->_SR2[ipt]-__conj_simframe->_SR1[ipt];
        gs=-htran*__conj_simframe->_F[ipt];
        gn+=dot(gs,ds0);
    }
    gn/=__conj_simframe->constrain_dist;
    /*INFO_Printf("gn=%e\n",gn);*/
    
    for(i=np0;i<np1;i++)
    {
        if((__conj_simframe->fixed[i])
           ||(__conj_simframe->conj_fixatoms))
            gs.clear();
        else
            gs=-htran*__conj_simframe->_F[i];
        gs.x*=__conj_simframe->_XPRECOND;
        gs.y*=__conj_simframe->_YPRECOND;
        gs.z*=__conj_simframe->_ZPRECOND;
        gs.copytoarray(df+i*3+9);
    }
    *f=__conj_simframe->_EPOT + vb;
    MP_Sync();
}

void MDFrame::cstr_potential_wrapper_c(int n,double x[],double *f,double df[])
{/* ensure Sync in the end */
    static int ncalls=0;
    cstr_potential_wrapper(&n,x,f,df);
    MP_BeginMasterOnly();
    INFO_Printf("%10d : %22.16e\n",ncalls++,*f);
    MP_EndMasterOnly();
}

int MDFrame::constrainedrelax()
{
    /* This function will be executed by multiprocesses synchronously */
    
    int i,n,ipt;
    double *conj_space,*conj_x,*conj_g,*conj_w, pres, omega;
    Matrix33 prp, hinv, hinvtran;
    Vector3 ds, ds0;
    double s;
    FILE *fp;
    char filename[100];
    
    INFO("Constrained Relax");

    if(constrainatoms[0]<=0)
    {
        ERROR("constrainedatoms not set!");
        return -1;
    }
    if(constrainatoms[0]>1000)
    {
        ERROR("constrainedatoms too big!");
        return -1;
    }
    

    /* rid center of mass shift in configB */
    ds0.clear();
    constrain_dist=0; s=0;
    for(i=1;i<=constrainatoms[0];i++)
    {
        ipt=constrainatoms[i];
        ds0+=_SR2[ipt];
        ds0-=_SR1[ipt];
    }
    ds0/=constrainatoms[0];
    for(i=1;i<=constrainatoms[0];i++)
    {
        ipt=constrainatoms[i];
        _SR2[ipt]-=ds0;
    }
    
    /* compute current constrain value */
    constrain_dist=0; s=0;
    for(i=1;i<=constrainatoms[0];i++)
    {
        ipt=constrainatoms[i];
        ds0=_SR2[ipt]-_SR1[ipt];
        ds =_SR[ipt]-_SR1[ipt];
        s+=dot(ds,ds0);
        constrain_dist+=ds0.norm2();
    }
    s/=constrain_dist;
    INFO_Printf("before displacement s=%f constrain_dist=%e\n",s,constrain_dist);

    /* shift current config to desired S */
    for(i=1;i<=constrainatoms[0];i++)
    {
        ipt=constrainatoms[i];
        ds0=_SR2[ipt]-_SR1[ipt];
        ds0*=(constrainS-s);
        _SR[ipt]+=ds0;
    }

    /* compute new constrain value */
    s=0;
    for(i=1;i<=constrainatoms[0];i++)
    {
        ipt=constrainatoms[i];
        ds0=_SR2[ipt]-_SR1[ipt];
        ds =_SR[ipt]-_SR1[ipt];
        s+=dot(ds,ds0);
    }
    s/=constrain_dist;
    INFO_Printf("after  displacement s=%f constrain_dist=%e\n",s,constrain_dist);
    
    n=_NP*3+9;
    conj_step=0;
    
    conj_space=NULL;
    Realloc(conj_space,double,n*8);
    
    conj_x=conj_space;    /* Array length n  */
    conj_g=conj_space+n;  /* Array length n  */
    conj_w=conj_space+n*2;/* Array length 6n */

    MP_BeginMasterOnly();
    _H.copytoarray(conj_x);

    /* preconditioning */
    for(i=0;i<9;i++) conj_x[i]/=_HPRECOND;

    MP_EndMasterOnly();

    //for(i=_NP;i<_NP;i++)  //!Bug
    for(i=0;i<_NP;i++)
        _SR[i].copytoarray(conj_x+i*3+9);

    for(i=0;i<_NP;i++)
    {
        conj_x[9+i*3]  /=_XPRECOND;
        conj_x[9+i*3+1]/=_YPRECOND;
        conj_x[9+i*3+2]/=_ZPRECOND;
    }
    __conj_simframe=this;

    /* prepare _SIGMA for PR relaxation */
    pres=_EXTSTRESS.trace()/3;
    prp=_EXTSTRESS;
    prp[0][0]-=pres;
    prp[1][1]-=pres;
    prp[2][2]-=pres;
    prp*=_EXTSTRESSMUL;
    pres*=_EXTSTRESSMUL;
    pres+=_EXTPRESSADD;

    hinv=_H.inv();
    hinvtran=hinv.tran();
    omega=_H.det()*(1-__conj_simframe->_VACUUMRATIO);
    _SIGMA=((hinv*prp)*hinvtran)*omega;
    _SIGMA/=160e3;/* convert MPa to eV/A^3 */
    
    MP_Sync();
    /* All processes enter CGRelax */
    CGRelax(cstr_potential_wrapper_c,n,
            conj_ftol,conj_fevalmax,conj_dfpred,
            conj_x,conj_g,&conj_f,conj_w);

    MP_BeginMasterOnly();
    _H.set(conj_x);

    /* preconditioing */
    _H*=_HPRECOND;
    
    MP_EndMasterOnly();

    for(i=0;i<_NP;i++)
    {
        _SR[i].set(conj_x+i*3+9);
        _SR[i].x*=_XPRECOND;
        _SR[i].y*=_YPRECOND;
        _SR[i].z*=_ZPRECOND;
    }
    INFO_Printf("\tconj_f = %24.18g\n",conj_f);

    /* compute final constrain value */
    s=0;
    for(i=1;i<=constrainatoms[0];i++)
    {
        ipt=constrainatoms[i];
        ds0=_SR2[ipt]-_SR1[ipt];
        ds =_SR[ipt]-_SR1[ipt];
        s+=dot(ds,ds0);
    }
    s/=constrain_dist;
    INFO_Printf("after  relaxation   s=%f E=%25.15e \n",s,conj_f);
    
    /* write configuration in NEB format */
    sprintf(filename,"chain%5.3f.out",s);
    fp=fopen(filename,"w");
    for(i=1;i<=constrainatoms[0];i++)
    {
        ipt=constrainatoms[i];
        fprintf(fp,"%25.15e %25.15e %25.15e\n",_R[ipt].x,_R[ipt].y,_R[ipt].z);
    }
    fclose(fp);
        
    MP_Sync();
    MP_BeginMasterOnly();
    MP_Free(conj_space);
    MP_EndMasterOnly();
    return 0;
}







/********************************************************************/
/* Harmonic potential of Einstein crystal */
/********************************************************************/

void MDFrame::ECpotential()
{
    int i;
    Vector3 dr, ds;
    double dE;
    if(_SR1==NULL)
    {
        FATAL("ECpotential: _SR1 == NULL");
        return;
    }
    _EPOT=0;
    for(i=0;i<_NP;i++)
    {
        ds=_SR[i]-_SR1[i];
        dr=_H*ds;
        _F[i]=dr*(-_ECSPRING);
        dE=dr.norm2()*_ECSPRING*0.5;
        _EPOT+=dE;
        /*_EPOT_IND[i]=dE;*/
    }
}

void MDFrame::ECpotential_energyonly()
{
    int i;
    Vector3 dr, ds;
    double dE;
    if(_SR1==NULL)
    {
        FATAL("ECpotential: _SR1 == NULL");
        return;
    }
    _EPOT=0;
    for(i=0;i<_NP;i++)
    {
        ds=_SR[i]-_SR1[i];
        dr=_H*ds;
        dE=dr.norm2()*_ECSPRING*0.5;
        _EPOT+=dE;
        /*_EPOT_IND[i]=dE;*/
    }
}

double MDFrame::ECpotential_energyonly(int iatom)
{
    double Eatom;
    Vector3 dr, ds;
    if(_SR1==NULL)
    {
        FATAL("ECpotential: _SR1 == NULL");
        return 0;
    }
    Eatom=0;

    ds=_SR[iatom]-_SR1[iatom];
    dr=_H*ds;
    Eatom=dr.norm2()*_ECSPRING*0.5;
    return Eatom;
}

void MDFrame::HApotential_energyonly()
{
    int n, i, id, j, jd;
    double Lam;
    Vector3 dr, ds;
    double dE, di, dj;
    if(_SR1==NULL)
    {
        FATAL("HApotential: _SR1 == NULL");
        return;
    }
    _EPOT=0;
    for(n=0;n<heslen;n++)
    {
        i = hesind[n*4  ];
        id= hesind[n*4+1];
        j = hesind[n*4+2];
        jd= hesind[n*4+3];

        Lam = hes[n];        

        ds=_SR[i]-_SR1[i];
        dr=_H*ds; di=dr[id];
        
        ds=_SR[j]-_SR1[j];
        dr=_H*ds; dj=dr[jd];

        dE=0.5*Lam*di*dj;
        _EPOT+=dE;
    }
}

void MDFrame::SWITCHpotential_energyonly(double lambda)
{
    int i, ipt;    double E;   Vector3 dr, ds;
    if((_SR1==NULL)&&(_SR2==NULL))
    {
        FATAL("SWITCHpotential: _SR1, _SR2 == NULL");
        return;
    }
    if(_SR2==NULL)
    {
        if (_REFPOTENTIAL==0)
        { /* switch to Einstein crystal */
            dEdlambda=0;
            if(_LAMBDA0==_LAMBDA1)
            {
                if(lambda==0){ potential_energyonly(); _EPOT-=_ECOH*_NP; return; }
                if(lambda==1){ ECpotential_energyonly(); return; }
            }
            ECpotential_energyonly();
            E=_EPOT;
            potential_energyonly(); _EPOT-=_ECOH*_NP;
            dEdlambda=E-_EPOT;
            _EPOT=_EPOT*(1-lambda)+E*lambda;
        }
        else if(_REFPOTENTIAL==1)
        {
            dEdlambda=0;
            if(_LAMBDA0==_LAMBDA1)
            {
                if(lambda==0){ potential_energyonly(); _EPOT-=_ECOH*_NP; return; }
                if(lambda==1){ HApotential_energyonly(); return; }
            }
            HApotential_energyonly();
            E=_EPOT;
            potential_energyonly(); _EPOT-=_ECOH*_NP;
            dEdlambda=E-_EPOT;
            _EPOT=_EPOT*(1-lambda)+E*lambda;
        }
        else
        {
            INFO_Printf("refpotential = %d",_REFPOTENTIAL);
            ERROR("unknown refpotential");
        }
    }
    else
    {/* switch to a different structure */
        dEdlambda=0;
        memcpy(_SR3,_SR,sizeof(Vector3)*_NP);
        /* remove atoms */
        for(i=1;i<=MC_switchoffatoms[0];i++)
                fixed[MC_switchoffatoms[i]]=-1;
        for(i=0;i<_NP;i++)
            _SR[i]=_SR[i]-_SR1[i]+_SR2[i];
        SHtoR();
        potential_energyonly();
        /* add Einstein potential to removed atoms */
        for(i=1;i<=MC_switchoffatoms[0];i++)
        {
                ipt=MC_switchoffatoms[i];
                ds=_SR[ipt]-_SR2[ipt];
                dr=_H*ds;
                _EPOT+=dr.norm2()*_ECSPRING*0.5;
        }
        /* restore these atoms */
        for(i=1;i<=MC_switchoffatoms[0];i++)
                fixed[MC_switchoffatoms[i]]=0;
        E=_EPOT;
        memcpy(_SR,_SR3,sizeof(Vector3)*_NP);
        SHtoR();
        potential_energyonly();

        dEdlambda=E-_EPOT;
        
        /* remove atoms for visulazation */
        for(i=1;i<=MC_switchoffatoms[0];i++)
                fixed[MC_switchoffatoms[i]]=-1;

        _EPOT=_EPOT*(1-lambda)+E*lambda;        
    }
}












/********************************************************************/
/* Molecular Dynamics simulation */
/********************************************************************/

void MDFrame::potential()
{
    INFO("MDFrame::potential() not implemented yet.");
}

void MDFrame::potential_energyonly()
{    
    INFO("MDFrame::potential_energyonly() not implemented yet.");
    /* if not implemented then simply call potential */
}

double MDFrame::potential_energyonly(int iatom)
{
    INFO("MDFrame::potential_energyonly(int) not implemented yet.");
    /* if not implemented then simply call potential_energyonly()
     */
    return _EPOT;
}

void MDFrame::integrator()
{
    switch (integrator_type) {
    case(0): Gear6_step();   break;
    case(1): VVerlet_step(curstep); break;
    default: break;
    }
}


void MDFrame::run()
{
    /* Multi Process will execute this function simultaneously */
    MP_BeginMasterOnly();
    SHtoR();
    DUMP("r[0]"<<_R[0]);
    DUMP("r[1]"<<_R[1]);
    DUMP("r[23]"<<_R[23]);
    MP_EndMasterOnly();
    MP_Sync();

    switch (integrator_type) {
    case(0): INFO("MD with Gear6 integrator");   break;
    case(1): INFO("MD with VVerlet integrator"); break;
    default: break;
    }
    
    
    for(curstep=0;curstep<totalsteps;curstep++)
    {
        MP_BeginMasterOnly();
        DUMP("step "<<curstep);
        while(win!=NULL)
        {
            if(win->IsPaused()) sleep(1);
            else break;
        }
        MP_EndMasterOnly();
        MP_Sync();

        MP_BeginMasterOnly();
        if(curstep%savepropfreq==0)INFO("curstep="<<curstep);
        winplot();
        
        if(curstep>=equilsteps)
        {
            if(saveprop)
            {
                if((curstep%savepropfreq)==0)
                {
                    potential();/* added by Keonwook Kang, Aug 14, 2006 */
                    calcprop(); /* added by Keonwook Kang, Aug 14, 2006 */
                    pf.write(this);
                }
            }

            if(savecn)
                if((curstep%savecnfreq)==0&&(curstep!=0))
                    intercn.write(this);
        }

        /* Integrator */
        step();
        
        if(win!=NULL)
            if(win->IsAlive())
            if(autowritegiffreq>0)
                if((curstep%autowritegiffreq)==0)
                {
                    win->LockWritegif(); //lock and wait for writegif
                }
        MP_EndMasterOnly();
    }
    MP_BeginMasterOnly();
    if(saveprop)
    {
        potential();/* added by Keonwook Kang, Aug 14, 2006 */
        calcprop(); /* added by Keonwook Kang, Aug 14, 2006 */
        pf.write(this);
    }
    MP_EndMasterOnly();
    MP_Sync();
}

void MDFrame::calcprop()  /*calculate physical properties*/
{
    int i, n;
    Matrix33 dH; Vector3 r0, r, dr, f;
    double tmp;
    
    tmp=0.5*(_ATOMMASS[0]*1.e-3/AVO)*1e-20
        /(_TIMESTEP*_TIMESTEP*1e-24)/EV;
    _KATOM=0; _PSTRESS.clear();
    n=0;

#if 0 /* need to implement */
    /* add indenter forces */
    if(indenterspec[0]!=0)
    {
        /* use real coordinates to calculate indentor force, no PBC */
        x0=indenterspec[1];
        y0=indenterspec[2];
        z0=indenterspec[3];
        R0=indenterspec[4];
        K =indenterspec[5];
        r0.set(x0,y0,z0);
        for(i=0;i<_NP;i++)
        {
            r=_H*_SR[i];
            dr=r-r0;
            dR=dr.norm();
            if(dR<R0)
            {
                phi=0.5*K*(R0-dR)*(R0-dR);
                f=dr*(K*(R0-dR)/R0);
                _F[i]+=f;
                _EPOT_IND[i]+=phi;
                _EPOT+=phi;
            }
        }
    }
#endif
    /* add external forces to surface atoms, 
     * for self-consistency, a linear potential is also added to potential energy
     */
    if(extforce[0] > 0)
    {
        for(i=0;i<_NP;i++)
        {
            if( (group[i]>0) && (group[i]<=extforce[0]) )
            {
                f.set(extforce+(group[i]-1)*3+1); f*=forcemul;
                _F[i]+=f;
                _R[i]=_H*_SR[i];
                _EPOT-=dot(f,_R[i]);
            }
        }
    }
    /* every process repeat the same procedure */
    for(i=0;i<_NP;i++)
    {
        if(fixed[i]) continue;
        n++;
        _VR[i]=_H*_VSR[i];
        if(species[i]==0)
        {
            _KATOM+=tmp*_VR[i].norm2();
            _PSTRESS.addnvv(2.0*tmp,_VR[i],_VR[i]);
        }
        else
        {
            _KATOM+=tmp*_VR[i].norm2()*_ATOMMASS[species[i]]/_ATOMMASS[0];
            _PSTRESS.addnvv(2.0*tmp*_ATOMMASS[species[i]]/_ATOMMASS[0],
                            _VR[i],_VR[i]);
        }
    }
    /* _T=_KATOM*EV/(1.5*BOLZ*(n-1)); */
    _T=_KATOM*EV/(1.5*BOLZ*(n));
    _TOTSTRESS=_PSTRESS+_VIRIAL;
//    _TOTSTRESS[0][0]=_PSTRESS+_VIRIAL[0][0];
//    _TOTSTRESS[1][1]=_PSTRESS+_VIRIAL[1][1];
//    _TOTSTRESS[2][2]=_PSTRESS+_VIRIAL[2][2];
    tmp=_H.det()*(1-_VACUUMRATIO);
    _TOTSTRESS/=tmp; /* in eV/A^3 */


    MP_BeginMasterOnly();
    /* only master keep track of Nose-Hoover zeta, HELM */
    _HELM = _HELMP = _EPOT + _KATOM;
    if(usenosehoover)
    {
        if(vt2!=0)
        {
            _HELM=_EPOT+_KATOM+3*n*_TDES*BOLZ/EV*zetav*zetav
                /_TIMESTEP/_TIMESTEP/1e-24/2/vt2;
            _HELMP=_HELM+3*n*_TDES*BOLZ/EV*zeta;
            /*
              INFO_Printf("HELM= %20.16e %20.16e\n"
              "T=%20.16e  zeta=%20.16e\n",_HELM,_HELMP,_T,zeta);
            */
        }
    }

    /* apply constant shear rate on box H */
    if(applyshear)
    {
        dH=_H*_SHEARRATE; dH*=_TIMESTEP;
        _H+=dH;
    }
    MP_EndMasterOnly();

    MP_Sync();

    if(plot_color_axis==2)
    {
        if(((curstep%plotfreq)==0)&&(conj_step%plotfreq)==0)
            calcentralsymmetry();
    }
}

void MDFrame::calcentralsymmetry()
{
    int i, j, k, kpt, *csflags, mk1, mk2;
    double mind1, mind2, d1, d2; Vector3 sik1, sik, rik;
    
    csflags = (int *)malloc(sizeof(int)*_NNM);
    
    for(i=0;i<_NP;i++)
    {
        _TOPOL[i]=0;
    }
    for(i=0;i<_NP;i++)
    {
        for(j=0;j<_NNM;j++)
            csflags[j]=0;
        for(j=0;j<NCS/2;j++)
        {
            mind1=1e10; mk1=-1; sik1.set(-1,-1,-1);
            /* finding the nearest neighor */
            for(k=0;k<nn[i];k++)
            {
                if(csflags[k]!=0) continue;
                kpt=nindex[i][k];
                if(kpt==i) continue;
                sik=_SR[kpt]-_SR[i]; sik.subint();
                rik=_H*sik; d1=rik.norm2();
                if(mind1>d1)
                {
                    mind1=d1; mk1=k; sik1=sik;
                }
            }
            csflags[mk1]=1;
            mind2=1e10; mk2=-1; sik.set(-1,-1,-1);
            /* finding the opposite atom */
            for(k=0;k<nn[i];k++)
            {
                if(csflags[k]!=0) continue;
                kpt=nindex[i][k];
                if(kpt==i) continue;
                sik=_SR[kpt]-_SR[i]; sik+=sik1; sik.subint();
                rik=_H*sik; d2=rik.norm2();
                if(mind2>d2)
                {
                    mind2=d2; mk2=k;
                }
            }
            csflags[mk2]=1;
            _TOPOL[i]+=mind2;
            
        }
    }

    free(csflags);
}

void MDFrame::findcore()
{ /* find the center of mass of defect atoms
   *  assuming that there is only one defect
   *  and that the defect can be characterized by CSD parameter
   */
    int i, n;
    double tmin, tmax;
    Vector3 s0, s1, ds, savg, ravg;

    tmin = input[0];
    tmax = input[1];

    n = 0;  savg.clear();
    for(i=0;i<_NP;i++)
    {
        if( (_TOPOL[i]>=tmin) && (_TOPOL[i]<tmax) )
        {
            if(n==0)
            {
                s0 = _SR[i];
                savg = s0;
            }
            else
            {
                s1 = _SR[i];
                ds = s1 - s0;  ds.subint();
                s1 = s0 + ds;
                savg+=s1;
            }
            n++;
        }
    }
    if (n>0)
       savg /= n;
    else
       ERROR("no defect atoms found!");

    ravg = _H*savg;
    INFO_Printf("findcore: avg pos =  %20.10e %20.10e %20.10e  %20.10e %20.10e %20.10e\n",
                savg.x,savg.y,savg.z, ravg.x,ravg.y,ravg.z);
}


void MDFrame::calHessian()
{
    FILE *fp;
    int i, ind, j, ipt;
    Matrix33 hinv;
    Vector3 df, ds, dr;

    /* prints out df/dx which is the negative of the Hessian matrix */
    fp=fopen("hessian.out","w");

    INFO("open hessian.out");
    SHtoR();
    potential();
    memcpy(_VR,_F,_NP*sizeof(Vector3));
    memcpy(_VSR,_SR,_NP*sizeof(Vector3));
    hinv=_H.inv();
    if(input[0]==0)
    {
        for(ipt=0;ipt<_NP;ipt++)
        {
            INFO("atom "<<ipt);
            for(ind=0;ind<3;ind++)
            {
                _R[ipt]=_H*_VSR[ipt];
                _R[ipt][ind]-=_TIMESTEP;
                _SR[ipt]=hinv*_R[ipt];
                potential();
                memcpy(_VR,_F,_NP*sizeof(Vector3));
                
                _R[ipt]=_H*_VSR[ipt];
                _R[ipt][ind]+=_TIMESTEP;
                _SR[ipt]=hinv*_R[ipt];
                potential();

                _R[ipt]=_H*_VSR[ipt];
                _SR[ipt]=_VSR[ipt];

                for(j=0;j<_NP;j++)
                {
                    df=_F[j]-_VR[j]; df/=(2*_TIMESTEP);
                    fprintf(fp,"%20.13e %20.13e %20.13e\n",df.x,df.y,df.z);
                }
            }
        }
    }
    else
    {
        for(i=0;i<(int)input[0];i++)
        {
            ipt=(int)input[i+1];
            //INFO("atom "<<ipt<<" has "<<nn[ipt]<<" neighbors:");
            //for(j=0;j<nn[ipt];j++)
            //    INFO_Printf(" %d ",nindex[ipt][j]);
            //INFO_Printf("\n");
            for(ind=0;ind<3;ind++)
            {
                /* use centered difference */
                _R[ipt]=_H*_VSR[ipt];
                _R[ipt][ind]-=_TIMESTEP;
                _SR[ipt]=hinv*_R[ipt];
                potential();
                memcpy(_VR,_F,_NP*sizeof(Vector3));

                //if(ipt==4) INFO(_F[ipt]<<"  "<<_F[167]);
                
                _R[ipt]=_H*_VSR[ipt];
                _R[ipt][ind]+=_TIMESTEP;
                _SR[ipt]=hinv*_R[ipt];
                potential();

                //if(ipt==4) INFO(_F[ipt]<<"  "<<_F[167]);
                
                _R[ipt]=_H*_VSR[ipt];
                _SR[ipt]=_VSR[ipt];
                
                for(j=0;j<_NP;j++)
                {
                    df=_F[j]-_VR[j]; df/=(2*_TIMESTEP);
                    ds=_SR[j]-_SR[ipt]; ds.subint(); dr=_H*ds;
                    if(df.norm()>1e-10)
                        fprintf(fp,"%6d %3d %6d %20.13e %20.13e %20.13e   %20.13e %20.13e %20.13e\n",
                                ipt,ind+1,j,df.x,df.y,df.z,dr.x,dr.y,dr.z);
                }
            }
        }        
    }
    fclose(fp);
}

void MDFrame::readHessian()
{
    FILE *fp;
    int i, id, j;
    Vector3 df;

    /* prints out df/dx which is the negative of the Hessian matrix */
    fp=fopen(incnfile,"r");

    Realloc(hes,double,(_NP*3)*(_NNM*3));
    Realloc(hesind,int,(_NP*3)*(_NNM*3)*4);

    heslen = 0;
    for(i=0;i<_NP;i++)
    {
        for(id=0;id<3;id++)
        {
            for(j=0;j<_NP;j++)
            {
                fscanf(fp,"%le %le %le",&df.x,&df.y,&df.z);
                if(df.norm2()>1e-16)
                {
                    hes[heslen] = -df.x;
                    hesind[heslen*4  ] = i;
                    hesind[heslen*4+1] = id;
                    hesind[heslen*4+2] = j;
                    hesind[heslen*4+3] = 0;                    
                    heslen ++;

                    hes[heslen] = -df.y;
                    hesind[heslen*4  ] = i;
                    hesind[heslen*4+1] = id;
                    hesind[heslen*4+2] = j;
                    hesind[heslen*4+3] = 1;                    
                    heslen ++;

                    hes[heslen] = -df.z;
                    hesind[heslen*4  ] = i;
                    hesind[heslen*4+1] = id;
                    hesind[heslen*4+2] = j;
                    hesind[heslen*4+3] = 2;                    
                    heslen ++;

                    if(heslen>(_NP*3)*(_NNM*3))
                    {
                        INFO_Printf("heslen = %d _NP = %d _NNM = %d\n",heslen,_NP,_NNM);
                        FATAL("heslen too large, increase NNM");
                    }
                }
            }
        }
    }
    INFO_Printf("readHessian: heslen = %d\n",heslen);
    fclose(fp);
}

void MDFrame::calphonondisp()
{
    INFO("MDFrame::calphonondisp not implemented");
}

void MDFrame::calmisfit()
{
    int nx, ny, nz, i, j, k, n, ind, inda, indb;
    double x0, y0, z0, x1, y1, z1, dx, dy, dz, area;
    FILE *fp;
    class Vector3 c1, c2, c3, ca, cb, caxcb, dc1, dc2, dc3; /* *S0 */
    class Matrix33 H_origin; //, StreSS;

    MP_BeginMasterOnly();

    /* direction of the surface normal */
    ind = (int)input[0]; ind--;

    /* the other two directions orthogonal to ind */
    inda = (ind+1)%3; indb = (inda+1)%3;
    
    x0 = input[1]; dx = input[2]; x1 = input[3];
    y0 = input[4]; dy = input[5]; y1 = input[6];
    z0 = input[7]; dz = input[8]; z1 = input[9];

    INFO_Printf("%%        x0 = %e, dx = %e, x1 = %e\n",x0,dx,x1);
    INFO_Printf("%%        y0 = %e, dy = %e, y1 = %e\n",y0,dy,y1);
    INFO_Printf("%%        z0 = %e, dz = %e, z1 = %e\n",z0,dz,z1);
    
    if(dx!=0) nx = (int) round((x1-x0)/dx); else nx = 0;
    if(dy!=0) ny = (int) round((y1-y0)/dy); else ny = 0;
    if(dz!=0) nz = (int) round((z1-z0)/dz); else nz = 0;

    INFO_Printf("%%        nx = %d, ny = %d, nz = %d\n",nx,ny,nz);

    fp = fopen("Emisfit.out","w");
    if(fp==NULL) FATAL("calmisfit: open file failure");
        
    fprintf(fp,"%% Data file for misfit energy E(i,j,k) (eV/A^2)\n");
    fprintf(fp,"%% Format: i, j, k, E(i,j,k)\n");
    fprintf(fp,"%%         i=1:nx, j=1:ny, k=1:nz\n");
    fprintf(fp,"%%         nx = %d, ny = %d, nz = %d\n",nx,ny,nz);
    fprintf(fp,"\n");    
    fprintf(fp,"%%         x0 = %e, dx = %e, x1 = %e\n",x0,dx,x1);
    fprintf(fp,"%%         y0 = %e, dy = %e, y1 = %e\n",y0,dy,y1);
    fprintf(fp,"%%         z0 = %e, dz = %e, z1 = %e\n",z0,dz,z1);
    fprintf(fp,"\n");

    c1.set(_H[0][0],_H[1][0],_H[2][0]); 
    c2.set(_H[0][1],_H[1][1],_H[2][1]);
    c3.set(_H[0][2],_H[1][2],_H[2][2]);
    c1 /= latticesize[0][3]; // one lattice vector in each direction
    c2 /= latticesize[1][3];
    c3 /= latticesize[2][3];
    
    ca.set(_H[0][inda],_H[1][inda],_H[2][inda]); 
    cb.set(_H[0][indb],_H[1][indb],_H[2][indb]);
    caxcb = cross(ca, cb);
    area = caxcb.norm();

    fprintf(fp,"%% area = %20.14e\n", area);
    fprintf(fp,"\n");

    H_origin = _H;
    for(n=0;n<_NP;n++) _VSR[n]=_R[n]; // Store original position

    potential();
    
    fprintf(fp,"%% Epot_0/area = %20.14e (eV/A^2)\n",_EPOT/area);
    fprintf(fp,"\n");    
    fprintf(fp,"%% i\t j\t k\t Epot/area (eV/A^2)\n");
    fprintf(fp,"%%-------------------------------------------\n");

    SHtoR();
    
    for(k=0;k<=nz;k++)
        for(j=0;j<=ny;j++)
            for(i=0;i<=nx;i++)
            {
                dc1 = c1*(x0+i*dx);
                dc2 = c2*(y0+j*dy);
                dc3 = c3*(z0+k*dz);

                _H[0][ind]= H_origin[0][ind]+dc1.x + dc2.x + dc3.x;
                _H[1][ind]= H_origin[1][ind]+dc1.y + dc2.y + dc3.y;
                _H[2][ind]= H_origin[2][ind]+dc1.z + dc2.z + dc3.z;

                for(n=0;n<_NP;n++) _R[n]=_VSR[n];
                RHtoS();

                /* forces neighborlist reconstruction every time (important!!) */
                /* clear Verlet neighbor list => enforce neighbor list updated at every time */
                for(n=0;n<_NP;n++) _R0[n].clear();
                potential();

                fprintf(fp, "%d\t %d\t %d\t %20.14e\n",i+1,j+1,k+1,_EPOT/area);

                //StreSS = _VIRIAL/(_H.det()*(1-_VACUUMRATIO))*160e3;
                //fprintf(fp,"%d %d %d %18.14e %lf %lf %lf %lf %lf %lf\n",p,q,r,_EPOT/area,StreSS[0][0],StreSS[0][1],StreSS[0][2],StreSS[1][1],StreSS[1][2],StreSS[2][2]);
            }
    fclose(fp);
    MP_EndMasterOnly();
}


void MDFrame::testpotential()
{
    int i, n, ipt;
    double E0, fx0, fy0, fz0, fx, fy, fz, dx, f2, fmax;
    int imax;
    Vector3 r0;

    potential();
    E0=_EPOT;
    f2=_F[0].norm2(); imax=0; fmax=f2;
    for(i=0;i<_NP;i++)
    {
        f2=_F[i].norm2();
        if(f2>1e4) INFO_Printf("atom [%d] f=(%e %e %e)\n",i,_F[i].x,_F[i].y,_F[i].z);
        if(fmax<f2)
        {
            imax=i;
            fmax=f2;
        }
    }
    INFO_Printf("max force\n");
    INFO_Printf("atom [%d] f=(%e %e %e)\n",imax,_F[imax].x,_F[imax].y,_F[imax].z);
    n=(int)input[0];
    dx=input[1];
    for(i=1;i<=n;i++)
    {
        ipt=(int)input[i+1];
        SHtoR();
        potential();
        E0=_EPOT;
        fx0=_F[ipt].x;
        fy0=_F[ipt].y;
        fz0=_F[ipt].z;
        
        r0=_R[ipt];
        
        _R[ipt].x+=dx; RHtoS();
        potential();
        fx=(E0-_EPOT)/dx;
        _R[ipt]=r0;    RHtoS();
        
        _R[ipt].y+=dx; RHtoS();
        potential();
        fy=(E0-_EPOT)/dx;
        _R[ipt]=r0;    RHtoS();
        
        _R[ipt].z+=dx; RHtoS();
        potential();
        fz=(E0-_EPOT)/dx;
        _R[ipt]=r0;    RHtoS();

        INFO_Printf("atom [%d] \tf0  =(%e %e %e)\n"
                    "          \tfnum=(%e %e %e)\n",
                    ipt, fx0, fy0, fz0, fx, fy, fz);
    }
}

void MDFrame::initvelocity()
{
    int i, n;
    Vector3 vavg,v2avg;
    Matrix33 hinv;
    double v2desired, totalmass, tmp;

    v2desired = 2
            *1E20*BOLZ*_TDES/(_ATOMMASS[0]*1e-3/AVO)
            *(_TIMESTEP*_TIMESTEP*1E-24);

    if(_DOUBLET!=1) v2desired*=.5;
    
    vavg.clear(); v2avg.clear();
    
    MP_BeginMasterOnly();
    n=0; totalmass=0;
    for(i=0;i<_NP;i++)
    {
        if(fixed[i])
        {
            _VR[i].clear();
            continue;
        }
        _VR[i].x=drand48();
        _VR[i].y=drand48();
        _VR[i].z=drand48();
        if(species[i]!=0)
        {
            tmp = sqrt(_ATOMMASS[0]/_ATOMMASS[species[i]]);
            _VR[i] *= tmp;
        }
        vavg+=_VR[i]*_ATOMMASS[species[i]];
        totalmass+=_ATOMMASS[species[i]];
        n++;
    }
    
    vavg/=totalmass;
    for(i=0;i<_NP;i++)
    {
        if(fixed[i]) continue;
        _VR[i]-=vavg;
    }
    for(i=0;i<_NP;i++)
    {
        if(species[i]==0)
            v2avg+=_VR[i].sq();
        else
            v2avg+=_VR[i].sq() * _ATOMMASS[species[i]]/_ATOMMASS[0];
    }
    v2avg/=(n-1)*v2desired;v2avg=v2avg.sqroot();
    
    for(i=0;i<_NP;i++)
    {
        if(v2avg.x>0)_VR[i].x/=v2avg.x;
        if(v2avg.y>0)_VR[i].y/=v2avg.y;
        if(v2avg.z>0)_VR[i].z/=v2avg.z;
    }
    hinv=_H.inv();
    for(i=0;i<_NP;i++)
        _VSR[i]=hinv*_VR[i];
    MP_EndMasterOnly();

    _KATOM=3.0*BOLZ*_TDES/EV*(n-1);
    if(_DOUBLET!=1) _KATOM*=.5;
    
}

void MDFrame::randomposition()
{
    int i;
    for(i=0;i<_NP;i++)
    {
        _SR[i].x=drand48()-0.5;
        _SR[i].y=drand48()-0.5;
        _SR[i].z=drand48()-0.5;
    }
}

void MDFrame::velscaler()
{
//    INFO("use velscaler");
    /* Box temperature rescaling not implemented */
    double katomdesired,eratio,vratio; int i,n;

    MP_BeginMasterOnly();
    n=0;
    for(i=0;i<_NP;i++)
    {
        if(!fixed[i]) n++;
    }
    katomdesired=1.5*BOLZ*_TDES/EV*(n-1);
    eratio=katomdesired/_KATOM;
    eratio=min(eratio,1000);
    vratio=(eratio-1.)/_ATOMTCPL+1.;

    for(i=0;i<_NP;i++)
    {
        _VSR[i]*=vratio;
    }
    MP_EndMasterOnly();
    MP_Sync();
}


void MDFrame::multiplyvelocity()
{
    double factor; int i;

    factor = input[0];
    for(i=0;i<_NP;i++)
    {
        _VSR[i]*=factor;
    }
}



void MDFrame::step()
{
    /* call external function */
    integrator();
    if(scalevelocity&&(!usenosehoover))
        velscaler();

    MP_BeginMasterOnly();
    DUMP("EPOT="<<_EPOT);
    DUMP("KATOM="<<_KATOM);
    DUMP("PRESSURE="<<_TOTSTRESS.trace()/3.);
    DUMP("EPOT(0)="<<_EPOT_IND[0]);
    DUMP("F(0)="<<_F[0]<<'\n');
    MP_EndMasterOnly();
}
    
void MDFrame::eval()
{
    /* multi process functions */
    potential();
    calcprop();

    MP_BeginMasterOnly();
    INFO_Printf("EPOT=%18.14e\n",_EPOT);
    INFO_Printf("KATOM=%18.14e\n",_KATOM);
    INFO_Printf("PRESSURE=%18.14e\n",_TOTSTRESS.trace()/3.);
    INFO("\nVIRIAL=["<<_VIRIAL<<"]\n"
         "\nH=["<<_H<<"]\n");
    INFO("\nStress (in MPa)=\n["<<_VIRIAL/(_H.det()*(1-_VACUUMRATIO))*160e3<<"]\n");
    MP_EndMasterOnly();
}
    
void MDFrame::multieval()
{
    int i;
    for(i=0;i<totalsteps;i++)
        potential();
}










/********************************************************************/
/* Monte Carlo Simulation */
/********************************************************************/

int MDFrame::MCstep()
{
    Vector3 dr, ds, dr0, ds0;
    Matrix33 hinv;
    int i;
    double Eatom0,Eatom1,tmp;
    
    hinv=_H.inv();
    if(MC_atom>=0)
    {/* randomly select trial atom */
        MC_atom = (int)floor(drand48()*_NP);
        while(fixed[MC_atom]==-1) {MC_atom = (int)floor(drand48()*_NP);}

        Eatom0=potential_energyonly(MC_atom);

        MC_dr.x=(drand48()-0.5)*_TIMESTEP;
        MC_dr.y=(drand48()-0.5)*_TIMESTEP;
        MC_dr.z=(drand48()-0.5)*_TIMESTEP;
        ds=hinv*MC_dr;
        _SR[MC_atom]+=ds;

        Eatom1=potential_energyonly(MC_atom);
        _EPOT=_EPOT0+Eatom1-Eatom0;

        /*
        INFO_Printf("_EPOT=%e _EPOT0=%e Eatom1=%e Eatom0=%e\n",
                    _EPOT,_EPOT0,Eatom1,Eatom0);
        potential_energyonly();
        INFO_Printf("_EPOT=%e \n",_EPOT);
        */
    }
    else
    {/* move all atoms at once */
        dr0.clear();
        for(i=0;i<_NP;i++)
        {
            if(fixed[i]==-1) continue;
            dr.x=(drand48()-0.5)*_TIMESTEP;
            dr.y=(drand48()-0.5)*_TIMESTEP;
            dr.z=(drand48()-0.5)*_TIMESTEP;
            dr0+=dr;
            ds=hinv*dr;
            _VSR[i]=ds;
        }
        /* fix center of mass */
        dr0/=_NP;
        ds0=hinv*dr0;
        for(i=0;i<_NP;i++)
        {
            _VSR[i]-=ds0;
            _SR[i]+=_VSR[i];
        }
        potential_energyonly();
    }
    
    if(_EPOT<_EPOT0)
        MC_accept=1;
    else
    {
        tmp=(_EPOT0-_EPOT)/(KB*_TDES);
        if(tmp<-40) MC_accept=0;
        else
        {
            if(drand48()<exp(tmp))
                MC_accept=1;
            else
                MC_accept=0;
        }
    }
    
    if(MC_accept)
    {
        _EPOT0=_EPOT;
        MC_accept_tot++;
    }
    else
    {
        if(MC_atom>=0)
        {
            _SR[MC_atom]-=ds;
        }
        else
        {
            for(i=0;i<_NP;i++)
            {
                _SR[i]-=_VSR[i];
            }
            _EPOT=_EPOT0;
            
        }
    }
    return MC_accept;
}

int MDFrame::MCSWITCHstep(double lambda)
{
    Vector3 dr, ds, dr0, ds0;
    Matrix33 hinv;
    int i;
    double Eatom0, Eatom1, ECatom0, ECatom1, tmp;
    
    hinv=_H.inv();
    Eatom0=ECatom0=Eatom1=ECatom1=0;
    if(MC_atom>=0)
    {/* move one atom at a time */
        MC_atom = (int)floor(drand48()*_NP);

        if(_SR2==NULL)/* switch into Einstein crystal */
        {
            Eatom0 = potential_energyonly(MC_atom);
            ECatom0= ECpotential_energyonly(MC_atom);
            
            MC_dr.x=(drand48()-0.5)*_TIMESTEP;
            MC_dr.y=(drand48()-0.5)*_TIMESTEP;
            MC_dr.z=(drand48()-0.5)*_TIMESTEP;
            ds=hinv*MC_dr;
            _SR[MC_atom]+=ds;
            
            Eatom1 = potential_energyonly(MC_atom);
            ECatom1= ECpotential_energyonly(MC_atom);
            
            _EPOT = _EPOT0 + ((1-lambda)*Eatom1+lambda*ECatom1)
                           - ((1-lambda)*Eatom0+lambda*ECatom0) ;

            dEdlambda += (ECatom1 - Eatom1) - (ECatom0 - Eatom0) ;
        }
        else/* switch into a different structure */
        {
            Eatom0 = potential_energyonly(MC_atom);
            memcpy(_SR3,_SR,sizeof(Vector3)*_NP);
            for(i=0;i<_NP;i++)
                _SR[i]=_SR[i]-_SR1[i]+_SR2[i];
            ECatom0 = potential_energyonly(MC_atom);
            memcpy(_SR,_SR3,sizeof(Vector3)*_NP);

            MC_dr.x=(drand48()-0.5)*_TIMESTEP;
            MC_dr.y=(drand48()-0.5)*_TIMESTEP;
            MC_dr.z=(drand48()-0.5)*_TIMESTEP;
            ds=hinv*MC_dr;
            _SR[MC_atom]+=ds;
            
            Eatom1 = potential_energyonly(MC_atom);
            memcpy(_SR3,_SR,sizeof(Vector3)*_NP);
            for(i=0;i<_NP;i++)
                _SR[i]=_SR[i]-_SR1[i]+_SR2[i];
            ECatom1 = potential_energyonly(MC_atom);
            memcpy(_SR,_SR3,sizeof(Vector3)*_NP);

            _EPOT = _EPOT0 + ((1-lambda)*Eatom1+lambda*ECatom1)
                           - ((1-lambda)*Eatom0+lambda*ECatom0) ;

            dEdlambda += (ECatom1 - Eatom1) - (ECatom0 - Eatom0) ;
        }
    }
    else
    {/* move all atoms at the same time */
        dr0.clear();
        for(i=0;i<_NP;i++)
        {
            dr.x=(drand48()-0.5)*_TIMESTEP;
            dr.y=(drand48()-0.5)*_TIMESTEP;
            dr.z=(drand48()-0.5)*_TIMESTEP;
            dr0+=dr;
            ds=hinv*dr;
            _VSR[i]=ds;
        }
        /* fix center of mass */
        dr0/=_NP;
        ds0=hinv*dr0;
        for(i=0;i<_NP;i++)
        {
            _VSR[i]-=ds0;
            _SR[i]+=_VSR[i];
        }
        SWITCHpotential_energyonly(lambda);
    }
    
    
    if(_EPOT<_EPOT0)
        MC_accept=1;
    else
    {
        tmp=(_EPOT0-_EPOT)/(KB*_TDES);
        if(tmp<-40) MC_accept=0;
        else
        {
            if(drand48()<exp(tmp))
                MC_accept=1;
            else
                MC_accept=0;
        }
    }
    
    if(MC_accept)
    {
        _EPOT0=_EPOT;
        MC_accept_tot++;
    }
    else
    {
        if(MC_atom>=0)
        {
            _SR[MC_atom]-=ds;

            dEdlambda -= (ECatom1 - Eatom1) - (ECatom0 - Eatom0) ;

            _EPOT=_EPOT0;
        }
        else
        {
            for(i=0;i<_NP;i++)
            {
                _SR[i]-=_VSR[i];
            }
            _EPOT=_EPOT0;
        }
    }
    return MC_accept;
}

int MDFrame::runMC()
{
    /* Multi Process will execute this function simultaneously */
    /* only implemented for single processor run */
    /* multi processor run will lead to unpredictable behavior */
    MP_BeginMasterOnly();
    SHtoR();
    MP_EndMasterOnly();
    MP_Sync();

    potential_energyonly();
    _EPOT0=_EPOT;

    MC_accept_tot=0;
    MC_accept_ratio=0;
    for(curstep=0;curstep<totalsteps;curstep++)
    {
        MP_BeginMasterOnly();
        while(win!=NULL)
        {
            if(win->IsPaused()) sleep(1);
            else break;
        }
        MP_EndMasterOnly();
        MP_Sync();

        /* multi process */
        MCstep();
        
        MP_BeginMasterOnly();
        if(curstep%printfreq==0)
        {
            MC_accept_ratio = ((double) MC_accept_tot) / (curstep+1);
            INFO_Printf("curstep = %d MC_atom = %d  MC_accept = %d "
                        "accept_ratio = %e\n",
                        curstep, MC_atom, MC_accept, MC_accept_ratio);
        }
        winplot();
        
        if(curstep>=equilsteps)
        {
            if(saveprop)
            {
                if((curstep%savepropfreq)==0) pf.write(this);
            }

            if(savecn)
                if((curstep%savecnfreq)==0&&(curstep!=0))
                    intercn.write(this);
        }
        if(win!=NULL)
            if(win->IsAlive())
            if(autowritegiffreq>0)
                if((curstep%autowritegiffreq)==0)
                {
                    win->LockWritegif(); //lock and wait for writegif
                }
        MP_EndMasterOnly();
    }
    MP_BeginMasterOnly();
    if(saveprop) pf.write(this);
    MP_EndMasterOnly();
    MP_Sync();

    return 0;
}

int MDFrame::runMCSWITCH()
{
    double lambda, lambdaprev, t, s, g, t5, t4, t3, t2;
    double Wtot, Wavg;
    /* Multi Process will execute this function simultaneously */
    /* only implemented for single processor run */
    /* multi processor run will lead to unpredictable behavior */
//    if(_SR2!=NULL)
//    {
//        Free(_SR3);
//        Realloc(_SR3,Vector3,_NP);
//    }
    
    MP_BeginMasterOnly();
    SHtoR();
    MP_EndMasterOnly();
    MP_Sync();

    lambda=_LAMBDA0;
    lambdaprev=lambda; g=0;
    SWITCHpotential_energyonly(lambda);
    _EPOT0=_EPOT;

    MC_accept_tot=0;
    MC_accept_ratio=0;
    Wtot=0;
    for(curstep=0;curstep<totalsteps;curstep++)
    {
        MP_BeginMasterOnly();
        while(win!=NULL)
        {
            if(win->IsPaused()) sleep(1);
            else break;
        }
        MP_EndMasterOnly();
        MP_Sync();

        /* multi process */
        if((curstep%_SWITCHFREQ)==0)
        {
            t = ((double) curstep)/ (totalsteps-1);
            if(_SWITCHFUNC == 0)
            {
                t2 = t*t;
                t3 = t2*t;
                t4 = t3*t;
                t5 = t4*t;
                s = (t5*(70.*t4-315.*t3+540.*t2-420.*t+126.));
                g = (t4*(630.*t4-2520.*t3+3780.*t2-2520.*t+630.));
            }
            else if(_SWITCHFUNC == 1)
            { /* a linear switching function */
                s = t;
                g = 1;
            }
            else
            {
                s = t;
                g = 1;
                ERROR("unknown switchfunc!");
            }
            lambda = _LAMBDA0 +  s * (_LAMBDA1-_LAMBDA0) ;
        }
        if(lambda!=lambdaprev)
        {
            lambdaprev=lambda;
            /* if lambda changes original potential need to be recomputed */
            SWITCHpotential_energyonly(lambda);
            _EPOT0=_EPOT;
        }
       
        MCSWITCHstep(lambda);
        Wtot += dEdlambda * g * (_LAMBDA1-_LAMBDA0);
        
        MP_BeginMasterOnly();
        if(curstep%printfreq==0)
        {
            MC_accept_ratio = ((double) MC_accept_tot) / (curstep+1);
            INFO_Printf("curstep = %d MC_atom = %d "
                        "accept_ratio = %e lambda = %e dEdlambda=%e E=%e\n",
                        curstep, MC_atom, MC_accept_ratio, lambda, dEdlambda,_EPOT);
        }
        winplot();
        
        if(curstep>=equilsteps)
        {
            if(saveprop)
            {
                if((curstep%savepropfreq)==0) pf.write(this);
            }

            if(savecn)
                if((curstep%savecnfreq)==0&&(curstep!=0))
                    intercn.write(this);
        }
        if(win!=NULL)
            if(win->IsAlive())
            if(autowritegiffreq>0)
                if((curstep%autowritegiffreq)==0)
                {
                    win->LockWritegif(); //lock and wait for writegif
                }
        MP_EndMasterOnly();
    }
    MP_BeginMasterOnly();
    if(saveprop) pf.write(this);
    MP_EndMasterOnly();
    MP_Sync();

    Wavg = Wtot/(totalsteps-1);
    INFO_Printf("dF = %20.16e\n",Wavg);
    return 0;
}

int MDFrame::runTAMC()
{
    int i, ipt, j, n, transition, cnindex, chainindex;
    Vector3 ds, dr;
    Matrix33 hinv;
    double Eatom0, Eatom1, tmp, r0, r2; 
    
    /* temperature accelerated dynamics in Monte Carlo */
    /* only implemented for single processor run */

    /* run MC only for constrainatoms
     * at supposely high temperature
     * periodically save configurations into _Rc (chain of states)
     * and periodically check for transition events by cgrelax
     * if transition event is identified, exit
     * writeRchain will save _Rc into file (if specified in script)
     */

    SHtoR();
    potential_energyonly();
    _EPOT0=_EPOT;

    MC_accept_tot=0;
    MC_accept_ratio=0;

    n=constrainatoms[0];
    transition=0;
    /* save original state to the starting point in chain */
    for(j=1;j<=n;j++)
    {
        ipt=constrainatoms[j];
        _Rc[0][j]=_R[ipt];
    }
    for(curstep=1;curstep<=totalsteps;curstep++)
    {
        while(win!=NULL)
        {
            if(win->IsPaused()) sleep(1);
            else break;
        }

        hinv=_H.inv();

        j = (int)ceil(drand48()*n);
        MC_atom = constrainatoms[j];

        Eatom0=potential_energyonly(MC_atom);

        MC_dr.x=(drand48()-0.5)*_TIMESTEP;
        MC_dr.y=(drand48()-0.5)*_TIMESTEP;
        MC_dr.z=(drand48()-0.5)*_TIMESTEP;
        ds=hinv*MC_dr;
        _SR[MC_atom]+=ds;

        Eatom1=potential_energyonly(MC_atom);
        _EPOT=_EPOT0+Eatom1-Eatom0;
    
        if(_EPOT<_EPOT0)
            MC_accept=1;
        else
        {
            tmp=(_EPOT0-_EPOT)/(KB*_TDES);
            if(tmp<-40) MC_accept=0;
            else
            {
                if(drand48()<exp(tmp))
                    MC_accept=1;
                else
                    MC_accept=0;
            }
        }
    
        if(MC_accept)
        {
            _EPOT0=_EPOT;
            MC_accept_tot++;
        }
        else
        {
            _SR[MC_atom]-=ds;            
        }
                
        if(curstep%printfreq==0)
        {
            MC_accept_ratio = ((double) MC_accept_tot) / (curstep+1);
            INFO_Printf("curstep = %d MC_atom = %d  MC_accept = %d "
                        "accept_ratio = %e\n",
                        curstep, MC_atom, MC_accept, MC_accept_ratio);
        }
        winplot();
        
        if(curstep%savecnfreq==0)
        {/* save current configuration */
            cnindex=curstep/savecnfreq;

            chainindex = (cnindex-1)%_CHAINLENGTH + 1;
            SHtoR();
            for(j=1;j<=n;j++)
            {
                ipt=constrainatoms[j];
                _Rc[chainindex][j]=_R[ipt];
            }            

            /*INFO_Printf("curstep=%d save to chain %d\n",curstep,chainindex);*/
            if(chainindex==_CHAINLENGTH)
            {/* check whether transition has occured */

                /* fix all outside atoms */
                for(i=0;i<_NP;i++)
                    fixed[i]=1;
                for(j=1;j<=n;j++)
                    fixed[constrainatoms[j]]=0;
                
                relax();
                r2=0;
                for(j=1;j<=n;j++)
                {
                    ipt=constrainatoms[j];
                    ds=_SR1[ipt]-_SR[ipt];
                    dr=_H*ds;
                    r2+=dr.norm2();
                }
                r0=sqrt(r2);
                transition = 0;
                if(r0>annealspec[0]) transition=1;

                INFO_Printf("curstep=%d r0 = %e transition = %d\n",
                            curstep,r0,transition);
                if(transition)
                {
                    return transition;
                }
                else
                {/* copy to starting point of the chain */
                    for(j=1;j<=n;j++)
                    {
                        _Rc[0][j]=_Rc[_CHAINLENGTH][j];
                        ipt=constrainatoms[j];
                        _R[ipt]=_Rc[_CHAINLENGTH][j];
                    }
                    RHtoS();
                    potential_energyonly();
                    INFO_Printf("E = %e\n",_EPOT);
                }
            }
        }
    }
    
    return transition;
}







/********************************************************************/
/* Nudged-Elastic-Band relaxation */
/********************************************************************/

/* Memory Allocation */
void MDFrame::AllocChain()
{/* need to fix this allocation later */
    int n,size, i;
    void *c;
    n=constrainatoms[0];
    size=sizeof(Vector3 *)*(_CHAINLENGTH+1)+sizeof(Vector3)*(_CHAINLENGTH+1)*n + 10;

    if(n==0)
    {
            ERROR("AllocChain: number of constrained atoms = 0");
            return;
    }
    if(n>1000)
    {
        ERROR("constrainatoms[0] too big!");
        return;
    }
    
    c=malloc(size); memset(c,0,size);
    _Rc=(Vector3 **) c;   _Rc[0]=(Vector3 *)(_Rc+(_CHAINLENGTH+1));
    for(i=1;i<=_CHAINLENGTH;i++) _Rc[i]=_Rc[i-1]+n;

    c=malloc(size); memset(c,0,size);
    _Fc=(Vector3 **) c;   _Fc[0]=(Vector3 *)(_Fc+(_CHAINLENGTH+1));
    for(i=1;i<=_CHAINLENGTH;i++) _Fc[i]=_Fc[i-1]+n;
}

int MDFrame::readRchain()
{
    int i, j;
    char *buffer; char *pp, *q; 
    char fname[300];

    LFile::SubHomeDir(incnfile,fname);
    LFile::LoadToString(fname,buffer,0);

    pp=buffer;
    
    q=pp; pp=strchr(pp,'\n'); if(pp) *(char *)pp++=0;
    sscanf(q, "%d", &_CHAINLENGTH);

    q=pp; pp=strchr(pp,'\n'); if(pp) *(char *)pp++=0;
    sscanf(q, "%d", constrainatoms);

    if(constrainatoms[0]>1000)
    {
            ERROR("constrainedatoms too large");
            return -1;
    }
    for(i=0;i<constrainatoms[0];i++)
    {
            q=pp; pp=strchr(pp,'\n'); if(pp) *(char *)pp++=0;
            sscanf(q, "%d", constrainatoms+i+1);
    }

    for(j=0;j<=_CHAINLENGTH;j++)
    {
        for(i=0;i<constrainatoms[0];i++)
        {
            q=pp; pp=strchr(pp,'\n'); if(pp) *(char *)pp++=0;
            sscanf(q, "%lf %lf %lf",&(_Rc[j][i].x),&(_Rc[j][i].y),&(_Rc[j][i].z));
        }
    }
    Free(buffer);
    DUMP("readRchain finished");
    return 0;
}

int MDFrame::writeRchain()
{
    int i, j;
    FILE *fp;

    fp=fopen(finalcnfile,"w");

    fprintf(fp,"%d\n",_CHAINLENGTH);
    fprintf(fp,"%d\n",constrainatoms[0]);

    for(i=0;i<constrainatoms[0];i++)
        fprintf(fp,"%d\n",constrainatoms[i+1]);

    for(j=0;j<=_CHAINLENGTH;j++)
        for(i=0;i<constrainatoms[0];i++)
            fprintf(fp,"%25.15e %25.15e %25.15e\n",_Rc[j][i].x,_Rc[j][i].y,_Rc[j][i].z);
    DUMP("writeRchain finished");
    return 0;
}

void MDFrame::initRchain()
{
    int ipt,i,j, n; double s; Vector3 ds;
//    INFO_Printf("initRchain: n not defined!\n");
    /* initialize path */
    for(j=0;j<=_CHAINLENGTH;j++)
    {
        s=(1.0*j)/_CHAINLENGTH;
        n=constrainatoms[0];
        for(i=0;i<n;i++)
        {
             ipt=constrainatoms[i+1];
             ds=_SR2[ipt]; ds-=_SR1[ipt]; ds*=s; ds+=_SR1[ipt];
            _Rc[j][i]=_H*ds;
        }
    }
}

void MDFrame::nebrelax()
{/* relax _SRc by nudged elastic band method (steepest descent) */
 /* fix box only */
    int i,n,j, ipt;
    Vector3 ds, ds0, dr; Matrix33 hinv;
    double s, fr, r2, lavg, *Ec, *Lc;
    FILE *fp;
    
    INFO("NEB Relax");

    if(constrainatoms[0]<=0)
    {
        ERROR("constrainedatoms not set! (atoms not selected)");
        return;
    }

    fp=fopen("nebeng.out","w");
    
    Ec=(double *)malloc(sizeof(double)*(_CHAINLENGTH+1));
    Lc=(double *)malloc(sizeof(double)*(_CHAINLENGTH+1));

    hinv=_H.inv();
    
    /* rid center of mass shift in configB */
    ds0.clear();
    constrain_dist=0; s=0;
    n=constrainatoms[0];
    for(i=0;i<n;i++)
    {
        ipt=constrainatoms[i+1];
        ds0+=_SR2[ipt];
        ds0-=_SR1[ipt];
    }
    ds0/=n;
    for(i=0;i<n;i++)
    {
        ipt=constrainatoms[i+1];
        _SR2[ipt]-=ds0;
    }
    /* compute current constrain value */
    constrain_dist=0; s=0;
    for(i=0;i<n;i++)
    {
        ipt=constrainatoms[i+1];
        ds0=_SR2[ipt]-_SR1[ipt];
        constrain_dist+=ds0.norm2();
    }
    INFO_Printf("neb: constrain_dist=%e\n",constrain_dist);

    for(curstep=0;curstep<totalsteps;curstep++)
    {
        /* get forces */
        for(j=0;j<=_CHAINLENGTH;j++)
        {
           s=(1.0*j)/_CHAINLENGTH;
           /* surrounding atoms at interpolated value */
           if((nebspec[0]==0)||(j==0))
           {
               for(i=0;i<_NP;i++)
               {
                   ds=_SR2[i]; ds-=_SR1[i]; ds*=s; ds+=_SR1[i];
                   _SR[i]=ds;
               }
           }
           /* constained atoms at chain value */
           for(i=0;i<n;i++)
           {
               ipt=constrainatoms[i+1];
               _SR[ipt]=hinv*_Rc[j][i];
           }
           if(nebspec[0]==1)
           {/* relax surrounding atoms */
               for(i=0;i<n;i++)
               {
                   ipt=constrainatoms[i+1];
                   fixed[ipt]=1;
               }
               relax();
           }               
           potential();
           Ec[j]=_EPOT;
           for(i=0;i<n;i++)
           {
               ipt=constrainatoms[i+1];
               _Fc[j][i]=_F[ipt];
           }
        }

        /* orthogonalize forces */
        for(j=1;j<_CHAINLENGTH;j++)
        {
           fr=0; r2=0;
           for(i=0;i<n;i++)
           {
               dr=_Rc[j+1][i]-_Rc[j-1][i];
               fr+=dot(_Fc[j][i],dr);
               r2+=dr.norm2();
           }
           fr/=r2;
           for(i=0;i<n;i++)
           {
               dr=_Rc[j+1][i]-_Rc[j-1][i];
               dr*=fr;
               _Fc[j][i]-=dr;
           }
        }
        

        /* move along force */
        for(j=1;j<_CHAINLENGTH;j++)
        {
           for(i=0;i<n;i++)
           {
                _Rc[j][i]+=_Fc[j][i]*_TIMESTEP;
           }
        }

        /* redistribution */
        if(curstep%nebspec[1]==0) /* redistribution frequency */
        {
            Lc[0]=0;
            for(j=1;j<=_CHAINLENGTH;j++)
            {
                r2=0;
                for(i=0;i<n;i++)
                {
                    dr=_Rc[j][i]-_Rc[j-1][i];
                    r2+=dr.norm2();
                }
                Lc[j]=Lc[j-1]+sqrt(r2);
            }
            lavg=Lc[_CHAINLENGTH]/_CHAINLENGTH;
            for(j=1;j<_CHAINLENGTH;j++)
            {
                for(i=0;i<n;i++)
                {
                    _Rc[j][i]+=(_Rc[j+1][i]-_Rc[j][i])*(lavg*j-Lc[j])/(Lc[j+1]-Lc[j]);
                }
            }
        }
            
        if(curstep%printfreq==0)
        {
           INFO_Printf("curstep = %d\n",curstep);
           for(j=0;j<=_CHAINLENGTH;j++)
                   INFO_Printf("%e  %25.15e\n",1.0*j/_CHAINLENGTH,Ec[j]-Ec[0]);
           fprintf(fp,"%d ",curstep);
           for(j=0;j<=_CHAINLENGTH;j++)
                   fprintf(fp," %25.15e ",Ec[j]-Ec[0]);
           fprintf(fp,"\n");
        }
    }


    free(Ec);
    free(Lc);
    fclose(fp);
}

void MDFrame::annealpath()
{/* simulated annealing of transition path by Monte Carlo */
 /* fix box only */
    int i,n,j,k, ipt, naccept, alg;
    Vector3 ds, ds0, dr, *Rold; Matrix33 hinv;
    double s, rmax, T, T0, lambda, r0, r1, r2, r02, r12, r22, rt;
    double Emax, Enewmax, Eold, Epen, Enewpen, Etot, Enewtot;
    double xi, crit, accratio, *Ec, *Lc;
    FILE *fp;
    
    INFO("annealpath");

    if(constrainatoms[0]<=0)
    {
        ERROR("constrainedatoms not set! (atoms not selected)");
        return;
    }

    rmax  = annealspec[0];
    T0    = annealspec[1];
    lambda= annealspec[2];
    alg   = (int)annealspec[3];
    
    if((rmax<=0)||(T0<=0)||(lambda<=0))
    {
        ERROR("Invalide annealspec !");
        return;
    }
    
    fp=fopen("nebeng.out","w");
    
    Ec=(double *)malloc(sizeof(double)*(_CHAINLENGTH+1));
    Lc=(double *)malloc(sizeof(double)*(_CHAINLENGTH+1));
    Rold=(Vector3 *)malloc(sizeof(Vector3)*constrainatoms[0]);
                           
    hinv=_H.inv();
    
    /* rid center of mass shift in configB */
    ds0.clear();
    constrain_dist=0; s=0;
    n=constrainatoms[0];
    for(i=0;i<n;i++)
    {
        ipt=constrainatoms[i+1];
        ds0+=_SR2[ipt];
        ds0-=_SR1[ipt];
    }
    ds0/=n;
    for(i=0;i<n;i++)
    {
        ipt=constrainatoms[i+1];
        _SR2[ipt]-=ds0;
    }

    INFO("find energy along the entire chain");
    /* get potential energy along the chain */
    for(j=0;j<=_CHAINLENGTH;j++)
    {
        s=(1.0*j)/_CHAINLENGTH;
        /* surrounding atoms at interpolated value */
        for(i=0;i<_NP;i++)
        {
            ds=_SR2[i]; ds-=_SR1[i]; ds*=s; ds+=_SR1[i];
            _SR[i]=ds;
        }
        /* constained atoms at chain value */
        for(i=0;i<n;i++)
        {
            ipt=constrainatoms[i+1];
            _SR[ipt]=hinv*_Rc[j][i];
        }
        potential_energyonly();
        Ec[j]=_EPOT;        
    }
    
    Emax=Ec[0]; Epen=0; Etot=0;
    for(j=1;j<=_CHAINLENGTH;j++)
    {
        /* find maximum energy along the path*/
        if(Emax<Ec[j])    Emax=Ec[j];
        /* find penalty energy */
        if(Ec[j]>Ec[j-1]) Epen+=Ec[j]-Ec[j-1];
        Etot+=Ec[j]-Ec[0];
    }
    
    INFO("begin annealing simulation");
    /* start annealing loop */
    naccept = 0;
    for(curstep=0;curstep<totalsteps;curstep++)
    {
        T = T0*exp(-lambda*curstep/totalsteps);
        
        /* randomly pick a state in the chain */
        j = (int) ceil( drand48()*(_CHAINLENGTH-1) );

        /* save old coordinate and energy */
        for(i=0;i<n;i++)
            Rold[i]=_Rc[j][i];
        Eold=Ec[j];
        
        /* randomly perturb the atoms in state j */
        /* replace constraint by 2-norm, rejection free algorithm */
        r02=0;
        for(i=0;i<n;i++)
        {
            dr=_Rc[j+1][i]-_Rc[j-1][i];
            r02+=dr.norm2();
        }        
        r0=sqrt(r02);
        if(r0>1e-6)
        {
            xi=(2*drand48()-1)*(rmax-r0*0.5);
            r12=rmax*rmax-(r0*0.5+fabs(xi))*(r0*0.5+fabs(xi));
            if(r12<0)
            {
                WARNING("r12<0");
                r12=0;
            }                
            r1=sqrt(r12);
            /* randomize Rc[j] */
            for(i=0;i<n;i++)
                for(k=0;k<3;k++)
                    _Rc[j][i][k]=2*drand48()-1;
            /* orthogonize with t[j]=Rc[j+1]-Rc[j-1] */
            rt=0;
            for(i=0;i<n;i++)
            {
                dr=_Rc[j+1][i]-_Rc[j-1][i];
                rt+=dot(_Rc[j][i],dr);
            }
            rt/=r02;
            for(i=0;i<n;i++)
            {
                dr=_Rc[j+1][i]-_Rc[j-1][i];
                dr*=rt;
                _Rc[j][i]-=dr;
            }
            /* normalize it to r1 */
            r22=0;
            for(i=0;i<n;i++)
                r22+=_Rc[j][i].norm2();
            r2=sqrt(r22);
            for(i=0;i<n;i++)
                _Rc[j][i]*=r1/r2;
            /* shift to center + xi along t[j] */
            for(i=0;i<n;i++)
            {
                dr=_Rc[j+1][i]-_Rc[j-1][i];
                dr/=r0;
                dr*=(r0*0.5+xi);
                dr+=_Rc[j-1][i];
                _Rc[j][i]+=dr;
            }
        }
        else
        {/* r0 = 0 */
            /* randomize Rc[j] */
            for(i=0;i<n;i++)
                for(k=0;k<3;k++)
                    _Rc[j][i][k]=2*drand48()-1;
            /* normalize it to rmax */
            r22=0;
            for(i=0;i<n;i++)
                r22+=_Rc[j][i].norm2();
            r2=sqrt(r22);
            for(i=0;i<n;i++)
                _Rc[j][i]*=(rmax-2e-6)/r2;
            /* shift to center */
            for(i=0;i<n;i++)
            {
                _Rc[j][i]+=(_Rc[j-1][i]+_Rc[j+1][i])*0.5;
            }
        }
        /* old version: using infinite-norm */
        /*
        for(i=0;i<n;i++)
            for(k=0;k<3;k++)
            {
                rold=_Rc[j][i][k];
                rnew=rold+(drand48()-0.5)*_TIMESTEP;
                if((fabs(rnew-_Rc[j-1][i][k])<rmax)&&(fabs(rnew-_Rc[j+1][i][k])<rmax))
                    _Rc[j][i][k]=rnew;
            }
        */
        
        s=(1.0*j)/_CHAINLENGTH;
        /* surrounding atoms at interpolated value */
        for(i=0;i<_NP;i++)
        {
            ds=_SR2[i]; ds-=_SR1[i]; ds*=s; ds+=_SR1[i];
            _SR[i]=ds;
        }
        /* constained atoms at chain value */
        for(i=0;i<n;i++)
        {
            ipt=constrainatoms[i+1];
            _SR[ipt]=hinv*_Rc[j][i];
        }
        potential_energyonly();
        Ec[j]=_EPOT;

        Enewmax=Ec[0]; Enewpen=0; Enewtot=0;
        for(k=1;k<=_CHAINLENGTH;k++)
        {
            /* find maximum energy along the path*/
            if(Enewmax<Ec[k]) Enewmax=Ec[k];
            /* find penalty energy */
            if(Ec[k]>Ec[k-1]) Enewpen+=Ec[k]-Ec[k-1];
            Enewtot+=Ec[k]-Ec[0];            
        }

        switch(alg) {
        case(0): crit=exp((Emax-Enewmax)/T); break;
        case(1): crit=exp((Epen-Enewpen)/T); break;
        case(2): crit=exp((Etot-Enewtot)/T); break;
        default: crit=0; ERROR("annealpath: MC alg not set!"); break;
        }
        xi = drand48();
        if(xi<crit)
        {/* accept the move */
            Emax=Enewmax;
            Epen=Enewpen;
            Etot=Enewtot;
            naccept ++;
        }
        else
        {/* reject the move */
            for(i=0;i<n;i++)
                _Rc[j][i]=Rold[i];
            Ec[j]=Eold;
        }
        accratio = 1.0*naccept/(curstep+1);
            
        if(curstep%printfreq==0)
        {
           INFO_Printf("curstep = %d\n",curstep);
           for(k=0;k<=_CHAINLENGTH;k++)
                   INFO_Printf("%e  %25.15e\n",1.0*k/_CHAINLENGTH,Ec[k]-Ec[0]);
           INFO_Printf("alg=%d Emax=%e Epen=%e Etot=%e T=%e j=%d acceptance ratio = %d/%d = %e\n",
                       alg,Emax-Ec[0],Epen,Etot,T,j,naccept,curstep+1,accratio);
           fprintf(fp,"%d ",curstep);
           for(k=0;k<=_CHAINLENGTH;k++)
                   fprintf(fp," %25.15e ",Ec[k]-Ec[0]);
           fprintf(fp,"\n");
        }
    }

    free(Ec);
    free(Lc);
    free(Rold);
    fclose(fp);
}

void MDFrame::cutpath()
{/* relax _SRc by nudged elastic band method (steepest descent) */
 /* fix box only */
    int i,n,j,n0,n1,step,jnew;
    
    INFO("cutpath");

    if(constrainatoms[0]<=0)
    {
        ERROR("constrainedatoms not set! (atoms not selected)");
        return;
    }

    step  = (int) annealspec[0];
    n0   = (int) annealspec[1];
    n1   = (int) annealspec[2];

    if(n0<=0) n0=1;
    if(n1>=_CHAINLENGTH) n1=_CHAINLENGTH-1;
    n=constrainatoms[0];
    jnew=1;
    for(j=n0;j<=n1;j+=step)
    {
        /* constained atoms at chain value */
        for(i=0;i<n;i++)
            _Rc[jnew][i]=_Rc[j][i];
        jnew++;
    }
    for(i=0;i<n;i++)
        _Rc[jnew][i]=_Rc[_CHAINLENGTH][i];
    _CHAINLENGTH=jnew;
}

int MDFrame::statedistance()
{/* compute the distance between two states _SR1 and _SR (current) */
    int i, n, j, k, ipt;
    Vector3 ds, dr;
    double r, r2, rinf, dx;

    n=constrainatoms[0];
    if(n<=0)
    {/* distance involving all atoms */
        INFO_Printf("distance involving all %d atoms\n",_NP);
        r2=0; rinf=0;
        for(i=0;i<_NP;i++)
        {
            ds=_SR[i]-_SR1[i];
            dr=_H*ds;
            r2+=dr.norm2();
            for(k=0;k<3;k++)
            {
                dx=fabs(dr[k]);
                if(dx>rinf) rinf=dx;
            }                
        }
        r=sqrt(r2);
    }
    else
    {/* distance only involving constrainatoms */
        INFO_Printf("distance involving %d constrain atoms\n",n);
        r2=0; rinf=0;
        for(j=1;j<=n;j++)
        {
            ipt=constrainatoms[j];
            ds=_SR[ipt]-_SR1[ipt];
            dr=_H*ds;
            r2+=dr.norm2();
            for(k=0;k<3;k++)
            {
                dx=fabs(dr[k]);
                if(dx>rinf) rinf=dx;
            }            
        }
        r=sqrt(r2);
    }
    INFO_Printf("2-norm r = %20.12e  infinite-norm r = %20.12e\n",r,rinf);
    return 0;
}








/********************************************************************/
/* Configuration manipulations */
/********************************************************************/

/* basis data for different crystal structures */
#include "lattice.h"

void MDFrame::makecrystal()
{   /* create perfect crystal structure */
    UnitCell my_unitcell;
    Matrix33 myorient, lattice;
    Vector3 tmpv;
    int i,j,k,m,n;

    INFO("makecrystal");
    myorient.set(latticesize[0][0],latticesize[0][1],latticesize[0][2],
                 latticesize[1][0],latticesize[1][1],latticesize[1][2],
                 latticesize[2][0],latticesize[2][1],latticesize[2][2]);

    if(strcmp(crystalstructure,"simple-cubic")==0)
    {
        latticeconst[1]=latticeconst[2]=latticeconst[0];
        my_unitcell.set(1,sc_basis);   /* simple-cubic */
        my_unitcell=my_unitcell*myorient.tran();
        nspecies=1;
    }
    else if(strcmp(crystalstructure,"body-centered-cubic")==0)
    {
        latticeconst[1]=latticeconst[2]=latticeconst[0];
        my_unitcell.set(2,bcc_basis); /* body-centered-cubic */
        my_unitcell=my_unitcell*myorient.tran();
        nspecies=1;
    }
    else if(strcmp(crystalstructure,"face-centered-cubic")==0)
    {
        latticeconst[1]=latticeconst[2]=latticeconst[0];
        my_unitcell.set(4,fcc_basis); /* face-centered-cubic */
        my_unitcell=my_unitcell*myorient.tran();
        nspecies=1;
    }
    else if(strcmp(crystalstructure,"NaCl")==0)
    {
        latticeconst[1]=latticeconst[2]=latticeconst[0];
        my_unitcell.set(8,nacl_basis,nacl_species); /* NaCl (B1) */
        my_unitcell=my_unitcell*myorient.tran();
        nspecies=2;
    }
    else if(strcmp(crystalstructure,"L1_2")==0)
    {
        latticeconst[1]=latticeconst[2]=latticeconst[0];
        my_unitcell.set(4,fcc_basis,l12_species);   /* L1_2 */
        my_unitcell=my_unitcell*myorient.tran();
        nspecies=2;
    }
    else if(strcmp(crystalstructure,"L1_0")==0)
    {
        latticeconst[1]=latticeconst[2]=latticeconst[0];
        my_unitcell.set(4,fcc_basis,l10_species);   /* L1_0 */
        my_unitcell=my_unitcell*myorient.tran();
        nspecies=2;
    }
    else if(strcmp(crystalstructure,"diamond-cubic")==0)
    {
        latticeconst[1]=latticeconst[2]=latticeconst[0];
        my_unitcell.set(8,dc_basis);   /* diamond-cubic */
        my_unitcell=my_unitcell*myorient.tran();
        nspecies=1;
    }
    else if(strcmp(crystalstructure,"zinc-blende")==0)
    {
        latticeconst[1]=latticeconst[2]=latticeconst[0];
        my_unitcell.set(8,dc_basis,zb_species);   /* zinc-blende */
        my_unitcell=my_unitcell*myorient.tran();
        nspecies=2;
    }
    else if(strcmp(crystalstructure,"CaF2")==0)
    {
        latticeconst[1]=latticeconst[2]=latticeconst[0];
        my_unitcell.set(12,caf2_basis,caf2_species);   /* zinc-blende */
        my_unitcell=my_unitcell*myorient.tran();
        nspecies=2;
    }
    else if(strcmp(crystalstructure,"hexagonal-ortho")==0)
    {
        latticeconst[1]=latticeconst[0]*1.73205080756887729352;
        my_unitcell.set(4,hex_ortho_basis);   /* hexagonal-ortho */
        my_unitcell=my_unitcell*myorient.tran();
        nspecies=1;
    }
    else if(strcmp(crystalstructure,"ice-Ih-O")==0)
    {
        latticeconst[1]=latticeconst[0]*1.73205080756887729352;
        my_unitcell.set(8,ice_Ih_O_basis);   /* ice-Ih-Oxygen */
        my_unitcell=my_unitcell*myorient.tran();
        nspecies=1;
    }
    else if(strcmp(crystalstructure,"ice-Ih")==0)
    {
        latticeconst[1]=latticeconst[0]*1.73205080756887729352;
        my_unitcell.set(24,ice_Ih_basis,ice_Ih_species);   /* ice-Ih */
        my_unitcell=my_unitcell*myorient.tran();
        nspecies=2;
    }
    else
    {
        ERROR("unknown lattice structure :"<<crystalstructure
              <<" for makecn");
    }
    INFO("my_unitcell\n"<<my_unitcell);

    n=0;
    _NP=(int)(latticesize[0][3]*latticesize[1][3]
              *latticesize[2][3]*my_unitcell.n);
    INFO("makecn: _NP="<<_NP);
    
    /* Multi process function */
    Alloc();
    
    MP_BeginMasterOnly();
    for(i=0;i<(int)latticesize[0][3];i++)
        for(j=0;j<(int)latticesize[1][3];j++)
            for(k=0;k<(int)latticesize[2][3];k++)
            {
                for(m=0;m<my_unitcell.n;m++)
                {
                    tmpv.set(i,j,k);
                    _SR[n+m]=my_unitcell.basis[m]+tmpv;
                    if(_SAVEMEMORY<9)
                            species[n+m]=my_unitcell.species[m];
                }
                n+=my_unitcell.n;
            }
    for(i=0;i<_NP;i++)
    {
        _SR[i].x/=latticesize[0][3];
        _SR[i].y/=latticesize[1][3];
        _SR[i].z/=latticesize[2][3];
    }
    for(i=0;i<_NP;i++)
    {
        _SR[i]-=0.5;
        fixed[i]=0;
        if(_SAVEMEMORY<9)
                _VSR[i].clear();
    }

    lattice.set(latticeconst[0],0.,0., 0.,latticeconst[1],0., 0.,0.,latticeconst[2]);
    _H=lattice*myorient.tran();
    lattice.set(latticesize[0][3],0.,0., 0.,latticesize[1][3],0., 0.,0.,latticesize[2][3]);
    _H=_H*lattice;
    
    _H=_H.reorient();
    
    MP_EndMasterOnly();
}


void MDFrame::makecut()
{
    INFO("MD++: makecut no longer supported");
}

void MDFrame::makedipole()
{
    /* Create Dislocation dipole by using approximated
       isotropic linear elasticity solution under
       Periodic Boundary Condition with image summations */
    int i,j,k,minx,maxx,miny,maxy;
    int xind,yind,zind;
    double theta[50], pre, frac, x, y, r21, r22, bx, by, nu, nua, nub;
    class Vector3 sr0,sr1,r0,r1,ds,dr,sb,b,du,st,due;
    class Vector3 sra,srb,src,ra,rb,rc,dua,dub,duc,ux,uy;
    class Vector3 px, py, pz, pa, pb, pc;
    double pxpa, pxpb, pypa, pypb;
    int removenum,n,insertnum;
    double dx, dy, dxc, dyc;
    int store, gcut;  
    
    MP_BeginMasterOnly();
    if(input[0]==0)
    {
        ERROR("makedipole(): no dipole geometry set");
        return;
    }
    store = (int) input[17];
    gcut = (int) input[18];
    
    /* read in parameters */
    zind=(int)input[0]; /* 1x/2y/3z */
    yind=(int)input[1];
    xind=zind+yind;if(xind==5)xind=1;if(xind==4)xind=2;
    xind--;yind--;zind--;
    sb.set(input[2],input[3],input[4]);
    b=_H*sb;
    if(fabs(b.x)<1e-8) b.x=0;
    if(fabs(b.y)<1e-8) b.y=0;
    if(fabs(b.z)<1e-8) b.z=0;
    INFO("sb="<<sb<<"\nb="<<b);
    if(!gcut)
    {
        sr0[xind]=input[5];sr0[yind]=input[6];sr0[zind]=0;
        sr1[xind]=input[5];sr1[yind]=input[7];sr1[zind]=0;
    }
    else
    {
        sr0[xind]=input[5];sr0[yind]=input[6];sr0[zind]=0;
        sr1[xind]=input[7];sr1[yind]=input[8];sr1[zind]=0;
        INFO("sr0="<<sr0);
        INFO("sr1="<<sr1);
    }
    r0=_H*sr0;r1=_H*sr1;
    pz.set(_H[0][zind],_H[1][zind],_H[2][zind]);
    pz/=pz.norm();
    py=r1-r0;
    py.orth(pz); py/=py.norm();
    px=cross(py,pz);
    INFO("px="<<px);
    INFO("py="<<py);
    INFO("pz="<<pz);
    pa.set(_H[0][xind],_H[1][xind],_H[2][xind]); //pa/=pa.norm();
    pb.set(_H[0][yind],_H[1][yind],_H[2][yind]); //pb/=pb.norm();
    pc.set(_H[0][zind],_H[1][zind],_H[2][zind]); //pc/=pc.norm();
    pxpa=px*pa; pxpb=px*pb; pypb=py*pb; pypa=py*pa;
    bx=b*px; by=b*py;
    INFO("bx="<<bx<<"  by="<<by);
    if(fabs(pxpa)<1e-8) pxpa=0;
    if(fabs(pxpb)<1e-8) pxpb=0;
    if(fabs(pypb)<1e-8) pypb=0;
    if(fabs(pypa)<1e-8) pypa=0;
    if(pxpa<0) { pxpa=-pxpa; pxpb=-pxpb; px*=-1; INFO("redefine x");}
    INFO_Printf("pxpa=%e pxpb=%e pypb=%e pypa=%e\n",pxpa,pxpb,pypb,pypa);
    
    /* reference (ghost) points */
    sra[xind]=-0.5;sra[yind]=-0.5;sra[zind]=0;
    srb[xind]= 0.5;srb[yind]=-0.5;srb[zind]=0;
    src[xind]=-0.5;src[yind]= 0.5;src[zind]=0;
    ra=_H*sra;rb=_H*srb;rc=_H*src;
    nu=input[8]; nua=1.0/4/(1-nu)/2/M_PI; nub=(1.0-2*nu)*nua;
    minx=(int)input[9]; maxx=(int)input[10];
    miny=(int)input[11];maxy=(int)input[12];
#define getdu_sav(SR) protect(\
                ds[xind]=j; ds[yind]=k; ds[zind]=0;\
                ds+=SR; ds-=sr0; \
                x=ds[xind]*pxpa+ds[yind]*pxpb;\
                y=ds[xind]*pypa+ds[yind]*pypb; r21=x*x+y*y;\
                theta[0]=atan2(-x,y);\
                ds[xind]=j; ds[yind]=k; ds[zind]=0;\
                ds+=SR; ds-=sr1; \
                x=ds[xind]*pxpa+ds[yind]*pxpb;\
                y=ds[xind]*pypa+ds[yind]*pypb; r22=x*x+y*y;\
                theta[1]=atan2(-x,y);\
                pre=0.5/M_PI*(theta[1]-theta[0]);\
                du=b*pre; if((fabs(bx)>1e-8)||(fabs(by)>1e-8)){due.clear();\
                due-=px*(-bx*sin(theta[0]*2)*nua+by*(log(r21)*nub+cos(theta[0]*2)*nua));\
                due-=py*( by*sin(theta[0]*2)*nua-bx*(log(r21)*nub-cos(theta[0]*2)*nua));\
                due+=px*(-bx*sin(theta[1]*2)*nua+by*(log(r22)*nub+cos(theta[1]*2)*nua));\
                due+=py*( by*sin(theta[1]*2)*nua-bx*(log(r22)*nub-cos(theta[1]*2)*nua));\
                /*INFO("due="<<due);*/ du+=due;} \
                )
#define getdu_s(SR) protect(\
                ds[xind]=j; ds[yind]=k; ds[zind]=0;\
                ds+=SR; ds-=sr0; \
                theta[0]=atan2(-ds[xind],ds[yind]);\
                ds[xind]=j; ds[yind]=k; ds[zind]=0;\
                ds+=SR; ds-=sr1; \
                theta[1]=atan2(-ds[xind],ds[yind]);\
                pre=0.5/M_PI*(theta[1]-theta[0]);\
                du=b*pre;)
#define getdu getdu_sav
    
    if(storedr==NULL)
    {
        INFO("allocate storedr");
        Realloc(storedr,Vector3,_NP*allocmultiple);
        /*storedr = (Vector3 *)malloc(sizeof(Vector3)*_NP*allocmultiple);*/
        SHtoR();
        for(i=0;i<_NP;i++) /*storedr[i].clear();*/ storedr[i]=_R[i];
    }
    
    /* remove/insert atoms */
    removenum=insertnum=0;
    if(fabs(b*px)>1e-8)
    {
        if((b*px)>0)
        {
            INFO("Need to remove atoms");
            removenum=0;
            dr=r1-r0; dyc=dr*py; dxc=b*px;
            for(i=0;i<_NP;i++)
            {
                /* limit dislocation length (cut depth) */
                if((int)input[14])
                    if((_SR[i][zind]<input[15])||(_SR[i][zind]>=input[16]))
                        continue;
                ds=_SR[i]; ds-=sr0; ds.subint();
                dr=_H*ds; dx=dr*px; dy=dr*py;
                if((dx>=-dxc*0.5)&&(dx<dxc*0.5)&&(dy>=0)&&(dy<dyc))
                {
                    INFO_Printf("need remove atom %d\n",i);
                    fixed[i] = -1; /* flag for removal */
                    removenum++;
                }
            }
            INFO("need removenum="<<removenum);
        }
        else
        {
            INFO("Need to insert atoms");
            insertnum=0;
            dr=r1-r0; dyc=dr*py; dxc=b*px; dxc=dxc*(-1.0);
            for(i=0;i<_NP;i++)
            {
                /* limit dislocation length (cut depth) */
                if((int)input[14])
                    if((_SR[i][zind]<input[15])||(_SR[i][zind]>=input[16]))
                        continue;
                ds=_SR[i]; ds-=sr0; ds.subint();
                dr=_H*ds; dx=dr*px; dy=dr*py;
                if((dx>=-dxc*0.5)&&(dx<dxc*0.5)&&(dy>=0)&&(dy<dyc))
                {
                    INFO_Printf("need replicate atom %d\n",i);
                    _SR[insertnum+_NP] = _SR[i];
                    _R[insertnum+_NP] = _R[i];
                    storedr[insertnum+_NP]=storedr[i];
                    insertnum++;
                }
            }
            INFO("need insertnum="<<insertnum);
        }
    }

    dua.clear(); dub.clear(); duc.clear();

    if((minx!=maxx)||(miny!=maxy))
    {/* pbc correction only if pbc is applied */
        for(j=minx;j<=maxx;j++)
            for(k=miny;k<=maxy;k++)
            {
                getdu(sra); dua+=du;
                getdu(srb); dub+=du;
                getdu(src); duc+=du;
            }
    }
    ux=dub-dua;
    uy=duc-dua;
    INFO("overall tilt ux="<<ux<<" uy="<<uy);

    SHtoR();
    for(i=0;i<_NP;i++)
    {
        if(fixed[i]) continue;
        /* limit dislocation length (cut depth) */
        if((int)input[14])
            if((_SR[i][zind]<input[15])||(_SR[i][zind]>=input[16]))
                continue;
        for(j=minx;j<=maxx;j++)
            for(k=miny;k<=maxy;k++)
            {
                st=_SR[i]; st[zind]=0; getdu(st);
                storedr[i]+=du;
            }
        storedr[i]-=ux*_SR[i][xind];
        storedr[i]-=uy*_SR[i][yind];
        if(i==0) INFO("storedr[0]="<<storedr[0]);
    }
    INFO("originally "<<_NP<<" atoms");
    INFO("insert "<<insertnum<<" atoms");
    _NP+=insertnum;
    INFO("now "<<_NP<<" atoms");


    /* shift box */
    if(input[13])
    {
        INFO("shiftbox to accommodate plastic strain");
        frac=-(input[7]-input[6]);
        dH[0][xind]+=(_H[0][0]*input[2]+_H[0][1]*input[3]
                      +_H[0][2]*input[4])*frac;
        dH[1][xind]+=(_H[1][0]*input[2]+_H[1][1]*input[3]
                      +_H[1][2]*input[4])*frac;
        dH[2][xind]+=(_H[2][0]*input[2]+_H[2][1]*input[3]
                      +_H[2][2]*input[4])*frac;
    }

    INFO_Printf("makedipole: store=%d\n",store);
    if(!store)
    {
        /* commit displacement field */
        SHtoR();
        for(i=0;i<_NP;i++)
        {
            /*_R[i]+=storedr[i];*/
            _R[i]=storedr[i];
        }
        RHtoS();
        INFO("free storedr");
        free(storedr);
        storedr = NULL;

        /* commit removing atoms */
        n=0; SHtoR();
        removenum=0;
        for(i=0;i<_NP;i++)
        {
            if(fixed[i]!=-1)
            {
                _R0[n]=_R[i];
                fixed[n]=fixed[i]; //0;
                n++;
            }
            else
            {
                removenum++;
            }
        }
        INFO("originally "<<_NP<<" atoms");
        INFO("remove "<<removenum<<" atoms");
        _NP-=removenum;
        INFO("now "<<_NP<<" atoms");
        R0toR();  RHtoS();
        INFO("NP="<<_NP<<"  n="<<n);
        /* end of remove atoms */

        /* commit shift box */
        _H+=dH;
    }
    


    MP_EndMasterOnly();
}

void MDFrame::makedislocation()
{
    /* create an arbitrarily oriented dislocation line
     * Input format:
     *            input = [ 0/1      : activation
     *                      a        : lattice constant (in Angstrom)
     *                      bx by bz : Burgers vector (in unit of a)
     *                      lx ly lz : line direction vector
     *                      nx ny nz : normal vector of cut plane
     *                      x0 y0 z0 : a point on dislocation (in Angstrom)
     *                      nu       : Poisson's ratio (for nonscrew dislocation)
     *                      0/1      : 0 commit, 1 store
     *                    ]
     * Notice:
     *   b, l, n are now specified in real coordinates (not reduced coordinates s)
     *   b is given in unit of a
     *   l and n are vectors which will be automatically normalized
     */
    double a, nu, nua, nub, bx, by, bz, x, y, r2, theta, pre;
    Vector3 b, l, n, r0, e1, e2, e3, b0, du, due;
    Matrix33 M;
    int store, cut, i, k, removenum, insertnum;
    
    MP_BeginMasterOnly();
    if(input[0]==0)
    {
        INFO_Printf("input = [%f %f %f ]\n",
                    input[0],input[1],input[2]);
        ERROR("makedislocation(): no dislocation geometry set");
        return;
    }
    a = input[1];
    b.set(input[2],input[3],input[4]);
    l.set(input[5],input[6],input[7]);
    n.set(input[8],input[9],input[10]);
    r0.set(input[11],input[12],input[13]);
    nu = input[14];
    store = (int) input[15];
    cut = (int) input[16];
    
    /* find real coordinates of b */
    b*=a;
    if(fabs(b.x)<1e-8) b.x=0;
    if(fabs(b.y)<1e-8) b.y=0;
    if(fabs(b.z)<1e-8) b.z=0;
    

    /* set up coordinate system */    
    e3=l;
    e3/=e3.norm();
    e2=n;
    e2/=e2.norm();
    e1=cross(e2,e3);
    M.setcol(e1,e2,e3);

    bx=b*e1; by=b*e2; bz=b*e3;
    b0.set(bx,by,bz);
    nu=input[7]; nua=1.0/4/(1-nu)/2/M_PI; nub=(1.0-2*nu)*nua;
    
    if((storedr==NULL))
    {
        INFO("allocate storedr");
        Realloc(storedr,Vector3,_NP*allocmultiple);
        /*storedr = (Vector3 *)malloc(sizeof(Vector3)*_NP*allocmultiple);*/
        SHtoR();
        for(i=0;i<_NP;i++) /*storedr[i].clear();*/ storedr[i]=_R[i];
    }

    /* remove/insert atoms */
    removenum=insertnum=0;
    if(fabs(by)>1e-8)
    {
        INFO("Remove/insert atoms not implemented yet");
        if((by)>0)
        {
            INFO("Need to remove atoms");
            removenum=0;
            INFO("need removenum="<<removenum);
        }
        else
        {
            INFO("Need to insert atoms");
            insertnum=0;
            INFO("need insertnum="<<insertnum);
        }
        return;
    }

    SHtoR();
    for(i=0;i<_NP;i++)
    {
        x = (_R[i]-r0)*e1;
        y = (_R[i]-r0)*e2;
        if(cut)
        {
            if(y>0)
                du=b0*0.5;
            else
                du=b0*(-0.5);
        }
        else
        {
        r2 = x*x + y*y;
        theta = atan2(y, x);
        pre = 0.5/M_PI*theta;
        du = b0*pre;
        if ((fabs(bx)>1e8)||(fabs(by)>1e8))
        {
            due.clear();
            due-=x*(-bx*sin(theta*2)*nua+by*(log(r2)*nub+cos(theta*2)*nua));
            due-=y*( by*sin(theta*2)*nua-bx*(log(r2)*nub-cos(theta*2)*nua));
            du += due;
        }
        du = M*du;
        storedr[i]+=du;
        if(store&&0)/* allow cut-planes to overlap when introducing multiple dislocations */
        {
#if 1
            if((x<0)&&(y<0)) du=b0*(-0.5);
            if((x<0)&&(y>0)) du=b0*(0.5);
#else
            if((x<0)&&(y>0)) du=b0;
#endif            
            du = M*du;
            _R[i]+=du;
        }
        }
        if(i==0) INFO("storedr[0]="<<storedr[0]);
    }
    RHtoS();

    if(!store)
    {
        /* commit displacement field */
        SHtoR();
        for(i=0;i<_NP;i++)
        {
            /*_R[i]+=storedr[i];*/
            _R[i]=storedr[i];
        }
        RHtoS();
//        free(storedr);
//        storedr = NULL;

        /* commit removing atoms */
        k=0; SHtoR();
        removenum=0;
        for(i=0;i<_NP;i++)
        {
            if(fixed[i]!=-1)
            {
                _R0[k]=_R[i];
                fixed[k]=fixed[i];
                k++;
            }
            else
            {
                removenum++;
            }
        }
        INFO("originally "<<_NP<<" atoms");
        INFO("remove "<<removenum<<" atoms");
        _NP-=removenum;
        INFO("now "<<_NP<<" atoms");
        R0toR();  RHtoS();
        INFO("NP="<<_NP<<"  n="<<n);
    }
    MP_EndMasterOnly();
    setfixbufferatoms();
}


void MDFrame::makecylinder()
{
    /* Cut a cylinder out of the current configuration
     *
     * input = [ zind yind x0 y0 rrem flag ]
     *
     *  zind: direction of the cylinder axis
     *  yind: direction of another axis (perpendicular to zind)
     *  x0, y0: center of the cylinder axis
     *  rrem: radius of the cylinder
     *  flag: if flag==0, then all atoms outside the cylinder is removed
     *        if flag==1, then all atoms outside the cylinder is fixed
     */
    
    int i, xind, yind, zind, flag;
    double r, rrem;
    double x, y, bx, by;
    class Vector3 sr0,r0,ds,dr,sb,b,du,st,due;
    class Vector3 ux,uy;
    class Vector3 px, py, pz, pa, pb, pc;
    double pxpa, pxpb, pypa, pypb;
    int removenum,n;

    MP_BeginMasterOnly();
    if(input[0]==0)
    {
        INFO_Printf("input = [%f %f %f ]\n",
                    input[0],input[1],input[2]);
        ERROR("makecylinder(): no dislocation geometry set");
        return;
    }

    /* read in parameters */
    zind=(int)input[0]; /* 1x/2y/3z */
    yind=(int)input[1];
    xind=zind+yind;if(xind==5)xind=1;if(xind==4)xind=2;
    xind--;yind--;zind--;
    
    /* set up coordinate system */
    sr0[xind]=input[2];sr0[yind]=input[3];sr0[zind]=0;
    
    r0=_H*sr0;
    pz.set(_H[0][zind],_H[1][zind],_H[2][zind]);
    pz/=pz.norm();
    py.set(_H[0][yind],_H[1][yind],_H[2][yind]);
    py.orth(pz); py/=py.norm();
    px=cross(py,pz);
    INFO("px="<<px);
    INFO("py="<<py);
    INFO("pz="<<pz);
    pa.set(_H[0][xind],_H[1][xind],_H[2][xind]); //pa/=pa.norm();
    pb.set(_H[0][yind],_H[1][yind],_H[2][yind]); //pb/=pb.norm();
    pc.set(_H[0][zind],_H[1][zind],_H[2][zind]); //pc/=pc.norm();
    pxpa=px*pa; pxpb=px*pb; pypb=py*pb; pypa=py*pa;
    bx=b*px; by=b*py;
    INFO("bx="<<bx<<"  by="<<by);
    if(fabs(pxpa)<1e-8) pxpa=0;
    if(fabs(pxpb)<1e-8) pxpb=0;
    if(fabs(pypb)<1e-8) pypb=0;
    if(fabs(pypa)<1e-8) pypa=0;
    if(pxpa<0) { pxpa=-pxpa; pxpb=-pxpb; px*=-1; INFO("redefine x");}
    INFO_Printf("pxpa=%e pxpb=%e pypb=%e pypa=%e\n",pxpa,pxpb,pypb,pypa);

    rrem=input[4];
    flag=(int) input[5];

    /* remove all atoms outside rrem */
#define getdr(SR) protect(\
                ds=SR; ds-=sr0; ds[zind]=0; \
                x=ds[xind]*pxpa+ds[yind]*pxpb;\
                y=ds[xind]*pypa+ds[yind]*pypb; r=sqrt(x*x+y*y);)
    
    SHtoR();
    for(i=0;i<_NP;i++)
    {
        getdr(_SR[i]);

        if(r>rrem)
        {
            if(flag==1)
                fixed[i]=1;
            else
                fixed[i]=-1;
        }
    }

    /* commit removing atoms */
    n=0; SHtoR();
    removenum=0;
    for(i=0;i<_NP;i++)
    {
        if(fixed[i]!=-1)
        {
            _R0[n]=_R[i];
            fixed[n]=fixed[i];
            n++;
        }
        else
        {
            removenum++;
        }
    }
    INFO("originally "<<_NP<<" atoms");
    INFO("remove "<<removenum<<" atoms");
    _NP-=removenum;
    INFO("now "<<_NP<<" atoms");
    R0toR();  RHtoS();
    INFO("NP="<<_NP<<"  n="<<n);

    MP_EndMasterOnly();
    setfixbufferatoms();
}

void MDFrame::setfixbufferatoms()
{
    int i, j, jpt, ninter, nfree, nfixed;
    Vector3 sij, rij;
    double r;
    
    refreshneighborlist();
    
    MP_BeginMasterOnly();
    INFO("setfixbufferatoms");
    
    if(fixedatomenergypartition)
    {/* find all fixed atoms that are interacting with free atoms
      * set them fixed[i]=2
      */
        for(i=0;i<_NP;i++)
        {
            if(!fixed[i]) continue;
            fixed[i]=1;
            for(j=0;j<nn[i];j++)
            {
                jpt=nindex[i][j];
                if(fixed[jpt]) continue;
                jpt=nindex[i][j];
                sij=_SR[jpt]-_SR[i];
                sij.subint();
                rij=_H*sij;
                r=rij.norm();
                if(r<_RLIST-_SKIN)
                {
                    fixed[i]=2;
                }
            }                
        }
        ninter=0; nfree=0; nfixed=0;
        for(i=0;i<_NP;i++)
        {
            if(fixed[i]!=1) ninter++;
            if(fixed[i]==0) nfree++;
            if(fixed[i]) nfixed++;
        }
        INFO("number of interacting atoms: ninter="<<ninter);
        INFO("number of free        atoms: nfree ="<<nfree);
        INFO("number of fixed       atoms: nfixed="<<nfixed);
    }
    MP_EndMasterOnly();
}

void MDFrame::makedislcylinder()
{
    /* Create single dislocation in a cylindrical fixed boundary */
    
    int i, xind, yind, zind;
    double r, rfix, rgfbc, rrem;
    
    /*double theta[2], pre, x, y, r21, r22, bx, by, nu, nua, nub;*/
    double theta[2], pre, x, y, r21, bx, by, nu, nua, nub;
    class Vector3 sr0,r0,ds,dr,sb,b,du,st,due;
    class Vector3 ux,uy;
    class Vector3 px, py, pz, pa, pb, pc;
    double pxpa, pxpb, pypa, pypb;
    int removenum,n,insertnum;
    double dx, dxc;
    static class Vector3 *storedr;
    int store;

    MP_BeginMasterOnly();

    INFO("outdated function, use makecylinder and makedislocation instead");
    INFO("see Computer Simulations of Dislocations, Section 3.1, ta-screw.script");
    
    if(input[0]==0)
    {
        INFO_Printf("input = [%f %f %f ]\n",
                    input[0],input[1],input[2]);
        ERROR("makedislocation(): no dislocation geometry set");
        return;
    }
    store = (int) input[11];
    
    /* read in parameters */
    zind=(int)input[0]; /* 1x/2y/3z */
    yind=(int)input[1];
    xind=zind+yind;if(xind==5)xind=1;if(xind==4)xind=2;
    xind--;yind--;zind--;
    
    sb.set(input[2],input[3],input[4]);
    b=_H*sb;
    if(fabs(b.x)<1e-8) b.x=0;
    if(fabs(b.y)<1e-8) b.y=0;
    if(fabs(b.z)<1e-8) b.z=0;

    /* set up coordinate system */
    sr0[xind]=input[5];sr0[yind]=input[6];sr0[zind]=0;
    
    r0=_H*sr0;
    pz.set(_H[0][zind],_H[1][zind],_H[2][zind]);
    pz/=pz.norm();
    py.set(_H[0][yind],_H[1][yind],_H[2][yind]);
    py.orth(pz); py/=py.norm();
    px=cross(py,pz);
    INFO("px="<<px);
    INFO("py="<<py);
    INFO("pz="<<pz);
    pa.set(_H[0][xind],_H[1][xind],_H[2][xind]); //pa/=pa.norm();
    pb.set(_H[0][yind],_H[1][yind],_H[2][yind]); //pb/=pb.norm();
    pc.set(_H[0][zind],_H[1][zind],_H[2][zind]); //pc/=pc.norm();
    pxpa=px*pa; pxpb=px*pb; pypb=py*pb; pypa=py*pa;
    bx=b*px; by=b*py;
    INFO("bx="<<bx<<"  by="<<by);
    if(fabs(pxpa)<1e-8) pxpa=0;
    if(fabs(pxpb)<1e-8) pxpb=0;
    if(fabs(pypb)<1e-8) pypb=0;
    if(fabs(pypa)<1e-8) pypa=0;
    if(pxpa<0) { pxpa=-pxpa; pxpb=-pxpb; px*=-1; INFO("redefine x");}
    INFO_Printf("pxpa=%e pxpb=%e pypb=%e pypa=%e\n",pxpa,pxpb,pypb,pypa);

    nu=input[7]; nua=1.0/4/(1-nu)/2/M_PI; nub=(1.0-2*nu)*nua;
    rfix=input[8]; rgfbc=input[9]; rrem=input[10];
    
    if(storedr==NULL)
    {
        INFO("allocate storedr");
        storedr = (Vector3 *)malloc(sizeof(Vector3)*_NP*allocmultiple);
        for(i=0;i<_NP;i++) storedr[i].clear();
    }

    /* remove/insert atoms */
    removenum=insertnum=0;
    if(fabs(b*px)>1e-8)
    {
        if((b*px)>0)
        {
            INFO("Need to remove atoms");
            removenum=0;
            dxc=b*px;
            for(i=0;i<_NP;i++)
            {
                ds=_SR[i]; ds-=sr0; ds.subint();
                dr=_H*ds; dx=dr*px; 
                if((dx>=-dxc*0.5)&&(dx<dxc*0.5))
                {
                    INFO_Printf("need remove atom %d\n",i);
                    fixed[i] = -1; /* flag for removal */
                    removenum++;
                }
            }
            INFO("need removenum="<<removenum);
        }
        else
        {
            INFO("Need to insert atoms");
            insertnum=0;
            dxc=b*px; dxc=dxc*(-1.0);
            for(i=0;i<_NP;i++)
            {
                ds=_SR[i]; ds-=sr0; ds.subint();
                dr=_H*ds; dx=dr*px; 
                if((dx>=-dxc*0.5)&&(dx<dxc*0.5))
                {
                    INFO_Printf("need replicate atom %d\n",i);
                    _SR[insertnum+_NP] = _SR[i];
                    _R[insertnum+_NP] = _R[i];
                    storedr[insertnum+_NP]=storedr[i];
                    insertnum++;
                }
            }
            INFO("need insertnum="<<insertnum);
        }
    }

#define getdu_single(SR) protect(\
                ds=SR; ds-=sr0; ds[zind]=0; \
                x=ds[xind]*pxpa+ds[yind]*pxpb;\
                y=ds[xind]*pypa+ds[yind]*pypb; r21=x*x+y*y;\
                theta[0]=atan2(-x,y);\
                pre=0.5/M_PI*(theta[0]);\
                du=b*pre; if((fabs(bx)>1e-8)||(fabs(by)>1e-8)){due.clear();\
                due-=px*(-bx*sin(theta[0]*2)*nua+by*(log(r21)*nub+cos(theta[0]*2)*nua));\
                due-=py*( by*sin(theta[0]*2)*nua-bx*(log(r21)*nub-cos(theta[0]*2)*nua));\
                /*due+=px*(-bx*sin(theta[1]*2)*nua+by*(log(r22)*nub+cos(theta[1]*2)*nua));\*/\
                /*due+=py*( by*sin(theta[1]*2)*nua-bx*(log(r22)*nub-cos(theta[1]*2)*nua));\*/\
                /*INFO("due="<<due);*/ du+=due;} \
                )
    
    SHtoR();
    for(i=0;i<_NP;i++)
    {
        getdr(_SR[i]);
        if(r>rrem) fixed[i]=-1;
        else if(r>rfix) fixed[i]=1;
        if(fixed[i]==-1) continue;
        st=_SR[i]; st[zind]=0; getdu_single(st);
        storedr[i]+=du;
        
        storedr[i]-=ux*_SR[i][xind];
        storedr[i]-=uy*_SR[i][yind];
        if(i==0) INFO("storedr[0]="<<storedr[0]);
    }
    INFO("originally "<<_NP<<" atoms");
    INFO("insert "<<insertnum<<" atoms");
    _NP+=insertnum;
    INFO("now "<<_NP<<" atoms");


    if(!store)
    {
        /* commit displacement field */
        SHtoR();
        for(i=0;i<_NP;i++)
        {
            _R[i]+=storedr[i];
        }
        RHtoS();
        free(storedr);
        storedr = NULL;

        /* commit removing atoms */
        n=0; SHtoR();
        removenum=0;
        for(i=0;i<_NP;i++)
        {
            if(fixed[i]!=-1)
            {
                _R0[n]=_R[i];
                fixed[n]=fixed[i];
                n++;
            }
            else
            {
                removenum++;
            }
        }
        INFO("originally "<<_NP<<" atoms");
        INFO("remove "<<removenum<<" atoms");
        _NP-=removenum;
        INFO("now "<<_NP<<" atoms");
        R0toR();  RHtoS();
        INFO("NP="<<_NP<<"  n="<<n);

    }
    MP_EndMasterOnly();
    setfixbufferatoms();
}

void MDFrame::makedisloop()
{
    /* Create Dislocation loop by using approximated
       isotropic linear elasticity solution under
       Periodic Boundary Condition with image summations */
    /*
      !!! Not fully implemented
          Not ready to use !!!
    */
    class Vector3 normal, sb, b, du, sr, rt; 
    double radius, height, V;
    int minx, maxx, miny, maxy, minz, maxz, i, j, k, m, n;
    int removenum, store;
    Matrix33 epsilon;
    static class Vector3 *storedr;
    static Matrix33 dH;
        
    MP_BeginMasterOnly();
    if(input[0]==0)
    {
        ERROR("makedisloop(): no dipole geometry set");
        return;
    }
    store = (int) input[16];
    
    /* read in parameters */
    normal.set(input[0],input[1],input[2]);
    normal/=normal.norm();
    INFO("normal="<<normal);
    radius=input[3];
    height=input[4];
    sb.set(input[5],input[6],input[7]);
    b=_H*sb;
    if(fabs(b.x)<1e-8) b.x=0;
    if(fabs(b.y)<1e-8) b.y=0;
    if(fabs(b.z)<1e-8) b.z=0;
    INFO("sb="<<sb<<"\nb="<<b);
    
    /* nu=input[8]; */
    minx=(int)input[9]; maxx=(int)input[10];
    miny=(int)input[11];maxy=(int)input[12];
    minz=(int)input[13];maxz=(int)input[14];
    
    
    if(storedr==NULL)
    {
        INFO("allocate storedr");
        storedr = (Vector3 *)malloc(sizeof(Vector3)*_NP*allocmultiple);
        for(i=0;i<_NP;i++) storedr[i].clear();
    }
    
    /* remove/insert atoms */
    removenum=0;
    if(fabs(b*normal)>1e-4)
    {
        INFO("b*normal="<<b*normal);
        if((b*normal)>0)
        {
            INFO("Need to remove atoms: not implemented!");
            return;
        }
        else
        {
            INFO("Need to insert atoms: not implemented!");
            return;
        }
    }   

    /* ignore c.c. image contributions for now
       (need to implement !!)
     */
    
    SHtoR();
    for(i=0;i<_NP;i++)
    {
        if(fixed[i]) continue;
        for(j=minx;j<=maxx;j++)
            for(k=miny;k<=maxy;k++)
                for(m=minz;m<=maxz;m++)
                {
                    /* calculate the displacement field */
                    sr=_SR[i]; sr.x+=j; sr.y+=k; sr.z+=m;
                    rt=_H*sr;
                    calloopdisp(&normal,radius,height,&b,&rt,&du);
//                    INFO("du="<<du);
                    storedr[i]+=du;
                }
        if(i==0) INFO("storedr[0]="<<storedr[0]);
    }

    /* ignore shift box for now */
    if(input[15])
    {
        INFO("shiftbox");
        epsilon.clear();
        V=_H.det();
        INFO("V="<<V);
        epsilon.addnvv
            (M_PI*radius*radius/(sqrt(3.0)*_H[0][0]*_H[0][0]),
             b,normal);
        INFO("epsilon="<<epsilon);
//        dH=epsilon*_H;
        dH=epsilon;
    }

    if(!store)
    {
        /* commit displacement field */
        SHtoR();
        for(i=0;i<_NP;i++)
        {
            _R[i]+=storedr[i];
        }
        RHtoS();
        free(storedr);
        storedr = NULL;

        /* commit removing atoms */
        n=0; SHtoR();
        removenum=0;
        for(i=0;i<_NP;i++)
        {
            if(fixed[i]!=-1)
            {
                _R0[n]=_R[i];
                fixed[n]=0;
                n++;
            }
            else
            {
                removenum++;
            }
        }
        INFO("originally "<<_NP<<" atoms");
        INFO("remove "<<removenum<<" atoms");
        _NP-=removenum;
        INFO("now "<<_NP<<" atoms");
        R0toR();  RHtoS();
        INFO("NP="<<_NP<<"  n="<<n);
        /* end of remove atoms */

        /* commit shift box */
        _H+=dH;

    }   
    MP_EndMasterOnly();
}

void MDFrame::calloopdisp
(Vector3 *normal,double radius,double height,
 Vector3 *b, Vector3 *rt,Vector3 *du)
{/* compute displacement field of a dislocation loop
    du = -b Omega / (4 pi)
    Omega = -\int r.dA/r^3
  */
    double hr, dx, x, y, z, r, r2, R, Omega;
    Vector3 rr;
    int Ns, i, j;
    /* reduced height, field point position, w.r.t. radius */
    hr=height/radius;
    rr=*rt; rr/=radius;
    z=rr*(*normal);
    r2=(rr.norm2()-z*z);
    if(r2>0) r=sqrt(r2);
    else r=0;
    
    /* subdivision number */
    if(r<1.0/5.5)
        Ns=5;
    else
        Ns=(int)(1.0/r-0.5);
    
    dx=2.0/(2*Ns+1);

    if(fabs(z*radius)<3)
    {
        if(r>1)
            Omega=0;
        else
            if(z>0)
                Omega=-2*M_PI;
            else
                Omega=2*M_PI;
        *du = *b;
        *du *= (-Omega/4/M_PI);
        return;
    }
    Omega=0;
    for(i=-Ns;i<=Ns;i++)
        for(j=-Ns;j<=Ns;j++)
        {
            /* Galerkin Points */
            x=dx*i;
            y=dx*j;
            if(x*x+y*y<=1)
            {
                R=sqrt((x-r)*(x-r)+y*y+z*z);
                Omega+=1./(R*R*R);
            }
        }        
    Omega*=(-z*dx*dx);
    *du = *b;
    *du *= (-Omega/4/M_PI);
    return;
}

void MDFrame::makegrainboundary()
{
    /* create grain boundary */
    int i,n,imgx,imgy;
    int gbtype;
    double sx0,sy0,p,q,shift[3], alpha;
    Matrix33 rot1, rot2, hnew, hinv;
    Vector3 r0, sr0, r1,dr,drr,s1,sr;
    
    gbtype = (int)input[0];
    sx0=input[1];
    sy0=input[2];
    p=input[3];
    q=input[4];
    shift[0]=input[5];
    shift[1]=input[6];
    shift[2]=input[7];
    
    MP_BeginMasterOnly();
    if(gbtype==0)
    {
        ERROR("makegb(): no gb geometry set");
        return;
    }

    alpha=atan2(p,q);
    rot1.set(cos(alpha),-sin(alpha),0,
             sin(alpha),cos(alpha),0,
             0,0,1);
    rot2.set(cos(alpha),sin(alpha),0,
             -sin(alpha),cos(alpha),0,
             0,0,1);
    hnew=_H;
    hnew[0][0]/=cos(alpha);
    hnew[0][1]/=cos(alpha);
    hnew[0][2]/=cos(alpha);
    hnew[1][0]/=cos(alpha);
    hnew[1][1]/=cos(alpha);
    hnew[1][2]/=cos(alpha);
    hinv=hnew.inv();
    sr0.set(sx0,sy0,0);
    r0=_H*sr0;
    n=0;
    for(i=0;i<_NP;i++)
    {
        {/* Crystal 1 */
            /* fit atoms back in the cell */
            for(imgx=-1;imgx<=1;imgx++)
                for(imgy=-1;imgy<=1;imgy++)
                {
                    sr=_SR[i]; sr.x+=imgx; sr.y+=imgy;
                    r1=_H*sr;
                    dr=r1-r0;
                    drr=rot1*dr;
                    _R[i]=r0+drr;
                    s1=hinv*_R[i];
                    if((s1.x>sx0)&&(s1.x<0.5)
                       &&(s1.y>=-0.5)&&(s1.y<0.5))
                    {
                        INFO("atom "<<i<<" "<<s1);
                        _R0[n]=s1;
                        n++;
                        if(n>_NP)
                            INFO("number of atoms "<<n<<" exceed "<<_NP);
                    }
                }
        }
        {/* Crystal 2 */
            /* fit atoms back in the cell */
            for(imgx=-1;imgx<=1;imgx++)
                for(imgy=-1;imgy<=1;imgy++)
                {
                    sr=_SR[i]; sr.x+=imgx; sr.y+=imgy;
                    r1=_H*sr;
                    dr=r1-r0;
                    drr=rot2*dr;
                    _R[i]=r0+drr;
                    s1=hinv*_R[i];
                    if((s1.x<=sx0)&&(s1.x>=-0.5)
                       &&(s1.y>=-0.5)&&(s1.y<0.5))
                    {
                        INFO("atom "<<i<<" "<<s1);
                        _R0[n]=s1;
                        n++;
                        if(n>_NP)
                            INFO("number of atoms "<<n<<" exceed "<<_NP);
                    }
                }
        }
    }
    INFO("makegb: old NP="<<_NP<<" new NP="<<n);
    _NP=n;
    for(i=0;i<_NP;i++)
    {
        _SR[i]=_R0[i];
        _R[i]=_H*_SR[i];
        _R0[i]=_R[i];
    }
    
    MP_EndMasterOnly();
}

void MDFrame::makewave()
{
    /* Give atom a displacment field in the form of a plane wave
     *
     * input = [ enable amplitude dr.x dr.y dr.z k.x k.y k.z vel ]
     *
     * enable:    if 1 then makewave is enabled
     * amplitude: maximum displacement of the atoms (scalar)
     * dr(x,y,z): vibration direction (will be noramlized by MD++)
     *            amplitude * dr is the maximum displacement vector
     * k(x,y,z):  plane wave vector
     * vel:       velocity of atoms (nonzero vel not implemented)
     */
    double amp;
    Vector3 dr, kvec;
    int i, vel;
    
    if(input[0]==0)
    {
        INFO_Printf("mkwavespec = [%f %f %f ]\n",
                    input[0],input[1],input[2]);
        ERROR("makewave(): no geometry set");
        return;
    }

    amp = input[1];
    dr.set(input[2],input[3],input[4]);
    dr/=dr.norm();
    dr*=amp;
    kvec.set(input[5],input[6],input[7]);
    vel = (int)input[8];

    SHtoR();
    for(i=0;i<_NP;i++)
    {
        _R[i]+=dr*cos(kvec*_SR[i]*2*M_PI);
    }
    RHtoS();
    if(vel!=0)
    {
        INFO_Printf("makewave(): vel=%d not implemented\n",vel);
        ERROR("makewave()");
    }
}


void MDFrame::maketorquehandle()
{
    /* create extra atoms to apply torque
       torquespec = [ dtheta dz ]
     */
    double dz;
    int i, n;

    MP_BeginMasterOnly();
    if(torquespec[1]==0)
    {
        INFO_Printf("torquespec = [%f %f]\n", torquespec[0], torquespec[1]);
        ERROR("maketorquehandle(): no geometry set");
        return;
    }
    dz=torquespec[1];

    INFO_Printf("NP=%d dz=%f\n",_NP,dz);
    n=0;
    for(i=0;i<_NP;i++)
    {
        if(_SR[i].z<dz-0.5)
        {
            _SR[_NP+n]=_SR[i];
            _SR[_NP+n].z+=1;
            fixed[_NP+n]=1;
            image[_NP+n]=i;
            n++;
        }
        if(_SR[i].z>0.5-dz)
        {
            _SR[_NP+n]=_SR[i];
            _SR[_NP+n].z-=1;
            fixed[_NP+n]=1;
            image[_NP+n]=i;
            n++;
        }
    }
    _NP+=n;
    
    for(i=0;i<_NP;i++)
        _SR[i].z=(_SR[i].z)/(1+4*dz);
    for(i=0;i<3;i++)
        _H[2][i]=_H[2][i]*(1+4*dz);

    INFO_Printf("NP=%d \n",_NP);
    
}


void MDFrame::addtorque()
{
    /* create extra atoms to apply torque
       torquespec = [ dtheta dz ]
     */
    Matrix33 hinv;
    Vector3 r, rnew;
    double dtheta, dz, theta, beta, cb, sb;
    int i;

    MP_BeginMasterOnly();
    dtheta=torquespec[0];
    dz=torquespec[1];
    theta=torquespec[2];
    theta+=dtheta;
    torquespec[2]=theta;
    
    if(dtheta==0)
    {
        INFO_Printf("torquespec = [%f %f]\n", torquespec[0], torquespec[1]);
        ERROR("addtorque(): dtheta==0");
        return;
    }

    hinv=_H.inv();
    for(i=0;i<_NP;i++)
    {        
        beta=dtheta*_SR[i].z*(1+4*dz);
        cb=cos(beta); sb=sin(beta);
        r=_H*_SR[i];
        rnew.x=r.x*cb-r.y*sb;
        rnew.y=r.x*sb+r.y*cb;
        rnew.z=r.z;
        _SR[i]=hinv*rnew;
    }
    
}



void MDFrame::scaleH()
{
    /* multiply H matrix by input[0] */
    INFO(_H);
     _H*=input[0];
    INFO(_H);
}
void MDFrame::saveH()
{
    /* copy H to H0 */
    INFO(_H);
    _H0=_H;
    INFO(_H);
}
void MDFrame::setH()
{
    /* input = [ i j c ]
       H[i][j] = c
    */
    int i,j;
    double h;
    i=(int)input[0];
    j=(int)input[1];
    h=input[2];
    _H[i][j]=h;
}
void MDFrame::restoreH()
{
    /* copy H0 to H */
    INFO(_H);
    _H=_H0;
    INFO(_H);
}






void MDFrame::shiftbox()
{ /*
   * input = [ i j delta ]
   *
   *   H[:][i--] += H[:][j--]*delta
   *   while keeping s fixed (affine transformation)
   */   
    int xind, yind;
    double frac;
    MP_BeginMasterOnly();
    if(input[0]==0)
    {
        ERROR("input(): no shift geometry set");
        return;
    }
    xind=(int)input[0]; //1x/2y/3z
    yind=(int)input[1];
    xind--;yind--;
    frac=input[2];
    
    INFO("The old H=\n"<<_H);
    
    _H[0][xind]+=_H[0][yind]*frac;
    _H[1][xind]+=_H[1][yind]*frac;
    _H[2][xind]+=_H[2][yind]*frac;
    
    INFO("The new H=\n"<<_H);
    SHtoR();
    MP_EndMasterOnly();
}

void MDFrame::redefinepbc()
{ /*
   * input = [ i j delta ]
   *
   *   H[:][i--] += H[:][j--]*delta
   *   while keeping r fixed (introduce displacement at box boundary)
   */   
    int xind, yind;
    double frac;
    MP_BeginMasterOnly();
    if(input[0]==0)
    {
        ERROR("input(): no shift geometry set");
        return;
    }
    xind=(int)input[0]; //1x/2y/3z
    yind=(int)input[1];
    xind--;yind--;
    frac=input[2];
    
    INFO("The old H=\n"<<_H);

    SHtoR();
    
    _H[0][xind]+=_H[0][yind]*frac;
    _H[1][xind]+=_H[1][yind]*frac;
    _H[2][xind]+=_H[2][yind]*frac;
    
    INFO("The new H=\n"<<_H);
    RHtoS();
    //for(i=0;i<_NP;i++)
    //    _SR[i].subint();
    MP_EndMasterOnly();
}

void MDFrame::applystrain()
{ /*
   * apply a strain to box H while keeping s fixed
   * (affine transformation)
   *
   * input = [ M11 M12 M13 M21 M22 M23 M31 M32 M33
               s11 s12 s13 s21 s22 s23 s31 s32 s33
               eps ]
   */
    class Matrix33 rot, rotv, strain, rstrain; double tmp;

    MP_BeginMasterOnly();
    rot.set(input);
    /* normalize rot matrix */
    tmp=sqrt(rot[0][0]*rot[0][0]+rot[0][1]*rot[0][1]+rot[0][2]*rot[0][2]);
    rot[0][0]/=tmp;rot[0][1]/=tmp;rot[0][2]/=tmp;
    tmp=sqrt(rot[1][0]*rot[1][0]+rot[1][1]*rot[1][1]+rot[1][2]*rot[1][2]);
    rot[1][0]/=tmp;rot[1][1]/=tmp;rot[1][2]/=tmp;
    tmp=sqrt(rot[2][0]*rot[2][0]+rot[2][1]*rot[2][1]+rot[2][2]*rot[2][2]);
    rot[2][0]/=tmp;rot[2][1]/=tmp;rot[2][2]/=tmp;
    rotv=rot.tran();
    
    strain.set(input+9);
    strain*=input[19];
    rstrain=rot*strain;
    rstrain=rstrain*rotv;

    INFO("applystrain rstrain="<<rstrain);
    _H0=_H;
    _H+=rstrain*_H;
    MP_EndMasterOnly();
}

void MDFrame::extendbox()
{ /*
   * input = [ i n ]
   * 
   * The i-th direction (i=1,2,3) of the box will be multiplied by 
   *  n times (by inserting more atoms)
   * Usually allocmultiple = n should be set before
   *  makecrystal or readcn is called before this function
   */
    int dir, n;
    int i,j;

    MP_BeginMasterOnly();
    dir=(int)input[0]; n=(int)input[1];
    if((dir<=0)||(dir>=4)) return;
    if(n<=1) return;
    dir--;

    for(i=0;i<_NP;i++)
    {
        for(j=1;j<n;j++)
        {
            _SR[i+_NP*j]=_SR[i];
            _SR[i+_NP*j][dir]+=j;
            fixed[i+_NP*j]=fixed[i];
            _EPOT_IND[i+_NP*j]=_EPOT_IND[i];
            _TOPOL[i+_NP*j]=_TOPOL[i];            
        }
    }
    for(i=0;i<_NP*n;i++)
    {
        _SR[i][dir]=(_SR[i][dir]+0.5)/n-0.5;
    }
    _H[0][dir]*=n;
    _H[1][dir]*=n;
    _H[2][dir]*=n;
    _NP*=n;
    SHtoR();
    INFO("extenbox: new NP="<<_NP);
    MP_EndMasterOnly();
}




void MDFrame::cutslice()
{ /* input = [ i n ]
   *
   * cut a slice (1/n) of the configuration along the i-th direction
   *   opposite of extendbox
   * note:  atoms in cn file has to be organized in n chunks,
   *   i.e. the result of a previous extendbox operation
   */
    int dir, n;
    int i;

    MP_BeginMasterOnly();
    dir=(int)input[0]; n=(int)input[1];
    if((dir<=0)||(dir>=4)) return;
    if(n<=1) return;
    dir--;

    INFO("dir = "<<dir<<"  n = "<<n);
    for(i=0;i<_NP/n;i++)
    {
        INFO_Printf("_SR:  %20.17e -> %20.17e\n",
                    _SR[i][dir],(_SR[i][dir]+0.5)*n-0.5);
        _SR[i][dir]=(_SR[i][dir]+0.5)*n-0.5;
    }
    _H[0][dir]/=n;
    _H[1][dir]/=n;
    _H[2][dir]/=n;
    _NP/=n;
    SHtoR();
    INFO("extenbox: new NP="<<_NP);
    MP_EndMasterOnly();
}

void MDFrame::splicecn()
{ /*
   * splice (put) two configuration files together along the i-th direction
   *
   * input = [ i w ]
   *
   * If w > 0.001, then the atoms within distance w to the interface will
   *  be interpolated between the two configurations to avoid a sharp
   *  interface.
   */
    
    int i, dir;
    Matrix33 h0; int np0; double ratio;
    Vector3 c,c0;
    double w;//blur width
    double alpha;

    INFO("splicecn");
    MP_BeginMasterOnly();
    dir=(int)input[0];
    w=input[1];
    if((dir<=0)||(dir>=4)) return;
    dir--;

    for(i=0;i<_NP;i++)
    {
//        _SR[i].subint();
        _R0[i]=_SR[i];
//        species0[i]=species[i];
    }
    h0=_H; np0=_NP;
    readcn(); 

    c.set(_H[0][dir],_H[1][dir],_H[2][dir]);
    c0.set(h0[0][dir],h0[1][dir],h0[2][dir]);
    ratio=c0.norm()/c.norm();
    
    for(i=0;i<_NP;i++)
    {
//        _SR[i].subint();
        _R0[i+np0]=_SR[i];
//        species0[i+np0]=species[i];
    }

    for(i=0;i<np0;i++)
    {
        if(w<0.001)
            alpha=1;
        else
        {
            if(_R0[i][dir]<(-0.5+w/2))
            {
                alpha=(0.5+_R0[i][dir])/w+0.5;
            }
            else if(_R0[i][dir]>(0.5-w/2))
            {
                alpha=(0.5-_R0[i][dir])/w+0.5;
            }
            else
                alpha=1;
        }
//        INFO_Printf("splicecn: i=%d alpha=%e\n",i,alpha);
        _SR[i]=_R0[i]*alpha+_R0[i+np0]*(1-alpha);
        _SR[i][dir]=(_R0[i][dir]+0.5)*ratio/(ratio+1)-0.5;
//        species[i]=species0[i];
    }
    for(i=np0;i<np0+_NP;i++)
    {
        if(w<0.001)
            alpha=1;
        else
        {
            if(_R0[i][dir]<(-0.5+w/2))
            {
                alpha=(0.5+_R0[i][dir])/w+0.5;
            }
            else if(_R0[i][dir]>(0.5-w/2))
            {
                alpha=(0.5-_R0[i][dir])/w+0.5;
            }
            else
                alpha=1;
        }
//        INFO_Printf("splicecn: i=%d alpha=%e\n",i,alpha);
        _SR[i]=_R0[i]*alpha+_R0[i-np0]*(1-alpha);
        _SR[i][dir]=(_R0[i][dir]-0.5)/(ratio+1)+0.5;
//        species[i]=species0[i];
    }
    
    _H[0][dir]=c[0]+c0[0];
    _H[1][dir]=c[1]+c0[1];
    _H[2][dir]=c[2]+c0[2];
    _NP+=np0;
    SHtoR();
    INFO("splicecn: new NP="<<_NP);
    MP_EndMasterOnly();
}

void MDFrame::cutpastecn()
{ /*
   * Replace all atoms whose i-th coordinates falls within [s0,s1]
   *  to those given in the incnfile.
   * Also displace the new atoms by ds in the i-th direction.
   *
   * Note: not finished, do not use!!
   *
   * input = [ i s0 s1 ds ]
   * incnfile = "filename"
   *
   */    
    int i,j,jpt,minj,dir,dirx,diry; double s0, s1, ds, mind, dis;
    Matrix33 h0; Vector3 sij;

    MP_BeginMasterOnly();
    dir=(int)input[0];
    if((dir<1)||(dir>3)) return;
    dir--; /* dir=0,1,2 for x,y,z */
    dirx=(dir+1)%3; diry=(dir+2)%3;
    s0=input[1];
    s1=input[2];
    ds=input[3];

    /* store the S of current config */
    for(i=0;i<_NP;i++)
    {
        _VSR[i]=_SR[i];
    }
    h0=_H;
    /* read in perfect lattice as reference */
    initcn.open(incnfile,LFile::O_Read);
    initcn.read(this);
    for(i=0;i<_NP;i++)
    {
        _R0[i]=_SR[i];
    }
    _H=h0;
    for(i=0;i<_NP;i++)
    {
        _SR[i]=_VSR[i];
    }
    for(i=0;i<_NP;i++)
    {
        if(fixed[i]) continue;
        if((_R0[i][dir]<s0)||(_R0[i][dir]>=s1)) continue;
        mind=100; minj=-1;
        for(j=0;j<nn[i];j++)
        {
            jpt=nindex[i][j];
            sij=_R0[i]-_R0[jpt]; sij[dir]-=ds; sij.subint();
            dis=sij.norm();
            if(dis<mind)
            {
                mind=dis;
                minj=jpt;
            }
        }
        if(mind>1e-4)
        {
            ERROR("cutpastecn: mind("<<mind<<") > 1e-4");
        }
        _SR[i][dirx]=_VSR[minj][dirx];
        _SR[i][diry]=_VSR[minj][diry];
        _SR[i][dir]=_VSR[minj][dir]+ds;
    }
    SHtoR();
    MP_EndMasterOnly();
}

void MDFrame::setconfig1()
{
    /* copy current configuration _SR to _SR1 */
    Free(_SR1);
    Realloc(_SR1,Vector3,_NP);
    memcpy(_SR1, _SR, sizeof(Vector3)*_NP);
}

void MDFrame::setconfig2()
{
    /* copy current configuration _SR to _SR2 */
    Free(_SR2);
    Realloc(_SR2,Vector3,_NP);
    memcpy(_SR2, _SR, sizeof(Vector3)*_NP);

    Free(_SR3);
    Realloc(_SR3,Vector3,_NP);
    memset(_SR3, 0, sizeof(Vector3)*_NP);
}

void MDFrame::switchconfig()
{
    /* _SR := _SR - SR1 + _SR2 */
    int i;
    if((_SR1!=NULL)&&(_SR2!=NULL))
    {
            for(i=0;i<_NP;i++)
            {
                    _SR[i]-=_SR1[i];
                    _SR[i]+=_SR2[i];
            }
    }
    else
    {
            ERROR("MDFrame::switchconfig _SR1 _SR2 not loaded");
    }
}

void MDFrame::replacefreeatom()
{
    int i;
    Matrix33 h0; 

    MP_BeginMasterOnly();
    for(i=0;i<_NP;i++)
    {
        _R0[i]=_SR[i];
    }
    h0=_H;
    readcn(); 

    for(i=0;i<_NP;i++)
    {
        if(fixed[i])
        {
            _SR[i]=_R0[i];
        }
    }
    _H=h0;

    SHtoR();
    MP_EndMasterOnly();
}

void MDFrame::relabelatom()
{
    /* input = [ n0 n1 ]
       switch the positions of atom n0 and atom n1 in memory array
    */
    int n0, n1;
    Vector3 s0;
    n0=(int)input[0];
    n1=(int)input[1];
    if(n0==n1) return;
    if((n0<0)||(n0>_NP)) {FATAL("relabelatom n0 out of range");}
    if((n1<0)||(n1>_NP)) {FATAL("relabelatom n0 out of range");}
    s0=_SR[n0];
    if(n1<n0)
        memmove(_SR+n1+1,_SR+n1,sizeof(Vector3)*(n0-n1));
    else
        memmove(_SR+n0,_SR+n0+1,sizeof(Vector3)*(n0-n1));
    _SR[n1]=s0;
}


void MDFrame::moveatom()
{ /*
   * Move specified atoms
   *
   * input = [ n dx dy dz i1 i2 ... in ]
   *
   * n:            number of atoms to be moved (n>0)
   * (dx,dy,dz):   displacement vector in real space
   * i1,i2,...,in: indices of atoms to be moved
   *
   */
    int i, n, id, fix;
    double mx, my, mz;
    double x0, y0, z0, r, r2, dmin, dmax;
    Vector3 s0, ds, dr;
    MP_BeginMasterOnly();

    n=(int)input[0];

    INFO_Printf("n=%d\n",n);

    if(n>0)
    {
        mx=input[1];
        my=input[2];
        mz=input[3];
        
        SHtoR();
        for(i=0;i<n;i++)
        {
            id=(int)input[4+i];
            _R[id].x+=mx;
            _R[id].y+=my;
            _R[id].z+=mz;
        }
        RHtoS();
        MP_EndMasterOnly();
    }
    else if(n==-2)
    {
        /* see above */
    }
    if(n==0)
    {
        INFO("move atoms in circular plate");
        x0=input[1];
        y0=input[2];
        z0=input[3];
        r =input[4];
        dmin=input[5];
        dmax=input[6];
        mx=input[7];
        my=input[8];
        mz=input[9];
        fix=(int)input[10];
        
        r2=r*r;
        s0.set(x0,y0,z0);
        for(i=0;i<_NP;i++)
        {            
            if((_SR[i].y>=dmax)||(_SR[i].y<dmin)) continue;
            if(fix)
            {
                fixed[i]=1;
                INFO_Printf("fix atom %d in plane\n",i);
            }
            ds=_SR[i]-s0;
            ds.subint();
            
            dr=_H*ds;
            if(dr.norm2()>r2) continue;
            
            _SR[i].x+=mx;
            _SR[i].y+=my;
            _SR[i].z+=mz;
            INFO_Printf("move atom %d\n",i);
        }        
        /* shift box */
        SHtoR();        
    }
    return;
}


void MDFrame::printatoms_in_sphere()
{ /* print the indices of all atoms in a sphere
   *
   * input = [ x0 y0 z0 r ]
   *
   * x0,y0,z0: center of the sphere
   * r:        radius of the sphere
   *
   */
    int i, n; double r2; Vector3 r0, s0, ds, dr; Matrix33 hinv;
    hinv=_H.inv();
    r0.set(input[0],input[1],input[2]);
    r2=input[3]*input[3];
    s0=hinv*r0;
    n=0;
    for(i=0;i<_NP;i++)
    {
         ds=_SR[i]-s0;
         ds.subint();
         dr=_H*ds;
         if(dr.norm2()<r2)
         {
             n++;
         }
    }
    INFO_Printf("\n%d   ",n);
    for(i=0;i<_NP;i++)
    {
         ds=_SR[i]-s0;
         ds.subint();
         dr=_H*ds;
         if(dr.norm2()<r2)
         {
                 INFO_Printf("%d ",i);
         }
    }
    INFO_Printf("\n");
}

void MDFrame::pbcshiftatom()
{
    int i;
    double dx,dy,dz;
    MP_BeginMasterOnly();
    dx=input[0];
    dy=input[1];
    dz=input[2];

    for(i=0;i<_NP;i++)
    {
        _SR[i].x+=dx;_SR[i].y+=dy;_SR[i].z+=dz;
        _SR[i].subint();
    }
    SHtoR();
    MP_EndMasterOnly();
}




void MDFrame::fixatoms_by_ID()
{ /* fix a set of atoms whose ID are specified
   *
   * input = [ n i1 i2 ... in ]
   *
   * n:             number of atoms to be fixed
   * i1,i2,...,in:  indices of atoms to be fixed
   */
    int i, n;
    MP_BeginMasterOnly();
    n=(int)input[0];
//    for(i=0;i<_NP;i++)
//    {
//        fixed[i]=0;
//    }
    for(i=1;i<=n;i++)
    {
        fixed[(int)input[i]]=1;
    }
    MP_EndMasterOnly();
}

void MDFrame::fixatoms_by_position()
{ /* fix all atoms whose position falls within a specified regime
   *
   * input = [ enable x0 x1 y0 y1 z0 z1 ]
   *
   * enable: set to 1 to activate
   * atoms whose reduced coordinate (x,y,z) satisfie
   *   x0 <= x < x1
   *   y0 <= y < y1
   *   z0 <= z < z1
   * will be fixed
   */
    int i, n, nfixed;
    double xmin, xmax, ymin, ymax, zmin, zmax;
    MP_BeginMasterOnly();
    n=(int)input[0];
    xmin=input[1];
    xmax=input[2];
    ymin=input[3];
    ymax=input[4];
    zmin=input[5];
    zmax=input[6];

    INFO_Printf("setfixedatoms = [\n");
    nfixed=0;
    for(i=0;i<_NP;i++)
    {
        if((_SR[i].x>=xmin)&&(_SR[i].x<xmax)
           &&(_SR[i].y>=ymin)&&(_SR[i].y<ymax)
           &&(_SR[i].z>=zmin)&&(_SR[i].z<zmax))
        {
            fixed[i]=1;
            nfixed++;
        }
    }
    INFO_Printf("%d\n",nfixed);
    for(i=0;i<_NP;i++)
    {
        if((_SR[i].x>=xmin)&&(_SR[i].x<xmax)
           &&(_SR[i].y>=ymin)&&(_SR[i].y<ymax)
           &&(_SR[i].z>=zmin)&&(_SR[i].z<zmax))
        {
            INFO_Printf("%d ",i);
        }
    }
    INFO_Printf("\n]\n");
    INFO_Printf("%d atoms fixed\n",nfixed);
    MP_EndMasterOnly();
}

void MDFrame::fixatoms_by_pos_topol()
{ /* fix all atoms whose position falls within a specified regime
   *
   * input = [ enable x0 x1 y0 y1 z0 z1 tmin tmax ]
   *
   * enable: set to 1 to activate
   * atoms whose reduced coordinate (x,y,z) satisfies
   *   x0 <= x < x1
   *   y0 <= y < y1
   *   z0 <= z < z1
   * and whose _TOPOL value satisfies
   *   tmin <= topol < tmax
   * will be fixed
   */
    int i, n, nfixed;
    double xmin, xmax, ymin, ymax, zmin, zmax, tmin, tmax;
    MP_BeginMasterOnly();
    n=(int)input[0];
    xmin=input[1];
    xmax=input[2];
    ymin=input[3];
    ymax=input[4];
    zmin=input[5];
    zmax=input[6];
    tmin=input[7];
    tmax=input[8];

    INFO_Printf("setfixedatoms = [\n");
    nfixed=0;
    for(i=0;i<_NP;i++)
    {
        //INFO_Printf("TOPOL[%d]=%f\n",i,_TOPOL[i]);
        if((_SR[i].x>=xmin)&&(_SR[i].x<xmax)
           &&(_SR[i].y>=ymin)&&(_SR[i].y<ymax)
           &&(_SR[i].z>=zmin)&&(_SR[i].z<zmax)
           &&(_TOPOL[i]>=tmin)&&(_TOPOL[i]<tmax))
        {
            fixed[i]=1;
            nfixed++;
        }
    }
    INFO_Printf("%d\n",nfixed);
    for(i=0;i<_NP;i++)
    {
        if((_SR[i].x>=xmin)&&(_SR[i].x<xmax)
           &&(_SR[i].y>=ymin)&&(_SR[i].y<ymax)
           &&(_SR[i].z>=zmin)&&(_SR[i].z<zmax)
           &&(_TOPOL[i]>=tmin)&&(_TOPOL[i]<tmax))
        {
            INFO_Printf("%d ",i);
        }
    }
    INFO_Printf("\n]\n");
    INFO_Printf("%d atoms fixed\n",nfixed);
    MP_EndMasterOnly();
}

void MDFrame::freeallatoms()
{
    int i;
    MP_BeginMasterOnly();

    for(i=0;i<_NP;i++)
    {
        fixed[i]=0;
    }
    MP_EndMasterOnly();
}

void MDFrame::reversefixedatoms()
{
    int i;
    MP_BeginMasterOnly();

    for(i=0;i<_NP;i++)
    {
        if(fixed[i]==0) fixed[i]=1;
        else if(fixed[i]==1) fixed[i]=0;
    }
    MP_EndMasterOnly();
}


void MDFrame::setfixedatomsspecies()
{ /* set the species value of all fixed atoms to input[0]
   */
    int i;
    for(i=0;i<_NP;i++)
    {
        if(fixed[i]==1)
            species[i]=(int)input[0];
    }
}
       
void MDFrame::setfixedatomsgroup()
{ /* set the group ID of all fixed atoms to input[0]
   */
    int i;
    for(i=0;i<_NP;i++)
    {
        if(fixed[i]==1)
            group[i]=(int)input[0];
    }
}
       
void MDFrame::reversespecies()
{ /* set the species value of all fixed atoms to input[0]
   */
    int i;
    for(i=0;i<_NP;i++)
    {
        if(species[i]==0)
            species[i]=1;
        else if(species[i]==1)
            species[i]=0;        
    }
}
       
void MDFrame::removefixedatoms()
{ /* remove all atom i if fixed[i] is not equal to zero */
    int i, j;
    
    MP_BeginMasterOnly();
    j=0;
    for(i=0;i<_NP;i++)
    {
        if(fixed[i]==0)
        {
            _SR[j]=_SR[i];
            fixed[j]=fixed[i];
            species[j]=species[i];
            _EPOT_IND[j]=_EPOT_IND[i];
            _TOPOL[j]=_TOPOL[i];
            j++;
        }
    }
    _NP=j;
    SHtoR();
    MP_EndMasterOnly();
}

void MDFrame::markremovefixedatoms()
{ /* if fixed[i]==1, change it to fixed[i]=-1 */
    int i;
    
    MP_BeginMasterOnly();
    for(i=0;i<_NP;i++)
    {
        if(fixed[i]==1)
        {
            fixed[i]=-1;
        }
    }
    MP_EndMasterOnly();
}

void MDFrame::movefixedatoms()
{ /* remove all atom i if fixed[i] is not equal to zero */
    int i;
    
    MP_BeginMasterOnly();
    for(i=0;i<_NP;i++)
    {
        if(fixed[i]==1)
        {
            _SR[i].x+=input[0];
            _SR[i].y+=input[1];
            _SR[i].z+=input[2];
        }
    }
    MP_EndMasterOnly();
}

void MDFrame::removeellipsoid()
{
    /* remove all atoms within an ellipsoid
     *
     * input = [ enable x0 y0 z0 a b c ]
     *
     * enable:     if 1 then removeellipsoid is enabled
     * (x0,y0,z0): center of the ellipsoid
     * a,b,c:      semi-axis of the ellipsoid
     */
    
    int i, n, removenum;
    double x0, y0, z0, arem, brem, crem;
    Vector3 r0, dr;
    
    MP_BeginMasterOnly();
    if(input[0]==0)
    {
        INFO_Printf("input = [%f %f %f ]\n",
                    input[0],input[1],input[2]);
        ERROR("removeellipsoid(): no geometry set");
        return;
    }

    /* read in parameters */
    x0=input[1];
    y0=input[2];
    z0=input[3];
    arem=input[4];
    brem=input[5];
    crem=input[6];
    
    r0.set(x0,y0,z0);
    /* remove all atoms outside rrem */
    
    SHtoR();
    for(i=0;i<_NP;i++)
    {
        dr = _R[i]-r0;
        dr.x/=arem;
        dr.y/=brem;
        dr.z/=crem;
        if(dr.norm2()<=1)
            fixed[i]=-1;
    }
    
    /* commit removing atoms */
    n=0; SHtoR();
    removenum=0;
    for(i=0;i<_NP;i++)
    {
        if(fixed[i]!=-1)
        {
            _R0[n]=_R[i];
            fixed[n]=fixed[i];
            n++;
        }
        else
        {
            removenum++;
        }
    }
    INFO("originally "<<_NP<<" atoms");
    INFO("remove "<<removenum<<" atoms");
    _NP-=removenum;
    INFO("now "<<_NP<<" atoms");
    R0toR();  RHtoS();
    INFO("NP="<<_NP<<"  n="<<n);

    MP_EndMasterOnly();
}

void MDFrame::removerectbox()
{ /* Remove a rectangular box of atoms
   *
   * input = [ ax ay az La bx by bz Lb cx cy cz Lc x0 y0 z0 plotonly outside ]
   *
   */
    int i, j, assist, plotonly, outside;
    Vector3 ds, dss, sa, sb, sc, s0;
    Vector3 ha, hb, hc;
    Matrix33 sh, shinv, latt, lattinv;
    
    MP_BeginMasterOnly();

    ha.set(latticesize[0][0],latticesize[0][1],latticesize[0][2]);
    hb.set(latticesize[1][0],latticesize[1][1],latticesize[1][2]);
    hc.set(latticesize[2][0],latticesize[2][1],latticesize[2][2]);
    ha*=latticesize[0][3];
    hb*=latticesize[1][3];
    hc*=latticesize[2][3];
    latt.setcol(ha,hb,hc);
    lattinv=latt.inv();
        
    sa.set(input[0],input[1],input[2]);
    sb.set(input[4],input[5],input[6]);
    sc.set(input[8],input[9],input[10]);
    sa*=input[3];
    sb*=input[7];
    sc*=input[11];
    s0.set(input[12],input[13],input[14]);
    assist=(int)input[15];
    plotonly=(int)input[16];
    outside=(int)input[17];

    sa=lattinv*sa;
    sb=lattinv*sb;
    sc=lattinv*sc;
    s0=lattinv*s0;
    
    INFO("assist="<<assist);
    INFO("plotonly="<<plotonly);
    
    sh.setcol(sa,sb,sc);
    shinv=sh.inv();
    INFO("vol="<<sh.det());
    INFO("s0="<<s0);
    
    for(i=0;i<_NP;i++)
    {
        ds=_SR[i]-s0;
        /*ds.subint();*/
        dss=shinv*ds;
        if(assist&&(!plotonly))
        {
            if((dss.x>=-0.5)&&(dss.x<0.5)&&
               (dss.y>=-0.5)&&(dss.y<0.5))
            {
                if(fabs(dss.z)<2)
                {
                    if((dss.z>=-0.5)&&(dss.z<0.5))
                    {
                        fixed[i]=-1;/* mark for deletion */
                    }
                    else if(dss.z>0.5)
                    {
                        dss.z=(dss.z-0.5)/(2-0.5)*(2-0.1)+0.1-dss.z;
                        dss.x=0; dss.y=0;
                        INFO("dss="<<dss);
                        _SR[i]+=sh*dss;
                    }
                    else
                    {
                        dss.z=(dss.z+0.5)/(-2+0.5)*(-2+0.1)-0.1-dss.z;
                        dss.x=0; dss.y=0;
                        INFO("dss="<<dss);
                        _SR[i]+=sh*dss;
                    }
                }
            }
        }
        else
        {
            if((dss.x>=-0.5)&&(dss.x<0.5)&&
               (dss.y>=-0.5)&&(dss.y<0.5)&&
               (dss.z>=-0.5)&&(dss.z<0.5))
            {
                fixed[i]=-1;/* mark for deletion */
            }
        }
    }
    if(outside)
    {
        for(i=0;i<_NP;i++)
        {
            if(fixed[i]==-1)
            {
                fixed[i]=0;
            }
            else if(fixed[i]==0)
            {
                fixed[i]=-1;
            }
        }
    }
    if(!plotonly)
    {
        j=0;
        for(i=0;i<_NP;i++)
        {
            if(fixed[i]!=-1)
            {
                _SR[j]=_SR[i];
                fixed[j]=0;
                if(_SAVEMEMORY<9)
                {
                        _EPOT_IND[j]=_EPOT_IND[i];
                        _TOPOL[j]=_TOPOL[i];
                }
                j++;
            }
        }
        INFO("removerectbox: "<<_NP-j<<" of atoms removed");
        _NP=j;
        INFO("new number of atoms "<<_NP);
        if(_SAVEMEMORY<9)
                SHtoR();
    }
    MP_EndMasterOnly();
}

void MDFrame::removeoverlapatoms()
{/* remove atoms that are closer than rc
  *
  * input = [ rc ]
  *
  */
    int i, j, jpt;
    double removerc, rc2;
    Vector3 ds, dr;

    refreshneighborlist();
    
    MP_BeginMasterOnly();

    removerc = input[0];
    
    rc2=removerc*removerc;
    for(i=0;i<_NP;i++)
    {
        for(j=0;j<nn[i];j++)
        {
            jpt=nindex[i][j];
            ds=_SR[i]-_SR[jpt];
            ds.subint();
            dr=_H*ds;
            if(dr.norm2()<rc2)
            {
                fixed[i]=-1;/* mark for deletion */
                if(fixed[jpt]==-1) fixed[i]=0;
            }
        }
    }
    j=0;
    for(i=0;i<_NP;i++)
    {
        if(fixed[i]!=-1)
        {
            _SR[j]=_SR[i];
            fixed[j]=0;
            j++;
        }
    }
    _NP=j;
    SHtoR();
    MP_EndMasterOnly();
}




/********************************************************************/
/* Neighbor list */
/********************************************************************/
void MDFrame::NbrList_reconstruct()
{
    /* use cell list combined with Verlet list */
    int ncx,ncy,ncz;
    int ncx0,ncx1,ncy0,ncy1,ncz0,ncz1,np0,np1;
    int i,j,k,n,i1,j1,k1,n1,i2,j2,k2;
    int ipt,jpt, itmp;
    double rsmax2;
    Vector3 s,sij,rij,h;
    int maxnic, maxnnm, m_nic, m_nnm;

    NbrList_init(_NP,_NNM);
    
    rsmax2=_RLIST*_RLIST;
    h=_H.height();

    MP_BeginMasterOnly();
    INFO("reconstruct neighborlist");
    DUMP("reconstruct_cell()  ");
    INFO("H="<<_H<<"  _RLIST="<<_RLIST);
    MP_EndMasterOnly();
    
    ncx=(int)floor(h[0]/((_RLIST)*1.05));if(ncx==0)ncx=1;
    ncy=(int)floor(h[1]/((_RLIST)*1.05));if(ncy==0)ncy=1;
    ncz=(int)floor(h[2]/((_RLIST)*1.05));if(ncz==0)ncz=1;
    
    NbrList_initcell(ncx,ncy,ncz,_NIC);
    
    /* Load distribution */
    if((ncx>=ncy)&&(ncx>=ncz))
    {
        ncx0=(int)rint(MP_cpu()*ncx/MP_ncpu());
        ncx1=(int)rint((MP_cpu()+1)*ncx/MP_ncpu());
        ncy0=0; ncy1=ncy; 
        ncz0=0; ncz1=ncz;
    }
    else if((ncy>=ncx)&&(ncy>=ncz))
    {
        ncy0=(int)rint(MP_cpu()*ncy/MP_ncpu());
        ncy1=(int)rint((MP_cpu()+1)*ncy/MP_ncpu());
        ncx0=0; ncx1=ncx; 
        ncz0=0; ncz1=ncz;
    }
    else 
    {
        ncz0=(int)rint(MP_cpu()*ncz/MP_ncpu());
        ncz1=(int)rint((MP_cpu()+1)*ncz/MP_ncpu());
        ncx0=0; ncx1=ncx; 
        ncy0=0; ncy1=ncy;
    }

    np0=0;
    np1=_NP;

    for(i=ncx0;i<ncx1;i++)
        for(j=ncy0;j<ncy1;j++)
            for(k=ncz0;k<ncz1;k++)
                celllist[i][j][k][0]=0;
    
    MP_Sync();

    m_nic = 0;
    for(ipt=0;ipt<_NP;ipt++)
    {
        s=_SR[ipt];
        s.subint();
        i=(int)floor((s.x+0.5)*ncx);i=(i+2*ncx)%ncx;
        j=(int)floor((s.y+0.5)*ncy);j=(j+2*ncy)%ncy;
        k=(int)floor((s.z+0.5)*ncz);k=(k+2*ncz)%ncz;
        if((i>=ncx0)&&(i<ncx1)&&(j>=ncy0)&&(j<ncy1)&&(k>=ncz0)&&(k<ncz1))
        {
            //INFO_Printf("NbrList_reconstruct: ipt=%d i=%d j=%d k=%d\n",ipt,i,j,k);
            celllist[i][j][k][0]++;
            if(celllist[i][j][k][0]>_NIC-1)
                FATAL("reconstruct(): too many atoms per cell "
                      <<celllist[i][j][k][0]<<" > limit("<<(_NIC-1)<<")"
                      <<"increase NIC in script");
            celllist[i][j][k][celllist[i][j][k][0]]=ipt;
            if(m_nic<celllist[i][j][k][0]) m_nic=celllist[i][j][k][0];
        }
    }
    maxnic=m_nic;
    
    DUMP("celllist constructed");
    MP_Sync();

    for(ipt=np0;ipt<np1;ipt++)
    {
        nn[ipt]=0;
        for(itmp=0;itmp<_NNM;itmp++)
            nindex[ipt][itmp]=0;
    }
    DUMP("list cleared");
    MP_Sync();

    m_nnm=0;
    for(i=ncx0;i<ncx1;i++)
        for(j=ncy0;j<ncy1;j++)
            for(k=ncz0;k<ncz1;k++)
                for(n=1;n<=celllist[i][j][k][0];n++)
                {
                    ipt=celllist[i][j][k][n];
                    //neighboring cell
                    for(i1=((ncx==1)?i:(i-1));i1<=((ncx<=2)?i:(i+1));i1++)
                        for(j1=((ncy==1)?j:(j-1));j1<=((ncy<=2)?j:(j+1));j1++)
                            for(k1=((ncz==1)?k:(k-1));k1<=((ncz<=2)?k:(k+1));k1++)
                            {
                                i2=(i1+2*ncx)%ncx;
                                j2=(j1+2*ncy)%ncy;
                                k2=(k1+2*ncz)%ncz;
                                //INFO_Printf("i2=%d j2=%d k2=%d\n",i2,j2,k2);
                                for(n1=1;n1<=celllist[i2][j2][k2][0];n1++)
                                {
                                    jpt=celllist[i2][j2][k2][n1];
                                    if(ipt==jpt) continue;
                                    sij=_SR[ipt]-_SR[jpt];
                                    sij.subint(); // subtract the nearest integer
                                    rij=_H*sij;
                                    if(rij.norm2()<rsmax2)
                                    {
                                        nindex[ipt][nn[ipt]]=jpt;
                                        nn[ipt]++;
                                        if(nn[ipt]>=_NNM)
                                            FATAL("reconstruct: index["<<ipt<<"] ("
                                                  <<nn[ipt]<<") >= NNM("<<_NNM<<")"
                                                  "increase NNM in script");
                                        if(m_nnm<nn[ipt]) m_nnm=nn[ipt];
                                    }
                                }
                            }
                }
    
    maxnnm=m_nnm;
    
    /* combine local lists to shm list */
    for(i=np0;i<np1;i++)
    {
        _R[i]=_H*_SR[i];
        _R0[i]=_R[i];
    }

    MP_BeginMasterOnly();
    INFO_Printf("reconstruct() finished. maxnic=%d maxnnm=%d\n",maxnic,maxnnm);
    MP_EndMasterOnly();
    MP_Sync();
}

void MDFrame::NbrList_print() 
{   /* debug use */
    int i;
    MP_BeginMasterOnly();
    fprintf(stderr,"rlist=%18.10f skin=%18.10f\n", _RLIST, _SKIN);
    for(i=0;i<10;i++)
    {
        fprintf(stderr,"nn(%d)=%d\n",i,nn[i]);
    }
    for(i=_NP-10;i<_NP;i++)
    {
        fprintf(stderr,"nn(%d)=%d\n",i,nn[i]);
    }
    MP_EndMasterOnly();
}

bool MDFrame::NbrList_needrefresh()
{
    /* all processes follow same procedure */
    double maxd;
    int i, need;
    if (firsttime)
    {
        firsttime=false;
        return true;
    }
    maxd=0;
    MP_BeginMasterOnly();
    SHtoR();
    for(i=0;i<_NP;i++)
    {
        maxd=max(fabs(_R[i].x-_R0[i].x),maxd);
        maxd=max(fabs(_R[i].y-_R0[i].y),maxd);
        maxd=max(fabs(_R[i].z-_R0[i].z),maxd);
    }
    MP_EndMasterOnly();
    need=(maxd>_SKIN);

    return (need!=0);
}

void MDFrame::NbrList_init(int mx,int mz)
{
    /* Multi process function */
    /* init two dimensional array */
    int shft1,shft2;//, mx, mz;
    int i;

    DUMP("initlist("<<mx<<","<<mz<<")");
    shft1=mx*mz*sizeof(int);
    shft2=mx*sizeof(int *);
    if(shft1+shft2==0) return;
    
    Realloc(nindex_mem,char,(shft1+shft2));

    nindex=(int **)(nindex_mem+shft1);

    MP_BeginMasterOnly();
    memset(nindex_mem,0,shft1+shft2);
    for(i=0;i<mx;i++)
    {
        nindex[i]=(int *)(nindex_mem+i*mz*sizeof(int));
    }
    MP_EndMasterOnly();

    Realloc(nn,int,mx);
}

void MDFrame::NbrList_initcell(int mx, int my, int mz, int mc)
{
    /* init two dimensional array */
    int shft1,shft2,shft3,shft4;
    int i, j, k;

//    INFO_Printf("MDFrame::NbrList_initcell(%d,%d,%d,%d)\n",mx,my,mz,mc);
//    freecelllist();
    shft1=mx*my*mz*mc*sizeof(int);
    shft2=mx*my*mz*sizeof(int *);
    shft3=mx*my*sizeof(int **);
    shft4=mx*sizeof(int ***);
    if(shft1+shft2+shft3+shft4==0) return;
    
    INFO("initcell("<<mx<<","<<my<<","<<mz<<","<<mc<<") cell list size "<<shft1+shft2+shft3+shft4);
    Realloc(cell_mem,char,(shft1+shft2+shft3+shft4));
    celllist=(int ****)(cell_mem+shft1+shft2+shft3);

    MP_BeginMasterOnly();
    memset(cell_mem,0,shft1+shft2+shft3+shft4);
    for(i=0;i<mx;i++)
    {
        celllist[i]=(int ***)(cell_mem+shft1+shft2+i*my*sizeof(int **));
        for(j=0;j<my;j++)
        {
            celllist[i][j]=(int **)(cell_mem+shft1+(i*my*mz+j*mz)*sizeof(int *));
            for(k=0;k<mz;k++)
            {
                celllist[i][j][k]=
                    (int *)(cell_mem+(i*my*mz*mc+j*mz*mc+k*mc)*sizeof(int));
            }
        }
    }
    MP_EndMasterOnly();
    MP_Sync();
//    INFO_Printf("MDFrame::NbrList_initcell done cell=%x\n",celllist);
//    INFO_Printf(" celllist[0][0][0][0] = %d\n",celllist[0][0][0][0]);
}

void MDFrame::NbrList_free()
{
    MP_Free(nindex_mem);
    nindex_mem=NULL;
    nn=NULL;
    nindex=NULL;
}

void MDFrame::NbrList_freecell()
{
    MP_Free(cell_mem);
    cell_mem=NULL;
    celllist=NULL;
}

void MDFrame::NbrList_refresh()
{
    if(NbrList_needrefresh()) NbrList_reconstruct();
}



/********************************************************************/
/* Numerical integrator */
/********************************************************************/

/* Gear 6 order predictor corrector Integrator */
void MDFrame::Gear6_step()
{
    Gear6_init();
    
    Gear6_predictor();
    Gear6_evaluator();
    Gear6_corrector();
}

void MDFrame::Gear6_init()
{
    /* Multi process function */
    if(npold==_NP) return;
    Realloc(s2,Vector3,_NP);
    Realloc(s3,Vector3,_NP);
    Realloc(s4,Vector3,_NP);
    Realloc(s5,Vector3,_NP);
    Realloc(s2real,Vector3,_NP);

    MP_BeginMasterOnly();
    memset(s2,0,sizeof(Vector3)*_NP);
    memset(s3,0,sizeof(Vector3)*_NP);
    memset(s4,0,sizeof(Vector3)*_NP);
    memset(s5,0,sizeof(Vector3)*_NP);
    memset(s2real,0,sizeof(Vector3)*_NP);
    MP_EndMasterOnly();

    npold=_NP;
    MP_Sync();
}

void MDFrame::Gear6_free()
{
    MP_Free(s2);
    MP_Free(s3);
    MP_Free(s4);
    MP_Free(s5);
}

void MDFrame::Gear6_predictor()
{
    int i,np0,np1;

    np0=0;
    np1=_NP;
    
    for(i=np0;i<np1;i++)
    {
        if(fixed[i]) continue;
        _SR[i]+=_VSR[i]+s2[i]+s3[i]+s4[i]+s5[i];
        _VSR[i]+=s2[i]*2.+s3[i]*3.+s4[i]*4.+s5[i]*5.;
        s2[i]+=s3[i]*3.+s4[i]*6.+s5[i]*10.;
        s3[i]+=s4[i]*4.+s5[i]*10.;
        s4[i]+=s5[i]*5.;
    }

    if(usenosehoover)
    {
        zeta+=zetav+zeta2+zeta3+zeta4+zeta5;
        zetav+=zeta2*2.+zeta3*3.+zeta4*4.+zeta5*5.;
        zeta2+=zeta3*3.+zeta4*6.+zeta5*10.;
        zeta3+=zeta4*4.+zeta5*10.;
        zeta4+=zeta5*5.;
    }
}

void MDFrame::Gear6_evaluator()
{
    int i, np0, np1;
    double tmp;
    Matrix33 hinv;

    np0=0;
    np1=_NP;

    /* multi process function */
    potential();
    calcprop();
    
    hinv=_H.inv();

    tmp=0.5*(_TIMESTEP)*(_TIMESTEP)*1e-24  //(ps^2)
        /(_ATOMMASS[0]*1e-3/AVO)*EV/1e-20;     //(A^2)
    
    for(i=np0;i<np1;i++)
    {
        if(fixed[i]) continue;
        if(species[i]==0)
            s2real[i]=hinv*_F[i]*tmp;
        else
            s2real[i]=hinv*_F[i]*tmp *_ATOMMASS[0]/_ATOMMASS[species[i]];
    }
    /* calculate property in each evaluator step */
    /* calcprop and nose hoover only for Master */
    MP_BeginMasterOnly();
    if(usenosehoover)
    {
        zetaa=vt2*
            (_T/_TDES-1)
            *_TIMESTEP*_TIMESTEP*1e-24*0.5;
        
        for(i=0;i<_NP;i++)
        {
            if(fixed[i]) continue;
            s2real[i]-=_VSR[i]*zetav*0.5;
        }
    }
    MP_EndMasterOnly();
    /*
        if (enablePR)
        h2real=??
    */
}

#define F02  (3./16.)
#define F12  (251./360.)
#define F32  (11./18.)
#define F42  (1./6.)
#define F52  (1./60.)

void MDFrame::Gear6_corrector()
{
    int i, np0, np1;
    Vector3 serr;
    double zetaerr;

    np0=0;
    np1=_NP;

    for(i=np0;i<np1;i++)
    {
        if(fixed[i]) continue;
        serr=s2[i]-s2real[i];
        _SR[i]-=serr*F02;
        _VSR[i]-=serr*F12;
        s2[i]-=serr;
        s3[i]-=serr*F32;
        s4[i]-=serr*F42;
        s5[i]-=serr*F52;
    }

    if(usenosehoover)
    {
        zetaerr=zeta2-zetaa;
        zeta-=zetaerr*F02;
        zetav-=zetaerr*F12;
        zeta2-=zetaerr;
        zeta3-=zetaerr*F32;
        zeta4-=zetaerr*F42;
        zeta5-=zetaerr*F52;
    }

    
    /* Parrinello-Rahman boundary condition not implemented in MD */
    /*
    if (enablePR)
    {
        herr=h2-h2real;
        _H-=herr*F02;
        _VH-=herr*F12;
        h2-=herr;
        h3-=herr*F32;
        h4-=herr*F42;
        h5-=herr*F52;
    }
    */
}


/* Velocity Verlet Integrator */
void MDFrame::VVerlet_step(int n)
{
    int i,np0,np1,maxiter;
    Matrix33 hinv; Vector3 dvs;
    double tmp, xinew, B, C;

    /* this line should not be here !! try get rid of it */
    VVerlet_init();

    /* Velocity Verlet algorithm */
    np0=0; np1=_NP;

  if(usenosehoover==0)
  {
    if(n>0)
    {
        /* s2[i] is the acceleration in previous time step (n) */
        for(i=np0;i<np1;i++)
        {
            if(fixed[i]) continue;
            _VSR[i]+=s2[i];        /* p(n+1/2) */     
            _SR[i]+=_VSR[i];       /* q(n+1) */
        }
    }
    /* _SR[i] is the position at current step,  q(n+1) */
    /* _VSR[i] is the velocity at half-step,    p(n+1/2) */

    /* compute acceleration a(n+1),  s2[i]=0.5*a*(dt)^2 */
    potential();
    calcprop();
    hinv=_H.inv();

    tmp=0.5*(_TIMESTEP)*(_TIMESTEP)*1e-24  //(ps^2)
        /(_ATOMMASS[0]*1e-3/AVO)*EV/1e-20;     //(A^2)
    for(i=np0;i<np1;i++)
    {
        if(fixed[i]) continue;
        if(species[i]==0)
            s2[i]=hinv*_F[i]*tmp;
        else
            s2[i]=hinv*_F[i]*tmp *_ATOMMASS[0]/_ATOMMASS[species[i]];
    }

    /* s2[i] is the acceleration at current step, 0.5*a(n+1)*(dt)^2 */
    if(n>0)
    {
        for(i=np0;i<np1;i++)
        {
            if(fixed[i]) continue;
            _VSR[i]+=s2[i];
        }
    }
    /* _VSR[i] is the velocity at current step, p(n+1) */
    
  }//if usenosehoover==0
  else if(usenosehoover==1) /* implicit method */
  { /* Bond, Leimkuhler, Laird, J. Comput. Phys. 151, 114-134, 1999.  Eq.(12) */
    if(n>0)
    {
        /* _T is the inst temperature at previous step, n */
        //INFO_Printf("_T 1 = %e\n\n",_T);
        zetaa=vt2* (_T/_TDES-1) *_TIMESTEP*_TIMESTEP*1e-24*0.5;
        /* zetaa = xi'(n) *(dt)^2 *0.5 */
        
        /* s2[i] is the acceleration in previous time step a(n)*(dt)^2*0.5 */
        for(i=np0;i<np1;i++)
        {
            if(fixed[i]) continue;
            dvs=_VSR[i]; dvs*=zetav*0.5; /* viscous force */
            _VSR[i]+=s2[i]; _VSR[i]-=dvs; /* p(n+1/2) */
            _SR[i]+=_VSR[i];              /* q(n+1) */
        }
        zetav+=zetaa;  /* zetav = xi(n+1/2) *(dt) */
        zeta +=zetav;  /* zeta  = eta(n+1) */
    }
    /* _SR[i] is the position at current step,  q(n+1) */
    /* _VSR[i] is the velocity at half-step,    p(n+1/2) */

    /* compute acceleration a(n+1),  s2[i]=0.5*a*(dt)^2 */
    potential();
    calcprop();
    hinv=_H.inv();

    tmp=0.5*(_TIMESTEP)*(_TIMESTEP)*1e-24      //(ps^2)
        /(_ATOMMASS[0]*1e-3/AVO)*EV/1e-20;     //(A^2)
    for(i=np0;i<np1;i++)
    {
        if(fixed[i]) continue;
        if(species[i]==0)
            s2[i]=hinv*_F[i]*tmp;
        else
            s2[i]=hinv*_F[i]*tmp *_ATOMMASS[0]/_ATOMMASS[species[i]];
    }
    /* s2[i] is the acceleration at current step, 0.5*a(n+1)*(dt)^2 */

    if(n>0)
    {
        for(i=np0;i<np1;i++)
        {
            if(fixed[i]) continue;
            _VSR[i]+=s2[i];
        }
        /* _VSR[i] is the velocity at current step with an unknown factor */
        /*    p(n+1) * (1 + xi(n+1)*dt/2) */
        /* need to solve for xi(n+1) */

        /* first compute instananeous temperature */
        tmp=0.5*(_ATOMMASS[0]*1.e-3/AVO)*1e-20 /(_TIMESTEP*_TIMESTEP*1e-24)/EV;
        _KATOM=0; n=0;
        for(i=0;i<_NP;i++)
        {
            if(fixed[i]) continue;
            n++;
            _VR[i]=_H*_VSR[i];
            if(species[i]==0)
                _KATOM+=tmp*_VR[i].norm2();
            else
                _KATOM+=tmp*_VR[i].norm2()*_ATOMMASS[species[i]]/_ATOMMASS[0];
        }
        /* _T=_KATOM*EV/(1.5*BOLZ*(n-1)); */
        _T=_KATOM*EV/(1.5*BOLZ*(n));
        
        /* xi(n+1) satisfies the equation:
         *
         *   xi(n+1) = xi(n+1/2) + vt2*(_T/_TDES/(1+0.5*xi(n+1))^2 - 1)*0.5
         *           = xi(n+1/2) + (B/(1+0.5*xi(n+1))^2 - 1)*C
         */
        maxiter = 1000;
        B = _T/_TDES;
        C = vt2*_TIMESTEP*_TIMESTEP*1e-24*0.5;
        xinew = zetav;
        for(i=0;i<maxiter;i++)
        {
            tmp = zetav + (B/(1+0.5*xinew)/(1+0.5*xinew) - 1)*C;
            if(fabs(tmp-xinew)<1e-14)
                break;
            else
                xinew = tmp;
        }
        
        if(i>=maxiter)
        {
            INFO_Printf("maxiter = %d xinew = %e %e\n",maxiter,xinew,tmp);
            ERROR("maximum iteration exceeded");
            zetav = xinew;        
        }
        else
        {
            //INFO_Printf("zetav_old = %e zetav = %e iter = %d\n",zetav,xinew,i);
            zetav = xinew;        
        }
        
        tmp = 1.0/(1+zetav*0.5);
        for(i=np0;i<np1;i++)
        {
            if(fixed[i]) continue;
            _VSR[i]*=tmp;
        }
        tmp = tmp*tmp; _KATOM *= tmp; _T *= tmp;
        //INFO_Printf("_T 0 = %e\n",_T);
        /* _VSR[i] is the velocity at current step,    p(n+1) */
    }// if n>0    
  }//if usenosehoover == 1
  else if(usenosehoover==2) /* explicit method */
  { /* Bond, Leimkuhler, Laird, J. Comput. Phys. 151, 114-134, 1999.  Eq.(13) */
    if(n>0)
    {
        /* s2[i] is the acceleration in previous time step a(n)*(dt)^2*0.5 */
        for(i=np0;i<np1;i++)
        {
            if(fixed[i]) continue;
            dvs=_VSR[i]; dvs*=zetav*0.5; /* viscous force */
            _VSR[i]+=s2[i]; _VSR[i]-=dvs; /* p(n+1/2) */
            _SR[i]+=_VSR[i];              /* q(n+1) */
        }

        /* compute instananeous temperature at half step*/
        tmp=0.5*(_ATOMMASS[0]*1.e-3/AVO)*1e-20 /(_TIMESTEP*_TIMESTEP*1e-24)/EV;
        _KATOM=0; n=0;
        for(i=0;i<_NP;i++)
        {
            if(fixed[i]) continue;
            n++;
            _VR[i]=_H*_VSR[i];
            if(species[i]==0)
                _KATOM+=tmp*_VR[i].norm2();
            else
                _KATOM+=tmp*_VR[i].norm2()*_ATOMMASS[species[i]]/_ATOMMASS[0];
        }
        /* _T=_KATOM*EV/(1.5*BOLZ*(n-1)); */
        _T=_KATOM*EV/(1.5*BOLZ*(n));

        zetaa=vt2* (_T/_TDES-1) *_TIMESTEP*_TIMESTEP*1e-24*0.5;
        /* zetaa = xi'(n) *(dt)^2 *0.5 */

        zeta +=zetav+zetaa;/* zeta  = eta(n+1) */
        zetav+=zetaa*2.0;  /* zetav = xi(n+1/2) *(dt) */
    }
    /* _SR[i] is the position at current step,  q(n+1) */
    /* _VSR[i] is the velocity at half-step,    p(n+1/2) */

    /* compute acceleration a(n+1),  s2[i]=0.5*a*(dt)^2 */
    potential();
    calcprop();
    hinv=_H.inv();

    tmp=0.5*(_TIMESTEP)*(_TIMESTEP)*1e-24      //(ps^2)
        /(_ATOMMASS[0]*1e-3/AVO)*EV/1e-20;     //(A^2)
    for(i=np0;i<np1;i++)
    {
        if(fixed[i]) continue;
        if(species[i]==0)
            s2[i]=hinv*_F[i]*tmp;
        else
            s2[i]=hinv*_F[i]*tmp *_ATOMMASS[0]/_ATOMMASS[species[i]];
    }
    /* s2[i] is the acceleration at current step, 0.5*a(n+1)*(dt)^2 */

    if(n>0)
    {
        for(i=np0;i<np1;i++)
        {
            if(fixed[i]) continue;
            dvs=_VSR[i]; dvs*=zetav*0.5; /* viscous force */
            _VSR[i]+=s2[i]; _VSR[i]-=dvs; /* p(n+1/2) */
        }
        /* _VSR[i] is the velocity at current step,    p(n+1) */
    }// if n>0    
  }//if usenosehoover == 2      
}

void MDFrame::VVerlet_init()
{
    /* Multi process function */
    if(npold==_NP) return;
    Realloc(s2,Vector3,_NP);

    MP_BeginMasterOnly();
    memset(s2,0,sizeof(Vector3)*_NP);
    MP_EndMasterOnly();

    npold=_NP;
    MP_Sync();
}

void MDFrame::VVerlet_free()
{
    MP_BeginMasterOnly();
    MP_Free(s2);
    MP_EndMasterOnly();
    MP_Sync();
}




/********************************************************************/
/* File input and output */
/********************************************************************/
    
int MDFrame::readcn()
{
    initcn.open(incnfile,LFile::O_Read);
    return initcn.read(this);
}

int MDFrame::readPOSCAR()
{
    initposcar.open(incnfile,LFile::O_Read);
    return initposcar.read(this);
}

int MDFrame::readMDCASK()
{
    initmdcaskcon.open(incnfile,LFile::O_Read);
    initmdcaskcon.read(this);
    initmdcaskinput.open("mold.in",LFile::O_Read);
    return initmdcaskinput.read(this);
}

int MDFrame::setfilecounter()
{
    intercn.setcount(filecounter);
    return 0;
}

int MDFrame::convertXDATCAR()
{
    int i, j;
    char *buffer; char *pp, *q;
    int np, nframe;
    char fname[200];

    strcpy(fname,incnfile);
    LFile::SubHomeDir(fname,fname);
    LFile::LoadToString(fname,buffer,0);

    pp=buffer;
    
    q=pp; pp=strchr(pp, '\n'); if(pp) *(char *)pp++=0;
    sscanf(q,"%d %d %d",&np, &j, &nframe);
    
    q=pp; pp=strchr(pp, '\n'); if(pp) *(char *)pp++=0;
    q=pp; pp=strchr(pp, '\n'); if(pp) *(char *)pp++=0;
    q=pp; pp=strchr(pp, '\n'); if(pp) *(char *)pp++=0;
    q=pp; pp=strchr(pp, '\n'); if(pp) *(char *)pp++=0;

    for(j=0;j<nframe;j++)
    {
        q=pp; pp=strchr(pp, '\n'); if(pp) *(char *)pp++=0;
        
        for(i=0;i<_NP;i++)
        {
            q=pp; pp=strchr(pp, '\n'); if(pp) *(char *)pp++=0;
            sscanf(q,"%lf %lf %lf",
                   &(_SR[i].x),&(_SR[i].y),&(_SR[i].z));
        }
        INFO("frame "<<j);
        writeintercnfile();
    }
    Free(buffer);
    return 0;
}


int MDFrame::writefinalcnfile(int zip, bool bg)
{
    finalcn.open(finalcnfile);
    return finalcn.write(this,zip,bg);
}

void MDFrame::saveintercn(int step)
{
    MP_BeginMasterOnly();
    if(savecn)
        if((step%savecnfreq)==0&&(step!=0))
            writeintercnfile();
    MP_EndMasterOnly();
}

int MDFrame::writeintercnfile(int zip,bool bg)
{
    return intercn.write(this,zip,bg);
}

int MDFrame::writePOSCAR()
{
    finalposcar.open(finalcnfile);
    return finalposcar.write(this);
}

int MDFrame::writeMDCASK()
{
    finalmdcaskcon.open(finalcnfile);
    finalmdcaskcon.write(this,0); /* no zip */
    finalmdcaskinput.open("mold.in");
    return finalmdcaskinput.write(this,0); /* no zip */
}

int MDFrame::openintercnfile()
{
    return intercn.open(intercnfile);
}

int MDFrame::openpropfile()
{
    return pf.open(outpropfile);
}

/* Configuration Files */
char * CNFile::describe()
{
    static char tmp[500];
    sprintf(tmp,"%s","Configuration File for Atom Positions, Format:\n"
            "sx1 sy1 sz1; ... ; sxn syn szn; H(3x3)");
    return tmp;
}

int CNFile::writeblock(void *p)
{
    int i;
    MDFrame &d=*((MDFrame *)p);
    f->Printf("%d\n",d._NP);
    for(i=0;i<d._NP;i++)
    {
        if (d.writeall)
            f->Printf("%25.17E %25.17E %25.17E %25.17E %25.17E %25.17E"
                      " %25.17E %2d %25.17E %2d\n",
                      d._SR[i].x,d._SR[i].y,d._SR[i].z,
                      d._VSR[i].x,d._VSR[i].y,d._VSR[i].z,
                      d._EPOT_IND[i],d.fixed[i],d._TOPOL[i],d.species[i]);
        else if (d.writevelocity)
            f->Printf("%25.17E %25.17E %25.17E %25.17E %25.17E %25.17E\n",
                      d._SR[i].x,d._SR[i].y,d._SR[i].z,
                      d._VSR[i].x,d._VSR[i].y,d._VSR[i].z);
        else
            f->Printf("%25.17E %25.17E %25.17E\n",
                  d._SR[i].x,d._SR[i].y,d._SR[i].z);
    }
    f->Printf("%25.17E %25.17E %25.17E\n"
              "%25.17E %25.17E %25.17E\n"
              "%25.17E %25.17E %25.17E\n",
              d._H[0][0],d._H[0][1],d._H[0][2],
              d._H[1][0],d._H[1][1],d._H[1][2],
              d._H[2][0],d._H[2][1],d._H[2][2]);
    f->Printf("%d ",d.nspecies);
    for(i=0;i<d.nspecies;i++) f->Printf("%s ",d.element[i]);
    f->Printf("\n");
    return 0;
}

int CNFile::readblock(void *p)
{
    int i;
    char *buffer, *pp, *q;
    MDFrame &d=*((MDFrame *)p);
    
    LFile::LoadToString(fname,buffer,0);

    pp=buffer;
    sscanf(pp, "%d", &d._NP);
    INFO("readblock: NP="<<d._NP);    
    
    d.Alloc();
    
    pp=strchr(pp, '\n');
    pp++;
    for(i=0;i<d._NP;i++)
    {
        q=pp;
        pp=strchr(pp,'\n');
        if(pp) *(char *)pp++=0;
        d._VSR[i].clear();
        d.fixed[i]=0;
        sscanf(q, "%lf %lf %lf %lf %lf %lf %lf %d %lf %d",
               &(d._SR[i].x),&(d._SR[i].y),&(d._SR[i].z),
               &(d._VSR[i].x),&(d._VSR[i].y),&(d._VSR[i].z),
               &(d._EPOT_IND[i]),&(d.fixed[i]),&(d._TOPOL[i]),&(d.species[i]));
//        d.fixed[i]=0;
//        INFO_Printf("species[%d]=%d\n",i,d.species[i]);
    }

//    INFO_Printf("reading H...\n");
    q=pp; pp=strchr(pp,'\n'); if(pp) *(char *)pp++=0;
    sscanf(q, "%lf %lf %lf",&d._H[0][0],&d._H[0][1],&d._H[0][2]);
    q=pp; pp=strchr(pp,'\n'); if(pp) *(char *)pp++=0;
    sscanf(q, "%lf %lf %lf",&d._H[1][0],&d._H[1][1],&d._H[1][2]);
    q=pp; pp=strchr(pp,'\n'); if(pp) *(char *)pp++=0;
    sscanf(q, "%lf %lf %lf",&d._H[2][0],&d._H[2][1],&d._H[2][2]);

    q=pp; pp=strchr(pp,'\n'); if(pp) *(char *)pp++=0;
//    INFO_Printf("reading species...\n");
    sscanf(q, "%d %s %s %s %s %s %s %s %s %s %s",
           &(d.nspecies),
           d.element[0],d.element[1],d.element[2],d.element[3],d.element[4],
           d.element[5],d.element[6],d.element[7],d.element[8],d.element[9]);
//    INFO_Printf("nspecies = %d %s %s\n",d.nspecies,d.element[0],d.element[1]);
    Free(buffer);
    DUMP("readblock finished");
    return 0;
}

/* Configuration Files in POSCAR (VASP) format */
char * POSCARFile::describe()
{
    static char tmp[500];
    sprintf(tmp,"%s","Ionic positions in POSCAR (VASP) format");
    return tmp;
}

int POSCARFile::writeblock(void *p)
{
    int i;
//    MDFrame &d=*((MDFrame *)p);
    MDFrame &d=*((MDFrame *)p);
    
    f->Printf("POSCAR by MD++ [%s]\n",d.dirname);
    f->Printf("   1.0\n");
    f->Printf(" %25.17e %25.17e %25.17e\n"
              " %25.17e %25.17e %25.17e\n"
              " %25.17e %25.17e %25.17e\n",
              d._H[0][0],d._H[1][0],d._H[2][0],
              d._H[0][1],d._H[1][1],d._H[2][1],
              d._H[0][2],d._H[1][2],d._H[2][2] );
    f->Printf("   %d\n",d._NP);
    f->Printf("direct  (relative coordinates s)\n");
    for(i=0;i<d._NP;i++)
    {
            f->Printf("%25.17e %25.17e %25.17e\n",
                  d._SR[i].x,d._SR[i].y,d._SR[i].z);
    }
        return 0;
}

int POSCARFile::readblock(void *p)
{
    int i;
    char *buffer; char *pp, *q, s[100]; 
    double scale;
    Matrix33 h, hinv;
    
    MDFrame &d=*((MDFrame *)p);
    
    LFile::LoadToString(fname,buffer,0);

    INFO("readPOSCAR:: read into string");
    /* skip first line */
    pp=buffer;
    pp=strchr(pp, '\n');
    pp++;

    q=pp;
    pp=strchr(pp,'\n');
    if(pp) *(char *)pp++=0;
//    printf("read line : %s\n",q);
    sscanf(q, "%lf", &scale);

    for(i=0;i<3;i++)
    {
        q=pp;
        pp=strchr(pp,'\n');
        if(pp) *(char *)pp++=0;
//        printf("read line : %s\n",q);
        sscanf(q, "%lf %lf %lf", &h[0][i], &h[1][i], &h[2][i]);
        h[0][i]*=scale;h[1][i]*=scale;h[2][i]*=scale;
    }
    
    q=pp;
    pp=strchr(pp,'\n');
    if(pp) *(char *)pp++=0;
    printf("read line : %s\n",q);
    sscanf(q, "%d", &d._NP);
    INFO("readblock: NP="<<d._NP);

    d.Alloc();
    d._H=h;
    INFO("readPOSCAR:: alloced");

    q=pp;
    pp=strchr(pp,'\n');
    if(pp) *(char *)pp++=0;
    sscanf(q, "%s", s);
    INFO("readblock: s="<<s);

    if((s[0]=='C')||(s[0]=='c')||(s[0]=='K')||(s[0]=='k'))
    {/* Cartesian */
        hinv = h.inv();
        for(i=0;i<d._NP;i++)
        {
            q=pp;
            pp=strchr(pp,'\n');
            if(pp) *(char *)pp++=0;
            sscanf(q, "%lf %lf %lf",
                   &(d._R[i].x),&(d._R[i].y),&(d._R[i].z));
            d._R[i]*=scale;
            d._SR[i]=hinv*d._R[i];
            d.fixed[i]=0;
        }
    }
    else
    {/* Direct or Reduced */
        for(i=0;i<d._NP;i++)
        {
            q=pp;
            pp=strchr(pp,'\n');
            if(pp) *(char *)pp++=0;
            sscanf(q, "%lf %lf %lf",
                   &(d._SR[i].x),&(d._SR[i].y),&(d._SR[i].z));
        }
    }
    Free(buffer);
    return 0;
}

/* Configuration Files in MDCASK (mdyn.con) format */
char * MDCASKconFile::describe()
{
    static char tmp[500];
    sprintf(tmp,"%s","Ionic positions in MDCASK (mdyn.con) format");
    return tmp;
}

int MDCASKconFile::writeblock(void *p)
{
    int i;
    MDFrame &d=*((MDFrame *)p);
//    f->Printf("      %d\n",d._NP);
    for(i=0;i<d._NP;i++)
    {
#if 0
        f->Printf("%8d %4d %13.6E %13.6E %13.6E %8.5E %13.6E %8.5E\n",
                      i+1,1,d._SR[i].x,d._SR[i].y,d._SR[i].z,
                      0.0,d._EPOT_IND[i],0.0);
#else /* RST format */
        f->Printf("%13.6E %13.6E %13.6E %13.6E %13.6E %13.6E %13.6E %13.6E %13.6E %13.6E %13.6E %13.6E %13.6E %13.6E %13.6E %13.6E\n",
                  d._SR[i].x,d._SR[i].y,d._SR[i].z,
                  0.0,0.0,0.0, 0.0,0.0,0.0, 0.0,0.0,0.0,
                  1.0,i+1.0,0.0,0.0);
        
#endif
        
    }
    return 0;
}

int MDCASKconFile::readblock(void *p)
{
    int i;
    char *buffer; char *pp, *q; 
    double tmp1, tmp2; int i1, i2;
    
    MDFrame &d=*((MDFrame *)p);
    
    LFile::LoadToString(fname,buffer,0);

    pp=buffer;
    sscanf(pp, "%d", &d._NP);
    INFO("readblock: NP="<<d._NP);
    
    d.Alloc();
    
    pp=strchr(pp, '\n');
    pp++;
    for(i=0;i<d._NP;i++)
    {
        q=pp;
        pp=strchr(pp,'\n');
        if(pp) *(char *)pp++=0;
        sscanf(q, "%d %d %lf %lf %lf %lf %lf %lf",
               &i1,&i2,&(d._SR[i].x),&(d._SR[i].y),&(d._SR[i].z),
               &tmp1,&(d._EPOT_IND[i]),&tmp2);
        d.fixed[i]=0;
    }
    Free(buffer);
    DUMP("readblock finished");
    return 0;
}
/* Input Files in MDCASK (mold.in) format */
char * MDCASKinputFile::describe()
{
    static char tmp[500];
    sprintf(tmp,"%s","Input File in MDCASK (mold.in) format");
    return tmp;
}

int MDCASKinputFile::writeblock(void *p)
{
    MDFrame &d=*((MDFrame *)p);

    /* This version only supports orthogonal cell */
    f->Printf("MOLDY - Input file by MD++ (orthongonal cell only)\n");
    f->Printf("EAM Cu Potential\n");

    /* only changing H matrix (diagonal part) */
    f->Printf("%10.6f %10.6f %10.6f\tALATT (IN ANGSTROMS) BLATT CLATT\n",
              d._H[0][0],d._H[1][1],d._H[2][2]);
    
    f->Printf(" .T  .T  .T             PBCX PBCY PBCZ\n");
    f->Printf(" .F                     LATOUT\n");
    f->Printf(" .F  0.0000000 0.00          Shift xshift yshift\n");
    
    if(strcasecmp(d.MDCASKpot,"TERSOFF")==0)
        f->Printf(" .T .F .F .F .F .F .F TERSOFF  ROCKETT  SW ZBL  EAM  EDIP MEAM\n");
    else if(strcasecmp(d.MDCASKpot,"ROCKETT")==0)
        f->Printf(" .F .T .F .F .F .F .F TERSOFF  ROCKETT  SW ZBL  EAM  EDIP MEAM\n");
    else if(strcasecmp(d.MDCASKpot,"SW")==0)
        f->Printf(" .F .F .T .F .F .F .F TERSOFF  ROCKETT  SW ZBL  EAM  EDIP MEAM\n");
    else if(strcasecmp(d.MDCASKpot,"ZBL")==0)
        f->Printf(" .F .F .F .T .F .F .F TERSOFF  ROCKETT  SW ZBL  EAM  EDIP MEAM\n");
    else if(strcasecmp(d.MDCASKpot,"EAM")==0)
        f->Printf(" .F .F .F .F .T .F .F TERSOFF  ROCKETT  SW ZBL  EAM  EDIP MEAM\n");
    else if(strcasecmp(d.MDCASKpot,"EDIP")==0)
        f->Printf(" .F .F .F .F .F .T .F TERSOFF  ROCKETT  SW ZBL  EAM  EDIP MEAM\n");
    else if(strcasecmp(d.MDCASKpot,"MEAM")==0)
        f->Printf(" .F .F .F .F .F .F .T TERSOFF  ROCKETT  SW ZBL  EAM  EDIP MEAM\n");
    
    f->Printf(" .F                     TWOCOMP\n");
    f->Printf(" .F   1000   0.8        TEMPCONT  NLANGEVIN  TEMPFACT  \n");
    f->Printf(" .F  .F  .F  10         ISOKBOT  ZTC XYTC  NTCLAYERS\n");
    f->Printf("  3.0e-14               BETAL(eV.S/A)\n");
    f->Printf(" .F  1 .F               FREESURF  NSURFLUS  DIMER(2X1) \n");
    f->Printf(" .F  1                  STLAY Nstatl\n");
    f->Printf(" .F  0  150.000         INTERFACE NTEMPLUS TEMPUP\n");
    f->Printf("2                       NELEM\n");
    f->Printf("283.0   283.0           MASS (Si) MASS (Si)\n");
    f->Printf("94.0     94.0           CHARGE(Si) CHARGE(Si) \n");
    f->Printf("0.0855     .F           ATOMIC DENSITY (atoms/A**3) IEL(T/F)\n");
    f->Printf("1.0E-15                 DELTA (TIMESTEP IN SECONDS)\n");
    f->Printf("0  0  0                 NST  NPR  NSC  \n");
    f->Printf("600.0001  0.0           TEMPRQ PRESS\n");
    f->Printf("1.0  0.0  0.0           B0\n"
              "0.0  1.0  0.0           B0\n"
              "0.0  0.0  1.0           B0\n");
    f->Printf("0 1                     Interstitial, Vacancy\n");
    f->Printf("1 0.0 0.0 0.0\n");
    f->Printf("1  1   100000 0 7   NSTEPS NPRINT NSCALE NIN IOU\n");
    f->Printf(" .T                     CONFIGFILE \n");
    f->Printf(" .F    4   2.75         XMOLNEIGHBORS NCOORD CUTOFFNEIGH\n");
    f->Printf(" .F                     DAMPIN\n");
    f->Printf(" .F                                        CLUSTER\n");
    f->Printf(" 1234.56                 SEED\n");
    f->Printf("0.0 178.0 47.5 4 1000000000  EPKA THETA PHIANG NPKA INPKA\n");
    f->Printf("0.00 0.00 0.00\n");
    f->Printf(" 63.55  2               PKAMASS  IDNPKA\n");
    f->Printf("    1 .F                NPARPKA REPEAT\n");
    f->Printf(".T                      ConjG\n");
    f->Printf("0.0008 500 0.1          GRADTL MAXFN DFPRED\n");
    f->Printf("0                       IQUEN\n");
    f->Printf("0.1e-14 0.5E-16 1.0 0.1 DTMAX DTMIN DEMAX DXMAX\n");
    f->Printf(" .F                     CHNGDT\n");
    f->Printf("  -1                     NCRYS \n");
    f->Printf("  Si     C\n");
    f->Printf("1830.8   471.18         AIJ   BIJ                     (Si)\n");
    f->Printf("2.4799   1.7322         XLIJ  XMUI                    (Si)\n");
    f->Printf("2.7      3.0            ARIJ  SIJ                     (Si)\n");
    f->Printf("1.1e-6   0.78734  100390.0  16.217  -0.59825   BETAI XNI CI DI H  (Si)\n");
    f->Printf("1393.6   346.7          AIJ   BIJ                     (C)\n");
    f->Printf("3.4879   2.2119         XLIJ  XMUI                    (C)\n");
    f->Printf("1.8      2.1            ARIJ  SIJ                     (C)\n");
    f->Printf("1.5724e-7 0.72751 38049.0 4.384  -0.57058   BETAI XNI CI DI H  (C)\n");
    f->Printf(" 3.25  0.2\n");
    f->Printf(" 1.7322\n");
    f->Printf(" 7.049556277  0.6022245584  1.80        ASI  BSI  ACUTSI\n");
    f->Printf(" 2.1685  2.0951  1.20  21.0             EPS  SIGM GAMSI  LAMBSI\n");
    f->Printf("-30.0 30.0 -30.0 30.0 -30.0 30.0 XMINP XMAXP YMINP YMAXP ZMINP ZMAXP\n");
    return 0;
}

int MDCASKinputFile::readblock(void *p)
{
    int i;
    char *buffer; char *pp, *q; 
    Matrix33 h;
    
    MDFrame &d=*((MDFrame *)p);
    
    LFile::LoadToString(fname,buffer,0);

    /* skip first line */
    pp=buffer;
    pp=strchr(pp, '\n');
    pp++;

    /* skip second line */
    pp=strchr(pp, '\n');
    pp++;

    q=pp;
    pp=strchr(pp, '\n');
    if(pp) *(char *) pp++=0;
    d._H.clear();
    sscanf(q, "%lf %lf %lf",
           &d._H[0][0],&d._H[1][1],&d._H[2][2]);
    INFO("_H="<<d._H);

    /* skip 18 lines */
    for(i=0;i<18;i++)
    {
        pp=strchr(pp, '\n');
        pp++;
    }

    /* read 3 lines */
    for(i=0;i<3;i++)
    {
        q=pp;
        pp=strchr(pp, '\n');
        if(pp) *(char *) pp++=0;
        sscanf(q, "%lf %lf %lf",&(h[i][0]),&(h[i][1]),&(h[i][2]));
    }
    INFO("h="<<h);
    /* renormalize sx,y,z */
    d._H[0][0]*=h[0][0];
    d._H[1][1]*=h[1][1];
    d._H[2][2]*=h[2][2];
    for(i=0;i<d._NP;i++)
    {
        d._SR[i].x/=h[0][0];
        d._SR[i].y/=h[1][1];
        d._SR[i].z/=h[2][2];
    }
    
    Free(buffer);
    DUMP("readblock finished");
    return 0;
}


int MDFrame::readMDCASKJAIME()
{
    int i;
    char *buffer; char *pp, *q; 
    int i1, i2;
    char fname[300];

    LFile::SubHomeDir(incnfile,fname);
    LFile::LoadToString(fname,buffer,0);

    pp=buffer;
    
    q=pp; pp=strchr(pp,'\n'); if(pp) *(char *)pp++=0;
    sscanf(q, "%d", &_NP);
    INFO("readMDCASKJAIME: NP="<<_NP);
    
    Alloc();
    
    q=pp; pp=strchr(pp,'\n'); if(pp) *(char *)pp++=0;
    for(i=0;i<_NP;i++)
    {
        q=pp; pp=strchr(pp,'\n'); if(pp) *(char *)pp++=0;
        sscanf(q, "%d %d %lf %lf %lf",
               &i1,&i2,&(_SR[i].z),&(_SR[i].y),&(_SR[i].x));
        _SR[i].z/=100.0;
        _SR[i].y/=-8.0;
        _SR[i].x/=12.0;
    }
    Free(buffer);
    DUMP("readMDCASKJAIME finished");
    return 0;
}

/* Output Property Files */
char * PropFile::describe()
{
    static char tmp[500];
    sprintf(tmp,"%s","Format:\n"
            "curstep TKinetic EPot Pressure");
    return tmp;
}

int PropFile::writeentry(void *p)
{
    char tmp[500];
    MDFrame &d=*((MDFrame *)p);
    sprintf(tmp,"%10d %16.10e %16.10e %16.10e "
            "%16.10e %16.10e %16.10e %16.10e %16.10e %20.16e %20.16e %20.16e\n",
            d.curstep,d._KATOM,d._EPOT,
            d._TOTSTRESS.trace()/3,d._TOTSTRESS[0][1],
            d._TOTSTRESS[1][2],d._TOTSTRESS[2][0],d._HELM,d._HELMP,
            d._T,d.zeta,d.dEdlambda);
    *f<<tmp;
    return 0;
}















/* Interface with Fortran codes */
void MDFrame::writefortraninifile(char *fname)
{
    FILE *fp;
    char extpath[200];

    INFO("MDFrame::writefortraninifile "<<fname);
    LFile::SubHomeDir(fortranpath,extpath);
    INFO("fortranpath "<<fortranpath<<" extpath "<<extpath<<" potfile"<<potfile);
    fp=fopen(fname,"w");
    if(fp==NULL)
    {
        FATAL("writefortraniinfile: open file failure");
    }
    fprintf(fp,"#1 Settings for F90 Relax BCC Code (generated by MD++)\n");
    fprintf(fp,"#2  Potential file name:  (Note that there is a Tab before the file name!\n");
    fprintf(fp,"\t%s/%s\n",extpath,potfile);
    fprintf(fp,"#4  Initial configuration file name(*.cn):\n");
    fprintf(fp,"\tinit.cn\n");
    fprintf(fp,"#6  Final output configuration file name (*.cn):\n");
    fprintf(fp,"\tfrelaxed.cn\n");
    fprintf(fp,"\n");
    fprintf(fp,"#9  Stress components:  (In GPa)\n");
    fprintf(fp,"#10 	pr(1,1), pr(2,2), pr(3,3):\n");
    fprintf(fp,"\t%E\t%E\t%E\n",
            _EXTSTRESS[0][0]*1e-3*_EXTSTRESSMUL+_EXTPRESSADD*1e-3,
            _EXTSTRESS[1][1]*1e-3*_EXTSTRESSMUL+_EXTPRESSADD*1e-3,
            _EXTSTRESS[2][2]*1e-3*_EXTSTRESSMUL+_EXTPRESSADD*1e-3);
    fprintf(fp,"#12	pr(1,2), pr(2,3), pr(3,1):\n");
    fprintf(fp,"\t%E\t%E\t%E\n",
            _EXTSTRESS[0][1]*1e-3*_EXTSTRESSMUL,
            _EXTSTRESS[1][2]*1e-3*_EXTSTRESSMUL,
            _EXTSTRESS[2][0]*1e-3*_EXTSTRESSMUL);
    fprintf(fp,"\n");
    fprintf(fp,"#15 Conjugate gradient search settings:\n");
    fprintf(fp,"#16 The first trial step in optimization (dfpred):\n");
    fprintf(fp,"\t%e\n",conj_dfpred);
    fprintf(fp,"#18 Tolerance on the residual gradient (gradtl / np):\n");
    fprintf(fp,"\t%e\n",conj_ftol);
    fprintf(fp,"#20 Max number of force calls (maxfn):\n");
    fprintf(fp,"\t%d\n",conj_fevalmax);
    fprintf(fp,"\n");
    fprintf(fp,"#23 Allow box vector to relax? ('1' for yes, '0' for no.)  \n");
    fprintf(fp,"\t%d\n",!conj_fixbox);
    fprintf(fp,"#25 Write the configuration sequence to files? ('1' for yes, '0' for no.)\n");
    fprintf(fp,"\t%d\n",savecn);
    fprintf(fp,"#27 If yes, frequency of saving configuration? (per how many timesteps)\n");
    fprintf(fp,"\t%d\n",savecnfreq);
    fprintf(fp,"\n");
    fprintf(fp,"#30 Set fixed atoms specification  (iffixed, sy0, sy1, sy2, sy3)\n");
    fprintf(fp,"\t0 0.0 0.0 0.0 0.0\n");
    fprintf(fp,"#32 CN format 1: sx,sy,sz, 2: sx,sy,sz, 3: sx,sy,sz,vx,vy,vz,pot,iffixed\n");
    if(writeall) fprintf(fp,"\t3\n");
    else if(writevelocity) fprintf(fp,"\t2\n");
    else fprintf(fp,"\t1\n");

    fclose(fp);
}

void MDFrame::fortranrelax()
{
    char extpath[200], comsav[200];

    LFile::SubHomeDir(fortranpath,extpath);
    
    writefortraninifile("Relax.ini");
    strcpy(comsav,command);

    sprintf(finalcnfile,"init.cn");
    writefinalcnfile(0);/* no zip */
    
    sprintf(command,"%s/%s",extpath,fortranexe);
    runcommand();

    sprintf(incnfile,"frelaxed.cn");
    readcn();

    /* disable background zip */
//    sprintf(command,"gzip -f frelaxed.cn &");
    sprintf(command,"gzip -f frelaxed.cn");
    runcommand();
    
//    sprintf(command,"gzip -f init.cn &");
    sprintf(command,"gzip -f init.cn");
    runcommand();

    strcpy(command,comsav);
}


/* interface with Li Ju's AtomEye viewer */
void MDFrame::writeatomeyecfgfile(char *fname)
{
    FILE *fp;
    int i, nplot, nr, n1, n2, n3, nn, ix, iy, iz, k;
    Matrix33 h;  Vector3 s;
    int nw, cind; double Emin, Emax;
    
    INFO("MDFrame::writeatomeyecfgfile "<<fname);
    
    switch(plot_color_axis){
    case(0): color_ind=_EPOT_IND; break;
    case(1): color_ind=_EPOT_IND; break;
    case(2): color_ind=_TOPOL; break;
    default: ERROR("plot() unknown coloraxis "<<plot_color_axis); return;
    }
    
    fp=fopen(fname,"w");
    if(fp==NULL)
    {
        FATAL("writeatomeyecfgfile: open file failure");
    }

    nplot = 0;
    nr = atomeyerepeat[0];
    if(nr!=0)
    {
        n1=atomeyerepeat[1];
        n2=atomeyerepeat[2];
        n3=atomeyerepeat[3];
        if(n1<=0) n1=1;
        if(n2<=0) n2=1;
        if(n3<=0) n3=1;
        nn = n1*n2*n3;
        INFO_Printf("atomeyerepeat ( %d %d %d )\n",
                    n1, n2, n3);
    }
    else
    {
        nn = n1 = n2 = n3 = 1;
    }

    nw = (int)plot_color_windows[0];
    for(i=0;i<_NP;i++)
    {
        if(nw>0)
        {
            if(plot_limits[0]==1)
                if((_SR[i].x<plot_limits[1])||(_SR[i].x>plot_limits[2])
                   ||(_SR[i].y<plot_limits[3])||(_SR[i].y>plot_limits[4])
                   ||(_SR[i].z<plot_limits[5])||(_SR[i].z>plot_limits[6]))
                {
                    continue;
                }
                for(k=0;k<nw;k++)
                {
                    Emin = plot_color_windows[k*3+1];
                    Emax = plot_color_windows[k*3+2];
                    cind = (int) plot_color_windows[k*3+3];

                    if((color_ind[i]>=Emin)&&
                       (color_ind[i]<=Emax))
                    {
                        nplot ++;
                        continue;    
                    }
                }
        }
        else
        {
            if(plot_limits[0]==1)
                if((_SR[i].x<plot_limits[1])||(_SR[i].x>plot_limits[2])
                   ||(_SR[i].y<plot_limits[3])||(_SR[i].y>plot_limits[4])
                   ||(_SR[i].z<plot_limits[5])||(_SR[i].z>plot_limits[6]))
                {
                    
                    continue;
                }        
            nplot ++;
        }
    }
    h=_H.tran();
    fprintf(fp,"Number of particles = %d\n",nplot*nn);
    fprintf(fp,"A = 1.0 Angstrom (basic length-scale)\n");
    fprintf(fp,"H0(1,1) = %f A\n",h[0][0]*n1);
    fprintf(fp,"H0(1,2) = %f A\n",h[0][1]*n1);
    fprintf(fp,"H0(1,3) = %f A\n",h[0][2]*n1);
    fprintf(fp,"H0(2,1) = %f A\n",h[1][0]*n2);
    fprintf(fp,"H0(2,2) = %f A\n",h[1][1]*n2);
    fprintf(fp,"H0(2,3) = %f A\n",h[1][2]*n2);
    fprintf(fp,"H0(3,1) = %f A\n",h[2][0]*n3);
    fprintf(fp,"H0(3,2) = %f A\n",h[2][1]*n3);
    fprintf(fp,"H0(3,3) = %f A\n",h[2][2]*n3);
#define ATOMEYE_EXTCFG
#ifdef ATOMEYE_EXTCFG
    fprintf(fp,".NO_VELOCITY.\n");
    fprintf(fp,"entry_count = 4\n");
    fprintf(fp,"auxiliary[0] = pote [eV]\n");
//    fprintf(fp,"1.00000\n");
//    fprintf(fp,"%s\n",element[0]);
    for(i=0;i<_NP;i++)
    {
        s=_SR[i];
        if(plot_map_pbc==1) s.subint();
        if(plot_limits[0]==1)
            if((_SR[i].x<plot_limits[1])||(_SR[i].x>plot_limits[2])
               ||(_SR[i].y<plot_limits[3])||(_SR[i].y>plot_limits[4])
               ||(_SR[i].z<plot_limits[5])||(_SR[i].z>plot_limits[6]))
            {
                continue;
            }
        
        if(nw>0)
        {
            for(k=0;k<nw;k++)
            {
                Emin = plot_color_windows[k*3+1];
                Emax = plot_color_windows[k*3+2];
                cind = (int) plot_color_windows[k*3+3];
                
                if((color_ind[i]>=Emin)&&
                   (color_ind[i]<=Emax))
                {
                    for(ix=0;ix<n1;ix++)
                        for(iy=0;iy<n2;iy++)
                            for(iz=0;iz<n3;iz++)
                            {
                                fprintf(fp,"%f\n%s\n %f %f %f %f  \n",
                                        _ATOMMASS[species[i]],element[species[i]],
                                        (s.x+0.5+ix)/n1,
                                        (s.y+0.5+iy)/n2,
                                        (s.z+0.5+iz)/n3,
                                        color_ind[i]);
                            }

                    continue;    
                }
            }
        }
        else
        {
            if(plot_limits[0]==1)
                if((_SR[i].x<plot_limits[1])||(_SR[i].x>plot_limits[2])
                   ||(_SR[i].y<plot_limits[3])||(_SR[i].y>plot_limits[4])
                   ||(_SR[i].z<plot_limits[5])||(_SR[i].z>plot_limits[6]))
                {
                    continue;
                }        
            for(ix=0;ix<n1;ix++)
                for(iy=0;iy<n2;iy++)
                    for(iz=0;iz<n3;iz++)
                    {
                        fprintf(fp,"%f\n%s\n%f %f %f %f  \n",
                                _ATOMMASS[species[i]],element[species[i]],                                
                                (s.x+0.5+ix)/n1,
                                (s.y+0.5+iy)/n2,
                                (s.z+0.5+iz)/n3,
                                color_ind[i]);
                    }
        }
    }        
#else
    for(i=0;i<_NP;i++)
    {
        for(ix=0;ix<n1;ix++)
            for(iy=0;iy<n2;iy++)
                for(iz=0;iz<n3;iz++)
                {
                    fprintf(fp,"%f %s %f %f %f %f %f %f\n",
                            _ATOMMASS[species[i]],element[species[i]],
                            (s.x+0.5+ix)/n1,
                            (s.y+0.5+iy)/n2,
                            (s.z+0.5+iz)/n3,
                            _VSR[i].x/n1,
                            _VSR[i].y/n2,
                            _VSR[i].z/n3);
                }
    }
#endif
    fclose(fp);
}

void MDFrame::convertCNtoCFG()
{
    int i, istart, iend;
    char cfgfname[100];
    
    istart = (int) input [0];
    iend   = (int) input [1];

    for(i=istart;i<=iend;i++)
    {
        if(strlen(incnfile)>=12)
        {
            sprintf(incnfile + strlen(incnfile)-12,"inter%04d.cn",i);
            strcpy(cfgfname,incnfile);
            sprintf(cfgfname + strlen(cfgfname)-12,"inter%04d.cfg",i);
        }
        else
        {
            sprintf(incnfile,"inter%04d.cn",i);
            sprintf(cfgfname,"inter%04d.cfg",i);
        }
        readcn();
        writeatomeyecfgfile(cfgfname);
    }
        
}


void MDFrame::writepovray(char *fname)
{
    FILE *fp;
    int i, nplot, nr, n1, n2, n3, nn, ix, iy, iz, k, ipt, jpt;
    Vector3 r, sr;
    int nw, cind; double Emin, Emax;
    
    INFO("MDFrame::writepovray "<<fname);
    
    switch(plot_color_axis){
    case(0): color_ind=_EPOT_IND; break;
    case(1): color_ind=_EPOT_IND; break;
    case(2): color_ind=_TOPOL; break;
    default: ERROR("plot() unknown coloraxis "<<plot_color_axis); return;
    }
    
    fp=fopen(fname,"w");
    if(fp==NULL)
    {
        FATAL("writepovray: open file failure");
    }

    nplot = 0;
    nr = atomeyerepeat[0];
    if(nr!=0)
    {
        n1=atomeyerepeat[1];
        n2=atomeyerepeat[2];
        n3=atomeyerepeat[3];
        if(n1<=0) n1=1;
        if(n2<=0) n2=1;
        if(n3<=0) n3=1;
        nn = n1*n2*n3;
        INFO_Printf("povraypeat ( %d %d %d )\n",
                    n1, n2, n3);
    }
    else
    {
        nn = n1 = n2 = n3 = 1;
    }

    fprintf(fp,"#include \"../viewpoint.pov\"\n");
    fprintf(fp,"\n");
    fprintf(fp,"union {\n");
    fprintf(fp,"  union {\n");
    fprintf(fp,"\n");
    
    nw = (int) plot_color_windows[0];
    for(i=0;i<_NP;i++)
    {
        if(plot_limits[0]==1)
            if((_SR[i].x<plot_limits[1])||(_SR[i].x>plot_limits[2])
               ||(_SR[i].y<plot_limits[3])||(_SR[i].y>plot_limits[4])
               ||(_SR[i].z<plot_limits[5])||(_SR[i].z>plot_limits[6]))
            {
                continue;
            }
        if(plot_limits[0]==2)
            if((_SR[i].x<plot_limits[1])||(_SR[i].x>plot_limits[2])
               ||(_SR[i].y<plot_limits[3])||(_SR[i].y>plot_limits[4])
               ||(_SR[i].z<plot_limits[5])||(_SR[i].z>plot_limits[6]))
            {
                    sr.set( (_SR[i].x)/n1,
                            (_SR[i].y)/n2,
                            (_SR[i].z)/n3 );
                    r = _H*sr;
                    fprintf(fp,"sphere { <%f, %f, %f>, radiusbc "
                            "pigment { color colorbc "
                            "transmit transmitbc } "
                            "finish { phong 1 metallic }"
                            " }\n",
//                            r.x, r.y, -r.z);
                            r.x, r.y, r.z);
                continue;
            }

        if(nw>0)
        {
            for(k=0;k<nw;k++)
            {
                Emin = plot_color_windows[k*3+1];
                Emax = plot_color_windows[k*3+2];
                cind = (int) plot_color_windows[k*3+3];
                
                if((color_ind[i]>=Emin)&&
                   (color_ind[i]<=Emax))
                {
                    for(ix=0;ix<n1;ix++)
                        for(iy=0;iy<n2;iy++)
                            for(iz=0;iz<n3;iz++)
                            {
                                sr.set( (_SR[i].x+ix)/n1,
                                        (_SR[i].y+iy)/n2,
                                        (_SR[i].z+iz)/n3 );
                                r = _H*sr;
                                fprintf(fp,"sphere { <%f, %f, %f>, radius%02d "
                                        "pigment { color color%02d "
                                        "transmit transmit%02d } "
                                        "finish { phong 1 metallic }"
                                        " }\n",
//                                        r.x, r.y, -r.z, cind, cind, cind);
                                        r.x, r.y, r.z, cind, cind, cind);
                                /* color_ind[i] */
                            }

                    continue;    
                }
            }
        }
        else
        {
            if(plot_limits[0]==1)
                if((_SR[i].x<plot_limits[1])||(_SR[i].x>plot_limits[2])
                   ||(_SR[i].y<plot_limits[3])||(_SR[i].y>plot_limits[4])
                   ||(_SR[i].z<plot_limits[5])||(_SR[i].z>plot_limits[6]))
                {
                    continue;
                }        
            for(ix=0;ix<n1;ix++)
                for(iy=0;iy<n2;iy++)
                    for(iz=0;iz<n3;iz++)
                    {
                        sr.set( (_SR[i].x+ix)/n1,
                                (_SR[i].y+iy)/n2,
                                (_SR[i].z+iz)/n3 );
                        r = _H*sr;
                        //INFO_Printf("species[%d]=%d\n",i,species[i]);
                        //INFO_Printf("fixed[%d]=%d\n",i,fixed[i]);
                                
                        fprintf(fp,"sphere { <%f, %f, %f>, radius%02d "
                                "pigment { color color%02d "
                                "transmit transmit%02d } "
                                "finish { phong 1 metallic }"
                                " }\n",
                                r.x, r.y, r.z,species[i],species[i],species[i]);
                        /* color_ind[i] */
                    }
        }
    }

    for(i=0;i<input[0];i++)
    {
        ipt=(int)input[2*i+1];
        jpt=(int)input[2*i+2];
        _R[ipt]=_H*_SR[ipt];
        _R[jpt]=_H*_SR[jpt];        
        fprintf(fp,"cylinder{ <%f,%f,%f>, <%f,%f,%f>, radiuscy "
                "pigment { color colorcy "
                "transmit transmitcy } "
                "finish { phong 1 metallic }"
                " }\n",
                _R[ipt].x,_R[ipt].y,_R[ipt].z,
                _R[jpt].x,_R[jpt].y,_R[jpt].z);
    }

    fprintf(fp,"\n");
    fprintf(fp,"\n");
    fprintf(fp,"  }\n");
    fprintf(fp,"  AdditionItems01\n");
    fprintf(fp,"  no_shadow\n");
    fprintf(fp,"  rotate rot01\n");
    fprintf(fp,"  rotate rot02\n");
    fprintf(fp,"  rotate rot03\n");
    fprintf(fp,"}\n");
    
    fclose(fp);
}

void MDFrame::atomeye()
{
    char extpath[200], comsav[200];

    LFile::SubHomeDir(atomeyepath,extpath);
    
    writeatomeyecfgfile("atomeye.cfg");
    strcpy(comsav,command);

#if 1 /* compress */
    sprintf(command,"gzip -f atomeye.cfg");
    runcommand();

    sprintf(command,"\"%s/%s\" atomeye.cfg.gz &",extpath,atomeyeexe);
    runcommand();
#else /* no compress */
    sprintf(command,"\"%s/%s\" atomeye.cfg &",extpath,atomeyeexe);
    runcommand();
#endif
    strcpy(command,comsav);
}




void MDFrame::writeENERGY(char *fname)
{
    FILE *fp;
    int i;
    
    INFO("MDFrame::writeENERGY "<<fname);

    switch(plot_color_axis){
    case(0): color_ind=_EPOT_IND; break;
    case(1): color_ind=_EPOT_IND; break;
    case(2): color_ind=_TOPOL; break;
    default: ERROR("writeENERGY() unknown coloraxis "<<plot_color_axis); return;
    }
    
    fp=fopen(fname,"w");
    if(fp==NULL)
    {
        FATAL("writeENERGY: open file failure");
    }

    for(i=0;i<_NP;i++)
    {
        fprintf(fp,"  %20.16e\n",color_ind[i]);
    }

    fclose(fp);
}

void MDFrame::writeFORCE(char *fname)
{
    FILE *fp;
    int i;
    
    INFO("MDFrame::writeFORCE "<<fname);

    fp=fopen(fname,"w");
    if(fp==NULL)
    {
        FATAL("writeFORCE: open file failure");
    }

    if(input[0]==1) INFO("only write forces on fixed atoms");
    for(i=0;i<_NP;i++)
    {
        if(input[0]==1)
        {
            if(fixed[i]!=0)
                fprintf(fp,"%6d  %20.12e %20.12e %20.12e %4d\n",
                        i,_F0[i].x,_F0[i].y,_F0[i].z,fixed[i]);
        }
        else
        {
            fprintf(fp,"%6d  %20.12e %20.12e %20.12e %4d\n",
                    i,_F[i].x,_F[i].y,_F[i].z,fixed[i]);
        }
    }
    fclose(fp);
}

void MDFrame::writePOSITION(char *fname)
{
    FILE *fp;
    int i;
    
    INFO("MDFrame::writePOSITION "<<fname);

    SHtoR();
    fp=fopen(fname,"w");
    if(fp==NULL)
    {
        FATAL("writePOSITION: open file failure");
    }

    if(input[0]==1) INFO("only write positions of fixed atoms");
    for(i=0;i<_NP;i++)
    {
        if(input[0]==1)
        {
            if(fixed[i]!=0)
                fprintf(fp,"%6d  %20.12e %20.12e %20.12e %4d\n",
                        i,_R[i].x,_R[i].y,_R[i].z,fixed[i]);
        }
        else
        {
            fprintf(fp,"%6d  %20.12e %20.12e %20.12e %4d\n",
                    i,_R[i].x,_R[i].y,_R[i].z,fixed[i]);
        }
    }
    fclose(fp);
}

void MDFrame::writeMDCASKXYZ(char *fname)
{
    FILE *fp;
    int i;
    char extname[200];
    Vector3 r;
    
    LFile::SubHomeDir(fname,extname);
    
    INFO("MDFrame::writeMDCASKXYZ "<<extname);

    fp=fopen(extname,"w");
    if(fp==NULL)
    {
        FATAL("writeMDCASKXYZ: open file failure");
    }

    fprintf(fp,"%d\n",_NP);
    if(_SAVEMEMORY==9)
    {
        for(i=0;i<_NP;i++)
        {
            r=_H*_SR[i];
            fprintf(fp,"%20.16e %20.16e %20.16e\n",
                    r.x,r.y,r.z);
        }
    }
    else
    {
        SHtoR();
        for(i=0;i<_NP;i++)
        {
            fprintf(fp,"%20.16e %20.16e %20.16e\n",
                    _R[i].x,_R[i].y,_R[i].z);
        }
    }
    fclose(fp);
}

void MDFrame::writeatomtv(char *fname)
{
    FILE *fp;
    int i, k, nw, cind, nplot;
    double Emin, Emax;
    char extname[200];
    
    LFile::SubHomeDir(fname,extname);
    
    INFO("MDFrame::writeatomtv "<<extname);

    fp=fopen(extname,"w");
    if(fp==NULL)
    {
        FATAL("writeatomtv: open file failure");
    }

    fprintf(fp,"%d\n",2);/* two frames */

    /* first one dummy: set the box */
    fprintf(fp,"           %d           0  \" box \"\n",8);
    /*
    fprintf(fp," %f %f %f %f %f\n",-_H[0][0]/2,-_H[1][1]/2,-_H[2][2]/2,0,0);
    fprintf(fp," %f %f %f %f %f\n",-_H[0][0]/2,-_H[1][1]/2, _H[2][2]/2,0,0);
    fprintf(fp," %f %f %f %f %f\n",-_H[0][0]/2, _H[1][1]/2,-_H[2][2]/2,0,0);
    fprintf(fp," %f %f %f %f %f\n",-_H[0][0]/2, _H[1][1]/2, _H[2][2]/2,0,0);
    fprintf(fp," %f %f %f %f %f\n", _H[0][0]/2,-_H[1][1]/2,-_H[2][2]/2,0,0);
    fprintf(fp," %f %f %f %f %f\n", _H[0][0]/2,-_H[1][1]/2, _H[2][2]/2,0,0);
    fprintf(fp," %f %f %f %f %f\n", _H[0][0]/2, _H[1][1]/2,-_H[2][2]/2,0,0);
    fprintf(fp," %f %f %f %f %f\n", _H[0][0]/2, _H[1][1]/2, _H[2][2]/2,0,0);
    */
    fprintf(fp," %lf %lf %lf %lf %lf\n",-_H[2][2]/2,-_H[1][1]/2,-_H[0][0]/2,0.,0.);
    fprintf(fp," %lf %lf %lf %lf %lf\n",-_H[2][2]/2,-_H[1][1]/2, _H[0][0]/2,0.,0.);
    fprintf(fp," %lf %lf %lf %lf %lf\n",-_H[2][2]/2, _H[1][1]/2,-_H[0][0]/2,0.,0.);
    fprintf(fp," %lf %lf %lf %lf %lf\n",-_H[2][2]/2, _H[1][1]/2, _H[0][0]/2,0.,0.);
    fprintf(fp," %lf %lf %lf %lf %lf\n", _H[2][2]/2,-_H[1][1]/2,-_H[0][0]/2,0.,0.);
    fprintf(fp," %lf %lf %lf %lf %lf\n", _H[2][2]/2,-_H[1][1]/2, _H[0][0]/2,0.,0.);
    fprintf(fp," %lf %lf %lf %lf %lf\n", _H[2][2]/2, _H[1][1]/2,-_H[0][0]/2,0.,0.);
    fprintf(fp," %lf %lf %lf %lf %lf\n", _H[2][2]/2, _H[1][1]/2, _H[0][0]/2,0.,0.);
    SHtoR();
    switch(plot_color_axis){
    case(0): color_ind=_EPOT_IND; break;
    case(1): color_ind=_EPOT_IND; break;
    case(2): color_ind=_TOPOL; break;
    default: ERROR("plot() unknown coloraxis "<<plot_color_axis); return;
    }
    nw = (int) plot_color_windows[0];
    nplot = 0;
    for(i=0;i<_NP;i++)
    {
       if(nw>0)
       {
           if(plot_limits[0]==1)
               if((_SR[i].x<plot_limits[1])||(_SR[i].x>plot_limits[2])
                  ||(_SR[i].y<plot_limits[3])||(_SR[i].y>plot_limits[4])
                  ||(_SR[i].z<plot_limits[5])||(_SR[i].z>plot_limits[6]))
              {
                      continue;
              }
          for(k=0;k<nw;k++)
          {
              Emin = plot_color_windows[k*3+1];
              Emax = plot_color_windows[k*3+2];
              cind = (int) plot_color_windows[k*3+3];
              
              if((color_ind[i]>=Emin)&&
                 (color_ind[i]<=Emax))
              {
                      nplot++;
                      continue;
              }
          }
       }
    }
    
    fprintf(fp,"           %d           0  \" real frame 1 \"\n",nplot);
    for(i=0;i<_NP;i++)
    {
       if(nw>0)
       {
           if(plot_limits[0]==1)
               if((_SR[i].x<plot_limits[1])||(_SR[i].x>plot_limits[2])
                  ||(_SR[i].y<plot_limits[3])||(_SR[i].y>plot_limits[4])
                  ||(_SR[i].z<plot_limits[5])||(_SR[i].z>plot_limits[6]))
              {
                      continue;
              }
           for(k=0;k<nw;k++)
           {
              Emin = plot_color_windows[k*3+1];
              Emax = plot_color_windows[k*3+2];
              cind = (int) plot_color_windows[k*3+3];
              
              if((color_ind[i]>=Emin)&&
                 (color_ind[i]<=Emax))
              {
                      fprintf(fp,"%f %f %f %f %f\n",
//                              _R[i].x,_R[i].y,_R[i].z,_EPOT_IND[i],_TOPOL[i]);
                              _R[i].z,_R[i].y,-_R[i].x,_EPOT_IND[i],_TOPOL[i]);
                      continue;    
              }
          }
       }
    }
    fclose(fp);
}

void MDFrame::readRASMOLXYZ(char *fname)
{
    int i;
    char *buffer; char *pp; char extname[200];

    LFile::SubHomeDir(fname,extname);
    
    LFile::LoadToString(extname,buffer,0);

    pp=buffer;
    sscanf(pp, "%d", &_NP);
    INFO("readRASMOLXYZ: NP="<<_NP);
    
    Alloc();
    
    pp=strchr(pp, '\n');    pp++;

    /* skip a line */
    pp=strchr(pp, '\n');    pp++;
    for(i=0;i<_NP;i++)
    {
        char *q;
        q=pp;
        pp=strchr(pp,'\n');
        if(pp) *(char *)pp++=0;
        _VSR[i].clear();
        fixed[i]=0;
        sscanf(q, "%s %lf %lf %lf %lf %lf",
               element[0],
               &(_R[i].x),&(_R[i].y),&(_R[i].z),
               &(_EPOT_IND[i]),&(_TOPOL[i]));
               
    }

    sscanf(pp, "%lf %lf %lf %lf %lf %lf %lf %lf %lf",
           &_H[0][0],&_H[0][1],&_H[0][2],
           &_H[1][0],&_H[1][1],&_H[1][2],
           &_H[2][0],&_H[2][1],&_H[2][2]);
    Free(buffer);
    RHtoS();
    INFO("readRASMOLXYZ finished");
}

void MDFrame::writeRASMOLXYZ(char *fname)
{
    FILE *fp;
    int i;
    char extname[200];

    LFile::SubHomeDir(fname,extname);
    
    INFO("MDFrame::writeRASMOLXYZ "<<extname);

    /*
    switch(plot_color_axis){
    case(0): color_ind=_EPOT_IND; break;
    case(1): color_ind=_EPOT_IND_AVG; break;
    case(2): color_ind=_TOPOL; break;
    default: ERROR("writeRASMOLXYZ() unknown coloraxis "<<plot_color_axis); return;
    }
    */
    
    fp=fopen(extname,"w");
    if(fp==NULL)
    {
        FATAL("writeRASMOLXYZ: open file failure");
    }

    fprintf(fp,"%d\n",_NP);
    fprintf(fp,"RASMOL XYZ file prepared by MD++\n");
    SHtoR();
    for(i=0;i<_NP;i++)
    {
        /*
        fprintf(fp,"%d %20.16e %20.16e %20.16e %20.16e\n",
                i,_R[i].x,_R[i].y,_R[i].z,color_ind[i]);
        */
        fprintf(fp,"%s  %20.16e %20.16e %20.16e  %20.16e %20.16e\n",
                element[0],_R[i].x,_R[i].y,_R[i].z,
                _EPOT_IND[i], _TOPOL[i]);
    }
    fprintf(fp," %20.16e %20.16e %20.16e\n"
            " %20.16e %20.16e %20.16e\n"
            " %20.16e %20.16e %20.16e\n",
            _H[0][0],_H[0][1],_H[0][2],
            _H[1][0],_H[1][1],_H[1][2],
            _H[2][0],_H[2][1],_H[2][2]);
            
    fclose(fp);
}

void MDFrame::writePINYMD(char *fname)
{
    FILE *fp;
    int i;
    char extname[200];

    LFile::SubHomeDir(fname,extname);
    
    INFO("MDFrame::writePINYMD "<<extname);

    fp=fopen(extname,"w");
    if(fp==NULL)
    {
        FATAL("writePINYMD: open file failure");
    }

    fprintf(fp,"natm_tot restart_typ itime pi_beads (from MD++)\n");
    fprintf(fp,"%d restart_all %d %d\n",_NP,totalsteps,1);
    fprintf(fp,"atm pos, atm_typ, mol_typ mol_num\n");
    SHtoR();
    for(i=0;i<_NP;i++)
    {
        fprintf(fp,"%20.16e %20.16e %20.16e a%s %s %s %d %d\n",
                _R[i].x,_R[i].y,_R[i].z,
                element[species[i]],
                element[species[i]],
                element[species[i]],
                i+1, 1);
    }
    fprintf(fp,"h matrix\n");
    fprintf(fp," %20.16e %20.16e %20.16e\n"
            " %20.16e %20.16e %20.16e\n"
            " %20.16e %20.16e %20.16e\n",
            _H[0][0],_H[0][1],_H[0][2],
            _H[1][0],_H[1][1],_H[1][2],
            _H[2][0],_H[2][1],_H[2][2]);
    fprintf(fp,"h matrix for Ewald setup\n");
    fprintf(fp," %20.16e %20.16e %20.16e\n"
            " %20.16e %20.16e %20.16e\n"
            " %20.16e %20.16e %20.16e\n",
            _H[0][0],_H[0][1],_H[0][2],
            _H[1][0],_H[1][1],_H[1][2],
            _H[2][0],_H[2][1],_H[2][2]);
    fprintf(fp,"1/3 log(Vol)\n%20.16e\n",log(_H.det())/3.0);

    fprintf(fp,"atm vel\n");
    for(i=0;i<_NP;i++)
    {
        fprintf(fp,"%20.16e %20.16e %20.16e\n",
                _VR[i].x,_VR[i].y,_VR[i].z);
    }
    fprintf(fp,"number of atm nhc, length of nhc\n%d %d\n",
            _NP,2);
    fprintf(fp,"atm nhc velocities\n");
    for(i=0;i<_NP;i++)
    {
        fprintf(fp,"%20.16e\n%20.16e\n",0.,0.);
    }
    fprintf(fp,"vol velocities\n");
    fprintf(fp," %20.16e %20.16e %20.16e\n"
            " %20.16e %20.16e %20.16e\n"
            " %20.16e %20.16e %20.16e\n",
            0.,0.,0.,0.,0.,0.,0.,0.,0.);
    fprintf(fp,"log(vol) velocity\n%20.16e\n",0.);
    fprintf(fp,"vol nhc velocities\n%20.16e\n%20.16e\n",0.,0.);
    
    fclose(fp);
}

void MDFrame::GnuPlotHistogram()
{ /* plot histogram of atom properties (e.g. energy, topology)
   * by calling Gnuplot
   *
   * input = [ E0 E1 n ]
   *
   * histogram from E0 to E1 with n bins
   */
    
    FILE *fp;
    int i, nbin, *counts, n;
    double Emin, Emax, *Ebin, dE;
    char comsav[200];
    
    INFO("MDFrame::GnuPlotEnergyHistogram");

    switch(plot_color_axis){
    case(0): color_ind=_EPOT_IND; break;
    case(1): color_ind=_EPOT_IND; break;
    case(2): color_ind=_TOPOL; break;
    default: ERROR("plot() unknown coloraxis "<<plot_color_axis); return;
    }
    
    /* determining plot specification */
    Emin = input[0];
    Emax = input[1];
    nbin = (int)input[2];

    if(Emin==Emax)
    {
        Emin=Emax=color_ind[0];
        for(i=1;i<_NP;i++)
        {
            if(Emin>color_ind[i]) Emin=color_ind[i];
            if(Emax<color_ind[i]) Emax=color_ind[i];
        }
    }
    if(fabs(Emin-Emax)<2e-8)
    {
        Emin-=1e-8;
        Emax+=1e-8;
    }
    if(nbin<=0) nbin=20;
    dE=(Emax-Emin)/nbin;
    
    /* constructing energy histogram */
    counts=(int    *)malloc(sizeof(double)*nbin);
    Ebin  =(double *)malloc(sizeof(double)*nbin);
    for(i=0;i<nbin;i++)
    {
        Ebin[i]=dE*(i+0.5)+Emin;
        counts[i]=0;
    }
    
    for(i=0;i<_NP;i++)
    {
        n=(int)floor((color_ind[i]-Emin)/dE);
        if((n>=0)&&(n<nbin))
            counts[n]++;
    }
    /* write energy into file */
    writeENERGY("eng.out");
    /* write energy histogram data into file */
    fp=fopen("enghist.out","w");
    if(fp==NULL)
    {
        FATAL("GnuPlotEnergyHistogram: open file failure");
    }

    for(i=0;i<nbin;i++)
    {
        fprintf(fp,"  %20.16e %d\n",Ebin[i],counts[i]);
    }
    fclose(fp);

    free(counts);
    free(Ebin);

    /* launch GnuPlot */
    /* write energy histogram data into file */
    fp=fopen("plotenghist.gp","w");
    if(fp==NULL)
    {
        FATAL("GnuPlotEnergyHistogram: open file failure");
    }
    fprintf(fp,"plot 'enghist.out' with histeps\n"); 
    fclose(fp);
    
    fp=fopen("gpw","w");
    if(fp==NULL)
    {
        FATAL("GnuPlotEnergyHistogram: open file failure");
    }
    fprintf(fp,
            "#!/bin/tcsh\n"
            "setenv LD_LIBRARY_PATH ~/Tools/Lib:\n"
            "module add gnuplot\n"
            "gnuplot plotenghist.gp -\n");
    fclose(fp);
    
    strcpy(comsav,command);
    sprintf(command,"chmod u+x gpw"); runcommand();
    sprintf(command,"xterm -fn 7x13 -sb -e ./gpw &\n");
    runcommand();
    strcpy(command,comsav);
}

















/********************************************************************/
/* Visualization */
/********************************************************************/

void MDFrame::openwin()
{
    MP_BeginMasterOnly();
    openwindow(win_width,win_height,dirname);
    MP_EndMasterOnly();
}

int MDFrame::openwindow(int w,int h,const char *n)
{
    if(win!=NULL)
        if(win->alive) return -1;
    MP_BeginMasterOnly();
    win=new YWindow(w,h,n,true,true,false);
    MP_EndMasterOnly();
    if(win==NULL) return -1;
    else
    {
        MP_BeginMasterOnly();
        win->setinterval(100);
        win->Run();
        MP_EndMasterOnly();
        return 0;
    }
}

void MDFrame::closewindow() {delete(win);}

void MDFrame::winplot()
{
    MP_BeginMasterOnly();
    if(win!=NULL)
        if(win->alive)
            if((curstep%plotfreq)==0)
            {
                plot();
            }
    MP_EndMasterOnly();
}

void MDFrame::winplot(int step)
{
    MP_BeginMasterOnly();
    if(win!=NULL)
        if(win->alive)
            if((step%plotfreq)==0)
            {
                plot();
            }
    MP_EndMasterOnly();
}        

void MDFrame::wintogglepause()
{
    if(win!=NULL)
        if(win->IsAlive())
            win->TogglePause();
}

#include "namecolor.c"
void MDFrame::alloccolors()
{
    int r,g,b;
    MP_BeginMasterOnly();
    if(win!=NULL)
    {
        if(win->alive)
        {
            for(int i=0;i<MAXCOLORS;i++)
            {
                Str2RGB(colornames[i],&r,&g,&b);
                colors[i]=win->AllocShortRGBColor(r,g,b);
            }
            
            Str2RGB(atomcolor[0],&r,&g,&b);
            colors[MAXCOLORS+0]=win->AllocShortRGBColor(r,g,b);
            Str2RGB(bondcolor,&r,&g,&b);
            colors[MAXCOLORS+1]=win->AllocShortRGBColor(r,g,b);
            Str2RGB(highlightcolor,&r,&g,&b);
            colors[MAXCOLORS+2]=win->AllocShortRGBColor(r,g,b);
            Str2RGB(fixatomcolor,&r,&g,&b);
            colors[MAXCOLORS+3]=win->AllocShortRGBColor(r,g,b);
            Str2RGB(backgroundcolor,&r,&g,&b);
            win->bgcolor=win->AllocShortRGBColor(r,g,b);

            for(int i=0;i<MAXSPECIES;i++)
            {
                Str2RGB(atomcolor[i],&r,&g,&b);
                colors[MAXCOLORS+5+i]=win->AllocShortRGBColor(r,g,b);
            }
        }
        else
        {
            WARNING("No window to allocate color for!");
        }
    }
    else
    {
        WARNING("No window to allocate color for!");
    }
    MP_EndMasterOnly();
}
void MDFrame::alloccolorsX()
{
    MP_BeginMasterOnly();
    if(win!=NULL)
    {
        if(win->alive)
        {
            colors[0]=win->AllocNamedColor(atomcolor[0]);
            colors[1]=win->AllocNamedColor(bondcolor);
            colors[2]=win->AllocNamedColor(highlightcolor);
            colors[3]=win->AllocNamedColor(fixatomcolor);
            win->bgcolor=win->AllocNamedColor(backgroundcolor);
        }
        else
        {
            WARNING("No window to allocate color for!");
        }
    }
    else
    {
        WARNING("No window to allocate color for!");
    }
    MP_EndMasterOnly();
}

void MDFrame::rotate()
{
    MP_BeginMasterOnly();
    if(win!=NULL)
    {
        if(win->alive)
        {
            win->horizontalRot(rotateangles[0]*M_PI/180);
            win->verticalRot(rotateangles[1]*M_PI/180);
            win->spinRot(rotateangles[2]*M_PI/180);
            if(rotateangles[3]!=0) win->zoom(rotateangles[3]);
            if(rotateangles[4]!=0) win->project(rotateangles[4]);
        }
        else
        {
            WARNING("No window to rotate for!");
        }
    }
    else
    {
        WARNING("No window to rotate for!");
    }
    MP_EndMasterOnly();
}

void MDFrame::saverot()
{
    MP_BeginMasterOnly();
    if(win!=NULL)
    {
        if(win->alive)
        {
            //win->saveRot(); win->saveScale();
            win->saveView();
        }
        else
        {
            WARNING("No window to rotate for!");
        }
    }
    else
    {
        WARNING("No window to rotate for!");
    }
    MP_EndMasterOnly();
}

#include "colormap.h"
  
void MDFrame::plot()
{
    int i,jp,j,k;
    double L,r2;
    char s[100]; bool high;
    double x1,y1,z1,x2,y2,z2,dx,dy,dz,dr;
    int r,g,b; double alpha; unsigned ce;
    Vector3 s1, s2, vr1, vr2, sri, srj, ri, rj;
    int nw, cind, show; double Emin, Emax;

    MP_BeginMasterOnly();

    if(win==NULL) return;
    if(!(win->alive)) return;
    
    L=max(_H[0][0],_H[1][1]);
    L=max(L,_H[2][2])*.5;

    switch(plot_color_axis){
     case(0): color_ind=_EPOT_IND; break;
     case(1): color_ind=_EPOT_IND; break;
     case(2): color_ind=_TOPOL; break;
     default: ERROR("plot() unknown coloraxis "<<plot_color_axis); return;
    }
    
    SHtoR();
    win->Lock();
    win->Clear();
    
    /* draw atom s*/
    for(i=0;i<_NP;i++)
    {
        sri=_SR[i];
        if(plot_map_pbc==1) sri.subint();
        ri = _H*sri;
        
        /* if plot_limits[0] equals to
           0: then this option is ignored
           1: then only plot atoms falling within the limits
              given by plot_limits[1~6]
           2: then atoms falling within the limits will be plotted
              with the same color as the fixed atoms
        */
        if(plot_limits[0]==1)
            if((sri.x<plot_limits[1])||(sri.x>plot_limits[2])
               ||(sri.y<plot_limits[3])||(sri.y>plot_limits[4])
               ||(sri.z<plot_limits[5])||(sri.z>plot_limits[6]))
            {
                continue;
            }
        if(plot_limits[0]==2)
            if((sri.x<plot_limits[1])||(sri.x>plot_limits[2])
               ||(sri.y<plot_limits[3])||(sri.y>plot_limits[4])
               ||(sri.z<plot_limits[5])||(sri.z>plot_limits[6]))
            {
                win->DrawPoint(ri.x/L,ri.y/L,ri.z/L,
                               atomradius[species[i]]/L,colors[MAXCOLORS+3],s,2);
                continue;
            }
        
        if (!plot_atom_info) s[0]=0;
        else
        {   /* when an atom is clicked */
            if(plot_atom_info==1)    /* print scaled coordinates of the atom */
                sprintf(s,"%d,%6.4f,%6.4f,%6.4f",
                        i,sri.x,sri.y,sri.z);
            else if(plot_atom_info==2) /* print real coordinates of the atom */
                sprintf(s,"%d,%6.4f,%6.4f,%6.4f",
                        i,ri.x,ri.y,ri.z);
            else if(plot_atom_info==3) /* print color_ind, fixed, species info of the atom */
                sprintf(s,"%d,%8.6e %d %d",
                        i,color_ind[i], fixed[i], species[i]);
            else if(plot_atom_info==4) /* print force on the atom */
                sprintf(s,"%d,%6.4f,%6.4f,%6.4f",
                        i,_F[i].x,_F[i].y,_F[i].z);
            else
                s[0]=0;
        }
        
        nw = (int)plot_color_windows[0];
        if(nw>0)
        {
            for(k=0;k<nw;k++)
            {
                Emin = plot_color_windows[k*3+1];
                Emax = plot_color_windows[k*3+2];
                cind = (int) plot_color_windows[k*3+3];

                if((color_ind[i]>=Emin)&&(color_ind[i]<=Emax))
                {
                    
                    if(plot_color_bar[0]==0)
                    {
                        win->DrawPoint(ri.x/L,ri.y/L,ri.z/L,
                                       atomradius[species[i]]/L,colors[cind],s,2);
                    }
                    else
                    { /* color atoms according to their properties given by color_ind
                         which is specified by coloraxis
                         there are two different color maps as specified by
                         plot_color_bar[0]: 1 or 2
                      */
                        alpha=(color_ind[i]-plot_color_bar[1])
                            /(plot_color_bar[2]-plot_color_bar[1]);
                        if(plot_color_bar[0]==1)
                            colormap1(alpha,&r,&g,&b);
                        else colormap2(alpha,&r,&g,&b);
                        
                        ce=win->AllocShortRGBColor(r,g,b);
                        if(plot_atom_info==5) /* print atom color (rgb) if clicked */
                            sprintf(s,"%d,%d,%d,%d,%x",i,r,g,b,ce);
                        win->DrawPoint(ri.x/L,ri.y/L,ri.z/L,
                                       atomradius[species[i]]/L,ce,s,2);
                    }                
                }
            }
            if(plot_color_windows[nw*3+1]) /* draw fixed atoms */
                if(fixed[i])   /* plot fixed atoms */
                    win->DrawPoint(ri.x/L,ri.y/L,ri.z/L,
                         atomradius[species[i]]/L,colors[MAXCOLORS+3],s,2);
            
        }
        else
        {
            high=false;
            for(j=1;j<=plot_highlight_atoms[0];j++)
                if(i==plot_highlight_atoms[j])
                {
                    high=true;
                    break;
                }
            
            if(fixed[i])   /* plot fixed atoms */
                win->DrawPoint(ri.x/L,ri.y/L,ri.z/L,
                               atomradius[species[i]]/L,colors[MAXCOLORS+3],s,2);
            else if (high) /* plot highlight atoms */
                win->DrawPoint(ri.x/L,ri.y/L,ri.z/L,
                               atomradius[species[i]]/L,colors[MAXCOLORS+2],s,2);
            
            else if(plot_color_bar[0]!=0)
            { /* color atoms according to their properties given by color_ind
                 which is specified by coloraxis
                 there are two different color maps as specified by
                 plot_color_bar[0]: 1 or 2
              */
                alpha=(color_ind[i]-plot_color_bar[1])
                        /(plot_color_bar[2]-plot_color_bar[1]);
                if(plot_color_bar[0]==1)
                    colormap1(alpha,&r,&g,&b);
                else colormap2(alpha,&r,&g,&b);
                
                ce=win->AllocShortRGBColor(r,g,b);
                if(plot_atom_info==5) /* print atom color (rgb) if clicked */
                    sprintf(s,"%d,%d,%d,%d,%x",i,r,g,b,ce);
                win->DrawPoint(ri.x/L,ri.y/L,ri.z/L,
                               atomradius[species[i]]/L,ce,s,2);
            }
            else
            {
                /* otherwise color atoms according to their species */
                ce=colors[MAXCOLORS+5+species[i]];
                if(plot_atom_info==5) /* print atom species and color code if clicked */
                    sprintf(s,"%d,%d,%x",i,species[i],ce);
                
                win->DrawPoint(ri.x/L,ri.y/L,ri.z/L,
                               atomradius[species[i]]/L,ce,s,2);
            }
        }
    }
    
    /* Draw Bonds */
    if(bondlength>1e-3)
    {
        for(i=0;i<_NP;i++)
        {
            sri=_SR[i];
            if(plot_map_pbc==1) sri.subint();
            ri = _H*sri;
            
            show = 0;
            nw = (int)plot_color_windows[0];
            if(nw>0)
            {
                for(k=0;k<nw;k++)
                {
                    Emin = plot_color_windows[k*3+1];
                    Emax = plot_color_windows[k*3+2];
                    cind = (int) plot_color_windows[k*3+3];
                    
                    if((color_ind[i]>=Emin)&&
                       (color_ind[i]<=Emax))
                    {
                        show = 1;
                        break;
                    }
                }
            }
            else
            {
                show = 1;
            }
            if(!show) continue;
            
            for(jp=0;jp<nn[i];jp++)
            {
                j=nindex[i][jp];
                srj=_SR[j];
                if(plot_map_pbc==1) srj.subint();
                rj = _H*srj;

                show = 0;
                nw = (int)plot_color_windows[0];
                if(nw>0)
                {
                    for(k=0;k<nw;k++)
                    {
                        Emin = plot_color_windows[k*3+1];
                        Emax = plot_color_windows[k*3+2];
                        cind = (int) plot_color_windows[k*3+3];
                        
                        if((color_ind[j]>=Emin)&&
                           (color_ind[j]<=Emax))
                        {
                            show = 1;
                            break;
                        }
                    }
                }
                else
                {
                    show = 1;
                }
                if(!show) continue;
                
                if(plot_limits[0])
                    if((sri.x<plot_limits[1])||(sri.x>plot_limits[2])
                       ||(sri.y<plot_limits[3])||(sri.y>plot_limits[4])
                       ||(sri.z<plot_limits[5])||(sri.z>plot_limits[6])
                       ||(srj.x<plot_limits[1])||(srj.x>plot_limits[2])
                       ||(srj.y<plot_limits[3])||(srj.y>plot_limits[4])
                       ||(srj.z<plot_limits[5])||(srj.z>plot_limits[6]))
                        continue;
                r2=(ri-rj).norm2();
                //INFO_Printf(" %d - %d : %f %f\n",i,j,r2,bondlength*bondlength);
                if(r2<bondlength*bondlength)
                {
                    /* only draw O-O bonds */
                    /* if((species[i]!=0)||(species[j]!=0)) continue; */
                    //INFO_Printf("atom %d %d %d %d form bond\n",i, j, i%8,j%8); 
                    x1=ri.x/L;y1=ri.y/L;z1=ri.z/L;
                    x2=rj.x/L;y2=rj.y/L;z2=rj.z/L;
                    dx=x2-x1;dy=y2-y1;dz=z2-z1;dr=sqrt(dx*dx+dy*dy+dz*dz);
                    dx/=dr;dy/=dr;dz/=dr;
                    win->DrawLine(x1+dx*atomradius[species[i]]/L,
                                  y1+dy*atomradius[species[i]]/L,
                                  z1+dz*atomradius[species[i]]/L,
                                  x2-dx*atomradius[species[j]]/L,
                                  y2-dy*atomradius[species[j]]/L,
                                  z2-dz*atomradius[species[j]]/L,
                                  colors[MAXCOLORS+1],bondradius/L,1);
                }
            }
        }
    }
    /* draw frame */
#define drawsline(a,b,c,d,e,f,g,h,i) s1.set(a,b,c); s2.set(d,e,f);\
        vr1=_H*s1; vr2=_H*s2; vr1/=2*L; vr2/=2*L;\
        win->DrawLine(vr1.x,vr1.y,vr1.z,vr2.x,vr2.y,vr2.z,g,h,i);
    
    drawsline(-1,-1,-1,-1,-1, 1,colors[MAXCOLORS+2],0,0);
    drawsline(-1,-1, 1,-1, 1, 1,colors[MAXCOLORS+2],0,0);
    drawsline(-1, 1, 1,-1, 1,-1,colors[MAXCOLORS+2],0,0);
    drawsline(-1, 1,-1,-1,-1,-1,colors[MAXCOLORS+2],0,0);
    drawsline( 1,-1,-1, 1,-1, 1,colors[MAXCOLORS+2],0,0);
    drawsline( 1,-1, 1, 1, 1, 1,colors[MAXCOLORS+2],0,0);
    drawsline( 1, 1, 1, 1, 1,-1,colors[MAXCOLORS+2],0,0);
    drawsline( 1, 1,-1, 1,-1,-1,colors[MAXCOLORS+2],0,0);
    drawsline(-1,-1,-1, 1,-1,-1,colors[MAXCOLORS+2],0,0);
    drawsline(-1,-1, 1, 1,-1, 1,colors[MAXCOLORS+2],0,0);
    drawsline(-1, 1, 1, 1, 1, 1,colors[MAXCOLORS+2],0,0);
    drawsline(-1, 1,-1, 1, 1,-1,colors[MAXCOLORS+2],0,0);
    
    win->Unlock();
    win->Refresh();

    MP_EndMasterOnly();
}




/********************************************************************/
/* End of class MDFrame definition */
/********************************************************************/




/********************************************************************/
/* Main program starts below */
/********************************************************************/

#ifdef _TEST

class TESTMD : public MDFrame
{
public:
    void potential()
    {
        /* Multi process function */
        /* a void potential */
        INFO("Empty potential");
    }
    virtual void initvars()
    {
        MDFrame::initvars();
        _RLIST=3.8;
    }
            
};

class TESTMD sim;

int main(int argc, char *argv[])
{
    sim.initvars();
    sim.parse(SCParser::getfilehandler(argc,argv));
    return 0;
}
#endif
