/* DFT++ is a density functional package developed by the research group of Professor Tomas Arias Copyright 1996-2003 Sohrab Ismail-Beigi This file is part of DFT++. DFT++ is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. DFT++ is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with DFT++; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Please see the file CREDITS for a list of authors. For academic users, we request that publications using results obtained with this software reference "New algebraic formulation of density functional calculation," by Sohrab Ismail-Beigi and T.A. Arias, Computer Physics Communications 128:1-2, 1-45 (June 2000). and, if using the wavelet basis, further reference "Multiresolution analysis of electronic structure: semicardinal and wavelet bases," T.A. Arias, Reviews of Modern Physics 71:1, 267-311 (January 1999). and "Robust ab initio calculation of condensed matter: transparent convergence through semicardinal multiresolution analysis,'' I.P. Daykov, T.A. Arias, and Torkel D. Engeness, Physical Review Letters, 90:21, 216402 (May 2003). For your convenience, preprints of the above articles may be obtained from http://arXiv.org/abs/cond-mat/9909130, 9805262, and 0204411, respectively. */ /* * ewald.c: Sohrab Ismail-Beigi Jan 31, 1997, May 12 1997 * * Calculates the Ewald energy for a set of ions, etc. * */ /* $Id: ewald.cpp,v 1.11.2.9 2003/05/29 18:54:22 ivan Exp $ */ #include "header.h" #ifdef _WIN32 // the following function has been copied from glibc 2.3.2 sources /* @(#)s_erf.c 5.1 93/09/24 */ /* * ==================================================== * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. * * Developed at SunPro, a Sun Microsystems, Inc. business. * Permission to use, copy, modify, and distribute this * software is freely granted, provided that this notice * is preserved. * ==================================================== */ typedef unsigned int u_int32_t; typedef int int32_t; typedef union { double value; struct { u_int32_t lsw; u_int32_t msw; } parts; } ieee_double_shape_type; #define GET_HIGH_WORD(i,d) \ do { \ ieee_double_shape_type gh_u; \ gh_u.value = (d); \ (i) = gh_u.parts.msw; \ } while (0) #define SET_LOW_WORD(d,v) \ do { \ ieee_double_shape_type sl_u; \ sl_u.value = (d); \ sl_u.parts.lsw = (v); \ (d) = sl_u.value; \ } while (0) static const double tiny = 1e-300, half= 5.00000000000000000000e-01, /* 0x3FE00000, 0x00000000 */ one = 1.00000000000000000000e+00, /* 0x3FF00000, 0x00000000 */ two = 2.00000000000000000000e+00, /* 0x40000000, 0x00000000 */ /* c = (float)0.84506291151 */ erx = 8.45062911510467529297e-01, /* 0x3FEB0AC1, 0x60000000 */ /* * Coefficients for approximation to erf on [0,0.84375] */ efx = 1.28379167095512586316e-01, /* 0x3FC06EBA, 0x8214DB69 */ efx8= 1.02703333676410069053e+00, /* 0x3FF06EBA, 0x8214DB69 */ pp[] = {1.28379167095512558561e-01, /* 0x3FC06EBA, 0x8214DB68 */ -3.25042107247001499370e-01, /* 0xBFD4CD7D, 0x691CB913 */ -2.84817495755985104766e-02, /* 0xBF9D2A51, 0xDBD7194F */ -5.77027029648944159157e-03, /* 0xBF77A291, 0x236668E4 */ -2.37630166566501626084e-05}, /* 0xBEF8EAD6, 0x120016AC */ qq[] = {0.0, 3.97917223959155352819e-01, /* 0x3FD97779, 0xCDDADC09 */ 6.50222499887672944485e-02, /* 0x3FB0A54C, 0x5536CEBA */ 5.08130628187576562776e-03, /* 0x3F74D022, 0xC4D36B0F */ 1.32494738004321644526e-04, /* 0x3F215DC9, 0x221C1A10 */ -3.96022827877536812320e-06}, /* 0xBED09C43, 0x42A26120 */ /* * Coefficients for approximation to erf in [0.84375,1.25] */ pa[] = {-2.36211856075265944077e-03, /* 0xBF6359B8, 0xBEF77538 */ 4.14856118683748331666e-01, /* 0x3FDA8D00, 0xAD92B34D */ -3.72207876035701323847e-01, /* 0xBFD7D240, 0xFBB8C3F1 */ 3.18346619901161753674e-01, /* 0x3FD45FCA, 0x805120E4 */ -1.10894694282396677476e-01, /* 0xBFBC6398, 0x3D3E28EC */ 3.54783043256182359371e-02, /* 0x3FA22A36, 0x599795EB */ -2.16637559486879084300e-03}, /* 0xBF61BF38, 0x0A96073F */ qa[] = {0.0, 1.06420880400844228286e-01, /* 0x3FBB3E66, 0x18EEE323 */ 5.40397917702171048937e-01, /* 0x3FE14AF0, 0x92EB6F33 */ 7.18286544141962662868e-02, /* 0x3FB2635C, 0xD99FE9A7 */ 1.26171219808761642112e-01, /* 0x3FC02660, 0xE763351F */ 1.36370839120290507362e-02, /* 0x3F8BEDC2, 0x6B51DD1C */ 1.19844998467991074170e-02}, /* 0x3F888B54, 0x5735151D */ /* * Coefficients for approximation to erfc in [1.25,1/0.35] */ ra[] = {-9.86494403484714822705e-03, /* 0xBF843412, 0x600D6435 */ -6.93858572707181764372e-01, /* 0xBFE63416, 0xE4BA7360 */ -1.05586262253232909814e+01, /* 0xC0251E04, 0x41B0E726 */ -6.23753324503260060396e+01, /* 0xC04F300A, 0xE4CBA38D */ -1.62396669462573470355e+02, /* 0xC0644CB1, 0x84282266 */ -1.84605092906711035994e+02, /* 0xC067135C, 0xEBCCABB2 */ -8.12874355063065934246e+01, /* 0xC0545265, 0x57E4D2F2 */ -9.81432934416914548592e+00}, /* 0xC023A0EF, 0xC69AC25C */ sa[] = {0.0,1.96512716674392571292e+01, /* 0x4033A6B9, 0xBD707687 */ 1.37657754143519042600e+02, /* 0x4061350C, 0x526AE721 */ 4.34565877475229228821e+02, /* 0x407B290D, 0xD58A1A71 */ 6.45387271733267880336e+02, /* 0x40842B19, 0x21EC2868 */ 4.29008140027567833386e+02, /* 0x407AD021, 0x57700314 */ 1.08635005541779435134e+02, /* 0x405B28A3, 0xEE48AE2C */ 6.57024977031928170135e+00, /* 0x401A47EF, 0x8E484A93 */ -6.04244152148580987438e-02}, /* 0xBFAEEFF2, 0xEE749A62 */ /* * Coefficients for approximation to erfc in [1/.35,28] */ rb[] = {-9.86494292470009928597e-03, /* 0xBF843412, 0x39E86F4A */ -7.99283237680523006574e-01, /* 0xBFE993BA, 0x70C285DE */ -1.77579549177547519889e+01, /* 0xC031C209, 0x555F995A */ -1.60636384855821916062e+02, /* 0xC064145D, 0x43C5ED98 */ -6.37566443368389627722e+02, /* 0xC083EC88, 0x1375F228 */ -1.02509513161107724954e+03, /* 0xC0900461, 0x6A2E5992 */ -4.83519191608651397019e+02}, /* 0xC07E384E, 0x9BDC383F */ sb[] = {0.0,3.03380607434824582924e+01, /* 0x403E568B, 0x261D5190 */ 3.25792512996573918826e+02, /* 0x40745CAE, 0x221B9F0A */ 1.53672958608443695994e+03, /* 0x409802EB, 0x189D5118 */ 3.19985821950859553908e+03, /* 0x40A8FFB7, 0x688C246A */ 2.55305040643316442583e+03, /* 0x40A3F219, 0xCEDF3BE6 */ 4.74528541206955367215e+02, /* 0x407DA874, 0xE79FE763 */ -2.24409524465858183362e+01}; /* 0xC03670E2, 0x42712D62 */ double erfc(double x) { int32_t hx,ix; double R,S,P,Q,s,y,z,r; GET_HIGH_WORD(hx,x); ix = hx&0x7fffffff; if(ix>=0x7ff00000) { /* erfc(nan)=nan */ /* erfc(+-inf)=0,2 */ return (double)(((u_int32_t)hx>>31)<<1)+one/x; } if(ix < 0x3feb0000) { /* |x|<0.84375 */ double r1,r2,s1,s2,s3,z2,z4; if(ix < 0x3c700000) /* |x|<2**-56 */ return one-x; z = x*x; r1 = pp[0]+z*pp[1]; z2=z*z; r2 = pp[2]+z*pp[3]; z4=z2*z2; s1 = one+z*qq[1]; s2 = qq[2]+z*qq[3]; s3 = qq[4]+z*qq[5]; r = r1 + z2*r2 + z4*pp[4]; s = s1 + z2*s2 + z4*s3; y = r/s; if(hx < 0x3fd00000) { /* x<1/4 */ return one-(x+x*y); } else { r = x*y; r += (x-half); return half - r ; } } if(ix < 0x3ff40000) { /* 0.84375 <= |x| < 1.25 */ double s2,s4,s6,P1,P2,P3,P4,Q1,Q2,Q3,Q4; s = fabs(x)-one; P1 = pa[0]+s*pa[1]; s2=s*s; Q1 = one+s*qa[1]; s4=s2*s2; P2 = pa[2]+s*pa[3]; s6=s4*s2; Q2 = qa[2]+s*qa[3]; P3 = pa[4]+s*pa[5]; Q3 = qa[4]+s*qa[5]; P4 = pa[6]; Q4 = qa[6]; P = P1 + s2*P2 + s4*P3 + s6*P4; Q = Q1 + s2*Q2 + s4*Q3 + s6*Q4; if(hx>=0) { z = one-erx; return z - P/Q; } else { z = erx+P/Q; return one+z; } } if (ix < 0x403c0000) { /* |x|<28 */ x = fabs(x); s = one/(x*x); if(ix< 0x4006DB6D) { /* |x| < 1/.35 ~ 2.857143*/ double R1,R2,R3,R4,S1,S2,S3,S4,s2,s4,s6,s8; R1 = ra[0]+s*ra[1];s2 = s*s; S1 = one+s*sa[1]; s4 = s2*s2; R2 = ra[2]+s*ra[3];s6 = s4*s2; S2 = sa[2]+s*sa[3];s8 = s4*s4; R3 = ra[4]+s*ra[5]; S3 = sa[4]+s*sa[5]; R4 = ra[6]+s*ra[7]; S4 = sa[6]+s*sa[7]; R = R1 + s2*R2 + s4*R3 + s6*R4; S = S1 + s2*S2 + s4*S3 + s6*S4 + s8*sa[8]; } else { /* |x| >= 1/.35 ~ 2.857143 */ double R1,R2,R3,S1,S2,S3,S4,s2,s4,s6; if(hx<0&&ix>=0x40180000) return two-tiny;/* x < -6 */ R1 = rb[0]+s*rb[1];s2 = s*s; S1 = one+s*sb[1]; s4 = s2*s2; R2 = rb[2]+s*rb[3];s6 = s4*s2; S2 = sb[2]+s*sb[3]; R3 = rb[4]+s*rb[5]; S3 = sb[4]+s*sb[5]; S4 = sb[6]+s*sb[7]; R = R1 + s2*R2 + s4*R3 + s6*rb[6]; S = S1 + s2*S2 + s4*S3 + s6*S4; } z = x; SET_LOW_WORD(z,0); r = exp(-z*z-0.5625)*exp((z-x)*(z+x)+R/S); if(hx>0) return r/x; else return two-r/x; } else { if(hx>0) return tiny*tiny; else return two-tiny; } } #endif /* * Global variables that control how far * out in R- and G-space the lattice sums go for the ewald energy. * Sums are done from _start to _end in x,y, and z directions. */ static int Ewald_setup_was_done = 0; static int Nlat_start_real, Nlat_end_real,Nlat_start_recip, Nlat_end_recip; /* * Nlat_start_real Nlat_end_real * Nlat_start_recip Nlat_end_recip * * The real and recip. space sums go from -Nlat to Nlat in each of x,y, and z * directions, with Nlat going from Nlat_start to Nlat_end. * */ void setup_Ewald(int nlat_s_real, int nlat_e_real, int nlat_s_recip, int nlat_e_recip) { Nlat_start_real = nlat_s_real; Nlat_end_real = nlat_e_real; Nlat_start_recip = nlat_s_recip; Nlat_end_recip = nlat_e_recip; Ewald_setup_was_done = 1; } /* * Retrieve the ewald parameters. */ int get_ewald(int &nlat_s_real, int &nlat_e_real, int &nlat_s_recip, int &nlat_e_recip) { if (! Ewald_setup_was_done) return 0; nlat_s_real = Nlat_start_real; nlat_e_real = Nlat_end_real; nlat_s_recip = Nlat_start_recip; nlat_e_recip = Nlat_end_recip; return 1; } /* * Calculates the Ewald energy per unit cell * for natoms atoms of charges Z[0..natoms-1] * emersed in a uniform compensating charg density sum_i(Z[i])/Vol * where Vol = det(R) = unit cell volume. * atpos[0..natoms-1][3] is in lattice coordinates. * R[][] contains the lattice vectors in the columns. * * With tau denoting an atom in the basis, * the energy is Ewald = 0.5*sum_{tau} { Z[tau]*phi[tau] } * where phi[tau] is the electrostatic potential caused by ALL OTHER ions * everywhere and the uniform compensating background. * * The energy comes in two parts: real-space sum of screened point-charges * (screend by gaussians), and a G-space sum of the potential of * Guassians in a uniform background; there are also some constants from * "renormalization" effects (cutoffs going to infinity in a controlled way). * * We calculate the above sums in R- and G-space by summing the values * in a box of size [-Nlat,Nlat]^3...Nlat is run through a set of values * to check for convergence. * */ real Ewald(Ioninfo &ioninfo, Lattice &lattice) { /* Constants */ const real pi = M_PI; const real twopi = 2.0*pi; const real fourpi = 4.0*pi; /* Local vars */ real *Z; vector3 *atpos; int natoms; matrix3 R,RTR,G,GGT; vector3 x; real vol,G2,r,temp,sigma,eta; real angle,SG[2]; real Ereal,Erecip,Etot; int sp,i,j,k,l,tau,taup,cell[3],Nlat; if (!Ewald_setup_was_done) die("Ewald() was not setup!!!\n"); /* Take out all the ions from the Ioninfo structure and put them into * a long list of Z[] and atpos[] values */ natoms = 0; for (sp=0; sp < ioninfo.nspecies; sp++) natoms += ioninfo.species[sp].natoms; Z = (real *)mymalloc(sizeof(real)*natoms,"Z","Ewald()"); atpos = (vector3 *)mymalloc(sizeof(vector3)*natoms,"atpos","Ewald()"); k = 0; for (sp=0; sp < ioninfo.nspecies; sp++) for (j=0; j < ioninfo.species[sp].natoms; j++) { Z[k] = ioninfo.species[sp].Z; atpos[k] = ioninfo.species[sp].atpos[j]; k++; } /* Unit cell volume */ vol = lattice.unit_cell_volume; // Lattice vector matrix R = lattice.R; /* matrix of dot-products of lattice vectors */ RTR = lattice.RTR; /* recip. lattice vectors in rows of G */ G = lattice.G; /* dot products of recip. lattice vectors */ GGT = lattice.GGT; dft_log("\n------ Ewald() -----\n"); dft_log("latvec = \n"); R.print(dft_global_log,"%lg "); dft_log("natoms = %d\n",natoms); dft_log(DFT_ANAL_LOG,"Z = [ "); for (i=0; i < natoms; i++) dft_log(DFT_ANAL_LOG,"%lg ",Z[i]); dft_log(DFT_ANAL_LOG,"]\natpos=\n"); if (dft_global_log->get_level() >= DFT_ANAL_LOG) for (i=0; i < natoms; i++) atpos[i].print(dft_global_log,"%lg "); /* set width of gaussian to 0.4 the nearest-neighbor distance */ /* Here I'll loop over the cells close to the origin and find * the minimal distance. */ sigma = sqrt(RTR.m[0][0]); for (i=-2; i<=2; i++) for (j=-2; j<=2; j++) for (k=-2; k<=2; k++) if ( i!=0 || j!=0 || k!=0 ) { x.v[0] = i; x.v[1] = j; x.v[2] = k; r = sqrt(x*(RTR*x)); if (r < sigma) sigma = r; } sigma *= 0.4; /* set scale of width of gaussian to roughly the interatomic distance */ /* sigma = 0.4*pow(vol/(real)natoms,1.0/3.0); */ eta = 1.0/(sqrt(2.0)*sigma); dft_log("Using sigma = %lg eta = %lg for gaussian\n\n", sigma,eta); /* Real-space part of energy: (1) constant parts */ Ereal = (real)0.0; for (tau=0; tau < natoms; tau++) for (taup=0; taup < natoms; taup++) { dft_log(DFT_NERD_LOG, "\nReal space potential for tau=%d taup=%d\n", tau,taup); /* The constant part of the energy from "renomalization" */ temp = -0.5*Z[tau]*Z[taup]*pi/(vol*eta*eta); /* If tau==tau', then add the "negative" potential of gaussian at * tau */ if (tau == taup) temp += -0.5*Z[tau]*Z[taup]*2.0*eta/sqrt(pi); Ereal += temp; dft_log(DFT_NERD_LOG,"Constant part = %le\n",temp); } /* Real-space part of energy: (2) lattice sums over screened ion pairs */ /* loop over size of lattice sums */ for (Nlat = 0; Nlat <= Nlat_end_real; Nlat++) { for (tau=0; tau < natoms; tau++) for (taup=0; taup < natoms; taup++) { /* loop over cells */ for (cell[0]=-Nlat; cell[0]<=Nlat; cell[0]++) for (cell[1]=-Nlat; cell[1]<=Nlat; cell[1]++) for (cell[2]=-Nlat; cell[2]<=Nlat; cell[2]++) /* For each value of Nlat, we only sum over the cells * which have one coordinate == +/-Nlat, i.e. the surfaces * of the cube of points running [-Nlat,Nlat] in each * direction. */ if ( abs(cell[0])==Nlat || abs(cell[1])==Nlat || abs(cell[2])==Nlat ) /* Only exclude the cell==0 and tau==tau' term */ if (tau!=taup || cell[0]!=0 || cell[1]!=0 || cell[2]!=0) { /* Find the distance |cell+tau'-tau| between atom at * tau and the other atom being considered. * x is this cell+tau'-tau vector in lattice coords; * r is its actual length in real distance units*/ for (l=0; l < 3; l++) x.v[l] = cell[l] + atpos[taup].v[l] - atpos[tau].v[l]; r = sqrt(x*(RTR*x)); temp = 0.5*Z[tau]*Z[taup]*erfc(eta*r)/r; Ereal += temp; dft_log(DFT_NERD_LOG, "cell=[%d %d %d] r =%lg e = %le\n", cell[0],cell[1],cell[2],r,temp); } } /* tau' loop */ dft_log("Nlat = %2d Real-space energy = %25.15le\n", Nlat,Ereal); dft_log_flush(); } /* Nlat loop */ /* * Reciprocal space contribution: * Erecip = 0.5*sum_{G!=0} * {4*pi*exp(-|G|^2/(4*eta^2))/(vol*|G|^2)*|S(G)|^2} * where S(G) = sum_{tau} { Z[tau]*exp(-i*G*r_tau) } * * r_tau = R*tau (R is matrix, tau is 3-vector) and * G = cell*G (second G is matrix, cell is row-vector of integers) * so G*r_tau = 2*pi*cell*tau. */ Erecip = (real)0.0; for (Nlat = 1; Nlat <= Nlat_end_recip; Nlat++) { for (cell[0]=-Nlat; cell[0]<=Nlat; cell[0]++) for (cell[1]=-Nlat; cell[1]<=Nlat; cell[1]++) for (cell[2]=-Nlat; cell[2]<=Nlat; cell[2]++) /* For each value of Nlat, we only sum over the cells * which have one coordinate == +/-Nlat, i.e. the surfaces * of the cube of points running [-Nlat,Nlat] in each * direction. */ if ( abs(cell[0])==Nlat || abs(cell[1])==Nlat || abs(cell[2])==Nlat ) /* Skip G=0 */ if (cell[0]!=0 || cell[1]!=0 || cell[2]!=0) { /* Calculate structure factor */ SG[0] = SG[1] = 0.0; for (tau=0; tau < natoms; tau++) { angle = -twopi*(cell[0]*atpos[tau].v[0]+ cell[1]*atpos[tau].v[1]+ cell[2]*atpos[tau].v[2] ); SG[0] += Z[tau]*cos(angle); SG[1] += Z[tau]*sin(angle); } /* Calculate |G|^2 */ G2 = GGT.m[0][0]*cell[0]*cell[0] + GGT.m[1][1]*cell[1]*cell[1] + GGT.m[2][2]*cell[2]*cell[2] + 2.0*( GGT.m[0][1]*cell[0]*cell[1] + GGT.m[0][2]*cell[0]*cell[2] + GGT.m[1][2]*cell[1]*cell[2] ); /* The energy for G */ temp = 0.5*fourpi*exp(-G2/(4.0*eta*eta))/(G2*vol)* (SG[0]*SG[0]+SG[1]*SG[1]); Erecip += temp; dft_log(DFT_NERD_LOG, "G=[%d %d %d] G2 =%lg e = %le\n", cell[0],cell[1],cell[2],G2,temp); } dft_log("Nlat = %2d Reciprocal space energy = %25.15le\n", Nlat,Erecip); dft_log_flush(); } /* of Nlat loop */ Etot = Ereal + Erecip; dft_log("\nEwald energy = %25.15le\n\n",Etot); dft_log_flush(); myfree(Z); myfree(atpos); return Etot; } /* * Derivative of Ewald energy versus the position of the atom 'atom' of * species 'species' (lattice coordinates). */ vector3 dEwald_datom_pos(Ioninfo &ioninfo, Lattice &lattice, const int species,const int atom) { const real pi = M_PI, twopi = 2.0*pi, fourpi = 4.0*pi, sqrtpi = sqrt(pi); /* Local vars */ real *Z; vector3 *atpos; int natoms; matrix3 R,RTR,G,GGT; real vol,sigma,eta; int sp,i,j,k,l,cell[3],Nlat,taup; int tau; /* the index of the atom corresponding to 'species' and 'atom' */ vector3 result(0.0,0.0,0.0); /* holds the final result */ if (!Ewald_setup_was_done) die("Ewald() was not setup!!!\n"); /* Take out all the ions from the Ioninfo structure and put them into * a long list of Z[] and atpos[] values */ natoms = 0; for (sp=0; sp < ioninfo.nspecies; sp++) natoms += ioninfo.species[sp].natoms; Z = (real *)mymalloc(sizeof(real)*natoms,"Z","dEwald_datom_pos()"); atpos = (vector3 *)mymalloc(sizeof(vector3)*natoms, "atpos","dEwald_datom_pos()"); k = 0; tau = -1; for (sp=0; sp < ioninfo.nspecies; sp++) for (j=0; j < ioninfo.species[sp].natoms; j++) { Z[k] = ioninfo.species[sp].Z; atpos[k] = ioninfo.species[sp].atpos[j]; /* Find the index correspoding to species/atom and store it in tau */ if (sp == species && j == atom) tau = k; k++; } if (tau == -1) die("dEwlad_datom_pos(): no atom corresponding to requested deriv!!!\n"); /* Unit cell volume */ vol = lattice.unit_cell_volume; // Lattice vector matrix R = lattice.R; /* matrix of dot-products of lattice vectors */ RTR = lattice.RTR; /* recip. lattice vectors in rows of G */ G = lattice.G; /* dot products of recip. lattice vectors */ GGT = lattice.GGT; /* set width of gaussian to 0.4 the nearest-neighbor distance */ /* Here I'll loop over the cells close to the origin and find * the minimal distance. */ sigma = sqrt(RTR.m[0][0]); for (i=-2; i<=2; i++) for (j=-2; j<=2; j++) for (k=-2; k<=2; k++) if ( i!=0 || j!=0 || k!=0 ) { vector3 x; real r; x.v[0] = i; x.v[1] = j; x.v[2] = k; r = sqrt(x*(RTR*x)); if (r < sigma) sigma = r; } sigma *= 0.4; eta = 1.0/(sqrt(2.0)*sigma); /* Real-space part of derivative */ /* Loop over atoms and cells */ Nlat = Nlat_end_real; for (taup=0; taup < natoms; taup++) for (cell[0]=-Nlat; cell[0]<=Nlat; cell[0]++) for (cell[1]=-Nlat; cell[1]<=Nlat; cell[1]++) for (cell[2]=-Nlat; cell[2]<=Nlat; cell[2]++) /* Only exclude the cell==0 and tau==tau' term */ if (tau!=taup || cell[0]!=0 || cell[1]!=0 || cell[2]!=0) { real temp,r; vector3 x,RTRx; /* Find the distance |cell+tau'-tau| between atom at tau * and the other atom being considered. * x is this cell+tau'-tau vector in lattice coords; * r is its actual length in real distance units*/ for (l=0; l < 3; l++) x.v[l] = cell[l] + atpos[tau].v[l] - atpos[taup].v[l]; RTRx = RTR*x; r = sqrt(x*RTRx); temp = -Z[tau]*Z[taup]*(erfc(eta*r)/(r*r) + 2.0*eta*exp(-eta*eta*r*r)/(r*sqrtpi) )/r; for (l=0; l < 3; l++) result.v[l] += temp*RTRx.v[l]; } /* * Reciprocal space contribution. */ Nlat = Nlat_end_recip; for (cell[0]=-Nlat; cell[0]<=Nlat; cell[0]++) for (cell[1]=-Nlat; cell[1]<=Nlat; cell[1]++) for (cell[2]=-Nlat; cell[2]<=Nlat; cell[2]++) /* Skip G=0 */ if (cell[0]!=0 || cell[1]!=0 || cell[2]!=0) { complex SG,Stau; real G2,temp,angle; int taup; /* Calculate structure factor */ SG.x = SG.y = 0.0; for (taup=0; taup < natoms; taup++) { angle = -twopi*(cell[0]*atpos[taup].v[0]+ cell[1]*atpos[taup].v[1]+ cell[2]*atpos[taup].v[2] ); SG.x += Z[taup]*cos(angle); SG.y += Z[taup]*sin(angle); } /* Structure factor for tau alone: Ztau*exp(-i*G.tau) */ angle = -twopi*(cell[0]*atpos[tau].v[0]+ cell[1]*atpos[tau].v[1]+ cell[2]*atpos[tau].v[2] ); Stau.x = Z[tau]*cos(angle); Stau.y = Z[tau]*sin(angle); /* Calculate |G|^2 */ G2 = GGT.m[0][0]*cell[0]*cell[0] + GGT.m[1][1]*cell[1]*cell[1] + GGT.m[2][2]*cell[2]*cell[2] + 2.0*( GGT.m[0][1]*cell[0]*cell[1] + GGT.m[0][2]*cell[0]*cell[2] + GGT.m[1][2]*cell[1]*cell[2] ); /* The contribution for G */ temp = fourpi*exp(-G2/(4.0*eta*eta))/(G2*vol); temp *= twopi*(SG.x*Stau.y-SG.y*Stau.x); for (l=0; l < 3; l++) result.v[l] += temp*cell[l]; } myfree(Z); myfree(atpos); /* return the fruits of our labors */ return result; }