//################################################################### 
//-------------------------------------------------------------------
//  yzo.h
//-------------------------------------------------------------------
//
//  by Hark Lee hblee@stanford.edu
//  Last Modified : Thu Mar  8 14:58:45 2007
//
//-------------------------------------------------------------------
// FUNCTION  :  YSZ MD simulation using (Coulomb+BMB) potential
//-------------------------------------------------------------------
//###################################################################

#ifndef _YZO_H
#define _YZO_H
#include "md.h"
#include <fftw3.h>
#include <ctime>

//###################################################################
// Constants, Operator, Classes
//###################################################################
//------------------------------------------------------------------- 
// Physical Constants : All physical constants starts with P_
//------------------------------------------------------------------- 
#define P_C 2.99792458e8  // (m/s) the speed of light (EXACT)
#define P_HBAR 1.0545919e-34 // (kg*m^2/s) hbar plank constant
#define P_KB 1.380662e-23  // (kg*m^2/s^2/K) Boltzmann constant
#define P_E 1.6021892e-19  // (C) electron charge
#define P_EV 1.60217653e-19 // (J/eV) electron volts (From wikipedia)
#define P_ME 9.109558e-31  // (kg) electron mass
#define P_G 6.6732e-11      // (m^3/s^2/kg) Gravitational constant
#define P_NA 6.022169e23   // (1/mol) Avogadro constant
#define P_AV 6.0221415e23 // (1/mol) Avogadro's number (From wikipedia)
#define P_U 1.660531e-27    // (kg) atomic mass unit
#define P_MU0 (4.0*M_PI*1.0e-7) // (kg*m/C^2) permeability of vacuum
#define P_EPSILON0 (1.0/P_MU0/P_C/P_C) // (C^2*s^2/kg/m^3) permittivity of vacuum
//#define P_BMB 1.0*1000.0/(P_E*P_NA) // (eV) conversion factor for BMB parameters
#define P_BMB 1.0*1000.0/(P_EV*P_AV) // (eV) conversion factor for BMB parameters (From wikipedia)
#define P_BMB_M 1.0
#define P_CLMB_E 1.0 // for the correction of Coulomb potential (dielectric function constant) 
#define P_CLMB (P_E*P_E/P_E*1.0e10)/(4.0*M_PI*P_EPSILON0*P_CLMB_E) // (eV*A) conversion factor for the Coulomb force
#define P_SQRT_CLMB sqrt(P_CLMB) //3.79470135954879
#define M_SQRT_PI sqrt(M_PI) //1.77245385090552

//-------------------------------------------------------------------
// Define Operators
//-------------------------------------------------------------------
#define SQR(A) ((A)*(A))
#define CUBE(A) ((A)*(A)*(A))
#define POW6(A) SQR(CUBE(A))
#define POW7(A) (POW6(A)*A)
#define POW12(A) SQR(POW6(A))
#define PME_int_mod(x,y) (((x%y)+y)%y)

//-------------------------------------------------------------------
// Interatomic Parameters
//-------------------------------------------------------------------
// BMB Potential Parameters
//-------------------------------------------------------------------
// [ Zr-Zr | Zr-O | O-O | Zr-Y | O-Y | None | Y-Y ] [ A | B | C ]
//-------------------------------------------------------------------
const double BMB_POT[7][3] = {{    0.0*P_BMB,        0.0*P_BMB,        1.0 },  // Zr-Zr
			      {    0.0*P_BMB,    95120.0*P_BMB, 2.65957450 },  // Zr-O
			      { 2691.0*P_BMB,  2196000.0*P_BMB, 6.71140940 },  // O-O
			      {    0.0*P_BMB,        0.0*P_BMB,        1.0 },  // Zr-Y
			      {    0.0*P_BMB,   129780.0*P_BMB, 2.86450870 },  // O-Y
			      {    0.0*P_BMB,        0.0*P_BMB,        1.0 },  // dummy variables
			      {    0.0*P_BMB,        0.0*P_BMB,        1.0 }}; // Y-Y

/*// Truncated COULOMB Potential Parameters
  const double CLMB_A[7] = { (+4.0)*(+4.0)*P_CLMB,   // Zr-Zr
  (+4.0)*(-2.0)*P_CLMB,   // Zr-O
  (-2.0)*(-2.0)*P_CLMB,   // O-O
  (+4.0)*(+3.0)*P_CLMB,   // Zr-Y
  (-2.0)*(+3.0)*P_CLMB,   // O-Y
  (+0.0)*(+0.0)*P_CLMB,   // just dummy variables
  (+3.0)*(+3.0)*P_CLMB }; // Y-Y*/

//-------------------------------------------------------------------
// Define Complex Array Type (Used in PME)
//-------------------------------------------------------------------
typedef double ComplexType[2];

//------------------------------------------------------------------- 
// Define EComplex Class (will be used in Classic Ewald summation)
//-------------------------------------------------------------------
struct EComplex
{
    double Re, Im; //Real and imaginary part (only data member)
public:
    double Norm() { return sqrt(Re*Re+Im*Im); }
    double Norm2() { return Re*Re+Im*Im; }
    
    static void Mul(const EComplex &a, const EComplex &b, EComplex &c)//c=a*b
    {
        double tmp=a.Re*b.Re-a.Im*b.Im;
        c.Im=a.Re*b.Im+a.Im*b.Re; 
        c.Re=tmp;
    }
    static void MulConjugate(const EComplex &a, const EComplex &b, EComplex &c)
    {
        c.Re=a.Re*b.Re+a.Im*b.Im; 
        c.Im=-a.Re*b.Im+a.Im*b.Re;
    }
    EComplex &operator *=(double d)
    {
        Re*=d;
        Im*=d;
        return *this;
    }
    EComplex &operator +=(EComplex &c)
    {
        Re+=c.Re;
        Im+=c.Im;
        return *this;
    }
    EComplex &operator -=(EComplex &c)
    {
        Re-=c.Re;
        Im-=c.Im;
        return *this;
    }
    void ExpI(double theta)
    { 
        Re=cos(theta);
        Im=sin(theta);
    }
    EComplex &operator =(double d) 
    { 
        Re=d;
        Im=0.0;
        return *this;
    }
    friend LOStream &operator <<(LOStream &os, EComplex &c)
    {
        return os << "["<<c.Re<<"(+)"<<c.Im<<"(I)]";
    }
};


//###################################################################
// Main Class
//###################################################################
//-------------------------------------------------------------------
// Define YZOFrame Class (Main frame)
//-------------------------------------------------------------------
class YZOFrame: public MDFrame
{    
public:
    //################################################################### 
    // Constructor and Extinguisher
    //###################################################################
    YZOFrame():MDFrame()
    {
        // general parameters
        potsetup=0; yttriaconc=0.0;
        PP_RC=9.0; _SKIN=1.0;
	
	// initialize gradient doping
    	GC_numlayers=5;
    	GC_topconc=0.08; GC_botconc=0.08;
    	GC_ytop=0; GC_ybot=0; GC_yttriaconc=0; GC_F_V=0; 
	GC_N_cat=0; GC_N_an=0; GC_Max_Y=0; GC_Max_V=0;
                
        // initializing SEMC
        MCjumps=100000;
        MDstep1=10; MDstep2=200;
        MDstep1freq=20; MDstep2freq=50; MCcheckfreq=5;
        
        // initializing NMC
        maxNNO=300;
        anfluc=0.0;
        
	// initializing CGNMC arrays
	CGNMC_YNNO_I=0; CGNMC_YNNO_R1=0; CGNMC_YNNO_R2=0; 
	CGNMC_ZrNNO_I=0; CGNMC_ZrNNO_R1=0; CGNMC_ZrNNO_R2=0;
	_SR_CGNMC=0;

        // initializing general Ewald parameters
        detAlpha=0; Ewaldprecision=4.2; EwaldRFratio=3.6;
        
        // initializing Conventional Ewald summation
        sckk=0; scx=0; KV=0; MKV=0;
        
        // initializing reduced Conventional Ewald summation
        MCsckk=0; MCsincosk=0; MCscx=0;
        
        // initializing PME parameters
        useCE=1; usePME=0;
        PME_K1=64; PME_K2=64; PME_K3=64; PME_bsp_n=8;
        testCE=1; testPME=1; testNUM=1;

        // initializing SAFM
        probesp=1;
    }
    virtual ~YZOFrame()
    {
        // general variables
        free(_FREC); free(_FREAL_B); free(_FREAL_C), free(_FREC0);

	// free variables in gradient doping
    	free(GC_ytop); free(GC_ybot); free(GC_yttriaconc); free(GC_F_V); 
	free(GC_N_cat); free(GC_N_an); free(GC_Max_Y); free(GC_Max_V);

        // free variables used in the classical Ewald summation
        if(useCE){CE_clear();}

        // free variables used in the Particle Mesh Ewald summation
        if(usePME){PME_clear();}
        
        // Monte Carlo general data
        free(yttriumdata); free(yttriumlist);
        free(zirconiumdata); free(zirconiumlist); free(zirconiumdist);
        
        // SEMC / NMC arrays
        free(BYNNO); free(BZrNNO);
        free(BvecYNNO); free(BvecZrNNO);

	// CGNMC arrays
	free(CGNMC_YNNO_I); free(CGNMC_YNNO_R1); free(CGNMC_YNNO_R2);
	free(CGNMC_ZrNNO_I); free(CGNMC_ZrNNO_R1); free(CGNMC_ZrNNO_R2);

        // reduced potential
        free(trialatomlist); free(trialatomindex); free(realpairE1); free(realpairE2);
        
        // SAFM parameters
        free(topTPs); free(toppot); free(botTPs); free(botpot); free(SAFMsincosk);
    }

    //###################################################################
    //   Variables
    //###################################################################  
    //-------------------------------------------------------------------
    // General Variables
    //-------------------------------------------------------------------
    Matrix33 _Hinv; // inverse matrix of _H
    double _EREAL, _EREAL_B, _EREAL_C; // real space energy, BMB, Coulomb real part
    double yttriaconc; // yttria concentration
    double PP_RC, PP_RC6, PP_RC7; // short range potential cut-off
    double _H_L1, _H_L2, _H_L3, _H_L1R, _H_L2R, _H_L3R; // system size
    int potsetup; // potential setup index ( 1:done / 2:undone )
    int drandseed;

    //-------------------------------------------------------------------
    // Gradient Doping
    //-------------------------------------------------------------------
    int GC_numlayers;
    double GC_topconc, GC_botconc;
    double GC_dy, GC_dyttriaconc;
    double *GC_ytop, *GC_ybot;
    double *GC_yttriaconc;
    double *GC_F_V;
    int *GC_N_cat, *GC_N_an;
    int *GC_Max_Y, *GC_Max_V;
    
    //-------------------------------------------------------------------
    // BMB potential parameters
    //-------------------------------------------------------------------
    double BMB_TMP_1[7]; // A * Rc^-6
    double BMB_TMP_2[7]; // C * Rc
    double BMB_TMP_3[7]; // B * exp( -C * Rc )
    double BMB_D[7]; // D parameters for BMB
    double BMB_E[7]; // E parameters for BMB
    double CLMB_B[7]; // B parameters for Coulomb
    double CLMB_C[7]; // C parameters for Coulomb
    
    //-------------------------------------------------------------------
    // BMB potential parameters
    //-------------------------------------------------------------------
    double *BMB_test_R;
    double *BMB_test_E;
    double *BMB_test_F;
    double *CS_test_E;
    double *CS_test_F;
    
    //-------------------------------------------------------------------
    // NMC / CGNMC parameters
    //-------------------------------------------------------------------
    // General MC parameters
    int MCjumps; // total required number of jumps
    int MCcounts; // Monte Carlo jump count
    int MDcounts; // MD step count
    int totMCtrials; // total number of MC trials
    int totMCacc; // Monte Carlo total acceptance number
    int MCacc; // Monte Carlo acceptance Index

    int MDstep1; // total number of MD steps 1
    int MDstep2; // total number of MD steps 2
    int MCcheckfreq; // check frequency of MC
    int MDstep1freq; // execution frequency of MD steps 1
    int MDstep2freq; // execution frequency of MD steps 2

    double catmigdist; // maximum cation migration distance
    
    // General MC arrays
    int nyttriums; // number of yttriums
    int *yttriumdata; // yttrium database
    int *yttriumlist; // yttrium database
    int nzirconiums; // number of yttriums
    int *zirconiumdata; // zirconium database
    int *zirconiumlist; // zirconium database
    double *zirconiumdist; // zirconium database
    
    // NMC paramters
    int maxNNO; // maximum number of NN oxygens
    double anfluc; // 1st NN Cation-Anion distance fluctuation
    int NNO_Rr_type; // type of index
    double NNO_Rc; // cut-off radius
    double NNO_R, NNO_r; // R and r values for R-r conversion
    double nYZr_Rr, nZrY_Rr; // number of bins in R-r curve
    double iRP_REAL, iRP_REC;
    clock_t sNMC, eNMC, sCGNMC, eCGNMC; // computation time 
    clock_t sRP_REAL, eRP_REAL, sRP_REC, eRP_REC;

    // NMC arrays
    double YZr_Rr[60][2];
    int BnYNNO; // number of NN oxygen atoms
    int BnZrNNO; // number of NN oxygen atoms
    int *BYNNO; // index database of NN oxygen atoms
    int *BZrNNO; // index database of NN oxygen atoms
    Vector3 *BvecYNNO; // backup original data
    Vector3 *BvecZrNNO; // backup original data    

    // CGNMC parameters
    int numofYNNOs, numofZrNNOs;

    // CGNMC arrays
    int *CGNMC_YNNO_I;
    double *CGNMC_YNNO_R1;
    double *CGNMC_YNNO_R2;
    int *CGNMC_ZrNNO_I; 
    double *CGNMC_ZrNNO_R1;
    double *CGNMC_ZrNNO_R2;
    Vector3 *_SR_CGNMC;
    
    //-------------------------------------------------------------------
    // Reduced potential parameters
    //-------------------------------------------------------------------
    int numtrialatoms; // number of trial atoms (usually less than 100)
    int *trialatomindex; // trial index ( 1 : trial / 0 : normal )

    int *trialatomlist; // list of trial atoms
    int maxnumofpairs; // maximum number of pairs
    int numofpairs; // number of pairs
    int pairindex; // pair count

    int checkneighborlist; // check the neighbor list index ( 1 or 0 )

    double EPOT_1, EPOT_2, NMC_EREAL1, NMC_EREAL2, NMC_EREC, dEREAL, dEREC, dEPOT;
    double *realpairE1, *realpairE2; // pair energy real part
    
    //-------------------------------------------------------------------
    // Semi-surface parameters
    //-------------------------------------------------------------------
    int surfacex; // x switch ( 1:create surface normal to x-axis / 0:no surface )
    int surfacey; // y switch ( 1:create surface normal to y-axis / 0:no surface )
    int surfacez; // z switch ( 1:create surface normal to z-axis / 0:no surface )
    double surfacexscale; // x length scale
    double surfaceyscale; // y length scale
    double surfacezscale; // z length scale
    
    //-------------------------------------------------------------------
    // SAFM parameters (Simulated Atomic Force Measurement)
    //-------------------------------------------------------------------
    int switchSAFM; // index of the axis to which test surface is orthogonal ( x:0 / y:1 / z:2 )
    int probesp; // probe ion species
    int topNP;
    int botNP;
    int totTPs; // number of test points
    int d1TPs; // number of test points in direction 1
    int d2TPs; // number of test points in direction 2
    double d3sep; // test surface seperation in direction 3
    
    int *topatoms;
    int *botatoms;    
    Vector3 *topTPs; // surface height in direction 3
    double *toppot; // surface height in direction 3
    Vector3 *botTPs; // surface height in direction 3
    double *botpot; // surface height in direction 3
    EComplex *SAFMsincosk; // Redueced Ewald parameters
    
    //-------------------------------------------------------------------
    // Ewald general parameters
    //-------------------------------------------------------------------
    Vector3 *_FREC, *_FREAL_B, *_FREAL_C, *_FREC0;
    int useCE, usePME; // CE / PME switch ( 1:on / 2:off )
    int detAlpha; // Way to determine gaussian shape factor ( 1:CE optimum / 2:controlled )
    double Ewaldprecision, EwaldRFratio; // Ewald precision and RF ratio
    double Alpha, qSQRsum, cell_V, _EREC, _EREC_self;

    //-------------------------------------------------------------------
    // Ewald general parameters
    //-------------------------------------------------------------------
    int MKV, nKV; //max and actual number of K-vectors in list
    int Mka, Mkb, Mkc; //Largest repeat reciprocal distance in each direction
    double Kc; // cut-off radius in reciprocal space
    
    struct K_rec { double kx, ky, kz, k2; int a,b,c; } *KV; //Actual K-vectors
    EComplex *sc_axby, *sckk; // exp(iK(rx+ry)), qj*exp(iK(rx+ry+rz))
    EComplex **scx, **scy, **scz; // sin-cos table
    
    clock_t sCEREAL, sCEREC, eCEREC;
    double iCEREAL, iCEREC, tCEREAL, tCEREC;
    
    //-------------------------------------------------------------------
    // Reduced Ewald parameters
    //-------------------------------------------------------------------  
    EComplex *MCsc_axby, *MCsckk, *MCsincosk;
    EComplex **MCscx, **MCscy, **MCscz;

    //-------------------------------------------------------------------
    // PME parameters
    //-------------------------------------------------------------------
    int PME_bsp_n; // order of B-spline interpolation

    int PME_K1, PME_K2, PME_K3, PME_K3S; // number of mesh points
    int PME_K23, PME_K123, PME_K23S, PME_K123S;

    double PME_K1d, PME_K2d, PME_K3d, PME_K123d; // double type
    double PME_C1[4], PME_C2[4], PME_C3[4]; // PME force parameters

    Vector3 *_UR; // Scaled atom positions
    
    double *PME_BC; // B*C array
    double *PME_Q; // Q array
    double *PME_CONV; // convolution of BC and IQ
    ComplexType *PME_IQ; // inverse Q array

    ComplexType R_bsp_Bm; // entries of B
    double *PME_bsp_Mk; // to calculate B entries
    double *PME_bsp_fac; // factorial array

    int *PME_m1s, *PME_m2s, *PME_m3s; // k-space start indices
    int *PME_m1, *PME_m2, *PME_m3; // k-space indices
    int *PME_m1mod, *PME_m2mod, *PME_m3mod; // actual indices after PBC

    double *PME_x1, *PME_x2, *PME_x3; // (u-k) after PBC
    double *PME_MU1, *PME_MU2, *PME_MU3; // Mn(u)
    double *PME_d1MU1, *PME_d1MU2, *PME_d1MU3; // Mn-1(u)    
    double *PME_d2MU1, *PME_d2MU2, *PME_d2MU3; // Mn-1(u-1)

    fftw_plan PME_fft_plan1, PME_fft_plan2; // fftw plans

    //-------------------------------------------------------------------
    // Test Ewald parameters
    //-------------------------------------------------------------------
    int testNUM; // total test number of testEwald
    int testCE, testPME; // test switch ( 1:on / 2:off )

    clock_t sPMEREAL, sPMEREC, ePMEREC; // computation time 
    double iPMEREAL, iPMEREC, tPMEREAL, tPMEREC; // converted to secs
    
    //-------------------------------------------------------------------
    // NMC reduced PME parameters
    //-------------------------------------------------------------------
    Vector3 *NMC_UR;
    double *NMC_PME_Q;

    int NMC_PME_m1s, NMC_PME_m2s, NMC_PME_m3s;
    int *NMC_PME_m1, *NMC_PME_m2, *NMC_PME_m3;
    int *NMC_PME_m1mod, *NMC_PME_m2mod, *NMC_PME_m3mod;
    double *NMC_PME_x1, *NMC_PME_x2, *NMC_PME_x3;
    double *NMC_PME_MU1, *NMC_PME_MU2, *NMC_PME_MU3;

    fftw_plan NMC_PME_fft_plan1;

    //###################################################################
    //   Functions
    //###################################################################
    //-------------------------------------------------------------------    
    // Default virtual fuctions
    //-------------------------------------------------------------------
    virtual void initvars();
    virtual int exec(char *);
    virtual void potential();
    virtual void initparser();
    
    //-------------------------------------------------------------------
    // YSZ Plot
    //-------------------------------------------------------------------
    void yszplot();
    
    //-------------------------------------------------------------------
    // Calculate lattice constant
    //-------------------------------------------------------------------
    void yszlatticeconst();    

    //-------------------------------------------------------------------
    // System charge check functions
    //-------------------------------------------------------------------
    void zirconianeutral();
    void yszneutral();
    
    //-------------------------------------------------------------------
    // Doping functions
    //-------------------------------------------------------------------
    void dopeyttriaSC(); // perfect lattice system
    void dopeyttriaGB(); // grain boundary system
    void dopeyttriaGC(); // graded concentration doping
    
    //-------------------------------------------------------------------
    // Potential initialization
    //-------------------------------------------------------------------
    void preparepotential(); 
    void updateRLIST();
    
    //-------------------------------------------------------------------
    // Potential functions
    //-------------------------------------------------------------------
    void y_zr_o();
    void testBMB();
    
    //-------------------------------------------------------------------
    // Monte Carlo / Molecular Dynamics coupling functions 
    //-------------------------------------------------------------------
    void SEMC(); // simple exchange Monte Carlo molecular dynamics coupling
    void NMC(); // NNO-included new Monte Carlo molecular dynamics coupling
    void NMC_init(); // NNO-included new Monte Carlo molecular dynamics coupling
    void NMC_Rr(); // NNO-included new Monte Carlo molecular dynamics coupling
    void CGNMC();
    void MDRELAX(int); // MD relaxation function
    
    //-------------------------------------------------------------------
    // MC reduced potential calculation
    //-------------------------------------------------------------------
    void NMC_preparepotential();
    void NMC_N1_potential();
    void NMC_R1_potential();
    void NMC_R1_potential_A();
    void NMC_R1_potential_B();
    void NMC_N2_potential();
    void NMC_R2_potential();
    void NMC_R2_potential_A();
    void NMC_R2_potential_B(); 
    void NMC_N3_potential();
    void NMC_R3_potential();
    void NMC_R3_potential_A();
    void NMC_R3_potential_B(); 
    void NMC_N_CE();
    void NMC_R_CE();
    void NMC_R_fillSinCosTables();
    
    //-------------------------------------------------------------------
    // Semi-surface function
    //-------------------------------------------------------------------
    void semisurface();
    
    //-------------------------------------------------------------------
    // SAFM functions
    //-------------------------------------------------------------------
    void SAFM();
    
    //-------------------------------------------------------------------
    // Ewald Summation
    //-------------------------------------------------------------------
    void fillSinCosTables();
    void enumKV();
    void CE_init();
    void CE();
    void CE_clear();
    
    //-------------------------------------------------------------------
    // PME functions
    //-------------------------------------------------------------------   
    void PME_cal_bsp_fac();
    void PME_cal_bsp_Mk();
    double PME_cal_bsp_Mu(int n, double u);
    void PME_cal_bsp_Bm(int mi, int Ki);
    double PME_cal_bsp_Cm(int m1, int m2, int m3);
    void PME_init();
    void PME();
    void PME_clear();
    void testEwald();
    //int PME_int_mod(int x,int y);

    //-------------------------------------------------------------------
    // NMC Reduced PME functions
    //-------------------------------------------------------------------
    void NMC_R_PME_init();
    void NMC_N_PME();
    void NMC_R_PME();
};

#endif // _YZO_H
