/*
    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;
}
