/* 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. */ #include "header.h" //Sum up the total charge recursively double totcharge(struct Grid *ingrid) { int ix,iy,iz; double sumcharge=0.; int nnx,nny,nnz; nnx=ingrid->dim.x; nny=ingrid->dim.y; nnz=ingrid->dim.z; double scalefactor; scalefactor=ingrid->scale.x*ingrid->scale.y*ingrid->scale.z; for (ix=0;ixparent)) sumcharge+=scalefactor*ingrid->dat[iz+(nnz*(iy+nny*ix))]; // sumcharge+=ingrid->dat[iz+(nnz*(iy+nny*ix))]; dft_log("Sumcharge is: %d %20.16lf\n",ingrid->level,sumcharge); if (ingrid->child) sumcharge+=totcharge(ingrid->child); if (ingrid->sibling) sumcharge+=totcharge(ingrid->sibling); return(sumcharge); } /* Using Teter's nuclear charge distribution*/ double nuc_charge(vector3 r, double Z) { const double pi = M_PI; const double c1=3.132576693428; const double c2=-2.68355838224; const double c3=1-c1-c2; double d,d1,d2,d3; double rad2=abs2(r); double charge; d=1./(4.*Z); d1=d/sqrt(2.); d2=d; d3=sqrt(2.)*d; charge= Z*c1*exp(-rad2/(d1*d1))/(pow(pi,1.5)*d1*d1*d1)+ Z*c2*exp(-rad2/(d2*d2))/(pow(pi,1.5)*d2*d2*d2)+ Z*c3*exp(-rad2/(d3*d3))/(pow(pi,1.5)*d3*d3*d3); return(charge); } void eval_nuc_charge(struct Grid *self, vector3 porg, matrix3 R, int pad, double Z) { // Scale of children (if any) vector3 scl; // Origin of self (note that self->org is in the _parents_ coordinates vector3 org, temp, Rtemp; int i,j,k; int i2,j2,k2; vector3 r; real A,B,C; // lengths of lattice vectors A = abs(R[0]); B = abs(R[1]); C = abs(R[2]); // Return immediately if there is nothing to do // No excutables BEFORE this statement, please! if (self==NULL) return; // set the scale scl.v[0]=self->scale.x; scl.v[1]=self->scale.y; scl.v[2]=self->scale.z; //dft_log("Level %d : scale (%lf %lf %lf)\n",self->level,scl.v[0],scl.v[1], // scl.v[2]); // Origin of self (note that self->org is in the _parents_ coordinates temp.v[0] = self->org.x*2.0*scl.v[0]/A; temp.v[1] = self->org.y*2.0*scl.v[1]/B; temp.v[2] = self->org.z*2.0*scl.v[2]/C; Rtemp = R*temp; org = porg + Rtemp; // Now org is a cartesian vector. // org.v[0] = porg.v[0] + self->org.x *2.0*scl.v[0]; // org.v[1] = porg.v[1] + self->org.y *2.0*scl.v[1]; // org.v[2] = porg.v[2] + self->org.z *2.0*scl.v[2]; //dft_log("Level %d : org (%lf %lf %lf)\n",self->level,org.v[0],org.v[1], // org.v[2]); //double maxval = 0.0; // Fill in data for (i=0; idim.x; i++) for (j=0; jdim.y; j++) for (k=0; kdim.z; k++){ *( self->dat+(i*self->dim.y+j)*self->dim.z+k ) = 0.0; for (i2=-pad; i2<=pad; i2++) for (j2=-pad; j2<=pad; j2++) for (k2=-pad; k2<=pad; k2++) { // For a non-orthorhombic cell, we have to multiply by R, // the matrix of lattice vectors. // r.v[0]=org.v[0]+i*scl.v[0]+i2*top.v[0]; // r.v[1]=org.v[1]+j*scl.v[1]+j2*top.v[1]; // r.v[2]=org.v[2]+k*scl.v[2]+k2*top.v[2]; temp.v[0] = i*scl.v[0]/A; temp.v[1] = j*scl.v[1]/B; temp.v[2] = k*scl.v[2]/C; Rtemp = R*temp; r = org + Rtemp + i2*R[0] + j2*R[1] + k2*R[2]; *( self->dat+(i*self->dim.y+j)*self->dim.z+k ) += nuc_charge(r,Z); // if((abs(r) <= 0.25) && (self->level < 2)) // dft_log("nuc_charge value = %1.12lf\n", // *( self->dat+(i*self->dim.y+j)*self->dim.z+k )); // if((fabs(*( self->dat+(i*self->dim.y+j)*self->dim.z+k )) // > maxval) && ( ( (i%2 == 1) || (j%2 == 1) || // (k%2 == 1) ) || (!self->parent) )) // maxval = // fabs(*( self->dat+(i*self->dim.y+j)*self->dim.z+k ) ); } } //dft_log("Level %d maxval = %1.12lf\n",self->level,maxval); // Grab data from up above to ensure consistency if (self->level>0) for (i=0; idim.x; i+=2) for (j=0; jdim.y; j+=2) for (k=0; kdim.z; k+=2) *( self->dat+(i*self->dim.y+j)*self->dim.z+k ) = *( self->parent->dat + ( (self->org.x+i/2) * self->parent->dim.y + (self->org.y+j/2) ) * self->parent->dim.z + (self->org.z+k/2) ); // Call child and sibling, return immediately if none if (self->child!=NULL) eval_nuc_charge(self->child,org,R,pad,Z); if (self->sibling!=NULL) eval_nuc_charge(self->sibling,porg,R,pad,Z); return; } // Origin must be in Cartesian coordinates! void get_nuc_charge(RealSpaceScalarFieldColumn &rho, vector3 origin, real charge) { // This only pays attention to the real part of rho. Zero out the // imaginary part before calling this routine. Gridspec *gridspec; // choose the appropriate gridspec switch(rho.embedding){ case SPARSE: gridspec = rho.basis_spec->gridspec; break; case DENSE: gridspec = rho.basis_spec->gridspec_dense; break; default: die("getnuc_charge: Unknown input embedding %d\n",rho.embedding); } Grid *grid = mkgridnew(gridspec,NULL); // print the grid structure only from the master node #ifdef DFT_MPI if(System::Get_procID() == 0) printgridstruct(stdout,grid); #else printgridstruct(stdout,grid); #endif // Evidently the eval_nuc_charge function uses the origin as a shift, so // negate it. origin *= (-1.0); GetRe(grid,rho); eval_nuc_charge(grid,origin,rho.basis_spec->lattice->latvec,1,charge); //dft_log("Totcharge = %1.12lf\n",totcharge(grid)); PutRe(grid,rho); killgridnew(grid); } void get_ionic_charge_density(RealSpaceScalarFieldColumn &rho, Ioninfo &ioninfo, Lattice &lattice) { int type, ion; vector3 origin; RealSpaceScalarFieldColumn add(rho); rho.zero_out(); add.zero_out(); for(type=0; type gridspec switch(rho.embedding){ case SPARSE: gridspec = rho.basis_spec->gridspec; break; case DENSE: gridspec = rho.basis_spec->gridspec_dense; break; default: die("getnuc_charge: Unknown input embedding %d\n",rho.embedding); } Grid *grid = mkgridnew(gridspec,NULL); //ipd: we must take care if parallel!!! - don't want to just use printf //dft_log("Grid Structure:\n"); //printgridstruct(stdout,grid); GetRe(grid,J(rho)); dft_log("\nTotal charge = %1.12lf\n",totcharge(grid)); killgridnew(grid); } void Vlocpot(Everything &everything) { // Check the read_vloc_flag to see if we have to calculate Vlocpot, or // just read it in. if(everything.ioninfo.read_vloc_flag) everything.elecvars.Vlocpot.read(everything.ioninfo.vloc_filename); else { RealSpaceScalarFieldColumn rho(everything.elecvars.n); get_ionic_charge_density(rho,everything.ioninfo,everything.lattice); // Rho is considered by the code to be a number density, so we have to // explicitly add a minus sign to deal with positive charge. rho *= (-1.0); // HACK ALERT!!! // Set the value of global_abs2Yg to give convdigits = 9 in invL. global_abs2Yg = 1.e-10; solve_poisson(rho,everything.elecvars.Vlocpot); // Grid *nucpoisson=mkgridnew(rho.basis_spec->gridspec,NULL); // GetRe(nucpoisson,everything.elecvars.Vlocpot); // FILE *fpnucpoisson=fopen("nuc_poisson.dft","w"); // writetofile(fpnucpoisson,nucpoisson); // killgridnew(nucpoisson); // fclose(fpnucpoisson); // This is due to differing definitions of Vlocpot between the wavelets // and planewave codes. apply_Obar(everything.elecvars.Vlocpot,everything.elecvars.Vlocpot); } }