/* Estimate the reciprocal space part error of the SPME Ewald sum. */ static real estimate_reciprocal( t_inputinfo *info, rvec x[], /* array of particles */ real q[], /* array of charges */ int nr, /* number of charges = size of the charge array */ FILE *fp_out, gmx_bool bVerbose, unsigned int seed, /* The seed for the random number generator */ int *nsamples, /* Return the number of samples used if Monte Carlo * algorithm is used for self energy error estimate */ t_commrec *cr) { real e_rec=0; /* reciprocal error estimate */ real e_rec1=0; /* Error estimate term 1*/ real e_rec2=0; /* Error estimate term 2*/ real e_rec3=0; /* Error estimate term 3 */ real e_rec3x=0; /* part of Error estimate term 3 in x */ real e_rec3y=0; /* part of Error estimate term 3 in y */ real e_rec3z=0; /* part of Error estimate term 3 in z */ int i,ci; int nx,ny,nz; /* grid coordinates */ real q2_all=0; /* sum of squared charges */ rvec gridpx; /* reciprocal grid point in x direction*/ rvec gridpxy; /* reciprocal grid point in x and y direction*/ rvec gridp; /* complete reciprocal grid point in 3 directions*/ rvec tmpvec; /* template to create points from basis vectors */ rvec tmpvec2; /* template to create points from basis vectors */ real coeff=0; /* variable to compute coefficients of the error estimate */ real coeff2=0; /* variable to compute coefficients of the error estimate */ real tmp=0; /* variables to compute different factors from vectors */ real tmp1=0; real tmp2=0; gmx_bool bFraction; /* Random number generator */ gmx_rng_t rng=NULL; int *numbers=NULL; /* Index variables for parallel work distribution */ int startglobal,stopglobal; int startlocal, stoplocal; int x_per_core; int xtot; #ifdef TAKETIME double t0=0.0; double t1=0.0; #endif rng=gmx_rng_init(seed); clear_rvec(gridpx); clear_rvec(gridpxy); clear_rvec(gridp); clear_rvec(tmpvec); clear_rvec(tmpvec2); for(i=0;i<nr;i++) { q2_all += q[i]*q[i]; } /* Calculate indices for work distribution */ startglobal=-info->nkx[0]/2; stopglobal = info->nkx[0]/2; xtot = stopglobal*2+1; if (PAR(cr)) { x_per_core = ceil((real)xtot / (real)cr->nnodes); startlocal = startglobal + x_per_core*cr->nodeid; stoplocal = startlocal + x_per_core -1; if (stoplocal > stopglobal) stoplocal = stopglobal; } else { startlocal = startglobal; stoplocal = stopglobal; x_per_core = xtot; } /* #ifdef GMX_LIB_MPI MPI_Barrier(MPI_COMM_WORLD); #endif */ #ifdef GMX_LIB_MPI #ifdef TAKETIME if (MASTER(cr)) t0 = MPI_Wtime(); #endif #endif if (MASTER(cr)){ fprintf(stderr, "Calculating reciprocal error part 1 ..."); } for(nx=startlocal; nx<=stoplocal; nx++) { svmul(nx,info->recipbox[XX],gridpx); for(ny=-info->nky[0]/2; ny<info->nky[0]/2+1; ny++) { svmul(ny,info->recipbox[YY],tmpvec); rvec_add(gridpx,tmpvec,gridpxy); for(nz=-info->nkz[0]/2; nz<info->nkz[0]/2+1; nz++) { if ( 0 == nx && 0 == ny && 0 == nz ) continue; svmul(nz,info->recipbox[ZZ],tmpvec); rvec_add(gridpxy,tmpvec,gridp); tmp=norm2(gridp); coeff=exp(-1.0 * M_PI * M_PI * tmp / info->ewald_beta[0] / info->ewald_beta[0] ) ; coeff/= 2.0 * M_PI * info->volume * tmp; coeff2=tmp ; tmp=eps_poly2(nx,info->nkx[0],info->pme_order[0]); tmp+=eps_poly2(ny,info->nkx[0],info->pme_order[0]); tmp+=eps_poly2(nz,info->nkx[0],info->pme_order[0]); tmp1=eps_poly1(nx,info->nkx[0],info->pme_order[0]); tmp2=eps_poly1(ny,info->nky[0],info->pme_order[0]); tmp+=2.0 * tmp1 * tmp2; tmp1=eps_poly1(nz,info->nkz[0],info->pme_order[0]); tmp2=eps_poly1(ny,info->nky[0],info->pme_order[0]); tmp+=2.0 * tmp1 * tmp2; tmp1=eps_poly1(nz,info->nkz[0],info->pme_order[0]); tmp2=eps_poly1(nx,info->nkx[0],info->pme_order[0]); tmp+=2.0 * tmp1 * tmp2; tmp1=eps_poly1(nx,info->nkx[0],info->pme_order[0]); tmp1+=eps_poly1(ny,info->nky[0],info->pme_order[0]); tmp1+=eps_poly1(nz,info->nkz[0],info->pme_order[0]); tmp+= tmp1 * tmp1; e_rec1+= 32.0 * M_PI * M_PI * coeff * coeff * coeff2 * tmp * q2_all * q2_all / nr ; tmp1=eps_poly3(nx,info->nkx[0],info->pme_order[0]); tmp1*=info->nkx[0]; tmp2=iprod(gridp,info->recipbox[XX]); tmp=tmp1*tmp2; tmp1=eps_poly3(ny,info->nky[0],info->pme_order[0]); tmp1*=info->nky[0]; tmp2=iprod(gridp,info->recipbox[YY]); tmp+=tmp1*tmp2; tmp1=eps_poly3(nz,info->nkz[0],info->pme_order[0]); tmp1*=info->nkz[0]; tmp2=iprod(gridp,info->recipbox[ZZ]); tmp+=tmp1*tmp2; tmp*=4.0 * M_PI; tmp1=eps_poly4(nx,info->nkx[0],info->pme_order[0]); tmp1*=norm2(info->recipbox[XX]); tmp1*=info->nkx[0] * info->nkx[0]; tmp+=tmp1; tmp1=eps_poly4(ny,info->nky[0],info->pme_order[0]); tmp1*=norm2(info->recipbox[YY]); tmp1*=info->nky[0] * info->nky[0]; tmp+=tmp1; tmp1=eps_poly4(nz,info->nkz[0],info->pme_order[0]); tmp1*=norm2(info->recipbox[ZZ]); tmp1*=info->nkz[0] * info->nkz[0]; tmp+=tmp1; e_rec2+= 4.0 * coeff * coeff * tmp * q2_all * q2_all / nr ; } } if (MASTER(cr)) fprintf(stderr, "\rCalculating reciprocal error part 1 ... %3.0f%%", 100.0*(nx-startlocal+1)/(x_per_core)); } if (MASTER(cr)) fprintf(stderr, "\n"); /* Use just a fraction of all charges to estimate the self energy error term? */ bFraction = (info->fracself > 0.0) && (info->fracself < 1.0); if (bFraction) { /* Here xtot is the number of samples taken for the Monte Carlo calculation * of the average of term IV of equation 35 in Wang2010. Round up to a * number of samples that is divisible by the number of nodes */ x_per_core = ceil(info->fracself * nr / (real)cr->nnodes); xtot = x_per_core * cr->nnodes; } else { /* In this case we use all nr particle positions */ xtot = nr; x_per_core = ceil( (real)xtot / (real)cr->nnodes ); } startlocal = x_per_core * cr->nodeid; stoplocal = min(startlocal + x_per_core, xtot); /* min needed if xtot == nr */ if (bFraction) { /* Make shure we get identical results in serial and parallel. Therefore, * take the sample indices from a single, global random number array that * is constructed on the master node and that only depends on the seed */ snew(numbers, xtot); if (MASTER(cr)) { for (i=0; i<xtot; i++) { numbers[i] = floor(gmx_rng_uniform_real(rng) * nr ); } } /* Broadcast the random number array to the other nodes */ if (PAR(cr)) { nblock_bc(cr,xtot,numbers); } if (bVerbose && MASTER(cr)) { fprintf(stdout, "Using %d sample%s to approximate the self interaction error term", xtot, xtot==1?"":"s"); if (PAR(cr)) fprintf(stdout, " (%d sample%s per node)", x_per_core, x_per_core==1?"":"s"); fprintf(stdout, ".\n"); } } /* Return the number of positions used for the Monte Carlo algorithm */ *nsamples = xtot; for(i=startlocal;i<stoplocal;i++) { e_rec3x=0; e_rec3y=0; e_rec3z=0; if (bFraction) { /* Randomly pick a charge */ ci = numbers[i]; } else { /* Use all charges */ ci = i; } /* for(nx=startlocal; nx<=stoplocal; nx++)*/ for(nx=-info->nkx[0]/2; nx<info->nkx[0]/2+1; nx++) { svmul(nx,info->recipbox[XX],gridpx); for(ny=-info->nky[0]/2; ny<info->nky[0]/2+1; ny++) { svmul(ny,info->recipbox[YY],tmpvec); rvec_add(gridpx,tmpvec,gridpxy); for(nz=-info->nkz[0]/2; nz<info->nkz[0]/2+1; nz++) { if ( 0 == nx && 0 == ny && 0 == nz) continue; svmul(nz,info->recipbox[ZZ],tmpvec); rvec_add(gridpxy,tmpvec,gridp); tmp=norm2(gridp); coeff=exp(-1.0 * M_PI * M_PI * tmp / info->ewald_beta[0] / info->ewald_beta[0] ); coeff/= tmp ; e_rec3x+=coeff*eps_self(nx,info->nkx[0],info->recipbox[XX],info->pme_order[0],x[ci]); e_rec3y+=coeff*eps_self(ny,info->nky[0],info->recipbox[YY],info->pme_order[0],x[ci]); e_rec3z+=coeff*eps_self(nz,info->nkz[0],info->recipbox[ZZ],info->pme_order[0],x[ci]); } } } clear_rvec(tmpvec2); svmul(e_rec3x,info->recipbox[XX],tmpvec); rvec_inc(tmpvec2,tmpvec); svmul(e_rec3y,info->recipbox[YY],tmpvec); rvec_inc(tmpvec2,tmpvec); svmul(e_rec3z,info->recipbox[ZZ],tmpvec); rvec_inc(tmpvec2,tmpvec); e_rec3 += q[ci]*q[ci]*q[ci]*q[ci]*norm2(tmpvec2) / ( xtot * M_PI * info->volume * M_PI * info->volume); if (MASTER(cr)){ fprintf(stderr, "\rCalculating reciprocal error part 2 ... %3.0f%%", 100.0*(i+1)/stoplocal); } } if (MASTER(cr)) fprintf(stderr, "\n"); #ifdef GMX_LIB_MPI #ifdef TAKETIME if (MASTER(cr)) { t1= MPI_Wtime() - t0; fprintf(fp_out, "Recip. err. est. took : %lf s\n", t1); } #endif #endif #ifdef DEBUG if (PAR(cr)) { fprintf(stderr, "Node %3d: nx=[%3d...%3d] e_rec3=%e\n", cr->nodeid, startlocal, stoplocal, e_rec3); } #endif if (PAR(cr)) { gmx_sum(1,&e_rec1,cr); gmx_sum(1,&e_rec2,cr); gmx_sum(1,&e_rec3,cr); } /* e_rec1*=8.0 * q2_all / info->volume / info->volume / nr ; e_rec2*= q2_all / M_PI / M_PI / info->volume / info->volume / nr ; e_rec3/= M_PI * M_PI * info->volume * info->volume * nr ; */ e_rec=sqrt(e_rec1+e_rec2+e_rec3); return ONE_4PI_EPS0 * e_rec; }
/* Estimate the reciprocal space part error of the SPME Ewald sum. */ static real estimate_reciprocal( t_inputinfo *info, rvec x[], /* array of particles */ real q[], /* array of charges */ int nr, /* number of charges = size of the charge array */ FILE *fp_out, t_commrec *cr) { real e_rec=0; /* reciprocal error estimate */ real e_rec1=0; /* Error estimate term 1*/ real e_rec2=0; /* Error estimate term 2*/ real e_rec3=0; /* Error estimate term 3 */ real e_rec3x=0; /* part of Error estimate term 3 in x */ real e_rec3y=0; /* part of Error estimate term 3 in y */ real e_rec3z=0; /* part of Error estimate term 3 in z */ int i,ci; int nx,ny,nz; /* grid coordinates */ real q2_all=0; /* sum of squared charges */ rvec gridpx; /* reciprocal grid point in x direction*/ rvec gridpxy; /* reciprocal grid point in x and y direction*/ rvec gridp; /* complete reciprocal grid point in 3 directions*/ rvec tmpvec; /* template to create points from basis vectors */ rvec tmpvec2; /* template to create points from basis vectors */ real coeff=0; /* variable to compute coefficients of the error estimate */ real coeff2=0; /* variable to compute coefficients of the error estimate */ real tmp=0; /* variables to compute different factors from vectors */ real tmp1=0; real tmp2=0; real xtmp=0; real ytmp=0; real ztmp=0; double ewald_error; /* Random number generator */ gmx_rng_t rng=NULL; /*rng=gmx_rng_init(gmx_rng_make_seed()); */ /* Index variables for parallel work distribution */ int startglobal,stopglobal; int startlocal, stoplocal; int x_per_core; int nrsamples; real xtot; /* #define TAKETIME */ #ifdef TAKETIME double t0=0.0; double t1=0.0; double t2=0.0; #endif rng=gmx_rng_init(cr->nodeid); clear_rvec(gridpx); clear_rvec(gridpxy); clear_rvec(gridp); clear_rvec(tmpvec); clear_rvec(tmpvec2); for(i=0;i<nr;i++) { q2_all += q[i]*q[i]; } /* Calculate indices for work distribution */ startglobal=-info->nkx[0]/2; stopglobal = info->nkx[0]/2; xtot = stopglobal*2+1; if (PAR(cr)) { x_per_core = ceil(xtot / cr->nnodes); startlocal = startglobal + x_per_core*cr->nodeid; stoplocal = startlocal + x_per_core -1; if (stoplocal > stopglobal) stoplocal = stopglobal; } else { startlocal = startglobal; stoplocal = stopglobal; x_per_core = xtot; } /* #ifdef GMX_MPI MPI_Barrier(MPI_COMM_WORLD); #endif */ #ifdef TAKETIME if (MASTER(cr)) t0 = MPI_Wtime(); #endif if (MASTER(cr)){ fprintf(stderr, "Calculating reciprocal error part 1 ..."); } for(nx=startlocal; nx<=stoplocal; nx++) { svmul(nx,info->recipbox[XX],gridpx); for(ny=-info->nky[0]/2; ny<info->nky[0]/2+1; ny++) { svmul(ny,info->recipbox[YY],tmpvec); rvec_add(gridpx,tmpvec,gridpxy); for(nz=-info->nkz[0]/2; nz<info->nkz[0]/2+1; nz++) { if ( 0 == nx && 0 == ny && 0 == nz ) continue; svmul(nz,info->recipbox[ZZ],tmpvec); rvec_add(gridpxy,tmpvec,gridp); tmp=norm2(gridp); coeff=exp(-1.0 * M_PI * M_PI * tmp / info->ewald_beta[0] / info->ewald_beta[0] ) ; coeff/= 2.0 * M_PI * info->volume * tmp; coeff2=tmp ; tmp=eps_poly2(nx,info->nkx[0],info->pme_order[0]); tmp+=eps_poly2(ny,info->nkx[0],info->pme_order[0]); tmp+=eps_poly2(nz,info->nkx[0],info->pme_order[0]); tmp1=eps_poly1(nx,info->nkx[0],info->pme_order[0]); tmp2=eps_poly1(ny,info->nky[0],info->pme_order[0]); tmp+=2.0 * tmp1 * tmp2; tmp1=eps_poly1(nz,info->nkz[0],info->pme_order[0]); tmp2=eps_poly1(ny,info->nky[0],info->pme_order[0]); tmp+=2.0 * tmp1 * tmp2; tmp1=eps_poly1(nz,info->nkz[0],info->pme_order[0]); tmp2=eps_poly1(nx,info->nkx[0],info->pme_order[0]); tmp+=2.0 * tmp1 * tmp2; tmp1=eps_poly1(nx,info->nkx[0],info->pme_order[0]); tmp1+=eps_poly1(ny,info->nky[0],info->pme_order[0]); tmp1+=eps_poly1(nz,info->nkz[0],info->pme_order[0]); tmp+= tmp1 * tmp1; e_rec1+= 32.0 * M_PI * M_PI * coeff * coeff * coeff2 * tmp * q2_all * q2_all / nr ; tmp1=eps_poly3(nx,info->nkx[0],info->pme_order[0]); tmp1*=info->nkx[0]; tmp2=iprod(gridp,info->recipbox[XX]); tmp=tmp1*tmp2; tmp1=eps_poly3(ny,info->nky[0],info->pme_order[0]); tmp1*=info->nky[0]; tmp2=iprod(gridp,info->recipbox[YY]); tmp+=tmp1*tmp2; tmp1=eps_poly3(nz,info->nkz[0],info->pme_order[0]); tmp1*=info->nkz[0]; tmp2=iprod(gridp,info->recipbox[ZZ]); tmp+=tmp1*tmp2; tmp*=4.0 * M_PI; tmp1=eps_poly4(nx,info->nkx[0],info->pme_order[0]); tmp1*=norm2(info->recipbox[XX]); tmp1*=info->nkx[0] * info->nkx[0]; tmp+=tmp1; tmp1=eps_poly4(ny,info->nky[0],info->pme_order[0]); tmp1*=norm2(info->recipbox[YY]); tmp1*=info->nky[0] * info->nky[0]; tmp+=tmp1; tmp1=eps_poly4(nz,info->nkz[0],info->pme_order[0]); tmp1*=norm2(info->recipbox[ZZ]); tmp1*=info->nkz[0] * info->nkz[0]; tmp+=tmp1; e_rec2+= 4.0 * coeff * coeff * tmp * q2_all * q2_all / nr ; } } if (MASTER(cr)) fprintf(stderr, "\rCalculating reciprocal error part 1 ... %3.0f%%", 100.0*(nx-startlocal+1)/(x_per_core)); } /* #ifdef GMX_MPI MPI_Barrier(MPI_COMM_WORLD); #endif */ if (MASTER(cr)) fprintf(stderr, "\n"); if (info->fracself>0) { nrsamples=ceil(info->fracself*nr); } else { nrsamples=nr; } xtot=nrsamples; startglobal=0; stopglobal=nr; if(PAR(cr)) { x_per_core=ceil(xtot/cr->nnodes); startlocal=startglobal+x_per_core*cr->nodeid; stoplocal=startglobal+x_per_core*(cr->nodeid+1); if (stoplocal>stopglobal) stoplocal=stopglobal; } else { startlocal=startglobal; stoplocal=stopglobal; x_per_core=xtot; } for(i=startlocal;i<stoplocal;i++) { e_rec3x=0; e_rec3y=0; e_rec3z=0; if (info->fracself<0) { ci=i; }else { ci=floor(gmx_rng_uniform_real(rng) * nr ); if (ci==nr) { ci=nr-1; } } /* for(nx=startlocal; nx<=stoplocal; nx++)*/ for(nx=-info->nkx[0]/2; nx<info->nkx[0]/2+1; nx++) { svmul(nx,info->recipbox[XX],gridpx); for(ny=-info->nky[0]/2; ny<info->nky[0]/2+1; ny++) { svmul(ny,info->recipbox[YY],tmpvec); rvec_add(gridpx,tmpvec,gridpxy); for(nz=-info->nkz[0]/2; nz<info->nkz[0]/2+1; nz++) { if ( 0 == nx && 0 == ny && 0 == nz) continue; svmul(nz,info->recipbox[ZZ],tmpvec); rvec_add(gridpxy,tmpvec,gridp); tmp=norm2(gridp); coeff=exp(-1.0 * M_PI * M_PI * tmp / info->ewald_beta[0] / info->ewald_beta[0] ); coeff/= tmp ; e_rec3x+=coeff*eps_self(nx,info->nkx[0],info->recipbox[XX],info->pme_order[0],x[ci]); e_rec3y+=coeff*eps_self(ny,info->nky[0],info->recipbox[YY],info->pme_order[0],x[ci]); e_rec3z+=coeff*eps_self(nz,info->nkz[0],info->recipbox[ZZ],info->pme_order[0],x[ci]); } } } clear_rvec(tmpvec2); svmul(e_rec3x,info->recipbox[XX],tmpvec); rvec_inc(tmpvec2,tmpvec); svmul(e_rec3y,info->recipbox[YY],tmpvec); rvec_inc(tmpvec2,tmpvec); svmul(e_rec3z,info->recipbox[ZZ],tmpvec); rvec_inc(tmpvec2,tmpvec); e_rec3 += q[ci]*q[ci]*q[ci]*q[ci]*norm2(tmpvec2) / ( nrsamples * M_PI * info->volume * M_PI * info->volume); if (MASTER(cr)){ fprintf(stderr, "\rCalculating reciprocal error part 2 ... %3.0f%%", 100.0*(i+1)/stoplocal); } } if (MASTER(cr)) fprintf(stderr, "\n"); #ifdef TAKETIME if (MASTER(cr)) { t1= MPI_Wtime() - t0; fprintf(fp_out, "Recip. err. est. took : %lf s\n", t1); } #endif #ifdef DEBUG if (PAR(cr)) { fprintf(stderr, "Node %3d: nx=[%3d...%3d] e_rec3=%e\n", cr->nodeid, startlocal, stoplocal, e_rec3); } #endif /* #ifdef GMX_MPI MPI_Barrier(MPI_COMM_WORLD); #endif */ #ifdef TAKETIME if (MASTER(cr)) { t2= MPI_Wtime() - t0; fprintf(fp_out, "barrier : %lf s\n", t2-t1); } #endif if (PAR(cr)) { gmx_sum(1,&e_rec1,cr); gmx_sum(1,&e_rec2,cr); gmx_sum(1,&e_rec3,cr); } #ifdef TAKETIME if (MASTER(cr)) fprintf(fp_out, "final reduce : %lf s\n", MPI_Wtime() - t0-t2); #endif /* e_rec1*=8.0 * q2_all / info->volume / info->volume / nr ; e_rec2*= q2_all / M_PI / M_PI / info->volume / info->volume / nr ; e_rec3/= M_PI * M_PI * info->volume * info->volume * nr ; */ e_rec=sqrt(e_rec1+e_rec2+e_rec3); return ONE_4PI_EPS0 * e_rec; }