//###################################################################
//-------------------------------------------------------------------
//yzo.cpp
//-------------------------------------------------------------------
//
//  by Hark Lee hblee@stanford.edu
//  Last Modified : Wed Aug 22 15:27:33 2007
//
//-------------------------------------------------------------------
// FUNCTION  :  YSZ MD simulation using (Coulomb+BMB) potential
//------------------------------------------------------------------- 
//###################################################################

//###################################################################
//  Include Libraries
//###################################################################
#include "yzo.h"

//###################################################################
//  Initialization of Variables and Customized Commands
//###################################################################
//-------------------------------------------------------------------
// Initialize Variables
//-------------------------------------------------------------------
void YZOFrame::initvars()
{
    MDFrame::initvars();
}

void YZOFrame::updateRLIST()
{
    _RLIST=PP_RC+_SKIN;
    INFO("------------------------------------------------------------------------------");
    INFO("             Initial Cut-off Radius for Short Range Interaction");
    INFO("------------------------------------------------------------------------------");
    INFO("             PP_RC = "<<PP_RC<<"  _SKIN = "<<_SKIN<<"  _RLIST = "<<_RLIST);
    INFO("------------------------------------------------------------------------------");
}

//-------------------------------------------------------------------
// Add Commands
//-------------------------------------------------------------------
int YZOFrame::exec(char *name)
{
    if(MDFrame::exec(name)==0) return 0;
    bindcommand(name,"updateRLIST",updateRLIST());
    bindcommand(name,"yszplot",yszplot());    
    bindcommand(name,"yszlatticeconst",yszlatticeconst());
    bindcommand(name,"dopeyttriaSC",dopeyttriaSC());
    bindcommand(name,"dopeyttriaGB",dopeyttriaGB());
    bindcommand(name,"dopeyttriaGC",dopeyttriaGC());
    bindcommand(name,"zirconianeutral",zirconianeutral());
    bindcommand(name,"yszneutral",yszneutral());
    bindcommand(name,"runNMC",NMC());
    bindcommand(name,"runCGNMC",CGNMC());
    bindcommand(name,"semisurface",semisurface());
    bindcommand(name,"runSAFM",SAFM());
    bindcommand(name,"testBMB",testBMB());    
    bindcommand(name,"testEwald",testEwald());
    return -1;
}

void YZOFrame::initparser()
{
    bindvar("seed",&drandseed,INT);

    bindvar("PP_RC",&PP_RC,DOUBLE);
    bindvar("SKIN",&_SKIN,DOUBLE);    
    bindvar("potsetup",&potsetup,INT); 
    bindvar("yttriaconc",&yttriaconc,DOUBLE);
    bindvar("topconc",&GC_topconc,DOUBLE);
    bindvar("botconc",&GC_botconc,DOUBLE);
    bindvar("numlayers",&GC_numlayers,INT);

    bindvar("MCjumps",&MCjumps,INT);
    bindvar("MDstep1",&MDstep1,INT);
    bindvar("MDstep2",&MDstep2,INT);
    bindvar("MDstep1freq",&MDstep1freq,INT);
    bindvar("MDstep2freq",&MDstep2freq,INT);
    bindvar("MCcheckfreq",&MCcheckfreq,INT);

    bindvar("maxNNO",&maxNNO,INT);
    bindvar("anfluc",&anfluc,DOUBLE);
    bindvar("catmigdist",&catmigdist,DOUBLE);
    bindvar("NNO_Rc",&NNO_Rc,DOUBLE);

    bindvar("surfacex",&surfacex,INT);
    bindvar("surfacey",&surfacey,INT);
    bindvar("surfacez",&surfacez,INT);
    bindvar("surfacexscale",&surfacexscale,DOUBLE);
    bindvar("surfaceyscale",&surfaceyscale,DOUBLE);
    bindvar("surfacezscale",&surfacezscale,DOUBLE);

    bindvar("switchSAFM",&switchSAFM,INT);
    bindvar("d1TPs",&d1TPs,INT);
    bindvar("d2TPs",&d2TPs,INT);
    bindvar("d3sep",&d3sep,DOUBLE);
    bindvar("probesp",&probesp,INT);
    
    bindvar("useCE",&useCE,INT);
    bindvar("usePME",&usePME,INT);
    bindvar("detAlpha",&detAlpha,INT);
    bindvar("Alpha",&Alpha,DOUBLE);
    bindvar("Kc",&Kc,DOUBLE);
    bindvar("Ewaldprecision",&Ewaldprecision,DOUBLE);
    bindvar("EwaldRFratio",&EwaldRFratio,DOUBLE);
    bindvar("PME_K1",&PME_K1,INT);
    bindvar("PME_K2",&PME_K2,INT);
    bindvar("PME_K3",&PME_K3,INT);
    bindvar("PME_bsp_n",&PME_bsp_n,INT);

    bindvar("testNUM",&testNUM,INT);
    bindvar("testCE",&testCE,INT);
    bindvar("testPME",&testPME,INT);
    MDFrame::initparser();
}

//###################################################################
//  YSZ Lattice Costant
//###################################################################
void YZOFrame::yszplot()
{
    int i;
    
    for(i=0;i<_NP;i++)
    {
        _EPOT_IND[i]=0.0;
    }
    refreshneighborlist();
    plot();
}
    
    
void YZOFrame::yszlatticeconst()
{
    double ysz_alpha, ysz_a300K;

    // specify the thermal coefficient and lattice constant at 300K
    ysz_alpha=1.0e-5;
    ysz_a300K=5.136;

    // calculate the lattice constant at destination temperature
    latticeconst[0]=ysz_a300K*(1.0+ysz_alpha*(_TDES-300.0));
    INFO("------------------------------------------------------------------------------");
    INFO("Automatically Calculating the Lattice Constant for YSZ at T = "<<_TDES<<"K");
    INFO("------------------------------------------------------------------------------");
    if(_TDES==0.0)
    {
        WARNING("System Temperature is Zero -> Check If You Set the Temperature Properly");
    }
    INFO("Lattice constant = "<<latticeconst[0]);
    INFO("------------------------------------------------------------------------------");
}
            
//###################################################################
//  Doping Fuctions and Charge Neutralizer
//###################################################################
//-------------------------------------------------------------------
// Dope Yttria [Perfect Lattice]
//-------------------------------------------------------------------
void YZOFrame::dopeyttriaSC()
{
    int Y_count,V_count,Max_Y,Max_V;
    int N_cat,N_an,N_cat_set,N_an_set;
    int I_cat_1,I_cat_2,I_an_1,I_an_2;
    double F_vac, rand_temp;
    
    // specify random seed
    srand48(drandseed);

    if(yttriaconc==0.0){WARNING("DOPEYTTRIA : YTTRIA CONCENTRATION IS NOT SPECIFIED"); return;}
    N_cat = _NP/12*4; // number of cations
    N_an = _NP/12*8; // number of anions
    F_vac = yttriaconc/(2.0+2.0*yttriaconc); // vacancy fraction (N_vac / N_an)
    Max_V = (int) floor(F_vac*((double)N_an)+0.5); // number of vacancy
    Max_Y = Max_V * 2; // number of Yttrium
    Y_count=0; // initialize Yttrium number
    V_count=0; // initialize vacancy number
    INFO("------------------------------------------------------------------------------");
    INFO("------------------------------------------------------------------------------");
    INFO("                             Doping Yttria [PL]");
    INFO("        Substitute Zirconium with Yttrium and Create Oxygen Vacancies");
    INFO("------------------------------------------------------------------------------");
    INFO("    Total Number of Atomic Sites  = "<<(_NP+Max_V));
    INFO("    Total Number of Cation Sites  = "<<N_cat);
    INFO("    Total Number of Anion Sites   = "<<N_an);
    INFO("    Vacancy Fraction              = "<<F_vac); 
    INFO("------------------------------------------------------------------------------");
    INFO("    Total Number of Zirconiums    = "<<(N_cat-Max_Y));
    INFO("    Total Number of Oxygens       = "<<N_an-Max_V);
    INFO("    Total Number of Yttriums      = "<<Max_Y);
    INFO("    Total Number of Vacancies     = "<<Max_V);
    INFO("------------------------------------------------------------------------------");
    INFO("------------------------------------------------------------------------------");

    // dope Yttrium into cation sites
    while(Y_count<Max_Y)
    {
        // pick a random number between 0 and 1
        rand_temp = ((double)drand48());
        if(rand_temp!=1.0)
	{
            // get the atom index of a random cation
            I_cat_1 = (int) floor(((double)N_cat)*rand_temp);
            N_an_set = I_cat_1/4; // implicitly floor
            I_cat_2 = I_cat_1+8*N_an_set; // final cation index
            
            // check the species of picked atom
            if(species[I_cat_2]==0) // if target is Zr
	    {
                species[I_cat_2]=3; // set the species index to 3
                Y_count++; // increase the Yttrium count
	    }
	}
    }
    // free all atoms
    freeallatoms();
    
    // create vacancy
    while(V_count<Max_V)
    {
        // pick a random number between 0 and 1
        rand_temp = ((double)drand48());
        if(rand_temp!=1.0)
        {
            // get the atom index of a random anion
            I_an_1 = (int) floor(((double)N_an)*rand_temp);
            N_cat_set = I_an_1/8+1;
            I_an_2 = I_an_1+4*N_cat_set;
            
            if(species[I_an_2]==1) // if target is oxygen
	    {
                species[I_an_2]=2; // set the species to 2(dummy)
                fixed[I_an_2]=1; // fix picked oxygen atom
                V_count++; // count number of picked atoms
	    }
	}
    }
    // create oxygen vacancies
    removefixedatoms();
    // free all atoms
    freeallatoms();
}

//-------------------------------------------------------------------
// Check System Charge Neutrality
//-------------------------------------------------------------------
void YZOFrame::zirconianeutral()
{
    int i, j, tZr, tO, nZr, nO, totalcharge;
    double rand_temp;
    
    // Calculate net system charge 
    totalcharge=0;
    for(i=0;i<_NP;i++)
    {
        if(species[i]==0){totalcharge+=(+4);}
        else if(species[i]==1){totalcharge+=(-2);}
        else{WARNING("ZRO2NEUTRAL : UNKNOWNSPECIES -> CANCELING DOPING");
        WARNING("ATOM  "<<i<<"  SPECIES  "<<species[i]); return;}
    }
    // Charge neutrality
    INFO("------------------------------------------------------------------------------");
    INFO("------------------------------------------------------------------------------");
    INFO("                        System Charge Neutralization ");
    INFO("------------------------------------------------------------------------------");
    INFO("------------------------------------------------------------------------------");
    INFO("     - Initial System Net Charge = "<<(P_E*((double)totalcharge))<<"C ["<<totalcharge<<"]");
    tZr=0; nZr=0; tO=0; nO=0;
    if(totalcharge!=0)
    {
        INFO("     - Problem in System Charge Neutrality ==> Trying to Correct ");
        if ((totalcharge%4)==0)
        {
            if(totalcharge>0)
            {
                tZr=totalcharge/4;
                INFO("     - Need to Remove "<<tZr<<" Zirconiums");
                while(nZr<tZr)
                {
                    rand_temp = ((double)drand48());
                    if(rand_temp!=1.0)
                    {
                        j=(int)floor(rand_temp*((double)_NP));
                        if((species[j]==0)&&(fixed[j]==0))
                        {
                            fixed[j]=1;
                            nZr++;
                        }
                    }
                }
            }
            else //(totalcharge<0)
            {
                tO=-totalcharge/2;
                INFO("     - Need to Remove "<<tO<<" Oxygens");
                while(nO<tO)
                {
                    rand_temp = ((double)drand48());
                    if(rand_temp!=1.0)
                    {
                        j=(int)floor(rand_temp*((double)_NP));
                        if((species[j]==1)&&(fixed[j]==0))
                        {
                            fixed[j]=1;
                            nO++;
                        }
                    }
                }
            }
        }
        else if (((totalcharge%4)==2)||((totalcharge%4)==-2))
        {
            if(totalcharge>0)
            {
                tZr=(int)(totalcharge/4)+1;
                tO=1;
                INFO("     - Need to Remove "<<tZr<<" Zirconiums and "<<tO<<" Oxygen");
                while(nZr<tZr)
                {
                    rand_temp = ((double)drand48());
                    if(rand_temp!=1.0)
                    {
                        j=(int)floor(rand_temp*((double)_NP));
                        if((species[j]==0)&&(fixed[j]==0))
                        {
                            fixed[j]=1;
                            nZr++;
                        }
                    }
                }
                while(nO<tO)
                {
                    rand_temp = ((double)drand48());
                    if(rand_temp!=1.0)
                    {
                        j=(int)floor(rand_temp*((double)_NP));
                        if((species[j]==1)&&(fixed[j]==0))
                        {
                            fixed[j]=1;
                            nO++;
                        }
                    }
                }
            }
            else //(totalcharge<0)
            {
                tO=-totalcharge/2;
                INFO("     - Need to Remove "<<tO<<" Oxygens");
                while(nO<tO)
                {
                    rand_temp = ((double)drand48());
                    if(rand_temp!=1.0)
                    {
                        j=(int)floor(rand_temp*((double)_NP));
                        if((species[j]==1)&&(fixed[j]==0))
                        {
                            fixed[j]=1;
                            nO++;
                        }
                    }
                }
            }
        }
        else {INFO("     - Cannot Retain System Charge Neutrality");}
        
        // Remove atoms
        removefixedatoms();
        freeallatoms();
        INFO("     - Redundant Atoms Removed ==> Acheiving Neutrality");
        
        // Recalculate totalcharge
        totalcharge=0;
        for(i=0;i<_NP;i++)
        {
            if(species[i]==0){totalcharge+=(+4);}
            else if(species[i]==1){totalcharge+=(-2);}
            else{WARNING("ZRO2NEUTRAL : : UNKNOWNSPECIES -> CANCELING DOPING");
            WARNING("ATOM  "<<i<<"  SPECIES  "<<species[i]); return;}
        }
    }
    
    // Verify System Neutrality
    if (totalcharge==0){INFO("     - System Charge Neutralized");}
    else {INFO("     - System Charge Neutralization Failed");}
    INFO("------------------------------------------------------------------------------");
}

void YZOFrame::yszneutral()
{
    int i, totalcharge;
    
    // Calculate net system charge 
    totalcharge=0;
    for(i=0;i<_NP;i++)
    {
        if(species[i]==0){totalcharge+=(+4);}
        else if(species[i]==1){totalcharge+=(-2);}
        else if(species[i]==3){totalcharge+=(+3);}
        else{INFO("YSZNEUTRAL : UNKNOWNSPECIES -> CANCELING DOPING");
        INFO("ATOM  "<<i<<"  SPECIES  "<<species[i]); return;}
    }
    INFO("------------------------------------------------------------------------------");
    INFO("------------------------------------------------------------------------------");
    INFO("                        Populating New System Charge ");
    INFO("------------------------------------------------------------------------------");
    INFO("------------------------------------------------------------------------------");
    INFO("     - Computed System Net Charge = "<<(P_E*((double)totalcharge))<<"C ["<<totalcharge<<"]");
    INFO("------------------------------------------------------------------------------");
}

//-------------------------------------------------------------------  
// Dope Yttria [Grain Boundary]
//------------------------------------------------------------------- 
void YZOFrame::dopeyttriaGB()
{
    int i,Y_count,V_count,Max_Y,Max_V;
    int GB_V_count, GB_Y_count, GB_V, GB_Y, B_V_count, B_Y_count, B_V, B_Y;
    int N_cat,N_an,I_cat,I_an;
    int totalcharge;
    double F_vac,rand_temp,F_V_GB, F_Y_GB, t_V_GB, t_Y_GB, tmp;
    
    srand48(drandseed);

    F_V_GB=0.0;
    F_Y_GB=0.0;
    t_V_GB=0.0;
    t_Y_GB=0.0;
    
    // Check if the yttria concentration is given
    if(yttriaconc==0.0){INFO("DOPEYTTRIA : YTTRIA CONCENTRATION IS NOT SPECIFIED");return;}
    
    // Count cation and anion sites
    N_cat=0; N_an=0;
    for(i=0;i<_NP;i++)
    {
        if(species[i]==0){N_cat++;}
        else if(species[i]==1){N_an++;}
        else{WARNING("DOPEYTTRIA_GB : UNKNOWNSPECIES -> CANCELING DOPING");
        WARNING("ATOM  "<<i<<"  SPECIES  "<<species[i]); return;}
    }
    
    // Dope yttria preparation
    F_vac = yttriaconc/(2.0+2.0*yttriaconc); // vacancy fraction (N_vac / N_an)
    Max_V = (int) floor(F_vac*((double)N_an)+0.5); // number of vacancy
    Max_Y = Max_V * 2; // number of Yttrium
    GB_V = (int) (F_V_GB*((double)Max_V)); // get the number of vacancies in GB
    GB_Y = (int) (F_Y_GB*((double)Max_Y)); // get the number of Yttriums in GB
    B_V = Max_V - GB_V; // get the number of vacancies in bulk
    B_Y = Max_Y - GB_Y; // get the number of Yttriums in bulk 
    
    Y_count=0; // initialize Yttrium number
    V_count=0; // initialize vacancy number
    GB_Y_count=0;
    GB_V_count=0;
    B_Y_count=0;
    B_V_count=0;
    
    // dope Yttrium into cation sites
    while(Y_count<Max_Y)
    {
        // pick a random number between 0 and 1
        rand_temp = ((double)drand48());
        if(rand_temp!=1.0)
        {
            // get the atom index of a random cation
            I_cat = (int) floor(rand_temp*((double)_NP));
            // check the species of picked atom
            if(species[I_cat]==0) // if target is Zr
            {
                tmp=_SR[I_cat].x;
                if(((tmp>(-0.25-t_Y_GB))&&(tmp<(-0.25+t_Y_GB)))
                   ||((tmp>(0.25-t_Y_GB))&&(tmp<(0.25+t_Y_GB))))
                {
                    if(GB_Y_count<GB_Y)
                    {
                        species[I_cat]=3; // set the species index to 3
                        Y_count++; // increase the Yttrium count
                        GB_Y_count++;
                    }
                }
                else 
                {
                    if(B_Y_count<B_Y)
                    {
                        species[I_cat]=3; // set the species index to 3
                        Y_count++; // increase the Yttrium count
                        B_Y_count++;
                    }
                }
            }
        }
    }
    
    // create vacancy
    freeallatoms();
    while(V_count<Max_V)
    {
        // pick a random number between 0 and 1
        rand_temp = ((double)drand48());
        if(rand_temp!=1.0)
        {
            // get the atom index of a random anion
            I_an = (int) floor(rand_temp*((double)_NP));
            if(species[I_an]==1) // if target is oxygen
            {
                tmp=_SR[I_an].x;
                if(((tmp>(-0.25-t_V_GB))&&(tmp<(-0.25+t_V_GB)))
                   ||((tmp>(0.25-t_V_GB))&&(tmp<(0.25+t_V_GB))))
                {
                    if(GB_V_count<GB_V)
                    {
                        species[I_an]=2; // set the species index to 2
                        fixed[I_an]=1; // fix picked oxygen atom
                        V_count++; // increase the Yttrium count
                        GB_V_count++;
                    }
                }
                else
                {
                    if(B_V_count<B_V)
                    {
                        species[I_an]=2; // set the species index to 2
                        fixed[I_an]=1; // fix picked oxygen atom
                        V_count++; // increase the Yttrium count
                        B_V_count++;
                    }
                }
            }
        }
    }
    removefixedatoms();
    freeallatoms();
    if((V_count!=(GB_V_count+B_V_count))||(Y_count!=(GB_Y_count+B_Y_count)))
    {INFO("Error in doping function! check the source code");}
    
    // Final Check of System Neutrality
    totalcharge=0;
    for(i=0;i<_NP;i++)
    {
        if(species[i]==0){totalcharge+=(+4);} // Zr
        else if(species[i]==1){totalcharge+=(-2);} // O
        else if(species[i]==3){totalcharge+=(+3);} // Y
        else{INFO("DOPEYTTRIA_GB : UNKNOWNSPECIES -> CANCELING DOPING");
        INFO("ATOM  "<<i<<"  SPECIES  "<<species[i]); return;}
    }
    INFO("------------------------------------------------------------------------------");
    INFO("     - Yttria successfully Doped ");
    if (totalcharge==0){INFO("     - Doped System Charge Neutrality Verified");}
    else {INFO("     - System Charge Neutralization Failed");}
    INFO("------------------------------------------------------------------------------");
    INFO("------------------------------------------------------------------------------");
    INFO("                             Doping Yttria [GB]");
    INFO("        Substitute Zirconium with Yttrium and Create Oxygen Vacancies");
    INFO("------------------------------------------------------------------------------");
    INFO("    Total Number of Atomic Sites  = "<<(_NP+Max_V));
    INFO("    Total Number of Cation Sites  = "<<N_cat);
    INFO("    Total Number of Anion Sites   = "<<N_an);
    INFO("    Vacancy Fraction              = "<<F_vac);
    INFO("------------------------------------------------------------------------------");
    INFO("    Total Number of Zirconiums    = "<<(N_cat-Max_Y));
    INFO("    Total Number of Oxygens       = "<<(N_an-Max_V));
    INFO("    Total Number of Yttriums      = "<<Max_Y);
    INFO("    Total Number of Vacancies     = "<<Max_V);
    INFO("------------------------------------------------------------------------------");
    INFO("------------------------------------------------------------------------------");
}

//-------------------------------------------------------------------  
// Dope Yttria [Gradient Doping]
//------------------------------------------------------------------- 
void YZOFrame::dopeyttriaGC()
{
    int i, j;
    int N_cat, N_an, N_vac;
    int Y_count,V_count;
    int I_cat_1,I_cat_2,I_an_1,I_an_2;
    int N_cat_set,N_an_set;
    int N_tot_Y, N_tot_Zr, N_tot_O;
    double rand_temp;

    srand48(drandseed);
    
    // Check if the yttria concentration is given
    if(GC_topconc==0.0){INFO("DOPEYTTRIA : TOP YTTRIA CONCENTRATION IS NOT SPECIFIED");return;}
    //if(GC_botconc==0.0){INFO("DOPEYTTRIA : BOTTOM YTTRIA CONCENTRATION IS NOT SPECIFIED");return;}
    
    // Construct dopant concentration array
    Realloc(GC_ytop,double,GC_numlayers);
    Realloc(GC_ybot,double,GC_numlayers);
    Realloc(GC_yttriaconc,double,GC_numlayers);
    Realloc(GC_F_V,double,GC_numlayers);
    Realloc(GC_N_cat,int,GC_numlayers);
    Realloc(GC_N_an,int,GC_numlayers);
    Realloc(GC_Max_Y,int,GC_numlayers);
    Realloc(GC_Max_V,int,GC_numlayers);
    
    // initialize variables (to avoid warning)
    for(i=0;i<GC_numlayers;i++)
      {
	GC_ytop[i]=0.0; GC_ybot[i]=0.0;
	GC_yttriaconc[i]=0.0;
	GC_F_V[i]=0.0;
	GC_N_cat[i]=0;
	GC_N_an[i]=0;
	GC_Max_Y[i]=0;
	GC_Max_V[i]=0;
      }
    
    // Calculate maximum number of dopants in each layer
    GC_dy=1.0/((double)GC_numlayers);
    GC_dyttriaconc=(GC_topconc-GC_botconc)/((double)(GC_numlayers-1));
    N_cat=0; N_an=0;
    for(i=0;i<GC_numlayers;i++)
      {
	// calculate the boundaries of layer i
	GC_ytop[i] = 0.5-GC_dy*((double)i);
	GC_ybot[i] = GC_ytop[i]-GC_dy;
	
	// calculate the yttria concentration and vacancy fraction of layer i
	GC_yttriaconc[i] = GC_topconc-GC_dyttriaconc*((double)i);
        GC_F_V[i] = GC_yttriaconc[i]/(2.0+2.0*GC_yttriaconc[i]);
	
	// count the number of cations and anions in layer i
	GC_N_cat[i]=0;	
	GC_N_an[i]=0;
	if(i!=(GC_numlayers-1))
	  {
	    for(j=0;j<_NP;j++)
	      {
		if((_SR[j].y>GC_ybot[i])&&(_SR[j].y<=GC_ytop[i]))
		  {
		    if(species[j]==0){GC_N_cat[i]++; N_cat++;}
		    else if(species[j]==1){GC_N_an[i]++; N_an++;}
		    else{WARNING("DOPEYTTRIA_GC : UNKNOWNSPECIES -> CANCELING DOPING");
		    WARNING("ATOM  "<<j<<"  SPECIES  "<<species[j]); return;}
		  }
	      }
	  }
	else if(i==(GC_numlayers-1))
	  {
	    for(j=0;j<_NP;j++)
	      {
		if((_SR[j].y>=GC_ybot[i])&&(_SR[j].y<=GC_ytop[i]))
		  {
		    if(species[j]==0){GC_N_cat[i]++; N_cat++;}
		    else if(species[j]==1){GC_N_an[i]++; N_an++;}
		    else{WARNING("DOPEYTTRIA_GC : UNKNOWNSPECIES -> CANCELING DOPING");
		    WARNING("ATOM  "<<j<<"  SPECIES  "<<species[j]); return;}
		  }
	      }
	  }
    	GC_Max_V[i] = (int) floor(GC_F_V[i]*((double)GC_N_an[i])+0.5);
    	GC_Max_Y[i] = GC_Max_V[i] * 2; // number of Yttrium
      }
    
    // calculate total number of oxygen vacancies
    N_vac=0;
    for(i=0;i<GC_numlayers;i++){N_vac=N_vac+GC_Max_V[i];}
    
    // dope yttriums into cation sites
    for(i=0;i<GC_numlayers;i++)
      {
    	Y_count=0; // initialize Yttrium number
    	while(Y_count<GC_Max_Y[i])
	  {
	    // pick a random number between 0 and 1
	    rand_temp = ((double)drand48());
	    // get the atom index of a random cation
	    I_cat_1 = (int) floor(((double)N_cat)*rand_temp);
	    N_an_set = I_cat_1/4; // implicitly floor
	    I_cat_2 = I_cat_1+8*N_an_set; // final cation index
	    if(i!=(GC_numlayers-1))
	      {	
		// check the position and species of picked atom
		if((_SR[I_cat_2].y>GC_ybot[i])&&(_SR[I_cat_2].y<=GC_ytop[i]))
		  {
		    if(species[I_cat_2]==0) // if target is Zr
		      {
			species[I_cat_2]=3; // set the species index to 3
			Y_count++; // increase the Yttrium count
		      }
		  }
	      }
	    else if(i==(GC_numlayers-1))
	      {	
		// check the position and species of picked atom
		if((_SR[I_cat_2].y>=GC_ybot[i])&&(_SR[I_cat_2].y<=GC_ytop[i]))
		  {
		    if(species[I_cat_2]==0) // if target is Zr
		      {
			species[I_cat_2]=3; // set the species index to 3
			Y_count++; // increase the Yttrium count
		      }
		  }
	      }
	    
	  }
      }
    // free all atoms
    freeallatoms();
    
    // create oxygen vacancies
    for(i=0;i<GC_numlayers;i++)
      {
    	V_count=0; // initialize vacancy number
    	while(V_count<GC_Max_V[i])
	  {
	    // pick a random number between 0 and 1
	    rand_temp = ((double)drand48());
	    // get the atom index of a random anion
	    I_an_1 = (int) floor(((double)N_an)*rand_temp);
	    N_cat_set = I_an_1/8+1;
	    I_an_2 = I_an_1+4*N_cat_set;
	    if(i!=(GC_numlayers-1))
	      {	
		// check the position and species of picked atom
		if((_SR[I_an_2].y>GC_ybot[i])&&(_SR[I_an_2].y<=GC_ytop[i]))
		  {
		    if(species[I_an_2]==1) // if target is oxygen
		      {
			species[I_an_2]=2; // set the species to 2(dummy)
			fixed[I_an_2]=1; // fix picked oxygen atom
			V_count++; // count number of picked atoms
		      }
		  }
	      }
	    else if(i==(GC_numlayers-1))
	      {	
		// check the position and species of picked atom
		if((_SR[I_an_2].y>=GC_ybot[i])&&(_SR[I_an_2].y<=GC_ytop[i]))
		  {
		    if(species[I_an_2]==1) // if target is oxygen
		      {
			species[I_an_2]=2; // set the species to 2(dummy)
			fixed[I_an_2]=1; // fix picked oxygen atom
			V_count++; // count number of picked atoms
		      }
		  }
	      }
	  }
      }
    
    // create oxygen vacancies
    removefixedatoms();

    // free all atoms
    freeallatoms();
    
    // check the number of atoms
    N_tot_Y=0; N_tot_Zr=0; N_tot_O=0;
    for(i=0;i<_NP;i++)
      {
	if(species[i]==3){N_tot_Y++;}
	else if(species[i]==0){N_tot_Zr++;}
	else if(species[i]==1){N_tot_O++;}
      }
    if(((N_tot_Y+N_tot_Zr+N_tot_O)!=_NP)||((N_tot_Y+N_tot_Zr)!=N_cat)||((N_an-N_tot_O)!=N_vac)||((N_tot_Y)!=(N_vac*2)))
      {WARNING("DOPEYTTRIA_GC : TOTAL NUMBERS OF SPECIES DON'T MATCH-> CHECK THE SOURCE CODE");}
    
    // display results
    INFO("------------------------------------------------------------------------------");
    INFO("------------------------------------------------------------------------------");
    INFO("                 Doping Yttria [Concentration Gradient]");
    INFO("        Substitute Zirconium with Yttrium and Create Oxygen Vacancies");
    INFO("------------------------------------------------------------------------------");
    INFO("    Total Number of Atomic Sites  = "<<(_NP+N_vac));
    INFO("    Total Number of Cation Sites  = "<<N_cat);
    INFO("    Total Number of Anion Sites   = "<<N_an);
    INFO("    Total Number of Vacancies = "<<N_vac);
    for(i=0;i<GC_numlayers;i++){
    INFO("------------------------------------------------------------------------------");
    INFO("    Yttria Mole Conc of layer "<<i<<" = "<<GC_yttriaconc[i]);
    INFO("    Vacancy Fraction of layer "<<i<<" = "<<GC_F_V[i]);
    INFO("------------------------------------------------------------------------------");
    INFO("    Total Number of Zirconiums    = "<<(GC_N_cat[i]-GC_Max_Y[i]));
    INFO("    Total Number of Oxygens       = "<<(GC_N_an[i]-GC_Max_V[i]));
    INFO("    Total Number of Yttriums      = "<<GC_Max_Y[i]);
    INFO("    Total Number of Vacancies     = "<<GC_Max_V[i]);}
    INFO("------------------------------------------------------------------------------");
    INFO("------------------------------------------------------------------------------");
}

//###################################################################
//  Create Semi-Surfaces
//###################################################################
void YZOFrame::semisurface()
{
    int i;
    SHtoR();
    if(surfacex==1)
    {
        _H[0][0]*=surfacexscale;
        _H[1][0]*=surfacexscale;
        _H[2][0]*=surfacexscale;
    }
    if(surfacey==1)
    {
        _H[0][1]*=surfaceyscale;
        _H[1][1]*=surfaceyscale;
        _H[2][1]*=surfaceyscale;
    }
    if(surfacez==1)
    {
        _H[0][2]*=surfacezscale;
        _H[1][2]*=surfacezscale;
        _H[2][2]*=surfacezscale;
    }
    _Hinv=_H.inv();
    for(i=0;i<_NP;i++)
    {
        _SR[i]=_Hinv*_R[i];
    }
    NbrList_reconstruct();
}

//###################################################################
//  Potential Fuctions
//###################################################################
//-------------------------------------------------------------------
// Initialize Potentials
//-------------------------------------------------------------------
void YZOFrame::preparepotential()
{
    double tmp_RLIST,tmp1,tmp2,tmp3,tmp4;
    
    // check if the potential is already initialized
    if (potsetup==1){INFO("Potentials are already prepared"); return;}

    //-------------------------------------------------------------------
    // Check the Atom Charge
    //-------------------------------------------------------------------     
    INFO("_SPCHARGEMD[0] = "<<_SPCHARGEMD[0]<<" _SPCHARGE[0] = "<<_SPCHARGEMD[0]/CCLMB);
    INFO("_SPCHARGEMD[1] = "<<_SPCHARGEMD[1]<<" _SPCHARGE[1] = "<<_SPCHARGEMD[1]/CCLMB);
    //INFO("_SPCHARGEMD[2] = "<<_SPCHARGEMD[2]<<" _SPCHARGE[2] = "<<_SPCHARGEMD[2]/CCLMB);
    INFO("_SPCHARGEMD[3] = "<<_SPCHARGEMD[3]<<" _SPCHARGE[3] = "<<_SPCHARGEMD[3]/CCLMB);
    //INFO("_SPCHARGEMD[4] = "<<_SPCHARGEMD[4]<<" _SPCHARGE[4] = "<<_SPCHARGEMD[4]/CCLMB);
    //INFO("_SPCHARGEMD[5] = "<<_SPCHARGEMD[5]<<" _SPCHARGE[5] = "<<_SPCHARGEMD[5]/CCLMB); 

    // Calculate inverse matrix and length of lattice vectors
    _Hinv=_H.inv();
    _H_L1=sqrt(SQR(_H[0][0])+SQR(_H[1][0])+SQR(_H[2][0]));
    _H_L2=sqrt(SQR(_H[0][1])+SQR(_H[1][1])+SQR(_H[2][1]));
    _H_L3=sqrt(SQR(_H[0][2])+SQR(_H[1][2])+SQR(_H[2][2]));
    _H_L1R=1.0/_H_L1;
    _H_L2R=1.0/_H_L2;
    _H_L3R=1.0/_H_L3;
    
    // Allocate real space force
    Realloc(_FREAL_B,Vector3,_NP);
    Realloc(_FREAL_C,Vector3,_NP);
    
    //-------------------------------------------------------------------
    // Initialize BMB Potential Parameters
    //-------------------------------------------------------------------    
    // calculate Rc^-6 and Rc^-7
    PP_RC6=POW6(PP_RC);
    PP_RC7=PP_RC6*PP_RC;
    
    //INFO("------------------------------------------------------------------------------");
    //INFO("------------------------------------------------------------------------------");
    INFO("         Born Meyer Buckingham Potential [Short Range Interaction] ");
    //INFO("           Truncated Coulomb Potential [Long Range Interaction]");
    INFO("------------------------------------------------------------------------------");
    INFO("    Short Range Potential Cutoff Radius (Rc)      = "<<PP_RC);
    INFO("    Neighbor List Skin Radius                     = "<<_SKIN);
    INFO("    Total Neighbor List Radius                    = "<<_RLIST);
    INFO("------------------------------------------------------------------------------");
    INFO("                 n=0     n=1   n=2    n=3   n=4    n=5    n=6");
    INFO("              [ Zr-Zr | Zr-O | O-O | Zr-Y | O-Y | BLANK | Y-Y ]");
    INFO("------------------------------------------------------------------------------");
    INFO("------------------------------------------------------------------------------");
    int n;
    for(n=0;n<7;n++){
        INFO("n="<<n);
        BMB_TMP_1[n] = (BMB_POT[n][0]/PP_RC6);              // [BMB] A * Rc^-6
        BMB_TMP_2[n] = (BMB_POT[n][2]*PP_RC);               // [BMB] C * Rc
        BMB_TMP_3[n] = (BMB_POT[n][1]*exp(-BMB_TMP_2[n]));   // [BMB] B * exp( -C * Rc)
        BMB_D[n] = (BMB_POT[n][2]*BMB_TMP_3[n]-6.0*BMB_TMP_1[n]/PP_RC); // [BMB] D
        BMB_E[n] = (BMB_TMP_1[n]-BMB_TMP_3[n]-(BMB_D[n]*PP_RC)); // [BMB] E
        //BMB_E[n]  = (7.0*BMB_TMP_1[n]-(1.0+BMB_TMP_2[n])*BMB_TMP_3[n]);
        //CLMB_B[n] = (CLMB_A[n]/(PP_RC*PP_RC)); // [TRUNCATED COULOMB] B
        //CLMB_C[n] = (-2.0*CLMB_A[n]/(PP_RC));      // [TRUNCATED COULOMB] C
        INFO("------------------------------------------------------------------------------");
        //INFO("    (A*Rc^-6)="<<BMB_TMP_1[n]<<" (C*Rc)="<<BMB_TMP_2[n]<<" (B*exp(-C*Rc))="<<BMB_TMP_3[n]);
        INFO("    BMB_A="<<BMB_POT[n][0]<<"   BMB_B="<<BMB_POT[n][1]<<"   BMB_C="<<BMB_POT[n][2]);
        //INFO("    BNB_D="<<BMB_D[n]<<"   BMB_E="<<BMB_E[n]);
        //INFO("    CLMB_A="<<CLMB_A[n]<<" CLMB_B="<<CLMB_B[n]<<" CLMB_C="<<CLMB_C[n]);
        INFO("------------------------------------------------------------------------------");
    }
    INFO("------------------------------------------------------------------------------");
    
    //-------------------------------------------------------------------
    // Initialize Ewald Parameters
    //-------------------------------------------------------------------
    // get the system volume
    cell_V=_H.det();
    
    //-------------------------------------------------------------------    
    // Detemine the Ewald accuracy
    //-------------------------------------------------------------------
    // Ewaldprecision=sqrt(-ln(error*Ewaldprecision^2))
    //-------------------------------------------------------------------
    // sqrt(-ln(10^-4)) = 3.03485425877029
    // sqrt(-ln(10^-5)) = 3.39307021220756
    // sqrt(-ln(10^-6)) = 3.71692218884984
    // sqrt(-ln(10^-7)) = 4.01473481701573
    // sqrt(-ln(10^-8)) = 4.29193205257869
    // sqrt(-ln(10^-9)) = 4.55228138815544
    // sqrt(-ln(10^-10))= 4.79852591218808
    //-------------------------------------------------------------------

    // check if Alpha has non-zero value
    if((detAlpha==2)&&(Alpha==0))
    {
        WARNING("Ewald Parameter Alpha is Not Specified -> Check Your Script File");
    }

    // determine Alpha (for CE only)
    if(detAlpha==0)
    {
        // get the optimal alpha
        Alpha = M_SQRT_PI*cbrt(sqrt(EwaldRFratio*_NP)/cell_V);
        PP_RC=Ewaldprecision/Alpha;

        tmp_RLIST=PP_RC+_SKIN;
        tmp1=_H_L1/3.0;
        tmp2=_H_L2/3.0;
        tmp3=_H_L3/3.0;
        tmp4=tmp_RLIST;

        if(tmp4>tmp1){tmp4=tmp1;}
        if(tmp4>tmp2){tmp4=tmp2;}
        if(tmp4>tmp3){tmp4=tmp3;}
        
        if(tmp_RLIST>tmp4)
        {
            WARNING("PP_RC Computed from Optimum Alpha is Too Big");
            WARNING("Automatically Setting _RLIST Equal to 1/3 of Minimum Cell Length");
            PP_RC=(tmp4*0.95)-_SKIN;
        }
        updateRLIST();
        detAlpha=2;
    }
    if(detAlpha==1)
    {
        // get the controlled alpha
        Alpha = Ewaldprecision/PP_RC;
    }
    
    // calculate sum of square charge
    qSQRsum=0.0;
    for(int i=0;i<_NP;i++){qSQRsum+=SQR(_SPCHARGEMD[species[i]]);}

    // compute the self-interaction energy
    _EREC_self=-Alpha/M_SQRT_PI*qSQRsum;

    // Allocate reciprocal force
    Realloc(_FREC,Vector3,_NP);
    Realloc(_FREC0,Vector3,_NP);
    
    // Initialize Classical Ewald or Particle Mesh Ewald 
    if((useCE==1)&&(usePME==0)){CE_init();}
    else if((useCE==0)&&(usePME==1)){PME_init();}
    else
    {
        WARNING("SOMETHING WRONG WITH use_CE OR use_PME => INITIALIZATION FAILED");
        potsetup=0;
        return;
    }

    potsetup=1;
}

//-------------------------------------------------------------------
// Compute Pair Potential (Short Range)
//-------------------------------------------------------------------
void YZOFrame::y_zr_o()
{
    int i,j,k,ipt,jpt,isp,jsp; // integers for atom counting, atom indices, atom species
    double U,UB,UC,r,r2,ri6,x,xcube,cx1,cx2,QQ,EXP;   // U = potential | r = radius | r2 = r^2 | ri6 = r^-6
    Vector3 sij, rij, fij, fijB, fijC; // sij : scaled coordinates | rij : coordinates | fij : forces
    DUMP(HIG"Coulomb-BMB Potential"NOR);
        
    // refresh neighbor list
    refreshneighborlist();
    
    // initialize force, potential and virials
    _EPOT=0.0; _EREAL_B=0.0; _EREAL_C=0.0; _EREC=0.0;
    _VIRIAL.clear();
    //_EPOT=0.0; _EREC=0.0; // faster mode

    for(i=0;i<_NP;i++)
    {
        _F[i].clear();
        _FREAL_B[i].clear();
        _FREAL_C[i].clear();
        _FREC[i].clear();
        _EPOT_IND[i]=0.0;
    }
    
    //-------------------------------------------------------------------
    //       Calculating Short Range Potential and Ewald Real Part
    //-------------------------------------------------------------------
    for(ipt=0;ipt<_NP;ipt++) // for all particles
    {
        for(j=0;j<nn[ipt];j++) // for all neighbors
	{
            jpt=nindex[ipt][j]; // get the atom index of j-th neighbor
            if(ipt>=jpt) continue; // only if i<j -> avoid redundancy
            sij=_SR[jpt]-_SR[ipt]; // scaled interatomics distance
            sij.subint(); // apply periodic boundary condition (PBC)
            rij=_H*sij; // get the interatomic distance vector (with PBC)
            r2=rij.norm2(); // get the square of interatomic distance
            r=sqrt(r2); // interatomic distance
            
            if(r<=PP_RC) // if the interatomic distance is shorter than the cut-off radius
	    {
                // precalculate r^-6
                ri6=1.0/(r2*r2*r2);

                // calculate the cutoff variables
                if(r>(PP_RC-1.0))
                {
                    x=r-PP_RC+1.0;
                    xcube=CUBE(x);
                    cx1=SQR(1.0-xcube*x);
                    cx2=8.0*(1.0-xcube*x)*xcube;
                }
                else
                {
                    cx1=1.0;
                    cx2=0.0;
                }

                // get the species indices
                isp=species[ipt]; // species of ipt-atom
                jsp=species[jpt]; // species of jpt-atom
                QQ=_SPCHARGEMD[isp]*_SPCHARGEMD[jsp]; // get the charge term
                k=isp+jsp; // interaction index
                
                // calculate potential
                UB=0.0; UC=0.0;
                UB=(-BMB_POT[k][0]*ri6+BMB_POT[k][1]*exp(-BMB_POT[k][2]*r))*cx1;
                //UB=-BMB_POT[k][0]*ri6+BMB_POT[k][1]*exp(-BMB_POT[k][2]*r)+BMB_D[k]*r+BMB_E[k];
                UC=(erfc(Alpha*r))/r*QQ;
                //UC=CLMB_A[k]/r+CLMB_B[k]*r+CLMB_C[k]; //Truncated Coulomb
                U=UB+UC;
		
                // calculate force
                fijB.clear(); fijC.clear();
                fijB=rij*(((-6.0*BMB_POT[k][0]*ri6/r+BMB_POT[k][1]*BMB_POT[k][2]
                           *exp(-BMB_POT[k][2]*r))*cx1+UB*cx2)/r);
                //fijB=rij*((-6.0*BMB_POT[k][0]*ri6/r+BMB_POT[k][1]*BMB_POT[k][2]
                //           *exp(-BMB_POT[k][2]*r)-BMB_D[k])/r);
                EXP=(2.0/M_SQRT_PI)*Alpha*exp(-SQR(Alpha*r))*QQ;
                fijC=rij*((UC+EXP)/r2);
                //fijC=rij/r*(CLMB_A[k]/r2-CLMB_B[k]); //Truncated Coulomb
                fij=fijB+fijC;
                
                // add pair terms to the accumulative sum
                //_EPOT+=U; // fast mode
                //_F[ipt]-=fij; _F[jpt]+=fij; // fast mode

                _FREAL_B[ipt]-=fijB; _FREAL_B[jpt]+=fijB;
                _FREAL_C[ipt]-=fijC; _FREAL_C[jpt]+=fijC;

                _EREAL_B+=UB;
                _EREAL_C+=UC;

                _EPOT_IND[ipt]+=U*0.5; _EPOT_IND[jpt]+=U*0.5;
                _VIRIAL.addnvv(1.,fij,rij);
	    }
	}
    }
    //-------------------------------------------------------------------
    //               Calculating Ewald Fourier Part
    //------------------------------------------------------------------- 
    // Measure time
    if(useCE){sCEREC=clock();}
    if(usePME){sPMEREC=clock();}

    // Execute CE or PME
    if(useCE){CE();}
    else {PME();}

    // Sum up Energy and Force
    _EPOT=_EREAL_B+_EREAL_C+_EREC+_EREC_self;
    for(i=0;i<_NP;i++){_F[i]=_FREAL_B[i]+_FREAL_C[i]+_FREC[i];}
    //INFO("POT : _EREAL = "<<(_EREAL_B+_EREAL_C)<<" _EREC = "<<_EREC);
}

//-------------------------------------------------------------------
// BMB Potential Test Function
//------------------------------------------------------------------- 
void YZOFrame::testBMB()
{
    int numofbins, i, isp, jsp, k;
    double testR1, testR2, testdR;
    double r, r2, ri6, x, xcube, cx1, cx2, QQ, EXP;
    FILE *fp1;
    fp1=fopen("BMB.dat","w");

    // test setup
    numofbins=1001;
    testR1=4;
    testR2=12;
    isp=1;
    jsp=1;

    // Check if atom mass and charge are ready //HARK
    if(evalprepared==0)
    {
        evalprepared=prepareeval();
        if(evalprepared==0)
        {
            WARNING("ATOM MASS AND CHARGE ARE NOT SPECIFIED. UNABLE TO EXECUTE RELAX()");
            return;
        }
    }

    // Calculate the Gaussain shape factor
    Alpha = Ewaldprecision/PP_RC;

    // Allocate the memory
    Realloc(BMB_test_R,double,numofbins);
    Realloc(BMB_test_E,double,numofbins);
    Realloc(BMB_test_F,double,numofbins);
    Realloc(CS_test_E,double,numofbins);
    Realloc(CS_test_F,double,numofbins);
    
    // Set the test point
    testdR=(testR2-testR1)/((double)(numofbins-1));
    for (i=0;i<numofbins;i++)
    {
        BMB_test_R[i]=testR1+testdR*((double)i);
    }

    // Start the test
    for (i=0;i<numofbins;i++)
    {
        r=BMB_test_R[i];
        r2=r*r;
        if(r<=PP_RC) // if the interatomic distance is shorter than the cut-off radius
        {
            // precalculate r^-6
            ri6=1.0/(r2*r2*r2);
            
            // calculate the cutoff variables
            if(r>(PP_RC-1.0))
            {
                x=r-PP_RC+1.0;
                xcube=CUBE(x);
                cx1=SQR(1.0-xcube*x);
                cx2=8.0*(1.0-xcube*x)*xcube;
            }
            else
            {
                cx1=1.0;
                cx2=0.0;
            }

            // get the species indices
            k=isp+jsp; // interaction index

            QQ=_SPCHARGEMD[isp]*_SPCHARGEMD[jsp]; // get the charge term
            
            // calculate potential
            BMB_test_E[i]=(-BMB_POT[k][0]*ri6+BMB_POT[k][1]*exp(-BMB_POT[k][2]*r))*cx1;

            CS_test_E[i]=(erfc(Alpha*r))/r*QQ;
            
            // calculate force
            BMB_test_F[i]=((-6.0*BMB_POT[k][0]*ri6/r+BMB_POT[k][1]*BMB_POT[k][2]
                        *exp(-BMB_POT[k][2]*r))*cx1+BMB_test_E[i]*cx2);

            EXP=(2.0/M_SQRT_PI)*Alpha*exp(-SQR(Alpha*r))*QQ;
            CS_test_F[i]=((CS_test_E[i]+EXP)/r);
            fprintf(fp1,"%5.4f %20.15f %20.15f %20.15f %20.15f\n",BMB_test_R[i], BMB_test_E[i], BMB_test_F[i], CS_test_E[i], CS_test_F[i]);
        }
    }
    fclose(fp1);
}

//-------------------------------------------------------------------  
// Potential Function -> Linked to y_zr_o()
//------------------------------------------------------------------- 
void YZOFrame::potential()
{    
    // Check if atom mass and charge are ready //HARK
    if(evalprepared==0)
    {
        evalprepared=prepareeval();
        if(evalprepared==0)
        {
            WARNING("ATOM MASS AND CHARGE ARE NOT SPECIFIED. UNABLE TO EXECUTE RELAX()");
            return;
        }
    }

    // prepare potential parameters
    if(potsetup==0) preparepotential();
    
    // measure Ewald time
    if(useCE){sCEREAL=clock();}
    if(usePME){sPMEREAL=clock();}

    
    // calculate potential
    y_zr_o();

    // measure Ewald time
    if(useCE){eCEREC=clock();}
    if(usePME){ePMEREC=clock();}
}

//###################################################################
//  Classical Ewald Functions
//###################################################################
//-------------------------------------------------------------------
// Enumerate K-Vectors
//-------------------------------------------------------------------
void YZOFrame::enumKV()
{
    //------------------------------------------------------------------- 
    //  In this function, following variables are prepared
    //  Mka, Mkb, Mkc; MKV, nKV, KV
    //-------------------------------------------------------------------
    const double INHOM=1.1; //The expected error in k-point estimation
    
    EComplex *p;
    int i;
    double kx, ky, kz, k2;
    int nka, nkb, nkc, ika, ikb, ikc;
        
    // Kc = (2*pi/Li)*ni and ni = Kc*Li/(2*pi)
    // So we can multiply Kc and 1/Gi to get the integer ni
    nka=(int)(Kc*_H_L1/(2.0*M_PI));
    nkb=(int)(Kc*_H_L2/(2.0*M_PI));
    nkc=(int)(Kc*_H_L3/(2.0*M_PI));
    
    // Get the max-number of K-vectors (half sphere)
    ika=(int)(INHOM/3.0*Kc*Kc*Kc*cell_V/(2.0*M_PI*2.0*M_PI));

    // Allocate K-vectors
    if(MKV < ika) {MKV=ika; Realloc(KV,K_rec,MKV);}
    
    // find K-vectors
    nKV=0;
    Mka=Mkb=Mkc=0;
    for(ika=0;ika<nka+1;ika++)
        for(ikb= (ika==0? 0:-nkb);ikb<=nkb;ikb++)
            for(ikc=((ika==0 && ikb==0)?1:-nkc);ikc<=nkc;ikc++)
            {
                //Notice that this (kx,ky,kz) is row vector, mul in left (??)
                kx=(2.0*M_PI)*(ika*_H_L1R);
                ky=(2.0*M_PI)*(ikb*_H_L2R);
                kz=(2.0*M_PI)*(ikc*_H_L3R);
                k2=kx*kx+ky*ky+kz*kz;
                if(k2 > Kc*Kc) continue;
                KV[nKV].kx=kx;
                KV[nKV].ky=ky;
                KV[nKV].kz=kz;
                KV[nKV].k2=k2;
                KV[nKV].a=ika;
                KV[nKV].b=ikb;
                KV[nKV].c=ikc;
                nKV++;
                
                if(nKV >= MKV) INFO("Error : enumKV() -> Out of KV space");
                if(Mka < ika) Mka=ika;
                if(Mkb < abs(ikb)) Mkb=abs(ikb); // Mkb can be negative
                if(Mkc < abs(ikc)) Mkc=abs(ikc); // Mkc can be negative
            }
    if(scx==NULL) p=0;
    else p=scx[0]; // assign p to scx[0]
    Realloc(p,EComplex,(_NP*(Mka+Mkb+Mkc+3)));
    INFO("CE - enumKV() : Number of Sincos Table Entries = "<<(_NP*(Mka+Mkb+Mkc+3)));
    if(p==NULL) INFO("Error : enumKV() -> Out of memory allocating sin/cos tables");
    Realloc(scx,EComplex*,(Mka+Mkb+Mkc+3)); // need to study more this part
    if(scx==NULL) INFO("Error : enumKV() -> Out of memory allocating sin/cos tables");
    scy=scx+(Mka+1);
    scz=scy+(Mkb+1);
    
    scx[0]=p; 
    for(i=1;i<=Mka;i++) scx[i]=scx[i-1]+_NP;
    scy[0]=scx[0]+(Mka+1)*_NP;
    for(i=1;i<=Mkb;i++) scy[i]=scy[i-1]+_NP;
    scz[0]=scy[0]+(Mkb+1)*_NP;
    for(i=1;i<=Mkc;i++) scz[i]=scz[i-1]+_NP;
}

//-------------------------------------------------------------------
// Filling Sin & Cos Table
//-------------------------------------------------------------------
// calculate exp(-iKx*rx), exp(-iKy*ry), exp(-iKz*rz) 
// for all atoms and all K-vectors, should be updated every time step
//-------------------------------------------------------------------
void YZOFrame::fillSinCosTables()
{
    int i,ki;
    EComplex *sc1;
    for(i=0;i<_NP;i++)
    {
        // when K-vector is zero vector (kx=0, ky=0, kz=0)
        scx[0][i].Re=1.0; scx[0][i].Im=0.0;
        scy[0][i].Re=1.0; scy[0][i].Im=0.0;
        scz[0][i].Re=1.0; scz[0][i].Im=0.0;
    }
    if(Mka>0) 
    {
        // calculate the exp(-ikx*rx) when kx=1 (for all atoms)
        sc1=scx[1];
        for(i=0;i<_NP;i++)
            sc1[i].ExpI(2.0*M_PI*_SR[i].x);
        // calculate all of the term for kx>=2 (for all atoms)
        for(ki=2;ki<=Mka;ki++) for(i=0;i<_NP;i++)
            EComplex::Mul(scx[ki-1][i], sc1[i], scx[ki][i]);
    }
    if(Mkb>0) 
    {
        // calculate the exp(-iky*ry) when ky=1 (for all atoms)
        sc1=scy[1];
        for(i=0;i<_NP;i++)
	    sc1[i].ExpI(2.0*M_PI*_SR[i].y);
        // calculate all of the term for ky>=2 (for all atoms)
        for(ki=2;ki<=Mkb;ki++) for(i=0;i<_NP;i++)
            EComplex::Mul(scy[ki-1][i], sc1[i], scy[ki][i]);
    }
    if(Mkc>0) 
    {
        // calculate the exp(-ikz*rz) when kz=1 (for all atoms)
        sc1=scz[1];
        for(i=0;i<_NP;i++)
	    sc1[i].ExpI(2.0*M_PI*_SR[i].z);
        // calculate all of the terms when kz=>2 (for all atoms)
        for(ki=2;ki<=Mkc;ki++) for(i=0;i<_NP;i++)
            EComplex::Mul(scz[ki-1][i], sc1[i], scz[ki][i]);
    }
}

//-------------------------------------------------------------------
// Initialize Classical Ewald 
//-------------------------------------------------------------------
void YZOFrame::CE_init()
{    
  // determine the cutoff radius
  if((detAlpha==0)||(detAlpha==1))
    {
      Kc=2*Alpha*Ewaldprecision; // reciprocal-space(only Ewald)
    }
      
    // Allocating sc_axby and sckk
    Realloc(sckk,EComplex,(_NP*2));
    sc_axby=sckk+_NP;

    // Calculate the K-vectors
    enumKV();

    // Print Out Ewald Information
    INFO("------------------------------------------------------------------------------");
    INFO("------------------------------------------------------------------------------");
    INFO("              Classical Ewald Parameters [Long Range Interaction] ");
    INFO("------------------------------------------------------------------------------");
    INFO("    Real Space Cutoff Radius (Rc)                 = "<<PP_RC);
    INFO("    Ewald Fourier Part Cutoff Radius (Kc)         = "<<Kc);
    INFO("    Ewald Gaussian Factor (Alpha)                 = "<<Alpha);
    INFO("    Total Number of K-Vectors                     = "<<nKV);
    INFO("    Max Number of K-Vectors                       = "<<MKV);
    INFO("    Max K-Points in A-direction                   = "<<Mka);
    INFO("    Max K-Points in B-direction                   = "<<Mkb);
    INFO("    Max K-Points in C-direction                   = "<<Mkc);
    INFO("------------------------------------------------------------------------------");
    INFO("------------------------------------------------------------------------------");
}

//-------------------------------------------------------------------
// Classical Ewald
//-------------------------------------------------------------------
void YZOFrame::CE()
{
    int i, ik, kal, kbl;
    int ka, kb, kc;
    double k2, kx, ky, kz;
    double pfac;
    
    // recalculate the system volume (in case the box is changing)
    if(conj_fixbox==0){cell_V=_H.det();}
    
    // precalculate exp(iKx*rx), exp(iKy*ry), exp(iKz*rz)
    fillSinCosTables();
    
    kal=-1; // an impossible value
    kbl=0;  // Just to avoid a warning
    for(ik=0;ik<nKV;ik++) // for all K-vectors
    {
        double UE, EXP;
        EComplex sincosk;
        // kx, ky, kz calculated in enumKV()
        kx=KV[ik].kx;
        ky=KV[ik].ky;
        kz=KV[ik].kz;
        k2=KV[ik].k2;
        ka=KV[ik].a;
        kb=KV[ik].b;
        kc=KV[ik].c;
        
        //quick sckk [ Qj*exp(iKr) ] calculation
        if(kal!=ka || kbl!=kb)  // Re-calculate sc_axby
        {
            kal=ka;
            kbl=kb;
            if(kb>=0) // if ky>=0
		for(i=0;i<_NP;i++) // for all atoms
                    EComplex::Mul(scx[ka][i], scy[kb][i], sc_axby[i]);
            else      // if ky<0
		for(i=0;i<_NP;i++) // for all atoms
                    EComplex::MulConjugate(scx[ka][i], scy[-kb][i], sc_axby[i]);
        }
        // initialize structure factor 
        sincosk.Re=0.0;
        sincosk.Im=0.0;
        
        if(kc>=0) // if kz>=0
	    for(i=0;i<_NP;i++) 
            {
		EComplex::Mul(sc_axby[i], scz[kc][i], sckk[i]);
		sckk[i]*=_SPCHARGEMD[species[i]];
		sincosk+=sckk[i];
            }
        else      // if kz<0
	    for(i=0;i<_NP;i++)
            {
		EComplex::MulConjugate(sc_axby[i], scz[-kc][i], sckk[i]);
		sckk[i]*=_SPCHARGEMD[species[i]];
		sincosk+=sckk[i];
            }

        // calculate the reciprocal energy pre-factor
        EXP=((8.0*M_PI)/cell_V)*exp(-k2/(4.0*SQR(Alpha)))/k2;

        // calculate the reciprocal force
        for(i=0;i<_NP;i++)
        {
            double ad=EXP*(sckk[i].Im*sincosk.Re-sckk[i].Re*sincosk.Im);
            _FREC[i].x+=ad*((double)kx); _FREC[i].y+=ad*((double)ky); _FREC[i].z+=ad*((double)kz);
        }
        pfac=1.0/k2+1/(4.0*SQR(Alpha));
        UE=EXP/2.0*sincosk.Norm2();
        
        // Potential Energy Calculation        
        _EREC+=UE;
        
        // Virial Stress Calculation
        _VIRIAL[0][0]+=UE*(1.0-2.0*((double)(kx*kx))*pfac); //xx
        _VIRIAL[1][1]+=UE*(1.0-2.0*((double)(ky*ky))*pfac); //yy
        _VIRIAL[2][2]+=UE*(1.0-2.0*((double)(kz*kz))*pfac); //zz
        _VIRIAL[0][1]+=UE*(-2.0*((double)(kx*ky))*pfac);  //xy
        _VIRIAL[1][0]+=UE*(-2.0*((double)(kx*ky))*pfac);  //yx
        _VIRIAL[0][2]+=UE*(-2.0*((double)(kx*kz))*pfac);  //xz
        _VIRIAL[2][0]+=UE*(-2.0*((double)(kx*kz))*pfac);  //zx
        _VIRIAL[1][2]+=UE*(-2.0*((double)(ky*kz))*pfac);  //yz
        _VIRIAL[2][1]+=UE*(-2.0*((double)(ky*kz))*pfac);  //zy
    }
    //INFO("[CE] Reciprocal Energy Calculated by CE = "<<_EREC);
}

//-------------------------------------------------------------------
//  Clear Particle Mesh Ewald
//-------------------------------------------------------------------
void YZOFrame::CE_clear()
{
    // free variables used in the Classical Ewald summation    
    if(scx) { free(scx[0]); free(scx); }
    if(sckk){ free(sckk); free(KV); }

    // free variables used in the NMC reduced CE
    if(MCscx) { free(MCscx[0]); free(MCscx); }
    if(MCsckk){ free(MCsckk); free(MCsincosk); }
}

//###################################################################
//  Prepareation of NNO-included New Monte Carlo 
//###################################################################
//-------------------------------------------------------------------
// Rr-curve Initialization //HARK
//-------------------------------------------------------------------
void YZOFrame::NMC_init()
{
  YZr_Rr[0][0]=0.000000;   YZr_Rr[0][1]=0.000000;
  YZr_Rr[1][0]=1.000000;   YZr_Rr[1][1]=0.590000;
  YZr_Rr[2][0]=1.125000;   YZr_Rr[2][1]=0.769667;
  YZr_Rr[3][0]=1.250000;   YZr_Rr[3][1]=0.938400;
  YZr_Rr[4][0]=1.375000;   YZr_Rr[4][1]=1.099182;
  YZr_Rr[5][0]=1.500000;   YZr_Rr[5][1]=1.254000;
  YZr_Rr[6][0]=1.625000;   YZr_Rr[6][1]=1.404231;
  YZr_Rr[7][0]=1.750000;   YZr_Rr[7][1]=1.550857;
  YZr_Rr[8][0]=1.875000;   YZr_Rr[8][1]=1.694600;
  YZr_Rr[9][0]=2.000000;   YZr_Rr[9][1]=1.836000;
  YZr_Rr[10][0]=2.125000;   YZr_Rr[10][1]=1.975471;
  YZr_Rr[11][0]=2.250000;   YZr_Rr[11][1]=2.113333;
  YZr_Rr[12][0]=2.375000;   YZr_Rr[12][1]=2.249842;
  YZr_Rr[13][0]=2.500000;   YZr_Rr[13][1]=2.385200;
  YZr_Rr[14][0]=2.625000;   YZr_Rr[14][1]=2.519571;
  YZr_Rr[15][0]=2.750000;   YZr_Rr[15][1]=2.653091;
  YZr_Rr[16][0]=2.875000;   YZr_Rr[16][1]=2.785870;
  YZr_Rr[17][0]=3.000000;   YZr_Rr[17][1]=2.918000;
  YZr_Rr[18][0]=3.125000;   YZr_Rr[18][1]=3.049560;
  YZr_Rr[19][0]=3.250000;   YZr_Rr[19][1]=3.180615;
  YZr_Rr[20][0]=3.375000;   YZr_Rr[20][1]=3.311222;
  YZr_Rr[21][0]=3.500000;   YZr_Rr[21][1]=3.441429;
  YZr_Rr[22][0]=3.625000;   YZr_Rr[22][1]=3.571276;
  YZr_Rr[23][0]=3.750000;   YZr_Rr[23][1]=3.700800;
  YZr_Rr[24][0]=3.875000;   YZr_Rr[24][1]=3.830032;
  YZr_Rr[25][0]=4.000000;   YZr_Rr[25][1]=3.959000;
  YZr_Rr[26][0]=4.125000;   YZr_Rr[26][1]=4.087727;
  YZr_Rr[27][0]=4.250000;   YZr_Rr[27][1]=4.216235;
  YZr_Rr[28][0]=4.375000;   YZr_Rr[28][1]=4.344543;
  YZr_Rr[29][0]=4.500000;   YZr_Rr[29][1]=4.472667;
  YZr_Rr[30][0]=4.625000;   YZr_Rr[30][1]=4.600622;
  YZr_Rr[31][0]=4.750000;   YZr_Rr[31][1]=4.728421;
  YZr_Rr[32][0]=4.875000;   YZr_Rr[32][1]=4.856077;
  YZr_Rr[33][0]=5.000000;   YZr_Rr[33][1]=4.983600;
  YZr_Rr[34][0]=5.125000;   YZr_Rr[34][1]=5.111000;
  YZr_Rr[35][0]=5.250000;   YZr_Rr[35][1]=5.238286;
  YZr_Rr[36][0]=5.375000;   YZr_Rr[36][1]=5.365465;
  YZr_Rr[37][0]=5.500000;   YZr_Rr[37][1]=5.492545;
  YZr_Rr[38][0]=5.625000;   YZr_Rr[38][1]=5.619533;
  YZr_Rr[39][0]=6.000000;   YZr_Rr[39][1]=6.000000;
  nYZr_Rr=40;
}

void YZOFrame::NMC_Rr()
{
    int i;
    double R_tmp1,R_tmp2,r_tmp1,r_tmp2;
    
    R_tmp1=0.0; R_tmp2=0.0; r_tmp1=0.0; r_tmp2=0.0; // just to avoid warning

    if(NNO_Rr_type==1) // YZr conversion
      {
	for(i=0;i<(nYZr_Rr-1);i++)
	  {
	    if((YZr_Rr[i][0]<NNO_R)&&(YZr_Rr[i+1][0]>=NNO_R))
	      {
		R_tmp1=YZr_Rr[i][0]; 
		R_tmp2=YZr_Rr[i+1][0];
		r_tmp1=YZr_Rr[i][1]; 
		r_tmp2=YZr_Rr[i+1][1];
	      }
	  }
      }
    else // ZrY conversion
      {
	for(i=0;i<(nYZr_Rr-1);i++)
	  {
	    if((YZr_Rr[i][1]<NNO_R)&&(YZr_Rr[i+1][1]>=NNO_R))
	      {
		R_tmp1=YZr_Rr[i][1];
		R_tmp2=YZr_Rr[i+1][1];
		r_tmp1=YZr_Rr[i][0];
		r_tmp2=YZr_Rr[i+1][0];
	      }
	 }
      }
    NNO_r=(NNO_R-R_tmp1)/(R_tmp2-R_tmp1)*(r_tmp2-r_tmp1)+r_tmp1;
}

//###################################################################
//  NNO-included New Monte Carlo
//###################################################################
//-------------------------------------------------------------------
// NNO-included New Monte Carlo //HARK
//-------------------------------------------------------------------
void YZOFrame::NMC()
{
    int i, j, nY, nZr, Yindex, Zrindex, Zrnum, Ynum;
    double vratioYZr, vratioZrY, EPOT1_1, EPOT1_2, dEPOT1, EPOT2_1, EPOT2_2, dEPOT2, EPOT3_1, EPOT3_2, dEPOT3;
    double tmpdbl, catdisp;
    Vector3 tmpvec, catdispvec;

    //-------------------------------------------------------------------
    // Energy output file
    //-------------------------------------------------------------------
    //FILE *fp1; 
    //FILE *fp2;
    FILE *fp3;
    //fp1=fopen("Index.dat","w"); 
    //fp2=fopen("Energy.dat","w");
    fp3=fopen("EnergyComp.dat","w");
    
    //-------------------------------------------------------------------
    // Calculate Velocity Conversion Ratio
    //-------------------------------------------------------------------
    vratioZrY=sqrt(_SPMASS[0]/_SPMASS[3]); // velocity ratio (Zr->Y)
    vratioYZr=sqrt(_SPMASS[3]/_SPMASS[0]); // velocity ratio (Y->Zr)
    
    //-------------------------------------------------------------------
    // MD Preparation
    //-------------------------------------------------------------------
    switch (integrator_type) {
    case(0): INFO("MD with Gear6 integrator");   break;
    case(1): INFO("MD with VVerlet integrator"); break;
    default: break; }
    INFO("    ");
    INFO("===========================================================================");
    INFO(" Preliminary Steps");
    INFO("===========================================================================");
    INFO("    ");
    INFO("===========================================================================");
    
    //-------------------------------------------------------------------
    // Prepare Yttrium Database
    //-------------------------------------------------------------------
    nyttriums=0; nY=0;
    for(i=0;i<_NP;i++){if(species[i]==3){nyttriums++;}}
    INFO("NMC - number of yttriums = "<<nyttriums);
    Realloc(yttriumdata,int,nyttriums); // reallocate the Y database
    for(i=0;i<_NP;i++){if(species[i]==3){yttriumdata[nY]=i; nY++;}}
    INFO("NMC - end of data : yttriumdata["<<nY<<"] = "<<yttriumdata[nY-1]);
    INFO("---------------------------------------------------------------------------");
    
#if 1
    //------------------------------------------------------------------- 
    // Prepare Zirconium Database  - For Random Switch
    //------------------------------------------------------------------- 
    nzirconiums=0; nZr=0;
    for(i=0;i<_NP;i++){if(species[i]==0){nzirconiums++;}}
    INFO("NMC - number of zirconiums = "<<nzirconiums);
    Realloc(zirconiumdata,int,nzirconiums); // reallocate the Zr database
    for(i=0;i<_NP;i++){if(species[i]==0){zirconiumdata[nZr]=i; nZr++;}}
    INFO("NMC - end of data : zirconiumdata["<<nZr<<"] = "<<zirconiumdata[nZr-1]); 
#endif

#if 0
    //-------------------------------------------------------------------
    // Reallocate Zirconium Database - For NN Switch
    //-------------------------------------------------------------------
    Realloc(zirconiumdata,int,maxNNO); // complete the Zr database 
    Realloc(zirconiumdist,double,maxNNO); // complete the Zr database 
#endif
    
    //-------------------------------------------------------------------
    // Allocation of Variables for NMC
    //-------------------------------------------------------------------
    Realloc(BYNNO,int,maxNNO); // allocate the Y-NNO atom index array
    Realloc(BZrNNO,int,maxNNO); // allocate the Zr-NNO atom index array
    Realloc(BvecYNNO,Vector3,maxNNO); // allocate the Y-NNO atom displacement vector array
    Realloc(BvecZrNNO,Vector3,maxNNO); // allocate the Zr-NNO atom displacement vector array    

    //-------------------------------------------------------------------
    // General Variables for NMC
    //-------------------------------------------------------------------
    MDcounts=0; // initialize MD step counts
    totMCtrials=0; // initialize MC counts
    totMCacc=0;  // initialize accepted MC counts
    MCacc=0; // initialize acceptance index
    checkneighborlist=0; // initialize checkNNlist index

    //-------------------------------------------------------------------
    // Compute the Initial Potential and Initialize Variables
    //-------------------------------------------------------------------
    potential(); // just to make Ewald dataset ready
    EPOT1_1=0.0;  // just to avoid warning
    EPOT1_2=0.0;  // just to avoid warning
    EPOT2_1=0.0;  // just to avoid warning
    EPOT2_2=0.0;  // just to avoid warning
    EPOT3_1=0.0;  // just to avoid warning
    EPOT3_2=0.0;  // just to avoid warning

    //-------------------------------------------------------------------
    // Prepare the YZr and ZrY r-R Curves and Reduced Potential Parameters
    //-------------------------------------------------------------------
    NMC_preparepotential(); // prepare reduced potential parameters
    NMC_init();
    if(useCE==1){PME_init();}
    
    //-------------------------------------------------------------------
    // Start NNO-included MC
    //-------------------------------------------------------------------
    INFO("   ");
    INFO("===========================================================================");
    INFO(" Beginning the NNO-included New Monte Carlo");
    INFO("===========================================================================");
    INFO("   ");
    sNMC=clock();
    for(MCcounts=0;MCcounts<MCjumps;MCcounts++)
      {
        //INFO("===========================================================================");
        //INFO(" Current MC Step =  "<<MCcounts);
        //INFO("===========================================================================");
	if(MCcounts==0){INFO("---------------------------------------------------------------------------");}
	
	//------------------------------------------------------------------- 
	// Calculate the Potential of Initial Configuration
	//-------------------------------------------------------------------
	if((MCcounts==0)||(MCacc==1)||(checkneighborlist==1))
	  {
	    if(checkneighborlist==1){NbrList_reconstruct(); checkneighborlist=0;}
	    //potential();
	    NMC_N1_potential();
	    EPOT1_1=_EPOT;
	    NMC_N2_potential();
	    EPOT2_1=_EPOT;
	    NMC_N3_potential();
	    EPOT3_1=_EPOT;
	    MCacc=0;
	  }
	
	//-------------------------------------------------------------------
        // Initialize the Number of Trial Atoms
	//-------------------------------------------------------------------
        numtrialatoms=0;
    
	//-------------------------------------------------------------------
	// Pick a Random Yttrium and Add to the Trial Atom List
	//-------------------------------------------------------------------
	Ynum=(int)floor(drand48()*((double)nyttriums));
	Yindex = yttriumdata[Ynum];
        trialatomlist[numtrialatoms]=Yindex;
        trialatomindex[Yindex]=1;
        numtrialatoms++;
	if(species[Yindex]!=3){WARNING("NMC - Yindex is wrong");}

#if 0
	//-------------------------------------------------------------------
	// Prepare NN Zirconium Database - Based on the NBL
	//-------------------------------------------------------------------
	nZr=0; // initialize the number of NN Zr's
	for(i=0;i<nn[Yindex];i++) // for all neighbors of the picked yttrium
	  {
	    j=nindex[Yindex][i]; // atom index of the neighbor
	    if (species[j]==0) // if the target is Zr
	      {
		tmpvec=(_SR[j]-_SR[Yindex]); // displacement vector
		tmpvec.subint(); // apply PBC
		tmpvec=_H*tmpvec; // convert to real-size vector
		tmpdbl=sqrt(tmpvec.norm2()); // the distance between Y and neighbor Zr
		if(tmpdbl<catmigdist) // make the database of NN Zr's
		  {
		    //INFO("NMC - interatomic distance of switching pair ["<<nZr<<"] = "<<tmpdbl);
		    zirconiumdata[nZr]=j;
		    zirconiumdist[nZr]=tmpdbl;
		    nZr++;
		  }
	      }
	  }
	nzirconiums=nZr;
#endif

#if 0
	//-------------------------------------------------------------------
	// Prepare NN Zirconium Database - Based on the NBL (Rc=catmigdist)
	//-------------------------------------------------------------------
	nZr=0; // initialize the number of NN Zr's
	for(i=0;i<nn[Yindex];i++) // for all neighbors of the picked yttrium
	  {
	    j=nindex[Yindex][i]; // atom index of the neighbor
	    if (species[j]==0) // if the target is Zr
	      {
		zirconiumdata[nZr]=j;
		nZr++;
	      }
	  }
	nzirconiums=nZr;
#endif
	
	//-------------------------------------------------------------------
	// Pick a Random Zirconium and Add to the Trial Atom List
	//-------------------------------------------------------------------
	Zrnum=(int)floor(drand48()*((double)nzirconiums));
	Zrindex = zirconiumdata[Zrnum];
        trialatomlist[numtrialatoms]=Zrindex;
        trialatomindex[Zrindex]=1;
        numtrialatoms++;
	if(species[Zrindex]!=0){WARNING("NMC - Zrindex is wrong");}
	    
	//-------------------------------------------------------------------
	// Get the Cation Displacement Unit Vector 
	//-------------------------------------------------------------------
	catdispvec=(_SR[Yindex]-_SR[Zrindex]); // displacement vector (Zr->Y)
	catdispvec.subint(); // apply PBC
	catdispvec=_H*catdispvec; // convert to real-size vector 
	catdisp=sqrt(catdispvec.norm2()); // distance between Y and neighbor Zr
	catdispvec/=catdisp; // transform to the unit vector
        
	//-------------------------------------------------------------------
	// Move NN Oxygens of Picked Yttrium
	//-------------------------------------------------------------------
	//INFO("---------------------------------------------------------------------------");
	//INFO(" Making Trial Moves for Yttrium NNO");
	//INFO("---------------------------------------------------------------------------");
	NNO_Rr_type=1; // YZr move
	BnYNNO=0; // initialize the number of NNOs
	for(i=0;i<nn[Yindex];i++) // for all neighbors of the picked yttrium
	  {
	    j=nindex[Yindex][i]; // atom index of the neighbor
	    if(species[j]==1) // if the target is NNO
	      {
		tmpvec=(_SR[j]-_SR[Yindex]); // displacement vector
		tmpvec.subint(); // apply PBC
		tmpvec=_H*tmpvec; // convert to real-size vector
		tmpdbl=sqrt(tmpvec.norm2()); // distance between Y and neighbor O
		if(tmpdbl<=NNO_Rc) // for the first NN oxygens of Y
		  {
		    BYNNO[BnYNNO]=j; // backup the oxygen atom index
		    BvecYNNO[BnYNNO]=_SR[j]; // backup original position
		    NNO_R=tmpdbl; NMC_Rr(); // calculate the new position
		    tmpvec/=tmpdbl; // making unit vector
		    tmpvec*=((NNO_r-NNO_R)+anfluc*(drand48()-0.5)*2.0); // trial move vector
		    tmpvec=_Hinv*tmpvec; // convert to scaled vector
		    _SR[j]=tmpvec+_SR[j]; // new position
		    BnYNNO++;
		    trialatomlist[numtrialatoms]=j; // add to the trial atom index
		    trialatomindex[j]=1;
		    numtrialatoms++; // increase the trial atom count

		    // check parts
		    //INFO("NMC - Y "<<BnYNNO<<"th NNO atom index = "<<j);
		    //INFO("NMC - Y "<<BnYNNO<<"th NNO displacement = "<<(NNO_r-NNO_R));
		    //if(BnYNNO>maxNNO){WARNING("NMC - number of NNO oxygens exceeds the maxNNO");}
		  }
	      }
	  }
	//INFO("---------------------------------------------------------------------------");
	
	//-------------------------------------------------------------------
	// Move NN Oxygens of Picked Zirconium
	//-------------------------------------------------------------------
	//INFO(" Making Trial Moves for Zirconium NNO");
	//INFO("---------------------------------------------------------------------------");
	NNO_Rr_type=2; // ZrY move
	BnZrNNO=0; // initialize the number of NNOs
	for(i=0;i<nn[Zrindex];i++) // for all neighbors of the picked yttrium
	  {
	    j=nindex[Zrindex][i]; // atom index of the neighbor
	    if(species[j]==1) // if the target is NNO
	      {
		tmpvec=(_SR[j]-_SR[Zrindex]); // displacement vector
		tmpvec.subint(); // apply PBC
		tmpvec=_H*tmpvec; // transform to real vector
		tmpdbl=sqrt(tmpvec.norm2()); // distance between Y and neighbor Zr
		if(tmpdbl<=NNO_Rc) // for the first NN oxygens of Zr
		  {
		    BZrNNO[BnZrNNO]=j; // backup the oxygen atom index
		    BvecZrNNO[BnZrNNO]=_SR[j]; // backup original position
		    NNO_R=tmpdbl; NMC_Rr(); // calculate the new position
		    tmpvec/=tmpdbl; // making unit vector
		    tmpvec*=((NNO_r-NNO_R)+anfluc*(drand48()-0.5)*2.0); // trial move vector
		    tmpvec=_Hinv*tmpvec; // convert to scaled vector	
		    _SR[j]=tmpvec+_SR[j]; // new position
		    BnZrNNO++;
		    if(trialatomindex[j]==0) // only if j is not a Y's NNO
		      {
		        trialatomlist[numtrialatoms]=j; // add to the trial atom index
		        trialatomindex[j]=1;
		        numtrialatoms++; // increase the trial atom count
		      }

		    // check parts
		    //INFO("NMC - Zr "<<BnZrNNO<<"th NNO atom index = "<<j);
		    //INFO("NMC - Zr "<<BnYNNO<<"th NNO displacement = "<<(NNO_r-NNO_R));
		    //if(BnZrNNO>maxNNO){INFO("NMC - number of NNO oxygens exceeds the maxNNO");}
		  }
	      }
	  }
	//INFO("---------------------------------------------------------------------------");
	//INFO("NMC - picked Y list index = "<<Ynum<<"  |  atom index = "<<Yindex);
	//INFO("NMC - picked Zr list index = "<<Zrnum<<"  |  atom index = "<<Zrindex);
	//INFO("NMC - interatomic distance = "<<catdisp);
	//INFO("---------------------------------------------------------------------------");
        
	//------------------------------------------------------------------- 
	// Make the Exchange Trial Move
	//------------------------------------------------------------------- 
	species[Yindex]=0; // change the species (Y to Zr)
	species[Zrindex]=3; // change the species (Zr to Y)

        //------------------------------------------------------------------- 
	// Recalculate the Potential
	//-------------------------------------------------------------------
	//if(NbrList_needrefresh()){checkneighborlist=1; INFO("NMC - NBL reconstruction requested ******");} potential();
	NMC_R1_potential(); // to avoid calculation redundancy
	EPOT1_2=_EPOT;
	NMC_R2_potential(); // to avoid calculation redundancy
	EPOT2_2=_EPOT;
	NMC_R3_potential(); // to avoid calculation redundancy
	EPOT3_2=_EPOT;
        
	//------------------------------------------------------------------- 
	// Calculate the Boltzmann Factor, Make a Decision and Record Energy
	//------------------------------------------------------------------- 
	//dEPOT=dEREC+dEREAL;
	dEPOT1=EPOT1_2-EPOT1_1;
	dEPOT2=EPOT2_2-EPOT2_1;
	dEPOT3=EPOT3_2-EPOT3_1;
	tmpdbl=exp(-dEPOT1/(KB*_TDES));
	if(tmpdbl>=1.0){MCacc=1;}
	else if(drand48()<=tmpdbl){MCacc=1;}
	else {MCacc=0;}
	//fprintf(fp1,"%d %d %d %d\n",Yindex,Zrindex,BnYNNO,BnZrNNO);
	//fprintf(fp2,"%20.12f %20.12f %20.12f %20.12f\n",catdisp,_EREAL,_EREC,dEPOT);
	//fprintf(fp2,"%20.12f %20.12f %20.12f %20.12f\n",catdisp,(_EREAL_B+_EREAL_C),_EREC,dEPOT);
	fprintf(fp3,"%20.12f %20.12f %20.12f %20.12f\n",catdisp,dEPOT1,dEPOT2,dEPOT3);

	//------------------------------------------------------------------- 
	// Retract the Trialatom Indices
	//------------------------------------------------------------------- 
        for(i=0;i<numtrialatoms;i++)
	  {
            trialatomindex[trialatomlist[i]]=0;
            trialatomlist[i]=0;
	  }
	
	//------------------------------------------------------------------- 
	// Accept or Reject the Move
	//-------------------------------------------------------------------
	if(MCacc==1)
	  {
	    // rescale the velocities
	    _VSR[Yindex]*=vratioYZr; _VR[Yindex]*=vratioYZr;
	    _VSR[Zrindex]*=vratioZrY; _VR[Zrindex]*=vratioZrY;
	    
	    // check parts
	    //if((yttriumdata[Ynum]!=Yindex)||(zirconiumdata[Zrnum]!=Zrindex))
	    //{WARNING("NMC - index error, check the source code");}

	    // update yttrium and zirconium lists
	    yttriumdata[Ynum]=Zrindex;
	    zirconiumdata[Zrnum]=Yindex;

	    // print out the results
	    totMCacc++;
	    //INFO("---------------------------------------------------------------------------");
	    //INFO("NMC - EPOT[old] = "<<EPOT1_1<<" eV  |  EPOT[new] = "<<EPOT1_2<<" eV");
	    //INFO("NMC - potential energy change = "<<dEPOT1<<" eV");
	    //INFO("NMC - acceptance probability = "<<tmpdbl);
	    //INFO("NMC - trial switch accepted ***********************************************");
	    //INFO("---------------------------------------------------------------------------");
            //INFO("NMC - Trial = "<<MCcounts<<"  Accepted = "<<totMCacc<<"  dEPOT = "<<dEPOT1<<"  Pacc = "<<tmpdbl);
            INFO("NMC - Trial = "<<MCcounts<<"  Accepted = "<<totMCacc<<"  dEPOT1 = "<<dEPOT1<<"  dEPOT2 = "<<dEPOT2<<"  dEPOT3 = "<<dEPOT3<<"  Pacc = "<<tmpdbl);
	  }
	else if(MCacc==0)
	  {
	    // retract the cation exchange
	    species[Yindex]=3; // retract the trial move
	    species[Zrindex]=0; // retract the trial move
	    
	    // retract the oxygen trial moves (YZr first / ZrY second)
	    for(i=0;i<BnZrNNO;i++){_SR[BZrNNO[i]]=BvecZrNNO[i];}
	    for(i=0;i<BnYNNO;i++){_SR[BYNNO[i]]=BvecYNNO[i];}
	    
	    // print out the results
	    //INFO("---------------------------------------------------------------------------");
	    //INFO("NMC - EPOT[old] = "<<EPOT1_1<<" eV  |  EPOT[new] = "<<EPOT1_2<<" eV");
	    //INFO("NMC - potential energy change = "<<dEPOT1<<" eV");
	    //INFO("NMC - acceptance probability = "<<tmpdbl);
	    //INFO("NMC - trial switch rejected");
	    //INFO("---------------------------------------------------------------------------");
            //INFO("NMC - Trial = "<<MCcounts<<"  dEPOT = "<<dEPOT1<<"  Pacc = "<<tmpdbl);
            INFO("NMC - Trial = "<<MCcounts<<"  dEPOT1 = "<<dEPOT1<<"  dEPOT2 = "<<dEPOT2<<"  dEPOT3 = "<<dEPOT3<<"  Pacc = "<<tmpdbl);
	  }
	totMCtrials++;
        
	//-------------------------------------------------------------------
	// MC Summary
	//------------------------------------------------------------------- 
	//if((MCacc==1)&&((totMCacc%MCcheckfreq)==0))
	//{
	//INFO("===========================================================================");
	//INFO("NMC - number of total trials = "<<totMCtrials);
	//INFO("NMC - number of accepted = "<<totMCacc);	
	//INFO("NMC - acceptance ratio = "<<(((double)totMCacc)/((double)(totMCtrials+1))));
	//INFO("===========================================================================");
	//}
        
	//-------------------------------------------------------------------
	// MD Relaxation Part 2
	//------------------------------------------------------------------- 
	if((MCacc==1)&&((totMCacc%MDstep1freq)==0)&&((totMCacc%MDstep2freq)!=0))
	  {
	    if((useCE==1)&&(usePME==0))
	      {
		useCE=0; usePME=1;
		MDRELAX(MDstep1); 
		useCE=1; usePME=0;
	      }
	    else
	      {
		MDRELAX(MDstep1);
	      }
	    checkneighborlist=1;
	    for(i=0;i<maxnumofpairs;i++){realpairE1[i]=0.0; realpairE2[i]=0.0;}
	  }

	//-------------------------------------------------------------------
	// MD Relaxation Part 3 : Rescale Temperature
	//-------------------------------------------------------------------
	if((MCacc==1)&&((totMCacc%MDstep2freq)==0))
	  {
    	    INFO("--------------------------------------------------------------------------");
            INFO("NMC - Beginning Temperature Scaling");
            INFO("--------------------------------------------------------------------------"); 
	    scalevelocity=1; // turn on velocity scaler
	    if((useCE==1)&&(usePME==0))
	      {
		useCE=0; usePME=1;
		MDRELAX(MDstep2);
		useCE=1; usePME=0;
	      }
	    else
	      {
		MDRELAX(MDstep2);
	      }
	    scalevelocity=0; // turn off velocity scaler
	    checkneighborlist=1;
	    for(i=0;i<maxnumofpairs;i++){realpairE1[i]=0.0; realpairE2[i]=0.0;}
	  }
      } //end of NMC
    eNMC=clock();
    tmpdbl=((double(eNMC))-(double(sNMC)))/CLOCKS_PER_SEC;
    INFO("---------------------------------------------------------------------------");
    INFO(" ");
    INFO("===========================================================================");
    INFO("NMC - execution time for "<<MCjumps<<" jumps = "<<tmpdbl);
    INFO("===========================================================================");    
    if(saveprop) pf.write(this);
    //fclose(fp1); 
    //fclose(fp2);
    fclose(fp3);
}

//###################################################################
//  CG-NNO-included New Monte Carlo
//###################################################################
//-------------------------------------------------------------------
// CG-NNO-included New Monte Carlo //HARK
//-------------------------------------------------------------------
void YZOFrame::CGNMC()
{
    int i, j, nY, nZr, Yindex, Zrindex, Zrnum, Ynum;
    double vratioYZr, vratioZrY;
    double tmpdbl, catdisp;
    Vector3 tmpvec, catdispvec;

    //-------------------------------------------------------------------
    // Radius output files
    //-------------------------------------------------------------------
    FILE *fp1;
    FILE *fp2;
    fp1=fopen("YZrNNO.dat","w");
    fp2=fopen("ZrYNNO.dat","w");

    //-------------------------------------------------------------------
    // Prepare the Neighbor List and Check if atom mass and charge are ready
    //-------------------------------------------------------------------
    potential();
    
    //-------------------------------------------------------------------
    // Calculate Velocity Conversion Ratio
    //-------------------------------------------------------------------
    vratioZrY=sqrt(_SPMASS[0]/_SPMASS[3]); // velocity ratio (Zr->Y)
    vratioYZr=sqrt(_SPMASS[3]/_SPMASS[0]); // velocity ratio (Y->Zr)
    
    //-------------------------------------------------------------------
    // MD Preparation
    //-------------------------------------------------------------------
    //switch (integrator_type) {
    //case(0): INFO("MD with Gear6 integrator");   break;
    //case(1): INFO("MD with VVerlet integrator"); break;
    //default: break; }
    INFO("    ");
    INFO("===========================================================================");
    INFO(" Preliminary Steps");
    INFO("===========================================================================");
    INFO("    ");
    INFO("===========================================================================");
    
    //-------------------------------------------------------------------
    // Prepare Yttrium Database
    //-------------------------------------------------------------------
    nyttriums=0; nY=0;
    for(i=0;i<_NP;i++){if(species[i]==3){nyttriums++;}}
    INFO("CGNMC - number of yttriums = "<<nyttriums);
    Realloc(yttriumdata,int,nyttriums); // complete the Y database
    for(i=0;i<_NP;i++){if(species[i]==3){yttriumdata[nY]=i; nY++;}}
    INFO("CGNMC - end of data : yttriumdata["<<nY<<"] = "<<yttriumdata[nY-1]);
    INFO("---------------------------------------------------------------------------");
    
    //-------------------------------------------------------------------
    // Prepare Zirconium Database  - For Random Switch
    //-------------------------------------------------------------------
    nzirconiums=0; nZr=0;
    for(i=0;i<_NP;i++){if(species[i]==0){nzirconiums++;}}
    INFO("CGNMC - number of zirconiums = "<<nzirconiums);
    Realloc(zirconiumdata,int,nzirconiums); // complete the Zr database
    for(i=0;i<_NP;i++){if(species[i]==0){zirconiumdata[nZr]=i; nZr++;}}
    INFO("CGNMC - end of data : zirconiumdata["<<nZr<<"] = "<<zirconiumdata[nZr-1]);
    
    //-------------------------------------------------------------------
    // Allocation of Variables for CGNMC
    //-------------------------------------------------------------------
    Realloc(CGNMC_YNNO_I,int,maxNNO); // allocate the Y oxygen neighbor atom index array
    Realloc(CGNMC_ZrNNO_I,int,maxNNO); // allocate the Zr oxygen neighbor atom index array
    Realloc(CGNMC_YNNO_R1,double,maxNNO); // allocate the Y oxygen neighbor atom distance array
    Realloc(CGNMC_YNNO_R2,double,maxNNO); // allocate the Y oxygen neighbor atom distance array
    Realloc(CGNMC_ZrNNO_R1,double,maxNNO); // allocate the Zr oxygen neighbor atom distance array
    Realloc(CGNMC_ZrNNO_R2,double,maxNNO); // allocate the Zr oxygen neighbor atom distance array
    Realloc(_SR_CGNMC,Vector3,_NP); // allocate the Zr oxygen neighbor atom distance array
    
    //-------------------------------------------------------------------
    // General Variables for CGNMC
    //-------------------------------------------------------------------
    MDcounts=0; // initialize MD step counts
    totMCtrials=0; // initialize MC counts
    totMCacc=0;  // initialize accepted MC counts
     
    //-------------------------------------------------------------------
    // Reallocate Zirconium Database - For NN Switch
    //-------------------------------------------------------------------
    //Realloc(zirconiumdata,int,500); // complete the Zr database 
    //Realloc(zirconiumdist,double,500); // complete the Zr database 
    
    //-------------------------------------------------------------------
    // Start //CG-NNO-included MC
    //-------------------------------------------------------------------
    INFO("   ");
    INFO("===========================================================================");
    INFO(" Beginning the NNO-included New Monte Carlo");
    INFO("===========================================================================");
    INFO("   ");
    EPOT_1=0.0; // just to avoid warning
    EPOT_2=0.0; // just to avoid warning
    MCacc=0; // initialize acceptance index

    sCGNMC=clock();
    for(MCcounts=0;MCcounts<MCjumps;MCcounts++)
      {
	INFO("===========================================================================");
        INFO(" Current MC Step =  "<<MCcounts);
        INFO("===========================================================================");

	//------------------------------------------------------------------- 
	// Calculate the Potential of Initial Configuration
	//-------------------------------------------------------------------
	if(MCcounts==0)
	  {
	    potential();
	    EPOT_1=_EPOT;
	    for(i=0;i<_NP;i++){_SR_CGNMC[i]=_SR[i];}
	  }

	if(MCacc==1)
	  {
	    EPOT_1=EPOT_2;
	    for(i=0;i<_NP;i++){_SR_CGNMC[i]=_SR[i];}
	  }

	//-------------------------------------------------------------------
	// Pick a Random Yttrium and Add to the Trial Atom List
	//-------------------------------------------------------------------
	Ynum=(int)floor(drand48()*((double)nyttriums));
	Yindex = yttriumdata[Ynum];
	if(species[Yindex]!=3){WARNING("CGNMC - Yindex is wrong");}
	    
	//-------------------------------------------------------------------
	// Prepare NN Zirconium Database 
	//-------------------------------------------------------------------
	//nZr=0; // initialize the number of NN Zr's
	//for(i=0;i<nn[Yindex];i++) // for all neighbors of the picked yttrium
	//{
	//    j=nindex[Yindex][i]; // atom index of the neighbor
	//    if (species[j]==0) // if the target is Zr
	//      {
	//	tmpvec=(_SR[j]-_SR[Yindex]); // displacement vector
	//	tmpvec.subint(); // apply PBC
	//	tmpvec=_H*tmpvec; // convert to real-size vector
	//	tmpdbl=sqrt(tmpvec.norm2()); // the distance between Y and neighbor Zr
	//	if(tmpdbl<MIGrange) // make the database of NN Zr's
	//	  {
	//	    INFO("CGNMC - interatomic distance of switching pair ["<<nZr<<"] = "<<tmpdbl);
	//	    zirconiumdata[nZr]=j;
	//	    zirconiumdist[nZr]=tmpdbl;
	//	    nZr++;
	//	  }
	//      }
	//  }
	//nzirconiums=nZr;
	
	//-------------------------------------------------------------------
	// Pick a Random Zirconium and Add to the Trial Atom List
	//-------------------------------------------------------------------
	Zrnum=(int)floor(drand48()*((double)nzirconiums));
	Zrindex = zirconiumdata[Zrnum];
	if(species[Zrindex]!=0){WARNING("CGNMC - Zrindex is wrong");}

	//-------------------------------------------------------------------
	// Get the Cation Displacement Unit Vector 
	//-------------------------------------------------------------------
	catdispvec=(_SR[Yindex]-_SR[Zrindex]); // displacement vector (Zr->Y)
	catdispvec.subint(); // apply PBC
	catdispvec=_H*catdispvec; // convert to real-size vector 
	catdisp=sqrt(catdispvec.norm2()); // distance between Y and neighbor Zr
	catdispvec/=catdisp; // transform to the unit vector
	INFO("CGNMC - interatomic distance of chosen switching pair = "<<catdisp);

	//-------------------------------------------------------------------
	// Check the NN Oxygen Bond Lengths of Picked Yttrium
	//-------------------------------------------------------------------
	numofYNNOs=0;
	for(i=0;i<nn[Yindex];i++) // for all neighbors of the picked yttrium
	  {
	    j=nindex[Yindex][i]; // atom index of the neighbor
	    if(species[j]==1) // if the target is O
	      {
		tmpvec=(_SR[j]-_SR[Yindex]); // displacement vector
		tmpvec.subint(); // apply PBC
		tmpvec=_H*tmpvec; // convert to real-size vector
		tmpdbl=sqrt(tmpvec.norm2()); // distance between Y and neighbor O
		CGNMC_YNNO_I[numofYNNOs]=j;
		CGNMC_YNNO_R1[numofYNNOs]=tmpdbl;
		numofYNNOs++;
	      }
	  }
	INFO("CGNMC - number of YNNOs = "<<numofYNNOs);
	numofZrNNOs=0;
	for(i=0;i<nn[Zrindex];i++) // for all neighbors of the picked yttrium
	  {
	    j=nindex[Zrindex][i]; // atom index of the neighbor
	    if(species[j]==1)
	      {
		tmpvec=(_SR[j]-_SR[Zrindex]); // displacement vector
		tmpvec.subint(); // apply PBC
		tmpvec=_H*tmpvec; // transform to real vector
		tmpdbl=sqrt(tmpvec.norm2()); // distance between Y and neighbor Zr
		CGNMC_ZrNNO_I[numofZrNNOs]=j;
		CGNMC_ZrNNO_R1[numofZrNNOs]=tmpdbl;
		numofZrNNOs++;
	      }
	  }
	INFO("CGNMC - number of ZrNNOs = "<<numofZrNNOs);

	//------------------------------------------------------------------- 
	// Make the Exchange Trial Move
	//------------------------------------------------------------------- 
	species[Yindex]=0; // change the species (Y to Zr)
	species[Zrindex]=3; // change the species (Zr to Y)
	
	//-------------------------------------------------------------------
	// CG Relaxation
	//-------------------------------------------------------------------
	relax();
	potential();
	EPOT_2=_EPOT;

	//-------------------------------------------------------------------
	// Check the NN Oxygen Bond Lengths after Relaxation
	//-------------------------------------------------------------------
	for(i=0;i<numofYNNOs;i++) // for all neighbors of the picked yttrium
	  {
	    j=CGNMC_YNNO_I[i]; // atom index of the neighbor
	    //INFO("CGNMC - the atom index of YNNO = "<<j);
	    tmpvec=(_SR[j]-_SR[Yindex]); // displacement vector
	    tmpvec.subint(); // apply PBC
	    tmpvec=_H*tmpvec; // convert to real-size vector
	    tmpdbl=sqrt(tmpvec.norm2()); // distance between Y and neighbor O
	    //INFO("CGNMC - displacement of YNNO = "<<(CGNMC_YNNO_R1[i]-tmpdbl));
	    CGNMC_YNNO_R2[i]=tmpdbl;
	  }

	for(i=0;i<numofZrNNOs;i++) // for all neighbors of the picked yttrium
	  {
	    j=CGNMC_ZrNNO_I[i]; // atom index of the neighbor
	    //INFO("CGNMC - the atom index of ZrNNO = "<<j);
	    tmpvec=(_SR[j]-_SR[Zrindex]); // displacement vector
	    tmpvec.subint(); // apply PBC
	    tmpvec=_H*tmpvec; // transform to real vector
	    tmpdbl=sqrt(tmpvec.norm2()); // distance between Zr and neighbor O
	    //INFO("CGNMC - displacement of ZrNNO = "<<(CGNMC_ZrNNO_R1[i]-tmpdbl));
	    CGNMC_ZrNNO_R2[i]=tmpdbl;
	  }

	//-------------------------------------------------------------------
	// Save the NN Oxygen Bond Lengths before and after Relaxation
	//-------------------------------------------------------------------
	for(i=0;i<numofYNNOs;i++)
	  {fprintf(fp1,"%f %f %f\n",catdisp,CGNMC_YNNO_R1[i],CGNMC_YNNO_R2[i]);}
	for(i=0;i<numofZrNNOs;i++)
	  {fprintf(fp2,"%f %f %f\n",catdisp,CGNMC_ZrNNO_R1[i],CGNMC_ZrNNO_R2[i]);}
	
	//------------------------------------------------------------------- 
	// Calculate the Boltzmann Factor and Make a Decision
	//------------------------------------------------------------------- 
	dEPOT=EPOT_2-EPOT_1;
	tmpdbl=exp(-dEPOT/(KB*_TDES));
	if(tmpdbl>=1.0){MCacc=1;}
	else if(drand48()<=tmpdbl){MCacc=1;}
	else {MCacc=0;}
	
	//------------------------------------------------------------------- 
	// Accept or Reject the Move
	//-------------------------------------------------------------------
	if(MCacc==1)
	  {
	    // rescale the velocities
	    _VSR[Yindex]*=vratioYZr; _VR[Yindex]*=vratioYZr;
	    _VSR[Zrindex]*=vratioZrY; _VR[Zrindex]*=vratioZrY;
	    
	    // update yttrium and zirconium lists
	    if((yttriumdata[Ynum]!=Yindex)||(zirconiumdata[Zrnum]!=Zrindex))
	    {WARNING("CGNMC - index error, check the source code");}
	    yttriumdata[Ynum]=Zrindex;
	    zirconiumdata[Zrnum]=Yindex;
	    
	    totMCacc++;
	    INFO("---------------------------------------------------------------------------");
	    INFO("CGNMC - EPOT[old] = "<<EPOT_1<<" eV  |  EPOT[new] = "<<EPOT_2<<" eV");
	    INFO("CGNMC - potential energy change = "<<(EPOT_2-EPOT_1)<<" eV");
	    INFO("CGNMC - acceptance probability = "<<tmpdbl);
	    INFO("CGNMC - trial switch accepted ******");
	    INFO("---------------------------------------------------------------------------");
            INFO("CGNMC - Trial = "<<MCcounts<<"  Accepted = "<<totMCacc<<"  dEPOT = "<<EPOT_2-EPOT_1<<"  Pacc = "<<tmpdbl);
	  }
	else if(MCacc==0)
	  {
	    // retract the cation exchange
	    species[Yindex]=3; // retract the trial move
	    species[Zrindex]=0; // retract the trial move
	    
	    // retract the relaxation
	    for(i=0;i<_NP;i++)
	      {
		_SR[i]=_SR_CGNMC[i];
	      }

	    // print out the results
	    INFO("---------------------------------------------------------------------------");
	    INFO("CGNMC - EPOT[old] = "<<EPOT_1<<" eV  |  EPOT[new] = "<<EPOT_2<<" eV");
	    INFO("CGNMC - potential energy change = "<<(EPOT_2-EPOT_1)<<" eV");
	    INFO("CGNMC - acceptance probability = "<<tmpdbl);
	    INFO("CGNMC - trial switch rejected");
	    INFO("---------------------------------------------------------------------------");
            INFO("CGNMC - Trial = "<<MCcounts<<"  dEPOT = "<<EPOT_2-EPOT_1<<"  Pacc = "<<tmpdbl);
	  }
	totMCtrials++;
        
	//-------------------------------------------------------------------
	// MC Summary
	//------------------------------------------------------------------- 
	if((MCacc==1)&&((totMCacc%MCcheckfreq)==0))
	  {
	    INFO("===========================================================================");
	    INFO("CGNMC - number of total trials = "<<totMCtrials);
	    INFO("CGNMC - number of accepted = "<<totMCacc);
	    INFO("CGNMC - acceptance ratio = "<<(((double)totMCacc)/((double)(totMCtrials+1))));
	    INFO("===========================================================================");
	  }
        
	//-------------------------------------------------------------------
	// MD Relaxation Part 2
	//------------------------------------------------------------------- 
	//if((MCacc==1)&&((totMCacc%MDstep1freq)==0)){MDRELAX(MDstep1);}
        
	//-------------------------------------------------------------------
	// MD Relaxation Part 3 : Rescale Temperature
	//-------------------------------------------------------------------
	//if((MCacc==1)&&((totMCacc%MDstep2freq)==0))
        //{
	//scalevelocity=1; // turn on velocity scaler
	//MDRELAX(MDstep2);
	//scalevelocity=0; // turn off velocity scaler
        //}
    } //end of CGNMC
    eCGNMC=clock();
    tmpdbl=((double(eCGNMC))-(double(sCGNMC)))/CLOCKS_PER_SEC;
    INFO("---------------------------------------------------------------------------");
    INFO(" ");
    INFO("===========================================================================");
    INFO("CGNMC - execution time for "<<MCjumps<<" jumps = "<<tmpdbl);
    INFO("===========================================================================");
    if(saveprop) pf.write(this);
    fclose(fp1);
    fclose(fp2);
}

//###################################################################
//  MD Relaxation Function for Monte Carlo
//###################################################################
void YZOFrame::MDRELAX(int MDtime)
{
    int i, tmpint;
    INFO("===========================================================================");
    INFO(" Starting MD for "<<MDtime<<" Steps");
    INFO("---------------------------------------------------------------------------");
    //INFO("MD - Initial MD Step = "<<MDcounts); // display the starting step 
    
    SHtoR();
    for(curstep=MDcounts;curstep<(MDcounts+MDtime);curstep++)
    {
        DUMP("MDstep "<<curstep);
        while(win!=NULL)
	{
            if(win->IsPaused()) sleep(1);
            else break;
	}
        
        // MD steps
        tmpint=curstep;
        curstep=curstep-MDcounts;
        step();
        curstep=tmpint;
        if(curstep%savepropfreq==0)INFO("curstep="<<curstep);
        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
                    } 
    }
    MDcounts+=MDtime; // increase the MD count 
    INFO("===========================================================================");
    //-------------------------------------------------------------------
    // Reset Integrator and Nose Hoover Thermostat
    //-------------------------------------------------------------------
    for(i=0;i<_NP;i++){s2[i].clear();} // clean the VVerlet integrator
    if(usenosehoover)
    {
      for(i=0;i<_NP;i++)
      {s3[i].clear(); s4[i].clear(); s5[i].clear(); s2real[i].clear();}
      zeta=0.0; zetaa=0.0; zetav=0.0;
      zeta2=0.0; zeta3=0.0; zeta4=0.0; zeta5=0.0;
    }
}


//###################################################################
//  Simulated AFM
//###################################################################
void YZOFrame::SAFM()
{
    int i, j, k, TPcount;
    int probeindex, size;
    int ipt, jpt, it, ib, isp, jsp;
    int ik, ki, kal, kbl, ka, kb, kc;
    double d1posTP, d2posTP, d3max, d3min, tmp;
    double r, r2, ri6, x, xcube, cx1, cx2, QQ, UB, UC, UE, EXP;
    double k2, kx, ky, kz;
    double scaledRC;
    Vector3 rij, sij, vsep;
    EComplex sc1, sc2, sincosfinal;

    // result output files
    FILE *fp1;
    FILE *fp2;
    fp1=fopen("toppot.dat","w");
    fp2=fopen("botpot.dat","w");
    //fprintf(fp1,"%d\n",switchSAFM);
    //fprintf(fp2,"%d\n",switchSAFM);
    
    // display SAFM title
    INFO("===========================================================================");
    INFO(" Preparing Variables for the Simulated AFM");
    INFO("===========================================================================");
    
    // find the highest and lowest atoms
    d3max=0.0; d3min=0.0;    
    for(i=0;i<_NP;i++)
    {
        switch (switchSAFM) {
        case(0): tmp=_SR[i].x; break;
        case(1): tmp=_SR[i].y; break;
        case(2): tmp=_SR[i].z; break;
        default: break; }
        if (tmp>d3max){d3max=tmp;}
        if (tmp<d3min){d3min=tmp;}        
    }

    // we add one atom at the end of the list(probe ion)
    probeindex=_NP; // atom index of the probe ion
    size=_NP*allocmultiple+1; // new size of arrays

    // reallocate the position and species arrays
    Realloc(_R0,Vector3,size);
    for(i=0;i<_NP;i++){_R0[i]=_SR[i];}
    Realloc(_SR,Vector3,size);
    for(i=0;i<_NP;i++){_SR[i]=_R0[i];}
    Realloc(species0,int,size);
    for(i=0;i<_NP;i++){species0[i]=species[i];}
    Realloc(species,int,size);
    for(i=0;i<_NP;i++){species[i]=species0[i];}

    // initialize probe ion identity
    _SR[probeindex].clear(); // clear probe ion position
    species[probeindex]=probesp; // probe ion species
    _NP++; // increase number of atoms by one
    
    // set up the test point database
    totTPs=(1+d1TPs)*(1+d2TPs); // size of 2D data points
    Realloc(topTPs,Vector3,totTPs);
    Realloc(toppot,double,totTPs);
    Realloc(botTPs,Vector3,totTPs);
    Realloc(botpot,double,totTPs);
    
    // convert seperation unit
    switch (switchSAFM)
    {
    case(0) : vsep.x=d3sep; vsep.y=0.0; vsep.z=0.0;
        vsep=_Hinv*vsep; d3sep=vsep.x; break;
    case(1) : vsep.x=0.0; vsep.y=d3sep; vsep.z=0.0; 
        vsep=_Hinv*vsep; d3sep=vsep.y; break;
    case(2) : vsep.x=0.0; vsep.y=0.0; vsep.z=d3sep; 
        vsep=_Hinv*vsep; d3sep=vsep.z; break;
    default: break;
    }

    // construct lists of top and bottom atoms
    scaledRC=1.2*PP_RC;
    switch (switchSAFM)
    {
    case(0) : vsep.x=scaledRC; vsep.y=0.0; vsep.z=0.0;
        vsep=_Hinv*vsep; scaledRC=vsep.x; break;
    case(1) : vsep.x=0.0; vsep.y=scaledRC; vsep.z=0.0; 
        vsep=_Hinv*vsep; scaledRC=vsep.y; break;
    case(2) : vsep.x=0.0; vsep.y=0.0; vsep.z=scaledRC; 
        vsep=_Hinv*vsep; scaledRC=vsep.z; break;
    default: break;
    }
    Realloc(topatoms,int,_NP);
    Realloc(botatoms,int,_NP);
    topNP=0; botNP=0;
    for (i=0;i<(_NP-1);i++)
    {
        switch (switchSAFM)
        {
        case (0) : if(_SR[i].x>(d3max-scaledRC)){topatoms[topNP]=i; topNP++;};
            if(_SR[i].x<(d3min+scaledRC)){botatoms[botNP]=i; botNP++;}; break;
        case (1) : if(_SR[i].y>(d3max-scaledRC)){topatoms[topNP]=i; topNP++;};
            if(_SR[i].y<(d3min+scaledRC)){botatoms[botNP]=i; botNP++;}; break;
        case (2) : if(_SR[i].z>(d3max-scaledRC)){topatoms[topNP]=i; topNP++;};
            if(_SR[i].z<(d3min+scaledRC)){botatoms[botNP]=i; botNP++;}; break;
        }
    }
    
    // construct test point vectors
    for(i=0;i<totTPs;i++)
    {
        j=(i/(d1TPs+1)); // index in direction 2
        k=(i%(d1TPs+1)); // index in direction 1
        d1posTP=(1.0/((double)d1TPs))*((double)k)-0.5;
        d2posTP=(1.0/((double)d2TPs))*((double)j)-0.5;
        switch (switchSAFM) {
            // x surface - ( y : direction 1 ) / ( z : direction 2 )
        case(0): topTPs[i].x=(d3max+d3sep); botTPs[i].x=(d3min-d3sep);
            topTPs[i].y=d1posTP; botTPs[i].y=d1posTP;
            topTPs[i].z=d2posTP; botTPs[i].z=d2posTP; break;
            // y surface - ( x : direction 1 ) / ( z : direction 2 )            
        case(1): topTPs[i].x=d1posTP; botTPs[i].x=d1posTP;
            topTPs[i].y=(d3max+d3sep); botTPs[i].y=(d3min-d3sep);
            topTPs[i].z=d2posTP; botTPs[i].z=d2posTP; break;
            // z surface - ( x : direction 1 ) / ( y : direction 2 )            
        case(2): topTPs[i].x=d1posTP; botTPs[i].x=d1posTP;
            topTPs[i].y=d2posTP; botTPs[i].y=d2posTP;
            topTPs[i].z=(d3max+d3sep); botTPs[i].z=(d3min-d3sep); break;
        default: break; }
    }

    //fprintf(fp1,"%d\n",totTPs);
    //fprintf(fp2,"%d\n",totTPs);
    
    // Check if atom mass and charge are ready //HARK
    if(evalprepared==0)
    {
        evalprepared=prepareeval();
        if(evalprepared==0)
        {
            WARNING("ATOM MASS AND CHARGE ARE NOT SPECIFIED. UNABLE TO EXECUTE RELAX()");
            return;
        }
    }
    
    //prepare potential and check the neighbor list
    potsetup=0; useCE=1; usePME=0;    
    preparepotential();
    //refreshneighborlist();
    INFO("===========================================================================");
    INFO(" Potential Parameter Repreparation Completed");
    INFO("===========================================================================");

    // calculate sum of probe ion
    qSQRsum=SQR(_SPCHARGEMD[species[probeindex]]);

    // compute the self-interaction energy
    _EREC_self=-Alpha/M_SQRT_PI*qSQRsum;
        
    //-------------------------------------------------------------------
    //       Starting CE
    //-------------------------------------------------------------------

    // recalculate the system volume (in case the box is changing)
    if(conj_fixbox==0){cell_V=_H.det();}
    
    // precalculate exp(iKx*rx), exp(iKy*ry), exp(iKz*rz)
    fillSinCosTables();

    //-------------------------------------------------------------------
    //       Test Point Loop 1
    //-------------------------------------------------------------------
    INFO("===========================================================================");
    INFO(" Starting Top Surface Potential Measurement");
    INFO("===========================================================================");
    Realloc(SAFMsincosk,EComplex,nKV);
    for(TPcount=0;TPcount<totTPs;TPcount++)
    {
        if(TPcount%100==0)INFO("SAFM - test point index (top) = "<<TPcount<<"  total = "<<totTPs);
        
        // update the position of probe ion
        _SR[probeindex]=topTPs[TPcount];
        
        // update the sincos table of probe ion
        scx[0][probeindex].Re=1.0; scx[0][probeindex].Im=0.0;
        scy[0][probeindex].Re=1.0; scy[0][probeindex].Im=0.0;
        scz[0][probeindex].Re=1.0; scz[0][probeindex].Im=0.0;
        if(Mka>0) 
	{
            sc2=scx[1][probeindex];
            sc2.ExpI(2.0*M_PI*_SR[probeindex].x);
            for(ki=2;ki<=Mka;ki++)
                EComplex::Mul(scx[ki-1][probeindex], sc2, scx[ki][probeindex]);
        }
        if(Mkb>0) 
	{
            sc2=scy[1][probeindex];
            sc2.ExpI(2.0*M_PI*_SR[probeindex].y);
            for(ki=2;ki<=Mkb;ki++)
                EComplex::Mul(scy[ki-1][probeindex], sc2, scy[ki][probeindex]);
	}
        if(Mkc>0) 
	{
            sc2=scz[1][probeindex];
            sc2.ExpI(2.0*M_PI*_SR[probeindex].z);
            for(ki=2;ki<=Mkc;ki++)
                EComplex::Mul(scz[ki-1][probeindex], sc2, scz[ki][probeindex]);
	}


        //-------------------------------------------------------------------
        //       Calculating Short Range Potential and Ewald Real Part
        //-------------------------------------------------------------------
        
        // initialize potential energy
        _EPOT=0.0; _EREAL_B=0.0; _EREAL_C=0.0; _EREC=0.0;

        // short range potential        
        ipt=probeindex; // only for the probeion
        for(it=0;it<topNP;it++) // for all atoms except probe ion
	{
            jpt=topatoms[it];
            sij=_SR[jpt]-_SR[ipt]; // scaled interatomics distance
            sij.subint(); // apply periodic boundary condition (PBC)
            rij=_H*sij; // get the interatomic distance vector (with PBC)
            r2=rij.norm2(); // get the square of interatomic distance
            r=sqrt(r2); // interatomic distance
            
            if(r<=PP_RC) // if the interatomic distance is shorter than the cut-off radius
	    {
                // precalculate r^-6
                ri6=1.0/(r2*r2*r2);

                // calculate the cutoff variables
                if(r>PP_RC-1.0)
                {
                    x=r-PP_RC+1.0;
                    xcube=CUBE(x);
                    cx1=SQR(1.0-xcube*x);
                    cx2=8.0*(1.0-xcube*x)*xcube;
                }
                else
                {
                    cx1=1.0;
                    cx2=0.0;
                }

                // get the species indices
                isp=species[ipt]; // species of probe ion
                jsp=species[jpt]; // species of jpt-atom
                QQ=_SPCHARGEMD[isp]*_SPCHARGEMD[jsp]; // get the charge term
                k=isp+jsp; // interaction index
                
                // calculate potential
                UB=0.0; UC=0.0;
                UB=(-BMB_POT[k][0]*ri6+BMB_POT[k][1]*exp(-BMB_POT[k][2]*r))*cx1;
                UC=(erfc(Alpha*r))/r*QQ;

                // collect BMB and real Coulomb energy
                _EREAL_B+=UB;
                _EREAL_C+=UC;
            }
        }
        //-------------------------------------------------------------------
        //       Calculating Ewald Reciprocal Part (CE)
        //-------------------------------------------------------------------
        kal=-1; // an impossible value
        kbl=0;  // Just to avoid a warning
        for(ik=0;ik<nKV;ik++) // for all K-vectors
	{
            // kx, ky, kz calculated in enumKV()
            kx=KV[ik].kx;
            ky=KV[ik].ky;
            kz=KV[ik].kz;
            k2=KV[ik].k2;
            ka=KV[ik].a;
            kb=KV[ik].b;
            kc=KV[ik].c;
            
            if(TPcount==0)
	    {
                if(kal!=ka || kbl!=kb)
		{
                    kal=ka;
                    kbl=kb;
                    if(kb>=0)
                        for(i=0;i<_NP;i++)
                            EComplex::Mul(scx[ka][i], scy[kb][i], sc_axby[i]);
                    else
                        for(i=0;i<_NP;i++)
                            EComplex::MulConjugate(scx[ka][i], scy[-kb][i], sc_axby[i]);
		}
                SAFMsincosk[ik].Re=0.0;
                SAFMsincosk[ik].Im=0.0;
                
                if(kc>=0)
                    for(i=0;i<_NP;i++)
                    {
                        EComplex::Mul(sc_axby[i], scz[kc][i], sckk[i]);
                        sckk[i]*=_SPCHARGEMD[species[i]];
                        SAFMsincosk[ik]+=sckk[i];
                    }
                else
                    for(i=0;i<_NP;i++)
                    {
                        EComplex::MulConjugate(sc_axby[i], scz[-kc][i], sckk[i]);
                        sckk[i]*=_SPCHARGEMD[species[i]];
                        SAFMsincosk[ik]+=sckk[i];
                    }
                SAFMsincosk[ik]-=sckk[probeindex]; // structure factor excluding probe ion
	    }
            else
	    {
                // if not the first time, calculate only the exponential term of probe ion
                if(kal!=ka || kbl!=kb)
		{
                    kal=ka;
                    kbl=kb;
                    if(kb>=0){EComplex::Mul(scx[ka][probeindex], scy[kb][probeindex], sc_axby[probeindex]);}
                    else{EComplex::MulConjugate(scx[ka][probeindex], scy[-kb][probeindex], sc_axby[probeindex]);}
                }
                if(kc>=0)
                {
                    EComplex::Mul(sc_axby[probeindex], scz[kc][probeindex], sckk[probeindex]);
                    sckk[probeindex]*=_SPCHARGEMD[species[probeindex]];
		}
                else
                {
                    EComplex::MulConjugate(sc_axby[probeindex], scz[-kc][probeindex], sckk[probeindex]);
                    sckk[probeindex]*=_SPCHARGEMD[species[probeindex]];
                }
	    }

            sc1.Re=0.0; sc1.Im=0.0;
            sc1+=SAFMsincosk[ik];
            sc1+=sckk[probeindex];
            EComplex::MulConjugate(sckk[probeindex],sc1, sincosfinal);
            EXP=((4.0*M_PI)/cell_V)*exp(-k2/(4.0*SQR(Alpha)))/k2;
            UE=EXP*2.0*(sincosfinal.Re);

            // Potential Energy Calculation
            _EREC+=UE;
	}

        // Sum up Energy and Force
        _EPOT=_EREAL_B+_EREAL_C+_EREC+_EREC_self;
        
        // Store the Measure Potential Energy
        toppot[TPcount]=_EPOT;
        if(TPcount%100==0)INFO("SAFM - calculated potential = "<<_EPOT<<" eV");
        fprintf(fp1,"%f %f %f %f\n",topTPs[TPcount].x,topTPs[TPcount].y,topTPs[TPcount].z,_EPOT);
    }
    
    //-------------------------------------------------------------------
    //       Test Point Loop 2
    //-------------------------------------------------------------------
    INFO("===========================================================================");
    INFO(" Starting Bottom Surface Potential Measurement");
    INFO("===========================================================================");
    for(TPcount=0;TPcount<totTPs;TPcount++)
    {
        if(TPcount%100==0)INFO("SAFM - test point index (bottom) = "<<TPcount<<"  total = "<<totTPs);

        // update the position of probe ion
        _SR[probeindex]=botTPs[TPcount];

        // update the sincos table of probe ion
        scx[0][probeindex].Re=1.0; scx[0][probeindex].Im=0.0;
        scy[0][probeindex].Re=1.0; scy[0][probeindex].Im=0.0;
        scz[0][probeindex].Re=1.0; scz[0][probeindex].Im=0.0;
        if(Mka>0) 
	{
            sc2=scx[1][probeindex];
            sc2.ExpI(2.0*M_PI*_SR[probeindex].x);
            for(ki=2;ki<=Mka;ki++)
                EComplex::Mul(scx[ki-1][probeindex], sc2, scx[ki][probeindex]);
        }
        if(Mkb>0) 
	{
            sc2=scy[1][probeindex];
            sc2.ExpI(2.0*M_PI*_SR[probeindex].y);
            for(ki=2;ki<=Mkb;ki++)
                EComplex::Mul(scy[ki-1][probeindex], sc2, scy[ki][probeindex]);
	}
        if(Mkc>0) 
	{
            sc2=scz[1][probeindex];
            sc2.ExpI(2.0*M_PI*_SR[probeindex].z);
            for(ki=2;ki<=Mkc;ki++)
                EComplex::Mul(scz[ki-1][probeindex], sc2, scz[ki][probeindex]);
	}


        //-------------------------------------------------------------------
        //       Calculating Short Range Potential and Ewald Real Part
        //-------------------------------------------------------------------
        
        // initialize potential energy
        _EPOT=0.0; _EREAL_B=0.0; _EREAL_C=0.0; _EREC=0.0;

        // short range potential        
        ipt=probeindex; // only for the probeion
        for(ib=0;ib<botNP;ib++) // for all atoms except probe ion
	{
            jpt=botatoms[ib];
            sij=_SR[jpt]-_SR[ipt]; // scaled interatomics distance
            sij.subint(); // apply periodic boundary condition (PBC)
            rij=_H*sij; // get the interatomic distance vector (with PBC)
            r2=rij.norm2(); // get the square of interatomic distance
            r=sqrt(r2); // interatomic distance
            
            if(r<=PP_RC) // if the interatomic distance is shorter than the cut-off radius
	    {
                // precalculate r^-6
                ri6=1.0/(r2*r2*r2);

                // calculate the cutoff variables
                if(r>PP_RC-1.0)
                {
                    x=r-PP_RC+1.0;
                    xcube=CUBE(x);
                    cx1=SQR(1.0-xcube*x);
                    cx2=8.0*(1.0-xcube*x)*xcube;
                }
                else
                {
                    cx1=1.0;
                    cx2=0.0;
                }

                // get the species indices
                isp=species[ipt]; // species of probe ion
                jsp=species[jpt]; // species of jpt-atom
                QQ=_SPCHARGEMD[isp]*_SPCHARGEMD[jsp]; // get the charge term
                k=isp+jsp; // interaction index
                
                // calculate potential
                UB=0.0; UC=0.0;
                UB=(-BMB_POT[k][0]*ri6+BMB_POT[k][1]*exp(-BMB_POT[k][2]*r))*cx1;
                UC=(erfc(Alpha*r))/r*QQ;

                // collect BMB and real Coulomb energy
                _EREAL_B+=UB;
                _EREAL_C+=UC;
            }
        }
        //-------------------------------------------------------------------
        //       Calculating Ewald Reciprocal Part (CE)
        //-------------------------------------------------------------------
        kal=-1; // an impossible value
        kbl=0;  // Just to avoid a warning
        for(ik=0;ik<nKV;ik++) // for all K-vectors
	{
            // kx, ky, kz calculated in enumKV()
            kx=KV[ik].kx;
            ky=KV[ik].ky;
            kz=KV[ik].kz;
            k2=KV[ik].k2;
            ka=KV[ik].a;
            kb=KV[ik].b;
            kc=KV[ik].c;
            
            if(TPcount==0)
	    {
                if(kal!=ka || kbl!=kb)
		{
                    kal=ka;
                    kbl=kb;
                    if(kb>=0)
                        for(i=0;i<_NP;i++)
                            EComplex::Mul(scx[ka][i], scy[kb][i], sc_axby[i]);
                    else
                        for(i=0;i<_NP;i++)
                            EComplex::MulConjugate(scx[ka][i], scy[-kb][i], sc_axby[i]);
		}
                SAFMsincosk[ik].Re=0.0;
                SAFMsincosk[ik].Im=0.0;
                
                if(kc>=0)
                    for(i=0;i<_NP;i++)
                    {
                        EComplex::Mul(sc_axby[i], scz[kc][i], sckk[i]);
                        sckk[i]*=_SPCHARGEMD[species[i]];
                        SAFMsincosk[ik]+=sckk[i];
                    }
                else
                    for(i=0;i<_NP;i++)
                    {
                        EComplex::MulConjugate(sc_axby[i], scz[-kc][i], sckk[i]);
                        sckk[i]*=_SPCHARGEMD[species[i]];
                        SAFMsincosk[ik]+=sckk[i];
                    }
                SAFMsincosk[ik]-=sckk[probeindex]; // structure factor excluding probe ion
	    }
            else
	    {
                // if not the first time, calculate only the exponential term of probe ion
                if(kal!=ka || kbl!=kb)
		{
                    kal=ka;
                    kbl=kb;
                    if(kb>=0){EComplex::Mul(scx[ka][probeindex], scy[kb][probeindex], sc_axby[probeindex]);}
                    else{EComplex::MulConjugate(scx[ka][probeindex], scy[-kb][probeindex], sc_axby[probeindex]);}
                }
                if(kc>=0)
                {
                    EComplex::Mul(sc_axby[probeindex], scz[kc][probeindex], sckk[probeindex]);
                    sckk[probeindex]*=_SPCHARGEMD[species[probeindex]];
		}
                else
                {
                    EComplex::MulConjugate(sc_axby[probeindex], scz[-kc][probeindex], sckk[probeindex]);
                    sckk[probeindex]*=_SPCHARGEMD[species[probeindex]];
                }
	    }

            sc1.Re=0.0; sc1.Im=0.0;
            sc1+=SAFMsincosk[ik];
            sc1+=sckk[probeindex];
            EComplex::MulConjugate(sckk[probeindex],sc1, sincosfinal);
            EXP=((4.0*M_PI)/cell_V)*exp(-k2/(4.0*SQR(Alpha)))/k2;
            UE=EXP*2.0*(sincosfinal.Re);

            // Potential Energy Calculation
            _EREC+=UE;
	}

        // Sum up Energy and Force
        _EPOT=_EREAL_B+_EREAL_C+_EREC+_EREC_self;

        // Store the Measure Potential Energy
        botpot[TPcount]=_EPOT;
        if(TPcount%100==0)INFO("SAFM - calculated potential = "<<_EPOT<<" eV");
        fprintf(fp2,"%f %f %f %f\n",botTPs[TPcount].x,botTPs[TPcount].y,botTPs[TPcount].z,_EPOT);
    }

    fclose(fp1);
    fclose(fp2);
    potsetup=0;
}

//###################################################################
//  Reduced Potential for Monte Carlo
//###################################################################
//-------------------------------------------------------------------
// Preparation for the Reduced Potential Parameters
//-------------------------------------------------------------------
void YZOFrame::NMC_preparepotential()
{
    int i,j,ipt,jpt;
    EComplex *p;
    
    // refresh neighbor list
    refreshneighborlist();
    
    // initialize force, potential and virials
    _EPOT=0.0; _EREAL=0.0; _EREC=0.0; 
    dEREAL=0.0; dEREC=0.0; 
    NMC_EREAL1=0.0; NMC_EREAL2=0.0; NMC_EREC=0.0;
    
    // counting the number of pairinteractions
    numofpairs=0;
    for(ipt=0;ipt<_NP;ipt++) // for all particles
      {
        for(j=0;j<nn[ipt];j++) // for all neighbors
	  {
            jpt=nindex[ipt][j]; // get the atom index of j-th neighbor
            if(ipt>jpt) continue; // only if i<=j -> avoid redundancy
            numofpairs++;
	  }
      }
    maxnumofpairs=(int)floor(((double)numofpairs)*1.2);
    INFO("NMC - Reduced Potential : Maximum Number of Pairpotentials to Save = "<<maxnumofpairs);
    
    // allocate and initialize trialatoms, realpairE
    Realloc(trialatomlist,int,(2*maxNNO));
    Realloc(trialatomindex,int,_NP);
    for(i=0;i<_NP;i++){trialatomindex[i]=0;}
    Realloc(realpairE1,double,maxnumofpairs);
    for(i=0;i<maxnumofpairs;i++){realpairE1[i]=0.0;}
    Realloc(realpairE2,double,maxnumofpairs);
    for(i=0;i<maxnumofpairs;i++){realpairE2[i]=0.0;}
    
    // allocate MCscx, MCscy, MCscz
    if(useCE)
      {
        INFO("---------------------------------------------------------------------------");
        INFO("NMC - Using Reduced CE for the Calculation of the Reciprocal Energy");
	
        if(MCscx==NULL) p=0;
        else p=scx[0];
        Realloc(p,EComplex,(_NP*(Mka+Mkb+Mkc+3)));
        INFO("NMC - Reduced Ewald : Number of Sincos Table Entries = "<<(_NP*(Mka+Mkb+Mkc+3)));
        if(p==NULL) INFO("Error : enumKV() -> Out of memory allocating sin/cos tables");
	
        Realloc(MCscx,EComplex*,(Mka+Mkb+Mkc+3)); 
        if(MCscx==NULL) INFO("Error : enumKV() -> Out of memory allocating sin/cos tables");
        MCscy=MCscx+(Mka+1);
        MCscz=MCscy+(Mkb+1);
    
        MCscx[0]=p; 
        for(i=1;i<=Mka;i++) MCscx[i]=MCscx[i-1]+_NP;
        MCscy[0]=MCscx[0]+(Mka+1)*_NP;
        for(i=1;i<=Mkb;i++) MCscy[i]=MCscy[i-1]+_NP;
        MCscz[0]=MCscy[0]+(Mkb+1)*_NP;
        for(i=1;i<=Mkc;i++) MCscz[i]=MCscz[i-1]+_NP;
    
        // allocate MCsckk, MCsc_axby
        Realloc(MCsckk,EComplex,(_NP*2));
        MCsc_axby=MCsckk+_NP;
    
        // allocate MCsincosk
        Realloc(MCsincosk,EComplex,MKV);
      }
    if(usePME)
      {
        INFO("---------------------------------------------------------------------------");
        INFO("NMC - Using Reduced PME for the Calculation of the Reciprocal Energy");
        NMC_R_PME_init();
      }
}

//###################################################################
//  Reduced Potential I
//###################################################################

//-------------------------------------------------------------------
// Potential Calculation (normal cases : Initial and resetting calculations)
//-------------------------------------------------------------------
void YZOFrame::NMC_N1_potential()
{
    int j,k,ipt,jpt,isp,jsp;
    double U,UB,UC,r,r2,ri6,x,xcube,cx1,QQ;
    Vector3 sij, rij;
    
    // refresh neighbor list
    refreshneighborlist();
    
    // initialize force, potential and virials
    _EPOT=0.0; _EREAL=0.0; _EREC=0.0; 
    NMC_EREAL1=0.0; NMC_EREC=0.0; 
    pairindex=-1;
  
    //-------------------------------------------------------------------
    //       Calculating real part potential completely
    //-------------------------------------------------------------------
    sRP_REAL=clock();
    for(ipt=0;ipt<_NP;ipt++) // for all particles
      {
        for(j=0;j<nn[ipt];j++) // for all neighbors
	  {
            jpt=nindex[ipt][j]; // get the atom index of j-th neighbor
            if(ipt>jpt) continue; // only if i<=j -> avoid redundancy
            pairindex++;

            sij=_SR[jpt]-_SR[ipt]; // scaled interatomics distance
            sij.subint(); // apply periodic boundary condition (PBC)
            rij=_H*sij; // get the interatomic distance vector (with PBC)
            r2=rij.norm2(); // get the square of interatomic distance
            r=sqrt(r2); // interatomic distance
            
            if(r<=PP_RC) // if the interatomic distance is shorter than the cut-off radius
	      {
                // precalculate r^-6
                ri6=1.0/(r2*r2*r2);
		
                // calculate the cutoff variables
                if(r>PP_RC-1.0)
		  {
                    x=r-PP_RC+1.0;
                    xcube=CUBE(x);
                    cx1=SQR(1.0-xcube*x);
		  }
                else
		  {
                    cx1=1.0;
		  }
                
                // get the species indices
                isp=species[ipt]; // species of ipt-atom
                jsp=species[jpt]; // species of jpt-atom
                QQ=_SPCHARGEMD[isp]*_SPCHARGEMD[jsp]; // get the charge term
                k=isp+jsp; // interaction index
                
                // calculate potential
                UB=0.0; UC=0.0;
                UB=(-BMB_POT[k][0]*ri6+BMB_POT[k][1]*exp(-BMB_POT[k][2]*r))*cx1;
                UC=QQ*(erfc(Alpha*r))/r;
                U=UB+UC;
                
                // add pair terms to the accumulative sum
                _EREAL+=U;
		
                // store the pairpotential energies
                realpairE1[pairindex]=U;
	      }
	  }
      }
    NMC_EREAL1=_EREAL;
    eRP_REAL=clock();
    //-------------------------------------------------------------------
    //               Calculating Ewald Fourier Part
    //------------------------------------------------------------------- 
    sRP_REC=clock();
    if(useCE){NMC_N_CE();}
    else{NMC_N_PME();}
    NMC_EREC=_EREC;
    eRP_REC=clock();
    //-------------------------------------------------------------------
    //               Sum Up Potential Energy
    //-------------------------------------------------------------------
    _EPOT=_EREAL+_EREC+_EREC_self;
    INFO("[RPN1] _EREAL = "<<_EREAL<<" _EREC = "<<_EREC);
    iRP_REAL=(-(double(sRP_REAL))+(double(eRP_REAL)))/CLOCKS_PER_SEC;
    iRP_REC=(-(double(sRP_REC))+(double(eRP_REC)))/CLOCKS_PER_SEC;
    //INFO("[RPN1] Real Space Calculation : "<<iRP_REAL<<" sec / Reciprocal Space Calculation : "<<iRP_REC<<" sec");
}

//-------------------------------------------------------------------
// Potential for MC (Reduced Algorithm)
//-------------------------------------------------------------------
void YZOFrame::NMC_R1_potential()
{
    //int i;
    // execute potential function
    if(NbrList_needrefresh())
      {
	NbrList_reconstruct();
	NMC_R1_potential_A();
	checkneighborlist=1;
	//for(i=0;i<maxnumofpairs;i++){realpairE1[i]=0.0;}
	INFO("NMC - Neighbor list reconstruction requested ********");
      }
    else 
      {
        NMC_R1_potential_B();
      }
}

//-------------------------------------------------------------------
// Potential for MC (Initial calculation)
//-------------------------------------------------------------------
void YZOFrame::NMC_R1_potential_A()
{
    int j,k,ipt,jpt,isp,jsp;
    double U,UB,UC,r,r2,ri6,x,xcube,cx1,QQ;
    Vector3 sij, rij;

    // initialize potentials
    _EPOT=0.0; _EREAL=0.0; _EREC=0.0; dEREAL=0.0; dEREC=0.0;

    //-------------------------------------------------------------------
    //       Calculating real part potential in normal fashion
    //-------------------------------------------------------------------
    sRP_REAL=clock();
    for(ipt=0;ipt<_NP;ipt++) // for all particles
    {
        for(j=0;j<nn[ipt];j++) // for all neighbors
	{
            jpt=nindex[ipt][j]; // get the atom index of j-th neighbor
            if(ipt>=jpt) continue; // only if i<=j -> avoid redundancy
            sij=_SR[jpt]-_SR[ipt]; // scaled interatomics distance
            sij.subint(); // apply periodic boundary condition (PBC)
            rij=_H*sij; // get the interatomic distance vector (with PBC)
            r2=rij.norm2(); // get the square of interatomic distance
            r=sqrt(r2); // interatomic distance
            
            if(r<=PP_RC) // if the interatomic distance is shorter than the cut-off radius
	    {
                // precalculate r^-6
                ri6=1.0/(r2*r2*r2);

                // calculate the cutoff variables
                if(r>PP_RC-1.0)
                {
                    x=r-PP_RC+1.0;
                    xcube=CUBE(x);
                    cx1=SQR(1.0-xcube*x);
                }
                else
                {
                    cx1=1.0;
                }

                // get the species indices
                isp=species[ipt]; // species of ipt-atom
                jsp=species[jpt]; // species of jpt-atom
                QQ=_SPCHARGEMD[isp]*_SPCHARGEMD[jsp]; // get the charge term
                k=isp+jsp; // interaction index
                
                // calculate potential
                UB=0.0; UC=0.0;
                UB=(-BMB_POT[k][0]*ri6+BMB_POT[k][1]*exp(-BMB_POT[k][2]*r))*cx1;
                UC=QQ*(erfc(Alpha*r))/r;
                U=UB+UC;
		               
                // add pair terms to the accumulative sum
                _EREAL+=U;
	    }
	}
    }
    dEREAL=_EREAL-NMC_EREAL1;
    eRP_REAL=clock();
    //-------------------------------------------------------------------
    //               Calculating Ewald Fourier Part
    //-------------------------------------------------------------------
    sRP_REC=clock();
    if(useCE){NMC_R_CE();}
    else{NMC_R_PME();}
    dEREC=_EREC-NMC_EREC;
    eRP_REC=clock();
    //-------------------------------------------------------------------
    //               Sum Up Potential Energy
    //-------------------------------------------------------------------
    _EPOT=_EREAL+_EREC+_EREC_self;
    INFO("[RPA1] _EREAL = "<<_EREAL<<" _EREC = "<<_EREC);
    iRP_REAL=(-(double(sRP_REAL))+(double(eRP_REAL)))/CLOCKS_PER_SEC;
    iRP_REC=(-(double(sRP_REC))+(double(eRP_REC)))/CLOCKS_PER_SEC;
    INFO("[RPA1] Real Space Calculation : "<<iRP_REAL<<" sec / Reciprocal Space Calculation : "<<iRP_REC<<" sec");
}

//-------------------------------------------------------------------
// Potential for MC (Reduced Algorithm)
//-------------------------------------------------------------------
void YZOFrame::NMC_R1_potential_B()
{
    int j,k,ipt,jpt,isp,jsp;
    double U,UB,UC,r,r2,ri6,x,xcube,cx1,QQ;
    Vector3 sij, rij;
    
    // initialize potentials 
    _EPOT=0.0; _EREAL=0.0; _EREC=0.0; dEREAL=0.0; dEREC=0.0; pairindex=-1;

    //-------------------------------------------------------------------
    //       Calculating potential for the first time (full scan)
    //-------------------------------------------------------------------
    sRP_REAL=clock();
    for(ipt=0;ipt<_NP;ipt++) // for all atoms
      {
	for(j=0;j<nn[ipt];j++) // for all neighbors
	  {
	    jpt=nindex[ipt][j]; // get the atom index of j-th neighbor
	    if(ipt>=jpt) continue;
            pairindex++;
            if((trialatomindex[ipt]==0)&&(trialatomindex[jpt]==0)) continue;

	    sij=_SR[jpt]-_SR[ipt]; // scaled interatomics distance
            sij.subint(); // apply periodic boundary condition (PBC)
            rij=_H*sij; // get the interatomic distance vector (with PBC)
            r2=rij.norm2(); // get the square of interatomic distance
            r=sqrt(r2); // interatomic distance

            if(r<=PP_RC) // if the interatomic distance is shorter than the cut-off radius
	      {
		    // precalculate r^-6
		    ri6=1.0/(r2*r2*r2);
		    
		    // calculate the cutoff variables
		    if(r>PP_RC-1.0)
		      {
			x=r-PP_RC+1.0;
			xcube=CUBE(x);
			cx1=SQR(1.0-xcube*x);
		      }
		    else
		      {
			cx1=1.0;
		      }
		    
		    // get the species indices
		    isp=species[ipt]; // species of ipt-atom
		    jsp=species[jpt]; // species of jpt-atom
		    QQ=_SPCHARGEMD[isp]*_SPCHARGEMD[jsp]; // get the charge term
		    k=isp+jsp; // interaction index
		    
		    // calculate potential
		    UB=0.0; UC=0.0;
		    UB=(-BMB_POT[k][0]*ri6+BMB_POT[k][1]*exp(-BMB_POT[k][2]*r))*cx1;
		    UC=QQ*(erfc(Alpha*r))/r;
		    U=UB+UC;

                    // add pair terms to the accumulative sum
                   dEREAL+=(U-realpairE1[pairindex]);
	      }
	  }
      }
    _EREAL=NMC_EREAL1+dEREAL;
    eRP_REAL=clock();
    //-------------------------------------------------------------------
    //               Calculating Ewald Fourier Part
    //------------------------------------------------------------------- 
    sRP_REC=clock();
    if(useCE){NMC_R_CE();}
    else{NMC_R_PME();}
    dEREC=_EREC-NMC_EREC;
    eRP_REC=clock();
    //-------------------------------------------------------------------
    //               Sum Up Potential Energy
    //-------------------------------------------------------------------
    _EPOT=_EREAL+_EREC+_EREC_self;
    INFO("[RPB1] _EREAL = "<<_EREAL<<" _EREC = "<<_EREC);
    iRP_REAL=(-(double(sRP_REAL))+(double(eRP_REAL)))/CLOCKS_PER_SEC;
    iRP_REC=(-(double(sRP_REC))+(double(eRP_REC)))/CLOCKS_PER_SEC;
    //INFO("[RPB1] Real Space Calculation : "<<iRP_REAL<<" sec / Reciprocal Space Calculation : "<<iRP_REC<<" sec");
}

//###################################################################
//  Reduced Potential II (Only Real Part)
//###################################################################

//-------------------------------------------------------------------
// Potential Calculation (normal cases : Initial and resetting calculations)
//-------------------------------------------------------------------
void YZOFrame::NMC_N2_potential()
{
    int j,k,ipt,jpt,isp,jsp;
    double U,UB,UC,r,r2,ri6,x,xcube,cx1,QQ;
    Vector3 sij, rij;
    
    // refresh neighbor list
    refreshneighborlist();
    
    // initialize force, potential and virials
    _EPOT=0.0; _EREAL=0.0; _EREC=0.0; 
    NMC_EREAL1=0.0; NMC_EREC=0.0; 
    pairindex=-1;
  
    //-------------------------------------------------------------------
    //       Calculating real part potential completely
    //-------------------------------------------------------------------
    sRP_REAL=clock();
    for(ipt=0;ipt<_NP;ipt++) // for all particles
      {
        for(j=0;j<nn[ipt];j++) // for all neighbors
	  {
            jpt=nindex[ipt][j]; // get the atom index of j-th neighbor
            if(ipt>jpt) continue; // only if i<=j -> avoid redundancy
            pairindex++;

            sij=_SR[jpt]-_SR[ipt]; // scaled interatomics distance
            sij.subint(); // apply periodic boundary condition (PBC)
            rij=_H*sij; // get the interatomic distance vector (with PBC)
            r2=rij.norm2(); // get the square of interatomic distance
            r=sqrt(r2); // interatomic distance
            
            if(r<=PP_RC) // if the interatomic distance is shorter than the cut-off radius
	      {
                // precalculate r^-6
                ri6=1.0/(r2*r2*r2);
		
                // calculate the cutoff variables
                if(r>PP_RC-1.0)
		  {
                    x=r-PP_RC+1.0;
                    xcube=CUBE(x);
                    cx1=SQR(1.0-xcube*x);
		  }
                else
		  {
                    cx1=1.0;
		  }
                
                // get the species indices
                isp=species[ipt]; // species of ipt-atom
                jsp=species[jpt]; // species of jpt-atom
                QQ=_SPCHARGEMD[isp]*_SPCHARGEMD[jsp]; // get the charge term
                k=isp+jsp; // interaction index
                
                // calculate potential
                UB=0.0; UC=0.0;
                UB=(-BMB_POT[k][0]*ri6+BMB_POT[k][1]*exp(-BMB_POT[k][2]*r))*cx1;
                UC=QQ*(erfc(Alpha*r))/r;
                U=UB+UC;
                
                // add pair terms to the accumulative sum
                _EREAL+=U;
		
                // store the pairpotential energies
                realpairE1[pairindex]=U;
	      }
	  }
      }
    NMC_EREAL1=_EREAL;
    eRP_REAL=clock();
    //-------------------------------------------------------------------
    //               Sum Up Potential Energy
    //-------------------------------------------------------------------
    _EPOT=_EREAL;
    INFO("[RPN2] _EREAL = "<<_EREAL);
    iRP_REAL=(-(double(sRP_REAL))+(double(eRP_REAL)))/CLOCKS_PER_SEC;
    //INFO("[RPN2] Real Space Calculation : "<<iRP_REAL<<" sec");
}

//-------------------------------------------------------------------
// Potential for MC (Reduced Algorithm)
//-------------------------------------------------------------------
void YZOFrame::NMC_R2_potential()
{
    int i;
    // execute potential function
    if(checkneighborlist==1)
      {
	//if(NbrList_needrefresh())
	//{
	//NbrList_reconstruct();
	NMC_R2_potential_A();
	//checkneighborlist=1;
	for(i=0;i<maxnumofpairs;i++){realpairE1[i]=0.0;}
	//INFO("NMC - Neighbor list reconstruction requested ********");
      }
    else 
      {
        NMC_R2_potential_B();
      }
}

//-------------------------------------------------------------------
// Potential for MC (Initial calculation)
//-------------------------------------------------------------------
void YZOFrame::NMC_R2_potential_A()
{
    int j,k,ipt,jpt,isp,jsp;
    double U,UB,UC,r,r2,ri6,x,xcube,cx1,QQ;
    Vector3 sij, rij;

    // initialize potentials
    _EPOT=0.0; _EREAL=0.0; _EREC=0.0; dEREAL=0.0; dEREC=0.0;

    //-------------------------------------------------------------------
    //       Calculating real part potential in normal fashion
    //-------------------------------------------------------------------
    sRP_REAL=clock();
    for(ipt=0;ipt<_NP;ipt++) // for all particles
    {
        for(j=0;j<nn[ipt];j++) // for all neighbors
	{
            jpt=nindex[ipt][j]; // get the atom index of j-th neighbor
            if(ipt>=jpt) continue; // only if i<=j -> avoid redundancy
            sij=_SR[jpt]-_SR[ipt]; // scaled interatomics distance
            sij.subint(); // apply periodic boundary condition (PBC)
            rij=_H*sij; // get the interatomic distance vector (with PBC)
            r2=rij.norm2(); // get the square of interatomic distance
            r=sqrt(r2); // interatomic distance
            
            if(r<=PP_RC) // if the interatomic distance is shorter than the cut-off radius
	    {
                // precalculate r^-6
                ri6=1.0/(r2*r2*r2);

                // calculate the cutoff variables
                if(r>PP_RC-1.0)
                {
                    x=r-PP_RC+1.0;
                    xcube=CUBE(x);
                    cx1=SQR(1.0-xcube*x);
                }
                else
                {
                    cx1=1.0;
                }

                // get the species indices
                isp=species[ipt]; // species of ipt-atom
                jsp=species[jpt]; // species of jpt-atom
                QQ=_SPCHARGEMD[isp]*_SPCHARGEMD[jsp]; // get the charge term
                k=isp+jsp; // interaction index
                
                // calculate potential
                UB=0.0; UC=0.0;
                UB=(-BMB_POT[k][0]*ri6+BMB_POT[k][1]*exp(-BMB_POT[k][2]*r))*cx1;
                UC=QQ*(erfc(Alpha*r))/r;
                U=UB+UC;
		               
                // add pair terms to the accumulative sum
                _EREAL+=U;
	    }
	}
    }
    dEREAL=_EREAL-NMC_EREAL1;
    eRP_REAL=clock();
    //-------------------------------------------------------------------
    //               Sum Up Potential Energy
    //-------------------------------------------------------------------
    _EPOT=_EREAL;
    INFO("[RPA2] _EREAL = "<<_EREAL);
    iRP_REAL=(-(double(sRP_REAL))+(double(eRP_REAL)))/CLOCKS_PER_SEC;
    iRP_REC=(-(double(sRP_REC))+(double(eRP_REC)))/CLOCKS_PER_SEC;
    INFO("[RPA2] Real Space Calculation : "<<iRP_REAL<<" sec / Reciprocal Space Calculation : "<<iRP_REC<<" sec");
}

//-------------------------------------------------------------------
// Potential for MC (Reduced Algorithm)
//-------------------------------------------------------------------
void YZOFrame::NMC_R2_potential_B()
{
    int j,k,ipt,jpt,isp,jsp;
    double U,UB,UC,r,r2,ri6,x,xcube,cx1,QQ;
    Vector3 sij, rij;
    
    // initialize potentials 
    _EPOT=0.0; _EREAL=0.0; _EREC=0.0; dEREAL=0.0; dEREC=0.0; pairindex=-1;

    //-------------------------------------------------------------------
    //       Calculating potential for the first time (full scan)
    //-------------------------------------------------------------------
    sRP_REAL=clock();
    for(ipt=0;ipt<_NP;ipt++) // for all atoms
      {
	for(j=0;j<nn[ipt];j++) // for all neighbors
	  {
	    jpt=nindex[ipt][j]; // get the atom index of j-th neighbor
	    if(ipt>=jpt) continue;
            pairindex++;
            if((trialatomindex[ipt]==0)&&(trialatomindex[jpt]==0)) continue;

	    sij=_SR[jpt]-_SR[ipt]; // scaled interatomics distance
            sij.subint(); // apply periodic boundary condition (PBC)
            rij=_H*sij; // get the interatomic distance vector (with PBC)
            r2=rij.norm2(); // get the square of interatomic distance
            r=sqrt(r2); // interatomic distance

            if(r<=PP_RC) // if the interatomic distance is shorter than the cut-off radius
	      {
		    // precalculate r^-6
		    ri6=1.0/(r2*r2*r2);
		    
		    // calculate the cutoff variables
		    if(r>PP_RC-1.0)
		      {
			x=r-PP_RC+1.0;
			xcube=CUBE(x);
			cx1=SQR(1.0-xcube*x);
		      }
		    else
		      {
			cx1=1.0;
		      }
		    
		    // get the species indices
		    isp=species[ipt]; // species of ipt-atom
		    jsp=species[jpt]; // species of jpt-atom
		    QQ=_SPCHARGEMD[isp]*_SPCHARGEMD[jsp]; // get the charge term
		    k=isp+jsp; // interaction index
		    
		    // calculate potential
		    UB=0.0; UC=0.0;
		    UB=(-BMB_POT[k][0]*ri6+BMB_POT[k][1]*exp(-BMB_POT[k][2]*r))*cx1;
		    UC=QQ*(erfc(Alpha*r))/r;
		    U=UB+UC;

                    // add pair terms to the accumulative sum
                   dEREAL+=(U-realpairE1[pairindex]);
	      }
	  }
      }
    _EREAL=NMC_EREAL1+dEREAL;
    eRP_REAL=clock();
    //-------------------------------------------------------------------
    //               Sum Up Potential Energy
    //-------------------------------------------------------------------
    _EPOT=_EREAL;
    INFO("[RPB2] _EREAL = "<<_EREAL);
    iRP_REAL=(-(double(sRP_REAL))+(double(eRP_REAL)))/CLOCKS_PER_SEC;
    //INFO("[RPB2] Real Space Calculation : "<<iRP_REAL<<" sec");
}

//###################################################################
//  Reduced Potential III
//###################################################################

//-------------------------------------------------------------------
// Potential Calculation (normal cases : Initial and resetting calculations)
//-------------------------------------------------------------------
void YZOFrame::NMC_N3_potential()
{
    int j,k,ipt,jpt,isp,jsp;
    double U,UB,UC,r,r2,ri6,x,xcube,cx1,QQ;
    Vector3 sij, rij;
    
    // refresh neighbor list
    refreshneighborlist();
    
    // initialize force, potential and virials
    _EPOT=0.0; _EREAL=0.0; _EREC=0.0; 
    NMC_EREAL2=0.0; NMC_EREC=0.0; 
    pairindex=-1;
  
    //-------------------------------------------------------------------
    //       Calculating real part potential completely
    //-------------------------------------------------------------------
    sRP_REAL=clock();
    for(ipt=0;ipt<_NP;ipt++) // for all particles
      {
        for(j=0;j<nn[ipt];j++) // for all neighbors
	  {
            jpt=nindex[ipt][j]; // get the atom index of j-th neighbor
            if(ipt>jpt) continue; // only if i<=j -> avoid redundancy
            pairindex++;

            sij=_SR[jpt]-_SR[ipt]; // scaled interatomics distance
            sij.subint(); // apply periodic boundary condition (PBC)
            rij=_H*sij; // get the interatomic distance vector (with PBC)
            r2=rij.norm2(); // get the square of interatomic distance
            r=sqrt(r2); // interatomic distance
            
            if(r<=PP_RC) // if the interatomic distance is shorter than the cut-off radius
	      {
                // precalculate r^-6
                ri6=1.0/(r2*r2*r2);
		
                // calculate the cutoff variables
                if(r>PP_RC-1.0)
		  {
                    x=r-PP_RC+1.0;
                    xcube=CUBE(x);
                    cx1=SQR(1.0-xcube*x);
		  }
                else
		  {
                    cx1=1.0;
		  }

                // get the species indices
                isp=species[ipt]; // species of ipt-atom
                jsp=species[jpt]; // species of jpt-atom
                QQ=_SPCHARGEMD[isp]*_SPCHARGEMD[jsp]; // get the charge term
                k=isp+jsp; // interaction index
                
                // calculate potential
                UB=0.0; UC=0.0;
                UB=(-BMB_POT[k][0]*ri6+BMB_POT[k][1]*exp(-BMB_POT[k][2]*r))*cx1;
                UC=QQ/r*cx1; //Truncated Coulomb
                U=UB+UC;
                
                // add pair terms to the accumulative sum
                _EREAL+=U;
		
                // store the pairpotential energies
                realpairE2[pairindex]=U;
	      }
	  }
      }
    NMC_EREAL2=_EREAL;
    eRP_REAL=clock();
    //-------------------------------------------------------------------
    //               Sum Up Potential Energy
    //-------------------------------------------------------------------
    _EPOT=_EREAL;
    INFO("[RPN3] _EREAL = "<<_EREAL);
    iRP_REAL=(-(double(sRP_REAL))+(double(eRP_REAL)))/CLOCKS_PER_SEC;
    //INFO("[RPN3] Real Space Calculation : "<<iRP_REAL<<" sec");
}

//-------------------------------------------------------------------
// Potential for MC (Reduced Algorithm)
//-------------------------------------------------------------------
void YZOFrame::NMC_R3_potential()
{
    int i;
    // execute potential function
    if(checkneighborlist==1)
      {
	//if(NbrList_needrefresh())
	//{
	//NbrList_reconstruct();
	NMC_R3_potential_A();
	//checkneighborlist=1;
	for(i=0;i<maxnumofpairs;i++){realpairE2[i]=0.0;}
	//INFO("NMC - Neighbor list reconstruction requested ********");
      }
    else
      {
        NMC_R3_potential_B();
      }
}

//-------------------------------------------------------------------
// Potential for MC (Initial calculation)
//-------------------------------------------------------------------
void YZOFrame::NMC_R3_potential_A()
{
    int j,k,ipt,jpt,isp,jsp;
    double U,UB,UC,r,r2,ri6,x,xcube,cx1,QQ;
    Vector3 sij, rij;

    // initialize potentials
    _EPOT=0.0; _EREAL=0.0; _EREC=0.0; dEREAL=0.0; dEREC=0.0;

    //-------------------------------------------------------------------
    //       Calculating real part potential in normal fashion
    //-------------------------------------------------------------------
    sRP_REAL=clock();
    for(ipt=0;ipt<_NP;ipt++) // for all particles
    {
        for(j=0;j<nn[ipt];j++) // for all neighbors
	{
            jpt=nindex[ipt][j]; // get the atom index of j-th neighbor
            if(ipt>=jpt) continue; // only if i<=j -> avoid redundancy
            sij=_SR[jpt]-_SR[ipt]; // scaled interatomics distance
            sij.subint(); // apply periodic boundary condition (PBC)
            rij=_H*sij; // get the interatomic distance vector (with PBC)
            r2=rij.norm2(); // get the square of interatomic distance
            r=sqrt(r2); // interatomic distance
            
            if(r<=PP_RC) // if the interatomic distance is shorter than the cut-off radius
	    {
                // precalculate r^-6
                ri6=1.0/(r2*r2*r2);

                // calculate the cutoff variables
                if(r>PP_RC-1.0)
                {
                    x=r-PP_RC+1.0;
                    xcube=CUBE(x);
                    cx1=SQR(1.0-xcube*x);
                }
                else
                {
                    cx1=1.0;
                }

                // get the species indices
                isp=species[ipt]; // species of ipt-atom
                jsp=species[jpt]; // species of jpt-atom
                QQ=_SPCHARGEMD[isp]*_SPCHARGEMD[jsp]; // get the charge term
                k=isp+jsp; // interaction index
                
                // calculate potential
                UB=0.0; UC=0.0;
                UB=(-BMB_POT[k][0]*ri6+BMB_POT[k][1]*exp(-BMB_POT[k][2]*r))*cx1;
                UC=QQ/r*cx1;
                U=UB+UC;
		               
                // add pair terms to the accumulative sum
                _EREAL+=U;
	    }
	}
    }
    dEREAL=_EREAL-NMC_EREAL2;
    eRP_REAL=clock();
    //-------------------------------------------------------------------
    //               Sum Up Potential Energy
    //-------------------------------------------------------------------
    _EPOT=_EREAL;
    INFO("[RPA3] _EREAL = "<<_EREAL);
    iRP_REAL=(-(double(sRP_REAL))+(double(eRP_REAL)))/CLOCKS_PER_SEC;
    INFO("[RPA3] Real Space Calculation : "<<iRP_REAL<<" sec");
}

//-------------------------------------------------------------------
// Potential for MC (Reduced Algorithm)
//-------------------------------------------------------------------
void YZOFrame::NMC_R3_potential_B()
{
    int j,k,ipt,jpt,isp,jsp;
    double U,UB,UC,r,r2,ri6,x,xcube,cx1,QQ;
    Vector3 sij, rij;
    
    // initialize potentials 
    _EPOT=0.0; _EREAL=0.0; _EREC=0.0; dEREAL=0.0; dEREC=0.0; pairindex=-1;

    //-------------------------------------------------------------------
    //       Calculating potential for the first time (full scan)
    //-------------------------------------------------------------------
    sRP_REAL=clock();
    for(ipt=0;ipt<_NP;ipt++) // for all atoms
      {
	for(j=0;j<nn[ipt];j++) // for all neighbors
	  {
	    jpt=nindex[ipt][j]; // get the atom index of j-th neighbor
	    if(ipt>=jpt) continue;
            pairindex++;
            if((trialatomindex[ipt]==0)&&(trialatomindex[jpt]==0)) continue;

	    sij=_SR[jpt]-_SR[ipt]; // scaled interatomics distance
            sij.subint(); // apply periodic boundary condition (PBC)
            rij=_H*sij; // get the interatomic distance vector (with PBC)
            r2=rij.norm2(); // get the square of interatomic distance
            r=sqrt(r2); // interatomic distance

            if(r<=PP_RC) // if the interatomic distance is shorter than the cut-off radius
	      {
		    // precalculate r^-6
		    ri6=1.0/(r2*r2*r2);
		    
		    // calculate the cutoff variables
		    if(r>PP_RC-1.0)
		      {
			x=r-PP_RC+1.0;
			xcube=CUBE(x);
			cx1=SQR(1.0-xcube*x);
		      }
		    else
		      {
			cx1=1.0;
		      }
		    
		    // get the species indices
		    isp=species[ipt]; // species of ipt-atom
		    jsp=species[jpt]; // species of jpt-atom
		    QQ=_SPCHARGEMD[isp]*_SPCHARGEMD[jsp]; // get the charge term
		    k=isp+jsp; // interaction index
		    
		    // calculate potential
		    UB=0.0; UC=0.0;
		    UB=(-BMB_POT[k][0]*ri6+BMB_POT[k][1]*exp(-BMB_POT[k][2]*r))*cx1;
		    UC=QQ/r*cx1;
		    U=UB+UC;

                    // add pair terms to the accumulative sum
                   dEREAL+=(U-realpairE2[pairindex]);
	      }
	  }
      }
    _EREAL=NMC_EREAL2+dEREAL;
    eRP_REAL=clock();
    //-------------------------------------------------------------------
    //               Sum Up Potential Energy
    //-------------------------------------------------------------------
    _EPOT=_EREAL;
    INFO("[RPB3] _EREAL = "<<_EREAL);
    iRP_REAL=(-(double(sRP_REAL))+(double(eRP_REAL)))/CLOCKS_PER_SEC;
    //INFO("[RPB3] Real Space Calculation : "<<iRP_REAL<<" sec");
}

//-------------------------------------------------------------------
//  Normal Ewald Summation Part
//-------------------------------------------------------------------
void YZOFrame::NMC_N_CE()
{
    int i, ik, kal, kbl; //Last calculated sinxy/cosxy
    int ka, kb, kc;
    double k2, kx, ky, kz;
    
    // recalculate the system volume (in case the box is changing)
    if(conj_fixbox==0){cell_V=_H.det();}

    // precalculate exp(iKx*rx), exp(iKy*ry), exp(iKz*rz)
    fillSinCosTables();
    
    kal=-1; // an impossible value
    kbl=0;  // Just to avoid a warning
    for(ik=0;ik<nKV;ik++) // for all K-vectors
    {
        double UE, EXP;
        EComplex sincosk;
        // kx, ky, kz calculated in enumKV()
        kx=KV[ik].kx;
        ky=KV[ik].ky;
        kz=KV[ik].kz;
        k2=KV[ik].k2;
        ka=KV[ik].a;
        kb=KV[ik].b;
        kc=KV[ik].c;
        
        //quick sckk [ Qj*exp(iKr) ] calculation
        if(kal!=ka || kbl!=kb)  // Re-calculate sc_axby
	{
            kal=ka;
            kbl=kb;
            if(kb>=0)
	    {
                for(i=0;i<_NP;i++)
		{
                    EComplex::Mul(scx[ka][i], scy[kb][i], sc_axby[i]);
		}
	    }
            else
	    {
                for(i=0;i<_NP;i++)
		{
                    EComplex::MulConjugate(scx[ka][i], scy[-kb][i], sc_axby[i]);
		}
	    }
	}
        sincosk.Re=0.0;
        sincosk.Im=0.0;
        
        if(kc>=0)
	{
            for(i=0;i<_NP;i++) 
	    {
                EComplex::Mul(sc_axby[i], scz[kc][i], sckk[i]);
                sckk[i]*=_SPCHARGEMD[species[i]];
                sincosk+=sckk[i];
	    }
	}
        else
	{
            for(i=0;i<_NP;i++)
	    {
                EComplex::MulConjugate(sc_axby[i], scz[-kc][i], sckk[i]);
                sckk[i]*=_SPCHARGEMD[species[i]];
                sincosk+=sckk[i];
	    }
	}
        MCsincosk[ik]=sincosk;
        EXP=((8.0*M_PI)/cell_V)*exp(-k2/(4.0*SQR(Alpha)))/k2;
        UE=EXP/2.0*sincosk.Norm2();
        
        // Potential Energy Calculation
        _EREC+=UE;
    }
    //INFO("[NMC_N_CE] Reciprocal Energy Calculated by NMC_N_CE = "<<_EREC);
}

//-------------------------------------------------------------------
//  Reduced Filling Sin & Cos Table
//-------------------------------------------------------------------
// calculate exp(iKx*rx), exp(iKy*ry), exp(iKz*rz) 
// for all atoms and all K-vectors, should be updated every time step
//-------------------------------------------------------------------
void YZOFrame::NMC_R_fillSinCosTables()
{
    int i, k, ki;
    EComplex *MCsc1;
    for(k=0;k<numtrialatoms;k++)
    {
        i=trialatomlist[k];
        MCscx[0][i].Re=1.0; MCscx[0][i].Im=0.0;
        MCscy[0][i].Re=1.0; MCscy[0][i].Im=0.0;
        MCscz[0][i].Re=1.0; MCscz[0][i].Im=0.0;
    }
    if(Mka>0)
    {
        MCsc1=MCscx[1];
        for(k=0;k<numtrialatoms;k++)
        {
            i=trialatomlist[k];
            MCsc1[i].ExpI(2.0*M_PI*_SR[i].x);
	}
        for(ki=2;ki<=Mka;ki++)
	{ 
            for(k=0;k<numtrialatoms;k++)
            {
                i=trialatomlist[k];
                EComplex::Mul(MCscx[ki-1][i], MCsc1[i], MCscx[ki][i]);
	    }
	}
    }
    if(Mkb>0) 
    {
        MCsc1=MCscy[1];
        for(k=0;k<numtrialatoms;k++)
        {
            i=trialatomlist[k];
            MCsc1[i].ExpI(2.0*M_PI*_SR[i].y);
	}
        for(ki=2;ki<=Mkb;ki++) 
	{
            for(k=0;k<numtrialatoms;k++)
            {
                i=trialatomlist[k];
                EComplex::Mul(MCscy[ki-1][i], MCsc1[i], MCscy[ki][i]);
	    }
	}
    }
    if(Mkc>0)
    {
        MCsc1=MCscz[1];
        for(k=0;k<numtrialatoms;k++)
        {
            i=trialatomlist[k];
            MCsc1[i].ExpI(2.0*M_PI*_SR[i].z);
	}
        for(ki=2;ki<=Mkc;ki++)
	{ 
            for(k=0;k<numtrialatoms;k++)
            {
                i=trialatomlist[k];
                EComplex::Mul(MCscz[ki-1][i], MCsc1[i], MCscz[ki][i]);
	    }
	}
    }
}

//-------------------------------------------------------------------
//  Reduced Ewald Summation Part
//-------------------------------------------------------------------
void YZOFrame::NMC_R_CE()
{
    int i, k, ik, kal, kbl; //Last calculated sinxy/cosxy
    int ka, kb, kc;
    double k2, kx, ky, kz;
    
    // recalculate the system volume (in case the box is changing)
    if(conj_fixbox==0){cell_V=_H.det();}
    
    // precalculate exp(iKx*rx), exp(iKy*ry), exp(iKz*rz)
    NMC_R_fillSinCosTables();
    
    kal=-1; // an impossible value
    kbl=0;  // Just to avoid a warning
    for(ik=0;ik<nKV;ik++) // for all K-vectors
    {
        double UE, EXP;
        EComplex sincosk;
        // kx, ky, kz calculated in enumKV()
        kx=KV[ik].kx;
        ky=KV[ik].ky;
        kz=KV[ik].kz;
        k2=KV[ik].k2;
        ka=KV[ik].a;
        kb=KV[ik].b;
        kc=KV[ik].c;
        
        //quick sckk [ Qj*exp(iKr) ] calculation
        if(kal!=ka || kbl!=kb)  // Re-calculate sc_axby
	{
            kal=ka;
            kbl=kb;
            if(kb>=0)
	    {
                for(k=0;k<numtrialatoms;k++)
		{
                    i=trialatomlist[k];
                    EComplex::Mul(scx[ka][i], scy[kb][i], sc_axby[i]);
                    EComplex::Mul(MCscx[ka][i], MCscy[kb][i], MCsc_axby[i]);
		}
	    }
            else
	    {
                for(k=0;k<numtrialatoms;k++)
		{
                    i=trialatomlist[k];
                    EComplex::MulConjugate(scx[ka][i], scy[-kb][i], sc_axby[i]);
                    EComplex::MulConjugate(MCscx[ka][i], MCscy[-kb][i], MCsc_axby[i]);
		}
	    }
	}
        sincosk.Re=0.0;
        sincosk.Im=0.0;
        
        if(kc>=0)
	{
            for(k=0;k<numtrialatoms;k++)
            {
                i=trialatomlist[k];
                EComplex::Mul(sc_axby[i], scz[kc][i], sckk[i]);
                EComplex::Mul(MCsc_axby[i], MCscz[kc][i], MCsckk[i]);
                if(species[i]==1)
                {
                    sckk[i]*=_SPCHARGEMD[1];
                    MCsckk[i]*=_SPCHARGEMD[1];
                }
                else if(species[i]==0)
                {
                    sckk[i]*=_SPCHARGEMD[3];
                    MCsckk[i]*=_SPCHARGEMD[0];
                }
                else if(species[i]==3)
                {
                    sckk[i]*=_SPCHARGEMD[0];
                    MCsckk[i]*=_SPCHARGEMD[3];
                }
                sincosk-=sckk[i];
                sincosk+=MCsckk[i];
            }
        }	
        else
	{
            for(k=0;k<numtrialatoms;k++)
            {
                i=trialatomlist[k];
                
                EComplex::MulConjugate(sc_axby[i], scz[-kc][i], sckk[i]);
                EComplex::MulConjugate(MCsc_axby[i], MCscz[-kc][i], MCsckk[i]);
                if(species[i]==1)
                {
                    sckk[i]*=_SPCHARGEMD[1];
                    MCsckk[i]*=_SPCHARGEMD[1];
                }
                else if(species[i]==0)
                {
                    sckk[i]*=_SPCHARGEMD[3];
                    MCsckk[i]*=_SPCHARGEMD[0];
                }
                else if(species[i]==3)
                {
                    sckk[i]*=_SPCHARGEMD[0];
                    MCsckk[i]*=_SPCHARGEMD[3];
                }
                sincosk-=sckk[i];
                sincosk+=MCsckk[i];
            }
        
        }
        sincosk+=MCsincosk[ik];
        EXP=((8.0*M_PI)/cell_V)*exp(-k2/(4.0*SQR(Alpha)))/k2;
        UE=EXP/2.0*sincosk.Norm2();
        
        // Potential Energy Calculation
        _EREC+=UE;
    }
    //INFO("[NMC_R_CE] Reciprocal Energy Calculated by NMC_R_CE = "<<_EREC);
}

//###################################################################
//  PME Functions
//###################################################################
//-------------------------------------------------------------------
//  Initialize Particle Mesh Ewald
//-------------------------------------------------------------------
void YZOFrame::PME_init()
{    
    int m1, m2, m3, tmp, tmp_index; // k-space indices
    ComplexType PME_BC_b1, PME_BC_b2, PME_BC_b3; // for the B matrix
    double tmp1, tmp2; // temporary dummy variable
    
    // k-space mesh setting
    PME_K23=PME_K2*PME_K3;
    PME_K123=PME_K1*PME_K23; // equal to the number of data points

    PME_K1d=((double)PME_K1);
    PME_K2d=((double)PME_K2);
    PME_K3d=((double)PME_K3);
    PME_K123d=((double)PME_K123);
    
    PME_K3S=(PME_K3/2)+1;
    PME_K23S=PME_K2*PME_K3S;
    PME_K123S=PME_K1*PME_K2*PME_K3S;
        
    // allocate array BC,Q,IQ,CONV, etc.
    Realloc(PME_BC,double,PME_K123);
    Realloc(PME_Q,double,(2*PME_K123S));
    Realloc(PME_IQ,ComplexType,PME_K123S);
    Realloc(PME_CONV,double,(2*PME_K123S));
    
    // scaled coefficients _UR
    Realloc(_UR,Vector3,_NP);
    
    // Mn(k+1) and factorial arrays
    Realloc(PME_bsp_Mk,double,(PME_bsp_n-1)); // caution : type double total (n-1) entries
    Realloc(PME_bsp_fac,double,(PME_bsp_n+1)); // caution : type integer total (n+1) entries
    
    // start and end indices of k-space mesh point
    Realloc(PME_m1s,int,_NP);
    Realloc(PME_m2s,int,_NP);
    Realloc(PME_m3s,int,_NP);
    
    // effective k-space indices
    tmp=PME_bsp_n*_NP;
    Realloc(PME_m1,int,PME_bsp_n);
    Realloc(PME_m2,int,PME_bsp_n);
    Realloc(PME_m3,int,PME_bsp_n);
    Realloc(PME_m1mod,int,tmp);
    Realloc(PME_m2mod,int,tmp);
    Realloc(PME_m3mod,int,tmp);
    
    // (u-k),Mn(u-k),Mn-1(u-k),Mn-1(u-k-1) arrays
    Realloc(PME_x1,double,tmp);
    Realloc(PME_x2,double,tmp);
    Realloc(PME_x3,double,tmp);
    Realloc(PME_MU1,double,tmp);
    Realloc(PME_MU2,double,tmp);
    Realloc(PME_MU3,double,tmp);
    Realloc(PME_d1MU1,double,PME_bsp_n);
    Realloc(PME_d1MU2,double,PME_bsp_n);
    Realloc(PME_d1MU3,double,PME_bsp_n);
    Realloc(PME_d2MU1,double,PME_bsp_n);
    Realloc(PME_d2MU2,double,PME_bsp_n);
    Realloc(PME_d2MU3,double,PME_bsp_n);

    // prepare coefficient array which will be used in force calculation
    PME_C1[0]=(_SPCHARGEMD[0]*PME_K123d*PME_K1d*_H_L1R);
    PME_C2[0]=(_SPCHARGEMD[0]*PME_K123d*PME_K2d*_H_L2R);
    PME_C3[0]=(_SPCHARGEMD[0]*PME_K123d*PME_K3d*_H_L3R);
    PME_C1[1]=(_SPCHARGEMD[1]*PME_K123d*PME_K1d*_H_L1R);
    PME_C2[1]=(_SPCHARGEMD[1]*PME_K123d*PME_K2d*_H_L2R);
    PME_C3[1]=(_SPCHARGEMD[1]*PME_K123d*PME_K3d*_H_L3R);
    PME_C1[2]=0.0;
    PME_C2[2]=0.0;
    PME_C3[2]=0.0;
    PME_C1[3]=(_SPCHARGEMD[3]*PME_K123d*PME_K1d*_H_L1R);
    PME_C2[3]=(_SPCHARGEMD[3]*PME_K123d*PME_K2d*_H_L2R);
    PME_C3[3]=(_SPCHARGEMD[3]*PME_K123d*PME_K3d*_H_L3R);

    // prepare data which will be used in the calculation
    PME_cal_bsp_fac();
    PME_cal_bsp_Mk();
    
    // calculate matrix B and C
    for (m1=0;m1<PME_K1;m1++) // from 0 to (K1-1)
    {
        for (m2=0;m2<PME_K2;m2++) // from 0 to (K2-1)
        {
            for (m3=0;m3<PME_K3;m3++) // from 0 to (K3-1)
            {
                tmp_index=m3+m2*PME_K3+m1*PME_K23;

                // calculate each B and C component
                PME_cal_bsp_Bm(m1,PME_K1); PME_BC_b1[0]=R_bsp_Bm[0];  PME_BC_b1[1]=R_bsp_Bm[1];
                PME_cal_bsp_Bm(m2,PME_K2); PME_BC_b2[0]=R_bsp_Bm[0];  PME_BC_b2[1]=R_bsp_Bm[1];
                PME_cal_bsp_Bm(m3,PME_K3); PME_BC_b3[0]=R_bsp_Bm[0];  PME_BC_b3[1]=R_bsp_Bm[1];
                tmp1=PME_cal_bsp_Cm(m1,m2,m3);
                tmp2=(SQR(PME_BC_b1[0])+SQR(PME_BC_b1[1]))*(SQR(PME_BC_b2[0])+SQR(PME_BC_b2[1]))*(SQR(PME_BC_b3[0])+SQR(PME_BC_b3[1]));
                
                // compute BC matrix entries
                PME_BC[tmp_index]=tmp1*tmp2;
            }
        }
    }

    // prepare fft plans
    PME_fft_plan1=fftw_plan_dft_r2c_3d(PME_K1,PME_K2,PME_K3,PME_Q,PME_IQ,FFTW_ESTIMATE);
    PME_fft_plan2=fftw_plan_dft_c2r_3d(PME_K1,PME_K2,PME_K3,PME_IQ,PME_CONV,FFTW_ESTIMATE);

    // Print Out PME Information
    INFO("------------------------------------------------------------------------------");
    INFO("------------------------------------------------------------------------------");
    INFO("            Particle Mesh Ewald Parameters [Long Range Interaction] ");
    INFO("------------------------------------------------------------------------------");
    INFO("    Real Space Cutoff Radius (Rc)                 = "<<PP_RC);
    INFO("    Ewald Gaussian Factor (Alpha)                 = "<<Alpha);
    INFO("    Mesh Points in X-direction (PME_K1)           = "<<PME_K1);
    INFO("    Mesh Points in Y-direction (PME_K2)           = "<<PME_K2);
    INFO("    Mesh Points in Z-direction (PME_K3)           = "<<PME_K3);
    INFO("    Order of B-spline Interpolation (PME_bsp_n)   = "<<PME_bsp_n);
    INFO("------------------------------------------------------------------------------");
    INFO("------------------------------------------------------------------------------");
}

//-------------------------------------------------------------------
//  Calculate Factorials
//-------------------------------------------------------------------
void YZOFrame::PME_cal_bsp_fac()
{
    int i, j, mult;
    
    // calculate factorials (0! to PME_bsp_n!)
    PME_bsp_fac[0]=1.0; // 0!=1
    for (i=1;i<=PME_bsp_n;i++) // from 1 to n
    {
        mult=1;
        for (j=1;j<=i;j++)
        {
            mult=mult*j;
        }
        PME_bsp_fac[i]=((double)mult);
        //INFO("[PME] Calculating Factorial => "<<i<<"! = "<<mult);
    }
}

//-------------------------------------------------------------------
//  Calculate Mn(n,k+1)
//-------------------------------------------------------------------
void YZOFrame::PME_cal_bsp_Mk()
{
    int i;
    
    // calculate Mn(k+1)
    for (i=0;i<(PME_bsp_n-1);i++) // from 0 to n-2
    {
        PME_bsp_Mk[i]=PME_cal_bsp_Mu(PME_bsp_n,((double)(i+1))); // Mn(n,i+1) => from 1 to n-1
        //INFO("[PME] bsp_Mk["<<i<<"] = "<<PME_bsp_Mk[i]);
    }
}

//-------------------------------------------------------------------
//  Calculate Mn(n,u)
//-------------------------------------------------------------------
double YZOFrame::PME_cal_bsp_Mu(int n, double u)
{
    int i,k;
    double bsp_sign, bsp_Mu, bsp_Muk, tmp;

    // calculate Mn(u-k)
    bsp_sign=1.0; // start from +1
    bsp_Mu=0.0; // initialize sum quantity
    for (k=0;k<=n;k++) // from 0 to n
    {
        tmp=u-((double)k); // convert k to double and calculate (u-k)
        if(tmp>0.0) // only when (u-k)>0
        {
            // calculate (u-k)^(n-1)
            bsp_Muk=1.0;
            for (i=1;i<n;i++) // from 1 to n-1
            {
                bsp_Muk*=tmp;
            }
            // calculate n!/[k!(n-k)!]*(u-k)^(n-1) and sum it up
            bsp_Mu+=(bsp_sign*PME_bsp_fac[n]/(PME_bsp_fac[k]*PME_bsp_fac[n-k])*bsp_Muk);
            bsp_sign*=-1.0;
        }
    }
    bsp_Mu/=PME_bsp_fac[n-1]; // divide by (n-1)!
    return bsp_Mu;
}

//-------------------------------------------------------------------
//  Calculate bi(mi)
//-------------------------------------------------------------------
void YZOFrame::PME_cal_bsp_Bm(int mi, int Ki)
{
    int k;
    double tmp;
    ComplexType bsp_bm, bsp_bms, bsp_bmn;
    
    // initialize the summation dummy complex number
    bsp_bms[0]=0.0;
    bsp_bms[1]=0.0;
    
    // calculate the B matrix coefficient Bi(mi)
    // compute denominator c+di
    for (k=0;k<(PME_bsp_n-1);k++) // from 0 to (n-2)
    {
        tmp=2.0*M_PI*(((double)(mi*k))/((double)Ki));
        bsp_bms[0]+=PME_bsp_Mk[k]*cos(tmp);
        bsp_bms[1]+=PME_bsp_Mk[k]*sin(tmp);
    }
    // compute numerator a+bi
    tmp=2.0*M_PI*(((double)((PME_bsp_n-1)*mi))/((double)Ki));
    bsp_bmn[0]=cos(tmp);
    bsp_bmn[1]=sin(tmp);
    
    // compute c^2+d^2
    tmp=SQR(bsp_bms[0])+SQR(bsp_bms[1]);
    
    // compute (a+bi)/(c+di) = [(ac+bd)+(bc-ad)i]/(c^2+d^2)
    bsp_bm[0]=(bsp_bmn[0]*bsp_bms[0]+bsp_bmn[1]*bsp_bms[1])/tmp; //(ac+bd)
    bsp_bm[1]=(bsp_bmn[1]*bsp_bms[0]-bsp_bmn[0]*bsp_bms[1])/tmp; //(bc-ad)
    R_bsp_Bm[0]=bsp_bm[0];
    R_bsp_Bm[1]=bsp_bm[1];
}

//-------------------------------------------------------------------
//  Calculate C(m1,m2,m3)
//-------------------------------------------------------------------
double YZOFrame::PME_cal_bsp_Cm(int m1, int m2, int m3)
{
    double msq, m1s, m2s, m3s, tmp1, tmp2, bsp_Cm;
    
    if((m1==0)&&(m2==0)&&(m3==0))
    {
        bsp_Cm=0.0;
    }
    else
    {
        if(m1>(PME_K1/2)){m1=m1-PME_K1;}
        if(m2>(PME_K2/2)){m2=m2-PME_K2;}
        if(m3>(PME_K3/2)){m3=m3-PME_K3;}
        m1s=((double)m1)*_H_L1R; // m1/L1 => double
        m2s=((double)m2)*_H_L2R; // m2/L2 => double
        m3s=((double)m3)*_H_L3R; // m3/L3 => double
        msq=SQR(m1s)+SQR(m2s)+SQR(m3s);
        tmp1=-SQR(M_PI)*msq/(SQR(Alpha));
        tmp2=1.0/M_PI/cell_V/msq;
        bsp_Cm=tmp2*exp(tmp1);
    }
    return bsp_Cm;
}

//-------------------------------------------------------------------
//  Particle Mesh Ewald
//-------------------------------------------------------------------
void YZOFrame::PME()
{
    int i, j, i1, i2, i3, m1, m2, m3;
    int tmp_ind_i, tmp_ind_1, tmp_ind_2, tmp_ind_3, tmp_index, tmp_sp;
    double tmp_double, tmp_charge;
    
    for (i1=0;i1<PME_K1;i1++)
    {
        for (i2=0;i2<PME_K2;i2++)
        {
            for (i3=0;i3<PME_K3;i3++)
            {
                PME_Q[i3+i2*PME_K3+i1*PME_K23]=0.0;
            }
        }
    }
    
    // get the scaled and manipulated coordinates UR
    for (i=0;i<_NP;i++)
    {
        _UR[i].x=(_SR[i].x+0.5)*PME_K1d;
        _UR[i].y=(_SR[i].y+0.5)*PME_K2d;
        _UR[i].z=(_SR[i].z+0.5)*PME_K3d;
    }
    
    // Calculate Q matrix
    for (i=0;i<_NP;i++) // for all atoms
    {
        tmp_ind_i=PME_bsp_n*i;
        tmp_charge=_SPCHARGEMD[species[i]];
        
        // since function bsp_Mu has bounded support, we can find the initial and
        // final k-space indices of each atom where Q matrix entries are non-zero
        // [ start index : floor(u)-n+1    end index : floor(u) ]
        
        PME_m1s[i]=((int)floor(_UR[i].x))-PME_bsp_n+1;
        PME_m2s[i]=((int)floor(_UR[i].y))-PME_bsp_n+1;
        PME_m3s[i]=((int)floor(_UR[i].z))-PME_bsp_n+1;
   
        for (j=0;j<PME_bsp_n;j++) // from 0 to (n-1)
        {
            // calculate temporary index
            tmp_index =j+tmp_ind_i;
            
            // goes through effective range of k-space for atoms i
            PME_m1[j]=PME_m1s[i]+j; // in x-direction
            PME_m2[j]=PME_m2s[i]+j; // in y-direction
            PME_m3[j]=PME_m3s[i]+j; // in z-direction
            
            // apply periodic boundary condition
            PME_m1mod[tmp_index]=PME_int_mod(PME_m1[j],PME_K1);
            PME_m2mod[tmp_index]=PME_int_mod(PME_m2[j],PME_K2);
            PME_m3mod[tmp_index]=PME_int_mod(PME_m3[j],PME_K3);
            
            // calculate (u-k)
            PME_x1[tmp_index]=_UR[i].x-((double)PME_m1[j]);
            PME_x2[tmp_index]=_UR[i].y-((double)PME_m2[j]);
            PME_x3[tmp_index]=_UR[i].z-((double)PME_m3[j]);
            
            // calculate Mn(u) coefficients
            PME_MU1[tmp_index]=PME_cal_bsp_Mu(PME_bsp_n,PME_x1[tmp_index]);
            PME_MU2[tmp_index]=PME_cal_bsp_Mu(PME_bsp_n,PME_x2[tmp_index]);
            PME_MU3[tmp_index]=PME_cal_bsp_Mu(PME_bsp_n,PME_x3[tmp_index]);
        }
        
        // scan the atomwise small cubic which has nonzero entries
        for (i1=0;i1<PME_bsp_n;i1++) // from 0 to (n-1)
        {
            for (i2=0;i2<PME_bsp_n;i2++) // from 0 to (n-1)
            {
                for (i3=0;i3<PME_bsp_n;i3++) // from 0 to (n-1)
                {
                    tmp_ind_1=i1+tmp_ind_i;
                    tmp_ind_2=i2+tmp_ind_i;
                    tmp_ind_3=i3+tmp_ind_i;
                    tmp_index=PME_m3mod[tmp_ind_3]+PME_m2mod[tmp_ind_2]*PME_K3+PME_m1mod[tmp_ind_1]*PME_K23;
                    PME_Q[tmp_index]+=tmp_charge*PME_MU1[tmp_ind_1]*PME_MU2[tmp_ind_2]*PME_MU3[tmp_ind_3];
                }
            }
        }
    }
    
    // out-of-place backward fft of matrix PME_Q (backward fft in the paper => forward fft for the fftw)
    fftw_execute(PME_fft_plan1); // real to complex
    
    // matrix multiplication of PME_BC and PME_IQ -> (IQ*BC)
    for (m1=0;m1<PME_K1;m1++) // from 0 to (PME_K1-1)
    {
        for (m2=0;m2<PME_K2;m2++) // from 0 to (PME_K2-1)
        {
            for (m3=0;m3<PME_K3S;m3++) // from 0 to (PME_K3S-1)
            {
                tmp_ind_1=m3+m2*PME_K3S+m1*PME_K23S;
                tmp_index=m3+m2*PME_K3+m1*PME_K23;
                tmp_double=PME_BC[tmp_index]/PME_K123d;                
                PME_IQ[tmp_ind_1][0]*=tmp_double;
                PME_IQ[tmp_ind_1][1]*=tmp_double;
            }
        }
    }

    // in-place forward fft of matrix PME_CONV (forward fft in the paper => backward fft for the fftw)
    fftw_execute(PME_fft_plan2); // complex to real

    // matrix multiplication of PME_Q and PME_CONV and calculate the reciprocal energy simultaneously
    for (m1=0;m1<PME_K1;m1++) // from 0 to (PME_K1-1)
    {
        for (m2=0;m2<PME_K2;m2++)  // from 0 to (PME_K2-1)
        {
            for (m3=0;m3<PME_K3;m3++) // from 0 to (PME_K3-1)
            {
                tmp_index=m3+m2*PME_K3+m1*PME_K23;
                _EREC+=(PME_Q[tmp_index]*PME_CONV[tmp_index]);
            }
        }
    }
    _EREC*=(PME_K123d/(2.0));
    //INFO("[PME] Reciprocal Energy Calculated by PME = "<<_EREC);
    
    // force calculation - Calculate dQ/dr matrix
    for (i=0;i<_NP;i++) // for all atoms
    {
        tmp_ind_i=PME_bsp_n*i;
        for (j=0;j<PME_bsp_n;j++) // from 0 to (n-1)
        {
            // calculate temporary index
            tmp_index =j+tmp_ind_i;
            
            PME_d1MU1[j]=PME_cal_bsp_Mu((PME_bsp_n-1),PME_x1[tmp_index]); // Mn-1(u1-k1)
            PME_d1MU2[j]=PME_cal_bsp_Mu((PME_bsp_n-1),PME_x2[tmp_index]); // Mn-1(u2-k2)
            PME_d1MU3[j]=PME_cal_bsp_Mu((PME_bsp_n-1),PME_x3[tmp_index]); // Mn-1(u3-k3)
            
            PME_d2MU1[j]=PME_cal_bsp_Mu((PME_bsp_n-1),(PME_x1[tmp_index]-1.0)); // Mn-1(u1-k1-1)
            PME_d2MU2[j]=PME_cal_bsp_Mu((PME_bsp_n-1),(PME_x2[tmp_index]-1.0)); // Mn-1(u2-k2-1)
            PME_d2MU3[j]=PME_cal_bsp_Mu((PME_bsp_n-1),(PME_x3[tmp_index]-1.0)); // Mn-1(u3-k3-1)
        }
        
        // scan the atomwise small cubic which has nonzero entries
        for (i1=0;i1<PME_bsp_n;i1++) // from 0 to (n-1)
        {
            for (i2=0;i2<PME_bsp_n;i2++) // from 0 to (n-1)
            {
                for (i3=0;i3<PME_bsp_n;i3++) // from 0 to (n-1)
                {
                    tmp_ind_1=i1+tmp_ind_i;
                    tmp_ind_2=i2+tmp_ind_i;
                    tmp_ind_3=i3+tmp_ind_i;
                    tmp_index=PME_m3mod[tmp_ind_3]+PME_m2mod[tmp_ind_2]*PME_K3+PME_m1mod[tmp_ind_1]*PME_K23;
                    _FREC[i].x+=((PME_d2MU1[i1]-PME_d1MU1[i1])*PME_MU2[tmp_ind_2]*PME_MU3[tmp_ind_3]*PME_CONV[tmp_index]);
                    _FREC[i].y+=((PME_d2MU2[i2]-PME_d1MU2[i2])*PME_MU1[tmp_ind_1]*PME_MU3[tmp_ind_3]*PME_CONV[tmp_index]);
                    _FREC[i].z+=((PME_d2MU3[i3]-PME_d1MU3[i3])*PME_MU1[tmp_ind_1]*PME_MU2[tmp_ind_2]*PME_CONV[tmp_index]);
                }
            }
        }
	tmp_sp=species[i];
        _FREC[i].x*=PME_C1[tmp_sp];
        _FREC[i].y*=PME_C2[tmp_sp];
        _FREC[i].z*=PME_C3[tmp_sp];
    }
}

//-------------------------------------------------------------------
//  Clear Particle Mesh Ewald
//-------------------------------------------------------------------
void YZOFrame::PME_clear()
{
    // free variables used in the Particle Mesh Ewald summation
    free(_UR); free(PME_bsp_Mk); free(PME_bsp_fac);
    
    free(PME_m1s); free(PME_m2s); free(PME_m3s);
    free(PME_m1); free(PME_m2); free(PME_m3);
    free(PME_m1mod); free(PME_m2mod); free(PME_m3mod);
    free(PME_x1); free(PME_x2); free(PME_x3);
    
    free(PME_MU1); free(PME_MU2); free(PME_MU3);
    free(PME_d1MU1); free(PME_d1MU2); free(PME_d1MU3);
    free(PME_d2MU1); free(PME_d2MU2); free(PME_d2MU3);
    
    free(PME_Q); free(PME_IQ);
    free(PME_BC); free(PME_CONV);
    
    fftw_destroy_plan(PME_fft_plan1);
    fftw_destroy_plan(PME_fft_plan2);

    // free variables used in the NMC Reduced PME
    free(NMC_UR);

    free(NMC_PME_m1); free(NMC_PME_m2); free(NMC_PME_m3);
    free(NMC_PME_m1mod); free(NMC_PME_m2mod); free(NMC_PME_m3mod);
    free(NMC_PME_x1); free(NMC_PME_x2); free(NMC_PME_x3);
    free(NMC_PME_MU1); free(NMC_PME_MU2); free(NMC_PME_MU3);
    
    free(NMC_PME_Q);
    
    fftw_destroy_plan(NMC_PME_fft_plan1);
}

//-------------------------------------------------------------------
//  NMC Normal Particle Mesh Ewald
//-------------------------------------------------------------------
void YZOFrame::NMC_N_PME()
{
    int i, j, i1, i2, i3, m1, m2, m3;
    int tmp_ind_i, tmp_ind_1, tmp_ind_2, tmp_ind_3, tmp_index;
    double tmp_double, tmp_charge;
    
    for (i1=0;i1<PME_K1;i1++)
    {
        for (i2=0;i2<PME_K2;i2++)
        {
            for (i3=0;i3<PME_K3;i3++)
            {
                PME_Q[i3+i2*PME_K3+i1*PME_K23]=0.0;
            }
        }
    }
    
    // get the scaled and manipulated coordinates UR
    for (i=0;i<_NP;i++)
    {
        _UR[i].x=(_SR[i].x+0.5)*PME_K1d;
        _UR[i].y=(_SR[i].y+0.5)*PME_K2d;
        _UR[i].z=(_SR[i].z+0.5)*PME_K3d;
    }
    
    // Calculate Q matrix
    for (i=0;i<_NP;i++) // for all atoms
    {
        tmp_ind_i=PME_bsp_n*i;
        tmp_charge=_SPCHARGEMD[species[i]];
                
        PME_m1s[i]=((int)floor(_UR[i].x))-PME_bsp_n+1;
        PME_m2s[i]=((int)floor(_UR[i].y))-PME_bsp_n+1;
        PME_m3s[i]=((int)floor(_UR[i].z))-PME_bsp_n+1;
   
        for (j=0;j<PME_bsp_n;j++) // from 0 to (n-1)
        {
            // calculate temporary index
            tmp_index =j+tmp_ind_i;
            
            // goes through effective range of k-space for atoms i
            PME_m1[j]=PME_m1s[i]+j; // in x-direction
            PME_m2[j]=PME_m2s[i]+j; // in y-direction
            PME_m3[j]=PME_m3s[i]+j; // in z-direction
            
            // apply periodic boundary condition
            PME_m1mod[tmp_index]=PME_int_mod(PME_m1[j],PME_K1);
            PME_m2mod[tmp_index]=PME_int_mod(PME_m2[j],PME_K2);
            PME_m3mod[tmp_index]=PME_int_mod(PME_m3[j],PME_K3);
            
            // calculate (u-k)
            PME_x1[tmp_index]=_UR[i].x-((double)PME_m1[j]);
            PME_x2[tmp_index]=_UR[i].y-((double)PME_m2[j]);
            PME_x3[tmp_index]=_UR[i].z-((double)PME_m3[j]);
            
            // calculate Mn(u) coefficients
            PME_MU1[tmp_index]=PME_cal_bsp_Mu(PME_bsp_n,PME_x1[tmp_index]);
            PME_MU2[tmp_index]=PME_cal_bsp_Mu(PME_bsp_n,PME_x2[tmp_index]);
            PME_MU3[tmp_index]=PME_cal_bsp_Mu(PME_bsp_n,PME_x3[tmp_index]);
        }
        
        // scan the atomwise small cubic which has nonzero entries
        for (i1=0;i1<PME_bsp_n;i1++) // from 0 to (n-1)
        {
            for (i2=0;i2<PME_bsp_n;i2++) // from 0 to (n-1)
            {
                for (i3=0;i3<PME_bsp_n;i3++) // from 0 to (n-1)
                {
                    tmp_ind_1=i1+tmp_ind_i;
                    tmp_ind_2=i2+tmp_ind_i;
                    tmp_ind_3=i3+tmp_ind_i;
                    tmp_index=PME_m3mod[tmp_ind_3]+PME_m2mod[tmp_ind_2]*PME_K3+PME_m1mod[tmp_ind_1]*PME_K23;
                    PME_Q[tmp_index]+=tmp_charge*PME_MU1[tmp_ind_1]*PME_MU2[tmp_ind_2]*PME_MU3[tmp_ind_3];
                }
            }
        }
    }

    // out-of-place backward fft of matrix PME_Q (backward fft in the paper => forward fft for the fftw)
    fftw_execute(PME_fft_plan1); // real to complex
    
    // matrix multiplication of PME_BC and PME_IQ -> (IQ*BC)
    for (m1=0;m1<PME_K1;m1++) // from 0 to (PME_K1-1)
    {
        for (m2=0;m2<PME_K2;m2++) // from 0 to (PME_K2-1)
        {
            for (m3=0;m3<PME_K3S;m3++) // from 0 to (PME_K3S-1)
            {
                tmp_ind_1=m3+m2*PME_K3S+m1*PME_K23S;
                tmp_index=m3+m2*PME_K3+m1*PME_K23;
                tmp_double=PME_BC[tmp_index]/PME_K123d;                
                PME_IQ[tmp_ind_1][0]*=tmp_double;
                PME_IQ[tmp_ind_1][1]*=tmp_double;
            }
        }
    }

    // in-place forward fft of matrix PME_CONV (forward fft in the paper => backward fft for the fftw)
    fftw_execute(PME_fft_plan2); // complex to real

    // matrix multiplication of PME_Q and PME_CONV and calculate the reciprocal energy simultaneously
    for (m1=0;m1<PME_K1;m1++) // from 0 to (PME_K1-1)
    {
        for (m2=0;m2<PME_K2;m2++)  // from 0 to (PME_K2-1)
        {
            for (m3=0;m3<PME_K3;m3++) // from 0 to (PME_K3-1)
            {
                tmp_index=m3+m2*PME_K3+m1*PME_K23;
                _EREC+=(PME_Q[tmp_index]*PME_CONV[tmp_index]);
            }
        }
    }
    _EREC*=(PME_K123d/(2.0));
    //INFO("[NMC_N_PME] Reciprocal Energy Calculated by NMC_N_PME = "<<_EREC);
}

//-------------------------------------------------------------------
//  Initialize NMC Reduced Particle Mesh Ewald
//-------------------------------------------------------------------
void YZOFrame::NMC_R_PME_init()
{
    // allocate array NMC_PME_Q
    Realloc(NMC_PME_Q,double,(2*PME_K123S));
    
    // scaled coefficients _UR
    Realloc(NMC_UR,Vector3,_NP);
    
    // effective k-space indices
    Realloc(NMC_PME_m1,int,PME_bsp_n);
    Realloc(NMC_PME_m2,int,PME_bsp_n);
    Realloc(NMC_PME_m3,int,PME_bsp_n);
    Realloc(NMC_PME_m1mod,int,PME_bsp_n);
    Realloc(NMC_PME_m2mod,int,PME_bsp_n);
    Realloc(NMC_PME_m3mod,int,PME_bsp_n);
    
    // (u-k),Mn(u-k),Mn-1(u-k),Mn-1(u-k-1) arrays
    Realloc(NMC_PME_x1,double,PME_bsp_n);
    Realloc(NMC_PME_x2,double,PME_bsp_n);
    Realloc(NMC_PME_x3,double,PME_bsp_n);
    Realloc(NMC_PME_MU1,double,PME_bsp_n);
    Realloc(NMC_PME_MU2,double,PME_bsp_n);
    Realloc(NMC_PME_MU3,double,PME_bsp_n);

    // prep the fftw3 plan
    NMC_PME_fft_plan1=fftw_plan_dft_r2c_3d(PME_K1,PME_K2,PME_K3,NMC_PME_Q,PME_IQ,FFTW_ESTIMATE);
}

//-------------------------------------------------------------------
//  NMC Reduced Particle Mesh Ewald
//-------------------------------------------------------------------
void YZOFrame::NMC_R_PME()
{
    int i, j, k, i1, i2, i3, m1, m2, m3;
    int tmp_ind_i, tmp_ind_1, tmp_index, tmp_sp;
    double tmp_double, tmp_charge_a=0.0, tmp_charge_b=0.0;
    
    // copy the PME_Q from last move
    for (i1=0;i1<PME_K1;i1++)
    {
        for (i2=0;i2<PME_K2;i2++)
        {
            for (i3=0;i3<PME_K3;i3++)
            {
                tmp_index=i3+i2*PME_K3+i1*PME_K23;
                NMC_PME_Q[tmp_index]=PME_Q[tmp_index];
            }
        }
    }
    
    // get the scaled and manipulated coordinates UR
    for(k=0;k<numtrialatoms;k++)
    {
        i=trialatomlist[k];
        NMC_UR[i].x=(_SR[i].x+0.5)*PME_K1d;
        NMC_UR[i].y=(_SR[i].y+0.5)*PME_K2d;
        NMC_UR[i].z=(_SR[i].z+0.5)*PME_K3d;
    }
    
    // Calculate NMC_R_PME_Q matrix
    for(k=0;k<numtrialatoms;k++)
    {
        // get the atom index
        i=trialatomlist[k];
        
        // get the data index and atom charge
        tmp_ind_i=PME_bsp_n*i;
        
        // get the species index
        tmp_sp=species[i];
        if(tmp_sp==1){tmp_charge_a=_SPCHARGEMD[1]; tmp_charge_b=_SPCHARGEMD[1];}
        else if(tmp_sp==0){tmp_charge_a=_SPCHARGEMD[0]; tmp_charge_b=_SPCHARGEMD[3];}
        else if(tmp_sp==3){tmp_charge_a=_SPCHARGEMD[3]; tmp_charge_b=_SPCHARGEMD[0];}
        
        //-------------------------------------------------------------------
        // first part : add the contribution from new move to NMC_R_PME_Q
        //-------------------------------------------------------------------

        // get the index
        NMC_PME_m1s=((int)floor(NMC_UR[i].x))-PME_bsp_n+1;
        NMC_PME_m2s=((int)floor(NMC_UR[i].y))-PME_bsp_n+1;
        NMC_PME_m3s=((int)floor(NMC_UR[i].z))-PME_bsp_n+1;
                        
        for (j=0;j<PME_bsp_n;j++) // from 0 to (n-1)
        {
            // goes through effective range of k-space for atoms i
            NMC_PME_m1[j]=NMC_PME_m1s+j; // in x-direction
            NMC_PME_m2[j]=NMC_PME_m2s+j; // in y-direction
            NMC_PME_m3[j]=NMC_PME_m3s+j; // in z-direction
            
            // apply periodic boundary condition
            NMC_PME_m1mod[j]=PME_int_mod(NMC_PME_m1[j],PME_K1);
            NMC_PME_m2mod[j]=PME_int_mod(NMC_PME_m2[j],PME_K2);
            NMC_PME_m3mod[j]=PME_int_mod(NMC_PME_m3[j],PME_K3);
            
            // calculate (u-k)
            NMC_PME_x1[j]=NMC_UR[i].x-((double)NMC_PME_m1[j]);
            NMC_PME_x2[j]=NMC_UR[i].y-((double)NMC_PME_m2[j]);
            NMC_PME_x3[j]=NMC_UR[i].z-((double)NMC_PME_m3[j]);
            
            // calculate Mn(u) coefficients
            NMC_PME_MU1[j]=PME_cal_bsp_Mu(PME_bsp_n,NMC_PME_x1[j]);
            NMC_PME_MU2[j]=PME_cal_bsp_Mu(PME_bsp_n,NMC_PME_x2[j]);
            NMC_PME_MU3[j]=PME_cal_bsp_Mu(PME_bsp_n,NMC_PME_x3[j]);
        }
        
            // scan the atomwise small cubic which has nonzero entries
        for (i1=0;i1<PME_bsp_n;i1++) // from 0 to (n-1)
        {
            for (i2=0;i2<PME_bsp_n;i2++) // from 0 to (n-1)
            {
                for (i3=0;i3<PME_bsp_n;i3++) // from 0 to (n-1)
                {
                    tmp_index=NMC_PME_m3mod[i3]+NMC_PME_m2mod[i2]*PME_K3+NMC_PME_m1mod[i1]*PME_K23;
                    NMC_PME_Q[tmp_index]+=tmp_charge_a*NMC_PME_MU1[i1]*NMC_PME_MU2[i2]*NMC_PME_MU3[i3];
                }
            }
        }
        
        //-------------------------------------------------------------------
        // second part : eliminate the contribution from old data
        //-------------------------------------------------------------------
        
        // call the needed data from last move
        for (j=0;j<PME_bsp_n;j++) // from 0 to (n-1)
        {
            // calculate temporary index
            tmp_index =j+tmp_ind_i;
            
            // call modified indices from last move
            NMC_PME_m1mod[j]=PME_m1mod[tmp_index]; 
            NMC_PME_m2mod[j]=PME_m2mod[tmp_index];
            NMC_PME_m3mod[j]=PME_m3mod[tmp_index];
            
            // call Mn(u) coefficients from last move
            NMC_PME_MU1[j]=PME_MU1[tmp_index];
            NMC_PME_MU2[j]=PME_MU2[tmp_index];
            NMC_PME_MU3[j]=PME_MU3[tmp_index];
        }
        
        // scan the atomwise small cubic which has nonzero entries
        for (i1=0;i1<PME_bsp_n;i1++) // from 0 to (n-1)
        {
            for (i2=0;i2<PME_bsp_n;i2++) // from 0 to (n-1)
            {
                for (i3=0;i3<PME_bsp_n;i3++) // from 0 to (n-1)
                {
                    tmp_index=NMC_PME_m3mod[i3]+NMC_PME_m2mod[i2]*PME_K3+NMC_PME_m1mod[i1]*PME_K23;
                    NMC_PME_Q[tmp_index]-=tmp_charge_b*NMC_PME_MU1[i1]*NMC_PME_MU2[i2]*NMC_PME_MU3[i3];
                }
            }
        }
    }
        
    // out-of-place backward fft of matrix PME_Q (backward fft in the paper => forward fft for the fftw)
    fftw_execute(NMC_PME_fft_plan1); // real to complex
    
    // matrix multiplication of PME_BC and PME_IQ -> (IQ*BC)
    for (m1=0;m1<PME_K1;m1++) // from 0 to (PME_K1-1)
    {
        for (m2=0;m2<PME_K2;m2++) // from 0 to (PME_K2-1)
        {
            for (m3=0;m3<PME_K3S;m3++) // from 0 to (PME_K3S-1)
            {
                tmp_ind_1=m3+m2*PME_K3S+m1*PME_K23S;
                tmp_index=m3+m2*PME_K3+m1*PME_K23;
                tmp_double=PME_BC[tmp_index]/PME_K123d;                
                PME_IQ[tmp_ind_1][0]*=tmp_double;
                PME_IQ[tmp_ind_1][1]*=tmp_double;
            }
        }
    }

    // in-place forward fft of matrix PME_CONV (forward fft in the paper => backward fft for the fftw)
    fftw_execute(PME_fft_plan2); // complex to real

    // matrix multiplication of PME_Q and PME_CONV and calculate the reciprocal energy simultaneously
    for (m1=0;m1<PME_K1;m1++) // from 0 to (PME_K1-1)
    {
        for (m2=0;m2<PME_K2;m2++)  // from 0 to (PME_K2-1)
        {
            for (m3=0;m3<PME_K3;m3++) // from 0 to (PME_K3-1)
            {
                tmp_index=m3+m2*PME_K3+m1*PME_K23;
                _EREC+=(NMC_PME_Q[tmp_index]*PME_CONV[tmp_index]);
            }
        }
    }
    _EREC*=(PME_K123d/(2.0));
    //INFO("[NMC_R_PME] Reciprocal Energy Calculated by NMC_R_PME = "<<_EREC);
}

//-------------------------------------------------------------------
//  Test Particle Mesh Ewald
//-------------------------------------------------------------------
void YZOFrame::testEwald()
{
    double tmp, E_tmp1, E_tmp2, E_tmp3, E_tmp4, C_tmp;
    Vector3 F_tmp1, F_tmp2, F_tmp3, F_tmp4;
    int i, j;
    FILE *fp1;
    FILE *fp2;
    FILE *fp3;
    FILE *fp4;
    fp1=fopen("CE_Frec.dat","w");
    fp2=fopen("CE_Ftot.dat","w");
    fp3=fopen("PME_Frec.dat","w");
    fp4=fopen("PME_Ftot.dat","w");
    
    E_tmp1=0.0; E_tmp2=0.0; E_tmp3=0.0; E_tmp4=0.0;
    F_tmp1.clear(); F_tmp2.clear(); F_tmp3.clear(); F_tmp4.clear();

    // print out the process
    if((testPME==1)&&(testCE==1))
    {
        INFO("==============================================================================");
        INFO("        Classical Ewald [CE] vs Particle Mesh Ewald [PME] Benchmark");
        INFO("==============================================================================");
    }
    else if((testCE==1)&&(testPME==0))
    {
        INFO("==============================================================================");
        INFO("                         Testing Classical Ewald [CE]");
        INFO("==============================================================================");
    }
    else
    {
        INFO("==============================================================================");
        INFO("                       Testing Particle Mesh Ewald [PME]");
        INFO("==============================================================================");        
    }
    
    // calculation with CE method
    if(testCE==1)
    {
        // potential reset
        useCE=1;
        usePME=0;
        potsetup=0;
        
        tCEREAL=0.0;
        tCEREC=0.0;
        for(i=0;i<=testNUM;i++)
        {
            potential();
            if(i!=0)
            {
                iCEREAL=(-(double(sCEREAL))+(double(sCEREC)))/CLOCKS_PER_SEC;
                iCEREC=(-(double(sCEREC))+(double(eCEREC)))/CLOCKS_PER_SEC;
                tCEREAL+=iCEREAL;
                tCEREC+=iCEREC;
                INFO("[CE] "<<i<<"th => Real Space Calculation : "
                     <<iCEREAL<<" sec / Reciprocal Space Calculation : "<<iCEREC<<" sec");
            }
        }
        E_tmp1=_EREC; // reciprocal space
        E_tmp2=_EREAL_B; // real space [BMB]
        E_tmp3=_EREAL_C; // real space [COULOMB]
        E_tmp4=_EPOT; // total potential
        F_tmp1=_FREC[0]; // reciprocal space force
        F_tmp2=_FREAL_B[0]; // real space force [BMB]
        F_tmp3=_FREAL_C[0]; // real space [COULOMB]
        F_tmp4=_F[0]; // total force
        for(j=0;j<_NP;j++)
        {
            _FREC0[j].clear();
            _FREC0[j]=_FREC[j];
            fprintf(fp1,"%20.15f  %20.15f  %20.15f\n",_FREC[j].x,_FREC[j].y,_FREC[j].z);
            fprintf(fp2,"%20.15f  %20.15f  %20.15f\n",_F[j].x,_F[j].y,_F[j].z);
        }
    }

    
    if(testPME==1)
    {
        // switch to PME mode
        INFO("==============================================================================");
        CE_clear();

        // potential reset
        useCE=0;
        usePME=1;
        potsetup=0;
        
        // repeat the calculation with PME method
        tPMEREAL=0.0;
        tPMEREC=0.0;
        for(i=0;i<=testNUM;i++)
        {
            potential();
            if(i!=0)
            {
                iPMEREAL=(-(double(sPMEREAL))+(double(sPMEREC)))/CLOCKS_PER_SEC;
                iPMEREC=(-(double(sPMEREC))+(double(ePMEREC)))/CLOCKS_PER_SEC;
                tPMEREAL+=iPMEREAL;
                tPMEREC+=iPMEREC;
                INFO("[PME] "<<i<<"th => Real Space Calculation : "<<iPMEREAL
                     <<" sec / Reciprocal Space Calculation : "<<iPMEREC<<" sec");
            }
        }
        for(j=0;j<_NP;j++)
        {
            fprintf(fp3,"%20.15f  %20.15f  %20.15f\n",_FREC[j].x,_FREC[j].y,_FREC[j].z);
            fprintf(fp4,"%20.15f  %20.15f  %20.15f\n",_F[j].x,_F[j].y,_F[j].z);
        }        
    }

    if(testCE==1)
    {
        INFO("==============================================================================");
        INFO("[CE] Real Space Energy [BMB]               = "<<E_tmp2<<" eV");
        INFO("[CE] Real Space Energy [COULOMB]           = "<<E_tmp3<<" eV");
        INFO("[CE] Real Space Energy [TOTAL]             = "<<(E_tmp2+E_tmp3)<<" eV");
        INFO("------------------------------------------------------------------------------");
        INFO("[CE] Reciprocal Space Energy               = "<<E_tmp1<<" eV");
        INFO("[CE] Reciprocal Space Self-Energy          = "<<_EREC_self<<" eV");
        INFO("[CE] Reciprocal Space Energy [TOTAL]       = "<<(E_tmp1+_EREC_self)<<" eV");
        INFO("------------------------------------------------------------------------------");
        INFO("[CE] Total Potential Energy [BMB]          = "<<E_tmp2<<" eV");
        INFO("[CE] Total Potential Energy [COULOMB]      = "<<(E_tmp1+E_tmp3+_EREC_self)<<" eV");
        INFO("------------------------------------------------------------------------------");
        INFO("[CE] Total Potential Energy [TOTAL]        = "<<E_tmp4<<" eV");
        //INFO("[CE] Accumulated Square Charge Sum         = "<<qSQRsum);
        INFO("==============================================================================");
        INFO("[CE] Real Space Force on Atom 1 [BMB]      = "<<F_tmp2);
        INFO("[CE] Real Space Force on Atom 1 [COULOMB]  = "<<F_tmp3);
        INFO("[CE] Real Space Force on Atom 1 [TOTAL]    = "<<(F_tmp2+F_tmp3));
        INFO("------------------------------------------------------------------------------");
        INFO("[CE] Reciprocal Space Force on Atom 1      = "<<F_tmp1);
        INFO("------------------------------------------------------------------------------");
        INFO("[CE] Total Force on Atom 1 [BMB]           = "<<F_tmp2);
        INFO("[CE] Total Force on Atom 1 [COULOMB]       = "<<(F_tmp1+F_tmp3));        
        INFO("------------------------------------------------------------------------------");
        INFO("[CE] Total Force on Atom 1 [TOTAL]         = "<<F_tmp4);
        INFO("==============================================================================");
        INFO("[CE] Average Real Space Time : "<<(tCEREAL/((double)testNUM))
             <<" Average Reciprocal Space Time : "<<(tCEREC/((double)testNUM)));
        INFO("[CE] Total Time Executing CE for "<<testNUM<<" Times = "<<(tCEREAL+tCEREC)<<" sec");
    }
    if(testPME==1)
    {
        INFO("==============================================================================");
        INFO("[PME] Real Space Energy [BMB]               = "<<_EREAL_B<<" eV");
        INFO("[PME] Real Space Energy [COULOMB]           = "<<_EREAL_C<<" eV");
        INFO("[PME] Real Space Energy [TOTAL]             = "<<(_EREAL_B+_EREAL_C)<<" eV");
        INFO("------------------------------------------------------------------------------");
        INFO("[PME] Reciprocal Space Energy               = "<<_EREC<<" eV");
        INFO("[PME] Reciprocal Space Self-Energy          = "<<_EREC_self<<" eV");
        INFO("[PME] Reciprocal Space Energy [TOTAL]       = "<<(_EREC+_EREC_self)<<" eV");
        INFO("------------------------------------------------------------------------------");
        INFO("[PME] Total Potential Energy [BMB]          = "<<_EREAL_B<<" eV");
        INFO("[PME] Total Potential Energy [COULOMB]      = "<<(_EREAL_C+_EREC+_EREC_self)<<" eV");
        INFO("------------------------------------------------------------------------------");
        INFO("[PME] Total Potential Energy by PME         = "<<_EPOT<<" eV");
        //INFO("[PME] Accumulated Square Charge Sum         = "<<qSQRsum);
        INFO("==============================================================================");
        INFO("[PME] Real Space Force on Atom 1 [BMB]      = "<<_FREAL_B[0]);
        INFO("[PME] Real Space Force on Atom 1 [COULOMB]  = "<<_FREAL_C[0]);
        INFO("[PME] Real Space Force on Atom 1 [TOTAL]    = "<<(_FREAL_B[0]+_FREAL_C[0]));
        INFO("------------------------------------------------------------------------------");
        INFO("[PME] Reciprocal Space Force on Atom 1      = "<<_FREC[0]);
        INFO("------------------------------------------------------------------------------");
        INFO("[PME] Total Force on Atom 1 [BMB]           = "<<_FREAL_B[0]);
        INFO("[PME] Total Force on Atom 1 [COULOMB]       = "<<(_FREAL_C[0]+_FREC[0]));
        INFO("------------------------------------------------------------------------------");
        INFO("[PME] Total Force on Atom 1                 = "<<_F[0]);
        INFO("==============================================================================");
        INFO("[PME] Average Real Space Time : "<<(tPMEREAL/((double)testNUM))
             <<" Average Reciprocal Space Time : "<<(tPMEREC/((double)testNUM)));
        INFO("[PME] Total Time Executing PME for "<<testNUM<<" Times = "<<(tPMEREAL+tPMEREC)<<" sec");
    }
    INFO("==============================================================================");
    if((testCE==1)&&(testPME==1))
    {
        for(j=0;j<_NP;j++)
        {
            tmp=(SQR(_FREC0[j].x-_FREC[j].x)+SQR(_FREC0[j].x-_FREC[j].x)+SQR(_FREC0[j].x-_FREC[j].x));
            if(tmp>0.0001)
            {
                WARNING("Error in Force Calculation -> Error in "<<j<<"th Atom Force = "<<tmp);
            }
        }
    }
        
    C_tmp=0.0;
    for(i=0;i<_NP;i++)
    {
        if(species[i]==0){}
        else if(species[i]==1){}
        else if(species[i]==3){}
        else { INFO("Problem with Atom Charges");}
        C_tmp+=_SPCHARGEMD[species[i]];        
    }
    if(C_tmp!=0.0){INFO("Error with Charge Balance => Net Charge = "<<C_tmp);}
    fclose(fp1);
    fclose(fp2);    
}

//###################################################################
//  Main Program Part
//###################################################################
//-------------------------------------------------------------------
// Main Program Begins
//-------------------------------------------------------------------
#ifdef _TEST
class YZOFrame yzo;

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

#endif//_TEST
