/********************************************************************/ /* Ewald V2.0: */ /* */ /* Calculate the total energy, force, stress tensor and force */ /* constant matrix (in k-space) of general 3D triclinic PBC rigid */ /* ion system with zero total charge. The result is the limit of */ /* (infinitely) many replicas stacked in a nearly spherical cluster */ /* and surrounded by conductive medium outside, thus the cluster */ /* has no surface charge distribution and there is no uniform */ /* macroscopic electric field; it is the only self-consistent */ /* set-up for both static and MD simulations in PBC. For details */ /* see reference "Doc/moldy.ps", and websites */ /* */ /* http://www.ee.duke.edu/~ayt/ewaldpaper/ */ /* http://www.keele.ac.uk/depts/ph/tc/cph_res/dynamo.html */ /* http://www.earth.ox.ac.uk/%7Ekeith/moldy.html */ /* http://www.fos.su.se/physical/sasha/md_prog.html */ /* */ /* Aug. 26, 1998 */ /* Developed by Ju Li at MIT */ /********************************************************************/ #include #include #include #define PI acos(-1.0) /* number of particles */ static int NP; /* real space positions and charges */ static double *x,*y,*z,*q; /* Ewald summation parameters */ static double my_accuracy,alpha,rcut,kcut; /* H matrix */ static double h11,h12,h13,h21,h22,h23,h31,h32,h33; /* G matrix: H*G^T = 2\pi I */ static double g11,g12,g13,g21,g22,g23,g31,g32,g33,volume; /* Distance from origin to three unit */ /* cell surfaces and reciprocal surfaces */ static double rd1,rd2,rd3,kd1,kd2,kd3; /* real space lattices to be be summed */ static int max_n1,max_n2,max_n3,max_xvec,num_xvec,*nvec; static double *xvec; /* reciprocal lattices to be be summed */ static int max_g1,max_g2,max_g3,max_gvec,num_gvec,*gvec; #ifdef _TABULATE_ERFC static double derr,*err,*cli; #endif #ifdef _TABULATE_SINE static double *sinn,*cosn; #endif /* Our convention is that (h11,h12,h13) (in f77 index) */ /* is the (x,y,z) component of the first cell edge, etc. */ void init_cell (double H[3][3]) { /* Because H is assumed to be coming from Fortran */ /* we have to do a little conversion */ h11 = H[0][0]; /* 1st element */ h12 = H[1][0]; /* 4th element */ h13 = H[2][0]; /* 7th element */ h21 = H[0][1]; /* 2nd element */ h22 = H[1][1]; /* 5th element */ h23 = H[2][1]; /* 8th element */ h31 = H[0][2]; /* 3rd element */ h32 = H[1][2]; /* 6th element */ h33 = H[2][2]; /* 9th element */ /* Get the reciprocal lattice vectors */ g11 = h22*h33 - h23*h32; g22 = h33*h11 - h31*h13; g33 = h11*h22 - h12*h21; g12 = h23*h31 - h21*h33; g23 = h31*h12 - h32*h11; g31 = h12*h23 - h13*h22; g13 = h21*h32 - h31*h22; g21 = h32*h13 - h12*h33; g32 = h13*h21 - h23*h11; volume = h11*g11 + h12*g12 + h13*g13; /* the shortest distance to respective surfaces */ rd1 = fabs(volume)/sqrt(g11*g11+g12*g12+g13*g13); rd2 = fabs(volume)/sqrt(g21*g21+g22*g22+g23*g23); rd3 = fabs(volume)/sqrt(g31*g31+g32*g32+g33*g33); /* reciprocal lattice vectors */ g11 *= 2*PI/volume; g12 *= 2*PI/volume; g13 *= 2*PI/volume; g21 *= 2*PI/volume; g22 *= 2*PI/volume; g23 *= 2*PI/volume; g31 *= 2*PI/volume; g32 *= 2*PI/volume; g33 *= 2*PI/volume; /* the shortest distance to respective reciprocal surfaces */ kd1 = 2*PI/sqrt(h11*h11+h12*h12+h13*h13); kd2 = 2*PI/sqrt(h21*h21+h22*h22+h23*h23); kd3 = 2*PI/sqrt(h31*h31+h32*h32+h33*h33); volume = fabs(volume); return; } /* end init_cell() */ /* the time ratio of evaluating a single term */ /* in the real and reciprocal space summation */ #define TRTF 5.5 /* empirical constants of Ju Li to the Fincham */ /* formulas to achieve the acclaimed accuracy */ #define RCUT_COEFF 1.2 #define KCUT_COEFF RCUT_COEFF void init_ewald (int *number_particles, double H[3][3], double *accuracy) { int i,j,k,max_err; double xx,yy,zz,r2,maxr; NP = *number_particles; my_accuracy = *accuracy; init_cell(H); /* Set the parameters alpha and cutoffs based on the formula by Fincham CCP5 38, p17 (1993) */ alpha = pow(NP*PI*PI*PI*TRTF/volume/volume,1./6.); rcut = sqrt(-log(my_accuracy))/alpha*RCUT_COEFF; kcut = 2*alpha*sqrt(-log(my_accuracy))*KCUT_COEFF; /* calculate the maximum separation between */ /* two particles inside the unit cell: must */ /* be one of the eight corners. */ maxr = 0.; for (i=-1; i<=1; i+=2) for (j=-1; j<=1; j+=2) for (k=-1; k<=1; k+=2) { xx = i*h11 + j*h21 + k*h31; yy = i*h12 + j*h22 + k*h32; zz = i*h13 + j*h23 + k*h33; r2 = xx*xx + yy*yy + zz*zz; if (r2>maxr*maxr) maxr=sqrt(r2); } /* construct a list of important real-space cells */ maxr += rcut; max_xvec = ceil(4.*PI*maxr*maxr*maxr/3./volume*1.2/16)*16; nvec = (int *) malloc(3*max_xvec*sizeof(int)); xvec = (double *) malloc(3*max_xvec*sizeof(double)); max_n1 = ceil(rcut/rd1); max_n2 = ceil(rcut/rd2); max_n3 = ceil(rcut/rd3); /* first record is the bare unit cell */ num_xvec = 3; xvec[0] = 0.; xvec[1] = 0.; xvec[2] = 0.; for (i=-max_n1; i<=max_n1; i++) for (j=-max_n2; j<=max_n2; j++) for (k=-max_n3; k<=max_n3; k++) if (!((i==0)&&(j==0)&&(k==0))) { xx = i*h11 + j*h21 + k*h31; yy = i*h12 + j*h22 + k*h32; zz = i*h13 + j*h23 + k*h33; r2 = xx*xx + yy*yy + zz*zz; /* only these cells are possible to */ /* have an interaction within rcut */ if (r2= 3*max_xvec) { printf ("init_ewald(): max_xvec reached.\n"); exit(1); } nvec[num_xvec-3] = i; nvec[num_xvec-2] = j; nvec[num_xvec-1] = k; } } /* construct a list of necessary reciprocal k-points */ max_gvec = ceil(2.*PI*kcut*kcut*kcut/3./ (8*PI*PI*PI/volume)*1.2/16)*16; gvec = (int *) malloc(3*max_gvec*sizeof(int)); max_g1 = ceil(kcut/kd1); max_g2 = ceil(kcut/kd2); max_g3 = ceil(kcut/kd3); /* first record is G=0, which has no inversion image */ num_gvec = 3; gvec[0] = 0; gvec[1] = 0; gvec[2] = 0; /* There are inversion symmetry in energy, */ /* force, stress, but not in force constant */ /* matrix calculations in a general system */ for (k=0; k<=max_g3; k++) for (j=-max_g2; j<=max_g2; j++) for (i=-max_g1; i<=max_g1; i++) if ((k>0)||(j>0)||((j==0)&&(i>0))) { xx = i*g11 + j*g21 + k*g31; yy = i*g12 + j*g22 + k*g32; zz = i*g13 + j*g23 + k*g33; r2 = xx*xx + yy*yy + zz*zz; if (r2 < kcut*kcut) { num_gvec += 3; if (num_gvec >= 3*max_gvec) { printf ("init_ewald(): max_gvec reached.\n"); exit(1); } gvec[num_gvec-3] = i; gvec[num_gvec-2] = j; gvec[num_gvec-1] = k; } } /* allocate real space positions and charges */ x = (double *) malloc(NP*sizeof(double)); y = (double *) malloc(NP*sizeof(double)); z = (double *) malloc(NP*sizeof(double)); q = (double *) malloc(NP*sizeof(double)); #ifdef _TABULATE_ERFC /* tabulate the error function and its derivative: */ /* because we will do expansion to the 2nd order, the */ /* interval should be proportional to (accuracy)^(1/3) */ derr = pow(*accuracy,1./3.)/2./_TABULATE_ERFC; /* default _TABULATE_ERFC = 1. */ max_err = ceil(alpha*rcut/derr); err = (double *)malloc(max_err*sizeof(double)); cli = (double *)malloc(max_err*sizeof(double)); for (i=0; i %d L-points\n", max_n1, max_n2, max_n3, num_xvec/3); printf ("Max_g1 = %d Max_g2 = %d Max_g3 = %d => %d G-points\n\n", max_g1, max_g2, max_g3, num_gvec/3); #endif return; } /* end init_ewald() */ void exit_ewald() { free(gvec); free(xvec); free(nvec); free(x); free(y); free(z); free(q); #ifdef _TABULATE_ERFC free(err); free(cli); #endif #ifdef _TABULATE_SINE free(sinn); free(cosn); #endif return; } /* end exit_ewald() */ void ewald (charge, s1, s2, s3, H, pote, fx, fy, fz, stress) /* charge of particles from 1 to NP */ double *charge; /* reduced coordinates of particles in [-0.5, 0.5] */ double *s1, *s2, *s3; /* H matrix of the cell, assumed to be passed from fortran */ /* code (column order): in there H(1,1),H(1,2),H(1,3) are */ /* the xyz components of the first edge, etc. */ double H[3][3]; /* Coulomb energy per cell of the system */ double *pote; /* Coulomb force on particles */ double *fx, *fy, *fz; /* stress tensor due to Coulomb interactions */ double stress[3][3]; { double dx,dy,dz,qsum=0.; double rx,ry,rz,r2,r,product,xx,margin,factor,gactor; double qij,ff; double kx,ky,kz,k2,cc,cossum,sinsum,ak; double sinx,cosx,siny,cosy,sinz,cosz; int i,j,k,l,n; int i000,ij00,ijk0,ijkl,jkl; /* make it work for Parrinello-Rahman MD */ init_cell(H); alpha = pow(NP*PI*PI*PI*TRTF/volume/volume,1./6.); rcut = sqrt(-log(my_accuracy))/alpha*RCUT_COEFF; kcut = 2*alpha*sqrt(-log(my_accuracy))*KCUT_COEFF; /* the formulas have nice scaling with volume, so */ /* generated L and G lattices need not be revised */ /* unless large shape changes happen during MD: */ for (n=0; n0.5)||(s1[i]<-0.5)|| (s2[i]>0.5)||(s2[i]<-0.5)|| (s3[i]>0.5)||(s3[i]<-0.5)) { printf("ewald(): reduced coordinates > 0.5.\n"); printf("may lead to inaccurate results.\n"); /* exit(1); */ if (s1[i]<-0.5) s1[i]++; if (s1[i]>0.5) s1[i]--; if (s2[i]<-0.5) s2[i]++; if (s2[i]>0.5) s2[i]--; if (s3[i]<-0.5) s3[i]++; if (s3[i]>0.5) s3[i]--; printf("reduced coordinates modified.\n"); } x[i] = s1[i]*h11 + s2[i]*h21 + s3[i]*h31; y[i] = s1[i]*h12 + s2[i]*h22 + s3[i]*h32; z[i] = s1[i]*h13 + s2[i]*h23 + s3[i]*h33; qsum += charge[i]; } if (fabs(qsum/NP) > 0.0001) printf("\nwarning from ewald():"\ "significant net charge in the system.\n"); for (i=0; i<3; i++) for (j=0; j<3; j++) stress[i][j] = 0.; *pote = 0.; for (i=0; i0.)&&(r2 line */ for (j=1; j<=2*max_g1; j++) { ij00 = i000 + j*(2*max_g2+1)*(2*max_g3+1); sinn[ij00] = sinn[ij00-(2*max_g2+1)*(2*max_g3+1)]*cosx + cosn[ij00-(2*max_g2+1)*(2*max_g3+1)]*sinx; cosn[ij00] = cosn[ij00-(2*max_g2+1)*(2*max_g3+1)]*cosx - sinn[ij00-(2*max_g2+1)*(2*max_g3+1)]*sinx; } /* line -> surface */ for (j=0; j<=2*max_g1; j++) { ij00 = i000 + j*(2*max_g2+1)*(2*max_g3+1); for (k=1; k<=2*max_g2; k++) { ijk0 = ij00 + k*(2*max_g3+1); sinn[ijk0] = sinn[ijk0-(2*max_g3+1)]*cosy + cosn[ijk0-(2*max_g3+1)]*siny; cosn[ijk0] = cosn[ijk0-(2*max_g3+1)]*cosy - sinn[ijk0-(2*max_g3+1)]*siny; } } /* surface -> cube */ for (j=0; j<=2*max_g1; j++) { ij00 = i000 + j*(2*max_g2+1)*(2*max_g3+1); for (k=0; k<=2*max_g2; k++) { ijk0 = ij00 + k*(2*max_g3+1); for (l=1; l<=2*max_g3; l++) { ijkl = ijk0 + l; sinn[ijkl] = sinn[ijkl-1]*cosz + cosn[ijkl-1]*sinz; cosn[ijkl] = cosn[ijkl-1]*cosz - sinn[ijkl-1]*sinz; } } } } #endif /* in the summation, omit G = 0 */ for (n=3; n ed. Maradudin, */ /* Solid State Phys. Academic Press, 1978, Page 209 */ /****************************************************/ /* last term of (6.2.47) with removal of divergence in Page 210: writes into a 3x3 complex matrix H */ void calc_ewald_dynamical_H (double fk[3], int m, int n, double H[3][6]) { int i,j,l; double rx[3],r2,r,product; double xx,margin,factor,gactor; double phase,a1,a2,cc; for (i=0; i<3; i++) for (j=0; j<6; j++) H[i][j] = 0.; /* m stands for \kappa, n stands for \kappa^\prime */ /* l is actually l^\prime in (6.2.47), the real l=0 */ for (l=0; l=3*NP. */ /*******************************************************/ void ewald_dynamical(double fk[3], double *phi, int *L) { int m,n,p,i,j,l; /* particle indices [0,NP-1], standing for kappa, */ /* kappa prime, and kappa double prime respectively */ double zero_k[3] = {TINY,0.,0.}; double Q[3][6], total[3][6]; double fk2 = fk[0]*fk[0] + fk[1]*fk[1] + fk[2]*fk[2]; if (fk2 < TINY*TINY) { printf ("\nwarning from ewald_dynamical():\n"); printf ("|k| is too small\n"); printf ("(kx=%e, ky=0, kz=0) is assumed.\n\n",TINY); fk[0] = TINY; fk[1] = fk[2] = 0.; fk2 = fk[0]*fk[0] + fk[1]*fk[1] + fk[2]*fk[2]; } /* Initialize the incoming matrix; in f77, */ /* complex number is stored in the simple */ /* way of (real,imag) in memory */ for (i=0; i<3*NP; i++) for (j=0; j<6*NP; j++) phi[i*2*(*L)+j] = 0.; /* evaluating the upper half is enough: */ for (m=0; m, Page 220. In fact, it is easy to see that the long range effect (change of forces on other atoms) of infinitesimal charge transfer occuring in a finite region, due to the infinitesimal displacement of one atom, is equivalent to that of the displacement itself, both by creating a small dipole. For a tetrahedral system or systems with higher symmetry, that dipole created by charge transfer must also be in the direction of the displacement, thus defining an effective e_T^* is completely plausible. 3. For lattice waves of displacing one type of atoms sinusoidally along certain k, we can add all the small dipoles together using linear superposition, which will create a polarization field with the same wave-vector k. The problem simplifies as k->0: if the displacements are perpendicular to k (transverse direction), there will be no spatial charge accumulation (\rho = -\nabla \cdot P) and so there will be no macroscopic electric field; if the displacements are in logitudinal direction then there will be spatial charge accumulation and corresponding excited electric field, but that field will induce further polarization of the electrons (charge re-distributed again) and so it will be screened. Thus the actual polarization field induced by logitudinal displacements is divided by the electronic dielectric constant \epsilon(\omega=0,k=0), and so are the forces on individual atoms. So the end the LO-TO splitting at k=0 is proportional to (e_T^*)^2 / \epsilon. */